/** * 测试脚本:验证表单状态和计算字段获取 */ import { PrismaClient } from '@prisma/client'; import { RedcapAdapter } from './src/modules/iit-manager/adapters/RedcapAdapter.js'; const prisma = new PrismaClient(); async function main() { console.log('='.repeat(60)); console.log('📋 表单状态和计算字段测试'); console.log('='.repeat(60)); // 1. 获取项目配置 const project = await prisma.iitProject.findFirst({ where: { name: { contains: 'test0207' } }, }); if (!project) { console.log('❌ 未找到项目'); await prisma.$disconnect(); return; } const adapter = new RedcapAdapter(project.redcapUrl, project.redcapApiToken); // =============================== // 测试 1:获取计算字段列表 // =============================== console.log('\n' + '='.repeat(60)); console.log('📊 测试 1:计算字段列表'); console.log('='.repeat(60)); const calcFields = await adapter.getCalculatedFields(); console.log(`\n共有 ${calcFields.length} 个计算字段:`); for (const field of calcFields) { console.log(` 📐 ${field.fieldName} (${field.fieldLabel})`); console.log(` 表单: ${field.formName}`); console.log(` 公式: ${field.calculation.substring(0, 80)}${field.calculation.length > 80 ? '...' : ''}`); } // =============================== // 测试 2:获取表单完成状态 // =============================== console.log('\n' + '='.repeat(60)); console.log('📊 测试 2:表单完成状态 (前 5 条记录)'); console.log('='.repeat(60)); const formStatus = await adapter.getFormCompletionStatus(); // 只显示前 5 条 for (const record of formStatus.slice(0, 5)) { console.log(`\n [Record ${record.recordId}] ${record.allComplete ? '✅ 全部完成' : '⚠️ 部分未完成'}`); // 统计各状态数量 const statusCounts = { Complete: 0, Unverified: 0, Incomplete: 0 }; for (const [formName, status] of Object.entries(record.forms)) { statusCounts[status.statusLabel]++; } console.log(` Complete: ${statusCounts.Complete}, Unverified: ${statusCounts.Unverified}, Incomplete: ${statusCounts.Incomplete}`); // 显示非 Complete 的表单 const nonComplete = Object.entries(record.forms) .filter(([_, s]) => s.statusLabel !== 'Complete') .map(([name, s]) => `${name}(${s.statusLabel})`); if (nonComplete.length > 0 && nonComplete.length <= 5) { console.log(` 未完成: ${nonComplete.join(', ')}`); } else if (nonComplete.length > 5) { console.log(` 未完成: ${nonComplete.slice(0, 5).join(', ')}... (共${nonComplete.length}个)`); } } // =============================== // 测试 3:单个表单状态检查 // =============================== console.log('\n' + '='.repeat(60)); console.log('📊 测试 3:检查 Record 3 的人口学表单状态'); console.log('='.repeat(60)); const demoFormStatus = await adapter.isFormComplete('3', 'basic_demography_form'); console.log(`\n Record 3 - basic_demography_form:`); console.log(` 状态: ${demoFormStatus.statusLabel} (${demoFormStatus.status})`); console.log(` 完成: ${demoFormStatus.isComplete ? '是' : '否'}`); // =============================== // 测试 4:关联分析 - 计算字段 vs 表单状态 // =============================== console.log('\n' + '='.repeat(60)); console.log('📊 测试 4:计算字段与表单状态关联分析'); console.log('='.repeat(60)); // 获取所有记录数据 const allRecords = await adapter.getAllRecordsMerged(); console.log('\n 分析计算字段 "age" 与表单状态的关系:'); console.log(' ' + '-'.repeat(50)); for (const record of allRecords.slice(0, 5)) { const recId = record.record_id; const age = record.age; const dob = record.date_of_birth; // 获取该记录的表单状态 const statusInfo = formStatus.find(s => s.recordId === recId); const demoStatus = statusInfo?.forms['basic_demography_form']?.statusLabel || 'Unknown'; const ageDisplay = age !== undefined && age !== '' ? age : '(空)'; console.log(` Record ${recId}: age=${ageDisplay}, dob=${dob || '(空)'}, 表单状态=${demoStatus}`); } // =============================== // 测试 5:按事件维度的表单完成状态(正确方式) // =============================== console.log('\n' + '='.repeat(60)); console.log('📊 测试 5:按事件维度的表单完成状态 (Record 1)'); console.log('='.repeat(60)); const eventStatus = await adapter.getFormCompletionStatusByEvent('1'); console.log(`\nRecord 1 共有 ${eventStatus.length} 个事件:`); for (const event of eventStatus) { const statusIcon = event.eventComplete ? '✅' : '⚠️'; console.log(`\n ${statusIcon} 事件: ${event.eventLabel} (${event.eventName})`); for (const [formName, status] of Object.entries(event.forms)) { const icon = status.status === 2 ? '✅' : (status.status === 1 ? '🟡' : '🔴'); console.log(` ${icon} ${formName}: ${status.statusLabel}`); } } // =============================== // 测试 6:获取表单-事件映射 // =============================== console.log('\n' + '='.repeat(60)); console.log('📊 测试 6:表单-事件映射统计'); console.log('='.repeat(60)); const formEventMapping = await adapter.getFormEventMapping(); console.log(`\n表单-事件映射总数: ${formEventMapping.length}`); // 按事件分组统计 const eventFormCount = new Map(); for (const m of formEventMapping) { eventFormCount.set(m.eventLabel, (eventFormCount.get(m.eventLabel) || 0) + 1); } console.log('\n各事件的表单数:'); for (const [event, count] of eventFormCount) { console.log(` ${event}: ${count} 个表单`); } console.log('\n' + '='.repeat(60)); console.log('✅ 测试完成'); console.log('='.repeat(60)); await prisma.$disconnect(); } main().catch(async (error) => { console.error('❌ 脚本出错:', error); await prisma.$disconnect(); process.exit(1); });