""" 设计相关路由 提供设计生成、查询、删除、下载接口 """ import os from fastapi import APIRouter, Depends, HTTPException, status, Query from fastapi.responses import FileResponse, RedirectResponse from sqlalchemy.orm import Session from ..database import get_db from ..models import User, Design from ..schemas import DesignCreate, DesignResponse, DesignListResponse, DesignImageResponse from ..utils.deps import get_current_user from ..services import design_service router = APIRouter(prefix="/api/designs", tags=["设计"]) def design_to_response(design: Design) -> DesignResponse: """将 Design 模型转换为响应格式""" # 构建多视角图片列表 images = [] if hasattr(design, 'images') and design.images: images = [ DesignImageResponse( id=img.id, view_name=img.view_name, image_url=img.image_url, model_used=img.model_used, prompt_used=img.prompt_used, sort_order=img.sort_order, ) for img in design.images ] return DesignResponse( id=design.id, user_id=design.user_id, category={ "id": design.category.id, "name": design.category.name, "icon": design.category.icon, "sort_order": design.category.sort_order, "flow_type": design.category.flow_type }, sub_type={ "id": design.sub_type.id, "category_id": design.sub_type.category_id, "name": design.sub_type.name, "description": design.sub_type.description, "preview_image": design.sub_type.preview_image, "sort_order": design.sub_type.sort_order } if design.sub_type else None, color={ "id": design.color.id, "category_id": design.color.category_id, "name": design.color.name, "hex_code": design.color.hex_code, "sort_order": design.color.sort_order } if design.color else None, prompt=design.prompt, carving_technique=design.carving_technique, design_style=design.design_style, motif=design.motif, size_spec=design.size_spec, surface_finish=design.surface_finish, usage_scene=design.usage_scene, image_url=design.image_url, images=images, status=design.status, created_at=design.created_at, updated_at=design.updated_at ) @router.post("/generate", response_model=DesignResponse) async def generate_design( design_data: DesignCreate, db: Session = Depends(get_db), current_user: User = Depends(get_current_user) ): """ 提交设计生成请求(异步,支持 AI 多视角生图) 需要认证 """ try: design = await design_service.create_design_async( db=db, user_id=current_user.id, design_data=design_data ) return design_to_response(design) except ValueError as e: raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, detail=str(e) ) @router.get("", response_model=DesignListResponse) def get_designs( page: int = Query(1, ge=1, description="页码"), page_size: int = Query(20, ge=1, le=100, description="每页数量"), db: Session = Depends(get_db), current_user: User = Depends(get_current_user) ): """ 获取当前用户的设计历史列表(分页) 需要认证 """ designs, total = design_service.get_user_designs( db=db, user_id=current_user.id, page=page, page_size=page_size ) return DesignListResponse( items=[design_to_response(d) for d in designs], total=total, page=page, page_size=page_size ) @router.get("/{design_id}", response_model=DesignResponse) def get_design( design_id: int, db: Session = Depends(get_db), current_user: User = Depends(get_current_user) ): """ 获取设计详情 只能查看自己的设计,非本人设计返回 404 """ design = design_service.get_design_by_id( db=db, design_id=design_id, user_id=current_user.id ) if not design: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail="设计不存在" ) return design_to_response(design) @router.delete("/{design_id}") def delete_design( design_id: int, db: Session = Depends(get_db), current_user: User = Depends(get_current_user) ): """ 删除设计 只能删除自己的设计,非本人设计返回 404 """ success = design_service.delete_design( db=db, design_id=design_id, user_id=current_user.id ) if not success: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail="设计不存在" ) return {"message": "删除成功"} @router.get("/{design_id}/download") def download_design( design_id: int, db: Session = Depends(get_db), current_user: User = Depends(get_current_user) ): """ 下载设计图 只能下载自己的设计,非本人设计返回 404 支持远程 URL(重定向)和本地文件(兼容历史数据) """ design = design_service.get_design_by_id( db=db, design_id=design_id, user_id=current_user.id ) if not design: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail="设计不存在" ) if not design.image_url: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail="设计图片不存在" ) # 远程 URL 直接重定向 if design.image_url.startswith("http"): return RedirectResponse(url=design.image_url) # 本地文件(兼容历史数据) file_path = design.image_url.lstrip("/") if not os.path.exists(file_path): raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail="设计图片文件不存在" ) return FileResponse( path=file_path, filename=f"design_{design_id}.png", media_type="image/png" )