diff --git a/core/backend/apps/projects/views.py b/core/backend/apps/projects/views.py index 0942446..561294d 100644 --- a/core/backend/apps/projects/views.py +++ b/core/backend/apps/projects/views.py @@ -43,6 +43,8 @@ from .models import ( ProjectStage, ScriptSegment, ScriptVersion, + StoryboardFrame, + StoryboardVersion, SubtitleTrack, Timeline, TimelineClip, @@ -429,6 +431,15 @@ class ProjectViewSet(TeamScopedViewSetMixin, ModelViewSet): script = segment.script_version if script.segments.count() <= 1: return Response({"detail": "至少保留一个分镜"}, status=status.HTTP_400_BAD_REQUEST) + # 删本场脚本时,它对应的故事板分镜帧也一起删 —— frame.script_segment 是 SET_NULL, + # 不主动删会留下孤儿帧,故事板里仍显示这一(已删的)场。各版本受影响的帧统一收口后重排 sort_order。 + affected_storyboards = list(StoryboardVersion.objects.filter(frames__script_segment=segment).distinct()) + StoryboardFrame.objects.filter(script_segment=segment).delete() + for sb in affected_storyboards: + for index, frame in enumerate(sb.frames.order_by("sort_order")): + if frame.sort_order != index: + frame.sort_order = index + frame.save(update_fields=["sort_order", "updated_at"]) segment.delete() for index, seg in enumerate(script.segments.order_by("sort_order")): if seg.sort_order != index: diff --git a/core/frontend/src/components/actor-library.tsx b/core/frontend/src/components/actor-library.tsx index ed75727..0579860 100644 --- a/core/frontend/src/components/actor-library.tsx +++ b/core/frontend/src/components/actor-library.tsx @@ -1,6 +1,7 @@ -import { useEffect, useMemo, useRef, useState } from "react"; +import { useCallback, useEffect, useMemo, useRef, useState } from "react"; import { createPortal } from "react-dom"; import type { CSSProperties } from "react"; +import { api } from "../api"; import type { Asset } from "../types"; import { useBodyScrollLock, MediaLightbox } from "./overlays"; @@ -52,8 +53,22 @@ export function ActorLibrary({ open, mode, initialStudio, assets, onClose, onPic setCandidates([]); setPicked(null); setActorName(""); setTriReady(false); }, [open, initialStudio]); - // person 类资产 → 演员;按 preset / mine 分两 tab - const people = useMemo(() => assets.filter((a) => a.category === "person" && previewOf(a)), [assets]); + // 演员库自取 person 资产:全局 assets 已不再全量预载(按需懒加载),不能再依赖传入的 assets prop, + // 否则平台预设/我的演员全空。打开时拉一页(团队 person 资产),保存后 reload 让新人物即时入列。 + const [fetched, setFetched] = useState([]); + const reload = useCallback(async () => { + const res = await api.assetsPage({ category: "person", pageSize: 200, ordering: "-created_at" }).catch(() => null); + setFetched(res?.results ?? []); + }, []); + useEffect(() => { if (open) void reload(); }, [open, reload]); + + // person 类资产 → 演员;按 preset / mine 分两 tab。合并 prop(兼容旧调用)+ 自取,按 id 去重。 + const source = useMemo(() => { + const map = new Map(); + for (const a of [...assets, ...fetched]) map.set(a.id, a); + return [...map.values()]; + }, [assets, fetched]); + const people = useMemo(() => source.filter((a) => a.category === "person" && previewOf(a)), [source]); const list = people.filter((a) => (tab === "preset" ? isPreset(a) : !isPreset(a))); // 选某张候选立绘 → 进右侧栏(预填名字,清三视图标志) @@ -99,6 +114,7 @@ export function ActorLibrary({ open, mode, initialStudio, assets, onClose, onPic const name = actorName.trim(); if (name && name !== picked.name && onRename) await onRename(picked.id, name); onRefresh(); + void reload(); // 新保存的人物即时进「我的演员」列表(自取,不依赖父级全局 assets) setStudio(false); setCandidates([]); setPicked(null); setActorName(""); setTriReady(false); setTab("mine"); diff --git a/core/frontend/src/pipeline-page.css b/core/frontend/src/pipeline-page.css index 42c0356..aed6159 100644 --- a/core/frontend/src/pipeline-page.css +++ b/core/frontend/src/pipeline-page.css @@ -564,11 +564,7 @@ .draft-shot-input:focus { border-color: var(--heat); box-shadow: 0 0 0 2px var(--heat-12); } .draft-shot-input::placeholder { color: var(--black-alpha-32); } - /* ── 行38 · 资产卡可编辑提示词 + 重跑/替换 ── */ - .asset-prompt-edit { width: 100%; margin-top: 8px; font-family: var(--font-mono); font-size: 12px; line-height: 1.55; letter-spacing: .01em; color: var(--black-alpha-72); background: var(--background-base); border: 1px solid var(--border-faint); border-radius: var(--r-sm); padding: 8px 10px; outline: none; resize: vertical; min-height: 56px; transition: border-color var(--t-base), background var(--t-base), box-shadow var(--t-base); } - .asset-prompt-edit:hover { border-color: var(--heat-20); } - .asset-prompt-edit:focus { border-color: var(--heat); background: var(--surface); color: var(--accent-black); box-shadow: 0 0 0 3px var(--heat-12); } - .asset-prompt-edit::placeholder { color: var(--black-alpha-32); } + /* ── 行38 · 资产卡可编辑提示词 + 重跑/替换 ──(.asset-prompt-edit 移到顶层:演员库弹窗 portal 到 body,嵌在 .pipeline-page 内会失效) */ .asset-card-actions { display: flex; align-items: center; gap: 8px; margin-top: 12px; } .asset-card-actions .btn { display: inline-flex; align-items: center; } @@ -680,6 +676,11 @@ @keyframes edXfadeFlash { from { opacity: 0.72; } to { opacity: 0; } } /* ── 流程步骤4 · 演员库覆盖层(portal 到 body,顶层选择器才生效)── */ +/* 资产卡/演员库工作台的提示词输入框:顶层定义,页面内(资产卡)与 portal 弹窗(添加人物工作台)都生效 */ +.asset-prompt-edit { width: 100%; margin-top: 8px; font-family: var(--font-mono); font-size: 12px; line-height: 1.55; letter-spacing: .01em; color: var(--black-alpha-72); background: var(--background-base); border: 1px solid var(--border-faint); border-radius: var(--r-sm); padding: 8px 10px; outline: none; resize: vertical; min-height: 56px; transition: border-color var(--t-base), background var(--t-base), box-shadow var(--t-base); } +.asset-prompt-edit:hover { border-color: var(--heat-20); } +.asset-prompt-edit:focus { border-color: var(--heat); background: var(--surface); color: var(--accent-black); box-shadow: 0 0 0 3px var(--heat-12); } +.asset-prompt-edit::placeholder { color: var(--black-alpha-32); } .actorlib-bg { position: fixed; inset: 0; background: rgba(0,0,0,.42); z-index: 1020; display: flex; align-items: center; justify-content: center; padding: 40px; } .actorlib { background: var(--surface); border: 1px solid var(--border-faint); border-radius: var(--r-md); width: min(920px, 100%); max-height: calc(100vh - 80px); overflow: hidden; display: flex; flex-direction: column; box-shadow: 0 16px 48px rgba(0,0,0,.18); } .actorlib-h { display: flex; align-items: center; gap: 10px; padding: 14px 20px; border-bottom: 1px solid var(--border-faint); } diff --git a/core/frontend/src/routes/pipeline.tsx b/core/frontend/src/routes/pipeline.tsx index 2691a8d..93c69d0 100644 --- a/core/frontend/src/routes/pipeline.tsx +++ b/core/frontend/src/routes/pipeline.tsx @@ -270,10 +270,10 @@ function AddTagInline({ onAdd, placeholder, ariaLabel }: { onAdd?: (value: strin // DOM,React 不再 reconcile 其子节点。受控写法(把 state 当 children 回灌)在输入触发换行时,浏览器会 // 往 contentEditable 里插
/
,React 拿单个文本子节点去对账就会 removeChild 崩溃 → 整页白屏。 // 读取走 onInput 写回上层 state(供「生成/重跑」带上),与渲染解耦,既能编辑又不崩。 -function PromptBox({ value, onChange }: { value: string; onChange: (v: string) => void }) { +function PromptBox({ value, onChange, className = "prompt-box", id, ariaLabel, stop = true }: { value: string; onChange: (v: string) => void; className?: string; id?: string; ariaLabel?: string; stop?: boolean }) { const ref = useRef(null); // 只挂载时灌初值;后续用户编辑由 DOM 自己维护,不受 value 变化驱动(避免重渲染打断输入/白屏)。 - // 切换实体/草稿时上层用 key 强制重挂载,会重新走这次初值灌入,所以不需要把 value 进依赖。 + // 切换实体/草稿/版本时上层用 key 强制重挂载,会重新走这次初值灌入,所以不需要把 value 进依赖。 useEffect(() => { if (ref.current && ref.current.textContent !== value) ref.current.textContent = value; // eslint-disable-next-line react-hooks/exhaustive-deps @@ -281,16 +281,50 @@ function PromptBox({ value, onChange }: { value: string; onChange: (v: string) = return (
onChange(e.currentTarget.textContent || "")} /> ); } +// 分镜「旁白 / 画面」可编辑行:同样**非受控** contentEditable,落库走 onBlur(回车=失焦提交)。 +// 受控写法(把 narration 当 children)在提交后父组件用旧值重渲染时,React 会把 DOM 文本重置回旧值 → +// 「按回车后变成空白」。这里不放受控子节点:初值/外部改动(切版本、AI 改稿)由 effect 同步进 DOM, +// 但**正在编辑(聚焦中)时绝不覆盖**,避免打断输入。 +function EditableShotField({ value, placeholder, onCommit }: { value: string; placeholder: string; onCommit: (text: string) => void }) { + const ref = useRef(null); + useEffect(() => { + const el = ref.current; + if (!el || document.activeElement === el) return; // 聚焦中=用户在打字,别覆盖 + if (el.textContent !== value) el.textContent = value; + }, [value]); + return ( +
{ e.currentTarget.removeAttribute("data-empty"); }} + onKeyDown={(e) => { if (e.key === "Enter" && !e.shiftKey && !e.nativeEvent.isComposing) { e.preventDefault(); e.currentTarget.blur(); } }} + onBlur={(e) => { + const text = (e.currentTarget.textContent || "").trim(); + if (!text) e.currentTarget.setAttribute("data-empty", "true"); + if (text !== value) onCommit(text); + }} + /> + ); +} + // 行34 · 「添加分镜」插入的本地可编辑空白卡片:输入旁白/画面,提交后落库 function DraftShotCard({ draft, onCommit, onCancel }: { draft: { id: string; afterId: string | null; narration: string; visual: string }; @@ -2071,39 +2105,19 @@ export function PipelinePage(props: {
旁白 -
{ event.currentTarget.removeAttribute("data-empty"); }} - onKeyDown={(event) => { if (event.key === "Enter" && !event.shiftKey && !event.nativeEvent.isComposing) { event.preventDefault(); event.currentTarget.blur(); } }} - onBlur={(event) => { - const text = (event.currentTarget.textContent || "").trim(); - if (!text) event.currentTarget.setAttribute("data-empty", "true"); - if (text !== narration) void onUpdateShot({ segment_id: shot.id, narration: text }); - }} - >{narration}
+ { void onUpdateShot({ segment_id: shot.id, narration: text }); }} + />
画面 -
{ event.currentTarget.removeAttribute("data-empty"); }} - onKeyDown={(event) => { if (event.key === "Enter" && !event.shiftKey && !event.nativeEvent.isComposing) { event.preventDefault(); event.currentTarget.blur(); } }} - onBlur={(event) => { - const text = (event.currentTarget.textContent || "").trim(); - if (!text) event.currentTarget.setAttribute("data-empty", "true"); - if (text !== visualShown) void onUpdateShot({ segment_id: shot.id, visual_prompt: text }); - }} - >{visualShown}
+ { void onUpdateShot({ segment_id: shot.id, visual_prompt: text }); }} + />
{/* ScriptDraft 结构化字段:镜型(钩子/痛点/卖点/CTA)+ 商品露出方式(只读展示) */} {(shot.role || shot.product_exposure) ? ( @@ -2298,7 +2312,13 @@ export function PipelinePage(props: { const previewTriAsset = (triPreviewId && productVersions.includes(triPreviewId)) ? triPreviewId : adoptedTriAsset; const hasTriView = productVersions.length > 0; const triPanelShow = triPanelOpen || hasTriView || triGenerating; - const productAssetUrl = assetUrl(productCover); // 商品卡显示商品主图,三视图在右侧面板 + // 商品主图:优先用序列化器内嵌的 preview_url(全局 assets 已不再全量取,assetUrl 反查会落空), + // 依次 封面 → 主图 → 首图,最后才回退旧的全局反查。 + const productAssetUrl = + productRecord?.cover_preview_url + || productRecord?.images?.find((img) => img.is_primary)?.preview_url + || productRecord?.images?.[0]?.preview_url + || assetUrl(productCover); // 商品卡显示商品主图,三视图在右侧面板 const triViewGen = `${productName} 商品三视图,从左到右:正面 / 侧面 / 背面,统一光照,白色背景,16:9`; const runProductTri = () => { setTriPanelOpen(true); setTriPreviewId(null); void genBaseAsset("product", triViewGen, undefined, "tri:product"); }; return ( @@ -2522,23 +2542,8 @@ export function PipelinePage(props: {
); })} - {/* 人物区末尾「添加人物」卡:点开「添加人物工作台」(studio) */} - {kind === "person" && ( -
setActorLib({ mode: "browse", studio: true })} - onKeyDown={(e) => { if (e.key === "Enter" || e.key === " ") { e.preventDefault(); setActorLib({ mode: "browse", studio: true }); } }}> -
- - 添加人物 -
-
-
添加人物工作台
-
// AI 生成 / 本地上传 → 三视图 → 入库
-
-
- )} - {entities.length === 0 && pendingTags.length === 0 && (kind !== "scene" || sceneDrafts.length === 0) && kind !== "person" && ( + {/* 添加人物入口统一走右上「+ 新增人物」按钮(原末尾常驻卡与之重复,已移除) */} + {entities.length === 0 && pendingTags.length === 0 && (kind !== "scene" || sceneDrafts.length === 0) && (
// 暂无{KIND_LABEL[kind]}资产 · 点右上「新增{KIND_LABEL[kind]}」
@@ -2616,16 +2621,15 @@ export function PipelinePage(props: {
仅支持整张重跑 · 不能局部改某一镜。如需调单镜,先在 { event.preventDefault(); goStage(1); }}>Stage 1 脚本 改镜头描述,再回此处整张重跑。
// 整张提示词(重跑时生效,可编辑)
- {/* key 跟版本走:切版本重新种子;onInput 实时回写 state → 整张重跑用的就是你编辑后的文本 */} -
setStoryboardPrompt((event.currentTarget.textContent || "").trim())} - >{displayedStoryboard?.prompt || SB_PROMPT_DEFAULT}
+ className="prompt-edit" + id="sb-prompt-edit" + stop={false} + value={displayedStoryboard?.prompt || SB_PROMPT_DEFAULT} + onChange={(v) => setStoryboardPrompt(v.trim())} + />
@@ -3177,16 +3190,14 @@ export function PipelinePage(props: { // 本场提示词(重跑生效,可编辑) 系统会自动织入本镜旁白与参考图 -
setVdPrompt((event.currentTarget.textContent || "").trim())} - >{`${videoPrompt} 第 ${vdSeg.sort_order + 1} 段,时长 ${vdSeg.target_duration_seconds} 秒`}
+ className="vd-prompt-edit" + ariaLabel="视频提示词" + stop={false} + value={`${videoPrompt} 第 ${vdSeg.sort_order + 1} 段,时长 ${vdSeg.target_duration_seconds} 秒`} + onChange={(v) => setVdPrompt(v.trim())} + /> diff --git a/core/qa/function-audit/output/perf-report.json b/core/qa/function-audit/output/perf-report.json index a7c514f..b6f56cc 100644 --- a/core/qa/function-audit/output/perf-report.json +++ b/core/qa/function-audit/output/perf-report.json @@ -1,11 +1,11 @@ { - "ts": "2026-06-17T10:24:13.586Z", + "ts": "2026-06-17T10:57:57.302Z", "base": "http://127.0.0.1:5174", "apiChecks": [ { "name": "page_size 生效 /api/assets/", "pass": true, - "detail": "count=227 返回=200 next=有" + "detail": "count=239 返回=200 next=有" }, { "name": "page_size 生效 /api/products/", @@ -15,31 +15,31 @@ { "name": "page_size 生效 /api/projects/", "pass": true, - "detail": "count=19 返回=19 next=无" + "detail": "count=21 返回=21 next=无" }, { "name": "延迟 /api/assets/?page_size=200", - "pass": false, + "pass": true, "soft": true, - "detail": "2235ms / 参考 1200ms (HTTP 200)" + "detail": "720ms / 参考 1200ms (HTTP 200)" }, { "name": "延迟 /api/products/?page_size=200", "pass": true, "soft": true, - "detail": "962ms / 参考 1200ms (HTTP 200)" + "detail": "597ms / 参考 1200ms (HTTP 200)" }, { "name": "延迟 /api/projects/?page_size=200", "pass": false, "soft": true, - "detail": "2703ms / 参考 1200ms (HTTP 200)" + "detail": "2004ms / 参考 1200ms (HTTP 200)" }, { "name": "延迟 /api/ops/notifications/?page_size=100", "pass": false, "soft": true, - "detail": "1959ms / 参考 1200ms (HTTP 200)" + "detail": "1228ms / 参考 1200ms (HTTP 200)" } ], "pageFailed": 0, @@ -47,9 +47,9 @@ { "name": "dashboard", "route": "http://127.0.0.1:5174/dashboard", - "unique": 9, + "unique": 6, "budget": 14, - "rawRequests": 11, + "rawRequests": 7, "waterfall": 0, "duplicates": [], "failed": [], @@ -58,15 +58,11 @@ "GET /api/products/", "GET /api/projects/", "GET /api/billing/summary/", - "GET /api/billing/ledgers/?page=1&page_size=10", - "GET /api/billing/trend/", - "GET /api/auth/team/members/", "GET /api/ai/models/", - "GET /api/ai/tasks/", - "GET /api/ops/notifications/?page_size=100", + "GET /api/ops/notifications/?page_size=1", "GET /api/assets/summary/" ], - "elapsedMs": 6445, + "elapsedMs": 5557, "problems": [], "warnings": [], "pass": true @@ -74,9 +70,9 @@ { "name": "products", "route": "http://127.0.0.1:5174/products", - "unique": 8, + "unique": 5, "budget": 6, - "rawRequests": 10, + "rawRequests": 6, "waterfall": 0, "duplicates": [], "failed": [], @@ -85,26 +81,20 @@ "GET /api/products/", "GET /api/projects/", "GET /api/billing/summary/", - "GET /api/billing/ledgers/?page=1&page_size=10", - "GET /api/billing/trend/", - "GET /api/auth/team/members/", "GET /api/ai/models/", - "GET /api/ai/tasks/", - "GET /api/ops/notifications/?page_size=100" + "GET /api/ops/notifications/?page_size=1" ], - "elapsedMs": 3892, + "elapsedMs": 3488, "problems": [], - "warnings": [ - "首屏接口数 8 > 预算 6(架构性过量拉取,待决策)" - ], + "warnings": [], "pass": true }, { "name": "projects", "route": "http://127.0.0.1:5174/projects", - "unique": 8, + "unique": 5, "budget": 6, - "rawRequests": 10, + "rawRequests": 6, "waterfall": 0, "duplicates": [], "failed": [], @@ -113,26 +103,20 @@ "GET /api/products/", "GET /api/projects/", "GET /api/billing/summary/", - "GET /api/billing/ledgers/?page=1&page_size=10", - "GET /api/billing/trend/", - "GET /api/auth/team/members/", "GET /api/ai/models/", - "GET /api/ai/tasks/", - "GET /api/ops/notifications/?page_size=100" + "GET /api/ops/notifications/?page_size=1" ], - "elapsedMs": 3765, + "elapsedMs": 3502, "problems": [], - "warnings": [ - "首屏接口数 8 > 预算 6(架构性过量拉取,待决策)" - ], + "warnings": [], "pass": true }, { "name": "library", "route": "http://127.0.0.1:5174/library", - "unique": 11, + "unique": 8, "budget": 8, - "rawRequests": 14, + "rawRequests": 10, "waterfall": 0, "duplicates": [ { @@ -147,20 +131,14 @@ "GET /api/products/", "GET /api/projects/", "GET /api/billing/summary/", - "GET /api/billing/ledgers/?page=1&page_size=10", - "GET /api/billing/trend/", - "GET /api/auth/team/members/", "GET /api/ai/models/", - "GET /api/ai/tasks/", - "GET /api/ops/notifications/?page_size=100", + "GET /api/ops/notifications/?page_size=1", "GET /api/assets/summary/", "GET /api/assets/facets/?meta_keys=gender,age,role&tab=people" ], - "elapsedMs": 6012, + "elapsedMs": 5728, "problems": [], - "warnings": [ - "首屏接口数 11 > 预算 8(架构性过量拉取,待决策)" - ], + "warnings": [], "pass": true }, { @@ -168,28 +146,22 @@ "route": "http://127.0.0.1:5174/account", "unique": 8, "budget": 8, - "rawRequests": 11, + "rawRequests": 9, "waterfall": 0, - "duplicates": [ - { - "key": "GET /api/billing/ledgers/?page=1&page_size=10", - "n": 2 - } - ], + "duplicates": [], "failed": [], "breakdown": [ - "GET /api/billing/ledgers/?page=1&page_size=10 ×2", "GET /api/auth/me/", "GET /api/products/", "GET /api/projects/", "GET /api/billing/summary/", + "GET /api/ai/models/", + "GET /api/ops/notifications/?page_size=1", "GET /api/billing/trend/", "GET /api/auth/team/members/", - "GET /api/ai/models/", - "GET /api/ai/tasks/", - "GET /api/ops/notifications/?page_size=100" + "GET /api/billing/ledgers/?page=1&page_size=10" ], - "elapsedMs": 5753, + "elapsedMs": 5743, "problems": [], "warnings": [], "pass": true @@ -197,9 +169,9 @@ { "name": "team", "route": "http://127.0.0.1:5174/team", - "unique": 8, + "unique": 6, "budget": 4, - "rawRequests": 10, + "rawRequests": 8, "waterfall": 0, "duplicates": [], "failed": [], @@ -208,26 +180,24 @@ "GET /api/products/", "GET /api/projects/", "GET /api/billing/summary/", - "GET /api/billing/ledgers/?page=1&page_size=10", - "GET /api/billing/trend/", - "GET /api/auth/team/members/", "GET /api/ai/models/", - "GET /api/ai/tasks/", + "GET /api/ops/notifications/?page_size=1", + "GET /api/auth/team/members/", "GET /api/ops/notifications/?page_size=100" ], - "elapsedMs": 3555, + "elapsedMs": 5500, "problems": [], "warnings": [ - "首屏接口数 8 > 预算 4(架构性过量拉取,待决策)" + "首屏接口数 6 > 预算 4(架构性过量拉取,待决策)" ], "pass": true }, { "name": "messages", "route": "http://127.0.0.1:5174/messages", - "unique": 8, + "unique": 5, "budget": 4, - "rawRequests": 11, + "rawRequests": 7, "waterfall": 0, "duplicates": [], "failed": [], @@ -236,27 +206,23 @@ "GET /api/products/", "GET /api/projects/", "GET /api/billing/summary/", - "GET /api/billing/ledgers/?page=1&page_size=10", - "GET /api/billing/trend/", - "GET /api/auth/team/members/", "GET /api/ai/models/", - "GET /api/ai/tasks/", - "GET /api/ops/notifications/?page_size=100", + "GET /api/ops/notifications/?page_size=1", "GET /api/ops/notifications/?page=1&page_size=10" ], - "elapsedMs": 3468, + "elapsedMs": 3485, "problems": [], "warnings": [ - "首屏接口数 8 > 预算 4(架构性过量拉取,待决策)" + "首屏接口数 5 > 预算 4(架构性过量拉取,待决策)" ], "pass": true }, { "name": "productDetail", "route": "http://127.0.0.1:5174/products/045ad8d6-8b15-486b-a4a9-f6968eb1555f", - "unique": 9, + "unique": 6, "budget": 8, - "rawRequests": 11, + "rawRequests": 7, "waterfall": 0, "duplicates": [], "failed": [], @@ -265,27 +231,21 @@ "GET /api/products/", "GET /api/projects/", "GET /api/billing/summary/", - "GET /api/billing/ledgers/?page=1&page_size=10", - "GET /api/billing/trend/", - "GET /api/auth/team/members/", "GET /api/ai/models/", - "GET /api/ai/tasks/", - "GET /api/ops/notifications/?page_size=100", + "GET /api/ops/notifications/?page_size=1", "GET /api/assets/?page_size=200&product=045ad8d6-8b15-486b-a4a9-f6968eb1555f" ], - "elapsedMs": 5956, + "elapsedMs": 5755, "problems": [], - "warnings": [ - "首屏接口数 9 > 预算 8(架构性过量拉取,待决策)" - ], + "warnings": [], "pass": true }, { "name": "pipeline", "route": "http://127.0.0.1:5174/pipeline/1048451e-3b4a-4b66-a8f4-cb865096de2f", - "unique": 11, + "unique": 7, "budget": 10, - "rawRequests": 13, + "rawRequests": 8, "waterfall": 0, "duplicates": [], "failed": [], @@ -295,20 +255,13 @@ "GET /api/products/", "GET /api/projects/", "GET /api/billing/summary/", - "GET /api/billing/ledgers/?page=1&page_size=10", - "GET /api/billing/trend/", - "GET /api/auth/team/members/", "GET /api/ai/models/", - "GET /api/ai/tasks/", - "GET /api/ops/notifications/?page_size=100", - "POST /api/projects/1048451e-3b4a-4b66-a8f4-cb865096de2f/generate-base-asset/", + "GET /api/ops/notifications/?page_size=1", "GET /api/projects/1048451e-3b4a-4b66-a8f4-cb865096de2f/pending-assets/" ], - "elapsedMs": 5702, + "elapsedMs": 6015, "problems": [], - "warnings": [ - "首屏接口数 11 > 预算 10(架构性过量拉取,待决策)" - ], + "warnings": [], "pass": true } ]