154 lines
5.0 KiB
Rust
154 lines
5.0 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::session::SessionManager;
|
||
use crate::storage::SessionStore;
|
||
use async_trait::async_trait;
|
||
use std::sync::Arc;
|
||
|
||
/// 会话命令处理器
|
||
///
|
||
/// 处理与会话管理相关的命令
|
||
pub struct SessionCommandHandler {
|
||
store: Arc<SessionStore>,
|
||
session_manager: Option<SessionManager>,
|
||
}
|
||
|
||
impl SessionCommandHandler {
|
||
/// 创建新的会话命令处理器
|
||
///
|
||
/// # Arguments
|
||
/// * `store` - Session 存储
|
||
pub(crate) fn new(store: Arc<SessionStore>) -> 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<CommandResponse, CommandError> {
|
||
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<String>,
|
||
ctx: CommandContext,
|
||
) -> Result<CommandResponse, CommandError> {
|
||
// 获取当前 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 }));
|
||
}
|
||
}
|