package scripts.qa; import java.io.File; import java.nio.file.Files; import java.sql.*; import java.util.*; public class IntegratedQaBootstrapAndValidation { private static final String TARGET_DB = "railwaysystem_integrated_qa_20260916"; private static final String TEMPLATE_DB = "training"; private static final String MAINTENANCE_DB_URL = "jdbc:kingbase8://127.0.0.1:4321/kingbase?useUnicode=true&characterEncoding=utf8"; private static final String QA_DB_URL = "jdbc:kingbase8://127.0.0.1:4321/" + TARGET_DB + "?useUnicode=true&characterEncoding=utf8¤tSchema=public,sys_catalog"; private static final String USER = "kingbase"; public static void main(String[] args) { String password = resolveDbPassword(); try { Class.forName("com.kingbase8.Driver"); // 1. Re-create QA Database from template training System.out.println("=== 1. DATABASE PROVISIONING ==="); try (Connection conn = DriverManager.getConnection(MAINTENANCE_DB_URL, USER, password)) { try (Statement st = conn.createStatement()) { // Disconnect any active connections to TARGET_DB st.execute("SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE datname = '" + TARGET_DB + "' AND pid <> pg_backend_pid()"); st.execute("DROP DATABASE IF EXISTS " + TARGET_DB); System.out.println("Dropped existing " + TARGET_DB); st.execute("CREATE DATABASE " + TARGET_DB + " TEMPLATE " + TEMPLATE_DB); System.out.println("Created " + TARGET_DB + " from TEMPLATE " + TEMPLATE_DB + " successfully."); } } // 2. DB Identity Gate & Clean Business State System.out.println("\n=== 2. DB IDENTITY GATE & CLEAN BUSINESS STATE ==="); Map initialBusinessCounts = new LinkedHashMap<>(); try (Connection conn = DriverManager.getConnection(QA_DB_URL, USER, password)) { try (Statement st = conn.createStatement(); ResultSet rs = st.executeQuery("SELECT current_database(), current_user, inet_server_addr(), inet_server_port()")) { if (rs.next()) { System.out.println("CURRENT_DATABASE: " + rs.getString(1)); System.out.println("CURRENT_USER: " + rs.getString(2)); System.out.println("SERVER_ADDR: " + rs.getString(3)); System.out.println("SERVER_PORT: " + rs.getString(4)); System.out.println("DB_ENVIRONMENT: ISOLATED_QA"); } } // Drop legacy backup tables try (Statement st = conn.createStatement()) { List bakTables = new ArrayList<>(); try (ResultSet rs = st.executeQuery("SELECT table_name FROM information_schema.tables WHERE table_schema = 'public' AND table_name LIKE 'bak_v1_%'")) { while (rs.next()) bakTables.add(rs.getString(1)); } for (String bak : bakTables) { st.execute("DROP TABLE IF EXISTS " + bak + " CASCADE"); } System.out.println("Dropped " + bakTables.size() + " legacy backup tables (bak_v1_*)."); } // Truncate all business demo tables in dependency order String[] businessTables = new String[]{ "train_student_checkin", "train_class_report", "train_student_score", "train_student_portrait", "train_study_duration", "train_accommodation", "train_external_operation", "train_external_resource_mapping", "train_dorm_auth", "train_lesson", "train_plan_course", "train_enrollment", "train_score_item", "train_course_resource", "train_resource", "train_course", "train_class_plan", "train_dormitory_info", "train_classroom_info", "train_training_site", "train_electronic_board", "train_student", "train_teacher", "sys_logininfor", "sys_oper_log", "sys_job_log", "gen_table_column", "gen_table", "app_config" }; try (Statement st = conn.createStatement()) { for (String table : businessTables) { try { st.execute("TRUNCATE TABLE " + table + " CASCADE"); } catch (Exception e) { try { st.execute("DELETE FROM " + table); } catch (Exception ex) { System.err.println("Clean table " + table + " warning: " + ex.getMessage()); } } } System.out.println("Business tables truncated successfully."); } // Clean sys_user to keep only system accounts (admin, ry) try (Statement st = conn.createStatement()) { st.execute("DELETE FROM sys_user_role WHERE user_id NOT IN (SELECT user_id FROM sys_user WHERE user_name IN ('admin', 'ry'))"); st.execute("DELETE FROM sys_user_post WHERE user_id NOT IN (SELECT user_id FROM sys_user WHERE user_name IN ('admin', 'ry'))"); st.execute("DELETE FROM sys_user WHERE user_name NOT IN ('admin', 'ry')"); System.out.println("sys_user cleaned: only admin/ry retained."); } // Verify and record initial business counts System.out.println("\n--- INITIAL BUSINESS DATA COUNTS ---"); try (Statement st = conn.createStatement()) { for (String table : businessTables) { try (ResultSet rs = st.executeQuery("SELECT count(*) FROM " + table)) { if (rs.next()) { int c = rs.getInt(1); initialBusinessCounts.put(table, c); System.out.println(String.format("%-35s : %d", table, c)); } } catch (Exception e) { System.out.println(String.format("%-35s : TABLE_NOT_EXIST_YET", table)); } } } } // 3. Migration First Run & Second Run System.out.println("\n=== 3. MIGRATION EXECUTION & IDEMPOTENCY ==="); String[] migrationFiles = new String[]{ "RuoYi-Vue/sql/system/kingbase/006_accommodation_history_constraint.sql", "RuoYi-Vue/sql/system/kingbase/008_external_resource_operation.sql", "RuoYi-Vue/sql/system/kingbase/009_dorm_authorization_orchestration.sql", "RuoYi-Vue/sql/system/kingbase/010_dms_access_management_permissions.sql" }; // FIRST RUN System.out.println("--- FIRST RUN ---"); boolean firstRunAllPass = true; try (Connection conn = DriverManager.getConnection(QA_DB_URL, USER, password)) { for (String path : migrationFiles) { boolean pass = executeSqlScript(conn, path); System.out.println("Migration " + new File(path).getName() + " FIRST_RUN: " + (pass ? "PASS" : "FAIL")); if (!pass) firstRunAllPass = false; } } // SECOND RUN (IDEMPOTENCY TEST) System.out.println("\n--- SECOND RUN (IDEMPOTENCY TEST) ---"); boolean secondRunAllPass = true; try (Connection conn = DriverManager.getConnection(QA_DB_URL, USER, password)) { for (String path : migrationFiles) { boolean pass = executeSqlScript(conn, path); System.out.println("Migration " + new File(path).getName() + " SECOND_RUN: " + (pass ? "PASS" : "FAIL")); if (!pass) secondRunAllPass = false; } } System.out.println("MIGRATION_FIRST_RUN = " + (firstRunAllPass ? "PASS" : "FAIL")); System.out.println("MIGRATION_SECOND_RUN = " + (secondRunAllPass ? "PASS" : "FAIL")); System.out.println("MIGRATION_IDEMPOTENT = " + (firstRunAllPass && secondRunAllPass ? "PASS" : "FAIL")); // 4. Schema Validation System.out.println("\n=== 4. SCHEMA VALIDATION ==="); try (Connection conn = DriverManager.getConnection(QA_DB_URL, USER, password)) { // Check required tables String[] requiredTables = new String[]{ "train_class_plan", "train_course", "train_enrollment", "train_lesson", "train_student_checkin", "train_accommodation", "train_dorm_auth", "train_external_resource_mapping", "train_external_operation" }; for (String t : requiredTables) { try (Statement st = conn.createStatement(); ResultSet rs = st.executeQuery("SELECT 1 FROM information_schema.tables WHERE table_schema = 'public' AND table_name = '" + t + "'")) { boolean ok = rs.next(); System.out.println("TABLE " + t + " EXISTS: " + (ok ? "YES" : "NO")); } } // Check constraints & indexes checkIndexOrConstraint(conn, "train_accommodation", "uq_accommodation_enroll_active", "Active accommodation partial unique index"); checkIndexOrConstraint(conn, "train_external_resource_mapping", "uq_ext_res_map_active_room", "Active room mapping partial unique index"); checkIndexOrConstraint(conn, "train_external_resource_mapping", "uq_ext_res_map_active_ext_res", "Active external res partial unique index"); checkIndexOrConstraint(conn, "train_external_operation", "uq_ext_op_idempotency", "External op idempotency unique index"); checkIndexOrConstraint(conn, "train_dorm_auth", "idx_dorm_auth_status", "Dorm auth status index"); checkIndexOrConstraint(conn, "train_dorm_auth", "idx_dorm_auth_latest_op", "Dorm auth latest op index"); // Check RBAC roles System.out.println("\n--- RBAC ROLES CHECK ---"); try (Statement st = conn.createStatement(); ResultSet rs = st.executeQuery("SELECT role_id, role_name, role_key, status FROM sys_role WHERE role_key IN ('admin', 'train_admin', 'train_teacher', 'train_student')")) { while (rs.next()) { System.out.println("ROLE: " + rs.getString("role_key") + " (" + rs.getString("role_name") + ") status=" + rs.getString("status")); } } // Check DMS Permissions in sys_menu System.out.println("\n--- DMS PERMISSIONS IN SYS_MENU ---"); try (Statement st = conn.createStatement(); ResultSet rs = st.executeQuery("SELECT menu_id, menu_name, perms FROM sys_menu WHERE perms IN ('train:access:query', 'train:access:manage', 'train:access:reconcile')")) { while (rs.next()) { System.out.println("MENU: " + rs.getString("perms") + " - " + rs.getString("menu_name")); } } // Check Orphan counts System.out.println("\n--- ORPHAN COUNTS CHECK ---"); int totalOrphans = 0; String[] orphanChecks = new String[]{ "SELECT count(*) FROM train_enrollment e WHERE NOT EXISTS (SELECT 1 FROM train_student s WHERE s.s_id = e.student_id)", "SELECT count(*) FROM train_enrollment e WHERE NOT EXISTS (SELECT 1 FROM train_class_plan p WHERE p.plan_id = e.plan_id)", "SELECT count(*) FROM train_lesson l WHERE NOT EXISTS (SELECT 1 FROM train_plan_course pc WHERE pc.plan_course_id = l.plan_course_id)", "SELECT count(*) FROM train_accommodation a WHERE NOT EXISTS (SELECT 1 FROM train_enrollment e WHERE e.enrollment_id = a.enrollment_id)", "SELECT count(*) FROM train_student_checkin c WHERE NOT EXISTS (SELECT 1 FROM train_enrollment e WHERE e.enrollment_id = c.enrollment_id)", "SELECT count(*) FROM train_dorm_auth d WHERE NOT EXISTS (SELECT 1 FROM train_accommodation a WHERE a.accommodation_id = d.accommodation_id)", "SELECT count(*) FROM train_external_operation o WHERE NOT EXISTS (SELECT 1 FROM train_dorm_auth d WHERE d.auth_id = o.door_auth_id)" }; for (String q : orphanChecks) { try (Statement st = conn.createStatement(); ResultSet rs = st.executeQuery(q)) { if (rs.next()) { totalOrphans += rs.getInt(1); } } } System.out.println("ORPHAN_COUNT = " + totalOrphans); } } catch (Exception e) { System.err.println("Execution failed: " + e.getMessage()); e.printStackTrace(); System.exit(1); } } private static boolean executeSqlScript(Connection conn, String path) { try { File f = new File(path); if (!f.exists()) { System.err.println("Script not found: " + path); return false; } String content = new String(Files.readAllBytes(f.toPath())); // For DO $$ ... $$; block if (content.contains("DO $$")) { try (Statement st = conn.createStatement()) { st.execute(content); } return true; } // Split by semicolon, accounting for BEGIN/COMMIT String[] stmts = content.split(";"); try (Statement st = conn.createStatement()) { for (String s : stmts) { String trimmed = s.trim(); if (!trimmed.isEmpty() && !trimmed.startsWith("--")) { st.execute(trimmed); } } } return true; } catch (Exception e) { System.err.println("Script execution failed on " + path + ": " + e.getMessage()); return false; } } private static void checkIndexOrConstraint(Connection conn, String table, String name, String desc) { try (Statement st = conn.createStatement()) { boolean found = false; // check constraint try (ResultSet rs = st.executeQuery("SELECT 1 FROM pg_constraint c JOIN pg_class t ON c.conrelid = t.oid WHERE t.relname = '" + table + "' AND c.conname = '" + name + "'")) { if (rs.next()) found = true; } if (!found) { // check index try (ResultSet rs = st.executeQuery("SELECT 1 FROM pg_class c JOIN pg_index i ON c.oid = i.indexrelid JOIN pg_class t ON i.indrelid = t.oid WHERE t.relname = '" + table + "' AND c.relname = '" + name + "'")) { if (rs.next()) found = true; } } System.out.println("CONSTRAINT/INDEX " + name + " (" + desc + ") ON " + table + ": " + (found ? "PASS" : "FAIL")); } catch (Exception e) { System.out.println("CONSTRAINT/INDEX " + name + " ERROR: " + e.getMessage()); } } private static String resolveDbPassword() { String env = System.getenv("DB_PASSWORD"); if (env != null && !env.isEmpty()) return env; try { File yml = new File("RuoYi-Vue/ruoyi-admin/src/main/resources/application-druid.yml"); if (!yml.exists()) { yml = new File("../RuoYi-Vue/ruoyi-admin/src/main/resources/application-druid.yml"); } if (yml.exists()) { for (String line : Files.readAllLines(yml.toPath())) { if (line.trim().startsWith("password:")) { return line.trim().substring("password:".length()).trim(); } } } } catch (Exception ignored) {} return "123456"; } }