docs(order-v3): #7441 团期车务已上线部分 changelog(正式用车需求四接口、确认行程清单房车短路、团车内部回调、核单车侧硬阻断)
changelog-filename-gate / validate (push) Failing after 1s
changelog-filename-gate / validate (push) Failing after 1s
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
这个提交包含在:
@@ -0,0 +1,567 @@
|
||||
---
|
||||
schema: "hl-changelog/v2"
|
||||
ticket: "7441"
|
||||
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: "2026-09-15"
|
||||
status_note: "四个端点的成功路径与 809101-809110 共 10 个错误码均有测试服真实网关调用记录(见第八节,来源 testserver/LEDGER.md 与 pr2ev/ev/*.json)。809100(withdraw 无活跃正式需求)、809114(waive 时车务已开工)、809115(免车态下 PUT 带分组)三个错误码目前只有源码走查、无测试服网关取证记录。809111(GROUP_VEHICLE_REQUIREMENT_STAGE_INVALID)与 809113(GROUP_VEHICLE_WAIVE_HAS_GROUP)在当前实现中均无抛出点:前者保留给 #7442 复用,后者被 2026-09-15 定案 b1 的换版逻辑取代,源码保留常量占号。被测服务:order-v3 = dev-v3 `7d8cecb3e`,2026-09-15 15:38 部署(含 PR-1 ~ PR-3)。"
|
||||
updated_at: "2026-09-15"
|
||||
base: "dev-v3"
|
||||
---
|
||||
|
||||
# order-v3: 团期正式用车需求声明
|
||||
|
||||
> **存放目录**: `changelogs-v2/{YYYY-MM}/`(管理后台,二期 order-v3)
|
||||
>
|
||||
> **服务**: hl-order-service-v3 (端口 8083)
|
||||
> **PR**: #7696(PR-1)、#7709(PR-1b)、#7763(PR-3,换版逻辑调整了本单的 waive 行为)
|
||||
> **Issue**: #7441
|
||||
> **日期**: 2026-09-15
|
||||
> **影响范围**: 团期需求页新增「正式用车需求」编辑区:整份保存(分组 × 逐日 × 成员)、只读回显、整份撤回、声明整团免车,共 4 个新端点;新增错误码 809100-809115(16 个码位,实占 13 个,2 个保留未用,1 个已停用)
|
||||
|
||||
---
|
||||
|
||||
## ⚠️ 关键变化
|
||||
|
||||
1. **团期管理员从此可以在需求页维护一份"正式"团级用车需求**——按乘车分组(如"大巴 A 组")× 逐日 × 成员三层结构整份提交,与定制师逐户所报的《用车需求》是**两份不同的数据**:既有的 `GET .../requirement-summary` 是定制师逐户所报的汇总视图(只读、未改),本单新增的这四个端点维护的是团期管理员按人数形成的正式团级需求,两者须在页面上**并列展示**,不能互相覆盖。
|
||||
2. **新增「本团无需用车」声明按钮**(`waive`),带原因必填;撤销走同一个 `withdraw` 端点,不另开撤销按钮。这是免车状态的**唯一运营可达入口**——此前该状态只能靠手工改库造出。
|
||||
3. **`waive`/`withdraw` 的失败提示三个新码**:`809113`(正式需求仍有分组)在当前实现中**已不会被抛出**(2026-09-15 换版逻辑上线后,带分组也能直接免车,见下文业务边界);`809114`(车务已开工,不能再声明免车)、`809115`(当前处于免车态,提交带分组前须先撤回)仍会触发,人话提示见「六.5 枚举」。
|
||||
|
||||
---
|
||||
|
||||
## 一、背景
|
||||
|
||||
`#7439` 交付了车侧地基(三层表、`requirement_kind` 分流、`GroupVehicleRequirementStatus` 枚举);`#7445` 交付了结算侧的团期用车户级闸门(当时是软预警)。本单在这两者基础上,给团期管理员一个真正可编辑、可审核留痕的"正式团级用车需求"入口,取代此前"逐户所报即定案"的隐式状态。
|
||||
|
||||
---
|
||||
|
||||
## 二、变更接口清单
|
||||
|
||||
| # | 接口 | 方法 | 路径 | 变更类型 | 说明 |
|
||||
|---|------|------|------|----------|------|
|
||||
| 1 | 保存团期正式用车需求 | PUT | `/v3/admin/order/group-batch/{groupBatchId}/vehicle-requirement` | 新增 | 全量替换整份(分组 × 逐日 × 成员),乐观锁版本控制 |
|
||||
| 2 | 读团期正式用车需求 | GET | `/v3/admin/order/group-batch/{groupBatchId}/vehicle-requirement` | 新增 | 只读回显,未形成正式需求时返回 `data: null` |
|
||||
| 3 | 整份撤回正式用车需求 | POST | `/v3/admin/order/group-batch/{groupBatchId}/vehicle-requirement/withdraw` | 新增 | CONFIRMED 或 DISPATCHED 回 DRAFT,已配车辆不动 |
|
||||
| 4 | 声明整团无需用车 | POST | `/v3/admin/order/group-batch/{groupBatchId}/vehicle-requirement/waive` | 新增 | 零分组已确认正式需求,撤销走 `withdraw` |
|
||||
|
||||
---
|
||||
|
||||
## 三、接口详情
|
||||
|
||||
### 1. 保存团期正式用车需求 `PUT /v3/admin/order/group-batch/{groupBatchId}/vehicle-requirement`
|
||||
|
||||
**VO**: `GroupVehicleRequirementSaveReqVO → Result<GroupVehicleRequirementRespVO>`
|
||||
|
||||
#### 使用场景
|
||||
|
||||
团期需求页"正式用车需求"编辑区点保存时调用。**全量替换**语义:本次提交里没出现的分组会被移出当前版本。首次保存 `version` 传 `null`,之后每次提交必须回传上次 `GET`/`PUT` 拿到的 `version` 做乐观锁。
|
||||
|
||||
#### 入参字段表
|
||||
|
||||
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|
||||
|------|------|------|------|------|------|
|
||||
| groupBatchId | Path | Long | 是 | - | 团期聚合主键 |
|
||||
| version | Body | Integer | 否 | 首次传 null | 乐观锁版本号 |
|
||||
| remark | Body | String | 否 | ≤500 字符 | 整份备注 |
|
||||
| groups | Body | List<GroupItem> | 是(可空数组) | `@NotNull`,**不是** `@NotEmpty` | 全部乘车分组;空数组是合法提交(团里没有需车户时) |
|
||||
| groups[].groupId | Body | Long | 否 | 新增分组传 null | 既有分组主键;带上它则 `groupCode` 不得变更 |
|
||||
| groups[].groupCode | Body | String | 是 | ≤32 字符 | 分组键,直接作为车费分摊分组;不可改名 |
|
||||
| groups[].vehicleType | Body | String | 是 | ≤64 字符 | 车型文本/字典值,不设档位枚举 |
|
||||
| groups[].serviceStartDate | Body | LocalDate | 是 | - | 本组服务开始日 |
|
||||
| groups[].serviceEndDate | Body | LocalDate | 是 | 不早于开始日 | 本组服务结束日 |
|
||||
| groups[].days | Body | List<DayItem> | 是 | `@NotEmpty` | 逐日用车明细 |
|
||||
| groups[].days[].tripDate | Body | LocalDate | 是 | 须落在本组服务日范围内 | 团期行程日 |
|
||||
| groups[].days[].headcount | Body | Integer | 是 | `@Min(1)`,须 ≥ 当日成员户数 | 该组该日用车人数(乘车人数,非户数) |
|
||||
| groups[].days[].memberOrderIds | Body | List<Long> | 是 | `@NotEmpty`,须全属本团在团户 | 该组该日实际乘车的子订单集合 |
|
||||
|
||||
#### 出参字段表
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| requirementId | Long(String) | 正式需求主键 |
|
||||
| groupBatchId | Long(String) | 团期聚合主键 |
|
||||
| status | String | 保存后恒为 `DRAFT` |
|
||||
| version | Integer | 保存后版本号,下次提交须回传 |
|
||||
| remark | String | 整份备注 |
|
||||
| confirmedBy | String | 整份确认人(`DRAFT` 时为 null) |
|
||||
| confirmedAt | LocalDateTime | 整份确认时间(`DRAFT` 时为 null) |
|
||||
| groups[] | List<GroupItem> | 全部乘车分组回显(整团免车态为空数组) |
|
||||
| groups[].groupId | Long(String) | 分组主键,下次提交同一组须回传 |
|
||||
| groups[].groupCode/vehicleType/serviceStartDate/serviceEndDate | - | 回显,同入参 |
|
||||
| groups[].days[].tripDate/headcount/memberOrderIds | - | 回显,同入参 |
|
||||
| groups[].days[].memberOrderCount | Integer | 当日成员**户数**(= memberOrderIds.size()),供填 headcount 时对照,避免把户数当人数填 |
|
||||
|
||||
#### 请求示例
|
||||
|
||||
```json
|
||||
{
|
||||
"version": 3,
|
||||
"remark": "9/13 起换大巴",
|
||||
"groups": [
|
||||
{
|
||||
"groupId": null,
|
||||
"groupCode": "A",
|
||||
"vehicleType": "35座大巴",
|
||||
"serviceStartDate": "2026-10-20",
|
||||
"serviceEndDate": "2026-10-24",
|
||||
"days": [
|
||||
{ "tripDate": "2026-10-20", "headcount": 9, "memberOrderIds": [2099463556918386689, 2099463556951941123] }
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
#### 响应示例
|
||||
|
||||
(测试服真实响应,2026-09-14 21:40:18,见 testserver/LEDGER.md G5 AC5-1)
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"message": "成功",
|
||||
"data": {
|
||||
"requirementId": "2099493442663985153",
|
||||
"groupBatchId": "2099463556951941122",
|
||||
"status": "DRAFT",
|
||||
"version": 1,
|
||||
"remark": "G5-AC5-step1-成功",
|
||||
"confirmedBy": null,
|
||||
"confirmedAt": null,
|
||||
"groups": [
|
||||
{
|
||||
"groupId": "2099493442672373761",
|
||||
"groupCode": "A",
|
||||
"vehicleType": "35座大巴",
|
||||
"serviceStartDate": "2026-10-20",
|
||||
"serviceEndDate": "2026-10-24",
|
||||
"days": [
|
||||
{ "tripDate": "2026-10-20", "headcount": 9, "memberOrderIds": ["2099463556918386689"], "memberOrderCount": 3 }
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"success": true
|
||||
}
|
||||
```
|
||||
|
||||
#### 空数据 / 降级响应
|
||||
|
||||
不涉及空态:本端点是写操作,要么整份成功、要么整份零写入并抛错误码。
|
||||
|
||||
```json
|
||||
{ "code": 200, "success": true, "data": { "groups": [] } }
|
||||
```
|
||||
|
||||
#### 错误响应
|
||||
|
||||
| 码 | 符号 | 触发 | 网关取证 |
|
||||
|----|------|------|------|
|
||||
| 589500 | GROUP_BATCH_NOT_FOUND | 团期不存在 | 源码走查 |
|
||||
| 589501 | GROUP_BATCH_STATUS_INVALID | 团期阶段不可编辑 | 源码走查 |
|
||||
| 809101 | GROUP_VEHICLE_REQUIREMENT_STATUS_INVALID | 活跃正式需求不在 DRAFT,且不处于免车确认态 | 有 |
|
||||
| 809102 | GROUP_VEHICLE_REQUIREMENT_VERSION_CONFLICT | 提交版本与当前版本不一致 | 有 |
|
||||
| 809103 | GROUP_VEHICLE_REQUIREMENT_NO_GROUP | 本团存在需要用车的子订单,却零分组 | 有 |
|
||||
| 809104 | GROUP_VEHICLE_GROUP_CODE_INVALID | 分组键重复或试图改名 | 有 |
|
||||
| 809105 | 日期越界 | 逐日行不在本组服务日范围内或重复提交 | 有 |
|
||||
| 809106 | 日期缺失 | 分组缺少某日的用车人数 | 有 |
|
||||
| 809107 | 成员不属本团 | 子订单不属于本团期,不能作为乘车成员 | 有 |
|
||||
| 809108 | 成员重复归属 | 子订单同一日同时属于两个分组 | 有 |
|
||||
| 809109 | 成员未覆盖 | 子订单某日没有被任何乘车分组覆盖 | 有 |
|
||||
| 809110 | 人数不足 | 分组当日用车人数小于当日成员户数 | 有 |
|
||||
| 809115 | GROUP_VEHICLE_WAIVED_GROUP_SAVE_REJECTED | 当前处于免车确认态,提交带分组前须先 withdraw | 源码走查,无测试服网关取证 |
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 809102,
|
||||
"message": "正式用车需求已被他人修改(提交版本 2,当前版本 3),请刷新后重试",
|
||||
"success": false,
|
||||
"data": null
|
||||
}
|
||||
```
|
||||
|
||||
(以上为测试服真实响应,2026-09-14 19:26:29,testserver/LEDGER.md G1 AC3-4-staleR4。)
|
||||
|
||||
#### 业务边界
|
||||
|
||||
- 判权:与整团需求确认/打回同一权限码(`group-batch:demand:confirm`),不另设专属码——四个端点动的是同一个 Tab 的同一份数据。
|
||||
- 幂等:`@Idempotent`(key 为 groupBatchId)挡 5 秒内双击;锁:与 `withdraw`/`waive`/`confirm`/`reject` 共用团级需求锁,同一团期同一时刻只有一个写操作能进。
|
||||
- 顺序不可调换:①团期阶段守卫 → ②免车态守卫(809115)→ ③源状态守卫(809101)→ ④乐观锁比对(809102,此时尚无任何写入)→ ⑤分组改名守卫(809104)→ ⑥六条逐日校验(809103/809105-809110)→ ⑦失活旧版本 → ⑧插新版本。前六步一律零写入。
|
||||
- `groups` 允许空数组:团里没有任何需要用车的户时,一个分组都不提交是合法提交;809103 只在"有在团需车户却零分组"时才抛。整团不需要用车请改用 `waive` 端点,那条路径带确认留痕与可撤回性。
|
||||
- 与既有 `GET .../requirement-summary`(定制师逐户所报,未改)是**两份不同数据**,前端须并列展示,不得混用或互相覆盖。
|
||||
|
||||
---
|
||||
|
||||
### 2. 读团期正式用车需求 `GET /v3/admin/order/group-batch/{groupBatchId}/vehicle-requirement`
|
||||
|
||||
**VO**: `无请求体 → Result<GroupVehicleRequirementRespVO>`
|
||||
|
||||
#### 使用场景
|
||||
|
||||
团期管理员编辑页回填。只读、零副作用,可任意重复调用。
|
||||
|
||||
#### 入参字段表
|
||||
|
||||
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|
||||
|------|------|------|------|------|------|
|
||||
| groupBatchId | Path | Long | 是 | - | 团期聚合主键 |
|
||||
|
||||
#### 出参字段表
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| requirementId | Long(String) | 正式需求主键(同「三、1」出参,结构不重复列出) |
|
||||
| groupBatchId | Long(String) | 团期聚合主键 |
|
||||
| status | String | DRAFT/CONFIRMED/DISPATCHED/DONE/PENDING_RECONFIRM/CANCELLED |
|
||||
| version | Integer | 版本号 |
|
||||
| remark | String | 整份备注 |
|
||||
| confirmedBy/confirmedAt | String/LocalDateTime | 整份确认人/时间 |
|
||||
| groups[] | List<GroupItem> | 全部乘车分组,字段同「三、1」出参表 |
|
||||
| data | GroupVehicleRequirementRespVO | **该团期尚未形成正式需求时整个 data 为 null**(不抛业务错误码)——编辑页首次打开是正常场景 |
|
||||
|
||||
#### 请求示例
|
||||
|
||||
```http
|
||||
GET /v3/admin/order/group-batch/2099463556951941122/vehicle-requirement HTTP/1.1
|
||||
Authorization: Bearer {token}
|
||||
```
|
||||
|
||||
#### 响应示例
|
||||
|
||||
(测试服真实响应,2026-09-14 22:11:15,testserver/LEDGER.md G6 step4-ready-get)
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"message": "成功",
|
||||
"data": {
|
||||
"requirementId": "2099493442663985153",
|
||||
"groupBatchId": "2099463556951941122",
|
||||
"status": "DRAFT",
|
||||
"version": 1,
|
||||
"groups": [
|
||||
{ "groupCode": "A", "vehicleType": "35座大巴", "serviceStartDate": "2026-10-20", "serviceEndDate": "2026-10-24" },
|
||||
{ "groupCode": "B", "vehicleType": "35座大巴", "serviceStartDate": "2026-10-20", "serviceEndDate": "2026-10-22" }
|
||||
]
|
||||
},
|
||||
"success": true
|
||||
}
|
||||
```
|
||||
|
||||
#### 空数据 / 降级响应
|
||||
|
||||
```json
|
||||
{ "code": 200, "message": "成功", "data": null, "success": true }
|
||||
```
|
||||
|
||||
#### 错误响应
|
||||
|
||||
| 码 | 符号 | 触发 | 网关取证 |
|
||||
|----|------|------|------|
|
||||
| 589500 | GROUP_BATCH_NOT_FOUND | 团期不存在 | 源码走查 |
|
||||
|
||||
```json
|
||||
{ "code": 589500, "message": "团期不存在", "success": false, "data": null }
|
||||
```
|
||||
|
||||
#### 业务边界
|
||||
|
||||
- 判权同「三、1」。
|
||||
- 与 `PUT` 响应体形状完全一致,前端可用同一套渲染逻辑处理保存回显与回填。
|
||||
|
||||
---
|
||||
|
||||
### 3. 整份撤回正式用车需求 `POST /v3/admin/order/group-batch/{groupBatchId}/vehicle-requirement/withdraw`
|
||||
|
||||
**VO**: `GroupVehicleRequirementWithdrawReqVO → Result<GroupVehicleRequirementRespVO>`
|
||||
|
||||
#### 使用场景
|
||||
|
||||
团期管理员把已确认(`CONFIRMED`/`DISPATCHED`)的正式需求整份撤回,退回 `DRAFT` 重新编辑。**已配车辆不动**——本端点不调用任何 fleet 释放,也不打回户级完成态。撤销「本团无需用车」声明同样走本端点,不另开撤销按钮。
|
||||
|
||||
#### 入参字段表
|
||||
|
||||
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|
||||
|------|------|------|------|------|------|
|
||||
| groupBatchId | Path | Long | 是 | - | 团期聚合主键 |
|
||||
| reason | Body | String | 是 | ≤200 字符 | 撤回原因,追加写入整份备注留痕(不覆盖原备注) |
|
||||
|
||||
#### 出参字段表
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| requirementId | Long(String) | 正式需求主键 |
|
||||
| groupBatchId | Long(String) | 团期聚合主键 |
|
||||
| status | String | 撤回后恒为 DRAFT |
|
||||
| version | Integer | 已 +1 |
|
||||
| remark | String | 追加了本次撤回留痕 |
|
||||
| confirmedBy/confirmedAt | String/LocalDateTime | 已清空,均为 null |
|
||||
| groups[] | List<GroupItem> | 分组数据保留(撤回不清分组),字段同「三、1」出参表 |
|
||||
|
||||
#### 请求示例
|
||||
|
||||
```json
|
||||
{ "reason": "客户改主意" }
|
||||
```
|
||||
|
||||
#### 响应示例
|
||||
|
||||
(测试服真实响应,2026-09-14 19:30:13,testserver/LEDGER.md G2 AC17-3-withdraw)
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"data": {
|
||||
"requirementId": "2099460678996697090",
|
||||
"groupBatchId": "2099460543147388930",
|
||||
"status": "DRAFT",
|
||||
"version": 2,
|
||||
"remark": "免车(2026-09-14 19:30):纯自驾 | 撤回(2026-09-14 19:30):客户改主意",
|
||||
"confirmedBy": null,
|
||||
"confirmedAt": null,
|
||||
"groups": []
|
||||
},
|
||||
"success": true
|
||||
}
|
||||
```
|
||||
|
||||
#### 空数据 / 降级响应
|
||||
|
||||
不涉及空态;不存在活跃正式需求时抛 809100(见错误响应)。
|
||||
|
||||
```json
|
||||
{ "code": 200, "success": true, "data": { "status": "DRAFT" } }
|
||||
```
|
||||
|
||||
#### 错误响应
|
||||
|
||||
| 码 | 符号 | 触发 | 网关取证 |
|
||||
|----|------|------|------|
|
||||
| 589500 | GROUP_BATCH_NOT_FOUND | 团期不存在 | 源码走查 |
|
||||
| 809100 | GROUP_VEHICLE_REQUIREMENT_NOT_FOUND | 该团期无活跃正式需求 | 源码走查,无测试服网关取证 |
|
||||
| 809101 | GROUP_VEHICLE_REQUIREMENT_STATUS_INVALID | 当前状态不在 `{CONFIRMED, DISPATCHED}`(例如已是 DRAFT) | 有 |
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 809101,
|
||||
"message": "正式用车需求当前状态 DRAFT 不允许本次操作,期望 [DISPATCHED, CONFIRMED]",
|
||||
"success": false,
|
||||
"data": null
|
||||
}
|
||||
```
|
||||
|
||||
(以上为测试服真实响应,2026-09-14 19:30:20,testserver/LEDGER.md G2 AC17-5-withdraw2;连续两次撤回间隔须 ≥5 秒,否则会先撞上 `@Idempotent` 而不是本条业务码。)
|
||||
|
||||
#### 业务边界
|
||||
|
||||
- **不带团期阶段守卫**:撤回是"已经确认过、要改回去"的纠错动作,冻结期也可用——加阶段守卫会让冻结期的团再也退不回来。
|
||||
- `reason` 追加写入整份备注,不覆盖原备注;整段超过 500 字符时从头部截,本次 `reason` 完整保留。
|
||||
- 撤回后正式需求的分组数据仍在,可再次 `PUT` 编辑;不触发户级完成态回退(户级只在 fleet 真的清零释放时才回退,见另一份 PR-2 内部接口分册的 changelog)。
|
||||
|
||||
---
|
||||
|
||||
### 4. 声明整团无需用车 `POST /v3/admin/order/group-batch/{groupBatchId}/vehicle-requirement/waive`
|
||||
|
||||
**VO**: `GroupVehicleRequirementWaiveReqVO → Result<GroupVehicleRequirementRespVO>`
|
||||
|
||||
#### 使用场景
|
||||
|
||||
团期管理员在需求页显式声明"本团整团无需用车"。这是免车状态的**唯一运营可达入口**——改前该状态只能靠手工改库造出。
|
||||
|
||||
#### 入参字段表
|
||||
|
||||
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|
||||
|------|------|------|------|------|------|
|
||||
| groupBatchId | Path | Long | 是 | - | 团期聚合主键 |
|
||||
| reason | Body | String | 是 | ≤200 字符 | 免车原因,追加写入整份备注留痕 |
|
||||
|
||||
#### 出参字段表
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| requirementId | Long(String) | 正式需求主键(无活跃需求时为新建行主键;带分组换版时为新版本主键) |
|
||||
| groupBatchId | Long(String) | 团期聚合主键 |
|
||||
| status | String | 成功后恒为 CONFIRMED |
|
||||
| version | Integer | 已 +1(新建行为 1;换版取历史最大 +1) |
|
||||
| remark | String | 追加了本次免车留痕 |
|
||||
| confirmedBy/confirmedAt | String/LocalDateTime | 已落,均非 null |
|
||||
| groups[] | List<GroupItem> | 恒为空数组 |
|
||||
|
||||
#### 请求示例
|
||||
|
||||
```json
|
||||
{ "reason": "纯自驾团,客户自理交通" }
|
||||
```
|
||||
|
||||
#### 响应示例
|
||||
|
||||
(测试服真实响应,2026-09-14 19:30:06,testserver/LEDGER.md G2 AC17-1-waive)
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"data": {
|
||||
"requirementId": "2099460678996697090",
|
||||
"groupBatchId": "2099460543147388930",
|
||||
"status": "CONFIRMED",
|
||||
"version": 1,
|
||||
"remark": "免车(2026-09-14 19:30):纯自驾",
|
||||
"confirmedBy": "1001",
|
||||
"groups": []
|
||||
},
|
||||
"success": true
|
||||
}
|
||||
```
|
||||
|
||||
#### 空数据 / 降级响应
|
||||
|
||||
已处于免车态时重复调用**幂等成功**(不抛错、不推版本),响应与上次成功响应一致。
|
||||
|
||||
```json
|
||||
{ "code": 200, "success": true, "data": { "status": "CONFIRMED", "groups": [] } }
|
||||
```
|
||||
|
||||
#### 错误响应
|
||||
|
||||
| 码 | 符号 | 触发 | 网关取证 |
|
||||
|----|------|------|------|
|
||||
| 589500 | GROUP_BATCH_NOT_FOUND | 团期不存在 | 源码走查 |
|
||||
| 589501 | GROUP_BATCH_STATUS_INVALID | 团期不在可免车阶段(招募中/已验团/已取消或流团) | 源码走查 |
|
||||
| 809101 | GROUP_VEHICLE_REQUIREMENT_STATUS_INVALID | 带分组的旧活跃版本不在 `{DRAFT, CONFIRMED}`;或 CAS 落空 | 源码走查,无测试服网关取证 |
|
||||
| 809114 | GROUP_VEHICLE_WAIVE_BLOCKED_BY_DISPATCH | 车务已开工:团级配车已就绪,或任一在团户车需求已进入 `PENDING`/`PROCESSING`/`DONE` | 源码走查,无测试服网关取证 |
|
||||
| ~~809113~~ | ~~GROUP_VEHICLE_WAIVE_HAS_GROUP~~ | ~~正式需求仍有分组~~ | **当前实现不会抛出**,见「业务边界」 |
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 809114,
|
||||
"message": "车务已开工(子订单 2098979230573330434:用车需求已到 PENDING),不能再声明整团免车",
|
||||
"success": false,
|
||||
"data": null
|
||||
}
|
||||
```
|
||||
|
||||
(该条为按源码模板拼出的示例,非测试服实测原文——本码本轮取证未覆盖。)
|
||||
|
||||
#### 业务边界
|
||||
|
||||
- **带分组也能直接免车**(2026-09-15 定案 b1,PR-3 生效):旧版本状态为 `DRAFT`/`CONFIRMED` 时,`waive` 会失活旧版本(连同其分组,原样留在历史里可查)、插入一条零分组的新版本并直接置 `CONFIRMED`,版本号取历史最大 +1。旧版本自身若已是 `DISPATCHED`/`DONE`/`PENDING_RECONFIRM`,视为车务已按它发出,`809101` 拒绝失活。
|
||||
- 已处于免车态(零分组且状态 ∈ `{CONFIRMED, DISPATCHED, DONE}`)时重复声明**幂等成功、不推版本**。
|
||||
- 阶段守卫比保存草稿宽:除可确认四态外,另放行 `TRIP_FINISHED`/`REVIEWING`——出团前忘了点免车的团,核单时仍可补点(本轮取证未覆盖该放宽本身,仅覆盖了 `RESOURCE_PREPARING` 阶段的免车)。
|
||||
- `809114` 只挡两类:团级 `vehicle_ready` 已置位、或户级车需求已进 `PENDING` 及之后;已知缺口是 fleet 已建团级派车行但上述两条都未触发时,仍可免车,详见另一份 PR-3 changelog 的「业务边界」。
|
||||
- 撤销免车走 `withdraw`,不另开撤销端点。
|
||||
|
||||
---
|
||||
|
||||
## 四、契约约束与正确调用方式
|
||||
|
||||
### 正确 / 错误 payload 对照
|
||||
|
||||
| 场景 | payload |
|
||||
|------|---------|
|
||||
| 首次保存 | `version: null` |
|
||||
| 后续保存(正确) | `version` 回传上次 GET/PUT 返回值 |
|
||||
| 改名分组(错误理解) | 提交同一 `groupCode` 不同 `groupId` 达不到改名效果,服务端按"新组"处理;带 `groupId` 且 `groupCode` 变了会抛 809104 |
|
||||
| 整团不需要用车(正确) | 调用 `waive` 端点,不是提交 `groups: []` 的 PUT(后者只在"没有在团需车户"时才合法) |
|
||||
|
||||
### 切换状态时的必要动作
|
||||
|
||||
`groupId` 是区分"改名"与"删旧建新"的唯一依据:编辑既有分组必须原样回传其 `groupId`;新增分组必须显式传 `null`,不能省略字段。
|
||||
|
||||
---
|
||||
|
||||
## 五、数据库行为
|
||||
|
||||
`PUT` 全量替换:每次保存都是"失活旧版本 + 插入新版本"(版本号递增),不做增量更新,旧版本连同其分组、逐日行原样保留在历史里可查(历史版本本单没有开放查询端点,仅供后端排障)。`withdraw`/`waive` 同样是原地状态流转(`withdraw`)或换版(`waive` 带分组时),不删除任何历史行。四个端点均不触发跨服务写(不发 Feign/MQ),仅在本服务事务内完成。
|
||||
|
||||
---
|
||||
|
||||
## 六、边界行为
|
||||
|
||||
- 未登录 → 401(网关拦截)
|
||||
- 无权限 → 沿用 `group-batch:demand:confirm` 判权
|
||||
- 团期不存在 → 589500
|
||||
- 并发编辑冲突 → 809102(PUT)
|
||||
- 状态机不匹配 → 809101(四个端点通用码,具体触发条件见各自错误响应表)
|
||||
- 老数据兼容:团期首次打开需求页、从未编辑过车需求时,`GET` 返回 `data: null`,不异常
|
||||
|
||||
---
|
||||
|
||||
## 六.5、枚举
|
||||
|
||||
### 809100-809115(com.hulalv.order.groupbatch.errorcode.GroupVehicleRequirementErrorCode)
|
||||
|
||||
**所属字段**: 各端点错误响应的 `code`/`message` | **类型**: `Integer`/`String`
|
||||
|
||||
| 码 | 符号 | 文案模板 | 抛出端点 | 状态 |
|
||||
|----|------|------|------|------|
|
||||
| 809100 | GROUP_VEHICLE_REQUIREMENT_NOT_FOUND | 团期 {0} 尚未形成正式用车需求 | withdraw | 有抛出点,无网关取证 |
|
||||
| 809101 | GROUP_VEHICLE_REQUIREMENT_STATUS_INVALID | 正式用车需求当前状态 {0} 不允许本次操作,期望 {1} | PUT/withdraw/waive | 有网关取证 |
|
||||
| 809102 | GROUP_VEHICLE_REQUIREMENT_VERSION_CONFLICT | 正式用车需求已被他人修改(提交版本 {0},当前版本 {1}),请刷新后重试 | PUT | 有网关取证 |
|
||||
| 809103 | GROUP_VEHICLE_REQUIREMENT_NO_GROUP | 本团存在需要用车的子订单,至少要提交一个乘车分组 | PUT | 有网关取证 |
|
||||
| 809104 | GROUP_VEHICLE_GROUP_CODE_INVALID | 乘车分组 {0} 重复或试图改名(改名请删除旧组后新增) | PUT | 有网关取证 |
|
||||
| 809105 | 日期越界 | 乘车分组 {0} 的逐日行 {1} 不在本组服务日范围内或重复提交 | PUT | 有网关取证 |
|
||||
| 809106 | 日期缺失 | 乘车分组 {0} 缺少 {1} 的逐日用车人数 | PUT | 有网关取证 |
|
||||
| 809107 | 成员不属本团 | 子订单 {0} 不属于本团期,不能作为乘车成员 | PUT | 有网关取证 |
|
||||
| 809108 | 成员重复归属 | 子订单 {0} 在 {1} 同时属于分组 {2}、{3},同一户同一日只能属于一个分组 | PUT | 有网关取证 |
|
||||
| 809109 | 成员未覆盖 | 子订单 {0} 的 {1} 没有被任何乘车分组覆盖 | PUT | 有网关取证 |
|
||||
| 809110 | 人数不足 | 乘车分组 {0} 在 {1} 的用车人数 {2} 小于当日成员户数 {3} | PUT | 有网关取证 |
|
||||
| 809111 | GROUP_VEHICLE_REQUIREMENT_STAGE_INVALID | 团期当前状态 {0} 不允许编辑或确认正式用车需求 | 无(本组端点均无抛出点,保留给 #7442 复用) | 未启用 |
|
||||
| 809113 | GROUP_VEHICLE_WAIVE_HAS_GROUP | 正式用车需求还有 {0} 个乘车分组,暂不能声明免车(本码已停用) | 无(waive 已改为换版逻辑,不再抛出本码) | 已停用 |
|
||||
| 809114 | GROUP_VEHICLE_WAIVE_BLOCKED_BY_DISPATCH | 车务已开工({0}:{1}),不能再声明整团免车 | waive | 有抛出点,无网关取证 |
|
||||
| 809115 | GROUP_VEHICLE_WAIVED_GROUP_SAVE_REJECTED | 团期 {0} 已声明整团免车,提交乘车分组前请先整份撤回(withdraw)回草稿 | PUT | 有抛出点,无网关取证 |
|
||||
|
||||
(809112 `GROUP_VEHICLE_DISPATCH_CAS_FAILED` 属整团确认端点 `POST .../requirement/confirm` 使用,不属于本单四个端点,见另一份 changelog。)
|
||||
|
||||
---
|
||||
|
||||
## 七、不影响范围
|
||||
|
||||
- **仅影响**: 团期需求页新增的 4 个端点,不改任何既有端点的契约。
|
||||
- **零影响**:
|
||||
- `GET .../requirement-summary`(定制师逐户所报汇总)——只读,未改。
|
||||
- `POST .../requirement/confirm`、`GET .../requirement/confirm-check`——整团确认接入车侧属另一份工作(`#7441` PR-4),本单不涉及。
|
||||
- `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,主要批次在 `61eb5c4b5`(2026-09-14 21:32:56)与 `79e3b9da7`(2026-09-14 21:59:54,PR-1b)两次部署上取证,均已并入本单最终形态 `7d8cecb3e`(2026-09-15 15:38 部署,含 PR-1~PR-3);部署版本核对留痕见工单 #7441 验收评论。
|
||||
|
||||
四个端点的成功路径(`PUT` 保存、`GET` 回显、`withdraw` 撤回、`waive` 免车)均有测试服真实网关调用记录,示例见「三、接口详情」各节。809101-809110 共 10 个错误码均有网关实测(示例分别见对应端点小节,完整记录见 `testserver/LEDGER.md` G1/G3/G5/G6 四批)。
|
||||
|
||||
**本轮未覆盖的分支(如实列出)**:
|
||||
- 809100(withdraw 无活跃正式需求)——源码走查,无网关取证。
|
||||
- 809114(waive 车务已开工)——源码走查,无网关取证;本码的第二条判据(团级配车已就绪)本身还是代偿实现(读 order-v3 侧标志而非 fleet 侧配车行),见「三、4」业务边界。
|
||||
- 809115(免车态下 PUT 带分组)——源码走查,无网关取证。
|
||||
- `waive` 阶段放宽到 `TRIP_FINISHED`/`REVIEWING`——本轮取证只覆盖了 `RESOURCE_PREPARING` 阶段的免车成功路径,未实测扩展到的两个新阶段。
|
||||
|
||||
---
|
||||
|
||||
## 十、相关文档
|
||||
|
||||
- 关联 Issue: [wx/HL#7441](https://git.1814.love:8443/wx/HL/issues/7441)
|
||||
- 关联 PR: #7696(PR-1)、#7709(PR-1b)
|
||||
- 前置依赖:`#7439`(车侧地基三层表与枚举)、`#7445`(结算侧车侧闸门,本单免车声明是它 584131 硬阻断的跳过条件,见另一份 PR-3 changelog)
|
||||
- 相关分册:`#7441` PR-2 内部接口分册(vehicle-ready/vehicle-ready-reset 团车完成回写)、`#7441` PR-2b/2c(确认行程清单 HOTEL_DONE/VEHICLE_DONE 改动)、`#7441` PR-3(finalize 584131 硬阻断)、`#7441` PR-4(整团确认接车侧,另案)
|
||||
|
||||
## 关联 / 联系人
|
||||
|
||||
### 链接
|
||||
|
||||
- **Issue**: [#7441](https://git.1814.love:8443/wx/HL/issues/7441)
|
||||
- **PR**: [#7696](https://git.1814.love:8443/wx/HL/pulls/7696)、[#7709](https://git.1814.love:8443/wx/HL/pulls/7709)
|
||||
|
||||
### 联系人
|
||||
|
||||
- **后端负责人**: @wx
|
||||
|
||||
在新工单中引用
屏蔽一个用户