文件
hl-api-changelog/changelogs-v2/2026-09/20_8061_解除共用关系只清成员占用同槽非成员不动-修改接口-管理后台.md
T
API Changelog Bot和Claude Opus 5 9d4b4387f0
changelog-filename-gate / validate (push) Failing after 1s
docs(changelog): #8061 清理 status_note 里重复的 gateway_status 说明
连续修订留下一段标着 frontend_status 却在讲 gateway_status 语义的残渣,
标签与内容不符会误导下一个填这个字段的人。内容并入括注,标签改回对的。

mmg 的 frontend_status=not_required 原样保留:他们在自己代码库里查实
getOrderOperationLog(api/fleet/board.js:58) 全仓零调用方、src/views/fleet
无 operationType 命中,后端担心的白名单过滤层尚不存在。那是前端负责人
带证据的判定,比后端的推断硬。

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

20 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 8061 解除共用关系只处置本关系的成员——同一服务日上的非成员在途行不再被连带清空(行为修复,接口形状不变) admin wx(GIT) 修改接口 deployed verified not_required backend_status=deployed:PR #8067 squash 为 00930f5d6,2026-09-20 22:42 已部署到测试服 192.168.100.236,deploy-status.sh 复核落点 = 00930f5d6,Nacos 两实例(8087/8187)healthy=true。gateway_status=verified:2026-09-21 在测试服网关上以全新三户夹具(团期 2101690789438570497、服务日 2026-10-30)取得阳性与阴性两条对照——阳性:releasedSourceIds 恰为两个成员且库内真变 unassigned;阴性:非成员 C 的 assignment_status/vehicle_id/driver_id 解除前后逐一相等。两条缺一不可:只有阳性的话,「C 没变」与「解除没跑起来」观测相同,且阳性在修复前同样成立。详见正文「八、测试环境已验证」。(📌 gateway_status 这个字段记的是「网关验证有没有做过」,不是「网关路由要不要改」——先例见 2026-08 #5405/#5407:『测试环境部署和网关验证尚未执行,因此 backend_status、gateway_status 保持 pending』。本次不新增端点、不改路由形状,但那不构成填 not_required 的理由,填 verified 的理由是上面那次真实调用。)frontend_status:后端起初填 pending,理由是——请求/响应字段确实零增删,但派车操作日志时间线新增了 operation_type 取值 share_release_cleared——前端若按 operation_type 白名单过滤,这条会被静默丢掉,用户就看不到『车和司机是被哪次共用关系解除清掉的』,而那正是本次补留痕要解决的问题。⇒ 前端至少需要确认自己有没有这层过滤、必要时加上,这是动作不是知会,故 pending。另按指南,not_required 会禁止 mmg 回写 frontend_owner 等认领字段,填错反而挡住认领。mmg 前端实证 2026-09-20:fleet 操作日志读口 getOrderOperationLog(api/fleet/board.js:58)在全仓零调用方,src/views/fleet 无 operationType 命中——后端担心的「时间线按 operation_type 白名单过滤会静默丢 share_release_cleared」过滤层在前端不存在(该时间线 UI 尚未建),故前端零改动,翻 not_required。后续接入操作日志时间线时 operation_type 渲染必须包含 share_release_cleared(detail_json 全字段字符串 ID 透传)。share-groups 解除入口前端未接入,属 #7444 挂起域,同 #8051 口径。 2026-09-20 dev-v3

fleet: 解除共用关系只处置本关系的成员,同槽非成员行保留(工单 #8061 + #8051 衍生)

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

服务: hl-fleet-service PR: #8067(squash 00930f5d6,已合入 dev-v3) | Issue: #8061 日期: 2026-09-20 影响范围: 管理后台「团期配车」页解除车辆或司机共用关系这一个动作,及其对派车账本的影响


⚠️ 关键变化

三个可观测行为都变了,前端必须知道:

① 处置范围收窄

改动前:解除某一个共用关系,会把该车/该司机在整个资源日槽上的全部在途派车行一起软清成「待派车」,同槽的非成员行也被清。 改动后:只处置该关系的成员占用。同一车/同一司机、同一服务日上那些从不属于本关系的在途派车行,一行不动。

⇒ 响应里的 releasedSourceIds / pendingReassignSourceIds 会比以前短。同一个服务日、同一辆车上那些与本关系无关的在途派单,不再被连带清空。

典型场景:接送机的接、送一对。它们本来就该在同一天占同一辆车,但不一定属于同一个共用关系;现在解除一个关系不再误伤另一对。

② 错误码触发条件变了

  • 602111(KEEP_LEGAL 下仍有非法组合)与 602112(收口断言)现在只在「至少一侧是本关系成员」的组合上触发。
  • ⇒ 以前会因为两条互不相干的第三方派单存量冲突而整单失败的情况,现在不会再失败了。前端若有「解除失败请联系管理员」这类兜底文案,触发频率会下降。

③ 🔴 派单操作日志时间线新增 share_release_cleared 记录

新增操作类型值 share_release_cleared,中文标签「共用关系解除释放占用」。detail_json 包含以下字段(所有 ID 都是字符串,防雪花 ID 过 JS 掉精度):

字段 类型 说明
shareGroupId String 被解除的共用关系 ID
groupBatchId String 团 ID
serviceDate String 服务日期(YYYY-MM-DD)
resourceType String 资源维度:VEHICLE 或 DRIVER
resourceId String 车辆或司机 ID
releaseReason String 解除原因
survivorPolicy String 本次采用的处置策略(RELEASE / REASSIGN / KEEP_LEGAL)
vehicleIdBefore String 派单清之前挂的车辆 ID(解除后被清成 NULL)
driverIdBefore String 派单清之前挂的司机 ID(解除后被清成 NULL)

⚠️ 关键警告:如果前端在时间线上按 operation_type 白名单过滤,这条新记录会被静默丢掉。用户就看不到「车和司机是被哪次共用关系解除清掉的」——而这正是本次补留痕要解决的问题。


一、背景

原 #8051 与本单的关系

#8051 修了「解除共用关系会跨服务日、跨团软清在途派车行」这个 bug,已由 PR #8058(ac9efb735)修复并部署。

本单从 #8051 分出,因为在修复过程中又发现了一个同一服务日范围内的残留问题:RELEASE 策略清的是该资源日槽上全部在途 claim,与"是否属于本关系的成员"之间没有任何约束。结果:同槽上的非成员行也被一起清了,且同样静默。

两处文档说了对的话,实现没跟上

  • ShareSurvivorPolicy.RELEASE 的 javadoc:释放本关系的成员的占用
  • GroupDispatchShareController 的 @ApiOperation.notes:同样说释放本关系的成员

两处独立写成同一个意思——这代表意图,而不是"实现当前的行为"。现状不是「文档没跟上实现」,是实现从来没有对齐过契约。

实测误清 6 行真实数据

2026-09-20 14:04:12 解除共用关系 359915397639704576(服务日 2026-10-03、车辆 2065329519232720897)时,日志记的是 释放=[7 个派单 ID],经库内逐行核对:

派单 ID 服务日 团 是否成员 预期 实际
359887954036002816 2026-10-18 2101496576453275649 ❌ 不该动 被清
359887954396712960 2026-10-18 2101496576453275649 ❌ 不该动 被清
359888468400279552 2026-10-25 2101498606508994561 ❌ 不该动 被清
359888468916178944 2026-10-25 2101498606508994561 ❌ 不该动 被清
359888884127109120 2026-10-25 2101498606508994561 ❌ 不该动 被清
359896486663819264 2026-11-20 2099073597908647938 ❌ 不该动 被清
359915650245857280 2026-10-03 2101524283048263681 ✅ 该清 被清

6 行误清:横跨 3 个其它服务日、3 个其它团;现状全部 assignment_status=unassigned、vehicle_id/driver_id 置 NULL。


二、变更接口清单

# 接口 方法 路径 变更类型 说明
1 解除团期车辆/司机共用关系 DELETE /admin/fleet/group-dispatch/share-groups/{shareGroupId} 行为修复(字段、错误码、网关路由均不变) 处置范围从「整个资源日槽」收窄为「本关系的成员」;错误码触发条件同步收窄
2 — — — 日志侧新增 派单操作日志新增 share_release_cleared 记录类型

三、接口详情

1. 解除团期车辆/司机共用关系 DELETE /admin/fleet/group-dispatch/share-groups/{shareGroupId}

VO: ShareGroupReleaseRespVO(字段零增删改)

使用场景

管理后台「团期配车」页,车务点「解除共用」。解除同时要在同一事务内完成幸存占用的实际处置,survivorPolicy 决定怎么处置。

入参(本次无变化)

字段 位置 类型 必填 约束 说明
shareGroupId Path Long ✅ — 共用关系 ID
survivorPolicy Query String 业务必填 KEEP_LEGAL / REASSIGN / RELEASE 幸存占用处置策略;不传返业务码 602110

出参(本次无变化)

字段 类型 说明
shareGroupId String 共用关系 ID(雪花 ID,按字符串序列化)
status String 固定 RELEASED
survivorPolicy String 本次采用的处置策略
keptSourceIds List<String> 保留占用的来源 ID(本关系成员中被保留的)
releasedSourceIds List<String> 已释放占用的来源 ID(仅本关系成员,不含非成员)
pendingReassignSourceIds List<String> 需人工改派的来源 ID(仅本关系成员)

请求示例

DELETE /admin/fleet/group-dispatch/share-groups/{shareGroupId}?survivorPolicy=RELEASE HTTP/1.1
Host: api.test.1814.love:9443
Authorization: Bearer {token}

响应示例

{
  "code": 200,
  "message": "成功",
  "data": {
    "shareGroupId": "{shareGroupId}",
    "status": "RELEASED",
    "survivorPolicy": "RELEASE",
    "keptSourceIds": ["2101524548279238658"],
    "releasedSourceIds": ["360022177073991680"],
    "pendingReassignSourceIds": []
  },
  "success": true
}

空数据 / 降级响应

该资源日上仅剩 0-1 个存活 claim 时,三个清单都可能是空数组 []——这是正常终态,不是异常。

错误响应

{
  "code": 602111,
  "message": "保留合法共用失败,幸存成员之间不满足衔接规则: GROUP_DISPATCH#2101524548279238658 ↔ ASSIGNMENT#360022539151478784",
  "data": null,
  "success": false
}

业务边界

  • 鉴权:需管理后台已登录且具备 fleet:group-dispatch:write 权限,未登录/无权限按网关与全局鉴权规则处理。
  • 幂等性:关系一旦解除即置 RELEASED;重复发起 DELETE 会拿到 602108。
  • 失败零写入:602111 / 602112 都在同一事务内回滚——关系不标 RELEASED,占用状态不变。
  • survivorPolicy 必须传值;不传返 602110,不是 400。
  • 🔴 已知未覆盖边界(不在本次修复范围):同一资源同一服务日上的非成员在途行仍会被 RELEASE 一并软清且同样静默——处置逻辑不区分"共用关系的成员"与"路过的其它派车行";最典型场景是接送机的接、送一对。已在工单 #8061 本单纳入。

错误码(本次不新增、语义不改)

code 含义
602108 共用关系不存在或已解除
602110 未指定处置策略
602111 KEEP_LEGAL 下本关系的幸存成员之间存在非法组合;整请求回滚
602112 收口断言不成立:处置后账本里至少一侧是本关系成员的组合中仍有非法;回滚

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

本节只写后端接受/拒绝 payload 的规则。接口契约本身没有变化——本节写的是既有契约,帮消费方确认自己一直调对了。

✅ 正确 / ❌ 错误 payload 对照

场景 payload 结果
✅ 释放本关系全部幸存成员占用 DELETE .../share-groups/{id}?survivorPolicy=RELEASE 200,成员占用全释,同槽非成员不动
✅ 仅在成员间两两合法时整槽保留 DELETE .../share-groups/{id}?survivorPolicy=KEEP_LEGAL 成员间有冲突时 602111 拒绝,整请求回滚
✅ 真释放冲突成员并标记待改派 DELETE .../share-groups/{id}?survivorPolicy=REASSIGN 200,释放冲突成员;成员与非成员冲突时成员让位
❌ 不传 survivorPolicy DELETE .../share-groups/{id}(无查询参数) 602110,不是 400
❌ shareGroupId 指向不存在或已解除的关系 任意 survivorPolicy 602108

切换状态时的必要动作

  • survivorPolicy 必须始终传值——不传拿到 602110,前端应按此逻辑引导用户补选。
  • 若修改派单分配方案,必须显式传递三选一的大写字面量,不要自行拼接或做大小写转换。

五、数据库行为

  • 本服务日范围内:解除共用关系后,该关系名下、在这一个服务日上仍存活的成员派车行会被置为「待派车」,车辆与司机清空(RELEASE / REASSIGN 都触发;KEEP_LEGAL 冲突时整请求回滚)。
  • 修复点(本次变化):同一资源、同一服务日上那些不属于本关系****的派车行不再受影响——即便它们挂在同一辆车/同一名司机上。
  • 新增操作日志:软清每一条成员行时,往 fleet_assignment_operation_log 追加一行 share_release_cleared 记录,带 shareGroupId / groupBatchId / vehicleIdBefore / driverIdBefore 等字段(见下节)。

六、边界行为

  • 未登录 → 401(网关拦截)
  • 无 fleet:group-dispatch:write 权限 → 403
  • shareGroupId 不存在或已解除 → 602108(不是 404)
  • 未传 survivorPolicy → 602110(不是 400)
  • survivorPolicy 不是三个合法字面量之一 → 不报错,落入隐式默认分支
  • 本接口不依赖外部服务(不查 Feign、不查 MQ),没有"下游降级"路径
  • 🔴 本次修复范围之外的已知缺陷:同一资源同一服务日上的非成员在途行仍会被 RELEASE 一并软清且同样静默,本次不改

六.5、枚举 / 数据字典

survivorPolicy(ShareSurvivorPolicy)

所属字段: survivorPolicy(Query 入参)| 类型: String

值 中文 说明
RELEASE 全部释放 释放本关系的全部幸存成员的占用,置「待派车」;同槽非成员不动
KEEP_LEGAL 仅合法保留 本关系幸存成员间两两全合法时整槽保留;有冲突则 602111 拒绝回滚
REASSIGN 释放并待改派 真释放冲突成员的占用,派单置「待改派」;成员与非成员冲突时成员让位

operation_type(AssignmentOperationTypeEnum)

新增值 share_release_cleared:共用关系解除释放占用

值 中文 说明
share_release_cleared 共用关系解除释放占用 解除共用关系时,该派单作为成员被释放占用并置「待改派」;详见 detail_json

detail_json 结构(JSON 字段类型):

{
  "shareGroupId": "359915397639704576",
  "groupBatchId": "2101524283048263681",
  "serviceDate": "2026-10-03",
  "resourceType": "VEHICLE",
  "resourceId": "2065329519232720897",
  "releaseReason": "共用关系解除",
  "survivorPolicy": "RELEASE",
  "vehicleIdBefore": "G",
  "driverIdBefore": "D001"
}

六.6、修改前后对比

字段级对比

字段 改前 改后
(无)请求 / 响应字段 不变 不变——本次未增删改任何字段

行为级对比

行为 改前 改后
RELEASE 处置的派车行范围 该车/该司机同一服务日上全部在途行 仅本关系的成员
releasedSourceIds 里可能出现非成员行 会 不会
同槽非成员在途行是否被清成「待派车」 会,且无任何信号 不会
错误码 602111 / 602112 的判定范围 整槽全部 claim 仅「至少一侧是本关系成员」的组合
派单操作日志 无 share_release_cleared 记录 新增记录,包含 vehicleIdBefore / driverIdBefore

六.7、影响评估

  • 是否破坏向后兼容: 否——请求/响应字段、错误码、网关路由全部不变。
  • 前端是否必须同步上线: 否——管理后台无需改任何代码;但操作日志展示侧需要注意新字段。
  • 前端 workaround 清理点: 无新增 workaround。但请转告使用方——测试环境中那些跨服务日、跨团、从不属于本关系的派单,如果曾因为解除共用关系而被误清,不再会发生这种事了。
  • 给 mmg 的知会重点:
    • 「解除共用关系」这个动作的影响范围变了——同一车/司机同一服务日上的非成员行不再被波及。
    • 操作日志时间线上会看到新的 share_release_cleared 记录——必须把这条记录类型加进白名单,否则用户看不到派单是被哪次共用关系解除清掉的。
  • 存量处理:无需任何 SQL 回滚。测试库中的误清行正常通过改派写口恢复。

七、不影响范围

  • 仅影响: 管理后台「团期配车」页解除车辆/司机共用关系这一个动作的副作用范围
  • 零影响:
    • 请求/响应字段结构、错误码(全部不变,前端无需改代码)
    • 网关路由配置(既有路由无需改动)
    • 确认共用关系 POST .../batches/{groupBatchId}/share-groups、查询共用关系 GET .../batches/{groupBatchId}/share-groups 两个端点
    • 同一资源同一服务日上非成员在途行的处置口径(仍是旧行为,已在本单纳入)

八、测试环境已验证

部署:fleet 于 2026-09-20 22:42 部署到测试服 192.168.100.236,落点 00930f5d6(deploy-status.sh 复核),Nacos 两实例(8087 / 8187)healthy=true。

网关实测(2026-09-21,真实请求 + 库回读)

夹具:全新团期 2101690789438570497,服务日 2026-10-30,共用车 G 2065329515961163778;三户 A / B / C 与团车行均实占该车该日,其中 A、B 是共用关系 360081477972660224 的成员,C 与团车行不是成员。

DELETE /admin/fleet/group-dispatch/share-groups/360081477972660224?survivorPolicy=RELEASE
→ 200
{"data":{"status":"RELEASED","survivorPolicy":"RELEASE",
  "keptSourceIds":["2101691129806340098","360081478538891264"],
  "releasedSourceIds":["360081478098489344","360081478333370368"]}}

① 阳性对照(证明解除真的执行了):releasedSourceIds 恰为成员 A 360081478098489344 与 B 360081478333370368;库回读两行 assignment_status: assigned → unassigned,vehicle_id / driver_id → NULL。

② 阴性对照(本次修复的核心断言):非成员 C 360081478538891264 三字段解除前后逐一相等,且不在 releasedSourceIds 里:

assignment_status vehicle_id driver_id
解除前 assigned 2065329515961163778 2065272147277758465
解除后 assigned 2065329515961163778 2065272147277758465

⚠️ 为什么必须两条一起看:只有 ① 的话,「C 没变」与「解除根本没跑起来」在观测上完全相同;而 ① 本身在修复前也成立(旧实现同样会释放成员,只是顺手把 C 也清了)——单看 ① 是一个在「修好了」和「没修」两个世界里都为真的读数。

后端单元测试 / 集成测试

  • GroupDispatchShareSurvivorServiceTest:15 条(原 11,+4)
  • GroupDispatchShareReleaseDayScopeIntegrationTest:4 条(原 2,+2,H2 真 Flyway DDL + 真 Mapper)
  • 变异证明:把处置范围回滚成「清整槽」后 5 条红且全部是新增用例,既有 71 条全绿;复原后全绿

十、相关文档

关联 / 联系人

链接

联系人

  • 后端负责人: @wx