844 lines
21 KiB
JavaScript
844 lines
21 KiB
JavaScript
import 'dotenv/config';
|
|
import { PrismaPg } from '@prisma/adapter-pg';
|
|
import { hash } from 'bcrypt';
|
|
import prismaClientPackage from '@prisma/client';
|
|
|
|
const { DictionaryType, 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 upsertUserByScope(data) {
|
|
return prisma.user.upsert({
|
|
where: {
|
|
phone_role_hospitalId: {
|
|
phone: data.phone,
|
|
role: data.role,
|
|
hospitalId: data.hospitalId,
|
|
},
|
|
},
|
|
// 每次重置/补种子时推进失效时间,确保历史 token 无法继续访问。
|
|
update: {
|
|
...data,
|
|
tokenValidAfter: new Date(),
|
|
},
|
|
create: data,
|
|
});
|
|
}
|
|
|
|
async function ensurePatient({
|
|
hospitalId,
|
|
doctorId,
|
|
creatorId,
|
|
name,
|
|
inpatientNo = null,
|
|
projectName = null,
|
|
phone,
|
|
idCard,
|
|
}) {
|
|
const existing = await prisma.patient.findFirst({
|
|
where: {
|
|
hospitalId,
|
|
phone,
|
|
idCard,
|
|
},
|
|
});
|
|
|
|
if (existing) {
|
|
if (
|
|
existing.doctorId !== doctorId ||
|
|
existing.creatorId !== creatorId ||
|
|
existing.name !== name ||
|
|
existing.inpatientNo !== inpatientNo ||
|
|
existing.projectName !== projectName
|
|
) {
|
|
return prisma.patient.update({
|
|
where: { id: existing.id },
|
|
data: { doctorId, creatorId, name, inpatientNo, projectName },
|
|
});
|
|
}
|
|
return existing;
|
|
}
|
|
|
|
return prisma.patient.create({
|
|
data: {
|
|
hospitalId,
|
|
doctorId,
|
|
creatorId,
|
|
name,
|
|
inpatientNo,
|
|
projectName,
|
|
phone,
|
|
idCard,
|
|
},
|
|
});
|
|
}
|
|
|
|
async function ensureFamilyMiniAppAccount({
|
|
phone,
|
|
openId = null,
|
|
serviceUid = null,
|
|
}) {
|
|
const existing = await prisma.familyMiniAppAccount.findUnique({
|
|
where: { phone },
|
|
});
|
|
|
|
if (existing) {
|
|
return prisma.familyMiniAppAccount.update({
|
|
where: { id: existing.id },
|
|
data: {
|
|
openId,
|
|
serviceUid,
|
|
lastLoginAt: new Date(),
|
|
},
|
|
});
|
|
}
|
|
|
|
return prisma.familyMiniAppAccount.create({
|
|
data: {
|
|
phone,
|
|
openId,
|
|
serviceUid,
|
|
lastLoginAt: new Date(),
|
|
},
|
|
});
|
|
}
|
|
|
|
async function ensureImplantCatalog({
|
|
modelCode,
|
|
manufacturer,
|
|
name,
|
|
pressureLevels = [],
|
|
isPressureAdjustable = true,
|
|
notes = null,
|
|
}) {
|
|
return prisma.implantCatalog.upsert({
|
|
where: { modelCode },
|
|
update: {
|
|
manufacturer,
|
|
name,
|
|
pressureLevels,
|
|
isPressureAdjustable,
|
|
notes,
|
|
},
|
|
create: {
|
|
modelCode,
|
|
manufacturer,
|
|
name,
|
|
pressureLevels,
|
|
isPressureAdjustable,
|
|
notes,
|
|
},
|
|
});
|
|
}
|
|
|
|
async function ensureDictionaryItem({
|
|
type,
|
|
label,
|
|
sortOrder = 0,
|
|
enabled = true,
|
|
}) {
|
|
return prisma.dictionaryItem.upsert({
|
|
where: {
|
|
type_label: {
|
|
type,
|
|
label,
|
|
},
|
|
},
|
|
update: {
|
|
sortOrder,
|
|
enabled,
|
|
},
|
|
create: {
|
|
type,
|
|
label,
|
|
sortOrder,
|
|
enabled,
|
|
},
|
|
});
|
|
}
|
|
|
|
async function ensurePatientSurgery({
|
|
patientId,
|
|
surgeryDate,
|
|
surgeryName,
|
|
surgeonName,
|
|
preOpPressure = null,
|
|
primaryDisease,
|
|
hydrocephalusTypes,
|
|
previousShuntSurgeryDate = null,
|
|
preOpMaterials = null,
|
|
notes = null,
|
|
}) {
|
|
const normalizedSurgeryDate = new Date(surgeryDate);
|
|
const normalizedPreviousDate = previousShuntSurgeryDate
|
|
? new Date(previousShuntSurgeryDate)
|
|
: null;
|
|
|
|
const existing = await prisma.patientSurgery.findFirst({
|
|
where: {
|
|
patientId,
|
|
surgeryDate: normalizedSurgeryDate,
|
|
surgeryName,
|
|
},
|
|
});
|
|
|
|
if (existing) {
|
|
return prisma.patientSurgery.update({
|
|
where: { id: existing.id },
|
|
data: {
|
|
surgeonName,
|
|
preOpPressure,
|
|
primaryDisease,
|
|
hydrocephalusTypes,
|
|
previousShuntSurgeryDate: normalizedPreviousDate,
|
|
preOpMaterials,
|
|
notes,
|
|
},
|
|
});
|
|
}
|
|
|
|
return prisma.patientSurgery.create({
|
|
data: {
|
|
patientId,
|
|
surgeryDate: normalizedSurgeryDate,
|
|
surgeryName,
|
|
surgeonName,
|
|
preOpPressure,
|
|
primaryDisease,
|
|
hydrocephalusTypes,
|
|
previousShuntSurgeryDate: normalizedPreviousDate,
|
|
preOpMaterials,
|
|
notes,
|
|
},
|
|
});
|
|
}
|
|
|
|
async function ensureDevice({
|
|
patientId,
|
|
surgeryId,
|
|
implantCatalogId,
|
|
currentPressure,
|
|
status,
|
|
implantModel,
|
|
implantManufacturer,
|
|
implantName,
|
|
isPressureAdjustable,
|
|
isAbandoned,
|
|
shuntMode,
|
|
proximalPunctureAreas,
|
|
valvePlacementSites,
|
|
distalShuntDirection,
|
|
initialPressure,
|
|
implantNotes,
|
|
labelImageUrl,
|
|
}) {
|
|
const existing = await prisma.device.findFirst({
|
|
where: {
|
|
patientId,
|
|
surgeryId,
|
|
implantNotes,
|
|
},
|
|
});
|
|
|
|
const data = {
|
|
patientId,
|
|
surgeryId,
|
|
implantCatalogId,
|
|
currentPressure,
|
|
status,
|
|
implantModel,
|
|
implantManufacturer,
|
|
implantName,
|
|
isPressureAdjustable,
|
|
isAbandoned,
|
|
shuntMode,
|
|
proximalPunctureAreas,
|
|
valvePlacementSites,
|
|
distalShuntDirection,
|
|
initialPressure,
|
|
implantNotes,
|
|
labelImageUrl,
|
|
};
|
|
|
|
if (existing) {
|
|
return prisma.device.update({
|
|
where: { id: existing.id },
|
|
data,
|
|
});
|
|
}
|
|
|
|
return prisma.device.create({ data });
|
|
}
|
|
|
|
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 upsertUserByScope({
|
|
name: 'Seed System Admin',
|
|
phone: '13800001000',
|
|
passwordHash: seedPasswordHash,
|
|
openId: 'seed-system-admin-openid',
|
|
role: Role.SYSTEM_ADMIN,
|
|
hospitalId: null,
|
|
departmentId: null,
|
|
groupId: null,
|
|
});
|
|
|
|
const hospitalAdminA = await upsertUserByScope({
|
|
name: 'Seed Hospital Admin A',
|
|
phone: '13800001001',
|
|
passwordHash: seedPasswordHash,
|
|
openId: 'seed-hospital-admin-a-openid',
|
|
role: Role.HOSPITAL_ADMIN,
|
|
hospitalId: hospitalA.id,
|
|
departmentId: null,
|
|
groupId: null,
|
|
});
|
|
|
|
await upsertUserByScope({
|
|
name: 'Seed Hospital Admin B',
|
|
phone: '13800001101',
|
|
passwordHash: seedPasswordHash,
|
|
openId: 'seed-hospital-admin-b-openid',
|
|
role: Role.HOSPITAL_ADMIN,
|
|
hospitalId: hospitalB.id,
|
|
departmentId: null,
|
|
groupId: null,
|
|
});
|
|
|
|
const directorA = await upsertUserByScope({
|
|
name: 'Seed Director A',
|
|
phone: '13800001002',
|
|
passwordHash: seedPasswordHash,
|
|
openId: 'seed-director-a-openid',
|
|
role: Role.DIRECTOR,
|
|
hospitalId: hospitalA.id,
|
|
departmentId: departmentA1.id,
|
|
groupId: null,
|
|
});
|
|
|
|
const leaderA = await upsertUserByScope({
|
|
name: 'Seed Leader A',
|
|
phone: '13800001003',
|
|
passwordHash: seedPasswordHash,
|
|
openId: 'seed-leader-a-openid',
|
|
role: Role.LEADER,
|
|
hospitalId: hospitalA.id,
|
|
departmentId: departmentA1.id,
|
|
groupId: groupA1.id,
|
|
});
|
|
|
|
const doctorA = await upsertUserByScope({
|
|
name: 'Seed Doctor A',
|
|
phone: '13800001004',
|
|
passwordHash: seedPasswordHash,
|
|
openId: 'seed-doctor-a-openid',
|
|
role: Role.DOCTOR,
|
|
hospitalId: hospitalA.id,
|
|
departmentId: departmentA1.id,
|
|
groupId: groupA1.id,
|
|
});
|
|
|
|
const doctorA2 = await upsertUserByScope({
|
|
name: 'Seed Doctor A2',
|
|
phone: '13800001204',
|
|
passwordHash: seedPasswordHash,
|
|
openId: 'seed-doctor-a2-openid',
|
|
role: Role.DOCTOR,
|
|
hospitalId: hospitalA.id,
|
|
departmentId: departmentA1.id,
|
|
groupId: groupA1.id,
|
|
});
|
|
|
|
const doctorA3 = await upsertUserByScope({
|
|
name: 'Seed Doctor A3',
|
|
phone: '13800001304',
|
|
passwordHash: seedPasswordHash,
|
|
openId: 'seed-doctor-a3-openid',
|
|
role: Role.DOCTOR,
|
|
hospitalId: hospitalA.id,
|
|
departmentId: departmentA2.id,
|
|
groupId: groupA2.id,
|
|
});
|
|
|
|
const doctorB = await upsertUserByScope({
|
|
name: 'Seed Doctor B',
|
|
phone: '13800001104',
|
|
passwordHash: seedPasswordHash,
|
|
openId: 'seed-doctor-b-openid',
|
|
role: Role.DOCTOR,
|
|
hospitalId: hospitalB.id,
|
|
departmentId: departmentB1.id,
|
|
groupId: groupB1.id,
|
|
});
|
|
|
|
const engineerA = await upsertUserByScope({
|
|
name: 'Seed Engineer A',
|
|
phone: '13800001005',
|
|
passwordHash: seedPasswordHash,
|
|
openId: 'seed-engineer-a-openid',
|
|
role: Role.ENGINEER,
|
|
hospitalId: hospitalA.id,
|
|
departmentId: null,
|
|
groupId: null,
|
|
});
|
|
|
|
const engineerB = await upsertUserByScope({
|
|
name: 'Seed Engineer B',
|
|
phone: '13800001105',
|
|
passwordHash: seedPasswordHash,
|
|
openId: 'seed-engineer-b-openid',
|
|
role: Role.ENGINEER,
|
|
hospitalId: hospitalB.id,
|
|
departmentId: null,
|
|
groupId: null,
|
|
});
|
|
|
|
const dictionarySeeds = {
|
|
[DictionaryType.PRIMARY_DISEASE]: [
|
|
'先天性脑积水',
|
|
'梗阻性脑积水',
|
|
'交通性脑积水',
|
|
'出血后脑积水',
|
|
'肿瘤相关脑积水',
|
|
'外伤后脑积水',
|
|
'感染后脑积水',
|
|
'分流功能障碍',
|
|
],
|
|
[DictionaryType.HYDROCEPHALUS_TYPE]: [
|
|
'交通性',
|
|
'梗阻性',
|
|
'高压性',
|
|
'正常压力',
|
|
'先天性',
|
|
'继发性',
|
|
],
|
|
[DictionaryType.SHUNT_MODE]: ['VPS', 'VPLS', 'LPS', '脑室心房分流'],
|
|
[DictionaryType.PROXIMAL_PUNCTURE_AREA]: [
|
|
'额角',
|
|
'枕角',
|
|
'三角区',
|
|
'腰穿',
|
|
'后角',
|
|
],
|
|
[DictionaryType.VALVE_PLACEMENT_SITE]: [
|
|
'耳后',
|
|
'胸前',
|
|
'锁骨下',
|
|
'腹壁',
|
|
'腰背部',
|
|
],
|
|
[DictionaryType.DISTAL_SHUNT_DIRECTION]: ['腹腔', '胸腔', '心房', '腰大池'],
|
|
};
|
|
|
|
await Promise.all(
|
|
Object.entries(dictionarySeeds).flatMap(([type, labels]) =>
|
|
labels.map((label, index) =>
|
|
ensureDictionaryItem({
|
|
type,
|
|
label,
|
|
sortOrder: index * 10,
|
|
}),
|
|
),
|
|
),
|
|
);
|
|
|
|
const patientA1 = await ensurePatient({
|
|
hospitalId: hospitalA.id,
|
|
doctorId: doctorA.id,
|
|
creatorId: doctorA.id,
|
|
name: 'Seed Patient A1',
|
|
inpatientNo: 'ZYH-A-0001',
|
|
projectName: '脑积水随访项目-A',
|
|
phone: '13800002001',
|
|
idCard: '110101199001010011',
|
|
});
|
|
|
|
const patientA2 = await ensurePatient({
|
|
hospitalId: hospitalA.id,
|
|
doctorId: doctorA2.id,
|
|
creatorId: doctorA2.id,
|
|
name: 'Seed Patient A2',
|
|
inpatientNo: 'ZYH-A-0002',
|
|
projectName: '脑积水随访项目-A',
|
|
phone: '13800002002',
|
|
idCard: '110101199002020022',
|
|
});
|
|
|
|
const patientA3 = await ensurePatient({
|
|
hospitalId: hospitalA.id,
|
|
doctorId: doctorA3.id,
|
|
creatorId: doctorA3.id,
|
|
name: 'Seed Patient A3',
|
|
inpatientNo: 'ZYH-A-0003',
|
|
projectName: '脑积水随访项目-A',
|
|
phone: '13800002003',
|
|
idCard: '110101199003030033',
|
|
});
|
|
|
|
const patientB1 = await ensurePatient({
|
|
hospitalId: hospitalB.id,
|
|
doctorId: doctorB.id,
|
|
creatorId: doctorB.id,
|
|
name: 'Seed Patient B1',
|
|
inpatientNo: 'ZYH-B-0001',
|
|
projectName: '脑积水随访项目-B',
|
|
phone: '13800002001',
|
|
idCard: '110101199001010011',
|
|
});
|
|
|
|
await ensureFamilyMiniAppAccount({
|
|
phone: patientA2.phone,
|
|
openId: 'seed-family-a2-openid',
|
|
});
|
|
|
|
const adjustableCatalog = await ensureImplantCatalog({
|
|
modelCode: 'SEED-ADJUSTABLE-VALVE',
|
|
manufacturer: 'Seed MedTech',
|
|
name: 'Seed 可调压分流阀',
|
|
pressureLevels: [80, 100, 120, 140, 160],
|
|
isPressureAdjustable: true,
|
|
notes: 'Seed 全局可调压目录样例',
|
|
});
|
|
|
|
const fixedCatalog = await ensureImplantCatalog({
|
|
modelCode: 'SEED-FIXED-VALVE',
|
|
manufacturer: 'Seed MedTech',
|
|
name: 'Seed 固定压分流阀',
|
|
pressureLevels: [],
|
|
isPressureAdjustable: false,
|
|
notes: 'Seed 固定压目录样例',
|
|
});
|
|
|
|
const surgeryA1Old = await ensurePatientSurgery({
|
|
patientId: patientA1.id,
|
|
surgeryDate: '2024-06-01T08:00:00.000Z',
|
|
surgeryName: '首次脑室腹腔分流术',
|
|
surgeonName: 'Seed Director A',
|
|
preOpPressure: 24,
|
|
primaryDisease: '先天性脑积水',
|
|
hydrocephalusTypes: ['交通性'],
|
|
notes: '首台手术',
|
|
});
|
|
|
|
const surgeryA1New = await ensurePatientSurgery({
|
|
patientId: patientA1.id,
|
|
surgeryDate: '2025-09-10T08:00:00.000Z',
|
|
surgeryName: '分流系统翻修术',
|
|
surgeonName: 'Seed Director A',
|
|
preOpPressure: 18,
|
|
primaryDisease: '分流功能障碍',
|
|
hydrocephalusTypes: ['交通性', '高压性'],
|
|
previousShuntSurgeryDate: '2024-06-01T08:00:00.000Z',
|
|
preOpMaterials: [
|
|
{
|
|
type: 'IMAGE',
|
|
url: 'https://seed.example.com/a1-ct-preop.png',
|
|
name: 'Seed A1 术前 CT',
|
|
},
|
|
],
|
|
notes: '二次手术,保留原设备历史',
|
|
});
|
|
|
|
const surgeryA2 = await ensurePatientSurgery({
|
|
patientId: patientA2.id,
|
|
surgeryDate: '2025-12-15T08:00:00.000Z',
|
|
surgeryName: '脑室腹腔分流术',
|
|
surgeonName: 'Seed Doctor A2',
|
|
preOpPressure: 20,
|
|
primaryDisease: '肿瘤相关脑积水',
|
|
hydrocephalusTypes: ['梗阻性'],
|
|
});
|
|
|
|
const surgeryA3 = await ensurePatientSurgery({
|
|
patientId: patientA3.id,
|
|
surgeryDate: '2025-11-20T08:00:00.000Z',
|
|
surgeryName: '脑室腹腔分流术',
|
|
surgeonName: 'Seed Doctor A3',
|
|
preOpPressure: 21,
|
|
primaryDisease: '外伤后脑积水',
|
|
hydrocephalusTypes: ['交通性'],
|
|
});
|
|
|
|
const surgeryB1 = await ensurePatientSurgery({
|
|
patientId: patientB1.id,
|
|
surgeryDate: '2025-10-05T08:00:00.000Z',
|
|
surgeryName: '脑室腹腔分流术',
|
|
surgeonName: 'Seed Doctor B',
|
|
preOpPressure: 23,
|
|
primaryDisease: '出血后脑积水',
|
|
hydrocephalusTypes: ['高压性'],
|
|
});
|
|
|
|
const deviceA1 = await ensureDevice({
|
|
patientId: patientA1.id,
|
|
surgeryId: surgeryA1New.id,
|
|
implantCatalogId: adjustableCatalog.id,
|
|
currentPressure: 118,
|
|
status: DeviceStatus.ACTIVE,
|
|
implantModel: adjustableCatalog.modelCode,
|
|
implantManufacturer: adjustableCatalog.manufacturer,
|
|
implantName: adjustableCatalog.name,
|
|
isPressureAdjustable: adjustableCatalog.isPressureAdjustable,
|
|
isAbandoned: false,
|
|
shuntMode: 'VPS',
|
|
proximalPunctureAreas: ['额角'],
|
|
valvePlacementSites: ['耳后'],
|
|
distalShuntDirection: '腹腔',
|
|
initialPressure: 118,
|
|
implantNotes: 'Seed A1 当前在用设备',
|
|
labelImageUrl: 'https://seed.example.com/labels/a1-001.jpg',
|
|
});
|
|
|
|
const deviceA2 = await ensureDevice({
|
|
patientId: patientA2.id,
|
|
surgeryId: surgeryA2.id,
|
|
implantCatalogId: adjustableCatalog.id,
|
|
currentPressure: 112,
|
|
status: DeviceStatus.ACTIVE,
|
|
implantModel: adjustableCatalog.modelCode,
|
|
implantManufacturer: adjustableCatalog.manufacturer,
|
|
implantName: adjustableCatalog.name,
|
|
isPressureAdjustable: adjustableCatalog.isPressureAdjustable,
|
|
isAbandoned: false,
|
|
shuntMode: 'VPS',
|
|
proximalPunctureAreas: ['枕角'],
|
|
valvePlacementSites: ['胸前'],
|
|
distalShuntDirection: '腹腔',
|
|
initialPressure: 112,
|
|
implantNotes: 'Seed A2 当前在用设备',
|
|
labelImageUrl: 'https://seed.example.com/labels/a2-002.jpg',
|
|
});
|
|
|
|
await ensureDevice({
|
|
patientId: patientA3.id,
|
|
surgeryId: surgeryA3.id,
|
|
implantCatalogId: adjustableCatalog.id,
|
|
currentPressure: 109,
|
|
status: DeviceStatus.ACTIVE,
|
|
implantModel: adjustableCatalog.modelCode,
|
|
implantManufacturer: adjustableCatalog.manufacturer,
|
|
implantName: adjustableCatalog.name,
|
|
isPressureAdjustable: adjustableCatalog.isPressureAdjustable,
|
|
isAbandoned: false,
|
|
shuntMode: 'LPS',
|
|
proximalPunctureAreas: ['腰穿'],
|
|
valvePlacementSites: ['腰背部'],
|
|
distalShuntDirection: '腹腔',
|
|
initialPressure: 109,
|
|
implantNotes: 'Seed A3 当前在用设备',
|
|
labelImageUrl: 'https://seed.example.com/labels/a3-003.jpg',
|
|
});
|
|
|
|
const deviceB1 = await ensureDevice({
|
|
patientId: patientB1.id,
|
|
surgeryId: surgeryB1.id,
|
|
implantCatalogId: adjustableCatalog.id,
|
|
currentPressure: 121,
|
|
status: DeviceStatus.ACTIVE,
|
|
implantModel: adjustableCatalog.modelCode,
|
|
implantManufacturer: adjustableCatalog.manufacturer,
|
|
implantName: adjustableCatalog.name,
|
|
isPressureAdjustable: adjustableCatalog.isPressureAdjustable,
|
|
isAbandoned: false,
|
|
shuntMode: 'VPS',
|
|
proximalPunctureAreas: ['额角'],
|
|
valvePlacementSites: ['耳后'],
|
|
distalShuntDirection: '腹腔',
|
|
initialPressure: 121,
|
|
implantNotes: 'Seed B1 当前在用设备',
|
|
labelImageUrl: 'https://seed.example.com/labels/b1-001.jpg',
|
|
});
|
|
|
|
await ensureDevice({
|
|
patientId: patientA1.id,
|
|
surgeryId: surgeryA1Old.id,
|
|
implantCatalogId: adjustableCatalog.id,
|
|
currentPressure: 130,
|
|
status: DeviceStatus.INACTIVE,
|
|
implantModel: adjustableCatalog.modelCode,
|
|
implantManufacturer: adjustableCatalog.manufacturer,
|
|
implantName: adjustableCatalog.name,
|
|
isPressureAdjustable: adjustableCatalog.isPressureAdjustable,
|
|
isAbandoned: true,
|
|
shuntMode: 'VPS',
|
|
proximalPunctureAreas: ['额角'],
|
|
valvePlacementSites: ['耳后'],
|
|
distalShuntDirection: '腹腔',
|
|
initialPressure: 130,
|
|
implantNotes: 'Seed A1 弃用历史设备',
|
|
labelImageUrl: 'https://seed.example.com/labels/a1-004.jpg',
|
|
});
|
|
|
|
// 清理与种子设备关联的历史任务,保证 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();
|
|
});
|