--- schema: "hl-changelog/v2" ticket: "7189" title: "团期看板基底改为产品全班期(含未建团行)+ 新增班期范围 scope 筛选默认未结束 + 产品页签期数改 product 侧计数" 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, 汇总工单项2/项4):scope 入 store filter 单一事实源默认 ONGOING,fetchSummary/fetchBatchPage/onExport 三处同传,SearchForm 加班期范围下拉即选即查,点后段桶且 scope 非 ALL 自动切 ALL,pickProduct/onResetFilter 打回 ONGOING;未建团行 groupBatchId==null 不展开/无进入团期只留新增子订单深链,孤儿行加班期已删灰标签,index.vue 行 key 补 productBatchId 修撞 key。" --- # 团期模块:看板以产品全班期为基底 + 班期范围 scope 筛选 + 页签期数 ## ⚠️ 关键变化 - **列表基底变了(传 `productId` 时)**:GB-ADM-001 分页与 GB-ADM-009 统计条改以「产品侧全部班期 ∪ 订单侧团期」为基底,返回三类行:**命中行**(有团期)、**未建团行**(`groupBatchId=null`,产品建了班期但还没人下单)、**孤儿行**(`productBatchRemoved=true`,产品侧班期已删但团期还有活跃子订单)。改前只查 `order_group_batch`,产品建了 8 期看板只显 1 期,运营点不到「新增子订单」。 - **新增筛选 `scope`**(四端点同名同义):`ONGOING` 未结束(返团日 ≥ 今天或未填)/ `FINISHED` 已结束 / `ALL` 全部。**缺省值按 `productId` 定**:有 `productId` → `ONGOING`;无 `productId` → `ALL`(老调用行集不变);board 恒默认 `ALL`。前端范围下拉默认「未结束」,**summary / export 必须与列表同传 scope**,否则统计条与列表对不上账。 - **范围 ∩ 桶是严格交集**:默认 `ONGOING` 下点后段桶 `TRIP_FINISHED` / `AUDITING` / `CHECKED`(返团日必已过)会得到空列表;前端点这三个页签时**自动把范围切成「全部」**(或提示)。 - **传 `productId` 时排序改为出发日升序**(改前 create_time 倒序),分页在内存完成,`pageSize` 仍 ≤ 100。 - 未建团行:`batchStatus=RECRUITING`(落 `RECRUIT` 桶)、`orderCount=0`、金额 0、`chips` 六项 `TODO`;`enrolledRooms` = 产品侧占房数(**含线下占位**)、`remainRooms` = `maxRooms - 占房`(不限房 → null);新增 `productBatchStatus / productBatchStatusLabel`(产品侧售卖状态:报名中 / 即将满额 / 已满额 / 已结束 / 取消中 / 已取消,与团期九态是两套口径)。前端对 `groupBatchId=null` 的行只放「新增子订单」,不展开子订单、不做任何团期操作。 - 命中行 `batchNo / batchName / batchLabel / departDate / endDate / enrollDeadline` 改取 product 实时值(改名改期即时生效),孤儿行回落订单快照。 - **子订单为 0 的行 `chips` 六项固定 `TODO`,不再为 null**(含 productId 缺省路径)。 - GB-ADM-000 产品页签 `batchCount` 改为 product 侧未删班期数(与传该 `productId` + `scope=ALL` 的列表行数一致),改前是订单侧团期数。 --- ## 一、背景 ### 现象 wx 2026-09-06 看测试服「团期订单」看板:产品「冻干粉发短信给」建了 8 期,看板只显 1 期(10-01,唯一有团期的班期),其余班期没有入口下单;且没有「未结束 / 已结束」筛选,历史班期与在售班期混在一起。 ### 调用链 1. hl-ui `src/stores/orderV2Batch.js` `getGroupBatchPage()` → `GET /v3/admin/order/group-batch`(GB-ADM-001)→ order-v3 `GroupBatchQueryService.listPage`:`productId` 有值 → `GroupBatchMergedRowsService.listMergedRows`(Feign `GET /internal/product/group/{productId}/detail` 取产品全班期 + `order_group_batch` 该产品团期 → 合并三类行 → 内存筛选 / 出发日升序 / 内存分页);`productId` 缺省 → 原 DB 分页 + `end_date` 条件 2. 统计条 `GET /v3/admin/order/group-batch/summary`(GB-ADM-009)→ `GroupBatchBoardStatsService.summary` 同一合并基底分桶 3. 导出 `GET /v3/admin/order/group-batch/export`(GB-ADM-008)→ 仍以 `order_group_batch` 为基底(不含未建团行),`scope` 按 product 实时返团日过滤 4. 产品页签 `GET /v3/admin/order/group-batch/products`(GB-ADM-000)→ Feign `GET /internal/product/group/all` 的 `batchCount`(product 侧 `countByProductIds` 一次 in 投影) ### 地面真相(测试服 dev-v3 ac0ffb02dfc3aa6d1edbba2788ba2542a9cb686b,2026-09-07 00:47-00:49,产品 2044306857534636034「冻干粉发短信给」,product 侧 8 期:06-04 / 07-03 / 07-10 / 07-17 / 07-24 / 07-31 已返团,10-01 有团期,12-01 未建团) | 接口 | 结果 | |---|---| | 分页 `scope=ALL` | 8 行出发日升序;10-01 行 `groupBatchId` 非空 `batchLabel="7"`;其余 7 行 `groupBatchId=null` `batchStatus=RECRUITING` `chips` 六项 `TODO` | | 分页默认(=ONGOING) | 2 行(10-01、12-01);`scope=FINISHED` 6 行 | | summary 默认 | `RECRUIT=1 FORMED=1 其余 0 total=2`;`scope=ALL` → `total=8 RECRUIT=7 FORMED=1` | | products | 该产品 `batchCount=8` | | board | 8 行(默认 ALL),`scope=ONGOING` 2 行;改前/改后 JSON 逐字段 diff:键集合无增减(`batchLabel` 已由 #7188 先行加入),仅 10-01 行 orderCount/enrolledRooms/enrolledPeople/remainRooms 因期间新增 4 单变化 | | 06-04 期(线下占位 9 房 / 限 11 房,未建团) | `enrolledRooms=9 remainRooms=2` | --- ## 二、变更接口清单 | # | 接口 | 方法 | 路径 | 变更类型 | 说明 | |---|------|------|------|----------|------| | 1 | 团期看板分页(GB-ADM-001) | GET | `/v3/admin/order/group-batch` | 入参新增 + 响应新增字段 + 基底/排序变化 | 新增 `scope`;`productId` 有值时基底改合并三类行、出发日升序;`records[]` 新增 `productBatchStatus / productBatchStatusLabel / productBatchRemoved` | | 2 | 团期看板统计条(GB-ADM-009) | GET | `/v3/admin/order/group-batch/summary` | 入参新增 + 基底变化 | 新增 `scope`;与 GB-ADM-001 同基底,未建团行计入 `RECRUIT` | | 3 | 团期看板导出(GB-ADM-008) | GET | `/v3/admin/order/group-batch/export` | 入参新增 | 新增 `scope`;基底不变(不含未建团行);`productId` 场景新增 589515 | | 4 | 团期看板合并层列表 | GET | `/v3/admin/order/group-batch/board` | 入参新增(可选) | 新增 `scope`,缺省 `ALL`;产品班期全删时不再返回空(孤儿行照常输出) | | 5 | 团期产品页签(GB-ADM-000) | GET | `/v3/admin/order/group-batch/products` | 响应字段语义变化 | `batchCount` 由订单侧团期数改为 product 侧未删班期数 | --- ## 三、接口详情 ### 1. 团期看板分页 `GET /v3/admin/order/group-batch` **VO**: `GroupBatchListReqVO → Result>` #### 使用场景 团期看板列表;传 `productId` 时显示该产品全部班期(含未建团行),范围下拉默认「未结束」。 #### 入参 | 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | |------|------|------|------|------|------| | productId | query | Long(字符串) | 否 | 雪花 ID | 有值 → 基底 = 产品全班期 ∪ 订单团期;缺省 → 只查订单侧团期(行集与改前一致) | | scope | query | String | 否 | ONGOING / FINISHED / ALL;非法值按缺省处理 | **新增**。班期范围。缺省:`productId` 有值 → ONGOING,缺省 → ALL | | opsStage | query | String | 否 | RECRUIT / FORMED / PENDING_TRIP / TRAVELLING / TRIP_FINISHED / AUDITING / CHECKED / DISBANDED | 桶筛选;与 scope 取交集;未建团行按 RECRUITING 落 RECRUIT | | batchStatus | query | String | 否 | 团期九态 | 未建团行按 RECRUITING 匹配 | | month | query | String | 否 | yyyy-MM | 出发月份(未建团行按 product 侧出发日) | | keyword | query | String | 否 | 已转义 | 班期编号 / 名称模糊(命中行 / 未建团行按 product 实时名) | | deadlineFrom / deadlineTo | query | String | 否 | yyyy-MM-dd | 报名截止日区间 | | page | query | Integer | 否 | 默认 1 | 页码 | | pageSize | query | Integer | 否 | 默认 20,最大 100 | 每页条数 | #### 出参 `Result>` | 字段 | 类型 | 说明 | |------|------|------| | data.records[] | Array | 团期行(`records / total / page / pageSize`);传 `productId` 时按出发日升序 | | data.records[].groupBatchId | String(Long) | **未建团行为 null**,前端据此只放「新增子订单」 | | data.records[].productBatchId / productId | String | 三类行都有值 | | data.records[].batchNo / batchName / batchLabel | String | 命中行 / 未建团行取 product 实时值;孤儿行取订单快照 | | data.records[].batchStatus / batchStatusName | String | 团期九态;未建团行固定 `RECRUITING` / 招募中 | | data.records[].productBatchStatus | String | **新增**。product 侧售卖状态 code:ENROLLING / NEARLY_FULL / FULL / FINISHED / CANCELLING / CANCELLED;孤儿行 null | | data.records[].productBatchStatusLabel | String | **新增**。上项中文:报名中 / 即将满额 / 已满额 / 已结束 / 取消中 / 已取消 | | data.records[].productBatchRemoved | Boolean | **新增**。孤儿行 true(product 侧班期已删) | | data.records[].departDate / endDate / enrollDeadline | String(yyyy-MM-dd) | 命中行 / 未建团行 product 实时;孤儿行快照 | | data.records[].maxRooms / enrolledRooms / remainRooms | Integer | 未建团行:`enrolledRooms` = product 占房数(含线下占位),`remainRooms` = maxRooms − 占房(不限房 null) | | data.records[].orderCount / receivableAmount / receivedAmount | Integer / String | 未建团行 0 / "0" / "0"(实测金额串为 "0") | | data.records[].chips | Object | hotel / vehicle / guide / photo / insurance / contract 六项(键名 `photo` 不是 photographer);**子订单为 0 的行六项固定 `TODO`(不再为 null)** | | 其余字段 | — | 不变 | #### 请求示例 ```http GET /v3/admin/order/group-batch?productId=2044306857534636034&scope=ALL&pageSize=50 HTTP/1.1 Host: api.test.1814.love:9443 Authorization: Bearer ``` #### 响应示例 实测(2026-09-07,只列本单相关字段;8 行中取命中行 10-01 与未建团行 12-01): ```json { "code": 200, "message": "成功", "data": { "records": [ { "groupBatchId": "2096412454643802114", "productBatchId": "2052935476557328386", "productId": "2044306857534636034", "batchNo": "Q202610012052935476548939777", "batchName": " 没,那你", "batchLabel": "7", "batchStatus": "RESOURCE_PREPARING", "batchStatusName": "资源准备中", "productBatchStatus": "ENROLLING", "productBatchStatusLabel": "报名中", "productBatchRemoved": false, "departDate": "2026-10-01", "endDate": "2026-10-03", "enrollDeadline": "2026-09-30", "maxRooms": 8, "enrolledRooms": 4, "remainRooms": 4, "orderCount": 4, "receivableAmount": "23400.00", "receivedAmount": "4000.00", "chips": { "hotel": "DOING", "vehicle": "TODO", "guide": "TODO", "photo": "TODO", "contract": "TODO", "insurance": "TODO" } }, { "groupBatchId": null, "productBatchId": "2096631555760807938", "productId": "2044306857534636034", "batchNo": "Q202612012096631555752419329", "batchName": "QA-7189-1201", "batchLabel": "8", "batchStatus": "RECRUITING", "batchStatusName": "招募中", "productBatchStatus": "ENROLLING", "productBatchStatusLabel": "报名中", "productBatchRemoved": false, "departDate": "2026-12-01", "endDate": "2026-12-03", "enrollDeadline": "2026-11-30", "maxRooms": 4, "enrolledRooms": 0, "remainRooms": 4, "orderCount": 0, "receivableAmount": "0", "receivedAmount": "0", "chips": { "hotel": "TODO", "vehicle": "TODO", "guide": "TODO", "photo": "TODO", "contract": "TODO", "insurance": "TODO" } } ], "total": 8, "page": 1, "pageSize": 50 }, "success": true } ``` #### 空数据 / 降级响应 传 `productId` 且 product 侧无班期、订单侧也无团期 → `records=[]`;默认 `ONGOING` 下点后段桶(TRIP_FINISHED / AUDITING / CHECKED)交集为空也返回 `records=[]`,前端应自动切 `scope=ALL`。 ```json { "code": 200, "message": "成功", "data": { "records": [], "total": 0, "page": 1, "pageSize": 20 }, "success": true } ``` #### 错误响应 ```json { "code": 589515, "message": "获取团期产品列表失败,请稍后重试", "data": null, "success": false } ``` `productId` 有值时依赖产品域(Feign 取全班期),产品域不可用返回 589515;`productId` 缺省不依赖产品域。另:`589507` 无操作权限。HTTP 始终 200,按 `code` 判断。 #### 业务边界 - 权限 `group-batch:view` - `scope` 判定用 product 实时返团日(命中 / 未建团行);孤儿行用快照;返团日未填视为未结束 - 传 `productId` 时排序固定出发日升序(无出发日排最后),分页在内存完成 - 未建团行 `groupBatchId=null`:不能展开子订单、不能做任何团期操作,只能「新增子订单」(深链预填 `productId` + `productBatchId`) ### 2. 团期看板统计条 `GET /v3/admin/order/group-batch/summary` **VO**: `Result`(查询参数 productId / scope / month / keyword,无请求 VO) #### 使用场景 看板顶部桶计数;**必须与列表同传 `productId / scope / month / keyword`**。 #### 入参 | 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | |------|------|------|------|------|------| | productId | query | Long(字符串) | 否 | 雪花 ID | 同 GB-ADM-001 | | scope | query | String | 否 | ONGOING / FINISHED / ALL | **新增**。缺省规则同 GB-ADM-001 | | month / keyword | query | String | 否 | 同 GB-ADM-001 | 逐字同义 | #### 出参 `Result` | 字段 | 类型 | 说明 | |------|------|------| | data.buckets | Object | 固定 8 键(RECRUIT / FORMED / PENDING_TRIP / TRAVELLING / TRIP_FINISHED / AUDITING / CHECKED / DISBANDED);传 `productId` 时未建团行计入 RECRUIT | | data.total | Integer | 8 桶之和 = 同筛选下 GB-ADM-001 的 total | | data.subOrderCount | Integer | 有效子订单合计(未建团行贡献 0) | #### 请求示例 ```http GET /v3/admin/order/group-batch/summary?productId=2044306857534636034 HTTP/1.1 Host: api.test.1814.love:9443 Authorization: Bearer ``` #### 响应示例 实测(默认 ONGOING): ```json { "code": 200, "message": "成功", "data": { "total": 2, "buckets": { "RECRUIT": 1, "FORMED": 1, "PENDING_TRIP": 0, "TRAVELLING": 0, "TRIP_FINISHED": 0, "AUDITING": 0, "CHECKED": 0, "DISBANDED": 0 }, "subOrderCount": 4 }, "success": true } ``` #### 空数据 / 降级响应 无命中时 8 键全 0、`total=0`,不省略键。 ```json { "code": 200, "message": "成功", "data": { "buckets": { "RECRUIT": 0, "FORMED": 0, "PENDING_TRIP": 0, "TRAVELLING": 0, "TRIP_FINISHED": 0, "AUDITING": 0, "CHECKED": 0, "DISBANDED": 0 }, "total": 0, "subOrderCount": 0 }, "success": true } ``` #### 错误响应 ```json { "code": 589515, "message": "获取团期产品列表失败,请稍后重试", "data": null, "success": false } ``` #### 业务边界 - 桶计数不受 opsStage 影响(本接口不接受 opsStage) - 传 `productId` 时与 GB-ADM-001 共用同一合并基底与筛选,逐桶 = 列表按该桶筛选的 total ### 3. 团期看板导出 `GET /v3/admin/order/group-batch/export` **VO**: `无请求 VO(查询参数 productId / scope / month / keyword / opsStage) → CSV 文件流 text/csv` #### 使用场景 导出当前列表;与列表同传 `scope`。 #### 入参 | 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | |------|------|------|------|------|------| | scope | query | String | 否 | ONGOING / FINISHED / ALL | **新增**。缺省规则同 GB-ADM-001;按 product 实时返团日过滤 | | productId / month / keyword / opsStage | query | — | 否 | 不变 | 不变 | #### 出参 `CSV 文件流` | 字段 | 类型 | 说明 | |------|------|------| | (响应体) | text/csv | BOM + 表头 `团期号,期号,日期,出团日,满团名额,已售,剩余,状态,子订单数,整团应收`,列与改前一致 | | 行集 | — | **不含未建团行**(只导有团期的行);行数 = 同 scope 下 GB-ADM-001 中 `groupBatchId` 非空的行数(例外:产品侧班期已删且 0 活跃单的残留团期会出现在导出、不出现在列表) | | 日期列 | — | 行内「日期 / 出团日」仍是订单快照;`scope` 的未结束判定用 product 实时返团日(AC-14 实测:改期后行进入导出,日期列仍显快照) | #### 请求示例 ```http GET /v3/admin/order/group-batch/export?productId=2044306857534636034&scope=ALL HTTP/1.1 Host: api.test.1814.love:9443 Authorization: Bearer ``` #### 响应示例 文件流(Content-Type: text/csv; Content-Disposition: attachment),JSON 仅示意: ```json { "code": 200, "message": "文件流(text/csv),实际响应体为下方 CSV" } ``` 实测(2026-09-07):`scope=ALL` 表头 + 1 行(10-01 团期);默认 ONGOING 1 行;`scope=FINISHED` 0 行。 ```text 团期号,期号,日期,出团日,满团名额,已售,剩余,状态,子订单数,整团应收 Q202610012052935476548939777,没,那你,2026-10-01~2026-10-03,2026-10-01,8,4,4,已成团,4,23400.00 ``` #### 空数据 / 降级响应 无命中只返回表头一行;命中 > 2000 行返回 `589517`。 ```json { "code": 589517, "message": "导出行数超过上限,请缩小筛选范围", "data": null, "success": false } ``` #### 错误响应 `productId` 有值且 `scope≠ALL` 时会调产品域取实时返团日,产品域不可用返回 589515(改前导出不依赖产品域)。 ```json { "code": 589515, "message": "获取团期产品列表失败,请稍后重试", "data": null, "success": false } ``` #### 业务边界 - 权限 `group-batch:export` - 导出基底与列表基底不同(不含未建团行),前端导出按钮旁如需提示请用「仅导出已建团期」 ### 4. 团期看板合并层列表 `GET /v3/admin/order/group-batch/board` **VO**: `Result>`(查询参数 productId 必填、scope 可选) #### 使用场景 以产品全部班期为基底的看板(hl-ui 当前未调用);本单只加可选 `scope`,响应结构不变。 #### 入参 | 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | |------|------|------|------|------|------| | productId | query | Long(字符串) | 是 | 雪花 ID | 不变 | | scope | query | String | 否 | ONGOING / FINISHED / ALL | **新增**。缺省 `ALL`(保持既有行集) | #### 出参 `Result>` | 字段 | 类型 | 说明 | |------|------|------| | data[] | Array | 行集:命中 / 未建团 / 孤儿三类行,出发日升序;本单结构不变 | | data[].groupBatchId | String(Long) | 未建团行 null(不变) | | data[].productBatchRemoved | Boolean | 孤儿行 true;**行为变化**:产品班期全部删除时不再提前返回空,仍有活跃子订单的孤儿行照常输出 | | data[].batchLabel / batchName / batchNo / departureDate / endDate | String | 命中 / 未建团行 product 实时,孤儿行快照(不变) | | 其余字段 | — | 不变 | #### 请求示例 ```http GET /v3/admin/order/group-batch/board?productId=2044306857534636034&scope=ONGOING HTTP/1.1 Host: api.test.1814.love:9443 Authorization: Bearer ``` #### 响应示例 实测默认(ALL)8 行、`scope=ONGOING` 2 行;改前/改后逐字段 diff 见「地面真相」。 ```json { "code": 200, "message": "成功", "data": [ { "productBatchId": "2052935476557328386", "groupBatchId": "2096412454643802114", "departureDate": "2026-10-01", "endDate": "2026-10-03", "batchName": " 没,那你", "batchLabel": "7", "batchStatus": "RESOURCE_PREPARING", "productBatchRemoved": false, "orderCount": 4, "enrolledRooms": 4, "remainRooms": 4 }, { "productBatchId": "2096631555760807938", "groupBatchId": null, "departureDate": "2026-12-01", "endDate": "2026-12-03", "batchName": "QA-7189-1201", "batchLabel": "8", "batchStatus": "RECRUITING", "productBatchRemoved": false, "orderCount": 0, "enrolledRooms": 0, "remainRooms": 4 } ], "success": true } ``` #### 空数据 / 降级响应 产品无班期且无团期 → `data=[]`。 ```json { "code": 200, "message": "成功", "data": [], "success": true } ``` #### 错误响应 ```json { "code": 589515, "message": "获取团期产品列表失败,请稍后重试", "data": null, "success": false } ``` 另:`589507` 无操作权限。 #### 业务边界 - 与 GB-ADM-001 共用 `GroupBatchMergedRowsService` 合并逻辑,三类行判定一致 ### 5. 团期产品页签 `GET /v3/admin/order/group-batch/products` **VO**: `Result>` #### 使用场景 看板左侧产品页签「N 期」徽标。 #### 入参 | 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | |------|------|------|------|------|------| | (无) | — | — | — | — | 无查询参数,返回当前账号可见的全部团期产品 | #### 出参 `Result>` | 字段 | 类型 | 说明 | |------|------|------| | data[].batchCount | Integer | **语义变化**:改前 = 订单侧团期数(只有下过单的班期才算),改后 = product 侧未删班期数,与传该 `productId` + `scope=ALL` 的列表行数一致 | | 其余字段 | — | 不变 | #### 请求示例 ```http GET /v3/admin/order/group-batch/products HTTP/1.1 Host: api.test.1814.love:9443 Authorization: Bearer ``` #### 响应示例 实测(只列该产品): ```json { "code": 200, "message": "成功", "data": [ { "productId": "2044306857534636034", "productName": "冻干粉发短信给", "productNo": "G260415004", "category": "family", "subtitle": "发短信给对方搞定", "coverImageUrl": "https://hlgl-test.oss-cn-beijing.aliyuncs.com/test/material/2026/03/07/ed278d1da74c3827cb06b7f0cf6091fe.jpg", "lineId": "2044248925572919297", "lineName": "阿斯蒂芬撒点", "tripDays": 3, "tripNights": 2, "status": "PUBLISHED", "sortOrder": 0, "batchCount": 8, "regionText": null } ], "success": true } ``` #### 空数据 / 降级响应 无团期产品 → `data=[]`;产品无班期 → `batchCount=0`。 ```json { "code": 200, "message": "成功", "data": [], "success": true } ``` #### 错误响应 ```json { "code": 589515, "message": "获取团期产品列表失败,请稍后重试", "data": null, "success": false } ``` #### 业务边界 - 徽标直接显示 `batchCount`,不要用列表 total 反推(列表默认 ONGOING 只含未结束) --- ## 四、契约约束与正确调用方式 > 四个 GET 接口均只读;本节写**前端消费规则**。 ### ✅ 正确 / ❌ 错误 payload 对照 | 场景 | payload / 处理 | |------|---------| | ✅ 看板默认加载 | 列表 `?productId=X`(不传 scope = ONGOING)+ 统计条 `?productId=X` 同样不传 | | ✅ 用户切范围 | 列表 / 统计条 / 导出三处**同时**带 `scope=FINISHED` 或 `ALL` | | ✅ 点后段桶 | `opsStage=TRIP_FINISHED / AUDITING / CHECKED` 时把范围切成 `scope=ALL` 再请求 | | ✅ 未建团行 | `groupBatchId == null` → 只渲染「新增子订单」(深链带 `productId` + `productBatchId`),隐藏展开 / 操作列 | | ✅ 孤儿行 | `productBatchRemoved == true` → 标「班期已删」,仍可看子订单 | | ✅ 状态展示 | 团期态用 `batchStatus`;售卖态用 `productBatchStatusLabel`(两套口径,不要互相推导) | | ❌ 只给列表传 scope、统计条不传 | 统计条会按默认 ONGOING,与列表对不上账 | | ❌ 用列表 total 当页签期数 | 默认 ONGOING 不含已结束期;徽标用 `batchCount` | | ❌ 对 `groupBatchId=null` 的行调详情 / 子订单 / 团期操作 | 后端 589500 团期不存在 | | ❌ 把 `chips` 为 `TODO` 的未建团行当「待办」 | 未建团行没有子订单,六项 `TODO` 只是零户占位 | ### 切换状态时的必要动作 无写接口。范围切换 / 页签切换后**同时**刷新列表与统计条(同一组参数)。 --- ## 五、数据库行为 无表变更、无 Flyway。读路径变化: | 时点 | 读取 | |------|------| | GB-ADM-001 / 009 传 `productId` | Feign `GET /internal/product/group/{productId}/detail`(product 全班期,含 `occupiedRooms / manualOrderCount`)+ `order_group_batch` 该产品团期 → 内存合并 | | GB-ADM-001 / 009 / 008 缺省 `productId` | `order_group_batch` 单表;`scope` 落 `end_date >= 今天 OR end_date IS NULL` / `end_date < 今天` | | GB-ADM-000 | Feign `GET /internal/product/group/all` 的 `batchCount`(product 侧 `group_tour_batch` 按 productId 一次 in 投影计数,软删不计) | 写路径不变(建团 / 下单刷新快照见 #7188)。 --- ## 六、边界行为 - 未登录 → 网关 401;无权限 → `589507` - 传 `productId` 时产品域不可用 → `589515`(列表 / 统计条 / 导出三处;缺省 `productId` 不依赖产品域) - `scope` 非法值(如 `scope=xxx`)→ 按缺省规则处理,不报错 - 返团日未填的班期 / 团期 → 视为未结束(ONGOING 含、FINISHED 不含) - 默认 ONGOING 下 `opsStage` 取后段桶 → 空列表(交集),不报错 - 产品班期全删但团期仍有活跃子订单 → 列表 / board 都输出孤儿行;0 活跃单的残留团期不输出(导出仍会导出它) - 合并行超过 500 记 WARN(护栏,不截断) ## 六.5、枚举 / 数据字典 | 枚举 | 值 | 说明 | |------|----|------| | scope | ONGOING / FINISHED / ALL | 未结束(返团日 ≥ 今天或未填)/ 已结束(返团日 < 今天)/ 全部 | | productBatchStatus | ENROLLING / NEARLY_FULL / FULL / FINISHED / CANCELLING / CANCELLED | 报名中 / 即将满额 / 已满额 / 已结束 / 取消中 / 已取消(product 侧售卖态) | | batchStatus(未建团行) | RECRUITING | 固定值,落 RECRUIT 桶 | ## 六.6、修改前后对比 ### 字段级对比 | 字段 | 修改前 | 修改后 | |------|--------|--------| | GB-ADM-001 / 009 / 008 / board `scope` | 无 | 新增(缺省按 productId:有 → ONGOING,无 → ALL;board → ALL) | | GB-ADM-001 `records[].productBatchStatus / productBatchStatusLabel / productBatchRemoved` | 无 | 新增 | | GB-ADM-001 `records[].groupBatchId` | 恒非空 | 未建团行 null | | GB-ADM-001 `records[].chips`(0 子订单行) | null | 六项 `TODO` | | GB-ADM-001 `records[].batchNo / batchName / 日期`(命中行) | 订单快照 | product 实时 | | GB-ADM-000 `data[].batchCount` | 订单侧团期数 | product 侧未删班期数 | | product `GET /internal/product/group/all` `batchCount`、`/group/{productId}/detail` `batches[].occupiedRooms / manualOrderCount` | 无 | 新增(内部接口,前端不直接调) | ### 行为级对比 | 场景 | 修改前 | 修改后 | |------|--------|--------| | 传 productId 的列表 | 只显有团期的班期(8 期显 1 期) | 显全部班期(8 行),未建团行可「新增子订单」 | | 传 productId 的排序 | create_time 倒序 | 出发日升序 | | 默认范围 | 无范围概念,全部 | 传 productId 默认只显未结束;缺省 productId 仍全部 | | 统计条 | 只统计有团期的 | 与列表同基底,未建团行计入 RECRUIT | | 产品班期全删 | board 返回空(有客人的团期被藏) | 孤儿行照常输出 | | 页签期数 | 下过单的班期数 | 产品班期数 | ## 六.7、影响评估 - 向后兼容:缺省 `productId` 的调用行集与排序不变;传 `productId` 的老调用**行数与排序会变**(多出未建团行、按出发日升序、默认只含未结束),hl-ui 看板必须按第四节处理 `groupBatchId=null` - 前端是否必须同步上线:**是**(否则看板会出现不可操作的未建团行、点后段桶得空列表) - 数据:无表变更;依赖产品域可用性(589515) --- ## 七、不影响范围 - **仅影响**: 团期看板列表 / 统计条 / 导出 / board / 产品页签 - **零影响**: - 团期详情 GB-ADM-002、子订单 GB-ADM-003、芯片明细 GB-ADM-092/093(仍按 groupBatchId 查) - 创单 / 报价 / 状态机 / 按日推进 job(#7190,仍 PAUSED) - 小程序、product 管理端班期列表 --- ## 八、测试环境已验证 真实接口输出(测试服 api.test.1814.love,2026-09-07 00:47-00:49,登录后切 ADMIN 角色): ``` GET /v3/admin/order/group-batch?productId=2044306857534636034&scope=ALL&pageSize=50 → 200, total=8,出发日升序 06-04..12-01;10-01 行 groupBatchId=2096412454643802114 batchLabel="7";其余 7 行 groupBatchId=null batchStatus=RECRUITING orderCount=0 receivableAmount="0" chips 六项 TODO productBatchStatus 有值 ✓ GET /v3/admin/order/group-batch?productId=2044306857534636034&pageSize=50 → 200, 默认 ONGOING total=2(10-01、12-01)✓;scope=FINISHED total=6 ✓ GET /v3/admin/order/group-batch/summary?productId=2044306857534636034 → RECRUIT=1 FORMED=1 其余 0 total=2 subOrderCount=4 ✓;scope=ALL → total=8 RECRUIT=7 FORMED=1 ✓ GET /v3/admin/order/group-batch?productId=…&scope=ALL&opsStage=RECRUIT → total=7 = summary(ALL).RECRUIT ✓;默认范围 opsStage=RECRUIT → total=1(12-01)= summary.RECRUIT ✓ GET /v3/admin/order/group-batch?productId=…&scope=ALL&month=2026-10 → total=1(10-01)✓;keyword=没 → total=1(10-01)✓ GET /v3/admin/order/group-batch/products → 该产品 batchCount=8 ✓ GET /v3/admin/order/group-batch?pageSize=100(缺省 productId) → 改前/改后 20 行 groupBatchId 集合与顺序完全一致 ✓;scope=ONGOING 全部 endDate ≥ 今天 ✓;行内差异仅 chips(0 单行 null→六项 TODO,16 行)与 10-01 行计数(期间他人新下 4 单) GET /v3/admin/order/group-batch/board?productId=2044306857534636034 → 8 行,改前/改后逐字段 diff:键集合无增减,仅 10-01 行 orderCount/enrolledRooms/enrolledPeople/remainRooms 因新增 4 单变化 ✓;scope=ONGOING → 2 行 ✓ GET /v3/admin/order/group-batch/export?productId=…&scope=ALL|默认|FINISHED → 数据行 1 / 1 / 0 = 列表同 scope 下 groupBatchId 非空行数 ✓ GET /v3/admin/order/group-batch?productId=2061640294608134146&scope=ALL → 造数 maxRooms=0+manual=3 期 enrolledRooms=3 remainRooms=null ✓;maxRooms=5+manual=2 期 enrolledRooms=2 remainRooms=3 ✓;P1 06-04 期(线下 9/11)enrolledRooms=9 remainRooms=2 ✓ AC-14:PUT /admin/product/item/2056944943066132481/schedule 把班期 2089667211995115522 出发日 09-05→09-13(返团日昨天→下周),不下单:分页默认 total 8→9 且含团期 2089667627927437313(departDate=2026-09-13 endDate=2026-09-14)✓、summary FORMED 5→6 ✓、export 数据行 6→7 含该班期号 ✓;随后改回 09-05,三处恢复 8 / 5 / 6 ✓ ``` 验证产品:`productId=2044306857534636034`(冻干粉发短信给,8 期)、AC-13 载体 `2061640294608134146`(副本,造 maxRooms=0/manual=3 与 maxRooms=5/manual=2 两期)、AC-14 载体 `2056944943066132481` 班期 `2089667211995115522`(返团日昨天 → 改出发日到下周 → 三处出现 → 改回)。单测:order-v3 全量 `JAVA_TOOL_OPTIONS=-Xmx3g mvn -pl hl-order-service-v3 test` 8658/0/0(Skipped 7,ArchTest 六道门禁全绿)、product-v2 全量 1556/0/0;触点定向 ConverterTest 49 / QueryServiceTest 37 / ConsoleQueryServiceTest 27 / BoardStatsServiceTest 21 / MergedRowsServiceTest 18 / MapperIT 17(Testcontainers)/ ScopeResolverTest 9 / QueryControllerTest 8 / BoardStatsControllerTest 7 = 234/0/0。部署:Deploy Panel 00:47-00:49 product-v2 与 order-v3 滚动完成,分支 dev-v3。 --- ## 十、相关文档 - 关联 Issue: [wx/HL#7189](https://git.1814.love:8443/wx/HL/issues/7189) - 关联 PR: [wx/HL#7219](https://git.1814.love:8443/wx/HL/pulls/7219) - 同现场: #7188(batchLabel「第N期」)、#7190(TRIP_FINISHED 九态 / 八桶)、#7204(导/摄芯片零指派口径) - 前端缺陷 changelog: `06_frontend_团期看板展开行子订单列表恒空-前端缺陷-管理后台.md` ## 关联 / 联系人 ### 链接 - **Issue**: [#7189](https://git.1814.love:8443/wx/HL/issues/7189) - **PR**: [#7219](https://git.1814.love:8443/wx/HL/pulls/7219) - **Merge commit**: [ac0ffb02](https://git.1814.love:8443/wx/HL/commit/ac0ffb02dfc3aa6d1edbba2788ba2542a9cb686b) ### 联系人 - **后端负责人**: @wx