hl-api-changelog/changelogs-v2/2026-08/11_5840_核单两报表加订单头orderHeader-修改接口-管理后台.md
2026-08-11 17:39:37 +08:00

17 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 5840 核单两报表统一实时路径 + 出参新增订单头 orderHeader admin 修改接口 yaosutu(GIT) deployed pending not_required mmg 2026-08-11 后端 PR #5854 已 merge 到 dev-v3merge commit 452ebd63,已部署测试服并网关实调验证 orderHeader 出参生效(主实例 8086;两核单报表出参统一新增 orderHeader 订单头对象,同时去掉已核单订单读终态快照的分叉、统一实时计算。前端实证 not_required(2026-08-11,mmg):出参纯增量、向后兼容、前端非必须同步上线(§11.1)。grep+源码实证——两报表接口封装 getSettlementGroupReport/getSettlementReimbursementReport(orderV2.js:968/951)纯透传,新增 orderHeader 不破坏现有解包;ReportModal 标题栏/打印头取 props.order.teamNo/productName(ReportModal.vue:30/567),该 order 是核单详情页 getSettlementDetail 已加载的主对象(detail.vue:944),非为标题另发请求,§12「可清理重复请求(非强制)」情形本案不成立、无重复请求可清;报表金额行/明细走 reportState.data,前端当前零读取 orderHeader;行为统一(去快照分叉)对前端透明(§2/§10.2),reportStatus 取值不变,#5739 已清 STALE 死代码无残留;orderHeader null 字段不下发/对象恒下发的可空性约束对零读取的现有代码无影响。可选增强(非本次交付):未来若改由报表自身 orderHeader 承载标题栏(含 customerName/consultantName/departDate/travelerComposition),按自身排期接入。 2026-08-11 dev-v3

修改接口·管理后台】核单两报表统一实时路径 + 出参新增订单头 orderHeader#5840

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

1. 接口背景

核单工作台的两个报表接口 —— 单团核算表、主报账人报账表 —— 此前出参只有金额汇总与行列表,没有订单识别信息(订单号 / 团号 / 产品 / 客户 / 出团日期 / 定制师 / 出行人构成),报表弹窗的标题栏只能靠前端从订单详情接口另取数据拼装。

同时,两个接口此前存在快照读分叉:未完成核单的订单走实时组装,已完成核单的订单读 finalize 时落库的历史快照 JSON。两条路径的出参结构虽然对齐过,但任何出参字段演进都要同时维护两套组装代码,容易出现「未核单有某字段 / 已核单没有」的不一致。

本次变更两件事:

  1. 出参纯增量:两个接口出参统一新增 orderHeader 订单头对象(字段全部来自 order_main 单表,无跨服务 JOIN
  2. 行为统一(对前端透明):删除快照读分叉,两个接口统一从 tab 明细实时计算。已核单订单的明细在核单完成时已冻结,实时计算结果与历史快照一致,前端对数据源切换无感知;reportStatus 取值与含义不变。

2. 变更清单

# 接口 方法 路径 变更类型 说明
1 查询单团核算表 GET /v3/admin/order/{orderId}/settlement/reports/group 修改(出参纯增量 + 数据源统一) 出参 data 新增 orderHeader 对象;已核单订单不再读终态快照,统一实时计算
2 查询主报账人报账表 GET /v3/admin/order/{orderId}/settlement/reports/reimbursement 修改(出参纯增量 + 数据源统一) 出参 data 新增 orderHeader 对象;已核单订单不再读终态快照,统一实时计算

配套数据字典(前端可调 GET /admin/dict/data/{dictType} 动态渲染中文名):

字典 type 用途 本次状态
product_type 产品类型中文名orderHeader.productTypeName 回填源) 已存在(不变)

3. 接口详情

3.1 查询单团核算表

  • 使用场景:核单工作台「单团核算」页签 / 报表弹窗,财务/运营查看单团收入成本毛利全貌
  • 认证:管理后台 JWT;房务角色ROOM_MANAGER / HOUSE_KEEPER_LEAD无权调用返 581045
  • 幂等性GET 只读)
  • 限流:无

3.2 查询主报账人报账表

  • 使用场景:核单工作台「主报账人报账」页签 / 报账表弹窗,财务查看主报账人代收 / 垫付 / 预支与转账结论
  • 认证:管理后台 JWT;房务角色ROOM_MANAGER / HOUSE_KEEPER_LEAD无权调用返 581045
  • 幂等性GET 只读)
  • 限流:无

4. 接口入参

4.1 路径参数(两接口相同)

字段 类型 必填 说明
orderId Long 订单 ID,必须 > 0,否则返参数校验错误

4.2 请求体字段

无请求体(两接口均为 GET

5. 出参(响应)

5.1 单团核算表顶层结构SettlementGroupReportRespVO

响应类型:Result<SettlementGroupReportRespVO>code=200 表示成功)。

字段 类型 说明
id String 报表 IDLong 序列化为字符串,无落库记录时缺省)
orderId String 订单 IDLong 序列化为字符串)
reportStatus String 报表状态:GENERATED=已生成 / CONFIRMED=已确认(取值与含义不变,见 §6.2
orderHeader Object 本次新增:订单头,结构见 §5.3
baseOrderAmount / otherIncomeAmount / discountAmount / adjustedReceivableAmount / paidAmount / actualRefundedAmount / netRevenueAmount / netReceivedAmount / outstandingAmount Number 收入侧金额汇总(不变)
hotelCost / ticketCost / mealCost / vehicleCost / guideCost / photographerCost / otherExpenseCost / insurancePremium / totalCost / paidCost / unpaidCost / grossProfit / grossProfitRate Number 成本与毛利汇总(不变)
travelerCount / perCapitaRevenue / perCapitaCost / perCapitaProfit Number 人均指标(不变)
incomeLines Array 收入行,固定 4 行(不变)
costCategories Array 成本分类行,固定 8 行(不变)
generatedBy / generatedByName / generatedAt / confirmedBy / confirmedByName / confirmedAt String 生成 / 确认审计字段(不变,未确认时确认字段缺省)

5.2 主报账人报账表顶层结构SettlementReimbursementReportRespVO,当前最终形态

响应类型:Result<SettlementReimbursementReportRespVO>code=200 表示成功)。

字段 类型 说明
baseInfo Object 基础信息:汇总值 + 主报账人 + 生成/确认审计字段(既有结构,本次不变)
orderHeader Object 本次新增:订单头,结构见 §5.3
incomeLines Array 收入行(司机代收),无数据固定返回空数组 [](既有结构,本次不变)
expenseLines Array 支出行(仅报账人垫付 CASH_PAID 支出),无数据固定返回空数组 [](既有结构,本次不变)
advanceLines Array 预支明细行(已审批预支逐条),无数据固定返回空数组 [](既有结构,本次不变)

5.3 订单头SettlementOrderHeaderVO 本次新增,两接口共用

字段 类型 必填 说明
orderNo String 订单号,如 HL20260730001
teamNo String 团号;订金未支付时为 null该 key 不下发)
productName String 产品名,如 呼伦贝尔草原 5 日游
productType String 产品类型枚举:CORE / ROUTE / CUSTOM / GROUP(见 §6.1
productTypeName String 产品类型中文名(字典 product_type 回填;字典缺失为 null,该 key 不下发)
customerName String 客户姓名;核单财务域看真名,不脱敏
departDate String 出团日期,格式 yyyy-MM-dd
consultantName String 定制师姓名
travelerComposition String 出行人构成文案:成人→、儿童→儿童、幼童→幼童、婴儿→婴儿 四档,数量为 0 的档不显示,空格拼接(如 2大 1儿童 1幼童);四档全空为 null该 key 不下发)

⚠️ orderHeader 对象本身永远下发(两接口统一实时计算后不再出现「已核单无头」的情况);但其内部 null 字段因 VO 标注 @JsonInclude(NON_NULL) 整体缺省,key 不出现在 JSON 中,前端按可选字段处理。

6. 枚举 / 数据字典

6.1 orderHeader.productType(字典 product_type

中文
CORE 核心产品
ROUTE 自驾路书
CUSTOM 私人定制
GROUP 小蒙马

6.2 reportStatus(取值与含义不变)

中文 说明
GENERATED 已生成 订单未完成核单
CONFIRMED 已确认 订单已完成核单SETTLED

行为说明:改前 CONFIRMED 走终态快照回放、GENERATED 走实时组装;改后两种状态统一实时计算(已核单订单明细已冻结,实时结果与快照一致),前端无需区分数据源。

7. 错误码

code 含义 触发场景
400 参数校验失败 orderId 缺失或 < 1
581007 订单不存在 orderId 对应订单不存在或已删除
581045 房务角色无权查看订单详情,房务仅可配房 房务管理员 / 房务组长角色调用

8. 示例3 组:典型 / 边界 / 异常)

8.1 典型成功orderHeader 全字段填充)

请求

GET /v3/admin/order/1956112233445566778/settlement/reports/group
Authorization: Bearer <admin JWT>
(无请求体)

响应(节选,仅展示本次新增的 orderHeader 与相邻字段,其余金额 / 行列表结构与变更前一致故省略):

{
  "code": 200,
  "data": {
    "id": "1958000000000000001",
    "orderId": "1956112233445566778",
    "reportStatus": "CONFIRMED",
    "orderHeader": {
      "orderNo": "HL20260730001",
      "teamNo": "T20260730001",
      "productName": "呼伦贝尔草原 5 日游",
      "productType": "CORE",
      "productTypeName": "核心产品",
      "customerName": "张三",
      "departDate": "2026-08-15",
      "consultantName": "李四",
      "travelerComposition": "2大 1儿童 1幼童"
    },
    "baseOrderAmount": 12800.00
  },
  "msg": ""
}

主报账人报账表同一订单的 orderHeader 完全一致:

请求

GET /v3/admin/order/1956112233445566778/settlement/reports/reimbursement
Authorization: Bearer <admin JWT>
(无请求体)

响应(节选顶层结构):

{
  "code": 200,
  "data": {
    "baseInfo": {
      "orderId": "1956112233445566778",
      "reportStatus": "CONFIRMED",
      "primaryReporterName": "司机甲",
      "transferDirection": "REPORTER_TO_COMPANY",
      "transferAmount": 700.00
    },
    "orderHeader": {
      "orderNo": "HL20260730001",
      "teamNo": "T20260730001",
      "productName": "呼伦贝尔草原 5 日游",
      "productType": "CORE",
      "productTypeName": "核心产品",
      "customerName": "张三",
      "departDate": "2026-08-15",
      "consultantName": "李四",
      "travelerComposition": "2大 1儿童 1幼童"
    },
    "incomeLines": [],
    "expenseLines": [],
    "advanceLines": []
  },
  "msg": ""
}

8.2 边界情况(订金未支付 / 无出行人构成 → null 字段不下发)

场景说明:订单订金未支付(teamNo 为 null、出行人四档数量全为 0travelerComposition 为 null、产品类型字典缺失productTypeName 为 null—— 这三个 key 整体不出现在 JSON 中(不是返回 null 值)。orderHeader 对象本身仍下发。

请求

GET /v3/admin/order/1956112233445566889/settlement/reports/group
Authorization: Bearer <admin JWT>
(无请求体)

响应(节选):

{
  "code": 200,
  "data": {
    "orderId": "1956112233445566889",
    "reportStatus": "GENERATED",
    "orderHeader": {
      "orderNo": "HL20260810002",
      "productName": "阿尔山秋色 3 日游",
      "productType": "CUSTOM",
      "customerName": "王五",
      "departDate": "2026-09-01",
      "consultantName": "赵六"
    }
  },
  "msg": ""
}

8.3 业务失败(订单不存在 / 房务角色越权)

场景说明 AorderId 不存在 → 581007。

请求

GET /v3/admin/order/999999999/settlement/reports/group
Authorization: Bearer <admin JWT>
(无请求体)

响应

{ "code": 581007, "msg": "订单不存在", "data": null }

场景说明 B:房务管理员角色调用 → 581045两接口同样拦截

请求

GET /v3/admin/order/1956112233445566778/settlement/reports/reimbursement
Authorization: Bearer <房务角色 admin JWT>
(无请求体)

响应

{ "code": 581045, "msg": "房务角色无权查看订单详情,房务仅可配房", "data": null }

9. 业务边界

  • 适用场景:订单存在即可调,未核单(GENERATED)与已核单(CONFIRMED)订单出参结构完全一致,都含 orderHeader
  • 不适用场景房务角色ROOM_MANAGER / HOUSE_KEEPER_LEAD调用 → 581045
  • ⚠️ 特殊边界
    • orderHeader.teamNo:订金未支付的订单没有团号,该 key 不下发,前端渲染团号位置需做空态处理(如显示 -
    • orderHeader.travelerComposition:四档(大 / 儿童 / 幼童 / 婴儿)数量全为 0 时该 key 不下发;不要假设其必存在
    • orderHeader.customerName真名不脱敏(核单财务域需要核对真实客户),前端不要二次脱敏
    • 已核单订单的明细在核单完成时已冻结,实时计算结果与历史快照一致;若核单后通过「反确认」重新打开,报表会随明细变动实时变化(与改前快照行为不同,但反确认本身就意味着数据要重算)

10. 修改前后对比

10.1 字段级对比

字段 改前 改后
data.orderHeader(两接口) 无此字段 新增对象,结构见 §5.3;内部 null 字段不下发
单团核算表其余顶层字段 / incomeLines / costCategories 现状 不变
主报账人报账表 baseInfo / incomeLines / expenseLines / advanceLines 现状 不变

10.2 行为级对比

行为 改前 改后
已核单订单CONFIRMED报表数据源 读 finalize 时落库的终态快照 JSON 与未核单订单一致,统一从 tab 明细实时计算(明细已冻结,结果与快照一致)
两路径出参一致性 快照 / 实时两套组装代码,字段演进可能出现不一致 单一实时组装路径,出参永远含 orderHeader,不再出现「未核单有头 / 已核单无头」分叉
reportStatus 取值 GENERATED / CONFIRMED 不变

11. 影响评估 / 回滚

11.1 影响评估

  • 是否破坏向后兼容:否。出参纯增量,原有字段名 / 类型 / 结构 / 金额口径零变化;老前端不读 orderHeader 不受影响。数据源切换对已核单订单结果无差异(明细已冻结)
  • 前端是否必须同步上线:否。前端按自身排期接入订单头展示即可

11.2 回滚方案

  • 回滚方式revert PR #5854
  • 回滚后清理:无(无 DDL、无缓存、无脏数据;回滚后已核单订单恢复读终态快照

12. 注意事项

  • null 字段整体缺省orderHeader 内部 null 字段teamNo / productTypeName / travelerCompositionkey 不出现在 JSON 中,前端按可选字段处理,不要断言 key 必存在、也不要按 key in obj 之外的 null 值逻辑处理
  • orderHeader 对象本身永远下发,不需要判空整个对象
  • 两个接口的 orderHeader 完全一致,前端可抽公共组件 / 公共 TS 类型复用
  • 中文名渲染productTypeName 后端已回填中文名(字典 product_type),可直接展示;如需动态字典渲染,调 GET /admin/dict/data/product_type
  • 若前端此前为报表弹窗标题栏从订单详情接口另行拼装订单号 / 产品名等信息,接入 orderHeader 后可清理该重复请求(非强制)
  • 无历史 workaround 需要清理(本能力此前不存在)

13. 关联 / 联系人

13.1 链接

13.2 联系人

  • 后端负责人: @yaosutu