feat: 添加新的模型,删除后端数据库
This commit is contained in:
+29
-112
@@ -1,143 +1,60 @@
|
||||
"""套图生成 API:创建任务 / 查询状态 / 导出 ZIP。"""
|
||||
"""套图任务 API:轮询进度 / 导出 ZIP(进程内内存任务表)。"""
|
||||
from __future__ import annotations
|
||||
|
||||
import io
|
||||
import zipfile
|
||||
from uuid import UUID
|
||||
|
||||
from fastapi import APIRouter, BackgroundTasks, Depends, HTTPException
|
||||
from fastapi import APIRouter, HTTPException
|
||||
from fastapi.responses import StreamingResponse
|
||||
from sqlalchemy import select
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from config import get_settings
|
||||
from db import get_db
|
||||
from models import Product, ProductAsset, Suite, SuiteImage, STATUS_OK
|
||||
from schemas import (
|
||||
PLATFORM_SPECS, SUPPORTED_TYPES, TONGYI_MODELS, RIGHTAPI_MODELS,
|
||||
SuiteCreateRequest, SuiteCreateResponse, SuiteImageOut, SuiteOut, resolve_provider,
|
||||
)
|
||||
from schemas import SuiteImageOut, SuiteOut
|
||||
from services import storage
|
||||
from services.generator import run_suite
|
||||
from services.tasks import IMG_OK, Task, get_task
|
||||
|
||||
router = APIRouter(prefix="/api", tags=["suites"])
|
||||
|
||||
|
||||
async def _suite_out(db: AsyncSession, suite: Suite) -> SuiteOut:
|
||||
images = (await db.scalars(
|
||||
select(SuiteImage).where(SuiteImage.suite_id == suite.id)
|
||||
.order_by(SuiteImage.created_at)
|
||||
)).all()
|
||||
def _task_out(task: Task) -> SuiteOut:
|
||||
return SuiteOut(
|
||||
id=str(suite.id),
|
||||
product_id=str(suite.product_id),
|
||||
status=suite.status,
|
||||
style_set=suite.style_set,
|
||||
platform=suite.platform,
|
||||
lang=suite.lang,
|
||||
ratio=suite.ratio,
|
||||
types=list(suite.types or []),
|
||||
provider=suite.provider,
|
||||
model=suite.model,
|
||||
id=task.id,
|
||||
status=task.status,
|
||||
style_set=task.style_set,
|
||||
platform=task.platform,
|
||||
lang=task.lang,
|
||||
ratio=task.ratio,
|
||||
provider=task.provider,
|
||||
model=task.model,
|
||||
total=task.total,
|
||||
images=[
|
||||
SuiteImageOut(
|
||||
type_id=i.type_id, name=i.name, url=i.stored_url or "",
|
||||
type_id=i.type_id, name=i.name, url=i.url,
|
||||
status=i.status, error=i.error,
|
||||
) for i in images
|
||||
) for i in task.images
|
||||
],
|
||||
error=suite.error,
|
||||
error=task.error,
|
||||
)
|
||||
|
||||
|
||||
@router.post("/products/{product_id}/suites", response_model=SuiteCreateResponse)
|
||||
async def create_suite(
|
||||
product_id: str,
|
||||
req: SuiteCreateRequest,
|
||||
background: BackgroundTasks,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
):
|
||||
product = await db.get(Product, UUID(product_id))
|
||||
if product is None:
|
||||
raise HTTPException(status_code=404, detail="商品不存在")
|
||||
|
||||
# 主图组至少一张图(不要求转存完成:生图可直接用源站 URL 代理解析)
|
||||
ok_assets = (await db.scalars(
|
||||
select(ProductAsset.id).where(
|
||||
ProductAsset.product_id == product.id,
|
||||
ProductAsset.group_key == "main",
|
||||
ProductAsset.type == "img",
|
||||
)
|
||||
)).all()
|
||||
if not ok_assets:
|
||||
raise HTTPException(status_code=400, detail="商品没有主图,无法生成")
|
||||
|
||||
bad = [t for t in req.types if t not in SUPPORTED_TYPES]
|
||||
if bad:
|
||||
raise HTTPException(status_code=400, detail=f"不支持的图类型: {bad}")
|
||||
if req.platform not in PLATFORM_SPECS:
|
||||
raise HTTPException(status_code=400, detail=f"不支持的目标平台: {req.platform}(ozon | wb | cn)")
|
||||
spec = PLATFORM_SPECS[req.platform]
|
||||
|
||||
settings = get_settings()
|
||||
# 模型名优先路由:已知模型直接定位 provider(gpt-image-2 → rightapi)
|
||||
provider_name = resolve_provider(req.model, req.provider, settings.image_provider)
|
||||
if provider_name == "tongyi" and req.model and req.model not in TONGYI_MODELS:
|
||||
raise HTTPException(status_code=400, detail=f"不支持的模型: {req.model}(tongyi 支持: {TONGYI_MODELS})")
|
||||
if provider_name == "rightapi" and req.model and req.model not in RIGHTAPI_MODELS:
|
||||
raise HTTPException(status_code=400, detail=f"不支持的模型: {req.model}(rightapi 支持: {RIGHTAPI_MODELS})")
|
||||
suite = Suite(
|
||||
product_id=product.id,
|
||||
style_set=req.style_set,
|
||||
requirements=req.requirements,
|
||||
platform=req.platform,
|
||||
lang=spec["lang"],
|
||||
ratio=spec["ratio"],
|
||||
types=req.types,
|
||||
provider=provider_name,
|
||||
model=req.model,
|
||||
)
|
||||
db.add(suite)
|
||||
await db.commit()
|
||||
await db.refresh(suite)
|
||||
|
||||
background.add_task(run_suite, str(suite.id))
|
||||
return SuiteCreateResponse(suite_id=str(suite.id))
|
||||
|
||||
|
||||
@router.get("/suites/{suite_id}", response_model=SuiteOut)
|
||||
async def get_suite(suite_id: str, db: AsyncSession = Depends(get_db)):
|
||||
suite = await db.get(Suite, UUID(suite_id))
|
||||
if suite is None:
|
||||
raise HTTPException(status_code=404, detail="任务不存在")
|
||||
return await _suite_out(db, suite)
|
||||
|
||||
|
||||
@router.get("/products/{product_id}/suites")
|
||||
async def list_suites(product_id: str, db: AsyncSession = Depends(get_db)):
|
||||
suites = (await db.scalars(
|
||||
select(Suite).where(Suite.product_id == UUID(product_id))
|
||||
.order_by(Suite.created_at.desc())
|
||||
)).all()
|
||||
return [await _suite_out(db, s) for s in suites]
|
||||
async def get_suite(suite_id: str):
|
||||
task = get_task(suite_id)
|
||||
if task is None:
|
||||
raise HTTPException(status_code=404, detail="任务不存在(服务可能已重启),请重新生成")
|
||||
return _task_out(task)
|
||||
|
||||
|
||||
@router.get("/suites/{suite_id}/zip")
|
||||
async def download_suite_zip(suite_id: str, db: AsyncSession = Depends(get_db)):
|
||||
async def download_suite_zip(suite_id: str):
|
||||
"""把任务内所有成功图打包成 ZIP(中文文件名)。"""
|
||||
suite = await db.get(Suite, UUID(suite_id))
|
||||
if suite is None:
|
||||
raise HTTPException(status_code=404, detail="任务不存在")
|
||||
images = (await db.scalars(
|
||||
select(SuiteImage).where(
|
||||
SuiteImage.suite_id == suite.id, SuiteImage.status == STATUS_OK,
|
||||
).order_by(SuiteImage.created_at)
|
||||
)).all()
|
||||
task = get_task(suite_id)
|
||||
if task is None:
|
||||
raise HTTPException(status_code=404, detail="任务不存在(服务可能已重启)")
|
||||
|
||||
buf = io.BytesIO()
|
||||
with zipfile.ZipFile(buf, "w", zipfile.ZIP_DEFLATED) as zf:
|
||||
seen: set[str] = set()
|
||||
for i, img in enumerate(images):
|
||||
path = storage.local_path(img.stored_url or "")
|
||||
for i, img in enumerate([i for i in task.images if i.status == IMG_OK]):
|
||||
path = storage.local_path(img.url or "")
|
||||
if path is None:
|
||||
continue
|
||||
filename = img.name or img.type_id
|
||||
@@ -149,5 +66,5 @@ async def download_suite_zip(suite_id: str, db: AsyncSession = Depends(get_db)):
|
||||
return StreamingResponse(
|
||||
buf,
|
||||
media_type="application/zip",
|
||||
headers={"Content-Disposition": f'attachment; filename="suite-{suite_id}.zip"'},
|
||||
headers={"Content-Disposition": f"attachment; filename=\"suite-{suite_id}.zip\""},
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user