From bb847479176ded579655fc3b86c2ce98d6d5caad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=B7=9D?= Date: Fri, 27 Mar 2026 17:39:01 +0800 Subject: [PATCH] =?UTF-8?q?feat(ai):=20=E5=8D=87=E7=BA=A7AI=E7=94=9F?= =?UTF-8?q?=E5=9B=BE=E6=A8=A1=E5=9E=8B=E5=8F=8A=E5=A4=9A=E8=A7=86=E8=A7=92?= =?UTF-8?q?=E4=B8=80=E8=87=B4=E6=80=A7=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 将默认AI生图模型升级为flux-dev及seedream-5.0版本 - SiliconFlow模型由FLUX.1-dev切换为Kolors,优化调用参数和返回值 - 火山引擎Seedream升级至5.0 lite版本,支持多视角参考图传入 - 设计图片字段由字符串改为Text扩展URL长度限制 - 设计图下载支持远程URL重定向和本地文件兼容 - 生成AI图片时多视角保持风格一致,SiliconFlow复用seed,Seedream传参考图 - 后台配置界面更改模型名称及价格显示,新增API Key状态检测 - 前端照片下载从链接改为按钮,远程文件新窗口打开 - 设计相关接口支持较长请求超时,下载走API路径无/api前缀 - 前端页面兼容驼峰与下划线格式URL参数识别 - 用户中心设计图下载支持本地文件Token授权下载 - 初始化数据库新增完整表结构与约束,适配新版设计业务逻辑 --- .DS_Store | Bin 0 -> 6148 bytes .qoder/.DS_Store | Bin 0 -> 6148 bytes backend/.DS_Store | Bin 0 -> 6148 bytes backend/app/config.py | 2 +- backend/app/main.py | 2 +- backend/app/models/design.py | 2 +- backend/app/models/design_image.py | 2 +- backend/app/routers/admin.py | 2 +- backend/app/routers/designs.py | 10 +- backend/app/services/ai_generator.py | 70 ++- backend/app/services/design_service.py | 50 +- backend/uploads/designs/1.png | Bin 34580 -> 0 bytes frontend/src/api/admin.ts | 2 +- frontend/src/api/design.ts | 8 +- frontend/src/components/DesignPreview.vue | 55 +- frontend/src/views/GeneratePage.vue | 43 +- frontend/src/views/UserCenter.vue | 33 +- frontend/src/views/admin/ConfigManage.vue | 38 +- frontend/src/views/admin/PromptManage.vue | 8 +- init_data.sql | 732 +++++++++++++--------- init_data.sql.zip | Bin 0 -> 9483 bytes 21 files changed, 645 insertions(+), 414 deletions(-) create mode 100644 .DS_Store create mode 100644 .qoder/.DS_Store create mode 100644 backend/.DS_Store delete mode 100644 backend/uploads/designs/1.png create mode 100644 init_data.sql.zip diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..a6c79ee32094855de7a40d6406f771375199cce6 GIT binary patch literal 6148 zcmeHK!A{&T5Pe>vHWVSjrD|^xS0n_84+yJJsRtwk9{{@>SPLETNAhoO$nW)W2|bgEGsyMcL(8e7<^cwRZJ= z-Ph;O>PLsp*)Y3ISG-`{4=9miA17XH(bn{C{Kd=G=eH)lH^-oS%3+d?mxqb-5?yrA z!!h$1B7a|$J-bpl7Tav^?zL$1nzfg=`g%i~nuVMB_K#oR+9vac)l=@1t-S1~Qb{Pr z0Zz!`5NBia|3Vu#|Au{j+AF$cS&W^`U0!^9{c~JEcY&MxcaIBf1acqGb9U|`XBPBU zndSBz$`-{gw@ZiE;pREz_9^_wr50+-ouBN<89q@vs?BiD8Md4UK8zAYiAxyN zw@6D0p-VAu{{>;q7KvvJhCVP57zhmfW~!?@LGzdU}7<~ zSQ(^-qKqrixGMXLp^Q7n6Kj`RtPC1=C>@#e*vQKMp(q`lgUOS=LKf()-ubhr`F`(#@k$Vc1@m;MtihIy^?CwE$Fsa zSNwyBZvB_l-P@7Y|Fln4Z=Pd&RPTATnvc$aGvEyD8Uxm&)5)$K+|TO_I0HX2Am4`o z70e7<#rWyK5U&8hG0aggm%D`I1jEd*RfGq^nhMlZwkrl}I_$yXGQ(C;(~0f!!B&~= zhQhHr_75?fI8*e|8E^*r42*O+mHvOS{r=xi@|82-4E!qwc$_ctIgVt#b#OT8wE=nt p6_L1B@goJ3*oqM=t@r>M1@<5lU}o4V!UFLhfk=Z7&cLrS@D8LsNr?ae literal 0 HcmV?d00001 diff --git a/backend/app/config.py b/backend/app/config.py index ae7f4f2..32f8486 100644 --- a/backend/app/config.py +++ b/backend/app/config.py @@ -18,7 +18,7 @@ class Settings(BaseSettings): SILICONFLOW_BASE_URL: str = "https://api.siliconflow.cn/v1" VOLCENGINE_API_KEY: str = "" VOLCENGINE_BASE_URL: str = "https://ark.cn-beijing.volces.com/api/v3" - AI_IMAGE_MODEL: str = "flux-dev" # flux-dev 或 seedream-4.5 + AI_IMAGE_MODEL: str = "flux-dev" # flux-dev 或 seedream-5.0 AI_IMAGE_SIZE: int = 1024 class Config: diff --git a/backend/app/main.py b/backend/app/main.py index b60e260..d55a0bd 100644 --- a/backend/app/main.py +++ b/backend/app/main.py @@ -35,7 +35,7 @@ app = FastAPI( # 配置 CORS app.add_middleware( CORSMiddleware, - allow_origins=["http://localhost:3000"], # 生产环境应限制具体域名 + allow_origins=["https://c02.wsg.plus", "http://c02.wsg.plus", "http://localhost:3000"], allow_credentials=True, allow_methods=["*"], allow_headers=["*"], diff --git a/backend/app/models/design.py b/backend/app/models/design.py index 892dc0d..5511ebe 100644 --- a/backend/app/models/design.py +++ b/backend/app/models/design.py @@ -24,7 +24,7 @@ class Design(Base): size_spec = Column(String(100), nullable=True, comment="尺寸规格") surface_finish = Column(String(50), nullable=True, comment="表面处理") usage_scene = Column(String(50), nullable=True, comment="用途场景") - image_url = Column(String(255), nullable=True, comment="设计图URL") + image_url = Column(Text, nullable=True, comment="设计图URL") status = Column(String(20), default="generating", comment="状态") created_at = Column(DateTime, server_default=func.now(), comment="创建时间") updated_at = Column(DateTime, server_default=func.now(), onupdate=func.now(), comment="更新时间") diff --git a/backend/app/models/design_image.py b/backend/app/models/design_image.py index c9a9192..e4ca468 100644 --- a/backend/app/models/design_image.py +++ b/backend/app/models/design_image.py @@ -16,7 +16,7 @@ class DesignImage(Base): id = Column(BigInteger, primary_key=True, autoincrement=True, comment="图片ID") design_id = Column(BigInteger, ForeignKey("designs.id", ondelete="CASCADE"), nullable=False, comment="关联设计ID") view_name = Column(String(20), nullable=False, comment="视角名称: 效果图/正面图/侧面图/背面图") - image_url = Column(String(255), nullable=True, comment="图片URL路径") + image_url = Column(Text, nullable=True, comment="图片URL路径") model_used = Column(String(50), nullable=True, comment="使用的AI模型: flux-dev/seedream-4.5") prompt_used = Column(Text, nullable=True, comment="实际使用的英文prompt") sort_order = Column(Integer, default=0, comment="排序") diff --git a/backend/app/routers/admin.py b/backend/app/routers/admin.py index 18effc5..a8ca0a5 100644 --- a/backend/app/routers/admin.py +++ b/backend/app/routers/admin.py @@ -114,7 +114,7 @@ def init_default_configs( ("SILICONFLOW_BASE_URL", "https://api.siliconflow.cn/v1", "SiliconFlow 接口地址", "ai", "N"), ("VOLCENGINE_API_KEY", "", "火山引擎 API Key", "ai", "Y"), ("VOLCENGINE_BASE_URL", "https://ark.cn-beijing.volces.com/api/v3", "火山引擎接口地址", "ai", "N"), - ("AI_IMAGE_MODEL", "flux-dev", "默认AI生图模型 (flux-dev / seedream-4.5)", "ai", "N"), + ("AI_IMAGE_MODEL", "flux-dev", "默认AI生图模型 (flux-dev / seedream-5.0)", "ai", "N"), ("AI_IMAGE_SIZE", "1024", "AI生图默认尺寸", "ai", "N"), ] for key, val, desc, group, secret in defaults: diff --git a/backend/app/routers/designs.py b/backend/app/routers/designs.py index d58fdb4..6736fb2 100644 --- a/backend/app/routers/designs.py +++ b/backend/app/routers/designs.py @@ -4,7 +4,7 @@ """ import os from fastapi import APIRouter, Depends, HTTPException, status, Query -from fastapi.responses import FileResponse +from fastapi.responses import FileResponse, RedirectResponse from sqlalchemy.orm import Session from ..database import get_db @@ -182,6 +182,7 @@ def download_design( """ 下载设计图 只能下载自己的设计,非本人设计返回 404 + 支持远程 URL(重定向)和本地文件(兼容历史数据) """ design = design_service.get_design_by_id( db=db, @@ -201,9 +202,12 @@ def download_design( detail="设计图片不存在" ) - # 转换 URL 为文件路径 - file_path = design.image_url.lstrip("/") + # 远程 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, diff --git a/backend/app/services/ai_generator.py b/backend/app/services/ai_generator.py index 491de50..b61f2ee 100644 --- a/backend/app/services/ai_generator.py +++ b/backend/app/services/ai_generator.py @@ -1,12 +1,12 @@ """ AI 生图服务 -支持双模型:SiliconFlow FLUX.1 [dev] 和 火山引擎 Seedream 4.5 +支持双模型:SiliconFlow Kolors 和 火山引擎 Seedream 5.0 lite """ import os import uuid import logging import httpx -from typing import Optional +from typing import Optional, Tuple from ..config import settings from .config_service import get_ai_config @@ -19,12 +19,15 @@ REQUEST_TIMEOUT = 90 MAX_RETRIES = 3 -async def _call_siliconflow(prompt: str, size: int = 1024, ai_config: dict = None) -> str: +async def _call_siliconflow(prompt: str, size: int = 1024, ai_config: dict = None, seed: Optional[int] = None) -> Tuple[str, Optional[int]]: """ - 调用 SiliconFlow FLUX.1 [dev] 生图 API + 调用 SiliconFlow 生图 API(Kolors 模型) + + Args: + seed: 随机种子,传入相同 seed 可保持多视角图片风格一致 Returns: - 远程图片 URL + (远程图片 URL, 使用的 seed) """ cfg = ai_config or get_ai_config() url = f"{cfg['SILICONFLOW_BASE_URL']}/images/generations" @@ -33,30 +36,38 @@ async def _call_siliconflow(prompt: str, size: int = 1024, ai_config: dict = Non "Content-Type": "application/json", } payload = { - "model": "black-forest-labs/FLUX.1-dev", + "model": "Kwai-Kolors/Kolors", "prompt": prompt, "image_size": f"{size}x{size}", + "batch_size": 1, "num_inference_steps": 20, + "guidance_scale": 7.5, } + if seed is not None: + payload["seed"] = seed async with httpx.AsyncClient(timeout=REQUEST_TIMEOUT) as client: resp = await client.post(url, json=payload, headers=headers) resp.raise_for_status() data = resp.json() - # SiliconFlow 响应格式: {"images": [{"url": "https://..."}]} + # SiliconFlow 响应格式: {"images": [{"url": "https://..."}], "seed": 12345} images = data.get("images", []) if not images: raise ValueError("SiliconFlow 返回空图片列表") - return images[0]["url"] + returned_seed = data.get("seed") + return images[0]["url"], returned_seed -async def _call_seedream(prompt: str, size: int = 1024, ai_config: dict = None) -> str: +async def _call_seedream(prompt: str, size: int = 1024, ai_config: dict = None, seed: Optional[int] = None, ref_image_url: Optional[str] = None) -> Tuple[str, Optional[int]]: """ - 调用火山引擎 Seedream 4.5 生图 API + 调用火山引擎 Seedream 5.0 lite 生图 API + + Args: + ref_image_url: 参考图 URL,用于多视角一致性(将第一张图作为参考传入后续视角) Returns: - 远程图片 URL + (远程图片 URL, seed) """ cfg = ai_config or get_ai_config() url = f"{cfg['VOLCENGINE_BASE_URL']}/images/generations" @@ -65,37 +76,42 @@ async def _call_seedream(prompt: str, size: int = 1024, ai_config: dict = None) "Content-Type": "application/json", } payload = { - "model": "doubao-seedream-4.5-t2i-250528", + "model": "doubao-seedream-5-0-260128", "prompt": prompt, - "size": f"{size}x{size}", + "size": "2K", "response_format": "url", + "watermark": False, } + # 传入参考图保持多视角一致性(API 要求数组格式) + if ref_image_url: + payload["image"] = [ref_image_url] async with httpx.AsyncClient(timeout=REQUEST_TIMEOUT) as client: resp = await client.post(url, json=payload, headers=headers) - resp.raise_for_status() + if resp.status_code != 200: + logger.error(f"Seedream API 错误: status={resp.status_code}, body={resp.text[:500]}") + resp.raise_for_status() data = resp.json() # Seedream 响应格式: {"data": [{"url": "https://..."}]} items = data.get("data", []) if not items: raise ValueError("Seedream 返回空图片列表") - return items[0]["url"] + return items[0]["url"], seed -async def generate_image(prompt: str, model: Optional[str] = None) -> str: +async def generate_image(prompt: str, model: Optional[str] = None, seed: Optional[int] = None, ref_image_url: Optional[str] = None) -> Tuple[str, Optional[int]]: """ 统一生图接口,带重试机制 Args: - prompt: 英文提示词 - model: 模型名称 (flux-dev / seedream-4.5),为空则使用配置默认值 + prompt: 提示词 + model: 模型名称 (flux-dev / seedream-5.0) + seed: 随机种子(SiliconFlow Kolors 支持) + ref_image_url: 参考图 URL(Seedream 5.0 支持,用于多视角一致性) Returns: - 远程图片 URL - - Raises: - Exception: 所有重试失败后抛出 + (远程图片 URL, 使用的 seed) """ ai_config = get_ai_config() model = model or ai_config.get("AI_IMAGE_MODEL", "flux-dev") @@ -104,12 +120,12 @@ async def generate_image(prompt: str, model: Optional[str] = None) -> str: last_error: Optional[Exception] = None for attempt in range(1, MAX_RETRIES + 1): try: - if model == "seedream-4.5": - image_url = await _call_seedream(prompt, size, ai_config) + if model in ("seedream-5.0", "seedream-4.5"): + image_url, returned_seed = await _call_seedream(prompt, size, ai_config, seed, ref_image_url) else: - image_url = await _call_siliconflow(prompt, size, ai_config) - logger.info(f"AI 生图成功 (model={model}, attempt={attempt})") - return image_url + image_url, returned_seed = await _call_siliconflow(prompt, size, ai_config, seed) + logger.info(f"AI 生图成功 (model={model}, seed={returned_seed}, attempt={attempt})") + return image_url, returned_seed except Exception as e: last_error = e logger.warning(f"AI 生图失败 (model={model}, attempt={attempt}/{MAX_RETRIES}): {e}") diff --git a/backend/app/services/design_service.py b/backend/app/services/design_service.py index 73c8e45..f922d36 100644 --- a/backend/app/services/design_service.py +++ b/backend/app/services/design_service.py @@ -19,11 +19,13 @@ logger = logging.getLogger(__name__) def _has_ai_key() -> bool: - """检查是否配置了 AI API Key""" - model = settings.AI_IMAGE_MODEL - if model == "seedream-4.5": - return bool(settings.VOLCENGINE_API_KEY) - return bool(settings.SILICONFLOW_API_KEY) + """检查是否配置了 AI API Key(从数据库配置优先读取)""" + from .config_service import get_ai_config + ai_config = get_ai_config() + model = ai_config.get("AI_IMAGE_MODEL", "flux-dev") + if model in ("seedream-5.0", "seedream-4.5"): + return bool(ai_config.get("VOLCENGINE_API_KEY")) + return bool(ai_config.get("SILICONFLOW_API_KEY")) async def create_design_async(db: Session, user_id: int, design_data: DesignCreate) -> Design: @@ -115,9 +117,19 @@ async def _generate_ai_images( color, design_data: DesignCreate, ) -> None: - """使用 AI 模型为每个视角生成图片""" + """使用 AI 模型为每个视角生成图片 + + 多视角一致性策略: + - SiliconFlow Kolors: 通过复用 seed 保持一致 + - Seedream 5.0 lite: 通过参考图(image参数)保持一致 + """ views = get_views_for_category(category.name) - model = settings.AI_IMAGE_MODEL + from .config_service import get_ai_config + ai_config = get_ai_config() + model = ai_config.get("AI_IMAGE_MODEL", "flux-dev") + + shared_seed = None # Kolors 用: 第一张图的 seed + first_remote_url = None # Seedream 用: 第一张图的远程 URL 作为参考图 for idx, view_name in enumerate(views): # 构建 prompt @@ -136,19 +148,27 @@ async def _generate_ai_images( ) # 调用 AI 生图 - remote_url = await ai_generator.generate_image(prompt_text, model) - - # 下载保存到本地 - save_path = os.path.join( - settings.UPLOAD_DIR, "designs", f"{design.id}_{view_name}.png" + # 后续视角传入 seed(Kolors)或参考图 URL(Seedream)保持一致性 + ref_url = first_remote_url if idx > 0 else None + remote_url, returned_seed = await ai_generator.generate_image( + prompt_text, model, seed=shared_seed, ref_image_url=ref_url ) - local_url = await ai_generator.download_and_save(remote_url, save_path) + + # 第一张图保存信息供后续视角复用 + if idx == 0: + first_remote_url = remote_url + if returned_seed is not None: + shared_seed = returned_seed + logger.info(f"多视角生图: seed={shared_seed}, ref_url={remote_url[:60]}...") + + # 直接使用远程 URL,不下载到本地(节省服务器存储空间) + image_url = remote_url # 创建 DesignImage 记录 design_image = DesignImage( design_id=design.id, view_name=view_name, - image_url=local_url, + image_url=image_url, model_used=model, prompt_used=prompt_text, sort_order=idx, @@ -157,7 +177,7 @@ async def _generate_ai_images( # 第一张图(效果图)存入 design.image_url 兼容旧逻辑 if idx == 0: - design.image_url = local_url + design.image_url = image_url design.status = "completed" diff --git a/backend/uploads/designs/1.png b/backend/uploads/designs/1.png deleted file mode 100644 index 52c54997bb0805c8a5edd7fcb7977e734c3c9ba6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 34580 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 diff --git a/frontend/src/api/admin.ts b/frontend/src/api/admin.ts index c8f26c0..18c8dfd 100644 --- a/frontend/src/api/admin.ts +++ b/frontend/src/api/admin.ts @@ -7,7 +7,7 @@ export const getDashboard = () => request.get('/admin/dashboard') export const getConfigs = (group?: string) => request.get('/admin/configs', { params: group ? { group } : {} }) -export const updateConfigs = (configs: Record) => +export const updateConfigs = (_configs: Record) => request.post('/admin/configs', null, { // PUT 方法 }) diff --git a/frontend/src/api/design.ts b/frontend/src/api/design.ts index 5586f87..a8a1757 100644 --- a/frontend/src/api/design.ts +++ b/frontend/src/api/design.ts @@ -70,9 +70,9 @@ export function getDesignApi(id: number) { return request.get(`/designs/${id}`) } -// 生成设计 +// 生成设计(AI生图需要较长时间,超时设为5分钟) export function generateDesignApi(data: GenerateDesignParams) { - return request.post('/designs/generate', data) + return request.post('/designs/generate', data, { timeout: 300000 }) } // 删除设计 @@ -80,7 +80,7 @@ export function deleteDesignApi(id: number) { return request.delete(`/designs/${id}`) } -// 获取设计下载 URL +// 获取设计下载 URL(相对于 baseURL /api) export function getDesignDownloadUrl(id: number) { - return `/api/designs/${id}/download` + return `/designs/${id}/download` } diff --git a/frontend/src/components/DesignPreview.vue b/frontend/src/components/DesignPreview.vue index a29f297..822e8f6 100644 --- a/frontend/src/components/DesignPreview.vue +++ b/frontend/src/components/DesignPreview.vue @@ -85,14 +85,14 @@
- - 下载设计图 - + {{ downloading ? '下载中...' : '下载设计图' }} +