docs(changelog): #7066 团期子订单列表补应收总额 totalAmount
changelog-filename-gate / validate (push) Successful in 2s

GET /v3/admin/order/group-batch/:groupBatchId/orders 出参新增 totalAmount
应收总额,口径复用既有 OrderAmountUtil.payableForDisplay(取消单归 0),
与 calcBalance 口径一致。纯 additive,不新建端点、不改老字段语义。

关键变化:前端不要再用 paidAmount + balanceAmount 反推应收总额。
balanceAmount 被钳在 ≥0,超付/退款/取消单三种情况反推值都偏大。

2026-09-04 于测试环境网关实测 8 组用例全部通过,两处在真实数据上
命中本单要修的缺陷:
- 超付单 totalAmount=2943.00,旧反推得 3270.00,偏差 327.00
- 取消单 totalAmount=0.00 而 paidAmount=3270.00,旧算法会把已取消
  子订单显示成有应收

backend_status=deployed / gateway_status=verified。

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
这个提交包含在:
jw
2026-09-04 15:01:20 +08:00
共同撰写人 Claude Opus 5
父节点 2096a47340
当前提交 db6698061b
@@ -0,0 +1,267 @@
---
schema: "hl-changelog/v2"
ticket: "7066"
title: "团期子订单列表补应收总额 totalAmount"
consumer: "admin"
author: "jw(GIT)"
change_type: "修改接口"
backend_status: "deployed"
gateway_status: "verified"
frontend_status: "pending"
frontend_owner: ""
frontend_ref: ""
target_release: ""
verified_at: "2026-09-04"
status_note: "PR #7068 已合入 dev-v3(合并提交 e1ad8550b);2026-09-04 部署测试环境,网关实测 8 组用例全部通过(含取消单归 0)"
updated_at: "2026-09-04"
base: "dev-v3"
---
# 团期子订单: 出参新增应收总额 totalAmount
> **服务**: hl-order-service-v3
> **PR**: #7068 | **Issue**: #7066 | **合并提交**: `e1ad8550b`
> **影响范围**: 管理后台「团期详情 → 子订单」页签
---
## ⚠️ 关键变化
**前端不要再用 `paidAmount + balanceAmount` 反推应收总额,改读新增的 `totalAmount`。**
旧反推在两种场景下**偏大**:
1. `balanceAmount` 被钳在 ≥0(`OrderAmountUtil.calcBalance` 末行),超付或退款后已付大于应收时反推值偏大
2. 已取消子订单 `calcBalance` 直接返 0,反推得到的是已付而非应收
**这不是理论风险**:2026-09-04 测试环境实测,现存子订单 `paidAmount=3270.00`、`balanceAmount=0.00`,
而真实应收 `totalAmount=2943.00`——**旧算法多显示 327.00**。
---
## 一、背景
团期详情「子订单」页签每张卡片显示「已付 / 应收总额」,但列表接口出参此前没有应收总额字段,
前端只能自行反推。本次补齐该字段,口径与订单域既有展示口径统一。
---
## 二、变更接口清单
| # | 接口 | 方法 | 路径 | 变更类型 | 说明 |
|---|------|------|------|----------|------|
| 1 | 团期下子订单列表 | GET | `/v3/admin/order/group-batch/:groupBatchId/orders` | **出参新增字段** | 新增 `totalAmount` 应收总额 |
---
## 三、接口详情
### 1. 团期下子订单列表 `GET /v3/admin/order/group-batch/:groupBatchId/orders`
**VO**: `GroupBatchOrderItemRespVO`
#### 使用场景
团期详情「子订单」页签加载时调用,渲染每户卡片。卡片右下角「已付 ¥X / ¥Y」中的 Y 即本次新增的 `totalAmount`。
#### 入参
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|------|------|------|------|------|------|
| `groupBatchId` | Path | Long | ✅ | — | 运营团期 ID(订单侧 `order_group_batch` 主键) |
| `includeTravelers` | Query | Boolean | ❌ | 缺省 false | 附出行人明细,证件号一律不返回 |
| `includeNeeds` | Query | Boolean | ❌ | 缺省 false | 附房数 / 房型 / 特殊需求 |
| `includeCancelled` | Query | Boolean | ❌ | 缺省 false | 是否含已取消子订单,缺省只返活跃集 |
#### 出参 `Result<List<GroupBatchOrderItemRespVO>>`
本次仅新增一个字段,其余 28 个字段不变:
| 字段 | 类型 | 说明 |
|------|------|------|
| `totalAmount` | String | **本次新增**。应收总额 = `orderAmount + 增项 − 优惠`;**已取消子订单归 0**。2 位小数,字符串输出 |
| `paidAmount` | String | 已支付金额(未变) |
| `balanceAmount` | String | 待支付尾款,**钳在 ≥0**(未变) |
> 三个金额字段均带 `@JsonSerialize(ToStringSerializer)`,**JSON 里是字符串**,前端直接解析为数字会有精度风险,应按字符串处理或用高精度解析。
#### 请求示例
```http
GET /v3/admin/order/group-batch/2089713777065832450/orders
```
#### 响应示例
```json
{
"code": 200,
"message": "成功",
"success": true,
"data": [
{
"orderId": 2094597053924487170,
"customerName": "6915验收造单01",
"orderStatus": "CUSTOMIZING",
"orderStatusName": "定制中",
"adultCount": 2,
"childCount": 0,
"participantCount": 2,
"contactPhone": "138****0001",
"paidAmount": "3270.00",
"balanceAmount": "0.00",
"totalAmount": "2943.00"
}
]
}
```
#### 空数据 / 降级响应
团期下无子订单时返回空数组,不报错:
```json
{
"code": 200,
"message": "成功",
"success": true,
"data": []
}
```
#### 错误响应
未携带管理端令牌:
```json
{
"code": 401,
"message": "缺少有效的 Authorization 头",
"success": false,
"data": null
}
```
#### 业务边界
- 只读接口,不产生任何写入
- `totalAmount` **不等于** `paidAmount + balanceAmount`,超付、退款、取消单三种情况下都会不等
- 已取消子订单(`includeCancelled=true` 才可见)的 `totalAmount` 与 `balanceAmount` 同为 0,闭合财务勾稽
- 金额一律 2 位小数(HALF_UP),与订单域其他接口格式一致
---
## 四、契约约束与正确调用方式
- **应收总额一律读 `totalAmount`**,不要自行用 `paidAmount + balanceAmount` 计算。
- **`totalAmount` 与 `balanceAmount` 是不同语义**:前者是「客户总共该付多少」,后者是「现在还差多少」。已付清时后者为 0,前者仍是原值。
- **三个金额字段是 JSON 字符串**,不是数字。
- 取消单场景下 `totalAmount` 归 0 是**有意设计**(对齐 `calcBalance` 的取消单归 0),用于闭合「应收 0 / 已付 0 / 已退 0 / 待收 0」的勾稽链,不是缺陷。
---
## 五、数据库行为
本接口只读,**不产生任何写入**,也不涉及表结构调整。`totalAmount` 由既有字段实时计算,不新增存储列。
---
## 六、边界行为
- 超付单 → `totalAmount` 为真实应收,小于 `paidAmount`
- 取消单 → `totalAmount` 归 0
- 团期无子订单 → 返回 `[]`
- 未登录 → 401(网关拦截)
---
## 六.5、枚举 / 数据字典
本次无新增枚举。
---
## 六.6、修改前后对比
| 项 | 变更前 | 变更后 |
|---|---|---|
| 出参字段数 | 28 | **29** |
| 应收总额 | **无字段**,前端用 `paidAmount + balanceAmount` 反推 | 新增 `totalAmount`,服务端按既有 `payableForDisplay` 口径给出 |
| 超付单显示 | 反推值偏大(实测多 327.00) | `totalAmount` 为真实应收 |
| 取消单显示 | 反推得到已付金额,非应收 | `totalAmount` 归 0,与 `balanceAmount` 一致 |
| `paidAmount` / `balanceAmount` | — | **语义与取值均不变** |
| 路径 / 入参 | — | **不变** |
## 六.7、影响评估
| 维度 | 评估 |
|---|---|
| 兼容性 | **纯 additive**,老调用方不读新字段即无感,无破坏性变更 |
| 前端 | 需改为读 `totalAmount`;不改也不会报错,但超付 / 取消单场景显示值偏大 |
| 数据 | 无 DDL、无迁移、无回填;`totalAmount` 实时计算不落库 |
| 性能 | 无额外查询,复用同一 `OrderInfo` 实体计算,零新增 IO |
| 回滚 | 移除字段即可,无数据侧残留 |
| 风险 | 低。口径复用 `AdjustmentService` 已在生产使用的既有方法,未新造公式 |
## 七、不影响范围
- **零影响**:`paidAmount` / `balanceAmount` 的现有语义与取值
- **零影响**:其余 28 个出参字段
- **零影响**:老调用方——纯新增字段,不读即无感
- **未新建端点**,未改动路径与入参
---
## 八、测试环境已验证
✅ 2026-09-04 于测试环境网关实测,真实鉴权(管理端 admin)。
- 网关 `https://api.test.1814.love`,分支 `dev-v3`,合并提交 `e1ad8550b`
- 部署方式:双实例滚动更新(8186 → 8086),各 11s 就绪,零停机
| # | 用例 | 期望 | 实测 |
|---|---|---|---|
| 1 | 字段上线 | 出参含 `totalAmount` | ✅ 字段数 27 → 29 |
| 2 | 真实数据口径 | `totalAmount` 为真实应收 | ✅ `2943.00`;旧反推得 `3270.00`,**偏差 327.00** |
| 3 | 序列化格式 | 字符串、2 位小数 | ✅ `"totalAmount":"2943.00"` |
| 4 | `includeCancelled=true` | 正常返回 | ✅ 返回 1 条 |
| 5 | 无 Authorization | 401 | ✅ `401 缺少有效的 Authorization 头` |
| 6 | 老字段回归 | 未受影响 | ✅ 8 个字段逐一核对一致 |
| 7 | 缺省过滤取消单 | 取消单不出现 | ✅ `GET .../orders` 返回 0 条 |
| 8 | **取消单归 0** | `totalAmount = 0` 且与 `balanceAmount` 一致 | ✅ `totalAmount="0.00"`、`balanceAmount="0.00"`、`paidAmount="3270.00"` |
**部署前基线对照**:同一接口部署前出参 27 字段、无 `totalAmount`,确认变更确实生效。
**本地单测**:`GroupBatchConverterTest` 新增 5 例(正常单 / 含增项优惠 / **超付单断言反推不等** / **取消单归 0** / 2 位小数);
本地全量 **750 例全过**。
**取消单分支已实测**(用例 7–8):验收期间该子订单被退单,恰好提供了取消单样本。
实测 `totalAmount="0.00"`,而 `paidAmount="3270.00"`——**旧算法 `paidAmount + balanceAmount` 会把一条
已取消的子订单显示成「应收 3270」,真实应收应为 0**。本字段同时修正了这一场景。
全部 8 组用例通过,无遗留未验分支。
---
## 九、相关历史 PR
- #7068 本次变更
- 口径来源:`OrderAmountUtil.payableForDisplay`(#4456 展示用应收总额,取消单归 0)
- 相关:#5733(`calcBalance` 改为退款冲减应收)、#2896 P1-3(金额统一 2 位小数)
---
## 十、相关文档
- 团期需求文档:`docs/group/`(dev-v3 分支)
- 3 天开发计划:`docs/group/团期模块3天开发计划.md`
---
## 关联 / 联系人
- **Issue**: #7066
- **PR**: #7068(合并提交 `e1ad8550b`)
- **服务**: hl-order-service-v3
- **后端**: jw
- **前端**: 待认领(`frontend_status: pending`)