mf_collab/
sync_service.rs

1use std::sync::Arc;
2use yrs::{Map, ReadTxn as _, Transact, WriteTxn as _};
3use serde::{Deserialize, Serialize};
4
5use crate::{mapping::Mapper};
6use crate::error::Result;
7use crate::yrs_manager::YrsManager;
8use mf_state::Transaction;
9use mf_model::tree::Tree;
10use crate::RoomSnapshot;
11
12/// 房间状态枚举
13#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
14pub enum RoomStatus {
15    /// 房间不存在
16    NotExists,
17    /// 房间已创建但未初始化数据
18    Created,
19    /// 房间已初始化并有数据
20    Initialized,
21    /// 房间正在下线中
22    Shutting,
23    /// 房间已下线
24    Offline,
25}
26
27/// 房间信息结构
28#[derive(Debug, Clone, Serialize, Deserialize)]
29pub struct RoomInfo {
30    pub room_id: String,
31    pub status: RoomStatus,
32    pub node_count: usize,
33    pub client_count: usize,
34    pub last_activity: std::time::SystemTime,
35}
36
37#[derive(Clone)]
38pub struct SyncService {
39    yrs_manager: Arc<YrsManager>,
40    client_id: u64,
41}
42
43impl SyncService {
44    pub fn new(yrs_manager: Arc<YrsManager>) -> Self {
45        Self { yrs_manager, client_id: fastrand::u64(..) }
46    }
47
48    /// 初始化房间,确保 Yrs 文档存在
49    pub fn init_room(
50        &self,
51        room_id: &str,
52    ) {
53        tracing::info!("🔄 初始化房间: {}", room_id);
54        self.yrs_manager.get_or_create_awareness(room_id);
55    }
56
57    /// 使用现有的 Tree 初始化房间,同步所有节点数据到 Yrs 文档
58    /// 这是在房间首次创建或需要重新同步时调用的关键方法
59    pub async fn init_room_with_tree(
60        &self,
61        room_id: &str,
62        tree: &Tree,
63    ) -> Result<()> {
64        tracing::info!("🔄 初始化房间: {} 使用现有的树数据", room_id);
65
66        // 获取或创建 awareness
67        let awareness_ref = self.yrs_manager.get_or_create_awareness(room_id);
68        let mut awareness = awareness_ref.write().await;
69        let doc = awareness.doc_mut();
70        let mut txn = doc.transact_mut_with(yrs::Origin::from(self.client_id));
71
72        // 清空现有数据(如果有的话)
73        let nodes_map = txn.get_or_insert_map("nodes");
74        nodes_map.clear(&mut txn);
75
76        // 同步 Tree 中的所有节点到 Yrs 文档
77        self.sync_tree_to_yrs(tree, &mut txn)?;
78
79        // 提交事务
80        txn.commit();
81
82        tracing::info!(
83            "Successfully initialized room {} with tree containing {} nodes",
84            room_id,
85            tree.nodes.len()
86        );
87        Ok(())
88    }
89
90    /// 将 Tree 中的所有节点同步到 Yrs 事务中
91    fn sync_tree_to_yrs(
92        &self,
93        tree: &Tree,
94        txn: &mut yrs::TransactionMut,
95    ) -> Result<()> {
96        use mf_transform::{step::Step, node_step::AddNodeStep};
97
98        let registry = Mapper::global_registry();
99
100        // 获取根节点的所有子树
101        if let Some(root_tree) = tree.all_children(&tree.root_id, None) {
102            // 创建一个 AddNodeStep 来添加整个子树
103            let add_step = AddNodeStep {
104                parent_id: tree.root_id.clone(),
105                nodes: vec![root_tree],
106            };
107
108            // 使用现有的转换器应用步骤
109            if let Some(converter) =
110                registry.find_converter(&add_step as &dyn Step)
111            {
112                if let Err(e) =
113                    converter.apply_to_yrs_txn(&add_step as &dyn Step, txn)
114                {
115                    tracing::error!("🔄 同步树节点到 Yrs 失败: {}", e);
116                    return Err(crate::error::TransmissionError::SyncError(
117                        format!("Failed to sync tree: {}", e),
118                    ));
119                }
120            } else {
121                tracing::error!(
122                    "🔄 同步树节点到 Yrs 失败: 没有找到 AddNodeStep 的转换器"
123                );
124                return Err(crate::error::TransmissionError::SyncError(
125                    "No converter found for AddNodeStep".to_string(),
126                ));
127            }
128        }
129
130        Ok(())
131    }
132
133    /// 处理多个业务逻辑事务并批量应用到 Yrs 文档
134    pub async fn handle_transaction_applied(
135        &self,
136        transactions: &[Transaction],
137        room_id: &str,
138    ) -> Result<()> {
139        // 使用异步锁获取房间信息
140        if let Some(awareness_ref) = self.yrs_manager.get_awareness_ref(room_id)
141        {
142            let mut awareness = awareness_ref.write().await;
143            let doc = awareness.doc_mut();
144            let mut txn =
145                doc.transact_mut_with(yrs::Origin::from(self.client_id));
146
147            // 使用全局注册表应用所有事务中的步骤
148            let registry = Mapper::global_registry();
149
150            for tr in transactions {
151                let steps = &tr.steps;
152                for step in steps {
153                    if let Some(converter) =
154                        registry.find_converter(step.as_ref())
155                    {
156                        if let Err(e) =
157                            converter.apply_to_yrs_txn(step.as_ref(), &mut txn)
158                        {
159                            tracing::error!(
160                                "🔄 应用步骤到 Yrs 事务失败: {}",
161                                e
162                            );
163                        }
164                    } else {
165                        let type_name =
166                            std::any::type_name_of_val(step.as_ref());
167                        tracing::warn!(
168                            "🔄 应用步骤到 Yrs 事务失败: 没有找到步骤的转换器: {}",
169                            type_name
170                        );
171                    }
172                }
173            }
174
175            // 统一提交所有更改
176            txn.commit();
177            tracing::debug!(
178                "🔄 应用 {} 个事务到房间: {}",
179                transactions.len(),
180                room_id
181            );
182        }
183
184        Ok(())
185    }
186
187    /// 获取房间的完整快照(用于新客户端初始化)
188    pub fn get_room_snapshot(
189        &self,
190        room_id: &str,
191        tree: &Tree,
192    ) -> RoomSnapshot {
193        tracing::debug!("🔄 获取房间快照: {}", room_id);
194        Mapper::tree_to_snapshot(tree, room_id.to_string())
195    }
196
197    /// 检查房间是否已初始化(有数据)
198    pub async fn is_room_initialized(
199        &self,
200        room_id: &str,
201    ) -> bool {
202        if let Some(awareness_ref) = self.yrs_manager.get_awareness_ref(room_id)
203        {
204            let awareness = awareness_ref.read().await;
205            let doc = awareness.doc();
206            let txn = doc.transact();
207
208            if let Some(nodes_map) = txn.get_map("nodes") {
209                nodes_map.len(&txn) > 0
210            } else {
211                false
212            }
213        } else {
214            false
215        }
216    }
217
218    /// 获取房间状态信息
219    pub async fn get_room_status(
220        &self,
221        room_id: &str,
222    ) -> RoomStatus {
223        if !self.yrs_manager.room_exists(room_id) {
224            return RoomStatus::NotExists;
225        }
226
227        if self.is_room_initialized(room_id).await {
228            RoomStatus::Initialized
229        } else {
230            RoomStatus::Created
231        }
232    }
233
234    /// 获取房间详细信息
235    pub async fn get_room_info(
236        &self,
237        room_id: &str,
238    ) -> Option<RoomInfo> {
239        if !self.yrs_manager.room_exists(room_id) {
240            return None;
241        }
242
243        let status = self.get_room_status(room_id).await;
244        let mut node_count = 0;
245        let mut client_count = 0;
246
247        if let Some(awareness_ref) = self.yrs_manager.get_awareness_ref(room_id)
248        {
249            if let Ok(awareness) = awareness_ref.try_read() {
250                let doc = awareness.doc();
251                let txn = doc.transact();
252
253                // 获取节点数量
254                if let Some(nodes_map) = txn.get_map("nodes") {
255                    node_count = nodes_map.len(&txn);
256                }
257
258                // 获取客户端数量
259                client_count = awareness.clients().len();
260            }
261        }
262
263        Some(RoomInfo {
264            room_id: room_id.to_string(),
265            status,
266            node_count: node_count as usize,
267            client_count,
268            last_activity: std::time::SystemTime::now(),
269        })
270    }
271
272    /// 房间下线 - 核心下线方法
273    /// 1. 断开所有客户端
274    /// 2. 可选保存数据
275    /// 3. 清理资源
276    pub async fn offline_room(
277        &self,
278        room_id: &str,
279        save_data: bool,
280    ) -> Result<Option<RoomSnapshot>> {
281        tracing::info!("🔄 开始下线房间: {}", room_id);
282
283        let mut final_snapshot = None;
284
285        // 1. 检查房间是否存在
286        if !self.yrs_manager.room_exists(room_id) {
287            tracing::warn!("🔄 尝试下线不存在的房间: {}", room_id);
288            return Ok(None);
289        }
290
291        // 2. 如果需要保存数据,先创建快照
292        if save_data {
293            if let Some(awareness_ref) =
294                self.yrs_manager.get_awareness_ref(room_id)
295            {
296                let awareness = awareness_ref.read().await;
297                let doc = awareness.doc();
298                let txn = doc.transact();
299
300                // 从 Yrs 文档重建 Tree 快照
301                if let Some(nodes_map) = txn.get_map("nodes") {
302                    let node_count = nodes_map.len(&txn);
303                    tracing::info!(
304                        "🔄 保存 {} 个节点 from room: {}",
305                        node_count,
306                        room_id
307                    );
308
309                    // 创建简化的快照(实际项目中可能需要完整的 Tree 重建)
310                    final_snapshot = Some(RoomSnapshot {
311                        room_id: room_id.to_string(),
312                        root_id: "root".to_string(), // 简化处理
313                        nodes: std::collections::HashMap::new(),
314                        version: 0,
315                    });
316                }
317            }
318        }
319
320        // 3. 从 YrsManager 中移除房间(这会自动断开客户端)
321        if let Some(_awareness_ref) =
322            self.yrs_manager.remove_room(room_id).await
323        {
324            tracing::info!("🔄 房间 '{}' 成功下线", room_id);
325        } else {
326            tracing::error!("🔄 从 YrsManager 中移除房间 '{}' 失败", room_id);
327            return Err(crate::error::TransmissionError::SyncError(format!(
328                "Failed to offline room: {}",
329                room_id
330            )));
331        }
332
333        Ok(final_snapshot)
334    }
335
336    /// 强制房间下线(用于紧急情况)
337    pub async fn force_offline_room(
338        &self,
339        room_id: &str,
340    ) -> Result<bool> {
341        tracing::warn!("Force offlining room: {}", room_id);
342
343        let success = self.yrs_manager.force_cleanup_room(room_id).await;
344
345        if success {
346            tracing::info!("Room '{}' force offlined successfully", room_id);
347        } else {
348            tracing::error!("Failed to force offline room: {}", room_id);
349        }
350
351        Ok(success)
352    }
353
354    /// 批量下线房间
355    pub async fn offline_rooms(
356        &self,
357        room_ids: &[String],
358        save_data: bool,
359    ) -> Result<Vec<(String, Option<RoomSnapshot>)>> {
360        tracing::info!("🔄 批量下线 {} 个房间", room_ids.len());
361
362        let mut results = Vec::new();
363
364        for room_id in room_ids {
365            match self.offline_room(room_id, save_data).await {
366                Ok(snapshot) => {
367                    results.push((room_id.clone(), snapshot));
368                },
369                Err(e) => {
370                    tracing::error!("🔄 下线房间 '{}' 失败: {}", room_id, e);
371                    results.push((room_id.clone(), None));
372                },
373            }
374        }
375
376        tracing::info!(
377            "🔄 批量下线完成: {}/{} 个房间成功下线",
378            results.iter().filter(|(_, snapshot)| snapshot.is_some()).count(),
379            room_ids.len()
380        );
381
382        Ok(results)
383    }
384
385    /// 获取所有活跃房间列表
386    pub fn get_active_rooms(&self) -> Vec<String> {
387        self.yrs_manager.get_active_rooms()
388    }
389
390    /// 获取房间统计信息
391    pub async fn get_rooms_stats(&self) -> Vec<RoomInfo> {
392        let room_ids = self.get_active_rooms();
393        let mut stats = Vec::new();
394
395        for room_id in room_ids {
396            if let Some(info) = self.get_room_info(&room_id).await {
397                stats.push(info);
398            }
399        }
400
401        stats
402    }
403
404    /// 获取 YrsManager 的引用(用于高级操作)
405    pub fn yrs_manager(&self) -> &Arc<YrsManager> {
406        &self.yrs_manager
407    }
408}
409
410impl std::fmt::Debug for SyncService {
411    fn fmt(
412        &self,
413        f: &mut std::fmt::Formatter<'_>,
414    ) -> std::fmt::Result {
415        f.debug_struct("SyncService")
416            .field("client_id", &self.client_id)
417            .finish()
418    }
419}