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>
这个提交包含在:
@@ -124,11 +124,10 @@ N/A(团期无可确认行时返 602007 错误)。
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"code": 602008,
|
||||
"message": "配车尚未覆盖完整, 不能确认: 乘车分组 A 缺失 2026-05-08",
|
||||
"data": null,
|
||||
"success": false,
|
||||
"errorCode": 602008
|
||||
"success": false
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
@@ -298,10 +298,10 @@ N/A(操作必返结果)。
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"code": 602203,
|
||||
"message": "订单的团期归属尚未回填, 无法派车",
|
||||
"success": false,
|
||||
"errorCode": 602203
|
||||
"data": null,
|
||||
"success": false
|
||||
}
|
||||
```
|
||||
|
||||
@@ -405,10 +405,10 @@ N/A(整批要么受理要么失败关闭,不存在「空成功」形态)
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"code": 602203,
|
||||
"message": "订单的团期归属尚未回填, 无法派车",
|
||||
"success": false,
|
||||
"errorCode": 602203
|
||||
"data": null,
|
||||
"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
|
||||
在新工单中引用
屏蔽一个用户