文件
hl-api-changelog/changelogs-v2/2026-09/21_8068_车务手动软清派车行记录留痕-修改接口-管理后台.md
T

16 KiB
原始文件 Blame 文件历史

schema, ticket, title, consumer, author, change_type, backend_status, gateway_status, frontend_status, frontend_owner, frontend_ref, target_release, verified_at, status_note, updated_at, base
schema ticket title consumer author change_type backend_status gateway_status frontend_status frontend_owner frontend_ref target_release verified_at status_note updated_at base
hl-changelog/v2 8068 派单操作时间线新增软清记录留痕 admin wx(GIT) 修改接口 deployed verified not_required 前端 2026-09-21 实证 not_required:orderLog.js operationKind 为关键字匹配+note 兜底(非 opType 白名单),title 直吃后端 opTypeLabel,OperationLogModal 只读时间线不过滤不解析 detailJson,soft_cleared 与 share_release_cleared 均正常渲染,无静默漏渲染风险;无业务改动。 2026-09-21 dev-v3

fleet: 派单操作时间线新增软清记录留痕

存放目录: changelogs-v2/2026-09/

服务: hl-fleet-service (端口 8003) PR: #8113 Issue: #8068 日期: 2026-09-21 影响范围: 车务手动派车清空链路;派单操作时间线接口返回的操作记录


⚠️ 关键变化

车务手动软清派车行(清空所选行的车辆/司机),以前在 fleet_assignment 表直接清空不留痕迹。现已补齐写口留痕:fleet_assignment_operation_log 新增 soft_cleared 操作类型。

前端影响:派单看板订单卡片「查看日志」时间线会新增这类记录。如果前端按 operation_type 做了白名单过滤,soft_cleared 需要加进去——不加会静默漏渲染。


一、背景

软清是车务人员在派车界面一次性清空多个选中行的车辆/司机字段,状态置回「待改派」。此前的实现直接 UPDATE 字段不写日志,导致事后无法查证「是谁、什么时候、清掉了哪个司机/哪台车」(工单 #8051 #8068 提及)。

本次补齐留痕机制:每次软清都在 fleet_assignment_operation_log 写一行,记录被清空前的身份快照及操作范围。


二、变更接口清单

# 接口 方法 路径 变更类型 说明
1 派单操作时间线 GET /admin/fleet/orders/{orderId}/operation-log 响应新增操作类型 新增 soft_cleared 枚举值 + detail_json 字段扩展

三、接口详情

1. 派单操作时间线 GET /admin/fleet/orders/{orderId}/operation-log

VO: FleetOperationLogItemVO → FleetOrderOperationLogRespVO

使用场景

车务在派车看板订单卡片内点「查看日志」,实时呈现该订单全部派车操作的不可变时间线。新操作类型 soft_cleared 会在此时间线中出现。

入参

字段 位置 类型 必填 约束 说明
orderId Path Long ✅ 雪花 ID 订单 ID
page Query Integer ❌ 默认 1,上限 100000 分页页码
pageSize Query Integer ❌ 默认 50,上限 200 每页条数
sortBy Query String ❌ 支持 time,asc 排序字段,默认 create_time 倒序

出参 Result<FleetOrderOperationLogRespVO>

字段 类型 说明
data.records List 当前页操作日志行
data.records[].id String 日志行 ID(雪花号,以字符串返回防精度丢失)
data.records[].time LocalDateTime 操作时间(ISO 8601 格式)
data.records[].opType String 操作类型英文枚举值,含新增的 soft_cleared
data.records[].opTypeLabel String 操作类型中文标签(由后端从枚举翻译)
data.records[].summary String 可读摘要(后端拼接:操作人 + 动作 + 前后值对比)
data.records[].operatorName String 操作人(企微名优先,无则用户名,系统动作显示「系统」)
data.records[].effectiveDate LocalDate 生效日期(按天改派等动作有值,无则 null)
data.records[].detailJson String 明细 JSON,格式见下节
data.total Long 总条数
data.page Integer 当前页码
data.pageSize Integer 本页条数

新增字段详情

opType 新增枚举值

值 中文标签 触发场景
soft_cleared 清空司机/车辆 车务在派车界面手动清空选中行的车辆/司机字段

detailJson 字段(当 opType="soft_cleared" 时)

响应的 detailJson 是字符串化的 JSON,结构如下(示例已脱敏保留字段结构):

{
  "scope": "GROUP",
  "assignmentGroupId": "360380081354444801",
  "statusBefore": "assigned",
  "clearedRowCount": 1,
  "vehicleIdBefore": "2085539421276286978",
  "driverIdBefore": "2067084362829979650",
  "vehiclePlateSnapshot": "蒙A-E2E99",
  "driverNameSnapshot": "王信",
  "clearedRows": [
    {
      "assignmentId": "2101990265092984833",
      "serviceDate": "2026-12-22",
      "vehicleIdBefore": "2085539421276286978",
      "vehiclePlateBefore": "蒙A-E2E99",
      "driverIdBefore": "2067084362829979650",
      "driverNameBefore": "王信"
    }
  ]
}

字段说明:

字段 类型 说明
scope String 清空范围,GROUP 表示按派车组(团期)整组清空
assignmentGroupId String 派车组 ID(字符串格式,防雪花 ID 过 JS 掉精度)
statusBefore String 清空前状态,通常为 assigned(已派车)
clearedRowCount Integer 本次清空涉及的派车行数
vehicleIdBefore String 清空前的车辆 ID(19 位雪花号,字符串返回)
driverIdBefore String 清空前的司机 ID(19 位雪花号,字符串返回)
vehiclePlateSnapshot String 清空前的车牌号(可读值快照,用于日志展示)
driverNameSnapshot String 清空前的司机名(可读值快照,用于日志展示)
clearedRows Array 本次清空涉及的所有派车行详情,每行包含 assignmentId / serviceDate / 清空前的车辆/司机 ID 与车牌/司机名

关键说明:所有 ID 字段都以字符串返回

派车 ID、司机 ID、车辆 ID 等均采用字符串格式。这很重要:JavaScript 的 Number 类型只能精确表示到 16 位数字,而雪花 ID 是 19 位。若以数字解析,末位会被四舍五入,导致 ID 失真且无任何错误提示。

请求示例

GET /admin/fleet/orders/2085539421276286978/operation-log?page=1&pageSize=50 HTTP/1.1
Host: {后台域名}
Authorization: Bearer {token}
Accept: application/json

响应示例

{
  "code": 200,
  "message": "成功",
  "success": true,
  "data": {
    "records": [
      {
        "id": "2101990265092984834",
        "time": "2026-09-21T18:45:32",
        "opType": "soft_cleared",
        "opTypeLabel": "清空司机/车辆",
        "summary": "车务 王信 清空司机/车辆:蒙A-E2E99王信,派车行 1 行待改派",
        "operatorName": "王信",
        "effectiveDate": null,
        "detailJson": "{\"scope\":\"GROUP\",\"assignmentGroupId\":\"360380081354444801\",\"statusBefore\":\"assigned\",\"clearedRowCount\":1,\"vehicleIdBefore\":\"2085539421276286978\",\"driverIdBefore\":\"2067084362829979650\",\"vehiclePlateSnapshot\":\"蒙A-E2E99\",\"driverNameSnapshot\":\"王信\",\"clearedRows\":[{\"assignmentId\":\"2101990265092984833\",\"serviceDate\":\"2026-12-22\",\"vehicleIdBefore\":\"2085539421276286978\",\"vehiclePlateBefore\":\"蒙A-E2E99\",\"driverIdBefore\":\"2067084362829979650\",\"driverNameBefore\":\"王信\"}]}",
        "changeDetail": null
      },
      {
        "id": "2101990265092984833",
        "time": "2026-09-21T18:45:31",
        "opType": "assignment_created",
        "opTypeLabel": "新建派单",
        "summary": "系统 新建派单:蒙A-E2E99王信,生效日 12/22",
        "operatorName": "系统",
        "effectiveDate": "2026-12-22",
        "detailJson": null,
        "changeDetail": null
      }
    ],
    "total": 2,
    "page": 1,
    "pageSize": 50
  }
}

空数据 / 降级响应

{
  "code": 200,
  "message": "成功",
  "success": true,
  "data": {
    "records": [],
    "total": 0,
    "page": 1,
    "pageSize": 50
  }
}

错误响应

{
  "code": 404,
  "message": "订单不存在",
  "success": false,
  "data": null
}

业务边界

  • 权限:网关 /admin/fleet/** 统一鉴权,车务/管理员可访问
  • 分页上限:pageSize 最高 200,page 最高 100000
  • 排序:默认按 create_time 倒序(新操作在前);支持 sortBy=time,asc 升序
  • ID 精度:所有雪花号均以字符串返回,前端切勿转为 Number 类型
  • 历史数据兼容:该接口是只读时间线,查询时包含所有 operation_type 值(包括本次新增的 soft_cleared)

四、契约约束与正确调用方式

✅ 正确 / ❌ 错误 payload 对照

此接口为只读 GET,无请求体。正确调用示例:

场景 URL
✅ 第一页,默认排序 GET /admin/fleet/orders/2085539421276286978/operation-log?page=1&pageSize=50
✅ 升序时间线 GET /admin/fleet/orders/2085539421276286978/operation-log?sortBy=time,asc&pageSize=50
✅ 处理 detailJson 字符串 JSON.parse(record.detailJson) 转为对象后访问字段
❌ ID 作为 Number parseInt(record.id) 会导致末位精度丢失
❌ 分页超过上限 pageSize=500 → 400 Bad Request

处理 detailJson 的正确方式

detailJson 字段值本身是 JSON 字符串(因为在 SQL 层 detail_json 列是 text 类型,序列化到 JSON 响应时被转义成字符串)。前端需要先 parse 再访问:

// ❌ 错误:直接访问
const vehicleId = record.detailJson.vehicleIdBefore;  // undefined

// ✅ 正确:先 parse
const detail = JSON.parse(record.detailJson);
const vehicleId = detail.vehicleIdBefore;  // "2085539421276286978"

// ✅ 保持字符串,不转 Number
const id = detail.vehicleIdBefore;  // String,保持精度
// ❌ 禁止转数字
const id = Number(detail.vehicleIdBefore);  // 末位被四舍五入

五、数据库行为

场景 数据库表 操作
软清执行 fleet_assignment UPDATE driver_id = null, vehicle_id = null 等字段
软清写口 fleet_assignment_operation_log INSERT 一行,operation_type = 'soft_cleared',detail_json 记录清空前的身份

六、边界行为

  • 订单不存在 → 404
  • 分页参数超范围 → 400(pageSize > 200 或 page > 100000)
  • 无鉴权 → 401(网关拦截)
  • 权限不足 → 403(非车务/管理员)
  • sortBy 值非法 → 忽略,使用默认倒序
  • 历史数据:该接口返回的是操作日志的完整历史,不因本次变更而改变已有记录;本次新增的 soft_cleared 仅出现在 2026-09-21 18:43 之后发生的清空操作

六.5、枚举 / 数据字典

operation_type 枚举(AssignmentOperationTypeEnum)

所属字段: FleetOperationLogItemVO.opType | 类型: String

新增值:

值 中文标签 说明
soft_cleared 清空司机/车辆 车务手动清空派车行的车辆/司机字段,状态置回待改派

现有值(不含全列表,仅示例):

值 中文标签 说明
assignment_created 新建派单 订单新建派单时
change_completed 修改派单完成 改派操作完成
share_release_cleared 共用关系解除释放占用 共用关系解除时,共用组成员释放占用(#8061 新增)
confirmed 确认执行 司机确认执行派单
completed 完结派单 派单完成

六.6、修改前后对比

响应字段级对比

字段 改前 改后 备注
records[].opType 不含 soft_cleared 新增 soft_cleared 前端若按白名单过滤需要加入
records[].detailJson 已有其他操作类型的内容 新增 soft_cleared 时的 detail_json 结构 当 opType 为 soft_cleared 时,detailJson 包含 scope/statusBefore/vehicleIdBefore 等字段

行为级对比

行为 改前 改后
软清留痕 派车行直接清空,无操作日志 调用软清写口时同步在 fleet_assignment_operation_log 写一行,记录被清空前的车/司机
时间线查询 软清操作不可见 软清操作以 soft_cleared 行显示在时间线中
事后取证 无法查证谁清了、清掉了谁 detail_json 中记录清空前的 vehicleId/driverId 快照,支持事后追溯

六.7、影响评估

  • 是否破坏向后兼容: 否

    • 新增操作类型 soft_cleared 不影响既有操作类型的解析
    • 响应字段无删除,仅新增返回内容
  • 前端是否必须同步上线: 否(但需适配白名单过滤)

    • 后端接口变更无必须的前端代码改动
    • 但是:如果前端在渲染时间线时对 operation_type 做了白名单过滤,白名单中必须加入 soft_cleared,否则该操作会静默漏渲染
  • 前端 workaround 清理点:

    • 若有硬编码的 operation_type 白名单(例如 ['assignment_created', 'change_completed', ...]),需补充 'soft_cleared'
    • 若用了枚举常量或字典,确保后台下发的字典已包含 soft_cleared 标签

七、不影响范围

  • 仅影响:派单操作时间线接口 /admin/fleet/orders/{orderId}/operation-log 的响应内容
  • 零影响:
    • 派车创建/改派/确认等业务流程
    • 订单详情接口
    • 派车行状态字段(清空行为本身不变,仍是直接 UPDATE 派车表)
    • 其他模块的操作日志接口(如房务)
    • 前端派车列表/派车详情等其他功能模块

八、测试环境已验证

测试服 hl-fleet-service 版本 dev-v3@9f6443de4(2026-09-21 18:46 部署,STATE=ok)

实测验证(订单 2085539421276286978、派车行已清空):

GET /admin/fleet/orders/2085539421276286978/operation-log?page=1&pageSize=50
  ↓
200 OK

响应中 records[0]:
  {
    "opType": "soft_cleared",
    "opTypeLabel": "清空司机/车辆",
    "operatorName": "王信",
    "time": "2026-09-21T18:45:32",
    "detailJson": "{\"scope\":\"GROUP\",\"assignmentGroupId\":\"360380081354444801\",\"statusBefore\":\"assigned\",\"vehicleIdBefore\":\"2085539421276286978\",\"driverIdBefore\":\"2067084362829979650\",\"vehiclePlateSnapshot\":\"蒙A-E2E99\",\"driverNameSnapshot\":\"王信\", ...}",
    ...
  }
  ✓ opType 正确为 soft_cleared
  ✓ detailJson 包含清空前的 vehicleId/driverId(字符串格式)
  ✓ 快照字段 vehiclePlateSnapshot/driverNameSnapshot 可读

九、特别说明:共用关系解除时的双行记录

共用关系解除(#8061)会在同一次操作中产生两条连续的操作日志:

  1. share_release_cleared — 由共用关系解除的调用点写,答「哪个共用关系、解除原因、采用何种幸存者策略」

    • detail_json 含 shareGroupId / releaseReason / survivorPolicy / vehicleIdBefore 等字段
  2. soft_cleared — 由软清写口本身写,答「派车行被清了、清前是什么」(本次新增)

    • detail_json 含 scope / statusBefore / vehicleIdBefore / driverIdBefore / clearedRows 等字段

这不是重复数据——两行记的是同一次操作的不同侧面,各自的 detail_json 内容完全不同,都有独特的业务含义。前端在渲染时间线时需要正确识别两种类型,包括在任何过滤/搜索逻辑中都要同时考虑这两个 operation_type。


十、相关文档


关联 / 联系人

链接

联系人

  • 后端负责人: @wx