export const sqliteSchema = ` PRAGMA foreign_keys = ON; CREATE TABLE IF NOT EXISTS schema_metadata ( id INTEGER PRIMARY KEY CHECK (id = 1), schema_version INTEGER NOT NULL DEFAULT 1, app_version INTEGER NOT NULL DEFAULT 1, self_registration_enabled INTEGER NOT NULL DEFAULT 0 CHECK (self_registration_enabled IN (0, 1)), created_at TEXT NOT NULL ) STRICT; CREATE TABLE IF NOT EXISTS organization ( id INTEGER PRIMARY KEY CHECK (id = 1), name TEXT NOT NULL, code TEXT NOT NULL, phone TEXT NOT NULL, address TEXT NOT NULL ) STRICT; CREATE TABLE IF NOT EXISTS schools ( id TEXT PRIMARY KEY, name TEXT NOT NULL UNIQUE, code TEXT NOT NULL UNIQUE, address TEXT, is_source_school INTEGER NOT NULL DEFAULT 1 CHECK (is_source_school IN (0, 1)), is_admission_school INTEGER NOT NULL DEFAULT 1 CHECK (is_admission_school IN (0, 1)), active INTEGER NOT NULL DEFAULT 1 CHECK (active IN (0, 1)) ) STRICT; CREATE TABLE IF NOT EXISTS school_classes ( id TEXT PRIMARY KEY, school_id TEXT NOT NULL REFERENCES schools(id) ON DELETE CASCADE, name TEXT NOT NULL, grade TEXT NOT NULL, active INTEGER NOT NULL DEFAULT 1 CHECK (active IN (0, 1)), UNIQUE (school_id, name) ) STRICT; CREATE TABLE IF NOT EXISTS school_student_partitions ( school_id TEXT PRIMARY KEY REFERENCES schools(id) ON DELETE CASCADE, partition_key TEXT NOT NULL UNIQUE, students_table TEXT NOT NULL UNIQUE, created_at TEXT NOT NULL, updated_at TEXT NOT NULL ) STRICT; CREATE TABLE IF NOT EXISTS users ( id TEXT PRIMARY KEY, username TEXT NOT NULL UNIQUE, candidate_number TEXT UNIQUE, password_hash TEXT NOT NULL, role TEXT NOT NULL CHECK (role IN ('admin', 'candidate', 'admission_school')), admin_level TEXT CHECK (admin_level IN ('super', 'school', 'class')), school_id TEXT REFERENCES schools(id) ON DELETE SET NULL, class_id TEXT REFERENCES school_classes(id) ON DELETE SET NULL, active INTEGER NOT NULL DEFAULT 1 CHECK (active IN (0, 1)), must_change_password INTEGER NOT NULL DEFAULT 0 CHECK (must_change_password IN (0, 1)), totp_enabled INTEGER NOT NULL DEFAULT 0 CHECK (totp_enabled IN (0, 1)), totp_secret_encrypted TEXT, totp_recovery_codes TEXT NOT NULL DEFAULT '[]', totp_last_used_step INTEGER, archived_at TEXT, archived_by TEXT REFERENCES users(id) ON DELETE RESTRICT, display_name TEXT NOT NULL, created_at TEXT NOT NULL ) STRICT; CREATE TABLE IF NOT EXISTS candidate_profiles ( id TEXT PRIMARY KEY, user_id TEXT NOT NULL UNIQUE REFERENCES users(id) ON DELETE CASCADE, name TEXT NOT NULL, gender TEXT, id_number TEXT NOT NULL UNIQUE, phone TEXT NOT NULL, email TEXT, school TEXT, grade TEXT, school_id TEXT REFERENCES schools(id) ON DELETE SET NULL, class_id TEXT REFERENCES school_classes(id) ON DELETE SET NULL, province_code TEXT, province_name TEXT, city_code TEXT, city_name TEXT, district_code TEXT, district_name TEXT, address TEXT, emergency_contact TEXT, emergency_phone TEXT, native_place TEXT, birth_date TEXT, ethnicity TEXT, postal_code TEXT, guardian_name TEXT, guardian_phone TEXT, specialty_category TEXT, specialty_type TEXT, specialty_types TEXT NOT NULL DEFAULT '[]', specialty_certificate TEXT, policy_eligibility TEXT, profile_completed INTEGER NOT NULL DEFAULT 0 CHECK (profile_completed IN (0, 1)), status TEXT NOT NULL CHECK (status IN ('pending', 'approved', 'rejected')), review_note TEXT, reviewed_at TEXT, reviewer_id TEXT REFERENCES users(id) ON DELETE SET NULL, updated_at TEXT NOT NULL ) STRICT; CREATE TABLE IF NOT EXISTS notices ( id TEXT PRIMARY KEY, title TEXT NOT NULL, summary TEXT NOT NULL, content TEXT NOT NULL, category TEXT NOT NULL, pinned INTEGER NOT NULL DEFAULT 0 CHECK (pinned IN (0, 1)), status TEXT NOT NULL CHECK (status IN ('draft', 'published')), publish_at TEXT, created_at TEXT, author TEXT NOT NULL ) STRICT; CREATE TABLE IF NOT EXISTS exams ( id TEXT PRIMARY KEY, code TEXT NOT NULL UNIQUE, name TEXT NOT NULL, description TEXT NOT NULL, registration_start TEXT NOT NULL, registration_end TEXT NOT NULL, exam_start TEXT NOT NULL, exam_end TEXT NOT NULL, admit_download_start TEXT NOT NULL, admit_download_end TEXT NOT NULL, location TEXT NOT NULL, pass_policy TEXT NOT NULL DEFAULT 'rank_percent' CHECK (pass_policy IN ('fixed_score', 'score_ratio', 'rank_percent', 'subject_scores', 'none')), pass_value REAL NOT NULL DEFAULT 60, status TEXT NOT NULL CHECK (status IN ('draft', 'published', 'closed')), archived_at TEXT, archived_by TEXT REFERENCES users(id) ON DELETE RESTRICT, created_at TEXT NOT NULL ) STRICT; CREATE TABLE IF NOT EXISTS exam_data_partitions ( exam_id TEXT PRIMARY KEY REFERENCES exams(id) ON DELETE CASCADE, partition_key TEXT NOT NULL UNIQUE, candidates_table TEXT NOT NULL UNIQUE, admissions_table TEXT NOT NULL UNIQUE, results_table TEXT NOT NULL UNIQUE, centers_table TEXT NOT NULL UNIQUE, created_at TEXT NOT NULL, updated_at TEXT NOT NULL ) STRICT; CREATE TABLE IF NOT EXISTS exam_subjects ( id TEXT PRIMARY KEY, exam_id TEXT NOT NULL REFERENCES exams(id) ON DELETE CASCADE, name TEXT NOT NULL, subject_date TEXT NOT NULL, start_time TEXT NOT NULL, end_time TEXT NOT NULL, fee REAL NOT NULL DEFAULT 0, full_score REAL NOT NULL DEFAULT 150, pass_score REAL NOT NULL DEFAULT 90, pass_rule TEXT NOT NULL DEFAULT 'fixed_score' CHECK (pass_rule IN ('fixed_score', 'score_ratio', 'none')), pass_value REAL NOT NULL DEFAULT 90, position INTEGER NOT NULL, UNIQUE (exam_id, position) ) STRICT; CREATE TABLE IF NOT EXISTS registrations ( id TEXT PRIMARY KEY, user_id TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE, exam_id TEXT NOT NULL REFERENCES exams(id) ON DELETE CASCADE, status TEXT NOT NULL CHECK (status IN ('pending', 'approved', 'rejected')), payment_status TEXT NOT NULL CHECK (payment_status IN ('unpaid', 'paid')), paid_at TEXT, paid_by TEXT REFERENCES users(id) ON DELETE SET NULL, created_at TEXT NOT NULL, reviewed_at TEXT, review_note TEXT, registration_number TEXT, number_rule_id TEXT, feature_score REAL NOT NULL DEFAULT 0 CHECK (feature_score >= 0), UNIQUE (user_id, exam_id) ) STRICT; CREATE TABLE IF NOT EXISTS registration_subjects ( registration_id TEXT NOT NULL REFERENCES registrations(id) ON DELETE CASCADE, subject_id TEXT NOT NULL REFERENCES exam_subjects(id) ON DELETE CASCADE, PRIMARY KEY (registration_id, subject_id) ) STRICT; CREATE TABLE IF NOT EXISTS admission_number_rules ( id TEXT PRIMARY KEY, code TEXT NOT NULL UNIQUE, name TEXT NOT NULL, description TEXT NOT NULL, separator TEXT NOT NULL DEFAULT '', segments_json TEXT NOT NULL, example TEXT NOT NULL, active INTEGER NOT NULL DEFAULT 1 CHECK (active IN (0, 1)), created_at TEXT NOT NULL ) STRICT; CREATE TABLE IF NOT EXISTS exam_arrangement_plans ( id TEXT PRIMARY KEY, exam_id TEXT NOT NULL UNIQUE REFERENCES exams(id) ON DELETE CASCADE, number_rule_id TEXT NOT NULL REFERENCES admission_number_rules(id), mixing_scope TEXT NOT NULL CHECK (mixing_scope IN ('class', 'school', 'district', 'city', 'province')), random_seed TEXT NOT NULL, candidate_count INTEGER NOT NULL CHECK (candidate_count >= 0), center_count INTEGER NOT NULL CHECK (center_count >= 0), subject_assignment_count INTEGER NOT NULL CHECK (subject_assignment_count >= 0), subject_combination_count INTEGER NOT NULL CHECK (subject_combination_count >= 0), same_school_center_rate REAL NOT NULL, warnings_json TEXT NOT NULL, generated_by TEXT REFERENCES users(id) ON DELETE SET NULL, generated_at TEXT NOT NULL ) STRICT; CREATE TABLE IF NOT EXISTS admit_cards ( registration_id TEXT PRIMARY KEY REFERENCES registrations(id) ON DELETE CASCADE, plan_id TEXT NOT NULL REFERENCES exam_arrangement_plans(id) ON DELETE CASCADE, card_number TEXT NOT NULL UNIQUE, center_id TEXT REFERENCES test_centers(id) ON DELETE SET NULL, test_center TEXT NOT NULL, center_code TEXT NOT NULL, center_address TEXT NOT NULL, generated_at TEXT NOT NULL ) STRICT; CREATE TABLE IF NOT EXISTS admit_card_subjects ( registration_id TEXT NOT NULL REFERENCES admit_cards(registration_id) ON DELETE CASCADE, subject_id TEXT NOT NULL REFERENCES exam_subjects(id) ON DELETE CASCADE, room_id TEXT REFERENCES test_rooms(id) ON DELETE SET NULL, room TEXT NOT NULL, room_code TEXT NOT NULL, exam_room_code TEXT NOT NULL, building TEXT NOT NULL, floor TEXT NOT NULL, seat TEXT NOT NULL, subject_signature TEXT NOT NULL, PRIMARY KEY (registration_id, subject_id), UNIQUE (subject_id, room_id, seat) ) STRICT; CREATE TABLE IF NOT EXISTS results ( id TEXT PRIMARY KEY, registration_id TEXT NOT NULL REFERENCES registrations(id) ON DELETE CASCADE, subject_id TEXT NOT NULL REFERENCES exam_subjects(id) ON DELETE CASCADE, score REAL NOT NULL CHECK (score >= 0), grade TEXT NOT NULL, published INTEGER NOT NULL DEFAULT 0 CHECK (published IN (0, 1)), updated_at TEXT, published_at TEXT, UNIQUE (registration_id, subject_id) ) STRICT; CREATE TABLE IF NOT EXISTS test_centers ( id TEXT PRIMARY KEY, school_id TEXT NOT NULL REFERENCES schools(id) ON DELETE CASCADE, code TEXT NOT NULL UNIQUE, name TEXT NOT NULL, province_code TEXT NOT NULL, province_name TEXT NOT NULL, city_code TEXT NOT NULL, city_name TEXT NOT NULL, district_code TEXT NOT NULL, district_name TEXT NOT NULL, address TEXT NOT NULL, contact TEXT, manager_name TEXT, manager_phone TEXT, emergency_phone TEXT, gate_open_time TEXT, transport TEXT, status TEXT NOT NULL DEFAULT 'active' CHECK (status IN ('active', 'inactive')), notes TEXT, rooms TEXT NOT NULL, updated_at TEXT NOT NULL, UNIQUE (school_id, name) ) STRICT; CREATE TABLE IF NOT EXISTS test_rooms ( id TEXT PRIMARY KEY, center_id TEXT NOT NULL REFERENCES test_centers(id) ON DELETE CASCADE, code TEXT NOT NULL, name TEXT NOT NULL, building TEXT NOT NULL, floor TEXT, capacity INTEGER NOT NULL CHECK (capacity > 0), seat_plan TEXT, seat_start INTEGER NOT NULL DEFAULT 1 CHECK (seat_start > 0), seat_end INTEGER NOT NULL CHECK (seat_end >= seat_start), room_type TEXT NOT NULL CHECK (room_type IN ('standard', 'computer', 'accessible', 'spare')), status TEXT NOT NULL DEFAULT 'active' CHECK (status IN ('active', 'inactive')), notes TEXT, UNIQUE (center_id, code) ) STRICT; CREATE TABLE IF NOT EXISTS center_change_requests ( id TEXT PRIMARY KEY, center_id TEXT REFERENCES test_centers(id) ON DELETE SET NULL, school_id TEXT NOT NULL REFERENCES schools(id) ON DELETE CASCADE, request_type TEXT NOT NULL CHECK (request_type IN ('create', 'update')), code TEXT NOT NULL, name TEXT NOT NULL, province_code TEXT NOT NULL, province_name TEXT NOT NULL, city_code TEXT NOT NULL, city_name TEXT NOT NULL, district_code TEXT NOT NULL, district_name TEXT NOT NULL, address TEXT NOT NULL, contact TEXT, manager_name TEXT, manager_phone TEXT, emergency_phone TEXT, gate_open_time TEXT, transport TEXT, center_status TEXT NOT NULL CHECK (center_status IN ('active', 'inactive')), notes TEXT, status TEXT NOT NULL CHECK (status IN ('pending', 'approved', 'rejected')), review_note TEXT, requested_by TEXT REFERENCES users(id) ON DELETE SET NULL, created_at TEXT NOT NULL, reviewed_at TEXT ) STRICT; CREATE TABLE IF NOT EXISTS center_change_rooms ( id TEXT PRIMARY KEY, request_id TEXT NOT NULL REFERENCES center_change_requests(id) ON DELETE CASCADE, room_id TEXT, code TEXT NOT NULL, name TEXT NOT NULL, building TEXT NOT NULL, floor TEXT, capacity INTEGER NOT NULL CHECK (capacity > 0), seat_plan TEXT, seat_start INTEGER NOT NULL DEFAULT 1, seat_end INTEGER NOT NULL, room_type TEXT NOT NULL CHECK (room_type IN ('standard', 'computer', 'accessible', 'spare')), status TEXT NOT NULL CHECK (status IN ('active', 'inactive')), notes TEXT, UNIQUE (request_id, code) ) STRICT; CREATE TABLE IF NOT EXISTS number_rules ( id TEXT PRIMARY KEY, name TEXT NOT NULL, separator TEXT NOT NULL DEFAULT '', active INTEGER NOT NULL DEFAULT 0 CHECK (active IN (0, 1)), created_by TEXT REFERENCES users(id) ON DELETE SET NULL, updated_at TEXT NOT NULL ) STRICT; CREATE TABLE IF NOT EXISTS number_rule_segments ( id TEXT PRIMARY KEY, rule_id TEXT NOT NULL REFERENCES number_rules(id) ON DELETE CASCADE, position INTEGER NOT NULL, type TEXT NOT NULL CHECK (type IN ('year', 'school_code', 'gender', 'sequence', 'literal')), value TEXT, width INTEGER NOT NULL DEFAULT 0, UNIQUE (rule_id, position) ) STRICT; CREATE TABLE IF NOT EXISTS candidate_account_batches ( id TEXT PRIMARY KEY, school_id TEXT NOT NULL REFERENCES schools(id) ON DELETE CASCADE, requested_by TEXT REFERENCES users(id) ON DELETE SET NULL, status TEXT NOT NULL CHECK (status IN ('pending', 'approved', 'rejected')), review_note TEXT, created_at TEXT NOT NULL, reviewed_at TEXT ) STRICT; CREATE TABLE IF NOT EXISTS candidate_account_batch_items ( id TEXT PRIMARY KEY, batch_id TEXT NOT NULL REFERENCES candidate_account_batches(id) ON DELETE CASCADE, class_id TEXT NOT NULL REFERENCES school_classes(id) ON DELETE RESTRICT, position INTEGER NOT NULL, candidate_number TEXT UNIQUE, initial_password TEXT, user_id TEXT UNIQUE REFERENCES users(id) ON DELETE SET NULL, created_at TEXT, UNIQUE (batch_id, position) ) STRICT; CREATE TABLE IF NOT EXISTS workflow_definitions ( id TEXT PRIMARY KEY, business_type TEXT NOT NULL CHECK (business_type IN ('profile_change', 'registration_review', 'center_change', 'candidate_account_batch', 'score_appeal')), name TEXT NOT NULL, active INTEGER NOT NULL DEFAULT 1 CHECK (active IN (0, 1)), updated_by TEXT REFERENCES users(id) ON DELETE SET NULL, updated_at TEXT NOT NULL, UNIQUE (business_type, active) ) STRICT; CREATE TABLE IF NOT EXISTS workflow_steps ( id TEXT PRIMARY KEY, workflow_id TEXT NOT NULL REFERENCES workflow_definitions(id) ON DELETE CASCADE, position INTEGER NOT NULL, name TEXT NOT NULL, admin_level TEXT NOT NULL CHECK (admin_level IN ('class', 'school', 'super')), UNIQUE (workflow_id, position) ) STRICT; CREATE TABLE IF NOT EXISTS workflow_instances ( id TEXT PRIMARY KEY, workflow_id TEXT NOT NULL REFERENCES workflow_definitions(id) ON DELETE RESTRICT, business_type TEXT NOT NULL, business_id TEXT NOT NULL, status TEXT NOT NULL CHECK (status IN ('pending', 'approved', 'rejected')), current_step INTEGER NOT NULL DEFAULT 1, assignee_id TEXT REFERENCES users(id) ON DELETE SET NULL, created_at TEXT NOT NULL, completed_at TEXT ) STRICT; CREATE TABLE IF NOT EXISTS workflow_actions ( id TEXT PRIMARY KEY, instance_id TEXT NOT NULL REFERENCES workflow_instances(id) ON DELETE CASCADE, actor_id TEXT REFERENCES users(id) ON DELETE SET NULL, action TEXT NOT NULL CHECK (action IN ('submit', 'approve', 'reject', 'transfer', 'return', 'supervise')), note TEXT, from_assignee_id TEXT REFERENCES users(id) ON DELETE SET NULL, to_assignee_id TEXT REFERENCES users(id) ON DELETE SET NULL, created_at TEXT NOT NULL ) STRICT; CREATE TABLE IF NOT EXISTS audit_logs ( id TEXT PRIMARY KEY, actor_id TEXT REFERENCES users(id) ON DELETE SET NULL, action TEXT NOT NULL, detail TEXT NOT NULL, created_at TEXT NOT NULL ) STRICT; CREATE TABLE IF NOT EXISTS admission_records ( id TEXT PRIMARY KEY, kind TEXT NOT NULL CHECK (kind IN ('setting', 'plan', 'preference', 'placement', 'notification', 'indicator_qualification', 'qualification_publication', 'cutoff_publication')), exam_id TEXT NOT NULL REFERENCES exams(id) ON DELETE CASCADE, user_id TEXT REFERENCES users(id) ON DELETE CASCADE, school_id TEXT REFERENCES schools(id) ON DELETE CASCADE, status TEXT NOT NULL, payload_json TEXT NOT NULL DEFAULT '{}', created_at TEXT NOT NULL, updated_at TEXT NOT NULL ) STRICT; CREATE INDEX IF NOT EXISTS idx_profiles_status ON candidate_profiles(status); CREATE INDEX IF NOT EXISTS idx_users_archive_scope ON users(role, school_id, class_id, archived_at); CREATE INDEX IF NOT EXISTS idx_exams_archive ON exams(archived_at, exam_end); CREATE INDEX IF NOT EXISTS idx_profiles_scope ON candidate_profiles(school_id, class_id, status); CREATE INDEX IF NOT EXISTS idx_notices_status_publish ON notices(status, publish_at); CREATE INDEX IF NOT EXISTS idx_exams_status_registration ON exams(status, registration_start, registration_end); CREATE INDEX IF NOT EXISTS idx_subjects_exam ON exam_subjects(exam_id, position); CREATE INDEX IF NOT EXISTS idx_registrations_status ON registrations(status); CREATE INDEX IF NOT EXISTS idx_registrations_payment ON registrations(payment_status, paid_at); CREATE INDEX IF NOT EXISTS idx_registrations_exam ON registrations(exam_id); CREATE INDEX IF NOT EXISTS idx_arrangement_plans_exam ON exam_arrangement_plans(exam_id, generated_at); CREATE INDEX IF NOT EXISTS idx_admit_subjects_room ON admit_card_subjects(subject_id, room_id, seat); CREATE INDEX IF NOT EXISTS idx_workflow_inbox ON workflow_instances(status, assignee_id, business_type); CREATE UNIQUE INDEX IF NOT EXISTS uq_test_centers_code ON test_centers(code); CREATE INDEX IF NOT EXISTS idx_rooms_center ON test_rooms(center_id, status, code); CREATE INDEX IF NOT EXISTS idx_center_changes_school ON center_change_requests(school_id, status, created_at); CREATE INDEX IF NOT EXISTS idx_account_batches_school ON candidate_account_batches(school_id, status, created_at); CREATE INDEX IF NOT EXISTS idx_account_batch_items ON candidate_account_batch_items(batch_id, class_id, position); CREATE INDEX IF NOT EXISTS idx_results_registration ON results(registration_id, published); CREATE INDEX IF NOT EXISTS idx_audit_created ON audit_logs(created_at); CREATE INDEX IF NOT EXISTS idx_admission_records_lookup ON admission_records(kind, exam_id, school_id, user_id, status); CREATE TRIGGER IF NOT EXISTS trg_results_lock_archived_insert BEFORE INSERT ON results WHEN EXISTS ( SELECT 1 FROM registrations registration JOIN exams exam ON exam.id = registration.exam_id WHERE registration.id = NEW.registration_id AND exam.archived_at IS NOT NULL ) BEGIN SELECT RAISE(ABORT, '归档考试成绩已永久锁定'); END; CREATE TRIGGER IF NOT EXISTS trg_results_lock_archived_update BEFORE UPDATE ON results WHEN EXISTS ( SELECT 1 FROM registrations registration JOIN exams exam ON exam.id = registration.exam_id WHERE registration.id IN (OLD.registration_id, NEW.registration_id) AND exam.archived_at IS NOT NULL ) BEGIN SELECT RAISE(ABORT, '归档考试成绩已永久锁定'); END; CREATE TRIGGER IF NOT EXISTS trg_results_lock_archived_delete BEFORE DELETE ON results WHEN EXISTS ( SELECT 1 FROM registrations registration JOIN exams exam ON exam.id = registration.exam_id WHERE registration.id = OLD.registration_id AND exam.archived_at IS NOT NULL ) BEGIN SELECT RAISE(ABORT, '归档考试成绩已永久锁定'); END; `; export const mysqlSchema = [ `CREATE TABLE IF NOT EXISTS schema_metadata ( id TINYINT UNSIGNED NOT NULL, schema_version INT UNSIGNED NOT NULL DEFAULT 1, app_version INT UNSIGNED NOT NULL DEFAULT 1, self_registration_enabled BOOLEAN NOT NULL DEFAULT FALSE, created_at VARCHAR(35) NOT NULL, PRIMARY KEY (id), CONSTRAINT chk_schema_metadata_singleton CHECK (id = 1) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci`, `CREATE TABLE IF NOT EXISTS organization ( id TINYINT UNSIGNED NOT NULL, name VARCHAR(120) NOT NULL, code VARCHAR(60) NOT NULL, phone VARCHAR(60) NOT NULL, address VARCHAR(255) NOT NULL, PRIMARY KEY (id), CONSTRAINT chk_organization_singleton CHECK (id = 1) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci`, `CREATE TABLE IF NOT EXISTS schools ( id VARCHAR(64) NOT NULL, name VARCHAR(160) NOT NULL, code VARCHAR(40) NOT NULL, address VARCHAR(255) NULL, is_source_school BOOLEAN NOT NULL DEFAULT TRUE, is_admission_school BOOLEAN NOT NULL DEFAULT TRUE, active BOOLEAN NOT NULL DEFAULT TRUE, PRIMARY KEY (id), UNIQUE KEY uq_schools_name (name), UNIQUE KEY uq_schools_code (code) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci`, `CREATE TABLE IF NOT EXISTS school_classes ( id VARCHAR(64) NOT NULL, school_id VARCHAR(64) NOT NULL, name VARCHAR(100) NOT NULL, grade VARCHAR(60) NOT NULL, active BOOLEAN NOT NULL DEFAULT TRUE, PRIMARY KEY (id), UNIQUE KEY uq_classes_school_name (school_id, name), CONSTRAINT fk_classes_school FOREIGN KEY (school_id) REFERENCES schools(id) ON DELETE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci`, `CREATE TABLE IF NOT EXISTS school_student_partitions ( school_id VARCHAR(64) NOT NULL, partition_key VARCHAR(32) NOT NULL, students_table VARCHAR(64) NOT NULL, created_at VARCHAR(35) NOT NULL, updated_at VARCHAR(35) NOT NULL, PRIMARY KEY (school_id), UNIQUE KEY uq_school_partitions_key (partition_key), UNIQUE KEY uq_school_partitions_table (students_table), CONSTRAINT fk_school_partitions_school FOREIGN KEY (school_id) REFERENCES schools(id) ON DELETE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci`, `CREATE TABLE IF NOT EXISTS users ( id VARCHAR(64) NOT NULL, username VARCHAR(100) NOT NULL, candidate_number VARCHAR(120) NULL, password_hash VARCHAR(255) NOT NULL, role ENUM('admin', 'candidate', 'admission_school') NOT NULL, admin_level ENUM('super', 'school', 'class') NULL, school_id VARCHAR(64) NULL, class_id VARCHAR(64) NULL, active BOOLEAN NOT NULL DEFAULT TRUE, must_change_password BOOLEAN NOT NULL DEFAULT FALSE, totp_enabled BOOLEAN NOT NULL DEFAULT FALSE, totp_secret_encrypted VARCHAR(512) NULL, totp_recovery_codes VARCHAR(2048) NOT NULL DEFAULT '[]', totp_last_used_step BIGINT NULL, archived_at VARCHAR(35) NULL, archived_by VARCHAR(64) NULL, display_name VARCHAR(100) NOT NULL, created_at VARCHAR(35) NOT NULL, PRIMARY KEY (id), UNIQUE KEY uq_users_username (username), UNIQUE KEY uq_users_candidate_number (candidate_number), KEY idx_users_admin_scope (role, admin_level, school_id, class_id), KEY idx_users_archive_scope (role, school_id, class_id, archived_at), CONSTRAINT fk_users_school FOREIGN KEY (school_id) REFERENCES schools(id) ON DELETE SET NULL, CONSTRAINT fk_users_class FOREIGN KEY (class_id) REFERENCES school_classes(id) ON DELETE SET NULL, CONSTRAINT fk_users_archiver FOREIGN KEY (archived_by) REFERENCES users(id) ON DELETE RESTRICT ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci`, `CREATE TABLE IF NOT EXISTS candidate_profiles ( id VARCHAR(64) NOT NULL, user_id VARCHAR(64) NOT NULL, name VARCHAR(100) NOT NULL, gender VARCHAR(20) NULL, id_number VARCHAR(60) NOT NULL, phone VARCHAR(60) NOT NULL, email VARCHAR(160) NULL, school VARCHAR(160) NULL, grade VARCHAR(100) NULL, school_id VARCHAR(64) NULL, class_id VARCHAR(64) NULL, province_code VARCHAR(6) NULL, province_name VARCHAR(80) NULL, city_code VARCHAR(6) NULL, city_name VARCHAR(100) NULL, district_code VARCHAR(6) NULL, district_name VARCHAR(100) NULL, address VARCHAR(255) NULL, emergency_contact VARCHAR(100) NULL, emergency_phone VARCHAR(60) NULL, native_place VARCHAR(160) NULL, birth_date VARCHAR(20) NULL, ethnicity VARCHAR(60) NULL, postal_code VARCHAR(20) NULL, guardian_name VARCHAR(100) NULL, guardian_phone VARCHAR(60) NULL, specialty_category VARCHAR(30) NULL, specialty_type VARCHAR(40) NULL, specialty_types JSON NOT NULL DEFAULT (JSON_ARRAY()), specialty_certificate VARCHAR(255) NULL, policy_eligibility VARCHAR(255) NULL, profile_completed BOOLEAN NOT NULL DEFAULT FALSE, status ENUM('pending', 'approved', 'rejected') NOT NULL, review_note VARCHAR(500) NULL, reviewed_at VARCHAR(35) NULL, reviewer_id VARCHAR(64) NULL, updated_at VARCHAR(35) NOT NULL, PRIMARY KEY (id), UNIQUE KEY uq_profiles_user (user_id), UNIQUE KEY uq_profiles_id_number (id_number), KEY idx_profiles_status (status), CONSTRAINT fk_profiles_user FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE, CONSTRAINT fk_profiles_reviewer FOREIGN KEY (reviewer_id) REFERENCES users(id) ON DELETE SET NULL, CONSTRAINT fk_profiles_school FOREIGN KEY (school_id) REFERENCES schools(id) ON DELETE SET NULL, CONSTRAINT fk_profiles_class FOREIGN KEY (class_id) REFERENCES school_classes(id) ON DELETE SET NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci`, `CREATE TABLE IF NOT EXISTS notices ( id VARCHAR(64) NOT NULL, title VARCHAR(240) NOT NULL, summary VARCHAR(500) NOT NULL, content TEXT NOT NULL, category VARCHAR(60) NOT NULL, pinned BOOLEAN NOT NULL DEFAULT FALSE, status ENUM('draft', 'published') NOT NULL, publish_at VARCHAR(35) NULL, created_at VARCHAR(35) NULL, author VARCHAR(100) NOT NULL, PRIMARY KEY (id), KEY idx_notices_status_publish (status, publish_at) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci`, `CREATE TABLE IF NOT EXISTS exams ( id VARCHAR(64) NOT NULL, code VARCHAR(60) NOT NULL, name VARCHAR(200) NOT NULL, description TEXT NOT NULL, registration_start VARCHAR(35) NOT NULL, registration_end VARCHAR(35) NOT NULL, exam_start VARCHAR(35) NOT NULL, exam_end VARCHAR(35) NOT NULL, admit_download_start VARCHAR(35) NOT NULL, admit_download_end VARCHAR(35) NOT NULL, location VARCHAR(200) NOT NULL, pass_policy ENUM('fixed_score', 'score_ratio', 'rank_percent', 'subject_scores', 'none') NOT NULL DEFAULT 'rank_percent', pass_value DOUBLE NOT NULL DEFAULT 60, status ENUM('draft', 'published', 'closed') NOT NULL, archived_at VARCHAR(35) NULL, archived_by VARCHAR(64) NULL, created_at VARCHAR(35) NOT NULL, PRIMARY KEY (id), UNIQUE KEY uq_exams_code (code), KEY idx_exams_status_registration (status, registration_start, registration_end), KEY idx_exams_archive (archived_at, exam_end), CONSTRAINT fk_exams_archived_by FOREIGN KEY (archived_by) REFERENCES users(id) ON DELETE RESTRICT ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci`, `CREATE TABLE IF NOT EXISTS exam_data_partitions ( exam_id VARCHAR(64) NOT NULL, partition_key VARCHAR(32) NOT NULL, candidates_table VARCHAR(64) NOT NULL, admissions_table VARCHAR(64) NOT NULL, results_table VARCHAR(64) NOT NULL, centers_table VARCHAR(64) NOT NULL, created_at VARCHAR(35) NOT NULL, updated_at VARCHAR(35) NOT NULL, PRIMARY KEY (exam_id), UNIQUE KEY uq_exam_partitions_key (partition_key), UNIQUE KEY uq_exam_partitions_candidates (candidates_table), UNIQUE KEY uq_exam_partitions_admissions (admissions_table), UNIQUE KEY uq_exam_partitions_results (results_table), UNIQUE KEY uq_exam_partitions_centers (centers_table), CONSTRAINT fk_exam_partitions_exam FOREIGN KEY (exam_id) REFERENCES exams(id) ON DELETE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci`, `CREATE TABLE IF NOT EXISTS exam_subjects ( id VARCHAR(64) NOT NULL, exam_id VARCHAR(64) NOT NULL, name VARCHAR(100) NOT NULL, subject_date VARCHAR(35) NOT NULL, start_time VARCHAR(20) NOT NULL, end_time VARCHAR(20) NOT NULL, fee DOUBLE NOT NULL DEFAULT 0, full_score DOUBLE NOT NULL DEFAULT 150, pass_score DOUBLE NOT NULL DEFAULT 90, pass_rule ENUM('fixed_score', 'score_ratio', 'none') NOT NULL DEFAULT 'fixed_score', pass_value DOUBLE NOT NULL DEFAULT 90, position INT UNSIGNED NOT NULL, PRIMARY KEY (id), UNIQUE KEY uq_subjects_exam_position (exam_id, position), KEY idx_subjects_exam (exam_id, position), CONSTRAINT fk_subjects_exam FOREIGN KEY (exam_id) REFERENCES exams(id) ON DELETE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci`, `CREATE TABLE IF NOT EXISTS registrations ( id VARCHAR(64) NOT NULL, user_id VARCHAR(64) NOT NULL, exam_id VARCHAR(64) NOT NULL, status ENUM('pending', 'approved', 'rejected') NOT NULL, payment_status ENUM('unpaid', 'paid') NOT NULL, paid_at VARCHAR(35) NULL, paid_by VARCHAR(64) NULL, created_at VARCHAR(35) NOT NULL, reviewed_at VARCHAR(35) NULL, review_note VARCHAR(500) NULL, registration_number VARCHAR(120) NULL, number_rule_id VARCHAR(64) NULL, feature_score DECIMAL(8,2) NOT NULL DEFAULT 0, PRIMARY KEY (id), UNIQUE KEY uq_registrations_user_exam (user_id, exam_id), KEY idx_registrations_status (status), KEY idx_registrations_payment (payment_status, paid_at), KEY idx_registrations_exam (exam_id), CONSTRAINT fk_registrations_user FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE, CONSTRAINT fk_registrations_exam FOREIGN KEY (exam_id) REFERENCES exams(id) ON DELETE CASCADE, CONSTRAINT fk_registrations_paid_by FOREIGN KEY (paid_by) REFERENCES users(id) ON DELETE SET NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci`, `CREATE TABLE IF NOT EXISTS registration_subjects ( registration_id VARCHAR(64) NOT NULL, subject_id VARCHAR(64) NOT NULL, PRIMARY KEY (registration_id, subject_id), CONSTRAINT fk_registration_subjects_registration FOREIGN KEY (registration_id) REFERENCES registrations(id) ON DELETE CASCADE, CONSTRAINT fk_registration_subjects_subject FOREIGN KEY (subject_id) REFERENCES exam_subjects(id) ON DELETE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci`, `CREATE TABLE IF NOT EXISTS results ( id VARCHAR(64) NOT NULL, registration_id VARCHAR(64) NOT NULL, subject_id VARCHAR(64) NOT NULL, score DOUBLE NOT NULL, grade VARCHAR(20) NOT NULL, published BOOLEAN NOT NULL DEFAULT FALSE, updated_at VARCHAR(35) NULL, published_at VARCHAR(35) NULL, PRIMARY KEY (id), UNIQUE KEY uq_results_registration_subject (registration_id, subject_id), KEY idx_results_registration (registration_id, published), CONSTRAINT chk_results_score CHECK (score >= 0 AND score <= 150), CONSTRAINT fk_results_registration FOREIGN KEY (registration_id) REFERENCES registrations(id) ON DELETE CASCADE, CONSTRAINT fk_results_subject FOREIGN KEY (subject_id) REFERENCES exam_subjects(id) ON DELETE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci`, `CREATE TABLE IF NOT EXISTS test_centers ( id VARCHAR(64) NOT NULL, school_id VARCHAR(64) NOT NULL, code VARCHAR(40) NOT NULL, name VARCHAR(200) NOT NULL, province_code VARCHAR(6) NOT NULL, province_name VARCHAR(80) NOT NULL, city_code VARCHAR(6) NOT NULL, city_name VARCHAR(100) NOT NULL, district_code VARCHAR(6) NOT NULL, district_name VARCHAR(100) NOT NULL, address VARCHAR(255) NOT NULL, contact VARCHAR(100) NULL, manager_name VARCHAR(100) NULL, manager_phone VARCHAR(60) NULL, emergency_phone VARCHAR(60) NULL, gate_open_time VARCHAR(40) NULL, transport VARCHAR(500) NULL, status ENUM('active', 'inactive') NOT NULL DEFAULT 'active', notes VARCHAR(1000) NULL, rooms TEXT NOT NULL, updated_at VARCHAR(35) NOT NULL, PRIMARY KEY (id), UNIQUE KEY uq_centers_code (code), UNIQUE KEY uq_centers_school_name (school_id, name), CONSTRAINT fk_centers_school FOREIGN KEY (school_id) REFERENCES schools(id) ON DELETE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci`, `CREATE TABLE IF NOT EXISTS test_rooms ( id VARCHAR(64) NOT NULL, center_id VARCHAR(64) NOT NULL, code VARCHAR(40) NOT NULL, name VARCHAR(120) NOT NULL, building VARCHAR(120) NOT NULL, floor VARCHAR(40) NULL, capacity INT UNSIGNED NOT NULL, seat_plan VARCHAR(500) NULL, seat_start INT UNSIGNED NOT NULL DEFAULT 1, seat_end INT UNSIGNED NOT NULL, room_type ENUM('standard', 'computer', 'accessible', 'spare') NOT NULL, status ENUM('active', 'inactive') NOT NULL DEFAULT 'active', notes VARCHAR(500) NULL, PRIMARY KEY (id), UNIQUE KEY uq_rooms_center_code (center_id, code), KEY idx_rooms_center (center_id, status, code), CONSTRAINT fk_rooms_center FOREIGN KEY (center_id) REFERENCES test_centers(id) ON DELETE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci`, `CREATE TABLE IF NOT EXISTS admission_number_rules ( id VARCHAR(64) NOT NULL, code VARCHAR(80) NOT NULL, name VARCHAR(160) NOT NULL, description VARCHAR(500) NOT NULL, \`separator\` VARCHAR(10) NOT NULL DEFAULT '', segments_json JSON NOT NULL, example VARCHAR(120) NOT NULL, active BOOLEAN NOT NULL DEFAULT TRUE, created_at VARCHAR(35) NOT NULL, PRIMARY KEY (id), UNIQUE KEY uq_admission_rules_code (code) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci`, `CREATE TABLE IF NOT EXISTS exam_arrangement_plans ( id VARCHAR(64) NOT NULL, exam_id VARCHAR(64) NOT NULL, number_rule_id VARCHAR(64) NOT NULL, mixing_scope ENUM('class', 'school', 'district', 'city', 'province') NOT NULL, random_seed VARCHAR(80) NOT NULL, candidate_count INT UNSIGNED NOT NULL, center_count INT UNSIGNED NOT NULL, subject_assignment_count INT UNSIGNED NOT NULL, subject_combination_count INT UNSIGNED NOT NULL, same_school_center_rate DOUBLE NOT NULL, warnings_json JSON NOT NULL, generated_by VARCHAR(64) NULL, generated_at VARCHAR(35) NOT NULL, PRIMARY KEY (id), UNIQUE KEY uq_arrangement_plans_exam (exam_id), CONSTRAINT fk_arrangement_plans_exam FOREIGN KEY (exam_id) REFERENCES exams(id) ON DELETE CASCADE, CONSTRAINT fk_arrangement_plans_rule FOREIGN KEY (number_rule_id) REFERENCES admission_number_rules(id), CONSTRAINT fk_arrangement_plans_generator FOREIGN KEY (generated_by) REFERENCES users(id) ON DELETE SET NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci`, `CREATE TABLE IF NOT EXISTS admit_cards ( registration_id VARCHAR(64) NOT NULL, plan_id VARCHAR(64) NOT NULL, card_number VARCHAR(100) NOT NULL, center_id VARCHAR(64) NULL, test_center VARCHAR(200) NOT NULL, center_code VARCHAR(40) NOT NULL, center_address VARCHAR(500) NOT NULL, generated_at VARCHAR(35) NOT NULL, PRIMARY KEY (registration_id), UNIQUE KEY uq_admit_cards_number (card_number), CONSTRAINT fk_admit_cards_registration FOREIGN KEY (registration_id) REFERENCES registrations(id) ON DELETE CASCADE, CONSTRAINT fk_admit_cards_plan FOREIGN KEY (plan_id) REFERENCES exam_arrangement_plans(id) ON DELETE CASCADE, CONSTRAINT fk_admit_cards_center FOREIGN KEY (center_id) REFERENCES test_centers(id) ON DELETE SET NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci`, `CREATE TABLE IF NOT EXISTS admit_card_subjects ( registration_id VARCHAR(64) NOT NULL, subject_id VARCHAR(64) NOT NULL, room_id VARCHAR(64) NULL, room VARCHAR(120) NOT NULL, room_code VARCHAR(40) NOT NULL, exam_room_code VARCHAR(20) NOT NULL, building VARCHAR(120) NOT NULL, floor VARCHAR(80) NOT NULL, seat VARCHAR(30) NOT NULL, subject_signature VARCHAR(1000) NOT NULL, PRIMARY KEY (registration_id, subject_id), UNIQUE KEY uq_admit_subject_room_seat (subject_id, room_id, seat), KEY idx_admit_subjects_room (subject_id, room_id, seat), CONSTRAINT fk_admit_subjects_card FOREIGN KEY (registration_id) REFERENCES admit_cards(registration_id) ON DELETE CASCADE, CONSTRAINT fk_admit_subjects_subject FOREIGN KEY (subject_id) REFERENCES exam_subjects(id) ON DELETE CASCADE, CONSTRAINT fk_admit_subjects_room FOREIGN KEY (room_id) REFERENCES test_rooms(id) ON DELETE SET NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci`, `CREATE TABLE IF NOT EXISTS center_change_requests ( id VARCHAR(64) NOT NULL, center_id VARCHAR(64) NULL, school_id VARCHAR(64) NOT NULL, request_type ENUM('create', 'update') NOT NULL, code VARCHAR(40) NOT NULL, name VARCHAR(200) NOT NULL, province_code VARCHAR(6) NOT NULL, province_name VARCHAR(80) NOT NULL, city_code VARCHAR(6) NOT NULL, city_name VARCHAR(100) NOT NULL, district_code VARCHAR(6) NOT NULL, district_name VARCHAR(100) NOT NULL, address VARCHAR(255) NOT NULL, contact VARCHAR(100) NULL, manager_name VARCHAR(100) NULL, manager_phone VARCHAR(60) NULL, emergency_phone VARCHAR(60) NULL, gate_open_time VARCHAR(40) NULL, transport VARCHAR(500) NULL, center_status ENUM('active', 'inactive') NOT NULL, notes VARCHAR(1000) NULL, status ENUM('pending', 'approved', 'rejected') NOT NULL, review_note VARCHAR(500) NULL, requested_by VARCHAR(64) NULL, created_at VARCHAR(35) NOT NULL, reviewed_at VARCHAR(35) NULL, PRIMARY KEY (id), KEY idx_center_changes_school (school_id, status, created_at), CONSTRAINT fk_center_change_center FOREIGN KEY (center_id) REFERENCES test_centers(id) ON DELETE SET NULL, CONSTRAINT fk_center_change_school FOREIGN KEY (school_id) REFERENCES schools(id) ON DELETE CASCADE, CONSTRAINT fk_center_change_requester FOREIGN KEY (requested_by) REFERENCES users(id) ON DELETE SET NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci`, `CREATE TABLE IF NOT EXISTS center_change_rooms ( id VARCHAR(64) NOT NULL, request_id VARCHAR(64) NOT NULL, room_id VARCHAR(64) NULL, code VARCHAR(40) NOT NULL, name VARCHAR(120) NOT NULL, building VARCHAR(120) NOT NULL, floor VARCHAR(40) NULL, capacity INT UNSIGNED NOT NULL, seat_plan VARCHAR(500) NULL, seat_start INT UNSIGNED NOT NULL DEFAULT 1, seat_end INT UNSIGNED NOT NULL, room_type ENUM('standard', 'computer', 'accessible', 'spare') NOT NULL, status ENUM('active', 'inactive') NOT NULL, notes VARCHAR(500) NULL, PRIMARY KEY (id), UNIQUE KEY uq_center_change_rooms_code (request_id, code), CONSTRAINT fk_center_change_rooms_request FOREIGN KEY (request_id) REFERENCES center_change_requests(id) ON DELETE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci`, `CREATE TABLE IF NOT EXISTS number_rules ( id VARCHAR(64) NOT NULL, name VARCHAR(120) NOT NULL, \`separator\` VARCHAR(10) NOT NULL DEFAULT '', active BOOLEAN NOT NULL DEFAULT FALSE, created_by VARCHAR(64) NULL, updated_at VARCHAR(35) NOT NULL, PRIMARY KEY (id), CONSTRAINT fk_number_rules_creator FOREIGN KEY (created_by) REFERENCES users(id) ON DELETE SET NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci`, `CREATE TABLE IF NOT EXISTS number_rule_segments ( id VARCHAR(64) NOT NULL, rule_id VARCHAR(64) NOT NULL, position INT UNSIGNED NOT NULL, type ENUM('year', 'school_code', 'gender', 'sequence', 'literal') NOT NULL, value VARCHAR(60) NULL, width INT UNSIGNED NOT NULL DEFAULT 0, PRIMARY KEY (id), UNIQUE KEY uq_rule_segments_position (rule_id, position), CONSTRAINT fk_rule_segments_rule FOREIGN KEY (rule_id) REFERENCES number_rules(id) ON DELETE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci`, `CREATE TABLE IF NOT EXISTS candidate_account_batches ( id VARCHAR(64) NOT NULL, school_id VARCHAR(64) NOT NULL, requested_by VARCHAR(64) NULL, status ENUM('pending', 'approved', 'rejected') NOT NULL, review_note VARCHAR(500) NULL, created_at VARCHAR(35) NOT NULL, reviewed_at VARCHAR(35) NULL, PRIMARY KEY (id), KEY idx_account_batches_school (school_id, status, created_at), CONSTRAINT fk_account_batches_school FOREIGN KEY (school_id) REFERENCES schools(id) ON DELETE CASCADE, CONSTRAINT fk_account_batches_requester FOREIGN KEY (requested_by) REFERENCES users(id) ON DELETE SET NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci`, `CREATE TABLE IF NOT EXISTS candidate_account_batch_items ( id VARCHAR(64) NOT NULL, batch_id VARCHAR(64) NOT NULL, class_id VARCHAR(64) NOT NULL, position INT UNSIGNED NOT NULL, candidate_number VARCHAR(120) NULL, initial_password VARCHAR(120) NULL, user_id VARCHAR(64) NULL, created_at VARCHAR(35) NULL, PRIMARY KEY (id), UNIQUE KEY uq_account_batch_position (batch_id, position), UNIQUE KEY uq_account_batch_number (candidate_number), UNIQUE KEY uq_account_batch_user (user_id), KEY idx_account_batch_items (batch_id, class_id, position), CONSTRAINT fk_account_batch_items_batch FOREIGN KEY (batch_id) REFERENCES candidate_account_batches(id) ON DELETE CASCADE, CONSTRAINT fk_account_batch_items_class FOREIGN KEY (class_id) REFERENCES school_classes(id), CONSTRAINT fk_account_batch_items_user FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE SET NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci`, `CREATE TABLE IF NOT EXISTS workflow_definitions ( id VARCHAR(64) NOT NULL, business_type ENUM('profile_change', 'registration_review', 'center_change', 'candidate_account_batch', 'score_appeal') NOT NULL, name VARCHAR(120) NOT NULL, active BOOLEAN NOT NULL DEFAULT TRUE, updated_by VARCHAR(64) NULL, updated_at VARCHAR(35) NOT NULL, PRIMARY KEY (id), UNIQUE KEY uq_workflow_type_active (business_type, active), CONSTRAINT fk_workflow_updater FOREIGN KEY (updated_by) REFERENCES users(id) ON DELETE SET NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci`, `CREATE TABLE IF NOT EXISTS workflow_steps ( id VARCHAR(64) NOT NULL, workflow_id VARCHAR(64) NOT NULL, position INT UNSIGNED NOT NULL, name VARCHAR(120) NOT NULL, admin_level ENUM('class', 'school', 'super') NOT NULL, PRIMARY KEY (id), UNIQUE KEY uq_workflow_steps_position (workflow_id, position), CONSTRAINT fk_workflow_steps_definition FOREIGN KEY (workflow_id) REFERENCES workflow_definitions(id) ON DELETE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci`, `CREATE TABLE IF NOT EXISTS workflow_instances ( id VARCHAR(64) NOT NULL, workflow_id VARCHAR(64) NOT NULL, business_type VARCHAR(40) NOT NULL, business_id VARCHAR(64) NOT NULL, status ENUM('pending', 'approved', 'rejected') NOT NULL, current_step INT UNSIGNED NOT NULL DEFAULT 1, assignee_id VARCHAR(64) NULL, created_at VARCHAR(35) NOT NULL, completed_at VARCHAR(35) NULL, PRIMARY KEY (id), KEY idx_workflow_inbox (status, assignee_id, business_type), CONSTRAINT fk_workflow_instance_definition FOREIGN KEY (workflow_id) REFERENCES workflow_definitions(id), CONSTRAINT fk_workflow_instance_assignee FOREIGN KEY (assignee_id) REFERENCES users(id) ON DELETE SET NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci`, `CREATE TABLE IF NOT EXISTS workflow_actions ( id VARCHAR(64) NOT NULL, instance_id VARCHAR(64) NOT NULL, actor_id VARCHAR(64) NULL, action ENUM('submit', 'approve', 'reject', 'transfer', 'return', 'supervise') NOT NULL, note VARCHAR(500) NULL, from_assignee_id VARCHAR(64) NULL, to_assignee_id VARCHAR(64) NULL, created_at VARCHAR(35) NOT NULL, PRIMARY KEY (id), KEY idx_workflow_actions_instance (instance_id, created_at), CONSTRAINT fk_workflow_action_instance FOREIGN KEY (instance_id) REFERENCES workflow_instances(id) ON DELETE CASCADE, CONSTRAINT fk_workflow_action_actor FOREIGN KEY (actor_id) REFERENCES users(id) ON DELETE SET NULL, CONSTRAINT fk_workflow_action_from FOREIGN KEY (from_assignee_id) REFERENCES users(id) ON DELETE SET NULL, CONSTRAINT fk_workflow_action_to FOREIGN KEY (to_assignee_id) REFERENCES users(id) ON DELETE SET NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci`, `CREATE TABLE IF NOT EXISTS admission_records ( id VARCHAR(64) NOT NULL, kind ENUM('setting', 'plan', 'preference', 'placement', 'notification', 'indicator_qualification', 'qualification_publication', 'cutoff_publication') NOT NULL, exam_id VARCHAR(64) NOT NULL, user_id VARCHAR(64) NULL, school_id VARCHAR(64) NULL, status VARCHAR(40) NOT NULL, payload_json JSON NOT NULL, created_at VARCHAR(35) NOT NULL, updated_at VARCHAR(35) NOT NULL, PRIMARY KEY (id), KEY idx_admission_records_lookup (kind, exam_id, school_id, user_id, status), CONSTRAINT fk_admission_record_exam FOREIGN KEY (exam_id) REFERENCES exams(id) ON DELETE CASCADE, CONSTRAINT fk_admission_record_user FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE, CONSTRAINT fk_admission_record_school FOREIGN KEY (school_id) REFERENCES schools(id) ON DELETE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci`, `CREATE TABLE IF NOT EXISTS audit_logs ( id VARCHAR(64) NOT NULL, actor_id VARCHAR(64) NULL, action VARCHAR(100) NOT NULL, detail VARCHAR(1000) NOT NULL, created_at VARCHAR(35) NOT NULL, PRIMARY KEY (id), KEY idx_audit_created (created_at), CONSTRAINT fk_audit_actor FOREIGN KEY (actor_id) REFERENCES users(id) ON DELETE SET NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci` ];