import 'dotenv/config'; import { PrismaPg } from '@prisma/adapter-pg'; import { hash } from 'bcrypt'; import prismaClientPackage from '@prisma/client'; const { DeviceStatus, PrismaClient, Role, TaskStatus } = prismaClientPackage; const connectionString = process.env.DATABASE_URL; if (!connectionString) { throw new Error('DATABASE_URL is required to run seed'); } const prisma = new PrismaClient({ adapter: new PrismaPg({ connectionString }), }); const SEED_PASSWORD_PLAIN = 'Seed@1234'; async function ensureHospital(name) { return ( (await prisma.hospital.findFirst({ where: { name } })) ?? prisma.hospital.create({ data: { name } }) ); } async function ensureDepartment(hospitalId, name) { return ( (await prisma.department.findFirst({ where: { hospitalId, name }, })) ?? prisma.department.create({ data: { hospitalId, name }, }) ); } async function ensureGroup(departmentId, name) { return ( (await prisma.group.findFirst({ where: { departmentId, name }, })) ?? prisma.group.create({ data: { departmentId, name }, }) ); } async function upsertUserByOpenId(openId, data) { return prisma.user.upsert({ where: { openId }, // 每次重置/补种子时推进失效时间,确保历史 token 无法继续访问。 update: { ...data, tokenValidAfter: new Date(), }, create: { ...data, openId, }, }); } async function ensurePatient({ hospitalId, doctorId, name, phone, idCard }) { const existing = await prisma.patient.findFirst({ where: { hospitalId, phone, idCard, }, }); if (existing) { if (existing.doctorId !== doctorId || existing.name !== name) { return prisma.patient.update({ where: { id: existing.id }, data: { doctorId, name }, }); } return existing; } return prisma.patient.create({ data: { hospitalId, doctorId, name, phone, idCard, }, }); } async function main() { const seedPasswordHash = await hash(SEED_PASSWORD_PLAIN, 12); const hospitalA = await ensureHospital('Seed Hospital A'); const hospitalB = await ensureHospital('Seed Hospital B'); const departmentA1 = await ensureDepartment(hospitalA.id, 'Neurosurgery-A1'); const departmentA2 = await ensureDepartment(hospitalA.id, 'Cardiology-A2'); const departmentB1 = await ensureDepartment(hospitalB.id, 'Neurosurgery-B1'); const groupA1 = await ensureGroup(departmentA1.id, 'Shift-A1'); const groupA2 = await ensureGroup(departmentA2.id, 'Shift-A2'); const groupB1 = await ensureGroup(departmentB1.id, 'Shift-B1'); const systemAdmin = await upsertUserByOpenId('seed-system-admin-openid', { name: 'Seed System Admin', phone: '13800001000', passwordHash: seedPasswordHash, role: Role.SYSTEM_ADMIN, hospitalId: null, departmentId: null, groupId: null, }); const hospitalAdminA = await upsertUserByOpenId( 'seed-hospital-admin-a-openid', { name: 'Seed Hospital Admin A', phone: '13800001001', passwordHash: seedPasswordHash, role: Role.HOSPITAL_ADMIN, hospitalId: hospitalA.id, departmentId: null, groupId: null, }, ); await upsertUserByOpenId('seed-hospital-admin-b-openid', { name: 'Seed Hospital Admin B', phone: '13800001101', passwordHash: seedPasswordHash, role: Role.HOSPITAL_ADMIN, hospitalId: hospitalB.id, departmentId: null, groupId: null, }); const directorA = await upsertUserByOpenId('seed-director-a-openid', { name: 'Seed Director A', phone: '13800001002', passwordHash: seedPasswordHash, role: Role.DIRECTOR, hospitalId: hospitalA.id, departmentId: departmentA1.id, groupId: null, }); const leaderA = await upsertUserByOpenId('seed-leader-a-openid', { name: 'Seed Leader A', phone: '13800001003', passwordHash: seedPasswordHash, role: Role.LEADER, hospitalId: hospitalA.id, departmentId: departmentA1.id, groupId: groupA1.id, }); const doctorA = await upsertUserByOpenId('seed-doctor-a-openid', { name: 'Seed Doctor A', phone: '13800001004', passwordHash: seedPasswordHash, role: Role.DOCTOR, hospitalId: hospitalA.id, departmentId: departmentA1.id, groupId: groupA1.id, }); const doctorA2 = await upsertUserByOpenId('seed-doctor-a2-openid', { name: 'Seed Doctor A2', phone: '13800001204', passwordHash: seedPasswordHash, role: Role.DOCTOR, hospitalId: hospitalA.id, departmentId: departmentA1.id, groupId: groupA1.id, }); const doctorA3 = await upsertUserByOpenId('seed-doctor-a3-openid', { name: 'Seed Doctor A3', phone: '13800001304', passwordHash: seedPasswordHash, role: Role.DOCTOR, hospitalId: hospitalA.id, departmentId: departmentA2.id, groupId: groupA2.id, }); const doctorB = await upsertUserByOpenId('seed-doctor-b-openid', { name: 'Seed Doctor B', phone: '13800001104', passwordHash: seedPasswordHash, role: Role.DOCTOR, hospitalId: hospitalB.id, departmentId: departmentB1.id, groupId: groupB1.id, }); const engineerA = await upsertUserByOpenId('seed-engineer-a-openid', { name: 'Seed Engineer A', phone: '13800001005', passwordHash: seedPasswordHash, role: Role.ENGINEER, hospitalId: hospitalA.id, departmentId: null, groupId: null, }); const engineerB = await upsertUserByOpenId('seed-engineer-b-openid', { name: 'Seed Engineer B', phone: '13800001105', passwordHash: seedPasswordHash, role: Role.ENGINEER, hospitalId: hospitalB.id, departmentId: null, groupId: null, }); const patientA1 = await ensurePatient({ hospitalId: hospitalA.id, doctorId: doctorA.id, name: 'Seed Patient A1', phone: '13800002001', idCard: '110101199001010011', }); const patientA2 = await ensurePatient({ hospitalId: hospitalA.id, doctorId: doctorA2.id, name: 'Seed Patient A2', phone: '13800002002', idCard: '110101199002020022', }); const patientA3 = await ensurePatient({ hospitalId: hospitalA.id, doctorId: doctorA3.id, name: 'Seed Patient A3', phone: '13800002003', idCard: '110101199003030033', }); const patientB1 = await ensurePatient({ hospitalId: hospitalB.id, doctorId: doctorB.id, name: 'Seed Patient B1', phone: '13800002001', idCard: '110101199001010011', }); const deviceA1 = await prisma.device.upsert({ where: { snCode: 'SEED-SN-A-001' }, update: { patientId: patientA1.id, currentPressure: 118, status: DeviceStatus.ACTIVE, }, create: { snCode: 'SEED-SN-A-001', patientId: patientA1.id, currentPressure: 118, status: DeviceStatus.ACTIVE, }, }); const deviceA2 = await prisma.device.upsert({ where: { snCode: 'SEED-SN-A-002' }, update: { patientId: patientA2.id, currentPressure: 112, status: DeviceStatus.ACTIVE, }, create: { snCode: 'SEED-SN-A-002', patientId: patientA2.id, currentPressure: 112, status: DeviceStatus.ACTIVE, }, }); await prisma.device.upsert({ where: { snCode: 'SEED-SN-A-003' }, update: { patientId: patientA3.id, currentPressure: 109, status: DeviceStatus.ACTIVE, }, create: { snCode: 'SEED-SN-A-003', patientId: patientA3.id, currentPressure: 109, status: DeviceStatus.ACTIVE, }, }); const deviceB1 = await prisma.device.upsert({ where: { snCode: 'SEED-SN-B-001' }, update: { patientId: patientB1.id, currentPressure: 121, status: DeviceStatus.ACTIVE, }, create: { snCode: 'SEED-SN-B-001', patientId: patientB1.id, currentPressure: 121, status: DeviceStatus.ACTIVE, }, }); await prisma.device.upsert({ where: { snCode: 'SEED-SN-A-004' }, update: { patientId: patientA1.id, currentPressure: 130, status: DeviceStatus.INACTIVE, }, create: { snCode: 'SEED-SN-A-004', patientId: patientA1.id, currentPressure: 130, status: DeviceStatus.INACTIVE, }, }); // 清理与种子设备关联的历史任务,保证 seed 可重复执行且生命周期夹具稳定。 const seedTaskItems = await prisma.taskItem.findMany({ where: { deviceId: { in: [deviceA1.id, deviceB1.id], }, }, select: { taskId: true }, }); const seedTaskIds = Array.from( new Set(seedTaskItems.map((item) => item.taskId)), ); if (seedTaskIds.length > 0) { await prisma.task.deleteMany({ where: { id: { in: seedTaskIds, }, }, }); } const lifecycleTaskA = await prisma.task.create({ data: { status: TaskStatus.COMPLETED, creatorId: doctorA.id, engineerId: engineerA.id, hospitalId: hospitalA.id, items: { create: [ { deviceId: deviceA1.id, oldPressure: 118, targetPressure: 120, }, ], }, }, include: { items: true }, }); const lifecycleTaskB = await prisma.task.create({ data: { status: TaskStatus.PENDING, creatorId: doctorB.id, engineerId: engineerB.id, hospitalId: hospitalB.id, items: { create: [ { deviceId: deviceB1.id, oldPressure: 121, targetPressure: 119, }, ], }, }, include: { items: true }, }); console.log( JSON.stringify( { ok: true, seedPasswordPlain: SEED_PASSWORD_PLAIN, hospitals: { hospitalAId: hospitalA.id, hospitalBId: hospitalB.id, }, departments: { departmentA1Id: departmentA1.id, departmentA2Id: departmentA2.id, departmentB1Id: departmentB1.id, }, groups: { groupA1Id: groupA1.id, groupA2Id: groupA2.id, groupB1Id: groupB1.id, }, users: { systemAdminId: systemAdmin.id, hospitalAdminAId: hospitalAdminA.id, directorAId: directorA.id, leaderAId: leaderA.id, doctorAId: doctorA.id, doctorA2Id: doctorA2.id, doctorA3Id: doctorA3.id, doctorBId: doctorB.id, engineerAId: engineerA.id, engineerBId: engineerB.id, }, patients: { patientA1Id: patientA1.id, patientA2Id: patientA2.id, patientA3Id: patientA3.id, patientB1Id: patientB1.id, }, devices: { deviceA1Id: deviceA1.id, deviceA2Id: deviceA2.id, deviceB1Id: deviceB1.id, }, tasks: { lifecycleTaskAId: lifecycleTaskA.id, lifecycleTaskBId: lifecycleTaskB.id, }, }, null, 2, ), ); } main() .catch((error) => { console.error('Seed failed:', error); process.exitCode = 1; }) .finally(async () => { await prisma.$disconnect(); });