# 架构设计 > 对应方案 v0.1 的实现版本。 ## 模块边界 | 模块 | 路径 | 职责 | | --- | --- | --- | | API | `backend/app/api/` | HTTP 路由,处理鉴权 / 入参 / 出参 | | 业务 | `backend/app/services/` | 抓取、翻译、领域逻辑 | | Worker | `backend/app/workers/` | 后台调度、pipeline | | 数据 | `backend/app/models/` | SQLAlchemy ORM | | 迁移 | `backend/alembic/` | 数据库 schema 版本 | | 前端 | `frontend/src/` | Vue 3 + Naive UI | ## 数据流 ``` Source (DB) │ ▼ Scheduler (cron / interval) │ ▼ RSSFetcher.fetch() ── HTTP GET ──► upstream RSS │ ▼ FetchedItem list │ ▼ (url_hash UNIQUE 去重) Article INSERT │ ▼ (translation_status='pending') TranslationService.translate() ├─ Redis cache hit → return ├─ quota check ├─ Tencent TMT (主) ──► 30 天 Redis 月度计数 └─ Local NLLB (降级,需启用) │ ▼ Article UPDATE (title_zh / body_zh_* / status) ``` ## 关键设计决策 - **PostgreSQL**:UNIQUE 约束 + `ON CONFLICT DO NOTHING` 做去重,O(1) 写 - **Redis 三用**:翻译缓存(30 天 TTL)+ 月度配额(INCRBY)+ 后续限流 - **AScheduler 重构 jobs**:每天 00:30 从 DB 重新读,运行时新增源自动生效 - **翻译分块**:按段落切,单段 > 1500 字符按句号再切,单请求 ≤ 4500 字符(腾讯 TMT 上限) - **失败退避**:某源连续失败 3 次,fetch_interval × 2(封顶 720 分钟),成功一次恢复 - **API Token 双轨**:网页用短期 JWT(15min)+ refresh(14d);Android 用长期 API Token(可独立撤销) ## 字段保留 `articles` 表里这些字段已建,MVP 全部 null,后续 enrichment 阶段直接写值不动表: - `category` / `commentary` / `entities` / `sentiment` / `topic_id` / `bias` ## 安全 - 密码 bcrypt(cost=12) - JWT 走 HTTPS-only cookie(网页) / Bearer header(APP) - 数据库/Redis 不暴露到宿主机 - Caddy 做 TLS 终止 - API 限流(MVP 暂未实现,后续加) ## 不在 MVP - ❌ 全文搜索(可用 PG `to_tsvector`,MVP 先简单 ILIKE) - ❌ PWA 离线缓存 - ❌ Android 客户端 - ❌ 自动分类/点评/实体识别 - ❌ 主题聚类 - ❌ 跨源立场 - ❌ Telegram 推送 - ❌ i18n(只 zh) 见 [`DEPLOY.md`](../DEPLOY.md) 跑起来,见 [`../README.md`](../README.md) 看全貌。