export const POINTS_SYSTEM_CONFIG_KEY = "points_system_config"; export const POINTS_PACKAGES_KEY = "points_packages"; export type PointsSystemConfig = { enabled: boolean; blockOnInsufficient: boolean; creditCostPer3D: number; }; export type PointsPackage = { id: string; name: string; points: number; price: number; currency: string; isEnabled: boolean; }; export const defaultPointsSystemConfig: PointsSystemConfig = { enabled: false, blockOnInsufficient: true, creditCostPer3D: 0, }; export const defaultPointsPackages: PointsPackage[] = [ { id: "starter-1000", name: "入门包", points: 1000, price: 9.9, currency: "CNY", isEnabled: true, }, { id: "pro-5000", name: "专业包", points: 5000, price: 39.9, currency: "CNY", isEnabled: true, }, ]; export function normalizePointsSystemConfig(value: unknown): PointsSystemConfig { if (!value || typeof value !== "object") { return defaultPointsSystemConfig; } const raw = value as Partial; const parsed3DCost = Number(raw.creditCostPer3D); return { enabled: raw.enabled === true, blockOnInsufficient: raw.blockOnInsufficient !== false, creditCostPer3D: Number.isFinite(parsed3DCost) && parsed3DCost > 0 ? Math.min(100000, Math.round(parsed3DCost)) : 0, }; } export function normalizePointsPackages(value: unknown): PointsPackage[] { if (!Array.isArray(value)) { return defaultPointsPackages; } const normalized = value .map((item, index) => { if (!item || typeof item !== "object") return null; const raw = item as Partial; const id = String(raw.id || `package-${index + 1}`).trim(); const name = String(raw.name || "").trim(); const points = Number(raw.points); const price = Number(raw.price); const currency = String(raw.currency || "CNY").trim().toUpperCase(); if (!id || !name || !Number.isFinite(points) || points <= 0) return null; if (!Number.isFinite(price) || price < 0) return null; return { id: id.slice(0, 64), name: name.slice(0, 64), points: Math.round(points), price: Number(price.toFixed(2)), currency: currency.slice(0, 8) || "CNY", isEnabled: raw.isEnabled !== false, } satisfies PointsPackage; }) .filter((item): item is PointsPackage => Boolean(item)) .slice(0, 30); return normalized.length > 0 ? normalized : defaultPointsPackages; }