docs(changelog): 团期配车三项契约变更交接件(#8543 #8548 #8549 #8560)
changelog-filename-gate / validate (push) Failing after 2s
changelog-filename-gate / validate (push) Failing after 2s
- #8543 团期订单 Tab 用车状态三处读口按需求类别拆分并统一文案(PR #8592) - #8548/#8549 整团免车放行户级接送机需求,换组重开团期补待审户读数(PR #8586) - #8560 矩阵未派订单卡下发 requirementKind,可区分行程用车与接送机(PR #8589) 三份均已在测试网关实测取证,两道门禁(frontmatter 校验 + changelog_workflow lint)全绿。 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
这个提交包含在:
@@ -0,0 +1,329 @@
|
|||||||
|
---
|
||||||
|
schema: "hl-changelog/v2"
|
||||||
|
ticket: "8543"
|
||||||
|
title: "团期订单 Tab 用车状态三处读口按需求类别拆分并统一文案"
|
||||||
|
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 #8592 合并 dev-v3(bc80606ac2);hl-order-service-v3 dev-v3 分支部署测试网关 @ 99fb369ba 并实测:配车芯片明细端点 items[] 按用车类别拆项、同一 orderId 出现两次(TRAVEL/TRANSFER 各一);totalCount/doneCount 由户数变为条目数(实测 5 项 3 完成,对应 3 户);子订单列表新增 vehicleRequirementKind 字段恒为 TRAVEL;文案分支待车队配/配车完成/待提交车务/待审核均已实测复现;团期列表页 chipStats.vehicle 与明细端点同步联动同一计数口径。"
|
||||||
|
updated_at: "2026-09-30"
|
||||||
|
base: "dev-v3"
|
||||||
|
---
|
||||||
|
|
||||||
|
# hl-order-service-v3: 团期订单 Tab 用车状态三处读口按需求类别拆分并统一文案
|
||||||
|
|
||||||
|
> **存放目录**: `changelogs-v2/2026-09/`
|
||||||
|
> **服务**: hl-order-service-v3 (端口 8086)
|
||||||
|
> **PR**: #8592
|
||||||
|
> **Issue**: #8543
|
||||||
|
> **日期**: 2026-09-30
|
||||||
|
> **影响范围**: 团期配车芯片明细端点 `GET /v3/admin/order/group-batch/{groupBatchId}/chips/vehicle`、团期下子订单列表端点 `GET /v3/admin/order/group-batch/{groupBatchId}/orders`;团期列表/看板端点的 `chipStats.vehicle` 计数口径联动变化(无字段新增,纯语义联动)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ⚠️ 关键变化
|
||||||
|
|
||||||
|
- 🔴 **破坏性变更**:`GET .../chips/vehicle` 的 `items[]` 从**一户一项**改为**一户每类一项**——同一户同时有"行程用车"(TRAVEL)与"接送机"(TRANSFER)两类需求时,`items[]` 里会出现两条 `orderId` 相同、`kind` 不同的记录。**前端不得再按 `orderId` 去重**,去重会随机丢掉其中一类需求的状态。
|
||||||
|
- `totalCount` / `doneCount` 的口径随之从"户数"变为"条目数":一户两类需求算两条,分母跟着变大。实测团期批次 T26-3963 为例:3 户、5 条需求行,`totalCount=5`(不是 3),`doneCount=3`。
|
||||||
|
- `GroupBatchChipItemRespVO`(仅配车芯片会用到)新增 `kind`(TRAVEL/TRANSFER,可空)与 `kindName`(配对中文名,可空)两个字段;房/导/摄/约/保五个芯片的 `items[]` 里这两个字段恒为 `null`。
|
||||||
|
- `GroupBatchOrderItemRespVO`(子订单列表 `.../orders` 出参)新增 `vehicleRequirementKind` 与 `vehicleRequirementKindName` 两个字段——是新增字段不是破坏性变更。这一列**当前恒为 TRAVEL**:子订单列表每户只占一行,装不下两类需求,上游按 TRAVEL 过滤取行;要看接送机需求要走配车芯片明细(一户两类各占一项)。
|
||||||
|
- 用车状态文案本次统一(此前配车芯片明细端点的映射与子订单列表端点各写各的,现在两处一致):`PENDING`→**待车队配**、`PENDING_REVIEW` 按 `kind` 分叉(TRAVEL 出**待提交车务**、TRANSFER 出**待审核**)、`REJECTED_TO_CONSULTANT`→**已驳回定制师**、`REJECTED_TO_ADMIN`→**已驳回管理员**、`DONE`→**配车完成**。
|
||||||
|
- 该户 `needsIt=true` 但尚无任何 active 车需求行时(已声明要用车、但一行都没提交),配车芯片明细该户仍占一项,`kind`/`status` 为 `null`、`statusName` 为**未提交**(源码逻辑与 PR 说明已确认,本轮实测样本未覆盖到该分支,样本团期均已提交需求行)。
|
||||||
|
- 团期列表页(`GET /v3/admin/order/group-batch`)与看板端点每行的 `chipStats.vehicle.total`/`chipStats.vehicle.done` 与本次改动**共用同一套聚合算法**(`GroupBatchChipResolver.aggregateAll`),因此同步从"户数"变为"条目数"——实测同一团期批次两处读数逐位一致(5/3 与 3/1)。这两个端点本身**没有新增字段**,只是既有的 `total`/`done` 计数口径联动变了,前端若在列表页展示"配车 X/Y"这类徽标也要同步理解口径变化。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 二、变更接口清单
|
||||||
|
|
||||||
|
| # | 接口 | 方法 | 路径 | 变更类型 | 说明 |
|
||||||
|
|---|------|------|------|----------|------|
|
||||||
|
| 1 | 团期配车芯片明细 | GET | `/v3/admin/order/group-batch/{groupBatchId}/chips/vehicle` | 🔴 破坏性变更 + 字段新增 | `items[]` 按用车类别拆项,同一 `orderId` 可出现两次;新增 `kind`/`kindName`;`totalCount`/`doneCount` 语义由户数变为条目数 |
|
||||||
|
| 2 | 团期下子订单列表 | GET | `/v3/admin/order/group-batch/{groupBatchId}/orders` | 字段新增 | 新增 `vehicleRequirementKind`/`vehicleRequirementKindName`(当前恒为 TRAVEL);`vehicleRequirementStatusName` 的 `PENDING_REVIEW` 文案按 `kind` 分叉 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 三、接口详情
|
||||||
|
|
||||||
|
### 1. 团期配车芯片明细 `GET /v3/admin/order/group-batch/{groupBatchId}/chips/vehicle`
|
||||||
|
|
||||||
|
**VO**: `(无请求体,仅路径参数)` → `GroupBatchChipDetailVO`
|
||||||
|
|
||||||
|
#### 使用场景
|
||||||
|
|
||||||
|
车务/团期管理员在团期订单 Tab 展开"配车"芯片查看逐户用车状态明细时调用。六个芯片(房/车/导/摄/约/保)共用同一套 `GroupBatchChipDetailVO` 响应结构与六个并列端点,本条目专指配车芯片(其余五芯片本次未受影响,`kind`/`kindName` 在那五芯片下恒为 `null`)。
|
||||||
|
|
||||||
|
#### 入参字段表
|
||||||
|
|
||||||
|
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|
||||||
|
|------|------|------|------|------|------|
|
||||||
|
| groupBatchId | Path | Long | ✅ | - | 团期批次 ID |
|
||||||
|
|
||||||
|
#### 出参字段表
|
||||||
|
|
||||||
|
以下是本次新增/变化的字段;`GroupBatchChipDetailVO` 顶层其余字段(`chipLabel`/`aggregateStatus`/`aggregateStatusName`/`staffList`)与 `items[]` 内未变化的字段(`orderId`/`orderNo`/`teamNo`/`customerName`/`peopleCount`/`needsIt`/`claimerId`/`claimerName`/`claimerSource`/`updateTime`/`staffs`)结构未变,不重复列出。
|
||||||
|
|
||||||
|
| 字段 | 类型 | 说明 |
|
||||||
|
|------|------|------|
|
||||||
|
| totalCount | Integer | 🔴 语义变化:改前是"计入统计的户数",改后是"条目数"(一户两类需求算两条) |
|
||||||
|
| doneCount | Integer | 🔴 语义变化:口径随 `totalCount` 同步改为条目数 |
|
||||||
|
| items[] | List | 🔴 数组长度语义变化:一户两类需求时占两个元素,`orderId` 相同 |
|
||||||
|
| items[].kind | String,可空 | 新增:用车类别(`TRAVEL` 行程用车 / `TRANSFER` 接送机);该户尚无任何 active 车需求行时为 `null`(此时该户仍占一项,`status` 为 `null`,`statusName` 为"未提交");房/导/摄/约/保五芯片恒为 `null` |
|
||||||
|
| items[].kindName | String,可空 | 新增:`kind` 配对中文名(行程用车/接送机);`kind` 为 `null` 或枚举外未知码时为 `null`——编码不回落当中文展示 |
|
||||||
|
| items[].statusName | String,可空 | 文案统一:`PENDING`→待车队配、`PENDING_REVIEW` 按 `kind` 分叉(TRAVEL→待提交车务,TRANSFER→待审核)、`REJECTED_TO_CONSULTANT`→已驳回定制师、`REJECTED_TO_ADMIN`→已驳回管理员、`DONE`→配车完成 |
|
||||||
|
|
||||||
|
#### 请求示例
|
||||||
|
|
||||||
|
```http
|
||||||
|
GET /v3/admin/order/group-batch/2104839654727618562/chips/vehicle
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 响应示例
|
||||||
|
|
||||||
|
真实实测(团期批次 T26-3963,3 户 5 项,含同一 `orderId` 两类需求各占一项):
|
||||||
|
|
||||||
|
```json
|
||||||
|
{"code":200,"message":"成功","data":{"batchId":"2104839654727618562","groupBatchId":"2104839654727618562","chipLabel":"配车","aggregateStatus":"DOING","aggregateStatusName":"进行中","totalCount":5,"doneCount":3,"staffList":null,"items":[{"orderId":"2104839654652121090","orderNo":"HL20260929154414207","teamNo":"26-2313","contactName":"李文博","customerName":"李文博","peopleCount":2,"status":"DONE","statusText":"配车完成","statusName":"配车完成","needsIt":true,"kind":"TRAVEL","kindName":"行程用车","updateTime":null,"claimerId":null,"claimerName":null,"claimerSource":null,"staffs":null},{"orderId":"2104839654652121090","orderNo":"HL20260929154414207","teamNo":"26-2313","contactName":"李文博","customerName":"李文博","peopleCount":2,"status":"PENDING","statusText":"待车队配","statusName":"待车队配","needsIt":true,"kind":"TRANSFER","kindName":"接送机","updateTime":null,"claimerId":null,"claimerName":null,"claimerSource":null,"staffs":null},{"orderId":"2104839686486888449","orderNo":"HL20260929154421828","teamNo":"26-9436","contactName":"张丽娟","customerName":"张丽娟","peopleCount":2,"status":"DONE","statusText":"配车完成","statusName":"配车完成","needsIt":true,"kind":"TRAVEL","kindName":"行程用车","updateTime":null,"claimerId":null,"claimerName":null,"claimerSource":null,"staffs":null},{"orderId":"2104839729176514562","orderNo":"HL20260929154432061","teamNo":"26-6559","contactName":"那顺","customerName":"那顺","peopleCount":3,"status":"DONE","statusText":"配车完成","statusName":"配车完成","needsIt":true,"kind":"TRAVEL","kindName":"行程用车","updateTime":null,"claimerId":null,"claimerName":null,"claimerSource":null,"staffs":null},{"orderId":"2104839729176514562","orderNo":"HL20260929154432061","teamNo":"26-6559","contactName":"那顺","customerName":"那顺","peopleCount":3,"status":"PENDING_REVIEW","statusText":"待审核","statusName":"待审核","needsIt":true,"kind":"TRANSFER","kindName":"接送机","updateTime":null,"claimerId":null,"claimerName":null,"claimerSource":null,"staffs":null}]},"traceId":null,"success":true}
|
||||||
|
```
|
||||||
|
|
||||||
|
另一团期批次(T26-6001,含"待提交车务"文案分支,TRAVEL 类 `PENDING_REVIEW`)实测节选:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{"orderId":"2104840685708525570","orderNo":"HL20260929154820126","teamNo":"26-0805","contactName":"谢丽萍","customerName":"谢丽萍","peopleCount":2,"status":"PENDING_REVIEW","statusText":"待提交车务","statusName":"待提交车务","needsIt":true,"kind":"TRAVEL","kindName":"行程用车","updateTime":null,"claimerId":null,"claimerName":null,"claimerSource":null,"staffs":null}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 空数据 / 降级响应
|
||||||
|
|
||||||
|
- 团期批次不存在:返回业务错误(见"错误响应"),`data` 为 `null`。
|
||||||
|
- 团期下暂无子订单:`items` 为空数组,`totalCount`/`doneCount` 均为 `0`。
|
||||||
|
- 户已声明要用车(`needs_vehicle=true`)但尚未提交任何车需求行:该户仍占一项,`kind`/`status` 为 `null`,`statusName` 为"未提交"(源码逻辑已确认,本轮实测样本未覆盖到该分支)。
|
||||||
|
|
||||||
|
#### 错误响应
|
||||||
|
|
||||||
|
```json
|
||||||
|
{"code":589500,"message":"团期不存在","data":null,"traceId":null,"success":false}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 业务边界
|
||||||
|
|
||||||
|
- 🔴 `items[]` 按 `orderId` 去重是错误用法——同一户两类需求会被拆成两个数组元素,去重会随机丢掉其中一类的状态展示。
|
||||||
|
- `totalCount`/`doneCount` 不再等于该团期的户数,若页面上另有独立的"户数"展示(如团期基础信息),不要复用这两个字段去推导户数。
|
||||||
|
- `kind`/`kindName` 只在配车芯片有意义,其余五芯片(房/导/摄/约/保)该字段恒为 `null`,前端渲染这五个芯片时不需要处理 `kind` 分支。
|
||||||
|
- `statusName` 的四个文案改动是全量替换(不是新增枚举值),旧文案(待车务配/驳回给定制师/驳回给管理员)不会再出现,前端若按旧文案字符串做过特殊判断需要同步更新。
|
||||||
|
|
||||||
|
### 2. 团期下子订单列表 `GET /v3/admin/order/group-batch/{groupBatchId}/orders`
|
||||||
|
|
||||||
|
**VO**: `(无请求体,Path + Query 参数)` → `PageResult<GroupBatchOrderItemRespVO>`
|
||||||
|
|
||||||
|
#### 使用场景
|
||||||
|
|
||||||
|
团期订单 Tab 展示子订单摘要列表(客户姓名、各状态列、支付/结算信息)时调用,是该 Tab 的主表格数据源。
|
||||||
|
|
||||||
|
#### 入参字段表
|
||||||
|
|
||||||
|
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|
||||||
|
|------|------|------|------|------|------|
|
||||||
|
| groupBatchId | Path | Long | ✅ | - | 团期 ID |
|
||||||
|
| page | Query | Integer | ❌ | 缺省 1,<1 归一为 1 | 页码,从 1 起 |
|
||||||
|
| pageSize | Query | Integer | ❌ | 缺省 20,<1 归一为 20,>200 截断为 200 | 每页条数 |
|
||||||
|
| includeTravelers | Query | Boolean | ❌ | 缺省 true | 是否附出行人明细(证件号/手机号一律不返回),本次未变 |
|
||||||
|
| includeNeeds | Query | Boolean | ❌ | 缺省 true | 是否附房数/房型/特殊需求,本次未变 |
|
||||||
|
| includeCancelled | Query | Boolean | ❌ | 缺省 false | 是否含已取消子订单,本次未变 |
|
||||||
|
|
||||||
|
#### 出参字段表
|
||||||
|
|
||||||
|
以下只列本次新增/变化的字段;`GroupBatchOrderItemRespVO` 其余既有字段(`orderId`/`customerName`/各状态码与状态中文名/金额字段/`travelers` 等)结构未变,不重复列出。
|
||||||
|
|
||||||
|
| 字段 | 类型 | 说明 |
|
||||||
|
|------|------|------|
|
||||||
|
| vehicleRequirementKind | String,可空 | 新增:本行车需求的用车类别(`TRAVEL`/`TRANSFER`)。**当前恒为 `TRAVEL`**——子订单列表每户只占一行,装不下两类,上游按 TRAVEL 过滤取行;接送机需求要看配车芯片明细(`.../chips/vehicle`,一户两类各占一项)。无 active 车需求行时为 `null`,与 `vehicleRequirementStatus` 同生同灭 |
|
||||||
|
| vehicleRequirementKindName | String,可空 | 新增:`vehicleRequirementKind` 配对中文名(行程用车/接送机);枚举外未知码不回落编码,直接给 `null` |
|
||||||
|
| vehicleRequirementStatusName | String,可空 | 既有字段,文案口径调整:`PENDING_REVIEW` 的措辞随同行的 `vehicleRequirementKind` 分叉——TRAVEL 下发"待提交车务"(该状态上没有逐户审核动作,推走它的是团期管理员整团一次的"提交车务"),TRANSFER 下发"待审核";由于本字段随行的 `vehicleRequirementKind` 当前恒为 TRAVEL,本端点实际只会出现"待提交车务"这一支 |
|
||||||
|
|
||||||
|
#### 请求示例
|
||||||
|
|
||||||
|
```http
|
||||||
|
GET /v3/admin/order/group-batch/2104839654727618562/orders?page=1&pageSize=20
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 响应示例
|
||||||
|
|
||||||
|
真实实测(团期批次 T26-3963,节选第 1 条完整记录):
|
||||||
|
|
||||||
|
```json
|
||||||
|
{"code":200,"message":"成功","data":{"records":[{"orderId":"2104839654652121090","orderNo":"HL20260929154414207","teamNo":"26-2313","customerName":"李文博","participantCount":2,"orderStatus":"CUSTOMIZING","orderStatusName":"定制中","flowStatus":"RESOURCE_PREPARING","flowStatusName":"资源准备","reviewStatus":null,"reviewStatusName":null,"settlementStatus":"NONE","settlementStatusName":"未结算","payStatus":"FULLY_PAID","payStatusName":"已付全款","contractStatus":null,"contractStatusName":null,"insuranceStatus":null,"insuranceStatusName":null,"paidAmount":"7360.00","balanceAmount":"0.00","hotelRequirementStatus":null,"hotelRequirementStatusName":null,"vehicleRequirementStatus":"DONE","vehicleRequirementStatusName":"配车完成","vehicleRequirementKind":"TRAVEL","vehicleRequirementKindName":"行程用车","consultantName":"cw_test_7443","totalPrice":"7360.00","tierCode":"2A","tierName":"2成人","travelerInfoComplete":true,"roomCount":1,"roomType":null,"roomTypeName":null,"specialNeeds":"夫妻同行,携带摄影器材较多,需预留后备箱空间","contactPhone":"138****3046","groupChatUnreadCount":0,"travelers":[{"name":"李文博","type":"ADULT","age":46,"birthdayInTrip":false},{"name":"赵梦琪","type":"ADULT","age":43,"birthdayInTrip":false}]}],"total":3,"page":1,"pageSize":20},"traceId":null,"success":true}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 空数据 / 降级响应
|
||||||
|
|
||||||
|
- 团期批次不存在:返回业务错误(见"错误响应")。
|
||||||
|
- 团期下暂无子订单(或 `includeCancelled=false` 时全部已取消):`records` 为空数组,`total=0`。
|
||||||
|
- 该户无 active 车需求行:`vehicleRequirementStatus`/`vehicleRequirementKind` 均为 `null`;`vehicleRequirementStatusName`/`vehicleRequirementKindName` 按该户 `needsVehicle` 分叉——`true` 出"未提交",否则为 `null`。
|
||||||
|
|
||||||
|
#### 错误响应
|
||||||
|
|
||||||
|
```json
|
||||||
|
{"code":589500,"message":"团期不存在","data":null,"traceId":null,"success":false}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 业务边界
|
||||||
|
|
||||||
|
- `vehicleRequirementKind` 在本端点当前恒为 `TRAVEL`,不能据此推断"该团期没有接送机需求"——接送机需求存在与否要看配车芯片明细端点。
|
||||||
|
- `vehicleRequirementStatusName` 的 `PENDING_REVIEW` 分支文案取决于 `vehicleRequirementKind`,但本端点该列恒为 TRAVEL,因此实际只会看到"待提交车务",不会看到"待审核"(后者只出现在配车芯片明细的 TRANSFER 项上)。
|
||||||
|
- 无 active 车需求行时 `vehicleRequirementStatus` 为 `null` 不回落 `PENDING`(#8249 起既有行为,本次未变)——`PENDING` 是需求行的真实状态之一,没有行时借用它会与"未提交"矛盾。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 四、契约约束与正确调用方式
|
||||||
|
|
||||||
|
> 本节只写后端接受/拒绝的规则与语义边界,不写 UI 渲染建议。
|
||||||
|
|
||||||
|
### ✅ 正确 / ❌ 错误 payload 对照
|
||||||
|
|
||||||
|
| 场景 | payload / 响应 |
|
||||||
|
|------|-----------------|
|
||||||
|
| ✅ 渲染配车芯片明细列表 | 按数组下标或 `orderId`+`kind` 复合键渲染每一项,允许同一 `orderId` 出现多行 |
|
||||||
|
| ✅ 展示配车芯片"已完成 N/M" | 直接用 `doneCount`/`totalCount`,两者已经是同一口径(条目数),无需自行按户去重再算 |
|
||||||
|
| ❌ 用 `items[].orderId` 做 `Map` 的 key 或做 `Set` 去重 | 一户两类需求时后写入的会覆盖/顶掉先写入的那一条,界面上会静默丢失一类需求的状态 |
|
||||||
|
| ❌ 用子订单列表的 `vehicleRequirementKind` 判断该团期是否存在接送机需求 | 该列当前恒为 TRAVEL,对接送机需求零分辨力;接送机需求判断要调配车芯片明细 |
|
||||||
|
|
||||||
|
### 切换状态时的必要动作
|
||||||
|
|
||||||
|
前端如果此前在配车芯片渲染层用 `orderId` 做过 `key`/去重/索引,本次上线前必须改为 `orderId + kind` 复合键;否则界面在两类需求并存的户上会稳定丢失一类需求的展示,且是静默丢失(不报错)。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 五、数据库行为
|
||||||
|
|
||||||
|
两个端点均为只读查询,无数据库写操作。配车芯片明细与子订单列表内部均从 `order_vehicle_requirement`(车需求行表)按 `order_id` 聚合取最新一版需求行,`kind` 直接取自需求行的 `requirement_kind` 列,不做兜底改写;历史行 `requirement_kind` 为 `NULL` 时 `kind`/`kindName` 原样透出 `null`,不会被兜底成 `TRAVEL`。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 六、边界行为
|
||||||
|
|
||||||
|
- 户尚未提交任何车需求行、但已声明要用车(`needs_vehicle=true`)→ 配车芯片该户仍占一项,`kind`/`status` 为 `null`,`statusName`="未提交"
|
||||||
|
- 户完全不需要用车(`needs_vehicle=false`)→ 该户在配车芯片 `items[]` 中不出现
|
||||||
|
- 户同时有 TRAVEL 与 TRANSFER 两类 active 需求行 → 配车芯片占两项,`orderId` 相同、`kind` 不同
|
||||||
|
- 户只有一类需求行 → 配车芯片只占一项,不会补一个空的另一类占位项
|
||||||
|
- 需求行历史 `requirement_kind` 为 `NULL` → `kind`/`kindName` 原样为 `null`,不兜底为 `TRAVEL`
|
||||||
|
- 子订单列表 `.../orders` 每户恒只出一行、按 TRAVEL 过滤取需求行,不受该户是否有 TRANSFER 需求影响
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 六.5、枚举 / 数据字典
|
||||||
|
|
||||||
|
### 用车类别(`VehicleRequirementKind`,`items[].kind` / `vehicleRequirementKind`)
|
||||||
|
|
||||||
|
**所属字段**: `GroupBatchChipItemRespVO.kind`、`GroupBatchOrderItemRespVO.vehicleRequirementKind` | **类型**: `String`
|
||||||
|
|
||||||
|
| 值 | 中文 | 本次是否新增 | 说明 |
|
||||||
|
|----|------|------|------|
|
||||||
|
| `TRAVEL` | 行程用车 | 既有枚举值,本次新增到这两个字段 | 团期行程内的用车安排 |
|
||||||
|
| `TRANSFER` | 接送机 | 既有枚举值,本次新增到这两个字段 | 接送机场/车站,独立于行程用车 |
|
||||||
|
| `null` | (无展示) | - | 该户尚无 active 车需求行时的状态,不是第三个枚举值 |
|
||||||
|
|
||||||
|
### 车需求状态中文名(`statusName` / `vehicleRequirementStatusName`,`PENDING_REVIEW` 按 `kind` 分叉)
|
||||||
|
|
||||||
|
**所属字段**: `items[].statusName`(配车芯片)、`vehicleRequirementStatusName`(子订单列表) | **类型**: `String`
|
||||||
|
|
||||||
|
| 状态码 | 中文(本次前) | 中文(本次后) | 说明 |
|
||||||
|
|--------|----------------|----------------|------|
|
||||||
|
| `PENDING` | 待车务配(仅配车芯片明细,子订单列表原已是"待车队配") | 待车队配 | 两处读口统一为同一文案 |
|
||||||
|
| `PROCESSING` | 配车中 | 配车中 | 未变 |
|
||||||
|
| `DONE` | 配车完成 | 配车完成 | 未变 |
|
||||||
|
| `PENDING_REVIEW`(TRAVEL) | 待审核(配车芯片明细此前不分类别) | 待提交车务 | 按 `kind` 新分叉 |
|
||||||
|
| `PENDING_REVIEW`(TRANSFER) | 待审核 | 待审核 | 未变(新分叉后仍是这个文案) |
|
||||||
|
| `REJECTED_TO_CONSULTANT` | 驳回给定制师(仅配车芯片明细) | 已驳回定制师 | 两处读口统一为同一文案 |
|
||||||
|
| `REJECTED_TO_ADMIN` | 驳回给管理员(仅配车芯片明细) | 已驳回管理员 | 两处读口统一为同一文案 |
|
||||||
|
| `null`(有需求但未提交) | 待审核(配车芯片明细此前误落到这一支) | 未提交 | 修正误报——"未提交"与"已提交等审核"是两个不同状态,此前混在一起 |
|
||||||
|
|
||||||
|
## 六.6、修改前后对比
|
||||||
|
|
||||||
|
### 字段级对比
|
||||||
|
|
||||||
|
| 字段 | 改前 | 改后 |
|
||||||
|
|------|------|------|
|
||||||
|
| `GroupBatchChipItemRespVO.kind` | 不存在 | 新增,`String`,可空,仅配车芯片有值,其余五芯片恒 `null` |
|
||||||
|
| `GroupBatchChipItemRespVO.kindName` | 不存在 | 新增,`String`,可空 |
|
||||||
|
| `GroupBatchChipDetailVO.totalCount`(配车芯片) | 户数 | 条目数(一户两类算两条) |
|
||||||
|
| `GroupBatchChipDetailVO.doneCount`(配车芯片) | 已完成户数 | 已完成条目数 |
|
||||||
|
| `GroupBatchChipDetailVO.items[]`(配车芯片) | 一户一项 | 一户每类一项,`orderId` 可重复 |
|
||||||
|
| `GroupBatchOrderItemRespVO.vehicleRequirementKind` | 不存在 | 新增,`String`,可空,当前恒为 `TRAVEL` |
|
||||||
|
| `GroupBatchOrderItemRespVO.vehicleRequirementKindName` | 不存在 | 新增,`String`,可空 |
|
||||||
|
| 团期列表/看板 `chipStats.vehicle.total`/`.done` | 户数 | 条目数(共用配车芯片同一聚合算法,联动变化,字段本身未新增) |
|
||||||
|
|
||||||
|
### 行为级对比
|
||||||
|
|
||||||
|
| 场景 | 改前 | 改后 |
|
||||||
|
|------|------|------|
|
||||||
|
| 户同时有 TRAVEL + TRANSFER 需求 | 配车芯片只显示其中一类,另一类无声消失 | 两类各占一项,均可见 |
|
||||||
|
| 户已声明用车但未提交需求行 | 落到"待审核",看起来像已提交 | 落到"未提交",与已提交待审核区分开 |
|
||||||
|
| `PENDING_REVIEW` 状态文案 | 配车芯片明细恒显示"待审核";子订单列表已按 kind 分叉(源于 #8218) | 配车芯片明细与子订单列表口径统一,均按 kind 分叉 |
|
||||||
|
| 配车芯片 `PENDING`/`REJECTED_TO_CONSULTANT`/`REJECTED_TO_ADMIN` 文案 | 待车务配/驳回给定制师/驳回给管理员 | 待车队配/已驳回定制师/已驳回管理员 |
|
||||||
|
| 配车芯片 `totalCount`/`doneCount` 与列表页 `chipStats.vehicle` 关系 | 两处各自独立计算,可能不一致 | 共用同一聚合算法,逐位一致 |
|
||||||
|
|
||||||
|
## 六.7、影响评估
|
||||||
|
|
||||||
|
- **是否破坏向后兼容**: 是——`.../chips/vehicle` 的 `items[]` 数组长度与 `orderId` 唯一性假设改变,任何按 `orderId` 做 key/去重/索引的前端代码都会在两类需求并存的户上产生数据丢失;`totalCount`/`doneCount` 数值口径也变了,若前端拿它们除以户数算百分比会得到错误结果。
|
||||||
|
- **前端是否必须同步上线**: 是(针对配车芯片展示场景)——只要页面渲染配车芯片明细,就必须按 `orderId+kind` 复合键处理 `items[]`;子订单列表的两个新字段是纯新增,不改也不会报错,但不展示就拿不到用车类别信息。
|
||||||
|
- **前端 workaround 清理点**: 若此前为"同一户两类需求显示不全/互相覆盖"这类现象写过特殊兼容或只取第一条的逻辑,现在后端已按类别拆项,可以确认不再需要。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 七、不影响范围
|
||||||
|
|
||||||
|
- **仅影响**: 配车芯片明细端点 `GET .../chips/vehicle` 的 `items[]` 结构与 `totalCount`/`doneCount` 语义;子订单列表端点 `GET .../orders` 新增两个字段;团期列表/看板端点 `chipStats.vehicle` 的计数口径(字段本身未变)。
|
||||||
|
- **零影响**:
|
||||||
|
- 房/导/摄/约/保五个芯片明细端点(`GET .../chips/{hotel|guide|photo|contract|insurance}`)的字段结构与计数口径
|
||||||
|
- 团期下子订单列表其余既有字段(金额、支付、结算、房需求等)
|
||||||
|
- 户级用车需求明细端点 `GET .../requirement/vehicle-households`(本次为纯内部代码去重,零响应契约变化,见下方"八、测试环境已验证"说明)
|
||||||
|
- 配车需求本身的写口(提交/确认/驳回等)不受本次改动影响
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 八、测试环境已验证
|
||||||
|
|
||||||
|
服务:`hl-order-service-v3`,dev-v3 分支部署测试网关 @ `99fb369ba`(含 #8543 所在提交 `bc80606ac2`),测试网关 `https://api.test.1814.love`;样本均为测试服现存真实业务数据:
|
||||||
|
|
||||||
|
```
|
||||||
|
✓ 团期批次 T26-3963(groupBatchId=2104839654727618562,3 户):
|
||||||
|
GET .../chips/vehicle → totalCount=5 doneCount=3
|
||||||
|
李文博(orderId=2104839654652121090):TRAVEL/DONE/配车完成 + TRANSFER/PENDING/待车队配(同一 orderId 两项)
|
||||||
|
那顺(orderId=2104839729176514562):TRAVEL/DONE/配车完成 + TRANSFER/PENDING_REVIEW/待审核(同一 orderId 两项)
|
||||||
|
张丽娟(orderId=2104839686486888449):仅 TRAVEL/DONE/配车完成(一项)
|
||||||
|
GET .../orders → 3 条记录,vehicleRequirementKind 均为 "TRAVEL"(与源码"本列当前恒为 TRAVEL"一致)
|
||||||
|
✓ 团期批次 T26-6001(groupBatchId=2104840641651556353,3 户):
|
||||||
|
GET .../chips/vehicle → totalCount=3 doneCount=1
|
||||||
|
董海涛:TRAVEL/DONE/配车完成 + TRANSFER/PENDING/待车队配
|
||||||
|
谢丽萍:TRAVEL/PENDING_REVIEW/待提交车务(复现"待提交车务"文案分支)
|
||||||
|
✓ 团期列表页 GET /v3/admin/order/group-batch 逐页扫描,同两个 groupBatchId 的 chipStats.vehicle:
|
||||||
|
{total:5, done:3} 与 {total:3, done:1},与明细端点 totalCount/doneCount 逐位一致(重复请求 5 次读数稳定)
|
||||||
|
✓ 错误响应:不存在的 groupBatchId → 两端点均返回 {"code":589500,"message":"团期不存在"}
|
||||||
|
```
|
||||||
|
|
||||||
|
注:`kind=null`(户已声明用车但未提交需求行,`statusName`="未提交")与配车芯片明细的 `REJECTED_TO_CONSULTANT`/`REJECTED_TO_ADMIN` 两个文案分支,本轮实测样本团期未覆盖到(样本户均已提交需求行且未被驳回);这三个分支的契约已在源码逐一核实(`GroupBatchChipResolver.vehicleItemsOf`、`GroupBatchConverter.resolveRequirementStatusName`),前端应按此契约实现,不依赖本轮是否观测到该取值。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 十、相关文档
|
||||||
|
|
||||||
|
- 关联 Issue: [wx/HL#8543](https://git.1814.love/wx/HL/issues/8543)
|
||||||
|
- 关联 PR: [wx/HL#8592](https://git.1814.love/wx/HL/pulls/8592)
|
||||||
|
|
||||||
|
## 关联 / 联系人
|
||||||
|
|
||||||
|
### 链接
|
||||||
|
|
||||||
|
- **Issue**: [#8543](https://git.1814.love/wx/HL/issues/8543)
|
||||||
|
- **PR**: [#8592](https://git.1814.love/wx/HL/pulls/8592)
|
||||||
|
- **Merge commit**: [bc80606ac2](https://git.1814.love/wx/HL/commit/bc80606ac2)
|
||||||
|
|
||||||
|
### 联系人
|
||||||
|
|
||||||
|
- **后端负责人**: @wx
|
||||||
@@ -0,0 +1,474 @@
|
|||||||
|
---
|
||||||
|
schema: "hl-changelog/v2"
|
||||||
|
ticket: "8548"
|
||||||
|
title: "团期需求重开待审提示接入 4 个只读端点,并修复整团免车团确认时接送机需求放行不到的问题"
|
||||||
|
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 #8586 合并 dev-v3(ddea7e710c);测试网关部署确认:hl-order-service-v3 @ ff6863754、hl-fleet-service @ 99fb369ba(deploy-status.sh 实测,两者均以 ddea7e710c 为祖先)。4 个只读端点中 3 个(团期详情 A2、房务看板详情 H2、fleet 配车总览)已用同一真实团期(groupBatchId=2104839654727618562,团号 T26-3963)实测捕获非空取值,三端一致;第 4 个(房务看板列表 H1)因该团未被房务认领、不出现在列表口,改用另一真实团期(groupBatchId=2104838272570245121)捕获到已确认团的双 null 基线,未能在本轮独立捕获 H1 的非空实例——这是列表口与详情口可见集合不同导致的结构性限制,不是契约缺口,H1 的字段契约与 H2/A2/总览完全同源同算法(同一个 GroupBatchRequirementReopenHintService)。#8548 的确认端点行为修复(整团免车放行接送机)本身是写操作,为避免误改测试服现存业务数据未做原子调用,已按源码逐行核实:GroupBatchRequirementService.java 505-593 行(doConfirm 内核 javadoc 与分支代码)、1161-1296 行(release 集合装配与 waivedVehicleSnapshot)。"
|
||||||
|
updated_at: "2026-09-30"
|
||||||
|
base: "dev-v3"
|
||||||
|
---
|
||||||
|
|
||||||
|
# hl-order-service-v3 / hl-fleet-service:团期需求重开待审提示接入 4 个只读端点,并修复整团免车团确认时接送机需求放行不到的问题
|
||||||
|
|
||||||
|
> **存放目录**: `changelogs-v2/2026-09/`
|
||||||
|
> **服务**: hl-order-service-v3(主,#8549 新提示的唯一产出口 + #8548 行为修复)、hl-fleet-service(透传消费方,无独立业务逻辑改动)
|
||||||
|
> **PR**: #8586
|
||||||
|
> **Issue**: #8548、#8549(一个 PR 同时处理两张关联工单)
|
||||||
|
> **日期**: 2026-09-30
|
||||||
|
> **影响范围**: 4 个只读端点响应新增 2 字段(团期详情 A2、房务看板列表 H1、房务看板详情 H2、fleet 团期配车总览);1 个写端点(团期整体确认需求)行为修复,响应结构不变
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ⚠️ 关键变化
|
||||||
|
|
||||||
|
- 新增 2 个响应字段:`requirementReopenPendingHouseholds`(`Integer`)、`requirementReopenResourceType`(`String`),出现在 4 个只读端点:`GET /v3/admin/order/group-batch/{groupBatchId}`、`GET /v3/admin/house/group-batches`、`GET /v3/admin/house/group-batches/{groupBatchId}`、`GET /admin/fleet/group-dispatch/batches/{groupBatchId}/overview`。四处取值同源同算法(`GroupBatchRequirementReopenHintService`,唯一产出口),不存在四套口径分叉的风险。
|
||||||
|
- 🔴 **`null` 不代表 `0`,不要折算成 0 渲染「0 户需求待审核」**。字段只在同时满足 3 个条件时才非空:① 团级 `requirementConfirmed=false`;② 能查到「定制师改需求触发的自动重开」留痕(区别于管理员手动打回,后者无此留痕);③ 重开后该团确实还有在团户处于待审核状态。三者任一不满足,两个新字段都是 `null`,前端应继续渲染原有的「待管理员重新确认」文案。
|
||||||
|
- **两个新字段不是「要么都有要么都无」的一对**:`requirementReopenPendingHouseholds` 非空时,`requirementReopenResourceType` 仍可能单独为 `null`(重开留痕的 `extra` JSON 解析失败/缺键时的降级),此时只渲染「N 户需求待审核」,不带 HOTEL/VEHICLE 类别文案,户数本身不受影响。
|
||||||
|
- **不要与既有字段 `pendingReviewHouseholds` 混淆**(仅 H2 详情端点有此字段):`pendingReviewHouseholds` 是房务看板自己的统计口径,只数房需求,任何时候都下发;新增的 `requirementReopenPendingHouseholds` 是团级确认闸的提示,数的是房、车两类待审需求的户去重并集,且只在上述 3 道闸门都满足时才有值。同一个团这两个数字不一致是正常的,不能互相对账。
|
||||||
|
- **#8548 行为修复(不涉及任何字段新增/删除)**:`POST /v3/admin/order/group-batch/{groupBatchId}/requirement/confirm` 整团确认时,若团期处于「整团免车」(管理员声明免车,`vehicleWaived=true`)状态,此前该分支对车侧释放集合恒返回空,导致该团后续补交的接送机(TRANSFER)需求永远放行不到、团级需求闸永久卡在待确认。修复后免车团确认会释放 **TRANSFER** 需求,但仍**不释放 TRAVEL**(整团免车声明的管辖范围只到 TRAVEL,释放 TRAVEL 等于替管理员推翻免车声明)。可观察的变化只是响应里既有字段 `transferDispatchedOrderIds`/`vehicleDispatchedCount` 现在对免车团也可能非空,响应 VO 结构本身零改动。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 二、变更接口清单
|
||||||
|
|
||||||
|
| # | 接口 | 方法 | 路径 | 变更类型 | 说明 |
|
||||||
|
|---|------|------|------|----------|------|
|
||||||
|
| 1 | A2 团期详情 | GET | `/v3/admin/order/group-batch/{groupBatchId}` | 字段新增(非破坏性) | 响应新增 `requirementReopenPendingHouseholds`/`requirementReopenResourceType` |
|
||||||
|
| 2 | H1 房务团期看板列表 | GET | `/v3/admin/house/group-batches` | 字段新增(非破坏性) | 同上,列表项级别 |
|
||||||
|
| 3 | H2 房务团期看板详情 | GET | `/v3/admin/house/group-batches/{groupBatchId}` | 字段新增(非破坏性) | 同上 |
|
||||||
|
| 4 | 团期配车总览 | GET | `/admin/fleet/group-dispatch/batches/{groupBatchId}/overview` | 字段新增(非破坏性) | 同上,order-v3 原样透传,fleet 不自算 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 三、接口详情
|
||||||
|
|
||||||
|
### 1. A2 团期详情 `GET /v3/admin/order/group-batch/{groupBatchId}`
|
||||||
|
|
||||||
|
**VO**: `(无请求体,仅路径参数)` → `GroupBatchDetailRespVO`
|
||||||
|
|
||||||
|
#### 使用场景
|
||||||
|
|
||||||
|
团期管理员在团期详情页查看需求确认状态。此前该页只能看到 `requirementConfirmed=false`,无法区分「等定制师第一次提交」「被管理员打回」「已重新提交但还有户没处理完」三种情况,一律渲染「待管理员重新确认」。本次起,属于第三种情况(定制师改需求触发的自动重开,且确实还有户在等审)时,响应额外带出具体待审户数与是谁(房/车)推倒了确认闸。
|
||||||
|
|
||||||
|
#### 入参字段表
|
||||||
|
|
||||||
|
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|
||||||
|
|------|------|------|------|------|------|
|
||||||
|
| groupBatchId | Path | Long | ✅ | - | 团期聚合主键 |
|
||||||
|
|
||||||
|
#### 出参字段表
|
||||||
|
|
||||||
|
以下是本次新增/说明文案更新的字段;其余既有字段结构未变,不重复列出。
|
||||||
|
|
||||||
|
| 字段 | 类型 | 说明 |
|
||||||
|
|------|------|------|
|
||||||
|
| requirementConfirmed | Boolean | 需求整体确认标记(既有字段,说明文案本次更新):为 `false` 时不要直接渲染「待管理员重新确认」,先看 `requirementReopenPendingHouseholds` |
|
||||||
|
| requirementReopenPendingHouseholds | Integer | **新增**。需求待审核户数:`requirementConfirmed=false` 且是「定制师改需求触发的自动重开」时下发;为 `null` 表示不下发(已确认 / 管理员打回置 0 / 已无人待审),按原有文案渲染 |
|
||||||
|
| requirementReopenResourceType | String | **新增**。触发最近一次需求重开的资源类别 `HOTEL` / `VEHICLE`:只标「谁把确认闸推倒了」,与户数口径无关(户数是房、车两类的并集);取不到时为 `null`,此时文案不带类别 |
|
||||||
|
|
||||||
|
#### 请求示例
|
||||||
|
|
||||||
|
```http
|
||||||
|
GET /v3/admin/order/group-batch/2104839654727618562
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 响应示例
|
||||||
|
|
||||||
|
真实实测(测试网关,业务 admin 身份)。以下为节选(仅摘录本次相关字段,其余既有字段结构未变,不重复列出):
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"code": 200,
|
||||||
|
"data": {
|
||||||
|
"groupBatchId": "2104839654727618562",
|
||||||
|
"batchNo": "T26-3963",
|
||||||
|
"requirementConfirmed": false,
|
||||||
|
"requirementReopenPendingHouseholds": 1,
|
||||||
|
"requirementReopenResourceType": "VEHICLE"
|
||||||
|
},
|
||||||
|
"success": true
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 空数据 / 降级响应
|
||||||
|
|
||||||
|
- 3 道闸门任一不满足(已确认 / 管理员手动打回 / 重开后已无人待审):两个新字段均为 `null`,前端按原有文案渲染。
|
||||||
|
- 重开留痕的 `extra` JSON 解析失败或缺键:仅 `requirementReopenResourceType` 单独降级为 `null`,`requirementReopenPendingHouseholds` 不受影响照常下发(服务端 `readResourceType()` 的不对称降级,不会因为类别取不到而连户数一起丢)。
|
||||||
|
|
||||||
|
#### 错误响应
|
||||||
|
|
||||||
|
```json
|
||||||
|
{"code":589500,"message":"团期不存在","data":null,"traceId":null,"success":false}
|
||||||
|
```
|
||||||
|
|
||||||
|
其余既有错误码本次未变:589507(无操作权限:当前角色未授予团期权限,或该团期不在您名下)。
|
||||||
|
|
||||||
|
#### 业务边界
|
||||||
|
|
||||||
|
- 🔴 `requirementReopenPendingHouseholds` 为 `null` 不代表 0,不要折算成 0 渲染。
|
||||||
|
- `requirementReopenResourceType` 可能单独为 `null`(即使户数非空),此时只显示户数、不带类别文案。
|
||||||
|
- 判断是否要展示这两个字段,先看 `requirementConfirmed`;`requirementConfirmed=true` 时两个新字段恒为 `null`,不需要额外判断。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 2. H1 房务团期看板列表 `GET /v3/admin/house/group-batches`
|
||||||
|
|
||||||
|
**VO**: `HouseGroupBatchBoardPageReqVO` → `PageResult<HouseGroupBatchBoardSimpleRespVO>`
|
||||||
|
|
||||||
|
#### 使用场景
|
||||||
|
|
||||||
|
房务在看板列表页浏览已认领的团期。列表项与详情页(见下)共用同一套团级字段判定逻辑,此前列表页同样只能看到 `requirementConfirmed=false` 一个布尔值,本次起可展示与详情页一致的待审户数与类别提示。
|
||||||
|
|
||||||
|
#### 入参字段表
|
||||||
|
|
||||||
|
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|
||||||
|
|------|------|------|------|------|------|
|
||||||
|
| scope | Query | String | ❌ | 最长 8 | 可见范围 `MINE`(默认,只看本人认领)/ `ALL`(看全部已认领团,#8491 起全体房务可传) |
|
||||||
|
| claimerAdminId | Query | Long | ❌ | - | 按认领人筛(仅 `scope=ALL` 生效) |
|
||||||
|
| planStatus | Query | String | ❌ | 最长 16 | 计划行状态 `PENDING` / `CONFIRMED`,不传=全部 |
|
||||||
|
| batchStatus | Query | String | ❌ | 最长 200 | 团期状态多选,逗号分隔;默认四态;`CANCELLED` 传入被忽略 |
|
||||||
|
| stayDateFrom | Query | LocalDate | ❌ | ISO 日期 | 住期区间起 |
|
||||||
|
| stayDateTo | Query | LocalDate | ❌ | ISO 日期 | 住期区间止 |
|
||||||
|
| departDateFrom | Query | LocalDate | ❌ | ISO 日期 | 出发日区间下界 |
|
||||||
|
| departDateTo | Query | LocalDate | ❌ | ISO 日期 | 出发日区间上界 |
|
||||||
|
| keyword | Query | String | ❌ | 最长 32 | 团期号或产品名包含匹配 |
|
||||||
|
| page | Query | Long | ❌ | ≥1,默认 1 | 页码 |
|
||||||
|
| pageSize | Query | Long | ❌ | 1~50,默认 20 | 每页条数 |
|
||||||
|
|
||||||
|
#### 出参字段表
|
||||||
|
|
||||||
|
以下是本次新增/说明文案更新的字段(列表项级别);其余既有字段结构未变,不重复列出。
|
||||||
|
|
||||||
|
| 字段 | 类型 | 说明 |
|
||||||
|
|------|------|------|
|
||||||
|
| requirementConfirmed | Boolean | 需求整体确认标记(既有字段,说明文案本次更新):为 `false` 时先看 `requirementReopenPendingHouseholds` 再定文案 |
|
||||||
|
| requirementReopenPendingHouseholds | Integer | **新增**。语义与 A2 完全一致(同一产出口) |
|
||||||
|
| requirementReopenResourceType | String | **新增**。语义与 A2 完全一致 |
|
||||||
|
|
||||||
|
#### 请求示例
|
||||||
|
|
||||||
|
```http
|
||||||
|
GET /v3/admin/house/group-batches?scope=ALL&pageSize=50
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 响应示例
|
||||||
|
|
||||||
|
真实实测(测试网关,房务角色)。列表口只显示**已被房务认领**的团,本次实测命中的这一条是已确认团(双 `null` 基线),节选:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"code": 200,
|
||||||
|
"data": {
|
||||||
|
"list": [
|
||||||
|
{
|
||||||
|
"groupBatchId": "2104838272570245121",
|
||||||
|
"requirementConfirmed": true,
|
||||||
|
"requirementReopenPendingHouseholds": null,
|
||||||
|
"requirementReopenResourceType": null
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"total": 1
|
||||||
|
},
|
||||||
|
"success": true
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
> 说明:本轮实测范围内命中的已认领团恰好都是已确认状态,未独立捕获非空实例;非空实例已在 A2/H2/fleet 总览三端用同一真实团期(T26-3963)交叉验证一致,H1 走的是同一个 `GroupBatchRequirementReopenHintService` 产出口,字段契约同源,只是列表口的可见集合(仅已认领团)与详情口不同。
|
||||||
|
|
||||||
|
#### 空数据 / 降级响应
|
||||||
|
|
||||||
|
- 无匹配团期:`list` 为空数组,`total` 为 0(既有行为未变)。
|
||||||
|
- 候选集超 500 时 `total` 返回 -1 表示未统计(既有行为,本次未变,与新字段无关)。
|
||||||
|
- 3 道闸门任一不满足:该行的两个新字段均为 `null`。
|
||||||
|
|
||||||
|
#### 错误响应
|
||||||
|
|
||||||
|
```json
|
||||||
|
{"code":808090,"message":"未登录或非房务角色,无权操作","data":null,"traceId":null,"success":false}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 业务边界
|
||||||
|
|
||||||
|
- 未认领的团不在本列表口,与团期抢单池页以「认领动作」为界互斥,新字段不改变这条边界。
|
||||||
|
- 同上,`null` 不代表 0;`requirementReopenResourceType` 可单独为 `null`。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 3. H2 房务团期看板详情 `GET /v3/admin/house/group-batches/{groupBatchId}`
|
||||||
|
|
||||||
|
**VO**: `(无请求体,仅路径参数)` → `HouseGroupBatchBoardRespVO`
|
||||||
|
|
||||||
|
#### 使用场景
|
||||||
|
|
||||||
|
房务点开具体团期查看逐日配房详情。全体房务可读任意团期(不校验认领归属),他人认领的团返回 `readOnly=true`(#8491,与本次改动无关)。
|
||||||
|
|
||||||
|
#### 入参字段表
|
||||||
|
|
||||||
|
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|
||||||
|
|------|------|------|------|------|------|
|
||||||
|
| groupBatchId | Path | Long | ✅ | - | 团期主订单 ID |
|
||||||
|
|
||||||
|
#### 出参字段表
|
||||||
|
|
||||||
|
以下是本次新增/说明文案更新的字段;其余既有字段(`hotelReady`、`pendingReviewHouseholds`、`days[]` 等)结构未变,不重复列出。
|
||||||
|
|
||||||
|
| 字段 | 类型 | 说明 |
|
||||||
|
|------|------|------|
|
||||||
|
| requirementConfirmed | Boolean | 需求整体确认标记(既有字段,说明文案本次更新):为 `false` 时先看 `requirementReopenPendingHouseholds` 再定文案 |
|
||||||
|
| requirementReopenPendingHouseholds | Integer | **新增**。语义与 A2 完全一致;🔴 与既有字段 `pendingReviewHouseholds` **不是一回事**——后者只数房需求、任何时候都下发,前者是房车并集且只在 3 道闸门满足时下发,同一个团两者数字不一致是正常的,不能互相对账 |
|
||||||
|
| requirementReopenResourceType | String | **新增**。语义与 A2 完全一致 |
|
||||||
|
|
||||||
|
#### 请求示例
|
||||||
|
|
||||||
|
```http
|
||||||
|
GET /v3/admin/house/group-batches/2104839654727618562
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 响应示例
|
||||||
|
|
||||||
|
真实实测(测试网关,房务角色)。节选:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"code": 200,
|
||||||
|
"data": {
|
||||||
|
"groupBatchId": "2104839654727618562",
|
||||||
|
"requirementConfirmed": false,
|
||||||
|
"requirementReopenPendingHouseholds": 1,
|
||||||
|
"requirementReopenResourceType": "VEHICLE",
|
||||||
|
"pendingReviewHouseholds": 0
|
||||||
|
},
|
||||||
|
"success": true
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
> 与同一团在 A2、fleet 总览的实测结果逐字段一致(`requirementReopenPendingHouseholds=1`、`requirementReopenResourceType="VEHICLE"`),印证三端同源同算法;`pendingReviewHouseholds=0` 与 `requirementReopenPendingHouseholds=1` 在此例中不同,正是上表说明的两套口径不对账的真实样本。
|
||||||
|
|
||||||
|
#### 空数据 / 降级响应
|
||||||
|
|
||||||
|
- 3 道闸门任一不满足:两个新字段均为 `null`。
|
||||||
|
- `requirementReopenResourceType` 单独降级为 `null` 时 `requirementReopenPendingHouseholds` 不受影响。
|
||||||
|
|
||||||
|
#### 错误响应
|
||||||
|
|
||||||
|
```json
|
||||||
|
{"code":589500,"message":"团期不存在","data":null,"traceId":null,"success":false}
|
||||||
|
```
|
||||||
|
|
||||||
|
```json
|
||||||
|
{"code":808090,"message":"未登录或非房务角色,无权操作","data":null,"traceId":null,"success":false}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 业务边界
|
||||||
|
|
||||||
|
- 🔴 `requirementReopenPendingHouseholds` 非空不要与 `pendingReviewHouseholds` 混淆或相加,两者统计口径不同。
|
||||||
|
- `null` 不代表 0;`requirementReopenResourceType` 可单独为 `null`。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 4. fleet 团期配车总览 `GET /admin/fleet/group-dispatch/batches/{groupBatchId}/overview`
|
||||||
|
|
||||||
|
**VO**: `(无请求体,仅路径参数)` → `GroupDispatchOverviewRespVO`
|
||||||
|
|
||||||
|
#### 使用场景
|
||||||
|
|
||||||
|
车务在配车总览页查看该团的需求确认状态、逐日排车与接送机缺口。`requirementReopenPendingHouseholds`/`requirementReopenResourceType` 由 order-v3 内部覆盖口(`GET /v3/internal/group-batch/{id}/vehicle-coverage`,Feign 专用,非前端可直接调用)原样透传,fleet 侧不做任何二次计算,与 A2/H2 保证同一口径。
|
||||||
|
|
||||||
|
#### 入参字段表
|
||||||
|
|
||||||
|
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|
||||||
|
|------|------|------|------|------|------|
|
||||||
|
| groupBatchId | Path | Long | ✅ | - | 团期主订单 ID |
|
||||||
|
|
||||||
|
#### 出参字段表
|
||||||
|
|
||||||
|
以下是本次新增/说明文案更新的字段;其余既有字段(`serviceDates`、`vehicleReady`、`days[]`、`orders[]`、`transferPendingTotal`、`conversationKey` 等)结构未变,不重复列出。
|
||||||
|
|
||||||
|
| 字段 | 类型 | 说明 |
|
||||||
|
|------|------|------|
|
||||||
|
| requirementConfirmed | Boolean | 整团需求是否已确认(既有字段,说明文案本次更新):为 `false` 时先看 `requirementReopenPendingHouseholds` 再定文案 |
|
||||||
|
| requirementReopenPendingHouseholds | Integer | **新增**。order-v3 覆盖口原样透传,fleet 不自算;🔴 `null` 不代表 0,不要折算成 0 渲染 |
|
||||||
|
| requirementReopenResourceType | String | **新增**。order-v3 覆盖口原样透传,语义与 A2 完全一致 |
|
||||||
|
|
||||||
|
#### 请求示例
|
||||||
|
|
||||||
|
```http
|
||||||
|
GET /admin/fleet/group-dispatch/batches/2104839654727618562/overview
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 响应示例
|
||||||
|
|
||||||
|
真实实测(测试网关,车务角色,roleId=5)。节选:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"code": 200,
|
||||||
|
"data": {
|
||||||
|
"groupBatchId": "2104839654727618562",
|
||||||
|
"batchNo": "T26-3963",
|
||||||
|
"requirementConfirmed": false,
|
||||||
|
"requirementReopenPendingHouseholds": 1,
|
||||||
|
"requirementReopenResourceType": "VEHICLE"
|
||||||
|
},
|
||||||
|
"success": true
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
> 与同一团在 A2、H2 的实测结果逐字段一致。
|
||||||
|
|
||||||
|
#### 空数据 / 降级响应
|
||||||
|
|
||||||
|
- 3 道闸门任一不满足:两个新字段均为 `null`,与 A2/H2 同步(同一份覆盖口数据)。
|
||||||
|
- order-v3 覆盖口不可达时,整个端点按既有降级规则返回 600012(团期配车基线不可达),不会出现「新字段单独降级、其余字段正常」的中间态——两个新字段与其余团级字段是同一次 Feign 调用的产物,不可能分开失败。
|
||||||
|
|
||||||
|
#### 错误响应
|
||||||
|
|
||||||
|
```json
|
||||||
|
{"code":600012,"message":"团期配车基线不可达,请稍后重试","data":null,"traceId":null,"success":false}
|
||||||
|
```
|
||||||
|
|
||||||
|
其余既有错误码本次未变:401(未登录)。
|
||||||
|
|
||||||
|
#### 业务边界
|
||||||
|
|
||||||
|
- 🔴 `requirementReopenPendingHouseholds` 为 `null` 不代表 0。
|
||||||
|
- 该字段与 fleet 自己的 `transferPendingTotal`(接送机未配计数)是两回事:前者是「团级需求确认闸的提示」,后者是「已确认需求里还有多少接送机缺口没排车」,两者可以同时非空,互不覆盖。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 四、契约约束与正确调用方式
|
||||||
|
|
||||||
|
> 本节只写后端响应字段的正确消费方式,不写 UI 渲染建议。
|
||||||
|
|
||||||
|
### ✅ 正确 / ❌ 错误 payload 对照
|
||||||
|
|
||||||
|
| 场景 | 说明 |
|
||||||
|
|------|------|
|
||||||
|
| ✅ 判断是否展示「N 户需求待审核」 | 先判 `requirementConfirmed===false`,再判 `requirementReopenPendingHouseholds != null` |
|
||||||
|
| ✅ 处理 `requirementReopenPendingHouseholds` 非空但 `requirementReopenResourceType` 为 `null` | 只渲染「N 户需求待审核」,不带类别文案,这是合法的降级态,不是异常 |
|
||||||
|
| ❌ 把 `requirementReopenPendingHouseholds` 为 `null` 折算成 0 渲染 | `null` 与「0 户待审」是两种不同状态:前者是「不适用/无需提示」,后者是「重开了但已处理完」——本次实现中「已处理完」同样落到 `null`(闸门 3 过滤),所以两者当前观察上是同一渲染结果,但契约上不保证永远如此,不要做数值折算 |
|
||||||
|
| ❌ 拿 H2 的 `pendingReviewHouseholds` 和 `requirementReopenPendingHouseholds` 相加或对账 | 两个字段统计口径不同(前者只数房、恒下发;后者数房车并集、条件下发) |
|
||||||
|
|
||||||
|
### 切换状态时的必要动作
|
||||||
|
|
||||||
|
无。本次 4 个端点均为只读字段新增,不涉及任何请求体/入参变化,前端无需在调用序列上做任何调整。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 五、数据库行为
|
||||||
|
|
||||||
|
`GroupBatchRequirementReopenHintService` 只读、不写任何表。查库固定 3 次批量查询(不随团期数量线性增长):① 从 `group_batch` 内存过滤未确认团;② 按团期 ID 批量查 `group_batch` 状态时间线,取最近一条 `BATCH_REQUIREMENT_REOPENED` 事件日志;③ 按事件命中的团批量取在团子订单 ID,再批量查待审核户。看板一页多团时同样是固定 3 次查询,不退化为 N+1。
|
||||||
|
|
||||||
|
#8548 修复:`doConfirm` 内核在整团免车分支新增一次车侧需求释放调用(`dispatchGroupTransferRequirements`),与既有的房侧放行在同一事务内,任一步失败整团零写入(既有的 809112 整团回滚保证不变)。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 六、边界行为
|
||||||
|
|
||||||
|
- 团级 `requirementConfirmed=true`(已确认)→ 4 个端点的两个新字段恒为 `null`。
|
||||||
|
- `requirementConfirmed=false` 但查不到 `BATCH_REQUIREMENT_REOPENED` 留痕(即管理员手动打回,而非定制师改需求触发的自动重开)→ 两个新字段恒为 `null`,前端渲染原有的「待管理员重新确认」文案。
|
||||||
|
- `requirementConfirmed=false` 且有重开留痕,但重开后该团在团户已全部处理完(待审户数为 0)→ 两个新字段恒为 `null`。
|
||||||
|
- 重开留痕的 `extra` JSON 缺失/为空/解析失败 → 仅 `requirementReopenResourceType` 单独为 `null`,`requirementReopenPendingHouseholds` 不受影响。
|
||||||
|
- **(#8548,确认端点行为变更,非本次响应字段变化)** 整团免车团(`vehicleWaived=true`)确认时,此前车侧释放集合恒为空,接送机需求永远放行不到;修复后释放 **TRANSFER**(接送机)需求,仍不释放 **TRAVEL**(行程用车,整团免车声明的管辖范围仅限于此);同时不推进正式团级用车需求(`groupVehicleRequirementId`/`Status`/`Version` 三个既有字段在免车分支仍为 `null`,因为免车团本就没有需要推进的正式需求,此行为本次未变)。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 六.5、枚举 / 数据字典
|
||||||
|
|
||||||
|
### 需求重开资源类别(`requirementReopenResourceType`)
|
||||||
|
|
||||||
|
**所属字段**: `requirementReopenPendingHouseholds` 的伴生字段,4 个端点通用 | **类型**: `String`(取不到时为 `null`)
|
||||||
|
|
||||||
|
| 值 | 含义 | 说明 |
|
||||||
|
|----|------|------|
|
||||||
|
| `HOTEL` | 定制师改住宿需求触发的自动重开 | |
|
||||||
|
| `VEHICLE` | 定制师改用车需求触发的自动重开 | |
|
||||||
|
| `null` | 取不到类别,或未落入需要下发的场景 | 户数字段仍可能非空,参见六、边界行为 |
|
||||||
|
|
||||||
|
取值来源:团期状态时间线里最近一条 `BATCH_REQUIREMENT_REOPENED` 事件日志的 `extra` JSON 中 `resourceType` 键,由触发重开的那条业务逻辑写入;本次未新增写入路径,只新增读取与下发。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 六.6、修改前后对比
|
||||||
|
|
||||||
|
### 字段级对比
|
||||||
|
|
||||||
|
| 字段 | 改前 | 改后 |
|
||||||
|
|------|------|------|
|
||||||
|
| `requirementReopenPendingHouseholds` | 不存在(4 个端点均无) | 新增,`Integer`,语义见上,4 端点同源同算法 |
|
||||||
|
| `requirementReopenResourceType` | 不存在(4 个端点均无) | 新增,`String`,语义见上 |
|
||||||
|
|
||||||
|
### 行为级对比
|
||||||
|
|
||||||
|
| 场景 | 改前 | 改后 |
|
||||||
|
|------|------|------|
|
||||||
|
| `requirementConfirmed=false` 且是定制师改需求触发的自动重开、确实还有户在等审 | 4 个端点均只有 `requirementConfirmed=false`,前端一律渲染「待管理员重新确认」,无法与「管理员手动打回」区分 | 额外带出具体待审户数与资源类别,可渲染「N 户需求待审核」区分于打回场景 |
|
||||||
|
| 整团免车团确认(`POST .../requirement/confirm`) | 车侧释放集合恒为空,免车后补交的接送机需求永远放行不到,团级需求闸永久卡在待确认,无任何报错或日志提示 | 释放 TRANSFER 需求(不释放 TRAVEL),响应既有字段 `transferDispatchedOrderIds`/`vehicleDispatchedCount` 对免车团可能非空 |
|
||||||
|
|
||||||
|
## 六.7、影响评估
|
||||||
|
|
||||||
|
- **是否破坏向后兼容**: 否——4 个只读端点均为纯字段新增,既有字段类型/取值/含义均未变;#8548 修复不改变响应 VO 结构,只改变部分既有字段(`transferDispatchedOrderIds`/`vehicleDispatchedCount`)在特定场景下的实际取值。旧前端忽略新字段不受任何影响。
|
||||||
|
- **前端是否必须同步上线**: 否(不上线不会报错或丢功能);建议同步——上线后可以把「待管理员重新确认」与「N 户需求待审核」两种场景分开展示,减少运营/房务/车务误判为同一种阻塞。
|
||||||
|
- **前端 workaround 清理点**: 若此前为区分「打回」与「重开待审」两种 `requirementConfirmed=false` 场景写过额外查询或猜测逻辑,现在可以直接用新字段替换。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 七、不影响范围
|
||||||
|
|
||||||
|
- **仅影响**: 上表列出的 4 个只读端点的响应字段;`POST /v3/admin/order/group-batch/{groupBatchId}/requirement/confirm` 端点在整团免车场景下的车侧释放行为。
|
||||||
|
- **零影响**:
|
||||||
|
- 4 个只读端点的请求参数与既有校验规则
|
||||||
|
- `POST .../requirement/confirm` 的请求体、响应 VO 结构、非免车团的确认行为
|
||||||
|
- `POST .../requirement/confirm` 在免车团场景下对 TRAVEL 需求的处理(仍不放行,逐单放行入口不受影响)
|
||||||
|
- `GET /v3/internal/group-batch/{id}/vehicle-coverage` 内部 Feign 端点之外的其它 internal 接口
|
||||||
|
- `PUT /admin/fleet/assignments/pickup-dropoff-config` 等接送机配置端点
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 八、测试环境已验证
|
||||||
|
|
||||||
|
服务:`hl-order-service-v3` @ `ff6863754`、`hl-fleet-service` @ `99fb369ba`(deploy-status.sh 实测部署登记,测试网关 `https://api.test.1814.love`);`git merge-base --is-ancestor ddea7e710c ff6863754` 与 `... 99fb369ba` 均为真,确认本单所在提交已随两个服务的当前部署一并上线。
|
||||||
|
|
||||||
|
```
|
||||||
|
✓ GET /v3/admin/order/group-batch/2104839654727618562(业务 admin):真实返回
|
||||||
|
requirementConfirmed=false, requirementReopenPendingHouseholds=1, requirementReopenResourceType="VEHICLE"
|
||||||
|
✓ GET /v3/admin/house/group-batches/2104839654727618562(房务角色):同一团返回同一取值,
|
||||||
|
三端一致;附带既有字段 pendingReviewHouseholds=0,印证两套口径不对账
|
||||||
|
✓ GET /admin/fleet/group-dispatch/batches/2104839654727618562/overview(车务角色,roleId=5):
|
||||||
|
同一团返回同一取值,三端一致
|
||||||
|
✓ GET /v3/admin/house/group-batches?scope=ALL&pageSize=50(房务角色):真实返回列表,
|
||||||
|
命中已认领团(groupBatchId=2104838272570245121)为已确认状态,
|
||||||
|
requirementReopenPendingHouseholds=null、requirementReopenResourceType=null,验证了 null 基线分支
|
||||||
|
```
|
||||||
|
|
||||||
|
注:H1 列表口本轮未独立捕获非空实例——T26-3963(本次用于交叉验证的团)未被房务认领,不出现在 H1 的可见集合里;H1 与其余三端共用同一个 `GroupBatchRequirementReopenHintService` 产出口,字段契约同源,此限制是列表口「仅显示已认领团」这一既有边界导致的取样限制,不是实现差异。
|
||||||
|
|
||||||
|
`#8548` 确认端点在整团免车分支释放 TRANSFER 需求的修复,本轮未做真实原子调用验证(该端点为写端点,会推进团级需求状态,测试服现存数据上误调用有污染业务状态的风险);已按源码逐行核实:`GroupBatchRequirementService.java` 505-593 行(`doConfirm` 内核 javadoc 第三段与 569-572 行分支代码)、1161-1296 行(放行集合装配 javadoc 与 `waivedVehicleSnapshot` 方法 javadoc)。前端如需验证该行为,应在确认后核对响应里 `transferDispatchedOrderIds` 是否包含预期订单,而不是依赖某个新字段(该端点响应结构本次未变)。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 十、相关文档
|
||||||
|
|
||||||
|
- 关联 Issue: [wx/HL#8548](https://git.1814.love/wx/HL/issues/8548)、[wx/HL#8549](https://git.1814.love/wx/HL/issues/8549)
|
||||||
|
- 关联 PR: [wx/HL#8586](https://git.1814.love/wx/HL/pulls/8586)
|
||||||
|
|
||||||
|
## 关联 / 联系人
|
||||||
|
|
||||||
|
### 链接
|
||||||
|
|
||||||
|
- **Issue**: [#8548](https://git.1814.love/wx/HL/issues/8548)、[#8549](https://git.1814.love/wx/HL/issues/8549)
|
||||||
|
- **PR**: [#8586](https://git.1814.love/wx/HL/pulls/8586)
|
||||||
|
- **Merge commit**: [ddea7e710c](https://git.1814.love/wx/HL/commit/ddea7e710c)
|
||||||
|
|
||||||
|
### 联系人
|
||||||
|
|
||||||
|
- **后端负责人**: @wx
|
||||||
@@ -0,0 +1,237 @@
|
|||||||
|
---
|
||||||
|
schema: "hl-changelog/v2"
|
||||||
|
ticket: "8560"
|
||||||
|
title: "矩阵未派订单清单响应新增用车需求类别字段,同订单双需求两张卡可区分"
|
||||||
|
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 #8589 合并 dev-v3(8b045a321b);测试网关部署确认:hl-fleet-service 现部署 @ 99fb369ba(deploy-status.sh 实测,状态 ok,该 SHA 经 git merge-base --is-ancestor 确认已包含 8b045a321b)。GET /admin/fleet/matrix/unassigned-orders 已用车务角色测试账号实测:2026-09 月拿到 TRAVEL 示例(订单 HL20260911193207642)、2026-11 月拿到 TRANSFER 示例(订单 HL20260929154809598),均为测试服真实响应;对 2026-06~2027-03 共 10 个月窗口扫描未发现 requirementKind=null 或同订单双卡的活跃实例,这两种边界行为当前仅由单元测试覆盖(BoardRequirementIdentitiesKindTest 5/5、MatrixServiceTest 新增 6 个 #8560 方法),尚未在测试服活数据上复现。"
|
||||||
|
updated_at: "2026-09-30"
|
||||||
|
base: "dev-v3"
|
||||||
|
---
|
||||||
|
|
||||||
|
# hl-fleet-service:矩阵未派订单清单响应新增用车需求类别字段,同订单双需求两张卡可区分
|
||||||
|
|
||||||
|
> **存放目录**: `changelogs-v2/2026-09/`
|
||||||
|
> **服务**: hl-fleet-service
|
||||||
|
> **PR**: #8589
|
||||||
|
> **Issue**: #8560
|
||||||
|
> **日期**: 2026-09-30
|
||||||
|
> **影响范围**: 1 个只读端点响应新增 2 字段(矩阵未派订单清单)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ⚠️ 关键变化
|
||||||
|
|
||||||
|
- `GET /admin/fleet/matrix/unassigned-orders` 响应每条记录新增 `requirementKind`(`TRAVEL`/`TRANSFER`/`null`)与 `requirementKindLabel`(`行程用车`/`接送机`/`null`),两者恒成对(一个为 null 另一个必为 null)。
|
||||||
|
- 背景(#7439):同一订单可并存两条活跃用车需求(行程用车 TRAVEL + 接送机 TRANSFER),车务分别对两者派车,未派池会出现同一订单的两张卡。此前两张卡除车型/日期外没有任何字段能分辨谁是哪一类——既有字段 `vehicleCategory`/`categoryLabel` 是车型(suv/bus)不是需求类别。新增这两个字段就是用来分辨这两张卡的。
|
||||||
|
- 🔴 **`null` 不兜底成 `TRAVEL`**,这是本次修复的核心边界。判不出类别(跨服务降级 context=null;或派车行挂着 #5720 换版过渡窗里的上一版 `requirement_id`,命中不了任何当前活跃身份;或命中的身份自身 `kind` 为空白)时两个新字段均为 `null`,前端应不显示类别标签,**禁止自行按业务猜测补默认值**——尤其禁止把 `null` 当 `TRAVEL` 处理。
|
||||||
|
- 与看板列表(`BoardOrderRecordVO.requirementKind`,#8518 既有)**在判不出这一档口径不同**:看板列表的解析方法判不出时兜底返 `TRAVEL`(那里类别同时是筛选维度,返空会让卡片从筛选后的视图里彻底消失);矩阵未派卡判不出时返 `null`(那里类别只是展示标签,车务会照标签去排完全不同的活,标错比不标更危险)。**同一张实体卡在两个入口可能显示不一致的类别信息,这是刻意保留的差异**,不是缺陷。
|
||||||
|
- 真实未派行与虚拟待派条目(`virtualPending=true`,#7067)两类条目都携带这两个新字段,取值口径一致。
|
||||||
|
- 类别取的是**这张卡自身所属需求**(真实行用该行自己的 `requirement_id`,虚拟条目用该候选自己的 `requirementId`)解析出的类别,**不是**已有字段 `requirementId`(该字段取「订单侧单值」,#5667 口径,同一订单两类需求并存时恒指向身份列表首项、即恒为 TRAVEL 那条)。同一订单两张卡的 `requirementId` 字段取值会相同,但 `requirementKind`/`requirementKindLabel` 不同——这正是新增这两个字段的意义所在,不能拿旧字段替代判断。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 二、变更接口清单
|
||||||
|
|
||||||
|
| # | 接口 | 方法 | 路径 | 变更类型 | 说明 |
|
||||||
|
|---|------|------|------|----------|------|
|
||||||
|
| 1 | 矩阵未派订单清单 | GET | `/admin/fleet/matrix/unassigned-orders` | 字段新增(非破坏性) | 响应新增 `requirementKind`/`requirementKindLabel` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 三、接口详情
|
||||||
|
|
||||||
|
### 1. 矩阵未派订单清单 `GET /admin/fleet/matrix/unassigned-orders`
|
||||||
|
|
||||||
|
**VO**: `MatrixUnassignedReqVO → List<MatrixUnassignedOrderVO>`
|
||||||
|
|
||||||
|
#### 使用场景
|
||||||
|
派单矩阵页右面板「未派订单窗口」,车务从此列表拖拽卡片到左面板某车某日完成派车;同一订单若同时有行程用车与接送机两条活跃需求,会在此列表出现两张卡,车务需要靠新增的类别字段区分要往哪类需求上派车,不能再靠车型/日期/备注这类间接信息猜。
|
||||||
|
|
||||||
|
#### 入参字段表
|
||||||
|
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|
||||||
|
|------|------|------|------|------|------|
|
||||||
|
| year | query | Integer | 是 | 2020-2100,越界返 605076 | 年份 |
|
||||||
|
| month | query | Integer | 是 | 1-12,越界返 605010 | 月份 |
|
||||||
|
| typeKeys | query | String[] | 否 | 取值 suv/mpv/bus/sedan,规范小写 | 车型大类多选,空=全部;对虚拟待派条目按当前需求车型明细任一项归一后命中过滤 |
|
||||||
|
|
||||||
|
#### 出参字段表(仅列本次新增字段及理解其语义所需的上下文字段,VO 全量共 39 个字段)
|
||||||
|
| 字段 | 类型 | 说明 |
|
||||||
|
|------|------|------|
|
||||||
|
| requirementKind | String | **新增**。用车需求类别:`TRAVEL`=行程用车 / `TRANSFER`=接送机 / `null`=判不出(不兜底为 TRAVEL) |
|
||||||
|
| requirementKindLabel | String | **新增**。类别中文名:`行程用车`/`接送机`/`null`,与 requirementKind 恒成对 |
|
||||||
|
| requirementId | Long(字符串序列化) | 既有字段,当前生效用车需求 ID;取订单侧单值(#5667),两类需求并存时恒指向 TRAVEL 那条,**不能**用它推导 requirementKind |
|
||||||
|
| assignmentId | Long(字符串序列化) | 既有字段,本行唯一主键;虚拟待派条目为 null |
|
||||||
|
| virtualPending | Boolean | 既有字段,true=虚拟待派条目(零派车行订单,按需求上下文补出) |
|
||||||
|
| vehicleCategory | String | 既有字段,规范小写车型 key(suv/mpv/bus/sedan),与需求类别是两个不同维度 |
|
||||||
|
| categoryLabel | String | 既有字段,车型中文标签(恒非 null) |
|
||||||
|
| orderId / orderNo | String | 既有字段,订单号 |
|
||||||
|
| teamNo | String | 既有字段,团号 |
|
||||||
|
|
||||||
|
#### 请求示例
|
||||||
|
```http
|
||||||
|
GET /admin/fleet/matrix/unassigned-orders?year=2026&month=11&typeKeys=mpv
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 响应示例
|
||||||
|
实测取自测试服真实数据(2026-11 月,TRANSFER 示例):
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"code": 200,
|
||||||
|
"message": "success",
|
||||||
|
"data": [
|
||||||
|
{
|
||||||
|
"orderId": "HL20260929154809598",
|
||||||
|
"orderNumericId": "2104840641597030402",
|
||||||
|
"orderNo": "HL20260929154809598",
|
||||||
|
"teamNo": "26-3682",
|
||||||
|
"virtualPending": true,
|
||||||
|
"assignmentId": null,
|
||||||
|
"assignmentGroupId": null,
|
||||||
|
"requirementId": "2104844928733548545",
|
||||||
|
"requirementKind": "TRANSFER",
|
||||||
|
"requirementKindLabel": "接送机",
|
||||||
|
"vehicleCategory": "mpv",
|
||||||
|
"categoryLabel": "商务车",
|
||||||
|
"customerName": "董海涛",
|
||||||
|
"headcount": 2,
|
||||||
|
"headcountLabel": "2大",
|
||||||
|
"startDate": "2026-11-11",
|
||||||
|
"endDate": "2026-11-17",
|
||||||
|
"pickupAt": "阿尔山伊尔施机场",
|
||||||
|
"dropoffAt": "阿尔山伊尔施机场",
|
||||||
|
"assignmentStatus": "unassigned",
|
||||||
|
"urgentBadge": null,
|
||||||
|
"vehicleAdvice": null,
|
||||||
|
"parallelAssignments": []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
对照:2026-09 月同一账号实测取到的 TRAVEL 示例(订单 `HL20260911193207642`),响应结构完全相同,仅 `requirementKind="TRAVEL"`、`requirementKindLabel="行程用车"`、`requirementId="2098950695167148034"`。两个示例均为测试服真实取数,未做任何字段改写。
|
||||||
|
|
||||||
|
#### 空数据 / 降级响应
|
||||||
|
- 当月无未派条目:`data: []`,非错误。
|
||||||
|
- `vehicleAdvice` 恒为 `null`(M2 数据源未建,既有降级行为,与本次改动无关)。
|
||||||
|
- Nacos `fleet.board.virtual-candidates-enabled=false` 或 order-v3 候选服务不可用时:只丢虚拟待派条目,真实未派行原样返回(fail-open);真实行的 `requirementKind` 解析走独立的上下文查询,不受此开关影响。
|
||||||
|
- `requirementKind`/`requirementKindLabel` 判不出时为 `null`(见「⚠️ 关键变化」),这不是接口异常,是正常的降级取值,前端应按无标签渲染,不得折算为 `TRAVEL`。
|
||||||
|
|
||||||
|
#### 错误响应
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"code": 605010,
|
||||||
|
"message": "月份超出范围",
|
||||||
|
"data": null
|
||||||
|
}
|
||||||
|
```
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"code": 605076,
|
||||||
|
"message": "年份超出范围(仅支持 2020-2100 年)",
|
||||||
|
"data": null
|
||||||
|
}
|
||||||
|
```
|
||||||
|
- `100001` 参数非法:year/month 缺失(框架校验)。
|
||||||
|
- `401` 未登录。
|
||||||
|
|
||||||
|
#### 业务边界
|
||||||
|
- **同一订单两类需求并存时,两张卡的 `requirementId` 字段值完全相同(均指向 TRAVEL 那条),但 `requirementKind`/`requirementKindLabel` 不同**——单元测试 `MatrixServiceTest#queryUnassignedOrders_orderWithBothKinds_twoCardsCarryDifferentKinds` 对此有显式反向对照断言,用来证明「拿 requirementId 反推类别」是错的,前端也不能这么做。
|
||||||
|
- `requirementKind=null` 时前端**禁止**折算成 `TRAVEL`;这既是判不出的真实状态,也是修复前的错误行为,回退等于复发。
|
||||||
|
- 矩阵未派卡与看板列表对同一张孤儿行(#5720 换版过渡窗)的类别展示口径不同(前者 null、后者兜底 TRAVEL),这是刻意保留的差异,不要据此判断某一端有 bug。
|
||||||
|
- 真实未派行与虚拟待派条目两种类型都下发这两个字段,前端不需要按 `virtualPending` 分支处理类别逻辑。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 四、契约约束与正确调用方式
|
||||||
|
|
||||||
|
- 判类别只认 `requirementKind`/`requirementKindLabel` 这两个新字段,不要用 `requirementId` 做二次推导。
|
||||||
|
- `requirementKind` 取值集合当前为 `{TRAVEL, TRANSFER, null}`,前端不应写死「非 TRANSFER 即 TRAVEL」的二值判断——若未来 order-v3 新增第三类需求,后端会同步扩展该字段取值与中文映射,二值判断会把新类别误标成 TRAVEL。
|
||||||
|
- 类别中文名由后端下发,前端不需要、也不应该自行维护 `TRAVEL`/`TRANSFER` 到中文的映射表。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 五、数据库行为
|
||||||
|
|
||||||
|
无数据库结构变更。本次改动只是查询层新增两次内存解析(基于已查出的订单需求上下文按 `requirement_id` 匹配),不新增表、不新增列、不新增索引,无 Flyway 迁移。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 六、边界行为
|
||||||
|
|
||||||
|
- 上下文降级(跨服务 Feign 调用失败,`OrderFleetBoardContextDTO` 为 `null`):类别字段为 `null`。
|
||||||
|
- 派车行/候选自身的 `requirement_id` 命中不到订单当前任何活跃需求身份(#5720 换版过渡窗孤儿行):类别字段为 `null`,不回退用订单上下文单值猜测。
|
||||||
|
- 命中的身份自身 `kind` 字段为空白:类别字段为 `null`(防御性分支;order-v3 当前写路径恒写枚举 `.name()`,正常不触发,仅覆盖历史/异常数据)。
|
||||||
|
- 以上三种 `null` 场景均只有单元测试覆盖(见八节),本次实测扫描未在测试服活数据中观测到对应真实记录。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 六.5、枚举 / 数据字典
|
||||||
|
|
||||||
|
| 取值 | 中文标签 | 说明 |
|
||||||
|
|------|----------|------|
|
||||||
|
| TRAVEL | 行程用车 | 行程用车需求 |
|
||||||
|
| TRANSFER | 接送机 | 接送机需求(#7439 引入) |
|
||||||
|
| null | (不显示标签) | 判不出类别,前端不得兜底为 TRAVEL |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 六.6、修改前后对比
|
||||||
|
|
||||||
|
- **字段层面**:`MatrixUnassignedOrderVO` 新增 `requirementKind`(String)、`requirementKindLabel`(String),VO 字段总数由 37 增至 39。
|
||||||
|
- **行为层面**:改动前,同一订单的两张未派卡在字段层面完全无法区分类别,只能靠车型/日期/备注人工判断,判断错了会把车派到错误的需求线上;改动后两张卡各自携带准确的类别标识,且判不出时明确返回 null 而非静默给出错误猜测。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 六.7、影响评估
|
||||||
|
|
||||||
|
- 破坏性:无。两个新增字段为可选新增,未删除/未重命名/未改变任何既有字段的类型或取值口径。
|
||||||
|
- 涉及消费端:仅管理后台派单矩阵页。
|
||||||
|
- 前端无需为此做兼容降级处理:未取到新字段(`undefined`)与取到 `null` 应做同等处理——均不显示类别标签。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 七、不影响范围
|
||||||
|
|
||||||
|
- 矩阵主数据端点 `GET /admin/fleet/matrix/grid`、年度月度统计 `GET /admin/fleet/matrix/month-counts`、当天订单清单 `GET /admin/fleet/matrix/day-orders`:均未改动。
|
||||||
|
- 看板列表端点(`BoardOrderRecordVO.requirementKind`,#8518):未改动,其判不出类别时仍兜底 TRAVEL 的既有行为不变。
|
||||||
|
- 写操作(拖拽派车、改派、取消等):本次改动只涉及查询响应字段新增,不涉及任何写路径。
|
||||||
|
- `MatrixUnassignedReqVO` 请求参数:未新增/未修改(year/month/typeKeys 均为既有字段,越界错误码路由此前已分别由 #8561/#8571 调整完成)。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 八、测试环境已验证
|
||||||
|
|
||||||
|
- **部署确认**:`hl-fleet-service` 现部署 SHA `99fb369ba`(`deploy-status.sh` 实测,状态 `ok`),经 `git merge-base --is-ancestor 8b045a321b 99fb369ba8` 确认已包含本次改动的合并提交 `8b045a321b`(PR #8589)。
|
||||||
|
- **实测(真实请求,非构造数据)**:使用车务角色测试账号(切至 VEHICLE_MANAGER 角色)对 `GET /admin/fleet/matrix/unassigned-orders` 发起真实请求:
|
||||||
|
- 2026-09 月:2 条记录,`requirementKind` 均为 `TRAVEL`,含示例订单 `HL20260911193207642`(见响应示例节)。
|
||||||
|
- 2026-11 月:1 条记录,`requirementKind` 为 `TRANSFER`,订单 `HL20260929154809598`(见响应示例节)。
|
||||||
|
- 对 2026-06 ~ 2027-03 共 10 个月窗口的扫描(合计 14 条记录)未发现 `requirementKind=null` 的记录,也未发现同一订单出现两条不同类别记录的活跃实例——测试服当前业务数据里暂未出现这两种边界场景,实测未覆盖,靠下面的单元测试兜底。
|
||||||
|
- **单元测试覆盖(源码单测验证,未在测试服活数据上复现)**:
|
||||||
|
- `BoardRequirementIdentitiesKindTest`(5/5 通过):覆盖双身份按需求 ID 各取各类别、上下文降级返 null(对照既有方法仍兜底 TRAVEL)、陈旧需求 ID 不猜返 null、身份自身类别空白返 null、灰度上下文合成 TRAVEL 身份仍可取到。
|
||||||
|
- `MatrixServiceTest` 新增 6 个 `#8560` 测试方法(均通过):同订单两类需求两张卡类别互不相同(含反向对照:两张卡 `requirementId` 字段完全相同)、需求身份类别空白返 null 不兜底 TRAVEL、上下文降级返 null 不兜底 TRAVEL、派车行挂陈旧需求 ID(#5720)返 null 不兜底 TRAVEL、虚拟待派条目携带类别、虚拟待派条目无身份列表时类别为 null。
|
||||||
|
- 聚合结果(`mvn -pl hl-fleet-service -am test`):`Tests run: 365, Failures: 0, Errors: 0, Skipped: 0`,`BUILD SUCCESS`;含 `VehicleRequirementKindsTest` 4、`BoardOrderServiceTest` 233、`FleetRedLineArchTest` 18(架构守护门禁绿)。
|
||||||
|
- 嵌套用例选择器守卫(`nested_selector_census`):通过,内层名比对无缺组。
|
||||||
|
- `spotless:check`:`BUILD SUCCESS`,916 文件全部合规。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 十、相关文档
|
||||||
|
|
||||||
|
- Issue #8560
|
||||||
|
- PR #8589(合并提交 `8b045a321b`)
|
||||||
|
- 相关既有机制:#7439(TRAVEL/TRANSFER 双需求引入)、#8518(看板列表既有类别字段)、#7067(虚拟待派条目/去槽位化)、#5667(`requirementId` 订单侧单值口径)、#5720(换版过渡窗孤儿行)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 关联 / 联系人
|
||||||
|
|
||||||
|
- 后端:wx(GIT)
|
||||||
|
- 消费端:管理后台(派单矩阵页)
|
||||||
在新工单中引用
屏蔽一个用户