From c8bb504b8d97fe194f5659558b1368bf9a0b466c Mon Sep 17 00:00:00 2001 From: yaosutu <770858045@qq.com> Date: Wed, 29 Jul 2026 08:58:03 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E6=A0=B8=E5=8D=95=E4=BA=BA?= =?UTF-8?q?=E5=91=98=E8=B4=B9=E7=94=A8=E5=88=86Tab=E6=8E=A5=E5=8F=A3?= =?UTF-8?q?=E5=8F=98=E6=9B=B4=E8=AF=B4=E6=98=8E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...325_核单人员费用分Tab-修改接口-管理后台.md | 1066 +++++++++++++++++ 1 file changed, 1066 insertions(+) create mode 100644 changelogs-v2/2026-07/29_5325_核单人员费用分Tab-修改接口-管理后台.md diff --git a/changelogs-v2/2026-07/29_5325_核单人员费用分Tab-修改接口-管理后台.md b/changelogs-v2/2026-07/29_5325_核单人员费用分Tab-修改接口-管理后台.md new file mode 100644 index 0000000..0ed421f --- /dev/null +++ b/changelogs-v2/2026-07/29_5325_核单人员费用分Tab-修改接口-管理后台.md @@ -0,0 +1,1066 @@ +--- +schema: "hl-changelog/v2" +ticket: "5325" +title: "核单人员费用分 Tab" +consumer: "admin" +change_type: "修改接口" +backend_status: "deployed" +gateway_status: "verified" +frontend_status: "pending" +frontend_owner: "" +frontend_ref: "" +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 +``` + +无请求体。 + +**响应**: + +```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 +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 +``` + +无请求体。 + +**响应**: + +```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 +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 +``` + +无请求体。 + +**响应**: + +```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 +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 +``` + +无请求体。 + +**响应**: + +```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 +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 +``` + +无请求体。 + +**响应**: + +```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 +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 +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 +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 +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 +``` + +无请求体。 + +**响应**: + +```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` 保存响应契约。 + +## 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