1.0.424.sql 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
  1. -- ===========================================================================
  2. -- UAT 租户占位菜单收口(S0 / S5 / S6 / S7)
  3. --
  4. -- 背景:一批菜单已在 SysMenu 全局登记,但 Component 仍指向通用占位页
  5. -- `/aidop/planning/index`(AidopDemoShell + el-empty「后续在此接入 Ai-DOP
  6. -- 业务页面」),对应前端业务目录与组件均不存在,属「纯菜单占位、零实现」。
  7. -- UAT 租户(SysOrg.Code = 'UATTEST_CHL')的测试账号能正常点进这些空壳页。
  8. --
  9. -- 目标:只让该 UAT 租户看不到这些占位功能。
  10. -- - 不删除、不禁用、不隐藏任何 SysMenu 全局记录
  11. -- - 不影响其余 9 个租户
  12. -- - 不改前端,不硬编码 tenantId
  13. --
  14. -- 机制:菜单可见性 = SysMenu(Type≠Btn ∧ Status=Enable)
  15. -- ∩ SysTenantMenu[登录租户]
  16. -- ∩ SysRoleMenu[用户角色集](含祖先补全)
  17. -- 其中 SysTenantMenu 是唯一的「租户维度」硬白名单
  18. -- (SysMenuService.GetSugarQueryableAndTenantId → InnerJoinIF<SysTenantMenu>),
  19. -- 对该租户内所有账号类型生效(含 SysAdmin 888),且会同时收窄
  20. -- 「该租户内新建角色时的可选菜单范围」
  21. -- (SysRoleMenuService.GrantRoleMenu 只落 SysTenantMenu 允许集合的交集)。
  22. -- 因此从 SysTenantMenu 移除,正是「租户级隐藏」的语义。
  23. --
  24. -- ⚠️ 为什么还要配一张抑制表:
  25. -- AidopMenuLinkSync.EnsureLinked(Plugin.AiDOP/Startup.cs:79,每次进程启动
  26. -- 无条件执行)会把 Ai-DOP 种子菜单回填到**全部租户**的 SysTenantMenu,判据
  27. -- 只有「这行还在吗」,且主键是 (tenantId, menuId) 的确定性哈希 —— 单纯 DELETE
  28. -- 会在下一次 restart_aidop.sh 后以同一主键原样复活。ado_tenant_menu_suppression
  29. -- 给回填逻辑一个「这是有意取消的」显式信号,并由启动逻辑强制执行,重启自愈。
  30. --
  31. -- 范围:12 个占位叶子(S0×1 + S5×4 + S6×5 + S7×2)
  32. -- + 4 个「子项全为占位」的父目录 = 16 对 (TenantId, MenuId)。
  33. -- S0 的父节点「S0 运营建模」下另有「数据建模」真实子树(58 个已实现页面),
  34. -- 故父节点**不摘**。
  35. -- S8 经审计**无**任何 planning 占位菜单,本脚本不涉及 S8。
  36. -- S9 / S4 / M11-M16 的占位菜单**本轮明确不处理**。
  37. --
  38. -- 幂等:CREATE TABLE IF NOT EXISTS / INSERT ... WHERE NOT EXISTS / DELETE,
  39. -- 可重复执行,重跑影响行数为 0。
  40. -- 校验:1.0.424.verify.sql(AutoVersionUpdate 自动逐条真值断言)
  41. -- 对账/回滚:UAT-PLACEHOLDER-MENU-HIDE.ops.sql(人工执行,不会被自动运行)
  42. -- ===========================================================================
  43. -- 租户由稳定业务键解析,不写死数字 Id(与 1.0.420.sql 同口径)
  44. SET @t_uat := (SELECT t.Id FROM SysTenant t JOIN SysOrg o ON o.Id = t.OrgId
  45. WHERE o.Code = 'UATTEST_CHL' AND t.Status = 1 LIMIT 1);
  46. -- ---------------------------------------------------------------------------
  47. -- 00 · 抑制清单表
  48. -- ---------------------------------------------------------------------------
  49. CREATE TABLE IF NOT EXISTS `ado_tenant_menu_suppression` (
  50. `Id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键',
  51. `TenantId` bigint NOT NULL COMMENT '租户 Id(SysTenant.Id)',
  52. `MenuId` bigint NOT NULL COMMENT '菜单 Id(SysMenu.Id)',
  53. `Reason` varchar(256) NULL COMMENT '抑制原因(为何该租户不应看到该菜单)',
  54. `CreatedAt` datetime NOT NULL COMMENT '创建时间',
  55. PRIMARY KEY (`Id`),
  56. UNIQUE KEY `uk_tenant_menu_suppression` (`TenantId`, `MenuId`)
  57. ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='租户级菜单抑制清单';
  58. -- ---------------------------------------------------------------------------
  59. -- 01 · 占位叶子(15 条)
  60. -- 双重业务键:Path 命中 + Component 必须仍是占位页。
  61. -- 若某条日后被换成真实组件,这里自然不再命中,不会误抑制真实功能。
  62. -- ---------------------------------------------------------------------------
  63. INSERT INTO ado_tenant_menu_suppression (TenantId, MenuId, Reason, CreatedAt)
  64. SELECT @t_uat, m.Id, CONCAT('UAT 收口:未实现占位页 ', m.Path), NOW()
  65. FROM SysMenu m
  66. WHERE @t_uat IS NOT NULL
  67. AND m.Component = '/aidop/planning/index'
  68. AND m.Path IN (
  69. -- S0 业务建模(FUNC-S0-065)
  70. -- 该条 SysMenu.Status 已为 2/停用、当前本就不渲染;纳入抑制是为了口径一致 +
  71. -- 防御「日后有人把 Status 改回 1 时它在 UAT 租户重新冒出来」。
  72. '/aidop/s0/002',
  73. -- S5 物料仓储看板(FUNC-S5-014 ~ 017)
  74. '/aidop/s5/warehouse-kanban/receiving-task',
  75. '/aidop/s5/warehouse-kanban/issuing-task',
  76. '/aidop/s5/warehouse-kanban/turnover-report',
  77. '/aidop/s5/warehouse-kanban/age-analysis',
  78. -- S6 生产记录管理(FUNC-S6-001)
  79. '/aidop/s6/production-record/order-list',
  80. -- S6 生产执行看板(FUNC-S6-005 ~ 008)
  81. '/aidop/s6/execution-kanban/work-order',
  82. '/aidop/s6/execution-kanban/workshop-efficiency',
  83. '/aidop/s6/execution-kanban/equipment-oee',
  84. '/aidop/s6/execution-kanban/labor-hours',
  85. -- S7 成品仓储看板(FUNC-S7-005 / 006)
  86. '/aidop/s7/finished-warehouse-kanban/shipment-task',
  87. '/aidop/s7/finished-warehouse-kanban/sluggish'
  88. )
  89. AND NOT EXISTS (SELECT 1 FROM ado_tenant_menu_suppression s
  90. WHERE s.TenantId = @t_uat AND s.MenuId = m.Id);
  91. -- ---------------------------------------------------------------------------
  92. -- 02 · 纯占位父目录(5 条)
  93. -- 菜单树不会自动裁掉空父节点:后端 ToTree 无 children.Any() 判断,
  94. -- ExpandMenuIdsWithAncestorsAsync 只正向补祖先;前端 vertical.vue 对
  95. -- children.length===0 的目录走 v-else 渲染成**可点击的死链 el-menu-item**。
  96. -- 所以父目录必须显式一并移除。
  97. --
  98. -- 安全断言:只在「该目录下所有非按钮子菜单都是占位」时才登记。
  99. -- 任何一个真实子菜单存在 → 整条不命中 → 绝不会摘掉还有真实功能的目录。
  100. -- ---------------------------------------------------------------------------
  101. INSERT INTO ado_tenant_menu_suppression (TenantId, MenuId, Reason, CreatedAt)
  102. SELECT @t_uat, m.Id, CONCAT('UAT 收口:子项全为占位,避免留下空壳目录 ', m.Path), NOW()
  103. FROM SysMenu m
  104. WHERE @t_uat IS NOT NULL
  105. AND m.Path IN (
  106. '/aidop/s5/warehouse-kanban', -- 物料仓储看板
  107. '/aidop/s6/production-record', -- 生产记录管理
  108. '/aidop/s6/execution-kanban', -- 生产执行看板
  109. '/aidop/s7/finished-warehouse-kanban' -- 成品仓储看板
  110. )
  111. -- 必须确有子菜单(防止把一个孤立节点当目录摘掉)
  112. AND EXISTS (SELECT 1 FROM SysMenu c WHERE c.Pid = m.Id AND c.Type <> 3)
  113. -- 且非按钮子菜单必须 100% 是占位页
  114. AND NOT EXISTS (
  115. SELECT 1 FROM SysMenu c
  116. WHERE c.Pid = m.Id
  117. AND c.Type <> 3
  118. AND (c.Component IS NULL OR c.Component <> '/aidop/planning/index')
  119. )
  120. AND NOT EXISTS (SELECT 1 FROM ado_tenant_menu_suppression s
  121. WHERE s.TenantId = @t_uat AND s.MenuId = m.Id);
  122. -- ---------------------------------------------------------------------------
  123. -- 03 · 按抑制清单移除该租户的菜单授权
  124. -- 只删 SysTenantMenu,且只删 @t_uat 这一个租户的行。
  125. -- **不动 SysRoleMenu** —— 角色授权保留,撤销抑制后可精确还原原可见性。
  126. -- **不动 SysMenu** —— 全局菜单定义零改动。
  127. -- ---------------------------------------------------------------------------
  128. DELETE tm FROM SysTenantMenu tm
  129. JOIN ado_tenant_menu_suppression s
  130. ON s.TenantId = tm.TenantId AND s.MenuId = tm.MenuId
  131. WHERE tm.TenantId = @t_uat;