# 技术架构设计 > 目标:支撑“网站 + 对外 API + 计费”的商用闭环。产品范围见 `docs/prd.md`,计费口径见 `docs/billing.md`。 ## 系统架构图 ``` ┌───────────────────────────────┐ │ CDN (静态资源/下载可选加速) │ └──────────────┬────────────────┘ │ ┌──────▼──────┐ │ Nginx/Caddy │ │ TLS/反向代理 │ └──────┬──────┘ ┌───────────────────────────┼───────────────────────────┐ │ │ │ ▼ ▼ ▼ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │ Web 前端 (Vue3) │ │ API 服务 (Axum) │ │ Admin 前端(Vue3)│ │ 上传/结果/账单 │ │ 认证/计费/接口 │ │ 运营/风控/配置 │ └─────────────────┘ └───────┬─────────┘ └─────────────────┘ │ ┌──────────────────┼──────────────────┐ │ │ │ ▼ ▼ ▼ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │ PostgreSQL (DB) │ │ Redis (缓存/队列)│ │ 对象存储 (S3) │ │ 用户/任务/账单/用量│ │ Streams/RateLimit│ │ 原图/压缩结果 │ └─────────┬───────┘ └───────┬─────────┘ └─────────┬───────┘ │ │ │ │ ▼ │ │ ┌───────────────┐ │ │ │ Worker (Rust) │ │ │ │ 压缩/计量/回写 │ │ │ └───────────────┘ │ │ │ ▼ ▼ ┌─────────────────┐ ┌─────────────────┐ │ 支付渠道/网关 │◄──────Webhooks───────│ API(Webhook处理) │ │ Stripe │ │ 订阅/发票/状态 │ └─────────────────┘ └─────────────────┘ ``` ## 核心组件设计 ### 0. 服务拆分(推荐) 为避免 CPU 密集的压缩任务影响 API 延迟,建议最小拆分为: - **API 服务**:认证、限流、计费/订阅、任务编排、回调、签名 URL、管理后台 API。 - **Worker 服务**:执行图片压缩(CPU 密集)、写入结果、落用量账本、推送进度。 本地开发可以合并进一个进程(feature flag);生产建议分开部署并可独立扩容。 ### 1. 压缩引擎 ```rust // 压缩配置 pub enum CompressionLevel { /// 高压缩比 - 有损压缩,文件最小 High, /// 中等压缩 - 平衡模式 Medium, /// 低压缩比 - 无损/近无损,质量优先 Low, } pub struct CompressionConfig { pub level: CompressionLevel, pub output_format: Option, // 可选转换格式 pub max_width: Option, // 可选调整尺寸 pub max_height: Option, pub preserve_metadata: bool, // 是否保留元数据(默认 false) } ``` ### 2. 压缩策略 | 格式 | 高压缩比(有损) | 低压缩比(无损) | 使用库 | |------|-----------------|-----------------|--------| | PNG | pngquant 量化到 256 色 | oxipng 无损优化 | `imagequant` + `oxipng` | | JPEG | mozjpeg quality=60 | mozjpeg quality=90 | `mozjpeg` | | WebP | lossy quality=75 | lossless | `webp` | | AVIF | quality=50 | quality=90 | `ravif` | ```rust // 压缩核心逻辑 pub trait ImageCompressor: Send + Sync { async fn compress(&self, input: &[u8], config: &CompressionConfig) -> Result>; fn supported_formats(&self) -> Vec; } pub struct PngCompressor; pub struct JpegCompressor; pub struct WebpCompressor; pub struct AvifCompressor; // 统一压缩入口 pub struct CompressionEngine { compressors: HashMap>, } impl CompressionEngine { pub async fn compress(&self, input: &[u8], config: &CompressionConfig) -> Result { let format = detect_format(input)?; let compressor = self.compressors.get(&format) .ok_or(Error::UnsupportedFormat)?; let output = compressor.compress(input, config).await?; Ok(CompressionResult { original_size: input.len(), compressed_size: output.len(), format, data: output, }) } } ``` ### 3. 任务处理模型 支持两种模式: #### 同步模式(小文件/单文件) ``` 请求 -> 压缩 -> 直接返回结果 ``` #### 异步模式(大文件/批量) ``` 请求 -> 创建任务 -> 返回任务ID ↓ 后台Worker处理 ↓ 客户端轮询/WebSocket通知 ↓ 下载结果 ``` ```rust pub enum TaskStatus { Pending, Processing, Completed, Failed, } pub struct CompressionTask { pub id: Uuid, pub user_id: Option, // 游客为空 pub session_id: Option, // 游客会话(Cookie) pub status: TaskStatus, pub files: Vec, pub config: CompressionConfig, pub created_at: DateTime, pub completed_at: Option>, } pub struct FileTask { pub id: Uuid, pub original_name: String, pub original_size: u64, pub compressed_size: Option, pub status: TaskStatus, pub error: Option, } ``` ### 4. 用户认证系统 ```rust // JWT Claims #[derive(Serialize, Deserialize)] pub struct Claims { pub sub: Uuid, // 用户ID pub role: UserRole, // 用户角色 pub exp: i64, // 过期时间 } pub enum UserRole { User, Admin, } // API Key 认证 pub struct ApiKey { pub id: Uuid, pub user_id: Uuid, pub key_prefix: String, // 前缀索引(仅展示用) pub key_hash: String, // 推荐:HMAC-SHA256(key, server_pepper) 或 sha256+pepper pub name: String, pub permissions: Vec, pub rate_limit: u32, // 每分钟请求数 pub created_at: DateTime, pub last_used_at: Option>, } pub enum Permission { Compress, BatchCompress, ReadStats, BillingRead, // 查看账单/用量(可选) WebhookManage, // 管理 Webhook(可选) } ``` ### 5. 限流与配额(Rate Limit & Quota) 用途区分: - **限流**:保护服务(超出返回 HTTP `429`) - **配额**:试用/计费限制(超出返回 HTTP `402` / `QUOTA_EXCEEDED`) 默认建议(最终以 `system_config` + 套餐 `plans.*` 为准): - 匿名试用:`10 req/min` + `10 units/day` + `5MB/文件` + `5 文件/批量` - 登录用户:`60 req/min`;文件大小/批量/保留期/周期额度由套餐决定 - API Key:`100 req/min`(可配置);文件大小/批量/周期额度由套餐/Key 覆盖决定 ### 6. 用量计量与计费(Metering & Billing) 计量口径见 `docs/billing.md`,架构上建议: - Worker 在每个文件成功输出后写入 `usage_events`(账本明细),并更新 `usage_periods`(按订阅周期聚合)。 - API 在创建任务/接收同步压缩请求时做**配额预检**(快速失败),Worker 做**最终扣减**(账本落地,保证一致性)。 - 对外 API 强烈建议支持 `Idempotency-Key`;DB 侧存储“幂等记录 + 响应摘要”,避免重复扣减与重复任务。 ### 7. 支付回调(Webhooks) Stripe 通常通过 webhook 推送订阅/支付状态变更: - API 服务提供 `/webhooks/{provider}` 入口,**验签 + 幂等 + 可重放**。 - webhook 事件入库后异步处理(避免回调超时),更新订阅/发票状态,并写审计日志。 ## 核心依赖 ```toml [dependencies] # Web 框架 axum = "0.7" tokio = { version = "1", features = ["full"] } tower = "0.4" tower-http = { version = "0.5", features = ["cors", "fs", "compression"] } # 图片处理 image = "0.25" oxipng = "9" imagequant = "4" # PNG 有损压缩(pngquant 核心) mozjpeg = "0.10" # JPEG 压缩 webp = "0.3" # WebP 编解码 ravif = "0.11" # AVIF 编码 # 数据库 sqlx = { version = "0.7", features = ["runtime-tokio", "postgres", "uuid", "chrono"] } # Redis redis = { version = "0.24", features = ["tokio-comp"] } # 认证 jsonwebtoken = "9" argon2 = "0.5" # 密码哈希 # 工具 uuid = { version = "1", features = ["v4", "serde"] } chrono = { version = "0.4", features = ["serde"] } serde = { version = "1", features = ["derive"] } serde_json = "1" thiserror = "1" tracing = "0.1" tracing-subscriber = "0.3" # 配置 config = "0.14" dotenvy = "0.15" ``` ## 性能考虑 ### 1. 并发处理 - 使用 Tokio 异步运行时 - 图片压缩使用 `spawn_blocking` 避免阻塞异步线程 - 可配置 Worker 线程数 ```rust // 在独立线程池中执行 CPU 密集型压缩 let result = tokio::task::spawn_blocking(move || { compress_image_sync(&data, &config) }).await??; ``` ### 2. 内存管理 - 流式处理大文件 - 限制并发压缩任务数 - 压缩完成后立即清理临时文件 ### 3. 缓存策略 - Redis 缓存用户会话 - 可选:相同图片哈希缓存结果(去重) ## 安全考虑 1. **输入验证**:检查文件魔数,不仅依赖扩展名 2. **文件大小限制**:防止 DoS 3. **像素/维度限制**:防止“图片炸弹”(解码后超大) 4. **路径遍历防护**:存储时使用 UUID 命名 5. **SQL 注入防护**:使用参数化查询(SQLx 自动处理) 6. **XSS 防护**:前端输出转义 7. **CSRF 防护**:SameSite Cookie + Token 8. **速率限制**:防止滥用 9. **默认移除元数据**:避免泄露定位/设备信息(除非用户明确开启保留) ## 可扩展性 ### 水平扩展 ``` ┌─────────────┐ │ Load Balancer│ └──────┬──────┘ ┌───────────────┼───────────────┐ ▼ ▼ ▼ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ API 实例1 │ │ API 实例2 │ │ API 实例3 │ └──────────┘ └──────────┘ └──────────┘ │ │ │ └───────────────┼───────────────┘ ▼ ┌─────────────────┐ │ 共享 PostgreSQL │ │ 共享 Redis │ │ 共享 S3 存储 │ └─────────────────┘ ``` ### 后续可添加功能 - 消息队列(RabbitMQ/NATS)处理异步任务 - 分布式任务调度 - CDN 加速下载