From c1440208b247a03d791605897043fa32574bb122 Mon Sep 17 00:00:00 2001 From: API Changelog Bot Date: Wed, 23 Sep 2026 16:30:02 +0800 Subject: [PATCH] =?UTF-8?q?changelog(8194):=20=E8=AE=A2=E6=AD=A3=E9=99=8D?= =?UTF-8?q?=E7=BA=A7=E6=80=81=E4=B8=8E=E8=AF=B7=E6=B1=82=E7=A4=BA=E4=BE=8B?= =?UTF-8?q?=E4=B8=89=E5=A4=84=EF=BC=88=E7=8B=AC=E7=AB=8B=E5=AF=B9=E6=8A=97?= =?UTF-8?q?=E8=AF=84=E5=AE=A1=E5=A4=8D=E6=A0=B8=E5=90=8E=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 字典服务(hl-user-service)不可用时,靠字典才能纳入配置位的角色 (GUIDE_ASSISTANT / STUDY_TEACHER / LIFE_TEACHER)**同样**返回 582117—— 原文「字典读挂时不会变成一律拒绝」只对内置兜底角色成立。582117 现在有两种成因, 排查顺序改为「先确认字典服务可用性,再看字典内容」。 2. 请求示例原用了修复后必被 582117 拒的 payload,却紧跟 200 成功响应示例,已换成 GUIDE。 3. 补记两条边界:5 分钟实例缓存窗口;存量脏行会被前端回显提交并逐行校验, 可能让整次保存(含不传 scopeRoles 的整期覆盖)被 582117 拦下。 同步订正 errorcode javadoc、resolver javadoc、单测 javadoc,并新增一条把降级态 行为钉住的用例(GroupBatchStaffRoleSlotGuardTest#dictUnavailable_dictRoleIsRejectedKnownBoundary)。 --- ...面字典角色被删回后不再静默放行-修改接口-管理后台.md | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/changelogs-v2/2026-09/23_8194_配置位守卫判据翻面字典角色被删回后不再静默放行-修改接口-管理后台.md b/changelogs-v2/2026-09/23_8194_配置位守卫判据翻面字典角色被删回后不再静默放行-修改接口-管理后台.md index 6157bf1f..b957c93d 100644 --- a/changelogs-v2/2026-09/23_8194_配置位守卫判据翻面字典角色被删回后不再静默放行-修改接口-管理后台.md +++ b/changelogs-v2/2026-09/23_8194_配置位守卫判据翻面字典角色被删回后不再静默放行-修改接口-管理后台.md @@ -14,6 +14,7 @@ target_release: "" verified_at: "" status_note: "工单 #8194 补的是 #8148 留下的残留缺口。角色↔人员类型的守卫 582117 判的是「该角色在配置位字典里查不到归属位」,而它的分流判据(GroupBatchStaffSlotResolver#participatesInSlotsByFallback)读的是编译期常量兜底名单,只认 GUIDE / LEADER / PHOTOGRAPHER。问题是 #7079 / #8148 立下的能力恰恰是「业务在字典页面加一行就能把某个人员类型配进配置位、不发版」——靠字典新增出来的角色永远不在这张常量名单里,于是「字典给某位加一行 → 有人按它配了人 → 那行又被删掉」这一形态仍走静默放行分支:接口 200、脏行落库、并异步扇出到团内全部活跃订单,该角色的 582114 当场失效,要到结算对账才发现人岗对不上。关键一点是撞上它不需要有人手搓请求——resolveStoredStaffRole 会把落库的 staff_role 改写成人员的真实类型(字典角色),前端回显后原样提交回来,系统自己就会产出那个落进缺口的 staffRole 值。修法(2026-09-23 定案 B,判据翻面):把「本该有位」的判据从「在不在兜底名单里」换成「不在结构性豁免闭集 STRUCTURALLY_SLOT_EXEMPT_ROLES(= DRIVER / OTHER)里」,豁免闭集落成显式常量集合并从 SettlementStaffRoleEnum 取值,方法名同步改为 requiresSlotMembership(改完它不再读 fallbackTypes(),名字与语义必须同批改)。行为变化:GUIDE_ASSISTANT / STUDY_TEACHER / LIFE_TEACHER 三个「只可能靠字典纳入配置位」的角色,在「字典里查不到归属位」时由静默 200 落库变为返回 582117;DRIVER / OTHER 照旧放行(OTHER 的常态就是不属于任何配置位,保护它会拒掉每一次正常的杂项人员配置,那是删功能不是补洞)。已知边界(按 #8194 决策点 4 有意保留、不在本单修):OTHER 在 VALID_STAFF_TYPES 里,业务能把 OTHER 写进某个位的字典再删掉,那一形态仍走静默放行。生产代码由 PR #8259 合入 dev-v3(squash 提交 f6d21648b)。2026-09-23 测试服已实测:PUT /v3/admin/group-batch/2102640646949105666/staff 提交 staffRole=GUIDE_ASSISTANT 返回 code=582117(HTTP 200,文案完整),读回确认零写入;同端点 staffRole=DRIVER 与 staffRole=OTHER 均返回 code=200 成功;探测插入的两行已用 scopeRoles=[DRIVER,OTHER] + staffList=[] 还原,读回与探测前逐字段一致。契约(路径 / 方法 / 权限 / 入参字段与取值域 / 成功响应结构)零变更——BatchStaffConfigReqVO 只加了 javadoc(含该表)。前端结论(2026-09-23):5821xx 全族在 hl-ui v2.1 无专门分支,统一由拦截器按 code 非成功 toast 后端 message;582117 文案已点名角色并指向配置位字典页,完整直出即可。命中后正确动作是去字典页补行,重试仍被拒不会误操作——与 #8123/#8148 既定拍板一致,维持通用 toast,不做专门引导,零改动闭环。" updated_at: "2026-09-23" +# ⚠️ 本条目于 2026-09-23 由独立对抗评审复核后订正过三处:①降级态(字典服务不可用)对字典角色同样返回 582117,原文「不会变成一律拒绝」只对内置角色成立;②请求示例原用了修复后必被拒的 payload;③补记缓存窗口与存量脏行两条边界。 base: "dev-v3" --- @@ -116,9 +117,11 @@ Host: api.test.1814.love:9443 Authorization: Bearer Content-Type: application/json -{"staffList":[{"staffId":1002,"staffRole":"GUIDE_ASSISTANT","sortOrder":0,"remark":"hl8194-gateway-ac1"}]} +{"staffList":[{"staffId":1002,"staffRole":"GUIDE","sortOrder":0,"remark":"hl8194-ok"}]} ``` +> ⚠️ 把上面的 `staffRole` 换成 `GUIDE_ASSISTANT`(或其他字典角色)在**修复后**会返回 582117,不再是 200——见「错误响应」。这正是本单的行为变化。 + #### 响应示例 成功时: @@ -143,7 +146,9 @@ Content-Type: application/json `staffList` 传空数组即清空覆盖范围内的配置,返回 200,`staffList` 为空数组、`affectedOrderCount` 为实际扇出订单数。 -配置位字典读挂 / 读空时**不会**变成一律拒绝:`typesOf` 回落内置默认值(导游位 `GUIDE+LEADER`、摄影位 `PHOTOGRAPHER`),内置角色的保存照常成功——这是 #7079 写下的降级保证,本单未动。 +配置位字典读挂 / 读空时,`typesOf` 回落内置默认值(导游位 `GUIDE+LEADER`、摄影位 `PHOTOGRAPHER`):**内置角色**(`GUIDE` / `LEADER` / `PHOTOGRAPHER`)的保存照常成功,这条降级保证本单未动。 + +⚠️ **但内置名单之外的角色不是这样**:`GUIDE_ASSISTANT` / `STUDY_TEACHER` / `LIFE_TEACHER` 不在兜底名单里,字典服务不可用期间它们依然「查不到归属位」⇒ **同样返回 582117**。也就是说 582117 现在有两种成因:①字典可读、但该角色不在任何配置位里(修复动作是补回那一行字典);②字典服务不可用、且该角色不是内置兜底成员(修复动作是恢复字典服务)。**文案当前不区分两者**,排查时请先确认字典服务可用性。 #### 错误响应 @@ -166,7 +171,7 @@ Content-Type: application/json #### 业务边界 -- **582117 不是「字典读挂」的兜底**:Feign 失败或字典整表为空时 `typesOf` 回落内置默认值,归属位照样查得到,走不到这个码。能触发它的只有「**字典非空、但这个角色被从里面删掉了**」(或该角色从没在任何位的字典里出现过)。 +- **582117 有两种成因,排查顺序是先服务后内容**:①字典可读、但该角色不在任何配置位里(被删掉,或从来没配过);②**字典服务(hl-user-service)不可用**而该角色又不是内置兜底成员(`GUIDE` / `LEADER` / `PHOTOGRAPHER`)。此时文案说的「字典是否被误删」会指错方向——先去字典页面看会发现那行可能压根没删过。 - **被判定的不是「你这个角色对不对」,而是「这个角色的归属位是不是丢了」**。角色本身合法(在 `@Pattern` 取值域内)但字典里没有它的位 → 582117;位找得到但人岗不符 → 582114。两者互补。 - **修复动作在配置位字典页面,不在这个弹窗里**,所以文案点名角色并指向字典,适合原样透出给运营。 - **豁免闭集是 `DRIVER` 与 `OTHER`**,两者在任何字典状态下都不做角色↔人员类型校验,582117 与 582114 都不触发。 @@ -221,7 +226,9 @@ Content-Type: application/json - **字典读挂 / 读空不降级成拒绝**:`typesOf` 回落内置默认值,内置角色(`GUIDE` / `LEADER` / `PHOTOGRAPHER`)照常保存。 - **将来往 `@Pattern` 里加第 9 个取值**而忘了判断它属不属于豁免闭集时,新角色默认落进「本该有位」侧 ⇒ 那一侧是**拒绝**(响亮失败、当场被发现),不是静默放行。失败方向正确。 - **已知边界,不在本单修**:`OTHER` 在 `VALID_STAFF_TYPES` 里,业务**能**把 `OTHER` 写进某个位的字典再删掉,那一形态仍走静默放行。判据是:`OTHER` 的常态化就是不属于任何配置位,把它移出豁免闭集会拒掉每一次正常的杂项人员配置;而「把『其他』纳入『导游位』」本身是配置位语义的误用(等于宣告「其他类型的人算导游」),错的是那行字典。 -- **存量脏行不在本单范围**:字典行被删之后库里已有的 `staff_role='GUIDE_ASSISTANT'` 行不再属于任何配置位,整位守卫 582116 也判不到它,这些行不会被 `scopeRoles` 的删除窗口覆盖或重建。治理另立工单。 +- **存量脏行的连带影响(本单不修,但必须知道)**:字典行被删之后库里已有的 `staff_role='GUIDE_ASSISTANT'` 行不再属于任何配置位,整位守卫 582116 也判不到它,这些行不会被 `scopeRoles` 的删除窗口覆盖或重建。⚠️ 更要紧的是:**这类存量行会被前端原样回显提交**,而校验是**逐行**做的——只要该团期的保存请求里带上这样一行且 `staffRole` 仍在缺口集合内,**整次保存(含不传 `scopeRoles` 的整期覆盖)都会返回 582117**。是否已有这类存量行未在本次取证中统计(生产库只读盘点属独立动作)。治理另立工单。 +- **缓存窗口**:`typesOf` 是固定 5 分钟 TTL 的实例本地缓存(`@VettedLocalCache`)。字典改完到全部实例生效之间,同一请求可能一台放行、一台返回 582117。所以「删回后不再静默放行」是**有界**结论:最长 5 分钟、按实例收敛。 +- **判据不认识「历史」**:`requiresSlotMembership` 只看字典**当前**内容。因此「从没在任何配置位字典里出现过」与「加过又被删回」走同一条拒绝分支,而文案一律写成「是否被误删」。首次配置某类人员(业务还没往字典加那一行)时会看到这句——正确动作是**去对应配置位字典加一行**,不是去查删除记录。 --- @@ -256,7 +263,8 @@ Content-Type: application/json | `staffRole=STUDY_TEACHER` / `LIFE_TEACHER`(同上) | 同上 | 582117,零写入 | | `staffRole=LEADER` 被从非空字典删掉 | 582117(#8148 已建立) | **不变** | | `staffRole=DRIVER` / `OTHER` | 放行 | **不变** | -| 字典读挂 / 读空 | 回落内置默认值,内置角色放行 | **不变** | +| 字典读挂 / 读空(内置角色) | 回落内置默认值,放行 | **不变** | +| 字典读挂 / 读空(字典角色 `GUIDE_ASSISTANT` 等) | **静默 200** | **582117,零写入**(降级态无法区分「被删掉」与「读不到」,见「六、边界行为」) | | 判据实现 | `participatesInSlotsByFallback` 读编译期常量 `fallbackTypes()` | `requiresSlotMembership` 判「不在结构性豁免闭集 `{DRIVER, OTHER}` 里」 | | 路径 / 入参 / 权限 / 成功响应结构 | — | **零变化** |