Files
AIclinicalresearch/docs/03-业务模块/ASL-AI智能文献/02-技术设计/01-数据库设计.md
HaHafeng 1b53ab9d52 feat(aia): Complete AIA V2.0 with universal streaming capabilities
Major Changes:
- Add StreamingService with OpenAI Compatible format
- Upgrade Chat component V2 with Ant Design X integration
- Implement AIA module with 12 intelligent agents
- Update API routes to unified /api/v1 prefix
- Update system documentation

Backend (~1300 lines):
- common/streaming: OpenAI Compatible adapter
- modules/aia: 12 agents, conversation service, streaming integration
- Update route versions (RVW, PKB to v1)

Frontend (~3500 lines):
- modules/aia: AgentHub + ChatWorkspace (100% prototype restoration)
- shared/Chat: AIStreamChat, ThinkingBlock, useAIStream Hook
- Update API endpoints to v1

Documentation:
- AIA module status guide
- Universal capabilities catalog
- System overview updates
- All module documentation sync

Tested: Stream response verified, authentication working
Status: AIA V2.0 core completed (85%)
2026-01-14 19:15:01 +08:00

35 KiB
Raw Blame History

AI智能æ‡çŒ®æ¨¡å<EFBFBD>— - æ•°æ<C2B0>®åº“设è®?

*文档版本� v3.0
*创建日期� 2025-10-29
维护者: AI智能æ‡çŒ®å¼€å<E282AC>å¢é˜? 最å<EFBFBD>Žæ´æ°ï¼š 2025-11-22(Day 4:全æ‡å¤<C3A5>ç­æ•°æ<C2B0>®åº“设计ï¼? *更新说明ï¼? æ°å¢žå…¨æ‡å¤<C3A5>ç­ç¸å…³è¡¨ï¼ˆAslLiterature扩展ã€<EFBFBD>AslFulltextScreeningTaskã€<EFBFBD>AslFulltextScreeningResultï¼?


📋 文档说明

æœ¬æ‡æ¡£æ<EFBFBD><EFBFBD>è¿°AI智能æ‡çŒ®æ¨¡å<EFBFBD>—的数æ<EFBFBD>®åº“è®¾è®¡ï¼ŒåŒ…æ¬æ•°æ<EFBFBD>®è¡¨ç»“æž„ã€<EFBFBD>关系设计ã€<EFBFBD>索引设计等ã€? 技术栈:

  • æ•°æ<EFBFBD>®åº“:PostgreSQL 16+
  • ORM:Prisma
  • Schema隔离:asl_schema
  • å…³è<EFBFBD>”用户表:platform_schema.users

ðŸ<EFBFBD>—ï¸?Schemaæž¶æž„

ASL模å<EFBFBD>—使用ç¬ç«çš?asl_schema è¿è¡Œæ•°æ<C2B0>®éš”离,确ä¿<C3A4>模å<C2A1>—ç¬ç«æ€§åŒæ•°æ<C2B0>®å®‰å…¨ã€?

platform_schema
  └── users (用户�
       �asl_schema
  ├── screening_projects (筛选项�
  ├── literatures (æ‡çŒ®æ<C2AE>¡ç®)
  ├── screening_results (标题åˆ<C3A5>ç­ç»“æžœ)
  ├── screening_tasks (标题åˆ<C3A5>ç­ä»»åŠ¡)
  ├── fulltext_screening_tasks (å…¨æ‡å¤<C3A5>ç­ä»»åŠ¡) â­?Day 4æ°å¢ž
  └── fulltext_screening_results (å…¨æ‡å¤<C3A5>ç­ç»“æžœ) â­?Day 4æ°å¢ž

**v3.0 更新说明ï¼?025-11-22ï¼?*ï¼?- âœ?扩展 literatures 表:支æŒ<C3A6>å…¨æ‡ç”Ÿå½å¨æœŸç®¡ç<C2A1>†ã€<C3A3>PDFå­˜å¨ã€<C3A3>å…¨æ‡å†…容引ç”?- âœ?新增 fulltext_screening_tasks 表:管ç<C2A1>†å…¨æ‡å¤<C3A5>ç­æ‰¹å¤„ç<E2809E>†ä»»åŠ?- âœ?新增 fulltext_screening_results 表:存å¨12字段评估结果

  • âœ?符å<C2A6>ˆäºåŽŸç”Ÿè§„èŒƒï¼šå…¨æ‡å†…容存å¨å¼•用而é<C592>žç´æŽ¥å­˜å¨

🗄ï¸?核心数æ<C2B0>®è¡?

1. 筛选项目表 (screening_projects)

**Prisma模åžå<E280B9>?*: AslScreeningProject
表å<EFBFBD><EFBFBD>: asl_schema.screening_projects

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")
  // âš ï¸<C3AF> æ ¼å¼<C3A5>兼容性说明:
  // å‰<C3A5>端使用: { P, I, C, O, S }
  // å<>Žç«¯å…¼å®¹: { P, I, C, O, S } æˆ?{ population, intervention, comparison, outcome, studyDesign }
  // screeningService.ts 中有字段映射逻辑
  
  // 筛选标�  inclusionCriteria String   @map("inclusion_criteria") @db.Text
  exclusionCriteria String   @map("exclusion_criteria") @db.Text
  
  // 状�  status            String   @default("draft")
  // å<>¯é€‰å€? draft, screening, completed
  
  // ç­é€‰é…<C3A9>ç½?  screeningConfig   Json?    @map("screening_config")
  // 结构: { models: ["DeepSeek-V3", "Qwen-Max"], style: "standard" }
  // âš ï¸<C3AF> 模åžå<E280B9><C3A5>称映射ï¼?  // å‰<C3A5>端展示å<C2BA>? DeepSeek-V3 â†?APIå<49>? deepseek-chat
  // å‰<C3A5>端展示å<C2BA>? Qwen-Max â†?APIå<49>? qwen-max
  // screeningService.ts 中有模åžå<E280B9><C3A5>映射逻è¾
  
  // å…³è<C2B3>”
  literatures       AslLiterature[]
  screeningTasks    AslScreeningTask[]
  screeningResults  AslScreeningResult[]
  
  createdAt         DateTime @default(now()) @map("created_at")
  updatedAt         DateTime @updatedAt @map("updated_at")
  
  @@map("screening_projects")
  @@schema("asl_schema")
  @@index([userId])
  @@index([status])
}

**SQL表结�*:

CREATE TABLE asl_schema.screening_projects (
  id TEXT PRIMARY KEY,
  user_id TEXT NOT NULL,
  project_name TEXT NOT NULL,
  pico_criteria JSONB NOT NULL,
  inclusion_criteria TEXT NOT NULL,
  exclusion_criteria TEXT NOT NULL,
  status TEXT NOT NULL DEFAULT 'draft',
  screening_config JSONB,
  created_at TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
  updated_at TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
  CONSTRAINT fk_user FOREIGN KEY (user_id) 
    REFERENCES platform_schema.users(id) ON DELETE CASCADE
);

CREATE INDEX idx_screening_projects_user_id ON asl_schema.screening_projects(user_id);
CREATE INDEX idx_screening_projects_status ON asl_schema.screening_projects(status);

2. æ‡çŒ®æ<C2AE>¡ç®è¡?(literatures) â­?v3.0æ›´æ–°

**Prisma模åžå<E280B9>?*: AslLiterature
表å<EFBFBD><EFBFBD>: asl_schema.literatures

v3.0 更新说明ï¼?- âœ?新增 stage 字段:追踪æ‡çŒ®ç”Ÿå½å¨æœŸï¼ˆimported â†?title_screened â†?pdf_acquired â†?fulltext_screened â†?data_extractedï¼?- âœ?新增 PDFå­˜å¨å­—段:支æŒ<C3A6>Dify/OSSå<53>Œé€é…<C3A9>(pdfStorageType, pdfStorageRef, pdfStatusï¼?- âœ?新增 全文存储字段ï¼?*符å<C2A6>ˆäºåŽŸç”Ÿè§„èŒƒï¼Œå­˜å¨å¼•用而é<C592>žå†…容**(fullTextStorageRef, fullTextUrlï¼?- âœ?新增索引:stage, hasPdf, pdfStatus æ<><C3A6>å<EFBFBD>‡æŸ¥è¯¢æ€§èƒ½

model AslLiterature {
  id                String   @id @default(uuid())
  projectId         String   @map("project_id")
  project           AslScreeningProject @relation(fields: [projectId], references: [id], onDelete: Cascade)
  
  // æ‡çŒ®åŸºæœ¬ä¿¡æ<C2A1>¯
  pmid              String?
  title             String   @db.Text
  abstract          String   @db.Text
  authors           String?
  journal           String?
  publicationYear   Int?     @map("publication_year")
  doi               String?
  
  // â­?v3.0 æ°å¢žï¼šæ‡çŒ®é˜¶æ®µï¼ˆç”Ÿå½å¨æœŸç®¡ç<C2A1>†ï¼?  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")  // æ‡ä»¶å¤§å°<C3A5>(字èŠï¼‰
  
  // â­?v3.0 æ°å¢žï¼šPDFå­˜å¨ï¼ˆDify/OSSå<53>Œé€é…<C3A9>ï¼?  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")
  
  // â­?v3.0 æ°å¢žï¼šå…¨æ‡å†…容存å¨ï¼ˆäºåŽŸç”Ÿï¼šå­˜å¨å¼•用而é<C592>žå†…容ï¼?  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")
  
  // å…³è<C2B3>”
  screeningResults  AslScreeningResult[]
  fulltextScreeningResults AslFulltextScreeningResult[] // �v3.0 新增
  
  createdAt         DateTime @default(now()) @map("created_at")
  updatedAt         DateTime @updatedAt @map("updated_at")
  
  @@map("literatures")
  @@schema("asl_schema")
  @@index([projectId])
  @@index([doi])
  @@index([stage])          // �v3.0 新增
  @@index([hasPdf])         // �v3.0 新增
  @@index([pdfStatus])      // �v3.0 新增
  @@unique([projectId, pmid])
}

**SQL表结�*(v3.0�

CREATE TABLE asl_schema.literatures (
  id TEXT PRIMARY KEY,
  project_id TEXT NOT NULL,
  
  -- æ‡çŒ®åŸºæœ¬ä¿¡æ<C2A1>¯
  pmid TEXT,
  title TEXT NOT NULL,
  abstract TEXT NOT NULL,
  authors TEXT,
  journal TEXT,
  publication_year INTEGER,
  doi TEXT,
  
  -- 文献阶段
  stage TEXT NOT NULL DEFAULT 'imported',
  
  -- PDFå­˜å¨ï¼ˆæ—§å­—段,V1.0预留ï¼?  pdf_url TEXT,
  pdf_oss_key TEXT,
  pdf_file_size INTEGER,
  
  -- PDFå­˜å¨ï¼ˆæ°å­—段,Dify/OSSå<53>Œé€é…<C3A9>ï¼?  has_pdf BOOLEAN NOT NULL DEFAULT false,
  pdf_storage_type TEXT,
  pdf_storage_ref TEXT,
  pdf_status TEXT,
  pdf_uploaded_at TIMESTAMP(3),
  
  -- 全文内容存储(引用)
  full_text_storage_type TEXT,
  full_text_storage_ref TEXT,
  full_text_url TEXT,
  full_text_format TEXT,
  full_text_source TEXT,
  full_text_token_count INTEGER,
  full_text_extracted_at TIMESTAMP(3),
  
  created_at TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
  updated_at TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
  
  CONSTRAINT fk_project FOREIGN KEY (project_id) 
    REFERENCES asl_schema.screening_projects(id) ON DELETE CASCADE,
  CONSTRAINT unique_project_pmid UNIQUE (project_id, pmid)
);

CREATE INDEX idx_literatures_project_id ON asl_schema.literatures(project_id);
CREATE INDEX idx_literatures_doi ON asl_schema.literatures(doi);
CREATE INDEX idx_literatures_stage ON asl_schema.literatures(stage);
CREATE INDEX idx_literatures_has_pdf ON asl_schema.literatures(has_pdf);
CREATE INDEX idx_literatures_pdf_status ON asl_schema.literatures(pdf_status);

字段说明�

字段 类型 说明 设计ç<EFBFBD>†ç”±
stage String 文献阶段 追踪æ‡çŒ®åœ¨æ•´ä¸ªæµ<EFBFBD>ç¨ä¸­çš„ä½<EFBFBD>ç½?
pdfStorageType String PDFå­˜å¨ç±»åž "dify"|"oss",支æŒ<C3A6>å<EFBFBD>Œé€é…<C3A9>å™?
pdfStorageRef String PDFå­˜å¨å¼•用 Difyçš„document_idæˆOSSçš„object_key
fullTextStorageType String 全文存储类型 äºåŽŸç”Ÿï¼šä¸<EFBFBD>ç´æŽ¥å­˜å…¨æ‡ï¼Œå­˜å¼•用 âœ?
fullTextStorageRef String 全文存储引用 指å<EFBFBD>DifyæˆOSSä¸­çš„å…¨æ‡æ‡æ¡£ âœ?
fullTextUrl String å…¨æ‡è®¿é—®URL ç´æŽ¥è®¿é—®å…¨æ‡çš„URL
fullTextTokenCount Int Tokenæ•°é‡<EFBFBD> 用于æˆ<EFBFBD>本估算åŒLLM调用优åŒ

*云原生设计亮ç‚? â­<C3A2>:

  • âœ?å…¨æ‡å†…容存å¨åœ¨OSS/Dify,数æ<C2B0>®åº“å<E2809C>ªå­˜å¼•用(符å<C2A6>ˆäºåŽŸç”Ÿè§„èŒƒï¼?- âœ?支æŒ<C3A6>Dify â†?OSSæ— ç¼<C3A7>è¿<C3A8>移(å<CB86>ªéœ€åˆ‡æ<E280A1>¢storageTypeï¼?- âœ?æ•°æ<C2B0>®åº“è½»é‡<C3A9>,é<C592>¿å…<C3A5>大é‡<C3A9>TEXT字段

3. 筛选结果表 (screening_results)

**Prisma模åžå<E280B9>?*: AslScreeningResult
表å<EFBFBD><EFBFBD>: asl_schema.screening_results

设计亮点:支æŒ<EFBFBD>å<EFBFBD>Œæ¨¡åžï¼ˆDeepSeek + Qwen)并行验è¯<C3A8>,包å<E280A6>«å®Œæ•´çš„判æ­ã€<C3A3>è¯<C3A8>æ<EFBFBD>®åŒå†²çª<C3A7>检æµã€?

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模åžè¯<C3A8>æ<EFBFBD>®
  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模åžè¯<C3A8>æ<EFBFBD>®
  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
  
  // 冲çª<C3A7>状æ€?  conflictStatus    String   @default("none") @map("conflict_status")
  // å<>¯é€‰å€? none, conflict, resolved
  conflictFields    Json?    @map("conflict_fields")
  // 示例: ["P", "I", "conclusion"]
  
  // 最终决ç­ï¼ˆWeek 4 æ··å<C2B7>ˆæ¹æ¡ˆä½¿ç”¨ï¼?  finalDecision     String?  @map("final_decision") // "include" | "exclude" | null
  // â­?Week 4 说明:人工å¤<C3A5>æ ¸å<C2B8>Žè®¾ç½®æ­¤å­—段,作为最终决ç­?  // - include: 人工决定纳入(å<CB86>¯èƒ½æŽ¨ç¿»AI建议ï¼?  // - exclude: 人工决定æŽé™¤ï¼ˆå<CB86>¯èƒ½æŽ¨ç¿»AI建议ï¼?  // - null: 未å¤<C3A5>核,使用AI决ç­
  
  finalDecisionBy   String?  @map("final_decision_by") // userId
  finalDecisionAt   DateTime? @map("final_decision_at")
  
  exclusionReason   String?  @map("exclusion_reason") @db.Text
  // â­?Week 4 说明:人工填写的æŽé™¤åŽŸå ï¼ˆä¼˜å…ˆçº§é«˜äºŽAIæ<49><C3A6>å<EFBFBD>ï¼?  // - 妿žœfinalDecision=exclude,此字段存å¨äººå·¥å¡«å†™çš„原å?  // - 妿žœä¸ºnull,å‰<C3A5>端自动从AI判æ­ä¸­æ<C2AD><C3A6>å<EFBFBD>(dsPJudgment/dsIJudgment等)
  // - Week 4 åˆ<C3A5>ç­ç»“果页使用此字段显示æŽé™¤åŽŸå 
  
  // AI处ç<E2809E>†çжæ€?  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
  
  // å<>¯è¿½æº¯ä¿¡æ<C2A1>?  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")
  
  @@map("screening_results")
  @@schema("asl_schema")
  @@index([projectId])
  @@index([literatureId])
  @@index([conflictStatus])
  @@index([finalDecision])
  @@unique([projectId, literatureId])  // 一篇æ‡çŒ®åœ¨ä¸€ä¸ªé¡¹ç®ä¸­å<C2AD>ªæœ‰ä¸€ä¸ªç­é€‰ç»“æž?}

**SQL表结�*(简化版�

CREATE TABLE asl_schema.screening_results (
  id TEXT PRIMARY KEY,
  project_id TEXT NOT NULL,
  literature_id TEXT NOT NULL,
  
  -- DeepSeek判æ­
  ds_model_name TEXT NOT NULL,
  ds_p_judgment TEXT,
  ds_i_judgment TEXT,
  ds_c_judgment TEXT,
  ds_s_judgment TEXT,
  ds_conclusion TEXT,
  ds_confidence DOUBLE PRECISION,
  ds_p_evidence TEXT,
  ds_i_evidence TEXT,
  ds_c_evidence TEXT,
  ds_s_evidence TEXT,
  ds_reason TEXT,
  
  -- Qwen判æ­
  qwen_model_name TEXT NOT NULL,
  qwen_p_judgment TEXT,
  qwen_i_judgment TEXT,
  qwen_c_judgment TEXT,
  qwen_s_judgment TEXT,
  qwen_conclusion TEXT,
  qwen_confidence DOUBLE PRECISION,
  qwen_p_evidence TEXT,
  qwen_i_evidence TEXT,
  qwen_c_evidence TEXT,
  qwen_s_evidence TEXT,
  qwen_reason TEXT,
  
  -- 冲çª<C3A7>状æ€?  conflict_status TEXT NOT NULL DEFAULT 'none',
  conflict_fields JSONB,
  
  -- 最终决�  final_decision TEXT,
  final_decision_by TEXT,
  final_decision_at TIMESTAMP(3),
  exclusion_reason TEXT,
  
  -- AI处ç<E2809E>†çжæ€?  ai_processing_status TEXT NOT NULL DEFAULT 'pending',
  ai_processed_at TIMESTAMP(3),
  ai_error_message TEXT,
  
  -- å<>¯è¿½æº¯ä¿¡æ<C2A1>?  prompt_version TEXT NOT NULL DEFAULT 'v1.0.0',
  raw_output JSONB,
  
  created_at TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
  updated_at TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
  
  CONSTRAINT fk_project_result FOREIGN KEY (project_id) 
    REFERENCES asl_schema.screening_projects(id) ON DELETE CASCADE,
  CONSTRAINT fk_literature FOREIGN KEY (literature_id) 
    REFERENCES asl_schema.literatures(id) ON DELETE CASCADE,
  CONSTRAINT unique_project_literature UNIQUE (project_id, literature_id)
);

CREATE INDEX idx_screening_results_project_id ON asl_schema.screening_results(project_id);
CREATE INDEX idx_screening_results_literature_id ON asl_schema.screening_results(literature_id);
CREATE INDEX idx_screening_results_conflict_status ON asl_schema.screening_results(conflict_status);
CREATE INDEX idx_screening_results_final_decision ON asl_schema.screening_results(final_decision);

4. 筛选任务表 (screening_tasks)

**Prisma模åžå<E280B9>?*: AslScreeningTask
表å<EFBFBD><EFBFBD>: asl_schema.screening_tasks

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")
  
  // æ—¶é—´ä¿¡æ<C2A1>¯
  startedAt         DateTime? @map("started_at")
  completedAt       DateTime? @map("completed_at")
  estimatedEndAt    DateTime? @map("estimated_end_at")
  
  // 错误信æ<C2A1>¯
  errorMessage      String?  @map("error_message") @db.Text
  
  createdAt         DateTime @default(now()) @map("created_at")
  updatedAt         DateTime @updatedAt @map("updated_at")
  
  @@map("screening_tasks")
  @@schema("asl_schema")
  @@index([projectId])
  @@index([status])
}

**SQL表结�*:

CREATE TABLE asl_schema.screening_tasks (
  id TEXT PRIMARY KEY,
  project_id TEXT NOT NULL,
  task_type TEXT NOT NULL,
  status TEXT NOT NULL DEFAULT 'pending',
  total_items INTEGER NOT NULL,
  processed_items INTEGER NOT NULL DEFAULT 0,
  success_items INTEGER NOT NULL DEFAULT 0,
  failed_items INTEGER NOT NULL DEFAULT 0,
  conflict_items INTEGER NOT NULL DEFAULT 0,
  started_at TIMESTAMP(3),
  completed_at TIMESTAMP(3),
  estimated_end_at TIMESTAMP(3),
  error_message TEXT,
  created_at TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
  updated_at TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
  CONSTRAINT fk_project_task FOREIGN KEY (project_id) 
    REFERENCES asl_schema.screening_projects(id) ON DELETE CASCADE
);

CREATE INDEX idx_screening_tasks_project_id ON asl_schema.screening_tasks(project_id);
CREATE INDEX idx_screening_tasks_status ON asl_schema.screening_tasks(status);

5. å…¨æ‡å¤<C3A5>ç­ä»»åŠ¡è¡?(fulltext_screening_tasks) â­?v3.0新增

**Prisma模åžå<E280B9>?*: AslFulltextScreeningTask
表å<EFBFBD><EFBFBD>: asl_schema.fulltext_screening_tasks

**设计目标**:管ç<C2A1>†å…¨æ‡å¤<C3A5>ç­çš„æ‰¹å¤„ç<E2809E>†ä»»åŠ¡ï¼Œæ”¯æŒ<C3A6>å<EFBFBD>Œæ¨¡åžå¹¶è¡Œè°ƒç”¨ã€<C3A3>æˆ<C3A6>本追踪ã€<C3A3>é™<C3A9>级模å¼?

model AslFulltextScreeningTask {
  id                String   @id @default(uuid())
  projectId         String   @map("project_id")
  project           AslScreeningProject @relation(fields: [projectId], references: [id], onDelete: Cascade)
  
  // 任务é…<C3A9>ç½®
  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")  // å<>•æ¨¡åžæˆ<C3A6>åŠ?  
  // æˆ<C3A6>本统计
  totalTokens       Int      @default(0) @map("total_tokens")
  totalCost         Float    @default(0) @map("total_cost")
  
  // æ—¶é—´ä¿¡æ<C2A1>¯
  startedAt         DateTime? @map("started_at")
  completedAt       DateTime? @map("completed_at")
  estimatedEndAt    DateTime? @map("estimated_end_at")
  
  // 错误信æ<C2A1>¯
  errorMessage      String?  @map("error_message") @db.Text
  errorStack        String?  @map("error_stack") @db.Text
  
  // å…³è<C2B3>”
  results           AslFulltextScreeningResult[]
  
  createdAt         DateTime @default(now()) @map("created_at")
  updatedAt         DateTime @updatedAt @map("updated_at")
  
  @@map("fulltext_screening_tasks")
  @@schema("asl_schema")
  @@index([projectId])
  @@index([status])
  @@index([createdAt])
}

**SQL表结�*:

CREATE TABLE asl_schema.fulltext_screening_tasks (
  id TEXT PRIMARY KEY,
  project_id TEXT NOT NULL,
  
  -- 任务é…<C3A9>ç½®
  model_a TEXT NOT NULL,
  model_b TEXT NOT NULL,
  prompt_version TEXT NOT NULL DEFAULT 'v1.0.0',
  
  -- 任务状�  status TEXT NOT NULL DEFAULT 'pending',
  
  -- 进度统计
  total_count INTEGER NOT NULL,
  processed_count INTEGER NOT NULL DEFAULT 0,
  success_count INTEGER NOT NULL DEFAULT 0,
  failed_count INTEGER NOT NULL DEFAULT 0,
  degraded_count INTEGER NOT NULL DEFAULT 0,
  
  -- æˆ<C3A6>本统计
  total_tokens INTEGER NOT NULL DEFAULT 0,
  total_cost DOUBLE PRECISION NOT NULL DEFAULT 0,
  
  -- æ—¶é—´ä¿¡æ<C2A1>¯
  started_at TIMESTAMP(3),
  completed_at TIMESTAMP(3),
  estimated_end_at TIMESTAMP(3),
  
  -- 错误信æ<C2A1>¯
  error_message TEXT,
  error_stack TEXT,
  
  created_at TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
  updated_at TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
  
  CONSTRAINT fk_project_fulltext_task FOREIGN KEY (project_id) 
    REFERENCES asl_schema.screening_projects(id) ON DELETE CASCADE
);

CREATE INDEX idx_fulltext_screening_tasks_project_id ON asl_schema.fulltext_screening_tasks(project_id);
CREATE INDEX idx_fulltext_screening_tasks_status ON asl_schema.fulltext_screening_tasks(status);
CREATE INDEX idx_fulltext_screening_tasks_created_at ON asl_schema.fulltext_screening_tasks(created_at);

字段说明�

字段 类型 说明
modelA / modelB String å<EFBFBD>Œæ¨¡åžå<EFBFBD><EFBFBD>称(deepseek-v3 + qwen-maxï¼?
degradedCount Int å<EFBFBD>•æ¨¡åžæˆ<EFBFBD>功的任务数(容错机制ï¼?
totalTokens Int 累计Token使用�
totalCost Float 累计æˆ<EFBFBD>本(元ï¼?
promptVersion String Prompt版本(å<EFBFBD>¯è¿½æº¯ï¼?

6. å…¨æ‡å¤<C3A5>ç­ç»“æžœè¡?(fulltext_screening_results) â­?v3.0新增

**Prisma模åžå<E280B9>?*: AslFulltextScreeningResult
表å<EFBFBD><EFBFBD>: asl_schema.fulltext_screening_results

**设计目标**:存å‚?2字段详细评估结果,支æŒ<C3A6>å<EFBFBD>Œæ¨¡åžå¯¹æ¯”ã€<C3A3>验è¯<C3A8>结果ã€<C3A3>冲çª<C3A7>检æµ? 设计亮点ï¼?- âœ?完整的å<E2809E>Œæ¨¡åžç»“果(fields + overall + logsï¼?- âœ?医学逻è¾éªŒè¯<C3A8>åŒè¯<C3A8>æ<EFBFBD>®é“¾éªŒè¯<C3A8>结果

  • âœ?冲çª<C3A7>检æµåŒå¤<C3A5>核优先çº?- âœ?é™<C3A9>级模å¼<C3A5>支æŒ<C3A6>(å<CB86>•æ¨¡åžæˆ<C3A6>功ï¼?- âœ?JSONå­˜å¨12字段评估(符å<C2A6>ˆäºåŽŸç”Ÿè§„èŒƒï¼?
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")
  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") @db.Text
  
  // ====== 验è¯<C3A8>结果 ======
  medicalLogicIssues Json?   @map("medical_logic_issues")  // MedicalLogicValidator输出
  evidenceChainIssues Json?  @map("evidence_chain_issues") // EvidenceChainValidator输出
  
  // ====== 冲çª<C3A7>检æµ?======
  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å¤<C3A5>核优先çº?  reviewDeadline    DateTime? @map("review_deadline")
  
  // ====== 最终决�======
  finalDecision     String?  @map("final_decision")           // "include" | "exclude" | null
  finalDecisionBy   String?  @map("final_decision_by")
  finalDecisionAt   DateTime? @map("final_decision_at")
  exclusionReason   String?  @map("exclusion_reason") @db.Text
  reviewNotes       String?  @map("review_notes") @db.Text
  
  // ====== 处ç<E2809E>†çжæ€?======
  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")
  
  // ====== å<>¯è¿½æº¯ä¿¡æ<C2A1>?======
  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")
  
  @@map("fulltext_screening_results")
  @@schema("asl_schema")
  @@index([taskId])
  @@index([projectId])
  @@index([literatureId])
  @@index([isConflict])
  @@index([finalDecision])
  @@index([reviewPriority])
  @@unique([projectId, literatureId])  // 一篇æ‡çŒ®å<C2AE>ªæœ‰ä¸€ä¸ªå…¨æ‡å¤<C3A5>ç­ç»“æž?}

**SQL表结æž?*(简åŒç‰ˆï¼Œå®žé™…包å<E280A6>«æ‰€æœ‰å­—段):

CREATE TABLE asl_schema.fulltext_screening_results (
  id TEXT PRIMARY KEY,
  task_id TEXT NOT NULL,
  project_id TEXT NOT NULL,
  literature_id TEXT NOT NULL,
  
  -- 模åžA结果
  model_a_name TEXT NOT NULL,
  model_a_status TEXT NOT NULL,
  model_a_fields JSONB NOT NULL,
  model_a_overall JSONB NOT NULL,
  model_a_processing_log JSONB,
  model_a_verification JSONB,
  model_a_tokens INTEGER,
  model_a_cost DOUBLE PRECISION,
  model_a_error TEXT,
  
  -- 模åžB结果(å<CB86>Œä¸Šï¼‰
  model_b_name TEXT NOT NULL,
  model_b_status TEXT NOT NULL,
  model_b_fields JSONB NOT NULL,
  model_b_overall JSONB NOT NULL,
  model_b_processing_log JSONB,
  model_b_verification JSONB,
  model_b_tokens INTEGER,
  model_b_cost DOUBLE PRECISION,
  model_b_error TEXT,
  
  -- 验è¯<C3A8>结果
  medical_logic_issues JSONB,
  evidence_chain_issues JSONB,
  
  -- 冲çª<C3A7>检æµ?  is_conflict BOOLEAN NOT NULL DEFAULT false,
  conflict_severity TEXT,
  conflict_fields TEXT[],
  conflict_details JSONB,
  review_priority INTEGER,
  review_deadline TIMESTAMP(3),
  
  -- 最终决�  final_decision TEXT,
  final_decision_by TEXT,
  final_decision_at TIMESTAMP(3),
  exclusion_reason TEXT,
  review_notes TEXT,
  
  -- 处ç<E2809E>†çжæ€?  processing_status TEXT NOT NULL DEFAULT 'pending',
  is_degraded BOOLEAN NOT NULL DEFAULT false,
  degraded_model TEXT,
  processed_at TIMESTAMP(3),
  
  -- å<>¯è¿½æº¯ä¿¡æ<C2A1>?  prompt_version TEXT NOT NULL DEFAULT 'v1.0.0',
  raw_output_a JSONB,
  raw_output_b JSONB,
  
  created_at TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
  updated_at TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
  
  CONSTRAINT fk_task FOREIGN KEY (task_id) 
    REFERENCES asl_schema.fulltext_screening_tasks(id) ON DELETE CASCADE,
  CONSTRAINT fk_project_fulltext_result FOREIGN KEY (project_id) 
    REFERENCES asl_schema.screening_projects(id) ON DELETE CASCADE,
  CONSTRAINT fk_literature_fulltext FOREIGN KEY (literature_id) 
    REFERENCES asl_schema.literatures(id) ON DELETE CASCADE,
  CONSTRAINT unique_project_literature_fulltext UNIQUE (project_id, literature_id)
);

CREATE INDEX idx_fulltext_screening_results_task_id ON asl_schema.fulltext_screening_results(task_id);
CREATE INDEX idx_fulltext_screening_results_project_id ON asl_schema.fulltext_screening_results(project_id);
CREATE INDEX idx_fulltext_screening_results_literature_id ON asl_schema.fulltext_screening_results(literature_id);
CREATE INDEX idx_fulltext_screening_results_is_conflict ON asl_schema.fulltext_screening_results(is_conflict);
CREATE INDEX idx_fulltext_screening_results_final_decision ON asl_schema.fulltext_screening_results(final_decision);
CREATE INDEX idx_fulltext_screening_results_review_priority ON asl_schema.fulltext_screening_results(review_priority);

**JSON字段示ä¾**ï¼? modelAFields (12字段评估):

{
  "field1": {
    "present": true,
    "completeness": "完整",
    "extractable": true,
    "quote": "第一作者:Zhang et al., å<>表äº?JAMA 2023...",
    "location": "Title page, Methods section",
    "note": ‡çŒ®æ<C2AE>¥æº<C3A6>ä¿¡æ<C2A1>¯å®Œæ•´"
  },
  "field2": { ... },
  // ... field3-field12
}

modelAOverall (总体评估):

{
  "decision": "include",
  "confidence": 0.92,
  "keyIssues": [
    "éš<C3A9>æœºåŒæ¹æ³•æ<E280A2><C3A6>述完æ•?,
    "盲法实施清晰",
    "结局指标å<EFBFBD>¯æ<EFBFBD><EFBFBD>å<EFBFBD>?
  ]
}

medicalLogicIssues (医学逻è¾éªŒè¯<C3A8>):

{
  "hasIssues": false,
  "issues": []
}

conflictDetails (冲çª<C3A7>详情):

{
  "field9": {
    "modelA": "完整",
    "modelB": ¸<C3A4>完æ•?,
    "severity": "high"
  }
}

📊 æ•°æ<C2B0>®å…³ç³»å¾ï¼ˆv3.0æ›´æ–°ï¼?

literature_screening_projects (1) ──< (N) literature_items
literature_screening_projects (1) ──< (N) title_abstract_screening_results
literature_items (1) ──< (1) title_abstract_screening_results
literature_screening_projects (1) ──< (N) screening_tasks

ðŸ”<EFBFBD> 索引设计汇总(v3.0æ›´æ–°ï¼?

表å<EFBFBD><EFBFBD> 索引字段 索引类型 说明
screening_projects user_id B-tree 用户项目查询
screening_projects status B-tree 状æ€<EFBFBD>ç­é€?
literatures project_id B-tree 项目文献查询
literatures doi B-tree DOI查é‡<EFBFBD>
literatures stage � B-tree 文献阶段查询 v3.0
literatures has_pdf â­? B-tree PDF获å<EFBFBD>状æ€?v3.0
literatures pdf_status � B-tree PDF上传状�v3.0
literatures (project_id, pmid) Unique 防止é‡<EFBFBD>å¤<EFBFBD>导入
screening_results project_id B-tree 项目结果查询
screening_results literature_id B-tree 文献结果查询
screening_results conflict_status B-tree 冲çª<EFBFBD>ç­é€?
screening_results final_decision B-tree 决策筛�
screening_results (project_id, literature_id) Unique 唯一性约æ<EFBFBD>?
screening_tasks project_id B-tree 项目任务查询
screening_tasks status B-tree 任务状æ€<EFBFBD>ç­é€?
fulltext_screening_tasks � project_id B-tree 全文任务查询 v3.0
fulltext_screening_tasks â­? status B-tree 任务状æ€<EFBFBD>ç­é€?v3.0
fulltext_screening_tasks â­? created_at B-tree æ—¶é—´æŽåº<EFBFBD> v3.0
fulltext_screening_results � task_id B-tree 任务结果查询 v3.0
fulltext_screening_results � project_id B-tree 项目结果查询 v3.0
fulltext_screening_results � literature_id B-tree 文献结果查询 v3.0
fulltext_screening_results â­? is_conflict B-tree 冲çª<EFBFBD>ç­é€?v3.0
fulltext_screening_results � final_decision B-tree 决策筛�v3.0
fulltext_screening_results â­? review_priority B-tree å¤<EFBFBD>核优先çº?v3.0
fulltext_screening_results â­? (project_id, literature_id) Unique 唯一性约æ<EFBFBD>?v3.0

索引总数: 25个(v3.0æ°å¢ž13个)
唯一约æ<EFBFBD>Ÿ: 4个(v3.0æ°å¢ž1个)

v3.0索引优化说明ï¼?- âœ?literatures.stage: 快速查询特定阶段的文献(如"pdf_acquired"å¾…å…¨æ‡å¤<C3A5>ç­ï¼‰

  • âœ?fulltext_screening_results.review_priority: 优åŒäººå·¥å¤<C3A5>核队列æŽåº<C3A5>
  • âœ?fulltext_screening_tasks.created_at: 任务历å<E280A0>²æŸ¥è¯¢ä¼˜åŒ

💾 æ•°æ<C2B0>®å­—å…¸

PICO标准 (picoCriteria JSON)

{
  "population": "研究人群,如ï¼?åžç³å°¿ç—…æˆ<C3A6>人æ£è€?,
  "intervention": "干预措æ½ï¼Œå¦ï¼šSGLT2抑制å‰?,
  "comparison": "对照,如:安慰剂或常规疗�,
  "outcome": "结局指标,如:心血管结局",
  "studyDesign": "研究设计,å¦ï¼šéš<EFBFBD>机对照试éª?(RCT)"
}

ç­é€‰é…<EFBFBD>ç½?(screeningConfig JSON)

{
  "models": ["deepseek-chat", "qwen-max"],
  "temperature": 0,
  "maxRetries": 3
}

冲çª<EFBFBD>字段 (conflictFields JSON)

["P", "I", "C", "S", "conclusion"]

原始输出 (rawOutput JSON)

{
  "deepseek": { "判断": {...}, "è¯<C3A8>æ<EFBFBD>®": {...} },
  "qwen": { "判断": {...}, "è¯<C3A8>æ<EFBFBD>®": {...} }
}

🔒 æ•°æ<C2B0>®å®‰å…¨

Schema隔离

  • 使用 asl_schema ä¸Žå…¶ä»æ¨¡å<C2A1>—æ•°æ<C2B0>®éš”ç¦?- 用户表在 platform_schema,统一管ç<EFBFBD>

级è<EFBFBD>”删除

  • 删除用户 â†?自动删除所有ç­é€‰é¡¹ç®å<C2AE>Šå…³è<C2B3>”æ•°æ<C2B0>®
  • 删除项目 â†?自动删除æ‡çŒ®ã€<C3A3>结果ã€<C3A3>ä»»åŠ?- 删除文献 â†?自动删除筛选结æž?

唯一性约æ<EFBFBD>?- å<>Œä¸€é¡¹ç®ä¸­PMID唯一(å…<C3A5>许无PMIDï¼?- å<>Œä¸€é¡¹ç®ä¸­ä¸€ç¯‡æ‡çŒ®å<C2AE>ªæœ‰ä¸€ä¸ªç­é€‰ç»“æž?


📈 æ•°æ<C2B0>®é‡<C3A9>预ä¼?

项目规模 文献� 筛选结� 存储空间
å°<EFBFBD>åž 100-500 100-500 < 10 MB
中型 500-2000 500-2000 10-50 MB
大型 2000-5000 2000-5000 50-200 MB
超大� 5000+ 5000+ 200 MB+

å<EFBFBD>•æ<EFBFBD>¡è®°å½•大å°<EFBFBD>ä¼°ç®—:

  • æ‡çŒ®æ<EFBFBD>¡ç®ï¼š~2-5 KB
  • 筛选结果:~5-10 KB(å<CB86>«å<C2AB>Œæ¨¡åžåˆ¤æ­åŒè¯<C3A8>æ<EFBFBD>®ï¼?

â<EFBFBD><>Žç»­è§„åˆ

Phase 2 (å…¨æ‡å¤<C3A5>ç­) âœ?v3.0已完æˆ?- [x] 扩展 literatures 表(生å½å¨æœŸç®¡ç<C2A1>†ï¼?- [x] 添加 fulltext_screening_tasks è¡?- [x] 添加 fulltext_screening_results 表(12字段ï¼?

Phase 3 (æ•°æ<C2B0>®æ<C2AE><C3A6>å<EFBFBD>) å¾…å¼€å<E282AC>?- [ ] å¤<C3A5>用 fulltext_screening_tasks 表(切æ<E280A1>¢æ¨¡å¼<C3A5>ï¼?- [ ] å¤<C3A5>用 fulltext_screening_results è¡¨ï¼ˆå­˜å¨æ<C2A8><C3A6>å<EFBFBD>æ•°æ<C2B0>®ï¼?- [ ] 或新å¢?data_extraction_results 表(如需独立ï¼?

Phase 4 (è´¨é‡<C3A9>评估) å¾…è§„åˆ?- [ ] è´¨é‡<C3A9>评估结果è¡?- [ ] å<><C3A5>倚风险评估表

  • GRADEè¯<EFBFBD>æ<EFBFBD>®è´¨é‡<EFBFBD>è¡?

ðŸ“<EFBFBD> v3.0 设计决策记录

决ç­1: å…¨æ‡å†…容存å¨å¼•用而é<C592>žç´æŽ¥å­˜å¨ âœ?

**问题**:全æ‡å†…容是å<C2AF>¦å­˜å¨åœ¨æ•°æ<C2B0>®åº“?

**方案对比**ï¼?| 方案 | 优点 | 缺点 | |------|------|------| | å­˜TEXT | LLM调用å¿?| è¿<C3A8>背äºåŽŸç”Ÿè§„èŒƒï¼Œæ•°æ<C2B0>®åº“臃è?| | 存引ç”?| 符å<C2A6>ˆè§„范,轻é‡?| LLM调用增加100-200ms |

**决策**:✅ é‡‡ç”¨æ¹æ¡ˆ2(存引用ï¼?- 符å<C2A6>ˆäºåŽŸç”Ÿå­˜å¨ä¸Žè®¡ç®—分离原åˆ

  • 支æŒ<EFBFBD>超大æ‡çŒ®ï¼?1MBï¼?- RDSå­˜å¨æˆ<C3A6>本是OSSçš?-10å€?

决ç­2: 12字段使用JSONå­˜å¨ âœ?

**问题**ï¼?2字段是æ†åˆ†ä¸ºåˆ—还是JSONå­˜å¨ï¼? **决策**:✅ 使用PostgreSQL JSONB

  • ä¸<EFBFBD>需è¦<EFBFBD>å<EFBFBD>•ç¬æŸ¥è¯¢æŸ<EFBFBD>个字段内éƒ?- 字段结构å¤<C3A5>æ<EFBFBD>ï¼?个å­<C3A5>字段ï¼?- JSONB性能优秀且支æŒ<C3A6>GIN索引

决ç­3: ç¬ç«å…¨æ‡å¤<C3A5>ç­ç»“æžœè¡?âœ?

**问题**:是å<C2AF>¦å¤<C3A5>ç”?screening_results 表?

**决策**:✅ 新增独立�fulltext_screening_results

  • æ•°æ<EFBFBD>®ç»“构完全ä¸<EFBFBD>å<EFBFBD>Œï¼ˆPICOS vs 12字段ï¼?- é<>¿å…<C3A5>字段冗余åŒé€»è¾è€¦å<C2A6>ˆ
  • 便于独立维护和优åŒ?

*文档版本� v3.0
最å<EFBFBD>Žæ´æ°ï¼š 2025-11-22(Day 4:全æ‡å¤<C3A5>ç­æ•°æ<C2B0>®åº“设计ï¼? 维护者: AI智能æ‡çŒ®å¼€å<E282AC>å¢é˜? 版本历å<EFBFBD>²ï¼?- v3.0 (2025-11-22): å…¨æ‡å¤<C3A5>ç­æ•°æ<C2B0>®åº“设计,æ°å¢ž3个表åŒç¸å…³å­—æ®?- v2.2 (2025-11-21): Week 4统计功能完æˆ<C3A6>

  • v2.0 (2025-11-18): 标题åˆ<C3A5>ç­æ•°æ<C2B0>®åº“设è®?- v1.0 (2025-10-29): åˆ<C3A5>å§ç‰ˆæœ¬