docs(changelog): 团期用车需求读口补中文名 / 汇总草稿换读侧 VO / 并列下发团级已确认座位(#8544 #8545 #8619)
changelog-filename-gate / validate (push) Failing after 2s
changelog-filename-gate / validate (push) Failing after 2s
四个读口的交接件:GET /vehicle-requirement 新增 4 个 *Name 字段、 GET /vehicle-requirement/aggregate-draft 的 draft 换成只读读侧 VO、 GET /requirement-summary 并列下发 confirmedSeats/confirmedCount、 GET /orders 的 vehicleRequirements[] 逐类下发用车需求行。 四个端点的响应示例全部为 2026-09-30 测试环境实测采集(团期批次 2104839654727618562 / 2104840641651556353),并标注了采集批次与时刻。 未知编码回落 null 这一支在当前环境无自然样本(三列库内全为 NULL), 已在第八节写明由源码与单测两侧钉住,不留悬念给前端。 三条给前端的硬约束: - groups[].remark 不要按格式解析:本单只改新生成的汇总草稿, 存量已确认团期仍是改前的订单号前缀格式,两种格式长期并存。 - 同一个 status 编码在不同 kind 上中文名不同(PENDING_REVIEW 在 TRAVEL 下是「待提交车务」、在 TRANSFER 下是「待审核」,#8218 刻意分叉), 前端不得自建 code 到中文的映射表,一律渲染后端下发的 statusName。 - 按类别判状态一律遍历 vehicleRequirements[],单值字段只表达展示序首条。 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
这个提交包含在:
@@ -0,0 +1,664 @@
|
|||||||
|
---
|
||||||
|
schema: "hl-changelog/v2"
|
||||||
|
ticket: "8544"
|
||||||
|
title: "团期正式用车需求读口补状态中文名、汇总草稿改用读侧 VO、需求汇总并列下发团级已确认座位、子订单列表逐类下发用车需求行(#8544 #8545 #8619)"
|
||||||
|
consumer: "admin"
|
||||||
|
author: "wx(GIT)"
|
||||||
|
change_type: "修改接口"
|
||||||
|
backend_status: "deployed"
|
||||||
|
gateway_status: "not_required"
|
||||||
|
frontend_status: "pending"
|
||||||
|
frontend_owner: ""
|
||||||
|
frontend_ref: ""
|
||||||
|
target_release: ""
|
||||||
|
verified_at: ""
|
||||||
|
status_note: "PR #8625(覆盖 #8544 #8545)与 PR #8624(覆盖 #8619)均已 squash 合并 dev-v3(5f2f86bc9 / ce7cd238e9),hl-order-service-v3 dev-v3 分支已滚测试服(HEAD 5f2f86bc9,jar mtime 2026-09-30 08:19:48,两实例 Nacos 健康)。四个 GET 端点响应体的新增/变更字段均实测通过;PUT / withdraw / waive 三个写端点与 GET 共用同一个 GroupVehicleRequirementRespVO 类(源码级共用,非各自派生),因此 #8544 新增的 4 个 *Name 字段在这三个端点的响应里同样存在,字段语义与本文档「三、接口详情」第 2 条完全一致。"
|
||||||
|
updated_at: "2026-09-30"
|
||||||
|
base: "dev-v3"
|
||||||
|
---
|
||||||
|
|
||||||
|
# hl-order-service-v3: 团期正式用车需求读口补状态中文名、汇总草稿改用读侧 VO、需求汇总并列下发团级已确认座位、子订单列表逐类下发用车需求行
|
||||||
|
|
||||||
|
> **存放目录**: `changelogs-v2/2026-09/`
|
||||||
|
> **服务**: hl-order-service-v3 (端口 8086)
|
||||||
|
> **PR**: #8625(#8544 #8545)、#8624(#8619)
|
||||||
|
> **Issue**: #8544、#8545、#8619
|
||||||
|
> **日期**: 2026-09-30
|
||||||
|
> **影响范围**: 团期需求管理 Tab 下 4 个 GET 端点(全团需求汇总 / 读正式用车需求 / 自动汇总草稿 / 子订单列表)的响应体字段;PUT / withdraw / waive 三个写端点共用的响应体同步补齐字段(契约见「四」,未单开详情块)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ⚠️ 关键变化
|
||||||
|
|
||||||
|
- **#8544**:`GroupVehicleRequirementRespVO`(PUT / GET / withdraw / waive 四端点共用的响应体)新增 4 个只读中文名字段——`statusName` / `planRefreshStateName` / `blockedStageName` / `planRefreshStalledReasonName`,分别配对既有的 `status` / `planRefreshState` / `blockedStage` / `planRefreshStalledReason`。**认不出的编码一律给 `null`,不回落成编码原文**(与本 VO 既有的 `SpecialTagItem.name` / `GroupItem.vehicleTypeName` 同口径)。
|
||||||
|
- 🔴 **这条 null 规则与看板列表的团期状态中文名口径是两套、刻意不同**:`GroupBatchConverter#resolveBatchStatusName`(团期列表页用)未知码会回落原编码;本次这 4 个字段所在的正式用车需求读口未知码给 `null`。前端不要把两处的「未知码兜底逻辑」当成同一套抄。
|
||||||
|
- **#8544**:`GET .../vehicle-requirement/aggregate-draft` 的 `draft` 字段类型从写侧 `GroupVehicleRequirementSaveReqVO` 改为新的只读读侧类型 `GroupVehicleRequirementDraftRespVO`。JSON 形状基本不变(由 `GroupVehicleRequirementDraftRespVOFieldParityTest` 钉住与写侧字段集一致),**唯一新增字段是 `groups[].vehicleTypeName`**(只读车型中文名,不参与保存,原样 `PUT` 回去时被忽略);该 VO **不挂任何校验注解**(`@NotNull`/`@NotEmpty`/`@Size` 等),因为草稿可能违反若干条保存态约束,违规项另在 `violations` 里列出。
|
||||||
|
- **#8544**:`draft.groups[].remark`(分组备注拼接文案)的逐户标签优先级改为 **团号(客户名)→ 团号 → 客户名 → 订单号 → "未知户"**,**不再回落雪花订单 ID**(原来兜底到裸数字 orderId 的情况,现在只有当团号/客户名/订单号三者都拿不到时才会出现,落成字面文案"未知户")。前端如果对这段 `remark` 文本做过正则匹配、高亮、或按"看起来像一串数字"识别订单号,需要同步更新——这段文本此后不会再出现裸数字订单 ID。
|
||||||
|
- **#8545**:`GET .../requirement-summary` 的 `vehicleSeatSummary[]` 新增 `confirmedSeats` / `confirmedCount`(均为 `int`,**尚未整团确认时恒为 `0`,不是 `null`**)。这是"团级已确认"口径,与既有的 `totalSeats` / `totalCount`("逐户已提交"口径)**并列**下发、允许不相等——两者不等是常态不是缺陷,任何断言两者相等的前端逻辑都是错的。
|
||||||
|
- 🔴 **#8545**:`vehicleSeatSummary[]` 可能新增一行只有 `confirmedSeats`/`confirmedCount` 非零、`totalSeats`/`totalCount` 恒为 `0` 的车型条目——发生在车务把车型整团定成了逐户报的车型集合之外的某个车型时(例如逐户都报 `mpv`,车务整团定成 `bus`)。前端渲染这张表时不能假设"有座位数就等于有逐户报",要按 4 个数字各自判断。
|
||||||
|
- **#8619**:`GET .../orders` 的 `vehicleRequirementStatus` / `vehicleRequirementKind` 口径从"只看行程用车(TRAVEL)"放宽为"该户展示序首条活跃用车需求行"(行程用车优先、其次接送机)。🔴 **改前只提交了接送机需求的户,这两列恒为 `null`、`vehicleRequirementStatusName` 被渲染成"未提交"——这是一个错误的展示(该户其实已提交、可能已在审/已派车/已完成),本次已修复**。有行程用车需求行的户这两列读数不变。
|
||||||
|
- **#8619**:`vehicleRequirementKind` 的**实际取值域**从恒为 `TRAVEL` 放宽为 `{TRAVEL, TRANSFER}`——`VehicleRequirementKind` 枚举本身没有新增第三个值,仍然只有这两个;变的是这一个响应字段过去被上游按 TRAVEL 过滤取行、现在按展示序取行,所以能观测到 `TRANSFER`。前端如果曾按"这一列永远是 TRAVEL"写死过图标/文案分支,需要按实际值渲染。
|
||||||
|
- **#8619**:`orders` 新增 `vehicleRequirements[]`(该户全部活跃用车需求行,**恒非 `null`,0~2 条**,行程用车在前、接送机在后)。一户同时提交了行程用车与接送机时,两条需求行状态可能各自不同(例如 TRAVEL 已 `DONE`、TRANSFER 还在 `PENDING_REVIEW`),此时上面那组单值字段(`vehicleRequirementStatus`/`vehicleRequirementKind`)**只能表达其中一条**——按类别判断状态必须读这个数组,不要只读单值字段。
|
||||||
|
- `orders` 端点其余 25 个既有字段(`hotelRequirementStatus`、`totalPrice`、`travelers` 等)本次未变,已由 changelog `30_8543_团期订单Tab用车状态按需求类别拆分并统一文案-修改接口-管理后台.md` 完整记录,此处不重复。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 二、变更接口清单
|
||||||
|
|
||||||
|
| # | 接口 | 方法 | 路径 | 变更类型 | 说明 |
|
||||||
|
|---|------|------|------|----------|------|
|
||||||
|
| 1 | 全团需求汇总 | GET | `/v3/admin/order/group-batch/{groupBatchId}/requirement-summary` | 修改 | `vehicleSeatSummary[]` 新增 `confirmedSeats`/`confirmedCount`(#8545) |
|
||||||
|
| 2 | 读团期正式用车需求 | GET | `/v3/admin/order/group-batch/{groupBatchId}/vehicle-requirement` | 修改 | 响应体新增 4 个 `*Name` 字段(#8544),PUT/withdraw/waive 共用同一响应体同步生效 |
|
||||||
|
| 3 | 自动汇总正式用车需求草稿 | GET | `/v3/admin/order/group-batch/{groupBatchId}/vehicle-requirement/aggregate-draft` | 修改 | `draft` 字段改用只读读侧 VO,新增 `groups[].vehicleTypeName`(#8544) |
|
||||||
|
| 4 | 团期下子订单列表 | GET | `/v3/admin/order/group-batch/{groupBatchId}/orders` | 修改 | `vehicleRequirementKind` 取值域放宽、新增 `vehicleRequirements[]`(#8619) |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 三、接口详情
|
||||||
|
|
||||||
|
### 1. 全团需求汇总 `GET /v3/admin/order/group-batch/{groupBatchId}/requirement-summary`
|
||||||
|
|
||||||
|
**VO**: `GroupRequirementSummaryRespVO`
|
||||||
|
|
||||||
|
#### 使用场景
|
||||||
|
|
||||||
|
团期需求管理 Tab 打开时拉取的"全团需求汇总"卡片,展示逐日房间合计与大巴座位合计。本次变更只影响座位合计部分——`vehicleSeatSummary[]` 新增团级已确认口径的两个字段,供页面并列展示"定制师报了多少座"与"车务最后定了多少座"。
|
||||||
|
|
||||||
|
#### 入参字段表
|
||||||
|
|
||||||
|
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|
||||||
|
|------|------|------|------|------|------|
|
||||||
|
| groupBatchId | Path | Long | 是 | - | 团期聚合主键 |
|
||||||
|
|
||||||
|
#### 出参字段表
|
||||||
|
|
||||||
|
| 字段 | 类型 | 说明 |
|
||||||
|
|------|------|------|
|
||||||
|
| activeOrderCount | int | 在团子订单数(仅排除 CANCELLED,含 COMPLETED),未变 |
|
||||||
|
| hotelRequirementCount | int | 有 active 用房需求记录的子订单数(兼容保留字段),未变 |
|
||||||
|
| hotelNeededOrderCount | int | 需要订房的户数,未变 |
|
||||||
|
| hotelFlagMismatchOrderCount | int | needsHotel≠true 却已提交有效用房需求的户数,未变 |
|
||||||
|
| hotelSubmittedOrderCount | int | 已提交有效用房需求且计入 dailyRoomBreakdown 的户数,未变 |
|
||||||
|
| vehicleRequirementCount | int | 已提交用车需求的子订单数,未变 |
|
||||||
|
| dailyRoomBreakdown | array | 逐日房间汇总,本次未变 |
|
||||||
|
| vehicleSeatSummary | array | 大巴座位汇总(按车型),本次新增字段见下表 |
|
||||||
|
| orderSpecialTags | array | 各子订单 specialTags,本次未变 |
|
||||||
|
| transferSummary | object | 接送机汇总(#8151 既有字段),本次未变 |
|
||||||
|
|
||||||
|
`vehicleSeatSummary[]` 单项字段:
|
||||||
|
|
||||||
|
| 字段 | 类型 | 说明 |
|
||||||
|
|------|------|------|
|
||||||
|
| vehicleType | string | 车型大类编码(suv/mpv/bus/sedan;缺失时为"未知"),未变 |
|
||||||
|
| vehicleTypeName | string | 车型大类中文名;查不到或车队服务不可用时为 `null`,未变 |
|
||||||
|
| totalSeats | int | 合计座位数(seats × count 之和);统计基数为"逐户已提交"的行程用车需求,未变 |
|
||||||
|
| totalCount | int | 合计车辆台数;统计基数为"逐户已提交"的行程用车需求,未变 |
|
||||||
|
| confirmedSeats 🆕 | int | 合计座位数(团级已确认的正式需求口径);尚未整团确认时为 `0`,与 totalSeats 不等是常态(#8545) |
|
||||||
|
| confirmedCount 🆕 | int | 合计车辆台数(团级已确认的正式需求口径);尚未整团确认时为 `0`(#8545) |
|
||||||
|
|
||||||
|
#### 请求示例
|
||||||
|
|
||||||
|
```http
|
||||||
|
GET /v3/admin/order/group-batch/2104839654727618562/requirement-summary
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 响应示例
|
||||||
|
|
||||||
|
以下为测试环境实测响应(团期批次 `2104840641651556353`,2026-09-30 采集;仅展示 `vehicleSeatSummary[0]`,其余字段结构未变不重复列出):
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"code": 200,
|
||||||
|
"message": "成功",
|
||||||
|
"data": {
|
||||||
|
"vehicleSeatSummary": [
|
||||||
|
{
|
||||||
|
"vehicleType": "mpv",
|
||||||
|
"vehicleTypeName": "商务车",
|
||||||
|
"totalSeats": 14,
|
||||||
|
"totalCount": 2,
|
||||||
|
"confirmedSeats": 7,
|
||||||
|
"confirmedCount": 1
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"traceId": null,
|
||||||
|
"success": true
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 空数据 / 降级响应
|
||||||
|
|
||||||
|
- 团期批次不存在:返回业务错误(见"错误响应"),`data` 为 `null`。
|
||||||
|
- 全团无任何用车需求:`vehicleSeatSummary` 为空数组,不是 `null`。
|
||||||
|
- 团级从未整团确认过(或已被重开成 `PENDING_RECONFIRM`):既有行的 `confirmedSeats`/`confirmedCount` 均为 `0`,不追加新行;此时该数组与改动前逐户口径的行完全一致。
|
||||||
|
- 车务整团定的车型不在逐户已提交的车型集合内:追加一行 `totalSeats=0`/`totalCount=0`、`confirmedSeats`/`confirmedCount` 非零的记录,`vehicleTypeName` 仍会被正常补全。
|
||||||
|
|
||||||
|
#### 错误响应
|
||||||
|
|
||||||
|
```json
|
||||||
|
{"code": 589500, "message": "团期不存在", "success": false, "data": null}
|
||||||
|
```
|
||||||
|
|
||||||
|
```json
|
||||||
|
{"code": 589507, "message": "无操作权限(当前角色未授予团期权限,或该团期不在您名下)", "success": false, "data": null}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 业务边界
|
||||||
|
|
||||||
|
- `confirmedSeats`/`confirmedCount` 与 `totalSeats`/`totalCount` 是两个独立基数,前端不得假设两者相等或用其中一个推算另一个。
|
||||||
|
- 权限码为 `group-batch:view`。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 2. 读团期正式用车需求 `GET /v3/admin/order/group-batch/{groupBatchId}/vehicle-requirement`
|
||||||
|
|
||||||
|
**VO**: `GroupVehicleRequirementRespVO`
|
||||||
|
|
||||||
|
#### 使用场景
|
||||||
|
|
||||||
|
团期正式用车需求编辑页/详情页打开时的回填读口,同时是"配车计划刷新是否死了"在 admin 侧唯一无副作用的观测口(#7988)。`PUT`(保存)、`withdraw`(撤回)、`waive`(免车)三个写端点返回的响应体与本端点完全一致(同一个 Java 类),前端可以对四个端点复用同一套渲染逻辑。
|
||||||
|
|
||||||
|
#### 入参字段表
|
||||||
|
|
||||||
|
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|
||||||
|
|------|------|------|------|------|------|
|
||||||
|
| groupBatchId | Path | Long | 是 | - | 团期聚合主键 |
|
||||||
|
|
||||||
|
#### 出参字段表
|
||||||
|
|
||||||
|
| 字段 | 类型 | 说明 |
|
||||||
|
|------|------|------|
|
||||||
|
| requirementId | string(Long 转字符串) | 正式需求主键,未变 |
|
||||||
|
| groupBatchId | string(Long 转字符串) | 团期聚合主键,未变 |
|
||||||
|
| status | string | 状态:DRAFT/CONFIRMED/DISPATCHED/DONE/PENDING_RECONFIRM/CANCELLED,未变 |
|
||||||
|
| statusName 🆕 | string | 状态中文名(草稿/已确认/已发车务/配车完成/待重新确认/已取消);status 为 null 或认不出的编码时为 `null`,不回落编码原文(#8544) |
|
||||||
|
| version | int | 乐观锁版本号,未变 |
|
||||||
|
| remark | string | 整份备注;撤回/免车会把操作追加进来(不覆盖原备注),超 500 字**从头部截**并以 `…` 开头,未变 |
|
||||||
|
| confirmedBy | string | 整份确认人,DRAFT 时为 null,未变 |
|
||||||
|
| confirmedAt | string(LocalDateTime) | 整份确认时间,DRAFT 时为 null,未变 |
|
||||||
|
| planRefreshState | string | 配车刷新状态原值:null=从未登记过刷新(多数团期正常态)/PENDING/DONE/FAILED,未变 |
|
||||||
|
| planRefreshStateName 🆕 | string | 配车刷新状态中文名(刷新中/刷新完成/刷新失败);为 null 或认不出的编码时为 `null`(#8544) |
|
||||||
|
| planRefreshReplayCount | int | 人工受控重投累计次数(管理员点确认触发,不含自动重试),上限 5,未变 |
|
||||||
|
| blockedStage | string | 团期阻断阶段快照:null=未阻断;非空为受控重开发生那一刻的团期状态码(GroupBatchStatus 全域,常见 RESOURCE_PREPARING/MATERIAL_PREPARING/PENDING_DEPARTURE),未变 |
|
||||||
|
| blockedStageName 🆕 | string | 阻断阶段中文名(如 资源准备中/物料准备中/待出发);为 null 或认不出的编码时为 `null`(#8544) |
|
||||||
|
| planRefreshStalled | boolean | 刷新是否已停滞、不会自愈(恒非 null);true=必须有人处置,未变 |
|
||||||
|
| planRefreshStalledReason | string | 停滞归因:STATE_FAILED/COMMAND_FAILED/TIMEOUT;未停滞时为 null,未变 |
|
||||||
|
| planRefreshStalledReasonName 🆕 | string | 停滞归因中文名(刷新已被判死/刷新命令已失败/刷新超时);未停滞或认不出编码时为 `null`(#8544) |
|
||||||
|
| planRefreshTimeoutAt | string(LocalDateTime) | 本轮刷新超时时刻;仅 PENDING 且已登记发起时刻时有值,未变 |
|
||||||
|
| planRefreshReplayExhausted | boolean | 人工重投额度是否已耗尽(恒非 null),未变 |
|
||||||
|
| groups | array | 全部乘车分组(整团免车态为空数组),结构未变,见下表 |
|
||||||
|
| exemptHouseholds | array | 仅保存草稿(PUT)响应填充,其余端点(含本 GET)恒为 `null`,未变 |
|
||||||
|
|
||||||
|
`groups[]` 单项字段(未变,随 VO 一起下发本次新增的 4 个 `*Name` 兄弟字段):
|
||||||
|
|
||||||
|
| 字段 | 类型 | 说明 |
|
||||||
|
|------|------|------|
|
||||||
|
| groupId | string(Long) | 分组主键 |
|
||||||
|
| groupCode | string | 分组键,直接作为车费 alloc_group |
|
||||||
|
| vehicleType | string | 车型文本/字典值 |
|
||||||
|
| vehicleTypeName | string | 车型中文名(既有字段,非本次新增) |
|
||||||
|
| serviceStartDate / serviceEndDate | string(LocalDate) | 本组服务起止日 |
|
||||||
|
| seats / count | int | 该组单车座位数/车辆数量;存量分组为 null 表示待填 |
|
||||||
|
| specialTags | array | 该组特殊诉求标签(code + name) |
|
||||||
|
| remark | string | 该组备注/其他诉求;存量分组为 null |
|
||||||
|
| totalSeatCount | int | 总座位数 = seats × count |
|
||||||
|
| maxHeadcount | int | 该组 days 里的最大用车人数 |
|
||||||
|
| remainingPassengerSeats | int | 余座(扣司机位后的可乘座位 − 最大用车人数) |
|
||||||
|
| days | array | 逐日用车人数与成员 |
|
||||||
|
|
||||||
|
#### 请求示例
|
||||||
|
|
||||||
|
```http
|
||||||
|
GET /v3/admin/order/group-batch/2104839654727618562/vehicle-requirement
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 响应示例
|
||||||
|
|
||||||
|
以下为测试环境实测响应(团期批次 `2104840641651556353`,2026-09-30 采集)。`planRefreshState` / `blockedStage` / `planRefreshStalledReason` 三列在库中为 `NULL`,对应 `*Name` 实测为 `null`:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"code": 200,
|
||||||
|
"message": "成功",
|
||||||
|
"data": {
|
||||||
|
"status": "DONE",
|
||||||
|
"statusName": "配车完成",
|
||||||
|
"planRefreshState": null,
|
||||||
|
"planRefreshStateName": null,
|
||||||
|
"blockedStage": null,
|
||||||
|
"blockedStageName": null,
|
||||||
|
"planRefreshStalledReason": null,
|
||||||
|
"planRefreshStalledReasonName": null
|
||||||
|
},
|
||||||
|
"traceId": null,
|
||||||
|
"success": true
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 空数据 / 降级响应
|
||||||
|
|
||||||
|
- 团期尚未形成正式需求(从未 PUT 过):`data` 为 `null`(源码 `@ApiOperation` 明确标注"未形成时返回 null"),不是报错。
|
||||||
|
- `planRefreshState`/`blockedStage`/`planRefreshStalledReason` 三列 DB 为 `NULL`(从未走过受控重开,绝大多数团期的正常态):对应的 3 个 `*Name` 字段一律为 `null`,不下发默认中文名。
|
||||||
|
- `groups` 为空数组场景:整团免车态(waive 后)。
|
||||||
|
|
||||||
|
#### 错误响应
|
||||||
|
|
||||||
|
```json
|
||||||
|
{"code": 589500, "message": "团期不存在", "success": false, "data": null}
|
||||||
|
```
|
||||||
|
|
||||||
|
```json
|
||||||
|
{"code": 589507, "message": "无操作权限(当前角色未授予团期权限,或该团期不在您名下)", "success": false, "data": null}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 业务边界
|
||||||
|
|
||||||
|
- 4 个 `*Name` 字段是**只读派生展示字段**,不接受回传;`PUT` 请求体仍用原编码字段(`GroupVehicleRequirementSaveReqVO` 未新增字段)。
|
||||||
|
- 认不出的编码 → 对应 `*Name` 为 `null`,**不回落原编码**——这与团期列表页 `GroupBatchConverter#resolveBatchStatusName`(未知码回落原码)是刻意不同的两套口径,不要混用同一套前端兜底逻辑。
|
||||||
|
- 本端点权限码为 `group-batch:demand:confirm`,与 PUT/withdraw/waive/aggregate-draft 四个端点同码。
|
||||||
|
- 本端点后续不会引入任何写操作(#7988 明确约束),可放心作为无副作用轮询口使用。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 3. 自动汇总正式用车需求草稿 `GET /v3/admin/order/group-batch/{groupBatchId}/vehicle-requirement/aggregate-draft`
|
||||||
|
|
||||||
|
**VO**: `GroupVehicleAggregateDraftRespVO`(外层诊断字段未变;`draft` 字段类型改为 `GroupVehicleRequirementDraftRespVO`)
|
||||||
|
|
||||||
|
#### 使用场景
|
||||||
|
|
||||||
|
团期正式用车需求编辑弹窗首次打开、或点"重新汇总"时调用:按各子订单已提交的活跃行程用车需求自动生成一份分组草稿,`draft` 可原样 `PUT` 回 `/vehicle-requirement` 保存。本次变更只影响 `draft` 内部的类型与新增字段,外层的 `droppedFleetItems`/`staleHeadcountOrders`/`paddedOrderDays`/`seatOptionAdjusted`/`violations`/`exemptHouseholds` 六个诊断字段结构未变。
|
||||||
|
|
||||||
|
#### 入参字段表
|
||||||
|
|
||||||
|
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|
||||||
|
|------|------|------|------|------|------|
|
||||||
|
| groupBatchId | Path | Long | 是 | - | 团期聚合主键 |
|
||||||
|
|
||||||
|
#### 出参字段表
|
||||||
|
|
||||||
|
| 字段 | 类型 | 说明 |
|
||||||
|
|------|------|------|
|
||||||
|
| draft | object 🆕类型变更 | 汇总草稿,类型由写侧 `GroupVehicleRequirementSaveReqVO` 改为只读读侧 `GroupVehicleRequirementDraftRespVO`,见下表 |
|
||||||
|
| droppedFleetItems | array | 多车型户被丢弃的车型,未变 |
|
||||||
|
| staleHeadcountOrders | array | 人数已过期的户,未变 |
|
||||||
|
| paddedOrderDays | array | 为覆盖出发~返回而补进的日期,未变 |
|
||||||
|
| seatOptionAdjusted | array | 座位被兜底调整到车型可选档位的户,未变 |
|
||||||
|
| violations | array | 草稿违反保存态校验的逐条诊断,未变 |
|
||||||
|
| exemptHouseholds | array | 提交不了的豁免户(订单不在定制中/团期冻结且未被打回),未变 |
|
||||||
|
|
||||||
|
`draft`(`GroupVehicleRequirementDraftRespVO`)字段:
|
||||||
|
|
||||||
|
| 字段 | 类型 | 说明 |
|
||||||
|
|------|------|------|
|
||||||
|
| version | int | 乐观锁版本号;原样回传给 PUT;尚未形成正式需求时为 `null` |
|
||||||
|
| remark | string | 整份需求备注,汇总草稿恒为 `null` |
|
||||||
|
| groups | array | 全部乘车分组,恒非 null,无可汇总内容时为空数组;见下表 |
|
||||||
|
|
||||||
|
`draft.groups[]` 字段:
|
||||||
|
|
||||||
|
| 字段 | 类型 | 说明 |
|
||||||
|
|------|------|------|
|
||||||
|
| groupId | string(Long) | 既有分组主键,汇总草稿恒为 `null` |
|
||||||
|
| groupCode | string | 分组键,直接作为车费 alloc_group |
|
||||||
|
| vehicleType | string | 车型大类编码(归一后的 key) |
|
||||||
|
| vehicleTypeName 🆕 | string | 车型大类中文名(只读,不参与保存);字典查不到时为 `null`,不回落编码原文(#8544,本类相对写侧唯一新增字段) |
|
||||||
|
| serviceStartDate / serviceEndDate | string(LocalDate) | 本组服务起止日 |
|
||||||
|
| seats / count | int | 该组单车座位数(存量/无档位可取时可能为 null)/车辆数量 |
|
||||||
|
| specialTags | array(string) | 该组特殊诉求标签编码数组 |
|
||||||
|
| remark | string | 该组备注;汇总时按"团号(客户名): 备注"拼接,超 500 字**从尾部截断** |
|
||||||
|
| days | array | 逐日用车人数与成员,见下表 |
|
||||||
|
|
||||||
|
`draft.groups[].days[]` 字段:
|
||||||
|
|
||||||
|
| 字段 | 类型 | 说明 |
|
||||||
|
|------|------|------|
|
||||||
|
| tripDate | string(LocalDate) | 团期行程日 |
|
||||||
|
| headcount | int | 该组该日用车人数(乘车人数,非户数) |
|
||||||
|
| memberOrderIds | array(string) | 该组该日实际乘车的子订单集合 |
|
||||||
|
|
||||||
|
#### 请求示例
|
||||||
|
|
||||||
|
```http
|
||||||
|
GET /v3/admin/order/group-batch/2104839654727618562/vehicle-requirement/aggregate-draft
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 响应示例
|
||||||
|
|
||||||
|
以下为测试环境实测响应(团期批次 `2104840641651556353`,2026-09-30 采集;`days` 实测为 7 天逐日行,此处只保留首日,其余日同形):
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"code": 200,
|
||||||
|
"message": "成功",
|
||||||
|
"data": {
|
||||||
|
"groupBatchId": "2104840641651556353",
|
||||||
|
"currentStatus": "DONE",
|
||||||
|
"draft": {
|
||||||
|
"version": 3,
|
||||||
|
"remark": null,
|
||||||
|
"groups": [
|
||||||
|
{
|
||||||
|
"groupId": null,
|
||||||
|
"groupCode": "MPV",
|
||||||
|
"vehicleType": "mpv",
|
||||||
|
"vehicleTypeName": "商务车",
|
||||||
|
"serviceStartDate": "2026-11-11",
|
||||||
|
"serviceEndDate": "2026-11-17",
|
||||||
|
"seats": 7,
|
||||||
|
"count": 1,
|
||||||
|
"specialTags": [],
|
||||||
|
"remark": "26-3682(董海涛): 结伴出行客户,整团统一 7 座商务车,已与客户确认路线;26-0805(谢丽萍): 9月30日至10月3日行程用车,成人4人其中1位长者,行李较多需大后备箱",
|
||||||
|
"days": [
|
||||||
|
{
|
||||||
|
"tripDate": "2026-11-11",
|
||||||
|
"headcount": 4,
|
||||||
|
"memberOrderIds": ["2104840641597030402", "2104840685708525570"],
|
||||||
|
"memberOrderCount": 2
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"violations": []
|
||||||
|
},
|
||||||
|
"traceId": null,
|
||||||
|
"success": true
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
`groups[].remark` 的一户标识格式是**「团号(客户名)」**(`26-3682(董海涛)`),多户合并进同一车型组时以 `;` 连接。本次改动前该位置拼的是 19 位订单 ID,因此:**已确认落库的存量团期,其 `GET .../vehicle-requirement` 返回的 `groups[].remark` 仍是旧格式(`HL2026…` 订单号前缀)**——本单只改新生成的汇总草稿,不回写历史数据。前端不要按固定格式解析 `remark`,它是给运营看的自由文本。
|
||||||
|
|
||||||
|
#### 空数据 / 降级响应
|
||||||
|
|
||||||
|
- 团期批次不存在:返回业务错误(见"错误响应")。
|
||||||
|
- 全团无可汇总的行程用车需求:`draft.groups` 为空数组。
|
||||||
|
- 车型字典不可用:`vehicleTypeName` 降级为 `null`,`groups` 其余字段照常下发(不阻断整个响应)。
|
||||||
|
|
||||||
|
#### 错误响应
|
||||||
|
|
||||||
|
```json
|
||||||
|
{"code": 589500, "message": "团期不存在", "success": false, "data": null}
|
||||||
|
```
|
||||||
|
|
||||||
|
```json
|
||||||
|
{"code": 589507, "message": "无操作权限(当前角色未授予团期权限,或该团期不在您名下)", "success": false, "data": null}
|
||||||
|
```
|
||||||
|
|
||||||
|
```json
|
||||||
|
{"code": 809120, "message": "车队车型字典暂不可用,无法校验车型,请稍后重试", "success": false, "data": null}
|
||||||
|
```
|
||||||
|
|
||||||
|
有在团需车户缺少可汇总用车需求时(809121,触发条件已被 #8577 收窄——只提交了接送机的户不再算缺少):
|
||||||
|
|
||||||
|
```json
|
||||||
|
{"code": 809121, "message": "团期 「第3期 10月8日出发团」 有 1 户缺少可汇总的用车需求,暂不能自动汇总:26-0480:未提交用车需求", "success": false, "data": null}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 业务边界
|
||||||
|
|
||||||
|
- `draft` 是**只读展示体**,不挂任何 `@NotNull`/`@NotEmpty`/`@Size` 校验注解;违反保存态约束的地方在 `violations` 里另行列出,不要用 `draft` 自身的字段是否为空来判断能不能保存。
|
||||||
|
- `draft` 除 `groups[].vehicleTypeName` 外的字段集与写侧 `GroupVehicleRequirementSaveReqVO` 完全一一对应(由专门的字段一致性单测钉住),可以原样 `PUT` 回 `/vehicle-requirement`;`vehicleTypeName` 回传时会被后端忽略,车型以 `vehicleType` 编码为准。
|
||||||
|
- `draft.groups[].remark` 的拼接标签口径:团号(客户名)→ 团号 → 客户名 → 订单号 → "未知户",不再回落裸雪花订单 ID。
|
||||||
|
- 权限码与「2」相同(`group-batch:demand:confirm`),本端点同样不引入任何写操作。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 4. 团期下子订单列表 `GET /v3/admin/order/group-batch/{groupBatchId}/orders`
|
||||||
|
|
||||||
|
**VO**: `GroupBatchOrderItemRespVO`(30 个字段中本次只变更 4 个,见下表标注)
|
||||||
|
|
||||||
|
#### 使用场景
|
||||||
|
|
||||||
|
团期详情页"子订单"Tab 的列表数据源,每户一行。本次变更聚焦用车需求相关的 4 个字段,其余 26 个字段(`hotelRequirementStatus`、`totalPrice`、`travelers` 等)未变,已由 `30_8543_团期订单Tab用车状态按需求类别拆分并统一文案-修改接口-管理后台.md` 完整记录。
|
||||||
|
|
||||||
|
#### 入参字段表
|
||||||
|
|
||||||
|
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|
||||||
|
|------|------|------|------|------|------|
|
||||||
|
| groupBatchId | Path | Long | 是 | - | 团期聚合主键 |
|
||||||
|
| page | Query | Integer | 否 | 缺省 1,<1 归一为 1 | 页码 |
|
||||||
|
| pageSize | Query | Integer | 否 | 缺省 20,<1 归一为 20,>200 截断为 200 | 每页条数 |
|
||||||
|
| includeTravelers | Query | Boolean | 否 | 缺省 true | 是否附出行人明细(证件号/手机号一律不返回) |
|
||||||
|
| includeNeeds | Query | Boolean | 否 | 缺省 true | 是否附房数/房型/特殊需求 |
|
||||||
|
| includeCancelled | Query | Boolean | 否 | 缺省 false | 是否含已取消子订单 |
|
||||||
|
|
||||||
|
#### 出参字段表(仅列本次变更相关字段)
|
||||||
|
|
||||||
|
| 字段 | 类型 | 说明 |
|
||||||
|
|------|------|------|
|
||||||
|
| vehicleRequirementStatus | string | 车需求状态:口径从"只看行程用车"改为"该户展示序首条活跃行"(行程用车优先、其次接送机)。该户一条活跃车需求行都没有时为 `null`(#8619) |
|
||||||
|
| vehicleRequirementStatusName | string | 状态中文名;未知码回落原 `code`(与 `vehicleRequirementKindName` 不同口径);`code` 为 `null` 时按该户 `needsVehicle` 分叉——`true` 出"未提交"、否则为 `null`。**#8619 起"未提交"只在两类需求都没报时出现** |
|
||||||
|
| vehicleRequirementKind | string | 本行车需求的用车类别:TRAVEL/TRANSFER。**#8619 起不再恒为 TRAVEL**——只报了接送机的户在这里下发 `TRANSFER`;无 active 行时为 `null` |
|
||||||
|
| vehicleRequirementKindName | string | 类别中文名:行程用车/接送机;认不出的类别给 `null`,**不回落编码**(与 `vehicleRequirementStatusName` 不同口径) |
|
||||||
|
| vehicleRequirements 🆕 | array | 该户全部活跃用车需求行(0~2 条:TRAVEL/TRANSFER 各至多一条),恒非 `null`,行程用车在前、接送机在后(#8619),见下表 |
|
||||||
|
|
||||||
|
`vehicleRequirements[]` 单项字段:
|
||||||
|
|
||||||
|
| 字段 | 类型 | 说明 |
|
||||||
|
|------|------|------|
|
||||||
|
| kind | string | 需求类别:TRAVEL=行程用车 / TRANSFER=接送机 |
|
||||||
|
| kindName | string | 类别中文名;认不出的类别给 `null`,不回落编码 |
|
||||||
|
| status | string | 该条需求状态:PENDING/PROCESSING/DONE/PENDING_REVIEW/REJECTED_TO_CONSULTANT/REJECTED_TO_ADMIN |
|
||||||
|
| statusName | string | 状态中文名;PENDING_REVIEW 按 kind 分两套文案:TRAVEL="待提交车务"、TRANSFER="待审核" |
|
||||||
|
|
||||||
|
#### 请求示例
|
||||||
|
|
||||||
|
```http
|
||||||
|
GET /v3/admin/order/group-batch/2104839654727618562/orders?page=1&pageSize=20
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 响应示例
|
||||||
|
|
||||||
|
以下为测试环境实测响应(团期批次 `2104839654727618562`,2026-09-30 采集)。每条 `records[]` 实测另有 26 个本次未变的字段,此处只保留与本单相关的 5 个,其余省略(完整字段集见 changelog `30_8543`):
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"code": 200,
|
||||||
|
"message": "成功",
|
||||||
|
"data": {
|
||||||
|
"records": [
|
||||||
|
{
|
||||||
|
"orderId": "2104839654652121090",
|
||||||
|
"vehicleRequirementStatus": "DONE",
|
||||||
|
"vehicleRequirementStatusName": "配车完成",
|
||||||
|
"vehicleRequirementKind": "TRAVEL",
|
||||||
|
"vehicleRequirementKindName": "行程用车",
|
||||||
|
"vehicleRequirements": [
|
||||||
|
{"kind": "TRAVEL", "kindName": "行程用车", "status": "DONE", "statusName": "配车完成"},
|
||||||
|
{"kind": "TRANSFER", "kindName": "接送机", "status": "PENDING", "statusName": "待车队配"}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"orderId": "2104839686486888449",
|
||||||
|
"vehicleRequirementStatus": "DONE",
|
||||||
|
"vehicleRequirementStatusName": "配车完成",
|
||||||
|
"vehicleRequirementKind": "TRAVEL",
|
||||||
|
"vehicleRequirementKindName": "行程用车",
|
||||||
|
"vehicleRequirements": [
|
||||||
|
{"kind": "TRAVEL", "kindName": "行程用车", "status": "DONE", "statusName": "配车完成"}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"orderId": "2104839729176514562",
|
||||||
|
"vehicleRequirementStatus": "DONE",
|
||||||
|
"vehicleRequirementStatusName": "配车完成",
|
||||||
|
"vehicleRequirementKind": "TRAVEL",
|
||||||
|
"vehicleRequirementKindName": "行程用车",
|
||||||
|
"vehicleRequirements": [
|
||||||
|
{"kind": "TRAVEL", "kindName": "行程用车", "status": "DONE", "statusName": "配车完成"},
|
||||||
|
{"kind": "TRANSFER", "kindName": "接送机", "status": "PENDING_REVIEW", "statusName": "待审核"}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"total": 3,
|
||||||
|
"page": 1,
|
||||||
|
"pageSize": 20
|
||||||
|
},
|
||||||
|
"traceId": null,
|
||||||
|
"success": true
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
上例里三户的 `vehicleRequirements` 长度分别为 2 / 1 / 2,`vehicleRequirementStatus` 与 `Kind` 取的都是展示序首条(TRAVEL 优先),因此三户顶层都是 `TRAVEL`;接送机那一行的真实状态只在 `vehicleRequirements[]` 里看得到。
|
||||||
|
|
||||||
|
#### 空数据 / 降级响应
|
||||||
|
|
||||||
|
- 团期批次不存在:返回业务错误(见"错误响应")。
|
||||||
|
- 团期下暂无子订单(或 `includeCancelled=false` 时全部已取消):`records` 为空数组,`total=0`。
|
||||||
|
- 该户一条活跃用车需求行都没有:`vehicleRequirementStatus`/`vehicleRequirementKind` 均为 `null`,`vehicleRequirements` 为**空数组**(不是 `null`);`vehicleRequirementStatusName` 按该户 `needsVehicle` 分叉。
|
||||||
|
- 该户只提交了接送机需求(#8619 修复的场景):`vehicleRequirementKind` 下发 `TRANSFER`、`vehicleRequirementStatus` 下发该行程的真实状态,不再是 `null`/"未提交"。
|
||||||
|
|
||||||
|
#### 错误响应
|
||||||
|
|
||||||
|
```json
|
||||||
|
{"code": 589500, "message": "团期不存在", "success": false, "data": null}
|
||||||
|
```
|
||||||
|
|
||||||
|
```json
|
||||||
|
{"code": 589507, "message": "无操作权限(当前角色未授予团期权限,或该团期不在您名下)", "success": false, "data": null}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 业务边界
|
||||||
|
|
||||||
|
- 按用车类别判断状态一律读 `vehicleRequirements[]`,不要只读 `vehicleRequirementStatus`/`vehicleRequirementKind` 单值字段——一户两类需求并存时单值字段只能表达展示序首条。
|
||||||
|
- `vehicleRequirementKindName` 与 `vehicleRequirementStatusName` 是两套不同的未知码兜底口径(前者给 `null`,后者回落原码),不要用同一段兜底逻辑处理。
|
||||||
|
- **🔴 同一个 `status` 编码在不同 `kind` 上的中文名不同,前端不能自建 code→中文 映射表**:`PENDING_REVIEW` 在 `TRAVEL` 行下发「待提交车务」(推走它的是团期管理员整团一次的「提交车务」,该状态上没有逐户审核动作),在 `TRANSFER` 行下发「待审核」(有逐户审核动作)——这是 #8218 起的刻意分叉,声明见 `GroupVehicleHouseholdsRespVO`/`GroupBatchOrderItemRespVO` 的 `@ApiModelProperty`。两侧均有实测样本:批次 `2104839654727618562` 的 `2104839729176514562` 户 TRANSFER 行 = `PENDING_REVIEW`/「待审核」;批次 `2104840641651556353` 的 `2104840685708525570` 户 TRAVEL 行 = `PENDING_REVIEW`/「待提交车务」。**一律直接渲染后端下发的 `statusName`。**
|
||||||
|
- 权限码为 `group-batch:view`。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 四、契约约束与正确调用方式
|
||||||
|
|
||||||
|
- **4 个新增 `*Name` 字段(#8544)是只读展示字段**:`statusName`/`planRefreshStateName`/`blockedStageName`/`planRefreshStalledReasonName` 只在响应体里出现,`PUT` 请求体 `GroupVehicleRequirementSaveReqVO` 未新增任何字段,回传这些字段会被忽略。
|
||||||
|
- **PUT `/vehicle-requirement`、POST `/vehicle-requirement/withdraw`、POST `/vehicle-requirement/waive` 三个写端点与本文档「三、2」共用完全同一个 `GroupVehicleRequirementRespVO` 类**:调用这三个端点后,响应体里同样带有 4 个新增 `*Name` 字段,字段语义、null 规则与「三、2」逐字一致,无需前端另写一套解析。
|
||||||
|
- **`aggregate-draft` 的 `draft` 字段类型变更(#8544)**:TypeScript/接口类型定义如果之前直接复用了 `GroupVehicleRequirementSaveReqVO` 的类型作为 `draft` 的类型,需要改成新类型(多一个只读字段 `groups[].vehicleTypeName`,其余字段名与类型逐一相同)。JSON 结构层面对已有解析代码零破坏,只有严格 schema 校验(如果有)需要放开这个新字段。
|
||||||
|
- **未知/认不出的编码统一规则**(本次涉及的所有 `*Name` 字段):`statusName`/`planRefreshStateName`/`blockedStageName`/`planRefreshStalledReasonName`/`draft.groups[].vehicleTypeName`/`vehicleRequirementKindName`/`vehicleRequirements[].kindName` 这一组字段认不出编码一律给 `null`,**不回落原编码**。这与 `orders` 端点的 `vehicleRequirementStatusName`(未知码回落原码,#8543/#8619 未改动此口径)以及团期列表页的团期状态中文名(同样回落原码)是**刻意不同**的两套口径,前端不要用同一段兜底组件处理。
|
||||||
|
- **`vehicleSeatSummary[].confirmedSeats`/`confirmedCount` 与 `totalSeats`/`totalCount` 不相等是设计上允许的常态**(#8545),不要写断言校验两者相等,也不要用其中一组数字反推另一组。
|
||||||
|
- **`orders` 端点判断某户是否提交了某类用车需求,一律遍历 `vehicleRequirements[]` 按 `kind` 过滤**,不要依赖单值字段 `vehicleRequirementStatus`/`vehicleRequirementKind`(#8619 起单值字段只表达展示序首条,可能丢失第二类需求的状态)。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 六、边界行为
|
||||||
|
|
||||||
|
- 未登录访问:网关拦截,不进入本文档描述的业务逻辑。
|
||||||
|
- 权限不足(当前角色未获授团期权限,或该团期不在本人名下):所有 4 个端点统一报 `589507`。
|
||||||
|
- 团期不存在:所有 4 个端点统一报 `589500`。
|
||||||
|
- `GET .../vehicle-requirement` 团期尚未形成正式需求:`data` 为 `null`,HTTP 200,不是错误。
|
||||||
|
- `planRefreshState`/`blockedStage`/`planRefreshStalledReason` 三列 DB 为 `NULL`(绝大多数团期的正常态,从未走过受控重开):对应 3 个 `*Name` 字段一律为 `null`。
|
||||||
|
- `vehicleSeatSummary[]`:团级从未确认过时 `confirmedSeats`/`confirmedCount` 恒为 `0`(不是 `null`);车务定的车型在逐户报的车型集合之外时会新增一行 `totalSeats=0`/`totalCount=0` 的记录。
|
||||||
|
- `orders` 端点 `vehicleRequirements[]`:该户没有任何活跃用车需求行时为空数组(不是 `null`)。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 六.5、枚举
|
||||||
|
|
||||||
|
**所属字段**:`GroupVehicleRequirementRespVO.status` / `statusName`
|
||||||
|
|
||||||
|
| 值 | 中文名 | 说明 |
|
||||||
|
|----|--------|------|
|
||||||
|
| DRAFT | 草稿 | 团期管理员正在汇总逐户需求、编辑分组与逐日人数 |
|
||||||
|
| CONFIRMED | 已确认 | 整份确认通过,等待发车务 |
|
||||||
|
| DISPATCHED | 已发车务 | 已推送到车务侧,等待配车 |
|
||||||
|
| DONE | 配车完成 | 车务侧已完成整团配车 |
|
||||||
|
| PENDING_RECONFIRM | 待重新确认 | 确认后团期人数/成员发生变化,需要重新确认整份 |
|
||||||
|
| CANCELLED | 已取消 | - |
|
||||||
|
|
||||||
|
**所属字段**:`GroupVehicleRequirementRespVO.planRefreshState` / `planRefreshStateName`
|
||||||
|
|
||||||
|
| 值 | 中文名 | 说明 |
|
||||||
|
|----|--------|------|
|
||||||
|
| null(列值 NULL) | (无中文名,字段为 null) | 从未登记过任何刷新,多数团期的正常态 |
|
||||||
|
| PENDING | 刷新中 | 刷新命令已登记,等 fleet 刷完 |
|
||||||
|
| DONE | 刷新完成 | 就绪回调已通过判定并应用,本轮刷新闭环 |
|
||||||
|
| FAILED | 刷新失败 | 已耗尽重试预算或被 fleet 判定性终结;不放行团期,出团门②持续拒绝 |
|
||||||
|
|
||||||
|
**所属字段**:`GroupVehicleRequirementRespVO.planRefreshStalledReason` / `planRefreshStalledReasonName`
|
||||||
|
|
||||||
|
| 值 | 中文名 | 说明 |
|
||||||
|
|----|--------|------|
|
||||||
|
| STATE_FAILED | 刷新已被判死 | `plan_refresh_state` 已经是 FAILED |
|
||||||
|
| COMMAND_FAILED | 刷新命令已失败 | 状态列仍是 PENDING,但刷新命令已终态失败(回写钩子未落成) |
|
||||||
|
| TIMEOUT | 刷新超时 | 状态列仍是 PENDING、命令也未判死,但已超过本轮窗口时限(缺省 120 分钟) |
|
||||||
|
|
||||||
|
**所属字段**:`GroupVehicleRequirementRespVO.blockedStage` / `blockedStageName`
|
||||||
|
|
||||||
|
取值域是团期状态枚举 `GroupBatchStatus`(既有枚举,本次未新增值)的全域,本次只是给这个已有编码字段配了中文名。常见值举例:
|
||||||
|
|
||||||
|
| 值 | 中文名 | 说明 |
|
||||||
|
|----|--------|------|
|
||||||
|
| RESOURCE_PREPARING | 资源准备中 | - |
|
||||||
|
| MATERIAL_PREPARING | 物料准备中 | - |
|
||||||
|
| PENDING_DEPARTURE | 待出发 | - |
|
||||||
|
| (其余 GroupBatchStatus 取值) | 对应中文名 | 认不出的编码给 `null` |
|
||||||
|
|
||||||
|
**所属字段**:`orders[].vehicleRequirementKind` / `vehicleRequirementKindName`、`orders[].vehicleRequirements[].kind` / `kindName`
|
||||||
|
|
||||||
|
`VehicleRequirementKind` 枚举本身固定只有 2 个值(本次未新增第三个值,放宽的是响应字段的**实际观测取值范围**,不是枚举定义):
|
||||||
|
|
||||||
|
| 值 | 中文名 | 说明 |
|
||||||
|
|----|--------|------|
|
||||||
|
| TRAVEL | 行程用车 | 服务日冻结为行程日 |
|
||||||
|
| TRANSFER | 接送机 | 服务日取航班/车次日期,允许落在行程日窗外 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 六.6、修改前后对比
|
||||||
|
|
||||||
|
| 项 | 改前 | 改后 |
|
||||||
|
|----|------|------|
|
||||||
|
| `GroupVehicleRequirementRespVO` 状态/刷新状态/阻断阶段/停滞归因 | 仅下发编码,前端自行映射中文 | 新增 4 个 `*Name` 字段直接下发中文名;认不出编码给 `null` |
|
||||||
|
| `aggregate-draft.draft` 字段类型 | 写侧 `GroupVehicleRequirementSaveReqVO`(挂校验注解,多余的约束会渲染进 Swagger) | 只读读侧 `GroupVehicleRequirementDraftRespVO`(不挂校验注解,多一个只读字段 `vehicleTypeName`) |
|
||||||
|
| `draft.groups[].remark` 逐户标签兜底 | 团号(客户名)→ 团号 → 客户名 → 订单号(裸雪花 ID) | 团号(客户名)→ 团号 → 客户名 → 订单号 → "未知户"(不再回落裸雪花 ID) |
|
||||||
|
| `requirement-summary.vehicleSeatSummary[]` | 只有 `totalSeats`/`totalCount`(逐户已提交口径) | 并列新增 `confirmedSeats`/`confirmedCount`(团级已确认口径),可能新增车型行 |
|
||||||
|
| `orders[].vehicleRequirementStatus`/`Kind` | 只按 TRAVEL 过滤取行;只提交接送机的户恒为 `null`/"未提交" | 取展示序首条活跃行(TRAVEL 优先、TRANSFER 次之);只提交接送机的户下发真实状态 |
|
||||||
|
| `orders[].vehicleRequirements` | 不存在该字段 | 新增,0~2 条,逐类下发状态 |
|
||||||
|
|
||||||
|
## 六.7、影响评估
|
||||||
|
|
||||||
|
- **前端必改**:如果曾对 `draft.remark` 文本做正则匹配来提取订单号或高亮逐户标签,需要兼容新的"未知户"文案且不再假设会出现裸数字订单 ID。**并且请不要按任何固定格式解析 `remark`**——本单只改新生成的汇总草稿,已确认落库的存量团期其 `GET .../vehicle-requirement` 返回的 `groups[].remark` 仍是改前格式(订单号前缀,形如 `HL20260929154809598: …`),不做历史数据回写。该字段是给运营看的自由文本,两种格式会长期并存。
|
||||||
|
- **前端必改**:如果曾假设 `orders[].vehicleRequirementKind` 恒为 `TRAVEL` 来做图标/文案硬编码分支,需要改为按实际值(`TRAVEL`/`TRANSFER`)渲染,并优先改用 `vehicleRequirements[]` 数组按类判断。
|
||||||
|
- **前端可选增强**:可以直接展示后端下发的 4 个新增中文名字段,替换掉前端此前自行维护的编码→中文映射表(如果有)。
|
||||||
|
- **前端需知悉但不需要立即改**:`vehicleSeatSummary[]` 新增的两个字段、可能新增的车型行,只在页面展示这两个数字时才需要处理;不展示则忽略即可,不影响既有渲染。
|
||||||
|
- **零风险**:所有新增字段都是**在既有 JSON 对象上新增键**,未删除、未改名任何既有字段(`aggregate-draft.draft` 虽改了后端类型,但 JSON 键集合与既有字段类型未变,由专门单测钉住);未做严格 schema 校验的前端代码可无感兼容。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 七、不影响范围
|
||||||
|
|
||||||
|
- `GroupVehicleRequirementSaveReqVO`(PUT 请求体)字段集未变,仍是原有编码字段集,本次新增的只读字段不参与保存也不会被写侧读取。
|
||||||
|
- `GroupVehicleRequirementDraftRespVO` 除 `groups[].vehicleTypeName` 外的全部字段(顶层 `version`/`remark`、`groups[]` 内 `groupId`/`groupCode`/`vehicleType`/`serviceStartDate`/`serviceEndDate`/`seats`/`count`/`specialTags`/`days`)未变,由 `GroupVehicleRequirementDraftRespVOFieldParityTest` 钉住与写侧字段集一致。
|
||||||
|
- `VehicleRequirementKind` 枚举定义本身未变,仍只有 `TRAVEL`/`TRANSFER` 两个值。
|
||||||
|
- 团期用房相关端点(`hotel-households` 等)不在本次改动范围内。
|
||||||
|
- `orders` 端点除本文档列出的 4 个字段外,其余 26 个字段未变(详见 changelog `30_8543`)。
|
||||||
|
- 809121/809122/809123 三个错误码的触发条件收窄与文案改写属于 #8577,不在本次三张工单范围内,详见 changelog `30_8577_只提交接送机的户不再被判未提交用车需求-修改接口-管理后台.md`。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 八、测试环境已验证
|
||||||
|
|
||||||
|
- hl-order-service-v3 dev-v3 分支已部署测试网关,HEAD `6373e5cf2`(`git merge-base --is-ancestor` 核实本单提交 `5f2f86bc96` 已在其祖先链内),jar mtime 2026-09-30 09:27:12,两个实例 Nacos 健康检查均为 healthy。
|
||||||
|
- 以下四条读口实测取样自团期批次 `2104840641651556353`(`vehicleRequirements[] == []` 一条取样自 `2104830530031919106`),2026-09-30 采集。
|
||||||
|
- `GET .../requirement-summary` 实测:`vehicleSeatSummary[0]` = `{"vehicleType":"mpv","vehicleTypeName":"商务车","totalSeats":14,"totalCount":2,"confirmedSeats":7,"confirmedCount":1}`。
|
||||||
|
- `GET .../vehicle-requirement/aggregate-draft` 实测:`draft.groups` 非空,含 `vehicleTypeName: "商务车"`;`groups[0].remark` 为「团号(客户名)」格式(`26-3682(董海涛): …;26-0805(谢丽萍): …`),未出现裸雪花订单 ID。
|
||||||
|
- `GET .../orders` 实测:2 条子订单,每条 `vehicleRequirements` 键均存在且非 `null`,长度分别为 2 与 1。另在一个无活跃用车需求行的户上实测该键为**空数组 `[]` 而不是 `null`**(与 `GroupBatchConverter.toVehicleRequirementItems()` 从 `new ArrayList<>()` 起手、无 null 分支一致)。
|
||||||
|
- `GET .../vehicle-requirement` 实测:顶层键包含 `status`/`statusName`/`planRefreshState`/`planRefreshStateName`/`blockedStage`/`blockedStageName`/`planRefreshStalledReason`/`planRefreshStalledReasonName` 共 8 个;观测样本 `status="DONE"` → `statusName="配车完成"`;另外三个 DB 列为 `NULL`,对应 `*Name` 字段均实测为 `null`。
|
||||||
|
- **「认不出的编码 → `null`」这条行为本轮没有实测样本**:候选团期的 `plan_refresh_state` / `blocked_stage` / `plan_refresh_stalled_reason` 三列在库中全为 `NULL`,测试环境里造不出一个库内存着字典外编码的自然样本。该行为由源码与单测两侧钉住:`GroupVehiclePlanRefreshState.labelOf()`、`GroupBatchStatus.descOf()` 对未命中编码返回 `null`(不回落编码原文)。前端按 `null` 兜底渲染即可。
|
||||||
|
- 单元测试:hl-order-service-v3 模块 `Tests run: 561, Failures: 0, Errors: 0, Skipped: 0`,38 个相关测试类逐类点名核对报告文件均存在(38/38 命中);`nested_selector_census` 退出码 0(无 `@Nested` 类被静默漏跑)。
|
||||||
|
- ArchUnit/架构门禁测试:`Tests run: 167`,全绿。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 十、相关文档
|
||||||
|
|
||||||
|
- `docs/CODE_RULES.md` §3(VO 命名与读写侧分离约定)
|
||||||
|
- changelog `30_8543_团期订单Tab用车状态按需求类别拆分并统一文案-修改接口-管理后台.md`(`orders` 端点其余字段的完整文档,及 `vehicleRequirementKind` 字段本次改动前的基线状态)
|
||||||
|
- changelog `30_8577_只提交接送机的户不再被判未提交用车需求-修改接口-管理后台.md`(PUT/aggregate-draft/confirm-check 三处 809121/809122/809123 错误码本次收窄的完整文档)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 关联 / 联系人
|
||||||
|
|
||||||
|
- 关联 Issue:#8544、#8545、#8619
|
||||||
|
- 关联 PR:#8625(#8544 #8545)、#8624(#8619)
|
||||||
|
- 联系人:wx
|
||||||
在新工单中引用
屏蔽一个用户