302 lines
18 KiB
Markdown
302 lines
18 KiB
Markdown
# OneOS 车辆业务与车辆数据中台集成分析
|
||
|
||
完整系统、模块、接口、安全、数据库和运维分析见 `docs/oneos-ln-asset-management-system-analysis.md`。本文件聚焦车辆数据中台集成边界。
|
||
|
||
更新时间:2026-07-14
|
||
|
||
生产数据库聚合审计结果见 `oneos-production-audit-report.md`。
|
||
涉及 OneOS 的精确修改文件、批次、测试和回滚边界见 `oneos-change-approval-proposal.md`;该提案尚未执行。
|
||
车辆数据中台全部 API 的客户 Scope、IDOR、导出和地图服务覆盖矩阵见 `customer-scope-api-enforcement-matrix.md`。
|
||
|
||
## 1. 分析范围与约束
|
||
|
||
- OneOS 根目录:`/Users/lingniu/project/ai-coding/oneos`
|
||
- 核心业务服务:`ln-asset-management`,分支 `dev`
|
||
- 相关身份服务:`ln-cloud/ruoyi-auth`、`ln-cloud/ruoyi-system`
|
||
- 本轮仅进行只读分析,没有修改 OneOS 文件、数据库或运行配置。
|
||
- 车辆锁车、控车不在本方案范围内。
|
||
|
||
## 2. 系统职责边界
|
||
|
||
详细业务影响与遥测迁移边界见 `oneos-business-impact-matrix.md`。
|
||
|
||
### OneOS 应作为权威来源
|
||
|
||
- 客户档案:`customer_info`
|
||
- 客户账号及启停:`ry-cloud.sys_user`
|
||
- 车辆基础信息:`vehicle_info`、`vehicle_model`、`vehicle_status`
|
||
- 合同客户:`vehicle_lease_contract_info`
|
||
- 当前车辆交付/归还状态:`vehicle_lease_order_record`,必要时结合交还车任务表复核
|
||
- 业务筛选维度:运营状态、车型、品牌、运营公司、业务部门、合同和项目
|
||
|
||
### 车辆数据中台应作为权威来源
|
||
|
||
- 多协议接入状态及最后上报时间
|
||
- 实时位置、车速、SOC 和车辆告警
|
||
- 历史轨迹和原始数据证据
|
||
- 按 VIN 归并后的每日里程、统计期里程和最新总里程
|
||
- 协议数据质量与异常诊断
|
||
|
||
OneOS 当前仍通过 HASP/旧车联网接口拉取位置和总里程,其中 `/vehicle-info/page?withTotalMileage=true` 会按 VIN 逐车调用外部 HTTP 接口,存在 N+1 请求。新集成不应让车辆中台反向依赖这条旧链路。
|
||
|
||
## 3. 已确认的数据关系
|
||
|
||
### 权威表与用途
|
||
|
||
| 数据域 | 表 | 主键/关联 | 集成用途 | 注意事项 |
|
||
| --- | --- | --- | --- | --- |
|
||
| 客户档案 | `customer_info` | `id` | 客户名称、编码、合作状态 | 含联系人、银行等敏感字段,范围接口只取最小必要字段 |
|
||
| 客户账号 | `ry-cloud.sys_user` | `user_id = customer_info.id` | 登录、启停、用户类型 | 账号可能缺失;当前 `user_type=customer` 未被权限枚举支持 |
|
||
| 账号角色 | `ry-cloud.sys_user_role`、`sys_role` | `user_id`、`role_id` | 客户门户功能权限 | 新客户当前不自动分配角色 |
|
||
| 车辆档案 | `vehicle_info` | `id`、`vin` | VIN、车牌、车型、运营组织 | 实体已不再保存客户归属;VIN 必须核验空值和重复值 |
|
||
| 车辆状态 | `vehicle_status` | `vehicle_id` | 运营、车辆、证照、保险状态 | `online_status`、里程仍混有旧链路数据,不作为中台遥测权威值 |
|
||
| 合同 | `vehicle_lease_contract_info` | `id`、`order_id` | 主客户、丙方客户、合同和项目 | 有效客户为 `COALESCE(other_customer_id, customer_id)` |
|
||
| 当前租赁聚合 | `vehicle_lease_order_record` | 设计上一车一行 | 当前客户、合同、最近交还车事实 | Mapper 不完整、写入有清空风险,必须审计后使用 |
|
||
| 租赁历史 | `vehicle_lease_order_record_log` | `contract_id + vehicle_id` | 归属和交还车复核 | 是可追溯证据之一,但当前按组合更新,并非不可变事件日志 |
|
||
| 交车事实 | `delivery_vehicle` | `vehicle_id`、`order_detail_id` | 核验交车时间、里程和单据 | 完成状态枚举需与生产字典确认 |
|
||
| 还车事实 | `return_vehicle_task` | `vehicle_id`、`contract_id` | 核验还车时间、里程和完成状态 | 多条完成路径会更新聚合表,部分异常不中断主流程 |
|
||
|
||
车辆数据中台不应复制 `contact_mobile`、身份证/信用代码、银行账号、密码等字段。客户投影只需要客户 ID、展示名称、账号状态、业务版本和同步时间。
|
||
|
||
### 客户与账号
|
||
|
||
新增客户时,`customer_info.id` 使用雪花 ID,并尝试以同一个 ID 创建 `sys_user.user_id`:
|
||
|
||
```text
|
||
customer_info.id = sys_user.user_id
|
||
sys_user.user_type = customer
|
||
```
|
||
|
||
但账号创建失败只记录日志,不阻断客户保存,因此该关系不是天然完整的,必须进行覆盖率核验和补偿。
|
||
|
||
### 车辆与客户
|
||
|
||
2026-03-27 的 `9a8e2a5f` 提交将以下业务归属字段从 `VehicleInfo` 实体中移除:
|
||
|
||
- `customerId`
|
||
- `contractCode`
|
||
- `businessId`
|
||
- `businessDeptId`
|
||
- `contractType`
|
||
|
||
代码注释明确这些字段改由 `vehicle_lease_order_record` 提供。因此不能再把 `vehicle_info.customer_id` 当作唯一权威口径,即使旧数据库或旧文档仍保留该列。
|
||
|
||
合同存在甲方客户和丙方客户,现有业务代码的有效客户规则为:
|
||
|
||
```text
|
||
effectiveCustomerId = other_customer_id != null ? other_customer_id : customer_id
|
||
```
|
||
|
||
客户车辆范围还必须考虑交还车边界:只有已经交车且尚未还车的车辆,才能视为客户当前可见车辆。仅按 `vehicle_lease_order_record.customer_id` 查询会把已经归还的车辆继续授权给客户。
|
||
|
||
建议的当前客户车辆口径已调整为事务事实优先:
|
||
|
||
```text
|
||
vehicle_info.del_flag = 0
|
||
AND vehicle_lease_order_record.del_flag = 0
|
||
AND effective_customer_id = 当前客户
|
||
AND 最新有效 delivery_vehicle.delivery_status IN (2,3)
|
||
AND 不存在该交车单对应的 return_vehicle_task.status IN (2,3,5)
|
||
```
|
||
|
||
`vehicle_lease_order_record` 只用于客户归属交叉校验,不使用 `last_return_time` 判断生命周期,因为还车草稿会提前写入该字段。生产影子查询已验证该条件,仍需由业务抽样确认换车、合同变更、丙方客户、重复交还车和历史补录场景。
|
||
|
||
### 订单记录的可信度限制
|
||
|
||
`vehicle_lease_order_record` 的设计目标是“每辆车一条当前记录”,但当前实现还不能未经校验就作为唯一授权依据:
|
||
|
||
- `VehicleLeaseOrderRecordMapper.xml` 的 `BaseResultMap` 和 `Base_Column_List` 没有 `customer_id`、`project_name`、交还车单 ID、交还车人等实体字段。调用 `selectByVehicleId`、`selectByVehicleIds` 等自定义查询时,这些字段会丢失。
|
||
- `saveOrUpdateForContract()` 在 `deliveryVehicleId == null` 时会清空已有交车和还车字段。合同提交、重新提交、审批通过等多条路径都会调用该方法,若车辆此前已经交付,可能暂时或永久破坏当前租赁状态。
|
||
- 还车提交路径先写入还车时间,完成路径又可能清空当前合同和客户字段;不同查询不能仅以 `customer_id IS NOT NULL` 或仅以时间字段判断。
|
||
- 交还车更新失败只记录日志、不中断主流程,业务任务成功不代表聚合记录一定成功同步。
|
||
|
||
因此阶段 A 需要同时以当前聚合表、历史表、交车单、还车任务和合同表交叉核对。正式的范围快照接口应在一个受测试的查询服务中封装口径,并对异常记录失败关闭,不能让各调用方直接读取聚合表自行解释。
|
||
|
||
## 4. 已发现的高风险缺口
|
||
|
||
### P0:现有客户车辆远程接口会返回全量车辆
|
||
|
||
`RemoteVehicleService.listByCustomerId(customerId)` 的客户过滤已被注释,当前实现实际查询全部未删除车辆;`RemoteVehicleVo.customerId` 和 `contractCode` 的回填也被注释。
|
||
|
||
该接口在修复前不得用于任何外部客户权限,否则存在严重跨客户数据泄露风险。
|
||
|
||
### P0:客户账号体系尚不能直接用于车辆数据中台
|
||
|
||
- `customer_info` 创建成功不代表 `sys_user` 一定创建成功。
|
||
- 新客户账号没有分配角色或菜单权限。
|
||
- `sys_user.user_type=customer`,但 `UserType` 枚举只识别 `sys_user` 和 `app_user`。
|
||
- Sa-Token 权限解析会调用 `UserType.getUserType()`,客户账号触发权限判断时可能抛出类型不存在异常。
|
||
- 车辆数据中台当前只支持静态 Bearer Token 和 `viewer/operator/admin`,Principal 中没有 `customerId`、`tenantId` 或 VIN 范围。
|
||
- 客户账号创建复用了公开注册 RPC;该 RPC受 `sys.account.registerUser` 开关控制。生产若关闭自助注册,客户档案仍会成功但账号创建必然失败。
|
||
- 所有业务账号使用相同初始密码 `ln123456`,没有发现首次登录强制改密或一次性激活机制。外部客户上线前必须废弃该做法。
|
||
- 通用密码登录按 `tenantId + username` 查询,不按 `user_type` 区分;客户编码必须在租户内与其他类型账号全局唯一,并需要防止账号类型混淆。
|
||
|
||
推荐新增“业务账号开通”内部能力,不再复用公开注册开关:
|
||
|
||
1. 以 `(tenant_id, user_type, business_id)` 幂等开通客户账号;`business_id` 对客户即 `customer_info.id`。
|
||
2. 同一事务或 Outbox 中完成账号、客户角色和业务绑定;失败可重试并可见,不再吞异常。
|
||
3. 使用一次性激活链接或随机临时口令,首次登录强制设置密码;禁止共享默认密码。
|
||
4. 登录成功后校验账号主体类型为 `customer`,并从服务端绑定关系取得 `customerId`。
|
||
5. 客户停用、合作终止、账号删除时撤销会话并让车辆中台短时声明快速失效。
|
||
6. 存量补偿任务只处理缺失/异常账号,输出成功、冲突、跳过清单,不覆盖现有密码。
|
||
|
||
### P0:OneOS 地图和车辆查询没有客户级数据隔离
|
||
|
||
- `MapTruckController` 的列表、搜索、详情和信息树均未按客户过滤。
|
||
- 地图 SQL 默认返回所有符合运营状态的车辆。
|
||
- `VehicleInfoMapper` 仅声明业务部门数据权限;业务专员/主管的默认过滤逻辑当前被整体注释。
|
||
- 客户账号既没有部门数据范围,也没有独立客户数据范围。
|
||
|
||
### P1:代码、文档和映射存在漂移
|
||
|
||
- `VehicleInfo` 实体已移除客户和合同字段。
|
||
- `VehicleInfoMapper.xml` 的基础 ResultMap 和字段清单仍包含旧字段。
|
||
- `docs/VehicleInfo.md` 仍描述 `vehicle_info.customer_id` 并给出过时接口参数。
|
||
- 部分详情服务明确把客户和合同字段设置为 `null`。
|
||
- `RemoteContractMatchService.matchByVehicleAtTime` 已处理交还车时间边界,但合同命中时直接返回 `contract.customerId`,没有采用丙方优先的有效客户规则。
|
||
- `VehicleLeaseOrderRecordMapper.xml` 未完整映射实体字段,自定义查询取得的 `customerId` 等值可能为 `null`。
|
||
- 合同保存会重置交还车字段,而交还车聚合更新异常又不会阻断主流程,存在聚合状态与真实业务任务不一致的窗口。
|
||
|
||
### P0:车辆中台现有授权是“功能角色”,不是“数据范围”
|
||
|
||
当前 `Principal` 只有 `Name` 和 `Role`,鉴权中间件仅按 `viewer/operator/admin` 判断 HTTP 方法与功能权限。Store 接口和 SQL 构造器没有客户或 VIN Scope,`PrincipalFromContext` 也只被用于写操作的审计 Actor。
|
||
|
||
这会产生以下具体越权面:
|
||
|
||
| API 组 | 当前行为 | 客户开放前要求 |
|
||
| --- | --- | --- |
|
||
| 监控、车辆、实时位置 | 全量查询,可按 VIN/车牌筛选 | 所有列表、汇总、点位、详情统一限定授权 VIN |
|
||
| 轨迹、历史、原始帧、里程 | 接受调用方传入 VIN/关键字 | 先校验 VIN Scope,再访问 MySQL/TDengine;空范围返回空,不返回全量 |
|
||
| 告警 | 告警列表只按业务筛选;详情直接按事件 ID 查询 | 列表和详情都通过事件 VIN 校验,越权统一返回 404 |
|
||
| 导出 | 任务没有 owner/customerId,列表展示全局任务,知道 ID 即可下载 | 任务固化创建者、客户、VIN Scope 和快照版本;列表/下载校验所有权 |
|
||
| 运维质量、接入管理、规则配置 | 只按 viewer/operator/admin 控制 | 外部客户主体默认禁止,不能因拥有 viewer 角色而开放 |
|
||
| 车辆档案写入、同步 | 管理员可写 | 仅内部管理员或受信同步服务可用,客户主体永远禁止 |
|
||
|
||
尤其是导出任务当前保存在全局 `exports` Map 和 `jobs.json`,`ListHistoryExports()` 不区分创建者,`HistoryExportFile(id)` 也不校验主体;告警详情同样存在按 ID 直接读取的问题。这两处在客户入口上线前必须按 P0 修复。
|
||
|
||
## 5. 推荐集成架构
|
||
|
||
### 身份链路
|
||
|
||
外部客户登录仍由 OneOS 管理,OneOS 负责账号启停和密码策略。车辆数据中台不复制客户密码。
|
||
|
||
推荐两种方案,优先级如下:
|
||
|
||
1. OneOS Gateway 验证 Sa-Token 后,向车辆数据中台转发由共享密钥签名的身份声明,包含 `userId`、`customerId`、`tenantId`、`role`、过期时间和请求 ID。
|
||
2. 车辆数据中台收到 OneOS Access Token 后,调用 OneOS 内部会话校验接口获得同样的身份声明。
|
||
|
||
禁止信任浏览器直接传入的 `customerId`、客户名或 VIN 列表。
|
||
|
||
### 车辆范围同步
|
||
|
||
OneOS 对外提供内部服务接口,返回客户当前授权车辆快照及版本:
|
||
|
||
```json
|
||
{
|
||
"customerId": 1001,
|
||
"version": "2026-07-14T12:00:00.000Z",
|
||
"vehicles": [
|
||
{
|
||
"vehicleId": 2001,
|
||
"vin": "...",
|
||
"plateNumber": "...",
|
||
"operationStatus": "...",
|
||
"modelName": "...",
|
||
"brandName": "...",
|
||
"contractId": 3001,
|
||
"contractCode": "...",
|
||
"projectName": "...",
|
||
"scopeStartAt": "..."
|
||
}
|
||
]
|
||
}
|
||
```
|
||
|
||
车辆中台保存只读投影,例如:
|
||
|
||
- `business_customer`
|
||
- `business_customer_vehicle_scope`
|
||
- `business_scope_sync_run`
|
||
- `business_scope_audit`
|
||
|
||
所有车辆 API 在 Store/SQL 层统一追加 VIN Scope,不能逐个页面自行过滤,也不能先查询全量后在前端过滤。
|
||
|
||
推荐把范围变成显式的 `QueryScope` 参数,而不是只放在 `context.Context` 中:Store 接口若没有 Scope 参数就无法编译,从机制上减少新增查询忘记加权限的概率。MySQL 查询优先 JOIN 本地范围投影表;TDengine 单车查询先做 VIN 授权检查,多车查询使用已授权 VIN 集合并设置数量上限。空 Scope 对外部客户必须恒等于“无数据”,绝不能解释为“不限制”。
|
||
|
||
### 数据请求原则
|
||
|
||
- 身份声明决定客户 ID。
|
||
- 客户 ID 决定 VIN Scope。
|
||
- VIN Scope 再约束监控、车辆、轨迹、历史、里程、告警、导出和地图聚合查询。
|
||
- 内部管理员可显式选择客户;外部客户不能覆盖身份中的客户 ID。
|
||
- 当 Scope 服务不可用或同步数据过期时,外部访问应失败关闭,不得降级为全量数据。
|
||
|
||
## 6. 分阶段实施
|
||
|
||
### 阶段 A:只读数据审计
|
||
|
||
- 执行 `oneos-readonly-audit.sql`。
|
||
- 确认生产表结构是否仍有 `vehicle_info.customer_id`。
|
||
- 统计客户账号覆盖率、启用率、角色覆盖率。
|
||
- 统计当前交付车辆的 VIN 完整率、客户完整率和客户档案匹配率。
|
||
- 抽样验证丙方客户、换车、已还车和多合同场景。
|
||
|
||
验收:形成数据质量基线,业务负责人确认“当前客户车辆”的唯一口径。
|
||
|
||
### 阶段 B:修复 OneOS 权威接口和账号能力
|
||
|
||
该阶段已停止:用户明确要求不修改 `ln-asset-management` 任何逻辑。以下内容仅保留为已知风险清单,不执行。
|
||
|
||
- 修复 `listByCustomerId`,按有效客户和交还车边界查询。
|
||
- 增加批量客户车辆快照接口,返回 VIN 和业务版本。
|
||
- 统一有效客户规则,覆盖丙方客户。
|
||
- 修复订单记录 Mapper 字段遗漏,并禁止合同更新覆盖已经发生的交还车事实。
|
||
- 对范围异常记录给出隔离清单,不将不确定归属车辆授权给任何外部客户。
|
||
- 补充客户账号类型、角色、启停和存量账号补偿。
|
||
- 新增专用业务账号开通能力,移除共享默认密码和对公开注册开关的依赖。
|
||
- 增加双客户越权测试。
|
||
|
||
验收:客户 A、B 的车辆集合互斥;已还车辆立即退出范围;接口默认失败关闭。
|
||
|
||
### 阶段 C:车辆数据中台身份和 Scope 投影
|
||
|
||
- 扩展 Principal,加入主体类型、外部用户 ID、客户 ID 和租户 ID。
|
||
- 增加 OneOS 身份校验或可信代理声明校验。
|
||
- 建立客户与车辆范围投影、版本和审计表。
|
||
- 在底层查询构建器统一注入 VIN Scope。
|
||
- 覆盖监控、车辆、轨迹、历史、统计、告警、导出、地图聚合和反向地理编码相关入口。
|
||
|
||
验收:任何 URL 参数、VIN 搜索、批量接口和导出均不能越权;Scope 过期时外部请求拒绝访问。
|
||
|
||
### 阶段 D:客户门户和入口
|
||
|
||
- 外部子域名提供独立登录页。
|
||
- 内部 OneOS 菜单以新标签页进入车辆数据中台。
|
||
- 外部页面隐藏接入配置、运维质量和管理型功能。
|
||
- 客户页默认展示授权车辆数量、实时地图、轨迹和里程统计。
|
||
|
||
验收:内部入口统一,外部入口独立;账号停用后现有会话及时失效。
|
||
|
||
### 阶段 E:历史里程补录
|
||
|
||
- 定义补录文件格式和来源平台。
|
||
- 以 VIN、自然日、来源和导入批次保证幂等。
|
||
- 导入约 20 天缺失里程并输出成功、冲突、缺 VIN 和异常跳变报告。
|
||
- 不伪造无法取得的历史轨迹,页面明确轨迹数据起始时间。
|
||
|
||
验收:补录前后总量可对账,重复导入不重复累计,每条补录记录可追溯。
|
||
|
||
## 7. 需要修改 OneOS 前必须同步的范围
|
||
|
||
预计至少涉及以下模块,尚未进行任何修改:
|
||
|
||
- `ln-asset-management/ln-asset-api`
|
||
- `ln-asset-management/src/main/java/com/ln/asset/api/impl/RemoteVehicleServiceImpl.java`
|
||
- `ln-asset-management` 的车辆/合同 Mapper 与 Service
|
||
- `ln-cloud/ruoyi-common-core` 的用户类型定义
|
||
- `ln-cloud/ruoyi-auth` 的客户登录策略
|
||
- `ln-cloud/ruoyi-system` 的客户账号角色和状态管理
|
||
- 可能新增 OneOS 内部 HTTP API、数据库索引和菜单脚本
|
||
|
||
修改前需要先同步:具体文件、接口契约、SQL 变更、兼容方案、回滚方案和测试范围。
|