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

94 行
6.7 KiB
Markdown
原始文件 Blame 文件历史

此文件含有模棱两可的 Unicode 字符
此文件含有可能会与其他字符混淆的 Unicode 字符。 如果您是想特意这样的,可以安全地忽略该警告。 使用 Escape 按钮显示他们。
---
schema: hl-changelog/v2
ticket: "frontend"
title: "核心订单首次排车:统一选择车辆/司机弹窗候选全空,报「canonicalSnapshot 缺少完整的代际、稳定派车组或服务日期」"
consumer: admin
author: "wx(GIT)"
change_type: "前端缺陷"
backend_status: "not_required"
gateway_status: "not_required"
frontend_status: "pending"
frontend_owner: ""
frontend_ref: ""
target_release: "v2.1"
verified_at: ""
status_note: "2026-09-24 同事报「核心订单的车务排车不对」,定位为前端缺陷:尚未产生任何派车行的订单(看板「待派车」)第一次打开「统一选择车辆/司机」时,GET 候选接口返回的 canonicalSnapshot.retainedGroupIds 是空数组(#7067 去槽位化后首次排车本就没有派车组),而前端 normalizeCanonicalAssignmentSnapshot 把空数组判为快照非法直接 throw,整条候选响应作废——车辆候选、司机候选都拿不到,弹窗显示红色报错与「没有符合筛选条件的司机」。后端接口本身正常(车辆/司机候选都返回了,用同一份响应喂 dailyPlan 提交后四步链可跑通),需要前端放宽校验。"
updated_at: "2026-09-24"
base: 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 就是这种),团期子订单同一代码路径同样命中
---
## 一、现象
同事报「核心订单的车务排车不对」,截图里弹窗顶部是红字:
```text
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`:
```json
{
"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` **都是有数据的**(车辆候选带车牌/车型/协议价,司机候选带姓名/脱敏手机号)——即后端没有出错,是前端在校验阶段把整份响应丢掉了。
3. 结论:`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`)。后端候选接口返回的数据本身正确,本次只需前端放宽校验。