changelog(#7528): 团期进入待出发七项硬门 + 详情 departureGates 展示 + recheck 端点
changelog-filename-gate / validate (push) Failing after 2s

这个提交包含在:
jw
2026-09-13 19:04:55 +08:00
父节点 0207e50800
当前提交 0686db9ad5
@@ -0,0 +1,291 @@
---
schema: "hl-changelog/v2"
ticket: "7528"
title: "团期进入待出发+出发推进两跳共用七项硬门(系统自动闸)+ 团期详情展示七项硬门 + 手工复判端点"
consumer: "admin"
author: "jw(GIT)"
change_type: "修改接口"
backend_status: "deployed"
gateway_status: "not_required"
frontend_status: "pending"
frontend_owner: "mmg"
frontend_ref: "#7528"
target_release: ""
verified_at: "2026-09-13"
status_note: "团期详情响应新增 departureGates(纯加字段,老前端不读该 key 照常运行);但这是「发车受阻可见性」的主入口,需前端在详情页渲染七项硬门状态(定案16)。另:确认物资 confirm-material 之后不能再假设已跳转待出发,需重新拉详情。零 DDL、零错误码、零网关改动。"
updated_at: "2026-09-13"
base: "dev-v3"
---
# order-v3: 团期进入待出发七项硬门 + 详情硬门展示 + 手工复判端点
**服务**: hl-order-service-v3
**PR**: #7628
**Issue**: #7528
---
## ⚠️ 关键变化
🟡 **行为变化(需前端配合,但非破坏)**:
1. **进入待出发从「零条件自动推进」改为「七项硬门系统自动闸」**。确认物资后不再无条件跳待出发;只有七项硬门全过才推进,任一未过则停在 `MATERIAL_PREPARING`(接口仍 200)。**前端点完确认物资后需重新拉详情看状态。**
2. **团期详情响应新增 `departureGates`**(七项硬门逐项状态,纯加字段)。发车受阻可见性主入口,需前端渲染。仅 `MATERIAL_PREPARING` / `PENDING_DEPARTURE` 返回,其余状态与求值失败均为 `null`。
3. **新增手工复判端点** `recheck-departure-gate`:补齐缺失项后手工触发一次进入待出发复判(幂等)。
七项硬门:①已成团 ②房车导摄四项配齐 ③物资已确认 ④活跃子订单全部已确认 ⑤出行人证件齐全 ⑥合同保险全齐 ⑦主报账人已设。
## 一、背景
团期状态机后半段两跳(进入待出发、出发推进)此前进入待出发是「双门恰好满足即自动推进」、出发推进是「到日子零校验发车」。本单为两跳加同一套七项硬门系统自动闸(GB-ADM-006),两跳复用同一求值器,判据永远一致。受阻不阻断、不报错、不倒退——补齐后由既有触发点或定时扫描自动重试;受阻原因通过详情页 `departureGates` 与时间线 `BATCH_DEPARTURE_BLOCKED` 展示。**无人工勾选字段、无人工提交端点、校验结果不落库。**
## 二、变更接口清单
| # | 接口 | 方法 | 路径 | 变更类型 | 说明 |
|---|------|------|------|----------|------|
| 1 | 团期详情 | GET | `/v3/admin/order/group-batch/:groupBatchId` | 修改 | 出参新增 `departureGates` 数组 |
| 2 | 确认物资 | POST | `/v3/admin/order/group-batch/:groupBatchId/confirm-material` | 修改 | 行为变化:按七项硬门决定是否推进,受阻停留并留痕 |
| 3 | 手工复判进入待出发硬门 | POST | `/v3/admin/order/group-batch/:groupBatchId/recheck-departure-gate` | 新增 | 补齐后手工触发一次复判 |
## 三、接口详情
### 1. 团期详情 `GET /v3/admin/order/group-batch/:groupBatchId`
**VO**: `GroupBatchDetailRespVO`
#### 使用场景
团期详情页展示。除既有字段外,新增七项硬门逐项状态,供运营看清「这个团还差哪几项才能发车」。
#### 入参字段表
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|---|---|---|---|---|---|
| groupBatchId | path | string(Long) | 是 | 雪花 ID | 团期主订单 ID |
#### 出参字段表
(仅列新增字段,既有字段一字未改)
| 字段 | 类型 | 说明 |
|---|---|---|
| departureGates | array\|null | 七项硬门逐项状态,**恒 7 项、顺序固定门①~门⑦**;仅 `MATERIAL_PREPARING`/`PENDING_DEPARTURE` 返回,其余状态或求值失败为 `null` |
| departureGates[].gateCode | string | 门码(FORMED/RESOURCE_READY/MATERIAL_CONFIRMED/SUB_ORDERS_CONFIRMED/TRAVELER_PROFILE/CONTRACT_INSURANCE/PRIMARY_REPORTER) |
| departureGates[].gateName | string | 门中文名,前端直接渲染,与时间线 content 同源 |
| departureGates[].passed | boolean | 是否通过 |
| departureGates[].failReason | string\|null | 未通过原因(通过时 null) |
| departureGates[].blockedOrderId | string\|null | 卡在哪一户(门⑤/⑥ 才有;**字符串形态**,防大整数精度丢失) |
#### 请求示例
```http
GET /v3/admin/order/group-batch/2099073597908647938
Authorization: Bearer <admin token>
```
#### 响应示例
```json
{
"code": 200,
"msg": "成功",
"data": {
"groupBatchId": "2099073597908647938",
"batchStatus": "MATERIAL_PREPARING",
"departureGates": [
{ "gateCode": "FORMED", "gateName": "已成团", "passed": true, "failReason": null, "blockedOrderId": null },
{ "gateCode": "RESOURCE_READY", "gateName": "房车导摄四项配齐", "passed": false, "failReason": "四项资源未配齐:房=未配置 车=已配置 导=已配置 摄=已配置", "blockedOrderId": null },
{ "gateCode": "MATERIAL_CONFIRMED", "gateName": "物资已确认", "passed": true, "failReason": null, "blockedOrderId": null },
{ "gateCode": "SUB_ORDERS_CONFIRMED", "gateName": "活跃子订单全部已确认", "passed": true, "failReason": null, "blockedOrderId": null },
{ "gateCode": "TRAVELER_PROFILE", "gateName": "出行人证件齐全", "passed": true, "failReason": null, "blockedOrderId": null },
{ "gateCode": "CONTRACT_INSURANCE", "gateName": "合同保险全齐", "passed": false, "failReason": "子订单 2099073597711515649 合同状态 GENERATED(需 SIGNED)", "blockedOrderId": "2099073597711515649" },
{ "gateCode": "PRIMARY_REPORTER", "gateName": "主报账人已设", "passed": true, "failReason": null, "blockedOrderId": null }
]
}
}
```
#### 空数据 / 降级响应
- 团期状态不属 `MATERIAL_PREPARING`/`PENDING_DEPARTURE`(招募中、已出行、已结算等):`departureGates` 为 `null`,前端整块不渲染。
- 求值取数失败(软依赖降级):`departureGates` 为 `null`,其余字段照常,后端记 WARN。
#### 错误响应
```json
{ "code": 589500, "msg": "团期不存在", "data": null }
```
(589507=无 group-batch:view 权限;求值器内部异常不转错误码,降级为 departureGates=null)
#### 业务边界
- `departureGates` 是**纯展示**、无动作按钮;补齐缺失项由系统自动重试。
- 门②的 `passed` = 既有 `hotelReady`/`vehicleReady`/`guideReady`/`photographerReady` 四布尔 AND;既有四字段与 `materialConfirmed` 原样保留、值不变。
- `blockedOrderId` 是字符串形态(`ToStringSerializer`),前端勿转 number。
### 2. 确认物资 `POST /v3/admin/order/group-batch/:groupBatchId/confirm-material`
**VO**: `Result<Void>`
#### 使用场景
运营确认团期物资清单,确认后系统尝试按七项硬门推进到待出发。
#### 入参字段表
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|---|---|---|---|---|---|
| groupBatchId | path | string(Long) | 是 | 雪花 ID | 团期主订单 ID |
#### 出参字段表
| 字段 | 类型 | 说明 |
|---|---|---|
| data | null | 返回 200 表示物资确认成功;**是否推进到待出发由七项硬门决定,需前端重新拉详情确认状态** |
#### 请求示例
```http
POST /v3/admin/order/group-batch/2099073597908647938/confirm-material
Authorization: Bearer <admin token>
```
#### 响应示例
```json
{ "code": 200, "msg": "成功", "data": null }
```
#### 空数据 / 降级响应
受阻时接口仍返 200(物资确认本身成功),团期停在 `MATERIAL_PREPARING`,时间线新增一条 `BATCH_DEPARTURE_BLOCKED`(`changeType=DATA`,content 为未通过门中文名逗号串,如「房车导摄四项配齐,合同保险全齐」)。
#### 错误响应
```json
{ "code": 589500, "msg": "团期不存在", "data": null }
```
(589507=无 group-batch:manage 权限;状态非 MATERIAL_PREPARING 等既有校验口径一行未改)
#### 业务边界
- **行为变化**:改前双门满足即推进;改后重判七项硬门,全过才跳 `PENDING_DEPARTURE`,否则停留并留痕。
- 七项不短路——一次收齐全部未通过项写进同一条留痕。
- 受阻不抛业务码、零新增错误码。
### 3. 手工复判进入待出发硬门 `POST /v3/admin/order/group-batch/:groupBatchId/recheck-departure-gate`
**VO**: `GroupBatchDepartureGateRecheckRespVO`
#### 使用场景
补齐缺失项(设主报账人、出齐合同保险等)后,运营手工触发一次进入待出发复判,不必等定时扫描。
#### 入参字段表
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|---|---|---|---|---|---|
| groupBatchId | path | string(Long) | 是 | 雪花 ID | 团期主订单 ID |
#### 出参字段表
| 字段 | 类型 | 说明 |
|---|---|---|
| advanced | boolean | 是否本次推进到待出发 |
| notApplicable | boolean | 状态不是 MATERIAL_PREPARING 等不适用场景(不报错) |
| gates | array\|null | 受阻时逐项返回未通过原因(结构同 departureGates) |
#### 请求示例
```http
POST /v3/admin/order/group-batch/2099073597908647938/recheck-departure-gate
Authorization: Bearer <admin token>
```
#### 响应示例
```json
{ "code": 200, "msg": "成功", "data": { "advanced": true, "notApplicable": false, "gates": null } }
```
#### 空数据 / 降级响应
不可复判(状态已变、七门未全过)时返回 `advanced=false`(或 `notApplicable=true`),**不抛业务异常**,故本端点零新增错误码。
#### 错误响应
```json
{ "code": 589500, "msg": "团期不存在", "data": null }
```
(589507=无 group-batch:manage 权限)
#### 业务边界
- 幂等:重复调用第二次因状态已变返 `notApplicable`。
- 与确认物资走同一套七项硬门求值器,判据一致。
## 四、契约约束与正确调用方式
- `departureGates[].blockedOrderId` 是字符串,勿按 number 解析。
- 确认物资返回 200 **不代表已进入待出发**——必须重新拉详情看 `batchStatus` 与 `departureGates`。
- 七项门中文名以后端 `gateName` 为准,前端直接渲染,勿在前端另写一份字面量。
## 五、数据库行为
- 唯一持久化相关改动:团期时间线 `group_batch_status_log.event_type` 新增枚举值 `BATCH_DEPARTURE_BLOCKED`(该列为 VARCHAR(64),**零 DDL**)。
- 无新表、无新列、无新索引、无 Flyway 迁移。
## 六、边界行为
- 出发推进跳(定时任务 1041)受阻:按指纹去重后写一条 `BATCH_DEPARTURE_BLOCKED`(同团同组未通过项最多一条,未通过项变化才追加),团期停在 `PENDING_DEPARTURE` 不倒退。
- 出行完毕跳(1042)不设门,行为一字未改。
- 进入待出发后某项被打回(房务打回 hotel_ready、主报账人改 NONE、合同重开):团期主状态**有意不倒退**,但出发跳会被拦下——运营需看详情页 `departureGates` 与时间线补齐。
## 六.6、修改前后对比
| 维度 | 改前 | 改后 |
|---|---|---|
| 进入待出发 | 确认物资时「双门恰好满足即自动推进」 | 确认物资后重判**七项硬门**,全过才推进,受阻停留 `MATERIAL_PREPARING` 并留痕 |
| 出发推进(1041) | 到日子零校验发车 | 到日子重判**同一套七项硬门**,受阻跳过该团(去重留痕),全过才发车 |
| 团期详情 `GroupBatchDetailRespVO` | 无 `departureGates` | 新增 `departureGates`(7 项,仅两个状态返回,否则 null) |
| 端点数 | — | 新增 `recheck-departure-gate` 1 个 |
| 确认物资响应 | 200 即已推进 | 200 仅表示物资确认成功,是否推进另看详情 |
| 错误码 / DDL / 网关 | — | **零新增、零改动** |
## 六.7、影响评估
- **兼容性**:详情响应体**纯增** `departureGates`,未消费该 key 的前端页面无需改动即可继续工作。
- **需要前端动的**:
1. 团期详情页渲染 `departureGates` 七项硬门状态(发车受阻可见性主入口,定案16,不渲染等于没落地);
2. 确认物资后改为**重新拉详情**判断状态,不再假设已跳待出发;
3. `blockedOrderId` 按字符串处理。
- **性能**:详情多一次「一次投影 + N 次出行人计数 + 复用已取的主报账人」,仅对 `MATERIAL_PREPARING`/`PENDING_DEPARTURE` 两状态求值;团内活跃子订单个位到几十,成本可接受,软依赖失败降级为 null。
- **存量风险(预期行为)**:存量团被新门卡住不豁免(定案10),处置是补齐缺失项(补合同保险/证件/主报账人/回补四项 ready),非开旁路。
## 七、不影响范围
- 既有字段(含四项 ready、materialConfirmed)名称/类型/取值/null 语义一字未改。
- 无网关路由改动、无 Feign/MQ 改动、无错误码新增。
- 出行完毕推进(1042)、其它团期端点行为不变。
## 八、测试环境已验证
2026-09-13 TEST 网关 + 内部端口 + 真 MySQL IT 三路取证,工单 #7528 的 AC-1~26 + AC-R1~R3 共 29 条全部逐条通过:
- 网关实测(自签 admin token):confirm-material 七门逐项拦截/放行、详情 departureGates 七项含字符串 blockedOrderId、TRAVELLING/RECRUITING → null。
- 内部端口(X-Internal-Token 直连 8086):1041 准入轮兜底推进、出发跳拦截留痕 + 指纹去重(3 次→1 条、换因→+1 条)、放行 TRAVELLING、一团受阻不影响其余、出行完毕跳不设门。
- 真 MySQL IT:`REQUIRES_NEW` 事务语义(外层回滚后写入存活)。
- 全量:order-v3 单测 10574 test / 0 failure / 0 error。
## 十、相关文档
- 工单 #7528(GB-ADM-006)
- 全过程记录:HL 仓 `dev-records/records/2026-09-13-local-7528-departure-gates.md`
## 关联 / 联系人
- 后端:jw(已部署 dev-v3 + TEST 实测)
- 前端:mmg(需在团期详情页渲染 departureGates 七项硬门状态;确认物资后改为重新拉详情)