| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253 |
- -- =====================================================================================
- -- S8-P0-4-API-AUTHORIZATION-1 · 权限种子(WIP,尚未取正式版本号)
- --
- -- 作用域:**仅当前客户 UAT 租户**
- -- TenantId = 838257186181189
- -- FactoryId = 838257186320453
- --
- -- 本脚本刻意**不做**全平台 S8 RBAC migration:
- -- · 不创建其它租户的 ROLE_S8_CONFIG_ADMIN
- -- · 不给其它 8 个租户的 54 个业务角色批量授权
- -- · 不向其它租户开放新能力码的 SysTenantMenu
- -- 非 UAT 租户在新鉴权上线后会出现 S8 403,已作为 KNOWN RISK 登记,本轮不消除。
- --
- -- ⚠️ WIP 占位(CLAUDE.md 第二节第 7 条)。提交时 rename 为 1.0.<next>.sql 并注册 csproj Copy。
- --
- -- ⚠️⚠️ 部署顺序硬约束 —— 禁止先 restart 新代码再补权限:
- -- S8PermissionAttribute 是 fail-closed,未被授予能力码即 403。
- -- snapshot → apply → DB verify → restart → **重新登录** → Gate 1→2→3→4
- -- (重新登录是必需项:GetOwnBtnPermList 有 7 天缓存 CacheConst.KeyUserButton+userId)
- --
- -- 幂等:所有 INSERT 带 NOT EXISTS;DELETE 谓词精确且可重复执行。
- -- Id 段:1329008900000–1329008999999(已实测该段在 SysRole / SysRoleMenu /
- -- SysUserRole / SysTenantMenu 上全部为空)。
- --
- -- ── 预计影响行数(窗口期实测不符即停止)────────────────────────────────────
- -- §1 SysMenu INSERT 9 新能力码
- -- §2 SysTenantMenu INSERT 9 仅 UAT 租户 × 9
- -- §3 SysRole INSERT 1 仅 UAT 租户的 ROLE_S8_CONFIG_ADMIN
- -- §4 SysRoleMenu INSERT 27 见下方逐角色明细
- -- §5 SysRoleMenu DELETE 12 UAT 租户 UAT_FUNCTION_USER,15 → 只剩 3 个只读
- -- §6 SysUserRole INSERT 1 仅 UATAdminA
- -- ────────────────────────────────────────────────────────────────────────
- -- 合计 = 59
- --
- -- ── 执行事实(2026-09-03 已在 aidopdev 应用)─────────────────────────────
- -- 以事务 + 逐段「预期影响行数」断言执行,任一不符即整体 ROLLBACK。
- -- 实测:§1=9 §2=9 §3=1 §4=27 §5=12 §6=1,合计 59,与上表逐项一致 → COMMIT。
- -- 事务内前置校验全过:UAT_FUNCTION_USER 剩余 s8 权限 3(只读三项);
- -- 其非 S8 授权 231 前后不变;另一同名角色(租户 838257212780613)15 行不变;
- -- ROLE_S8_CONFIG_ADMIN 全库恰 1 个(仅 UAT)。
- -- ──────────────────────────────────────────────────────────────────────────
- --
- -- ── §4 授权依据(每条都对应可查的 UAT 事实,不能证明会用到的一律不授予)──────
- -- ROLE_S8_CONFIG_ADMIN → 10 config + debug + exception:create = 12
- -- 依据:UATAdminA 需 config/debug;timeline 实测 CREATE 由 UATAdminA 执行
- -- ROLE_EQUIP_MAINT → claim,start,verification:submit,create = 4
- -- 依据:UATExceptionA 实测执行 CREATE / START_PROGRESS / VERIFY_SUBMITTED,
- -- 且名下 GEX-019-EQ(IN_PROGRESS) 与新异常 1329909430019(NEW) 待处理
- -- ROLE_PURCHASER → claim,start,verification:submit = 3
- -- 依据:emp 3183(梁军/UATPurchaseA) 为 GEX-002-MP / GEX-002-ETA / GEX-012-MP 处理人
- -- ROLE_WH_INBOUND → claim,start,verification:submit = 3
- -- ROLE_WH_OUTBOUND → claim,start,verification:submit = 3
- -- 依据:emp 3179(章立坤/UATWarehouseA) 名下 3 条**未闭**:
- -- GEX-019-KIT(ASSIGNED) / GEX-020-SHIP(IN_PROGRESS) / GEX-020-PEND(ASSIGNED)
- -- ROLE_QC → verification:approve, verification:reject = 2
- -- 依据:UATQualityA 实测执行 VERIFY_APPROVED / VERIFY_REJECTED
- -- ROLE_ORDER_PLANNER / ROLE_PRODUCTION_PLANNER → 0
- -- 依据:UATPlanA 在 UAT 数据中**从未**作为任何异常的处理人/检验人;
- -- 只读能力经保留的 UAT_FUNCTION_USER 获得,无需额外授权
- --
- -- 刻意未授予(无 UAT 证据,需要时再单独加,勿"保险式"补齐):
- -- s8:exception:assign(转派) · s8:exception:reject · s8:exception:upgrade
- -- · s8:exception:comment · Purchase/Warehouse 的 s8:exception:create
- -- ──────────────────────────────────────────────────────────────────────────
- -- =====================================================================================
- -- =====================================================================================
- -- §0 执行前只读快照(窗口期第 1 步,留档后再执行写入)
- -- =====================================================================================
- -- (0.1) UAT 租户 UAT_FUNCTION_USER 当前 s8:* 权限(预期 15 行)
- SELECT m.`Id` AS MenuId, m.`Permission`
- FROM `SysRoleMenu` rm JOIN `SysMenu` m ON m.`Id` = rm.`MenuId`
- WHERE rm.`RoleId` = (SELECT `Id` FROM `SysRole` WHERE `Code`='UAT_FUNCTION_USER' AND `TenantId`=838257186181189)
- AND m.`Permission` LIKE 's8:%' ORDER BY m.`Permission`;
- -- (0.2) 6 个验收账号当前角色
- SELECT u.`Account`, r.`Code` AS RoleCode
- FROM `SysUser` u JOIN `SysUserRole` ur ON ur.`UserId` = u.`Id` JOIN `SysRole` r ON r.`Id` = ur.`RoleId`
- WHERE u.`TenantId` = 838257186181189 ORDER BY u.`Account`, r.`Code`;
- -- (0.3) ROLE_S8_CONFIG_ADMIN 是否已存在(预期 0)
- SELECT COUNT(*) AS existing_config_admin FROM `SysRole` WHERE `Code` = 'ROLE_S8_CONFIG_ADMIN';
- -- (0.4) §5 预计删除行数(预期 12)
- SELECT COUNT(*) AS expect_delete
- FROM `SysRoleMenu` rm JOIN `SysMenu` m ON m.`Id` = rm.`MenuId`
- WHERE rm.`RoleId` = (SELECT `Id` FROM `SysRole` WHERE `Code`='UAT_FUNCTION_USER' AND `TenantId`=838257186181189)
- AND m.`Permission` LIKE 's8:%'
- AND m.`Permission` NOT IN ('s8:exception:read','s8:config:read','s8:dashboard:read');
- -- (0.5) 该角色非 S8 授权数(§5 前后必须完全相等;实测基线 = 231)
- SELECT COUNT(*) AS non_s8_rows
- FROM `SysRoleMenu` rm JOIN `SysMenu` m ON m.`Id` = rm.`MenuId`
- WHERE rm.`RoleId` = (SELECT `Id` FROM `SysRole` WHERE `Code`='UAT_FUNCTION_USER' AND `TenantId`=838257186181189)
- AND (m.`Permission` IS NULL OR m.`Permission` NOT LIKE 's8:%');
- -- (0.6) 另一个同名角色(租户 838257212780613)基线,§5 后必须不变(预期 15)
- SELECT COUNT(*) AS other_tenant_s8_rows
- FROM `SysRoleMenu` rm JOIN `SysMenu` m ON m.`Id` = rm.`MenuId`
- WHERE rm.`RoleId` = (SELECT `Id` FROM `SysRole` WHERE `Code`='UAT_FUNCTION_USER' AND `TenantId`=838257212780613)
- AND m.`Permission` LIKE 's8:%';
- -- =====================================================================================
- -- §1 新增 9 个 S8 能力码(SysMenu Type=3 Btn)。菜单本身是全局元数据,
- -- 租户可见性由 §2 的 SysTenantMenu 控制,故此处不带租户维度。
- -- =====================================================================================
- INSERT INTO `SysMenu` (`Id`,`Pid`,`Title`,`Name`,`Type`,`Permission`,`OrderNo`,`Status`,
- `IsHide`,`IsKeepAlive`,`IsAffix`,`IsIframe`,`Remark`,`CreateTime`)
- SELECT src.* FROM (
- SELECT 1329008000115 AS Id, 1329008000016 AS Pid, '维护监控规则' AS Title, 'aidopS8Btn15' AS Name, 3 AS Type, 's8:config:watch-rule' AS Permission, 240 AS OrderNo, 1 AS Status, 0 AS IsHide, 1 AS IsKeepAlive, 0 AS IsAffix, 0 AS IsIframe, 'S8 权限点 s8:config:watch-rule' AS Remark, NOW() AS CreateTime
- UNION ALL SELECT 1329008000116, 1329008000015, '维护数据源', 'aidopS8Btn16', 3, 's8:config:data-source', 250, 1, 0, 1, 0, 0, 'S8 权限点 s8:config:data-source', NOW()
- UNION ALL SELECT 1329008000117, 1329008000017, '维护异常类型', 'aidopS8Btn17', 3, 's8:config:exception-type', 260, 1, 0, 1, 0, 0, 'S8 权限点 s8:config:exception-type', NOW()
- UNION ALL SELECT 1329008000118, 1329008000012, '维护通知分层', 'aidopS8Btn18', 3, 's8:config:notification', 270, 1, 0, 1, 0, 0, 'S8 权限点 s8:config:notification', NOW()
- UNION ALL SELECT 1329008000119, 1329008000011, '维护场景配置', 'aidopS8Btn19', 3, 's8:config:scene', 280, 1, 0, 1, 0, 0, 'S8 权限点 s8:config:scene', NOW()
- UNION ALL SELECT 1329008000120, 1329008000019, '维护KPI目标值', 'aidopS8Btn20', 3, 's8:config:kpi', 290, 1, 0, 1, 0, 0, 'S8 权限点 s8:config:kpi', NOW()
- UNION ALL SELECT 1329008000121, 1329008000018, '维护大屏卡片', 'aidopS8Btn21', 3, 's8:config:dashboard', 300, 1, 0, 1, 0, 0, 'S8 权限点 s8:config:dashboard', NOW()
- UNION ALL SELECT 1329008000122, 1329008000010, '补充说明', 'aidopS8Btn22', 3, 's8:exception:comment', 90, 1, 0, 1, 0, 0, 'S8 权限点 s8:exception:comment', NOW()
- UNION ALL SELECT 1329008000123, 1329008000004, '运维调试接口', 'aidopS8Btn23', 3, 's8:debug:run', 900, 1, 0, 1, 0, 0, 'S8 权限点 s8:debug:run', NOW()
- ) AS src
- WHERE NOT EXISTS (SELECT 1 FROM `SysMenu` m WHERE m.`Id` = src.Id OR m.`Permission` = src.Permission);
- -- =====================================================================================
- -- §2 租户可见性 —— **仅 UAT 租户**。
- -- GetOwnBtnPermList 对非超管 INNER JOIN SysTenantMenu,不建这些行则取不到新能力码。
- -- 其它租户刻意不建:非本轮验收作用域(见文件头 KNOWN RISK)。
- -- =====================================================================================
- INSERT INTO `SysTenantMenu` (`Id`, `TenantId`, `MenuId`)
- SELECT 1329008900000 + ROW_NUMBER() OVER (ORDER BY s.MenuId), 838257186181189, s.MenuId
- FROM (
- SELECT m.`Id` AS MenuId FROM `SysMenu` m
- WHERE m.`Id` BETWEEN 1329008000115 AND 1329008000123
- AND NOT EXISTS (SELECT 1 FROM `SysTenantMenu` x
- WHERE x.`TenantId` = 838257186181189 AND x.`MenuId` = m.`Id`)
- ) s;
- -- =====================================================================================
- -- §3 仅为 UAT 租户创建 ROLE_S8_CONFIG_ADMIN
- -- ⚠️ 不加入 S8CanonicalRole.BusinessRoles —— 那是 CFG_NOTIFY 角色候选的唯一来源,
- -- 已被 Runtime Certification 钉死「恰好 7 个」,加进去会当场改掉已认证契约。
- -- =====================================================================================
- INSERT INTO `SysRole` (`Id`,`Name`,`Code`,`OrderNo`,`DataScope`,`Remark`,`Status`,`TenantId`,`CreateTime`)
- SELECT 1329008910001, 'S8配置管理员', 'ROLE_S8_CONFIG_ADMIN', 810, 1,
- 'S8-P0-4:S8 配置类接口的授权载体;能力经 SysRoleMenu 授予,业务代码不判 RoleCode。仅 UAT 租户。',
- 1, 838257186181189, NOW()
- FROM DUAL
- WHERE NOT EXISTS (SELECT 1 FROM `SysRole` r
- WHERE r.`TenantId` = 838257186181189 AND r.`Code` = 'ROLE_S8_CONFIG_ADMIN');
- -- =====================================================================================
- -- §4 授权(全部 additive;仅 UAT 租户的角色;每条对应文件头列出的 UAT 依据)
- -- 只读能力(exception:read / config:read / dashboard:read)由保留的 UAT_FUNCTION_USER
- -- 统一提供,6 个账号都有,故此处不重复授予。
- -- =====================================================================================
- INSERT INTO `SysRoleMenu` (`Id`, `RoleId`, `MenuId`)
- SELECT 1329008920000 + ROW_NUMBER() OVER (ORDER BY s.RoleId, s.MenuId), s.RoleId, s.MenuId
- FROM (
- -- §4.1 ROLE_S8_CONFIG_ADMIN → 10 个 config + debug + exception:create (12)
- SELECT r.`Id` AS RoleId, m.`Id` AS MenuId
- FROM `SysRole` r CROSS JOIN `SysMenu` m
- WHERE r.`TenantId` = 838257186181189 AND r.`Code` = 'ROLE_S8_CONFIG_ADMIN'
- AND m.`Permission` IN ('s8:config:read','s8:config:operator:bind','s8:config:role:write',
- 's8:config:watch-rule','s8:config:data-source','s8:config:exception-type',
- 's8:config:notification','s8:config:scene','s8:config:kpi','s8:config:dashboard',
- 's8:debug:run','s8:exception:create')
- UNION ALL
- -- §4.2 ROLE_EQUIP_MAINT(UATExceptionA)→ operator 最小集 + 提报 (4)
- SELECT r.`Id`, m.`Id`
- FROM `SysRole` r CROSS JOIN `SysMenu` m
- WHERE r.`TenantId` = 838257186181189 AND r.`Code` = 'ROLE_EQUIP_MAINT'
- AND m.`Permission` IN ('s8:exception:claim','s8:exception:start','s8:verification:submit',
- 's8:exception:create')
- UNION ALL
- -- §4.3 采购 / 仓储(UATPurchaseA、UATWarehouseA)→ operator 最小集 (3 × 3 = 9)
- SELECT r.`Id`, m.`Id`
- FROM `SysRole` r CROSS JOIN `SysMenu` m
- WHERE r.`TenantId` = 838257186181189
- AND r.`Code` IN ('ROLE_PURCHASER','ROLE_WH_INBOUND','ROLE_WH_OUTBOUND')
- AND m.`Permission` IN ('s8:exception:claim','s8:exception:start','s8:verification:submit')
- UNION ALL
- -- §4.4 ROLE_QC(UATQualityA)→ 仅质检复核,**不含**任何 operator 能力 (2)
- SELECT r.`Id`, m.`Id`
- FROM `SysRole` r CROSS JOIN `SysMenu` m
- WHERE r.`TenantId` = 838257186181189 AND r.`Code` = 'ROLE_QC'
- AND m.`Permission` IN ('s8:verification:approve','s8:verification:reject')
- ) s
- WHERE NOT EXISTS (SELECT 1 FROM `SysRoleMenu` x WHERE x.`RoleId` = s.RoleId AND x.`MenuId` = s.MenuId);
- -- =====================================================================================
- -- §5 收口 UAT 租户的 UAT_FUNCTION_USER:只保留 3 个公共只读,其余 S8 能力全部撤销
- -- (config 写 / debug / operator / verification 由各自专用角色重新提供)
- --
- -- 约束逐条落实:
- -- · RoleId 按 (Code, TenantId) 精确定位 —— 该 Code 在 838257212780613 也存在,
- -- §0.6 / §7.4 前后对比确保另一租户不受影响;
- -- · 只删 s8:* 且不在保留名单内的行,**不触碰任何非 S8 权限**(基线 231 行,前后相等);
- -- · 不重建菜单、不覆盖 SysRoleMenu。
- -- 预计 12 行(15 - 3)。含已废弃的 s8:exception:close(无对应 Action,属 operator 类,一并收回)。
- -- =====================================================================================
- DELETE rm FROM `SysRoleMenu` rm
- JOIN `SysMenu` m ON m.`Id` = rm.`MenuId`
- WHERE rm.`RoleId` = (SELECT `Id` FROM `SysRole` WHERE `Code`='UAT_FUNCTION_USER' AND `TenantId`=838257186181189)
- AND m.`Permission` LIKE 's8:%'
- AND m.`Permission` NOT IN ('s8:exception:read','s8:config:read','s8:dashboard:read');
- -- =====================================================================================
- -- §6 仅把 ROLE_S8_CONFIG_ADMIN 绑给 UATAdminA(预计 1 行)
- -- 明确不绑:UATExceptionA / UATQualityA / UATPlanA / UATPurchaseA / UATWarehouseA
- -- =====================================================================================
- INSERT INTO `SysUserRole` (`Id`, `UserId`, `RoleId`)
- SELECT 1329008990001, u.`Id`, r.`Id`
- FROM `SysUser` u
- JOIN `SysRole` r ON r.`TenantId` = u.`TenantId` AND r.`Code` = 'ROLE_S8_CONFIG_ADMIN'
- WHERE u.`Account` = 'UATAdminA' AND u.`TenantId` = 838257186181189
- AND NOT EXISTS (SELECT 1 FROM `SysUserRole` x WHERE x.`UserId` = u.`Id` AND x.`RoleId` = r.`Id`);
- -- =====================================================================================
- -- §7 执行后 DB verify(窗口期第 3 步;任一不符即停止,不得 restart)
- -- =====================================================================================
- SELECT COUNT(*) AS new_menus FROM `SysMenu` WHERE `Id` BETWEEN 1329008000115 AND 1329008000123; -- 期望 9
- SELECT COUNT(*) AS uat_tenant_menus FROM `SysTenantMenu` WHERE `TenantId`=838257186181189
- AND `MenuId` BETWEEN 1329008000115 AND 1329008000123; -- 期望 9
- SELECT COUNT(*) AS config_admin_roles FROM `SysRole` WHERE `Code`='ROLE_S8_CONFIG_ADMIN'; -- 期望 1(仅 UAT)
- SELECT COUNT(*) AS new_grants FROM `SysRoleMenu` WHERE `Id` BETWEEN 1329008920001 AND 1329008920999; -- 期望 27
- -- (7.3) UAT_FUNCTION_USER 只剩 3 个只读;非 S8 授权数不变
- SELECT m.`Permission` FROM `SysRoleMenu` rm JOIN `SysMenu` m ON m.`Id` = rm.`MenuId`
- WHERE rm.`RoleId` = (SELECT `Id` FROM `SysRole` WHERE `Code`='UAT_FUNCTION_USER' AND `TenantId`=838257186181189)
- AND m.`Permission` LIKE 's8:%' ORDER BY m.`Permission`;
- -- 期望恰好 3 行:s8:config:read / s8:dashboard:read / s8:exception:read
- SELECT COUNT(*) AS non_s8_rows_after FROM `SysRoleMenu` rm JOIN `SysMenu` m ON m.`Id` = rm.`MenuId`
- WHERE rm.`RoleId` = (SELECT `Id` FROM `SysRole` WHERE `Code`='UAT_FUNCTION_USER' AND `TenantId`=838257186181189)
- AND (m.`Permission` IS NULL OR m.`Permission` NOT LIKE 's8:%'); -- 期望 231
- -- (7.4) 另一租户同名角色未被误伤
- SELECT COUNT(*) AS other_tenant_s8_rows FROM `SysRoleMenu` rm JOIN `SysMenu` m ON m.`Id` = rm.`MenuId`
- WHERE rm.`RoleId` = (SELECT `Id` FROM `SysRole` WHERE `Code`='UAT_FUNCTION_USER' AND `TenantId`=838257212780613)
- AND m.`Permission` LIKE 's8:%'; -- 期望 15(不变)
- -- (7.5) 6 个账号的有效 S8 能力(Gate 4 逐项预期)
- SELECT u.`Account`, m.`Permission`
- FROM `SysUser` u
- JOIN `SysUserRole` ur ON ur.`UserId` = u.`Id`
- JOIN `SysRoleMenu` rm ON rm.`RoleId` = ur.`RoleId`
- JOIN `SysMenu` m ON m.`Id` = rm.`MenuId`
- WHERE u.`TenantId` = 838257186181189 AND m.`Permission` LIKE 's8:%'
- GROUP BY u.`Account`, m.`Permission` ORDER BY u.`Account`, m.`Permission`;
- -- 期望:
- -- UATAdminA : 3 只读 + 10 config + debug:run + exception:create (15)
- -- UATExceptionA : 3 只读 + claim/start/verification:submit/create (7)
- -- UATQualityA : 3 只读 + verification:approve/reject (5)
- -- UATPurchaseA : 3 只读 + claim/start/verification:submit (6)
- -- UATWarehouseA : 3 只读 + claim/start/verification:submit (6,两角色并集去重后同)
- -- UATPlanA : 3 只读 (3)
|