generator client { provider = "prisma-client" output = "../src/generated/prisma" } // 兼容 seed 脚本在 Node.js 直接运行时使用 @prisma/client runtime。 generator seed_client { provider = "prisma-client-js" } datasource db { provider = "postgresql" } // 角色枚举:用于鉴权与数据可见性控制。 enum Role { SYSTEM_ADMIN HOSPITAL_ADMIN DIRECTOR LEADER DOCTOR ENGINEER } // 设备状态枚举:表示设备是否处于使用中。 enum DeviceStatus { ACTIVE INACTIVE } // 任务状态枚举:定义任务流转状态机。 enum TaskStatus { PENDING ACCEPTED COMPLETED CANCELLED } // 医学字典类型:驱动患者手术表单中的单选/多选项。 enum DictionaryType { PRIMARY_DISEASE HYDROCEPHALUS_TYPE SHUNT_MODE PROXIMAL_PUNCTURE_AREA VALVE_PLACEMENT_SITE DISTAL_SHUNT_DIRECTION } // 上传资产类型:用于图库/视频库分类。 enum UploadAssetType { IMAGE VIDEO FILE } // 医院主表:多租户顶层实体。 model Hospital { id Int @id @default(autoincrement()) name String departments Department[] users User[] patients Patient[] tasks Task[] uploads UploadAsset[] } // 科室表:归属于医院。 model Department { id Int @id @default(autoincrement()) name String hospitalId Int hospital Hospital @relation(fields: [hospitalId], references: [id]) groups Group[] users User[] @@index([hospitalId]) } // 小组表:归属于科室。 model Group { id Int @id @default(autoincrement()) name String departmentId Int department Department @relation(fields: [departmentId], references: [id]) users User[] @@index([departmentId]) } // 用户表:支持后台密码登录与小程序 openId。 // 同一个微信 openId 允许绑定多个院内账号,便于多角色/多院区切换。 model User { id Int @id @default(autoincrement()) name String phone String // 后台登录密码哈希(bcrypt)。 passwordHash String? // 该时间点之前签发的 token 一律失效。 tokenValidAfter DateTime @default(now()) openId String? role Role hospitalId Int? departmentId Int? groupId Int? hospital Hospital? @relation(fields: [hospitalId], references: [id]) department Department? @relation(fields: [departmentId], references: [id]) // 小组删除必须先清理成员,避免静默把用户 groupId 置空。 group Group? @relation(fields: [groupId], references: [id], onDelete: Restrict) doctorPatients Patient[] @relation("DoctorPatients") createdPatients Patient[] @relation("PatientCreator") createdTasks Task[] @relation("TaskCreator") acceptedTasks Task[] @relation("TaskEngineer") surgeonSurgeries PatientSurgery[] @relation("SurgerySurgeon") createdUploads UploadAsset[] @relation("UploadCreator") @@unique([phone, role, hospitalId]) @@index([phone]) @@index([openId]) @@index([hospitalId, role]) @@index([departmentId, role]) @@index([groupId, role]) } // 家属小程序账号:按手机号承载 C 端登录身份,并预留服务号绑定字段。 model FamilyMiniAppAccount { id Int @id @default(autoincrement()) phone String @unique openId String? @unique serviceUid String? @unique lastLoginAt DateTime @default(now()) createdAt DateTime @default(now()) updatedAt DateTime @default(now()) @updatedAt @@index([lastLoginAt]) } // 患者表:院内患者档案,按医院隔离。 model Patient { id Int @id @default(autoincrement()) name String createdAt DateTime @default(now()) // 住院号:用于院内患者检索与病案关联。 inpatientNo String? // 项目名称:用于区分患者所属项目/课题。 projectName String? phone String // 患者身份证号,录入与查询都使用原始证件号。 idCard String hospitalId Int doctorId Int creatorId Int hospital Hospital @relation(fields: [hospitalId], references: [id]) doctor User @relation("DoctorPatients", fields: [doctorId], references: [id]) creator User @relation("PatientCreator", fields: [creatorId], references: [id]) surgeries PatientSurgery[] devices Device[] @@index([phone, idCard]) @@index([hospitalId, doctorId]) @@index([inpatientNo]) @@index([creatorId]) } // 患者手术表:保存每次分流/复手术档案。 model PatientSurgery { id Int @id @default(autoincrement()) patientId Int surgeryDate DateTime surgeryName String surgeonId Int? surgeonName String // 术前测压:部分患者可为空。 preOpPressure Int? // 原发病:前端单选,后端先按字符串存储,方便后续补字典。 primaryDisease String // 脑积水类型:前端多选。 hydrocephalusTypes String[] @default([]) // 上次分流手术时间:无既往分流史时为空。 previousShuntSurgeryDate DateTime? // 术前影像/资料:支持图片、视频等附件元数据。 preOpMaterials Json? notes String? createdAt DateTime @default(now()) patient Patient @relation(fields: [patientId], references: [id], onDelete: Cascade) surgeon User? @relation("SurgerySurgeon", fields: [surgeonId], references: [id], onDelete: SetNull) devices Device[] @@index([patientId, surgeryDate]) @@index([surgeonId]) } // 植入物型号字典:供前端单选型号后自动回填厂家与名称。 model ImplantCatalog { id Int @id @default(autoincrement()) modelCode String @unique manufacturer String name String // 是否为阀门;关闭时表示管子/附件,不提供压力挡位。 isValve Boolean @default(true) // 可调压器械的可选挡位,由系统管理员维护。 pressureLevels String[] @default([]) isPressureAdjustable Boolean @default(true) notes String? createdAt DateTime @default(now()) updatedAt DateTime @default(now()) @updatedAt devices Device[] } // 系统级字典项:由系统管理员维护,供患者手术表单选择使用。 model DictionaryItem { id Int @id @default(autoincrement()) type DictionaryType label String sortOrder Int @default(0) enabled Boolean @default(true) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt @@unique([type, label]) @@index([type, enabled, sortOrder]) } // 上传资产表:保存图片/视频/文件元数据,供图库与患者表单复用。 model UploadAsset { id Int @id @default(autoincrement()) hospitalId Int creatorId Int type UploadAssetType originalName String fileName String storagePath String @unique url String mimeType String fileSize Int createdAt DateTime @default(now()) hospital Hospital @relation(fields: [hospitalId], references: [id]) creator User @relation("UploadCreator", fields: [creatorId], references: [id]) @@index([hospitalId, type, createdAt]) @@index([creatorId, createdAt]) } // 设备表:每次手术植入的设备实例,保留当前压力与历史调压记录。 model Device { id Int @id @default(autoincrement()) currentPressure String status DeviceStatus @default(ACTIVE) patientId Int surgeryId Int? implantCatalogId Int? // 植入物快照:避免型号字典修改后影响历史病历。 implantModel String? implantManufacturer String? implantName String? isValve Boolean @default(true) isPressureAdjustable Boolean @default(true) // 二次手术后旧设备可标记弃用,但历史调压任务仍需保留。 isAbandoned Boolean @default(false) shuntMode String? proximalPunctureAreas String[] @default([]) valvePlacementSites String[] @default([]) distalShuntDirection String? initialPressure String? implantNotes String? labelImageUrl String? patient Patient @relation(fields: [patientId], references: [id]) surgery PatientSurgery? @relation(fields: [surgeryId], references: [id], onDelete: SetNull) implantCatalog ImplantCatalog? @relation(fields: [implantCatalogId], references: [id], onDelete: SetNull) taskItems TaskItem[] @@index([patientId, status]) @@index([surgeryId]) @@index([implantCatalogId]) @@index([patientId, isAbandoned]) } // 主任务表:记录调压任务主单。 model Task { id Int @id @default(autoincrement()) status TaskStatus @default(PENDING) creatorId Int engineerId Int? hospitalId Int createdAt DateTime @default(now()) // 工程师完成任务时上传的图片/视频凭证。 completionMaterials Json? creator User @relation("TaskCreator", fields: [creatorId], references: [id]) engineer User? @relation("TaskEngineer", fields: [engineerId], references: [id]) hospital Hospital @relation(fields: [hospitalId], references: [id]) items TaskItem[] @@index([hospitalId, status, createdAt]) } // 任务明细表:一个任务可包含多个设备调压项。 model TaskItem { id Int @id @default(autoincrement()) taskId Int deviceId Int oldPressure String targetPressure String task Task @relation(fields: [taskId], references: [id], onDelete: Cascade) device Device @relation(fields: [deviceId], references: [id]) @@index([taskId]) @@index([deviceId]) }