You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

270 lines
7.6 KiB
Rust

//! 聊天相关命令
use tauri::{AppHandle, Emitter};
use crate::database::Database;
use crate::models::{ChatMessage, event_names};
use crate::state::AppState;
use crate::utils::{CommandError, CommandResult, HEARTBEAT_INTERVAL_MS};
use crate::websocket::{HeartbeatManager, WsClient, WsConnectionEvent, WsServer};
/// 启动 WebSocket 服务端
#[tauri::command]
pub async fn start_websocket_server(app: AppHandle) -> CommandResult<()> {
let state = AppState::get();
let local_device = state.local_device.read().clone();
// 创建并启动 WS 服务端
let mut server = WsServer::new(
local_device.ws_port,
local_device.device_id.clone(),
state.connection_manager.clone(),
);
server.start().await.map_err(|e| CommandError {
code: "WS_SERVER_ERROR".to_string(),
message: e.to_string(),
})?;
*state.ws_server.write() = Some(server);
// 创建 WS 客户端
let client = WsClient::new(
local_device.device_id.clone(),
state.connection_manager.clone(),
);
*state.ws_client.write() = Some(client);
// 启动心跳管理
HeartbeatManager::start(
state.connection_manager.clone(),
local_device.device_id.clone(),
HEARTBEAT_INTERVAL_MS,
);
// 监听连接事件
let mut rx = state.connection_manager.subscribe();
let app_handle = app.clone();
tokio::spawn(async move {
while let Ok(event) = rx.recv().await {
match event {
WsConnectionEvent::Connected { device_id } => {
let _ = app_handle.emit(event_names::WEBSOCKET_CONNECTED, serde_json::json!({
"deviceId": device_id
}));
}
WsConnectionEvent::Disconnected { device_id, reason } => {
let _ = app_handle.emit(event_names::WEBSOCKET_DISCONNECTED, serde_json::json!({
"deviceId": device_id,
"reason": reason
}));
}
WsConnectionEvent::MessageReceived { message } => {
let _ = app_handle.emit(event_names::MESSAGE_RECEIVED, &message);
}
}
}
});
log::info!("WebSocket server started on port {}", local_device.ws_port);
Ok(())
}
/// 停止 WebSocket 服务端
#[tauri::command]
pub async fn stop_websocket_server() -> CommandResult<()> {
let state = AppState::get();
// 关闭所有连接
state.connection_manager.close_all().await;
// 停止服务端
if let Some(mut server) = state.ws_server.write().take() {
server.stop();
}
*state.ws_client.write() = None;
log::info!("WebSocket server stopped");
Ok(())
}
/// 连接到指定设备
#[tauri::command]
pub async fn connect_to_peer(device_id: String) -> CommandResult<()> {
let state = AppState::get();
// 获取设备信息
let device = state
.device_manager
.get_device(&device_id)
.ok_or_else(|| CommandError {
code: "DEVICE_NOT_FOUND".to_string(),
message: format!("Device {} not found", device_id),
})?;
// 检查是否已连接 (同步检查,立即释放锁)
{
let client_guard = state.ws_client.read();
if let Some(client) = client_guard.as_ref() {
if client.is_connected(&device_id) || state.connection_manager.is_connected(&device_id) {
return Ok(());
}
} else if state.connection_manager.is_connected(&device_id) {
return Ok(());
}
}
// 克隆客户端引用以便异步使用
let ws_client = {
let client_guard = state.ws_client.read();
client_guard.as_ref().cloned()
};
let client = ws_client.ok_or_else(|| CommandError {
code: "WS_NOT_STARTED".to_string(),
message: "WebSocket client not started".to_string(),
})?;
client.connect(&device).await.map_err(|e| CommandError {
code: "CONNECTION_ERROR".to_string(),
message: e.to_string(),
})?;
// 更新设备在线状态
state.device_manager.set_device_online(&device_id, true);
log::info!("Connected to device {}", device_id);
Ok(())
}
/// 断开与指定设备的连接
#[tauri::command]
pub async fn disconnect_from_peer(device_id: String) -> CommandResult<()> {
let state = AppState::get();
// 克隆客户端引用
let ws_client = {
let client_guard = state.ws_client.read();
client_guard.as_ref().cloned()
};
if let Some(client) = ws_client {
client.disconnect(&device_id).await;
}
state.device_manager.set_device_online(&device_id, false);
log::info!("Disconnected from device {}", device_id);
Ok(())
}
/// 发送聊天消息
#[tauri::command]
pub async fn send_chat_message(
device_id: String,
content: String,
message_type: Option<String>,
) -> CommandResult<ChatMessage> {
let state = AppState::get();
// 获取本地设备 ID (立即释放锁)
let local_device_id = {
let local_device = state.local_device.read();
local_device.device_id.clone()
};
// 创建消息
let message = match message_type.as_deref() {
Some("image") => ChatMessage::new_image(
local_device_id,
device_id.clone(),
content,
),
_ => ChatMessage::new_text(
local_device_id,
device_id.clone(),
content,
),
};
// 保存到数据库
Database::save_message(&message).map_err(|e| CommandError {
code: "DB_ERROR".to_string(),
message: e.to_string(),
})?;
// 获取客户端 (立即释放锁)
let ws_client = {
let client_guard = state.ws_client.read();
client_guard.as_ref().cloned()
};
let mut sent = false;
// 尝试通过客户端发送(主动连接的情况)
if let Some(client) = &ws_client {
if client.is_connected(&device_id) {
client.send_to(&device_id, &message).await.map_err(|e| CommandError {
code: "SEND_ERROR".to_string(),
message: e,
})?;
sent = true;
}
}
// 如果客户端没有发送,尝试通过服务端连接发送(被动连接的情况)
if !sent {
state
.connection_manager
.send_to(&device_id, &message)
.await
.map_err(|e| CommandError {
code: "SEND_ERROR".to_string(),
message: e,
})?;
}
Ok(message)
}
/// 获取聊天历史
#[tauri::command]
pub async fn get_chat_history(
device_id: String,
limit: Option<usize>,
offset: Option<usize>,
) -> CommandResult<Vec<ChatMessage>> {
let state = AppState::get();
let local_device = state.local_device.read();
let messages = Database::get_chat_history(
&device_id,
&local_device.device_id,
limit.unwrap_or(50),
offset.unwrap_or(0),
)
.map_err(|e| CommandError {
code: "DB_ERROR".to_string(),
message: e.to_string(),
})?;
Ok(messages)
}
/// 删除聊天历史
#[tauri::command]
pub async fn delete_chat_history(device_id: String) -> CommandResult<usize> {
let state = AppState::get();
let local_device = state.local_device.read();
let count = Database::delete_chat_history(&device_id, &local_device.device_id)
.map_err(|e| CommandError {
code: "DB_ERROR".to_string(),
message: e.to_string(),
})?;
Ok(count)
}