28 KiB
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 | 8114 | 派单操作时间线新增司机拒接记录留痕 | admin | wx(GIT) | 修改接口 | deployed | not_required | not_required | backend_status=deployed: hl-fleet-service 测试服部署 sha=10ed12133,与本单 PR #8139 的合并提交完全一致,STATE=ok(该结论由管理者侧执行 deploy-status.sh 核验后转达,本会话未持有目标机器 SSH 权限、未亲自运行该脚本,如实披露)。gateway_status=not_required 特指网关路由层:driver-reject 写口与 operation-log 读口均为存量路由,本单未新增/改动任何 hl-gateway 路由配置;本会话对 driver-reject 写口做过真实网关实测(见八节,2026-09-22 04:00 前后),命中的是「非 holding 状态」守卫分支(605020),该守卫早于本单存在、与本单新增代码不重叠;受限于测试环境仅有 #5827 后新建的 assigned 态数据、没有存量 holding 派单,未能网关实测出「拒接成功→写入 driver_rejected→读侧可见」这条正向链路,该正向链路的验证依据是 AC-1/AC-2 的真库集成测试(H2 MODE=MySQL + 真 Flyway + 真 MyBatis + 真 AssignmentService Bean),非网关活测,已在三/八节如实注明并附逐字 dump。frontend_status=pending: 未获得 mmg 对 operation_type 白名单现状的新鲜核验,不认定 not_required。 | 2026-09-22 | dev-v3 |
fleet: 派单操作时间线新增司机拒接记录留痕
存放目录:
changelogs-v2/2026-09/服务: hl-fleet-service (端口 8003) PR: #8139 Issue: #8114 日期: 2026-09-22 影响范围: 司机拒接/车务退回待派链路;派单操作时间线接口返回的操作记录
⚠️ 关键变化
司机拒接/车务退回待派(POST /admin/fleet/assignments/{assignmentId}/driver-reject,仅 holding 状态可调用,成功后派单回到 unassigned)以前在 fleet_assignment 表清空车辆/司机字段后不留任何痕迹。现已补齐写口留痕:fleet_assignment_operation_log 新增 driver_rejected 操作类型——与 #8068 已上线的 soft_cleared 是两个独立取值,不共用。
前端影响:派单看板订单卡片「查看日志」时间线会新增这类记录。如果前端按 operation_type 做了白名单过滤,driver_rejected 需要加进去——不加会静默漏渲染。
覆盖边界:driver_rejected 只在司机拒接/退回待派成功执行时写入,而该写口只接受 holding 状态的派单(组)。自 #5827 起,新建派单提交即派定,落库恒为 assigned(holding 仅剩发版前的存量数据)——因此在测试环境用新建的派单去调 driver-reject 会拿到 605020(本会话已实测复现,见八节),不会触发这条新留痕;能触发它的只有 #5827 发版前遗留、目前仍停留在 holding 的存量派单(组)。
一、背景
fleet_assignment 的 vehicle_id / driver_id 会因两类写口被清空:车务手动软清(#8068,已补齐留痕)与司机拒接/车务退回待派(本单)。此前拒接写口(AssignmentService#doDriverRejectInLock,覆盖 updateDriverReject 单派与 updateDriverRejectGroup 组派两条路径)在 CAS 清空成功后不写任何操作日志,事后无法查证「是谁拒的、拒掉的是哪位司机哪台车」——与 #8068 描述的是同一个洞,只是入口不同(工单 #8051 #8068 #8114)。
本次补齐留痕机制:每次拒接成功都在 fleet_assignment_operation_log 写一行 operation_type = driver_rejected,记录被清空前的身份快照及操作范围,形状与 #8068 的 soft_cleared 一致(锚点行 + clearedRows[]),但两者是两个独立的枚举取值——读侧 AssignmentOperationLogQueryService 没有 operation_type 白名单,共用会让「车务主动清空」与「司机拒接退回」在时间线上无法区分,而两者的责任归属不同。
触发本次留痕的写口端点本身未变:POST /admin/fleet/assignments/{assignmentId}/driver-reject(单派/组派共用同一端点,按当前派单是否属于某个派车组自动分流),请求体/响应结构/错误码均未调整。
二、变更接口清单
| # | 接口 | 方法 | 路径 | 变更类型 | 说明 |
|---|---|---|---|---|---|
| 1 | 派单操作时间线 | GET | /admin/fleet/orders/{orderId}/operation-log |
响应新增操作类型 | 新增 driver_rejected 枚举值 + detail_json 字段扩展 |
三、接口详情
1. 派单操作时间线 GET /admin/fleet/orders/{orderId}/operation-log
VO: FleetOperationLogItemVO → FleetOrderOperationLogRespVO
使用场景
车务在派车看板订单卡片内点「查看日志」,实时呈现该订单全部派车操作的不可变时间线。新操作类型 driver_rejected 会在司机拒接/车务退回待派成功执行后出现在此时间线中。
入参
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|---|---|---|---|---|---|
| orderId | Path | Long | ✅ | 雪花 ID | 订单 ID |
| page | Query | Long | ❌ | 默认 1,上限 100000 | 分页页码 |
| pageSize | Query | Long | ❌ | 默认 50,上限 200 | 每页条数 |
| keyword | Query | String | ❌ | 最长 32 | 按操作人/摘要模糊匹配 |
| sortBy | Query | String | ❌ | 如 time,desc / time,asc |
排序字段,默认按 create_time 倒序 |
| startDate / endDate | Query | LocalDateTime | ❌ | ISO 格式 yyyy-MM-dd'T'HH:mm:ss |
时间范围过滤 |
出参 Result<FleetOrderOperationLogRespVO>
| 字段 | 类型 | 说明 |
|---|---|---|
| data.records | List | 当前页操作日志行 |
| data.records[].id | String | 日志行 ID(雪花号,以字符串返回防精度丢失) |
| data.records[].time | LocalDateTime | 操作时间 |
| data.records[].opType | String | 操作类型英文枚举值,含新增的 driver_rejected |
| data.records[].opTypeLabel | String | 操作类型中文标签(由后端从枚举翻译,driver_rejected → "司机拒接/退回待派") |
| data.records[].summary | String | 可读摘要(固定文案:"司机拒接/退回待派,清空车与司机,派车行置待选择") |
| data.records[].operatorName | String | 操作人(企微名优先,无则用户名,系统动作显示"系统") |
| data.records[].effectiveDate | LocalDate | 生效日期(取锚点行的 serviceDate) |
| data.records[].detailJson | String | 明细 JSON(字符串化),格式见下节 |
| data.total | Long | 总条数 |
| data.page | Integer | 当前页码 |
| data.pageSize | Integer | 本页条数 |
新增字段详情
opType 新增枚举值
| 值 | 中文标签 | 触发场景 |
|---|---|---|
driver_rejected |
司机拒接/退回待派 | 司机拒接本次排车锁定,或车务确认锁定无效,调用 driver-reject 端点成功清空车辆/司机字段 |
detailJson 字段(当 opType="driver_rejected" 时)
响应的 detailJson 是字符串化的 JSON。字段与顺序如下(取自真实集成测试落库结果,逐字未改;单派场景,DriverRejectWritebackTraceIntegrationTest AC-1):
{"scope":"SINGLE","assignmentGroupId":"8114100001","statusBefore":"holding","rejectReason":"司机临时车辆抛锚,退回待派","clearedRowCount":1,"vehicleIdBefore":"8114300001","driverIdBefore":"8114400001","vehiclePlateSnapshot":"蒙A-81141","driverNameSnapshot":"拒接司机甲","clearedRows":[{"assignmentId":"8114100001","serviceDate":"2031-07-03","vehicleIdBefore":"8114300001","vehiclePlateBefore":"蒙A-81141","driverIdBefore":"8114400001","driverNameBefore":"拒接司机甲"}]}
组派场景(一次拒接影响整组多行,AC-2):
{"scope":"GROUP","assignmentGroupId":"8114200001","statusBefore":"holding","rejectReason":"司机拒接整组行程,退回待派","clearedRowCount":2,"vehicleIdBefore":"8114300011","driverIdBefore":"8114400011","vehiclePlateSnapshot":"蒙A-81142","driverNameSnapshot":"拒接司机乙","clearedRows":[{"assignmentId":"8114100011","serviceDate":"2031-07-10","vehicleIdBefore":"8114300011","vehiclePlateBefore":"蒙A-81142","driverIdBefore":"8114400011","driverNameBefore":"拒接司机乙"},{"assignmentId":"8114100012","serviceDate":"2031-07-11","vehicleIdBefore":"8114300012","vehiclePlateBefore":"蒙A-81143","driverIdBefore":"8114400011","driverNameBefore":"拒接司机乙"}]}
字段说明(LinkedHashMap 插入顺序,来自 AssignmentService#clearedIdentitySnapshot):
| 字段 | 类型 | 说明 |
|---|---|---|
| scope | String | 拒接范围,SINGLE 单派、GROUP 按派车组整组拒接 |
| assignmentGroupId | String | 派车组 ID(字符串格式;见下方「关键说明 1」的类型对照) |
| statusBefore | String | 清空前状态,恒为 holding(拒接只放行 holding → unassigned 这一种迁移,非 holding 一律 605020,不写日志) |
| rejectReason | String | 拒接/退回原因,与请求体 rejectReason 同一份取值(仅存证,不参与任何判定) |
| clearedRowCount | Integer | 本次清空涉及的派车行数(单派恒为 1,组派为组内行数) |
| vehicleIdBefore | String | 清空前的车辆 ID(锚点行,字符串返回) |
| driverIdBefore | String | 清空前的司机 ID(锚点行,字符串返回) |
| vehiclePlateSnapshot | String | 清空前的车牌号(可读值快照) |
| driverNameSnapshot | String | 清空前的司机名(可读值快照) |
| clearedRows | Array | 本次实际被清空的每一行明细:assignmentId / serviceDate / vehicleIdBefore / vehiclePlateBefore / driverIdBefore / driverNameBefore(组派时逐切片各自的车/司机可能不同,不是锚点行的复制,见下方「关键说明 2」) |
关键说明 1:所有 ID 字段都以字符串返回,但 DB 列类型不代表 JSON 字段类型
detail_json 里的 vehicleIdBefore / driverIdBefore / assignmentId / clearedRows[].assignmentId 等 ID,均由后端 AssignmentService#idText(Object) 转换为字符串后再落 JSON(javadoc 原文:「可空 ID 转字符串(雪花 ID 落 JSON 防 JS 精度丢失;null 原样保留)」)。雪花 ID 是 19 位数字,JavaScript 的 Number 只能精确表示到 16 位,若以数字解析会在末位静默丢精度、无任何报错提示。
⚠️ 不要把 DB 列类型和 JSON 字段类型混为一谈:fleet_assignment_operation_log 表的 assignment_group_id 列在数据库里是 BIGINT NOT NULL(这一列是这一行日志自身的分组归属,落值就是数字,不在 detail_json 里);而 detail_json 内部的 assignmentGroupId 键是经 idText(...) 转换后的字符串——两者字段名相近,是两个不同的东西。前端通过这个 JSON 接口只会消费到后者(字符串),不会直接看到 DB 列。
关键说明 2:组拒接只写一行——锚点 + clearedRows[],不是逐槽位各写一条
一次组派拒接会清空组内全部行(上面示例是 2 行),但 fleet_assignment_operation_log 只 INSERT 一行:锚点行(组内第一行)写 operation_type=driver_rejected,组内其余被清空的行不会各自再产生一条独立的日志行,它们清空前的身份只出现在这一行的 detail_json.clearedRows[] 数组里。
实测依据(AC-2):把查询条件放宽成只按 order_id 扫描整张表(不加 assignment_id 过滤、不加 operation_type 过滤),针对一次影响 2 行的组拒接,整单也只返回 1 行日志(assignment_id 为锚点行 8114100011),组内另一行 8114100012 名下没有独立的日志行。⇒ 前端如果按 assignmentId 去时间线表里找某一行派车行「自己的」拒接记录,组内非锚点行是找不到的,需要改为解析锚点行的 clearedRows[]。
关键说明 3:若按 operation_type 白名单过滤,需要加入新取值
时间线读侧(AssignmentOperationLogQueryService)没有 operation_type 白名单,新取值会自动随查询结果返回。但如果前端渲染时自行对 operation_type 做了白名单过滤,必须把 driver_rejected 加入白名单,否则这条记录会被过滤掉、静默漏渲染(不报错、不提示)。
关键说明 4:新记录只出现在存量 holding 派单(组)被拒接时
产生 driver_rejected 记录的前提是 driver-reject 写口调用成功,而该写口只接受 holding 状态的派单(组)——FleetAssignmentMapper.updateDriverReject / updateDriverRejectGroup 均带 .eq(assignment_status, holding) 的 CAS 条件,非 holding 一律返回 605020、不写任何日志。自 #5827 起新建派单提交即派定,落库恒为 assigned(holding 仅剩发版前的存量数据,CreateAssignmentCommand javadoc 原文:"落库目标态:#5827 起提交即派定,最终态恒 assigned(holding 只是同事务内的瞬时中间态)")。⇒ 在测试环境用新建的派单调用 driver-reject 会拿到 605020(本会话已实测复现,详见八节的真实网关响应),不会产生 driver_rejected 记录;要看到这条新记录的渲染效果,需要用 #5827 发版前遗留、目前仍处于 holding 的存量派单(组),或等真实司机拒接场景产生新数据——这是该端点自身的既有前置条件,不是本次改动新增的限制。
请求示例
GET /admin/fleet/orders/8114000001/operation-log?page=1&pageSize=50 HTTP/1.1
Host: {后台域名}
Authorization: Bearer {token}
Accept: application/json
响应示例
以下 records[0] 按 FleetOperationLogItemVO 字段映射规则,从 AC-1 集成测试真实落库结果重新组装(detailJson 内容逐字符取自该次真库 IT 结果;id/time/effectiveDate 取自同一行的 operation_id/create_time/effective_date;operatorName 为该 IT 用例夹具下的 actor_name 原值「系统」——集成测试未经过网关鉴权链路、没有设置 operator_id,真实生产场景下这里通常是执行拒接操作的车务人员姓名,机制与其他写口的 operatorName 完全一致,本单未改动。整份响应信封本身未经网关活捕获,参见八节的受限说明):
{
"code": 200,
"message": "成功",
"success": true,
"data": {
"records": [
{
"id": "2102121618417971202",
"time": "2026-09-22T03:43:44",
"opType": "driver_rejected",
"opTypeLabel": "司机拒接/退回待派",
"summary": "司机拒接/退回待派,清空车与司机,派车行置待选择",
"operatorName": "系统",
"effectiveDate": "2031-07-03",
"detailJson": "{\"scope\":\"SINGLE\",\"assignmentGroupId\":\"8114100001\",\"statusBefore\":\"holding\",\"rejectReason\":\"司机临时车辆抛锚,退回待派\",\"clearedRowCount\":1,\"vehicleIdBefore\":\"8114300001\",\"driverIdBefore\":\"8114400001\",\"vehiclePlateSnapshot\":\"蒙A-81141\",\"driverNameSnapshot\":\"拒接司机甲\",\"clearedRows\":[{\"assignmentId\":\"8114100001\",\"serviceDate\":\"2031-07-03\",\"vehicleIdBefore\":\"8114300001\",\"vehiclePlateBefore\":\"蒙A-81141\",\"driverIdBefore\":\"8114400001\",\"driverNameBefore\":\"拒接司机甲\"}]}",
"changeDetail": null
}
],
"total": 1,
"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
- ID 精度:所有雪花号均以字符串返回,前端切勿转为 Number 类型
- operation_type 白名单:若前端自行维护白名单过滤时间线渲染,必须加入
driver_rejected,否则静默漏渲染 - 组拒接留痕形状:一次组拒接只在
fleet_assignment_operation_log里产生一行(锚点行),组内其余行的清空前身份只存在于该行detail_json.clearedRows[]数组内,不能按assignmentId直接从时间线表里查到独立行 - 可达性边界:
driver_rejected只在拒接holding状态的存量派单(组)成功时产生;测试环境新建的派单调用driver-reject恒得605020,不会产生这条记录
四、契约约束与正确调用方式
✅ 正确 / ❌ 错误 payload 对照
此接口为只读 GET,无请求体。正确调用示例:
| 场景 | URL |
|---|---|
| ✅ 第一页,默认排序 | GET /admin/fleet/orders/{orderId}/operation-log?page=1&pageSize=50 |
| ✅ 处理 detailJson 字符串 | JSON.parse(record.detailJson) 转为对象后访问字段 |
| ❌ ID 作为 Number | parseInt(record.id) / Number(detail.vehicleIdBefore) 会导致末位精度丢失 |
| ❌ 按 assignmentId 直查组内非锚点行的独立日志 | 组内非锚点行没有独立日志行,需解析锚点行的 clearedRows[] |
处理 detailJson 的正确方式
// ✅ 正确:先 parse,再按需读取 driver_rejected 专属字段
const detail = JSON.parse(record.detailJson);
if (record.opType === 'driver_rejected') {
const rejectReason = detail.rejectReason; // 拒接原因
const clearedRows = detail.clearedRows; // 组内逐行明细,SINGLE 场景长度恒为 1
const vehicleId = detail.vehicleIdBefore; // String,保持精度,不转 Number
}
// ❌ 禁止转数字
const id = Number(detail.vehicleIdBefore); // 末位被四舍五入
operation_type 白名单排查清单(若前端有)
- 渲染时间线的组件是否存在
operation_type白名单/枚举映射? - 若存在,是否已加入
driver_rejected→ "司机拒接/退回待派"? - 图标/颜色映射表是否需要为
driver_rejected配一个默认展示(未配置时不应崩溃或空白)?
五、数据库行为
| 场景 | 数据库表 | 操作 |
|---|---|---|
| 司机拒接(单派)执行成功 | fleet_assignment |
UPDATE vehicle_id = null, driver_id = null 等字段(CAS 条件 assignment_status = holding) |
| 司机拒接(组派)执行成功 | fleet_assignment |
按 assignment_group_id 批量 UPDATE 组内多行,同上 CAS 条件 |
| 拒接留痕写口 | fleet_assignment_operation_log |
INSERT 一行,operation_type = 'driver_rejected',detail_json 记录清空前的身份(锚点行 1 条,覆盖整组) |
六、边界行为
- 派单不存在 →
605009 - 非
holding状态 →605020(不允许退回待派;自 #5827 起新建派单落库恒为assigned,holding仅剩发版前的存量数据——对新建派单调用会必得605020,这不是缺陷,见「关键说明 4」) rejectReason为空 →400001- 旧 HOLD 通知结果正在确认中 →
605042(可稍后重试;此次不改变派单状态、不清空车辆/司机身份、不改变资源占用,也不写留痕) - 无鉴权 →
401(网关拦截);权限不足(非车务/管理员)→403 - 历史数据:本次新增的
driver_rejected仅出现在部署后发生的拒接成功操作中,不影响此前已有的操作日志记录
六.5、枚举 / 数据字典
operation_type 枚举(AssignmentOperationTypeEnum)
所属字段: FleetOperationLogItemVO.opType | 类型: String
新增值:
| 值 | 中文标签 | 说明 |
|---|---|---|
driver_rejected |
司机拒接/退回待派 | 司机拒接锁定或车务确认锁定无效,调用 driver-reject 成功后清空派车行的车辆/司机字段 |
现有值(不含全列表,仅示例,与 driver_rejected 语义最接近的一项一并列出对照):
| 值 | 中文标签 | 说明 |
|---|---|---|
assignment_created |
新建派单 | 订单新建派单时 |
change_completed |
修改派单完成 | 改派操作完成 |
soft_cleared |
清空司机/车辆 | 车务手动清空派车行的车辆/司机字段(#8068,与 driver_rejected 是同形状但独立的取值,不共用) |
confirmed |
确认执行 | 司机确认执行派单 |
completed |
完结派单 | 派单完成 |
六.6、修改前后对比
响应字段级对比
| 字段 | 改前 | 改后 | 备注 |
|---|---|---|---|
records[].opType |
不含 driver_rejected |
新增 driver_rejected |
前端若按白名单过滤需要加入 |
records[].detailJson |
已有其他操作类型的内容 | 新增 opType="driver_rejected" 时的结构 |
含 rejectReason 等专属字段,见三节 |
行为级对比
| 行为 | 改前 | 改后 |
|---|---|---|
| 司机拒接/退回待派留痕 | 派车行 vehicle_id/driver_id 直接清空,无操作日志 |
调用 driver-reject 成功后同事务在 fleet_assignment_operation_log 写一行,记录清空前的车/司机 |
| 时间线查询 | 拒接操作不可见 | 拒接操作以 driver_rejected 行显示在时间线中 |
| 事后取证 | 无法查证谁拒的、拒掉了谁 | detail_json 中记录清空前的 vehicleId/driverId 等快照,支持事后追溯 |
六.7、影响评估
-
是否破坏向后兼容:否
- 新增操作类型
driver_rejected不影响既有类型的解析 - 响应字段无删除,仅新增返回内容(仅当
opType=driver_rejected时出现新的detail_json结构)
- 新增操作类型
-
前端是否必须同步上线:否(但需适配白名单过滤)
- 后端接口变更无必须的前端代码改动
- 但是:如果前端渲染时间线时对
operation_type做了白名单过滤,白名单中必须加入driver_rejected,否则该操作会静默漏渲染
-
前端 workaround 清理点:
- 若有硬编码的
operation_type白名单,需补充'driver_rejected' - 若用了枚举常量或字典,确保下发的字典已包含
driver_rejected标签
- 若有硬编码的
-
覆盖边界(前端自测数据来源受限,需知悉):
driver_rejected只在「存量holding派单(组)」被拒接成功时产生。自 #5827 起,新建派单提交即派定,落库恒为assigned——因此对着测试环境新建的派单调用driver-reject会返回605020,不会产生这条新记录;能触发它的只有 #5827 发版前遗留、目前仍处于holding的存量派单(组)。- 前端要验证
driver_rejected的渲染效果,需要去找这类存量数据(或等真实司机拒接场景产生新数据),而不是自己新建一条派单来复现——这是该端点自身的既有前置条件(holding状态限定,非本单引入),不是本次改动新增的限制。
七、不影响范围
- 仅影响:派单操作时间线接口
/admin/fleet/orders/{orderId}/operation-log的响应内容 - 零影响:
driver-reject端点自身的请求体/响应结构/错误码(一律未改)updateDriverReject/updateDriverRejectGroup两个 Mapper default 方法(一行未改)- 派车创建/改派/确认等其他业务流程
- 订单详情接口
- 其他模块的操作日志接口(如房务)
- 前端派车列表/派车详情等其他功能模块
八、测试环境已验证
后端部署:hl-fleet-service 测试服部署 sha 10ed12133,与本单 PR #8139 的合并提交完全一致,STATE=ok。⚠️ 该结论由管理者侧执行 deploy-status.sh 核验后转达,本会话未持有目标机器的 SSH 访问权限、未亲自运行该脚本,如实披露。
网关实测(本会话 2026-09-22 04:00 前后,真实发起,非构造):
对一条刚通过 POST /admin/fleet/assignments/batch 新建、状态为 assigned 的派单(assignmentId=2102125764705181697,04:00:12 创建)调用拒接端点:
POST /admin/fleet/assignments/2102125764705181697/driver-reject
↓
HTTP 200,code=605020,msg=当前派单状态不允许此操作
这与「关键说明 4」描述的可达性边界完全吻合:新建派单落库恒为 assigned,非 holding 一律 605020。同时执行了两组反例校验:
POST /admin/fleet/assignments/2102125764705181697999/driver-reject(ID 格式非法)
↓ HTTP 200,code=400,msg=参数 assignmentId 格式错误,请检查后重试
POST /admin/fleet/assignments/2102125764705181698/driver-reject(格式合法但不存在)
↓ HTTP 200,code=605009,msg=派单不存在
调用前后对该派单行与 fleet_assignment_operation_log 的 SELECT 复核:拒接调用前后该行 vehicle_id/driver_id/assignment_status 均无变化(assigned,2085539421276286978/2065272150012444674),operation_type='driver_rejected' 的行数前后均为 0——确认失败调用没有副作用、也没有写出留痕,符合预期。
受限说明:本会话测试环境内没有可用的存量 holding 派单(组),因此上面这组网关实测只覆盖了 driver-reject 的失败分支,没有现场网关实测出「拒接成功 → 写入 driver_rejected → 时间线读侧可见」这条正向链路(该限制本身正是「关键说明 4」所述的可达性边界,不是本次验证的疏漏)。正向链路的验证依据是集成测试真库取证(见下):
用例:DriverRejectWritebackTraceIntegrationTest(test profile 的 H2 MODE=MySQL + 真 Flyway DDL + 真 MyBatis 生成 SQL + 真 AssignmentService Bean,非 MySQL 8 容器)。
- 单派(AC-1):拒接前该行
vehicle_id=8114300001, driver_id=8114400001, assignment_status=holding;拒接后同一行vehicle_id=null, driver_id=null, assignment_status=unassigned;同时写出一行fleet_assignment_operation_log(operation_id=2102121618417971202, operation_type=driver_rejected),其detail_json.vehicleIdBefore="8114300001"/.driverIdBefore="8114400001"与清空前的 DB 值一致——排除了「UPDATE 之后再 reload 导致全 null」这一最强反例。 - 组派(AC-2):两个切片清空前分别持有不同的车/司机(
8114300011/8114400011与8114300012/8114400011),拒接后按order_id全表扫描整单只返回 1 行日志(锚点assignment_id=8114100011),第二个切片8114100012名下无独立日志行,两切片各自的清空前身份分别体现在锚点行detail_json.clearedRows[0]与[1]里——确认了「关键说明 2」所述的锚点+clearedRows[]留痕形状。
三节的响应示例即按上述 AC-1 真库 IT 结果、按 FleetOperationLogItemVO 字段映射规则重新组装(非网关活捕获,已在三节标注)。
十、相关文档
- 关联 Issue: wx/HL#8114
- 关联 PR: wx/HL#8139
- 相关工单: #8068(软清留痕,同形状参照实现),#5827(取消司机确认环节,holding 状态自此变为遗留态)
关联 / 联系人
链接
联系人
- 后端负责人: @wx