准考证编排

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
+36 -4
View File
@@ -286,7 +286,11 @@ try {
registrationStart: new Date(now - hour).toISOString(), registrationEnd: new Date(now + 24 * hour).toISOString(),
examStart: new Date(now + 48 * hour).toISOString(), examEnd: new Date(now + 60 * hour).toISOString(),
admitDownloadStart: new Date(now - hour).toISOString(), admitDownloadEnd: new Date(now + 47 * hour).toISOString(),
location: '测试考点', status: 'published', subjects: ['语文', '数学', '外语']
location: '测试考点', status: 'published', subjects: [
{ name: '语文', date: new Date(now + 48 * hour).toISOString().slice(0, 10), start: '09:00', end: '11:00', fee: 0, fullScore: 150, passScore: 90 },
{ name: '数学', date: new Date(now + 48 * hour).toISOString().slice(0, 10), start: '13:00', end: '15:00', fee: 0, fullScore: 150, passScore: 90 },
{ name: '外语', date: new Date(now + 48 * hour).toISOString().slice(0, 10), start: '15:30', end: '17:00', fee: 0, fullScore: 150, passScore: 90 }
]
}
});
assert.equal(createExam.response.status, 201);
@@ -379,8 +383,32 @@ try {
assert.equal(approveRegistration.data.registration.status, 'approved');
assert.equal(approveRegistration.data.registration.registrationNumber, candidateNumber, '考试报名必须复用考生账户的固定报名号');
const generateAdmit = await admin.request(`/api/admin/registrations/${registrationId}/admit-card`, { method: 'POST', body: { testCenter: '海州市测试中学' } });
assert.ok(generateAdmit.data.admitCard.number, '管理员应能生成准考证号');
const arrangementContext = await admin.request('/api/admin/admission-arrangements');
assert.equal(arrangementContext.data.rules.length, 4, '系统应预置四种准考证号规则');
assert.deepEqual(arrangementContext.data.mixingScopes.map(item => item.code), ['class', 'school', 'district', 'city', 'province'], '应支持班、校、县区、市、省五级混编');
for (const scope of arrangementContext.data.mixingScopes) {
const preview = await admin.request(`/api/admin/exams/${exam.id}/admission-arrangement/preview`, { method: 'POST', body: {
mixingScope: scope.code, numberRuleId: arrangementContext.data.rules[0].id, seed: `scope-${scope.code}`
} });
assert.equal(preview.response.status, 200, `${scope.name}应通过容量与同考点约束预检`);
}
for (const rule of arrangementContext.data.rules) {
const preview = await admin.request(`/api/admin/exams/${exam.id}/admission-arrangement/preview`, { method: 'POST', body: {
mixingScope: 'school', numberRuleId: rule.id, seed: `rule-${rule.code}`
} });
assert.equal(preview.response.status, 200, `${rule.name}应能生成有效号码`);
assert.equal(new Set(preview.data.samples.map(item => item.number)).size, preview.data.samples.length, `${rule.name}生成的样例号码不得重复`);
}
const arrangementBody = { mixingScope: 'school', numberRuleId: 'admit_rule_district_room_seat', seed: 'system-test-stable' };
const arrangementPreview = await admin.request(`/api/admin/exams/${exam.id}/admission-arrangement/preview`, { method: 'POST', body: arrangementBody });
assert.equal(arrangementPreview.response.status, 200, '正式写入前应可预检整场编排');
assert.ok(arrangementPreview.data.summary.subjectAssignmentCount >= arrangementPreview.data.summary.candidateCount, '预检应统计全部科次座位');
const generateAdmit = await admin.request(`/api/admin/exams/${exam.id}/admission-arrangement`, { method: 'POST', body: arrangementBody });
const generatedCard = generateAdmit.data.cards.find(item => item.registrationId === registrationId);
assert.ok(generatedCard.number, '管理员应能按整场考试批量生成准考证号');
assert.equal(new Set(generatedCard.assignments.map(item => item.centerId)).size, 1, '同一考生所有科目必须固定在同一考点');
assert.equal(generatedCard.assignments.length, submitRegistration.data.registration.subjectIds.length, '每个报考科目都必须有独立考场座位');
assert.equal((await admin.request(`/api/admin/registrations/${registrationId}/admit-card`, { method: 'POST', body: {} })).response.status, 410, '旧单人生成入口应明确停用');
const candidateRegistrations = await candidate.request('/api/candidate/registrations');
const candidateRegistration = candidateRegistrations.data.registrations.find(item => item.id === registrationId);
@@ -409,7 +437,10 @@ try {
await futureSchoolClient.request(`/api/admin/registrations/${futureRegistrationId}`, { method: 'PATCH', body: { status: 'approved', reviewNote: '学校通过' } });
const approvedFutureRegistration = await admin.request(`/api/admin/registrations/${futureRegistrationId}`, { method: 'PATCH', body: { status: 'approved', reviewNote: '通过' } });
assert.equal(approvedFutureRegistration.data.registration.registrationNumber, candidateNumber, '同一考生参加不同考试必须保持相同报名号');
await admin.request(`/api/admin/registrations/${futureRegistrationId}/admit-card`, { method: 'POST', body: { testCenter: '海州市测试中学' } });
const futureArrangement = await admin.request(`/api/admin/exams/${futureExam.id}/admission-arrangement`, { method: 'POST', body: {
mixingScope: 'district', numberRuleId: 'admit_rule_candidate_school_room_seat', seed: 'future-window-test'
} });
assert.equal(futureArrangement.response.status, 200, '不同考试应分别保存独立编排方案');
const earlyDownload = await candidate.request(`/api/candidate/registrations/${futureRegistrationId}/admit-card`);
assert.equal(earlyDownload.response.status, 403, '准考证下载窗口开放前必须拒绝下载');
@@ -479,6 +510,7 @@ try {
console.log('✓ 超级、校级、班级管理员的数据范围与权限隔离');
console.log('✓ 固定报名号账户、首次强制改密、完整资料与注册开关');
console.log('✓ 多科目考试创建与考生自主选科报名');
console.log('✓ 五级混编、四种号码规则、多科目同考点与逐科座位');
console.log('✓ 审批流程设计、同级转交与超级管理员监督退回');
console.log('✓ 校级按班级批量申领、终审原子建号与结果返回');
console.log('✓ 结构化考点考场档案、变更审批与班级只读边界');