docs(changelog): #7443 接送机用车需求分叉 PR-1;并订正 09-17 两份的错误响应信封

新增 18_7443:派车批量创建与接送机配置两个端点新增可选入参 kind(TRAVEL|TRANSFER,
不传=TRAVEL,存量请求形状不变),新增错误码 602200/602201/602202/602205。
字段表逐条对源码反向 grep,53/53 全命中。

文档里写明了两条前端必须知道的限制:
1. 上游写口还关着——PUT /v3/admin/order/{id}/vehicle-requirement 传 kind=TRANSFER
   恒返 809009(transfer-kind-submit-enabled 默认 false,无 @RefreshScope 要重启),
   即产品上目前产不出 TRANSFER 需求,前端可按契约对接但别排进本期可演示范围。
2. 已建出的 TRANSFER 派车行,确认与改派仍会 605041(三个内部命令对象不携带 kind,
   属跨服务契约缺口,已登记 #7443 AC-24)。

八、测试环境已验证写的是实测:单元层 fleet 全量 4220/0/0/4、完整性 322/322;
网关端到端 AC-6/AC-7 均通过(batch 带 kind=TRANSFER 返回 200,落库两行服务日
2026-10-11 与 2026-10-17 均在行程窗外,未出现 605041/605062/605905;改接机行后
送机行逐字段未变)。同时保留限定:那条 TRANSFER 需求行是 SQL 构造的,订单侧真实
写口不在本次证据范围内——没有写成「全链路已验证」。

订正 17_7442 与 17_7443 的错误响应示例(共 3 处):原写 {"code": 200, ...,
"errorCode": <业务码>},两处都错——Result 没有 errorCode 字段,而业务失败时
GlobalExceptionHandler 走 Result.error(e.getCode(), message) 把业务码放进 code。
前端照原文按 code == 200 判成功,会把业务失败读成成功。
⚠️ success 是真实字段没有删:Result.java:43 public boolean isSuccess() 没有
@JsonIgnore(全类只有 :142 getCheckedData 被忽略),Jackson 会序列化它——
只按 private 字段清单 grep 会误判它是编的,差点据此把一个前端正在读的字段删掉。

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
这个提交包含在:
API Changelog Bot
2026-09-18 08:04:03 +08:00
共同撰写人 Claude Opus 5
父节点 778d618ef4
当前提交 c7ce1009d6
共修改 3 个文件,包含 533 行新增和 9 行删除
@@ -124,11 +124,10 @@ N/A(团期无可确认行时返 602007 错误)。
```json ```json
{ {
"code": 200, "code": 602008,
"message": "配车尚未覆盖完整, 不能确认: 乘车分组 A 缺失 2026-05-08", "message": "配车尚未覆盖完整, 不能确认: 乘车分组 A 缺失 2026-05-08",
"data": null, "data": null,
"success": false, "success": false
"errorCode": 602008
} }
``` ```
@@ -298,10 +298,10 @@ N/A(操作必返结果)。
```json ```json
{ {
"code": 200, "code": 602203,
"message": "订单的团期归属尚未回填, 无法派车", "message": "订单的团期归属尚未回填, 无法派车",
"success": false, "data": null,
"errorCode": 602203 "success": false
} }
``` ```
@@ -405,10 +405,10 @@ N/A(整批要么受理要么失败关闭,不存在「空成功」形态)
```json ```json
{ {
"code": 200, "code": 602203,
"message": "订单的团期归属尚未回填, 无法派车", "message": "订单的团期归属尚未回填, 无法派车",
"success": false, "data": null,
"errorCode": 602203 "success": false
} }
``` ```
@@ -0,0 +1,525 @@
---
schema: "hl-changelog/v2"
ticket: "7443"
title: "接送机用车需求分叉 PR-1——派车入参新增需求类别kind"
consumer: "admin"
author: "wx(GIT)"
change_type: "修改接口"
backend_status: "deployed"
gateway_status: "verified"
frontend_status: "pending"
frontend_owner: "mmg"
frontend_ref: ""
target_release: ""
verified_at: "2026-09-18"
status_note: "后端交付。派车批量与接送机配置入参新增可选 kind 字段;新增 4 个错误码(602200/602201/602202/602205)。"
updated_at: "2026-09-18"
base: "dev-v3"
---
# fleet: 接送机用车需求分叉 PR-1——派车入参新增需求类别kind
> **存放目录**: changelogs-v2/{YYYY-MM}/
>
> **服务**: hl-fleet-service
> **PR**: #7911(`9287e6eb9`)、#7912(`d2d63bcb6`)、#7913(`5a6bd95f8`)
> **Issue**: #7443 AC-6~9
> **日期**: 2026-09-18
---
## 关键变化
> ⚠️ **上游写口关着(2026-09-18 补充,比下面这条已知限制更影响前端排期,请先读这条)**:
> **目前没有任何常规途径能创建 `TRANSFER`(接送机)用车需求。**
>
> `PUT /v3/admin/order/{id}/vehicle-requirement`(提交/修改/调整用车需求)传 `kind=TRANSFER`
> **恒返 809009**(`VehicleRequirementKindErrorCode.TRANSFER_REQUIREMENT_SUBMIT_NOT_OPENED`,
> 文案「接送机用车需求尚未开放提交,请联系管理员确认开放时间(订单 {0})」,
> `VehicleRequirementKindErrorCode.java:95-98`)。根因:`RequirementService.java:383` 的
> `@Value("${hl.order.requirement.transfer-kind-submit-enabled:false}")` 默认 `false`
> (`application.yml:59`,env `HL_ORDER_REQUIREMENT_TRANSFER_KIND_SUBMIT_ENABLED`),测试服
> Nacos 的 `hl-order-service-v3-test.yml` / `hl-common-test.yml` 两份配置均未覆盖(2026-09-18
> 实测)。该字段**没有 `@RefreshScope`**(`RequirementService.java:338`),改配置须**重启
> order-v3 实例**才生效,不能靠 Nacos 热推。
>
> **前端影响**:本次(PR #7911/#7912/#7913)交付的是**车务侧**接住 TRANSFER 需求的能力,不是
> 订单侧写口;开关打开之前产品上产生不出 `TRANSFER` 需求,本文档两个接口的 `kind=TRANSFER`
> 分支因此暂时无数据可用。前端可以先按契约对接,但**不要把它排进本期可演示范围**。
> ⚠️ **已知限制(2026-09-18 补充,前端集成前必读)**:`kind=TRANSFER` 建出来的派车行,
> **目前「确认」和「改派」会失败,返回 605041**(`AssignmentErrorCode.FINAL_CONFIRMATION_BASELINE_MISMATCH`,
> "订单行程或用车派单已变化,请按逐日差异处理后重试")。批量创建(本文档接口 1)今天已修复
> 同一基线复核缺口(PR #7913,commit `5a6bd95f8`),`kind=TRANSFER` 建行本身现在可以正常
> 建行;但确认(`POST /requirements/{id}/confirm`、`POST /{assignmentId}/confirm`)与改派
> (`POST /{assignmentId}/change`)三个端点内部的基线复核仍未做 kind 感知,必现 605041。
> 根因、影响面与跟踪单号见「六、边界行为」。**前端本版请只接「建接送机派车行」这一步,
> 确认/改派暂缓接入,接了必然拿到 605041。**
1. `BatchCreateAssignmentReqVO` 新增可选入参 `kind`(`TRAVEL`|`TRANSFER`,不传=`TRAVEL`)
2. `PickupDropoffConfigReqVO` 新增可选入参 `kind`(`TRAVEL`|`TRANSFER`,不传=`TRAVEL`)
3. `kind=TRANSFER` 时两端点均走接送机需求「四步前置」(无条件回填服务日 → 按结果分流 →
strict GET 需求 → 大交通声明日覆盖校验),任一步不通过即失败关闭,新增 4 个错误码:
602200 / 602201 / 602202 / 602205
4. 向后兼容:`kind` 不传等于 `TRAVEL`,存量前端请求形状一个字不变、行为与本次改动前逐字一致
5. **对前端最要紧的一句**:客人没填大交通的订单去派接送机车(`kind=TRANSFER`),会得到
**602205**,前端须引导先回订单侧补齐大交通再重试,而不是提示「接送机需求不存在」
---
## 二、变更接口清单
| # | 接口 | 方法 | 路径 | 变更类型 | 说明 |
|---|------|------|------|----------|------|
| 1 | 派车批量创建 | POST | `/admin/fleet/assignments/batch` | 修改 | 入参新增可选 `kind`;`kind=TRANSFER` 时新增 3 个错误码(602200/602201/602205) |
| 2 | 接送机配置 | PUT | `/admin/fleet/assignments/pickup-dropoff-config` | 修改 | 入参新增可选 `kind`;`kind=TRANSFER` 时新增 4 个错误码(602200/602201/602202/602205) |
---
## 三、接口详情
### 1. 派车批量创建 `POST /admin/fleet/assignments/batch`
**VO**: `BatchCreateAssignmentReqVO → BatchAssignmentWriteRespVO`
#### 使用场景
车务最终实派方案原子提交(#7067 去槽位化)。本次为其新增可选入参 `kind`,用于区分本次提交
的是常规行程用车(`TRAVEL`)还是接送机用车(`TRANSFER`)需求。`kind=TRANSFER` 时,服务端在
取需求锁**之前**按订单 ID 解析当前生效的接送机需求:①无条件调用 order-v3 的服务日回填端点
(幂等);②回填结果为「客人未填大交通」→ 602205 就地失败关闭(不再继续 GET);回填结果为
「该订单没有生效接送机需求」→ 602200;③其余情况 strict GET 该需求,两次调用之间需求被撤
同样按 602200 处理;④需求服务日仍解析为空 → 602205;服务日非空则校验大交通声明要接送的
日期是否都落在服务日内,不覆盖 → 602201。`kind` 不传或为空一律按 `TRAVEL` 处理,取需求、
校验、落库路径与本次改动之前逐字一致。
#### 入参(本次新增 1 个,其余 27 个原有参数不变)
> 覆盖范围:`BatchCreateAssignmentReqVO`(含内部类 `DailyPlanItem`,`D:/work2/HL-v3` 已 ff 到
> `origin/dev-v3@9287e6eb9`)全量 28 个字段,逐一核对源码。
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|------|------|------|------|------|------|
| orderId | Body | Long | 是 | - | 订单 ID |
| orderNo | Body | String | 否 | - | 订单号冗余 |
| requirementId | Body | Long | 是 | - | 当前生效用车需求 ID |
| kind | Body | String | 否 | `TRAVEL`\|`TRANSFER` | 🆕 需求类别:`TRAVEL`=行程用车(默认),`TRANSFER`=接送机用车。不传按 `TRAVEL`,存量前端无需改动;`TRANSFER` 时服务日取大交通航班日,可以落在行程日窗之外 |
| startDate | Body | Date | 是 | - | 用车开始日期;`TRANSFER` 请传接送机服务日的最早一天 |
| endDate | Body | Date | 是 | - | 用车结束日期 |
| pickupAt | Body | String | 否 | - | 接客地 |
| dropoffAt | Body | String | 否 | - | 送客地 |
| headcount | Body | Integer | 否 | - | 乘客人数 |
| confirmNoVehicleServiceDates | Body | Boolean | 否 | - | 逐日计划未覆盖全部服务日期(存在不配车日期)时的显式二次确认 |
| sendItinerarySms | Body | Boolean | 否 | 不传按 false | 是否向本批各车师傅发送行程短信;整批统一决策 |
| skipCityJunctionException | Body | Boolean | 否 | - | 跳过城市衔接例外 |
| fromEntry | Body | String | 否 | - | 操作来源 |
| requestId | Body | String | 是 | ≤64 | 批次级幂等请求标识 |
| dailyPlan[] | Body | Array | 是 | ≤4000 项 | 按行程日的完整配车列表:逐项为 服务日期×车辆×司机;同一服务日允许多条;需求日期窗内未出现的服务日视为该日不配车 |
| dailyPlan[].serviceDate | Body | Date | 是 | - | 服务日期 |
| dailyPlan[].vehicleId | Body | Long | 是 | - | 车辆 ID |
| dailyPlan[].driverId | Body | Long | 是 | - | 司机 ID |
| dailyPlan[].assignmentPrice | Body | BigDecimal | 否 | ≥0.00,整数最多10位/小数最多2位 | 本车当天实际价格;未传按车型价格日历参考价兜底 |
| dailyPlan[].priceAdjustmentReason | Body | String | 否 | ≤256 | 实际价格与价格日历参考价不一致时的调整原因 |
| dailyPlan[].confirmCrossResident | Body | Boolean | 否 | - | 跨常驻车显式确认 |
| ~~items~~ | Body | - | 否 | 携带非 null 值即 400 | 已移除:去槽位化后不再有槽位序号;携带将被 400 拒绝 |
| ~~chargeableServiceDates~~ | Body | - | 否 | 携带非 null 值即 400 | 已移除:旧收费日期字段;携带将被 400 拒绝 |
| ~~vehicleFeeWaiverReason~~ | Body | - | 否 | 携带非 null 值即 400 | 已移除:旧免费服务日字段;携带将被 400 拒绝 |
| ~~confirmAllServiceDatesFree~~ | Body | - | 否 | 携带非 null 值即 400 | 已移除:旧免费服务日字段;携带将被 400 拒绝 |
| ~~holdMode~~ | Body | - | 否 | 携带非 null 值即 400 | 已移除:#5827 起一步派定;携带将被 400 拒绝 |
| ~~dailyPlan[].fleetItemIndex~~ | Body | - | 否 | 携带非 null 值即 400 | 已移除:稳定车辆槽位序号;携带将被 400 拒绝 |
| ~~dailyPlan[].used~~ | Body | - | 否 | 携带非 null 值即 400 | 已移除:用车开关;携带将被 400 拒绝 |
| ~~dailyPlan[].pickupParticipant~~ | Body | - | 否 | 携带非 null 值即 400 | 已移除:接机标志改由接送机配置步骤写入;携带将被 400 拒绝 |
#### 出参(本次无变化)
本次无新增字段。`BatchAssignmentWriteRespVO` 结构未动,下表为全量 13 个字段(含 `pickupDropoffGate` 展开):
| 字段 | 类型 | 说明 |
|------|------|------|
| assignments[] | Array | 按 `fleetItemIndex` 升序返回的派单结果 |
| assignments[].fleetItemIndex | Integer | 当前用车需求展开后的车辆槽位序号 |
| assignments[].assignment | Object | 复用单槽位派单响应(`AssignmentWriteRespVO`),本次未展开 |
| finalPlanPublished | Boolean | 本次是否已发布最终方案;false 表示排车已落库但接送机未配齐,须继续走第③步 |
| pickupDropoffGate | Object | 接送机门禁状态 |
| pickupDropoffGate.arrivalRequiredDates | Array | 大交通声明需要接机的服务日 |
| pickupDropoffGate.departureRequiredDates | Array | 大交通声明需要送机的服务日 |
| pickupDropoffGate.missingPickupDates | Array | 尚未配置接机车辆的服务日(升序) |
| pickupDropoffGate.missingDropoffDates | Array | 尚未配置送机车辆的服务日(升序) |
| pickupDropoffGate.declared | Boolean | 该订单大交通是否有任一方向的接送机声明 |
| pickupDropoffGate.satisfied | Boolean | 门禁是否已满足;false 时不发布最终方案 |
| failedFleetItemIndex | Integer | 直接派定基线失败的车辆槽位序号 |
| dailyDifferences | Array | 直接派定基线失败的逐日差异 |
#### 请求示例
```json
POST /admin/fleet/assignments/batch
{
"orderId": 1934567890123456789,
"requirementId": 1934567890123456790,
"kind": "TRANSFER",
"startDate": "2026-05-06",
"endDate": "2026-05-06",
"requestId": "batch-20260918-0001",
"dailyPlan": [
{"serviceDate": "2026-05-06", "vehicleId": 99, "driverId": 88}
]
}
```
#### 响应示例
```json
{
"code": 200,
"message": "成功",
"data": {
"assignments": [
{"fleetItemIndex": 0, "assignment": {"id": "1234567890123456789", "assignmentStatus": "assigned"}}
],
"finalPlanPublished": false,
"pickupDropoffGate": {
"arrivalRequiredDates": ["2026-05-06"],
"departureRequiredDates": [],
"missingPickupDates": ["2026-05-06"],
"missingDropoffDates": [],
"declared": true,
"satisfied": false
},
"failedFleetItemIndex": null,
"dailyDifferences": null
},
"success": true
}
```
#### 空数据 / 降级响应
N/A(整批要么受理要么失败关闭,不存在「空成功」形态;`assignments[]` 恒随 `dailyPlan[]` 项数非空)。
#### 错误响应
```json
{
"code": 602205,
"message": "订单 1934567890123456789 的接送机需求 1934567890123456790 还没有服务日, 请先在订单侧补齐大交通后重试",
"data": null,
"success": false
}
```
其余两个 `kind=TRANSFER` 专属错误码:
```json
{
"code": 602200,
"message": "订单 1934567890123456789 没有生效的接送机用车需求, 无法按接送机派车",
"data": null,
"success": false
}
```
```json
{
"code": 602201,
"message": "大交通声明要接送的日期 2026-05-07 不在接送机需求的服务日内, 请回订单侧核对大交通",
"data": null,
"success": false
}
```
> ⚠️ **响应信封订正**:本仓 `Result` 类(`hl-common/hl-common-core/src/main/java/com/hulalv/common/result/Result.java`)
> 只有 `code`/`message`/`data`/`traceId` 四个字段,业务异常时 `code` 字段本身就是业务错误码
> (见 `GlobalExceptionHandler.handleBusiness` 第 205 行 `Result.error(e.getCode(), message)`),
> HTTP 状态码固定 200(`@ResponseStatus(HttpStatus.OK)`),**不存在独立的 `errorCode` 字段**。
> **错误响应信封的正确形状**:业务失败时 `code` 就是业务错误码(HTTP 仍为 200),`success` 由 `Result.isSuccess()` 按 `code == 200` 自动得出、**是真实字段**,**没有 `errorCode` 字段**。2026-09-17 的 `17_7442` / `17_7443` 两份曾写成 `"code": 200, ..., "errorCode": <业务码>`,已随本次一并订正——前端按 `code == 200` 判成功即可,不要去读 `errorCode`。
#### 业务边界
- `kind` 不传或空=`TRAVEL`,存量批量派车零改动,走既有 `605906`/`605905` 校验路径
- `kind=TRANSFER` 时四步前置顺序钉死,任一步失败即整批拒绝、不落库
- 602202(同需求同日同方向重复参与)**不在本接口触发**,仅 `PUT /pickup-dropoff-config` 校验
---
### 2. 接送机配置 `PUT /admin/fleet/assignments/pickup-dropoff-config`
**VO**: `PickupDropoffConfigReqVO → PickupDropoffConfigRespVO`
#### 使用场景
派车第③步:整批幂等覆盖接送机参与标志。本次新增 `kind` 入参,决定 `requirementId` 应当
匹配哪一条需求(`TRAVEL` 走订单详情上下文里那条固定 `TRAVEL` 需求;`TRANSFER` 走与批量创建
相同的接送机四步前置解析)。写入落库后额外做「同需求同日同方向唯一参与」校验:`kind=TRANSFER`
时,同一天同一方向若有多于一条生效派车行标记参与,抛 602202;`TRAVEL` 需求不受本校验影响
(同日多行标接机是既有合法形态)。
#### 入参(本次新增 1 个,其余 7 个原有参数不变)
> 覆盖范围:`PickupDropoffConfigReqVO`(含内部类 `Item`)全量 8 个字段。
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|------|------|------|------|------|------|
| orderId | Body | Long | 是 | - | 订单 ID |
| requirementId | Body | Long | 是 | - | 当前生效用车需求 ID |
| kind | Body | String | 否 | `TRAVEL`\|`TRANSFER` | 🆕 需求类别:`TRAVEL`=行程用车(默认),`TRANSFER`=接送机用车。不传按 `TRAVEL`,存量前端无需改动;决定 `requirementId` 应匹配哪一条需求,以及归零作用域落在哪一条需求名下 |
| requestId | Body | String | 是 | ≤64 | 幂等请求标识 |
| items[] | Body | Array | 是 | ≤4000 项 | 要标记接送机参与的派车行集合;该订单+当前需求下未出现在 items 中的生效派车行两个方向标志一律归 0 |
| items[].assignmentId | Body | Long | 是 | - | 派车行 ID(必须是当前需求下的生效派车行) |
| items[].pickupParticipant | Body | Boolean | 是 | - | 当日该车是否参与 ARRIVAL 接机/接站 |
| items[].dropoffParticipant | Body | Boolean | 是 | - | 当日该车是否参与 DEPARTURE 送机/送站;与 pickupParticipant 不可同时为 false |
#### 出参(本次无变化)
本次无新增字段。`PickupDropoffConfigRespVO` 结构未动,下表为全量 10 个字段(含 `pickupDropoffGate` 展开):
| 字段 | 类型 | 说明 |
|------|------|------|
| finalPlanPublished | Boolean | 本次是否发布了最终方案快照(配齐即发) |
| requirementReopened | Boolean | 是否已把订单拉回处理中(门禁不满足而订单仍是完成态时触发) |
| reopenBlockedReason | String | 本该拉回处理中却没拉回的原因;null=不适用或已成功拉回 |
| pickupDropoffGate | Object | 写入后的接送机门禁状态 |
| pickupDropoffGate.arrivalRequiredDates | Array | 大交通声明需要接机的服务日 |
| pickupDropoffGate.departureRequiredDates | Array | 大交通声明需要送机的服务日 |
| pickupDropoffGate.missingPickupDates | Array | 尚未配置接机车辆的服务日(升序) |
| pickupDropoffGate.missingDropoffDates | Array | 尚未配置送机车辆的服务日(升序) |
| pickupDropoffGate.declared | Boolean | 该订单大交通是否有任一方向的接送机声明 |
| pickupDropoffGate.satisfied | Boolean | 门禁是否已满足;false 时不发布最终方案 |
#### 请求示例
```json
PUT /admin/fleet/assignments/pickup-dropoff-config
{
"orderId": 1934567890123456789,
"requirementId": 1934567890123456790,
"kind": "TRANSFER",
"requestId": "pdc-20260918-0001",
"items": [
{"assignmentId": 1234567890123456789, "pickupParticipant": true, "dropoffParticipant": false}
]
}
```
#### 响应示例
```json
{
"code": 200,
"message": "成功",
"data": {
"finalPlanPublished": true,
"requirementReopened": false,
"reopenBlockedReason": null,
"pickupDropoffGate": {
"arrivalRequiredDates": ["2026-05-06"],
"departureRequiredDates": [],
"missingPickupDates": [],
"missingDropoffDates": [],
"declared": true,
"satisfied": true
}
},
"success": true
}
```
#### 空数据 / 降级响应
N/A(整批幂等覆盖,`items` 可传空数组表示"本次不新增任何参与标记,仅把此前未出现在列表中的生效派车行归零",仍返回正常 200 结构,不是独立的空数据形态)。
#### 错误响应
```json
{
"code": 602202,
"message": "2026-05-06 的 ARRIVAL 接机 已有多条派车行标记参与(派车行 1234567890123456789,1234567890123456790), 同一天同一方向只能有一条",
"data": null,
"success": false
}
```
本接口同样可能返回 602200 / 602201 / 602205(触发条件与「1. 派车批量创建」一致,见上方示例,
本次不重复贴)。
#### 业务边界
- `kind` 不传=`TRAVEL`,与批量派车保持一致
- 602202 只对 `TRANSFER` 需求生效,作用域严格限于本 `requirementId` 名下;已取消/异常状态的
派车行不计入判据(不代表现状)
- 跨需求的历史行重复不属本校验范围,归 AC-12 的 602204(本单未实现,预留号)
---
## 四、契约约束与正确调用方式
| 场景 | 做法 |
|------|------|
| `kind=TRANSFER` 且客人未填大交通 | 返 602205,**前端须引导先在订单侧补齐大交通再重试**(不要提示"接送机需求不存在") |
| `kind=TRANSFER` 但订单没有生效接送机需求 | 返 602200,需回订单侧确认/新建接送机需求 |
| 大交通声明的接送日超出接送机需求服务日 | 返 602201,需回订单侧核对大交通与服务日是否漂了 |
| 同需求同日同方向已有多条生效派车行标记参与 | 返 602202(仅接送机配置端点触发),需人工核对是数据错乱还是业务上确需两辆车 |
| `kind` 不传或传空字符串 | 按 `TRAVEL` 处理,行为与本次改动前逐字一致 |
| `kind` 传非 `TRAVEL`/`TRANSFER` 值 | 400 参数校验失败(`@Pattern` 拦在入参层,不到业务码) |
| `kind=TRANSFER` 派车行走「确认」`POST /requirements/{id}/confirm`、`POST /{assignmentId}/confirm`、「改派」`POST /{assignmentId}/change` | 返 605041(已知限制,三处内部基线复核未做 kind 感知,见「六、边界行为」),前端本版**暂不要接入**,只接「建接送机派车行」这一步 |
---
## 五、数据库行为
| 操作 | 影响 |
|------|------|
| `kind=TRANSFER` 且四步前置全部通过 | 正常写入/更新 `fleet_assignment` 行,落库结构不因 `kind` 改变 |
| `kind=TRANSFER` 但前置任一步失败(602200/602201/602205) | 直接拒绝,批量场景整批不落库 |
| `configurePickupDropoff` 写后校验 602202 | 抛出时整个事务回滚,本次接送机标志更新不落库 |
| `kind` 不传或 `TRAVEL` | 行为与改动前一致,无新增数据库副作用 |
---
## 六、边界行为
- `kind` 传空字符串/null:按 `TRAVEL` 处理
- `kind` 传非 `TRAVEL`/`TRANSFER` 枚举值:400 参数校验失败
- `kind=TRANSFER` 且回填端点返回「客人未填大交通」(`NO_TRANSPORT`):602205,**不再继续 GET**
- `kind=TRANSFER` 但回填端点返回「无生效接送机需求」(`NOT_FOUND`):602200
- `kind=TRANSFER` 且回填/GET 之间需求被撤(回填说有、GET 说没有):按 602200 处理(fail-closed,不当成"还在")
- `kind=TRANSFER` 且服务日解析仍为空(未知 outcome,或 strict GET 后 `serviceDates` 为空):602205(fail-closed,不兜底成行程日)
- 大交通声明要接送的日期不在接送机服务日内:602201(判据方向"声明日 ⊆ 服务日",服务日多出来的部分不算异常)
- 同需求同日同方向已有另一条生效派车行标记参与(仅 `TRANSFER`,仅 `configurePickupDropoff`):602202;`TRAVEL` 需求下同日多行标接机仍是既有合法形态,不受影响
- 已取消(canceled)/异常(exception)状态的派车行不计入 602202 判据
- ⚠️ **`kind=TRANSFER` 派车行建成后走「确认」「改派」必现 605041**(已知限制,2026-09-18
查实):`POST /requirements/{id}/confirm`(`confirmRequirement`)、`POST
/{assignmentId}/confirm`(`confirm`,含其 HOLDING 分支与基线变更后复验
`revalidateAssignedAfterBaselineChange` 两条内部路径)、`POST /{assignmentId}/change`
(改派 `doChangeInLock`)——这四处内部调用 `assertFinalConfirmationBaseline` 时基线需求
参数仍传 `null`,于是取用 order-v3 详情上下文里恒为 `TRAVEL` 的那条需求
(`getFleetDetailContextStrict` 写死 kind=TRAVEL)去和派车行的 `requirementId` 比对,
TRANSFER 需求 ID 必然不等,差异类型记为 `REQUIREMENT_VERSION_MISMATCH`("用车需求已更新,
请刷新后按最新需求重新派车"),整体抛 `605041 FINAL_CONFIRMATION_BASELINE_MISMATCH`
("订单行程或用车派单已变化,请按逐日差异处理后重试")。根因:`ConfirmAssignmentCommand`/
`ChangeAssignmentCommand`/`ConfirmRequirementCommand` 三个内部命令对象均不携带 `kind`、
`FleetAssignmentDO` 也无 kind 列,`RequirementKindResolver` 明确不提供按 requirementId
反查 kind 的入口;这四处调用又都在 `@Transactional` 事务内,临时补一次远程读会撞事务内
同步 Feign 的红线。已在 Gitea #7443 的 AC-24 登记,需单独设计(补齐三个命令对象的 kind
字段,或由 order-v3 在 `OrderFleetDetailContextDTO` 里一并带回 TRANSFER 那条需求)后再修,
属跨服务契约变更,本单不实现。**前端本版暂不要对接 TRANSFER 派车行的确认/改派操作。**
- 与之相对:`POST /admin/fleet/assignments/batch`(本文档接口 1)的同一基线复核缺口已于
2026-09-18 修复(PR #7913,commit `5a6bd95f8`)——批量创建改传锁内按 kind 复核过的
`lockedRequirement` 做基线,`kind=TRANSFER` 批量创建不再必现 605041
---
## 六.5 枚举(新增错误码)
**新增 4 个错误码**(`TransferDispatchErrorCode`,段位 602200-602299,归属 `hl-fleet-service`):
| 错误码 | 常量名 | 文案(含占位符) |
|--------|--------|------|
| 602200 | `TRANSFER_REQUIREMENT_NOT_FOUND` | 订单 {0} 没有生效的接送机用车需求, 无法按接送机派车 |
| 602201 | `TRANSFER_SERVICE_DATES_MISMATCH` | 大交通声明要接送的日期 {0} 不在接送机需求的服务日内, 请回订单侧核对大交通 |
| 602202 | `TRANSFER_DUPLICATE_PARTICIPANT` | {0} 的 {1} 已有多条派车行标记参与(派车行 {2}), 同一天同一方向只能有一条 |
| 602205 | `TRANSFER_SERVICE_DATES_UNRESOLVED` | 订单 {0} 的接送机需求 {1} 还没有服务日, 请先在订单侧补齐大交通后重试 |
**错误码段位**(`TransferDispatchErrorCode` 内部规划):
- 602200-602203/602205: 本工单(#7443)已用(602203 属上一 PR #7864,已在 2026-09-17 的
changelog 中交付,本文件不重复交付)
- 602204: 预留给 AC-12(存量认领撞上互斥历史生效行),本单未实现
---
## 六.6、修改前后对比
| 项目 | 改前 | 改后 |
|------|------|------|
| `BatchCreateAssignmentReqVO.kind` | 不存在 | 新增可选字段(`TRAVEL`\|`TRANSFER`,不传=`TRAVEL`) |
| `PickupDropoffConfigReqVO.kind` | 不存在 | 新增可选字段(同上) |
| `POST /admin/fleet/assignments/batch`(`kind=TRANSFER`) | 不支持按接送机需求分叉,恒取 `TRAVEL` 需求派车 | 按 `kind` 分流,`TRANSFER` 走独立四步前置校验 |
| `PUT /pickup-dropoff-config`(`kind=TRANSFER`) | 同上;无同日同方向重复参与校验 | 同上,另加写后 602202 校验(仅 `TRANSFER`) |
| `TransferDispatchErrorCode` 段内已用码 | 仅 602203(#7443 上一 PR #7864) | 新增 602200/602201/602202/602205 |
---
## 六.7、影响评估
- **向后兼容**: 是
- 新增参数可选,不传=`TRAVEL`,存量前端请求形状一个字不变
- `kind` 不传或为 `TRAVEL` 时两端点行为与改动前逐字一致
- **前端同步**: 是(必须)
- `kind=TRANSFER` 场景需处理 4 个新错误码,尤其 **602205 要引导先补大交通再重试**
- 若前端已有/将有"按接送机派车"的入口,需在请求体带上 `kind=TRANSFER`
- **数据**: 无需迁移,落库结构未变
---
## 七、不影响范围
- 单笔创建派单 `POST /admin/fleet/assignments`(该 VO 无 `kind` 入参,未改动)
- 车务确认执行 `POST /{assignmentId}/confirm`、按需求原子确认 `POST /requirements/{id}/confirm`、
改派 `POST /{assignmentId}/change`(三者 VO/入参本次均未改动;但 `kind=TRANSFER` 派车行走到
这三个端点目前必现 605041,是已知限制而非本次刻意变更,见「⚠️ 关键变化」与「六、边界行为」)
- 声明/撤销整段不用车 `POST|DELETE /requirements/{id}/no-vehicle`
- 看板列表、矩阵日订单等查询接口
- `kind` 不传或=`TRAVEL` 时两端点的既有校验与落库路径
---
## 八、测试环境已验证
2026-09-18 已做网关端到端,**AC-6 / AC-7 均通过**:
- 环境(取证前后各跑一次 `deploy-status.sh`,三行 COMMIT 未变):
`hl-gateway 631baab0c` / `hl-order-service-v3 9287e6eb9` / `hl-fleet-service 5a6bd95f8`
- `POST /admin/fleet/assignments/batch`,`kind="TRANSFER"`,`dailyPlan` 含 `2026-10-11` 与
`2026-10-17` ⇒ **`code=200`**,落库两行(`fleet_assignment`)服务日分别为 10-11 与 10-17、
`requirement_id` 都指向该 TRANSFER 需求,**未出现 605041 / 605062 / 605905**。
- 两方向独立性:两行车辆/司机不同;单独改接机行后,送机行的 `service_date` / `vehicle_id` /
`driver_id` / `status` **逐字段未变**。
> ⚠️ **必须同时写明的限定**:那条 TRANSFER 需求行是 **SQL 构造**的,不是走真实写口建的
> (因为「⚠️ 关键变化」里那个 809009 开关)。⇒ **已验证的是「车务侧拿到活跃 TRANSFER 需求
> 后能把车派到行程窗外」**;「订单侧真实写口能产出等价需求行」**不在本次证据范围内**。
> **不许把这条限定省掉写成"端到端全链路已验证"。**
单元层证据:
```
mvn -o -pl hl-fleet-service -am test(全量)→ Tests run: 4220, Failures: 0, Errors: 0, Skipped: 4,BUILD SUCCESS
完整性核对:322 个测试类 / 322 应跑(全部执行,无遗漏)
spotless:check → 846 files clean, 0 changed
```
代码已部署到测试环境:`hl-fleet-service` 跑在 `dev-v3` 分支的 `5a6bd95f8` 上(含 2026-09-18
的 605041 批次基线修复 PR #7913;Nacos healthy)。
以下三处不属于"未验证",而是"已知会失败"(AC-24 修完前不应验,验也是必现 605041,
详见「六、边界行为」):
```
POST /requirements/{id}/confirm、POST /{assignmentId}/confirm、POST /{assignmentId}/change
对 kind=TRANSFER 派车行的确认/改派 — 已知必现 605041,非本单验收范围
```
---
## 十、相关文档
- Issue: [#7443](https://git.1814.love:8443/wx/HL/issues/7443)
- PR: [#7911](https://git.1814.love:8443/wx/HL/pulls/7911)(AC-6~9 主体,merge
[9287e6eb9](https://git.1814.love:8443/wx/HL/commit/9287e6eb9))/
[#7912](https://git.1814.love:8443/wx/HL/pulls/7912)(AC-9 兜底落点对照测试,merge
[d2d63bcb6](https://git.1814.love:8443/wx/HL/commit/d2d63bcb6))/
[#7913](https://git.1814.love:8443/wx/HL/pulls/7913)(605041 批次基线修复,merge
[5a6bd95f8](https://git.1814.love:8443/wx/HL/commit/5a6bd95f8)——仅修复批量创建,确认/改派
缺口仍在,登记为 AC-24)
## 关联 / 联系人
### 链接
- **Issue**: [#7443](https://git.1814.love:8443/wx/HL/issues/7443)
- **PR**: [#7911](https://git.1814.love:8443/wx/HL/pulls/7911)(已合并,`9287e6eb9`)、
[#7912](https://git.1814.love:8443/wx/HL/pulls/7912)(已合并,`d2d63bcb6`)、
[#7913](https://git.1814.love:8443/wx/HL/pulls/7913)(已合并,`5a6bd95f8`,仅修复批量创建,
确认/改派缺口登记为 AC-24)
### 联系人
- **后端**: @wx