# 【修改接口·管理后台】核单 Step3 合并辅助人员结算 (#4869)

> **PR**: #4870 | **服务**: hl-order-service-v3 | **更新时间**: 2026-07-10 00:00

## 1. 接口背景

核单流程中的“辅助人员结算 / 小算拨款”不再使用独立 payout 接口维护，统一收敛到 Step3 人员费用保存接口。前端需要在 Step3 人员费用行里一起提交结算状态、结算日期和转账流水号，并停止调用旧的 payout 查询/保存接口。

## 2. 变更清单

| # | 接口 | 方法 | 路径 | 变更类型 | 说明 |
|---|------|------|------|----------|------|
| 1 | Step3 人员费用保存 | PUT | `/v3/admin/order/{orderId}/settlement/step3` | 修改接口 | `items[]` 入参和响应行新增 `settleStatus`、`settledDate`、`transferRef` |
| 2 | 小算拨款列表查询 | GET | `/v3/admin/order/{orderId}/settlement/payout` | 删除接口 | 不再提供独立 payout 查询 |
| 3 | 小算拨款状态保存 | PUT | `/v3/admin/order/{orderId}/settlement/payout` | 删除接口 | 不再提供独立 payout 保存 |

## 3. 接口详情

### 3.1 PUT Step3 人员费用保存

- **使用场景**: 核单中保存人员费用，并同步保存辅助人员结算状态。
- **认证**: 管理后台 JWT。
- **幂等性**: 非幂等；每次按 `items` 全量替换 Step3 人员费用行。
- **路径**: `PUT /v3/admin/order/{orderId}/settlement/step3`

#### 路径参数

| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| orderId | Long/String | 是 | 订单 ID，雪花 ID 建议前端按字符串传递 |

#### 请求体

| 字段 | 类型 | 必填 | 说明 | 校验规则 |
|------|------|------|------|----------|
| items | Array<StaffFeeItem> | 是 | 人员费用明细数组，全量替换 | 不可为 null |

#### StaffFeeItem 字段

| 字段 | 类型 | 必填 | 说明 | 校验规则 |
|------|------|------|------|----------|
| id | Long/String | 否 | 已存在行 ID；为空表示新增行 | 雪花 ID 建议字符串传递 |
| staffRole | String | 是 | 人员角色 | `LEADER` / `DRIVER` / `GUIDE` / `PHOTOGRAPHER` / `OTHER` |
| staffId | Long/String | 否 | 人员派单 ID | 雪花 ID 建议字符串传递 |
| detail | Object | 是 | 按角色派生的费用明细结构 | 不可为 null |
| reimburse | Decimal | 否 | 小额报销金额 | 不传按 0 处理；传值需大于等于 0 |
| settleStatus | String | 否 | 辅助人员结算状态；主报账人行会忽略该字段 | `PENDING` / `COMPLETED` |
| settledDate | String | 否 | 辅助人员结算日期 | `yyyy-MM-dd` |
| transferRef | String | 条件必填 | 辅助人员结算转账流水号 | `settleStatus=COMPLETED` 时必填；最长 128 字符 |
| remark | String | 否 | 备注 | 最长 500 字符 |

#### 响应字段

| 字段 | 类型 | 说明 |
|------|------|------|
| addedIds | Array<String> | 新增行 ID 列表 |
| updatedIds | Array<String> | 更新行 ID 列表 |
| deletedIds | Array<String> | 本次全量替换删除的历史行 ID 列表 |
| totalActualCost | String/Decimal | 人员费用实际成本合计 |
| items | Array<StaffFeeRespItem> | 保存后的人员费用明细 |

#### StaffFeeRespItem 字段

| 字段 | 类型 | 说明 |
|------|------|------|
| id | String | 行 ID |
| staffRole | String | 人员角色 |
| staffId | String/null | 人员派单 ID |
| staffName | String/null | 人员姓名快照 |
| totalPlannedCost | String/Decimal | 计划成本 |
| totalActualCost | String/Decimal | 实际成本 |
| detail | Object | 按角色派生的费用明细结构 |
| reimburse | String/Decimal | 小额报销金额 |
| settleStatus | String/null | 辅助人员结算状态；主报账人行为空 |
| settledDate | String/null | 辅助人员结算日期 |
| transferRef | String/null | 辅助人员结算转账流水号 |
| isPrimaryReporter | Boolean | 是否主报账人 |
| remark | String/null | 备注 |

### 3.2 已删除 GET payout

- **删除接口**: `GET /v3/admin/order/{orderId}/settlement/payout`
- **替代方式**: 使用 Step3 人员费用响应 `items[]` 中的 `settleStatus`、`settledDate`、`transferRef` 字段展示辅助人员结算状态。

### 3.3 已删除 PUT payout

- **删除接口**: `PUT /v3/admin/order/{orderId}/settlement/payout`
- **替代方式**: 调用 `PUT /v3/admin/order/{orderId}/settlement/step3`，在对应 `items[]` 行内提交 `settleStatus`、`settledDate`、`transferRef`。

## 4. 入参

本变更的有效入参集中在 `PUT /v3/admin/order/{orderId}/settlement/step3`。旧 payout 入参整体删除。

### 4.1 新增字段

| 字段 | 位置 | 类型 | 必填 | 说明 |
|------|------|------|------|------|
| settleStatus | `items[]` | String | 否 | 辅助人员结算状态；主报账人行忽略 |
| settledDate | `items[]` | String | 否 | 结算日期，格式 `yyyy-MM-dd` |
| transferRef | `items[]` | String | 条件必填 | `settleStatus=COMPLETED` 时必填 |

### 4.2 删除的旧入参

| 旧接口 | 字段 | 处理方式 |
|--------|------|----------|
| `PUT /v3/admin/order/{orderId}/settlement/payout` | `items[].staffId` | 改为 Step3 `items[].staffId` |
| `PUT /v3/admin/order/{orderId}/settlement/payout` | `items[].staffRole` | 改为 Step3 `items[].staffRole` |
| `PUT /v3/admin/order/{orderId}/settlement/payout` | `items[].staffName` | 不再作为保存入参 |
| `PUT /v3/admin/order/{orderId}/settlement/payout` | `items[].settleStatus` | 改为 Step3 `items[].settleStatus` |
| `PUT /v3/admin/order/{orderId}/settlement/payout` | `items[].settledDate` | 改为 Step3 `items[].settledDate` |
| `PUT /v3/admin/order/{orderId}/settlement/payout` | `items[].transferRef` | 改为 Step3 `items[].transferRef` |

## 5. 出参

### 5.1 Step3 响应新增字段

| 字段 | 位置 | 类型 | 说明 |
|------|------|------|------|
| settleStatus | `data.items[]` | String/null | 辅助人员结算状态；主报账人行为空 |
| settledDate | `data.items[]` | String/null | 辅助人员结算日期 |
| transferRef | `data.items[]` | String/null | 辅助人员结算转账流水号 |

### 5.2 删除的旧出参

| 旧接口 | 字段 | 处理方式 |
|--------|------|----------|
| `GET /v3/admin/order/{orderId}/settlement/payout` | `staffId` | 从 Step3 `data.items[].staffId` 读取 |
| `GET /v3/admin/order/{orderId}/settlement/payout` | `staffName` | 从 Step3 `data.items[].staffName` 读取 |
| `GET /v3/admin/order/{orderId}/settlement/payout` | `staffRole` | 从 Step3 `data.items[].staffRole` 读取 |
| `GET /v3/admin/order/{orderId}/settlement/payout` | `laborCost` | 不再独立返回；人员费用成本看 Step3 `totalActualCost` / 明细 `detail` |
| `GET /v3/admin/order/{orderId}/settlement/payout` | `reimburse` | 从 Step3 `data.items[].reimburse` 读取 |
| `GET /v3/admin/order/{orderId}/settlement/payout` | `dueAmount` | 不再独立返回；如需展示由前端按 Step3 行数据计算 |
| `GET /v3/admin/order/{orderId}/settlement/payout` | `settleStatus` | 从 Step3 `data.items[].settleStatus` 读取 |
| `GET /v3/admin/order/{orderId}/settlement/payout` | `settledDate` | 从 Step3 `data.items[].settledDate` 读取 |
| `GET /v3/admin/order/{orderId}/settlement/payout` | `transferRef` | 从 Step3 `data.items[].transferRef` 读取 |

## 6. 枚举 / 数据字典

### 6.1 settleStatus

**所属字段**: `items[].settleStatus` / `data.items[].settleStatus` | **类型**: String | **必填**: 否，`COMPLETED` 时需配合 `transferRef`

| 值 | 中文 | 说明 |
|----|------|------|
| `PENDING` | 待结算 | 辅助人员尚未完成转账 |
| `COMPLETED` | 已结算 | 辅助人员已完成转账，必须填写 `transferRef` |

### 6.2 staffRole

**所属字段**: `items[].staffRole` / `data.items[].staffRole` | **类型**: String | **必填**: 是

| 值 | 中文 | 说明 |
|----|------|------|
| `LEADER` | 地接 | 地接人员费用 |
| `DRIVER` | 司机 | 司机人员费用 |
| `GUIDE` | 导游 | 导游人员费用 |
| `PHOTOGRAPHER` | 摄影师 | 摄影师人员费用 |
| `OTHER` | 其他 | 其他人员费用 |

## 7. 错误码

| code | 含义 | 触发场景 |
|------|------|----------|
| 400 | 参数校验失败 | `settleStatus` 不在 `PENDING/COMPLETED` 内，或字段类型不合法 |
| 584038 | 结算状态为 COMPLETED 时转账流水号不能为空 | Step3 行 `settleStatus=COMPLETED` 且 `transferRef` 为空 |
| 584039 | 结算状态非法，必须是 PENDING 或 COMPLETED | Step3 保存时传入非法结算状态 |

## 8. 示例

### 8.1 典型成功：Step3 保存辅助人员结算状态

**请求**

```http
PUT /v3/admin/order/2075415561948315650/settlement/step3
Authorization: Bearer <token>
Content-Type: application/json
```

```json
{
  "items": [
    {
      "id": "2076000000000000001",
      "staffRole": "GUIDE",
      "staffId": "2075000000000000001",
      "detail": {
        "persons": [
          {
            "name": "张三",
            "days": [
              {
                "date": "2026-07-10",
                "is_used": true
              }
            ],
            "per_day": 300
          }
        ]
      },
      "reimburse": 50,
      "settleStatus": "COMPLETED",
      "settledDate": "2026-07-10",
      "transferRef": "TR202607100001",
      "remark": "导游费用已转账"
    }
  ]
}
```

**响应**

```json
{
  "code": 200,
  "data": {
    "addedIds": [],
    "updatedIds": ["2076000000000000001"],
    "deletedIds": [],
    "totalActualCost": "350.00",
    "items": [
      {
        "id": "2076000000000000001",
        "staffRole": "GUIDE",
        "staffId": "2075000000000000001",
        "staffName": "张三",
        "totalPlannedCost": "300.00",
        "totalActualCost": "350.00",
        "detail": {
          "persons": [
            {
              "name": "张三",
              "days": [
                {
                  "date": "2026-07-10",
                  "is_used": true
                }
              ],
              "per_day": 300
            }
          ]
        },
        "reimburse": "50.00",
        "settleStatus": "COMPLETED",
        "settledDate": "2026-07-10",
        "transferRef": "TR202607100001",
        "isPrimaryReporter": false,
        "remark": "导游费用已转账"
      }
    ]
  },
  "msg": "success"
}
```

### 8.2 边界：待结算可不传转账流水号

**请求**

```http
PUT /v3/admin/order/2075415561948315650/settlement/step3
Authorization: Bearer <token>
Content-Type: application/json
```

```json
{
  "items": [
    {
      "staffRole": "DRIVER",
      "staffId": "2075000000000000002",
      "detail": {
        "days": [
          {
            "service_date": "2026-07-10",
            "daily_fee": 500,
            "is_used": true
          }
        ],
        "extra_cost": 0
      },
      "reimburse": 0,
      "settleStatus": "PENDING",
      "settledDate": null,
      "transferRef": null
    }
  ]
}
```

**响应**

```json
{
  "code": 200,
  "data": {
    "addedIds": ["2076000000000000002"],
    "updatedIds": [],
    "deletedIds": [],
    "totalActualCost": "500.00",
    "items": [
      {
        "id": "2076000000000000002",
        "staffRole": "DRIVER",
        "staffId": "2075000000000000002",
        "staffName": "李四",
        "totalPlannedCost": "500.00",
        "totalActualCost": "500.00",
        "detail": {
          "days": [
            {
              "service_date": "2026-07-10",
              "daily_fee": 500,
              "is_used": true
            }
          ],
          "extra_cost": 0
        },
        "reimburse": "0.00",
        "settleStatus": "PENDING",
        "settledDate": null,
        "transferRef": null,
        "isPrimaryReporter": false,
        "remark": null
      }
    ]
  },
  "msg": "success"
}
```

### 8.3 异常：已结算但未传 transferRef

**请求**

```http
PUT /v3/admin/order/2075415561948315650/settlement/step3
Authorization: Bearer <token>
Content-Type: application/json
```

```json
{
  "items": [
    {
      "staffRole": "GUIDE",
      "staffId": "2075000000000000001",
      "detail": {
        "persons": []
      },
      "settleStatus": "COMPLETED",
      "settledDate": "2026-07-10",
      "transferRef": ""
    }
  ]
}
```

**响应**

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

## 9. 业务边界

- 仅影响管理后台核单流程。
- `settleStatus` / `settledDate` / `transferRef` 只用于辅助人员结算；主报账人行的 `settleStatus` 在响应中为空，提交时可不传。
- `settleStatus=COMPLETED` 时 `transferRef` 必填。
- 旧 payout 两个接口删除后，前端不能再依赖独立“小算拨款”接口加载或保存。
- Step3 保存仍是全量替换语义，前端保存时需要提交完整 `items` 列表，不能只提交单行结算状态。

## 10. 修改前后对比

### 10.1 字段级对比

| 字段 | 修改前 | 修改后 |
|------|--------|--------|
| Step3 `items[].settleStatus` | 无 | 新增，保存辅助人员结算状态 |
| Step3 `items[].settledDate` | 无 | 新增，保存辅助人员结算日期 |
| Step3 `items[].transferRef` | 无 | 新增，保存辅助人员结算转账流水号 |
| Step3 `data.items[].settleStatus` | 无 | 新增，返回辅助人员结算状态 |
| Step3 `data.items[].settledDate` | 无 | 新增，返回辅助人员结算日期 |
| Step3 `data.items[].transferRef` | 无 | 新增，返回辅助人员结算转账流水号 |

### 10.2 行为级对比

| 行为 | 修改前 | 修改后 |
|------|--------|--------|
| 查询小算拨款 | 调 `GET /settlement/payout` | 从 Step3 响应 `data.items[]` 读取 |
| 保存小算拨款 | 调 `PUT /settlement/payout` | 调 `PUT /settlement/step3`，在人员费用行内提交 |
| 已结算流水号校验 | payout 接口校验 | Step3 接口校验 |

## 11. 影响评估 / 回滚

### 11.1 影响评估

- **是否破坏向后兼容**: 是。旧 `GET/PUT /settlement/payout` 删除，继续调用会失败。
- **前端是否必须同步上线**: 是。前端需要移除 payout 查询/保存调用，并把小算拨款字段并入 Step3 人员费用行。

### 11.2 回滚方案

- 如需回滚，需要恢复旧 `GET/PUT /settlement/payout` 契约，并让前端重新按旧接口查询/保存小算拨款。

## 12. 注意事项

- 前端需要清理 `getSettlementPayout` / `saveSettlementPayout` 相关调用。
- 前端保存 Step3 时请求体必须是 `{ "items": [...] }`，不是裸数组。
- 前端如果仍保留“小算拨款”独立步骤，也必须从 Step3 `items[]` 取数和回写，不能再请求 payout 接口。
- `settleStatus=COMPLETED` 时必须带 `transferRef`，否则返回 `584038`。

## 13. 关联 / 联系人

### 13.1 链接

- **Issue**: [#4869](https://git.1814.love:8443/wx/HL/issues/4869)
- **PR**: [#4870](https://git.1814.love:8443/wx/HL/pulls/4870)
- **Merge commit**: [6d99b982a](https://git.1814.love:8443/wx/HL/commit/6d99b982a7bf8f5bacc52afb3de07bf5ed533f0a)
- **前置通知**: [#4768 核单对账 payout 新增接口](https://git.1814.love:8443/wx/hl-api-changelog/src/branch/main/changelogs-v2/2026-07/07_4768_核单对账payout-新增接口-管理后台.md)

### 13.2 联系人

- **后端负责人**: @腰苏图
