From 6f2cf2ded41604524763cb6578b44091dfd9796a Mon Sep 17 00:00:00 2001 From: biss Date: Tue, 21 Jul 2026 20:43:04 +0800 Subject: [PATCH] =?UTF-8?q?=E6=BC=94=E7=A4=BA=E6=95=B0=E6=8D=AE=E7=94=9F?= =?UTF-8?q?=E6=88=90=E9=80=BB=E8=BE=91=E5=BE=AE=E8=B0=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/data/seed.mjs | 15 ++++++++++----- tests/seed.test.mjs | 10 ++++++++++ 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/src/data/seed.mjs b/src/data/seed.mjs index 4900380..4e98d88 100644 --- a/src/data/seed.mjs +++ b/src/data/seed.mjs @@ -347,11 +347,11 @@ export function createSeedDatabase({ nowIso, hashPassword, candidateCount = 1200 id: `admission_plan_${school.key}`, kind: 'plan', examId, userId: `usr_${school.key}`, schoolId: school.id, status: 'approved', payload: { categories: [ - { code: 'general', name: '普通生', quota: mainCandidateCount, specialtyCategory: '', specialtyType: '', indicatorAllocations: [] }, - { code: 'sports', name: '体育特长生', quota: specialtyCandidateCount, specialtyCategory: 'sports', specialtyType: '', indicatorAllocations: [] }, - { code: 'arts', name: '艺术特长生', quota: specialtyCandidateCount, specialtyCategory: 'arts', specialtyType: '', indicatorAllocations: [] } + { code: 'general', name: '普通生', quota: 350, specialtyCategory: '', specialtyType: '', indicatorAllocations: [] }, + { code: 'sports', name: '体育特长生', quota: 1, specialtyCategory: 'sports', specialtyType: '', indicatorAllocations: [] }, + { code: 'arts', name: '艺术特长生', quota: 1, specialtyCategory: 'arts', specialtyType: '', indicatorAllocations: [] } ], - note: '演示数据招生计划', submittedBy: `${school.name}招生办`, reviewedBy: '林老师', reviewedAt: admissionCreatedAt, publicVisible: true + note: '演示数据招生计划:普通类 350 人,特长生合计 2 人', submittedBy: `${school.name}招生办`, reviewedBy: '林老师', reviewedAt: admissionCreatedAt, publicVisible: true }, createdAt: admissionCreatedAt, updatedAt: admissionCreatedAt }); @@ -370,12 +370,17 @@ export function createSeedDatabase({ nowIso, hashPassword, candidateCount = 1200 }); } const rotatedAdmissionSchools = admissionSchools.map((_, offset) => admissionSchools[(candidateIndex + offset) % admissionSchools.length]); + const choices = rotatedAdmissionSchools.map((school, choiceIndex) => ({ + schoolId: school.id, + categoryCode: isSpecialtyCandidate && choiceIndex === 0 ? profile.specialtyCategory : 'general', + preferenceType: 'general' + })); database.admissionRecords.push({ id: `preference_main_${String(candidateIndex + 1).padStart(4, '0')}`, kind: 'preference', examId, userId: registration.userId, schoolId: null, status: 'submitted', payload: { round: 1, submissionCount: 1, submittedAt: new Date(Date.UTC(2026, 6, 5 + (candidateIndex % 10), 1 + (candidateIndex % 8), candidateIndex % 60)).toISOString(), - choices: rotatedAdmissionSchools.map(school => ({ schoolId: school.id, categoryCode: 'general', preferenceType: 'general' })) + choices }, createdAt: admissionCreatedAt, updatedAt: '2026-07-15T08:00:00.000Z' }); diff --git a/tests/seed.test.mjs b/tests/seed.test.mjs index a702272..b508cbb 100644 --- a/tests/seed.test.mjs +++ b/tests/seed.test.mjs @@ -7,8 +7,10 @@ const mainRegistrations = state.registrations.filter(registration => registratio const mainRegistrationIds = new Set(mainRegistrations.map(registration => registration.id)); const mainResults = state.results.filter(result => mainRegistrationIds.has(result.registrationId)); const mainPreferences = state.admissionRecords.filter(record => record.kind === 'preference' && record.examId === mainExam.id && Number(record.payload?.round || 1) === 1); +const mainPlans = state.admissionRecords.filter(record => record.kind === 'plan' && record.examId === mainExam.id && record.status === 'approved'); const specialtyProfiles = state.candidateProfiles.filter(profile => profile.specialtyCategory && profile.specialtyType); const specialtyUserIds = new Set(specialtyProfiles.map(profile => profile.userId)); +const profilesByUserId = new Map(state.candidateProfiles.map(profile => [profile.userId, profile])); assert.ok(state.exams.length >= 2, '演示数据至少包含两场考试'); assert.equal(state.schools.filter(school => school.isSourceSchool).length, 5, '演示数据应包含 5 所生源校'); @@ -22,8 +24,16 @@ assert.equal(mainResults.length, 1200 * 9, '每名主考试考生都应有完整 assert.ok(mainResults.every(result => result.published), '主考试成绩应全部发布'); assert.equal(mainPreferences.length, 1200, '主考试每名考生都应完成第一轮志愿'); assert.ok(mainPreferences.every(record => record.status === 'submitted' && record.payload.submissionCount === 1 && record.payload.choices.length === 3), '第一轮志愿应提交并填满 3 个招生校'); +assert.equal(mainPlans.length, 3, '三所招生校都应有已审核通过的招生计划'); +assert.ok(mainPlans.every(plan => plan.payload.categories.find(category => category.code === 'general')?.quota === 350), '每所招生校普通类计划应为 350 人'); +assert.ok(mainPlans.every(plan => plan.payload.categories.filter(category => category.specialtyCategory).reduce((sum, category) => sum + category.quota, 0) === 2), '每所招生校特长生计划合计应为 2 人'); assert.equal(specialtyProfiles.length, 150, '应有 150 名特长生'); assert.ok(mainRegistrations.filter(registration => specialtyUserIds.has(registration.userId)).every(registration => registration.featureScore >= 80 && registration.featureScore <= 100), '特长生特征分应分布在 80-100 分'); +assert.ok(mainPreferences.every(preference => { + const profile = profilesByUserId.get(preference.userId); + const categories = preference.payload.choices.map(choice => choice.categoryCode); + return profile.specialtyCategory ? categories[0] === profile.specialtyCategory && categories.slice(1).every(code => code === 'general') : categories.every(code => code === 'general'); +}), '特长生第一志愿应匹配本人特长类别,其余志愿及普通考生志愿应填报普通类'); assert.deepEqual(new Set(state.users.map(user => user.passwordHash)), new Set(['test-12345678']), '所有预置账号密码应统一为 12345678'); for (const subject of mainExam.subjects) {