---
schema: "hl-changelog/v2"
ticket: "5955"
title: "主报账表baseInfo尾款口径透出三新字段+outstandingAmount下线"
consumer: "admin"
change_type: "修改接口"
author: "yst"
backend_status: "deployed"
gateway_status: "verified"
frontend_status: "verified"
frontend_owner: "mmg"
frontend_ref: "c2783946"
target_release: ""
verified_at: "2026-08-14"
status_note: "PR #5958 已合并 dev-v3（commit cb40e197cb）。破坏性契约变化：baseInfo 删除 outstandingAmount（整单未收尾款），新增 receivableBalanceAmount（应收尾款）/ paidBalanceAmount（实收尾款）/ reporterPaidBalanceAmount（报账人实收尾款）；reporterNetAmount 保留为唯一带正负净额字段。仅出参变化，无入参/枚举/DDL 变化。"
updated_at: "2026-08-14"
base: "dev-v3"
---

# 【修改接口·管理后台】主报账表 baseInfo 尾款口径透出三新字段 + outstandingAmount 下线（#5955）

## 1. 接口背景

主报账人报账表（reimbursement）是管理后台订单核单页查看主报账人（通常是司机）代收、垫付支出、预支与净额结算情况的只读报表，`baseInfo` 块承载全部汇总金额。

此前 baseInfo 用 `outstandingAmount`（整单未收尾款，口径同核单应收财务总览）一个字段表达尾款情况，前端无法区分「应收多少尾款」「实际已收多少尾款」「其中主报账人线下代收了多少尾款」，做尾款对账展示时信息不够。

本次变更把尾款口径拆成三个权威字段直接透出，同时**删除 outstandingAmount**：未收尾款改由前端以「应收尾款 − 实收尾款」自算。报账人净额 `reporterNetAmount` 保留为**唯一**带正负的净额字段（正=报账人应转回公司，负=公司应补报账人），不再单出绝对值字段。

## 2. 变更清单

| # | 位置 | 变更 | 类型 |
|---|------|------|------|
| 1 | baseInfo.outstandingAmount | **字段删除**：整单未收尾款不再透出，前端读到 undefined/键不存在 | ⚠️ 破坏性删除 |
| 2 | baseInfo.receivableBalanceAmount | **新增**：应收尾款（调整后应收合计 − 已退 − 订金实收，负截 0） | ✨ 新增字段 |
| 3 | baseInfo.paidBalanceAmount | **新增**：实收尾款（payType∈{BALANCE,FULL} 已收：线上 SUCCESS 流水 + 线下手工收款） | ✨ 新增字段 |
| 4 | baseInfo.reporterPaidBalanceAmount | **新增**：报账人实收尾款（主报账人 channel=DRIVER_CASH 且 payType∈{BALANCE,FULL} 的线下收款合计） | ✨ 新增字段 |
| 5 | baseInfo.reporterNetAmount | **保留**，口径不变；强调其为唯一带正负净额字段，绝对值展示（如「公司应补报账人」）由前端按符号渲染 | 📝 口径声明（数值零漂移） |
| 6 | baseInfo.primaryReporterCollectedAmount | **保留**，口径不变（所有 channel=DRIVER_CASH 收款行合计）；与 reporterPaidBalanceAmount（仅其 BALANCE/FULL 尾款子集）语义不同 | 📝 口径声明（数值零漂移） |

无入参变化、无新接口、无枚举/字典变化、无 DDL。组报 / finalize 门禁内部的 outstandingAmount 计算口径**不受影响**（仅本接口出参不再透出）。

## 3. 接口详情

| 项 | 值 |
|---|---|
| 方法 + 路径 | GET /v3/admin/order/{orderId}/settlement/reports/reimbursement |
| 接口名 | 查询主报账人报账表 |
| 使用场景 | 管理后台订单核单页，查看主报账人代收/垫付/预支/净额结算报表（含尾款三口径） |
| 认证 | 管理后台 JWT（/v3/admin/* 走网关鉴权） |
| 角色限制 | 房务角色（HOUSE）不可访问，调了会被 403 拦截 |
| 幂等性 | 只读查询，幂等 |
| 限流 | 走网关默认限流，无接口级特殊限流 |

## 4. 接口入参

### 4.1 路径参数

| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| orderId | Long | 是 | 订单 ID，必须大于 0（否则 400「订单 ID 必须大于 0」） |

### 4.2 请求体 / Query

无请求体、无 Query 参数。

## 5. 出参字段

返回 `Result<SettlementReimbursementReportRespVO>`。顶层 4 个字段不变：baseInfo / incomeLines / expenseLines / advanceLines。本次**仅 baseInfo 金额区变化**，incomeLines / expenseLines / advanceLines 结构零变化。

### 5.1 baseInfo 全量字段表（变化字段加粗）

| 字段 | 类型 | 说明 |
|------|------|------|
| id | Long(String) | 核单记录 ID（settlement_recon.recon_id）；未生成时不输出该键 |
| orderId | Long(String) | 订单 ID |
| orderNo | String | 订单号 |
| teamNo | String | 团号（订金未支付为 null，不输出该键） |
| productName | String | 产品名 |
| productType | String | 产品类型枚举（如 CORE） |
| productTypeName | String | 产品类型中文名（字典 product_type 回填） |
| customerName | String | 客户姓名 |
| departDate | String | 出团日期，格式 yyyy-MM-dd |
| returnDate | String | 返回日期，格式 yyyy-MM-dd |
| consultantName | String | 定制师姓名 |
| travelerCount | Integer | 出行人总数（成人+儿童+幼童+婴儿，空档按 0 计） |
| travelerComposition | String | 出行人构成（数量为 0 的档不显示，全空为 null），如「2大 1儿童 1幼童」 |
| primaryReporterId | Long(String) | 主报账人人员安排 ID |
| primaryReporterName | String | 主报账人姓名 |
| primaryReporterRole | String | 主报账人角色（如 DRIVER） |
| primaryReporterCollectedAmount | BigDecimal | 主报账人代收金额：所有 channel=DRIVER_CASH 收款行合计（**含订金性质现金**，不含 OTHER_INCOME 其他收入展示行），保留两位小数 |
| approvedAdvanceAmount | BigDecimal | 已审批预支金额（预支行合计），保留两位小数 |
| reportablePaidCostAmount | BigDecimal | 可报账已付成本（仅 CASH_PAID 现金垫付支出行合计，含 OTHER_EXPENSE 分类中的 CASH_PAID 行），保留两位小数 |
| reporterNetAmount | BigDecimal | 报账人净额 = 代收 + 预支 − 支出，保留两位小数；**正=报账人应转回公司，负=公司应补报账人，0=两清**。唯一净额字段，带正负 |
| ~~outstandingAmount~~ | — | **已删除**：整单未收尾款不再透出（见 §10） |
| **receivableBalanceAmount** | BigDecimal | **新增**·应收尾款 = 调整后应收合计 − 已退 − 订金实收，负值截 0，保留两位小数。其中：订金实收 = 线上 SUCCESS 支付流水 + 线下手工收款中 payType=DEPOSIT 部分；调整后应收合计/已退口径同核单应收财务总览 adjustedReceivableAmount / actualRefundedAmount |
| **paidBalanceAmount** | BigDecimal | **新增**·实收尾款 = 已收且 payType∈{BALANCE, FULL} 的合计（线上 SUCCESS 支付流水 + 线下手工收款），保留两位小数。**FULL 全款单无订金概念，首笔即全收，计入实收尾款** |
| **reporterPaidBalanceAmount** | BigDecimal | **新增**·报账人实收尾款 = 主报账人 channel=DRIVER_CASH 且 payType∈{BALANCE, FULL} 且 collectorStaffId=主报账人的线下收款合计；无主报账人时为 0，保留两位小数 |

### 5.2 三新字段的派生关系（前端对账用）

```
receivableBalanceAmount（应收尾款）= 调整后应收合计 − 已退 − 订金实收（DEPOSIT 已收），负截 0
paidBalanceAmount（实收尾款）     = Σ 已收且 payType∈{BALANCE,FULL}（线上 SUCCESS + 线下手工）
reporterPaidBalanceAmount         = paidBalanceAmount 中「主报账人 DRIVER_CASH 线下代收」的子集
未收尾款（前端自算）              = max(0, receivableBalanceAmount − paidBalanceAmount)
```

**与 primaryReporterCollectedAmount 的区别**：代收（collected）含主报账人所有 DRIVER_CASH 收款（含 payType=DEPOSIT 的订金现金）；reporterPaidBalanceAmount 只算其中 BALANCE/FULL 尾款部分。订单存在「主报账人代收订金现金」时两者会分叉，属预期。

## 6. 枚举 / 数据字典

本次无新增枚举/字典。涉及的既有枚举（仅用于理解口径，不在 baseInfo 出参里）：

| 枚举 | 取值 | 说明 |
|------|------|------|
| PayType（款项类型） | DEPOSIT（订金）/ FULL（全款）/ BALANCE（尾款） | 实收尾款口径 = BALANCE + FULL；订金实收口径 = DEPOSIT |
| 收款渠道 | DRIVER_CASH（报账人现金）/ BANK_TRANSFER / CONSULTANT_COLLECTION | reporterPaidBalanceAmount 只算 DRIVER_CASH 且收款人=主报账人 |

## 7. 错误码

本次无新增错误码，沿用既有：

| code | message | 触发场景 |
|------|---------|----------|
| 400 | 订单 ID 必须大于 0 | orderId 路径参数校验失败 |
| 403 | 无权限访问 | 房务角色（HOUSE）JWT 调用 |
| 581007 | 订单不存在 | orderId 查不到订单 |

## 8. 示例

### 8.1 典型成功（订金单部分付尾款，含主报账人 DRIVER_CASH 尾款代收）

场景：调整后应收 10000，已退 0，订金实收 2000（线上 SUCCESS）→ 应收尾款 8000；尾款已收 2200（线上 1700 + 主报账人现金代收 500）。

    GET /v3/admin/order/2087088947225038849/settlement/reports/reimbursement

```json
{
  "code": 200,
  "message": "成功",
  "data": {
    "baseInfo": {
      "id": "6001",
      "orderId": "2087088947225038849",
      "orderNo": "HL20260730001",
      "teamNo": "T20260730001",
      "productName": "呼伦贝尔草原 5 日游",
      "productType": "CORE",
      "productTypeName": "核心产品",
      "customerName": "张三",
      "departDate": "2026-07-30",
      "returnDate": "2026-08-03",
      "consultantName": "李四",
      "travelerCount": 4,
      "travelerComposition": "2大 2儿童",
      "primaryReporterId": "7001",
      "primaryReporterName": "司机甲",
      "primaryReporterRole": "DRIVER",
      "primaryReporterCollectedAmount": 500.00,
      "approvedAdvanceAmount": 300.00,
      "reportablePaidCostAmount": 100.00,
      "reporterNetAmount": 700.00,
      "receivableBalanceAmount": 8000.00,
      "paidBalanceAmount": 2200.00,
      "reporterPaidBalanceAmount": 500.00
    },
    "incomeLines": [
      {
        "type": "DRIVER_CASH_RECEIPT",
        "typeName": "司机现金收款",
        "receiptId": "8001",
        "amount": 500.00,
        "channel": "DRIVER_CASH",
        "channelName": "报账人收款",
        "payType": "BALANCE",
        "payTypeName": "尾款",
        "collectorStaffId": "7001",
        "collectorName": "司机甲",
        "collectorRole": "DRIVER",
        "collectorRoleName": "司机",
        "receivedAt": "2026-07-30 18:20:30",
        "remark": "尾款现金"
      }
    ],
    "expenseLines": [],
    "advanceLines": []
  },
  "success": true
}
```

前端展示：未收尾款 = max(0, 8000 − 2200) = **5800.00**（自算）；reporterNetAmount=700.00>0 → 「报账人应转回公司 700.00」。

### 8.2 边界情况（FULL 全款单，首笔即全收 + 全额已付后再部分退款）

场景：FULL 全款单 5000 线上一次付清，无订金概念。应收尾款 = 5000 − 0 − 0（无 DEPOSIT 实收）= 5000；实收尾款 = 5000（FULL 计入）；未收尾款自算 = 0。若此后部分退款 1000，应收尾款冲减为 4000、实收仍 5000，自算未收尾款为负 → **前端必须 max(0, ·) 截 0 展示**：

```json
{
  "baseInfo": {
    "orderId": "2087088947225038849",
    "orderNo": "HL20260801002",
    "primaryReporterName": "司机乙",
    "primaryReporterRole": "DRIVER",
    "primaryReporterCollectedAmount": 0.00,
    "approvedAdvanceAmount": 0.00,
    "reportablePaidCostAmount": 0.00,
    "reporterNetAmount": 0.00,
    "receivableBalanceAmount": 4000.00,
    "paidBalanceAmount": 5000.00,
    "reporterPaidBalanceAmount": 0.00
  }
}
```

边界要点：reporterNetAmount=0 → 两清；reporterPaidBalanceAmount=0（主报账人没有线下代收尾款）；未收尾款展示 0.00（负值截 0）。

### 8.3 业务失败（字段已删——历史代码读 outstandingAmount 拿到 undefined）

修改后 baseInfo **不再包含 outstandingAmount 键**（不是 null，是键不存在）。前端旧代码 `baseInfo.outstandingAmount` 读到 undefined，若直接参与渲染/计算会出现「undefined元」或 NaN——**必须改为三新字段**：

```json
{
  "baseInfo": {
    "orderId": "2087088947225038849",
    "reporterNetAmount": 700.00,
    "receivableBalanceAmount": 8000.00,
    "paidBalanceAmount": 2200.00,
    "reporterPaidBalanceAmount": 500.00
  }
}
```

另附通用业务失败响应（与修改前一致）：

```json
{ "code": 403, "message": "无权限访问", "success": false }
```

```json
{ "code": 581007, "message": "订单不存在", "success": false }
```

## 9. 业务边界

**适用**：

- 核单页查看主报账人结算全貌 + 尾款三口径（应收/实收/报账人实收），未收尾款由前端自算展示。

**不适用**：

- 组报 / finalize 门禁判断 —— 内部仍用 outstandingAmount 口径，但那是服务端逻辑，前端无需关心，也不要再试图从本接口读它。

**特殊边界（前端必知）**：

1. **outstandingAmount 已删，不是 null 是键不存在**：VO 带 @JsonInclude(NON_NULL)，删除后该键彻底不下发。旧代码读到 undefined，必须改。
2. **历史 finalize 快照无新三字段**：查看本次上线前生成的旧结算快照时，receivableBalanceAmount / paidBalanceAmount / reporterPaidBalanceAmount 三键为 null（不下发），前端需容忍空态展示（如「--」）。
3. **前端自算未收尾款可为负**：「全额已付后再部分退款」场景下 应收尾款−实收尾款 为负，展示时必须 max(0, ·)。
4. **净额方向**：reporterNetAmount > 0 → 报账人欠公司（应转回）；< 0 → 公司欠报账人（应补）；= 0 → 两清。界面「公司应补报账人」等绝对值文案由前端按符号渲染，后端不再单出绝对值字段。
5. **代收 ≠ 报账人实收尾款**：primaryReporterCollectedAmount 含订金性质现金（DEPOSIT）；reporterPaidBalanceAmount 只含 BALANCE/FULL。两者在「主报账人代收过订金现金」时分叉，属预期，不要互相校验相等。

## 10. 修改前后对比

### 10.1 字段级对比

| 位置 | 字段 | 修改前 | 修改后 |
|------|------|--------|--------|
| baseInfo | outstandingAmount | 整单未收尾款（口径同财务总览） | **已删除**（键不再下发） |
| baseInfo | receivableBalanceAmount | 无 | **新增**：应收尾款（调整后应收 − 已退 − 订金实收，负截 0） |
| baseInfo | paidBalanceAmount | 无 | **新增**：实收尾款（BALANCE/FULL 已收，线上 SUCCESS + 线下手工） |
| baseInfo | reporterPaidBalanceAmount | 无 | **新增**：报账人实收尾款（主报账人 DRIVER_CASH 的 BALANCE/FULL 子集） |
| baseInfo | reporterNetAmount | 报账人净额（带正负） | **口径不变**，明确为唯一净额字段 |
| baseInfo | primaryReporterCollectedAmount | 主报账人代收 | **口径不变** |
| baseInfo | 其余字段 | 原样 | **零变化** |

### 10.2 行为级对比

| 场景 | 修改前 | 修改后 |
|------|--------|--------|
| 未收尾款展示 | 直接读 baseInfo.outstandingAmount | **前端自算** max(0, receivableBalanceAmount − paidBalanceAmount) |
| FULL 全款单尾款口径 | outstandingAmount 一个数，无细分 | 应收=调整后应收（无订金可减），实收含 FULL 首笔全额，自算未收=0 |
| 主报账人代收了多少尾款 | 无法区分（代收含订金现金混在一起） | reporterPaidBalanceAmount 直接给出 BALANCE/FULL 子集 |
| 历史 finalize 快照 | 含 outstandingAmount | 旧快照无新三字段（键为 null 不下发），前端空态容忍 |

## 11. 影响评估 / 回滚

### 11.1 影响评估

- **破坏兼容性**：⚠️ **破坏性变更**。baseInfo 删除 outstandingAmount 字段，凡读取该字段的前端代码必须改造；新增三字段对不读旧字段的页面无影响。
- **前端是否必须同步上线**：**是（针对 outstandingAmount 消费点）**。后端上线后 outstandingAmount 立即消失，读它的界面会显示 undefined/空白，前端三新字段改造需与后端同窗口上线。
- **改造点清单**：
  1. 删 baseInfo.outstandingAmount 的所有读取，未收尾款改自算 max(0, receivableBalanceAmount − paidBalanceAmount)；
  2. 新增应收尾款 / 实收尾款 / 报账人实收尾款三处展示（按产品 UI 需要）；
  3. 「公司应补报账人」等绝对值展示按 reporterNetAmount 符号渲染；
  4. 历史快照空态容忍（三新字段键可能不存在）。
- **无 DDL、无枚举、无字典变更**，无其他服务依赖。

### 11.2 回滚方案

- 后端回滚 = revert PR #5958 的 commit（cb40e197cb），重启 hl-order-service-v3，outstandingAmount 恢复透出、三新字段消失。
- 前端若已按新字段上线而后端回滚，三新字段读到 undefined → 前端回滚需同步；建议前后端同窗口切换。
- 零 DDL / 零数据迁移，回滚无数据残留风险。

## 12. 注意事项

1. **NON_NULL 省略**：baseInfo 带 @JsonInclude(NON_NULL)，null 字段不下发该键（不是下 null）；旧快照的三新字段、未生成核单的 id、未付订金的 teamNo 都是「键不存在」，前端读取必须兜底。
2. **Long 主键序列化为字符串**（id / orderId / primaryReporterId 等），前端按 string 处理，不要 Number() 转换。
3. **金额保留两位小数**，BigDecimal JSON 输出为 number（如 8000.00），前端展示格式化自行处理。
4. **汇总只读 baseInfo**：三新字段与 reporterNetAmount 都是后端算好的权威值，前端不要拿 incomeLines 数组自算尾款（收款行还含 DEPOSIT，口径不同必然对不上）。
5. 组报 / finalize 门禁内部 outstandingAmount 口径不受本变更影响，无需前端配合。
6. 本变更只影响 reimbursement 报账表的 baseInfo；incomeLines / expenseLines / advanceLines 结构零变化，单团核算表（reports/group）不受影响。

## 13. 关联 / 联系人

- Issue #5955：https://git.1814.love:8443/wx/HL/issues/5955
- PR #5958：https://git.1814.love:8443/wx/HL/pulls/5958
- Commit：https://git.1814.love:8443/wx/HL/commit/cb40e197cb
- 服务：hl-order-service-v3（端口 8086）
- 后端负责人：yst（腰苏图）

## 14. 验证证据

- 后端单测：SettlementReportFlowServiceTest / SettlementControllerTest 补 FULL 全款单、部分退款、混合拆分类边界用例（实收尾款含 FULL、应收尾款负截 0、报账人 DRIVER_CASH+BALANCE/FULL 子集口径），PR #5958 已合并 dev-v3（commit cb40e197cb）。
- 代码已合并 dev-v3 并部署测试服。
- 前端联调验证点：旧 outstandingAmount 消费点全部改造；§8.1 典型场景未收尾款自算 = 5800.00；§8.2 FULL 单部分退款后自算负值截 0 展示；历史快照三新字段空态不报错。
