--- schema: "hl-changelog/v2" ticket: "7204" title: "团期看板导/摄芯片聚合态:零指派灰、部分指派橙、全部指派绿" consumer: "admin" author: "wx(GIT)" change_type: "修改接口" backend_status: "deployed" gateway_status: "verified" frontend_status: "not_required" 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>` #### 使用场景 团期看板列表(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>` | 字段 | 类型 | 说明 | |------|------|------| | 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 ``` #### 响应示例 改后(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`(无请求 VO,仅路径参数) #### 使用场景 看板行点击「导」芯片时拉逐户明细(hl-ui `getGroupBatchChip(id, 'guide')`);头部 `aggregateStatus` 即看板 `chips.guide`。 #### 入参 | 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | |------|------|------|------|------|------| | groupBatchId | path | Long(字符串) | 是 | 团期主订单 ID | 不是 product 侧 productBatchId | #### 出参 `Result` | 字段 | 类型 | 说明 | |------|------|------| | 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 ``` #### 响应示例 改后(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`(无请求 VO,仅路径参数) #### 使用场景 看板行点击「摄」芯片时拉逐户明细(hl-ui `getGroupBatchChip(id, 'photo')`);头部 `aggregateStatus` 即看板 `chips.photo`。 #### 入参 | 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | |------|------|------|------|------|------| | groupBatchId | path | Long(字符串) | 是 | 团期主订单 ID | 同上 | #### 出参 `Result` | 字段 | 类型 | 说明 | |------|------|------| | 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 ``` #### 响应示例 改后(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