Files
AIclinicalresearch/backend/prisma/schema.prisma
HaHafeng f01981bf78 feat(dc/tool-c): 完成AI代码生成服务(Day 3 MVP)
核心功能:
- 新增AICodeService(550行):AI代码生成核心服务
- 新增AIController(257行):4个API端点
- 新增dc_tool_c_ai_history表:存储对话历史
- 实现自我修正机制:最多3次智能重试
- 集成LLMFactory:复用通用能力层
- 10个Few-shot示例:覆盖Level 1-4场景

技术优化:
- 修复NaN序列化问题(Python端转None)
- 修复数据传递问题(从Session获取真实数据)
- 优化System Prompt(明确环境信息)
- 调整Few-shot示例(移除import语句)

测试结果:
- 通过率:9/11(81.8%) 达到MVP标准
- 成功场景:缺失值处理、编码、分箱、BMI、筛选、填补、统计、分类
- 待优化:数值清洗、智能去重(已记录技术债务TD-C-006)

API端点:
- POST /api/v1/dc/tool-c/ai/generate(生成代码)
- POST /api/v1/dc/tool-c/ai/execute(执行代码)
- POST /api/v1/dc/tool-c/ai/process(生成并执行,一步到位)
- GET /api/v1/dc/tool-c/ai/history/:sessionId(对话历史)

文档更新:
- 新增Day 3开发完成总结(770行)
- 新增复杂场景优化技术债务(TD-C-006)
- 更新工具C当前状态文档
- 更新技术债务清单

影响范围:
- backend/src/modules/dc/tool-c/*(新增2个文件,更新1个文件)
- backend/scripts/create-tool-c-ai-history-table.mjs(新增)
- backend/prisma/schema.prisma(新增DcToolCAiHistory模型)
- extraction_service/services/dc_executor.py(NaN序列化修复)
- docs/03-业务模块/DC-数据清洗整理/*(5份文档更新)

Breaking Changes: 无

总代码行数:+950行

Refs: #Tool-C-Day3
2025-12-07 16:21:32 +08:00

903 lines
31 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// 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"] 列名数组
encoding String? @map("encoding") // 文件编码 utf-8, gbk等
fileSize Int @map("file_size") // 文件大小(字节)
// 时间戳
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")
}