ApplyLobsterMenuIcons.java 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198
  1. import java.nio.charset.StandardCharsets;
  2. import java.nio.file.Files;
  3. import java.nio.file.Path;
  4. import java.sql.Connection;
  5. import java.sql.DriverManager;
  6. import java.sql.PreparedStatement;
  7. import java.sql.ResultSet;
  8. import java.sql.SQLException;
  9. import java.sql.Statement;
  10. import java.util.ArrayList;
  11. import java.util.List;
  12. /** Apply V20260802 lobster menu icon SVG migration to master + all active tenant DBs. */
  13. public class ApplyLobsterMenuIcons {
  14. private static final String MASTER_HOST = getenv("DB_HOST", "cq-cdb-8fjmemkb.sql.tencentcdb.com");
  15. private static final int MASTER_PORT = Integer.parseInt(getenv("DB_PORT", "27220"));
  16. private static final String MASTER_USER = getenv("DB_USER", "root");
  17. private static final String MASTER_PASSWORD = getenv("DB_PASSWORD", "Ylrz_1q2w3e4r5t6y");
  18. private static final String MASTER_DB = getenv("DB_NAME", "ylrz_saas");
  19. private static final String VERSION = "V20260803";
  20. private static final String SCRIPT_NAME = "V20260803_01__lobster_company_menu_icons_fixup.sql";
  21. private static final Path TENANT_SQL_PATH = Path.of(
  22. "d:/ylrz_saas_new/java/fs-agent/src/main/resources/db/migration/tenant/V20260803_01__lobster_company_menu_icons_fixup.sql");
  23. private static final Path MASTER_SQL_PATH = Path.of(
  24. "d:/ylrz_saas_new/java/fs-agent/src/main/resources/db/migration/master/V20260802_01__lobster_menu_icons_svg.sql");
  25. public static void main(String[] args) throws Exception {
  26. Class.forName("com.mysql.cj.jdbc.Driver");
  27. String tenantSql = readSql(TENANT_SQL_PATH);
  28. String masterSql = readSql(MASTER_SQL_PATH);
  29. System.out.println("=== MASTER " + MASTER_DB + " (tenant_sys_menu + tenant_company_menu) ===");
  30. try (Connection master = open(MASTER_HOST, MASTER_PORT, MASTER_DB, MASTER_USER, MASTER_PASSWORD)) {
  31. execScript(master, masterSql);
  32. reportIcons(master, "tenant_sys_menu");
  33. reportIcons(master, "tenant_company_menu");
  34. for (Long tenantId : loadActiveTenantIds(master)) {
  35. TenantDb tenant = loadTenant(master, tenantId);
  36. if (tenant == null) {
  37. System.out.println("[SKIP] tenant " + tenantId + " (no db config)");
  38. continue;
  39. }
  40. System.out.println("\n=== TENANT " + tenantId + " -> " + tenant.database + " ===");
  41. try (Connection c = open(tenant.host, tenant.port, tenant.database, tenant.user, tenant.password)) {
  42. if (alreadyApplied(c)) {
  43. System.out.println("[SKIP] " + VERSION + " already SUCCESS in tenant_db_migration");
  44. } else {
  45. execScript(c, tenantSql);
  46. recordMigration(c);
  47. System.out.println("[OK] applied " + SCRIPT_NAME);
  48. }
  49. reportIcons(c, "sys_menu");
  50. reportIcons(c, "company_menu");
  51. } catch (Exception e) {
  52. System.err.println("[FAIL] tenant " + tenantId + ": " + e.getMessage());
  53. e.printStackTrace(System.err);
  54. }
  55. }
  56. }
  57. System.out.println("\nDone. Re-login tenant/sales UI to refresh menus.");
  58. }
  59. private static boolean alreadyApplied(Connection c) throws SQLException {
  60. try (Statement st = c.createStatement();
  61. ResultSet rs = st.executeQuery(
  62. "SELECT 1 FROM tenant_db_migration WHERE version = '" + VERSION
  63. + "' AND status = 'SUCCESS' LIMIT 1")) {
  64. return rs.next();
  65. } catch (SQLException e) {
  66. if (isMissingMigrationTable(e)) {
  67. return false;
  68. }
  69. throw e;
  70. }
  71. }
  72. private static void recordMigration(Connection c) throws SQLException {
  73. try {
  74. try (Statement st = c.createStatement()) {
  75. st.execute("DELETE FROM tenant_db_migration WHERE version = '" + VERSION + "'");
  76. }
  77. String sql = "INSERT INTO tenant_db_migration "
  78. + "(version, script_name, checksum, status, started_at, finished_at, executor) "
  79. + "VALUES (?, ?, '', 'SUCCESS', NOW(), NOW(), 'ApplyLobsterMenuIcons')";
  80. try (PreparedStatement ps = c.prepareStatement(sql)) {
  81. ps.setString(1, VERSION);
  82. ps.setString(2, SCRIPT_NAME);
  83. ps.executeUpdate();
  84. }
  85. } catch (SQLException e) {
  86. if (isMissingMigrationTable(e)) {
  87. System.out.println("[WARN] tenant_db_migration table missing, skip record");
  88. return;
  89. }
  90. throw e;
  91. }
  92. }
  93. private static boolean isMissingMigrationTable(SQLException e) {
  94. String msg = e.getMessage();
  95. return msg != null && msg.contains("tenant_db_migration");
  96. }
  97. private static String readSql(Path path) throws Exception {
  98. byte[] bytes = Files.readAllBytes(path);
  99. String sql = new String(bytes, StandardCharsets.UTF_8);
  100. if (!sql.isEmpty() && sql.charAt(0) == '\uFEFF') {
  101. sql = sql.substring(1);
  102. }
  103. return sql;
  104. }
  105. private static void execScript(Connection conn, String sql) throws SQLException {
  106. try (Statement st = conn.createStatement()) {
  107. st.execute(sql);
  108. }
  109. }
  110. private static void reportIcons(Connection c, String table) throws SQLException {
  111. System.out.println("--- " + table + " icons ---");
  112. String sql = "SELECT menu_id, menu_name, path, icon FROM " + table
  113. + " WHERE menu_id = 29900 OR parent_id = 29900 ORDER BY order_num, menu_id";
  114. try (Statement st = c.createStatement(); ResultSet rs = st.executeQuery(sql)) {
  115. while (rs.next()) {
  116. System.out.printf("[%d] %s | %s | icon=%s%n",
  117. rs.getLong("menu_id"),
  118. rs.getString("menu_name"),
  119. rs.getString("path"),
  120. rs.getString("icon"));
  121. }
  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. }