feat(health): AI 行动分发器 — 风险分级路由到自动执行/医生审批/紧急确认
- dispatch_decision: 根据风险等级生成执行决策(low=自动, medium=24h审批, high=4h紧急) - handle_ai_suggestions: 遍历建议列表,按决策分发 - execute_action: 低风险自动发送预警/随访事件 - create_pending_action: 中高风险发送待审批事件 - 4 个单元测试覆盖:低/中/高/未知风险等级路由
This commit is contained in:
185
crates/erp-health/src/service/ai_action_dispatcher.rs
Normal file
185
crates/erp-health/src/service/ai_action_dispatcher.rs
Normal file
@@ -0,0 +1,185 @@
|
||||
use std::time::Duration;
|
||||
|
||||
use erp_core::events::EventBus;
|
||||
use sea_orm::DatabaseConnection;
|
||||
use uuid::Uuid;
|
||||
|
||||
/// 执行模式
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum ExecutionMode {
|
||||
AutoExecute,
|
||||
DoctorReview,
|
||||
UrgentConfirm,
|
||||
}
|
||||
|
||||
/// 分发决策
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct DispatchDecision {
|
||||
pub execution_mode: ExecutionMode,
|
||||
pub response_timeout: Option<Duration>,
|
||||
}
|
||||
|
||||
/// 根据风险等级和建议类型生成执行决策
|
||||
pub fn dispatch_decision(risk_level: &str, _suggestion_type: &str) -> DispatchDecision {
|
||||
match risk_level {
|
||||
"low" => DispatchDecision {
|
||||
execution_mode: ExecutionMode::AutoExecute,
|
||||
response_timeout: None,
|
||||
},
|
||||
"medium" => DispatchDecision {
|
||||
execution_mode: ExecutionMode::DoctorReview,
|
||||
response_timeout: Some(Duration::from_secs(86400)),
|
||||
},
|
||||
"high" => DispatchDecision {
|
||||
execution_mode: ExecutionMode::UrgentConfirm,
|
||||
response_timeout: Some(Duration::from_secs(14400)),
|
||||
},
|
||||
_ => DispatchDecision {
|
||||
execution_mode: ExecutionMode::DoctorReview,
|
||||
response_timeout: Some(Duration::from_secs(86400)),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// 处理 AI 建议事件:根据风险等级分发到不同执行路径
|
||||
pub async fn handle_ai_suggestions(
|
||||
db: &DatabaseConnection,
|
||||
event_bus: &EventBus,
|
||||
tenant_id: Uuid,
|
||||
_analysis_id: Uuid,
|
||||
patient_id: Uuid,
|
||||
doctor_id: Option<Uuid>,
|
||||
suggestions: &[serde_json::Value],
|
||||
risk_level: &str,
|
||||
) {
|
||||
for suggestion in suggestions {
|
||||
let suggestion_type = suggestion["type"].as_str().unwrap_or("alert");
|
||||
let decision = dispatch_decision(risk_level, suggestion_type);
|
||||
|
||||
match decision.execution_mode {
|
||||
ExecutionMode::AutoExecute => {
|
||||
execute_action(
|
||||
db,
|
||||
event_bus,
|
||||
tenant_id,
|
||||
patient_id,
|
||||
suggestion_type,
|
||||
suggestion,
|
||||
)
|
||||
.await;
|
||||
}
|
||||
ExecutionMode::DoctorReview | ExecutionMode::UrgentConfirm => {
|
||||
create_pending_action(
|
||||
db,
|
||||
event_bus,
|
||||
tenant_id,
|
||||
patient_id,
|
||||
doctor_id,
|
||||
suggestion_type,
|
||||
suggestion,
|
||||
risk_level,
|
||||
&decision,
|
||||
)
|
||||
.await;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn execute_action(
|
||||
db: &DatabaseConnection,
|
||||
event_bus: &EventBus,
|
||||
tenant_id: Uuid,
|
||||
patient_id: Uuid,
|
||||
action_type: &str,
|
||||
params: &serde_json::Value,
|
||||
) {
|
||||
match action_type {
|
||||
"alert" => {
|
||||
let event = erp_core::events::DomainEvent::new(
|
||||
"health.ai_alert.sent",
|
||||
tenant_id,
|
||||
erp_core::events::build_event_payload(serde_json::json!({
|
||||
"patient_id": patient_id,
|
||||
"alert_type": "ai_risk_warning",
|
||||
"severity": params.get("severity").and_then(|v| v.as_str()).unwrap_or("warning"),
|
||||
"message": params.get("message").and_then(|v| v.as_str()).unwrap_or(""),
|
||||
"source": "ai_analysis",
|
||||
})),
|
||||
);
|
||||
event_bus.publish(event, db).await;
|
||||
}
|
||||
"followup" | "appointment" => {
|
||||
let event = erp_core::events::DomainEvent::new(
|
||||
"health.ai_action.auto_executed",
|
||||
tenant_id,
|
||||
erp_core::events::build_event_payload(serde_json::json!({
|
||||
"patient_id": patient_id,
|
||||
"action_type": action_type,
|
||||
"params": params,
|
||||
})),
|
||||
);
|
||||
event_bus.publish(event, db).await;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
async fn create_pending_action(
|
||||
db: &DatabaseConnection,
|
||||
event_bus: &EventBus,
|
||||
tenant_id: Uuid,
|
||||
patient_id: Uuid,
|
||||
doctor_id: Option<Uuid>,
|
||||
action_type: &str,
|
||||
params: &serde_json::Value,
|
||||
risk_level: &str,
|
||||
decision: &DispatchDecision,
|
||||
) {
|
||||
let event = erp_core::events::DomainEvent::new(
|
||||
"health.ai_action.pending_approval",
|
||||
tenant_id,
|
||||
erp_core::events::build_event_payload(serde_json::json!({
|
||||
"patient_id": patient_id,
|
||||
"doctor_id": doctor_id,
|
||||
"action_type": action_type,
|
||||
"risk_level": risk_level,
|
||||
"timeout_seconds": decision.response_timeout.map(|d| d.as_secs()),
|
||||
"params": params,
|
||||
})),
|
||||
);
|
||||
event_bus.publish(event, db).await;
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn route_low_risk_to_auto_execute() {
|
||||
let decision = dispatch_decision("low", "alert");
|
||||
assert_eq!(decision.execution_mode, ExecutionMode::AutoExecute);
|
||||
assert_eq!(decision.response_timeout, None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn route_medium_risk_to_doctor_review() {
|
||||
let decision = dispatch_decision("medium", "followup");
|
||||
assert_eq!(decision.execution_mode, ExecutionMode::DoctorReview);
|
||||
assert_eq!(decision.response_timeout, Some(Duration::from_secs(86400)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn route_high_risk_to_urgent_confirm() {
|
||||
let decision = dispatch_decision("high", "alert");
|
||||
assert_eq!(decision.execution_mode, ExecutionMode::UrgentConfirm);
|
||||
assert_eq!(decision.response_timeout, Some(Duration::from_secs(14400)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn route_unknown_defaults_to_doctor_review() {
|
||||
let decision = dispatch_decision("unknown", "followup");
|
||||
assert_eq!(decision.execution_mode, ExecutionMode::DoctorReview);
|
||||
assert_eq!(decision.response_timeout, Some(Duration::from_secs(86400)));
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
pub mod ai_action_dispatcher;
|
||||
pub mod alert_engine;
|
||||
pub mod alert_rule_service;
|
||||
pub mod alert_service;
|
||||
|
||||
Reference in New Issue
Block a user