---
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-service（8087）
> **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
