feat(db): 15 条 Copilot 内置规则种子数据

覆盖 5 大类: 体征异常(4) + 化验异常(4) + 依从性(2) + 透析质量(3) + 综合(2)
系统级规则(tenant_id=nil)适用于所有机构
This commit is contained in:
iven
2026-05-12 12:18:40 +08:00
parent 57f33dd726
commit 95db4fe9ff
2 changed files with 216 additions and 0 deletions

View File

@@ -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),
]
}
}

View File

@@ -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(())
}
}