补充派单操作日志快照契约(#5862)
所有检测均成功
changelog-filename-gate / validate (push) Successful in 2s

这个提交包含在:
lc 2026-08-11 18:48:41 +08:00
父节点 45ea603be0
当前提交 a4fb47b2e3

查看文件

@ -0,0 +1,225 @@
---
schema: "hl-changelog/v2"
ticket: "5862"
title: "派单操作日志补全操作人、操作时间与改派资源快照"
consumer: "admin"
author: "lc(GIT)"
change_type: "修改接口"
backend_status: "deployed"
gateway_status: "verified"
frontend_status: "not_required"
frontend_owner: ""
frontend_ref: ""
target_release: ""
verified_at: ""
status_note: "PR #5869 已合入 dev-v3 并部署 TEST;真实登录经 Gateway 验证操作时间、操作人、动作摘要及历史改派降级契约。现有管理端可直接展示,无前端代码变更。"
updated_at: "2026-08-11"
base: "dev-v3"
---
# 车务派单:操作日志补全操作人、操作时间与改派资源快照
> **服务**: hl-fleet-service8087
> **PR**: #5869
> **Issue**: #5862
> **日期**: 2026-08-11
> **影响范围**: 管理端车务看板订单详情和“查看日志”中的派单操作记录
---
## ⚠️ 关键变化
- 操作日志现在稳定返回 `time``operatorName``opTypeLabel``summary`,操作人按“企业微信姓名 → 系统用户名 → 系统”的顺序解析。
- 改派日志新增结构化 `changeDetail`。新日志保存操作当时的新旧车牌、司机姓名快照,不再要求调用方解析 `detailJson` 中的 ID 后查询当前车辆或司机档案。
- 历史日志没有快照时保持兼容:展示字段允许按 ID 查询当前档案,`dataCompleteness` 明确返回 `HISTORICAL_PARTIAL`,不得把当前档案值误认为操作时快照。
## 一、变更接口清单
| # | 接口 | 方法 | 路径 | 变更类型 | 说明 |
|---|---|---|---|---|---|
| 1 | 派单操作时间线 | GET | `/admin/fleet/orders/{orderId}/operation-log` | 响应增强 | 日志行补全操作信息与结构化改派详情 |
| 2 | 车务看板订单详情 | GET | `/admin/fleet/board/orders/{orderId}` | 响应增强 | `operationLog.records[]` 同步返回相同字段 |
两个接口沿用原有路径、鉴权和分页契约,不新增请求字段。
## 二、接口详情
### 1. 派单操作时间线 `GET /admin/fleet/orders/{orderId}/operation-log`
**响应模型**: `Result<FleetOrderOperationLogRespVO>`
#### 入参
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|---|---|---|---|---|---|
| orderId | Path | String | 是 | Long 字符串 | 订单 ID |
| page | Query | Integer | 否 | 默认 1 | 页码 |
| pageSize | Query | Integer | 否 | 默认 50,最大 200 | 每页条数 |
| sortBy | Query | String | 否 | `time,desc` / `time,asc` | 默认按操作时间倒序 |
| keyword | Query | String | 否 | 最长 32 字 | 按展示后的摘要、操作人等内容搜索 |
| startDate | Query | LocalDateTime | 否 | ISO 8601 | 操作时间起点 |
| endDate | Query | LocalDateTime | 否 | ISO 8601 | 操作时间终点 |
#### 日志行出参
| 字段 | 类型 | 说明 |
|---|---|---|
| id | String / null | 日志 ID;合成里程碑记录可为 null |
| time | String | 操作时间,格式 `yyyy-MM-dd HH:mm:ss` |
| opType | String | 操作类型英文枚举 |
| opTypeLabel | String | 可直接展示的中文动作名称 |
| summary | String | 后端生成的操作摘要,改派时包含前后资源和生效日 |
| operatorName | String | 企业微信姓名优先,无企业微信姓名时使用系统用户名;自动任务为“系统” |
| effectiveDate | String / null | 生效日期,格式 `yyyy-MM-dd` |
| detailJson | String / null | 兼容保留的原始明细 JSON,不建议前端再自行解析 ID |
| changeDetail | Object / null | 改派结构化详情;非改派动作返回 null |
#### `changeDetail` 字段
| 字段 | 类型 | 说明 |
|---|---|---|
| changeDimensions | String[] | 变更维度:`VEHICLE``DRIVER``VEHICLE_FEE` |
| previousVehicleId / newVehicleId | String / null | 原车、新车 ID |
| previousVehiclePlateSnapshot / newVehiclePlateSnapshot | String / null | 操作当时的原车牌、新车牌快照;历史无快照时为 null |
| previousVehiclePlate / newVehiclePlate | String / null | 展示值;优先使用快照,历史数据才按 ID 降级查询当前档案 |
| previousDriverId / newDriverId | String / null | 原司机、新司机 ID |
| previousDriverNameSnapshot / newDriverNameSnapshot | String / null | 操作当时的原司机、新司机姓名快照;历史无快照时为 null |
| previousDriverName / newDriverName | String / null | 展示值;优先使用快照,历史数据才按 ID 降级查询当前档案 |
| effectiveDate / endDate | String / null | 本次变更的生效日和结束日 |
| previousUsedDays | Integer / null | 生效日前原资源在同一槽位已使用的有效服务日数;历史未知为 null |
| affectedServiceDates | String[] | 本次实际变更的服务日期,支持非连续日期集合 |
| affectedDays | Integer / null | 本次实际变更服务日数;历史未知为 null |
| reason | String / null | 变更原因 |
| dataCompleteness | String | `COMPLETE``HISTORICAL_PARTIAL` |
#### 完整快照响应示例
```json
{
"code": 200,
"message": "成功",
"data": {
"records": [
{
"id": "2086386125831561218",
"time": "2026-08-11 15:05:14",
"opType": "change_completed",
"opTypeLabel": "修改派单完成",
"summary": "lc 修改派单完成蒙A-E2E99王信 → 蒙A-E2E01道尔吉,生效日 2026-08-23",
"operatorName": "lc",
"effectiveDate": "2026-08-23",
"detailJson": "{...}",
"changeDetail": {
"changeDimensions": ["VEHICLE", "DRIVER"],
"previousVehicleId": "2086000000000000001",
"previousVehiclePlateSnapshot": "蒙A-E2E99",
"previousVehiclePlate": "蒙A-E2E99",
"previousDriverId": "2086000000000000002",
"previousDriverNameSnapshot": "王信",
"previousDriverName": "王信",
"newVehicleId": "2086000000000000003",
"newVehiclePlateSnapshot": "蒙A-E2E01",
"newVehiclePlate": "蒙A-E2E01",
"newDriverId": "2086000000000000004",
"newDriverNameSnapshot": "道尔吉",
"newDriverName": "道尔吉",
"effectiveDate": "2026-08-23",
"endDate": "2026-08-25",
"previousUsedDays": 2,
"affectedServiceDates": ["2026-08-23", "2026-08-24", "2026-08-25"],
"affectedDays": 3,
"reason": "调整车辆与司机",
"dataCompleteness": "COMPLETE"
}
}
],
"total": 1,
"page": 1,
"pageSize": 50
},
"success": true
}
```
#### 历史日志降级示例
```json
{
"previousVehiclePlateSnapshot": null,
"previousVehiclePlate": "蒙A-E2E99",
"previousDriverNameSnapshot": null,
"previousDriverName": "王信",
"newVehiclePlateSnapshot": null,
"newVehiclePlate": "蒙A-E2E01",
"newDriverNameSnapshot": null,
"newDriverName": "道尔吉",
"previousUsedDays": null,
"affectedServiceDates": [],
"affectedDays": null,
"dataCompleteness": "HISTORICAL_PARTIAL"
}
```
### 2. 车务看板订单详情 `GET /admin/fleet/board/orders/{orderId}`
订单详情原有 `operationLog.records[]` 复用上述日志行模型。本次同步增加并补全 `time``opTypeLabel``summary``operatorName``changeDetail`,字段含义及历史降级规则与独立操作时间线接口一致。
## 三、修改前后对比
### 字段级对比
| 字段 | 修改前 | 修改后 |
|---|---|---|
| time | 部分操作记录展示字段不完整 | 每条可展示记录返回标准操作时间 |
| operatorName | 可能只有泛化角色名或缺失 | 企业微信姓名优先,系统用户名兜底,自动任务显示“系统” |
| summary / opTypeLabel | 动作信息不完整或需要调用方拼装 | 返回中文动作名称和可直接展示的完整摘要 |
| detailJson | 调用方需解析车辆、司机 ID,且查询结果会受当前档案变化影响 | 兼容保留,不再作为稳定展示契约 |
| changeDetail | 无 | 返回新旧车辆、司机、使用天数、影响日期及完整性标记 |
| 车牌与司机姓名 | 可能展示当前档案值,无法证明操作当时名称 | 新日志保存操作时快照;历史日志明确标记降级状态 |
### 行为级对比
| 场景 | 修改前 | 修改后 |
|---|---|---|
| 操作人展示 | 可能无法识别具体人员 | 企微姓名优先,无企微姓名时显示系统用户名 |
| 换车或换司机 | 仅凭 ID 无法稳定还原前后资源 | 直接返回操作当时的新旧车牌与司机姓名快照 |
| 历史无快照 | 容易把当前档案误当作历史事实 | 可降级展示,但通过 `HISTORICAL_PARTIAL` 明确说明不完整 |
| 车辆费用单独变化 | 可能被误写成换车或换司机 | `changeDimensions` 单独标记 `VEHICLE_FEE`,动作摘要按实际维度生成 |
## 四、契约约束与兼容边界
- 调用方应优先读取结构化 `changeDetail``detailJson` 仅为兼容字段,不保证适合作为长期展示数据源。
- `*Snapshot` 是操作时事实;不带 `Snapshot` 的车牌、司机展示字段可能是历史数据按 ID 查询当前档案后的降级值。
- 只有本次操作应保存的车牌和司机姓名快照均齐全时,`dataCompleteness` 才返回 `COMPLETE`;存量日志不会伪造快照。
- 仅变更车辆、仅变更司机、同时变更车辆与司机、仅变更车辆费用,均以 `changeDimensions` 的实际值为准。
- ID 按字符串返回,调用方不得转换为 JavaScript Number。
- 本次不改变接口路径、请求参数、权限、分页、排序和统一 `Result` 响应结构。
- 快照仅包含车牌和司机姓名,不新增手机号、证件号或其他敏感信息。
## 五、前端影响
`frontend_status=not_required`。现有管理端已经可以直接展示操作时间、操作人和后端摘要,本次 TEST 页面已看到“原车牌/原司机 → 新车牌/新司机”的改派结果,无需同步修改前端代码。其他调用方如仍解析 `detailJson`,应逐步改为优先消费 `changeDetail`
## 六、验证与测试证据
- PR #5869 已合入 `dev-v3`,合并提交:`2d9c6ba4f8af1ad997b8b6e3ba7d215b9754ae66`;TEST 的 Fleet 服务双实例健康。
- 使用真实登录身份经 TEST Gateway 调用 `GET /admin/fleet/orders/{orderId}/operation-log` 返回 200。订单 `26-3420` 的 10 条记录均具有非空操作时间、动作、操作人和摘要。
- TEST 管理端订单 `26-6436` 的日志页面直接展示 `蒙A-E2E99/王信 → 蒙A-E2E01/道尔吉`、动作、生效日、操作人和时间。
- 同一订单 3 条存量改派记录均返回 `HISTORICAL_PARTIAL`,且 `changeDetail` 快照、展示值、使用天数和受影响日期等契约字段完整存在,历史无快照字段保持 null。
- #5862 定向自动化测试 620 项通过;Fleet 扩大测试 3604 项通过、0 失败、0 错误、7 跳过;固定端口 MySQL 集成测试 11/11 通过。
- 为避免通知、派单和审计副作用,TEST 验收未提交新的改派操作;新写日志 `COMPLETE` 快照路径由自动化测试覆盖,测试业务数据状态未改变。
- 工单验收标准 14/14 已勾选,验收记录已写入,Issue #5862 已关闭。
## 七、不影响范围
- 不新增或修改数据库结构。
- 不修改管理端、小程序、H5 或其他前端代码。
- 不改变派单状态机、派单确认、司机通知、保险、结算和车辆费用计算规则。
- 不回填或改写历史日志;历史记录只做只读降级展示。
## 关联
- Issue: #5862
- PR: #5869
- 后端合并提交: `2d9c6ba4f8af1ad997b8b6e3ba7d215b9754ae66`
- 联系人: @lc