优化2 patch2
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import assert from 'node:assert/strict';
|
||||
import { admissionCutoffRows, buildVolunteerPlacements, candidateTotalScore, publicAdmissionRows, remainingPlanQuota, sourceSchoolQualificationStatus } from '../src/services/volunteer-admission.mjs';
|
||||
import { admissionCutoffRows, buildVolunteerPlacements, candidateAdmissionScore, candidateTotalScore, publicAdmissionRows, remainingPlanQuota, sourceSchoolQualificationStatus } from '../src/services/volunteer-admission.mjs';
|
||||
import { candidateEligibleForCategory, specialtyLabel } from '../src/data/specialty-types.mjs';
|
||||
|
||||
const now = '2026-07-21T08:00:00.000Z';
|
||||
@@ -46,24 +46,42 @@ const db = {
|
||||
const setting = { examId: 'exam', payload: { round: 1 } };
|
||||
const placements = buildVolunteerPlacements(db, setting, { uid: prefix => `${prefix}-${++sequence}`, nowIso: () => now });
|
||||
assert.equal(candidateTotalScore(db, 'exam', 'u-high'), 250, '投档总分应取当次全部已发布科目之和');
|
||||
assert.equal(candidateAdmissionScore(db, 'exam', 'u-sport', { code: 'general' }), 215, '普通招生类别不得加入特征分');
|
||||
assert.equal(candidateAdmissionScore(db, 'exam', 'u-sport', { specialtyCategory: 'sports', specialtyType: 'track_field' }), 303.5, '特长生招生类别应使用文化课总分加特征分');
|
||||
assert.equal(placements.length, 3, '三个符合条件且计划充足的考生都应投档');
|
||||
assert.equal(placements.find(item => item.userId === 'u-high').schoolId, 'target-b', '最高分考生应优先满足第一志愿');
|
||||
assert.equal(placements.find(item => item.userId === 'u-low').schoolId, 'target-a', '第一志愿已满时应继续遵循下一志愿');
|
||||
assert.equal(placements.find(item => item.userId === 'u-sport').payload.quotaBucket, 'indicator:source-b', '特长生指标应使用对应生源学校指标名额');
|
||||
assert.equal(placements.find(item => item.userId === 'u-sport').payload.featureScore, 88.5, '特征分应随投档材料发送,但不并入文化课总分');
|
||||
assert.equal(placements.find(item => item.userId === 'u-sport').payload.featureScore, 88.5, '特征分应随投档材料发送');
|
||||
assert.equal(placements.find(item => item.userId === 'u-sport').payload.culturalScore, 215, '特长生投档材料应保留文化课原始总分');
|
||||
assert.equal(placements.find(item => item.userId === 'u-sport').payload.totalScore, 303.5, '特长生类别投档总分应加入特征分');
|
||||
assert.equal(specialtyLabel('sports', 'track_field'), '体育·田径', '特长资格应显示大类和小类');
|
||||
assert.equal(candidateEligibleForCategory(db.candidateProfiles[2], { specialtyCategory: 'arts', specialtyType: 'fine_arts' }), false, '体育资格考生不得填报艺术类计划');
|
||||
|
||||
const specialtyPriorityDb = structuredClone(db);
|
||||
specialtyPriorityDb.users.push({ id: 'u-sport-rival', role: 'candidate', active: true, candidateNumber: '20260004', displayName: '特长竞争考生' });
|
||||
specialtyPriorityDb.candidateProfiles.push({ userId: 'u-sport-rival', name: '特长竞争考生', schoolId: 'source-b', profileCompleted: true, specialtyCategory: 'sports', specialtyType: 'track_field', specialtyTypes: ['track_field'] });
|
||||
specialtyPriorityDb.registrations.push({ id: 'r-sport-rival', examId: 'exam', userId: 'u-sport-rival', status: 'approved', subjectIds: ['cn', 'math'], featureScore: 0 });
|
||||
specialtyPriorityDb.results.push({ registrationId: 'r-sport-rival', subjectId: 'cn', score: 125, published: true }, { registrationId: 'r-sport-rival', subjectId: 'math', score: 125, published: true });
|
||||
specialtyPriorityDb.admissionRecords.push(
|
||||
{ id: 'qual-sport-rival', kind: 'indicator_qualification', examId: 'exam', userId: 'u-sport-rival', schoolId: 'source-b', status: 'confirmed', payload: { eligible: true } },
|
||||
{ id: 'pref-sport-rival', kind: 'preference', examId: 'exam', userId: 'u-sport-rival', status: 'submitted', payload: { round: 1, choices: [{ schoolId: 'target-b', categoryCode: 'sport', preferenceType: 'indicator' }] } }
|
||||
);
|
||||
const specialtyPriorityPlacements = buildVolunteerPlacements(specialtyPriorityDb, setting, { uid: prefix => `${prefix}-priority-${++sequence}`, nowIso: () => now });
|
||||
assert.equal(specialtyPriorityPlacements.find(item => item.payload.categoryCode === 'sport').userId, 'u-sport', '特长类别应按文化课加特征分排序,而不是只按文化课排序');
|
||||
|
||||
db.admissionRecords.push(...placements.map(item => ({ ...item, status: 'final' })));
|
||||
const remaining = remainingPlanQuota(db, db.admissionRecords.find(item => item.id === 'plan-b'));
|
||||
assert.equal(remaining.find(item => item.code === 'general').remaining, 0, '普通生计划占用应准确统计');
|
||||
assert.equal(remaining.find(item => item.code === 'sport').remaining, 0, '特长生计划占用应准确统计');
|
||||
const publicRows = publicAdmissionRows(db, 'exam');
|
||||
assert.equal(publicRows[0].registrationNumber, '20260001', '公示必须公开报名号');
|
||||
assert.equal(publicRows[0].name, '高分考生', '公示必须公开姓名');
|
||||
assert.equal(publicRows[0].totalScore, 250, '公示必须公开总成绩');
|
||||
assert.equal(publicRows[0].admittedSchool, '第二中学', '公示必须公开录取学校');
|
||||
assert.ok(publicRows[0].idNumberMasked.includes('*') && !publicRows[0].idNumberMasked.includes('20090101'), '重要身份信息必须脱敏');
|
||||
const highPublicRow = publicRows.find(item => item.registrationNumber === '20260001');
|
||||
assert.equal(highPublicRow.registrationNumber, '20260001', '公示必须公开报名号');
|
||||
assert.equal(highPublicRow.name, '高分考生', '公示必须公开姓名');
|
||||
assert.equal(highPublicRow.totalScore, 250, '普通类别公示总成绩不得加入特征分');
|
||||
assert.equal(highPublicRow.admittedSchool, '第二中学', '公示必须公开录取学校');
|
||||
assert.ok(highPublicRow.idNumberMasked.includes('*') && !highPublicRow.idNumberMasked.includes('20090101'), '重要身份信息必须脱敏');
|
||||
assert.equal(publicRows.find(item => item.registrationNumber === '20260003').totalScore, 303.5, '特长生类别公示总成绩应包含特征分');
|
||||
const cutoffs = admissionCutoffRows(db, 'exam');
|
||||
assert.equal(cutoffs.find(item => item.schoolId === 'target-b' && item.categoryCode === 'general').cutoffScore, 250, '录取分数线应取学校招生类别最终录取最低总分');
|
||||
const qualificationStatus = sourceSchoolQualificationStatus(db, 'exam', 'source-b');
|
||||
|
||||
+30
-5
@@ -595,6 +595,11 @@ try {
|
||||
assert.equal(structuredPlan.response.status, 201, '招生校应能提交结构化类别与生源校指标计划');
|
||||
assert.equal(structuredPlan.data.plan.payload.categories[1].specialtyType, 'fine_arts');
|
||||
assert.equal((await admin.request(`/api/admin/admission-plans/${structuredPlan.data.plan.id}`, { method: 'PATCH', body: { status: 'approved', reviewNote: '系统测试通过' } })).response.status, 200, '超级管理员应能审核结构化招生计划');
|
||||
const publicPlanAnnouncements = await anonymous.request('/api/public/announcements');
|
||||
const publicPlan = publicPlanAnnouncements.data.plans.find(item => item.id === structuredPlan.data.plan.id);
|
||||
assert.ok(publicPlan, '招生计划审核通过后应立即自动进入公开公示接口');
|
||||
assert.equal(publicPlan.schoolName, '海州市招生实验学校', '招生计划公示应标明招生学校');
|
||||
assert.deepEqual(publicPlan.rows.map(item => item.quota), [20, 4], '招生计划公示应保留各类别计划人数');
|
||||
assert.equal((await classAdmin.request('/api/admin/indicator-qualifications')).response.status, 403, '班级管理员不得查看或确认指标分配资格');
|
||||
assert.equal((await admin.request('/api/admin/indicator-qualifications')).response.status, 403, '超级管理员不得代替生源校确认指标分配资格');
|
||||
const qualificationLedger = await schoolAdmin.request('/api/admin/indicator-qualifications');
|
||||
@@ -895,6 +900,20 @@ try {
|
||||
assert.ok(publicRichNotice, '管理员发布通知后首页应可见');
|
||||
assert.equal(publicRichNotice.contentHtml, publishNotice.data.notice.contentHtml, '公开接口应返回已清洗的富文本正文');
|
||||
|
||||
const resultTemplate = await admin.request(`/api/admin/excel/results?template=1&examId=${encodeURIComponent(exam.id)}`);
|
||||
assert.equal(resultTemplate.response.status, 200, '成绩模板应支持按考试下载完整录分名单');
|
||||
const resultTemplateWorkbook = new ExcelJS.Workbook();
|
||||
await resultTemplateWorkbook.xlsx.load(resultTemplate.data);
|
||||
const resultTemplateSheet = resultTemplateWorkbook.getWorksheet('成绩');
|
||||
const templateHeaders = resultTemplateSheet.getRow(2).values;
|
||||
assert.ok(templateHeaders.includes('报名号*') && templateHeaders.includes('准考证号(只读参考)') && templateHeaders.includes('姓名(只读参考)'), '成绩模板应包含报名号、准考证号与姓名列');
|
||||
const templateCandidateNumberColumn = templateHeaders.indexOf('报名号*');
|
||||
const templateCandidateNameColumn = templateHeaders.indexOf('姓名(只读参考)');
|
||||
const templateRows = resultTemplateSheet.getRows(3, Math.max(0, resultTemplateSheet.rowCount - 2)) || [];
|
||||
const candidateTemplateRow = templateRows.find(row => row.getCell(templateCandidateNumberColumn).text === candidateNumber);
|
||||
assert.ok(candidateTemplateRow, '成绩模板应预填已通过报名的真实考生,而不是空白示例行');
|
||||
assert.ok(candidateTemplateRow.getCell(templateCandidateNameColumn).text, '成绩模板应预填考生姓名供核对');
|
||||
|
||||
const stagedResultFile = Buffer.from(await buildWorkbook('results', [{ candidateNumber, examCode: exam.code, subjectName: exam.subjects[0].name, score: 125, grade: 'A', published: '不发布' }]));
|
||||
const stagedPreview = await admin.request('/api/admin/excel/results', { method: 'POST', headers: { 'Content-Type': 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' }, body: stagedResultFile });
|
||||
assert.equal(stagedPreview.response.status, 200, '成绩 Excel 应先解析为预览');
|
||||
@@ -905,16 +924,22 @@ try {
|
||||
assert.equal(stagedCommit.response.status, 200, '确认后应批量提交预览成绩');
|
||||
assert.equal(stagedCommit.data.count, 1);
|
||||
assert.ok((await admin.request('/api/admin/results')).data.results.some(item => item.registrationId === registrationId && item.subjectId === exam.subjects[0].id && item.score === 125 && !item.published), '批量提交后成绩应以预览状态写库');
|
||||
const publishResult = await admin.request('/api/admin/results', { method: 'POST', body: { registrationId, subjectId: exam.subjects[0].id, score: 126, grade: 'A', published: true } });
|
||||
assert.equal(publishResult.response.status, 200);
|
||||
const bulkDraftResult = await admin.request('/api/admin/results/bulk', { method: 'POST', body: { examId: exam.id, subjectId: exam.subjects[0].id, published: false, rows: [{ registrationId, score: 125.5 }] } });
|
||||
assert.equal(bulkDraftResult.response.status, 200, '名单式录分应支持按科目批量暂存');
|
||||
assert.equal(bulkDraftResult.data.published, false);
|
||||
const publishResult = await admin.request('/api/admin/results/bulk', { method: 'POST', body: { examId: exam.id, subjectId: exam.subjects[0].id, published: true, rows: [{ registrationId, score: 126 }] } });
|
||||
assert.equal(publishResult.response.status, 200, '名单式录分应支持按科目批量发布');
|
||||
assert.equal(publishResult.data.published, true);
|
||||
assert.equal((await admin.request('/api/admin/results', { method: 'POST', body: { registrationId, subjectId: exam.subjects[0].id, score: 151, published: true } })).response.status, 400, '成绩不得超过该科配置的满分');
|
||||
assert.equal((await admin.request('/api/admin/results', { method: 'POST', body: { registrationId, subjectId: exam.subjects[2].id, score: 90, published: true } })).response.status, 200);
|
||||
const adminResults = await admin.request('/api/admin/results');
|
||||
assert.equal(adminResults.data.registrations.find(item => item.id === registrationId).featureScore, 0, '所有考试的特征分默认应为 0');
|
||||
const featureScoreUpdate = await admin.request(`/api/admin/registrations/${registrationId}/feature-score`, { method: 'PATCH', body: { featureScore: 87.5 } });
|
||||
assert.equal(featureScoreUpdate.response.status, 200, '超级管理员应能登记与科目无关的特征分');
|
||||
assert.equal(featureScoreUpdate.data.registration.featureScore, 87.5);
|
||||
const featureScoreUpdate = await admin.request('/api/admin/feature-scores/bulk', { method: 'POST', body: { examId: exam.id, rows: [{ registrationId, featureScore: 87.5 }] } });
|
||||
assert.equal(featureScoreUpdate.response.status, 200, '超级管理员应能按考试名单批量登记特征分');
|
||||
assert.equal(featureScoreUpdate.data.count, 1);
|
||||
assert.equal((await admin.request('/api/admin/results')).data.registrations.find(item => item.id === registrationId).featureScore, 87.5, '批量保存后应立即回填特征分');
|
||||
assert.equal((await classAdmin.request(`/api/admin/registrations/${registrationId}/feature-score`, { method: 'PATCH', body: { featureScore: 10 } })).response.status, 403, '班级管理员不得登记特征分');
|
||||
assert.equal((await classAdmin.request('/api/admin/feature-scores/bulk', { method: 'POST', body: { examId: exam.id, rows: [{ registrationId, featureScore: 10 }] } })).response.status, 403, '班级管理员不得批量登记特征分');
|
||||
assert.equal(adminResults.data.resultCache.status, 'disabled', '未配置 REDIS_URL 时成绩管理接口应报告缓存未启用');
|
||||
assert.equal((await schoolAdmin.request('/api/admin/results/cache/refresh', { method: 'POST' })).response.status, 403, '仅超级管理员可以手动刷新成绩缓存');
|
||||
const cacheRefresh = await admin.request('/api/admin/results/cache/refresh', { method: 'POST' });
|
||||
|
||||
Reference in New Issue
Block a user