WIP-S8-P0-4-API-AUTHORIZATION.sql 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253
  1. -- =====================================================================================
  2. -- S8-P0-4-API-AUTHORIZATION-1 · 权限种子(WIP,尚未取正式版本号)
  3. --
  4. -- 作用域:**仅当前客户 UAT 租户**
  5. -- TenantId = 838257186181189
  6. -- FactoryId = 838257186320453
  7. --
  8. -- 本脚本刻意**不做**全平台 S8 RBAC migration:
  9. -- · 不创建其它租户的 ROLE_S8_CONFIG_ADMIN
  10. -- · 不给其它 8 个租户的 54 个业务角色批量授权
  11. -- · 不向其它租户开放新能力码的 SysTenantMenu
  12. -- 非 UAT 租户在新鉴权上线后会出现 S8 403,已作为 KNOWN RISK 登记,本轮不消除。
  13. --
  14. -- ⚠️ WIP 占位(CLAUDE.md 第二节第 7 条)。提交时 rename 为 1.0.<next>.sql 并注册 csproj Copy。
  15. --
  16. -- ⚠️⚠️ 部署顺序硬约束 —— 禁止先 restart 新代码再补权限:
  17. -- S8PermissionAttribute 是 fail-closed,未被授予能力码即 403。
  18. -- snapshot → apply → DB verify → restart → **重新登录** → Gate 1→2→3→4
  19. -- (重新登录是必需项:GetOwnBtnPermList 有 7 天缓存 CacheConst.KeyUserButton+userId)
  20. --
  21. -- 幂等:所有 INSERT 带 NOT EXISTS;DELETE 谓词精确且可重复执行。
  22. -- Id 段:1329008900000–1329008999999(已实测该段在 SysRole / SysRoleMenu /
  23. -- SysUserRole / SysTenantMenu 上全部为空)。
  24. --
  25. -- ── 预计影响行数(窗口期实测不符即停止)────────────────────────────────────
  26. -- §1 SysMenu INSERT 9 新能力码
  27. -- §2 SysTenantMenu INSERT 9 仅 UAT 租户 × 9
  28. -- §3 SysRole INSERT 1 仅 UAT 租户的 ROLE_S8_CONFIG_ADMIN
  29. -- §4 SysRoleMenu INSERT 27 见下方逐角色明细
  30. -- §5 SysRoleMenu DELETE 12 UAT 租户 UAT_FUNCTION_USER,15 → 只剩 3 个只读
  31. -- §6 SysUserRole INSERT 1 仅 UATAdminA
  32. -- ────────────────────────────────────────────────────────────────────────
  33. -- 合计 = 59
  34. --
  35. -- ── 执行事实(2026-09-03 已在 aidopdev 应用)─────────────────────────────
  36. -- 以事务 + 逐段「预期影响行数」断言执行,任一不符即整体 ROLLBACK。
  37. -- 实测:§1=9 §2=9 §3=1 §4=27 §5=12 §6=1,合计 59,与上表逐项一致 → COMMIT。
  38. -- 事务内前置校验全过:UAT_FUNCTION_USER 剩余 s8 权限 3(只读三项);
  39. -- 其非 S8 授权 231 前后不变;另一同名角色(租户 838257212780613)15 行不变;
  40. -- ROLE_S8_CONFIG_ADMIN 全库恰 1 个(仅 UAT)。
  41. -- ──────────────────────────────────────────────────────────────────────────
  42. --
  43. -- ── §4 授权依据(每条都对应可查的 UAT 事实,不能证明会用到的一律不授予)──────
  44. -- ROLE_S8_CONFIG_ADMIN → 10 config + debug + exception:create = 12
  45. -- 依据:UATAdminA 需 config/debug;timeline 实测 CREATE 由 UATAdminA 执行
  46. -- ROLE_EQUIP_MAINT → claim,start,verification:submit,create = 4
  47. -- 依据:UATExceptionA 实测执行 CREATE / START_PROGRESS / VERIFY_SUBMITTED,
  48. -- 且名下 GEX-019-EQ(IN_PROGRESS) 与新异常 1329909430019(NEW) 待处理
  49. -- ROLE_PURCHASER → claim,start,verification:submit = 3
  50. -- 依据:emp 3183(梁军/UATPurchaseA) 为 GEX-002-MP / GEX-002-ETA / GEX-012-MP 处理人
  51. -- ROLE_WH_INBOUND → claim,start,verification:submit = 3
  52. -- ROLE_WH_OUTBOUND → claim,start,verification:submit = 3
  53. -- 依据:emp 3179(章立坤/UATWarehouseA) 名下 3 条**未闭**:
  54. -- GEX-019-KIT(ASSIGNED) / GEX-020-SHIP(IN_PROGRESS) / GEX-020-PEND(ASSIGNED)
  55. -- ROLE_QC → verification:approve, verification:reject = 2
  56. -- 依据:UATQualityA 实测执行 VERIFY_APPROVED / VERIFY_REJECTED
  57. -- ROLE_ORDER_PLANNER / ROLE_PRODUCTION_PLANNER → 0
  58. -- 依据:UATPlanA 在 UAT 数据中**从未**作为任何异常的处理人/检验人;
  59. -- 只读能力经保留的 UAT_FUNCTION_USER 获得,无需额外授权
  60. --
  61. -- 刻意未授予(无 UAT 证据,需要时再单独加,勿"保险式"补齐):
  62. -- s8:exception:assign(转派) · s8:exception:reject · s8:exception:upgrade
  63. -- · s8:exception:comment · Purchase/Warehouse 的 s8:exception:create
  64. -- ──────────────────────────────────────────────────────────────────────────
  65. -- =====================================================================================
  66. -- =====================================================================================
  67. -- §0 执行前只读快照(窗口期第 1 步,留档后再执行写入)
  68. -- =====================================================================================
  69. -- (0.1) UAT 租户 UAT_FUNCTION_USER 当前 s8:* 权限(预期 15 行)
  70. SELECT m.`Id` AS MenuId, m.`Permission`
  71. FROM `SysRoleMenu` rm JOIN `SysMenu` m ON m.`Id` = rm.`MenuId`
  72. WHERE rm.`RoleId` = (SELECT `Id` FROM `SysRole` WHERE `Code`='UAT_FUNCTION_USER' AND `TenantId`=838257186181189)
  73. AND m.`Permission` LIKE 's8:%' ORDER BY m.`Permission`;
  74. -- (0.2) 6 个验收账号当前角色
  75. SELECT u.`Account`, r.`Code` AS RoleCode
  76. FROM `SysUser` u JOIN `SysUserRole` ur ON ur.`UserId` = u.`Id` JOIN `SysRole` r ON r.`Id` = ur.`RoleId`
  77. WHERE u.`TenantId` = 838257186181189 ORDER BY u.`Account`, r.`Code`;
  78. -- (0.3) ROLE_S8_CONFIG_ADMIN 是否已存在(预期 0)
  79. SELECT COUNT(*) AS existing_config_admin FROM `SysRole` WHERE `Code` = 'ROLE_S8_CONFIG_ADMIN';
  80. -- (0.4) §5 预计删除行数(预期 12)
  81. SELECT COUNT(*) AS expect_delete
  82. FROM `SysRoleMenu` rm JOIN `SysMenu` m ON m.`Id` = rm.`MenuId`
  83. WHERE rm.`RoleId` = (SELECT `Id` FROM `SysRole` WHERE `Code`='UAT_FUNCTION_USER' AND `TenantId`=838257186181189)
  84. AND m.`Permission` LIKE 's8:%'
  85. AND m.`Permission` NOT IN ('s8:exception:read','s8:config:read','s8:dashboard:read');
  86. -- (0.5) 该角色非 S8 授权数(§5 前后必须完全相等;实测基线 = 231)
  87. SELECT COUNT(*) AS non_s8_rows
  88. FROM `SysRoleMenu` rm JOIN `SysMenu` m ON m.`Id` = rm.`MenuId`
  89. WHERE rm.`RoleId` = (SELECT `Id` FROM `SysRole` WHERE `Code`='UAT_FUNCTION_USER' AND `TenantId`=838257186181189)
  90. AND (m.`Permission` IS NULL OR m.`Permission` NOT LIKE 's8:%');
  91. -- (0.6) 另一个同名角色(租户 838257212780613)基线,§5 后必须不变(预期 15)
  92. SELECT COUNT(*) AS other_tenant_s8_rows
  93. FROM `SysRoleMenu` rm JOIN `SysMenu` m ON m.`Id` = rm.`MenuId`
  94. WHERE rm.`RoleId` = (SELECT `Id` FROM `SysRole` WHERE `Code`='UAT_FUNCTION_USER' AND `TenantId`=838257212780613)
  95. AND m.`Permission` LIKE 's8:%';
  96. -- =====================================================================================
  97. -- §1 新增 9 个 S8 能力码(SysMenu Type=3 Btn)。菜单本身是全局元数据,
  98. -- 租户可见性由 §2 的 SysTenantMenu 控制,故此处不带租户维度。
  99. -- =====================================================================================
  100. INSERT INTO `SysMenu` (`Id`,`Pid`,`Title`,`Name`,`Type`,`Permission`,`OrderNo`,`Status`,
  101. `IsHide`,`IsKeepAlive`,`IsAffix`,`IsIframe`,`Remark`,`CreateTime`)
  102. SELECT src.* FROM (
  103. 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
  104. UNION ALL SELECT 1329008000116, 1329008000015, '维护数据源', 'aidopS8Btn16', 3, 's8:config:data-source', 250, 1, 0, 1, 0, 0, 'S8 权限点 s8:config:data-source', NOW()
  105. UNION ALL SELECT 1329008000117, 1329008000017, '维护异常类型', 'aidopS8Btn17', 3, 's8:config:exception-type', 260, 1, 0, 1, 0, 0, 'S8 权限点 s8:config:exception-type', NOW()
  106. UNION ALL SELECT 1329008000118, 1329008000012, '维护通知分层', 'aidopS8Btn18', 3, 's8:config:notification', 270, 1, 0, 1, 0, 0, 'S8 权限点 s8:config:notification', NOW()
  107. UNION ALL SELECT 1329008000119, 1329008000011, '维护场景配置', 'aidopS8Btn19', 3, 's8:config:scene', 280, 1, 0, 1, 0, 0, 'S8 权限点 s8:config:scene', NOW()
  108. UNION ALL SELECT 1329008000120, 1329008000019, '维护KPI目标值', 'aidopS8Btn20', 3, 's8:config:kpi', 290, 1, 0, 1, 0, 0, 'S8 权限点 s8:config:kpi', NOW()
  109. UNION ALL SELECT 1329008000121, 1329008000018, '维护大屏卡片', 'aidopS8Btn21', 3, 's8:config:dashboard', 300, 1, 0, 1, 0, 0, 'S8 权限点 s8:config:dashboard', NOW()
  110. UNION ALL SELECT 1329008000122, 1329008000010, '补充说明', 'aidopS8Btn22', 3, 's8:exception:comment', 90, 1, 0, 1, 0, 0, 'S8 权限点 s8:exception:comment', NOW()
  111. UNION ALL SELECT 1329008000123, 1329008000004, '运维调试接口', 'aidopS8Btn23', 3, 's8:debug:run', 900, 1, 0, 1, 0, 0, 'S8 权限点 s8:debug:run', NOW()
  112. ) AS src
  113. WHERE NOT EXISTS (SELECT 1 FROM `SysMenu` m WHERE m.`Id` = src.Id OR m.`Permission` = src.Permission);
  114. -- =====================================================================================
  115. -- §2 租户可见性 —— **仅 UAT 租户**。
  116. -- GetOwnBtnPermList 对非超管 INNER JOIN SysTenantMenu,不建这些行则取不到新能力码。
  117. -- 其它租户刻意不建:非本轮验收作用域(见文件头 KNOWN RISK)。
  118. -- =====================================================================================
  119. INSERT INTO `SysTenantMenu` (`Id`, `TenantId`, `MenuId`)
  120. SELECT 1329008900000 + ROW_NUMBER() OVER (ORDER BY s.MenuId), 838257186181189, s.MenuId
  121. FROM (
  122. SELECT m.`Id` AS MenuId FROM `SysMenu` m
  123. WHERE m.`Id` BETWEEN 1329008000115 AND 1329008000123
  124. AND NOT EXISTS (SELECT 1 FROM `SysTenantMenu` x
  125. WHERE x.`TenantId` = 838257186181189 AND x.`MenuId` = m.`Id`)
  126. ) s;
  127. -- =====================================================================================
  128. -- §3 仅为 UAT 租户创建 ROLE_S8_CONFIG_ADMIN
  129. -- ⚠️ 不加入 S8CanonicalRole.BusinessRoles —— 那是 CFG_NOTIFY 角色候选的唯一来源,
  130. -- 已被 Runtime Certification 钉死「恰好 7 个」,加进去会当场改掉已认证契约。
  131. -- =====================================================================================
  132. INSERT INTO `SysRole` (`Id`,`Name`,`Code`,`OrderNo`,`DataScope`,`Remark`,`Status`,`TenantId`,`CreateTime`)
  133. SELECT 1329008910001, 'S8配置管理员', 'ROLE_S8_CONFIG_ADMIN', 810, 1,
  134. 'S8-P0-4:S8 配置类接口的授权载体;能力经 SysRoleMenu 授予,业务代码不判 RoleCode。仅 UAT 租户。',
  135. 1, 838257186181189, NOW()
  136. FROM DUAL
  137. WHERE NOT EXISTS (SELECT 1 FROM `SysRole` r
  138. WHERE r.`TenantId` = 838257186181189 AND r.`Code` = 'ROLE_S8_CONFIG_ADMIN');
  139. -- =====================================================================================
  140. -- §4 授权(全部 additive;仅 UAT 租户的角色;每条对应文件头列出的 UAT 依据)
  141. -- 只读能力(exception:read / config:read / dashboard:read)由保留的 UAT_FUNCTION_USER
  142. -- 统一提供,6 个账号都有,故此处不重复授予。
  143. -- =====================================================================================
  144. INSERT INTO `SysRoleMenu` (`Id`, `RoleId`, `MenuId`)
  145. SELECT 1329008920000 + ROW_NUMBER() OVER (ORDER BY s.RoleId, s.MenuId), s.RoleId, s.MenuId
  146. FROM (
  147. -- §4.1 ROLE_S8_CONFIG_ADMIN → 10 个 config + debug + exception:create (12)
  148. SELECT r.`Id` AS RoleId, m.`Id` AS MenuId
  149. FROM `SysRole` r CROSS JOIN `SysMenu` m
  150. WHERE r.`TenantId` = 838257186181189 AND r.`Code` = 'ROLE_S8_CONFIG_ADMIN'
  151. AND m.`Permission` IN ('s8:config:read','s8:config:operator:bind','s8:config:role:write',
  152. 's8:config:watch-rule','s8:config:data-source','s8:config:exception-type',
  153. 's8:config:notification','s8:config:scene','s8:config:kpi','s8:config:dashboard',
  154. 's8:debug:run','s8:exception:create')
  155. UNION ALL
  156. -- §4.2 ROLE_EQUIP_MAINT(UATExceptionA)→ operator 最小集 + 提报 (4)
  157. SELECT r.`Id`, m.`Id`
  158. FROM `SysRole` r CROSS JOIN `SysMenu` m
  159. WHERE r.`TenantId` = 838257186181189 AND r.`Code` = 'ROLE_EQUIP_MAINT'
  160. AND m.`Permission` IN ('s8:exception:claim','s8:exception:start','s8:verification:submit',
  161. 's8:exception:create')
  162. UNION ALL
  163. -- §4.3 采购 / 仓储(UATPurchaseA、UATWarehouseA)→ operator 最小集 (3 × 3 = 9)
  164. SELECT r.`Id`, m.`Id`
  165. FROM `SysRole` r CROSS JOIN `SysMenu` m
  166. WHERE r.`TenantId` = 838257186181189
  167. AND r.`Code` IN ('ROLE_PURCHASER','ROLE_WH_INBOUND','ROLE_WH_OUTBOUND')
  168. AND m.`Permission` IN ('s8:exception:claim','s8:exception:start','s8:verification:submit')
  169. UNION ALL
  170. -- §4.4 ROLE_QC(UATQualityA)→ 仅质检复核,**不含**任何 operator 能力 (2)
  171. SELECT r.`Id`, m.`Id`
  172. FROM `SysRole` r CROSS JOIN `SysMenu` m
  173. WHERE r.`TenantId` = 838257186181189 AND r.`Code` = 'ROLE_QC'
  174. AND m.`Permission` IN ('s8:verification:approve','s8:verification:reject')
  175. ) s
  176. WHERE NOT EXISTS (SELECT 1 FROM `SysRoleMenu` x WHERE x.`RoleId` = s.RoleId AND x.`MenuId` = s.MenuId);
  177. -- =====================================================================================
  178. -- §5 收口 UAT 租户的 UAT_FUNCTION_USER:只保留 3 个公共只读,其余 S8 能力全部撤销
  179. -- (config 写 / debug / operator / verification 由各自专用角色重新提供)
  180. --
  181. -- 约束逐条落实:
  182. -- · RoleId 按 (Code, TenantId) 精确定位 —— 该 Code 在 838257212780613 也存在,
  183. -- §0.6 / §7.4 前后对比确保另一租户不受影响;
  184. -- · 只删 s8:* 且不在保留名单内的行,**不触碰任何非 S8 权限**(基线 231 行,前后相等);
  185. -- · 不重建菜单、不覆盖 SysRoleMenu。
  186. -- 预计 12 行(15 - 3)。含已废弃的 s8:exception:close(无对应 Action,属 operator 类,一并收回)。
  187. -- =====================================================================================
  188. DELETE rm FROM `SysRoleMenu` rm
  189. JOIN `SysMenu` m ON m.`Id` = rm.`MenuId`
  190. WHERE rm.`RoleId` = (SELECT `Id` FROM `SysRole` WHERE `Code`='UAT_FUNCTION_USER' AND `TenantId`=838257186181189)
  191. AND m.`Permission` LIKE 's8:%'
  192. AND m.`Permission` NOT IN ('s8:exception:read','s8:config:read','s8:dashboard:read');
  193. -- =====================================================================================
  194. -- §6 仅把 ROLE_S8_CONFIG_ADMIN 绑给 UATAdminA(预计 1 行)
  195. -- 明确不绑:UATExceptionA / UATQualityA / UATPlanA / UATPurchaseA / UATWarehouseA
  196. -- =====================================================================================
  197. INSERT INTO `SysUserRole` (`Id`, `UserId`, `RoleId`)
  198. SELECT 1329008990001, u.`Id`, r.`Id`
  199. FROM `SysUser` u
  200. JOIN `SysRole` r ON r.`TenantId` = u.`TenantId` AND r.`Code` = 'ROLE_S8_CONFIG_ADMIN'
  201. WHERE u.`Account` = 'UATAdminA' AND u.`TenantId` = 838257186181189
  202. AND NOT EXISTS (SELECT 1 FROM `SysUserRole` x WHERE x.`UserId` = u.`Id` AND x.`RoleId` = r.`Id`);
  203. -- =====================================================================================
  204. -- §7 执行后 DB verify(窗口期第 3 步;任一不符即停止,不得 restart)
  205. -- =====================================================================================
  206. SELECT COUNT(*) AS new_menus FROM `SysMenu` WHERE `Id` BETWEEN 1329008000115 AND 1329008000123; -- 期望 9
  207. SELECT COUNT(*) AS uat_tenant_menus FROM `SysTenantMenu` WHERE `TenantId`=838257186181189
  208. AND `MenuId` BETWEEN 1329008000115 AND 1329008000123; -- 期望 9
  209. SELECT COUNT(*) AS config_admin_roles FROM `SysRole` WHERE `Code`='ROLE_S8_CONFIG_ADMIN'; -- 期望 1(仅 UAT)
  210. SELECT COUNT(*) AS new_grants FROM `SysRoleMenu` WHERE `Id` BETWEEN 1329008920001 AND 1329008920999; -- 期望 27
  211. -- (7.3) UAT_FUNCTION_USER 只剩 3 个只读;非 S8 授权数不变
  212. SELECT m.`Permission` FROM `SysRoleMenu` rm JOIN `SysMenu` m ON m.`Id` = rm.`MenuId`
  213. WHERE rm.`RoleId` = (SELECT `Id` FROM `SysRole` WHERE `Code`='UAT_FUNCTION_USER' AND `TenantId`=838257186181189)
  214. AND m.`Permission` LIKE 's8:%' ORDER BY m.`Permission`;
  215. -- 期望恰好 3 行:s8:config:read / s8:dashboard:read / s8:exception:read
  216. SELECT COUNT(*) AS non_s8_rows_after FROM `SysRoleMenu` rm JOIN `SysMenu` m ON m.`Id` = rm.`MenuId`
  217. WHERE rm.`RoleId` = (SELECT `Id` FROM `SysRole` WHERE `Code`='UAT_FUNCTION_USER' AND `TenantId`=838257186181189)
  218. AND (m.`Permission` IS NULL OR m.`Permission` NOT LIKE 's8:%'); -- 期望 231
  219. -- (7.4) 另一租户同名角色未被误伤
  220. SELECT COUNT(*) AS other_tenant_s8_rows FROM `SysRoleMenu` rm JOIN `SysMenu` m ON m.`Id` = rm.`MenuId`
  221. WHERE rm.`RoleId` = (SELECT `Id` FROM `SysRole` WHERE `Code`='UAT_FUNCTION_USER' AND `TenantId`=838257212780613)
  222. AND m.`Permission` LIKE 's8:%'; -- 期望 15(不变)
  223. -- (7.5) 6 个账号的有效 S8 能力(Gate 4 逐项预期)
  224. SELECT u.`Account`, m.`Permission`
  225. FROM `SysUser` u
  226. JOIN `SysUserRole` ur ON ur.`UserId` = u.`Id`
  227. JOIN `SysRoleMenu` rm ON rm.`RoleId` = ur.`RoleId`
  228. JOIN `SysMenu` m ON m.`Id` = rm.`MenuId`
  229. WHERE u.`TenantId` = 838257186181189 AND m.`Permission` LIKE 's8:%'
  230. GROUP BY u.`Account`, m.`Permission` ORDER BY u.`Account`, m.`Permission`;
  231. -- 期望:
  232. -- UATAdminA : 3 只读 + 10 config + debug:run + exception:create (15)
  233. -- UATExceptionA : 3 只读 + claim/start/verification:submit/create (7)
  234. -- UATQualityA : 3 只读 + verification:approve/reject (5)
  235. -- UATPurchaseA : 3 只读 + claim/start/verification:submit (6)
  236. -- UATWarehouseA : 3 只读 + claim/start/verification:submit (6,两角色并集去重后同)
  237. -- UATPlanA : 3 只读 (3)