EL 569d827b78 prisma.config.ts 新增 seed 命令:node --env-file=.env --loader ts-node/esm prisma/seed.ts
各 DTO 增加 Swagger ApiProperty/ApiPropertyOptional 描述与示例(尤其 phone/password)
seed.ts 为新增完整 seed 脚本(幂等 upsert + 角色样例 + 患者 + 工程师分配)
2026-03-12 18:50:03 +08:00

302 lines
7.2 KiB
TypeScript

import { PrismaPg } from '@prisma/adapter-pg';
import { randomBytes, scrypt } from 'node:crypto';
import { promisify } from 'node:util';
import { PrismaClient } from '../src/generated/prisma/client.js';
import { UserRole } from '../src/generated/prisma/enums.js';
const scryptAsync = promisify(scrypt);
const TEST_PASSWORD = 'Test123456';
const HOSPITAL_CODE = 'DEMO_HOSP_001';
const HOSPITAL_NAME = 'Demo Hospital';
const DEPARTMENT_NAME = 'Cardiology';
const MEDICAL_GROUP_NAME = 'Group A';
interface SeedUserInput {
phone: string;
name: string;
role: UserRole;
hospitalId: number | null;
departmentId: number | null;
medicalGroupId: number | null;
managerId: number | null;
}
interface PatientInput {
name: string;
hospitalId: number;
departmentId: number | null;
medicalGroupId: number | null;
doctorId: number;
}
function requireDatabaseUrl(): string {
const url = process.env.DATABASE_URL;
if (!url) {
throw new Error('DATABASE_URL is required. Run with `node --env-file=.env ...`.');
}
return url;
}
async function hashPassword(password: string): Promise<string> {
const salt = randomBytes(16).toString('hex');
const derivedKey = (await scryptAsync(password, salt, 64)) as Buffer;
return `${salt}:${derivedKey.toString('hex')}`;
}
async function upsertUser(
prisma: PrismaClient,
user: SeedUserInput,
passwordHash: string,
) {
return prisma.user.upsert({
where: { phone: user.phone },
create: {
phone: user.phone,
passwordHash,
name: user.name,
role: user.role,
hospitalId: user.hospitalId,
departmentId: user.departmentId,
medicalGroupId: user.medicalGroupId,
managerId: user.managerId,
isActive: true,
},
update: {
passwordHash,
name: user.name,
role: user.role,
hospitalId: user.hospitalId,
departmentId: user.departmentId,
medicalGroupId: user.medicalGroupId,
managerId: user.managerId,
isActive: true,
},
select: {
id: true,
role: true,
phone: true,
name: true,
},
});
}
async function upsertPatientByNaturalKey(
prisma: PrismaClient,
patient: PatientInput,
) {
const existing = await prisma.patient.findFirst({
where: {
name: patient.name,
hospitalId: patient.hospitalId,
doctorId: patient.doctorId,
},
select: { id: true },
});
if (existing) {
return prisma.patient.update({
where: { id: existing.id },
data: {
departmentId: patient.departmentId,
medicalGroupId: patient.medicalGroupId,
},
select: { id: true, name: true },
});
}
return prisma.patient.create({
data: patient,
select: { id: true, name: true },
});
}
async function main() {
const adapter = new PrismaPg({ connectionString: requireDatabaseUrl() });
const prisma = new PrismaClient({ adapter });
try {
const passwordHash = await hashPassword(TEST_PASSWORD);
const hospital = await prisma.hospital.upsert({
where: { code: HOSPITAL_CODE },
create: {
code: HOSPITAL_CODE,
name: HOSPITAL_NAME,
},
update: {
name: HOSPITAL_NAME,
},
select: { id: true, name: true, code: true },
});
const department = await prisma.department.upsert({
where: {
hospitalId_name: {
hospitalId: hospital.id,
name: DEPARTMENT_NAME,
},
},
create: {
name: DEPARTMENT_NAME,
hospitalId: hospital.id,
},
update: {},
select: { id: true, name: true },
});
const medicalGroup = await prisma.medicalGroup.upsert({
where: {
departmentId_name: {
departmentId: department.id,
name: MEDICAL_GROUP_NAME,
},
},
create: {
name: MEDICAL_GROUP_NAME,
departmentId: department.id,
},
update: {},
select: { id: true, name: true },
});
const systemAdmin = await upsertUser(
prisma,
{
phone: '+8613800000001',
name: 'System Admin',
role: UserRole.SYSTEM_ADMIN,
hospitalId: null,
departmentId: null,
medicalGroupId: null,
managerId: null,
},
passwordHash,
);
const hospitalAdmin = await upsertUser(
prisma,
{
phone: '+8613800000002',
name: 'Hospital Admin',
role: UserRole.HOSPITAL_ADMIN,
hospitalId: hospital.id,
departmentId: null,
medicalGroupId: null,
managerId: null,
},
passwordHash,
);
const director = await upsertUser(
prisma,
{
phone: '+8613800000003',
name: 'Director',
role: UserRole.DIRECTOR,
hospitalId: hospital.id,
departmentId: department.id,
medicalGroupId: null,
managerId: hospitalAdmin.id,
},
passwordHash,
);
const teamLead = await upsertUser(
prisma,
{
phone: '+8613800000004',
name: 'Team Lead',
role: UserRole.TEAM_LEAD,
hospitalId: hospital.id,
departmentId: department.id,
medicalGroupId: medicalGroup.id,
managerId: director.id,
},
passwordHash,
);
const doctor = await upsertUser(
prisma,
{
phone: '+8613800000005',
name: 'Doctor',
role: UserRole.DOCTOR,
hospitalId: hospital.id,
departmentId: department.id,
medicalGroupId: medicalGroup.id,
managerId: teamLead.id,
},
passwordHash,
);
const engineer = await upsertUser(
prisma,
{
phone: '+8613800000006',
name: 'Engineer',
role: UserRole.ENGINEER,
hospitalId: hospital.id,
departmentId: null,
medicalGroupId: null,
managerId: hospitalAdmin.id,
},
passwordHash,
);
await upsertPatientByNaturalKey(prisma, {
name: 'Patient Alpha',
hospitalId: hospital.id,
departmentId: department.id,
medicalGroupId: medicalGroup.id,
doctorId: doctor.id,
});
await upsertPatientByNaturalKey(prisma, {
name: 'Patient Beta',
hospitalId: hospital.id,
departmentId: department.id,
medicalGroupId: medicalGroup.id,
doctorId: doctor.id,
});
await prisma.engineerHospitalAssignment.upsert({
where: {
hospitalId_engineerId: {
hospitalId: hospital.id,
engineerId: engineer.id,
},
},
create: {
hospitalId: hospital.id,
engineerId: engineer.id,
assignedById: systemAdmin.id,
},
update: {
assignedById: systemAdmin.id,
},
select: { id: true },
});
console.log(`Seed completed for hospital ${hospital.code} (${hospital.name}).`);
console.log('Test password for all seeded users:', TEST_PASSWORD);
console.table(
[systemAdmin, hospitalAdmin, director, teamLead, doctor, engineer].map(
(user) => ({
role: user.role,
phone: user.phone,
name: user.name ?? '',
password: TEST_PASSWORD,
}),
),
);
console.log('No mini-program or official-account openId was pre-seeded.');
} finally {
await prisma.$disconnect();
}
}
main().catch((error) => {
console.error('Seed failed:', error);
process.exit(1);
});