update
This commit is contained in:
parent
9d6ba6cc29
commit
42943f9967
@ -4,6 +4,9 @@ HOST=0.0.0.0
|
|||||||
PORT=9001
|
PORT=9001
|
||||||
DEBUG=true
|
DEBUG=true
|
||||||
|
|
||||||
|
# API响应格式配置
|
||||||
|
USE_STANDARD_RESPONSE=1
|
||||||
|
|
||||||
# 数据库配置
|
# 数据库配置
|
||||||
DB_HOST=localhost
|
DB_HOST=localhost
|
||||||
DB_PORT=3306
|
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进行管理
|
- 敏感信息应通过环境变量或Docker secrets进行管理
|
||||||
- 建议将`.env`文件添加到`.gitignore`中,避免意外提交
|
- 建议将`.env`文件添加到`.gitignore`中,避免意外提交
|
||||||
|
|
||||||
## API 文档
|
## API接口文档
|
||||||
|
|
||||||
启动服务后,访问以下地址查看自动生成的 API 文档:
|
服务启动后,可通过以下地址访问API文档:
|
||||||
|
|
||||||
- Swagger UI: http://localhost:9001/docs
|
- Swagger UI: http://localhost:9001/docs
|
||||||
- ReDoc: http://localhost:9001/redoc
|
- 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 端点
|
## 主要 API 端点
|
||||||
|
|
||||||
### 大模型对话
|
### 大模型对话
|
||||||
|
|||||||
@ -1,90 +1,82 @@
|
|||||||
from fastapi import FastAPI, Request, status
|
from fastapi import FastAPI, Request
|
||||||
from fastapi.responses import JSONResponse
|
from fastapi.responses import JSONResponse
|
||||||
from fastapi.exceptions import RequestValidationError
|
from fastapi.exceptions import RequestValidationError
|
||||||
from starlette.exceptions import HTTPException as StarletteHTTPException
|
from starlette.exceptions import HTTPException as StarletteHTTPException
|
||||||
|
import os
|
||||||
|
import traceback
|
||||||
import logging
|
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__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
class CustomHTTPException(Exception):
|
class CustomHTTPException(Exception):
|
||||||
"""自定义HTTP异常"""
|
"""自定义HTTP异常类,用于抛出自定义的HTTP错误"""
|
||||||
|
def __init__(self, status_code: int, detail: str = None):
|
||||||
def __init__(
|
|
||||||
self,
|
|
||||||
status_code: int = 400,
|
|
||||||
code: int = None,
|
|
||||||
message: str = "操作失败",
|
|
||||||
data: Any = None
|
|
||||||
):
|
|
||||||
self.status_code = status_code
|
self.status_code = status_code
|
||||||
self.code = code or status_code
|
self.detail = detail
|
||||||
self.message = message
|
|
||||||
self.data = data
|
|
||||||
super().__init__(self.message)
|
|
||||||
|
|
||||||
def setup_exception_handlers(app: FastAPI) -> None:
|
async def http_exception_handler(request: Request, exc: CustomHTTPException):
|
||||||
"""设置FastAPI应用的异常处理程序"""
|
"""处理自定义HTTP异常的处理器"""
|
||||||
|
logger.error(f"HTTP错误: {exc.status_code} - {exc.detail}")
|
||||||
|
|
||||||
@app.exception_handler(CustomHTTPException)
|
return JSONResponse(
|
||||||
async def custom_http_exception_handler(request: Request, exc: CustomHTTPException):
|
status_code=exc.status_code,
|
||||||
"""处理自定义HTTP异常"""
|
content=APIResponseModel(
|
||||||
logger.error(f"自定义HTTP异常: {exc.message} (状态码: {exc.status_code}, 业务码: {exc.code})")
|
code=exc.status_code,
|
||||||
return JSONResponse(
|
message=exc.detail or "请求处理失败"
|
||||||
status_code=exc.status_code,
|
).dict()
|
||||||
content=APIResponse.error(
|
)
|
||||||
message=exc.message,
|
|
||||||
code=exc.code,
|
|
||||||
data=exc.data
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
@app.exception_handler(StarletteHTTPException)
|
async def starlette_exception_handler(request: Request, exc: StarletteHTTPException):
|
||||||
async def http_exception_handler(request: Request, exc: StarletteHTTPException):
|
"""处理FastAPI内置的HTTP异常的处理器"""
|
||||||
"""处理标准HTTP异常"""
|
logger.error(f"Starlette HTTP错误: {exc.status_code} - {exc.detail}")
|
||||||
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
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
@app.exception_handler(RequestValidationError)
|
return JSONResponse(
|
||||||
async def validation_exception_handler(request: Request, exc: RequestValidationError):
|
status_code=exc.status_code,
|
||||||
"""处理请求验证异常"""
|
content=APIResponseModel(
|
||||||
error_details = exc.errors()
|
code=exc.status_code,
|
||||||
error_messages = []
|
message=str(exc.detail)
|
||||||
|
).dict()
|
||||||
|
)
|
||||||
|
|
||||||
for error in error_details:
|
async def validation_exception_handler(request: Request, exc: RequestValidationError):
|
||||||
location = " -> ".join(str(loc) for loc in error["loc"])
|
"""处理请求验证错误的处理器"""
|
||||||
message = f"{location}: {error['msg']}"
|
error_detail = exc.errors()
|
||||||
error_messages.append(message)
|
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)
|
return JSONResponse(
|
||||||
logger.error(f"验证错误: {error_message}")
|
status_code=422,
|
||||||
|
content=APIResponseModel(
|
||||||
|
code=422,
|
||||||
|
message=f"请求参数验证失败: {error_message}",
|
||||||
|
data=error_detail
|
||||||
|
).dict()
|
||||||
|
)
|
||||||
|
|
||||||
return JSONResponse(
|
async def general_exception_handler(request: Request, exc: Exception):
|
||||||
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
|
"""处理一般性异常的处理器"""
|
||||||
content=APIResponse.error(
|
# 记录详细的错误信息
|
||||||
message="请求参数验证失败",
|
error_msg = str(exc)
|
||||||
code=status.HTTP_422_UNPROCESSABLE_ENTITY,
|
logger.error(f"服务器错误: {error_msg}")
|
||||||
data={"details": error_details}
|
logger.error(traceback.format_exc())
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
@app.exception_handler(Exception)
|
return JSONResponse(
|
||||||
async def general_exception_handler(request: Request, exc: Exception):
|
status_code=500,
|
||||||
"""处理通用异常"""
|
content=APIResponseModel(
|
||||||
logger.exception(f"未处理的异常: {str(exc)}")
|
code=500,
|
||||||
return JSONResponse(
|
message="服务器内部错误",
|
||||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
data={"detail": error_msg} if os.environ.get("DEBUG") == "1" else None
|
||||||
content=APIResponse.error(
|
).dict()
|
||||||
message="服务器内部错误",
|
)
|
||||||
code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
|
||||||
data={"detail": str(exc)} if app.debug else None
|
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__)
|
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(
|
app = FastAPI(
|
||||||
title="AI-Dressing API",
|
title="AI-Dressing API",
|
||||||
description="基于 DashScope 的 AI 服务 API",
|
description="基于 DashScope 的 AI 服务 API",
|
||||||
@ -53,7 +57,7 @@ app.include_router(tryon_router.router, prefix="/api/tryons", tags=["试穿"])
|
|||||||
@app.get("/", tags=["健康检查"])
|
@app.get("/", tags=["健康检查"])
|
||||||
async def root():
|
async def root():
|
||||||
"""API 根端点"""
|
"""API 根端点"""
|
||||||
return APIResponse.ok(message="服务运行中")
|
return {"message": "服务运行中"}
|
||||||
|
|
||||||
@app.get("/health", tags=["健康检查"])
|
@app.get("/health", tags=["健康检查"])
|
||||||
async def health_check():
|
async def health_check():
|
||||||
@ -86,9 +90,17 @@ async def health_check():
|
|||||||
health_data["checks"]["qcloud"] = {"status": "missing", "message": "腾讯云凭证未配置"}
|
health_data["checks"]["qcloud"] = {"status": "missing", "message": "腾讯云凭证未配置"}
|
||||||
|
|
||||||
if health_status == "healthy":
|
if health_status == "healthy":
|
||||||
return APIResponse.ok(data=health_data, message=health_message)
|
return {
|
||||||
|
"status": health_status,
|
||||||
|
"message": health_message,
|
||||||
|
"data": health_data
|
||||||
|
}
|
||||||
else:
|
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=["服务信息"])
|
@app.get("/info", tags=["服务信息"])
|
||||||
async def get_info():
|
async def get_info():
|
||||||
@ -98,8 +110,9 @@ async def get_info():
|
|||||||
"app_name": "AI-Dressing API",
|
"app_name": "AI-Dressing API",
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"debug_mode": settings.debug,
|
"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__":
|
if __name__ == "__main__":
|
||||||
import uvicorn
|
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 starlette.middleware.base import BaseHTTPMiddleware
|
||||||
from typing import Any, Dict, Optional, Union
|
from typing import Any, Dict, Optional, Union
|
||||||
import json
|
import json
|
||||||
|
import os
|
||||||
|
|
||||||
|
from app.utils.response import APIResponse
|
||||||
|
|
||||||
class ResponseWrapperMiddleware(BaseHTTPMiddleware):
|
class ResponseWrapperMiddleware(BaseHTTPMiddleware):
|
||||||
"""
|
"""
|
||||||
@ -14,11 +17,22 @@ class ResponseWrapperMiddleware(BaseHTTPMiddleware):
|
|||||||
"message": "操作成功",
|
"message": "操作成功",
|
||||||
"data": 原始响应数据
|
"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(
|
async def dispatch(
|
||||||
self, request: Request, call_next
|
self, request: Request, call_next
|
||||||
) -> Response:
|
) -> Response:
|
||||||
|
# 如果禁用了标准响应,直接返回原始响应
|
||||||
|
if not self.use_standard_response:
|
||||||
|
return await call_next(request)
|
||||||
|
|
||||||
# 排除不需要包装的路径
|
# 排除不需要包装的路径
|
||||||
if self._should_skip_path(request.url.path):
|
if self._should_skip_path(request.url.path):
|
||||||
return await call_next(request)
|
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.database import get_db
|
||||||
from app.models.dress import Dress
|
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
|
from app.utils.response import APIResponse
|
||||||
|
|
||||||
router = APIRouter()
|
router = APIRouter()
|
||||||
@ -17,12 +17,15 @@ def create_dress(dress: DressCreate, db: Session = Depends(get_db)):
|
|||||||
db.add(db_dress)
|
db.add(db_dress)
|
||||||
db.commit()
|
db.commit()
|
||||||
db.refresh(db_dress)
|
db.refresh(db_dress)
|
||||||
return APIResponse.created(data=db_dress, message="服装创建成功")
|
return db_dress
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
db.rollback()
|
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(
|
def get_all_dresses(
|
||||||
skip: int = 0,
|
skip: int = 0,
|
||||||
limit: int = 100,
|
limit: int = 100,
|
||||||
@ -32,28 +35,32 @@ def get_all_dresses(
|
|||||||
try:
|
try:
|
||||||
dresses = db.query(Dress).offset(skip).limit(limit).all()
|
dresses = db.query(Dress).offset(skip).limit(limit).all()
|
||||||
total = db.query(Dress).count()
|
total = db.query(Dress).count()
|
||||||
return APIResponse.ok(
|
|
||||||
data={
|
# 构建分页数据
|
||||||
"items": dresses,
|
result = {
|
||||||
"total": total,
|
"items": dresses,
|
||||||
"page": skip // limit + 1 if limit > 0 else 1,
|
"total": total,
|
||||||
"size": limit
|
"page": skip // limit + 1 if limit > 0 else 1,
|
||||||
},
|
"size": limit
|
||||||
message="服装列表获取成功"
|
}
|
||||||
)
|
|
||||||
|
return result
|
||||||
except Exception as e:
|
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)
|
@router.get("/{dress_id}", response_model=DressResponse)
|
||||||
def get_dress(dress_id: int, db: Session = Depends(get_db)):
|
def get_dress(dress_id: int, db: Session = Depends(get_db)):
|
||||||
"""获取单个服装记录"""
|
"""获取单个服装记录"""
|
||||||
try:
|
dress = db.query(Dress).filter(Dress.id == dress_id).first()
|
||||||
dress = db.query(Dress).filter(Dress.id == dress_id).first()
|
if dress is None:
|
||||||
if dress is None:
|
raise HTTPException(
|
||||||
return APIResponse.not_found(message=f"未找到ID为{dress_id}的服装")
|
status_code=status.HTTP_404_NOT_FOUND,
|
||||||
return APIResponse.ok(data=dress, message="服装获取成功")
|
detail=f"未找到ID为{dress_id}的服装"
|
||||||
except Exception as e:
|
)
|
||||||
return APIResponse.error(message=f"获取服装失败: {str(e)}", code=500)
|
return dress
|
||||||
|
|
||||||
@router.put("/{dress_id}", response_model=DressResponse)
|
@router.put("/{dress_id}", response_model=DressResponse)
|
||||||
def update_dress(dress_id: int, dress: DressUpdate, db: Session = Depends(get_db)):
|
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:
|
try:
|
||||||
db_dress = db.query(Dress).filter(Dress.id == dress_id).first()
|
db_dress = db.query(Dress).filter(Dress.id == dress_id).first()
|
||||||
if db_dress is None:
|
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():
|
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.commit()
|
||||||
db.refresh(db_dress)
|
db.refresh(db_dress)
|
||||||
return APIResponse.ok(data=db_dress, message="服装更新成功")
|
return db_dress
|
||||||
|
except HTTPException:
|
||||||
|
db.rollback()
|
||||||
|
raise
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
db.rollback()
|
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)):
|
def delete_dress(dress_id: int, db: Session = Depends(get_db)):
|
||||||
"""删除服装记录"""
|
"""删除服装记录"""
|
||||||
try:
|
try:
|
||||||
db_dress = db.query(Dress).filter(Dress.id == dress_id).first()
|
db_dress = db.query(Dress).filter(Dress.id == dress_id).first()
|
||||||
if db_dress is None:
|
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.delete(db_dress)
|
||||||
db.commit()
|
db.commit()
|
||||||
return APIResponse.ok(message="服装删除成功")
|
return {"message": "服装删除成功"}
|
||||||
|
except HTTPException:
|
||||||
|
db.rollback()
|
||||||
|
raise
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
db.rollback()
|
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_KEY = os.getenv("DASHSCOPE_API_KEY")
|
||||||
DASHSCOPE_API_URL = "https://dashscope.aliyuncs.com/api/v1/services/aigc/image2image/image-synthesis"
|
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(
|
async def create_tryon(
|
||||||
tryon_data: TryOnCreate,
|
tryon_data: TryOnCreate,
|
||||||
background_tasks: BackgroundTasks,
|
background_tasks: BackgroundTasks,
|
||||||
@ -63,7 +63,7 @@ async def create_tryon(
|
|||||||
person_image_url=tryon_data.person_image_url
|
person_image_url=tryon_data.person_image_url
|
||||||
)
|
)
|
||||||
|
|
||||||
return APIResponse.ok(data=db_tryon, message="试穿记录创建成功")
|
return db_tryon
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"创建试穿记录失败: {str(e)}")
|
logger.error(f"创建试穿记录失败: {str(e)}")
|
||||||
db.rollback()
|
db.rollback()
|
||||||
@ -120,7 +120,8 @@ async def get_tryons(
|
|||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
tryons = db.query(TryOn).order_by(TryOn.id.desc()).offset(skip).limit(limit).all()
|
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:
|
except Exception as e:
|
||||||
logger.error(f"获取试穿记录列表失败: {str(e)}")
|
logger.error(f"获取试穿记录列表失败: {str(e)}")
|
||||||
raise HTTPException(status_code=500, detail=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()
|
tryon = db.query(TryOn).filter(TryOn.id == tryon_id).first()
|
||||||
if not tryon:
|
if not tryon:
|
||||||
raise HTTPException(status_code=404, detail=f"未找到ID为{tryon_id}的试穿记录")
|
raise HTTPException(status_code=404, detail=f"未找到ID为{tryon_id}的试穿记录")
|
||||||
return APIResponse.ok(data=tryon, message="试穿记录获取成功")
|
return tryon
|
||||||
except HTTPException:
|
except HTTPException:
|
||||||
raise
|
raise
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
@ -172,7 +173,7 @@ async def update_tryon(
|
|||||||
|
|
||||||
db.commit()
|
db.commit()
|
||||||
db.refresh(db_tryon)
|
db.refresh(db_tryon)
|
||||||
return APIResponse.ok(data=db_tryon, message="试穿记录更新成功")
|
return db_tryon
|
||||||
except HTTPException:
|
except HTTPException:
|
||||||
raise
|
raise
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
@ -217,7 +218,7 @@ async def check_tryon_status(
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"调用DashScope API检查任务状态失败: {str(e)}")
|
logger.error(f"调用DashScope API检查任务状态失败: {str(e)}")
|
||||||
|
|
||||||
return APIResponse.ok(data=db_tryon, message="试穿任务状态检查成功")
|
return db_tryon
|
||||||
except HTTPException:
|
except HTTPException:
|
||||||
raise
|
raise
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
from pydantic import BaseModel, Field, HttpUrl
|
from pydantic import BaseModel, Field, HttpUrl
|
||||||
from typing import Optional, List
|
from typing import Optional, List, Any, Dict, Union
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
from app.models.dress import GarmentType
|
from app.models.dress import GarmentType
|
||||||
@ -39,8 +39,9 @@ class DressResponse(DressInDB):
|
|||||||
"""服装API响应模型"""
|
"""服装API响应模型"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
class DressListResponse(BaseModel):
|
class PaginatedResponse(BaseModel):
|
||||||
items: List[DressResponse]
|
"""通用分页响应"""
|
||||||
|
items: List[Any]
|
||||||
total: int
|
total: int
|
||||||
page: int
|
page: int
|
||||||
size: int
|
size: int
|
||||||
@ -50,7 +51,6 @@ class DressListResponse(BaseModel):
|
|||||||
|
|
||||||
class StandardResponse(BaseModel):
|
class StandardResponse(BaseModel):
|
||||||
"""标准API响应格式"""
|
"""标准API响应格式"""
|
||||||
success: bool = True
|
|
||||||
code: int = 200
|
code: int = 200
|
||||||
message: str = "操作成功"
|
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 import status
|
||||||
from fastapi.responses import JSONResponse
|
from fastapi.responses import JSONResponse
|
||||||
|
import os
|
||||||
|
|
||||||
|
from app.models.api_response import APIResponseModel
|
||||||
|
|
||||||
class APIResponse:
|
class APIResponse:
|
||||||
"""API标准响应格式工具类"""
|
"""API标准响应格式工具类"""
|
||||||
|
|
||||||
@staticmethod
|
# 全局开关,控制是否使用标准响应格式
|
||||||
def success(
|
USE_STANDARD_RESPONSE = os.environ.get("USE_STANDARD_RESPONSE", "1") == "1"
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def error(
|
def ok(data: Any = None, message: str = "操作成功") -> Dict[str, Any]:
|
||||||
message: str = "操作失败",
|
"""成功响应"""
|
||||||
code: int = 400,
|
if not APIResponse.USE_STANDARD_RESPONSE:
|
||||||
data: Any = None
|
return data
|
||||||
) -> Dict[str, Any]:
|
|
||||||
"""
|
|
||||||
错误响应
|
|
||||||
|
|
||||||
Args:
|
return APIResponseModel(
|
||||||
message: 错误消息
|
code=200,
|
||||||
code: 错误状态码
|
message=message,
|
||||||
data: 附加错误数据
|
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,
|
如果USE_STANDARD_RESPONSE=1,返回标准格式:
|
||||||
"message": message,
|
{
|
||||||
"data": data
|
"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
|
@staticmethod
|
||||||
def json_response(
|
def json_response(
|
||||||
data: Any = None,
|
data: Any = None,
|
||||||
@ -76,12 +75,18 @@ class APIResponse:
|
|||||||
Returns:
|
Returns:
|
||||||
JSONResponse对象
|
JSONResponse对象
|
||||||
"""
|
"""
|
||||||
content = {
|
if not APIResponse.USE_STANDARD_RESPONSE and success:
|
||||||
"success": success,
|
return JSONResponse(
|
||||||
"code": code,
|
content=data,
|
||||||
"message": message,
|
status_code=status_code,
|
||||||
"data": data
|
headers=headers
|
||||||
}
|
)
|
||||||
|
|
||||||
|
content = APIResponseModel(
|
||||||
|
code=code,
|
||||||
|
message=message,
|
||||||
|
data=data
|
||||||
|
).dict()
|
||||||
|
|
||||||
return JSONResponse(
|
return JSONResponse(
|
||||||
content=content,
|
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
|
@classmethod
|
||||||
def created(cls, data: Any = None, message: str = "创建成功") -> Dict[str, Any]:
|
def created(cls, data: Any = None, message: str = "创建成功") -> Dict[str, Any]:
|
||||||
"""201 创建成功"""
|
"""201 创建成功"""
|
||||||
return cls.success(data, message, 201)
|
if not cls.USE_STANDARD_RESPONSE:
|
||||||
|
return data
|
||||||
|
return cls.ok(data, message)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def accepted(cls, data: Any = None, message: str = "请求已接受") -> Dict[str, Any]:
|
def accepted(cls, data: Any = None, message: str = "请求已接受") -> Dict[str, Any]:
|
||||||
"""202 已接受"""
|
"""202 已接受"""
|
||||||
return cls.success(data, message, 202)
|
if not cls.USE_STANDARD_RESPONSE:
|
||||||
|
return data
|
||||||
|
return cls.ok(data, message)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def no_content(cls) -> Dict[str, Any]:
|
def no_content(cls) -> Dict[str, Any]:
|
||||||
"""204 无内容"""
|
"""204 无内容"""
|
||||||
return cls.success(None, "无内容", 204)
|
if not cls.USE_STANDARD_RESPONSE:
|
||||||
|
return None
|
||||||
|
return cls.ok(None, "无内容")
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def bad_request(cls, message: str = "请求参数错误") -> Dict[str, Any]:
|
def bad_request(cls, message: str = "请求参数错误") -> Dict[str, Any]:
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user