feat(ai): Copilot 事件消费者(订阅 health 事件触发风险评分刷新)
This commit is contained in:
85
crates/erp-ai/src/event/copilot_consumer.rs
Normal file
85
crates/erp-ai/src/event/copilot_consumer.rs
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
//! Copilot 事件消费者 — 订阅 health 模块事件,触发风险评分刷新
|
||||||
|
|
||||||
|
/// Copilot 关注的事件前缀
|
||||||
|
pub fn copilot_event_prefixes() -> Vec<String> {
|
||||||
|
vec![
|
||||||
|
"daily_monitoring.".to_string(),
|
||||||
|
"lab_report.".to_string(),
|
||||||
|
"follow_up.".to_string(),
|
||||||
|
"patient.".to_string(),
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 判断事件是否应触发风险评分刷新
|
||||||
|
pub fn should_trigger_risk_refresh(event_type: &str) -> bool {
|
||||||
|
matches!(
|
||||||
|
event_type,
|
||||||
|
"daily_monitoring.created"
|
||||||
|
| "lab_report.reviewed"
|
||||||
|
| "follow_up.completed"
|
||||||
|
| "follow_up.overdue"
|
||||||
|
| "patient.created"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 启动 Copilot 事件消费者
|
||||||
|
pub fn spawn(
|
||||||
|
db: &sea_orm::DatabaseConnection,
|
||||||
|
event_bus: &erp_core::events::EventBus,
|
||||||
|
) -> Vec<erp_core::events::SubscriptionHandle> {
|
||||||
|
let mut handles = Vec::new();
|
||||||
|
for prefix in copilot_event_prefixes() {
|
||||||
|
let (mut rx, handle) = event_bus.subscribe_filtered(prefix);
|
||||||
|
handles.push(handle);
|
||||||
|
let db = db.clone();
|
||||||
|
tokio::spawn(async move {
|
||||||
|
while let Some(event) = rx.recv().await {
|
||||||
|
if should_trigger_risk_refresh(&event.event_type) {
|
||||||
|
process_event(&db, &event).await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
handles
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn process_event(db: &sea_orm::DatabaseConnection, event: &erp_core::events::DomainEvent) {
|
||||||
|
if erp_core::events::is_event_processed(db, event.id, "copilot_consumer")
|
||||||
|
.await
|
||||||
|
.unwrap_or(false)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let tenant_id = event.tenant_id;
|
||||||
|
let patient_id = match event.payload.get("patient_id").and_then(|v| v.as_str()) {
|
||||||
|
Some(id) => match uuid::Uuid::parse_str(id) {
|
||||||
|
Ok(uid) => uid,
|
||||||
|
Err(_) => return,
|
||||||
|
},
|
||||||
|
None => return,
|
||||||
|
};
|
||||||
|
let _ =
|
||||||
|
crate::service::risk_service::RiskService::compute_risk(db, tenant_id, patient_id).await;
|
||||||
|
let _ = erp_core::events::mark_event_processed(db, event.id, "copilot_consumer").await;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_event_prefixes_include_health_events() {
|
||||||
|
let prefixes = copilot_event_prefixes();
|
||||||
|
assert!(prefixes.contains(&"daily_monitoring.".to_string()));
|
||||||
|
assert!(prefixes.contains(&"lab_report.".to_string()));
|
||||||
|
assert!(prefixes.contains(&"follow_up.".to_string()));
|
||||||
|
assert!(prefixes.contains(&"patient.".to_string()));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_should_trigger_risk_refresh_for_vital_signs() {
|
||||||
|
assert!(should_trigger_risk_refresh("daily_monitoring.created"));
|
||||||
|
assert!(should_trigger_risk_refresh("lab_report.reviewed"));
|
||||||
|
assert!(!should_trigger_risk_refresh("patient.updated"));
|
||||||
|
}
|
||||||
|
}
|
||||||
1
crates/erp-ai/src/event/mod.rs
Normal file
1
crates/erp-ai/src/event/mod.rs
Normal file
@@ -0,0 +1 @@
|
|||||||
|
pub mod copilot_consumer;
|
||||||
@@ -3,6 +3,7 @@ pub mod copilot;
|
|||||||
pub mod dto;
|
pub mod dto;
|
||||||
pub mod entity;
|
pub mod entity;
|
||||||
pub mod error;
|
pub mod error;
|
||||||
|
pub mod event;
|
||||||
pub mod handler;
|
pub mod handler;
|
||||||
pub mod knowledge;
|
pub mod knowledge;
|
||||||
pub mod module;
|
pub mod module;
|
||||||
|
|||||||
@@ -308,7 +308,14 @@ impl ErpModule for AiModule {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
tracing::info!(module = "ai", "AI 模块事件处理器已注册(监听 ai.* 事件)");
|
// Copilot 事件消费者 — 订阅 health 事件触发风险评分刷新
|
||||||
|
let copilot_handles = crate::event::copilot_consumer::spawn(&ctx.db, &ctx.event_bus);
|
||||||
|
std::mem::forget(copilot_handles);
|
||||||
|
|
||||||
|
tracing::info!(
|
||||||
|
module = "ai",
|
||||||
|
"AI 模块事件处理器已注册(监听 ai.* 事件 + Copilot 事件)"
|
||||||
|
);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user