上线版: 产品表单统一+form嵌套修复+用户管理+部署+三套叙事

- 产品编辑入口统一走 ProductFormFull(卖点/风格/人群/品牌词全字段);
  修复开任务页 <form> 套 <form> 致"编辑产品"报错、改不了、跳回首个产品
- dashboard 入口卡片对齐实际路由: 系统管理(/config) 与 工作配置(/settings) 分开;
  settings ?tab=products 直达改用挂载后读 URL, 消除 hydration mismatch
- 新增用户管理(users API/admin service/改密页) + alembic 022/023/024
- 上线部署: Dockerfile / docker-compose.prod+https / nginx https / .env.example
- A8 三套正交叙事(痛点/场景/成分背书) + beige 调色去AI化 + 飞轮 text_import 高权重信号

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
yangqianqian
2026-06-30 18:08:13 +08:00
parent a77212781c
commit df1856d793
150 changed files with 8616 additions and 1765 deletions

View File

@@ -68,17 +68,19 @@ def create_generation_task(
# 已生成完等挑选/审核的不占 worker。红线=每用户5个(可配置)。
_check_concurrency_limit(db, current_user.user_id, current_user.workspace_id)
from app.models.product import Product
product = db.query(Product).filter(
Product.id == body.product_id,
Product.workspace_id == current_user.workspace_id,
).first()
if not product:
raise_business("产品不存在")
# 禁降级铁律:本次产品入镜(need_product_image=True)时,产品必须已上传参考图,
# 否则拒绝建任务(不允许降级纯文生图,防产品包装跑偏/过抽检失败)。
need_img = getattr(body, "need_product_image", True)
if need_img:
from app.models.product import Product
product = db.query(Product).filter(
Product.id == body.product_id,
Product.workspace_id == current_user.workspace_id,
).first()
if not product:
raise_business("产品不存在")
if not (product.image_path or "").strip():
raise_business("该产品未上传参考图,无法生成产品入镜内容;请先到产品库上传产品图,或关闭「产品入镜」开关")
@@ -111,10 +113,14 @@ def create_generation_task(
def enqueue_generation(task_id: int, regen_strategy: str | None = None,
regen_role: str | None = None, custom_prompt: str | None = None) -> None:
regen_role: str | None = None, custom_prompt: str | None = None,
reuse_text: bool = False) -> None:
"""只推 task_id+可选重生参数)入队,绝不推 key基石B
regen_strategy/regen_role/custom_prompt 仅 R2 单张/单套重生时传,常规生成留 None。"""
regen_strategy/regen_role/custom_prompt 仅 R2 单张/单套重生时传,常规生成留 None。
reuse_text=True 仅导入轨「去生图」时传:首次出图但复用库内导入文案,跳过文案生成。"""
from app.workers.tasks import run_generation_pipeline
run_generation_pipeline.delay(task_id, regen_strategy=regen_strategy,
regen_role=regen_role, custom_prompt=custom_prompt)
logger.info("Enqueued task_id=%s regen=%s/%s", task_id, regen_strategy, regen_role)
regen_role=regen_role, custom_prompt=custom_prompt,
reuse_text=reuse_text)
logger.info("Enqueued task_id=%s regen=%s/%s reuse_text=%s",
task_id, regen_strategy, regen_role, reuse_text)