raisfast 全栈开发底座 — 产品与技术指导手册
1. 项目概述
基于 Rust + Axum 构建的高性能全栈开发底座。面向个人开发者和小型团队,提供内容管理、插件系统、多租户、RBAC 权限、实时事件等全栈能力,支持 Web 服务器和 Tauri 桌面应用双模式部署。
1.1 项目目标
- 高性能:利用 Rust 零成本抽象和异步 I/O,单机支撑高并发访问
- 安全性:密码加盐哈希、JWT 鉴权、XSS/CSRF 防护、SQL 注入防护
- 易部署:Docker 容器化,单一二进制输出,资源占用低
- 可扩展:模块化架构,便于后续功能迭代
1.2 目标用户
| 角色 |
描述 |
| 管理员 |
系统配置、用户管理、内容审核 |
| 作者 |
创建和发布文章、管理个人内容 |
| 读者 |
浏览文章、评论互动、订阅 RSS |
2. 功能规格
2.1 用户系统
2.1.1 注册与登录
- 邮箱 + 密码注册,邮箱唯一性校验
- 密码强度校验(最少 8 位,包含字母和数字)
- 密码使用 Argon2 加盐哈希存储,禁止明文存储
- 登录成功返回 JWT Access Token(15 分钟过期)+ Refresh Token(7 天过期)
- Refresh Token 用于无感刷新 Access Token
2.1.2 用户资料
- 用户名、头像、个人简介、个人网站链接
- 头像上传支持 JPEG/PNG,最大 2MB,存储为 WebP 格式
2.1.3 角色与权限
2.2 文章系统
2.2.1 文章模型
| 字段 |
类型 |
说明 |
| id |
UUID |
主键 |
| title |
String |
标题,最长 200 字符 |
| slug |
String |
URL 友好标识,自动从标题生成,唯一 |
| content |
Text |
Markdown 格式正文 |
| excerpt |
String |
摘要,最长 500 字符,自动从正文提取 |
| cover_image |
String |
封面图片 URL |
| status |
Enum |
draft / published |
| author_id |
UUID |
外键关联用户 |
| category_id |
UUID |
外键关联分类 |
| created_at |
Timestamp |
创建时间 |
| updated_at |
Timestamp |
更新时间 |
| published_at |
Timestamp |
发布时间 |
2.2.2 文章功能
- Markdown 编辑,服务端渲染为 HTML
- 草稿自动保存
- 文章状态流转:
draft → published → archived
- 发布时自动生成 slug(如
my-first-post),支持自定义
- 文章置顶、排序
- 浏览量统计(去重,基于 IP + User-Agent)
2.2.3 分类与标签
- 分类:树形结构(支持一级子分类),每篇文章归属一个分类
- 标签:平铺结构,多对多关系,一篇文章可有多个标签
- 标签云展示
2.3 评论系统
- 评论关联文章,支持嵌套回复(最多 3 层)
- 评论者填写昵称 + 邮箱(未登录用户)或使用已登录身份
- 管理员可审核/删除评论
- 支持按时间正序/倒序排列
- 评论分页加载
2.4 搜索
- 按关键词搜索文章标题和正文
- 支持按分类、标签、作者筛选
- 搜索结果高亮关键词
- 后续可接入 Meilisearch 提升搜索体验
2.5 媒体管理
- 图片上传(JPEG、PNG、GIF、WebP)
- 单文件最大 5MB
- 支持本地存储和 S3 兼容对象存储
- 图片自动压缩和缩略图生成
- 上传后返回可访问的 URL
- 提供
/feed.xml 全局 RSS 订阅
- 包含最新 20 篇已发布文章
- 符合 RSS 2.0 规范
2.7 系统管理
- 站点基础配置(标题、描述、每页文章数)
- 友情链接管理
- 导航菜单配置
3. API 设计
3.1 通用约定
- 基础路径:
/api/v1
- 请求/响应格式:JSON
- 认证方式:
Authorization: Bearer <token>
- 分页参数:
?page=1&page_size=20
- 排序参数:
?sort=created_at&order=desc
- 时间格式:ISO 8601(
2026-04-10T12:00:00Z)
3.2 统一响应结构
分页响应:
错误响应:
3.3 错误码规范
| 范围 |
说明 |
| 0 |
成功 |
| 40000-40099 |
通用参数错误 |
| 40100-40199 |
认证与授权错误 |
| 40400-40499 |
资源不存在 |
| 40900-40999 |
资源冲突 |
| 50000-50099 |
服务端内部错误 |
3.4 接口列表
认证
| 方法 |
路径 |
说明 |
认证 |
| POST |
/api/v1/auth/register |
用户注册 |
否 |
| POST |
/api/v1/auth/login |
用户登录 |
否 |
| POST |
/api/v1/auth/refresh |
刷新 Token |
否 |
| POST |
/api/v1/auth/logout |
登出 |
是 |
用户
| 方法 |
路径 |
说明 |
认证 |
| GET |
/api/v1/users/me |
获取当前用户信息 |
是 |
| PUT |
/api/v1/users/me |
更新当前用户信息 |
是 |
| PUT |
/api/v1/users/me/password |
修改密码 |
是 |
| GET |
/api/v1/users/:id |
获取用户公开信息 |
否 |
| GET |
/api/v1/users |
获取用户列表(管理员) |
是 |
文章
| 方法 |
路径 |
说明 |
认证 |
| GET |
/api/v1/posts |
文章列表 |
否 |
| GET |
/api/v1/posts/:slug |
文章详情 |
否 |
| POST |
/api/v1/posts |
创建文章 |
是 |
| PUT |
/api/v1/posts/:slug |
更新文章 |
是 |
| DELETE |
/api/v1/posts/:slug |
删除文章 |
是 |
分类
| 方法 |
路径 |
说明 |
认证 |
| GET |
/api/v1/categories |
分类列表 |
否 |
| POST |
/api/v1/categories |
创建分类 |
是 |
| PUT |
/api/v1/categories/:id |
更新分类 |
是 |
| DELETE |
/api/v1/categories/:id |
删除分类 |
是 |
标签
| 方法 |
路径 |
说明 |
认证 |
| GET |
/api/v1/tags |
标签列表 |
否 |
| POST |
/api/v1/tags |
创建标签 |
是 |
| DELETE |
/api/v1/tags/:id |
删除标签 |
是 |
评论
| 方法 |
路径 |
说明 |
认证 |
| GET |
/api/v1/posts/:slug/comments |
文章评论列表 |
否 |
| POST |
/api/v1/posts/:slug/comments |
发表评论 |
可选 |
| DELETE |
/api/v1/comments/:id |
删除评论 |
是 |
媒体
| 方法 |
路径 |
说明 |
认证 |
| POST |
/api/v1/media/upload |
上传文件 |
是 |
| GET |
/api/v1/media |
文件列表 |
是 |
| DELETE |
/api/v1/media/:id |
删除文件 |
是 |
| 方法 |
路径 |
说明 |
认证 |
| GET |
/feed.xml |
RSS 订阅 |
否 |
4. 数据库设计
4.1 ER 关系概览
4.2 表结构
users
| 列名 |
类型 |
约束 |
说明 |
| id |
UUID |
PK |
主键 |
| email |
VARCHAR(255) |
UNIQUE, NOT NULL |
邮箱 |
| username |
VARCHAR(50) |
UNIQUE, NOT NULL |
用户名 |
| password_hash |
VARCHAR(255) |
NOT NULL |
Argon2 哈希 |
| role |
VARCHAR(20) |
NOT NULL, DEFAULT 'reader' |
角色 |
| avatar |
VARCHAR(500) |
|
头像 URL |
| bio |
TEXT |
|
个人简介 |
| website |
VARCHAR(500) |
|
个人网站 |
| created_at |
TEXT |
NOT NULL |
创建时间(ISO 8601) |
| updated_at |
TEXT |
NOT NULL |
更新时间(ISO 8601) |
posts
| 列名 |
类型 |
约束 |
说明 |
| id |
UUID |
PK |
主键 |
| title |
VARCHAR(200) |
NOT NULL |
标题 |
| slug |
VARCHAR(250) |
UNIQUE, NOT NULL |
URL 标识 |
| content |
TEXT |
NOT NULL |
Markdown 正文 |
| excerpt |
VARCHAR(500) |
|
摘要 |
| cover_image |
VARCHAR(500) |
|
封面图 URL |
| status |
VARCHAR(20) |
NOT NULL, DEFAULT 'draft' |
draft/published/archived |
| author_id |
UUID |
FK → users.id |
作者 |
| category_id |
UUID |
FK → categories.id |
分类 |
| view_count |
INTEGER |
NOT NULL, DEFAULT 0 |
浏览量 |
| is_pinned |
BOOLEAN |
NOT NULL, DEFAULT 0 |
是否置顶 |
| created_at |
TEXT |
NOT NULL |
创建时间(ISO 8601) |
| updated_at |
TEXT |
NOT NULL |
更新时间(ISO 8601) |
| published_at |
TEXT |
|
发布时间(ISO 8601) |
categories
| 列名 |
类型 |
约束 |
说明 |
| id |
UUID |
PK |
主键 |
| name |
VARCHAR(100) |
UNIQUE, NOT NULL |
分类名 |
| slug |
VARCHAR(120) |
UNIQUE, NOT NULL |
URL 标识 |
| description |
VARCHAR(500) |
|
描述 |
| parent_id |
UUID |
FK → categories.id |
父分类 |
| sort_order |
INTEGER |
NOT NULL, DEFAULT 0 |
排序 |
| created_at |
TEXT |
NOT NULL |
创建时间(ISO 8601) |
tags
| 列名 |
类型 |
约束 |
说明 |
| id |
UUID |
PK |
主键 |
| name |
VARCHAR(50) |
UNIQUE, NOT NULL |
标签名 |
| slug |
VARCHAR(60) |
UNIQUE, NOT NULL |
URL 标识 |
| created_at |
TEXT |
NOT NULL |
创建时间(ISO 8601) |
posts_tags
| 列名 |
类型 |
约束 |
说明 |
| post_id |
UUID |
FK → posts.id |
文章 ID |
| tag_id |
UUID |
FK → tags.id |
标签 ID |
主键:(post_id, tag_id)
| 列名 |
类型 |
约束 |
说明 |
| id |
UUID |
PK |
主键 |
| post_id |
UUID |
FK → posts.id |
所属文章 |
| author_id |
UUID |
FK → users.id, NULLABLE |
登录用户 |
| nickname |
VARCHAR(50) |
|
游客昵称 |
| email |
VARCHAR(255) |
|
游客邮箱 |
| content |
TEXT |
NOT NULL |
评论内容 |
| parent_id |
UUID |
FK → comments.id, NULLABLE |
父评论 |
| status |
VARCHAR(20) |
NOT NULL, DEFAULT 'pending' |
pending/approved/spam |
| created_at |
TEXT |
NOT NULL |
创建时间(ISO 8601) |
media
| 列名 |
类型 |
约束 |
说明 |
| id |
UUID |
PK |
主键 |
| user_id |
UUID |
FK → users.id |
上传者 |
| filename |
VARCHAR(255) |
NOT NULL |
原始文件名 |
| filepath |
VARCHAR(500) |
NOT NULL |
存储路径 |
| mimetype |
VARCHAR(100) |
NOT NULL |
MIME 类型 |
| size |
INTEGER |
NOT NULL |
文件大小(字节) |
| created_at |
TEXT |
NOT NULL |
创建时间(ISO 8601) |
refresh_tokens
| 列名 |
类型 |
约束 |
说明 |
| id |
UUID |
PK |
主键 |
| user_id |
UUID |
FK → users.id |
所属用户 |
| token |
VARCHAR(255) |
UNIQUE, NOT NULL |
Refresh Token |
| expires_at |
TEXT |
NOT NULL |
过期时间(ISO 8601) |
| created_at |
TEXT |
NOT NULL |
创建时间(ISO 8601) |
5. 技术架构
5.1 技术栈
核心框架
| 层级 |
技术 |
版本 |
用途 |
| 语言 |
Rust |
Edition 2024 |
核心开发语言 |
| Web 框架 |
axum |
0.8.x |
HTTP 路由、中间件、提取器 |
| 异步运行时 |
tokio |
1.x (full features) |
异步 I/O 运行时 |
| 中间件 |
tower / tower-http |
0.5.x / 0.6.x |
CORS、限流、请求日志、压缩、静态文件 |
数据层
| 层级 |
技术 |
版本 |
用途 |
| 数据库 |
SQLite |
3.x |
轻量级嵌入式数据存储(开发阶段) |
| SQL 工具 |
sqlx |
0.8.x |
编译期 SQL 检查、异步查询 |
| 迁移 |
sqlx-cli |
0.8.x |
数据库版本管理 |
序列化与数据
| 层级 |
技术 |
版本 |
用途 |
| 序列化 |
serde / serde_json |
1.x |
JSON 序列化/反序列化 |
| UUID |
uuid |
1.x (v7) |
主键生成,时间排序友好 |
| 时间 |
chrono |
0.4.x |
时间处理,序列化配合 serde |
安全与认证
| 层级 |
技术 |
版本 |
用途 |
| 密码哈希 |
argon2 |
0.5.x |
Argon2id 密码安全哈希(OWASP 推荐) |
| JWT |
jsonwebtoken |
9.x |
Token 签发与验证 |
| HTML 净化 |
ammonia |
0.16.x |
XSS 防护,清理用户提交的 HTML |
| CSRF |
tokio (nonce) |
— |
CSRF Token 生成与校验 |
业务功能
| 层级 |
技术 |
版本 |
用途 |
| Markdown |
comrak |
0.36.x |
CommonMark/GFM 兼容 Markdown → HTML 渲染 |
| 代码高亮 |
syntect |
5.x |
Markdown 内代码块语法高亮 |
| Slug 生成 |
slug |
0.1.x |
URL 友好标题生成 |
| 校验 |
validator |
0.19.x |
声明式请求参数校验(derive 宏) |
| 图片处理 |
image |
0.25.x |
图片压缩、缩略图生成、格式转换 |
| RSS |
rss |
2.x |
RSS 2.0 订阅源生成 |
错误处理与日志
| 层级 |
技术 |
版本 |
用途 |
| 应用错误 |
anyhow |
1.x |
应用级通用错误传播(Service 层内部) |
| 业务错误 |
thiserror |
2.x |
自定义错误类型 derive(定义 AppError) |
| 日志 |
tracing |
0.1.x |
结构化日志,异步友好,支持 span |
| 日志格式 |
tracing-subscriber |
0.3.x |
日志输出格式化和过滤 |
配置与环境
| 层级 |
技术 |
版本 |
用途 |
| 环境变量 |
dotenvy |
0.15.x |
.env 文件加载 |
| 配置管理 |
config |
0.14.x |
多格式配置文件 + 环境变量 + 类型安全 |
测试与质量
| 层级 |
技术 |
版本 |
用途 |
| 单元测试 |
#[test] + rstest |
0.23.x |
参数化测试、fixture 注入 |
| 快照测试 |
insta |
1.x |
API 响应快照测试,防回归 |
| HTTP 集成测试 |
axum::test / reqwest |
0.12.x |
Handler 级别集成测试 |
| Mock |
mockall |
0.13.x |
Service 层 Mock,隔离数据库依赖 |
| 基准测试 |
criterion |
0.5.x |
性能基准测试,防性能退化 |
开发工具链(CI 必装)
| 工具 |
用途 |
| cargo fmt |
代码格式化,统一风格 |
| cargo clippy |
代码 Lint,零警告要求 |
| cargo audit |
安全漏洞审计(检查依赖 CVE) |
| cargo deny |
许可证合规 + 安全策略检查 |
| cargo outdated |
依赖版本检查 |
| cargo nextest |
更快的测试运行器(比 cargo test 快 3x+) |
| cargo insta test --review |
快照测试审查流程 |
5.2 项目结构
5.3 架构分层
- Handler 只负责提取请求参数、调用 Service、构造响应,不包含业务逻辑
- Service 包含所有业务逻辑,通过参数接收数据库连接,便于测试
- Model 定义数据结构和数据库操作,与数据库表一一对应
- Middleware 处理横切关注点(认证、日志、限流)
6. 安全设计
6.1 认证与授权
- JWT 签发使用 HS256 或 RS256 算法
- Access Token 短过期(15 分钟),Refresh Token 长过期(7 天)
- Refresh Token 存储在数据库中,支持吊销
- 密码使用 Argon2id 算法哈希,自动加盐
6.2 输入防护
- 所有用户输入通过
validator 进行服务端校验
- Markdown 渲染时过滤危险的 HTML 标签(XSS 防护)
- 使用 sqlx 参数化查询,杜绝 SQL 注入
- 请求体大小限制(默认 2MB)
6.3 传输安全
- 生产环境强制 HTTPS
- Cookie 设置
HttpOnly、Secure、SameSite=Strict
- CORS 白名单配置
6.4 限流
- 登录接口:同一 IP 每分钟最多 10 次
- 注册接口:同一 IP 每小时最多 5 次
- 评论接口:同一用户每分钟最多 3 次
- 通用接口:同一 IP 每分钟最多 60 次
7. 开发阶段规划
Phase 1:项目骨架(1-2 天)
Phase 2:用户认证(2-3 天)
Phase 3:文章核心(3-4 天)
Phase 4:评论与互动(2 天)
Phase 5:媒体与辅助功能(2-3 天)
Phase 6:安全与优化(1-2 天)
Phase 7:部署(1 天)
8. 环境变量
9. 编码规范
9.1 Rust 风格
- 遵循
rustfmt 默认配置,使用 cargo fmt 格式化
- 使用
cargo clippy 检查代码质量,零警告
- 公开函数和类型必须添加文档注释
///
- 禁止使用
unsafe,除非在极端性能热点且经过团队评审
- 使用
#![deny(unsafe_code)] 在 crate 级别禁止 unsafe
- 充分利用 Rust 类型系统:用
Option<T> 表示可能缺失,用 Result<T, E> 表示可能失败,用枚举表示有限状态
- 优先使用
impl Into<String> / AsRef<str> 而非固定 String 参数,减少不必要的分配
- 使用
Cow<str> 处理可能借用也可能拥有的字符串
- 优先使用迭代器方法链(
.map() / .filter() / .collect())替代命令式循环
- 使用
#[non_exhaustive] 标记公开枚举和结构体,保证 API 向后兼容
9.2 命名约定
- 文件名:snake_case(如
auth_handler.rs)
- 类型/结构体:PascalCase(如
CreatePostRequest)
- 函数/变量:snake_case(如
create_post)
- 常量:SCREAMING_SNAKE_CASE(如
MAX_PAGE_SIZE)
- 数据库列:snake_case
- API 路径:kebab-case(如
/api/v1/refresh-tokens)
9.3 错误处理
- 错误定义:使用
thiserror derive 宏定义 AppError 枚举,每个变体对应一类业务错误
- 错误传播:Service 内部使用
anyhow::Result 简化错误传播,在 Handler 边界转换为 AppError
AppError 实现 IntoResponse,自动转换为 HTTP 响应
- 禁止使用
unwrap() 和 expect() 在非测试代码中,使用 ? 或显式错误处理
- 数据库错误映射为业务语义错误(如唯一约束冲突 → 409 Conflict)
- 使用
.ok_or(AppError::...)? 将 Option 转为 Result
- 使用
thiserror 的 #[source] 属性保留错误链,配合 tracing 输出完整上下文
9.4 测试要求
- 每个 Service 函数编写单元测试
- 每个 Handler 编写集成测试
- 使用测试数据库,测试后清理
- 使用
rstest 编写参数化测试,减少重复
- 使用
insta 做 API 响应快照测试
- 使用
mockall Mock Service 层,隔离数据库依赖
- 目标测试覆盖率 > 70%
- CI 中使用
cargo nextest run 加速测试执行
9.5 性能要求
- 数据库连接使用连接池(sqlx 内置),避免频繁建连
- 热点查询使用
sqlx::query_scalar! 等编译期检查宏,减少运行时开销
- 分页查询必须使用
LIMIT + OFFSET 或游标分页,禁止全表扫描
- 使用
Arc<T> 共享状态,避免深层克隆(如 Arc<Pool>)
- 字符串处理优先使用
&str 借用,仅必要时转为 String
- 大量数据使用流式处理(
axum::body::BodyStream),避免全量加载到内存
- 静态资源使用
tower-http::services::ServeDir,配合 CompressionLayer 压缩
9.6 Rust Idiom 清单
以下为必须遵循的 Rust 惯用法:
| 场景 |
推荐做法 |
反模式 |
| 错误处理 |
Result<T, E> + ? |
unwrap() / expect() / panic! |
| 空值 |
Option<T> |
null / 占位默认值 |
| 字符串参数 |
&str / AsRef<str> |
到处 String 克隆 |
| 共享所有权 |
Arc<T> |
大量 clone() |
| 配置初始化 |
std::sync::LazyLock / once_cell |
全局 static mut |
| 并发安全 |
Mutex<T> / RwLock<T> / tokio::sync |
手动加锁 / unsafe |
| 类型转换 |
From<T> / Into<T> / TryFrom<T> |
手动 as 强转 |
| 条件编译 |
#[cfg(test)] / features |
运行时判断 |
| 资源管理 |
RAII(Drop 守卫) |
手动 close() / free() |
10. 部署方案
10.1 Docker
10.2 docker-compose
10.3 Nginx 反向代理