update
This commit is contained in:
parent
9d6ba6cc29
commit
42943f9967
@ -4,6 +4,9 @@ HOST=0.0.0.0
|
||||
PORT=9001
|
||||
DEBUG=true
|
||||
|
||||
# API响应格式配置
|
||||
USE_STANDARD_RESPONSE=1
|
||||
|
||||
# 数据库配置
|
||||
DB_HOST=localhost
|
||||
DB_PORT=3306
|
||||
|
||||
50
README.md
50
README.md
@ -235,13 +235,59 @@ docker exec -it ai-dressing-app bash -c "env | sort"
|
||||
- 敏感信息应通过环境变量或Docker secrets进行管理
|
||||
- 建议将`.env`文件添加到`.gitignore`中,避免意外提交
|
||||
|
||||
## API 文档
|
||||
## API接口文档
|
||||
|
||||
启动服务后,访问以下地址查看自动生成的 API 文档:
|
||||
服务启动后,可通过以下地址访问API文档:
|
||||
|
||||
- Swagger UI: http://localhost:9001/docs
|
||||
- ReDoc: http://localhost:9001/redoc
|
||||
|
||||
## API响应格式
|
||||
|
||||
系统支持两种API响应格式,可以通过环境变量进行配置:
|
||||
|
||||
### 标准响应格式 (USE_STANDARD_RESPONSE=1)
|
||||
|
||||
所有API接口都会返回统一的标准格式:
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"message": "操作成功",
|
||||
"data": { ... }
|
||||
}
|
||||
```
|
||||
|
||||
错误响应示例:
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 400,
|
||||
"message": "请求参数错误",
|
||||
"data": null
|
||||
}
|
||||
```
|
||||
|
||||
### 简洁响应格式 (USE_STANDARD_RESPONSE=0)
|
||||
|
||||
API接口直接返回数据,不包含额外的包装:
|
||||
|
||||
```json
|
||||
{ ... } // 直接返回业务数据
|
||||
```
|
||||
|
||||
错误响应仍然保持标准格式,以确保错误信息清晰:
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 400,
|
||||
"message": "请求参数错误",
|
||||
"data": null
|
||||
}
|
||||
```
|
||||
|
||||
> 注意:健康检查端点 `/health` 和 `/` 始终返回简洁格式,不受环境变量影响。
|
||||
|
||||
## 主要 API 端点
|
||||
|
||||
### 大模型对话
|
||||
|
||||
@ -1,90 +1,82 @@
|
||||
from fastapi import FastAPI, Request, status
|
||||
from fastapi import FastAPI, Request
|
||||
from fastapi.responses import JSONResponse
|
||||
from fastapi.exceptions import RequestValidationError
|
||||
from starlette.exceptions import HTTPException as StarletteHTTPException
|
||||
import os
|
||||
import traceback
|
||||
import logging
|
||||
from typing import Any, Dict, Optional, Union
|
||||
|
||||
from app.utils.response import APIResponse
|
||||
from app.models.api_response import APIResponseModel
|
||||
|
||||
# 根据环境变量决定是否使用标准响应格式
|
||||
use_standard_response = os.environ.get("USE_STANDARD_RESPONSE") == "1"
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
class CustomHTTPException(Exception):
|
||||
"""自定义HTTP异常"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
status_code: int = 400,
|
||||
code: int = None,
|
||||
message: str = "操作失败",
|
||||
data: Any = None
|
||||
):
|
||||
"""自定义HTTP异常类,用于抛出自定义的HTTP错误"""
|
||||
def __init__(self, status_code: int, detail: str = None):
|
||||
self.status_code = status_code
|
||||
self.code = code or status_code
|
||||
self.message = message
|
||||
self.data = data
|
||||
super().__init__(self.message)
|
||||
self.detail = detail
|
||||
|
||||
def setup_exception_handlers(app: FastAPI) -> None:
|
||||
"""设置FastAPI应用的异常处理程序"""
|
||||
async def http_exception_handler(request: Request, exc: CustomHTTPException):
|
||||
"""处理自定义HTTP异常的处理器"""
|
||||
logger.error(f"HTTP错误: {exc.status_code} - {exc.detail}")
|
||||
|
||||
@app.exception_handler(CustomHTTPException)
|
||||
async def custom_http_exception_handler(request: Request, exc: CustomHTTPException):
|
||||
"""处理自定义HTTP异常"""
|
||||
logger.error(f"自定义HTTP异常: {exc.message} (状态码: {exc.status_code}, 业务码: {exc.code})")
|
||||
return JSONResponse(
|
||||
status_code=exc.status_code,
|
||||
content=APIResponse.error(
|
||||
message=exc.message,
|
||||
code=exc.code,
|
||||
data=exc.data
|
||||
)
|
||||
)
|
||||
return JSONResponse(
|
||||
status_code=exc.status_code,
|
||||
content=APIResponseModel(
|
||||
code=exc.status_code,
|
||||
message=exc.detail or "请求处理失败"
|
||||
).dict()
|
||||
)
|
||||
|
||||
@app.exception_handler(StarletteHTTPException)
|
||||
async def http_exception_handler(request: Request, exc: StarletteHTTPException):
|
||||
"""处理标准HTTP异常"""
|
||||
logger.error(f"HTTP异常: {exc.detail} (状态码: {exc.status_code})")
|
||||
return JSONResponse(
|
||||
status_code=exc.status_code,
|
||||
content=APIResponse.error(
|
||||
message=str(exc.detail),
|
||||
code=exc.status_code
|
||||
)
|
||||
)
|
||||
async def starlette_exception_handler(request: Request, exc: StarletteHTTPException):
|
||||
"""处理FastAPI内置的HTTP异常的处理器"""
|
||||
logger.error(f"Starlette HTTP错误: {exc.status_code} - {exc.detail}")
|
||||
|
||||
@app.exception_handler(RequestValidationError)
|
||||
async def validation_exception_handler(request: Request, exc: RequestValidationError):
|
||||
"""处理请求验证异常"""
|
||||
error_details = exc.errors()
|
||||
error_messages = []
|
||||
return JSONResponse(
|
||||
status_code=exc.status_code,
|
||||
content=APIResponseModel(
|
||||
code=exc.status_code,
|
||||
message=str(exc.detail)
|
||||
).dict()
|
||||
)
|
||||
|
||||
for error in error_details:
|
||||
location = " -> ".join(str(loc) for loc in error["loc"])
|
||||
message = f"{location}: {error['msg']}"
|
||||
error_messages.append(message)
|
||||
async def validation_exception_handler(request: Request, exc: RequestValidationError):
|
||||
"""处理请求验证错误的处理器"""
|
||||
error_detail = exc.errors()
|
||||
error_messages = [f"{e['loc'][-1]}: {e['msg']}" for e in error_detail]
|
||||
error_message = ", ".join(error_messages)
|
||||
logger.error(f"请求验证错误: {error_message}")
|
||||
|
||||
error_message = "; ".join(error_messages)
|
||||
logger.error(f"验证错误: {error_message}")
|
||||
return JSONResponse(
|
||||
status_code=422,
|
||||
content=APIResponseModel(
|
||||
code=422,
|
||||
message=f"请求参数验证失败: {error_message}",
|
||||
data=error_detail
|
||||
).dict()
|
||||
)
|
||||
|
||||
return JSONResponse(
|
||||
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
|
||||
content=APIResponse.error(
|
||||
message="请求参数验证失败",
|
||||
code=status.HTTP_422_UNPROCESSABLE_ENTITY,
|
||||
data={"details": error_details}
|
||||
)
|
||||
)
|
||||
async def general_exception_handler(request: Request, exc: Exception):
|
||||
"""处理一般性异常的处理器"""
|
||||
# 记录详细的错误信息
|
||||
error_msg = str(exc)
|
||||
logger.error(f"服务器错误: {error_msg}")
|
||||
logger.error(traceback.format_exc())
|
||||
|
||||
@app.exception_handler(Exception)
|
||||
async def general_exception_handler(request: Request, exc: Exception):
|
||||
"""处理通用异常"""
|
||||
logger.exception(f"未处理的异常: {str(exc)}")
|
||||
return JSONResponse(
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
content=APIResponse.error(
|
||||
message="服务器内部错误",
|
||||
code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
data={"detail": str(exc)} if app.debug else None
|
||||
)
|
||||
)
|
||||
return JSONResponse(
|
||||
status_code=500,
|
||||
content=APIResponseModel(
|
||||
code=500,
|
||||
message="服务器内部错误",
|
||||
data={"detail": error_msg} if os.environ.get("DEBUG") == "1" else None
|
||||
).dict()
|
||||
)
|
||||
|
||||
def setup_exception_handlers(app: FastAPI):
|
||||
"""为FastAPI应用设置所有异常处理器"""
|
||||
app.add_exception_handler(CustomHTTPException, http_exception_handler)
|
||||
app.add_exception_handler(StarletteHTTPException, starlette_exception_handler)
|
||||
app.add_exception_handler(RequestValidationError, validation_exception_handler)
|
||||
app.add_exception_handler(Exception, general_exception_handler)
|
||||
21
app/main.py
21
app/main.py
@ -24,6 +24,10 @@ logging.basicConfig(
|
||||
)
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# 全局配置:是否使用标准响应格式,默认禁用直接返回数据
|
||||
os.environ["USE_STANDARD_RESPONSE"] = os.environ.get("USE_STANDARD_RESPONSE", "0")
|
||||
logger.info(f"标准响应格式: {'已启用' if os.environ['USE_STANDARD_RESPONSE'] == '1' else '已禁用'}")
|
||||
|
||||
app = FastAPI(
|
||||
title="AI-Dressing API",
|
||||
description="基于 DashScope 的 AI 服务 API",
|
||||
@ -53,7 +57,7 @@ app.include_router(tryon_router.router, prefix="/api/tryons", tags=["试穿"])
|
||||
@app.get("/", tags=["健康检查"])
|
||||
async def root():
|
||||
"""API 根端点"""
|
||||
return APIResponse.ok(message="服务运行中")
|
||||
return {"message": "服务运行中"}
|
||||
|
||||
@app.get("/health", tags=["健康检查"])
|
||||
async def health_check():
|
||||
@ -86,9 +90,17 @@ async def health_check():
|
||||
health_data["checks"]["qcloud"] = {"status": "missing", "message": "腾讯云凭证未配置"}
|
||||
|
||||
if health_status == "healthy":
|
||||
return APIResponse.ok(data=health_data, message=health_message)
|
||||
return {
|
||||
"status": health_status,
|
||||
"message": health_message,
|
||||
"data": health_data
|
||||
}
|
||||
else:
|
||||
return APIResponse.error(message=health_message, code=503, data=health_data)
|
||||
return {
|
||||
"status": health_status,
|
||||
"message": health_message,
|
||||
"data": health_data
|
||||
}
|
||||
|
||||
@app.get("/info", tags=["服务信息"])
|
||||
async def get_info():
|
||||
@ -98,8 +110,9 @@ async def get_info():
|
||||
"app_name": "AI-Dressing API",
|
||||
"version": "0.1.0",
|
||||
"debug_mode": settings.debug,
|
||||
"standard_response": os.environ.get("USE_STANDARD_RESPONSE") == "1"
|
||||
}
|
||||
return APIResponse.ok(data=info_data, message="服务信息获取成功")
|
||||
return info_data
|
||||
|
||||
if __name__ == "__main__":
|
||||
import uvicorn
|
||||
|
||||
66
app/middleware.py
Normal file
66
app/middleware.py
Normal file
@ -0,0 +1,66 @@
|
||||
from fastapi import Request
|
||||
from starlette.middleware.base import BaseHTTPMiddleware
|
||||
from starlette.responses import Response, JSONResponse
|
||||
import json
|
||||
import os
|
||||
|
||||
from app.models.api_response import APIResponseModel
|
||||
from app.utils.response import APIResponse
|
||||
|
||||
class ResponseWrapperMiddleware(BaseHTTPMiddleware):
|
||||
async def dispatch(self, request: Request, call_next):
|
||||
# 检查是否需要启用标准响应包装
|
||||
use_standard_response = os.environ.get("USE_STANDARD_RESPONSE") == "1"
|
||||
|
||||
# 不包装的情况:健康检查端点或禁用了标准响应
|
||||
health_endpoints = ["/health", "/"]
|
||||
if request.url.path in health_endpoints or not use_standard_response:
|
||||
return await call_next(request)
|
||||
|
||||
response = await call_next(request)
|
||||
|
||||
# 如果响应已经被包装或者是文件下载等特殊响应,则不再处理
|
||||
if (
|
||||
isinstance(response, Response)
|
||||
and not isinstance(response, JSONResponse)
|
||||
or response.headers.get("content-type") != "application/json"
|
||||
):
|
||||
return response
|
||||
|
||||
# 处理JSON响应
|
||||
try:
|
||||
response_body = [section async for section in response.body_iterator]
|
||||
response.body_iterator = None
|
||||
|
||||
if len(response_body) > 0:
|
||||
body = response_body[0].decode()
|
||||
json_body = json.loads(body)
|
||||
|
||||
# 检查是否已经是标准格式
|
||||
if isinstance(json_body, dict) and "code" in json_body and "data" in json_body and "message" in json_body:
|
||||
# 已经是标准格式,直接返回
|
||||
new_response = Response(
|
||||
content=body,
|
||||
status_code=response.status_code,
|
||||
headers=dict(response.headers),
|
||||
media_type=response.media_type
|
||||
)
|
||||
return new_response
|
||||
|
||||
# 包装响应
|
||||
api_response = APIResponseModel(
|
||||
code=200,
|
||||
message="操作成功",
|
||||
data=json_body
|
||||
)
|
||||
|
||||
return JSONResponse(
|
||||
content=api_response.dict(),
|
||||
status_code=response.status_code,
|
||||
headers=dict(response.headers),
|
||||
)
|
||||
|
||||
return response
|
||||
except Exception as e:
|
||||
# 如果解析失败,返回原始响应
|
||||
return response
|
||||
@ -3,6 +3,9 @@ from fastapi.responses import JSONResponse
|
||||
from starlette.middleware.base import BaseHTTPMiddleware
|
||||
from typing import Any, Dict, Optional, Union
|
||||
import json
|
||||
import os
|
||||
|
||||
from app.utils.response import APIResponse
|
||||
|
||||
class ResponseWrapperMiddleware(BaseHTTPMiddleware):
|
||||
"""
|
||||
@ -14,11 +17,22 @@ class ResponseWrapperMiddleware(BaseHTTPMiddleware):
|
||||
"message": "操作成功",
|
||||
"data": 原始响应数据
|
||||
}
|
||||
|
||||
可以通过设置环境变量USE_STANDARD_RESPONSE=0来禁用标准响应格式
|
||||
"""
|
||||
|
||||
def __init__(self, app):
|
||||
super().__init__(app)
|
||||
# 从环境变量中读取是否启用标准响应格式
|
||||
self.use_standard_response = os.environ.get("USE_STANDARD_RESPONSE", "1") == "1"
|
||||
|
||||
async def dispatch(
|
||||
self, request: Request, call_next
|
||||
) -> Response:
|
||||
# 如果禁用了标准响应,直接返回原始响应
|
||||
if not self.use_standard_response:
|
||||
return await call_next(request)
|
||||
|
||||
# 排除不需要包装的路径
|
||||
if self._should_skip_path(request.url.path):
|
||||
return await call_next(request)
|
||||
|
||||
@ -1 +1 @@
|
||||
|
||||
from app.models.api_response import APIResponseModel, PaginatedResponseModel
|
||||
27
app/models/api_response.py
Normal file
27
app/models/api_response.py
Normal file
@ -0,0 +1,27 @@
|
||||
from typing import Any, Dict, List, Optional, Union, ClassVar
|
||||
from pydantic import BaseModel, Field
|
||||
from datetime import datetime
|
||||
|
||||
class APIResponseModel(BaseModel):
|
||||
"""API响应基础模型"""
|
||||
code: int = Field(200, description="业务状态码")
|
||||
message: str = Field("操作成功", description="响应消息")
|
||||
data: Optional[Any] = Field(None, description="响应数据")
|
||||
|
||||
class Config:
|
||||
"""模型配置"""
|
||||
arbitrary_types_allowed = True
|
||||
json_encoders = {
|
||||
datetime: lambda dt: dt.isoformat()
|
||||
}
|
||||
|
||||
class PaginatedResponseModel(BaseModel):
|
||||
"""分页响应模型"""
|
||||
items: List[Any] = Field(..., description="数据项列表")
|
||||
total: int = Field(..., description="总数据量")
|
||||
page: int = Field(1, description="当前页码")
|
||||
size: int = Field(10, description="每页数据量")
|
||||
|
||||
class Config:
|
||||
"""模型配置"""
|
||||
arbitrary_types_allowed = True
|
||||
@ -4,7 +4,7 @@ from typing import List
|
||||
|
||||
from app.database import get_db
|
||||
from app.models.dress import Dress
|
||||
from app.schemas.dress import DressCreate, DressUpdate, DressResponse, DressListResponse
|
||||
from app.schemas.dress import DressCreate, DressUpdate, DressResponse
|
||||
from app.utils.response import APIResponse
|
||||
|
||||
router = APIRouter()
|
||||
@ -17,12 +17,15 @@ def create_dress(dress: DressCreate, db: Session = Depends(get_db)):
|
||||
db.add(db_dress)
|
||||
db.commit()
|
||||
db.refresh(db_dress)
|
||||
return APIResponse.created(data=db_dress, message="服装创建成功")
|
||||
return db_dress
|
||||
except Exception as e:
|
||||
db.rollback()
|
||||
return APIResponse.error(message=f"服装创建失败: {str(e)}", code=500)
|
||||
raise HTTPException(
|
||||
status_code=500,
|
||||
detail=f"服装创建失败: {str(e)}"
|
||||
)
|
||||
|
||||
@router.get("/", response_model=DressListResponse)
|
||||
@router.get("/", response_model=List[DressResponse])
|
||||
def get_all_dresses(
|
||||
skip: int = 0,
|
||||
limit: int = 100,
|
||||
@ -32,28 +35,32 @@ def get_all_dresses(
|
||||
try:
|
||||
dresses = db.query(Dress).offset(skip).limit(limit).all()
|
||||
total = db.query(Dress).count()
|
||||
return APIResponse.ok(
|
||||
data={
|
||||
"items": dresses,
|
||||
"total": total,
|
||||
"page": skip // limit + 1 if limit > 0 else 1,
|
||||
"size": limit
|
||||
},
|
||||
message="服装列表获取成功"
|
||||
)
|
||||
|
||||
# 构建分页数据
|
||||
result = {
|
||||
"items": dresses,
|
||||
"total": total,
|
||||
"page": skip // limit + 1 if limit > 0 else 1,
|
||||
"size": limit
|
||||
}
|
||||
|
||||
return result
|
||||
except Exception as e:
|
||||
return APIResponse.error(message=f"获取服装列表失败: {str(e)}", code=500)
|
||||
raise HTTPException(
|
||||
status_code=500,
|
||||
detail=f"获取服装列表失败: {str(e)}"
|
||||
)
|
||||
|
||||
@router.get("/{dress_id}", response_model=DressResponse)
|
||||
def get_dress(dress_id: int, db: Session = Depends(get_db)):
|
||||
"""获取单个服装记录"""
|
||||
try:
|
||||
dress = db.query(Dress).filter(Dress.id == dress_id).first()
|
||||
if dress is None:
|
||||
return APIResponse.not_found(message=f"未找到ID为{dress_id}的服装")
|
||||
return APIResponse.ok(data=dress, message="服装获取成功")
|
||||
except Exception as e:
|
||||
return APIResponse.error(message=f"获取服装失败: {str(e)}", code=500)
|
||||
dress = db.query(Dress).filter(Dress.id == dress_id).first()
|
||||
if dress is None:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND,
|
||||
detail=f"未找到ID为{dress_id}的服装"
|
||||
)
|
||||
return dress
|
||||
|
||||
@router.put("/{dress_id}", response_model=DressResponse)
|
||||
def update_dress(dress_id: int, dress: DressUpdate, db: Session = Depends(get_db)):
|
||||
@ -61,7 +68,10 @@ def update_dress(dress_id: int, dress: DressUpdate, db: Session = Depends(get_db
|
||||
try:
|
||||
db_dress = db.query(Dress).filter(Dress.id == dress_id).first()
|
||||
if db_dress is None:
|
||||
return APIResponse.not_found(message=f"未找到ID为{dress_id}的服装")
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND,
|
||||
detail=f"未找到ID为{dress_id}的服装"
|
||||
)
|
||||
|
||||
# 更新服装字段
|
||||
for field, value in dress.dict(exclude_unset=True).items():
|
||||
@ -69,22 +79,37 @@ def update_dress(dress_id: int, dress: DressUpdate, db: Session = Depends(get_db
|
||||
|
||||
db.commit()
|
||||
db.refresh(db_dress)
|
||||
return APIResponse.ok(data=db_dress, message="服装更新成功")
|
||||
return db_dress
|
||||
except HTTPException:
|
||||
db.rollback()
|
||||
raise
|
||||
except Exception as e:
|
||||
db.rollback()
|
||||
return APIResponse.error(message=f"更新服装失败: {str(e)}", code=500)
|
||||
raise HTTPException(
|
||||
status_code=500,
|
||||
detail=f"更新服装失败: {str(e)}"
|
||||
)
|
||||
|
||||
@router.delete("/{dress_id}")
|
||||
@router.delete("/{dress_id}", status_code=status.HTTP_200_OK)
|
||||
def delete_dress(dress_id: int, db: Session = Depends(get_db)):
|
||||
"""删除服装记录"""
|
||||
try:
|
||||
db_dress = db.query(Dress).filter(Dress.id == dress_id).first()
|
||||
if db_dress is None:
|
||||
return APIResponse.not_found(message=f"未找到ID为{dress_id}的服装")
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND,
|
||||
detail=f"未找到ID为{dress_id}的服装"
|
||||
)
|
||||
|
||||
db.delete(db_dress)
|
||||
db.commit()
|
||||
return APIResponse.ok(message="服装删除成功")
|
||||
return {"message": "服装删除成功"}
|
||||
except HTTPException:
|
||||
db.rollback()
|
||||
raise
|
||||
except Exception as e:
|
||||
db.rollback()
|
||||
return APIResponse.error(message=f"删除服装失败: {str(e)}", code=500)
|
||||
raise HTTPException(
|
||||
status_code=500,
|
||||
detail=f"删除服装失败: {str(e)}"
|
||||
)
|
||||
@ -24,7 +24,7 @@ router = APIRouter()
|
||||
DASHSCOPE_API_KEY = os.getenv("DASHSCOPE_API_KEY")
|
||||
DASHSCOPE_API_URL = "https://dashscope.aliyuncs.com/api/v1/services/aigc/image2image/image-synthesis"
|
||||
|
||||
@router.post("/", response_model=TryOnResponse, status_code=201)
|
||||
@router.post("", response_model=TryOnResponse, status_code=201)
|
||||
async def create_tryon(
|
||||
tryon_data: TryOnCreate,
|
||||
background_tasks: BackgroundTasks,
|
||||
@ -63,7 +63,7 @@ async def create_tryon(
|
||||
person_image_url=tryon_data.person_image_url
|
||||
)
|
||||
|
||||
return APIResponse.ok(data=db_tryon, message="试穿记录创建成功")
|
||||
return db_tryon
|
||||
except Exception as e:
|
||||
logger.error(f"创建试穿记录失败: {str(e)}")
|
||||
db.rollback()
|
||||
@ -120,7 +120,8 @@ async def get_tryons(
|
||||
"""
|
||||
try:
|
||||
tryons = db.query(TryOn).order_by(TryOn.id.desc()).offset(skip).limit(limit).all()
|
||||
return APIResponse.ok(data=tryons, message="试穿记录列表获取成功")
|
||||
|
||||
return tryons
|
||||
except Exception as e:
|
||||
logger.error(f"获取试穿记录列表失败: {str(e)}")
|
||||
raise HTTPException(status_code=500, detail=f"获取试穿记录列表失败: {str(e)}")
|
||||
@ -139,7 +140,7 @@ async def get_tryon(
|
||||
tryon = db.query(TryOn).filter(TryOn.id == tryon_id).first()
|
||||
if not tryon:
|
||||
raise HTTPException(status_code=404, detail=f"未找到ID为{tryon_id}的试穿记录")
|
||||
return APIResponse.ok(data=tryon, message="试穿记录获取成功")
|
||||
return tryon
|
||||
except HTTPException:
|
||||
raise
|
||||
except Exception as e:
|
||||
@ -172,7 +173,7 @@ async def update_tryon(
|
||||
|
||||
db.commit()
|
||||
db.refresh(db_tryon)
|
||||
return APIResponse.ok(data=db_tryon, message="试穿记录更新成功")
|
||||
return db_tryon
|
||||
except HTTPException:
|
||||
raise
|
||||
except Exception as e:
|
||||
@ -217,7 +218,7 @@ async def check_tryon_status(
|
||||
except Exception as e:
|
||||
logger.error(f"调用DashScope API检查任务状态失败: {str(e)}")
|
||||
|
||||
return APIResponse.ok(data=db_tryon, message="试穿任务状态检查成功")
|
||||
return db_tryon
|
||||
except HTTPException:
|
||||
raise
|
||||
except Exception as e:
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
from pydantic import BaseModel, Field, HttpUrl
|
||||
from typing import Optional, List
|
||||
from typing import Optional, List, Any, Dict, Union
|
||||
from datetime import datetime
|
||||
from enum import Enum
|
||||
from app.models.dress import GarmentType
|
||||
@ -39,8 +39,9 @@ class DressResponse(DressInDB):
|
||||
"""服装API响应模型"""
|
||||
pass
|
||||
|
||||
class DressListResponse(BaseModel):
|
||||
items: List[DressResponse]
|
||||
class PaginatedResponse(BaseModel):
|
||||
"""通用分页响应"""
|
||||
items: List[Any]
|
||||
total: int
|
||||
page: int
|
||||
size: int
|
||||
@ -50,7 +51,6 @@ class DressListResponse(BaseModel):
|
||||
|
||||
class StandardResponse(BaseModel):
|
||||
"""标准API响应格式"""
|
||||
success: bool = True
|
||||
code: int = 200
|
||||
message: str = "操作成功"
|
||||
data: Optional[dict] = None
|
||||
data: Optional[Any] = None
|
||||
@ -1,58 +1,57 @@
|
||||
from typing import Any, Dict, List, Optional, Union
|
||||
from typing import Any, Dict, List, Optional, Union, ClassVar
|
||||
from fastapi import status
|
||||
from fastapi.responses import JSONResponse
|
||||
import os
|
||||
|
||||
from app.models.api_response import APIResponseModel
|
||||
|
||||
class APIResponse:
|
||||
"""API标准响应格式工具类"""
|
||||
|
||||
@staticmethod
|
||||
def success(
|
||||
data: Any = None,
|
||||
message: str = "操作成功",
|
||||
code: int = 200
|
||||
) -> Dict[str, Any]:
|
||||
"""
|
||||
成功响应
|
||||
|
||||
Args:
|
||||
data: 响应数据
|
||||
message: 响应消息
|
||||
code: 状态码
|
||||
|
||||
Returns:
|
||||
标准响应格式的字典
|
||||
"""
|
||||
return {
|
||||
"success": True,
|
||||
"code": code,
|
||||
"message": message,
|
||||
"data": data
|
||||
}
|
||||
# 全局开关,控制是否使用标准响应格式
|
||||
USE_STANDARD_RESPONSE = os.environ.get("USE_STANDARD_RESPONSE", "1") == "1"
|
||||
|
||||
@staticmethod
|
||||
def error(
|
||||
message: str = "操作失败",
|
||||
code: int = 400,
|
||||
data: Any = None
|
||||
) -> Dict[str, Any]:
|
||||
"""
|
||||
错误响应
|
||||
def ok(data: Any = None, message: str = "操作成功") -> Dict[str, Any]:
|
||||
"""成功响应"""
|
||||
if not APIResponse.USE_STANDARD_RESPONSE:
|
||||
return data
|
||||
|
||||
Args:
|
||||
message: 错误消息
|
||||
code: 错误状态码
|
||||
data: 附加错误数据
|
||||
return APIResponseModel(
|
||||
code=200,
|
||||
message=message,
|
||||
data=data
|
||||
).dict()
|
||||
|
||||
Returns:
|
||||
标准响应格式的字典
|
||||
@staticmethod
|
||||
def error(message: str = "操作失败", code: int = 400, data: Any = None) -> Dict[str, Any]:
|
||||
"""错误响应"""
|
||||
# 错误响应即使禁用了标准格式也返回标准格式,确保错误信息清晰
|
||||
return APIResponseModel(
|
||||
code=code,
|
||||
message=message,
|
||||
data=data
|
||||
).dict()
|
||||
|
||||
@staticmethod
|
||||
def format_response(data: Any, message: str = "操作成功", code: int = 200) -> Dict:
|
||||
"""
|
||||
return {
|
||||
"success": False,
|
||||
"code": code,
|
||||
"message": message,
|
||||
"data": data
|
||||
根据环境变量决定是否返回标准格式的响应
|
||||
|
||||
如果USE_STANDARD_RESPONSE=1,返回标准格式:
|
||||
{
|
||||
"code": 200,
|
||||
"message": "操作成功",
|
||||
"data": {...}
|
||||
}
|
||||
|
||||
如果USE_STANDARD_RESPONSE=0,直接返回data
|
||||
"""
|
||||
if APIResponse.USE_STANDARD_RESPONSE:
|
||||
return APIResponseModel(code=code, message=message, data=data).dict()
|
||||
else:
|
||||
return data if data is not None else {"message": message}
|
||||
|
||||
@staticmethod
|
||||
def json_response(
|
||||
data: Any = None,
|
||||
@ -76,12 +75,18 @@ class APIResponse:
|
||||
Returns:
|
||||
JSONResponse对象
|
||||
"""
|
||||
content = {
|
||||
"success": success,
|
||||
"code": code,
|
||||
"message": message,
|
||||
"data": data
|
||||
}
|
||||
if not APIResponse.USE_STANDARD_RESPONSE and success:
|
||||
return JSONResponse(
|
||||
content=data,
|
||||
status_code=status_code,
|
||||
headers=headers
|
||||
)
|
||||
|
||||
content = APIResponseModel(
|
||||
code=code,
|
||||
message=message,
|
||||
data=data
|
||||
).dict()
|
||||
|
||||
return JSONResponse(
|
||||
content=content,
|
||||
@ -90,25 +95,26 @@ class APIResponse:
|
||||
)
|
||||
|
||||
# 常用响应码封装
|
||||
@classmethod
|
||||
def ok(cls, data: Any = None, message: str = "操作成功") -> Dict[str, Any]:
|
||||
"""200 成功"""
|
||||
return cls.success(data, message, 200)
|
||||
|
||||
@classmethod
|
||||
def created(cls, data: Any = None, message: str = "创建成功") -> Dict[str, Any]:
|
||||
"""201 创建成功"""
|
||||
return cls.success(data, message, 201)
|
||||
if not cls.USE_STANDARD_RESPONSE:
|
||||
return data
|
||||
return cls.ok(data, message)
|
||||
|
||||
@classmethod
|
||||
def accepted(cls, data: Any = None, message: str = "请求已接受") -> Dict[str, Any]:
|
||||
"""202 已接受"""
|
||||
return cls.success(data, message, 202)
|
||||
if not cls.USE_STANDARD_RESPONSE:
|
||||
return data
|
||||
return cls.ok(data, message)
|
||||
|
||||
@classmethod
|
||||
def no_content(cls) -> Dict[str, Any]:
|
||||
"""204 无内容"""
|
||||
return cls.success(None, "无内容", 204)
|
||||
if not cls.USE_STANDARD_RESPONSE:
|
||||
return None
|
||||
return cls.ok(None, "无内容")
|
||||
|
||||
@classmethod
|
||||
def bad_request(cls, message: str = "请求参数错误") -> Dict[str, Any]:
|
||||
|
||||
Loading…
Reference in New Issue
Block a user