---
frontend_status: "implemented"
frontend_owner: "hl-ui-codex"
frontend_ref: "mmg/hl-ui@00ea8e2ad187b6b91626f069fc3a4a5ba5a76e06"
updated_at: "2026-07-25T08:37:46.681Z"
---
# 【修改接口·管理后台】核单原型流迁移 (#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 <token>
```

响应：

```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 <token>
```

响应：

```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 <token>
```

响应：

```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 <token>
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 <token>
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 <token>
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<Object> | 收入明细行 |
| `expenseLines` | Array<Object> | 支出明细行 |
| `advanceLines` | Array<Object> | 预支明细行 |
| `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 <token>
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 <token>
```

响应：

```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 <token>
```

响应：

```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<Object> | 收入明细行 |
| `costCategories` | Array<Object> | 成本分类行 |
| `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 <token>
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 <token>
```

响应：

```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 <token>
```

响应：

```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<String> | 软预警列表 |

**错误码**

| code | 含义 | 触发场景 |
|------|------|----------|
| 584314 | 单团核算表尚未确认或数据已变化 | finalize 前单团核算表未确认或已过期 |
| 584317 | 当前报告状态不允许执行该操作 | 当前流程状态不能完成核单 |

**典型成功示例**

请求：

```http
POST /v3/admin/order/1914050000000001/settlement/finalize
Authorization: Bearer <token>
```

响应：

```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 <token>
```

响应：

```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 <token>
```

响应：

```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 <token>
```

响应：

```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
