文件
hl-api-changelog/changelogs-v2/2026-09/28_8464_派单看板加订单归属页签与团期配车菜单下线-修改接口-管理后台.md
T
2026-09-28 20:00:35 +08:00

41 KiB
原始文件 Blame 文件历史

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 + body code=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),不是权限点模型,前端不按权限码做按钮显隐;非车务角色调用返 body code=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)仍在。

这不是「一处跳转失效」,是整个模块不可达:

  1. hl-ui 的业务路由 100% 由菜单树动态注册(src/router/index.js 的 addDynamicRoutes:先 removeDynamicRoutes(),再按后端菜单 generateRoutes(menus) 挂到 Layout 下)。
  2. 静态路由表里没有 /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 等),证明查的位置与命令都有效。
  3. 菜单行不下发 ⇒ 该路由不注册 ⇒ 直链 /fleet/group-dispatch 落 NotFound。
  4. 涉及的是 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(:11 const 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 非法 → body code=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 删除「团期配车」独立菜单行(权限点保留) ✅ 最新

十、相关文档

关联 / 联系人

链接

联系人

  • 后端负责人: @wx