fix: 修复图片补图删除回填

This commit is contained in:
hh
2026-07-16 15:30:02 +08:00
parent 95cd4abd3c
commit ff5eaeee8a
8 changed files with 112 additions and 19 deletions
+15 -1
View File
@@ -2775,7 +2775,7 @@ def _reap_stale_standalone_image_tasks(*, team) -> None:
continue
def enqueue_standalone_images(*, team, user, prompt: str, mode: str = "image", count: int = 1, product_id: str | None = None, reference_product: bool = False, model_id: str | None = None, model_entity_id: str | None = None, ratio: str | None = None, image_model: str | None = None, conversation=None, reference_image_ids: list[str] | None = None, platform_id: str | None = None, batch_id: str | None = None, tryon_prompt_v2_override: bool = False, tryon_ab: dict | None = None, dispatch: bool = True) -> list[AITask]:
def enqueue_standalone_images(*, team, user, prompt: str, mode: str = "image", count: int = 1, product_id: str | None = None, reference_product: bool = False, model_id: str | None = None, model_entity_id: str | None = None, ratio: str | None = None, image_model: str | None = None, conversation=None, reference_image_ids: list[str] | None = None, platform_id: str | None = None, batch_id: str | None = None, retry_of_task_id: str | None = None, tryon_prompt_v2_override: bool = False, tryon_ab: dict | None = None, dispatch: bool = True) -> list[AITask]:
"""独立生图(图片创作 / 模特上身图 / 平台套图)改为**异步**:本函数在 Web 请求里只做「建任务 +
预留额度」这种秒级的活,真正 ~30s 的 ARK 出图交给 Celery worker(generate_standalone_image_task)。
@@ -2812,6 +2812,18 @@ def enqueue_standalone_images(*, team, user, prompt: str, mode: str = "image", c
batch_id = None
if not is_append:
batch_id = str(uuid.uuid4())
retry_of_task_id = None
elif retry_of_task_id:
retry_of_task = AITask.objects.filter(
id=retry_of_task_id,
team=team,
project__isnull=True,
is_deleted=False,
purged_at__isnull=True,
request_payload__batch_id=batch_id,
status__in=(AITask.Status.FAILED, AITask.Status.CANCELLED),
).first()
retry_of_task_id = str(retry_of_task.id) if retry_of_task else None
# 平台套图:规范化平台 id(前端 dy/tb… → canonical),用于注入平台版式块(优化版);非 cover 模式忽略。
platform_key = str(platform_id or "").strip() if mode == "cover" else ""
platform_name = _PLATFORM_NAMES.get(platform_key, "")
@@ -2884,6 +2896,8 @@ def enqueue_standalone_images(*, team, user, prompt: str, mode: str = "image", c
# 只在重跑/补图时落键(不落 False):workbench 用 KeyTextTransform 抽文本,"false" 字符串也是真值,会误判
if is_append:
request_payload["batch_append"] = True
if retry_of_task_id:
request_payload["retry_of_task_id"] = retry_of_task_id
if quote.meta.get("rate"):
request_payload["points_per_yuan_snapshot"] = quote.meta["rate"]
task = AITask.objects.create(
+5 -1
View File
@@ -1215,10 +1215,13 @@ class ImageConversationTests(TestCase):
conv_id = first.json()["conversation_id"]
batch_id = first.json()["batch_id"]
self.assertTrue(batch_id)
failed_task = AITask.objects.filter(conversation_id=conv_id).order_by("created_at").first()
failed_task.status = AITask.Status.FAILED
failed_task.save(update_fields=["status"])
# 重跑一张:带原对话 + 原 batch_id → 归回原批次
second = self.client.post(
"/api/ai/generate-image/",
{"prompt": "一只猫", "mode": "image", "count": 1, "conversation_id": conv_id, "batch_id": batch_id},
{"prompt": "一只猫", "mode": "image", "count": 1, "conversation_id": conv_id, "batch_id": batch_id, "retry_of_task_id": str(failed_task.id)},
format="json",
)
self.assertEqual(second.status_code, 202, second.content)
@@ -1227,6 +1230,7 @@ class ImageConversationTests(TestCase):
self.assertEqual(len(tasks), 3)
self.assertTrue(all(t["batch_id"] == batch_id for t in tasks))
self.assertEqual([t["rerun"] for t in tasks], [False, False, True])
self.assertEqual(tasks[-1]["retry_of_task_id"], str(failed_task.id))
# 非法 batch_id(不是 UUID):不沿用,铸新批次,不打 append 标记
third = self.client.post(
"/api/ai/generate-image/",
+4 -1
View File
@@ -48,6 +48,7 @@ class GenerateImageView(APIView):
conversation_id = str(request.data.get("conversation_id") or "").strip() or None
# 重跑/补图:前端带原批次 batch_id → enqueue 沿用(UUID 校验),记录归回原批次不裂新卡
batch_id = str(request.data.get("batch_id") or "").strip() or None
retry_of_task_id = str(request.data.get("retry_of_task_id") or "").strip() or None
# 用户在图片创作里上传的参考图(已先传成 Asset),按 id 列表带入 → 生成时作多图参考(image_edit)
raw_refs = request.data.get("reference_image_ids") or []
if isinstance(raw_refs, str):
@@ -69,7 +70,7 @@ class GenerateImageView(APIView):
title=(prompt[:24] or "默认创作"),
)
try:
tasks = enqueue_standalone_images(team=team, user=request.user, prompt=prompt, mode=mode, count=count, product_id=product_id, reference_product=reference_product, model_id=model_id, model_entity_id=model_entity_id, ratio=ratio, image_model=image_model, conversation=conversation, reference_image_ids=reference_image_ids, platform_id=platform_id, batch_id=batch_id)
tasks = enqueue_standalone_images(team=team, user=request.user, prompt=prompt, mode=mode, count=count, product_id=product_id, reference_product=reference_product, model_id=model_id, model_entity_id=model_entity_id, ratio=ratio, image_model=image_model, conversation=conversation, reference_image_ids=reference_image_ids, platform_id=platform_id, batch_id=batch_id, retry_of_task_id=retry_of_task_id)
except ValueError as exc: # 无可用模型 / 余额不足等,立即反馈
internal_kind = "user_credit_insufficient" if str(exc).strip().lower() == "insufficient credit" else ""
public_error = classify_generation_error(
@@ -365,6 +366,7 @@ class AITaskViewSet(TeamScopedViewSetMixin, ReadOnlyModelViewSet):
"model_entity_id": t.rp_model_entity_id or "",
"platform_id": t.rp_platform_id or "",
"rerun": bool(t.rp_batch_append),
"retry_of_task_id": str((t.request_payload or {}).get("retry_of_task_id") or ""),
"created_at": t.created_at,
# 软删的图不再出现在工作台记录里(R109:删除资产库图片 → 任务记录联动)
"assets": AssetSerializer(
@@ -499,6 +501,7 @@ class ImageConversationViewSet(TeamScopedViewSetMixin, ModelViewSet):
"ratio": (t.request_payload or {}).get("ratio") or "",
# 重跑/补图任务:不计入批次「应出张数」,前端据此正确渲染失败格数量
"rerun": bool((t.request_payload or {}).get("batch_append")),
"retry_of_task_id": str((t.request_payload or {}).get("retry_of_task_id") or ""),
"reference_images": resolve_refs((t.request_payload or {}).get("reference_image_ids")),
"created_at": t.created_at,
"assets": AssetSerializer(