文件
hl-api-changelog/changelogs-v2/2026-09/06_7190_团期状态新增出行完毕TRIP_FINISHED看板八桶-修改接口-管理后台.md
T
Mimingguang d2ed4973f7
changelog-filename-gate / validate (push) Failing after 2s
chore(changelog): 回写批次A(#7188/#7189/#7190/#7250)与 #7291 前端交付验证
五条均 frontend_status verified、owner mmg、verified_at 2026-09-08;批次A 四项 frontend_ref=2eb27845,#7291 frontend_ref=41f0bacc(均 hl-admin v2.1 可达)。
2026-09-08 08:02:04 +08:00

459 行
20 KiB
Markdown
原始文件 Blame 文件历史

此文件含有模棱两可的 Unicode 字符
此文件含有可能会与其他字符混淆的 Unicode 字符。 如果您是想特意这样的,可以安全地忽略该警告。 使用 Escape 按钮显示他们。
---
schema: "hl-changelog/v2"
ticket: "7190"
title: "团期状态新增「出行完毕」TRIP_FINISHED,看板七桶扩八桶"
consumer: "admin"
author: "wx(GIT)"
change_type: "修改接口"
backend_status: "deployed"
gateway_status: "verified"
frontend_status: "verified"
frontend_owner: "mmg"
frontend_ref: "2eb27845"
target_release: ""
verified_at: "2026-09-08"
updated_at: "2026-09-08"
base: "dev-v3"
status_note: "前端已交付并验证(commit 2eb27845, 汇总工单项3):batchLifecycle OPS_STAGES/OPS_STAT_TABS/BATCH_STATUS_TO_OPS 三处插 TRIP_FINISHED「出行完毕」单射,位置在出行中与待审核之间,统计条按键名读 buckets.TRIP_FINISHED;batchLifecycle.spec 硬断言扩八键顺序+TRIP_FINISHED 单射。"
---
# 团期模块:状态九态(新增「出行完毕」)与看板八桶
## ⚠️ 关键变化
- **团期状态 8 → 9 态**:新增 `TRIP_FINISHED`「出行完毕」,位于 `TRAVELLING`「出行中」之后、`REVIEWING`「核单中」之前。九态与中文名:`RECRUITING` 招募中 / `RESOURCE_PREPARING` 资源准备中 / `MATERIAL_PREPARING` 物料准备中 / `PENDING_DEPARTURE` 待出发 / `TRAVELLING` 出行中 / **`TRIP_FINISHED` 出行完毕** / `REVIEWING` 核单中 / `SETTLED` 已结算 / `CANCELLED` 已取消。
- **看板运营阶段 7 桶 → 8 桶**:新增桶 `TRIP_FINISHED`「出行完毕」,位于 `TRAVELLING` 之后、`AUDITING` 之前。八桶顺序:`RECRUIT, FORMED, PENDING_TRIP, TRAVELLING, TRIP_FINISHED, AUDITING, CHECKED, DISBANDED`。GB-ADM-009 统计条 `buckets` 固定键由 7 个变 8 个;GB-ADM-001 / GB-ADM-008 的 `opsStage` 多一个可选值。
- **推进由两个定时任务按日自动完成**(出发日 ≤ 今天:待出发 → 出行中;返团日 < 今天:出行中 → 出行完毕;日期取 product 侧实时班期日期,不取团期快照)。**上线时两个 sys_job 为 PAUSED,「出行完毕 → 核单中」发起核单入口落地前不会 resume**,所以近期页面上不会自然出现该态,但契约已变,前端要先把桶/页签/映射补齐,否则出现时页签少一个、桶键被忽略。
- 联动:出行完毕团期的房/车/导/摄芯片恒 `DONE`(与已返团同硬规则);对其子订单退团被拒 `589501`;预支入口在该态仍开放;转期/调容量/需求提报按既有规则在该态均拒。
---
## 一、背景
### 现象
wx 2026-09-06 要求看板状态页签在「出行中」后增加「出行完毕」。排查发现团期八态的后半段(待出发 → 出行中 → 核单中 → 已结算)此前没有任何代码推进(订单级有按日期推进的 job,团期级没有),测试库全部团期都停在已成团。本单新增正式状态并补齐团期级按日推进。
### 调用链
1. hl-user-service sys_job(Quartz)`groupBatchLifecycleJob.processDepartures() / processTripFinishes()` → 桥接 bean 经 Feign → order-v3 `POST /v3/internal/jobs/group-batch-departure/run`、`/group-batch-trip-finish/run`(内部端点,网关不开放)
2. order-v3 `GroupBatchLifecycleJobService`:取候选(`PENDING_DEPARTURE` / `TRAVELLING`)→ 按产品经 Feign 取 product 实时班期日期 → `GroupBatchLifecycleTxService.advanceOne` 同一事务 CAS 改状态 + 写时间线(`BATCH_DEPART`「系统自动发团」/ 新事件 `BATCH_TRIP_FINISH`「系统自动出行完毕」)
3. 看板读侧:`GroupBatchStageBuckets` 九态折叠八桶 → GB-ADM-001 `records[].batchStatus / batchStatusName`、GB-ADM-009 `buckets`、GB-ADM-008 导出 `opsStage`
### 地面真相(测试服 dev-v3,2026-09-06 21:38-21:42,团期房务会话临时造数)
| 团期 | 造数 | 触发 job 后 | 说明 |
|---|---|---|---|
| A `2096495107078328322`(#7158验收班期) | 状态 → PENDING_DEPARTURE;product 侧出发日 12-20 → 09-05;order 快照出发日仍 12-20 | 第一轮(出发日 12-20,未来)不动;第二轮(09-05)→ **TRAVELLING** | 读 product 实时日期,快照未动 |
| B `2096510069465088002`(#7178验收班期) | 状态 → TRAVELLING;product 侧返团日 12-28 → 09-05;order 快照返团日仍 12-27 | 第一轮不动;第二轮 → **TRIP_FINISHED**「出行完毕」 | 同上 |
两轮 sys_job 1041 / 1042 执行日志均 SUCCESS(毫秒级)。造数已由团期房务会话复原。
---
## 二、变更接口清单
| # | 接口 | 方法 | 路径 | 变更类型 | 说明 |
|---|------|------|------|----------|------|
| 1 | 团期看板分页(GB-ADM-001) | GET | `/v3/admin/order/group-batch` | 入参枚举扩展 + 响应取值扩展 | `opsStage` / `batchStatus` 新增 `TRIP_FINISHED`;`records[].batchStatus` 可为 `TRIP_FINISHED`、`batchStatusName="出行完毕"` |
| 2 | 团期看板统计条(GB-ADM-009) | GET | `/v3/admin/order/group-batch/summary` | 响应结构扩展 | `buckets` 固定键 7 → 8,新增 `TRIP_FINISHED`,`total` = 八桶之和 |
| 3 | 团期看板导出(GB-ADM-008) | GET | `/v3/admin/order/group-batch/export` | 入参枚举扩展 | `opsStage` 新增 `TRIP_FINISHED` |
---
## 三、接口详情
### 1. 团期看板分页 `GET /v3/admin/order/group-batch`
**VO**: `GroupBatchListReqVO → Result<PageResult<GroupBatchPageItemRespVO>>`
#### 使用场景
团期看板列表(hl-ui `src/stores/orderV2Batch.js:93-97` `getGroupBatchPage()`);点「出行完毕」页签时传 `opsStage=TRIP_FINISHED`。
#### 入参
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|------|------|------|------|------|------|
| productId | query | Long(字符串) | 否 | 雪花 ID | 按产品筛选 |
| opsStage | query | String | 否 | `RECRUIT / FORMED / PENDING_TRIP / TRAVELLING / TRIP_FINISHED / AUDITING / CHECKED / DISBANDED` | **新增 `TRIP_FINISHED`**;非法值忽略不报错 |
| batchStatus | query | String | 否 | 九态之一 | **可传 `TRIP_FINISHED`** |
| month | query | String | 否 | yyyy-MM | 出发月份 |
| keyword | query | String | 否 | 已转义 | 班期编号 / 名称模糊 |
| deadlineFrom / deadlineTo | query | String | 否 | yyyy-MM-dd | 报名截止日区间 |
| pageNo | query | Integer | 否 | 默认 1 | 页码 |
| pageSize | query | Integer | 否 | 默认 20,最大 100 | 每页条数 |
#### 出参 `Result<PageResult<GroupBatchPageItemRespVO>>`
| 字段 | 类型 | 说明 |
|------|------|------|
| data.records[] | Array | 团期行(分页容器 `records / total / page / pageSize`) |
| data.records[].groupBatchId | String(Long) | 团期主订单 ID |
| data.records[].batchStatus | String | 九态之一,**可为 `TRIP_FINISHED`** |
| data.records[].batchStatusName | String | 中文名,`TRIP_FINISHED` 对应「出行完毕」 |
| data.records[].chips | Object | 六芯片;`TRIP_FINISHED` 团期房/车/导/摄恒 `DONE`(硬规则,与已返团同) |
| 其余字段 | — | 不变(productBatchId / productId / productName / batchNo / batchName / batchLabel / maxRooms / maxParticipants / enrolledPeople / enrolledRooms / remainRooms / remainParticipants / orderCount / enrollDeadline / departDate / endDate / receivableAmount / receivedAmount) |
#### 请求示例
```http
GET /v3/admin/order/group-batch?productId=2056947670512971778&opsStage=TRIP_FINISHED&pageSize=10 HTTP/1.1
Host: api.test.1814.love:9443
Authorization: Bearer <token>
```
#### 响应示例
第二轮造数后实测(2026-09-06 21:42):
```json
{
"code": 200,
"message": "成功",
"data": {
"records": [
{
"groupBatchId": "2096510069465088002",
"productId": "2056947670512971778",
"productName": "测试小蒙马-多档-固定金额",
"batchName": "#7178验收班期",
"batchStatus": "TRIP_FINISHED",
"batchStatusName": "出行完毕",
"orderCount": 1,
"departDate": "2026-12-27",
"endDate": "2026-12-28"
}
],
"total": 1,
"page": 1,
"pageSize": 10
},
"success": true
}
```
#### 空数据 / 降级响应
`opsStage=TRIP_FINISHED` 在 sys_job 未 resume 期间通常无命中:`records` 为空数组、`total=0`,不是错误。
```json
{
"code": 200,
"message": "成功",
"data": { "records": [], "total": 0, "page": 1, "pageSize": 20 },
"success": true
}
```
#### 错误响应
```json
{
"code": 589507,
"message": "无操作权限(非团期管理员 / 非本定制师名下)",
"data": null,
"success": false
}
```
#### 业务边界
- `opsStage` 与 `batchStatus` 的映射:`TRIP_FINISHED` 桶 = `TRIP_FINISHED` 态(单射,不是复合桶;`FORMED` 仍是 RESOURCE_PREPARING + MATERIAL_PREPARING 复合桶)
- #7189 合入后有「班期范围」筛选,默认「未结束」按返团日过滤;出行完毕/待审核/已审核的团期返团日必然已过,前端点这三个页签时应自动把范围切到「全部」
### 2. 团期看板统计条 `GET /v3/admin/order/group-batch/summary`
**VO**: `Result<GroupBatchSummaryVO>`(查询参数 productId / month / keyword,无请求 VO)
#### 使用场景
看板页签计数(hl-ui `orderV2Batch.js:74` `getGroupBatchSummary()`),与 GB-ADM-001 同筛选、不含 opsStage。
#### 入参
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|------|------|------|------|------|------|
| productId | query | Long(字符串) | 否 | 雪花 ID | 按产品 |
| month | query | String | 否 | yyyy-MM | 出发月份 |
| keyword | query | String | 否 | 已转义 | 班期编号 / 名称模糊 |
#### 出参 `Result<GroupBatchSummaryVO>`
| 字段 | 类型 | 说明 |
|------|------|------|
| data.total | Integer | 命中团期总数 = 八桶之和 |
| data.buckets | Object | **固定 8 键**,顺序 `RECRUIT, FORMED, PENDING_TRIP, TRAVELLING, TRIP_FINISHED, AUDITING, CHECKED, DISBANDED`,无命中为 0(改前 7 键,无 `TRIP_FINISHED`) |
| data.subOrderCount | Integer | 命中团期活跃子订单合计(不变) |
#### 请求示例
```http
GET /v3/admin/order/group-batch/summary?productId=2056947670512971778 HTTP/1.1
Host: api.test.1814.love:9443
Authorization: Bearer <token>
```
#### 响应示例
第二轮造数后实测(21:42):
```json
{
"code": 200,
"message": "成功",
"data": {
"total": 6,
"buckets": {
"RECRUIT": 0,
"FORMED": 2,
"PENDING_TRIP": 0,
"TRAVELLING": 1,
"TRIP_FINISHED": 1,
"AUDITING": 0,
"CHECKED": 0,
"DISBANDED": 2
},
"subOrderCount": 4
},
"success": true
}
```
#### 空数据 / 降级响应
无命中团期时 `total=0`、`subOrderCount=0`,`buckets` 仍固定返回 8 键全 0。
```json
{
"code": 200,
"message": "成功",
"data": { "total": 0, "buckets": { "RECRUIT": 0, "FORMED": 0, "PENDING_TRIP": 0, "TRAVELLING": 0, "TRIP_FINISHED": 0, "AUDITING": 0, "CHECKED": 0, "DISBANDED": 0 }, "subOrderCount": 0 },
"success": true
}
```
#### 错误响应
```json
{
"code": 589507,
"message": "无操作权限(非团期管理员 / 非本定制师名下)",
"data": null,
"success": false
}
```
#### 业务边界
- 桶键由后端枚举顺序生成,前端按对象键顺序或按自己的页签表读取都可以,但**不要按索引位置取第 5 个当「待审核」**(改前第 5 个是 AUDITING,改后是 TRIP_FINISHED)
- 老前端未加新键时:多出的 `TRIP_FINISHED` 键被忽略,`total` 与页签计数之和会差出该桶的数量
### 3. 团期看板导出 `GET /v3/admin/order/group-batch/export`
**VO**: `无请求 VO(查询参数) → CSV 文件流 text/csv`
#### 使用场景
看板「导出」按钮(hl-ui `exportGroupBatch()`),筛选参数与 GB-ADM-001 同义、不含分页。
#### 入参
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|------|------|------|------|------|------|
| productId | query | Long(字符串) | 否 | 雪花 ID | 按产品 |
| month | query | String | 否 | yyyy-MM | 出发月份 |
| keyword | query | String | 否 | 已转义 | 班期编号 / 名称模糊 |
| opsStage | query | String | 否 | 八桶之一 | **新增 `TRIP_FINISHED`** |
#### 出参 `CSV 文件流`
| 字段 | 类型 | 说明 |
|------|------|------|
| (响应体) | text/csv | 列与改前一致;状态列可出现「出行完毕」 |
#### 请求示例
```http
GET /v3/admin/order/group-batch/export?productId=2056947670512971778&opsStage=TRIP_FINISHED HTTP/1.1
Host: api.test.1814.love:9443
Authorization: Bearer <token>
```
#### 响应示例
```json
{
"code": 200,
"message": "文件流(Content-Type: text/csv; Content-Disposition: attachment),此处仅示意,实际响应体为 CSV"
}
```
#### 空数据 / 降级响应
无命中时返回只有表头的 CSV;命中 > 2000 行返回 `589517`。
```json
{
"code": 589517,
"message": "导出行数超过上限,请缩小筛选范围",
"data": null,
"success": false
}
```
#### 错误响应
```json
{
"code": 589507,
"message": "无操作权限(非团期管理员 / 非本定制师名下)",
"data": null,
"success": false
}
```
#### 业务边界
- 权限 `group-batch:export`
- `opsStage` 非法值忽略(与 GB-ADM-001 一致)
---
## 四、契约约束与正确调用方式
> 三个接口均为只读 GET,无请求体;本节写的是**前端消费九态/八桶的规则**。
### ✅ 正确 / ❌ 错误 payload 对照
| 场景 | payload / 处理 |
|------|---------|
| ✅ 页签 → 筛选 | 点「出行完毕」页签传 `opsStage=TRIP_FINISHED`(与其它页签同一套 `opsStage`) |
| ✅ 状态 → 桶 | 八态映射表加 `TRIP_FINISHED: 'TRIP_FINISHED'`;未知状态值按灰/默认样式,不抛错 |
| ✅ 统计条 | 按键名读 `buckets.TRIP_FINISHED`,不按索引 |
| ❌ 硬编码七桶数组 | 少一个页签、`total` 对不上 |
| ❌ 把 `TRIP_FINISHED` 当「已完成」终态 | 它之后还有核单中 / 已结算;核单入口在后续单 |
### 切换状态时的必要动作
无写接口。团期进入 `TRIP_FINISHED` 只由 sys_job 1042 完成(当前 PAUSED);前端不需要也不能手动推进。
---
## 五、数据库行为
`order_group_batch.batch_status` 为 VARCHAR(32),直接存新值 `TRIP_FINISHED`,无表变更、无 Flyway(order-v3)。hl-user-service Flyway `V20260906_005__register_group_batch_lifecycle_jobs.sql` 种子两行 `sys_job`(1041 团期出发推进 `0 10 0 * * ?`、1042 团期出行完毕推进 `0 20 0 * * ?`,`job_group=ORDER`,`status=PAUSED`)。
| job | 扫描条件 | 推进 | 时间线事件 |
|------|-----------------|------|------|
| 1041 | `PENDING_DEPARTURE` 且 product 侧出发日 ≤ 今天 | → `TRAVELLING` | `BATCH_DEPART` 系统自动发团 |
| 1042 | `TRAVELLING` 且 product 侧返团日 < 今天 | → `TRIP_FINISHED` | `BATCH_TRIP_FINISH` 系统自动出行完毕 |
状态 CAS 与时间线在同一事务内原子提交;日期取不到 product 实时值时回退团期快照并记 WARN。
---
## 六、边界行为
- 未登录 → 网关 401;无权限 → `589507`
- `opsStage` / `batchStatus` 传未知值 → 忽略该筛选,不报错
- 返团日当天仍算出行中(`< 今天` 才推进);出发日当天即算出行中(`≤ 今天`)
- 出行完毕团期:退团 → `589501`;转期 / 调满团名额 / 需求提报按既有阶段门拒绝;预支仍可发起
- HTTP 始终 200,按 `code` 判断
## 六.5、枚举 / 数据字典
### batchStatus(`com.hulalv.order.groupbatch.enums.GroupBatchStatus`)
| 值 | 中文名 | 桶(`GroupBatchStageBuckets.Bucket`) |
|---|---|---|
| `RECRUITING` | 招募中 | `RECRUIT` |
| `RESOURCE_PREPARING` | 资源准备中 | `FORMED` |
| `MATERIAL_PREPARING` | 物料准备中 | `FORMED` |
| `PENDING_DEPARTURE` | 待出发 | `PENDING_TRIP` |
| `TRAVELLING` | 出行中 | `TRAVELLING` |
| **`TRIP_FINISHED`** | **出行完毕(新增)** | **`TRIP_FINISHED`(新增)** |
| `REVIEWING` | 核单中 | `AUDITING` |
| `SETTLED` | 已结算 | `CHECKED` |
| `CANCELLED` | 已取消 | `DISBANDED` |
### opsStage / buckets 键(`GroupBatchStageBuckets.Bucket`)
`RECRUIT` 招募中 / `FORMED` 已成团 / `PENDING_TRIP` 待出行 / `TRAVELLING` 出行中 / **`TRIP_FINISHED` 出行完毕** / `AUDITING` 待审核 / `CHECKED` 已审核 / `DISBANDED` 流团(前端页签文案沿用现有)。
## 六.6、修改前后对比
### 字段级对比
| 字段 | 修改前 | 修改后 |
|------|--------|--------|
| GB-ADM-001 / 008 `opsStage` 取值 | 7 值 | 8 值(+ `TRIP_FINISHED`) |
| GB-ADM-001 `records[].batchStatus` 取值 | 8 值 | 9 值(+ `TRIP_FINISHED`,中文名「出行完毕」) |
| GB-ADM-009 `buckets` 键 | 7 键 | 8 键,`TRIP_FINISHED` 插在 `TRAVELLING` 与 `AUDITING` 之间 |
### 行为级对比
| 场景 | 修改前 | 修改后 |
|------|--------|--------|
| 待出发团期到出发日 | 无人推进,永远待出发 | sys_job 1041(resume 后)每日 00:10 → 出行中 |
| 出行中团期过返团日 | 无人推进,永远出行中 | sys_job 1042(resume 后)每日 00:20 → 出行完毕 |
| 出行完毕团期芯片 | 不存在该态 | 房/车/导/摄恒 `DONE`(与已返团同) |
| 出行完毕团期退团 | 不存在该态 | `589501` |
| 原七桶各自口径 | — | 不变 |
## 六.7、影响评估
- 向后兼容:只增枚举值/键,不改字段名;老前端不改代码不报错,但页签少「出行完毕」、统计条多出的键被忽略
- 前端是否必须同步上线:否(job PAUSED 期间不会出现该态数据);但应在 job resume 前补齐
- 数据:order-v3 无表变更;user-service 只有 sys_job 种子
---
## 七、不影响范围
- **仅影响**: 团期看板页签/统计条/导出的桶集合,团期状态取值
- **零影响**:
- #7204 导/摄芯片零指派口径、房/车/约/保芯片
- 订单(子订单)状态机与订单接口
- 团期成团 / 取消成团 / 流团 / 物资确认 / 结算守卫(仍要求核单中)
- 小程序
---
## 八、测试环境已验证
真实接口输出(测试服 api.test.1814.love,2026-09-06 21:38-21:42,登录后切 ADMIN / 触发 job 切 SUPER_ADMIN):
```
GET /v3/admin/order/group-batch/summary?productId=2044306857534636034 → 200, buckets 键序 RECRUIT,FORMED,PENDING_TRIP,TRAVELLING,TRIP_FINISHED,AUDITING,CHECKED,DISBANDED ✓
POST /admin/job/1041/trigger、/admin/job/1042/trigger(第一轮,造数期均为未来日期) → sys_job_log SUCCESS,A/B 状态不动 ✓
POST /admin/job/1041/trigger、/admin/job/1042/trigger(第二轮,product 侧日期改到 09-05) → SUCCESS,A→TRAVELLING、B→TRIP_FINISHED,order 快照日期未动 ✓
GET /v3/admin/order/group-batch?productId=2056947670512971778&opsStage=TRIP_FINISHED → 200, 1 行 B「出行完毕」 ✓
GET /v3/admin/order/group-batch/summary?productId=2056947670512971778 → TRAVELLING=1, TRIP_FINISHED=1 ✓
GET /v3/admin/order/group-batch/2096510069465088002/chips/hotel → aggregateStatus=DONE(硬规则) ✓
POST /v3/admin/order/group-batch/2096510069465088002/sub-order/2096510069234401282/withdraw → 589501 ✓
```
验证团期: A `2096495107078328322`、B `2096510069465088002`(产品 2056947670512971778「测试小蒙马-多档-固定金额」),造数已复原。单测:`GroupBatchStageBucketsTest` 12/0、`GroupBatchSummaryVOTest` 2/0、`GroupBatchStatusTest` 6/0、`GroupBatchLifecycleJobServiceTest` 11/0、`GroupBatchLifecycleTxServiceTest` 4/0、`GroupBatchLifecycleJobTest` 3/0、`GroupBatchMapperIT` 13/0;合入后 dev-v3 HEAD 全量 order-v3 8726/0、hl-user-service `QuartzJobExecutorTest` 16/0。部署:Deploy Panel 21:20-21:22 双实例滚动完成(order-v3 + user-service),分支 dev-v3。
---
## 十、相关文档
- 关联 Issue: [wx/HL#7190](https://git.1814.love:8443/wx/HL/issues/7190)
- 关联 PR: [wx/HL#7212](https://git.1814.love:8443/wx/HL/pulls/7212)
- 同现场: #7188(「第N期」序号)、#7189(看板以产品全班期为基底 + 班期范围筛选)、#7204(导/摄芯片零指派口径)
- 后续单: 「出行完毕 → 核单中」发起核单入口(落地前两个 sys_job 保持 PAUSED)
## 关联 / 联系人
### 链接
- **Issue**: [#7190](https://git.1814.love:8443/wx/HL/issues/7190)
- **PR**: [#7212](https://git.1814.love:8443/wx/HL/pulls/7212)
- **Merge commit**: [ccb4d8fd](https://git.1814.love:8443/wx/HL/commit/ccb4d8fd5177ef816e71fadd33335f8f1dce3966)
### 联系人
- **后端负责人**: @wx