文件
hl-api-changelog/changelogs-v2/2026-09/22_8159_团级配车成员共用候选查询-新增接口-管理后台.md
T
2026-09-22 22:06:19 +08:00

19 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 8159 团级配车成员共用候选查询 admin wx(GIT) 新增接口 deployed verified verified mmg bc4024952daa784232abe6fbf56b658b57361bb3 v2.1 2026-09-22 2026-09-22 mmg 交付:ShareGroupEditModal 新建/编辑选举(浏览态三态 selectable 不误禁、shareGroupId 复算恢复本关系成员并预勾、members 全集提交、605036 确认重发、602106 刷新重选),ShareGroupPanel 加入口,抽屉透传 serviceDates;checkpoint 全绿 26 测试 2026-09-22 dev-v3

团级配车成员共用候选查询(#8159)

服务: hl-fleet-service(经网关调用,无需关心服务端口) PR: #8188 Issue: #8159 日期: 2026-09-22 影响范围: 管理后台团级配车页面成员共用选举


⚠️ 关键变化

新增查询端点,返回「成员共用」的候选行清单,喂给已有的「确认共用关系」写口。

三条会直接影响你怎么写代码的点,按严重度排:

  1. 🔴 shareGroupId 必须由前端比对,本接口不会替你做(业务边界第 2 条)。编辑一个已存在的共用关系时,它自己的既有成员会被本接口置灰成 ALREADY_IN_ANOTHER_GROUP——前端要把 shareGroupId 等于「正在编辑的关系 ID」的行恢复成可勾选。并且提交时 members 是全集不是增量:漏掉的既有成员会被写口按「移出本关系」处理,不报错、静默生效。
  2. 🔴 occupying / selectable 是三态,null 是「未判定」不是「否」。不传 resourceId 时 occupying 恒 null、selectable 永不为 true——但它可能是 false(已属别的关系、或团期身份缺失这两条浏览态就判得出来),所以置灰要照常渲染。
  3. 成功码是 code: 200,不是 0;业务失败也返 HTTP 200,一律按 body.code 判,不要按 HTTP 状态行判。

一、背景(选填)

团级配车支持多个成员(派车行、团级配车行)共享同一车或司机资源的场景。选举新成员进关系时,需先查询该服务日下哪些成员行可选。本接口支持按资源维度(车/司机)和具体资源 ID 筛选候选。


二、变更接口清单

# 接口 方法 路径 变更类型 说明
1 查询成员共用候选 GET /admin/fleet/group-dispatch/batches/{groupBatchId}/share-member-candidates 新增接口 返回该团服务日下可选成员清单

三、接口详情

1. 查询成员共用候选 GET /admin/fleet/group-dispatch/batches/{groupBatchId}/share-member-candidates

VO: ShareMemberCandidateRespVO

使用场景

管理后台团级配车页面「成员共用」选举弹窗打开时,调用本接口获取候选成员清单。前端先让用户选择资源维度(车 / 司机),再传 resourceId 重新查询,获得勾选框可选状态。

入参

字段 位置 类型 必填 约束 说明
groupBatchId Path Long ✅ — 团期 ID(group_batch_id)。🔴 这不是 product_batch_id;维度错误不报参数校验错,只返 data: []
serviceDate Query String(yyyy-MM-dd) ✅ 必须在团期服务日窗内 服务日。窗外日期返 code: 602104
resourceType Query String ✅ VEHICLE / DRIVER 资源维度。缺失或非法值返 code: 100001
resourceId Query Long ❌ — 具体资源 ID(车 ID / 司机 ID)。不传时 occupying / selectable 返 null(未判定状态,见业务边界第 1 条)

出参 Result<List<ShareMemberCandidateRespVO>>

字段 类型 说明
sourceType String ASSIGNMENT(派车行)/ GROUP_DISPATCH(团级配车行)
sourceId String 雪花 ID,字符串形态(19 位,前端一律当字符串处理,禁转 Number)
requirementId String 需求 ID(派车行有值,团级配车行可能 null)
orderId String / null 订单 ID。GROUP_DISPATCH 行可能为 null
orderNo String 订单号
customerName String 客户名称
headcount Integer 人数
pickupAt String 上车地点
dropoffAt String / null 下车地点,可能为 null
pickupParticipant Integer 上车人数
dropoffParticipant Integer 下车人数
groupCode String / null 团号,可能为 null
vehicleModel String / null 车型
occupiedVehicleId String / null 该行实际占用的车 ID;未占用时 null
occupiedVehiclePlate String / null 该行实际占用的车牌号
occupiedDriverId String / null 该行实际占用的司机 ID;未占用时 null
occupiedDriverName String / null 该行实际占用的司机名称
occupying Boolean / null 本行在 (serviceDate, resourceType, resourceId) 上是否活跃占用。resourceId 未传时为 null(未判定)
shareGroupId String / null 本行所属的 ACTIVE 共用关系 ID;不属任何关系时 null。🔴 调用方必须拿它和「正在编辑的关系 ID」比对,见业务边界第 2 条
selectable Boolean / null 是否可勾选。true = 可选;false = 不可选(此时 unselectableReason 非空);null = 未判定(resourceId 未传、且本行未被 ALREADY_IN_ANOTHER_GROUP / CROSS_BATCH 判死时出现)。🔴 resourceId 未传时本字段永不为 true
unselectableReason String / null 不可选原因枚举值(见六.5 章节);selectable=true 时为 null
unselectableDetail String / null 人类可读的补充说明,如「订单 HLxxxx 已在车 A 关系中」

请求示例

GET /admin/fleet/group-dispatch/batches/2102125467954044929/share-member-candidates?serviceDate=2026-12-25&resourceType=VEHICLE&resourceId=2085539421276286978

无请求体。Authorization 头由网关透传(管理后台 JWT)。

响应示例

成功响应(带具体资源 ID 的正常清单):

{
  "code": 200,
  "message": "成功",
  "data": [
    {
      "sourceType": "ASSIGNMENT",
      "sourceId": "2102125764705181697",
      "requirementId": "2102125585692295169",
      "orderId": "2102125467924684801",
      "orderNo": "HL20260922035901739",
      "customerName": "#8114AC8-X",
      "headcount": 1,
      "pickupAt": "满洲里口岸",
      "dropoffAt": null,
      "pickupParticipant": 0,
      "dropoffParticipant": 0,
      "groupCode": null,
      "vehicleModel": "丰田普拉多",
      "occupiedVehicleId": "2085539421276286978",
      "occupiedVehiclePlate": "蒙A-E2E99",
      "occupiedDriverId": "2065272150012444674",
      "occupiedDriverName": "道尔吉",
      "occupying": true,
      "shareGroupId": null,
      "selectable": true,
      "unselectableReason": null,
      "unselectableDetail": null
    }
  ],
  "success": true
}

示例说明:示例值取自测试环境某次真实调用,不构成可复现夹具。

空数据 / 降级响应

该团该服务日无任何在飞派单与活跃团级配车行时返 code: 200, data: [](不是 null、不抛错)。

{
  "code": 200,
  "message": "成功",
  "data": [],
  "success": true
}

🔴 区分「空」与「失败关闭」:拿不到团期权威基线时,本接口不降级返空,而是失败关闭抛 GROUP_BATCH_BASELINE_UNAVAILABLE。⇒ data: [] 只意味着「确实没有候选」,前端可以放心按「无可选成员」渲染,不必担心它其实是一次被吞掉的下游故障。

错误响应

服务日不在团期窗内:

{
  "code": 602104,
  "message": "服务日不在团期服务日窗内: 2026-12-28",
  "success": false,
  "data": null
}

参数非法(缺 resourceType 或传非法值):

{
  "code": 100001,
  "message": "参数非法: 资源维度非法: HOUSE",
  "success": false,
  "data": null
}

业务边界

0. 本清单直接喂给写口:返回的 sourceType + sourceId 就是「确认共用关系」写口 members[] 要的两个字段,原样透传即可,不需要再做映射。

1. resourceId 未传时的 null 三态语义

字段 传了 resourceId 未传 resourceId(浏览态)
occupying true / false 恒 null(未判定)
selectable true / false false 或 null,永不为 true
unselectableReason selectable=false 时非空 同左

null 的含义是未判定,不是「否」。不传 resourceId 时「当日占没占着这个资源」根本算不出来,所以 occupying 一律 null。

🔴 但浏览态下 selectable 并不总是 null:有两条判据不依赖 resourceId,浏览态照样能判死——

  • 已属别的 ACTIVE 共用关系 → selectable=false,unselectableReason=ALREADY_IN_ANOTHER_GROUP
  • 团期身份缺失(requirement_id 为空,或团期与本团不符)→ selectable=false,unselectableReason=CROSS_BATCH ⚠️ 订正 2026-09-22:本行原写「group_batch_id / requirement_id 为空」,group_batch_id 那一半不成立——取数条件是 eq(group_batch_id, ?),group_batch_id 为空的派单行结构上查不出来、压根不在清单里(见第六节「4. 覆盖范围」)。所以前端不必为「group_batch_id 为空 + CROSS_BATCH」写分支,那个组合不可能出现;会以 CROSS_BATCH 出现在清单里的,只有 requirement_id 为空或团期不符这两种。

⇒ 前端在浏览态不能把 selectable == null 当成 false 禁用勾选,也不能当成 true 放开;但要照常渲染 selectable === false 的置灰与原因——那些行在浏览态就已经定死了,选了具体资源也不会变回可选。

2. 🔴 shareGroupId 必须由调用方比对——这是契约的一部分,不是可选优化

本读口不知道你正在编辑哪一个共用关系,所以凡已属任一 ACTIVE 关系的行,一律置灰为 ALREADY_IN_ANOTHER_GROUP。而写口的 602106 拒的是「属于另一个关系」——同一关系的既有成员,写口是收的。

⇒ 给同一个 shareGroupId 追加第 3 个成员这个动作,本清单的置灰结构上覆盖不到。 前端必须自己做两件事:

  1. 把 shareGroupId 等于「正在编辑的关系 ID」的行视为可勾选(忽略本接口给它的 ALREADY_IN_ANOTHER_GROUP 置灰)。不做这一步的表现是:编辑一个已有关系时,它自己的现有成员全部不可选,功能做不出来。
  2. 🔴 提交时 members 是全集不是增量——要把该关系的既有成员连同新成员一起放进 members。漏掉的既有成员会被写口按「移出本关系」处理。 只提交新成员的写法不会报错,但会静默把原有成员全部踢出关系。

3. selectable=true 不是「提交必成功」的承诺

本清单是时点快照、不加锁。返回之后别的车务仍可能把某成员纳入另一个关系,提交时照样可能撞 602106。

⇒ 这种 602106 请渲染成「有人先一步占了,请刷新候选清单重选」,🔴 不要当系统故障自动重试——重试必然同样失败。这条竞态读口消除不了(加锁也不行,锁在响应返回那一刻就已放开),最终由写口的锁定读与唯一键兜底。

4. 覆盖范围:未关联团期的派单行结构上不在本清单内

本清单只列 group_batch_id 指向本团的派单行。group_batch_id 为空的派单行查不出来——但这不是缺口:这类行在写口同样恒被 602103 拒,「看不见」与「看得见也提交不了」等价。要让这类行可共用,得先补上团期身份(数据侧动作,不是前端能处理的)。


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

✅ 正确 / ❌ 错误请求对照

🔴 先说判据:本服务的业务失败与入参校验一律返 HTTP 200(GlobalExceptionHandler 带 @ResponseStatus(HttpStatus.OK)),body.code 才是真相。前端判错请一律读 body.code,不要读 HTTP 状态行——按状态行判会把所有业务错误当成成功。

场景 请求 预期(HTTP 恒 200)
✅ 浏览态,看全清单 ?serviceDate=2026-12-25&resourceType=VEHICLE(无 resourceId) code: 200;各行 occupying=null;selectable 为 null 或 false(被判死的行),不会是 true
✅ 选定车后判可选 ?serviceDate=2026-12-25&resourceType=VEHICLE&resourceId=2085539421276286978 code: 200;各行 occupying=true/false,selectable=true/false
✅ 同团其他服务日 ?serviceDate=2026-12-26&resourceType=VEHICLE&resourceId=xxx 在该团服务日窗内则照常返回候选,否则 code: 602104
❌ 维度错填 ?serviceDate=2026-12-25&resourceType=HOUSE&resourceId=xxx code: 100001「参数非法: 资源维度非法: HOUSE」
❌ 缺维度参数 ?serviceDate=2026-12-25&resourceId=xxx(无 resourceType) code: 100001「参数非法: 资源维度不能为空」
❌ 路径参数填成产品班期 ID /batches/{product_batch_id}/share-member-candidates?... 🔴 不报参数错,返 code: 200 + data: []。失败形态是静默的,看起来像「接口没数据」

五、数据库行为

本接口仅读,无任何 DB 写入,不新增表、不改表结构、无 Flyway 脚本。


六、边界行为

鉴权(🔴 本单专门订正过一处长期误解,前端排查 403 时会用到):

  • 未登录 / token 失效 → 网关 JwtAuthFilter 拦下
  • 已登录但角色不符 → FleetAdminRoleGuardInterceptor 对 /admin/fleet/** 只放行 VEHICLE_MANAGER 与 SUPER_ADMIN 两个角色
  • 🔴 鉴权的真相是角色,不是权限点:fleet:group-dispatch:view / fleet:group-dispatch:write 这两个字符串在整个 Java 侧零引用,它们只是权限字典里的声明,不是运行时判据。⇒ 「给某个非车务角色开个只读权限点,让他看候选清单」这件事配置不出来——那类角色连 /admin/fleet/** 都进不来。要改授权粒度得动拦截器,是另一件事。

其余边界(一律 HTTP 200,按 body.code 判):

  • 资源维度缺失 / 非法 → code: 100001
  • 服务日超出团期服务日窗 → code: 602104
  • 团期权威基线拿不到 → 失败关闭(不降级返空),见「空数据 / 降级响应」
  • 本团本服务日无候选 → code: 200 + data: []

网关路由:复用既有 - Path=/admin/fleet/** → lb://hl-fleet-service,本单不新增路由。


六.5 枚举 / 数据字典

sourceType(行源类型)

所属字段: sourceType | 类型: String

值 中文 说明
ASSIGNMENT 派车行 订单需求对应的派车行
GROUP_DISPATCH 团级配车行 团级配车单位产生的配车行

resourceType(资源维度)

所属字段: resourceType(查询参数) | 类型: String

值 中文 说明
VEHICLE 车辆 按车 ID 筛选候选
DRIVER 司机 按司机 ID 筛选候选

unselectableReason(不可选原因)

所属字段: unselectableReason | 类型: String

值 触发条件 浏览态(未传 resourceId)能否判出 对应写口错误码
CROSS_BATCH requirement_id 为空,或团期与本团不符。⚠️ 订正 2026-09-22:不含 group_batch_id 为空,那类行结构上查不出来、不在清单里(见第六节「4. 覆盖范围」) ✅ 能 602103
NOT_OCCUPYING 派单不在可改派的在飞态,或当日标记不用车 ❌ 不能(需 resourceId) 602102
ALREADY_IN_ANOTHER_GROUP 本行已属某个 ACTIVE 共用关系(此时 shareGroupId 非空) ✅ 能 602106
GROUP_DISPATCH_INVALID 团级配车行当日不在此资源上 ❌ 不能(需 resourceId) 602105

说明:这一列值不是接口返回的 code,而是 unselectableReason 字段的取值。本接口只要能返回清单,code 恒为 200;上面那些 6021xx 是提交共用关系(写口)失败时才会出现在 code 里的。

🔴 ALREADY_IN_ANOTHER_GROUP 这一行前端必须自己复算:本读口不知道你在编辑哪个关系,会把正在编辑的那个关系的既有成员也置灰成它。判据是 shareGroupId 是否等于正在编辑的关系 ID,详见业务边界第 2 条。


六.6 修改前后对比

无,本接口为新增。


六.7 影响评估

  • 破坏兼容:否,新增接口
  • 前端同步上线要求:否,新接口不影响现有流程
  • workaround 清理:无

七、不影响范围

  • 仅影响:管理后台团级配车页面成员共用选举功能
  • 零影响:
    • 派车行的查询 / 新增 / 编辑
    • 订单创建 / 支付流程
    • 团期成团 / 分配等其他流程

八、测试环境已验证

真实接口调用结果(hl-fleet-service 已部署 dev-v3 @ cf9dc85a4,merge commit a584bd087):

GET /admin/fleet/group-dispatch/batches/2102125467954044929/share-member-candidates
  ?serviceDate=2026-12-25&resourceType=VEHICLE&resourceId=2085539421276286978
  → 200 + 候选清单(occupying=true/false, selectable=true/false) ✓

GET /admin/fleet/group-dispatch/batches/2102125467954044929/share-member-candidates
  ?serviceDate=2026-12-25&resourceType=VEHICLE
  → 200 + 候选清单(occupying=null, selectable=null) ✓

GET /admin/fleet/group-dispatch/batches/2102125467954044929/share-member-candidates
  ?serviceDate=2026-12-28&resourceType=VEHICLE&resourceId=xxx
  → 602104(服务日不在团期服务日窗内) ✓

GET /admin/fleet/group-dispatch/batches/2102125467954044929/share-member-candidates
  ?serviceDate=2026-12-25&resourceType=HOUSE
  → 100001(参数非法: 资源维度非法: HOUSE) ✓

九、相关历史 PR

无,本接口为新增。


十、相关文档


关联 / 联系人

链接

联系人

  • 后端负责人: @wx