---
schema: "hl-changelog/v2"
ticket: "5325"
title: "核单人员费用分 Tab"
consumer: "admin"
change_type: "修改接口"
backend_status: "deployed"
gateway_status: "verified"
frontend_status: "implemented"
frontend_owner: "hl-ui-pi"
frontend_ref: "mmg/hl-ui@4cea39f7e87e74f42fb28ab260da12b585f034de"
target_release: ""
verified_at: ""
status_note: ""
updated_at: "2026-07-29"
base: "dev-v3"
---

# ⚠️【修改接口·管理后台】核单人员费用分 Tab（#5325）

> **PR**：#5332 ｜ **服务**：hl-order-service-v3 ｜ **更新时间**：2026-07-29

## 1. 接口背景

核单页面的领队、司机、导游、摄影师、其他人员是五个独立 Tab，需要分别加载、分别保存。原 `/settlement/step3` 把所有人员类型聚合在同一请求中，还要求调用方提交人员类型；车辆费用也曾通过 Order 管理端接口直接暴露。

本次将人员费用改为五组独立 GET/PUT。人员类型由接口路径唯一确定，保存请求不再接收人员类型；每次 PUT 只全量替换当前 Tab，成功响应只表达成功，不返回新增、修改或删除的数据 ID。旧 Step3 和两个 Order 管理端车辆费用接口直接删除，不保留兼容路由。

## 变更接口

本次对外契约由五组独立 GET/PUT 和四个删除路由组成，完整清单如下。

## 2. 变更清单

| # | 接口名 | 方法 | 路径 | 变更类型 | 说明 |
|---|--------|------|------|----------|------|
| 1 | 查询领队人员费用 | GET | `/v3/admin/order/:orderId/settlement/staff-fees/leaders` | 新增接口 | 仅返回领队 Tab |
| 2 | 保存领队人员费用 | PUT | `/v3/admin/order/:orderId/settlement/staff-fees/leaders` | 新增接口 | 路径固定为领队，全量替换领队 Tab |
| 3 | 查询司机人员费用 | GET | `/v3/admin/order/:orderId/settlement/staff-fees/drivers` | 新增接口 | 仅返回司机 Tab |
| 4 | 保存司机人员费用 | PUT | `/v3/admin/order/:orderId/settlement/staff-fees/drivers` | 新增接口 | 路径固定为司机，全量替换司机 Tab |
| 5 | 查询导游人员费用 | GET | `/v3/admin/order/:orderId/settlement/staff-fees/guides` | 新增接口 | 仅返回导游 Tab |
| 6 | 保存导游人员费用 | PUT | `/v3/admin/order/:orderId/settlement/staff-fees/guides` | 新增接口 | 路径固定为导游，全量替换导游 Tab |
| 7 | 查询摄影师人员费用 | GET | `/v3/admin/order/:orderId/settlement/staff-fees/photographers` | 新增接口 | 仅返回摄影师 Tab |
| 8 | 保存摄影师人员费用 | PUT | `/v3/admin/order/:orderId/settlement/staff-fees/photographers` | 新增接口 | 路径固定为摄影师，全量替换摄影师 Tab |
| 9 | 查询其他人员费用 | GET | `/v3/admin/order/:orderId/settlement/staff-fees/others` | 新增接口 | 仅返回其他人员 Tab |
| 10 | 保存其他人员费用 | PUT | `/v3/admin/order/:orderId/settlement/staff-fees/others` | 新增接口 | 路径固定为其他人员，全量替换其他人员 Tab |
| 11 | Step3 聚合查询 | GET | `/v3/admin/order/:orderId/settlement/step3` | 删除接口 | 不保留兼容 |
| 12 | Step3 聚合保存 | PUT | `/v3/admin/order/:orderId/settlement/step3` | 删除接口 | 不保留兼容 |
| 13 | 查询核单车辆总车费 | GET | `/v3/admin/order/:orderId/settlement/vehicle-fees` | 删除接口 | 管理后台不再直接调用 |
| 14 | 确认并冻结核单车辆总车费 | POST | `/v3/admin/order/:orderId/settlement/vehicle-fees/confirm` | 删除接口 | 管理后台不再直接调用 |

## 3. 接口详情

五组接口均使用管理后台登录态，`orderId` 必须大于 0。GET 只返回路径所代表的人员类型；PUT 只替换路径所代表的 Tab，不影响另外四个 Tab。

### 3.1 领队 Tab

- **查询**：`GET /v3/admin/order/:orderId/settlement/staff-fees/leaders`
- **保存**：`PUT /v3/admin/order/:orderId/settlement/staff-fees/leaders`
- **人员类型**：路径固定为领队，PUT 不传 `staffRole`
- **保存语义**：全量替换领队 Tab；`items: []` 表示清空领队 Tab
- **人员引用**：每行 `staffId` 必填，必须属于当前订单的领队
- **费用明细**：

| `detail` 字段 | 类型 | 必填 | 说明 | 校验 |
|---------------|------|------|------|------|
| `days` | Integer | 是 | 服务天数 | `>= 0` |
| `per_day` | Decimal | 是 | 每天费用 | `>= 0` |

- **费用口径**：计划成本为 `days × per_day`；实际成本为 `days × per_day + reimburse`

### 3.2 司机 Tab

- **查询**：`GET /v3/admin/order/:orderId/settlement/staff-fees/drivers`
- **保存**：`PUT /v3/admin/order/:orderId/settlement/staff-fees/drivers`
- **人员类型**：路径固定为司机，PUT 不传 `staffRole`
- **保存语义**：全量替换司机 Tab；`items: []` 表示清空司机 Tab
- **人员引用**：每行 `staffId` 必填，必须属于当前订单的司机
- **费用明细**：

| `detail` 字段 | 类型 | 必填 | 说明 | 校验 |
|---------------|------|------|------|------|
| `days` | Array | 是 | 服务日明细 | 可为空数组 |
| `days[].service_date` | String(date) | 是 | 服务日期 | `YYYY-MM-DD` |
| `days[].vehicle_brief` | String | 否 | 车辆摘要 | 可空 |
| `days[].daily_fee` | Decimal | 是 | 日费，仅回显，不计入人员费用 | `>= 0` |
| `days[].is_used` | Boolean | 否 | 是否使用 | 可空 |
| `days[].note` | String | 否 | 服务日备注 | 可空 |
| `extra_cost` | Decimal | 否 | 司机额外费用 | 空按 0，且 `>= 0` |
| `extra_breakdown` | Array | 否 | 额外费用说明 | 各项金额合计必须等于 `extra_cost` |
| `extra_breakdown[].name` | String | 是 | 费用名称 | 非空 |
| `extra_breakdown[].amount` | Decimal | 是 | 金额 | `>= 0` |
| `extra_breakdown[].note` | String | 否 | 备注 | 可空 |

- **费用口径**：司机基础服务费不在人员费用中重复计算；计划成本为 0，实际成本为 `extra_cost + reimburse`

### 3.3 导游 Tab

- **查询**：`GET /v3/admin/order/:orderId/settlement/staff-fees/guides`
- **保存**：`PUT /v3/admin/order/:orderId/settlement/staff-fees/guides`
- **人员类型**：路径固定为导游，PUT 不传 `staffRole`
- **保存语义**：全量替换导游 Tab；`items: []` 表示清空导游 Tab
- **人员引用**：`staffId` 可空；非空时必须属于当前订单的导游，空值表示按 `persons[]` 保存聚合行
- **费用明细**：

| `detail` 字段 | 类型 | 必填 | 说明 | 校验 |
|---------------|------|------|------|------|
| `persons` | Array | 是 | 导游计费明细 | 可为空数组 |
| `persons[].name` | String | 是 | 姓名 | 非空 |
| `persons[].days` | Integer | 是 | 天数 | `>= 0` |
| `persons[].per_day` | Decimal | 是 | 每天费用 | `>= 0` |
| `persons[].note` | String | 否 | 备注 | 可空 |

- **费用口径**：计划成本为 `Σ(persons[].days × persons[].per_day)`；实际成本为计划成本加 `reimburse`

### 3.4 摄影师 Tab

- **查询**：`GET /v3/admin/order/:orderId/settlement/staff-fees/photographers`
- **保存**：`PUT /v3/admin/order/:orderId/settlement/staff-fees/photographers`
- **人员类型**：路径固定为摄影师，PUT 不传 `staffRole`
- **保存语义**：全量替换摄影师 Tab；`items: []` 表示清空摄影师 Tab
- **人员引用**：`staffId` 可空；非空时必须属于当前订单的摄影师，空值表示按 `persons[]` 保存聚合行
- **费用明细**：

| `detail` 字段 | 类型 | 必填 | 说明 | 校验 |
|---------------|------|------|------|------|
| `persons` | Array | 是 | 摄影师计费明细 | 可为空数组 |
| `persons[].name` | String | 是 | 姓名 | 非空 |
| `persons[].days` | Integer | 是 | 天数 | `>= 0` |
| `persons[].per_day` | Decimal | 是 | 每天费用 | `>= 0` |
| `persons[].note` | String | 否 | 备注 | 可空 |

- **费用口径**：计划成本为 `Σ(persons[].days × persons[].per_day)`；实际成本为计划成本加 `reimburse`

### 3.5 其他人员 Tab

- **查询**：`GET /v3/admin/order/:orderId/settlement/staff-fees/others`
- **保存**：`PUT /v3/admin/order/:orderId/settlement/staff-fees/others`
- **人员类型**：路径固定为其他人员，PUT 不传 `staffRole`
- **保存语义**：全量替换其他人员 Tab；`items: []` 表示清空其他人员 Tab
- **人员引用**：`staffId` 可空；非空时必须属于当前订单的其他人员，空值表示按 `detail.items[]` 保存聚合行
- **费用明细**：

| `detail` 字段 | 类型 | 必填 | 说明 | 校验 |
|---------------|------|------|------|------|
| `items` | Array | 是 | 其他人员费用项 | 可为空数组 |
| `items[].name` | String | 是 | 费用名称 | 非空 |
| `items[].amount` | Decimal | 是 | 金额 | `>= 0` |
| `items[].note` | String | 否 | 备注 | 可空 |

- **费用口径**：计划成本为 `Σ(detail.items[].amount)`；实际成本为计划成本加 `reimburse`

## 4. 接口入参

### 4.1 GET 路径参数

五个 GET 的路径参数相同。

| 字段 | 类型 | 必填 | 说明 | 校验 |
|------|------|------|------|------|
| `orderId` | String | 是 | 订单 ID | 正整数 |

GET 无 Query 参数、无请求体。

### 4.2 PUT 路径参数

五个 PUT 的路径参数相同。

| 字段 | 类型 | 必填 | 说明 | 校验 |
|------|------|------|------|------|
| `orderId` | String | 是 | 订单 ID | 正整数 |

### 4.3 PUT 请求体

| 字段 | 类型 | 必填 | 说明 | 校验 |
|------|------|------|------|------|
| `items` | Array | 是 | 当前 Tab 的完整人员费用行 | 空数组表示清空当前 Tab |

### 4.4 PUT `items[]` 通用字段

| 字段 | 类型 | 必填 | 说明 | 校验/默认值 |
|------|------|------|------|-------------|
| `staffId` | String | 条件必填 | 当前订单人员分配 ID | 领队、司机必填；其他三个 Tab 可空；非空时必须属于当前订单且角色与路径一致 |
| `detail` | Object | 是 | 当前 Tab 对应的角色明细 | 结构见 §3 |
| `reimburse` | Decimal | 否 | 小额报销 | 空按 0，且 `>= 0` |
| `paymentMethod` | String | 否 | 付款方式 | 空按 `COMPANY_PAID` |
| `voucherUrls` | String[] | 否 | 凭证 URL | 最多 9 个；每个非空、最长 1024 字符，仅支持 HTTP/HTTPS |
| `settleStatus` | String | 否 | 辅助人员结算状态 | 空按 `PENDING`；主报账人行不使用该字段 |
| `settledDate` | String(date) | 否 | 辅助人员结算日期 | `YYYY-MM-DD`；主报账人行不使用该字段 |
| `transferRef` | String | 条件必填 | 辅助人员结算转账流水号 | 最长 128 字符；辅助人员 `settleStatus=COMPLETED` 时必填；主报账人行不使用该字段 |
| `remark` | String | 否 | 备注 | 最长 500 字符 |

### 4.5 PUT 禁止提交的字段

请求体采用严格字段校验。以下字段属于路径确定项、服务端状态或查询回显，不得提交：

| 禁止字段 | 原因 |
|----------|------|
| `staffRole` | 人员类型由 `leaders/drivers/guides/photographers/others` 路径唯一确定 |
| `id` | 保存为全量替换，不按数据行 ID 执行新增或修改 |
| `staffName` | 查询回显字段 |
| `totalPlannedCost` | 查询回显字段 |
| `totalActualCost` | 查询回显字段 |
| `settlementConfirmStatus` | 核单确认状态不由 Tab 保存请求指定 |
| `isPrimaryReporter` | 查询回显字段 |

出现未知字段时请求失败，不会静默忽略。

## 5. 出参（响应）

### 5.1 统一响应外层

| 字段 | 类型 | 说明 |
|------|------|------|
| `code` | Integer | 业务码；成功为 200 |
| `message` | String | 结果说明 |
| `data` | Object/null | GET 为当前 Tab 数据；PUT 成功固定为 `null` |
| `traceId` | String/null | 链路追踪 ID |
| `success` | Boolean | `code=200` 时为 `true`，否则为 `false` |

### 5.2 五个 GET 的 `data`

| 字段 | 类型 | 说明 |
|------|------|------|
| `totalActualCost` | Decimal | 当前 Tab 实际费用合计 |
| `items` | Array | 当前 Tab 已保存行；没有已保存行时返回该角色候选草稿 |

### 5.3 GET `data.items[]`

| 字段 | 类型 | 可空 | 说明 |
|------|------|------|------|
| `id` | String | 是 | 已保存行 ID；候选草稿为 `null` |
| `staffId` | String | 是 | 人员分配 ID；聚合行可为 `null` |
| `staffName` | String | 是 | 人员姓名或聚合姓名摘要 |
| `detail` | Object | 否 | 当前 Tab 对应的角色明细，结构见 §3 |
| `totalPlannedCost` | Decimal | 否 | 计划成本 |
| `totalActualCost` | Decimal | 否 | 实际成本，已包含 `reimburse` |
| `reimburse` | Decimal | 否 | 小额报销 |
| `paymentMethod` | String | 否 | 付款方式 |
| `voucherUrls` | String[] | 否 | 凭证 URL；无凭证为 `[]` |
| `settlementConfirmStatus` | String | 否 | 人员费用核单确认状态 |
| `settleStatus` | String | 是 | 辅助人员结算状态；主报账人行返回 `null` |
| `settledDate` | String(date) | 是 | 辅助人员结算日期 |
| `transferRef` | String | 是 | 辅助人员结算转账流水号 |
| `isPrimaryReporter` | Boolean | 否 | 是否主报账人 |
| `remark` | String | 是 | 备注 |

ID 字段按字符串返回。

### 5.4 PUT 成功响应

五个 PUT 均为统一成功响应，不返回操作数据 ID：

```json
{
  "code": 200,
  "message": "成功",
  "data": null,
  "traceId": null,
  "success": true
}
```

## 6. 枚举 / 数据字典

### 6.1 `paymentMethod`

**所属字段**：PUT `items[].paymentMethod`、GET `data.items[].paymentMethod` ｜ **类型**：String ｜ **必填**：否

| 值 | 中文 | 说明 |
|----|------|------|
| `CASH_PAID` | 现金已付 | 现金支付 |
| `COMPANY_PAID` | 公司支付 | 请求不传时的默认值 |
| `SIGNED` | 签单 | 按签单方式结算 |

### 6.2 `settleStatus`

**所属字段**：PUT `items[].settleStatus`、GET `data.items[].settleStatus` ｜ **类型**：String ｜ **必填**：否

| 值 | 中文 | 说明 |
|----|------|------|
| `PENDING` | 待结算 | 辅助人员默认值 |
| `COMPLETED` | 已结算 | 辅助人员使用时必须同时填写 `transferRef` |

主报账人行的 `settleStatus` 为 `null`。

### 6.3 `settlementConfirmStatus`

**所属字段**：GET `data.items[].settlementConfirmStatus` ｜ **类型**：String ｜ **入参**：禁止提交

| 值 | 中文 | 说明 |
|----|------|------|
| `UNCONFIRMED` | 未确认 | Tab 保存后为未确认 |
| `CONFIRMED` | 已确认 | 人员费用已完成核单确认 |

### 6.4 人员类型与路径映射

人员类型不是请求字段，仅用于说明路径含义。

| 路径尾段 | 人员类型 | 中文 |
|----------|----------|------|
| `leaders` | `LEADER` | 领队 |
| `drivers` | `DRIVER` | 司机 |
| `guides` | `GUIDE` | 导游 |
| `photographers` | `PHOTOGRAPHER` | 摄影师 |
| `others` | `OTHER` | 其他人员 |

## 7. 错误码

| code | 含义 | 触发场景 |
|------|------|----------|
| `400` | 请求参数错误 | 提交 `staffRole`、`id`、`settlementConfirmStatus` 等未知/禁止字段，或字段格式、长度、枚举校验失败 |
| `404` | 接口不存在 | 继续调用已删除的 Step3 或 Order 管理端车辆费用接口 |
| `584020` | 订单不存在 | `orderId` 对应订单不存在 |
| `584021` | 当前核单状态不允许录人员费用 | PUT 时订单不是待核单或核单中 |
| `584023` | 司机明细非法 | `days[]` 缺失、服务日期/日费非法，或额外费用明细合计不等于 `extra_cost` |
| `584024` | 导游/摄影师明细非法 | `persons[]` 缺失，或姓名、天数、每天费用非法 |
| `584025` | 领队明细非法 | 缺少 `days` 或 `per_day`，或值小于 0 |
| `584026` | 人员实际费用非法 | 报销或折算后的实际费用小于 0 |
| `584027` | 人员引用无效 | `staffId` 不属于当前订单、角色与路径不一致，或领队/司机未传 `staffId` |
| `584028` | 其他人员明细非法 | `detail.items[]` 缺失，或名称、金额非法 |
| `584038` | 已结算但缺转账流水号 | 辅助人员 `settleStatus=COMPLETED` 且 `transferRef` 为空 |
| `584039` | 结算状态非法 | `settleStatus` 不是 `PENDING` 或 `COMPLETED` |
| `584080` | 团期共享科目不可在子订单录入 | 团期子订单保存非空领队或摄影师费用 |

## 8. 示例

以下示例中的订单 ID、人员 ID、行 ID 均为格式示例。

### 8.1 领队 Tab：典型 GET

**请求**：

```http
GET /v3/admin/order/2080000000000000001/settlement/staff-fees/leaders
Authorization: Bearer <token>
```

无请求体。

**响应**：

```json
{
  "code": 200,
  "message": "成功",
  "data": {
    "totalActualCost": 2450.00,
    "items": [
      {
        "id": "2081000000000000001",
        "staffId": "2082000000000000001",
        "staffName": "领队甲",
        "detail": {
          "days": 3,
          "per_day": 800.00
        },
        "totalPlannedCost": 2400.00,
        "totalActualCost": 2450.00,
        "reimburse": 50.00,
        "paymentMethod": "COMPANY_PAID",
        "voucherUrls": [],
        "settlementConfirmStatus": "UNCONFIRMED",
        "settleStatus": null,
        "settledDate": null,
        "transferRef": null,
        "isPrimaryReporter": true,
        "remark": "主报账人"
      }
    ]
  },
  "traceId": null,
  "success": true
}
```

### 8.2 领队 Tab：典型 PUT

**请求**：

```http
PUT /v3/admin/order/2080000000000000001/settlement/staff-fees/leaders
Authorization: Bearer <token>
Content-Type: application/json
```

```json
{
  "items": [
    {
      "staffId": "2082000000000000001",
      "detail": {
        "days": 3,
        "per_day": 800.00
      },
      "reimburse": 50.00,
      "paymentMethod": "COMPANY_PAID",
      "voucherUrls": [],
      "remark": "主报账人"
    }
  ]
}
```

**响应**：

```json
{
  "code": 200,
  "message": "成功",
  "data": null,
  "traceId": null,
  "success": true
}
```

### 8.3 司机 Tab：典型 GET

**请求**：

```http
GET /v3/admin/order/2080000000000000001/settlement/staff-fees/drivers
Authorization: Bearer <token>
```

无请求体。

**响应**：

```json
{
  "code": 200,
  "message": "成功",
  "data": {
    "totalActualCost": 120.00,
    "items": [
      {
        "id": "2081000000000000002",
        "staffId": "2082000000000000002",
        "staffName": "司机甲",
        "detail": {
          "days": [
            {
              "service_date": "2026-07-29",
              "vehicle_brief": "示例车辆",
              "daily_fee": 700.00,
              "is_used": true,
              "note": ""
            }
          ],
          "extra_cost": 100.00,
          "extra_breakdown": [
            {
              "name": "临时停车",
              "amount": 100.00,
              "note": ""
            }
          ]
        },
        "totalPlannedCost": 0,
        "totalActualCost": 120.00,
        "reimburse": 20.00,
        "paymentMethod": "COMPANY_PAID",
        "voucherUrls": [],
        "settlementConfirmStatus": "UNCONFIRMED",
        "settleStatus": "PENDING",
        "settledDate": null,
        "transferRef": null,
        "isPrimaryReporter": false,
        "remark": ""
      }
    ]
  },
  "traceId": null,
  "success": true
}
```

### 8.4 司机 Tab：典型 PUT

**请求**：

```http
PUT /v3/admin/order/2080000000000000001/settlement/staff-fees/drivers
Authorization: Bearer <token>
Content-Type: application/json
```

```json
{
  "items": [
    {
      "staffId": "2082000000000000002",
      "detail": {
        "days": [
          {
            "service_date": "2026-07-29",
            "vehicle_brief": "示例车辆",
            "daily_fee": 700.00,
            "is_used": true,
            "note": ""
          }
        ],
        "extra_cost": 100.00,
        "extra_breakdown": [
          {
            "name": "临时停车",
            "amount": 100.00,
            "note": ""
          }
        ]
      },
      "reimburse": 20.00,
      "paymentMethod": "COMPANY_PAID",
      "voucherUrls": [],
      "settleStatus": "PENDING",
      "settledDate": null,
      "transferRef": null,
      "remark": ""
    }
  ]
}
```

**响应**：

```json
{
  "code": 200,
  "message": "成功",
  "data": null,
  "traceId": null,
  "success": true
}
```

### 8.5 导游 Tab：典型 GET

**请求**：

```http
GET /v3/admin/order/2080000000000000001/settlement/staff-fees/guides
Authorization: Bearer <token>
```

无请求体。

**响应**：

```json
{
  "code": 200,
  "message": "成功",
  "data": {
    "totalActualCost": 1800.00,
    "items": [
      {
        "id": "2081000000000000003",
        "staffId": null,
        "staffName": "导游甲、导游乙",
        "detail": {
          "persons": [
            {
              "name": "导游甲",
              "days": 2,
              "per_day": 500.00,
              "note": ""
            },
            {
              "name": "导游乙",
              "days": 2,
              "per_day": 400.00,
              "note": ""
            }
          ]
        },
        "totalPlannedCost": 1800.00,
        "totalActualCost": 1800.00,
        "reimburse": 0,
        "paymentMethod": "SIGNED",
        "voucherUrls": [],
        "settlementConfirmStatus": "UNCONFIRMED",
        "settleStatus": "COMPLETED",
        "settledDate": "2026-07-29",
        "transferRef": "TRANSFER-20260729-001",
        "isPrimaryReporter": false,
        "remark": ""
      }
    ]
  },
  "traceId": null,
  "success": true
}
```

### 8.6 导游 Tab：典型 PUT

**请求**：

```http
PUT /v3/admin/order/2080000000000000001/settlement/staff-fees/guides
Authorization: Bearer <token>
Content-Type: application/json
```

```json
{
  "items": [
    {
      "staffId": null,
      "detail": {
        "persons": [
          {
            "name": "导游甲",
            "days": 2,
            "per_day": 500.00,
            "note": ""
          },
          {
            "name": "导游乙",
            "days": 2,
            "per_day": 400.00,
            "note": ""
          }
        ]
      },
      "reimburse": 0,
      "paymentMethod": "SIGNED",
      "voucherUrls": [],
      "settleStatus": "COMPLETED",
      "settledDate": "2026-07-29",
      "transferRef": "TRANSFER-20260729-001",
      "remark": ""
    }
  ]
}
```

**响应**：

```json
{
  "code": 200,
  "message": "成功",
  "data": null,
  "traceId": null,
  "success": true
}
```

### 8.7 摄影师 Tab：典型 GET

**请求**：

```http
GET /v3/admin/order/2080000000000000001/settlement/staff-fees/photographers
Authorization: Bearer <token>
```

无请求体。

**响应**：

```json
{
  "code": 200,
  "message": "成功",
  "data": {
    "totalActualCost": 1500.00,
    "items": [
      {
        "id": "2081000000000000004",
        "staffId": "2082000000000000004",
        "staffName": "摄影师甲",
        "detail": {
          "persons": [
            {
              "name": "摄影师甲",
              "days": 3,
              "per_day": 500.00,
              "note": ""
            }
          ]
        },
        "totalPlannedCost": 1500.00,
        "totalActualCost": 1500.00,
        "reimburse": 0,
        "paymentMethod": "COMPANY_PAID",
        "voucherUrls": [],
        "settlementConfirmStatus": "UNCONFIRMED",
        "settleStatus": "PENDING",
        "settledDate": null,
        "transferRef": null,
        "isPrimaryReporter": false,
        "remark": ""
      }
    ]
  },
  "traceId": null,
  "success": true
}
```

### 8.8 摄影师 Tab：典型 PUT

**请求**：

```http
PUT /v3/admin/order/2080000000000000001/settlement/staff-fees/photographers
Authorization: Bearer <token>
Content-Type: application/json
```

```json
{
  "items": [
    {
      "staffId": "2082000000000000004",
      "detail": {
        "persons": [
          {
            "name": "摄影师甲",
            "days": 3,
            "per_day": 500.00,
            "note": ""
          }
        ]
      },
      "reimburse": 0,
      "paymentMethod": "COMPANY_PAID",
      "voucherUrls": [],
      "settleStatus": "PENDING",
      "settledDate": null,
      "transferRef": null,
      "remark": ""
    }
  ]
}
```

**响应**：

```json
{
  "code": 200,
  "message": "成功",
  "data": null,
  "traceId": null,
  "success": true
}
```

### 8.9 其他人员 Tab：典型 GET

**请求**：

```http
GET /v3/admin/order/2080000000000000001/settlement/staff-fees/others
Authorization: Bearer <token>
```

无请求体。

**响应**：

```json
{
  "code": 200,
  "message": "成功",
  "data": {
    "totalActualCost": 350.00,
    "items": [
      {
        "id": "2081000000000000005",
        "staffId": null,
        "staffName": "临时协助",
        "detail": {
          "items": [
            {
              "name": "临时协助",
              "amount": 300.00,
              "note": ""
            }
          ]
        },
        "totalPlannedCost": 300.00,
        "totalActualCost": 350.00,
        "reimburse": 50.00,
        "paymentMethod": "CASH_PAID",
        "voucherUrls": [
          "https://example.com/vouchers/other-001.jpg"
        ],
        "settlementConfirmStatus": "UNCONFIRMED",
        "settleStatus": "COMPLETED",
        "settledDate": "2026-07-29",
        "transferRef": "TRANSFER-20260729-002",
        "isPrimaryReporter": false,
        "remark": ""
      }
    ]
  },
  "traceId": null,
  "success": true
}
```

### 8.10 其他人员 Tab：典型 PUT

**请求**：

```http
PUT /v3/admin/order/2080000000000000001/settlement/staff-fees/others
Authorization: Bearer <token>
Content-Type: application/json
```

```json
{
  "items": [
    {
      "staffId": null,
      "detail": {
        "items": [
          {
            "name": "临时协助",
            "amount": 300.00,
            "note": ""
          }
        ]
      },
      "reimburse": 50.00,
      "paymentMethod": "CASH_PAID",
      "voucherUrls": [
        "https://example.com/vouchers/other-001.jpg"
      ],
      "settleStatus": "COMPLETED",
      "settledDate": "2026-07-29",
      "transferRef": "TRANSFER-20260729-002",
      "remark": ""
    }
  ]
}
```

**响应**：

```json
{
  "code": 200,
  "message": "成功",
  "data": null,
  "traceId": null,
  "success": true
}
```

### 8.11 边界：清空单个 Tab

以下请求只清空导游 Tab，不影响领队、司机、摄影师和其他人员。

**请求**：

```http
PUT /v3/admin/order/2080000000000000001/settlement/staff-fees/guides
Authorization: Bearer <token>
Content-Type: application/json
```

```json
{
  "items": []
}
```

**响应**：

```json
{
  "code": 200,
  "message": "成功",
  "data": null,
  "traceId": null,
  "success": true
}
```

### 8.12 异常：提交人员类型或服务端字段

**请求**：

```http
PUT /v3/admin/order/2080000000000000001/settlement/staff-fees/drivers
Authorization: Bearer <token>
Content-Type: application/json
```

```json
{
  "staffRole": "GUIDE",
  "items": []
}
```

**响应**：

```json
{
  "code": 400,
  "message": "请求数据格式错误：人员费用 Tab 请求不支持字段: staffRole",
  "data": null,
  "traceId": null,
  "success": false
}
```

`id`、`settlementConfirmStatus` 等禁止字段同样会被拒绝。

### 8.13 异常：辅助人员已结算但未填流水号

**请求**：

```http
PUT /v3/admin/order/2080000000000000001/settlement/staff-fees/guides
Authorization: Bearer <token>
Content-Type: application/json
```

```json
{
  "items": [
    {
      "staffId": null,
      "detail": {
        "persons": [
          {
            "name": "导游甲",
            "days": 1,
            "per_day": 500.00
          }
        ]
      },
      "settleStatus": "COMPLETED",
      "transferRef": null
    }
  ]
}
```

**响应**：

```json
{
  "code": 584038,
  "message": "结算状态为 COMPLETED 时转账流水号不能为空",
  "data": null,
  "traceId": null,
  "success": false
}
```

### 8.14 异常：调用已删除接口

**请求**：

```http
GET /v3/admin/order/2080000000000000001/settlement/step3
Authorization: Bearer <token>
```

无请求体。

**响应**：

```json
{
  "code": 404,
  "message": "接口不存在",
  "data": null,
  "traceId": null,
  "success": false
}
```

`PUT /settlement/step3`、`GET /settlement/vehicle-fees`、`POST /settlement/vehicle-fees/confirm` 同样不可用。

## 验证证据

- 五个 Tab 的 GET 均已通过管理后台网关返回 HTTP 200、业务码 200。
- `staffRole`、`id`、`settlementConfirmStatus` 禁止字段探针均返回业务码 400。
- 旧 Step3 GET/PUT、旧车辆费用 GET/confirm 路由均已返回业务码 404。
- Merge commit 已核对五组 GET/PUT 路由、严格请求字段和 `Result<Void>` 保存响应契约。

## 9. 业务边界

- 每个 GET 只返回路径对应的人员类型，调用方不需要也不应再按 `staffRole` 过滤。
- 每个 PUT 是当前 Tab 的全量替换；遗漏的当前 Tab 行会被删除，另外四个 Tab 不受影响。
- `items: []` 是合法请求，表示清空当前 Tab；`items: null` 或缺少 `items` 会失败。
- 领队、司机必须引用当前订单对应角色的 `staffId`。
- 导游、摄影师、其他人员允许 `staffId=null` 的聚合行；如果传了 `staffId`，仍必须与订单和路径角色匹配。
- `settlementConfirmStatus` 是查询状态，PUT 不接收；Tab 保存后该 Tab 行为未确认状态。
- `settleStatus`、`settledDate`、`transferRef` 仅用于辅助人员的结算记录；主报账人行这三个字段不参与保存并在查询时为空。
- 辅助人员 `settleStatus=COMPLETED` 时必须填写 `transferRef`。
- 司机 `daily_fee` 仅回显，不计入人员费用；司机实际人员费用只计算 `extra_cost + reimburse`。
- 团期子订单不能录入非空的领队、摄影师共享费用。
- 旧 Step3 与两个 Order 管理端车辆费用接口没有兼容期，继续调用会失败。

## 10. 修改前后对比

### 10.1 路径与调用方式

| 项目 | 修改前 | 修改后 |
|------|--------|--------|
| 人员费用查询 | 一个 `GET /settlement/step3` 返回全部人员类型 | 五个 Tab 各自 GET，只返回路径对应类型 |
| 人员费用保存 | 一个 `PUT /settlement/step3` 保存全部人员类型 | 五个 Tab 各自 PUT，只替换当前 Tab |
| 人员类型 | 请求行提交 `staffRole` | 由 URL 路径唯一确定，禁止提交 `staffRole` |
| 保存数据行标识 | 响应可包含新增/更新/删除 ID | PUT 成功固定 `data=null` |
| 保存入参 ID | 可按旧聚合结构提交 `id` | 全量替换，不提交 `id` |
| 核单确认状态 | 可在旧聚合行中携带相关状态 | `settlementConfirmStatus` 只查询回显，PUT 禁止提交 |
| Order 管理端车辆费用 | 前端可调用查询/确认接口 | 两个接口删除，前端不再调用 |

### 10.2 旧路径到新路径

| 旧调用 | 新调用 |
|--------|--------|
| `GET /settlement/step3` 后按 `staffRole=LEADER` 过滤 | `GET /settlement/staff-fees/leaders` |
| `GET /settlement/step3` 后按 `staffRole=DRIVER` 过滤 | `GET /settlement/staff-fees/drivers` |
| `GET /settlement/step3` 后按 `staffRole=GUIDE` 过滤 | `GET /settlement/staff-fees/guides` |
| `GET /settlement/step3` 后按 `staffRole=PHOTOGRAPHER` 过滤 | `GET /settlement/staff-fees/photographers` |
| `GET /settlement/step3` 后按 `staffRole=OTHER` 过滤 | `GET /settlement/staff-fees/others` |
| `PUT /settlement/step3` 提交全部人员 | 按 Tab 调用对应 PUT |
| `GET /settlement/vehicle-fees` | 删除，无前端替代调用 |
| `POST /settlement/vehicle-fees/confirm` | 删除，无前端替代调用 |

## 11. 影响评估 / 回滚

### 11.1 影响评估

- **是否破坏向后兼容**：是。四个旧路由直接删除，旧 Step3 请求结构不再接受。
- **前端是否必须同步调整**：是。五个 Tab 必须切换到各自 GET/PUT，并删除 `staffRole`、`id`、`settlementConfirmStatus` 等保存字段。
- **保存响应处理**：PUT 只判断统一成功/失败结果，不再读取操作数据 ID。

### 11.2 回滚边界

- 当前接口不提供旧 Step3 和 Order 管理端车辆费用兼容路由。
- 前端若回滚到仍调用旧路由的版本，将无法完成查询或保存；回滚版本必须仍使用本文五组接口。

## 12. 注意事项

- 不要在五个 PUT 的根对象或行对象中发送 `staffRole`。
- 不要把 GET 返回的完整行对象原样回传；至少移除 `id`、`staffName`、`totalPlannedCost`、`totalActualCost`、`settlementConfirmStatus`、`isPrimaryReporter`。
- 五个 Tab 应分别维护请求状态和保存动作；保存某个 Tab 时不要拼入其他类型的行。
- PUT 成功后的 `data` 为 `null`，不再解析新增、修改、删除 ID。
- 删除前端对 `/settlement/step3`、`/settlement/vehicle-fees`、`/settlement/vehicle-fees/confirm` 的调用。
- 金额字段按 Decimal 处理；ID 字段按 String 处理。

## 13. 关联 / 联系人

### 13.1 链接

- **Issue**：[#5325](https://git.1814.love:8443/wx/HL/issues/5325)
- **PR**：[#5332](https://git.1814.love:8443/wx/HL/pulls/5332)
- **Merge commit**：[10996166df493ee4237fdfdbaece611cb6c0cb52](https://git.1814.love:8443/wx/HL/commit/10996166df493ee4237fdfdbaece611cb6c0cb52)

### 13.2 联系人

- **后端负责人**：@yst
