41 KiB
schema, ticket, title, consumer, author, change_type, backend_status, gateway_status, frontend_status, frontend_owner, frontend_ref, target_release, verified_at, status_note, updated_at, base
| schema | ticket | title | consumer | author | change_type | backend_status | gateway_status | frontend_status | frontend_owner | frontend_ref | target_release | verified_at | status_note | updated_at | base |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| hl-changelog/v2 | 8464 | 派单看板两个读口新增订单归属过滤 orderKind,团期配车菜单下线 | admin | wx(GIT) | 修改接口 | deployed | verified | implemented | mmg | d7e932ac5b0291cc260f3c04fb74eedc653e892a | v2.1 | 2026-09-28 | 前端已交付(派单看板订单归属页签+团期配车模块删除),详见 hl-admin v2.1 提交 d7e932ac。 | 2026-09-28 | dev-v3 |
hl-fleet-service: 派单看板加订单归属页签 orderKind,团期配车菜单下线
服务: hl-fleet-service(菜单行由 hl-user-service 下发)
PR: #8473(fleet,已合入 dev-v3,squash ad4e65a27)/ #8474(user 菜单,已合入 dev-v3,squash f49ae8d40)
Issue: #8464
日期: 2026-09-28
影响范围: 管理后台车务「派单看板」的列表与汇总两个读口;以及「团期配车」独立菜单入口
⚠️ 关键变化
GET /admin/fleet/board/orders与GET /admin/fleet/board/summary新增可选查询参数orderKind,取值ALL/NORMAL/GROUP,判据是「这条派车需求当前属不属于某个运营团期」。- 不传或传空串 =
ALL(不过滤)。这与订单列表GET /v3/admin/order的orderKind同名同取值但缺省相反(那边不传缺省NORMAL)。两个页面的筛选状态不能直接互相透传,详见「四、契约约束与正确调用方式」。 - 非法取值不被静默容忍:大小写不符(如
group)或取值域外(如XX)一律 HTTP 200 + bodycode=100001,data=null、success=false。 - 汇总接口的
consultantOptions、todayDepartCount、pendingCount、pendingUrgentCount、statusCounts、statusOptions[].count随orderKind一起收窄——它们是「当前筛选范围内」的分面,不是全局统计。只有idleVehicleCount/idleDriverCount是全局物理资源指标,切页签时这两个数字不会变(实测三档恒为 48 / 51)。 - 「团期配车」独立菜单行已删除,管理后台的
/fleet/group-dispatch路由不再注册,src/views/fleet/group-dispatch/整个模块失去唯一入口。前端要做的处置见「四 → 团期配车模块处置」。
一、背景
车务原本有两个并列菜单:派单看板(逐条用车需求)与团期配车(一团一行)。#7443 已给看板加了 groupBatchId 运营团期精确筛选(等值匹配某一个团)。本次补的是粗筛这一档——「只看普通订单」或「只看团期订单」,车务不必在两个菜单之间来回切。
粗细两档用的是同一个归属判定(GroupBatchOwnershipResolver):订单上下文的实时归属优先,order-v3 整体不可达时才回退派车行上的建行快照。所以 orderKind=GROUP 与 groupBatchId=<某团> 对同一批行给出一致的口径,不会出现「按团筛出来的行自己显示成非团」。
| 维度 | 粗筛 orderKind |
精筛 groupBatchId |
|---|---|---|
| 问题 | 属不属于团期 | 属不属于这一个团期 |
| 类型 | String 常量 |
Long(雪花,字符串透传) |
| 组合 | 与 groupBatchId 按 AND 组合 |
同左 |
二、变更接口清单
| # | 接口 | 方法 | 路径 | 变更类型 | 说明 |
|---|---|---|---|---|---|
| 1 | 派单看板列表 | GET | /admin/fleet/board/orders |
新增可选入参 | 新增 orderKind,缺省 ALL;非法值返 100001 |
| 2 | 派单看板汇总 | GET | /admin/fleet/board/summary |
新增可选入参 + 汇总口径 | 同上;且 consultantOptions / todayDepartCount 等分面随之收窄 |
三、接口详情
1. 派单看板列表 GET /admin/fleet/board/orders
VO: BoardOrderPageReqVO → BoardOrderPageRespVO
使用场景
车务「派单看板」主列表。本次在原有筛选条件上加一个订单归属页签(全部 / 常规订单 / 团期订单):切页签时把 orderKind 带上重新拉列表;选「全部」时可以不带该参数,与显式传 ALL 等价。
维度不变:同一 requirementId 只返回一条 record,dailyAssignments 保留逐日派车行;顶层兼容字段指向最需要处理的代表日行。
入参字段表
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|---|---|---|---|---|---|
| orderKind | Query | String | ❌ | ALL / NORMAL / GROUP,大小写敏感;其余值返 100001 |
本次新增。订单归属粗筛。不传或空串 = ALL 不过滤;NORMAL = 当前无团期归属;GROUP = 当前属于某个运营团期。首尾空白会被去掉后再判定 |
| groupBatchId | Query | Long | ❌ | 雪花 ID,字符串透传禁 Number() |
运营团期精确筛(#7443)。与 orderKind 按 AND 组合 |
| statuses | Query | String[] | ❌ | unassigned / unassigned_urgent / holding / holding_urgent / assigned / canceled / completed |
多状态筛选,任一命中即返;空 = 不按状态过滤 |
| status | Query | String | ❌ | 同 statuses 取值 |
别名,单值或逗号分隔,与 statuses 合并 |
| startDayFrom | Query | String | ❌ | YYYY-MM-DD |
日期区间起,与行程区间 [startDate,endDate] 重叠(不是仅出团日);单边只约束一侧 |
| startDayTo | Query | String | ❌ | YYYY-MM-DD |
日期区间止,同上 |
| startDate | Query | String | ❌ | YYYY-MM-DD |
startDayFrom 的别名,未传 startDayFrom 时生效 |
| endDate | Query | String | ❌ | YYYY-MM-DD |
startDayTo 的别名,未传 startDayTo 时生效 |
| vehicleTypeKeys | Query | String[] | ❌ | suv / mpv / bus / sedan |
车型大类多选;未派按需求车型、已派按实际车辆大类过滤 |
| typeKeys | Query | String[] | ❌ | 同上 | vehicleTypeKeys 的别名,未传时生效 |
| driverName | Query | String | ❌ | - | 司机姓名模糊搜索 |
| keyword | Query | String | ❌ | - | 统一文字搜索:司机 / 联系人客户 / 团号 / 订单号 / 定制师显示名,任一包含即命中 |
| contactName | Query | String | ❌ | - | 联系人、客户名模糊搜索 |
| contactKeyword | Query | String | ❌ | - | contactName 的别名 |
| teamNo | Query | String | ❌ | - | 团号模糊搜索,只匹配真实团号、不匹配订单号 |
| consultantId | Query | Long | ❌ | 下拉值来自 summary.consultantOptions |
当前负责定制师精确筛 |
| plannerName | Query | String | ❌ | - | 定制师姓名模糊搜索(兼容参数,优先用 consultantId) |
| consultantName | Query | String | ❌ | - | plannerName 的别名 |
| variant | Query | String | ❌ | list(默认)/ grid;其余值返 100001 |
视图切换 |
| page | Query | Integer | ❌ | ≥1,默认 1 | 页码 |
| pageSize | Query | Integer | ❌ | 1~100,默认 20 | 每页条数 |
| pageNo | Query | Integer | ❌ | 同 page |
历史别名,映射到 page |
出参字段表
| 字段 | 类型 | 说明 |
|---|---|---|
| data.records | Array | 需求卡片数组,结构见下方各行(本次无字段增删) |
| data.total | Long | 命中总条数(按 orderKind 过滤后的口径) |
| data.page | Integer | 当前页码 |
| data.pageSize | Integer | 每页条数 |
| data.records[].id | String | 与 orderNo 同值,列表行 key |
| data.records[].orderNo | String | 订单号 |
| data.records[].teamNo | String | 团号;order-v3 不可达时回退派车行快照 |
| data.records[].groupBatchId | String | 判定 orderKind 的那个值:运营团期 ID,雪花序列化为字符串;null = 该订单当前不属于任何团期(含非团订单、已退团)。orderKind=GROUP 等价于本字段非空 |
| data.records[].orderId | String | 订单 ID(雪花字符串) |
| data.records[].requirementId | String | 用车需求 ID(雪花字符串),卡片维度 |
| data.records[].assignmentId | String | 代表派车行 ID;虚拟待派条目为 null |
| data.records[].virtualPending | Boolean | true = 还没有任何派车行的待派卡片;false = 已有派车行 |
| data.records[].dailyAssignments | Array | 逐日派车行的身份与状态 |
| data.records[].assignmentStatus | String | 卡片状态码(unassigned / holding / assigned / completed / canceled / exception) |
| data.records[].consultantId | String | 当前负责定制师 ID(雪花字符串) |
| data.records[].customerName | String | 客户名 |
| data.records[].productName | String | 产品名 |
| data.records[].startDate | String | 出团日 YYYY-MM-DD |
| data.records[].endDate | String | 返程日 YYYY-MM-DD |
请求示例
GET /admin/fleet/board/orders?pageSize=100&startDayFrom=2026-01-01&startDayTo=2028-12-31&orderKind=GROUP HTTP/1.1
Host: 192.168.100.236:8080
Authorization: Bearer <VEHICLE_MANAGER 的 token>
(GET 无请求体)
响应示例
测试服 2026-09-28 15:13 实测原文(orderKind=GROUP;同窗口 ALL 62 条、NORMAL 61 条,GROUP 就是下面这 1 条):
{
"code": 200,
"message": "成功",
"data": {
"records": [
{
"id": "HL20260928143200064",
"orderNo": "HL20260928143200064",
"teamNo": "26-8345",
"groupBatchId": "2104459089860001794",
"orderId": "2104459089616732161",
"unreadMessageCount": 0,
"assignmentId": null,
"assignmentGroupId": null,
"requirementId": "2104461918343430146",
"dailySummary": {
"serviceDate": null,
"totalDailyItems": 0,
"serviceStartDate": "2026-11-12",
"serviceEndDate": "2026-11-14",
"serviceDays": 3,
"requiredVehicleType": "mpv",
"requiredVehicleTypeLabel": "商务车",
"requiredSeats": 7
},
"assignmentProgress": {
"totalDailyItems": 0,
"finalizedByFleet": false,
"staleFinalizedPlan": false,
"dispatchedDailyItems": 0,
"completedDailyItems": 0,
"canceledDailyItems": 0
},
"dailyAssignments": [],
"customerName": "刘明远",
"contactName": "刘明远",
"productName": "王骁测试团期产品",
"headcount": 3,
"adultCount": 3,
"childCount": 0,
"youngChildCount": 0,
"babyCount": 0,
"startDate": "2026-11-12",
"endDate": "2026-11-14",
"days": 3,
"pickupAt": null,
"dropoffAt": null,
"pickupSummary": {
"required": true,
"statusCode": "MISSING",
"statusLabel": "待补接客信息",
"batchCount": 0,
"readyBatchCount": 0,
"transportNos": [],
"earliestTime": null,
"latestTime": null,
"stations": []
},
"dropoffSummary": {
"required": true,
"statusCode": "MISSING",
"statusLabel": "待补送客信息",
"batchCount": 0,
"readyBatchCount": 0,
"transportNos": [],
"earliestTime": null,
"latestTime": null,
"stations": []
},
"routeSummary": "额尔古纳市 → 满洲里市",
"daysUntilDeparture": 45,
"readiness": {
"statusCode": "PARTIAL",
"statusLabel": "部分信息待补",
"itineraryStatusCode": "READY",
"itineraryStatusLabel": "行程已完整",
"itineraryDayCount": 3,
"itineraryExpectedDayCount": 3,
"missingItemCodes": ["PICKUP_TRANSFER", "DROPOFF_TRANSFER"],
"missingItemLabels": ["接客信息", "送客信息"],
"blocksAssignment": false
},
"isHailarPickup": false,
"isHailarDropoff": false,
"consultantId": "2103791514381664257",
"plannerName": "ha_r1_cz",
"consultantName": "ha_r1_cz",
"consultantDisplayName": "ha_r1_cz",
"customerNote": "一家三口,需儿童座椅",
"vehicleAdvice": null,
"specialTags": ["儿童安全座椅"],
"requirementRemark": "全程行程用车,含额尔古纳湿地往返",
"requiredVehicles": [
{ "vehicleType": "mpv", "categoryLabel": "商务车", "seats": 7, "count": 1 }
],
"assignmentStatus": "unassigned",
"assignmentStatusLabel": "待派车",
"baseAssignmentStatus": "unassigned",
"manualUrgent": false,
"lifecycleStageCode": "unassigned",
"lifecycleStageLabel": "待派车",
"currentStep": 2,
"availableActionCodes": ["ASSIGN", "REJECT_REQUIREMENT"],
"currentVehiclePlate": null,
"currentVehicleModel": null,
"currentVehicleSeats": null,
"currentVehicleFleet": null,
"currentVehicleFleetTeamId": null,
"currentVehicleFleetTeamName": null,
"currentVehicleFleetTeamType": null,
"currentVehicleFleetTeamSettleType": null,
"currentDriverName": null,
"currentDriverPhone": null,
"urgentBadge": null,
"canAssign": true,
"dispatchReadOnly": false,
"dispatchReadOnlyReason": null,
"canRejectRequirement": true,
"virtualPending": true,
"groupVehicleCovered": null,
"transferChangePending": null
}
],
"total": 1,
"page": 1,
"pageSize": 100
},
"traceId": null,
"success": true
}
空数据 / 降级响应
筛选命中 0 条时返回空数组 + total=0,不是 null、不报错。下面是 orderKind=NORMAL 在一个只有团期订单的时间窗内的实测原文:
{
"code": 200,
"message": "成功",
"data": {
"records": [],
"total": 0,
"page": 1,
"pageSize": 100
},
"traceId": null,
"success": true
}
NORMAL 与 groupBatchId 同传属于逻辑互斥,按 AND 组合走同一条空列表路径,不报错。
order-v3 整体不可达时看板不会空、也不会 500:归属判定回退到派车行的建行快照,此时 records[].groupBatchId 取自快照,取值边界见「六、边界行为」。
错误响应
orderKind 非法(大小写不符或取值域外),实测原文:
{
"code": 100001,
"message": "参数非法: orderKind 仅支持 ALL/NORMAL/GROUP,传入非法值:group",
"data": null,
"traceId": null,
"success": false
}
HTTP 状态行仍是 200,判据在 body 的 code / success,不要只看状态码。未登录或 token 失效同理走 body:{"code": 401, "message": "Token 无效", "data": null, "success": false}。
业务边界
- 鉴权:
/admin/fleet/**是路径级角色门禁(VEHICLE_MANAGER/SUPER_ADMIN),不是权限点模型,前端不按权限码做按钮显隐;非车务角色调用返 bodycode=403、message为「无权限访问车务管理,请切换到车务角色」。 orderKind大小写敏感:group、Group一律100001;首尾空白会被去掉," GROUP "合法。- 不传与显式传
ALL的响应逐字节相同(2026-09-28 14:32 实测比对为真)。 orderKind与其余所有筛选条件(含groupBatchId、statuses、日期、车型、consultantId、keyword)都是 AND 组合。- 归属判定的值就是
records[].groupBatchId:GROUP⇔ 该字段非空,NORMAL⇔ 该字段为空。实测NORMAL与GROUP两档把全集二分(61 + 1 = 62,交集为空),前端可以拿返回的字段自行复核。 - 还没有派车行的虚拟待派卡片(
virtualPending=true)同样参与orderKind过滤,它的归属只看订单上下文的实时值。 - 分页在过滤之后:
total是过滤后的条数,切换orderKind后请把页码重置回 1。
2. 派单看板汇总 GET /admin/fleet/board/summary
VO: BoardOrderPageReqVO → BoardSummaryVO
使用场景
派单看板顶部的数字卡 + 状态页签计数 + 定制师下拉数据源。与列表接口共用同一套筛选参数(后端是同一个入参 VO),用于给出「同一筛选范围内」的全状态分面。切订单归属页签时,这个接口要和列表接口用同一份 orderKind 一起重新拉,否则会出现「列表 1 条、状态页签写着 15 条」这种对不上的界面。
入参字段表
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|---|---|---|---|---|---|
| orderKind | Query | String | ❌ | ALL / NORMAL / GROUP,大小写敏感;其余值返 100001 |
本次新增。与列表接口同一取值与缺省口径(不传或空串 = ALL) |
| groupBatchId | Query | Long | ❌ | 雪花 ID,字符串透传 | 运营团期精确筛。本接口同样接受并应用该参数,与 orderKind AND 组合 |
| startDayFrom | Query | String | ❌ | YYYY-MM-DD |
日期区间起,行程区间重叠口径 |
| startDayTo | Query | String | ❌ | YYYY-MM-DD |
日期区间止 |
| startDate | Query | String | ❌ | YYYY-MM-DD |
startDayFrom 别名 |
| endDate | Query | String | ❌ | YYYY-MM-DD |
startDayTo 别名 |
| vehicleTypeKeys | Query | String[] | ❌ | suv / mpv / bus / sedan |
车型大类多选 |
| typeKeys | Query | String[] | ❌ | 同上 | 别名 |
| driverName | Query | String | ❌ | - | 司机姓名模糊搜索 |
| keyword | Query | String | ❌ | - | 统一文字搜索,口径同列表 |
| contactName | Query | String | ❌ | - | 联系人、客户名模糊搜索 |
| contactKeyword | Query | String | ❌ | - | 别名 |
| teamNo | Query | String | ❌ | - | 团号模糊搜索 |
| consultantId | Query | Long | ❌ | - | 定制师精确筛 |
| plannerName | Query | String | ❌ | - | 定制师姓名模糊搜索(兼容参数) |
| consultantName | Query | String | ❌ | - | 别名 |
| status / statuses | Query | String / String[] | ❌ | - | 汇总中被忽略:本接口本来就是返回同一范围内的全部状态分面 |
| page / pageSize / pageNo | Query | Integer | ❌ | - | 汇总中被忽略 |
出参字段表
| 字段 | 类型 | 说明 |
|---|---|---|
| data.pendingCount | Integer | 待派车数。随 orderKind 收窄 |
| data.pendingUrgentCount | Integer | 待派车中临近出团的数量。随 orderKind 收窄 |
| data.todayDepartCount | Integer | 今日出发数。随 orderKind 收窄——与列表同一份过滤结果统计出来,不是全局今日出发 |
| data.idleVehicleCount | Integer | 空闲车辆数。全局物理资源指标,不随任何订单筛选变化(含 orderKind) |
| data.idleDriverCount | Integer | 空闲司机数。同样全局,不随筛选变化 |
| data.holdingTimeoutCount | Integer | 排车锁定超时数。随筛选收窄 |
| data.statusCounts | Object | 全状态计数块,键为 unassigned / holding / assigned / completed / canceled / exception / unassignedUrgent / holdingUrgent,值为 Integer。键集合固定不随 orderKind 变,只有计数收窄 |
| data.statusOptions | Array | 状态页签下发项 |
| data.statusOptions[].value | String | 状态码 |
| data.statusOptions[].label | String | 状态中文名 |
| data.statusOptions[].description | String | 状态释义 |
| data.statusOptions[].count | Integer | 该状态条数。随 orderKind 收窄 |
| data.statusOptions[].urgentCount | Integer | 该状态中的紧急条数。随 orderKind 收窄 |
| data.consultantOptions | Array | 定制师下拉项,列表接口的 consultantId 取值就从这里来 |
| data.consultantOptions[].value | String | 定制师管理员 ID,雪花序列化为字符串 |
| data.consultantOptions[].label | String | 定制师显示名 |
请求示例
GET /admin/fleet/board/summary?pageSize=100&startDayFrom=2026-01-01&startDayTo=2028-12-31&orderKind=GROUP HTTP/1.1
Host: 192.168.100.236:8080
Authorization: Bearer <VEHICLE_MANAGER 的 token>
(GET 无请求体)
响应示例
测试服 2026-09-28 15:13 实测原文(orderKind=GROUP):
{
"code": 200,
"message": "成功",
"data": {
"pendingCount": 1,
"pendingUrgentCount": 0,
"todayDepartCount": 0,
"idleVehicleCount": 48,
"idleDriverCount": 51,
"holdingTimeoutCount": 0,
"statusCounts": {
"unassigned": 1,
"holding": 0,
"assigned": 0,
"completed": 0,
"canceled": 0,
"exception": 0,
"unassignedUrgent": 0,
"holdingUrgent": 0
},
"statusOptions": [
{
"value": "unassigned",
"label": "待派车",
"description": "已提出有效用车需求,车务尚未派车",
"count": 1,
"urgentCount": 0
},
{
"value": "holding",
"label": "排车中",
"description": "车务已排车,待车务确认执行",
"count": 0,
"urgentCount": 0
},
{
"value": "assigned",
"label": "已派车",
"description": "车务已派定,行程单已生成",
"count": 0,
"urgentCount": 0
},
{
"value": "completed",
"label": "已完结",
"description": "用车行程已完结",
"count": 0,
"urgentCount": 0
},
{
"value": "canceled",
"label": "已取消",
"description": "派车需求已取消(仅未派车即取消,#6152)",
"count": 0,
"urgentCount": 0
},
{
"value": "exception",
"label": "异常",
"description": "已派车后再取消,待人工处置(释放资源后处置完成)",
"count": 0,
"urgentCount": 0
}
],
"consultantOptions": [
{ "value": "2103791514381664257", "label": "ha_r1_cz" }
]
},
"traceId": null,
"success": true
}
同一时刻同一窗口的三档对照读数(证明分面随 orderKind 收窄,而空闲车辆/司机不随):
orderKind |
pendingCount |
statusCounts 桶和 |
consultantOptions 条数 |
idleVehicleCount |
idleDriverCount |
|---|---|---|---|---|---|
ALL |
15 | 62 | 9 | 48 | 51 |
NORMAL |
14 | 61 | 8 | 48 | 51 |
GROUP |
1 | 1 | 1 | 48 | 51 |
空数据 / 降级响应
无数据时各计数返 0 而不是 null,数组返 []。下面是 orderKind=NORMAL 在一个只有团期订单的窗口内的实测片段:
{
"code": 200,
"message": "成功",
"data": {
"pendingCount": 0,
"pendingUrgentCount": 0,
"todayDepartCount": 0,
"idleVehicleCount": 48,
"idleDriverCount": 51,
"holdingTimeoutCount": 0,
"statusCounts": {
"unassigned": 0,
"holding": 0,
"assigned": 0,
"completed": 0,
"canceled": 0,
"exception": 0,
"unassignedUrgent": 0,
"holdingUrgent": 0
},
"consultantOptions": []
},
"traceId": null,
"success": true
}
注意 consultantOptions 为 [] 时定制师下拉没有候选项——这是「当前筛选范围内没有任何订单」的正常结果,不是接口故障。order-v3 不可达时汇总同样返回结构完整的 200,归属判定退化为建行快照口径。
错误响应
与列表接口同一套校验,实测原文:
{
"code": 100001,
"message": "参数非法: orderKind 仅支持 ALL/NORMAL/GROUP,传入非法值:XX",
"data": null,
"traceId": null,
"success": false
}
业务边界
- 鉴权同列表:
VEHICLE_MANAGER/SUPER_ADMIN路径级角色门禁。 - 本接口与列表接口必须传同一份筛选参数,包括
orderKind。只改一边会让数字卡与列表对不上。 status/statuses与分页参数在本接口中被忽略,传了不报错也不生效。consultantOptions是筛选范围内的定制师,不是全量管理员名单:切orderKind后下拉候选会变(实测 9 / 8 / 1),已选中的consultantId可能不在新候选里,切页签时建议连带清空定制师选择。idleVehicleCount/idleDriverCount恒为全局值,切页签这两个数字不会变,不要把它们当作「当前页签下的空闲资源」展示。- 不传
orderKind与传ALL的响应逐字节相同(2026-09-28 14:32 实测比对为真)。
四、契约约束与正确调用方式
本节只写后端接受/拒绝请求的规则,以及前端相应要改的代码位置;不写 UI 视觉建议。
✅ 正确 / ❌ 错误 query 对照
| 场景 | query |
|---|---|
| ✅ 全部(推荐显式写) | ?orderKind=ALL |
| ✅ 全部(省略等价) | ?(不带 orderKind) |
| ✅ 空串等价于全部 | ?orderKind= |
| ✅ 只看常规订单 | ?orderKind=NORMAL |
| ✅ 只看团期订单 | ?orderKind=GROUP |
| ✅ 某一个团 | ?orderKind=GROUP&groupBatchId=2104459089860001794 |
| ⚠️ 逻辑互斥但合法 | ?orderKind=NORMAL&groupBatchId=2104459089860001794 → 200 空列表,不报错 |
| ❌ 小写 | ?orderKind=group → 200 + code=100001 |
| ❌ 取值域外 | ?orderKind=XX → 200 + code=100001 |
🔴 页签改造:orderKind 必须进「两个接口共用」的那份参数
派单看板加「常规订单 / 团期订单」两个页签,分别对应 orderKind=NORMAL / orderKind=GROUP;保留「全部」时不传或传 ALL。落到 src/views/fleet/board/index.vue(origin/v2.1):
fetchBoard()(:522-525)是并发两发:getBoardSummary(buildBoardSharedParams())与getBoardOrders(buildBoardParams())。- ⇒
orderKind要加进buildBoardSharedParams()(:491-503),不要加进buildBoardParams()(:505-515)。只加后者的话列表会过滤、而数字卡与状态页签计数仍是全量口径,界面上表现为「列表 1 条、页签写着 15 条」,且不报任何错。 :511那行注释「009 式 summary 契约未带该参,不传」与后端事实不符:两个接口共用同一个入参 VO,summary同样接受并应用groupBatchId。改造时建议把groupBatchId一并挪进 shared params,让数字卡与列表在按团筛时也同口径;注释同步订正。- 两个页签共用同一套状态枚举:
statusCounts的键集合与statusOptions的value集合不随orderKind变化,只有count/urgentCount收窄。状态页签组件不需要按订单归属拆成两套。 - 切页签时把页码重置回 1,并考虑清空
consultantId(下拉候选会随orderKind变)。
🔴 跨服务同名参数:与订单列表 orderKind 的语义差
orderKind 这个名字是刻意与 order-v3 订单列表 GET /v3/admin/order 对齐的(两端取值域都是 ALL / NORMAL / GROUP),但缺省档与组合规则不同:
| 维度 | 派单看板(本次两个接口) | 订单列表 GET /v3/admin/order |
|---|---|---|
| 取值域 | ALL / NORMAL / GROUP |
ALL / GROUP / NORMAL(同一套) |
| 不传或空串 | ALL:团期订单照常出现 |
NORMAL:只看散客;例外——传了 productType 而 orderKind 未传或为空串时,不缺省 NORMAL |
与 groupBatchId 同传 |
AND 组合:NORMAL + groupBatchId 返空列表,不报错 |
groupBatchId 传入时 orderKind 被强制视为 GROUP |
| 非法取值 | 业务层校验,200 + code=100001,message 含传入的原值 |
入参正则校验,提示「orderKind 非法,可选值: ALL, GROUP, NORMAL」 |
这意味着:把订单列表页的 orderKind 状态直接透传给看板(或反过来),「不传」这一档的含义会翻面——在订单列表是「只看散客」,在看板是「全部」。两个页面各自维护自己的筛选状态,或者在跨页跳转时显式写死要传的值,不要依赖缺省。
唯一静默的失效形态就在这里:把 NORMAL 当成「默认值」传给看板,团期订单会从列表和汇总里一起消失,没有任何报错,表现只是少了几行。传错取值反而是响的(100001)。
另外,看板页上还有一个名字相近但维度完全不同的参数:到期提醒读口 GET /admin/fleet/board/expiry 的 kinds,取值是 inspect / vehInsure / license / driverInsure(车辆年检、车险、驾照、司机保险),与订单归属无关,两者不要互相传值。
🔴 团期配车模块处置:菜单行已删除,模块失去唯一入口
后端已把「团期配车」菜单行从 /admin/auth 下发的菜单树里删除(PR #8474,hl-user-service 已滚测试服,2026-09-28 15:05:03,Flyway 20260928.8464 success=1)。测试服实测:VEHICLE_MANAGER 账号拉到的菜单树里 group-dispatch 零命中,同一份里 /fleet 及其余 15 个 /fleet/* 路径(含 /fleet/board)仍在。
这不是「一处跳转失效」,是整个模块不可达:
- hl-ui 的业务路由 100% 由菜单树动态注册(
src/router/index.js的addDynamicRoutes:先removeDynamicRoutes(),再按后端菜单generateRoutes(menus)挂到Layout下)。 - 静态路由表里没有
/fleet/group-dispatch兜底:git grep -c -- 'group-dispatch' origin/v2.1 -- src/router零命中;同口径阳性对照git grep -c -- 'routes' origin/v2.1 -- src/router命中 7 个文件(index.js、routes.js、routes.spec.js、navigationScope.spec.js等),证明查的位置与命令都有效。 - 菜单行不下发 ⇒ 该路由不注册 ⇒ 直链
/fleet/group-dispatch落 NotFound。 - 涉及的是
src/views/fleet/group-dispatch/下 12 个文件:index.vue、labels.js、6 个组件(GroupDispatchOverviewDrawer.vue、GroupDispatchPlanEditor.vue、ShareDispositionAlert.vue、ShareGroupEditModal.vue、ShareGroupHistory.vue、ShareGroupPanel.vue)、4 个组件测试;加上src/api/fleet/group-dispatch.js(:11const BASE = '/fleet/group-dispatch')。
要做的处置(二选一,由 mmg 定):把这批页面按「不再需要」清理掉,或给它们另挂一个可达入口。两种做法都有一条必须遵守的例外:
🔴
src/api/fleet/group-dispatch.js的getGroupDispatchPendingBatches不能一起删。src/views/fleet/board/index.vue:261正在 import 它,用于 #7443 的「按运营团期筛选」下拉候选(:318-345),与本次的归属页签是两件事。连它一起删会让看板的团期下拉直接报错。
后端端点没有下线,仍然可以调用:/admin/fleet/group-dispatch/**(pending-batches / overview / resource-schedule / reconfigure / confirm / readiness / share-groups / share-member-candidates)全部照旧,鉴权仍是 VEHICLE_MANAGER / SUPER_ADMIN 路径级角色门禁。测试服 2026-09-28 15:11 实测:具备车务角色的账号调 GET /admin/fleet/group-dispatch/pending-batches 返 200;非车务角色返 body code=403;伪造 token 返 body code=401。删的是菜单入口,不是接口能力。
看板「团期订单」页签目前覆盖到哪一步,如实说清(决定上面二选一时需要这条):
orderKind=GROUP做的是列表与汇总层面的归属过滤,返回的仍是逐条用车需求的卡片(维度 = 当前有效用车需求,同一requirementId一条),不是团期配车那种「一团一行」的整团视图。- 看板上的派车动作是逐需求派车;团期配车的整团逐日计划差量重配(
reconfigure)与整团定稿(confirm)在看板上没有对应入口。
五、数据库行为
本次变更的两个接口都是只读 GET,零数据库写入,不产生任何落库副作用。orderKind 只影响查询结果的过滤范围,不改写任何行。
菜单侧(PR #8474)的库变更与接口契约无关,只删 sys_menu 中 /fleet/group-dispatch 那一行菜单及其 sys_role_menu 授权;权限码 fleet:group-dispatch:view 与它的授权原样保留(/admin/fleet/** 走路径级角色门禁,不看权限码)。
六、边界行为
- 未登录或 token 失效 → body
code=401、message为「Token 无效」,HTTP 状态行仍是 200,判据在 body。 - 非车务角色 → body
code=403、message为「无权限访问车务管理,请切换到车务角色」。 orderKind非法 → bodycode=100001,message为参数非法: orderKind 仅支持 ALL/NORMAL/GROUP,传入非法值:<原值>。- 命中 0 条 →
records: []+total: 0;汇总各计数0、consultantOptions: [],不返null、不 500。 - order-v3 降级(订单上下文整体不可达)时的归属口径:判定从「订单当前归属」退化为「派车行的建行快照」,有三个可观察差异,前端把它们当成降级期的已知边界即可,不必额外处理:
- 已退团/换团的订单,其历史派车行在
orderKind=GROUP下仍会出现(宁可多给一条历史行,也不让整块团期看板变空); - 建行快照为空的行(该列之前的存量派车行、车务手工建的行)在
GROUP下落选、在NORMAL下命中; - 过滤按逐条日行的快照判,而卡片回显的
groupBatchId取代表日行的快照,所以同一需求下若各日行的建行快照不一致,可能出现「卡片被GROUP筛出来、但卡片上的groupBatchId为空」这类回显与筛选看起来对不上的行。非降级路径不会出现——上下文可用时两处取的是同一个订单实时归属值。
- 已退团/换团的订单,其历史派车行在
- 老数据兼容:
records[].groupBatchId本来就允许为空,前端原有的空值处理逻辑不用改。 - 未传
orderKind的旧前端调用行为完全不变(缺省ALL)。
六.5、枚举 / 数据字典
orderKind(com.hulalv.fleet.board.support.BoardOrderKind)
所属字段: orderKind(两个接口共用的查询参数) | 类型: String
后端是常量类(不是 Java 枚举),取值用精确相等比较 ⇒ 大小写敏感。
| 值 | 中文 | 说明 |
|---|---|---|
ALL |
全部 | 不按订单归属过滤。不传、传空串、传纯空白都等价于本值 |
NORMAL |
常规订单 | 当前不属于任何运营团期的订单(records[].groupBatchId 为空),含非团订单与已退团 |
GROUP |
团期订单 | 当前属于某个运营团期的订单(records[].groupBatchId 非空) |
首尾空白会被去掉后再判定;其余任何取值返 100001。
六.6、修改前后对比
字段级对比
| 字段 | 改前 | 改后 |
|---|---|---|
orderKind(两个接口的 query) |
不存在,传了被忽略 | 可选参数,ALL / NORMAL / GROUP,缺省 ALL,非法值返 100001 |
| 响应字段 | — | 无任何增删改,BoardOrderPageRespVO 与 BoardSummaryVO 的字段集合与类型一字未动 |
行为级对比
| 行为 | 改前 | 改后 |
|---|---|---|
| 看板列表的订单范围 | 常规订单与团期订单混在一起,只能用 groupBatchId 精确筛某一个团 |
可按归属粗筛两档,也可与 groupBatchId AND 组合 |
| 汇总各分面的口径 | 与列表同筛选口径(不含归属维度) | 同左,并把 orderKind 一起纳入;idleVehicleCount / idleDriverCount 仍为全局 |
传了无法识别的 orderKind |
参数不存在,被忽略,返回全量 | 返 code=100001,不静默放行 |
| 「团期配车」菜单入口 | 车务管理下的独立菜单页 /fleet/group-dispatch |
菜单行已删除,该路由不再注册;接口与鉴权、权限码原样保留 |
六.7、影响评估
- 是否破坏向后兼容: 否。不传
orderKind的旧调用行为与改前完全一致(实测不传与ALL响应逐字节相同),响应结构零变化。 - 前端是否必须同步上线: 是。菜单行已删除,
/fleet/group-dispatch路由不再注册,src/views/fleet/group-dispatch/需要按「四」的二选一处置;看板的orderKind页签本身是增量能力,不做也不破坏原有功能。 - 前端 workaround 清理点: 若页面上原先用「先拉全量再在前端按
groupBatchId是否为空分组」的方式模拟常规/团期分类,可以改为直接传orderKind让后端过滤——前端分组只能对当前这一页生效,而total与汇总计数是全量口径,两者会对不上。另:index.vue:511那条关于 summary 不接受groupBatchId的注释可以订正掉。
七、不影响范围
- 仅影响: 管理后台车务「派单看板」的列表与汇总两个读口,以及「团期配车」菜单入口。
- 零影响:
/admin/fleet/group-dispatch/**团期配车的全部接口(契约、鉴权、返回结构一字未动);- 车务看板的其他读口:到期提醒
board/expiry、时间线、矩阵、接送机相关端点; - 派车、改派、取消等所有写口;
- 订单列表
GET /v3/admin/order与它自己的orderKind(本次未动 order-v3 任何代码); - 小程序端全部接口;
- 权限点
fleet:group-dispatch:view与它的角色授权(本次只删菜单行,权限定义原样保留); - 历史数据:不做任何迁移,归属判定是查询期计算,不改写存量行。
八、测试环境已验证
测试服网关 http://192.168.100.236:8080,账号 ha_r1_vm(VEHICLE_MANAGER,测试专用账号)。部署读数:hl-fleet-service dev-v3 @ ad4e65a27(2026-09-28 14:22:43,ok),hl-user-service dev-v3 @ f49ae8d40(2026-09-28 15:05:03,ok),Flyway 20260928.8464 success=1。
第一轮 2026-09-28 14:32(窗口 pageSize=100&startDayFrom=2026-01-01&startDayTo=2028-12-31,库内当时只有常规订单)
GET /admin/fleet/board/orders (不带 orderKind) → 200 + total 61 ✓
GET /admin/fleet/board/orders?orderKind=ALL → 200 + total 61,与上一条响应逐字节相同 ✓
GET /admin/fleet/board/orders?orderKind=NORMAL → 200 + total 61 ✓
GET /admin/fleet/board/orders?orderKind=GROUP → 200 + total 0(空数组,不报错)✓
GET /admin/fleet/board/summary (不带 orderKind) → 200,与 orderKind=ALL 响应逐字节相同 ✓
GET /admin/fleet/board/orders?orderKind=group → 200 + code 100001「参数非法: orderKind 仅支持 ALL/NORMAL/GROUP,传入非法值:group」✓
GET /admin/fleet/board/orders?orderKind=XX → 200 + code 100001(同上,原值 XX)✓
GET /admin/fleet/board/summary?orderKind=group → 200 + code 100001 ✓
GET /admin/fleet/board/summary?orderKind=XX → 200 + code 100001 ✓
GET /admin/fleet/board/orders (伪造 token,阴性对照) → 200 + code 401「Token 无效」✓
第二轮 2026-09-28 15:13(同一窗口,库内已有 1 条团期订单派车需求)
GET /admin/fleet/board/orders?orderKind=ALL → 200 + total 62 ✓
GET /admin/fleet/board/orders?orderKind=NORMAL → 200 + total 61,61 条的 groupBatchId 全为 null ✓
GET /admin/fleet/board/orders?orderKind=GROUP → 200 + total 1,该条 groupBatchId = "2104459089860001794" ✓
62 = 61 + 1,两档把全集二分、交集为空 ✓
GET /admin/fleet/board/summary?orderKind=ALL → pendingCount 15 / statusCounts 桶和 62 / consultantOptions 9 项 ✓
GET /admin/fleet/board/summary?orderKind=NORMAL → pendingCount 14 / statusCounts 桶和 61 / consultantOptions 8 项 ✓
GET /admin/fleet/board/summary?orderKind=GROUP → pendingCount 1 / statusCounts 桶和 1 / consultantOptions 1 项 ✓
三档 idleVehicleCount 恒 48、idleDriverCount 恒 51 ✓
GET /admin/fleet/board/orders?orderKind=GROUP (阳性对照,与非法值同一轮)→ 200 + code 200 ✓
第三轮 2026-09-28 15:10~15:11(菜单删除后的入口与接口可达性)
GET /admin/auth 菜单树(VEHICLE_MANAGER) → group-dispatch 零命中;
同一份里 /fleet 与其余 15 个 /fleet/* 路径仍在(阳性对照)✓
GET /admin/fleet/group-dispatch/pending-batches (VEHICLE_MANAGER)→ 200 ✓
GET /admin/fleet/group-dispatch/pending-batches (非车务角色) → 200 + code 403「无权限访问车务管理,请切换到车务角色」✓
GET /admin/fleet/group-dispatch/pending-batches (伪造 token) → 200 + code 401「Token 无效」✓
验证用团期订单:orderNo=HL20260928143200064、teamNo=26-8345、groupBatchId=2104459089860001794、出团日 2026-11-12。
九、相关历史 PR
| PR | Issue | 说明 | 是否仍有效 |
|---|---|---|---|
| — | #7443 | 看板新增 groupBatchId 运营团期精确筛(活体优先、降级回退建行快照) |
✅ 有效,本次的粗筛与它同源同口径 |
| #8473 | #8464 | 看板两个读口新增 orderKind 订单归属粗筛 |
✅ 最新 |
| #8474 | #8464 | 删除「团期配车」独立菜单行(权限点保留) | ✅ 最新 |
十、相关文档
- 关联 Issue: wx/HL#8464
- 关联 PR: wx/HL#8473、wx/HL#8474
关联 / 联系人
链接
联系人
- 后端负责人: @wx