use serde::{Deserialize, Serialize}; use std::collections::HashMap; use uuid::Uuid; use redis::AsyncCommands; #[derive(Debug, Clone, Serialize, Deserialize)] pub struct ClientState { pub user_id: Uuid, pub last_seq: HashMap, pub last_seen: chrono::DateTime, } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct MissedMessage { pub room_id: Uuid, pub message_id: Uuid, pub seq: i64, pub content: String, pub sender_id: Option, pub send_at: chrono::DateTime, } #[derive(Clone)] pub struct ReconnectManager { cache: db::cache::AppCache, db: db::database::AppDatabase, } impl ReconnectManager { pub fn new(cache: db::cache::AppCache, db: db::database::AppDatabase) -> Self { Self { cache, db } } pub async fn save_client_state( &self, user_id: Uuid, room_id: Uuid, last_seq: i64, ) -> Result<(), crate::error::AppTransportError> { let key = format!("client:state:{}:{}", user_id, room_id); let value = last_seq.to_string(); let mut conn = self.cache.conn().await .map_err(|_| crate::error::AppTransportError::Internal)?; let _: () = conn.set_ex(&key, &value, 86400) .await .map_err(|_| crate::error::AppTransportError::Internal)?; Ok(()) } pub async fn get_last_seq( &self, user_id: Uuid, room_id: Uuid, ) -> Result, crate::error::AppTransportError> { let key = format!("client:state:{}:{}", user_id, room_id); let mut conn = self.cache.conn().await .map_err(|_| crate::error::AppTransportError::Internal)?; let value: Option = conn.get(&key).await .map_err(|_| crate::error::AppTransportError::Internal)?; Ok(value.and_then(|v| v.parse::().ok())) } pub async fn get_missed_messages( &self, _user_id: Uuid, room_id: Uuid, since_seq: i64, ) -> Result, crate::error::AppTransportError> { use sea_orm::*; use models::rooms::room_message; let messages = room_message::Entity::find() .filter(room_message::Column::Room.eq(room_id)) .filter(room_message::Column::Seq.gt(since_seq)) .order_by_asc(room_message::Column::Seq) .limit(100) .all(&self.db) .await .map_err(|_| crate::error::AppTransportError::Internal)?; let missed: Vec = messages .into_iter() .map(|m| MissedMessage { room_id: m.room, message_id: m.id, seq: m.seq, content: m.content, sender_id: m.sender_id, send_at: m.send_at, }) .collect(); Ok(missed) } pub async fn handle_reconnect( &self, user_id: Uuid, room_states: HashMap, ) -> Result>, crate::error::AppTransportError> { let mut result = HashMap::new(); for (room_id, client_seq) in room_states { let missed = self.get_missed_messages(user_id, room_id, client_seq).await?; if !missed.is_empty() { result.insert(room_id, missed); } } Ok(result) } pub async fn cleanup_expired_states(&self) -> Result<(), crate::error::AppTransportError> { Ok(()) } }