save code
This commit is contained in:
@@ -0,0 +1,838 @@
|
||||
# 后端API文档
|
||||
|
||||
## 文档信息
|
||||
- **文档版本**: V1.0
|
||||
- **创建日期**: 2026-01-26
|
||||
- **文档类型**: 后端API文档
|
||||
|
||||
---
|
||||
|
||||
## 1. API概述
|
||||
|
||||
### 1.1 基础信息
|
||||
|
||||
- **API前缀**: `/api`
|
||||
- **API文档地址**: `/docs`
|
||||
- **认证方式**: JWT Token (Bearer Token)
|
||||
- **响应格式**: JSON
|
||||
- **错误处理**: 统一错误响应格式
|
||||
|
||||
### 1.2 响应格式
|
||||
|
||||
#### 成功响应
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"message": "操作成功",
|
||||
"data": {...}
|
||||
}
|
||||
```
|
||||
|
||||
#### 错误响应
|
||||
```json
|
||||
{
|
||||
"code": 错误码,
|
||||
"message": "错误信息"
|
||||
}
|
||||
```
|
||||
|
||||
### 1.3 错误码
|
||||
|
||||
| 错误码 | 说明 |
|
||||
|--------|------|
|
||||
| 400 | 请求参数错误 |
|
||||
| 401 | 未授权 |
|
||||
| 403 | 禁止访问 |
|
||||
| 404 | 资源不存在 |
|
||||
| 500 | 服务器内部错误 |
|
||||
| 1001 | 用户名或密码错误 |
|
||||
| 1002 | 用户不存在 |
|
||||
| 1003 | 用户名已存在 |
|
||||
| 1004 | 密码不一致 |
|
||||
| 2001 | 项目不存在 |
|
||||
| 2002 | 无权限操作 |
|
||||
|
||||
---
|
||||
|
||||
## 2. 认证API
|
||||
|
||||
### 2.1 登录
|
||||
|
||||
**接口地址**: `/api/auth/login`
|
||||
|
||||
**请求方法**: `POST`
|
||||
|
||||
**请求参数**:
|
||||
|
||||
| 参数名 | 类型 | 位置 | 必填 | 说明 |
|
||||
|--------|------|------|------|------|
|
||||
| username | string | body | 是 | 用户名 |
|
||||
| password | string | body | 是 | 密码 |
|
||||
|
||||
**请求示例**:
|
||||
```json
|
||||
{
|
||||
"username": "admin",
|
||||
"password": "123456"
|
||||
}
|
||||
```
|
||||
|
||||
**响应示例**:
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"message": "登录成功",
|
||||
"data": {
|
||||
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
|
||||
"userInfo": {
|
||||
"userId": "admin001",
|
||||
"username": "admin",
|
||||
"realName": "系统管理员",
|
||||
"department": "ADMIN",
|
||||
"role": "ADMIN"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 2.2 退出登录
|
||||
|
||||
**接口地址**: `/api/auth/logout`
|
||||
|
||||
**请求方法**: `POST`
|
||||
|
||||
**请求参数**: 无
|
||||
|
||||
**响应示例**:
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"message": "退出成功"
|
||||
}
|
||||
```
|
||||
|
||||
### 2.3 获取用户信息
|
||||
|
||||
**接口地址**: `/api/auth/userInfo`
|
||||
|
||||
**请求方法**: `GET`
|
||||
|
||||
**请求参数**: 无
|
||||
|
||||
**响应示例**:
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"message": "查询成功",
|
||||
"data": {
|
||||
"userId": "admin001",
|
||||
"username": "admin",
|
||||
"realName": "系统管理员",
|
||||
"department": "ADMIN",
|
||||
"role": "ADMIN"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. 用户API
|
||||
|
||||
### 3.1 创建用户
|
||||
|
||||
**接口地址**: `/api/user`
|
||||
|
||||
**请求方法**: `POST`
|
||||
|
||||
**请求参数**:
|
||||
|
||||
| 参数名 | 类型 | 位置 | 必填 | 说明 |
|
||||
|--------|------|------|------|------|
|
||||
| username | string | body | 是 | 用户名 |
|
||||
| password | string | body | 是 | 密码 |
|
||||
| realName | string | body | 是 | 真实姓名 |
|
||||
| department | string | body | 是 | 部门 |
|
||||
| phone | string | body | 否 | 联系电话 |
|
||||
| email | string | body | 否 | 邮箱 |
|
||||
| role | string | body | 是 | 角色 |
|
||||
|
||||
**请求示例**:
|
||||
```json
|
||||
{
|
||||
"username": "test",
|
||||
"password": "123456",
|
||||
"realName": "测试用户",
|
||||
"department": "技术部",
|
||||
"phone": "13800138000",
|
||||
"email": "test@example.com",
|
||||
"role": "OTHER"
|
||||
}
|
||||
```
|
||||
|
||||
**响应示例**:
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"message": "创建成功"
|
||||
}
|
||||
```
|
||||
|
||||
### 3.2 更新用户
|
||||
|
||||
**接口地址**: `/api/user/{id}`
|
||||
|
||||
**请求方法**: `PUT`
|
||||
|
||||
**请求参数**:
|
||||
|
||||
| 参数名 | 类型 | 位置 | 必填 | 说明 |
|
||||
|--------|------|------|------|------|
|
||||
| id | string | path | 是 | 用户ID |
|
||||
| realName | string | body | 否 | 真实姓名 |
|
||||
| department | string | body | 否 | 部门 |
|
||||
| phone | string | body | 否 | 联系电话 |
|
||||
| email | string | body | 否 | 邮箱 |
|
||||
| role | string | body | 否 | 角色 |
|
||||
|
||||
**请求示例**:
|
||||
```json
|
||||
{
|
||||
"realName": "测试用户1",
|
||||
"department": "市场部",
|
||||
"role": "MARKETING"
|
||||
}
|
||||
```
|
||||
|
||||
**响应示例**:
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"message": "更新成功"
|
||||
}
|
||||
```
|
||||
|
||||
### 3.3 删除用户
|
||||
|
||||
**接口地址**: `/api/user/{id}`
|
||||
|
||||
**请求方法**: `DELETE`
|
||||
|
||||
**请求参数**:
|
||||
|
||||
| 参数名 | 类型 | 位置 | 必填 | 说明 |
|
||||
|--------|------|------|------|------|
|
||||
| id | string | path | 是 | 用户ID |
|
||||
|
||||
**响应示例**:
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"message": "删除成功"
|
||||
}
|
||||
```
|
||||
|
||||
### 3.4 获取用户列表
|
||||
|
||||
**接口地址**: `/api/user`
|
||||
|
||||
**请求方法**: `GET`
|
||||
|
||||
**请求参数**:
|
||||
|
||||
| 参数名 | 类型 | 位置 | 必填 | 说明 |
|
||||
|--------|------|------|------|------|
|
||||
| page | integer | query | 否 | 页码,默认1 |
|
||||
| size | integer | query | 否 | 每页数量,默认10 |
|
||||
| username | string | query | 否 | 用户名(模糊查询) |
|
||||
| department | string | query | 否 | 部门 |
|
||||
| role | string | query | 否 | 角色 |
|
||||
|
||||
**响应示例**:
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"message": "查询成功",
|
||||
"data": [
|
||||
{
|
||||
"userId": "admin001",
|
||||
"username": "admin",
|
||||
"realName": "系统管理员",
|
||||
"department": "ADMIN",
|
||||
"role": "ADMIN",
|
||||
"status": 1,
|
||||
"createTime": "2025-01-01 00:00:00",
|
||||
"lastLoginTime": "2025-01-15 10:00:00"
|
||||
}
|
||||
],
|
||||
"total": 1
|
||||
}
|
||||
```
|
||||
|
||||
### 3.5 重置密码
|
||||
|
||||
**接口地址**: `/api/user/resetPassword/{id}`
|
||||
|
||||
**请求方法**: `PUT`
|
||||
|
||||
**请求参数**:
|
||||
|
||||
| 参数名 | 类型 | 位置 | 必填 | 说明 |
|
||||
|--------|------|------|------|------|
|
||||
| id | string | path | 是 | 用户ID |
|
||||
| password | string | body | 是 | 新密码 |
|
||||
|
||||
**请求示例**:
|
||||
```json
|
||||
{
|
||||
"password": "123456"
|
||||
}
|
||||
```
|
||||
|
||||
**响应示例**:
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"message": "密码重置成功"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. 项目API
|
||||
|
||||
### 4.1 创建项目
|
||||
|
||||
**接口地址**: `/api/project`
|
||||
|
||||
**请求方法**: `POST`
|
||||
|
||||
**请求参数**:
|
||||
|
||||
| 参数名 | 类型 | 位置 | 必填 | 说明 |
|
||||
|--------|------|------|------|------|
|
||||
| projectName | string | body | 是 | 项目名称 |
|
||||
| status | string | body | 是 | 项目状态 |
|
||||
| leader | string | body | 是 | 负责人 |
|
||||
| phone | string | body | 否 | 联系电话 |
|
||||
| email | string | body | 否 | 邮箱 |
|
||||
| background | string | body | 否 | 项目背景 |
|
||||
| goal | string | body | 否 | 项目目标 |
|
||||
| scope | string | body | 否 | 项目范围 |
|
||||
| startDate | string | body | 是 | 开始日期 |
|
||||
| plannedEndDate | string | body | 是 | 预计结束日期 |
|
||||
| actualEndDate | string | body | 否 | 实际结束日期 |
|
||||
| totalBudget | number | body | 是 | 总预算 |
|
||||
| usedBudget | number | body | 否 | 已使用预算 |
|
||||
| members | array | body | 否 | 项目成员 |
|
||||
| milestones | array | body | 否 | 项目里程碑 |
|
||||
| risks | array | body | 否 | 项目风险 |
|
||||
| remarks | string | body | 否 | 备注 |
|
||||
|
||||
**请求示例**:
|
||||
```json
|
||||
{
|
||||
"projectName": "测试项目",
|
||||
"status": "NOT_STARTED",
|
||||
"leader": "张三",
|
||||
"phone": "13800138000",
|
||||
"email": "zhangsan@example.com",
|
||||
"background": "为了提升企业运营效率",
|
||||
"goal": "完成企业内部管理系统的全面数字化改造",
|
||||
"scope": "包括人力资源、财务管理、供应链管理等模块",
|
||||
"startDate": "2025-01-15",
|
||||
"plannedEndDate": "2025-06-30",
|
||||
"totalBudget": 500000,
|
||||
"usedBudget": 0,
|
||||
"members": [
|
||||
{
|
||||
"name": "张三",
|
||||
"role": "项目经理",
|
||||
"department": "市场部"
|
||||
},
|
||||
{
|
||||
"name": "李四",
|
||||
"role": "技术负责人",
|
||||
"department": "技术部"
|
||||
}
|
||||
],
|
||||
"milestones": [
|
||||
{
|
||||
"name": "需求分析完成",
|
||||
"plannedDate": "2025-02-15",
|
||||
"status": "NOT_STARTED"
|
||||
},
|
||||
{
|
||||
"name": "系统设计完成",
|
||||
"plannedDate": "2025-03-15",
|
||||
"status": "NOT_STARTED"
|
||||
}
|
||||
],
|
||||
"risks": [
|
||||
{
|
||||
"description": "技术难度较高,可能影响进度",
|
||||
"level": "HIGH",
|
||||
"measure": "增加技术团队人员投入"
|
||||
}
|
||||
],
|
||||
"remarks": "项目需要与现有系统进行数据对接"
|
||||
}
|
||||
```
|
||||
|
||||
**响应示例**:
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"message": "创建成功",
|
||||
"data": {
|
||||
"projectId": "PRJ2025001"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 4.2 更新项目
|
||||
|
||||
**接口地址**: `/api/project/{id}`
|
||||
|
||||
**请求方法**: `PUT`
|
||||
|
||||
**请求参数**:
|
||||
|
||||
| 参数名 | 类型 | 位置 | 必填 | 说明 |
|
||||
|--------|------|------|------|------|
|
||||
| id | string | path | 是 | 项目ID |
|
||||
| projectName | string | body | 否 | 项目名称 |
|
||||
| status | string | body | 否 | 项目状态 |
|
||||
| leader | string | body | 否 | 负责人 |
|
||||
| phone | string | body | 否 | 联系电话 |
|
||||
| email | string | body | 否 | 邮箱 |
|
||||
| background | string | body | 否 | 项目背景 |
|
||||
| goal | string | body | 否 | 项目目标 |
|
||||
| scope | string | body | 否 | 项目范围 |
|
||||
| startDate | string | body | 否 | 开始日期 |
|
||||
| plannedEndDate | string | body | 否 | 预计结束日期 |
|
||||
| actualEndDate | string | body | 否 | 实际结束日期 |
|
||||
| totalBudget | number | body | 否 | 总预算 |
|
||||
| usedBudget | number | body | 否 | 已使用预算 |
|
||||
| members | array | body | 否 | 项目成员 |
|
||||
| milestones | array | body | 否 | 项目里程碑 |
|
||||
| risks | array | body | 否 | 项目风险 |
|
||||
| remarks | string | body | 否 | 备注 |
|
||||
|
||||
**请求示例**:
|
||||
```json
|
||||
{
|
||||
"projectName": "测试项目1",
|
||||
"status": "IN_PROGRESS",
|
||||
"usedBudget": 100000
|
||||
}
|
||||
```
|
||||
|
||||
**响应示例**:
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"message": "更新成功"
|
||||
}
|
||||
```
|
||||
|
||||
### 4.3 删除项目
|
||||
|
||||
**接口地址**: `/api/project/{id}`
|
||||
|
||||
**请求方法**: `DELETE`
|
||||
|
||||
**请求参数**:
|
||||
|
||||
| 参数名 | 类型 | 位置 | 必填 | 说明 |
|
||||
|--------|------|------|------|------|
|
||||
| id | string | path | 是 | 项目ID |
|
||||
|
||||
**响应示例**:
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"message": "删除成功"
|
||||
}
|
||||
```
|
||||
|
||||
### 4.4 获取项目列表
|
||||
|
||||
**接口地址**: `/api/project`
|
||||
|
||||
**请求方法**: `GET`
|
||||
|
||||
**请求参数**:
|
||||
|
||||
| 参数名 | 类型 | 位置 | 必填 | 说明 |
|
||||
|--------|------|------|------|------|
|
||||
| page | integer | query | 否 | 页码,默认1 |
|
||||
| size | integer | query | 否 | 每页数量,默认10 |
|
||||
| projectName | string | query | 否 | 项目名称(模糊查询) |
|
||||
| status | string | query | 否 | 项目状态 |
|
||||
| leader | string | query | 否 | 负责人(模糊查询) |
|
||||
|
||||
**响应示例**:
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"message": "查询成功",
|
||||
"data": [
|
||||
{
|
||||
"projectId": "PRJ2025001",
|
||||
"projectNo": "PRJ2025001",
|
||||
"projectName": "测试项目",
|
||||
"status": "IN_PROGRESS",
|
||||
"leader": "张三",
|
||||
"createTime": "2025-01-15 00:00:00",
|
||||
"updateTime": "2025-01-20 10:00:00"
|
||||
}
|
||||
],
|
||||
"total": 1
|
||||
}
|
||||
```
|
||||
|
||||
### 4.5 获取项目详情
|
||||
|
||||
**接口地址**: `/api/project/{id}`
|
||||
|
||||
**请求方法**: `GET`
|
||||
|
||||
**请求参数**:
|
||||
|
||||
| 参数名 | 类型 | 位置 | 必填 | 说明 |
|
||||
|--------|------|------|------|------|
|
||||
| id | string | path | 是 | 项目ID |
|
||||
|
||||
**响应示例**:
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"message": "查询成功",
|
||||
"data": {
|
||||
"projectId": "PRJ2025001",
|
||||
"projectNo": "PRJ2025001",
|
||||
"projectName": "测试项目",
|
||||
"status": "IN_PROGRESS",
|
||||
"leader": "张三",
|
||||
"phone": "13800138000",
|
||||
"email": "zhangsan@example.com",
|
||||
"background": "为了提升企业运营效率",
|
||||
"goal": "完成企业内部管理系统的全面数字化改造",
|
||||
"scope": "包括人力资源、财务管理、供应链管理等模块",
|
||||
"startDate": "2025-01-15",
|
||||
"plannedEndDate": "2025-06-30",
|
||||
"actualEndDate": null,
|
||||
"totalBudget": 500000,
|
||||
"usedBudget": 100000,
|
||||
"remainingBudget": 400000,
|
||||
"members": [
|
||||
{
|
||||
"name": "张三",
|
||||
"role": "项目经理",
|
||||
"department": "市场部"
|
||||
},
|
||||
{
|
||||
"name": "李四",
|
||||
"role": "技术负责人",
|
||||
"department": "技术部"
|
||||
}
|
||||
],
|
||||
"milestones": [
|
||||
{
|
||||
"name": "需求分析完成",
|
||||
"plannedDate": "2025-02-15",
|
||||
"actualDate": null,
|
||||
"status": "NOT_STARTED"
|
||||
},
|
||||
{
|
||||
"name": "系统设计完成",
|
||||
"plannedDate": "2025-03-15",
|
||||
"actualDate": null,
|
||||
"status": "NOT_STARTED"
|
||||
}
|
||||
],
|
||||
"risks": [
|
||||
{
|
||||
"description": "技术难度较高,可能影响进度",
|
||||
"level": "HIGH",
|
||||
"measure": "增加技术团队人员投入"
|
||||
}
|
||||
],
|
||||
"remarks": "项目需要与现有系统进行数据对接",
|
||||
"creator": "marketing",
|
||||
"createTime": "2025-01-15 00:00:00",
|
||||
"lastModifier": "admin",
|
||||
"updateTime": "2025-01-20 10:00:00"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. 历史记录API
|
||||
|
||||
### 5.1 获取项目历史
|
||||
|
||||
**接口地址**: `/api/history/{projectId}`
|
||||
|
||||
**请求方法**: `GET`
|
||||
|
||||
**请求参数**:
|
||||
|
||||
| 参数名 | 类型 | 位置 | 必填 | 说明 |
|
||||
|--------|------|------|------|------|
|
||||
| projectId | string | path | 是 | 项目ID |
|
||||
| page | integer | query | 否 | 页码,默认1 |
|
||||
| size | integer | query | 否 | 每页数量,默认10 |
|
||||
| operator | string | query | 否 | 操作人(模糊查询) |
|
||||
| startDate | string | query | 否 | 开始日期 |
|
||||
| endDate | string | query | 否 | 结束日期 |
|
||||
|
||||
**响应示例**:
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"message": "查询成功",
|
||||
"data": [
|
||||
{
|
||||
"historyId": "HIS2025001",
|
||||
"projectId": "PRJ2025001",
|
||||
"projectNo": "PRJ2025001",
|
||||
"operationType": "UPDATE",
|
||||
"operator": "admin",
|
||||
"operationTime": "2025-01-20 10:00:00",
|
||||
"fieldName": "status",
|
||||
"oldValue": "NOT_STARTED",
|
||||
"newValue": "IN_PROGRESS"
|
||||
}
|
||||
],
|
||||
"total": 1
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. 统计API
|
||||
|
||||
### 6.1 筛选项目
|
||||
|
||||
**接口地址**: `/api/statistics/filter`
|
||||
|
||||
**请求方法**: `POST`
|
||||
|
||||
**请求参数**:
|
||||
|
||||
| 参数名 | 类型 | 位置 | 必填 | 说明 |
|
||||
|--------|------|------|------|------|
|
||||
| projectNo | string | body | 否 | 项目编号 |
|
||||
| projectName | string | body | 否 | 项目名称(模糊查询) |
|
||||
| status | string | body | 否 | 项目状态 |
|
||||
| leader | string | body | 否 | 负责人(模糊查询) |
|
||||
| creator | string | body | 否 | 创建人(模糊查询) |
|
||||
| createStartDate | string | body | 否 | 创建开始日期 |
|
||||
| createEndDate | string | body | 否 | 创建结束日期 |
|
||||
| updateStartDate | string | body | 否 | 更新开始日期 |
|
||||
| updateEndDate | string | body | 否 | 更新结束日期 |
|
||||
| page | integer | body | 否 | 页码,默认1 |
|
||||
| size | integer | body | 否 | 每页数量,默认10 |
|
||||
|
||||
**请求示例**:
|
||||
```json
|
||||
{
|
||||
"projectName": "测试",
|
||||
"status": "IN_PROGRESS",
|
||||
"createStartDate": "2025-01-01",
|
||||
"createEndDate": "2025-12-31"
|
||||
}
|
||||
```
|
||||
|
||||
**响应示例**:
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"message": "查询成功",
|
||||
"data": [
|
||||
{
|
||||
"projectId": "PRJ2025001",
|
||||
"projectNo": "PRJ2025001",
|
||||
"projectName": "测试项目",
|
||||
"status": "IN_PROGRESS",
|
||||
"leader": "张三",
|
||||
"createTime": "2025-01-15 00:00:00",
|
||||
"updateTime": "2025-01-20 10:00:00"
|
||||
}
|
||||
],
|
||||
"total": 1
|
||||
}
|
||||
```
|
||||
|
||||
### 6.2 统计项目
|
||||
|
||||
**接口地址**: `/api/statistics/statistics`
|
||||
|
||||
**请求方法**: `POST`
|
||||
|
||||
**请求参数**:
|
||||
|
||||
| 参数名 | 类型 | 位置 | 必填 | 说明 |
|
||||
|--------|------|------|------|------|
|
||||
| dimension | string | body | 是 | 统计维度:status, department, leader, month |
|
||||
| startDate | string | body | 否 | 开始日期 |
|
||||
| endDate | string | body | 否 | 结束日期 |
|
||||
|
||||
**请求示例**:
|
||||
```json
|
||||
{
|
||||
"dimension": "status",
|
||||
"startDate": "2025-01-01",
|
||||
"endDate": "2025-12-31"
|
||||
}
|
||||
```
|
||||
|
||||
**响应示例**:
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"message": "统计成功",
|
||||
"data": {
|
||||
"dimension": "status",
|
||||
"total": 10,
|
||||
"details": [
|
||||
{
|
||||
"name": "NOT_STARTED",
|
||||
"value": 2
|
||||
},
|
||||
{
|
||||
"name": "IN_PROGRESS",
|
||||
"value": 5
|
||||
},
|
||||
{
|
||||
"name": "COMPLETED",
|
||||
"value": 2
|
||||
},
|
||||
{
|
||||
"name": "PAUSED",
|
||||
"value": 1
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. 系统API
|
||||
|
||||
### 7.1 获取操作日志
|
||||
|
||||
**接口地址**: `/api/system/log`
|
||||
|
||||
**请求方法**: `GET`
|
||||
|
||||
**请求参数**:
|
||||
|
||||
| 参数名 | 类型 | 位置 | 必填 | 说明 |
|
||||
|--------|------|------|------|------|
|
||||
| page | integer | query | 否 | 页码,默认1 |
|
||||
| size | integer | query | 否 | 每页数量,默认10 |
|
||||
| username | string | query | 否 | 用户名(模糊查询) |
|
||||
| operation | string | query | 否 | 操作内容(模糊查询) |
|
||||
| startDate | string | query | 否 | 开始日期 |
|
||||
| endDate | string | query | 否 | 结束日期 |
|
||||
|
||||
**响应示例**:
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"message": "查询成功",
|
||||
"data": [
|
||||
{
|
||||
"logId": "LOG2025001",
|
||||
"username": "admin",
|
||||
"operation": "登录系统",
|
||||
"ipAddress": "127.0.0.1",
|
||||
"userAgent": "Mozilla/5.0...",
|
||||
"operationTime": "2025-01-20 10:00:00"
|
||||
}
|
||||
],
|
||||
"total": 1
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 8. 认证与授权
|
||||
|
||||
### 8.1 认证流程
|
||||
|
||||
1. 客户端发送登录请求到 `/api/auth/login`
|
||||
2. 服务端验证用户名和密码
|
||||
3. 验证成功后生成 JWT Token
|
||||
4. 客户端保存 Token 到 localStorage
|
||||
5. 后续请求在请求头中携带 Token: `Authorization: Bearer <token>`
|
||||
6. 服务端验证 Token 的有效性
|
||||
7. 验证通过后处理请求
|
||||
|
||||
### 8.2 授权流程
|
||||
|
||||
1. 服务端从 Token 中解析出用户信息
|
||||
2. 根据用户角色和权限判断是否有权限访问请求的资源
|
||||
3. 有权限则继续处理请求,无权限则返回 403 错误
|
||||
|
||||
### 8.3 角色权限
|
||||
|
||||
| 角色 | 权限 |
|
||||
|------|------|
|
||||
| ADMIN | 所有权限 |
|
||||
| MARKETING | 创建项目、编辑项目、查看项目、查看历史记录 |
|
||||
| OTHER | 编辑项目、查看项目、查看历史记录 |
|
||||
|
||||
---
|
||||
|
||||
## 9. 附录
|
||||
|
||||
### 9.1 数据字典
|
||||
|
||||
#### 项目状态
|
||||
| 值 | 说明 |
|
||||
|----|------|
|
||||
| NOT_STARTED | 未开始 |
|
||||
| IN_PROGRESS | 进行中 |
|
||||
| COMPLETED | 已完成 |
|
||||
| PAUSED | 已暂停 |
|
||||
| CANCELLED | 已取消 |
|
||||
|
||||
#### 角色
|
||||
| 值 | 说明 |
|
||||
|----|------|
|
||||
| ADMIN | 管理员 |
|
||||
| MARKETING | 市场部 |
|
||||
| OTHER | 其他部门 |
|
||||
|
||||
#### 风险等级
|
||||
| 值 | 说明 |
|
||||
|----|------|
|
||||
| HIGH | 高 |
|
||||
| MEDIUM | 中 |
|
||||
| LOW | 低 |
|
||||
|
||||
#### 操作类型
|
||||
| 值 | 说明 |
|
||||
|----|------|
|
||||
| CREATE | 创建 |
|
||||
| UPDATE | 更新 |
|
||||
| DELETE | 删除 |
|
||||
|
||||
### 9.2 开发规范
|
||||
|
||||
- **API设计**: 遵循 RESTful API 设计规范
|
||||
- **参数验证**: 使用 Pydantic 进行参数验证
|
||||
- **错误处理**: 使用统一的错误处理机制
|
||||
- **日志记录**: 记录重要操作的日志
|
||||
- **测试**: 为每个 API 编写单元测试
|
||||
|
||||
### 9.3 变更记录
|
||||
|
||||
| 版本 | 日期 | 修改人 | 修改内容 |
|
||||
|------|------|--------|----------|
|
||||
| V1.0 | 2026-01-26 | - | 初始版本创建 |
|
||||
@@ -0,0 +1,381 @@
|
||||
# 后端依赖管理文档
|
||||
|
||||
## 文档信息
|
||||
- **文档版本**: V1.0
|
||||
- **创建日期**: 2026-01-26
|
||||
- **文档类型**: 后端依赖管理文档
|
||||
|
||||
---
|
||||
|
||||
## 1. 依赖管理工具
|
||||
|
||||
本项目使用以下工具管理依赖:
|
||||
|
||||
### 1.1 Poetry (推荐)
|
||||
|
||||
Poetry 是一个现代化的 Python 依赖管理和打包工具,提供了更简洁、更一致的依赖管理体验。
|
||||
|
||||
**安装 Poetry**:
|
||||
```bash
|
||||
# 使用 pip 安装
|
||||
pip install poetry
|
||||
|
||||
# 或使用官方安装脚本
|
||||
curl -sSL https://install.python-poetry.org | python3 -
|
||||
```
|
||||
|
||||
### 1.2 pip
|
||||
|
||||
如果您不使用 Poetry,也可以使用 pip 管理依赖。
|
||||
|
||||
---
|
||||
|
||||
## 2. 核心依赖
|
||||
|
||||
### 2.1 主要依赖
|
||||
|
||||
| 依赖 | 版本 | 用途 |
|
||||
|------|------|------|
|
||||
| fastapi | ^0.104.0 | 后端 Web 框架 |
|
||||
| uvicorn[standard] | ^0.24.0 | ASGI 服务器 |
|
||||
| sqlalchemy | ^2.0.0 | ORM 框架 |
|
||||
| pymysql | ^1.1.0 | MySQL 数据库驱动 |
|
||||
| python-jose[cryptography] | ^3.3.0 | JWT 库 |
|
||||
| passlib[bcrypt] | ^1.7.4 | 密码哈希库 |
|
||||
| pydantic | ^2.5.0 | 数据验证库 |
|
||||
| pydantic-settings | ^2.1.0 | 配置管理库 |
|
||||
| alembic | ^1.12.0 | 数据库迁移工具 |
|
||||
| python-multipart | ^0.0.6 | 表单数据处理 |
|
||||
| python-dotenv | ^1.0.0 | 环境变量管理 |
|
||||
| aiomysql | ^0.2.0 | 异步 MySQL 驱动 |
|
||||
|
||||
### 2.2 开发依赖
|
||||
|
||||
| 依赖 | 版本 | 用途 |
|
||||
|------|------|------|
|
||||
| pytest | ^7.4.0 | 测试框架 |
|
||||
| pytest-asyncio | ^0.21.0 | 异步测试支持 |
|
||||
| pytest-cov | ^4.1.0 | 测试覆盖率 |
|
||||
| flake8 | ^6.1.0 | 代码风格检查 |
|
||||
| black | ^23.11.0 | 代码格式化 |
|
||||
| isort | ^5.12.0 | 导入排序 |
|
||||
| mypy | ^1.7.0 | 类型检查 |
|
||||
|
||||
---
|
||||
|
||||
## 3. 配置文件
|
||||
|
||||
### 3.1 pyproject.toml (Poetry)
|
||||
|
||||
```toml
|
||||
[tool.poetry]
|
||||
name = "project-management-backend"
|
||||
version = "0.1.0"
|
||||
description = "项目管理系统后端"
|
||||
authors = [{ name = "Developer", email = "developer@example.com" }]
|
||||
readme = "README.md"
|
||||
|
||||
[tool.poetry.dependencies]
|
||||
python = "^3.9"
|
||||
fastapi = "^0.104.0"
|
||||
uvicorn = { version = "^0.24.0", extras = ["standard"] }
|
||||
sqlalchemy = "^2.0.0"
|
||||
pymysql = "^1.1.0"
|
||||
python-jose = { version = "^3.3.0", extras = ["cryptography"] }
|
||||
passlib = { version = "^1.7.4", extras = ["bcrypt"] }
|
||||
pydantic = "^2.5.0"
|
||||
pydantic-settings = "^2.1.0"
|
||||
alembic = "^1.12.0"
|
||||
python-multipart = "^0.0.6"
|
||||
python-dotenv = "^1.0.0"
|
||||
aiomysql = "^0.2.0"
|
||||
|
||||
[tool.poetry.group.dev.dependencies]
|
||||
pytest = "^7.4.0"
|
||||
pytest-asyncio = "^0.21.0"
|
||||
pytest-cov = "^4.1.0"
|
||||
flake8 = "^6.1.0"
|
||||
black = "^23.11.0"
|
||||
isort = "^5.12.0"
|
||||
mypy = "^1.7.0"
|
||||
|
||||
[build-system]
|
||||
requires = ["poetry-core"]
|
||||
build-backend = "poetry.core.masonry.api"
|
||||
```
|
||||
|
||||
### 3.2 requirements.txt (pip)
|
||||
|
||||
```
|
||||
fastapi==0.104.0
|
||||
uvicorn[standard]==0.24.0
|
||||
sqlalchemy==2.0.0
|
||||
pymysql==1.1.0
|
||||
python-jose[cryptography]==3.3.0
|
||||
passlib[bcrypt]==1.7.4
|
||||
pydantic==2.5.0
|
||||
pydantic-settings==2.1.0
|
||||
alembic==1.12.0
|
||||
python-multipart==0.0.6
|
||||
python-dotenv==1.0.0
|
||||
aiomysql==0.2.0
|
||||
|
||||
# 开发依赖
|
||||
pytest==7.4.0
|
||||
pytest-asyncio==0.21.0
|
||||
pytest-cov==4.1.0
|
||||
flake8==6.1.0
|
||||
black==23.11.0
|
||||
isort==5.12.0
|
||||
mypy==1.7.0
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. 依赖管理命令
|
||||
|
||||
### 4.1 使用 Poetry
|
||||
|
||||
#### 安装依赖
|
||||
```bash
|
||||
# 安装所有依赖(包括开发依赖)
|
||||
poetry install
|
||||
|
||||
# 仅安装生产依赖
|
||||
poetry install --no-dev
|
||||
```
|
||||
|
||||
#### 添加依赖
|
||||
```bash
|
||||
# 添加生产依赖
|
||||
poetry add <package>
|
||||
|
||||
# 添加开发依赖
|
||||
poetry add --group dev <package>
|
||||
```
|
||||
|
||||
#### 更新依赖
|
||||
```bash
|
||||
# 更新所有依赖
|
||||
poetry update
|
||||
|
||||
# 更新特定依赖
|
||||
poetry update <package>
|
||||
```
|
||||
|
||||
#### 导出依赖
|
||||
```bash
|
||||
# 导出到 requirements.txt
|
||||
poetry export --output requirements.txt
|
||||
|
||||
# 导出生产依赖
|
||||
poetry export --output requirements.txt --without dev
|
||||
```
|
||||
|
||||
#### 运行命令
|
||||
```bash
|
||||
# 在虚拟环境中运行命令
|
||||
poetry run <command>
|
||||
|
||||
# 例如运行开发服务器
|
||||
poetry run uvicorn app.main:app --reload
|
||||
|
||||
# 例如运行测试
|
||||
poetry run pytest
|
||||
```
|
||||
|
||||
### 4.2 使用 pip
|
||||
|
||||
#### 安装依赖
|
||||
```bash
|
||||
# 安装所有依赖
|
||||
pip install -r requirements.txt
|
||||
|
||||
# 安装生产依赖(需要单独创建生产依赖文件)
|
||||
pip install -r requirements-prod.txt
|
||||
```
|
||||
|
||||
#### 添加依赖
|
||||
```bash
|
||||
# 安装并添加到 requirements.txt
|
||||
pip install <package> && pip freeze | grep <package> >> requirements.txt
|
||||
```
|
||||
|
||||
#### 更新依赖
|
||||
```bash
|
||||
# 更新所有依赖
|
||||
pip install --upgrade -r requirements.txt
|
||||
|
||||
# 更新特定依赖
|
||||
pip install --upgrade <package>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. 环境配置
|
||||
|
||||
### 5.1 环境变量
|
||||
|
||||
项目使用 `.env` 文件管理环境变量,示例配置如下:
|
||||
|
||||
```env
|
||||
# 数据库连接信息
|
||||
DATABASE_URL="mysql+pymysql://username:password@localhost:3306/project_management"
|
||||
|
||||
# JWT 配置
|
||||
SECRET_KEY="your-secret-key"
|
||||
ALGORITHM="HS256"
|
||||
ACCESS_TOKEN_EXPIRE_MINUTES=30
|
||||
|
||||
# 应用配置
|
||||
APP_NAME="Project Management System"
|
||||
DEBUG=True
|
||||
```
|
||||
|
||||
### 5.2 配置文件
|
||||
|
||||
项目使用 `app/config.py` 管理应用配置:
|
||||
|
||||
```python
|
||||
from pydantic_settings import BaseSettings
|
||||
from functools import lru_cache
|
||||
|
||||
|
||||
class Settings(BaseSettings):
|
||||
"""应用配置"""
|
||||
# 应用配置
|
||||
app_name: str = "Project Management System"
|
||||
debug: bool = True
|
||||
|
||||
# 数据库配置
|
||||
database_url: str
|
||||
|
||||
# JWT 配置
|
||||
secret_key: str
|
||||
algorithm: str = "HS256"
|
||||
access_token_expire_minutes: int = 30
|
||||
|
||||
class Config:
|
||||
env_file = ".env"
|
||||
case_sensitive = False
|
||||
|
||||
|
||||
@lru_cache()
|
||||
def get_settings() -> Settings:
|
||||
"""获取配置实例"""
|
||||
return Settings()
|
||||
|
||||
|
||||
settings = get_settings()
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. 依赖版本管理
|
||||
|
||||
### 6.1 版本约束
|
||||
|
||||
Poetry 使用语义化版本约束,常见的版本约束格式:
|
||||
|
||||
| 格式 | 说明 | 示例 |
|
||||
|------|------|------|
|
||||
| ^1.0.0 | 兼容 1.x.x 版本 | ^2.0.0 匹配 2.0.0, 2.1.0 但不匹配 3.0.0 |
|
||||
| ~1.0.0 | 兼容 1.0.x 版本 | ~1.0.0 匹配 1.0.0, 1.0.1 但不匹配 1.1.0 |
|
||||
| >=1.0.0 | 大于等于 1.0.0 | >=2.0.0 匹配 2.0.0, 3.0.0 |
|
||||
| ==1.0.0 | 精确匹配 1.0.0 | ==1.0.0 只匹配 1.0.0 |
|
||||
|
||||
### 6.2 依赖锁定
|
||||
|
||||
Poetry 使用 `poetry.lock` 文件锁定依赖版本,确保在不同环境中安装相同版本的依赖:
|
||||
|
||||
```bash
|
||||
# 生成锁定文件
|
||||
poetry lock
|
||||
|
||||
# 使用锁定文件安装依赖
|
||||
poetry install
|
||||
```
|
||||
|
||||
### 6.3 依赖解析
|
||||
|
||||
如果遇到依赖冲突,可以使用以下方法解决:
|
||||
|
||||
1. **更新依赖版本**:尝试更新冲突的依赖版本
|
||||
2. **指定版本**:为冲突的依赖指定兼容的版本
|
||||
3. **使用虚拟环境**:为不同项目使用独立的虚拟环境
|
||||
|
||||
---
|
||||
|
||||
## 7. 开发最佳实践
|
||||
|
||||
### 7.1 依赖管理建议
|
||||
|
||||
1. **定期更新依赖**:定期更新依赖以获取安全补丁和新特性
|
||||
2. **使用锁定文件**:在生产环境中使用锁定文件确保依赖版本一致
|
||||
3. **分离开发和生产依赖**:只在生产环境中安装必要的依赖
|
||||
4. **使用虚拟环境**:为每个项目使用独立的虚拟环境
|
||||
5. **记录依赖变更**:在提交代码时同时提交依赖变更
|
||||
|
||||
### 7.2 安全注意事项
|
||||
|
||||
1. **检查依赖安全**:使用安全扫描工具检查依赖的安全漏洞
|
||||
2. **使用可信依赖**:只使用来自可信源的依赖
|
||||
3. **固定依赖版本**:在生产环境中固定依赖版本,避免自动更新引入问题
|
||||
4. **定期审计依赖**:定期审计项目依赖,移除不再使用的依赖
|
||||
|
||||
---
|
||||
|
||||
## 8. 常见问题
|
||||
|
||||
### 8.1 依赖安装失败
|
||||
|
||||
**问题**:依赖安装失败,出现版本冲突
|
||||
|
||||
**解决方法**:
|
||||
1. 检查 Python 版本是否兼容
|
||||
2. 尝试使用 `poetry update` 更新依赖
|
||||
3. 检查网络连接是否正常
|
||||
4. 尝试清理缓存:`poetry cache clear --all pypi`
|
||||
|
||||
### 8.2 数据库连接失败
|
||||
|
||||
**问题**:无法连接到数据库
|
||||
|
||||
**解决方法**:
|
||||
1. 检查数据库服务是否运行
|
||||
2. 检查数据库连接字符串是否正确
|
||||
3. 检查数据库用户权限是否正确
|
||||
4. 检查网络连接是否正常
|
||||
|
||||
### 8.3 JWT 认证失败
|
||||
|
||||
**问题**:JWT 认证失败
|
||||
|
||||
**解决方法**:
|
||||
1. 检查 `SECRET_KEY` 是否正确设置
|
||||
2. 检查 Token 是否过期
|
||||
3. 检查 Token 格式是否正确
|
||||
4. 检查认证中间件配置是否正确
|
||||
|
||||
---
|
||||
|
||||
## 9. 附录
|
||||
|
||||
### 9.1 Python 版本要求
|
||||
|
||||
本项目要求 Python 3.9 或更高版本。
|
||||
|
||||
### 9.2 开发工具推荐
|
||||
|
||||
| 工具 | 用途 | 安装命令 |
|
||||
|------|------|----------|
|
||||
| Poetry | 依赖管理 | `pip install poetry` |
|
||||
| PyCharm | IDE | [官网下载](https://www.jetbrains.com/pycharm/) |
|
||||
| VS Code | 编辑器 | [官网下载](https://code.visualstudio.com/) |
|
||||
| MySQL Workbench | 数据库管理 | [官网下载](https://www.mysql.com/products/workbench/) |
|
||||
|
||||
### 9.3 变更记录
|
||||
|
||||
| 版本 | 日期 | 修改人 | 修改内容 |
|
||||
|------|------|--------|----------|
|
||||
| V1.0 | 2026-01-26 | - | 初始版本创建 |
|
||||
@@ -0,0 +1,479 @@
|
||||
# 后端技术架构文档
|
||||
|
||||
## 文档信息
|
||||
- **文档版本**: V1.0
|
||||
- **创建日期**: 2026-01-26
|
||||
- **文档类型**: 后端技术架构文档
|
||||
|
||||
---
|
||||
|
||||
## 1. 技术选型
|
||||
|
||||
| 分类 | 技术 | 版本 | 选型理由 |
|
||||
|------|------|------|----------|
|
||||
| 后端框架 | FastAPI | 0.104.0+ | 高性能Python Web框架,自动生成API文档,支持异步 |
|
||||
| ORM框架 | SQLAlchemy | 2.0.0+ | Python最流行的ORM框架,支持多种数据库 |
|
||||
| 数据库驱动 | PyMySQL | 1.1.0+ | 纯Python实现的MySQL客户端 |
|
||||
| 数据库 | MySQL | 8.0+ | 关系型数据库,稳定可靠,适合企业级应用 |
|
||||
| 认证 | JWT | 2.8.0+ | JSON Web Token,用于用户认证和授权 |
|
||||
| 密码加密 | Passlib | 1.7.4+ | 密码哈希库,支持多种加密算法 |
|
||||
| 数据验证 | Pydantic | 2.5.0+ | 数据验证和设置库,与FastAPI完美集成 |
|
||||
| API文档 | Swagger | - | FastAPI自动生成,提供交互式API文档 |
|
||||
| 构建工具 | Poetry | 1.7.0+ | Python依赖管理和打包工具 |
|
||||
| 数据库迁移 | Alembic | 1.12.0+ | SQLAlchemy的数据库迁移工具 |
|
||||
| 测试框架 | Pytest | 7.4.0+ | Python最流行的测试框架 |
|
||||
| 异步支持 | asyncio | - | Python内置的异步IO库 |
|
||||
|
||||
---
|
||||
|
||||
## 2. 架构设计
|
||||
|
||||
### 2.1 分层架构
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────┐
|
||||
│ 路由层 (API) │
|
||||
│ (Router) │
|
||||
├─────────────────────────────────────────────────────────┤
|
||||
│ 业务逻辑层 (Service) │
|
||||
│ (Service) │
|
||||
├─────────────────────────────────────────────────────────┤
|
||||
│ 数据访问层 (Model) │
|
||||
│ (ORM Model) │
|
||||
├─────────────────────────────────────────────────────────┤
|
||||
│ 数据层 │
|
||||
│ (MySQL) │
|
||||
└─────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### 2.2 核心模块
|
||||
|
||||
| 模块名称 | 主要职责 | 文件路径 | 依赖关系 |
|
||||
|---------|---------|---------|----------|
|
||||
| 认证模块 | 用户登录、退出、认证 | app/api/auth.py | 依赖用户模型、JWT |
|
||||
| 用户模块 | 用户管理、权限管理 | app/api/user.py | 依赖用户模型、认证模块 |
|
||||
| 项目模块 | 项目管理、创建、编辑 | app/api/project.py | 依赖项目模型、历史记录模块 |
|
||||
| 历史记录模块 | 项目修改历史记录 | app/api/history.py | 依赖历史记录模型 |
|
||||
| 统计模块 | 项目统计、数据分析 | app/api/statistics.py | 依赖项目模型 |
|
||||
| 系统模块 | 系统配置、操作日志 | app/api/system.py | 依赖日志模型 |
|
||||
|
||||
### 2.3 数据流
|
||||
|
||||
1. **请求处理流程**:
|
||||
- 客户端发送请求到API路由
|
||||
- 路由层验证请求参数和用户权限
|
||||
- 调用服务层处理业务逻辑
|
||||
- 服务层调用数据访问层操作数据库
|
||||
- 数据访问层返回数据给服务层
|
||||
- 服务层处理数据并返回给路由层
|
||||
- 路由层返回响应给客户端
|
||||
|
||||
2. **项目创建流程**:
|
||||
- 验证用户权限(必须是市场部或管理员)
|
||||
- 生成项目编号
|
||||
- 创建项目记录
|
||||
- 创建项目成员记录
|
||||
- 创建项目里程碑记录
|
||||
- 创建项目风险记录
|
||||
- 创建项目历史记录
|
||||
|
||||
3. **项目编辑流程**:
|
||||
- 验证用户权限
|
||||
- 获取项目当前信息
|
||||
- 对比修改前后的数据
|
||||
- 更新项目记录
|
||||
- 更新相关表数据
|
||||
- 创建项目历史记录
|
||||
|
||||
---
|
||||
|
||||
## 3. 目录结构
|
||||
|
||||
```
|
||||
├── app/ # 应用代码
|
||||
│ ├── api/ # API路由
|
||||
│ │ ├── auth.py # 认证API
|
||||
│ │ ├── user.py # 用户API
|
||||
│ │ ├── project.py # 项目API
|
||||
│ │ ├── history.py # 历史记录API
|
||||
│ │ ├── statistics.py # 统计API
|
||||
│ │ ├── system.py # 系统API
|
||||
│ │ └── __init__.py # 导出API
|
||||
│ ├── models/ # 数据模型
|
||||
│ │ ├── __init__.py # 导出模型
|
||||
│ │ ├── user.py # 用户模型
|
||||
│ │ ├── project.py # 项目模型
|
||||
│ │ ├── member.py # 成员模型
|
||||
│ │ ├── milestone.py # 里程碑模型
|
||||
│ │ ├── risk.py # 风险模型
|
||||
│ │ ├── history.py # 历史记录模型
|
||||
│ │ └── log.py # 日志模型
|
||||
│ ├── schemas/ # Pydantic模式
|
||||
│ │ ├── __init__.py # 导出模式
|
||||
│ │ ├── auth.py # 认证模式
|
||||
│ │ ├── user.py # 用户模式
|
||||
│ │ ├── project.py # 项目模式
|
||||
│ │ ├── history.py # 历史记录模式
|
||||
│ │ ├── statistics.py # 统计模式
|
||||
│ │ ├── system.py # 系统模式
|
||||
│ │ └── common.py # 通用模式
|
||||
│ ├── services/ # 业务逻辑
|
||||
│ │ ├── __init__.py # 导出服务
|
||||
│ │ ├── auth_service.py # 认证服务
|
||||
│ │ ├── user_service.py # 用户服务
|
||||
│ │ ├── project_service.py # 项目服务
|
||||
│ │ ├── history_service.py # 历史记录服务
|
||||
│ │ ├── statistics_service.py # 统计服务
|
||||
│ │ └── system_service.py # 系统服务
|
||||
│ ├── database/ # 数据库配置
|
||||
│ │ ├── __init__.py # 导出配置
|
||||
│ │ ├── config.py # 数据库配置
|
||||
│ │ └── session.py # 数据库会话
|
||||
│ ├── common/ # 公共模块
|
||||
│ │ ├── __init__.py # 导出公共模块
|
||||
│ │ ├── constants.py # 常量定义
|
||||
│ │ ├── utils.py # 工具函数
|
||||
│ │ ├── dependencies.py # 依赖注入
|
||||
│ │ └── exceptions.py # 异常处理
|
||||
│ ├── config.py # 应用配置
|
||||
│ └── main.py # 应用入口
|
||||
├── tests/ # 测试代码
|
||||
│ ├── __init__.py # 测试初始化
|
||||
│ ├── test_auth.py # 认证测试
|
||||
│ ├── test_user.py # 用户测试
|
||||
│ ├── test_project.py # 项目测试
|
||||
│ ├── test_history.py # 历史记录测试
|
||||
│ ├── test_statistics.py # 统计测试
|
||||
│ └── conftest.py # 测试配置
|
||||
├── alembic/ # 数据库迁移
|
||||
│ ├── versions/ # 迁移版本
|
||||
│ ├── env.py # 迁移环境
|
||||
│ └── script.py.mako # 迁移脚本模板
|
||||
├── pyproject.toml # Poetry配置
|
||||
├── poetry.lock # Poetry锁文件
|
||||
├── requirements.txt # 依赖列表
|
||||
├── README.md # 项目说明
|
||||
├── .env.example # 环境变量示例
|
||||
└── .gitignore # Git忽略文件
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. 核心类与函数
|
||||
|
||||
### 4.1 认证模块
|
||||
|
||||
| 类/函数名 | 说明 | 参数(类型/含义) | 成功返回结构/类型 | 失败返回结构/类型 | 所属文件/模块 |
|
||||
|-----------|------|-----------------|-----------------|-----------------|--------------|
|
||||
| `AuthRouter.login()` | 用户登录 | LoginRequest 登录请求 | `AuthResponse` | `HTTPException` | app/api/auth.py |
|
||||
| `AuthRouter.logout()` | 用户退出 | 无 | `BaseResponse` | `HTTPException` | app/api/auth.py |
|
||||
| `AuthRouter.get_user_info()` | 获取用户信息 | 无 | `UserInfoResponse` | `HTTPException` | app/api/auth.py |
|
||||
| `AuthService.authenticate()` | 认证用户 | str username, str password | `AuthResponse` | `AuthenticationException` | app/services/auth_service.py |
|
||||
| `AuthService.create_token()` | 创建Token | str user_id | `str` Token | `Exception` | app/services/auth_service.py |
|
||||
| `AuthService.verify_token()` | 验证Token | str token | `dict` Token载荷 | `JWTError` | app/services/auth_service.py |
|
||||
|
||||
### 4.2 用户模块
|
||||
|
||||
| 类/函数名 | 说明 | 参数(类型/含义) | 成功返回结构/类型 | 失败返回结构/类型 | 所属文件/模块 |
|
||||
|-----------|------|-----------------|-----------------|-----------------|--------------|
|
||||
| `UserRouter.create()` | 创建用户 | UserCreate 用户创建请求 | `BaseResponse` | `HTTPException` | app/api/user.py |
|
||||
| `UserRouter.update()` | 更新用户 | str id, UserUpdate 用户更新请求 | `BaseResponse` | `HTTPException` | app/api/user.py |
|
||||
| `UserRouter.delete()` | 删除用户 | str id | `BaseResponse` | `HTTPException` | app/api/user.py |
|
||||
| `UserRouter.list()` | 获取用户列表 | UserQuery 查询参数 | `PageResponse[User]` | `HTTPException` | app/api/user.py |
|
||||
| `UserRouter.reset_password()` | 重置密码 | str id, PasswordReset 密码重置请求 | `BaseResponse` | `HTTPException` | app/api/user.py |
|
||||
| `UserService.create_user()` | 创建用户 | UserCreate 用户创建请求 | `User` | `ServiceException` | app/services/user_service.py |
|
||||
| `UserService.update_user()` | 更新用户 | str id, UserUpdate 用户更新请求 | `User` | `ServiceException` | app/services/user_service.py |
|
||||
| `UserService.delete_user()` | 删除用户 | str id | `bool` | `ServiceException` | app/services/user_service.py |
|
||||
| `UserService.get_user_list()` | 获取用户列表 | UserQuery 查询参数 | `List[User]` | `ServiceException` | app/services/user_service.py |
|
||||
| `UserService.reset_password()` | 重置密码 | str id, str password | `bool` | `ServiceException` | app/services/user_service.py |
|
||||
|
||||
### 4.3 项目模块
|
||||
|
||||
| 类/函数名 | 说明 | 参数(类型/含义) | 成功返回结构/类型 | 失败返回结构/类型 | 所属文件/模块 |
|
||||
|-----------|------|-----------------|-----------------|-----------------|--------------|
|
||||
| `ProjectRouter.create()` | 创建项目 | ProjectCreate 项目创建请求 | `BaseResponse` | `HTTPException` | app/api/project.py |
|
||||
| `ProjectRouter.update()` | 更新项目 | str id, ProjectUpdate 项目更新请求 | `BaseResponse` | `HTTPException` | app/api/project.py |
|
||||
| `ProjectRouter.delete()` | 删除项目 | str id | `BaseResponse` | `HTTPException` | app/api/project.py |
|
||||
| `ProjectRouter.list()` | 获取项目列表 | ProjectQuery 查询参数 | `PageResponse[Project]` | `HTTPException` | app/api/project.py |
|
||||
| `ProjectRouter.detail()` | 获取项目详情 | str id | `ProjectDetailResponse` | `HTTPException` | app/api/project.py |
|
||||
| `ProjectService.create_project()` | 创建项目 | ProjectCreate 项目创建请求, str username | `Project` | `ServiceException` | app/services/project_service.py |
|
||||
| `ProjectService.update_project()` | 更新项目 | str id, ProjectUpdate 项目更新请求, str username | `Project` | `ServiceException` | app/services/project_service.py |
|
||||
| `ProjectService.delete_project()` | 删除项目 | str id | `bool` | `ServiceException` | app/services/project_service.py |
|
||||
| `ProjectService.get_project_list()` | 获取项目列表 | ProjectQuery 查询参数 | `List[Project]` | `ServiceException` | app/services/project_service.py |
|
||||
| `ProjectService.get_project_detail()` | 获取项目详情 | str id | `ProjectDetail` | `ServiceException` | app/services/project_service.py |
|
||||
|
||||
### 4.4 历史记录模块
|
||||
|
||||
| 类/函数名 | 说明 | 参数(类型/含义) | 成功返回结构/类型 | 失败返回结构/类型 | 所属文件/模块 |
|
||||
|-----------|------|-----------------|-----------------|-----------------|--------------|
|
||||
| `HistoryRouter.list()` | 获取项目历史 | str project_id, HistoryQuery 查询参数 | `PageResponse[ProjectHistory]` | `HTTPException` | app/api/history.py |
|
||||
| `HistoryService.record_history()` | 记录历史 | str project_id, str operation_type, str operator, dict changes | `ProjectHistory` | `ServiceException` | app/services/history_service.py |
|
||||
| `HistoryService.get_project_history()` | 获取项目历史 | str project_id, HistoryQuery 查询参数 | `List[ProjectHistory]` | `ServiceException` | app/services/history_service.py |
|
||||
|
||||
### 4.5 统计模块
|
||||
|
||||
| 类/函数名 | 说明 | 参数(类型/含义) | 成功返回结构/类型 | 失败返回结构/类型 | 所属文件/模块 |
|
||||
|-----------|------|-----------------|-----------------|-----------------|--------------|
|
||||
| `StatisticsRouter.filter()` | 筛选项目 | ProjectFilter 筛选条件 | `PageResponse[Project]` | `HTTPException` | app/api/statistics.py |
|
||||
| `StatisticsRouter.statistics()` | 统计项目 | StatisticsRequest 统计请求 | `StatisticsResponse` | `HTTPException` | app/api/statistics.py |
|
||||
| `StatisticsService.filter_projects()` | 筛选项目 | ProjectFilter 筛选条件 | `List[Project]` | `ServiceException` | app/services/statistics_service.py |
|
||||
| `StatisticsService.get_project_statistics()` | 获取项目统计 | StatisticsRequest 统计请求 | `StatisticsResult` | `ServiceException` | app/services/statistics_service.py |
|
||||
|
||||
---
|
||||
|
||||
## 5. 数据库设计
|
||||
|
||||
### 5.1 数据库表结构
|
||||
|
||||
详细的数据库表结构见《数据库设计文档》。
|
||||
|
||||
### 5.2 数据模型关系
|
||||
|
||||
```
|
||||
User (用户)
|
||||
|
|
||||
| 1
|
||||
|
|
||||
| N
|
||||
|
|
||||
OperationLog (操作日志)
|
||||
|
||||
Project (项目)
|
||||
|
|
||||
| 1
|
||||
|
|
||||
| N
|
||||
|
|
||||
+-- ProjectMember (项目成员)
|
||||
|
|
||||
| 1
|
||||
|
|
||||
| N
|
||||
|
|
||||
+-- ProjectMilestone (项目里程碑)
|
||||
|
|
||||
| 1
|
||||
|
|
||||
| N
|
||||
|
|
||||
+-- ProjectRisk (项目风险)
|
||||
|
|
||||
| 1
|
||||
|
|
||||
| N
|
||||
|
|
||||
+-- ProjectHistory (项目历史)
|
||||
```
|
||||
|
||||
### 5.3 数据库连接配置
|
||||
|
||||
- **开发环境**:使用本地MySQL数据库
|
||||
- **测试环境**:使用测试MySQL数据库
|
||||
- **生产环境**:使用生产MySQL数据库
|
||||
|
||||
---
|
||||
|
||||
## 6. API设计
|
||||
|
||||
### 6.1 认证API
|
||||
|
||||
| API路径 | 方法 | 模块/文件 | 功能描述 | 请求体 (JSON) | 成功响应 (200 OK) |
|
||||
|---------|------|-----------|----------|--------------|------------------|
|
||||
| `/api/auth/login` | `POST` | `app/api/auth.py` | 用户登录 | `{"username": "admin", "password": "123456"}` | `{"code": 200, "data": {"token": "...", "userInfo": {...}}, "message": "登录成功"}` |
|
||||
| `/api/auth/logout` | `POST` | `app/api/auth.py` | 用户退出 | N/A | `{"code": 200, "message": "退出成功"}` |
|
||||
| `/api/auth/userInfo` | `GET` | `app/api/auth.py` | 获取用户信息 | N/A | `{"code": 200, "data": {"userId": "...", "username": "...", "realName": "...", "department": "...", "role": "..."}}` |
|
||||
|
||||
### 6.2 用户API
|
||||
|
||||
| API路径 | 方法 | 模块/文件 | 功能描述 | 请求体 (JSON) | 成功响应 (200 OK) |
|
||||
|---------|------|-----------|----------|--------------|------------------|
|
||||
| `/api/user` | `GET` | `app/api/user.py` | 获取用户列表 | N/A (Query参数: page, size, username, department, role) | `{"code": 200, "data": [{...}], "total": 10, "message": "查询成功"}` |
|
||||
| `/api/user` | `POST` | `app/api/user.py` | 创建用户 | `{"username": "test", "password": "123456", "realName": "测试用户", "department": "技术部", "role": "OTHER"}` | `{"code": 200, "message": "创建成功"}` |
|
||||
| `/api/user/{id}` | `PUT` | `app/api/user.py` | 更新用户 | `{"realName": "测试用户1", "department": "市场部", "role": "MARKETING"}` | `{"code": 200, "message": "更新成功"}` |
|
||||
| `/api/user/{id}` | `DELETE` | `app/api/user.py` | 删除用户 | N/A | `{"code": 200, "message": "删除成功"}` |
|
||||
| `/api/user/resetPassword/{id}` | `PUT` | `app/api/user.py` | 重置密码 | `{"password": "123456"}` | `{"code": 200, "message": "密码重置成功"}` |
|
||||
|
||||
### 6.3 项目API
|
||||
|
||||
| API路径 | 方法 | 模块/文件 | 功能描述 | 请求体 (JSON) | 成功响应 (200 OK) |
|
||||
|---------|------|-----------|----------|--------------|------------------|
|
||||
| `/api/project` | `GET` | `app/api/project.py` | 获取项目列表 | N/A (Query参数: page, size, projectName, status, leader) | `{"code": 200, "data": [{...}], "total": 10, "message": "查询成功"}` |
|
||||
| `/api/project` | `POST` | `app/api/project.py` | 创建项目 | `{"projectName": "测试项目", "status": "NOT_STARTED", "leader": "张三", ...}` | `{"code": 200, "data": {"projectId": "..."}, "message": "创建成功"}` |
|
||||
| `/api/project/{id}` | `GET` | `app/api/project.py` | 获取项目详情 | N/A | `{"code": 200, "data": {...}, "message": "查询成功"}` |
|
||||
| `/api/project/{id}` | `PUT` | `app/api/project.py` | 更新项目 | `{"projectName": "测试项目1", "status": "IN_PROGRESS", ...}` | `{"code": 200, "message": "更新成功"}` |
|
||||
| `/api/project/{id}` | `DELETE` | `app/api/project.py` | 删除项目 | N/A | `{"code": 200, "message": "删除成功"}` |
|
||||
|
||||
### 6.4 历史记录API
|
||||
|
||||
| API路径 | 方法 | 模块/文件 | 功能描述 | 请求体 (JSON) | 成功响应 (200 OK) |
|
||||
|---------|------|-----------|----------|--------------|------------------|
|
||||
| `/api/history/{projectId}` | `GET` | `app/api/history.py` | 获取项目历史 | N/A (Query参数: page, size, operator, startDate, endDate) | `{"code": 200, "data": [{...}], "total": 10, "message": "查询成功"}` |
|
||||
|
||||
### 6.5 统计API
|
||||
|
||||
| API路径 | 方法 | 模块/文件 | 功能描述 | 请求体 (JSON) | 成功响应 (200 OK) |
|
||||
|---------|------|-----------|----------|--------------|------------------|
|
||||
| `/api/statistics/filter` | `POST` | `app/api/statistics.py` | 筛选项目 | `{"projectNo": "PRJ2025001", "projectName": "测试", "status": "IN_PROGRESS", ...}` | `{"code": 200, "data": [{...}], "total": 10, "message": "查询成功"}` |
|
||||
| `/api/statistics/statistics` | `POST` | `app/api/statistics.py` | 统计项目 | `{"dimension": "status", "startDate": "2025-01-01", "endDate": "2025-12-31"}` | `{"code": 200, "data": {...}, "message": "统计成功"}` |
|
||||
|
||||
---
|
||||
|
||||
## 7. 安全设计
|
||||
|
||||
### 7.1 认证与授权
|
||||
|
||||
- **认证方式**: JWT (JSON Web Token) 认证
|
||||
- **授权方式**: 基于角色的访问控制 (RBAC)
|
||||
- **密码加密**: 使用 Passlib 的 bcrypt 算法加密存储密码
|
||||
- **会话管理**: JWT Token 存储在前端 localStorage 中,后端验证 Token 签名和有效期
|
||||
|
||||
### 7.2 接口安全
|
||||
|
||||
- **统一认证拦截**: 所有接口(除登录接口外)都需要验证 Token
|
||||
- **权限验证**: 基于用户角色和权限进行接口访问控制
|
||||
- **请求参数验证**: 使用 Pydantic 验证请求参数的合法性
|
||||
- **防止SQL注入**: 使用 SQLAlchemy 的参数化查询,避免SQL注入
|
||||
- **防止XSS攻击**: 前端使用 v-html 时进行转义,后端对输入进行过滤
|
||||
- **防止CSRF攻击**: 使用 Token 验证,确保请求来自合法来源
|
||||
|
||||
### 7.3 数据安全
|
||||
|
||||
- **敏感数据加密**: 敏感数据(如密码)加密存储
|
||||
- **数据备份**: 定期备份数据库,防止数据丢失
|
||||
- **数据恢复**: 制定数据恢复计划,确保数据可恢复
|
||||
- **日志审计**: 记录用户操作日志,便于追溯操作行为
|
||||
|
||||
---
|
||||
|
||||
## 8. 部署与集成
|
||||
|
||||
### 8.1 开发环境部署
|
||||
|
||||
1. 克隆代码库: `git clone <repository-url>`
|
||||
2. 安装依赖: `poetry install` 或 `pip install -r requirements.txt`
|
||||
3. 配置环境变量: 复制 `.env.example` 为 `.env` 并修改配置
|
||||
4. 初始化数据库: `alembic upgrade head`
|
||||
5. 启动应用: `uvicorn app.main:app --reload`
|
||||
6. 访问: `http://localhost:8000`
|
||||
7. API文档: `http://localhost:8000/docs`
|
||||
|
||||
### 8.2 生产环境部署
|
||||
|
||||
1. 克隆代码库: `git clone <repository-url>`
|
||||
2. 安装依赖: `poetry install --no-dev` 或 `pip install -r requirements.txt`
|
||||
3. 配置环境变量: 复制 `.env.example` 为 `.env` 并修改配置
|
||||
4. 初始化数据库: `alembic upgrade head`
|
||||
5. 构建应用: 无需构建,直接运行
|
||||
6. 启动应用: `gunicorn app.main:app -w 4 -k uvicorn.workers.UvicornWorker`
|
||||
7. 配置 Nginx 反向代理
|
||||
|
||||
### 8.3 集成方案
|
||||
|
||||
- **前端与后端集成**: 通过 RESTful API 进行数据交互
|
||||
- **数据库集成**: 使用 SQLAlchemy ORM 操作数据库
|
||||
- **日志集成**: 使用 Python logging 模块记录系统日志
|
||||
- **监控集成**: 可集成 Prometheus + Grafana 监控系统性能
|
||||
|
||||
---
|
||||
|
||||
## 9. 性能优化
|
||||
|
||||
### 9.1 代码优化
|
||||
|
||||
- **异步处理**: 使用 FastAPI 的异步特性,提高并发处理能力
|
||||
- **批量操作**: 对批量数据操作使用批量处理,减少数据库交互次数
|
||||
- **缓存**: 对热点数据使用内存缓存,减少数据库查询
|
||||
- **分页查询**: 使用分页查询,避免一次性加载大量数据
|
||||
|
||||
### 9.2 数据库优化
|
||||
|
||||
- **索引优化**: 为常用查询字段创建索引,提高查询性能
|
||||
- **连接池**: 使用 SQLAlchemy 的连接池,提高数据库连接效率
|
||||
- **查询优化**: 使用 SQLAlchemy 的 lazy loading 和 eager loading 优化查询
|
||||
- **数据库配置**: 优化 MySQL 配置参数,提高数据库性能
|
||||
|
||||
### 9.3 部署优化
|
||||
|
||||
- **负载均衡**: 对多实例部署使用负载均衡
|
||||
- **水平扩展**: 根据业务需求进行水平扩展
|
||||
- **资源限制**: 合理设置应用的资源限制(CPU、内存等)
|
||||
|
||||
---
|
||||
|
||||
## 10. 监控与维护
|
||||
|
||||
### 10.1 系统监控
|
||||
|
||||
- **应用监控**: 监控应用的运行状态、CPU、内存使用情况
|
||||
- **数据库监控**: 监控数据库的连接数、查询性能、存储空间
|
||||
- **API监控**: 监控 API 的响应时间、调用次数、错误率
|
||||
- **日志监控**: 监控系统日志,及时发现异常情况
|
||||
|
||||
### 10.2 故障处理
|
||||
|
||||
- **故障定位**: 通过日志和监控工具定位故障原因
|
||||
- **故障恢复**: 制定故障恢复方案,确保系统快速恢复
|
||||
- **故障预防**: 定期进行系统检查,预防故障发生
|
||||
|
||||
### 10.3 系统维护
|
||||
|
||||
- **定期更新**: 定期更新依赖库和框架版本,修复安全漏洞
|
||||
- **数据备份**: 定期备份数据库,防止数据丢失
|
||||
- **性能调优**: 定期分析系统性能,进行性能调优
|
||||
- **文档更新**: 及时更新系统文档,保持文档与系统同步
|
||||
|
||||
---
|
||||
|
||||
## 11. 开发规范
|
||||
|
||||
### 11.1 代码规范
|
||||
|
||||
- **代码风格**: 遵循 PEP 8 规范
|
||||
- **命名规范**:
|
||||
- 类名: 大驼峰命名法 (PascalCase)
|
||||
- 函数名: 小驼峰命名法 (camelCase)
|
||||
- 变量名: 小写下划线分隔 (snake_case)
|
||||
- 常量名: 全大写下划线分隔 (SNAKE_CASE)
|
||||
- **注释规范**: 为所有公共函数和类添加文档字符串
|
||||
- **导入规范**: 分组导入,按标准库、第三方库、本地模块顺序
|
||||
|
||||
### 11.2 版本控制
|
||||
|
||||
- **分支管理**: 使用 Git Flow 分支管理策略
|
||||
- **提交规范**: 提交信息使用英文,格式为 `[类型]: 描述`
|
||||
- **版本号**: 使用语义化版本号 (Semantic Versioning)
|
||||
|
||||
### 11.3 测试规范
|
||||
|
||||
- **测试覆盖率**: 核心功能测试覆盖率不低于 80%
|
||||
- **测试类型**: 单元测试、集成测试、端到端测试
|
||||
- **测试框架**: 使用 Pytest 进行测试
|
||||
|
||||
---
|
||||
|
||||
## 12. 附录
|
||||
|
||||
### 12.1 技术选型对比
|
||||
|
||||
| 技术 | 对比方案 | 最终选择理由 |
|
||||
|------|----------|--------------|
|
||||
| 后端框架 | FastAPI vs Flask vs Django | FastAPI 高性能,自动生成API文档,支持异步 |
|
||||
| ORM框架 | SQLAlchemy vs Django ORM | SQLAlchemy 灵活强大,支持多种数据库 |
|
||||
| 数据库 | MySQL vs PostgreSQL | MySQL 社区活跃,生态成熟,适合企业级应用 |
|
||||
| 认证 | JWT vs Session | JWT 无状态,便于水平扩展 |
|
||||
| 构建工具 | Poetry vs pip | Poetry 提供更好的依赖管理和版本控制 |
|
||||
|
||||
### 12.2 常用命令
|
||||
|
||||
- **安装依赖**: `poetry install` 或 `pip install -r requirements.txt`
|
||||
- **添加依赖**: `poetry add <package>` 或 `pip install <package>`
|
||||
- **启动开发服务器**: `uvicorn app.main:app --reload`
|
||||
- **运行测试**: `pytest`
|
||||
- **生成数据库迁移**: `alembic revision --autogenerate -m "描述"`
|
||||
- **执行数据库迁移**: `alembic upgrade head`
|
||||
|
||||
### 12.3 变更记录
|
||||
|
||||
| 版本 | 日期 | 修改人 | 修改内容 |
|
||||
|------|------|--------|----------|
|
||||
| V1.0 | 2026-01-26 | - | 初始版本创建 |
|
||||
@@ -0,0 +1,789 @@
|
||||
# 后端数据库迁移文档
|
||||
|
||||
## 文档信息
|
||||
- **文档版本**: V1.0
|
||||
- **创建日期**: 2026-01-26
|
||||
- **文档类型**: 后端数据库迁移文档
|
||||
|
||||
---
|
||||
|
||||
## 1. 数据库迁移概述
|
||||
|
||||
### 1.1 什么是数据库迁移
|
||||
|
||||
数据库迁移是一种管理数据库模式变更的方法,它允许您:
|
||||
|
||||
- 版本化数据库模式
|
||||
- 跟踪数据库变更历史
|
||||
- 在不同环境之间一致地应用数据库变更
|
||||
- 回滚错误的数据库变更
|
||||
- 协作开发数据库模式
|
||||
|
||||
### 1.2 为什么使用数据库迁移
|
||||
|
||||
- **可追溯性**: 记录所有数据库变更的历史
|
||||
- **一致性**: 在开发、测试和生产环境中应用相同的变更
|
||||
- **安全性**: 提供回滚机制,防止错误变更导致的数据丢失
|
||||
- **协作**: 团队成员可以共享和审查数据库变更
|
||||
- **自动化**: 简化数据库部署流程
|
||||
|
||||
---
|
||||
|
||||
## 2. 迁移工具
|
||||
|
||||
### 2.1 Alembic
|
||||
|
||||
本项目使用 **Alembic** 作为数据库迁移工具。Alembic 是 SQLAlchemy 的官方数据库迁移工具,提供了以下功能:
|
||||
|
||||
- 自动生成迁移脚本
|
||||
- 管理迁移版本
|
||||
- 应用和回滚迁移
|
||||
- 支持多种数据库
|
||||
- 与 SQLAlchemy 无缝集成
|
||||
|
||||
### 2.2 安装 Alembic
|
||||
|
||||
Alembic 已经在项目依赖中包含,使用以下命令安装:
|
||||
|
||||
```bash
|
||||
# 使用 Poetry
|
||||
poetry install
|
||||
|
||||
# 使用 pip
|
||||
pip install -r requirements.txt
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. 迁移配置
|
||||
|
||||
### 3.1 初始化 Alembic
|
||||
|
||||
如果项目还没有初始化 Alembic,使用以下命令:
|
||||
|
||||
```bash
|
||||
# 在项目根目录执行
|
||||
alembic init alembic
|
||||
```
|
||||
|
||||
### 3.2 配置文件
|
||||
|
||||
Alembic 的主要配置文件是 `alembic.ini`,位于项目根目录:
|
||||
|
||||
```ini
|
||||
# alembic.ini
|
||||
|
||||
# A generic, single database configuration.
|
||||
|
||||
[alembic]
|
||||
# path to migration scripts
|
||||
script_location = alembic
|
||||
|
||||
# template used to generate migration file names; The default value is %%(rev)s_%%(slug)s
|
||||
# Uncomment the line below if you want the files to be prepended with date and time
|
||||
# file_template = %%(year)d%%(month).2d%%(day).2d_%%(hour).2d%%(minute).2d-%%(rev)s_%%(slug)s
|
||||
|
||||
# sys.path path, will be prepended to sys.path if present.
|
||||
# defaults to the current working directory.
|
||||
prepend_sys_path = .
|
||||
|
||||
# timezone to use when rendering the date within the migration file
|
||||
# as well as the filename.
|
||||
# If specified, requires the python-dateutil library
|
||||
# timezone =
|
||||
|
||||
# max length of characters to apply to the
|
||||
# "slug" field
|
||||
# truncate_slug_length = 40
|
||||
|
||||
# set to 'true' to run the environment during
|
||||
# the 'revision' command, regardless of autogenerate
|
||||
# revision_environment = false
|
||||
|
||||
# set to 'true' to allow .pyc and .pyo files without
|
||||
# a source .py file to be detected as revisions in the
|
||||
# versions/ directory
|
||||
# sourceless = false
|
||||
|
||||
# version location specification; This defaults
|
||||
# to alembic/versions. When using multiple version
|
||||
# directories, initial revisions must be specified with --version-path.
|
||||
# The path separator used here should be the separator specified by "version_path_separator" below.
|
||||
# version_locations = %(here)s/bar:%(here)s/bat:alembic/versions
|
||||
|
||||
# version path separator; As mentioned above, this is the character used to split
|
||||
# version_locations. The default within new alembic.ini files is "os", which uses os.pathsep.
|
||||
# If this key is omitted entirely, it falls back to the legacy behavior of splitting on spaces and/or commas.
|
||||
# Valid values for version_path_separator are:
|
||||
#
|
||||
# version_path_separator = :
|
||||
# version_path_separator = ;
|
||||
# version_path_separator = space
|
||||
version_path_separator = os # Use os.pathsep.
|
||||
# the output encoding used when revision files
|
||||
# are written from script.py.mako
|
||||
# output_encoding = utf-8
|
||||
|
||||
sqlalchemy.url = mysql+pymysql://username:password@localhost:3306/project_management
|
||||
|
||||
|
||||
[post_write_hooks]
|
||||
# post_write_hooks defines scripts or Python functions that are run
|
||||
# on newly generated revision scripts. See the documentation for further
|
||||
# detail and examples
|
||||
|
||||
# format using "black" - use the console_scripts runner, against the "black" entrypoint
|
||||
# hooks = black
|
||||
# black.type = console_scripts
|
||||
# black.entrypoint = black
|
||||
# black.options = -l 79 REVISION_SCRIPT_FILENAME
|
||||
|
||||
# Logging configuration
|
||||
[loggers]
|
||||
keys = root,sqlalchemy,alembic
|
||||
|
||||
[handlers]
|
||||
keys = console
|
||||
|
||||
[formatters]
|
||||
keys = generic
|
||||
|
||||
[logger_root]
|
||||
level = WARN
|
||||
handlers = console
|
||||
qualname =
|
||||
|
||||
[logger_sqlalchemy]
|
||||
level = WARN
|
||||
handlers =
|
||||
qualname = sqlalchemy.engine
|
||||
|
||||
[logger_alembic]
|
||||
level = INFO
|
||||
handlers =
|
||||
qualname = alembic
|
||||
|
||||
[handler_console]
|
||||
class = StreamHandler
|
||||
args = (sys.stderr,)
|
||||
level = NOTSET
|
||||
formatter = generic
|
||||
|
||||
[formatter_generic]
|
||||
format = %(levelname)-5.5s [%(name)s] %(message)s
|
||||
datefmt = %H:%M:%S
|
||||
```
|
||||
|
||||
### 3.3 环境配置
|
||||
|
||||
Alembic 的环境配置文件是 `alembic/env.py`,用于配置数据库连接和迁移行为:
|
||||
|
||||
```python
|
||||
# alembic/env.py
|
||||
|
||||
from logging.config import fileConfig
|
||||
from sqlalchemy import engine_from_config
|
||||
from sqlalchemy import pool
|
||||
from alembic import context
|
||||
import os
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
# 将项目根目录添加到 Python 路径
|
||||
sys.path.append(str(Path(__file__).parent.parent))
|
||||
|
||||
# 导入模型和配置
|
||||
from app.config import settings
|
||||
from app.database.base import Base
|
||||
from app.models import * # 导入所有模型
|
||||
|
||||
# this is the Alembic Config object, which provides
|
||||
# access to the values within the .ini file in use.
|
||||
config = context.config
|
||||
|
||||
# 使用环境变量中的数据库 URL
|
||||
config.set_main_option('sqlalchemy.url', settings.database_url)
|
||||
|
||||
# Interpret the config file for Python logging.
|
||||
# This line sets up loggers basically.
|
||||
if config.config_file_name is not None:
|
||||
fileConfig(config.config_file_name)
|
||||
|
||||
# add your model's MetaData object here
|
||||
# for 'autogenerate' support
|
||||
# from myapp import mymodel
|
||||
# target_metadata = mymodel.Base.metadata
|
||||
target_metadata = Base.metadata
|
||||
|
||||
# other values from the config, defined by the needs of env.py,
|
||||
# can be acquired:
|
||||
# my_important_option = config.get_main_option("my_important_option")
|
||||
# ... etc.
|
||||
|
||||
|
||||
def run_migrations_offline() -> None:
|
||||
"""Run migrations in 'offline' mode.
|
||||
|
||||
This configures the context with just a URL
|
||||
and not an Engine, though an Engine is acceptable
|
||||
here as well. By skipping the Engine creation
|
||||
we don't even need a DBAPI to be available.
|
||||
|
||||
Calls to context.execute() here emit the given string to the
|
||||
script output.
|
||||
|
||||
"""
|
||||
url = config.get_main_option("sqlalchemy.url")
|
||||
context.configure(
|
||||
url=url,
|
||||
target_metadata=target_metadata,
|
||||
literal_binds=True,
|
||||
dialect_opts={"paramstyle": "named"},
|
||||
)
|
||||
|
||||
with context.begin_transaction():
|
||||
context.run_migrations()
|
||||
|
||||
|
||||
def run_migrations_online() -> None:
|
||||
"""Run migrations in 'online' mode.
|
||||
|
||||
In this scenario we need to create an Engine
|
||||
and associate a connection with the context.
|
||||
|
||||
"""
|
||||
connectable = engine_from_config(
|
||||
config.get_section(config.config_ini_section, {}),
|
||||
prefix="sqlalchemy.",
|
||||
poolclass=pool.NullPool,
|
||||
)
|
||||
|
||||
with connectable.connect() as connection:
|
||||
context.configure(
|
||||
connection=connection, target_metadata=target_metadata
|
||||
)
|
||||
|
||||
with context.begin_transaction():
|
||||
context.run_migrations()
|
||||
|
||||
|
||||
if context.is_offline_mode():
|
||||
run_migrations_offline()
|
||||
else:
|
||||
run_migrations_online()
|
||||
```
|
||||
|
||||
### 3.4 脚本模板
|
||||
|
||||
Alembic 使用 `script.py.mako` 作为迁移脚本的模板,位于 `alembic` 目录:
|
||||
|
||||
```mako
|
||||
"""${message}
|
||||
|
||||
Revision ID: ${up_revision}
|
||||
Revises: ${down_revision | comma,n}
|
||||
Create Date: ${create_date}
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
${imports if imports else ""}
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = ${repr(up_revision)}
|
||||
down_revision = ${repr(down_revision)}
|
||||
branch_labels = ${repr(branch_labels)}
|
||||
depends_on = ${repr(depends_on)}
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
${upgrades if upgrades else "pass"}
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
${downgrades if downgrades else "pass"}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. 迁移操作
|
||||
|
||||
### 4.1 生成迁移脚本
|
||||
|
||||
#### 自动生成迁移脚本
|
||||
|
||||
当您修改了模型定义后,使用以下命令自动生成迁移脚本:
|
||||
|
||||
```bash
|
||||
# 在项目根目录执行
|
||||
alembic revision --autogenerate -m "描述迁移内容"
|
||||
|
||||
# 示例
|
||||
alembic revision --autogenerate -m "创建用户表"
|
||||
```
|
||||
|
||||
#### 手动创建迁移脚本
|
||||
|
||||
如果需要手动创建迁移脚本,使用以下命令:
|
||||
|
||||
```bash
|
||||
# 在项目根目录执行
|
||||
alembic revision -m "描述迁移内容"
|
||||
|
||||
# 示例
|
||||
alembic revision -m "添加用户状态字段"
|
||||
```
|
||||
|
||||
然后编辑生成的迁移脚本,添加具体的迁移操作。
|
||||
|
||||
### 4.2 应用迁移
|
||||
|
||||
#### 应用所有迁移
|
||||
|
||||
使用以下命令将所有未应用的迁移应用到数据库:
|
||||
|
||||
```bash
|
||||
# 在项目根目录执行
|
||||
alembic upgrade head
|
||||
```
|
||||
|
||||
#### 应用特定迁移
|
||||
|
||||
使用以下命令将迁移应用到特定版本:
|
||||
|
||||
```bash
|
||||
# 在项目根目录执行
|
||||
alembic upgrade <revision_id>
|
||||
|
||||
# 示例
|
||||
alembic upgrade 1234abcd
|
||||
```
|
||||
|
||||
### 4.3 回滚迁移
|
||||
|
||||
#### 回滚到上一个版本
|
||||
|
||||
使用以下命令回滚到上一个迁移版本:
|
||||
|
||||
```bash
|
||||
# 在项目根目录执行
|
||||
alembic downgrade -1
|
||||
```
|
||||
|
||||
#### 回滚到特定版本
|
||||
|
||||
使用以下命令回滚到特定迁移版本:
|
||||
|
||||
```bash
|
||||
# 在项目根目录执行
|
||||
alembic downgrade <revision_id>
|
||||
|
||||
# 示例
|
||||
alembic downgrade 1234abcd
|
||||
```
|
||||
|
||||
#### 回滚到初始状态
|
||||
|
||||
使用以下命令回滚到初始状态:
|
||||
|
||||
```bash
|
||||
# 在项目根目录执行
|
||||
alembic downgrade base
|
||||
```
|
||||
|
||||
### 4.4 查看迁移状态
|
||||
|
||||
使用以下命令查看当前的迁移状态:
|
||||
|
||||
```bash
|
||||
# 在项目根目录执行
|
||||
alembic current
|
||||
```
|
||||
|
||||
使用以下命令查看所有迁移版本:
|
||||
|
||||
```bash
|
||||
# 在项目根目录执行
|
||||
alembic history
|
||||
|
||||
# 显示更详细的信息
|
||||
alembic history --verbose
|
||||
|
||||
# 显示图形化的迁移树
|
||||
alembic history --graph
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. 迁移最佳实践
|
||||
|
||||
### 5.1 迁移脚本管理
|
||||
|
||||
1. **清晰的迁移消息**: 使用描述性的迁移消息,说明迁移的目的
|
||||
2. **版本控制**: 将迁移脚本纳入版本控制
|
||||
3. **测试迁移**: 在应用到生产环境之前,在测试环境中测试迁移
|
||||
4. **备份数据**: 在应用迁移之前,备份数据库
|
||||
5. **逐步迁移**: 对于大型迁移,考虑分步骤进行
|
||||
|
||||
### 5.2 迁移脚本编写
|
||||
|
||||
1. **保持迁移脚本简单**: 每个迁移脚本只包含一个逻辑变更
|
||||
2. **处理默认值**: 为新添加的列提供合理的默认值
|
||||
3. **处理数据迁移**: 如果需要迁移数据,在迁移脚本中添加相应的代码
|
||||
4. **处理约束**: 注意外键约束和唯一约束的处理
|
||||
5. **编写回滚脚本**: 确保每个迁移都有对应的回滚操作
|
||||
|
||||
### 5.3 常见迁移操作
|
||||
|
||||
#### 添加表
|
||||
|
||||
```python
|
||||
def upgrade() -> None:
|
||||
op.create_table(
|
||||
'users',
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('username', sa.String(length=50), nullable=False),
|
||||
sa.Column('password', sa.String(length=128), nullable=False),
|
||||
sa.PrimaryKeyConstraint('id'),
|
||||
sa.UniqueConstraint('username')
|
||||
)
|
||||
|
||||
def downgrade() -> None:
|
||||
op.drop_table('users')
|
||||
```
|
||||
|
||||
#### 添加列
|
||||
|
||||
```python
|
||||
def upgrade() -> None:
|
||||
op.add_column(
|
||||
'users',
|
||||
sa.Column('email', sa.String(length=100), nullable=True)
|
||||
)
|
||||
|
||||
def downgrade() -> None:
|
||||
op.drop_column('users', 'email')
|
||||
```
|
||||
|
||||
#### 修改列
|
||||
|
||||
```python
|
||||
def upgrade() -> None:
|
||||
op.alter_column(
|
||||
'users',
|
||||
'email',
|
||||
existing_type=sa.String(length=100),
|
||||
nullable=False
|
||||
)
|
||||
|
||||
def downgrade() -> None:
|
||||
op.alter_column(
|
||||
'users',
|
||||
'email',
|
||||
existing_type=sa.String(length=100),
|
||||
nullable=True
|
||||
)
|
||||
```
|
||||
|
||||
#### 添加索引
|
||||
|
||||
```python
|
||||
def upgrade() -> None:
|
||||
op.create_index(
|
||||
op.f('ix_users_email'),
|
||||
'users',
|
||||
['email'],
|
||||
unique=True
|
||||
)
|
||||
|
||||
def downgrade() -> None:
|
||||
op.drop_index(op.f('ix_users_email'), table_name='users')
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. 数据库初始化
|
||||
|
||||
### 6.1 初始迁移
|
||||
|
||||
当创建新项目时,需要执行初始迁移:
|
||||
|
||||
1. **创建模型**: 定义所有数据库模型
|
||||
2. **生成初始迁移**: `alembic revision --autogenerate -m "初始迁移"`
|
||||
3. **应用初始迁移**: `alembic upgrade head`
|
||||
|
||||
### 6.2 数据种子
|
||||
|
||||
在应用初始迁移后,可能需要添加一些初始数据,例如管理员用户:
|
||||
|
||||
```python
|
||||
# 在 app/database/seed.py 中
|
||||
|
||||
from sqlalchemy.orm import Session
|
||||
from app.models.user import User
|
||||
from app.database.session import get_db
|
||||
from passlib.context import CryptContext
|
||||
|
||||
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
|
||||
|
||||
def seed_data(db: Session) -> None:
|
||||
"""添加初始数据"""
|
||||
# 检查是否已有管理员用户
|
||||
admin_user = db.query(User).filter(User.username == "admin").first()
|
||||
if not admin_user:
|
||||
# 创建管理员用户
|
||||
admin_user = User(
|
||||
username="admin",
|
||||
password=pwd_context.hash("123456"),
|
||||
real_name="系统管理员",
|
||||
department="ADMIN",
|
||||
role="ADMIN",
|
||||
status=1
|
||||
)
|
||||
db.add(admin_user)
|
||||
|
||||
# 检查是否已有市场部用户
|
||||
marketing_user = db.query(User).filter(User.username == "marketing").first()
|
||||
if not marketing_user:
|
||||
# 创建市场部用户
|
||||
marketing_user = User(
|
||||
username="marketing",
|
||||
password=pwd_context.hash("123456"),
|
||||
real_name="市场部经理",
|
||||
department="MARKETING",
|
||||
role="MARKETING",
|
||||
status=1
|
||||
)
|
||||
db.add(marketing_user)
|
||||
|
||||
# 检查是否已有其他部门用户
|
||||
other_user = db.query(User).filter(User.username == "other").first()
|
||||
if not other_user:
|
||||
# 创建其他部门用户
|
||||
other_user = User(
|
||||
username="other",
|
||||
password=pwd_context.hash("123456"),
|
||||
real_name="技术部经理",
|
||||
department="TECHNOLOGY",
|
||||
role="OTHER",
|
||||
status=1
|
||||
)
|
||||
db.add(other_user)
|
||||
|
||||
db.commit()
|
||||
|
||||
if __name__ == "__main__":
|
||||
db = next(get_db())
|
||||
try:
|
||||
seed_data(db)
|
||||
print("数据种子添加成功")
|
||||
finally:
|
||||
db.close()
|
||||
```
|
||||
|
||||
执行数据种子脚本:
|
||||
|
||||
```bash
|
||||
# 在项目根目录执行
|
||||
python -m app.database.seed
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. 迁移示例
|
||||
|
||||
### 7.1 示例 1: 创建用户表
|
||||
|
||||
**迁移脚本**: `alembic/versions/1234abcd_create_user_table.py`
|
||||
|
||||
```python
|
||||
"""创建用户表
|
||||
|
||||
Revision ID: 1234abcd
|
||||
Revises:
|
||||
Create Date: 2025-01-01 00:00:00.000000
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '1234abcd'
|
||||
down_revision = None
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
op.create_table('sys_user',
|
||||
sa.Column('user_id', sa.String(length=32), nullable=False),
|
||||
sa.Column('username', sa.String(length=50), nullable=False),
|
||||
sa.Column('password', sa.String(length=128), nullable=False),
|
||||
sa.Column('real_name', sa.String(length=50), nullable=False),
|
||||
sa.Column('department', sa.String(length=50), nullable=False),
|
||||
sa.Column('phone', sa.String(length=20), nullable=True),
|
||||
sa.Column('email', sa.String(length=100), nullable=True),
|
||||
sa.Column('role', sa.String(length=20), nullable=False),
|
||||
sa.Column('status', sa.Integer(), nullable=False),
|
||||
sa.Column('create_time', sa.DateTime(), nullable=False),
|
||||
sa.Column('update_time', sa.DateTime(), nullable=False),
|
||||
sa.Column('last_login_time', sa.DateTime(), nullable=True),
|
||||
sa.PrimaryKeyConstraint('user_id'),
|
||||
sa.UniqueConstraint('username')
|
||||
)
|
||||
op.create_index(op.f('ix_sys_user_department'), 'sys_user', ['department'], unique=False)
|
||||
op.create_index(op.f('ix_sys_user_role'), 'sys_user', ['role'], unique=False)
|
||||
op.create_index(op.f('ix_sys_user_status'), 'sys_user', ['status'], unique=False)
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
op.drop_index(op.f('ix_sys_user_status'), table_name='sys_user')
|
||||
op.drop_index(op.f('ix_sys_user_role'), table_name='sys_user')
|
||||
op.drop_index(op.f('ix_sys_user_department'), table_name='sys_user')
|
||||
op.drop_table('sys_user')
|
||||
```
|
||||
|
||||
### 7.2 示例 2: 创建项目表
|
||||
|
||||
**迁移脚本**: `alembic/versions/5678efgh_create_project_table.py`
|
||||
|
||||
```python
|
||||
"""创建项目表
|
||||
|
||||
Revision ID: 5678efgh
|
||||
Revises: 1234abcd
|
||||
Create Date: 2025-01-02 00:00:00.000000
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '5678efgh'
|
||||
down_revision = '1234abcd'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
op.create_table('project',
|
||||
sa.Column('project_id', sa.String(length=32), nullable=False),
|
||||
sa.Column('project_no', sa.String(length=20), nullable=False),
|
||||
sa.Column('project_name', sa.String(length=200), nullable=False),
|
||||
sa.Column('status', sa.String(length=20), nullable=False),
|
||||
sa.Column('create_time', sa.DateTime(), nullable=False),
|
||||
sa.Column('creator', sa.String(length=50), nullable=False),
|
||||
sa.Column('update_time', sa.DateTime(), nullable=False),
|
||||
sa.Column('last_modifier', sa.String(length=50), nullable=True),
|
||||
sa.Column('leader', sa.String(length=50), nullable=False),
|
||||
sa.Column('phone', sa.String(length=20), nullable=True),
|
||||
sa.Column('email', sa.String(length=100), nullable=True),
|
||||
sa.Column('background', sa.Text(), nullable=True),
|
||||
sa.Column('goal', sa.Text(), nullable=True),
|
||||
sa.Column('scope', sa.Text(), nullable=True),
|
||||
sa.Column('start_date', sa.Date(), nullable=False),
|
||||
sa.Column('planned_end_date', sa.Date(), nullable=False),
|
||||
sa.Column('actual_end_date', sa.Date(), nullable=True),
|
||||
sa.Column('total_budget', sa.Numeric(precision=15, scale=2), nullable=False),
|
||||
sa.Column('used_budget', sa.Numeric(precision=15, scale=2), nullable=False),
|
||||
sa.Column('remaining_budget', sa.Numeric(precision=15, scale=2), nullable=False),
|
||||
sa.Column('remarks', sa.Text(), nullable=True),
|
||||
sa.PrimaryKeyConstraint('project_id'),
|
||||
sa.UniqueConstraint('project_no')
|
||||
)
|
||||
op.create_index(op.f('ix_project_create_time'), 'project', ['create_time'], unique=False)
|
||||
op.create_index(op.f('ix_project_creator'), 'project', ['creator'], unique=False)
|
||||
op.create_index(op.f('ix_project_leader'), 'project', ['leader'], unique=False)
|
||||
op.create_index(op.f('ix_project_status'), 'project', ['status'], unique=False)
|
||||
op.create_index(op.f('ix_project_update_time'), 'project', ['update_time'], unique=False)
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
op.drop_index(op.f('ix_project_update_time'), table_name='project')
|
||||
op.drop_index(op.f('ix_project_status'), table_name='project')
|
||||
op.drop_index(op.f('ix_project_leader'), table_name='project')
|
||||
op.drop_index(op.f('ix_project_creator'), table_name='project')
|
||||
op.drop_index(op.f('ix_project_create_time'), table_name='project')
|
||||
op.drop_table('project')
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 8. 常见问题
|
||||
|
||||
### 8.1 迁移失败
|
||||
|
||||
**问题**:迁移应用失败,出现错误
|
||||
|
||||
**解决方法**:
|
||||
1. 查看错误信息,了解失败原因
|
||||
2. 检查迁移脚本是否正确
|
||||
3. 检查数据库连接是否正常
|
||||
4. 如果需要,回滚到之前的版本并修复问题
|
||||
|
||||
### 8.2 自动生成的迁移脚本不正确
|
||||
|
||||
**问题**:Alembic 自动生成的迁移脚本与预期不符
|
||||
|
||||
**解决方法**:
|
||||
1. 检查模型定义是否正确
|
||||
2. 手动编辑生成的迁移脚本
|
||||
3. 确保所有模型都已导入到 `env.py` 中
|
||||
|
||||
### 8.3 数据库连接错误
|
||||
|
||||
**问题**:无法连接到数据库进行迁移
|
||||
|
||||
**解决方法**:
|
||||
1. 检查数据库服务是否运行
|
||||
2. 检查数据库连接字符串是否正确
|
||||
3. 检查数据库用户权限是否正确
|
||||
|
||||
### 8.4 迁移版本冲突
|
||||
|
||||
**问题**:多个开发者创建了相同版本号的迁移
|
||||
|
||||
**解决方法**:
|
||||
1. 使用唯一的迁移版本号
|
||||
2. 在提交迁移脚本前检查版本冲突
|
||||
3. 如果发生冲突,重新生成迁移脚本
|
||||
|
||||
---
|
||||
|
||||
## 9. 附录
|
||||
|
||||
### 9.1 常用命令汇总
|
||||
|
||||
| 命令 | 说明 | 示例 |
|
||||
|------|------|------|
|
||||
| `alembic init` | 初始化 Alembic | `alembic init alembic` |
|
||||
| `alembic revision --autogenerate` | 自动生成迁移脚本 | `alembic revision --autogenerate -m "创建用户表"` |
|
||||
| `alembic revision` | 手动创建迁移脚本 | `alembic revision -m "添加用户状态字段"` |
|
||||
| `alembic upgrade head` | 应用所有迁移 | `alembic upgrade head` |
|
||||
| `alembic upgrade <revision>` | 应用到特定版本 | `alembic upgrade 1234abcd` |
|
||||
| `alembic downgrade -1` | 回滚到上一个版本 | `alembic downgrade -1` |
|
||||
| `alembic downgrade <revision>` | 回滚到特定版本 | `alembic downgrade 1234abcd` |
|
||||
| `alembic downgrade base` | 回滚到初始状态 | `alembic downgrade base` |
|
||||
| `alembic current` | 查看当前版本 | `alembic current` |
|
||||
| `alembic history` | 查看所有版本 | `alembic history` |
|
||||
| `alembic history --verbose` | 查看详细版本信息 | `alembic history --verbose` |
|
||||
| `alembic history --graph` | 查看迁移树 | `alembic history --graph` |
|
||||
|
||||
### 9.2 迁移最佳实践总结
|
||||
|
||||
1. **版本控制**: 将迁移脚本纳入版本控制
|
||||
2. **测试**: 在应用到生产环境之前测试迁移
|
||||
3. **备份**: 在应用迁移之前备份数据库
|
||||
4. **简单**: 每个迁移只包含一个逻辑变更
|
||||
5. **清晰**: 使用描述性的迁移消息
|
||||
6. **完整**: 编写正确的升级和降级脚本
|
||||
7. **协作**: 与团队成员协调迁移工作
|
||||
8. **监控**: 监控迁移执行情况
|
||||
|
||||
### 9.3 变更记录
|
||||
|
||||
| 版本 | 日期 | 修改人 | 修改内容 |
|
||||
|------|------|--------|----------|
|
||||
| V1.0 | 2026-01-26 | - | 初始版本创建 |
|
||||
Reference in New Issue
Block a user