/** * IIT Manager Agent - 质控规则初始化脚本 * * 项目:原发性痛经队列研究 * * 使用方法: * npx tsx prisma/seed-iit-qc-rules.ts * * 创建日期:2026-02-07 */ import { PrismaClient } from '@prisma/client'; const prisma = new PrismaClient(); // ============================================================ // 项目配置 // ============================================================ const PROJECT_ID = 'test0102-project-id'; // 需要替换为实际的项目 ID // ============================================================ // 纳入标准规则 // ============================================================ const INCLUSION_RULES = [ { id: 'inc_001', name: '年龄范围检查', field: 'age', logic: { and: [ { '>=': [{ var: 'age' }, 16] }, { '<=': [{ var: 'age' }, 35] } ] }, message: '年龄不在 16-35 岁范围内', severity: 'error', category: 'inclusion' }, { id: 'inc_002', name: '出生日期范围检查', field: 'birth_date', logic: { and: [ { '>=': [{ var: 'birth_date' }, '1989-01-01'] }, { '<=': [{ var: 'birth_date' }, '2008-01-01'] } ] }, message: '出生日期不在 1989-01-01 至 2008-01-01 范围内', severity: 'error', category: 'inclusion' }, { id: 'inc_003', name: '月经周期规律性检查', field: 'menstrual_cycle', logic: { and: [ { '>=': [{ var: 'menstrual_cycle' }, 21] }, { '<=': [{ var: 'menstrual_cycle' }, 35] } ] }, message: '月经周期不在 21-35 天范围内(28±7天)', severity: 'error', category: 'inclusion' }, { id: 'inc_004', name: 'VAS 评分检查', field: 'vas_score', logic: { '>=': [{ var: 'vas_score' }, 4] }, message: 'VAS 疼痛评分 < 4 分,不符合入组条件', severity: 'error', category: 'inclusion' }, { id: 'inc_005', name: '知情同意书签署检查', field: 'informed_consent', logic: { '==': [{ var: 'informed_consent' }, 1] }, message: '未签署知情同意书', severity: 'error', category: 'inclusion' } ]; // ============================================================ // 排除标准规则 // ============================================================ const EXCLUSION_RULES = [ { id: 'exc_001', name: '继发性痛经排除', field: 'secondary_dysmenorrhea', logic: { '!=': [{ var: 'secondary_dysmenorrhea' }, 1] }, message: '存在继发性痛经(盆腔炎、子宫内膜异位症、子宫腺肌病等)', severity: 'error', category: 'exclusion' }, { id: 'exc_002', name: '妊娠哺乳期排除', field: 'pregnancy_lactation', logic: { '!=': [{ var: 'pregnancy_lactation' }, 1] }, message: '妊娠或哺乳期妇女,不符合入组条件', severity: 'error', category: 'exclusion' }, { id: 'exc_003', name: '严重疾病排除', field: 'severe_disease', logic: { '!=': [{ var: 'severe_disease' }, 1] }, message: '合并有心脑血管、肝、肾、造血系统等严重疾病或精神病', severity: 'error', category: 'exclusion' }, { id: 'exc_004', name: '月经周期不规律排除', field: 'irregular_menstruation', logic: { '!=': [{ var: 'irregular_menstruation' }, 1] }, message: '月经周期不规律或间歇性痛经发作', severity: 'error', category: 'exclusion' } ]; // ============================================================ // 血常规变量范围规则 // ============================================================ const LAB_VALUE_RULES = [ // 1. 白细胞计数 { id: 'lab_001', name: '白细胞计数', field: 'wbc', min: 3.5, max: 9.5, unit: '*10^9/L' }, // 2. 红细胞计数 { id: 'lab_002', name: '红细胞计数', field: 'rbc', min: 3.8, max: 5.1, unit: '*10^12/L' }, // 3. 血红蛋白 { id: 'lab_003', name: '血红蛋白', field: 'hgb', min: 115, max: 150, unit: 'g/L' }, // 4. 红细胞压积 { id: 'lab_004', name: '红细胞压积', field: 'hct', min: 35, max: 45, unit: '%' }, // 5. 平均红细胞体积 { id: 'lab_005', name: '平均红细胞体积', field: 'mcv', min: 82, max: 100, unit: 'fL' }, // 6. 平均血红蛋白量 { id: 'lab_006', name: '平均血红蛋白量', field: 'mch', min: 27, max: 34, unit: 'pg' }, // 7. 平均血红蛋白浓度 { id: 'lab_007', name: '平均血红蛋白浓度', field: 'mchc', min: 316, max: 354, unit: 'g/L' }, // 8. 红细胞体积分布宽度-SD { id: 'lab_008', name: '红细胞体积分布宽度-SD', field: 'rdw_sd', min: 37, max: 51, unit: 'fL' }, // 9. 红细胞体积分布宽度-CV { id: 'lab_009', name: '红细胞体积分布宽度-CV', field: 'rdw_cv', min: 0, max: 14.9, unit: '%' }, // 10. 血小板计数 { id: 'lab_010', name: '血小板计数', field: 'plt', min: 125, max: 350, unit: '*10^9/L' }, // 11. 血小板平均体积 { id: 'lab_011', name: '血小板平均体积', field: 'mpv', min: 7.2, max: 13.2, unit: 'fL' }, // 12. 血小板体积分布宽度 { id: 'lab_012', name: '血小板体积分布宽度', field: 'pdw', min: 9, max: 13, unit: 'fL' }, // 13. 血小板比积 { id: 'lab_013', name: '血小板比积', field: 'pct', min: 0.18, max: 0.22, unit: '%' }, // 14. 大血小板比率 { id: 'lab_014', name: '大血小板比率', field: 'p_lcr', min: 13, max: 43, unit: '%' }, // 15. 中性粒细胞绝对值 { id: 'lab_015', name: '中性粒细胞绝对值', field: 'neut_abs', min: 1.8, max: 6.3, unit: '*10^9/L' }, // 16. 淋巴细胞绝对值 { id: 'lab_016', name: '淋巴细胞绝对值', field: 'lymph_abs', min: 1.1, max: 3.2, unit: '*10^9/L' }, // 17. 单核细胞绝对值 { id: 'lab_017', name: '单核细胞绝对值', field: 'mono_abs', min: 0.1, max: 0.6, unit: '*10^9/L' }, // 18. 嗜酸细胞绝对值 { id: 'lab_018', name: '嗜酸细胞绝对值', field: 'eo_abs', min: 0.02, max: 0.52, unit: '*10^9/L' }, // 19. 嗜碱细胞绝对值 { id: 'lab_019', name: '嗜碱细胞绝对值', field: 'baso_abs', min: 0, max: 0.06, unit: '*10^9/L' }, // 20. 中性粒细胞相对值 { id: 'lab_020', name: '中性粒细胞相对值', field: 'neut_pct', min: 40, max: 75, unit: '%' }, // 21. 淋巴细胞相对值 { id: 'lab_021', name: '淋巴细胞相对值', field: 'lymph_pct', min: 20, max: 50, unit: '%' }, // 22. 单核细胞相对值 { id: 'lab_022', name: '单核细胞相对值', field: 'mono_pct', min: 3, max: 10, unit: '%' }, // 23. 嗜酸细胞相对值 { id: 'lab_023', name: '嗜酸细胞相对值', field: 'eo_pct', min: 0.4, max: 8, unit: '%' }, // 24. 嗜碱细胞相对值 { id: 'lab_024', name: '嗜碱细胞相对值', field: 'baso_pct', min: 0, max: 1, unit: '%' }, ]; // 将实验室检查转换为 JSON Logic 规则 const LAB_RULES = LAB_VALUE_RULES.map(item => ({ id: item.id, name: `${item.name}范围检查`, field: item.field, logic: { or: [ { '==': [{ var: item.field }, null] }, // 允许为空(可选检查项) { and: [ { '>=': [{ var: item.field }, item.min] }, { '<=': [{ var: item.field }, item.max] } ] } ] }, message: `${item.name}超出正常范围(${item.min}-${item.max} ${item.unit})`, severity: 'warning', category: 'lab_values', metadata: { min: item.min, max: item.max, unit: item.unit } })); // ============================================================ // 字段映射配置 // ============================================================ const FIELD_MAPPINGS = [ // 基本信息 { aliasName: '年龄', actualName: 'age', fieldType: 'number', fieldLabel: '年龄' }, { aliasName: 'age', actualName: 'age', fieldType: 'number', fieldLabel: '年龄' }, { aliasName: '出生日期', actualName: 'birth_date', fieldType: 'date', fieldLabel: '出生日期' }, { aliasName: 'birth_date', actualName: 'birth_date', fieldType: 'date', fieldLabel: '出生日期' }, // 入排标准相关 { aliasName: '月经周期', actualName: 'menstrual_cycle', fieldType: 'number', fieldLabel: '月经周期(天)' }, { aliasName: 'menstrual_cycle', actualName: 'menstrual_cycle', fieldType: 'number', fieldLabel: '月经周期(天)' }, { aliasName: 'VAS评分', actualName: 'vas_score', fieldType: 'number', fieldLabel: 'VAS疼痛评分' }, { aliasName: 'vas_score', actualName: 'vas_score', fieldType: 'number', fieldLabel: 'VAS疼痛评分' }, { aliasName: '知情同意', actualName: 'informed_consent', fieldType: 'radio', fieldLabel: '是否签署知情同意书' }, { aliasName: 'informed_consent', actualName: 'informed_consent', fieldType: 'radio', fieldLabel: '是否签署知情同意书' }, // 排除标准相关 { aliasName: '继发性痛经', actualName: 'secondary_dysmenorrhea', fieldType: 'radio', fieldLabel: '继发性痛经' }, { aliasName: '妊娠哺乳', actualName: 'pregnancy_lactation', fieldType: 'radio', fieldLabel: '妊娠或哺乳期' }, { aliasName: '严重疾病', actualName: 'severe_disease', fieldType: 'radio', fieldLabel: '严重疾病' }, { aliasName: '月经不规律', actualName: 'irregular_menstruation', fieldType: 'radio', fieldLabel: '月经周期不规律' }, // 血常规字段映射 { aliasName: '白细胞', actualName: 'wbc', fieldType: 'number', fieldLabel: '白细胞计数' }, { aliasName: '红细胞', actualName: 'rbc', fieldType: 'number', fieldLabel: '红细胞计数' }, { aliasName: '血红蛋白', actualName: 'hgb', fieldType: 'number', fieldLabel: '血红蛋白' }, { aliasName: '血小板', actualName: 'plt', fieldType: 'number', fieldLabel: '血小板计数' }, // ... 更多字段可按需添加 ]; // ============================================================ // 主函数 // ============================================================ async function main() { console.log('🚀 开始初始化 IIT Manager 质控规则...\n'); // 1. 先获取项目 ID const project = await prisma.iitProject.findFirst({ where: { name: 'test0102' } }); if (!project) { console.error('❌ 未找到项目 test0102,请先创建项目'); console.log('💡 提示:可以在 iit_schema.projects 表中创建项目'); return; } const projectId = project.id; console.log(`✅ 找到项目: ${project.name} (${projectId})\n`); // 2. 创建质控技能配置 console.log('📋 创建质控技能配置...'); const allRules = [...INCLUSION_RULES, ...EXCLUSION_RULES, ...LAB_RULES]; await prisma.iitSkill.upsert({ where: { projectId_skillType: { projectId: projectId, skillType: 'qc_process' } }, update: { name: '原发性痛经队列研究-入组质控', description: '包含纳入标准、排除标准、血常规范围检查', config: { version: '1.0', rules: allRules, summary: { totalRules: allRules.length, inclusionRules: INCLUSION_RULES.length, exclusionRules: EXCLUSION_RULES.length, labRules: LAB_RULES.length } }, isActive: true, triggerType: 'webhook', updatedAt: new Date() }, create: { projectId: projectId, skillType: 'qc_process', name: '原发性痛经队列研究-入组质控', description: '包含纳入标准、排除标准、血常规范围检查', config: { version: '1.0', rules: allRules, summary: { totalRules: allRules.length, inclusionRules: INCLUSION_RULES.length, exclusionRules: EXCLUSION_RULES.length, labRules: LAB_RULES.length } }, isActive: true, triggerType: 'webhook' } }); console.log(` ✅ 已创建质控技能,共 ${allRules.length} 条规则`); console.log(` - 纳入标准: ${INCLUSION_RULES.length} 条`); console.log(` - 排除标准: ${EXCLUSION_RULES.length} 条`); console.log(` - 血常规范围: ${LAB_RULES.length} 条\n`); // 3. 创建字段映射 console.log('🔗 创建字段映射...'); for (const mapping of FIELD_MAPPINGS) { await prisma.iitFieldMapping.upsert({ where: { projectId_aliasName: { projectId: projectId, aliasName: mapping.aliasName } }, update: { actualName: mapping.actualName, fieldType: mapping.fieldType, fieldLabel: mapping.fieldLabel }, create: { projectId: projectId, aliasName: mapping.aliasName, actualName: mapping.actualName, fieldType: mapping.fieldType, fieldLabel: mapping.fieldLabel } }); } console.log(` ✅ 已创建 ${FIELD_MAPPINGS.length} 条字段映射\n`); // 4. 输出汇总 console.log('=' .repeat(60)); console.log('📊 初始化完成汇总'); console.log('=' .repeat(60)); console.log(`项目名称: ${project.name}`); console.log(`项目 ID: ${projectId}`); console.log(`质控规则总数: ${allRules.length}`); console.log(`字段映射总数: ${FIELD_MAPPINGS.length}`); console.log('=' .repeat(60)); console.log('\n✅ IIT Manager 质控规则初始化完成!'); } main() .catch((e) => { console.error('❌ 初始化失败:', e); process.exit(1); }) .finally(async () => { await prisma.$disconnect(); });