文件
hl-api-changelog/changelogs-v2/2026-09/30_8603_派单确认响应删除恒空的接送机缺失日期字段-修改接口-管理后台.md
T
API Changelog Bot和Claude Opus 5 903e30b4d1
changelog-filename-gate / validate (push) Failing after 1s
docs(changelog): 订正 #8576 的 frontend_status 误判,并给 #8597/#8603 补 not_required 的限定
#8576:我在 2026-09-30 把它从 not_required 改成 pending 是错的,本次改回。
错因是读了落后 693 个提交的 hl-ui 本地工作树——#8464(提交 d7e932ac,2026-09-28)
已整体删除 src/views/fleet/group-dispatch/,src/api/fleet/group-dispatch.js 随之
收缩到只剩 getGroupDispatchPendingBatches,reconfigure / confirm 在前端已无消费方。
对 origin/v2.1 第三次复核:specWarnings 在 src/ 下 0 命中(唯一命中在 .claude 备忘文件),
reconfigure 的 src/ 命中全是注释或 order-v2 同词异义;阳性对照 13 个文件有 export function、
matrix.js 有活跃消费方,证明检索本身有分辨力。

#8597:点明「房务控制台」整个域在前端尚不存在(9 个关键词 0 命中,阳性对照 house-allocation
活跃),故此处的 not_required 是「没有可改的代码」而非「对现有页面透明」,需与 #8491 一并核对。

#8603:点名前端真实渲染点 Step3PickupDropoff.vue:178-179 读的是 props.order.pickupDropoffGate
(未动的那个对象),与本次删字段的 ConfirmRequirementRespVO 不是同一载体,避免下一个人
按字段名 grep 得出相反结论。

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-30 15:55:39 +08:00

431 行
26 KiB
Markdown
原始文件 Blame 文件历史

此文件含有模棱两可的 Unicode 字符
此文件含有可能会与其他字符混淆的 Unicode 字符。 如果您是想特意这样的,可以安全地忽略该警告。 使用 Escape 按钮显示他们。
---
schema: "hl-changelog/v2"
ticket: "8603"
title: "派单原子确认响应删除恒为空的 missingPickupDates / missingDropoffDates,缺失日期只由 605914/605915 错误消息承载"
consumer: "admin"
author: "wx(GIT)"
change_type: "修改接口"
backend_status: "deployed"
gateway_status: "verified"
frontend_status: "not_required"
frontend_owner: ""
frontend_ref: ""
target_release: ""
verified_at: ""
status_note: "PR #8608 已 squash 合并 dev-v3(b4eb919b47),hl-fleet-service dev-v3 分支已滚测试服。删除的两个字段是 #8579 加的、在本端点上恒为空数组;接送机缺口在本端点是硬门禁,日期写在 605914/605915 的错误消息里。真正带「已落库但还差几天」中间态的是 POST /admin/fleet/assignments/batch 的 pickupDropoffGate 对象,该对象未动。【同名字段两个载体,勿按字段名 grep 判断影响面,2026-09-30 查证】hl-ui origin/v2.1 的 src/views/fleet/board/components/Step3PickupDropoff.vue:178-179 确实渲染 missingPickupDates / missingDropoffDates(「待配置接机 / 送机」两行),但它取的是 props.gate,而 gate 由 AssignModal.vue:1626 与 OrderDrawer.vue:803 从 props.order.pickupDropoffGate 派生—— 即上面那个未动的对象,与本次删字段的 ConfirmRequirementRespVO 不是同一个载体。本端点(POST /admin/fleet/assignments/requirements/{id}/confirm,前端出口 src/api/fleet/board.js:162)的响应上,这两个键没有任何读取点,故 not_required 成立。"
updated_at: "2026-09-30"
base: "dev-v3"
---
# hl-fleet-service: 派单原子确认响应删除恒为空的接送机缺失日期字段
> **存放目录**: `changelogs-v2/2026-09/`
> **服务**: hl-fleet-service (端口 8089)
> **PR**: #8608
> **Issue**: #8603
> **日期**: 2026-09-30
> **影响范围**: 车务四步向导第③步「按需求整组原子确认」的响应体
---
## ⚠️ 关键变化
- 🔴 **`ConfirmRequirementRespVO` 删除两个字段**:`missingPickupDates`、`missingDropoffDates`。它们是 #8579 加进来的,在**本端点上恒为空数组**。
- **为什么恒空**:本端点的接送机门禁是**硬门禁**——有缺口一定在写入之前抛 605914 / 605915,缺哪几天以 `yyyy-MM-dd` 逗号分隔原样写在错误消息里。**能拿到 200 响应,就说明门禁已经通过了**,此时「缺失日期」这个概念在本端点上不存在。
- 🔴 **这两个字段的存在制造了一个不存在的中间态**:前端若按 `finalPlanPublished=false && missingPickupDates.length>0` 去渲染「确认成功但还差 N 天」,这个分支**永远不会成立**——本端点没有这种中间态。
- ✅ **「已落库但还差几天」这个中间态确实存在,但它在另一个端点上**:`POST /admin/fleet/assignments/batch`(批量创建派单)的响应里,字段挂在 **`pickupDropoffGate` 对象**下(`arrivalRequiredDates` / `departureRequiredDates` / `missingPickupDates` / `missingDropoffDates` / `declared` / `satisfied`)。**该对象本次未动,全部字段照旧**。要做「还差哪几天」的提示,读那里。
- **前端要做的事**:把本端点响应里对 `missingPickupDates` / `missingDropoffDates` 的读取删掉,改成**捕获 605914 / 605915 并把错误消息里的日期展示给用户**;若已有「差 N 天」的提示 UI,把它的数据源指向 `POST /batch` 的 `pickupDropoffGate`。
- **其余字段全部未变**:`requirementId`、`dispatchPlanGeneration`、`confirmed`、`finalPlanPublished`、`finalPlanNotPublishedReason`、`groups` 及其内部结构逐字段不变;请求体完全未变。
---
## 一、背景
车务四步向导第③步是「按当前派车方案代际原子确认全部执行段」。接送机门禁在这条路径上有**两个不同位置**的判定,二者的失败表现完全不同:
| 位置 | 时机 | 门禁不满足时 |
|------|------|--------------|
| `assertPickupDropoffCoverage` | **确认动作开始之前**(硬门禁) | 抛 605914 / 605915,**整笔不执行**,缺失日期在错误消息里 |
| 最终方案发布漏斗 | 确认已成功、准备发布 finalPlan 时 | 确认仍算成功,`finalPlanPublished=false` + `finalPlanNotPublishedReason` 给原因 |
#8579 把 `missingPickupDates` / `missingDropoffDates` 加进响应,想表达的是第二个位置的「还差几天」。但**第一个位置排在前面且是硬门禁**:门禁开启且真有缺口时,请求在第一个位置就被拦掉了,根本走不到组装响应那一步;门禁关闭时则两处都不判缺口。两条路都不会产出非空的缺失日期列表,于是这两个字段在本端点上**结构性恒为空数组**——它们不是"通常为空",是**没有任何取值路径能让它们非空**。
真正存在该中间态的是批量提交端点:那里"写入成功"与"门禁满足"确实是两件独立的事,所以 `BatchAssignmentWriteRespVO.pickupDropoffGate` 里的六个字段有实际取值。
---
## 二、变更接口清单
| # | 接口 | 方法 | 路径 | 变更类型 | 说明 |
|---|------|------|------|----------|------|
| 1 | 按当前派车方案代际原子确认全部执行段 | POST | `/admin/fleet/assignments/requirements/{requirementId}/confirm` | 响应删除字段 | 删除恒为空的 `missingPickupDates` / `missingDropoffDates`;缺口由 605914/605915 承载 |
---
## 三、接口详情
### 1. 按当前派车方案代际原子确认全部执行段 `POST /admin/fleet/assignments/requirements/{requirementId}/confirm`
**VO**: `ConfirmRequirementReqVO → ConfirmRequirementRespVO`
#### 使用场景
车务四步向导第③步的整组原子确认入口。请求必须**精确列出**当前最终方案的全部有效派车组及各组是否发行程短信;服务端按 `expectedPlanGeneration` 锁定并重读完整方案,重跑最终确认基线 + 行程短信决策一致性校验 + 接送机门禁,通过后重发最终方案快照。任一组缺失、过期或通知歧义则**整笔回滚**,不产生部分 assigned、不产生部分副作用。
#### 入参
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|------|------|------|------|------|------|
| `requirementId` | Path | Long | ✅ | - | 当前用车需求 ID |
| `orderId` | Body | Long | ✅ | `@NotNull` | 订单 ID;为空返 400「订单ID不能为空」 |
| `requestId` | Body | String | ✅ | `@NotBlank`,`@Size(max=64)` | 幂等请求标识;同一 `requestId` 用于**不同**确认内容时返 605059 |
| `expectedRequirementVersion` | Body | Integer | ✅ | `@NotNull` | 预期当前有效用车需求版本,取自 Board 读口 |
| `expectedRequirementSha256` | Body | String | ✅ | `@NotBlank`,`@Pattern("^[0-9a-f]{64}$")` | Board 返回的当前用车需求 canonical SHA-256;必须是**小写**十六进制 64 位,否则返 400 |
| `expectedPlanGeneration` | Body | Long | ✅ | `@NotNull` | 预期当前最终派车方案代际 |
| `groups` | Body | Array | ✅ | `@NotEmpty`,`@Size(max=50)` | 当前有效执行段的**精确集合**;超 50 个返 400「单次确认执行段不能超过50个」 |
| `groups[].assignmentGroupId` | Body | Long | ✅ | `@NotNull` | 当前有效派车组 ID |
| `groups[].sendItinerarySms` | Body | Boolean | ✅ | `@NotNull` | 是否向本执行段司机发送行程短信;为空返 400「请选择是否向本段司机发送行程短信」 |
#### 出参
| 字段 | 类型 | 说明 |
|------|------|------|
| `requirementId` | String | 用车需求 ID(雪花 ID,JSON 中为字符串) |
| `dispatchPlanGeneration` | String | 已确认的最终派车方案代际(JSON 中为字符串) |
| `confirmed` | Boolean | 整组是否原子确认成功;返 200 时恒为 `true` |
| `finalPlanPublished` | Boolean | 本次是否真的发布了最终方案快照。`false` 表示确认已成功但订单车控仍处理中(方案未派满等) |
| `finalPlanNotPublishedReason` | String | 最终方案未发布的原因;已发布时为 `null`。取值见「六.5、枚举 / 数据字典」 |
| ~~`missingPickupDates`~~ | ~~Array~~ | 🔴 **本次删除**(#8579 加入,在本端点恒为空数组)。缺失接机日改由 605914 的错误消息承载 |
| ~~`missingDropoffDates`~~ | ~~Array~~ | 🔴 **本次删除**(同上)。缺失送机日改由 605915 的错误消息承载 |
| `groups` | Array | 各执行段确认结果 |
| `groups[].assignmentId` | String | 代表派单 ID(雪花 ID,JSON 中为字符串) |
| `groups[].assignmentGroupId` | String | 派车组 ID;历史行无该 ID 时回退下发 `assignmentId`,对任何真实行恒非空 |
| `groups[].assignmentStatus` | String | 派单状态 |
| `groups[].confirmedAt` | String | 车务最终确认时间(`yyyy-MM-dd HH:mm:ss`) |
| `groups[].sendItinerarySms` | Boolean | 本段是否选择了发送行程短信(回显请求中的选择) |
| `groups[].itinerarySmsEventId` | String | 行程短信 Outbox 事件 ID;**未发送时为 `null`** |
| `groups[].itinerarySmsStatus` | String | 行程短信状态,取值见「六.5、枚举 / 数据字典」 |
| `groups[].itineraryUrl` | String | 本段电子行程单 H5 链接;本端点组装时**恒为 `null`**,签发链接请走行程短信状态查询端点 |
#### 请求示例
```json
{
"orderId": "2099459272533323777",
"requestId": "confirm-2099459272533323777-20260930-01",
"expectedRequirementVersion": 3,
"expectedRequirementSha256": "9f2c4e1ab7d05836c41fbe2907a5d4638e1c0b7a53d92f8146ce70bb2d5a3ff4",
"expectedPlanGeneration": "12",
"groups": [
{ "assignmentGroupId": "2099461003812864001", "sendItinerarySms": true },
{ "assignmentGroupId": "2099461003812864002", "sendItinerarySms": false }
]
}
```
#### 响应示例
```json
{
"code": 200,
"message": "成功",
"success": true,
"data": {
"requirementId": "2099460881234567890",
"dispatchPlanGeneration": "12",
"confirmed": true,
"finalPlanPublished": true,
"finalPlanNotPublishedReason": null,
"groups": [
{
"assignmentId": "2099461003812864001",
"assignmentGroupId": "2099461003812864001",
"assignmentStatus": "assigned",
"confirmedAt": "2026-09-30 10:12:33",
"sendItinerarySms": true,
"itinerarySmsEventId": "2099461099887766554",
"itinerarySmsStatus": "PENDING",
"itineraryUrl": null
},
{
"assignmentId": "2099461003812864002",
"assignmentGroupId": "2099461003812864002",
"assignmentStatus": "assigned",
"confirmedAt": "2026-09-30 10:12:33",
"sendItinerarySms": false,
"itinerarySmsEventId": null,
"itinerarySmsStatus": "NOT_SENT",
"itineraryUrl": null
}
]
}
}
```
**注意响应里没有 `missingPickupDates` / `missingDropoffDates` 两个键**——不是值为空数组,是**键本身不存在**。
#### 空数据 / 降级响应
「确认成功但最终方案未发布」是本端点唯一的部分成功形态:`confirmed=true` + `finalPlanPublished=false` + `finalPlanNotPublishedReason` 给出原因。此时**没有缺失日期可读**:
```json
{
"code": 200,
"message": "成功",
"success": true,
"data": {
"requirementId": "2099460881234567890",
"dispatchPlanGeneration": "12",
"confirmed": true,
"finalPlanPublished": false,
"finalPlanNotPublishedReason": "PLAN_INCOMPLETE",
"groups": [
{
"assignmentId": "2099461003812864001",
"assignmentGroupId": "2099461003812864001",
"assignmentStatus": "assigned",
"confirmedAt": "2026-09-30 10:12:33",
"sendItinerarySms": false,
"itinerarySmsEventId": null,
"itinerarySmsStatus": "NOT_SENT",
"itineraryUrl": null
}
]
}
}
```
`groups` 恒非空(请求 `@NotEmpty` 保证至少一段,且每段都要有结果)。幂等重放命中已成功回执时返回**与首次逐字段相同**的结果,含冻结在回执里的 `finalPlanPublished` 与 `finalPlanNotPublishedReason`。
#### 错误响应
接送机缺口的唯一载体(`{0}` = 缺失日期,`yyyy-MM-dd` 逗号分隔、升序):
```json
{
"code": 605914,
"message": "大交通要求接机,以下日期未配置接机车辆:2026-10-08,2026-10-09",
"success": false,
"data": null
}
```
```json
{
"code": 605915,
"message": "大交通要求送机,以下日期未配置送机车辆:2026-10-12",
"success": false,
"data": null
}
```
其余错误码(本次未变):
| 码 | 报文 | 触发 / 处置 |
|----|------|-------------|
| 605062 | `派车日期 {0} 越出当前{1}日期窗(版本 v{2},窗内服务日 {3}):请先调整或取消这些越窗槽位,或让定制师重新提交{1}换版后再派车` | 存在越窗在途槽位;须先调整或取消 |
| 605037 | `车辆处于维保或停用状态,不能派车:{0}` | 先改派换车 |
| 605038 | `司机处于休假或待激活状态,不能派车:{0}` | 先改派换司机 |
| 605059 | `幂等请求标识已用于不同确认内容` | 同一 `requestId` 配了不同载荷;**换新 `requestId` 重试** |
| 605063 | `原子确认回执已损坏,无法幂等重放,请联系管理员` | 🔴 **不可自愈终态**,重试同一 `requestId` 永远同码;前端**不得自动重试、不得静默轮询**,须直接提示用户联系管理员 |
#### 业务边界
- **鉴权**:`AssignmentController` 未挂方法级权限注解,只有网关登录态校验;未登录返 401。
- 🔴 **缺失日期只存在于错误消息里**:本端点拿到 200 就代表接送机门禁已通过,不要在响应体里找缺口字段。
- 🔴 **「还差几天」的中间态在 `POST /admin/fleet/assignments/batch`**:读其响应的 `pickupDropoffGate` 对象(含 `arrivalRequiredDates` / `departureRequiredDates` / `missingPickupDates` / `missingDropoffDates` / `declared` / `satisfied`),该对象本次未动。
- **门禁开关关闭时不判缺口**:接送机门禁受服务端配置开关控制;关闭时硬门禁直接放行、发布漏斗也不判门禁,所以既不会抛 605914/605915,也不会因接送机原因压住发布。这是服务端配置项,**不是请求参数,前端无法也无需感知**。
- **`GATE_UNSATISFIED` 在本端点上几乎不可达**:门禁开启且有缺口时请求在硬门禁处就被拦成 605914/605915;门禁关闭时不判。它只剩「需求身份不全」的兜底分支,而那条分支按源码注释本来就**没有任何缺失日期可言**。
- **`groups` 必须是精确集合**:少给一组、多给一组、或组已过期,整笔回滚返错,不会部分生效。
- **幂等**:以 `requestId` 为键;重放已成功的回执返回同一份结果(含冻结的发布结论),载荷变了返 605059。
- **`itineraryUrl` 在本响应中恒为 `null`**:行程单链接由行程短信状态查询端点下发。
- **失败零副作用**:所有门禁与基线校验都排在写入之前,报错时不产生部分 assigned、不产生短信事件。
---
## 四、契约约束与正确调用方式
> 本节只写后端接受 / 拒绝请求的规则与响应字段的正确读法,不写 UI 渲染建议。
### ✅ 正确 / ❌ 错误 读法对照
| 目的 | ✅ 正确做法 | ❌ 错误做法 |
|------|-------------|-------------|
| 判断「接送机缺哪几天」 | 捕获 605914 / 605915,从 `message` 里取日期(`yyyy-MM-dd` 逗号分隔) | 读本端点响应的 `missingPickupDates` / `missingDropoffDates`——**这两个键已不存在** |
| 渲染「已落库但还差 N 天」 | 读 `POST /admin/fleet/assignments/batch` 响应的 `data.pickupDropoffGate.missingPickupDates` / `.missingDropoffDates` | 在本端点响应上拼这个中间态——本端点没有该中间态 |
| 判断「确认成功了吗」 | 看 HTTP 层 `code=200` + `data.confirmed` | 看 `finalPlanPublished`——它答的是另一个问题(方案有没有发布) |
| 判断「最终方案发出去了吗」 | `data.finalPlanPublished`;为 `false` 时读 `finalPlanNotPublishedReason` | 假定 `confirmed=true` 就等于已发布 |
| 撞到 605063 | 停止重试,提示用户联系管理员 | 自动重试 / 静默轮询——同一 `requestId` 永远返同码 |
| 撞到 605059 | **换一个新的 `requestId`** 重发 | 用同一个 `requestId` 重试 |
### 前端必须做的改动
1. **删掉对本端点响应 `missingPickupDates` / `missingDropoffDates` 的一切读取**(含可选链兜底、空数组判断、TS 类型定义)。
2. **接送机缺口提示改走 605914 / 605915 的错误消息**,日期在 `message` 里逐字给出。
3. 若页面上有「已落库但还差几天」的提示块,**把它的数据源改指向 `POST /admin/fleet/assignments/batch` 的 `pickupDropoffGate`**。
---
## 五、数据库行为
**本次改动不涉及任何数据库变更**:无建表、无加列、无改列、无数据迁移、无 Flyway 脚本。
端点自身的写入行为(本次未变):
| 动作 | 写入 |
|------|------|
| 整组原子确认 | 各执行段派单行 `assignment_status → assigned`、写 `confirmed_at` |
| 行程短信 | `sendItinerarySms=true` 的段写一条短信 Outbox 事件,`itinerary_sms_event_id` 回填到派单行 |
| 幂等回执 | 落一条确认回执,冻结本次结果(含 `finalPlanPublished` 与 `finalPlanNotPublishedReason`)供重放 |
| 最终方案快照 | 发布判据全部通过时冻结一次 DAILY_V3 finalPlan,由 order-v3 消费后把车控状态推进 |
**失败零写入**:接送机硬门禁、越窗门禁、基线校验全部排在写入之前;任一失败整事务回滚。
---
## 六、边界行为
- 未登录 → 401(网关拦截)。
- 请求体字段缺失 / 格式不符(`expectedRequirementSha256` 不是小写 64 位十六进制、`groups` 为空、超 50 段等)→ 400,消息即上表「约束」列所写的校验文案。
- 接送机门禁开启且缺接机日 → 605914,日期在消息里,零写入。
- 接送机门禁开启且缺送机日 → 605915,日期在消息里,零写入(接机缺口先判,两者都缺时先报 605914)。
- 接送机门禁关闭 → 不判缺口,既不抛 605914/605915,也不因接送机压住发布。
- 存在越窗在途槽位 → 605062,零写入。
- 车辆维保/停用、司机休假/待激活 → 605037 / 605038,零写入。
- 同一 `requestId` 配不同载荷 → 605059;换新 `requestId` 即可。
- 回执损坏 → 605063,不可自愈终态。
- 幂等重放命中成功回执 → 200,返回与首次逐字段相同的结果。
- 确认成功但方案未发布 → 200 + `confirmed=true` + `finalPlanPublished=false` + `finalPlanNotPublishedReason`,**此时无缺失日期可读**。
---
## 六.5、枚举 / 数据字典
### finalPlanNotPublishedReason(最终方案未发布原因)
**所属字段**: `data.finalPlanNotPublishedReason` | **类型**: `String` | 已发布时为 `null`
判据按固定顺序执行,**只回第一个没通过的原因**:
| 顺序 | 值 | 含义 |
|------|----|------|
| 1 | `STALE_FINALIZED_PLAN` | 存在按旧需求定稿的陈旧行,需车务对当前需求重新确认 |
| 2 | `INVALID_PLAN_GENERATION` | 当前生效行的方案代际不一致(部分已定稿、部分未定稿或代际不同) |
| 3 | `PLAN_INCOMPLETE` | 满派拓扑不完整:有逻辑 key 没派车、缺司机、在途行越窗、同 key 多行等 |
| 4 | `CAPACITY_INSUFFICIENT` | 未定稿分支上当日载客量不足以覆盖需求人数 |
| 5 | `GATE_UNSATISFIED` | 大交通要求的接/送机日没有配车。🔴 **在本端点上几乎不可达**(有缺口时硬门禁先抛 605914/605915) |
> 另有 `NO_GATE_TRANSITION` 与 `PICKUP_DROPOFF_GATE_DISABLED` 两个值,**只在接送机配置端点出现**,本端点不会返回。
### itinerarySmsStatus(行程短信状态)
**所属字段**: `data.groups[].itinerarySmsStatus` | **类型**: `String`
| 值 | 含义 |
|----|------|
| `NOT_SENT` | 本段未选择发送,或历史行没有短信事件 |
| `PENDING` | 本次已产生短信 Outbox 事件,投递中 |
| `SENT` | 短信已发出(出现在已确认段的重放回显里) |
---
## 六.6、修改前后对比
### 字段级对比
| 字段 | 改前 | 改后 |
|------|------|------|
| `data.missingPickupDates` | `Array<String>`,**恒为空数组 `[]`** | 🔴 **键已删除,响应中不存在** |
| `data.missingDropoffDates` | `Array<String>`,**恒为空数组 `[]`** | 🔴 **键已删除,响应中不存在** |
| `data.requirementId` | String | 未变 |
| `data.dispatchPlanGeneration` | String | 未变 |
| `data.confirmed` | Boolean | 未变 |
| `data.finalPlanPublished` | Boolean | 未变 |
| `data.finalPlanNotPublishedReason` | String / null | 未变 |
| `data.groups[*]` 全部字段 | 8 个字段 | 未变 |
| 请求体全部字段 | — | 未变 |
| 错误码集合 | 605062 / 605914 / 605915 / 605037 / 605038 / 605059 / 605063 | 未变 |
| `BatchAssignmentWriteRespVO.pickupDropoffGate` | 6 个字段 | **未变**(缺失日期的正确来源) |
### 行为级对比
| 行为 | 改前 | 改后 |
|------|------|------|
| 接送机有缺口 + 门禁开启 | 抛 605914/605915(响应根本到不了组装步) | 未变 |
| 接送机门禁通过、拿到 200 | 响应带两个**恒为空**的日期数组 | 响应**不含**这两个键 |
| 前端按 `missingPickupDates.length > 0` 判缺口 | 永远为 `false`,分支不可达 | 该字段不存在;改捕获 605914/605915 |
| 「已落库但还差几天」的读法 | 本端点读不到(恒空),实际在 `POST /batch` | 未变,仍在 `POST /batch` 的 `pickupDropoffGate` |
---
## 六.7、影响评估
- **是否破坏向后兼容**: **是(响应删字段)**。但删的是**在本端点恒为空数组**的两个字段,任何依赖它们做判断的前端分支在改前也永远不成立——即行为上前端看不到差异,看得到差异的是**读取代码本身**(可选链失效 / TS 类型不匹配 / 空数组默认值)。
- **前端是否必须同步上线**: **建议同步**。JS 里读不存在的键得 `undefined`,若代码写的是 `resp.data.missingPickupDates.length` 会抛 TypeError;写成 `?.length` 或有默认值则不报错。TS 侧需删掉类型声明里的这两个字段。
- **前端 workaround 清理点**: 如果曾为「这两个字段总是空」做过兜底(写死不展示、或转去读别的来源),可以连同兜底一起清掉,直接按 605914/605915 + `POST /batch` 的 `pickupDropoffGate` 这两条正路走。
- **联调注意**: 缺口提示的数据源从此分两处——**硬门禁报错**(本端点,错误消息)与**中间态展示**(`POST /batch`,`pickupDropoffGate` 对象),不要把两者混为一处。
---
## 七、不影响范围
- **仅影响**: `POST /admin/fleet/assignments/requirements/{requirementId}/confirm` 的响应体字段集合。
- **零影响**:
- `POST /admin/fleet/assignments/batch` 及其 `pickupDropoffGate` 对象(六个字段全部保留,取值逻辑未动)
- 接送机配置端点 `POST /admin/fleet/assignments/requirements/{requirementId}/pickup-dropoff`(含它专属的 `NO_GATE_TRANSITION` / `PICKUP_DROPOFF_GATE_DISABLED` 两个原因值)
- 派单创建 / 修改 / 取消 / 软清 / 一键重派推荐 / 候选查询 / 预校验
- 行程短信状态查询与受控重发
- 接送机门禁自身的判定逻辑与开关语义(**只删了响应回显,门禁一步没动**)
- 最终方案发布漏斗与 order-v3 的车控状态推进
- 数据库:无表结构或数据变更
---
## 八、测试环境已验证
- **代码事实**(对 `origin/dev-v3` 逐一查证):
- 合并提交 `b4eb919b47`(PR #8608 squash 合并进 `dev-v3`),8 文件 / +119 −55。
- `ConfirmRequirementRespVO` 当前字段集已逐字段核对:`requirementId` / `dispatchPlanGeneration` / `confirmed` / `finalPlanPublished` / `finalPlanNotPublishedReason` / `groups`,两个日期字段处留有说明注释、字段已删。
- `AssignmentController` 的 `@ApiOperation(notes=…)` 新增 6 行说明,逐行核对:缺失日期载体是错误码、`yyyy-MM-dd` 逗号分隔、中间态在 `POST /batch` 的 `pickupDropoffGate`。
- `assertPickupDropoffCoverage` 两条抛错分支(605914 接机、605915 送机,日期以 `,` join)与开关关闭时的早返回逐行核对;确认主流程里该硬门禁排在回执重放与任何写入之前。
- `BatchAssignmentWriteRespVO.pickupDropoffGate` 与 `PickupDropoffGateVO` 六字段在 `dev-v3` 上原样存在,本提交未触及这两个文件。
- `FinalPlanNotPublishedReasons` 七个常量与两份 Swagger 说明文本已核对:本端点用的是只含五个取值的通用说明。
- 回归覆盖:`AssignmentControllerTest` 断言 `$.data.missingPickupDates` / `$.data.missingDropoffDates` **不存在**;`AssignmentServicePickupDropoffTest` 新增「门禁显式开启且有缺口时抛错且日期在消息里」用例;`RequirementConfirmationReceiptServiceTest` 新增回执往返用例。
- **部署**:`hl-fleet-service` 的 `dev-v3` 分支已滚到测试服,端点走管理端网关 `/admin/fleet/**` 既有路由,无新增路由。
```
POST /admin/fleet/assignments/requirements/{requirementId}/confirm → 200,响应无 missingPickupDates / missingDropoffDates 两键 ✓
POST /admin/fleet/assignments/requirements/{requirementId}/confirm(缺接机日) → 605914,日期在 message ✓
POST /admin/fleet/assignments/batch → 200,data.pickupDropoffGate 六字段照旧 ✓
```
---
## 九、相关历史 PR
| PR | Issue | 说明 | 是否仍有效 |
|----|-------|------|------------|
| — | #7067 | 接送机门禁落地:605914/605915 + 最终方案发布漏斗 | ✅ 有效 |
| — | #8429 | 未发布原因 `finalPlanNotPublishedReason` 进响应 | ✅ 有效 |
| — | #8579 | 给确认响应加 `missingPickupDates` / `missingDropoffDates` | ❌ **已被本单撤销**(在本端点恒为空) |
| **本 PR #8608** | **#8603** | 删除上述两个恒空字段,缺口归错误码承载 | ✅ 最新 |
---
## 十、相关文档
- 关联 Issue: [wx/HL#8603](https://git.1814.love:8443/wx/HL/issues/8603)
- 关联 PR: [wx/HL#8608](https://git.1814.love:8443/wx/HL/pulls/8608)
## 关联 / 联系人
### 链接
- **Issue**: [#8603](https://git.1814.love:8443/wx/HL/issues/8603)
- **PR**: [#8608](https://git.1814.love:8443/wx/HL/pulls/8608)
- **Merge commit**: [b4eb919b47](https://git.1814.love:8443/wx/HL/commit/b4eb919b47)
### 联系人
- **后端负责人**: @wx