/** * 全文复筛API集成测试 * * 运行方式: * npx tsx src/modules/asl/fulltext-screening/__tests__/api-integration-test.ts */ import { PrismaClient } from '@prisma/client'; const prisma = new PrismaClient(); const BASE_URL = 'http://localhost:3001'; const API_PREFIX = '/api/v1/asl/fulltext-screening'; // 测试辅助函数 async function fetchJSON(url: string, options: RequestInit = {}) { const response = await fetch(url, { ...options, headers: { 'Content-Type': 'application/json', ...options.headers, }, }); const data = await response.json(); return { response, data }; } // 等待函数 const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms)); async function runTests() { console.log('🧪 开始全文复筛API集成测试\n'); try { // ==================== 准备测试数据 ==================== console.log('📋 步骤1: 准备测试数据...'); // 获取第一个项目 const project = await prisma.aslScreeningProject.findFirst({ include: { literatures: { take: 3, select: { id: true, title: true, }, }, }, }); if (!project) { throw new Error('未找到测试项目,请先创建项目和文献'); } if (project.literatures.length === 0) { throw new Error('项目中没有文献,请先导入文献'); } const projectId = project.id; const literatureIds = project.literatures.map((lit) => lit.id).slice(0, 2); console.log(` ✅ 项目ID: ${projectId}`); console.log(` ✅ 文献数量: ${literatureIds.length}`); console.log(` ✅ 文献列表:`, literatureIds); console.log(''); // ==================== 测试API 1: 创建任务 ==================== console.log('📋 步骤2: 测试创建全文复筛任务...'); const { response: createResponse, data: createData } = await fetchJSON( `${BASE_URL}${API_PREFIX}/tasks`, { method: 'POST', body: JSON.stringify({ projectId, literatureIds, modelA: 'deepseek-v3', modelB: 'qwen-max', promptVersion: 'v1.0.0', }), } ); if (createResponse.status !== 201 || !createData.success) { throw new Error(`创建任务失败: ${JSON.stringify(createData)}`); } const taskId = createData.data.taskId; console.log(` ✅ 任务创建成功`); console.log(` ✅ 任务ID: ${taskId}`); console.log(` ✅ 状态: ${createData.data.status}`); console.log(` ✅ 文献总数: ${createData.data.totalCount}`); console.log(''); // ==================== 测试API 2: 获取任务进度 ==================== console.log('📋 步骤3: 测试获取任务进度...'); // 等待一段时间让任务开始处理 console.log(' ⏳ 等待3秒让任务开始处理...'); await sleep(3000); const { response: progressResponse, data: progressData } = await fetchJSON( `${BASE_URL}${API_PREFIX}/tasks/${taskId}` ); if (progressResponse.status !== 200 || !progressData.success) { throw new Error(`获取进度失败: ${JSON.stringify(progressData)}`); } console.log(` ✅ 任务状态: ${progressData.data.status}`); console.log(` ✅ 进度: ${progressData.data.progress.processedCount}/${progressData.data.progress.totalCount} (${progressData.data.progress.progressPercent}%)`); console.log(` ✅ 成功: ${progressData.data.progress.successCount}`); console.log(` ✅ 失败: ${progressData.data.progress.failedCount}`); console.log(` ✅ 降级: ${progressData.data.progress.degradedCount}`); console.log(` ✅ Token: ${progressData.data.statistics.totalTokens}`); console.log(` ✅ 成本: ¥${progressData.data.statistics.totalCost.toFixed(4)}`); console.log(''); // 如果任务还在处理,等待完成 if (progressData.data.status === 'processing' || progressData.data.status === 'pending') { console.log(' ⏳ 任务仍在处理中,等待完成...'); let attempts = 0; const maxAttempts = 20; // 最多等待20次(约100秒) while (attempts < maxAttempts) { await sleep(5000); // 每5秒查询一次 const { data: checkData } = await fetchJSON( `${BASE_URL}${API_PREFIX}/tasks/${taskId}` ); console.log(` 📊 [${attempts + 1}/${maxAttempts}] 进度: ${checkData.data.progress.progressPercent}%, 状态: ${checkData.data.status}`); if (checkData.data.status === 'completed' || checkData.data.status === 'failed') { console.log(` ✅ 任务已完成,状态: ${checkData.data.status}`); break; } attempts++; } if (attempts >= maxAttempts) { console.log(' ⚠️ 任务处理超时,但继续测试后续API'); } console.log(''); } // ==================== 测试API 3: 获取任务结果 ==================== console.log('📋 步骤4: 测试获取任务结果...'); // 4.1 获取所有结果 const { response: resultsResponse, data: resultsData } = await fetchJSON( `${BASE_URL}${API_PREFIX}/tasks/${taskId}/results` ); if (resultsResponse.status !== 200 || !resultsData.success) { throw new Error(`获取结果失败: ${JSON.stringify(resultsData)}`); } console.log(` ✅ 获取所有结果成功`); console.log(` ✅ 总结果数: ${resultsData.data.total}`); console.log(` ✅ 当前页结果数: ${resultsData.data.results.length}`); console.log(` ✅ 冲突数: ${resultsData.data.summary.conflictCount}`); console.log(` ✅ 待审核: ${resultsData.data.summary.pendingReview}`); console.log(` ✅ 已审核: ${resultsData.data.summary.reviewed}`); if (resultsData.data.results.length > 0) { const firstResult = resultsData.data.results[0]; console.log(`\n 📄 第一个结果详情:`); console.log(` - 文献ID: ${firstResult.literatureId}`); console.log(` - 标题: ${firstResult.literature.title.slice(0, 60)}...`); console.log(` - 模型A状态: ${firstResult.modelAResult.status}`); console.log(` - 模型B状态: ${firstResult.modelBResult.status}`); console.log(` - 是否冲突: ${firstResult.conflict.isConflict ? '是' : '否'}`); console.log(` - 最终决策: ${firstResult.review.finalDecision || '待审核'}`); } console.log(''); // 4.2 测试筛选功能 console.log(' 🔍 测试结果筛选功能...'); const { data: conflictData } = await fetchJSON( `${BASE_URL}${API_PREFIX}/tasks/${taskId}/results?filter=conflict` ); console.log(` ✅ 冲突项筛选: ${conflictData.data.filtered}条`); const { data: pendingData } = await fetchJSON( `${BASE_URL}${API_PREFIX}/tasks/${taskId}/results?filter=pending` ); console.log(` ✅ 待审核筛选: ${pendingData.data.filtered}条`); console.log(''); // ==================== 测试API 4: 人工审核决策 ==================== if (resultsData.data.results.length > 0) { console.log('📋 步骤5: 测试人工审核决策...'); const resultId = resultsData.data.results[0].resultId; // 5.1 测试纳入决策 const { response: decisionResponse, data: decisionData } = await fetchJSON( `${BASE_URL}${API_PREFIX}/results/${resultId}/decision`, { method: 'PUT', body: JSON.stringify({ finalDecision: 'include', reviewNotes: '集成测试 - 自动审核纳入', }), } ); if (decisionResponse.status !== 200 || !decisionData.success) { throw new Error(`更新决策失败: ${JSON.stringify(decisionData)}`); } console.log(` ✅ 更新决策成功`); console.log(` ✅ 结果ID: ${decisionData.data.resultId}`); console.log(` ✅ 最终决策: ${decisionData.data.finalDecision}`); console.log(` ✅ 审核人: ${decisionData.data.reviewedBy}`); console.log(` ✅ 审核时间: ${new Date(decisionData.data.reviewedAt).toLocaleString('zh-CN')}`); console.log(''); // 5.2 测试排除决策(如果有第二个结果) if (resultsData.data.results.length > 1) { const secondResultId = resultsData.data.results[1].resultId; const { data: excludeData } = await fetchJSON( `${BASE_URL}${API_PREFIX}/results/${secondResultId}/decision`, { method: 'PUT', body: JSON.stringify({ finalDecision: 'exclude', exclusionReason: '测试排除原因 - 数据不完整', reviewNotes: '集成测试 - 自动审核排除', }), } ); console.log(` ✅ 排除决策测试成功`); console.log(` ✅ 排除原因: ${excludeData.data.exclusionReason}`); console.log(''); } } // ==================== 测试API 5: 导出Excel ==================== console.log('📋 步骤6: 测试导出Excel...'); const exportResponse = await fetch( `${BASE_URL}${API_PREFIX}/tasks/${taskId}/export`, { headers: { Accept: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', }, } ); if (exportResponse.status !== 200) { throw new Error(`导出Excel失败: ${exportResponse.statusText}`); } const buffer = await exportResponse.arrayBuffer(); console.log(` ✅ Excel导出成功`); console.log(` ✅ 文件大小: ${(buffer.byteLength / 1024).toFixed(2)} KB`); console.log(` ✅ Content-Type: ${exportResponse.headers.get('Content-Type')}`); console.log(''); // ==================== 测试完成 ==================== console.log('✅ 所有测试通过!\n'); console.log('📊 测试总结:'); console.log(' ✅ API 1: 创建任务 - 通过'); console.log(' ✅ API 2: 获取进度 - 通过'); console.log(' ✅ API 3: 获取结果 - 通过'); console.log(' ✅ API 4: 人工审核 - 通过'); console.log(' ✅ API 5: 导出Excel - 通过'); console.log(''); } catch (error: any) { console.error('\n❌ 测试失败:', error.message); console.error('\n详细错误:', error); process.exit(1); } finally { await prisma.$disconnect(); } } // 运行测试 runTests().catch((error) => { console.error('测试运行失败:', error); process.exit(1); });