--- schema: "hl-changelog/v2" ticket: "5781" title: "单团核算表扩充逐项明细出参(incomeLines.details / costCategories.lines)" consumer: "admin" change_type: "修改接口" author: "yaosutu(GIT)" backend_status: "deployed" gateway_status: "verified" frontend_status: "implemented" frontend_owner: "mmg" frontend_ref: "3993f15a" target_release: "" verified_at: "2026-08-11" status_note: "前端已实现(3993f15a):单团核算表收入行 incomeLines[].details、成本分类 costCategories[].lines 逐项明细行内展开——detailColumns 显式剔除 details/lines/voucherUrls 父列(否则嵌套结构被 detailCellValue JSON.stringify 成多余列,后端已 deployed 老前端会自动渲出),新增 expand 列+renderChildTable 渲染逐项子表(仅当行确有非空逐项才显示展开入口,空数组不渲染);REPORT_FIELD_LABELS 补逐项字段中文表头(stayDate/hotelName/dailyPrice/totalPremium 等),REPORT_CODE_TO_NAME_FIELD 补 source/serviceType/bizType code→Name(后端回填 sourceName/serviceTypeName/bizTypeName,缺名回退 code)。负数金额不取绝对值、*Name 中文名直接展示均遵循 changelog 边界。ReportModal.spec 新增展开用例:父表不渲 JSON 列、展开后显示逐项子表。核单域 spec 全过,checkpoint 全绿。" updated_at: "2026-08-11" base: "dev-v3" --- # 【✨ 修改接口·管理后台】单团核算表扩充逐项明细出参(#5781) > **PR**: #5786 | **服务**: hl-order-service-v3 | **更新时间**: 2026-08-10 ## 1. 接口背景 单团核算表(GET /v3/admin/order/{orderId}/settlement/reports/group)此前只返回「4 行收入合计 + 8 行成本合计」,财务/运营在核对某一行合计时看不到它是由哪些逐项明细加总出来的,只能跳回各分类明细页签逐条对账。 本次变更为**纯出参增量**:在每条收入行下挂 details(收入逐项明细)、在每个成本分类下挂 lines(成本逐项明细,分类专属结构),让前端在核算表内直接展开逐项,无需再跳页签拼装。 **不变的部分**:顶部汇总字段(baseOrderAmount / totalCost / grossProfit 等全部金额字段)、incomeLines 仍固定 4 行、costCategories 仍固定 8 行、各行 amount 合计口径,全部与变更前一致。 ## 2. 变更清单 | # | 接口 | 方法 | 路径 | 变更类型 | 说明 | |---|------|------|------|----------|------| | 1 | 查询单团核算表 | GET | /v3/admin/order/{orderId}/settlement/reports/group | 修改(出参纯增量) | incomeLines[i] 新增 details 数组;costCategories[i] 新增 lines 数组(元素结构随 category 不同而不同,按 category 窄化) | 配套数据字典(前端可调 GET /admin/dict/data/{dictType} 动态渲染中文名): | 字典 type | 用途 | 本次状态 | |-----------|------|----------| | settlement_category | 成本分类中文名 | 已存在(不变) | | settlement_payment_method | 付款方式中文名 | 已存在(不变) | | settlement_report_line_type | 收入行类型中文名 | 已存在(不变) | | insurance_biz_type | 保险业务类型中文名 | **本次新增** | | settlement_refund_source | 人工返还来源中文名 | **本次新增** | ## 3. 接口详情 ### 3.1 查询单团核算表 - **使用场景**:核单工作台「单团核算」页签,财务/运营查看单团收入成本毛利全貌及逐项明细 - **认证**:管理后台 JWT;房务角色(ROOM_MANAGER / HOUSE_KEEPER_LEAD)无权调用(返 581045) - **幂等性**:是(GET 只读) - **限流**:无 ## 4. 接口入参 ### 4.1 路径参数 | 字段 | 类型 | 必填 | 说明 | |------|------|------|------| | `orderId` | Long | ✅ | 订单 ID,必须 > 0,否则返参数校验错误 | ### 4.2 请求体字段 无请求体。 ## 5. 出参(响应) 响应类型:`Result`(`code=200` 表示成功,`data` 为下表结构)。 ### 5.1 顶层字段(SettlementGroupReportRespVO) > ⚠️ 本表全部字段与变更前一致,**本次无增删改**,列出仅为自包含。 | 字段 | 类型 | 说明 | |------|------|------| | `id` | String | 报表 ID(Long 序列化为字符串,无落库记录时可能缺省) | | `orderId` | String | 订单 ID(Long 序列化为字符串) | | `reportStatus` | String | 报表状态:`GENERATED`=已生成(实时组装)/ `CONFIRMED`=已确认(终态快照回放) | | `baseOrderAmount` | Number | 订单应收金额,两位小数 | | `otherIncomeAmount` | Number | 其他收入合计 | | `discountAmount` | Number | 优惠合计(负数) | | `adjustedReceivableAmount` | Number | 调整后应收 | | `paidAmount` | Number | 已收金额 | | `actualRefundedAmount` | Number | 实际退款合计(负数) | | `netRevenueAmount` | Number | 净收入 | | `netReceivedAmount` | Number | 实收净额 | | `outstandingAmount` | Number | 未收尾款 | | `hotelCost` / `ticketCost` / `mealCost` / `vehicleCost` / `guideCost` / `photographerCost` / `otherExpenseCost` / `insurancePremium` | Number | 8 个成本分类合计(住宿/门票/餐食/车辆/导游/摄影/其他支出/保险) | | `totalCost` | Number | 成本总计 | | `paidCost` | Number | 已付成本合计 | | `unpaidCost` | Number | 未付成本合计 | | `grossProfit` | Number | 毛利 | | `grossProfitRate` | Number | 毛利率 | | `travelerCount` | Number | 出行人数 | | `perCapitaRevenue` / `perCapitaCost` / `perCapitaProfit` | Number | 人均收入 / 人均成本 / 人均毛利 | | `incomeLines` | Array | 收入行,**固定 4 行**,结构见 5.2 | | `costCategories` | Array | 成本分类行,**固定 8 行**,结构见 5.3 | | `generatedBy` / `generatedByName` / `generatedAt` | String / String / String | 生成人 ID / 姓名 / 生成时间 | | `confirmedBy` / `confirmedByName` / `confirmedAt` | String / String / String | 确认人 ID / 姓名 / 确认时间(未确认时缺省) | ### 5.2 收入行(SettlementGroupIncomeLineVO) | 字段 | 类型 | 说明 | |------|------|------| | `type` | String | 行类型:`BASE_ORDER`=订单应收 / `OTHER_INCOME`=其他收入 / `DISCOUNT`=优惠 / `ACTUAL_REFUND`=实际退款 | | `typeName` | String | 行类型中文名(字典 `settlement_report_line_type` 回填) | | `amount` | Number | 行合计金额,两位小数;`DISCOUNT` / `ACTUAL_REFUND` 为负数 | | `details` | Array | ✨ **本次新增**:收入逐项明细(`SettlementGroupIncomeDetailVO`),无逐项时为空数组 `[]` | ### 5.3 收入逐项明细(SettlementGroupIncomeDetailVO)✨ 本次新增 | 字段 | 类型 | 说明 | |------|------|------| | `itemName` | String | 项目名(订单应收 / 增费项目名 / 优惠名称 / 退款来源中文名) | | `content` | String | 内容说明(如规格、退款原因) | | `source` | String | 人工返还来源 code,**仅** `ACTUAL_REFUND` 下的人工返还行透出:`DRIVER_ONSITE` / `COMPANY_COMPENSATION` | | `sourceName` | String | 人工返还来源中文名(字典 `settlement_refund_source`) | | `unitPrice` | Number | 单价,两位小数;无单价概念时缺省 | | `headCount` | Number | 人数;无人数概念时缺省 | | `quantity` | Number | 数量;无数量概念时缺省 | | `amount` | Number | 金额,两位小数;`DISCOUNT` / `ACTUAL_REFUND` 明细为负数 | | `paymentMethod` | String | 付款方式:`CASH_PAID` / `COMPANY_PAID` / `SIGNED`;无付款方式时缺省 | | `paymentMethodName` | String | 付款方式中文名(字典 `settlement_payment_method`) | | `sourceType` | String | 来源类型(9 值,见 §6.6);无来源时缺省 | | `sourceTypeName` | String | 来源中文名 | | `remark` | String | 备注 | | `voucherUrls` | Array<String> | 凭证 URL 数组 | **各 type 下 details 的内容口径**: | type | details 内容 | |------|-------------| | `BASE_ORDER` | 订单应收一行汇总(订单侧无逐项时单行) | | `OTHER_INCOME` | 其他收入逐项 | | `DISCOUNT` | 优惠逐项(金额取负) | | `ACTUAL_REFUND` | 实际退款逐项:线上退款 + 人工返还(`source` = DRIVER_ONSITE / COMPANY_COMPENSATION),金额取负 | ### 5.4 成本分类行(SettlementGroupCostCategoryVO) | 字段 | 类型 | 说明 | |------|------|------| | `category` | String | 费用类别:`HOTEL` / `TICKET` / `MEAL` / `VEHICLE` / `GUIDE` / `PHOTOGRAPHER` / `OTHER_EXPENSE` / `INSURANCE` | | `categoryName` | String | 费用类别中文名(字典 `settlement_category`) | | `amount` | Number | 分类合计金额,两位小数 | | `lines` | Array | ✨ **本次新增**:成本逐项明细,**元素结构随 `category` 不同而不同**(分类专属行 VO),无逐项时为空数组 `[]`;前端按 `category` 判别窄化到 5.5~5.11 的对应结构 | ### 5.5 成本逐项行 · HOTEL 住宿(SettlementGroupHotelLineVO)✨ | 字段 | 类型 | 说明 | |------|------|------| | `stayDate` | String | 入住日期,`yyyy-MM-dd` | | `hotelName` | String | 酒店名称 | | `roomTypeName` | String | 房型名称 | | `roomCount` | Number | 房间数 | | `unitPrice` | Number | 单价,两位小数 | | `plannedCost` | Number | 计划成本,两位小数 | | `amount` | Number | 核算金额(实际成本),两位小数 | | `paymentMethod` / `paymentMethodName` | String | 付款方式 code / 中文名 | | `sourceType` / `sourceTypeName` | String | 来源类型 code / 中文名 | | `remark` | String | 备注 | | `voucherUrls` | Array<String> | 凭证 URL 数组 | ### 5.6 成本逐项行 · TICKET 门票(SettlementGroupTicketLineVO)✨ | 字段 | 类型 | 说明 | |------|------|------| | `dayDate` | String | 游玩日期,`yyyy-MM-dd` | | `scenicName` | String | 景区/项目名称 | | `specName` | String | 规格名称(如 成人票) | | `ticketCount` | Number | 票数 | | `ticketUnitPrice` | Number | 门票单价,两位小数 | | `sellPrice` | Number | 销售价,两位小数 | | `plannedCost` | Number | 计划成本,两位小数 | | `amount` | Number | 核算金额(实际成本),两位小数 | | `paymentMethod` / `paymentMethodName` | String | 付款方式 code / 中文名 | | `sourceType` / `sourceTypeName` | String | 来源类型 code / 中文名 | | `remark` | String | 备注 | | `voucherUrls` | Array<String> | 凭证 URL 数组 | ### 5.7 成本逐项行 · MEAL 餐食(SettlementGroupMealLineVO)✨ | 字段 | 类型 | 说明 | |------|------|------| | `mealDate` | String | 用餐日期,`yyyy-MM-dd` | | `mealName` | String | 餐食名称 | | `mealType` | String | 餐食类型:`BREAKFAST` / `LUNCH` / `DINNER` / `SELF` | | `mealTypeName` | String | 餐食类型中文名(字典 `meal_type`) | | `quantity` | Number | 份数 | | `unitPrice` | Number | 单价,两位小数 | | `amount` | Number | 核算金额(实际金额),两位小数 | | `paymentMethod` / `paymentMethodName` | String | 付款方式 code / 中文名 | | `sourceType` / `sourceTypeName` | String | 来源类型 code / 中文名;餐食明细无来源时缺省 | | `remark` | String | 备注 | | `voucherUrls` | Array<String> | 凭证 URL 数组 | ### 5.8 成本逐项行 · VEHICLE 用车(SettlementGroupVehicleLineVO)✨ > 车务逐日行(VEHICLE_FEE 族)与车辆费用行(EXPENSE 族:油费/过路费等)的**稀疏并集**:每行仅本族字段非空,另一族字段整体缺省。无单价/数量概念,`dailyPrice` 即核算单价列。 | 字段 | 类型 | 说明 | |------|------|------| | `serviceDate` | String | [VEHICLE_FEE] 服务日期,`yyyy-MM-dd` | | `startDate` | String | [VEHICLE_FEE] 服务开始日期,`yyyy-MM-dd` | | `endDate` | String | [VEHICLE_FEE] 服务结束日期,`yyyy-MM-dd` | | `vehiclePlate` | String | [VEHICLE_FEE] 车牌号 | | `vehicleModelName` | String | [VEHICLE_FEE] 车型名称 | | `driverName` | String | [VEHICLE_FEE] 司机姓名 | | `dailyPrice` | Number | [VEHICLE_FEE] 日单价(即核算单价列),两位小数 | | `paymentTypeName` | String | [VEHICLE_FEE] 车务付款类型名称 | | `amount` | Number | 核算金额,两位小数 | | `paymentMethod` / `paymentMethodName` | String | 付款方式 code / 中文名 | | `sourceType` / `sourceTypeName` | String | 来源类型 code / 中文名 | | `expenseType` | String | [EXPENSE] 车辆费用类型:`FUEL` / `TOLL` / `PARKING` / `RENTAL` / `MAINTENANCE` | | `expenseTypeName` | String | [EXPENSE] 车辆费用类型中文名(字典 `expense_type`) | | `projectName` | String | [EXPENSE] 项目名称 | | `expenseDate` | String | [EXPENSE] 费用发生日期,`yyyy-MM-dd` | | `remark` | String | 备注 | | `voucherUrls` | Array<String> | 凭证 URL 数组 | ### 5.9 成本逐项行 · GUIDE 导游 / PHOTOGRAPHER 摄影(SettlementGroupStaffLineVO,两分类共用)✨ | 字段 | 类型 | 说明 | |------|------|------| | `serviceDate` | String | 服务日期,`yyyy-MM-dd`;无服务日时缺省 | | `name` | String | 人员姓名 | | `serviceType` | String | 服务类型:`GUIDE` / `PHOTOGRAPHER` | | `serviceTypeName` | String | 服务类型中文名(字典 `staff_role`) | | `amount` | Number | 核算金额,两位小数 | | `paymentMethod` / `paymentMethodName` | String | 付款方式 code / 中文名 | | `sourceType` / `sourceTypeName` | String | 来源类型 code / 中文名;无来源时缺省 | | `remark` | String | 备注 | | `voucherUrls` | Array<String> | 凭证 URL 数组 | ### 5.10 成本逐项行 · OTHER_EXPENSE 其他支出(SettlementGroupOtherExpenseLineVO)✨ > 其他费用行(EXPENSE 族)与补贴行(SUBSIDY 族)的**稀疏并集**:每行仅本族字段非空。无单价/数量概念。 | 字段 | 类型 | 说明 | |------|------|------| | `expenseDate` | String | 费用发生日期,`yyyy-MM-dd` | | `projectName` | String | 项目名称 | | `expenseType` | String | [EXPENSE] 其他费用类型:固定 `OTHER` | | `expenseTypeName` | String | [EXPENSE] 其他费用类型中文名(字典 `expense_type`) | | `subsidyType` | String | [SUBSIDY] 补贴类型:`PHONE` / `OVERTIME` | | `subsidyTypeName` | String | [SUBSIDY] 补贴类型中文名(字典 `subsidy_type`) | | `amount` | Number | 核算金额(实际金额),两位小数 | | `paymentMethod` / `paymentMethodName` | String | 付款方式 code / 中文名 | | `sourceType` / `sourceTypeName` | String | 来源类型 code / 中文名;无来源时缺省 | | `remark` | String | 备注 | | `voucherUrls` | Array<String> | 凭证 URL 数组 | ### 5.11 成本逐项行 · INSURANCE 保险(SettlementGroupInsuranceLineVO)✨ | 字段 | 类型 | 说明 | |------|------|------| | `productName` | String | 保险产品名称 | | `bizType` | String | 业务类型:`ORDER` / `DRIVER` | | `bizTypeName` | String | 业务类型中文名(字典 `insurance_biz_type`,本次新增) | | `totalPremium` | Number | 保费(即核算金额列),两位小数 | | `extPolicyNo` | String | 外部保单号 | | `voucherUrls` | Array<String> | 凭证 URL 数组(电子保单 PDF) | > 保险行无 `plannedCost` / `sourceType` 字段,不输出。 ## 6. 枚举 / 数据字典 ### 6.1 `incomeLines[].type`(字典 `settlement_report_line_type`) | 值 | 中文 | 说明 | |----|------|------| | `BASE_ORDER` | 订单应收 | 订单应收行 | | `OTHER_INCOME` | 其他收入 | 其他收入行 | | `DISCOUNT` | 优惠 | 优惠行(金额为负) | | `ACTUAL_REFUND` | 实际退款 | 实际退款行(金额为负) | ### 6.2 `costCategories[].category`(字典 `settlement_category`) | 值 | 中文 | 对应 lines 行结构 | |----|------|-------------------| | `HOTEL` | 住宿 | §5.5 | | `TICKET` | 门票/游玩项目 | §5.6 | | `MEAL` | 餐食 | §5.7 | | `VEHICLE` | 车辆 | §5.8 | | `GUIDE` | 导游 | §5.9 | | `PHOTOGRAPHER` | 摄影 | §5.9(与 GUIDE 共用) | | `OTHER_EXPENSE` | 其他支出 | §5.10 | | `INSURANCE` | 保险 | §5.11 | ### 6.3 `paymentMethod`(字典 `settlement_payment_method`) 出现于:收入逐项明细、HOTEL / TICKET / MEAL / VEHICLE / 人员 / 其他支出各成本逐项行。 | 值 | 中文 | |----|------| | `CASH_PAID` | 现金已付 | | `COMPANY_PAID` | 公司支付 | | `SIGNED` | 签单 | ### 6.4 `details[].source`(字典 `settlement_refund_source`,✨ 本次新增字典) 仅 `ACTUAL_REFUND` 下人工返还行透出。 | 值 | 中文 | |----|------| | `DRIVER_ONSITE` | 司机现场返还 | | `COMPANY_COMPENSATION` | 公司赔付 | ### 6.5 `lines[].bizType`(字典 `insurance_biz_type`,✨ 本次新增字典) 仅 INSURANCE 保险行。 | 值 | 中文 | |----|------| | `ORDER` | 订单险 | | `DRIVER` | 司机险 | ### 6.6 `sourceType`(后端枚举 SettlementDetailSourceType) 出现于:收入逐项明细、HOTEL / TICKET / MEAL / VEHICLE / 人员 / 其他支出各成本逐项行。 | 值 | 中文 | |----|------| | `MANUAL` | 手工 | | `HOUSE_ASSIGNMENT` | 配房结果 | | `SCENIC_ASSIGNMENT` | 景区 | | `ACTIVITY_ASSIGNMENT` | 游玩项目 | | `MEAL_ASSIGNMENT` | 餐饮安排 | | `FLEET` | 车务 | | `STAFF_ASSIGNMENT` | 人员安排 | | `ORDER_SURCHARGE` | 订单增费 | | `SYSTEM` | 系统 | ### 6.7 `lines[].mealType`(字典 `meal_type`) 仅 MEAL 餐食行。 | 值 | 中文 | |----|------| | `BREAKFAST` | 早餐 | | `LUNCH` | 午餐 | | `DINNER` | 晚餐 | | `SELF` | 自理 | ### 6.8 `lines[].serviceType`(字典 `staff_role`) 仅 GUIDE / PHOTOGRAPHER 人员行。本接口只会出现 `GUIDE` / `PHOTOGRAPHER` 两个值(字典另有 助理导游/领队/司机/其他,不在本接口出现)。 | 值 | 中文 | |----|------| | `GUIDE` | 导游 | | `PHOTOGRAPHER` | 摄影师 | ### 6.9 `lines[].expenseType`(字典 `expense_type`) VEHICLE 行(EXPENSE 族):`FUEL`=油费 / `TOLL`=过路费 / `PARKING`=停车费 / `RENTAL`=租车费 / `MAINTENANCE`=维修保养。 OTHER_EXPENSE 行(EXPENSE 族):固定 `OTHER`=其他。 ### 6.10 `lines[].subsidyType`(字典 `subsidy_type`) 仅 OTHER_EXPENSE 行(SUBSIDY 族)。 | 值 | 中文 | |----|------| | `PHONE` | 话补 | | `OVERTIME` | 加班补贴 | ### 6.11 `reportStatus` | 值 | 中文 | 说明 | |----|------|------| | `GENERATED` | 已生成 | 未核单订单,实时组装 | | `CONFIRMED` | 已确认 | 已核单(SETTLED)订单,终态快照回放 | ## 7. 错误码 | code | 含义 | 触发场景 | |------|------|----------| | 400 | 参数校验失败 | `orderId` 缺失或 < 1 | | 581007 | 订单不存在 | `orderId` 对应订单不存在或已删除 | | 581045 | 房务角色无权查看订单详情,房务仅可配房 | 房务管理员 / 房务组长角色调用 | ## 8. 示例(3 组:典型 / 边界 / 异常) ### 8.1 典型成功(已核单订单,逐项明细完整填充) **请求**: GET /v3/admin/order/1956112233445566778/settlement/reports/group Authorization: Bearer (无请求体) **响应**(节选,仅展示本次新增结构所在的 incomeLines / costCategories,顶层汇总字段与变更前一致故省略): ```json { "code": 200, "data": { "id": "1957000000000000001", "orderId": "1956112233445566778", "reportStatus": "CONFIRMED", "incomeLines": [ { "type": "BASE_ORDER", "typeName": "订单应收", "amount": 12800.00, "details": [ { "itemName": "订单应收", "content": "小红书(孙雷) 王彧琪", "unitPrice": 1280.00, "headCount": 10, "amount": 12800.00 } ] }, { "type": "OTHER_INCOME", "typeName": "其他收入", "amount": 200.00, "details": [ { "itemName": "现场加收骑马费", "unitPrice": 100.00, "quantity": 2, "amount": 200.00, "paymentMethod": "CASH_PAID", "paymentMethodName": "现金已付", "sourceType": "ORDER_SURCHARGE", "sourceTypeName": "订单增费", "remark": "现场加收" } ] }, { "type": "DISCOUNT", "typeName": "优惠", "amount": -500.00, "details": [ { "itemName": "早鸟优惠", "amount": -500.00 } ] }, { "type": "ACTUAL_REFUND", "typeName": "实际退款", "amount": -300.00, "details": [ { "itemName": "司机现场返还", "content": "少住一晚退房差", "source": "DRIVER_ONSITE", "sourceName": "司机现场返还", "amount": -300.00, "paymentMethod": "CASH_PAID", "paymentMethodName": "现金已付", "remark": "现场返还现金" } ] } ], "costCategories": [ { "category": "HOTEL", "categoryName": "住宿", "amount": 3600.00, "lines": [ { "stayDate": "2026-07-30", "hotelName": "草原明珠大酒店", "roomTypeName": "标间", "roomCount": 3, "unitPrice": 400.00, "plannedCost": 1200.00, "amount": 1200.00, "paymentMethod": "CASH_PAID", "paymentMethodName": "现金已付", "sourceType": "HOUSE_ASSIGNMENT", "sourceTypeName": "配房结果", "remark": "含早", "voucherUrls": ["https://oss/v1.jpg"] } ] }, { "category": "VEHICLE", "categoryName": "车辆", "amount": 1760.00, "lines": [ { "serviceDate": "2026-07-30", "startDate": "2026-07-30", "endDate": "2026-07-31", "vehiclePlate": "蒙A-5376", "vehicleModelName": "坦克500", "driverName": "司机甲", "dailyPrice": 880.00, "amount": 880.00, "paymentMethod": "CASH_PAID", "paymentMethodName": "现金已付", "sourceType": "FLEET", "sourceTypeName": "车务" }, { "expenseType": "FUEL", "expenseTypeName": "油费", "projectName": "全程油费", "expenseDate": "2026-07-31", "amount": 880.00, "paymentMethod": "COMPANY_PAID", "paymentMethodName": "公司支付", "sourceType": "FLEET", "sourceTypeName": "车务" } ] }, { "category": "GUIDE", "categoryName": "导游", "amount": 1600.00, "lines": [ { "serviceDate": "2026-07-30", "name": "导游乙", "serviceType": "GUIDE", "serviceTypeName": "导游", "amount": 1600.00, "paymentMethod": "SIGNED", "paymentMethodName": "签单", "sourceType": "STAFF_ASSIGNMENT", "sourceTypeName": "人员安排" } ] }, { "category": "INSURANCE", "categoryName": "保险", "amount": 150.00, "lines": [ { "productName": "保游畅享境内游保险", "bizType": "ORDER", "bizTypeName": "订单险", "totalPremium": 150.00, "extPolicyNo": "PY20260730001", "voucherUrls": ["https://oss/policy1.pdf"] } ] } ] }, "msg": "" } ``` ### 8.2 边界情况(无任何逐项明细 / 金额为 0) **场景说明**:订单无任何其他收入、优惠、退款,且各成本分类未录入任何逐项 —— `details` / `lines` 返回**空数组** `[]`(不是 null),对应分类 `amount` 可为 `0.00`。所有 null 的可选字段(unitPrice / headCount / paymentMethod / remark / voucherUrls 等)**整体缺省不出现在 JSON 中**。 **请求**: GET /v3/admin/order/1956112233445566889/settlement/reports/group Authorization: Bearer (无请求体) **响应**(节选): ```json { "code": 200, "data": { "orderId": "1956112233445566889", "reportStatus": "GENERATED", "incomeLines": [ { "type": "BASE_ORDER", "typeName": "订单应收", "amount": 12800.00, "details": [ { "itemName": "订单应收", "unitPrice": 1280.00, "headCount": 10, "amount": 12800.00 } ] }, { "type": "OTHER_INCOME", "typeName": "其他收入", "amount": 0.00, "details": [] }, { "type": "DISCOUNT", "typeName": "优惠", "amount": 0.00, "details": [] }, { "type": "ACTUAL_REFUND", "typeName": "实际退款", "amount": 0.00, "details": [] } ], "costCategories": [ { "category": "HOTEL", "categoryName": "住宿", "amount": 0.00, "lines": [] }, { "category": "TICKET", "categoryName": "门票/游玩项目", "amount": 0.00, "lines": [] }, { "category": "MEAL", "categoryName": "餐食", "amount": 0.00, "lines": [] }, { "category": "VEHICLE", "categoryName": "车辆", "amount": 0.00, "lines": [] }, { "category": "GUIDE", "categoryName": "导游", "amount": 0.00, "lines": [] }, { "category": "PHOTOGRAPHER", "categoryName": "摄影", "amount": 0.00, "lines": [] }, { "category": "OTHER_EXPENSE", "categoryName": "其他支出", "amount": 0.00, "lines": [] }, { "category": "INSURANCE", "categoryName": "保险", "amount": 0.00, "lines": [] } ] }, "msg": "" } ``` ### 8.3 业务失败(订单不存在 / 房务角色越权) **场景说明 A**:`orderId` 不存在 → 581007。 **请求**: GET /v3/admin/order/999999999/settlement/reports/group Authorization: Bearer (无请求体) **响应**: ```json { "code": 581007, "msg": "订单不存在", "data": null } ``` **场景说明 B**:房务管理员角色调用 → 581045。 ```json { "code": 581045, "msg": "房务角色无权查看订单详情,房务仅可配房", "data": null } ``` ## 9. 业务边界 - ✅ **适用场景**:订单存在即可调;未核单订单(`reportStatus=GENERATED`)走实时组装,已核单订单(`reportStatus=CONFIRMED`)走终态快照回放,**两条路径出参结构完全一致**,前端无需区分 - ❌ **不适用场景**:房务角色(ROOM_MANAGER / HOUSE_KEEPER_LEAD)调用 → 581045 - ⚠️ **特殊边界**: - 逐项明细**不出已付/未付逐行列**,逐行只有单价/数量/核算金额(`paidCost` / `unpaidCost` 仍是顶层分类维度的合计口径,不在逐项层) - VEHICLE / OTHER_EXPENSE 两分类的行是同数组内两族结构稀疏并集(见 §5.8 / §5.10),前端渲染列时按「本族字段是否出现」判别 - `DISCOUNT` / `ACTUAL_REFUND` 行及其明细金额均为**负数**,前端不要自行取绝对值 ## 10. 修改前后对比 ### 10.1 字段级对比 | 字段 | 改前 | 改后 | |------|------|------| | `incomeLines[].details` | 无此字段 | ✨ 新增 `Array`,无逐项时为 `[]` | | `costCategories[].lines` | 无此字段 | ✨ 新增 `Array`(分类专属行结构,按 `category` 窄化),无逐项时为 `[]` | | 顶层汇总字段 / 4 行收入合计 / 8 行成本合计 | 现状 | **不变** | ### 10.2 行为级对比 | 行为 | 改前 | 改后 | |------|------|------| | 核算表核对逐项明细 | 只能看到行合计,需跳各分类明细页签逐条对账 | 行内直接展开 `details` / `lines` 逐项 | | 数据字典 | 无 `insurance_biz_type` / `settlement_refund_source` | ✨ 新增两个字典(保险业务类型 / 人工返还来源) | ## 11. 影响评估 / 回滚 ### 11.1 影响评估 - **是否破坏向后兼容**:否。纯出参增量,原有字段名 / 类型 / 结构 / 合计口径零变化;老前端不读 `details` / `lines` 不受影响 - **前端是否必须同步上线**:否。前端按自身排期接入逐项展开即可 ### 11.2 回滚方案 - **回滚方式**:revert PR #5786 - **回滚后清理**:无(无 DDL、无缓存、无脏数据;两个字典 `insurance_biz_type` / `settlement_refund_source` 保留无害) ## 12. 注意事项 - **null 字段整体缺省**:所有行 VO 标注了 null 不序列化,可选字段(unitPrice / headCount / quantity / paymentMethod / sourceType / remark / voucherUrls 等)为 null 时**该 key 不出现在 JSON 中**,前端按可选字段处理,不要断言 key 必存在 - `details` / `lines` 无逐项时是**空数组 `[]`** 而不是字段缺省,可直接 `.length` 判空 - **行结构窄化**:`costCategories[].lines` 元素是多态结构,前端必须先按 `category` 判别再取分类专属字段(GUIDE / PHOTOGRAPHER 共用人员行结构) - **金额符号**:`DISCOUNT` / `ACTUAL_REFUND` 的行金额与明细金额均为负数;成本各行为正数 - **中文名渲染**:`*Name` 字段后端已回填中文名(字典缺值时回退硬编码),可直接展示;如需动态字典渲染,调 `GET /admin/dict/data/{dictType}`,dictType 见 §6 各子节 - 无历史 workaround 需要清理(本能力此前不存在) ## 13. 关联 / 联系人 ### 13.1 链接 - **Issue**: [#5781](https://git.1814.love:8443/wx/HL/issues/5781) - **PR**: [#5786](https://git.1814.love:8443/wx/HL/pulls/5786) - **Merge commit**: [fdc429bf](https://git.1814.love:8443/wx/HL/commit/fdc429bf8546ce2cbb8c69f4166b153cccb4839c) ### 13.2 联系人 - **后端负责人**: @yaosutu