update
This commit is contained in:
parent
b1bd8c2e82
commit
64ed01f5dc
@ -5,6 +5,7 @@ from app.api.v1.auth import router as auth_router
|
||||
from app.api.v1.person_images import router as person_images_router
|
||||
from app.api.v1.clothing import router as clothing_router
|
||||
from app.api.v1.tryon import router as tryon_router
|
||||
from app.api.v1.upload import router as upload_router
|
||||
|
||||
api_router = APIRouter()
|
||||
api_router.include_router(endpoints_router, prefix="")
|
||||
@ -13,3 +14,4 @@ api_router.include_router(auth_router, prefix="/auth")
|
||||
api_router.include_router(person_images_router, prefix="/person-images")
|
||||
api_router.include_router(clothing_router, prefix="/clothing")
|
||||
api_router.include_router(tryon_router, prefix="/tryon")
|
||||
api_router.include_router(upload_router, prefix="/upload")
|
||||
@ -13,6 +13,8 @@ from app.api.deps import get_current_user
|
||||
from app.services.dashscope_service import DashScopeService
|
||||
from app.schemas.response import StandardResponse
|
||||
from app.models.tryon import TryonHistory
|
||||
from app.schemas.tryon import TryonHistoryModel
|
||||
from sqlalchemy import select
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
logger.setLevel(logging.DEBUG)
|
||||
@ -72,3 +74,16 @@ async def tryon(
|
||||
return StandardResponse(code=200, message="试穿任务已提交", data=tryon_history.id)
|
||||
else:
|
||||
return StandardResponse(code=500, message="试穿任务提交失败")
|
||||
|
||||
@router.get("/tryon/histories", tags=["tryon"])
|
||||
async def get_tryon_histories(
|
||||
db: AsyncSession = Depends(deps.get_db),
|
||||
current_user: User = Depends(get_current_user)
|
||||
):
|
||||
"""
|
||||
获取试穿历史
|
||||
"""
|
||||
histories = await db.execute(select(TryonHistory).where(TryonHistory.user_id == current_user.id))
|
||||
tryon_histories = histories.scalars().all()
|
||||
|
||||
return StandardResponse(code=200, message="试穿历史获取成功", data=[TryonHistoryModel.model_validate(history) for history in tryon_histories])
|
||||
|
||||
88
app/api/v1/upload.py
Normal file
88
app/api/v1/upload.py
Normal file
@ -0,0 +1,88 @@
|
||||
from fastapi import APIRouter, Depends, UploadFile, File, HTTPException, status, Request
|
||||
from fastapi.responses import JSONResponse
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from typing import List, Optional
|
||||
from app.api import deps
|
||||
from app.services import cos as cos_service
|
||||
from app.schemas.response import StandardResponse
|
||||
import logging
|
||||
|
||||
router = APIRouter()
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@router.post("", response_model=StandardResponse, tags=["upload"])
|
||||
async def upload_file(
|
||||
file: UploadFile = File(...),
|
||||
directory: str = "uploads"
|
||||
):
|
||||
"""
|
||||
上传文件到腾讯云COS
|
||||
|
||||
- 支持的文件类型: 图片(jpg, jpeg, png, gif, webp), 文档(pdf, doc, docx)
|
||||
- 返回文件的访问URL
|
||||
"""
|
||||
try:
|
||||
content_type = file.content_type or ""
|
||||
|
||||
# 检查文件类型
|
||||
allowed_types = [
|
||||
"image/jpeg", "image/jpg", "image/png", "image/gif", "image/webp",
|
||||
"application/pdf", "application/msword",
|
||||
"application/vnd.openxmlformats-officedocument.wordprocessingml.document"
|
||||
]
|
||||
|
||||
if content_type not in allowed_types:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
detail="不支持的文件类型"
|
||||
)
|
||||
|
||||
# 获取文件内容
|
||||
file_content = await file.read()
|
||||
|
||||
# 获取文件扩展名
|
||||
filename = file.filename or ""
|
||||
file_extension = "." + filename.split(".")[-1] if "." in filename else ""
|
||||
|
||||
# 上传到腾讯云COS
|
||||
url = await cos_service.upload_file(
|
||||
file_content=file_content,
|
||||
file_extension=file_extension,
|
||||
directory=directory
|
||||
)
|
||||
|
||||
return StandardResponse(code=200, data={"url": url})
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"文件上传失败: {str(e)}")
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
detail=f"文件上传失败: {str(e)}"
|
||||
)
|
||||
|
||||
@router.post("/from-url", response_model=StandardResponse, tags=["upload"])
|
||||
async def upload_from_url(
|
||||
url: str,
|
||||
directory: str = "uploads"
|
||||
):
|
||||
"""
|
||||
从URL上传文件到腾讯云COS
|
||||
|
||||
- 支持的文件类型: 图片(jpg, jpeg, png, gif, webp)
|
||||
- 返回文件的访问URL
|
||||
"""
|
||||
try:
|
||||
# 从URL上传文件
|
||||
cos_url = await cos_service.upload_file_from_url(
|
||||
url=url,
|
||||
directory=directory
|
||||
)
|
||||
|
||||
return StandardResponse(code=200, data={"url": cos_url})
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"从URL上传文件失败: {str(e)}")
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
detail=f"从URL上传文件失败: {str(e)}"
|
||||
)
|
||||
@ -35,6 +35,12 @@ class Settings(BaseSettings):
|
||||
WECHAT_APP_ID: str = os.getenv("WECHAT_APP_ID", "")
|
||||
WECHAT_APP_SECRET: str = os.getenv("WECHAT_APP_SECRET", "")
|
||||
|
||||
# 腾讯云COS配置
|
||||
COS_SECRET_ID: str = os.getenv("COS_SECRET_ID", "")
|
||||
COS_SECRET_KEY: str = os.getenv("COS_SECRET_KEY", "")
|
||||
COS_REGION: str = os.getenv("COS_REGION", "ap-guangzhou")
|
||||
COS_BUCKET: str = os.getenv("COS_BUCKET", "")
|
||||
|
||||
@property
|
||||
def cors_origins(self):
|
||||
"""获取CORS来源列表"""
|
||||
|
||||
@ -1,5 +1,7 @@
|
||||
from pydantic import BaseModel
|
||||
from typing import Optional
|
||||
from app.models.tryon import TryonStatus
|
||||
from datetime import datetime
|
||||
|
||||
|
||||
class TryonRequest(BaseModel):
|
||||
@ -7,3 +9,18 @@ class TryonRequest(BaseModel):
|
||||
bottom_clothing_id: Optional[int] = None
|
||||
top_clothing_url: Optional[str] = None
|
||||
bottom_clothing_url: Optional[str] = None
|
||||
|
||||
|
||||
class TryonHistoryModel(BaseModel):
|
||||
id: int
|
||||
person_image_id: int
|
||||
top_clothing_id: int
|
||||
bottom_clothing_id: int
|
||||
top_clothing_url: str
|
||||
bottom_clothing_url: str
|
||||
status: TryonStatus
|
||||
create_time: datetime
|
||||
update_time: datetime
|
||||
|
||||
class Config:
|
||||
from_attributes = True
|
||||
|
||||
103
app/services/cos.py
Normal file
103
app/services/cos.py
Normal file
@ -0,0 +1,103 @@
|
||||
import logging
|
||||
import os
|
||||
import uuid
|
||||
from datetime import datetime
|
||||
from qcloud_cos import CosConfig, CosS3Client
|
||||
from app.core.config import settings
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# 腾讯云COS配置
|
||||
config = CosConfig(
|
||||
Region=settings.COS_REGION,
|
||||
SecretId=settings.COS_SECRET_ID,
|
||||
SecretKey=settings.COS_SECRET_KEY
|
||||
)
|
||||
|
||||
# 创建客户端
|
||||
cos_client = CosS3Client(config)
|
||||
|
||||
def generate_file_path(directory: str, file_extension: str) -> str:
|
||||
"""生成文件路径"""
|
||||
today = datetime.now().strftime("%Y%m%d")
|
||||
filename = f"{uuid.uuid4().hex}{file_extension}"
|
||||
return f"{directory}/{today}/{filename}"
|
||||
|
||||
async def upload_file(file_content: bytes, file_extension: str, directory: str = "uploads") -> str:
|
||||
"""上传文件到腾讯云COS"""
|
||||
try:
|
||||
# 生成唯一文件路径
|
||||
file_path = generate_file_path(directory, file_extension)
|
||||
|
||||
# 上传到腾讯云COS
|
||||
cos_client.put_object(
|
||||
Bucket=settings.COS_BUCKET,
|
||||
Body=file_content,
|
||||
Key=file_path
|
||||
)
|
||||
|
||||
# 返回可访问的URL
|
||||
url = f"https://{settings.COS_BUCKET}.cos.{settings.COS_REGION}.myqcloud.com/{file_path}"
|
||||
logger.info(f"文件上传成功: {url}")
|
||||
return url
|
||||
except Exception as e:
|
||||
logger.error(f"文件上传失败: {str(e)}")
|
||||
raise
|
||||
|
||||
async def upload_file_from_url(url: str, directory: str = "uploads") -> str:
|
||||
"""从URL下载文件并上传到腾讯云COS"""
|
||||
try:
|
||||
import requests
|
||||
|
||||
# 下载文件
|
||||
response = requests.get(url, timeout=10)
|
||||
response.raise_for_status()
|
||||
|
||||
# 获取文件扩展名
|
||||
file_extension = os.path.splitext(url)[1]
|
||||
if not file_extension:
|
||||
# 如果URL没有扩展名,根据内容类型判断
|
||||
content_type = response.headers.get("Content-Type", "")
|
||||
if "jpeg" in content_type or "jpg" in content_type:
|
||||
file_extension = ".jpg"
|
||||
elif "png" in content_type:
|
||||
file_extension = ".png"
|
||||
elif "gif" in content_type:
|
||||
file_extension = ".gif"
|
||||
else:
|
||||
file_extension = ".bin" # 默认二进制文件
|
||||
|
||||
# 上传到腾讯云COS
|
||||
return await upload_file(response.content, file_extension, directory)
|
||||
except Exception as e:
|
||||
logger.error(f"从URL上传文件失败: {str(e)}")
|
||||
raise
|
||||
|
||||
async def get_presigned_url(key: str, expires: int = 3600) -> str:
|
||||
"""获取预签名URL"""
|
||||
try:
|
||||
# 生成预签名URL
|
||||
url = cos_client.get_presigned_url(
|
||||
Method='GET',
|
||||
Bucket=settings.COS_BUCKET,
|
||||
Key=key,
|
||||
Expired=expires
|
||||
)
|
||||
return url
|
||||
except Exception as e:
|
||||
logger.error(f"获取预签名URL失败: {str(e)}")
|
||||
raise
|
||||
|
||||
async def delete_file(key: str) -> bool:
|
||||
"""删除文件"""
|
||||
try:
|
||||
# 删除文件
|
||||
cos_client.delete_object(
|
||||
Bucket=settings.COS_BUCKET,
|
||||
Key=key
|
||||
)
|
||||
logger.info(f"文件删除成功: {key}")
|
||||
return True
|
||||
except Exception as e:
|
||||
logger.error(f"文件删除失败: {str(e)}")
|
||||
return False
|
||||
@ -11,3 +11,7 @@ passlib==1.7.4
|
||||
httpx==0.24.1
|
||||
dashscope==1.10.0
|
||||
itsdangerous==2.2.0
|
||||
# 腾讯云COS SDK
|
||||
cos-python-sdk-v5==1.9.25
|
||||
requests>=2.28.1
|
||||
python-multipart>=0.0.10
|
||||
Loading…
Reference in New Issue
Block a user