docs(changelog): 补入 #5849 前端根因——候选证据戳对服务端回显日格恒 null(部署包已核实)
所有检测均成功
changelog-filename-gate / validate (push) Successful in 2s

这个提交包含在:
API Changelog Bot 2026-08-11 18:32:18 +08:00
父节点 2490021161
当前提交 738e239a07

查看文件

@ -12,7 +12,7 @@ frontend_owner: "mmg"
frontend_ref: "" frontend_ref: ""
target_release: "" target_release: ""
verified_at: "" verified_at: ""
status_note: "对应后端工单 #5849本条只交付「后端侧已排除」这一部分的实测证据(测试服 26-3698 网关真实 API + DB 双向核实),前端判定表达式的精确定位仍在进行中,定位完成后会更新本文件。后端若需要为前端补充推进态字段,另发接口类 changelog,不在本条。" status_note: "对应后端工单 #5849根因已定位并在测试服部署包核实:日格「已完成」要求一枚客户端候选证据戳 _candidateEvidence,而从服务端快照重建的既有派车日格该戳恒为 null,唯一逃生口 preservedFinalized 又被 #5851「换版即作废定稿」关闭,导致换版后每个日格都必须重新走一遍选车弹窗才能推进。后端侧已实测排除8/18 返回 available/selectable 均为 true、conflicts 为空)。后端若需为前端补推进态字段,另发接口类 changelog。"
updated_at: "2026-08-11" updated_at: "2026-08-11"
base: "dev-v3" base: "dev-v3"
--- ---
@ -113,22 +113,76 @@ Content-Type: application/json
--- ---
## 三、⚠️ 待确认推测(尚未定位到表达式,供参考不要直接照做) ## 三、✅ 根因已定位:日格要求一枚「候选证据」客户端戳,而服务端加载的既有派车行这枚戳恒为 `null`
三行数据里唯一的差异是「接机/参与」勾选8/17 勾了,另两天没勾),而「日格已完成」正好是 **1/3** **已在测试服部署包 `/var/www/hl-admin/assets/` 核实**(不是本地陈旧源码):`daily-vehicle-plan-CQiA9Iru.js``AssignModal-BWuKA9uB.js` 中逻辑与源码一致
**推测**:前端的「日格完成」判定可能把「接机/参与」勾选当成了必要条件。 ### 3.1 判定链路
> 这条**尚未验证**,请以你那边的实际表达式为准。我们仍在定位「尚未通过当前日期、车辆和司机的候选校验」这句话的判定链路,定位完成后会更新本文件。 `src/views/fleet/board/utils/daily-vehicle-plan.js`
其它候选怀疑点(同样未验证): ```js
- 把 `vehicleStatus === "busy"` 当成不可用(该车全局状态确实是 `busy`,因为正被本单占着,这是**正常**的) // 日格「已完成」判定(约 :465的最后一关
- 用 `selected` 字段做当前选中回显匹配(逐日查询返回的是 `selected: false` return preservedFinalized || dailyVehiclePlanCandidateEvidenceMatches(normalized)
- 用 `availabilityWindows` 要求覆盖**整个服务期**,而逐日查询只返回当天那一格
// 逐日方案校验(约 :570-577报出那句提示的地方
if (requireCandidateEvidence && !preservedFinalized
&& !dailyVehiclePlanCandidateEvidenceMatches(cell)) {
return `${label}尚未通过当前日期、车辆和司机的候选校验`
}
// 判定式本身(约 :81-90
export function dailyVehiclePlanCandidateEvidenceMatches(cell = {}) {
const evidence = normalizeCandidateEvidence(cell._candidateEvidence)
return Boolean(
evidence &&
evidence.serviceDate === String(cell.serviceDate || '') &&
evidence.vehicleId === stringId(cell.vehicleId) &&
evidence.driverId === stringId(cell.driverId) &&
evidence.confirmCrossResident === (cell.confirmCrossResident === true)
)
}
```
### 3.2 关键:`_candidateEvidence` 只有「本次会话手动走过选车弹窗」才会有
| 来源 | `_candidateEvidence` |
|---|---|
| 用户在选车弹窗里当场选车/选司机 | 写入(`AssignModal.vue` 约 :2122 / :2477 |
| **从服务端快照重建日格**(打开弹窗回显既有派车) | **恒 `null`**`utils/canonical-assignment-snapshot.js` 约 :134,部署包同构 |
| 新建槽位的空白格 | `null``AssignModal.vue` 约 :3560 |
**凡是从后端读回来的既有派车日格,天生就"没通过候选校验"**,哪怕车、司机、价格全都在,哪怕后端明确回 `selectable: true`
### 3.3 唯一的逃生口已被 #5851 关上
`preservedFinalized` = `isUnchangedFinalizedPreservedCell(cell)`,要求 **`(readOnly || locked) && planFinalized === true && _preservedPlanSignature 未变`**。
#5851「换版即作废定稿」的既定行为是:**定制师改需求/人数/天数/出发日期后,派车行的 `dispatch_plan_finalized` 置 0**(这是有意为之,让车务重新过一遍)。于是 `planFinalized === false` → 逃生口关闭 → **换版后每一个日格都必须由车务重新点开选车弹窗、重新选一遍车和司机,才能推进下一步**
这正是 wx 说的那句话的反面:「定制师的需求变了,但车务可以不改派车」。
### 3.4 与现场完全对上
26-3698 三个日格里,8/17 有证据wx 在本次会话里动过这一天,触发了候选查询并写戳,8/18、8/19 是回显数据、证据为 `null`**「1/3 个日格已完成」**,槽位因此算未完成 → **「已完成 0/1 个最终方案槽位」** → 「下一步」置灰。
> 补充:先前怀疑的「接机/参与勾选」不是原因——`pickupParticipant` 只参与「某服务日要求接机则至少一辆车参与接机」这条独立校验,不进日格完成度判定。
--- ---
## 四、目标行为wx 2026-08-11 拍板) ## 四、建议改法
**核心认识:客户端「候选证据」是对后端已有保证的重复设卡。** 提交时后端在锁内会重新校验资源可用性、档期冲突、车辆维保/司机休假、日期窗,真有问题会返回明确错误码;前端不需要、也不应该要求车务为「本来就已经派好、且没改动过」的日格重新走一遍选车弹窗。
按优先级:
1. **(必做)从服务端快照重建日格时,用服务端数据补齐 `_candidateEvidence`**——即把「后端已存在这条派车行」本身当作证据(`serviceDate` / `vehicleId` / `driverId` / `confirmCrossResident` 直接取该行的值)。这样未被用户改动的既有日格天然通过,改动过的仍需走弹窗。
2. **(按 wx 口径)放宽门禁本身**:日格判定与「下一步」启用条件收敛为 **有车 + 有司机 + 服务日期在当前行程窗内**;车型不匹配、座位不足、槽位数与需求数量不符一律降级为提示(见下方分类表)。
3. 保留「用户手动改过车/司机后需重新校验」的行为是合理的,但**即使证据缺失也不应该硬置灰**——改为提示 + 允许提交,由后端做最终裁决。
---
## 五、目标行为wx 2026-08-11 拍板)
> 只要**槽位里有司机、有车辆,且日期是对的**,就可以进行下一步。**定制师的需求变了,但车务可以不改派车**;和需求数对不上也可以。 > 只要**槽位里有司机、有车辆,且日期是对的**,就可以进行下一步。**定制师的需求变了,但车务可以不改派车**;和需求数对不上也可以。
@ -150,14 +204,14 @@ Content-Type: application/json
--- ---
## 、后端侧的后续(不在本条,另发) ## 、后端侧的后续(不在本条,另发)
- 前端放开门禁后,后端提交链路上是否还有会拒的守卫,正在核;需要后端改的部分由 **#5849** 处理,涉及契约变化会另发接口类 changelog。 - 前端放开门禁后,后端提交链路上是否还有会拒的守卫,正在核;需要后端改的部分由 **#5849** 处理,涉及契约变化会另发接口类 changelog。
- 若确认需要后端为前端补一个「本槽位是否可推进 + 不可推进原因」的字段,会在那份 changelog 里给出字段名与口径,届时前端可从「自己推导」改为「读后端结论」,避免两边口径再次劈叉。 - 若确认需要后端为前端补一个「本槽位是否可推进 + 不可推进原因」的字段,会在那份 changelog 里给出字段名与口径,届时前端可从「自己推导」改为「读后端结论」,避免两边口径再次劈叉。
--- ---
## 、关联 ## 、关联
- 后端工单 #5849 - 后端工单 #5849
- `selectable` 字段口径:`11_5850_候选接口显式返回selectable-修改接口-管理后台.md` - `selectable` 字段口径:`11_5850_候选接口显式返回selectable-修改接口-管理后台.md`