docs(changelog): #7456 团期生命周期后半段——核团开工自动推进 + 验团归档 + 反确认
changelog-filename-gate / validate (push) Successful in 2s

这个提交包含在:
jw
2026-09-10 18:10:23 +08:00
父节点 6fee8114b8
当前提交 6c62b1aa2b
@@ -0,0 +1,348 @@
---
schema: "hl-changelog/v2"
ticket: "7456"
title: "团期生命周期后半段打通:核团开工自动推进 + 验团归档 + 验团反确认"
consumer: "admin"
author: "jw(GIT)"
change_type: "修改接口"
backend_status: "deployed"
gateway_status: "verified"
frontend_status: "pending"
frontend_owner: "mmg"
frontend_ref: ""
target_release: ""
verified_at: "2026-09-10"
status_note: "团期生命周期此前在「出行完毕」之后就断了——核团(REVIEWING)与验团(SETTLED)两个状态是死枚举,全 order-v3 没有任何代码能推进到它们,看板的「核团中」「已验团」两个桶恒为 0,团期共享成本录入端点因状态门写死 REVIEWING 而根本调不通。本次照订单核单范式补上两跳:① 首次录团期共享成本时把团期从「出行完毕」自动推进到「核团中」(不靠定时任务、不靠日期,与订单侧 advanceSettlementReviewing 同构);② 新增验团归档端点把「核团中」推到「已验团」终态;③ 新增验团反确认端点,验错了能退回核团中。前端三条必看:① 录共享成本这个既有端点现在有了状态副作用,调用后请重取团期详情刷新状态;② 验团与反确认走财务口径(超管/管理员/财务),与订单核单的 finalize/confirm 同一套角色判据;③ 已验团是终态,流团、改容量、录成本都会被既有状态门挡住。⚠️ 已知缺口:验团前置目前只校验到「状态是 REVIEWING」,原型要求的「核团提交」依赖尚未建的核团四表(GB-ADM-050~052),在那之前验团可能在成本没录完时被点掉,反确认端点兜住一部分。无表变更、无 Flyway,新增错误码 589555。"
updated_at: "2026-09-10"
base: "dev-v3"
---
# 团期生命周期后半段:核团开工 + 验团归档 + 反确认
## 一、给前端的一句话
团期以前走到「出行完毕」就**卡死了**——核团、验团两个状态在代码里推不进去,看板那两个桶永远是 0。
现在通了:**首次录团期共享成本**会自动把团期推进到「核团中」,**新增的验团归档端点**把它推到「已验团」终态,
验错了还有**反确认端点**能退回去。
## 二、变更接口清单
| # | 接口 | 方法 | 路径 | 变更类型 | 说明 |
|---|---|---|---|---|---|
| 1 | 团期验团归档 | POST | `/v3/admin/order/group-batch/:groupBatchId/settle` | 新增 | 核团中 → 已验团(终态) |
| 2 | 团期验团反确认 | POST | `/v3/admin/order/group-batch/:groupBatchId/settle/reopen` | 新增 | 已验团 → 核团中,给验错了留退路 |
| 3 | 录入团期共享成本 | POST | `/v3/admin/order/group-batch/:groupBatchId/settlement/cost` | 修改 | **新增状态副作用**:团期处于「出行完毕」时,首次录入会先把它推进到「核团中」 |
## 三、接口详情
### 1. 团期验团归档 `POST /v3/admin/order/group-batch/:groupBatchId/settle`
**VO**: `无请求体,返回 Result<Void>`
#### 使用场景
核团完成后,团期管理员或财务在团期详情页点「验团归档」,团期进入终态。
对应原型状态生命周期表的阶段六:责任人「团期管理员 / 财务」,期间「整团验团复核,确认无误,归档结束」,退出「— 终态」。
#### 入参
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|---|---|---|---|---|---|
| `groupBatchId` | path | Long | 是 | 雪花 ID,按字符串传 | 团期 ID |
无 body。
#### 出参
`Result<Void>`。
| 字段 | 类型 | 说明 |
|---|---|---|
| `data` | null | 无返回体;状态变化请重取团期详情 |
#### 请求示例
```http
POST /v3/admin/order/group-batch/2089713777065832450/settle
Authorization: Bearer <admin token>
```
#### 响应示例
```json
{ "code": 200, "message": "成功", "data": null, "success": true }
```
#### 空数据 / 降级响应
写口,无空数据形态。团期不存在报 `589500`。
**验团时间不落新列**——`order_group_batch` 没有 `settled_at`(那是 `order_main` 的列),
验团时间从 `GET .../status-logs` 里 `BATCH_SETTLE`「验团结算」那条的 `changedAt` 读,`#7304` 已开了这个读端点。
#### 错误响应
团期已验团归档,重复调用:
```json
{ "code": 589555, "message": "该团期已验团归档,不可重复验团", "data": null, "success": false }
```
团期状态不是「核团中」:
```json
{ "code": 589501, "message": "团期状态不允许当前操作", "data": null, "success": false }
```
无权限(非超管 / 管理员 / 财务):
```json
{ "code": 589507, "message": "无操作权限(非团期管理员 / 非本定制师名下)", "data": null, "success": false }
```
#### 业务边界
- 前置:团期状态必须是 `REVIEWING`(核团中),CAS 推进,并发下只有一个请求成功。
- 同事务写一条 `group_batch_status_log`,事件类型 `BATCH_SETTLE`「验团结算」,`REVIEWING → SETTLED`。
- **已验团是终态**:流团报 `589544`、改容量报 `589538`、录成本报 `589501`——都是既有状态门,本次一行未补。
- ⚠️ **已知缺口**:前置目前只校验「状态是 `REVIEWING`」。原型要求的「核团提交」依赖尚未建的核团四表
(GB-ADM-050~052),在那之前**验团可能在成本没录完时被点掉**,靠下面的反确认端点兜。
- 判权借用订单核单 `SettlementWriteGuard` 的角色判据(超管 / 管理员 / 财务),错误码用团期域的 `589507`。
### 2. 团期验团反确认 `POST /v3/admin/order/group-batch/:groupBatchId/settle/reopen`
**VO**: `无请求体,返回 Result<Void>`
#### 使用场景
验团点早了、或归档后发现成本有误,把团期退回「核团中」继续处理。
形状照订单核单的 `final-snapshots/reopen`——那边验错了有退路,团期这边不该没有。
#### 入参
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|---|---|---|---|---|---|
| `groupBatchId` | path | Long | 是 | 雪花 ID,按字符串传 | 团期 ID |
无 body。
#### 出参
`Result<Void>`。
| 字段 | 类型 | 说明 |
|---|---|---|
| `data` | null | 无返回体;状态变化请重取团期详情 |
#### 请求示例
```http
POST /v3/admin/order/group-batch/2089713777065832450/settle/reopen
Authorization: Bearer <admin token>
```
#### 响应示例
```json
{ "code": 200, "message": "成功", "data": null, "success": true }
```
#### 空数据 / 降级响应
写口,无空数据形态。团期不存在报 `589500`。
#### 错误响应
团期不是「已验团」状态(比如已经被别人退回过):
```json
{ "code": 589501, "message": "团期状态不允许当前操作", "data": null, "success": false }
```
无权限:
```json
{ "code": 589507, "message": "无操作权限(非团期管理员 / 非本定制师名下)", "data": null, "success": false }
```
#### 业务边界
- 前置:团期状态必须是 `SETTLED`,CAS 推进,并发下只有一个请求成功。
- 同事务写 `group_batch_status_log`,`SETTLED → REVIEWING`。
- 退回后可继续录共享成本、可再次验团(TEST 已实测走通一整圈)。
- 判权与验团归档同一套角色判据。
### 3. 录入团期共享成本 `POST /v3/admin/order/group-batch/:groupBatchId/settlement/cost`
**VO**: `RecordBatchCostReqVO`
#### 使用场景
核团阶段录整团共享成本(大巴 / 领队 / 摄影 / 其它),后续按户分摊。
**这个端点本身不是新的,本次变的是它的状态副作用**——在此之前,团期根本进不到 `REVIEWING`,
而这个端点的状态门写死「仅 `REVIEWING` 允许」,所以**它实际上一直调不通**。
#### 入参
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|---|---|---|---|---|---|
| `groupBatchId` | path | Long | 是 | 雪花 ID | 团期 ID |
| `costType` | body | String(枚举) | 是 | `BUS` / `LEADER` / `PHOTOGRAPHER` / `OTHER` | 成本分类 |
| `amount` | body | BigDecimal | 是 | — | 金额 |
| `source` | body | String | 是 | — | 来源 |
| `remark` | body | String | 否 | — | 备注 |
#### 出参
`Result<Long>`。
| 字段 | 类型 | 说明 |
|---|---|---|
| `data` | String | 成本行 ID(雪花,按字符串下发) |
#### 请求示例
```http
POST /v3/admin/order/group-batch/2089713777065832450/settlement/cost
Authorization: Bearer <admin token>
Content-Type: application/json
{"costType":"BUS","amount":1200.00,"source":"MANUAL","remark":"大巴费用"}
```
#### 响应示例
```json
{ "code": 200, "message": "成功", "data": "2097990074078580737", "success": true }
```
#### 空数据 / 降级响应
写口,无空数据形态。**入参、出参、既有错误码本次一律未变**,变的只是状态副作用。
#### 错误响应
团期既不是「出行完毕」也不是「核团中」:
```json
{ "code": 589501, "message": "团期状态不允许当前操作", "data": null, "success": false }
```
无权限(该端点走 `group-batch:finance:advance`,#7411 已接):
```json
{ "code": 589507, "message": "无操作权限(非团期管理员 / 非本定制师名下)", "data": null, "success": false }
```
#### 业务边界
- **新增副作用**:团期处于 `TRIP_FINISHED`(出行完毕)时,本端点会**先把它 CAS 推进到 `REVIEWING`**(核团中),
再执行原有的成本录入。同事务写 `BATCH_TRIP_END`「返团核团」时间线。
- 团期已在 `REVIEWING` 时不重复推进、不重复写时间线(幂等)。
- **推进不靠定时任务、不靠日期**——与订单侧「第一次录核单明细自动把订单从待核单转成核单中」逐字同构。
- ⚠️ **前端请注意**:调用成功后团期状态可能已经变了,**请重取团期详情刷新状态与按钮可用性**。
- 已验团(`SETTLED`)的团期调本端点报 `589501`,不会被带回核团中(有单测钉住)。
## 四、契约约束与正确调用方式
1. **录共享成本之后要重取团期详情**。它现在有状态副作用,页面上的阶段条、按钮可用性都会变。
2. **验团 / 反确认走财务口径**:超管 / 管理员 / 财务,与订单核单的 `finalize` / `confirm` 同一套角色判据。
定制师、房务、车务等角色会拿到 `589507`。
3. **验团时间不要找 `settled_at` 字段**——团期表没有这一列。从 `GET .../status-logs` 里
`BATCH_SETTLE` 那条的 `changedAt` 读。
4. **已验团是终态**:流团 `589544`、改容量 `589538`、录成本 `589501`。界面上这些按钮应当置灰。
5. **`589555` 是「已验团归档,不可重复验团」**,与 `589501`「状态不允许」分开——前者告诉用户「已经做过了」,
后者告诉用户「现在这个阶段做不了」,提示文案建议区分。
6. **雪花 ID 一律按字符串处理**。
## 五、数据库行为
- **无表变更、无 Flyway、无数据迁移**——验团时间复用 `group_batch_status_log`,不加 `settled_at` 列。
- 两次状态推进都是 **CAS**(`TRIP_FINISHED → REVIEWING`、`REVIEWING → SETTLED`、`SETTLED → REVIEWING`),
并发下只有一个请求成功,重复调用不重复写流水。
- 新增错误码 **589555**(团期段 589500-589599,此前占用到 589554)。
## 六、边界行为
| 场景 | 行为 |
|---|---|
| 团期在「出行完毕」,首次录共享成本 | 先推进到「核团中」+ 写 `BATCH_TRIP_END`,再录成本 |
| 团期已在「核团中」,再录成本 | 不重复推进、不重复写时间线,正常录入 |
| 团期在「已验团」,录成本 | `589501`,**不会被带回核团中** |
| 验团时团期不是「核团中」 | `589501` |
| 重复验团 | `589555` |
| 反确认时团期不是「已验团」 | `589501` |
| 反确认后 | 回到「核团中」,可继续录成本、可再次验团 |
| 已验团后流团 / 改容量 | `589544` / `589538`(既有状态门,本次未补) |
| 无权限调验团 / 反确认 | `589507` |
| 并发验团 | CAS 保证只有一个成功,另一个拿 `589555` 或 `589501` |
## 六.6、修改前后对比
| 项 | 改前 | 改后 |
|---|---|---|
| `TRIP_FINISHED → REVIEWING` | **没有任何代码能推进**(全 order-v3 对 `GroupBatchStatus.REVIEWING` 的命中只有读判据) | 首次录共享成本时 CAS 推进 |
| `REVIEWING → SETTLED` | **没有任何代码能推进** | 新增验团归档端点 |
| `SETTLED → REVIEWING` | 无 | 新增反确认端点 |
| 录共享成本端点 | 状态门写死 `REVIEWING`,而团期进不到该状态 → **实际调不通** | 可从「出行完毕」直接调,端点自己把状态带过去 |
| 看板「核团中」「已验团」桶 | **恒为 0** | 能真正出数 |
| 时间线 `BATCH_TRIP_END` / `BATCH_SETTLE` | 枚举定义了但**零引用** | 两跳各写一条 |
## 六.7、影响评估
**新增的两个端点**对现有调用方零影响——没有人在调它们。
**录共享成本端点的副作用是增量的**:改前团期不可能处于 `TRIP_FINISHED` 还能调通这个端点(状态门直接拒),
所以**不存在「改前能调通、改后行为变了」的调用方**。改后只是多了一条此前走不到的成功路径。
**权限**:验团 / 反确认走超管 / 管理员 / 财务。这与原型状态生命周期表里验团责任人「团期管理员 / 财务」完全对上。
没有选 `group-batch:manage` 是因为该码**没有授予 FINANCE**,选它会把财务挡在验团之外。
**运营侧可见变化**:团期看板的「核团中」「已验团」两个桶从此会有数据;此前这两个桶恒空,运营看不到任何在核或已归档的团。
## 七、不影响范围
- 不改订单侧核单(`settlement` 域)的任何端点与语义。
- 不改团期前半段(招募 / 成团 / 资源 / 物资 / 待出发 / 出行中)的任何推进逻辑。
- 不做核团四表、主 / 次报账人记账、成本平均分摊到各子订单(GB-ADM-050~052)——本单只做状态推进骨架。
- 无表变更、无 Flyway、无网关路由新增。
- 只滚 `hl-order-service-v3` 一个服务。
## 八、测试环境已验证
TEST(`api.test.1814.love`)真实网关,团期 `2089713777065832450` 走完整圈(2026-09-10)。
造数:把该团期状态临时置为 `TRIP_FINISHED`(0 活跃户,不影响其它验收),**验完已全部回滚**
(状态复原 `RESOURCE_PREPARING`、造的成本行与 4 条时间线均已删除,复核残留为 0)。
| 步 | 实测 |
|---|---|
| ① 起始状态 | `TRIP_FINISHED` |
| ② 录共享成本 | `200`,返回成本行 ID |
| ③ **录后状态自动变成 `REVIEWING`** | ✅ 时间线 `BATCH_TRIP_END`「返团核团」`TRIP_FINISHED → REVIEWING` |
| ④ 验团归档 | `200`;状态 → `SETTLED`;时间线 `BATCH_SETTLE`「验团结算」`REVIEWING → SETTLED` |
| ⑤ 重复验团 | `589555`「该团期已验团归档,不可重复验团」 |
| ⑥ 终态约束:录成本 | `589501` |
| ⑦ 终态约束:流团 | `589544`「团期已出行,不可发起或批复流团」 |
| ⑧ 终态约束:改容量 | `589538`「物料准备开始后不可再调整满团名额」 |
| ⑨ 判权:`CUSTOMIZER` 调反确认 | `589507` |
| ⑩ 判权:`FINANCE` 调反确认 | `200` —— 财务口径生效,与原型「团期管理员 / 财务」对上 |
| ⑪ 反确认后状态 | `REVIEWING`;时间线 `BATCH_SETTLE` `SETTLED → REVIEWING` |
| ⑫ 反确认后可再次验团 | `200`,回到 `SETTLED` |
完整链路 `TRIP_FINISHED → REVIEWING → SETTLED → REVIEWING → SETTLED` **全通**。
**本机全量**:`mvn -o -pl hl-order-service-v3 test` → **Tests run: 9654, Failures: 0, Errors: 0, Skipped: 7, BUILD SUCCESS**。
新增单测 `GroupBatchSettleServiceTest` 10 例 + `GroupBatchSettlementServiceTest` 补 5 例。
## 十、相关文档
- 工单:`#7456`(本单)
- 原型:团期详情 → 状态生命周期 Tab(阶段六「核团」「验团」两行)
- 范式来源:订单核单 `settlement` 域的 `advanceSettlementReviewing` / `finalize` / `confirm` / `reopenFinalSnapshot`
- 前序:`#7190`(出行段两跳 `PENDING_DEPARTURE → TRAVELLING → TRIP_FINISHED`,本单接着它往下做)、`#7304`(时间线读端点)
- 后续:GB-ADM-050~052 核团四表落地后,验团前置要补上「核团已提交」这一条(代码里已留 `TODO` 锚点)
## 关联 / 联系人
- 工单:https://git.1814.love:8443/wx/HL/issues/7456
- PR:https://git.1814.love:8443/wx/HL/pulls/7485
- 后端:jw;前端:mmg