文件
hl-api-changelog/changelogs-v2/2026-09/24_frontend_核心订单首次排车统一选择车辆司机候选全空canonicalSnapshot空派车组被判非法-前端缺陷-管理后台.md
T

7.1 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 frontend 核心订单首次排车:统一选择车辆/司机弹窗候选全空,报「canonicalSnapshot 缺少完整的代际、稳定派车组或服务日期」 admin wx(GIT) 前端缺陷 not_required not_required verified mmg 75c053035247e577f2646610c93abc7ee6e641bc v2.1 2026-09-24 2026-09-24 同事报「核心订单的车务排车不对」,定位为前端缺陷:尚未产生任何派车行的订单(看板「待派车」)第一次打开「统一选择车辆/司机」时,GET 候选接口返回的 canonicalSnapshot.retainedGroupIds 是空数组(#7067 去槽位化后首次排车本就没有派车组),而前端 normalizeCanonicalAssignmentSnapshot 把空数组判为快照非法直接 throw,整条候选响应作废——车辆候选、司机候选都拿不到,弹窗显示红色报错与「没有符合筛选条件的司机」。后端接口本身正常(车辆/司机候选都返回了,用同一份响应喂 dailyPlan 提交后四步链可跑通),需要前端放宽校验。 前端已修复并验证:normalizeCanonicalAssignmentSnapshot 撤掉 retainedGroupIds 非空校验(槽位时代遗留),空组+空格合法放行、空组带格仍判越界、非空组缺格仍判矩阵不完整;canonical-assignment-snapshot spec 17 例全绿(新增 2 例),hl-admin@75c05303。 2026-09-24 dev-v3

车务派单看板:核心订单首次排车「统一选择车辆/司机」候选全空(canonicalSnapshot 空派车组被前端判非法)

页面: 管理后台 → 车务 → 派单看板 →「统一选择车辆/司机」弹窗(四步向导第②步) 文件: src/views/fleet/board/utils/canonical-assignment-snapshot.js(normalizeCanonicalAssignmentSnapshot,hl-ui v2.1;测试服部署产物 assets/AssignModal-*.js 内函数 yt) 日期: 2026-09-24 影响范围: 前端。所有「当前用车需求下还没有任何派车行」的订单——即看板「待派车」的全部订单(本次实测的 26-0013 就是这种),团期子订单同一代码路径同样命中


一、现象

同事报「核心订单的车务排车不对」,截图里弹窗顶部是红字:

canonicalSnapshot 缺少完整的代际、稳定派车组或服务日期

且弹窗里 车辆候选和司机候选都是空的(右侧显示「没有符合筛选条件的司机」),车务因此完全无法为该订单选车排车。

实例:订单 HL20260924152729671(团号 26-0013,客户「谢谢」,2026-09-30 ~ 2026-10-03,看板状态「待派车」)。

二、复现与证据(测试服实测,只读)

  1. 车务角色登录 → GET /admin/fleet/board/orders/2103023501973848066:requirementId=2103024417066123265、vehicleSlots=[]、activeAssignments=[](该单尚未派车);
  2. POST /admin/fleet/assignments/candidates(前端同形请求体)→ HTTP 200 / code 200,data.canonicalSnapshot:
{
  "planGeneration": "361414334036971520",
  "snapshotVersion": 1,
  "retainedGroupIds": [],
  "editableServiceDates": ["2026-09-30", "2026-10-01", "2026-10-02", "2026-10-03"],
  "cells": [],
  "selectedVehicle": null,
  "selectedDriver": null
}

同一份响应里 data.vehicles.records 与 data.drivers.records 都是有数据的(车辆候选带车牌/车型/协议价,司机候选带姓名/脱敏手机号)——即后端没有出错,是前端在校验阶段把整份响应丢掉了。

  1. 结论:normalizeCanonicalAssignmentSnapshot 的第一个校验分支要求 retainedGroupIds 非空,空数组直接 throw,异常冒泡到候选请求的 catch,于是 canonicalSnapshot = null 之后的赋值全部没执行、apiVehicles/apiDrivers 保持空 → 弹窗只剩报错 + 空候选。

证据文件:D:/work2/_task-evidence/fleet-core-dispatch/evidence/02-candidates-travel-26-0013.json(26-0013 的真实响应) 对照:同一个接口在订单已经有派车行之后返回 retainedGroupIds=["361428998049370112"] + 3 个 cells,前端校验通过、弹窗正常。

三、根因

后端 retainedGroupIds 的语义是 RetainedGroupSet:当前需求下已存在的稳定派车组 ID:

  • #7067「派单去槽位化」之前,订单进入排车时后端会自动建 unassigned 占位槽位行,所以快照里至少有一个槽位,前端「非空」这条前置条件一直成立;
  • #7067 之后不再建占位行,派车行只由第②步「逐日方案提交」产生(Step2CanonicalSnapshotService 类注释:「首次进入且无存活行时 retained 为空集」)。于是第一次排车时 retainedGroupIds=[]、cells=[] 是完全合法的状态,表示「这一版方案还没有任何派车组」,栅格应为空、由车务现场选车生成。

前端的校验仍是槽位时代写的「必须非空」,故把合法空态误判为快照非法。

四、修法(前端)

normalizeCanonicalAssignmentSnapshot 需要接受「空派车组」这一合法状态:

  • planGeneration、snapshotVersion 缺失/为空 → 仍按现在处理(快照不可用);
  • retainedGroupIds 为空数组且 cells 为空数组 → 返回合法快照 {planGeneration, snapshotVersion, retainedGroupIds: [], editableServiceDates, cells: [], selectedVehicle, selectedDriver},不要 throw;
  • 只有「retainedGroupIds 非空但 cells.length != groups × dates」「cells 越界/重复」这类不一致才继续判非法;
  • editableServiceDates 为空的判断保持不变(没有服务日确实无法排车)。

下游要注意的只有一处:空 retainedGroupIds 时 reconcileCanonicalAssignmentDraft 会算出「0 张卡片」,这正是期望结果——车务在弹窗里选车/司机 → 应用后走本地新草稿(isNewDraft)→ 第②步提交照旧走稀疏 dailyPlan(提交体只带 serviceDate+vehicleId+driverId,与派车组无关)。不要把空数组退化成「快照不可用 → canonicalSnapshot=null 的旧模式」,去槽位化后旧模式依赖的槽位数据已不再下发,会得到另一个空栅格。

五、请前端确认/回归的点

  1. 打开任意「待派车」订单(如测试服 26-0013)→「统一选择车辆/司机」:不再出现红字报错,车辆/司机候选正常列出;
  2. 在该弹窗选车+选司机 → 应用 → 栅格出现一张待提交卡片 → 提交方案成功(后端 POST /admin/fleet/assignments/batch 返回 200);
  3. 已派车订单再打开同一弹窗:行为与现在一致(栅格回显已有派车组的日期格);
  4. 建议补一条单测:canonicalSnapshot.retainedGroupIds=[] + cells=[] → 归一化通过且 retainedGroupIds 为 [](现有用例全是非空快照,所以这条错法在测试里看不见)。

六、后端改动

无(backend_status: not_required)。后端候选接口返回的数据本身正确,本次只需前端放宽校验。