import fs from 'node:fs' import path from 'node:path' import { PROJECT_ROOT } from '../../config/runtime.js' const KUAISHOU_CLOUD_FULFILLMENT_FILE_PATH = path.join(PROJECT_ROOT, 'data', 'kuaishou-cloud-fulfillment.json') export function getKuaishouCloudFulfillmentFilePath() { return KUAISHOU_CLOUD_FULFILLMENT_FILE_PATH } export function getKuaishouCloudFulfillmentConfig() { return loadKuaishouCloudFulfillmentConfigFromFile() } export function saveKuaishouCloudFulfillmentConfig(rawValue) { const normalized = normalizeKuaishouCloudFulfillmentConfig(rawValue) fs.mkdirSync(path.dirname(KUAISHOU_CLOUD_FULFILLMENT_FILE_PATH), { recursive: true }) fs.writeFileSync( KUAISHOU_CLOUD_FULFILLMENT_FILE_PATH, `${JSON.stringify(normalized, null, 2)}\n`, 'utf8', ) return normalized } export function mapKuaishouCloudFulfillmentItemsToBindings(config = {}) { 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', 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() { if (!fs.existsSync(KUAISHOU_CLOUD_FULFILLMENT_FILE_PATH)) { return normalizeKuaishouCloudFulfillmentConfig({}) } try { const rawText = fs.readFileSync(KUAISHOU_CLOUD_FULFILLMENT_FILE_PATH, 'utf8') const parsed = JSON.parse(rawText) return normalizeKuaishouCloudFulfillmentConfig(parsed) } catch { return 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() 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', 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(rawValue.kuaishouConsumeShopId || '').trim(), kuaishouConsumeShopName: String(rawValue.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]' }