From 34eb54f906d244bf57ba1fce6d58d15d86af15b0 Mon Sep 17 00:00:00 2001 From: yml Date: Mon, 4 May 2026 14:53:37 +0800 Subject: [PATCH] =?UTF-8?q?=E6=8A=BD=E7=A6=BB=E5=90=8E=E5=8F=B0=E5=B9=B3?= =?UTF-8?q?=E5=8F=B0=E9=85=8D=E7=BD=AE=E9=A2=86=E5=9F=9F=E5=8C=B9=E9=85=8D?= =?UTF-8?q?=E8=BE=85=E5=8A=A9=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../admin/admin-platform-config-domain.js | 136 ++++++++++++ .../admin-platform-config-domain.test.js | 197 ++++++++++++++++++ .../admin/admin-platform-config-service.js | 137 +----------- 3 files changed, 341 insertions(+), 129 deletions(-) create mode 100644 apps/backend/src/services/admin/admin-platform-config-domain.js create mode 100644 apps/backend/src/services/admin/admin-platform-config-domain.test.js diff --git a/apps/backend/src/services/admin/admin-platform-config-domain.js b/apps/backend/src/services/admin/admin-platform-config-domain.js new file mode 100644 index 00000000..9067529b --- /dev/null +++ b/apps/backend/src/services/admin/admin-platform-config-domain.js @@ -0,0 +1,136 @@ +// @ts-check + +import { normalizeAgisoMessageTemplate } from '../platforms/agiso/xianyu/message-service.js' +import { maskSecret } from './admin-platform-config-mappers.js' + +export function mapAdminAgisoMessagingDefaults(defaults = {}) { + return { + messageTemplate: normalizeAgisoMessageTemplate(String(defaults.messageTemplate || '').trim()), + autoDeliveryMessageTemplate: normalizeAgisoMessageTemplate( + String(defaults.autoDeliveryMessageTemplate || '').trim(), + ), + } +} + +export function mapAdminAgisoShopConfigItem(shopId, config = {}) { + return { + shopId, + shopName: String(config.shopName || '').trim(), + accessToken: String(config.accessToken || '').trim(), + accessTokenMasked: maskSecret(config.accessToken), + enabled: typeof config.enabled === 'boolean' ? config.enabled : null, + messageTemplate: normalizeAgisoMessageTemplate(String(config.messageTemplate || '').trim()), + autoDeliveryMessageTemplate: normalizeAgisoMessageTemplate( + String(config.autoDeliveryMessageTemplate || '').trim(), + ), + appSecretConfigured: Boolean(String(config.appSecret || '').trim()), + apiVersion: String(config.apiVersion || '').trim(), + sendMessageEndpoint: String(config.sendMessageEndpoint || '').trim(), + } +} + +export function applyOptionalStringField(target, key, source) { + if (!source || typeof source[key] !== 'string') { + return + } + + const value = String(source[key] || '').trim() + if (value) { + target[key] = normalizeAgisoMessageTemplate(value) + return + } + + delete target[key] +} + +export function mapAdminFulfillmentBindingConfigItem(item) { + const match = item?.match || {} + return { + provider: String(item?.provider || '').trim(), + platform: String(item?.platform || '').trim(), + shopId: String(item?.shopId || '').trim(), + shopName: String(item?.shopName || '').trim(), + khhaoShopId: String(item?.khhaoShopId || '').trim(), + skuCode: String(item?.skuCode || '').trim(), + skuName: String(item?.skuName || '').trim(), + profileKey: String(item?.profileKey || '').trim(), + enabled: item?.enabled !== false, + priority: Number(item?.priority || 100), + config: item?.config || {}, + match: { + externalSkuCode: String(match.externalSkuCode || '').trim(), + externalItemId: String(match.externalItemId || '').trim(), + externalSkuName: String(match.externalSkuName || '').trim(), + config: match.config || {}, + }, + } +} + +export function resolveFulfillmentBindingMatchShopId({ + provider = '', + platform = '', + shopId = '', + khhaoShopId = '', +} = {}) { + if (String(provider || '').trim() === 'khhao' && String(platform || '').trim() === 'kuaishou') { + return String(khhaoShopId || shopId || '').trim() + } + + return String(shopId || '').trim() +} + +export function matchesObservedProduct(binding, observed) { + const provider = String(binding?.provider || '').trim() + const platform = String(binding?.platform || '').trim() + const shopId = String(binding?.shopId || '').trim() + const match = binding?.match || {} + const externalSkuCode = String(match.externalSkuCode || '').trim() + const externalItemId = String(match.externalItemId || '').trim() + const externalSkuName = String(match.externalSkuName || '').trim() + + if (provider && provider !== String(observed?.provider || '').trim()) { + return false + } + + if (platform && platform !== String(observed?.platform || '').trim()) { + return false + } + + if (shopId && shopId !== String(observed?.shopId || '').trim()) { + return false + } + + return ( + (externalSkuCode && externalSkuCode === String(observed?.externalSkuCode || '').trim()) + || (externalItemId && externalItemId === String(observed?.externalItemId || '').trim()) + || (externalSkuName && externalSkuName === String(observed?.externalSkuName || '').trim()) + ) +} + +export function findMatchingObservedBinding(bindings, observed) { + return (Array.isArray(bindings) ? bindings : []).find((binding) => matchesObservedProduct(binding, observed)) || null +} + +export function mapAdminObservedProductItem(item, bindings = []) { + const matchedBinding = findMatchingObservedBinding(bindings, item) + + return { + provider: String(item?.provider || '').trim(), + platform: String(item?.platform || '').trim(), + shopId: String(item?.shopId || '').trim(), + shopName: String(item?.shopName || '').trim(), + externalItemId: String(item?.externalItemId || '').trim(), + externalSkuCode: String(item?.externalSkuCode || '').trim(), + externalSkuName: String(item?.externalSkuName || '').trim(), + latestSeenAt: item?.latestSeenAt || null, + orderItemCount: Number(item?.orderItemCount || 0), + configured: Boolean(matchedBinding), + matchedBinding: matchedBinding + ? { + skuCode: String(matchedBinding.skuCode || '').trim(), + skuName: String(matchedBinding.skuName || '').trim(), + profileKey: String(matchedBinding.profileKey || '').trim(), + } + : null, + } +} diff --git a/apps/backend/src/services/admin/admin-platform-config-domain.test.js b/apps/backend/src/services/admin/admin-platform-config-domain.test.js new file mode 100644 index 00000000..0672e77a --- /dev/null +++ b/apps/backend/src/services/admin/admin-platform-config-domain.test.js @@ -0,0 +1,197 @@ +import test from 'node:test' +import assert from 'node:assert/strict' + +import { + applyOptionalStringField, + findMatchingObservedBinding, + mapAdminAgisoMessagingDefaults, + mapAdminAgisoShopConfigItem, + mapAdminFulfillmentBindingConfigItem, + mapAdminObservedProductItem, + matchesObservedProduct, + resolveFulfillmentBindingMatchShopId, +} from './admin-platform-config-domain.js' + +test('mapAdminAgisoMessagingDefaults normalizes escaped newlines', () => { + assert.deepEqual( + mapAdminAgisoMessagingDefaults({ + messageTemplate: ' hello\\nworld ', + autoDeliveryMessageTemplate: ' done\\nnow ', + }), + { + messageTemplate: 'hello\nworld', + autoDeliveryMessageTemplate: 'done\nnow', + }, + ) +}) + +test('mapAdminAgisoShopConfigItem masks secrets and reports configured flags', () => { + assert.deepEqual( + mapAdminAgisoShopConfigItem('1001', { + shopName: ' 店铺A ', + accessToken: 'abcdef1234567890', + enabled: true, + messageTemplate: ' hi\\nall ', + autoDeliveryMessageTemplate: ' ok ', + appSecret: 'secret-1', + apiVersion: ' v2 ', + sendMessageEndpoint: ' /send ', + }), + { + shopId: '1001', + shopName: '店铺A', + accessToken: 'abcdef1234567890', + accessTokenMasked: 'abcdef****567890', + enabled: true, + messageTemplate: 'hi\nall', + autoDeliveryMessageTemplate: 'ok', + appSecretConfigured: true, + apiVersion: 'v2', + sendMessageEndpoint: '/send', + }, + ) +}) + +test('applyOptionalStringField updates and clears normalized template fields', () => { + const target = { messageTemplate: 'old' } + applyOptionalStringField(target, 'messageTemplate', { messageTemplate: ' next\\nline ' }) + assert.deepEqual(target, { messageTemplate: 'next\nline' }) + + applyOptionalStringField(target, 'messageTemplate', { messageTemplate: ' ' }) + assert.deepEqual(target, {}) +}) + +test('mapAdminFulfillmentBindingConfigItem normalizes binding shape', () => { + assert.deepEqual( + mapAdminFulfillmentBindingConfigItem({ + provider: ' agiso ', + platform: ' xianyu ', + shopId: ' shop-1 ', + skuCode: ' sku-1 ', + priority: '80', + match: { + externalSkuCode: ' ext-1 ', + }, + }), + { + provider: 'agiso', + platform: 'xianyu', + shopId: 'shop-1', + shopName: '', + khhaoShopId: '', + skuCode: 'sku-1', + skuName: '', + profileKey: '', + enabled: true, + priority: 80, + config: {}, + match: { + externalSkuCode: 'ext-1', + externalItemId: '', + externalSkuName: '', + config: {}, + }, + }, + ) +}) + +test('resolveFulfillmentBindingMatchShopId prefers khhaoShopId for khhao kuaishou bindings', () => { + assert.equal( + resolveFulfillmentBindingMatchShopId({ + provider: 'khhao', + platform: 'kuaishou', + shopId: 'shop-a', + khhaoShopId: 'khhao-1', + }), + 'khhao-1', + ) + + assert.equal( + resolveFulfillmentBindingMatchShopId({ + provider: 'agiso', + platform: 'xianyu', + shopId: 'shop-a', + khhaoShopId: 'khhao-1', + }), + 'shop-a', + ) +}) + +test('matchesObservedProduct honors provider platform shop and external fields', () => { + const binding = { + provider: 'agiso', + platform: 'xianyu', + shopId: 'shop-1', + match: { + externalSkuCode: 'sku-ext-1', + externalItemId: '', + externalSkuName: '', + }, + } + + assert.equal( + matchesObservedProduct(binding, { + provider: 'agiso', + platform: 'xianyu', + shopId: 'shop-1', + externalSkuCode: 'sku-ext-1', + }), + true, + ) + + assert.equal( + matchesObservedProduct(binding, { + provider: 'agiso', + platform: 'xianyu', + shopId: 'shop-2', + externalSkuCode: 'sku-ext-1', + }), + false, + ) +}) + +test('mapAdminObservedProductItem attaches matched binding summary', () => { + const bindings = [ + { + provider: 'agiso', + platform: 'xianyu', + shopId: 'shop-1', + skuCode: 'sku-1', + skuName: 'SKU 1', + profileKey: 'profile-1', + match: { + externalSkuCode: 'ext-1', + }, + }, + ] + const observed = { + provider: 'agiso', + platform: 'xianyu', + shopId: 'shop-1', + shopName: '店铺1', + externalItemId: '', + externalSkuCode: 'ext-1', + externalSkuName: '礼包1', + latestSeenAt: '2026-05-04T10:00:00.000Z', + orderItemCount: 2, + } + + assert.deepEqual(findMatchingObservedBinding(bindings, observed), bindings[0]) + assert.deepEqual(mapAdminObservedProductItem(observed, bindings), { + provider: 'agiso', + platform: 'xianyu', + shopId: 'shop-1', + shopName: '店铺1', + externalItemId: '', + externalSkuCode: 'ext-1', + externalSkuName: '礼包1', + latestSeenAt: '2026-05-04T10:00:00.000Z', + orderItemCount: 2, + configured: true, + matchedBinding: { + skuCode: 'sku-1', + skuName: 'SKU 1', + profileKey: 'profile-1', + }, + }) +}) diff --git a/apps/backend/src/services/admin/admin-platform-config-service.js b/apps/backend/src/services/admin/admin-platform-config-service.js index a9f3a572..c9170748 100644 --- a/apps/backend/src/services/admin/admin-platform-config-service.js +++ b/apps/backend/src/services/admin/admin-platform-config-service.js @@ -63,7 +63,6 @@ import { verifyCloudtentaclesLoginCode, } from '../platforms/cloudtentacles/virtual-number-service.js' import { runCloudtentaclesFullDebugFlow } from '../platforms/cloudtentacles/debug-flow-service.js' -import { normalizeAgisoMessageTemplate } from '../platforms/agiso/xianyu/message-service.js' import { getOrderFulfillmentBindingConfigs, getOrderFulfillmentBindingsFilePath, @@ -87,6 +86,14 @@ import { resolveCloudtentaclesAdminContext, resolveKuaishouEticketAdminContext, } from './admin-platform-config-context.js' +import { + applyOptionalStringField, + mapAdminAgisoMessagingDefaults, + mapAdminAgisoShopConfigItem, + mapAdminFulfillmentBindingConfigItem, + mapAdminObservedProductItem, + resolveFulfillmentBindingMatchShopId, +} from './admin-platform-config-domain.js' import { mapAdminCloudtentaclesSession, mapAdminCloudtentaclesSourceConfig, @@ -233,42 +240,6 @@ export function updateAdminAgisoShopConfigs(payload = /** @type {AdminAgisoShopC } } -function mapAdminAgisoMessagingDefaults(defaults = {}) { - return { - messageTemplate: normalizeAgisoMessageTemplate(String(defaults.messageTemplate || '').trim()), - autoDeliveryMessageTemplate: normalizeAgisoMessageTemplate(String(defaults.autoDeliveryMessageTemplate || '').trim()), - } -} - -function mapAdminAgisoShopConfigItem(shopId, config = {}) { - return { - shopId, - shopName: String(config.shopName || '').trim(), - accessToken: String(config.accessToken || '').trim(), - accessTokenMasked: maskSecret(config.accessToken), - enabled: typeof config.enabled === 'boolean' ? config.enabled : null, - messageTemplate: normalizeAgisoMessageTemplate(String(config.messageTemplate || '').trim()), - autoDeliveryMessageTemplate: normalizeAgisoMessageTemplate(String(config.autoDeliveryMessageTemplate || '').trim()), - appSecretConfigured: Boolean(String(config.appSecret || '').trim()), - apiVersion: String(config.apiVersion || '').trim(), - sendMessageEndpoint: String(config.sendMessageEndpoint || '').trim(), - } -} - -function applyOptionalStringField(target, key, source) { - if (!source || typeof source[key] !== 'string') { - return - } - - const value = String(source[key] || '').trim() - if (value) { - target[key] = normalizeAgisoMessageTemplate(value) - return - } - - delete target[key] -} - export function getAdminKhhaoSourceConfig() { const config = getKhhaoSourceConfig() const syncState = getKhhaoSyncState() @@ -1094,95 +1065,3 @@ async function validateAdminFulfillmentBindingConfigs(bindings = []) { return normalizedBindings } - -function mapAdminFulfillmentBindingConfigItem(item) { - const match = item?.match || {} - return { - provider: String(item?.provider || '').trim(), - platform: String(item?.platform || '').trim(), - shopId: String(item?.shopId || '').trim(), - shopName: String(item?.shopName || '').trim(), - khhaoShopId: String(item?.khhaoShopId || '').trim(), - skuCode: String(item?.skuCode || '').trim(), - skuName: String(item?.skuName || '').trim(), - profileKey: String(item?.profileKey || '').trim(), - enabled: item?.enabled !== false, - priority: Number(item?.priority || 100), - config: item?.config || {}, - match: { - externalSkuCode: String(match.externalSkuCode || '').trim(), - externalItemId: String(match.externalItemId || '').trim(), - externalSkuName: String(match.externalSkuName || '').trim(), - config: match.config || {}, - }, - } -} - -function resolveFulfillmentBindingMatchShopId({ - provider = '', - platform = '', - shopId = '', - khhaoShopId = '', -} = {}) { - if (String(provider || '').trim() === 'khhao' && String(platform || '').trim() === 'kuaishou') { - return String(khhaoShopId || shopId || '').trim() - } - - return String(shopId || '').trim() -} - -function matchesObservedProduct(binding, observed) { - const provider = String(binding?.provider || '').trim() - const platform = String(binding?.platform || '').trim() - const shopId = String(binding?.shopId || '').trim() - const match = binding?.match || {} - const externalSkuCode = String(match.externalSkuCode || '').trim() - const externalItemId = String(match.externalItemId || '').trim() - const externalSkuName = String(match.externalSkuName || '').trim() - - if (provider && provider !== String(observed?.provider || '').trim()) { - return false - } - - if (platform && platform !== String(observed?.platform || '').trim()) { - return false - } - - if (shopId && shopId !== String(observed?.shopId || '').trim()) { - return false - } - - return ( - (externalSkuCode && externalSkuCode === String(observed?.externalSkuCode || '').trim()) - || (externalItemId && externalItemId === String(observed?.externalItemId || '').trim()) - || (externalSkuName && externalSkuName === String(observed?.externalSkuName || '').trim()) - ) -} - -function mapAdminObservedProductItem(item, bindings = []) { - const matchedBinding = findMatchingObservedBinding(bindings, item) - - return { - provider: String(item?.provider || '').trim(), - platform: String(item?.platform || '').trim(), - shopId: String(item?.shopId || '').trim(), - shopName: String(item?.shopName || '').trim(), - externalItemId: String(item?.externalItemId || '').trim(), - externalSkuCode: String(item?.externalSkuCode || '').trim(), - externalSkuName: String(item?.externalSkuName || '').trim(), - latestSeenAt: item?.latestSeenAt || null, - orderItemCount: Number(item?.orderItemCount || 0), - configured: Boolean(matchedBinding), - matchedBinding: matchedBinding - ? { - skuCode: String(matchedBinding.skuCode || '').trim(), - skuName: String(matchedBinding.skuName || '').trim(), - profileKey: String(matchedBinding.profileKey || '').trim(), - } - : null, - } -} - -function findMatchingObservedBinding(bindings, observed) { - return (Array.isArray(bindings) ? bindings : []).find((binding) => matchesObservedProduct(binding, observed)) || null -}