Files
yingqing/core/backend/apps/assets/serializers.py
T
zycandClaude Opus 4.8 8ba76e5bb7 perf(core): 资产预览改 TOS 公读直链 + 通知只取首页,减刷新开销与缓存失效
- 资产 preview_url 从「逐图签发预签名 URL」改为虚拟主机式公读直链
  (https://{bucket}.{host}/{key})。桶公读已验证免签可访问;直链稳定可被浏览器/CDN
  缓存,而签名链每次都变、1h 过期反而打不到缓存。boto3 也移出序列化热路径。
- allNotifications 不再逐页翻全部(原 O(N) 随消息增长拖慢每次刷新),只取首页 100 条
  (侧边栏徽标 unread_count + 团队动态仅展示最近 6 条已足够);「共 N」改用后端真实 count。
- perf-probe:page_size 由 API 级硬闸确定性守住后,浏览器端「资产翻页」降为提示
  (翻 1~2 页是资产真超 200 的合法分页,仅 ≥3 页才疑似 page_size 失效)。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-17 16:58:48 +08:00

108 lines
3.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 公读直链。
# TOS 桶公读,直链(虚拟主机式)免签名、且稳定可缓存——不再逐图签发预签名 URL
# (签名是纯本地计算但每次都变、1h 过期会打不到浏览器/CDN 缓存;直链一劳永逸)。
if obj.preview_url:
return obj.preview_url
if not obj.object_key or not settings.TOS.get("endpoint"):
return ""
try:
return _tos().public_url(object_key=obj.object_key, bucket=obj.bucket or None)
except Exception:
return ""
class AssetSerializer(serializers.ModelSerializer):
files = AssetFileSerializer(many=True, read_only=True)
# 资产归属商品(只读):商品详情页据此只展示「该商品」的 AI 素材,而非全团队
product = serializers.SerializerMethodField()
def get_product(self, obj):
"""解析资产所属商品:
1) 独立生图(图片创作/模特图/平台套图):生图时已写入 metadata.product_id;
2) 项目内生成(基础资产/分镜图等):回溯 origin_task → project → product。
两条路都拿不到则归属 None(如纯手动上传到素材库、与任何商品无关的图)。"""
pid = (obj.metadata or {}).get("product_id")
if pid:
return str(pid)
task = obj.origin_task
if task and task.project_id and task.project.product_id:
return str(task.project.product_id)
return None
class Meta:
model = Asset
fields = [
"id",
"name",
"asset_type",
"source",
"category",
"description",
"metadata",
"is_deleted",
"origin_task",
"product",
"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)