diff --git a/changelogs-v2/2026-07/60_4936_车务看板详情候选与矩阵读模型统一-管理后台.md b/changelogs-v2/2026-07/60_4936_车务看板详情候选与矩阵读模型统一-管理后台.md new file mode 100644 index 0000000..73a4598 --- /dev/null +++ b/changelogs-v2/2026-07/60_4936_车务看板详情候选与矩阵读模型统一-管理后台.md @@ -0,0 +1,775 @@ +# 【前端对接·管理后台】车务看板、详情、候选与矩阵读模型统一 + +> Issue: [wx/HL#4936](https://git.1814.love:8443/wx/HL/issues/4936) +> +> PR: [wx/HL#5031](https://git.1814.love:8443/wx/HL/pulls/5031)、[wx/HL#5032](https://git.1814.love:8443/wx/HL/pulls/5032)、[wx/HL#5034](https://git.1814.love:8443/wx/HL/pulls/5034) +> +> 服务: `hl-fleet-service` / `hl-order-service-v3` +> +> 日期: 2026-07-18 +> +> 影响范围: 车务派单看板汇总与列表、派单详情、车辆/司机候选、矩阵月视图、相邻订单衔接风险 + +## 一、对接结论 + +1. 派单看板继续使用分页接口,`pageSize` 最大 100;没有新增“不分页全量接口”。 +2. `/summary` 与 `/orders` 共用日期、车型、司机、联系人、团号、定制师和 `keyword` 筛选;汇总忽略 `status/statuses/page/pageSize`,返回同一筛选范围内的全部状态分面。`pendingCount/pendingUrgentCount/todayDepartCount/holdingTimeoutCount` 同样随这些订单筛选变化;`idleVehicleCount/idleDriverCount` 是不随订单筛选变化的全局资源指标。 +3. 派单列表、详情和矩阵均以**当前订单 + 当前有效用车需求 + 当前有效派车组**为准,历史需求和历史派单不能覆盖当前数据。 +4. 详情一次返回逐日行程、大交通、当前需求、全部有效派车组、生命周期、凭证和操作记录。 +5. 候选车辆和司机分别分页,允许先选车或先选司机;返回完整闭区间可用时间窗、结构化可用性原因、冲突和常驻关系。 +6. 矩阵按整月查询,但同一跨月派车组先补齐完整组再裁剪显示;不会因只查到月内一天而丢失真实起止日期。 +7. 矩阵相邻订单衔接风险由后端返回 `status/statusLabel/style/reasonCode/reasonMessage`,前端不得自行根据颜色或时间重新推导。 +8. **大交通允许不填写。** 无大交通时仍可提交用车需求、查询候选、预检和派车;前端只能显示提示,不得禁用派车按钮。 +9. 所有雪花 ID 均按字符串处理,禁止转为 JavaScript `Number`。 +10. 本次未修改 `hl-ui`,前端只按本文完成接口对接。 + +## 二、接口清单 + +| # | 接口 | 方法 | 路径 | 用途 | +|---|---|---|---|---| +| 1 | 看板汇总 | GET | `/admin/fleet/board/summary` | 同筛选状态计数、急单数、资源数、定制师选项 | +| 2 | 看板列表 | GET | `/admin/fleet/board/orders` | 分页卡片、统一筛选、当前状态和操作能力 | +| 3 | 看板详情 | GET | `/admin/fleet/board/orders/{orderId}` | 当前订单、当前需求、行程、大交通、派车组和日志 | +| 4 | 出行人脱敏列表 | GET | `/admin/fleet/board/orders/{orderId}/travelers` | 默认脱敏查看出行人 | +| 5 | 出行人明文查询 | POST | `/admin/fleet/board/orders/{orderId}/travelers/plain` | 有权限且有审计理由时查看明文 | +| 6 | 派单候选 | POST | `/admin/fleet/assignments/candidates` | 车辆和司机独立分页、冲突、可用时间窗、常驻关系 | +| 7 | 矩阵月视图 | GET | `/admin/fleet/matrix/grid` | 车辆月历、派车段、并行车辆、大交通和衔接风险 | + +## 三、看板汇总与列表公共筛选 + +### 3.1 查询参数 + +| 参数 | 类型 | 必填 | 说明 | +|---|---|---|---| +| `statuses` | `string[]` | 否 | 多状态任一命中;支持重复 query 参数或逗号分隔。 | +| `status` | `string` | 否 | 单状态/逗号分隔别名,与 `statuses` 合并。 | +| `startDayFrom` | `date` | 否 | 日期区间起,与当前行程闭区间做重叠匹配。 | +| `startDate` | `date` | 否 | `startDayFrom` 别名;前者未传时生效。 | +| `startDayTo` | `date` | 否 | 日期区间止,与当前行程闭区间做重叠匹配。 | +| `endDate` | `date` | 否 | `startDayTo` 别名;前者未传时生效。 | +| `vehicleTypeKeys` | `string[]` | 否 | 车型大类:`suv/mpv/bus/sedan`,任一命中。 | +| `typeKeys` | `string[]` | 否 | `vehicleTypeKeys` 别名。 | +| `driverName` | `string` | 否 | 当前司机姓名模糊匹配。 | +| `keyword` | `string` | 否 | 司机、联系人/客户、团号、订单号、当前负责定制师展示名任一包含即命中。定制师展示名为企业微信昵称优先、用户名兜底;order-v3 降级时回退派单快照,只匹配后端最终解析出的一个展示名。 | +| `contactName` | `string` | 否 | 联系人/客户名模糊匹配。 | +| `contactKeyword` | `string` | 否 | `contactName` 别名。 | +| `teamNo` | `string` | 否 | 团号包含匹配,例如 `7218` 可命中 `26-7218`。 | +| `consultantId` | `string` | 否 | 当前负责定制师管理员 ID 精确匹配。 | +| `plannerName` | `string` | 否 | 定制师显示名模糊匹配兼容参数。 | +| `consultantName` | `string` | 否 | `plannerName` 别名。 | +| `variant` | `string` | 否 | `list` 默认;`grid` 为兼容值,其他值返回参数错误。 | +| `page` | `int` | 列表否 | 默认 1;汇总忽略。 | +| `pageSize` | `int` | 列表否 | 默认 20、最大 100;汇总忽略。 | + +状态值: + +```text +unassigned / unassigned_urgent / holding / holding_urgent / +assigned / change_requested / completed / canceled +``` + +状态含义由后端 `statusOptions` 返回。`holding` 是“车务已排车、司机尚未完成确认链路”,不是“车务正在浏览详情”。 + +### 3.2 汇总请求示例 + +```http +GET /admin/fleet/board/summary?startDate=2026-07-01&endDate=2026-07-31&typeKeys=suv&keyword=王 +Authorization: Bearer +``` + +### 3.3 汇总响应示例 + +```json +{ + "code": 200, + "message": "成功", + "success": true, + "data": { + "pendingCount": 3, + "pendingUrgentCount": 1, + "todayDepartCount": 1, + "idleVehicleCount": 9, + "idleDriverCount": 5, + "holdingTimeoutCount": 1, + "statusCounts": { + "unassigned": 3, + "holding": 1, + "assigned": 2, + "changeRequested": 0, + "completed": 4, + "canceled": 1, + "unassignedUrgent": 1, + "holdingUrgent": 1 + }, + "statusOptions": [ + { + "value": "unassigned", + "label": "待派车", + "count": 3, + "urgentCount": 1 + }, + { + "value": "holding", + "label": "排车中", + "count": 1, + "urgentCount": 1 + }, + { + "value": "assigned", + "label": "已派车", + "count": 2, + "urgentCount": 0 + } + ], + "consultantOptions": [ + { + "value": "2000000000000000001", + "label": "企业微信昵称" + } + ] + } +} +``` + +一致性规则: + +```text +同一组非状态筛选条件下: +summary.statusOptions[value=X].count + == orders?statuses=X 返回的 data.total +``` + +`idleVehicleCount/idleDriverCount` 是当前物理资源指标,不受订单文字筛选影响;其他订单状态计数使用同一筛选后的记录集。 + +### 3.4 列表请求示例 + +```http +GET /admin/fleet/board/orders?page=1&pageSize=20&statuses=unassigned,holding&keyword=7218&consultantId=2000000000000000001 +Authorization: Bearer +``` + +### 3.5 列表响应示例 + +```json +{ + "code": 200, + "message": "成功", + "success": true, + "data": { + "page": 1, + "pageSize": 20, + "total": 1, + "records": [ + { + "id": "HL202607180001", + "orderNo": "HL202607180001", + "orderId": "2000000000000000101", + "teamNo": "26-7218", + "assignmentId": "2000000000000000201", + "assignmentGroupId": "2000000000000000201", + "fleetItemIndex": 0, + "customerName": "测试联系人", + "contactName": "测试联系人", + "productName": "测试产品", + "headcount": 4, + "adultCount": 3, + "childCount": 1, + "youngChildCount": 0, + "babyCount": 0, + "startDate": "2026-07-20", + "endDate": "2026-07-22", + "days": 3, + "pickupAt": null, + "dropoffAt": null, + "isHailarPickup": false, + "isHailarDropoff": false, + "consultantId": "2000000000000000001", + "plannerName": "企业微信昵称", + "consultantName": "企业微信昵称", + "consultantDisplayName": "企业微信昵称", + "specialTags": ["中文司机", "大行李空间"], + "requirementRemark": "无大交通,按行程安排车辆", + "requiredVehicles": [ + { + "vehicleType": "suv", + "categoryLabel": "SUV系列", + "seats": 7, + "count": 1 + } + ], + "assignmentStatus": "unassigned", + "assignmentStatusLabel": "待派车", + "lifecycleStageCode": "requirement_pending", + "lifecycleStageLabel": "待车务派车", + "currentStep": 1, + "availableActionCodes": ["ASSIGN", "REJECT_REQUIREMENT"], + "urgentBadge": null, + "canAssign": true, + "canRejectRequirement": true + } + ] + } +} +``` + +### 3.6 列表字段绑定规则 + +| 字段 | 前端规则 | +|---|---| +| `teamNo` | 展示当前团号;空值不回退拼造。 | +| `contactName` | 卡片联系人。 | +| `consultantDisplayName` | 定制师展示名,企业微信昵称优先、用户名兜底。 | +| `startDate/endDate/days` | 当前订单档期,闭区间含首尾。 | +| `specialTags/requirementRemark` | 当前有效用车需求,不得混入历史需求。 | +| `assignmentStatusLabel/lifecycleStageLabel` | 直接展示,前端不维护独立中文映射。 | +| `availableActionCodes/canAssign/canRejectRequirement` | 决定操作入口;急单样式不得隐藏按钮。 | + +列表按派车组聚合;底层一天一条派车切片不会把同一派车组重复成多张卡片。紧急待处理在前、普通进行中次之、终态沉底,同优先级以稳定 ID 兜底;前端不得二次排序。 + +## 四、派单详情 + +### 4.1 请求 + +```http +GET /admin/fleet/board/orders/2000000000000000101 +Authorization: Bearer +``` + +路径参数是数字订单 ID,按字符串传递,不是订单号。 + +### 4.2 响应示例 + +```json +{ + "code": 200, + "message": "成功", + "success": true, + "data": { + "id": "HL202607180001", + "orderNo": "HL202607180001", + "teamNo": "26-7218", + "customerName": "测试联系人", + "headcount": 4, + "adultCount": 3, + "childCount": 1, + "youngChildCount": 0, + "babyCount": 0, + "startDate": "2026-07-20", + "endDate": "2026-07-22", + "pickupAt": null, + "dropoffAt": null, + "productName": "测试产品", + "consultantId": "2000000000000000001", + "plannerName": "企业微信昵称", + "consultantName": "企业微信昵称", + "consultantDisplayName": "企业微信昵称", + "specialTags": ["中文司机", "大行李空间"], + "requirementRemark": "无大交通,按行程安排车辆", + "itinerary": { + "theme": "草原三日", + "route": "海拉尔 → 额尔古纳 → 满洲里", + "days": [ + { + "dayNumber": 1, + "date": "2026-07-20", + "title": "抵达海拉尔", + "detail": "市区行程" + }, + { + "dayNumber": 2, + "date": "2026-07-21", + "title": "额尔古纳", + "detail": "草原行程" + }, + { + "dayNumber": 3, + "date": "2026-07-22", + "title": "满洲里", + "detail": "返程" + } + ] + }, + "transport": { + "transferTimeHint": "暂无接送机时间", + "arrive": null, + "depart": null, + "batches": [], + "pickupRequired": null + }, + "currentAssignment": { + "id": "2000000000000000201", + "assignmentGroupId": "2000000000000000201", + "fleetItemIndex": 0, + "requiredVehicleType": "suv", + "requiredSeats": 7, + "startDate": "2026-07-20", + "endDate": "2026-07-22", + "vehicleId": "2000000000000000301", + "vehiclePlate": "蒙A·TEST1", + "driverId": "2000000000000000401", + "driverName": "测试司机", + "baseAssignmentStatus": "assigned", + "assignmentStatus": "assigned", + "lifecycleStageCode": "confirmed", + "lifecycleStageLabel": "已确认执行", + "currentStep": 4, + "availableActionCodes": ["CANCEL", "CHANGE_DRIVER", "COMPLETE_EARLY"], + "protocolPrice": "1300.00", + "driverConfirmationEvidenceFileIds": [] + }, + "activeAssignments": [ + { + "assignmentGroupId": "2000000000000000201", + "fleetItemIndex": 0, + "startDate": "2026-07-20", + "endDate": "2026-07-22", + "vehiclePlate": "蒙A·TEST1", + "driverName": "测试司机", + "assignmentStatus": "assigned" + } + ], + "operationLog": { + "records": [], + "total": 0, + "summary": {"totalCount": 0} + }, + "relatedDetailReady": true + } +} +``` + +### 4.3 当前需求和派车组隔离 + +- `currentAssignment` 是最新一个有效派车组的兼容字段。 +- `activeAssignments` 才是当前需求下全部有效派车组;一单多车时必须渲染完整数组。 +- 历史需求、已取消组和旧订单快照不能进入当前需求详情。 +- `baseAssignmentStatus` 是落库基础态;`assignmentStatus` 是当前有效状态,前端展示后者。 + +### 4.4 逐日行程规则 + +- `itinerary.days` 来自 `order_itinerary_day`,一天一条事实数据。 +- 返回日期必须位于当前 `[startDate,endDate]`,按 `dayNumber` 稳定排序。 +- 改期后按当前订单档期对齐;越界、旧版本和非法日序数据不返回。 +- 无有效行程时返回 `days=[]`,前端显示空态,不得生成假行程。 + +### 4.5 大交通规则 + +有数据时: + +- 到达接客使用大交通 `arriveTime`。 +- 返程送客使用大交通 `departTime`。 +- 分批接送完整返回 `batches`。 + +无数据时固定返回: + +```json +{ + "transport": { + "transferTimeHint": "暂无接送机时间", + "arrive": null, + "depart": null, + "batches": [], + "pickupRequired": null + } +} +``` + +**空大交通是正常业务状态,不是参数错误,也不是派车阻断条件。** `pickupAt/dropoffAt` 同样允许为 `null`。 + +## 五、车辆与司机候选 + +### 5.1 请求 + +```http +POST /admin/fleet/assignments/candidates +Authorization: Bearer +Content-Type: application/json +``` + +```json +{ + "orderId": "2000000000000000101", + "requirementId": "2000000000000000501", + "fleetItemIndex": 0, + "startDate": "2026-07-20", + "endDate": "2026-07-22", + "headcount": 4, + "selectedVehicleId": null, + "selectedDriverId": null, + "excludeAssignmentId": null, + "vehicleKeyword": "GL8", + "driverKeyword": "张", + "vehiclePage": 1, + "vehiclePageSize": 20, + "driverPage": 1, + "driverPageSize": 20 +} +``` + +`pickupAt/dropoffAt` 未出现是合法请求;有城市信息时可额外传入,供城市衔接判断使用。 + +### 5.2 请求参数 + +| 参数 | 类型 | 必填 | 说明 | +|---|---|---|---| +| `orderId` | `string` | 改派时是 | 当前订单 ID。 | +| `requirementId` | `string` | 改派时是 | 当前有效用车需求 ID。 | +| `fleetItemIndex` | `int` | 否 | 当前车型项序号,从 0 开始。 | +| `startDate/endDate` | `date` | 是 | 请求用车闭区间。 | +| `pickupAt/dropoffAt` | `string` | 否 | 城市衔接辅助信息;空值不阻断候选和派车。 | +| `headcount` | `int` | 否 | 乘客人数,不含司机。车辆乘客容量=`seats-1`。 | +| `selectedVehicleId` | `string` | 否 | 已选车辆,支持先选车。 | +| `selectedDriverId` | `string` | 否 | 已选司机,支持先选司机。 | +| `excludeAssignmentId` | `string` | 否 | 改派时排除当前派单,且必须属于当前订单和需求。 | +| `vehicleKeyword` | `string` | 否 | 车牌或车型。 | +| `driverKeyword` | `string` | 否 | 司机姓名或完整手机号。 | +| `vehiclePage/driverPage` | `int` | 是 | 各自分页页码,默认 1。 | +| `vehiclePageSize/driverPageSize` | `int` | 是 | 各自每页条数,最大 100。 | + +### 5.3 响应示例 + +```json +{ + "code": 200, + "message": "成功", + "success": true, + "data": { + "vehicles": { + "records": [ + { + "vehicleId": "2000000000000000301", + "plate": "蒙A·TEST1", + "modelName": "测试车型", + "seats": 7, + "passengerCapacity": 6, + "seatsEnough": true, + "fleet": "own", + "selected": false, + "available": true, + "availabilityReasonCode": "AVAILABLE", + "availabilityReasonMessage": "所选服务日期内可用", + "availabilityWindows": [ + {"startDate": "2026-07-20", "endDate": "2026-07-22"} + ], + "residentMatch": false, + "crossResident": false, + "requiresCrossResidentConfirmation": false, + "relationMessage": null, + "conflicts": [] + } + ], + "total": 1, + "page": 1, + "pageSize": 20 + }, + "drivers": { + "records": [ + { + "driverId": "2000000000000000401", + "name": "测试司机", + "maskedPhone": "138****0000", + "years": 8, + "season": "active", + "completedOrderCount": 12, + "rating": 5.0, + "ratingDefaulted": true, + "selected": false, + "available": true, + "availabilityReasonCode": "CITY_JUNCTION_SHAREABLE", + "availabilityReasonMessage": "仅存在可衔接的同城边界占用", + "availabilityWindows": [ + {"startDate": "2026-07-20", "endDate": "2026-07-22"} + ], + "residentMatch": false, + "crossResident": false, + "requiresCrossResidentConfirmation": false, + "conflicts": [] + } + ], + "total": 1, + "page": 1, + "pageSize": 20 + }, + "selectedRelation": null + } +} +``` + +### 5.4 候选判定规则 + +| `availabilityReasonCode` | 含义 | `available` | +|---|---|---| +| `AVAILABLE` | 整个请求闭区间无阻塞占用 | `true` | +| `CITY_JUNCTION_SHAREABLE` | 只有满足规则的城市边界衔接 | `true` | +| `ASSIGNMENT_CONFLICT` | 请求区间内存在阻塞派单 | `false` | + +- `availabilityWindows` 是请求区间内的实际可用**闭区间**,冲突会切分时间窗。 +- 车辆和司机都必须覆盖完整请求区间才可直接选中。 +- 司机无评价时返回 `rating=5.0` 且 `ratingDefaulted=true`;有真实评价时为 `false`。 +- 车辆总座位数包含司机,`passengerCapacity=seats-1`;前端不得把司机座位再次给乘客。 +- `selectedRelation` 在车辆和司机都已选时返回常驻关系。跨常驻组合必须显示后端提示并显式确认。 + +## 六、矩阵月视图 + +### 6.1 请求 + +```http +GET /admin/fleet/matrix/grid?year=2026&month=7&season=active&fleets=own,coopA&typeKeys=suv&status=all +Authorization: Bearer +``` + +### 6.2 请求参数 + +| 参数 | 类型 | 必填 | 说明 | +|---|---|---|---| +| `year` | `int` | 是 | 查询年份。 | +| `month` | `int` | 是 | 1-12,越界返回车务月份错误。 | +| `season` | `string` | 否 | `active` 默认;也支持 `pending/archived/blacklist`。 | +| `fleets` | `string[]` | 否 | `own/coopA/coopB` 多选。 | +| `typeKeys` | `string[]` | 否 | `suv/mpv/bus/sedan` 多选。 | +| `status` | `string` | 否 | `all` 默认、`unassigned`、`assigned`。 | + +### 6.3 响应示例 + +```json +{ + "code": 200, + "message": "成功", + "success": true, + "data": { + "year": 2026, + "month": 7, + "daysInMonth": 31, + "todayDay": 18, + "weekendDays": [4, 5, 11, 12, 18, 19, 25, 26], + "fleetCount": {"own": 5, "coopA": 4, "coopB": 3}, + "statusCounts": { + "totalAssignments": 21, + "unassignedAssignments": 10, + "assignedAssignments": 11, + "totalOrders": 16, + "unassignedOrders": 5, + "partialOrders": 2, + "assignedOrders": 11 + }, + "unassignedWindowCount": 5, + "vehicles": [ + { + "id": "2000000000000000301", + "plate": "蒙A·TEST1", + "modelName": "测试车型", + "seats": 7, + "fleet": "own", + "primaryDriverName": "测试司机", + "primaryDriverPhone": "138****0000", + "assignments": [ + { + "id": "2000000000000000201", + "assignmentGroupId": "2000000000000000201", + "orderNumericId": "2000000000000000101", + "orderNo": "HL202607180001", + "teamNo": "26-7218", + "consultantId": "2000000000000000001", + "consultantName": "企业微信昵称", + "customerName": "测试联系人", + "headcount": 4, + "adultCount": 3, + "childCount": 1, + "startDay": 20, + "endDay": 22, + "startDate": "2026-07-20", + "endDate": "2026-07-22", + "clippedHead": false, + "clippedTail": false, + "vehicleCategory": "suv", + "categoryLabel": "SUV系列", + "assignmentStatus": "assigned", + "protocolPrice": "1300.00", + "vehicleSpecialTags": ["中文司机"], + "vehicleRequirementRemark": "按行程安排", + "pickupTransports": [], + "dropoffTransports": [], + "parallelAssignments": [ + { + "assignmentGroupId": "2000000000000000201", + "fleetItemIndex": 0, + "vehicleCategory": "suv", + "categoryLabel": "SUV系列", + "requiredSeats": 7, + "startDate": "2026-07-20", + "endDate": "2026-07-22", + "vehicleId": "2000000000000000301", + "vehiclePlate": "蒙A·TEST1", + "driverId": "2000000000000000401", + "driverName": "测试司机", + "assignmentStatus": "assigned", + "protocolPrice": "1300.00" + } + ] + } + ], + "connections": [ + { + "fromAssignmentGroupId": "2000000000000000201", + "toAssignmentGroupId": "2000000000000000202", + "fromEndDate": "2026-07-22", + "toStartDate": "2026-07-23", + "previousDepartureTime": null, + "nextArrivalTime": null, + "previousCity": null, + "nextCity": null, + "connectionMinutes": null, + "thresholdMinutes": 120, + "status": "MISSING_TIME", + "statusLabel": "缺少接送时间", + "style": "RED_DASHED", + "reasonCode": "PREVIOUS_REQUIRED_DEPARTURE_MISSING", + "reasonMessage": "前一订单缺少必需送客批次" + } + ] + } + ] + } +} +``` + +### 6.4 派单统计口径 + +- `totalAssignments` 是派车行数,不是订单数。 +- `totalOrders` 是去重订单数。 +- 一单同时存在已派和未派项时计入 `partialOrders`,也计入 `unassignedOrders`。 +- `unassignedWindowCount` 是含未派项的去重订单数,不等于未派逐日切片条数。 +- 跨月派车组使用完整原始 `startDate/endDate`,仅 `startDay/endDay` 裁剪到当前月;`clippedHead/clippedTail` 告诉前端是否跨月续接。 + +### 6.5 相邻订单衔接四态 + +| `status` | `style` | 含义 | +|---|---|---| +| `MISSING_TIME` | `RED_DASHED` | 缺少必需大交通、时刻或城市,无法完成衔接判断。 | +| `DIFFERENT_CITY` | `DARK_RED` | 前后订单城市不同。 | +| `SAME_CITY_TOO_SHORT` | `LIGHT_RED` | 同城但间隔小于配置阈值。 | +| `SAME_CITY_OK` | `GREEN` | 同城且间隔达到配置阈值。 | + +原因码: + +```text +PREVIOUS_REQUIRED_DEPARTURE_MISSING +NEXT_REQUIRED_ARRIVAL_MISSING +PREVIOUS_DEPARTURE_TIME_MISSING +NEXT_ARRIVAL_TIME_MISSING +PREVIOUS_DEPARTURE_CITY_MISSING +NEXT_ARRIVAL_CITY_MISSING +DIFFERENT_CITY +SAME_CITY_INTERVAL_TOO_SHORT +SAME_CITY_INTERVAL_SUFFICIENT +``` + +同城最小衔接阈值读取 Nacos 配置,默认 120 分钟;恰好等于阈值属于 `SAME_CITY_OK`。 + +注意:`MISSING_TIME` 是矩阵风险提示,不表示订单不能派车。用户未填写大交通时仍允许完成派车。 + +## 七、出行人权限与审计 + +### 7.1 脱敏列表 + +```http +GET /admin/fleet/board/orders/2000000000000000101/travelers +Authorization: Bearer +``` + +默认返回姓名、年龄类型和脱敏证件/手机号;前端日常派车只使用此接口。 + +### 7.2 明文查询 + +```http +POST /admin/fleet/board/orders/2000000000000000101/travelers/plain +Authorization: Bearer +Content-Type: application/json +``` + +```json +{ + "reason": "司机出发前核对接客人信息" +} +``` + +明文接口必须经过权限校验并记录操作人、订单、理由和时间。前端不得缓存、日志打印或二次持久化明文个人信息。 + +## 八、前端必须处理 + +1. 看板使用分页接口,分页器读取 `data.total/page/pageSize`。 +2. 状态项、状态中文、急单数读取 `summary.statusOptions`,不维护独立枚举和独立计数。 +3. 汇总请求必须携带与列表相同的非状态筛选;不要把当前状态筛选传成汇总统计范围。 +4. 卡片展示 `teamNo/contactName/headcount/consultantDisplayName/startDate/endDate/specialTags/requirementRemark`。 +5. 定制师选择值传 `consultantId`;显示使用后端返回的企业微信优先名称。 +6. 统一文本框传 `keyword`,可同时匹配司机、联系人、团号、订单号和当前定制师展示名;定制师按企业微信昵称优先、用户名兜底,前端不得自行并行匹配多个名称别名。 +7. 操作按钮使用 `availableActionCodes/canAssign/canRejectRequirement`,不能按颜色或前端状态猜测。 +8. 详情多车读取 `activeAssignments`;`currentAssignment` 只是兼容的最新一组。 +9. `transport.transferTimeHint="暂无接送机时间"` 时显示提示,但保持派车入口可用。 +10. 候选资源分别读取 `vehicles/drivers` 分页;显示后端原因和常驻关系提示。 +11. 矩阵直接使用 `connections[].status/style/reasonCode/reasonMessage`;大红、淡红、虚线红和绿色语义不能自行交换。 +12. 所有雪花 ID 当字符串处理。 + +## 九、兼容性与不影响范围 + +- 保留 `status/startDate/endDate/typeKeys/contactKeyword/consultantName` 等兼容别名。 +- 不新增前端内部接口,不改变现有派车写接口。 +- 不要求填写大交通,也不把空大交通改成校验错误。 +- 不改变“一天一条派车切片”的数据库事实模型;本轮只修正读模型聚合。 +- 不处理团期配车。 +- 不修改 `hl-ui`。 + +## 十、验证证据 + +### 10.1 代码与测试 + +```text +hl-fleet-service 定向测试:253/253 通过 +hl-fleet-service 全量 verify:1799/1799 通过 +hl-order-service-v3 相关契约测试:30/30 通过 +独立代码评审:无 P0/P1 阻断项 +OpenAPI 说明定向校验:spotless:check + compile 通过 +``` + +Order-v3 全量测试中 4 项环境/基线失败已在同提交干净基线复现:3 项为 H2 缺少 `payment_manual_receipt`,1 项为既有本地缓存架构门禁;不由本次车务改动引入。 + +### 10.2 部署 + +```text +hl-order-service-v3:Deploy Panel 任务 22ec81e8,8086/8186 双实例成功 +hl-fleet-service:Deploy Panel 任务 68487f94,8087/8187 双实例成功 +hl-fleet-service OpenAPI 口径补充:Deploy Panel 任务 249594e3,8087/8187 双实例成功 +``` + +部署后已通过网关读取车务 OpenAPI,确认线上文档明确区分“同一订单筛选范围内的状态计数”与“不随订单筛选变化的全局空闲资源数”,并包含统一关键词对当前定制师展示名的匹配规则。 + +### 10.3 真实网关 API + +使用独立车务和定制师测试账号,经 `https://api.test.1814.love:9443` 完成真实订单全流程回归;未使用 `admin`、`wx` 或 Mock 数据。 + +```text +最终回归:161/161 通过,失败 0 +覆盖:看板汇总/列表/详情、统一筛选、定制师、脱敏/明文出行人、候选车辆/司机、 + 常驻与跨常驻、预检、派车、司机确认、取消/恢复/改派/提前完结、矩阵、价格、 + 车辆/司机、对账、模板,以及无大交通订单完整派车链路。 +``` + +无大交通真实场景额外断言: + +```text +- 新建真实订单并补齐 2 名出行人 +- 不创建任何大交通计划 +- 成功提交有效用车需求 +- 详情:arrive=null、depart=null、batches=[]、transferTimeHint=暂无接送机时间 +- 候选查询成功,pickupAt/dropoffAt 均省略 +- 派车预检成功,conflict=false +- 直派成功并进入已派车状态 +- DB:派车组逐日 6 条,pickup_at/dropoff_at 6 条均为空 +``` + +## 十一、相关文档 + +- 看板字段、统计、行程和保险事务:`57_4882_车务派单看板当前订单字段统计行程与保险事务收口-管理后台.md` +- 需求级最终完成回调:`59_4935_车务需求级派单完成回调与滚动发布契约-管理后台.md` +- 提需求和派单基础契约:`53_4871_车务提需求派单看板闭环契约-管理后台.md` +- 团号与定制师筛选:`54_4876_派单看板团号与定制师下拉筛选-管理后台.md`