683 lines
18 KiB
Plaintext
683 lines
18 KiB
Plaintext
generator client {
|
|
provider = "prisma-client"
|
|
output = "../src/generated/prisma"
|
|
}
|
|
|
|
datasource db {
|
|
provider = "postgresql"
|
|
}
|
|
|
|
enum CaseStatus {
|
|
NEW
|
|
CONTACTED
|
|
DOCS_PENDING
|
|
REVIEWING
|
|
PRE_APPROVED
|
|
REJECTED
|
|
CLOSED
|
|
CONVERTED_TO_HIS
|
|
}
|
|
|
|
// enum DocumentType {
|
|
// PASSPORT
|
|
// MEDICAL_RECORD
|
|
// IMAGING
|
|
// LAB_RESULT
|
|
// OTHER
|
|
// }
|
|
|
|
enum InteractionType {
|
|
PHONE
|
|
WHATSAPP
|
|
EMAIL
|
|
SYSTEM
|
|
}
|
|
|
|
model Patient {
|
|
id String @id @default(uuid())
|
|
|
|
pid String @unique @db.VarChar(10)
|
|
// -------- Identity --------
|
|
firstName String @db.VarChar(100)
|
|
lastName String @db.VarChar(100)
|
|
|
|
birthDate DateTime?
|
|
sex Sex?
|
|
age Int?
|
|
// -------- Nationality --------
|
|
nationality Countries? @relation(fields: [nationalityId], references: [id])
|
|
nationalityId Int?
|
|
nationalityCode String? @db.Char(3)
|
|
|
|
passportCode String? @db.VarChar(20)
|
|
|
|
// -------- Contact --------
|
|
phone String? @db.VarChar(20)
|
|
email String? @db.VarChar(255)
|
|
preferredLanguage String? @db.VarChar(10)
|
|
|
|
// -------- Address --------
|
|
address String? @db.VarChar(500)
|
|
postalCode String? @db.VarChar(20)
|
|
|
|
// -------- System --------
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
deletedAt DateTime?
|
|
|
|
// -------- Relations --------
|
|
cases OnlineCase[]
|
|
documents Document[]
|
|
|
|
// -------- Indexes & Constraints --------
|
|
@@unique([nationalityCode]) // کد ملی
|
|
@@unique([passportCode, nationalityId]) // پاسپورت
|
|
@@unique([phone])
|
|
@@unique([email])
|
|
@@unique([firstName, lastName, birthDate])
|
|
@@index([pid])
|
|
}
|
|
|
|
model Document {
|
|
id String @id @default(uuid())
|
|
caseId String?
|
|
patientId String?
|
|
uploadedById String?
|
|
|
|
type DocumentType
|
|
filename String
|
|
fileUrl String
|
|
fileKey String
|
|
mimeType String?
|
|
size Int?
|
|
checksum String?
|
|
status DocStatus @default(NEW)
|
|
|
|
is_deleted Boolean @default(false)
|
|
createdAt DateTime @default(now())
|
|
|
|
case OnlineCase? @relation(fields: [caseId], references: [id])
|
|
patient Patient? @relation(fields: [patientId], references: [id])
|
|
uploadedBy Staff? @relation(fields: [uploadedById], references: [id])
|
|
}
|
|
|
|
enum DocumentType {
|
|
MEDICAL_IMAGE
|
|
LAB_RESULT
|
|
PRESCRIPTION
|
|
PASSPORT
|
|
NATIONAL_ID
|
|
OTHER
|
|
OTHER_FILE
|
|
}
|
|
|
|
enum DocStatus {
|
|
NEW
|
|
PENDING_SCAN
|
|
SAFE
|
|
INFECTED
|
|
INVALID
|
|
}
|
|
|
|
enum Sex {
|
|
male
|
|
female
|
|
other
|
|
}
|
|
|
|
model OnlineCase {
|
|
id String @id @default(uuid())
|
|
patientId String
|
|
trackingCode String @unique
|
|
|
|
message String?
|
|
specialty String?
|
|
formData Json?
|
|
|
|
status CaseStatus @default(NEW)
|
|
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
patient Patient @relation(fields: [patientId], references: [id])
|
|
documents Document[]
|
|
// interactions Interaction[]
|
|
reviews Review[]
|
|
statusHistory CaseStatusHistory[]
|
|
}
|
|
|
|
model Review {
|
|
id String @id @default(uuid())
|
|
caseId String
|
|
doctorId String?
|
|
|
|
createdAt DateTime @default(now())
|
|
deletedAt DateTime?
|
|
|
|
case OnlineCase @relation(fields: [caseId], references: [id])
|
|
doctor Staff? @relation(fields: [doctorId], references: [id])
|
|
|
|
translations ReviewTranslation[]
|
|
}
|
|
|
|
model ReviewTranslation {
|
|
id Int @id @default(autoincrement())
|
|
reviewId String
|
|
lang Language? @relation(fields: [lang_id], references: [id])
|
|
lang_id Int?
|
|
|
|
note String?
|
|
result String? // eligible, needs more docs, not eligible (میتونه ترجمه شود)
|
|
|
|
review Review @relation(fields: [reviewId], references: [id])
|
|
|
|
@@unique([reviewId, lang_id]) // هر Review در هر زبان فقط یک ترجمه دارد
|
|
}
|
|
|
|
model CaseStatusHistory {
|
|
id String @id @default(uuid())
|
|
caseId String
|
|
from CaseStatus?
|
|
to CaseStatus?
|
|
changedBy String?
|
|
|
|
createdAt DateTime @default(now())
|
|
|
|
case OnlineCase @relation(fields: [caseId], references: [id])
|
|
|
|
@@index([id, caseId])
|
|
}
|
|
|
|
model UploadSession {
|
|
id String @id @default(uuid())
|
|
uploadKey String @unique // path/key to store file on CDN (e.g. documents/{uuid}.pdf)
|
|
nonce String @unique
|
|
used Boolean @default(false)
|
|
createdById String? // user id
|
|
purpose String // "document" | "image"
|
|
allowedTypes String? // JSON string array
|
|
maxSize Int?
|
|
status String @default("PENDING")
|
|
createdAt DateTime @default(now())
|
|
expiresAt DateTime
|
|
|
|
// indexes
|
|
@@index([createdById])
|
|
@@index([status])
|
|
}
|
|
|
|
enum UploadStatus {
|
|
PENDING
|
|
UPLOADING
|
|
UPLOADED
|
|
VERIFIED
|
|
FAILED
|
|
EXPIRED
|
|
}
|
|
|
|
enum StaffRoles {
|
|
developer
|
|
admin
|
|
doctor
|
|
coordinator
|
|
}
|
|
|
|
model Staff {
|
|
id String @id @default(uuid())
|
|
username String @unique
|
|
password String
|
|
email String? @unique
|
|
role StaffRoles
|
|
|
|
is_verified Boolean @default(false)
|
|
send_notif_with_email Boolean @default(false)
|
|
resetPasswordToken String?
|
|
resetPasswordExpires DateTime?
|
|
status StaffStatus @default(ACTIVE)
|
|
trustScore Int @default(100)
|
|
strikes Int @default(0)
|
|
|
|
restrictions Restriction[]
|
|
violations Violation[]
|
|
documents Document[]
|
|
reviews Review[]
|
|
|
|
profilePicture Image? @relation(fields: [profilePictureID],references: [id])
|
|
profilePictureID Int?
|
|
|
|
translations StaffTranslation[]
|
|
tokens RefreshToken[]
|
|
}
|
|
|
|
enum StaffStatus {
|
|
ACTIVE
|
|
WARNED
|
|
RESTRICTED
|
|
BANNED
|
|
}
|
|
|
|
model Violation {
|
|
id String @id @default(uuid())
|
|
userId String
|
|
type ViolationType
|
|
severity Int
|
|
reason String
|
|
|
|
user Staff @relation(fields: [userId], references: [id])
|
|
createdAt DateTime @default(now())
|
|
}
|
|
|
|
enum ViolationType {
|
|
SPAM
|
|
RATE_LIMIT
|
|
CONTENT
|
|
ABUSE
|
|
}
|
|
|
|
model Restriction {
|
|
id String @id @default(uuid())
|
|
userId String
|
|
type RestrictionType
|
|
expiresAt DateTime?
|
|
|
|
user Staff @relation(fields: [userId], references: [id])
|
|
}
|
|
|
|
enum RestrictionType {
|
|
TEMP
|
|
SHADOW
|
|
PERMANENT
|
|
}
|
|
|
|
model StaffTranslation {
|
|
id Int @id @default(autoincrement())
|
|
staffId String
|
|
lang Language? @relation(fields: [lang_id], references: [id])
|
|
lang_id Int?
|
|
|
|
// فیلدهایی که نیاز به ترجمه دارند
|
|
displayName String?
|
|
position String?
|
|
description String?
|
|
|
|
staff Staff @relation(fields: [staffId], references: [id])
|
|
|
|
@@unique([staffId, lang_id]) // هر کارمند در هر زبان فقط یک ترجمه دارد
|
|
}
|
|
|
|
model Image {
|
|
id Int @id @default(autoincrement())
|
|
fileKey String
|
|
filename String?
|
|
fileUrl String?
|
|
mimeType String?
|
|
size Int?
|
|
usersProfile Users[]
|
|
countriesCover Countries[]
|
|
medicalPackagesThumbnails MedicalPackage[]
|
|
transferPackageLocationImages TransferPackage[] @relation(name: "TransferPackageLocationImages")
|
|
transferPackageGalleryImages TransferPackage[] @relation(name: "TransferPackageGalleryImages")
|
|
staffProfilePictures Staff[]
|
|
}
|
|
|
|
model Users {
|
|
id Int @id @default(autoincrement())
|
|
slug String @unique
|
|
type UsersType @default(DOCTOR)
|
|
|
|
displayInMainPage Boolean @default(false)
|
|
phone String?
|
|
email String?
|
|
teamName String?
|
|
medicalNumber String?
|
|
expertise Expertise? @relation(fields: [expertiseId], references: [id])
|
|
expertiseId Int?
|
|
image Image? @relation(fields: [imageId], references: [id])
|
|
imageId Int?
|
|
|
|
createdAt DateTime @default(now())
|
|
|
|
translations UserTranslation[]
|
|
transerTeamMembers TransferTeam[]
|
|
|
|
@@index([id, slug])
|
|
}
|
|
|
|
model UserTranslation {
|
|
id Int @id @default(autoincrement())
|
|
userId Int
|
|
lang Language? @relation(fields: [lang_id], references: [id])
|
|
lang_id Int?
|
|
firstName String?
|
|
lastName String?
|
|
position String?
|
|
bio String?
|
|
excerpt String?
|
|
user Users @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
|
|
@@unique([userId, lang_id]) // هر کاربر در هر زبان فقط یک ترجمه دارد
|
|
}
|
|
|
|
model Expertise {
|
|
id Int @id @default(autoincrement())
|
|
slug String @db.VarChar(100)
|
|
users Users[]
|
|
translations ExpertiseTranslation[]
|
|
}
|
|
|
|
model ExpertiseTranslation {
|
|
id Int @id @default(autoincrement())
|
|
expertiseId Int
|
|
lang Language? @relation(fields: [lang_id], references: [id])
|
|
lang_id Int?
|
|
displayName String?
|
|
level EducationLevel?
|
|
expertise Expertise @relation(fields: [expertiseId], references: [id])
|
|
|
|
@@unique([expertiseId, lang_id]) // هر کاربر در هر زبان فقط یک ترجمه دارد
|
|
}
|
|
enum EducationLevel {
|
|
GP
|
|
SPECIALIST
|
|
SUBSPECIALIST
|
|
FELLOWSHIP
|
|
}
|
|
enum UsersType {
|
|
DOCTOR
|
|
TRANSFER_TEAM
|
|
DEPARTMENT
|
|
}
|
|
|
|
model PanelConfig {
|
|
id String @id @default(uuid())
|
|
key String @unique
|
|
value String // مقدار خام (string)
|
|
type ConfigType // نوع داده
|
|
description String?
|
|
updatedAt DateTime @updatedAt
|
|
createdAt DateTime @default(now())
|
|
}
|
|
|
|
model Default {
|
|
id String @id @default("SITE_DEFAULTS")
|
|
|
|
email String
|
|
hospitalPhone String
|
|
logoUrl String?
|
|
mapAddress String
|
|
instagramLink String?
|
|
linkedinLink String?
|
|
ipdNumber String?
|
|
updatedAt DateTime @updatedAt
|
|
|
|
translations DefaultTranslation[]
|
|
}
|
|
|
|
model DefaultTranslation {
|
|
id Int @id @default(autoincrement())
|
|
default Default @relation(fields: [defaultId], references: [id])
|
|
defaultId String
|
|
language Language @relation(fields: [languageId], references: [id])
|
|
languageId Int
|
|
|
|
address String
|
|
underLogoText String?
|
|
aboutUsText String?
|
|
patientsRights String?
|
|
@@unique([defaultId, languageId])
|
|
}
|
|
|
|
model Statics {
|
|
id String @id @default(uuid())
|
|
key String @unique @db.VarChar(191)
|
|
group String @db.VarChar(100)
|
|
description String? @db.Text
|
|
|
|
translations StaticsTranslation[]
|
|
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
@@index([group])
|
|
}
|
|
|
|
model StaticsTranslation {
|
|
id String @id @default(uuid())
|
|
|
|
languageId Int
|
|
staticsKeyId String
|
|
|
|
value String @db.Text
|
|
|
|
language Language @relation(fields: [languageId], references: [id], onDelete: Cascade)
|
|
staticsKey Statics @relation(fields: [staticsKeyId], references: [id], onDelete: Cascade)
|
|
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
@@unique([languageId, staticsKeyId])
|
|
@@index([languageId])
|
|
@@index([staticsKeyId])
|
|
}
|
|
|
|
enum ConfigType {
|
|
BOOLEAN
|
|
NUMBER
|
|
STRING
|
|
JSON
|
|
STRING_ARRAY
|
|
}
|
|
|
|
model Page {
|
|
id Int @id @default(autoincrement())
|
|
slug String @unique
|
|
createdAt DateTime @default(now())
|
|
pageBlocks PageBlock[]
|
|
}
|
|
|
|
model PageBlock {
|
|
id Int @id @default(autoincrement())
|
|
pageId Int
|
|
type String // hero, text, image, faq, etc
|
|
sort Int
|
|
|
|
page Page @relation(fields: [pageId], references: [id])
|
|
pageBlockTranslations PageBlockTranslation[]
|
|
}
|
|
|
|
model PageBlockTranslation {
|
|
id Int @id @default(autoincrement())
|
|
blockId Int
|
|
lang Language? @relation(fields: [lang_id], references: [id])
|
|
lang_id Int?
|
|
field String // title, subtitle, description, button_text
|
|
value String
|
|
|
|
block PageBlock @relation(fields: [blockId], references: [id])
|
|
}
|
|
|
|
model Language {
|
|
id Int @id @default(autoincrement())
|
|
title String
|
|
slug String @unique
|
|
|
|
pageBlockTranslations PageBlockTranslation[]
|
|
// translations Translation[]
|
|
reviewTranslations ReviewTranslation[]
|
|
staffTranslations StaffTranslation[]
|
|
userTranslations UserTranslation[]
|
|
expertiseTranslations ExpertiseTranslation[]
|
|
defaultTranslations DefaultTranslation[]
|
|
medicalPackagesTranslations MedicalPackagesTranslation[]
|
|
transferPackageTranslations TransferPackageTranslations[]
|
|
transferTeamTranslations TransferTeamTranslation[]
|
|
staticsTranslations StaticsTranslation[]
|
|
|
|
@@index([id, slug])
|
|
}
|
|
|
|
model RefreshToken {
|
|
id Int @id @default(autoincrement())
|
|
token String @unique
|
|
staff Staff @relation(fields: [staffId], references: [id])
|
|
staffId String
|
|
|
|
expiresAt DateTime
|
|
createdAt DateTime @default(now())
|
|
}
|
|
|
|
model Countries {
|
|
id Int @id @default(autoincrement())
|
|
name String
|
|
callCode String
|
|
cover Image? @relation(fields: [coverId], references: [id])
|
|
coverId Int?
|
|
createdAt DateTime @default(now())
|
|
patients Patient[]
|
|
}
|
|
|
|
enum AuditAction {
|
|
CREATE
|
|
UPDATE
|
|
DELETE
|
|
READ
|
|
}
|
|
|
|
model AuditLog {
|
|
id Int @id @default(autoincrement())
|
|
actorId String?
|
|
actorRole StaffRoles?
|
|
action AuditAction
|
|
entity String
|
|
entityId Int?
|
|
before Json?
|
|
after Json?
|
|
ip String?
|
|
createdAt DateTime @default(now())
|
|
|
|
@@index([entity, entityId])
|
|
}
|
|
|
|
model AccessLog {
|
|
id Int @id @default(autoincrement())
|
|
userId String
|
|
userRole StaffRoles
|
|
resource String
|
|
resourceId Int?
|
|
reason String?
|
|
ip String?
|
|
createdAt DateTime @default(now())
|
|
}
|
|
|
|
model DecisionLog {
|
|
id Int @id @default(autoincrement())
|
|
decisionType String
|
|
input Json
|
|
output Json
|
|
algorithmVersion String
|
|
actorId String?
|
|
createdAt DateTime @default(now())
|
|
}
|
|
|
|
model TermsOfService {
|
|
id String @id @default(uuid()) // شناسه یکتا
|
|
title String // عنوان سند، مثلا "Terms of Service"
|
|
content String // متن کامل TOS (Markdown یا HTML)
|
|
version String @unique // نسخه سند، مثلا "v1.0.0"
|
|
isActive Boolean @default(true) // آیا این نسخه فعال است؟
|
|
createdAt DateTime @default(now()) // زمان ایجاد نسخه
|
|
updatedAt DateTime @updatedAt // زمان آخرین آپدیت
|
|
|
|
/// روابط اختیاری، اگر بخواهید لاگ پذیرش کاربران را نگه دارید
|
|
acceptances TosAcceptanceLog[] // ثبت کاربرانی که این نسخه را پذیرفتهاند
|
|
}
|
|
|
|
model TosAcceptanceLog {
|
|
id Int @id @default(autoincrement())
|
|
userId String // شناسه کاربر
|
|
policyType String // نوع سند (TOS یا Privacy Policy)
|
|
policyVersion String // نسخه سند پذیرفته شده
|
|
acceptedAt DateTime @default(now()) // زمان پذیرش
|
|
ip String? // آیپی کاربر برای ثبت لاگ
|
|
tos TermsOfService? @relation(fields: [policyVersion], references: [version])
|
|
}
|
|
|
|
model PrivacyPolicy {
|
|
id Int @id @default(autoincrement()) // شناسه یکتا
|
|
content String // متن کامل TOS (Markdown یا HTML)
|
|
createdAt DateTime @default(now()) // زمان ایجاد نسخه
|
|
updatedAt DateTime @updatedAt // زمان آخرین آپدیت
|
|
}
|
|
|
|
model MedicalPackage {
|
|
id Int @id @default(autoincrement())
|
|
|
|
thumbnail Image? @relation(fields: [thumbnail_id], references: [id])
|
|
thumbnail_id Int?
|
|
icon String?
|
|
priority Int?
|
|
price String
|
|
parent MedicalPackage? @relation("MedicalPackageHierarchy", fields: [parent_id], references: [id], onDelete: SetNull)
|
|
parent_id Int? // برای اشاره به دستهبندی والد
|
|
children MedicalPackage[] @relation("MedicalPackageHierarchy")
|
|
translations MedicalPackagesTranslation[]
|
|
}
|
|
|
|
model MedicalPackagesTranslation {
|
|
id Int @id @default(autoincrement())
|
|
title String @db.VarChar(50)
|
|
content String?
|
|
lang Language? @relation(fields: [lang_id], references: [id])
|
|
lang_id Int?
|
|
medicalPackage MedicalPackage? @relation(fields: [medicalPackageId], references: [id])
|
|
medicalPackageId Int?
|
|
|
|
@@unique([medicalPackageId, lang_id]) // هر کاربر در هر زبان فقط یک ترجمه دارد
|
|
}
|
|
|
|
model TransferPackage {
|
|
id Int @id @default(autoincrement())
|
|
|
|
location String
|
|
price String
|
|
locationImages Image[] @relation(name: "TransferPackageLocationImages")
|
|
galleryImages Image[] @relation(name: "TransferPackageGalleryImages")
|
|
transferTeam TransferTeam[]
|
|
translations TransferPackageTranslations[]
|
|
}
|
|
|
|
model TransferPackageTranslations {
|
|
id Int @id @default(autoincrement())
|
|
|
|
name String
|
|
content String
|
|
lang Language? @relation(fields: [lang_id], references: [id])
|
|
lang_id Int?
|
|
transferPackage TransferPackage? @relation(fields: [transferPackageId], references: [id])
|
|
transferPackageId Int?
|
|
|
|
@@unique([transferPackageId, lang_id]) // هر کاربر در هر زبان فقط یک ترجمه دارد
|
|
}
|
|
|
|
model TransferTeam {
|
|
id Int @id @default(autoincrement())
|
|
|
|
members Users[]
|
|
packages TransferPackage[]
|
|
translations TransferTeamTranslation[]
|
|
}
|
|
|
|
model TransferTeamTranslation {
|
|
id Int @id @default(autoincrement())
|
|
lang Language? @relation(fields: [lang_id], references: [id])
|
|
lang_id Int?
|
|
transferTeam TransferTeam? @relation(fields: [transferTeamId], references: [id])
|
|
transferTeamId Int?
|
|
name String @db.VarChar(50)
|
|
introduction String
|
|
duties String
|
|
services String
|
|
|
|
@@unique([transferTeamId, lang_id]) // هر کاربر در هر زبان فقط یک ترجمه دارد
|
|
}
|