feat(asl): Complete Day 5 - Fulltext Screening Backend API Development

- Implement 5 core API endpoints (create task, get progress, get results, update decision, export Excel)
- Add FulltextScreeningController with Zod validation (652 lines)
- Implement ExcelExporter service with 4-sheet report generation (352 lines)
- Register routes under /api/v1/asl/fulltext-screening
- Create 31 REST Client test cases
- Add automated integration test script
- Fix PDF extraction fallback mechanism in LLM12FieldsService
- Update API design documentation to v3.0
- Update development plan to v1.2
- Create Day 5 development record
- Clean up temporary test files
This commit is contained in:
2025-11-23 10:52:07 +08:00
parent 08aa3f6c28
commit 88cc049fb3
232 changed files with 7780 additions and 441 deletions

View File

@@ -0,0 +1,141 @@
-- =====================================================
-- 全文复筛数据库迁移脚本(手动执行)
-- Schema: asl_schema
-- 日期: 2025-11-23
-- 说明: 只操作asl_schema不影响其他schema
-- =====================================================
-- 1. 修改 literatures 表,添加全文复筛相关字段
ALTER TABLE asl_schema.literatures
ADD COLUMN IF NOT EXISTS stage TEXT DEFAULT 'imported',
ADD COLUMN IF NOT EXISTS has_pdf BOOLEAN DEFAULT false,
ADD COLUMN IF NOT EXISTS pdf_storage_type TEXT,
ADD COLUMN IF NOT EXISTS pdf_storage_ref TEXT,
ADD COLUMN IF NOT EXISTS pdf_status TEXT DEFAULT 'pending',
ADD COLUMN IF NOT EXISTS pdf_uploaded_at TIMESTAMP(3),
ADD COLUMN IF NOT EXISTS full_text_storage_type TEXT,
ADD COLUMN IF NOT EXISTS full_text_storage_ref TEXT,
ADD COLUMN IF NOT EXISTS full_text_url TEXT,
ADD COLUMN IF NOT EXISTS full_text_format TEXT,
ADD COLUMN IF NOT EXISTS full_text_source TEXT,
ADD COLUMN IF NOT EXISTS full_text_token_count INTEGER,
ADD COLUMN IF NOT EXISTS full_text_extracted_at TIMESTAMP(3);
-- 添加索引
CREATE INDEX IF NOT EXISTS idx_literatures_stage ON asl_schema.literatures(stage);
CREATE INDEX IF NOT EXISTS idx_literatures_has_pdf ON asl_schema.literatures(has_pdf);
CREATE INDEX IF NOT EXISTS idx_literatures_pdf_status ON asl_schema.literatures(pdf_status);
-- 2. 创建 fulltext_screening_tasks 表
CREATE TABLE IF NOT EXISTS asl_schema.fulltext_screening_tasks (
id TEXT PRIMARY KEY,
project_id TEXT NOT NULL,
model_a TEXT NOT NULL,
model_b TEXT NOT NULL,
prompt_version TEXT,
status TEXT NOT NULL DEFAULT 'pending',
total_count INTEGER NOT NULL DEFAULT 0,
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,
total_tokens INTEGER DEFAULT 0,
total_cost DOUBLE PRECISION DEFAULT 0,
started_at TIMESTAMP(3),
completed_at TIMESTAMP(3),
estimated_end_at TIMESTAMP(3),
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_fulltext_task_project FOREIGN KEY (project_id)
REFERENCES asl_schema.screening_projects(id) ON DELETE CASCADE
);
-- 添加索引
CREATE INDEX IF NOT EXISTS idx_fulltext_tasks_project_id ON asl_schema.fulltext_screening_tasks(project_id);
CREATE INDEX IF NOT EXISTS idx_fulltext_tasks_status ON asl_schema.fulltext_screening_tasks(status);
CREATE INDEX IF NOT EXISTS idx_fulltext_tasks_created_at ON asl_schema.fulltext_screening_tasks(created_at);
-- 3. 创建 fulltext_screening_results 表
CREATE TABLE IF NOT EXISTS asl_schema.fulltext_screening_results (
id TEXT PRIMARY KEY,
task_id TEXT NOT NULL,
project_id TEXT NOT NULL,
literature_id TEXT NOT NULL,
-- Model A (DeepSeek-V3) 结果
model_a_name TEXT,
model_a_status TEXT,
model_a_fields JSONB,
model_a_overall JSONB,
model_a_processing_log JSONB,
model_a_verification JSONB,
model_a_tokens INTEGER,
model_a_cost DOUBLE PRECISION,
model_a_error TEXT,
-- Model B (Qwen-Max) 结果
model_b_name TEXT,
model_b_status TEXT,
model_b_fields JSONB,
model_b_overall JSONB,
model_b_processing_log JSONB,
model_b_verification JSONB,
model_b_tokens INTEGER,
model_b_cost DOUBLE PRECISION,
model_b_error TEXT,
-- 验证结果
medical_logic_issues JSONB,
evidence_chain_issues JSONB,
-- 冲突检测
is_conflict BOOLEAN DEFAULT false,
conflict_severity TEXT,
conflict_fields TEXT[],
conflict_details JSONB,
review_priority INTEGER DEFAULT 50,
review_deadline TIMESTAMP(3),
-- 人工复核
final_decision TEXT,
final_decision_by TEXT,
final_decision_at TIMESTAMP(3),
exclusion_reason TEXT,
review_notes TEXT,
-- 处理状态
processing_status TEXT DEFAULT 'pending',
is_degraded BOOLEAN DEFAULT false,
degraded_model TEXT,
-- 元数据
processed_at TIMESTAMP(3),
prompt_version TEXT,
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_fulltext_result_task FOREIGN KEY (task_id)
REFERENCES asl_schema.fulltext_screening_tasks(id) ON DELETE CASCADE,
CONSTRAINT fk_fulltext_result_project FOREIGN KEY (project_id)
REFERENCES asl_schema.screening_projects(id) ON DELETE CASCADE,
CONSTRAINT fk_fulltext_result_literature FOREIGN KEY (literature_id)
REFERENCES asl_schema.literatures(id) ON DELETE CASCADE,
CONSTRAINT unique_project_literature_fulltext UNIQUE (project_id, literature_id)
);
-- 添加索引
CREATE INDEX IF NOT EXISTS idx_fulltext_results_task_id ON asl_schema.fulltext_screening_results(task_id);
CREATE INDEX IF NOT EXISTS idx_fulltext_results_project_id ON asl_schema.fulltext_screening_results(project_id);
CREATE INDEX IF NOT EXISTS idx_fulltext_results_literature_id ON asl_schema.fulltext_screening_results(literature_id);
CREATE INDEX IF NOT EXISTS idx_fulltext_results_is_conflict ON asl_schema.fulltext_screening_results(is_conflict);
CREATE INDEX IF NOT EXISTS idx_fulltext_results_final_decision ON asl_schema.fulltext_screening_results(final_decision);
CREATE INDEX IF NOT EXISTS idx_fulltext_results_review_priority ON asl_schema.fulltext_screening_results(review_priority);
-- 完成
SELECT 'Migration completed successfully!' AS status;

File diff suppressed because it is too large Load Diff

View File

@@ -114,6 +114,8 @@ main()