---
schema: "hl-changelog/v2"
ticket: "5374"
title: "保险任务与派单候选补齐真实团号契约"
consumer: "admin"
change_type: "修改接口"
backend_status: "deployed"
gateway_status: "verified"
frontend_status: "verified"
frontend_owner: "Pi"
frontend_ref: "hl-admin@193575171d817f1042040baf8e0d9d28e7206048"
target_release: "v2.1"
verified_at: "2026-08-03"
status_note: "后端 PR #5415 已合并为 dev-v3@ee7ad816b；测试部署任务 23928056 双实例健康，保险任务与派单候选经网关真登录验证，Flyway 001/002、nullable VARCHAR(32)、回填收敛和普通 EXPLAIN 均通过；管理后台已由 Pi 领取，正在适配。"
updated_at: "2026-08-04"
base: "dev-v3"
---


# Fleet：保险任务与派单候选补齐真实团号契约

> **服务**: hl-fleet-service (端口 8087)
> **PR**: [wx/HL#5415](https://git.1814.love:8443/wx/HL/pulls/5415)
> **Issue**: [wx/HL#5374](https://git.1814.love:8443/wx/HL/issues/5374)
> **日期**: 2026-08-02
> **影响范围**: 管理后台车务保险任务列表与派单候选冲突提示

---

## 一、关键变化

- 保险任务列表新增独立 `teamNo` 团号 contains 查询，并在任务项返回真实 `teamNo`。
- 派单候选的车辆、司机冲突快照新增真实 `teamNo`。
- 无团号统一返回 `null`，**禁止回退或伪装为 `orderNo`**；现有订单号和雪花 ID 字符串序列化契约不变。

---

## 变更接口

| # | 接口 | 方法 | 路径 | 变更类型 | 说明 |
|---|---|---|---|---|---|
| 1 | 司机险任务/流水分页 | GET | `/admin/fleet/insurance/tasks` | 请求与响应字段新增 | 新增 `teamNo` 查询及任务团号 |
| 2 | 派单车辆/司机候选 | POST | `/admin/fleet/assignments/candidates` | 嵌套响应字段新增 | 两类 `conflicts[]` 新增真实团号 |

---

## 三、接口详情

### 1. 司机险任务/流水分页 `GET /admin/fleet/insurance/tasks`

**请求 VO**: `FleetInsuranceTaskPageReqVO`

#### 新增入参

| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|---|---|---|---|---|---|
| `teamNo` | Query | String | 否 | contains 模糊匹配 | 只搜索团号；不会 OR 匹配订单号；过滤在分页前完成 |

#### 新增出参 `Result<PageResult<FleetInsuranceTaskRespVO>>`

| 字段 | 类型 | 说明 |
|---|---|---|
| `data.records[].teamNo` | `String|null` | 保险任务冻结的真实团号快照；无团号返回 `null` |

请求示例：

```http
GET /admin/fleet/insurance/tasks?teamNo=2607&page=1&pageSize=20
```

响应字段示例：

```json
{
  "code": 200,
  "data": {
    "records": [
      {
        "taskId": "9007199254740993",
        "orderId": "9007199254740995",
        "orderNo": "26-0701",
        "teamNo": "HL-2607-001"
      }
    ],
    "total": 1,
    "page": 1,
    "pageSize": 20
  },
  "success": true
}
```

### 2. 派单车辆/司机候选 `POST /admin/fleet/assignments/candidates`

**响应 VO**: `AssignmentCandidateRespVO.ConflictVO`

#### 新增出参

| 字段 | 类型 | 说明 |
|---|---|---|
| `data.vehicles.records[].conflicts[].teamNo` | `String|null` | 冲突派单的 Fleet 本地真实团号快照 |
| `data.vehicles.list[].conflicts[].teamNo` | `String|null` | `records` 的兼容序列化别名，字段一致 |
| `data.drivers.records[].conflicts[].teamNo` | `String|null` | 冲突派单的 Fleet 本地真实团号快照 |
| `data.drivers.list[].conflicts[].teamNo` | `String|null` | `records` 的兼容序列化别名，字段一致 |

响应片段：

```json
{
  "vehicles": {
    "records": [
      {
        "vehicleId": "101",
        "conflicts": [
          {
            "assignmentId": "9001",
            "assignmentGroupId": "9101",
            "orderNo": "26-0701",
            "teamNo": "HL-2607-001",
            "blocking": true,
            "reasonCode": "ASSIGNMENT_CONFLICT"
          }
        ]
      }
    ]
  },
  "drivers": {
    "records": [
      {
        "driverId": "201",
        "conflicts": [
          {
            "assignmentId": "9001",
            "assignmentGroupId": "9101",
            "orderNo": "26-0701",
            "teamNo": null,
            "blocking": true,
            "reasonCode": "ASSIGNMENT_CONFLICT"
          }
        ]
      }
    ]
  }
}
```

---

## 四、契约约束与正确调用方式

- 前端展示团号必须读取 `teamNo`；值为 `null` 时展示 `-`。
- 不得把 `orderNo` 当作团号回退值。
- `orderNo`、`orderId`、`assignmentId`、`assignmentGroupId` 继续保留；雪花 ID 继续按 JSON String 返回，用于路由和动作。
- `teamNo` 只增加展示与查询能力，不改变候选 `available`、`blocking`、同城首尾衔接、可用窗口或派单写侧锁内复核。
- 保险任务查询的 `teamNo` 是独立参数，不沿用派车看板兼容匹配订单号的既有语义。

---

## 五、数据库行为

- `fleet_insurance_task.team_no VARCHAR(32) NULL` 与 `fleet_assignment.team_no` 对齐。
- nullable DDL 与历史回填 DML 使用两个独立 Flyway 版本。
- 历史数据仅按 `assignment_id` 从 `fleet_assignment.team_no` 回填。
- 无匹配派单、来源团号为空或任务已有非空团号时保持原值；不跨服务查询、不按订单号猜测。

### 生产两阶段迁移门禁（本单未授权、未执行生产）

1. 第一阶段必须设置 `SPRING_FLYWAY_TARGET=20260802.001`，只允许 nullable DDL 落库；禁止携带默认 `latest` 直接滚动发布。
2. 完成 Fleet Java 全量滚动并确认所有旧实例退出；此时新写入已冻结 `teamNo`，不会再产生旧代码造成的新增空快照。
3. 重新只读统计待回填数、可匹配数并执行普通 `EXPLAIN`；影响行数 ≤ 50k、扫描行数 ≤ 200k、staging 单事务 ≤ 5s、锁等待 ≤ 1s、复制延迟增量 ≤ 2s 才可继续。
4. 第二阶段才移除 target 或设置 `SPRING_FLYWAY_TARGET=20260802.002`，由受控单实例执行幂等 DML，再核验 Flyway history 与剩余空值。
5. 任一阈值超限或证据缺失即停止第二阶段，改用 1k–5k 行受控分批；生产禁止 `EXPLAIN ANALYZE UPDATE`。

---

## 六、边界行为

- `teamNo` 完整或部分关键词均按 contains 命中。
- 只有 `orderNo` 含关键词而 `teamNo` 不含时，不命中保险任务列表。
- 历史任务或冲突派单没有团号时，响应字段为 `null`，接口不异常。
- 空 `teamNo` 等同不启用该筛选；其他现有筛选条件与其按 AND 组合。

---

## 七、不影响范围

- 不修改派车矩阵、甘特、车务工作台或派车看板字段与搜索语义。
- 不修改司机档案 `relatedOrders`。
- 不新增 order-v3 Feign，不修改 `hl-common-feign`、内部接口或共享 Java DTO。
- 不修改 `hl-ui`。

---

## 验证证据

- 定向测试：226 项通过，0 failures、0 errors、0 skipped。
- Fleet Spotless：通过。
- JaCoCo changed executable lines：9/9（100%）。
- 独立 Reviewer(max)：无 P0–P2。
- oasdiff：项目缺少可复现 Swagger 2 → OAS3 转换链，标记 `not_configured`；已完成源码级手工契约对比。
- MySQL 8.0.33 隔离实例：`FleetInsuranceTaskTeamNoMysqlTest` 3/3 通过，0 skipped；H2 全迁移回归 `FleetInsuranceTaskMigrationTest` 8/8 通过。
- Fleet 全模块 `verify` 已执行 2764 项；接入本单隔离 Redis 后相关 4 项通过且无 key 残留，当前仅余 3 项基线失败（Release E Windows 时序 2 项、保险并发夹具 1 项，后者已在未含本单改动的 `dev-v3@a999a3df7` 同样复现），未将该次命令记为通过。
- 测试部署及网关真实 round-trip 结果将在后端完成后更新本文件元数据与本节。

---

## 九、前端消费动作

1. 保险任务列表筛选增加 `teamNo` 参数，并按 `data.records[].teamNo` 展示团号。
2. 派单候选的车辆/司机冲突提示改用各自 `conflicts[].teamNo` 展示团号。
3. `teamNo === null` 时展示 `-`，不得显示 `orderNo` 作为替代团号。
4. 保留现有 ID 与订单号用于路由、动作及兼容逻辑。

## 关联 / 联系人

### 链接

- **Issue**: [#5374](https://git.1814.love:8443/wx/HL/issues/5374)
- **PR**: [#5415](https://git.1814.love:8443/wx/HL/pulls/5415)
- **Merge commit**: [ee7ad816b0](https://git.1814.love:8443/wx/HL/commit/ee7ad816b0)
### 联系人

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