hl-api-changelog/changelogs-v2/2026-07/23_5187_多车辆槽位原子批量派车与价格日历带价_前端待处理-新增接口-管理后台.md
API Changelog Bot e97a559737
所有检测均成功
changelog-filename-gate / validate (push) Successful in 1s
docs(changelog): reconcile frontend status for #5187
2026-07-24 17:36:08 +08:00

13 KiB

schema, ticket, title, consumer, change_type, backend_status, gateway_status, frontend_status, frontend_owner, frontend_ref, target_release, verified_at, status_note, updated_at, base, generated
schema ticket title consumer change_type backend_status gateway_status frontend_status frontend_owner frontend_ref target_release verified_at status_note updated_at base generated
hl-changelog/v2 5187 多车辆槽位原子批量派车与价格日历带价 admin 新增接口 deployed verified implemented hl-ui-codex mmg/hl-ui@6479adf1ca hl-ui/v2.1 前端 v2.1 已实现按 fleetItemIndex 的多槽位选择、批量提交和重复车辆/司机禁选;测试环境 9527 已提供对应源码,尚待登录态页面实操验收。 2026-07-24 dev-v3 2026-07-23T15:38:00+08:00

【新增接口·前端待处理·管理后台】多车辆槽位原子批量派车与价格日历带价

目标前端

  • 端类型管理后台Web
  • 目标仓库:mmg/hl-ui
  • 目标分支:v2.1
  • 页面:车务管理 → 派车看板 → 派车派人弹窗、派单详情
  • 前端交接:仅以本 hl-api-changelog 文档为准,不另建前端仓库工单。
  • 小程序:无需处理

后端工单: wx/HL#5187

后端 PR: wx/HL#5189

兼容性: 既有单槽位 POST /admin/fleet/assignments 不变;多车订单必须改用本次批量接口, 前端不得循环调用单派接口。

一、业务口径

一条用车需求可能展开出多个车辆槽位。派车弹窗应按 fleetItemIndex 维护多组 “车辆 + 司机 + 协议价”,允许一次选择多辆车并一次提交。整批任一槽位失败时不得留下前面 已成功、后面失败的半批派单。

  • 同一批内 fleetItemIndexvehicleIddriverId 分别不可重复。
  • 前端只允许选择当前需求实际展开出的待派槽位,不得自行增加超过需求数量的车辆。
  • 雪花 ID 全程按字符串保存和提交。
  • 同一次提交及其网络重试必须复用同一个 requestId;用户修改选择后主动再次提交应生成新值。
  • holdMode=1 表示排车中等待司机确认,holdMode=0 表示直接派定;整批模式必须一致。

变更接口

POST /admin/fleet/assignments/batch
Content-Type: application/json

请求示例:

{
  "orderId": "2046800000000000001",
  "orderNo": "26-4165",
  "requirementId": "2046800000000000101",
  "startDate": "2026-07-28",
  "endDate": "2026-07-30",
  "pickupAt": "海拉尔",
  "dropoffAt": "满洲里",
  "headcount": 8,
  "chargeableServiceDates": [
    "2026-07-28",
    "2026-07-29",
    "2026-07-30"
  ],
  "holdMode": 1,
  "skipCityJunctionException": false,
  "fromEntry": "from-board",
  "requestId": "fleet-batch-7fe5c3a8",
  "items": [
    {
      "fleetItemIndex": 0,
      "vehicleId": "2046800000000000201",
      "driverId": "2046800000000000301",
      "protocolPrice": "520.00",
      "confirmCrossResident": false
    },
    {
      "fleetItemIndex": 1,
      "vehicleId": "2046800000000000202",
      "driverId": "2046800000000000302",
      "protocolPrice": "860.00",
      "confirmCrossResident": false
    }
  ]
}

公共字段

字段 类型 必填 说明
orderId String(Long) 订单 ID
orderNo String 订单号冗余
requirementId String(Long) 当前生效用车需求 ID
startDate / endDate LocalDate 整批服务日期闭区间
pickupAt / dropoffAt String 接送地
headcount Integer 乘客人数
chargeableServiceDates LocalDate[] 不传=全部计费;空数组=全部免费
vehicleFeeWaiverReason String 条件必填 存在免费服务日时填写
confirmAllServiceDatesFree Boolean 条件必填 全部免费时必须为 true
holdMode Integer 1=排车中0=直接派定
skipCityJunctionException Boolean 与单派接口同义
fromEntry String 操作来源
requestId String 批次幂等键,最大 64 字符
items Object[] 1-20 个车辆槽位

items[]

字段 类型 必填 说明
fleetItemIndex Integer 当前需求展开后的槽位序号,0 起
vehicleId String(Long) 所选车辆 ID,批内不可重复
driverId String(Long) 所选司机 ID,批内不可重复
protocolPrice String(BigDecimal) 元/车天;不传时后端按车型价格日历兜底
messageTemplateId String(Long) holdMode=1 的通知模板
customBody String holdMode=1 的本次自定义通知正文
confirmCrossResident Boolean 跨常驻车辆组合的显式确认

成功响应按 fleetItemIndex 升序返回:

{
  "code": 200,
  "data": {
    "assignments": [
      {
        "fleetItemIndex": 0,
        "assignment": {
          "id": "2046800000000000401",
          "assignmentGroupId": "2046800000000000501",
          "assignmentSlotId": "2046800000000000601",
          "assignmentStatus": "holding",
          "stageCode": "holding_wait_driver",
          "stageLabel": "排车中·等待司机确认",
          "currentStep": 2,
          "skippedStepCodes": [],
          "protocolPrice": "520.00",
          "holdSentAt": null,
          "confirmedAt": null,
          "sideEffects": null,
          "dailyDifferences": null
        }
      }
    ],
    "failedFleetItemIndex": null,
    "dailyDifferences": null
  },
  "message": "成功",
  "success": true
}

直接派定发生订单/行程/需求冻结基线不一致时返回既有业务码 605041,并额外指出失败槽位:

{
  "code": 605041,
  "data": {
    "assignments": [],
    "failedFleetItemIndex": 1,
    "dailyDifferences": [
      {
        "serviceDate": "2026-07-29",
        "differenceType": "CAPACITY_INSUFFICIENT",
        "message": "逐日车辆可用座位不足"
      }
    ]
  }
}

无论返回哪一种失败,整批均不产生部分成功数据。前端失败后保留用户当前选择并展示后端文案; 605041 可同时高亮 failedFleetItemIndex 对应槽位及逐日差异。

三、派车弹窗前端修改

3.1 多车辆选择

当前实现只有全局单值 selVehicle/selDriver,再次选择会覆盖上一辆车。需改成按 fleetItemIndex 保存的槽位数组或 Map

selectedSlots[fleetItemIndex] = {
  vehicle,
  driver,
  protocolPrice,
  confirmCrossResident
}
  • 点击某个候选车辆只修改当前待选槽位,不清空其他已选槽位。
  • 已选摘要、取消车辆、司机选择和常驻组合提示均必须作用于对应槽位。
  • 提交前校验所有本次待派槽位都有车辆和司机,然后一次调用批量接口。
  • 禁止用 for 循环调用旧单派接口;那会在中途失败时留下半批状态。
  • 成功后一次关闭弹窗并刷新看板;不得每成功一辆刷新一次。

当前消费差距

  • useVehicleDriverPicker.js 仍只维护一组 selVehicle/selDriver
  • AssignModalFooter.vue 仍只展示一组车辆和司机,并按这一组决定按钮是否可用。
  • useAssignFlow.js 仍只调用 createAssignment,没有构造 items[]
  • src/api/fleet/board.js 尚未封装 POST /fleet/assignments/batch

展示矩阵

场景 “已选车辆”区域 候选/司机联动 主操作
尚未选择 显示 已选车辆 0/N 和 N 个待选槽位 提示先选择车辆 禁用,显示未完成组数
已选一辆 槽位 01 显示车牌、车型、司机和移除操作,并成为当前编辑槽位 已选车辆标记不可重复;司机只写入当前槽位 未完成全部槽位时保持禁用
继续多选 新车辆进入下一个待选 fleetItemIndex;其他已选槽位保持不变 已被其他槽位使用的车辆和司机不可重复选择 全部槽位完整后启用
切换槽位 高亮当前编辑槽位;允许单独更换车辆、司机和价格 候选与司机面板切换到该槽位上下文 完整度实时更新
搜索/筛选/翻页 已选区域固定可见,集合不丢失 只改变候选列表 状态保持
HOLD 完整 显示 已选择 N/N 辆,司机 N/N 每槽位独立司机 下一步 · 发送给 N 名司机
DIRECT 完整 显示 已选择 N/N 辆,司机 N/N 每槽位独立司机 直接派定 N 辆车
批量失败 保留全部选择;高亮 failedFleetItemIndex 允许修正失败槽位 原批次不产生部分成功
批量成功 清空选择并关闭弹窗 看板只统一刷新一次 仅发送一次批量请求

“已选车辆”应作为车辆筛选与候选列表之间持续可见的紧凑区域,不得只在底栏显示最后一辆。 选择数量不得超过当前需求的待派车辆槽位数;移除某一槽位不得重排或清空其他槽位。

3.2 车型价格日历自动带价

候选接口 vehicles[].protocolPrice 已返回所选车辆车型在服务开始日的价格日历单价。当前页面 只从订单级 props.order.protocolPrice 初始化输入框,导致价格日历明明有值仍显示空。

  • 选中车辆时,把该车辆的 protocolPrice 写入对应槽位价格框。
  • 每辆车独立显示、独立可编辑,提交到 items[].protocolPrice
  • 切换车辆时改为新车辆的价格日历值;不能沿用上一辆车的价格。
  • 候选值为空时输入框可留空,后端仍会在最终保存时按所选车辆车型 + startDate 再兜底一次。
  • 不得把一个全局价格复制给所有不同车型。

3.3 联系定制师

派车看板卡片已有“联系定制师”,派单详情第 1 步和后续派车弹窗也应与房务详情保持一致:

  • 在详情可见区域补“联系定制师”按钮,复用现有 open-fleet 会话流程。
  • 订单 ID 使用数字雪花字符串,不能传 HL... 展示号或团号。
  • 按钮位置、图标、禁用态、加载态和聊天抽屉交互复用房务模块,不另做一套样式。
  • 首次打开真实会话和实时未读角标仍按 #5180 前端交接处理。

四、派车看板默认状态筛选

这是前端初始化逻辑修复,不需要后端接口变更:

  • statusSel 初始值必须为 [],页面首次进入状态框显示空/不限。
  • 首次列表请求不得携带 statuses=unassigned,默认展示全部状态。
  • 点击“重置”后的值和首次进入完全一致。
  • 用户主动选择“待派车”后才传对应状态;刷新筛选结果时不得偷偷恢复默认待派车。

五、前端验收清单

  • 一条需求展开 2 个车辆槽位时,可同时选择 2 辆不同车辆和 2 名不同司机,第一辆不会被第二辆覆盖。
  • 提交只发送 1 次 /admin/fleet/assignments/batch,不循环调用旧单派接口。
  • 第二槽位失败时页面提示失败,刷新后两个槽位都没有半批残留。
  • 价格日历有值时,选择每辆车后各自价格框立即带出对应 protocolPrice
  • 修改某辆车价格只影响该槽位,成功响应按槽位回显冻结价格。
  • 派单详情第 1 步和派车流程均能直接“联系定制师”,交互与房务一致。
  • 派车看板首次进入状态筛选为空,首次请求不传 statuses,默认可见全部状态。
  • 主动筛选“待派车”及重置行为正确。
  • 增加多槽位状态管理、批量请求映射、车型切换带价和默认空筛选的组件/组合式函数测试。

验证证据

  • mvn -pl hl-fleet-service spotless:check 通过。
  • AssignmentControllerTest + AssignmentServiceTest281 项通过。
  • mvn -pl hl-fleet-service -am verify 通过fleet 2334 项,0 failure / 0 error,1 skipped。
  • 批量成功、空明细校验、重复槽位校验、字符串雪花 ID、直接派定基线差异及事务/幂等注解均有测试覆盖。
  • 测试环境部署任务 28f9048a 成功,hl-fleet-service 两个滚动实例均恢复健康。
  • 经测试环境网关验证:
    • 派车看板列表请求返回 HTTP 200 / 业务码 200。
    • 批量接口空明细返回业务码 400,文案为“派单车辆槽位不能为空”。
    • 批量接口重复 fleetItemIndex 返回业务码 100001,且未产生写入。
    • 自建并标记测试订单,使用 SUV + MPV 两个槽位执行失败探针:第一槽位合法、第二槽位车辆不存在, 接口返回 605001;随后详情仍为 0 个有效派单,证明第一槽位及副作用意图随整批回滚。
    • 同一测试订单使用两个合法槽位执行成功探针:接口返回 200,结果按 fleetItemIndex=[0,1] 排序,价格快照分别为 700.00860.00,详情恰有 2 个 assigned 派单。
    • 使用相同 requestId 重放返回业务码 100502;重放后详情仍恰有 2 个有效派单,无重复写入。
    • 验收后已通过订单取消 API 精确清理自建测试订单,订单状态为 CANCELLED,详情有效派单恢复为 0。
  • 部署后 fleet 服务与网关日志未发现 ERROR;重复槽位探针只产生预期的业务校验 WARN。

本文是前端接入通知,不代表已修改或发布 mmg/hl-ui