diff --git a/changelogs-v2/2026-07/23_5194_待确认详情补全多车多司机与按槽位改派-修改接口-管理后台.md b/changelogs-v2/2026-07/23_5194_待确认详情补全多车多司机与按槽位改派-修改接口-管理后台.md new file mode 100644 index 0000000..6934769 --- /dev/null +++ b/changelogs-v2/2026-07/23_5194_待确认详情补全多车多司机与按槽位改派-修改接口-管理后台.md @@ -0,0 +1,230 @@ +--- +schema: "hl-changelog/v1" +ticket: "5194" +title: "待确认详情补全多车多司机与按槽位改派" +consumer: "admin" +backend: "verified" +gateway: "verified" +frontend: "pending" +base: "dev-v3" +generated: "2026-07-23T18:02:00+08:00" +--- + +# 【修改接口·前端待处理·管理后台】待确认详情补全多车多司机与按槽位改派 + +## 目标前端 + +- 端类型:管理后台(Web) +- 目标仓库:`mmg/hl-ui` +- 目标分支:`v2.1` +- 联调/验收环境: +- 小程序:无需处理 + +> **服务**: hl-fleet-service +> +> **工单**: [wx/HL#5194](https://git.1814.love:8443/wx/HL/issues/5194) +> +> **后端 PR**: [wx/HL#5196](https://git.1814.love:8443/wx/HL/pulls/5196) +> +> **影响范围**: 派车看板状态、派单弹窗改派、司机待确认页 + +## 关键业务口径 + +1. `holding` 落库状态不变,但等待司机真实回复时,页面展示文案统一为“待确认”,不得再显示“排车中”。 +2. 一张订单可以同时存在多个有效车辆槽位,每个槽位有独立车辆、司机和确认阶段。前端必须遍历 `activeAssignments`,不能只读兼容字段 `currentAssignment`。 +3. 改派以选中的稳定槽位为单位。两辆车中只改一辆时,只提交目标槽位对应的 `activeAssignments[i].id`;其他槽位不取消、不重建、不改变。 +4. 改派界面必须先展示历史车辆/司机,并要求车务人员在界面中明确清除目标槽位的旧选择后才能选择新车/新司机。 +5. “清除旧选择”只修改前端草稿状态,**不得先调用取消派单接口**。最终一次调用 `change` 原子替换;用户关闭弹窗时后端原派单保持不变。 +6. 多车待确认页按槽位分别登记司机回复。只要任一有效 HOLD 槽位未收到司机回复,订单顶部整体步骤仍停在“待确认”。 + +## 一、派单详情 + +```http +GET /admin/fleet/board/orders/{orderId} +``` + +### `activeAssignments[]` 完整字段 + +| 字段 | 类型 | 说明 | +| --- | --- | --- | +| `id` | `String` | 当前有效派车组锚点 ID;改派、登记司机确认、最终确认均使用该值 | +| `assignmentGroupId` | `String` | 当前派车组 ID;改派后会生成新值 | +| `assignmentSlotId` | `String` | 稳定车辆槽位 ID;同一槽位改派前后保持不变 | +| `fleetItemIndex` | `Integer` | 用车需求项序号 | +| `requiredVehicleType` | `String` | 需求车型 | +| `requiredSeats` | `Integer` | 需求座位数 | +| `vehicleId` | `String` | 当前车辆 ID | +| `vehiclePlate` | `String` | 当前车牌 | +| `vehicleModel` | `String` | 当前车型;优先派车冻结快照,缺失时回填车辆档案 | +| `vehicleSeats` | `Integer` | 当前车辆座位数 | +| `vehicleFleetTeamId` | `String` | 当前车辆所属车队 ID | +| `vehicleFleetTeamName` | `String` | 当前车辆所属车队名称 | +| `driverId` | `String` | 当前司机 ID | +| `driverName` | `String` | 当前司机姓名 | +| `driverPhone` | `String` | 当前司机脱敏手机号,例如 `138****1234` | +| `assignmentStatus` | `String` | 派生态 | +| `assignmentStatusLabel` | `String` | 后端统一展示文案 | +| `lifecycleStageCode` | `String` | 当前槽位生命周期阶段 | +| `lifecycleStageLabel` | `String` | 当前槽位阶段文案 | +| `currentStep` | `Integer` | 当前槽位所在步骤 | +| `availableActionCodes` | `String[]` | 当前槽位允许操作 | +| `driverConfirmedAt` | `LocalDateTime/null` | 本槽位司机确认时间 | + +示例(字段已脱敏): + +```json +{ + "currentAssignment": { + "id": "2079502431745396738", + "assignmentSlotId": "2079502431745396738" + }, + "activeAssignments": [ + { + "id": "2079502431745396738", + "assignmentGroupId": "2079502431745396738", + "assignmentSlotId": "2079502431745396738", + "fleetItemIndex": 0, + "vehicleId": "2079857985374363650", + "vehiclePlate": "蒙A-T1557", + "vehicleModel": "丰田普拉多", + "vehicleSeats": 7, + "vehicleFleetTeamId": "2079857981112934401", + "vehicleFleetTeamName": "合作车队A", + "driverId": "2079857983403024385", + "driverName": "司机姓名", + "driverPhone": "199****1557", + "baseAssignmentStatus": "holding", + "assignmentStatus": "holding", + "assignmentStatusLabel": "待确认", + "lifecycleStageCode": "holding_wait_driver", + "lifecycleStageLabel": "待确认", + "currentStep": 3 + } + ] +} +``` + +### 兼容与聚合规则 + +- `currentAssignment` 仍返回“最新有效派车组”,仅用于兼容旧版单车页面;新页面不得据此判断订单只有一辆车。 +- `activeAssignments` 只含有效 `holding/assigned` 派车组,按需求项、服务日期、派车组 ID 稳定排序。 +- `progressSteps` 是订单整体步骤,多车时按最慢有效槽位聚合。 +- 每辆车的实际阶段以对应 `activeAssignments[i].lifecycleStageCode/currentStep` 为准。 +- 老异常数据若车辆档案或司机电话确实缺失,对应字段可能为 `null`;页面显示 `-`,不得导致整页报错。 + +## 二、按目标槽位改派 + +```http +POST /admin/fleet/assignments/{assignmentId}/change +``` + +`assignmentId` 必须使用车务人员选中的 `activeAssignments[i].id`,不要使用订单 ID,也不要默认使用 `currentAssignment.id`。 + +请求示例: + +```json +{ + "effectiveDate": "2026-07-29", + "newVehicleId": "2079857985374363999", + "newDriverId": "2079857983403024999", + "holdMode": 1, + "messageTemplateId": "2073978002412105729", + "protocolPrice": 700.00, + "reason": "替换第 2 个车辆槽位", + "requestId": "change-slot-20260723-001" +} +``` + +原子替换成功响应会明确返回稳定槽位和其他未改车辆: + +```json +{ + "code": 200, + "data": { + "assignmentId": "新的有效派单锚点ID", + "assignmentSlotId": "改派前后不变的槽位ID", + "previousAssignmentGroupId": "被替换的旧派车组ID", + "newAssignmentGroupId": "新派车组ID", + "assignmentStatus": "holding", + "effectiveDate": "2026-07-29", + "affectedDays": 3, + "otherVehicleCount": 1, + "warningCode": "ORDER_HAS_OTHER_VEHICLES", + "otherVehicles": [ + { + "assignmentSlotId": "未改车辆槽位ID", + "vehiclePlate": "蒙A-U1557", + "driverName": "另一位司机", + "startDate": "2026-07-29", + "endDate": "2026-07-31" + } + ] + }, + "success": true +} +``` + +### 改派页面正确流程 + +1. 打开改派时用 `activeAssignments` 渲染全部现有槽位卡片,显示车牌、车型、座位、司机姓名、脱敏电话。 +2. 用户先选择要替换的槽位;两车订单不得自动选“最新一辆”代替用户决定。 +3. 目标槽位显示“清除当前车辆/司机”。用户明确点击后,只清空本地候选草稿并解锁新车/新司机选择。 +4. 未清除目标槽位前禁用候选选择和提交;其他槽位仍只读展示,不跟随清空。 +5. 提交时只调用一次目标 `id` 的 `change`。禁止先 `DELETE /assignments/{id}`,也禁止先调用 `driver-reject`。 +6. 成功后重新请求订单详情,用新的 `activeAssignments` 替换页面状态;不要在前端自行拼接新旧派车组。 +7. `warningCode=ORDER_HAS_OTHER_VEHICLES` 是“还有其他车辆保持不变”的强提示,不是失败,不得继续批量改派其他槽位。 + +## 三、多司机待确认页 + +司机待确认页必须遍历 `activeAssignments`,每个槽位至少显示: + +- 车型、车牌、座位数; +- 司机姓名、脱敏手机号; +- 当前阶段文案; +- 本槽位的通知模板预览、司机回复摘要、确认凭证和操作按钮。 + +每名司机独立调用: + +```http +POST /admin/fleet/assignments/{activeAssignments[i].id}/driver-confirmation +POST /admin/fleet/assignments/{activeAssignments[i].id}/confirm +``` + +不得因为其中一名司机已确认,就把其他仍待回复的司机一起标记为已确认。 + +## 四、模板渲染的 `canceled` 处理 + +测试网关已验证真实 `hold_notify` 模板列表和 `render` 接口均为 200,订单 26-7042 的模板正文可正常渲染。页面出现原样英文 `canceled` 是前端取消旧请求被当成业务错误展示,不是后端模板错误。 + +前端必须: + +1. 模板、车辆或司机快速切换时允许取消旧 render 请求。 +2. 对 Axios `CanceledError`、`ERR_CANCELED` 或项目统一的取消请求判定静默处理,不弹错误条、不清空最后一次成功预览。 +3. 只采纳最新一次请求的响应;较早请求即使后返回也不得覆盖新预览。 +4. 真正的 HTTP/业务错误才显示“模板加载失败”,并保留“重试加载”。 +5. 禁止把异常对象的 `message`(例如 `canceled`)直接展示给用户。 + +## 五、前端处理清单 + +- [ ] 看板卡片对 `holding_wait_driver` 展示“待确认”,不再硬编码“排车中”。 +- [ ] 派单详情、改派和待确认页全部遍历 `activeAssignments`,不再只读 `currentAssignment`。 +- [ ] 每个槽位显示车型、车牌、座位、司机姓名和脱敏手机号。 +- [ ] 多车顶部步骤使用后端 `progressSteps`,每车状态使用本项生命周期字段。 +- [ ] 改派前展示全部现有槽位,由车务人员明确选择目标槽位。 +- [ ] 目标槽位必须先在界面中手动清除旧选择,才允许重新选择车辆/司机。 +- [ ] 清除操作仅修改前端草稿,不调用取消或退回接口;最终只调用一次目标槽位的 `change`。 +- [ ] 两车只换一车时,另一槽位保持原样;成功后重新拉取详情。 +- [ ] 每名司机分别登记确认和最终确认,不能用一个槽位状态覆盖全部司机。 +- [ ] render 请求取消时静默处理,不再显示原样英文 `canceled`。 + +## 六、验证证据 + +- 后端:fleet 及依赖模块全量 `verify` 成功,2,336 个测试 0 失败、1 个既有跳过;`spotless:check` 通过。 +- 按槽位改派测试:验证替换目标槽位时不会调用其他槽位的取消语句。 +- 多车测试:验证完整返回两个稳定槽位及车辆/司机字段;任一司机未回复时整体仍停在待确认。 +- 部署:测试环境部署任务 `6679d69c` 成功,8087/8187 双实例滚动发布并通过健康检查。 +- 网关:订单 26-7042 返回 `assignmentStatus=holding`、`assignmentStatusLabel=待确认`、`lifecycleStageCode=holding_wait_driver`、`lifecycleStageLabel=待确认`。 +- 网关:订单 26-7042 的 `activeAssignments` 已返回稳定槽位、车型、座位、司机和脱敏手机号;真实模板 render 正文长度 240,未出现后端 `canceled` 错误。 +- 当前测试环境看板前 99 个唯一订单没有多有效派车样本,因此多车网关展示不能靠现存业务数据复验,后端多车契约由自动化测试覆盖。 + +> 本文是前端接入通知,不代表已修改或发布 `mmg/hl-ui`。