Files
lingniu-vehicle-ingest/vehicle-data-platform/docs/oneos-integration-analysis.md

302 lines
18 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 车辆业务与车辆数据中台集成分析
完整系统、模块、接口、安全、数据库和运维分析见 `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. 存量补偿任务只处理缺失/异常账号,输出成功、冲突、跳过清单,不覆盖现有密码。
### P0OneOS 地图和车辆查询没有客户级数据隔离
- `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 变更、兼容方案、回滚方案和测试范围。