tyt-api-nest/prisma/seed.mjs
EL 6ec8891be5 修复 E2E 准备脚本:
package.json
test:e2e:prepare 现在是 migrate reset --force && prisma generate && seed
为 seed 运行时补充 JS Prisma client 生成器:
schema.prisma
修复 seed 在 ESM/CJS 下的 Prisma 导入兼容:
seed.mjs
修复 Jest 环境未加载 .env 导致连到 127.0.0.1 的问题:
e2e-app.helper.ts
修复夹具依赖“名称”导致被组织测试改名后失效的问题(改为按 seed openId 反查):
e2e-fixtures.helper.ts
修复组织测试的状态污染与清理逻辑,并收敛 afterAll 资源释放:
organization.e2e-spec.ts
e2e-context.helper.ts
2026-03-13 03:29:16 +08:00

450 lines
11 KiB
JavaScript

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 },
update: data,
create: {
...data,
openId,
},
});
}
async function ensurePatient({
hospitalId,
doctorId,
name,
phone,
idCardHash,
}) {
const existing = await prisma.patient.findFirst({
where: {
hospitalId,
phone,
idCardHash,
},
});
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,
idCardHash,
},
});
}
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',
idCardHash: 'seed-id-card-cross-hospital',
});
const patientA2 = await ensurePatient({
hospitalId: hospitalA.id,
doctorId: doctorA2.id,
name: 'Seed Patient A2',
phone: '13800002002',
idCardHash: 'seed-id-card-a2',
});
const patientA3 = await ensurePatient({
hospitalId: hospitalA.id,
doctorId: doctorA3.id,
name: 'Seed Patient A3',
phone: '13800002003',
idCardHash: 'seed-id-card-a3',
});
const patientB1 = await ensurePatient({
hospitalId: hospitalB.id,
doctorId: doctorB.id,
name: 'Seed Patient B1',
phone: '13800002001',
idCardHash: 'seed-id-card-cross-hospital',
});
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();
});