Files
lingniu-vehicle-ingest/vehicle-data-platform/docs/oneos-change-approval-proposal.md

207 lines
10 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# OneOS 客户车辆门户变更审批提案
更新时间2026-07-14
状态已停止。2026-07-14 用户明确要求不修改 `ln-asset-management` 任何逻辑此前本地试验改动已全部撤销OneOS 仓库保持干净。本文件仅保留为风险证据,不构成待执行审批项。
## 1. 审批目标
本提案只解决客户只读访问车辆数据所必需的三件事:
1. OneOS 能稳定识别客户账号并输出不可伪造的客户身份;
2. OneOS 的“当前客户车辆”事实不会因合同保存被破坏;
3. 车辆数据中台收到客户身份后,只能访问该客户的车辆 Scope。
车辆锁车、控车以及保险、结算、事故、违章等客户功能不在本批次范围内。
## 2. 当前源码证据
### 2.1 客户车辆 RPC 当前会泄露全量车辆
`ln-asset-management/src/main/java/com/ln/asset/api/impl/RemoteVehicleServiceImpl.java:83`
`listByCustomerId` 忽略 `customerId`,实际只按 `del_flag=0` 查询。其 VO 转换也没有回填
`customerId``contractCode`
该接口在修复前必须保持内部禁用,不能作为车辆数据中台的授权来源。
### 2.2 当前租赁 Mapper 会丢字段
`ln-asset-management/src/main/resources/mapper/VehicleLeaseOrderRecordMapper.xml:6`
`BaseResultMap``Base_Column_List` 缺少以下实体字段:
- `customer_id``project_name`
- `delivery_vehicle_id``last_delivery_name`
- `return_vehicle_task_id``last_return_name`
- `update_by`
使用这些自定义查询取得记录后,上述字段会变成空值。生产审计同时发现聚合记录与最新历史记录有
325 条关键字段差异,不能把空值当成“无业务关系”。
### 2.3 合同保存会清空交还车事实
`VehicleLeaseOrderRecordServiceImpl.java:262``deliveryVehicleId == null` 时,同时清空交车和还车的
单据 ID、时间、里程与操作人。合同提交/更新并不代表交车事实撤销,因此这里违反业务事实的单一写入原则。
交车字段只能由交车完成/撤销流程修改;还车字段只能由还车完成/撤销流程修改。合同更新只能更新客户、合同、
项目和业务组织字段。
### 2.4 客户账号当前不能安全登录
- `CustomerInfoServiceImpl.java:188` 先尝试创建 `sys_user`,之后才保存客户档案;远程调用失败被吞掉,两个库之间不存在事务一致性。
- `SysUserRegistrar.java:27` 给所有业务账号使用共享初始密码 `ln123456`
- `RemoteUserServiceImpl.java:222` 把业务账号开通绑定到公开注册开关 `sys.account.registerUser`
- `UserType.java:14` 只支持 `sys_user``app_user``customer` 会在 Sa-Token 权限解析时抛异常。
- 密码登录按 `tenantId + username` 查询,短信登录默认取同手机号的第一条记录,均不能可靠区分客户账号。
- 更新客户档案不会同步账号;删除客户时账号删除异常同样只记日志。
生产审计结果是 335 个有效客户中,当前客户账号数量为 0因此不能直接批量启用现有逻辑。
## 3. 推荐变更批次
### B1修复客户车辆业务事实`ln-asset-management`
预计修改文件:
- `src/main/resources/mapper/VehicleLeaseOrderRecordMapper.xml`
- `src/main/java/com/ln/asset/modules/contract/mapper/VehicleLeaseOrderRecordMapper.java`
- `src/main/java/com/ln/asset/modules/contract/service/impl/VehicleLeaseOrderRecordServiceImpl.java`
- `src/main/java/com/ln/asset/api/impl/RemoteVehicleServiceImpl.java`
- `ln-asset-api/src/main/java/com/ln/asset/api/RemoteVehicleService.java`
- `ln-asset-api/src/main/java/com/ln/asset/api/domain/vo/RemoteVehicleVo.java`
- 对应单元/集成测试文件
变更内容:
1. 补齐 Mapper 的所有实体字段映射与列清单;禁止“读出空值再全实体更新”覆盖已有事实。
2. 把合同字段更新与交车/还车生命周期更新拆开:合同保存不再修改任何交还车字段。
3. 增加显式的当前 Scope 查询,条件必须同时包含有效车辆、有效客户、有效合同、已交车未还车、
`record.customer_id = COALESCE(contract.other_customer_id, contract.customer_id)`
4. 异常关系返回隔离原因,不为其生成授权记录。
5. 修复 `listByCustomerId`,但将其标注为兼容接口;车辆中台继续使用已实现的最小权限 RDS 快照同步,
不依赖一次 RPC 返回无限列表。
6. 交车、还车聚合更新失败必须让业务事务失败或写入可重试 Outbox不能仅记录日志后继续成功。
明确不做:
- 不把遥测位置、在线状态或总里程回写为交还车业务事实;
- 不恢复 `vehicle_info.customer_id` 作为客户归属;
- 不修改锁车、控车业务。
验收测试:
- 客户 A 查询结果不含客户 B 车辆;空客户 ID 返回空集合;
- 丙方客户存在时只归属 `other_customer_id`
- 未交车、已还车、客户/合同不一致、缺 VIN 的车辆均不授权;
- 合同重复保存前后,交还车单 ID、时间、里程完全不变
- 同一车辆存在多个当前关系时整体隔离,而不是任选一条;
- 本段验收基线已被事务事实影子查询取代;当前只读中台口径为 639 个候选、613 个可发布、26 个隔离。
回滚:
- B1 不删除字段、不改变 Dubbo 方法签名;可按服务版本回滚;
- 新查询与旧接口并存一个版本,车辆中台只读同步可独立停用;
- 数据修复 SQL 与代码发布分开审批,禁止发布脚本自动改写异常关系。
### B2建立客户账号一致性`ln-cloud` + `ln-asset-management`
预计修改文件/模块:
- `ln-cloud/ruoyi-common/ruoyi-common-core/.../UserType.java`
- `ln-cloud/ruoyi-common/ruoyi-common-satoken/.../SaPermissionImpl.java`
- `ln-cloud/ruoyi-api/ruoyi-api-system/.../RemoteUserService.java`
- `ln-cloud/ruoyi-modules/ruoyi-system/.../RemoteUserServiceImpl.java`
- `ln-cloud/ruoyi-modules/ruoyi-system/.../ISysUserService.java`
- `ln-cloud/ruoyi-modules/ruoyi-system/.../SysUserServiceImpl.java`
- `ln-cloud/ruoyi-auth` 新增独立客户认证策略
- `ln-asset-management` 客户账号开通、更新、停用 Outbox/补偿任务
- 新增账号绑定/激活所需数据库迁移与测试
变更内容:
1. 增加明确的 `CUSTOMER("customer")` 类型,并为客户主体返回固定、最小的门户权限,不继承后台菜单权限。
2. 新增内部“业务账号开通”接口,不再复用公开自助注册开关;接口以
`(tenantId, userType, businessId)` 幂等,并校验用户名、手机号冲突。
3. 增加显式业务绑定,不能仅依赖 `sys_user.user_id == customer_info.id` 的偶然约定。
4. 客户档案事务写入 Outbox账号、角色、绑定创建失败可重试、可告警、可人工补偿。前端明确展示“账号待开通”
不再静默成功。
5. 删除共享默认密码。采用一次性随机激活凭证,短时有效、单次使用;激活后立即失效。
6. 新增独立 `customer_password` / `customer_sms` 登录策略。客户类型由受信客户端配置或策略固定,不能由浏览器
任意传入;后台密码登录仍只允许后台账号。
7. 客户停用、删除或合作终止时撤销其会话,并产生身份版本变更事件。
8. 存量补偿先输出冲突清单335 个客户不得按手机号自动合并,审计发现的 4 个手机号匹配必须人工确认。
验收测试:
- 同用户名/手机号存在不同主体类型时,客户登录只能命中 customer
- 客户账号不能使用后台登录策略,后台账号不能使用客户策略;
- 公开注册关闭时,内部业务账号开通仍可按授权调用;
- 重复 Outbox 消息不会创建重复账号、绑定或角色;
- 激活凭证不能复用,日志中不出现明文凭证或密码;
- 删除/停用客户后现有 token 立即失效;
- 客户权限解析不抛 `UserType not found`,且没有后台菜单权限。
回滚:
- 新客户认证使用独立 grant type 和独立客户端,关闭该客户端即可停止入口;
- 绑定表和 Outbox 只新增,不破坏现有后台账号;
- 回滚应用版本前先停止补偿消费者,不删除已经创建的客户账号,只统一停用,避免 ID 被复用。
### B3向车辆中台签发短时身份`ln-cloud/ruoyi-auth`
推荐新增内部/登录后交换接口:
```http
POST /inner/v1/vehicle-platform/token
Authorization: Bearer <oneos-customer-token>
```
服务端从 Sa-Token 会话和业务绑定取得 `userId/customerId/tenantId/userType`,签发最长 5 分钟、
`aud=vehicle-data-platform` 的短时 JWT。浏览器不能提交或覆盖 `customerId`
最低声明:
```json
{
"iss": "oneos-auth",
"aud": "vehicle-data-platform",
"sub": "<sys_user.user_id>",
"subjectType": "customer",
"customerId": "<customer_info.id>",
"tenantId": "<tenant_id>",
"scope": ["vehicle:read"],
"identityVersion": 1,
"iat": 0,
"exp": 0,
"jti": "<random>"
}
```
安全要求:
- 使用非对称签名,车辆中台只持有公钥;密钥 ID 支持轮换;
- 严格校验发行方、受众、算法、`exp/nbf/iat`、主体类型和客户 ID
- 网关删除外部传入的同名身份头;不信任 `X-Customer-Id`
- Scope 投影过期、身份绑定缺失或账号停用时,车辆中台失败关闭;
- 任何越权详情统一返回 404避免泄露 VIN 或告警 ID 是否存在。
回滚:停止 token 交换接口或撤销对应客户端;车辆中台客户入口关闭,内部管理员入口不受影响。
## 4. 数据库变更必须单独审批
在修复 1 组重复聚合记录并确认软删除语义前,不直接添加唯一约束。建议顺序:
1. 输出重复记录和 26 条隔离关系的业务核对清单;
2. 由合同/交还车业务负责人确认每条修复方式;
3. 在预发布库回放合同保存、交车、还车、再次交车和丙方客户用例;
4. 再评估 `vehicle_lease_order_record(vehicle_id)` 唯一约束及 Scope 查询索引;
5. DDL 使用在线方式并准备反向 DDL不与应用代码在同一步骤自动执行。
## 5. 推荐审批顺序
1. B1/B2 已停止,不修改 OneOS 业务逻辑;
2. 先完成车辆中台只读 Scope 投影与异常隔离;
3. 客户身份入口需另行确定可信身份来源并通过双客户越权测试;
4. 最后才进行外部客户灰度。
每个批次独立提交、独立发布、独立回滚。未经用户明确同意,不修改上述 OneOS 文件。