203 lines
6.4 KiB
Rust
203 lines
6.4 KiB
Rust
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<CommandResponse, CommandError> {
|
|
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<CommandResponse, CommandError> {
|
|
let records = handler
|
|
.cli_sessions
|
|
.list(include_archived)
|
|
.map_err(|e| CommandError::new("LIST_SESSIONS_ERROR", e.to_string()))?;
|
|
|
|
let summaries: Vec<SessionSummary> = 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 <session_id> 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<CommandResponse, CommandError> {
|
|
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);
|
|
}
|
|
}
|