14 KiB
14 KiB
【前端对接·管理后台】车务提需求与派单看板闭环契约
Issue: wx/HL#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. 提交用车需求
PUT /v3/admin/order/2074746808742928386/vehicle-requirement
Authorization: Bearer <admin-token>
Content-Type: application/json
请求示例:
{
"fleet": [
{
"vehicleType": "suv2",
"seats": 5,
"count": 1
}
],
"specialTags": ["大行李空间", "中文司机"],
"remark": "接机后直接去酒店"
}
成功响应新增座位字段:
{
"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
}
}
座位公式:
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 座车会失败:
{
"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
GET /v3/admin/order/2074746808742928386/itinerary
Authorization: Bearer <admin-token>
vehicleGroup.requirement 同步新增座位字段:
{
"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. 派单看板汇总与状态选项
GET /admin/fleet/board/summary
Authorization: Bearer <fleet-admin-token>
响应示例:
{
"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. 派单看板列表
GET /admin/fleet/board/orders?page=1&pageSize=10&statuses=unassigned&contactName=乐瑶&teamNo=26-7218&consultantName=阿如娜
Authorization: Bearer <fleet-admin-token>
新增/强化查询参数:
| 参数 | 类型 | 说明 |
|---|---|---|
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 | 司机姓名搜索,兼容旧页面。 |
响应示例:
{
"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. 车务工作台
GET /admin/fleet/dashboard/summary
Authorization: Bearer <fleet-admin-token>
upcomingTrips[] 与派单看板列表同步字段:
{
"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. 取消派单
DELETE /admin/fleet/assignments/2075001000000000001
Authorization: Bearer <fleet-admin-token>
Content-Type: application/json
{
"cancelReason": "客户调整接送时间,原司机无法执行",
"driverNotified": true
}
响应示例:
{
"code": 200,
"message": "成功",
"success": true,
"data": {
"assignmentStatus": "canceled",
"sideEffects": {
"vehicleReleased": true,
"driverReleased": true
}
}
}
前端处理:
driverNotified=false:可直接二次确认后取消。driverNotified=true:仍可取消,但必须用强提示,例如“已告知司机,取消后需线下同步司机,确认取消?”。- 不要因为已告知司机就禁用取消按钮;后端已经允许并记录该标记。
8. 司机拒接/退回待派
司机拒接或车务确认本次排车无效时调用。只允许 holding 状态。
POST /admin/fleet/assignments/2075001000000000001/driver-reject
Authorization: Bearer <fleet-admin-token>
Content-Type: application/json
{
"rejectReason": "司机临时无法出车"
}
响应示例:
{
"code": 200,
"message": "成功",
"success": true,
"data": {
"assignmentStatus": "unassigned",
"assignmentGroupId": "2075001000000000001",
"sideEffects": {
"vehicleReleased": true,
"driverReleased": true
}
}
}
错误响应示例:
{
"code": 605020,
"message": "当前状态不允许退回待派",
"success": false,
"data": null
}
前端处理:
- 司机未确认前的
holding行可展示“司机拒接/退回待派”。 - 调用成功后该行回到
unassigned,可重新派车。 - 已
assigned的行不走本接口,如需取消用 §7 取消派单。
9. 车务聊天在线态
POST /admin/message/chat/open-fleet
Authorization: Bearer <admin-token>
Content-Type: application/json
{
"orderId": "2074746808742928386",
"peerAdminId": null
}
响应中的对端在线态现在按“车务管理员角色是否有人在线”聚合:
{
"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. 后端本地验证
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=排车中。- 车务团队在线态按车务角色聚合。