DeleteLobsterProfileConfigMenu.java 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239
  1. import java.sql.*;
  2. import java.util.ArrayList;
  3. import java.util.List;
  4. /**
  5. * 删除所有租户库中的"用户画像配置"菜单(menu_id=29922)。
  6. *
  7. * 影响表(每个租户库):
  8. * 1. sys_menu (租户管理端菜单)
  9. * 2. sys_role_menu (租户管理端角色菜单关联)
  10. * 3. company_menu (租户销售端菜单)
  11. * 4. company_role_menu (租户销售端角色菜单关联)
  12. *
  13. * 删除条件:
  14. * - menu_id = 29922
  15. * - 或 path='profile-config' AND component='lobster/profile-config/index'(兜底)
  16. *
  17. * 用法:
  18. * java DeleteLobsterProfileConfigMenu # 真实执行
  19. * java DeleteLobsterProfileConfigMenu dry-run # 仅预览,不执行删除
  20. */
  21. public class DeleteLobsterProfileConfigMenu {
  22. private static final long MENU_ID = 29922L;
  23. private static final String PATH = "profile-config";
  24. private static final String COMPONENT = "lobster/profile-config/index";
  25. private static final String MASTER_HOST = getenv("DB_HOST", "cq-cdb-8fjmemkb.sql.tencentcdb.com");
  26. private static final int MASTER_PORT = Integer.parseInt(getenv("DB_PORT", "27220"));
  27. private static final String MASTER_USER = getenv("DB_USER", "root");
  28. private static final String MASTER_PASSWORD = getenv("DB_PASSWORD", "Ylrz_1q2w3e4r5t6y");
  29. private static final String MASTER_DB = getenv("DB_NAME", "ylrz_saas");
  30. public static void main(String[] args) throws Exception {
  31. Class.forName("com.mysql.cj.jdbc.Driver");
  32. boolean dryRun = args.length > 0 && "dry-run".equalsIgnoreCase(args[0]);
  33. System.out.println("=== 删除租户库「用户画像配置」菜单 (menu_id=29922) ===");
  34. System.out.println("dryRun=" + dryRun);
  35. List<Long> tenantIds;
  36. try (Connection master = open(MASTER_HOST, MASTER_PORT, MASTER_DB, MASTER_USER, MASTER_PASSWORD)) {
  37. tenantIds = loadActiveTenantIds(master);
  38. }
  39. int ok = 0;
  40. int skip = 0;
  41. int fail = 0;
  42. for (Long tenantId : tenantIds) {
  43. TenantDb tenant;
  44. try (Connection master = open(MASTER_HOST, MASTER_PORT, MASTER_DB, MASTER_USER, MASTER_PASSWORD)) {
  45. tenant = loadTenant(master, tenantId);
  46. }
  47. if (tenant == null) {
  48. skip++;
  49. continue;
  50. }
  51. System.out.println("\n[TENANT " + tenantId + "] " + tenant.database);
  52. try (Connection conn = open(tenant.host, tenant.port, tenant.database, tenant.user, tenant.password)) {
  53. boolean changed = false;
  54. // 1. 租户管理端 sys_menu
  55. if (tableExists(conn, "sys_menu")) {
  56. int n = deleteMenu(conn, "sys_menu", "sys_role_menu", dryRun);
  57. if (n > 0) {
  58. System.out.println(" sys_menu: deleted rows=" + n);
  59. changed = true;
  60. }
  61. } else {
  62. System.out.println(" [SKIP] no sys_menu");
  63. }
  64. // 2. 租户销售端 company_menu
  65. if (tableExists(conn, "company_menu")) {
  66. int n = deleteMenu(conn, "company_menu", "company_role_menu", dryRun);
  67. if (n > 0) {
  68. System.out.println(" company_menu: deleted rows=" + n);
  69. changed = true;
  70. }
  71. } else {
  72. System.out.println(" [SKIP] no company_menu");
  73. }
  74. if (changed) {
  75. ok++;
  76. } else {
  77. skip++;
  78. }
  79. } catch (Exception e) {
  80. System.out.println(" [ERR] " + e.getMessage());
  81. fail++;
  82. }
  83. }
  84. System.out.println("\n=== SUMMARY ===");
  85. System.out.println("OK=" + ok + " SKIP=" + skip + " FAIL=" + fail);
  86. if (fail > 0) {
  87. System.exit(1);
  88. }
  89. }
  90. /**
  91. * 删除指定菜单表中的记录,并清理对应的 role_menu 关联表。
  92. * 返回删除的菜单行数。
  93. */
  94. private static int deleteMenu(Connection conn, String menuTable, String roleMenuTable, boolean dryRun)
  95. throws SQLException {
  96. // 收集所有匹配的 menu_id(兜底匹配 path+component)
  97. List<Long> ids = new ArrayList<>();
  98. String sql = "SELECT menu_id FROM " + menuTable
  99. + " WHERE menu_id = ? "
  100. + " OR (path = ? AND component = ?)";
  101. try (PreparedStatement ps = conn.prepareStatement(sql)) {
  102. ps.setLong(1, MENU_ID);
  103. ps.setString(2, PATH);
  104. ps.setString(3, COMPONENT);
  105. try (ResultSet rs = ps.executeQuery()) {
  106. while (rs.next()) {
  107. ids.add(rs.getLong(1));
  108. }
  109. }
  110. }
  111. if (ids.isEmpty()) {
  112. return 0;
  113. }
  114. if (dryRun) {
  115. System.out.println(" [DRY-RUN] " + menuTable + " would delete ids=" + ids);
  116. return ids.size();
  117. }
  118. // 先删 role_menu 关联
  119. if (tableExists(conn, roleMenuTable)) {
  120. String in = placeholders(ids.size());
  121. String delRole = "DELETE FROM " + roleMenuTable + " WHERE menu_id IN (" + in + ")";
  122. try (PreparedStatement ps = conn.prepareStatement(delRole)) {
  123. bindIds(ps, ids);
  124. int n = ps.executeUpdate();
  125. if (n > 0) {
  126. System.out.println(" " + roleMenuTable + ": deleted rows=" + n);
  127. }
  128. }
  129. }
  130. // 再删 menu
  131. int total = 0;
  132. String delMenu = "DELETE FROM " + menuTable + " WHERE menu_id = ?";
  133. try (PreparedStatement ps = conn.prepareStatement(delMenu)) {
  134. for (Long id : ids) {
  135. ps.setLong(1, id);
  136. total += ps.executeUpdate();
  137. }
  138. }
  139. return total;
  140. }
  141. private static String placeholders(int n) {
  142. StringBuilder sb = new StringBuilder();
  143. for (int i = 0; i < n; i++) {
  144. if (i > 0) sb.append(',');
  145. sb.append('?');
  146. }
  147. return sb.toString();
  148. }
  149. private static void bindIds(PreparedStatement ps, List<Long> ids) throws SQLException {
  150. int i = 1;
  151. for (Long id : ids) {
  152. ps.setLong(i++, id);
  153. }
  154. }
  155. private static boolean tableExists(Connection conn, String table) throws SQLException {
  156. try (ResultSet rs = conn.getMetaData().getTables(null, null, table, null)) {
  157. return rs.next();
  158. }
  159. }
  160. private static List<Long> loadActiveTenantIds(Connection master) throws SQLException {
  161. List<Long> ids = new ArrayList<>();
  162. try (Statement st = master.createStatement();
  163. ResultSet rs = st.executeQuery("SELECT id FROM tenant_info WHERE status = 1 ORDER BY id")) {
  164. while (rs.next()) {
  165. ids.add(rs.getLong(1));
  166. }
  167. }
  168. return ids;
  169. }
  170. private static TenantDb loadTenant(Connection master, long tenantId) throws SQLException {
  171. try (PreparedStatement ps = master.prepareStatement(
  172. "SELECT db_url, db_account, db_pwd FROM tenant_info WHERE id = ? AND status = 1 LIMIT 1")) {
  173. ps.setLong(1, tenantId);
  174. try (ResultSet rs = ps.executeQuery()) {
  175. if (!rs.next()) {
  176. return null;
  177. }
  178. return parseJdbcUrl(rs.getString("db_url"), rs.getString("db_account"), rs.getString("db_pwd"));
  179. }
  180. }
  181. }
  182. private static TenantDb parseJdbcUrl(String jdbcUrl, String user, String password) {
  183. String body = jdbcUrl.substring("jdbc:mysql://".length());
  184. int slash = body.indexOf('/');
  185. String hostPort = body.substring(0, slash);
  186. String rest = body.substring(slash + 1);
  187. int q = rest.indexOf('?');
  188. String database = q >= 0 ? rest.substring(0, q) : rest;
  189. int colon = hostPort.indexOf(':');
  190. TenantDb t = new TenantDb();
  191. t.host = colon >= 0 ? hostPort.substring(0, colon) : hostPort;
  192. t.port = colon >= 0 ? Integer.parseInt(hostPort.substring(colon + 1)) : 3306;
  193. t.database = database;
  194. t.user = user != null ? user : MASTER_USER;
  195. t.password = password != null ? password : MASTER_PASSWORD;
  196. return t;
  197. }
  198. private static Connection open(String host, int port, String database, String user, String password)
  199. throws SQLException {
  200. return DriverManager.getConnection(
  201. "jdbc:mysql://" + host + ":" + port + "/" + database
  202. + "?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8&allowMultiQueries=true",
  203. user, password);
  204. }
  205. private static String getenv(String key, String defaultValue) {
  206. String v = System.getenv(key);
  207. return v != null && !v.isEmpty() ? v : defaultValue;
  208. }
  209. private static final class TenantDb {
  210. String host;
  211. int port;
  212. String database;
  213. String user;
  214. String password;
  215. }
  216. }