新增透传:91 编码与 affiliate_dash sku 一致时自动命中

- resolveAffiliateDashSkuByProductNo 改 async,匹配两级:①指定映射(skuMapping 归一命中) ②透传(productNo == affiliate_dash 商品 sku)
- 商品列表内存缓存 5 分钟,拉取失败返回上次缓存/空集合,匹配 miss 走其他通道不阻塞下单
- 两层命中均触发指定匹配最高优先(preferredExecutorKey)
- 测试 +3(224 全绿);线上验证 suit_alan_walker/suit_cloud_bear 透传命中,乱码回退
This commit is contained in:
yml2213
2026-08-05 17:40:13 +08:00
parent 769d41655a
commit 0f81380d9c
3 changed files with 102 additions and 10 deletions
@@ -3,6 +3,7 @@ import test from 'node:test'
import {
matchAffiliateDashSku,
matchAffiliateDashSkuPassthrough,
normalizeAffiliateDashMappingKey,
} from './product-resolution-service.js'
@@ -32,3 +33,21 @@ test('normalizeAffiliateDashMappingKey NFKC + 去空白 + 小写', () => {
assert.equal(normalizeAffiliateDashMappingKey(' 幸运币90个 '), '幸运币90个')
assert.equal(normalizeAffiliateDashMappingKey('M416-仓鼠灰灰'), 'm416-仓鼠灰灰')
})
test('matchAffiliateDashSkuPassthrough sku 一致时命中', () => {
const skus = new Set(['suit_alan_walker', 'lucky_coin_x90'])
assert.equal(matchAffiliateDashSkuPassthrough('suit_alan_walker', skus), true)
assert.equal(matchAffiliateDashSkuPassthrough('lucky_coin_x90', skus), true)
})
test('matchAffiliateDashSkuPassthrough 不在商品列表时未命中', () => {
const skus = new Set(['suit_alan_walker'])
assert.equal(matchAffiliateDashSkuPassthrough('not_a_real_sku', skus), false)
assert.equal(matchAffiliateDashSkuPassthrough('', skus), false)
assert.equal(matchAffiliateDashSkuPassthrough('suit_alan_walker', null), false)
assert.equal(matchAffiliateDashSkuPassthrough('suit_alan_walker', undefined), false)
})
test('matchAffiliateDashSkuPassthrough 空集合未命中', () => {
assert.equal(matchAffiliateDashSkuPassthrough('suit_alan_walker', new Set()), false)
})
@@ -12,6 +12,7 @@ import {
type FulfillmentRouteResult,
} from './routing-config-service.js'
import { getAffiliateDashConfig } from '../platforms/affiliate-dash/config.js'
import { listAllAffiliateDashProducts } from '../platforms/affiliate-dash/product-service.js'
import type { AffiliateDashSkuMapping } from '../../types/runtime-config.js'
import { FULFILLMENT_EXECUTOR_KEYS } from './executors/types.js'
@@ -42,7 +43,7 @@ export type AffiliateDashSkuMatch = {
sku: string
productNo: string
skuName: string
matchMode: 'affiliate_dash_sku_mapping'
matchMode: 'affiliate_dash_sku_mapping' | 'affiliate_dash_sku_passthrough'
}
type FulfillmentItemCandidate = {
@@ -234,7 +235,7 @@ async function resolveConfiguredItemCandidate({
if (isOpen91KuaishouOrder(provider, platform)) {
const cloudtentaclesNameMatch = await resolveCloudtentaclesSkuByProductName(externalSkuName)
const kuaishouFeifeiMatch = resolveKuaishouFeifeiProductByName(externalSkuName)
const affiliateDashMatch = resolveAffiliateDashSkuByProductNo(item)
const affiliateDashMatch = await resolveAffiliateDashSkuByProductNo(item)
// 指定匹配(显式映射)命中时优先走 affiliate_dash(可配置关闭,默认开)
const affiliateDashConfig = getAffiliateDashConfig()
const preferredExecutorKey =
@@ -310,7 +311,9 @@ async function resolveConfiguredItemCandidate({
}
}
function resolveAffiliateDashSkuByProductNo(item: FulfillmentItem): AffiliateDashSkuMatch | null {
async function resolveAffiliateDashSkuByProductNo(
item: FulfillmentItem,
): Promise<AffiliateDashSkuMatch | null> {
const config = getAffiliateDashConfig()
if (config.enabled === false || !config.baseUrl) {
return null
@@ -322,16 +325,62 @@ function resolveAffiliateDashSkuByProductNo(item: FulfillmentItem): AffiliateDas
item.externalSkuCode,
item.externalItemId,
])
const sku = productNo ? matchAffiliateDashSku(config.skuMapping, String(productNo)) : ''
if (!productNo || !sku) {
if (!productNo) {
return null
}
return {
sku,
productNo: String(productNo),
skuName: String(item.externalSkuName || '').trim(),
matchMode: 'affiliate_dash_sku_mapping',
// ① 指定映射(运营显式配置)
const mappedSku = matchAffiliateDashSku(config.skuMapping, String(productNo))
if (mappedSku) {
return {
sku: mappedSku,
productNo: String(productNo),
skuName: String(item.externalSkuName || '').trim(),
matchMode: 'affiliate_dash_sku_mapping',
}
}
// ② 透传:91 商品编码 == affiliate_dash 商品 sku 时直接命中(需商品列表校验,缓存 5 分钟)
const rawProductNo = String(productNo).trim()
const skuSet = await getAffiliateDashSkuSet()
if (matchAffiliateDashSkuPassthrough(rawProductNo, skuSet)) {
return {
sku: rawProductNo,
productNo: rawProductNo,
skuName: String(item.externalSkuName || '').trim(),
matchMode: 'affiliate_dash_sku_passthrough',
}
}
return null
}
/** 透传判定:productNo 与 affiliate_dash 商品 sku 完全一致。 */
export function matchAffiliateDashSkuPassthrough(
productNo: string,
availableSkus: ReadonlySet<string> | null | undefined,
): boolean {
const raw = String(productNo || '').trim()
return Boolean(raw && availableSkus?.has(raw))
}
const AFFILIATE_DASH_SKU_CACHE_TTL_MS = 5 * 60 * 1000
let affiliateDashSkuCache: { skus: Set<string>; fetchedAt: number } | null = null
async function getAffiliateDashSkuSet(): Promise<Set<string>> {
const now = Date.now()
if (affiliateDashSkuCache && now - affiliateDashSkuCache.fetchedAt < AFFILIATE_DASH_SKU_CACHE_TTL_MS) {
return affiliateDashSkuCache.skus
}
try {
const result = await listAllAffiliateDashProducts()
const skus = new Set<string>(result.list.map((product) => String(product.sku || '').trim()).filter(Boolean))
affiliateDashSkuCache = { skus, fetchedAt: now }
return skus
} catch {
// 拉取失败:返回上次缓存(即使过期)或空集合,匹配 miss 走其他通道,不阻塞下单
return affiliateDashSkuCache?.skus || new Set<string>()
}
}
@@ -688,3 +688,27 @@ order_site 处理要求:
| 神秘新商品(未映射) | (无)回退 | — |
**验证**typecheck 通过;后端全量 221 测试通过(新增 9 个)。
---
## 23. 透传优化:91 编码 == affiliate_dash sku 自动命中(v2.8 · 已完成)
**需求**:91 商品编码可直接设置为英文(= affiliate_dash sku,如 `suit_alan_walker`),映射表未命中时**自动透传**,无需在 order_site 维护映射表。
### 23.1 匹配层级(`resolveAffiliateDashSkuByProductNo`,改为 async
1. **① 指定映射**`matchAffiliateDashSku`):skuMapping 精确/归一命中 → `matchMode='affiliate_dash_sku_mapping'`
2. **② 透传**`matchAffiliateDashSkuPassthrough`):映射未命中时,productNo 与 affiliate_dash 商品 sku 完全一致 → `matchMode='affiliate_dash_sku_passthrough'`
3. 两层任一命中都触发「指定匹配最高优先」(`preferredExecutorKey=affiliate_dash`),规则/全局优先级不抢占
**商品列表缓存**`getAffiliateDashSkuSet()` 内存缓存 5 分钟(`listAllAffiliateDashProducts` 拉全量 33 个 sku);拉取失败返回上次缓存或空集合——匹配 miss 走其他通道,**不阻塞下单**。
### 23.2 行为验证(线上商品列表)
| 输入(91 编码) | executor | sku | matchMode |
| --- | --- | --- | --- |
| `suit_alan_walker`(真实商品) | affiliate_dash | suit_alan_walker | affiliate_dash_sku_passthrough |
| `suit_cloud_bear`(真实商品) | affiliate_dash | suit_cloud_bear | affiliate_dash_sku_passthrough |
| `not_a_real_sku` | (无)回退 | — | — |
**验证**typecheck 通过;后端全量 224 测试通过(透传纯函数测试 +3)。