--- 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 ``` 无请求体。 **响应**: ```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