diff --git a/changelogs-v2/2026-08/13_5953_主报账表展示其他收支明细-修改接口-管理后台.md b/changelogs-v2/2026-08/13_5953_主报账表展示其他收支明细-修改接口-管理后台.md new file mode 100644 index 0000000..3d630ef --- /dev/null +++ b/changelogs-v2/2026-08/13_5953_主报账表展示其他收支明细-修改接口-管理后台.md @@ -0,0 +1,408 @@ +--- +schema: "hl-changelog/v2" +ticket: "5953" +title: "主报账表展示其他收入/其他支出明细(不影响报账人净额)" +consumer: "admin" +change_type: "修改接口" +author: "yst" +backend_status: "deployed" +gateway_status: "verified" +frontend_status: "pending" +frontend_owner: "" +frontend_ref: "" +target_release: "" +verified_at: "" +status_note: "PR #5954 已合并 dev-v3(merge commit d945ffc3d6)。纯追加式变更:incomeLines 追加 OTHER_INCOME 其他收入展示行、expenseLines 追加 OTHER_EXPENSE 其他支出展示行并扩充行字段,baseInfo 汇总口径零漂移;字典 settlement_report_line_type 补 OTHER_EXPENSE(user-service Flyway V20260813_001,随 hl-user-service 部署生效)。" +updated_at: "2026-08-13" +base: "dev-v3" +--- + +# 【修改接口·管理后台】主报账表展示其他收入/其他支出明细(不影响报账人净额)(#5953) + +## 1. 接口背景 + +主报账人报账表(reimbursement)是管理后台订单核单页查看主报账人(通常是司机)代收、垫付支出、预支与净额结算情况的只读报表。 + +此前报表有两个信息缺口: + +- **收入侧**:只有 DRIVER_CASH_RECEIPT(报账人现金收款)一类行,订单的「其他收入」(升级房型、下马酒等逐项增收)完全看不到,财务对账时要跳到别的页面查。 +- **支出侧**:只含报账人现金垫付(CASH_PAID)的支出,「其他支出」分类(其他费用/补贴)整类不透出,签单/公司直付的其他支出也无从展示。 + +本次变更把 **OTHER_INCOME 其他收入** 和 **OTHER_EXPENSE 其他支出** 两类的逐项明细以**展示行**形式追加进报账表:**纯展示、不并入 baseInfo 任何汇总值**,报账人净额口径与之前完全一致(零漂移)。 + +## 2. 变更清单 + +| # | 位置 | 变更 | 类型 | +|---|------|------|------| +| 1 | incomeLines | 追加 OTHER_INCOME 其他收入展示行(type=OTHER_INCOME),逐行透出 itemName/content/unitPrice/quantity/amount/paymentMethod(+Name)/sourceType(+Name)/remark/voucherUrls | ✨ 新增展示行 | +| 2 | incomeLines 元素 | 新增 9 个字段:itemName / content / unitPrice / quantity / paymentMethod / paymentMethodName / sourceType / sourceTypeName / voucherUrls(仅 OTHER_INCOME 行输出) | ✨ 新增字段 | +| 3 | expenseLines | 追加 OTHER_EXPENSE 其他支出展示行(type=OTHER_EXPENSE,整类追加在支出行数组尾部),透出实际付款方式(不再固定 CASH_PAID) | ✨ 新增展示行 | +| 4 | expenseLines 元素 | 新增 8 个字段:type / typeName / expenseType / expenseTypeName / subsidyType / subsidyTypeName / sourceType / sourceTypeName(仅 OTHER_EXPENSE 展示行输出 type 系字段) | ✨ 新增字段 | +| 5 | baseInfo 汇总口径 | **不变但更明确**:primaryReporterCollectedAmount 仍只算 DRIVER_CASH_RECEIPT 行;reportablePaidCostAmount 仍只算 CASH_PAID 垫付行(含 OTHER_EXPENSE 分类中的 CASH_PAID 行);reporterNetAmount 公式不变 | 🔧 口径声明(数值零漂移) | +| 6 | expenseLines 排序 | OTHER_EXPENSE 分类的 CASH_PAID 行**从原主排序块移到支出行数组尾部**(整类统一走尾部展示块) | 🔧 行为变化 | +| 7 | 字典 | settlement_report_line_type 新增 OTHER_EXPENSE=其他支出(dict_data_id=101457) | ✨ 新增字典值 | + +无入参变化、无新接口、无删除字段,属**非破坏性追加**。 + +## 3. 接口详情 + +| 项 | 值 | +|---|---| +| 方法 + 路径 | GET /v3/admin/order/{orderId}/settlement/reports/reimbursement | +| 接口名 | 查询主报账人报账表 | +| 使用场景 | 管理后台订单核单页,查看主报账人代收/垫付/预支/净额结算报表(含其他收入/其他支出逐项展示) | +| 认证 | 管理后台 JWT(/v3/admin/* 走网关鉴权) | +| 角色限制 | 房务角色(HOUSE)不可访问,调了会被 403 拦截 | +| 幂等性 | 只读查询,幂等 | +| 限流 | 走网关默认限流,无接口级特殊限流 | + +## 4. 接口入参 + +### 4.1 路径参数 + +| 参数 | 类型 | 必填 | 说明 | +|------|------|------|------| +| orderId | Long | 是 | 订单 ID,必须大于 0(否则 400「订单 ID 必须大于 0」) | + +### 4.2 请求体 / Query + +无请求体、无 Query 参数。 + +## 5. 出参字段 + +返回 `Result`。顶层 4 个字段不变:baseInfo / incomeLines / expenseLines / advanceLines。baseInfo 与 advanceLines 字段结构**本次零变化**(baseInfo 汇总口径见 §9),下面只列**有变化的 incomeLines / expenseLines**。 + +### 5.1 incomeLines 元素字段表(两类行:DRIVER_CASH_RECEIPT + OTHER_INCOME) + +| 字段 | 类型 | 输出条件 | 说明 | +|------|------|----------|------| +| type | String | 恒输出 | 行类型:DRIVER_CASH_RECEIPT(报账人现金收款)/ **OTHER_INCOME(其他收入展示行,本次新增)** | +| 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 | 无备注不输出 | 备注 | +| **itemName** | String | **仅 OTHER_INCOME 行** | 项目名(与支出行 / 明细 tab 的 itemName 同义对齐,如「升级房型」) | +| **content** | String | 仅 OTHER_INCOME 行,无规格不输出 | 规格/内容(如「豪华蒙古包」) | +| **unitPrice** | BigDecimal | 仅 OTHER_INCOME 行 | 单价,保留两位小数 | +| **quantity** | BigDecimal | 仅 OTHER_INCOME 行 | 数量 | +| **paymentMethod** | String | 仅 OTHER_INCOME 行 | 收付款方式:CASH_PAID / COMPANY_PAID / SIGNED | +| **paymentMethodName** | String | 仅 OTHER_INCOME 行 | 收付款方式中文名(字典 settlement_payment_method,缺值回退硬编码) | +| **sourceType** | String | 仅 OTHER_INCOME 行,无来源不输出 | 明细来源类型(如 ORDER_SURCHARGE,全量取值见 §6) | +| **sourceTypeName** | String | 仅 OTHER_INCOME 行 | 明细来源类型中文名(SettlementDetailSourceType 枚举 label) | +| **voucherUrls** | Array | 仅 OTHER_INCOME 行,无凭证不输出该键 | 凭证 URL 数组 | + +**排序**:收款行(DRIVER_CASH_RECEIPT)按收款时间升序在前,OTHER_INCOME 展示行按分类快照稳定序**追加在后**。 + +### 5.2 expenseLines 元素字段表(CASH_PAID 垫付行 + OTHER_EXPENSE 展示行) + +| 字段 | 类型 | 输出条件 | 说明 | +|------|------|----------|------| +| **type** | String | **仅 OTHER_EXPENSE 展示行输出,其余支出行不输出该键** | 行类型,固定 OTHER_EXPENSE | +| **typeName** | String | 仅 OTHER_EXPENSE 展示行 | 行类型中文名(字典 settlement_report_line_type,值「其他支出」) | +| category | String | 恒输出 | 费用类别:HOTEL / TICKET / MEAL / VEHICLE / GUIDE / PHOTOGRAPHER / OTHER_EXPENSE / INSURANCE | +| categoryName | String | 恒输出 | 费用类别中文名(字典 settlement_category) | +| itemName | String | 恒输出 | 项目名(分类特有信息折叠:住宿=酒店-房型,餐食=餐食名(餐类型),其他支出=项目名 等) | +| unitPrice | BigDecimal | 无单价概念不输出 | 单价,保留两位小数 | +| quantity | BigDecimal | 无数量概念不输出 | 数量 | +| amount | BigDecimal | 恒输出 | 实际金额,保留两位小数 | +| reimburseAmount | BigDecimal | 恒输出 | 报账金额;注意 **OTHER_EXPENSE 非 CASH_PAID 展示行的 reimburseAmount 也是全额**(仅展示口径,不代表可报账),前端自算合计见 §9 警示 | +| paymentMethod | String | 恒输出 | 付款方式:CASH_PAID / COMPANY_PAID / SIGNED;非 OTHER_EXPENSE 支出行固定 CASH_PAID,**OTHER_EXPENSE 展示行透出实际付款方式** | +| paymentMethodName | String | 恒输出 | 付款方式中文名(字典 settlement_payment_method,缺值回退硬编码) | +| date | String | 无日期概念不输出 | 业务日期,格式 yyyy-MM-dd | +| remark | String | 无备注不输出 | 备注 | +| voucherUrls | Array | 无凭证不输出该键 | 凭证 URL 数组 | +| **expenseType** | String | 仅 OTHER_EXPENSE 展示行(EXPENSE 族) | 其他费用类型(如 OTHER) | +| **expenseTypeName** | String | 仅 OTHER_EXPENSE 展示行(EXPENSE 族) | 其他费用类型中文名(字典 expense_type) | +| **subsidyType** | String | 仅 OTHER_EXPENSE 展示行(SUBSIDY 族) | 补贴类型:PHONE / OVERTIME | +| **subsidyTypeName** | String | 仅 OTHER_EXPENSE 展示行(SUBSIDY 族) | 补贴类型中文名(字典 subsidy_type,如「话补」) | +| **sourceType** | String | 仅 OTHER_EXPENSE 展示行,无来源不输出 | 明细来源类型(全量取值见 §6) | +| **sourceTypeName** | String | 仅 OTHER_EXPENSE 展示行 | 明细来源类型中文名(SettlementDetailSourceType 枚举 label) | + +**排序**:原主排序块(全分类 CASH_PAID 垫付行,不含 OTHER_EXPENSE)在前;OTHER_EXPENSE 分类**整类追加在支出行数组尾部**(含其中的 CASH_PAID 行——这类行原本混在主排序块里,本次统一移到尾部展示块)。 + +### 5.3 baseInfo / advanceLines + +结构零变化。baseInfo 汇总口径(数值与修改前完全一致): + +- primaryReporterCollectedAmount = 仅 DRIVER_CASH_RECEIPT 收款行合计(**不含** OTHER_INCOME 展示行) +- reportablePaidCostAmount = 仅 CASH_PAID 现金垫付支出行合计(**含** OTHER_EXPENSE 分类中的 CASH_PAID 行,不含其它付款方式展示行) +- reporterNetAmount = 代收 + 预支 - 支出,公式不变 + +## 6. 枚举 / 数据字典 + +| 字段 | 来源 | 取值 | +|------|------|------| +| incomeLines[].type | settlement_report_line_type 字典 | DRIVER_CASH_RECEIPT(司机现金收款)/ **OTHER_INCOME(其他收入)** | +| expenseLines[].type | settlement_report_line_type 字典 | **OTHER_EXPENSE(其他支出)—— 本次新增字典值**(dict_data_id=101457,sort=70);其余支出行不输出 type 键 | +| incomeLines[].channel | PaymentChannelEnum 枚举 | DRIVER_CASH / BANK_TRANSFER / CONSULTANT_COLLECTION | +| incomeLines[].payType | PayType 枚举 | DEPOSIT(订金)/ FULL(全款)/ BALANCE(尾款) | +| *.paymentMethod | settlement_payment_method 字典 | CASH_PAID(现金已付)/ COMPANY_PAID(公司直付)/ SIGNED(签单) | +| expenseLines[].expenseType | expense_type 字典 | 如 OTHER(其他)等 | +| expenseLines[].subsidyType | subsidy_type 字典 | PHONE(话补)/ OVERTIME(加班补)等 | +| *.sourceType | SettlementDetailSourceType 枚举 | MANUAL(手工)/ HOUSE_ASSIGNMENT / SCENIC_ASSIGNMENT / ACTIVITY_ASSIGNMENT / MEAL_ASSIGNMENT / FLEET / STAFF_ASSIGNMENT / ORDER_SURCHARGE(订单增费)/ SYSTEM | +| collectorRole 等角色字段 | staff_role 字典 | DRIVER(司机)/ GUIDE(导游)等 | + +## 7. 错误码 + +本次无新增错误码,沿用既有: + +| code | message | 触发场景 | +|------|---------|----------| +| 400 | 订单 ID 必须大于 0 | orderId 路径参数校验失败 | +| 403 | 无权限访问 | 房务角色(HOUSE)JWT 调用 | +| 581007 | 订单不存在 | orderId 查不到订单 | + +## 8. 示例 + +### 8.1 典型成功(含 OTHER_INCOME / OTHER_EXPENSE 展示行) + + GET /v3/admin/order/2087088947225038849/settlement/reports/reimbursement + +```json +{ + "code": 200, + "message": "成功", + "data": { + "baseInfo": { + "orderId": "2087088947225038849", + "orderNo": "HL20260730001", + "primaryReporterName": "司机甲", + "primaryReporterRole": "DRIVER", + "primaryReporterCollectedAmount": 2000.00, + "approvedAdvanceAmount": 500.00, + "reportablePaidCostAmount": 1500.00, + "reporterNetAmount": 1000.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": "尾款现金" + }, + { + "type": "OTHER_INCOME", + "typeName": "其他收入", + "itemName": "升级房型", + "content": "豪华蒙古包", + "unitPrice": 50.00, + "quantity": 2, + "amount": 100.00, + "paymentMethod": "CASH_PAID", + "paymentMethodName": "现金已付", + "sourceType": "ORDER_SURCHARGE", + "sourceTypeName": "订单增费", + "remark": "客人现场升级", + "voucherUrls": ["https://oss/i1.jpg"] + }, + { + "type": "OTHER_INCOME", + "typeName": "其他收入", + "itemName": "下马酒", + "unitPrice": 50.00, + "quantity": 1, + "amount": 50.00, + "paymentMethod": "SIGNED", + "paymentMethodName": "签单", + "sourceType": "MANUAL", + "sourceTypeName": "手工" + } + ], + "expenseLines": [ + { + "category": "HOTEL", + "categoryName": "住宿", + "itemName": "草原明珠大酒店-标间", + "unitPrice": 400.00, + "quantity": 3, + "amount": 1200.00, + "reimburseAmount": 1200.00, + "paymentMethod": "CASH_PAID", + "paymentMethodName": "现金已付", + "date": "2026-07-30", + "voucherUrls": ["https://oss/v1.jpg"] + }, + { + "type": "OTHER_EXPENSE", + "typeName": "其他支出", + "category": "OTHER_EXPENSE", + "categoryName": "其他支出", + "itemName": "景区停车费", + "amount": 300.00, + "reimburseAmount": 300.00, + "paymentMethod": "CASH_PAID", + "paymentMethodName": "现金已付", + "expenseType": "OTHER", + "expenseTypeName": "其他", + "sourceType": "MANUAL", + "sourceTypeName": "手工", + "voucherUrls": ["https://oss/v2.jpg"] + }, + { + "type": "OTHER_EXPENSE", + "typeName": "其他支出", + "category": "OTHER_EXPENSE", + "categoryName": "其他支出", + "itemName": "司机话补", + "amount": 200.00, + "reimburseAmount": 200.00, + "paymentMethod": "SIGNED", + "paymentMethodName": "签单", + "subsidyType": "PHONE", + "subsidyTypeName": "话补", + "sourceType": "STAFF_ASSIGNMENT", + "sourceTypeName": "人员安排" + } + ], + "advanceLines": [] + }, + "success": true +} +``` + +注意上例的**对账关系**(本变更的核心语义): + +- incomeLines 三行 amount 合计 2150.00,但 baseInfo.primaryReporterCollectedAmount = **2000.00**(只算 DRIVER_CASH_RECEIPT 行)—— 数组和 ≠ 汇总值属**预期**。 +- expenseLines 三行 amount 合计 1700.00,但 baseInfo.reportablePaidCostAmount = **1500.00**(只算 CASH_PAID 行:住宿 1200 + 其他支出现金垫付 300;签单的话补 200 仅展示不入账)。 +- reporterNetAmount = 2000 + 500 - 1500 = **1000.00**,与修改前公式完全一致。 + +### 8.2 边界情况 + +**边界 1:无其他收入/其他支出** —— incomeLines / expenseLines 只有原有行(或空数组 []),不出现 OTHER_INCOME / OTHER_EXPENSE 行;新增字段键一律不输出(NON_NULL 省略),与修改前响应完全一致: + +```json +{ "incomeLines": [], "expenseLines": [], "advanceLines": [] } +``` + +**边界 2:OTHER_INCOME 行无规格/无凭证/无来源** —— content / voucherUrls / sourceType 键不输出(不是 null): + +```json +{ + "type": "OTHER_INCOME", + "typeName": "其他收入", + "itemName": "下马酒", + "unitPrice": 50.00, + "quantity": 1, + "amount": 50.00, + "paymentMethod": "COMPANY_PAID", + "paymentMethodName": "公司直付" +} +``` + +### 8.3 业务失败 + +**房务角色(HOUSE)访问被拦**: + +```json +{ "code": 403, "message": "无权限访问", "success": false } +``` + +**订单不存在**: + +```json +{ "code": 581007, "message": "订单不存在", "success": false } +``` + +## 9. 业务边界 + +**适用**: + +- 核单页查看主报账人结算全貌:代收 + 垫付 + 预支 + 净额(baseInfo),以及其他收入 / 其他支出的逐项展示(incomeLines / expenseLines 展示行)。 + +**不适用**: + +- 拿 incomeLines / expenseLines **数组自算合计去对 baseInfo 汇总值** —— 两者口径不同(见下「特殊边界」),对不上属预期。 +- 想看整单收入/成本/毛利 —— 走单团核算表(reports/group),报账表是主报账人维度。 + +**特殊边界(前端必知)**: + +1. **数组和 ≠ 汇总值属预期**:OTHER_INCOME / OTHER_EXPENSE 展示行**纯展示不计入** baseInfo 任何汇总。primaryReporterCollectedAmount 仍只算 DRIVER_CASH_RECEIPT 行;reportablePaidCostAmount 仍只算 CASH_PAID 垫付行;reporterNetAmount 公式不变。**汇总值一律直接读 baseInfo,不要前端自算。** +2. **OTHER_EXPENSE 非 CASH_PAID 展示行的 reimburseAmount 也是全额**(如签单话补 reimburseAmount=200.00),但它不代表可报账。前端若坚持自算 sum(reimburseAmount),必须**按 type=OTHER_EXPENSE 排除展示行**(更精确的做法是按 paymentMethod=CASH_PAID 过滤,与后端口径一致;直接排除全部 OTHER_EXPENSE 行会漏掉其中 CASH_PAID 垫付行,与 reportablePaidCostAmount 差出该行金额)。 +3. **OTHER_EXPENSE 的 CASH_PAID 行位置变了**:这类行原本混在支出行主排序块里,本次整类移到**支出行数组尾部**。若前端曾按「位置/下标」识别这类行,需改为按 type=OTHER_EXPENSE 识别。 +4. **行类型判别方式两侧不对称**: + - 收入行:靠 **type 值**判别(DRIVER_CASH_RECEIPT / OTHER_INCOME,type 恒输出); + - 支出行:靠 **type 键是否出现**判别(仅 OTHER_EXPENSE 展示行输出 type=OTHER_EXPENSE,其余支出行**没有 type 键**——不是 null,是键不存在)。 +5. OTHER_EXPENSE 展示行**全量付款方式**都透出(CASH_PAID / COMPANY_PAID / SIGNED),不再像普通支出行那样固定 CASH_PAID。 + +## 10. 修改前后对比 + +### 10.1 字段级对比 + +| 位置 | 字段 | 修改前 | 修改后 | +|------|------|--------|--------| +| incomeLines | type 取值 | 仅 DRIVER_CASH_RECEIPT | DRIVER_CASH_RECEIPT + **OTHER_INCOME** | +| incomeLines 元素 | itemName / content / unitPrice / quantity / paymentMethod(+Name) / sourceType(+Name) / voucherUrls | 无 | **新增**(仅 OTHER_INCOME 行输出) | +| expenseLines 元素 | type / typeName | 无 | **新增**(仅 OTHER_EXPENSE 展示行输出) | +| expenseLines 元素 | expenseType(+Name) / subsidyType(+Name) / sourceType(+Name) | 无 | **新增**(仅 OTHER_EXPENSE 展示行输出) | +| expenseLines | paymentMethod 取值 | 固定 CASH_PAID | 普通支出行仍固定 CASH_PAID;**OTHER_EXPENSE 展示行透出实际付款方式**(CASH_PAID / COMPANY_PAID / SIGNED) | +| baseInfo | 全部汇总字段 | 原口径 | **零变化**(仅口径描述更明确,数值不变) | +| 字典 | settlement_report_line_type | 6 值(无 OTHER_EXPENSE) | **+OTHER_EXPENSE=其他支出** | + +### 10.2 行为级对比 + +| 场景 | 修改前 | 修改后 | +|------|--------|--------| +| 其他收入逐项 | 报账表完全看不到 | incomeLines 尾部追加 OTHER_INCOME 展示行 | +| 其他支出逐项 | 仅 CASH_PAID 垫付行混在主排序块 | OTHER_EXPENSE 整类(全付款方式)追加在 expenseLines 尾部展示 | +| OTHER_EXPENSE CASH_PAID 行位置 | 支出行主排序块内(按金额等排序) | **移到支出行数组尾部**展示块 | +| baseInfo 汇总值 | 代收=收款行合计,支出=CASH_PAID 合计 | **数值逐分不差**(展示行不入账) | +| 无其他收支的订单 | 原样 | **响应与原样完全一致**(新行不出现、新字段键不输出) | + +## 11. 影响评估 / 回滚 + +### 11.1 影响评估 + +- **破坏兼容性**:非破坏性。纯追加行 + 追加字段,既有字段名/类型/结构零变化;无其他收支的订单响应与修改前逐字节一致。 +- **前端是否必须同步上线**:**不强制**。只读 baseInfo 汇总 + 渲染既有行的前端**零改动可继续用**;但要看到其他收入/其他支出明细,需按 §5 新行结构做渲染,并按 §9 特殊边界处理(汇总读 baseInfo、支出行按 type 键识别 OTHER_EXPENSE)。 +- **前端如有 workaround 需清理**:若前端曾从别的接口拼「其他收入/其他支出」进报账页,可改读本接口展示行;若前端曾自算 sum(incomeLines.amount) 当代收,需改读 baseInfo.primaryReporterCollectedAmount(此前碰巧相等,现在会虚高)。 +- **字典依赖**:expenseLines[].typeName(「其他支出」)依赖 hl-user-service 字典迁移 V20260813_001(sys_dict_data 101457);该迁移未部署前 typeName 会落 null,前端需兜底显示原 code。 + +### 11.2 回滚方案 + +- 后端回滚 = revert PR #5954 的 merge commit(d945ffc3d6),重启 hl-order-service-v3。 +- 字典迁移残留:V20260813_001 新增的 OTHER_EXPENSE 字典值在回滚后无消费方,残留无害,无需回退。 +- 零业务表 DDL,回滚无数据残留风险。 + +## 12. 注意事项 + +1. **汇总只读 baseInfo**:primaryReporterCollectedAmount / reportablePaidCostAmount / reporterNetAmount 是后端算好的权威值,前端不要拿 incomeLines / expenseLines 数组自算(口径不同,必然对不上)。 +2. **NON_NULL 省略**:incomeLines / expenseLines 元素带 @JsonInclude(NON_NULL),**null 字段不下发该 key**(不是下 null);OTHER_INCOME / OTHER_EXPENSE 专属字段在普通行上键不存在,前端读取必须兜底。 +3. **行判别**:收入行看 type 值;支出行看 type 键**是否存在**(JS 可用 'type' in line 判别)。 +4. **Long 主键序列化为字符串**(orderId / receiptId / collectorStaffId 等),前端按 string 处理,不要 Number() 转换。 +5. OTHER_EXPENSE 展示行的 expenseType 与 subsidyType 互斥(EXPENSE 族出费用类型 / SUBSIDY 族出补贴类型),渲染时按存在的键展示即可。 +6. 排序约定:收入行 = 收款行(按收款时间升序)在前 + OTHER_INCOME 展示行在后;支出行 = 主排序块在前 + OTHER_EXPENSE 展示块在尾部。前端按数组顺序渲染即可,不要自行重排。 +7. 本变更只影响 reimbursement 报账表;单团核算表(reports/group)结构本次零变化。 + +## 13. 关联 / 联系人 + +- Issue #5953:https://git.1814.love:8443/wx/HL/issues/5953 +- PR #5954:https://git.1814.love:8443/wx/HL/pulls/5954 +- Commit(merge):https://git.1814.love:8443/wx/HL/commit/d945ffc3d6 +- 服务:hl-order-service-v3(端口 8086);字典迁移在 hl-user-service(V20260813_001,随 user-service 部署生效) +- 后端负责人:yst(腰苏图) + +## 14. 验证证据 + +- 后端单测:SettlementReportFlowServiceTest / SettlementReportLabelEnricherTest / SettlementReportLineConverterTest 覆盖 OTHER_INCOME/OTHER_EXPENSE 展示行组装、汇总口径零漂移(代收仍只算 DRIVER_CASH 行、可报账支出仍只算 CASH_PAID 行)、字典 label 回填,PR #5954 CI 通过。 +- 代码已合并 dev-v3(merge commit d945ffc3d6)并部署测试服;字典迁移 V20260813_001 随 hl-user-service 部署生效(INSERT IGNORE 幂等)。 +- 前端联调验证点:无其他收支订单响应与修改前一致;含其他收支订单按 §8.1 对账关系核对(数组和 ≠ 汇总值属预期)。