changelog(7067): 补第三轮返工的契约增量九之三——requirementReopened 语义扩宽与 NO_DISPATCH_ANCHOR
changelog-filename-gate / validate (push) Successful in 2s

第二轮返工合并后的独立复审发现 2 处缺口(PR #7237 已修并部署测试服):
- 存量订单二次改派后最终方案被误判不完整(改派血缘退休),
- 接送机清空后「开关补开再重存」无法收敛(重开判据由跃迁改为补偿式)。

对前端无破坏性变更(无字段增删、无改名改类型),只有:
C1 requirementReopened 触发面变宽(跃迁 → 状态补偿),重复保存幂等;
C2 reopenBlockedReason 新增取值 NO_DISPATCH_ANCHOR(原来这种情况沉默回 null);
C3 补充九之二 B6「重存一次即可收敛」的前提——要求日上得有可配置的派车行;
C4 内部修复说明(无契约变更)。

front matter 的 status_note 同步更新为三轮返工的口径。
这个提交包含在:
API Changelog Bot
2026-09-07 12:01:57 +08:00
父节点 3b2883d1d3
当前提交 ca5e4073ce
@@ -12,7 +12,7 @@ frontend_owner: "mmg"
frontend_ref: ""
target_release: ""
verified_at: "2026-09-07"
status_note: "2026-09-07 返工二轮(PR #7218)+端到端阻断修复(PR #7223)已合 dev-v3 并部署测试服(fleet/order-v3/user 三服务,user-service 补齐了 #7194 漏部署的 hl-common 通知契约)。端到端实测:新模型订单 D1=A车/D2=B车/D3=A车 判完整并发布 finalPlan、订单车控回到 DONE;接送机门禁三段(605914 阻断→配齐→发布)全通;改派 200 且操作日志恢复写入;需求已 DONE 后再次排车不再 605906;FLEET_ITINERARY_READY 真实投递到 user-service 并写入 notification_send_log。前端 U2-U7 仍在推进,本条保持 pending 至全部闭环;新增的四步向导 step 号变化(九之二 B1)、POST /batch 的 finalPlanPublished(B2)、pickupDropoffGate 可为 null(B3) 三条为破坏性,请优先处理。"
status_note: "2026-09-07 三轮返工全部合入 dev-v3 并部署测试服。二轮(PR #7218)+端到端阻断修复(PR #7223)已完成;三轮(PR #7237)修独立复审发现的 2 处缺口:存量订单二次改派后最终方案被误判不完整(改派血缘退休)、接送机清空后开关补开重存无法收敛(重开判据改补偿式)。端到端实测:新模型订单 D1=A车/D2=B车/D3=A车 判完整并发布 finalPlan、订单车控回到 DONE;接送机门禁三段(605914 阻断→配齐→发布)全通;改派 200 且操作日志恢复写入;需求已 DONE 后再次排车不再 605906;FLEET_ITINERARY_READY 真实投递到 user-service 并写入 notification_send_log。前端 U2-U7 仍在推进,本条保持 pending 至全部闭环;破坏性三条(九之二 B1 step 号 3→4、B2 POST /batch 的 finalPlanPublished、B3 pickupDropoffGate 可为 null)请优先处理;九之三 C1/C2 非破坏性但建议按新口径调提示语。"
updated_at: "2026-09-07"
base: "dev-v3"
---
@@ -1608,6 +1608,63 @@ GET /admin/fleet/matrix/day-orders?date=2026-05-04
---
## 九之三、2026-09-07 第三轮返工(独立复审缺口)的契约增量
第二轮返工合并后的独立复审发现 2 处逻辑缺口,均属历史数据兼容与「承诺与实现不符」。
本轮对前端**没有破坏性变更**:没有新增/删除字段,没有改字段名或类型。
只有一个字段的**触发面变宽**,和一个原因码取值新增——两条都会让前端少踩坑,不用改代码也能跑,
但按下面的口径调提示语会更准确。
### C1 `requirementReopened` 的触发条件从「跃迁」变成「状态补偿」
`PUT /admin/fleet/assignments/pickup-dropoff-config` 响应里的 `requirementReopened`:
- **以前**:只有「本次保存恰好把门禁从满足变成不满足」才为 `true`。
- **现在**:只要「订单车务仍是完成态、而写入后的门禁不满足」就为 `true`,不要求本次保存发生跃迁。
为什么改:旧判据有个进不去的死角。当运维把 `fleet.assign.requirement-lifecycle-enabled`
关着的时候清空接送机勾选,第一次保存会如实返回
`reopenBlockedReason=REQUIREMENT_LIFECYCLE_DISABLED`(订单没被拉回);
随后运维开启开关、车务重新保存一次时,保存前后门禁**都已经是不满足**,跃迁不成立,
旧代码永远进不到重开分支——`requirementReopened` 恒 `false`、`reopenBlockedReason` 还退化成 `null`,
订单永久停在「已完成但门禁不满足」的不一致态。这与九之二 B6 里写的
「开关打开后重存一次即可收敛」直接矛盾。改成补偿判据后那句承诺才真正成立。
**对前端的影响**:字段名与类型不变,原有「`true` 就提示订单已回到处理中」的处理逻辑仍然成立,
只是会在更多场景下为 `true`(例如车务只是改了另一天的勾选、而某个要求日本来就没配齐)。
另外重复保存是**幂等**的:同一需求已有未消费的重开意图时不会再写第二条,此时仍返回 `true`
(含义是「已被拉回/重开意图已在途」)。
### C2 `reopenBlockedReason` 新增取值 `NO_DISPATCH_ANCHOR`
| 取值 | 含义 | 前端提示建议 |
|---|---|---|
| `null` | 不适用,或已成功拉回处理中 | 无需提示 |
| `REQUIREMENT_LIFECYCLE_DISABLED` | 需求级生命周期开关未启用;勾选已落库但订单仍是完成态 | 提示「配置已保存,订单状态需运维开启开关后重存一次收敛」 |
| **`NO_DISPATCH_ANCHOR`**(新增) | 该需求下已没有可作锚点的派车行(只剩已取消/异常/未派占位行),本该拉回却拉不回 | 提示「配置已保存,但该需求已无有效派车,请回第②步重新排车」 |
以前这种情况会返回 `requirementReopened=false` + `reopenBlockedReason=null`,
按契约会被读成「不适用」,而实际是「本该拉回却没拉回」——是个沉默失败。现在显式回报。
### C3 「重存一次即可收敛」的前提(B6 的补充说明)
九之二 B6 说「开关打开后重存一次即可收敛」,这句话有个前提:**要求日上得有可配置的派车行**。
如果某个接送机要求日根本没有排车(该日不配车),门禁在第③步怎么存都满足不了,
重存只会反复把订单拉回处理中——这种情况要回**第②步**先给该日排车,不是第③步能自愈的。
前端在 `pickupDropoffGate.missingPickupDates` / `missingDropoffDates` 命中的日期上,
若该日在排车结果里没有任何车,应引导回第②步而不是让用户在第③步反复点保存。
### C4 后端内部修复(无契约变更,但影响可观察行为)
- **存量订单二次改派后,最终方案不再被误判为不完整**。
旧模型下已经从 A 改派到 B 的订单(两者共享历史车位血缘),再把 B 改派成新模型的 C 之后,
更早的取消记录 A 会失去同血缘的有效行、重新被计入完成条件,导致改派后的最终方案发不出去、
订单车控卡在「处理中」且没有自动回到「已完成」的路径。
已修复为:改派时把「因本次改派而失去同血缘有效行」的整条历史链路一并退休。
「整槽取消待恢复」的记录不受影响,仍照旧计入完成条件(该防线未被削弱)。
---
## 十、相关文档
- 展示口径权威源(HL 仓库路径):`docs/tasks/7067-display-matrix.md`(看板/详情/四步向导/矩阵的数据源、状态范围、空态、守恒规则)