RenameLobsterSemanticMenu.java 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202
  1. import java.sql.*;
  2. import java.util.ArrayList;
  3. import java.util.List;
  4. /**
  5. * 批量更新菜单名:「租户语义关键词」→「语义关键词」(menu_id=29933)
  6. *
  7. * 影响范围:
  8. * 主库:
  9. * 1. sys_menu (admin 总后台菜单)
  10. * 2. tenant_sys_menu (租户管理端模板表)
  11. * 3. tenant_company_menu (租户销售端模板表)
  12. * 各租户库:
  13. * 4. sys_menu (租户管理端菜单)
  14. * 5. company_menu (租户销售端菜单)
  15. *
  16. * 用法:
  17. * java RenameLobsterSemanticMenu dry-run # 仅预览
  18. * java RenameLobsterSemanticMenu # 真实执行
  19. */
  20. public class RenameLobsterSemanticMenu {
  21. private static final long MENU_ID = 29933L;
  22. private static final String OLD_NAME = "租户语义关键词";
  23. private static final String NEW_NAME = "语义关键词";
  24. private static final String MASTER_HOST = getenv("DB_HOST", "cq-cdb-8fjmemkb.sql.tencentcdb.com");
  25. private static final int MASTER_PORT = Integer.parseInt(getenv("DB_PORT", "27220"));
  26. private static final String MASTER_USER = getenv("DB_USER", "root");
  27. private static final String MASTER_PASSWORD = getenv("DB_PASSWORD", "Ylrz_1q2w3e4r5t6y");
  28. private static final String MASTER_DB = getenv("DB_NAME", "ylrz_saas");
  29. public static void main(String[] args) throws Exception {
  30. Class.forName("com.mysql.cj.jdbc.Driver");
  31. boolean dryRun = args.length > 0 && "dry-run".equalsIgnoreCase(args[0]);
  32. System.out.println("=== 重命名菜单: 「" + OLD_NAME + "」→「" + NEW_NAME + "」 (menu_id=" + MENU_ID + ") ===");
  33. System.out.println("dryRun=" + dryRun);
  34. int masterOk = 0;
  35. int tenantOk = 0;
  36. int tenantSkip = 0;
  37. int tenantFail = 0;
  38. // ===== 1. 主库 =====
  39. System.out.println("\n[MASTER] " + MASTER_DB);
  40. try (Connection master = open(MASTER_HOST, MASTER_PORT, MASTER_DB, MASTER_USER, MASTER_PASSWORD)) {
  41. masterOk += updateMenu(master, "sys_menu", dryRun);
  42. masterOk += updateMenu(master, "tenant_sys_menu", dryRun);
  43. masterOk += updateMenu(master, "tenant_company_menu", dryRun);
  44. } catch (Exception e) {
  45. System.out.println(" [ERR] " + e.getMessage());
  46. }
  47. // ===== 2. 各租户库 =====
  48. List<Long> tenantIds;
  49. try (Connection master = open(MASTER_HOST, MASTER_PORT, MASTER_DB, MASTER_USER, MASTER_PASSWORD)) {
  50. tenantIds = loadActiveTenantIds(master);
  51. }
  52. for (Long tenantId : tenantIds) {
  53. TenantDb tenant;
  54. try (Connection master = open(MASTER_HOST, MASTER_PORT, MASTER_DB, MASTER_USER, MASTER_PASSWORD)) {
  55. tenant = loadTenant(master, tenantId);
  56. }
  57. if (tenant == null) {
  58. tenantSkip++;
  59. continue;
  60. }
  61. System.out.println("\n[TENANT " + tenantId + "] " + tenant.database);
  62. try (Connection conn = open(tenant.host, tenant.port, tenant.database, tenant.user, tenant.password)) {
  63. int n = updateMenu(conn, "sys_menu", dryRun);
  64. if (n > 0) tenantOk++;
  65. int m = updateMenu(conn, "company_menu", dryRun);
  66. if (m > 0) tenantOk++;
  67. if (n == 0 && m == 0) tenantSkip++;
  68. } catch (Exception e) {
  69. System.out.println(" [ERR] " + e.getMessage());
  70. tenantFail++;
  71. }
  72. }
  73. System.out.println("\n=== SUMMARY ===");
  74. System.out.println("Master updated=" + masterOk);
  75. System.out.println("Tenant updated=" + tenantOk + " SKIP=" + tenantSkip + " FAIL=" + tenantFail);
  76. if (tenantFail > 0) {
  77. System.exit(1);
  78. }
  79. }
  80. /**
  81. * 更新指定菜单表中的菜单名。
  82. * 返回受影响的行数。
  83. */
  84. private static int updateMenu(Connection conn, String table, boolean dryRun) throws SQLException {
  85. if (!tableExists(conn, table)) {
  86. System.out.println(" [SKIP] no " + table);
  87. return 0;
  88. }
  89. // 先查询当前菜单名
  90. String currentName = null;
  91. try (PreparedStatement ps = conn.prepareStatement(
  92. "SELECT menu_name FROM " + table + " WHERE menu_id = ? LIMIT 1")) {
  93. ps.setLong(1, MENU_ID);
  94. try (ResultSet rs = ps.executeQuery()) {
  95. if (rs.next()) {
  96. currentName = rs.getString(1);
  97. }
  98. }
  99. }
  100. if (currentName == null) {
  101. System.out.println(" [SKIP] " + table + ": menu_id=" + MENU_ID + " not found");
  102. return 0;
  103. }
  104. if (NEW_NAME.equals(currentName)) {
  105. System.out.println(" [SKIP] " + table + ": already renamed to '" + NEW_NAME + "'");
  106. return 0;
  107. }
  108. System.out.println(" " + table + ": '" + currentName + "' -> '" + NEW_NAME + "'");
  109. if (dryRun) {
  110. return 1;
  111. }
  112. String sql = "UPDATE " + table + " SET menu_name = ?, update_time = NOW() WHERE menu_id = ?";
  113. try (PreparedStatement ps = conn.prepareStatement(sql)) {
  114. ps.setString(1, NEW_NAME);
  115. ps.setLong(2, MENU_ID);
  116. return ps.executeUpdate();
  117. }
  118. }
  119. private static boolean tableExists(Connection conn, String table) throws SQLException {
  120. try (ResultSet rs = conn.getMetaData().getTables(null, null, table, null)) {
  121. return rs.next();
  122. }
  123. }
  124. private static List<Long> loadActiveTenantIds(Connection master) throws SQLException {
  125. List<Long> ids = new ArrayList<>();
  126. try (Statement st = master.createStatement();
  127. ResultSet rs = st.executeQuery("SELECT id FROM tenant_info WHERE status = 1 ORDER BY id")) {
  128. while (rs.next()) {
  129. ids.add(rs.getLong(1));
  130. }
  131. }
  132. return ids;
  133. }
  134. private static TenantDb loadTenant(Connection master, long tenantId) throws SQLException {
  135. try (PreparedStatement ps = master.prepareStatement(
  136. "SELECT db_url, db_account, db_pwd FROM tenant_info WHERE id = ? AND status = 1 LIMIT 1")) {
  137. ps.setLong(1, tenantId);
  138. try (ResultSet rs = ps.executeQuery()) {
  139. if (!rs.next()) {
  140. return null;
  141. }
  142. return parseJdbcUrl(rs.getString("db_url"), rs.getString("db_account"), rs.getString("db_pwd"));
  143. }
  144. }
  145. }
  146. private static TenantDb parseJdbcUrl(String jdbcUrl, String user, String password) {
  147. String body = jdbcUrl.substring("jdbc:mysql://".length());
  148. int slash = body.indexOf('/');
  149. String hostPort = body.substring(0, slash);
  150. String rest = body.substring(slash + 1);
  151. int q = rest.indexOf('?');
  152. String database = q >= 0 ? rest.substring(0, q) : rest;
  153. int colon = hostPort.indexOf(':');
  154. TenantDb t = new TenantDb();
  155. t.host = colon >= 0 ? hostPort.substring(0, colon) : hostPort;
  156. t.port = colon >= 0 ? Integer.parseInt(hostPort.substring(colon + 1)) : 3306;
  157. t.database = database;
  158. t.user = user != null ? user : MASTER_USER;
  159. t.password = password != null ? password : MASTER_PASSWORD;
  160. return t;
  161. }
  162. private static Connection open(String host, int port, String database, String user, String password)
  163. throws SQLException {
  164. return DriverManager.getConnection(
  165. "jdbc:mysql://" + host + ":" + port + "/" + database
  166. + "?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8&allowMultiQueries=true",
  167. user, password);
  168. }
  169. private static String getenv(String key, String defaultValue) {
  170. String v = System.getenv(key);
  171. return v != null && !v.isEmpty() ? v : defaultValue;
  172. }
  173. private static final class TenantDb {
  174. String host;
  175. int port;
  176. String database;
  177. String user;
  178. String password;
  179. }
  180. }