文件
hl-api-changelog/changelogs-v2/2026-09/30_8560_矩阵未派订单卡下发用车需求类别可区分行程用车与接送机-修改接口-管理后台.md
T
API Changelog Bot和Claude Opus 5 877d69e651
changelog-filename-gate / validate (push) Failing after 2s
docs(changelog): 8560 订正 requirementId 两卡是否同值按路径分档
原文按真实行路径过度概括成「两张卡 requirementId 恒相同」。实测代码:MatrixService.java:497-498 真实行优先取订单侧单值(两卡相同),:637-638 虚拟待派条目优先取候选自身需求 ID(两卡不同),而未派订单最常见的形态正是后者。四处(正文口径、出参表、业务边界、测试说明)一并按路径分档,判类别只认 requirementKind 的结论不变、且更必要。

Refs #8560

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-30 14:24:30 +08:00

16 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 8560 矩阵未派订单清单响应新增用车需求类别字段,同订单双需求两张卡可区分 admin wx(GIT) 修改接口 deployed verified pending PR #8589 合并 dev-v3(8b045a321b);测试网关部署确认:hl-fleet-service 现部署 @ 99fb369ba(deploy-status.sh 实测,状态 ok,该 SHA 经 git merge-base --is-ancestor 确认已包含 8b045a321b)。GET /admin/fleet/matrix/unassigned-orders 已用车务角色测试账号实测:2026-09 月拿到 TRAVEL 示例(订单 HL20260911193207642)、2026-11 月拿到 TRANSFER 示例(订单 HL20260929154809598),均为测试服真实响应;对 2026-06~2027-03 共 10 个月窗口扫描未发现 requirementKind=null 或同订单双卡的活跃实例,这两种边界行为当前仅由单元测试覆盖(BoardRequirementIdentitiesKindTest 5/5、MatrixServiceTest 新增 6 个 #8560 方法),尚未在测试服活数据上复现。 2026-09-30 dev-v3

hl-fleet-service:矩阵未派订单清单响应新增用车需求类别字段,同订单双需求两张卡可区分

存放目录: changelogs-v2/2026-09/ 服务: hl-fleet-service PR: #8589 Issue: #8560 日期: 2026-09-30 影响范围: 1 个只读端点响应新增 2 字段(矩阵未派订单清单)


⚠️ 关键变化

  • GET /admin/fleet/matrix/unassigned-orders 响应每条记录新增 requirementKind(TRAVEL/TRANSFER/null)与 requirementKindLabel(行程用车/接送机/null),两者恒成对(一个为 null 另一个必为 null)。
  • 背景(#7439):同一订单可并存两条活跃用车需求(行程用车 TRAVEL + 接送机 TRANSFER),车务分别对两者派车,未派池会出现同一订单的两张卡。此前两张卡除车型/日期外没有任何字段能分辨谁是哪一类——既有字段 vehicleCategory/categoryLabel 是车型(suv/bus)不是需求类别。新增这两个字段就是用来分辨这两张卡的。
  • 🔴 null 不兜底成 TRAVEL,这是本次修复的核心边界。判不出类别(跨服务降级 context=null;或派车行挂着 #5720 换版过渡窗里的上一版 requirement_id,命中不了任何当前活跃身份;或命中的身份自身 kind 为空白)时两个新字段均为 null,前端应不显示类别标签,禁止自行按业务猜测补默认值——尤其禁止把 null 当 TRAVEL 处理。
  • 与看板列表(BoardOrderRecordVO.requirementKind,#8518 既有)在判不出这一档口径不同:看板列表的解析方法判不出时兜底返 TRAVEL(那里类别同时是筛选维度,返空会让卡片从筛选后的视图里彻底消失);矩阵未派卡判不出时返 null(那里类别只是展示标签,车务会照标签去排完全不同的活,标错比不标更危险)。同一张实体卡在两个入口可能显示不一致的类别信息,这是刻意保留的差异,不是缺陷。
  • 真实未派行与虚拟待派条目(virtualPending=true,#7067)两类条目都携带这两个新字段,取值口径一致。
  • 类别取的是这张卡自身所属需求(真实行用该行自己的 requirement_id,虚拟条目用该候选自己的 requirementId)解析出的类别,不是已有字段 requirementId(该字段取「订单侧单值」,#5667 口径,同一订单两类需求并存时恒指向身份列表首项、即恒为 TRAVEL 那条)。⚠️ requirementId 字段两张卡是否相同取决于这张卡走哪条路径:virtualPending=true(未派池的虚拟待派条目,未派订单最常见的形态)下它取该候选自身的需求 ID,两张卡不相同;virtualPending=false(已有派车行的真实行)下它优先取订单侧单值,两张卡相同。两条路径都不能拿 requirementId 判类别——相同时它分辨不出,不同时它也只是碰巧对得上。判类别一律只认 requirementKind/requirementKindLabel。

二、变更接口清单

# 接口 方法 路径 变更类型 说明
1 矩阵未派订单清单 GET /admin/fleet/matrix/unassigned-orders 字段新增(非破坏性) 响应新增 requirementKind/requirementKindLabel

三、接口详情

1. 矩阵未派订单清单 GET /admin/fleet/matrix/unassigned-orders

VO: MatrixUnassignedReqVO → List<MatrixUnassignedOrderVO>

使用场景

派单矩阵页右面板「未派订单窗口」,车务从此列表拖拽卡片到左面板某车某日完成派车;同一订单若同时有行程用车与接送机两条活跃需求,会在此列表出现两张卡,车务需要靠新增的类别字段区分要往哪类需求上派车,不能再靠车型/日期/备注这类间接信息猜。

入参字段表

字段 位置 类型 必填 约束 说明
year query Integer 是 2020-2100,越界返 605076 年份
month query Integer 是 1-12,越界返 605010 月份
typeKeys query String[] 否 取值 suv/mpv/bus/sedan,规范小写 车型大类多选,空=全部;对虚拟待派条目按当前需求车型明细任一项归一后命中过滤

出参字段表(仅列本次新增字段及理解其语义所需的上下文字段,VO 全量共 39 个字段)

字段 类型 说明
requirementKind String 新增。用车需求类别:TRAVEL=行程用车 / TRANSFER=接送机 / null=判不出(不兜底为 TRAVEL)
requirementKindLabel String 新增。类别中文名:行程用车/接送机/null,与 requirementKind 恒成对
requirementId Long(字符串序列化) 既有字段,这张卡对应的用车需求 ID。virtualPending=false 时优先取订单侧单值(#5667,两类并存时恒指向 TRAVEL 那条);virtualPending=true 时取该候选自身的需求 ID。两条路径取值口径不同,一律不能用它推导 requirementKind
assignmentId Long(字符串序列化) 既有字段,本行唯一主键;虚拟待派条目为 null
virtualPending Boolean 既有字段,true=虚拟待派条目(零派车行订单,按需求上下文补出)
vehicleCategory String 既有字段,规范小写车型 key(suv/mpv/bus/sedan),与需求类别是两个不同维度
categoryLabel String 既有字段,车型中文标签(恒非 null)
orderId / orderNo String 既有字段,订单号
teamNo String 既有字段,团号

请求示例

GET /admin/fleet/matrix/unassigned-orders?year=2026&month=11&typeKeys=mpv

响应示例

实测取自测试服真实数据(2026-11 月,TRANSFER 示例):

{
  "code": 200,
  "message": "success",
  "data": [
    {
      "orderId": "HL20260929154809598",
      "orderNumericId": "2104840641597030402",
      "orderNo": "HL20260929154809598",
      "teamNo": "26-3682",
      "virtualPending": true,
      "assignmentId": null,
      "assignmentGroupId": null,
      "requirementId": "2104844928733548545",
      "requirementKind": "TRANSFER",
      "requirementKindLabel": "接送机",
      "vehicleCategory": "mpv",
      "categoryLabel": "商务车",
      "customerName": "董海涛",
      "headcount": 2,
      "headcountLabel": "2大",
      "startDate": "2026-11-11",
      "endDate": "2026-11-17",
      "pickupAt": "阿尔山伊尔施机场",
      "dropoffAt": "阿尔山伊尔施机场",
      "assignmentStatus": "unassigned",
      "urgentBadge": null,
      "vehicleAdvice": null,
      "parallelAssignments": []
    }
  ]
}

对照:2026-09 月同一账号实测取到的 TRAVEL 示例(订单 HL20260911193207642),响应结构完全相同,仅 requirementKind="TRAVEL"、requirementKindLabel="行程用车"、requirementId="2098950695167148034"。两个示例均为测试服真实取数,未做任何字段改写。

空数据 / 降级响应

  • 当月无未派条目:data: [],非错误。
  • vehicleAdvice 恒为 null(M2 数据源未建,既有降级行为,与本次改动无关)。
  • Nacos fleet.board.virtual-candidates-enabled=false 或 order-v3 候选服务不可用时:只丢虚拟待派条目,真实未派行原样返回(fail-open);真实行的 requirementKind 解析走独立的上下文查询,不受此开关影响。
  • requirementKind/requirementKindLabel 判不出时为 null(见「⚠️ 关键变化」),这不是接口异常,是正常的降级取值,前端应按无标签渲染,不得折算为 TRAVEL。

错误响应

{
  "code": 605010,
  "message": "月份超出范围",
  "data": null
}
{
  "code": 605076,
  "message": "年份超出范围(仅支持 2020-2100 年)",
  "data": null
}
  • 100001 参数非法:year/month 缺失(框架校验)。
  • 401 未登录。

业务边界

  • 同一订单两类需求并存时,两张卡的 requirementKind/requirementKindLabel 必不相同;而 requirementId 字段是否相同取决于路径——真实行(virtualPending=false)下两张卡相同(均取订单侧单值),虚拟待派条目(virtualPending=true)下两张卡各取自身需求 ID、并不相同。单元测试 MatrixServiceTest#queryUnassignedOrders_orderWithBothKinds_twoCardsCarryDifferentKinds 断言的是真实行那条路径。两条路径都不能拿 requirementId 反推类别,前端也不能这么做。
  • requirementKind=null 时前端禁止折算成 TRAVEL;这既是判不出的真实状态,也是修复前的错误行为,回退等于复发。
  • 矩阵未派卡与看板列表对同一张孤儿行(#5720 换版过渡窗)的类别展示口径不同(前者 null、后者兜底 TRAVEL),这是刻意保留的差异,不要据此判断某一端有 bug。
  • 真实未派行与虚拟待派条目两种类型都下发这两个字段,前端不需要按 virtualPending 分支处理类别逻辑。

四、契约约束与正确调用方式

  • 判类别只认 requirementKind/requirementKindLabel 这两个新字段,不要用 requirementId 做二次推导。
  • requirementKind 取值集合当前为 {TRAVEL, TRANSFER, null},前端不应写死「非 TRANSFER 即 TRAVEL」的二值判断——若未来 order-v3 新增第三类需求,后端会同步扩展该字段取值与中文映射,二值判断会把新类别误标成 TRAVEL。
  • 类别中文名由后端下发,前端不需要、也不应该自行维护 TRAVEL/TRANSFER 到中文的映射表。

五、数据库行为

无数据库结构变更。本次改动只是查询层新增两次内存解析(基于已查出的订单需求上下文按 requirement_id 匹配),不新增表、不新增列、不新增索引,无 Flyway 迁移。


六、边界行为

  • 上下文降级(跨服务 Feign 调用失败,OrderFleetBoardContextDTO 为 null):类别字段为 null。
  • 派车行/候选自身的 requirement_id 命中不到订单当前任何活跃需求身份(#5720 换版过渡窗孤儿行):类别字段为 null,不回退用订单上下文单值猜测。
  • 命中的身份自身 kind 字段为空白:类别字段为 null(防御性分支;order-v3 当前写路径恒写枚举 .name(),正常不触发,仅覆盖历史/异常数据)。
  • 以上三种 null 场景均只有单元测试覆盖(见八节),本次实测扫描未在测试服活数据中观测到对应真实记录。

六.5、枚举 / 数据字典

取值 中文标签 说明
TRAVEL 行程用车 行程用车需求
TRANSFER 接送机 接送机需求(#7439 引入)
null (不显示标签) 判不出类别,前端不得兜底为 TRAVEL

六.6、修改前后对比

  • 字段层面:MatrixUnassignedOrderVO 新增 requirementKind(String)、requirementKindLabel(String),VO 字段总数由 37 增至 39。
  • 行为层面:改动前,同一订单的两张未派卡在字段层面完全无法区分类别,只能靠车型/日期/备注人工判断,判断错了会把车派到错误的需求线上;改动后两张卡各自携带准确的类别标识,且判不出时明确返回 null 而非静默给出错误猜测。

六.7、影响评估

  • 破坏性:无。两个新增字段为可选新增,未删除/未重命名/未改变任何既有字段的类型或取值口径。
  • 涉及消费端:仅管理后台派单矩阵页。
  • 前端无需为此做兼容降级处理:未取到新字段(undefined)与取到 null 应做同等处理——均不显示类别标签。

七、不影响范围

  • 矩阵主数据端点 GET /admin/fleet/matrix/grid、年度月度统计 GET /admin/fleet/matrix/month-counts、当天订单清单 GET /admin/fleet/matrix/day-orders:均未改动。
  • 看板列表端点(BoardOrderRecordVO.requirementKind,#8518):未改动,其判不出类别时仍兜底 TRAVEL 的既有行为不变。
  • 写操作(拖拽派车、改派、取消等):本次改动只涉及查询响应字段新增,不涉及任何写路径。
  • MatrixUnassignedReqVO 请求参数:未新增/未修改(year/month/typeKeys 均为既有字段,越界错误码路由此前已分别由 #8561/#8571 调整完成)。

八、测试环境已验证

  • 部署确认:hl-fleet-service 现部署 SHA 99fb369ba(deploy-status.sh 实测,状态 ok),经 git merge-base --is-ancestor 8b045a321b 99fb369ba8 确认已包含本次改动的合并提交 8b045a321b(PR #8589)。
  • 实测(真实请求,非构造数据):使用车务角色测试账号(切至 VEHICLE_MANAGER 角色)对 GET /admin/fleet/matrix/unassigned-orders 发起真实请求:
    • 2026-09 月:2 条记录,requirementKind 均为 TRAVEL,含示例订单 HL20260911193207642(见响应示例节)。
    • 2026-11 月:1 条记录,requirementKind 为 TRANSFER,订单 HL20260929154809598(见响应示例节)。
    • 对 2026-06 ~ 2027-03 共 10 个月窗口的扫描(合计 14 条记录)未发现 requirementKind=null 的记录,也未发现同一订单出现两条不同类别记录的活跃实例——测试服当前业务数据里暂未出现这两种边界场景,实测未覆盖,靠下面的单元测试兜底。
  • 单元测试覆盖(源码单测验证,未在测试服活数据上复现):
    • BoardRequirementIdentitiesKindTest(5/5 通过):覆盖双身份按需求 ID 各取各类别、上下文降级返 null(对照既有方法仍兜底 TRAVEL)、陈旧需求 ID 不猜返 null、身份自身类别空白返 null、灰度上下文合成 TRAVEL 身份仍可取到。
    • MatrixServiceTest 新增 6 个 #8560 测试方法(均通过):同订单两类需求两张卡类别互不相同(含反向对照:真实行路径下两张卡 requirementId 字段完全相同;虚拟待派路径不适用该对照)、需求身份类别空白返 null 不兜底 TRAVEL、上下文降级返 null 不兜底 TRAVEL、派车行挂陈旧需求 ID(#5720)返 null 不兜底 TRAVEL、虚拟待派条目携带类别、虚拟待派条目无身份列表时类别为 null。
    • 聚合结果(mvn -pl hl-fleet-service -am test):Tests run: 365, Failures: 0, Errors: 0, Skipped: 0,BUILD SUCCESS;含 VehicleRequirementKindsTest 4、BoardOrderServiceTest 233、FleetRedLineArchTest 18(架构守护门禁绿)。
    • 嵌套用例选择器守卫(nested_selector_census):通过,内层名比对无缺组。
    • spotless:check:BUILD SUCCESS,916 文件全部合规。

十、相关文档

  • Issue #8560
  • PR #8589(合并提交 8b045a321b)
  • 相关既有机制:#7439(TRAVEL/TRANSFER 双需求引入)、#8518(看板列表既有类别字段)、#7067(虚拟待派条目/去槽位化)、#5667(requirementId 订单侧单值口径)、#5720(换版过渡窗孤儿行)

关联 / 联系人

  • 后端:wx(GIT)
  • 消费端:管理后台(派单矩阵页)