DeleteLegacyLobsterMenu2951.java 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234
  1. import java.sql.*;
  2. import java.util.ArrayList;
  3. import java.util.Collections;
  4. import java.util.LinkedHashSet;
  5. import java.util.List;
  6. import java.util.Set;
  7. /**
  8. * Delete legacy lobster menu tree (root menu_id=2951, path=lobster) from tenant sys_menu.
  9. * Keeps the canonical tree under menu_id=29900 (path=/lobster).
  10. */
  11. public class DeleteLegacyLobsterMenu2951 {
  12. private static final String MASTER_HOST = getenv("DB_HOST", "cq-cdb-8fjmemkb.sql.tencentcdb.com");
  13. private static final int MASTER_PORT = Integer.parseInt(getenv("DB_PORT", "27220"));
  14. private static final String MASTER_USER = getenv("DB_USER", "root");
  15. private static final String MASTER_PASSWORD = getenv("DB_PASSWORD", "Ylrz_1q2w3e4r5t6y");
  16. private static final String MASTER_DB = getenv("DB_NAME", "ylrz_saas");
  17. private static final long LEGACY_ROOT = 2951L;
  18. public static void main(String[] args) throws Exception {
  19. Class.forName("com.mysql.cj.jdbc.Driver");
  20. boolean dryRun = args.length > 0 && "dry-run".equalsIgnoreCase(args[0]);
  21. System.out.println("=== Delete legacy lobster menu tree (2951) from tenant sys_menu ===");
  22. System.out.println("dryRun=" + dryRun);
  23. List<Long> tenantIds;
  24. try (Connection master = open(MASTER_HOST, MASTER_PORT, MASTER_DB, MASTER_USER, MASTER_PASSWORD)) {
  25. tenantIds = loadActiveTenantIds(master);
  26. }
  27. int ok = 0;
  28. int skip = 0;
  29. int fail = 0;
  30. for (Long tenantId : tenantIds) {
  31. TenantDb tenant;
  32. try (Connection master = open(MASTER_HOST, MASTER_PORT, MASTER_DB, MASTER_USER, MASTER_PASSWORD)) {
  33. tenant = loadTenant(master, tenantId);
  34. }
  35. if (tenant == null) {
  36. skip++;
  37. continue;
  38. }
  39. System.out.println("\n[TENANT " + tenantId + "] " + tenant.database);
  40. try (Connection conn = open(tenant.host, tenant.port, tenant.database, tenant.user, tenant.password)) {
  41. if (!tableExists(conn, "sys_menu")) {
  42. System.out.println(" [SKIP] no sys_menu");
  43. skip++;
  44. continue;
  45. }
  46. if (!menuExists(conn, LEGACY_ROOT)) {
  47. System.out.println(" [SKIP] menu 2951 not found");
  48. skip++;
  49. continue;
  50. }
  51. Set<Long> ids = collectSubtree(conn, LEGACY_ROOT);
  52. System.out.println(" subtree size=" + ids.size() + " ids=" + ids);
  53. if (dryRun) {
  54. ok++;
  55. continue;
  56. }
  57. deleteRoleMenuRefs(conn, ids);
  58. int deleted = deleteMenus(conn, ids);
  59. printRoots(conn);
  60. System.out.println(" [OK] deleted sys_menu rows=" + deleted);
  61. ok++;
  62. } catch (Exception e) {
  63. System.out.println(" [ERR] " + e.getMessage());
  64. fail++;
  65. }
  66. }
  67. System.out.println("\n=== SUMMARY ===");
  68. System.out.println("OK=" + ok + " SKIP=" + skip + " FAIL=" + fail);
  69. if (fail > 0) {
  70. System.exit(1);
  71. }
  72. }
  73. private static Set<Long> collectSubtree(Connection conn, long rootId) throws SQLException {
  74. Set<Long> all = new LinkedHashSet<>();
  75. collectChildren(conn, rootId, all);
  76. all.add(rootId);
  77. return all;
  78. }
  79. private static void collectChildren(Connection conn, long parentId, Set<Long> acc) throws SQLException {
  80. String sql = "SELECT menu_id FROM sys_menu WHERE parent_id = ?";
  81. try (PreparedStatement ps = conn.prepareStatement(sql)) {
  82. ps.setLong(1, parentId);
  83. try (ResultSet rs = ps.executeQuery()) {
  84. while (rs.next()) {
  85. long id = rs.getLong(1);
  86. if (acc.add(id)) {
  87. collectChildren(conn, id, acc);
  88. }
  89. }
  90. }
  91. }
  92. }
  93. private static void deleteRoleMenuRefs(Connection conn, Set<Long> menuIds) throws SQLException {
  94. if (!tableExists(conn, "sys_role_menu") || menuIds.isEmpty()) {
  95. return;
  96. }
  97. String in = placeholders(menuIds.size());
  98. String sql = "DELETE FROM sys_role_menu WHERE menu_id IN (" + in + ")";
  99. try (PreparedStatement ps = conn.prepareStatement(sql)) {
  100. bindIds(ps, menuIds);
  101. int n = ps.executeUpdate();
  102. if (n > 0) {
  103. System.out.println(" deleted sys_role_menu rows=" + n);
  104. }
  105. }
  106. }
  107. private static int deleteMenus(Connection conn, Set<Long> menuIds) throws SQLException {
  108. List<Long> ordered = new ArrayList<>(menuIds);
  109. Collections.sort(ordered, Collections.reverseOrder());
  110. int total = 0;
  111. String sql = "DELETE FROM sys_menu WHERE menu_id = ?";
  112. try (PreparedStatement ps = conn.prepareStatement(sql)) {
  113. for (Long id : ordered) {
  114. ps.setLong(1, id);
  115. total += ps.executeUpdate();
  116. }
  117. }
  118. return total;
  119. }
  120. private static void printRoots(Connection conn) throws SQLException {
  121. String sql = "SELECT menu_id, menu_name, path, visible FROM sys_menu "
  122. + "WHERE parent_id = 0 AND (menu_id = 29900 OR path LIKE '%lobster%') ORDER BY menu_id";
  123. try (Statement st = conn.createStatement(); ResultSet rs = st.executeQuery(sql)) {
  124. while (rs.next()) {
  125. System.out.printf(" remaining root id=%d path=%s visible=%s name=%s%n",
  126. rs.getLong(1), rs.getString(3), rs.getString(4), rs.getString(2));
  127. }
  128. }
  129. }
  130. private static boolean menuExists(Connection conn, long menuId) throws SQLException {
  131. try (PreparedStatement ps = conn.prepareStatement("SELECT 1 FROM sys_menu WHERE menu_id = ? LIMIT 1")) {
  132. ps.setLong(1, menuId);
  133. try (ResultSet rs = ps.executeQuery()) {
  134. return rs.next();
  135. }
  136. }
  137. }
  138. private static String placeholders(int n) {
  139. StringBuilder sb = new StringBuilder();
  140. for (int i = 0; i < n; i++) {
  141. if (i > 0) sb.append(',');
  142. sb.append('?');
  143. }
  144. return sb.toString();
  145. }
  146. private static void bindIds(PreparedStatement ps, Set<Long> ids) throws SQLException {
  147. int i = 1;
  148. for (Long id : ids) {
  149. ps.setLong(i++, id);
  150. }
  151. }
  152. private static boolean tableExists(Connection conn, String table) throws SQLException {
  153. try (ResultSet rs = conn.getMetaData().getTables(null, null, table, null)) {
  154. return rs.next();
  155. }
  156. }
  157. private static List<Long> loadActiveTenantIds(Connection master) throws SQLException {
  158. List<Long> ids = new ArrayList<>();
  159. try (Statement st = master.createStatement();
  160. ResultSet rs = st.executeQuery("SELECT id FROM tenant_info WHERE status = 1 ORDER BY id")) {
  161. while (rs.next()) {
  162. ids.add(rs.getLong(1));
  163. }
  164. }
  165. return ids;
  166. }
  167. private static TenantDb loadTenant(Connection master, long tenantId) throws SQLException {
  168. try (PreparedStatement ps = master.prepareStatement(
  169. "SELECT db_url, db_account, db_pwd FROM tenant_info WHERE id = ? AND status = 1 LIMIT 1")) {
  170. ps.setLong(1, tenantId);
  171. try (ResultSet rs = ps.executeQuery()) {
  172. if (!rs.next()) {
  173. return null;
  174. }
  175. return parseJdbcUrl(rs.getString("db_url"), rs.getString("db_account"), rs.getString("db_pwd"));
  176. }
  177. }
  178. }
  179. private static TenantDb parseJdbcUrl(String jdbcUrl, String user, String password) {
  180. String body = jdbcUrl.substring("jdbc:mysql://".length());
  181. int slash = body.indexOf('/');
  182. String hostPort = body.substring(0, slash);
  183. String rest = body.substring(slash + 1);
  184. int q = rest.indexOf('?');
  185. String database = q >= 0 ? rest.substring(0, q) : rest;
  186. int colon = hostPort.indexOf(':');
  187. TenantDb t = new TenantDb();
  188. t.host = colon >= 0 ? hostPort.substring(0, colon) : hostPort;
  189. t.port = colon >= 0 ? Integer.parseInt(hostPort.substring(colon + 1)) : 3306;
  190. t.database = database;
  191. t.user = user != null ? user : MASTER_USER;
  192. t.password = password != null ? password : MASTER_PASSWORD;
  193. return t;
  194. }
  195. private static Connection open(String host, int port, String database, String user, String password)
  196. throws SQLException {
  197. return DriverManager.getConnection(
  198. "jdbc:mysql://" + host + ":" + port + "/" + database
  199. + "?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8&allowMultiQueries=true",
  200. user, password);
  201. }
  202. private static String getenv(String key, String defaultValue) {
  203. String v = System.getenv(key);
  204. return v != null && !v.isEmpty() ? v : defaultValue;
  205. }
  206. private static final class TenantDb {
  207. String host;
  208. int port;
  209. String database;
  210. String user;
  211. String password;
  212. }
  213. }