实现考试归档并永久锁定成绩流程

This commit is contained in:
2026-07-20 17:08:17 +08:00 Unverified
parent 6d22b41ce8
commit 1a87e06fc8
15 changed files with 265 additions and 39 deletions
+41 -3
View File
@@ -66,6 +66,8 @@ export function createMysqlAdapter(context) {
'ALTER TABLE registrations ADD COLUMN IF NOT EXISTS number_rule_id VARCHAR(64) NULL',
"ALTER TABLE exams ADD COLUMN IF NOT EXISTS pass_policy ENUM('fixed_score', 'score_ratio', 'rank_percent', 'subject_scores', 'none') NOT NULL DEFAULT 'rank_percent'",
'ALTER TABLE exams ADD COLUMN IF NOT EXISTS pass_value DOUBLE NOT NULL DEFAULT 60',
'ALTER TABLE exams ADD COLUMN IF NOT EXISTS archived_at VARCHAR(35) NULL',
'ALTER TABLE exams ADD COLUMN IF NOT EXISTS archived_by VARCHAR(64) NULL',
'ALTER TABLE exam_subjects ADD COLUMN IF NOT EXISTS full_score DOUBLE NOT NULL DEFAULT 150',
'ALTER TABLE exam_subjects ADD COLUMN IF NOT EXISTS pass_score DOUBLE NOT NULL DEFAULT 90',
"ALTER TABLE exam_subjects ADD COLUMN IF NOT EXISTS pass_rule ENUM('fixed_score', 'score_ratio', 'none') NOT NULL DEFAULT 'fixed_score'",
@@ -99,6 +101,37 @@ export function createMysqlAdapter(context) {
"ALTER TABLE workflow_definitions MODIFY COLUMN business_type ENUM('profile_change', 'registration_review', 'center_change', 'candidate_account_batch') NOT NULL"
];
for (const statement of mysqlColumnMigrations) await pool.execute(statement);
const [resultLockTriggers] = await pool.execute(`
SELECT TRIGGER_NAME FROM information_schema.TRIGGERS
WHERE TRIGGER_SCHEMA = DATABASE() AND TRIGGER_NAME LIKE 'trg_results_lock_archived_%'
`);
const existingResultLockTriggers = new Set(resultLockTriggers.map(item => item.TRIGGER_NAME));
const mysqlResultLockTriggers = {
trg_results_lock_archived_insert: `CREATE TRIGGER trg_results_lock_archived_insert BEFORE INSERT ON results FOR EACH ROW
BEGIN
IF 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) THEN
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '归档考试成绩已永久锁定';
END IF;
END`,
trg_results_lock_archived_update: `CREATE TRIGGER trg_results_lock_archived_update BEFORE UPDATE ON results FOR EACH ROW
BEGIN
IF 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) THEN
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '归档考试成绩已永久锁定';
END IF;
END`,
trg_results_lock_archived_delete: `CREATE TRIGGER trg_results_lock_archived_delete BEFORE DELETE ON results FOR EACH ROW
BEGIN
IF 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) THEN
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '归档考试成绩已永久锁定';
END IF;
END`
};
for (const [name, statement] of Object.entries(mysqlResultLockTriggers)) {
if (!existingResultLockTriggers.has(name)) await pool.execute(statement);
}
const [legacyRegistrationNumberIndexes] = await pool.execute("SHOW INDEX FROM registrations WHERE Key_name = 'uq_registrations_number'");
if (legacyRegistrationNumberIndexes.length) await pool.execute('ALTER TABLE registrations DROP INDEX uq_registrations_number');
const [existing] = await pool.execute('SELECT id FROM schema_metadata WHERE id = 1');
@@ -155,6 +188,11 @@ export function createMysqlAdapter(context) {
metadataRows[0].schema_version = 13;
metadataRows[0].app_version = 13;
}
if (Number(metadataRows[0]?.schema_version || 1) < 14) {
await pool.execute('UPDATE schema_metadata SET schema_version = 14, app_version = 14 WHERE id = 1');
metadataRows[0].schema_version = 14;
metadataRows[0].app_version = 14;
}
if (Number(metadataRows[0]?.app_version || 1) < 2) {
const extension = seed();
const connection = await pool.getConnection();
@@ -326,8 +364,8 @@ export function createMysqlAdapter(context) {
if (Number(metadataRows[0]?.app_version || 1) < 7) {
await pool.execute('UPDATE schema_metadata SET schema_version = 7, app_version = 7 WHERE id = 1');
}
if (Number(metadataRows[0]?.schema_version || 1) < 13) {
await pool.execute('UPDATE schema_metadata SET schema_version = 13, app_version = 13 WHERE id = 1');
if (Number(metadataRows[0]?.schema_version || 1) < 14) {
await pool.execute('UPDATE schema_metadata SET schema_version = 14, app_version = 14 WHERE id = 1');
}
}
if (!existing.length) {
@@ -337,7 +375,7 @@ export function createMysqlAdapter(context) {
await connection.beginTransaction();
const [insert] = await connection.execute(`
INSERT IGNORE INTO schema_metadata (id, schema_version, app_version, self_registration_enabled, created_at)
VALUES (1, 13, ?, ?, ?)
VALUES (1, 14, ?, ?, ?)
`, [Number(initialState.meta?.version || 1), initialState.settings?.selfRegistrationEnabled ? 1 : 0, initialState.meta?.createdAt || new Date().toISOString()]);
if (insert.affectedRows === 1) {
for (const item of buildSeedOperations(initialState)) await connection.execute(item.sql, item.params);