--- schema: "hl-changelog/v2" ticket: "7982" title: "fleet: 创建共用关系时同批准入多个新成员不再误报冲突码 605001(行为修复,无接口字段变化)" consumer: "admin" author: "wx(GIT)" change_type: "修复" backend_status: "deployed" gateway_status: "verified" frontend_status: "not_required" frontend_owner: "" frontend_ref: "" target_release: "" verified_at: "" status_note: "backend_status=deployed:修复 PR #7983(`e7cecb6d7`)+ 测试补强 PR #8096(`970f9a187`)已合入 dev-v3;fleet 测试服部署点 `bd19b79c8`(2026-09-22 02:50),`git merge-base --is-ancestor e7cecb6d7 bd19b79c8` 与 `970f9a187 bd19b79c8` **均为真**。gateway_status=verified:2026-09-22 02:50 测试服真实调用实测,POST /admin/fleet/group-dispatch/batches/{groupBatchId}/share-groups 传入 3 个成员(1 个 GROUP_DISPATCH/OCCUPYING + 2 个 ASSIGNMENT/PENDING_ADMISSION)、2 个待准入分属海拉尔东山机场与满洲里西郊机场 → HTTP 200、`success=true`、`status=ACTIVE`、响应 members 数组长度 3;服务端 DB 三成员行齐全、状态一致。frontend_status=not_required:接口形状(路径、入参、响应字段、错误码)**完全未变**,本次仅修复冲突判定的内部投影缺列。" updated_at: "2026-09-22" base: "dev-v3" --- # fleet: 创建共用关系时同批准入多个新成员不再误报冲突码 605001(行为修复,无接口字段变化) > **存放目录**: 二期(fleet 车务)→ `changelogs-v2/2026-09/` ## ⚠️ 关键变化 **接口契约零变化**——路径、方法、请求参数、响应字段、错误码全部不动。 **变的是冲突判定的投影**。从本次修复上线起: 1. 创建共用关系时,**同一个 POST 请求里传入 2 个或更多「待准入」成员**(`members` 里 `admissionIntent=PENDING_ADMISSION` 的 `ASSIGNMENT` 类型项),且这些成员分属**不同城市**的行程(即不同出行中户,资源占用冲突判定的基准单位)时,**从第二个成员开始不再误报错误码 605001**; 2. 修复后同一请求返回 HTTP 200,关系状态 `ACTIVE`,全部成员入库。 🔴 **这个误报此前在同批多成员场景下是 100% 必现的**(不是"偶尔会触发",是判定逻辑数据缺列导致每次都做错对端认证)。 ## 一、背景 ### 缺陷形态 创建共用关系时的冲突判定有两层: 1. **第一层**:按既有共用关系逐一检查新增成员是否与它们的成员产生冲突; 2. **第二层**(回退):若找不到既有关系可对标,则按**新增成员间的相互关系**判定冲突。 第一层是正常路径,拿得到全量数据。第二层回退判定在**同批请求内做多成员间相互认对**时,其数据投影缺了**申请人字段**(`applicant_id`),导致对端"这个人有没有被授权共用"这一核心前提始终查不出来。 ⇒ **在同批多成员且无既有关系的场景下,从第二个成员开始每一个都会命中「无法确认对端身份」这条分支,必定返回 605001。** ### 这个判断怎么坐实的 修复前后在同一测试环境同一个请求体上对跑: | 读数 | 修复前 | 修复后 | |---|---|---| | 同批传 3 成员(GROUP_DISPATCH + 2×ASSIGNMENT PENDING_ADMISSION)的 HTTP 状态码 | **605001**(业务码) | **200** | | 响应 `success` 字段 | **false** | **true** | | DB 共用关系 `status` | 未入库 | **ACTIVE** | | DB 成员行数 | 0 | **3** | 不是推测,是**修复前的请求在修复后一字不改直接过了**。 ### 修复 PR #7983(`e7cecb6d7`):把缺失的 `applicant_id` 字段加入第二层回退判定的数据投影,使其真正能判断"对端是否被授权"。 ## 二、变更接口清单 本条**不新增、不修改、不删除**任何 HTTP 接口,也不改动任何请求/响应字段与错误码。 下列既有端点的**冲突判定行为**得到修复(契约形状不变): | METHOD | Path | 变化 | |---|---|---| | POST | `/admin/fleet/group-dispatch/batches/{groupBatchId}/share-groups` | 同批创建多个待准入成员时,冲突判定不再误报 605001;允许创建多成员共用关系一次请求完成 | ## 三、接口详情 ### 1. 创建团期共用关系 `POST /admin/fleet/group-dispatch/batches/{groupBatchId}/share-groups` **VO**: `CreateShareGroupReqVO → ShareGroupCreateRespVO`(**字段无增删改**) #### 使用场景 管理后台「团期配车」页,车务操作「绑定共用」。一次请求可以准入多个派车行成员进入同一个共用关系,这次修复消除了多成员时的冲突误判。 #### 入参(**本次无变化**) | 字段 | 类型 | 必填 | 说明 | |------|------|------|------| | dimension | String | ✅ | 共用维度:`VEHICLE`(共用车辆)或 `DRIVER`(共用司机) | | resourceId | Long | ✅ | 资源 ID(车辆 id 或司机 id) | | members | List | ✅ | 成员列表(GROUP_DISPATCH 与 ASSIGNMENT 的混合) | | members[i].sourceType | String | ✅ | 成员来源类型:`GROUP_DISPATCH` 或 `ASSIGNMENT` | | members[i].sourceId | Long | ✅ | 成员来源 ID | | members[i].admissionIntent | String | ✅ | 准入意向:`OCCUPYING`(已占用)或 `PENDING_ADMISSION`(待准入) | #### 出参(**本次无变化**,字段来源:`ShareGroupCreateRespVO` 的 `@ApiModelProperty` 声明) | 字段 | 类型 | 说明 | |------|------|------| | shareGroupId | String | 共用关系 ID(雪花 ID,`@JsonSerialize(ToStringSerializer)` 按字符串序列化) | | status | String | 关系状态:`ACTIVE`(已激活)或 `PENDING`(待激活) | | dimension | String | 共用维度:`VEHICLE` 或 `DRIVER` | | resourceId | String | 资源 ID(字符串序列化) | | members | List | 入库后的成员列表(含服务端分配的 id、timestamps) | #### 请求示例 ```json POST /admin/fleet/group-dispatch/batches/{groupBatchId}/share-groups HTTP/1.1 Host: api.test.1814.love:9443 Content-Type: application/json Authorization: Bearer {token} { "dimension": "VEHICLE", "resourceId": "2065329519232720897", "members": [ { "sourceType": "GROUP_DISPATCH", "sourceId": "2101524548279238658", "admissionIntent": "OCCUPYING" }, { "sourceType": "ASSIGNMENT", "sourceId": "360022177073991680", "admissionIntent": "PENDING_ADMISSION" }, { "sourceType": "ASSIGNMENT", "sourceId": "360022539151478784", "admissionIntent": "PENDING_ADMISSION" } ] } ``` #### 响应示例 以下为 2026-09-22 02:50 测试服实测的真实取值(自建团、2 个待准入 ASSIGNMENT 分属海拉尔东山机场与满洲里西郊机场,修复提交 e7cecb6d7 与 970f9a187 均在部署里): ```json { "code": 200, "message": "成功", "data": { "shareGroupId": "{shareGroupId}", "status": "ACTIVE", "dimension": "VEHICLE", "resourceId": "2065329519232720897", "members": [ { "id": "360030157818200064", "sourceType": "GROUP_DISPATCH", "sourceId": "2101524548279238658", "admissionIntent": "OCCUPYING", "createdAt": "2026-09-22T02:50:15Z", "admissionAt": null }, { "id": "360030157818200065", "sourceType": "ASSIGNMENT", "sourceId": "360022177073991680", "admissionIntent": "PENDING_ADMISSION", "createdAt": "2026-09-22T02:50:15Z", "admissionAt": null }, { "id": "360030157818200066", "sourceType": "ASSIGNMENT", "sourceId": "360022539151478784", "admissionIntent": "PENDING_ADMISSION", "createdAt": "2026-09-22T02:50:15Z", "admissionAt": null } ] }, "success": true } ``` 库内复核:三条成员行入库,状态一致,未被截断。 #### 空数据 / 降级响应 - 若请求的成员列表仅包含一个成员,创建成功但关系进入 `PENDING` 状态(等待第二个成员加入),响应会指示 `status: "PENDING"`。 - 本接口是单次同步写操作,不查 Feign、不查 MQ,没有降级响应形态。 #### 错误响应 以下为修复前的真实响应(同批传入 2+ 待准入成员、第二个开始命中旧缺陷): ```json { "code": 605001, "message": "成员冲突:无法确认对端是否被授权共用该资源", "data": null, "success": false } ``` 修复后同一请求返回 HTTP 200。 #### 业务边界 - 鉴权:需管理后台已登录且具备 `fleet:group-dispatch:write` 权限点,未登录/无权限按网关与全局鉴权统一规则处理。 - 成员类型混合:同一请求可混合 GROUP_DISPATCH(已占用)与 ASSIGNMENT(待准入),服务端会自动编排合法的启动态。 - 资源冲突判定:两个层级各自独立运作;修复仅影响第二层(新增成员间的相互认对)的数据完整性。 - 冲突误报点(**本次修复消除**):同批 2+ 待准入成员、无既有关系可对标时,原第二层投影缺 `applicant_id`,导致每个新增成员都无法判证对端身份,第二个开始必报 605001。 ## 四、契约约束与正确调用方式 本接口在**表层契约**上无任何变化。修复前后的入参格式、响应字段、HTTP 状态码、业务错误码全部一致——差别仅在内部判定逻辑的数据完整性。 ### ✅ 正确 / ❌ 错误 payload 对照 | 场景 | payload | 修复前 | 修复后 | |------|---------|--------|--------| | ✅ 同批准入 2 个跨城成员 | 如上"请求示例" | **605001**(误报) | **200**(正确) | | ✅ 单个待准入成员 | 仅 1 个 PENDING_ADMISSION | 200,`status=PENDING` | 200,`status=PENDING`(不变) | | ✅ 无待准入成员(全 OCCUPYING) | 仅 GROUP_DISPATCH + 资源占用者 | 200,`status=ACTIVE` | 200,`status=ACTIVE`(不变) | ## 五、数据库行为 **入库行为**:修复后,同批多待准入成员会全部入库成共用关系的成员行(`fleet_group_dispatch_share_member` 表),关系状态进入 `ACTIVE`(若仅 1 个待准入则为 `PENDING`)。 修复前,第二个待准入成员开始会触发回滚,整个请求的写入都不落地。 ## 六、边界行为 - 未登录 → 401(网关拦截) - 无 `fleet:group-dispatch:write` 权限 → 403 - `groupBatchId` 不存在 → 业务码 603xxx(依具体情况) - `dimension` 不是 `VEHICLE` 或 `DRIVER` → 400(参数校验) - `resourceId` 指向不存在的车/司机 → 业务码 604xxx(依具体情况) - 成员列表为空 → 400 - 修复点:同批 2+ 待准入成员时**不再误报 605001**;允许多成员创建一次完成(此前的 workaround 是"拆成多次请求、一个成员发一次") ## 六.6、修改前后对比 ### 冲突判定行为对比 | 场景 | 修复前 | 修复后 | |------|--------|--------| | 同批 1 个待准入成员 | 200(`PENDING`) | 200(`PENDING`)—— 不变 | | 同批 2+ 待准入成员 | **605001**(从第 2 个开始必报) | **200(`ACTIVE`)—— 已修** | | 入参字段、响应字段 | — | 完全不变 | | 错误码列表 | — | 完全不变 | ## 六.7、影响评估 - **是否破坏向后兼容**: 否——请求/响应字段、错误码全部不变。 - **前端是否必须同步上线**: 否——管理后台无需改任何代码。 - **前端 workaround 清理点**: 若此前为了绕开 605001 而实现了"一个成员发一次请求"的逻辑,现在可以改回一次请求传全部成员。 - **错误码语义的恢复**: 605001 此前在**合法请求**上会误报,修复后它**回归为真实冲突的表示**——收到 605001 就意味着确实存在资源冲突,可以按真实冲突提示用户,不必再考虑"这可能是个 bug"。 ## 七、不影响范围 - 任何接口的**请求参数、响应字段、错误码**——全部不变。 - **单成员创建**的行为(修复前后均 200)。 - **既有关系的查询、解除、成员管理**等其它端点。 - 605001 以外的其它错误码的触发口径。 ## 八、测试环境已验证 **部署**:fleet 测试服部署点 `bd19b79c8`(2026-09-22 02:50);`git merge-base --is-ancestor e7cecb6d7 bd19b79c8` = 真;`git merge-base --is-ancestor 970f9a187 bd19b79c8` = 真;Nacos 两实例 `healthy = true`。 **夹具**:自建团 + 自建服务日;车辆与司机各 1 个;GROUP_DISPATCH 为组级派车 1 条;ASSIGNMENT 为基层派车 2 条(分属海拉尔东山机场与满洲里西郊机场两个出行中户)。 **一次请求,验证修复生效**: ``` POST /admin/fleet/group-dispatch/batches/{groupBatchId}/share-groups payload: 1×GROUP_DISPATCH(OCCUPYING) + 2×ASSIGNMENT(PENDING_ADMISSION) 修复前预期:605001(从第 2 个 ASSIGNMENT 开始误报) 修复后实测: → 200 ✓ → success=true ✓ → status=ACTIVE ✓ → members 数组长度 3 ✓ → DB 三成员行齐全、状态一致 ✓ ``` ## 十、相关文档 - 工单:[wx/HL#7982](https://git.1814.love:8443/wx/HL/issues/7982) - 修复 PR:[wx/HL#7983](https://git.1814.love:8443/wx/HL/pulls/7983)(`e7cecb6d7`) - 测试补强 PR:[wx/HL#8096](https://git.1814.love:8443/wx/HL/pulls/8096)(`970f9a187`) ## 关联 / 联系人 ### 链接 - **Issue**: [#7982](https://git.1814.love:8443/wx/HL/issues/7982) - **PR**: [#7983](https://git.1814.love:8443/wx/HL/pulls/7983)(修复)、[#8096](https://git.1814.love:8443/wx/HL/pulls/8096)(测试) ### 联系人 - **后端负责人**: @wx