From b794316e5a546903cc3fa85f7719871f90a6cc68 Mon Sep 17 00:00:00 2001 From: API Changelog Bot Date: Mon, 7 Sep 2026 00:54:11 +0800 Subject: [PATCH] =?UTF-8?q?changelog(7189):=20=E5=9B=A2=E6=9C=9F=E7=9C=8B?= =?UTF-8?q?=E6=9D=BF=E4=BB=A5=E4=BA=A7=E5=93=81=E5=85=A8=E7=8F=AD=E6=9C=9F?= =?UTF-8?q?=E4=B8=BA=E5=9F=BA=E5=BA=95=20+=20scope=20=E7=8F=AD=E6=9C=9F?= =?UTF-8?q?=E8=8C=83=E5=9B=B4=E7=AD=9B=E9=80=89=20+=20=E9=A1=B5=E7=AD=BE?= =?UTF-8?q?=E6=9C=9F=E6=95=B0=20product=20=E4=BE=A7=E8=AE=A1=E6=95=B0?= =?UTF-8?q?=EF=BC=88PR=20#7219=EF=BC=89=EF=BC=8C=E5=B7=B2=E9=83=A8?= =?UTF-8?q?=E7=BD=B2=20dev-v3=20=E7=BD=91=E5=85=B3=E5=A4=8D=E6=B5=8B=2023/?= =?UTF-8?q?23=20+=20AC-14?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...“�全班期基底与scope范围筛选-修改接口-管理后台.md | 669 ++++++++++++++++++ 1 file changed, 669 insertions(+) create mode 100644 changelogs-v2/2026-09/07_7189_团期看板产品全班期基底与scope范围筛选-修改接口-管理后台.md diff --git a/changelogs-v2/2026-09/07_7189_团期看板产品全班期基底与scope范围筛选-修改接口-管理后台.md b/changelogs-v2/2026-09/07_7189_团期看板产品全班期基底与scope范围筛选-修改接口-管理后台.md new file mode 100644 index 00000000..1132fb4e --- /dev/null +++ b/changelogs-v2/2026-09/07_7189_团期看板产品全班期基底与scope范围筛选-修改接口-管理后台.md @@ -0,0 +1,669 @@ +--- +schema: "hl-changelog/v2" +ticket: "7189" +title: "团期看板基底改为产品全班期(含未建团行)+ 新增班期范围 scope 筛选默认未结束 + 产品页签期数改 product 侧计数" +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-07" +updated_at: "2026-09-07" +base: "dev-v3" +status_note: "后端已合并(PR #7219,合并提交 ac0ffb02dfc3aa6d1edbba2788ba2542a9cb686b)并于 2026-09-07 00:47-00:49 部署测试服 dev-v3(product-v2 + order-v3),网关复测 23/23 + AC-14 改期一致性 通过;前端需:范围下拉默认「未结束」、点后段桶自动切「全部」、未建团行(groupBatchId=null)只放「新增子订单」、summary/export 与列表同传 scope、行标题「第{batchLabel}期 {batchName}」、页签期数直接用 batchCount。" +--- + +# 团期模块:看板以产品全班期为基底 + 班期范围 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.00" / "0.00" | +| data.records[].chips | Object | hotel / vehicle / guide / photographer / insurance / contract 六项;**子订单为 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**: CSV 附件(查询参数 productId / scope / month / keyword / opsStage,无请求 VO) + +#### 使用场景 + +导出当前列表;与列表同传 `scope`。 + +#### 入参 + +| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | +|------|------|------|------|------|------| +| scope | query | String | 否 | ONGOING / FINISHED / ALL | **新增**。缺省规则同 GB-ADM-001;按 product 实时返团日过滤 | +| productId / month / keyword / opsStage | query | — | 否 | 不变 | 不变 | + +#### 出参 + +CSV 字节流(BOM + 表头),**不含未建团行**(只导有团期的行);行数 = 同 scope 下 GB-ADM-001 中 `groupBatchId` 非空的行数(例外:产品侧班期已删且 0 活跃单的残留团期会出现在导出、不出现在列表)。 + +#### 请求示例 + +```http +GET /v3/admin/order/group-batch/export?productId=2044306857534636034&scope=ALL HTTP/1.1 +Host: api.test.1814.love:9443 +Authorization: Bearer +``` + +#### 响应示例 + +实测:`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 +``` + +#### 空数据 / 降级响应 + +无命中只返回表头一行。 + +#### 错误响应 + +`productId` 有值且 `scope≠ALL` 时会调产品域取实时返团日,产品域不可用返回 589515(改前导出不依赖产品域);超过 2000 行 589517。 + +```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>` + +字段不变;行为变化:产品班期全部删除时不再提前返回空,仍有活跃子订单的孤儿行照常输出(`productBatchRemoved=true`)。 + +#### 请求示例 + +```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=[]`。 + +#### 错误响应 + +`589515` 产品域不可用;`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`。 + +#### 错误响应 + +`589515` 产品域不可用。 + +#### 业务边界 + +- 徽标直接显示 `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