fix(core): 基础资产「生成中」占位卡刷新后从后端认领恢复(出图本在 worker 跑,只是前端 loading 丢了)

问题:立绘/商品图出图是异步(worker),但 loading 占位只活在前端内存 genBusy + 当前页轮询 Promise,
刷新即丢 → 占位卡退回「待生成」,虽 worker 仍在跑、跑完手动刷新才见。
修复:
- 后端 GET /api/projects/{id}/pending-assets/ 返回在途出图任务(id/kind/label/status,读 request_payload)
- 前端进基础资产趴轮询该端点,据 (kind,label) 用 pendingHas 把对应占位卡置「生成中」;
  在途数减少=有任务完成 → onRefreshProject 把占位卡换成真卡。离开该趴停止轮询。
tsc + py_compile + 21 测试通过。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
zyc
2026-06-17 15:36:30 +08:00
co-authored by Claude Opus 4.8
parent 296888f0bf
commit 0d1fce1cc1
3 changed files with 58 additions and 4 deletions
+21 -1
View File
@@ -11,7 +11,7 @@ from rest_framework.renderers import BaseRenderer
from rest_framework.response import Response
from rest_framework.viewsets import ModelViewSet
from apps.ai.models import ModelConfig
from apps.ai.models import AITask, ModelConfig
from apps.ai.providers import TtsNotConfigured
from apps.ai.script_agent import stream_script_agent
from apps.ai.services import (
@@ -238,6 +238,26 @@ class ProjectViewSet(TeamScopedViewSetMixin, ModelViewSet):
# 异步:出图在 worker 里跑,秒回 RESERVED 任务,前端轮询 /api/ai/generate-image/?ids=… 取结果后刷新项目
return Response({"task": {"id": str(task.id), "status": task.status}}, status=status.HTTP_202_ACCEPTED)
@action(detail=True, methods=["get"], url_path="pending-assets")
def pending_assets(self, request, pk=None):
"""本项目在途的基础资产出图任务(立绘/商品图/三视图)。前端进基础资产趴时据此重建
「生成中」占位卡 loading —— 出图在 worker 跑,刷新页面后内存态 genBusy 丢了,用它从后端认领恢复。"""
project = self.get_object()
inflight = [AITask.Status.CREATED, AITask.Status.RESERVED, AITask.Status.SUBMITTED, AITask.Status.POLLING]
image_types = [AITask.Type.PRODUCT_IMAGE, AITask.Type.PERSON_IMAGE, AITask.Type.SCENE_IMAGE]
tasks = AITask.objects.filter(project=project, task_type__in=image_types, status__in=inflight)
pending = []
for task in tasks:
payload = task.request_payload or {}
pending.append({
"id": str(task.id),
"kind": payload.get("kind") or "",
"label": payload.get("label") or "",
"is_triview": bool(payload.get("triview_of")),
"status": task.status,
})
return Response({"pending": pending})
@action(detail=True, methods=["post"], url_path="poll-reviews")
def poll_reviews(self, request, pk=None):
"""轮询本团队真人资产的火山审核状态(绿盾 active / 红标 failed)。前端基础资产趴定时调,刷新徽章。"""