From bb0cdc6e80d29b86d97027af46625db7c0bd02e8 Mon Sep 17 00:00:00 2001 From: yaosutu <770858045@qq.com> Date: Tue, 28 Jul 2026 18:10:53 +0800 Subject: [PATCH] =?UTF-8?q?=E5=88=A0=E9=99=A4=E6=97=A7=E6=A0=B8=E5=8D=95?= =?UTF-8?q?=E5=85=BC=E5=AE=B9=E6=8E=A5=E5=8F=A3=E5=B9=B6=E8=A1=A5=E5=85=85?= =?UTF-8?q?=E5=89=8D=E7=AB=AF=E8=BF=81=E7=A7=BB=E8=AF=B4=E6=98=8E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...24_删除旧核单兼容接口-删除接口-管理后台.md | 875 ++++++++++++++++++ 1 file changed, 875 insertions(+) create mode 100644 changelogs-v2/2026-07/28_5324_删除旧核单兼容接口-删除接口-管理后台.md diff --git a/changelogs-v2/2026-07/28_5324_删除旧核单兼容接口-删除接口-管理后台.md b/changelogs-v2/2026-07/28_5324_删除旧核单兼容接口-删除接口-管理后台.md new file mode 100644 index 0000000..571af84 --- /dev/null +++ b/changelogs-v2/2026-07/28_5324_删除旧核单兼容接口-删除接口-管理后台.md @@ -0,0 +1,875 @@ +--- +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 +- **前端负责人**: 待认领