From 1ecfa85f54eb07a4aea009cc346db06b9614ea12 Mon Sep 17 00:00:00 2001 From: API Changelog Bot Date: Tue, 29 Sep 2026 22:46:53 +0800 Subject: [PATCH] =?UTF-8?q?docs(changelog):=20=E5=9B=A2=E6=9C=9F=E7=94=A8?= =?UTF-8?q?=E8=BD=A6=E9=9C=80=E6=B1=82=E6=8D=A2=E7=BB=84=E6=97=B6=E9=85=8D?= =?UTF-8?q?=E8=BD=A6=E8=BF=81=E7=A7=BB=E4=B8=8E=20migratedAssignmentCount?= =?UTF-8?q?=20=E4=B8=8B=E7=95=8C=E5=AD=97=E6=AE=B5=EF=BC=88#8530=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PUT /v3/admin/order/{id}/vehicle-requirement 响应新增 migratedAssignmentCount。 该字段是下界而非真实迁移条数:它统计的是上一版需求上挂着的已排车行数, 连续换组 A→B→C 时 B→C 这一次会读到 0,但迁移确实发生过。 Refs #8530 Co-Authored-By: Claude Opus 5 (1M context) --- ...¦需求换组配车迁移下界字段-修改接口-管理后台.md | 332 ++++++++++++++++++ 1 file changed, 332 insertions(+) create mode 100644 changelogs-v2/2026-09/29_8530_团期用车需求换组配车迁移下界字段-修改接口-管理后台.md diff --git a/changelogs-v2/2026-09/29_8530_团期用车需求换组配车迁移下界字段-修改接口-管理后台.md b/changelogs-v2/2026-09/29_8530_团期用车需求换组配车迁移下界字段-修改接口-管理后台.md new file mode 100644 index 00000000..c26e0568 --- /dev/null +++ b/changelogs-v2/2026-09/29_8530_团期用车需求换组配车迁移下界字段-修改接口-管理后台.md @@ -0,0 +1,332 @@ +--- +schema: "hl-changelog/v2" +ticket: "8530" +title: "团期用车需求换组时把旧版已排车行整槽平移,响应新增 migratedAssignmentCount 平移下界字段" +consumer: "admin" +author: "wx(GIT)" +change_type: "修改接口" +backend_status: "deployed" +gateway_status: "verified" +frontend_status: "pending" +frontend_owner: "" +frontend_ref: "" +target_release: "" +verified_at: "" +status_note: "PR #8563 合并 dev-v3(cb876bf780);hl-order-service-v3 + hl-fleet-service 已随该提交部署测试网关;order-v3 新增单测 6 条(RequirementServiceTest)+ fleet 新增单测 6 条(AssignmentServiceTest)+ mapper 层 2 条 + 跨服务常量 1 条,共 15 条,均为换组迁移/无配车迁移/同内容重放不迁移/非团期恒 0/订单终态不迁移等场景的定向用例。" +updated_at: "2026-09-29" +base: "dev-v3" +--- + +# 团期用车需求换组:响应新增 migratedAssignmentCount 平移下界字段 + +> **存放目录**: `changelogs-v2/2026-09/` +> **服务**: hl-order-service-v3(响应端点所在服务)+ hl-fleet-service(旧版配车整槽平移的执行方,经 Outbox/Feign 异步处理,前端不直接调用它) +> **PR**: #8563 +> **Issue**: #8530 +> **日期**: 2026-09-29 +> **影响范围**: 管理后台订单详情页「提交/修改/调整用车需求」接口的响应体(仅团期子订单换组场景新增字段值有意义) + +--- + +## ⚠️ 关键变化 + +- 团期子订单在「改提/换组」用车需求时,旧版本名下已经排好的车行,此前会原地留在已失活的旧 `requirementId` 下,车务侧对这些行的任何后续推进都会撞错误码 605905/605913 且没有任何提示;现在后端会在换组的同一时刻把这些行整槽平移到新需求。 +- 响应 VO `VehicleRequirementRespVO` 新增字段 `migratedAssignmentCount`(`Integer`,恒非 `null`):本次换版交给车务平移的配车基数(下界),**不是**真实迁移行数。它在所有分支(首提/改提/完成后调整/幂等重放)都会回填,取 0 或正整数,从不为 `null`。 + +--- + +## 二、变更接口清单 + +| # | 接口 | 方法 | 路径 | 变更类型 | 说明 | +|---|------|------|------|----------|------| +| 1 | 提交/修改/调整用车需求 | PUT | `/v3/admin/order/{id}/vehicle-requirement` | 响应新增字段 | `data.migratedAssignmentCount` | + +--- + +## 三、接口详情 + +### 1. 提交/修改/调整用车需求 `PUT /v3/admin/order/{id}/vehicle-requirement` + +**VO**: `VehicleRequirementReqVO` → `VehicleRequirementRespVO` + +#### 使用场景 + +定制师/团期管理员在订单详情页提交、修改或调整用车需求。后端按 `order_vehicle_requirement` 表当前 active 行是否存在及其状态自动判三分支:无 active → `INIT_SUBMIT` 首提;`PENDING` → `PENDING_EDIT` 改提;`DONE` → `DONE_ADJUST` 完成后调整。同内容重放(与当前生效版本完全一致)额外命中 `IDEMPOTENT_NOOP`,不换版。`consultantId` 由后端从 JWT 解析,不接受前端传入。 + +团期子订单每次「改提/换组」(即 `PENDING_EDIT` 分支且确实发生换版)都会触发本次改动:旧版本名下已经排好的车行会被整槽平移到新需求,响应回填 `migratedAssignmentCount` 说明本次交给车务平移的基数。非团期(核心)订单、首次提交、同内容重放三种情况都不触发平移,字段恒为 0。 + +#### 入参字段表 + +| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | +|------|------|------|------|------|------| +| `id` | Path | Long | 是 | 正整数 ID | 订单 ID | +| `kind` | Body | String | 否 | `TRAVEL`/`TRANSFER`,不传按 `TRAVEL` | 需求类别:`TRAVEL`=团期行程用车(服务日冻结为行程日),`TRANSFER`=接送机(服务日由大交通派生) | +| `fleet` | Body | List\ | 是 | 至少 1 项 | 车型组合 | +| `fleet[].vehicleType` | Body | String | 是 | `suv`/`mpv`/`bus`/`sedan` | 车型大类 key(只选大类,不选具体车型) | +| `fleet[].seats` | Body | Integer | 是 | 需在该大类座位选项内 | 座位数 | +| `fleet[].count` | Body | Integer | 是 | >0 | 辆数 | +| `specialTags` | Body | List\ | 否 | 需在字典 `vehicle_special_demand` 内 | 通用特殊诉求标签,应用到全部车辆 | +| `pickupRequired` | Body | Boolean | 否 | - | 兼容字段,接机/接站以实时大交通批次为准 | +| `dropoffRequired` | Body | Boolean | 否 | - | 兼容字段,送机/送站以实时大交通批次为准 | +| `remark` | Body | String | 否 | ≤500 字符 | 备注 | + +本次改动**未新增或修改任何入参字段**,上表为该端点既有契约,供本节自包含阅读。 + +#### 出参字段表 + +| 字段 | 类型 | 说明 | +|------|------|------| +| `data.id` | Long | 需求行 ID | +| `data.kind` | String | 需求类别:`TRAVEL`/`TRANSFER` | +| `data.serviceDates` | List\ | 本版冻结的服务日期,升序去重 | +| `data.version` | Integer | 版本号 | +| `data.isActive` | Boolean | 是否为当前生效版本 | +| `data.status` | String | 需求状态(`PENDING`/`PENDING_REVIEW`/`PROCESSING`/`DONE` 等) | +| `data.branchTaken` | String | 实际走的分支:`INIT_SUBMIT`/`PENDING_EDIT`/`DONE_ADJUST`/`IDEMPOTENT_NOOP` | +| `data.previousVersion` | Integer/null | `DONE_ADJUST` 分支回填上一版本号;其余分支为 `null` | +| `data.assignmentDeletedCount` | Integer/null | `DONE_ADJUST` 分支回填软删旧配车行数;其余分支为 `null` | +| `data.migratedAssignmentCount` | Integer | **【新增】** 本次换版交给车务平移的配车基数(下界),恒非 `null`;语义见下方业务边界 | +| `data.passengerCount` | Integer | 订单乘车人数(成人+儿童+幼童+婴儿),结构不变 | +| `data.vehicleCount` | Integer | 车辆总数,结构不变 | +| `data.totalSeatCount` | Integer | 车辆座位总数(含司机座),结构不变 | +| `data.driverSeatCount` | Integer | 司机占用座位数,结构不变 | +| `data.passengerSeatCapacity` | Integer | 可载客座位数,结构不变 | +| `data.remainingPassengerSeats` | Integer | 剩余可载客座位数,结构不变 | +| `data.pickupRequired` / `data.dropoffRequired` | Boolean | 兼容回显字段,结构不变 | +| `data.submittedAt` / `data.claimerId` / `data.claimerName` / `data.claimedAt` | - | 结构不变 | + +#### 请求示例 + +```json +{ + "kind": "TRAVEL", + "fleet": [ + { "vehicleType": "mpv", "seats": 7, "count": 1 } + ], + "specialTags": ["中文司机"], + "remark": "客户要求中文司机" +} +``` + +#### 响应示例 + +团期子订单换组,旧版名下有 3 行在途配车镜像(`branchTaken=PENDING_EDIT` 且触发平移): + +```json +{ + "code": 200, + "message": "成功", + "success": true, + "data": { + "id": "2810002", + "kind": "TRAVEL", + "serviceDates": ["2026-07-19", "2026-07-23"], + "version": 2, + "isActive": true, + "status": "PENDING_REVIEW", + "branchTaken": "PENDING_EDIT", + "previousVersion": null, + "assignmentDeletedCount": null, + "migratedAssignmentCount": 3, + "passengerCount": 2, + "vehicleCount": 1, + "totalSeatCount": 7, + "driverSeatCount": 1, + "passengerSeatCapacity": 6, + "remainingPassengerSeats": 4, + "pickupRequired": true, + "dropoffRequired": true + } +} +``` + +首次提交 / 同内容重放(`IDEMPOTENT_NOOP`),没有旧版可迁移: + +```json +{ + "code": 200, + "message": "成功", + "success": true, + "data": { + "id": "2812001", + "branchTaken": "IDEMPOTENT_NOOP", + "migratedAssignmentCount": 0 + } +} +``` + +#### 空数据 / 降级响应 + +本接口不存在空数据形态:请求参数合法时恒返回单条需求行;下游依赖(车队字典等)不可用时走错误响应,不降级为空对象。 + +#### 错误响应 + +```json +{ + "code": 582021, + "message": "用车需求数组不能为空", + "success": false, + "data": null +} +``` + +```json +{ + "code": 582030, + "message": "当前需求状态不可修改(处理中)", + "success": false, + "data": null +} +``` + +本次改动**未新增任何错误码**,以上两例是该端点既有校验错误码,供本节自包含阅读。 + +#### 业务边界 + +- `migratedAssignmentCount` 是「下界」不是真实迁移行数:取值 = 换组前那一版 requirement 上挂着的已排车行数(按上一版 `requirementId` 统计 `order_vehicle_assignment`);真实平移由车务侧异步幂等执行,只搬活跃非取消行,可能小于等于该下界。 +- 连续换组(A→B 再 B→C)第二次读到的恒为 0,但迁移确实发生了:A→B 那一次已经把行迁到 B 名下,B→C 这一次按「上一版」(B)统计,B 名下此时还没有新快照(新快照要等车务重新确认后才按新需求落),统计结果恒为 0。**前端不能把 0 解读成"这条链路从未发生过迁移",只能解读成"本次调用没有新的迁移基数"。** +- 首次提交(`INIT_SUBMIT`)与同内容重放的幂等分支(`IDEMPOTENT_NOOP`):字段恒为 0,且不触发平移信号。 +- 非团期(核心)订单:字段恒为 0,且不额外查询配车镜像表(不新增一次 DB 往返),行为与本次改动之前完全一致。 +- 订单处于终态(`COMPLETED`/`CANCELLED`)时不触发平移信号,字段为 0——即使是经由"订单调整"入口提交(该入口本身绕过普通提交闸),终态订单同样不发平移。 +- 该字段在全部四个分支(`INIT_SUBMIT`/`PENDING_EDIT`/`DONE_ADJUST`/`IDEMPOTENT_NOOP`)都会回填,恒非 `null`。 + +--- + +## 四、契约约束与正确调用方式 + +> 本节只写后端字段语义与正确/错误解读,不写 UI 渲染建议。 + +### ✅ 正确 / ❌ 错误解读对照 + +| 场景 | 解读 | +|------|------| +| ✅ `migratedAssignmentCount > 0` | 本次换版确有旧版配车行被交给车务侧平移 | +| ✅ `migratedAssignmentCount = 0` 且 `branchTaken = INIT_SUBMIT` / `IDEMPOTENT_NOOP` | 本来就没有触发换版,字段恒为 0,属正常值 | +| ✅ `migratedAssignmentCount = 0` 且 `branchTaken = PENDING_EDIT` | 旧版名下当时没有在途配车镜像,或本次读到的是连续换组链路中的中间一跳 | +| ❌ 用单次 `= 0` 断言"这条订单从未发生过配车平移" | 连续换组 A→B→C 时,B→C 这一次恒读 0,但 A→B 时已经迁移过;单次响应不是整条历史的累计值 | + +### 不需要的前置动作 + +该字段只出现在响应里,不改变入参契约:请求体字段零变化,无需为这个字段额外传参或做请求前置。 + +--- + +## 五、数据库行为 + +`migratedAssignmentCount` **不落库、不是新增列**:它是响应组装时的即时统计结果,等价于: + +``` +SELECT COUNT(*) FROM order_vehicle_assignment +WHERE requirement_id = <换组前的旧 requirementId> AND deleted = 0 +``` + +需求行本身仍按既有逻辑落库(旧版 `deactivate` + 新版 `insert`),本次未新增、未变更任何表结构或列。真实的配车行迁移(把 `order_vehicle_assignment.requirement_id` 从旧值改写为新值)发生在车务侧(`hl-fleet-service`),经异步 Outbox/Feign 通道执行,与本端点的同步响应解耦。 + +--- + +## 六、边界行为 + +- 未登录 → 401(网关拦截)。 +- 入参校验失败(`fleet` 为空、车型非法等)→ 既有错误码,HTTP 200,`data=null`。 +- 非团期(核心)订单:`migratedAssignmentCount` 恒为 0,且不额外查询 `order_vehicle_assignment`,行为与本次改动之前一字不变。 +- 订单终态(`COMPLETED`/`CANCELLED`):不触发平移信号,`migratedAssignmentCount=0`。 +- 业务失败仍可能是 HTTP 200,需同时检查 `code`、`success` 和 `message`。 + +--- + +## 六.5、枚举 / 数据字典 + +本次未新增或变更任何枚举取值。`branchTaken` 沿用既有四个取值,未变化: + +### branchTaken(响应字段) + +**所属字段**: `data.branchTaken` | **类型**: `String` + +| 值 | 中文 | 说明 | +|----|------|------| +| `INIT_SUBMIT` | 首次提交 | 订单无 active 需求行时的分支 | +| `PENDING_EDIT` | 改提/换组 | 当前 active 需求为 `PENDING` 时的分支;团期订单在此分支触发配车平移 | +| `DONE_ADJUST` | 完成后调整 | 当前 active 需求为 `DONE` 时的分支 | +| `IDEMPOTENT_NOOP` | 幂等重放 | 提交内容与当前生效版本完全一致,不换版、不触发平移 | + +--- + +## 六.6、修改前后对比 + +### 字段级对比 + +| 字段 | 改前 | 改后 | +|------|------|------| +| `data.migratedAssignmentCount` | 不存在 | 新增,`Integer`,恒非 `null`,团期换组场景回填平移基数,其余场景为 0 | + +### 行为级对比 + +| 行为 | 改前 | 改后 | +|------|------|------| +| 团期子订单改提/换组时,旧版名下已排的车行 | 留在已失活的旧 `requirementId` 下,车务对其任何后续推进都会撞 605905/605913,且没有任何信号 | 换组的同一时刻整槽平移到新需求,响应回填平移基数 | +| 团期换组是否发布逐日配车快照 | 不适用(旧版本就是孤儿状态,不发快照) | 平移这一档只做整槽平移,仍不发布逐日快照(新需求停在 `PENDING_REVIEW`,须经管理员审核 + C2 提交车务后才放行) | + +--- + +## 六.7、影响评估 + +- **是否破坏向后兼容**: 否。新增响应字段,旧前端忽略它不受影响;入参契约与既有错误码零变化。 +- **前端是否必须同步上线**: 否。字段是可选适配;若前端不读取该字段,端点行为(换组成功、旧配车被平移)照常生效,只是前端看不到"本次平移了几行"这个信息。 +- **前端 workaround 清理点**: 无。此前该场景下前端没有任何字段可用于感知"旧配车是否被平移",因此没有需要清理的旧逻辑。 + +--- + +## 七、不影响范围 + +- **仅影响**: 团期子订单在订单详情页「改提/换组」用车需求后的响应体,以及旧版已排车行是否被后端平移这一行为。 +- **零影响**: + - 请求参数(`VehicleRequirementReqVO` 零变化) + - 首次提交(`INIT_SUBMIT`)与完成后调整(`DONE_ADJUST`)两个分支的既有字段语义(`previousVersion`/`assignmentDeletedCount` 等) + - 非团期(核心)订单提交用车需求的行为——该路径 `migratedAssignmentCount` 恒 0 且不新增 DB 查询,与改动前完全一致 + - 该端点既有错误码(零新增) + - 派单看板、逐日配车快照发布等下游读接口的响应结构(本次不改写它们) + +--- + +## 八、测试环境已验证 + +服务:`hl-order-service-v3` + `hl-fleet-service`,合并提交 `cb876bf780`(PR #8563)已合入 `dev-v3` 并部署测试网关。 + +新增单测(均为源码可核实的真实用例,覆盖以下场景): + +`hl-order-service-v3` `RequirementServiceTest`(6 条): +- `upsertVehicle_groupOrderPendingEdit_migratesPreviousRequirement`:团期改提换版 → 登记平移信号,`migratedAssignmentCount=3`(按旧需求 ID 精确统计,非按新需求 ID 误统计) +- `upsertVehicle_groupOrderPendingEditNoAssignment_migratedCountZero`:旧需求名下无在途配车 → 仍登记平移信号,`migratedAssignmentCount=0` +- `upsertVehicle_groupOrderSameContent_idempotentNoopNoMigrate`:同内容重放(`IDEMPOTENT_NOOP`)→ 不登记平移信号,不查询镜像表,`migratedAssignmentCount=0` +- `upsertVehicle_coreOrderPendingEdit_migratedCountZero`:非团期订单改提 → `migratedAssignmentCount` 恒 0,不新增 DB 查询 +- `upsertVehicleForOrderAdjustment_groupOrderCompleted_noMigrate`:订单已完成(`COMPLETED`)→ 不发平移信号 +- `upsertVehicleForOrderAdjustment_groupOrderNotTerminal_stillMigrates`:同订单调整入口、订单非终态 → 照常发平移信号(上一条的阳性对照) + +`hl-fleet-service` `AssignmentServiceTest`(6 条,验证可派性闸放宽范围不外溢): +- `expand_团期改提新需求待审核_放行整槽平移` +- `expand_非待审核需求换版平移_仍按既有出口发布逐日快照`(阳性对照) +- `expand_不可派且非待审核需求_仍然拒绝` +- `expand_待审核需求但无前序需求_仍然拒绝` +- `create_当前需求待审核_仍抛605906`(放宽不外溢到一步派定) +- `restoreCancel_当前需求待审核_仍拒绝恢复`(放宽不外溢到撤销取消) + +另有 mapper 层 `OrderVehicleAssignmentMapperTest` 新增 2 条、跨服务状态字面量对齐 `RequirementStatusTest` 新增 1 条。 + +--- + +## 十、相关文档 + +- 关联 Issue: [wx/HL#8530](https://git.1814.love/wx/HL/issues/8530) +- 关联 PR: [wx/HL#8563](https://git.1814.love/wx/HL/pulls/8563) + +## 关联 / 联系人 + +### 链接 + +- **Issue**: [#8530](https://git.1814.love/wx/HL/issues/8530) +- **PR**: [#8563](https://git.1814.love/wx/HL/pulls/8563) +- **Merge commit**: [cb876bf780](https://git.1814.love/wx/HL/commit/cb876bf780) + +### 联系人 + +- **后端负责人**: @wx