From 95db4fe9ff4452f21e9f20dc4961cb0a008d71be Mon Sep 17 00:00:00 2001 From: iven Date: Tue, 12 May 2026 12:18:40 +0800 Subject: [PATCH] =?UTF-8?q?feat(db):=2015=20=E6=9D=A1=20Copilot=20?= =?UTF-8?q?=E5=86=85=E7=BD=AE=E8=A7=84=E5=88=99=E7=A7=8D=E5=AD=90=E6=95=B0?= =?UTF-8?q?=E6=8D=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 覆盖 5 大类: 体征异常(4) + 化验异常(4) + 依从性(2) + 透析质量(3) + 综合(2) 系统级规则(tenant_id=nil)适用于所有机构 --- crates/erp-server/migration/src/lib.rs | 2 + .../m20260512_000142_seed_copilot_rules.rs | 214 ++++++++++++++++++ 2 files changed, 216 insertions(+) create mode 100644 crates/erp-server/migration/src/m20260512_000142_seed_copilot_rules.rs diff --git a/crates/erp-server/migration/src/lib.rs b/crates/erp-server/migration/src/lib.rs index 6561fe1..3d2fb45 100644 --- a/crates/erp-server/migration/src/lib.rs +++ b/crates/erp-server/migration/src/lib.rs @@ -143,6 +143,7 @@ mod m20260512_000138_create_copilot_rules; mod m20260512_000139_create_copilot_insights; mod m20260512_000140_create_copilot_risk_snapshots; mod m20260512_000141_create_copilot_chat_logs; +mod m20260512_000142_seed_copilot_rules; pub struct Migrator; @@ -293,6 +294,7 @@ impl MigratorTrait for Migrator { Box::new(m20260512_000139_create_copilot_insights::Migration), Box::new(m20260512_000140_create_copilot_risk_snapshots::Migration), Box::new(m20260512_000141_create_copilot_chat_logs::Migration), + Box::new(m20260512_000142_seed_copilot_rules::Migration), ] } } diff --git a/crates/erp-server/migration/src/m20260512_000142_seed_copilot_rules.rs b/crates/erp-server/migration/src/m20260512_000142_seed_copilot_rules.rs new file mode 100644 index 0000000..1956c68 --- /dev/null +++ b/crates/erp-server/migration/src/m20260512_000142_seed_copilot_rules.rs @@ -0,0 +1,214 @@ +//! 15 条 Copilot 内置规则种子数据 +//! +//! 覆盖 spec §3.1 的 5 大类:体征异常(4)、化验异常(4)、依从性(2)、透析质量(3)、综合(2) +//! 所有规则 tenant_id = nil(系统级规则,适用于所有机构) + +use sea_orm_migration::prelude::*; + +#[derive(DeriveMigrationName)] +pub struct Migration; + +#[async_trait::async_trait] +impl MigrationTrait for Migration { + async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> { + let db = manager.get_connection(); + let nil_tenant = "00000000-0000-0000-0000-000000000000"; + + // 15 条内置 Copilot 规则 (使用固定 UUID) + // (id_suffix, name, category, condition_expr, score, severity, suggestion, sort_order) + let rules: &[(i32, &str, &str, &str, i16, &str, &str, i32)] = &[ + // 体征异常 + ( + 1, + "血压持续偏高", + "vital_signs", + r#"{"and":[{"=": [{"var":"vital_signs.systolic_bp_morning.count_gte_140"}, 3]}]}"#, + 2, + "warning", + "建议增加血压监测频率并评估降压方案", + 1, + ), + ( + 2, + "舒张压持续偏高", + "vital_signs", + r#"{"and":[{"=": [{"var":"vital_signs.diastolic_bp_morning.count_gte_90"}, 3]}]}"#, + 1, + "info", + "持续舒张压偏高,建议关注并记录血压变化", + 2, + ), + ( + 3, + "体重周增幅过大", + "vital_signs", + r#"{">": [{"var":"vital_signs.weight.weekly_change_kg"}, 2]}"#, + 2, + "warning", + "体重周增幅超过2kg,建议评估水钠潴留情况", + 3, + ), + ( + 4, + "心动过速", + "vital_signs", + r#"{">": [{"var":"vital_signs.heart_rate.latest"}, 100]}"#, + 1, + "info", + "心率超过100次/分,建议进一步评估原因", + 4, + ), + // 化验异常 + ( + 5, + "eGFR下降", + "lab", + r#"{"<": [{"var":"lab_reports.egfr.latest"}, 60]}"#, + 3, + "warning", + "eGFR<60提示肾功能受损,建议调整透析方案", + 5, + ), + ( + 6, + "高钾血症风险", + "lab", + r#"{">": [{"var":"lab_reports.potassium.latest"}, 5.5]}"#, + 4, + "critical", + "立即通知主治医生,评估紧急透析需求", + 6, + ), + ( + 7, + "肌酐快速上升", + "lab", + r#"{">": [{"var":"lab_reports.creatinine.change_pct"}, 20]}"#, + 3, + "warning", + "肌酐环比增幅超过20%,建议评估急性肾损伤可能", + 7, + ), + ( + 8, + "血磷偏高", + "lab", + r#"{">": [{"var":"lab_reports.phosphorus.latest"}, 1.5]}"#, + 2, + "warning", + "血磷>1.5mmol/L,建议调整磷结合剂用量", + 8, + ), + // 依从性 + ( + 9, + "随访失约过多", + "compliance", + r#"{">": [{"var":"follow_up.missed_count"}, 2]}"#, + 1, + "info", + "近期随访失约超过2次,建议主动联系患者", + 9, + ), + ( + 10, + "药物依从性不足", + "compliance", + r#"{"<": [{"var":"medication.adherence_rate"}, 80]}"#, + 2, + "warning", + "药物依从性低于80%,建议加强用药教育", + 10, + ), + // 透析质量 + ( + 11, + "Kt/V不达标", + "dialysis", + r#"{"<": [{"var":"dialysis.kt_v.latest"}, 1.2]}"#, + 2, + "warning", + "Kt/V<1.2,建议评估透析充分性并调整透析参数", + 11, + ), + ( + 12, + "透析间期体重增长过多", + "dialysis", + r#"{">": [{"var":"dialysis.interdialytic_weight_gain_pct"}, 5]}"#, + 3, + "warning", + "透析间期体重增长超过5%,建议加强液体管理教育", + 12, + ), + ( + 13, + "透析前收缩压过高", + "dialysis", + r#"{">": [{"var":"dialysis.pre_systolic.latest"}, 180]}"#, + 3, + "warning", + "透析前收缩压>180mmHg,建议评估降压方案", + 13, + ), + // 综合规则 + ( + 14, + "肾功能恶化伴高钾", + "composite", + r#"{"and":[{"<": [{"var":"lab_reports.egfr.latest"}, 60]},{">": [{"var":"lab_reports.potassium.latest"}, 5.5]}]}"#, + 5, + "critical", + "eGFR下降且高钾血症,建议紧急评估并考虑紧急透析", + 14, + ), + ( + 15, + "血压急剧升高伴肾功能恶化", + "composite", + r#"{"and":[{">": [{"var":"vital_signs.systolic_bp_morning.latest"}, 160]},{">": [{"var":"lab_reports.creatinine.change_pct"}, 20]}]}"#, + 4, + "critical", + "血压急剧升高且肌酐快速上升,建议紧急评估并调整治疗方案", + 15, + ), + ]; + + for &(idx, name, category, cond, score, severity, suggestion, sort) in rules { + // 生成确定性 UUID: copilot-rule-XXXX 前缀 + let id = format!("c0000000-0000-0000-0000-{:012}", idx); + let sql = format!( + r#"INSERT INTO copilot_rules (id, tenant_id, name, category, condition_expr, score, severity, suggestion, enabled, sort_order, created_at, updated_at, version_lock) + VALUES ('{}', '{}', $1, $2, $3::jsonb, $4, $5, $6, true, $7, NOW(), NOW(), 1)"#, + id, nil_tenant + ); + db.execute(sea_orm::Statement::from_sql_and_values( + sea_orm::DatabaseBackend::Postgres, + sql, + [ + name.into(), + category.into(), + cond.into(), + score.into(), + severity.into(), + suggestion.into(), + sort.into(), + ], + )) + .await?; + } + + Ok(()) + } + + async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> { + let db = manager.get_connection(); + db.execute(sea_orm::Statement::from_sql_and_values( + sea_orm::DatabaseBackend::Postgres, + "DELETE FROM copilot_rules WHERE tenant_id = '00000000-0000-0000-0000-000000000000'", + [], + )) + .await?; + Ok(()) + } +}