简化履约匹配并支持mock订单测试
This commit is contained in:
@@ -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
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user