文件
hl-api-changelog/changelogs-v2/2026-09/16_7441_团期整团确认接车侧-修改接口-管理后台.md
T
2026-09-16 09:31:17 +08:00

520 行
33 KiB
Markdown
原始文件 Blame 文件历史

此文件含有模棱两可的 Unicode 字符
此文件含有可能会与其他字符混淆的 Unicode 字符。 如果您是想特意这样的,可以安全地忽略该警告。 使用 Escape 按钮显示他们。
---
schema: "hl-changelog/v2"
ticket: "7441"
title: "团期整团确认改造接入车侧——预检/确认新增车侧字段,checkedResourceTypes 扩至用车,免车团确认不放行车侧"
consumer: "admin"
author: "wx(GIT)"
change_type: "修改接口"
backend_status: "deployed"
gateway_status: "verified"
frontend_status: "verified"
frontend_owner: "mmg"
frontend_ref: "a91d62f837637d54ce9814c8e4df64758e5e0419"
target_release: ""
verified_at: "2026-09-16"
status_note: "本单(#7441 PR-4)已由 PR #7778 squash 合入 dev-v3(合并提交 f0277a14f),测试服 order-v3 于 2026-09-16 01:21 部署该提交。confirm-check/confirm 两端点的核心场景(vehicleWaived 逃生口、ready 语义变化、809100/809103/809108/589533 收窄、重复确认幂等、PENDING_RECONFIRM 推进、CAS 回滚 809112)均经 hl-gateway 网关真实调用验证,见第八节。TRAVEL+TRANSFER 同户两类需求并存的场景(AC-13/14/21)因测试环境 TRANSFER 写侧全局开关关闭(809009,#7443 未上线的既有开关)未能验证,按源码核对列示,第八节已如实说明覆盖边界。mmg 2026-09-16 前端已交付:RequirementTab 预检区并列渲染 vehicleMissing 车侧缺失清单(detail 人话直显)+「本团整团免车」标识 + 预检文案两类缺失合并计数;确认成功提示补车侧放行条数(vehicleDispatchedCount 条数口径);直读 ready 不自算,809 段码走拦截器透 message;RequirementTab.spec +3 例 9/9,checkpoint 全绿。"
updated_at: "2026-09-16"
base: "dev-v3"
---
# order-v3: 团期整团确认改造接入车侧
> **存放目录**: `changelogs-v2/{YYYY-MM}/`(管理后台,二期 order-v3)
>
> **服务**: hl-order-service-v3 (端口 8083)
> **PR**: [#7778](https://git.1814.love:8443/wx/HL/pulls/7778)(squash 合入 dev-v3,合并提交 `f0277a14f`,同批含 PR-2d/PR-2e,另有一份 changelog 覆盖)
> **Issue**: #7441
> **日期**: 2026-09-16
> **影响范围**: 团期需求页「整体确认前预检」`GET .../requirement/confirm-check` 与「整体确认需求」`POST .../requirement/confirm` 两个既有管理后台端点,响应新增车侧字段,`confirm` 新增 4 个车侧错误码
---
## ⚠️ 关键变化(本版与上版行为不同,必读)
1. **`checkedResourceTypes` 从 `["HOTEL"]` 变为 `["HOTEL","VEHICLE"]`**——`#7535` 当时明确承诺「扩到用车是另一张单」,本单就是那张单。若前端此前按「该数组恒为 `["HOTEL"]`」写死判断,这里会翻转。
2. **`ready` 的必要条件变多**:改前 `ready = missing.isEmpty() && 阶段可确认`;改后 `ready = missing.isEmpty() && vehicleMissing.isEmpty() && 阶段可确认`。**同一个团可能出现 `missing` 为空数组但 `ready=false`** 的情况,此时必须读新增的 `vehicleMissing` 才能知道原因,不能再假设「`missing` 空即可确认」。
3. **确认响应新增 7 个车侧字段,3 个旧字段语义保持不变**(`dispatchedOrderIds`/`skippedOrderIds`/`dispatchedCount` 仍然只统计住宿,不含车)。
4. **`groupVehicleRequirementStatus` 不再恒为 `CONFIRMED`**:车侧已进入 `DISPATCHED` 或 `DONE` 的团再次确认时该字段回**原状态**(不倒退),这是正常态,不要渲染成异常。
5. **免车团(`vehicleWaived=true`)整团确认不放行车侧**:`vehicleDispatchedCount=0`,三个车侧 ID 列表为空数组,正式需求不推进——这不是漏放,是设计如此。
6. **`589533` 触发条件收窄为「只管住宿」**:改前住宿或车任一缺失都可能报 `589533`;本单起车侧缺失改抛 809 段专属码,`589533` 的 `{0}` 只统计住宿缺失户数。
7. **背景信息(非本单改动,供理解字段含义)**:团期管理员可在需求页对整团声明「本团无需用车」(`POST /v3/admin/order/group-batch/{groupBatchId}/vehicle-requirement/waive`,已上线)或撤销声明(`POST .../vehicle-requirement/withdraw`,已上线)。本单不改这两个端点的契约,只是预检/确认从此会读取它们产生的结果。声明免车曾经在「已有分组仍要声明免车」时报错码 `809113`;**该码自 `#7441` PR-3(已合并 dev-v3)起停用不再抛出**(改为整份换版为免车版本),本单起进一步不出现 `vehicleMissing` 意义上的相关缺失项,前端若还留有 `809113` 专属提示文案,可以确认无需再触发但不必删除(错误码本身仍占位保留)。
---
## 一、背景
`#7210` 交付的「整体确认」原本只校验、只放行**住宿**:预检 `GET .../requirement/confirm-check` 只看住宿缺失清单,确认 `POST .../requirement/confirm` 只推进住宿需求。团期正式**车**需求(分组 × 逐日 × 成员,由 `PUT/GET .../vehicle-requirement` 两个已上线端点维护)与「本团无需用车」声明(`waive`/`withdraw`,已上线)此前完全不接入这两个端点——车侧需要逐单在订单详情页另行放行,管理员在团期需求页看不到车侧是否齐备。
本单(`#7441` PR-4)让这两个已有端点在住宿之外**对称接入车侧**:预检同时给出车侧缺失清单,确认在住宿放行完成后,如果不是免车团,再推进正式车需求并批量放行在团户的车需求。
---
## 二、变更接口清单
| # | 接口 | 方法 | 路径 | 变更类型 | 说明 |
|---|------|------|------|----------|------|
| 1 | 整体确认前的缺失预检 | GET | `/v3/admin/order/group-batch/{groupBatchId}/requirement/confirm-check` | 响应新增字段 | 新增 5 个顶层字段 + 车侧缺失清单;`ready`/`checkedResourceTypes` 语义变化 |
| 2 | 整体确认需求(放行住宿+车) | POST | `/v3/admin/order/group-batch/{groupBatchId}/requirement/confirm` | 响应新增字段 + 新增错误码 | 车侧校验接入;免车团跳过车侧放行;新增 7 个响应字段 + 4 个车侧错误码;`589533` 收窄为只管住宿 |
---
## 三、接口详情
### 1. 整体确认前的缺失预检 `GET /v3/admin/order/group-batch/{groupBatchId}/requirement/confirm-check`
**VO**: `无请求体 → Result<GroupBatchRequirementCheckRespVO>`
#### 使用场景
团期需求页进入「查看需求」Tab 时调用,以及点击「确认」按钮前调用,用于据 `ready` 置灰按钮、据 `missing`/`vehicleMissing` 展示缺哪些户/哪些车侧问题。只读,零副作用,可任意重复调用。本单起该端点同时覆盖住宿与车侧两类资源。
#### 入参字段表
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|------|------|------|------|------|------|
| groupBatchId | Path | Long | 是 | - | 团期聚合主键(不变) |
(无查询参数、无请求体,本单未改动。)
#### 出参字段表
| 字段 | 类型 | 说明 |
|------|------|------|
| groupBatchId | Long(序列化为 String) | 团期聚合主键(不变) |
| batchStatus | String | 团期当前状态码(不变) |
| batchStatusName | String | 团期当前状态中文名(不变) |
| ready | Boolean | 语义变化:是否可以整体确认,改为 missing 为空 且 vehicleMissing 为空 且团期处于可确认阶段 |
| missing | List<MissingItem> | 改名为「住宿缺失清单」(字段名不变,含义收窄为只描述住宿),结构不变 |
| checkedResourceTypes | List<String> | 取值变化:恒为 ["HOTEL","VEHICLE"](改前恒为 ["HOTEL"]),服务端常量,非按团期配置动态算出 |
| 🆕 vehicleWaived | Boolean | 整团都不需要车时为 true,此时车侧校验整体跳过、vehicleMissing 恒为空数组。这是合法逃生口,不是异常 |
| 🆕 vehicleMissing | List<VehicleMissingItem> | 车侧缺失清单(按校验顺序全部列出,不按户合并);vehicleWaived=true 时为空数组 |
| 🆕 groupVehicleRequirementId | Long(序列化为 String) | 当前活跃正式用车需求主键;无活跃正式需求时为 null |
| 🆕 groupVehicleRequirementStatus | String | 当前活跃正式用车需求状态;无则 null。取值 DRAFT/CONFIRMED/DISPATCHED/DONE/PENDING_RECONFIRM——DISPATCHED 与 DONE 同样属于预检可通过的正常状态,不要渲染成异常 |
| 🆕 groupVehicleRequirementVersion | Integer | 当前活跃正式用车需求版本号;无则 null |
missing[](MissingItem,结构不变,仅补充在本节自包含):
| 字段 | 类型 | 说明 |
|------|------|------|
| orderId | Long(序列化为 String) | 子订单 ID |
| orderNo | String | 子订单号 |
| customerName | String | 客户姓名 |
| consultantId | String | 定制师 adminId |
| consultantName | String | 定制师姓名快照 |
| reason | String | NOT_SUBMITTED/ROOM_CATEGORY_MISSING/INVALID_REQUIREMENT/NIGHTS_MISMATCH/DAY_NUMBER_INVALID |
| reasonName | String | 缺失原因中文名 |
| dayNumber | Integer | 第几晚(仅 ROOM_CATEGORY_MISSING 有值) |
| segmentIndex | Integer | 第几段(仅 ROOM_CATEGORY_MISSING 有值) |
| expectedNights | Integer | 应住晚数(仅 NIGHTS_MISMATCH/DAY_NUMBER_INVALID 有值) |
| actualNights | Integer | 实际填写晚数(仅 NIGHTS_MISMATCH/DAY_NUMBER_INVALID 有值) |
🆕 vehicleMissing[](VehicleMissingItem):
| 字段 | 类型 | 说明 |
|------|------|------|
| reason | String | 取值见「六.5」,与 809 段错误码/809007 一一对应 |
| groupCode | String | 涉及的乘车分组编码;无分组维度时为 null |
| tripDate | LocalDate | 涉及的日期;无日期维度时为 null |
| orderId | Long(序列化为 String) | 涉及的子订单 ID;无订单维度时为 null |
| orderNo | String | 子订单号快照 |
| detail | String | 人话描述,与整团确认时抛出的错误报文逐字相同,可直接展示 |
#### 请求示例
```http
GET /v3/admin/order/group-batch/1867000000001/requirement/confirm-check HTTP/1.1
Authorization: Bearer {token}
```
(无请求体,仅 Path 参数 groupBatchId。)
#### 响应示例
```json
{
"code": 200,
"message": "成功",
"success": true,
"data": {
"groupBatchId": "1867000000001",
"batchStatus": "RESOURCE_PREPARING",
"batchStatusName": "资源准备中",
"ready": false,
"missing": [],
"checkedResourceTypes": ["HOTEL", "VEHICLE"],
"vehicleWaived": false,
"vehicleMissing": [
{
"reason": "ORDER_DAY_UNCOVERED",
"groupCode": null,
"tripDate": null,
"orderId": "60123456789001",
"orderNo": "HL2606010001",
"detail": "子订单 60123456789001 的 2026-09-13 没有被任何乘车分组覆盖"
}
],
"groupVehicleRequirementId": "1868000000001",
"groupVehicleRequirementStatus": "DRAFT",
"groupVehicleRequirementVersion": 3
}
}
```
免车团示例(vehicleWaived=true):
```json
{
"code": 200,
"message": "成功",
"success": true,
"data": {
"groupBatchId": "1867000000002",
"batchStatus": "RESOURCE_PREPARING",
"batchStatusName": "资源准备中",
"ready": true,
"missing": [],
"checkedResourceTypes": ["HOTEL", "VEHICLE"],
"vehicleWaived": true,
"vehicleMissing": [],
"groupVehicleRequirementId": "1868000000005",
"groupVehicleRequirementStatus": "CONFIRMED",
"groupVehicleRequirementVersion": 1
}
}
```
#### 空数据 / 降级响应
团期尚未提交任何正式车需求(未编辑过 PUT .../vehicle-requirement、也未 waive)时,车侧三个身份字段(groupVehicleRequirementId/Status/Version)均为 null,vehicleMissing 按住宿同款缺失校验给出实际内容(不是空数组,除非该团确实零缺失或已免车)。本端点全程同步内存/DB 读取,不经 Feign/MQ,不产生降级分支。
```json
{ "code": 200, "success": true, "data": { "groupVehicleRequirementId": null, "groupVehicleRequirementStatus": null, "groupVehicleRequirementVersion": null, "vehicleMissing": [] } }
```
#### 错误响应
沿用既有码,本单未新增:
```json
{
"code": 589500,
"message": "团期不存在",
"success": false,
"data": null
}
```
#### 业务边界
- 判权沿用既有 group-batch:demand:confirm(GroupBatchPermissionGuard.PERMISSION_DEMAND_CONFIRM),本单未改。
- 车侧校验集合口径与保存草稿(PUT .../vehicle-requirement)、整团确认(见下条)完全共用一份校验内核,三处同一份数据得到同一个结论——本端点的 vehicleMissing 为空当且仅当真去点确认不会报车侧错误码(并发写入除外)。
- vehicleWaived=true 时车侧校验整体跳过,与是否有历史违规无关。
- 老数据兼容:本单不改任何已有字段的类型或序列化方式,存量前端若忽略新字段仍可正常渲染住宿部分;但 checkedResourceTypes 与 ready 的取值/语义已变,见「⚠️ 关键变化」。
---
### 2. 整体确认需求(放行住宿+车) `POST /v3/admin/order/group-batch/{groupBatchId}/requirement/confirm`
**VO**: `无请求体 → Result<GroupBatchRequirementConfirmRespVO>`
#### 使用场景
团期需求页点击「确认」按钮时调用。改前只校验并放行住宿;本单起在住宿放行成功之后,非免车团额外推进正式车需求并批量放行在团户的车需求(行程用车 TRAVEL + 接送机 TRANSFER 两类)。
#### 入参字段表
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|------|------|------|------|------|------|
| groupBatchId | Path | Long | 是 | - | 团期聚合主键(不变) |
(无请求体,本单未改动。)
#### 出参字段表
| 字段 | 类型 | 说明 |
|------|------|------|
| groupBatchId | Long(序列化为 String) | 团期聚合主键(不变) |
| requirementConfirmed | Boolean | 团期需求整体确认标记,成功后恒 true(不变) |
| dispatchedOrderIds | List<Long>(String) | 语义不变:仍只统计住宿,本次放行的住宿子订单 |
| skippedOrderIds | List<Long>(String) | 语义不变:仍只统计住宿 |
| dispatchedCount | Integer | 语义不变:仍只统计住宿(= dispatchedOrderIds.size()) |
| 🆕 vehicleDispatchedOrderIds | List<Long>(String) | 本次由「待审核」放行的【行程用车 TRAVEL】需求所属子订单 |
| 🆕 transferDispatchedOrderIds | List<Long>(String) | 本次由「待审核」放行的【接送机 TRANSFER】需求所属子订单 |
| 🆕 vehicleSkippedOrderIds | List<Long>(String) | 车侧本次未动的子订单(两类合并去重;该户该类车需求已非「待审核」)。整团免车时为空数组 |
| 🆕 vehicleDispatchedCount | Integer | 车侧本次放行的需求条数 = vehicleDispatchedOrderIds.size() + transferDispatchedOrderIds.size()。⚠️ 是条数不是户数,一户两类都放行计 2 |
| 🆕 groupVehicleRequirementId | Long(序列化为 String) | 本次确认所对应的正式车需求主键;整团免车且无正式需求(存量判据)时为 null |
| 🆕 groupVehicleRequirementStatus | String | 确认后正式车需求的实际状态,不是恒为 CONFIRMED:源状态 DRAFT/PENDING_RECONFIRM → 回 CONFIRMED;CONFIRMED 保持 CONFIRMED;DISPATCHED/DONE 回原值(车侧不动、状态不倒退,属正常态)。免车且无正式需求时为 null |
| 🆕 groupVehicleRequirementVersion | Integer | 确认后正式车需求版本号(推进到 CONFIRMED 时已 +1,原样返回时不变);免车且无正式需求时为 null |
#### 请求示例
```http
POST /v3/admin/order/group-batch/1867000000001/requirement/confirm HTTP/1.1
Authorization: Bearer {token}
```
(无请求体,仅 Path 参数 groupBatchId。)
#### 响应示例
```json
{
"code": 200,
"message": "成功",
"success": true,
"data": {
"groupBatchId": "1867000000001",
"requirementConfirmed": true,
"dispatchedOrderIds": ["60123456789001", "60123456789002", "60123456789003"],
"skippedOrderIds": [],
"dispatchedCount": 3,
"vehicleDispatchedOrderIds": ["60123456789001", "60123456789002", "60123456789003", "60123456789004", "60123456789005"],
"transferDispatchedOrderIds": [],
"vehicleSkippedOrderIds": [],
"vehicleDispatchedCount": 5,
"groupVehicleRequirementId": "1868000000001",
"groupVehicleRequirementStatus": "CONFIRMED",
"groupVehicleRequirementVersion": 2
}
}
```
免车团响应示例(vehicleDispatchedCount=0):
```json
{
"code": 200,
"message": "成功",
"success": true,
"data": {
"groupBatchId": "1867000000002",
"requirementConfirmed": true,
"dispatchedOrderIds": ["60123456789006"],
"skippedOrderIds": [],
"dispatchedCount": 1,
"vehicleDispatchedOrderIds": [],
"transferDispatchedOrderIds": [],
"vehicleSkippedOrderIds": [],
"vehicleDispatchedCount": 0,
"groupVehicleRequirementId": "1868000000005",
"groupVehicleRequirementStatus": "CONFIRMED",
"groupVehicleRequirementVersion": 1
}
}
```
#### 空数据 / 降级响应
不存在空态:本端点是写操作,要么全部成功返回上述结构,要么整个事务回滚并抛错误码。没有部分成功或静默降级的分支。
```json
{ "code": 200, "success": true, "data": { "vehicleDispatchedOrderIds": [], "transferDispatchedOrderIds": [], "vehicleSkippedOrderIds": [], "vehicleDispatchedCount": 0 } }
```
#### 错误响应
| 码 | 符号 | 触发 | 本单 |
|----|------|------|------|
| 589501 | GROUP_BATCH_STATUS_INVALID | 团期非可确认阶段 | 不变 |
| 589533 | GROUP_BATCH_REQUIREMENT_INCOMPLETE | 语义收窄:只在住宿缺失时抛,{0} 仍是住宿缺失户数 | 收窄 |
| 🆕 809100 | GROUP_VEHICLE_REQUIREMENT_NOT_FOUND | 有在团需车户却无正式车需求 | 本单起会抛 |
| 🆕 809101 | GROUP_VEHICLE_REQUIREMENT_STATUS_INVALID | 正式车需求状态不在 {DRAFT,CONFIRMED,DISPATCHED,DONE,PENDING_RECONFIRM},或推进 CAS 落空 | 本单起会抛 |
| 🆕 809103-809110 | 车侧八条逐日/成员/人数校验 | 见「六.5」 | 本单起会抛 |
| 🆕 809007 | TRANSFER_SERVICE_DATES_NOT_BACKFILLED | 待放行的接送机需求未回填服务日;{0} 为该需求主键(数字,非字符串) | 本单起会抛 |
| 🆕 809112 | GROUP_VEHICLE_DISPATCH_CAS_FAILED | 放行某户车需求时并发冲突(CAS 落空),整团事务回滚,零写入 | 本单起会抛 |
```json
{
"code": 589533,
"message": "仍有 2 户未提交需求或需求不完整,无法整体确认,请先查看缺失清单",
"success": false,
"data": null
}
```
车侧违规示例(该团抛第一条命中的违规,取决于哪条违规先被发现):
```json
{
"code": 809109,
"message": "子订单 60123456789001 的 2026-09-13 没有被任何乘车分组覆盖",
"success": false,
"data": null
}
```
#### 业务边界
- 顺序不可调换:① 阶段守卫(589501)→ ② 住宿缺失校验(589533,零写入)→ ③ 车侧校验(免车团整体跳过,否则抛第一条违规,零写入)→ ④ 置团级标记 + 写团级时间线 → ⑤ 逐户住宿放行 → ⑥ 非免车团:推进正式车需求 → ⑦ 非免车团:逐户放行车需求两类 → ⑧ 事务提交后异步通知房务。全部在同一个事务里,任一步失败整团零写入(809112 整团回滚即靠这一点)。
- 住宿排在车侧之前:两边都缺时仍报 589533,与改造前一致;只有住宿齐备、车侧有问题时才会看到 809 段码。
- 免车团(vehicleWaived=true)跳过 ⑥⑦ 两步:正式需求不推进(版本不 +1)、逐户车需求不放行(在团户残留的待审核车需求本次不动,需要就走逐单放行入口)。
- 809007 排在放行动作之前抛:预检阶段(上一条端点)已经把这类需求列进 vehicleMissing,ready=true 时不会再遇到本码,两者结构性一致。
- 重复确认不失败:正式车需求已是 CONFIRMED 时重复确认走幂等成功,不抛 809101;已放行的车需求本次计入 vehicleSkippedOrderIds 而不是报错——与住宿侧口径一致。
- 只改住宿的再次确认必须成功:车侧已 DISPATCHED/DONE、只有住宿需求被打回重提时,再次点整团确认——正式车需求保持原状态不倒退,车侧逐户按「已放行」计入 vehicleSkippedOrderIds(vehicleDispatchedCount=0),住宿正常放行。改前这一场景会被状态白名单直接拒掉,团再也确认不了;本单起这条路走通。
- 本端点不取车侧专属锁:与住宿放行共用团期需求锁,车侧两步不额外加锁(避免自锁)。
- 判权沿用既有 group-batch:demand:confirm,本单未改。
---
## 四、契约约束与正确调用方式
### 正确 / 错误 调用结果对照
| 场景 | 结果 |
|------|------|
| 预检 ready=true 后立即点确认,期间无其他人并发改动(正确) | confirm 成功,不因校验类错误码失败(CAS 类失败如 809101/809112 不在此等价关系内) |
| 前端只判断 missing.isEmpty() 就以为可以确认(错误) | 车侧仍可能缺失,点确认会报 809 段码;必须同时判 vehicleMissing.isEmpty()(或直接看 ready) |
| 前端按「checkedResourceTypes 恒为 ["HOTEL"]」写死判断(错误) | 本单起恒为 ["HOTEL","VEHICLE"],写死判断会得出错误结论 |
| 前端按「groupVehicleRequirementStatus 恒为 CONFIRMED」渲染确认结果(错误) | 车侧已 DISPATCHED/DONE 时该字段回原值,不是 CONFIRMED;按恒等判断会误判为异常 |
### 切换状态时的必要动作
前端渲染「整体确认」按钮的可用性时,必须把 vehicleMissing.isEmpty() 并入判断(或直接读 ready,不要自己用 missing.isEmpty() 重新计算);确认成功后的提示文案不能只读旧三个字段(否则车侧放行结果对用户不可见)。
---
## 五、数据库行为
预检端点全程只读,不产生任何写入。确认端点在同一个事务里,除既有的「置团级确认标记 + 住宿放行」外,本单额外做两件事:①非免车团把当前活跃正式车需求的状态从「待确认」推进为「已确认」(若已经是「已确认」及之后的状态则保持不变,不会倒退);②非免车团把在团户的行程用车与接送机需求从「待审核」批量放行为「待处理」。任一步失败(含并发写入冲突)都会让本次确认动作(含住宿放行)整体回滚,不会出现部分成功。
---
## 六、边界行为
- 未登录 → 401(网关拦截)
- 无权限 → 沿用既有 group-batch:demand:confirm 判权(未改)
- 团期不存在 → 589500(未改)
- 团期非可确认阶段 → 589501(未改)
- 住宿缺失 → 589533(收窄为只管住宿)
- 车侧缺失(非免车团)→ 809 段码,见「三、2」错误响应表
- 免车团 → 车侧校验/放行整体跳过,不产生任何车侧相关错误
- 老数据兼容:存量团期第一次读取本端点时,若从未编辑过车需求,groupVehicleRequirementId/Status/Version 均为 null,不异常
---
## 六.5、枚举
### vehicleMissing[].reason(服务端内部校验原因码,无独立 Java 枚举类,值见下表)
**所属字段**: `vehicleMissing[].reason` | **类型**: `String`
| 值 | 对应错误码 | 说明 |
|----|------|------|
| GROUP_REQUIREMENT_NOT_FOUND | 809100 | 有在团需车户却无正式车需求 |
| GROUP_REQUIREMENT_STATUS_INVALID | 809101 | 正式车需求状态不在合法集合 |
| NO_GROUP | 809103 | 本团存在需要用车的子订单,却零乘车分组 |
| GROUP_CODE_INVALID | 809104 | 乘车分组重复或试图改名 |
| DAY_OUT_OF_GROUP_RANGE | 809105 | 分组逐日行不在本组服务日范围内或重复提交 |
| DAY_GAP_IN_GROUP_RANGE | 809106 | 分组缺少某日的用车人数 |
| MEMBER_FOREIGN_ORDER | 809107 | 子订单不属于本团期,不能作为乘车成员 |
| MEMBER_DUPLICATE_DAY | 809108 | 子订单在同一日同时属于两个分组 |
| ORDER_DAY_UNCOVERED | 809109 | 子订单某日没有被任何乘车分组覆盖 |
| HEADCOUNT_LESS_THAN_MEMBERS | 809110 | 分组当日用车人数小于当日成员户数 |
| 🆕 TRANSFER_SERVICE_DATES_NOT_BACKFILLED | 809007 | 待放行的接送机需求未回填服务日;只带 orderId,groupCode/tripDate 为 null;处置是先回填服务日再确认;复用 #7439 既有码,非本单新增 |
### groupVehicleRequirementStatus(com.hulalv.order.groupbatch.enums.GroupVehicleRequirementStatus)
**所属字段**: `confirm-check`/`confirm` 响应的 `groupVehicleRequirementStatus` | **类型**: `String`
| 值 | 中文 | 说明 |
|----|------|------|
| DRAFT | 草稿 | 尚未提交审核 |
| CONFIRMED | 已确认 | 整团确认推进后的常见终态 |
| DISPATCHED | 已放行车务 | 团车已开始配车;确认时保持不动,不倒退 |
| DONE | 已完成 | 团车配完;确认时保持不动,不倒退 |
| PENDING_RECONFIRM | 待重确认 | 再次确认时可推进到 CONFIRMED |
---
## 六.6、修改前后对比
### 字段级对比
| 字段 | 改前 | 改后 |
|------|------|------|
| confirm-check.checkedResourceTypes | 恒 ["HOTEL"] | 恒 ["HOTEL","VEHICLE"] |
| confirm-check.ready | missing.isEmpty() && 阶段可确认 | missing.isEmpty() && vehicleMissing.isEmpty() && 阶段可确认 |
| confirm-check 车侧字段 | 不存在 | 新增 vehicleWaived/vehicleMissing/groupVehicleRequirementId/Status/Version 5 个 |
| confirm.dispatchedOrderIds/skippedOrderIds/dispatchedCount | 语义为「住宿」 | 语义不变,仍只统计住宿 |
| confirm 车侧字段 | 不存在 | 新增 7 个:见出参字段表 |
| 589533 触发条件 | 住宿或车任一缺失 | 只在住宿缺失时触发 |
### 行为级对比
| 行为 | 改前 | 改后 |
|------|------|------|
| 团期需求页点「确认」,住宿齐、车不齐 | 200 成功(车侧无感) | 809 段码阻断(非免车团) |
| 车侧已 DISPATCHED/DONE,住宿被打回重提后再次确认 | 809101 阻断,团再也确认不了 | 200 成功,车侧保持原状态、住宿正常放行 |
| 免车团点「确认」 | (本端点改造前免车概念不影响本端点) | 200 成功,车侧三个 ID 列表为空、vehicleDispatchedCount=0 |
| 预检时车侧有缺失 | ready 不受车侧影响(预检本不检查车) | ready=false,需读 vehicleMissing |
---
## 六.7、影响评估
- **是否破坏向后兼容**: 是。此前 ready=true(只看住宿)的部分团在本单合并部署后,若车侧未齐备会变成 ready=false;589533 触发条件收窄,改前依赖它同时报告车缺失的前端提示会失真。
- **前端是否必须同步上线**: 是。按「⚠️ 关键变化」逐条改:checkedResourceTypes 判断、ready/vehicleMissing 联合判断、确认响应新增字段的展示、groupVehicleRequirementStatus 不再恒 CONFIRMED 的容错。
- **前端 workaround 清理点**: 无(新增字段与语义收紧,非清理旧逻辑)。
---
## 七、不影响范围
- **仅影响**: `GET .../requirement/confirm-check` 与 `POST .../requirement/confirm` 两个既有端点的响应体与 `confirm` 的错误码集合。
- **零影响**:
- 路径、Path 参数、请求体(均无请求体)——完全不变。
- 判权码 group-batch:demand:confirm——未改。
- PUT/GET .../vehicle-requirement、POST .../vehicle-requirement/withdraw、POST .../vehicle-requirement/waive 四个已上线端点自身的契约——本单不改,只是被读取结果。
- POST .../requirement/reject(按户打回)、GET .../requirement-summary(全团需求汇总)——本单未改,按户打回逐户需求的语义完全不动。
- POST /v3/admin/order/{id}/vehicle-requirement/dispatch(逐单放行)——保留,与本单整团路径共用同一份副作用实现,未新增未删除。
- hl-common-*、hl-fleet-service、hl-gateway 路由——本单只改 hl-order-service-v3,/v3/admin/** 路由沿用既有通配,未新增路由配置。
---
## 八、测试环境已验证
**取证环境**:order-v3 = dev-v3 `f0277a14f`(2026-09-16 01:21 部署,含本单 PR-4 全部改动),经 hl-gateway 网关真实调用,见工单 #7441 验收评论 54924(AC-8/9/10/11/12/15/18)与 54939(AC-16)。
`GET .../requirement/confirm-check`:
- 免车逃生口(`vehicleWaived=true`):`ready=true`、`missing=[]`、`vehicleMissing=[]`,车侧三个身份字段均为 `null`(团 2099918391610314754)。
- 车侧正式需求缺失(`vehicleWaived=false`):`vehicleMissing=[{"reason":"GROUP_REQUIREMENT_NOT_FOUND","detail":"团期 2099918391610314754 尚未形成正式用车需求"}]`。
- `ready` 语义变化:房齐备、车零分组的团返回 `ready=false`、`missing=[]`、`vehicleMissing=[{"reason":"NO_GROUP",...}]`——「`missing` 空但 `ready=false`」的形态经真实调用坐实。
- 同日同户重复归属(数据经 SQL 直接向 `order_group_vehicle_group`/`order_group_vehicle_group_day` 造重叠行,读侧取证):`vehicleMissing=[{"reason":"MEMBER_DUPLICATE_DAY","groupCode":"GB","tripDate":"2026-10-24","orderId":"2099919145398046721","orderNo":"HL20260916015153509"}]`。
- 房侧缺失清单:`missing` 含两条真实户(`orderId` 2099918640269627394 / 2099918650218516481,`reason=NOT_SUBMITTED`)。
`POST .../requirement/confirm`:
- 免车团确认成功:`code=200`、`vehicleDispatchedCount=0`、`groupVehicleRequirementId=null`。
- 车侧需求缺失:`code=809100`,`message="团期 2099918596187598850 尚未形成正式用车需求"`。
- 房齐车零分组:`code=809103`,`message="本团存在需要用车的子订单,至少要提交一个乘车分组"`。
- 房缺 2 户、车侧已免车:`code=589533`,`message="仍有 2 户未提交需求或需求不完整,无法整体确认,请先查看缺失清单"`(`{0}=2` 坐实)。
- 同日同户重复归属:三入口(`PUT`/`confirm-check`/`confirm`)均报 `code=809108`,`message="子订单 2099919145398046721 在 2026-10-24 同时属于分组 GA、GB,同一户同一日只能属于一个分组"`——`PUT` 由业务接口直接触发重叠数据被拒;`confirm-check`/`confirm` 因 `PUT` 会在写入前就拒绝重叠数据、结构上无法经业务路径把该状态存进库,按 SQL 造重叠行后走读侧验证。
- 重复确认幂等:单户团 T0 `confirm` 成功(`vehicleDispatchedOrderIds=["2099919607505596417"]`、`vehicleDispatchedCount=1`、`groupVehicleRequirementStatus=CONFIRMED`、`version=2`),间隔 >5 秒后 T1 再次 `confirm` 成功且不抛 809101(`vehicleSkippedOrderIds=["2099919607505596417"]`、`vehicleDispatchedCount=0`,状态与版本不变)。
- `PENDING_RECONFIRM → CONFIRMED`:正式需求经 SQL 置为 `PENDING_RECONFIRM` 后再次 `confirm`,成功且 `groupVehicleRequirementStatus=CONFIRMED`(`version=3`)。
- CAS 落空整团回滚:并发导致放行第 3 户车需求时 CAS 落空,抛 `809112`,前 2 户车需求状态、房侧放行结果、正式需求状态、团期确认标记全部回滚,零写入(评论 54939)。
**仅代码核对,未经该场景网关验证**:AC-13/14/21 要求的「同一户同时提交 TRAVEL + TRANSFER 两类需求、`confirm` 一次响应内房 3 户 + 车 5 条同时出现」这一组合场景,因测试环境 TRANSFER 写侧全局开关关闭(既有错误码 809009,`#7443` 派车侧尚未上线的独立开关,与本单代码无关)未能取得;待该开关在测试环境临时打开后补测,结果回写工单 #7441 验收评论。已取得的替代证据:全员仅提交 TRAVEL 需求的团确认成功,`vehicleDispatchedOrderIds` 含 3 户、`transferDispatchedOrderIds=[]`、`vehicleDispatchedCount=3`——证明 TRAVEL 单类路径工作正常,但未覆盖两类需求同户并存、以及打回其中一类后另一类不受影响(`POST .../vehicle-requirement/reject?kind=TRANSFER`)的场景;相关字段的类型与计算口径已按源码核对(见「出参字段表」与「六.6」),这部分示例响应仍是按源码拼装的合理构造,不是该组合场景的实测原文。
---
## 十、相关文档
- 关联 Issue: [wx/HL#7441](https://git.1814.love:8443/wx/HL/issues/7441)
- 关联 PR: [#7778](https://git.1814.love:8443/wx/HL/pulls/7778)(squash 合入 dev-v3,合并提交 `f0277a14f`)
- 前置依赖:`#7535`(首次引入 checkedResourceTypes,本单是其承诺的「扩到用车」那张单)、`#7210`(confirm-check/confirm 首次交付,只管住宿)、`#7441` PR-1/PR-2/PR-2b/PR-2c/PR-3(团期正式车需求声明、整团免车、团车完成回写、结算闸,均已合并 dev-v3,本单依赖它们提供的数据但不改它们的契约)
- 关联文档:本单合并部署后的户级/团级联动效果(确认行程清单、待办、团期详情看板、物资门等)见同批另一份 changelog(`#7441` PR-2d/PR-2e,同一个 PR #7778)
- **验收口径边界**:页面完成状态单独跟踪(前端归 mmg),不计入 #7441 验收;#7441 验收范围 = API 契约 + 状态流转 + 占用账本 + changelog 交接件。
## 关联 / 联系人
### 链接
- **Issue**: [#7441](https://git.1814.love:8443/wx/HL/issues/7441)
- **PR**: [#7778](https://git.1814.love:8443/wx/HL/pulls/7778)
- **Merge commit**: `f0277a14f`
### 联系人
- **后端负责人**: @wx
- **前端负责人(收件人)**: @mmg