refactor(core): 基础资产改对逻辑+对齐设计稿(一组多版本候选 + 详情弹窗 asset-detail-grid)
逻辑(后端):用 BaseAssetGroup 本来的设计,不再每次生成新建组 - generate_base_asset 复用同实体的组(商品=唯一商品组;人物/场景按 label):追加 candidate + 采用最新 - 版本 = candidate_assets,采用/当前 = adopted_asset;采用/切换走真 adopt-base-asset(下游只认 adopted) - 人物三视图绑定到「某一版立绘 asset」:metadata.triview_of=<立绘 asset id>,同立绘多次=同组追加候选 - generate-triview 端点改取 portrait_asset_id;attach-base-asset 坏参数 404 加固 设计(前端): - 人物/场景卡对齐设计稿(.prompt-box 可编辑 + 重跑/替换 + 转圈态,区头 N 个 + 克制「+ 新增」) - 详情弹窗整套换成设计稿 asset-detail-grid:左大立绘(3:4)+版本缩略条;右三视图 section(16:9 + 重跑/下载 + 单张大图 + 暂无 tip + v1/v2 版本条)+ 提示词 section;脚部 下载立绘 + 使用该资产;场景无三视图 - 商品三视图内联面板版本=candidate_assets、采用走真 adopt - 修:.thumb/.v-thumb/.h-thumb 非 .placeholder 导致缩略图不显图 → 接 --mock-media-url 本地 playwright 截图实测:基础资产卡 + 详情弹窗均按设计稿渲染。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -561,7 +561,22 @@ def _store_generated_media(*, team, user, project, task, media: str, name: str,
|
||||
return asset
|
||||
|
||||
|
||||
def generate_base_asset(*, project, user, kind: str, prompt: str, label: str = "") -> BaseAssetGroup:
|
||||
def _find_entity_group(project, kind: str, label: str, group_id: str | None):
|
||||
"""复用同一实体的基础资产组(版本=candidate_assets,采用=adopted_asset)。
|
||||
优先 group_id;商品=该项目唯一商品组;人物/场景按 label 命中非三视图组;都没有则 None(新建)。"""
|
||||
if group_id:
|
||||
return project.base_asset_groups.filter(id=group_id).first()
|
||||
candidates = list(project.base_asset_groups.filter(kind=kind).order_by("created_at"))
|
||||
candidates = [g for g in candidates if not (g.metadata or {}).get("triview_of")] # 三视图组不算实体本体
|
||||
if kind == BaseAssetGroup.Kind.PRODUCT:
|
||||
return candidates[0] if candidates else None
|
||||
lbl = (label or "").strip()
|
||||
if lbl:
|
||||
return next((g for g in candidates if (g.metadata or {}).get("label") == lbl), None)
|
||||
return None
|
||||
|
||||
|
||||
def generate_base_asset(*, project, user, kind: str, prompt: str, label: str = "", group_id: str | None = None) -> BaseAssetGroup:
|
||||
model_config = get_default_model(ModelConfig.Capability.IMAGE)
|
||||
if model_config is None:
|
||||
raise ValueError("no active image model configured")
|
||||
@@ -604,12 +619,16 @@ def generate_base_asset(*, project, user, kind: str, prompt: str, label: str = "
|
||||
category=category,
|
||||
asset_type=Asset.Type.IMAGE,
|
||||
)
|
||||
# label = 该资产对应的脚本提取标签(人物/场景名),用于把生成结果归回对应的标签卡
|
||||
group_meta = {"label": label.strip()} if label and label.strip() else {}
|
||||
group = BaseAssetGroup.objects.create(project=project, kind=kind, task=task, prompt=prompt, metadata=group_meta)
|
||||
# 复用同实体的组:追加候选 + 采用最新(版本=candidate_assets,采用=adopted_asset);无则新建
|
||||
group = _find_entity_group(project, kind, label, group_id)
|
||||
if group is None:
|
||||
group_meta = {"label": label.strip()} if label and label.strip() else {}
|
||||
group = BaseAssetGroup.objects.create(project=project, kind=kind, task=task, prompt=prompt, metadata=group_meta)
|
||||
elif prompt:
|
||||
group.prompt = prompt
|
||||
group.candidate_assets.add(asset)
|
||||
group.adopted_asset = asset
|
||||
group.save(update_fields=["adopted_asset", "updated_at"])
|
||||
group.save(update_fields=["adopted_asset", "prompt", "updated_at"])
|
||||
# 真人资产:事务提交后静默送火山审核(best-effort,网络调用放 on_commit 避免占着事务)
|
||||
if kind == BaseAssetGroup.Kind.PERSON:
|
||||
from apps.assets.review import submit_asset_for_review
|
||||
@@ -625,16 +644,14 @@ def generate_base_asset(*, project, user, kind: str, prompt: str, label: str = "
|
||||
raise
|
||||
|
||||
|
||||
def generate_person_triview(*, project, user, portrait_group) -> "BaseAssetGroup":
|
||||
"""流程步骤4 · 据「某一版立绘」生成它配套的三视图(image_edit 以立绘为参考,锁角色一致性)。
|
||||
三视图与立绘 1:1 绑定:新立绘 → 新三视图。落一个 metadata.triview_of=<立绘组id> 的 person 组。"""
|
||||
def generate_person_triview(*, project, user, portrait_asset) -> "BaseAssetGroup":
|
||||
"""流程步骤4 · 据「某一版立绘资产」生成它配套的三视图(image_edit 以立绘为参考,锁角色一致性)。
|
||||
三视图与立绘 1:1 绑定:metadata.triview_of=<立绘 asset id>;同一立绘多次=同组追加候选(版本)。"""
|
||||
from apps.ai.model_library import THREE_VIEW_PROMPT
|
||||
|
||||
if portrait_group.kind != BaseAssetGroup.Kind.PERSON:
|
||||
raise ValueError("三视图仅用于人物立绘")
|
||||
portrait_asset = portrait_group.adopted_asset
|
||||
if portrait_asset is None:
|
||||
raise ValueError("该立绘尚未生成,无法据它生成三视图")
|
||||
asset_key = str(portrait_asset.id)
|
||||
model_config = get_default_model(ModelConfig.Capability.IMAGE)
|
||||
if model_config is None:
|
||||
raise ValueError("no active image model configured")
|
||||
@@ -642,7 +659,7 @@ def generate_person_triview(*, project, user, portrait_group) -> "BaseAssetGroup
|
||||
if not hasattr(provider, "image_edit"):
|
||||
raise ValueError(f"当前图像模型 {model_config.provider.name}:{model_config.name} 不支持参考图三视图(image_edit)")
|
||||
ref_url = _asset_preview_url(portrait_asset)
|
||||
payload = {"model": model_config.name, "prompt": THREE_VIEW_PROMPT, "kind": "person", "triview_of": str(portrait_group.id)}
|
||||
payload = {"model": model_config.name, "prompt": THREE_VIEW_PROMPT, "kind": "person", "triview_of": asset_key}
|
||||
task = create_ai_task(project=project, user=user, task_type=AITask.Type.PERSON_IMAGE, model_config=model_config, request_payload=payload)
|
||||
reservation = task.credit_reservation
|
||||
try:
|
||||
@@ -659,11 +676,14 @@ def generate_person_triview(*, project, user, portrait_group) -> "BaseAssetGroup
|
||||
team=project.team, user=user, project=project, task=task, media=media,
|
||||
name=f"{project.name}-三视图", category=Asset.Category.PERSON, asset_type=Asset.Type.IMAGE,
|
||||
)
|
||||
# label 带立绘组 id:前端据此把三视图归到对应立绘版本(切立绘 → 切三视图)
|
||||
group = BaseAssetGroup.objects.create(
|
||||
project=project, kind=BaseAssetGroup.Kind.PERSON, task=task, prompt=THREE_VIEW_PROMPT,
|
||||
metadata={"label": f"{portrait_group.id} ·三视图", "triview_of": str(portrait_group.id)},
|
||||
)
|
||||
# 复用该立绘的三视图组(triview_of==立绘asset id):追加候选 + 采用最新
|
||||
group = next((g for g in project.base_asset_groups.filter(kind=BaseAssetGroup.Kind.PERSON).order_by("created_at")
|
||||
if (g.metadata or {}).get("triview_of") == asset_key), None)
|
||||
if group is None:
|
||||
group = BaseAssetGroup.objects.create(
|
||||
project=project, kind=BaseAssetGroup.Kind.PERSON, task=task, prompt=THREE_VIEW_PROMPT,
|
||||
metadata={"label": "·三视图", "triview_of": asset_key},
|
||||
)
|
||||
group.candidate_assets.add(asset)
|
||||
group.adopted_asset = asset
|
||||
group.save(update_fields=["adopted_asset", "updated_at"])
|
||||
|
||||
Reference in New Issue
Block a user