hl-api-changelog/changelogs-v2/2026-08/09_5739_核单指纹下线-修改接口-管理后台.md

18 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 5739 核单报表快照指纹彻底下线:报表出参删除 sourceFingerprint,reportStatus 不再返回 STALE admin 修改接口 yaosutu(GIT) deployed verified claimed mmg 前端已认领:实证确认存在 stale 死代码链路adaptSettlementReport 投影 stale + ReportModal 徽章/提示/表单守卫 + detail.vue openReport stale 重载),将停读并清理后与后端同批发布(前端先上)。 2026-08-10 dev-v3

⚠️ 修改接口·管理后台】核单报表快照指纹彻底下线,出参删 sourceFingerprint、reportStatus 不再返回 STALE#5739

PR: #5764 | 服务: hl-order-service-v3 | 更新时间: 2026-08-09

1. 接口背景

本次是核单域指纹机制下线的收尾批次,承接 #5704PR #5709,写入路径 6 端点指纹入参/出参下线)。

#5704 之后,核单报表(单团核算表 / 主报账人报账表)的读取路径仍残留一套「快照指纹」逻辑:报表落库时记录来源数据 sha256 指纹,前端每次 GET 报表时后端实时重算当前来源指纹,两者不一致就把 reportStatus 实时改报为 STALE提示「数据已变化,需刷新」,并在出参中携带 sourceFingerprint。

指纹机制整体废弃后,该派生逻辑同步删除:

  • 两个报表查询接口出参删除 sourceFingerprint 字段
  • reportStatus 枚举删除 STALE 值,只保留落库状态 GENERATED / CONFIRMED,接口不再做实时指纹比对。

⚠️ 本次为出参删字段 + 枚举删值的硬破坏契约:前端若还在读取 sourceFingerprint、或对 reportStatus === 'STALE' 写过特判(刷新提示 / 重新生成按钮等),必须清理后再与后端同批发布。

2. 变更清单

# 接口 方法 路径 变更类型 前端动作
1 查询单团核算表 GET /v3/admin/order/{orderId}/settlement/reports/group 删除出参 sourceFingerprint;reportStatus 枚举删除 STALE 停读字段 / 清理 STALE 特判
2 查询主报账人报账表 GET /v3/admin/order/{orderId}/settlement/reports/reimbursement 删除出参 sourceFingerprint;reportStatus 枚举删除 STALE 停读字段 / 清理 STALE 特判

3. 接口详情

  • 使用场景:核单页面展示单团核算表(全团收入/成本/毛利核算)与主报账人报账表(主报账人收付对账与转账结论)。
  • 认证需要管理后台登录态Bearer Token;房务角色HOUSE被 OrderViewGuard.assertNotHouseRole 拦截。
  • 幂等性:只读查询,天然幂等。
  • 限流:未声明接口专属限流。
  • 方法/路径:见 §2 变更清单。

4. 接口入参

4.1 路径参数

参数 类型 必填 说明
orderId Long 订单 ID,路径参数,必须大于 0

4.2 请求体字段

GET 请求,无请求体,无 Query 参数)。入参零变化

5. 出参字段

5.1 GET /v3/admin/order/{orderId}/settlement/reports/group单团核算表

统一响应 Result 包装,data 字段如下(按响应 JSON 字段序):

字段 类型 说明
id String(Long) 报表记录 ID;报表未落库时可为 null
orderId String(Long) 订单 ID
reportStatus String 报表状态,枚举见 §6;不再返回 STALE
sourceFingerprint - 已删除,前端不再收到此字段
baseOrderAmount String(BigDecimal) 订单应收(基础订单金额)
otherIncomeAmount String(BigDecimal) 其他收入合计
discountAmount String(BigDecimal) 优惠合计(负数)
adjustedReceivableAmount String(BigDecimal) 调整后应收
paidAmount String(BigDecimal) 已收金额
actualRefundedAmount String(BigDecimal) 实际退款金额
netRevenueAmount String(BigDecimal) 净收入
netReceivedAmount String(BigDecimal) 净收款
outstandingAmount String(BigDecimal) 待收尾款
hotelCost String(BigDecimal) 住宿成本
ticketCost String(BigDecimal) 门票成本
mealCost String(BigDecimal) 餐食成本
vehicleCost String(BigDecimal) 车辆成本
guideCost String(BigDecimal) 导游成本
photographerCost String(BigDecimal) 摄影成本
otherExpenseCost String(BigDecimal) 其他支出成本
insurancePremium String(BigDecimal) 保费
totalCost String(BigDecimal) 成本合计
paidCost String(BigDecimal) 已付成本
unpaidCost String(BigDecimal) 未付成本
grossProfit String(BigDecimal) 毛利
grossProfitRate String(BigDecimal) 毛利率
travelerCount Integer 出行人数
perCapitaRevenue String(BigDecimal) 人均收入
perCapitaCost String(BigDecimal) 人均成本
perCapitaProfit String(BigDecimal) 人均毛利
incomeLines Array 收入行,固定 4 行;每项含 type(BASE_ORDER/OTHER_INCOME/DISCOUNT/ACTUAL_REFUND) / typeName / amount
costCategories Array 成本分类行,固定 8 行;每项含 category(HOTEL/TICKET/MEAL/VEHICLE/GUIDE/PHOTOGRAPHER/OTHER_EXPENSE/INSURANCE) / categoryName / amount
generatedBy String(Long) 生成人 ID
generatedByName String 生成人姓名
generatedAt String 生成时间,格式 yyyy-MM-dd HH:mm:ss
confirmedBy String(Long) 确认人 ID;未确认为 null
confirmedByName String 确认人姓名
confirmedAt String 确认时间

5.2 GET /v3/admin/order/{orderId}/settlement/reports/reimbursement主报账人报账表

统一响应 Result 包装,data 字段如下:

字段 类型 说明
id String(Long) 报表记录 ID;未落库时可为 null
orderId String(Long) 订单 ID
reportStatus String 报表状态,枚举见 §6;不再返回 STALE
sourceFingerprint - 已删除,前端不再收到此字段
primaryReporterId String(Long) 主报账人人员安排 ID
primaryReporterName String 主报账人姓名
primaryReporterRole String 主报账人角色(如 DRIVER
reportVersion Integer 报表版本号
driverCollectedTailAmount String(BigDecimal) 司机代收尾款
approvedAdvanceAmount String(BigDecimal) 已审批预支金额
reportablePaidCostAmount String(BigDecimal) 可报账已付成本
reporterNetAmount String(BigDecimal) 报账人净额
primaryReporterCollectedAmount String(BigDecimal) 主报账人已收款
publicPrepaidAmount String(BigDecimal) 对公预付金额
primaryReporterDueAmount String(BigDecimal) 主报账人应结金额
advanceOutstandingAmount String(BigDecimal) 预支未销余额
reconNetAmount String(BigDecimal) 对账净额
transferDirection String 转账方向
transferAmount String(BigDecimal) 转账金额
incomeLines Array 收款行;每项含 type / typeName / receiptId / amount / channel / channelName / payType / payTypeName / collectorStaffId / collectorName / collectorRole / collectorRoleName / receivedAt / remark
expenseLines Array 支出行;每项含 kind / category / categoryName / amount / paymentMethod / paymentMethodName / voucherUrls / remark + 类别扩展字段(住宿 hotelName/roomTypeName 等)
advanceLines Array 预支行
vehicleLines Array 已确认车辆逐日费用明细;无数据固定返回空数组
transferStatus String 转账状态
transferDate String 转账日期,格式 yyyy-MM-dd
transferRef String 转账流水号
advanceSettledFlag Boolean 预支是否已处理
signedVoucher Object 签字凭证;含 files[{name,url}] + note
generatedBy String(Long) 生成人 ID
generatedByName String 生成人姓名
generatedAt String 生成时间
confirmedBy String(Long) 确认人 ID
confirmedByName String 确认人姓名
confirmedAt String 确认时间

上述两表除删除 sourceFingerprint 外,其余字段名称、类型、语义均无变化。前端不要再读取 sourceFingerprint,读取结果恒为 undefined。

6. 枚举 / 数据字典

reportStatus两接口共用

含义 本次变化
GENERATED 报表已生成(或实时计算态),未确认 不变
CONFIRMED 报表已随完成核单确认 不变
STALE 原语义:落库后来源数据发生变化(指纹不一致),实时派生 已删除,不再返回

STALE 原触发场景(现已消亡):报表落库(生成/确认)后,订单的收退款、费用明细、车辆费用等来源数据又被修改(含 reopen 反确认后改数),原来 GET 报表时后端会比对落库指纹与实时指纹,不一致则把 reportStatus 改报 STALE。现在该实时比对整体移除,reportStatus 就是落库原值。

其余枚举/字典incomeLines[].type、costCategories[].category、channel、payType、paymentMethod、transferDirection、transferStatus 等)取值与语义均无变化。

7. 错误码

本次无新增、无删除错误码。两接口既有的访问拦截(如房务角色 403、订单不存在行为不变;finalize 前置的双报告已生成保护584311 / 584313不在本次范围、保持不变。

8. 示例

8.1 典型成功(单团核算表)

GET /v3/admin/order/2084000000000002978/settlement/reports/group
Authorization: Bearer <token>
{
  "code": 200,
  "message": "success",
  "data": {
    "id": "8801",
    "orderId": "2084000000000002978",
    "reportStatus": "GENERATED",
    "baseOrderAmount": "12800.00",
    "otherIncomeAmount": "500.00",
    "discountAmount": "-300.00",
    "adjustedReceivableAmount": "13000.00",
    "paidAmount": "13000.00",
    "actualRefundedAmount": "0.00",
    "netRevenueAmount": "13000.00",
    "netReceivedAmount": "13000.00",
    "outstandingAmount": "0.00",
    "hotelCost": "3600.00",
    "ticketCost": "1200.00",
    "mealCost": "800.00",
    "vehicleCost": "2000.00",
    "guideCost": "500.00",
    "photographerCost": "0.00",
    "otherExpenseCost": "100.00",
    "insurancePremium": "200.00",
    "totalCost": "8400.00",
    "paidCost": "7000.00",
    "unpaidCost": "1400.00",
    "grossProfit": "4600.00",
    "grossProfitRate": "0.3538",
    "travelerCount": 4,
    "perCapitaRevenue": "3250.00",
    "perCapitaCost": "2100.00",
    "perCapitaProfit": "1150.00",
    "incomeLines": [
      {"type": "BASE_ORDER", "typeName": "订单应收", "amount": "12800.00"},
      {"type": "OTHER_INCOME", "typeName": "其他收入", "amount": "500.00"},
      {"type": "DISCOUNT", "typeName": "优惠", "amount": "-300.00"},
      {"type": "ACTUAL_REFUND", "typeName": "实际退款", "amount": "0.00"}
    ],
    "costCategories": [
      {"category": "HOTEL", "categoryName": "住宿", "amount": "3600.00"},
      {"category": "TICKET", "categoryName": "门票", "amount": "1200.00"}
    ],
    "generatedBy": "1001",
    "generatedByName": "张三",
    "generatedAt": "2026-08-09 10:20:30",
    "confirmedBy": null,
    "confirmedByName": null,
    "confirmedAt": null
  },
  "success": true
}

注意:响应中已无 sourceFingerprint 字段;reportStatus 只会是 GENERATED 或 CONFIRMED。

8.2 边界(报账表无车辆明细 + 未落库实时态)

报表未落库订单尚未生成过报账表时接口仍正常返回实时计算结果,id 可为 null、reportStatus 固定 GENERATED、vehicleLines 固定空数组:

GET /v3/admin/order/2084000000000002978/settlement/reports/reimbursement
Authorization: Bearer <token>
{
  "code": 200,
  "message": "success",
  "data": {
    "id": null,
    "orderId": "2084000000000002978",
    "reportStatus": "GENERATED",
    "primaryReporterId": "7001",
    "primaryReporterName": "司机甲",
    "primaryReporterRole": "DRIVER",
    "reportVersion": null,
    "driverCollectedTailAmount": "0.00",
    "approvedAdvanceAmount": "0.00",
    "reportablePaidCostAmount": "0.00",
    "reporterNetAmount": "0.00",
    "primaryReporterCollectedAmount": "0.00",
    "publicPrepaidAmount": "0.00",
    "primaryReporterDueAmount": "0.00",
    "advanceOutstandingAmount": "0.00",
    "reconNetAmount": "0.00",
    "transferDirection": null,
    "transferAmount": "0.00",
    "incomeLines": [],
    "expenseLines": [],
    "advanceLines": [],
    "vehicleLines": [],
    "transferStatus": null,
    "transferDate": null,
    "transferRef": null,
    "advanceSettledFlag": null,
    "signedVoucher": null,
    "generatedBy": null,
    "generatedByName": null,
    "generatedAt": null,
    "confirmedBy": null,
    "confirmedByName": null,
    "confirmedAt": null
  },
  "success": true
}

8.3 业务失败(订单不存在)

GET /v3/admin/order/999999999/settlement/reports/group
Authorization: Bearer <token>
{"code": 584xxx, "message": "订单不存在", "data": null, "success": false}

该失败路径与本次变更无关,仅作最小复现样例;具体错误码以订单域统一「订单不存在」码为准。

9. 业务边界

  • 报表落库后再改来源数据收款、费用、车辆费用等,reportStatus 保持落库原值GENERATED / CONFIRMED,不再实时翻成 STALE;前端展示的报表金额仍是实时计算的当前值。
  • reopen反确认后重新查报表,reportStatus 按落库记录返回,不再出现 STALE 中间态。
  • 不要再依赖 reportStatus === 'STALE' 做「数据已变化」提示;该信号已彻底不存在,且没有替代字段(指纹机制整体废弃,核单为单人负责场景,见 #5704 背景)。
  • 不要把历史缓存/本地存储里的 sourceFingerprint 回传到任何接口——报表两个 GET 接口本就无入参;写入侧接口的指纹入参已在 #5704 删除。

10. 修改前后对比

10.1 字段级对比

接口 字段 原来 现在
GET reports/group sourceFingerprint 出参,64 位十六进制 sha256 已删除
GET reports/reimbursement sourceFingerprint 出参,64 位十六进制 sha256 已删除
两接口 reportStatus GENERATED / CONFIRMED / STALE实时派生 仅 GENERATED / CONFIRMED落库原值

10.2 行为级对比

场景 原来 现在
报表落库后来源数据被修改,再 GET 报表 reportStatus 实时改报 STALE 返回落库原值GENERATED 或 CONFIRMED
reopen 反确认后 GET 报表 指纹不一致,reportStatus = STALE 按落库状态返回,无 STALE
出参字段 含 sourceFingerprint 不再含该字段
报表金额数值 实时计算 实时计算(不变

11. 影响评估 / 回滚

11.1 影响评估

  • 是否破坏向后兼容是,硬破坏。出参删除 sourceFingerprint 字段 + 枚举删除 STALE 值。旧前端若读取 sourceFingerprint 得到 undefined、若对 STALE 写了特判分支则永远不会再命中(静默失效,不报错)。
  • 前端是否必须同步上线必须同批。前端需先停读 sourceFingerprint、清理 STALE 特判(刷新提示/禁用确认按钮/重新生成入口等),再与后端同批发布。
  • 上线顺序边界:前后端同批发布;若必须分先后,先上前端(停读字段 + 清理 STALE 分支),再上后端。旧前端 + 新后端不会报 500,但 STALE 相关 UI 逻辑成死代码。
  • 数据库侧:本批次后端同步 DROP 了 settlement 域 15 个指纹列Flyway 迁移随服务部署执行),与前端无直接关系,但意味着回滚后指纹无法恢复原值(见 §11.2)。

11.2 回滚方案

  • 代码回滚即恢复 sourceFingerprint 出参与 STALE 派生逻辑;但数据库指纹列已 DROP,回滚后重新计算的落库指纹为空,历史报表的 STALE 比对行为不可完全复原。
  • 因此回滚必须前后端同批回滚,且接受「历史报表不再派生 STALE」的行为落差。数据侧无业务数据修复成本指纹非业务数据

12. 注意事项

  • 本次只动两个报表查询接口的出参与 reportStatus 枚举;接口路径、入参、金额数值计算逻辑均未变化
  • 与 #5704 的关系:#5704 下线写入路径(保存/确认/完成核单 6 个端点)的指纹入参与出参;本次 #5739 下线报表读取路径的指纹出参与 STALE 派生。两批共同完成指纹机制的整体下线,前端的指纹相关代码应已全部清除。
  • 原分类确认状态 VOSettlementCategoryChecksRespVO中的 sourceFingerprint 字段本次也顺手删除、confirmStatus 的 STALE 派生同步移除;但该 VO 对应的 category-checks 接口早在 PR #5324 已下线,当前无任何端点返回它,前端无感,无需处理。
  • finalize完成核单接口的 Swagger notes 中「提交双指纹」属文档残留,实际入参指纹字段已在 #5704 删除,以 #5704 changelog 为准。
  • 小程序端(/v3/mp/*)不涉及本次变更,无需任何改动。

13. 关联 / 联系人

13.1 链接

  • Issue: #5739
  • PR: #5764
  • Merge Commit: 60e4409a
  • 前置批次: Issue #5704 / PR #5709写入路径指纹下线,changelog 见 2026-08/08_5704

13.2 联系人

  • 后端负责人: @yaosutu (yst)