perf(core): 项目列表改轻量序列化 + 计数走真实 count,根治刷新 2-3s

/api/projects/ 列表此前与详情共用重序列化器(嵌套 阶段/片段/故事板/时间线 + 每资产算 url),
20 个项目把全套关联拉出 → 实测 ~2-3s,正是每次刷新慢的主因。

- 新增 ProjectListSerializer(轻:名/状态/阶段/product/封面/脚本数/镜数);
  ProjectViewSet.get_serializer_class 列表用轻的、详情用全量;
  get_queryset 列表走轻查询(select_related product + annotate 计数,不做那串 12 个重 prefetch)。
  实测 /api/projects/ 2-3s → 0.48s。
- 新增 /api/projects/summary/(total + by_status,只跑 COUNT,不拉项目对象)。
- 计数显示(侧栏徽标 / 仪表盘 总项目·SKU·N 个)改用分页响应的真实 count(productTotal/projectTotal),
  不再用「已加载 20 条」的长度;projects 列表页脚本状态/镜数改读 *_count 字段。

闸:9 页全绿、无瀑布、API 全过;projects 端点提速 ~5x。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
zyc
2026-06-17 19:27:10 +08:00
co-authored by Claude Opus 4.8
parent c62abed536
commit a0ed8666df
7 changed files with 76 additions and 11 deletions
+7 -3
View File
@@ -101,7 +101,9 @@ export function App() {
const [user, setUser] = useState<User | null>(null);
const [team, setTeam] = useState<Team | null>(null);
const [products, setProducts] = useState<Product[]>([]);
const [productTotal, setProductTotal] = useState(0); // 后端真实总数(分页 count),侧栏/仪表盘徽标用
const [projects, setProjects] = useState<Project[]>([]);
const [projectTotal, setProjectTotal] = useState(0);
const [modelConfigs, setModelConfigs] = useState<ModelConfig[]>([]);
const [billing, setBilling] = useState<BillingSummary | null>(null);
const [unreadCount, setUnreadCount] = useState(0);
@@ -144,7 +146,9 @@ export function App() {
api.notificationsBadge().catch(() => null)
]);
setProducts(productData.results);
setProductTotal(productData.count ?? productData.results.length);
setProjects(projectData.results);
setProjectTotal(projectData.count ?? projectData.results.length);
setModelConfigs(modelData?.results || []);
if (billingData) setBilling(billingData);
if (badgeData) setUnreadCount(badgeData.unread_count);
@@ -543,7 +547,7 @@ export function App() {
function renderPage() {
switch (page) {
case "dashboard":
return <Dashboard products={products} projects={projects} billing={billing} userName={currentUser.username} navigate={navigate} />;
return <Dashboard products={products} projects={projects} productTotal={productTotal} projectTotal={projectTotal} billing={billing} userName={currentUser.username} navigate={navigate} />;
case "products":
return (
<ProductsPage
@@ -684,7 +688,7 @@ export function App() {
case "settingsNotify":
return <SettingsPage user={currentUser} team={currentTeam} initialSection="notify" preferences={preferences} sessions={sessions} onSavePreferences={savePreferences} onRevokeSession={revokeSession} onRevokeOthers={revokeOtherSessions} onSaveProfile={saveProfile} onChangePassword={changeOwnPassword} onUploadAvatar={uploadOwnAvatar} onResetAvatar={resetOwnAvatar} onNotify={(text) => setNotice({ type: "success", text })} onLogout={logout} />;
default:
return <Dashboard products={products} projects={projects} billing={billing} navigate={navigate} />;
return <Dashboard products={products} projects={projects} productTotal={productTotal} projectTotal={projectTotal} billing={billing} navigate={navigate} />;
}
}
@@ -809,7 +813,7 @@ export function App() {
return (
<div className="app">
<Sidebar page={page} navigate={navigate} user={currentUser} team={currentTeam} products={products} projects={projects} />
<Sidebar page={page} navigate={navigate} user={currentUser} team={currentTeam} products={products} projects={projects} productTotal={productTotal} projectTotal={projectTotal} />
<main>
<Decorations />
<header className="topbar">