---
schema: "hl-changelog/v2"
ticket: "5916"
title: "核单两报表出参瘦身合并版（#5876 + #5916）：orderHeader 拍平 + 审计字段下线 + finalize warnings 结构化 + summary 新增 settled（破坏性变更）"
consumer: "admin"
change_type: "修改接口"
author: "yst"
backend_status: "deployed"
gateway_status: "pending"
frontend_status: "verified"
frontend_owner: "mmg"
frontend_ref: "ea2916fc"
target_release: ""
verified_at: "2026-08-12"
status_note: "本份是 #5876（PR #5881）与 #5916（PR #5920）两个 PR 对核单报表接口的合并 changelog，以 #5916 合并后的最终出参为准。两份 PR 均已 merge 到 dev-v3 并部署测试服。破坏性：reimbursement 删顶层 orderHeader 块（11 抬头字段拍平进 baseInfo）+ baseInfo 删 10 字段（reportStatus/reportVersion/generated*/confirmed*/transferAmount/primaryReporterDueAmount）+ 新增 outstandingAmount；group 删 orderHeader 块（10 抬头字段平铺顶层）+ 删 reportStatus/generated*/confirmed* 共 8 字段；finalize warnings 由字符串数组改对象数组；summary 新增 settled。此前 #5876 单独那份（12_5876）的 finalize/summary 内容并入本份，前端按本份最终结构一次改到位即可。"
updated_at: "2026-08-12"
base: "dev-v3"
---

# 【⚠️ 修改接口·管理后台】核单两报表出参瘦身（合并 #5876 + #5916 最终态）

## 1. 接口背景

核单结算域的 4 个管理后台接口，近期经过**两次连续变更**（#5876 → #5916），本份 changelog 合并描述**累计最终态**，前端按本份一次改到位即可，**不要**分两次对接：

1. **主报账人报账表**（reimbursement）：核单页查看主报账人（通常是司机）的代收、垫付支出、预支与净额结算情况。
2. **单团核算表**（group）：核单页 / 财务页查看整团收入、成本、毛利核算。
3. **完成核单**（finalize）：核单页点「完成核单」提交，原子冻结核单事实并生成核单汇总。
4. **核单汇总快照**（summary）：核单页 / 财务报表页读取整单金额、成本、毛利汇总。

两次变更分别做了什么：

- **#5876（PR #5881）**：finalize 出参 warnings 由字符串数组改为对象数组（可定位明细行）；summary 出参新增 settled 字段（可靠判「是否已核单」）。
- **#5916（PR #5920，最终态）**：两报表出参瘦身 —— 顶层 orderHeader 子块下线（抬头字段拍平），报表状态 / 生成确认审计字段出参下线，reimbursement 的尾款口径由「主报账人维度」换成「整单未收尾款」。

## 2. 变更清单

| # | 接口 | 变更 | 类型 |
|---|------|------|------|
| 1 | reimbursement | 顶层 orderHeader 子块删除，11 个订单抬头字段拍平进 baseInfo（orderNo/teamNo/productName/productType/productTypeName/customerName/departDate/returnDate/consultantName/travelerCount/travelerComposition） | ⚠️ 破坏性 |
| 2 | reimbursement | baseInfo 删除 10 个字段：reportStatus / reportVersion / generatedBy / generatedByName / generatedAt / confirmedBy / confirmedByName / confirmedAt / transferAmount / primaryReporterDueAmount | ⚠️ 破坏性 |
| 3 | reimbursement | baseInfo 新增 outstandingAmount（整单未收尾款，口径同 financial-overview），替代已删的 primaryReporterDueAmount | ✨ 新增字段 |
| 4 | group | 顶层 orderHeader 子块删除，10 个订单抬头字段平铺到顶层（orderNo/teamNo/productName/productType/productTypeName/customerName/departDate/returnDate/consultantName/travelerComposition） | ⚠️ 破坏性 |
| 5 | group | 删除 8 个字段：reportStatus / generatedBy / generatedByName / generatedAt / confirmedBy / confirmedByName / confirmedAt，及 orderHeader 块整体 | ⚠️ 破坏性 |
| 6 | finalize | 出参 warnings 由 List<String> 改为 List<WarningItemVO>（每条带 settlementId / category / dayNumber / message）（#5876 引入，本份仍有效） | ⚠️ 破坏性 |
| 7 | summary | 出参新增 settled 字段（Boolean）：已核单 = true，未核单空壳 = false（#5876 引入，本份仍有效） | ✨ 新增字段 |

无入参变化、无 DDL（报表状态 / 版本列仍保留在 DB，仅出参不再下发）。

## 3. 接口详情

### 3.1 查主报账人报账表

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

### 3.2 查单团核算表

| 项 | 值 |
|---|---|
| 方法 + 路径 | GET /v3/admin/order/{orderId}/settlement/reports/group |
| 接口名 | 查询单团核算表 |
| 使用场景 | 管理后台核单页 / 财务页，查看整团收入、成本、毛利核算 |
| 认证 | 管理后台 JWT |
| 角色限制 | 房务角色（HOUSE）不可访问，调了会被 403 拦截 |
| 幂等性 | 只读查询，幂等 |
| 限流 | 走网关默认限流 |

### 3.3 完成核单

| 项 | 值 |
|---|---|
| 方法 + 路径 | POST /v3/admin/order/{orderId}/settlement/finalize |
| 接口名 | 完成核单 |
| 使用场景 | 管理后台核单页，明细核对完成后提交，原子冻结核单事实并生成核单汇总快照 |
| 认证 | 管理后台 JWT |
| 角色限制 | 房务角色（HOUSE）不可访问，调了会被 403 拦截 |
| 幂等性 | 非幂等写操作；重复提交会被「缺少核单终态快照 / 快照已变化」类错误码拦截，前端不要自动重试 |
| 限流 | 走网关默认限流 |

### 3.4 查核单汇总快照

| 项 | 值 |
|---|---|
| 方法 + 路径 | GET /v3/admin/order/{orderId}/settlement/summary |
| 接口名 | 查核单汇总快照 |
| 使用场景 | 管理后台核单页 / 财务报表页读取整单金额、成本、毛利汇总 |
| 认证 | 管理后台 JWT |
| 角色限制 | 无接口级角色限制（网关鉴权通过即可） |
| 幂等性 | 只读查询，幂等 |
| 限流 | 走网关默认限流 |

## 4. 接口入参

### 4.1 路径参数

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

### 4.2 请求体 / Query

4 个接口均无请求体、无 Query 参数。

## 5. 出参字段

### 5.1 reimbursement 出参（Result<SettlementReimbursementReportRespVO>，最终结构）

**顶层结构**（只有 4 个字段，不再有 orderHeader 块、不再有平铺汇总字段）：

| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| baseInfo | Object | 是 | 基础信息（订单抬头 + 报表汇总 + 报账人），恒下发对象 |
| incomeLines | Array | 是 | 收入行（司机/报账人代收）；无数据固定返回空数组 [] |
| expenseLines | Array | 是 | 支出行（统一扁平字段，仅报账人垫付 CASH_PAID 支出）；无数据固定返回空数组 [] |
| advanceLines | Array | 是 | 预支明细行（已审批预支逐条）；无数据固定返回空数组 [] |

**baseInfo 字段表（最终 21 字段）**：

| 字段 | 类型 | 说明 |
|------|------|------|
| id | Long(String) | 核单记录 ID（settlement_recon.recon_id），序列化为字符串；未生成时不输出 |
| orderId | Long(String) | 订单 ID，序列化为字符串 |
| orderNo | String | 订单号 |
| teamNo | String | 团号（订金未支付时不输出） |
| 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 | 主报账人代收金额（收入行合计），保留两位小数 |
| approvedAdvanceAmount | BigDecimal | 已审批预支金额（预支行合计），保留两位小数 |
| reportablePaidCostAmount | BigDecimal | 可报账已付成本（支出行合计），保留两位小数 |
| reporterNetAmount | BigDecimal | 报账人净额（代收 + 预支 - 支出）；正 = 报账人应转回公司，负 = 公司应补报账人 |
| outstandingAmount | BigDecimal | **本次新增**：整单未收尾款（口径同核单应收财务总览 outstandingAmount），保留两位小数 |

> 注：baseInfo 带 @JsonInclude(NON_NULL)，null 字段不下发该 key，前端读取要兜底。

**转账方向判定**（transferAmount / transferDirection 均已删，前端自行推导）：

- reporterNetAmount > 0 → 报账人应转回公司
- reporterNetAmount < 0 → 公司应补报账人
- 转账金额 = reporterNetAmount 的绝对值

**incomeLines 元素字段表**：

| 字段 | 类型 | 说明 |
|------|------|------|
| type | String | 行类型，当前仅 DRIVER_CASH_RECEIPT（司机/报账人现金收款） |
| typeName | String | 行类型中文名（字典 settlement_report_line_type） |
| receiptId | Long(String) | 线下收款记录 ID，序列化为字符串 |
| amount | BigDecimal | 收款金额，保留两位小数 |
| channel | String | 收款渠道：DRIVER_CASH / BANK_TRANSFER / CONSULTANT_COLLECTION |
| channelName | String | 收款渠道中文名（枚举 label） |
| payType | String | 收款款项类型：DEPOSIT / FULL / BALANCE |
| payTypeName | String | 款项类型中文名（枚举 label） |
| collectorStaffId | Long(String) | 收款人人员安排 ID，序列化为字符串 |
| collectorName | String | 收款人姓名 |
| collectorRole | String | 收款人角色（如 DRIVER） |
| collectorRoleName | String | 收款人角色中文名（字典 staff_role） |
| receivedAt | String | 收款时间，格式 yyyy-MM-dd HH:mm:ss |
| remark | String | 备注；无备注时不输出 |

**expenseLines 元素字段表（统一 12 字段，所有费用分类共用）**：

| 字段 | 类型 | 说明 |
|------|------|------|
| category | String | 费用类别：HOTEL / TICKET / MEAL / VEHICLE / GUIDE / PHOTOGRAPHER / OTHER_EXPENSE / INSURANCE |
| categoryName | String | 费用类别中文名（字典 settlement_category） |
| itemName | String | 项目名（分类特有信息折叠：住宿=酒店-房型，门票=景区-规格，餐食=餐食名(餐类型)，车辆=车牌 车型/司机，人员=姓名，其他=项目名） |
| unitPrice | BigDecimal | 单价，保留两位小数；无单价概念的分类不输出 |
| quantity | BigDecimal | 数量（住宿=房间数，餐食=份数，门票=票数，车辆按天每行=1）；无数量概念的分类不输出 |
| amount | BigDecimal | 实际金额，保留两位小数 |
| reimburseAmount | BigDecimal | 报账金额，保留两位小数；人员行应报销与金额不同时分别给出，其余分类同 amount |
| paymentMethod | String | 付款方式；报账支出行固定 CASH_PAID |
| paymentMethodName | String | 付款方式中文名（字典 settlement_payment_method，缺值回退硬编码） |
| date | String | 业务日期（住宿=入住日，门票=游玩日，餐食=用餐日，车辆=服务日，人员=结算日），格式 yyyy-MM-dd；无日期概念的分类不输出 |
| remark | String | 备注；无备注时不输出 |
| voucherUrls | Array<String> | 凭证 URL 数组；无凭证时不输出该键 |

**advanceLines 元素字段表**：

| 字段 | 类型 | 说明 |
|------|------|------|
| type | String | 行类型，当前仅 APPROVED_ADVANCE（已审批预支） |
| typeName | String | 行类型中文名（字典 settlement_report_line_type） |
| advanceId | Long(String) | 预支单 ID，序列化为字符串 |
| payeeStaffId | Long(String) | 借款对象人员安排 ID，序列化为字符串 |
| payeeName | String | 借款对象姓名 |
| payeeRole | String | 借款对象角色（如 DRIVER） |
| payeeRoleName | String | 借款对象角色中文名（字典 staff_role） |
| advanceType | String | 预支类型（如 ACCOMMODATION_DEPOSIT） |
| advanceTypeName | String | 预支类型中文名（字典 advance_type） |
| amount | BigDecimal | 预支金额，保留两位小数 |
| purpose | String | 预支用途 |
| voucherUrl | String | 凭证 URL |
| status | String | 预支状态：SUBMITTED / APPROVED / REJECTED |
| statusText | String | 预支状态中文名（枚举 label） |
| submittedAt | String | 提交时间，格式 yyyy-MM-dd HH:mm:ss |
| approvedAt | String | 审批时间，格式 yyyy-MM-dd HH:mm:ss |
| approvedBy | String | 审批人姓名 |

### 5.2 group 出参（Result<SettlementGroupReportRespVO>，最终结构：全部平铺顶层）

| 字段 | 类型 | 说明 |
|------|------|------|
| id | Long(String) | 单团核算记录 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 | 定制师姓名 |
| travelerComposition | String | 出行人构成（数量为 0 的档不显示，全空为 null） |
| baseOrderAmount | BigDecimal | 订单应收（基础订单金额） |
| otherIncomeAmount | BigDecimal | 其他收入合计 |
| discountAmount | BigDecimal | 优惠金额（负数） |
| adjustedReceivableAmount | BigDecimal | 调整后应收 = baseOrderAmount + otherIncomeAmount + discountAmount |
| paidAmount | BigDecimal | 已收金额 |
| actualRefundedAmount | BigDecimal | 实际退款金额 |
| netRevenueAmount | BigDecimal | 净收入 = adjustedReceivableAmount - actualRefundedAmount |
| netReceivedAmount | BigDecimal | 净实收 = paidAmount - actualRefundedAmount |
| outstandingAmount | BigDecimal | 未收尾款（口径同 financial-overview） |
| hotelCost | BigDecimal | 住宿成本 |
| ticketCost | BigDecimal | 门票成本 |
| mealCost | BigDecimal | 餐食成本 |
| vehicleCost | BigDecimal | 车辆成本 |
| guideCost | BigDecimal | 导游成本 |
| photographerCost | BigDecimal | 摄影成本 |
| otherExpenseCost | BigDecimal | 其他支出成本 |
| insurancePremium | BigDecimal | 保险保费 |
| totalCost | BigDecimal | 总成本 |
| paidCost | BigDecimal | 已付成本 |
| unpaidCost | BigDecimal | 未付成本 |
| grossProfit | BigDecimal | 毛利 = netRevenueAmount - totalCost |
| grossProfitRate | BigDecimal | 毛利率（小数，netRevenueAmount=0 时填 0） |
| travelerCount | Integer | 出行人总数 |
| perCapitaRevenue | BigDecimal | 人均收入（travelerCount=0 时为 null） |
| perCapitaCost | BigDecimal | 人均成本 |
| perCapitaProfit | BigDecimal | 人均毛利 |
| incomeLines | Array | 收入行，固定 4 行（见下） |
| costCategories | Array | 成本分类行，固定 8 行（见下） |

**incomeLines 元素**（固定 4 行）：

| 字段 | 类型 | 说明 |
|------|------|------|
| type | String | BASE_ORDER（订单应收）/ OTHER_INCOME（其他收入）/ DISCOUNT（优惠，负数）/ ACTUAL_REFUND（实际退款，负数） |
| typeName | String | 行类型中文名（字典 settlement_report_line_type） |
| amount | BigDecimal | 金额，保留两位小数；DISCOUNT / ACTUAL_REFUND 为负数 |
| details | Array | 收入逐项明细；无逐项时为空数组 [] |

**costCategories 元素**（固定 8 行）：

| 字段 | 类型 | 说明 |
|------|------|------|
| category | String | HOTEL / TICKET / MEAL / VEHICLE / GUIDE / PHOTOGRAPHER / OTHER_EXPENSE / INSURANCE |
| categoryName | String | 费用类别中文名（字典 settlement_category） |
| amount | BigDecimal | 金额，保留两位小数 |
| lines | Array | 成本逐项明细（分类专属行结构，前端按 category 判别窄化）；无逐项时为空数组 [] |

### 5.3 finalize 出参（Result<SettlementSubmitRespVO>，本次仅 warnings 变化）

| 字段 | 类型 | 说明 |
|------|------|------|
| summaryId | Long(String) | 新写入的 settlement_summary 主键，序列化为字符串 |
| finalSnapshotId | Long(String) | 核单终态快照 ID，序列化为字符串 |
| finalSnapshotVersionNo | Integer | 核单终态快照版本号 |
| finalSnapshotStatus | String | 核单终态快照状态，固定 FINALIZED |
| orderId | Long(String) | 订单 ID，序列化为字符串 |
| settledAt | String | 核单完成时间，格式 yyyy-MM-ddTHH:mm:ss |
| totalAmount | String | 订单总金额快照（BigDecimal 序列化为字符串，下同） |
| paidAmount | String | 已付金额快照 |
| balanceAmount | String | 尾款金额快照 |
| roomCost | String | 住宿实际成本 |
| ticketCost | String | 门票实际成本 |
| staffCost | String | 人员费用实际成本 |
| subsidyCost | String | 补助实际成本 |
| mealCost | String | 餐食实际成本 |
| vehicleCost | String | 车辆基础服务总车费 |
| otherExpenseCost | String | 其他支出实际成本 |
| insurancePremium | String | 保险实际保费（未出单/已撤单 = 0） |
| totalActualCost | String | 总实际成本 |
| driverTransferAmount | String | 给司机/主报账人转回金额（仅计 CASH_PAID，不含保险） |
| profitAmount | String | 公司毛利 = totalAmount - totalActualCost |
| profitRate | Number | 毛利率（小数，totalAmount=0 时填 0） |
| orderStatusAfter | String | 结算后订单状态中文文案，如「待财务复核」 |
| mqTriggered | Boolean | 当前版本固定 false；完成核单不发布结算 MQ |
| warnings | Array<WarningItemVO> | **本次变更**：软预警对象数组（原来为字符串数组）；无预警时为空数组 [] |

**WarningItemVO 字段表**：

| 字段 | 类型 | 说明 |
|------|------|------|
| settlementId | Long(String) | 触发预警的核单明细行 id（settlement_hotel / settlement_ticket 主键），序列化为字符串；前端据此锚定/跳转具体明细行 |
| category | String | 核单分类枚举名，当前仅 HOTEL（住宿）/ TICKET（门票/游玩项目）两类会触发预警 |
| dayNumber | Integer | 行程第几天 |
| message | String | 预警文案，如「住宿 D2 现付缺凭证」 |

### 5.4 summary 出参（Result<SettlementSummaryRespVO>，本次新增 settled）

| 字段 | 类型 | 说明 |
|------|------|------|
| settled | Boolean | **本次新增**：是否已核单。summary 快照存在 = true；未核单空壳 = false |
| id | Long | settlement_summary 主键；未核单时为 null |
| orderId | Long | 订单 ID；空壳时也有值 |
| totalAmount | String | 订单总金额快照（BigDecimal 序列化为字符串，下同） |
| paidAmount | String | 已付金额快照 |
| balanceAmount | String | 尾款金额快照 |
| roomCost | String | 住宿实际成本 |
| ticketCost | String | 门票实际成本 |
| staffCost | String | 人员费用实际成本 |
| subsidyCost | String | 补助实际成本 |
| mealCost | String | 餐食实际成本 |
| vehicleCost | String | 车辆基础服务总车费 |
| otherExpenseCost | String | 其他支出实际成本 |
| insurancePremium | String | 保险实际保费 |
| refundTotal | String | 返还合计（只读展示，不进利润公式） |
| totalActualCost | String | 总实际成本 |
| driverTransferAmount | String | 给司机/主报账人转回金额（仅计 CASH_PAID） |
| profitAmount | String | 公司毛利 |
| profitRate | Number | 毛利率（小数） |
| settledBy | Long | 核单人 id；未核单时为 null |
| settledByName | String | 核单人姓名快照 |
| settledAt | String | 核单完成时间，格式 yyyy-MM-ddTHH:mm:ss |
| remark | String | 核单备注 |
| finalSnapshotId | Long(String) | 当前核单终态快照 ID；未完成核单或已重新打开时为 null |
| finalSnapshotVersionNo | Integer | 当前核单终态快照版本号；无当前快照时为 null |
| finalSnapshotStatus | String | 有当前快照时固定 FINALIZED，否则为 null |
| finalizedAt | String | 当前核单终态快照完成时间；无当前快照时为 null |
| finalizedByName | String | 当前核单终态快照操作人姓名；无当前快照时为 null |
| advanceSummary | Object | 已审批通过的订单预支汇总；**恒下发对象，不是 null**（空壳时也有值），含 approvedAmount（String，默认 "0"）+ records（Array，默认 []） |

> 未核单空壳时：settled=false，仅 orderId 与 advanceSummary（默认空对象）有值，**其余字段全部为 null**。

## 6. 枚举 / 数据字典

| 字段 | 来源 | 取值 |
|------|------|------|
| expenseLines[].category / costCategories[].category | settlement_category 字典 | HOTEL（住宿）/ TICKET（门票）/ MEAL（餐食）/ VEHICLE（车辆）/ GUIDE（导游）/ PHOTOGRAPHER（摄影）/ OTHER_EXPENSE（其他支出）/ INSURANCE（保险） |
| expenseLines[].paymentMethod | settlement_payment_method 字典 | CASH_PAID（现金已付）/ COMPANY_PAID（公司直付）/ SIGNED（签单）；报账支出行固定 CASH_PAID |
| incomeLines[].type / advanceLines[].type | settlement_report_line_type 字典 | DRIVER_CASH_RECEIPT / APPROVED_ADVANCE / BASE_ORDER / OTHER_INCOME / DISCOUNT / ACTUAL_REFUND |
| incomeLines[].channel（reimbursement） | PaymentChannelEnum 枚举 | DRIVER_CASH / BANK_TRANSFER / CONSULTANT_COLLECTION |
| incomeLines[].payType（reimbursement） | PayType 枚举 | DEPOSIT（订金）/ FULL（全款）/ BALANCE（尾款） |
| collectorRole / payeeRole / primaryReporterRole | staff_role 字典 | DRIVER（司机）/ GUIDE（导游）等 |
| advanceLines[].advanceType | advance_type 字典 | 如 ACCOMMODATION_DEPOSIT（住宿押金）等 |
| advanceLines[].status | AdvanceStatus 枚举 | SUBMITTED / APPROVED / REJECTED |
| productType | product_type 字典 | 如 CORE（核心产品）等 |
| warnings[].category | SettlementCategory 枚举 | 全量同 settlement_category；**当前预警只会出现 HOTEL / TICKET** |
| finalSnapshotStatus | 快照状态枚举 | FINALIZED（已定稿） |

本次无枚举值增删；category 只是从隐含在预警文案里变成显式字段。

## 7. 错误码

### 7.1 reimbursement / group（两报表）

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

### 7.2 finalize

| code | message | 触发场景 |
|------|---------|----------|
| 581007 | 订单不存在 | orderId 查不到订单 |
| 584081 | 对账数据不一致：已付金额与支付流水和线下收款合计不符，禁止带病结算 | paid_amount 与实际入账总额不符 |
| 584085 | 存在辅助人员结算未完成，请全部结算后提交 | 任一非主报账司机费用行未结算完成或缺转账凭证号 |
| 584321 | 当前订单缺少核单终态快照，请重新完成核单 | 缺终态快照（含重复提交场景） |
| 400 | 订单 ID 必须大于 0 | orderId 路径参数校验失败 |
| 403 | 无权限访问 | 房务角色（HOUSE）JWT 调用 |

> finalize 还有人员费用 / 其他收入未就绪等前置门禁错误码（584xxx 段），非本次变更，按响应 message 直接提示即可。

### 7.3 summary

只读接口，无业务错误码；orderId 查不到快照时返回 200 + 空壳对象（settled=false），**不会报「订单不存在」**。

## 8. 示例

### 8.1 典型成功

**reimbursement**（最终结构：抬头在 baseInfo 内，无 orderHeader 块）：

    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": 5,
      "travelerComposition": "2大 2儿童 1幼童",
      "primaryReporterId": "7001",
      "primaryReporterName": "司机甲",
      "primaryReporterRole": "DRIVER",
      "primaryReporterCollectedAmount": 2000.00,
      "approvedAdvanceAmount": 500.00,
      "reportablePaidCostAmount": 1800.00,
      "reporterNetAmount": 700.00,
      "outstandingAmount": 0.00
    },
    "incomeLines": [
      {
        "type": "DRIVER_CASH_RECEIPT",
        "typeName": "司机现金收款",
        "receiptId": "8001",
        "amount": 2000.00,
        "channel": "DRIVER_CASH",
        "channelName": "报账人收款",
        "payType": "BALANCE",
        "payTypeName": "尾款",
        "collectorStaffId": "7001",
        "collectorName": "司机甲",
        "collectorRole": "DRIVER",
        "collectorRoleName": "司机",
        "receivedAt": "2026-07-30 18:20:30",
        "remark": "尾款现金"
      }
    ],
    "expenseLines": [
      {
        "category": "HOTEL",
        "categoryName": "住宿",
        "itemName": "草原明珠大酒店-标间",
        "unitPrice": 400.00,
        "quantity": 3,
        "amount": 1200.00,
        "reimburseAmount": 1200.00,
        "paymentMethod": "CASH_PAID",
        "paymentMethodName": "现金已付",
        "date": "2026-07-30",
        "remark": "含早",
        "voucherUrls": ["https://oss/v1.jpg"]
      }
    ],
    "advanceLines": [
      {
        "type": "APPROVED_ADVANCE",
        "typeName": "已审批预支",
        "advanceId": "5001",
        "payeeStaffId": "7001",
        "payeeName": "司机甲",
        "payeeRole": "DRIVER",
        "payeeRoleName": "司机",
        "advanceType": "ACCOMMODATION_DEPOSIT",
        "advanceTypeName": "住宿押金",
        "amount": 500.00,
        "purpose": "酒店押金",
        "voucherUrl": "https://oss/advance-v1.jpg",
        "status": "APPROVED",
        "statusText": "已通过",
        "submittedAt": "2026-07-28 10:00:00",
        "approvedAt": "2026-07-28 12:00:00",
        "approvedBy": "财务丙"
      }
    ]
  },
  "success": true
}
```

**group**（最终结构：抬头平铺顶层，无 orderHeader 块）：

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

```json
{
  "code": 200,
  "message": "成功",
  "data": {
    "id": "6002",
    "orderId": "2087088947225038849",
    "orderNo": "HL20260730001",
    "teamNo": "T20260730001",
    "productName": "呼伦贝尔草原 5 日游",
    "productType": "CORE",
    "productTypeName": "核心产品",
    "customerName": "张三",
    "departDate": "2026-07-30",
    "returnDate": "2026-08-03",
    "consultantName": "李四",
    "travelerComposition": "2大 2儿童 1幼童",
    "baseOrderAmount": 12800.00,
    "otherIncomeAmount": 600.00,
    "discountAmount": -200.00,
    "adjustedReceivableAmount": 13200.00,
    "paidAmount": 13200.00,
    "actualRefundedAmount": 0.00,
    "netRevenueAmount": 13200.00,
    "netReceivedAmount": 13200.00,
    "outstandingAmount": 0.00,
    "hotelCost": 3600.00,
    "ticketCost": 1500.00,
    "mealCost": 900.00,
    "vehicleCost": 2500.00,
    "guideCost": 800.00,
    "photographerCost": 0.00,
    "otherExpenseCost": 300.00,
    "insurancePremium": 180.00,
    "totalCost": 9780.00,
    "paidCost": 9780.00,
    "unpaidCost": 0.00,
    "grossProfit": 3420.00,
    "grossProfitRate": 0.2591,
    "travelerCount": 5,
    "perCapitaRevenue": 2640.00,
    "perCapitaCost": 1956.00,
    "perCapitaProfit": 684.00,
    "incomeLines": [
      { "type": "BASE_ORDER", "typeName": "订单应收", "amount": 12800.00, "details": [] },
      { "type": "OTHER_INCOME", "typeName": "其他收入", "amount": 600.00, "details": [] },
      { "type": "DISCOUNT", "typeName": "优惠", "amount": -200.00, "details": [] },
      { "type": "ACTUAL_REFUND", "typeName": "实际退款", "amount": 0.00, "details": [] }
    ],
    "costCategories": [
      { "category": "HOTEL", "categoryName": "住宿", "amount": 3600.00, "lines": [] },
      { "category": "TICKET", "categoryName": "门票", "amount": 1500.00, "lines": [] },
      { "category": "MEAL", "categoryName": "餐食", "amount": 900.00, "lines": [] },
      { "category": "VEHICLE", "categoryName": "车辆", "amount": 2500.00, "lines": [] },
      { "category": "GUIDE", "categoryName": "导游", "amount": 800.00, "lines": [] },
      { "category": "PHOTOGRAPHER", "categoryName": "摄影", "amount": 0.00, "lines": [] },
      { "category": "OTHER_EXPENSE", "categoryName": "其他支出", "amount": 300.00, "lines": [] },
      { "category": "INSURANCE", "categoryName": "保险", "amount": 180.00, "lines": [] }
    ]
  },
  "success": true
}
```

### 8.2 边界情况

**边界 1：订金未支付、无团号**——reimbursement 的 baseInfo 因 NON_NULL 不输出 teamNo 键；group 顶层 teamNo 为 null。前端两处都要兜底：

```json
"baseInfo": {
  "orderId": "2087088947225038850",
  "orderNo": "HL20260801002",
  "productName": "库布其沙漠 2 日游",
  "outstandingAmount": 3200.00
}
```

**边界 2：无任何明细行**——incomeLines / expenseLines / advanceLines 均为空数组 []，不是 null：

```json
{ "incomeLines": [], "expenseLines": [], "advanceLines": [] }
```

**边界 3：finalize 无软预警 + summary 查未核单订单**——warnings 为空数组 []；summary 返回空壳（settled=false，除 orderId / advanceSummary 外全 null）：

```json
{ "code": 200, "data": { "settled": false, "id": null, "orderId": "2087088947225038850", "totalAmount": null, "advanceSummary": { "approvedAmount": "0", "records": [] } }, "success": true }
```

### 8.3 业务失败

**finalize 辅助人员结算未完成**：

```json
{ "code": 584085, "message": "存在辅助人员结算未完成，请全部结算后提交", "success": false }
```

**房务角色访问报表被拦**（House 角色 JWT 调 reimbursement / group / finalize）：

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

**订单不存在**：

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

## 9. 业务边界

**适用**：

- reimbursement：订单核单页查看主报账人结算报表；支出行只含报账人垫付（CASH_PAID）支出，签单/公司直付不进本列表
- group：整团收入成本毛利核算；incomeLines 固定 4 行、costCategories 固定 8 行恒下发（金额为 0 的行也在）
- finalize：核单明细已逐步保存完成、核对无误后提交
- summary：随时读取汇总；未核单订单也可调（返回空壳），前端据此展示「尚未核单」占位

**不适用**：

- reimbursement：想看待收整单尾款时不要再用已删的 primaryReporterDueAmount（主报账人维度），改读 outstandingAmount（整单维度）
- finalize：明细未保存完整 / 辅助人员结算未完成 / 对账不一致的订单（被 584xxx 门禁拦截，见 §7）
- summary：想看明细行级数据时不要用它，走各分类明细接口；本接口只有汇总快照

**特殊边界**：

- 报表的「报表状态 / 生成人 / 确认人」审计信息**不再下发**（DB 仍保留），前端如曾展示「已生成/已确认」徽标需删除该 UI
- warnings 是**软预警**，不阻塞 finalize 提交；有预警也照常返回 200 完成核单
- 预警仅覆盖 CASH_PAID（现付）且 voucher_urls 为空的住宿 / 门票明细行；签单、公司直付等其他支付方式不产生预警
- summary 空壳的 advanceSummary 恒为默认对象（approvedAmount="0"、records=[]），**不是 null**，不能拿它当判空依据

## 10. 修改前后对比

> 「修改前」= 两次变更前的旧结构（含 orderHeader 子块 + 审计字段）；「修改后」= 本份最终态。

### 10.1 字段级对比

| 接口 | 字段 | 修改前 | 修改后 |
|------|------|--------|--------|
| reimbursement | 顶层 orderHeader | Object 子块（11 个抬头字段） | **删除**，11 字段拍平进 baseInfo |
| reimbursement | baseInfo.reportStatus / reportVersion | 有 | **删除** |
| reimbursement | baseInfo.generatedBy / generatedByName / generatedAt | 有 | **删除** |
| reimbursement | baseInfo.confirmedBy / confirmedByName / confirmedAt | 有 | **删除** |
| reimbursement | baseInfo.transferAmount | 有（净额绝对值） | **删除**，前端用 reporterNetAmount 绝对值自取 |
| reimbursement | baseInfo.primaryReporterDueAmount | 有（主报账人维度应收尾款） | **删除**，由 outstandingAmount 替代 |
| reimbursement | baseInfo.outstandingAmount | 无 | **新增**：整单未收尾款（口径同 financial-overview） |
| group | 顶层 orderHeader | Object 子块（10 个抬头字段） | **删除**，10 字段平铺到顶层 |
| group | reportStatus | 有 | **删除** |
| group | generatedBy / generatedByName / generatedAt | 有 | **删除** |
| group | confirmedBy / confirmedByName / confirmedAt | 有 | **删除** |
| finalize | warnings | List<String>，元素如「住宿 D2 现付缺凭证」字符串 | List<WarningItemVO>，元素为对象 { settlementId, category, dayNumber, message } |
| summary | settled | 无此字段 | 新增 Boolean：已核单 = true，未核单空壳 = false |

### 10.2 行为级对比

| 场景 | 修改前 | 修改后 |
|------|--------|--------|
| 读订单抬头（两报表） | 读 orderHeader.orderNo 等 | 读 baseInfo.orderNo（reimbursement）/ 顶层 orderNo（group） |
| 展示「已生成/已确认」徽标 | 读 reportStatus / confirmedAt | **字段已删除，恒取不到值**，UI 必须删除该徽标 |
| 展示转账金额/方向 | 读 transferAmount / 推导方向 | 用 reporterNetAmount 正负推导方向，金额取绝对值 |
| 展示应收尾款（reimbursement） | 读 primaryReporterDueAmount（主报账人维度） | 读 outstandingAmount（整单维度，与财务总览一致） |
| 同日多条同类缺凭证预警 | 多条相同文案，无法区分哪一行 | 每条带独立 settlementId，可定位/跳转具体明细行 |
| 前端渲染 finalize 预警 | 直接渲染字符串 | 必须读 warnings[].message |
| 前端判「是否已核单」（summary） | 判 data.id == null（不可靠） | 判 data.settled === false |

## 11. 影响评估 / 回滚

### 11.1 影响评估

- **破坏兼容性**：⚠️ 破坏性。删 orderHeader 块 + 删 baseInfo/顶层多字段 + finalize warnings 结构变化，按旧结构取值的前端代码会拿到 undefined / 渲染异常。
- **前端必须同步上线**：是。前端需要：
  1. reimbursement 抬头取值从 orderHeader.xxx 改 baseInfo.xxx；group 抬头取值从 orderHeader.xxx 改顶层 xxx
  2. 删除「报表状态 / 生成人 / 确认人」相关 UI（字段已删，恒 undefined）
  3. 转账金额改取 reporterNetAmount 绝对值，方向按 reporterNetAmount 正负推导
  4. 应收尾款改读 outstandingAmount（注意口径已从主报账人维度变整单维度，展示文案如有「主报账人应收」字样需同步改）
  5. finalize warnings 渲染改读 item.message，可用 settlementId + category 做行锚定
  6. summary 判「是否已核单」改读 settled 字段
- **后端兼容**：零 DDL、无数据迁移；DB 的 report_status / 版本 / 审计列保留，finalize 幂等与反确认逻辑不受影响。
- **本份覆盖旧份**：此前 2026-08-12 推送的 12_5876_核单finalize-summary出参整改-修改接口-管理后台.md 内容已并入本份；两报表结构**以本份为准**。

### 11.2 回滚方案

- 后端回滚 = 依次 revert PR #5920 的 merge commit（b912ff4876）与 PR #5881 的 merge commit（125bb9c96e），重启 hl-order-service-v3。只 revert b912ff4876 则回到 #5876 后的中间态（orderHeader 块恢复 + 审计字段恢复，warnings/settled 保留）。
- 前端回滚 = 切回旧版前端包。注意新旧前后端要配套：新前端 + 旧后端时 baseInfo.orderNo / outstandingAmount / settled 取不到值。
- 零 DDL，回滚无残留风险。

## 12. 注意事项

1. **所有 Long 主键字段序列化为 JSON 字符串**（id / orderId / primaryReporterId / receiptId / collectorStaffId / advanceId / payeeStaffId / settlementId 等，防 JS 精度丢失），前端按 string 处理，不要 Number() 转换。
2. reimbursement 的 baseInfo / incomeLines / expenseLines / advanceLines 均带 @JsonInclude(NON_NULL)：**null 字段不下发该 key**（不是下 null），前端读取必须兜底，不能假设 key 恒存在。
3. group 出参不做 NON_NULL 裁剪，teamNo 等可空字段会下发 null，注意与 reimbursement 的差异。
4. expenseLines 支出行**只含报账人垫付（CASH_PAID）支出**；签单 / 公司直付不在本列表，不要拿 expenseLines 合计去对整单成本（整单成本看 group 报表）。
5. group 的 incomeLines 固定 4 行、costCategories 固定 8 行恒下发（含金额 0 的行），前端按 type / category 取值渲染即可，不要按数组下标硬编码。
6. outstandingAmount 是**整单**未收尾款（与核单应收财务总览同口径），不是主报账人维度；两报表该字段口径一致。
7. warnings 无预警时返回空数组 [] 不是 null；category 当前只会出现 HOTEL / TICKET，做映射表建议按全量 settlement_category 枚举写，缺值兜底显示原 code。
8. summary 判空**只认 settled**：settled === false 即「尚未核单」，此时除 orderId / advanceSummary 外全部字段为 null，展示必须兜底。
9. finalize 是写操作且有前置门禁，失败按错误码 message 提示，**不要静默重试**；重复提交会被快照类错误码拦截。
10. finalize / summary 的金额类字段序列化为**字符串**；两报表（reimbursement / group）的金额类字段为 JSON 数值（BigDecimal 两位小数），两类接口序列化风格不同，前端注意区分。

## 13. 关联 / 联系人

- Issue #5876：https://git.1814.love:8443/wx/HL/issues/5876
- Issue #5916：https://git.1814.love:8443/wx/HL/issues/5916
- PR #5881：https://git.1814.love:8443/wx/HL/pulls/5881
- PR #5920：https://git.1814.love:8443/wx/HL/pulls/5920
- Commit（#5876）：https://git.1814.love:8443/wx/HL/commit/125bb9c96e
- Commit（#5916 最终态）：https://git.1814.love:8443/wx/HL/commit/b912ff4876
- 服务：hl-order-service-v3（端口 8086）
- 后端负责人：yst（腰苏图）
