tyt-api-nest/prisma/schema.prisma
EL 7c4ba1e1a0 feat(auth): 支持同一微信 openId 绑定多个院内账号
feat(patients): 增强 B 端患者列表返回原发病/压力/手术日期字段
2026-03-24 16:51:37 +08:00

308 lines
10 KiB
Plaintext
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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])
}