diff --git a/core/backend/apps/assets/review.py b/core/backend/apps/assets/review.py index dff4ec1..96cc746 100644 --- a/core/backend/apps/assets/review.py +++ b/core/backend/apps/assets/review.py @@ -59,26 +59,30 @@ def get_or_create_team_group(team) -> AssetReviewGroup: return grp -def submit_asset_for_review(asset: Asset) -> None: - """真人资产静默送审:建组(若无)→ 传素材 → 标 processing。出错只记日志,不抛。""" +def submit_asset_for_review(asset: Asset) -> bool: + """真人资产送审:建组(若无)→ 传素材 → 标 processing。出错只记日志,不抛。 + 返回是否真正进入审核(True=已标 processing;False=未送审/未配置/失败), + 供手动兜底端点据此如实回报,避免前端把「没送出去」误显示成「审核中」。""" if not assets_client.is_enabled() or asset.category not in Asset.REVIEW_CATEGORIES: - return + return False url = _asset_url(asset) if not url: - return + return False try: grp = get_or_create_team_group(asset.team) remote_id = assets_client.create_asset(group_id=grp.remote_group_id, image_url=url, name=(asset.name or "person")[:64]) if not remote_id: # 火山没回 Id:不要标 processing(否则 remote_id 为空、poll 永远早退、卡死黄),留空可重试 logger.warning("create_asset 返回空 id,asset %s 暂不送审(可重试)", asset.id) - return + return False asset.review_remote_id = remote_id asset.review_status = "processing" asset.review_error = "" asset.save(update_fields=["review_remote_id", "review_status", "review_error", "updated_at"]) + return True except Exception as exc: # noqa: BLE001 logger.warning("submit_asset_for_review failed for asset %s: %s", asset.id, exc) + return False def poll_asset_review(asset: Asset) -> str: diff --git a/core/backend/apps/assets/tests.py b/core/backend/apps/assets/tests.py index 24b0eaf..32387cb 100644 --- a/core/backend/apps/assets/tests.py +++ b/core/backend/apps/assets/tests.py @@ -162,6 +162,27 @@ class SubmitReviewTests(TestCase): res = self.client.post(f"/api/assets/{self.product.id}/submit-review/") self.assertEqual(res.status_code, 400) + def test_submit_review_503_when_submission_did_not_start(self): + """送审没真正发出去(服务未启用/未配 SDK/火山未回 Id),且素材非终态 → 503, + review_status 仍为空。这样前端不会把「没送出去」误显示成「审核中」(刷新即打回原形)。""" + with patch("apps.assets.review.submit_asset_for_review", return_value=False) as sub: + res = self.client.post(f"/api/assets/{self.person.id}/submit-review/") + sub.assert_called_once() + self.assertEqual(res.status_code, 503) + self.assertEqual(res.json().get("review_status"), "") + + def test_submit_review_200_when_started(self): + """真正进入审核(processing)→ 200 + 真态,供前端落 processing。""" + def _mark_processing(asset): + asset.review_status = "processing" + asset.save(update_fields=["review_status"]) + return True + + with patch("apps.assets.review.submit_asset_for_review", side_effect=_mark_processing): + res = self.client.post(f"/api/assets/{self.person.id}/submit-review/") + self.assertEqual(res.status_code, 200) + self.assertEqual(res.json().get("review_status"), "processing") + class ReviewScopeTests(TestCase): """送审范围 = 角色定妆照(person)/ 三视图(tri_view)/ 分镜图(storyboard);图片趴不送审。""" diff --git a/core/backend/apps/assets/views.py b/core/backend/apps/assets/views.py index 396631a..1ed294b 100644 --- a/core/backend/apps/assets/views.py +++ b/core/backend/apps/assets/views.py @@ -125,8 +125,15 @@ class AssetViewSet(TeamScopedViewSetMixin, ModelViewSet): asset = self.get_object() # team-scoped + is_deleted 过滤 if asset.category not in Asset.REVIEW_CATEGORIES: return Response({"detail": "该素材无需审核"}, status=status.HTTP_400_BAD_REQUEST) - submit_asset_for_review(asset) + started = submit_asset_for_review(asset) asset.refresh_from_db() + # 没真正送出去(审核服务未启用/未配置 SDK/火山未回 Id/调用异常),且素材也不是已终态: + # 如实回报 503,别返回空 review_status —— 否则前端会把「没送出去」误显示成「审核中」,刷新就打回原形。 + if not started and asset.review_status not in ("processing", "active", "failed"): + return Response( + {"detail": "审核服务暂不可用,请稍后重试", "review_status": asset.review_status or ""}, + status=status.HTTP_503_SERVICE_UNAVAILABLE, + ) return Response({"review_status": asset.review_status, "review_error": asset.review_error or ""}) @action(detail=False, methods=["get"], url_path="video-packs")