优化feifei商品名映射
This commit is contained in:
@@ -6,6 +6,7 @@ import {
|
||||
listAdminKuaishouFeifeiProducts,
|
||||
matchAdminKuaishouFeifeiProduct,
|
||||
queryAdminKuaishouFeifeiTestOrder,
|
||||
syncAdminKuaishouFeifeiProductRules,
|
||||
updateAdminKuaishouFeifeiConfig,
|
||||
} from "../../../services/admin/platform-config/kuaishou-feifei-service.js";
|
||||
import { createJsonHandler } from "../session.js";
|
||||
@@ -74,6 +75,33 @@ router.post(
|
||||
)
|
||||
);
|
||||
|
||||
router.post(
|
||||
"/kuaishou-feifei/sync-products",
|
||||
createJsonHandler(
|
||||
(req) => syncAdminKuaishouFeifeiProductRules(req.body as JsonRecord),
|
||||
{
|
||||
successMessage: "kuaishou-feifei 商品映射已同步",
|
||||
errorMessage: "同步 kuaishou-feifei 商品映射失败",
|
||||
scope: "[admin/platform-config/kuaishou-feifei/sync-products]",
|
||||
audit: (_req, data) => {
|
||||
const result = data as JsonRecord;
|
||||
const sync = result.sync as JsonRecord | undefined;
|
||||
|
||||
return {
|
||||
action: "platform_kuaishou_feifei_products_synced",
|
||||
targetType: "platform_config",
|
||||
targetId: "kuaishou_feifei",
|
||||
data: {
|
||||
productCount: Number(sync?.productCount || 0) || 0,
|
||||
ruleCount: Number(sync?.ruleCount || 0) || 0,
|
||||
status: String(sync?.status || "").trim(),
|
||||
},
|
||||
};
|
||||
},
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
router.post(
|
||||
"/kuaishou-feifei/test-order",
|
||||
createJsonHandler(
|
||||
|
||||
@@ -3,6 +3,7 @@ import {
|
||||
createKuaishouFeifeiOrder,
|
||||
listKuaishouFeifeiProducts,
|
||||
queryKuaishouFeifeiOrder,
|
||||
type KuaishouFeifeiProductListResult,
|
||||
} from '../../platforms/kuaishou-feifei/order-service.js'
|
||||
import { resolveKuaishouFeifeiProductByName } from '../../platforms/kuaishou-feifei/product-rule-service.js'
|
||||
import {
|
||||
@@ -64,6 +65,52 @@ export async function listAdminKuaishouFeifeiProducts(payload: JsonObject = {})
|
||||
})
|
||||
}
|
||||
|
||||
export async function syncAdminKuaishouFeifeiProductRules(payload: JsonObject = {}) {
|
||||
const source = getAdminEditableKuaishouFeifeiConfig()
|
||||
const status = String(payload.status || 'on_sale').trim() || 'on_sale'
|
||||
const supplyProductName = String(payload.supplyProductName || payload.supply_product_name || '').trim()
|
||||
const products = await listAllKuaishouFeifeiProducts({
|
||||
status,
|
||||
supplyProductName,
|
||||
})
|
||||
const productRules = products
|
||||
.map((product) => {
|
||||
const productName = String(product.name || '').trim()
|
||||
const productCode = String(product.productCode || '').trim()
|
||||
if (!productName || !productCode) {
|
||||
return null
|
||||
}
|
||||
|
||||
return {
|
||||
id: normalizeCloudtentaclesMatchName(productName) || productCode,
|
||||
enabled: product.status !== 'off_sale' && product.supplyStatus !== 'off_sale',
|
||||
productName,
|
||||
normalizedProductName: normalizeCloudtentaclesMatchName(productName),
|
||||
productCode,
|
||||
skuName: productName,
|
||||
notes: `feifei 商品目录同步:${product.channel || 'unknown'}`,
|
||||
}
|
||||
})
|
||||
.filter((rule): rule is NonNullable<typeof rule> => Boolean(rule))
|
||||
const saved = saveKuaishouFeifeiSourceConfig({
|
||||
...source,
|
||||
productRules,
|
||||
})
|
||||
const effective = getKuaishouFeifeiConfig()
|
||||
|
||||
return {
|
||||
filePath: getKuaishouFeifeiConfigFilePath(),
|
||||
source: saved,
|
||||
effective: mapEffectiveKuaishouFeifeiConfig(effective),
|
||||
sync: {
|
||||
productCount: products.length,
|
||||
ruleCount: saved.productRules.length,
|
||||
status,
|
||||
supplyProductName,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
export async function createAdminKuaishouFeifeiTestOrder(payload: JsonObject = {}) {
|
||||
const platformOrderNo = String(payload.platformOrderNo || payload.platform_order_no || '').trim()
|
||||
const productCode = String(payload.productCode || payload.product_code || '').trim()
|
||||
@@ -139,6 +186,34 @@ function mapEffectiveKuaishouFeifeiConfig(config: ReturnType<typeof getKuaishouF
|
||||
}
|
||||
}
|
||||
|
||||
async function listAllKuaishouFeifeiProducts(input: {
|
||||
status: string
|
||||
supplyProductName: string
|
||||
}) {
|
||||
const perPage = 100
|
||||
const firstPage = await listKuaishouFeifeiProducts({
|
||||
page: 1,
|
||||
perPage,
|
||||
status: input.status,
|
||||
supplyProductName: input.supplyProductName,
|
||||
})
|
||||
const products = [...firstPage.items]
|
||||
const total = Number(firstPage.total || products.length) || products.length
|
||||
const pageCount = Math.max(1, Math.ceil(total / perPage))
|
||||
|
||||
for (let page = 2; page <= pageCount; page += 1) {
|
||||
const result: KuaishouFeifeiProductListResult = await listKuaishouFeifeiProducts({
|
||||
page,
|
||||
perPage,
|
||||
status: input.status,
|
||||
supplyProductName: input.supplyProductName,
|
||||
})
|
||||
products.push(...result.items)
|
||||
}
|
||||
|
||||
return products
|
||||
}
|
||||
|
||||
function normalizeOptionalNumber(value: unknown) {
|
||||
if (value == null || String(value).trim() === '') {
|
||||
return undefined
|
||||
|
||||
@@ -545,9 +545,9 @@ async function resolveDynamicKuaishouFeifeiProfile(
|
||||
kuaishouFeifei: {
|
||||
productCode,
|
||||
productName: String(feifei.skuName || feifei.productName || item.sku_name || '').trim(),
|
||||
matchMode: String(feifei.matchMode || 'kuaishou_feifei_rule').trim(),
|
||||
matchMode: String(feifei.matchMode || 'kuaishou_feifei_name').trim(),
|
||||
},
|
||||
notes: '91卡券商品名命中 kuaishou-feifei 商品规则',
|
||||
notes: '91卡券商品名自动匹配 kuaishou-feifei 商品映射',
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,8 @@ import { kuaishouFeifeiRequest } from './http-client.js'
|
||||
|
||||
type JsonObject = Record<string, any>
|
||||
|
||||
export type KuaishouFeifeiProductListResult = ReturnType<typeof mapKuaishouFeifeiProductList>
|
||||
|
||||
export async function listKuaishouFeifeiProducts(input: {
|
||||
page?: number
|
||||
perPage?: number
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
import test from 'node:test'
|
||||
import assert from 'node:assert/strict'
|
||||
|
||||
import { resolveKuaishouFeifeiProductByName } from './product-rule-service.js'
|
||||
|
||||
test('resolveKuaishouFeifeiProductByName matches generated name-code mapping by normalized name', () => {
|
||||
const match = resolveKuaishouFeifeiProductByName('【自动发货】套装-Alan Walker', {
|
||||
rules: [
|
||||
{
|
||||
productName: '套装-Alan Walker',
|
||||
normalizedProductName: '套装 alan walker',
|
||||
productCode: '10000013',
|
||||
skuName: '套装-Alan Walker',
|
||||
},
|
||||
],
|
||||
})
|
||||
|
||||
assert.equal(match?.productCode, '10000013')
|
||||
assert.equal(match?.matchMode, 'kuaishou_feifei_name')
|
||||
assert.equal(match?.skuName, '套装-Alan Walker')
|
||||
assert.equal(match?.normalizedProductName, '套装 alan walker')
|
||||
})
|
||||
@@ -3,7 +3,7 @@ import { getKuaishouFeifeiConfig } from './config.js'
|
||||
import type { KuaishouFeifeiProductRule } from '../../../types/runtime-config.js'
|
||||
|
||||
export type KuaishouFeifeiProductMatch = {
|
||||
matchMode: 'kuaishou_feifei_rule'
|
||||
matchMode: 'kuaishou_feifei_name'
|
||||
productName: string
|
||||
normalizedProductName: string
|
||||
productCode: string
|
||||
@@ -37,7 +37,7 @@ export function resolveKuaishouFeifeiProductByName(
|
||||
}
|
||||
|
||||
return {
|
||||
matchMode: 'kuaishou_feifei_rule',
|
||||
matchMode: 'kuaishou_feifei_name',
|
||||
productName: String(matched.productName || productName || '').trim(),
|
||||
normalizedProductName,
|
||||
productCode,
|
||||
|
||||
Reference in New Issue
Block a user