perf(core): 资产/通知「取全部」改大页+并行,根治每次刷新串十几个请求
资产接口原固定 20/页且不支持 page_size,前端 allAssets 逐页串行翻完(190 资产=10 个 串行请求,每个 0.5~1.7s 依次等)→ 整页刷新好几秒。 - 后端:AssetViewSet 加 AssetPagination(page_size_query_param,上限 200) - 前端:allAssets/allNotifications 改「首页大页(200/100)+ 剩余页 Promise.all 并行」 资产 ≤200 时 1 个请求取全;再多也并行不串行。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -6,6 +6,7 @@ from django.db import transaction
|
||||
from django.http import StreamingHttpResponse
|
||||
from rest_framework import status
|
||||
from rest_framework.decorators import action
|
||||
from rest_framework.pagination import PageNumberPagination
|
||||
from rest_framework.parsers import FormParser, MultiPartParser
|
||||
from rest_framework.response import Response
|
||||
from rest_framework.views import APIView
|
||||
@@ -18,9 +19,18 @@ from .serializers import AssetSerializer, AssetUploadSerializer
|
||||
from .storage import TosStorage
|
||||
|
||||
|
||||
class AssetPagination(PageNumberPagination):
|
||||
"""允许前端用 ?page_size= 覆盖(默认 20,上限 200)。前端「取全部资产」一次大页拉完,
|
||||
不再逐页串行(原来 20/页 → 资产多时每次刷新要串十几个请求,整页很慢)。"""
|
||||
|
||||
page_size_query_param = "page_size"
|
||||
max_page_size = 200
|
||||
|
||||
|
||||
class AssetViewSet(TeamScopedViewSetMixin, ModelViewSet):
|
||||
queryset = Asset.objects.prefetch_related("files").all()
|
||||
serializer_class = AssetSerializer
|
||||
pagination_class = AssetPagination
|
||||
search_fields = ["name", "description"]
|
||||
ordering_fields = ["created_at", "updated_at", "name"]
|
||||
|
||||
|
||||
+31
-18
@@ -373,13 +373,21 @@ export const api = {
|
||||
// 跟随 DRF 分页 next 取全部资产 —— 商品图/AI 素材/资产库都靠 asset.id 在这份列表里查 preview_url,
|
||||
// 只取第 1 页(20 条)会让第 20 条之后的资产解析不到图、渲染成空占位。
|
||||
async allAssets(): Promise<Asset[]> {
|
||||
const out: Asset[] = [];
|
||||
let path = "/api/assets/";
|
||||
for (let guard = 0; guard < 50 && path; guard += 1) {
|
||||
const page: Paginated<Asset> = await request<Paginated<Asset>>(path);
|
||||
out.push(...page.results);
|
||||
path = page.next ? new URL(page.next).pathname + new URL(page.next).search : "";
|
||||
}
|
||||
// 一次大页(page_size=200)尽量取全;若还有多页,并行取剩余页(不再逐页串行等,根治整页刷新慢)
|
||||
const SIZE = 200;
|
||||
const first = await request<Paginated<Asset>>(`/api/assets/?page_size=${SIZE}`);
|
||||
const out: Asset[] = [...first.results];
|
||||
const pageSize = first.results.length || SIZE; // 后端若忽略 page_size,按实际页大小算页数,逻辑仍正确
|
||||
const totalPages = pageSize > 0 ? Math.ceil((first.count || out.length) / pageSize) : 1;
|
||||
if (totalPages <= 1) return out;
|
||||
const rest = await Promise.all(
|
||||
Array.from({ length: totalPages - 1 }, (_, i) =>
|
||||
request<Paginated<Asset>>(`/api/assets/?page_size=${SIZE}&page=${i + 2}`)
|
||||
.then((p) => p.results)
|
||||
.catch(() => [] as Asset[])
|
||||
)
|
||||
);
|
||||
rest.forEach((r) => out.push(...r));
|
||||
return out;
|
||||
},
|
||||
uploadAsset(formData: FormData) {
|
||||
@@ -423,18 +431,23 @@ export const api = {
|
||||
},
|
||||
// 跟着 next 把所有消息取全(给侧边栏徽标 + 团队动态用,不参与收件箱滚动渲染),与 allAssets 同套路
|
||||
async allNotifications(): Promise<NotificationList> {
|
||||
const out: Notification[] = [];
|
||||
let path = "/api/ops/notifications/?page_size=100";
|
||||
let unreadCount = 0;
|
||||
let typeCounts: NotificationList["type_counts"];
|
||||
for (let guard = 0; guard < 100 && path; guard += 1) {
|
||||
const page = await request<NotificationList>(path);
|
||||
out.push(...page.results);
|
||||
unreadCount = page.unread_count;
|
||||
typeCounts = page.type_counts ?? typeCounts;
|
||||
path = page.next ? new URL(page.next).pathname + new URL(page.next).search : "";
|
||||
// 首页拿计数,剩余页并行(原串行逐页:消息多时侧边栏徽标加载也拖慢整页)
|
||||
const SIZE = 100;
|
||||
const first = await request<NotificationList>(`/api/ops/notifications/?page_size=${SIZE}`);
|
||||
const out: Notification[] = [...first.results];
|
||||
const pageSize = first.results.length || SIZE;
|
||||
const totalPages = pageSize > 0 ? Math.ceil((first.count || out.length) / pageSize) : 1;
|
||||
if (totalPages > 1) {
|
||||
const rest = await Promise.all(
|
||||
Array.from({ length: totalPages - 1 }, (_, i) =>
|
||||
request<NotificationList>(`/api/ops/notifications/?page_size=${SIZE}&page=${i + 2}`)
|
||||
.then((p) => p.results)
|
||||
.catch(() => [] as Notification[])
|
||||
)
|
||||
);
|
||||
rest.forEach((r) => out.push(...r));
|
||||
}
|
||||
return { count: out.length, next: null, previous: null, results: out, unread_count: unreadCount, type_counts: typeCounts };
|
||||
return { count: out.length, next: null, previous: null, results: out, unread_count: first.unread_count, type_counts: first.type_counts };
|
||||
},
|
||||
markAllNotificationsRead() {
|
||||
return request<{ updated: number; unread_count: number }>("/api/ops/notifications/mark-all-read/", {
|
||||
|
||||
Reference in New Issue
Block a user