适配 4 项:adapter 改读 baseInfo、driver 汇总卡 6 字段改读 baseInfo+转账方向推导、 删 vehicleLines 项、补 date/reimburseAmount 字段翻译;group 分支零改动。 测试:ReportModal.spec 重构+1,returnDetailAdapter.spec +4,settlement 91+4 全绿。
26 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 | 5820 | 主报账人报账表出参重构:顶层三维化 + 支出行统一扁平字段(破坏性变更) | admin | 修改接口 | yaosutu(GIT) | deployed | pending | implemented | mmg | fd070dc2 | 2026-08-11 | 前端已实现(2026-08-11,mmg,fd070dc2),仅 driver/reimbursement 分支,group 单团核算未重构零改动:①adaptSettlementReport 的 reportStatus/confirmedAt 改优先读 baseInfo 回退顶层(不改则 confirmed 恒 false);②driver 汇总卡 6 字段改读 baseInfo,公共预支 publicPrepaidAmount→approvedAdvanceAmount,删 transferDirection 改 reporterNetAmount 正负推导(正=报账人应转回公司/负=公司应补报账人,金额取 transferAmount);③REPORT_DETAIL_CONFIG.driver 删 vehicleLines 项(车辆并入 expenseLines category=VEHICLE 逐天一行);④REPORT_FIELD_LABELS 补 date/reimburseAmount,expenseLines 12 扁平明细列数据驱动自适应。orderHeader 新增 returnDate/travelerCount 未接入(ReportModal 标题栏取 props.order 不读 orderHeader,按本次范围不接)。测试:ReportModal.spec 重构 fixture+1 baseInfo 方向推导用例,returnDetailAdapter.spec +4 baseInfo 兼容用例,settlement 91+4 全绿,checkpoint 精确文件集全过。 | 2026-08-11 | dev-v3 |
【⚠️ 修改接口·管理后台】主报账人报账表出参重构:顶层三维化 + 支出行统一扁平字段(#5820 / #5859)
1. 接口背景
核单结算域的「主报账人报账表」接口,供管理后台在订单核单时查看主报账人(通常是司机)的代收、垫付支出、预支与净额结算情况。
本次重构解决两个历史问题:
- 出参结构混乱(#5820):原出参把 30+ 个汇总字段平铺在顶层,支出行按费用类别拆成 7 族各自一套字段名(住宿用 hotelName/roomTypeName/stayDate、餐食用 mealName/mealTypeName/mealDate、门票用 scenicName/specName/dayDate、车辆用 vehiclePlate/driverName/serviceDate/dailyPrice……),前端需要为每类支出写一套渲染逻辑,且存在 4 对语义重复的镜像字段。
- 订单抬头字段不全 + 方向字段冗余(#5859):baseInfo.transferDirection 与 transferAmount 正负 / reporterNetAmount 表达的信息重复;orderHeader 缺返回日期与出行人总数。
2. 变更清单
| # | 变更 | 类型 |
|---|---|---|
| 1 | 顶层 30+ 平铺汇总字段全部收拢进 baseInfo 子对象 | ⚠️ 破坏性 |
| 2 | expenseLines 支出行由 7 族稀疏字段统一为同一套 12 个扁平字段 | ⚠️ 破坏性 |
| 3 | 顶层 vehicleLines 字段删除(车辆支出行并入 expenseLines,签单/公司直付不再重复列出) | ⚠️ 破坏性 |
| 4 | baseInfo 删除 6 个冗余字段:reconNetAmount / driverCollectedTailAmount / publicPrepaidAmount / advanceOutstandingAmount / transferStatus / transferDirection | ⚠️ 破坏性 |
| 5 | 报账口径明确为口径 A:expenseLines 只含报账人垫付(CASH_PAID)支出 | 🔧 行为变化 |
| 6 | orderHeader 新增 returnDate(返回日期)、travelerCount(出行人总数) | ✨ 新增字段 |
3. 接口详情
| 项 | 值 |
|---|---|
| 方法 + 路径 | GET /v3/admin/order/{orderId}/settlement/reports/reimbursement |
| 接口名 | 查询主报账人报账表 |
| 使用场景 | 管理后台订单核单页,查看主报账人代收/垫付/预支/净额结算报表 |
| 认证 | 管理后台 JWT(/v3/admin/* 走网关鉴权) |
| 角色限制 | 房务角色(HOUSE)不可访问,调了会被拦截 |
| 幂等性 | 只读查询,幂等 |
| 限流 | 走网关默认限流,无接口级特殊限流 |
4. 接口入参
4.1 路径参数
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| orderId | Long | 是 | 订单 ID,必须大于 0 |
4.2 请求体
无请求体,无 Query 参数。
5. 出参字段
统一响应 Result,data 结构如下。
5.1 顶层结构
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| baseInfo | Object | 是 | 基础信息(汇总值 + 报账人 + 审计字段),恒下发对象 |
| orderHeader | Object | 否 | 订单头(订单号/团号/产品/客户/出团日期/定制师/出行人构成) |
| incomeLines | Array | 是 | 收入行(司机代收);无数据固定返回空数组 [] |
| expenseLines | Array | 是 | 支出行(统一扁平字段,仅报账人垫付支出);无数据固定返回空数组 [] |
| advanceLines | Array | 是 | 预支明细行(已审批预支逐条);无数据固定返回空数组 [] |
顶层不再有任何平铺的汇总金额字段,也不再有 vehicleLines。
5.2 baseInfo 字段表
| 字段 | 类型 | 说明 |
|---|---|---|
| id | Long(String) | 核单记录 ID(settlement_recon.recon_id);未生成时为空。Long 序列化为字符串防 JS 精度丢失 |
| orderId | Long(String) | 订单 ID,序列化为字符串 |
| reportStatus | String | 报表状态,枚举:GENERATED(已生成)/ CONFIRMED(已确认) |
| reportVersion | Integer | 报表版本号,固定从 1 开始 |
| primaryReporterId | Long(String) | 主报账人人员安排 ID |
| primaryReporterName | String | 主报账人姓名 |
| primaryReporterRole | String | 主报账人角色(如 DRIVER) |
| primaryReporterCollectedAmount | BigDecimal | 主报账人代收金额(收入行合计),保留两位小数 |
| approvedAdvanceAmount | BigDecimal | 已审批预支金额(预支行合计),保留两位小数 |
| reportablePaidCostAmount | BigDecimal | 可报账已付成本(支出行合计),保留两位小数 |
| reporterNetAmount | BigDecimal | 报账人净额(代收 + 预支 - 支出);正 = 报账人应转回公司,负 = 公司应补报账人 |
| primaryReporterDueAmount | BigDecimal | 主报账人应收尾款(核单口径),保留两位小数 |
| transferAmount | BigDecimal | 转账金额(净额绝对值),保留两位小数 |
| generatedBy | Long(String) | 生成人 ID |
| generatedByName | String | 生成人姓名 |
| generatedAt | String | 生成时间,格式 yyyy-MM-dd HH:mm:ss |
| confirmedBy | Long(String) | 确认人 ID;未确认时为空 |
| confirmedByName | String | 确认人姓名;未确认时为空 |
| confirmedAt | String | 确认时间,格式 yyyy-MM-dd HH:mm:ss;未确认时为空 |
转账方向判定(替代被删的 transferDirection):
- reporterNetAmount > 0 → 报账人应转回公司
- reporterNetAmount < 0 → 公司应补报账人
- transferAmount = |reporterNetAmount|
5.3 orderHeader 字段表
| 字段 | 类型 | 说明 |
|---|---|---|
| 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 | 定制师姓名 |
| travelerCount | Integer | 新增:出行人总数(成人+儿童+幼童+婴儿,空档按 0 计) |
| travelerComposition | String | 出行人构成(数量为 0 的档不显示,全空为 null),如 "2大 1儿童 1幼童" |
5.4 expenseLines 支出行字段表(统一扁平 12 字段)
所有分类共用同一套字段,前端单 table 渲染即可,不用再按类别分叉。
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| category | String | 是 | 费用类别 code: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 数组;无凭证时不输出该键 |
itemName 折叠规则(按 category):
| category | itemName 格式 | 示例 |
|---|---|---|
| HOTEL | 酒店名-房型 | 呼伦贝尔香格里拉大酒店-大床房 |
| TICKET | 景区名-规格 | 套娃景区-成人票 |
| MEAL | 餐食名(餐类型) | 手把肉套餐(午餐) |
| VEHICLE | 车牌 车型/司机 | 蒙A-E2E01 丰田普拉多/巴雅尔 |
| GUIDE / PHOTOGRAPHER | 人员姓名 | 巴特尔 |
| OTHER_EXPENSE / INSURANCE | 项目名 | 旅游意外险 |
5.5 incomeLines 收入行字段表(本次未动)
| 字段 | 类型 | 说明 |
|---|---|---|
| type | String | 行类型 code |
| typeName | String | 行类型中文名(字典 settlement_report_line_type) |
| receiptId | Long(String) | 线下收款记录 ID |
| amount | BigDecimal | 收款金额,保留两位小数 |
| channel | String | 收款渠道 code |
| channelName | String | 收款渠道中文名(PaymentChannelEnum 枚举 label) |
| payType | String | 收款款项类型 code |
| payTypeName | String | 收款款项类型中文名(PayType 枚举 label) |
| collectorStaffId | Long(String) | 收款人人员安排 ID |
| collectorName | String | 收款人姓名 |
| collectorRole | String | 收款人角色 code |
| collectorRoleName | String | 收款人角色中文名(字典 staff_role) |
| receivedAt | String | 收款时间,格式 yyyy-MM-dd HH:mm:ss |
| remark | String | 备注;无备注时为空 |
5.6 advanceLines 预支明细行字段表(本次未动)
| 字段 | 类型 | 说明 |
|---|---|---|
| type | String | 行类型 code |
| typeName | String | 行类型中文名(字典 settlement_report_line_type),如 "已审批预支" |
| advanceId | Long(String) | 预支单 ID |
| payeeStaffId | Long(String) | 借款对象人员安排 ID |
| payeeName | String | 借款对象姓名 |
| payeeRole | String | 借款对象角色 code |
| payeeRoleName | String | 借款对象角色中文名(字典 staff_role) |
| advanceType | String | 预支类型 code |
| advanceTypeName | String | 预支类型中文名(字典 advance_type),如 "住宿押金" |
| amount | BigDecimal | 预支金额,保留两位小数 |
| purpose | String | 预支用途 |
| voucherUrl | String | 凭证 URL |
| status | String | 预支状态 code |
| statusText | String | 预支状态中文名(AdvanceStatus 枚举 label),如 "已通过" |
| submittedAt | String | 提交时间,格式 yyyy-MM-dd HH:mm:ss |
| approvedAt | String | 审批时间,格式 yyyy-MM-dd HH:mm:ss |
| approvedBy | String | 审批人姓名 |
6. 枚举 / 数据字典
| 字段 | 来源 | 取值 |
|---|---|---|
| baseInfo.reportStatus | 枚举 | GENERATED(已生成)/ CONFIRMED(已确认) |
| expenseLines.category | 字典 settlement_category | HOTEL(住宿)/ TICKET(门票/游玩项目)/ MEAL(餐食)/ VEHICLE(车辆)/ GUIDE(导游)/ PHOTOGRAPHER(摄影师)/ OTHER_EXPENSE(其他费用)/ INSURANCE(保险) |
| expenseLines.paymentMethod | 字典 settlement_payment_method | 报账支出行固定 CASH_PAID(现金已付) |
| incomeLines.channel | PaymentChannelEnum | 收款渠道枚举 |
| incomeLines.payType | PayType 枚举 | 收款款项类型枚举(如尾款) |
| advanceLines.status | AdvanceStatus 枚举 | 预支状态(如已通过) |
| orderHeader.productType | 字典 product_type | 产品类型 |
已删除的枚举字段:transferStatus(#5816 连带下线)、transferDirection(#5859 删除,方向由 transferAmount 正负 / reporterNetAmount 表达)。
7. 错误码
| code | message | 触发场景 |
|---|---|---|
| 581007 | 订单不存在 | orderId 查不到订单 |
| 584088 | 核单凭证数据损坏,请联系管理员处理 | 快照读回时凭证数据异常(脏行),不会抛 500 |
| 400 | 订单 ID 必须大于 0 | orderId 路径参数校验失败 |
8. 示例
8.1 典型成功
请求:
GET /v3/admin/order/2087088947225038849/settlement/reports/reimbursement
响应(已核单 CONFIRMED 订单,含住宿/门票/餐食/车辆支出 + 一条已审批预支):
{
"code": 200,
"message": "成功",
"data": {
"baseInfo": {
"id": "2087089146102255617",
"orderId": "2087088947225038849",
"reportStatus": "CONFIRMED",
"reportVersion": 1,
"primaryReporterId": "7001",
"primaryReporterName": "巴雅尔",
"primaryReporterRole": "DRIVER",
"primaryReporterCollectedAmount": 0,
"approvedAdvanceAmount": 3000.00,
"reportablePaidCostAmount": 1799.00,
"reporterNetAmount": -4799.00,
"primaryReporterDueAmount": 0,
"transferAmount": 4799.00,
"generatedBy": "2037350531801993218",
"generatedByName": "腰苏图",
"generatedAt": "2026-08-11 16:10:03",
"confirmedBy": "2037350531801993218",
"confirmedByName": "腰苏图",
"confirmedAt": "2026-08-11 16:10:03"
},
"orderHeader": {
"orderNo": "HL20260811001",
"teamNo": "T20260811001",
"productName": "呼伦贝尔草原 5 日游",
"productType": "CORE",
"productTypeName": "核心产品",
"customerName": "张三",
"departDate": "2026-08-29",
"returnDate": "2026-09-02",
"consultantName": "李四",
"travelerCount": 5,
"travelerComposition": "2大 1儿童 1幼童"
},
"incomeLines": [],
"expenseLines": [
{
"category": "HOTEL",
"categoryName": "住宿",
"itemName": "呼伦贝尔香格里拉大酒店-大床房",
"unitPrice": 320.00,
"quantity": 1,
"amount": 320.00,
"reimburseAmount": 320.00,
"paymentMethod": "CASH_PAID",
"paymentMethodName": "现金已付",
"date": "2026-08-29",
"remark": "含早",
"voucherUrls": ["https://oss.example.com/voucher/hotel-1.jpg"]
},
{
"category": "TICKET",
"categoryName": "门票/游玩项目",
"itemName": "套娃景区-成人票",
"unitPrice": 99.00,
"quantity": 1,
"amount": 99.00,
"reimburseAmount": 99.00,
"paymentMethod": "CASH_PAID",
"paymentMethodName": "现金已付",
"date": "2026-08-30"
},
{
"category": "MEAL",
"categoryName": "餐食",
"itemName": "手把肉套餐(午餐)",
"unitPrice": 68.00,
"quantity": 5,
"amount": 340.00,
"reimburseAmount": 340.00,
"paymentMethod": "CASH_PAID",
"paymentMethodName": "现金已付",
"date": "2026-08-30"
},
{
"category": "VEHICLE",
"categoryName": "车辆",
"itemName": "蒙A-E2E01 丰田普拉多/巴雅尔",
"unitPrice": 1000.00,
"quantity": 1,
"amount": 1000.00,
"reimburseAmount": 1000.00,
"paymentMethod": "CASH_PAID",
"paymentMethodName": "现金已付",
"date": "2026-08-29"
}
],
"advanceLines": [
{
"type": "APPROVED_ADVANCE",
"typeName": "已审批预支",
"advanceId": "5001",
"payeeStaffId": "7001",
"payeeName": "巴雅尔",
"payeeRole": "DRIVER",
"payeeRoleName": "司机",
"advanceType": "ACCOMMODATION_DEPOSIT",
"advanceTypeName": "住宿押金",
"amount": 3000.00,
"purpose": "酒店押金",
"voucherUrl": "https://oss.example.com/advance-v1.jpg",
"status": "APPROVED",
"statusText": "已通过",
"submittedAt": "2026-08-28 10:00:00",
"approvedAt": "2026-08-28 12:00:00",
"approvedBy": "财务丙"
}
]
},
"traceId": null,
"success": true
}
8.2 边界情况
边界 1:零收入零预支零支出(刚生成报表、尚未录任何行)——三个数组固定返回空数组,不是 null:
{
"code": 200,
"data": {
"baseInfo": {
"id": "2087089146102255617",
"orderId": "2087088947225038849",
"reportStatus": "GENERATED",
"reportVersion": 1,
"primaryReporterId": "7001",
"primaryReporterName": "巴雅尔",
"primaryReporterRole": "DRIVER",
"primaryReporterCollectedAmount": 0,
"approvedAdvanceAmount": 0,
"reportablePaidCostAmount": 0,
"reporterNetAmount": 0,
"primaryReporterDueAmount": 0,
"transferAmount": 0,
"generatedBy": "2037350531801993218",
"generatedByName": "腰苏图",
"generatedAt": "2026-08-11 16:10:03",
"confirmedBy": null,
"confirmedByName": null,
"confirmedAt": null
},
"orderHeader": {
"orderNo": "HL20260811001",
"teamNo": null,
"productName": "呼伦贝尔草原 5 日游",
"productType": "CORE",
"productTypeName": "核心产品",
"customerName": "张三",
"departDate": "2026-08-29",
"returnDate": "2026-09-02",
"consultantName": "李四",
"travelerCount": 0,
"travelerComposition": null
},
"incomeLines": [],
"expenseLines": [],
"advanceLines": []
},
"success": true
}
边界 2:人员行(GUIDE)+ 无凭证无备注 + 报销金额与金额不同——unitPrice/quantity/remark/voucherUrls 键不输出:
{
"category": "GUIDE",
"categoryName": "导游",
"itemName": "巴特尔",
"amount": 500.00,
"reimburseAmount": 450.00,
"paymentMethod": "CASH_PAID",
"paymentMethodName": "现金已付",
"date": "2026-08-31"
}
边界 3:门票单价为 0(免费票)——unitPrice: 0.0 正常输出,金额 0:
{
"category": "TICKET",
"categoryName": "门票/游玩项目",
"itemName": "蓝房子(乌苏浪子湖)-成人票",
"unitPrice": 0.0,
"quantity": 1,
"amount": 0.0,
"reimburseAmount": 0.0,
"paymentMethod": "CASH_PAID",
"paymentMethodName": "现金已付",
"date": "2026-08-31"
}
8.3 业务失败
订单不存在:
GET /v3/admin/order/999999999/settlement/reports/reimbursement
{
"code": 581007,
"message": "订单不存在",
"success": false
}
参数校验失败(orderId = 0):
GET /v3/admin/order/0/settlement/reports/reimbursement
{
"code": 400,
"message": "订单 ID 必须大于 0",
"success": false
}
房务角色访问被拦(House 角色 JWT 调用):
{
"code": 403,
"message": "无权限访问",
"success": false
}
9. 业务边界
适用:
- 订单已进入核单流程(已生成核单记录),查看主报账人维度的结算报表
- 已核单(CONFIRMED)与核单中(GENERATED)订单均可调,报表实时计算
不适用:
- 未发起核单的订单:baseInfo.id 等审计字段为空,汇总金额为 0
- 需要看整团(含非报账人支付)口径时用单团核算表接口 GET /v3/admin/order/{orderId}/settlement/reports/group,本接口是报账人视角
特殊边界:
- 报账口径 A:expenseLines 只含报账人垫付(CASH_PAID)的支出;签单 / 公司直接付的车务费不进 expenseLines,前端不要期待在支出行里看到所有成本
- incomeLines 只含报账人代收,线上支付(微信等)不在此列
- 旧快照兼容:历史已确认报表的旧快照数据读回时,旧扁平键静默忽略、落默认空 baseInfo,不会抛 500
10. 修改前后对比
10.1 顶层结构对比
| 维度 | 修改前 | 修改后 |
|---|---|---|
| 汇总字段 | 30+ 个平铺在顶层(id/orderId/reportStatus/primaryReporterCollectedAmount/...) | 全部收拢进 baseInfo 子对象 |
| 车辆支出 | 顶层独立 vehicleLines 数组(车辆专属字段) | 删除;车辆支出行并入 expenseLines(category=VEHICLE) |
| 订单头 | orderHeader(9 字段) | orderHeader(11 字段,新增 returnDate/travelerCount) |
10.2 baseInfo 字段级对比(删除清单)
| 删除字段 | 原位置 | 替代取值 |
|---|---|---|
| reconNetAmount | 顶层平铺 | baseInfo.reporterNetAmount(同值镜像) |
| driverCollectedTailAmount | 顶层平铺 | baseInfo.primaryReporterCollectedAmount(同值镜像) |
| publicPrepaidAmount | 顶层平铺 | baseInfo.approvedAdvanceAmount(同口径) |
| advanceOutstandingAmount | 顶层平铺 | 无替代(该口径废弃,预支看 approvedAdvanceAmount + advanceLines) |
| transferStatus | baseInfo(#5816 连带下线) | 无替代(转账状态跟踪能力下线) |
| transferDirection | baseInfo | 由 reporterNetAmount 正负判定:正=报账人应转回公司,负=公司应补报账人;金额取 transferAmount |
其余原顶层平铺字段(id/orderId/reportStatus/reportVersion/primaryReporter*/各金额/generated*/confirmed*)字段名与语义不变,仅位置从顶层移入 baseInfo。
10.3 expenseLines 字段级对比
| 修改前(按类别稀疏字段) | 修改后(统一扁平字段) |
|---|---|
| HOTEL: hotelName / roomTypeName / stayDate / roomCount / unitPrice | itemName=「酒店-房型」/ date / quantity / unitPrice |
| TICKET: scenicName / specName / dayDate / ticketCount / ticketUnitPrice | itemName=「景区-规格」/ date / quantity / unitPrice |
| MEAL: mealName / mealTypeName / mealDate / quantity / unitPrice | itemName=「餐食名(餐类型)」/ date / quantity / unitPrice |
| VEHICLE: vehiclePlate / driverName / serviceDate / dailyPrice | itemName=「车牌 车型/司机」/ date / unitPrice(quantity 恒 1) |
| GUIDE/PHOTOGRAPHER: staffName / settledDate / amount / reimburseAmount | itemName=姓名 / date / amount / reimburseAmount |
| 各类各自的付款方式/备注/凭证字段名 | 统一 paymentMethod/paymentMethodName/remark/voucherUrls |
10.4 行为级对比
| 场景 | 修改前 | 修改后 |
|---|---|---|
| 签单/公司直付车务费 | 出现在 vehicleLines | 不出现在本接口任何行(口径 A:只列报账人垫付) |
| 支出行渲染 | 前端按 7 族类别各写一套列 | 单 table 统一 12 字段渲染 |
| 调旧字段(如 data.reportStatus、data.vehicleLines、data.baseInfo.transferDirection) | 有值 | undefined(字段不存在,不报错静默丢失) |
11. 影响评估 / 回滚
11.1 影响评估
- 破坏兼容性:⚠️ 是。所有读顶层平铺汇总字段、读 vehicleLines、按类别分叉渲染支出行、读 transferStatus/transferDirection 的前端代码全部失效(取到 undefined)。
- 前端必须同步上线:是。前端需要:
- 汇总字段读取路径从 data.xxx 改为 data.baseInfo.xxx
- 支出行渲染改为单 table 统一字段(category/categoryName/itemName/unitPrice/quantity/amount/reimburseAmount/paymentMethod/paymentMethodName/date/remark/voucherUrls)
- 删除 vehicleLines 相关渲染
- 转账方向展示改由 reporterNetAmount 正负 + transferAmount 推导
- 4 个镜像字段改读保留字段(见 §10.2 替代表)
- 后端兼容:旧快照数据读回不报错(静默忽略旧键),无需数据迁移。
11.2 回滚方案
- 后端回滚 = revert PR #5839 + PR #5865 两个 merge commit,重启 hl-order-service-v3。出参即恢复旧结构。
- 前端回滚 = 切回旧版前端包。前后端必须同版本(新后端 + 旧前端 = 页面全空)。
- 无 DDL,无数据迁移,回滚无残留风险。
12. 注意事项
- 所有 Long ID 字段(id/orderId/primaryReporterId/receiptId/advanceId/各种 staffId/generatedBy/confirmedBy)序列化为 JSON 字符串,前端按 string 处理,不要 Number() 转换(防 JS 精度丢失)。
- expenseLines 中 unitPrice / quantity / remark / voucherUrls 是条件输出键:无值时整个键不出现(NON_NULL),前端取值前判空。
- reimburseAmount 大多数分类与 amount 相同;只有人员行(GUIDE/PHOTOGRAPHER)应报销与金额可能不同,展示报账口径时以 reimburseAmount 为准。
- 转账方向不要再找 transferDirection 字段:用 reporterNetAmount > 0 判「报账人转回公司」、< 0 判「公司补报账人」,transferAmount 恒为绝对值。
- orderHeader.travelerComposition 全空档时为 null,teamNo 订金未支付时为 null,展示需兜底。
- 本接口与单团核算表 reports/group 是两个口径:本接口 = 主报账人视角(只含报账人垫付),group = 整团视角(含全部成本)。前端不要把两个接口的行混在一起渲染。
- 车辆支出行在 expenseLines 里按服务日逐天一行(quantity 恒 1),不是一车一行。
13. 关联 / 联系人
- Issue:
- wx/HL#5820 (主报账人报账表 RespVO 重构)
- wx/HL#5859 (删 transferDirection + orderHeader 补 returnDate/travelerCount)
- PR:
- wx/HL#5839 (#5820 出参重构)
- wx/HL#5865 (#5859 orderHeader 补字段 + 删 transferDirection)
- 服务:hl-order-service-v3(端口 8086)
- 后端负责人:腰苏图