From 272b23b2518ac1ba4bfeb8c8f149e48da10bb9fc Mon Sep 17 00:00:00 2001 From: yaosutu <770858045@qq.com> Date: Sat, 25 Jul 2026 16:12:38 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E6=A0=B8=E5=8D=95=E5=8E=9F?= =?UTF-8?q?=E5=9E=8B=E6=B5=81=E7=AE=A1=E7=90=86=E5=90=8E=E5=8F=B0=E6=8E=A5?= =?UTF-8?q?=E5=8F=A3=E5=8F=98=E6=9B=B4=E8=AF=B4=E6=98=8E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...5_5219_核单原型流迁移-修改接口-管理后台.md | 1074 +++++++++++++++++ 1 file changed, 1074 insertions(+) create mode 100644 changelogs-v2/2026-07/25_5219_核单原型流迁移-修改接口-管理后台.md diff --git a/changelogs-v2/2026-07/25_5219_核单原型流迁移-修改接口-管理后台.md b/changelogs-v2/2026-07/25_5219_核单原型流迁移-修改接口-管理后台.md new file mode 100644 index 0000000..9b3818a --- /dev/null +++ b/changelogs-v2/2026-07/25_5219_核单原型流迁移-修改接口-管理后台.md @@ -0,0 +1,1074 @@ +# 【修改接口·管理后台】核单原型流迁移 (#5219) + +> **PR**: #5243 | **服务**: hl-order-service-v3 | **更新时间**: 2026-07-25 16:08 + +## 1. 接口背景 + +核单流程从旧的“财务总览/优惠逐条确认后直接 Step6 提交”调整为原型流:先确认 8 个核单分类,再生成并确认主报账人报账表,再生成并确认单团核算表,最后完成核单。 + +## 2. 变更清单 + +| # | 接口 | 方法 | 路径 | 变更类型 | 说明 | +|---|------|------|------|----------|------| +| 1 | 查询八分类确认状态 | GET | `/v3/admin/order/{orderId}/settlement/category-checks` | 新增 | 返回 8 个核单分类的确认状态、行数和来源指纹 | +| 2 | 确认单个核单分类 | POST | `/v3/admin/order/{orderId}/settlement/category-checks/{category}/confirm` | 新增 | 用最近读取的分类指纹确认单个分类 | +| 3 | 查询主报账人报账表 | GET | `/v3/admin/order/{orderId}/settlement/reports/reimbursement` | 新增 | 查询主报账表当前快照 | +| 4 | 生成主报账人报账表 | POST | `/v3/admin/order/{orderId}/settlement/reports/reimbursement/generate` | 新增 | 8 分类全部确认后生成 | +| 5 | 确认主报账人报账表 | POST | `/v3/admin/order/{orderId}/settlement/reports/reimbursement/confirm` | 新增 | 确认转账、预支和签字单 | +| 6 | 查询单团核算表 | GET | `/v3/admin/order/{orderId}/settlement/reports/group` | 新增 | 查询单团核算表当前快照 | +| 7 | 生成单团核算表 | POST | `/v3/admin/order/{orderId}/settlement/reports/group/generate` | 新增 | 主报账表确认后生成 | +| 8 | 确认单团核算表 | POST | `/v3/admin/order/{orderId}/settlement/reports/group/confirm` | 新增 | 用最近读取的报告指纹确认 | +| 9 | 完成核单 | POST | `/v3/admin/order/{orderId}/settlement/finalize` | 新增 | 单团核算表确认后提交核单 | +| 10 | 查询核单应收财务总览 | GET | `/v3/admin/order/{orderId}/settlement/financial-overview` | 修改 | 仍保留查询,但不再返回确认状态/指纹 | +| 11 | 确认财务总览 | POST | `/v3/admin/order/{orderId}/settlement/financial-overview/confirm` | 删除 | 旧 POST 契约下线 | +| 12 | 确认单条优惠 | POST | `/v3/admin/order/{orderId}/settlement/financial-overview/discounts/{discountId}/confirm` | 删除 | 旧 POST 契约下线 | + +## 3. 接口详情 + +### 3.1 查询八分类确认状态 + +- **方法 + 路径**: `GET /v3/admin/order/{orderId}/settlement/category-checks` +- **接口名**: 查询原型八个核单分类确认状态 +- **认证**: 管理后台 JWT;房务角色不可访问 +- **幂等性**: 幂等,只读 +- **限流**: 无接口级特殊限流 + +**路径参数** + +| 字段 | 类型 | 必填 | 说明 | +|------|------|------|------| +| `orderId` | Long/String | 是 | 订单 ID,必须大于 0 | + +**请求体**: 无 + +**响应字段** + +| 字段 | 类型 | 说明 | +|------|------|------| +| `orderId` | String | 订单 ID | +| `allConfirmed` | Boolean | 8 个分类是否全部已确认 | +| `items` | Array | 分类确认项 | +| `items[].category` | String | 分类枚举,见 §6.1 | +| `items[].categoryName` | String | 分类中文名 | +| `items[].rowCount` | Integer | 当前分类参与核单的行数 | +| `items[].empty` | Boolean | 当前分类是否为空 | +| `items[].sourceFingerprint` | String | 当前分类来源事实 SHA-256 | +| `items[].confirmStatus` | String | `UNCONFIRMED` / `CONFIRMED` | +| `items[].confirmedBy` | String/null | 确认人 ID | +| `items[].confirmedByName` | String/null | 确认人名称 | +| `items[].confirmedAt` | String/null | 确认时间,未确认时为 null | + +**错误码** + +| code | 含义 | 触发场景 | +|------|------|----------| +| 400 | 参数校验失败 | `orderId <= 0` | +| 403 | 无访问权限 | 房务角色访问 | + +**典型成功示例** + +请求: + +```http +GET /v3/admin/order/1914050000000001/settlement/category-checks +Authorization: Bearer +``` + +响应: + +```json +{ + "code": 200, + "msg": "success", + "data": { + "orderId": "1914050000000001", + "allConfirmed": false, + "items": [ + { + "category": "HOTEL", + "categoryName": "住宿", + "rowCount": 2, + "empty": false, + "sourceFingerprint": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "confirmStatus": "CONFIRMED", + "confirmedBy": "10001", + "confirmedByName": "张三", + "confirmedAt": "2026-07-25T15:20:00" + }, + { + "category": "OTHER_EXPENSE", + "categoryName": "其他支出", + "rowCount": 0, + "empty": true, + "sourceFingerprint": "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", + "confirmStatus": "UNCONFIRMED", + "confirmedBy": null, + "confirmedByName": null, + "confirmedAt": null + } + ] + } +} +``` + +**边界示例** + +请求: + +```http +GET /v3/admin/order/1914050000000001/settlement/category-checks +Authorization: Bearer +``` + +响应: + +```json +{ + "code": 200, + "msg": "success", + "data": { + "orderId": "1914050000000001", + "allConfirmed": false, + "items": [ + { + "category": "MEAL", + "categoryName": "餐食", + "rowCount": 0, + "empty": true, + "sourceFingerprint": "cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc", + "confirmStatus": "UNCONFIRMED", + "confirmedBy": null, + "confirmedByName": null, + "confirmedAt": null + } + ] + } +} +``` + +**异常示例** + +请求: + +```http +GET /v3/admin/order/0/settlement/category-checks +Authorization: Bearer +``` + +响应: + +```json +{ + "code": 400, + "msg": "订单 ID 必须大于 0", + "data": null +} +``` + +**业务边界** + +- 适用:进入核单流程后读取 8 个分类状态。 +- 不适用:房务角色不可查看。 +- 特殊边界:空分类仍需要走显式确认,不能因为 `rowCount=0` 自动通过。 + +### 3.2 确认单个核单分类 + +- **方法 + 路径**: `POST /v3/admin/order/{orderId}/settlement/category-checks/{category}/confirm` +- **接口名**: 按最近读取指纹确认单个核单分类 +- **认证**: 管理后台 JWT;需要财务写权限;房务角色不可访问 +- **幂等性**: 非纯幂等;相同指纹重复确认不会改变来源事实 +- **限流**: 无接口级特殊限流 + +**路径参数** + +| 字段 | 类型 | 必填 | 说明 | +|------|------|------|------| +| `orderId` | Long/String | 是 | 订单 ID,必须大于 0 | +| `category` | String | 是 | 分类枚举,大小写不敏感,见 §6.1 | + +**请求体字段** + +| 字段 | 类型 | 必填 | 说明 | 校验规则 | +|------|------|------|------|----------| +| `expectedSourceFingerprint` | String | 是 | 最近读取的分类来源事实 SHA-256 | 64 位小写十六进制 | +| `confirmEmpty` | Boolean | 是 | 是否明确确认空分类 | 空分类传 `true`,非空分类传 `false` | + +**响应字段**: 同 §3.1 `items[]` 单项。 + +**错误码** + +| code | 含义 | 触发场景 | +|------|------|----------| +| 400 | 参数校验失败 | 指纹不是 64 位十六进制、缺少 `confirmEmpty` | +| 584315 | 核单来源数据已变化,请刷新后重新生成 | 前端传入的指纹与当前分类来源不一致 | +| 584318 | 空分类必须显式确认,非空分类不得按空分类确认 | `confirmEmpty` 与当前分类是否为空不匹配 | +| 584319 | 核单存在未知分类或历史迁移数据不完整 | `category` 不是 8 分类枚举 | + +**典型成功示例** + +请求: + +```http +POST /v3/admin/order/1914050000000001/settlement/category-checks/HOTEL/confirm +Authorization: Bearer +Content-Type: application/json + +{ + "expectedSourceFingerprint": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "confirmEmpty": false +} +``` + +响应: + +```json +{ + "code": 200, + "msg": "success", + "data": { + "category": "HOTEL", + "categoryName": "住宿", + "rowCount": 2, + "empty": false, + "sourceFingerprint": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "confirmStatus": "CONFIRMED", + "confirmedBy": "10001", + "confirmedByName": "张三", + "confirmedAt": "2026-07-25T15:24:00" + } +} +``` + +**边界示例** + +请求: + +```http +POST /v3/admin/order/1914050000000001/settlement/category-checks/MEAL/confirm +Authorization: Bearer +Content-Type: application/json + +{ + "expectedSourceFingerprint": "cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc", + "confirmEmpty": true +} +``` + +响应: + +```json +{ + "code": 200, + "msg": "success", + "data": { + "category": "MEAL", + "categoryName": "餐食", + "rowCount": 0, + "empty": true, + "sourceFingerprint": "cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc", + "confirmStatus": "CONFIRMED", + "confirmedBy": "10001", + "confirmedByName": "张三", + "confirmedAt": "2026-07-25T15:25:00" + } +} +``` + +**异常示例** + +请求: + +```http +POST /v3/admin/order/1914050000000001/settlement/category-checks/UNKNOWN/confirm +Authorization: Bearer +Content-Type: application/json + +{ + "expectedSourceFingerprint": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "confirmEmpty": false +} +``` + +响应: + +```json +{ + "code": 584319, + "msg": "核单存在未知分类或历史迁移数据不完整", + "data": null +} +``` + +**业务边界** + +- 适用:确认当前分类数据没有变化。 +- 不适用:前端未先读取 `sourceFingerprint` 时不应直接确认。 +- 特殊边界:`OTHER_INCOME` 确认前会校验其他收入可提交状态。 + +### 3.3 主报账人报账表 + +- **查询**: `GET /v3/admin/order/{orderId}/settlement/reports/reimbursement` +- **生成**: `POST /v3/admin/order/{orderId}/settlement/reports/reimbursement/generate` +- **确认**: `POST /v3/admin/order/{orderId}/settlement/reports/reimbursement/confirm` +- **接口名**: 查询主报账人报账表 / 八分类确认后生成主报账人报账表 / 完成转账、预支及签字门禁后确认主报账人报账表 +- **认证**: 管理后台 JWT;房务角色不可访问 +- **幂等性**: 查询幂等;生成/确认会更新报告状态和确认信息 +- **限流**: 无接口级特殊限流 + +**路径参数** + +| 字段 | 类型 | 必填 | 说明 | +|------|------|------|------| +| `orderId` | Long/String | 是 | 订单 ID,必须大于 0 | + +**确认请求体字段** + +| 字段 | 类型 | 必填 | 说明 | 校验规则 | +|------|------|------|------|----------| +| `expectedSourceFingerprint` | String | 是 | 最近读取的主报账表来源 SHA-256 | 64 位小写十六进制 | +| `transferStatus` | String | 否 | 转账状态 | 后端按字符串保存 | +| `transferDate` | String/date | 否 | 转账日期 | `YYYY-MM-DD` | +| `transferRef` | String | 否 | 转账流水/备注 | 字符串 | +| `advanceSettledFlag` | Boolean | 是 | 预支是否已结清 | 不能为空 | +| `signedVoucher` | Object | 是 | 签字单回收信息 | 不能为空 | +| `signedVoucher.files` | Array | 否 | 文件列表 | 元素见下 | +| `signedVoucher.files[].name` | String | 否 | 文件名 | 字符串 | +| `signedVoucher.files[].url` | String | 否 | OSS 文件地址 | 字符串 | +| `signedVoucher.note` | String | 否 | 备注 | 字符串 | + +**响应字段** + +| 字段 | 类型 | 说明 | +|------|------|------| +| `id` | String/null | 主报账记录 ID;未生成时可为 null | +| `orderId` | String | 订单 ID | +| `reportStatus` | String | 报告状态,见 §6.2 | +| `sourceFingerprint` | String | 当前来源 SHA-256 | +| `primaryReporterId` | String/null | 主报账人 ID | +| `primaryReporterName` | String/null | 主报账人姓名 | +| `primaryReporterRole` | String/null | 主报账人角色 | +| `primaryReporterCollectedAmount` | Decimal | 主报账人代收金额 | +| `publicPrepaidAmount` | Decimal | 公共预支金额 | +| `primaryReporterDueAmount` | Decimal | 主报账人应报账金额 | +| `advanceOutstandingAmount` | Decimal | 未结清预支金额 | +| `reconNetAmount` | Decimal | 报账净额 | +| `transferDirection` | String/null | 转账方向 | +| `transferAmount` | Decimal | 转账金额 | +| `incomeLines` | Array | 收入明细行 | +| `expenseLines` | Array | 支出明细行 | +| `advanceLines` | Array | 预支明细行 | +| `transferStatus` | String/null | 转账状态 | +| `transferDate` | String/date/null | 转账日期 | +| `transferRef` | String/null | 转账流水/备注 | +| `advanceSettledFlag` | Boolean/null | 预支是否已结清 | +| `signedVoucher` | Object/null | 签字单信息 | +| `generatedBy` / `generatedByName` / `generatedAt` | String/String/String | 生成信息 | +| `confirmedBy` / `confirmedByName` / `confirmedAt` | String/String/String | 确认信息 | + +**错误码** + +| code | 含义 | 触发场景 | +|------|------|----------| +| 584310 | 八个核单分类尚未全部确认或数据已变化 | 生成主报账表前 8 分类未全部确认或指纹已失效 | +| 584311 | 主报账表尚未生成 | 查询/确认时还没有可用主报账表 | +| 584315 | 核单来源数据已变化,请刷新后重新生成 | 确认时传入的报告指纹已过期 | +| 584316 | 核单报告发生并发变化,请刷新后重试 | 并发确认冲突 | +| 584317 | 当前报告状态不允许执行该操作 | 当前状态不能生成或确认 | + +**典型成功示例** + +请求: + +```http +POST /v3/admin/order/1914050000000001/settlement/reports/reimbursement/confirm +Authorization: Bearer +Content-Type: application/json + +{ + "expectedSourceFingerprint": "dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd", + "transferStatus": "TRANSFERRED", + "transferDate": "2026-07-25", + "transferRef": "BANK-20260725-001", + "advanceSettledFlag": true, + "signedVoucher": { + "files": [ + { + "name": "签字单.pdf", + "url": "https://oss.example.com/voucher.pdf" + } + ], + "note": "签字单已回收" + } +} +``` + +响应: + +```json +{ + "code": 200, + "msg": "success", + "data": { + "id": "9200000000001", + "orderId": "1914050000000001", + "reportStatus": "CONFIRMED", + "sourceFingerprint": "dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd", + "primaryReporterId": "3001", + "primaryReporterName": "王司机", + "primaryReporterRole": "DRIVER", + "primaryReporterCollectedAmount": 2000.00, + "publicPrepaidAmount": 500.00, + "primaryReporterDueAmount": 1500.00, + "advanceOutstandingAmount": 0.00, + "reconNetAmount": 1500.00, + "transferDirection": "COMPANY_TO_REPORTER", + "transferAmount": 1500.00, + "incomeLines": [], + "expenseLines": [], + "advanceLines": [], + "transferStatus": "TRANSFERRED", + "transferDate": "2026-07-25", + "transferRef": "BANK-20260725-001", + "advanceSettledFlag": true, + "signedVoucher": { + "files": [ + { + "name": "签字单.pdf", + "url": "https://oss.example.com/voucher.pdf" + } + ], + "note": "签字单已回收" + }, + "generatedBy": "10001", + "generatedByName": "张三", + "generatedAt": "2026-07-25T15:30:00", + "confirmedBy": "10001", + "confirmedByName": "张三", + "confirmedAt": "2026-07-25T15:35:00" + } +} +``` + +**边界示例** + +请求: + +```http +GET /v3/admin/order/1914050000000001/settlement/reports/reimbursement +Authorization: Bearer +``` + +响应: + +```json +{ + "code": 200, + "msg": "success", + "data": { + "id": "9200000000001", + "orderId": "1914050000000001", + "reportStatus": "STALE", + "sourceFingerprint": "eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee", + "primaryReporterId": null, + "primaryReporterName": null, + "primaryReporterRole": null, + "primaryReporterCollectedAmount": 0.00, + "publicPrepaidAmount": 0.00, + "primaryReporterDueAmount": 0.00, + "advanceOutstandingAmount": 0.00, + "reconNetAmount": 0.00, + "transferDirection": null, + "transferAmount": 0.00, + "incomeLines": [], + "expenseLines": [], + "advanceLines": [], + "transferStatus": null, + "transferDate": null, + "transferRef": null, + "advanceSettledFlag": null, + "signedVoucher": null, + "generatedBy": "10001", + "generatedByName": "张三", + "generatedAt": "2026-07-25T15:30:00", + "confirmedBy": null, + "confirmedByName": null, + "confirmedAt": null + } +} +``` + +**异常示例** + +请求: + +```http +POST /v3/admin/order/1914050000000001/settlement/reports/reimbursement/generate +Authorization: Bearer +``` + +响应: + +```json +{ + "code": 584310, + "msg": "八个核单分类尚未全部确认或数据已变化", + "data": null +} +``` + +**业务边界** + +- 适用:8 分类全部确认后生成主报账表。 +- 不适用:8 分类未确认完成时不能生成。 +- 特殊边界:查询返回 `STALE` 时表示来源已变化,应重新生成后再确认。 + +### 3.4 单团核算表 + +- **查询**: `GET /v3/admin/order/{orderId}/settlement/reports/group` +- **生成**: `POST /v3/admin/order/{orderId}/settlement/reports/group/generate` +- **确认**: `POST /v3/admin/order/{orderId}/settlement/reports/group/confirm` +- **接口名**: 查询单团核算表 / 主报账表确认后生成单团核算表 / 按最近读取指纹确认单团核算表 +- **认证**: 管理后台 JWT;房务角色不可访问 +- **幂等性**: 查询幂等;生成/确认会更新报告状态和确认信息 +- **限流**: 无接口级特殊限流 + +**路径参数** + +| 字段 | 类型 | 必填 | 说明 | +|------|------|------|------| +| `orderId` | Long/String | 是 | 订单 ID,必须大于 0 | + +**确认请求体字段** + +| 字段 | 类型 | 必填 | 说明 | 校验规则 | +|------|------|------|------|----------| +| `expectedSourceFingerprint` | String | 是 | 最近读取的单团核算表来源 SHA-256 | 64 位小写十六进制 | + +**响应字段** + +| 字段 | 类型 | 说明 | +|------|------|------| +| `id` | String/null | 单团核算记录 ID | +| `orderId` | String | 订单 ID | +| `reportStatus` | String | 报告状态,见 §6.2 | +| `sourceFingerprint` | String | 当前来源 SHA-256 | +| `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 | 毛利率 | +| `travelerCount` | Integer | 出行人数 | +| `perCapitaRevenue` | Decimal | 人均收入 | +| `perCapitaCost` | Decimal | 人均成本 | +| `perCapitaProfit` | Decimal | 人均利润 | +| `incomeLines` | Array | 收入明细行 | +| `costCategories` | Array | 成本分类行 | +| `generatedBy` / `generatedByName` / `generatedAt` | String/String/String | 生成信息 | +| `confirmedBy` / `confirmedByName` / `confirmedAt` | String/String/String | 确认信息 | + +**错误码** + +| code | 含义 | 触发场景 | +|------|------|----------| +| 584312 | 主报账表尚未确认或数据已变化 | 生成单团核算表前主报账表未确认或已过期 | +| 584313 | 单团核算表尚未生成 | 查询/确认时还没有可用单团核算表 | +| 584315 | 核单来源数据已变化,请刷新后重新生成 | 确认时传入的报告指纹已过期 | +| 584316 | 核单报告发生并发变化,请刷新后重试 | 并发确认冲突 | +| 584317 | 当前报告状态不允许执行该操作 | 当前状态不能生成或确认 | + +**典型成功示例** + +请求: + +```http +POST /v3/admin/order/1914050000000001/settlement/reports/group/confirm +Authorization: Bearer +Content-Type: application/json + +{ + "expectedSourceFingerprint": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" +} +``` + +响应: + +```json +{ + "code": 200, + "msg": "success", + "data": { + "id": "9300000000001", + "orderId": "1914050000000001", + "reportStatus": "CONFIRMED", + "sourceFingerprint": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "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.521600, + "travelerCount": 5, + "perCapitaRevenue": 5000.00, + "perCapitaCost": 2392.00, + "perCapitaProfit": 2608.00, + "incomeLines": [], + "costCategories": [], + "generatedBy": "10001", + "generatedByName": "张三", + "generatedAt": "2026-07-25T15:40:00", + "confirmedBy": "10001", + "confirmedByName": "张三", + "confirmedAt": "2026-07-25T15:45:00" + } +} +``` + +**边界示例** + +请求: + +```http +GET /v3/admin/order/1914050000000001/settlement/reports/group +Authorization: Bearer +``` + +响应: + +```json +{ + "code": 200, + "msg": "success", + "data": { + "id": "9300000000001", + "orderId": "1914050000000001", + "reportStatus": "GENERATED", + "sourceFingerprint": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "baseOrderAmount": 0.00, + "otherIncomeAmount": 0.00, + "discountAmount": 0.00, + "adjustedReceivableAmount": 0.00, + "paidAmount": 0.00, + "actualRefundedAmount": 0.00, + "netRevenueAmount": 0.00, + "netReceivedAmount": 0.00, + "outstandingAmount": 0.00, + "hotelCost": 0.00, + "ticketCost": 0.00, + "mealCost": 0.00, + "vehicleCost": 0.00, + "guideCost": 0.00, + "photographerCost": 0.00, + "otherExpenseCost": 0.00, + "insurancePremium": 0.00, + "totalCost": 0.00, + "paidCost": 0.00, + "unpaidCost": 0.00, + "grossProfit": 0.00, + "grossProfitRate": 0.000000, + "travelerCount": 0, + "perCapitaRevenue": 0.00, + "perCapitaCost": 0.00, + "perCapitaProfit": 0.00, + "incomeLines": [], + "costCategories": [], + "generatedBy": "10001", + "generatedByName": "张三", + "generatedAt": "2026-07-25T15:40:00", + "confirmedBy": null, + "confirmedByName": null, + "confirmedAt": null + } +} +``` + +**异常示例** + +请求: + +```http +POST /v3/admin/order/1914050000000001/settlement/reports/group/generate +Authorization: Bearer +``` + +响应: + +```json +{ + "code": 584312, + "msg": "主报账表尚未确认或数据已变化", + "data": null +} +``` + +**业务边界** + +- 适用:主报账表已确认后生成单团核算表。 +- 不适用:主报账表未确认或为 `STALE` 时不能生成。 +- 特殊边界:金额为 0 时仍返回完整字段,数组字段为空数组。 + +### 3.5 完成核单 + +- **方法 + 路径**: `POST /v3/admin/order/{orderId}/settlement/finalize` +- **接口名**: 单团核算表确认后完成核单 +- **认证**: 管理后台 JWT;房务角色不可访问 +- **幂等性**: 非幂等;完成后订单进入核单提交后的状态 +- **限流**: 无接口级特殊限流 + +**路径参数** + +| 字段 | 类型 | 必填 | 说明 | +|------|------|------|------| +| `orderId` | Long/String | 是 | 订单 ID,必须大于 0 | + +**请求体**: 无 + +**响应字段** + +| 字段 | 类型 | 说明 | +|------|------|------| +| `summaryId` | String | 新写入的 settlement summary 主键 | +| `orderId` | String | 订单 ID | +| `settledAt` | String | 核单完成时间 | +| `totalAmount` | Decimal | 订单总金额快照 | +| `paidAmount` | Decimal | 已付金额快照 | +| `balanceAmount` | Decimal | 尾款金额快照 | +| `roomCost` | Decimal | 住宿实际成本 | +| `ticketCost` | Decimal | 门票实际成本 | +| `staffCost` | Decimal | 人员费用实际成本 | +| `subsidyCost` | Decimal | 补助实际成本 | +| `mealCost` | Decimal | 餐食实际成本 | +| `otherExpenseCost` | Decimal | 其他支出实际成本,含车辆费用 | +| `insurancePremium` | Decimal | 保险实际保费 | +| `totalActualCost` | Decimal | 总实际成本 | +| `driverTransferAmount` | Decimal | 给司机/主报账人转回金额 | +| `profitAmount` | Decimal | 公司毛利 | +| `profitRate` | Decimal | 毛利率 | +| `orderStatusAfter` | String | 结算后订单状态 | +| `mqTriggered` | Boolean | 结算事件是否成功触发 | +| `warnings` | Array | 软预警列表 | + +**错误码** + +| code | 含义 | 触发场景 | +|------|------|----------| +| 584314 | 单团核算表尚未确认或数据已变化 | finalize 前单团核算表未确认或已过期 | +| 584317 | 当前报告状态不允许执行该操作 | 当前流程状态不能完成核单 | + +**典型成功示例** + +请求: + +```http +POST /v3/admin/order/1914050000000001/settlement/finalize +Authorization: Bearer +``` + +响应: + +```json +{ + "code": 200, + "msg": "success", + "data": { + "summaryId": "9400000000001", + "orderId": "1914050000000001", + "settledAt": "2026-07-25T15:50: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, + "otherExpenseCost": 1560.00, + "insurancePremium": 180.00, + "totalActualCost": 11960.00, + "driverTransferAmount": 11780.00, + "profitAmount": 13040.00, + "profitRate": 0.5216, + "orderStatusAfter": "待财务复核", + "mqTriggered": true, + "warnings": [] + } +} +``` + +**边界示例** + +请求: + +```http +POST /v3/admin/order/1914050000000001/settlement/finalize +Authorization: Bearer +``` + +响应: + +```json +{ + "code": 200, + "msg": "success", + "data": { + "summaryId": "9400000000002", + "orderId": "1914050000000001", + "settledAt": "2026-07-25T15:55:00", + "totalAmount": 0.00, + "paidAmount": 0.00, + "balanceAmount": 0.00, + "roomCost": 0.00, + "ticketCost": 0.00, + "staffCost": 0.00, + "subsidyCost": 0.00, + "mealCost": 0.00, + "otherExpenseCost": 0.00, + "insurancePremium": 0.00, + "totalActualCost": 0.00, + "driverTransferAmount": 0.00, + "profitAmount": 0.00, + "profitRate": 0, + "orderStatusAfter": "待财务复核", + "mqTriggered": true, + "warnings": ["住宿 D2 现付缺凭证"] + } +} +``` + +**异常示例** + +请求: + +```http +POST /v3/admin/order/1914050000000001/settlement/finalize +Authorization: Bearer +``` + +响应: + +```json +{ + "code": 584314, + "msg": "单团核算表尚未确认或数据已变化", + "data": null +} +``` + +**业务边界** + +- 适用:单团核算表已确认后完成核单。 +- 不适用:单团核算表未确认、已过期或流程状态不允许。 +- 特殊边界:`warnings` 为软预警,不阻塞成功响应。 + +### 3.6 查询核单应收财务总览 + +- **方法 + 路径**: `GET /v3/admin/order/{orderId}/settlement/financial-overview` +- **接口名**: 查询核单应收财务总览 +- **变更点**: 查询接口保留,但返回字段删除旧确认状态/指纹语义;确认动作迁移到 8 分类和报告流。 + +**响应字段** + +| 字段 | 类型 | 说明 | +|------|------|------| +| `baseOrderAmount` | Decimal | 订单基础金额 | +| `otherIncomeAmount` | Decimal | 有效增费合计 | +| `discountAmount` | Decimal | 有效优惠合计 | +| `adjustedReceivableAmount` | Decimal | 调整后应收 | +| `onlinePaidAmount` | Decimal | 成功线上支付合计 | +| `offlinePaidAmount` | Decimal | 有效线下收款合计 | +| `primaryReporterCollectedAmount` | Decimal | 当前主报账司机正式代收 | +| `paidAmount` | Decimal | 已收合计 | +| `actualRefundedAmount` | Decimal | 实际退款合计 | +| `netPaidAmount` | Decimal | 净已收 | +| `outstandingAmount` | Decimal | 待收 | +| `surchargeMirrorMatched` | Boolean | 增费镜像是否匹配权威明细 | +| `discountMirrorMatched` | Boolean | 优惠镜像是否匹配权威明细 | +| `paidMirrorMatched` | Boolean | 已收镜像是否匹配权威明细 | +| `refundedMirrorMatched` | Boolean | 退款镜像是否匹配权威明细 | +| `discounts` | Array | 当前有效优惠 | +| `discounts[].discountId` | String | 优惠 ID | +| `discounts[].name` | String | 优惠名称 | +| `discounts[].type` | String | 优惠类型 | +| `discounts[].amount` | Decimal | 优惠金额 | + +**示例** + +请求: + +```http +GET /v3/admin/order/1914050000000001/settlement/financial-overview +Authorization: Bearer +``` + +响应: + +```json +{ + "code": 200, + "msg": "success", + "data": { + "baseOrderAmount": 24800.00, + "otherIncomeAmount": 500.00, + "discountAmount": 300.00, + "adjustedReceivableAmount": 25000.00, + "onlinePaidAmount": 22000.00, + "offlinePaidAmount": 3000.00, + "primaryReporterCollectedAmount": 2000.00, + "paidAmount": 25000.00, + "actualRefundedAmount": 0.00, + "netPaidAmount": 25000.00, + "outstandingAmount": 0.00, + "surchargeMirrorMatched": true, + "discountMirrorMatched": true, + "paidMirrorMatched": true, + "refundedMirrorMatched": true, + "discounts": [ + { + "discountId": "9100000000001", + "name": "老客优惠", + "type": "CUSTOMER_DISCOUNT", + "amount": 300.00 + } + ] + } +} +``` + +## 6. 枚举 / 数据字典 + +### 6.1 `category`(SettlementCategory) + +**所属字段**: 路径参数 `category`、响应 `items[].category` | **类型**: String + +| 值 | 中文 | 说明 | +|----|------|------| +| `HOTEL` | 住宿 | 住宿核单分类 | +| `TICKET` | 门票/游玩项目 | 门票及游玩项目核单分类 | +| `MEAL` | 餐食 | 餐食核单分类 | +| `VEHICLE` | 车辆 | 车辆费用核单分类 | +| `GUIDE` | 导游 | 导游费用核单分类 | +| `PHOTOGRAPHER` | 摄影 | 摄影费用核单分类 | +| `OTHER_INCOME` | 其他收入 | 其他收入核单分类 | +| `OTHER_EXPENSE` | 其他支出 | 其他支出核单分类 | + +### 6.2 `reportStatus`(SettlementReportStatus) + +**所属字段**: `reportStatus` | **类型**: String + +| 值 | 中文 | 说明 | +|----|------|------| +| `GENERATED` | 已生成 | 报告已生成,尚未确认 | +| `CONFIRMED` | 已确认 | 报告已确认 | +| `STALE` | 已过期 | 来源事实已变化,需要重新生成 | + +### 6.3 `confirmStatus` + +**所属字段**: `items[].confirmStatus` | **类型**: String + +| 值 | 中文 | 说明 | +|----|------|------| +| `UNCONFIRMED` | 未确认 | 分类尚未确认 | +| `CONFIRMED` | 已确认 | 分类已确认 | + +## 7. 错误码汇总 + +| code | 含义 | 触发场景 | +|------|------|----------| +| 584310 | 八个核单分类尚未全部确认或数据已变化 | 生成主报账表前门禁失败 | +| 584311 | 主报账表尚未生成 | 主报账表查询/确认前置缺失 | +| 584312 | 主报账表尚未确认或数据已变化 | 生成单团核算表前门禁失败 | +| 584313 | 单团核算表尚未生成 | 单团核算表查询/确认前置缺失 | +| 584314 | 单团核算表尚未确认或数据已变化 | finalize 前门禁失败 | +| 584315 | 核单来源数据已变化,请刷新后重新生成 | 分类或报告指纹过期 | +| 584316 | 核单报告发生并发变化,请刷新后重试 | 并发确认冲突 | +| 584317 | 当前报告状态不允许执行该操作 | 当前状态不能执行生成/确认/finalize | +| 584318 | 空分类必须显式确认,非空分类不得按空分类确认 | `confirmEmpty` 与分类是否为空不匹配 | +| 584319 | 核单存在未知分类或历史迁移数据不完整 | 分类枚举非法或历史数据缺失 | + +## 10. 修改前后对比 + +### 10.1 字段级对比 + +| 接口/字段 | 改前 | 改后 | +|-----------|------|------| +| `GET /settlement/financial-overview` | 返回应收总览及逐条优惠确认状态 | 只返回应收总览和优惠明细,不再承载确认流 | +| `POST /settlement/financial-overview/confirm` 请求体 | `expectedOverviewFingerprint` | 接口删除,改用 `POST /settlement/category-checks/{category}/confirm` 的 `expectedSourceFingerprint` + `confirmEmpty` | +| `POST /settlement/financial-overview/discounts/{discountId}/confirm` 请求体 | `expectedSourceFingerprint` | 接口删除,优惠纳入 8 分类/报告流 | +| 主报账表 | 无独立响应结构 | 新增 `SettlementReimbursementReportRespVO` | +| 单团核算表 | 无独立响应结构 | 新增 `SettlementGroupReportRespVO` | +| 完成核单 | 旧入口为 `POST /settlement/step6/submit` | 新增原型流入口 `POST /settlement/finalize`,返回 `SettlementSubmitRespVO` | + +### 10.2 行为级对比 + +| 行为 | 改前 | 改后 | +|------|------|------| +| 核单确认门禁 | 财务总览/优惠逐条确认 | 8 分类全部确认 | +| 报账表 | 无独立生成/确认步骤 | 先生成并确认主报账人报账表 | +| 单团核算 | 无独立生成/确认步骤 | 主报账表确认后生成并确认单团核算表 | +| 最终提交 | 直接 Step6 提交 | 单团核算表确认后调用 finalize | +| 旧 POST 接口 | 可调用 | 下线,调用方需迁移 | + +## 11. 影响评估 / 回滚 + +### 11.1 影响评估 + +- **是否破坏向后兼容**: 是。两个旧 POST 确认接口删除;财务总览查询响应语义收窄。 +- **前端是否必须同步上线**: 是。核单页面需要按 8 分类确认、主报账表、单团核算表、finalize 的顺序对接。 +- **影响已有数据**: 前端接口层不需要处理数据库迁移细节;历史订单在接口层按当前数据计算状态。 + +### 11.2 回滚方案 + +- **回滚方式**: 回滚 PR #5243 后恢复旧确认流。 +- **回滚后清理**: 前端需恢复旧 `financial-overview/confirm` 和 `discounts/{discountId}/confirm` 调用。 +- **回滚耗时**: 取决于后端回滚与重新部署;前端接口调用需同步回退。 + +## 12. 注意事项 + +- 前端不要继续调用已删除的两个旧 POST 确认接口。 +- 8 分类确认必须使用刚查询到的 `sourceFingerprint`,报告确认必须使用刚查询/生成返回的 `sourceFingerprint`。 +- `STALE` 表示来源已变化,不能继续确认。 +- 空分类确认必须传 `confirmEmpty=true`;非空分类必须传 `false`。 + +## 13. 关联 / 联系人 + +### 13.1 链接 + +- **Issue**: [#5219](https://git.1814.love:8443/wx/HL/issues/5219) +- **PR**: [#5243](https://git.1814.love:8443/wx/HL/pulls/5243) +- **Merge commit**: [f64606c](https://git.1814.love:8443/wx/HL/commit/f64606c5cc65b52880aaa8985c61e62e0b90e0ea) + +### 13.2 联系人 + +- **后端负责人**: @yst