hl-api-changelog/changelogs-v2/2026-08/12_5916_核单两报表出参瘦身-修改接口-管理后台.md
yaosutu d98db3e76e
一些检查失败了
changelog-filename-gate / validate (push) Failing after 2s
docs(changelog): 核单两报表出参瘦身合并版(#5876 + #5916 / PR #5881 + #5920)管理后台
orderHeader 拍平 + 审计字段下线 + outstandingAmount 替代 primaryReporterDueAmount
+ finalize warnings 结构化 + summary 新增 settled,以 #5916 合并后最终态为准
2026-08-12 17:11:30 +08:00

39 KiB

schema, ticket, title, consumer, change_type, author, backend_status, gateway_status, frontend_status, frontend_owner, frontend_ref, target_release, verified_at, status_note, updated_at, base
schema ticket title consumer change_type author backend_status gateway_status frontend_status frontend_owner frontend_ref target_release verified_at status_note updated_at base
hl-changelog/v2 5916 核单两报表出参瘦身合并版(#5876 + #5916orderHeader 拍平 + 审计字段下线 + finalize warnings 结构化 + summary 新增 settled破坏性变更 admin 修改接口 yst deployed pending pending 本份是 #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 内容并入本份,前端按本份最终结构一次改到位即可。 2026-08-12 dev-v3

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

1. 接口背景

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

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

两次变更分别做了什么:

  • #5876PR #5881finalize 出参 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 改为 List每条带 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,最终结构

顶层结构(只有 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 凭证 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,最终结构全部平铺顶层

字段 类型 说明
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,本次仅 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 字段表

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

5.4 summary 出参Result,本次新增 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
{
  "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
{
  "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。前端两处都要兜底

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

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

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

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

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

8.3 业务失败

finalize 辅助人员结算未完成

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

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

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

订单不存在

{ "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,元素如「住宿 D2 现付缺凭证」字符串 List,元素为对象 { 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 判空只认 settledsettled === false 即「尚未核单」,此时除 orderId / advanceSummary 外全部字段为 null,展示必须兜底。
  9. finalize 是写操作且有前置门禁,失败按错误码 message 提示,不要静默重试;重复提交会被快照类错误码拦截。
  10. finalize / summary 的金额类字段序列化为字符串;两报表reimbursement / group的金额类字段为 JSON 数值BigDecimal 两位小数),两类接口序列化风格不同,前端注意区分。

13. 关联 / 联系人