use crate::command::context::CommandContext; use crate::command::handler::CommandHandler; use crate::command::response::{CommandError, CommandResponse, MessageKind}; use crate::command::Command; use crate::gateway::session::SessionManager; use crate::storage::SessionStore; use async_trait::async_trait; use std::sync::Arc; /// 会话命令处理器 /// /// 处理与会话管理相关的命令 pub struct SessionCommandHandler { store: Arc, session_manager: Option, } impl SessionCommandHandler { /// 创建新的会话命令处理器 /// /// # Arguments /// * `store` - Session 存储 pub(crate) fn new(store: Arc) -> Self { Self { store, session_manager: None, } } /// 设置 SessionManager(用于 CreateSession 命令自动切换话题) pub fn with_session_manager(mut self, session_manager: SessionManager) -> Self { self.session_manager = Some(session_manager); self } } #[async_trait] impl CommandHandler for SessionCommandHandler { fn can_handle(&self, cmd: &Command) -> bool { matches!(cmd, Command::CreateSession { .. }) } async fn handle( &self, cmd: Command, ctx: CommandContext, ) -> Result { match cmd { Command::CreateSession { title } => handle_create_session(self, title, ctx).await, Command::SaveSession { .. } => unreachable!("SaveSession should be handled by SaveSessionCommandHandler"), _ => unreachable!("Other commands should be handled by other handlers"), } } } /// 处理创建会话命令 async fn handle_create_session( handler: &SessionCommandHandler, title: Option, ctx: CommandContext, ) -> Result { // 获取当前 session_id,如果没有则报错 let session_id = ctx.session_id.as_deref() .ok_or_else(|| CommandError::new("NO_SESSION", "No active session. Please ensure a session exists first."))?; // 创建新话题(在同一个 Session 内) let topic_title = title.unwrap_or_else(|| { format!("Topic {}", &uuid::Uuid::new_v4().to_string()[..8]) }); let topic = handler .store .create_topic(session_id, &topic_title, None) .map_err(|e| CommandError::new("CREATE_TOPIC_ERROR", e.to_string()))?; // 获取 chat_id let chat_id = ctx.chat_id.as_deref() .ok_or_else(|| CommandError::new("NO_CHAT_ID", "No chat_id in context"))?; // 如果有 SessionManager,自动切换到新话题 if let Some(ref session_manager) = handler.session_manager { if let Some(session) = session_manager.get(&ctx.channel_name).await { let mut session_guard = session.lock().await; session_guard.switch_topic(chat_id, &topic.id) .map_err(|e| CommandError::new("SWITCH_TOPIC_ERROR", e.to_string()))?; } } Ok(CommandResponse::success(ctx.request_id) .with_message(MessageKind::Notification, &topic.title) .with_metadata("topic_id", &topic.id) .with_metadata("session_id", &topic.session_id) .with_metadata("message_count", &topic.message_count.to_string())) } #[cfg(test)] mod tests { use super::*; use crate::storage::SessionStore; use std::sync::Arc; fn create_test_handler() -> SessionCommandHandler { let store = Arc::new(SessionStore::in_memory().unwrap()); SessionCommandHandler::new(store) } #[tokio::test] async fn test_create_session_with_title() { let handler = create_test_handler(); // 需要先创建一个 session let store = handler.store.clone(); let session = store.create_session("cli", Some("test session")).unwrap(); let ctx = CommandContext::new("test", "cli") .with_session_id(&session.id) .with_chat_id(&session.id); let cmd = Command::CreateSession { title: Some("my topic".to_string()), }; let result = handler.handle(cmd, ctx).await; assert!(result.is_ok()); let resp = result.unwrap(); assert!(resp.success); assert!(resp.metadata.contains_key("topic_id")); } #[tokio::test] async fn test_create_session_without_title() { let handler = create_test_handler(); let store = handler.store.clone(); let session = store.create_session("cli", Some("test session")).unwrap(); let ctx = CommandContext::new("test", "cli") .with_session_id(&session.id) .with_chat_id(&session.id); let cmd = Command::CreateSession { title: None }; let result = handler.handle(cmd, ctx).await; assert!(result.is_ok()); let resp = result.unwrap(); assert!(resp.success); } #[test] fn test_can_handle() { let handler = create_test_handler(); assert!(handler.can_handle(&Command::CreateSession { title: None })); } }