- Add React/Vite frontend for login, dashboard, reports, templates, users, settings, AI, speech, and media workflows. - Add NestJS/Prisma/PostgreSQL backend with auth, dashboard stats, reports, templates, users, departments, settings, files, AI, speech, audit logs, and HTML sanitization. - Add Prisma schema, migrations, seed data, persistent app sessions, Docker/Nginx deployment files, and upload volume configuration. - Add Vitest, Playwright, backend integration tests, and project documentation for requirements, design, permissions, API contracts, testing, deployment, security, and progress. - Configure production local fallback switch and remove unused Gemini direct dependency/env wiring.
281 lines
7.9 KiB
Plaintext
281 lines
7.9 KiB
Plaintext
generator client {
|
|
provider = "prisma-client-js"
|
|
}
|
|
|
|
datasource db {
|
|
provider = "postgresql"
|
|
}
|
|
|
|
enum UserRole {
|
|
SUPER
|
|
ADMIN
|
|
DOCTOR
|
|
}
|
|
|
|
enum UserStatus {
|
|
ACTIVE
|
|
INACTIVE
|
|
}
|
|
|
|
enum ReportStatus {
|
|
DRAFT
|
|
COMPLETED
|
|
}
|
|
|
|
enum TemplateScope {
|
|
DEPARTMENT
|
|
PERSONAL
|
|
}
|
|
|
|
enum FileKind {
|
|
SIGNATURE
|
|
TEMPLATE_ASSET
|
|
VIDEO
|
|
FRAME
|
|
REPORT_EXPORT
|
|
}
|
|
|
|
enum ReportMediaKind {
|
|
VIDEO
|
|
FRAME
|
|
}
|
|
|
|
model Tenant {
|
|
id String @id @default(cuid())
|
|
name String
|
|
code String @unique
|
|
departments Department[]
|
|
users User[]
|
|
reports Report[]
|
|
reportMedia ReportMedia[]
|
|
templates Template[]
|
|
files FileResource[]
|
|
auditLogs AuditLog[]
|
|
settings SystemSetting[]
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
}
|
|
|
|
model Department {
|
|
id String @id @default(cuid())
|
|
tenantId String
|
|
tenant Tenant @relation(fields: [tenantId], references: [id], onDelete: Cascade)
|
|
name String
|
|
code String
|
|
users User[]
|
|
reports Report[]
|
|
templates Template[] @relation("TemplateOwnerDepartment")
|
|
permissions TemplateDepartmentPermission[]
|
|
settings SystemSetting[]
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
@@unique([tenantId, code])
|
|
}
|
|
|
|
model User {
|
|
id String @id @default(cuid())
|
|
tenantId String
|
|
tenant Tenant @relation(fields: [tenantId], references: [id], onDelete: Cascade)
|
|
departmentId String
|
|
department Department @relation(fields: [departmentId], references: [id])
|
|
username String
|
|
passwordHash String
|
|
role UserRole
|
|
name String
|
|
status UserStatus @default(ACTIVE)
|
|
phone String?
|
|
email String?
|
|
signatureFileId String?
|
|
reports Report[] @relation("ReportAuthor")
|
|
sessions UserSession[]
|
|
personalTemplates Template[] @relation("PersonalTemplates")
|
|
auditLogs AuditLog[]
|
|
lastLoginAt DateTime?
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
@@unique([tenantId, username])
|
|
@@index([tenantId, departmentId, role])
|
|
}
|
|
|
|
model UserSession {
|
|
id String @id @default(cuid())
|
|
userId String
|
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
tokenHash String @unique
|
|
expiresAt DateTime
|
|
createdAt DateTime @default(now())
|
|
}
|
|
|
|
model AppSession {
|
|
id String @id
|
|
data Json
|
|
expiresAt DateTime
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
@@index([expiresAt])
|
|
}
|
|
|
|
model Template {
|
|
id String @id @default(cuid())
|
|
tenantId String
|
|
tenant Tenant @relation(fields: [tenantId], references: [id], onDelete: Cascade)
|
|
name String
|
|
description String?
|
|
content String
|
|
fields Json @default("[]")
|
|
scope TemplateScope
|
|
ownerDepartmentId String?
|
|
ownerDepartment Department? @relation("TemplateOwnerDepartment", fields: [ownerDepartmentId], references: [id])
|
|
ownerUserId String?
|
|
ownerUser User? @relation("PersonalTemplates", fields: [ownerUserId], references: [id])
|
|
permissions TemplateDepartmentPermission[]
|
|
reports Report[]
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
@@index([tenantId, scope])
|
|
@@index([tenantId, ownerDepartmentId])
|
|
@@index([tenantId, ownerUserId])
|
|
}
|
|
|
|
model TemplateDepartmentPermission {
|
|
id String @id @default(cuid())
|
|
templateId String
|
|
template Template @relation(fields: [templateId], references: [id], onDelete: Cascade)
|
|
departmentId String
|
|
department Department @relation(fields: [departmentId], references: [id], onDelete: Cascade)
|
|
canUse Boolean @default(true)
|
|
canManage Boolean @default(false)
|
|
createdAt DateTime @default(now())
|
|
|
|
@@unique([templateId, departmentId])
|
|
}
|
|
|
|
model Report {
|
|
id String @id @default(cuid())
|
|
tenantId String
|
|
tenant Tenant @relation(fields: [tenantId], references: [id], onDelete: Cascade)
|
|
departmentId String
|
|
department Department @relation(fields: [departmentId], references: [id])
|
|
authorId String
|
|
author User @relation("ReportAuthor", fields: [authorId], references: [id])
|
|
templateId String?
|
|
template Template? @relation(fields: [templateId], references: [id])
|
|
title String
|
|
patientName String
|
|
hospitalId String
|
|
status ReportStatus @default(DRAFT)
|
|
revision Int @default(1)
|
|
content String
|
|
metadata Json @default("{}")
|
|
histories ReportHistory[]
|
|
files FileResource[]
|
|
media ReportMedia[]
|
|
deletedAt DateTime?
|
|
deletedBy String?
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
@@index([tenantId, departmentId, status])
|
|
@@index([tenantId, authorId])
|
|
@@index([tenantId, deletedAt])
|
|
}
|
|
|
|
model ReportHistory {
|
|
id String @id @default(cuid())
|
|
reportId String
|
|
report Report @relation(fields: [reportId], references: [id], onDelete: Cascade)
|
|
revision Int
|
|
content String
|
|
action String
|
|
updatedById String
|
|
updatedBy String
|
|
createdAt DateTime @default(now())
|
|
|
|
@@index([reportId, revision])
|
|
}
|
|
|
|
model FileResource {
|
|
id String @id @default(cuid())
|
|
tenantId String
|
|
tenant Tenant @relation(fields: [tenantId], references: [id], onDelete: Cascade)
|
|
ownerId String?
|
|
reportId String?
|
|
report Report? @relation(fields: [reportId], references: [id], onDelete: Cascade)
|
|
reportMedia ReportMedia[]
|
|
kind FileKind
|
|
filename String
|
|
mimeType String
|
|
size Int
|
|
storageKey String
|
|
checksum String?
|
|
createdAt DateTime @default(now())
|
|
|
|
@@index([tenantId, kind])
|
|
@@index([tenantId, reportId])
|
|
}
|
|
|
|
model ReportMedia {
|
|
id String @id @default(cuid())
|
|
tenantId String
|
|
tenant Tenant @relation(fields: [tenantId], references: [id], onDelete: Cascade)
|
|
reportId String
|
|
report Report @relation(fields: [reportId], references: [id], onDelete: Cascade)
|
|
fileId String?
|
|
file FileResource? @relation(fields: [fileId], references: [id], onDelete: SetNull)
|
|
kind ReportMediaKind
|
|
clientId String
|
|
name String?
|
|
url String?
|
|
time Float?
|
|
videoIndex Int?
|
|
videoName String?
|
|
sortOrder Int @default(0)
|
|
metadata Json @default("{}")
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
@@index([tenantId, reportId, kind])
|
|
@@index([tenantId, fileId])
|
|
}
|
|
|
|
model SystemSetting {
|
|
id String @id @default(cuid())
|
|
tenantId String
|
|
tenant Tenant @relation(fields: [tenantId], references: [id], onDelete: Cascade)
|
|
scope String
|
|
departmentId String?
|
|
department Department? @relation(fields: [departmentId], references: [id])
|
|
key String
|
|
value Json
|
|
secretValue String?
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
@@unique([tenantId, scope, departmentId, key])
|
|
}
|
|
|
|
model AuditLog {
|
|
id String @id @default(cuid())
|
|
tenantId String
|
|
tenant Tenant @relation(fields: [tenantId], references: [id], onDelete: Cascade)
|
|
actorUserId String?
|
|
actor User? @relation(fields: [actorUserId], references: [id])
|
|
actorRole String?
|
|
action String
|
|
targetType String
|
|
targetId String?
|
|
departmentId String?
|
|
ip String?
|
|
userAgent String?
|
|
metadata Json?
|
|
createdAt DateTime @default(now())
|
|
|
|
@@index([tenantId, action])
|
|
@@index([tenantId, actorUserId])
|
|
@@index([tenantId, targetType, targetId])
|
|
}
|