新增 B 端上传接口与列表接口,统一文件上传和分页查询能力 上传能力支持医院级数据隔离:系统管理员需显式指定医院,院内角色按登录医院自动隔离 图片上传自动压缩并转为 webp,视频上传自动转码并压缩为 mp4,普通文件按原始类型存储 增加上传目录与公开访问能力,统一输出可直接预览的访问地址 前端新增影像库页面,支持按类型筛选、关键字检索、分页浏览、在线预览与原文件访问 前端新增通用上传组件,支持在页面内复用并返回上传结果 管理后台新增影像库菜单与路由,并补充页面级角色权限控制 患者手术相关表单接入上传复用能力,支持术前资料与设备标签上传回填 新增上传模块 e2e 用例,覆盖成功路径、权限矩阵与关键失败场景 补充上传模块文档与安装依赖说明,完善工程内使用说明
288 lines
9.4 KiB
Plaintext
288 lines
9.4 KiB
Plaintext
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。
|
||
model User {
|
||
id Int @id @default(autoincrement())
|
||
name String
|
||
phone String
|
||
// 后台登录密码哈希(bcrypt)。
|
||
passwordHash String?
|
||
// 该时间点之前签发的 token 一律失效。
|
||
tokenValidAfter DateTime @default(now())
|
||
openId String? @unique
|
||
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([hospitalId, role])
|
||
@@index([departmentId, role])
|
||
@@index([groupId, role])
|
||
}
|
||
|
||
// 患者表:院内患者档案,按医院隔离。
|
||
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
|
||
// 可调压器械的可选挡位,由系统管理员维护。
|
||
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?
|
||
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())
|
||
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])
|
||
}
|