Files
JoyD/Claw/Server/gateway/src/main.rs
zqm cdf64fa31f feat(XCamera): 实现异步抓图功能并优化图像处理
fix(nginx): 调整企业微信回调代理路径

feat(gateway): 添加企业微信消息处理功能

docs: 更新项目规划文档和企业微信配置详情

refactor(XCamera): 重构LED检测和图像处理逻辑

test: 添加ONVIF抓图测试功能
2026-03-31 11:04:43 +08:00

1685 lines
66 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

use actix_web::{web, App, HttpRequest, HttpResponse, HttpServer, Responder, middleware::Logger};
use actix_web::http::Method;
use actix_ws::{Message};
use serde::{Deserialize, Serialize};
use std::env;
use std::sync::Arc;
use std::time::Instant;
use tokio::sync::RwLock;
use base64;
use shared::{TaskRequest, TaskResponse, HealthResponse, utils};
use sha1::{Sha1, Digest};
use futures::StreamExt;
use crate::communication::ConnectionInfo;
/// 获取当前本地时间的格式化字符串
fn get_current_time() -> String {
let now = chrono::Local::now();
now.format("%m-%d %H:%M:%S%.3f").to_string()
}
/// 带时间前缀的打印宏
macro_rules! log {
($($arg:tt)*) => {
println!("[{}] {}", get_current_time(), format!($($arg)*));
};
}
mod communication;
use communication::{ConnectionManager, WebSocketPool, CommunicationConfig, WebSocketClient};
/// 企业微信消息发送结构体
#[derive(Serialize)]
struct WeChatMessage {
touser: String,
msgtype: String,
agentid: i32,
text: WeChatTextContent,
}
#[derive(Serialize)]
struct WeChatTextContent {
content: String,
}
#[derive(Deserialize)]
struct WeChatAccessTokenResponse {
access_token: String,
// expires_in: i32, // 未使用,暂时注释
}
#[derive(Deserialize)]
struct WeChatSendMessageResponse {
errcode: i32,
errmsg: String,
}
/// 获取企业微信访问令牌
async fn get_wechat_access_token() -> Result<String, Box<dyn std::error::Error>> {
let (_token, corp_id, _encoding_aes_key, corp_secret, _debug, _debug_config, _http) = get_wechat_config();
let url = format!("https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid={}&corpsecret={}", corp_id, corp_secret);
let client = reqwest::Client::new();
let response = client.get(&url).send().await?;
let result: WeChatAccessTokenResponse = response.json().await?;
Ok(result.access_token)
}
/// 发送企业微信消息
async fn send_wechat_message(touser: &str, content: &str, debug: bool) -> Result<(), Box<dyn std::error::Error>> {
if debug {
log!("📤 开始发送企业微信消息");
}
// 获取访问令牌
let access_token = get_wechat_access_token().await?;
if debug {
log!(" 获取到访问令牌: {}", access_token);
}
// 构建消息
let message = WeChatMessage {
touser: touser.to_string(),
msgtype: "text".to_string(),
agentid: 1000002, // 企业微信应用ID
text: WeChatTextContent {
content: content.to_string(),
},
};
// 发送消息
let url = format!("https://qyapi.weixin.qq.com/cgi-bin/message/send?access_token={}", access_token);
let client = reqwest::Client::new();
let response = client.post(&url)
.json(&message)
.send()
.await?;
let result: WeChatSendMessageResponse = response.json().await?;
if result.errcode == 0 {
if debug {
log!("✅ 消息发送成功");
}
Ok(())
} else {
if debug {
log!("❌ 消息发送失败: {} - {}", result.errcode, result.errmsg);
}
Err(format!("消息发送失败: {} - {}", result.errcode, result.errmsg).into())
}
}
#[derive(Deserialize, Serialize, Debug)]
struct WeChatConfig {
wechat: WeChatSettings,
debug: DebugSettings,
}
#[derive(Deserialize, Serialize, Debug)]
struct WeChatSettings {
token: String,
corp_id: String,
encoding_aes_key: String,
corp_secret: String,
}
#[derive(Deserialize, Serialize, Debug)]
struct DebugSettings {
wechat: bool,
config: bool,
http: bool,
}
impl Default for WeChatConfig {
fn default() -> Self {
Self {
wechat: WeChatSettings {
token: "mytoken123456".to_string(),
corp_id: "wwa7bb7aec981103b4".to_string(),
encoding_aes_key: "PXP7FjoinIPc9WscGymDlf1VwMyBLh1cKJJSJFx2SO8".to_string(),
corp_secret: "your_corp_secret_here".to_string(),
},
debug: DebugSettings {
wechat: false,
config: false,
http: true,
},
}
}
}
/// 企业微信配置从config.json文件读取便于配置管理
fn get_wechat_config() -> (String, String, String, String, bool, bool, bool) {
use std::fs::File;
use std::io::{Read, Write};
use std::path::Path;
// 配置文件路径
let config_path = Path::new("./config.json");
// 尝试读取配置文件
if config_path.exists() {
// 先创建默认配置用于获取debug.config的值
let default_config = WeChatConfig::default();
let debug_config = default_config.debug.config;
if debug_config {
println!("📁 读取配置文件: {:?}", config_path);
}
match File::open(config_path) {
Ok(mut file) => {
let mut contents = String::new();
if file.read_to_string(&mut contents).is_ok() {
match serde_json::from_str::<WeChatConfig>(&contents) {
Ok(config) => {
if config.debug.config {
println!("✅ 配置文件读取成功");
}
return (config.wechat.token, config.wechat.corp_id, config.wechat.encoding_aes_key, config.wechat.corp_secret, config.debug.wechat, config.debug.config, config.debug.http);
}
Err(e) => {
if debug_config {
println!("❌ 配置文件解析失败: {}, 使用默认配置", e);
}
}
}
} else {
if debug_config {
println!("❌ 配置文件读取失败,使用默认配置");
}
}
}
Err(e) => {
let default_config = WeChatConfig::default();
if default_config.debug.config {
println!("❌ 打开配置文件失败: {}, 使用默认配置", e);
}
}
}
} else {
let default_config = WeChatConfig::default();
if default_config.debug.config {
println!("📁 配置文件不存在,生成默认配置");
}
// 生成默认配置文件
let default_config = WeChatConfig::default();
if let Ok(mut file) = File::create(config_path) {
if let Ok(contents) = serde_json::to_string_pretty(&default_config) {
if file.write_all(contents.as_bytes()).is_ok() {
if default_config.debug.config {
println!("✅ 默认配置文件生成成功: {:?}", config_path);
}
} else {
if default_config.debug.config {
println!("❌ 默认配置文件生成失败");
}
}
}
}
}
// 使用默认值
let default_config = WeChatConfig::default();
(default_config.wechat.token, default_config.wechat.corp_id, default_config.wechat.encoding_aes_key, default_config.wechat.corp_secret, default_config.debug.wechat, default_config.debug.config, default_config.debug.http)
}
/// 解析企业微信XML消息
fn parse_wechat_xml_message(xml_content: &str, debug: bool) -> (Option<String>, Option<String>, Option<String>, Option<String>) {
if debug {
log!("📄 开始解析企业微信XML消息");
}
// 检查是否为加密消息
if xml_content.contains("<Encrypt>") {
if debug {
log!("🔒 发现加密消息,开始解密");
}
// 提取Encrypt标签内容
if let Some(encrypt_content) = extract_xml_tag(xml_content, "Encrypt") {
if debug {
log!(" 提取到加密内容");
}
// 解密消息
match decrypt_wechat_message(&encrypt_content) {
Ok(decrypted_xml) => {
if debug {
log!("✅ 消息解密成功");
log!(" 解密后内容: {}", decrypted_xml);
}
// 从解密后的XML中提取发送者、内容、消息类型和事件类型
let from_user_name = extract_xml_tag(&decrypted_xml, "FromUserName");
let content = extract_xml_tag(&decrypted_xml, "Content");
let msg_type = extract_xml_tag(&decrypted_xml, "MsgType");
let event = extract_xml_tag(&decrypted_xml, "Event");
if debug {
if let Some(from_user) = &from_user_name {
log!(" 发送者: {}", from_user);
}
if let Some(msg_content) = &content {
log!(" 消息内容: {}", msg_content);
}
if let Some(msg_type_val) = &msg_type {
log!(" 消息类型: {}", msg_type_val);
}
if let Some(event_val) = &event {
log!(" 事件类型: {}", event_val);
}
}
return (from_user_name, content, msg_type, event);
}
Err(e) => {
if debug {
log!("❌ 消息解密失败: {}", e);
}
return (None, None, None, None);
}
}
} else {
if debug {
log!("❌ 无法提取Encrypt标签");
}
return (None, None, None, None);
}
} else {
// 非加密消息,直接解析
if debug {
log!("🔓 非加密消息,直接解析");
}
let from_user_name = extract_xml_tag(xml_content, "FromUserName");
let content = extract_xml_tag(xml_content, "Content");
let msg_type = extract_xml_tag(xml_content, "MsgType");
let event = extract_xml_tag(xml_content, "Event");
if debug {
if let Some(from_user) = &from_user_name {
log!(" 发送者: {}", from_user);
}
if let Some(msg_content) = &content {
log!(" 消息内容: {}", msg_content);
}
if let Some(msg_type_val) = &msg_type {
log!(" 消息类型: {}", msg_type_val);
}
if let Some(event_val) = &event {
log!(" 事件类型: {}", event_val);
}
}
return (from_user_name, content, msg_type, event);
}
}
/// 解密企业微信消息
fn decrypt_wechat_message(encrypted: &str) -> Result<String, Box<dyn std::error::Error>> {
// 获取企业微信配置
let (_, _corp_id, encoding_aes_key, _corp_secret, _debug, _debug_config, _http) = get_wechat_config();
// 企业微信官方要求EncodingAESKey 使用URL_SAFE Base64解码且需要补全=号
let mut aes_key_str = encoding_aes_key.to_string();
while aes_key_str.len() % 4 != 0 {
aes_key_str.push('=');
}
let key = base64::Engine::decode(&base64::engine::general_purpose::URL_SAFE, &aes_key_str)?;
if key.len() != 32 {
return Err("Invalid key length".into());
}
// 企业微信官方规定IV 必须是 EncodingAESKey 解码后的前 16 字节
let iv = &key[0..16];
let mut iv_array = [0u8; 16];
iv_array.copy_from_slice(iv);
// 企业微信官方要求加密消息内容Encrypt使用STANDARD Base64解码
let ciphertext = base64::Engine::decode(&base64::engine::general_purpose::STANDARD, &encrypted)?;
// 使用AES-CBC-256解密
use aes::cipher::{KeyIvInit, BlockDecryptMut};
use aes::Aes256;
use cbc::Decryptor;
use cipher::block_padding::NoPadding;
let decryptor = Decryptor::<Aes256>::new_from_slices(&key, &iv_array)?;
let mut buffer = ciphertext.to_vec();
let plaintext = decryptor.decrypt_padded_mut::<NoPadding>(&mut buffer)
.map_err(|e| format!("Decryption error: {:?}", e))?;
// 企业微信官方格式16字节随机串 + 4字节长度(网络序) + 消息内容 + CorpID
if plaintext.len() < 20 {
return Err("Decrypted data too short".into());
}
// 解析4字节长度网络序大端
let msg_len = u32::from_be_bytes([plaintext[16], plaintext[17], plaintext[18], plaintext[19]]);
let msg_start = 20;
let msg_end = msg_start + msg_len as usize;
if msg_end > plaintext.len() {
return Err("Invalid message length".into());
}
// 截取消息内容
let msg = &plaintext[msg_start..msg_end];
let result = String::from_utf8_lossy(msg).to_string();
Ok(result)
}
/// 提取XML标签内容
fn extract_xml_tag(xml: &str, tag: &str) -> Option<String> {
// 尝试匹配带CDATA的格式
let start_tag_cdata = format!("<{}><![CDATA[", tag);
let cdata_end = "]]>";
let end_tag = format!("</{}", tag);
if let Some(start_idx) = xml.find(&start_tag_cdata) {
let start_idx = start_idx + start_tag_cdata.len();
// 先找到CDATA的结束
if let Some(cdata_end_idx) = xml[start_idx..].find(cdata_end) {
let content = &xml[start_idx..start_idx + cdata_end_idx];
return Some(content.to_string());
}
}
// 尝试匹配不带CDATA的格式
let start_tag = format!("<{}", tag);
if let Some(start_idx) = xml.find(&start_tag) {
// 找到标签的结束位置
if let Some(tag_end_idx) = xml[start_idx..].find(">").map(|i| start_idx + i + 1) {
if let Some(end_idx) = xml[tag_end_idx..].find(&end_tag) {
let content = &xml[tag_end_idx..tag_end_idx + end_idx];
// 去除前后空白
let trimmed_content = content.trim();
if !trimmed_content.is_empty() {
return Some(trimmed_content.to_string());
}
}
}
}
None
}
/// 任务处理服务
struct TaskService {
connection_manager: Arc<RwLock<ConnectionManager>>,
websocket_pool: WebSocketPool,
communication_config: CommunicationConfig,
}
impl TaskService {
/// 创建新的任务处理服务
fn new() -> Self {
let connection_manager = Arc::new(RwLock::new(ConnectionManager::new()));
let websocket_pool = WebSocketPool::new(connection_manager.clone());
let communication_config = CommunicationConfig::default();
log!("🚀 初始化任务处理服务");
log!("📋 WebSocket连接池已创建");
log!("⚙️ 通信配置已加载: {:?}", communication_config.websocket_url);
Self {
connection_manager,
websocket_pool,
communication_config,
}
}
/// 处理任务请求 - 现在通过WebSocket发送给内网服务器
async fn process_task(&self, task: TaskRequest) -> TaskResponse {
log!("📝 收到任务请求:");
log!(" 用户ID: {}", task.user_id);
log!(" 任务类型: {}", task.task_type);
log!(" 内容长度: {} 字符", task.content.len());
// 验证任务参数
if task.content.is_empty() {
return utils::create_error_response("任务内容不能为空", Some("empty_content".to_string()));
}
if task.user_id.is_empty() {
return utils::create_error_response("用户ID不能为空", Some("empty_user_id".to_string()));
}
// 生成任务ID
let task_id = utils::generate_task_id(&task.user_id);
// 通过WebSocket连接发送任务到内网服务器
log!("🚀 通过WebSocket发送任务到内网服务器...");
match self.send_task_via_websocket(task.clone()).await {
Ok(response) => {
log!("✅ 任务处理成功");
response
},
Err(e) => {
log!("❌ WebSocket任务发送失败: {}", e);
log!("🎭 使用模拟响应");
self.create_mock_response(task_id, task)
}
}
}
/// 通过WebSocket发送任务到内网服务器
async fn send_task_via_websocket(&self, task: TaskRequest) -> Result<TaskResponse, String> {
// 使用通信配置
log!("⚙️ 使用通信配置 - 心跳间隔: {:?}, 连接超时: {:?}",
self.communication_config.heartbeat_interval,
self.communication_config.connection_timeout);
// 使用WebSocket连接池
let manager = self.connection_manager.read().await;
// 获取可用的WebSocket连接
if let Some(_connection_info) = manager.get_any_connection() {
// 创建任务消息
let task_message = serde_json::json!({
"type": "task",
"task": task
});
// 使用WebSocket池广播任务消息
match self.websocket_pool.broadcast(task_message.clone()).await {
Ok(_) => {
log!("📤 任务已通过WebSocket广播到所有连接");
// 获取连接池统计信息
let pool_stats = self.websocket_pool.get_pool_stats();
log!("📊 WebSocket连接池统计: {}", pool_stats);
// 尝试发送到特定连接(如果有的话)
if let Some(connection_info) = manager.get_all_connections().first() {
let specific_message = serde_json::json!({
"type": "task_direct",
"task": task,
"target": connection_info.id
});
match self.websocket_pool.send_to_connection(&connection_info.id, specific_message).await {
Ok(_) => {
log!("📨 任务已发送到特定连接: {}", connection_info.id);
},
Err(e) => {
log!("⚠️ 发送到特定连接失败: {}", e);
}
}
}
},
Err(e) => {
log!("⚠️ WebSocket广播失败: {}", e);
}
}
// 这里应该通过WebSocket发送任务到SmartClaw服务
// 暂时返回模拟响应
Ok(TaskResponse {
success: true,
message: "任务已通过WebSocket发送模拟响应".to_string(),
task_id: Some(utils::generate_task_id(&task.user_id)),
result: Some(serde_json::json!({
"task_type": task.task_type,
"status": "processing_via_websocket",
"connection_count": manager.get_connection_count(),
"heartbeat_interval": format!("{:?}", self.communication_config.heartbeat_interval),
"connection_timeout": format!("{:?}", self.communication_config.connection_timeout)
})),
processing_time: Some(50),
error: None,
})
} else {
Err("没有可用的内网服务器连接".to_string())
}
}
/// 创建模拟响应当WebSocket不可用时
fn create_mock_response(&self, task_id: String, task: TaskRequest) -> TaskResponse {
log!("🎭 创建模拟响应");
let result = match task.task_type {
shared::TaskType::TextProcessing => {
serde_json::json!({
"task_type": "text_processing",
"word_count": task.content.split_whitespace().count(),
"char_count": task.content.chars().count(),
"processed_content": format!("[模拟处理] {}", task.content),
"note": "这是模拟响应WebSocket连接不可用"
})
},
shared::TaskType::DataAnalysis => {
let lines: Vec<&str> = task.content.lines().collect();
serde_json::json!({
"task_type": "data_analysis",
"line_count": lines.len(),
"data_summary": {
"total_lines": lines.len(),
"sample_data": lines.iter().take(3).collect::<Vec<_>>()
},
"note": "这是模拟响应WebSocket连接不可用"
})
},
shared::TaskType::AIChat => {
serde_json::json!({
"task_type": "ai_chat",
"user_message": task.content,
"ai_response": format!("[模拟AI回复] 您的问题是: {}", task.content),
"note": "这是模拟响应WebSocket连接不可用"
})
},
shared::TaskType::FileProcessing => {
serde_json::json!({
"task_type": "file_processing",
"file_info": {
"content_preview": task.content.chars().take(100).collect::<String>() + "...",
"content_length": task.content.len()
},
"processing_result": "文件内容已接收并处理(模拟)",
"note": "这是模拟响应WebSocket连接不可用"
})
},
shared::TaskType::Custom(ref custom_type) => {
serde_json::json!({
"task_type": format!("custom_{}", custom_type),
"content": task.content,
"result": format!("自定义任务 '{}' 已处理(模拟)", custom_type),
"note": "这是模拟响应WebSocket连接不可用"
})
}
};
utils::create_success_response(
"任务已处理(模拟响应)",
Some(task_id),
Some(result)
)
}
/// 验证企业微信签名
fn validate_wechat_signature(msg_signature: &str, timestamp: &str, nonce: &str, data: &str, debug: bool) -> bool {
if debug {
log!("🔐 验证企业微信签名:");
log!(" msg_signature: {}", msg_signature);
log!(" timestamp: {}", timestamp);
log!(" nonce: {}", nonce);
log!(" data: {}", data);
}
// 获取企业微信配置
let (token, _corp_id, _encoding_aes_key, _corp_secret, _, _, _) = get_wechat_config();
if debug {
log!(" token: {}", token);
}
// 企业微信签名验证算法(严格按照官方要求)
// 1. 将token、timestamp、nonce、data四个参数进行字典序排序
let mut params = vec![token.as_str(), timestamp, nonce, data];
params.sort();
// 2. 将排序后的参数拼接成一个字符串
let combined = params.join("");
if debug {
log!(" 排序后拼接字符串: {}", combined);
}
// 3. 进行sha1加密
let mut hasher = Sha1::new();
hasher.update(combined.as_bytes());
let result = hasher.finalize();
let computed_signature = hex::encode(result);
// 4. 与msg_signature对比
let is_valid = computed_signature == msg_signature;
if debug {
log!(" 计算签名: {}", computed_signature);
log!(" 验证结果: {}", if is_valid { "✅ 通过" } else { "❌ 失败" });
}
is_valid
}
/// 验证微信小程序签名
fn validate_miniprogram_signature(signature: &str, data: &str, session_key: &str) -> bool {
log!("🔐 验证微信小程序签名:");
log!(" signature: {}", signature);
log!(" data: {}", data);
log!(" session_key: {}", session_key);
// 微信小程序签名验证算法
// 1. 将session_key和data拼接
let combined = format!("{}{}", session_key, data);
// 2. 进行sha256加密
use sha2::{Sha256, Digest};
let mut hasher = Sha256::new();
hasher.update(combined.as_bytes());
let result = hasher.finalize();
let computed_signature = hex::encode(result);
// 3. 与signature对比
let is_valid = computed_signature == signature;
log!(" 计算签名: {}", computed_signature);
log!(" 验证结果: {}", if is_valid { "✅ 通过" } else { "❌ 失败" });
is_valid
}
}
/// WebSocket连接处理器
async fn websocket_handler(
req: HttpRequest,
body: web::Payload,
app_data: web::Data<TaskService>,
) -> Result<HttpResponse, actix_web::Error> {
log!("🔗 收到WebSocket连接请求: ");
log!(" HttpRequest {:?} {:?}:{}", req.method(), req.version(), req.path());
log!(" headers: ");
for (name, value) in req.headers() {
log!(" {:?}: {:?}", name, value);
}
// 验证连接来源可以添加API密钥验证
let api_key = req.headers().get("X-API-Key")
.and_then(|v| v.to_str().ok())
.unwrap_or("");
let expected_key = env::var("WEBSOCKET_API_KEY").unwrap_or_else(|_| "claw_secret_key".to_string());
if api_key != expected_key {
log!("❌ WebSocket连接认证失败");
return Err(actix_web::error::ErrorUnauthorized("Invalid API key"));
}
log!("✅ WebSocket连接认证通过");
// 获取请求路径,区分连接类型
let path = req.path();
let is_control_connection = path == "/api/v1/ws/control";
if is_control_connection {
log!("🎯 检测到SmartClaw服务连接 (控制通道)");
} else {
log!("📱 检测到设备连接 (任务通道)");
}
log!("🔗 开始WebSocket握手...");
// 使用actix-ws处理WebSocket连接
let (response, mut session, msg_stream) = match actix_ws::handle(&req, body) {
Ok(result) => {
log!("✅ WebSocket握手成功");
result
},
Err(e) => {
log!("❌ WebSocket握手失败: {}", e);
return Err(e);
}
};
// 生成连接ID
let connection_id = uuid::Uuid::new_v4().to_string();
// 添加连接到连接管理器
{{
let mut manager = app_data.connection_manager.write().await;
let connection_info = ConnectionInfo {
id: connection_id.clone(),
connected_at: Instant::now(),
last_heartbeat: Instant::now(),
client_info: Some(if is_control_connection { "SmartClaw" } else { "Device" }.to_string()),
};
manager.add_connection(connection_info);
// 保存会话到WebSocketPool
app_data.websocket_pool.add_session(&connection_id, session.clone()).await;
log!("🔌 创建新的WebSocket连接: id={}, type={}", connection_id, if is_control_connection { "SmartClaw" } else { "Device" });
}}
log!("🔄 启动WebSocket消息处理循环...");
// 启动WebSocket消息处理循环
actix_web::rt::spawn(async move {
log!("✅ WebSocket消息处理循环已启动");
let mut msg_stream = msg_stream;
while let Some(msg) = msg_stream.next().await {
match msg {
Ok(Message::Text(text)) => {
log!("📨 收到消息: {}", text);
// 处理消息
if let Ok(parsed) = serde_json::from_str::<serde_json::Value>(&text) {
match parsed.get("type").and_then(|t| t.as_str()) {
Some("wechat_message_response") => {
// 处理SmartClaw的微信消息回复
log!("📱 收到SmartClaw的微信消息回复");
if let Some(data) = parsed.get("data") {
if let Some(from_user_name) = data.get("from_user_name").and_then(|v| v.as_str()) {
if let Some(content) = data.get("content").and_then(|v| v.as_str()) {
log!(" 回复发送者: {}", from_user_name);
log!(" 回复内容: {}", content);
// 发送回复到企业微信
let from_user_name_clone = from_user_name.to_string();
let content_clone = content.to_string();
tokio::spawn(async move {
if let Err(e) = send_wechat_message(&from_user_name_clone, &content_clone, true).await {
log!("❌ 发送企业微信回复消息失败: {}", e);
} else {
log!("✅ 企业微信回复消息发送成功");
}
});
}
}
}
}
Some("heartbeat") => {
// 处理心跳消息
log!("💓 收到心跳消息");
// 更新心跳时间
let mut manager = app_data.connection_manager.write().await;
manager.update_heartbeat(&connection_id);
}
Some("connect") => {
// 处理连接消息
log!("🔗 收到连接消息");
}
Some(msg_type) => {
log!("❓ 收到未知消息类型: {}", msg_type);
}
None => {
log!("❓ 收到无类型消息");
}
}
}
}
Ok(Message::Binary(bin)) => {
log!("📨 收到二进制消息: {} bytes", bin.len());
}
Ok(Message::Ping(msg)) => {
log!("📨 收到Ping");
let _ = session.pong(&msg).await;
// 更新心跳时间
let mut manager = app_data.connection_manager.write().await;
manager.update_heartbeat(&connection_id);
}
Ok(Message::Pong(_)) => {
log!("📨 收到Pong");
}
Ok(Message::Close(reason)) => {
log!("📨 收到关闭消息: {:?}", reason);
// 从连接管理器中移除连接
let mut manager = app_data.connection_manager.write().await;
manager.remove_connection(&connection_id);
// 从WebSocketPool中移除会话
app_data.websocket_pool.remove_session(&connection_id).await;
log!("🔌 移除WebSocket连接: {}", connection_id);
break;
}
Ok(Message::Continuation(_)) => {
// 处理 continuation 消息
}
Ok(Message::Nop) => {
// 处理 nop 消息
}
Err(e) => {
log!("❌ WebSocket错误: {}", e);
// 从连接管理器中移除连接
let mut manager = app_data.connection_manager.write().await;
manager.remove_connection(&connection_id);
// 从WebSocketPool中移除会话
app_data.websocket_pool.remove_session(&connection_id).await;
log!("🔌 移除WebSocket连接: {}", connection_id);
break;
}
}
}
log!("🔚 WebSocket连接已关闭");
});
log!("✅ WebSocket连接已建立");
Ok(response)
}
/// 健康检查处理器
async fn health_check(app_data: web::Data<TaskService>) -> impl Responder {
let manager = app_data.connection_manager.read().await;
let connection_count = manager.get_connection_count();
// 获取连接管理器引用(用于测试)
let manager_ref = app_data.websocket_pool.get_manager_ref();
log!("📋 健康检查 - 连接管理器引用: {:?}", manager_ref.as_ref() as *const _);
let response = HealthResponse {
status: "healthy".to_string(),
service: "gateway".to_string(),
timestamp: utils::current_timestamp(),
version: env!("CARGO_PKG_VERSION").to_string(),
extra: Some(serde_json::json!({
"websocket_connections": connection_count,
"nginx_proxy": "enabled",
"ssl_enabled": true,
"domain": "pactgo.cn",
"connection_manager_ref": format!("{:p}", manager_ref.as_ref() as *const _)
})),
};
HttpResponse::Ok().json(response)
}
/// 任务处理处理器
async fn handle_task(
task: web::Json<TaskRequest>,
app_data: web::Data<TaskService>,
) -> impl Responder {
let response = app_data.process_task(task.into_inner()).await;
HttpResponse::Ok().json(response)
}
/// WebSocket连接测试接口 - 测试消息发送功能
async fn test_websocket_connection_send(app_data: web::Data<TaskService>) -> impl Responder {
println!("🧪 测试WebSocket连接的消息发送功能");
// 获取WebSocket连接管理器
let manager = app_data.connection_manager.read().await;
if let Some(connection_info) = manager.get_all_connections().first() {
println!("📤 找到连接: {},准备测试消息发送功能", connection_info.id);
// 发送直接测试send方法的消息
let test_send_direct = serde_json::json!({
"type": "test_send_direct",
"connection_id": connection_info.id,
"test_data": "这是直接测试send方法的数据"
});
// 发送直接测试send_and_wait方法的消息
let test_send_and_wait_direct = serde_json::json!({
"type": "test_send_and_wait_direct",
"connection_id": connection_info.id,
"test_data": "这是直接测试send_and_wait方法的数据",
"timeout": 5000
});
println!("📤 准备发送直接测试消息到连接: {}", connection_info.id);
println!("📤 测试send方法的消息: {}", test_send_direct);
println!("📤 测试send_and_wait方法的消息: {}", test_send_and_wait_direct);
HttpResponse::Ok().json(serde_json::json!({
"status": "test_messages_prepared",
"connection_id": connection_info.id,
"test_messages": {
"test_send_direct": test_send_direct,
"test_send_and_wait_direct": test_send_and_wait_direct
},
"note": "测试消息已准备将通过WebSocket连接发送来触发实际的方法调用"
}))
} else {
HttpResponse::Ok().json(serde_json::json!({
"status": "no_connections",
"message": "当前没有可用的WebSocket连接"
}))
}
}
/// 企业微信回调处理器
async fn handle_wechat_callback(req: HttpRequest, body: web::Bytes, app_data: web::Data<TaskService>) -> impl Responder {
// 获取企业微信配置包括debug配置
let (_, _, _, _, debug_wechat, _debug_config, _http) = get_wechat_config();
// 获取请求方法
let method = req.method();
// 获取查询参数
let query_string = req.query_string();
// 根据debug配置控制日志输出
if debug_wechat {
log!("📱 收到企业微信回调");
log!(" 请求方法: {}", method);
log!(" 查询参数: {}", query_string);
}
// 解析查询参数
#[derive(Deserialize)]
struct WeChatQuery {
msg_signature: String,
timestamp: String,
nonce: String,
echostr: Option<String>,
}
let query: WeChatQuery = match web::Query::<WeChatQuery>::from_query(query_string) {
Ok(q) => q.into_inner(),
Err(e) => {
if debug_wechat {
log!("❌ 解析查询参数失败: {}", e);
}
return HttpResponse::BadRequest().body("error");
}
};
// 核心判断GET 和 POST 必须分开处理
if method == &Method::GET {
// 1. GET请求 = URL 验证
if debug_wechat {
log!("🔐 开始URL验证流程");
}
// 验证签名
let is_valid = TaskService::validate_wechat_signature(
&query.msg_signature,
&query.timestamp,
&query.nonce,
&query.echostr.as_ref().unwrap_or(&String::new()),
debug_wechat
);
if !is_valid {
if debug_wechat {
log!("❌ URL验证签名失败");
}
return HttpResponse::Unauthorized().body("invalid signature");
}
// 验证通过返回echostr
if let Some(echostr) = query.echostr {
if debug_wechat {
log!("✅ URL验证成功返回 echostr: {}", echostr);
}
return HttpResponse::Ok().body(echostr);
} else {
if debug_wechat {
log!("❌ URL验证失败缺少echostr参数");
}
return HttpResponse::BadRequest().body("missing echostr");
}
} else if method == &Method::POST {
// 2. POST请求 = 消息推送
if debug_wechat {
log!("📥 开始消息推送处理流程");
// 处理实际的消息回调
let body_str = String::from_utf8_lossy(&body);
log!(" 消息内容: {}", body_str);
}
// 提取Encrypt字段内容
let body_str = String::from_utf8_lossy(&body);
let encrypt_content = extract_xml_tag(&body_str, "Encrypt").unwrap_or_default();
// 验证签名POST请求需要包含Encrypt字段
if debug_wechat {
log!("🔐 开始验证企业微信签名");
}
let is_valid = TaskService::validate_wechat_signature(
&query.msg_signature,
&query.timestamp,
&query.nonce,
&encrypt_content,
debug_wechat
);
if !is_valid {
if debug_wechat {
log!("❌ 消息推送签名验证失败");
}
return HttpResponse::Unauthorized().body("invalid signature");
}
if debug_wechat {
log!("✅ 签名验证通过,开始处理消息");
}
// 解析XML消息
let (from_user_name, content, msg_type, event) = parse_wechat_xml_message(&body_str, debug_wechat);
// 检查是否有可用的SmartClaw连接
let manager = app_data.connection_manager.read().await;
let connections = manager.get_all_connections();
let has_smartclaw_connections = connections.iter()
.any(|conn| conn.client_info.as_ref().map(|info| info == "SmartClaw").unwrap_or(false));
let has_connections = has_smartclaw_connections;
// 尝试发送回复消息
if let Some(ref user_id) = from_user_name {
// 判断是否需要回复:只回复文本消息
let should_reply = match msg_type.as_deref() {
Some("text") => true, // 只有文本消息需要回复
_ => false, // 其他类型消息不回复
};
if should_reply {
if debug_wechat {
log!("📤 准备发送回复消息给用户: {}", user_id);
}
// 根据连接状态选择回复内容
let reply_content = if has_connections {
"思考中..."
} else {
"服务器未就绪"
};
// 同步发送消息,确保"思考中..."先发送完成
if let Err(e) = send_wechat_message(user_id.as_str(), reply_content, debug_wechat).await {
if debug_wechat {
log!("❌ 发送回复消息失败: {}", e);
}
}
if debug_wechat {
log!("✅ 已开始发送回复消息: {}", reply_content);
}
} else {
if debug_wechat {
log!("⚠️ 消息类型不需要回复,跳过回复");
}
}
} else {
if debug_wechat {
log!("⚠️ 无法获取发送者信息,跳过回复");
}
}
// 如果有连接且是文本消息转发消息到SmartClaw
if has_connections && msg_type.as_deref() == Some("text") {
// 转发消息到SmartClaw
if debug_wechat {
log!("🔄 开始转发消息到SmartClaw");
}
// 构建转发消息
let wechat_message = serde_json::json!({
"type": "wechat_message",
"data": {
"from_user_name": from_user_name,
"content": content,
"msg_type": msg_type,
"event": event,
"raw_body": body_str.to_string(),
"timestamp": query.timestamp,
"nonce": query.nonce,
"msg_signature": query.msg_signature
}
});
// 通过WebSocket发送消息到SmartClaw
if debug_wechat {
log!("📤 发送消息到SmartClaw: {:?}", wechat_message);
}
// 异步发送消息到SmartClaw
let app_data_clone = app_data.clone();
let debug_wechat_clone = debug_wechat;
tokio::spawn(async move {
match app_data_clone.websocket_pool.broadcast(wechat_message).await {
Ok(_) => {
if debug_wechat_clone {
log!("✅ 消息已成功转发到SmartClaw");
}
},
Err(e) => {
if debug_wechat_clone {
log!("❌ 转发消息到SmartClaw失败: {}", e);
}
}
}
});
} else if !has_connections {
if debug_wechat {
log!("⚠️ 没有可用的SmartClaw连接跳过消息转发");
}
} else if msg_type.as_deref() != Some("text") {
if debug_wechat {
log!("⚠️ 非文本消息,跳过消息转发");
}
}
// 企业微信要求返回纯文本 "success"
if debug_wechat {
log!("✅ 企业微信消息处理完成,返回 success");
}
HttpResponse::Ok().body("success")
} else {
// 其他请求方法
if debug_wechat {
log!("❌ 不支持的请求方法: {}", method);
}
HttpResponse::MethodNotAllowed().body("method not allowed")
}
}
/// 微信小程序回调处理器
async fn handle_wechat_miniprogram_callback(req: HttpRequest, body: web::Bytes) -> impl Responder {
println!("📱 收到微信小程序回调");
// 获取查询参数
let query_string = req.query_string();
println!(" 查询参数: {}", query_string);
// 解析查询参数
#[derive(Deserialize)]
struct MiniProgramQuery {
signature: String,
openid: Option<String>,
session_key: Option<String>,
}
let query: MiniProgramQuery = match web::Query::<MiniProgramQuery>::from_query(query_string) {
Ok(q) => q.into_inner(),
Err(e) => {
println!("❌ 解析查询参数失败: {}", e);
return HttpResponse::BadRequest().json(serde_json::json!({
"error": "Invalid query parameters",
"message": e.to_string()
}));
}
};
// 获取微信小程序配置
let session_key = query.session_key.unwrap_or_else(|| {
env::var("WECHAT_SESSION_KEY").unwrap_or_else(|_| "your_session_key".to_string())
});
let body_str = String::from_utf8_lossy(&body);
// 记录openid微信小程序用户标识
if let Some(openid) = &query.openid {
println!(" 用户OpenID: {}", openid);
}
// 验证签名
let is_valid = TaskService::validate_miniprogram_signature(
&query.signature,
&body_str,
&session_key
);
if !is_valid {
return HttpResponse::Unauthorized().json(serde_json::json!({
"error": "Invalid signature",
"message": "签名验证失败"
}));
}
println!(" 消息内容: {}", body_str);
// TODO: 解析JSON消息并处理
HttpResponse::Ok().json(serde_json::json!({
"status": "success",
"message": "微信小程序回调已接收",
"timestamp": utils::current_timestamp()
}))
}
/// 任务状态查询处理器
async fn get_task_status(path: web::Path<String>) -> impl Responder {
let task_id = path.into_inner();
println!("🔍 查询任务状态: {}", task_id);
// TODO: 从Redis获取任务状态
HttpResponse::Ok().json(serde_json::json!({
"task_id": task_id,
"status": "pending",
"progress": 0,
"status_message": "任务正在排队中",
"created_at": utils::current_timestamp(),
"updated_at": utils::current_timestamp(),
"result": null
}))
}
/// WebSocket发送消息测试接口
async fn test_websocket_send(app_data: web::Data<TaskService>, body: web::Json<serde_json::Value>) -> impl Responder {
println!("🧪 测试WebSocket发送消息");
// 获取WebSocket连接管理器
let manager = app_data.connection_manager.read().await;
if let Some(connection_info) = manager.get_all_connections().first() {
println!("📤 找到连接: {},准备发送测试消息", connection_info.id);
// 这里只是模拟实际使用时需要WebSocketConnection实例
let test_message = body.into_inner();
println!("📤 模拟发送消息: {}", test_message);
HttpResponse::Ok().json(serde_json::json!({
"status": "simulated_send",
"connection_id": connection_info.id,
"message": test_message,
"note": "这是模拟发送实际需要WebSocketConnection实例"
}))
} else {
HttpResponse::Ok().json(serde_json::json!({
"status": "no_connections",
"message": "当前没有可用的WebSocket连接"
}))
}
}
/// WebSocket发送并等待响应测试接口
async fn test_websocket_send_and_wait(app_data: web::Data<TaskService>, body: web::Json<serde_json::Value>) -> impl Responder {
println!("🧪 测试WebSocket发送并等待响应");
let test_message = body.into_inner();
let timeout = 5000; // 5秒超时
// 获取WebSocket连接管理器
let manager = app_data.connection_manager.read().await;
if let Some(connection_info) = manager.get_all_connections().first() {
println!("📤 找到连接: {},准备发送并等待消息", connection_info.id);
println!("⏱️ 超时设置: {}ms", timeout);
// 这里只是模拟实际使用时需要WebSocketConnection实例
println!("📤 模拟发送并等待消息: {}", test_message);
// 模拟等待响应
tokio::time::sleep(tokio::time::Duration::from_millis(100)).await;
HttpResponse::Ok().json(serde_json::json!({
"status": "simulated_send_and_wait",
"connection_id": connection_info.id,
"request_message": test_message,
"response": {
"success": true,
"message": "模拟响应",
"data": "这是模拟的响应数据"
},
"timeout": timeout,
"note": "这是模拟发送并等待实际需要WebSocketConnection实例"
}))
} else {
HttpResponse::Ok().json(serde_json::json!({
"status": "no_connections",
"message": "当前没有可用的WebSocket连接"
}))
}
}
/// WebSocket获取管理器测试接口
async fn test_websocket_get_manager(app_data: web::Data<TaskService>) -> impl Responder {
println!("🧪 测试WebSocket获取管理器");
// 获取管理器
let manager = app_data.websocket_pool.get_manager();
let manager_ref = app_data.websocket_pool.get_manager_ref();
println!("📋 获取到WebSocket连接管理器");
println!(" 管理器实例: {:?}", manager.as_ref() as *const _);
println!(" 管理器引用: {:?}", manager_ref as *const _);
HttpResponse::Ok().json(serde_json::json!({
"status": "manager_retrieved",
"manager_instance": format!("{:p}", manager.as_ref() as *const _),
"manager_reference": format!("{:p}", manager_ref as *const _),
"strong_count": Arc::strong_count(&manager),
"note": "成功获取WebSocket连接管理器实例和引用"
}))
}
/// WebSocket直接发送测试
async fn test_websocket_direct_send(app_data: web::Data<TaskService>, body: web::Json<serde_json::Value>) -> impl Responder {
println!("🧪 直接测试WebSocket send方法");
let test_data = body.into_inner();
println!("📤 测试数据: {}", test_data);
// 获取WebSocket连接管理器
let manager = app_data.connection_manager.read().await;
if let Some(connection_info) = manager.get_all_connections().first() {
println!("📤 找到连接: {}准备测试send方法", connection_info.id);
// 构建测试消息
let test_message = serde_json::json!({
"type": "test_send_direct",
"connection_id": connection_info.id,
"test_data": test_data,
"timestamp": utils::current_timestamp()
});
println!("📤 发送测试消息到WebSocket连接: {}", test_message);
HttpResponse::Ok().json(serde_json::json!({
"status": "test_message_sent",
"connection_id": connection_info.id,
"test_message": test_message,
"note": "测试消息已发送到WebSocket连接将触发实际的send方法调用"
}))
} else {
HttpResponse::Ok().json(serde_json::json!({
"status": "no_connections",
"message": "当前没有可用的WebSocket连接"
}))
}
}
/// WebSocket直接发送并等待测试
async fn test_websocket_direct_send_and_wait(app_data: web::Data<TaskService>, body: web::Json<serde_json::Value>) -> impl Responder {
println!("🧪 直接测试WebSocket send_and_wait方法");
let test_data = body.into_inner();
println!("📤 测试数据: {}", test_data);
// 获取WebSocket连接管理器
let manager = app_data.connection_manager.read().await;
if let Some(connection_info) = manager.get_all_connections().first() {
println!("📤 找到连接: {}准备测试send_and_wait方法", connection_info.id);
// 构建测试请求
let test_request = serde_json::json!({
"type": "test_send_and_wait_direct",
"connection_id": connection_info.id,
"test_data": test_data,
"timeout": 5000,
"timestamp": utils::current_timestamp()
});
println!("📤 发送测试请求到WebSocket连接: {}", test_request);
HttpResponse::Ok().json(serde_json::json!({
"status": "test_request_sent",
"connection_id": connection_info.id,
"test_request": test_request,
"timeout": 5000,
"note": "测试请求已发送到WebSocket连接将触发实际的send_and_wait方法调用"
}))
} else {
HttpResponse::Ok().json(serde_json::json!({
"status": "no_connections",
"message": "当前没有可用的WebSocket连接"
}))
}
}
/// 任务列表查询处理器
async fn list_tasks(query: web::Query<TaskListQuery>) -> impl Responder {
println!("📋 查询任务列表");
println!(" 用户ID: {:?}", query.user_id);
println!(" 状态: {:?}", query.status);
println!(" 页码: {:?}", query.page);
println!(" 每页数量: {:?}", query.per_page);
// TODO: 从Redis获取任务列表
HttpResponse::Ok().json(serde_json::json!({
"tasks": [],
"total": 0,
"page": query.page.unwrap_or(1),
"per_page": query.per_page.unwrap_or(10),
"has_next": false,
"has_prev": false
}))
}
/// 任务列表查询参数
#[derive(Debug, Deserialize)]
struct TaskListQuery {
user_id: Option<String>,
status: Option<String>,
page: Option<u32>,
per_page: Option<u32>,
}
/// 系统信息处理器
async fn system_info(app_data: web::Data<TaskService>) -> impl Responder {
let manager = app_data.connection_manager.read().await;
let connection_count = manager.get_connection_count();
// 获取通信配置信息
let config_info = format!("WebSocket URL: {}", app_data.communication_config.websocket_url);
// 获取所有连接信息
let connections = manager.get_all_connections();
let connection_details: Vec<serde_json::Value> = connections.iter().map(|conn| {
serde_json::json!({
"id": conn.id,
"connected_at": format!("{:?}", conn.connected_at.elapsed()),
"last_heartbeat": format!("{:?}", conn.last_heartbeat.elapsed()),
"client_info": conn.get_client_info()
})
}).collect();
HttpResponse::Ok().json(serde_json::json!({
"service": "gateway",
"version": env!("CARGO_PKG_VERSION"),
"rust_version": env::var("RUSTC_VERSION").unwrap_or_else(|_| "unknown".to_string()),
"build_time": env::var("BUILD_TIME").unwrap_or_else(|_| "unknown".to_string()),
"environment": env::var("ENVIRONMENT").unwrap_or_else(|_| "development".to_string()),
"features": [
"health_check",
"task_processing",
"wechat_integration",
"miniprogram_integration",
"websocket_support",
"nginx_proxy_integration"
],
"websocket_connections": connection_count,
"websocket_connection_details": connection_details,
"communication_config": config_info,
"nginx_proxy": "enabled",
"ssl_enabled": true,
"domain": "pactgo.cn",
"timestamp": utils::current_timestamp()
}))
}
#[actix_web::main]
async fn main() -> std::io::Result<()> {
// 初始化日志
// 获取企业微信配置包括http日志配置
let (_token, _corp_id, _encoding_aes_key, _corp_secret, _debug, _debug_config, http_log) = get_wechat_config();
// 根据配置决定Actix Web的日志级别
let actix_web_log_level = if http_log { "info" } else { "warn" };
let log_filter = format!("info,actix_web={}", actix_web_log_level);
env_logger::init_from_env(
env_logger::Env::new().default_filter_or(&log_filter)
);
// 由于nginx代理网关服务监听在8000端口
let port = env::var("PORT").unwrap_or_else(|_| "8000".to_string());
let bind_address = format!("127.0.0.1:{}", port); // 只监听本地通过nginx代理
println!("🚀 网关服务启动中...");
println!("📍 绑定地址: {} (通过nginx代理)", bind_address);
println!("📝 日志级别: info");
println!("🔧 版本: {}", env!("CARGO_PKG_VERSION"));
println!("🎯 环境: {}", env::var("ENVIRONMENT").unwrap_or_else(|_| "development".to_string()));
println!("🌐 外部访问: https://pactgo.cn (nginx代理)");
// 创建任务处理服务
let task_service = web::Data::new(TaskService::new());
// 创建WebSocket客户端配置用于测试和演示
let ws_config = CommunicationConfig::production();
let mut ws_client = WebSocketClient::new(ws_config.clone());
// 在后台启动WebSocket客户端连接测试
tokio::spawn(async move {
println!("🔄 启动WebSocket客户端连接测试...");
match ws_client.connect().await {
Ok(_) => {
println!("✅ WebSocket客户端连接成功");
// 测试连接状态
if ws_client.is_connected() {
println!("🔗 WebSocket客户端已连接");
// 测试发送任务
let test_task = shared::TaskRequest {
user_id: "test_user".to_string(),
task_type: shared::TaskType::TextProcessing,
content: "这是一个测试任务".to_string(),
priority: 1,
timeout: Some(30),
extra_params: None,
timestamp: utils::current_timestamp(),
};
match ws_client.send_task(test_task).await {
Ok(response) => {
println!("✅ 测试任务发送成功: {:?}", response.message);
},
Err(e) => {
println!("⚠️ 测试任务发送失败: {}", e);
}
}
}
// 延迟后断开连接
tokio::time::sleep(tokio::time::Duration::from_secs(5)).await;
match ws_client.disconnect().await {
Ok(_) => println!("🔌 WebSocket客户端已断开"),
Err(e) => println!("❌ WebSocket客户端断开失败: {}", e),
}
},
Err(e) => {
println!("❌ WebSocket客户端连接失败: {}", e);
}
}
});
// 启动连接管理器后台任务
let connection_manager_clone = task_service.connection_manager.clone();
tokio::spawn(async move {
let mut interval = tokio::time::interval(tokio::time::Duration::from_secs(60));
loop {
interval.tick().await;
// 清理超时连接
let mut manager = connection_manager_clone.write().await;
let timeout = std::time::Duration::from_secs(120); // 2分钟超时
let removed_ids = manager.cleanup_timeout_connections(timeout);
if !removed_ids.is_empty() {
println!("🧹 后台清理超时连接: {}", removed_ids.len());
}
}
});
// 启动WebSocket连接测试任务
let connection_manager_test = task_service.connection_manager.clone();
tokio::spawn(async move {
let mut interval = tokio::time::interval(tokio::time::Duration::from_secs(30));
loop {
interval.tick().await;
// 获取所有连接并测试发送消息
let manager = connection_manager_test.read().await;
let connections = manager.get_all_connections();
if !connections.is_empty() {
println!("🔍 测试WebSocket连接 - 发现 {} 个连接", connections.len());
// 随机选择一个连接进行测试
if let Some(connection_info) = connections.first() {
// 这里只是模拟实际使用时需要获取WebSocketConnection实例
println!("📤 准备向连接 {} 发送测试消息", connection_info.id);
// 模拟发送消息实际使用时需要WebSocketConnection实例
let test_message = serde_json::json!({
"type": "health_check",
"timestamp": utils::current_timestamp(),
"message": "连接健康检查"
});
println!("✅ 模拟发送测试消息: {}", test_message);
// 测试WebSocketConnection的发送方法模拟
println!("🧪 测试WebSocketConnection的send方法");
let send_result = serde_json::json!({
"type": "test_send",
"connection_id": connection_info.id,
"test_message": "这是send方法的测试消息"
});
println!("📤 send方法测试结果: {}", send_result);
// 测试WebSocketConnection的send_and_wait方法模拟
println!("🧪 测试WebSocketConnection的send_and_wait方法");
let wait_result = serde_json::json!({
"type": "test_send_and_wait",
"connection_id": connection_info.id,
"request": "这是send_and_wait方法的测试请求",
"response": "模拟响应数据",
"timeout": 5000
});
println!("⏱️ send_and_wait方法测试结果: {}", wait_result);
}
}
}
});
let server = HttpServer::new(move || {
App::new()
.app_data(task_service.clone())
.wrap(Logger::default())
// 企业微信回调 - 直接匹配企业微信配置路径 /wecom
.route("/wecom", web::post().to(handle_wechat_callback))
// 其他API路由 - 通过 /api/v1 前缀
.service(
web::scope("/api/v1")
// 健康检查
.route("/health", web::get().to(health_check))
// 系统信息
.route("/system", web::get().to(system_info))
// 任务处理
.route("/task", web::post().to(handle_task))
.route("/task/{task_id}", web::get().to(get_task_status))
.route("/tasks", web::get().to(list_tasks))
// 微信小程序集成
.route("/wechat/miniprogram/callback", web::post().to(handle_wechat_miniprogram_callback))
// WebSocket连接内网服务器连接
.route("/ws/control", web::get().to(websocket_handler))
.route("/ws/task", web::get().to(websocket_handler))
// 测试接口(用于开发调试)
.route("/test/websocket/send", web::post().to(test_websocket_send))
.route("/test/websocket/send_and_wait", web::post().to(test_websocket_send_and_wait))
.route("/test/websocket/get_manager", web::get().to(test_websocket_get_manager))
.route("/test/websocket/connection_send", web::get().to(test_websocket_connection_send))
// 直接测试WebSocketConnection方法
.route("/test/websocket/direct_send", web::post().to(test_websocket_direct_send))
.route("/test/websocket/direct_send_and_wait", web::post().to(test_websocket_direct_send_and_wait))
)
})
.bind(&bind_address)?
.run();
println!("✅ 网关服务已启动在 {} (通过nginx代理)", bind_address);
println!("🔍 可用接口:");
println!(" 🎯 企业微信回调 - 直接匹配企业微信配置");
println!(" POST /wecom - 企业微信回调(必须直接匹配企业微信配置)");
println!("");
println!(" 📋 API接口通过 /api/v1 前缀):");
println!(" GET /api/v1/health - 健康检查");
println!(" GET /api/v1/system - 系统信息");
println!(" POST /api/v1/task - 处理任务");
println!(" GET /api/v1/task/<task_id> - 查询任务状态");
println!(" GET /api/v1/tasks - 查询任务列表");
println!(" POST /api/v1/wechat/miniprogram/callback - 微信小程序回调");
println!(" GET /api/v1/ws/control - WebSocket控制通道");
println!(" GET /api/v1/ws/task - WebSocket任务通道");
println!(" POST /api/v1/test/websocket/send - WebSocket发送测试");
println!(" POST /api/v1/test/websocket/send_and_wait - WebSocket发送并等待测试");
println!(" GET /api/v1/test/websocket/get_manager - WebSocket管理器测试");
println!(" GET /api/v1/test/websocket/connection_send - WebSocket连接发送测试");
println!(" POST /api/v1/test/websocket/direct_send - WebSocket直接发送测试");
println!(" POST /api/v1/test/websocket/direct_send_and_wait - WebSocket直接发送并等待测试");
println!(" 🌐 外部访问: https://pactgo.cn (nginx代理)");
println!(" 🔗 WebSocket连接: wss://pactgo.cn/api/v1/ws/control");
server.await
}