docs(changelog): 团期用车需求换组时配车迁移与 migratedAssignmentCount 下界字段(#8530)
changelog-filename-gate / validate (push) Failing after 2s
changelog-filename-gate / validate (push) Failing after 2s
PUT /v3/admin/order/{id}/vehicle-requirement 响应新增 migratedAssignmentCount。
该字段是下界而非真实迁移条数:它统计的是上一版需求上挂着的已排车行数,
连续换组 A→B→C 时 B→C 这一次会读到 0,但迁移确实发生过。
Refs #8530
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
这个提交包含在:
@@ -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\<FleetItem\> | 是 | 至少 1 项 | 车型组合 |
|
||||
| `fleet[].vehicleType` | Body | String | 是 | `suv`/`mpv`/`bus`/`sedan` | 车型大类 key(只选大类,不选具体车型) |
|
||||
| `fleet[].seats` | Body | Integer | 是 | 需在该大类座位选项内 | 座位数 |
|
||||
| `fleet[].count` | Body | Integer | 是 | >0 | 辆数 |
|
||||
| `specialTags` | Body | List\<String\> | 否 | 需在字典 `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\<LocalDate\> | 本版冻结的服务日期,升序去重 |
|
||||
| `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
|
||||
在新工单中引用
屏蔽一个用户