docs(api): hand off fleet requirement aggregation (#5257)
所有检测均成功
changelog-filename-gate / validate (pull_request) Successful in 2s
所有检测均成功
changelog-filename-gate / validate (pull_request) Successful in 2s
这个提交包含在:
父节点
6c23a7367f
当前提交
bf465d93e0
@ -0,0 +1,279 @@
|
|||||||
|
---
|
||||||
|
schema: "hl-changelog/v2"
|
||||||
|
ticket: "5257"
|
||||||
|
title: "车务看板按用车需求聚合多车型槽位"
|
||||||
|
consumer: "admin"
|
||||||
|
change_type: "修改接口"
|
||||||
|
backend_status: "deployed"
|
||||||
|
gateway_status: "verified"
|
||||||
|
frontend_status: "pending"
|
||||||
|
frontend_owner: ""
|
||||||
|
frontend_ref: ""
|
||||||
|
target_release: ""
|
||||||
|
verified_at: "2026-07-26T11:08:17+08:00"
|
||||||
|
status_note: "后端候选 commit fd641651 已部署测试环境并经网关验证;本契约明确替代 #5216 的按槽位卡片维度。前端尚未认领,需按需求卡消费 assignmentSlots。"
|
||||||
|
updated_at: "2026-07-26"
|
||||||
|
base: "dev-v3"
|
||||||
|
---
|
||||||
|
|
||||||
|
# 车务看板按用车需求聚合多车型槽位
|
||||||
|
|
||||||
|
## 关联
|
||||||
|
|
||||||
|
- Issue: [wx/HL#5257](https://git.1814.love:8443/wx/HL/issues/5257)
|
||||||
|
- 服务: `hl-fleet-service`
|
||||||
|
- 前端仓库/分支: `mmg/hl-ui` / `v2.1`
|
||||||
|
- 影响范围: 车务管理 → 派车看板列表、需求卡和需求详情
|
||||||
|
- 被本契约替代的旧口径:
|
||||||
|
[#5216 派车看板补充槽位接送路线与就绪摘要](./24_5216_派车看板补充槽位接送路线与就绪摘要-修改接口-管理后台.md)
|
||||||
|
中“每张卡对应一个稳定车辆槽位”的维度说明
|
||||||
|
|
||||||
|
## 关键变化
|
||||||
|
|
||||||
|
同一订单的一条当前有效用车需求可能同时需要多种车型、多个车辆槽位。例如
|
||||||
|
`SUV×1 + 商务车×1` 是 **1 条用车需求、合计 2 辆车**,不能显示成 2 条“用车需求”。
|
||||||
|
|
||||||
|
`GET /admin/fleet/board/orders` 的 `data.records[]` 从派车槽位粒度调整为当前
|
||||||
|
active `requirementId` 粒度:
|
||||||
|
|
||||||
|
- 同一 `requirementId` 只返回一条 record。
|
||||||
|
- `requiredVehicles[]` 展示全部车型组及数量。
|
||||||
|
- `assignmentProgress.totalSlots` 展示合计需要的车辆数。
|
||||||
|
- 新增 `assignmentSlots[]`,完整保留逐辆派车身份、状态、车辆和司机。
|
||||||
|
- 原顶层单槽位字段兼容保留,统一表示当前最需要处理的代表槽位。
|
||||||
|
- 状态、车型或司机筛选命中需求内任一槽位时,需求只返回一次。
|
||||||
|
- 后端先按需求聚合,再排序和分页;`total` 与汇总接口不再按槽位重复计数。
|
||||||
|
|
||||||
|
请求参数、路径、HTTP method、响应包络、错误码、数据库和派车执行语义均不变。
|
||||||
|
|
||||||
|
## 变更接口
|
||||||
|
|
||||||
|
```http
|
||||||
|
GET /admin/fleet/board/orders
|
||||||
|
```
|
||||||
|
|
||||||
|
### 响应示例
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"code": 200,
|
||||||
|
"message": "成功",
|
||||||
|
"data": {
|
||||||
|
"records": [
|
||||||
|
{
|
||||||
|
"id": "26-5256",
|
||||||
|
"orderId": "2080200000000000000",
|
||||||
|
"requirementId": "2080200000000000900",
|
||||||
|
"requiredVehicles": [
|
||||||
|
{
|
||||||
|
"vehicleType": "suv",
|
||||||
|
"categoryLabel": "SUV",
|
||||||
|
"seats": 5,
|
||||||
|
"count": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"vehicleType": "mpv",
|
||||||
|
"categoryLabel": "商务车",
|
||||||
|
"seats": 7,
|
||||||
|
"count": 1
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"assignmentProgress": {
|
||||||
|
"totalSlots": 2,
|
||||||
|
"unassignedSlots": 2,
|
||||||
|
"holdingSlots": 0,
|
||||||
|
"assignedSlots": 0,
|
||||||
|
"completedSlots": 0,
|
||||||
|
"canceledSlots": 0
|
||||||
|
},
|
||||||
|
"assignmentStatus": "unassigned",
|
||||||
|
"assignmentId": "2080200000000000001",
|
||||||
|
"assignmentSlotId": "2080200000000000101",
|
||||||
|
"fleetItemIndex": 0,
|
||||||
|
"canAssign": true,
|
||||||
|
"canRejectRequirement": true,
|
||||||
|
"assignmentSlots": [
|
||||||
|
{
|
||||||
|
"assignmentId": "2080200000000000001",
|
||||||
|
"assignmentGroupId": "2080200000000000201",
|
||||||
|
"assignmentSlotId": "2080200000000000101",
|
||||||
|
"fleetItemIndex": 0,
|
||||||
|
"slotSummary": {
|
||||||
|
"requiredVehicleType": "suv",
|
||||||
|
"requiredVehicleTypeLabel": "SUV",
|
||||||
|
"requiredSeats": 5
|
||||||
|
},
|
||||||
|
"baseAssignmentStatus": "unassigned",
|
||||||
|
"assignmentStatus": "unassigned",
|
||||||
|
"assignmentStatusLabel": "待派车",
|
||||||
|
"availableActionCodes": ["ASSIGN"],
|
||||||
|
"vehiclePlate": null,
|
||||||
|
"vehicleModel": null,
|
||||||
|
"vehicleSeats": null,
|
||||||
|
"driverName": null,
|
||||||
|
"driverPhone": null,
|
||||||
|
"urgentBadge": null,
|
||||||
|
"canAssign": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"assignmentId": "2080200000000000002",
|
||||||
|
"assignmentGroupId": "2080200000000000202",
|
||||||
|
"assignmentSlotId": "2080200000000000102",
|
||||||
|
"fleetItemIndex": 1,
|
||||||
|
"slotSummary": {
|
||||||
|
"requiredVehicleType": "mpv",
|
||||||
|
"requiredVehicleTypeLabel": "商务车",
|
||||||
|
"requiredSeats": 7
|
||||||
|
},
|
||||||
|
"baseAssignmentStatus": "unassigned",
|
||||||
|
"assignmentStatus": "unassigned",
|
||||||
|
"assignmentStatusLabel": "待派车",
|
||||||
|
"availableActionCodes": ["ASSIGN"],
|
||||||
|
"vehiclePlate": null,
|
||||||
|
"vehicleModel": null,
|
||||||
|
"vehicleSeats": null,
|
||||||
|
"driverName": null,
|
||||||
|
"driverPhone": null,
|
||||||
|
"urgentBadge": null,
|
||||||
|
"canAssign": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"total": 1,
|
||||||
|
"page": 1,
|
||||||
|
"pageSize": 20
|
||||||
|
},
|
||||||
|
"success": true
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 新增字段 `assignmentSlots[]`
|
||||||
|
|
||||||
|
所有雪花 ID 必须按字符串消费。
|
||||||
|
|
||||||
|
| 字段 | 类型 | 空值 | 说明 |
|
||||||
|
|---|---|---|---|
|
||||||
|
| `assignmentId` | `String` | 否 | 逐槽位派车/改派操作使用的派单 ID |
|
||||||
|
| `assignmentGroupId` | `String` | 否 | 同一派车组 ID |
|
||||||
|
| `assignmentSlotId` | `String` | 否 | 跨逐日切片稳定的车辆槽位 ID |
|
||||||
|
| `fleetItemIndex` | `Integer` | 否 | 当前需求 `fleet[]` 展开后的槽位序号,0 起 |
|
||||||
|
| `slotSummary` | `Object` | 否 | 该槽位车型、座位、服务范围和整单槽位数 |
|
||||||
|
| `baseAssignmentStatus` | `String` | 否 | 基础落库态 |
|
||||||
|
| `assignmentStatus` | `String` | 否 | 当前有效状态,可含紧急派生态 |
|
||||||
|
| `assignmentStatusLabel` | `String` | 否 | 后端中文状态标签 |
|
||||||
|
| `lifecycleStageCode/lifecycleStageLabel` | `String` | 否 | 该槽位生命周期阶段 |
|
||||||
|
| `currentStep` | `Integer` | 否 | 该槽位当前步骤 |
|
||||||
|
| `availableActionCodes` | `String[]` | 否 | 该槽位可用动作;需求级驳回不在此数组 |
|
||||||
|
| `vehiclePlate/vehicleModel` | `String` | 是 | 已派车辆信息 |
|
||||||
|
| `vehicleSeats` | `Integer` | 是 | 已派车辆座位数 |
|
||||||
|
| `vehicleFleetTeamId` | `String` | 是 | 已派车辆所属车队 ID |
|
||||||
|
| `vehicleFleetTeamName/vehicleFleetTeamType/vehicleFleetTeamSettleType` | `String` | 是 | 已派车辆所属车队信息 |
|
||||||
|
| `driverName` | `String` | 是 | 已派司机姓名 |
|
||||||
|
| `driverPhone` | `String` | 是 | 已脱敏司机手机号 |
|
||||||
|
| `urgentBadge` | `String` | 是 | `T-N` 或 `Nh`;非紧急为 `null` |
|
||||||
|
| `canAssign` | `Boolean` | 否 | 该槽位是否可进入派车/改派流程 |
|
||||||
|
|
||||||
|
## 顶层兼容字段
|
||||||
|
|
||||||
|
以下既有字段没有删除,旧前端继续读取不会报错:
|
||||||
|
|
||||||
|
- `assignmentId/assignmentGroupId/assignmentSlotId/fleetItemIndex`
|
||||||
|
- `slotSummary`
|
||||||
|
- `assignmentStatus/assignmentStatusLabel`
|
||||||
|
- `lifecycleStageCode/lifecycleStageLabel/currentStep/availableActionCodes`
|
||||||
|
- `currentVehicle*`
|
||||||
|
- `currentDriver*`
|
||||||
|
- `urgentBadge/canAssign/canRejectRequirement`
|
||||||
|
|
||||||
|
它们统一指向代表槽位,选择优先级为:
|
||||||
|
|
||||||
|
```text
|
||||||
|
未派 → 排车中 → 已派 → 已完成 → 已取消
|
||||||
|
```
|
||||||
|
|
||||||
|
同级按 `fleetItemIndex/assignmentSlotId/assignmentId` 稳定排序。部分已派时,顶层仍指向
|
||||||
|
未派槽位,保证原“派车派人”入口可继续派下一辆;所有槽位的真值以
|
||||||
|
`assignmentSlots[]` 和 `assignmentProgress` 为准。
|
||||||
|
|
||||||
|
`canRejectRequirement` 与需求级驳回动作只读外层 record。任一槽位已进入
|
||||||
|
`holding/assigned` 时,外层不会错误开放驳回;槽位级 `availableActionCodes` 不包含需求级驳回。
|
||||||
|
|
||||||
|
## 筛选、分页和汇总
|
||||||
|
|
||||||
|
- 状态多选:任一槽位命中即返回该需求一次。
|
||||||
|
- 车型多选:任一需求槽位命中即返回该需求一次,`requiredVehicles[]` 仍保留全部车型。
|
||||||
|
- 司机筛选/关键词:任一槽位司机命中即返回该需求一次。
|
||||||
|
- 混合状态:顶层状态取代表槽位状态;完整状态分布读取 `assignmentProgress` 和
|
||||||
|
`assignmentSlots[]`。
|
||||||
|
- 空态:无当前有效需求或无 fleet 看板候选时返回 `records=[]/total=0`,不按人数合成虚假槽位。
|
||||||
|
- 顺序:先聚合为唯一 requirement record,再确定性排序和分页。
|
||||||
|
- `data.total`:查询范围内唯一 active `requirementId` 数,不是车辆槽位数。
|
||||||
|
- `GET /admin/fleet/board/summary`:状态和今日出团数使用相同需求粒度,不重复计数。
|
||||||
|
|
||||||
|
守恒关系:
|
||||||
|
|
||||||
|
```text
|
||||||
|
assignmentProgress.totalSlots
|
||||||
|
== unassignedSlots
|
||||||
|
+ holdingSlots
|
||||||
|
+ assignedSlots
|
||||||
|
+ completedSlots
|
||||||
|
+ canceledSlots
|
||||||
|
|
||||||
|
assignmentProgress.totalSlots
|
||||||
|
== sum(requiredVehicles[].count)
|
||||||
|
```
|
||||||
|
|
||||||
|
## 前端处理清单
|
||||||
|
|
||||||
|
- [ ] 看板列表对每个 `records[]` 只渲染一张需求卡,不再按
|
||||||
|
`fleetItemIndex/assignmentSlotId` 拆卡,也不得展开 `assignmentSlots[]` 生成额外卡片。
|
||||||
|
- [ ] 列表 row key 优先使用 `requirementId`;仅兼容历史空值时回退 `id/orderId`。
|
||||||
|
- [ ] 需求卡展示 `requiredVehicles[]` 的全部车型组,并显示
|
||||||
|
`assignmentProgress.totalSlots` 为“需要 N 辆车”。
|
||||||
|
- [ ] 需求详情遍历 `assignmentSlots[]` 展示全部车辆槽位、各自状态和已派车辆/司机。
|
||||||
|
- [ ] 顶层按钮可继续使用代表槽位字段;逐辆操作必须使用所选
|
||||||
|
`assignmentSlots[i].assignmentId/fleetItemIndex/assignmentSlotId`,不得复用其他槽位 ID。
|
||||||
|
- [ ] 需求级驳回只使用外层 `canRejectRequirement/availableActionCodes`,不从槽位数组推断。
|
||||||
|
- [ ] 混合状态显示以 `assignmentProgress` 为准,不用顶层单一状态覆盖所有槽位。
|
||||||
|
- [ ] 继续按既有稳定状态 token 映射颜色,不按中文文案判断色值。
|
||||||
|
- [ ] 覆盖单车型×1、多车型各×1、同车型×2、部分已派、全已派、完结/取消和空列表场景。
|
||||||
|
|
||||||
|
## 不影响范围
|
||||||
|
|
||||||
|
- 不修改用车需求 `fleet[]` 的业务语义。
|
||||||
|
- 不合并、删除或改写真实 `fleet_assignment`;派车仍逐辆、逐槽位执行。
|
||||||
|
- 不修改派车、确认、取消、改派、需求驳回接口的请求结构。
|
||||||
|
- 不修改 §7 矩阵派单的车辆槽位维度。
|
||||||
|
- 不修改数据库、网关路由、错误码、车辆/司机占用、保险或费用。
|
||||||
|
- 本交接不代表已修改、发布或验证 `mmg/hl-ui`。
|
||||||
|
|
||||||
|
## 后端验证
|
||||||
|
|
||||||
|
- 精确复现测试:同一 `requirementId` 下 `SUV×1 + 商务车×1` 返回
|
||||||
|
`total=1/records=1`、`requiredVehicles=2` 组、`assignmentSlots=2`、
|
||||||
|
`assignmentProgress.totalSlots=2`。
|
||||||
|
- 同车型×2、部分已派、全已派、混合完结/取消、状态多选、聚合后分页和汇总去重均有自动化覆盖。
|
||||||
|
- 相关定向测试 64 项通过,0 failures/errors。
|
||||||
|
- OpenAPI diff 状态为 `not_configured`:当前环境没有 `oasdiff`,仓库也没有可复现的
|
||||||
|
Swagger 2 → OpenAPI 3 导出链;契约审查保存了 Controller/VO 字段对比和自动化测试
|
||||||
|
作为人工 fallback 证据。
|
||||||
|
|
||||||
|
## 验证证据
|
||||||
|
|
||||||
|
- 后端 commit:`fd641651144ab429ec1b371e1902a70bf3e7f025`
|
||||||
|
- 后端 PR:[wx/HL#5259](https://git.1814.love:8443/wx/HL/pulls/5259)
|
||||||
|
- 候选分支测试部署:现有部署 API 已滚动发布
|
||||||
|
`fix/5257-fleet-requirement-card`,`hl-fleet-service:8087/8187` 均健康;
|
||||||
|
构建、发布成功,日志尾部未见 `ERROR/FATAL/Exception`。
|
||||||
|
- 网关复现订单 `26-5256`:HTTP/业务码均为 200,`records=1`、`total=1`、
|
||||||
|
`requiredVehicles=SUV×1+MPV×1`、`assignmentSlots=2` 且逐槽位 ID 唯一、
|
||||||
|
`assignmentProgress.totalSlots=2`、状态数量之和为 2。
|
||||||
|
- fleet 定向测试 64 项通过;`mvn -pl hl-fleet-service spotless:check`
|
||||||
|
611 个文件通过;`mvn -pl hl-fleet-service -am verify` 中 fleet 2386 项测试
|
||||||
|
0 failures/errors、skipped 1,完整 reactor `BUILD SUCCESS`。
|
||||||
|
- OpenAPI diff 为 `not_configured`,已保存 Controller/VO 字段对比、兼容语义、
|
||||||
|
自动化测试和脱敏网关结构作为人工 fallback 证据。
|
||||||
|
- `frontend_status=pending`:本次未修改、部署或验证 `mmg/hl-ui`;
|
||||||
|
页面仍需按“1 条需求卡 + 2 个槽位详情”的新契约完成消费。
|
||||||
正在加载...
x
在新工单中引用
屏蔽一个用户