hl-api-changelog/changelogs-v2/2026-08/12_5916_核单两报表出参瘦身-修改接口-管理后台.md
Mimingguang dfbd18506c
一些检查失败了
changelog-filename-gate / validate (push) Failing after 1s
docs(changelogs-v2): #5912/#5916/#5914 前端 verified,#5915 not_required
#5912 改期残留三字段消费 frontend_ref=6c417cac
#5916 核单两报表瘦身 frontend_ref=ea2916fc
#5914 换版原因渲染 frontend_ref=a254f29f
#5915 删换车请求状态实证 not_required frontend_ref=58428593
2026-08-12 19:03:39 +08:00

708 行
39 KiB
Markdown

此文件含有模棱两可的 Unicode 字符

此文件含有可能会与其他字符混淆的 Unicode 字符。 如果您是想特意这样的,可以安全地忽略该警告。 使用 Escape 按钮显示他们。

---
schema: "hl-changelog/v2"
ticket: "5916"
title: "核单两报表出参瘦身合并版(#5876 + #5916orderHeader 拍平 + 审计字段下线 + 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: "本份是 #5876PR #5881)与 #5916PR #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核单页 / 财务报表页读取整单金额、成本、毛利汇总。
两次变更分别做了什么:
- **#5876PR #5881**finalize 出参 warnings 由字符串数组改为对象数组可定位明细行;summary 出参新增 settled 字段(可靠判「是否已核单」)。
- **#5916PR #5920,最终态)**:两报表出参瘦身 —— 顶层 orderHeader 子块下线(抬头字段拍平),报表状态 / 生成确认审计字段出参下线,reimbursement 的尾款口径由「主报账人维度」换成「整单未收尾款」。
## 2. 变更清单
| # | 接口 | 变更 | 类型 |
|---|------|------|------|
| 1 | reimbursement | 顶层 orderHeader 子块删除,11 个订单抬头字段拍平进 baseInfoorderNo/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) | 核单记录 IDsettlement_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) | 触发预警的核单明细行 idsettlement_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**(空壳时也有值),含 approvedAmountString,默认 "0"+ recordsArray,默认 [] |
> 未核单空壳时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[].channelreimbursement | PaymentChannelEnum 枚举 | DRIVER_CASH / BANK_TRANSFER / CONSULTANT_COLLECTION |
| incomeLines[].payTypereimbursement | 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 | 无权限访问 | 房务角色HOUSEJWT 调用 |
| 581007 | 订单不存在 | orderId 查不到订单 |
### 7.2 finalize
| code | message | 触发场景 |
|------|---------|----------|
| 581007 | 订单不存在 | orderId 查不到订单 |
| 584081 | 对账数据不一致:已付金额与支付流水和线下收款合计不符,禁止带病结算 | paid_amount 与实际入账总额不符 |
| 584085 | 存在辅助人员结算未完成,请全部结算后提交 | 任一非主报账司机费用行未结算完成或缺转账凭证号 |
| 584321 | 当前订单缺少核单终态快照,请重新完成核单 | 缺终态快照(含重复提交场景) |
| 400 | 订单 ID 必须大于 0 | orderId 路径参数校验失败 |
| 403 | 无权限访问 | 房务角色HOUSEJWT 调用 |
> 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": [] }
```
**边界 3finalize 无软预警 + 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.orderNoreimbursement/ 顶层 orderNogroup |
| 展示「已生成/已确认」徽标 | 读 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 commitb912ff4876与 PR #5881 的 merge commit125bb9c96e,重启 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 #5876https://git.1814.love:8443/wx/HL/issues/5876
- Issue #5916https://git.1814.love:8443/wx/HL/issues/5916
- PR #5881https://git.1814.love:8443/wx/HL/pulls/5881
- PR #5920https://git.1814.love:8443/wx/HL/pulls/5920
- Commit#5876https://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腰苏图