diff --git a/app/api/v1/clothing.py b/app/api/v1/clothing.py index 7638b52..9a512e8 100644 --- a/app/api/v1/clothing.py +++ b/app/api/v1/clothing.py @@ -137,11 +137,6 @@ async def read_clothes_by_category( db: AsyncSession = Depends(get_db) ): """根据分类获取衣服""" - # 检查分类是否存在 - category = await clothing_service.get_category(db, category_id=category_id) - if category is None: - raise BusinessError("分类不存在", code=404) - clothes = await clothing_service.get_clothes_by_category( db=db, category_id=category_id, @@ -151,46 +146,6 @@ async def read_clothes_by_category( # 手动返回标准响应格式 return StandardResponse(code=200, data=[Clothing.model_validate(clothing) for clothing in clothes]) -@router.get("/{clothing_id}", tags=["clothing"]) -async def read_clothing( - clothing_id: int, - db: AsyncSession = Depends(get_db) -): - """获取单个衣服""" - clothing = await clothing_service.get_clothing(db, clothing_id=clothing_id) - if clothing is None: - raise BusinessError("衣服不存在", code=404) - # 手动返回标准响应格式 - return StandardResponse(code=200, data=Clothing.model_validate(clothing)) - -@router.put("/{clothing_id}", tags=["clothing"]) -async def update_clothing( - clothing_id: int, - clothing: ClothingUpdate, - db: AsyncSession = Depends(get_db), - current_user: UserModel = Depends(get_current_user) -): - """ - 更新衣服 - - 需要JWT令牌认证 - """ - # 检查分类是否存在 - if clothing.clothing_category_id is not None: - category = await clothing_service.get_category(db, category_id=clothing.clothing_category_id) - if category is None: - raise BusinessError("指定的分类不存在", code=404) - - clothing = await clothing_service.update_clothing( - db=db, - clothing_id=clothing_id, - clothing_update=clothing - ) - if clothing is None: - raise BusinessError("衣服不存在", code=404) - # 手动返回标准响应格式 - return StandardResponse(code=200, data=Clothing.model_validate(clothing)) - @router.delete("/{clothing_id}", tags=["clothing"]) async def delete_clothing( clothing_id: int, diff --git a/app/api/v1/tryon.py b/app/api/v1/tryon.py new file mode 100644 index 0000000..76dec01 --- /dev/null +++ b/app/api/v1/tryon.py @@ -0,0 +1,70 @@ +from fastapi import APIRouter, Depends, HTTPException, status, Response +from sqlalchemy.ext.asyncio import AsyncSession +import logging +from fastapi.security import OAuth2PasswordRequestForm +from app.api import deps +from app.core import security +from app.core.config import settings +from app.schemas.tryon import TryonRequest +from app.schemas.user import User +from app.services import person_image as person_image_service +from app.services import clothing as clothing_service +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 + +logger = logging.getLogger(__name__) +logger.setLevel(logging.DEBUG) +router = APIRouter() + +@router.post("/tryon", tags=["tryon"]) +async def tryon( + tryon_request: TryonRequest, + db: AsyncSession = Depends(deps.get_db), + current_user: User = Depends(get_current_user) +): + # 获取当前用户的默认形象 + person_image = await person_image_service.get_default_image(db, current_user.id) + if not person_image: + raise HTTPException(status_code=404, detail="默认形象不存在") + + # 获取试穿请求中的衣物ID + top_clothing_id = tryon_request.top_clothing_id + bottom_clothing_id = tryon_request.bottom_clothing_id + + # 获取衣物详情 + top_clothing_url = tryon_request.top_clothing_url + bottom_clothing_url = tryon_request.bottom_clothing_url + + if top_clothing_id: + top_clothing = await clothing_service.get_clothing(db, top_clothing_id) + top_clothing_url = top_clothing.image_url + + if bottom_clothing_id: + bottom_clothing = await clothing_service.get_clothing(db, bottom_clothing_id) + bottom_clothing_url = bottom_clothing.image_url + + + # 调用试穿服务 + dashscope_service = DashScopeService() + tryon_result = await dashscope_service.generate_tryon(person_image.image_url, + top_clothing_url, + bottom_clothing_url) + + task_id = tryon_result.get("task_id") + if task_id: + tryon_history = TryonHistory( + user_id=current_user.id, + person_image_id=person_image.id, + top_clothing_id=top_clothing_id, + bottom_clothing_id=bottom_clothing_id, + top_clothing_url=top_clothing_url, + bottom_clothing_url=bottom_clothing_url, + task_id=task_id + ) + db.add(tryon_history) + await db.commit() + return StandardResponse(code=200, message="试穿任务已提交", data=tryon_history.id) + else: + return StandardResponse(code=500, message="试穿任务提交失败") diff --git a/app/db/init_db.py b/app/db/init_db.py index 611d9c8..3b271e6 100644 --- a/app/db/init_db.py +++ b/app/db/init_db.py @@ -3,6 +3,7 @@ from app.db.database import Base, engine from app.models.users import User from app.models.person_images import PersonImage from app.models.clothing import ClothingCategory, Clothing +from app.models.tryon import TryonHistory # 创建所有表格 async def init_db(): diff --git a/app/models/tryon.py b/app/models/tryon.py new file mode 100644 index 0000000..1aa11cd --- /dev/null +++ b/app/models/tryon.py @@ -0,0 +1,37 @@ +from sqlalchemy import Column, Integer, String, DateTime, ForeignKey, Enum +from sqlalchemy.sql import func +from sqlalchemy.orm import relationship +from app.db.database import Base +import enum + +class TryonStatus(enum.Enum): + """试穿状态枚举""" + GENERATING = "生成中" + COMPLETED = "已生成" + FAILED = "失败" + +class TryonHistory(Base): + """试穿记录数据模型""" + __tablename__ = "tryon_history" + + id = Column(Integer, primary_key=True, autoincrement=True, index=True) + user_id = Column(Integer, ForeignKey("users.id"), nullable=False, index=True, comment="用户ID") + person_image_id = Column(Integer, ForeignKey("person_images.id"), nullable=False, index=True, comment="人物形象ID") + top_clothing_id = Column(Integer, ForeignKey("clothing.id"), nullable=True, comment="上衣ID") + bottom_clothing_id = Column(Integer, ForeignKey("clothing.id"), nullable=True, comment="下装ID") + top_clothing_url = Column(String(500), nullable=True, comment="上衣图片URL") + bottom_clothing_url = Column(String(500), nullable=True, comment="下装图片URL") + task_id = Column(String(100), nullable=True, index=True, comment="任务ID") + completion_url = Column(String(500), nullable=True, comment="生成结果URL") + status = Column(Enum(TryonStatus), default=TryonStatus.GENERATING, comment="状态") + create_time = Column(DateTime, default=func.now(), comment="创建时间") + update_time = Column(DateTime, default=func.now(), onupdate=func.now(), comment="更新时间") + + # 关系 + user = relationship("User", backref="tryon_histories") + person_image = relationship("PersonImage", backref="tryon_histories") + top_clothing = relationship("Clothing", foreign_keys=[top_clothing_id], backref="top_tryon_histories") + bottom_clothing = relationship("Clothing", foreign_keys=[bottom_clothing_id], backref="bottom_tryon_histories") + + def __repr__(self): + return f"" \ No newline at end of file diff --git a/app/models/users.py b/app/models/users.py index d79bbdf..2866844 100644 --- a/app/models/users.py +++ b/app/models/users.py @@ -18,6 +18,7 @@ class User(Base): # 关系 person_images = relationship("PersonImage", back_populates="user", cascade="all, delete-orphan") clothings = relationship("Clothing", back_populates="user", cascade="all, delete-orphan") + # tryon_histories已通过backref在TryonHistory模型中定义 def __repr__(self): return f"" diff --git a/app/schemas/tryon.py b/app/schemas/tryon.py new file mode 100644 index 0000000..bb04087 --- /dev/null +++ b/app/schemas/tryon.py @@ -0,0 +1,9 @@ +from pydantic import BaseModel +from typing import Optional + + +class TryonRequest(BaseModel): + top_clothing_id: Optional[int] = None + bottom_clothing_id: Optional[int] = None + top_clothing_url: Optional[str] = None + bottom_clothing_url: Optional[str] = None