changelog(7204): 团期看板导/摄芯片聚合态零指派 TODO(PR #7207,已部署 dev-v3 网关复测)
changelog-filename-gate / validate (push) Successful in 2s

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01N7Xpgcv9nncadAXQtWhg4P
这个提交包含在:
API Changelog Bot
2026-09-06 20:33:08 +08:00
共同撰写人 Claude Fable 5.1
父节点 7f634e509d
当前提交 375520d4bd
@@ -0,0 +1,522 @@
---
schema: "hl-changelog/v2"
ticket: "7204"
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-06"
updated_at: "2026-09-06"
base: "dev-v3"
status_note: "后端已合并(PR #7207,合并提交 7c705b80)并于 2026-09-06 19:54 部署测试服 dev-v3,网关复测通过(零指派改前 DOING → 改后 TODO);前端无需改代码,建议补芯片图例/tooltip。"
---
# 团期模块:看板导/摄芯片聚合态口径调整(零指派返未开始)
## ⚠️ 关键变化
**只改一条聚合规则,不改任何字段名、枚举值集或户级明细。** 导游(guide)/ 摄影(photo)两枚芯片的整团聚合态 `aggregateStatus`(同一份值也就是 GB-ADM-001 `records[].chips.guide` / `chips.photo`):
| 计入户(needsIt=true)情况 | 改前 | 改后 |
|---|---|---|
| 一户都没指派(doneCount = 0,totalCount > 0) | `DOING`(橙「进行中」) | **`TODO`(灰「未开始」)** |
| 部分已指派(0 < doneCount < totalCount) | `DOING` | `DOING`(不变) |
| 全部已指派(doneCount = totalCount > 0) | `DONE` | `DONE`(不变) |
| 没有计入户(totalCount = 0,全部免闸或无子订单) | `TODO` | `TODO`(不变) |
**根因**:户级 `guide_status / photographer_status` 为 NULL(从未指派)时读侧归一为 `PENDING`「待指派」,而 `PENDING` 又在导/摄的「进行中」集合里,于是只要有户需要导游就恒 `DOING`。wx 2026-09-06 拍板改为与房/车「未提需求=灰、提了需求=橙、配完=绿」同一直觉。
**前端影响**:hl-ui `src/views/order-v2/batch/_shared/batchLifecycle.js` `chipAggState` 已把 `TODO` 映射为灰、`DOING` 橙、`DONE` 绿、`ERROR` 红,**无需改代码**;效果是「一户都没指派」的团期导/摄从橙变灰。建议:芯片区补图例或 tooltip(灰=未开始、橙=进行中、绿=已完成、红=异常)——运营把橙读成「配置完了」是本单起因。
房 / 车 / 约 / 保四芯片、GB-ADM-090~095 户级明细的 `items[].status`(`NONE / PENDING / DONE`)与 `statusText`(无需 / 待指派 / 已指派)**均不变**。
---
## 一、背景
### 现象
2026-09-06 测试服「团期订单」看板,产品「冻干粉发短信给」班期 2026-10-01 行,只有 1 个需要导游/摄影但从未指派的活跃子订单,「导 / 摄」芯片却是橙色(`DOING`),运营误读为「已配置完」。
### 调用链
1. hl-ui `src/stores/orderV2Batch.js:93-97` `getGroupBatchPage()` → `GET /v3/admin/order/group-batch`(GB-ADM-001)→ `records[].chips.guide / photo`
2. hl-ui 点击芯片 → `getGroupBatchChip(groupBatchId, 'guide'|'photo')` → `GET /v3/admin/order/group-batch/{groupBatchId}/chips/guide|photo`(GB-ADM-092/093)→ `aggregateStatus`
3. hl-order-service-v3:两处共用 `order/groupbatch/helper/GroupBatchChipResolver.resolveAggregateStatus`;本次在「失败→ERROR」「全完成→DONE」之后、扫描进行中值之前,对 GUIDE / PHOTOGRAPHER 增加 `doneCount == 0` 返 `TODO` 的分支
4. 硬规则不变:流团(CANCELLED)六芯片恒 `TODO`;已返团(REVIEWING / SETTLED)房车导摄恒 `DONE`;约 / 保在房车导摄四项全 `DONE` 前恒 `TODO`
### 地面真相(测试服 dev-v3,团期 groupBatchId=2096412454643802114)
| 时点 | 子订单 | `chips/guide` 响应要点 |
|---|---|---|
| 改前 17:20(部署前) | 1 户,needs_guide=1,guide_status=NULL | `aggregateStatus="DOING"`,totalCount=1,doneCount=0,items[0].status=`PENDING`「待指派」 |
| 改后 19:58(部署 7c705b80 后,另造 1 户零指派测试单 2096568044598820866,测完已取消) | 同上 | `aggregateStatus="TODO"`,totalCount=1,doneCount=0,items[0].status=`PENDING`「待指派」(户级不变) |
---
## 二、变更接口清单
| # | 接口 | 方法 | 路径 | 变更类型 | 说明 |
|---|------|------|------|----------|------|
| 1 | 团期看板分页(GB-ADM-001) | GET | `/v3/admin/order/group-batch` | 响应值语义变化 | `records[].chips.guide` / `chips.photo` 零指派由 `DOING` 改为 `TODO` |
| 2 | 导游芯片逐户明细(GB-ADM-092) | GET | `/v3/admin/order/group-batch/{groupBatchId}/chips/guide` | 响应值语义变化 | `aggregateStatus` 零指派由 `DOING` 改为 `TODO`;`items[]` 不变 |
| 3 | 摄影芯片逐户明细(GB-ADM-093) | GET | `/v3/admin/order/group-batch/{groupBatchId}/chips/photo` | 响应值语义变化 | 同上 |
---
## 三、接口详情
### 1. 团期看板分页 `GET /v3/admin/order/group-batch`
**VO**: `GroupBatchListReqVO → Result<PageResult<GroupBatchPageItemRespVO>>`
#### 使用场景
团期看板列表(hl-ui `getGroupBatchPage()`);本次只有 `records[].chips.guide` / `chips.photo` 的取值语义变化,其余字段与参数不变。
#### 入参
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|------|------|------|------|------|------|
| productId | query | Long(字符串) | 否 | 雪花 ID | 按产品筛选 |
| opsStage | query | String | 否 | RECRUIT / FORMED / PENDING_TRIP / TRAVELLING / AUDITING / CHECKED / DISBANDED | 七桶筛选 |
| month | query | String | 否 | yyyy-MM | 出发月份 |
| keyword | query | String | 否 | 已转义 | 班期编号 / 名称模糊 |
| batchStatus | 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[].productBatchId | String(Long) | product 侧班期 ID |
| data.records[].batchStatus / batchStatusName | String | 团期状态码 / 中文名 |
| data.records[].orderCount | Integer | 活跃子订单数 |
| data.records[].chips | Object | 六键固定:`hotel / vehicle / guide / photo / contract / insurance`,值为 `TODO / DOING / DONE / ERROR` 字符串;无活跃子订单时整个 `chips` 为 `null` |
| data.records[].chips.guide | String | **本次变化**:零指派 `TODO`,部分指派 `DOING`,全部指派 `DONE`(导/摄没有失败值,不会出现 `ERROR`) |
| data.records[].chips.photo | String | 同 `chips.guide` |
| 其余字段 | — | 不变(enrolledRooms / remainRooms / receivableAmount / receivedAmount / departDate / endDate …) |
#### 请求示例
```http
GET /v3/admin/order/group-batch?productId=2044306857534636034&pageNo=1&pageSize=20 HTTP/1.1
Host: api.test.1814.love:9443
Authorization: Bearer <token>
```
#### 响应示例
改后(2026-09-06 19:58 实测,该行 1 户零指派、未提房车需求):
```json
{
"code": 200,
"message": "成功",
"data": {
"records": [
{
"groupBatchId": "2096412454643802114",
"productBatchId": "2052935476557328386",
"productId": "2044306857534636034",
"batchName": " 没,那你",
"batchStatus": "RESOURCE_PREPARING",
"batchStatusName": "资源准备中",
"orderCount": 1,
"chips": {
"hotel": "TODO",
"vehicle": "TODO",
"guide": "TODO",
"photo": "TODO",
"contract": "TODO",
"insurance": "TODO"
}
}
],
"total": 1,
"page": 1,
"pageSize": 20
},
"success": true
}
```
改前同一场景 `chips.guide` / `chips.photo` 为 `"DOING"`。
#### 空数据 / 降级响应
没有命中团期时 `records` 为空数组;团期无活跃子订单时该行 `chips` 为 `null`(前端六芯片全灰,不要当字符串解析)。
```json
{
"code": 200,
"message": "成功",
"data": { "records": [], "total": 0, "page": 1, "pageSize": 20 },
"success": true
}
```
#### 错误响应
```json
{
"code": 589507,
"message": "无操作权限(非团期管理员 / 非本定制师名下)",
"data": null,
"success": false
}
```
HTTP 始终 200,按 `code` 判断。
#### 业务边界
- 权限 `group-batch:view`
- `chips.guide` / `chips.photo` 与 GB-ADM-092/093 的 `aggregateStatus` 同源同算法,两处必然一致
- 硬规则优先:流团行六芯片恒 `TODO`;已返团行房车导摄恒 `DONE`;约/保在房车导摄四项全 `DONE` 前恒 `TODO`
### 2. 导游芯片逐户明细 `GET /v3/admin/order/group-batch/{groupBatchId}/chips/guide`
**VO**: `Result<GroupBatchChipDetailRespVO>`(无请求 VO,仅路径参数)
#### 使用场景
看板行点击「导」芯片时拉逐户明细(hl-ui `getGroupBatchChip(id, 'guide')`);头部 `aggregateStatus` 即看板 `chips.guide`。
#### 入参
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|------|------|------|------|------|------|
| groupBatchId | path | Long(字符串) | 是 | 团期主订单 ID | 不是 product 侧 productBatchId |
#### 出参 `Result<GroupBatchChipDetailRespVO>`
| 字段 | 类型 | 说明 |
|------|------|------|
| data.batchId | String(Long) | 团期主订单 ID |
| data.chipLabel | String | 「配导游」 |
| data.aggregateStatus | String | **本次变化**:`TODO`(零指派)/ `DOING`(部分指派)/ `DONE`(全部指派);导/摄不会出现 `ERROR` |
| data.totalCount | Integer | 计入户数(needsIt=true 的活跃子订单) |
| data.doneCount | Integer | 已指派户数 |
| data.items[] | Array | 逐户明细(**不变**) |
| data.items[].orderId / orderNo / contactName / peopleCount | String / String / String / Integer | 户标识与人数 |
| data.items[].status | String | `NONE`(免闸)/ `PENDING`(待指派)/ `DONE`(已指派) |
| data.items[].statusText | String | 无需 / 待指派 / 已指派 |
| data.items[].needsIt | Boolean | 是否需要导游 |
| data.items[].updateTime | String | 恒 null(无独立时间列) |
#### 请求示例
```http
GET /v3/admin/order/group-batch/2096412454643802114/chips/guide HTTP/1.1
Host: api.test.1814.love:9443
Authorization: Bearer <token>
```
#### 响应示例
改后(2026-09-06 19:58 实测):
```json
{
"code": 200,
"message": "成功",
"data": {
"batchId": "2096412454643802114",
"chipLabel": "配导游",
"aggregateStatus": "TODO",
"totalCount": 1,
"doneCount": 0,
"items": [
{
"orderId": "2096568044598820866",
"orderNo": "HL20260906195548607",
"contactName": "7204复测",
"peopleCount": 1,
"status": "PENDING",
"statusText": "待指派",
"needsIt": true,
"updateTime": null
}
]
},
"success": true
}
```
改前(17:20 实测,同一团期另一户零指派)除 `aggregateStatus="DOING"` 外结构相同。
#### 空数据 / 降级响应
团期无活跃子订单或全部免闸:`totalCount=0`、`doneCount=0`、`aggregateStatus="TODO"`;`items[]` 为全部活跃户(免闸户 `status="NONE"`),无活跃户时为空数组。
```json
{
"code": 200,
"message": "成功",
"data": {
"batchId": "2096412454643802114",
"chipLabel": "配导游",
"aggregateStatus": "TODO",
"totalCount": 0,
"doneCount": 0,
"items": []
},
"success": true
}
```
#### 错误响应
```json
{
"code": 589500,
"message": "团期不存在",
"data": null,
"success": false
}
```
另:`589507` 无操作权限。
#### 业务边界
- 免闸户(needsIt=false)不计入 `totalCount / doneCount`,`status="NONE"`
- `aggregateStatus` 只读派生,不落库;在团期详情「配置导游」后扇出到全部子订单即 `DONE`
### 3. 摄影芯片逐户明细 `GET /v3/admin/order/group-batch/{groupBatchId}/chips/photo`
**VO**: `Result<GroupBatchChipDetailRespVO>`(无请求 VO,仅路径参数)
#### 使用场景
看板行点击「摄」芯片时拉逐户明细(hl-ui `getGroupBatchChip(id, 'photo')`);头部 `aggregateStatus` 即看板 `chips.photo`。
#### 入参
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|------|------|------|------|------|------|
| groupBatchId | path | Long(字符串) | 是 | 团期主订单 ID | 同上 |
#### 出参 `Result<GroupBatchChipDetailRespVO>`
| 字段 | 类型 | 说明 |
|------|------|------|
| data.chipLabel | String | 「配摄影」 |
| data.aggregateStatus | String | **本次变化**:规则同导游 |
| data.totalCount / doneCount | Integer | 同导游 |
| data.items[] | Array | 结构与导游接口完全相同,`items[].needsIt` 取需要摄影 |
#### 请求示例
```http
GET /v3/admin/order/group-batch/2096412454643802114/chips/photo HTTP/1.1
Host: api.test.1814.love:9443
Authorization: Bearer <token>
```
#### 响应示例
改后(19:58 实测):
```json
{
"code": 200,
"message": "成功",
"data": {
"batchId": "2096412454643802114",
"chipLabel": "配摄影",
"aggregateStatus": "TODO",
"totalCount": 1,
"doneCount": 0,
"items": [
{
"orderId": "2096568044598820866",
"orderNo": "HL20260906195548607",
"contactName": "7204复测",
"peopleCount": 1,
"status": "PENDING",
"statusText": "待指派",
"needsIt": true,
"updateTime": null
}
]
},
"success": true
}
```
#### 空数据 / 降级响应
同导游接口:无计入户时 `totalCount=0`、`aggregateStatus="TODO"`。
```json
{
"code": 200,
"message": "成功",
"data": { "batchId": "2096412454643802114", "chipLabel": "配摄影", "aggregateStatus": "TODO", "totalCount": 0, "doneCount": 0, "items": [] },
"success": true
}
```
#### 错误响应
```json
{
"code": 589500,
"message": "团期不存在",
"data": null,
"success": false
}
```
#### 业务边界
- 同导游接口
---
## 四、契约约束与正确调用方式
> 三个接口均为只读 GET,无请求体;本节写的是**前端消费聚合态的规则**。
### ✅ 正确 / ❌ 错误 payload 对照
| 场景 | payload / 处理 |
|------|---------|
| ✅ 按四值渲染 | `chips.guide` ∈ `TODO / DOING / DONE / ERROR` → 灰 / 橙 / 绿 / 红;未知值按灰 |
| ✅ `chips` 为 null | 六芯片全灰,不请求 090~095 明细 |
| ✅ 弹层头部与看板一致 | `chips/guide.aggregateStatus` 与 `records[].chips.guide` 同源,勿各算各的 |
| ❌ 用户级 `items[].status` 反推整团态 | 户级 `PENDING`「待指派」不等于整团进行中:零指派时整团是 `TODO` |
| ❌ 把 `DOING` 当「已配置」 | `DOING` 只表示部分户已指派 |
### 切换状态时的必要动作
无写接口;指派动作走团期详情「配置导游 / 摄影」,完成后重新拉 GB-ADM-001 或 090~095 即可看到 `DONE`。
---
## 五、数据库行为
本次无写操作、无表变更。`aggregateStatus` / `chips.*` 为读时派生(`GroupBatchChipResolver` 内存计算),不落库;户级来源仍是 `order_main.guide_status / photographer_status`(取值只有 `DONE` 或 NULL)。
| 计入户情况 | 派生结果 |
|------|-----------------|
| doneCount = 0,totalCount > 0 | `TODO` |
| 0 < doneCount < totalCount | `DOING` |
| doneCount = totalCount > 0 | `DONE` |
| totalCount = 0 | `TODO` |
---
## 六、边界行为
- 未登录 → 网关 401;无 `group-batch:view` → `589507`
- 团期不存在 → `589500`
- 流团团期 → 六芯片恒 `TODO`(硬规则 1);已返团团期 → 房车导摄恒 `DONE`(硬规则 2)
- 约 / 保在房车导摄四项未全 `DONE` 前恒 `TODO`(硬规则 3,本次导/摄零指派落 `TODO` 后约/保同样保持 `TODO`)
- HTTP 始终 200,按 `code` 判断
## 六.5、枚举 / 数据字典
### aggregateStatus / chips.*(`com.hulalv.order.groupbatch.helper.GroupBatchChipResolver` 常量 `AGGREGATE_TODO / AGGREGATE_DOING / AGGREGATE_DONE / AGGREGATE_ERROR`)
| 值 | 含义 | 前端色 |
|---|---|---|
| `TODO` | 未开始(导/摄:零指派;房/车:未提需求) | 灰 |
| `DOING` | 进行中(导/摄:部分指派;房/车:需求已提交/处理中/待审核) | 橙 |
| `DONE` | 已完成 | 绿 |
| `ERROR` | 异常(房/车驳回、约作废、保失败;导/摄不会出现) | 红 |
### items[].status(导/摄户级,`GroupBatchChipResolver.readGuideStatus`)
| 值 | 含义 | statusText |
|---|---|---|
| `NONE` | 该户无需导游/摄影(免闸,不计入) | 无需 |
| `PENDING` | 需要且未指派 | 待指派 |
| `DONE` | 已指派 | 已指派 |
## 六.6、修改前后对比
### 字段级对比
| 字段 | 修改前 | 修改后 |
|------|--------|--------|
| `records[].chips.guide` / `chips.photo`、`aggregateStatus` 取值集 | `TODO / DOING / DONE` | 不变 |
| 零指派场景取值 | `DOING` | `TODO` |
### 行为级对比
| 场景 | 修改前 | 修改后 |
|------|--------|--------|
| 零指派(doneCount=0,totalCount=1) | `DOING`(橙) | `TODO`(灰) |
| 部分指派(doneCount=1,totalCount=2) | `DOING`(橙) | `DOING`(橙) |
| 全部指派(doneCount=2,totalCount=2) | `DONE`(绿) | `DONE`(绿) |
| 全部免闸(totalCount=0) | `TODO` | `TODO` |
| 已返团(REVIEWING / SETTLED) | `DONE`(硬规则) | `DONE` |
| 流团(CANCELLED) | `TODO`(硬规则) | `TODO` |
## 六.7、影响评估
- 向后兼容:字段名、枚举值集不变,只是零指派场景的取值变化;老前端不改也能正确渲染
- 前端是否必须同步上线:否
- 数据:无表变更、无迁移;聚合态实时派生不落库
---
## 七、不影响范围
- **仅影响**: 管理后台团期看板行「导 / 摄」芯片颜色,以及 090~095 弹层头部聚合态
- **零影响**:
- 房 / 车 / 约 / 保四芯片
- 户级明细 `items[]`(值与文案不变)
- 团期详情 `hotelReady / guideReady / photographerReady` 标志
- 指派流程、团期状态机、订单接口
- 小程序
---
## 八、测试环境已验证
真实接口输出(测试服 api.test.1814.love,2026-09-06,登录后切 ADMIN 角色):
```
GET /v3/admin/order/group-batch/2096412454643802114/chips/guide → 200, aggregateStatus=TODO, total=1, done=0, items[0]=PENDING/待指派 ✓(改前 17:20 同场景 DOING)
GET /v3/admin/order/group-batch/2096412454643802114/chips/photo → 200, aggregateStatus=TODO, total=1, done=0 ✓
GET /v3/admin/order/group-batch?productId=2044306857534636034 → 200, 该行 chips.guide=TODO, chips.photo=TODO, hotel=TODO, vehicle=TODO ✓
```
验证团期: `groupBatchId=2096412454643802114`(产品 2044306857534636034「冻干粉发短信给」,班期 2026-10-01);造的零指派测试单 2096568044598820866 已取消。
单测:`GroupBatchChipResolverTest` 30/0/0(新增零指派 / 部分指派 / 全部指派 / 免闸 / 房车不受影响五例)、`GroupBatchChipServiceTest` 14/0、`GroupBatchQueryServiceTest` 23/0、ArchTest 门禁 41/0。部署:Deploy Panel 19:54 双实例滚动完成,分支 dev-v3。
---
## 十、相关文档
- 关联 Issue: [wx/HL#7204](https://git.1814.love:8443/wx/HL/issues/7204)
- 关联 PR: [wx/HL#7207](https://git.1814.love:8443/wx/HL/pulls/7207)
- 同现场: #7188(「第N期」序号)、#7189(看板以产品全班期为基底 + 班期范围筛选)、#7190(「出行完毕」状态与八桶)
- 前端缺陷 changelog: `06_frontend_团期看板展开行子订单列表恒空-前端缺陷-管理后台.md`
## 关联 / 联系人
### 链接
- **Issue**: [#7204](https://git.1814.love:8443/wx/HL/issues/7204)
- **PR**: [#7207](https://git.1814.love:8443/wx/HL/pulls/7207)
- **Merge commit**: [7c705b80](https://git.1814.love:8443/wx/HL/commit/7c705b808723bc415b5dc99d0535d0521668f90f)
### 联系人
- **后端负责人**: @wx