feat(health): P0 平台基座回顾 — 7项上线前必修
Some checks failed
CI / rust-check (push) Has been cancelled
CI / rust-test (push) Has been cancelled
CI / frontend-build (push) Has been cancelled
CI / security-audit (push) Has been cancelled

P0-1: 危急值告警消费者 — health_data.critical_alert 事件推送给责任医护
P0-2: 危急值阈值可配置化 — 硬编码改为数据库配置(critical_value_threshold表),支持科室/年龄差异化
P0-3: daily_monitoring合并后告警验证 — update_vital_signs也触发危急值检测
P0-4: 随访逾期通知+幂等保护 — 只通知本次新标记的逾期任务,避免重复
P0-5: 知情同意记录(consent) — 新增实体/迁移/Service/Handler,PIPL合规
P0-6: 审计日志补全 — 患者更新记录前后值(过敏史/病史/状态变更)
P0-7: EventBus持久化增强 — 两阶段提交(pending→published)+启动时outbox relay恢复
This commit is contained in:
iven
2026-04-26 03:37:31 +08:00
parent e3177f262c
commit 4ab189283e
22 changed files with 1338 additions and 130 deletions

View File

@@ -280,6 +280,127 @@ async fn handle_workflow_event(
.map_err(|e| e.to_string())?;
}
}
"health_data.critical_alert" => {
let patient_name = event
.payload
.get("patient_name")
.and_then(|v| v.as_str())
.unwrap_or("未知患者");
let alert = event.payload.get("alert");
let indicator = alert
.and_then(|a| a.get("indicator"))
.and_then(|v| v.as_str())
.unwrap_or("未知指标");
let value = alert
.and_then(|a| a.get("value"))
.map(|v| v.to_string())
.unwrap_or_else(|| "?".to_string());
let direction = alert
.and_then(|a| a.get("direction"))
.and_then(|v| v.as_str())
.unwrap_or("high");
let direction_text = match direction {
"low" => "偏低",
_ => "偏高",
};
// 通知责任医生(优先)
if let Some(doctor_uid) = event
.payload
.get("doctor_user_id")
.and_then(|v| v.as_str())
.and_then(|s| uuid::Uuid::parse_str(s).ok())
{
let _ = crate::service::message_service::MessageService::send_system(
event.tenant_id,
doctor_uid,
format!("危急值告警:患者 {}", patient_name),
format!(
"患者 {}{}{}(值:{}),请立即关注处理。",
patient_name, indicator, direction_text, value
),
"urgent",
Some("critical_alert".to_string()),
Some(event.id),
db,
event_bus,
)
.await
.map_err(|e| e.to_string())?;
}
// 同时通知操作人(录入者)
if let Some(operator_uid) = event
.payload
.get("operator_id")
.and_then(|v| v.as_str())
.and_then(|s| uuid::Uuid::parse_str(s).ok())
{
// 避免医生和操作人是同一人时重复通知
let is_doctor = event
.payload
.get("doctor_user_id")
.and_then(|v| v.as_str())
.map(|s| s == operator_uid.to_string())
.unwrap_or(false);
if !is_doctor {
let _ = crate::service::message_service::MessageService::send_system(
event.tenant_id,
operator_uid,
format!("危急值告警:患者 {}", patient_name),
format!(
"患者 {}{}{}(值:{})已触发危急值告警,已通知责任医生。",
patient_name, indicator, direction_text, value
),
"important",
Some("critical_alert".to_string()),
Some(event.id),
db,
event_bus,
)
.await
.map_err(|e| e.to_string())?;
}
}
}
"follow_up.overdue" => {
let task_id = event
.payload
.get("task_id")
.and_then(|v| v.as_str())
.unwrap_or("unknown");
let assigned_to = event
.payload
.get("assigned_to")
.and_then(|v| v.as_str())
.and_then(|s| uuid::Uuid::parse_str(s).ok());
let planned_date = event
.payload
.get("planned_date")
.and_then(|v| v.as_str())
.unwrap_or("未知日期");
if let Some(assignee) = assigned_to {
let _ = crate::service::message_service::MessageService::send_system(
event.tenant_id,
assignee,
"随访任务逾期提醒".to_string(),
format!(
"您的随访任务(计划日期:{})已逾期,请尽快处理。",
planned_date
),
"important",
Some("follow_up".to_string()),
uuid::Uuid::parse_str(task_id).ok(),
db,
event_bus,
)
.await
.map_err(|e| e.to_string())?;
}
}
_ => {}
}
Ok(())