feat(health): P0 平台基座回顾 — 7项上线前必修
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:
@@ -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(())
|
||||
|
||||
Reference in New Issue
Block a user