各 DTO 增加 Swagger ApiProperty/ApiPropertyOptional 描述与示例(尤其 phone/password) seed.ts 为新增完整 seed 脚本(幂等 upsert + 角色样例 + 患者 + 工程师分配)
302 lines
7.2 KiB
TypeScript
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);
|
|
});
|