删除旧核单兼容接口并补充前端迁移说明
一些检查失败了
changelog-filename-gate / validate (push) Failing after 1s

这个提交包含在:
yaosutu 2026-07-28 18:10:53 +08:00
父节点 a3ee62d00d
当前提交 bb0cdc6e80

查看文件

@ -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<SettlementReimbursementReportRespVO>`,完整字段见 §5.2。
- **关键规则**:前端必须保存本次响应的 `data.sourceFingerprint`;确认请求不得使用缓存的旧指纹。
**请求示例**
```http
GET /v3/admin/order/1914050000000001/settlement/reports/reimbursement
Authorization: Bearer <token>
```
**响应示例**
```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 <token>
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<SettlementGroupReportRespVO>`,完整字段见 §5.3。
- **关键规则**:前端必须保存本次响应的 `data.sourceFingerprint`;确认请求不得使用缓存的旧指纹。
**请求示例**
```http
GET /v3/admin/order/1914050000000001/settlement/reports/group
Authorization: Bearer <token>
```
**响应示例**
```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 <token>
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<SettlementSubmitRespVO>`,完整字段见 §5.4。
**请求示例**
```http
POST /v3/admin/order/1914050000000001/settlement/finalize
Authorization: Bearer <token>
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<Object> | 司机代收尾款明细 |
| `expenseLines` | Array<Object> | 主报账人现金支付成本明细 |
| `advanceLines` | Array<Object> | 已审批预支明细 |
| `vehicleLines` | Array<Object> | 车辆逐日明细;允许空数组 |
| `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<Object> | 收入构成;元素字段为 `type``amount` |
| `costCategories` | Array<Object> | 成本构成;元素字段为 `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<String> | 软提示列表;不阻塞成功结果 |
## 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 <token>
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 <token>
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 <token>
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
- **前端负责人**: 待认领