feat(ai): 每日风险快照批量刷新定时任务
- risk_service 新增 refresh_all_patients 方法 - module on_startup 启动每日刷新后台任务
This commit is contained in:
@@ -312,6 +312,25 @@ impl ErpModule for AiModule {
|
|||||||
let copilot_handles = crate::event::copilot_consumer::spawn(&ctx.db, &ctx.event_bus);
|
let copilot_handles = crate::event::copilot_consumer::spawn(&ctx.db, &ctx.event_bus);
|
||||||
std::mem::forget(copilot_handles);
|
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!(
|
tracing::info!(
|
||||||
module = "ai",
|
module = "ai",
|
||||||
"AI 模块事件处理器已注册(监听 ai.* 事件 + Copilot 事件)"
|
"AI 模块事件处理器已注册(监听 ai.* 事件 + Copilot 事件)"
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ use crate::entity::copilot_risk_snapshots;
|
|||||||
use crate::entity::copilot_rules;
|
use crate::entity::copilot_rules;
|
||||||
use crate::provider::registry::ProviderRegistry;
|
use crate::provider::registry::ProviderRegistry;
|
||||||
use erp_core::error::AppResult;
|
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 std::sync::Arc;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
@@ -165,4 +165,36 @@ impl RiskService {
|
|||||||
let _ = db;
|
let _ = db;
|
||||||
Ok(serde_json::json!({}))
|
Ok(serde_json::json!({}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 每日批量刷新所有在管患者的风险快照
|
||||||
|
/// 通过 raw SQL 查询患者列表(因为 erp-ai 不依赖 erp-health entity)
|
||||||
|
pub async fn refresh_all_patients(db: &sea_orm::DatabaseConnection) -> AppResult<u64> {
|
||||||
|
#[derive(sea_orm::FromQueryResult)]
|
||||||
|
struct PatientRow {
|
||||||
|
id: Uuid,
|
||||||
|
tenant_id: Uuid,
|
||||||
|
}
|
||||||
|
|
||||||
|
let patients: Vec<PatientRow> =
|
||||||
|
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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user