use crate::command::context::CommandContext; use crate::command::handler::CommandHandler; use crate::command::response::{CommandError, CommandResponse, MessageKind}; use crate::command::Command; use crate::gateway::cli_session::CliSessionService; use crate::protocol::SessionSummary; use async_trait::async_trait; /// 会话查询命令处理器 /// /// 处理 ListSessions 和 LoadSession 命令 pub struct SessionQueryCommandHandler { cli_sessions: CliSessionService, } impl SessionQueryCommandHandler { /// 创建新的会话查询命令处理器 pub fn new(cli_sessions: CliSessionService) -> Self { Self { cli_sessions } } } #[async_trait] impl CommandHandler for SessionQueryCommandHandler { fn can_handle(&self, cmd: &Command) -> bool { matches!(cmd, Command::ListSessions { .. } | Command::LoadSession { .. }) } async fn handle( &self, cmd: Command, ctx: CommandContext, ) -> Result { match cmd { Command::ListSessions { include_archived } => { handle_list_sessions(self, include_archived, ctx).await } Command::LoadSession { session_id } => { handle_load_session(self, session_id, ctx).await } _ => unreachable!(), } } } /// 处理列出会话命令 async fn handle_list_sessions( handler: &SessionQueryCommandHandler, include_archived: bool, ctx: CommandContext, ) -> Result { let records = handler .cli_sessions .list(include_archived) .map_err(|e| CommandError::new("LIST_SESSIONS_ERROR", e.to_string()))?; let summaries: Vec = records .into_iter() .map(|r| SessionSummary { session_id: r.id, title: r.title, channel_name: r.channel_name, chat_id: r.chat_id, message_count: r.message_count, last_active_at: r.last_active_at, archived_at: r.archived_at, }) .collect(); // 将会话列表序列化为 JSON 存储在 metadata 中 let sessions_json = serde_json::to_string(&summaries).map_err(|e| CommandError::new("SERIALIZE_ERROR", e.to_string()))?; // 构建可读的会话列表消息 let message = if summaries.is_empty() { "No sessions found.".to_string() } else { let mut lines = vec![format!("Found {} session(s):", summaries.len())]; for summary in &summaries { let archived_info = summary .archived_at .map(|_| " [archived]") .unwrap_or(""); lines.push(format!( " - {}: {}{}", summary.session_id, summary.title, archived_info )); } lines.push("".to_string()); lines.push("Use /use to switch to a session".to_string()); lines.join("\n") }; Ok(CommandResponse::success(ctx.request_id) .with_message(MessageKind::Notification, &message) .with_metadata("sessions", &sessions_json) .with_metadata("count", &summaries.len().to_string())) } /// 处理加载会话命令 async fn handle_load_session( handler: &SessionQueryCommandHandler, session_id: String, ctx: CommandContext, ) -> Result { let record = handler .cli_sessions .get(&session_id) .map_err(|e| CommandError::new("LOAD_SESSION_ERROR", e.to_string()))? .ok_or_else(|| CommandError::new("SESSION_NOT_FOUND", format!("Session not found: {}", session_id)))?; Ok(CommandResponse::success(ctx.request_id) .with_message(MessageKind::Notification, &record.title) .with_metadata("session_id", &record.id) .with_metadata("title", &record.title) .with_metadata("message_count", &record.message_count.to_string())) } #[cfg(test)] mod tests { use super::*; use crate::storage::SessionStore; use std::sync::Arc; fn create_test_service() -> CliSessionService { let store = Arc::new(SessionStore::in_memory().unwrap()); CliSessionService::new(store) } #[tokio::test] async fn test_list_sessions_empty() { let service = create_test_service(); let handler = SessionQueryCommandHandler::new(service); let ctx = CommandContext::new("test", "test"); let cmd = Command::ListSessions { include_archived: false, }; let result = handler.handle(cmd, ctx).await; assert!(result.is_ok()); let resp = result.unwrap(); assert!(resp.success); assert!(resp.messages[0].content.contains("No sessions")); } #[tokio::test] async fn test_list_sessions_with_items() { let service = create_test_service(); let handler = SessionQueryCommandHandler::new(service.clone()); // 创建一些会话 service.create(Some("test session")).unwrap(); let ctx = CommandContext::new("test", "test"); let cmd = Command::ListSessions { include_archived: false, }; let result = handler.handle(cmd, ctx).await; assert!(result.is_ok()); let resp = result.unwrap(); assert!(resp.success); assert!(resp.metadata.contains_key("sessions")); } #[tokio::test] async fn test_load_session_not_found() { let service = create_test_service(); let handler = SessionQueryCommandHandler::new(service); let ctx = CommandContext::new("test", "test"); let cmd = Command::LoadSession { session_id: "nonexistent".to_string(), }; let result = handler.handle(cmd, ctx).await; assert!(result.is_err()); } #[tokio::test] async fn test_load_session_success() { let service = create_test_service(); let handler = SessionQueryCommandHandler::new(service.clone()); // 创建会话 let record = service.create(Some("test session")).unwrap(); let ctx = CommandContext::new("test", "test"); let cmd = Command::LoadSession { session_id: record.id.clone(), }; let result = handler.handle(cmd, ctx).await; assert!(result.is_ok()); let resp = result.unwrap(); assert!(resp.success); assert_eq!(resp.metadata.get("session_id").unwrap(), &record.id); } }