diff --git a/changelogs-v2/2026-09/20_8003_共用关系跨维度成员改绑与悬挂核对任务1045-修复-管理后台.md b/changelogs-v2/2026-09/20_8003_共用关系跨维度成员改绑与悬挂核对任务1045-修复-管理后台.md new file mode 100644 index 00000000..31fd65d1 --- /dev/null +++ b/changelogs-v2/2026-09/20_8003_共用关系跨维度成员改绑与悬挂核对任务1045-修复-管理后台.md @@ -0,0 +1,121 @@ +--- +schema: "hl-changelog/v2" +ticket: "8003" +title: "团期车辆共用关系:跨维度确认后成员行跟随最新派单行 + 新增每日悬挂核对任务 1045(无接口契约变更)" +consumer: "admin" +author: "jw(GIT)" +change_type: "修复" +backend_status: "deployed" +gateway_status: "verified" +frontend_status: "not_required" +frontend_owner: "" +frontend_ref: "" +target_release: "" +verified_at: "" +status_note: "本单没有新增、修改、删除任何 admin/mp 端点,请求参数与响应字段全部不变,change_type 取「修复」是按 20_7539 先例(校验器只允许 新增接口/修改接口/删除接口/修复/前端* 几类,接口三类要求逐端点模板而本单无 admin 端点可列)。唯一新增的是 Quartz 桥接用的内部端点 POST /internal/fleet/jobs/share-member-dangling-reconcile/run,只由 hl-user-service 的定时任务经 X-Internal-Token 调用,不经网关、前端不可达,故不单列后端 changelog 条目。gateway_status=verified 指共用关系确认端点 POST /admin/fleet/group-dispatch/batches/{groupBatchId}/share-groups 经测试网关实测通过(2026-09-20 在团期 2101174125761265666 上连建车、司机两个维度的关系,库内复核两组成员行均指向同一张最新派单行)。frontend_status=not_required:响应字段与结构不变,前端无需改调用;但 members[].sourceId 的取值行为变了(见第二节),任何把它缓存/固化的前端逻辑需要复核。" +updated_at: "2026-09-20" +base: "dev-v3" +--- + +# fleet: 团期车辆共用关系跨维度确认后成员行跟随最新派单行 + 每日悬挂核对任务 1045 + +> **服务**: hl-fleet-service(改绑与核对逻辑)/ hl-user-service(任务注册 1045) +> **PR**: [#8029](https://git.1814.love:8443/wx/HL/pulls/8029) +> **Issue**: [#8003](https://git.1814.love:8443/wx/HL/issues/8003) +> **日期**: 2026-09-20 +> **影响范围**: 无对外接口变化;共用关系详情里 `members[].sourceId` 在跨维度确认后会指向最新那张派单行 + +--- + +## ⚠️ 关键变化 + +- **本单没有任何可调用的新接口**:admin 侧没有新增、修改、删除端点,请求参数、响应字段、枚举、路径全部不变。 +- **同一批成员先后建「车维度」和「司机维度」两个共用关系时,两个关系的成员身份现在指向同一张派单行**。 + 此前第二次确认只更新它自己那个维度,先建的那个维度会一直指着一张已经作废的派单。 +- **新增每日定时任务 1045「共用关系成员悬挂核对」**(每天 `04:10`),只巡检、只记日志,**不改任何业务数据**。 +- **前端不需要改任何调用**,但要知道:`GET /admin/fleet/group-dispatch/batches/{groupBatchId}/share-groups` + 返回的 `members[].sourceId` 会在成员被重新派车后换成新值,**不能把它当成稳定标识长期缓存**。 + +--- + +## 一、原先的现象 + +确认共用关系时,如果某个成员当前还没占着这辆共用车(或这名共用司机),后端会替它走一次改派 +把它派进来。**改派是「作废旧的派车记录、生成一张新的」**,派车记录的 ID 必然换新。 + +一户成员可以同时在两个关系里:一个是「共用这辆车」,另一个是「共用这名司机」。 +过去第二次确认只把**它自己那个维度**的成员身份更新成新的派车记录,先建的那个维度仍记着旧的那张 +——而旧的那张此刻已经作废。结果是: + +- 关系的成员名单里挂着一个**已经不存在的派车记录**; +- 这户真正在跑的那张派车记录,反而不在那个关系的成员名单里。 + +对使用者当时不可见(冲突校验会跳过已作废的记录,所以不会误判、不会报错), +但凡是按「这个关系有几个成员」计数的地方都会多算一个。 + +## 二、现在的行为 + +| 场景 | 原来 | 现在 | +|------|------|------| +| 先建车维度关系,再建司机维度关系 | 车维度成员仍指向被作废的派车记录 | 两个维度的成员都指向同一张、当前在跑的派车记录 | +| 关系详情里的 `members[].sourceId` | 可能是一个已作废的派车记录 ID | 始终是当前在跑的那张 | +| 关系的活跃成员数 | 可能把同一户算两次(旧 ID 一次、新 ID 一次) | 与真实成员数一致 | + +**响应结构没有任何变化**,变的是 `members[].sourceId` 的取值: + +```jsonc +// GET /admin/fleet/group-dispatch/batches/2101174125761265666/share-groups?serviceDate=2027-01-06 +// 车维度关系(359966491308855296)——下面这个 sourceId 就是修复后的取值 +{ + "code": 200, + "message": "成功", + "data": [ + { + "shareGroupId": "359966491308855296", + "resourceType": "VEHICLE", + "resourceId": "2065329519232720897", + "status": "ACTIVE", + "members": [ + { "sourceType": "GROUP_DISPATCH", "sourceId": "2101174278589190146", "requirementId": null, "orderId": null }, + { "sourceType": "ASSIGNMENT", "sourceId": "359966520803201024", "requirementId": "7330996163649609", "orderId": "2101174125723516929" } + ] + } + ] +} +``` + +修复前这一格会停在 `359966492122550272`(已作废的那张),修复后是 `359966520803201024`(当前在跑的那张), +与司机维度关系里那一格完全相同。 + +**给前端的一句话**:`sourceId` 是「这户当前挂的那张派车记录」,会随改派变化; +要长期标识一户成员请用 `requirementId`(用车需求 ID,跨改派不变)或 `orderId`。 + +## 三、新增定时任务 1045「共用关系成员悬挂核对」 + +| 任务 | 时间 | 做什么 | 会不会改数据 | +|------|------|--------|--------------| +| 1045 共用关系成员悬挂核对 | 每天 `04:10` | 巡检全部在关系里的成员,找出那些指向**已作废/已不存在**派车记录的 | **不会**,只记日志并返回条数 | + +- **只发现、不处置**:一条这样的记录,可能该改绑到新的派车记录、也可能该退出关系, + 从记录本身分辨不出,自动猜错方向会把车务人工确认过的关系悄悄改掉。所以任务只把它们报出来,由人处置。 +- 任务的结果在 fleet 服务日志里按关系汇总(`共用关系存在悬挂成员行: shareGroupId=... 悬挂行数=...`), + 并作为返回值回给调度侧的执行日志。 +- 任务不对前端暴露任何接口,前端无需感知。 + +## 四、测试服实测(2026-09-20) + +| 项 | 实测 | +|---|---| +| 连建车、司机两个维度的关系(团期 `2101174125761265666` / `2027-01-06`) | 两次确认均 `code=200`;库内复核两个关系的成员行 `source_id` 同为 `359966520803201024`,被作废的 `359966492122550272` 上无任何在关系中的成员 | +| 定时任务 1045 | `sys_job` 已注册且 `ACTIVE`,cron `0 10 4 * * ?`;两个 fleet 实例各触发一次均 `code=200` | +| 核对结果与库读数对账 | 存量处置前 **4** → 处置 2 条后 **2** → 新建两个关系后仍 **2**(新关系零悬挂),每一次都与同口径 SQL 读数相同 | + +存量:测试服清查出的历史遗留记录共 4 条,已处置 2 条(改绑到当前在跑的派车记录), +另 2 条因对应需求当日已无在途派车记录而保留待人工判断,任务会继续把它们报出来。 + +--- + +## 五、前端要不要动 + +**不需要**。响应结构、字段名、枚举、路径、错误码全部不变。唯一需要知悉的是第二节末尾那句: +`members[].sourceId` 不是稳定标识,不要缓存或用它做跨请求的比对,要稳定标识用 `requirementId` / `orderId`。