Separate base database initialization from test data seeding

This commit is contained in:
2026-07-20 19:49:29 +08:00 Unverified
parent 5b86cdaec2
commit b8f2799ed6
11 changed files with 345 additions and 43 deletions
+141 -23
View File
@@ -5,7 +5,7 @@ export function createSeedDatabase({ nowIso, hashPassword }) {
const candidateId = 'usr_demo';
const examId = 'exam_autumn_2026';
const registrationId = 'reg_demo_2026';
return {
const database = {
meta: { version: 15, createdAt: nowIso() },
settings: { selfRegistrationEnabled: false },
organization: {
@@ -75,23 +75,10 @@ export function createSeedDatabase({ nowIso, hashPassword }) {
registrations: [
{
id: registrationId, userId: candidateId, examId, subjectIds: ['sub_chinese', 'sub_math', 'sub_physics', 'sub_english', 'sub_chemistry'],
status: 'approved', paymentStatus: 'paid', paidAt: '2026-07-18T09:00:00.000Z', paidBy: 'usr_class_admin', createdAt: '2026-07-08T05:18:00.000Z', reviewedAt: '2026-07-18T08:32:00.000Z', registrationNumber: '2026-HZ01-F-0001', numberRuleId: 'rule_default',
admitCard: {
planId: 'arrangement_demo_2026', number: '32070600108', centerId: 'center_hz3', testCenter: '海州市第三中学考点', centerCode: 'HZ03-C01', centerAddress: '江苏省 连云港市 连云区 育才路 16 号',
room: '第 001 考场', seat: '08', generatedAt: '2026-07-19T02:00:00.000Z', assignments: [
{ subjectId: 'sub_chinese', roomId: 'room_hz3_001', roomName: '第 001 考场', roomCode: '001', examRoomCode: '001', building: '笃学楼', floor: '1 层', seat: '08', subjectSignature: 'sub_chinese|sub_math|sub_physics|sub_english|sub_chemistry' },
{ subjectId: 'sub_math', roomId: 'room_hz3_001', roomName: '第 001 考场', roomCode: '001', examRoomCode: '001', building: '笃学楼', floor: '1 层', seat: '08', subjectSignature: 'sub_chinese|sub_math|sub_physics|sub_english|sub_chemistry' },
{ subjectId: 'sub_physics', roomId: 'room_hz3_002', roomName: '第 002 考场', roomCode: '002', examRoomCode: '002', building: '笃学楼', floor: '1 层', seat: '08', subjectSignature: 'sub_chinese|sub_math|sub_physics|sub_english|sub_chemistry' },
{ subjectId: 'sub_english', roomId: 'room_hz3_001', roomName: '第 001 考场', roomCode: '001', examRoomCode: '001', building: '笃学楼', floor: '1 层', seat: '08', subjectSignature: 'sub_chinese|sub_math|sub_physics|sub_english|sub_chemistry' },
{ subjectId: 'sub_chemistry', roomId: 'room_hz3_002', roomName: '第 002 考场', roomCode: '002', examRoomCode: '002', building: '笃学楼', floor: '1 层', seat: '08', subjectSignature: 'sub_chinese|sub_math|sub_physics|sub_english|sub_chemistry' }
]
}
status: 'approved', paymentStatus: 'paid', paidAt: '2026-07-18T09:00:00.000Z', paidBy: 'usr_class_admin', createdAt: '2026-07-08T05:18:00.000Z', reviewedAt: '2026-07-18T08:32:00.000Z', registrationNumber: '2026-HZ01-F-0001', numberRuleId: 'rule_default'
}
],
results: [
{ id: 'result_demo_1', registrationId, subjectId: 'sub_chinese', score: 118, grade: 'B+', published: true, publishedAt: '2026-07-19T03:00:00.000Z' },
{ id: 'result_demo_2', registrationId, subjectId: 'sub_math', score: 132, grade: 'A', published: true, publishedAt: '2026-07-19T03:00:00.000Z' }
],
results: [],
testCenters: [
{ id: 'center_hz1', schoolId: 'school_hz1', code: 'HZ01-C01', name: '海州市第一中学考点', provinceCode: '320000', provinceName: '江苏省', cityCode: '320700', cityName: '连云港市', districtCode: '320706', districtName: '海州区', address: '学府路 8 号', contact: '0518-8602 1101', managerName: '王立新', managerPhone: '13800001101', emergencyPhone: '0518-8602 1190', gateOpenTime: '07:00', transport: '地铁 2 号线学府路站 2 号口,步行约 600 米', status: 'active', notes: '南门为考生唯一入口,无障碍通道位于东侧。', rooms: '教学楼 A001、002;实验楼:机考 01', updatedAt: nowIso() },
{ id: 'center_hz3', schoolId: 'school_hz3', code: 'HZ03-C01', name: '海州市第三中学考点', provinceCode: '320000', provinceName: '江苏省', cityCode: '320700', cityName: '连云港市', districtCode: '320703', districtName: '连云区', address: '育才路 16 号', contact: '0518-8602 3301', managerName: '李文峰', managerPhone: '13800003301', emergencyPhone: '0518-8602 3390', gateOpenTime: '07:10', transport: '公交 18、32 路育才路站,考点不提供社会车辆停车位', status: 'active', notes: '西门设置临时物品存放区。', rooms: '笃学楼:001、002', updatedAt: nowIso() }
@@ -143,13 +130,7 @@ export function createSeedDatabase({ nowIso, hashPassword }) {
]
}
],
arrangementPlans: [
{
id: 'arrangement_demo_2026', examId, numberRuleId: 'admit_rule_district_room_seat', mixingScope: 'city', randomSeed: 'EX-2026-AUT',
candidateCount: 1, centerCount: 1, subjectAssignmentCount: 5, subjectCombinationCount: 1, sameSchoolCenterRate: 0,
warnings: [], generatedBy: adminId, generatedAt: '2026-07-19T02:00:00.000Z'
}
],
arrangementPlans: [],
candidateAccountBatches: [],
candidateAccountBatchItems: [],
numberRules: [
@@ -187,4 +168,141 @@ export function createSeedDatabase({ nowIso, hashPassword }) {
{ id: 'log_1', actorId: adminId, action: '发布通知', detail: '发布《市第三中学考点交通提示》', createdAt: '2026-07-18T06:00:00.000Z' }
]
};
const schoolDefinitions = [
{ key: 'hz1', id: 'school_hz1', code: 'HZ01', name: '海州市第一中学', districtCode: '320706', districtName: '海州区', address: '学府路 8 号' },
{ key: 'hz3', id: 'school_hz3', code: 'HZ03', name: '海州市第三中学', districtCode: '320703', districtName: '连云区', address: '育才路 16 号' },
{ key: 'hz5', id: 'school_hz5', code: 'HZ05', name: '海州市第五中学', districtCode: '320707', districtName: '赣榆区', address: '青口路 28 号' },
{ key: 'hz7', id: 'school_hz7', code: 'HZ07', name: '海州市第七中学', districtCode: '320723', districtName: '灌云县', address: '胜利路 66 号' }
];
for (const school of schoolDefinitions) {
if (!database.schools.some(item => item.id === school.id)) {
database.schools.push({ id: school.id, name: school.name, code: school.code, address: `江苏省连云港市${school.districtName}${school.address}`, active: true });
}
for (let classIndex = 1; classIndex <= 3; classIndex += 1) {
const classId = `class_${school.key}_30${classIndex}`;
if (!database.classes.some(item => item.id === classId)) {
database.classes.push({ id: classId, schoolId: school.id, name: `高三(${classIndex})班`, grade: '高三', active: true });
}
}
}
const schoolPasswordHash = hashPassword('School123!');
const classPasswordHash = hashPassword('Class123!');
const candidatePasswordHash = hashPassword('Candidate123!');
for (const school of schoolDefinitions) {
const generatedSchoolAdminId = `usr_test_school_admin_${school.key}`;
database.users.push({
id: generatedSchoolAdminId, username: `test_school_admin_${school.key}`, passwordHash: schoolPasswordHash,
role: 'admin', adminLevel: 'school', schoolId: school.id, displayName: `${school.name}测试校管`, active: true, createdAt: nowIso()
});
for (let classIndex = 1; classIndex <= 3; classIndex += 1) {
const classId = `class_${school.key}_30${classIndex}`;
database.users.push({
id: `usr_test_class_admin_${school.key}_${classIndex}`, username: `test_class_admin_${school.key}_${classIndex}`,
passwordHash: classPasswordHash, role: 'admin', adminLevel: 'class', schoolId: school.id, classId,
displayName: `${school.name}高三${classIndex}班测试班管`, active: true, createdAt: nowIso()
});
}
}
for (const school of schoolDefinitions) {
let center = database.testCenters.find(item => item.schoolId === school.id);
if (!center) {
center = {
id: `center_${school.key}`, schoolId: school.id, code: `${school.code}-C01`, name: `${school.name}考点`,
provinceCode: '320000', provinceName: '江苏省', cityCode: '320700', cityName: '连云港市',
districtCode: school.districtCode, districtName: school.districtName, address: school.address,
contact: '0518-8602 0000', managerName: '测试负责人', managerPhone: '13800000000', emergencyPhone: '0518-8602 0120',
gateOpenTime: '07:00', transport: '测试数据:请以正式考点通知为准', status: 'active', notes: '仅供手动导入测试数据使用', rooms: '', updatedAt: nowIso()
};
database.testCenters.push(center);
}
for (let roomIndex = 1; roomIndex <= 4; roomIndex += 1) {
const roomId = `room_${school.key}_test_${roomIndex}`;
if (!database.testRooms.some(item => item.id === roomId)) {
database.testRooms.push({
id: roomId, centerId: center.id, code: `T0${roomIndex}`, name: `测试第 ${roomIndex} 考场`, building: '测试教学楼',
floor: `${Math.ceil(roomIndex / 2)}`, capacity: 30, seatPlan: '等待正式编排', roomType: 'standard', status: 'active', notes: '未编排'
});
}
}
}
// 测试库停留在考场编排前:不预置编排计划、准考证或成绩。
const familyNames = ['赵', '钱', '孙', '李', '周', '吴', '郑', '王', '冯', '陈', '褚', '卫'];
const givenNames = ['子涵', '梓萱', '宇航', '雨欣', '浩然', '思远', '佳宁', '晨曦', '明轩', '若彤', '嘉诚', '欣怡'];
const subjectSets = [
['sub_chinese', 'sub_math', 'sub_english'],
['sub_chinese', 'sub_math', 'sub_physics', 'sub_chemistry'],
['sub_chinese', 'sub_math', 'sub_history', 'sub_biology'],
['sub_chinese', 'sub_math', 'sub_english', 'sub_physics', 'sub_chemistry'],
['sub_chinese', 'sub_math', 'sub_english', 'sub_history', 'sub_biology']
];
const registrationWorkflowId = 'workflow_registration';
for (let index = 0; index < 360; index += 1) {
const serial = index + 1001;
const school = schoolDefinitions[index % schoolDefinitions.length];
const classIndex = Math.floor(index / schoolDefinitions.length) % 3 + 1;
const classId = `class_${school.key}_30${classIndex}`;
const gender = index % 2 === 0 ? '男' : '女';
const genderCode = gender === '男' ? 'M' : 'F';
const userId = `usr_bulk_${String(index + 1).padStart(4, '0')}`;
const profileId = `profile_bulk_${String(index + 1).padStart(4, '0')}`;
const registrationIdBulk = `reg_bulk_${String(index + 1).padStart(4, '0')}`;
const candidateNumber = `2026-${school.code}-${genderCode}-${String(serial).padStart(4, '0')}`;
const createdAt = new Date(Date.UTC(2026, 6, 2 + (index % 20), 1 + (index % 8), index % 60)).toISOString();
const name = `${familyNames[index % familyNames.length]}${givenNames[Math.floor(index / familyNames.length) % givenNames.length]}${Math.floor(index / 144) + 1}`;
const idNumber = `3207002008${String(index % 12 + 1).padStart(2, '0')}${String(index % 28 + 1).padStart(2, '0')}${String(index + 1).padStart(4, '0')}`;
const phone = `138${String(10000000 + index).padStart(8, '0')}`;
database.users.push({
id: userId, username: candidateNumber, candidateNumber, passwordHash: candidatePasswordHash, role: 'candidate',
displayName: name, active: true, mustChangePassword: false, createdAt
});
database.candidateProfiles.push({
id: profileId, userId, name, gender, idNumber, phone, email: `candidate${String(index + 1).padStart(4, '0')}@example.test`,
school: school.name, grade: `高三(${classIndex})班`, schoolId: school.id, classId,
provinceCode: '320000', provinceName: '江苏省', cityCode: '320700', cityName: '连云港市',
districtCode: school.districtCode, districtName: school.districtName, address: `${school.address}测试宿舍 ${index % 20 + 1}`,
emergencyContact: `${familyNames[index % familyNames.length]}家长`, emergencyPhone: `139${String(10000000 + index).padStart(8, '0')}`,
nativePlace: `江苏${school.districtName}`, birthDate: `2008-${String(index % 12 + 1).padStart(2, '0')}-${String(index % 28 + 1).padStart(2, '0')}`,
ethnicity: index % 19 === 0 ? '回族' : '汉族', postalCode: '222000', guardianName: `${familyNames[index % familyNames.length]}家长`,
guardianPhone: `139${String(10000000 + index).padStart(8, '0')}`, profileCompleted: true, status: 'approved',
reviewNote: '批量测试数据:学籍核验通过', reviewedAt: '2026-06-30T08:00:00.000Z', reviewerId: adminId, updatedAt: createdAt
});
const scenario = index % 4;
const status = scenario === 0 ? 'pending' : scenario === 1 ? 'rejected' : 'approved';
const paymentStatus = scenario === 3 ? 'paid' : 'unpaid';
const classAdminId = `usr_test_class_admin_${school.key}_${classIndex}`;
const schoolAdmin = `usr_test_school_admin_${school.key}`;
database.registrations.push({
id: registrationIdBulk, userId, examId, subjectIds: subjectSets[index % subjectSets.length], status, paymentStatus,
paidAt: paymentStatus === 'paid' ? '2026-07-25T08:30:00.000Z' : null,
paidBy: paymentStatus === 'paid' ? classAdminId : null,
createdAt, reviewedAt: status === 'pending' ? null : '2026-07-22T08:00:00.000Z',
reviewNote: status === 'rejected' ? '测试场景:报名资料被退回' : status === 'approved' ? '测试场景:报名审核通过' : '',
registrationNumber: candidateNumber, numberRuleId: 'rule_default'
});
if (status === 'pending' || status === 'rejected') {
const instanceId = `flow_reg_bulk_${String(index + 1).padStart(4, '0')}`;
database.workflowInstances.push({
id: instanceId, workflowId: registrationWorkflowId, businessType: 'registration_review', businessId: registrationIdBulk,
status, currentStep: 1, assigneeId: status === 'pending' ? schoolAdmin : null, createdAt,
completedAt: status === 'rejected' ? '2026-07-22T08:00:00.000Z' : null
});
database.workflowActions.push({
id: `flow_action_submit_bulk_${String(index + 1).padStart(4, '0')}`, instanceId, actorId: userId, action: 'submit',
note: '提交考试报名', fromAssigneeId: null, toAssigneeId: schoolAdmin, createdAt
});
if (status === 'rejected') {
database.workflowActions.push({
id: `flow_action_reject_bulk_${String(index + 1).padStart(4, '0')}`, instanceId, actorId: schoolAdmin, action: 'reject',
note: '测试场景:报名资料被退回', fromAssigneeId: schoolAdmin, toAssigneeId: null, createdAt: '2026-07-22T08:00:00.000Z'
});
}
}
}
return database;
}