Files
zclaw_openfang/docs/superpowers/specs/2026-03-27-saas-backend-design.md
iven 44256a511c feat: 增强SaaS后端功能与安全性
refactor: 重构数据库连接使用PostgreSQL替代SQLite
feat(auth): 增加JWT验证的audience和issuer检查
feat(crypto): 添加AES-256-GCM字段加密支持
feat(api): 集成utoipa实现OpenAPI文档
fix(admin): 修复配置项表单验证逻辑
style: 统一代码格式与类型定义
docs: 更新技术栈文档说明PostgreSQL
2026-03-31 00:12:53 +08:00

30 KiB
Raw Blame History

ZCLAW SaaS 后台系统设计规格

日期: 2026-03-27 状态: 待审阅 范围: 独立 SaaS 后端服务 (Rust + Axum) + 独立 Web 管理后台 (React + TypeScript)

1. 概述

1.1 问题陈述

ZCLAW 当前是纯桌面单用户应用缺少用户账号系统、API 服务端、多租户支持和请求中转能力。所有 LLM 调用直接从桌面端发起API 密钥存储在本地环境变量或 OS Keyring模型配置散落在 TOML 文件和 localStorage 中。这限制了多用户协作、集中管控和资源优化。

1.2 设计目标

  1. 为 ZCLAW 添加独立 SaaS 后端,提供账号权限、模型配置、请求中转、配置迁移四大能力
  2. 桌面端通过简单配置切换即可接入 SaaSbase_url + 添加 API Token
  3. MVP 阶段支持 <100 用户,后续可扩展至更大规模

1.3 用户决策

决策点 选择
部署形态 独立 SaaS 后端服务
技术栈 Rust + Axum
实施顺序 按依赖顺序: 账号→API→中转→迁移
管理界面 独立 React Web 管理后台
用户规模 MVP <100 用户
支持模型 智谱 GLM, 通义千问, Kimi, DeepSeek
代码位置 同仓库 workspace 成员 crates/zclaw-saas/

2. 架构

2.1 系统架构图

┌─────────────────────┐     ┌─────────────────────────────────────────┐
│  ZCLAW 桌面客户端     │     │         SaaS 后端服务                    │
│  (Tauri + React)    │     │         (Rust + Axum)                   │
│                     │     │                                         │
│  OpenAiDriver ──────┼────►│  /api/v1/relay/chat/completions        │
│  (改 base_url)      │     │    → 权限检查 → 队列 → 转发 → 流式响应    │
│                     │     │                                         │
│  SaaSConfigClient ──┼────►│  /api/v1/config/sync                   │
│  (配置同步)          │     │    → 配置覆盖/冲突检测                   │
│                     │     │                                         │
│  Model Picker ──────┼────►│  /api/v1/catalog/models                │
│  (模型目录)          │     │    → 动态模型列表                       │
└─────────────────────┘     └─────────────────────────────────────────┘
                                    │
                                    ▼
                            ┌───────────────┐
                            │  PostgreSQL    │
                            │  zclaw 数据库   │
                            └───────────────┘

┌──────────────────────────────────────────────────────┐
│              管理后台 (React + TypeScript)              │
│  /admin/accounts  /admin/providers  /admin/relay      │
│  /admin/roles     /admin/usage      /admin/migration  │
└──────────────────────────────────────────────────────┘

2.2 Crate 依赖

zclaw-types          (无依赖,复用 ID/错误/消息类型)
    ↑
zclaw-saas           (→ types + axum/sqlx/argon2/jsonwebtoken)
    ↑ (通过 API)
desktop/             (→ 通过 HTTP API 调用 SaaS)
saas-admin/          (→ 通过 HTTP API 调用 SaaS)

2.3 文件结构

crates/zclaw-saas/
  Cargo.toml
  src/
    lib.rs                  # 模块声明
    main.rs                 # 入口
    error.rs                # SaaS 错误类型
    config.rs               # SaaS 服务器配置 (TOML)
    db.rs                   # 数据库初始化 + 迁移
    state.rs                # AppState
    auth/
      mod.rs
      jwt.rs                # JWT 创建/验证
      password.rs           # Argon2 哈希
      totp.rs               # TOTP 2FA
      middleware.rs          # Axum 认证中间件
    account/
      mod.rs
      types.rs              # Account/Role/Permission 类型
      handlers.rs           # HTTP 处理器
      service.rs            # 业务逻辑
    model_config/
      mod.rs
      types.rs              # Provider/Model/APIKey 类型
      handlers.rs           # HTTP 处理器
      service.rs            # 业务逻辑
    relay/
      mod.rs
      types.rs              # 请求/响应/队列类型
      handlers.rs           # HTTP 处理器 (代理端点)
      service.rs            # 队列管理、调度、批处理
      provider_impl.rs      # 提供商特定转发逻辑
    migration/
      mod.rs
      types.rs              # ConfigItem/SyncRecord 类型
      handlers.rs           # HTTP 处理器
      service.rs            # 配置分析和同步逻辑

saas-admin/                   # 独立 React 管理后台
  package.json
  src/
    App.tsx
    pages/
      Login.tsx
      Dashboard.tsx
      accounts/
      providers/
      api-keys/
      usage/
      relay/
      migration/
      roles/
      logs/

3. 数据库设计

3.1 概述

  • 引擎: PostgreSQL 16
  • 连接: 通过 DATABASE_URL 环境变量配置 (推荐) 或 saas-config.toml 中指定
  • 迁移: 版本化 schema启动时自动迁移

3.2 完整 Schema

-- Schema 版本控制
CREATE TABLE IF NOT EXISTS saas_schema_version (
    version INTEGER PRIMARY KEY
);

-- ============================================================
-- 模块一: 账号权限
-- ============================================================

CREATE TABLE IF NOT EXISTS accounts (
    id TEXT PRIMARY KEY,              -- UUID v4
    username TEXT NOT NULL UNIQUE,
    email TEXT NOT NULL UNIQUE,
    password_hash TEXT NOT NULL,       -- Argon2id
    display_name TEXT NOT NULL DEFAULT '',
    avatar_url TEXT,
    role TEXT NOT NULL DEFAULT 'user', -- 'super_admin' | 'admin' | 'user'
    status TEXT NOT NULL DEFAULT 'active', -- 'active' | 'disabled' | 'suspended'
    totp_secret TEXT,                 -- 加密存储
    totp_enabled BOOLEAN NOT NULL DEFAULT false,
    last_login_at TIMESTAMPTZ,
    created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE INDEX IF NOT EXISTS idx_accounts_email ON accounts(email);
CREATE INDEX IF NOT EXISTS idx_accounts_role ON accounts(role);

CREATE TABLE IF NOT EXISTS api_tokens (
    id TEXT PRIMARY KEY,              -- UUID
    account_id TEXT NOT NULL,
    name TEXT NOT NULL,               -- e.g. "桌面客户端"
    token_hash TEXT NOT NULL,         -- SHA256(token)
    token_prefix TEXT NOT NULL,       -- 前 8 字符用于展示
    permissions TEXT NOT NULL DEFAULT '[]', -- JSON 权限数组
    last_used_at TIMESTAMPTZ,
    expires_at TIMESTAMPTZ,                  -- NULL = 永不过期
    created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    revoked_at TIMESTAMPTZ,
    FOREIGN KEY (account_id) REFERENCES accounts(id) ON DELETE CASCADE
);
CREATE INDEX IF NOT EXISTS idx_api_tokens_account ON api_tokens(account_id);
CREATE INDEX IF NOT EXISTS idx_api_tokens_hash ON api_tokens(token_hash);

CREATE TABLE IF NOT EXISTS roles (
    id TEXT PRIMARY KEY,              -- 'super_admin' | 'admin' | 'user' | 自定义 UUID
    name TEXT NOT NULL,               -- 显示名称 (中文)
    description TEXT,
    permissions TEXT NOT NULL DEFAULT '[]', -- JSON 权限数组
    is_system BOOLEAN NOT NULL DEFAULT false,  -- 系统角色不可删除
    created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);

CREATE TABLE IF NOT EXISTS permission_templates (
    id TEXT PRIMARY KEY,              -- UUID
    name TEXT NOT NULL,               -- e.g. "标准用户", "只读用户"
    description TEXT,
    permissions TEXT NOT NULL DEFAULT '[]', -- JSON 权限数组
    created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);

CREATE TABLE IF NOT EXISTS operation_logs (
    id BIGSERIAL PRIMARY KEY,
    account_id TEXT,                  -- NULL = 系统操作
    action TEXT NOT NULL,             -- e.g. "account.create", "model.update"
    target_type TEXT,                 -- e.g. "account", "api_key", "model"
    target_id TEXT,
    details TEXT,                     -- JSON 详情
    ip_address TEXT,
    created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE INDEX IF NOT EXISTS idx_op_logs_account ON operation_logs(account_id);
CREATE INDEX IF NOT EXISTS idx_op_logs_action ON operation_logs(action);
CREATE INDEX IF NOT EXISTS idx_op_logs_time ON operation_logs(created_at);

-- ============================================================
-- 模块二: 模型配置
-- ============================================================

CREATE TABLE IF NOT EXISTS providers (
    id TEXT PRIMARY KEY,              -- UUID
    name TEXT NOT NULL UNIQUE,        -- e.g. 'zhipu', 'qwen', 'kimi', 'deepseek'
    display_name TEXT NOT NULL,       -- e.g. '智谱 AI'
    api_key TEXT,                     -- 服务端提供商 API 密钥 (加密存储)
    base_url TEXT NOT NULL,
    api_protocol TEXT NOT NULL DEFAULT 'openai', -- 'openai' | 'anthropic'
    enabled BOOLEAN NOT NULL DEFAULT true,
    rate_limit_rpm INTEGER,           -- 每分钟请求数
    rate_limit_tpm INTEGER,           -- 每分钟 token 数
    config_json TEXT DEFAULT '{}',    -- 提供商特定配置
    created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);

CREATE TABLE IF NOT EXISTS models (
    id TEXT PRIMARY KEY,              -- UUID
    provider_id TEXT NOT NULL,
    model_id TEXT NOT NULL,           -- 传给 API 的模型标识
    alias TEXT NOT NULL,              -- 显示名称
    context_window INTEGER NOT NULL DEFAULT 8192,
    max_output_tokens INTEGER NOT NULL DEFAULT 4096,
    supports_streaming BOOLEAN NOT NULL DEFAULT true,
    supports_vision BOOLEAN NOT NULL DEFAULT false,
    enabled BOOLEAN NOT NULL DEFAULT true,
    pricing_input DOUBLE PRECISION DEFAULT 0,     -- 每 1K token 价格
    pricing_output DOUBLE PRECISION DEFAULT 0,
    created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    UNIQUE(provider_id, model_id),
    FOREIGN KEY (provider_id) REFERENCES providers(id) ON DELETE CASCADE
);
CREATE INDEX IF NOT EXISTS idx_models_provider ON models(provider_id);

CREATE TABLE IF NOT EXISTS account_api_keys (
    id TEXT PRIMARY KEY,              -- UUID
    account_id TEXT NOT NULL,
    provider_id TEXT NOT NULL,
    key_value TEXT NOT NULL,          -- API 密钥 (加密存储)
    key_label TEXT,                   -- e.g. "主密钥", "备用密钥"
    permissions TEXT NOT NULL DEFAULT '[]', -- JSON: 可访问的模型 ID 列表
    enabled BOOLEAN NOT NULL DEFAULT true,
    last_used_at TIMESTAMPTZ,
    created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    revoked_at TIMESTAMPTZ,
    FOREIGN KEY (account_id) REFERENCES accounts(id) ON DELETE CASCADE,
    FOREIGN KEY (provider_id) REFERENCES providers(id) ON DELETE CASCADE
);
CREATE INDEX IF NOT EXISTS idx_account_api_keys_account ON account_api_keys(account_id);

CREATE TABLE IF NOT EXISTS usage_records (
    id BIGSERIAL PRIMARY KEY,
    account_id TEXT NOT NULL,
    provider_id TEXT NOT NULL,
    model_id TEXT NOT NULL,
    input_tokens INTEGER NOT NULL DEFAULT 0,
    output_tokens INTEGER NOT NULL DEFAULT 0,
    latency_ms INTEGER,
    status TEXT NOT NULL DEFAULT 'success', -- 'success' | 'error' | 'rate_limited'
    error_message TEXT,
    created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE INDEX IF NOT EXISTS idx_usage_account ON usage_records(account_id);
CREATE INDEX IF NOT EXISTS idx_usage_time ON usage_records(created_at);

-- ============================================================
-- 模块三: 中转服务
-- ============================================================

CREATE TABLE IF NOT EXISTS relay_tasks (
    id TEXT PRIMARY KEY,              -- UUID
    account_id TEXT NOT NULL,
    provider_id TEXT NOT NULL,
    model_id TEXT NOT NULL,
    request_hash TEXT NOT NULL,       -- 用于去重
    status TEXT NOT NULL DEFAULT 'queued', -- 'queued' | 'processing' | 'completed' | 'failed'
    priority INTEGER NOT NULL DEFAULT 0,
    attempt_count INTEGER NOT NULL DEFAULT 0,
    max_attempts INTEGER NOT NULL DEFAULT 3,
    request_body TEXT NOT NULL,       -- JSON: 转发的请求体
    response_body TEXT,               -- JSON: 响应体
    input_tokens INTEGER DEFAULT 0,
    output_tokens INTEGER DEFAULT 0,
    error_message TEXT,
    queued_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    started_at TIMESTAMPTZ,
    completed_at TIMESTAMPTZ,
    created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE INDEX IF NOT EXISTS idx_relay_status ON relay_tasks(status);
CREATE INDEX IF NOT EXISTS idx_relay_account ON relay_tasks(account_id);
CREATE INDEX IF NOT EXISTS idx_relay_provider ON relay_tasks(provider_id);

-- ============================================================
-- 模块四: 配置迁移
-- ============================================================

CREATE TABLE IF NOT EXISTS config_items (
    id TEXT PRIMARY KEY,              -- UUID
    category TEXT NOT NULL,           -- 'llm' | 'agent' | 'security' | 'hands' | 'skills' | 'tools' | 'logging' | 'desktop' | 'server'
    key_path TEXT NOT NULL,           -- e.g. 'llm.default_provider'
    value_type TEXT NOT NULL,         -- 'string' | 'integer' | 'float' | 'boolean' | 'array' | 'object'
    current_value TEXT,               -- JSON 编码的当前值
    default_value TEXT,               -- JSON 编码的默认值
    source TEXT NOT NULL DEFAULT 'local', -- 'local' | 'saas' | 'override'
    description TEXT,                 -- 中文描述
    requires_restart BOOLEAN NOT NULL DEFAULT false,
    created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    UNIQUE(category, key_path)
);
CREATE INDEX IF NOT EXISTS idx_config_category ON config_items(category);

CREATE TABLE IF NOT EXISTS config_sync_log (
    id BIGSERIAL PRIMARY KEY,
    account_id TEXT NOT NULL,
    client_fingerprint TEXT NOT NULL,
    action TEXT NOT NULL,             -- 'push' | 'pull' | 'conflict'
    config_keys TEXT NOT NULL,        -- JSON: 受影响的配置键
    client_values TEXT,               -- JSON: 客户端值
    saas_values TEXT,                 -- JSON: SaaS 值
    resolution TEXT,                  -- 'client_wins' | 'saas_wins' | 'manual'
    created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE INDEX IF NOT EXISTS idx_sync_account ON config_sync_log(account_id);

4. 模块一: 账号权限管理

4.1 认证机制

机制 实现 用途
密码 Argon2id (argon2 crate) 账号登录
JWT HMAC-SHA256 (jsonwebtoken crate) 管理后台会话
TOTP 2FA totp-rs crate 可选双因素认证
API Token SHA-256 哈希存储 桌面客户端/程序化访问

JWT Payload: { sub: account_id, role: "admin", exp: timestamp } API Token 格式: zclaw_<48 random bytes>,仅创建时展示一次

4.2 认证中间件

// auth/middleware.rs
pub struct AuthContext {
    pub account_id: Uuid,
    pub role: AccountRole,
    pub permissions: Vec<String>,
    pub auth_method: AuthMethod,  // Jwt | ApiToken
}

// 中间件从 Authorization: Bearer <jwt> 或 X-API-Key: <token> 提取身份
// 将 AuthContext 注入 request extensions

4.3 角色与权限

权限使用字符串常量,检查时匹配字符串列表:

pub mod permissions {
    pub const ACCOUNT_READ: &str = "account:read";
    pub const ACCOUNT_WRITE: &str = "account:write";
    pub const ACCOUNT_ADMIN: &str = "account:admin";
    pub const MODEL_READ: &str = "model:read";
    pub const MODEL_WRITE: &str = "model:write";
    pub const RELAY_USE: &str = "relay:use";
    pub const RELAY_ADMIN: &str = "relay:admin";
    pub const CONFIG_READ: &str = "config:read";
    pub const CONFIG_WRITE: &str = "config:write";
    pub const ADMIN_FULL: &str = "admin:full";
}
角色 权限
super_admin admin:full (授权检查时直接放行)
admin account:read/write, model:read/write, relay:use/admin, config:read/write
user model:read, relay:use, config:read

4.4 API 端点

方法 路径 权限 说明
POST /api/v1/auth/register admin:full 创建账号
POST /api/v1/auth/login 公开 登录返回 JWT
POST /api/v1/auth/refresh JWT 刷新 JWT
POST /api/v1/auth/totp/setup JWT 获取 TOTP 设置 URI
POST /api/v1/auth/totp/verify JWT 验证并启用 TOTP
POST /api/v1/auth/totp/disable JWT 禁用 TOTP
GET /api/v1/accounts account:read 账号列表
GET /api/v1/accounts/:id account:read 账号详情
PUT /api/v1/accounts/:id account:write 更新账号
PATCH /api/v1/accounts/:id/status account:admin 启用/禁用
GET /api/v1/tokens - (本人) 列出 API 令牌
POST /api/v1/tokens - (本人) 创建令牌
DELETE /api/v1/tokens/:id - (本人) 撤销令牌
GET /api/v1/roles account:read 角色列表
POST /api/v1/roles account:admin 创建自定义角色
PUT /api/v1/roles/:id account:admin 更新角色权限
DELETE /api/v1/roles/:id account:admin 删除自定义角色
GET /api/v1/permission-templates account:read 模板列表
POST /api/v1/permission-templates account:admin 创建模板
POST /api/v1/permission-templates/:id/apply account:admin 批量应用
GET /api/v1/logs/operations account:read 操作日志 (分页)

4.5 关键数据结构

pub struct Account {
    pub id: Uuid,
    pub username: String,
    pub email: String,
    pub password_hash: String,       // skip_serializing
    pub display_name: String,
    pub avatar_url: Option<String>,
    pub role: AccountRole,
    pub status: AccountStatus,
    pub totp_enabled: bool,
    pub last_login_at: Option<DateTime<Utc>>,
    pub created_at: DateTime<Utc>,
    pub updated_at: DateTime<Utc>,
}

pub enum AccountRole { SuperAdmin, Admin, User }
pub enum AccountStatus { Active, Disabled, Suspended }

5. 模块二: 模型与 API 配置中心

5.1 提供商管理

提供商存储服务端 API 密钥 (加密) 和基础配置。管理员创建提供商后,可为每个提供商添加多个模型。

API 密钥加密: AES-256-GCM密钥通过 HKDF-SHA256 从服务器 JWT 密钥派生。

5.2 账号级 API 密钥

每个账号可以为每个提供商配置独立的 API 密钥。密钥权限 (JSON 数组) 控制该密钥可访问的模型 ID 列表。支持:

  • 生成: 创建新密钥,返回明文 (仅一次)
  • 轮换: 生成新密钥替换旧密钥
  • 禁用: 临时禁用
  • 撤销: 永久删除

5.3 使用量统计

每次中转请求完成后记录 usage_records。统计查询使用 SQL 聚合MVP 阶段不需要预计算视图。

5.4 API 端点

方法 路径 权限 说明
GET /api/v1/providers model:read 提供商列表
POST /api/v1/providers model:write 创建提供商
GET /api/v1/providers/:id model:read 提供商详情
PUT /api/v1/providers/:id model:write 更新提供商
PATCH /api/v1/providers/:id/status model:write 启用/禁用
GET /api/v1/providers/:id/models model:read 模型列表
POST /api/v1/providers/:id/models model:write 添加模型
PUT /api/v1/models/:id model:write 更新模型
PATCH /api/v1/models/:id/status model:write 启用/禁用模型
GET /api/v1/account/api-keys - (本人) 密钥列表
POST /api/v1/account/api-keys - (本人) 创建密钥
PUT /api/v1/account/api-keys/:id - (本人) 更新权限/标签
POST /api/v1/account/api-keys/:id/rotate - (本人) 轮换密钥
PATCH /api/v1/account/api-keys/:id/status - (本人) 启用/禁用
DELETE /api/v1/account/api-keys/:id - (本人) 撤销密钥
GET /api/v1/usage/stats relay:admin 总体统计
GET /api/v1/usage/daily - (本人) 每日统计
GET /api/v1/usage/account/:id relay:admin 账号统计
GET /api/v1/catalog/models model:read 公开模型目录

5.5 初始种子数据

config/chinese-providers.toml 导入:

提供商 模型
智谱 AI GLM-4-Plus, GLM-4-Flash, GLM-4V-Plus, GLM-Z1-AirX
通义千问 Qwen-Max, Qwen-Plus, Qwen-Turbo, Qwen-VL-Max
Kimi moonshot-v1-8k, moonshot-v1-32k, moonshot-v1-128k
DeepSeek deepseek-chat, deepseek-reasoner

6. 模块三: 模型请求中转服务

6.1 请求流

桌面客户端
  POST /api/v1/relay/chat/completions
  Headers: Authorization: Bearer <api_token> 或 X-API-Key: <token>
  Body: { model: "glm-4-plus", messages: [...], stream: true }
    ↓
认证中间件 → 提取 account_id + permissions
    ↓
权限检查 → relay:use
    ↓
提供商解析 → models 表查找 provider_id → providers 表获取 base_url + api_protocol
    ↓
API 密钥解析 → account_api_keys 表获取该账号的提供商密钥
    ↓
速率限制检查 → 令牌桶算法
    ↓
队列调度 → tokio::sync::mpsc (如并发已满则排队)
    ↓
转发请求 → reqwest::Client POST 到上游提供商
    ↓
流式响应 → SSE 转发给客户端 (stream: true)
    ↓
记录使用量 → usage_records 表

6.2 队列与调度

pub struct RelayService {
    queues: Arc<DashMap<Uuid, mpsc::Sender<RelayTask>>>,      // 每提供商独立队列
    semaphores: Arc<DashMap<Uuid, Arc<Semaphore>>>,            // 并发控制
    clients: Arc<DashMap<Uuid, reqwest::Client>>,              // 每提供商 HTTP 客户端
    config: RelayConfig,
}
  • 每提供商一个 mpsc channel (容量 100)
  • 每提供商一个 Semaphore 控制并发 (默认 5)
  • Worker task 从 channel 拉取请求并执行

6.3 速率限制

令牌桶算法:

  • RPM (每分钟请求数): tokio::sync::Semaphore + 定时释放
  • TPM (每分钟 token 数): 基于最近一分钟 usage_records 的 SQL 查询

6.4 错误处理与重试

  • 瞬态错误 (超时, 5xx): 指数退避重试 (1s, 2s, 4s),最多 3 次
  • 客户端错误 (4xx, 认证失败): 立即返回,不重试
  • 提供商不可用: 标记 provider disabled通知管理员

6.5 API 端点

方法 路径 权限 说明
POST /api/v1/relay/chat/completions relay:use 中转聊天补全 (支持流式)
GET /api/v1/relay/status relay:admin 队列状态/提供商健康
GET /api/v1/relay/tasks relay:admin 任务列表
GET /api/v1/relay/tasks/:id relay:admin 任务详情
DELETE /api/v1/relay/tasks/:id relay:admin 取消排队任务
POST /api/v1/relay/retry/:id relay:admin 重试失败任务

6.6 桌面端集成

桌面端 OpenAiDriverbase_url 指向 SaaS 中转:

原来: base_url = "https://open.bigmodel.cn/api/paas/v4"
改为: base_url = "https://saas.zclaw.example.com/api/v1/relay"
添加: X-API-Key: zclaw_xxxxx (账号的 API Token)

中转保持 OpenAI 兼容格式,桌面端无需任何协议适配。


7. 模块四: 系统配置迁移分析

7.1 迁移优先级分类

基于 config/config.tomlconfig/chinese-providers.tomlconfig/security.toml 分析:

Critical (必须迁移):

  • llm.providers[].api_key — API 密钥应服务端管理
  • llm.providers[].base_url — 提供商端点集中管控
  • llm.providers[].models[] — 模型目录从 SaaS 下发
  • llm.default_provider — 默认提供商可按账号配置
  • llm.default_model — 默认模型可按账号配置

High (重要):

  • llm.requests_per_minute / llm.tokens_per_minute — 按账号限流
  • llm.max_retries / llm.retry_delay — 重试策略
  • security.auth.token_expiration — 认证配置
  • security.rate_limit.* — 限流配置

Medium (有益):

  • agent.defaults.max_sessions — 会话限制
  • hands.default_approval_mode — 默认审批模式
  • skills.execution_timeout — 技能超时

Low (可选):

  • desktop.ui.* — UI 偏好
  • logging.level — 日志级别

Local Only (不迁移):

  • server.host/port — 服务端特有
  • agent.defaults.workspace — 本地文件路径
  • security.shell_exec.* — 本地安全策略
  • tools.fs.allowed_paths — 本地文件路径
  • logging.file.path — 本地文件路径

7.2 同步协议

桌面客户端                          SaaS 服务端
    │                                  │
    │  POST /api/v1/config/sync        │
    │  { action: "push",               │
    │    fingerprint: "machine_xxx",   │
    │    snapshot: { ... } }           │
    │ ──────────────────────────────►  │
    │                                  │ 比对 SaaS 存储
    │  { updates: [...],               │
    │    conflicts: [...] }            │
    │ ◄──────────────────────────────  │
    │                                  │
    │  应用更新 / 提示冲突             │
    │                                  │

冲突解决策略:

  • source: "saas" / "override" → 默认 saas_wins
  • source: "local" → 默认 client_wins
  • 管理员可按配置项覆盖

7.3 API 端点

方法 路径 权限 说明
GET /api/v1/migration/report config:read 迁移分析报告
POST /api/v1/migration/analyze config:read 分析配置快照
GET /api/v1/migration/items config:read 配置项列表
PUT /api/v1/migration/items/:id config:write 更新配置项
PUT /api/v1/migration/items/batch config:write 批量更新
POST /api/v1/config/sync - (本人) 客户端↔SaaS 同步
GET /api/v1/config/snapshot config:read SaaS 配置快照
GET /api/v1/config/diff config:read 客户端与 SaaS 差异

8. 管理后台前端

8.1 技术栈

React 18 + TypeScript + Tailwind CSS + Recharts + React Router

8.2 页面清单

路径 模块 说明
/admin/login 通用 登录页 (JWT 认证)
/admin/dashboard 通用 概览仪表板 (账号数/使用量/中转状态)
/admin/accounts M1 账号列表 (搜索/角色筛选/状态筛选)
/admin/accounts/:id M1 账号详情 (编辑/查看令牌/查看日志)
/admin/roles M1 角色权限矩阵编辑
/admin/templates M1 权限模板 CRUD + 批量应用
/admin/logs M1 操作日志 (分页/筛选/时间范围)
/admin/profile M1 个人设置 (密码/2FA/显示名)
/admin/providers M2 提供商卡片 (状态/模型数)
/admin/providers/:id M2 提供商详情/编辑
/admin/providers/:id/models M2 模型表格 (添加/编辑/禁用)
/admin/api-keys M2 API 密钥管理 (创建/轮换/撤销)
/admin/usage M2 使用量仪表板 (图表/按提供商/按模型/按日期)
/admin/relay/dashboard M3 中转状态 (队列/提供商健康/实时指标)
/admin/relay/tasks M3 任务列表 (状态筛选/取消/重试)
/admin/migration/report M4 迁移分析报告 (分类统计/优先级图表)
/admin/migration/items M4 配置项管理 (分类/优先级/来源筛选)
/admin/config/sync-log M4 同步日志 (冲突详情/解决记录)

9. 实施阶段

Phase 1: 基础 + 账号模块

  1. crates/zclaw-saas/ 基础结构 (Cargo.toml, db.rs, config.rs, state.rs, error.rs)
  2. auth/ 模块 (JWT, Argon2, TOTP, 中间件)
  3. account/ 模块 (CRUD, 角色, 模板, 操作日志)
  4. saas-admin/ 脚手架 (登录 + 账号管理页面)
  5. 验证: 注册→登录→创建 API Token→操作日志

Phase 2: 模型配置模块

  1. model_config/ 模块 (提供商/模型 CRUD)
  2. 种子数据导入 (chinese-providers.toml)
  3. 账号 API 密钥管理 + 使用量统计
  4. 管理后台: 提供商/模型/API 密钥/使用量
  5. 验证: 创建提供商→添加模型→分配密钥→查看使用量

Phase 3: 中转服务模块

  1. relay/ 模块 (队列, 调度, 流式转发)
  2. 速率限制 + 重试逻辑
  3. 管理后台: 中转仪表板/任务列表
  4. 桌面端集成 (改 base_url)
  5. 验证: 桌面端→中转→提供商→流式响应→使用量记录

Phase 4: 配置迁移模块

  1. migration/ 模块 (配置分析, 同步协议)
  2. 种子配置项 (从 TOML 文件分析)
  3. 桌面端 SaaSConfigClient
  4. 更新 KernelConfig::load() 支持 SaaS 覆盖
  5. 验证: 配置同步→覆盖→冲突检测→解决

10. 验证方案

每阶段验证

  1. cargo build -p zclaw-saas — 编译通过
  2. cargo test -p zclaw-saas — 单元测试通过
  3. curl 测试每个 API 端点
  4. 管理后台操作 → 验证数据库状态

最终端到端验证

  1. 完整流程: 注册账号 → 登录 → 创建 API Token → 配置提供商 → 桌面端通过中转调用模型 → 查看使用量 → 配置同步
  2. 安全验证: JWT 过期拒绝、权限不足拒绝、API Token 撤销后拒绝、TOTP 强制验证
  3. 错误处理: 提供商不可用降级、速率限制触发排队、队列溢出错误、配置冲突检测

11. 关键参考文件

文件 用途
crates/zclaw-kernel/src/config.rs LlmConfig/ApiProtocol 模式参考
crates/zclaw-memory/src/schema.rs SQL schema 模式参考
crates/zclaw-runtime/src/driver/openai.rs 中转转发实现参考
crates/zclaw-runtime/src/driver/mod.rs LlmDriver trait 参考
config/chinese-providers.toml 提供商种子数据源
config/config.toml 配置迁移分析源
config/security.toml 安全配置迁移分析源
desktop/src-tauri/src/secure_storage.rs 密钥加密存储参考
desktop/src-tauri/src/lib.rs Tauri 命令模式参考