Add independent subject pass rules and atomic score imports

This commit is contained in:
2026-07-20 15:42:36 +08:00 Unverified
parent c5b18d0b15
commit 9f850fa045
17 changed files with 688 additions and 114 deletions
+46 -14
View File
@@ -57,7 +57,7 @@ function buildSeedOperations(state) {
const nullable = value => value == null || value === '' ? null : value;
add(
'UPDATE schema_metadata SET schema_version = 10, app_version = ?, self_registration_enabled = ?, created_at = ? WHERE id = 1',
'UPDATE schema_metadata SET schema_version = 12, 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()
);
@@ -129,15 +129,16 @@ function buildSeedOperations(state) {
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
exam.id, exam.code, exam.name, exam.description || '', exam.registrationStart, exam.registrationEnd,
exam.examStart, exam.examEnd, exam.admitDownloadStart, exam.admitDownloadEnd, exam.location || '',
exam.passPolicy || 'score_ratio', Number(exam.passValue ?? 60), exam.status, exam.createdAt
exam.passPolicy === 'score_ratio' ? 'rank_percent' : (exam.passPolicy || 'rank_percent'), Number(exam.passValue ?? 60), exam.status, exam.createdAt
);
exam.subjects.forEach((subject, index) => add(
`INSERT INTO exam_subjects (
id, exam_id, name, subject_date, start_time, end_time, fee, full_score, pass_score, position
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
id, exam_id, name, subject_date, start_time, end_time, fee, full_score, pass_score, pass_rule, pass_value, position
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
subject.id, exam.id, subject.name, subject.date || String(exam.examStart).slice(0, 10),
subject.start || '', subject.end || '', Number(subject.fee || 0), Number(subject.fullScore || 150),
Number(subject.passScore ?? Number(subject.fullScore || 150) * .6), Number(subject.order || index + 1)
Number(subject.passScore ?? 0), subject.passRule === 'rank_percent' ? 'score_ratio' : (subject.passRule || 'fixed_score'),
Number(subject.passValue ?? subject.passScore ?? Number(subject.fullScore || 150) * .6), Number(subject.order || index + 1)
));
}
@@ -342,6 +343,9 @@ function buildSeedOperations(state) {
function stateFromRows(rows) {
const subjectsByExam = new Map();
for (const row of rows.subjects) {
const fullScore = Number(row.full_score ?? 150);
const passRule = row.pass_rule === 'score_ratio' ? 'rank_percent' : (row.pass_rule || 'fixed_score');
const passValue = Number(row.pass_value ?? row.pass_score ?? fullScore * .6);
const subject = {
id: row.id,
name: row.name,
@@ -349,8 +353,10 @@ function stateFromRows(rows) {
start: row.start_time,
end: row.end_time,
fee: Number(row.fee),
fullScore: Number(row.full_score ?? 150),
passScore: Number(row.pass_score ?? 90),
fullScore,
passRule,
passValue,
passScore: passRule === 'fixed_score' ? Number(passValue.toFixed(2)) : null,
order: Number(row.position)
};
const subjects = subjectsByExam.get(row.exam_id) || [];
@@ -521,7 +527,7 @@ function stateFromRows(rows) {
admitDownloadStart: row.admit_download_start,
admitDownloadEnd: row.admit_download_end,
location: row.location,
passPolicy: row.pass_policy || 'score_ratio',
passPolicy: row.pass_policy === 'score_ratio' ? 'rank_percent' : (row.pass_policy || 'rank_percent'),
passValue: Number(row.pass_value ?? 60),
status: row.status,
createdAt: row.created_at,
@@ -1008,6 +1014,12 @@ function createRepository({ client, location, read, transaction, close }) {
status = ?, review_note = ?, reviewed_at = ? WHERE id = ?`,
business.status, optional(business.reviewNote), optional(business.reviewedAt), business.id
));
} else if (instance.businessType === 'score_appeal' && business) {
operations.push(operation(
`UPDATE results SET score = ?, grade = ?, published = ?, updated_at = ?, published_at = ? WHERE id = ?`,
Number(business.score), business.grade, business.published ? 1 : 0,
optional(business.updatedAt), optional(business.publishedAt), business.id
));
}
if (log) operations.push(auditOperation(log));
await transaction(operations);
@@ -1298,11 +1310,11 @@ function createRepository({ client, location, read, transaction, close }) {
)];
exam.subjects.forEach((subject, index) => operations.push(operation(
`INSERT INTO exam_subjects (
id, exam_id, name, subject_date, start_time, end_time, fee, full_score, pass_score, position
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
id, exam_id, name, subject_date, start_time, end_time, fee, full_score, pass_score, pass_rule, pass_value, position
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
subject.id, exam.id, subject.name, subject.date || String(exam.examStart).slice(0, 10),
subject.start || '', subject.end || '', Number(subject.fee || 0), Number(subject.fullScore),
Number(subject.passScore), Number(subject.order || index + 1)
Number(subject.passScore ?? 0), subject.passRule === 'rank_percent' ? 'score_ratio' : (subject.passRule || 'fixed_score'), Number(subject.passValue ?? subject.passScore ?? 0), Number(subject.order || index + 1)
)));
operations.push(auditOperation(log));
await transaction(operations);
@@ -1323,11 +1335,11 @@ function createRepository({ client, location, read, transaction, close }) {
operations.push(operation('DELETE FROM exam_subjects WHERE exam_id = ?', exam.id));
exam.subjects.forEach((subject, index) => operations.push(operation(
`INSERT INTO exam_subjects (
id, exam_id, name, subject_date, start_time, end_time, fee, full_score, pass_score, position
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
id, exam_id, name, subject_date, start_time, end_time, fee, full_score, pass_score, pass_rule, pass_value, position
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
subject.id, exam.id, subject.name, subject.date || String(exam.examStart).slice(0, 10),
subject.start || '', subject.end || '', Number(subject.fee || 0), Number(subject.fullScore),
Number(subject.passScore), Number(subject.order || index + 1)
Number(subject.passScore ?? 0), subject.passRule === 'rank_percent' ? 'score_ratio' : (subject.passRule || 'fixed_score'), Number(subject.passValue ?? subject.passScore ?? 0), Number(subject.order || index + 1)
)));
}
operations.push(auditOperation(log));
@@ -1370,6 +1382,26 @@ function createRepository({ client, location, read, transaction, close }) {
optional(result.updatedAt), optional(result.publishedAt), result.id
);
await transaction([resultOperation, auditOperation(log)]);
},
async saveResults(entries) {
const operations = [];
for (const { result, isNew, log } of entries) {
operations.push(isNew
? operation(
`INSERT INTO results (
id, registration_id, subject_id, score, grade, published, updated_at, published_at
) VALUES (?, ?, ?, ?, ?, ?, ?, ?)`,
result.id, result.registrationId, result.subjectId, result.score, result.grade,
result.published ? 1 : 0, optional(result.updatedAt), optional(result.publishedAt)
)
: operation(
`UPDATE results SET score = ?, grade = ?, published = ?, updated_at = ?, published_at = ? WHERE id = ?`,
result.score, result.grade, result.published ? 1 : 0,
optional(result.updatedAt), optional(result.publishedAt), result.id
));
operations.push(auditOperation(log));
}
await transaction(operations);
}
};
}