210 lines
8.0 KiB
Markdown
210 lines
8.0 KiB
Markdown
# OneOS 客户车辆范围内部接口契约(草案)
|
||
|
||
更新时间:2026-07-14
|
||
|
||
## 1. 目的与安全边界
|
||
|
||
该接口仅向车辆数据中台提供 OneOS 已确认的客户车辆授权范围,不提供密码,不允许浏览器直连,也不承担实时遥测查询。
|
||
|
||
- OneOS 是客户、合同、交还车事实和车辆档案的权威来源。
|
||
- 车辆数据中台只保存可重建的只读投影。
|
||
- 客户 ID 必须来自已验证的 OneOS 会话或内部同步任务,不能采用浏览器请求参数。
|
||
- 归属不确定、VIN 缺失、存在多客户冲突的车辆必须放入异常列表,不能进入授权列表。
|
||
- 内部请求需要服务身份认证、时间戳、防重放和请求 ID;网关必须剥离外部同名身份头。
|
||
|
||
## 2. 推荐接口
|
||
|
||
### 查询车辆中台全量业务范围快照
|
||
|
||
车辆数据中台的定时同步需要一次读取全部客户的当前车辆范围,避免先拉客户列表再产生 N+1 请求。推荐由 OneOS 提供专用平台快照:
|
||
|
||
```http
|
||
GET /inner/v1/vehicle-data-platform/customer-vehicle-scopes?cursor=&limit=500
|
||
Authorization: Service <service-token>
|
||
X-Request-Id: <uuid>
|
||
X-Request-Timestamp: <unix-seconds>
|
||
X-Request-Signature: <hmac-sha256>
|
||
```
|
||
|
||
签名原文必须逐字节使用:
|
||
|
||
```text
|
||
GET\n<path-and-sorted-query>\n<unix-seconds>\n<request-id>
|
||
```
|
||
|
||
双方使用独立共享密钥计算 HMAC-SHA256 小写十六进制;服务令牌与签名密钥不能相同。OneOS 应拒绝超过 60 秒的时间戳、重复 request ID、错误 audience 和外网来源。网关必须剥离外部传入的同名身份头。
|
||
|
||
响应:
|
||
|
||
```json
|
||
{
|
||
"code": 0,
|
||
"data": {
|
||
"scopeVersion": "2026-07-14T12:00:00.123Z/987654",
|
||
"generatedAt": "2026-07-14T12:00:00.123Z",
|
||
"complete": true,
|
||
"nextCursor": null,
|
||
"items": [
|
||
{
|
||
"vehicleId": "2001",
|
||
"vin": "LXXXXXXXXXXXXXXXX",
|
||
"plateNumber": "沪A00000",
|
||
"customerId": "1001",
|
||
"customerName": "示例客户",
|
||
"contractId": "3001",
|
||
"contractCode": "HT20260001",
|
||
"projectName": "示例项目",
|
||
"departmentId": "4001",
|
||
"departmentName": "运营一部",
|
||
"responsibleUserId": "5001",
|
||
"responsibleUserName": "示例负责人",
|
||
"modelName": "示例车型",
|
||
"brandName": "示例品牌",
|
||
"operationStatus": "2",
|
||
"scopeStartAt": "2026-07-01T08:00:00+08:00",
|
||
"sourceUpdatedAt": "2026-07-14T11:59:58.456+08:00"
|
||
}
|
||
],
|
||
"rejected": [
|
||
{
|
||
"vehicleId": "2002",
|
||
"reason": "VIN_MISSING"
|
||
}
|
||
]
|
||
},
|
||
"requestId": "..."
|
||
}
|
||
```
|
||
|
||
约束:
|
||
|
||
- 雪花 ID 一律用 JSON 字符串传输,避免 JavaScript 数字精度丢失。
|
||
- `scopeVersion` 对同一业务快照稳定;分页中的每一页必须来自同一个快照。
|
||
- `generatedAt` 和 `scopeVersion` 在所有分页中必须完全一致。
|
||
- 任意分页 `complete=false` 表示快照不完整,车辆中台不得发布该版本。
|
||
- `items` 内 VIN 必须非空、标准化为大写并在当前快照内唯一。
|
||
- `rejected` 只返回机器可读原因和内部车辆 ID,不返回敏感客户信息。
|
||
- 每页最多 500 条;全部快照最多 50,000 条、100 页。游标不能重复。
|
||
- 客户、车辆、合同等雪花 ID 必须使用 JSON 字符串;部门和负责人允许为空,但不能伪造。
|
||
- 建议 `ETag=scopeVersion`,后续支持 `If-None-Match` 降低同步流量。
|
||
|
||
车辆中台已实现该契约的客户端:总同步超时默认 60 秒,单请求超时 10 秒;网络错误、HTTP 429 和 5xx 最多重试 3 次,其他 4xx、业务 code 非 0、JSON 异常、分页版本漂移、重复游标、越界行数或不完整快照立即失败关闭。
|
||
|
||
### 查询单个客户车辆快照
|
||
|
||
如 OneOS 还需要为内部业务提供客户级接口,可保留:
|
||
|
||
```http
|
||
GET /inner/v1/customer-vehicle-scopes/{customerId}?cursor=&limit=500
|
||
```
|
||
|
||
该接口不作为车辆中台全量定时同步的唯一入口,以免形成客户级 N+1。
|
||
|
||
### 批量检查客户车辆归属
|
||
|
||
供登录后单车访问和安全审计使用,不代替快照同步:
|
||
|
||
```http
|
||
POST /inner/v1/customer-vehicle-scopes/check
|
||
Content-Type: application/json
|
||
|
||
{
|
||
"customerId": "1001",
|
||
"vins": ["LXXXXXXXXXXXXXXXX"]
|
||
}
|
||
```
|
||
|
||
响应必须对每个 VIN 返回 `allowed/denied/unknown`。`unknown` 与接口异常在外部访问场景中均按拒绝处理。
|
||
|
||
## 3. 当前车辆判定
|
||
|
||
候选记录至少满足:
|
||
|
||
```text
|
||
vehicle_info.del_flag = 0
|
||
vehicle_lease_order_record.del_flag = 0
|
||
最新有效 delivery_vehicle.delivery_status IN (2,3)
|
||
不存在该交车单对应的有效 return_vehicle_task.status IN (2,3,5)
|
||
vehicle_lease_order_record.customer_id = customerId(仅作为独立归属交叉校验)
|
||
VIN 非空且唯一
|
||
```
|
||
|
||
同时必须核验:
|
||
|
||
- 记录客户等于合同 `COALESCE(other_customer_id, customer_id)`;
|
||
- 不使用 `vehicle_lease_order_record.last_return_time` 判定当前生命周期;OneOS 还车草稿保存会提前写入该聚合字段;
|
||
- 聚合记录的合同、交车单和最近历史记录一致;
|
||
- 不存在同一 VIN/vehicleId 同时授权给多个客户;
|
||
- 合同保存没有覆盖已发生的交还车事实。
|
||
|
||
生产数据审计完成前,该判定仅为候选口径,不得直接开放外部客户。
|
||
|
||
## 4. 身份声明契约
|
||
|
||
客户请求由 OneOS Gateway 完成登录态验证后,可向车辆中台传递短时签名声明:
|
||
|
||
```json
|
||
{
|
||
"iss": "oneos-gateway",
|
||
"aud": "vehicle-data-platform",
|
||
"sub": "1001",
|
||
"subjectType": "customer",
|
||
"customerId": "1001",
|
||
"tenantId": "000000",
|
||
"roles": ["customer_vehicle_viewer"],
|
||
"iat": 1784001600,
|
||
"exp": 1784001900,
|
||
"jti": "..."
|
||
}
|
||
```
|
||
|
||
车辆中台必须校验发行方、受众、签名、有效期和 `jti`,并把 `customerId` 写入请求上下文。禁止从 Query、Body 或普通转发头覆盖它。
|
||
|
||
## 5. 车辆中台投影
|
||
|
||
建议表:
|
||
|
||
```text
|
||
business_customer
|
||
customer_id, status, source_version, synced_at
|
||
|
||
business_customer_vehicle_scope
|
||
customer_id, vin, vehicle_id, contract_id, scope_start_at,
|
||
source_version, source_updated_at, published_at
|
||
|
||
business_scope_sync_run
|
||
run_id, customer_id, source_version, status,
|
||
received_count, accepted_count, rejected_count, started_at, finished_at, error
|
||
|
||
business_scope_audit
|
||
request_id, principal_id, customer_id, action, vin, decision, reason, created_at
|
||
```
|
||
|
||
唯一约束至少包括 `(customer_id, vin)`;发布新版本时应在事务中整体切换,不能边分页边覆盖当前版本。
|
||
|
||
## 6. 失败策略
|
||
|
||
- 首次同步失败:客户门户不可用,不回退到全量车辆。
|
||
- 已有快照过期:达到配置的最大陈旧时间后拒绝访问,并告知“业务车辆范围同步异常”。
|
||
- 单车不在范围:统一返回 404,避免泄露 VIN 是否存在。
|
||
- 多客户冲突或 VIN 缺失:隔离并告警,不授权。
|
||
- OneOS 账号停用:会话校验立即失败;车辆中台短时声明最长建议 5 分钟。
|
||
|
||
## 7. 验收用例
|
||
|
||
1. 客户 A 无法通过 VIN、车牌、导出、轨迹、告警 ID 或分页游标访问客户 B 的车辆。
|
||
2. 车辆还车后,新快照发布即从客户范围移除;旧 URL 也返回 404。
|
||
3. 车辆再次交付给另一客户后,仅新客户可见。
|
||
4. `other_customer_id` 存在时,只授权给有效丙方客户。
|
||
5. 缺 VIN、多客户冲突、聚合与合同不一致的车辆不进入任何外部范围。
|
||
6. OneOS 不可用、签名错误、Scope 过期或分页版本变化时全部失败关闭。
|
||
7. 1,000 台车辆的快照分页结果无重复、无遗漏,条件请求可返回 304。
|
||
8. 所有拒绝决策保留 request ID、主体、VIN 摘要和原因,且不记录访问令牌。
|
||
|
||
## 8. 兼容与回滚
|
||
|
||
- 保留现有 Dubbo `RemoteVehicleService`,但在修复前明确标记 `listByCustomerId` 不可用于外部授权。
|
||
- 新 HTTP 内部接口使用独立 `/inner/v1` 前缀,不改变现有页面接口。
|
||
- 先以影子模式计算快照并与人工样本对账,再允许车辆中台消费。
|
||
- 若上线后异常,关闭客户入口并回退到上一个已验证 Scope 版本;不得回退为“无 Scope 查询”。
|