docs(fleet): hand off atomic batch assignment (#5187)
所有检测均成功
changelog-filename-gate / validate (push) Successful in 2s
所有检测均成功
changelog-filename-gate / validate (push) Successful in 2s
这个提交包含在:
父节点
3740020603
当前提交
b279ae7eeb
@ -0,0 +1,250 @@
|
||||
---
|
||||
schema: "hl-changelog/v1"
|
||||
ticket: "5187"
|
||||
title: "多车辆槽位原子批量派车与价格日历带价"
|
||||
consumer: "admin"
|
||||
backend: "verified"
|
||||
gateway: "pending"
|
||||
frontend: "pending"
|
||||
base: "dev-v3"
|
||||
generated: "2026-07-23T15:38:00+08:00"
|
||||
---
|
||||
|
||||
# 【新增接口·前端待处理·管理后台】多车辆槽位原子批量派车与价格日历带价
|
||||
|
||||
## 目标前端
|
||||
|
||||
- 端类型:管理后台(Web)
|
||||
- 目标仓库:`mmg/hl-ui`
|
||||
- 目标分支:`v2.1`
|
||||
- 页面:车务管理 → 派车看板 → 派车派人弹窗、派单详情
|
||||
- 前端跟踪:[mmg/hl-ui#16](https://git.1814.love:8443/mmg/hl-ui/issues/16)、
|
||||
[mmg/hl-ui#17](https://git.1814.love:8443/mmg/hl-ui/issues/17)
|
||||
- 小程序:无需处理
|
||||
|
||||
> **后端工单**: [wx/HL#5187](https://git.1814.love:8443/wx/HL/issues/5187)
|
||||
>
|
||||
> **后端 PR**: [wx/HL#5189](https://git.1814.love:8443/wx/HL/pulls/5189)
|
||||
>
|
||||
> **兼容性**: 既有单槽位 `POST /admin/fleet/assignments` 不变;多车订单必须改用本次批量接口,
|
||||
> 前端不得循环调用单派接口。
|
||||
|
||||
## 一、业务口径
|
||||
|
||||
一条用车需求可能展开出多个车辆槽位。派车弹窗应按 `fleetItemIndex` 维护多组
|
||||
“车辆 + 司机 + 协议价”,允许一次选择多辆车并一次提交。整批任一槽位失败时不得留下前面
|
||||
已成功、后面失败的半批派单。
|
||||
|
||||
- 同一批内 `fleetItemIndex`、`vehicleId`、`driverId` 分别不可重复。
|
||||
- 前端只允许选择当前需求实际展开出的待派槽位,不得自行增加超过需求数量的车辆。
|
||||
- 雪花 ID 全程按字符串保存和提交。
|
||||
- 同一次提交及其网络重试必须复用同一个 `requestId`;用户修改选择后主动再次提交应生成新值。
|
||||
- `holdMode=1` 表示排车中等待司机确认,`holdMode=0` 表示直接派定;整批模式必须一致。
|
||||
|
||||
## 二、新增原子批量派单接口
|
||||
|
||||
```http
|
||||
POST /admin/fleet/assignments/batch
|
||||
Content-Type: application/json
|
||||
```
|
||||
|
||||
请求示例:
|
||||
|
||||
```json
|
||||
{
|
||||
"orderId": "2046800000000000001",
|
||||
"orderNo": "26-4165",
|
||||
"requirementId": "2046800000000000101",
|
||||
"startDate": "2026-07-28",
|
||||
"endDate": "2026-07-30",
|
||||
"pickupAt": "海拉尔",
|
||||
"dropoffAt": "满洲里",
|
||||
"headcount": 8,
|
||||
"chargeableServiceDates": [
|
||||
"2026-07-28",
|
||||
"2026-07-29",
|
||||
"2026-07-30"
|
||||
],
|
||||
"holdMode": 1,
|
||||
"skipCityJunctionException": false,
|
||||
"fromEntry": "from-board",
|
||||
"requestId": "fleet-batch-7fe5c3a8",
|
||||
"items": [
|
||||
{
|
||||
"fleetItemIndex": 0,
|
||||
"vehicleId": "2046800000000000201",
|
||||
"driverId": "2046800000000000301",
|
||||
"protocolPrice": "520.00",
|
||||
"confirmCrossResident": false
|
||||
},
|
||||
{
|
||||
"fleetItemIndex": 1,
|
||||
"vehicleId": "2046800000000000202",
|
||||
"driverId": "2046800000000000302",
|
||||
"protocolPrice": "860.00",
|
||||
"confirmCrossResident": false
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### 公共字段
|
||||
|
||||
| 字段 | 类型 | 必填 | 说明 |
|
||||
| --- | --- | ---: | --- |
|
||||
| `orderId` | String(Long) | 是 | 订单 ID |
|
||||
| `orderNo` | String | 否 | 订单号冗余 |
|
||||
| `requirementId` | String(Long) | 是 | 当前生效用车需求 ID |
|
||||
| `startDate` / `endDate` | LocalDate | 是 | 整批服务日期闭区间 |
|
||||
| `pickupAt` / `dropoffAt` | String | 否 | 接送地 |
|
||||
| `headcount` | Integer | 否 | 乘客人数 |
|
||||
| `chargeableServiceDates` | LocalDate[] | 否 | 不传=全部计费;空数组=全部免费 |
|
||||
| `vehicleFeeWaiverReason` | String | 条件必填 | 存在免费服务日时填写 |
|
||||
| `confirmAllServiceDatesFree` | Boolean | 条件必填 | 全部免费时必须为 `true` |
|
||||
| `holdMode` | Integer | 是 | `1=排车中`,`0=直接派定` |
|
||||
| `skipCityJunctionException` | Boolean | 否 | 与单派接口同义 |
|
||||
| `fromEntry` | String | 否 | 操作来源 |
|
||||
| `requestId` | String | 是 | 批次幂等键,最大 64 字符 |
|
||||
| `items` | Object[] | 是 | 1-20 个车辆槽位 |
|
||||
|
||||
### `items[]`
|
||||
|
||||
| 字段 | 类型 | 必填 | 说明 |
|
||||
| --- | --- | ---: | --- |
|
||||
| `fleetItemIndex` | Integer | 是 | 当前需求展开后的槽位序号,0 起 |
|
||||
| `vehicleId` | String(Long) | 是 | 所选车辆 ID,批内不可重复 |
|
||||
| `driverId` | String(Long) | 是 | 所选司机 ID,批内不可重复 |
|
||||
| `protocolPrice` | String(BigDecimal) | 否 | 元/车天;不传时后端按车型价格日历兜底 |
|
||||
| `messageTemplateId` | String(Long) | 否 | `holdMode=1` 的通知模板 |
|
||||
| `customBody` | String | 否 | `holdMode=1` 的本次自定义通知正文 |
|
||||
| `confirmCrossResident` | Boolean | 否 | 跨常驻车辆组合的显式确认 |
|
||||
|
||||
成功响应按 `fleetItemIndex` 升序返回:
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"data": {
|
||||
"assignments": [
|
||||
{
|
||||
"fleetItemIndex": 0,
|
||||
"assignment": {
|
||||
"id": "2046800000000000401",
|
||||
"assignmentGroupId": "2046800000000000501",
|
||||
"assignmentSlotId": "2046800000000000601",
|
||||
"assignmentStatus": "holding",
|
||||
"stageCode": "holding_wait_driver",
|
||||
"stageLabel": "排车中·等待司机确认",
|
||||
"currentStep": 2,
|
||||
"skippedStepCodes": [],
|
||||
"protocolPrice": "520.00",
|
||||
"holdSentAt": null,
|
||||
"confirmedAt": null,
|
||||
"sideEffects": null,
|
||||
"dailyDifferences": null
|
||||
}
|
||||
}
|
||||
],
|
||||
"failedFleetItemIndex": null,
|
||||
"dailyDifferences": null
|
||||
},
|
||||
"message": "成功",
|
||||
"success": true
|
||||
}
|
||||
```
|
||||
|
||||
直接派定发生订单/行程/需求冻结基线不一致时返回既有业务码 `605041`,并额外指出失败槽位:
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 605041,
|
||||
"data": {
|
||||
"assignments": [],
|
||||
"failedFleetItemIndex": 1,
|
||||
"dailyDifferences": [
|
||||
{
|
||||
"serviceDate": "2026-07-29",
|
||||
"differenceType": "CAPACITY_INSUFFICIENT",
|
||||
"message": "逐日车辆可用座位不足"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
无论返回哪一种失败,整批均不产生部分成功数据。前端失败后保留用户当前选择并展示后端文案;
|
||||
`605041` 可同时高亮 `failedFleetItemIndex` 对应槽位及逐日差异。
|
||||
|
||||
## 三、派车弹窗前端修改
|
||||
|
||||
### 3.1 多车辆选择
|
||||
|
||||
当前实现只有全局单值 `selVehicle/selDriver`,再次选择会覆盖上一辆车。需改成按
|
||||
`fleetItemIndex` 保存的槽位数组或 Map:
|
||||
|
||||
```text
|
||||
selectedSlots[fleetItemIndex] = {
|
||||
vehicle,
|
||||
driver,
|
||||
protocolPrice,
|
||||
confirmCrossResident
|
||||
}
|
||||
```
|
||||
|
||||
- 点击某个候选车辆只修改当前待选槽位,不清空其他已选槽位。
|
||||
- 已选摘要、取消车辆、司机选择和常驻组合提示均必须作用于对应槽位。
|
||||
- 提交前校验所有本次待派槽位都有车辆和司机,然后一次调用批量接口。
|
||||
- 禁止用 `for` 循环调用旧单派接口;那会在中途失败时留下半批状态。
|
||||
- 成功后一次关闭弹窗并刷新看板;不得每成功一辆刷新一次。
|
||||
|
||||
### 3.2 车型价格日历自动带价
|
||||
|
||||
候选接口 `vehicles[].protocolPrice` 已返回所选车辆车型在服务开始日的价格日历单价。当前页面
|
||||
只从订单级 `props.order.protocolPrice` 初始化输入框,导致价格日历明明有值仍显示空。
|
||||
|
||||
- 选中车辆时,把该车辆的 `protocolPrice` 写入对应槽位价格框。
|
||||
- 每辆车独立显示、独立可编辑,提交到 `items[].protocolPrice`。
|
||||
- 切换车辆时改为新车辆的价格日历值;不能沿用上一辆车的价格。
|
||||
- 候选值为空时输入框可留空,后端仍会在最终保存时按所选车辆车型 + `startDate` 再兜底一次。
|
||||
- 不得把一个全局价格复制给所有不同车型。
|
||||
|
||||
### 3.3 联系定制师
|
||||
|
||||
派车看板卡片已有“联系定制师”,派单详情第 1 步和后续派车弹窗也应与房务详情保持一致:
|
||||
|
||||
- 在详情可见区域补“联系定制师”按钮,复用现有 `open-fleet` 会话流程。
|
||||
- 订单 ID 使用数字雪花字符串,不能传 `HL...` 展示号或团号。
|
||||
- 按钮位置、图标、禁用态、加载态和聊天抽屉交互复用房务模块,不另做一套样式。
|
||||
- 首次打开真实会话和实时未读角标仍按
|
||||
[#5180 前端交接](./23_5180_订单详情联系车务独立未读红点-修改接口-管理后台.md)处理。
|
||||
|
||||
## 四、派车看板默认状态筛选
|
||||
|
||||
这是前端初始化逻辑修复,不需要后端接口变更:
|
||||
|
||||
- `statusSel` 初始值必须为 `[]`,页面首次进入状态框显示空/不限。
|
||||
- 首次列表请求不得携带 `statuses=unassigned`,默认展示全部状态。
|
||||
- 点击“重置”后的值和首次进入完全一致。
|
||||
- 用户主动选择“待派车”后才传对应状态;刷新筛选结果时不得偷偷恢复默认待派车。
|
||||
|
||||
## 五、前端验收清单
|
||||
|
||||
- [ ] 一条需求展开 2 个车辆槽位时,可同时选择 2 辆不同车辆和 2 名不同司机,第一辆不会被第二辆覆盖。
|
||||
- [ ] 提交只发送 1 次 `/admin/fleet/assignments/batch`,不循环调用旧单派接口。
|
||||
- [ ] 第二槽位失败时页面提示失败,刷新后两个槽位都没有半批残留。
|
||||
- [ ] 价格日历有值时,选择每辆车后各自价格框立即带出对应 `protocolPrice`。
|
||||
- [ ] 修改某辆车价格只影响该槽位,成功响应按槽位回显冻结价格。
|
||||
- [ ] 派单详情第 1 步和派车流程均能直接“联系定制师”,交互与房务一致。
|
||||
- [ ] 派车看板首次进入状态筛选为空,首次请求不传 `statuses`,默认可见全部状态。
|
||||
- [ ] 主动筛选“待派车”及重置行为正确。
|
||||
- [ ] 增加多槽位状态管理、批量请求映射、车型切换带价和默认空筛选的组件/组合式函数测试。
|
||||
|
||||
## 六、后端验证证据
|
||||
|
||||
- `mvn -pl hl-fleet-service spotless:check` 通过。
|
||||
- `AssignmentControllerTest + AssignmentServiceTest`:281 项通过。
|
||||
- `mvn -pl hl-fleet-service -am verify` 通过:fleet 2334 项,0 failure / 0 error,1 skipped。
|
||||
- 批量成功、空明细校验、重复槽位校验、字符串雪花 ID、直接派定基线差异及事务/幂等注解均有测试覆盖。
|
||||
|
||||
> 本文是前端接入通知,不代表已修改或发布 `mmg/hl-ui`。测试环境网关实测完成后会补充
|
||||
> `gateway` 状态和请求证据。
|
||||
正在加载...
x
在新工单中引用
屏蔽一个用户