Files
yingqing/core/backend/apps/assets/serializers.py
T
seaislee1209andClaude Opus 4.8 b30eaed838 feat(core): 火山人像素材库审核(绿/红盾)接入 + 端到端验证
- assets_client.py:照搬 AirDrama,volcengine SDK + AK/SK 签名(凭证走 settings.ASSETS_API)。
- review.py:真人资产送审/轮询编排。一团队一组(AssetReviewGroup,OneToOne),全 best-effort 不破坏主流程。
- 模型:AssetReviewGroup + Asset.review_status/review_remote_id/review_error(迁移 assets/0003)。
- 集成:真人基础资产生成后 transaction.on_commit 静默送审(services.generate_base_asset kind==person)。
- 端点 poll-reviews + 序列化暴露 review_status;前端基础资产趴每8s轮询、人物卡渲染 审核✓(绿)/审核✗·重生(红)/审核中,审核终态后停轮询。
- e2e 已验:送审→建组→processing→active(真拿到火山绿盾)。

对抗式交叉验证(2审查员,0 critical)修复:
- get_or_create_team_group 并发竞态:DB 唯一约束去重 + select_for_update 串行化远程建组,杜绝重复建组/丢送审。
- create_asset 返回空 Id 不再标 processing(否则卡死黄);processing 超 15 分钟兜底判 failed,未知 Status 记日志,防永久 processing + 无限轮询。

凭证暂借 AirDrama,.env 改 ASSETS_API_* 两行即可换;ASSETS_API_ENABLED=false 可一键关停。回归 18/18 过。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-17 10:51:23 +08:00

90 lines
2.4 KiB
Python

from django.conf import settings
from rest_framework import serializers
from .models import Asset, AssetFile
from .storage import TosStorage
_tos_storage = None
def _tos():
global _tos_storage
if _tos_storage is None:
_tos_storage = TosStorage()
return _tos_storage
class AssetFileSerializer(serializers.ModelSerializer):
preview_url = serializers.SerializerMethodField()
class Meta:
model = AssetFile
fields = [
"id",
"object_key",
"bucket",
"content_type",
"size_bytes",
"width",
"height",
"duration_ms",
"preview_url",
"is_primary",
]
read_only_fields = [
"id",
"object_key",
"bucket",
"content_type",
"size_bytes",
"width",
"height",
"duration_ms",
"is_primary",
]
def get_preview_url(self, obj):
# 存储字段优先(如外部已写入绝对 URL);否则用 object_key 实时签发 TOS 预签名 GET URL
if obj.preview_url:
return obj.preview_url
if not obj.object_key or not settings.TOS.get("endpoint"):
return ""
try:
return _tos().presigned_get_url(object_key=obj.object_key)
except Exception:
return ""
class AssetSerializer(serializers.ModelSerializer):
files = AssetFileSerializer(many=True, read_only=True)
class Meta:
model = Asset
fields = [
"id",
"name",
"asset_type",
"source",
"category",
"description",
"metadata",
"is_deleted",
"origin_task",
"files",
"review_status",
"review_error",
"created_at",
"updated_at",
]
read_only_fields = ["id", "review_status", "review_error", "created_at", "updated_at"]
class AssetUploadSerializer(serializers.Serializer):
file = serializers.FileField()
name = serializers.CharField(max_length=255, required=False, allow_blank=True)
asset_type = serializers.ChoiceField(choices=Asset.Type.choices)
category = serializers.ChoiceField(choices=Asset.Category.choices, default=Asset.Category.UPLOAD)
description = serializers.CharField(required=False, allow_blank=True)