30 KiB
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