简化履约匹配并支持mock订单测试

This commit is contained in:
yml
2026-05-27 12:55:44 +08:00
parent e8010728a4
commit dd6776c4e4
21 changed files with 1892 additions and 683 deletions
@@ -0,0 +1,165 @@
import {
listCloudtentaclesSources,
} from '../platforms/cloudtentacles/source-config-service.js'
import {
getCloudtentaclesSessionStateByKey,
} from '../platforms/cloudtentacles/session-state-service.js'
import {
listCloudtentaclesSku,
} from '../platforms/cloudtentacles/catalog-service.js'
type JsonObject = Record<string, any>
type CloudtentaclesSource = {
key?: string
label?: string
enabled?: boolean
baseUrl?: string
deviceId?: string
deviceType?: number
}
type CloudtentaclesSession = {
token?: string
baseUrl?: string
deviceId?: string
deviceType?: number
}
type CloudtentaclesSku = {
id?: number
name?: string
inventory?: number
price?: number
[key: string]: unknown
}
type CloudtentaclesNameMatchDeps = {
listCloudtentaclesSources?: typeof listCloudtentaclesSources
getCloudtentaclesSessionStateByKey?: typeof getCloudtentaclesSessionStateByKey
listCloudtentaclesSku?: typeof listCloudtentaclesSku
}
export type CloudtentaclesNameMatchResult = {
matchMode: 'cloudtentacles_name'
productName: string
normalizedProductName: string
cloudSkuId: number
cloudSkuName: string
cloudSkuPrice: number
cloudSkuInventory: number
cloudSourceKeys: string[]
resolvedSourceKey: string
skuSnapshot: JsonObject
}
export async function resolveCloudtentaclesSkuByProductName(
productName: unknown,
deps: CloudtentaclesNameMatchDeps = {},
): Promise<CloudtentaclesNameMatchResult | null> {
const normalizedProductName = normalizeCloudtentaclesMatchName(productName)
if (!normalizedProductName) {
return null
}
const listSources = deps.listCloudtentaclesSources || listCloudtentaclesSources
const getSessionByKey = deps.getCloudtentaclesSessionStateByKey || getCloudtentaclesSessionStateByKey
const listSku = deps.listCloudtentaclesSku || listCloudtentaclesSku
const sourcesConfig = listSources()
if (sourcesConfig.enabled === false) {
return null
}
const sourceContexts = (Array.isArray(sourcesConfig.sources) ? sourcesConfig.sources : [])
.map((source: CloudtentaclesSource) => buildCloudtentaclesSourceContext(source, getSessionByKey))
.filter((context): context is NonNullable<ReturnType<typeof buildCloudtentaclesSourceContext>> => Boolean(context))
if (sourceContexts.length === 0) {
return null
}
const cloudSourceKeys = sourceContexts.map((context) => context.sourceKey)
for (const context of sourceContexts) {
try {
const skuList = await listSku(context)
const matchedSku = findCloudtentaclesSkuByName(
Array.isArray(skuList.items) ? skuList.items : [],
productName,
)
if (!matchedSku) {
continue
}
const cloudSkuId = Number(matchedSku.id || 0)
const cloudSkuName = String(matchedSku.name || '').trim()
if (!Number.isInteger(cloudSkuId) || cloudSkuId <= 0 || !cloudSkuName) {
continue
}
return {
matchMode: 'cloudtentacles_name',
productName: String(productName || '').trim(),
normalizedProductName,
cloudSkuId,
cloudSkuName,
cloudSkuPrice: Number(matchedSku.price || 0) || 0,
cloudSkuInventory: Number(matchedSku.inventory || 0) || 0,
cloudSourceKeys,
resolvedSourceKey: context.sourceKey,
skuSnapshot: { ...matchedSku },
}
} catch {
// 当前账号不可用时继续尝试下一个已登录账号。
}
}
return null
}
export function normalizeCloudtentaclesMatchName(value: unknown) {
return String(value || '')
.toLowerCase()
.replace(/[【】\[\]()()]/g, ' ')
.replace(/(自动发货|秒发|极速发货|官方直充|官方充值)/gi, ' ')
.replace(/[^\p{L}\p{N}]+/gu, ' ')
.replace(/\s+/g, ' ')
.trim()
}
function buildCloudtentaclesSourceContext(
source: CloudtentaclesSource,
getSessionByKey: typeof getCloudtentaclesSessionStateByKey,
) {
const sourceKey = String(source.key || '').trim()
if (!sourceKey || source.enabled === false) {
return null
}
const session = getSessionByKey(sourceKey) as CloudtentaclesSession | null
const token = String(session?.token || '').trim()
if (!token) {
return null
}
return {
sourceKey,
baseUrl: String(session?.baseUrl || source.baseUrl || '').trim(),
token,
deviceId: String(session?.deviceId || source.deviceId || '-').trim() || '-',
deviceType: Number(session?.deviceType ?? source.deviceType ?? 0),
}
}
function findCloudtentaclesSkuByName(items: CloudtentaclesSku[], productName: unknown) {
const rawName = String(productName || '').trim()
const normalizedName = normalizeCloudtentaclesMatchName(rawName)
return (
items.find((item) => String(item.name || '').trim() === rawName) ||
items.find((item) => normalizeCloudtentaclesMatchName(item.name) === normalizedName) ||
null
)
}