import path from "node:path"; import { PROJECT_ROOT } from "../../config/runtime.js"; import { readJsonFile, writeJsonFile } from "../../utils/json-file-store.js"; import { resolveKuaishouEticketShopConfig } from "../platforms/kuaishou-eticket/source-config-service.js"; const KUAISHOU_CLOUD_FULFILLMENT_FILE_PATH = path.join( PROJECT_ROOT, "data", "kuaishou-cloud-fulfillment.json" ); type JsonObject = Record; export function getKuaishouCloudFulfillmentFilePath() { return KUAISHOU_CLOUD_FULFILLMENT_FILE_PATH; } export function getKuaishouCloudFulfillmentConfig() { return loadKuaishouCloudFulfillmentConfigFromFile(); } export function saveKuaishouCloudFulfillmentConfig(rawValue) { return writeJsonFile( KUAISHOU_CLOUD_FULFILLMENT_FILE_PATH, rawValue, normalizeKuaishouCloudFulfillmentConfig ); } export function mapKuaishouCloudFulfillmentItemsToBindings(config: JsonObject = {}) { const items = Array.isArray(config.items) ? config.items : []; return items .filter((item) => item && item.enabled !== false) .map((item) => ({ provider: String(item.provider || "91kaquan").trim() || "91kaquan", platform: String(item.platform || "kuaishou").trim() || "kuaishou", shopId: String(item.shopId || "").trim(), skuCode: String(item.internalSkuCode || "").trim(), skuName: String(item.internalSkuName || "").trim(), profileKey: "kuaishou_ct_assisted", enabled: item.enabled !== false, priority: normalizePriority(item.priority), config: { flowType: "kuaishou_cloud_fulfillment", configId: String(item.id || "").trim(), cloudtentacles: { cloudSourceKey: String(item.cloudSourceKey || "default").trim() || "default", cloudSourceKeyFallbacks: Array.isArray(item.cloudSourceKeyFallbacks) ? item.cloudSourceKeyFallbacks : [], skuId: normalizePositiveInteger(item.cloudSkuId), skuName: String(item.cloudSkuName || "").trim(), vnKey: "1", autoBuyEnabled: item.autoBuyEnabled !== false, minAssetReserve: normalizeNonNegativeInteger(item.minAssetReserve, 0), autoReturnNumberAfterDispatch: item.autoReturnNumberAfterDispatch === true, }, kuaishouConsume: { shopId: String(item.kuaishouConsumeShopId || "").trim(), shopName: String(item.kuaishouConsumeShopName || "").trim(), autoConsumeAfterDispatch: item.autoConsumeAfterDispatch === true, }, notes: String(item.notes || "").trim(), }, match: { externalSkuCode: String(item.externalSkuCode || "").trim(), externalItemId: String(item.externalItemId || "").trim(), externalSkuName: String(item.externalSkuName || "").trim(), config: { resolvedSkuName: String( item.resolvedSkuName || item.internalSkuName || "" ).trim(), }, }, })) .filter( (item) => item.skuCode && (item.match.externalSkuCode || item.match.externalItemId || item.match.externalSkuName) ); } function loadKuaishouCloudFulfillmentConfigFromFile() { return readJsonFile( KUAISHOU_CLOUD_FULFILLMENT_FILE_PATH, () => normalizeKuaishouCloudFulfillmentConfig({}), normalizeKuaishouCloudFulfillmentConfig ); } function normalizeKuaishouCloudFulfillmentConfig(rawValue) { const source = isPlainObject(rawValue) ? rawValue : {}; return { enabled: source.enabled !== false, items: Array.isArray(source.items) ? source.items .map((item) => normalizeKuaishouCloudFulfillmentItem(item)) .filter(Boolean) : [], }; } function normalizeKuaishouCloudFulfillmentItem(rawValue) { if (!isPlainObject(rawValue)) { return null; } const internalSkuCode = String(rawValue.internalSkuCode || "").trim(); const cloudSkuId = normalizePositiveInteger(rawValue.cloudSkuId); const externalSkuCode = String(rawValue.externalSkuCode || "").trim(); const externalItemId = String(rawValue.externalItemId || "").trim(); const externalSkuName = String(rawValue.externalSkuName || "").trim(); const kuaishouConsumeShopId = String( rawValue.kuaishouConsumeShopId || "" ).trim(); const kuaishouConsumeShopName = String( rawValue.kuaishouConsumeShopName || "" ).trim(); const kuaishouShopConfig = resolveKuaishouEticketShopConfig({ shopId: kuaishouConsumeShopId, shopName: kuaishouConsumeShopName, }); if (!internalSkuCode) { return null; } if (!cloudSkuId) { return null; } if (!externalSkuCode && !externalItemId && !externalSkuName) { return null; } return { id: String(rawValue.id || internalSkuCode).trim() || internalSkuCode, enabled: rawValue.enabled !== false, priority: normalizePriority(rawValue.priority), provider: String(rawValue.provider || "91kaquan").trim() || "91kaquan", platform: String(rawValue.platform || "kuaishou").trim() || "kuaishou", shopId: String(rawValue.shopId || "").trim(), internalSkuCode, internalSkuName: String(rawValue.internalSkuName || "").trim() || internalSkuCode, externalSkuCode, externalItemId, externalSkuName, resolvedSkuName: String( rawValue.resolvedSkuName || rawValue.internalSkuName || "" ).trim(), cloudSourceKey: String(rawValue.cloudSourceKey || "default").trim() || "default", cloudSourceKeyFallbacks: normalizeStringArray( rawValue.cloudSourceKeyFallbacks ), cloudSkuId, cloudSkuName: String(rawValue.cloudSkuName || "").trim(), vnKey: "1", autoBuyEnabled: rawValue.autoBuyEnabled !== false, minAssetReserve: normalizeNonNegativeInteger(rawValue.minAssetReserve, 0), autoReturnNumberAfterDispatch: rawValue.autoReturnNumberAfterDispatch === true, autoConsumeAfterDispatch: rawValue.autoConsumeAfterDispatch === true, kuaishouConsumeShopId: String( kuaishouShopConfig?.shopId || kuaishouConsumeShopId ).trim(), kuaishouConsumeShopName: String( kuaishouShopConfig?.kshopName || kuaishouConsumeShopName ).trim(), notes: String(rawValue.notes || "").trim(), }; } function normalizePositiveInteger(value) { const parsed = Number(value); return Number.isInteger(parsed) && parsed > 0 ? parsed : 0; } function normalizeNonNegativeInteger(value, fallback) { const parsed = Number(value); return Number.isInteger(parsed) && parsed >= 0 ? parsed : fallback; } function normalizePriority(value) { const parsed = Number(value); if (!Number.isFinite(parsed)) { return 100; } return Math.max(1, Math.round(parsed)); } function isPlainObject(value) { return Object.prototype.toString.call(value) === "[object Object]"; } function normalizeStringArray(value) { if (Array.isArray(value)) { return value.map((v) => String(v || "").trim()).filter(Boolean); } // 兼容旧格式:字符串用逗号分隔 if (typeof value === "string") { return value .split(",") .map((v) => v.trim()) .filter(Boolean); } return []; }