--- schema: "hl-changelog/v2" ticket: "5324" title: "删除旧核单兼容接口" consumer: "admin" change_type: "删除接口" backend_status: "deployed" backend_ref: "PR #5328 · merge 709105c1a1d26ae1c867fcc998286781f499faf2" deployment_status: "deployed" deployment_ref: "deploy-panel task ad042377" gateway_status: "verified" verification_status: "verified" verification_ref: "D:/work/project-doc/PRPs/reports/5328-deploy-qa-report.md · D:/work/project-doc/test/5328/evidence.json" frontend_status: "pending" frontend_owner: "" frontend_ref: "" target_release: "" verified_at: "2026-07-28T18:09:00+08:00" status_note: "部署 task ad042377 成功;8086/8186 正常;网关 20/20 HTTP 200、0 网络错误、0 个 5xx、RST 0;3 个删除路由均返回业务 404,4 个保留路由进入业务门禁且零写入。管理后台待迁移到双报表确认后调用 finalize 的五步流程。" updated_at: "2026-07-28" base: "dev-v3" generated: "2026-07-28T18:04:49+08:00" --- # 【删除接口·管理后台】删除旧核单兼容接口 (#5324) > **PR**: #5328 | **服务**: hl-order-service-v3 | **更新时间**: 2026-07-28 18:04 ## 1. 接口背景 核单完成入口统一为“双报表确认后完成核单”:管理后台先读取并确认主报账人报账表,再读取并确认单团核算表,最后携带两份报告的当前来源指纹调用 `finalize`。旧分类确认兼容接口和旧 Step6 提交入口不再提供。 ## 2. 变更清单 ### 2.1 删除的接口 | # | 接口 | 方法 | 路径 | 变更类型 | 前端动作 | |---|------|------|------|----------|----------| | 1 | 查询核单分类确认状态 | GET | `/v3/admin/order/{orderId}/settlement/category-checks` | 删除 | 删除调用及分类确认状态门禁 | | 2 | 确认单个核单分类 | POST | `/v3/admin/order/{orderId}/settlement/category-checks/{category}/confirm` | 删除 | 删除调用及“本分类已确认”交互 | | 3 | 旧 Step6 提交核单 | POST | `/v3/admin/order/{orderId}/settlement/step6/submit` | 删除 | 改为下表五步流程 | ### 2.2 唯一替代流程 | 顺序 | 接口 | 方法 | 路径 | 用途 | |------|------|------|------|------| | 1 | 查询主报账人报账表 | GET | `/v3/admin/order/{orderId}/settlement/reports/reimbursement` | 获取实时数据和主报账表 `sourceFingerprint` | | 2 | 确认主报账人报账表 | POST | `/v3/admin/order/{orderId}/settlement/reports/reimbursement/confirm` | 确认转账、预支结清标志和签字凭证 | | 3 | 查询单团核算表 | GET | `/v3/admin/order/{orderId}/settlement/reports/group` | 获取实时数据和单团核算表 `sourceFingerprint` | | 4 | 确认单团核算表 | POST | `/v3/admin/order/{orderId}/settlement/reports/group/confirm` | 确认当前单团核算结果 | | 5 | 完成核单 | POST | `/v3/admin/order/{orderId}/settlement/finalize` | 携带两份已确认报告的当前指纹完成核单 | ## 3. 接口详情 以下五个接口都需要管理后台登录态,房务角色不可访问;`orderId` 为必填路径参数,类型为 `Long/String`,值必须大于 0。 ### 3.1 查询主报账人报账表 - **方法 + 路径**:`GET /v3/admin/order/{orderId}/settlement/reports/reimbursement` - **使用场景**:进入报账报表页面、确认前刷新、来源数据变化后重新获取。 - **幂等性**:幂等,只读。 - **请求体**:无。 - **成功响应**:`Result`,完整字段见 §5.2。 - **关键规则**:前端必须保存本次响应的 `data.sourceFingerprint`;确认请求不得使用缓存的旧指纹。 **请求示例** ```http GET /v3/admin/order/1914050000000001/settlement/reports/reimbursement Authorization: Bearer ``` **响应示例** ```json { "code": 200, "msg": "success", "data": { "id": null, "orderId": "1914050000000001", "reportStatus": "GENERATED", "sourceFingerprint": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "primaryReporterId": "3001", "primaryReporterName": "王司机", "primaryReporterRole": "DRIVER", "reportVersion": 1, "driverCollectedTailAmount": 2000.00, "approvedAdvanceAmount": 500.00, "reportablePaidCostAmount": 1000.00, "reporterNetAmount": 1500.00, "primaryReporterCollectedAmount": 2000.00, "publicPrepaidAmount": 1000.00, "primaryReporterDueAmount": 1000.00, "advanceOutstandingAmount": 500.00, "reconNetAmount": 1500.00, "transferDirection": "REPORTER_TO_COMPANY", "transferAmount": 1500.00, "incomeLines": [ { "type": "DRIVER_CASH_RECEIPT", "receiptId": "9100000000001", "amount": 2000.00, "channel": "DRIVER_CASH", "payType": "CASH", "collectorStaffId": "3001", "collectorName": "王司机", "collectorRole": "DRIVER", "receivedAt": "2026-07-27T18:30:00", "remark": "司机代收尾款" } ], "expenseLines": [ { "category": "HOTEL", "kind": "HOTEL", "hotelAssignmentId": "9200000000001", "hotelName": "示例酒店", "stayDate": "2026-07-20", "amount": 1000.00, "paymentMethod": "CASH_PAID", "remark": null } ], "advanceLines": [ { "type": "APPROVED_ADVANCE", "advanceId": "9300000000001", "payeeStaffId": "3001", "payeeName": "王司机", "payeeRole": "DRIVER", "advanceType": "PUBLIC", "amount": 500.00, "purpose": "途中费用", "voucherUrl": "https://oss.example.com/advance.jpg", "status": "APPROVED", "submittedAt": "2026-07-18T10:00:00", "approvedAt": "2026-07-18T11:00:00", "approvedBy": "10001" } ], "vehicleLines": [], "transferStatus": null, "transferDate": null, "transferRef": null, "advanceSettledFlag": false, "signedVoucher": null, "generatedBy": null, "generatedByName": null, "generatedAt": null, "confirmedBy": null, "confirmedByName": null, "confirmedAt": null } } ``` ### 3.2 确认主报账人报账表 - **方法 + 路径**:`POST /v3/admin/order/{orderId}/settlement/reports/reimbursement/confirm` - **使用场景**:已核对主报账表,且转账、预支标记和签字凭证已经填写完毕。 - **幂等性**:同一当前指纹和完全相同的确认内容可重复提交;确认后改传其它内容返回 `584317`。 - **请求体**:见 §4.2。 - **成功响应**:与 §3.1 相同,`reportStatus=CONFIRMED`,并返回确认信息。 **请求示例** ```http POST /v3/admin/order/1914050000000001/settlement/reports/reimbursement/confirm Authorization: Bearer Content-Type: application/json { "expectedSourceFingerprint": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "transferStatus": "COMPLETED", "transferDate": "2026-07-28", "transferRef": "BANK-20260728-001", "advanceSettledFlag": true, "signedVoucher": { "files": [ { "name": "司机签字单.pdf", "url": "https://oss.example.com/signed-voucher.pdf" } ], "note": "签字凭证已回收" } } ``` **响应示例** ```json { "code": 200, "msg": "success", "data": { "id": "9400000000001", "orderId": "1914050000000001", "reportStatus": "CONFIRMED", "sourceFingerprint": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "primaryReporterId": "3001", "primaryReporterName": "王司机", "primaryReporterRole": "DRIVER", "reportVersion": 1, "driverCollectedTailAmount": 2000.00, "approvedAdvanceAmount": 500.00, "reportablePaidCostAmount": 1000.00, "reporterNetAmount": 1500.00, "primaryReporterCollectedAmount": 2000.00, "publicPrepaidAmount": 1000.00, "primaryReporterDueAmount": 1000.00, "advanceOutstandingAmount": 500.00, "reconNetAmount": 1500.00, "transferDirection": "REPORTER_TO_COMPANY", "transferAmount": 1500.00, "incomeLines": [], "expenseLines": [], "advanceLines": [], "vehicleLines": [], "transferStatus": "COMPLETED", "transferDate": "2026-07-28", "transferRef": "BANK-20260728-001", "advanceSettledFlag": true, "signedVoucher": { "files": [ { "name": "司机签字单.pdf", "url": "https://oss.example.com/signed-voucher.pdf" } ], "note": "签字凭证已回收" }, "generatedBy": null, "generatedByName": null, "generatedAt": null, "confirmedBy": "10001", "confirmedByName": "财务管理员", "confirmedAt": "2026-07-28T18:10:00" } } ``` ### 3.3 查询单团核算表 - **方法 + 路径**:`GET /v3/admin/order/{orderId}/settlement/reports/group` - **使用场景**:主报账表确认后查看单团收入、成本、毛利和人均指标。 - **幂等性**:幂等,只读。 - **请求体**:无。 - **成功响应**:`Result`,完整字段见 §5.3。 - **关键规则**:前端必须保存本次响应的 `data.sourceFingerprint`;确认请求不得使用缓存的旧指纹。 **请求示例** ```http GET /v3/admin/order/1914050000000001/settlement/reports/group Authorization: Bearer ``` **响应示例** ```json { "code": 200, "msg": "success", "data": { "id": null, "orderId": "1914050000000001", "reportStatus": "GENERATED", "sourceFingerprint": "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", "baseOrderAmount": 24800.00, "otherIncomeAmount": 500.00, "discountAmount": 300.00, "adjustedReceivableAmount": 25000.00, "paidAmount": 25000.00, "actualRefundedAmount": 0.00, "netRevenueAmount": 25000.00, "netReceivedAmount": 25000.00, "outstandingAmount": 0.00, "hotelCost": 4280.00, "ticketCost": 3680.00, "mealCost": 860.00, "vehicleCost": 1260.00, "guideCost": 800.00, "photographerCost": 600.00, "otherExpenseCost": 300.00, "insurancePremium": 180.00, "totalCost": 11960.00, "paidCost": 11960.00, "unpaidCost": 0.00, "grossProfit": 13040.00, "grossProfitRate": 0.5216, "travelerCount": 5, "perCapitaRevenue": 5000.00, "perCapitaCost": 2392.00, "perCapitaProfit": 2608.00, "incomeLines": [ {"type": "BASE_ORDER", "amount": 24800.00}, {"type": "OTHER_INCOME", "amount": 500.00}, {"type": "DISCOUNT", "amount": -300.00}, {"type": "ACTUAL_REFUND", "amount": 0.00} ], "costCategories": [ {"category": "HOTEL", "amount": 4280.00}, {"category": "TICKET", "amount": 3680.00}, {"category": "MEAL", "amount": 860.00}, {"category": "VEHICLE", "amount": 1260.00}, {"category": "GUIDE", "amount": 800.00}, {"category": "PHOTOGRAPHER", "amount": 600.00}, {"category": "OTHER_EXPENSE", "amount": 300.00}, {"category": "INSURANCE", "amount": 180.00} ], "generatedBy": null, "generatedByName": null, "generatedAt": null, "confirmedBy": null, "confirmedByName": null, "confirmedAt": null } } ``` ### 3.4 确认单团核算表 - **方法 + 路径**:`POST /v3/admin/order/{orderId}/settlement/reports/group/confirm` - **使用场景**:主报账表已经确认,且已核对当前单团收入、成本和利润。 - **幂等性**:相同当前指纹重复确认返回已确认结果。 - **请求体**:见 §4.3。 - **成功响应**:与 §3.3 相同,`reportStatus=CONFIRMED`,并返回确认人和确认时间。 **请求示例** ```http POST /v3/admin/order/1914050000000001/settlement/reports/group/confirm Authorization: Bearer Content-Type: application/json { "expectedSourceFingerprint": "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb" } ``` **响应示例** ```json { "code": 200, "msg": "success", "data": { "id": "9500000000001", "orderId": "1914050000000001", "reportStatus": "CONFIRMED", "sourceFingerprint": "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", "baseOrderAmount": 24800.00, "otherIncomeAmount": 500.00, "discountAmount": 300.00, "adjustedReceivableAmount": 25000.00, "paidAmount": 25000.00, "actualRefundedAmount": 0.00, "netRevenueAmount": 25000.00, "netReceivedAmount": 25000.00, "outstandingAmount": 0.00, "hotelCost": 4280.00, "ticketCost": 3680.00, "mealCost": 860.00, "vehicleCost": 1260.00, "guideCost": 800.00, "photographerCost": 600.00, "otherExpenseCost": 300.00, "insurancePremium": 180.00, "totalCost": 11960.00, "paidCost": 11960.00, "unpaidCost": 0.00, "grossProfit": 13040.00, "grossProfitRate": 0.5216, "travelerCount": 5, "perCapitaRevenue": 5000.00, "perCapitaCost": 2392.00, "perCapitaProfit": 2608.00, "incomeLines": [ {"type": "BASE_ORDER", "amount": 24800.00}, {"type": "OTHER_INCOME", "amount": 500.00}, {"type": "DISCOUNT", "amount": -300.00}, {"type": "ACTUAL_REFUND", "amount": 0.00} ], "costCategories": [ {"category": "HOTEL", "amount": 4280.00}, {"category": "TICKET", "amount": 3680.00}, {"category": "MEAL", "amount": 860.00}, {"category": "VEHICLE", "amount": 1260.00}, {"category": "GUIDE", "amount": 800.00}, {"category": "PHOTOGRAPHER", "amount": 600.00}, {"category": "OTHER_EXPENSE", "amount": 300.00}, {"category": "INSURANCE", "amount": 180.00} ], "generatedBy": null, "generatedByName": null, "generatedAt": null, "confirmedBy": "10001", "confirmedByName": "财务管理员", "confirmedAt": "2026-07-28T18:12:00" } } ``` ### 3.5 完成核单 - **方法 + 路径**:`POST /v3/admin/order/{orderId}/settlement/finalize` - **使用场景**:两份报告均已确认且来源仍为当前版本时,点击“完成核单”。 - **幂等性**:已完成且存在当前终态结果时,重复提交返回当前终态结果。 - **请求体**:见 §4.4;请求体在业务上必填。 - **成功响应**:`Result`,完整字段见 §5.4。 **请求示例** ```http POST /v3/admin/order/1914050000000001/settlement/finalize Authorization: Bearer Content-Type: application/json { "remark": "双报表已核对完成", "reimbursementExpectedSourceFingerprint": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "groupExpectedSourceFingerprint": "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb" } ``` **响应示例** ```json { "code": 200, "msg": "success", "data": { "summaryId": "9600000000001", "finalSnapshotId": "9600000000002", "finalSnapshotVersionNo": 1, "finalSnapshotStatus": "FINALIZED", "orderId": "1914050000000001", "settledAt": "2026-07-28T18:15:00", "totalAmount": 25000.00, "paidAmount": 25000.00, "balanceAmount": 0.00, "roomCost": 4280.00, "ticketCost": 3680.00, "staffCost": 1400.00, "subsidyCost": 0.00, "mealCost": 860.00, "vehicleCost": 1260.00, "otherExpenseCost": 300.00, "insurancePremium": 180.00, "totalActualCost": 11960.00, "driverTransferAmount": 1000.00, "profitAmount": 13040.00, "profitRate": 0.5216, "orderStatusAfter": "待财务复核", "mqTriggered": true, "warnings": [] } } ``` ## 4. 接口入参 ### 4.1 五个替代接口共用路径参数 | 字段 | 类型 | 必填 | 说明 | 校验 | |------|------|------|------|------| | `orderId` | Long/String | 是 | 订单 ID | 必须大于 0 | 两个 GET 接口没有 Query 参数和请求体。 ### 4.2 主报账表确认请求体 | 字段 | 类型 | 必填 | 说明 | 校验规则 | |------|------|------|------|----------| | `expectedSourceFingerprint` | String | 是 | §3.1 最新响应的 `data.sourceFingerprint` | 64 位小写十六进制 | | `transferStatus` | String | 是 | 转账处理状态 | 固定传 `COMPLETED` | | `transferDate` | String/date | 条件必填 | 转账日期 | `reporterNetAmount != 0` 时必填;格式 `YYYY-MM-DD` | | `transferRef` | String | 条件必填 | 转账流水号或可追溯凭证号 | `reporterNetAmount != 0` 时不得为空白 | | `advanceSettledFlag` | Boolean | 是 | 预支款项是否已处理完毕 | 不得为 `null` | | `signedVoucher` | Object | 是 | 签字凭证 | 不得为 `null` | | `signedVoucher.files` | Array | 是 | 签字凭证文件列表 | 至少 1 项 | | `signedVoucher.files[].name` | String | 否 | 文件名 | 可为空 | | `signedVoucher.files[].url` | String | 是 | 文件地址 | 不得为空白 | | `signedVoucher.note` | String | 否 | 凭证备注 | 可为空 | 当 `reporterNetAmount = 0` 时,`transferDate` 和 `transferRef` 可不传;`transferStatus` 仍必须是 `COMPLETED`,签字凭证仍必须至少包含一个有效文件。 ### 4.3 单团核算表确认请求体 | 字段 | 类型 | 必填 | 说明 | 校验规则 | |------|------|------|------|----------| | `expectedSourceFingerprint` | String | 是 | §3.3 最新响应的 `data.sourceFingerprint` | 64 位小写十六进制 | ### 4.4 完成核单请求体 | 字段 | 类型 | 必填 | 说明 | 校验规则 | |------|------|------|------|----------| | `remark` | String | 否 | 本次完成核单的整体备注 | 最长 500 字 | | `reimbursementExpectedSourceFingerprint` | String | 是 | 已确认主报账表的当前 `sourceFingerprint` | 64 位小写十六进制 | | `groupExpectedSourceFingerprint` | String | 是 | 已确认单团核算表的当前 `sourceFingerprint` | 64 位小写十六进制 | ### 4.5 指纹传递关系 | 来源 | 确认接口字段 | 完成核单字段 | |------|--------------|--------------| | `GET .../reports/reimbursement` 的 `data.sourceFingerprint` | `POST .../reports/reimbursement/confirm` 的 `expectedSourceFingerprint` | `POST .../finalize` 的 `reimbursementExpectedSourceFingerprint` | | `GET .../reports/group` 的 `data.sourceFingerprint` | `POST .../reports/group/confirm` 的 `expectedSourceFingerprint` | `POST .../finalize` 的 `groupExpectedSourceFingerprint` | ## 5. 出参 ### 5.1 统一响应外层 | 字段 | 类型 | 说明 | |------|------|------| | `code` | Integer | `200` 表示成功;其它值见 §7 | | `msg` | String | 结果说明 | | `data` | Object/null | 成功时为业务数据,失败时通常为 `null` | 所有 Long ID 以 JSON 字符串消费,避免前端数字精度损失;金额字段为十进制数。 ### 5.2 主报账人报账表响应 | 字段 | 类型 | 说明 | |------|------|------| | `id` | String/null | 报账表记录 ID;仅实时预览、尚未确认时可为 `null` | | `orderId` | String | 订单 ID | | `reportStatus` | String | `GENERATED` / `CONFIRMED` / `STALE`,见 §6.1 | | `sourceFingerprint` | String | 当前来源指纹,64 位小写十六进制 | | `primaryReporterId` | String/null | 主报账人 ID | | `primaryReporterName` | String/null | 主报账人姓名 | | `primaryReporterRole` | String/null | 主报账人角色 | | `reportVersion` | Integer/null | 报账表版本 | | `driverCollectedTailAmount` | Decimal | 司机代收尾款 | | `approvedAdvanceAmount` | Decimal | 已审批预支合计 | | `reportablePaidCostAmount` | Decimal | 主报账人已支付、可报账成本合计 | | `reporterNetAmount` | Decimal | 报账净额:司机代收尾款 + 已审批预支 - 可报账已支付成本 | | `primaryReporterCollectedAmount` | Decimal | 主报账人代收金额 | | `publicPrepaidAmount` | Decimal | 公共预支金额 | | `primaryReporterDueAmount` | Decimal | 主报账人应报账金额 | | `advanceOutstandingAmount` | Decimal | 待处理预支金额 | | `reconNetAmount` | Decimal | 报账净额兼容字段 | | `transferDirection` | String | 转账方向,见 §6.2 | | `transferAmount` | Decimal | 需转账金额,取 `reporterNetAmount` 绝对值 | | `incomeLines` | Array | 司机代收尾款明细 | | `expenseLines` | Array | 主报账人现金支付成本明细 | | `advanceLines` | Array | 已审批预支明细 | | `vehicleLines` | Array | 车辆逐日明细;允许空数组 | | `transferStatus` | String/null | 未确认时可为空;确认后为 `COMPLETED` | | `transferDate` | String/date/null | 转账日期 | | `transferRef` | String/null | 转账流水号或凭证号 | | `advanceSettledFlag` | Boolean | 预支是否已处理完毕 | | `signedVoucher` | Object/null | 签字凭证,结构同 §4.2 | | `generatedBy` | String/null | 历史生成操作人 ID | | `generatedByName` | String/null | 历史生成操作人姓名 | | `generatedAt` | String/date-time/null | 历史生成时间 | | `confirmedBy` | String/null | 确认人 ID | | `confirmedByName` | String/null | 确认人姓名 | | `confirmedAt` | String/date-time/null | 确认时间 | `incomeLines[]` 的固定字段为 `type`、`receiptId`、`amount`、`channel`、`payType`、`collectorStaffId`、`collectorName`、`collectorRole`、`receivedAt`、`remark`。 `advanceLines[]` 的固定字段为 `type`、`advanceId`、`payeeStaffId`、`payeeName`、`payeeRole`、`advanceType`、`amount`、`purpose`、`voucherUrl`、`status`、`submittedAt`、`approvedAt`、`approvedBy`。 `expenseLines[]` 至少包含 `category`、`kind`、`amount`、`paymentMethod`;按分类还会包含对应的名称、日期、数量、单价、人员或车辆标识、凭证和备注字段。前端列表应按字段是否存在展示,不依赖固定列宽。 ### 5.3 单团核算表响应 | 字段 | 类型 | 说明 | |------|------|------| | `id` | String/null | 单团核算表记录 ID;仅实时预览、尚未确认时可为 `null` | | `orderId` | String | 订单 ID | | `reportStatus` | String | `GENERATED` / `CONFIRMED` / `STALE` | | `sourceFingerprint` | String | 当前来源指纹,64 位小写十六进制 | | `baseOrderAmount` | Decimal | 订单基础金额 | | `otherIncomeAmount` | Decimal | 其他收入 | | `discountAmount` | Decimal | 优惠金额 | | `adjustedReceivableAmount` | Decimal | 调整后应收 | | `paidAmount` | Decimal | 已收金额 | | `actualRefundedAmount` | Decimal | 实际退款 | | `netRevenueAmount` | Decimal | 净收入 | | `netReceivedAmount` | Decimal | 净已收 | | `outstandingAmount` | Decimal | 待收金额 | | `hotelCost` | Decimal | 住宿成本 | | `ticketCost` | Decimal | 门票/游玩项目成本 | | `mealCost` | Decimal | 餐食成本 | | `vehicleCost` | Decimal | 车辆成本 | | `guideCost` | Decimal | 导游成本 | | `photographerCost` | Decimal | 摄影成本 | | `otherExpenseCost` | Decimal | 其他支出成本 | | `insurancePremium` | Decimal | 保险保费 | | `totalCost` | Decimal | 总成本 | | `paidCost` | Decimal | 已支付成本 | | `unpaidCost` | Decimal | 未支付成本 | | `grossProfit` | Decimal | 毛利 | | `grossProfitRate` | Decimal | 毛利率;收入为 0 时为 0 | | `travelerCount` | Integer | 出行人数 | | `perCapitaRevenue` | Decimal | 人均收入 | | `perCapitaCost` | Decimal | 人均成本 | | `perCapitaProfit` | Decimal | 人均利润 | | `incomeLines` | Array | 收入构成;元素字段为 `type`、`amount` | | `costCategories` | Array | 成本构成;元素字段为 `category`、`amount` | | `generatedBy` | String/null | 历史生成操作人 ID | | `generatedByName` | String/null | 历史生成操作人姓名 | | `generatedAt` | String/date-time/null | 历史生成时间 | | `confirmedBy` | String/null | 确认人 ID | | `confirmedByName` | String/null | 确认人姓名 | | `confirmedAt` | String/date-time/null | 确认时间 | ### 5.4 完成核单响应 | 字段 | 类型 | 说明 | |------|------|------| | `summaryId` | String | 核单汇总 ID | | `finalSnapshotId` | String | 核单终态版本 ID | | `finalSnapshotVersionNo` | Integer | 核单终态版本号 | | `finalSnapshotStatus` | String | 核单终态状态,成功时为 `FINALIZED` | | `orderId` | String | 订单 ID | | `settledAt` | String/date-time | 核单完成时间 | | `totalAmount` | Decimal | 订单总金额 | | `paidAmount` | Decimal | 已收金额 | | `balanceAmount` | Decimal | 待收金额;完成核单时必须为 0 | | `roomCost` | Decimal | 住宿实际成本 | | `ticketCost` | Decimal | 门票实际成本 | | `staffCost` | Decimal | 人员实际成本 | | `subsidyCost` | Decimal | 补助实际成本 | | `mealCost` | Decimal | 餐食实际成本 | | `vehicleCost` | Decimal | 车辆实际成本 | | `otherExpenseCost` | Decimal | 其他支出实际成本 | | `insurancePremium` | Decimal | 保险保费 | | `totalActualCost` | Decimal | 总实际成本 | | `driverTransferAmount` | Decimal | 需与司机/主报账人结算的金额 | | `profitAmount` | Decimal | 公司毛利 | | `profitRate` | Decimal | 公司毛利率 | | `orderStatusAfter` | String | 完成核单后的订单状态 | | `mqTriggered` | Boolean | 核单完成事件是否已触发 | | `warnings` | Array | 软提示列表;不阻塞成功结果 | ## 6. 枚举 / 数据字典 ### 6.1 `reportStatus` **所属字段**:两份报告的 `reportStatus` | **类型**:String | 值 | 中文 | 说明 | |----|------|------| | `GENERATED` | 待确认 | 当前实时数据可供核对,尚未确认 | | `CONFIRMED` | 已确认 | 当前来源数据已经确认 | | `STALE` | 已失效 | 来源数据已变化,旧确认不能用于完成核单 | ### 6.2 `transferDirection` **所属字段**:主报账表 `transferDirection` | **类型**:String | 值 | 中文 | 说明 | |----|------|------| | `REPORTER_TO_COMPANY` | 报账人转给公司 | `reporterNetAmount > 0` | | `COMPANY_TO_REPORTER` | 公司转给报账人 | `reporterNetAmount < 0` | | `BALANCED` | 无需转账 | `reporterNetAmount = 0` | ### 6.3 `transferStatus` **所属字段**:主报账表确认请求和响应 `transferStatus` | **类型**:String | 值 | 中文 | 说明 | |----|------|------| | `COMPLETED` | 已完成 | 确认主报账表时唯一允许值 | ### 6.4 单团收入行 `type` | 值 | 中文 | 金额符号 | |----|------|----------| | `BASE_ORDER` | 订单基础收入 | 正数 | | `OTHER_INCOME` | 其他收入 | 正数 | | `DISCOUNT` | 优惠 | 负数 | | `ACTUAL_REFUND` | 实际退款 | 负数或 0 | ### 6.5 单团成本行 `category` | 值 | 中文 | |----|------| | `HOTEL` | 住宿 | | `TICKET` | 门票/游玩项目 | | `MEAL` | 餐食 | | `VEHICLE` | 车辆 | | `GUIDE` | 导游 | | `PHOTOGRAPHER` | 摄影 | | `OTHER_EXPENSE` | 其他支出 | | `INSURANCE` | 保险 | ## 7. 错误码 | code | 含义 | 触发场景 | |------|------|----------| | `400` | 参数校验失败 | `orderId <= 0`、请求体缺字段、指纹格式错误等 | | `403` | 无访问或写入权限 | 房务角色访问,或操作人没有核单写权限 | | `404` | 接口不存在 | 调用本次删除的 3 个旧接口 | | `584082` | 存在待收尾款,请收齐后再提交核单 | `finalize` 时单团核算表 `outstandingAmount != 0` | | `584312` | 主报账表尚未确认或数据已变化 | 单团核算表确认前,主报账表未确认或已失效 | | `584314` | 单团核算表尚未确认或数据已变化 | `finalize` 时单团核算表未确认、已失效或指纹不匹配 | | `584315` | 核单来源数据已变化,请刷新后重新确认 | 确认报告时提交的 `expectedSourceFingerprint` 不是当前值 | | `584316` | 核单报告发生并发变化,请刷新后重试 | 多人同时确认同一报告发生冲突 | | `584317` | 当前报告状态不允许执行该操作 | 确认内容不合法,或报告当前状态不允许重复变更 | | `584325` | 完成核单必须提交主报账和单团核算的当前指纹 | `finalize` 缺少任一指纹或指纹不是 64 位小写十六进制 | ## 8. 示例(典型 / 边界 / 异常) ### 8.1 典型成功:五步完成核单 1. 调用 `GET .../reports/reimbursement`,保存响应 `sourceFingerprint=aaaa...`。 2. 调用 `POST .../reports/reimbursement/confirm`,`expectedSourceFingerprint` 传 `aaaa...`,响应状态为 `CONFIRMED`。 3. 调用 `GET .../reports/group`,保存响应 `sourceFingerprint=bbbb...`。 4. 调用 `POST .../reports/group/confirm`,`expectedSourceFingerprint` 传 `bbbb...`,响应状态为 `CONFIRMED`。 5. 调用 `POST .../finalize`,两个指纹分别传 `aaaa...` 和 `bbbb...`,响应 `finalSnapshotStatus=FINALIZED`。 各步完整请求和响应见 §3.1~§3.5。 ### 8.2 边界:报账净额为 0 当最新主报账表返回 `reporterNetAmount=0`、`transferDirection=BALANCED` 时: ```http POST /v3/admin/order/1914050000000001/settlement/reports/reimbursement/confirm Authorization: Bearer Content-Type: application/json { "expectedSourceFingerprint": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "transferStatus": "COMPLETED", "advanceSettledFlag": true, "signedVoucher": { "files": [ { "name": "司机签字单.pdf", "url": "https://oss.example.com/signed-voucher.pdf" } ], "note": null } } ``` ```json { "code": 200, "msg": "success", "data": { "orderId": "1914050000000001", "reportStatus": "CONFIRMED", "sourceFingerprint": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "reporterNetAmount": 0.00, "transferDirection": "BALANCED", "transferAmount": 0.00, "transferStatus": "COMPLETED", "transferDate": null, "transferRef": null, "advanceSettledFlag": true, "signedVoucher": { "files": [ { "name": "司机签字单.pdf", "url": "https://oss.example.com/signed-voucher.pdf" } ], "note": null } } } ``` ### 8.3 异常:来源数据变化 ```http POST /v3/admin/order/1914050000000001/settlement/reports/group/confirm Authorization: Bearer Content-Type: application/json { "expectedSourceFingerprint": "cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc" } ``` ```json { "code": 584315, "msg": "核单来源数据已变化,请刷新后重新确认", "data": null } ``` 收到该错误后重新执行对应 GET,使用新的 `data.sourceFingerprint` 重新确认;不得继续用旧指纹调用 `finalize`。 ### 8.4 异常:仍有待收尾款 ```http POST /v3/admin/order/1914050000000001/settlement/finalize Authorization: Bearer Content-Type: application/json { "reimbursementExpectedSourceFingerprint": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "groupExpectedSourceFingerprint": "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb" } ``` ```json { "code": 584082, "msg": "存在待收尾款,请收齐后再提交核单", "data": null } ``` ## 9. 业务边界 - 必须按“查询主报账表 → 确认主报账表 → 查询单团核算表 → 确认单团核算表 → 完成核单”的顺序执行。 - 两份报告的指纹互不通用;禁止把主报账表指纹传到单团核算字段,或反向混用。 - 每次确认前都应重新 GET;当 `reportStatus=STALE` 或收到 `584315` 时,必须刷新数据并使用新指纹。 - 单团核算表确认依赖当前有效的主报账表确认,否则返回 `584312`。 - `finalize` 同时校验两份报告已确认、指纹仍为当前值,以及 `outstandingAmount=0`。 - 主报账表 `reporterNetAmount != 0` 时,确认请求必须提供 `transferDate` 和非空 `transferRef`。 - 主报账表确认始终要求至少一个含有效 `url` 的签字凭证文件。 ## 10. 修改前后对比 ### 10.1 接口级对比 | 功能 | 修改前 | 修改后 | |------|--------|--------| | 分类状态 | 调用 `GET .../category-checks` | 不再查询分类确认状态 | | 分类确认 | 调用 `POST .../category-checks/{category}/confirm` | 保存各核单明细即可,不再单独确认分类 | | 报账与单团核算 | 可能绕过双报表直接提交旧 Step6 | 必须分别 GET、confirm 两份报告 | | 完成核单 | `POST .../step6/submit` | `POST .../finalize`,请求体必须携带两个当前指纹 | ### 10.2 请求体对比 | 入口 | 修改前 | 修改后 | |------|--------|--------| | 旧 `step6/submit` | 旧提交请求 | 接口删除 | | 新 `finalize` | 不适用 | `remark` 可选;`reimbursementExpectedSourceFingerprint`、`groupExpectedSourceFingerprint` 必填 | ## 11. 影响评估 / 回滚 ### 11.1 影响评估 - **是否破坏向后兼容**:是。3 个旧接口已删除。 - **前端是否必须同步上线**:是。仍调用任一旧接口的管理后台将收到 404;旧 Step6 提交必须迁移为五步流程。 ### 11.2 回滚原则 - 后端回退时,前端仍可保留五步新流程。 - 前端不得因为短期回退重新新增分类确认入口或恢复旧 Step6 调用;如需临时兼容,应单独确认接口契约后再处理。 ## 12. 注意事项 - 删除 `category-checks` 查询、分类确认 API 封装、分类确认按钮和相关状态门禁。 - 删除 `step6/submit` API 封装及所有调用点。 - “完成核单”按钮改为调用 `finalize`,并在调用前确保两份报告都为 `CONFIRMED`。 - 页面状态中分别保存两份 `sourceFingerprint`,不要只保存一个通用指纹。 - 主报账表确认成功后再开放单团核算确认;单团核算确认成功后再开放“完成核单”。 - 收到 `584312`、`584314`、`584315`、`584316` 时刷新对应报告,不得自动使用旧数据重试。 ## 13. 关联 / 联系人 ### 13.1 链接 - **Issue**: [#5324](https://git.1814.love:8443/wx/HL/issues/5324) - **PR**: [#5328](https://git.1814.love:8443/wx/HL/pulls/5328) - **Merge commit**: [709105c1a1](https://git.1814.love:8443/wx/HL/commit/709105c1a1d26ae1c867fcc998286781f499faf2) ### 13.2 联系人 - **后端负责人**: @yst - **前端负责人**: 待认领