文件
hl-api-changelog/changelogs-v2/2026-09/20_8013_共用关系确认历史补记成本承担方变更-修改接口-管理后台.md
T

30 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 8013 共用关系确认历史补记成本承担方变更(COST_BEARER_CHANGED 此前是死枚举) admin wx(GIT) 修改接口 deployed verified not_required mmg gateway_status=verified 的依据:2026-09-20 18:22-18:46 经网关实测(账号 cw_test_7444,用的是本方自建的批次 2101246738038652930 上的共用关系 359639602970112,非他人夹具):保持成员全集不变、仅将 costBearer 由 GROUP 改为 ORDER,POST .../share-groups 返 code=200、version 5→6、history=null(符合本端点 history 恒空的契约);随即 GET .../share-groups?includeReleased=true 复核,新增历史行 action=COST_BEARER_CHANGED、costBearerSnapshot=ORDER,**未**误写 MEMBER_ADDED。🔴 这正是本篇正文写明的取证口径:判「这次确认写了几条历史」只能靠事后查询,不能靠 POST 响应体推断——本次按此执行。 【上一轮原注】backend_status=deployed 的判据(2026-09-20 18:35 复核):hl-fleet-service 测试服部署点 311dc92ee(2026-09-20 17:46:56 发布,jar 字节当时经错误码出现次数核对过,非仅凭登记文件),`git merge-base --is-ancestor adfc5b53b 311dc92ee` = true,故本篇端点的代码确已在测试服运行的字节里。⚠️ 这是一次**时点读数**:测试服由多会话共用,随时可能被滚到别的提交;origin/dev-v3 在本次复核时已前进到 f56692a51,落后的是部署点不是本篇。 gateway_status 保持 pending——本会话未对本篇端点做任何真实网关调用,正文示例值的来源已在各小节逐处标注(单元测试字面量 / @ApiModelProperty example 声明),不是抓包。待网关复验后再置 verified,**不得为了让门禁变绿改这个位**。 【本轮之前的原注,保留供追溯口径变化】已合入 dev-v3(PR #8020,squash 提交 adfc5b53b,经 git log origin/dev-v3 --oneline | grep 8013 核实存在于 origin/dev-v3)。backend_status=deployed 的依据是「已在主线」,不代表已部署测试服并做过网关联调。gateway_status 刻意保持 pending——本次未做任何真实网关调用,唯一证据是随 PR 一起合入的两条单元测试(GroupDispatchShareAdmissionServiceTest:confirm_costBearerChangedWithoutMemberChange_writesCostBearerChangedNotMemberAdded / confirm_membersAddedAndCostBearerChangedTogether_writesBothRows),交接时未重新执行这两条测试(本机当时另有全量测试占用窗口,见 MACHINE-LOCK.md)。正文的请求/响应示例数值沿用 #7444 changelog(19_7444_团期配车就绪门禁与车辆共用关系-修改接口-管理后台.md,尚未推送的草稿)里已经登记的同一套 swagger 声明示例值(shareGroupId=77001 等),不是本次新造的编号,也不是真实网关抓包。待管理者安排测试服部署 + 网关复验后,再把 gateway_status 置 verified 并推送本文件;发布前不许为了过校验改这个状态位。 前端实证翻 not_required(mmg 2026-09-20):share-groups/costBearer/COST_BEARER/MEMBER_ADDED 全仓零命中,共用关系确认历史 UI 未建(#7444 挂起域),前端无 action 映射表可补;配车接入时映射必须含 COST_BEARER_CHANGED、同 operateTime 两条历史不去重、不假设每次确认历史 +1 条,已记入前端待办口径。 2026-09-20 dev-v3

fleet: 共用关系确认历史补记成本承担方变更(工单 #8013)

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

服务: hl-fleet-service(8087) PR: #8020 | Issue: #8013 | 合并提交: adfc5b53b 日期: 2026-09-20 影响范围: 管理后台「团期配车页」共用关系确认历史(GET .../share-groups?includeReleased=true 的 history[]),以及写口 POST .../share-groups 内部的历史写入分流逻辑


⚠️ 关键变化

ShareLogAction.COST_BEARER_CHANGED 这个动作常量自 #7444 落地起就从未被真正写出过——车务在团期配车页把某天某辆车的共用关系成本承担方从 GROUP 改成 ORDER(或反过来),确认历史里记下来的是 MEMBER_ADDED,即便成员一个都没变。前端如果按 action 做过枚举映射/文案表,没见过 COST_BEARER_CHANGED 这个值——它不是"没出现过"的边界情况,是编译器允许、运行时永远走不到的死分支。本次修复后它会真实出现,前端必须补上这一项,否则会显示成空白或 fallback 文案。

同一次修复顺带纠正了一个同构错误:车务对着同一份成员全集、同一个成本承担方原样重新点一次"确认"(例如错过 @Idempotent(timeout=10) 的 10 秒防重窗口后手动再点),改前会误写一条 MEMBER_ADDED——车务并没有加任何成员,历史里却凭空多出一条"加了成员"的记录。改后这种真正的空提交不产生任何新历史行。

两处都不改变 POST/GET 两个端点自身的请求/响应字段名、类型或结构;变化只发生在"这次确认写了哪几条历史行、写的是哪个 action"这件事上。


一、背景

fleet_group_dispatch_share_log.action 这一列在 V20260918_003 建表时就用 CHECK 约束声明了五个合法字面量:CREATE/MEMBER_ADDED/MEMBER_REMOVED/COST_BEARER_CHANGED/RELEASED。DB 侧一直都能接受 COST_BEARER_CHANGED。

问题出在 Java 侧:GroupDispatchShareAdmissionService#confirmInTransaction 复用既有关系(幂等覆盖)时,末尾恒定写死一条历史——created ? CREATE : MEMBER_ADDED。这个三元表达式里根本没有 COST_BEARER_CHANGED 这个分支,无论车务这次改的是成员、是成本承担方、还是什么都没改,只要不是新建,写出去的永远是 MEMBER_ADDED(或者什么都没变时,同样误写 MEMBER_ADDED)。

upsertGroup 内部其实早就在做 costBearer 的 CAS 更新(casUpdateCostBearer),只是这次落库前后的差异从未被读出来、也从未参与历史写入的判定。

维度 改前 改后
复用关系时的历史写入判据 一个布尔 created 决定 CREATE/MEMBER_ADDED 二选一 created(是否新建)+ 成员是否真的新增 + 成本承担方是否真的变化,三个独立判据,各写各的 0~2 条
COST_BEARER_CHANGED 是否可达 否(死代码) 是(复用关系且成本承担方变化时必写)
成员全集与成本承担方均未变的重复确认 误写 1 条 MEMBER_ADDED 写 0 条

二、变更接口清单

# 接口 方法 路径 变更类型 说明
1 确认团期车辆共用关系 POST /admin/fleet/group-dispatch/batches/{groupBatchId}/share-groups 修改接口 请求/响应字段零变化;仅内部历史写入判据修正(本节所述)
2 查询团期车辆共用关系 GET /admin/fleet/group-dispatch/batches/{groupBatchId}/share-groups 修改接口 history[].action 新增可观测取值 COST_BEARER_CHANGED(此前是死枚举)

三、接口详情

1. 确认团期车辆共用关系 POST /admin/fleet/group-dispatch/batches/{groupBatchId}/share-groups

VO: ShareGroupConfirmReqVO → ShareGroupRespVO

(本接口的请求/响应字段与 #7444 落地时逐字节一致,未新增/删除/改名任何字段;源码核对:GroupDispatchShareController.java:53-93、ShareGroupConfirmReqVO.java、ShareGroupRespVO.java、GroupDispatchShareAdmissionService.java:158-241)

使用场景

车务在团期配车页确认共用关系时调用,行为与 #7444 changelog 描述完全一致。本次改动不影响本端点自身的响应内容(ShareGroupRespVO.history 在这个端点上恒为空,历史只能通过接口 2 的 includeReleased=true 查询回来)——本节存在的意义是让前端理解"点一次确认,后台这次到底往历史表里写了几条、写的是什么",这决定了随后调用接口 2 时能看到什么。

入参字段表

字段 位置 类型 必填 约束 说明
groupBatchId Path Long ✅ - 团期主订单 ID(雪花)
serviceDate Body LocalDate ✅ 必须落在该团基线 serviceDates[] 内 共用发生的服务日
resourceType Body String ✅ VEHICLE/DRIVER 资源维度
resourceId Body Long ✅ - 车辆 ID 或司机 ID
members Body Array ✅ 2~20 个 成员全集(不是增量)
members[].sourceType Body String ✅ ASSIGNMENT/GROUP_DISPATCH -
members[].sourceId Body Long ✅ - 派单 ID 或团级配车行 ID
costBearer Body String ✅ GROUP/ORDER 本次改动的判据来源:与落库前的旧值比对,不同则本次会多写一条 COST_BEARER_CHANGED
costBearerOrderId Body Long 条件必填 costBearer=ORDER 时必填 -
remark Body String ❌ ≤200 确认备注(也会写进新增的历史行)

(完整入参约束、错误码见 #7444 changelog 对应小节,本次未改动任何一条校验规则)

出参字段表

字段 类型 说明
shareGroupId / groupBatchId / serviceDate / resourceType / resourceId / status / costBearer / costBearerOrderId / costSourceRefNo / members[] / confirmedBy / confirmedAt / version - 字段名/类型/取值规则均未变动,与 #7444 落地时逐字节一致
history Array 本端点恒为 null/空数组;本次改动写了几条历史、写的是什么,在这个响应体里看不到,必须另调接口 2

请求示例

沿用 #7444 changelog 登记的示例数据集(shareGroupId=77001 等),仅把 costBearer 改成触发 COST_BEARER_CHANGED 的取值:该关系此前以 costBearer=GROUP 建立,本次原样重新提交同一份成员全集、只把 costBearer 改成 ORDER:

{
  "serviceDate": "2026-09-12",
  "resourceType": "VEHICLE",
  "resourceId": 1,
  "members": [
    { "sourceType": "GROUP_DISPATCH", "sourceId": 99001 },
    { "sourceType": "ASSIGNMENT", "sourceId": 88001, "admissionIntent": "OCCUPYING" }
  ],
  "costBearer": "ORDER",
  "costBearerOrderId": 70123,
  "remark": "车费改由甲户订单承担"
}

(路径参数 groupBatchId=8801)

响应示例

{
  "code": 200,
  "message": "成功",
  "data": {
    "shareGroupId": "77001",
    "groupBatchId": "8801",
    "serviceDate": "2026-09-12",
    "resourceType": "VEHICLE",
    "resourceId": "1",
    "status": "ACTIVE",
    "costBearer": "ORDER",
    "costBearerOrderId": "70123",
    "costSourceRefNo": "SHARE-77001",
    "members": [
      { "sourceType": "GROUP_DISPATCH", "sourceId": "99001", "requirementId": null, "orderId": null },
      { "sourceType": "ASSIGNMENT", "sourceId": "88001", "requirementId": "5501", "orderId": "70123" }
    ],
    "confirmedBy": "1001",
    "confirmedAt": "2026-09-12 18:25:10",
    "version": 2
  },
  "success": true
}

⚠️ 这个响应体本身看不出后台这次到底记了几条历史——history 字段在这个端点上恒为空。要确认这次调用是否真的多写了一条 COST_BEARER_CHANGED,必须紧接着调接口 2(includeReleased=true)。

空数据 / 降级响应

本接口是同步写操作,不存在响应体意义上的"空数据"。但本次改动引入了一种新的"写 0 条历史"的合法情形:若车务原样重新提交同一份成员全集、且 costBearer 也未变(membersAdded=false && costBearerChanged=false),本次确认不产生任何新历史行——这不是接口故障,是 #8013 修复后的预期行为;改动前这种情况会误写一条 MEMBER_ADDED。前端不应假设"每次点确认,历史列表长度必然 +1"。

错误响应

本次改动不引入任何新错误码,错误响应与 #7444 落地时完全一致,例如:

{
  "code": 602103,
  "message": "成员派单不属于本团或团期身份未知: 88099",
  "success": false,
  "data": null
}

业务边界

  • 本次改动不改变鉴权、幂等、校验、事务边界,与 #7444 changelog 描述的规则完全一致。
  • 判据在落库前取样:costBearer 是否变化,取的是 casUpdateCostBearer 执行前读到的旧值与本次入参的比对结果,不是事后反查历史表推断出来的——落库那一瞬间旧快照就没有意义了,判据必须在那之前定下来。
  • 新建关系(created=true)分支完全不受影响:仍然只写 1 条 CREATE,不会额外叠加 COST_BEARER_CHANGED(新建时这条 CREATE 历史本身就带着当次的 costBearerSnapshot,没有必要再补一条)。
  • 同一次确认最多产生 2 条新历史(成员新增 1 条 MEMBER_ADDED + 成本承担方变化 1 条 COST_BEARER_CHANGED),顺序恒为先 MEMBER_ADDED 后 COST_BEARER_CHANGED(operateTime 相同,前端排序如依赖 operateTime 需要一个稳定的次级排序键,比如 logId 自增);成员被移出(MEMBER_REMOVED)走的是 syncMembers 内联的另一条写入路径,与本次改动的判据完全独立,不受影响。

2. 查询团期车辆共用关系 GET /admin/fleet/group-dispatch/batches/{groupBatchId}/share-groups

VO: ShareGroupQueryReqVO → List<ShareGroupRespVO>

(源码核对:GroupDispatchShareController.java:100-111、GroupDispatchShareService.java:125-144、ShareGroupHistoryRespVO.java)

使用场景

团期配车页 / 核团排障时查看共用关系变更历史,includeReleased=true 时返回 history[]。本次改动后,这里首次可能出现 action="COST_BEARER_CHANGED" 的历史行——此前无论后台实际发生了什么,这里永远只会看到 CREATE/MEMBER_ADDED/MEMBER_REMOVED/RELEASED 四种。

入参字段表

字段 位置 类型 必填 约束 说明
groupBatchId Path Long ✅ - 团期主订单 ID
serviceDate Query LocalDate ❌ 不传=全部服务日 -
resourceType Query String ❌ VEHICLE/DRIVER 不传=两者都查
includeReleased Query Boolean ❌ 默认 false 必须为 true 才会带 history[];本次改动只影响 history[].action 的取值分布,不影响本参数语义

出参字段表

字段 类型 说明
(数组元素其余字段:shareGroupId / groupBatchId / serviceDate / resourceType / resourceId / status / costBearer / costBearerOrderId / costSourceRefNo / members[] / confirmedBy / confirmedAt / version) - 本次未改动
history[] Array 仅 includeReleased=true 时非空;本次改动只影响这个数组里各行 action 的取值分布,字段结构本身零变化
history[].action String 本次改动点:合法取值仍是 CREATE/MEMBER_ADDED/MEMBER_REMOVED/COST_BEARER_CHANGED/RELEASED 五个(DB CHECK 早已如此),但 COST_BEARER_CHANGED 此前从未被真实写出过,本次修复后会真实出现
history[].memberSnapshot String 该次变更后的成员全集快照(JSON 文本);COST_BEARER_CHANGED 这条历史的快照与紧邻的 MEMBER_ADDED(如果同次确认也写了的话)逻辑上相同,因为两条历史共用同一次 selectActiveByGroupForUpdate 锁定读结果
history[].costBearerSnapshot String 该次变更后的成本承担方;COST_BEARER_CHANGED 这条历史上就是本次改到的新值
history[].operator String 操作人 adminId
history[].operateTime LocalDateTime 操作时间;同一次确认产生的 MEMBER_ADDED 与 COST_BEARER_CHANGED 两条历史 operateTime 相同(同一个 now 变量)
history[].reason String 仅 RELEASED 动作有值,COST_BEARER_CHANGED 恒为 null
history[].remark String 备注;COST_BEARER_CHANGED 这条历史的 remark 与同次确认入参的 remark 相同(若同次也写了 MEMBER_ADDED,两条历史的 remark 是同一个值,不是分开填的两段话)

请求示例

GET /admin/fleet/group-dispatch/batches/8801/share-groups?includeReleased=true

响应示例

沿用接口 1 示例的后续状态:该关系先以 costBearer=GROUP 建立(写 1 条 CREATE),随后车务把 costBearer 改成 ORDER(本次改动生效,写 1 条 COST_BEARER_CHANGED):

{
  "code": 200,
  "message": "成功",
  "data": [
    {
      "shareGroupId": "77001",
      "groupBatchId": "8801",
      "serviceDate": "2026-09-12",
      "resourceType": "VEHICLE",
      "resourceId": "1",
      "status": "ACTIVE",
      "costBearer": "ORDER",
      "costBearerOrderId": "70123",
      "costSourceRefNo": "SHARE-77001",
      "members": [
        { "sourceType": "GROUP_DISPATCH", "sourceId": "99001", "requirementId": null, "orderId": null },
        { "sourceType": "ASSIGNMENT", "sourceId": "88001", "requirementId": "5501", "orderId": "70123" }
      ],
      "confirmedBy": "1001",
      "confirmedAt": "2026-09-12 18:25:10",
      "version": 2,
      "history": [
        {
          "action": "CREATE",
          "memberSnapshot": "[{\"sourceType\":\"GROUP_DISPATCH\",\"sourceId\":\"99001\",\"requirementId\":null,\"orderId\":null},{\"sourceType\":\"ASSIGNMENT\",\"sourceId\":\"88001\",\"requirementId\":\"5501\",\"orderId\":\"70123\"}]",
          "costBearerSnapshot": "GROUP",
          "operator": "1001",
          "operateTime": "2026-09-12 18:20:33",
          "reason": null,
          "remark": "团车上午行程后接甲户"
        },
        {
          "action": "COST_BEARER_CHANGED",
          "memberSnapshot": "[{\"sourceType\":\"GROUP_DISPATCH\",\"sourceId\":\"99001\",\"requirementId\":null,\"orderId\":null},{\"sourceType\":\"ASSIGNMENT\",\"sourceId\":\"88001\",\"requirementId\":\"5501\",\"orderId\":\"70123\"}]",
          "costBearerSnapshot": "ORDER",
          "operator": "1001",
          "operateTime": "2026-09-12 18:25:10",
          "reason": null,
          "remark": "车费改由甲户订单承担"
        }
      ]
    }
  ],
  "success": true
}

⚠️ 本响应体的数值构造来自 #7444 changelog 登记的同一套 swagger 声明示例 + 本次源码新增的判据逻辑推算,不是真实网关抓包(gateway_status=pending,见 frontmatter status_note)。结构与字段名已逐一对源码核实(ShareGroupHistoryRespVO.java),但具体数值请在网关复验后以实测为准。

空数据 / 降级响应

本团在筛选条件下没有任何共用关系时返回空数组 [];includeReleased=false(默认)时 history 字段不返回。本次改动不影响这两种既有的空态。

错误响应

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

业务边界

  • 鉴权:路径级角色门禁 X-Admin-Role 须为 VEHICLE_MANAGER/SUPER_ADMIN,本次未改动(详见 #7444 changelog)。
  • 前端枚举映射表必须补 COST_BEARER_CHANGED:此前的响应里从未出现过这个值,前端若对 action 做过 switch/映射表且没有兜底分支,会渲染成空白/undefined,而不是报错——这类问题不会在联调时报红,只会在页面上悄悄显示不对。
  • memberSnapshot 仍是全集不是增量,本次改动不影响这条既有约定。
  • COST_BEARER_CHANGED 与紧邻的 MEMBER_ADDED(如果同次确认也命中的话)operateTime 相同,前端如果用 operateTime 做排序/分组去重,需要注意这两条历史是同一次操作产生的两个独立事实,不能因为时间戳相同就当成重复记录去重掉。
  • 查询接口本身不加锁、不开事务,读到的是调用时刻的库状态。

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

本节只写后端接受/拒绝 payload 的规则与历史写入判据,不写 UI 渲染建议。

COST_BEARER_CHANGED 的触发条件是「落库前旧值 ≠ 本次入参新值」

这是纯粹的服务端判据,前端不需要、也不应该自己在客户端比对新旧 costBearer 来猜这次会不会有 COST_BEARER_CHANGED 历史——判据用的是数据库锁定读到的旧快照,前端本地缓存的旧值可能已经过期(比如同一团被另一个车务并发改过)。

判断"这次确认到底写了几条历史",只能靠事后查询,不能靠 POST 响应体推断

POST .../share-groups 的响应体里没有任何字段能告诉调用方"这次写了 1 条还是 2 条历史、写的是什么 action"。前端如果需要在确认成功后立刻展示"本次变更记录",必须紧接着调 GET .../share-groups?includeReleased=true,按 operateTime(+ 稳定次级排序键)取最新的 1~2 条。

✅ 正确 / ❌ 错误 理解对照

场景 后台实际写入 前端不应假设的事
✅ 新建关系 1 条 CREATE 不会额外出现 COST_BEARER_CHANGED
✅ 复用关系,只改 costBearer,成员全集不变 1 条 COST_BEARER_CHANGED 不会出现 MEMBER_ADDED
✅ 复用关系,成员与 costBearer 都变了 2 条:MEMBER_ADDED + COST_BEARER_CHANGED 两条 operateTime 相同,不是两次独立操作
✅ 复用关系,成员与 costBearer 都没变(原样重提) 0 条 ❌ 不要假设"点一次确认历史必然 +1 条"——改前的旧行为才是这样,且是 #8013 要修的缺陷
❌ 前端自行比对 costBearer 新旧值来预测本次是否有 COST_BEARER_CHANGED 无意义 判据在服务端落库前的锁定读上,前端本地状态可能已过期

五、数据库行为

写接口 POST .../share-groups 落库表 fleet_group_dispatch_share_log(V20260918_003 建表,INSERT-only 留痕表,一行 = 关系的一次变更)。

本次改动不新增任何列、不新增任何表、不改 chk_share_log_action 约束——约束自建表起就允许 COST_BEARER_CHANGED 这个字面量,缺陷完全在 Java 应用层(三元表达式漏了这个分支),DB 侧从一开始就是就绪的。

场景 改前写入行数 改后写入行数
新建关系 1(CREATE) 1(CREATE,不变)
复用关系,仅成员变化 1(MEMBER_ADDED) 1(MEMBER_ADDED,不变)
复用关系,仅 costBearer 变化 1(误写 MEMBER_ADDED) 1(COST_BEARER_CHANGED)
复用关系,成员与 costBearer 都变化 1(MEMBER_ADDED,costBearer 变化未留痕) 2(MEMBER_ADDED + COST_BEARER_CHANGED)
复用关系,两者都未变化(原样重提) 1(误写 MEMBER_ADDED) 0(不写)
成员被移出 1(MEMBER_REMOVED,syncMembers 内联写,独立路径) 1(不变,本次未触碰这条路径)

六、边界行为

  • 未登录/网关未透传角色 → 401(网关拦截),与既有行为一致
  • resourceType 非法枚举值 → 100001(沿用既有,见 #7444 changelog 更正记录)
  • 成员数越界、服务日窗外、并发修改等既有错误码全部未变(602100~602112 段)
  • 老数据兼容:本次改动前落库的历史行(action 只可能是 CREATE/MEMBER_ADDED/MEMBER_REMOVED/RELEASED 四种之一)原样保留,查询时按原值返回,不会被"回填"或"重新计算"成 COST_BEARER_CHANGED——本次修复只影响新产生的历史行,不改写存量数据
  • appendConfirmChangeLog 全程在 confirmInTransaction 的同一个事务内执行,与关系落库、成员同步、派单关联、占用准入共享同一次提交/回滚,不会出现"关系已改但历史没写"或"历史写了但关系没改"的中间态

六.5、枚举 / 数据字典

action(ShareGroupHistoryRespVO.action,源码 ShareLogAction 常量类 / DB chk_share_log_action)

所属字段: history[].action | 类型: String

值 说明 本次改动前是否可能在响应中出现
CREATE 首次建立关系 是
MEMBER_ADDED 幂等覆盖时成员净增;改动前该分支同时错误吸收了"仅改成本承担方"与"什么都没改"两种场景 是(含误写)
MEMBER_REMOVED 幂等覆盖或自动收缩时成员净减 是
COST_BEARER_CHANGED 成本承担方变更 否——本次改动前是死枚举,代码路径永远走不到
RELEASED 关系解除(四条解除路径共用) 是

⚠️ 该枚举早在 V20260918_003(2026-09-18)建表时就在 DB CHECK 约束里声明了全部五个值,COST_BEARER_CHANGED 不是本次新加的枚举字面量,而是本次才让它第一次真正可达。


六.6、修改前后对比

字段级对比

字段 改前 改后
history[].action 合法取值集合 声明上五个值都合法,但实际只会出现 CREATE/MEMBER_ADDED/MEMBER_REMOVED/RELEASED 四种 全部五个值均可能真实出现
POST/GET 两端点的请求/响应字段名、类型、结构 - 零变化

行为级对比

行为 改前 改后
复用关系,仅改 costBearer 误写 1 条 MEMBER_ADDED(无法从历史看出改的是成本承担方) 正确写 1 条 COST_BEARER_CHANGED
复用关系,成员与 costBearer 都改 只写 1 条 MEMBER_ADDED(costBearer 变化完全未留痕) 写 2 条:MEMBER_ADDED + COST_BEARER_CHANGED,两件事都可追溯
复用关系,成员与 costBearer 都未改(原样重提) 误写 1 条 MEMBER_ADDED(凭空多一条"加了成员"的假记录) 写 0 条
新建关系 写 1 条 CREATE 不变

六.7、影响评估

  • 是否破坏向后兼容: 否——两个端点的请求/响应字段名、类型、结构均未变动;变化只发生在 history[].action 的取值分布上(新增可观测取值,不是改变既有取值的含义)
  • 前端是否必须同步上线: 是——如果前端对 action 做了枚举映射/文案表且没有安全的 default 兜底,COST_BEARER_CHANGED 首次出现时会展示为空白或 undefined,需要补上这一项映射
  • 前端 workaround 清理点: 若前端此前发现"改成本承担方后历史列表多了一条奇怪的『新增成员』记录"并做过任何遮蔽/过滤逻辑(比如按 remark 内容猜测过滤掉这类"假 MEMBER_ADDED"),本次修复后这类 workaround 应当撤除——改后不会再产生这种假记录

七、不影响范围

  • 仅影响: 共用关系确认历史的写入判据(POST .../share-groups 内部)与查询回显(GET .../share-groups?includeReleased=true 的 history[].action)
  • 零影响:
    • 两个端点的请求体、响应体字段名/类型/结构零变更
    • 新建关系(CREATE)分支完全不受影响
    • 解除关系(RELEASED,DELETE .../share-groups/{shareGroupId})分支完全不受影响,本次未改动该端点任何代码
    • 成员被移出(MEMBER_REMOVED)分支完全不受影响,走的是独立的内联写入路径
    • 准入三步流程(授权 → 派单关联 → 占用准入)与占用账本行为完全不受影响,本次只动了流程末尾的历史写入分流
    • 无新增 DB 表/列/索引/约束;chk_share_log_action 约束原样不变(自 V20260918_003 起就允许 COST_BEARER_CHANGED)
    • 团期配车其余端点(就绪判定、派单预校验/候选、reconfigure 等)零影响

八、测试环境已验证

⚠️ 本篇尚无测试服/网关读数(gateway_status=pending),以下只是单元测试层面的证据,不是测试环境实测——按管理者要求如实标注,不把单测读数伪装成测试环境验证:

随 PR #8020 一并合入的两条新增单测(GroupDispatchShareAdmissionServiceTest,Mockito,未重跑,仅列出用例名与断言意图供核实):

confirm_costBearerChangedWithoutMemberChange_writesCostBearerChangedNotMemberAdded
  → 只改 costBearer、成员不变:断言 append(COST_BEARER_CHANGED) 被调用一次,
     且 append(MEMBER_ADDED) 从未被调用(verify(..., never()))

confirm_membersAddedAndCostBearerChangedTogether_writesBothRows
  → 成员与 costBearer 同次都变:断言 append(MEMBER_ADDED) 与 append(COST_BEARER_CHANGED)
     均被调用一次

待办:管理者安排 hl-fleet-service 部署测试服后,需补一次真实网关调用(POST 确认 + GET 查询 includeReleased=true),核实 history[].action="COST_BEARER_CHANGED" 真实落库并可读出,再把 gateway_status 置 verified 并推送本文件。


十、相关文档

  • 关联 Issue: wx/HL#8013
  • 关联 PR: wx/HL#8020(squash 合并至 dev-v3 @adfc5b53b)
  • 共用关系机制本身(confirm/query/release 三端点完整契约)见 #7444 changelog:changelogs-v2/2026-09/19_7444_团期配车就绪门禁与车辆共用关系-修改接口-管理后台.md(尚未推送的草稿,仅供内部核对参考,不作为已发布依据)

关联 / 联系人

链接

联系人

  • 后端负责人: wx(GIT)
  • 前端负责人: mmg