Files
YuShiSheJiShi/backend/app/routers/designs.py
032c43525a feat(ai): 支持双模型多视角AI设计生图与后台管理系统
- 实现AI多视角设计图生成功能,支持6个可选设计参数配置
- 集成SiliconFlow FLUX.1与火山引擎Seedream 4.5双模型切换
- 构建专业中文转英文prompt系统,提升AI生成质量
- 前端设计预览支持多视角切换与视角指示器展示
- 增加多视角设计图片DesignImage模型关联及存储
- 后端设计服务异步调用AI接口,失败时降级生成mock图
- 新增管理员后台管理路由及完整的权限校验机制
- 实现后台模块:仪表盘、系统配置、用户/品类/设计管理
- 配置数据库系统配置表,支持动态AI配置及热更新
- 增加用户管理员标识字段,管理后台登录鉴权支持
- 更新API接口支持多视角设计参数及后台管理接口
- 优化设计删除逻辑,删除多视角相关图片文件
- 前端新增管理后台页面与路由,布局样式独立分离
- 更新环境变量增加AI模型相关Key与参数配置说明
- 引入httpx异步HTTP客户端用于AI接口调用及图片下载
- README文档完善AI多视角生图与后台管理详细功能与流程说明
2026-03-27 15:29:50 +08:00

218 lines
6.0 KiB
Python

"""
设计相关路由
提供设计生成、查询、删除、下载接口
"""
import os
from fastapi import APIRouter, Depends, HTTPException, status, Query
from fastapi.responses import FileResponse
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
"""
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 为文件路径
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"
)