FastAPI + Docker 迁移设计文档
日期: 2026-05-09
版本: 1.0
状态: 设计阶段
1. 背景与目标
将现有的广西政府采购网公告监控系统从 Flask CLI 架构迁移到 FastAPI + Docker 部署方案,同时提升代码质量、安全性和可维护性。
核心目标
- FastAPI 替代 Flask 回调服务器 + argparse CLI,统一为一个 Web 应用
- Docker 单容器部署,连接现有 PostgreSQL
- SQLAlchemy ORM 替代原始 psycopg2 操作
- APScheduler 内置定时任务
- 环境变量管理配置,敏感信息不提交 git
- 异步改造(httpx + asyncpg)
2. 新项目结构
3. API 端点设计
3.1 公告查询
| 方法 |
路径 |
说明 |
GET |
/api/v1/announcements |
查询公告列表(分页、来源筛选、日期筛选、关键词搜索) |
GET |
/api/v1/announcements/{id} |
获取单条公告详情 |
GET |
/api/v1/announcements/today |
获取今日公告 |
GET |
/api/v1/announcements/stats |
获取公告统计信息 |
3.2 爬取控制
| 方法 |
路径 |
说明 |
POST |
/api/v1/crawl/trigger |
手动触发一次爬取(支持关键词/来源参数) |
GET |
/api/v1/crawl/status |
查看最近爬取状态 |
GET |
/api/v1/crawl/sources |
获取已配置的公告来源列表 |
3.3 定时任务
| 方法 |
路径 |
说明 |
GET |
/api/v1/scheduler/jobs |
查看活跃的定时任务 |
POST |
/api/v1/scheduler/pause/{job_id} |
暂停某个定时任务 |
POST |
/api/v1/scheduler/resume/{job_id} |
恢复某个定时任务 |
3.4 企业微信
| 方法 |
路径 |
说明 |
GET POST |
/api/v1/wechat/callback |
企业微信回调入口(URL验证 + 消息接收) |
3.5 系统
| 方法 |
路径 |
说明 |
GET |
/health |
健康检查(包含数据库连通性) |
3.6 通用特性
- FastAPI 自动生成 Swagger UI (
/docs) 和 ReDoc (/redoc)
- 统一分页格式:
{ total, page, page_size, items }
- 统一错误响应:
{ detail: "error message" }
- 依赖注入:数据库 session、配置、调度器均通过 FastAPI
Depends() 注入
- CORS 默认关闭,通过
CORS_ORIGINS 环境变量可选开启
4. 数据模型
4.1 公告表 (announcements)
合并现有的 announcements、auto_announcements、manual_announcements 三张表为一张,通过 crawl_mode 字段区分自动/手动。
| 字段 |
类型 |
说明 |
| id |
Integer |
主键 |
| title |
String(500) |
公告标题 |
| publish_date |
DateTime |
发布时间 |
| purchase_name |
String(200) |
发布单位 |
| content_url |
Text |
内容链接 |
| source_code |
String(50) |
来源代码 |
| source_name |
String(100) |
来源名称 |
| announcement_type |
String(50) |
公告类型枚举值 |
| content_hash |
String(64) |
SHA256 内容哈希,UNIQUE |
| crawl_mode |
String(20) |
"auto" / "manual" |
| is_new |
Boolean |
是否新公告 |
| is_today |
Boolean |
是否今日公告 |
| created_at |
DateTime |
创建时间 |
| updated_at |
DateTime |
更新时间 |
4.2 大化县公告表 (dahuagov_announcements)
保留独立表,因为推送逻辑不同(全部推送,不筛选关键词)。
| 字段 |
类型 |
说明 |
| id |
Integer |
主键 |
| title |
String(500) |
公告标题 |
| publish_date |
DateTime |
发布时间 |
| purchase_name |
String(200) |
发布单位 |
| content_url |
Text |
内容链接 |
| source_code |
String(50) |
默认 "dahuagov" |
| source_name |
String(100) |
默认 "大化县政府网采购公告" |
| content_hash |
String(64) |
SHA256 内容哈希,UNIQUE |
| is_new |
Boolean |
是否新公告(未推送) |
| created_at |
DateTime |
创建时间 |
| updated_at |
DateTime |
更新时间 |
4.3 变更说明
content_hash 从 MD5 (32位) 改为 SHA256 (64位)
- 去掉
announcement_sources 表,来源信息从环境变量配置动态读取
- 去掉
crawl_results 表,爬取结果通过日志记录
- 去掉多余的索引表(keyword_matched / date_filtered 字段),筛选在应用层处理
- 使用 Alembic 管理数据库迁移
5. 配置管理
5.1 pydantic-settings
所有配置通过 pydantic-settings 的 BaseSettings 加载,来源优先级:环境变量 > .env 文件 > 默认值。
5.2 环境变量
5.3 安全措施
.env 文件加入 .gitignore
config.yaml 中的敏感信息不再提交到仓库
- 使用 SHA256 替代 MD5 做内容哈希
- 建议清理 git 历史中的敏感信息
6. Docker 部署
6.1 架构
6.2 Dockerfile
6.3 docker-compose.yml
6.4 启动步骤
7. 技术栈
| 组件 |
当前 |
迁移后 |
| Web 框架 |
Flask (仅回调用) |
FastAPI |
| CLI |
argparse |
不再需要,API 替代 |
| 数据库驱动 |
psycopg2 (同步) |
asyncpg (异步) + SQLAlchemy 2.0 |
| HTTP 客户端 |
requests (同步) |
httpx (异步) |
| 定时任务 |
schedule 库 |
APScheduler |
| 配置 |
PyYAML + config.yaml |
pydantic-settings + .env |
| 加密 |
pycryptodome + cryptography |
保留 |
| 部署 |
裸机 + uWSGI |
Docker + uvicorn |
| 代码检查 |
无 |
ruff |
| 测试 |
无 |
pytest + httpx |
| 迁移 |
手动 CREATE TABLE |
Alembic |
8. 额外改进
8.1 异步改造
- 爬虫网络请求:
httpx.AsyncClient 替代 requests
- 数据库:
asyncpg + SQLAlchemy async engine
- 通知发送:
httpx.AsyncClient
- API 端点使用
async def
8.2 自动化测试
- 爬虫解析器单元测试(Mock 网页响应,验证解析逻辑)
- 筛选逻辑单元测试
- API 端点集成测试(用
httpx.AsyncClient + 测试数据库)
- pytest + pytest-asyncio + pytest-cov
8.3 日志改进
- 结构化日志(JSON 格式)
- Docker 环境下输出到 stdout
- 保留文件日志供宿主机查看(挂载
./logs 目录)
8.4 错误处理
- 统一异常处理中间件
- 健康检查
/health 含数据库连通性验证
- 关键操作结构化日志记录
8.5 代码质量
ruff 替代 flake8 + black
pyproject.toml 统一管理依赖和工具配置
- 全面 Type Hints
8.6 .gitignore
9. 迁移策略
分步执行
- 创建新项目结构骨架(FastAPI app + config)
- 数据库模型 + Alembic 迁移
- 迁移爬虫模块(异步改造)
- 迁移服务层(筛选、通知、Markdown)
- 迁移 WeChat 回调(Flask → FastAPI route)
- 添加 APScheduler 定时任务
- 编写测试
- Docker 化
- 清理旧文件
向后兼容
- 保留
dahuagov_announcements 表结构不变
- 企业微信回调 URL 路径保持不变 (
/api/v1/wechat/callback)
- 数据库迁移使用 Alembic,不丢失现有数据
10. 未包含的内容
- 不做多用户认证/授权(个人使用)
- 不做 Celery 分布式任务(个人使用,APScheduler 足够)
- 不做 Redis 缓存
- 不做前端 UI
11. 风险与缓解
| 风险 |
缓解 |
| 数据库迁移丢失数据 |
Alembic 自动生成迁移脚本,先在测试环境验证 |
| 异步爬虫被目标站限流 |
保留现有延迟/重试机制,httpx 支持同样的超时配置 |
| 企业微信回调兼容性 |
回调路径不变,加解密逻辑完全复用现有代码 |
| Docker 网络访问 10.10.10.14 |
确认 Docker 宿主机可访问该 IP,必要时用 host.docker.internal |