1use std::collections::HashMap;
6use std::sync::Arc;
7use std::time::Duration;
8
9use async_trait::async_trait;
10use chrono::{DateTime, Utc};
11use tokio::sync::RwLock;
12
13use crate::dao::SaTokenDao;
14use crate::error::{SaTokenError, SaTokenResult};
15use crate::keys::LOGIN_TYPE_DEFAULT;
16
17mod push;
18mod store;
19
20pub use push::dispatch_to_pushers;
21pub use store::{DistributedOnlineStore, LocalOnlineStore, OnlineStore, StoredOnlineUser};
22
23#[derive(Debug, Clone)]
26pub struct OnlineUser {
27 pub login_type: String,
30 pub login_id: String,
32 pub token: String,
34 pub device: String,
36 pub connect_time: DateTime<Utc>,
38 pub last_activity: DateTime<Utc>,
40 pub metadata: HashMap<String, String>,
42}
43
44impl OnlineUser {
45 pub fn new(
48 login_id: impl Into<String>,
49 token: impl Into<String>,
50 device: impl Into<String>,
51 ) -> Self {
52 let now = Utc::now();
53 Self {
54 login_type: LOGIN_TYPE_DEFAULT.to_string(),
55 login_id: login_id.into(),
56 token: token.into(),
57 device: device.into(),
58 connect_time: now,
59 last_activity: now,
60 metadata: HashMap::new(),
61 }
62 }
63}
64
65#[derive(Debug, Clone)]
67pub struct PushMessage {
68 pub message_id: String,
70 pub content: String,
72 pub message_type: MessageType,
74 pub timestamp: DateTime<Utc>,
76 pub metadata: HashMap<String, String>,
78}
79
80#[derive(Debug, Clone, PartialEq)]
82pub enum MessageType {
83 Text,
85 Binary,
87 KickOut,
89 Notification,
91 Custom(String),
93}
94
95#[async_trait]
98pub trait MessagePusher: Send + Sync {
99 async fn push(&self, login_id: &str, message: PushMessage) -> Result<(), SaTokenError>;
101}
102
103pub struct OnlineManager {
105 store: Arc<dyn OnlineStore>,
106 pushers: Arc<RwLock<Vec<Arc<dyn MessagePusher>>>>,
107}
108
109impl std::fmt::Debug for OnlineManager {
110 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
111 f.write_str("OnlineManager { .. }")
112 }
113}
114
115impl OnlineManager {
116 pub fn new() -> Self {
119 Self::local()
120 }
121
122 pub fn local() -> Self {
124 Self {
125 store: Arc::new(LocalOnlineStore::new()),
126 pushers: Arc::new(RwLock::new(Vec::new())),
127 }
128 }
129
130 pub fn distributed(dao: Arc<SaTokenDao>) -> Self {
133 Self {
134 store: Arc::new(DistributedOnlineStore::new(
135 dao,
136 Some(Duration::from_secs(86400)),
137 )),
138 pushers: Arc::new(RwLock::new(Vec::new())),
139 }
140 }
141
142 pub fn with_store(store: Arc<dyn OnlineStore>) -> Self {
144 Self {
145 store,
146 pushers: Arc::new(RwLock::new(Vec::new())),
147 }
148 }
149
150 pub async fn register_pusher(&self, pusher: Arc<dyn MessagePusher>) {
152 self.pushers.write().await.push(pusher);
153 }
154
155 pub async fn mark_online(&self, user: OnlineUser) -> SaTokenResult<()> {
157 self.store.mark_online(user).await
158 }
159
160 pub async fn mark_offline(&self, login_id: &str, token: &str) -> SaTokenResult<()> {
163 self.store
164 .mark_offline(LOGIN_TYPE_DEFAULT, login_id, token)
165 .await
166 }
167
168 pub async fn mark_offline_with_type(
170 &self,
171 login_type: &str,
172 login_id: &str,
173 token: &str,
174 ) -> SaTokenResult<()> {
175 self.store.mark_offline(login_type, login_id, token).await
176 }
177
178 pub async fn mark_offline_all(&self, login_id: &str) -> SaTokenResult<()> {
180 self.store
181 .mark_offline_all(LOGIN_TYPE_DEFAULT, login_id)
182 .await
183 }
184
185 pub async fn mark_offline_all_with_type(
187 &self,
188 login_type: &str,
189 login_id: &str,
190 ) -> SaTokenResult<()> {
191 self.store.mark_offline_all(login_type, login_id).await
192 }
193
194 pub async fn is_online(&self, login_id: &str) -> SaTokenResult<bool> {
196 self.store.is_online(LOGIN_TYPE_DEFAULT, login_id).await
197 }
198
199 pub async fn get_online_count(&self) -> SaTokenResult<usize> {
201 self.store.get_online_count().await
202 }
203
204 pub async fn get_online_users(&self) -> SaTokenResult<Vec<String>> {
206 self.store.get_online_users().await
207 }
208
209 pub async fn get_user_sessions(&self, login_id: &str) -> SaTokenResult<Vec<OnlineUser>> {
211 self.store
212 .get_user_sessions(LOGIN_TYPE_DEFAULT, login_id)
213 .await
214 }
215
216 pub async fn update_activity(&self, login_id: &str, token: &str) -> SaTokenResult<()> {
218 self.store
219 .update_activity(LOGIN_TYPE_DEFAULT, login_id, token)
220 .await
221 }
222
223 pub async fn update_activity_with_type(
225 &self,
226 login_type: &str,
227 login_id: &str,
228 token: &str,
229 ) -> SaTokenResult<()> {
230 self.store
231 .update_activity(login_type, login_id, token)
232 .await
233 }
234
235 async fn cloned_pushers(&self) -> Vec<Arc<dyn MessagePusher>> {
236 self.pushers.read().await.clone()
237 }
238
239 pub async fn push_to_user(&self, login_id: &str, content: String) -> SaTokenResult<()> {
241 let message = PushMessage {
242 message_id: uuid::Uuid::new_v4().to_string(),
243 content,
244 message_type: MessageType::Text,
245 timestamp: Utc::now(),
246 metadata: HashMap::new(),
247 };
248 let pushers = self.cloned_pushers().await;
249 dispatch_to_pushers(&pushers, login_id, message).await
250 }
251
252 pub async fn push_to_users(
254 &self,
255 login_ids: Vec<String>,
256 content: String,
257 ) -> SaTokenResult<()> {
258 for login_id in login_ids {
259 self.push_to_user(&login_id, content.clone()).await?;
260 }
261 Ok(())
262 }
263
264 pub async fn broadcast(&self, content: String) -> SaTokenResult<()> {
266 let login_ids = self.get_online_users().await?;
267 self.push_to_users(login_ids, content).await
268 }
269
270 pub async fn push_message_to_user(
272 &self,
273 login_id: &str,
274 message: PushMessage,
275 ) -> SaTokenResult<()> {
276 let pushers = self.cloned_pushers().await;
277 dispatch_to_pushers(&pushers, login_id, message).await
278 }
279
280 pub async fn kick_out_notify(&self, login_id: &str, reason: String) -> SaTokenResult<()> {
282 let message = PushMessage {
283 message_id: uuid::Uuid::new_v4().to_string(),
284 content: reason,
285 message_type: MessageType::KickOut,
286 timestamp: Utc::now(),
287 metadata: HashMap::new(),
288 };
289 self.push_message_to_user(login_id, message).await?;
290 self.mark_offline_all(login_id).await
291 }
292}
293
294impl Default for OnlineManager {
295 fn default() -> Self {
296 Self::new()
297 }
298}
299
300pub struct InMemoryPusher {
303 messages: Arc<RwLock<HashMap<String, Vec<PushMessage>>>>,
304}
305
306impl std::fmt::Debug for InMemoryPusher {
307 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
308 f.write_str("InMemoryPusher { .. }")
309 }
310}
311
312impl InMemoryPusher {
313 pub fn new() -> Self {
315 Self {
316 messages: Arc::new(RwLock::new(HashMap::new())),
317 }
318 }
319
320 pub async fn get_messages(&self, login_id: &str) -> Vec<PushMessage> {
322 self.messages
323 .read()
324 .await
325 .get(login_id)
326 .cloned()
327 .unwrap_or_default()
328 }
329
330 pub async fn clear_messages(&self, login_id: &str) {
333 self.messages.write().await.remove(login_id);
334 }
335}
336
337impl Default for InMemoryPusher {
338 fn default() -> Self {
339 Self::new()
340 }
341}
342
343#[async_trait]
344impl MessagePusher for InMemoryPusher {
345 async fn push(&self, login_id: &str, message: PushMessage) -> Result<(), SaTokenError> {
346 self.messages
347 .write()
348 .await
349 .entry(login_id.to_string())
350 .or_default()
351 .push(message);
352 Ok(())
353 }
354}