refactor: 清理未使用参数、移除死代码、聚焦电商核心模块
主要变更概述: ================ 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) 零错误通过 - 所有保留文件的功能完整性未受影响
This commit is contained in:
@@ -0,0 +1,131 @@
|
||||
import { isOptionalApiRouteMissing } from "./apiErrorUtils";
|
||||
import { serverRequest } from "./serverConnection";
|
||||
|
||||
const PENDING_RECORDS_KEY = "omniai:generation-records.pending";
|
||||
const MAX_PENDING_RECORDS = 80;
|
||||
|
||||
export type GenerationRecordStatus = "queued" | "running" | "completed" | "failed" | "cancelled";
|
||||
|
||||
export interface GenerationRecordAsset {
|
||||
role: "source" | "reference" | "intermediate" | "result" | "thumbnail";
|
||||
mediaType: "image" | "video" | "text" | "asset" | string;
|
||||
url: string;
|
||||
ossKey?: string | null;
|
||||
scope?: string;
|
||||
label?: string;
|
||||
taskId?: string | null;
|
||||
metadata?: Record<string, unknown>;
|
||||
}
|
||||
|
||||
export interface SaveGenerationRecordInput {
|
||||
clientRecordId: string;
|
||||
tool: string;
|
||||
mode?: string;
|
||||
title: string;
|
||||
status: GenerationRecordStatus;
|
||||
prompt?: string;
|
||||
taskIds?: string[];
|
||||
assets?: GenerationRecordAsset[];
|
||||
config?: Record<string, unknown>;
|
||||
result?: Record<string, unknown>;
|
||||
metadata?: Record<string, unknown>;
|
||||
createdAt?: string;
|
||||
updatedAt?: string;
|
||||
}
|
||||
|
||||
export interface SaveGenerationRecordResult {
|
||||
source: "server" | "local";
|
||||
id: string;
|
||||
}
|
||||
|
||||
function readPendingRecords(): SaveGenerationRecordInput[] {
|
||||
try {
|
||||
const raw = window.localStorage.getItem(PENDING_RECORDS_KEY);
|
||||
if (!raw) return [];
|
||||
const parsed = JSON.parse(raw);
|
||||
return Array.isArray(parsed) ? parsed.filter((item): item is SaveGenerationRecordInput => Boolean(item?.clientRecordId)) : [];
|
||||
} catch {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
function writePendingRecord(input: SaveGenerationRecordInput): void {
|
||||
try {
|
||||
const records = readPendingRecords();
|
||||
const next = [input, ...records.filter((item) => item.clientRecordId !== input.clientRecordId)].slice(0, MAX_PENDING_RECORDS);
|
||||
window.localStorage.setItem(PENDING_RECORDS_KEY, JSON.stringify(next));
|
||||
} catch {
|
||||
// Ignore storage quota failures; generation itself must not be blocked by history persistence.
|
||||
}
|
||||
}
|
||||
|
||||
export async function saveGenerationRecord(input: SaveGenerationRecordInput): Promise<SaveGenerationRecordResult> {
|
||||
try {
|
||||
const response = await serverRequest<{ id?: string | number }>("ai/generation-records", {
|
||||
method: "POST",
|
||||
body: input,
|
||||
maxRetries: 0,
|
||||
fallbackMessage: "Failed to save generation record",
|
||||
});
|
||||
return { source: "server", id: String(response.id ?? input.clientRecordId) };
|
||||
} catch (error) {
|
||||
if (!isOptionalApiRouteMissing(error)) {
|
||||
// Keep a local recovery copy even when the route exists but the save fails.
|
||||
writePendingRecord(input);
|
||||
return { source: "local", id: input.clientRecordId };
|
||||
}
|
||||
writePendingRecord(input);
|
||||
return { source: "local", id: input.clientRecordId };
|
||||
}
|
||||
}
|
||||
|
||||
export async function flushPendingGenerationRecords(): Promise<{ synced: number; remaining: number }> {
|
||||
const pending = readPendingRecords();
|
||||
if (!pending.length) return { synced: 0, remaining: 0 };
|
||||
|
||||
const remaining: SaveGenerationRecordInput[] = [];
|
||||
let synced = 0;
|
||||
|
||||
for (const record of pending.slice().reverse()) {
|
||||
try {
|
||||
await serverRequest<{ id?: string | number }>("ai/generation-records", {
|
||||
method: "POST",
|
||||
body: record,
|
||||
maxRetries: 0,
|
||||
fallbackMessage: "Failed to sync generation record",
|
||||
});
|
||||
synced += 1;
|
||||
} catch {
|
||||
remaining.unshift(record);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
if (remaining.length) {
|
||||
window.localStorage.setItem(PENDING_RECORDS_KEY, JSON.stringify(remaining.slice(0, MAX_PENDING_RECORDS)));
|
||||
} else {
|
||||
window.localStorage.removeItem(PENDING_RECORDS_KEY);
|
||||
}
|
||||
} catch {
|
||||
// Keep runtime generation unaffected if browser storage is unavailable.
|
||||
}
|
||||
|
||||
return { synced, remaining: remaining.length };
|
||||
}
|
||||
|
||||
export async function deleteGenerationRecordByClientId(clientRecordId: string): Promise<void> {
|
||||
await serverRequest<{ success: boolean }>(`ai/generation-records/by-client-id/${encodeURIComponent(clientRecordId)}`, {
|
||||
method: "DELETE",
|
||||
maxRetries: 0,
|
||||
fallbackMessage: "Failed to delete generation record",
|
||||
});
|
||||
}
|
||||
|
||||
export function buildGenerationOssScope(parts: Array<string | number | null | undefined>): string {
|
||||
return parts
|
||||
.map((part) => String(part ?? "").trim().toLowerCase())
|
||||
.filter(Boolean)
|
||||
.map((part) => part.replace(/[^a-z0-9_-]+/g, "-").replace(/^-+|-+$/g, ""))
|
||||
.filter(Boolean)
|
||||
.join("/");
|
||||
}
|
||||
Reference in New Issue
Block a user