---
schema: "hl-changelog/v2"
ticket: "5356"
title: "核单八类来源确认状态与车辆 Step3 接口统一"
consumer: "admin"
change_type: "修改接口"
backend_status: "deployed"
gateway_status: "verified"
frontend_status: "implemented"
frontend_owner: "Pi"
frontend_ref: "9b9865a50304b1acc55ab5e4f94bb7dfe52293c0"
target_release: ""
verified_at: "2026-07-30"
status_note: "管理后台已统一八类核单来源与逐行确认状态，车辆改用 Order Step3 GET/PUT 并携带 version、保护 FLEET 权威字段；pnpm checkpoint 全量通过，业务提交 9b9865a50304b1acc55ab5e4f94bb7dfe52293c0 已推送至 origin/v2.1。后端车辆 DTO 正向数据与 Full E2E 仍受测试订单无可核单车辆费用限制。"
updated_at: "2026-07-30"
base: "dev-v3"
---

# ⚠️【修改接口·管理后台】核单八类来源确认状态与车辆 Step3 接口统一 (#5356)

> **PR**: #5362 | **服务**: hl-order-service-v3 | **更新时间**: 2026-07-30 15:46

## 1. 接口背景

核单页面需要用同一套规则识别“手工行”和“系统来源行”，并逐行完成确认。此前各 Tab 的 `sourceType`、确认状态和车辆费用入口不一致，车辆数据还残留过已下线接口的字段口径。本次统一八类核单来源与逐行确认语义，并新增 Order 侧车辆 Step3 草稿查询、全量保存接口。

管理后台应以本文列出的 Order 侧接口为准；已删除的
`GET /v3/admin/order/:orderId/settlement/vehicle-fees` 和
`POST /v3/admin/order/:orderId/settlement/vehicle-fees/confirm`
继续保持下线，不得恢复调用。

## 变更接口（2. 变更清单）

| # | 接口 | 方法 | 路径 | 变更类型 | 说明 |
|---|---|---|---|---|---|
| 1 | Step 1 查询住宿核单明细 | GET | `/v3/admin/order/:orderId/settlement/step1` | 修改 | 来源值统一；系统派生行初始为未确认；ID 按字符串返回 |
| 2 | Step 1 保存住宿核单明细 | PUT | `/v3/admin/order/:orderId/settlement/step1` | 修改 | 支持逐行 `UNCONFIRMED/CONFIRMED`；手工新行必须先未确认 |
| 3 | Step 2 查询门票核单明细 | GET | `/v3/admin/order/:orderId/settlement/step2` | 修改 | 响应新增逐行确认状态；ID 按字符串返回 |
| 4 | Step 2 保存门票核单明细 | PUT | `/v3/admin/order/:orderId/settlement/step2` | 修改 | 请求新增逐行确认状态；手工新行必须先未确认 |
| 5 | 查询车辆核单草稿 | GET | `/v3/admin/order/:orderId/settlement/step3/vehicles` | 新增 | Order 侧车辆 Step3 唯一查询入口 |
| 6 | 全量保存车辆核单草稿 | PUT | `/v3/admin/order/:orderId/settlement/step3/vehicles` | 新增 | 带 `version` 全量保存，支持车务行确认和手工行维护 |
| 7 | 查询领队人员费用 | GET | `/v3/admin/order/:orderId/settlement/staff-fees/leaders` | 修改 | 新增来源与确认状态名称 |
| 8 | 保存领队人员费用 | PUT | `/v3/admin/order/:orderId/settlement/staff-fees/leaders` | 修改 | 请求行新增 `id/sourceType/settlementConfirmStatus` |
| 9 | 查询司机人员费用 | GET | `/v3/admin/order/:orderId/settlement/staff-fees/drivers` | 修改 | 新增来源与确认状态名称 |
| 10 | 保存司机人员费用 | PUT | `/v3/admin/order/:orderId/settlement/staff-fees/drivers` | 修改 | 请求行新增 `id/sourceType/settlementConfirmStatus` |
| 11 | 查询导游人员费用 | GET | `/v3/admin/order/:orderId/settlement/staff-fees/guides` | 修改 | 新增来源与确认状态名称 |
| 12 | 保存导游人员费用 | PUT | `/v3/admin/order/:orderId/settlement/staff-fees/guides` | 修改 | 请求行新增 `id/sourceType/settlementConfirmStatus` |
| 13 | 查询摄影师人员费用 | GET | `/v3/admin/order/:orderId/settlement/staff-fees/photographers` | 修改 | 新增来源与确认状态名称 |
| 14 | 保存摄影师人员费用 | PUT | `/v3/admin/order/:orderId/settlement/staff-fees/photographers` | 修改 | 请求行新增 `id/sourceType/settlementConfirmStatus` |
| 15 | 查询其他人员费用 | GET | `/v3/admin/order/:orderId/settlement/staff-fees/others` | 修改 | 新增来源与确认状态名称 |
| 16 | 保存其他人员费用 | PUT | `/v3/admin/order/:orderId/settlement/staff-fees/others` | 修改 | 请求行新增 `id/sourceType/settlementConfirmStatus` |
| 17 | 查询餐食费用 | GET | `/v3/admin/order/:orderId/settlement/meals` | 修改 | 响应新增来源、来源名称、确认状态名称 |
| 18 | 新增餐食费用 | POST | `/v3/admin/order/:orderId/settlement/meals` | 修改 | 请求新增来源和确认状态；新行必须先未确认 |
| 19 | 修改餐食费用 | PUT | `/v3/admin/order/:orderId/settlement/meals/:settlementId` | 修改 | 可把已存在未确认行保存为已确认 |
| 20 | 查询其他支出 | GET | `/v3/admin/order/:orderId/settlement/other-expenses` | 修改 | 响应新增来源、来源名称、确认状态名称 |
| 21 | 新增其他支出 | POST | `/v3/admin/order/:orderId/settlement/other-expenses` | 修改 | 手工新行必须先未确认 |
| 22 | 修改其他支出 | PUT | `/v3/admin/order/:orderId/settlement/other-expenses/:settlementId` | 修改 | 可把已存在未确认行保存为已确认 |
| 23 | 查询其他收入 | GET | `/v3/admin/order/:orderId/settlement/other-incomes` | 修改 | `sourceType` 从内部来源值改为 `MANUAL/SYSTEM` |
| 24 | 新增其他收入 | POST | `/v3/admin/order/:orderId/settlement/other-incomes` | 修改 | 手工新行必须先未确认 |
| 25 | 修改其他收入 | PUT | `/v3/admin/order/:orderId/settlement/other-incomes/:incomeId` | 修改 | 可把已存在未确认行保存为已确认 |
| 26 | 完成核单 | POST | `/v3/admin/order/:orderId/settlement/finalize` | 修改 | 只接受八类来源同步完成且所有非空明细均已确认的数据 |

## 3. 接口详情

以下接口均需登录态 JWT 和现有订单查看/核单权限；无接口级特殊限流。GET 为只读幂等；PUT 为全量替换幂等；POST 新增其他收入使用 `requestId` 保证同订单幂等。

### 3.1 住宿 Step 1

**接口**

| 方法 | 路径 | 使用场景 |
|---|---|---|
| GET | `/v3/admin/order/:orderId/settlement/step1` | 打开住宿 Tab、刷新系统配房来源 |
| PUT | `/v3/admin/order/:orderId/settlement/step1` | 全量保存住宿行及逐行确认状态 |

**路径参数**

| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| `orderId` | String(Long) | 是 | 订单 ID，必须大于 0 |

**PUT 请求体**

| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| `items` | HotelItem[] | 是 | 全量数组；缺少的手工现存行按删除处理 |
| `items[].id` | String(Long) | 更新时是 | 已保存行 ID；新行为空 |
| `items[].hotelAssignmentId` | String(Long) | 否 | 配房来源行 ID；手工行为空 |
| `items[].hotelId` | String(Long) | 否 | 酒店 ID |
| `items[].roomTypeId` | String(Long) | 否 | 房型 ID |
| `items[].stayDate` | Date | 是 | `yyyy-MM-dd` |
| `items[].hotelName` | String | 是 | 最长 200 字符 |
| `items[].roomType` | String | 否 | 房型摘要，最长 64 字符 |
| `items[].roomTypeName` | String | 否 | 房型/规格名称，最长 128 字符 |
| `items[].roomCount` | Integer | 是 | 总间数 |
| `items[].unitPrice` | Decimal | 否 | 核算单价，必须大于等于 0 |
| `items[].plannedCost` | Decimal | 是 | 计划成本，必须大于等于 0 |
| `items[].actualCost` | Decimal | 是 | 实际成本，必须大于等于 0 |
| `items[].paymentMethod` | String | 条件必填 | `SIGNED/COMPANY_PAID/CASH_PAID`；手工行必填 |
| `items[].settleType` | String | 否 | `cash/sign/company`，兼容配房来源付款口径 |
| `items[].sourceType` | String | 是 | `HOUSE_ASSIGNMENT/MANUAL/SYSTEM`；`TEMPLATE` 仅兼容旧入参 |
| `items[].sourceId` | String(Long) | 否 | 系统来源业务 ID |
| `items[].settlementConfirmStatus` | String | 是 | `UNCONFIRMED/CONFIRMED` |
| `items[].remark` | String | 否 | 最长 500 字符 |
| `items[].voucherUrls` | String[] | 否 | 凭证 URL |

**GET 响应 `data[]`**

除上述行字段外，还返回：

| 字段 | 类型 | 说明 |
|---|---|---|
| `paymentMethodName` | String | 付款方式名称 |
| `sourceTypeName` | String | 来源名称：配房结果/手工/系统 |
| `settlementConfirmStatusName` | String | 未确认/已确认 |

**PUT 响应 `data`**

| 字段 | 类型 | 说明 |
|---|---|---|
| `addedIds` | Long[] | 新增行 ID |
| `updatedIds` | Long[] | 更新行 ID |
| `deletedIds` | Long[] | 删除行 ID |
| `totalActualCost` | String(Decimal) | 保存后住宿实际成本合计 |

**业务边界**

- 配房来源行首次进入草稿返回 `UNCONFIRMED`；带已有 `id` 保存时可改为 `CONFIRMED`。
- 手工新行 `id=null` 时只允许 `UNCONFIRMED`；保存取得 ID 后，下一次 PUT 才可改为 `CONFIRMED`。
- `TEMPLATE` 只兼容旧请求，响应统一为 `SYSTEM`。

### 3.2 门票 Step 2

**接口**

| 方法 | 路径 | 使用场景 |
|---|---|---|
| GET | `/v3/admin/order/:orderId/settlement/step2` | 打开门票/游玩项目 Tab、刷新行程来源 |
| PUT | `/v3/admin/order/:orderId/settlement/step2` | 全量保存门票行及逐行确认状态 |

**PUT 请求体**

| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| `items` | TicketItem[] | 是 | 全量数组 |
| `items[].id` | String(Long) | 更新时是 | 已保存行 ID；新行为空 |
| `items[].sourceType` | String | 是 | `SCENIC_ASSIGNMENT/ACTIVITY_ASSIGNMENT/MANUAL`；`CUSTOM_ASSIGNMENT` 仅兼容旧入参 |
| `items[].scenicAssignmentId` | String(Long) | 系统行是 | 景区或活动来源 ID |
| `items[].dayNumber` | Integer | 否 | 行程第几天，响应派生 |
| `items[].dayDate` | Date | 是 | 行程日 |
| `items[].scenicName` | String | 是 | 项目名，最长 200 字符 |
| `items[].specName` | String | 否 | 票型/规格，最长 128 字符 |
| `items[].ticketCount` | Integer | 是 | 实际购票数量，可为 0 |
| `items[].ticketUnitPrice` | Decimal | 否 | 参考单价 |
| `items[].sellPrice` | Decimal | 否 | 客户成交单价，必须大于等于 0 |
| `items[].totalAmount` | Decimal | 否 | 客户成交小计，必须大于等于 0 |
| `items[].plannedCost` | Decimal | 是 | 计划成本，必须大于等于 0 |
| `items[].actualCost` | Decimal | 是 | 实际成本，必须大于等于 0 |
| `items[].paymentMethod` | String | 否 | `SIGNED/COMPANY_PAID/CASH_PAID` |
| `items[].voucherUrls` | String[] | 否 | 凭证 URL |
| `items[].remark` | String | 否 | 最长 500 字符 |
| `items[].settlementConfirmStatus` | String | 是 | `UNCONFIRMED/CONFIRMED` |

**GET 响应 `data[]`**

返回完整 `TicketItem`，并增加：

| 字段 | 类型 | 说明 |
|---|---|---|
| `sourceTypeName` | String | 景区/游玩项目/手工 |
| `paymentMethodName` | String | 付款方式名称 |
| `settlementConfirmStatusName` | String | 未确认/已确认 |

**PUT 响应**与住宿 Step 1 相同：`addedIds/updatedIds/deletedIds/totalActualCost`。

**业务边界**

- 系统来源首次同步为 `UNCONFIRMED`，来源事实变化后会重新变为 `UNCONFIRMED`。
- 手工新行必须先保存为 `UNCONFIRMED`，已有 ID 后可保存为 `CONFIRMED`。
- 响应不再返回 `CUSTOM_ASSIGNMENT`，历史自定义值统一返回 `MANUAL`。

### 3.3 车辆 Step 3

**接口**

| 方法 | 路径 | 使用场景 |
|---|---|---|
| GET | `/v3/admin/order/:orderId/settlement/step3/vehicles` | 查询 Order 侧车辆核单草稿 |
| PUT | `/v3/admin/order/:orderId/settlement/step3/vehicles` | 带版本全量保存车辆行 |

**PUT 请求体**

| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| `version` | Long | 是 | GET 返回的草稿版本，最小 0 |
| `items` | VehicleItem[] | 是 | 全量明细；所有现存 `FLEET` 行必须原样带回 |
| `items[].id` | String(Long) | FLEET/更新时是 | 行 ID；手工新行为空 |
| `items[].sourceType` | String | 是 | `FLEET/MANUAL` |
| `items[].serviceDate` | Date | 是 | 服务日期 |
| `items[].vehicleId` | String(Long) | 否 | 车辆 ID |
| `items[].vehiclePlate` | String | 否 | 车牌，最长 64 字符 |
| `items[].vehicleModelId` | String(Long) | 否 | 车型 ID |
| `items[].vehicleModelName` | String | 否 | 车型名，最长 128 字符 |
| `items[].driverId` | String(Long) | 否 | 司机 ID |
| `items[].driverName` | String | 否 | 司机名，最长 64 字符 |
| `items[].amount` | Decimal | 是 | 金额，0～10 位整数、2 位小数 |
| `items[].paymentMethod` | String | 是 | `CASH_PAID/SIGNED/COMPANY_PAID` |
| `items[].settlementConfirmStatus` | String | 是 | `UNCONFIRMED/CONFIRMED` |
| `items[].remark` | String | 否 | 最长 500 字符 |
| `items[].voucherUrls` | String[] | 否 | 最多 9 个 http/https URL，单个最长 1024 字符 |

未知字段会被拒绝。

**GET/PUT 响应 `data`**

| 字段 | 类型 | 说明 |
|---|---|---|
| `orderId` | String(Long) | 订单 ID |
| `version` | Long | 当前草稿版本 |
| `totalAmount` | Decimal | 当前全部明细金额合计 |
| `allConfirmed` | Boolean | 非空行是否全部已确认；合法空集为 `true` |
| `items` | VehicleItem[] | 当前全量明细 |
| `items[].id` | String(Long) | 行 ID |
| `items[].sourceType` | String | `FLEET/MANUAL` |
| `items[].sourceTypeName` | String | 车务/手工 |
| `items[].serviceDate` | Date | 服务日期 |
| `items[].vehicleId` | String(Long) | 车辆 ID，可空 |
| `items[].vehiclePlate` | String | 车牌，可空 |
| `items[].vehicleModelId` | String(Long) | 车型 ID，可空 |
| `items[].vehicleModelName` | String | 车型名，可空 |
| `items[].driverId` | String(Long) | 司机 ID，可空 |
| `items[].driverName` | String | 司机名，可空 |
| `items[].amount` | Decimal | 核单金额 |
| `items[].paymentMethod` | String | 付款方式编码 |
| `items[].paymentMethodName` | String | 付款方式名称 |
| `items[].settlementConfirmStatus` | String | 确认状态 |
| `items[].settlementConfirmStatusName` | String | 未确认/已确认 |
| `items[].remark` | String | 备注 |
| `items[].voucherUrls` | String[] | 凭证 URL |

**业务边界**

- `FLEET` 行的日期、车辆、司机、金额和付款方式不可修改或删除；只允许修改确认状态、备注、凭证。
- 全量保存时必须带回全部 `FLEET` 行。手工行可新增、修改或从全量数组中删除。
- 手工新行必须先保存为 `UNCONFIRMED`；已有 ID 后可保存为 `CONFIRMED`。
- `version` 不匹配返回 584108，必须重新 GET 后再保存。
- 旧响应字段 `frozen/requirementId/settlementReady/totalVehicleFee` 及 Fleet 对账明细字段不再对管理后台输出。

### 3.4 人员费用五个 Tab

**接口路径**

| 角色 | GET/PUT 路径 | `detail` 结构 |
|---|---|---|
| 领队 | `/v3/admin/order/:orderId/settlement/staff-fees/leaders` | `days + per_day` |
| 司机 | `/v3/admin/order/:orderId/settlement/staff-fees/drivers` | `days[] + extra_cost + extra_breakdown[]` |
| 导游 | `/v3/admin/order/:orderId/settlement/staff-fees/guides` | `persons[]` |
| 摄影师 | `/v3/admin/order/:orderId/settlement/staff-fees/photographers` | `persons[]` |
| 其他 | `/v3/admin/order/:orderId/settlement/staff-fees/others` | `items[]` |

**PUT 请求体**

| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| `items` | StaffItem[] | 是 | 当前角色全量数组；空数组清空该 Tab 可删除的行 |
| `items[].id` | String(Long) | 更新时是 | 已保存行 ID；新行为空 |
| `items[].sourceType` | String | 是 | `STAFF_ASSIGNMENT/MANUAL/SYSTEM` |
| `items[].staffId` | String(Long) | 否 | 人员安排 ID；聚合或手工行可空 |
| `items[].detail` | Object | 是 | 由路径角色固定，结构见下表 |
| `items[].reimburse` | Decimal | 否 | 小额报销，空按 0 |
| `items[].paymentMethod` | String | 否 | 空按 `COMPANY_PAID` |
| `items[].voucherUrls` | String[] | 否 | 最多 9 个 http/https URL |
| `items[].settlementConfirmStatus` | String | 是 | `UNCONFIRMED/CONFIRMED` |
| `items[].settleStatus` | String | 否 | 辅助人员 `PENDING/COMPLETED`，空按 `PENDING` |
| `items[].settledDate` | Date | 否 | 辅助人员结算日期 |
| `items[].transferRef` | String | 条件必填 | `settleStatus=COMPLETED` 时必填，最长 128 字符 |
| `items[].remark` | String | 否 | 最长 500 字符 |

**角色 `detail` 字段**

| 路径角色 | 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|---|
| leaders | `days` | Integer | 是 | 天数，最小 0 |
| leaders | `per_day` | Decimal | 是 | 每天费用，最小 0 |
| drivers | `days` | DriverDay[] | 是 | 服务日明细 |
| drivers | `days[].service_date` | Date | 是 | 服务日期 |
| drivers | `days[].vehicle_brief` | String | 否 | 车辆摘要 |
| drivers | `days[].daily_fee` | Decimal | 是 | 日费，仅回显，不计入人员费用 |
| drivers | `days[].is_used` | Boolean | 否 | 是否使用 |
| drivers | `days[].note` | String | 否 | 备注 |
| drivers | `extra_cost` | Decimal | 否 | 额外费用，空按 0 |
| drivers | `extra_breakdown` | ExtraItem[] | 否 | 合计必须等于 `extra_cost` |
| drivers | `extra_breakdown[].name` | String | 是 | 费用名 |
| drivers | `extra_breakdown[].amount` | Decimal | 是 | 金额，最小 0 |
| drivers | `extra_breakdown[].note` | String | 否 | 备注 |
| guides/photographers | `persons` | Person[] | 是 | 人员计费明细 |
| guides/photographers | `persons[].name` | String | 是 | 姓名 |
| guides/photographers | `persons[].days` | Integer | 是 | 天数，最小 0 |
| guides/photographers | `persons[].per_day` | Decimal | 是 | 每天费用，最小 0 |
| guides/photographers | `persons[].note` | String | 否 | 备注 |
| others | `items` | OtherItem[] | 是 | 其他人员费用项 |
| others | `items[].name` | String | 是 | 费用名称 |
| others | `items[].amount` | Decimal | 是 | 金额，最小 0 |
| others | `items[].note` | String | 否 | 备注 |

**GET 响应 `data`**

| 字段 | 类型 | 说明 |
|---|---|---|
| `totalActualCost` | Decimal | 当前 Tab 实际费用合计 |
| `items` | StaffItem[] | 已保存行；未保存时可返回候选草稿 |
| `items[].id` | String(Long) | 行 ID；未保存候选为空 |
| `items[].sourceType` | String | 来源编码 |
| `items[].sourceTypeName` | String | 人员安排/手工/系统 |
| `items[].staffId` | String(Long) | 人员安排 ID，可空 |
| `items[].staffName` | String | 人员姓名或聚合摘要 |
| `items[].detail` | Object | 对应角色明细 |
| `items[].totalPlannedCost` | Decimal | 计划成本 |
| `items[].totalActualCost` | Decimal | 实际成本 |
| `items[].reimburse` | Decimal | 小额报销 |
| `items[].paymentMethod` | String | 付款方式 |
| `items[].voucherUrls` | String[] | 凭证 URL |
| `items[].settlementConfirmStatus` | String | 确认状态 |
| `items[].settlementConfirmStatusName` | String | 未确认/已确认 |
| `items[].settleStatus` | String | 辅助人员结算状态；主报账人为空 |
| `items[].settledDate` | Date | 结算日期 |
| `items[].transferRef` | String | 转账流水号 |
| `items[].isPrimaryReporter` | Boolean | 是否主报账人 |
| `items[].remark` | String | 备注 |

PUT 成功返回统一成功包，`data=null`。

### 3.5 餐食费用

**接口**

| 方法 | 路径 | 请求/响应 |
|---|---|---|
| GET | `/v3/admin/order/:orderId/settlement/meals` | `data` 为 MealItem[] |
| POST | `/v3/admin/order/:orderId/settlement/meals` | 请求 MealSave；响应 MealItem |
| PUT | `/v3/admin/order/:orderId/settlement/meals/:settlementId` | 请求 MealSave；响应 MealItem |

**MealSave 请求**

| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| `mealType` | String | 是 | `BREAKFAST/LUNCH/DINNER` |
| `mealDate` | Date | 否 | 发生日期 |
| `mealName` | String | 是 | 最长 200 字符 |
| `quantity` | Integer | 是 | 1～10000 |
| `unitPrice` | Decimal | 是 | 最多 8 位整数、2 位小数，最小 0 |
| `paymentMethod` | String | 是 | `CASH_PAID/COMPANY_PAID/SIGNED` |
| `sourceType` | String | 是 | `MEAL_ASSIGNMENT/MANUAL/SYSTEM`；新增接口请传 `MANUAL` |
| `settlementConfirmStatus` | String | 是 | `UNCONFIRMED/CONFIRMED` |
| `voucherUrls` | String[] | 否 | 最多 9 个 http/https URL |
| `remark` | String | 否 | 最长 512 字符 |

`unitPrice × quantity` 不得超过 `99999999.99`。

**MealItem 响应**

| 字段 | 类型 | 说明 |
|---|---|---|
| `id` | String(Long) | 餐食费用 ID |
| `mealType` | String | 餐型 |
| `mealDate` | Date | 发生日期 |
| `mealName` | String | 餐食名称 |
| `quantity` | Integer | 数量 |
| `unitPrice` | String(Decimal) | 单价 |
| `actualAmount` | String(Decimal) | 实际金额 |
| `paymentMethod` | String | 付款类型 |
| `sourceType` | String | 来源编码 |
| `sourceTypeName` | String | 餐饮安排/手工/系统 |
| `voucherUrls` | String[] | 凭证 URL |
| `settlementConfirmStatus` | String | 确认状态 |
| `settlementConfirmStatusName` | String | 未确认/已确认 |
| `remark` | String | 备注 |

POST 创建的是手工行，必须先传 `UNCONFIRMED`；PUT 已存在行时可传 `CONFIRMED`，且不得改变原 `sourceType`。

### 3.6 其他支出

**接口**

| 方法 | 路径 | 请求/响应 |
|---|---|---|
| GET | `/v3/admin/order/:orderId/settlement/other-expenses` | `data` 为 OtherExpenseItem[] |
| POST | `/v3/admin/order/:orderId/settlement/other-expenses` | 请求 OtherExpenseSave；响应 OtherExpenseItem |
| PUT | `/v3/admin/order/:orderId/settlement/other-expenses/:settlementId` | 请求 OtherExpenseSave；响应 OtherExpenseItem |

**OtherExpenseSave 请求**

| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| `expenseType` | String | 是 | `FUEL/TOLL/PARKING/RENTAL/MAINTENANCE/OTHER` |
| `projectName` | String | 是 | 最长 200 字符 |
| `expenseDate` | Date | 否 | 发生日期 |
| `actualAmount` | Decimal | 是 | 最多 8 位整数、2 位小数，最小 0 |
| `paymentMethod` | String | 是 | `CASH_PAID/COMPANY_PAID/SIGNED` |
| `settlementConfirmStatus` | String | 是 | `UNCONFIRMED/CONFIRMED` |
| `voucherUrls` | String[] | 否 | 最多 9 个 http/https URL |
| `remark` | String | 否 | 最长 512 字符 |

**OtherExpenseItem 响应**

| 字段 | 类型 | 说明 |
|---|---|---|
| `id` | String(Long) | 其他支出 ID |
| `expenseType` | String | 支出类型 |
| `projectName` | String | 项目名称 |
| `expenseDate` | Date | 发生日期 |
| `actualAmount` | String(Decimal) | 实际金额 |
| `paymentMethod` | String | 付款类型 |
| `sourceType` | String | `MANUAL/SYSTEM` |
| `sourceTypeName` | String | 手工/系统 |
| `voucherUrls` | String[] | 凭证 URL |
| `settlementConfirmStatus` | String | 确认状态 |
| `settlementConfirmStatusName` | String | 未确认/已确认 |
| `remark` | String | 备注 |

POST 创建的是 `MANUAL` 行且必须先为 `UNCONFIRMED`；已有 ID 后通过 PUT 可改为 `CONFIRMED`。

### 3.7 其他收入

**接口**

| 方法 | 路径 | 请求/响应 |
|---|---|---|
| GET | `/v3/admin/order/:orderId/settlement/other-incomes` | `data` 为列表聚合对象 |
| POST | `/v3/admin/order/:orderId/settlement/other-incomes` | 请求 Create；响应 OtherIncomeItem |
| PUT | `/v3/admin/order/:orderId/settlement/other-incomes/:incomeId` | 请求 Update；响应 OtherIncomeItem |

**Create/Update 请求**

| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| `requestId` | String | 仅 POST 是 | 同订单永久唯一，最长 64 字符 |
| `incomeDate` | Date | 是 | 收入日期 |
| `projectName` | String | 是 | 最长 100 字符 |
| `projectCategory` | String | 是 | 启用字典值，最长 64 字符 |
| `specification` | String | 否 | 票种/规格，最长 100 字符 |
| `quantity` | Decimal | 是 | 最多 8 位整数、4 位小数，最小 0 |
| `unitPrice` | Decimal | 是 | 最多 8 位整数、2 位小数，最小 0 |
| `settlementAmount` | Decimal | 是 | 最多 8 位整数、2 位小数，必须大于 0，且等于数量乘单价（四舍五入到 2 位） |
| `paymentMethod` | String | 是 | `CASH_PAID/COMPANY_PAID/SIGNED` |
| `settlementConfirmStatus` | String | 是 | `UNCONFIRMED/CONFIRMED` |
| `voucherUrls` | String[] | 否 | 最多 9 个 http/https URL |
| `remark` | String | 否 | 最长 500 字符 |

**OtherIncomeItem 响应**

| 字段 | 类型 | 说明 |
|---|---|---|
| `id` | String(Long) | 其他收入 ID |
| `requestId` | String | 手工新增幂等 ID；系统投影为空 |
| `incomeDate` | Date | 收入日期 |
| `projectName` | String | 项目名称 |
| `projectCategory` | String | 项目类别 |
| `projectCategoryName` | String | 项目类别名称 |
| `specification` | String | 票种/规格 |
| `quantity` | Decimal | 数量 |
| `unitPrice` | Decimal | 核算单价 |
| `settlementAmount` | Decimal | 核算金额 |
| `paymentMethod` | String | 付款类型 |
| `paymentMethodName` | String | 付款类型名称 |
| `voucherUrls` | String[] | 凭证 URL |
| `settlementConfirmStatus` | String | 确认状态 |
| `settlementConfirmStatusName` | String | 未确认/已确认 |
| `remark` | String | 备注 |
| `sourceType` | String | `MANUAL/SYSTEM` |
| `sourceTypeName` | String | 手工/系统 |
| `sourceId` | String(Long) | 关联来源 ID |

**GET 聚合响应**

| 字段 | 类型 | 说明 |
|---|---|---|
| `items` | OtherIncomeItem[] | 其他收入明细 |
| `deductions` | Deduction[] | 只读减费明细 |
| `deductions[].id` | String(Long) | 减费 ID |
| `deductions[].discountName` | String | 减费名称 |
| `deductions[].discountAmount` | Decimal | 减费金额 |
| `deductions[].sourceType` | String | 减费来源 |
| `deductions[].sourceId` | String(Long) | 来源业务 ID |
| `deductions[].createdAt` | DateTime | 创建时间 |
| `summary.surchargeAmount` | Decimal | 有效增费合计 |
| `summary.discountAmount` | Decimal | 有效减费合计 |
| `summary.netAdjustmentAmount` | Decimal | 增费减去减费 |

手工新增行的公开来源为 `MANUAL`；系统自动投影行公开来源为 `SYSTEM`。原公开值 `ORDER_SURCHARGE` 不再返回。

### 3.8 完成核单

**接口**：`POST /v3/admin/order/:orderId/settlement/finalize`

**请求体**

| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| `remark` | String | 否 | 整体备注，最长 500 字符 |
| `reimbursementExpectedSourceFingerprint` | String | 是 | 主报账表 64 位小写 SHA-256 指纹 |
| `groupExpectedSourceFingerprint` | String | 是 | 单团核算表 64 位小写 SHA-256 指纹 |
| `reimbursementConfirmation` | Object | 是 | 主报账确认凭据 |
| `reimbursementConfirmation.transferDate` | Date | 条件必填 | 主报账净额非 0 时必填 |
| `reimbursementConfirmation.transferRef` | String | 条件必填 | 主报账净额非 0 时必填，最长 128 字符 |
| `reimbursementConfirmation.advanceSettledFlag` | Boolean | 是 | 预支是否已处理；`false` 是合法值 |
| `reimbursementConfirmation.signedVoucher` | Object | 是 | 签字凭证 |
| `reimbursementConfirmation.signedVoucher.files` | File[] | 是 | 1～9 项 |
| `reimbursementConfirmation.signedVoucher.files[].name` | String | 否 | 文件名，最长 255 字符 |
| `reimbursementConfirmation.signedVoucher.files[].url` | String | 是 | 文件 URL，最长 1024 字符 |
| `reimbursementConfirmation.signedVoucher.note` | String | 否 | 最长 500 字符 |

**响应 `data`**

| 字段 | 类型 | 说明 |
|---|---|---|
| `summaryId` | String(Long) | 核单汇总 ID |
| `finalSnapshotId` | String(Long) | 终态快照 ID |
| `finalSnapshotVersionNo` | Integer | 快照版本 |
| `finalSnapshotStatus` | String | 成功时为 `FINALIZED` |
| `orderId` | String(Long) | 订单 ID |
| `settledAt` | DateTime | 核单完成时间 |
| `totalAmount` | String(Decimal) | 订单总金额快照 |
| `paidAmount` | String(Decimal) | 已付金额快照 |
| `balanceAmount` | String(Decimal) | 尾款金额快照 |
| `roomCost` | String(Decimal) | 住宿实际成本 |
| `ticketCost` | String(Decimal) | 门票实际成本 |
| `staffCost` | String(Decimal) | 人员费用实际成本 |
| `subsidyCost` | String(Decimal) | 补助实际成本 |
| `mealCost` | String(Decimal) | 餐食实际成本 |
| `vehicleCost` | String(Decimal) | 车辆实际成本 |
| `otherExpenseCost` | String(Decimal) | 其他支出实际成本 |
| `insurancePremium` | String(Decimal) | 保险实际保费 |
| `totalActualCost` | String(Decimal) | 总实际成本 |
| `driverTransferAmount` | String(Decimal) | 给主报账人的转回金额 |
| `profitAmount` | String(Decimal) | 公司毛利 |
| `profitRate` | Decimal | 毛利率小数 |
| `orderStatusAfter` | String | 完成后的订单状态 |
| `mqTriggered` | Boolean | 当前固定为 `false` |
| `warnings` | String[] | 不阻塞完成核单的软预警 |

**业务边界**

- 住宿、门票/游玩、餐食、车辆、导游、摄影、其他收入、其他支出八类来源必须同步完成。
- 任一非空分类存在 `UNCONFIRMED` 行时，finalize 返回 584310，不生成终态快照。
- 系统来源事实变化会使对应行重新变为 `UNCONFIRMED`；应刷新、复核并保存后再 finalize。

## 4. 接口入参汇总

| 输入类型 | 适用接口 | 关键变化 |
|---|---|---|
| 路径参数 | 全部 26 个接口 | `orderId` 必填；行级修改另有 `settlementId/incomeId` |
| 全量明细 | 住宿、门票、车辆、五个人员 Tab | 必须提交完整 `items`；新增手工行先传 `UNCONFIRMED` |
| 单行保存 | 餐食、其他支出、其他收入 | POST 新增先未确认，PUT 已有行可确认 |
| 车辆版本 | 车辆 PUT | 必须原样回传最近 GET 的 `version` |
| 完成核单凭据 | finalize | 双报告指纹 + 主报账转账/签字凭据 |

完整字段、必填性和校验已分别内联在 §3.1～§3.8。

## 5. 出参字段汇总

| 变化 | 适用响应 |
|---|---|
| 新增 `sourceType/sourceTypeName` | 人员、餐食、其他支出；其他收入的来源语义调整 |
| 新增 `settlementConfirmStatus/settlementConfirmStatusName` | 门票；餐食、其他支出、人员补齐名称 |
| Long ID 按字符串返回 | 住宿、门票、人员、车辆以及已有明确字符串序列化的资金明细 |
| 新车辆草稿结构 | `orderId/version/totalAmount/allConfirmed/items` |
| 删除车辆内部/兼容字段 | 不再输出 `frozen/requirementId/settlementReady/totalVehicleFee` 及 Fleet 对账字段 |

## 6. 枚举 / 数据字典

### 6.1 住宿 `sourceType`

| 值 | 中文 | 说明 |
|---|---|---|
| `HOUSE_ASSIGNMENT` | 配房结果 | 系统配房来源 |
| `MANUAL` | 手工 | 管理后台手工新增 |
| `SYSTEM` | 系统 | 其他系统来源；替代旧公开值 `TEMPLATE` |
| `TEMPLATE` | 历史兼容 | 仅请求兼容，响应不返回 |

### 6.2 门票 `sourceType`

| 值 | 中文 | 说明 |
|---|---|---|
| `SCENIC_ASSIGNMENT` | 景区 | 景区安排来源 |
| `ACTIVITY_ASSIGNMENT` | 游玩项目 | 活动安排来源 |
| `MANUAL` | 手工 | 手工新增；响应统一值 |
| `CUSTOM_ASSIGNMENT` | 历史兼容 | 仅请求兼容，响应归一为 `MANUAL` |

### 6.3 餐食 `sourceType`

| 值 | 中文 | 说明 |
|---|---|---|
| `MEAL_ASSIGNMENT` | 餐饮安排 | 系统餐饮安排来源 |
| `MANUAL` | 手工 | 管理后台新增 |
| `SYSTEM` | 系统 | 其他系统来源 |

### 6.4 车辆 `sourceType`

| 值 | 中文 | 说明 |
|---|---|---|
| `FLEET` | 车务 | 车务同步行，业务字段不可改删 |
| `MANUAL` | 手工 | 核单页手工补录 |

### 6.5 人员 `sourceType`

| 值 | 中文 | 说明 |
|---|---|---|
| `STAFF_ASSIGNMENT` | 人员安排 | 系统人员安排来源 |
| `MANUAL` | 手工 | 手工新增 |
| `SYSTEM` | 系统 | 其他系统来源 |

### 6.6 其他收入/其他支出 `sourceType`

| 值 | 中文 | 说明 |
|---|---|---|
| `MANUAL` | 手工 | 管理后台创建 |
| `SYSTEM` | 系统 | 自动投影或系统来源 |

### 6.7 `settlementConfirmStatus`

| 值 | 中文 | 说明 |
|---|---|---|
| `UNCONFIRMED` | 未确认 | 首次系统同步或手工新行的初始状态 |
| `CONFIRMED` | 已确认 | 已有行复核后保存的状态 |

### 6.8 `paymentMethod`

| 值 | 中文 | 说明 |
|---|---|---|
| `CASH_PAID` | 现付 | 现场/主报账人支付 |
| `COMPANY_PAID` | 公司付款 | 公司直接支付 |
| `SIGNED` | 签单 | 签单结算 |

### 6.9 餐型 `mealType`

| 值 | 中文 |
|---|---|
| `BREAKFAST` | 早餐 |
| `LUNCH` | 午餐 |
| `DINNER` | 晚餐 |

### 6.10 支出类型 `expenseType`

| 值 | 中文 |
|---|---|
| `FUEL` | 油费 |
| `TOLL` | 过路费 |
| `PARKING` | 停车费 |
| `RENTAL` | 租赁费 |
| `MAINTENANCE` | 维修保养 |
| `OTHER` | 其他 |

### 6.11 人员辅助结算状态 `settleStatus`

| 值 | 中文 | 说明 |
|---|---|---|
| `PENDING` | 待结算 | 默认值 |
| `COMPLETED` | 已结算 | 必须同时提交 `transferRef` |

## 7. 错误码

| code | 含义 | 触发场景 |
|---|---|---|
| `400` | 参数校验失败 | 必填缺失、格式/长度/枚举错误、车辆或人员请求出现未知字段 |
| `584001/584010/584020/584050/584070` | 订单不存在 | 对应住宿、门票、人员、finalize 或通用查询找不到订单 |
| `584002/584011/584021` | 当前状态不可写 | 住宿、门票、人员费用不在允许的核单阶段 |
| `584006/584012/584022` | 枚举或角色非法 | 住宿付款、门票来源、人员角色非法 |
| `584067/584068/584069` | 住宿资源无效或暂不可用 | 手工酒店/房型无效或资源服务不可用 |
| `584071` | 无权访问该订单 | 公司隔离或现有订单权限不满足 |
| `584073` | 其他收入不存在 | `incomeId` 不属于当前订单 |
| `584074` | 当前状态不允许修改其他收入 | 核单状态不可写 |
| `584076` | 其他收入金额不一致 | `settlementAmount != quantity × unitPrice` |
| `584077` | 存在未确认的其他收入 | 生成报告或完成核单前仍有其他收入未确认 |
| `584086` | 无权修改核单资金数据 | 非主管、管理员或财务 |
| `584087` | `requestId` 冲突 | 同订单相同 `requestId` 被另一笔请求占用 |
| `584089` | 核单或结算已完成 | 再次修改资金明细 |
| `584090/584091` | 餐食/其他支出不存在 | 行 ID 不属于当前订单 |
| `584092` | 存在未确认的人员费用 | 生成报告或完成核单前仍有人员费用未确认 |
| `584094/584095` | 餐食/其他支出字段非法 | 字段越界或试图改变系统来源 |
| `584096/584097` | 付款方式/凭证非法 | 枚举错误或 URL 数量、格式错误 |
| `584098` | 餐食或其他支出存在未确认记录 | 生成报告或完成核单前仍有餐食/其他支出未确认 |
| `584100/584101/584102` | 车辆来源不可用/未就绪/为空 | 车务数据不可读、未完结/未确认、无可核单费用 |
| `584103/584104/584105` | 其他收入字典非法或不可用 | 项目类别、规格无效或字典不可用 |
| `584106` | 确认状态非法 | 非 `UNCONFIRMED/CONFIRMED` |
| `584107` | 手工新增行必须先未确认 | `id=null` 的手工新行直接传 `CONFIRMED` |
| `584108` | 车辆草稿版本冲突 | PUT 的 `version` 已过期 |
| `584109` | 车务来源字段不可改删 | 修改/漏传 `FLEET` 行的权威字段 |
| `584310` | 八类核单未全部确认或数据已变化 | finalize 前有非空未确认行、来源未就绪 |
| `584315` | 报告来源已变化 | finalize 双报告指纹过期 |
| `584317` | 当前报告状态不允许操作 | finalize 凭据结构或状态不满足 |
| `584325` | 完成核单必须提交当前指纹 | 双报告指纹缺失 |

## 验证证据（8. 示例：典型 / 边界 / 异常）

已完成以下测试环境路由与业务负向验证：

- 部署任务 `80f1695a` 成功，order-v3 主、副实例滚动完成。
- 两个核算中订单实调
  `GET /v3/admin/order/:orderId/settlement/step3/vehicles`
  均进入新代码并返回业务前置码 `584102`，证明新路由已生效。
- 旧 `GET /v3/admin/order/:orderId/settlement/vehicle-fees` 返回 `404`，证明旧入口已下线。

本节下列 JSON 是按已合并 Controller/VO 契约给出的自包含调用示例。由于测试订单缺少可核单车辆费用，本次未取得车辆 DTO 正向数据，也未完成车辆链路 Full E2E；不得把上述 584102 负向结果描述为正向业务通过。

### 8.1 典型成功：确认车辆系统来源行

**请求**

```http
PUT /v3/admin/order/900000000001/settlement/step3/vehicles
Authorization: Bearer <JWT>
Content-Type: application/json
```

```json
{
  "version": 3,
  "items": [
    {
      "id": "930000000001",
      "sourceType": "FLEET",
      "serviceDate": "2026-07-30",
      "vehicleId": "880000000001",
      "vehiclePlate": "藏A12345",
      "vehicleModelId": "870000000001",
      "vehicleModelName": "七座商务车",
      "driverId": "860000000001",
      "driverName": "张师傅",
      "amount": 1200.00,
      "paymentMethod": "COMPANY_PAID",
      "settlementConfirmStatus": "CONFIRMED",
      "remark": "金额已核对",
      "voucherUrls": []
    }
  ]
}
```

**响应**

```json
{
  "code": 200,
  "message": "成功",
  "data": {
    "orderId": "900000000001",
    "version": 4,
    "totalAmount": 1200.00,
    "allConfirmed": true,
    "items": [
      {
        "id": "930000000001",
        "sourceType": "FLEET",
        "sourceTypeName": "车务",
        "serviceDate": "2026-07-30",
        "vehicleId": "880000000001",
        "vehiclePlate": "藏A12345",
        "vehicleModelId": "870000000001",
        "vehicleModelName": "七座商务车",
        "driverId": "860000000001",
        "driverName": "张师傅",
        "amount": 1200.00,
        "paymentMethod": "COMPANY_PAID",
        "paymentMethodName": "公司付款",
        "settlementConfirmStatus": "CONFIRMED",
        "settlementConfirmStatusName": "已确认",
        "remark": "金额已核对",
        "voucherUrls": []
      }
    ]
  },
  "success": true
}
```

### 8.2 边界情况：合法空车辆草稿

无当前用车需求时，GET 可返回合法空集；`allConfirmed=true` 表示“空集中没有未确认行”，不表示存在车辆费用。

**请求**

```http
GET /v3/admin/order/900000000001/settlement/step3/vehicles
Authorization: Bearer <JWT>
```

无请求体。

**响应**

```json
{
  "code": 200,
  "message": "成功",
  "data": {
    "orderId": "900000000001",
    "version": 1,
    "totalAmount": 0.00,
    "allConfirmed": true,
    "items": []
  },
  "success": true
}
```

### 8.3 业务失败：手工新行直接确认

**请求**

```http
POST /v3/admin/order/900000000001/settlement/meals
Authorization: Bearer <JWT>
Content-Type: application/json
```

```json
{
  "mealType": "LUNCH",
  "mealDate": "2026-07-30",
  "mealName": "团队午餐",
  "quantity": 10,
  "unitPrice": 50.00,
  "paymentMethod": "CASH_PAID",
  "sourceType": "MANUAL",
  "settlementConfirmStatus": "CONFIRMED",
  "voucherUrls": [],
  "remark": null
}
```

**响应**

```json
{
  "code": 584107,
  "message": "手工新增核单明细必须先保存为未确认",
  "data": null,
  "success": false
}
```

## 9. 业务边界

- ✅ **系统来源首次同步**：生成已有 ID 的 `UNCONFIRMED` 行；复核后可直接在对应 PUT 中保存为 `CONFIRMED`。
- ✅ **手工新增**：第一次必须保存为 `UNCONFIRMED`；接口返回 ID 后，第二次更新才允许保存为 `CONFIRMED`。
- ✅ **来源统一**：手工行统一公开为 `MANUAL`；系统行公开为各分类系统来源值，无法细分的系统行为 `SYSTEM`。
- ❌ **不可混用确认和辅助结算状态**：`settlementConfirmStatus` 表示核单确认；人员 `settleStatus` 表示辅助人员款项是否结清。
- ❌ **不可修改系统权威字段**：系统来源事实变化后应重新 GET；车辆 `FLEET` 行不得由前端改删。
- ⚠️ **finalize 门禁**：八个核单分类来源必须就绪，且每个非空分类全部逐行 `CONFIRMED`。
- ⚠️ **空分类**：合法空分类没有未确认行，但来源同步仍必须就绪；`allConfirmed=true` 不等于有费用。

## 10. 修改前后对比

### 10.1 字段级对比

| 范围 | 改前 | 改后 |
|---|---|---|
| 住宿系统来源 | `TEMPLATE` | `SYSTEM`；`TEMPLATE` 仅兼容旧入参 |
| 门票手工来源 | 可能返回 `CUSTOM_ASSIGNMENT` | 统一返回 `MANUAL` |
| 其他收入来源 | `ORDER_SURCHARGE` | `MANUAL` 或 `SYSTEM` |
| 门票行确认 | 无逐行确认字段 | 新增 `settlementConfirmStatus/Name` |
| 人员请求行 | 无 `id/sourceType/settlementConfirmStatus` | 三字段纳入全量保存契约 |
| 餐食请求/响应 | 无公开来源，确认名称不完整 | 增加 `sourceType/sourceTypeName/settlementConfirmStatusName` |
| 其他支出响应 | 无公开来源和确认名称 | 增加 `sourceType/sourceTypeName/settlementConfirmStatusName` |
| 住宿/门票/人员 ID | 部分按 JSON 数字输出 | 明细 `id`、来源 ID 按字符串输出 |
| 车辆顶层响应 | `frozen/requirementId/settlementReady/totalVehicleFee/items` | `orderId/version/totalAmount/allConfirmed/items` |
| 车辆行响应 | 暴露 Fleet 对账、冻结和自动车费字段 | 仅输出核单需要的来源、车辆、司机、金额、付款、确认、凭证字段 |

### 10.2 行为级对比

| 行为 | 改前 | 改后 |
|---|---|---|
| 系统派生行初始状态 | 分类规则不一致，部分直接视为已确认 | 统一先 `UNCONFIRMED`，用户复核后保存为 `CONFIRMED` |
| 手工新增并确认 | 部分接口允许一次保存即确认 | 必须先未确认，取得 ID 后再确认 |
| 车辆入口 | 旧 `/settlement/vehicle-fees` 已下线且无新独立编辑入口 | 使用 `/settlement/step3/vehicles` GET/PUT |
| 车辆并发保存 | 无前端草稿版本 | 必须携带 `version`，冲突时刷新 |
| 完成核单 | 分类确认来源不完全统一 | 只接受八类来源就绪且非空行全部已确认的数据 |

## 11. 影响评估 / 回滚

### 11.1 影响评估

- **是否破坏向后兼容**：是。车辆入口和响应结构为新契约；其他收入 `sourceType` 值发生变化；多个请求/响应新增确认与来源字段。
- **前端是否必须同步上线**：是。需切换车辆接口、适配字符串 ID、新来源枚举和“先保存未确认、再确认”的交互。

### 11.2 回滚说明

若后端回滚，前端需同时回滚车辆 Step3 新入口及新增字段依赖；旧
`/settlement/vehicle-fees` 两个接口在本次变更前已下线，不能作为回滚兜底。

## 12. 注意事项

- 删除对旧 `GET /settlement/vehicle-fees` 和 `POST /settlement/vehicle-fees/confirm` 的任何残留调用。
- 车辆保存必须回传最近 GET 的 `version` 和全部 `FLEET` 行；584108 时刷新后让用户重新确认。
- 不再把 `ORDER_SURCHARGE`、`TEMPLATE`、`CUSTOM_ASSIGNMENT` 当作新响应值。
- 所有 Long 类型字符串 ID 按字符串比较、传递，不转为 JavaScript Number。
- finalize 返回 584310 时，应刷新相关 Tab；来源变化可能已把已确认行重新置为未确认。

## 13. 关联 / 联系人

### 13.1 链接

- **Issue**: [#5356](https://git.1814.love:8443/wx/HL/issues/5356)
- **PR**: [#5362](https://git.1814.love:8443/wx/HL/pulls/5362)
- **Feature commit**: [6e396f6fc4](https://git.1814.love:8443/wx/HL/commit/6e396f6fc48fbf6581e87224bb85f2c811759727)
- **Merge commit**: [cfac945db2](https://git.1814.love:8443/wx/HL/commit/cfac945db268640b0b9e60b4d8c7ab55739a69e3)

### 13.2 联系人

- **后端负责人**: @yaosutu
