Claw 项目完整结构提交

This commit is contained in:
zqm
2026-03-16 15:47:55 +08:00
parent ca4970bcbf
commit fb0aeb6ca2
118 changed files with 28648 additions and 281 deletions

View File

@@ -0,0 +1,15 @@
[package]
name = "shared"
version = "0.1.0"
edition = "2024"
[dependencies]
serde = { version = "^1.0", features = ["derive"] }
serde_json = "^1.0"
chrono = { version = "^0.4", features = ["serde"] }
uuid = { version = "^1.0", features = ["v4", "serde"] }
tokio = { version = "^1.0", features = ["full"] }
bytes = "1.0"
# heed = "^0.20" # 暂时移除后续实现HeedDB功能
# redis = "^0.29" # 暂时移除后续实现Redis功能

View File

@@ -0,0 +1,51 @@
use shared::embedded_redis::{EmbeddedRedisManager, EmbeddedRedisConfig};
use std::sync::Arc;
/// 嵌入式Redis使用示例
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
println!("🚀 启动嵌入式Redis服务器示例...");
// 配置嵌入式Redis
let config = EmbeddedRedisConfig {
bind: "127.0.0.1".to_string(),
port: 6379,
data_dir: "./embedded_redis_data".to_string(),
persistence: true,
max_memory: 64 * 1024 * 1024, // 64MB
cleanup_interval: 300, // 5分钟
};
// 创建管理器
let redis_manager = Arc::new(EmbeddedRedisManager::new(config));
// 启动Redis服务器
redis_manager.start().await?;
println!("✅ 嵌入式Redis服务器已启动");
println!("📍 监听地址: {}", redis_manager.get_connection_url().await);
// 验证服务器状态
let is_running = redis_manager.is_running().await;
println!("🔍 服务器运行状态: {}", if is_running { "运行中" } else { "已停止" });
// 获取配置信息
let config = redis_manager.get_config().await;
println!("📋 配置信息:");
println!(" - 绑定地址: {}", config.bind);
println!(" - 监听端口: {}", config.port);
println!(" - 数据目录: {}", config.data_dir);
println!(" - 持久化: {}", if config.persistence { "启用" } else { "禁用" });
println!(" - 最大内存: {}MB", config.max_memory / (1024 * 1024));
// 保持服务器运行一段时间
println!("⏰ 服务器将在5秒后停止...");
tokio::time::sleep(tokio::time::Duration::from_secs(5)).await;
// 停止Redis服务器
redis_manager.stop().await;
println!("✅ 嵌入式Redis服务器已停止");
Ok(())
}

View File

@@ -0,0 +1,745 @@
use tokio::net::{TcpListener, TcpStream};
use std::sync::Arc;
use tokio::sync::{Mutex, RwLock};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::path::Path;
use std::fs;
use std::time::{SystemTime, Duration, UNIX_EPOCH};
/// 嵌入式Redis配置
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EmbeddedRedisConfig {
pub bind: String,
pub port: u16,
pub data_dir: String,
pub persistence: bool,
pub max_memory: usize,
pub cleanup_interval: u64, // 秒
}
impl Default for EmbeddedRedisConfig {
fn default() -> Self {
Self {
bind: "127.0.0.1".to_string(),
port: 6379,
data_dir: "./embedded_redis_data".to_string(),
persistence: true,
max_memory: 64 * 1024 * 1024, // 64MB
cleanup_interval: 300, // 5分钟
}
}
}
/// 键值对数据结构
#[derive(Debug, Clone, Serialize, Deserialize)]
struct RedisValue {
data: Vec<u8>,
expires_at: Option<u64>, // Unix时间戳
}
impl RedisValue {
fn new(data: Vec<u8>) -> Self {
Self {
data,
expires_at: None,
}
}
fn with_expiry(data: Vec<u8>, ttl_seconds: u64) -> Self {
let expires_at = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_secs() + ttl_seconds;
Self {
data,
expires_at: Some(expires_at),
}
}
fn is_expired(&self) -> bool {
if let Some(expires_at) = self.expires_at {
let now = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_secs();
now >= expires_at
} else {
false
}
}
}
/// 嵌入式Redis数据库
pub struct EmbeddedRedisDb {
data: RwLock<HashMap<String, RedisValue>>,
config: EmbeddedRedisConfig,
}
impl EmbeddedRedisDb {
fn new(config: EmbeddedRedisConfig) -> Self {
let mut db = Self {
data: RwLock::new(HashMap::new()),
config,
};
// 如果启用了持久化,加载数据
if db.config.persistence {
let _ = db.load_from_disk();
}
db
}
/// 从磁盘加载数据
async fn load_from_disk(&mut self) -> Result<(), Box<dyn std::error::Error>> {
let data_path = Path::new(&self.config.data_dir).join("redis_data.json");
if data_path.exists() {
let data = fs::read_to_string(&data_path)?;
let loaded_data: HashMap<String, RedisValue> = serde_json::from_str(&data)?;
let mut db = self.data.write().await;
*db = loaded_data;
println!("✅ 从磁盘加载 {} 个键值对", db.len());
}
Ok(())
}
/// 保存到磁盘
async fn save_to_disk(&self) -> Result<(), Box<dyn std::error::Error>> {
if !self.config.persistence {
return Ok(());
}
// 确保数据目录存在
fs::create_dir_all(&self.config.data_dir)?;
let data_path = Path::new(&self.config.data_dir).join("redis_data.json");
let temp_path = Path::new(&self.config.data_dir).join("redis_data.json.tmp");
let db = self.data.read().await;
let data = serde_json::to_string_pretty(&*db)?;
// 先写入临时文件
fs::write(&temp_path, data)?;
// 原子性重命名
fs::rename(&temp_path, &data_path)?;
println!("💾 保存 {} 个键值对到磁盘", db.len());
Ok(())
}
/// 清理过期键
async fn cleanup_expired(&self) {
let mut db = self.data.write().await;
let keys_to_remove: Vec<String> = db
.iter()
.filter_map(|(k, v)| if v.is_expired() { Some(k.clone()) } else { None })
.collect();
for key in keys_to_remove {
db.remove(&key);
println!("🧹 清理过期键: {}", key);
}
}
/// 获取值
async fn get(&self, key: &str) -> Option<Vec<u8>> {
let db = self.data.read().await;
if let Some(value) = db.get(key) {
if !value.is_expired() {
Some(value.data.clone())
} else {
None
}
} else {
None
}
}
/// 设置值
async fn set(&self, key: String, value: Vec<u8>) {
let mut db = self.data.write().await;
db.insert(key, RedisValue::new(value));
// 异步保存到磁盘(简化实现)
if self.config.persistence {
// 这里可以添加异步保存逻辑
// 为了简化,暂时不实现自动保存
}
}
/// 设置带过期时间的值
async fn setex(&self, key: String, value: Vec<u8>, ttl_seconds: u64) {
let mut db = self.data.write().await;
db.insert(key, RedisValue::with_expiry(value, ttl_seconds));
}
/// 删除键
async fn del(&self, keys: &[String]) -> i64 {
let mut db = self.data.write().await;
let mut count = 0;
for key in keys {
if db.remove(key).is_some() {
count += 1;
}
}
count
}
/// 检查键是否存在
async fn exists(&self, key: &str) -> bool {
let db = self.data.read().await;
if let Some(value) = db.get(key) {
!value.is_expired()
} else {
false
}
}
/// 获取所有键
async fn keys(&self, pattern: &str) -> Vec<String> {
let db = self.data.read().await;
db.keys()
.filter(|key| {
// 简单的通配符匹配
if pattern == "*" {
true
} else if pattern.ends_with("*") {
let prefix = &pattern[..pattern.len() - 1];
key.starts_with(prefix)
} else {
*key == pattern
}
})
.cloned()
.collect()
}
/// 获取TTL
async fn ttl(&self, key: &str) -> i64 {
let db = self.data.read().await;
if let Some(value) = db.get(key) {
if let Some(expires_at) = value.expires_at {
let now = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_secs();
if now < expires_at {
(expires_at - now) as i64
} else {
-2 // 键已过期
}
} else {
-1 // 键永不过期
}
} else {
-2 // 键不存在
}
}
/// 获取数据库大小
async fn dbsize(&self) -> usize {
let db = self.data.read().await;
db.len()
}
/// 清空数据库
async fn flushdb(&self) {
let mut db = self.data.write().await;
db.clear();
println!("🗑️ 清空数据库");
}
}
/// 嵌入式Redis服务器
pub struct EmbeddedRedisServer {
config: EmbeddedRedisConfig,
listener: Option<TcpListener>,
db: Arc<EmbeddedRedisDb>,
running: Arc<Mutex<bool>>,
}
impl EmbeddedRedisServer {
/// 创建新的嵌入式Redis服务器
pub fn new(config: EmbeddedRedisConfig) -> Self {
let db = Arc::new(EmbeddedRedisDb::new(config.clone()));
Self {
config,
listener: None,
db,
running: Arc::new(Mutex::new(false)),
}
}
/// 启动嵌入式Redis服务器
pub async fn start(&mut self) -> Result<(), Box<dyn std::error::Error>> {
use std::net::SocketAddr;
let addr: SocketAddr = format!("{}:{}", self.config.bind, self.config.port).parse()?;
println!("🚀 启动嵌入式Redis服务器...");
println!("📍 绑定地址: {}", addr);
println!("💾 数据目录: {}", self.config.data_dir);
println!("💾 持久化: {}", if self.config.persistence { "启用" } else { "禁用" });
println!("💾 最大内存: {}MB", self.config.max_memory / (1024 * 1024));
// 创建TCP监听器
let listener = TcpListener::bind(addr).await?;
self.listener = Some(listener);
// 设置运行状态
*self.running.lock().await = true;
println!("✅ 嵌入式Redis服务器启动成功监听地址: {}", addr);
// 启动后台任务
self.start_background_tasks().await;
// 启动接受连接的任务
self.accept_connections();
Ok(())
}
/// 启动后台任务
async fn start_background_tasks(&self) {
let db = self.db.clone();
let running = self.running.clone();
let cleanup_interval = self.config.cleanup_interval;
let persistence = self.config.persistence;
// 清理过期键任务
tokio::spawn(async move {
while *running.lock().await {
db.cleanup_expired().await;
tokio::time::sleep(Duration::from_secs(cleanup_interval)).await;
}
});
// 如果启用了持久化,启动定期保存任务
if persistence {
let db = self.db.clone();
let running = self.running.clone();
tokio::spawn(async move {
while *running.lock().await {
tokio::time::sleep(Duration::from_secs(60)).await;
if let Err(e) = db.save_to_disk().await {
eprintln!("❌ 自动保存失败: {}", e);
}
}
});
}
}
/// 接受连接
fn accept_connections(&self) {
if let Some(listener) = &self.listener {
let db = self.db.clone();
let running = self.running.clone();
// 创建一个新的监听器用于异步任务
let addr = listener.local_addr().expect("Failed to get local address");
tokio::spawn(async move {
// 重新绑定监听器
match TcpListener::bind(addr).await {
Ok(new_listener) => {
loop {
if !*running.lock().await {
break;
}
match new_listener.accept().await {
Ok((socket, addr)) => {
println!("🔗 新连接来自: {}", addr);
let db = db.clone();
let running = running.clone();
tokio::spawn(async move {
if let Err(e) = handle_connection(socket, db, running).await {
eprintln!("❌ 连接处理错误: {}", e);
}
});
}
Err(e) => {
eprintln!("❌ 接受连接错误: {}", e);
break;
}
}
}
}
Err(e) => {
eprintln!("❌ 重新绑定监听器失败: {}", e);
}
}
});
}
}
/// 获取Redis连接URL
pub fn get_connection_url(&self) -> String {
format!("redis://{}:{}/", self.config.bind, self.config.port)
}
/// 获取配置
pub fn get_config(&self) -> &EmbeddedRedisConfig {
&self.config
}
/// 停止服务器
pub async fn stop(&mut self) {
println!("🛑 停止嵌入式Redis服务器...");
// 设置停止标志
*self.running.lock().await = false;
// 保存数据到磁盘
if self.config.persistence {
if let Err(e) = self.db.save_to_disk().await {
eprintln!("❌ 保存数据失败: {}", e);
}
}
self.listener = None;
println!("✅ 嵌入式Redis服务器已停止");
}
/// 获取数据库引用(供内部使用)
pub fn get_db(&self) -> Arc<EmbeddedRedisDb> {
self.db.clone()
}
}
/// 处理单个连接
async fn handle_connection(
mut socket: TcpStream,
db: Arc<EmbeddedRedisDb>,
running: Arc<Mutex<bool>>,
) -> Result<(), Box<dyn std::error::Error>> {
use tokio::io::{AsyncBufReadExt, AsyncWriteExt, BufReader};
let (reader, mut writer) = socket.split();
let mut reader = BufReader::new(reader);
let mut buffer = String::new();
loop {
if !*running.lock().await {
break;
}
buffer.clear();
match reader.read_line(&mut buffer).await {
Ok(0) => break, // 连接关闭
Ok(_) => {
let line = buffer.trim();
if line.is_empty() {
continue;
}
let response = match parse_and_execute_command(line, &db).await {
Ok(resp) => resp,
Err(e) => format!("-ERR {}\r\n", e),
};
writer.write_all(response.as_bytes()).await?;
writer.flush().await?;
}
Err(e) => {
eprintln!("❌ 读取错误: {}", e);
break;
}
}
}
Ok(())
}
/// 解析并执行命令
async fn parse_and_execute_command(
line: &str,
db: &Arc<EmbeddedRedisDb>,
) -> Result<String, Box<dyn std::error::Error>> {
// 解析Redis协议格式
let parts = parse_redis_protocol(line)?;
if parts.is_empty() {
return Ok("+OK\r\n".to_string());
}
let command = parts[0].to_uppercase();
match command.as_str() {
"PING" => {
if parts.len() > 1 {
Ok(format!("+{}\r\n", parts[1..].join(" ")))
} else {
Ok("+PONG\r\n".to_string())
}
}
"GET" => {
if parts.len() != 2 {
return Err("wrong number of arguments for 'get' command".into());
}
let key = &parts[1];
match db.get(key).await {
Some(value) => {
let value_str = String::from_utf8_lossy(&value);
Ok(format!("${}\r\n{}\r\n", value_str.len(), value_str))
}
None => Ok("$-1\r\n".to_string()),
}
}
"SET" => {
if parts.len() < 3 {
return Err("wrong number of arguments for 'set' command".into());
}
let key = &parts[1];
let value = &parts[2];
db.set(key.clone(), value.as_bytes().to_vec()).await;
Ok("+OK\r\n".to_string())
}
"SETEX" => {
if parts.len() != 4 {
return Err("wrong number of arguments for 'setex' command".into());
}
let key = &parts[1];
let ttl: u64 = parts[2].parse().map_err(|_| "invalid expire time")?;
let value = &parts[3];
db.setex(key.clone(), value.as_bytes().to_vec(), ttl).await;
Ok("+OK\r\n".to_string())
}
"DEL" => {
if parts.len() < 2 {
return Err("wrong number of arguments for 'del' command".into());
}
let keys: Vec<String> = parts[1..].to_vec();
let count = db.del(&keys).await;
Ok(format!(":{}\r\n", count))
}
"EXISTS" => {
if parts.len() != 2 {
return Err("wrong number of arguments for 'exists' command".into());
}
let key = &parts[1];
if db.exists(key).await {
Ok(":1\r\n".to_string())
} else {
Ok(":0\r\n".to_string())
}
}
"KEYS" => {
let pattern = if parts.len() > 1 { &parts[1] } else { "*" };
let keys = db.keys(pattern).await;
if keys.is_empty() {
Ok("*0\r\n".to_string())
} else {
let mut response = format!("*{}\r\n", keys.len());
for key in keys {
response.push_str(&format!("${}\r\n{}\r\n", key.len(), key));
}
Ok(response)
}
}
"TTL" => {
if parts.len() != 2 {
return Err("wrong number of arguments for 'ttl' command".into());
}
let key = &parts[1];
let ttl = db.ttl(key).await;
Ok(format!(":{}\r\n", ttl))
}
"DBSIZE" => {
let size = db.dbsize().await;
Ok(format!(":{}\r\n", size))
}
"FLUSHDB" => {
db.flushdb().await;
Ok("+OK\r\n".to_string())
}
"INFO" => {
let info = format!(
"# Server\r\n\
redis_version:embedded-2.0\r\n\
redis_mode:standalone\r\n\
tcp_port:{}\r\n\
\r\n\
# Clients\r\n\
connected_clients:1\r\n\
\r\n\
# Memory\r\n\
used_memory:{}\r\n\
maxmemory:{}\r\n\
\r\n\
# Persistence\r\n\
rdb_last_save_time:{}\r\n\
\r\n\
# Stats\r\n\
total_connections_received:1\r\n\
total_commands_processed:1\r\n\
\r\n",
6379,
1024,
64 * 1024 * 1024,
SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_secs()
);
Ok(format!("${}\r\n{}\r\n", info.len(), info))
}
"QUIT" => {
Ok("+OK\r\n".to_string())
}
_ => {
Err(format!("unknown command '{}'", command).into())
}
}
}
/// 解析Redis协议简化版
fn parse_redis_protocol(line: &str) -> Result<Vec<String>, Box<dyn std::error::Error>> {
// 这里实现简化的Redis协议解析
// 实际应该解析RESP协议这里为了简化直接按空格分割
Ok(line.split_whitespace().map(|s| s.to_string()).collect())
}
/// 嵌入式Redis管理器
pub struct EmbeddedRedisManager {
server: Arc<Mutex<EmbeddedRedisServer>>,
}
impl EmbeddedRedisManager {
/// 创建新的管理器
pub fn new(config: EmbeddedRedisConfig) -> Self {
Self {
server: Arc::new(Mutex::new(EmbeddedRedisServer::new(config))),
}
}
/// 启动Redis服务器
pub async fn start(&self) -> Result<(), Box<dyn std::error::Error>> {
let mut server = self.server.lock().await;
server.start().await
}
/// 停止Redis服务器
pub async fn stop(&self) {
let mut server = self.server.lock().await;
server.stop().await;
}
/// 获取Redis连接URL
pub async fn get_connection_url(&self) -> String {
let server = self.server.lock().await;
server.get_connection_url()
}
/// 获取配置
pub async fn get_config(&self) -> EmbeddedRedisConfig {
let server = self.server.lock().await;
server.get_config().clone()
}
/// 检查是否运行中
pub async fn is_running(&self) -> bool {
let server = self.server.lock().await;
*server.running.lock().await
}
/// 获取数据库引用(供高级操作使用)
pub async fn get_db(&self) -> Arc<EmbeddedRedisDb> {
let server = self.server.lock().await;
server.get_db()
}
/// 手动保存到磁盘
pub async fn save(&self) -> Result<(), Box<dyn std::error::Error>> {
let server = self.server.lock().await;
server.get_db().save_to_disk().await
}
}
impl Drop for EmbeddedRedisManager {
fn drop(&mut self) {
// 在析构时尝试停止服务器并保存数据
let server = self.server.clone();
tokio::spawn(async move {
let mut server = server.lock().await;
server.stop().await;
});
}
}
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
async fn test_embedded_redis_config() {
let config = EmbeddedRedisConfig::default();
assert_eq!(config.port, 6379);
assert_eq!(config.max_memory, 64 * 1024 * 1024);
assert!(config.persistence);
}
#[tokio::test]
async fn test_embedded_redis_manager() {
let config = EmbeddedRedisConfig {
port: 16379, // 使用非标准端口避免冲突
data_dir: "./test_redis_data".to_string(),
..Default::default()
};
let manager = EmbeddedRedisManager::new(config);
assert_eq!(manager.get_connection_url().await, "redis://127.0.0.1:16379/");
}
#[tokio::test]
async fn test_redis_commands() {
let config = EmbeddedRedisConfig {
port: 16380,
data_dir: "./test_redis_commands".to_string(),
..Default::default()
};
let db = Arc::new(EmbeddedRedisDb::new(config));
// 测试SET和GET
db.set("test_key".to_string(), b"test_value".to_vec()).await;
let value = db.get("test_key").await;
assert_eq!(value, Some(b"test_value".to_vec()));
// 测试EXISTS
assert!(db.exists("test_key").await);
assert!(!db.exists("non_existent").await);
// 测试DEL
let deleted = db.del(&["test_key".to_string()]).await;
assert_eq!(deleted, 1);
assert!(!db.exists("test_key").await);
// 测试KEYS
db.set("key1".to_string(), b"value1".to_vec()).await;
db.set("key2".to_string(), b"value2".to_vec()).await;
let keys = db.keys("*").await;
assert!(keys.contains(&"key1".to_string()));
assert!(keys.contains(&"key2".to_string()));
// 清理测试数据
db.flushdb().await;
}
}

View File

@@ -0,0 +1,293 @@
use serde::{Deserialize, Serialize};
use std::fmt;
pub mod embedded_redis;
pub use embedded_redis::{EmbeddedRedisServer, EmbeddedRedisConfig, EmbeddedRedisManager};
/// 任务类型枚举
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub enum TaskType {
/// 文本处理任务
TextProcessing,
/// 数据分析任务
DataAnalysis,
/// AI对话任务
AIChat,
/// 文件处理任务
FileProcessing,
/// 自定义任务
Custom(String),
}
impl fmt::Display for TaskType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
TaskType::TextProcessing => write!(f, "text_processing"),
TaskType::DataAnalysis => write!(f, "data_analysis"),
TaskType::AIChat => write!(f, "ai_chat"),
TaskType::FileProcessing => write!(f, "file_processing"),
TaskType::Custom(s) => write!(f, "custom_{}", s),
}
}
}
/// 任务状态枚举
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub enum TaskStatus {
/// 待处理
Pending,
/// 处理中
Processing,
/// 已完成
Completed,
/// 失败
Failed,
/// 已取消
Cancelled,
}
impl fmt::Display for TaskStatus {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
TaskStatus::Pending => write!(f, "pending"),
TaskStatus::Processing => write!(f, "processing"),
TaskStatus::Completed => write!(f, "completed"),
TaskStatus::Failed => write!(f, "failed"),
TaskStatus::Cancelled => write!(f, "cancelled"),
}
}
}
/// 任务请求数据结构
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TaskRequest {
/// 用户ID
pub user_id: String,
/// 任务类型
pub task_type: TaskType,
/// 任务内容
pub content: String,
/// 时间戳
pub timestamp: i64,
/// 优先级 (1-10, 数字越大优先级越高)
pub priority: u8,
/// 超时时间(秒)
pub timeout: Option<u64>,
/// 额外参数
pub extra_params: Option<serde_json::Value>,
}
/// 任务响应数据结构
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TaskResponse {
/// 是否成功
pub success: bool,
/// 消息
pub message: String,
/// 任务ID
pub task_id: Option<String>,
/// 结果数据
pub result: Option<serde_json::Value>,
/// 处理时间(毫秒)
pub processing_time: Option<u64>,
/// 错误信息
pub error: Option<String>,
}
/// 任务状态数据结构
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TaskStatusResponse {
/// 任务ID
pub task_id: String,
/// 任务状态
pub status: TaskStatus,
/// 进度 (0-100)
pub progress: u8,
/// 状态消息
pub status_message: String,
/// 创建时间
pub created_at: i64,
/// 更新时间
pub updated_at: i64,
/// 结果数据
pub result: Option<serde_json::Value>,
}
/// 服务健康检查响应
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct HealthResponse {
/// 服务状态
pub status: String,
/// 服务名称
pub service: String,
/// 时间戳
pub timestamp: i64,
/// 版本号
pub version: String,
/// 额外信息
pub extra: Option<serde_json::Value>,
}
/// 错误响应数据结构
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ErrorResponse {
/// 错误代码
pub code: String,
/// 错误消息
pub message: String,
/// 详细错误信息
pub details: Option<String>,
/// 时间戳
pub timestamp: i64,
}
/// WebSocket 消息类型
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum WebSocketMessage {
/// 任务请求
TaskRequest(TaskRequest),
/// 任务响应
TaskResponse(TaskResponse),
/// 任务状态更新
TaskStatusUpdate(TaskStatusResponse),
/// 心跳消息
Heartbeat,
/// 错误消息
Error(ErrorResponse),
}
/// 企业微信消息数据结构
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct WeChatMessage {
/// 消息ID
pub msg_id: String,
/// 发送者
pub from_user: String,
/// 接收者
pub to_user: String,
/// 消息类型
pub msg_type: String,
/// 消息内容
pub content: String,
/// 时间戳
pub timestamp: i64,
}
/// 微信小程序消息数据结构
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MiniProgramMessage {
/// 用户ID
pub user_id: String,
/// 消息类型
pub msg_type: String,
/// 消息内容
pub content: String,
/// 时间戳
pub timestamp: i64,
/// 额外参数
pub extra: Option<serde_json::Value>,
}
/// 常量定义
pub mod constants {
/// 默认任务超时时间(秒)
pub const DEFAULT_TASK_TIMEOUT: u64 = 300;
/// 最大任务优先级
pub const MAX_TASK_PRIORITY: u8 = 10;
/// 最小任务优先级
pub const MIN_TASK_PRIORITY: u8 = 1;
/// 默认服务端口
pub const DEFAULT_GATEWAY_PORT: u16 = 3000;
pub const DEFAULT_SMARTCLAW_PORT: u16 = 3001;
/// WebSocket 心跳间隔(秒)
pub const WEBSOCKET_HEARTBEAT_INTERVAL: u64 = 30;
/// WebSocket 超时时间(秒)
pub const WEBSOCKET_TIMEOUT: u64 = 300;
}
/// 工具函数
pub mod utils {
use super::*;
use chrono::Utc;
/// 生成任务ID
pub fn generate_task_id(user_id: &str) -> String {
format!("task_{}_{}", user_id, Utc::now().timestamp_millis())
}
/// 生成消息ID
pub fn generate_msg_id() -> String {
format!("msg_{}", Utc::now().timestamp_millis())
}
/// 验证任务优先级
pub fn validate_priority(priority: u8) -> u8 {
priority.clamp(constants::MIN_TASK_PRIORITY, constants::MAX_TASK_PRIORITY)
}
/// 获取当前时间戳
pub fn current_timestamp() -> i64 {
Utc::now().timestamp()
}
/// 创建成功响应
pub fn create_success_response(message: &str, task_id: Option<String>, result: Option<serde_json::Value>) -> TaskResponse {
TaskResponse {
success: true,
message: message.to_string(),
task_id,
result,
processing_time: None,
error: None,
}
}
/// 创建错误响应
pub fn create_error_response(message: &str, error: Option<String>) -> TaskResponse {
TaskResponse {
success: false,
message: message.to_string(),
task_id: None,
result: None,
processing_time: None,
error,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_task_type_display() {
assert_eq!(TaskType::TextProcessing.to_string(), "text_processing");
assert_eq!(TaskType::DataAnalysis.to_string(), "data_analysis");
assert_eq!(TaskType::Custom("test".to_string()).to_string(), "custom_test");
}
#[test]
fn test_task_status_display() {
assert_eq!(TaskStatus::Pending.to_string(), "pending");
assert_eq!(TaskStatus::Completed.to_string(), "completed");
assert_eq!(TaskStatus::Failed.to_string(), "failed");
}
#[test]
fn test_generate_task_id() {
let task_id = utils::generate_task_id("user123");
assert!(task_id.starts_with("task_user123_"));
}
#[test]
fn test_validate_priority() {
assert_eq!(utils::validate_priority(0), 1);
assert_eq!(utils::validate_priority(5), 5);
assert_eq!(utils::validate_priority(15), 10);
}
}