/** * 手动创建ASL模块的4张表 * 避免影响现有表 */ import { prisma } from '../src/config/database.js'; async function createAslTables() { try { console.log('🔍 开始创建ASL模块表...\n'); // 1. 创建筛选项目表 await prisma.$executeRawUnsafe(` CREATE TABLE IF NOT EXISTS 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 ); `); console.log('✅ 创建 asl_schema.screening_projects'); // 创建索引 await prisma.$executeRawUnsafe(` CREATE INDEX IF NOT EXISTS idx_screening_projects_user_id ON asl_schema.screening_projects(user_id); `); await prisma.$executeRawUnsafe(` CREATE INDEX IF NOT EXISTS idx_screening_projects_status ON asl_schema.screening_projects(status); `); // 2. 创建文献条目表 await prisma.$executeRawUnsafe(` CREATE TABLE IF NOT EXISTS asl_schema.literatures ( id TEXT PRIMARY KEY, project_id TEXT NOT NULL, pmid TEXT, title TEXT NOT NULL, abstract TEXT NOT NULL, authors TEXT, journal TEXT, publication_year INTEGER, doi TEXT, pdf_url TEXT, pdf_oss_key TEXT, pdf_file_size INTEGER, 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) ); `); console.log('✅ 创建 asl_schema.literatures'); // 创建索引 await prisma.$executeRawUnsafe(` CREATE INDEX IF NOT EXISTS idx_literatures_project_id ON asl_schema.literatures(project_id); `); await prisma.$executeRawUnsafe(` CREATE INDEX IF NOT EXISTS idx_literatures_doi ON asl_schema.literatures(doi); `); // 3. 创建筛选结果表 await prisma.$executeRawUnsafe(` CREATE TABLE IF NOT EXISTS 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, -- 冲突状态 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处理状态 ai_processing_status TEXT NOT NULL DEFAULT 'pending', ai_processed_at TIMESTAMP(3), ai_error_message TEXT, -- 可追溯信息 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) ); `); console.log('✅ 创建 asl_schema.screening_results'); // 创建索引 await prisma.$executeRawUnsafe(` CREATE INDEX IF NOT EXISTS idx_screening_results_project_id ON asl_schema.screening_results(project_id); `); await prisma.$executeRawUnsafe(` CREATE INDEX IF NOT EXISTS idx_screening_results_literature_id ON asl_schema.screening_results(literature_id); `); await prisma.$executeRawUnsafe(` CREATE INDEX IF NOT EXISTS idx_screening_results_conflict_status ON asl_schema.screening_results(conflict_status); `); await prisma.$executeRawUnsafe(` CREATE INDEX IF NOT EXISTS idx_screening_results_final_decision ON asl_schema.screening_results(final_decision); `); // 4. 创建筛选任务表 await prisma.$executeRawUnsafe(` CREATE TABLE IF NOT EXISTS 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 ); `); console.log('✅ 创建 asl_schema.screening_tasks'); // 创建索引 await prisma.$executeRawUnsafe(` CREATE INDEX IF NOT EXISTS idx_screening_tasks_project_id ON asl_schema.screening_tasks(project_id); `); await prisma.$executeRawUnsafe(` CREATE INDEX IF NOT EXISTS idx_screening_tasks_status ON asl_schema.screening_tasks(status); `); console.log('\n✅ ASL模块4张表创建完成!'); console.log('📊 表列表:'); console.log(' - asl_schema.screening_projects (筛选项目)'); console.log(' - asl_schema.literatures (文献条目)'); console.log(' - asl_schema.screening_results (筛选结果)'); console.log(' - asl_schema.screening_tasks (筛选任务)'); // 验证表 const tables = await prisma.$queryRawUnsafe(` SELECT tablename FROM pg_tables WHERE schemaname = 'asl_schema' ORDER BY tablename; `); console.log('\n🔍 数据库验证:'); tables.forEach(t => console.log(` ✓ ${t.tablename}`)); } catch (error) { console.error('❌ 创建表失败:', error); throw error; } finally { await prisma.$disconnect(); } } createAslTables();