Major fixes: - Fix pivot transformation with special characters in column names - Fix compute column validation for Chinese punctuation - Fix recode dialog to fetch unique values from full dataset via new API - Add column mapping mechanism to handle special characters Database migration: - Add column_mapping field to dc_tool_c_sessions table - Migration file: 20251208_add_column_mapping UX improvements: - Darken table grid lines for better visibility - Reduce column width by 40% with tooltip support - Insert new columns next to source columns - Preserve original row order after operations - Add notice about 50-row preview limit Modified files: - Backend: SessionService, SessionController, QuickActionService, routes - Python: pivot.py, compute.py, recode.py, binning.py, conditional.py - Frontend: DataGrid, RecodeDialog, index.tsx, ag-grid-custom.css - Database: schema.prisma, migration SQL Status: Code complete, database migrated, ready for testing
907 lines
32 KiB
Plaintext
907 lines
32 KiB
Plaintext
// This is your Prisma schema file,
|
||
// learn more about it in the docs: https://pris.ly/d/prisma-schema
|
||
|
||
generator client {
|
||
provider = "prisma-client-js"
|
||
}
|
||
|
||
datasource db {
|
||
provider = "postgresql"
|
||
url = env("DATABASE_URL")
|
||
schemas = ["platform_schema", "aia_schema", "pkb_schema", "asl_schema", "common_schema", "dc_schema", "rvw_schema", "admin_schema", "ssa_schema", "st_schema", "public"]
|
||
}
|
||
|
||
// ==================== 用户模块 ====================
|
||
|
||
model User {
|
||
id String @id @default(uuid())
|
||
email String @unique
|
||
password String
|
||
name String?
|
||
avatarUrl String? @map("avatar_url")
|
||
|
||
role String @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")
|
||
|
||
projects Project[]
|
||
conversations Conversation[]
|
||
knowledgeBases KnowledgeBase[]
|
||
documents Document[]
|
||
adminLogs AdminLog[]
|
||
generalConversations GeneralConversation[]
|
||
batchTasks BatchTask[] // Phase 3: 批处理任务
|
||
taskTemplates TaskTemplate[] // Phase 3: 任务模板
|
||
reviewTasks ReviewTask[] // 稿件审查任务
|
||
aslProjects AslScreeningProject[] @relation("AslProjects") // ASL智能文献项目
|
||
|
||
@@index([email])
|
||
@@index([status])
|
||
@@index([createdAt])
|
||
@@map("users")
|
||
@@schema("platform_schema")
|
||
}
|
||
|
||
// ==================== 项目模块 ====================
|
||
|
||
model Project {
|
||
id String @id @default(uuid())
|
||
userId String @map("user_id")
|
||
name String
|
||
background String @default("") @db.Text
|
||
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")
|
||
|
||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||
conversations Conversation[]
|
||
|
||
@@index([userId])
|
||
@@index([createdAt])
|
||
@@index([deletedAt])
|
||
@@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")
|
||
|
||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||
project Project? @relation(fields: [projectId], references: [id], onDelete: Cascade)
|
||
messages Message[]
|
||
|
||
@@index([userId])
|
||
@@index([projectId])
|
||
@@index([agentId])
|
||
@@index([createdAt])
|
||
@@index([deletedAt])
|
||
@@map("conversations")
|
||
@@schema("aia_schema")
|
||
}
|
||
|
||
model Message {
|
||
id String @id @default(uuid())
|
||
conversationId String @map("conversation_id")
|
||
role String
|
||
content String @db.Text
|
||
model String?
|
||
metadata Json?
|
||
tokens Int?
|
||
isPinned Boolean @default(false) @map("is_pinned")
|
||
|
||
createdAt DateTime @default(now()) @map("created_at")
|
||
|
||
conversation Conversation @relation(fields: [conversationId], references: [id], onDelete: Cascade)
|
||
|
||
@@index([conversationId])
|
||
@@index([createdAt])
|
||
@@index([isPinned])
|
||
@@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")
|
||
|
||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||
documents Document[]
|
||
batchTasks BatchTask[] // Phase 3: 批处理任务
|
||
|
||
@@index([userId])
|
||
@@index([difyDatasetId])
|
||
@@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")
|
||
|
||
// Phase 2: 全文阅读模式新增字段
|
||
extractionMethod String? @map("extraction_method") // pymupdf/nougat/mammoth/direct
|
||
extractionQuality Float? @map("extraction_quality") // 0-1质量分数
|
||
charCount Int? @map("char_count") // 字符数
|
||
language String? // 检测到的语言 (chinese/english)
|
||
extractedText String? @map("extracted_text") @db.Text // 提取的文本内容
|
||
|
||
uploadedAt DateTime @default(now()) @map("uploaded_at")
|
||
processedAt DateTime? @map("processed_at")
|
||
|
||
knowledgeBase KnowledgeBase @relation(fields: [kbId], references: [id], onDelete: Cascade)
|
||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||
batchResults BatchResult[] // Phase 3: 批处理结果
|
||
|
||
@@index([kbId])
|
||
@@index([userId])
|
||
@@index([status])
|
||
@@index([difyDocumentId])
|
||
@@index([extractionMethod])
|
||
@@map("documents")
|
||
@@schema("pkb_schema")
|
||
}
|
||
|
||
// ==================== Phase 3: 批处理模块 ====================
|
||
|
||
// 批处理任务
|
||
model BatchTask {
|
||
id String @id @default(uuid())
|
||
userId String @map("user_id")
|
||
kbId String @map("kb_id")
|
||
|
||
// 任务基本信息
|
||
name String // 任务名称(用户可自定义)
|
||
templateType String @map("template_type") // 'preset' | 'custom'
|
||
templateId String? @map("template_id") // 预设模板ID(如'clinical_research')
|
||
prompt String @db.Text // 提示词(完整的)
|
||
|
||
// 执行状态
|
||
status String // 'processing' | 'completed' | 'failed' | 'paused'
|
||
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) // 固定为3
|
||
|
||
// 时间统计
|
||
startedAt DateTime? @map("started_at")
|
||
completedAt DateTime? @map("completed_at")
|
||
durationSeconds Int? @map("duration_seconds") // 执行时长(秒)
|
||
|
||
// 关联
|
||
results BatchResult[]
|
||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||
knowledgeBase KnowledgeBase @relation(fields: [kbId], references: [id], onDelete: Cascade)
|
||
|
||
createdAt DateTime @default(now()) @map("created_at")
|
||
updatedAt DateTime @updatedAt @map("updated_at")
|
||
|
||
@@index([userId])
|
||
@@index([kbId])
|
||
@@index([status])
|
||
@@index([createdAt])
|
||
@@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 // 'success' | 'failed'
|
||
data Json? // 提取的结构化数据(预设模板)或文本(自定义)
|
||
rawOutput String? @map("raw_output") @db.Text // AI原始输出(备份)
|
||
errorMessage String? @map("error_message") @db.Text // 错误信息
|
||
|
||
// 性能指标
|
||
processingTimeMs Int? @map("processing_time_ms") // 处理时长(毫秒)
|
||
tokensUsed Int? @map("tokens_used") // Token使用量
|
||
|
||
// 关联
|
||
task BatchTask @relation(fields: [taskId], references: [id], onDelete: Cascade)
|
||
document Document @relation(fields: [documentId], references: [id], onDelete: Cascade)
|
||
|
||
createdAt DateTime @default(now()) @map("created_at")
|
||
|
||
@@index([taskId])
|
||
@@index([documentId])
|
||
@@index([status])
|
||
@@map("batch_results")
|
||
@@schema("pkb_schema")
|
||
}
|
||
|
||
// 任务模板(暂不实现,预留)
|
||
model TaskTemplate {
|
||
id String @id @default(uuid())
|
||
userId String @map("user_id")
|
||
|
||
name String
|
||
description String?
|
||
prompt String @db.Text
|
||
outputFields Json // 期望的输出字段定义
|
||
isPublic Boolean @default(false) @map("is_public")
|
||
|
||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||
|
||
createdAt DateTime @default(now()) @map("created_at")
|
||
updatedAt DateTime @updatedAt @map("updated_at")
|
||
|
||
@@index([userId])
|
||
@@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 User @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")
|
||
|
||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||
messages GeneralMessage[]
|
||
|
||
@@index([userId])
|
||
@@index([createdAt])
|
||
@@index([updatedAt])
|
||
@@map("general_conversations")
|
||
@@schema("aia_schema")
|
||
}
|
||
|
||
model GeneralMessage {
|
||
id String @id @default(uuid())
|
||
conversationId String @map("conversation_id")
|
||
role String
|
||
content String @db.Text
|
||
model String?
|
||
metadata Json?
|
||
tokens Int?
|
||
|
||
createdAt DateTime @default(now()) @map("created_at")
|
||
|
||
conversation GeneralConversation @relation(fields: [conversationId], references: [id], onDelete: Cascade)
|
||
|
||
@@index([conversationId])
|
||
@@index([createdAt])
|
||
@@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") @db.Text
|
||
wordCount Int? @map("word_count")
|
||
|
||
// 执行状态
|
||
status String @default("pending")
|
||
// pending, extracting, reviewing_editorial, reviewing_methodology, completed, failed
|
||
|
||
// 评估结果(JSON)
|
||
editorialReview Json? @map("editorial_review")
|
||
methodologyReview Json? @map("methodology_review")
|
||
overallScore Float? @map("overall_score")
|
||
|
||
// 执行信息
|
||
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") @db.Text
|
||
|
||
// 元数据
|
||
createdAt DateTime @default(now()) @map("created_at")
|
||
updatedAt DateTime @updatedAt @map("updated_at")
|
||
|
||
// 关联
|
||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||
|
||
@@index([userId])
|
||
@@index([status])
|
||
@@index([createdAt])
|
||
@@map("review_tasks")
|
||
@@schema("public")
|
||
}
|
||
|
||
// ==================== ASL智能文献模块 ====================
|
||
|
||
// ASL 筛选项目表
|
||
model AslScreeningProject {
|
||
id String @id @default(uuid())
|
||
userId String @map("user_id")
|
||
user User @relation("AslProjects", fields: [userId], references: [id], onDelete: Cascade)
|
||
|
||
projectName String @map("project_name")
|
||
|
||
// PICO标准
|
||
picoCriteria Json @map("pico_criteria") // { population, intervention, comparison, outcome, studyDesign }
|
||
|
||
// 筛选标准
|
||
inclusionCriteria String @map("inclusion_criteria") @db.Text
|
||
exclusionCriteria String @map("exclusion_criteria") @db.Text
|
||
|
||
// 状态
|
||
status String @default("draft") // draft, screening, completed
|
||
|
||
// 筛选配置
|
||
screeningConfig Json? @map("screening_config") // { models: ["deepseek", "qwen"], temperature: 0 }
|
||
|
||
// 关联
|
||
literatures AslLiterature[]
|
||
screeningTasks AslScreeningTask[]
|
||
screeningResults AslScreeningResult[]
|
||
fulltextScreeningTasks AslFulltextScreeningTask[]
|
||
fulltextScreeningResults AslFulltextScreeningResult[]
|
||
|
||
createdAt DateTime @default(now()) @map("created_at")
|
||
updatedAt DateTime @updatedAt @map("updated_at")
|
||
|
||
@@index([userId])
|
||
@@index([status])
|
||
@@map("screening_projects")
|
||
@@schema("asl_schema")
|
||
}
|
||
|
||
// ASL 文献条目表
|
||
model AslLiterature {
|
||
id String @id @default(uuid())
|
||
projectId String @map("project_id")
|
||
project AslScreeningProject @relation(fields: [projectId], references: [id], onDelete: Cascade)
|
||
|
||
// 文献基本信息
|
||
pmid String?
|
||
title String @db.Text
|
||
abstract String @db.Text
|
||
authors String?
|
||
journal String?
|
||
publicationYear Int? @map("publication_year")
|
||
doi String?
|
||
|
||
// 文献阶段(生命周期管理)
|
||
stage String @default("imported") @map("stage")
|
||
// imported, title_screened, title_included, pdf_acquired, fulltext_screened, data_extracted
|
||
|
||
// 云原生存储字段(V1.0 阶段使用,MVP阶段预留)
|
||
pdfUrl String? @map("pdf_url") // PDF访问URL
|
||
pdfOssKey String? @map("pdf_oss_key") // OSS存储Key(用于删除)
|
||
pdfFileSize Int? @map("pdf_file_size") // 文件大小(字节)
|
||
|
||
// PDF存储(Dify/OSS双适配)
|
||
hasPdf Boolean @default(false) @map("has_pdf")
|
||
pdfStorageType String? @map("pdf_storage_type") // "dify" | "oss"
|
||
pdfStorageRef String? @map("pdf_storage_ref") // Dify: document_id, OSS: object_key
|
||
pdfStatus String? @map("pdf_status") // "uploading" | "ready" | "failed"
|
||
pdfUploadedAt DateTime? @map("pdf_uploaded_at")
|
||
|
||
// 全文内容存储(云原生:存储引用而非内容)
|
||
fullTextStorageType String? @map("full_text_storage_type") // "dify" | "oss"
|
||
fullTextStorageRef String? @map("full_text_storage_ref") // document_id 或 object_key
|
||
fullTextUrl String? @map("full_text_url") // 访问URL
|
||
fullTextFormat String? @map("full_text_format") // "markdown" | "plaintext"
|
||
fullTextSource String? @map("full_text_source") // "nougat" | "pymupdf"
|
||
fullTextTokenCount Int? @map("full_text_token_count")
|
||
fullTextExtractedAt DateTime? @map("full_text_extracted_at")
|
||
|
||
// 关联
|
||
screeningResults AslScreeningResult[]
|
||
fulltextScreeningResults AslFulltextScreeningResult[]
|
||
|
||
createdAt DateTime @default(now()) @map("created_at")
|
||
updatedAt DateTime @updatedAt @map("updated_at")
|
||
|
||
@@unique([projectId, pmid])
|
||
@@index([projectId])
|
||
@@index([doi])
|
||
@@index([stage])
|
||
@@index([hasPdf])
|
||
@@index([pdfStatus])
|
||
@@map("literatures")
|
||
@@schema("asl_schema")
|
||
}
|
||
|
||
// ASL 筛选结果表
|
||
model AslScreeningResult {
|
||
id String @id @default(uuid())
|
||
projectId String @map("project_id")
|
||
project AslScreeningProject @relation(fields: [projectId], references: [id], onDelete: Cascade)
|
||
literatureId String @map("literature_id")
|
||
literature AslLiterature @relation(fields: [literatureId], references: [id], onDelete: Cascade)
|
||
|
||
// DeepSeek模型判断
|
||
dsModelName String @map("ds_model_name") // "deepseek-chat"
|
||
dsPJudgment String? @map("ds_p_judgment") // "match" | "partial" | "mismatch"
|
||
dsIJudgment String? @map("ds_i_judgment")
|
||
dsCJudgment String? @map("ds_c_judgment")
|
||
dsSJudgment String? @map("ds_s_judgment")
|
||
dsConclusion String? @map("ds_conclusion") // "include" | "exclude" | "uncertain"
|
||
dsConfidence Float? @map("ds_confidence") // 0-1
|
||
|
||
// DeepSeek模型证据
|
||
dsPEvidence String? @map("ds_p_evidence") @db.Text
|
||
dsIEvidence String? @map("ds_i_evidence") @db.Text
|
||
dsCEvidence String? @map("ds_c_evidence") @db.Text
|
||
dsSEvidence String? @map("ds_s_evidence") @db.Text
|
||
dsReason String? @map("ds_reason") @db.Text
|
||
|
||
// Qwen模型判断
|
||
qwenModelName String @map("qwen_model_name") // "qwen-max"
|
||
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")
|
||
|
||
// Qwen模型证据
|
||
qwenPEvidence String? @map("qwen_p_evidence") @db.Text
|
||
qwenIEvidence String? @map("qwen_i_evidence") @db.Text
|
||
qwenCEvidence String? @map("qwen_c_evidence") @db.Text
|
||
qwenSEvidence String? @map("qwen_s_evidence") @db.Text
|
||
qwenReason String? @map("qwen_reason") @db.Text
|
||
|
||
// 冲突状态
|
||
conflictStatus String @default("none") @map("conflict_status") // "none" | "conflict" | "resolved"
|
||
conflictFields Json? @map("conflict_fields") // ["P", "I", "conclusion"]
|
||
|
||
// 最终决策
|
||
finalDecision String? @map("final_decision") // "include" | "exclude" | "pending"
|
||
finalDecisionBy String? @map("final_decision_by") // userId
|
||
finalDecisionAt DateTime? @map("final_decision_at")
|
||
exclusionReason String? @map("exclusion_reason") @db.Text
|
||
|
||
// AI处理状态
|
||
aiProcessingStatus String @default("pending") @map("ai_processing_status") // "pending" | "processing" | "completed" | "failed"
|
||
aiProcessedAt DateTime? @map("ai_processed_at")
|
||
aiErrorMessage String? @map("ai_error_message") @db.Text
|
||
|
||
// 可追溯信息
|
||
promptVersion String @default("v1.0.0") @map("prompt_version")
|
||
rawOutput Json? @map("raw_output") // 原始LLM输出(备份)
|
||
|
||
createdAt DateTime @default(now()) @map("created_at")
|
||
updatedAt DateTime @updatedAt @map("updated_at")
|
||
|
||
@@unique([projectId, literatureId])
|
||
@@index([projectId])
|
||
@@index([literatureId])
|
||
@@index([conflictStatus])
|
||
@@index([finalDecision])
|
||
@@map("screening_results")
|
||
@@schema("asl_schema")
|
||
}
|
||
|
||
// ASL 筛选任务表(标题摘要初筛)
|
||
model AslScreeningTask {
|
||
id String @id @default(uuid())
|
||
projectId String @map("project_id")
|
||
project AslScreeningProject @relation(fields: [projectId], references: [id], onDelete: Cascade)
|
||
|
||
taskType String @map("task_type") // "title_abstract" | "full_text"
|
||
status String @default("pending") // "pending" | "running" | "completed" | "failed"
|
||
|
||
// 进度统计
|
||
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") @db.Text
|
||
|
||
createdAt DateTime @default(now()) @map("created_at")
|
||
updatedAt DateTime @updatedAt @map("updated_at")
|
||
|
||
@@index([projectId])
|
||
@@index([status])
|
||
@@map("screening_tasks")
|
||
@@schema("asl_schema")
|
||
}
|
||
|
||
// ASL 全文复筛任务表
|
||
model AslFulltextScreeningTask {
|
||
id String @id @default(uuid())
|
||
projectId String @map("project_id")
|
||
project AslScreeningProject @relation(fields: [projectId], references: [id], onDelete: Cascade)
|
||
|
||
// 任务配置
|
||
modelA String @map("model_a") // "deepseek-v3"
|
||
modelB String @map("model_b") // "qwen-max"
|
||
promptVersion String @default("v1.0.0") @map("prompt_version")
|
||
|
||
// 任务状态
|
||
status String @default("pending")
|
||
// "pending" | "running" | "completed" | "failed" | "cancelled"
|
||
|
||
// 进度统计
|
||
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") @db.Text
|
||
errorStack String? @map("error_stack") @db.Text
|
||
|
||
// 关联
|
||
results AslFulltextScreeningResult[]
|
||
|
||
createdAt DateTime @default(now()) @map("created_at")
|
||
updatedAt DateTime @updatedAt @map("updated_at")
|
||
|
||
@@index([projectId])
|
||
@@index([status])
|
||
@@index([createdAt])
|
||
@@map("fulltext_screening_tasks")
|
||
@@schema("asl_schema")
|
||
}
|
||
|
||
// ASL 全文复筛结果表(12字段评估)
|
||
model AslFulltextScreeningResult {
|
||
id String @id @default(uuid())
|
||
taskId String @map("task_id")
|
||
task AslFulltextScreeningTask @relation(fields: [taskId], references: [id], onDelete: Cascade)
|
||
projectId String @map("project_id")
|
||
project AslScreeningProject @relation(fields: [projectId], references: [id], onDelete: Cascade)
|
||
literatureId String @map("literature_id")
|
||
literature AslLiterature @relation(fields: [literatureId], references: [id], onDelete: Cascade)
|
||
|
||
// ====== 模型A结果(DeepSeek-V3)======
|
||
modelAName String @map("model_a_name")
|
||
modelAStatus String @map("model_a_status") // "success" | "failed"
|
||
modelAFields Json @map("model_a_fields") // 12字段评估 { field1: {...}, field2: {...}, ... }
|
||
modelAOverall Json @map("model_a_overall") // 总体评估 { decision, confidence, keyIssues }
|
||
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") @db.Text
|
||
|
||
// ====== 模型B结果(Qwen-Max)======
|
||
modelBName String @map("model_b_name")
|
||
modelBStatus String @map("model_b_status") // "success" | "failed"
|
||
modelBFields Json @map("model_b_fields") // 12字段评估
|
||
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") @db.Text
|
||
|
||
// ====== 验证结果 ======
|
||
medicalLogicIssues Json? @map("medical_logic_issues") // MedicalLogicValidator输出
|
||
evidenceChainIssues Json? @map("evidence_chain_issues") // EvidenceChainValidator输出
|
||
|
||
// ====== 冲突检测 ======
|
||
isConflict Boolean @default(false) @map("is_conflict")
|
||
conflictSeverity String? @map("conflict_severity") // "high" | "medium" | "low"
|
||
conflictFields String[] @map("conflict_fields") // ["field1", "field9", "overall"]
|
||
conflictDetails Json? @map("conflict_details") // 详细冲突描述
|
||
reviewPriority Int? @map("review_priority") // 0-100复核优先级
|
||
reviewDeadline DateTime? @map("review_deadline")
|
||
|
||
// ====== 最终决策 ======
|
||
finalDecision String? @map("final_decision") // "include" | "exclude" | null
|
||
finalDecisionBy String? @map("final_decision_by") // userId
|
||
finalDecisionAt DateTime? @map("final_decision_at")
|
||
exclusionReason String? @map("exclusion_reason") @db.Text
|
||
reviewNotes String? @map("review_notes") @db.Text
|
||
|
||
// ====== 处理状态 ======
|
||
processingStatus String @default("pending") @map("processing_status")
|
||
// "pending" | "processing" | "completed" | "failed" | "degraded"
|
||
isDegraded Boolean @default(false) @map("is_degraded") // 单模型成功
|
||
degradedModel String? @map("degraded_model") // "modelA" | "modelB"
|
||
|
||
processedAt DateTime? @map("processed_at")
|
||
|
||
// ====== 可追溯信息 ======
|
||
promptVersion String @default("v1.0.0") @map("prompt_version")
|
||
rawOutputA Json? @map("raw_output_a") // 模型A原始输出
|
||
rawOutputB Json? @map("raw_output_b") // 模型B原始输出
|
||
|
||
createdAt DateTime @default(now()) @map("created_at")
|
||
updatedAt DateTime @updatedAt @map("updated_at")
|
||
|
||
@@unique([projectId, literatureId]) // 一篇文献只有一个全文复筛结果
|
||
@@index([taskId])
|
||
@@index([projectId])
|
||
@@index([literatureId])
|
||
@@index([isConflict])
|
||
@@index([finalDecision])
|
||
@@index([reviewPriority])
|
||
@@map("fulltext_screening_results")
|
||
@@schema("asl_schema")
|
||
}
|
||
|
||
// ==================== DC数据清洗模块 - Tool B (病历结构化机器人) ====================
|
||
|
||
// 健康检查缓存表
|
||
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") // 空值率 (0-1)
|
||
avgLength Float @map("avg_length") // 平均文本长度
|
||
totalRows Int @map("total_rows")
|
||
estimatedTokens Int @map("estimated_tokens")
|
||
|
||
// 检查结果
|
||
status String @map("status") // 'good' | 'bad'
|
||
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") // 'lung_cancer', 'diabetes', 'hypertension'
|
||
reportType String @map("report_type") // 'pathology', 'admission', 'outpatient'
|
||
displayName String @map("display_name") // '肺癌病理报告'
|
||
|
||
fields Json @map("fields") // [{name, desc, width}]
|
||
promptTemplate String @map("prompt_template") @db.Text
|
||
|
||
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") // Storage中的路径
|
||
textColumn String @map("text_column")
|
||
|
||
// 模板配置
|
||
diseaseType String @map("disease_type")
|
||
reportType String @map("report_type")
|
||
targetFields Json @map("target_fields") // [{name, desc}]
|
||
|
||
// 双模型配置
|
||
modelA String @default("deepseek-v3") @map("model_a")
|
||
modelB String @default("qwen-max") @map("model_b")
|
||
|
||
// 任务状态
|
||
status String @default("pending") @map("status") // 'pending'|'processing'|'completed'|'failed'
|
||
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") @db.Text
|
||
|
||
// 双模型结果 (V2核心)
|
||
resultA Json? @map("result_a") // DeepSeek结果 {"肿瘤大小": "3cm"}
|
||
resultB Json? @map("result_b") // Qwen结果 {"肿瘤大小": "3.0cm"}
|
||
|
||
// 冲突检测
|
||
status String @default("pending") @map("status") // 'pending'|'clean'|'conflict'|'resolved'|'failed'
|
||
conflictFields String[] @default([]) @map("conflict_fields") // ["肿瘤大小"]
|
||
|
||
// 最终结果 (用户裁决后或自动采纳)
|
||
finalResult Json? @map("final_result")
|
||
|
||
// Token统计
|
||
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")
|
||
}
|
||
|
||
// ==================== DC数据清洗模块 - Tool C (科研数据编辑器) ====================
|
||
|
||
// Tool C Session 会话表
|
||
model DcToolCSession {
|
||
id String @id @default(uuid())
|
||
userId String @map("user_id")
|
||
fileName String @map("file_name")
|
||
fileKey String @map("file_key") // OSS存储key: dc/tool-c/sessions/{timestamp}-{fileName}
|
||
|
||
// 数据元信息
|
||
totalRows Int @map("total_rows")
|
||
totalCols Int @map("total_cols")
|
||
columns Json @map("columns") // ["age", "gender", "diagnosis"] 列名数组
|
||
columnMapping Json? @map("column_mapping") // ✨ 列名映射:[{originalName, safeName, displayName}] 解决特殊字符问题
|
||
encoding String? @map("encoding") // 文件编码 utf-8, gbk等
|
||
fileSize Int @map("file_size") // 文件大小(字节)
|
||
|
||
// ✨ 数据统计信息(用于数据探索问答)
|
||
dataStats Json? @map("data_stats") // 缓存的统计信息:{ totalRows, columnStats: [{name, missingCount, missingRate, uniqueCount, dataType, mean, median}] }
|
||
|
||
// 时间戳
|
||
createdAt DateTime @default(now()) @map("created_at")
|
||
updatedAt DateTime @updatedAt @map("updated_at")
|
||
expiresAt DateTime @map("expires_at") // 过期时间(创建后10分钟)
|
||
|
||
@@index([userId])
|
||
@@index([expiresAt])
|
||
@@map("dc_tool_c_sessions")
|
||
@@schema("dc_schema")
|
||
}
|
||
|
||
// Tool C AI对话历史表
|
||
model DcToolCAiHistory {
|
||
id String @id @default(uuid())
|
||
sessionId String @map("session_id") // 关联Tool C Session
|
||
userId String @map("user_id")
|
||
role String @map("role") // user/assistant/system
|
||
content String @db.Text // 消息内容
|
||
|
||
// Tool C特有字段
|
||
generatedCode String? @db.Text @map("generated_code") // AI生成的代码
|
||
codeExplanation String? @db.Text @map("code_explanation") // 代码解释
|
||
executeStatus String? @map("execute_status") // pending/success/failed
|
||
executeResult Json? @map("execute_result") // 执行结果
|
||
executeError String? @db.Text @map("execute_error") // 错误信息
|
||
retryCount Int @default(0) @map("retry_count") // 重试次数
|
||
|
||
// LLM相关
|
||
model String? @map("model") // deepseek-v3/qwen3等
|
||
createdAt DateTime @default(now()) @map("created_at")
|
||
|
||
@@index([sessionId])
|
||
@@index([userId])
|
||
@@index([createdAt])
|
||
@@map("dc_tool_c_ai_history")
|
||
@@schema("dc_schema")
|
||
}
|