refactor(saas): 架构重构 + 性能优化 — 借鉴 loco-rs 模式
Phase 0: 知识库
- docs/knowledge-base/loco-rs-patterns.md — loco-rs 10 个可借鉴模式研究
Phase 1: 数据层重构
- crates/zclaw-saas/src/models/ — 15 个 FromRow 类型化模型
- Login 3 次查询合并为 1 次 AccountLoginRow 查询
- 所有 service 文件从元组解构迁移到 FromRow 结构体
Phase 2: Worker + Scheduler 系统
- crates/zclaw-saas/src/workers/ — Worker trait + 5 个具体实现
- crates/zclaw-saas/src/scheduler.rs — TOML 声明式调度器
- crates/zclaw-saas/src/tasks/ — CLI 任务系统
Phase 3: 性能修复
- Relay N+1 查询 → 精准 SQL (relay/handlers.rs)
- Config RwLock → AtomicU32 无锁 rate limit (state.rs, middleware.rs)
- SSE std::sync::Mutex → tokio::sync::Mutex (relay/service.rs)
- /auth/refresh 阻塞清理 → Scheduler 定期执行
Phase 4: 多环境配置
- config/saas-{development,production,test}.toml
- ZCLAW_ENV 环境选择 + ZCLAW_SAAS_CONFIG 精确覆盖
- scheduler 配置集成到 TOML
This commit is contained in:
@@ -14,6 +14,37 @@ pub struct SaaSConfig {
|
||||
pub relay: RelayConfig,
|
||||
#[serde(default)]
|
||||
pub rate_limit: RateLimitConfig,
|
||||
#[serde(default)]
|
||||
pub scheduler: SchedulerConfig,
|
||||
}
|
||||
|
||||
/// Scheduler 定时任务配置
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct SchedulerConfig {
|
||||
#[serde(default)]
|
||||
pub jobs: Vec<JobConfig>,
|
||||
}
|
||||
|
||||
/// 单个定时任务配置
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct JobConfig {
|
||||
pub name: String,
|
||||
/// 间隔时间,支持 "5m", "1h", "24h", "30s" 格式
|
||||
pub interval: String,
|
||||
/// 对应的 Worker 名称
|
||||
pub task: String,
|
||||
/// 传递给 Worker 的参数(JSON 格式)
|
||||
#[serde(default)]
|
||||
pub args: Option<serde_json::Value>,
|
||||
/// 是否在启动时立即执行
|
||||
#[serde(default)]
|
||||
pub run_on_start: bool,
|
||||
}
|
||||
|
||||
impl Default for SchedulerConfig {
|
||||
fn default() -> Self {
|
||||
Self { jobs: Vec::new() }
|
||||
}
|
||||
}
|
||||
|
||||
/// 服务器配置
|
||||
@@ -51,8 +82,10 @@ pub struct AuthConfig {
|
||||
pub struct RelayConfig {
|
||||
#[serde(default = "default_max_queue")]
|
||||
pub max_queue_size: usize,
|
||||
// TODO: implement per-provider concurrency limiting
|
||||
#[serde(default = "default_max_concurrent")]
|
||||
pub max_concurrent_per_provider: usize,
|
||||
// TODO: implement batch window
|
||||
#[serde(default = "default_batch_window")]
|
||||
pub batch_window_ms: u64,
|
||||
#[serde(default = "default_retry_delay")]
|
||||
@@ -104,6 +137,7 @@ impl Default for SaaSConfig {
|
||||
auth: AuthConfig::default(),
|
||||
relay: RelayConfig::default(),
|
||||
rate_limit: RateLimitConfig::default(),
|
||||
scheduler: SchedulerConfig::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -147,11 +181,31 @@ impl Default for RelayConfig {
|
||||
}
|
||||
|
||||
impl SaaSConfig {
|
||||
/// 加载配置文件,优先级: 环境变量 > ZCLAW_SAAS_CONFIG > ./saas-config.toml
|
||||
/// 加载配置文件,优先级: ZCLAW_SAAS_CONFIG > ZCLAW_ENV > ./saas-config.toml
|
||||
///
|
||||
/// ZCLAW_ENV 环境选择:
|
||||
/// development → config/saas-development.toml
|
||||
/// production → config/saas-production.toml
|
||||
/// test → config/saas-test.toml
|
||||
///
|
||||
/// ZCLAW_SAAS_CONFIG 指定精确路径(最高优先级)
|
||||
pub fn load() -> anyhow::Result<Self> {
|
||||
let config_path = std::env::var("ZCLAW_SAAS_CONFIG")
|
||||
.map(PathBuf::from)
|
||||
.unwrap_or_else(|_| PathBuf::from("saas-config.toml"));
|
||||
let config_path = if let Ok(path) = std::env::var("ZCLAW_SAAS_CONFIG") {
|
||||
PathBuf::from(path)
|
||||
} else if let Ok(env) = std::env::var("ZCLAW_ENV") {
|
||||
let filename = format!("config/saas-{}.toml", env);
|
||||
let path = PathBuf::from(&filename);
|
||||
if !path.exists() {
|
||||
anyhow::bail!(
|
||||
"ZCLAW_ENV={} 指定的配置文件 {} 不存在",
|
||||
env, filename
|
||||
);
|
||||
}
|
||||
tracing::info!("Loading config for environment: {}", env);
|
||||
path
|
||||
} else {
|
||||
PathBuf::from("saas-config.toml")
|
||||
};
|
||||
|
||||
let mut config = if config_path.exists() {
|
||||
let content = std::fs::read_to_string(&config_path)?;
|
||||
|
||||
Reference in New Issue
Block a user