diff --git a/changelogs-v2/2026-07/53_4871_车务提需求派单看板闭环契约-管理后台.md b/changelogs-v2/2026-07/53_4871_车务提需求派单看板闭环契约-管理后台.md new file mode 100644 index 0000000..3fba2e2 --- /dev/null +++ b/changelogs-v2/2026-07/53_4871_车务提需求派单看板闭环契约-管理后台.md @@ -0,0 +1,487 @@ +# 【前端对接·管理后台】车务提需求与派单看板闭环契约 + +> Issue: [wx/HL#4871](https://git.1814.love:8443/wx/HL/issues/4871) +> 服务: `hl-order-service-v3` / `hl-fleet-service` / `hl-user-service` +> 日期: 2026-07-09 +> 影响范围: 订单详情车辆需求、车务工作台、派单看板、车务聊天、派单操作 + +## 1. 结论 + +- 用车需求只选车型大类,不选具体车型型号;`fleet[].vehicleType` 传大类 key,`fleet[].seats` 传该大类 seatOptions 中的座位数。 +- 司机占 1 个座位:后端新增并返回 `passengerSeatCapacity`、`remainingPassengerSeats` 等字段,前端余座展示以这些字段为准。 +- 派单看板状态枚举以后读 `GET /admin/fleet/board/summary` 的 `statusOptions`,前端不要再独立维护状态文案映射。 +- `holding` 的业务含义是“排车中”:车务已经派给司机,但司机还没有确认。 +- 派单看板列表/工作台补齐联系人、团号、定制师展示名、操作能力字段,支持按联系人/团号/定制师筛选。 +- 车务聊天在线态按“是否有任一车务管理员角色在线”聚合,不再绑定单个 admin。 +- 派单取消和司机拒接/退回待派已补后端接口。已告知司机也允许取消,但前端必须做强提示确认。 + +## 2. 提交用车需求 + +```http +PUT /v3/admin/order/2074746808742928386/vehicle-requirement +Authorization: Bearer +Content-Type: application/json +``` + +请求示例: + +```json +{ + "fleet": [ + { + "vehicleType": "suv2", + "seats": 5, + "count": 1 + } + ], + "specialTags": ["大行李空间", "中文司机"], + "remark": "接机后直接去酒店" +} +``` + +成功响应新增座位字段: + +```json +{ + "code": 200, + "message": "成功", + "success": true, + "data": { + "id": "2075000000000000001", + "orderId": "2074746808742928386", + "version": 1, + "status": "PENDING", + "fleet": [ + { "vehicleType": "suv", "seats": 5, "count": 1 } + ], + "specialTags": ["大行李空间", "中文司机"], + "remark": "接机后直接去酒店", + "passengerCount": 3, + "vehicleCount": 1, + "totalSeatCount": 5, + "driverSeatCount": 1, + "passengerSeatCapacity": 4, + "remainingPassengerSeats": 1 + } +} +``` + +座位公式: + +```text +vehicleCount = sum(fleet[].count) +totalSeatCount = sum(fleet[].seats * fleet[].count) +driverSeatCount = vehicleCount +passengerSeatCapacity = totalSeatCount - driverSeatCount +remainingPassengerSeats = passengerSeatCapacity - passengerCount +``` + +示例:1 台 5 座车只能坐 4 名乘客,司机占 1 座。订单 5 名乘客提交 1 台 5 座车会失败: + +```json +{ + "code": 582027, + "message": "车辆载客座位不足:每辆车需扣除1个司机座", + "success": false, + "data": null +} +``` + +前端处理: + +- “余 X 座”展示请读 `remainingPassengerSeats`,不要用 `seats * count - passengerCount` 自行计算。 +- 仍然从 `GET /admin/fleet/vehicle-types/list` 读取车型大类和 `seatOptions`。 +- `fleet[].vehicleType` 传 `typeKey`;`fleet[].seats` 只能从该大类 `seatOptions` 里选。 +- `specialTags` 继续来自字典 `vehicle_special_demand`。 + +## 3. 订单详情行程 Tab + +```http +GET /v3/admin/order/2074746808742928386/itinerary +Authorization: Bearer +``` + +`vehicleGroup.requirement` 同步新增座位字段: + +```json +{ + "code": 200, + "message": "成功", + "success": true, + "data": { + "canContactFleet": true, + "contactFleetDisabledReason": null, + "vehicleGroup": { + "requirement": { + "requirementId": "2075000000000000001", + "status": "PENDING", + "vehicleTypeSummary": "suv×1", + "specialTags": ["大行李空间", "中文司机"], + "remark": "接机后直接去酒店", + "passengerCount": 3, + "vehicleCount": 1, + "totalSeatCount": 5, + "driverSeatCount": 1, + "passengerSeatCapacity": 4, + "remainingPassengerSeats": 1 + }, + "assignments": [] + } + } +} +``` + +## 4. 派单看板汇总与状态选项 + +```http +GET /admin/fleet/board/summary +Authorization: Bearer +``` + +响应示例: + +```json +{ + "code": 200, + "message": "成功", + "success": true, + "data": { + "totalOrders": 132, + "unassigned": 111, + "holding": 2, + "assigned": 8, + "changeRequested": 3, + "completed": 3, + "canceled": 2, + "statusOptions": [ + { + "value": "unassigned", + "label": "待派车", + "description": "未派车,可派车/可驳回用车需求", + "count": 111, + "urgentCount": 2 + }, + { + "value": "holding", + "label": "排车中", + "description": "车务已派给司机,司机未确认", + "count": 2, + "urgentCount": 2 + }, + { + "value": "assigned", + "label": "已派车", + "description": "司机已确认执行", + "count": 8, + "urgentCount": 0 + }, + { + "value": "change_requested", + "label": "换车请求", + "description": "客户或定制师提出换车", + "count": 3, + "urgentCount": 0 + }, + { + "value": "completed", + "label": "已完结", + "description": "派车已完结", + "count": 3, + "urgentCount": 0 + }, + { + "value": "canceled", + "label": "已取消", + "description": "派车已取消", + "count": 2, + "urgentCount": 0 + } + ] + } +} +``` + +前端处理: + +- 筛选下拉和状态文案读 `statusOptions[].value/label/count/urgentCount`。 +- 前端不要把 `holding` 显示成“待派车”或“已派车”;它固定显示为“排车中”。 +- 红色急单角标可读 `urgentCount`;列表行是否急单仍读行内 `assignmentStatus` 派生态。 + +## 5. 派单看板列表 + +```http +GET /admin/fleet/board/orders?page=1&pageSize=10&statuses=unassigned&contactName=乐瑶&teamNo=26-7218&consultantName=阿如娜 +Authorization: Bearer +``` + +新增/强化查询参数: + +| 参数 | 类型 | 说明 | +|------|------|------| +| `contactName` | string | 联系人/客户名模糊筛选。 | +| `contactKeyword` | string | `contactName` 别名,二者任选其一。 | +| `teamNo` | string | 团号模糊筛选,例如 `26-7218`。 | +| `plannerName` | string | 定制师模糊筛选,旧字段保留。 | +| `consultantName` | string | 定制师模糊筛选,`plannerName` 别名。 | +| `statuses` | string[] | 状态多选,值来自 `statusOptions[].value`。 | +| `status` | string | 单状态或逗号分隔状态。 | +| `typeKeys` | string[] | 车型大类筛选。 | +| `startDate/endDate` | date | 行程日期区间。 | +| `keyword` | string | 司机姓名搜索,兼容旧页面。 | + +响应示例: + +```json +{ + "code": 200, + "message": "成功", + "success": true, + "data": { + "page": 1, + "pageSize": 10, + "total": 27, + "records": [ + { + "id": "HL20260708144557879", + "orderId": "2074746808742928386", + "orderNo": "HL20260708144557879", + "teamNo": "26-7218", + "customerName": "何子墨", + "contactName": "何子墨", + "productName": "测试核心产品-单档-固定订金", + "headcount": 2, + "startDate": "2026-07-20", + "endDate": "2026-07-22", + "tripDays": 3, + "assignmentStatus": "unassigned_urgent", + "assignmentStatusLabel": "待派车", + "urgentBadge": "待派即将超时", + "consultantName": "阿如娜", + "consultantDisplayName": "阿如娜", + "plannerName": "阿如娜", + "customerNote": "需要接送机", + "requiredVehicles": [ + { + "vehicleType": "suv", + "categoryLabel": "SUV系列", + "seats": 5, + "count": 1 + } + ], + "canAssign": true, + "canRejectRequirement": true, + "unreadMessageCount": 0 + } + ] + } +} +``` + +前端处理: + +- 列表卡片至少展示联系人/人数/定制师/团号;字段优先级: + - 联系人:`contactName || customerName` + - 定制师:`consultantDisplayName || consultantName || plannerName` + - 团号:`teamNo` +- 派车按钮展示条件:`canAssign === true`。 +- 驳回用车需求按钮展示条件:`canRejectRequirement === true`。 +- 红色即将超时订单也必须显示派车/驳回按钮,不能因为急单样式隐藏操作区。 +- 当前接口仍是分页接口;前端不要改成不分页一次性全拉。 + +## 6. 车务工作台 + +```http +GET /admin/fleet/dashboard/summary +Authorization: Bearer +``` + +`upcomingTrips[]` 与派单看板列表同步字段: + +```json +{ + "code": 200, + "message": "成功", + "success": true, + "data": { + "pendingAssignmentCount": 111, + "upcomingTrips": [ + { + "orderId": "2074746808742928386", + "orderNo": "HL20260708144557879", + "teamNo": "26-7218", + "contactName": "何子墨", + "customerName": "何子墨", + "productName": "测试核心产品-单档-固定订金", + "startDate": "2026-07-20", + "headcount": 2, + "assignmentStatus": "unassigned", + "assignmentStatusLabel": "待派车", + "consultantName": "阿如娜", + "consultantDisplayName": "阿如娜", + "canAssign": true, + "canRejectRequirement": true + } + ] + } +} +``` + +前端处理同派单看板列表:工作台快捷入口可直接根据 `canAssign/canRejectRequirement` 展示派车/驳回入口。 + +## 7. 取消派单 + +```http +DELETE /admin/fleet/assignments/2075001000000000001 +Authorization: Bearer +Content-Type: application/json + +{ + "cancelReason": "客户调整接送时间,原司机无法执行", + "driverNotified": true +} +``` + +响应示例: + +```json +{ + "code": 200, + "message": "成功", + "success": true, + "data": { + "assignmentStatus": "canceled", + "sideEffects": { + "vehicleReleased": true, + "driverReleased": true + } + } +} +``` + +前端处理: + +- `driverNotified=false`:可直接二次确认后取消。 +- `driverNotified=true`:仍可取消,但必须用强提示,例如“已告知司机,取消后需线下同步司机,确认取消?”。 +- 不要因为已告知司机就禁用取消按钮;后端已经允许并记录该标记。 + +## 8. 司机拒接/退回待派 + +司机拒接或车务确认本次排车无效时调用。只允许 `holding` 状态。 + +```http +POST /admin/fleet/assignments/2075001000000000001/driver-reject +Authorization: Bearer +Content-Type: application/json + +{ + "rejectReason": "司机临时无法出车" +} +``` + +响应示例: + +```json +{ + "code": 200, + "message": "成功", + "success": true, + "data": { + "assignmentStatus": "unassigned", + "assignmentGroupId": "2075001000000000001", + "sideEffects": { + "vehicleReleased": true, + "driverReleased": true + } + } +} +``` + +错误响应示例: + +```json +{ + "code": 605020, + "message": "当前状态不允许退回待派", + "success": false, + "data": null +} +``` + +前端处理: + +- 司机未确认前的 `holding` 行可展示“司机拒接/退回待派”。 +- 调用成功后该行回到 `unassigned`,可重新派车。 +- 已 `assigned` 的行不走本接口,如需取消用 §7 取消派单。 + +## 9. 车务聊天在线态 + +```http +POST /admin/message/chat/open-fleet +Authorization: Bearer +Content-Type: application/json + +{ + "orderId": "2074746808742928386", + "peerAdminId": null +} +``` + +响应中的对端在线态现在按“车务管理员角色是否有人在线”聚合: + +```json +{ + "code": 200, + "message": "成功", + "success": true, + "data": { + "conversationKey": "FLEET:2074746808742928386", + "peer": { + "peerAdminId": "0", + "peerRole": "FLEET", + "peerRoleLabel": "车务", + "peerOnline": true + }, + "order": { + "orderId": "2074746808742928386", + "orderNo": "HL20260708144557879", + "customerName": "何子墨", + "teamNo": "26-7218" + } + } +} +``` + +前端处理: + +- `peerRole=FLEET` 且 `peerAdminId=0` 表示车务团队,不是某一个 admin。 +- 只要任一车务管理员在线,`peerOnline=true`。 +- 管理员必须以车务角色登录系统,才会被计入车务团队在线态。 + +## 10. 前端对接清单 + +| 页面/弹窗 | 必做 | +|----------|------| +| 调整订单车辆页 | 车型选大类,座位选 seatOptions;余座读后端 `remainingPassengerSeats`。 | +| 订单详情行程 Tab | `vehicleGroup.requirement` 新增座位字段直接展示,不要前端自算司机座。 | +| 派单看板 | 状态下拉读 `summary.statusOptions`;列表保持分页;卡片展示联系人/人数/定制师/团号。 | +| 派单看板操作 | `canAssign` 控制派车按钮,`canRejectRequirement` 控制驳回需求按钮。 | +| 派单取消 | `driverNotified=true` 时强提示,但不能禁用取消。 | +| 排车中 | `holding` 显示为“排车中”,含义是司机未确认。 | +| 车务聊天 | `peerOnline` 按车务团队聚合显示在线/离线。 | + +## 11. 后端本地验证 + +```bash +mvn -pl hl-order-service-v3 -am "-Dtest=RequirementServiceTest,OrderDetailServiceTest,OrderDetailConverterTest" "-DfailIfNoTests=false" test +mvn -pl hl-fleet-service -am "-Dtest=BoardOrderServiceTest,BoardSummaryServiceTest,FleetDashboardSummaryServiceTest,AssignmentControllerTest,AssignmentServiceTest,AssignmentStateMachineHelperTest" "-DfailIfNoTests=false" test +mvn -pl hl-user-service -am "-Dtest=ChatManagerTest,ConversationMemberServiceTest,AdminPresenceServiceTest,AdminSseServiceTest" "-DfailIfNoTests=false" test +``` + +覆盖点: + +- 5 座车只允许 4 名乘客;5 名乘客提交 1 台 5 座车返回 `582027`。 +- 订单详情 `vehicleGroup.requirement` 返回 `passengerSeatCapacity/remainingPassengerSeats`。 +- 派车 strictSeats 对 5 座车 + 5 名乘客返回 `605002`。 +- 派单看板返回联系人/团号/定制师/操作能力/状态文案。 +- `statusOptions` 由后端返回,含 `holding=排车中`。 +- 车务团队在线态按车务角色聚合。