实现考试归档并永久锁定成绩流程
This commit is contained in:
@@ -753,6 +753,7 @@ export function createAdminRoutes(context) {
|
||||
const body = await readJson(request);
|
||||
const registration = db.registrations.find(item => item.id === registrationMatch[1]);
|
||||
if (!registration) return sendError(response, 404, '报名记录不存在');
|
||||
if (db.exams.find(item => item.id === registration.examId)?.archivedAt) return sendError(response, 409, '该考试已归档,报名流程已冻结');
|
||||
if (!registrationInScope(db, user, registration) && user.adminLevel !== 'super') return sendError(response, 403, '该报名不在你的数据范围内');
|
||||
if (!['approved', 'rejected'].includes(body.status)) return sendError(response, 400, '审核状态无效');
|
||||
const profile = db.candidateProfiles.find(item => item.userId === registration.userId);
|
||||
@@ -802,6 +803,7 @@ export function createAdminRoutes(context) {
|
||||
const note = cleanText(body.reviewNote, 300);
|
||||
const action = { id: uid('flow_action'), instanceId: instance.id, actorId: user.id, action: body.status === 'approved' ? 'approve' : 'reject', note, fromAssigneeId: instance.assigneeId, toAssigneeId: null, createdAt: nowIso() };
|
||||
const exam = db.exams.find(item => item.id === registration.examId);
|
||||
if (exam?.archivedAt) return sendError(response, 409, '该考试已归档,成绩及复议流程已永久锁定');
|
||||
const subject = exam?.subjects.find(item => item.id === result.subjectId);
|
||||
const finalStep = instance.currentStep >= workflow.steps.length;
|
||||
let reviewedScore = null;
|
||||
@@ -841,6 +843,8 @@ export function createAdminRoutes(context) {
|
||||
const arrangementMatch = pathname.match(/^\/api\/admin\/exams\/([^/]+)\/admission-arrangement(\/preview)?$/);
|
||||
if (request.method === 'POST' && arrangementMatch) {
|
||||
if (!requirePermission(user, response, '*')) return true;
|
||||
const arrangementExam = db.exams.find(item => item.id === arrangementMatch[1]);
|
||||
if (arrangementExam?.archivedAt) return sendError(response, 409, '该考试已归档,不能重新编排准考证');
|
||||
const body = await readJson(request);
|
||||
const generatedAt = nowIso();
|
||||
const result = buildAdmissionArrangement(db, {
|
||||
@@ -897,12 +901,32 @@ export function createAdminRoutes(context) {
|
||||
await database.createExam(exam, log);
|
||||
return sendJson(response, 201, { ok: true, exam });
|
||||
}
|
||||
const examArchiveMatch = pathname.match(/^\/api\/admin\/exams\/([^/]+)\/archive$/);
|
||||
if (request.method === 'POST' && examArchiveMatch) {
|
||||
if (!requirePermission(user, response, '*')) return true;
|
||||
const exam = db.exams.find(item => item.id === examArchiveMatch[1]);
|
||||
if (!exam) return sendError(response, 404, '考试不存在');
|
||||
if (exam.archivedAt) return sendError(response, 409, '该考试已经归档,归档操作不可撤销');
|
||||
const resultIds = new Set(db.results.filter(result => {
|
||||
const registration = db.registrations.find(item => item.id === result.registrationId);
|
||||
return registration?.examId === exam.id;
|
||||
}).map(result => result.id));
|
||||
const pendingAppeals = db.workflowInstances.filter(instance => instance.businessType === 'score_appeal' && instance.status === 'pending' && resultIds.has(instance.businessId));
|
||||
if (pendingAppeals.length) return sendError(response, 409, `本场还有 ${pendingAppeals.length} 项成绩复议待处理,请先办结后再归档`);
|
||||
exam.archivedAt = nowIso();
|
||||
exam.archivedBy = user.id;
|
||||
exam.status = 'closed';
|
||||
const log = logAction(db, user, '归档考试并永久锁定成绩', `${exam.name} · ${exam.code}`);
|
||||
await database.archiveExam(exam, log);
|
||||
return sendJson(response, 200, { ok: true, exam: publicExam(exam), message: '考试已归档,全部成绩已永久锁定' });
|
||||
}
|
||||
const examMatch = pathname.match(/^\/api\/admin\/exams\/([^/]+)$/);
|
||||
if (request.method === 'PATCH' && examMatch) {
|
||||
if (!requirePermission(user, response, '*')) return true;
|
||||
const body = await readJson(request);
|
||||
const exam = db.exams.find(item => item.id === examMatch[1]);
|
||||
if (!exam) return sendError(response, 404, '考试不存在');
|
||||
if (exam.archivedAt) return sendError(response, 409, '该考试已归档,所有配置和成绩均已锁定');
|
||||
const originalStatus = exam.status;
|
||||
const detailFields = ['code', 'name', 'description', 'location', 'registrationStart', 'registrationEnd', 'examStart', 'examEnd', 'admitDownloadStart', 'admitDownloadEnd'];
|
||||
const editingDetails = detailFields.some(field => body[field] != null) || body.subjects != null || body.passPolicy != null || body.passValue != null;
|
||||
@@ -1021,6 +1045,7 @@ export function createAdminRoutes(context) {
|
||||
const registration = db.registrations.find(item => item.id === body.registrationId && item.status === 'approved');
|
||||
if (!registration) return sendError(response, 404, '已通过的报名记录不存在');
|
||||
const exam = db.exams.find(item => item.id === registration.examId);
|
||||
if (exam?.archivedAt) return sendError(response, 409, '该考试已归档,成绩已永久锁定');
|
||||
if (!registration.subjectIds.includes(body.subjectId) || !exam.subjects.some(item => item.id === body.subjectId)) return sendError(response, 400, '该考生未报名此科目');
|
||||
const score = Number(body.score);
|
||||
const subject = exam.subjects.find(item => item.id === body.subjectId);
|
||||
|
||||
Reference in New Issue
Block a user