Personal Blog - 后端设计文档
FastAPI + SQLAlchemy + SQLite 架构
1. 应用入口 (app.py)
1.1 启动流程
1.2 CORS 配置
1.3 路由注册
| 路由前缀 |
文件 |
说明 |
/api/auth |
routers/auth.py |
登录、Token、管理员创建 |
/api/posts |
routers/posts.py |
文章 CRUD、搜索、统计 |
/api/categories |
routers/categories.py |
分类 CRUD |
/api/tags |
routers/tags.py |
标签 CRUD |
/api/images |
routers/images.py |
图片上传/删除 |
/api/settings |
routers/settings.py |
系统设置、自定义页面 |
/api/crawl |
routers/crawl.py |
通用网页采集 |
/api/wechat-crawl |
routers/wechat_crawl.py |
公众号文章采集 |
/uploads |
app.py |
静态图片文件服务 |
/ |
app.py |
静态文件 (index.html 等) |
2. 数据库模型 (models.py)
2.1 ER 关系图
2.2 模型详解
Post (文章)
| 字段 |
类型 |
说明 |
id |
Integer, PK |
主键自增 |
title |
String(200) |
文章标题 |
slug |
String(200), UQ |
URL 标识,如 hello-world |
content |
Text |
Markdown 格式正文 |
summary |
Text |
文章摘要 |
cover_image |
String(500) |
封面图 URL(可为空) |
is_published |
Boolean |
是否已发布 |
is_draft |
Boolean |
是否草稿 |
created_at |
DateTime |
创建时间 |
updated_at |
DateTime |
更新时间 |
view_count |
Integer |
浏览次数 |
category_id |
Integer, FK |
所属分类(可为空) |
is_published 和 is_draft 互斥:发布后 is_published=True, is_draft=False
- 状态组合:
- 草稿:
is_published=False, is_draft=True
- 已发布:
is_published=True, is_draft=False
- 暂不支持 "已发布但隐藏"
Category (分类)
| 字段 |
类型 |
说明 |
id |
Integer, PK |
主键自增 |
name |
String(50), UQ |
分类名称 |
description |
Text |
分类描述 |
created_at |
DateTime |
创建时间 |
- 删除分类时,关联文章自动设为
category_id=None
Tag (标签)
| 字段 |
类型 |
说明 |
id |
Integer, PK |
主键自增 |
name |
String(30), UQ |
标签名称 |
created_at |
DateTime |
创建时间 |
- 与 Post 为多对多关系,通过
post_tags 关联表实现
- 删除标签时自动解除关联
Admin (管理员)
| 字段 |
类型 |
说明 |
id |
Integer, PK |
主键自增 |
username |
String(50), UQ |
用户名 |
password_hash |
String(255) |
bcrypt 哈希密码 |
created_at |
DateTime |
创建时间 |
- 系统初始化时创建默认管理员(
admin / admin123)
Setting (系统设置)
| 字段 |
类型 |
说明 |
id |
Integer, PK |
主键自增 |
site_name |
String(100) |
站点名称 |
site_description |
Text |
站点描述 |
site_logo |
String(500) |
Logo URL |
posts_per_page |
Integer |
每页文章数 |
theme |
String(50) |
主题标识 |
custom_css |
Text |
自定义 CSS |
icp_number |
String(50) |
备案号 |
copyright |
String(200) |
版权信息 |
analytics_code |
Text |
统计代码 |
- 单记录表(
get_or_create_settings 保证只有一条记录)
Page (自定义页面)
| 字段 |
类型 |
说明 |
id |
Integer, PK |
主键自增 |
title |
String(200) |
页面标题 |
slug |
String(200), UQ |
URL 标识 |
content |
Text |
Markdown 内容 |
is_nav |
Boolean |
是否在导航栏显示 |
nav_order |
Integer |
导航排序 |
created_at |
DateTime |
创建时间 |
updated_at |
DateTime |
更新时间 |
3. 认证机制 (routers/auth.py)
3.1 JWT Token
- 算法:HS256
- 密钥:硬编码
your-secret-key-123(生产环境应改为环境变量)
- 有效期:30 天
- 签发内容:
{"sub": "admin", "exp": timestamp}
3.2 双重认证方式
-
Bearer Token(管理后台使用)
-
Cookie Token(可选,用于同源场景)
3.3 鉴权装饰器
- 优先读取 Bearer Token
- 如不存在则读取 Cookie
- 验证失败返回 401
4. 文章 API (routers/posts.py)
4.1 端点列表
| 方法 |
路径 |
说明 |
鉴权 |
| GET |
/api/posts |
文章列表(含分页/搜索/筛选) |
公开 |
| GET |
/api/posts/{post_id} |
单篇文章详情 |
公开 |
| POST |
/api/posts |
创建文章 |
管理员 |
| PUT |
/api/posts/{post_id} |
更新文章 |
管理员 |
| DELETE |
/api/posts/{post_id} |
删除文章 |
管理员 |
| GET |
/api/posts/stats/overview |
统计概览 |
管理员 |
| GET |
/api/posts/slug/{slug} |
通过 slug 获取文章 |
公开 |
| GET |
/api/posts/category/{category_id} |
按分类获取文章 |
公开 |
| GET |
/api/posts/tag/{tag_name} |
按标签获取文章 |
公开 |
4.2 列表查询参数
| 参数 |
类型 |
说明 |
skip |
int |
分页偏移,默认 0 |
limit |
int |
每页数量,默认 10 |
search |
str |
标题/摘要/内容关键词搜索(like %xxx%) |
category_id |
int |
按分类筛选 |
tag |
str |
按标签名筛选 |
status |
str |
published / draft / all,默认 published |
4.3 响应数据结构
4.4 搜索实现
4.5 浏览次数
- 每次 GET 单篇文章时
view_count += 1
- 通过
/api/posts/{id}?increment_view=true 控制
5. 图片上传 (routers/images.py)
5.1 上传接口
5.2 处理流程
- 校验文件类型:白名单
jpg, jpeg, png, gif, webp
- 生成唯一文件名:
uuid.uuid4().hex + 扩展名
- 保存到
backend/uploads/
- 返回可访问的 URL:
/uploads/{filename}
5.3 图片删除
- 从文件系统删除
- 同时从所有文章
cover_image 和 content 字段中移除引用
5.4 图片裁剪(封面图)
6. 数据库连接 (database.py)
6.1 配置
6.2 Session 获取
- 通过 FastAPI
Depends 注入到路由处理函数
7. Pydantic 模型 (schemas.py)
| Schema |
用途 |
PostCreate |
创建文章请求 |
PostUpdate |
更新文章请求 |
PostResponse |
文章响应(含 category/tags) |
CategoryCreate |
创建分类请求 |
CategoryResponse |
分类响应(含 post_count) |
TagCreate |
创建标签请求 |
TagResponse |
标签响应 |
SettingUpdate |
更新设置请求 |
SettingResponse |
设置响应 |
PageCreate |
创建页面请求 |
PageUpdate |
更新页面请求 |
PageResponse |
页面响应 |
8. 错误处理
后端使用 FastAPI 的 HTTPException 统一返回错误:
常见错误码:
| 状态码 |
场景 |
| 400 |
参数错误、数量超限、名称重复 |
| 401 |
未登录/Token 无效 |
| 403 |
无权限 |
| 404 |
资源不存在 |
| 422 |
请求体校验失败 |
本文档由 nanobot 根据项目源码自动生成。