支持 khhao 快手双店铺ID匹配

This commit is contained in:
yml
2026-05-04 18:52:23 +08:00
parent 42979d9581
commit c0431a6379
14 changed files with 251 additions and 53 deletions
@@ -33,6 +33,7 @@ export function mapKuaishouCloudFulfillmentItemsToBindings(config = {}) {
provider: String(item.provider || 'khhao').trim() || 'khhao',
platform: String(item.platform || 'kuaishou').trim() || 'kuaishou',
shopId: String(item.shopId || '').trim(),
khhaoShopId: String(item.khhaoShopId || '').trim(),
skuCode: String(item.internalSkuCode || '').trim(),
skuName: String(item.internalSkuName || '').trim(),
profileKey: 'kuaishou_ct_assisted',
@@ -123,6 +124,7 @@ function normalizeKuaishouCloudFulfillmentItem(rawValue) {
provider: String(rawValue.provider || 'khhao').trim() || 'khhao',
platform: String(rawValue.platform || 'kuaishou').trim() || 'kuaishou',
shopId: String(rawValue.shopId || '').trim(),
khhaoShopId: String(rawValue.khhaoShopId || '').trim(),
internalSkuCode,
internalSkuName: String(rawValue.internalSkuName || '').trim() || internalSkuCode,
externalSkuCode,
@@ -0,0 +1,66 @@
import test from 'node:test'
import assert from 'node:assert/strict'
import { mapKuaishouCloudFulfillmentItemsToBindings } from './kuaishou-cloud-fulfillment-config-service.js'
test('mapKuaishouCloudFulfillmentItemsToBindings preserves khhao shop id', () => {
assert.deepEqual(
mapKuaishouCloudFulfillmentItemsToBindings({
items: [
{
enabled: true,
provider: 'khhao',
platform: 'kuaishou',
shopId: '4269276762',
khhaoShopId: '10',
internalSkuCode: 'sku-1',
internalSkuName: '内部商品',
externalSkuCode: '952',
externalItemId: '952',
externalSkuName: '测试2',
cloudSkuId: 28,
},
],
}),
[
{
provider: 'khhao',
platform: 'kuaishou',
shopId: '4269276762',
khhaoShopId: '10',
skuCode: 'sku-1',
skuName: '内部商品',
profileKey: 'kuaishou_ct_assisted',
enabled: true,
priority: 100,
config: {
flowType: 'kuaishou_cloud_fulfillment',
configId: '',
cloudtentacles: {
cloudSourceKey: 'default',
skuId: 28,
skuName: '',
vnKey: '1',
autoBuyEnabled: true,
minAssetReserve: 0,
autoReturnNumberAfterDispatch: false,
},
kuaishouConsume: {
shopId: '',
shopName: '',
autoConsumeAfterDispatch: false,
},
notes: '',
},
match: {
externalSkuCode: '952',
externalItemId: '952',
externalSkuName: '测试2',
config: {
resolvedSkuName: '内部商品',
},
},
},
],
)
})
@@ -46,6 +46,7 @@ export async function upsertOrderFromSource(event, { sourceLabel = 'source' } =
provider: event.provider,
platform: event.platform,
shopId: event.shopId,
shopIdAliases: event.shopIdAliases,
item,
})),
)
@@ -21,6 +21,7 @@ import { resolveProductMatchRule } from '../../repositories/product-match-rule-r
* provider?: string
* platform?: string
* shopId?: string
* shopIdAliases?: string[]
* item?: FulfillmentItem
* }} params
*/
@@ -28,12 +29,14 @@ export async function resolveOrderItemForFulfillment({
provider = '',
platform = '',
shopId = '',
shopIdAliases = [],
item = /** @type {FulfillmentItem} */ ({}),
}) {
const candidate = await resolveConfiguredItemCandidate({
provider,
platform,
shopId,
shopIdAliases,
item,
})
const {
@@ -89,6 +92,7 @@ export async function resolveOrderItemForFulfillment({
* provider?: string
* platform?: string
* shopId?: string
* shopIdAliases?: string[]
* items?: FulfillmentItem[]
* }} params
*/
@@ -96,6 +100,7 @@ export async function hasConfiguredOrderItems({
provider = '',
platform = '',
shopId = '',
shopIdAliases = [],
items = [],
}) {
const candidates = await Promise.all(
@@ -103,6 +108,7 @@ export async function hasConfiguredOrderItems({
provider,
platform,
shopId,
shopIdAliases,
item,
})),
)
@@ -135,6 +141,7 @@ async function resolveConfiguredItemCandidate({
provider = '',
platform = '',
shopId = '',
shopIdAliases = [],
item = /** @type {FulfillmentItem} */ ({}),
}) {
const externalItemId = pickFirstNonEmpty([
@@ -152,28 +159,47 @@ async function resolveConfiguredItemCandidate({
externalSkuCode,
])
const externalSkuNameNormalized = normalizeProductName(externalSkuName)
const shopIdCandidates = resolveShopIdCandidates({ shopId, shopIdAliases })
let matchedRule = null
let binding = null
for (const candidateShopId of shopIdCandidates) {
const candidateRule = await resolveProductMatchRule({
provider,
platform,
shopId: candidateShopId,
externalItemId,
externalSkuCode,
externalSkuNameNormalized,
})
if (candidateRule) {
matchedRule = candidateRule
}
const resolvedSkuCode = pickFirstNonEmpty([
candidateRule?.resolved_sku_code,
externalSkuCode,
externalItemId,
])
binding = resolvedSkuCode
? await resolveFulfillmentBinding({
skuCode: resolvedSkuCode,
provider,
platform,
shopId: candidateShopId,
})
: null
if (binding) {
break
}
}
const matchedRule = await resolveProductMatchRule({
provider,
platform,
shopId,
externalItemId,
externalSkuCode,
externalSkuNameNormalized,
})
const resolvedSkuCode = pickFirstNonEmpty([
matchedRule?.resolved_sku_code,
externalSkuCode,
externalItemId,
])
const binding = resolvedSkuCode
? await resolveFulfillmentBinding({
skuCode: resolvedSkuCode,
provider,
platform,
shopId,
})
: null
return {
externalItemId,
@@ -187,6 +213,15 @@ async function resolveConfiguredItemCandidate({
}
}
export function resolveShopIdCandidates({ shopId = '', shopIdAliases = [] } = {}) {
const candidates = [...new Set([
String(shopId || '').trim(),
...(Array.isArray(shopIdAliases) ? shopIdAliases : []).map((item) => String(item || '').trim()),
].filter(Boolean))]
return candidates.length > 0 ? candidates : ['']
}
function readConfigValue(rawValue, key) {
if (!rawValue) {
return ''
@@ -0,0 +1,18 @@
import test from 'node:test'
import assert from 'node:assert/strict'
import { resolveShopIdCandidates } from './product-match-service.js'
test('resolveShopIdCandidates merges primary and alias shop ids', () => {
assert.deepEqual(
resolveShopIdCandidates({
shopId: '10',
shopIdAliases: ['4269276762', '10', ''],
}),
['10', '4269276762'],
)
})
test('resolveShopIdCandidates preserves wildcard fallback when ids are empty', () => {
assert.deepEqual(resolveShopIdCandidates({}), [''])
})
@@ -141,6 +141,7 @@ async function executeAgisoTradeWebhook(parsed, webhookEventId, { requestId = ''
provider: parsed.provider,
platform: parsed.platform,
shopId: parsed.shopId,
shopIdAliases: parsed.shopIdAliases,
items: parsed.items,
})