Features:
- Backend: SystemKbService with full CRUD (knowledge bases + documents)
- Backend: 8 RESTful API endpoints (list/detail/create/update/delete/upload/download)
- Backend: OSS storage integration (system/knowledge-bases/{kbId}/{docId})
- Backend: RAG engine integration (document parsing, chunking, vectorization)
- Frontend: SystemKbListPage with card-based layout
- Frontend: SystemKbDetailPage with document management table
- Frontend: Master-Detail UX pattern for better user experience
- Document upload (single/batch), download (preserving original filename), delete
Technical:
- Database migration for system_knowledge_bases and system_kb_documents tables
- OSSAdapter.getSignedUrl with Content-Disposition for original filename
- Reuse RAG engine from common/rag for document processing
Tested: Local environment verified, all features working
1771 lines
74 KiB
Plaintext
1771 lines
74 KiB
Plaintext
generator client {
|
||
provider = "prisma-client-js"
|
||
previewFeatures = ["multiSchema"]
|
||
}
|
||
|
||
datasource db {
|
||
provider = "postgresql"
|
||
url = env("DATABASE_URL")
|
||
schemas = ["admin_schema", "agent_schema", "aia_schema", "asl_schema", "capability_schema", "common_schema", "dc_schema", "ekb_schema", "iit_schema", "pkb_schema", "platform_schema", "protocol_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[]
|
||
user_modules user_modules[]
|
||
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")
|
||
// V2.1 新增字段
|
||
thinkingContent String? @map("thinking_content") @db.Text // 深度思考内容 <think>...</think>
|
||
attachments Json? @db.JsonB // 附件数组(上限5个,单个≤20MB,提取文本≤30K tokens)
|
||
|
||
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")
|
||
storageKey String @map("dify_document_id") // 原 difyDocumentId,现存储 OSS 路径
|
||
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([storageKey], 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")
|
||
}
|
||
|
||
// GeneralConversation 和 GeneralMessage 已删除(2026-01-11)
|
||
// 原因:与 Conversation/Message 功能重叠,使用 conversations.project_id = NULL 表示无项目对话
|
||
|
||
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")
|
||
// 注意:userId 暂不添加外键约束,因为用户来自不同 schema (platform_schema.users)
|
||
// 跨 schema 外键在 PostgreSQL 中需要特殊处理
|
||
|
||
@@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")
|
||
}
|
||
|
||
/// 智能文献检索任务(DeepSearch)
|
||
model AslResearchTask {
|
||
id String @id @default(uuid())
|
||
|
||
// 关联
|
||
projectId String @map("project_id")
|
||
userId String @map("user_id")
|
||
|
||
// 检索输入
|
||
query String // 用户的自然语言查询
|
||
filters Json? // 🔜 后续:高级筛选 { yearFrom, yearTo, articleTypes }
|
||
|
||
// unifuncs 任务
|
||
externalTaskId String? @map("external_task_id")
|
||
|
||
// 状态
|
||
status String @default("pending") // pending/processing/completed/failed
|
||
errorMessage String? @map("error_message")
|
||
|
||
// 结果
|
||
resultCount Int? @map("result_count")
|
||
rawResult String? @map("raw_result") @db.Text
|
||
reasoningContent String? @map("reasoning_content") @db.Text // AI思考过程
|
||
literatures Json? // 解析后的文献列表
|
||
|
||
// 统计(🔜 后续展示)
|
||
tokenUsage Json? @map("token_usage")
|
||
searchCount Int? @map("search_count")
|
||
readCount Int? @map("read_count")
|
||
iterations Int?
|
||
|
||
// 时间
|
||
createdAt DateTime @default(now()) @map("created_at")
|
||
updatedAt DateTime @updatedAt @map("updated_at")
|
||
completedAt DateTime? @map("completed_at")
|
||
|
||
@@index([projectId], map: "idx_research_tasks_project_id")
|
||
@@index([userId], map: "idx_research_tasks_user_id")
|
||
@@index([status], map: "idx_research_tasks_status")
|
||
@@index([createdAt], map: "idx_research_tasks_created_at")
|
||
@@map("research_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.userId 现在不再引用此表
|
||
|
||
@@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")
|
||
}
|
||
|
||
/// 运营日志表 (MVP V3.1)
|
||
/// 用于记录用户行为,支持 DAU/DAT 统计和用户360画像
|
||
model simple_logs {
|
||
id String @id @default(dbgenerated("gen_random_uuid()")) @db.Uuid
|
||
created_at DateTime @default(now())
|
||
|
||
// 租户和用户信息
|
||
tenant_id String @db.VarChar(50)
|
||
tenant_name String? @db.VarChar(100) // 冗余字段,避免JOIN
|
||
user_id String @db.Uuid
|
||
user_name String? @db.VarChar(50)
|
||
|
||
// 行为记录
|
||
module String @db.VarChar(20) // AIA, PKB, ASL, DC, RVW, IIT, SSA, ST, SYSTEM
|
||
feature String @db.VarChar(50) // 细分功能
|
||
action String @db.VarChar(20) // LOGIN, USE, EXPORT, CREATE, DELETE, ERROR
|
||
|
||
// 详情信息
|
||
info String? @db.Text // JSON或文本详情
|
||
|
||
@@index([created_at], map: "idx_simple_logs_created_at")
|
||
@@index([tenant_id], map: "idx_simple_logs_tenant_id")
|
||
@@index([user_id], map: "idx_simple_logs_user_id")
|
||
@@index([module, feature], map: "idx_simple_logs_module_feature")
|
||
@@index([action], map: "idx_simple_logs_action")
|
||
@@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 modules {
|
||
code String @id // 模块代码: RVW, PKB, ASL, DC, IIT, AIA
|
||
name String // 显示名称
|
||
description String? // 模块描述
|
||
icon String? // 图标(可选)
|
||
is_active Boolean @default(true) // 是否上线
|
||
sort_order Int @default(0) // 排序
|
||
created_at DateTime @default(now())
|
||
updated_at DateTime @updatedAt
|
||
|
||
@@index([is_active])
|
||
@@index([sort_order])
|
||
@@schema("platform_schema")
|
||
}
|
||
|
||
/// 租户模块订阅表
|
||
model tenant_modules {
|
||
id String @id @default(uuid())
|
||
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])
|
||
@@index([tenant_id])
|
||
@@index([module_code])
|
||
@@schema("platform_schema")
|
||
}
|
||
|
||
/// 用户模块权限表(精细控制用户可访问的模块)
|
||
model user_modules {
|
||
id String @id @default(uuid())
|
||
user_id String
|
||
tenant_id String /// 在哪个租户内的权限
|
||
module_code String /// 模块代码: RVW, PKB, ASL, DC, IIT, AIA
|
||
is_enabled Boolean @default(true) /// 是否启用
|
||
created_at DateTime @default(now())
|
||
updated_at DateTime @updatedAt
|
||
|
||
user User @relation(fields: [user_id], references: [id], onDelete: Cascade)
|
||
tenant tenants @relation(fields: [tenant_id], references: [id], onDelete: Cascade)
|
||
|
||
@@unique([user_id, tenant_id, module_code])
|
||
@@index([user_id])
|
||
@@index([tenant_id])
|
||
@@index([module_code])
|
||
@@map("user_modules")
|
||
@@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[]
|
||
user_modules user_modules[]
|
||
|
||
@@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"]
|
||
knowledge_config Json? @map("knowledge_config") /// 知识库增强配置
|
||
|
||
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")
|
||
}
|
||
|
||
/// 系统知识库 - 运营管理的公共知识库,供 Prompt 引用
|
||
model system_knowledge_bases {
|
||
id String @id @default(uuid())
|
||
code String @unique @db.VarChar(50) /// 唯一编码,如 'CONSORT_2010'
|
||
name String @db.VarChar(100) /// 名称,如 'CONSORT 2010 声明'
|
||
description String? @db.Text /// 描述
|
||
category String? @db.VarChar(50) /// 分类: methodology, statistics, crf
|
||
document_count Int @default(0) @map("document_count") /// 文档数量
|
||
total_tokens Int @default(0) @map("total_tokens") /// 总 Token 数
|
||
status String @default("active") @db.VarChar(20) /// 状态: active, archived
|
||
|
||
documents system_kb_documents[]
|
||
|
||
created_at DateTime @default(now()) @map("created_at")
|
||
updated_at DateTime @updatedAt @map("updated_at")
|
||
|
||
@@index([category], map: "idx_system_kb_category")
|
||
@@index([status], map: "idx_system_kb_status")
|
||
@@map("system_knowledge_bases")
|
||
@@schema("capability_schema")
|
||
}
|
||
|
||
/// 系统知识库文档 - 知识库中的文档
|
||
model system_kb_documents {
|
||
id String @id @default(uuid())
|
||
kb_id String @map("kb_id") /// 所属知识库ID
|
||
filename String @db.VarChar(255) /// 原始文件名
|
||
file_path String? @db.VarChar(500) @map("file_path") /// OSS 存储路径
|
||
file_size Int? @map("file_size") /// 文件大小(字节)
|
||
file_type String? @db.VarChar(50) @map("file_type") /// 文件类型: pdf, docx, md, txt
|
||
content String? @db.Text /// 解析后的文本内容
|
||
token_count Int @default(0) @map("token_count") /// Token 数量
|
||
status String @default("pending") @db.VarChar(20) /// 状态: pending, processing, ready, failed
|
||
error_message String? @db.Text @map("error_message") /// 错误信息
|
||
|
||
knowledge_base system_knowledge_bases @relation(fields: [kb_id], references: [id], onDelete: Cascade)
|
||
|
||
created_at DateTime @default(now()) @map("created_at")
|
||
updated_at DateTime @updatedAt @map("updated_at")
|
||
|
||
@@index([kb_id], map: "idx_system_kb_docs_kb_id")
|
||
@@index([status], map: "idx_system_kb_docs_status")
|
||
@@map("system_kb_documents")
|
||
@@schema("capability_schema")
|
||
}
|
||
|
||
// ============================================================
|
||
// EKB Schema - 知识库引擎 (Enterprise Knowledge Base)
|
||
// 参考文档: docs/02-通用能力层/03-RAG引擎/04-数据模型设计.md
|
||
// ============================================================
|
||
|
||
/// 知识库容器表 - 管理知识库的归属和策略配置
|
||
model EkbKnowledgeBase {
|
||
id String @id @default(uuid())
|
||
name String /// 知识库名称
|
||
description String? /// 描述
|
||
|
||
/// 核心隔离字段
|
||
/// USER: 用户私有,ownerId = userId
|
||
/// SYSTEM: 系统公共,ownerId = moduleId (如 "ASL", "AIA")
|
||
type String @default("USER") /// USER | SYSTEM
|
||
ownerId String @map("owner_id") /// userId 或 moduleId
|
||
|
||
/// 策略配置 (JSONB)
|
||
/// { chunkSize, topK, enableRerank, embeddingModel }
|
||
config Json? @db.JsonB
|
||
|
||
documents EkbDocument[]
|
||
|
||
createdAt DateTime @default(now()) @map("created_at")
|
||
updatedAt DateTime @updatedAt @map("updated_at")
|
||
|
||
@@index([ownerId], map: "idx_ekb_kb_owner")
|
||
@@index([type], map: "idx_ekb_kb_type")
|
||
@@map("ekb_knowledge_base")
|
||
@@schema("ekb_schema")
|
||
}
|
||
|
||
/// 文档表 - 存储上传的文档及其元数据
|
||
model EkbDocument {
|
||
id String @id @default(uuid())
|
||
kbId String @map("kb_id") /// 所属知识库
|
||
userId String @map("user_id") /// 上传者(冗余存储)
|
||
|
||
// ===== Layer 1: 基础信息(必须)=====
|
||
filename String /// 文件名
|
||
fileType String @map("file_type") /// pdf, docx, pptx, xlsx, md, txt
|
||
fileSizeBytes BigInt @map("file_size_bytes") /// 文件大小(字节)
|
||
fileUrl String @map("file_url") /// OSS 存储路径
|
||
fileHash String? @map("file_hash") /// SHA256 哈希(秒传去重)
|
||
status String @default("pending") /// pending, processing, completed, failed
|
||
errorMessage String? @map("error_message") @db.Text
|
||
|
||
// ===== Layer 0: RAG 核心(必须)=====
|
||
extractedText String? @map("extracted_text") @db.Text /// Markdown 全文
|
||
|
||
// ===== Layer 2: 内容增强(可选)=====
|
||
summary String? @db.Text /// AI 摘要
|
||
tokenCount Int? @map("token_count") /// Token 数量
|
||
pageCount Int? @map("page_count") /// 页数
|
||
|
||
// ===== Layer 3: 分类标签(可选)=====
|
||
contentType String? @map("content_type") /// 内容类型
|
||
tags String[] /// 用户标签
|
||
category String? /// 分类目录
|
||
|
||
// ===== Layer 4: 结构化数据(可选)=====
|
||
metadata Json? @db.JsonB /// 文献属性 JSONB
|
||
structuredData Json? @map("structured_data") @db.JsonB /// 类型特定数据 JSONB
|
||
|
||
// ===== 关联 =====
|
||
knowledgeBase EkbKnowledgeBase @relation(fields: [kbId], references: [id], onDelete: Cascade)
|
||
chunks EkbChunk[]
|
||
|
||
createdAt DateTime @default(now()) @map("created_at")
|
||
updatedAt DateTime @updatedAt @map("updated_at")
|
||
|
||
@@index([kbId], map: "idx_ekb_doc_kb")
|
||
@@index([userId], map: "idx_ekb_doc_user")
|
||
@@index([status], map: "idx_ekb_doc_status")
|
||
@@index([contentType], map: "idx_ekb_doc_content_type")
|
||
@@index([fileHash], map: "idx_ekb_doc_file_hash")
|
||
@@map("ekb_document")
|
||
@@schema("ekb_schema")
|
||
}
|
||
|
||
/// 切片表 - 存储文档切片和向量嵌入
|
||
model EkbChunk {
|
||
id String @id @default(uuid())
|
||
documentId String @map("document_id") /// 所属文档
|
||
|
||
// ===== 核心内容 =====
|
||
content String @db.Text /// 切片文本(Markdown)
|
||
chunkIndex Int @map("chunk_index") /// 切片序号(从 0 开始)
|
||
|
||
// ===== 向量 =====
|
||
/// pgvector 1024 维向量
|
||
/// 注意:需要手动创建 HNSW 索引
|
||
embedding Unsupported("vector(1024)")?
|
||
|
||
// ===== 溯源信息(可选)=====
|
||
pageNumber Int? @map("page_number") /// 页码(PDF 溯源)
|
||
sectionType String? @map("section_type") /// 章节类型
|
||
|
||
// ===== 扩展元数据(可选)=====
|
||
metadata Json? @db.JsonB /// 切片级元数据 JSONB
|
||
|
||
document EkbDocument @relation(fields: [documentId], references: [id], onDelete: Cascade)
|
||
|
||
createdAt DateTime @default(now()) @map("created_at")
|
||
|
||
@@index([documentId], map: "idx_ekb_chunk_doc")
|
||
@@map("ekb_chunk")
|
||
@@schema("ekb_schema")
|
||
}
|
||
|
||
// ============================================================
|
||
// Agent Framework Schema (agent_schema)
|
||
// 通用Agent框架 - 可复用于多种Agent类型
|
||
// ============================================================
|
||
|
||
/// Agent定义表 - 存储Agent的基本配置
|
||
model AgentDefinition {
|
||
id String @id @default(uuid())
|
||
code String @unique /// 唯一标识: protocol_agent, stat_agent
|
||
name String /// 显示名称
|
||
description String? /// 描述
|
||
version String @default("1.0.0") /// 版本号
|
||
|
||
/// Agent配置
|
||
config Json? @db.JsonB /// 全局配置 { defaultModel, maxTurns, timeout }
|
||
|
||
/// 状态
|
||
isActive Boolean @default(true) @map("is_active")
|
||
|
||
/// 关联
|
||
stages AgentStage[]
|
||
prompts AgentPrompt[]
|
||
sessions AgentSession[]
|
||
reflexionRules ReflexionRule[]
|
||
|
||
createdAt DateTime @default(now()) @map("created_at")
|
||
updatedAt DateTime @updatedAt @map("updated_at")
|
||
|
||
@@index([code], map: "idx_agent_def_code")
|
||
@@index([isActive], map: "idx_agent_def_active")
|
||
@@map("agent_definitions")
|
||
@@schema("agent_schema")
|
||
}
|
||
|
||
/// Agent阶段配置表 - 定义Agent的工作流阶段
|
||
model AgentStage {
|
||
id String @id @default(uuid())
|
||
agentId String @map("agent_id") /// 关联的Agent
|
||
|
||
/// 阶段标识
|
||
stageCode String @map("stage_code") /// 阶段代码: scientific_question, pico
|
||
stageName String @map("stage_name") /// 阶段名称: 科学问题梳理
|
||
sortOrder Int @map("sort_order") /// 排序顺序
|
||
|
||
/// 阶段配置
|
||
config Json? @db.JsonB /// 阶段特定配置
|
||
|
||
/// 状态机配置
|
||
nextStages String[] @map("next_stages") /// 可转换的下一阶段列表
|
||
isInitial Boolean @default(false) @map("is_initial") /// 是否为起始阶段
|
||
isFinal Boolean @default(false) @map("is_final") /// 是否为结束阶段
|
||
|
||
/// 关联
|
||
agent AgentDefinition @relation(fields: [agentId], references: [id], onDelete: Cascade)
|
||
prompts AgentPrompt[]
|
||
|
||
createdAt DateTime @default(now()) @map("created_at")
|
||
updatedAt DateTime @updatedAt @map("updated_at")
|
||
|
||
@@unique([agentId, stageCode], map: "unique_agent_stage")
|
||
@@index([agentId], map: "idx_agent_stage_agent")
|
||
@@index([sortOrder], map: "idx_agent_stage_order")
|
||
@@map("agent_stages")
|
||
@@schema("agent_schema")
|
||
}
|
||
|
||
/// Agent Prompt模板表 - 存储各阶段的Prompt
|
||
model AgentPrompt {
|
||
id String @id @default(uuid())
|
||
agentId String @map("agent_id") /// 关联的Agent
|
||
stageId String? @map("stage_id") /// 关联的阶段(可选,null表示通用Prompt)
|
||
|
||
/// Prompt标识
|
||
promptType String @map("prompt_type") /// system, stage, extraction, reflexion
|
||
promptCode String @map("prompt_code") /// 唯一代码
|
||
|
||
/// Prompt内容
|
||
content String @db.Text /// Prompt模板内容(支持变量)
|
||
variables String[] /// 预期变量列表
|
||
|
||
/// 版本控制
|
||
version Int @default(1) /// 版本号
|
||
isActive Boolean @default(true) @map("is_active")
|
||
|
||
/// 关联
|
||
agent AgentDefinition @relation(fields: [agentId], references: [id], onDelete: Cascade)
|
||
stage AgentStage? @relation(fields: [stageId], references: [id], onDelete: SetNull)
|
||
|
||
createdAt DateTime @default(now()) @map("created_at")
|
||
updatedAt DateTime @updatedAt @map("updated_at")
|
||
|
||
@@unique([agentId, promptCode, version], map: "unique_agent_prompt_version")
|
||
@@index([agentId, promptType], map: "idx_agent_prompt_type")
|
||
@@index([stageId], map: "idx_agent_prompt_stage")
|
||
@@map("agent_prompts")
|
||
@@schema("agent_schema")
|
||
}
|
||
|
||
/// Agent会话表 - 存储Agent的运行时会话状态
|
||
model AgentSession {
|
||
id String @id @default(uuid())
|
||
agentId String @map("agent_id") /// 关联的Agent定义
|
||
conversationId String @map("conversation_id") /// 关联的对话ID(aia_schema.conversations)
|
||
userId String @map("user_id") /// 用户ID
|
||
|
||
/// 当前状态
|
||
currentStage String @map("current_stage") /// 当前阶段代码
|
||
status String @default("active") /// active, completed, paused, error
|
||
|
||
/// 上下文数据(具体Agent的上下文存储在对应schema)
|
||
contextRef String? @map("context_ref") /// 上下文引用ID(如protocol_schema.protocol_contexts.id)
|
||
|
||
/// 统计信息
|
||
turnCount Int @default(0) @map("turn_count") /// 对话轮数
|
||
totalTokens Int @default(0) @map("total_tokens") /// 总Token数
|
||
|
||
/// 关联
|
||
agent AgentDefinition @relation(fields: [agentId], references: [id])
|
||
traces AgentTrace[]
|
||
|
||
createdAt DateTime @default(now()) @map("created_at")
|
||
updatedAt DateTime @updatedAt @map("updated_at")
|
||
|
||
@@unique([conversationId], map: "unique_agent_session_conv")
|
||
@@index([agentId], map: "idx_agent_session_agent")
|
||
@@index([userId], map: "idx_agent_session_user")
|
||
@@index([status], map: "idx_agent_session_status")
|
||
@@map("agent_sessions")
|
||
@@schema("agent_schema")
|
||
}
|
||
|
||
/// Agent执行追踪表 - 记录每一步的执行详情
|
||
model AgentTrace {
|
||
id String @id @default(uuid())
|
||
sessionId String @map("session_id") /// 关联的会话
|
||
|
||
/// 追踪信息
|
||
traceId String @map("trace_id") /// 请求追踪ID(用于关联日志)
|
||
stepIndex Int @map("step_index") /// 步骤序号
|
||
stepType String @map("step_type") /// query, plan, execute, reflect, tool_call
|
||
|
||
/// 输入输出
|
||
input Json? @db.JsonB /// 步骤输入
|
||
output Json? @db.JsonB /// 步骤输出
|
||
|
||
/// 执行信息
|
||
stageCode String? @map("stage_code") /// 执行时的阶段
|
||
modelUsed String? @map("model_used") /// 使用的模型
|
||
tokensUsed Int? @map("tokens_used") /// 消耗的Token
|
||
durationMs Int? @map("duration_ms") /// 执行时长(毫秒)
|
||
|
||
/// 错误信息
|
||
errorType String? @map("error_type") /// 错误类型
|
||
errorMsg String? @map("error_msg") /// 错误信息
|
||
|
||
/// 关联
|
||
session AgentSession @relation(fields: [sessionId], references: [id], onDelete: Cascade)
|
||
|
||
createdAt DateTime @default(now()) @map("created_at")
|
||
|
||
@@index([sessionId, stepIndex], map: "idx_agent_trace_session_step")
|
||
@@index([traceId], map: "idx_agent_trace_trace_id")
|
||
@@index([stepType], map: "idx_agent_trace_step_type")
|
||
@@map("agent_traces")
|
||
@@schema("agent_schema")
|
||
}
|
||
|
||
/// Reflexion规则表 - 定义质量检查规则
|
||
model ReflexionRule {
|
||
id String @id @default(uuid())
|
||
agentId String @map("agent_id") /// 关联的Agent
|
||
|
||
/// 规则标识
|
||
ruleCode String @map("rule_code") /// 规则代码
|
||
ruleName String @map("rule_name") /// 规则名称
|
||
|
||
/// 触发条件
|
||
triggerStage String? @map("trigger_stage") /// 触发阶段(null表示全局)
|
||
triggerTiming String @map("trigger_timing") /// on_sync, on_stage_complete, on_generate
|
||
|
||
/// 规则类型
|
||
ruleType String @map("rule_type") /// rule_based, prompt_based
|
||
|
||
/// 规则内容
|
||
conditions Json? @db.JsonB /// 规则条件(rule_based时使用)
|
||
promptTemplate String? @map("prompt_template") @db.Text /// Prompt模板(prompt_based时使用)
|
||
|
||
/// 行为配置
|
||
severity String @default("warning") /// error, warning, info
|
||
failureAction String @default("warn") @map("failure_action") /// block, warn, log
|
||
|
||
/// 状态
|
||
isActive Boolean @default(true) @map("is_active")
|
||
sortOrder Int @default(0) @map("sort_order")
|
||
|
||
/// 关联
|
||
agent AgentDefinition @relation(fields: [agentId], references: [id], onDelete: Cascade)
|
||
|
||
createdAt DateTime @default(now()) @map("created_at")
|
||
updatedAt DateTime @updatedAt @map("updated_at")
|
||
|
||
@@unique([agentId, ruleCode], map: "unique_agent_rule")
|
||
@@index([agentId, triggerStage], map: "idx_reflexion_rule_agent_stage")
|
||
@@index([isActive], map: "idx_reflexion_rule_active")
|
||
@@map("reflexion_rules")
|
||
@@schema("agent_schema")
|
||
}
|
||
|
||
// ============================================================
|
||
// Protocol Agent Schema (protocol_schema)
|
||
// Protocol Agent专用 - 研究方案制定
|
||
// ============================================================
|
||
|
||
/// Protocol Context表 - 存储研究方案的核心上下文数据
|
||
model ProtocolContext {
|
||
id String @id @default(uuid())
|
||
conversationId String @unique @map("conversation_id") /// 关联的对话ID
|
||
userId String @map("user_id")
|
||
|
||
/// 当前状态
|
||
currentStage String @default("scientific_question") @map("current_stage")
|
||
status String @default("in_progress") /// in_progress, completed, abandoned
|
||
|
||
/// ===== 5个核心阶段数据 =====
|
||
|
||
/// 阶段1: 科学问题
|
||
scientificQuestion Json? @map("scientific_question") @db.JsonB
|
||
/// { content, background, significance, confirmed, confirmedAt }
|
||
|
||
/// 阶段2: PICO
|
||
pico Json? @db.JsonB
|
||
/// { P: {value, details}, I: {}, C: {}, O: {}, confirmed, confirmedAt }
|
||
|
||
/// 阶段3: 研究设计
|
||
studyDesign Json? @map("study_design") @db.JsonB
|
||
/// { type, blinding, randomization, duration, multiCenter, confirmed }
|
||
|
||
/// 阶段4: 样本量
|
||
sampleSize Json? @map("sample_size") @db.JsonB
|
||
/// { total, perGroup, alpha, power, effectSize, dropoutRate, justification, confirmed }
|
||
|
||
/// 阶段5: 观察指标(终点指标)
|
||
endpoints Json? @db.JsonB
|
||
/// { primary: [{name, definition, method, timePoint}], secondary: [], safety: [], confirmed }
|
||
|
||
/// ===== 元数据 =====
|
||
completedStages String[] @default([]) @map("completed_stages") /// 已完成的阶段列表
|
||
lastActiveAt DateTime @default(now()) @map("last_active_at")
|
||
|
||
/// 关联
|
||
generations ProtocolGeneration[]
|
||
|
||
createdAt DateTime @default(now()) @map("created_at")
|
||
updatedAt DateTime @updatedAt @map("updated_at")
|
||
|
||
@@index([userId], map: "idx_protocol_context_user")
|
||
@@index([status], map: "idx_protocol_context_status")
|
||
@@index([currentStage], map: "idx_protocol_context_stage")
|
||
@@map("protocol_contexts")
|
||
@@schema("protocol_schema")
|
||
}
|
||
|
||
/// Protocol生成记录表 - 存储一键生成的研究方案
|
||
model ProtocolGeneration {
|
||
id String @id @default(uuid())
|
||
contextId String @map("context_id") /// 关联的Context
|
||
userId String @map("user_id")
|
||
|
||
/// 生成内容
|
||
generatedContent String @map("generated_content") @db.Text /// 生成的研究方案全文(Markdown)
|
||
contentVersion Int @default(1) @map("content_version") /// 版本号
|
||
|
||
/// 使用的Prompt
|
||
promptUsed String @map("prompt_used") @db.Text /// 实际使用的Prompt
|
||
|
||
/// 生成参数
|
||
modelUsed String @map("model_used") /// 使用的模型
|
||
tokensUsed Int? @map("tokens_used") /// 消耗的Token
|
||
durationMs Int? @map("duration_ms") /// 生成耗时(毫秒)
|
||
|
||
/// 导出记录
|
||
wordFileKey String? @map("word_file_key") /// Word文件OSS Key
|
||
lastExportedAt DateTime? @map("last_exported_at")
|
||
|
||
/// 状态
|
||
status String @default("completed") /// generating, completed, failed
|
||
errorMessage String? @map("error_message")
|
||
|
||
/// 关联
|
||
context ProtocolContext @relation(fields: [contextId], references: [id], onDelete: Cascade)
|
||
|
||
createdAt DateTime @default(now()) @map("created_at")
|
||
updatedAt DateTime @updatedAt @map("updated_at")
|
||
|
||
@@index([contextId], map: "idx_protocol_gen_context")
|
||
@@index([userId, createdAt], map: "idx_protocol_gen_user_time")
|
||
@@map("protocol_generations")
|
||
@@schema("protocol_schema")
|
||
}
|