---
schema: "hl-changelog/v2"
ticket: "5818"
title: "车务全量代码审计整改：保险错误码迁段(540033-540037→601200-601204)、新增多个业务码、对账/看板响应结构与数值变化、若干入参新增校验"
consumer: "admin"
author: "wx(GIT)"
change_type: "修改接口"
backend_status: "deployed"
gateway_status: "not_required"
frontend_status: "implemented"
frontend_owner: "mmg"
frontend_ref: "46a57557"
target_release: ""
verified_at: "2026-08-11"
status_note: "PR #5837 已合并 dev-v3 并部署测试服，全量 3485 用例 0 失败、ArchTest 12/12、Flyway 迁移实测通过（540033 残留 0 / 601200 恰好 7 行）。本条汇总一轮全量审计整改中**所有前端可见**的变化，按「必须改」「按需改」「只需知晓」三档排列，逐条给了触发条件与处置口径。前端已实现(46a57557)：实证后 A1 保险错误码(前端零引用)/A2 dayPrice(仅展示经 formatPrice 内部 Number 安全)/A5 到期看板(前端固定白名单传值)/B3 opType(无下拉)/B4 priceSource(DAILY_FEE_SOURCES 已含 FREE)/B5(纯后端)零改动；实际落地子集——A6 模板编辑 isDefault 恒参与 diff 对比、submit 写接口前按编辑态裁剪(编辑未变省略避免 600804，顺带修复仅切换默认被无变化短路误拦)；A3/A4 useAssignFlow 新增 605063(回执损坏禁自动重试)/605064(全程槽逐日部分免费)专用提示；B1 对账行标识改后端权威 fleet 字段(兜底行 fleetTeamId 可为 null,旧数据回退)并抽 reconAdapter 纯函数；B2 保险分来源金额优先读 manualAmount/baoyouAmount+sourceSubtotals(消除同司机跨来源矛盾行,兼容旧响应回退)；B6 清 CAPACITY_INSUFFICIENT/passengerCapacity/capacityGap 死契约。测试:reconAdapter.spec 6+EditModal.spec 4+useAssignFlow final-confirm +2+baselineDifference 同步,fleet 全域 66 文件 618 用例全绿,checkpoint 10 文件含生产构建全过。"
updated_at: "2026-08-11"
base: "dev-v3"
---

# 车务：全量代码审计整改的前端契约变化（#5818 / PR #5837）

> **服务**: hl-fleet-service (8087/8187)
> **PR**: #5837
> **Issue**: [#5818](https://git.1814.love:8443/wx/HL/issues/5818)
> **背景**: 对 hl-fleet-service（740 文件约 10 万行）做了一轮规范/质量/逻辑三维全量审计并整改（确认 151 条缺陷，修复 127 条 + CR 整改）。绝大多数是后端内部修复，但有以下几类**前端能看见**的变化。

---

## 变更接口清单

| 接口 | 变化类型 | 见下文 |
|------|---------|--------|
| `GET /admin/fleet/insurance/tasks` | `errorCode` 值域迁段（存量数据一并改写） | A1 |
| `GET /admin/fleet/vehicles/options` | `dayPrice` number → string | A2 |
| `POST /admin/fleet/assignments/{assignmentId}/confirm` | 新增错误码 605063 | A3 |
| `POST /admin/fleet/assignments/requirements/{requirementId}/confirm` | 新增 605063、首次给出完整错误码表（605059/605062/605063）；`dailyDifferences[]` 删 2 字段 | A3 / B6 |
| `POST /admin/fleet/assignments` | 新增错误码 605064 | A4 |
| `POST /admin/fleet/assignments/batch` | 新增错误码 605064 | A4 |
| `POST /admin/fleet/assignments/{assignmentId}/change` | 新增错误码 605064 | A4 |
| `GET /admin/fleet/board/expiry` | `kinds`/`buckets` 非法值由静默忽略改为 100001 | A5 |
| `PUT /admin/fleet/message-templates/{id}` | `isDefault` 语义变化 + 新增 600804 | A6 |
| `GET /admin/fleet/reconciliation/cars` | `grandTotal.actual`/`diff` 数值变化；`fleets[]` 可能出现兜底行 | B1 |
| `GET /admin/fleet/reconciliation/insurance` | 数值变化；`drivers[]` 新增分来源金额字段 | B2 |
| `GET /admin/fleet/reconciliation/pending-compensations` | `opType` 白名单补 `INSURANCE` | B3 |
| `GET /admin/fleet/board/orders/{orderId}` | `priceSource` 新增 `FREE`；混态派车组展示区间修正 | B4 / B5 |
| `POST /admin/fleet/h5/tokens` | `expireDays` 新增 1-90 上限 | C1 |
| 操作时间线查询 | `page` 新增 100000 上限 | C1 |
| `POST` / `PUT /admin/fleet/vehicles` | `primaryDriverId` 新增存在性校验（600205） | C2 |
| `POST /admin/fleet/insurance/tasks/{taskId}/retry` | 补登记 601210-601215（原返 100500） | C3 |
| `POST /admin/fleet/drivers/{driverId}/insurance/purchase`、`/insure` | 降级时由 100500 改返 605601 | C3 |
| `POST /admin/fleet/driver-pending/{pendingId}/approve` | 删 605022，新增 600205 / 600210 | C3 |

---

## 🔴 A. 必须改（不改会踩坑）

### A1. 保险任务 `errorCode` 值域迁段：540033-540037 → 601200-601204

**接口**：`GET /admin/fleet/insurance/tasks`（列表）响应 `errorCode` 字段。

**原因**：车务此前在 order 服务的 5xxxxx 段私铸错误码，其中 **540033 / 540034 与 hl-order-service-v3 已注册的真码撞号且语义完全不同**（order 侧 540033 是「被保人缺少出生日期」、540034 是「产品已下架/不可售」）。现已迁到 fleet 自有的 601200-601299 段。

| 旧码 | 新码 | 含义 |
|------|------|------|
| 540033 | **601200** | 司机年保未覆盖服务日 |
| 540034 | **601201** | 保险台账与司机年保档案不一致，需人工核对 |
| 540035 | **601202** | 退保后保障状态待复核 |
| 540036 | **601203** | 司机保险档案在处理期间已变更 |
| 540037 | **601204** | 历史取消事件缺少取消前状态 |

⚠️ **存量数据已随 Flyway 迁移一并改写**（测试服实测：迁移后 540033-540037 残留 0）。所以**发版当天所有历史待处理行的码会同时跳变**。

**前端处置**：若在任何地方硬编码过 540033-540037 做分支、文案映射或埋点，必须同步改成 601200-601204。

⚠️ **注意 `errorCode` 这一列是两个来源合用**：601200-601204 是车务自有的失败原因码，其余是保险服务上游业务码原样透传（如 540007 无匹配费率）。**不能按段位反推服务归属，也不能把它当 HTTP 响应码用。**

### A2. 车辆下拉 `dayPrice` 由 JSON number 改为 string

**接口**：`GET /admin/fleet/vehicles/options` 的 `dayPrice`。

`450.00`（number）→ `"450.00"`（string），与 `VehicleModelRespVO.basePrice` 口径统一（金额统一字符串下发，避免 JS 浮点精度问题）。

**前端处置**：参与计算前必须 `Number(dayPrice)`。否则 `dayPrice * days` 会退化成字符串拼接（`"450.00450.00"`）、`dayPrice.toFixed(2)` 直接 TypeError、`dayPrice > 0` 变成字符串比较。

### A3. 派车确认新增错误码 605063（不可自愈终态，**禁止自动重试**）

**接口**：`POST /admin/fleet/assignments/{assignmentId}/confirm` 与 `POST /admin/fleet/assignments/requirements/{requirementId}/confirm`。

```json
{ "code": 605063, "msg": "原子确认回执已损坏，无法幂等重放，请联系管理员" }
```

原来这个场景走全局兜底返 `100500 系统繁忙`，前端会落 default 分支甚至无脑重试。

**前端处置**：**这是不可自愈终态**——同 requestId 重试永远返回同码。不得自动重试、不得静默轮询，直接提示用户联系管理员人工处理。

另：需求级确认端点 `POST /admin/fleet/assignments/requirements/{requirementId}/confirm` 首次给出完整错误码表，前端若之前只处理 605062，需补 **605059**（同 requestId 用于不同确认内容，需换新 requestId）与 **605063**。

### A4. 全程槽「逐日部分免费」新增专用错误码 605064

**接口**：`POST /admin/fleet/assignments`（单派）、`POST /admin/fleet/assignments/batch`、`POST /admin/fleet/assignments/{assignmentId}/change`。

```json
{ "code": 605064, "msg": "全程槽不支持逐日部分免费，请整程统一计费或改用逐日派车方案" }
```

**触发条件**：对一个**全程槽**（一条行覆盖整个服务期）提交的 `chargeableServiceDates` 既不是全部服务日、也不是空集合，而是真子集。

**原因（重要）**：修复前这种输入会被静默算错——按首日单点判定后整行写入，首日免费就导致**整程车费归零**（资损）。现在明确拒绝。

**前端处置**：给该码专用提示，引导用户「整程统一计费」或「改用逐日派车方案」；该码**不可自动重试**（同参数必然同码）。若前端有按 `code == 100001` 的通用参数错误提示分支，此场景会不再命中它。

### A5. 到期看板 `kinds` / `buckets` 非法值由静默忽略改为报错

**接口**：`GET /admin/fleet/board/expiry`。

传白名单外的值原来是「静默忽略、返四类全量」，现在直接返 `100001`。

- `kinds` 合法值：`inspect` / `vehInsure` / `license` / `driverInsure`
- `buckets` 合法值：`expired` / `urgent` / `soon` / `watch` / `ok`
- **大小写敏感**（传 `LICENSE` / `Expired` 会报错）

**为什么这么改**：静默忽略会让「筛选不生效却返回全量」被误读成「该类目下真有这么多」。这是对齐 #5455 已定案口径——列表接口的 `statuses` / `vehicleTypeKeys` 本来就是拒绝非法值，到期接口是唯一例外。

### A6. 消息模板编辑 `isDefault` 语义变化 + 新增 600804

**接口**：`PUT /admin/fleet/message-templates/{id}`。

- `isDefault` 语义由「不传 = 非默认」改为「**编辑不传 = 保持原值**」（新增仍是不传 = 非默认）。
- 新增错误码 **600804**「默认模板不可取消默认」（与 delete 的 600803 对称）。

**前端处置**：若编辑表单里始终回传 `isDefault: false`，取消默认会从「静默成功」变为 600804 报错。请改为只在用户真的切换时才传该字段。

---

## 🟡 B. 按需改（数值/结构变化，看你怎么渲染）

### B1. 对账「车队」Tab 数值与结构变化

**接口**：`GET /admin/fleet/reconciliation/cars`

1. **`grandTotal.actual` 与 `diff` 的数值会变**：修复前从 `fleets[]` 累加，漏计了 `team_no` 为空的核单车费行与「本期已无 active prep 但有核单车费」的车队；现在对费用行全量求和。财务侧会看到**历史月份合计变大、diff 由偏负回正**——这是修 bug 不是回归。
2. **`fleets[]` 可能出现「兜底车队行」**：`fleet="unknown"` / `fleetName="未归属车队"`（team_no 缺失桶），或 `fleet=` 真实 teamCode（本期无 active prep 但有核单车费）。这类行 `vehicles=[]`、`estimatedTotal=payableTotal="0.00"`、`actualTotal`/`diff` 有值；能反查到车队的已补齐 `fleetTeamId`/`fleetType`/`settleType`/`settleMode`，真无主数据的 `unknown` 桶 `fleetTeamId` 仍为 null。
   **前端处置**：**行 key 请用 `fleet` 字段，不要用 `fleetTeamId`**；按 `settleMode` 分列渲染时要容忍兜底行。
   注：只在**未传** `fleets`/`fleetTeamIds` 时出现；显式筛车队时行为不变（且此时 `grandTotal.actual` 只统计被选中车队，比修复前更严格）。

### B2. 对账「保险」Tab 数值与结构变化

**接口**：`GET /admin/fleet/reconciliation/insurance`

1. **数值会变**：修复前用「该司机在区间内最早那一行」的 source 代表全月，导致逐日投保的司机整月被渲染成「无保险」且金额不入小计。现在逐行按 `driver_insurance_source` 分组求和。`fleets[].sourceSubtotals.manual/baoyou`、`grandTotal`、`typeCounts`、`drivers[]` 的多个字段**数值与取值都会变**。
2. **`drivers[]` 新增分来源金额字段**（`manualAmount` / `baoyouAmount`），`insuranceAmount` 保留为合计。这样「Σ drivers 按 source 分桶 == sourceSubtotals」可对账（修复前同司机跨来源会出现 `source=BAOYOU` 但金额含 MANUAL 的矛盾行）。

**建议**：部署后拉一个已有数据的月份做前后对比截图给财务确认。

### B3. 待补偿列表 `opType` 接受 `INSURANCE`

**接口**：`GET /admin/fleet/reconciliation/pending-compensations`

`opType` 白名单补齐 `INSURANCE`（此前传 `INSURANCE` 返 400，而该值确实会被写入）。前端下拉可补该项：`GENERATE` / `INVALIDATE` / `REACTIVATE` / `TRUNCATE` / `INSURANCE`。

### B4. 派车板逐日车费 `priceSource` 新增 `FREE`

**接口**：`GET /admin/fleet/board/orders/{orderId}` 的 `dailyVehiclePlan[].priceSource`

免费服务日此前被**错标为 `OVERRIDE`**（因为一个 `@TableField(exist=false)` 的字段恒为 null 导致整段判断是死分支），现在正确返回 `FREE`，且 `priceAdjustmentReason` 在 `FREE` 时返回免费豁免原因而非恒 null。金额不变（免费日仍 0.00）。

### B5. 派车板混态派车组的展示区间修正

**接口**：`GET /admin/fleet/board/orders/{orderId}`（组视图 / 槽位卡）

多日派车组在行程进行中是天然混态（前几天已完结、后几天在途）。修复前展示区间只按「主状态行」收窄，却带着全程的逐日车费明细，自相矛盾（区间 1 天却有 5 条逐日车费）。现在区间与车费明细同源，**逐日车费条数会变多（变正确）**，无字段结构变化。

### B6. 派车确认差异契约删字段

**接口**：`POST /admin/fleet/assignments/requirements/{requirementId}/confirm` 的 `data.dailyDifferences[]`

删除 `passengerCapacity` / `capacityGap` 两字段，`differenceType` 的取值去掉 `CAPACITY_INSUFFICIENT`——三者自 #5810 起后端已永远不产出，属死契约。

---

## 🟢 C. 只需知晓（新增入参校验 / 错误码登记）

### C1. 新增入参上限

| 接口 | 字段 | 新增校验 | 影响 |
|------|------|---------|------|
| `POST /admin/fleet/h5/tokens` | `expireDays` | `@Min(1) @Max(90)` | 传 0 或 365 原来能建 token，现在 400。若有「长期有效」按钮传大值会失败 |
| 操作时间线 | `page` | `@Max(100000)` | 超限原来返空列表，现在 400。建议前端自行夹紧避免翻页越界 |

### C2. 车辆绑定常驻司机新增存在性校验

**接口**：`POST` / `PUT /admin/fleet/vehicles` 与导入 UPDATE 分支。

`primaryDriverId` 指向不存在或已软删的司机，现在直接返 **600205「司机不存在」**（此前静默落库成悬挂引用）。前端下拉缓存过期会踩到，需要有兜底提示。

### C3. 错误码清单补登记（行为零变化，只是 Swagger 补全）

- `POST /admin/fleet/insurance/tasks/{taskId}/retry`：补登记 **601210-601215**（权威保单未通过复核 / 权威保单查询失败 / 权威年保快照查询失败 / 既有成功任务等待权威数据复核 / 冲突后权威查询失败 / 冲突后档案复核失败）。这些场景**原先一律返 100500「系统繁忙」**，现在返精确码。
- `POST /admin/fleet/drivers/{driverId}/insurance/purchase` 与 `/insure`：order-v3 保险服务降级时由 100500 改为按 Controller 承诺的 **605601** 返回。
- `POST /admin/fleet/driver-pending/{pendingId}/approve`：**删除 605022**（该码全服务已无抛出点，前端若有该分支应删或转为不可达兜底），**新增 600205**（自带车常驻司机不存在）与 **600210**（并发冲突，续签审核 CAS 失败，需刷新重试）。

### C4. 已删除的请求字段

`DriverSaveReqVO.blacklistReason` 请求字段已删除（后端早已不消费）。不会 400（Jackson 忽略未知字段），但 Swagger 契约里没有了，前端可清理。

---

## 验证证据

**门禁**：全量 `mvn -pl hl-fleet-service test`（clean 后）**3485 个用例 0 失败**，仅剩 10 个 `ReleaseEOccupancyMysql8033RecoveryTest` 错误（该类设计上只能由 `test/run-mysql833-provider.ps1` 传冻结 SHA 系统属性运行，基线同款）；`FleetRedLineArchTest` 12/12 绿；`spotless:check` clean。相比基线（3382 用例 / 1 失败 + 13 错误）新增约 100 个用例，红项只减不增。

**测试服实测**（部署 task 55207386，双实例 12:59 重启）：

1. **错误码迁段（A1）**：迁移前 `SELECT error_code, LEFT(error_message,30), COUNT(*) FROM fleet_insurance_task WHERE error_code IN ('540033','540034') GROUP BY 1,2` → 7 行 540033、文案全部以「司机年保」开头（100% 命中迁移条件）；迁移后 540033-540037 **残留 0**、601200 **恰好 7 行**。
2. **P0 终止截断**（本次整改的最高优先级缺陷，前端不可见但影响车务可用性）：订单 2086697883247022081 含两条全程行（08-26~28 与 08-29~31），`POST /internal/fleet/assignment/truncate-from-termination` 传 terminateDate=08-30 返回 **200**（修复前必抛 605907 且永久失败、车/司机占用永不释放）。落库形态：原全程行按天拆片 → 首日 08-29 保留 assigned 且车费按新边界从 4500 重算为 1500，08-30/08-31 两片 canceled；另一条 08-26~28 的行完全未被触碰；对账 prep 精确反标（08-29 active、08-30/08-31 canceled、另一行 08-26~28 全 active）——既不漏截也不多截。
3. Flyway 全量迁移在 H2 与测试服 MySQL 均执行成功。

---

## 前端 checklist

- [ ] A1 保险 `errorCode` 码表由 540033-540037 改为 601200-601204
- [ ] A2 `/vehicles/options` 的 `dayPrice` 参与计算前 `Number()` 转换
- [ ] A3 confirm 两端点接住 605063（**禁自动重试**），需求级确认补 605059 分支
- [ ] A4 派单三端点接住 605064（**禁自动重试**），给专用引导文案
- [ ] A5 到期看板 `kinds`/`buckets` 只传白名单值且**大小写严格**
- [ ] A6 模板编辑不再无条件回传 `isDefault: false`，接住 600804
- [ ] B1 对账车队行 key 改用 `fleet` 字段，容忍兜底行（`vehicles=[]`、`fleetTeamId` 可能为 null）
- [ ] B2 对账保险 Tab 消费新增的 `manualAmount`/`baoyouAmount`
- [ ] B3 待补偿 `opType` 下拉补 `INSURANCE`
- [ ] B4 逐日车费 `priceSource` 支持 `FREE`
- [ ] C1 `expireDays` ≤90、`page` ≤100000 前端夹紧
- [ ] C2 车辆保存接住 600205

做完请把 frontmatter 的 `frontend_status` 改为 `implemented` 并填 `frontend_ref`。
