后端: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>
76 lines
2.8 KiB
Python
76 lines
2.8 KiB
Python
from rest_framework import serializers
|
|
|
|
from apps.accounts.models import Team, TeamMember, User
|
|
from apps.ai.models import QualityWord
|
|
from apps.assets.models import Asset
|
|
|
|
|
|
class QualityWordSerializer(serializers.ModelSerializer):
|
|
class Meta:
|
|
model = QualityWord
|
|
fields = ["id", "stage", "slot", "text", "sort", "enabled", "created_at"]
|
|
read_only_fields = ["id", "created_at"]
|
|
|
|
|
|
class AdminReviewAssetSerializer(serializers.ModelSerializer):
|
|
"""跨团队人像审核队列行:复用 AssetFileSerializer 的首图 preview_url。"""
|
|
|
|
team_name = serializers.CharField(source="team.name", read_only=True, default=None)
|
|
preview_url = serializers.SerializerMethodField()
|
|
|
|
class Meta:
|
|
model = Asset
|
|
fields = ["id", "name", "category", "review_status", "review_error", "team", "team_name", "preview_url", "created_at"]
|
|
read_only_fields = fields
|
|
|
|
def get_preview_url(self, obj):
|
|
from apps.assets.serializers import AssetFileSerializer
|
|
|
|
# 用 prefetch 缓存(list(...))而非 .first():后者会另发查询,列表页 N+1 拖慢到数秒
|
|
files = list(obj.files.all())
|
|
return AssetFileSerializer(files[0]).data.get("preview_url", "") if files else ""
|
|
|
|
|
|
class AdminTeamSerializer(serializers.ModelSerializer):
|
|
owner_username = serializers.CharField(source="owner.username", read_only=True, default=None)
|
|
member_count = serializers.SerializerMethodField()
|
|
balance = serializers.SerializerMethodField()
|
|
|
|
class Meta:
|
|
model = Team
|
|
fields = ["id", "name", "status", "owner", "owner_username", "member_count", "balance", "created_at"]
|
|
read_only_fields = fields
|
|
|
|
def get_member_count(self, obj):
|
|
anno = getattr(obj, "member_count_anno", None)
|
|
return anno if anno is not None else obj.members.count()
|
|
|
|
def get_balance(self, obj):
|
|
acct = getattr(obj, "credit_account", None)
|
|
return str(acct.balance) if acct is not None else "0"
|
|
|
|
|
|
class AdminTeamMemberSerializer(serializers.ModelSerializer):
|
|
username = serializers.CharField(source="user.username", read_only=True)
|
|
user_status = serializers.CharField(source="user.status", read_only=True)
|
|
|
|
class Meta:
|
|
model = TeamMember
|
|
fields = ["id", "username", "role", "status", "user_status", "monthly_credit_limit"]
|
|
read_only_fields = fields
|
|
|
|
|
|
class AdminUserSerializer(serializers.ModelSerializer):
|
|
teams = serializers.SerializerMethodField()
|
|
|
|
class Meta:
|
|
model = User
|
|
fields = ["id", "username", "status", "is_platform_admin", "date_joined", "teams"]
|
|
read_only_fields = fields
|
|
|
|
def get_teams(self, obj):
|
|
return [
|
|
{"team_id": str(m.team_id), "team_name": m.team.name, "role": m.role}
|
|
for m in obj.team_memberships.select_related("team").all()
|
|
]
|