diff --git a/changelogs-v2/2026-09/17_7442_团级确认态-需求已发车务回写-新增接口-管理后台.md b/changelogs-v2/2026-09/17_7442_团级确认态-需求已发车务回写-新增接口-管理后台.md index 7b6bca5f..d7e7ecd9 100644 --- a/changelogs-v2/2026-09/17_7442_团级确认态-需求已发车务回写-新增接口-管理后台.md +++ b/changelogs-v2/2026-09/17_7442_团级确认态-需求已发车务回写-新增接口-管理后台.md @@ -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 } ``` diff --git a/changelogs-v2/2026-09/17_7443_团期身份失败关闭与看板矩阵按团筛选-修改接口-管理后台.md b/changelogs-v2/2026-09/17_7443_团期身份失败关闭与看板矩阵按团筛选-修改接口-管理后台.md index 3325caf4..877b5ecd 100644 --- a/changelogs-v2/2026-09/17_7443_团期身份失败关闭与看板矩阵按团筛选-修改接口-管理后台.md +++ b/changelogs-v2/2026-09/17_7443_团期身份失败关闭与看板矩阵按团筛选-修改接口-管理后台.md @@ -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 } ``` diff --git a/changelogs-v2/2026-09/18_7443_接送机用车需求分叉PR1派车入参新增需求类别kind-修改接口-管理后台.md b/changelogs-v2/2026-09/18_7443_接送机用车需求分叉PR1派车入参新增需求类别kind-修改接口-管理后台.md new file mode 100644 index 00000000..0e4546f5 --- /dev/null +++ b/changelogs-v2/2026-09/18_7443_接送机用车需求分叉PR1派车入参新增需求类别kind-修改接口-管理后台.md @@ -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