--- schema: "hl-changelog/v2" ticket: "8413" title: "团期看板导出与统计条补齐列表新筛选:导出接收团期状态 / 截止日 / 出发区间 / 排序(看到什么导什么),统计条接收截止日 / 出发区间;三接口截止日非法值改为忽略" consumer: "admin" author: "jw(GIT)" change_type: "修改接口" backend_status: "deployed" gateway_status: "verified" frontend_status: "verified" frontend_owner: "mmg" frontend_ref: "31e17e92726aa8d4251e60b107dc2d6ea8fb67d3" target_release: "v2.1" verified_at: "2026-09-27" status_note: "jw 2026-09-27 定:统计条只跟截止日、出发区间(不跟团期状态 / 看板桶 / 排序);导出看到什么导什么(传排序同列表,不传与列表各自默认一致:按产品 = 出发日升序、不按产品 = 建团时间倒序);导出不带分页、保持 2000 行上限。PR #8417(主体)+ #8432(截止日宽容解析)已合 dev-v3(dada3a8f4)并部署 TEST。2026-09-27 11:54~15:12 经真实网关实测:按产品与全局两种基底、团期状态 / 截止日 / 出发区间组合与三个排序字段 × 升降序、不传排序,导出行集与顺序均与列表已建团行逐一致;产品侧改期(快照 12-10 / 实时 12-15)后按实时日期筛,列表与导出同时命中、按快照日期同时不命中;统计条 total = 列表 total = 七桶之和,filteredOutCount 非零场景(8 / 15)与「ALL − ONGOING」一致;统计条多传团期状态 / 看板桶 / 排序结果不变;非法截止日期三接口均 200 且等同不传该条件(改前列表与导出 500)。TEST 全库团期仅 298 条,2000 行上限由单测覆盖。前端需把列表当前筛选同步传给统计条与导出,故 frontend_status 记 pending。前端已交付:看板 UI 无团期状态/报名截止日筛选控件(grep 实证筛选事实源仅产品/桶/月/关键词/出团区间/排序),batchStatus/deadline 三参无事实源不落;store fetchSummary 补 depart 对(空值不落参,排序/桶仍不带),onExport 补 depart 对+sortBy/sortOrder(看到什么导什么);截止日非法值忽略对前端无影响。store spec 13 例+看板页 spec 10 例全绿,checkpoint 全量通过。" updated_at: "2026-09-27" base: "dev-v3" --- # 团期看板:导出与统计条跟上列表筛选(管理后台) > **服务**: hl-order-service-v3(端口 8086/8186) > **PR**: #8417、#8432 > **Issue**: #8413 > **日期**: 2026-09-27 > **影响范围**: 管理后台「团期订单」看板的统计条与「导出」按钮;列表接口仅截止日非法值的处理变化 --- ## ⚠️ 关键变化 1. **导出新增入参**:`batchStatus`、`deadlineFrom` / `deadlineTo`、`departFrom` / `departTo`、`sortBy` / `sortOrder`,与列表同名同义。前端把列表**当前的全部筛选和排序**原样传给导出,导出的行和顺序就与列表一致(导出不含未建团行,这一点不变)。 2. **导出不传排序时的默认顺序变了(仅不按产品时)**:以前一律按出发日升序;现在与列表一致——**按产品看 = 出发日升序,不按产品看 = 建团时间倒序**。 3. **统计条新增入参**:`deadlineFrom` / `deadlineTo`、`departFrom` / `departTo`。**不接收**团期状态、看板桶、排序——统计条是按桶分组的计数,前端传了也会被忽略。 4. **报名截止日非法值不再报 500**:列表、导出、统计条传非法截止日期(如 `bad-date`、`2026-02-30`)时忽略该条件,与出发区间一致。以前列表和导出会整页返回 500。 --- ## 一、背景 列表先后加了团期状态、报名截止日、出发区间(#7770)和排序(#7770),导出和统计条没跟上:运营在列表上筛完,顶部统计条数字对不上,导出拿到的也是没筛的全量、顺序也不同。 --- ## 二、变更接口清单 | # | 接口 | 方法 | 路径 | 变更类型 | 说明 | |---|---|---|---|---|---| | 1 | 导出团期列表 | GET | `/v3/admin/order/group-batch/export` | 修改 | 新增团期状态 / 截止日 / 出发区间 / 排序入参;不按产品时默认顺序改为建团时间倒序 | | 2 | 团期看板统计条 | GET | `/v3/admin/order/group-batch/summary` | 修改 | 新增截止日 / 出发区间入参 | | 3 | 团期分页列表 | GET | `/v3/admin/order/group-batch` | 修改 | 截止日非法值由 500 改为忽略该条件 | --- ## 三、接口详情 ### 1. 导出团期列表 `GET /v3/admin/order/group-batch/export` **VO**: `GroupBatchListReqVO`(入参,与列表同一个)→ CSV 附件(固定 10 列,不变) #### 使用场景 看板点「导出」,导出当前筛选下的全部已建团团期(不分页,单次上限 2000 行)。 #### 入参 | 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | |---|---|---|---|---|---| | productId | query | Long | 否 | — | 原有 | | month | query | String | 否 | yyyy-MM | 原有,决定文件名 | | keyword | query | String | 否 | — | 原有 | | opsStage | query | String | 否 | 七桶 code | 原有 | | scope | query | String | 否 | ONGOING / FINISHED / ALL | 原有 | | batchStatus | query | String | 否 | 九态 code | **新增**,精确匹配 | | deadlineFrom / deadlineTo | query | String | 否 | yyyy-MM-dd,非法值忽略 | **新增**,报名截止日区间 | | departFrom / departTo | query | String | 否 | yyyy-MM-dd,非法值忽略 | **新增**,出团日期区间 | | sortBy | query | String | 否 | departDate / enrollDeadline / createTime,非法值忽略 | **新增** | | sortOrder | query | String | 否 | asc / desc,缺省或非法 asc | **新增** | `pageNo` / `pageSize` 即使传了也不生效(导出不分页)。 #### 出参 | 字段 | 类型 | 说明 | |---|---|---| | (CSV 附件) | text/csv | 列:团期号、期号、日期、出团日、满团名额、已售、剩余、状态、子订单数、整团应收;不变 | #### 请求示例 ```http GET /v3/admin/order/group-batch/export?productId=2056944943066132481&scope=ALL&batchStatus=RECRUITING&deadlineFrom=2026-10-01&deadlineTo=2026-12-31&departFrom=2026-10-01&departTo=2027-01-31&sortBy=departDate&sortOrder=desc Authorization: Bearer ``` #### 响应示例 成功时直接返回 CSV 文件流(`Content-Disposition: attachment`),不套 Result 信封。 ```json { "说明": "成功响应为 CSV 附件,此处仅示意首行表头", "header": "团期号,期号,日期,出团日,满团名额,已售,剩余,状态,子订单数,整团应收" } ``` #### 空数据 / 降级响应 无命中时返回只含表头的 CSV。按产品导出时要调产品服务取实时日期,产品服务不可用返回 589515(与列表、统计条一致): ```json { "code": 589515, "message": "获取团期产品列表失败,请稍后重试", "data": null, "success": false } ``` #### 错误响应 | code | 触发条件 | |---|---| | `589517` | 全部筛选后命中超过 2000 行 | | `589515` | 按产品导出时产品服务不可用 | | `589507` | 无导出权限(`group-batch:export`,未改) | ```json { "code": 589517, "message": "导出数据量超过单次上限,请缩小筛选范围后重试", "data": null, "success": false } ``` #### 业务边界 - **看到什么导什么**:同一组参数下,导出行 = 列表里已建团的行,顺序一致(未建团行本来就不导出)。 - **不传排序**:按产品看 = 出发日升序;不按产品看 = 建团时间倒序(**以前一律出发日升序**)。 - **按产品导出**时,班期范围、截止日、出发区间按**产品侧实时日期**筛选和排序,与列表一致;产品改期后不会再出现「列表里有、导出里没有」。 - 2000 行上限在全部筛选之后判断。 ### 2. 团期看板统计条 `GET /v3/admin/order/group-batch/summary` **VO**: `GroupBatchSummaryVO`(不变) #### 使用场景 看板顶部统计条(总数 + 七桶计数 + 被过滤条数),随列表的截止日、出发区间一起刷新。 #### 入参 | 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | |---|---|---|---|---|---| | productId / month / keyword / scope | query | — | 否 | — | 原有 | | deadlineFrom / deadlineTo | query | String | 否 | yyyy-MM-dd,非法值忽略 | **新增** | | departFrom / departTo | query | String | 否 | yyyy-MM-dd,非法值忽略 | **新增** | #### 出参 | 字段 | 类型 | 说明 | |---|---|---| | total | Integer | 命中团期数(与列表同条件、不筛团期状态和看板桶时的 total 相等) | | buckets | Map | 七桶计数(之和 = total)+ 旧八桶别名键(#8271 过渡期兼容,不变) | | subOrderCount / minToForm / maxRooms | Integer | 不变 | | effectiveScope | String | 生效的班期范围,不变 | | filteredOutCount | Integer | 因班期范围被过滤掉的条数,**现在带上截止日与出发区间计算** | #### 请求示例 ```http GET /v3/admin/order/group-batch/summary?productId=2056944943066132481&scope=ONGOING&departFrom=2026-05-01&departTo=2026-12-31 Authorization: Bearer ``` #### 响应示例 ```json { "code": 200, "message": "成功", "data": { "total": 22, "buckets": { "RECRUIT": 4, "CONFIGURE": 7, "CONFIRM": 0, "TRIP": 0, "REVIEW": 7, "SETTLE": 1, "DISBANDED": 3, "FORMED": 7, "PENDING_TRIP": 0, "TRAVELLING": 0, "TRIP_FINISHED": 0, "AUDITING": 7, "CHECKED": 1 }, "subOrderCount": 42, "minToForm": null, "maxRooms": null, "effectiveScope": "ONGOING", "filteredOutCount": 8 }, "success": true } ``` #### 空数据 / 降级响应 无命中时 `total=0`、各桶为 0: ```json { "code": 200, "message": "成功", "data": { "total": 0, "buckets": { "RECRUIT": 0 }, "filteredOutCount": 0 }, "success": true } ``` #### 错误响应 判权未改(`group-batch:list`)。 ```json { "code": 589507, "message": "无操作权限(当前角色未授予团期权限,或该团期不在您名下)", "data": null, "success": false } ``` #### 业务边界 - 不接收 `batchStatus`、`opsStage`、`sortBy`、`sortOrder`,传了也忽略(统计条要给出全部桶的计数)。 - 不传新参数时结果与改前一致。 ### 3. 团期分页列表 `GET /v3/admin/order/group-batch` **VO**: `GroupBatchListReqVO` → `PageResult`(均不变) #### 使用场景 看板列表。本次只改报名截止日非法值的处理。 #### 入参 | 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | |---|---|---|---|---|---| | deadlineFrom / deadlineTo | query | String | 否 | yyyy-MM-dd | **非法值改为忽略该条件**(以前 500) | | 其余 | query | — | 否 | — | 不变 | #### 出参 | 字段 | 类型 | 说明 | |---|---|---| | (全部) | — | 不变 | #### 请求示例 ```http GET /v3/admin/order/group-batch?deadlineFrom=bad-date&pageNo=1&pageSize=20 Authorization: Bearer ``` #### 响应示例 ```json { "code": 200, "message": "成功", "data": { "records": [], "total": 298, "page": 1, "pageSize": 20 }, "success": true } ``` #### 空数据 / 降级响应 不变。 ```json { "code": 200, "message": "成功", "data": { "records": [], "total": 0, "page": 1, "pageSize": 20 }, "success": true } ``` #### 错误响应 判权未改。 ```json { "code": 589507, "message": "无操作权限(当前角色未授予团期权限,或该团期不在您名下)", "data": null, "success": false } ``` #### 业务边界 - 非法截止日期等同不传该条件,服务端记 warn 日志。 --- ## 四、契约约束与正确调用方式 - 前端把列表当前的筛选参数**同一份**传给统计条和导出: - 导出:`productId / month / keyword / opsStage / scope / batchStatus / deadlineFrom / deadlineTo / departFrom / departTo / sortBy / sortOrder`; - 统计条:`productId / month / keyword / scope / deadlineFrom / deadlineTo / departFrom / departTo`。 - 统计条不要传团期状态和看板桶;传了会被忽略,但不要依赖这一点做业务判断。 --- ## 五、数据库行为 - 零表结构变更、零迁移脚本;三个接口均只读(导出成功后照旧写导出留痕,逻辑未改)。 - 三个接口的筛选条件由同一段代码生成,不按产品时下推数据库,按产品时日期类条件按产品侧实时日期在内存筛选。 --- ## 六、边界行为 - 导出单次上限 2000 行不变,判断在全部筛选之后。 - 按产品导出时,`month` 与 `keyword` 按订单侧快照匹配、CSV「日期 / 出团日」列打印订单侧快照:产品侧改过期或改过名的班期,这两处可能与列表显示不同。 --- ## 六.6、修改前后对比 | 项 | 改前 | 改后 | |---|---|---| | 导出可用筛选 | productId / month / keyword / opsStage / scope | 另加 batchStatus / 截止日 / 出发区间 / 排序 | | 导出默认顺序(不按产品) | 出发日升序 | 建团时间倒序(与列表一致) | | 导出默认顺序(按产品) | 出发日升序(订单快照) | 出发日升序(产品侧实时) | | 统计条可用筛选 | productId / month / keyword / scope | 另加截止日 / 出发区间 | | 非法截止日期 | 列表、导出 500 | 三接口忽略该条件 | --- ## 六.7、影响评估 - 前端:统计条与导出要补传参数才生效;不补传时统计条结果不变,导出仅「不按产品」时默认顺序变化。 - 列表:只有非法截止日期的处理变化,合法请求不受影响。 --- ## 七、不影响范围 - 导出 CSV 的列与格式、导出留痕、判权码。 - 看板列表 `GET /v3/admin/order/group-batch/board`、产品选择器、团期详情。 --- ## 八、测试环境已验证 部署 dev-v3 @ `7c69e8227`(主体)与 `dada3a8f4`(截止日宽容解析)后,经真实网关 `https://api.test.1814.love` 实测: | 用例 | 期望 | 实测 | |---|---|---| | 构建身份:统计条 departFrom=2099-01-01 | total 由 33 变 0 | 通过 | | 按产品:scope=ALL 不传排序 / 团期状态+截止日+出发区间 / 3 排序字段 × 升降序 | 导出行集与顺序 = 列表已建团行 | 8 组全部一致(列表 34 条含 5 条未建团,导出 29 行) | | 全局:出发区间不传排序 / 团期状态+截止日 / 3 排序字段 × 升降序 | 同上 | 8 组全部一致(169 / 165 / 260 行) | | 产品侧改期(快照 12-10、实时 12-15) | 按实时日期命中、按快照不命中,列表与导出一致 | 出发区间与截止日各两组,通过 | | 统计条 截止日+出发区间(按产品 / 全局,ALL / ONGOING) | total = 列表 total = 七桶之和 | 通过(21 / 23 / 229 / 260) | | 统计条 filteredOutCount 非零场景 | = ALL − ONGOING | 8 / 15,通过 | | 统计条多传团期状态 / 看板桶 / 排序 | 结果不变 | 通过 | | 非法截止日期 `bad-date` / `2026-02-30` | 三接口 200,等同不传 | 通过(改前列表与导出 500) | | 非法出发日期 / 非法排序 | 忽略 | 通过 | | 不带 token | 401 | 通过 | TEST 全库团期 298 条,2000 行上限(589517)由单测覆盖。造数已清理(产品侧日期已改回、班期已取消)。 --- ## 十、相关文档 - Issue #8413、PR #8417、PR #8432 - 列表出团区间与排序:Issue #7770 - 统计条被过滤条数:Issue #7939 --- ## 关联 / 联系人 - 后端:jw - 前端:mmg