Summary: - Implement Prompt management infrastructure and core services - Build admin portal frontend with light theme - Integrate CodeMirror 6 editor for non-technical users Phase 3.5.1: Infrastructure Setup - Create capability_schema for Prompt storage - Add prompt_templates and prompt_versions tables - Add prompt:view/edit/debug/publish permissions - Migrate RVW prompts to database (RVW_EDITORIAL, RVW_METHODOLOGY) Phase 3.5.2: PromptService Core - Implement gray preview logic (DRAFT for debuggers, ACTIVE for users) - Module-level debug control (setDebugMode) - Handlebars template rendering - Variable extraction and validation (extractVariables, validateVariables) - Three-level disaster recovery (database -> cache -> hardcoded fallback) Phase 3.5.3: Management API - 8 RESTful endpoints (/api/admin/prompts/*) - Permission control (PROMPT_ENGINEER can edit, SUPER_ADMIN can publish) Phase 3.5.4: Frontend Management UI - Build admin portal architecture (AdminLayout, OrgLayout) - Add route system (/admin/*, /org/*) - Implement PromptListPage (filter, search, debug switch) - Implement PromptEditor (CodeMirror 6 simplified for clinical users) - Implement PromptEditorPage (edit, save, publish, test, version history) Technical Details: - Backend: 6 files, ~2044 lines (prompt.service.ts 596 lines) - Frontend: 9 files, ~1735 lines (PromptEditorPage.tsx 399 lines) - CodeMirror 6: Line numbers, auto-wrap, variable highlight, search, undo/redo - Chinese-friendly: 15px font, 1.8 line-height, system fonts Next Step: Phase 3.5.5 - Integrate RVW module with PromptService Tested: Backend API tests passed (8/8), Frontend pending user testing Status: Ready for Phase 3.5.5 RVW integration
1224 lines
51 KiB
Plaintext
1224 lines
51 KiB
Plaintext
generator client {
|
||
provider = "prisma-client-js"
|
||
previewFeatures = ["multiSchema"]
|
||
}
|
||
|
||
datasource db {
|
||
provider = "postgresql"
|
||
url = env("DATABASE_URL")
|
||
schemas = ["admin_schema", "aia_schema", "asl_schema", "capability_schema", "common_schema", "dc_schema", "iit_schema", "pkb_schema", "platform_schema", "public", "rvw_schema", "ssa_schema", "st_schema"]
|
||
}
|
||
|
||
/// 应用缓存表 - Postgres-Only架构
|
||
/// 用于替代Redis缓存,支持LLM结果缓存、健康检查缓存等
|
||
model AppCache {
|
||
id Int @id @default(autoincrement())
|
||
key String @unique @db.VarChar(500)
|
||
value Json
|
||
expiresAt DateTime @map("expires_at") @db.Timestamp(6)
|
||
createdAt DateTime? @default(now()) @map("created_at") @db.Timestamp(6)
|
||
|
||
@@index([expiresAt], map: "idx_app_cache_expires")
|
||
@@index([key, expiresAt], map: "idx_app_cache_key_expires")
|
||
@@map("app_cache")
|
||
@@schema("platform_schema")
|
||
}
|
||
|
||
model User {
|
||
id String @id @default(uuid())
|
||
phone String @unique
|
||
password String
|
||
email String? @unique
|
||
is_default_password Boolean @default(true)
|
||
password_changed_at DateTime?
|
||
name String
|
||
tenant_id String
|
||
department_id String?
|
||
avatarUrl String? @map("avatar_url")
|
||
role UserRole @default(USER)
|
||
status String @default("active")
|
||
kbQuota Int @default(3) @map("kb_quota")
|
||
kbUsed Int @default(0) @map("kb_used")
|
||
trialEndsAt DateTime? @map("trial_ends_at")
|
||
isTrial Boolean @default(true) @map("is_trial")
|
||
lastLoginAt DateTime? @map("last_login_at")
|
||
createdAt DateTime @default(now()) @map("created_at")
|
||
updatedAt DateTime @updatedAt @map("updated_at")
|
||
tenant_members tenant_members[]
|
||
departments departments? @relation(fields: [department_id], references: [id])
|
||
tenants tenants @relation(fields: [tenant_id], references: [id])
|
||
|
||
@@index([createdAt], map: "idx_platform_users_created_at")
|
||
@@index([email], map: "idx_platform_users_email")
|
||
@@index([status], map: "idx_platform_users_status")
|
||
@@index([phone], map: "idx_platform_users_phone")
|
||
@@index([tenant_id], map: "idx_platform_users_tenant_id")
|
||
@@map("users")
|
||
@@schema("platform_schema")
|
||
}
|
||
|
||
model Project {
|
||
id String @id @default(uuid())
|
||
userId String @map("user_id")
|
||
name String
|
||
background String @default("")
|
||
researchType String @default("observational") @map("research_type")
|
||
conversationCount Int @default(0) @map("conversation_count")
|
||
createdAt DateTime @default(now()) @map("created_at")
|
||
updatedAt DateTime @updatedAt @map("updated_at")
|
||
deletedAt DateTime? @map("deleted_at")
|
||
conversations Conversation[] @relation("ProjectConversations")
|
||
|
||
@@index([createdAt], map: "idx_aia_projects_created_at")
|
||
@@index([deletedAt], map: "idx_aia_projects_deleted_at")
|
||
@@index([userId], map: "idx_aia_projects_user_id")
|
||
@@map("projects")
|
||
@@schema("aia_schema")
|
||
}
|
||
|
||
model Conversation {
|
||
id String @id @default(uuid())
|
||
userId String @map("user_id")
|
||
projectId String? @map("project_id")
|
||
agentId String @map("agent_id")
|
||
title String
|
||
modelName String @default("deepseek-v3") @map("model_name")
|
||
messageCount Int @default(0) @map("message_count")
|
||
totalTokens Int @default(0) @map("total_tokens")
|
||
metadata Json?
|
||
createdAt DateTime @default(now()) @map("created_at")
|
||
updatedAt DateTime @updatedAt @map("updated_at")
|
||
deletedAt DateTime? @map("deleted_at")
|
||
project Project? @relation("ProjectConversations", fields: [projectId], references: [id])
|
||
messages Message[] @relation("ConversationMessages")
|
||
|
||
@@index([agentId], map: "idx_aia_conversations_agent_id")
|
||
@@index([createdAt], map: "idx_aia_conversations_created_at")
|
||
@@index([deletedAt], map: "idx_aia_conversations_deleted_at")
|
||
@@index([projectId], map: "idx_aia_conversations_project_id")
|
||
@@index([userId], map: "idx_aia_conversations_user_id")
|
||
@@map("conversations")
|
||
@@schema("aia_schema")
|
||
}
|
||
|
||
model Message {
|
||
id String @id @default(uuid())
|
||
conversationId String @map("conversation_id")
|
||
role String
|
||
content String
|
||
model String?
|
||
metadata Json?
|
||
tokens Int?
|
||
isPinned Boolean @default(false) @map("is_pinned")
|
||
createdAt DateTime @default(now()) @map("created_at")
|
||
conversation Conversation @relation("ConversationMessages", fields: [conversationId], references: [id], onDelete: Cascade)
|
||
|
||
@@index([conversationId], map: "idx_aia_messages_conversation_id")
|
||
@@index([createdAt], map: "idx_aia_messages_created_at")
|
||
@@index([isPinned], map: "idx_aia_messages_is_pinned")
|
||
@@map("messages")
|
||
@@schema("aia_schema")
|
||
}
|
||
|
||
model KnowledgeBase {
|
||
id String @id @default(uuid())
|
||
userId String @map("user_id")
|
||
name String
|
||
description String?
|
||
difyDatasetId String @map("dify_dataset_id")
|
||
fileCount Int @default(0) @map("file_count")
|
||
totalSizeBytes BigInt @default(0) @map("total_size_bytes")
|
||
createdAt DateTime @default(now()) @map("created_at")
|
||
updatedAt DateTime @updatedAt @map("updated_at")
|
||
batchTasks BatchTask[] @relation("KnowledgeBaseBatchTasks")
|
||
documents Document[] @relation("KnowledgeBaseDocuments")
|
||
|
||
@@index([difyDatasetId], map: "idx_pkb_knowledge_bases_dify_dataset_id")
|
||
@@index([userId], map: "idx_pkb_knowledge_bases_user_id")
|
||
@@map("knowledge_bases")
|
||
@@schema("pkb_schema")
|
||
}
|
||
|
||
model Document {
|
||
id String @id @default(uuid())
|
||
kbId String @map("kb_id")
|
||
userId String @map("user_id")
|
||
filename String
|
||
fileType String @map("file_type")
|
||
fileSizeBytes BigInt @map("file_size_bytes")
|
||
fileUrl String @map("file_url")
|
||
difyDocumentId String @map("dify_document_id")
|
||
status String @default("uploading")
|
||
progress Int @default(0)
|
||
errorMessage String? @map("error_message")
|
||
segmentsCount Int? @map("segments_count")
|
||
tokensCount Int? @map("tokens_count")
|
||
extractionMethod String? @map("extraction_method")
|
||
extractionQuality Float? @map("extraction_quality")
|
||
charCount Int? @map("char_count")
|
||
language String?
|
||
extractedText String? @map("extracted_text")
|
||
uploadedAt DateTime @default(now()) @map("uploaded_at")
|
||
processedAt DateTime? @map("processed_at")
|
||
batchResults BatchResult[] @relation("DocumentBatchResults")
|
||
knowledgeBase KnowledgeBase @relation("KnowledgeBaseDocuments", fields: [kbId], references: [id], onDelete: Cascade)
|
||
|
||
@@index([difyDocumentId], map: "idx_pkb_documents_dify_document_id")
|
||
@@index([extractionMethod], map: "idx_pkb_documents_extraction_method")
|
||
@@index([kbId], map: "idx_pkb_documents_kb_id")
|
||
@@index([status], map: "idx_pkb_documents_status")
|
||
@@index([userId], map: "idx_pkb_documents_user_id")
|
||
@@map("documents")
|
||
@@schema("pkb_schema")
|
||
}
|
||
|
||
model BatchTask {
|
||
id String @id @default(uuid())
|
||
userId String @map("user_id")
|
||
kbId String @map("kb_id")
|
||
name String
|
||
templateType String @map("template_type")
|
||
templateId String? @map("template_id")
|
||
prompt String
|
||
status String
|
||
totalDocuments Int @map("total_documents")
|
||
completedCount Int @default(0) @map("completed_count")
|
||
failedCount Int @default(0) @map("failed_count")
|
||
modelType String @map("model_type")
|
||
concurrency Int @default(3)
|
||
startedAt DateTime? @map("started_at")
|
||
completedAt DateTime? @map("completed_at")
|
||
durationSeconds Int? @map("duration_seconds")
|
||
createdAt DateTime @default(now()) @map("created_at")
|
||
updatedAt DateTime @updatedAt @map("updated_at")
|
||
results BatchResult[] @relation("TaskBatchResults")
|
||
knowledgeBase KnowledgeBase @relation("KnowledgeBaseBatchTasks", fields: [kbId], references: [id], onDelete: Cascade)
|
||
|
||
@@index([createdAt], map: "idx_pkb_batch_tasks_created_at")
|
||
@@index([kbId], map: "idx_pkb_batch_tasks_kb_id")
|
||
@@index([status], map: "idx_pkb_batch_tasks_status")
|
||
@@index([userId], map: "idx_pkb_batch_tasks_user_id")
|
||
@@map("batch_tasks")
|
||
@@schema("pkb_schema")
|
||
}
|
||
|
||
model BatchResult {
|
||
id String @id @default(uuid())
|
||
taskId String @map("task_id")
|
||
documentId String @map("document_id")
|
||
status String
|
||
data Json?
|
||
rawOutput String? @map("raw_output")
|
||
errorMessage String? @map("error_message")
|
||
processingTimeMs Int? @map("processing_time_ms")
|
||
tokensUsed Int? @map("tokens_used")
|
||
createdAt DateTime @default(now()) @map("created_at")
|
||
document Document @relation("DocumentBatchResults", fields: [documentId], references: [id], onDelete: Cascade)
|
||
task BatchTask @relation("TaskBatchResults", fields: [taskId], references: [id], onDelete: Cascade)
|
||
|
||
@@index([documentId], map: "idx_pkb_batch_results_document_id")
|
||
@@index([status], map: "idx_pkb_batch_results_status")
|
||
@@index([taskId], map: "idx_pkb_batch_results_task_id")
|
||
@@map("batch_results")
|
||
@@schema("pkb_schema")
|
||
}
|
||
|
||
model TaskTemplate {
|
||
id String @id @default(uuid())
|
||
userId String @map("user_id")
|
||
name String
|
||
description String?
|
||
prompt String
|
||
isPublic Boolean @default(false) @map("is_public")
|
||
createdAt DateTime @default(now()) @map("created_at")
|
||
updatedAt DateTime @updatedAt @map("updated_at")
|
||
outputFields Json
|
||
|
||
@@index([userId], map: "idx_pkb_task_templates_user_id")
|
||
@@map("task_templates")
|
||
@@schema("pkb_schema")
|
||
}
|
||
|
||
model AdminLog {
|
||
id Int @id @default(autoincrement())
|
||
adminId String @map("admin_id")
|
||
action String
|
||
resourceType String? @map("resource_type")
|
||
resourceId String? @map("resource_id")
|
||
details Json?
|
||
ipAddress String? @map("ip_address")
|
||
userAgent String? @map("user_agent")
|
||
createdAt DateTime @default(now()) @map("created_at")
|
||
admin users @relation(fields: [adminId], references: [id], onDelete: Cascade)
|
||
|
||
@@index([adminId])
|
||
@@index([createdAt])
|
||
@@index([action])
|
||
@@map("admin_logs")
|
||
@@schema("public")
|
||
}
|
||
|
||
model GeneralConversation {
|
||
id String @id @default(uuid())
|
||
userId String @map("user_id")
|
||
title String
|
||
modelName String? @map("model_name")
|
||
createdAt DateTime @default(now()) @map("created_at")
|
||
updatedAt DateTime @default(now()) @updatedAt @map("updated_at")
|
||
deletedAt DateTime? @map("deleted_at")
|
||
|
||
@@index([createdAt], map: "idx_aia_general_conversations_created_at")
|
||
@@index([updatedAt], map: "idx_aia_general_conversations_updated_at")
|
||
@@index([userId], map: "idx_aia_general_conversations_user_id")
|
||
@@map("general_conversations")
|
||
@@schema("aia_schema")
|
||
}
|
||
|
||
model GeneralMessage {
|
||
id String @id @default(uuid())
|
||
conversationId String @map("conversation_id")
|
||
role String
|
||
content String
|
||
model String?
|
||
metadata Json?
|
||
tokens Int?
|
||
createdAt DateTime @default(now()) @map("created_at")
|
||
|
||
@@index([conversationId], map: "idx_aia_general_messages_conversation_id")
|
||
@@index([createdAt], map: "idx_aia_general_messages_created_at")
|
||
@@map("general_messages")
|
||
@@schema("aia_schema")
|
||
}
|
||
|
||
model ReviewTask {
|
||
id String @id @default(uuid())
|
||
userId String @map("user_id")
|
||
fileName String @map("file_name")
|
||
fileSize Int @map("file_size")
|
||
filePath String? @map("file_path")
|
||
extractedText String @map("extracted_text")
|
||
wordCount Int? @map("word_count")
|
||
status String @default("pending")
|
||
selectedAgents String[] @default(["editorial", "methodology"]) @map("selected_agents")
|
||
editorialReview Json? @map("editorial_review")
|
||
methodologyReview Json? @map("methodology_review")
|
||
overallScore Float? @map("overall_score")
|
||
editorialScore Float? @map("editorial_score")
|
||
methodologyScore Float? @map("methodology_score")
|
||
methodologyStatus String? @map("methodology_status")
|
||
picoExtract Json? @map("pico_extract")
|
||
isArchived Boolean @default(false) @map("is_archived")
|
||
archivedAt DateTime? @map("archived_at")
|
||
modelUsed String? @map("model_used")
|
||
startedAt DateTime? @map("started_at")
|
||
completedAt DateTime? @map("completed_at")
|
||
durationSeconds Int? @map("duration_seconds")
|
||
errorMessage String? @map("error_message")
|
||
createdAt DateTime @default(now()) @map("created_at")
|
||
updatedAt DateTime @updatedAt @map("updated_at")
|
||
user users @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||
|
||
@@index([userId])
|
||
@@index([status])
|
||
@@index([createdAt])
|
||
@@index([isArchived])
|
||
@@map("review_tasks")
|
||
@@schema("rvw_schema")
|
||
}
|
||
|
||
model AslScreeningProject {
|
||
id String @id @default(uuid())
|
||
userId String @map("user_id")
|
||
projectName String @map("project_name")
|
||
picoCriteria Json @map("pico_criteria")
|
||
inclusionCriteria String @map("inclusion_criteria")
|
||
exclusionCriteria String @map("exclusion_criteria")
|
||
status String @default("draft")
|
||
screeningConfig Json? @map("screening_config")
|
||
createdAt DateTime @default(now()) @map("created_at")
|
||
updatedAt DateTime @updatedAt @map("updated_at")
|
||
fulltextResults AslFulltextScreeningResult[] @relation("ProjectFulltextResults")
|
||
fulltextTasks AslFulltextScreeningTask[] @relation("ProjectFulltextTasks")
|
||
literatures AslLiterature[] @relation("ProjectLiteratures")
|
||
screeningResults AslScreeningResult[] @relation("ProjectScreeningResults")
|
||
screeningTasks AslScreeningTask[] @relation("ProjectScreeningTasks")
|
||
|
||
@@index([status], map: "idx_screening_projects_status")
|
||
@@index([userId], map: "idx_screening_projects_user_id")
|
||
@@map("screening_projects")
|
||
@@schema("asl_schema")
|
||
}
|
||
|
||
model AslLiterature {
|
||
id String @id @default(uuid())
|
||
projectId String @map("project_id")
|
||
pmid String?
|
||
title String
|
||
abstract String
|
||
authors String?
|
||
journal String?
|
||
publicationYear Int? @map("publication_year")
|
||
doi String?
|
||
pdfUrl String? @map("pdf_url")
|
||
pdfOssKey String? @map("pdf_oss_key")
|
||
pdfFileSize Int? @map("pdf_file_size")
|
||
createdAt DateTime @default(now()) @map("created_at")
|
||
updatedAt DateTime @updatedAt @map("updated_at")
|
||
stage String @default("imported") @map("stage")
|
||
hasPdf Boolean @default(false) @map("has_pdf")
|
||
pdfStorageType String? @map("pdf_storage_type")
|
||
pdfStorageRef String? @map("pdf_storage_ref")
|
||
pdfStatus String? @map("pdf_status")
|
||
pdfUploadedAt DateTime? @map("pdf_uploaded_at")
|
||
fullTextStorageType String? @map("full_text_storage_type")
|
||
fullTextStorageRef String? @map("full_text_storage_ref")
|
||
fullTextUrl String? @map("full_text_url")
|
||
fullTextFormat String? @map("full_text_format")
|
||
fullTextSource String? @map("full_text_source")
|
||
fullTextTokenCount Int? @map("full_text_token_count")
|
||
fullTextExtractedAt DateTime? @map("full_text_extracted_at")
|
||
fulltextResults AslFulltextScreeningResult[] @relation("LiteratureFulltextResults")
|
||
project AslScreeningProject @relation("ProjectLiteratures", fields: [projectId], references: [id], onDelete: Cascade)
|
||
screeningResults AslScreeningResult[] @relation("LiteratureScreeningResults")
|
||
|
||
@@unique([projectId, pmid], map: "unique_project_pmid")
|
||
@@index([doi], map: "idx_literatures_doi")
|
||
@@index([hasPdf], map: "idx_literatures_has_pdf")
|
||
@@index([pdfStatus], map: "idx_literatures_pdf_status")
|
||
@@index([projectId], map: "idx_literatures_project_id")
|
||
@@index([stage], map: "idx_literatures_stage")
|
||
@@map("literatures")
|
||
@@schema("asl_schema")
|
||
}
|
||
|
||
model AslScreeningResult {
|
||
id String @id @default(uuid())
|
||
projectId String @map("project_id")
|
||
literatureId String @map("literature_id")
|
||
dsModelName String @map("ds_model_name")
|
||
dsPJudgment String? @map("ds_p_judgment")
|
||
dsIJudgment String? @map("ds_i_judgment")
|
||
dsCJudgment String? @map("ds_c_judgment")
|
||
dsSJudgment String? @map("ds_s_judgment")
|
||
dsConclusion String? @map("ds_conclusion")
|
||
dsConfidence Float? @map("ds_confidence")
|
||
dsPEvidence String? @map("ds_p_evidence")
|
||
dsIEvidence String? @map("ds_i_evidence")
|
||
dsCEvidence String? @map("ds_c_evidence")
|
||
dsSEvidence String? @map("ds_s_evidence")
|
||
dsReason String? @map("ds_reason")
|
||
qwenModelName String @map("qwen_model_name")
|
||
qwenPJudgment String? @map("qwen_p_judgment")
|
||
qwenIJudgment String? @map("qwen_i_judgment")
|
||
qwenCJudgment String? @map("qwen_c_judgment")
|
||
qwenSJudgment String? @map("qwen_s_judgment")
|
||
qwenConclusion String? @map("qwen_conclusion")
|
||
qwenConfidence Float? @map("qwen_confidence")
|
||
qwenPEvidence String? @map("qwen_p_evidence")
|
||
qwenIEvidence String? @map("qwen_i_evidence")
|
||
qwenCEvidence String? @map("qwen_c_evidence")
|
||
qwenSEvidence String? @map("qwen_s_evidence")
|
||
qwenReason String? @map("qwen_reason")
|
||
conflictStatus String @default("none") @map("conflict_status")
|
||
conflictFields Json? @map("conflict_fields")
|
||
finalDecision String? @map("final_decision")
|
||
finalDecisionBy String? @map("final_decision_by")
|
||
finalDecisionAt DateTime? @map("final_decision_at")
|
||
exclusionReason String? @map("exclusion_reason")
|
||
aiProcessingStatus String @default("pending") @map("ai_processing_status")
|
||
aiProcessedAt DateTime? @map("ai_processed_at")
|
||
aiErrorMessage String? @map("ai_error_message")
|
||
promptVersion String @default("v1.0.0") @map("prompt_version")
|
||
rawOutput Json? @map("raw_output")
|
||
createdAt DateTime @default(now()) @map("created_at")
|
||
updatedAt DateTime @updatedAt @map("updated_at")
|
||
literature AslLiterature @relation("LiteratureScreeningResults", fields: [literatureId], references: [id], onDelete: Cascade)
|
||
project AslScreeningProject @relation("ProjectScreeningResults", fields: [projectId], references: [id], onDelete: Cascade)
|
||
|
||
@@unique([projectId, literatureId], map: "unique_project_literature")
|
||
@@index([conflictStatus], map: "idx_screening_results_conflict_status")
|
||
@@index([finalDecision], map: "idx_screening_results_final_decision")
|
||
@@index([literatureId], map: "idx_screening_results_literature_id")
|
||
@@index([projectId], map: "idx_screening_results_project_id")
|
||
@@map("screening_results")
|
||
@@schema("asl_schema")
|
||
}
|
||
|
||
model AslScreeningTask {
|
||
id String @id @default(uuid())
|
||
projectId String @map("project_id")
|
||
taskType String @map("task_type")
|
||
status String @default("pending")
|
||
totalItems Int @map("total_items")
|
||
processedItems Int @default(0) @map("processed_items")
|
||
successItems Int @default(0) @map("success_items")
|
||
failedItems Int @default(0) @map("failed_items")
|
||
conflictItems Int @default(0) @map("conflict_items")
|
||
startedAt DateTime? @map("started_at")
|
||
completedAt DateTime? @map("completed_at")
|
||
estimatedEndAt DateTime? @map("estimated_end_at")
|
||
errorMessage String? @map("error_message")
|
||
createdAt DateTime @default(now()) @map("created_at")
|
||
updatedAt DateTime @updatedAt @map("updated_at")
|
||
project AslScreeningProject @relation("ProjectScreeningTasks", fields: [projectId], references: [id], onDelete: Cascade)
|
||
|
||
@@index([projectId], map: "idx_screening_tasks_project_id")
|
||
@@index([status], map: "idx_screening_tasks_status")
|
||
@@map("screening_tasks")
|
||
@@schema("asl_schema")
|
||
}
|
||
|
||
model AslFulltextScreeningTask {
|
||
id String @id @default(uuid())
|
||
projectId String @map("project_id")
|
||
modelA String @map("model_a")
|
||
modelB String @map("model_b")
|
||
promptVersion String @default("v1.0.0") @map("prompt_version")
|
||
status String @default("pending")
|
||
totalCount Int @map("total_count")
|
||
processedCount Int @default(0) @map("processed_count")
|
||
successCount Int @default(0) @map("success_count")
|
||
failedCount Int @default(0) @map("failed_count")
|
||
degradedCount Int @default(0) @map("degraded_count")
|
||
totalTokens Int @default(0) @map("total_tokens")
|
||
totalCost Float @default(0) @map("total_cost")
|
||
startedAt DateTime? @map("started_at")
|
||
completedAt DateTime? @map("completed_at")
|
||
estimatedEndAt DateTime? @map("estimated_end_at")
|
||
errorMessage String? @map("error_message")
|
||
errorStack String? @map("error_stack")
|
||
createdAt DateTime @default(now()) @map("created_at")
|
||
updatedAt DateTime @updatedAt @map("updated_at")
|
||
results AslFulltextScreeningResult[] @relation("TaskFulltextResults")
|
||
project AslScreeningProject @relation("ProjectFulltextTasks", fields: [projectId], references: [id], onDelete: Cascade)
|
||
|
||
@@index([createdAt], map: "idx_fulltext_tasks_created_at")
|
||
@@index([projectId], map: "idx_fulltext_tasks_project_id")
|
||
@@index([status], map: "idx_fulltext_tasks_status")
|
||
@@map("fulltext_screening_tasks")
|
||
@@schema("asl_schema")
|
||
}
|
||
|
||
model AslFulltextScreeningResult {
|
||
id String @id @default(uuid())
|
||
taskId String @map("task_id")
|
||
projectId String @map("project_id")
|
||
literatureId String @map("literature_id")
|
||
modelAName String @map("model_a_name")
|
||
modelAStatus String @map("model_a_status")
|
||
modelAFields Json @map("model_a_fields")
|
||
modelAOverall Json @map("model_a_overall")
|
||
modelAProcessingLog Json? @map("model_a_processing_log")
|
||
modelAVerification Json? @map("model_a_verification")
|
||
modelATokens Int? @map("model_a_tokens")
|
||
modelACost Float? @map("model_a_cost")
|
||
modelAError String? @map("model_a_error")
|
||
modelBName String @map("model_b_name")
|
||
modelBStatus String @map("model_b_status")
|
||
modelBFields Json @map("model_b_fields")
|
||
modelBOverall Json @map("model_b_overall")
|
||
modelBProcessingLog Json? @map("model_b_processing_log")
|
||
modelBVerification Json? @map("model_b_verification")
|
||
modelBTokens Int? @map("model_b_tokens")
|
||
modelBCost Float? @map("model_b_cost")
|
||
modelBError String? @map("model_b_error")
|
||
medicalLogicIssues Json? @map("medical_logic_issues")
|
||
evidenceChainIssues Json? @map("evidence_chain_issues")
|
||
isConflict Boolean @default(false) @map("is_conflict")
|
||
conflictSeverity String? @map("conflict_severity")
|
||
conflictFields String[] @map("conflict_fields")
|
||
conflictDetails Json? @map("conflict_details")
|
||
reviewPriority Int? @map("review_priority")
|
||
reviewDeadline DateTime? @map("review_deadline")
|
||
finalDecision String? @map("final_decision")
|
||
finalDecisionBy String? @map("final_decision_by")
|
||
finalDecisionAt DateTime? @map("final_decision_at")
|
||
exclusionReason String? @map("exclusion_reason")
|
||
reviewNotes String? @map("review_notes")
|
||
processingStatus String @default("pending") @map("processing_status")
|
||
isDegraded Boolean @default(false) @map("is_degraded")
|
||
degradedModel String? @map("degraded_model")
|
||
processedAt DateTime? @map("processed_at")
|
||
promptVersion String @default("v1.0.0") @map("prompt_version")
|
||
rawOutputA Json? @map("raw_output_a")
|
||
rawOutputB Json? @map("raw_output_b")
|
||
createdAt DateTime @default(now()) @map("created_at")
|
||
updatedAt DateTime @updatedAt @map("updated_at")
|
||
literature AslLiterature @relation("LiteratureFulltextResults", fields: [literatureId], references: [id], onDelete: Cascade)
|
||
project AslScreeningProject @relation("ProjectFulltextResults", fields: [projectId], references: [id], onDelete: Cascade)
|
||
task AslFulltextScreeningTask @relation("TaskFulltextResults", fields: [taskId], references: [id], onDelete: Cascade)
|
||
|
||
@@unique([projectId, literatureId], map: "unique_project_literature_fulltext")
|
||
@@index([finalDecision], map: "idx_fulltext_results_final_decision")
|
||
@@index([isConflict], map: "idx_fulltext_results_is_conflict")
|
||
@@index([literatureId], map: "idx_fulltext_results_literature_id")
|
||
@@index([projectId], map: "idx_fulltext_results_project_id")
|
||
@@index([reviewPriority], map: "idx_fulltext_results_review_priority")
|
||
@@index([taskId], map: "idx_fulltext_results_task_id")
|
||
@@map("fulltext_screening_results")
|
||
@@schema("asl_schema")
|
||
}
|
||
|
||
model DCHealthCheck {
|
||
id String @id @default(uuid())
|
||
userId String @map("user_id")
|
||
fileName String @map("file_name")
|
||
columnName String @map("column_name")
|
||
emptyRate Float @map("empty_rate")
|
||
avgLength Float @map("avg_length")
|
||
totalRows Int @map("total_rows")
|
||
estimatedTokens Int @map("estimated_tokens")
|
||
status String @map("status")
|
||
message String @map("message")
|
||
createdAt DateTime @default(now()) @map("created_at")
|
||
|
||
@@index([userId, fileName])
|
||
@@map("dc_health_checks")
|
||
@@schema("dc_schema")
|
||
}
|
||
|
||
model DCTemplate {
|
||
id String @id @default(uuid())
|
||
diseaseType String @map("disease_type")
|
||
reportType String @map("report_type")
|
||
displayName String @map("display_name")
|
||
fields Json @map("fields")
|
||
promptTemplate String @map("prompt_template")
|
||
createdAt DateTime @default(now()) @map("created_at")
|
||
updatedAt DateTime @updatedAt @map("updated_at")
|
||
|
||
@@unique([diseaseType, reportType])
|
||
@@map("dc_templates")
|
||
@@schema("dc_schema")
|
||
}
|
||
|
||
model DCExtractionTask {
|
||
id String @id @default(uuid())
|
||
userId String @map("user_id")
|
||
projectName String @map("project_name")
|
||
sourceFileKey String @map("source_file_key")
|
||
textColumn String @map("text_column")
|
||
diseaseType String @map("disease_type")
|
||
reportType String @map("report_type")
|
||
targetFields Json @map("target_fields")
|
||
modelA String @default("deepseek-v3") @map("model_a")
|
||
modelB String @default("qwen-max") @map("model_b")
|
||
status String @default("pending") @map("status")
|
||
totalCount Int @default(0) @map("total_count")
|
||
processedCount Int @default(0) @map("processed_count")
|
||
cleanCount Int @default(0) @map("clean_count")
|
||
conflictCount Int @default(0) @map("conflict_count")
|
||
failedCount Int @default(0) @map("failed_count")
|
||
totalTokens Int @default(0) @map("total_tokens")
|
||
totalCost Float @default(0) @map("total_cost")
|
||
error String? @map("error")
|
||
createdAt DateTime @default(now()) @map("created_at")
|
||
startedAt DateTime? @map("started_at")
|
||
completedAt DateTime? @map("completed_at")
|
||
items DCExtractionItem[]
|
||
|
||
@@index([userId, status])
|
||
@@map("dc_extraction_tasks")
|
||
@@schema("dc_schema")
|
||
}
|
||
|
||
model DCExtractionItem {
|
||
id String @id @default(uuid())
|
||
taskId String @map("task_id")
|
||
rowIndex Int @map("row_index")
|
||
originalText String @map("original_text")
|
||
resultA Json? @map("result_a")
|
||
resultB Json? @map("result_b")
|
||
status String @default("pending") @map("status")
|
||
conflictFields String[] @default([]) @map("conflict_fields")
|
||
finalResult Json? @map("final_result")
|
||
tokensA Int @default(0) @map("tokens_a")
|
||
tokensB Int @default(0) @map("tokens_b")
|
||
error String? @map("error")
|
||
createdAt DateTime @default(now()) @map("created_at")
|
||
resolvedAt DateTime? @map("resolved_at")
|
||
task DCExtractionTask @relation(fields: [taskId], references: [id], onDelete: Cascade)
|
||
|
||
@@index([taskId, status])
|
||
@@map("dc_extraction_items")
|
||
@@schema("dc_schema")
|
||
}
|
||
|
||
model DcToolCSession {
|
||
id String @id @default(dbgenerated("gen_random_uuid()")) @db.Uuid
|
||
userId String @map("user_id") @db.VarChar(255)
|
||
fileName String @map("file_name") @db.VarChar(500)
|
||
fileKey String @map("file_key") @db.VarChar(500)
|
||
totalRows Int? @map("total_rows")
|
||
totalCols Int? @map("total_cols")
|
||
columns Json? @map("columns")
|
||
encoding String? @map("encoding") @db.VarChar(50)
|
||
fileSize Int @map("file_size")
|
||
createdAt DateTime @default(now()) @map("created_at") @db.Timestamp(6)
|
||
updatedAt DateTime @default(now()) @updatedAt @map("updated_at") @db.Timestamp(6)
|
||
expiresAt DateTime @map("expires_at") @db.Timestamp(6)
|
||
dataStats Json? @map("data_stats")
|
||
columnMapping Json? @map("column_mapping")
|
||
cleanDataKey String? @map("clean_data_key") @db.VarChar(1000)
|
||
|
||
@@index([expiresAt], map: "idx_dc_tool_c_sessions_expires_at")
|
||
@@index([userId], map: "idx_dc_tool_c_sessions_user_id")
|
||
@@map("dc_tool_c_sessions")
|
||
@@schema("dc_schema")
|
||
}
|
||
|
||
model DcToolCAiHistory {
|
||
id String @id @default(dbgenerated("gen_random_uuid()")) @db.Uuid
|
||
sessionId String @map("session_id") @db.VarChar(255)
|
||
userId String @map("user_id") @db.VarChar(255)
|
||
role String @map("role") @db.VarChar(50)
|
||
content String
|
||
generatedCode String? @map("generated_code")
|
||
codeExplanation String? @map("code_explanation")
|
||
executeStatus String? @map("execute_status") @db.VarChar(50)
|
||
executeResult Json? @map("execute_result")
|
||
executeError String? @map("execute_error")
|
||
retryCount Int? @default(0) @map("retry_count")
|
||
model String? @map("model") @db.VarChar(100)
|
||
createdAt DateTime @default(now()) @map("created_at") @db.Timestamp(6)
|
||
|
||
@@index([createdAt], map: "idx_dc_tool_c_ai_history_created_at")
|
||
@@index([sessionId], map: "idx_dc_tool_c_ai_history_session_id")
|
||
@@index([userId], map: "idx_dc_tool_c_ai_history_user_id")
|
||
@@map("dc_tool_c_ai_history")
|
||
@@schema("dc_schema")
|
||
}
|
||
|
||
/// This table is a partition table and requires additional setup for migrations. Visit https://pris.ly/d/partition-tables for more info.
|
||
model job {
|
||
id String @default(dbgenerated("gen_random_uuid()")) @db.Uuid
|
||
name String
|
||
priority Int @default(0)
|
||
data Json?
|
||
state job_state @default(created)
|
||
retry_limit Int @default(2)
|
||
retry_count Int @default(0)
|
||
retry_delay Int @default(0)
|
||
retry_backoff Boolean @default(false)
|
||
retry_delay_max Int?
|
||
expire_seconds Int @default(900)
|
||
deletion_seconds Int @default(604800)
|
||
singleton_key String?
|
||
singleton_on DateTime? @db.Timestamp(6)
|
||
start_after DateTime @default(now()) @db.Timestamptz(6)
|
||
created_on DateTime @default(now()) @db.Timestamptz(6)
|
||
started_on DateTime? @db.Timestamptz(6)
|
||
completed_on DateTime? @db.Timestamptz(6)
|
||
keep_until DateTime @default(dbgenerated("(now() + '336:00:00'::interval)")) @db.Timestamptz(6)
|
||
output Json?
|
||
dead_letter String?
|
||
policy String?
|
||
|
||
@@id([name, id])
|
||
@@schema("platform_schema")
|
||
}
|
||
|
||
/// This table contains check constraints and requires additional setup for migrations. Visit https://pris.ly/d/check-constraints for more info.
|
||
model queue {
|
||
name String @id
|
||
policy String
|
||
retry_limit Int
|
||
retry_delay Int
|
||
retry_backoff Boolean
|
||
retry_delay_max Int?
|
||
expire_seconds Int
|
||
retention_seconds Int
|
||
deletion_seconds Int
|
||
dead_letter String?
|
||
partition Boolean
|
||
table_name String
|
||
deferred_count Int @default(0)
|
||
queued_count Int @default(0)
|
||
warning_queued Int @default(0)
|
||
active_count Int @default(0)
|
||
total_count Int @default(0)
|
||
singletons_active String[]
|
||
monitor_on DateTime? @db.Timestamptz(6)
|
||
maintain_on DateTime? @db.Timestamptz(6)
|
||
created_on DateTime @default(now()) @db.Timestamptz(6)
|
||
updated_on DateTime @default(now()) @db.Timestamptz(6)
|
||
queue queue? @relation("queueToqueue", fields: [dead_letter], references: [name], onDelete: NoAction, onUpdate: NoAction)
|
||
other_queue queue[] @relation("queueToqueue")
|
||
schedule schedule[]
|
||
subscription subscription[]
|
||
|
||
@@schema("platform_schema")
|
||
}
|
||
|
||
model schedule {
|
||
name String
|
||
key String @default("")
|
||
cron String
|
||
timezone String?
|
||
data Json?
|
||
options Json?
|
||
created_on DateTime @default(now()) @db.Timestamptz(6)
|
||
updated_on DateTime @default(now()) @db.Timestamptz(6)
|
||
queue queue @relation(fields: [name], references: [name], onDelete: Cascade, onUpdate: NoAction)
|
||
|
||
@@id([name, key])
|
||
@@schema("platform_schema")
|
||
}
|
||
|
||
model subscription {
|
||
event String
|
||
name String
|
||
created_on DateTime @default(now()) @db.Timestamptz(6)
|
||
updated_on DateTime @default(now()) @db.Timestamptz(6)
|
||
queue queue @relation(fields: [name], references: [name], onDelete: Cascade, onUpdate: NoAction)
|
||
|
||
@@id([event, name])
|
||
@@schema("platform_schema")
|
||
}
|
||
|
||
model version {
|
||
version Int @id
|
||
cron_on DateTime? @db.Timestamptz(6)
|
||
|
||
@@schema("platform_schema")
|
||
}
|
||
|
||
model users {
|
||
id String @id
|
||
email String @unique
|
||
password String
|
||
name String?
|
||
avatar_url String?
|
||
role String @default("user")
|
||
status String @default("active")
|
||
kb_quota Int @default(3)
|
||
kb_used Int @default(0)
|
||
trial_ends_at DateTime?
|
||
is_trial Boolean @default(true)
|
||
last_login_at DateTime?
|
||
created_at DateTime @default(now())
|
||
updated_at DateTime
|
||
adminLogs AdminLog[]
|
||
reviewTasks ReviewTask[]
|
||
|
||
@@index([created_at])
|
||
@@index([email])
|
||
@@index([status])
|
||
@@schema("public")
|
||
}
|
||
|
||
/// IIT项目表
|
||
model IitProject {
|
||
id String @id @default(uuid())
|
||
name String
|
||
description String?
|
||
difyDatasetId String? @unique @map("dify_dataset_id")
|
||
protocolFileKey String? @map("protocol_file_key")
|
||
cachedRules Json? @map("cached_rules")
|
||
fieldMappings Json @map("field_mappings")
|
||
redcapProjectId String @map("redcap_project_id")
|
||
redcapApiToken String @map("redcap_api_token")
|
||
redcapUrl String @map("redcap_url")
|
||
lastSyncAt DateTime? @map("last_sync_at")
|
||
status String @default("active")
|
||
createdAt DateTime @default(now()) @map("created_at")
|
||
updatedAt DateTime @updatedAt @map("updated_at")
|
||
deletedAt DateTime? @map("deleted_at")
|
||
auditLogs IitAuditLog[]
|
||
pendingActions IitPendingAction[]
|
||
taskRuns IitTaskRun[]
|
||
userMappings IitUserMapping[]
|
||
|
||
@@index([status, deletedAt])
|
||
@@map("projects")
|
||
@@schema("iit_schema")
|
||
}
|
||
|
||
/// 影子状态表(核心)
|
||
model IitPendingAction {
|
||
id String @id @default(uuid())
|
||
projectId String @map("project_id")
|
||
recordId String @map("record_id")
|
||
fieldName String @map("field_name")
|
||
currentValue Json? @map("current_value")
|
||
suggestedValue Json? @map("suggested_value")
|
||
status String
|
||
agentType String @map("agent_type")
|
||
reasoning String
|
||
evidence Json
|
||
approvedBy String? @map("approved_by")
|
||
approvedAt DateTime? @map("approved_at")
|
||
rejectionReason String? @map("rejection_reason")
|
||
executedAt DateTime? @map("executed_at")
|
||
errorMessage String? @map("error_message")
|
||
createdAt DateTime @default(now()) @map("created_at")
|
||
updatedAt DateTime @updatedAt @map("updated_at")
|
||
project IitProject @relation(fields: [projectId], references: [id])
|
||
|
||
@@index([projectId, status])
|
||
@@index([projectId, recordId])
|
||
@@index([status, createdAt])
|
||
@@map("pending_actions")
|
||
@@schema("iit_schema")
|
||
}
|
||
|
||
/// 任务运行记录(与 pg-boss 关联)
|
||
model IitTaskRun {
|
||
id String @id @default(uuid())
|
||
projectId String @map("project_id")
|
||
taskType String @map("task_type")
|
||
jobId String? @unique @map("job_id")
|
||
status String
|
||
totalItems Int @map("total_items")
|
||
processedItems Int @default(0) @map("processed_items")
|
||
successItems Int @default(0) @map("success_items")
|
||
failedItems Int @default(0) @map("failed_items")
|
||
startedAt DateTime? @map("started_at")
|
||
completedAt DateTime? @map("completed_at")
|
||
duration Int?
|
||
createdAt DateTime @default(now()) @map("created_at")
|
||
updatedAt DateTime @updatedAt @map("updated_at")
|
||
project IitProject @relation(fields: [projectId], references: [id])
|
||
|
||
@@index([projectId, taskType, status])
|
||
@@index([jobId])
|
||
@@map("task_runs")
|
||
@@schema("iit_schema")
|
||
}
|
||
|
||
/// 用户映射表(异构系统身份关联)
|
||
model IitUserMapping {
|
||
id String @id @default(uuid())
|
||
projectId String @map("project_id")
|
||
systemUserId String @map("system_user_id")
|
||
redcapUsername String @map("redcap_username")
|
||
wecomUserId String? @map("wecom_user_id")
|
||
miniProgramOpenId String? @unique @map("mini_program_open_id")
|
||
sessionKey String? @map("session_key")
|
||
role String
|
||
createdAt DateTime @default(now()) @map("created_at")
|
||
updatedAt DateTime @updatedAt @map("updated_at")
|
||
project IitProject @relation(fields: [projectId], references: [id])
|
||
|
||
@@unique([projectId, systemUserId])
|
||
@@unique([projectId, redcapUsername])
|
||
@@index([wecomUserId])
|
||
@@index([miniProgramOpenId])
|
||
@@map("user_mappings")
|
||
@@schema("iit_schema")
|
||
}
|
||
|
||
/// 审计日志表
|
||
model IitAuditLog {
|
||
id String @id @default(uuid())
|
||
projectId String @map("project_id")
|
||
userId String @map("user_id")
|
||
actionType String @map("action_type")
|
||
entityType String @map("entity_type")
|
||
entityId String @map("entity_id")
|
||
details Json?
|
||
traceId String @map("trace_id")
|
||
createdAt DateTime @default(now()) @map("created_at")
|
||
project IitProject @relation(fields: [projectId], references: [id])
|
||
|
||
@@index([projectId, createdAt])
|
||
@@index([userId, createdAt])
|
||
@@index([actionType, createdAt])
|
||
@@index([traceId])
|
||
@@map("audit_logs")
|
||
@@schema("iit_schema")
|
||
}
|
||
|
||
model admin_operation_logs {
|
||
id Int @id @default(autoincrement())
|
||
admin_id String
|
||
operation_type String
|
||
target_type String
|
||
target_id String
|
||
module String?
|
||
before_data Json?
|
||
after_data Json?
|
||
ip_address String?
|
||
user_agent String?
|
||
created_at DateTime @default(now())
|
||
|
||
@@index([admin_id], map: "idx_admin_logs_admin_id")
|
||
@@index([created_at], map: "idx_admin_logs_created_at")
|
||
@@index([module], map: "idx_admin_logs_module")
|
||
@@index([operation_type], map: "idx_admin_logs_operation_type")
|
||
@@schema("admin_schema")
|
||
}
|
||
|
||
model departments {
|
||
id String @id
|
||
tenant_id String
|
||
name String
|
||
parent_id String?
|
||
description String?
|
||
created_at DateTime @default(now())
|
||
updated_at DateTime
|
||
departments departments? @relation("departmentsTodepartments", fields: [parent_id], references: [id])
|
||
other_departments departments[] @relation("departmentsTodepartments")
|
||
tenants tenants @relation(fields: [tenant_id], references: [id], onDelete: Cascade)
|
||
users User[]
|
||
|
||
@@index([parent_id], map: "idx_departments_parent_id")
|
||
@@index([tenant_id], map: "idx_departments_tenant_id")
|
||
@@schema("platform_schema")
|
||
}
|
||
|
||
/// The underlying table does not contain a valid unique identifier and can therefore currently not be handled by Prisma Client.
|
||
model job_common {
|
||
id String @default(dbgenerated("gen_random_uuid()")) @db.Uuid
|
||
name String
|
||
priority Int @default(0)
|
||
data Json?
|
||
state job_state @default(created)
|
||
retry_limit Int @default(2)
|
||
retry_count Int @default(0)
|
||
retry_delay Int @default(0)
|
||
retry_backoff Boolean @default(false)
|
||
retry_delay_max Int?
|
||
expire_seconds Int @default(900)
|
||
deletion_seconds Int @default(604800)
|
||
singleton_key String?
|
||
singleton_on DateTime? @db.Timestamp(6)
|
||
start_after DateTime @default(now()) @db.Timestamptz(6)
|
||
created_on DateTime @default(now()) @db.Timestamptz(6)
|
||
started_on DateTime? @db.Timestamptz(6)
|
||
completed_on DateTime? @db.Timestamptz(6)
|
||
keep_until DateTime @default(dbgenerated("(now() + '336:00:00'::interval)")) @db.Timestamptz(6)
|
||
output Json?
|
||
dead_letter String?
|
||
policy String?
|
||
|
||
@@ignore
|
||
@@schema("platform_schema")
|
||
}
|
||
|
||
model permissions {
|
||
id Int @id @default(autoincrement())
|
||
code String @unique
|
||
name String
|
||
description String?
|
||
module String?
|
||
created_at DateTime @default(now())
|
||
role_permissions role_permissions[]
|
||
|
||
@@schema("platform_schema")
|
||
}
|
||
|
||
model role_permissions {
|
||
id Int @id @default(autoincrement())
|
||
role UserRole
|
||
permission_id Int
|
||
created_at DateTime @default(now())
|
||
permissions permissions @relation(fields: [permission_id], references: [id], onDelete: Cascade)
|
||
|
||
@@unique([role, permission_id])
|
||
@@schema("platform_schema")
|
||
}
|
||
|
||
model tenant_members {
|
||
id String @id
|
||
tenant_id String
|
||
user_id String
|
||
role UserRole
|
||
joined_at DateTime @default(now())
|
||
tenants tenants @relation(fields: [tenant_id], references: [id], onDelete: Cascade)
|
||
users User @relation(fields: [user_id], references: [id], onDelete: Cascade)
|
||
|
||
@@unique([tenant_id, user_id])
|
||
@@index([tenant_id], map: "idx_tenant_members_tenant_id")
|
||
@@index([user_id], map: "idx_tenant_members_user_id")
|
||
@@schema("platform_schema")
|
||
}
|
||
|
||
model tenant_modules {
|
||
id String @id
|
||
tenant_id String
|
||
module_code String
|
||
is_enabled Boolean @default(true)
|
||
expires_at DateTime?
|
||
created_at DateTime @default(now())
|
||
tenants tenants @relation(fields: [tenant_id], references: [id], onDelete: Cascade)
|
||
|
||
@@unique([tenant_id, module_code])
|
||
@@schema("platform_schema")
|
||
}
|
||
|
||
model tenant_quota_allocations {
|
||
id Int @id @default(autoincrement())
|
||
tenant_id String
|
||
target_type String
|
||
target_key String
|
||
limit_amount BigInt
|
||
used_amount BigInt @default(0)
|
||
created_at DateTime @default(now())
|
||
updated_at DateTime
|
||
tenants tenants @relation(fields: [tenant_id], references: [id], onDelete: Cascade)
|
||
|
||
@@unique([tenant_id, target_type, target_key])
|
||
@@index([tenant_id], map: "idx_quota_allocations_tenant_id")
|
||
@@schema("platform_schema")
|
||
}
|
||
|
||
model tenant_quotas {
|
||
id String @id
|
||
tenant_id String
|
||
quota_type String
|
||
total_amount BigInt
|
||
used_amount BigInt @default(0)
|
||
reset_period String?
|
||
last_reset_at DateTime?
|
||
created_at DateTime @default(now())
|
||
updated_at DateTime
|
||
tenants tenants @relation(fields: [tenant_id], references: [id], onDelete: Cascade)
|
||
|
||
@@unique([tenant_id, quota_type])
|
||
@@schema("platform_schema")
|
||
}
|
||
|
||
model tenants {
|
||
id String @id
|
||
code String @unique
|
||
name String
|
||
type TenantType
|
||
status TenantStatus @default(ACTIVE)
|
||
config Json? @default("{}")
|
||
total_quota BigInt @default(0)
|
||
used_quota BigInt @default(0)
|
||
contact_name String?
|
||
contact_phone String?
|
||
contact_email String?
|
||
created_at DateTime @default(now())
|
||
updated_at DateTime
|
||
expires_at DateTime?
|
||
departments departments[]
|
||
tenant_members tenant_members[]
|
||
tenant_modules tenant_modules[]
|
||
tenant_quota_allocations tenant_quota_allocations[]
|
||
tenant_quotas tenant_quotas[]
|
||
users User[]
|
||
|
||
@@index([code], map: "idx_tenants_code")
|
||
@@index([status], map: "idx_tenants_status")
|
||
@@index([type], map: "idx_tenants_type")
|
||
@@schema("platform_schema")
|
||
}
|
||
|
||
model verification_codes {
|
||
id Int @id @default(autoincrement())
|
||
phone String
|
||
code String @db.VarChar(6)
|
||
type VerificationType
|
||
expires_at DateTime
|
||
is_used Boolean @default(false)
|
||
attempts Int @default(0)
|
||
created_at DateTime @default(now())
|
||
|
||
@@index([expires_at], map: "idx_verification_codes_expires")
|
||
@@index([phone, type, is_used], map: "idx_verification_codes_phone_type")
|
||
@@schema("platform_schema")
|
||
}
|
||
|
||
enum job_state {
|
||
created
|
||
retry
|
||
active
|
||
completed
|
||
cancelled
|
||
failed
|
||
|
||
@@schema("platform_schema")
|
||
}
|
||
|
||
enum TenantStatus {
|
||
ACTIVE
|
||
SUSPENDED
|
||
EXPIRED
|
||
|
||
@@schema("platform_schema")
|
||
}
|
||
|
||
enum TenantType {
|
||
HOSPITAL // 医院
|
||
PHARMA // 药企
|
||
INTERNAL // 内部(公司自己)
|
||
PUBLIC // 个人用户公共池
|
||
|
||
@@schema("platform_schema")
|
||
}
|
||
|
||
enum UserRole {
|
||
SUPER_ADMIN
|
||
PROMPT_ENGINEER
|
||
HOSPITAL_ADMIN
|
||
PHARMA_ADMIN
|
||
DEPARTMENT_ADMIN
|
||
USER
|
||
|
||
@@schema("platform_schema")
|
||
}
|
||
|
||
enum VerificationType {
|
||
LOGIN
|
||
RESET_PASSWORD
|
||
BIND_PHONE
|
||
|
||
@@schema("platform_schema")
|
||
}
|
||
|
||
// ============================================
|
||
// Prompt Management System (capability_schema)
|
||
// ============================================
|
||
|
||
/// Prompt模板 - 存储Prompt的元信息
|
||
model prompt_templates {
|
||
id Int @id @default(autoincrement())
|
||
code String @unique /// 唯一标识符,如 'RVW_EDITORIAL'
|
||
name String /// 人类可读名称,如 "稿约规范性评估"
|
||
module String /// 所属模块: RVW, ASL, DC, IIT, PKB, AIA
|
||
description String? /// 描述
|
||
variables Json? /// 预期变量列表,如 ["title", "abstract"]
|
||
|
||
versions prompt_versions[]
|
||
|
||
created_at DateTime @default(now())
|
||
updated_at DateTime @updatedAt
|
||
|
||
@@index([module], map: "idx_prompt_templates_module")
|
||
@@map("prompt_templates")
|
||
@@schema("capability_schema")
|
||
}
|
||
|
||
/// Prompt版本 - 存储Prompt的具体内容和版本历史
|
||
model prompt_versions {
|
||
id Int @id @default(autoincrement())
|
||
template_id Int /// 关联的模板ID
|
||
version Int /// 版本号: 1, 2, 3...
|
||
content String @db.Text /// Prompt内容(支持Handlebars模板)
|
||
model_config Json? /// 模型参数: {"temperature": 0.3, "model": "deepseek-v3"}
|
||
status PromptStatus @default(DRAFT)
|
||
changelog String? /// 变更说明
|
||
created_by String? /// 修改人userId(审计用)
|
||
|
||
template prompt_templates @relation(fields: [template_id], references: [id])
|
||
|
||
created_at DateTime @default(now())
|
||
|
||
@@index([template_id, status], map: "idx_prompt_versions_template_status")
|
||
@@index([status], map: "idx_prompt_versions_status")
|
||
@@map("prompt_versions")
|
||
@@schema("capability_schema")
|
||
}
|
||
|
||
/// Prompt状态枚举
|
||
enum PromptStatus {
|
||
DRAFT /// 草稿(仅调试者可见)
|
||
ACTIVE /// 线上生效(默认可见)
|
||
ARCHIVED /// 归档
|
||
|
||
@@schema("capability_schema")
|
||
}
|