# 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 变更、兼容方案、回滚方案和测试范围。