diff --git a/crates/erp-ai/src/module.rs b/crates/erp-ai/src/module.rs index 08a265b..c0cb87c 100644 --- a/crates/erp-ai/src/module.rs +++ b/crates/erp-ai/src/module.rs @@ -312,6 +312,25 @@ impl ErpModule for AiModule { let copilot_handles = crate::event::copilot_consumer::spawn(&ctx.db, &ctx.event_bus); std::mem::forget(copilot_handles); + // 每日凌晨 2:00 批量刷新所有在管患者风险快照 + let refresh_db = ctx.db.clone(); + tokio::spawn(async move { + let mut interval = tokio::time::interval(std::time::Duration::from_secs(86400)); + loop { + interval.tick().await; + match crate::service::risk_service::RiskService::refresh_all_patients(&refresh_db) + .await + { + Ok(count) => { + tracing::info!(patient_count = count, "每日风险快照刷新完成"); + } + Err(e) => { + tracing::warn!(error = %e, "每日风险快照刷新失败"); + } + } + } + }); + tracing::info!( module = "ai", "AI 模块事件处理器已注册(监听 ai.* 事件 + Copilot 事件)" diff --git a/crates/erp-ai/src/service/risk_service.rs b/crates/erp-ai/src/service/risk_service.rs index 804f3e9..ca1d913 100644 --- a/crates/erp-ai/src/service/risk_service.rs +++ b/crates/erp-ai/src/service/risk_service.rs @@ -5,7 +5,7 @@ use crate::entity::copilot_risk_snapshots; use crate::entity::copilot_rules; use crate::provider::registry::ProviderRegistry; use erp_core::error::AppResult; -use sea_orm::{ActiveModelTrait, ColumnTrait, EntityTrait, QueryFilter, Set}; +use sea_orm::{ActiveModelTrait, ColumnTrait, EntityTrait, FromQueryResult, QueryFilter, Set}; use std::sync::Arc; use uuid::Uuid; @@ -165,4 +165,36 @@ impl RiskService { let _ = db; Ok(serde_json::json!({})) } + + /// 每日批量刷新所有在管患者的风险快照 + /// 通过 raw SQL 查询患者列表(因为 erp-ai 不依赖 erp-health entity) + pub async fn refresh_all_patients(db: &sea_orm::DatabaseConnection) -> AppResult { + #[derive(sea_orm::FromQueryResult)] + struct PatientRow { + id: Uuid, + tenant_id: Uuid, + } + + let patients: Vec = + PatientRow::find_by_statement(sea_orm::Statement::from_sql_and_values( + sea_orm::DatabaseBackend::Postgres, + "SELECT id, tenant_id FROM patients WHERE deleted_at IS NULL", + [], + )) + .all(db) + .await?; + + let total = patients.len() as u64; + for p in &patients { + if let Err(e) = Self::compute_risk(db, p.tenant_id, p.id).await { + tracing::warn!( + patient_id = %p.id, + tenant_id = %p.tenant_id, + error = %e, + "风险评分刷新失败" + ); + } + } + Ok(total) + } }