All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 1m37s
消息中心:全量渲染 → 真·后端分页滚动加载 - backend(ops/views): NotificationPagination(10/页,page_size 可覆盖)+ 响应回 type_counts(按收件人绝对计数,不受分页/搜索影响) - frontend(messages): 自管分页,滚到底加载下一批;tab/搜索走服务端并重置到第1页; 代号作废在途旧请求防切换卡空白;乐观标已读;「已加载 X / Y」分母用当前筛选总数 - api/App/types: listNotifications 支持 page/page_size/search;allNotifications 携带 type_counts 命令面板(侧边栏搜索):修复点开后 UI 错位 - app-shell: 遮罩 className 漏了基类 shell-command-bg(只有 .show)致无定位塌到左下; 补回基类 + header 类名对齐 .shell-command-h - messages-page.css: 工作台收进视口高度,收件箱在面板内滚动 本次提交一并带入此前若干未提交 WIP(account/ai-tools/library/pipeline/products/settings + accounts/ai/assets/billing/projects 后端),按用户要求整体推 dev。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
88 lines
2.3 KiB
Python
88 lines
2.3 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",
|
|
"created_at",
|
|
"updated_at",
|
|
]
|
|
read_only_fields = ["id", "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)
|
|
|