6d93c2b9b8
主要变更概述: ================ 1. 清理未使用的函数参数 (TypeScript noUnusedParameters) ------------------------------------------------------ - AppShell.tsx: 移除未使用的 backendHealth prop 及 ServerConnectionHealth 导入 - canvasUtils.ts: 移除 resolveWorkflowVideoModel 的 workflowModel 参数 - canvasWorkflowDeserialize.ts: 同步更新调用方 - CanvasPage.tsx: 移除 resolveWorkflowVideoModel 未使用导入 - HomePage.tsx: 移除 onOpenTokenMonitor、onOpenImageTool 未使用 props - ToolboxSection.tsx: 移除 onOpenImageTool 未使用 prop 及 WebImageWorkbenchTool 类型导入 - ScriptTokensPage.tsx: 移除 formatReportMarkdown 的 script 参数,更新 2 处调用 - TokenUsagePage.tsx: 移除 onOpenImageTool、onSelectView 未使用 props - WorkbenchPage.tsx: 移除 renderComposerToolbar 的 showStop 参数,更新 2 处调用 2. 移除未使用的模块和死代码 -------------------------- 删除以下未在电商模块中使用的功能模块: - 画布模块 (canvas/): CanvasPage, canvasUtils, canvasWorkflow* 等 - 主页模块 (home/): HomePage, ToolboxSection, WelcomeSplash 等 - 工作台模块 (workbench/): WorkbenchPage, ConversationSidebar 等 - 社区模块 (community/, community-review/) - 数字人模块 (digital-human/) - 图片工作台 (image-workbench/) - 其他独立工具页: agent, assets, beta-applications, character-mix, compliance, dialog-generator, more, profile, provider-health, report, resolution-upscale, script-tokens, settings, size-template, subtitle-removal, watermark-removal 3. 移除未使用的公共组件 ---------------------- - AnimatedPanel, BeforeAfterCompare, BetaApplicationModal - CookieConsentBanner, DropZone, EmptyState, NotFoundPage - NotificationCenter, OnboardingTour, OptimizedImage - PageTransition, RechargeModal, ShellIcon, Skeleton - StudioToolLayout, TaskStatusBar, WorkspacePageShell 4. 移除未使用的 API 客户端 -------------------------- - betaApplicationClient, communityClient, conversationClient - draftClient, modelCapabilitiesClient, notificationClient - projectTaskClient, providerHealthClient, publicConfigClient - referenceUploadService, reportClient, scriptEvalClient - uploadWithProgress 5. 移除未使用的工具函数和 hooks ------------------------------- - utils/: imageModelVisibility, mentionTrigger, modelOptions, ossImageOptimize, toolPageUtils - hooks/: useGenerationStatus, useScrollEntrance - scripts/: 所有分析脚本 (check-governance, dynamic-analysis 等) 6. 移除未使用的样式文件 ---------------------- 删除与已移除模块对应的 CSS 文件,保留电商模块专用样式 7. 新增电商模块功能文件 ---------------------- + src/api/generationRecordClient.ts (生成记录客户端) + src/features/ecommerce/ecommerceGenerationPersistence.ts (生成持久化) 验证: - TypeScript 编译 (tsc --noEmit --noUnusedParameters) 零错误通过 - 所有保留文件的功能完整性未受影响
177 lines
5.4 KiB
TypeScript
177 lines
5.4 KiB
TypeScript
import { useEffect, useMemo, useRef, useCallback } from "react";
|
|
import { useShallow } from "zustand/react/shallow";
|
|
import type { GenerationQueueItem } from "../stores/useGenerationStore";
|
|
import { useGenerationStore } from "../stores/useGenerationStore";
|
|
import { saveGenerationRecord, type GenerationRecordStatus } from "../api/generationRecordClient";
|
|
import {
|
|
startBackgroundPolling,
|
|
subscribeToTaskUpdates,
|
|
} from "../services/backgroundTaskRunner";
|
|
|
|
interface UseGenerationTasksOptions {
|
|
sourceView: string;
|
|
autoResume?: boolean;
|
|
}
|
|
|
|
type TerminalGenerationRecordStatus = Extract<GenerationRecordStatus, GenerationQueueItem["status"]>;
|
|
|
|
function isTerminalStatus(status: GenerationQueueItem["status"] | undefined): status is TerminalGenerationRecordStatus {
|
|
return status === "completed" || status === "failed" || status === "cancelled";
|
|
}
|
|
|
|
function persistUnifiedTaskRecord(task: GenerationQueueItem): void {
|
|
if (!isTerminalStatus(task.status)) return;
|
|
void saveGenerationRecord({
|
|
clientRecordId: task.id,
|
|
tool: task.sourceView,
|
|
mode: task.type,
|
|
title: task.title,
|
|
status: task.status,
|
|
prompt: task.prompt,
|
|
taskIds: task.taskId ? [task.taskId] : [],
|
|
assets: task.resultUrl
|
|
? [{
|
|
role: "result",
|
|
mediaType: task.type === "video" || task.type === "ecommerce-video" ? "video" : "image",
|
|
url: task.resultUrl,
|
|
taskId: task.taskId,
|
|
scope: `${task.sourceView}/result/${task.type}`,
|
|
}]
|
|
: [],
|
|
config: task.params,
|
|
result: {
|
|
resultUrl: task.resultUrl,
|
|
error: task.error,
|
|
progress: task.progress,
|
|
},
|
|
metadata: {
|
|
queueCreatedAt: task.createdAt,
|
|
source: "generation-queue",
|
|
},
|
|
createdAt: new Date(task.createdAt).toISOString(),
|
|
});
|
|
}
|
|
|
|
export function useGenerationTasks(options: UseGenerationTasksOptions) {
|
|
const { sourceView, autoResume = true } = options;
|
|
const {
|
|
queue,
|
|
addTask,
|
|
updateTask: updateStoredTask,
|
|
getRunningTasks,
|
|
} = useGenerationStore(useShallow((s) => ({
|
|
queue: s.queue,
|
|
addTask: s.addTask,
|
|
updateTask: s.updateTask,
|
|
getRunningTasks: s.getRunningTasks,
|
|
})));
|
|
const pollingStartedRef = useRef(false);
|
|
|
|
// ── Auto-resume: re-subscribe to running tasks on mount ────
|
|
useEffect(() => {
|
|
if (!autoResume || pollingStartedRef.current) return;
|
|
pollingStartedRef.current = true;
|
|
|
|
const active = getRunningTasks().filter((t) => t.sourceView === sourceView);
|
|
if (active.length > 0) {
|
|
startBackgroundPolling();
|
|
}
|
|
|
|
return () => {
|
|
pollingStartedRef.current = false;
|
|
};
|
|
}, [autoResume, sourceView, getRunningTasks]);
|
|
|
|
// ── Subscribe to live updates ───────────────────────────
|
|
useEffect(() => {
|
|
return subscribeToTaskUpdates((updated) => {
|
|
updateStoredTask(updated.id, updated);
|
|
});
|
|
}, [updateStoredTask]);
|
|
|
|
// ── View-scoped computed lists ──────────────────────────
|
|
const myTasks = useMemo(
|
|
() => queue.filter((t) => t.sourceView === sourceView),
|
|
[queue, sourceView],
|
|
);
|
|
|
|
const activeTasks = useMemo(
|
|
() => myTasks.filter((t) => t.status === "running" || t.status === "pending"),
|
|
[myTasks],
|
|
);
|
|
|
|
const completedTasks = useMemo(
|
|
() => myTasks.filter((t) => t.status === "completed"),
|
|
[myTasks],
|
|
);
|
|
|
|
const failedTasks = useMemo(
|
|
() => myTasks.filter((t) => t.status === "failed"),
|
|
[myTasks],
|
|
);
|
|
|
|
// ── Actions ─────────────────────────────────────────────
|
|
const submitTask = useCallback(
|
|
(task: Omit<GenerationQueueItem, "id" | "createdAt">) => {
|
|
const id = `gen-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
addTask({ ...task, id, createdAt: Date.now() });
|
|
return id;
|
|
},
|
|
[addTask],
|
|
);
|
|
|
|
const updateTask = useCallback(
|
|
(id: string, patch: Partial<GenerationQueueItem>) => {
|
|
if (isTerminalStatus(patch.status)) {
|
|
const current = queue.find((task) => task.id === id);
|
|
if (current) persistUnifiedTaskRecord({ ...current, ...patch });
|
|
}
|
|
updateStoredTask(id, patch);
|
|
},
|
|
[queue, updateStoredTask],
|
|
);
|
|
|
|
const markCompleted = useCallback(
|
|
(id: string, resultUrl: string) => {
|
|
const current = queue.find((task) => task.id === id);
|
|
const patch: Partial<GenerationQueueItem> = { status: "completed", progress: 100, resultUrl };
|
|
if (current) persistUnifiedTaskRecord({ ...current, ...patch });
|
|
updateStoredTask(id, patch);
|
|
},
|
|
[queue, updateStoredTask],
|
|
);
|
|
|
|
const markFailed = useCallback(
|
|
(id: string, error: string) => {
|
|
const current = queue.find((task) => task.id === id);
|
|
const patch: Partial<GenerationQueueItem> = { status: "failed", error };
|
|
if (current) persistUnifiedTaskRecord({ ...current, ...patch });
|
|
updateStoredTask(id, patch);
|
|
},
|
|
[queue, updateStoredTask],
|
|
);
|
|
|
|
const retryTask = useCallback(
|
|
(id: string) => {
|
|
const task = queue.find((t) => t.id === id);
|
|
if (task) {
|
|
updateStoredTask(id, { status: "pending", progress: 0, error: null });
|
|
}
|
|
},
|
|
[queue, updateStoredTask],
|
|
);
|
|
|
|
return {
|
|
tasks: myTasks,
|
|
activeTasks,
|
|
completedTasks,
|
|
failedTasks,
|
|
submitTask,
|
|
updateTask,
|
|
markCompleted,
|
|
markFailed,
|
|
retryTask,
|
|
hasActiveTasks: activeTasks.length > 0,
|
|
};
|
|
}
|