first commit
This commit is contained in:
459
docs/development/getting-started.md
Normal file
459
docs/development/getting-started.md
Normal file
@@ -0,0 +1,459 @@
|
||||
# 快速开始指南
|
||||
|
||||
本文档将指导你完成 Web Rust Template 项目的安装、配置和运行。
|
||||
|
||||
## 目录
|
||||
|
||||
- [环境要求](#环境要求)
|
||||
- [安装步骤](#安装步骤)
|
||||
- [配置说明](#配置说明)
|
||||
- [运行项目](#运行项目)
|
||||
- [验证安装](#验证安装)
|
||||
- [常见问题](#常见问题)
|
||||
|
||||
---
|
||||
|
||||
## 环境要求
|
||||
|
||||
### 必需环境
|
||||
|
||||
- **Rust**:1.70 或更高版本
|
||||
- 安装方法:访问 [rustup.rs](https://rustup.rs/) 或使用 `curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh`
|
||||
- **Git**:用于克隆项目
|
||||
|
||||
### 数据库(任选其一)
|
||||
|
||||
- **SQLite**:默认选项,无需额外安装
|
||||
- **MySQL**:5.7 或更高版本
|
||||
- **PostgreSQL**:12 或更高版本
|
||||
|
||||
### 可选环境
|
||||
|
||||
- **Redis**:用于存储 Refresh Token(推荐)
|
||||
- Windows:下载 [Redis for Windows](https://github.com/microsoftarchive/redis/releases)
|
||||
- macOS:`brew install redis`
|
||||
- Linux:`sudo apt-get install redis-server`
|
||||
|
||||
### 检查环境
|
||||
|
||||
```bash
|
||||
# 检查 Rust 版本
|
||||
rustc --version
|
||||
|
||||
# 检查 Cargo 版本
|
||||
cargo --version
|
||||
|
||||
# 检查 Git 版本
|
||||
git --version
|
||||
|
||||
# 检查 MySQL(如果使用)
|
||||
mysql --version
|
||||
|
||||
# 检查 PostgreSQL(如果使用)
|
||||
psql --version
|
||||
|
||||
# 检查 Redis(如果使用)
|
||||
redis-cli --version
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 安装步骤
|
||||
|
||||
### 1. 克隆项目
|
||||
|
||||
```bash
|
||||
git clone <repository-url>
|
||||
cd web-rust-template
|
||||
```
|
||||
|
||||
### 2. 安装依赖
|
||||
|
||||
使用 Cargo 构建项目(会自动下载依赖):
|
||||
|
||||
```bash
|
||||
cargo build
|
||||
```
|
||||
|
||||
### 3. 配置项目
|
||||
|
||||
#### 方式一:使用默认配置(SQLite,最简单)
|
||||
|
||||
**无需任何配置!** 直接运行即可:
|
||||
|
||||
```bash
|
||||
cargo run
|
||||
```
|
||||
|
||||
默认配置:
|
||||
- 数据库:SQLite(自动创建 `db.sqlite3`)
|
||||
- 服务器:`127.0.0.1:3000`
|
||||
- Redis:`localhost:6379`
|
||||
|
||||
#### 方式二:使用 MySQL/PostgreSQL
|
||||
|
||||
**步骤 1**:复制对应的配置文件
|
||||
|
||||
```bash
|
||||
# 使用 MySQL
|
||||
cp config/development.mysql.toml config/local.toml
|
||||
|
||||
# 或使用 PostgreSQL
|
||||
cp config/development.postgresql.toml config/local.toml
|
||||
```
|
||||
|
||||
**步骤 2**:修改配置文件
|
||||
|
||||
编辑 `config/local.toml`,修改数据库连接信息:
|
||||
|
||||
```toml
|
||||
[database]
|
||||
# MySQL 配置
|
||||
host = "localhost"
|
||||
port = 3306
|
||||
user = "root"
|
||||
password = "your-password"
|
||||
database = "web_template_dev"
|
||||
|
||||
# 或 PostgreSQL 配置
|
||||
# host = "localhost"
|
||||
# port = 5432
|
||||
# user = "postgres"
|
||||
# password = "your-password"
|
||||
# database = "web_template_dev"
|
||||
```
|
||||
|
||||
**步骤 3**:运行项目
|
||||
|
||||
```bash
|
||||
# 使用指定配置文件运行
|
||||
cargo run -- -c config/local.toml
|
||||
```
|
||||
|
||||
#### 方式三:通过环境变量覆盖(适用于 Docker/Kubernetes)
|
||||
|
||||
```bash
|
||||
# 使用环境变量
|
||||
DATABASE_TYPE=postgresql \
|
||||
DATABASE_HOST=localhost \
|
||||
DATABASE_PORT=5432 \
|
||||
DATABASE_USER=postgres \
|
||||
DATABASE_PASSWORD=password \
|
||||
DATABASE_DATABASE=web_template_dev \
|
||||
cargo run
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 配置说明
|
||||
|
||||
### 数据库配置
|
||||
|
||||
#### SQLite(默认,推荐用于开发)
|
||||
|
||||
**优点**:无需额外安装,文件存储,易于测试
|
||||
|
||||
**缺点**:不支持高并发写入
|
||||
|
||||
**适用场景**:开发环境、小型应用
|
||||
|
||||
**使用方法**:无需配置,直接运行
|
||||
|
||||
#### MySQL
|
||||
|
||||
**优点**:成熟稳定,支持高并发
|
||||
|
||||
**缺点**:需要额外安装和配置
|
||||
|
||||
**适用场景**:生产环境、大型应用
|
||||
|
||||
**配置方法**:
|
||||
|
||||
**选项 1**:修改配置文件
|
||||
|
||||
```bash
|
||||
# 复制 MySQL 配置模板
|
||||
cp config/development.mysql.toml config/local.toml
|
||||
|
||||
# 编辑 config/local.toml,修改数据库连接信息
|
||||
```
|
||||
|
||||
**选项 2**:使用环境变量
|
||||
|
||||
```bash
|
||||
DATABASE_TYPE=mysql \
|
||||
DATABASE_HOST=localhost \
|
||||
DATABASE_PORT=3306 \
|
||||
DATABASE_USER=root \
|
||||
DATABASE_PASSWORD=your-password \
|
||||
DATABASE_DATABASE=web_template_dev \
|
||||
cargo run
|
||||
```
|
||||
|
||||
#### PostgreSQL
|
||||
|
||||
**优点**:功能强大,支持高级特性
|
||||
|
||||
**缺点**:资源占用较大
|
||||
|
||||
**适用场景**:需要高级数据库功能的应用
|
||||
|
||||
**配置方法**:与 MySQL 类似,使用 `config/development.postgresql.toml` 或环境变量
|
||||
|
||||
### 认证配置
|
||||
|
||||
**开发环境**:使用默认配置即可(JWT 密钥已在配置文件中)
|
||||
|
||||
**生产环境**:必须修改配置文件中的 JWT 密钥
|
||||
|
||||
```toml
|
||||
[auth]
|
||||
# 生产环境必须使用强密钥
|
||||
jwt_secret = "Kx7Yn2Zp9qR8wF4tL6mN3vB5xC8zD1sE9aH2jK7"
|
||||
```
|
||||
|
||||
生成强密钥:
|
||||
```bash
|
||||
openssl rand -base64 32
|
||||
```
|
||||
|
||||
### Redis 配置
|
||||
|
||||
**开发环境**:默认连接 `localhost:6379`,无需配置
|
||||
|
||||
**生产环境**:修改配置文件或设置环境变量
|
||||
|
||||
```bash
|
||||
REDIS_HOST=your-redis-host \
|
||||
REDIS_PORT=6379 \
|
||||
REDIS_PASSWORD=your-password \
|
||||
cargo run
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 运行项目
|
||||
|
||||
### 开发模式
|
||||
|
||||
**使用默认配置(SQLite)**:
|
||||
```bash
|
||||
cargo run
|
||||
```
|
||||
|
||||
**使用指定配置文件**:
|
||||
```bash
|
||||
cargo run -- -c config/development.mysql.toml
|
||||
```
|
||||
|
||||
**使用环境变量**:
|
||||
```bash
|
||||
DATABASE_TYPE=mysql DATABASE_HOST=localhost cargo run
|
||||
```
|
||||
|
||||
### 指定环境
|
||||
|
||||
```bash
|
||||
# 开发环境
|
||||
cargo run -- -e development
|
||||
|
||||
# 生产环境
|
||||
cargo run -- -e production
|
||||
```
|
||||
|
||||
### 后台运行(生产环境)
|
||||
|
||||
```bash
|
||||
# 使用 nohup
|
||||
nohup cargo run -- -e production > app.log 2>&1 &
|
||||
|
||||
# 使用 screen
|
||||
screen -S web-rust-template
|
||||
cargo run -- -e production
|
||||
# 按 Ctrl+A 然后 D 分离会话
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 验证安装
|
||||
|
||||
### 1. 健康检查
|
||||
|
||||
```bash
|
||||
curl http://localhost:3000/health
|
||||
```
|
||||
|
||||
预期响应:
|
||||
```json
|
||||
{
|
||||
"status": "ok"
|
||||
}
|
||||
```
|
||||
|
||||
### 2. 服务器信息
|
||||
|
||||
```bash
|
||||
curl http://localhost:3000/info
|
||||
```
|
||||
|
||||
预期响应:
|
||||
```json
|
||||
{
|
||||
"name": "web-rust-template",
|
||||
"version": "0.1.0",
|
||||
"status": "running",
|
||||
"timestamp": 1704112800
|
||||
}
|
||||
```
|
||||
|
||||
### 3. 用户注册
|
||||
|
||||
```bash
|
||||
curl -X POST http://localhost:3000/auth/register \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"email": "test@example.com",
|
||||
"password": "password123"
|
||||
}'
|
||||
```
|
||||
|
||||
预期响应:
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"message": "Success",
|
||||
"data": {
|
||||
"email": "test@example.com",
|
||||
"created_at": "2026-02-13T12:00:00.000Z",
|
||||
"access_token": "eyJhbGciOiJIUzI1NiIs...",
|
||||
"refresh_token": "eyJhbGciOiJIUzI1NiIs..."
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 4. 用户登录
|
||||
|
||||
```bash
|
||||
curl -X POST http://localhost:3000/auth/login \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"email": "test@example.com",
|
||||
"password": "password123"
|
||||
}'
|
||||
```
|
||||
|
||||
预期响应:
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"message": "Success",
|
||||
"data": {
|
||||
"id": "1234567890",
|
||||
"email": "test@example.com",
|
||||
"created_at": "2026-02-13T12:00:00.000Z",
|
||||
"access_token": "eyJhbGciOiJIUzI1NiIs...",
|
||||
"refresh_token": "eyJhbGciOiJIUzI1NiIs..."
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 常见问题
|
||||
|
||||
### 1. 端口被占用
|
||||
|
||||
**错误信息**:`Os { code: 10048, kind: AddrInUse }` 或 `Address already in use`
|
||||
|
||||
**解决方案**:
|
||||
|
||||
**选项 1**:修改配置文件中的端口
|
||||
|
||||
```toml
|
||||
[server]
|
||||
port = 3001
|
||||
```
|
||||
|
||||
**选项 2**:通过环境变量覆盖
|
||||
|
||||
```bash
|
||||
SERVER_PORT=3001 cargo run
|
||||
```
|
||||
|
||||
**选项 3**:停止占用端口的进程
|
||||
|
||||
```bash
|
||||
# Windows
|
||||
netstat -ano | findstr :3000
|
||||
taskkill /PID <pid> /F
|
||||
|
||||
# macOS/Linux
|
||||
lsof -ti:3000 | xargs kill -9
|
||||
```
|
||||
|
||||
### 2. 数据库连接失败
|
||||
|
||||
**错误信息**:`Database connection failed`
|
||||
|
||||
**解决方案**:
|
||||
|
||||
- 检查数据库服务是否启动
|
||||
- 检查配置文件中的数据库配置是否正确
|
||||
- 确认数据库用户权限
|
||||
- SQLite:检查是否有写入权限
|
||||
|
||||
### 3. Redis 连接失败
|
||||
|
||||
**错误信息**:`Redis 连接失败`
|
||||
|
||||
**解决方案**:
|
||||
|
||||
- 检查 Redis 服务是否启动:`redis-cli ping`
|
||||
- 检查配置文件中的 Redis 配置是否正确
|
||||
- 如果不需要 Redis 功能,可以暂时禁用(需要修改代码)
|
||||
|
||||
### 4. 编译错误
|
||||
|
||||
**错误信息**:`error: linking with link.exe failed`
|
||||
|
||||
**解决方案**:
|
||||
|
||||
- Windows 用户需要安装 [C++ Build Tools](https://visualstudio.microsoft.com/visual-cpp-build-tools/)
|
||||
- 或使用 `cargo install cargo-vcpkg` 安装依赖
|
||||
|
||||
### 5. 权限错误
|
||||
|
||||
**错误信息**:`Permission denied`
|
||||
|
||||
**解决方案**:
|
||||
|
||||
```bash
|
||||
# Linux/macOS
|
||||
chmod +x target/debug/web-rust-template
|
||||
|
||||
# 或使用 sudo 运行(不推荐生产环境)
|
||||
sudo cargo run
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 下一步
|
||||
|
||||
安装成功后,你可以:
|
||||
|
||||
1. 阅读 [API 接口文档](../api/api-overview.md) 了解所有可用的 API
|
||||
2. 查看 [项目结构详解](project-structure.md) 了解代码组织
|
||||
3. 学习 [DDD 架构规范](ddd-architecture.md) 了解设计原则
|
||||
4. 参考 [前端集成示例](../api/examples/frontend-integration.md) 集成前端应用
|
||||
|
||||
---
|
||||
|
||||
## 相关文档
|
||||
|
||||
- [配置文件详解](../deployment/configuration.md) - 配置文件组织说明
|
||||
- [环境变量配置](../deployment/environment-variables.md) - 完整的环境变量列表
|
||||
- [API 接口文档](../api/api-overview.md) - 完整的 API 接口说明
|
||||
|
||||
---
|
||||
|
||||
**提示**:遇到问题?查看 [常见问题](#常见问题) 或提交 Issue 到项目仓库。
|
||||
615
docs/development/project-structure.md
Normal file
615
docs/development/project-structure.md
Normal file
@@ -0,0 +1,615 @@
|
||||
# 项目结构详解
|
||||
|
||||
本文档详细说明 Web Rust Template 的项目结构、DDD 分层架构和各层职责。
|
||||
|
||||
## 目录
|
||||
|
||||
- [DDD 分层架构](#ddd-分层架构)
|
||||
- [项目目录结构](#项目目录结构)
|
||||
- [各层职责说明](#各层职责说明)
|
||||
- [数据流转](#数据流转)
|
||||
- [核心组件](#核心组件)
|
||||
|
||||
---
|
||||
|
||||
## DDD 分层架构
|
||||
|
||||
本系统采用**领域驱动设计(DDD)**的分层架构,将代码划分为不同的职责层次。
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────┐
|
||||
│ Interface Layer (handlers) │ HTTP 处理器层
|
||||
│ 路由定义、请求处理、响应封装 │
|
||||
└──────────────┬───────────────────────┘
|
||||
│
|
||||
┌──────────────▼───────────────────────┐
|
||||
│ Application Layer (services) │ 业务逻辑层
|
||||
│ 业务逻辑、Token 生成、认证 │
|
||||
└──────────────┬───────────────────────┘
|
||||
│
|
||||
┌───────┴────────┐
|
||||
│ │
|
||||
┌──────▼──────┐ ┌─────▼──────────┐
|
||||
│ Domain │ │ Infrastructure│
|
||||
│ Layer │ │ Layer │
|
||||
│ │ │ │
|
||||
│ - DTO │ │ - Middleware │
|
||||
│ - Entities │ │ - Redis │
|
||||
│ - VO │ │ - Repositories│
|
||||
└─────────────┘ └────────────────┘
|
||||
```
|
||||
|
||||
### 分层优势
|
||||
|
||||
| 优势 | 说明 |
|
||||
|------|------|
|
||||
| 职责清晰 | 每层只关注自己的职责,降低耦合 |
|
||||
| 易于测试 | 每层可独立测试,Mock 依赖 |
|
||||
| 易于维护 | 修改某层不影响其他层 |
|
||||
| 易于扩展 | 添加新功能只需扩展相应层 |
|
||||
|
||||
---
|
||||
|
||||
## 项目目录结构
|
||||
|
||||
```
|
||||
web-rust-template/
|
||||
├── src/ # 源代码目录
|
||||
│ ├── main.rs # 应用入口
|
||||
│ ├── cli.rs # 命令行参数解析
|
||||
│ ├── config.rs # 配置模块导出
|
||||
│ ├── db.rs # 数据库连接池
|
||||
│ ├── error.rs # 错误处理
|
||||
│ │
|
||||
│ ├── config/ # 配置模块
|
||||
│ │ ├── app.rs # 主配置结构
|
||||
│ │ ├── auth.rs # 认证配置
|
||||
│ │ ├── database.rs # 数据库配置
|
||||
│ │ ├── redis.rs # Redis 配置
|
||||
│ │ └── server.rs # 服务器配置
|
||||
│ │
|
||||
│ ├── domain/ # 领域层(DDD)
|
||||
│ │ ├── dto/ # 数据传输对象(Data Transfer Object)
|
||||
│ │ │ └── auth.rs # 认证相关 DTO
|
||||
│ │ ├── entities/ # 实体(数据库模型)
|
||||
│ │ │ └── users.rs # 用户实体
|
||||
│ │ └── vo/ # 视图对象(View Object)
|
||||
│ │ └── auth.rs # 认证相关 VO
|
||||
│ │
|
||||
│ ├── handlers/ # HTTP 处理器层(接口层)
|
||||
│ │ ├── auth.rs # 认证接口
|
||||
│ │ └── health.rs # 健康检查接口
|
||||
│ │
|
||||
│ ├── infra/ # 基础设施层
|
||||
│ │ ├── middleware/ # 中间件
|
||||
│ │ │ ├── auth.rs # JWT 认证中间件
|
||||
│ │ │ └── logging.rs # 日志中间件
|
||||
│ │ └── redis/ # Redis 客户端封装
|
||||
│ │ ├── redis_client.rs
|
||||
│ │ └── redis_key.rs
|
||||
│ │
|
||||
│ ├── repositories/ # 数据访问层
|
||||
│ │ └── user_repository.rs # 用户数据访问
|
||||
│ │
|
||||
│ ├── services/ # 业务逻辑层
|
||||
│ │ └── auth_service.rs # 认证业务逻辑
|
||||
│ │
|
||||
│ └── utils/ # 工具函数
|
||||
│ └── jwt.rs # JWT 工具类
|
||||
│
|
||||
├── config/ # 配置文件目录
|
||||
│ ├── default.toml # 默认配置
|
||||
│ ├── development.sqlite.toml # SQLite 开发环境配置
|
||||
│ ├── development.mysql.toml # MySQL 开发环境配置
|
||||
│ ├── development.postgresql.toml # PostgreSQL 开发环境配置
|
||||
│ └── production.toml # 生产环境配置
|
||||
│
|
||||
├── sql/ # SQL 脚本
|
||||
│ └── init.sql # 数据库初始化脚本
|
||||
│
|
||||
├── tests/ # 测试目录
|
||||
│ └── integration_test.rs # 集成测试
|
||||
│
|
||||
├── docs/ # 文档目录
|
||||
│ ├── README.md
|
||||
│ ├── api/
|
||||
│ ├── development/
|
||||
│ └── deployment/
|
||||
│
|
||||
├── .env.example # 环境变量参考(仅用于 Docker/Kubernetes 等部署场景)
|
||||
├── .gitignore # Git 忽略文件
|
||||
├── Cargo.toml # 项目依赖定义
|
||||
├── README.md # 项目说明
|
||||
└── rust-toolchain.toml # Rust 工具链配置
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 各层职责说明
|
||||
|
||||
### 1. 接口层(handlers/)
|
||||
|
||||
**职责**:处理 HTTP 请求和响应
|
||||
|
||||
**位置**:`src/handlers/`
|
||||
|
||||
**关键文件**:
|
||||
- `auth.rs`:认证相关接口(注册、登录、刷新 Token、删除账号)
|
||||
- `health.rs`:健康检查和服务器信息接口
|
||||
|
||||
**示例**:
|
||||
```rust
|
||||
// src/handlers/auth.rs
|
||||
|
||||
pub async fn register(
|
||||
Extension(request_id): Extension<RequestId>,
|
||||
State(state): State<AppState>,
|
||||
Json(payload): Json<RegisterRequest>,
|
||||
) -> Result<Json<ApiResponse<RegisterResult>>, ErrorResponse> {
|
||||
// 1. 记录日志
|
||||
log_info(&request_id, "注册请求参数", &payload);
|
||||
|
||||
// 2. 调用服务层处理业务逻辑
|
||||
let user_repo = UserRepository::new(state.pool.clone());
|
||||
let service = AuthService::new(user_repo, state.redis_client.clone(), state.config.auth.clone());
|
||||
|
||||
// 3. 调用业务逻辑
|
||||
match service.register(payload).await {
|
||||
Ok((user_model, access_token, refresh_token)) => {
|
||||
let data = RegisterResult::from((user_model, access_token, refresh_token));
|
||||
let response = ApiResponse::success(data);
|
||||
log_info(&request_id, "注册成功", &response);
|
||||
Ok(Json(response))
|
||||
}
|
||||
Err(e) => {
|
||||
log_info(&request_id, "注册失败", &e.to_string());
|
||||
Err(ErrorResponse::new(e.to_string()))
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**职责边界**:
|
||||
- ✅ 接收 HTTP 请求
|
||||
- ✅ 提取请求参数
|
||||
- ✅ 调用服务层处理业务逻辑
|
||||
- ✅ 封装响应数据
|
||||
- ❌ 不包含业务逻辑
|
||||
- ❌ 不直接访问数据库
|
||||
|
||||
### 2. 业务逻辑层(services/)
|
||||
|
||||
**职责**:实现核心业务逻辑
|
||||
|
||||
**位置**:`src/services/`
|
||||
|
||||
**关键文件**:
|
||||
- `auth_service.rs`:认证业务逻辑(注册、登录、Token 刷新、密码哈希)
|
||||
|
||||
**示例**:
|
||||
```rust
|
||||
// src/services/auth_service.rs
|
||||
|
||||
pub struct AuthService {
|
||||
user_repo: UserRepository,
|
||||
redis_client: RedisClient,
|
||||
auth_config: AuthConfig,
|
||||
}
|
||||
|
||||
impl AuthService {
|
||||
/// 用户注册
|
||||
pub async fn register(&self, payload: RegisterRequest) -> Result<(Model, String, String)> {
|
||||
// 1. 验证邮箱格式
|
||||
if !payload.email.contains('@') {
|
||||
return Err(anyhow!("邮箱格式错误"));
|
||||
}
|
||||
|
||||
// 2. 生成唯一用户 ID
|
||||
let user_id = self.generate_unique_user_id().await?;
|
||||
|
||||
// 3. 哈希密码
|
||||
let password_hash = self.hash_password(&payload.password)?;
|
||||
|
||||
// 4. 创建用户实体
|
||||
let user_model = users::Model {
|
||||
id: user_id,
|
||||
email: payload.email.clone(),
|
||||
password_hash,
|
||||
created_at: chrono::Utc::now().naive_utc(),
|
||||
updated_at: chrono::Utc::now().naive_utc(),
|
||||
};
|
||||
|
||||
// 5. 保存到数据库
|
||||
let created_user = self.user_repo.create(user_model).await?;
|
||||
|
||||
// 6. 生成 Token
|
||||
let (access_token, refresh_token) = TokenService::generate_token_pair(
|
||||
&created_user.id,
|
||||
self.auth_config.access_token_expiration_minutes,
|
||||
self.auth_config.refresh_token_expiration_days,
|
||||
&self.auth_config.jwt_secret,
|
||||
)?;
|
||||
|
||||
// 7. 保存 Refresh Token 到 Redis
|
||||
self.save_refresh_token(&created_user.id, &refresh_token, self.auth_config.refresh_token_expiration_days).await?;
|
||||
|
||||
Ok((created_user, access_token, refresh_token))
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**职责边界**:
|
||||
- ✅ 实现业务逻辑
|
||||
- ✅ 协调 Repository 和基础设施
|
||||
- ✅ 事务管理
|
||||
- ❌ 不处理 HTTP 请求/响应
|
||||
- ❌ 不直接访问外部资源(通过 Repository)
|
||||
|
||||
### 3. 数据访问层(repositories/)
|
||||
|
||||
**职责**:封装数据库访问逻辑
|
||||
|
||||
**位置**:`src/repositories/`
|
||||
|
||||
**关键文件**:
|
||||
- `user_repository.rs`:用户数据访问(增删改查)
|
||||
|
||||
**示例**:
|
||||
```rust
|
||||
// src/repositories/user_repository.rs
|
||||
|
||||
pub struct UserRepository {
|
||||
pool: DbPool,
|
||||
}
|
||||
|
||||
impl UserRepository {
|
||||
pub fn new(pool: DbPool) -> Self {
|
||||
Self { pool }
|
||||
}
|
||||
|
||||
/// 创建用户
|
||||
pub async fn create(&self, user_model: users::Model) -> Result<users::Model> {
|
||||
let result = users::Entity::insert(user_model.into_active_model())
|
||||
.exec(&self.pool)
|
||||
.await
|
||||
.map_err(|e| anyhow!("创建用户失败: {}", e))?;
|
||||
|
||||
Ok(users::Entity::find_by_id(result.last_insert_id))
|
||||
.one(&self.pool)
|
||||
.await
|
||||
.map_err(|e| anyhow!("查询用户失败: {}", e))?
|
||||
.ok_or_else(|| anyhow!("用户不存在"))
|
||||
}
|
||||
|
||||
/// 根据邮箱查询用户
|
||||
pub async fn find_by_email(&self, email: &str) -> Result<Option<users::Model>> {
|
||||
Ok(users::Entity::find()
|
||||
.filter(users::Column::Email.eq(email))
|
||||
.one(&self.pool)
|
||||
.await?)
|
||||
}
|
||||
|
||||
/// 根据ID查询用户
|
||||
pub async fn find_by_id(&self, id: &str) -> Result<Option<users::Model>> {
|
||||
Ok(users::Entity::find_by_id(id.to_string())
|
||||
.one(&self.pool)
|
||||
.await?)
|
||||
}
|
||||
|
||||
/// 统计相同ID的用户数量
|
||||
pub async fn count_by_id(&self, id: &str) -> Result<u64> {
|
||||
Ok(users::Entity::find_by_id(id.to_string())
|
||||
.count(&self.pool)
|
||||
.await?)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**职责边界**:
|
||||
- ✅ 数据库 CRUD 操作
|
||||
- ✅ 封装 SeaORM 细节
|
||||
- ❌ 不包含业务逻辑
|
||||
- ❌ 不处理 HTTP 请求
|
||||
|
||||
### 4. 领域层(domain/)
|
||||
|
||||
**职责**:定义核心业务模型
|
||||
|
||||
**位置**:`src/domain/`
|
||||
|
||||
#### DTO(Data Transfer Object)
|
||||
|
||||
**职责**:定义 API 请求和响应的数据结构
|
||||
|
||||
**位置**:`src/domain/dto/`
|
||||
|
||||
**示例**:
|
||||
```rust
|
||||
// src/domain/dto/auth.rs
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct RegisterRequest {
|
||||
pub email: String,
|
||||
pub password: String,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct LoginRequest {
|
||||
pub email: String,
|
||||
pub password: String,
|
||||
}
|
||||
```
|
||||
|
||||
#### Entities(实体)
|
||||
|
||||
**职责**:定义数据库表模型
|
||||
|
||||
**位置**:`src/domain/entities/`
|
||||
|
||||
**示例**:
|
||||
```rust
|
||||
// src/domain/entities/users.rs
|
||||
|
||||
use sea_orm::entity::prelude::*;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, DeriveEntityModel)]
|
||||
#[sea_orm(table_name = "users")]
|
||||
pub struct Model {
|
||||
#[sea_orm(primary_key)]
|
||||
pub id: String,
|
||||
#[sea_orm(column_type = "Text", unique)]
|
||||
pub email: String,
|
||||
pub password_hash: String,
|
||||
pub created_at: DateTime,
|
||||
pub updated_at: DateTime,
|
||||
}
|
||||
```
|
||||
|
||||
#### VO(View Object)
|
||||
|
||||
**职责**:定义 API 响应的数据结构
|
||||
|
||||
**位置**:`src/domain/vo/`
|
||||
|
||||
**示例**:
|
||||
```rust
|
||||
// src/domain/vo/auth.rs
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
pub struct RegisterResult {
|
||||
pub email: String,
|
||||
pub created_at: String,
|
||||
pub access_token: String,
|
||||
pub refresh_token: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
pub struct LoginResult {
|
||||
pub id: String,
|
||||
pub email: String,
|
||||
pub created_at: String,
|
||||
pub access_token: String,
|
||||
pub refresh_token: String,
|
||||
}
|
||||
```
|
||||
|
||||
### 5. 基础设施层(infra/)
|
||||
|
||||
**职责**:提供技术基础设施
|
||||
|
||||
**位置**:`src/infra/`
|
||||
|
||||
#### 中间件(middleware/)
|
||||
|
||||
**职责**:请求拦截和处理
|
||||
|
||||
**位置**:`src/infra/middleware/`
|
||||
|
||||
**关键文件**:
|
||||
- `auth.rs`:JWT 认证中间件
|
||||
- `logging.rs`:日志中间件
|
||||
|
||||
**示例**:
|
||||
```rust
|
||||
// src/infra/middleware/auth.rs
|
||||
|
||||
pub async fn auth_middleware(
|
||||
State(state): State<AppState>,
|
||||
mut request: Request,
|
||||
next: Next,
|
||||
) -> Result<Response, ErrorResponse> {
|
||||
// 1. 提取 Authorization header
|
||||
let auth_header = request
|
||||
.headers()
|
||||
.get("Authorization")
|
||||
.and_then(|h| h.to_str().ok())
|
||||
.ok_or_else(|| ErrorResponse::new("缺少 Authorization header".to_string()))?;
|
||||
|
||||
// 2. 验证 Bearer 格式
|
||||
if !auth_header.starts_with("Bearer ") {
|
||||
return Err(ErrorResponse::new("Authorization header 格式错误".to_string()));
|
||||
}
|
||||
|
||||
let token = &auth_header[7..];
|
||||
|
||||
// 3. 验证 JWT
|
||||
let claims = TokenService::decode_user_id(token, &state.config.auth.jwt_secret)?;
|
||||
|
||||
// 4. 将 user_id 添加到请求扩展
|
||||
request.extensions_mut().insert(claims.sub);
|
||||
|
||||
// 5. 继续处理请求
|
||||
Ok(next.run(request).await)
|
||||
}
|
||||
```
|
||||
|
||||
#### Redis 客户端(redis/)
|
||||
|
||||
**职责**:封装 Redis 操作
|
||||
|
||||
**位置**:`src/infra/redis/`
|
||||
|
||||
**关键文件**:
|
||||
- `redis_client.rs`:Redis 客户端封装
|
||||
- `redis_key.rs`:Redis Key 命名规范
|
||||
|
||||
### 6. 工具层(utils/)
|
||||
|
||||
**职责**:提供通用工具函数
|
||||
|
||||
**位置**:`src/utils/`
|
||||
|
||||
**关键文件**:
|
||||
- `jwt.rs`:JWT Token 生成和验证
|
||||
|
||||
**示例**:
|
||||
```rust
|
||||
// src/utils/jwt.rs
|
||||
|
||||
pub struct TokenService;
|
||||
|
||||
impl TokenService {
|
||||
/// 生成 Access Token
|
||||
pub fn generate_access_token(
|
||||
user_id: &str,
|
||||
expiration_minutes: u64,
|
||||
jwt_secret: &str,
|
||||
) -> Result<String> {
|
||||
// ... 生成 JWT Token
|
||||
}
|
||||
|
||||
/// 验证 Token 并提取 user_id
|
||||
pub fn decode_user_id(token: &str, jwt_secret: &str) -> Result<String> {
|
||||
// ... 验证并解码 JWT Token
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 数据流转
|
||||
|
||||
### 用户注册流程
|
||||
|
||||
```
|
||||
1. 客户端发起 POST /auth/register 请求
|
||||
↓
|
||||
2. handlers/auth.rs::register() 接收请求
|
||||
- 提取请求参数(RegisterRequest)
|
||||
↓
|
||||
3. services/auth_service.rs::register() 处理业务逻辑
|
||||
- 验证邮箱格式
|
||||
- 生成唯一用户 ID
|
||||
- 哈希密码
|
||||
- 创建用户实体
|
||||
↓
|
||||
4. repositories/user_repository.rs::create() 保存到数据库
|
||||
- 使用 SeaORM 插入数据
|
||||
↓
|
||||
5. services/auth_service.rs 生成 Token
|
||||
- 生成 Access Token
|
||||
- 生成 Refresh Token
|
||||
↓
|
||||
6. Redis 保存 Refresh Token
|
||||
↓
|
||||
7. handlers/auth.rs 封装响应(RegisterResult)
|
||||
↓
|
||||
8. 返回 JSON 响应给客户端
|
||||
```
|
||||
|
||||
### 访问受保护接口流程
|
||||
|
||||
```
|
||||
1. 客户端发起 POST /auth/delete 请求
|
||||
- 携带 Authorization: Bearer <access_token>
|
||||
↓
|
||||
2. infra/middleware/auth.rs::auth_middleware() 拦截
|
||||
- 验证 Token 格式
|
||||
- 验证 JWT 签名
|
||||
- 检查 Token 过期时间
|
||||
- 提取 user_id 并添加到请求扩展
|
||||
↓
|
||||
3. handlers/auth.rs::delete_account() 接收请求
|
||||
- 从扩展中提取 user_id
|
||||
↓
|
||||
4. services/auth_service.rs::delete_account() 处理业务逻辑
|
||||
- 验证密码
|
||||
- 调用 Repository 删除用户
|
||||
↓
|
||||
5. repositories/user_repository.rs::delete() 删除数据库记录
|
||||
↓
|
||||
6. 返回响应
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 核心组件
|
||||
|
||||
### AppState
|
||||
|
||||
**职责**:应用全局状态
|
||||
|
||||
**位置**:`src/main.rs`
|
||||
|
||||
```rust
|
||||
#[derive(Clone)]
|
||||
pub struct AppState {
|
||||
pub pool: db::DbPool, // 数据库连接池
|
||||
pub config: config::app::AppConfig, // 应用配置
|
||||
pub redis_client: infra::redis::redis_client::RedisClient, // Redis 客户端
|
||||
}
|
||||
```
|
||||
|
||||
**用途**:
|
||||
- 通过 Axum State 机制注入到所有处理器
|
||||
- 提供数据库访问
|
||||
- 提供配置信息
|
||||
- 提供 Redis 访问
|
||||
|
||||
### 路由配置
|
||||
|
||||
**位置**:`src/main.rs`
|
||||
|
||||
```rust
|
||||
// 公开路由
|
||||
let public_routes = Router::new()
|
||||
.route("/health", get(handlers::health::health_check))
|
||||
.route("/info", get(handlers::health::server_info))
|
||||
.route("/auth/register", post(handlers::auth::register))
|
||||
.route("/auth/login", post(handlers::auth::login))
|
||||
.route("/auth/refresh", post(handlers::auth::refresh));
|
||||
|
||||
// 受保护路由
|
||||
let protected_routes = Router::new()
|
||||
.route("/auth/delete", post(handlers::auth::delete_account))
|
||||
.route("//auth/delete-refresh-token", post(handlers::auth::delete_refresh_token))
|
||||
.route_layer(axum::middleware::from_fn_with_state(
|
||||
app_state.clone(),
|
||||
infra::middleware::auth::auth_middleware,
|
||||
));
|
||||
|
||||
// 合并所有路由
|
||||
let app = public_routes
|
||||
.merge(protected_routes)
|
||||
.layer(
|
||||
CorsLayer::new()
|
||||
.allow_origin(Any)
|
||||
.allow_methods(Any)
|
||||
.allow_headers(Any),
|
||||
)
|
||||
.layer(axum::middleware::from_fn(
|
||||
infra::middleware::logging::logging_middleware,
|
||||
));
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 相关文档
|
||||
|
||||
- [DDD 架构规范](ddd-architecture.md) - DDD 设计原则和最佳实践
|
||||
- [代码风格规范](code-style.md) - Rust 代码风格和命名规范
|
||||
- [快速开始指南](getting-started.md) - 安装和运行项目
|
||||
|
||||
---
|
||||
|
||||
**提示**:遵循 DDD 分层架构可以提高代码质量和可维护性。
|
||||
Reference in New Issue
Block a user