sa_token_core/event/
mod.rs

1// Author: 金书记
2//
3//! Event Listener Module | 事件监听模块
4//! 
5//! Provides event listening capabilities for sa-token, supporting monitoring of login, logout, kick-out, and other operations.
6//! 
7//! 提供 sa-token 的事件监听功能,支持监听登录、登出、踢出等操作。
8//! 
9//! ## EventBus Code Flow Logic | EventBus 代码流程逻辑
10//! 
11//! ### Overall Architecture | 整体架构
12//! 
13//! ```text
14//! ┌─────────────────────────────────────────────────────────────┐
15//! │                    SaTokenEventBus                          │
16//! │  ┌────────────────────────────────────────────────────┐    │
17//! │  │  listeners: Arc<RwLock<Vec<Arc<dyn SaTokenListener>>>>  │
18//! │  │  - Stores all registered listeners                 │
19//! │  │    存储所有注册的监听器                             │
20//! │  │  - Uses RwLock for thread safety                   │
21//! │  │    使用 RwLock 保证线程安全                        │
22//! │  │  - Arc wrapping allows multi-thread sharing        │
23//! │  │    Arc 包装允许多线程共享                          │
24//! │  └────────────────────────────────────────────────────┘    │
25//! └─────────────────────────────────────────────────────────────┘
26//! ```
27//! 
28//! ### Core Processes | 核心流程
29//! 
30//! #### 1. Listener Registration Process | 监听器注册流程
31//! 
32//! ```text
33//! ┌──────────┐     ┌──────────────┐     ┌─────────────┐
34//! │User Code │────▶│ register()   │────▶│Acquire Write│
35//! │用户代码  │     │              │     │Lock 写锁获取│
36//! └──────────┘     │ - Receive    │     │             │
37//!                  │   listener   │     │ - Get lock  │
38//!                  │   接收监听器  │     │   获取写锁   │
39//!                  │ - Arc wrap   │     │ - Add to    │
40//!                  │   Arc包装    │     │   list      │
41//!                  └──────────────┘     │   添加到列表 │
42//!                                       │ - Release   │
43//!                                       │   释放写锁   │
44//!                                       └─────────────┘
45//! 
46//! Steps | 步骤:
47//! 1. User creates custom listener instance
48//!    用户创建自定义监听器实例
49//! 2. Wrap listener with Arc::new()
50//!    使用 Arc::new() 包装监听器
51//! 3. Call event_bus.register(listener).await
52//!    调用 event_bus.register(listener).await
53//! 4. EventBus acquires write lock, adds listener to Vec
54//!    EventBus 获取写锁,将监听器添加到 Vec 中
55//! 5. Registration complete, waiting for event triggers
56//!    监听器注册完成,等待事件触发
57//! ```
58//! 
59//! #### 2. Event Publishing Process | 事件发布流程
60//! 
61//! ```text
62//! ┌──────────────┐     ┌──────────────┐     ┌──────────────┐
63//! │SaTokenManager│────▶│ publish()    │────▶│Acquire Read  │
64//! │(login)       │     │              │     │Lock 读锁获取 │
65//! └──────────────┘     │ 1.Create     │     │              │
66//!                      │   event      │     │ 2.Iterate    │
67//!                      │   创建事件    │     │   listeners  │
68//!                      │ 2.Call       │     │   遍历监听器  │
69//!                      │   publish    │     │ 3.Invoke     │
70//!                      │   调用publish │     │   callbacks  │
71//!                      └──────────────┘     │   调用回调    │
72//!                             │             └──────────────┘
73//!                             ▼                     │
74//!                      ┌──────────────┐             ▼
75//!                      │ SaTokenEvent │     ┌──────────────┐
76//!                      │ - event_type │     │ Listener 1   │
77//!                      │ - login_id   │     │ on_login()   │
78//!                      │ - token      │     ├──────────────┤
79//!                      │ - timestamp  │     │ Listener 2   │
80//!                      └──────────────┘     │ on_login()   │
81//!                                          ├──────────────┤
82//!                                          │ Listener N   │
83//!                                          │ on_login()   │
84//!                                          └──────────────┘
85//! 
86//! Steps | 步骤:
87//! 1. After business operation (e.g., login) completes, create corresponding event object
88//!    业务操作(如 login)完成后,创建对应的事件对象
89//! 2. Call event_bus.publish(event).await
90//!    调用 event_bus.publish(event).await
91//! 3. EventBus acquires read lock, accesses listener list
92//!    EventBus 获取读锁,访问监听器列表
93//! 4. Call each listener's corresponding method in registration order
94//!    按注册顺序依次调用每个监听器的对应方法
95//! 5. After all listeners complete, event publishing process ends
96//!    所有监听器执行完成后,事件发布流程结束
97//! ```
98//! 
99//! #### 3. Event Dispatching Process | 事件分发流程
100//! 
101//! ```text
102//! ┌──────────────────┐
103//! │  publish(event)  │
104//! └────────┬─────────┘
105//!          │
106//!          ▼
107//! ┌──────────────────────────────────────┐
108//! │ 1. Get all listeners (read lock)     │
109//! │    获取所有监听器(读锁)              │
110//! └────────┬─────────────────────────────┘
111//!          │
112//!          ▼
113//! ┌──────────────────────────────────────┐
114//! │ 2. Iterate listener list             │
115//! │    遍历监听器列表                     │
116//! └────────┬─────────────────────────────┘
117//!          │
118//!          ├──▶ on_event(event)  ──▶ Generic event handler
119//!          │                         通用事件处理
120//!          │
121//!          └──▶ Dispatch by event type | 根据事件类型分发:
122//!               │
123//!               ├─ Login ──────▶ on_login(...)
124//!               ├─ Logout ─────▶ on_logout(...)
125//!               ├─ KickOut ────▶ on_kick_out(...)
126//!               ├─ RenewTimeout ▶ on_renew_timeout(...)
127//!               ├─ Replaced ───▶ on_replaced(...)
128//!               └─ Banned ─────▶ on_banned(...)
129//! 
130//! Notes | 注意:
131//! - Listeners execute in registration order
132//!   监听器按注册顺序执行
133//! - Each listener executes asynchronously
134//!   每个监听器都是异步执行的
135//! - Errors in listeners don't interrupt event propagation
136//!   监听器中的错误不会中断事件传播
137//! ```
138//! 
139//! ### Thread Safety Guarantees | 线程安全保证
140//! 
141//! ```text
142//! Arc<RwLock<Vec<Arc<dyn SaTokenListener>>>>
143//!  │    │     │    │
144//!  │    │     │    └─ Listener trait object | 监听器 trait 对象
145//!  │    │     └────── Listener collection | 监听器集合
146//!  │    └──────────── Read-write lock protection | 读写锁保护
147//!  └───────────────── Cross-thread sharing | 跨线程共享
148//! 
149//! - Arc: Allows EventBus to be shared across multiple Manager instances
150//!        允许 EventBus 被多个 Manager 实例共享
151//! - RwLock: Allows multiple readers to publish events concurrently, writer has exclusive registration
152//!           允许多个读者同时发布事件,写者独占注册
153//! - Inner Arc: Listeners can be shared across multiple EventBus instances
154//!              监听器可以被多个 EventBus 共享
155//! ```
156//! 
157//! ### Complete Call Chain Example | 完整调用链示例
158//! 
159//! ```text
160//! User Code | 用户代码
161//!   │
162//!   └─▶ StpUtil::login("user_123")
163//!         │
164//!         └─▶ SaTokenManager::login(...)
165//!               │
166//!               ├─ 1. Generate token | 生成 token
167//!               ├─ 2. Save to storage | 保存到存储
168//!               └─ 3. Trigger event | 触发事件
169//!                     │
170//!                     └─▶ event_bus.publish(
171//!                           SaTokenEvent::login("user_123", "token_abc")
172//!                         )
173//!                           │
174//!                           ├─▶ LoggingListener::on_login()
175//!                           │     └─ Log to file | 记录日志
176//!                           │
177//!                           ├─▶ DatabaseListener::on_login()
178//!                           │     └─ Save to database | 保存到数据库
179//!                           │
180//!                           └─▶ StatisticsListener::on_login()
181//!                                 └─ Update statistics | 更新统计信息
182//! 
183//! After all listeners complete, login() returns token
184//! 所有监听器执行完成后,login() 方法返回 token
185//! ```
186//! 
187//! ### Performance Considerations | 性能考虑
188//! 
189//! 1. **Async Execution | 异步执行**: All listener methods are async, but execute sequentially
190//!    所有监听器方法都是异步的,但按顺序执行
191//! 2. **Read-Write Lock | 读写锁**: Multiple events can be published concurrently (read lock), registration requires exclusive access (write lock)
192//!    多个事件可以并发发布(读锁),注册需要独占(写锁)
193//! 3. **Zero-Copy | 零拷贝**: Event objects are passed by reference, avoiding unnecessary cloning
194//!    事件对象通过引用传递,避免不必要的克隆
195//! 4. **Error Isolation | 错误隔离**: Errors in one listener don't affect other listeners
196//!    单个监听器的错误不会影响其他监听器
197//! 
198//! ### Error Handling | 错误处理
199//! 
200//! ```text
201//! ┌────────────────┐
202//! │ Listener 1     │ ─▶ Success ✓ | 成功 ✓
203//! ├────────────────┤
204//! │ Listener 2     │ ─▶ Error ✗ (handled internally, doesn't affect others)
205//! │                │    错误 ✗ (内部处理,不影响后续)
206//! ├────────────────┤
207//! │ Listener 3     │ ─▶ Success ✓ (still executes) | 成功 ✓ (仍然执行)
208//! └────────────────┘
209//! 
210//! Recommendation | 建议:
211//! Listeners should catch all errors internally and handle them appropriately
212//! 监听器内部应捕获所有错误并适当处理
213//! ```
214//! 
215//! ## Usage Example | 使用示例
216//! 
217//! ```rust,ignore
218//! use sa_token_core::event::{SaTokenEvent, SaTokenListener, SaTokenEventBus};
219//! 
220//! // Custom listener | 自定义监听器
221//! struct MyListener;
222//! 
223//! #[async_trait]
224//! impl SaTokenListener for MyListener {
225//!     async fn on_login(&self, login_id: &str, token: &str, login_type: &str) {
226//!         println!("User {} logged in, token: {}", login_id, token);
227//!         // 用户 {} 登录了,token: {}
228//!     }
229//!     
230//!     async fn on_logout(&self, login_id: &str, token: &str, login_type: &str) {
231//!         println!("User {} logged out", login_id);
232//!         // 用户 {} 登出了
233//!     }
234//! }
235//! 
236//! // Register listener | 注册监听器
237//! let event_bus = SaTokenEventBus::new();
238//! event_bus.register(Arc::new(MyListener)).await;
239//! ```
240
241use async_trait::async_trait;
242use std::sync::Arc;
243use std::sync::RwLock;
244use chrono::{DateTime, Utc};
245use serde::{Serialize, Deserialize};
246
247/// 事件类型
248#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
249pub enum SaTokenEventType {
250    /// 登录事件
251    Login,
252    /// 登出事件
253    Logout,
254    /// 踢出下线事件
255    KickOut,
256    /// Token 续期事件
257    RenewTimeout,
258    /// 被顶下线事件(被其他设备登录)
259    Replaced,
260    /// 被封禁事件
261    Banned,
262}
263
264/// 事件数据
265#[derive(Debug, Clone, Serialize, Deserialize)]
266pub struct SaTokenEvent {
267    /// 事件类型
268    pub event_type: SaTokenEventType,
269    /// 登录ID
270    pub login_id: String,
271    /// Token 值
272    pub token: String,
273    /// 登录类型(如 "default", "admin" 等)
274    pub login_type: String,
275    /// 事件发生时间
276    pub timestamp: DateTime<Utc>,
277    /// 额外数据(用于扩展)
278    pub extra: Option<serde_json::Value>,
279}
280
281impl SaTokenEvent {
282    /// 创建登录事件
283    pub fn login(login_id: impl Into<String>, token: impl Into<String>) -> Self {
284        Self {
285            event_type: SaTokenEventType::Login,
286            login_id: login_id.into(),
287            token: token.into(),
288            login_type: "default".to_string(),
289            timestamp: Utc::now(),
290            extra: None,
291        }
292    }
293
294    /// 创建登出事件
295    pub fn logout(login_id: impl Into<String>, token: impl Into<String>) -> Self {
296        Self {
297            event_type: SaTokenEventType::Logout,
298            login_id: login_id.into(),
299            token: token.into(),
300            login_type: "default".to_string(),
301            timestamp: Utc::now(),
302            extra: None,
303        }
304    }
305
306    /// 创建踢出下线事件
307    pub fn kick_out(login_id: impl Into<String>, token: impl Into<String>) -> Self {
308        Self {
309            event_type: SaTokenEventType::KickOut,
310            login_id: login_id.into(),
311            token: token.into(),
312            login_type: "default".to_string(),
313            timestamp: Utc::now(),
314            extra: None,
315        }
316    }
317
318    /// 创建 Token 续期事件
319    pub fn renew_timeout(login_id: impl Into<String>, token: impl Into<String>) -> Self {
320        Self {
321            event_type: SaTokenEventType::RenewTimeout,
322            login_id: login_id.into(),
323            token: token.into(),
324            login_type: "default".to_string(),
325            timestamp: Utc::now(),
326            extra: None,
327        }
328    }
329
330    /// 创建被顶下线事件
331    pub fn replaced(login_id: impl Into<String>, token: impl Into<String>) -> Self {
332        Self {
333            event_type: SaTokenEventType::Replaced,
334            login_id: login_id.into(),
335            token: token.into(),
336            login_type: "default".to_string(),
337            timestamp: Utc::now(),
338            extra: None,
339        }
340    }
341
342    /// 创建被封禁事件
343    pub fn banned(login_id: impl Into<String>) -> Self {
344        Self {
345            event_type: SaTokenEventType::Banned,
346            login_id: login_id.into(),
347            token: String::new(),
348            login_type: "default".to_string(),
349            timestamp: Utc::now(),
350            extra: None,
351        }
352    }
353
354    /// 设置登录类型
355    pub fn with_login_type(mut self, login_type: impl Into<String>) -> Self {
356        self.login_type = login_type.into();
357        self
358    }
359
360    /// 设置额外数据
361    pub fn with_extra(mut self, extra: serde_json::Value) -> Self {
362        self.extra = Some(extra);
363        self
364    }
365}
366
367/// 事件监听器 trait | Event Listener Trait
368/// 
369/// 实现此 trait 来自定义事件处理逻辑
370/// Implement this trait to customize event handling logic
371/// 
372/// # 使用示例 | Usage Example
373/// 
374/// ```rust,ignore
375/// use async_trait::async_trait;
376/// use sa_token_core::SaTokenListener;
377/// 
378/// struct MyListener;
379/// 
380/// #[async_trait]
381/// impl SaTokenListener for MyListener {
382///     async fn on_login(&self, login_id: &str, token: &str, login_type: &str) {
383///         // 自定义登录处理 | Custom login handling
384///         println!("User {} logged in", login_id);
385///     }
386/// }
387/// ```
388#[async_trait]
389pub trait SaTokenListener: Send + Sync {
390    /// 登录事件 | Login Event
391    /// 
392    /// 当用户成功登录时触发 | Triggered when user successfully logs in
393    /// 
394    /// # 参数 | Parameters
395    /// - `login_id`: 登录 ID | Login ID
396    /// - `token`: Token 值 | Token value
397    /// - `login_type`: 登录类型(如 "web", "websocket")| Login type (e.g., "web", "websocket")
398    async fn on_login(&self, login_id: &str, token: &str, login_type: &str) {
399        let _ = (login_id, token, login_type);
400    }
401
402    /// 登出事件 | Logout Event
403    /// 
404    /// 当用户主动登出时触发 | Triggered when user actively logs out
405    /// 
406    /// # 参数 | Parameters
407    /// - `login_id`: 登录 ID | Login ID
408    /// - `token`: Token 值 | Token value
409    /// - `login_type`: 登录类型 | Login type
410    async fn on_logout(&self, login_id: &str, token: &str, login_type: &str) {
411        let _ = (login_id, token, login_type);
412    }
413
414    /// 踢出下线事件 | Kick Out Event
415    /// 
416    /// 当用户被强制踢出下线时触发 | Triggered when user is forcefully kicked out
417    /// 
418    /// # 参数 | Parameters
419    /// - `login_id`: 登录 ID | Login ID
420    /// - `token`: Token 值 | Token value
421    /// - `login_type`: 登录类型 | Login type
422    async fn on_kick_out(&self, login_id: &str, token: &str, login_type: &str) {
423        let _ = (login_id, token, login_type);
424    }
425
426    /// Token 续期事件 | Token Renewal Event
427    /// 
428    /// 当 Token 有效期被延长时触发 | Triggered when token validity is extended
429    /// 
430    /// # 参数 | Parameters
431    /// - `login_id`: 登录 ID | Login ID
432    /// - `token`: Token 值 | Token value
433    /// - `login_type`: 登录类型 | Login type
434    async fn on_renew_timeout(&self, login_id: &str, token: &str, login_type: &str) {
435        let _ = (login_id, token, login_type);
436    }
437
438    /// 被顶下线事件 | Replaced Event
439    /// 
440    /// 当用户在其他设备登录导致当前设备被顶下线时触发
441    /// Triggered when user logs in on another device and current device is replaced
442    /// 
443    /// # 参数 | Parameters
444    /// - `login_id`: 登录 ID | Login ID
445    /// - `token`: Token 值 | Token value
446    /// - `login_type`: 登录类型 | Login type
447    async fn on_replaced(&self, login_id: &str, token: &str, login_type: &str) {
448        let _ = (login_id, token, login_type);
449    }
450
451    /// 被封禁事件 | Banned Event
452    /// 
453    /// 当用户账号被封禁时触发 | Triggered when user account is banned
454    /// 
455    /// # 参数 | Parameters
456    /// - `login_id`: 登录 ID | Login ID
457    /// - `login_type`: 登录类型 | Login type
458    async fn on_banned(&self, login_id: &str, login_type: &str) {
459        let _ = (login_id, login_type);
460    }
461
462    /// 通用事件处理(所有事件都会触发此方法)
463    /// Generic Event Handler (triggered by all events)
464    /// 
465    /// # 参数 | Parameters
466    /// - `event`: 事件对象 | Event object
467    async fn on_event(&self, event: &SaTokenEvent) {
468        let _ = event;
469    }
470}
471
472/// 事件总线 - 管理所有监听器并分发事件
473#[derive(Clone)]
474pub struct SaTokenEventBus {
475    listeners: Arc<RwLock<Vec<Arc<dyn SaTokenListener>>>>,
476}
477
478impl SaTokenEventBus {
479    /// 创建新的事件总线
480    pub fn new() -> Self {
481        Self {
482            listeners: Arc::new(RwLock::new(Vec::new())),
483        }
484    }
485
486    /// 注册监听器
487    /// Register a listener
488    pub fn register(&self, listener: Arc<dyn SaTokenListener>) {
489        let mut listeners = self.listeners.write().unwrap();
490        listeners.push(listener);
491    }
492    
493    /// 异步注册监听器(为了保持 API 兼容性)
494    /// Register a listener asynchronously (for API compatibility)
495    pub async fn register_async(&self, listener: Arc<dyn SaTokenListener>) {
496        self.register(listener);
497    }
498
499    /// 移除所有监听器
500    /// Clear all listeners
501    pub fn clear(&self) {
502        let mut listeners = self.listeners.write().unwrap();
503        listeners.clear();
504    }
505
506    /// 获取监听器数量
507    /// Get listener count
508    pub fn listener_count(&self) -> usize {
509        let listeners = self.listeners.read().unwrap();
510        listeners.len()
511    }
512
513    /// 发布事件
514    /// Publish an event to all listeners
515    pub async fn publish(&self, event: SaTokenEvent) {
516        // 克隆监听器列表以避免持有锁时异步等待
517        // Clone listener list to avoid holding lock during async operations
518        let listeners = {
519            let guard = self.listeners.read().unwrap();
520            guard.clone()
521        };
522        
523        for listener in listeners.iter() {
524            // 触发通用事件处理
525            listener.on_event(&event).await;
526            
527            // 根据事件类型触发特定处理
528            match event.event_type {
529                SaTokenEventType::Login => {
530                    listener.on_login(&event.login_id, &event.token, &event.login_type).await;
531                }
532                SaTokenEventType::Logout => {
533                    listener.on_logout(&event.login_id, &event.token, &event.login_type).await;
534                }
535                SaTokenEventType::KickOut => {
536                    listener.on_kick_out(&event.login_id, &event.token, &event.login_type).await;
537                }
538                SaTokenEventType::RenewTimeout => {
539                    listener.on_renew_timeout(&event.login_id, &event.token, &event.login_type).await;
540                }
541                SaTokenEventType::Replaced => {
542                    listener.on_replaced(&event.login_id, &event.token, &event.login_type).await;
543                }
544                SaTokenEventType::Banned => {
545                    listener.on_banned(&event.login_id, &event.login_type).await;
546                }
547            }
548        }
549    }
550}
551
552impl Default for SaTokenEventBus {
553    fn default() -> Self {
554        Self::new()
555    }
556}
557
558/// 简单的日志监听器示例
559pub struct LoggingListener;
560
561#[async_trait]
562impl SaTokenListener for LoggingListener {
563    async fn on_login(&self, login_id: &str, token: &str, login_type: &str) {
564        tracing::info!(
565            login_id = %login_id,
566            token = %token,
567            login_type = %login_type,
568            "用户登录"
569        );
570    }
571
572    async fn on_logout(&self, login_id: &str, token: &str, login_type: &str) {
573        tracing::info!(
574            login_id = %login_id,
575            token = %token,
576            login_type = %login_type,
577            "用户登出"
578        );
579    }
580
581    async fn on_kick_out(&self, login_id: &str, token: &str, login_type: &str) {
582        tracing::warn!(
583            login_id = %login_id,
584            token = %token,
585            login_type = %login_type,
586            "用户被踢出下线"
587        );
588    }
589
590    async fn on_renew_timeout(&self, login_id: &str, token: &str, login_type: &str) {
591        tracing::debug!(
592            login_id = %login_id,
593            token = %token,
594            login_type = %login_type,
595            "Token 续期"
596        );
597    }
598
599    async fn on_replaced(&self, login_id: &str, token: &str, login_type: &str) {
600        tracing::warn!(
601            login_id = %login_id,
602            token = %token,
603            login_type = %login_type,
604            "用户被顶下线"
605        );
606    }
607
608    async fn on_banned(&self, login_id: &str, login_type: &str) {
609        tracing::warn!(
610            login_id = %login_id,
611            login_type = %login_type,
612            "用户被封禁"
613        );
614    }
615}
616
617#[cfg(test)]
618mod tests {
619    use super::*;
620
621    struct TestListener {
622        login_count: Arc<RwLock<i32>>,
623    }
624
625    impl TestListener {
626        fn new() -> Self {
627            Self {
628                login_count: Arc::new(RwLock::new(0)),
629            }
630        }
631    }
632
633    #[async_trait]
634    impl SaTokenListener for TestListener {
635        async fn on_login(&self, _login_id: &str, _token: &str, _login_type: &str) {
636            let mut count = self.login_count.write().await;
637            *count += 1;
638        }
639    }
640
641    #[tokio::test]
642    async fn test_event_bus() {
643        let bus = SaTokenEventBus::new();
644        let listener = Arc::new(TestListener::new());
645        let login_count = Arc::clone(&listener.login_count);
646        
647        bus.register(listener).await;
648        
649        // 发布登录事件
650        let event = SaTokenEvent::login("user_123", "token_abc");
651        bus.publish(event).await;
652        
653        // 验证监听器被调用
654        let count = login_count.read().await;
655        assert_eq!(*count, 1);
656    }
657
658    #[test]
659    fn test_event_creation() {
660        let event = SaTokenEvent::login("user_123", "token_abc");
661        assert_eq!(event.event_type, SaTokenEventType::Login);
662        assert_eq!(event.login_id, "user_123");
663        assert_eq!(event.token, "token_abc");
664    }
665}
666