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
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)
|
|
}
|