所有检测均成功
changelog-filename-gate / validate (push) Successful in 3s
修改原因:管理后台已完成五类人员费用独立 Tab 接入,需要回写消费终态。 修改内容:将 #5325 frontend_status 更新为 implemented,并记录可达业务提交 4cea39f7e87e74f42fb28ab260da12b585f034de。 实际验证:npm test 通过 46 项 changelog 质量门禁;git diff --check 通过。 Changelog:changelogs-v2/2026-07/29_5325_核单人员费用分Tab-修改接口-管理后台.md
1067 行
32 KiB
Markdown
1067 行
32 KiB
Markdown
---
|
||
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
|