feat: 审计修复 Phase 6-7 — SSE 推送/工作流补全/消息群发/前端收尾
Phase 6 功能补全: - P1-3: 消息 SSE 实时推送端点 + 前端 EventSource 连接 - P1-6: ServiceTask HTTP 调用能力 (reqwest GET/POST) - P1-7: user.deleted 事件处理 — 终止相关流程实例 - P1-8: 任务认领 (claim) 端点 + handler - P1-9: 超时检查器发布 task.timeout 事件 - P1-15: 组织/部门名称唯一性校验 (create + update) - P1-18: 消息群发 fan-out (role/department/all 批量投递) Phase 7 P3-P4 收尾: - PluginAdmin purge 按钮状态修复 - ChangePassword 最小 8 字符 + 新旧密码不同验证 - AuditLogViewer 用户名缓存 + 扩展资源类型 - InstanceMonitor 通过 definition 缓存解析 node_name - NotificationPreferences DND 时间范围校验
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
pub mod message_handler;
|
||||
pub mod sse_handler;
|
||||
pub mod subscription_handler;
|
||||
pub mod template_handler;
|
||||
|
||||
53
crates/erp-message/src/handler/sse_handler.rs
Normal file
53
crates/erp-message/src/handler/sse_handler.rs
Normal file
@@ -0,0 +1,53 @@
|
||||
use std::convert::Infallible;
|
||||
|
||||
use axum::extract::Extension;
|
||||
use axum::response::sse::{Event, KeepAlive, Sse};
|
||||
use futures::stream::Stream;
|
||||
|
||||
use erp_core::error::AppError;
|
||||
use erp_core::types::TenantContext;
|
||||
|
||||
use crate::message_state::MessageState;
|
||||
|
||||
/// SSE 消息推送端点。
|
||||
///
|
||||
/// 客户端连接后监听 `message.sent` 事件,仅推送当前用户的消息。
|
||||
/// 使用 EventBus 的 filtered subscriber 按前缀过滤事件。
|
||||
pub async fn message_stream(
|
||||
axum::extract::State(state): axum::extract::State<MessageState>,
|
||||
Extension(ctx): Extension<TenantContext>,
|
||||
) -> Result<Sse<impl Stream<Item = Result<Event, Infallible>>>, AppError> {
|
||||
let user_id = ctx.user_id;
|
||||
let tenant_id = ctx.tenant_id;
|
||||
let (mut rx, _handle) = state.event_bus.subscribe_filtered("message.sent".to_string());
|
||||
|
||||
let sse_stream = async_stream::stream! {
|
||||
loop {
|
||||
match rx.recv().await {
|
||||
Some(event) => {
|
||||
if event.tenant_id != tenant_id {
|
||||
continue;
|
||||
}
|
||||
let is_recipient = event.payload.get("recipient_id")
|
||||
.and_then(|v: &serde_json::Value| v.as_str())
|
||||
.map(|s| s == user_id.to_string())
|
||||
.unwrap_or(false);
|
||||
if !is_recipient {
|
||||
continue;
|
||||
}
|
||||
|
||||
let data = serde_json::to_string(&event.payload)
|
||||
.unwrap_or_default();
|
||||
yield Ok(Event::default()
|
||||
.event("message")
|
||||
.data(data));
|
||||
}
|
||||
None => {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Ok(Sse::new(sse_stream).keep_alive(KeepAlive::default()))
|
||||
}
|
||||
Reference in New Issue
Block a user