changelog(7536): 团期接口返回值整改·破坏批(A3 分页信封+删5冗余字段+财务金额字符串,修改接口,管理后台)
changelog-filename-gate / validate (push) Failing after 2s

Refs #7536 PR #7688
这个提交包含在:
jw
2026-09-14 16:55:29 +08:00
父节点 5fa9fdae6b
当前提交 22d34c8023
@@ -0,0 +1,367 @@
---
schema: "hl-changelog/v2"
ticket: "7536"
title: "团期接口返回值整改·破坏批:A3 子订单列表改分页信封 + 删 5 冗余字段 + include* 默认开 + 财务 12 金额转字符串 + 3 逐行 VO 补团号"
consumer: "admin"
author: "jw(GIT)"
change_type: "修改接口"
backend_status: "deployed"
gateway_status: "verified"
frontend_status: "pending"
frontend_owner: "mmg"
frontend_ref: "#7536"
target_release: ""
verified_at: "2026-09-14"
status_note: "破坏性变更,前端必须整页面批量切换、提前协调。A3『报名清单』tab 主数据源 `GET .../orders` 从裸数组改标准分页信封 `Result<PageResult<T>>`(新增 page/pageSize,默认首页 20 条并带 total),且 includeNeeds/includeTravelers 缺省由 false 改 true(房型/房数/出行人默认返回);同时删除 5 个冗余字段(estimatedCost/adultCount/childCount/youngChildCount/babyCount,人数改用 participantCount + tierName)。财务 tab `GET .../finance` 的 12 个金额字段 JSON 形态由 number 改 string(与报名清单 tab 对齐)。A3/财务/预支三个逐行 VO 各补一个 teamNo(本户团号,逐户不同,未付订金为 null)。零 DDL、零网关路由改动。**注:groupChatUnreadCount 本轮保留不删——hl-ui 全文搜索零命中但未取得 mmg 书面回执,按工单 AC-1 兜底口径保留该字段。**"
updated_at: "2026-09-14"
base: "dev-v3"
---
# order-v3: 团期接口返回值整改·破坏批(A3 子订单列表 / 财务 / 预支)
**服务**: hl-order-service-v3
**PR**: #7688
**Issue**: #7536
---
## ⚠️ 关键变化
🔴 **破坏性变更,前端必须整页面批量切换(不提供兼容期双形态返回)**:
1. **A3 子订单列表 `GET .../orders` 返回包装从裸数组改分页信封**:`Result<List<GroupBatchOrderItemRespVO>>` → `Result<PageResult<GroupBatchOrderItemRespVO>>`。`data` 由裸数组变为 `{records, total, page, pageSize}`;新增 `page`/`pageSize` 查询参数(默认首页 20 条、`pageSize` 上限 200)。老前端读 `data.length` 会拿到 `undefined`、列表整个空白——**这是本单最危险的失败形态**。
2. **删除 5 个冗余字段**(`GroupBatchOrderItemRespVO`):`estimatedCost`(无写入点、恒 null)、`adultCount`/`childCount`/`youngChildCount`/`babyCount`(人数四件套)。前端毛利列删除、人数改用 `participantCount`(总数)+ `tierName`/`tierCode`(档位)。
3. **`includeNeeds` / `includeTravelers` 缺省值由 `false` 改为 `true`**:`roomCount`/`roomType`/`specialNeeds` 与 `travelers[]` 默认返回;前端要精简响应须显式传 `false`。
4. **财务 tab `GET .../finance` 的 12 个 `BigDecimal` 金额字段 JSON 形态 number → string**(补 `@JsonSerialize(using = ToStringSerializer.class)`,与报名清单 tab 对齐)。前端勿按 number 解析这些字段。
5. **A3 / 财务 / 预支三个逐行 VO 各补一个 `teamNo`**(纯加):本户团号(`order_main.team_no`,形如 `26-0001`),**逐户各不相同**,该户订金未支付成功时为 `null`(不返空串、不回退 `orderNo`)。
🟢 **纯加(已随零破坏批 PR-1 先行合入,本单一并归档)**:A3 子订单项补 6 个中文配对字段(`roomTypeName` + `payStatusName`/`contractStatusName`/`insuranceStatusName`/`hotelRequirementStatusName`/`vehicleRequirementStatusName`)。
**无 DDL、无 Flyway、无网关路由改动(`/v3/admin/order/**` 既有覆盖)、无新增错误码。**
## 一、背景
团期控制台(hl-ui 管理后台)「团期详情 → 报名清单 / 财务」两个 tab 有三处肉眼可见坏结果:页头「子订单 N 户」恒显示 0(列表接口无 `total`)、报名清单默认缺「房型/房数」「出行人」两列(藏在 opt-in 开关后)、报名清单多给了「预计毛利」「会话未读」两列(原型没有)。口径来源 `docs/group/团期闭环整改方案-v1.1.md` §6,判据是 wx 定的三条「原型有的必须有;可以多给;不能给离谱的」。本单是三批整改中的**破坏批**,独占「团期子订单列表 A3」「团期财务总览」「团期预支记录」三个端点的全部改动。
## 二、变更接口清单
| # | 接口 | 方法 | 路径 | 变更类型 | 说明 |
|---|------|------|------|----------|------|
| 1 | 团期下子订单列表(报名清单 tab) | GET | `/v3/admin/order/group-batch/:groupBatchId/orders` | 修改 | 裸数组→分页信封 `PageResult`;新增 page/pageSize;include* 默认改 true;删 5 冗余字段;补 teamNo |
| 2 | 团期财务总览(财务 tab) | GET | `/v3/admin/order/group-batch/:groupBatchId/finance` | 修改 | 12 个金额字段 JSON number→string;逐户行补 teamNo |
| 3 | 团期预支记录 | GET | `/v3/admin/order/group-batch/:groupBatchId/advances` | 修改 | 预支逐行补 teamNo(团期级行 scope=GROUP_BATCH 恒 null) |
## 三、接口详情
### 1. 团期下子订单列表 `GET /v3/admin/order/group-batch/:groupBatchId/orders`
**VO**: `Result<PageResult<GroupBatchOrderItemRespVO>>`(path: groupBatchId;query: page / pageSize / includeTravelers / includeNeeds / includeCancelled)
#### 使用场景
团期详情页「报名清单」tab 主数据源。改造后默认返首页 20 条并带 `total`,房型/房数/出行人默认返回,6 个裸 code 带中文配对,逐户带团号。沿用既有鉴权 `GroupBatchPermissionGuard.PERMISSION_VIEW` 与网关路由。
#### 入参字段表
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|---|---|---|---|---|---|
| groupBatchId | path | string(Long) | 是 | 雪花 ID | 团期主订单 ID(运营侧团期主键,不是 productBatchId) |
| page | query | int | 否 | 缺省 1;<1 归一为 1 | ★新增。页码,从 1 起 |
| pageSize | query | int | 否 | 缺省 20;<1 归一为 20;>200 截断为 200 | ★新增。每页条数 |
| includeTravelers | query | boolean | 否 | ★默认 `false`→`true` | 是否附 `travelers[]`(证件号/手机号任何情况不返回) |
| includeNeeds | query | boolean | 否 | ★默认 `false`→`true` | 是否附 `roomCount`/`roomType`/`specialNeeds` |
| includeCancelled | query | boolean | 否 | 缺省 `false`(不变) | 是否含已取消子订单(只返活跃集,剔除 CANCELLED) |
#### 出参字段表
| 字段 | 类型 | 说明 |
|---|---|---|
| data | object | ★改前是裸数组,改后是 `PageResult` 信封 |
| data.records | array | 本页子订单行(`GroupBatchOrderItemRespVO`) |
| data.total | int | ★整团活跃子订单总数(含未在本页的) |
| data.page / data.pageSize | int | ★当前页码 / 每页条数 |
| records[].orderId | string(Long) | 子订单 ID(字符串防精度丢失) |
| records[].orderNo | string | 订单编号 |
| records[].teamNo | string | ★本户团号(`order_main.team_no`),逐户不同,未付订金为 null |
| records[].customerName | string | 客户姓名 |
| records[].participantCount | int | 出行人总数(= adult+child+youngChild+baby) |
| records[].tierCode / tierName | string | 档位码 / 档位名 |
| records[].payStatus / payStatusName | string | 支付状态 code / 中文(★Name 纯加) |
| records[].contractStatus / contractStatusName | string | 合同状态 code / 中文(无合同时均 null) |
| records[].insuranceStatus / insuranceStatusName | string | 保险状态 code / 中文(无保险时均 null) |
| records[].hotelRequirementStatus / hotelRequirementStatusName | string | 房需求状态 code / 中文 |
| records[].vehicleRequirementStatus / vehicleRequirementStatusName | string | 车需求状态 code / 中文 |
| records[].paidAmount / balanceAmount / totalPrice | string(decimal) | 已付 / 待收尾款 / 本户应收(字符串两位小数) |
| records[].roomCount / roomType / roomTypeName / specialNeeds | int/string | ★默认返回;roomTypeName 按「、」逐段翻译再拼回 |
| records[].travelers | array | ★默认返回;`name`/`type`/`age`/`birthdayInTrip`(无证件号/手机号) |
| ~~records[].estimatedCost / adultCount / childCount / youngChildCount / babyCount~~ | — | ★**已删除**(毛利改核单页,人数改用 participantCount + tierName) |
#### 请求示例
```http
GET /v3/admin/order/group-batch/2096412454643802114/orders?page=1&pageSize=20
Authorization: Bearer <admin token>
```
#### 响应示例
```json
{
"code": 200,
"message": "success",
"success": true,
"traceId": "…",
"data": {
"records": [
{
"orderId": "2000000001",
"orderNo": "HL20260601001",
"teamNo": "26-0001",
"customerName": "王先生家庭",
"participantCount": 4,
"tierCode": "2A1C",
"tierName": "2成人1儿童",
"payStatus": "DEPOSIT_PAID",
"payStatusName": "已付定金",
"paidAmount": "3000.00",
"balanceAmount": "9000.00",
"totalPrice": "12000.00",
"roomCount": 2,
"roomType": "FAMILY",
"roomTypeName": "家庭房",
"specialNeeds": "需要婴儿床",
"travelers": [ { "name": "王小明", "type": "CHILD", "age": 6, "birthdayInTrip": false } ]
}
],
"total": 55,
"page": 1,
"pageSize": 20
}
}
```
#### 空数据 / 降级响应
团期无任何活跃子订单 → `data` 为 `{"records":[],"total":0,"page":1,"pageSize":20}`(**不返 `data:null`**)。`page` 超末页 → `records` 为空数组、`total` 仍为真实总数、`code=200` 不抛异常(前端据 `total` 回退首页)。字典不可达时 `roomTypeName` 回落 `roomType` 原值。
#### 错误响应
```json
{ "code": 589500, "message": "团期不存在", "data": null }
```
团期不存在 → **589500**(`GROUP_BATCH_NOT_FOUND`);无 `group-batch:view` → **589507**(`GROUP_BATCH_PERMISSION_DENIED`)。分页参数越界**不产生错误码**(归一/截断)。
#### 业务边界
- 分页在**内存内**做(切片在 `assembleSubOrders` 装配之后、事务外);不下推 SQL——数据源经 OrderService 跨聚合逐单装配,下推会打散成 N+1,且 `@TableLogic` 与 CANCELLED 过滤都在内存侧。团期子订单 N 上界为单团满员名额,本端点页面级低频。
- `page<1` 归一为 1;`pageSize<1` 归一为 20、`>200` 截断为 200(读接口对越界宽容,不抛异常)。
- `includeTravelers`/`includeNeeds` 传 null 按 true;`includeCancelled` 传 null 按 false。
- 保留一个**不分页的内部全量方法**(`listSubOrders(id, boolean, boolean, boolean)`)供 #7533 团级文档出全团名册;HTTP 入口才走分页(复审修正 1)。
### 2. 团期财务总览 `GET /v3/admin/order/group-batch/:groupBatchId/finance`
**VO**: `Result<GroupBatchFinanceRespVO>`(path: groupBatchId)
#### 使用场景
团期详情页「财务」tab(GB-ADM-040:四张金额卡 + 逐户付款 + 整团合计)。本次只改**金额字段 JSON 形态**(number→string)与逐户行补 `teamNo`,其余字段名与语义全部不变。权限 `GroupBatchPermissionGuard.PERMISSION_FINANCE_VIEW`(比 view 更严)。
#### 入参字段表
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|---|---|---|---|---|---|
| groupBatchId | path | string(Long) | 是 | 雪花 ID | 团期主订单 ID |
#### 出参字段表
| 字段 | 类型 | 说明 |
|---|---|---|
| receivableAmount / receivedAmount / unpaidAmount | string(decimal) | ★形态 number→string。整团应收 / 已收 / 待收 |
| advanceApproved / advancePending / advanceAvailable | string(decimal) | ★形态 number→string。已预支 / 待审批 / 可支取余额 |
| totals.totalPrice / totals.paidAmount / totals.unpaidAmount | string(decimal) | ★形态 number→string。整团合计三列 |
| items[].totalPrice / items[].paidAmount / items[].unpaidAmount | string(decimal) | ★形态 number→string。逐户应收 / 已付 / 待收 |
| items[].teamNo | string | ★纯加。本户团号,逐户不同,未付订金为 null |
| withdrawnCount | int | 不变(Integer) |
| primaryPayeeName / secondaryPayeeName | string | 不变 |
#### 请求示例
```http
GET /v3/admin/order/group-batch/2096412454643802114/finance
Authorization: Bearer <admin token>
```
#### 响应示例
```json
{
"code": 200, "message": "success", "success": true, "traceId": "…",
"data": {
"receivableAmount": "40200.00",
"receivedAmount": "32900.00",
"unpaidAmount": "7300.00",
"advanceApproved": "0.00",
"advancePending": "0.00",
"advanceAvailable": "7300.00",
"withdrawnCount": 0,
"primaryPayeeName": "李雯",
"secondaryPayeeName": null,
"totals": { "totalPrice": "40200.00", "paidAmount": "32900.00", "unpaidAmount": "7300.00" },
"items": [
{ "orderId": "770145", "orderNo": "GT-26-0081", "teamNo": "26-0001",
"customerName": "罗敏", "consultantName": "李雯",
"totalPrice": "12300.00", "paidAmount": "12300.00", "unpaidAmount": "0.00" }
]
}
}
```
#### 空数据 / 降级响应
无活跃子订单团期 `items` 为空数组、四张金额卡为 `"0.00"`(字符串);金额字段恒非 null(`BigDecimal.ZERO` 参与计算)。未付订金的逐户行 `teamNo` 为 null。
#### 错误响应
```json
{ "code": 589507, "message": "无权限", "data": null }
```
团期不存在 → 589500;无 `group-batch:finance:view` → 589507。
#### 业务边界
- 12 个金额字段(顶层 6 + `totals` 3 + `items[]` 3)JSON 形态统一为**带双引号的字符串**;`withdrawnCount`(Integer)不动。本次只消除同页两个 tab 金额形态不一致,**不作通用精度保证**(按分折整数 ≤1e12 在 2^53 内不丢量级,但算术会累积误差)。
- `items[].teamNo` 逐户取值(同一次 `orderService.selectBatchByIds`,零新增查询),不是全团共用一个值。
### 3. 团期预支记录 `GET /v3/admin/order/group-batch/:groupBatchId/advances`
**VO**: `Result<List<GroupBatchAdvanceItemVO>>`(path: groupBatchId;倒序、不分页)
#### 使用场景
团期详情页预支记录列表。本次只在逐行补 `teamNo`(纯加),其余不变。权限 `GroupBatchPermissionGuard.PERMISSION_FINANCE_VIEW`。
#### 入参字段表
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|---|---|---|---|---|---|
| groupBatchId | path | string(Long) | 是 | 雪花 ID | 团期主订单 ID |
#### 出参字段表
| 字段 | 类型 | 说明 |
|---|---|---|
| data | array | 预支记录逐行(倒序、不分页;裸数组,非分页信封) |
| data[].orderNo | string | 归属订单号(团期级行 scope=GROUP_BATCH 为 null) |
| data[].teamNo | string | ★纯加。归属订单团号;scope=GROUP_BATCH 团期级行恒 null(与 orderNo 同规则) |
| data[].scope | string | `ORDER` / `GROUP_BATCH` |
| data[].amount / status 等 | — | 不变 |
#### 请求示例
```http
GET /v3/admin/order/group-batch/2096412454643802114/advances
Authorization: Bearer <admin token>
```
#### 响应示例
```json
{
"code": 200, "message": "success", "success": true, "traceId": "…",
"data": [
{ "orderNo": "GT-26-0081", "teamNo": "26-0001", "scope": "ORDER", "amount": "500.00" },
{ "orderNo": null, "teamNo": null, "scope": "GROUP_BATCH", "amount": "1000.00" }
]
}
```
#### 空数据 / 降级响应
无预支记录返空 `records`。`scope=GROUP_BATCH` 的团期级行没有归属订单,`orderNo` 与 `teamNo` **同时为 null**(前端按 scope 判断渲染,勿把团期级行的空团号当异常)。
#### 错误响应
```json
{ "code": 589507, "message": "无权限", "data": null }
```
团期不存在 → 589500;无 `group-batch:finance:view` → 589507。
#### 业务边界
- `teamNo` 与 `orderNo` 从**同一张** `Map<Long, OrderInfo>`(同一次 `selectBatchByIds`)取,不新增查询;`activeIds` 为空时一次都不查。
- 团期级行(`orderId == null`)`teamNo` 恒 null,与既有 `orderNo` 处理同规则。
## 四、契约约束与正确调用方式
- **A3 分页**:`data` 是 `PageResult` 信封,读 `data.records`(不是 `data`);`data.total` 为整团活跃总数、`data.records.length` 只是本页条数。不传 `page`/`pageSize` 返首页 20 条。
- **金额字符串**:A3 的 `paidAmount`/`balanceAmount`/`totalPrice` 与财务 tab 的 12 个金额字段均为字符串,勿按 number 解析;`orderId`/`groupBatchId` 亦为字符串防精度丢失。
- **teamNo 语义**:本户团号、逐户不同、订金支付成功后才生成;未付订金/团期级行为 `null`——**前端不得**回退成 `orderNo`、填空串或拿团期侧编号顶替(否则无法区分「未付订金」与「已生成」)。
- **默认视图**:报名清单默认已带房型/房数/出行人(无需再传 `includeNeeds=true`/`includeTravelers=true`);毛利列与人数四件套字段已删除,人数改用 `participantCount` + `tierName`/`tierCode`。
## 五、数据库行为
- 无表变更、无 Flyway、无新增列、无索引调整、无 H2 `*-schema.sql` 改动。
- 分页在内存做(`assembleSubOrders` 返回全量后切片),**无 SQL 变更**;`teamNo` 取自已取全列的订单对象,无新增查询与投影。
## 六、边界行为
- 分页参数越界一律归一/截断、HTTP 与 `code` 均 200,不新增错误码。
- 空团期返 `{records:[],total:0}` 而非 `data:null`。
- `groupChatUnreadCount` 字段**保留**:hl-ui 全文搜索 `groupChatUnreadCount` 零命中,但未取得 mmg 书面回执,按工单 AC-1 兜底口径「拿不到回执则该字段保留、其余五个冗余字段照删」。
- 复用既有错误码 589500 / 589507,本单错误码配额为「无新增」。
## 六.6、修改前后对比
| 端点 / 字段 | 改前 | 改后 |
|---|---|---|
| A3 `data` 包装 | 裸数组 `List<GroupBatchOrderItemRespVO>` | 分页信封 `PageResult`:`{records,total,page,pageSize}` |
| A3 查询参数 | 无 page/pageSize | 新增 `page`(默认 1)/`pageSize`(默认 20,上限 200) |
| A3 `includeNeeds` / `includeTravelers` 默认 | `false` | **`true`**(房型/房数/出行人默认返回) |
| A3 字段 | 含 `estimatedCost`/`adultCount`/`childCount`/`youngChildCount`/`babyCount` | **删 5 个**(人数改 `participantCount` + `tierName`/`tierCode`;毛利改核单页) |
| A3 逐行 | 无 `teamNo` | 补 `teamNo`(逐户不同,未付订金 null) |
| 财务顶层 6 + `totals` 3 + `items[]` 3 金额 | JSON `number`(如 `40200.00`) | JSON `string`(如 `"40200.00"`) |
| 财务逐户行 | 无 `teamNo` | 补 `teamNo` |
| 预支逐行 | 无 `teamNo` | 补 `teamNo`(团期级行 null) |
(🟢 A3 的 6 个中文配对字段与三处 teamNo **装配**随零破坏批 PR-1 先行合入,此表为本单破坏批与 PR-1 合并后的整体前后对比。)
## 六.7、影响评估
- **最危险失败形态**:老前端读 `data.length`(原裸数组)改后拿到 `undefined`,报名清单整个空白。缓解:本 changelog 先行 + 拿 mmg 「已知悉、将同步改前端」回执;后端上线后做**后端契约回归**(断言 `records`/`total`/分页字段),**页面回归由 `frontend_status` 单独跟踪、不作为后端关单条件**(复审修正 2)。不提供兼容期双形态返回。
- **金额形态 number→string**:前端解析财务 tab 的 12 个金额字段与 A3 三个金额字段的代码需改(不能直接当数字算术)。
- **删字段**:前端「预计毛利」列删除;人数四件套的直接引用改用 `participantCount`(总数)+ `tierName`/`tierCode`(档位)。
- **teamNo null 分支**:前端逐户/逐行按 `null` 渲染,**不得兜底**成 `orderNo`/空串/团期编号。
- **回滚**:后端零 DDL、零网关路由、零新增错误码,回滚只需回退代码分支;`#7533` 团级文档经保留的内部全量方法读取,不受本单分页影响。
## 七、不影响范围
- `GroupBatchService.java` 零行改动;订单级端点、团期列表/看板/详情端点结构未改;无网关路由、无 Feign/MQ、无 DDL。
- 财务包内其余 26 处未加 `@JsonSerialize` 的 `BigDecimal` 字段(含 3 处 ReqVO)本单一处未碰。
- 6 个中文配对字段与 3 处 teamNo 的**装配逻辑**随零破坏批 PR-1 先行合入;本破坏批只做删字段 + 分页 + include* 默认 + 金额形态。
## 八、测试环境已验证
2026-09-14 TEST 网关(`https://api.test.1814.love:9443`,自签 admin token)+ 真 MySQL 实测(部署归属 `hl-order-service-v3 | refactor/7536-a3-breaking-pr2 | 513258beb | 0/N | ok`;本单已合入 dev-v3,PR #7688):
- **A3 分页**:56 户团 `?page=1&pageSize=20` → `data={records:[20],total:56,page:1,pageSize:20}`;`page=9` → `records=[]`、`total=56`、不抛异常;`pageSize=500` → `pageSize=200`、records=56;空团 → `{records:[],total:0}`(非 `data:null`)。
- **include* 默认**:不传 → `roomCount`/`roomType`/`specialNeeds`/`travelers` 装配;显式 `includeNeeds=false&includeTravelers=false` → 四项值 null。
- **中文配对**:`roomTypeName`(`STANDARD→标间`)+ 5 个 `<字段>Name`;`contractStatus=null → contractStatusName=null`、`payStatus=DEPOSIT_PAID → 已付定金`。
- **删字段**:`records[0]` 无 `estimatedCost`/`adultCount`/`childCount`/`youngChildCount`/`babyCount`;`groupChatUnreadCount` 按 AC-1 兜底保留。
- **财务 12 金额**:顶层 6 + `totals` 3 + `items[]` 3 全为字符串(`receivableAmount="327600.00"` 等);`withdrawnCount=17` 仍为数字。
- **teamNo**:有团号户 `orders`/`finance` 返 `26-7464` 与 SQL 逐字相等;未付订金户两端点返 JSON `null`。
- **AC-R1(与 #7533 共验)**:56 户团 `print-itinerary` `totalHouseholds=56`、`roster` 56 户、A3 首页仍 20 条、旧内部全量调用点编译通过。
- **全量单测**:`mvn -o -pl hl-order-service-v3 -am test`(Testcontainers)Tests run 10811 / 0 fail / 0 error / 7 skip,含 RedLineArchTest/MapperBoundaryArchTest,BUILD SUCCESS。
## 十、相关文档
- 工单 #7536
- 方案文档:`docs/group/团期闭环整改方案-v1.1.md` §6.1 / §6.2 / §6.3
- 全过程记录:HL 仓 `dev-records/records/2026-09-14-local-7536-a3-paging-breaking.md`
## 关联 / 联系人
- 后端:jw(破坏批:分页 + 删冗余 + 金额形态;中文配对/teamNo 装配随 PR-1)
- 前端:mmg(**必须整页面批量切换**:读 `data.records`/`data.total`;删「预计毛利」「人数四件套」列,人数改 participantCount + tierName;财务金额按字符串解析;三处 teamNo 逐户渲染、null 不兜底)