docs(changelog): #7939 团期看板统计条回带 effectiveScope 与 filteredOutCount(修改接口)
changelog-filename-gate / validate (push) Failing after 1s
changelog-filename-gate / validate (push) Failing after 1s
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
这个提交包含在:
@@ -0,0 +1,302 @@
|
||||
---
|
||||
schema: "hl-changelog/v2"
|
||||
ticket: "7939"
|
||||
title: "团期看板统计条回带生效班期范围 effectiveScope 与被过滤条数 filteredOutCount"
|
||||
consumer: "admin"
|
||||
author: "jw(GIT)"
|
||||
change_type: "修改接口"
|
||||
backend_status: "deployed"
|
||||
gateway_status: "verified"
|
||||
frontend_status: "pending"
|
||||
frontend_owner: "mmg"
|
||||
frontend_ref: ""
|
||||
target_release: ""
|
||||
verified_at: ""
|
||||
status_note: "团期看板统计条 GET /v3/admin/order/group-batch/summary 出参纯新增 effectiveScope(本次生效的班期范围 ONGOING/FINISHED/ALL)与 filteredOutCount(被范围过滤掉的班期数,与 total 同一套 productId/month/keyword 筛选,ALL 时恒 0);入参、既有 5 个字段、错误码、scope 缺省值均不变。解决「产品卡片 10 期、统计条共 8 期」同屏打架且无解释的问题。后端已合并 dev-v3(129de0292)并部署 TEST,网关实测 29/29 通过。前端待办:effectiveScope≠ALL 且 filteredOutCount>0 时,在统计条旁提示「仅显示未结束班期(另有 N 期已结束)」,见第四节。"
|
||||
updated_at: "2026-09-18"
|
||||
base: "dev-v3"
|
||||
---
|
||||
|
||||
# order-v3: 团期看板统计条回带生效班期范围与被过滤条数
|
||||
|
||||
> **存放目录**: `changelogs-v2/{YYYY-MM}/`(管理后台,二期 order-v3)
|
||||
>
|
||||
> **服务**: hl-order-service-v3 (端口 8086)
|
||||
> **PR**: #7941
|
||||
> **Issue**: #7939
|
||||
> **日期**: 2026-09-18
|
||||
> **影响范围**: 管理后台「团期订单」页面的统计条(班期总览 / 状态页签计数)
|
||||
|
||||
---
|
||||
|
||||
## ⚠️ 关键变化
|
||||
|
||||
1. **统计条接口多返回两个字段**:`effectiveScope`(本次实际按哪个班期范围统计)和 `filteredOutCount`(被这个范围过滤掉了几期)。
|
||||
2. **只加字段,不改原有字段**,也不改 `scope` 的缺省规则:带 `productId` 不传 `scope` 时仍只统计**未结束**班期。
|
||||
3. **前端待办**:结果集被时间过滤时给出提示,避免运营把「共 8 期」当成「这个产品只有 8 期」,见第四节。
|
||||
|
||||
---
|
||||
|
||||
## 一、背景
|
||||
|
||||
「团期订单」页面同一屏出现两个互相矛盾的班期数:左侧产品卡片写「王骁测试团期产品 **10 期**」,右侧班期总览写「共 **8 期**」、状态页签「全部期 **8**」,界面上没有任何解释。
|
||||
|
||||
原因是统计条接口在 `productId` 有值且不传 `scope` 时,缺省只统计未结束班期(`end_date >= 今天` 或未填返团日),已结束的 2 期被排除;而响应里没有任何字段说明「结果被按时间过滤过」,前端无从提示。2026-09-18 测试环境造数时当事人第一反应是「数据丢了」。
|
||||
|
||||
本次**只让过滤变得可见**,缺省只看未结束班期是刻意设计,保持不变。
|
||||
|
||||
---
|
||||
|
||||
## 二、变更接口清单
|
||||
|
||||
| # | 接口 | 方法 | 路径 | 变更类型 | 说明 |
|
||||
|---|------|------|------|----------|------|
|
||||
| 1 | 团期看板统计条 | GET | `/v3/admin/order/group-batch/summary` | 修改 | 出参新增 `effectiveScope`、`filteredOutCount` |
|
||||
|
||||
---
|
||||
|
||||
## 三、接口详情
|
||||
|
||||
### 1. 团期看板统计条 `GET /v3/admin/order/group-batch/summary`
|
||||
|
||||
**VO**: `Result<GroupBatchSummaryVO>`(新增 `effectiveScope`、`filteredOutCount`)
|
||||
|
||||
#### 使用场景
|
||||
|
||||
管理后台「团期订单」页面顶部统计条:班期总数、8 个运营阶段页签计数、子订单数、成团/满团门槛副标题。本次新增的两个字段用于在结果集被班期范围过滤时给出提示。
|
||||
|
||||
#### 入参
|
||||
|
||||
本次入参**不变**,原样列出供核对。
|
||||
|
||||
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|
||||
|------|------|------|------|------|------|
|
||||
| productId | Query | Long | 否 | 产品 ID | 缺省不限。**有值时 `scope` 缺省为 `ONGOING`** |
|
||||
| month | Query | String | 否 | `yyyy-MM`,非法值忽略 | 按出团日落月 |
|
||||
| keyword | Query | String | 否 | trim 后包含匹配 | 团期编号 / 名称模糊 |
|
||||
| scope | Query | String | 否 | `ONGOING` / `FINISHED` / `ALL`,非法值按缺省 | 缺省:`productId` 有值→`ONGOING`,否则→`ALL` |
|
||||
|
||||
#### 出参
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| total | Integer | 不变。当前范围内的班期总数(= 8 桶之和) |
|
||||
| buckets | Map<String,Integer> | 不变。8 桶固定全键 |
|
||||
| subOrderCount | Integer | 不变。活跃子订单合计 |
|
||||
| minToForm | Integer | 不变。成团门槛(唯一才给,否则 null) |
|
||||
| maxRooms | Integer | 不变。满团门槛(唯一才给,否则 null) |
|
||||
| effectiveScope | String | **新增**。本次实际生效的班期范围:`ONGOING` / `FINISHED` / `ALL`。不传 `scope` 时由 `productId` 决定;传了非法值时是回落后的缺省值 |
|
||||
| filteredOutCount | Integer | **新增**。被本次范围过滤掉的班期数。与 `total` 使用**同一套** `productId` / `month` / `keyword` 条件,所以恒有 `total + filteredOutCount = 同条件 scope=ALL 的 total`;`effectiveScope=ALL` 时恒为 0;永不为 null |
|
||||
|
||||
#### 请求示例
|
||||
|
||||
```http
|
||||
GET /v3/admin/order/group-batch/summary?productId=2100839045562077186
|
||||
Authorization: Bearer <token>
|
||||
```
|
||||
|
||||
#### 响应示例
|
||||
|
||||
(测试服真实响应,2026-09-18;该产品 10 个班期,其中 08-20~08-22、09-03~09-05 两期已结束)
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"message": "成功",
|
||||
"data": {
|
||||
"total": 8,
|
||||
"buckets": {
|
||||
"RECRUIT": 8,
|
||||
"FORMED": 0,
|
||||
"PENDING_TRIP": 0,
|
||||
"TRAVELLING": 0,
|
||||
"TRIP_FINISHED": 0,
|
||||
"AUDITING": 0,
|
||||
"CHECKED": 0,
|
||||
"DISBANDED": 0
|
||||
},
|
||||
"subOrderCount": 0,
|
||||
"minToForm": 0,
|
||||
"maxRooms": 0,
|
||||
"effectiveScope": "ONGOING",
|
||||
"filteredOutCount": 2
|
||||
},
|
||||
"success": true
|
||||
}
|
||||
```
|
||||
|
||||
同一产品显式传 `scope=ALL`:
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"message": "成功",
|
||||
"data": {
|
||||
"total": 10,
|
||||
"buckets": { "RECRUIT": 10, "FORMED": 0, "PENDING_TRIP": 0, "TRAVELLING": 0, "TRIP_FINISHED": 0, "AUDITING": 0, "CHECKED": 0, "DISBANDED": 0 },
|
||||
"subOrderCount": 0,
|
||||
"minToForm": 0,
|
||||
"maxRooms": 0,
|
||||
"effectiveScope": "ALL",
|
||||
"filteredOutCount": 0
|
||||
},
|
||||
"success": true
|
||||
}
|
||||
```
|
||||
|
||||
#### 空数据 / 降级响应
|
||||
|
||||
产品名下没有任何班期时,两个新字段照常返回,不为 null(测试服真实响应,产品 `2045486260364996609`):
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"message": "成功",
|
||||
"data": {
|
||||
"total": 0,
|
||||
"buckets": { "RECRUIT": 0, "FORMED": 0, "PENDING_TRIP": 0, "TRAVELLING": 0, "TRIP_FINISHED": 0, "AUDITING": 0, "CHECKED": 0, "DISBANDED": 0 },
|
||||
"subOrderCount": 0,
|
||||
"minToForm": null,
|
||||
"maxRooms": null,
|
||||
"effectiveScope": "ONGOING",
|
||||
"filteredOutCount": 0
|
||||
},
|
||||
"success": true
|
||||
}
|
||||
```
|
||||
|
||||
#### 错误响应
|
||||
|
||||
```json
|
||||
{ "code": 589507, "message": "无操作权限(非团期管理员 / 非本定制师名下)", "data": null }
|
||||
```
|
||||
|
||||
| code | 触发条件 |
|
||||
|------|----------|
|
||||
| 589507 | 没有团期列表查看权限(`group-batch:list`) |
|
||||
| 401 | 未登录(网关拦截) |
|
||||
|
||||
与改动前相同,没有新增错误码。
|
||||
|
||||
#### 业务边界
|
||||
|
||||
- 只读接口,无事务、无写库;判权与改动前一致。
|
||||
- `filteredOutCount` 只统计**时间范围**这一个维度过滤掉的条数;`month` / `keyword` 过滤掉的不算在内。
|
||||
- 状态未知的脏数据行既不计入 `total`,也不计入 `filteredOutCount`,两者可直接相加。
|
||||
- `productId` 有值时第二次统计是纯内存计算(产品班期只拉一次);`productId` 缺省且 `scope≠ALL` 时多一次数据库查询。
|
||||
- 同一请求内「今天」只取一次,两次统计的范围判定口径一致。
|
||||
|
||||
---
|
||||
|
||||
## 四、契约约束与正确调用方式
|
||||
|
||||
### 前端展示建议
|
||||
|
||||
| 条件 | 建议提示 |
|
||||
|------|----------|
|
||||
| `effectiveScope === 'ONGOING' && filteredOutCount > 0` | 统计条旁:「仅显示未结束班期(另有 {filteredOutCount} 期已结束)」,可带「查看全部」切到 `scope=ALL` |
|
||||
| `effectiveScope === 'FINISHED' && filteredOutCount > 0` | 「仅显示已结束班期(另有 {filteredOutCount} 期未结束)」 |
|
||||
| `effectiveScope === 'ALL'` 或 `filteredOutCount === 0` | 不提示 |
|
||||
|
||||
### ✅ 正确 / ❌ 错误 用法
|
||||
|
||||
| 场景 | 做法 |
|
||||
|------|------|
|
||||
| ✅ 判断统计结果有没有被时间过滤 | 看 `effectiveScope` 与 `filteredOutCount`,不要自己按「是否传了 scope」推断 |
|
||||
| ✅ 得到该产品在当前筛选下的全部期数 | `total + filteredOutCount` |
|
||||
| ❌ 把 `total` 当成「这个产品一共有多少期」 | 缺省只统计未结束班期,要么提示,要么显式传 `scope=ALL` |
|
||||
| ❌ 用 `filteredOutCount` 解释 `month` / `keyword` 的差额 | 它只含时间范围过滤掉的条数 |
|
||||
|
||||
---
|
||||
|
||||
## 六、边界行为
|
||||
|
||||
- 产品有已结束班期、不传 `scope` → `effectiveScope=ONGOING`,`filteredOutCount` = 已结束期数。
|
||||
- 显式 `scope=ALL` → `filteredOutCount=0`。
|
||||
- 显式 `scope=FINISHED` → `filteredOutCount` = 未结束期数。
|
||||
- 产品无班期 → `total=0`、`filteredOutCount=0`,均不为 null。
|
||||
- 全部班期都已结束且不传 `scope` → `total=0`,`filteredOutCount` = 全部期数。
|
||||
- 返团日是今天的班期算「未结束」;没填返团日的班期也算「未结束」。
|
||||
- `scope` 传非法值 → 按缺省处理(原行为),`effectiveScope` 返回回落后的值。
|
||||
- 无权限 → 589507;未登录 → 401(与改动前相同)。
|
||||
|
||||
---
|
||||
|
||||
## 六.6、修改前后对比
|
||||
|
||||
### 字段级对比
|
||||
|
||||
| 字段 | 改前 | 改后 |
|
||||
|------|------|------|
|
||||
| `effectiveScope` | 无 | 新增,String |
|
||||
| `filteredOutCount` | 无 | 新增,Integer,永不为 null |
|
||||
| `total` / `buckets` / `subOrderCount` / `minToForm` / `maxRooms` | — | 不变(TEST 18 组请求改前改后逐字比对一致) |
|
||||
| 入参 4 个 | — | 不变 |
|
||||
|
||||
### 行为级对比
|
||||
|
||||
| 行为 | 改前 | 改后 |
|
||||
|------|------|------|
|
||||
| 结果被按时间过滤时 | 响应无任何标识,前端无法提示 | 响应声明生效范围与被过滤条数 |
|
||||
| `scope` 缺省规则 | `productId` 有值→ONGOING,否则→ALL | 不变 |
|
||||
| 查询次数 | 1 次 | `scope=ALL` 时仍 1 次;否则 `productId` 场景多一次内存筛选,`productId` 缺省场景多一次 DB 查询 |
|
||||
|
||||
## 六.7、影响评估
|
||||
|
||||
- **是否破坏向后兼容**: 否,只新增字段。
|
||||
- **前端是否必须同步上线**: 否,不接入也不会报错;但需要前端接入后,页面才会出现提示,「10 期 / 8 期」的困惑才会消失。
|
||||
- **回滚**: 回滚 PR #7941 即可,无数据变更。
|
||||
|
||||
---
|
||||
|
||||
## 七、不影响范围
|
||||
|
||||
- **仅影响**: `GET /v3/admin/order/group-batch/summary` 的响应(新增 2 个字段)。
|
||||
- **零影响**:
|
||||
- `GET /v3/admin/order/group-batch/board`(缺省仍为 `ALL`,与 summary 缺省不一致的问题另行立项)
|
||||
- `GET /v3/admin/order/group-batch/export`(与 summary 共用缺省规则,导出同样只含未结束班期且无说明,另行立项)
|
||||
- GB-ADM-001 团期分页列表
|
||||
- 数据库:无表结构变更,只读
|
||||
|
||||
---
|
||||
|
||||
## 八、测试环境已验证
|
||||
|
||||
被测版本:hl-order-service-v3 = dev-v3 `129de0292`(2026-09-18 15:41 部署,双实例滚动完成)。取证前确认面板检出 `dev-v3 @ 129de0292`、`hl-order-service-v3` 与 `hl-gateway` 均 running,且新字段在线。网关 `https://api.test.1814.love:9443` 实测 **29/29** 通过。
|
||||
|
||||
```
|
||||
AC-1 productId=2100839045562077186 不传 scope → effectiveScope=ONGOING total=8 filteredOutCount=2;8+2 = ALL 的 10 ✓
|
||||
AC-2 同产品 scope=ALL → effectiveScope=ALL filteredOutCount=0 ✓;scope=FINISHED → total=2 filteredOutCount=8 ✓
|
||||
AC-3 productId + month=2026-09 → 0+1 = ALL 的 1 ✓
|
||||
productId + keyword=Q20260 → 0+2 = ALL 的 2 ✓
|
||||
productId + month=2026-10 → 4+0 = ALL 的 4 ✓
|
||||
productId + month=2026-08 + keyword → 0+1 = ALL 的 1 ✓
|
||||
无 productId month=2026-09 ONGOING → 9+8 = ALL 的 17 ✓
|
||||
无 productId keyword=GB ONGOING → 7+0 = ALL 的 7 ✓
|
||||
无 productId month=2026-09 FINISHED → 8+9 = ALL 的 17 ✓
|
||||
AC-4 0 班期产品 2045486260364996609 → total=0 filteredOutCount=0 effectiveScope=ONGOING ✓
|
||||
AC-5 18 组请求(各 scope × 有无 productId × month/keyword)改前改后既有 5 字段逐字一致 ✓
|
||||
```
|
||||
|
||||
本地:`GroupBatchBoardStatsServiceTest` 33/0/0(新增 9 例);order-v3 全量 Half A 7849/0/0、Half B 3964/F1(`MapperBoundaryArchTest.non_refund_not_depend_on_refund_mapper`,干净 dev-v3 `846998c4b` 上同样失败,既存,与本次无关)。
|
||||
|
||||
---
|
||||
|
||||
## 十、相关文档
|
||||
|
||||
- 关联 Issue: [wx/HL#7939](https://git.1814.love:8443/wx/HL/issues/7939)
|
||||
- 关联 PR: [wx/HL#7941](https://git.1814.love:8443/wx/HL/pulls/7941)
|
||||
- 缺省范围单源:`GroupBatchScopeResolver.defaultFor`(#7189)
|
||||
|
||||
## 关联 / 联系人
|
||||
|
||||
### 链接
|
||||
|
||||
- **Issue**: [#7939](https://git.1814.love:8443/wx/HL/issues/7939)
|
||||
- **PR**: [#7941](https://git.1814.love:8443/wx/HL/pulls/7941)
|
||||
- **Merge commit**: [129de0292](https://git.1814.love:8443/wx/HL/commit/129de0292)
|
||||
|
||||
### 联系人
|
||||
|
||||
- **后端负责人**: @jw
|
||||
- **前端负责人**: @mmg
|
||||
在新工单中引用
屏蔽一个用户