- 从服务器拉取完整代码 - 按框架规范整理项目结构 - 配置 Drone CI 测试环境部署 - 包含后端(FastAPI)、前端(Vue3)、管理端 技术栈: Vue3 + TypeScript + FastAPI + MySQL
381 lines
9.3 KiB
Markdown
381 lines
9.3 KiB
Markdown
# 考培练系统后端开发规范
|
||
|
||
## 1. 代码规范
|
||
|
||
### 1.1 Python代码规范
|
||
- **格式化工具**:使用 Black 进行代码格式化,配置行长度为 88
|
||
- **导入排序**:使用 isort 进行导入排序
|
||
- **类型检查**:使用 mypy 进行静态类型检查
|
||
- **代码检查**:使用 flake8 进行代码质量检查
|
||
|
||
### 1.2 命名规范
|
||
```python
|
||
# 文件名:使用小写字母和下划线
|
||
user_service.py
|
||
|
||
# 类名:使用大驼峰命名法
|
||
class UserService:
|
||
pass
|
||
|
||
# 函数和变量:使用小写字母和下划线
|
||
def get_user_by_id(user_id: int) -> User:
|
||
pass
|
||
|
||
# 常量:使用大写字母和下划线
|
||
MAX_RETRY_COUNT = 3
|
||
|
||
# 私有成员:使用单下划线前缀
|
||
def _validate_input(data: dict) -> bool:
|
||
pass
|
||
```
|
||
|
||
### 1.3 文档字符串规范
|
||
```python
|
||
def create_user(
|
||
username: str,
|
||
email: str,
|
||
password: str,
|
||
role: UserRole = UserRole.TRAINEE
|
||
) -> User:
|
||
"""
|
||
创建新用户
|
||
|
||
Args:
|
||
username: 用户名,长度3-20个字符
|
||
email: 用户邮箱,必须是有效的邮箱格式
|
||
password: 密码,最少8个字符
|
||
role: 用户角色,默认为学员
|
||
|
||
Returns:
|
||
User: 创建成功的用户对象
|
||
|
||
Raises:
|
||
ValidationError: 输入参数验证失败
|
||
DuplicateError: 用户名或邮箱已存在
|
||
"""
|
||
pass
|
||
```
|
||
|
||
## 2. API开发规范
|
||
|
||
### 2.1 RESTful API设计原则
|
||
- 使用名词而非动词
|
||
- 使用复数形式
|
||
- 使用标准HTTP方法
|
||
- 合理使用HTTP状态码
|
||
|
||
```python
|
||
# 好的例子
|
||
GET /api/v1/users # 获取用户列表
|
||
GET /api/v1/users/{id} # 获取单个用户
|
||
POST /api/v1/users # 创建用户
|
||
PUT /api/v1/users/{id} # 更新用户
|
||
DELETE /api/v1/users/{id} # 删除用户
|
||
|
||
# 错误的例子
|
||
GET /api/v1/getUsers # 不要使用动词
|
||
POST /api/v1/user/create # 不要在URL中包含动作
|
||
```
|
||
|
||
### 2.2 统一响应格式
|
||
```python
|
||
from typing import Optional, Any
|
||
from pydantic import BaseModel
|
||
|
||
class ResponseModel(BaseModel):
|
||
"""统一API响应模型"""
|
||
code: int = 200
|
||
message: str = "success"
|
||
data: Optional[Any] = None
|
||
|
||
class ErrorResponse(BaseModel):
|
||
"""错误响应模型"""
|
||
code: int
|
||
message: str
|
||
details: Optional[str] = None
|
||
trace_id: Optional[str] = None
|
||
|
||
# 成功响应示例
|
||
{
|
||
"code": 200,
|
||
"message": "success",
|
||
"data": {
|
||
"id": 1,
|
||
"username": "张三",
|
||
"email": "zhangsan@example.com"
|
||
}
|
||
}
|
||
|
||
# 错误响应示例
|
||
{
|
||
"code": 400,
|
||
"message": "参数验证失败",
|
||
"details": "用户名长度必须在3-20个字符之间",
|
||
"trace_id": "abc123def456"
|
||
}
|
||
```
|
||
|
||
### 2.3 API版本控制
|
||
- 使用URL路径进行版本控制:`/api/v1/`, `/api/v2/`
|
||
- 保持向后兼容,避免破坏性更改
|
||
- 废弃的API需要提前通知并设置过渡期
|
||
|
||
## 3. 数据库规范
|
||
|
||
### 3.1 表命名规范
|
||
- 使用复数形式:`users`, `courses`, `exams`
|
||
- 关联表使用下划线连接:`user_courses`, `exam_questions`
|
||
- 避免使用MySQL保留字
|
||
|
||
### 3.2 字段命名规范
|
||
- 使用蛇形命名法:`user_name`, `created_at`
|
||
- 主键统一使用 `id`
|
||
- 外键使用 `表名_id` 格式:`user_id`, `course_id`
|
||
|
||
### 3.3 必需字段
|
||
每个表必须包含以下字段:
|
||
```sql
|
||
id BIGINT PRIMARY KEY AUTO_INCREMENT -- 主键
|
||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP -- 创建时间
|
||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP -- 更新时间
|
||
created_by BIGINT -- 创建人ID(可选)
|
||
updated_by BIGINT -- 更新人ID(可选)
|
||
is_deleted BOOLEAN DEFAULT FALSE -- 软删除标记(如需要)
|
||
```
|
||
|
||
### 3.4 索引规范
|
||
- 为外键字段创建索引
|
||
- 为经常查询的字段创建索引
|
||
- 避免创建过多索引影响写入性能
|
||
- 索引命名:`idx_表名_字段名`
|
||
|
||
## 4. 异常处理规范
|
||
|
||
### 4.1 自定义异常类
|
||
```python
|
||
# app/core/exceptions.py
|
||
class BaseAPIException(Exception):
|
||
"""API异常基类"""
|
||
def __init__(
|
||
self,
|
||
code: int,
|
||
message: str,
|
||
details: str = None
|
||
):
|
||
self.code = code
|
||
self.message = message
|
||
self.details = details
|
||
super().__init__(message)
|
||
|
||
class ValidationError(BaseAPIException):
|
||
"""参数验证异常"""
|
||
def __init__(self, message: str, details: str = None):
|
||
super().__init__(400, message, details)
|
||
|
||
class NotFoundError(BaseAPIException):
|
||
"""资源不存在异常"""
|
||
def __init__(self, resource: str):
|
||
super().__init__(404, f"{resource}不存在")
|
||
|
||
class PermissionError(BaseAPIException):
|
||
"""权限不足异常"""
|
||
def __init__(self, message: str = "权限不足"):
|
||
super().__init__(403, message)
|
||
```
|
||
|
||
### 4.2 全局异常处理
|
||
```python
|
||
# app/core/middleware.py
|
||
from fastapi import Request
|
||
from fastapi.responses import JSONResponse
|
||
|
||
async def global_exception_handler(request: Request, exc: Exception):
|
||
"""全局异常处理器"""
|
||
if isinstance(exc, BaseAPIException):
|
||
return JSONResponse(
|
||
status_code=exc.code,
|
||
content={
|
||
"code": exc.code,
|
||
"message": exc.message,
|
||
"details": exc.details,
|
||
"trace_id": request.state.trace_id
|
||
}
|
||
)
|
||
|
||
# 记录未预期的异常
|
||
logger.error(f"未处理的异常: {exc}", exc_info=True)
|
||
return JSONResponse(
|
||
status_code=500,
|
||
content={
|
||
"code": 500,
|
||
"message": "服务器内部错误",
|
||
"trace_id": request.state.trace_id
|
||
}
|
||
)
|
||
```
|
||
|
||
## 5. 日志规范
|
||
|
||
### 5.1 日志级别使用
|
||
- **DEBUG**: 调试信息,仅在开发环境使用
|
||
- **INFO**: 一般信息,如API请求、重要业务操作
|
||
- **WARNING**: 警告信息,如已废弃的API调用
|
||
- **ERROR**: 错误信息,需要关注但不影响系统运行
|
||
- **CRITICAL**: 严重错误,可能导致系统无法正常运行
|
||
|
||
### 5.2 结构化日志
|
||
```python
|
||
import structlog
|
||
|
||
logger = structlog.get_logger()
|
||
|
||
# 使用结构化日志
|
||
logger.info(
|
||
"用户登录成功",
|
||
user_id=user.id,
|
||
username=user.username,
|
||
ip_address=request.client.host,
|
||
user_agent=request.headers.get("user-agent")
|
||
)
|
||
|
||
# 记录错误信息
|
||
logger.error(
|
||
"数据库连接失败",
|
||
error=str(e),
|
||
database_url=settings.DATABASE_URL,
|
||
retry_count=retry_count,
|
||
exc_info=True # 包含完整堆栈信息
|
||
)
|
||
```
|
||
|
||
### 5.3 日志格式配置
|
||
```python
|
||
# app/core/logger.py
|
||
import structlog
|
||
|
||
def configure_logging():
|
||
structlog.configure(
|
||
processors=[
|
||
structlog.stdlib.filter_by_level,
|
||
structlog.stdlib.add_logger_name,
|
||
structlog.stdlib.add_log_level,
|
||
structlog.stdlib.PositionalArgumentsFormatter(),
|
||
structlog.processors.TimeStamper(fmt="iso"),
|
||
structlog.processors.StackInfoRenderer(),
|
||
structlog.processors.format_exc_info,
|
||
structlog.processors.UnicodeDecoder(),
|
||
structlog.processors.JSONRenderer()
|
||
],
|
||
context_class=dict,
|
||
logger_factory=structlog.stdlib.LoggerFactory(),
|
||
cache_logger_on_first_use=True,
|
||
)
|
||
```
|
||
|
||
## 6. 测试规范
|
||
|
||
### 6.1 测试文件组织
|
||
```
|
||
tests/
|
||
├── unit/ # 单元测试
|
||
│ ├── test_services/
|
||
│ └── test_utils/
|
||
├── integration/ # 集成测试
|
||
│ ├── test_api/
|
||
│ └── test_database/
|
||
└── e2e/ # 端到端测试
|
||
└── test_workflows/
|
||
```
|
||
|
||
### 6.2 测试命名规范
|
||
```python
|
||
# 测试文件名:test_模块名.py
|
||
test_user_service.py
|
||
|
||
# 测试类名:Test + 被测试类名
|
||
class TestUserService:
|
||
pass
|
||
|
||
# 测试方法名:test_应该_做什么_当什么条件
|
||
def test_should_create_user_when_valid_data(self):
|
||
pass
|
||
|
||
def test_should_raise_error_when_duplicate_username(self):
|
||
pass
|
||
```
|
||
|
||
### 6.3 测试覆盖率要求
|
||
- 核心业务逻辑:≥ 90%
|
||
- API接口:≥ 80%
|
||
- 工具函数:≥ 70%
|
||
- 总体覆盖率:≥ 80%
|
||
|
||
## 7. Git提交规范
|
||
|
||
### 7.1 提交信息格式
|
||
```
|
||
<type>(<scope>): <subject>
|
||
|
||
<body>
|
||
|
||
<footer>
|
||
```
|
||
|
||
### 7.2 Type类型
|
||
- **feat**: 新功能
|
||
- **fix**: 修复bug
|
||
- **docs**: 文档更新
|
||
- **style**: 代码格式调整
|
||
- **refactor**: 代码重构
|
||
- **test**: 测试相关
|
||
- **chore**: 构建过程或辅助工具的变动
|
||
|
||
### 7.3 示例
|
||
```
|
||
feat(user): 添加用户批量导入功能
|
||
|
||
- 支持Excel文件导入
|
||
- 支持CSV文件导入
|
||
- 添加导入进度显示
|
||
- 添加错误信息导出
|
||
|
||
Closes #123
|
||
```
|
||
|
||
## 8. 代码审查清单
|
||
|
||
提交代码前,请确保:
|
||
|
||
### 基础检查
|
||
- [ ] 代码通过 Black 格式化
|
||
- [ ] 代码通过 isort 导入排序
|
||
- [ ] 代码通过 mypy 类型检查
|
||
- [ ] 代码通过 flake8 质量检查
|
||
|
||
### 功能检查
|
||
- [ ] 所有函数都有完整的文档字符串
|
||
- [ ] 所有函数都有类型注解
|
||
- [ ] API响应格式符合统一标准
|
||
- [ ] 错误处理使用统一的异常类
|
||
|
||
### 测试检查
|
||
- [ ] 编写了相应的单元测试
|
||
- [ ] 测试覆盖率符合要求
|
||
- [ ] 所有测试通过
|
||
|
||
### 安全检查
|
||
- [ ] 没有硬编码的密钥或密码
|
||
- [ ] SQL查询使用参数化查询
|
||
- [ ] 敏感操作有权限检查
|
||
|
||
### 性能检查
|
||
- [ ] 数据库查询使用了适当的索引
|
||
- [ ] 避免了N+1查询问题
|
||
- [ ] 大量数据处理使用了分页或流式处理
|
||
|
||
### 日志检查
|
||
- [ ] 关键操作有日志记录
|
||
- [ ] 错误信息包含完整的上下文
|
||
- [ ] 日志级别使用恰当
|
||
|