文件
hl-api-changelog/changelogs-v2/2026-09/21_7994_受控重开窗口内无分组历史派车行可被收编-修改接口-管理后台.md
T
2026-09-21 11:30:56 +08:00

21 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 7994 团期配车受控重开窗口:分组列上线前的历史派车行现在可以被收编,不再让团期卡死 admin wx(GIT) 修改接口 deployed verified verified mmg 2044ec5281e0eb4a0fb3b1567a92e73916bd4204 2026-09-21 gateway_status=verified 的判据:2026-09-21 经测试服网关 https://api.test.1814.love:9443 对团期 2099959465330556929 实跑了完整四步(重开窗口 → 重配 → 确认配车 → 确认需求),全部 200,三行历史派车行的分组由空写成 GC、团期车务就绪位由 0 置 1,逐步请求/响应已记入工单 #7994 AC-3。backend_status=deployed 有两条互相独立的判据:①行为自证——这四步在本次修复之前必然报 602013(工单背景节实测过两次),能走通本身就说明跑着的字节里含本次修复;②测试服上 /opt/hulalv/jars/hl-fleet-service-1.0.0-SNAPSHOT.jar 的 mtime 是 2026-09-21 10:18:22,晚于本次修复合入 dev-v3 的 09:47:35。🔴 顺带订正一条会误导人的登记值:另一条取证线记录的 fleet 部署点 51571c58a 与上述两条判据矛盾(51571c58a 的提交时刻是 04:25,且本次修复不是它的祖先)——那是 10:18 重滚之前的陈旧读数,别再据它判断「某修复还没上测试服」。⚠️ 未覆盖:团期状态推进(本次只解开死路,未推进 batch_status);计划刷新是同步还是异步未区分(两者终态相同)。 2026-09-21 dev-v3

团期车务:受控重开窗口内,无分组的历史派车行可以被收编(工单 #7994)

存放目录: 二期(order-v3 标签工单)→ changelogs-v2/2026-09/

服务: hl-fleet-service(8087) PR: #8085 Issue: #7994 日期: 2026-09-21 影响范围: 管理后台「团期配车页」——受控重开窗口内的重新配车操作,以及它报错时的提示文案


⚠️ 关键变化

  • 请求体和响应体的字段一个都没变,前端不需要改接口对接代码。变的是两件事:①一类原先必定被拒的请求现在会成功;②被拒时的提示文案变长了,且新文案里带着操作指引。
  • 🔴 唯一需要前端确认的一点:602013 的提示文案现在可能很长,必须完整展示给车务,不能截断、不能只显示前一行。 新增的那半句正是告诉车务「该怎么自救」的部分,截掉了这条路就没人找得到(详见「三、接口详情 → 错误响应」)。
  • 这次改动没有放松任何权限或范围校验:窗口授权范围以外的分组、以外的日期,行为一字未变,仍然整批拒绝且零写入。

一、背景

「分组」这一列是 2026-09-17 才加到团期派车记录上的。在那之前排的车,记录上没有分组——不是脏数据,是那个事实当时就不存在。

车务给一个团期开「受控重开窗口」重新配车时,系统要检查每一行改动有没有越出窗口授权的范围。此前的规则是:没有分组的历史行一律判越界(说不出它属于哪个组,就不敢让窗口里的操作动它)。

这条规则本身没错,但它和另一条规则撞上了:团期进入「物资准备」阶段后,不开窗口就不许配车。两条一叠加,一个带历史派车行的团期就成了单向死路:

车务的走法 结果
开窗口后重新配车 拒绝:「本次配车改动越出重开窗口授权范围」(602013)
不开窗口直接配车 拒绝:「团期当前状态不可配车: 物资准备中」(600010)
跳过配车直接确认 拒绝:「整组未排车」(602008,历史行不算数)

而开窗口这个动作本身会把团期的车务就绪位清零,于是团期再也过不了发团门禁——出不了团,也修不回去。测试环境实际撞上这个状态的团期有 1 个。

改后的判定口径:按「这一行会被怎么处理」分,而不是按「它属于哪个组」分

没有分组的历史行,在一次重新配车里只有两种下场,风险完全不对称:

下场 什么时候发生 改后
被收编——这一行被写上分组 车务在本次请求里带上了同一辆车、同一天,并给了分组 ✅ 放行
被删除——这一行被撤掉、占用被释放 车务在本次请求里没写这辆车这一天 ❌ 仍然拒绝(602013),一字未变

放行为什么是安全的:收编时写进去的那个分组,本身仍要经过窗口授权范围的检查。想把历史行收编进一个没被授权的分组,一样会被拒。⇒ 历史行永远不可能被写进未授权的分组,也永远不可能被窗口删掉。


二、变更接口清单

# 接口 方法 路径 变更类型 说明
1 整团逐日配车提交(重新配车) POST /admin/fleet/group-dispatch/batches/{groupBatchId}/reconfigure 行为放宽 + 错误提示文案变化 请求体/响应体结构零变化

三、接口详情

1. 整团逐日配车提交 POST /admin/fleet/group-dispatch/batches/{groupBatchId}/reconfigure

VO: GroupDispatchReconfigureReqVO → GroupDispatchReconfigureRespVO(字段无增删改)

使用场景

管理后台「团期配车页」,车务在受控重开窗口有效期内提交整团逐日的配车安排。本次改动只影响**团期带有「分组列上线前的历史派车行」**这一种情形;不带历史行的团期,行为逐字不变。

入参(本次无变化)

字段 位置 类型 必填 约束 说明
groupBatchId path Long 是 — 团期主订单 ID
requirementId body Long 是 须等于基线当前活跃需求 正式团级用车需求 ID,落后抛 602005
requirementVersion body Integer 是 须等于基线当前版本 需求版本,落后抛 602005
clearAll body Boolean 否 默认 false 显式整团清零标志
reconfigureWindowToken body String 条件必填 团期已过资源准备阶段时必填 受控重开窗口令牌;缺失/不匹配/过期抛 602012
survivorPolicy body String 条件必填 clearAll=true 且存在 active 共用关系时必填 幸存共用派单处置策略
demands body Array 条件必填 clearAll=false 时必填 逐日配车需求列表
demands[].tripDate body LocalDate 是 yyyy-MM-dd 行程日期
demands[].assignments body Array 是 非空 当日排车项列表
demands[].assignments[].groupId body String 是 非空白;须在窗口授权范围内 乘车分组键(= 需求侧 group_code)
demands[].assignments[].vehicleId body Long 是 — 派出车辆 ID
demands[].assignments[].driverId body Long 否 可空 = 仅排车未排司机 派出司机 ID
demands[].assignments[].remark body String 否 — 备注

🔴 收编一条历史行的做法就落在这张表里,没有任何新字段:把历史行所在的「tripDate + vehicleId」原样写进 demands,并给它一个 groupId。

出参(本次无变化,字段来源:GroupDispatchReconfigureRespVO 的 @ApiModelProperty 声明)

字段 类型 说明
groupBatchId Long 团期主订单 ID
requirementId Long 正式团级用车需求 ID
requirementVersion Integer 正式团级用车需求版本
planVersion Long 团期计划版本
addedCount Integer 新增派车记录数
removedCount Integer 软删派车记录数
keptCount Integer 保留未变派车记录数
updatedCount Integer 就地更新派车记录数——收编走的就是这一格
aliveCount Integer 存活派车记录总数
addedDispatchIds List<Long> 新增派车记录主键列表
idempotentShortCircuit Boolean 本次是否被计划去重短路(幂等成功,非失败)
coverage Object 按乘车分组的覆盖明细,含 groups / missingGroupCodes / wholeBatchSatisfied
legacyGroupRowCount Integer 无分组键的历史派车行数(非错误,仅留痕)——这一格就是本次改动针对的那类行
releasedShareGroupIds List<Long> 本次连带解除的共用关系 ID 清单
keptSourceIds List<Long> 保留占用的 claim 来源 ID 清单
releasedSourceIds List<Long> 占用已被真正释放的派单 ID 清单
pendingReassignSourceIds List<Long> 待人工改派的派单 ID 清单(占用已释放、当前无车)

请求示例

收编三行历史派车行(同一辆车、三个连续日期),给它们分组 GC:

{
  "requirementId": 2099959465330556930,
  "requirementVersion": 5,
  "clearAll": false,
  "reconfigureWindowToken": "<重开窗口返回的令牌>",
  "demands": [
    {
      "tripDate": "2026-11-27",
      "assignments": [
        { "groupId": "GC", "vehicleId": 2064995255698010113, "driverId": 2065272145289658370 }
      ]
    },
    {
      "tripDate": "2026-11-28",
      "assignments": [
        { "groupId": "GC", "vehicleId": 2064995255698010113, "driverId": 2065272145289658370 }
      ]
    },
    {
      "tripDate": "2026-11-29",
      "assignments": [
        { "groupId": "GC", "vehicleId": 2064995255698010113, "driverId": 2065272145289658370 }
      ]
    }
  ]
}

响应示例

{
  "code": 0,
  "msg": "操作成功",
  "data": {
    "groupBatchId": 2099959465330556929,
    "planVersion": 2,
    "addedCount": 0,
    "removedCount": 0,
    "keptCount": 0,
    "updatedCount": 3,
    "aliveCount": 3,
    "addedDispatchIds": [],
    "idempotentShortCircuit": false,
    "coverage": {
      "missingGroupCodes": [],
      "wholeBatchSatisfied": true
    },
    "legacyGroupRowCount": 0
  }
}

⚠️ 上例中只有 updatedCount / addedCount / removedCount / wholeBatchSatisfied 四项是 2026-09-21 实测读数(见「八、测试环境已验证」);其余字段按其语义填的示意值,且为便于阅读省略了 coverage.groups 与几个空数组字段——不要拿它当字段全集,字段全集以上面的出参表为准。

📌 收编是就地更新,不是删掉重建:三行历史行被收编后 updatedCount=3 / addedCount=0 / removedCount=0,派车记录的 id 不变。前端若按派车记录 id 做过本地缓存或选中态,不会失效。

空数据 / 降级响应

  • 团期一条派车行都没有:aliveCount=0、coverage.wholeBatchSatisfied=false、coverage.missingGroupCodes 列出缺的组码;这是正常响应不是错误。
  • 重复提交同一份计划(超出 10 秒防重窗口):正常受理并返回 idempotentShortCircuit=true,addedCount/removedCount/updatedCount 全 0——那是成功(计划未变、未落库),前端不要按错误提示。
  • 10 秒内重复提交:被防重窗口拒绝,提示「团期配车重配处理中,请勿重复提交」,前端按「稍后重试」处理。
  • 以上三种在本次改动中行为一字未变。

错误响应

602013 的 message 此前只有越界项清单。改后,当越界项里含「(无分组历史行)」时,末尾会追加一段操作指引:

{
  "code": 602013,
  "msg": "本次配车改动越出重开窗口授权范围: (无分组历史行):2026-11-27,(无分组历史行):2026-11-28; 其中 (无分组历史行) 是分组列上线前的历史派车行, 本次请求没有它因而会被删除; 窗口内不允许删除它, 请把该日期该车一并写进本次配车请求并给出正确的乘车分组, 即可将其收编",
  "data": null
}

⇒ 🔴 请确认「团期配车页」的错误提示区能完整展示这段文字(多行换行展示即可,不要单行截断、不要因为 tooltip 放不下就省略)。这段话是车务自救的唯一入口。

📌 越界项里不含「(无分组历史行)」时(即普通的分组越界、日期越界),message 与改前逐字节相同,不会变长:

{
  "code": 602013,
  "msg": "本次配车改动越出重开窗口授权范围: GA:2026-11-27",
  "data": null
}

错误码清单(本次不新增、不删除任何错误码):

code 说明 本次是否变化
602013 本次配车改动越出重开窗口授权范围 触发条件收窄;含历史行时文案追加指引
602012 重开窗口令牌缺失/不匹配/已过期 不变
602011 窗口内不允许整团清空 不变
602008 整组未排车 不变(但历史行被收编后不再误报)
602005 需求身份落后 不变
602000 配车请求缺少乘车分组 不变
600010 团期当前状态不可配车 不变
600006 / 600007 车辆/司机当天已被占用 不变

业务边界

  • 只有**同时满足「同一行程日 + 同一车辆」**的历史行才会被收编;只对上日期或只对上车辆都不算,仍按删除处理并拒绝。
  • 收编时给的 groupId 照样要过窗口授权范围校验:不在范围内仍返 602013,且零写入。
  • 本次改动不触碰窗口授权范围本身的计算,也不触碰「窗口内不允许整团清空」(602011)。
  • 一次请求里可以同时收编多行;三行历史行在一次请求里全部收编是实测过的形态。
  • 收编不改变派车记录的主键,也不产生新的派车记录。

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

✅ 正确 / ❌ 错误 的应对方式

遇到「越出重开窗口授权范围」且提示里出现「(无分组历史行)」时:

做法 结果
❌ 去找管理员重开一个范围更大的窗口 没用。窗口里写什么分组都授权不了「没有分组」的行——这条路改前改后都走不通
✅ 把提示里点名的那个日期、那辆车一并写进本次 demands,并给它一个正确的乘车分组 该行被就地收编,计入 updatedCount

前端需要做什么

事项 是否需要改
请求体字段 ❌ 不用改
响应体字段 ❌ 不用改
错误码分支 ❌ 不用改(没有新错误码)
602013 提示文案的展示 ✅ 确认能完整展示长文案,不截断
页面文案/引导 选做:若「团期配车页」有配车失败的帮助文案,可以补一句「提示里出现『无分组历史行』时,把它点名的日期和车辆一并加进本次配车即可」

前端已交付(mmg 2026-09-21, hl-admin 2044ec52):配车计划编辑器(GroupDispatchPlanEditor)提交失败时若 message 含「无分组历史行」,在编辑器内常驻 n-alert 完整展示该指引(自然换行不截断),再次提交自动清空;其余错误仍走拦截器 toast。请求/响应字段零变化,对接代码未动。editor spec 6 例全过(长文案常驻展示+普通失败不出常驻块),checkpoint 通过。 |


五、数据库行为

  • 收编走的是就地 UPDATE:派车记录表 fleet_group_dispatch 中被收编的行,group_id 由 NULL 写成请求里给的分组码,version +1,主键 dispatch_id 不变,不产生新行、不软删旧行。
  • 实测三行(2026-11-27/28/29,同车同司机):group_id NULL → GC、status ASSIGNED → CONFIRMED(第 3 步确认配车所致)、version 0 → 1。
  • 被拒绝时(602013)零写入——这一点改前改后相同,本次未放松。
  • 无新增表、无新增列、无 Flyway 迁移。

六、边界行为

情形 改前 改后
历史行所在的「日期 + 车辆」出现在本次 demands 里 602013 ✅ 成功,该行分组被写成请求里给的值,计入 updatedCount
历史行所在的「日期 + 车辆」没有出现在本次 demands 里 602013 602013(不变)
收编时给的分组不在窗口授权范围内 602013 602013(不变)
团期里没有任何无分组历史行 按窗口授权范围判 完全不变(越界集合与错误文案两侧都逐字节相同)
普通的分组越界 / 日期越界 602013 602013,文案逐字节不变

六.6、修改前后对比

字段级对比

无任何字段变化——请求体、响应体、错误信封三处的字段名、类型、层级全部与改前一致。这也是本篇不要求前端改对接代码的原因。

行为级对比

维度 改前 改后
判定依据 这一行属于哪个组(没有组 ⇒ 一律越界) 这一行会被怎么处理(被收编 ⇒ 放行;被删除 ⇒ 越界)
带历史行的团期 单向死路,出不了团也修不回 车务可自行收编后继续走确认流程
602013 文案 只有越界项清单 含历史行时追加操作指引;不含时逐字节不变

六.7、影响评估

  • 前端:只有一处——602013 长文案的展示。无字段改动、无错误码改动。
  • 后端:仅 hl-fleet-service 一个服务,改动落在受控重开窗口的范围校验与错误文案两处。
  • 数据:不需要任何存量数据订正。历史行被收编是车务在页面上的正常操作,不需要刷库。
  • 回归面:不带无分组历史行的团期,越界判定与错误文案两侧结构性不可达本次改动(非空分组码走的仍是改前那条判断,一字未改),且该类的 10 条既有单测一字未动全绿。

七、不影响范围

显式声明没有被这次改动碰到的东西,帮前端/QA 缩小排查面:

  • 整团清空(clearAll=true)与 survivorPolicy 的处理:未碰。
  • 窗口授权范围本身怎么算出来的:未碰。
  • 602012(令牌)、602011(窗口内禁清空)、602005(需求身份落后)、600010(团期状态不可配车)、600006/600007(车/人被占)的触发条件:全部未碰。
  • 共用关系(share-group)的建立、解除、收缩:未碰。
  • 权限点 fleet:group-dispatch:write / fleet:group-dispatch:view:未碰。
  • 防重窗口与幂等短路(idempotentShortCircuit):未碰。
  • 团期状态机的推进:未碰——本次只解开死路,batch_status 不因这次改动而变化。

八、测试环境已验证

2026-09-21 经测试服网关 https://api.test.1814.love:9443,对团期 2099959465330556929(三行 2026-11-27/28/29 的无分组历史派车行)走完整链路,四步全部 200:

# 动作 结果
1 重开受控窗口 200,拿到新窗口令牌
2 带窗口重新配车(请求里带上那辆车 + 三天 + 分组 GC) 200,updatedCount=3 / addedCount=0 / removedCount=0,wholeBatchSatisfied=true
3 确认配车 200,confirmedCount=3(改前此处报 602008「整组未排车」)
4 确认团期车务需求 200,需求状态转 CONFIRMED

三行派车记录的分组由空变为 GC,团期车务就绪位由 0 置 1,单向死路解除。

⚠️ 取证中踩到的一个坑(与本次改动无关,但会影响联调):以 ADMIN 角色调 /admin/fleet/** 全部端点返 403「无权限访问车务管理」,必须切到车务角色或超级管理员。这是既有的路径级角色门禁,不是本次引入的。

⚠️ 本次未覆盖:团期状态推进(本次只解开死路,batch_status 仍是物资准备中);「计划刷新」是同步完成还是极快的异步消费,两者终态相同,本次没有做间隔采样因而分不出来——这只影响时序假设,不影响上表任何一行结论。


九、相关历史 PR

PR 说明
#8085 本次修复(工单 #7994)
工单 #7442 分组列与受控重开窗口的来源——本次要修的死路正是这两条规则叠加出来的

十、相关文档

  • 工单 #7994(含逐条验收取证与本次改动的结构性论证)
  • 工单 #7442(分组列 + 受控重开窗口的原始需求)
  • 同期同服务的相邻变更:20_8051_解除共用关系不再跨服务日跨团误清派车行-修改接口-管理后台.md、20_8061_解除共用关系只清成员占用同槽非成员不动-修改接口-管理后台.md

关联 / 联系人

链接

  • Issue: #7994
  • PR: #8085
  • 服务: hl-fleet-service(8087)
  • 网关: https://api.test.1814.love:9443

联系人

  • 后端: wx
  • 前端(管理后台): mmg