准考证编排

This commit is contained in:
2026-07-20 13:06:00 +08:00 Unverified
parent 4c7fafc03d
commit 9566542414
14 changed files with 846 additions and 86 deletions
+139 -24
View File
@@ -16,7 +16,10 @@ export const relationalTables = [
'exam_subjects',
'registrations',
'registration_subjects',
'admission_number_rules',
'exam_arrangement_plans',
'admit_cards',
'admit_card_subjects',
'results',
'test_centers',
'test_rooms',
@@ -37,6 +40,7 @@ function validateState(state, source = '数据库') {
const collections = [
'schools', 'classes', 'users', 'candidateProfiles', 'notices', 'exams', 'registrations', 'results',
'testCenters', 'testRooms', 'centerChangeRequests', 'centerChangeRooms',
'admissionNumberRules', 'arrangementPlans',
'numberRules', 'candidateAccountBatches', 'candidateAccountBatchItems',
'workflows', 'workflowInstances', 'workflowActions', 'auditLogs'
];
@@ -53,7 +57,7 @@ function buildSeedOperations(state) {
const nullable = value => value == null || value === '' ? null : value;
add(
'UPDATE schema_metadata SET schema_version = 8, app_version = ?, self_registration_enabled = ?, created_at = ? WHERE id = 1',
'UPDATE schema_metadata SET schema_version = 9, app_version = ?, self_registration_enabled = ?, created_at = ? WHERE id = 1',
Number(state.meta?.version || 1), state.settings?.selfRegistrationEnabled ? 1 : 0,
state.meta?.createdAt || new Date().toISOString()
);
@@ -150,15 +154,6 @@ function buildSeedOperations(state) {
for (const subjectId of registration.subjectIds) {
add('INSERT INTO registration_subjects (registration_id, subject_id) VALUES (?, ?)', registration.id, subjectId);
}
if (registration.admitCard) {
add(
`INSERT INTO admit_cards (
registration_id, card_number, test_center, room, seat, generated_at
) VALUES (?, ?, ?, ?, ?, ?)`,
registration.id, registration.admitCard.number, registration.admitCard.testCenter,
registration.admitCard.room, registration.admitCard.seat, registration.admitCard.generatedAt
);
}
}
for (const result of state.results) {
@@ -197,6 +192,46 @@ function buildSeedOperations(state) {
);
}
for (const rule of state.admissionNumberRules) {
add(
`INSERT INTO admission_number_rules (
id, code, name, description, separator, segments_json, example, active, created_at
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`,
rule.id, rule.code, rule.name, rule.description, rule.separator || '', JSON.stringify(rule.segments || []),
rule.example || '', rule.active === false ? 0 : 1, rule.createdAt
);
}
for (const plan of state.arrangementPlans) {
add(
`INSERT INTO exam_arrangement_plans (
id, exam_id, number_rule_id, mixing_scope, random_seed, candidate_count, center_count,
subject_assignment_count, subject_combination_count, same_school_center_rate, warnings_json, generated_by, generated_at
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
plan.id, plan.examId, plan.numberRuleId, plan.mixingScope, plan.randomSeed,
Number(plan.candidateCount || 0), Number(plan.centerCount || 0), Number(plan.subjectAssignmentCount || 0),
Number(plan.subjectCombinationCount || 0), Number(plan.sameSchoolCenterRate || 0), JSON.stringify(plan.warnings || []),
nullable(plan.generatedBy), plan.generatedAt
);
}
for (const registration of state.registrations.filter(item => item.admitCard)) {
const card = registration.admitCard;
add(
`INSERT INTO admit_cards (
registration_id, plan_id, card_number, center_id, test_center, generated_at
) VALUES (?, ?, ?, ?, ?, ?)`,
registration.id, card.planId, card.number, nullable(card.centerId), card.testCenter, card.generatedAt
);
for (const assignment of card.assignments || []) add(
`INSERT INTO admit_card_subjects (
registration_id, subject_id, room_id, room, room_code, exam_room_code, seat, subject_signature
) VALUES (?, ?, ?, ?, ?, ?, ?, ?)`,
registration.id, assignment.subjectId, nullable(assignment.roomId), assignment.roomName || assignment.room,
assignment.roomCode, assignment.examRoomCode, assignment.seat, assignment.subjectSignature || ''
);
}
for (const request of state.centerChangeRequests) {
add(
`INSERT INTO center_change_requests (
@@ -328,13 +363,39 @@ function stateFromRows(rows) {
subjectIds.push(row.subject_id);
registrationSubjects.set(row.registration_id, subjectIds);
}
const admitCards = new Map(rows.admitCards.map(row => [row.registration_id, {
number: row.card_number,
testCenter: row.test_center,
room: row.room,
seat: row.seat,
generatedAt: row.generated_at
}]));
const subjectPositions = new Map(rows.subjects.map(row => [row.id, Number(row.position)]));
const admitAssignments = new Map();
for (const row of rows.admitCardSubjects) {
const assignments = admitAssignments.get(row.registration_id) || [];
assignments.push({
subjectId: row.subject_id,
roomId: row.room_id,
roomName: row.room,
room: row.room,
roomCode: row.room_code,
examRoomCode: row.exam_room_code,
seat: row.seat,
subjectSignature: row.subject_signature || ''
});
admitAssignments.set(row.registration_id, assignments);
}
for (const assignments of admitAssignments.values()) assignments.sort((left, right) =>
(subjectPositions.get(left.subjectId) || 0) - (subjectPositions.get(right.subjectId) || 0)
);
const admitCards = new Map(rows.admitCards.map(row => {
const assignments = admitAssignments.get(row.registration_id) || [];
const primary = assignments[0] || {};
return [row.registration_id, {
planId: row.plan_id,
number: row.card_number,
centerId: row.center_id,
testCenter: row.test_center,
room: primary.roomName || '',
seat: primary.seat || '',
assignments,
generatedAt: row.generated_at
}];
}));
const ruleSegments = new Map();
for (const row of rows.numberRuleSegments) {
const segments = ruleSegments.get(row.rule_id) || [];
@@ -567,6 +628,32 @@ function stateFromRows(rows) {
status: row.status,
notes: row.notes || ''
})),
admissionNumberRules: rows.admissionNumberRules.map(row => ({
id: row.id,
code: row.code,
name: row.name,
description: row.description,
separator: row.separator || '',
segments: Array.isArray(row.segments_json) ? row.segments_json : JSON.parse(row.segments_json || '[]'),
example: row.example || '',
active: Boolean(row.active),
createdAt: row.created_at
})),
arrangementPlans: rows.arrangementPlans.map(row => ({
id: row.id,
examId: row.exam_id,
numberRuleId: row.number_rule_id,
mixingScope: row.mixing_scope,
randomSeed: row.random_seed,
candidateCount: Number(row.candidate_count),
centerCount: Number(row.center_count),
subjectAssignmentCount: Number(row.subject_assignment_count),
subjectCombinationCount: Number(row.subject_combination_count),
sameSchoolCenterRate: Number(row.same_school_center_rate),
warnings: Array.isArray(row.warnings_json) ? row.warnings_json : JSON.parse(row.warnings_json || '[]'),
generatedBy: row.generated_by,
generatedAt: row.generated_at
})),
numberRules: rows.numberRules.map(row => ({
id: row.id,
name: row.name,
@@ -649,7 +736,10 @@ function readSqliteRows(connection) {
subjects: connection.prepare('SELECT * FROM exam_subjects ORDER BY exam_id, position, id').all(),
registrations: connection.prepare('SELECT * FROM registrations ORDER BY created_at, id').all(),
registrationSubjects: connection.prepare('SELECT * FROM registration_subjects ORDER BY registration_id, subject_id').all(),
admissionNumberRules: connection.prepare('SELECT * FROM admission_number_rules ORDER BY created_at, id').all(),
arrangementPlans: connection.prepare('SELECT * FROM exam_arrangement_plans ORDER BY generated_at DESC, id').all(),
admitCards: connection.prepare('SELECT * FROM admit_cards ORDER BY registration_id').all(),
admitCardSubjects: connection.prepare('SELECT * FROM admit_card_subjects ORDER BY registration_id, subject_id').all(),
results: connection.prepare('SELECT * FROM results ORDER BY id').all(),
testCenters: connection.prepare('SELECT * FROM test_centers ORDER BY school_id, name, id').all(),
testRooms: connection.prepare('SELECT * FROM test_rooms ORDER BY center_id, code, id').all(),
@@ -682,7 +772,10 @@ async function readMysqlRows(connection) {
subjects: await query('SELECT * FROM exam_subjects ORDER BY exam_id, position, id'),
registrations: await query('SELECT * FROM registrations ORDER BY created_at, id'),
registrationSubjects: await query('SELECT * FROM registration_subjects ORDER BY registration_id, subject_id'),
admissionNumberRules: await query('SELECT * FROM admission_number_rules ORDER BY created_at, id'),
arrangementPlans: await query('SELECT * FROM exam_arrangement_plans ORDER BY generated_at DESC, id'),
admitCards: await query('SELECT * FROM admit_cards ORDER BY registration_id'),
admitCardSubjects: await query('SELECT * FROM admit_card_subjects ORDER BY registration_id, subject_id'),
results: await query('SELECT * FROM results ORDER BY id'),
testCenters: await query('SELECT * FROM test_centers ORDER BY school_id, name, id'),
testRooms: await query('SELECT * FROM test_rooms ORDER BY center_id, code, id'),
@@ -1154,16 +1247,38 @@ function createRepository({ client, location, read, transaction, close }) {
operations.push(auditOperation(log));
await transaction(operations);
},
async createAdmitCard(registrationId, admitCard, log) {
await transaction([
async saveAdmissionArrangement(plan, cards, log) {
const operations = [
operation('DELETE FROM exam_arrangement_plans WHERE exam_id = ?', plan.examId),
operation(
`INSERT INTO exam_arrangement_plans (
id, exam_id, number_rule_id, mixing_scope, random_seed, candidate_count, center_count,
subject_assignment_count, subject_combination_count, same_school_center_rate,
warnings_json, generated_by, generated_at
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
plan.id, plan.examId, plan.numberRuleId, plan.mixingScope, plan.randomSeed,
Number(plan.candidateCount), Number(plan.centerCount), Number(plan.subjectAssignmentCount),
Number(plan.subjectCombinationCount), Number(plan.sameSchoolCenterRate), JSON.stringify(plan.warnings || []),
optional(plan.generatedBy), plan.generatedAt
)
];
for (const card of cards) {
operations.push(operation(
`INSERT INTO admit_cards (
registration_id, card_number, test_center, room, seat, generated_at
registration_id, plan_id, card_number, center_id, test_center, generated_at
) VALUES (?, ?, ?, ?, ?, ?)`,
registrationId, admitCard.number, admitCard.testCenter, admitCard.room, admitCard.seat, admitCard.generatedAt
),
auditOperation(log)
]);
card.registrationId, plan.id, card.number, optional(card.centerId), card.testCenter, card.generatedAt
));
for (const assignment of card.assignments) operations.push(operation(
`INSERT INTO admit_card_subjects (
registration_id, subject_id, room_id, room, room_code, exam_room_code, seat, subject_signature
) VALUES (?, ?, ?, ?, ?, ?, ?, ?)`,
card.registrationId, assignment.subjectId, optional(assignment.roomId), assignment.roomName,
assignment.roomCode, assignment.examRoomCode, assignment.seat, assignment.subjectSignature
));
}
operations.push(auditOperation(log));
await transaction(operations);
},
async createExam(exam, log) {
const operations = [operation(