import type { ImageTemplate } from "@/lib/types"; type ImageTemplateBody = { name?: unknown; description?: unknown; prompt?: unknown; previewImageUrl?: unknown; settings?: unknown; sortOrder?: unknown; }; type ImageTemplatePatch = Partial>; export function normalizeImageTemplateCreate(body: ImageTemplateBody): Omit { return { name: requiredText(body.name, "模板名称", 80), description: optionalText(body.description, 240), prompt: requiredText(body.prompt, "预设提示词", 4000), previewImageUrl: optionalPreviewUrl(body.previewImageUrl), settings: normalizeTemplateSettings(body.settings), sortOrder: optionalNumber(body.sortOrder) ?? 0 }; } export function normalizeImageTemplateUpdate(body: ImageTemplateBody): ImageTemplatePatch { const patch: ImageTemplatePatch = {}; if ("name" in body) patch.name = requiredText(body.name, "模板名称", 80); if ("description" in body) patch.description = optionalText(body.description, 240); if ("prompt" in body) patch.prompt = requiredText(body.prompt, "预设提示词", 4000); if ("previewImageUrl" in body) patch.previewImageUrl = optionalPreviewUrl(body.previewImageUrl); if ("settings" in body) patch.settings = normalizeTemplateSettings(body.settings); if ("sortOrder" in body) patch.sortOrder = optionalNumber(body.sortOrder) ?? 0; return patch; } function requiredText(value: unknown, label: string, maxLength: number): string { const text = optionalText(value, maxLength); if (!text) throw new Error(`${label}不能为空`); return text; } function optionalText(value: unknown, maxLength: number): string | undefined { if (value === undefined || value === null) return undefined; const text = String(value).trim(); if (!text) return undefined; return text.slice(0, maxLength); } function optionalPreviewUrl(value: unknown): string | undefined { const url = optionalText(value, 1000); if (!url) return undefined; if (url.startsWith("/") || /^https?:\/\//i.test(url)) return url; throw new Error("效果预览图地址必须是 http(s) 或站内路径"); } function normalizeTemplateSettings(value: unknown): ImageTemplate["settings"] { if (!isRecord(value)) return {}; const width = optionalPositiveInteger(value.width); const height = optionalPositiveInteger(value.height); const engine = normalizeImageEngine(value.engine); const scale = optionalScale(value.scale); const quality = normalizeQuality(value.quality); return { ...(engine ? { engine } : {}), ...(width && height ? { width, height } : {}), ...(typeof value.forceSingle === "boolean" ? { forceSingle: value.forceSingle } : {}), ...(scale !== undefined ? { scale } : {}), ...(quality ? { quality } : {}) }; } function optionalNumber(value: unknown): number | undefined { if (value === undefined || value === null || value === "") return undefined; const parsed = Number(value); return Number.isFinite(parsed) ? Math.trunc(parsed) : undefined; } function optionalPositiveInteger(value: unknown): number | undefined { const parsed = optionalNumber(value); if (!parsed || parsed < 1) return undefined; return Math.min(parsed, 8192); } function optionalScale(value: unknown): number | undefined { const parsed = optionalNumber(value); if (parsed === undefined) return undefined; return Math.max(1, Math.min(100, parsed)); } function normalizeImageEngine(value: unknown): ImageTemplate["settings"]["engine"] | undefined { const text = optionalText(value, 20); if (text === "jimeng" || text === "evolink" || text === "bailian") return text; return undefined; } function normalizeQuality(value: unknown): ImageTemplate["settings"]["quality"] | undefined { const text = optionalText(value, 20); if (text === "low" || text === "medium" || text === "high") return text; return undefined; } function isRecord(value: unknown): value is Record { return typeof value === "object" && value !== null && !Array.isArray(value); }