feat(admin): Phase 5 火山人像审核队列 — 跨团队 person 资产审核(列/筛/批量送审/轮询)
后端:adminpanel asset-reviews(列跨团队 person + 状态筛分页)+ submit(批量送审/重试)+ poll(轮询 processing), 复用 assets/review.py,IsPlatformAdmin + 审计;AdminReviewAssetSerializer;修 N+1(prefetch 缓存取首图,列表 5s→1s)。 前端:adminApi 审核系列;Admin 资产审核页(状态筛 + 多选表格 + 绿/红/灰状态盾 + 重试 + 吸底 bulk-bar 批量送审 + 刷新状态)。 测试:adminpanel 28 单测过(mock 火山:队列只列 person/状态筛/批量送审计数/poll 只轮 processing); 无头 e2e _admin-p5.mjs 5 断言过 + 0 console error(只读 poll,不点真送审);tsc+build 绿。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
01d4f235ed
commit
5aab8a568f
@@ -11,9 +11,12 @@ from apps.accounts.models import Invitation, Team, User
|
||||
from apps.accounts.permissions import IsPlatformAdmin
|
||||
from apps.accounts.serializers import InvitationSerializer
|
||||
from apps.ai.models import QualityWord
|
||||
from apps.assets.models import Asset
|
||||
from apps.assets.review import poll_asset_review, submit_asset_for_review
|
||||
from apps.common.pagination import DefaultPagination
|
||||
|
||||
from .serializers import (
|
||||
AdminReviewAssetSerializer,
|
||||
AdminTeamMemberSerializer,
|
||||
AdminTeamSerializer,
|
||||
AdminUserSerializer,
|
||||
@@ -262,3 +265,64 @@ def admin_quality_word_detail(request, word_id):
|
||||
after={"text": obj.text},
|
||||
)
|
||||
return Response(QualityWordSerializer(obj).data)
|
||||
|
||||
|
||||
# ─────────────────────────── 火山人像审核队列 ───────────────────────────
|
||||
|
||||
_REVIEW_STATUSES = {"", "processing", "active", "failed"}
|
||||
|
||||
|
||||
@api_view(["GET"])
|
||||
@permission_classes([IsPlatformAdmin])
|
||||
def admin_asset_reviews(request):
|
||||
"""跨团队真人(person)资产审核队列。?review_status=none|processing|active|failed 过滤(none=未送审)。"""
|
||||
qs = (
|
||||
Asset.objects.filter(category=Asset.Category.PERSON, is_deleted=False)
|
||||
.select_related("team")
|
||||
.prefetch_related("files")
|
||||
.order_by("-created_at")
|
||||
)
|
||||
rs = request.query_params.get("review_status")
|
||||
if rs == "none":
|
||||
qs = qs.filter(review_status="")
|
||||
elif rs in {"processing", "active", "failed"}:
|
||||
qs = qs.filter(review_status=rs)
|
||||
search = (request.query_params.get("search") or "").strip()
|
||||
if search:
|
||||
qs = qs.filter(team__name__icontains=search)
|
||||
paginator = DefaultPagination()
|
||||
page = paginator.paginate_queryset(qs, request)
|
||||
return paginator.get_paginated_response(AdminReviewAssetSerializer(page, many=True).data)
|
||||
|
||||
|
||||
@api_view(["POST"])
|
||||
@permission_classes([IsPlatformAdmin])
|
||||
def admin_asset_reviews_submit(request):
|
||||
"""批量送审(也用于失败重试):对给定 person 资产逐个 submit_asset_for_review。"""
|
||||
ids = request.data.get("asset_ids") or []
|
||||
assets = list(Asset.objects.filter(id__in=ids, category=Asset.Category.PERSON, is_deleted=False))
|
||||
for asset in assets:
|
||||
submit_asset_for_review(asset)
|
||||
statuses = {str(a.id): a.review_status for a in Asset.objects.filter(id__in=ids)}
|
||||
log_admin_action(
|
||||
request,
|
||||
"asset_review.submit",
|
||||
target_type="asset",
|
||||
target_name=f"{len(assets)} assets",
|
||||
after={"count": len(assets)},
|
||||
)
|
||||
return Response({"submitted": len(assets), "statuses": statuses})
|
||||
|
||||
|
||||
@api_view(["POST"])
|
||||
@permission_classes([IsPlatformAdmin])
|
||||
def admin_asset_reviews_poll(request):
|
||||
"""轮询审核中(processing)资产的最新状态。给 asset_ids 则只轮询这些,否则轮询全平台 processing。"""
|
||||
ids = request.data.get("asset_ids")
|
||||
qs = Asset.objects.filter(category=Asset.Category.PERSON, review_status="processing", is_deleted=False)
|
||||
if ids:
|
||||
qs = qs.filter(id__in=ids)
|
||||
statuses = {}
|
||||
for asset in qs:
|
||||
statuses[str(asset.id)] = poll_asset_review(asset)
|
||||
return Response({"polled": len(statuses), "statuses": statuses})
|
||||
|
||||
Reference in New Issue
Block a user