156 lines
5.3 KiB
TypeScript
156 lines
5.3 KiB
TypeScript
/**
|
||
* 调试脚本:诊断字段获取问题
|
||
*
|
||
* 问题:
|
||
* 1. Record 1 有 age=21,但质控显示空
|
||
* 2. Record 3 应该有年龄但 API 返回无
|
||
*/
|
||
import { PrismaClient } from '@prisma/client';
|
||
import { RedcapAdapter } from './src/modules/iit-manager/adapters/RedcapAdapter.js';
|
||
import jsonLogic from 'json-logic-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;
|
||
}
|
||
|
||
// 2. 获取质控规则配置
|
||
const skill = await prisma.iitSkill.findFirst({
|
||
where: { projectId: project.id, isActive: true },
|
||
});
|
||
|
||
if (!skill) {
|
||
console.log('❌ 未找到激活的 Skill');
|
||
await prisma.$disconnect();
|
||
return;
|
||
}
|
||
|
||
const config = skill.config as any;
|
||
const rules = config?.rules || [];
|
||
|
||
console.log('\n📋 质控规则中的字段配置:');
|
||
for (const rule of rules) {
|
||
console.log(` ${rule.name}:`);
|
||
console.log(` field: "${rule.field}"`);
|
||
console.log(` logic: ${JSON.stringify(rule.logic)}`);
|
||
}
|
||
|
||
// 3. 从 REDCap 获取原始数据(检查 Record 1 和 Record 3)
|
||
const adapter = new RedcapAdapter(project.redcapUrl, project.redcapApiToken);
|
||
|
||
console.log('\n' + '='.repeat(60));
|
||
console.log('📊 检查 Record 1 的原始 REDCap 数据');
|
||
console.log('='.repeat(60));
|
||
|
||
// 获取 Record 1 的原始记录(不合并)
|
||
const rawRecords1 = await adapter.exportRecords({ records: ['1'] });
|
||
console.log(`\nRecord 1 原始记录数: ${rawRecords1.length} (多事件)`);
|
||
for (const r of rawRecords1) {
|
||
console.log(` 事件: ${r.redcap_event_name || '(无)'}`);
|
||
console.log(` age: ${r.age !== undefined ? r.age : '(字段不存在)'}`);
|
||
console.log(` date_of_birth: ${r.date_of_birth || '(空)'}`);
|
||
}
|
||
|
||
// 获取 Record 1 合并后的数据
|
||
const merged1 = await adapter.getRecordById('1');
|
||
console.log(`\nRecord 1 合并后数据:`);
|
||
console.log(` age: ${merged1?.age}`);
|
||
console.log(` date_of_birth: ${merged1?.date_of_birth}`);
|
||
|
||
console.log('\n' + '='.repeat(60));
|
||
console.log('📊 检查 Record 3 的原始 REDCap 数据');
|
||
console.log('='.repeat(60));
|
||
|
||
const rawRecords3 = await adapter.exportRecords({ records: ['3'] });
|
||
console.log(`\nRecord 3 原始记录数: ${rawRecords3.length} (多事件)`);
|
||
for (const r of rawRecords3) {
|
||
console.log(` 事件: ${r.redcap_event_name || '(无)'}`);
|
||
console.log(` age: ${r.age !== undefined ? r.age : '(字段不存在)'}`);
|
||
console.log(` date_of_birth: ${r.date_of_birth || '(空)'}`);
|
||
}
|
||
|
||
const merged3 = await adapter.getRecordById('3');
|
||
console.log(`\nRecord 3 合并后数据:`);
|
||
console.log(` age: ${merged3?.age}`);
|
||
console.log(` date_of_birth: ${merged3?.date_of_birth}`);
|
||
|
||
// 4. 模拟质控规则执行,看看哪里出问题
|
||
console.log('\n' + '='.repeat(60));
|
||
console.log('📊 模拟质控规则执行 (Record 1)');
|
||
console.log('='.repeat(60));
|
||
|
||
// 找到年龄规则
|
||
const ageRule = rules.find((r: any) => r.name.includes('年龄'));
|
||
if (ageRule && merged1) {
|
||
console.log(`\n规则: ${ageRule.name}`);
|
||
console.log(` field: "${ageRule.field}"`);
|
||
console.log(` logic: ${JSON.stringify(ageRule.logic)}`);
|
||
|
||
// 检查字段值
|
||
const fieldValue = merged1[ageRule.field];
|
||
console.log(`\n 从数据中获取 "${ageRule.field}" 的值: ${fieldValue} (类型: ${typeof fieldValue})`);
|
||
|
||
// 尝试执行 JSON Logic
|
||
try {
|
||
const result = jsonLogic.apply(ageRule.logic, merged1);
|
||
console.log(` JSON Logic 执行结果: ${result}`);
|
||
} catch (error: any) {
|
||
console.log(` JSON Logic 执行失败: ${error.message}`);
|
||
}
|
||
|
||
// 列出数据中所有可能的年龄相关字段
|
||
console.log(`\n 数据中的年龄相关字段:`);
|
||
for (const [key, value] of Object.entries(merged1)) {
|
||
if (key.toLowerCase().includes('age') || key === ageRule.field) {
|
||
console.log(` ${key}: ${value}`);
|
||
}
|
||
}
|
||
}
|
||
|
||
// 5. 检查 SkillRunner 如何获取记录
|
||
console.log('\n' + '='.repeat(60));
|
||
console.log('📊 检查 SkillRunner 获取记录的方式');
|
||
console.log('='.repeat(60));
|
||
|
||
// 查看 getRecordsToProcess 的实现逻辑
|
||
// 它可能使用不同的方法获取数据
|
||
const allRecordsMerged = await adapter.getAllRecordsMerged();
|
||
console.log(`\ngetAllRecordsMerged 返回 ${allRecordsMerged.length} 条记录`);
|
||
|
||
const r1FromAll = allRecordsMerged.find(r => r.record_id === '1');
|
||
const r3FromAll = allRecordsMerged.find(r => r.record_id === '3');
|
||
|
||
console.log(`\nRecord 1 from getAllRecordsMerged:`);
|
||
console.log(` age: ${r1FromAll?.age}`);
|
||
console.log(` date_of_birth: ${r1FromAll?.date_of_birth}`);
|
||
|
||
console.log(`\nRecord 3 from getAllRecordsMerged:`);
|
||
console.log(` age: ${r3FromAll?.age}`);
|
||
console.log(` date_of_birth: ${r3FromAll?.date_of_birth}`);
|
||
|
||
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);
|
||
});
|