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.
This commit is contained in:
280
server/prisma/schema.prisma
Normal file
280
server/prisma/schema.prisma
Normal file
@@ -0,0 +1,280 @@
|
||||
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])
|
||||
}
|
||||
Reference in New Issue
Block a user