Files
hms/crates/erp-plugin/src/notification.rs
iven 6d5a711d2c
Some checks failed
CI / rust-check (push) Has been cancelled
CI / rust-test (push) Has been cancelled
CI / frontend-build (push) Has been cancelled
CI / security-audit (push) Has been cancelled
fix: 修复测试发现的 7 个问题 + 全 workspace clippy 清零
功能修复:
1. 患者创建空名称验证:后端添加 name.trim().is_empty() 检查
2. 仪表盘统计容错:单个查询失败返回零值而非 500
3. FHIR 路由修复:从 /fhir 移到 /api/v1/fhir 保持一致
4. 冻结模块后端中间件:新增 frozen_module_middleware 拦截冻结路径
5. 积分端点权限码:health.health-data.list → health.points.list
6. 角色权限迁移:护士补充 devices.list,运营补充 points.list/manage
7. 测试结果文档:R01-R05 角色测试 + T00/T10 结果归档

Clippy 全 workspace 清零(14→0 errors):
- erp-core: 修复 empty doc line、collapsible if、redundant closure 等 9 处
- erp-health: 修复 too_many_arguments、unused var、unnecessary parens 等 58 处
- erp-ai: 修复 dead_code、unused import 等 11 处
- erp-plugin: 修复 too_many_arguments、wildcard pattern 等 11 处
- erp-server-migration: 修复 enum_variant_names 5 处
- erp-auth/config/workflow/message: 各 1-3 处

工程改进:
- lint-staged 配置迁移到 .lintstagedrc.js(函数式避免文件列表传给 clippy)
- cargo fmt 统一格式化
2026-05-07 23:43:14 +08:00

110 lines
3.3 KiB
Rust

use chrono::Utc;
use sea_orm::{ConnectionTrait, FromQueryResult, Statement};
use uuid::Uuid;
use erp_core::error::AppResult;
use erp_core::events::{DomainEvent, EventBus};
/// 启动插件通知监听器 — 订阅 plugin.trigger.* 事件
pub fn start_notification_listener(db: sea_orm::DatabaseConnection, event_bus: EventBus) {
let (mut rx, _handle) = event_bus.subscribe_filtered("plugin.trigger.".to_string());
tokio::spawn(async move {
while let Some(event) = rx.recv().await {
if let Err(e) = handle_trigger_event(&event, &db).await {
tracing::warn!(
event_type = %event.event_type,
error = %e,
"Failed to handle plugin trigger notification"
);
}
}
tracing::info!("Plugin notification listener stopped");
});
}
async fn handle_trigger_event(
event: &DomainEvent,
db: &sea_orm::DatabaseConnection,
) -> AppResult<()> {
let plugin_id = event
.payload
.get("plugin_id")
.and_then(|v| v.as_str())
.unwrap_or("unknown");
let trigger_name = event
.payload
.get("trigger_name")
.and_then(|v| v.as_str())
.unwrap_or("unknown");
let entity = event
.payload
.get("entity")
.and_then(|v| v.as_str())
.unwrap_or("unknown");
let action = event
.payload
.get("action")
.and_then(|v| v.as_str())
.unwrap_or("unknown");
let title = format!("插件事件: {}.{}", plugin_id, trigger_name);
let body = format!(
"插件 [{}] 的实体 [{}] 触发了 [{}] 事件",
plugin_id, entity, action
);
// 查询所有管理员用户
#[derive(FromQueryResult)]
struct AdminUser {
id: Uuid,
}
let admins = AdminUser::find_by_statement(Statement::from_sql_and_values(
sea_orm::DatabaseBackend::Postgres,
r#"SELECT u.id FROM users u
JOIN user_roles ur ON ur.user_id = u.id
JOIN roles r ON r.id = ur.role_id
WHERE u.tenant_id = $1 AND r.name = 'admin' AND u.deleted_at IS NULL"#,
[event.tenant_id.into()],
))
.all(db)
.await
.map_err(|e| erp_core::error::AppError::Internal(e.to_string()))?;
// 为每个管理员插入消息记录
let now = Utc::now();
for admin in &admins {
let msg_id = Uuid::now_v7();
let sql = r#"
INSERT INTO messages (id, tenant_id, sender_type, recipient_id, recipient_type,
title, body, priority, is_read, created_at, updated_at, version)
VALUES ($1, $2, 'system', $3, 'user', $4, $5, 'normal', false, $6, $7, 1)
"#;
db.execute(Statement::from_sql_and_values(
sea_orm::DatabaseBackend::Postgres,
sql,
[
msg_id.into(),
event.tenant_id.into(),
admin.id.into(),
title.clone().into(),
body.clone().into(),
now.into(),
now.into(),
],
))
.await
.map_err(|e| erp_core::error::AppError::Internal(e.to_string()))?;
}
tracing::info!(
plugin_id = %plugin_id,
trigger = %trigger_name,
admin_count = admins.len(),
"Plugin trigger notification sent"
);
Ok(())
}