# 【前端对接·管理后台】车务每日派车切片与车型大类需求契约

> Issue: [wx/HL#4837](https://git.1814.love:8443/wx/HL/issues/4837)  
> PR: [#4840](https://git.1814.love:8443/wx/HL/pulls/4840)、[#4841](https://git.1814.love:8443/wx/HL/pulls/4841)  
> 服务: `hl-order-service-v3` + `hl-fleet-service`  
> 日期: 2026-07-08  
> 影响范围: 订单调整/用车需求、车务派单、矩阵派单、派单看板、车队对账

## 1. 结论

- 用车需求只选“车型大类”，不要选具体车型型号。前端应从 `GET /admin/fleet/vehicle-types/list` 或树接口的大类节点取 `typeKey`，提交到 `fleet[].vehicleType`。
- 当前测试库 SUV 大类 `typeKey` 是 `suv2`；后端提交后会归一保存为规范 key `suv`。前端不要自己把 `suv2` 改成具体车型 ID，也不要提交 `modelId/modelName`。
- `fleet_assignment` 已改为“每日切片”：7 天行程同一辆车会落 7 条记录，每条 `startDate=endDate=serviceDate`。
- 读接口仍按派车组展示，前端不要把每日切片渲染成 7 张卡。以 `assignmentGroupId` 作为同一连续派车段的稳定分组 key。
- 历史跨天单条派车记录已通过迁移拆分；新派单创建前也会兜底拆分历史占位，再按派车组消费。

## 2. 用车需求车型大类

### 2.1 车型大类列表

```http
GET /admin/fleet/vehicle-types/list
Authorization: Bearer <token>
```

响应示例：

```json
{
  "code": 200,
  "message": "成功",
  "success": true,
  "data": [
    {
      "id": "2057378611889180674",
      "typeKey": "suv2",
      "typeName": "SUV系列",
      "icon": "Car",
      "description": null,
      "sortOrder": 1,
      "modelCount": 5,
      "inUseCount": 4
    },
    {
      "id": "2057378611889180675",
      "typeKey": "mpv",
      "typeName": "商务车",
      "modelCount": 5,
      "inUseCount": 10
    }
  ]
}
```

前端取值规则：

| 用途 | 使用字段 |
|------|----------|
| 下拉展示 | `typeName` |
| 提交用车需求 | `typeKey` |
| 不可用于需求提交 | `models[].id`、`models[].modelName`、`vehicleTypeId` |

### 2.2 车型大类树

```http
GET /admin/fleet/vehicle-types
Authorization: Bearer <token>
```

响应中会带 `models[]`，这是给车型管理/价格日历/车辆档案使用的型号列表。订单用车需求仍只提交大类节点的 `typeKey`。

```json
{
  "code": 200,
  "data": [
    {
      "id": "2057378611889180674",
      "typeKey": "suv2",
      "typeName": "SUV系列",
      "models": [
        {
          "id": "2057378611889180701",
          "modelName": "丰田普拉多",
          "seats": 7
        }
      ]
    }
  ]
}
```

## 3. 提交用车需求

```http
PUT /v3/admin/order/2074724409473458177/vehicle-requirement
Authorization: Bearer <token>
Content-Type: application/json

{
  "fleet": [
    {
      "vehicleType": "suv2",
      "seats": 7,
      "count": 1
    }
  ],
  "specialTags": ["中文司机", "需要接送机"],
  "remark": "司机会蒙语，第 3 天需要儿童安全座椅"
}
```

响应示例：

```json
{
  "code": 200,
  "message": "成功",
  "success": true,
  "data": {
    "id": "2074724419590119426",
    "version": 1,
    "isActive": true,
    "status": "PENDING",
    "submittedAt": "2026-07-08T13:17:03",
    "claimerId": null,
    "claimerName": null,
    "claimedAt": null,
    "branchTaken": "INIT_SUBMIT",
    "previousVersion": null,
    "assignmentDeletedCount": null
  }
}
```

后端落库口径：

```json
[
  {
    "vehicleType": "suv",
    "seats": 7,
    "count": 1
  }
]
```

说明：

- `suv2` 是当前测试库 SUV 大类真实 `typeKey`，后端会归一为 `suv` 参与车务过滤、矩阵、对账。
- `seats/count` 仍按组提交；每组默认覆盖整个行程，不支持“前几天/后几天车型不同”的 UI。
- 多车场景仍用多组或 `count>1` 表达，后端会展开为多个 `fleetItemIndex`。

## 4. 创建派单与每日切片

```http
POST /admin/fleet/assignments
Authorization: Bearer <token>
Content-Type: application/json

{
  "orderId": "2074724409473458177",
  "orderNo": "HL20260708131700144",
  "requirementId": "2074724419590119426",
  "fleetItemIndex": 0,
  "vehicleId": "2065329516292513793",
  "driverId": "2074724495473479681",
  "startDate": "2026-07-13",
  "endDate": "2026-07-18",
  "pickupAt": "海拉尔东山机场",
  "dropoffAt": "海拉尔站",
  "headcount": 2,
  "protocolPrice": "700.00",
  "holdMode": 0,
  "fromEntry": "from-board",
  "skipCityJunctionException": false,
  "strictSeats": false,
  "requestId": "fleet-assign-20260708-001"
}
```

响应示例：

```json
{
  "code": 200,
  "message": "成功",
  "success": true,
  "data": {
    "id": "2074724499256741890",
    "assignmentGroupId": "333114315542499328",
    "assignmentStatus": "assigned",
    "protocolPrice": "700.00",
    "holdSentAt": null,
    "confirmedAt": "2026-07-08T13:17:22",
    "sideEffects": {
      "vehicleStatusUpdated": true,
      "driverStatusUpdated": true
    }
  }
}
```

落库不变量：

| 字段 | 口径 |
|------|------|
| `assignment_id` | 每日切片自己的 ID |
| `assignment_group_id` | 同一连续派车段共用，前端展示/操作分组 key |
| `start_date` / `end_date` | 每条切片都等于当天服务日 |
| `service_date` | 该条切片服务日 |
| `required_vehicle_type` | 规范车型大类 key，例如 `suv` |
| `protocol_price` | 派车时冻结的协议价日单价 |

示例：2026-07-13 至 2026-07-18 会落 6 条：

```json
[
  {"assignmentGroupId": "333114315542499328", "serviceDate": "2026-07-13", "startDate": "2026-07-13", "endDate": "2026-07-13"},
  {"assignmentGroupId": "333114315542499328", "serviceDate": "2026-07-14", "startDate": "2026-07-14", "endDate": "2026-07-14"},
  {"assignmentGroupId": "333114315542499328", "serviceDate": "2026-07-15", "startDate": "2026-07-15", "endDate": "2026-07-15"},
  {"assignmentGroupId": "333114315542499328", "serviceDate": "2026-07-16", "startDate": "2026-07-16", "endDate": "2026-07-16"},
  {"assignmentGroupId": "333114315542499328", "serviceDate": "2026-07-17", "startDate": "2026-07-17", "endDate": "2026-07-17"},
  {"assignmentGroupId": "333114315542499328", "serviceDate": "2026-07-18", "startDate": "2026-07-18", "endDate": "2026-07-18"}
]
```

## 5. 矩阵/看板读取口径

### 5.1 矩阵月视图

```http
GET /admin/fleet/matrix/grid?year=2026&month=7&status=all
```

响应片段：

```json
{
  "code": 200,
  "data": {
    "year": 2026,
    "month": 7,
    "daysInMonth": 31,
    "vehicles": [
      {
        "id": "2065329516292513793",
        "plate": "蒙C06E06",
        "assignments": [
          {
            "id": "2074724499256741890",
            "assignmentGroupId": "333114315542499328",
            "orderNumericId": "2074724409473458177",
            "orderNo": "HL20260708131700144",
            "startDay": 13,
            "endDay": 18,
            "startDate": "2026-07-13",
            "endDate": "2026-07-18",
            "vehicleCategory": "suv",
            "categoryLabel": "SUV",
            "assignmentStatus": "assigned",
            "protocolPrice": "700.00"
          }
        ]
      }
    ]
  }
}
```

前端处理：

- 月视图应按 `vehicles[].assignments[]` 渲染，一个 `assignmentGroupId` 只是一条甘特段。
- 不要再按 DB 每日切片数量重复生成条。
- 拖拽/操作时优先保留 `assignmentGroupId`；旧接口仍以 `assignmentId` 作为 path var 时，传该组任一 `id` 即可，后端会按组处理已接通的分组操作。

### 5.2 矩阵日视图

```http
GET /admin/fleet/matrix/day-orders?date=2026-07-13
```

响应片段：

```json
{
  "code": 200,
  "data": [
    {
      "orderNumericId": "2074724409473458177",
      "orderNo": "HL20260708131700144",
      "startDate": "2026-07-13",
      "endDate": "2026-07-18",
      "dayInTrip": 1,
      "totalDays": 6,
      "orderAssignStatus": "assigned",
      "assignments": [
        {
          "assignmentId": "2074724499256741890",
          "assignmentGroupId": "333114315542499328",
          "fleetItemIndex": 0,
          "vehicleCategory": "suv",
          "categoryLabel": "SUV",
          "vehiclePlate": "蒙C06E06",
          "driverName": "API按单险司机4792",
          "assignmentStatus": "assigned",
          "protocolPrice": "700.00"
        }
      ]
    }
  ]
}
```

### 5.3 看板详情

```http
GET /admin/fleet/board/orders/2074724409473458177
```

响应关注点：

```json
{
  "code": 200,
  "data": {
    "transport": {
      "batches": [
        {
          "travelerNames": "张测试, 李测试",
          "transportNo": "CA4792",
          "time": "2026-07-13 10:45:00",
          "station": "海拉尔东山机场"
        },
        {
          "travelerNames": "张测试, 李测试",
          "transportNo": "K4792",
          "time": "2026-07-18 16:30:00",
          "station": "海拉尔站"
        }
      ],
      "pickupRequired": true
    },
    "operationLog": {
      "records": [
        {
          "opType": "CONFIRMED",
          "detail": {
            "assignmentId": "2074724499256741890",
            "assignmentGroupId": "333114315542499328",
            "requiredVehicleType": "SUV",
            "requiredSeats": 7,
            "tripRange": "2026-07-13 至 2026-07-18"
          }
        }
      ]
    },
    "currentAssignment": {
      "id": "2074724499256741890",
      "assignmentGroupId": "333114315542499328",
      "vehiclePlate": "蒙C06E06",
      "driverName": "API按单险司机4792",
      "assignmentStatus": "assigned",
      "protocolPrice": "700.00"
    }
  }
}
```

说明：

- 终态 `completed/canceled` 时 `currentAssignment` 可能为 `null`，但操作日志仍保留 `assignmentGroupId`。
- 大交通接送数据在 `transport.batches[]`；没有接送机/站时间时，调整订单快照接口使用 `vehicleTransportSummary.emptyText = "暂无接送机时间"`，见 44_4829 文档。

## 6. 车队对账影响

车队对账仍按每日服务日聚合。前端不用直接消费 `fleet_assignment` 每日切片，但要理解金额来源：

- `GET /admin/fleet/reconciliation/cars` 的车天数来自每日切片/对账 prep。
- 一个 6 天派车组会贡献 6 个车天。
- `protocolPrice` 是元/车天快照，估算金额 = 日单价 × 车天。

## 7. 本次验证证据

已在测试环境验证：

- 部署：`hl-fleet-service` deploy task `bcac32b8` 成功。
- 全链路 API：`.tmp/4792_fleet_full_api_report.md`，115/115 passed。
- 专项契约验证：`.tmp/4837_daily_slice_contract_verify.md`，9/9 passed。
- 验证订单：`HL20260708131700144` / `2074724409473458177`。
- 专项断言：
  - `fleet_assignment` 未删除记录中 `start_date < end_date` 数量为 0。
  - 主订单 2026-07-13 至 2026-07-18 生成 6 条每日切片。
  - 同一组 `assignmentGroupId=333114315542499328` 在矩阵月视图只出现 1 次。
  - 矩阵日视图 2026-07-13 能查到该派车组。
  - 用车需求提交 `suv2` 后，订单侧保存为规范 `suv`。

