-- =====================================================================================
-- 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）
