/** * 端到端真实测试 v2 - 简化版 * * 使用真实数据测试完整流程: * 1. 创建项目 * 2. 导入1篇文献(简化) * 3. 创建全文复筛任务 * 4. 等待LLM处理 * 5. 查看结果 */ import axios from 'axios'; import { PrismaClient } from '@prisma/client'; import fs from 'fs/promises'; import path from 'path'; const API_BASE = 'http://localhost:3000/api/v1/asl'; const prisma = new PrismaClient(); interface TestResult { projectId?: string; literatureIds?: string[]; taskId?: string; success: boolean; error?: string; } async function runTest(): Promise { console.log('🚀 开始端到端真实测试 v2\n'); console.log('⏰ 测试时间:', new Date().toLocaleString('zh-CN')); console.log('📍 API地址:', API_BASE); console.log('=' .repeat(80) + '\n'); const result: TestResult = { success: false }; try { // ======================================== // Step 1: 创建测试项目 // ======================================== console.log('📋 Step 1: 创建测试项目'); const picosPath = path.join( process.cwd(), '../docs/03-业务模块/ASL-AI智能文献/05-测试文档/03-测试数据/screening/测试案例的PICOS、纳入标准、排除标准.txt' ); const picosContent = await fs.readFile(picosPath, 'utf-8'); // 解析PICOS const populationMatch = picosContent.match(/P \(Population\)[::]\s*(.+)/); const interventionMatch = picosContent.match(/I \(Intervention\)[::]\s*(.+)/); const comparisonMatch = picosContent.match(/C \(Comparison\)[::]\s*(.+)/); const outcomeMatch = picosContent.match(/O \(Outcome\)[::]\s*(.+)/); const studyDesignMatch = picosContent.match(/S \(Study Design\)[::]\s*(.+)/); const projectData = { name: `E2E测试-${Date.now()}`, description: '端到端真实测试项目', picoCriteria: { P: populationMatch?.[1]?.trim() || '缺血性卒中患者', I: interventionMatch?.[1]?.trim() || '抗血小板治疗', C: comparisonMatch?.[1]?.trim() || '对照组', O: outcomeMatch?.[1]?.trim() || '卒中复发', S: studyDesignMatch?.[1]?.trim() || 'RCT', }, }; const projectResponse = await axios.post(`${API_BASE}/projects`, projectData); result.projectId = projectResponse.data.data.id; console.log(`✅ 项目创建成功: ${result.projectId}\n`); // ======================================== // Step 2: 导入1篇简单测试文献 // ======================================== console.log('📚 Step 2: 导入测试文献(使用简化数据)'); const literatureData = { projectId: result.projectId, literatures: [ { pmid: 'TEST001', title: 'Antiplatelet Therapy for Secondary Stroke Prevention: A Randomized Controlled Trial', abstract: 'Background: Stroke is a major cause of death worldwide. This study evaluates antiplatelet therapy effectiveness. Methods: We conducted an RCT with 500 patients randomized to aspirin vs clopidogrel groups. The study was double-blind. Results: Primary outcome (stroke recurrence) occurred in 12% of aspirin group vs 8% of clopidogrel group (p=0.03). Secondary outcomes showed similar trends. Conclusion: Clopidogrel demonstrates superior efficacy for secondary stroke prevention in Asian patients.', authors: 'Zhang W, Li H, Wang Y', journal: 'Stroke Research', publicationYear: 2023, hasPdf: false, }, ], }; const importResponse = await axios.post(`${API_BASE}/literatures/import`, literatureData); console.log(`✅ 文献导入成功: ${importResponse.data.data.importedCount}篇\n`); // 获取文献ID const literatures = await prisma.aslLiterature.findMany({ where: { projectId: result.projectId }, select: { id: true, title: true }, }); result.literatureIds = literatures.map(lit => lit.id); console.log('📄 导入的文献:'); literatures.forEach(lit => { console.log(` - ${lit.id.slice(0, 8)}: ${lit.title.slice(0, 60)}...`); }); console.log(''); // ======================================== // Step 3: 创建全文复筛任务 // ======================================== console.log('🤖 Step 3: 创建全文复筛任务'); const taskData = { projectId: result.projectId, literatureIds: result.literatureIds, config: { modelA: 'deepseek-v3', modelB: 'qwen-max', concurrency: 1, skipExtraction: true, // 跳过PDF提取,使用标题+摘要 }, }; const taskResponse = await axios.post(`${API_BASE}/fulltext-screening/tasks`, taskData); result.taskId = taskResponse.data.data.taskId; console.log(`✅ 任务创建成功: ${result.taskId}\n`); // ======================================== // Step 4: 监控任务进度 // ======================================== console.log('⏳ Step 4: 监控任务进度(等待LLM处理)\n'); let maxAttempts = 30; // 最多等待5分钟 let attempt = 0; let taskCompleted = false; while (attempt < maxAttempts && !taskCompleted) { await new Promise(resolve => setTimeout(resolve, 10000)); // 每10秒查询一次 attempt++; try { const progressResponse = await axios.get( `${API_BASE}/fulltext-screening/tasks/${result.taskId}/progress` ); const progress = progressResponse.data.data; console.log(`[${attempt}/${maxAttempts}] 进度: ${progress.processedCount}/${progress.totalCount} | ` + `成功: ${progress.successCount} | 失败: ${progress.failedCount} | ` + `Token: ${progress.totalTokens} | 成本: ¥${progress.totalCost.toFixed(4)}`); if (progress.status === 'completed' || progress.status === 'failed') { taskCompleted = true; console.log(`\n✅ 任务完成!状态: ${progress.status}\n`); } } catch (error: any) { console.log(`⚠️ 查询进度失败: ${error.message}`); } } if (!taskCompleted) { console.log('⚠️ 任务超时,但可能仍在后台处理\n'); } // ======================================== // Step 5: 获取结果 // ======================================== console.log('📊 Step 5: 获取处理结果\n'); try { const resultsResponse = await axios.get( `${API_BASE}/fulltext-screening/tasks/${result.taskId}/results` ); const results = resultsResponse.data.data; console.log('=' .repeat(80)); console.log('📈 最终统计:'); console.log(` - 总文献数: ${results.results.length}`); console.log(` - 总Token: ${results.summary.totalTokens}`); console.log(` - 总成本: ¥${results.summary.totalCost.toFixed(4)}`); console.log(''); if (results.results.length > 0) { console.log('📄 文献结果详情:'); results.results.forEach((r: any, idx: number) => { console.log(`\n[${idx + 1}] ${r.literatureTitle}`); console.log(` Model A (${r.modelAName}): ${r.modelAStatus}`); console.log(` Model B (${r.modelBName}): ${r.modelBStatus}`); console.log(` Token: ${r.modelATokens + r.modelBTokens}`); console.log(` 成本: ¥${(r.modelACost + r.modelBCost).toFixed(4)}`); if (r.modelAStatus === 'success' && r.modelAOverall) { console.log(` 决策: ${r.modelAOverall.overall_decision || 'N/A'}`); } }); } result.success = results.results.length > 0; } catch (error: any) { console.log(`❌ 获取结果失败: ${error.message}`); } console.log('\n' + '=' .repeat(80)); console.log('🎉 测试完成!\n'); } catch (error: any) { console.error('\n❌ 测试失败:', error.message); if (error.response?.data) { console.error('错误详情:', JSON.stringify(error.response.data, null, 2)); } result.success = false; result.error = error.message; } finally { await prisma.$disconnect(); } return result; } // 运行测试 runTest() .then(result => { if (result.success) { console.log('✅ 端到端测试成功!'); process.exit(0); } else { console.log('❌ 端到端测试失败'); process.exit(1); } }) .catch(error => { console.error('💥 测试执行异常:', error); process.exit(1); });