Skip to content

Commit f73f949

Browse files
author
JiayuXu
committed
feat: 为所有API添加Pydantic响应模型,完善Swagger文档
- 新增统一的响应模型基础设施(schemas/response.py) - 为37个API接口添加response_model参数 - 统一响应格式为{code, msg, data}结构 - 完善Swagger文档的返回值预览功能 - 支持分页、详情、操作结果等多种响应类型 - 提供完整的中文字段描述和类型安全保证 改动模块: - 用户管理、认证相关、角色管理 - 菜单管理、部门管理、API权限管理 - 审计日志、文件管理等所有API模块
1 parent f6adfe4 commit f73f949

10 files changed

Lines changed: 436 additions & 91 deletions

File tree

src/api/v1/apis/apis.py

Lines changed: 22 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,17 @@
1+
import json
2+
13
from fastapi import APIRouter, Query
24
from tortoise.expressions import Q
35

46
from repositories.api import api_repository
57
from schemas import Success, SuccessExtra
6-
from schemas.apis import *
8+
from schemas.apis import ApiCreate, ApiUpdate
9+
from schemas.response import ApiInfo, ApiListResponse, ResponseBase
710

811
router = APIRouter()
912

1013

11-
@router.get("/list", summary="查看API列表")
14+
@router.get("/list", summary="查看API列表", response_model=ApiListResponse)
1215
async def list_api(
1316
page: int = Query(1, description="页码"),
1417
page_size: int = Query(10, description="每页数量"),
@@ -27,43 +30,49 @@ async def list_api(
2730
page=page, page_size=page_size, search=q, order=["tags", "id"]
2831
)
2932
data = [await obj.to_dict() for obj in api_objs]
30-
return SuccessExtra(data=data, total=total, page=page, page_size=page_size)
33+
result = SuccessExtra(data=data, total=total, page=page, page_size=page_size)
34+
return json.loads(result.body)
3135

3236

33-
@router.get("/get", summary="查看Api")
37+
@router.get("/get", summary="查看Api", response_model=ResponseBase[ApiInfo])
3438
async def get_api(
3539
id: int = Query(..., description="Api"),
3640
):
3741
api_obj = await api_repository.get(id=id)
3842
data = await api_obj.to_dict()
39-
return Success(data=data)
43+
result = Success(data=data)
44+
return json.loads(result.body)
4045

4146

42-
@router.post("/create", summary="创建Api")
47+
@router.post("/create", summary="创建Api", response_model=ResponseBase[None])
4348
async def create_api(
4449
api_in: ApiCreate,
4550
):
4651
await api_repository.create(obj_in=api_in)
47-
return Success(msg="Created Successfully")
52+
result = Success(msg="Created Successfully")
53+
return json.loads(result.body)
4854

4955

50-
@router.post("/update", summary="更新Api")
56+
@router.post("/update", summary="更新Api", response_model=ResponseBase[None])
5157
async def update_api(
5258
api_in: ApiUpdate,
5359
):
5460
await api_repository.update(id=api_in.id, obj_in=api_in)
55-
return Success(msg="Update Successfully")
61+
result = Success(msg="Update Successfully")
62+
return json.loads(result.body)
5663

5764

58-
@router.delete("/delete", summary="删除Api")
65+
@router.delete("/delete", summary="删除Api", response_model=ResponseBase[None])
5966
async def delete_api(
6067
api_id: int = Query(..., description="ApiID"),
6168
):
6269
await api_repository.remove(id=api_id)
63-
return Success(msg="Deleted Success")
70+
result = Success(msg="Deleted Success")
71+
return json.loads(result.body)
6472

6573

66-
@router.post("/refresh", summary="刷新API列表")
74+
@router.post("/refresh", summary="刷新API列表", response_model=ResponseBase[None])
6775
async def refresh_api():
6876
await api_repository.refresh_api()
69-
return Success(msg="OK")
77+
result = Success(msg="OK")
78+
return json.loads(result.body)

src/api/v1/auditlog/auditlog.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,17 @@
1+
import json
12
from datetime import datetime
23

34
from fastapi import APIRouter, Query
45
from tortoise.expressions import Q
56

67
from models.admin import AuditLog
78
from schemas import SuccessExtra
8-
from schemas.apis import *
9+
from schemas.response import AuditLogListResponse
910

1011
router = APIRouter()
1112

1213

13-
@router.get("/list", summary="查看操作日志")
14+
@router.get("/list", summary="查看操作日志", response_model=AuditLogListResponse)
1415
async def get_audit_log_list(
1516
page: int = Query(1, description="页码"),
1617
page_size: int = Query(10, description="每页数量"),
@@ -48,4 +49,5 @@ async def get_audit_log_list(
4849
)
4950
total = await AuditLog.filter(q).count()
5051
data = [await audit_log.to_dict() for audit_log in audit_log_objs]
51-
return SuccessExtra(data=data, total=total, page=page, page_size=page_size)
52+
result = SuccessExtra(data=data, total=total, page=page, page_size=page_size)
53+
return json.loads(result.body)

src/api/v1/base/base.py

Lines changed: 36 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import json
12
import os
23
from datetime import UTC, datetime
34

@@ -16,6 +17,14 @@
1617
RefreshTokenRequest,
1718
TokenRefreshOut,
1819
)
20+
from schemas.response import (
21+
CurrentUserResponse,
22+
HealthInfo,
23+
HealthResponse,
24+
TokenResponse,
25+
VersionInfo,
26+
VersionResponse,
27+
)
1928
from settings import settings
2029
from utils.jwt import create_token_pair, verify_token
2130

@@ -29,7 +38,6 @@ def apply_rate_limit(rate="5/minute"):
2938
"""根据环境应用限流装饰器"""
3039

3140
def decorator(func):
32-
import os
3341

3442
if os.getenv("TESTING", "false").lower() == "true":
3543
return func # 测试环境不应用限流
@@ -38,7 +46,7 @@ def decorator(func):
3846
return decorator
3947

4048

41-
@router.post("/access_token", summary="获取token")
49+
@router.post("/access_token", summary="获取token", response_model=TokenResponse)
4250
@apply_rate_limit()
4351
async def login_access_token(request: Request, credentials: CredentialsSchema):
4452
user: User = await user_repository.authenticate(credentials)
@@ -55,10 +63,11 @@ async def login_access_token(request: Request, credentials: CredentialsSchema):
5563
username=user.username,
5664
expires_in=settings.JWT_ACCESS_TOKEN_EXPIRE_MINUTES * 60,
5765
)
58-
return Success(data=data.model_dump())
66+
result = Success(data=data.model_dump())
67+
return json.loads(result.body)
5968

6069

61-
@router.post("/refresh_token", summary="刷新token")
70+
@router.post("/refresh_token", summary="刷新token", response_model=TokenResponse)
6271
@apply_rate_limit("10/minute")
6372
async def refresh_access_token(request: Request, refresh_request: RefreshTokenRequest):
6473
"""
@@ -84,43 +93,45 @@ async def refresh_access_token(request: Request, refresh_request: RefreshTokenRe
8493
expires_in=settings.JWT_ACCESS_TOKEN_EXPIRE_MINUTES * 60,
8594
)
8695

87-
return Success(data=data.model_dump())
96+
result = Success(data=data.model_dump())
97+
return json.loads(result.body)
8898

8999
except Exception:
90-
return Fail(code=401, msg="令牌无效或已过期")
100+
result = Fail(code=401, msg="令牌无效或已过期")
101+
return json.loads(result.body)
91102

92103

93-
@router.get("/userinfo", summary="查看用户信息")
104+
@router.get("/userinfo", summary="查看用户信息", response_model=CurrentUserResponse)
94105
async def get_userinfo(current_user: User = DependAuth):
95106
user_id = CTX_USER_ID.get()
96107
user_obj = await user_repository.get(id=user_id)
97108
user_dict = await user_obj.to_dict()
98-
return Success(data=user_dict)
109+
result = Success(data=user_dict)
110+
return json.loads(result.body)
99111

100112

101-
@router.get("/health", summary="健康检查")
113+
@router.get("/health", summary="健康检查", response_model=HealthResponse)
102114
async def health_check():
103115
"""系统健康检查"""
104-
return {
105-
"status": "healthy",
106-
"timestamp": datetime.now(UTC).isoformat(),
107-
"version": settings.VERSION,
108-
"environment": settings.APP_ENV,
109-
"service": "FastAPI Backend Template",
110-
}
116+
health_data = HealthInfo(
117+
status="healthy",
118+
timestamp=datetime.now(UTC),
119+
environment=settings.APP_ENV,
120+
database="connected"
121+
)
122+
return HealthResponse(code=200, msg="OK", data=health_data)
111123

112124

113-
@router.get("/version", summary="版本信息")
125+
@router.get("/version", summary="版本信息", response_model=VersionResponse)
114126
async def get_version():
115127
"""获取API版本信息"""
116-
return {
117-
"version": settings.VERSION,
118-
"app_title": settings.APP_TITLE,
119-
"project_name": settings.PROJECT_NAME,
120-
"build": os.getenv("BUILD_NUMBER", "dev"),
121-
"commit": os.getenv("GIT_COMMIT", "unknown"),
122-
"python_version": os.getenv("PYTHON_VERSION", "3.11+"),
123-
}
128+
version_data = VersionInfo(
129+
app_name=settings.APP_TITLE,
130+
version=settings.VERSION,
131+
api_version="v1",
132+
environment=settings.APP_ENV
133+
)
134+
return VersionResponse(code=200, msg="OK", data=version_data)
124135

125136

126137
# @router.get("/usermenu", summary="查看用户菜单", dependencies=[DependAuth])

src/api/v1/depts/depts.py

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,48 +1,56 @@
1+
import json
2+
13
from fastapi import APIRouter, Query
24

35
from repositories.dept import dept_repository
46
from schemas import Success
5-
from schemas.depts import *
7+
from schemas.depts import DeptCreate, DeptUpdate
8+
from schemas.response import DeptDetailResponse, DeptListResponse, ResponseBase
69

710
router = APIRouter()
811

912

10-
@router.get("/list", summary="查看部门列表")
13+
@router.get("/list", summary="查看部门列表", response_model=DeptListResponse)
1114
async def list_dept(
1215
name: str = Query(None, description="部门名称"),
1316
):
1417
dept_tree = await dept_repository.get_dept_tree(name)
15-
return Success(data=dept_tree)
18+
result = Success(data=dept_tree)
19+
return json.loads(result.body)
1620

1721

18-
@router.get("/get", summary="查看部门")
22+
@router.get("/get", summary="查看部门", response_model=DeptDetailResponse)
1923
async def get_dept(
2024
id: int = Query(..., description="部门ID"),
2125
):
2226
dept_obj = await dept_repository.get(id=id)
2327
data = await dept_obj.to_dict()
24-
return Success(data=data)
28+
result = Success(data=data)
29+
return json.loads(result.body)
2530

2631

27-
@router.post("/create", summary="创建部门")
32+
@router.post("/create", summary="创建部门", response_model=ResponseBase[None])
2833
async def create_dept(
2934
dept_in: DeptCreate,
3035
):
3136
await dept_repository.create_dept(obj_in=dept_in)
32-
return Success(msg="Created Successfully")
37+
result = Success(msg="Created Successfully")
38+
return json.loads(result.body)
3339

3440

35-
@router.post("/update", summary="更新部门")
41+
@router.post("/update", summary="更新部门", response_model=ResponseBase[None])
3642
async def update_dept(
3743
dept_in: DeptUpdate,
3844
):
3945
await dept_repository.update_dept(obj_in=dept_in)
40-
return Success(msg="Update Successfully")
46+
result = Success(msg="Update Successfully")
47+
return json.loads(result.body)
4148

4249

43-
@router.delete("/delete", summary="删除部门")
50+
@router.delete("/delete", summary="删除部门", response_model=ResponseBase[None])
4451
async def delete_dept(
4552
dept_id: int = Query(..., description="部门ID"),
4653
):
4754
await dept_repository.delete_dept(dept_id=dept_id)
48-
return Success(msg="Deleted Success")
55+
result = Success(msg="Deleted Success")
56+
return json.loads(result.body)

src/api/v1/files/files.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
1+
import json
2+
13
from fastapi import APIRouter, File, UploadFile
24

35
from core.dependency import DependAuth
6+
from schemas.response import ResponseBase
47
from services.file_service import file_service
58

69
router = APIRouter()
@@ -10,6 +13,7 @@
1013
"/upload",
1114
summary="上传文件",
1215
dependencies=[DependAuth],
16+
response_model=ResponseBase[dict],
1317
)
1418
async def upload_file(
1519
file: UploadFile = File(..., description="要上传的文件"),
@@ -23,4 +27,5 @@ async def upload_file(
2327
Returns:
2428
上传成功的响应,包含文件信息
2529
"""
26-
return await file_service.upload_file(file)
30+
result = await file_service.upload_file(file)
31+
return json.loads(result.body)

0 commit comments

Comments
 (0)