import java.nio.file.*; import java.sql.*; import java.util.*; import java.util.stream.*; /** * 增量 SQL 执行器:仅执行 t_sql_migration 中尚未记录的脚本,避免重复执行。 */ public class RunAllSql { private static final String URL = "jdbc:kingbase8://8.162.3.203:54321/economic_crime_dev?currentSchema=public&clientEncoding=utf8&socketTimeout=30000&loginTimeout=30"; private static final String USER = "dcms_dev"; private static final String PASS = "sdy@2025#dc$ks"; /** 迁移追踪表引入前已在库中执行过的脚本(仅首次 bootstrap 写入,不重复跑) */ private static final String[] BOOTSTRAP_APPLIED = { "20260625_add_disposal_status.sql", "20260626_fix_check_constraints.sql", "20260627_add_alarm_category.sql", "20260627_add_alarm_detail_fields.sql", "20260627_add_evidence_file_key.sql", "20260629_add_case_type_77_dict.sql", "20260629_reorganize_top_menus.sql", "20260706_alarm_source_register.sql", "20260706_alarm_void_roles.sql", "20260706_fix_evidence_type_check.sql", "20260708_void_audit_menu.sql", "20260709_void_audit_grant_jz_admin.sql", "20260711_migrate_pending_info_to_pending_review.sql", "20260722_alarm_audit_group.sql", "20260731_property_transfer_menu.sql", }; /** 显式顺序清单;自动扫描 docs/202*.sql 时会与此合并去重排序 */ private static final String[] SCRIPTS = { "00000000_sql_migration_table.sql", "20260625_add_disposal_status.sql", "20260626_fix_check_constraints.sql", "20260627_add_alarm_category.sql", "20260627_add_alarm_detail_fields.sql", "20260627_add_evidence_file_key.sql", "20260629_add_case_type_77_dict.sql", "20260629_reorganize_top_menus.sql", "20260706_alarm_source_register.sql", "20260706_alarm_void_roles.sql", "20260706_fix_evidence_type_check.sql", "20260708_void_audit_menu.sql", "20260709_void_audit_grant_jz_admin.sql", "20260711_migrate_pending_info_to_pending_review.sql", "20260722_alarm_audit_group.sql", "20260731_property_transfer_menu.sql", "20260731_property_return_checkout_menu.sql", "20260731_property_tables.sql", "20260804_case_seizure_doc_table.sql", "20260804_property_unregistered_menu.sql", "20260804_case_property_item_table.sql", "20260804_property_omission_menu.sql", "20260804_property_transfer_audit_flow.sql", "20260804_property_return_audit_flow.sql", "20260804_property_checkout_audit_flow.sql", "20260804_dossier_module.sql", "20260805_tl_police_department_org.sql", "20260805_hide_system_demo_menus.sql", "20260805_add_alarm_case_source.sql", "20260805_alarm_no_accept_review.sql", "20260805_fix_employee_sequence.sql", "20260805_fix_duplicate_employee_email.sql", "20260805_sync_all_function_permissions.sql", "20260805_fix_alarm_audit_permission_codes.sql", "20260907_dossier_borrow_self_seed.sql", }; public static void main(String[] args) throws Exception { Class.forName("com.kingbase8.Driver"); Path base = Paths.get("d:/project/economic_crime/docs"); List scriptList = resolveScripts(base); int ran = 0; int skipped = 0; int fail = 0; try (Connection conn = DriverManager.getConnection(URL, USER, PASS)) { conn.setAutoCommit(true); System.out.println("Connected: economic_crime_dev"); ensureMigrationTable(conn); bootstrapIfEmpty(conn); for (String file : scriptList) { Path path = base.resolve(file); if (!Files.exists(path)) { System.out.println("[SKIP missing] " + file); skipped++; continue; } if (isApplied(conn, file)) { System.out.println("[SKIP applied] " + file); skipped++; continue; } System.out.println("[RUN] " + file); boolean fileOk = executeFile(conn, Files.readString(path)); if (fileOk) { markApplied(conn, file); System.out.println(" => OK"); ran++; } else { System.out.println(" => FAIL (not recorded)"); fail++; } } printPropertyMenus(conn); } System.out.printf("%nDone: %d ran, %d skipped, %d failed%n", ran, skipped, fail); if (fail > 0) System.exit(1); } private static List resolveScripts(Path base) throws Exception { Set names = new LinkedHashSet<>(Arrays.asList(SCRIPTS)); try (DirectoryStream stream = Files.newDirectoryStream(base, "202*.sql")) { for (Path p : stream) { names.add(p.getFileName().toString()); } } try (DirectoryStream stream = Files.newDirectoryStream(base, "00000000_*.sql")) { for (Path p : stream) { names.add(p.getFileName().toString()); } } return names.stream().sorted().collect(Collectors.toList()); } private static void ensureMigrationTable(Connection conn) throws SQLException { String ddl = """ CREATE TABLE IF NOT EXISTS t_sql_migration ( script_name VARCHAR(128) PRIMARY KEY, executed_at TIMESTAMP NOT NULL DEFAULT NOW(), success_flag BOOLEAN NOT NULL DEFAULT TRUE ) """; try (Statement s = conn.createStatement()) { s.execute(ddl); } } private static void bootstrapIfEmpty(Connection conn) throws SQLException { int count = 0; try (Statement s = conn.createStatement(); ResultSet rs = s.executeQuery("SELECT COUNT(*) FROM t_sql_migration")) { if (rs.next()) count = rs.getInt(1); } if (count > 0) return; System.out.println("[BOOTSTRAP] seeding historical applied scripts..."); for (String script : BOOTSTRAP_APPLIED) { markApplied(conn, script); System.out.println(" [seed] " + script); } } private static boolean isApplied(Connection conn, String script) throws SQLException { try (PreparedStatement ps = conn.prepareStatement( "SELECT 1 FROM t_sql_migration WHERE script_name = ? AND success_flag = TRUE")) { ps.setString(1, script); try (ResultSet rs = ps.executeQuery()) { return rs.next(); } } } private static void markApplied(Connection conn, String script) throws SQLException { try (PreparedStatement ps = conn.prepareStatement( "INSERT INTO t_sql_migration (script_name, executed_at, success_flag) " + "SELECT ?, NOW(), TRUE WHERE NOT EXISTS (" + "SELECT 1 FROM t_sql_migration WHERE script_name = ?)")) { ps.setString(1, script); ps.setString(2, script); ps.executeUpdate(); } } private static boolean executeFile(Connection conn, String sql) throws SQLException { List statements = splitStatements(sql); boolean fileOk = true; for (String stmt : statements) { if (stmt.isBlank()) continue; try (Statement s = conn.createStatement()) { s.execute(stmt); } catch (SQLException e) { if (isIgnorable(e)) { System.out.println(" [SKIP stmt] " + shorten(e.getMessage())); } else { System.out.println(" [FAIL stmt] " + shorten(e.getMessage())); fileOk = false; } } } return fileOk; } private static void printPropertyMenus(Connection conn) throws SQLException { System.out.println("\n--- 涉案财物菜单验证 ---"); try (Statement s = conn.createStatement(); ResultSet rs = s.executeQuery( "SELECT menu_name, path, web_perms FROM t_menu WHERE deleted_flag = 0 " + "AND (path LIKE '/property/%' OR web_perms LIKE 'property:%') ORDER BY menu_id")) { while (rs.next()) { System.out.printf(" %s | %s | %s%n", rs.getString(1), rs.getString(2), rs.getString(3)); } } } private static boolean isIgnorable(SQLException e) { String msg = e.getMessage().toLowerCase(Locale.ROOT); return msg.contains("already exists") || msg.contains("duplicate key") || (msg.contains("does not exist") && msg.contains("drop")); } private static String shorten(String msg) { if (msg == null) return ""; int idx = msg.indexOf('\n'); return idx > 0 ? msg.substring(0, idx) : msg; } static List splitStatements(String sql) { List out = new ArrayList<>(); StringBuilder cur = new StringBuilder(); boolean inDollar = false; String[] lines = sql.split("\n", -1); for (String line : lines) { String trimmed = line.trim(); if (!inDollar && (trimmed.startsWith("--") || trimmed.isEmpty())) { continue; } if (trimmed.contains("DO $$") || trimmed.startsWith("DO $$")) { inDollar = true; } cur.append(line).append('\n'); if (inDollar) { if (trimmed.endsWith("$$;") || trimmed.equals("$$;")) { inDollar = false; out.add(cur.toString().trim()); cur.setLength(0); } } else if (trimmed.endsWith(";")) { out.add(cur.toString().trim()); cur.setLength(0); } } if (!cur.toString().trim().isEmpty()) { out.add(cur.toString().trim()); } return out; } }