Files
Mdeical_Sur_Report/server/prisma/schema.prisma
admin 014aca8619 Initialize backendized SurClaw report system
- 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.
2026-05-02 01:41:57 +08:00

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