206 lines
6.9 KiB
JavaScript
206 lines
6.9 KiB
JavaScript
'use strict';
|
|
|
|
const assert = require('node:assert/strict');
|
|
const test = require('node:test');
|
|
const {
|
|
findCategoryMapping,
|
|
mergePropertyRecommendations,
|
|
resolveExcelMappedProperties,
|
|
} = require('../src/services/pdd1688PropertyMappingService');
|
|
const {
|
|
collectPddAttributeImages,
|
|
preparePddDraftAttributes,
|
|
recognizeImageText,
|
|
} = require('../src/services/pddDraftAttributePreparationService');
|
|
const {
|
|
listPddPublishingShops,
|
|
resolvePddPublishingShop,
|
|
} = require('../src/services/pddPublishingShopService');
|
|
const {
|
|
DEFAULT_PDD_PROPERTY_MODEL,
|
|
normalizePddPropertyRecommendations,
|
|
} = require('../src/services/selectionFissionAiSettings');
|
|
|
|
function createItem() {
|
|
return {
|
|
selectedProductTitle: '现代简约实木餐桌',
|
|
metadata: {
|
|
sourceRow: {
|
|
product: {
|
|
mainImages: ['https://img.example/shared.jpg', 'https://img.example/main-2.jpg'],
|
|
detailImages: ['https://img.example/shared.jpg', 'https://img.example/detail-1.jpg'],
|
|
attributes: [{ name: '主材质', value: '实木' }],
|
|
},
|
|
},
|
|
},
|
|
mainImageGeneration: { rows: [] },
|
|
skuSceneImages: {
|
|
selectedSkuCodes: ['SKU-1', 'SKU-2'],
|
|
rows: [
|
|
{ skuCode: 'SKU-1', generatedImageUrl: 'https://img.example/sku-shared.jpg' },
|
|
{ skuCode: 'SKU-2', generatedImageUrl: 'https://img.example/sku-shared.jpg' },
|
|
],
|
|
},
|
|
skuSnapshot: {
|
|
rows: [
|
|
{ skuCode: 'SKU-1', skuImageUrl: 'https://img.example/source-1.jpg' },
|
|
{ skuCode: 'SKU-2', skuImageUrl: 'https://img.example/source-2.jpg' },
|
|
],
|
|
},
|
|
pricingRows: [],
|
|
};
|
|
}
|
|
|
|
test('Excel contains the configured category mapping and locks mapped values', () => {
|
|
assert.equal(findCategoryMapping({ categoryName: '普通餐桌' }).categoryName, '餐桌');
|
|
const categoryRule = {
|
|
properties: [{
|
|
refPid: 373,
|
|
name: '主材质',
|
|
values: [{ vid: 12859, value: '木' }, { vid: 12860, value: '金属' }],
|
|
}],
|
|
};
|
|
const excel = resolveExcelMappedProperties({
|
|
categoryName: '普通餐桌',
|
|
sourceAttributes: [{ name: '主材质', value: '进口橡胶木实木' }],
|
|
categoryRule,
|
|
});
|
|
assert.deepEqual(excel.properties.map((row) => ({
|
|
refPid: row.refPid,
|
|
vid: row.vid,
|
|
value: row.value,
|
|
source: row.source,
|
|
locked: row.locked,
|
|
})), [{ refPid: 373, vid: 12859, value: '木', source: 'excel', locked: true }]);
|
|
|
|
const merged = mergePropertyRecommendations(categoryRule, excel, [{
|
|
refPid: 373,
|
|
vid: 12860,
|
|
value: '金属',
|
|
evidence: 'AI错误结果',
|
|
}]);
|
|
assert.equal(merged[0].value, '木');
|
|
assert.equal(merged[0].source, 'excel');
|
|
});
|
|
|
|
test('AI recommendations are limited to live PDD property IDs and enum values', () => {
|
|
const categoryRule = {
|
|
properties: [{
|
|
refPid: 1628,
|
|
name: '环保等级',
|
|
values: [{ vid: 368832, value: 'E0级' }],
|
|
}],
|
|
};
|
|
assert.equal(DEFAULT_PDD_PROPERTY_MODEL, 'qwen3.8-max');
|
|
assert.deepEqual(normalizePddPropertyRecommendations({
|
|
properties: [
|
|
{ refPid: 1628, vid: 368832, value: 'E0级', confidence: 0.9 },
|
|
{ refPid: 1628, vid: 999, value: '虚构等级' },
|
|
{ refPid: 9999, value: '虚构属性' },
|
|
],
|
|
}, categoryRule), [{
|
|
refPid: 1628,
|
|
vid: 368832,
|
|
value: 'E0级',
|
|
propertyName: '环保等级',
|
|
evidence: '',
|
|
confidence: 0.9,
|
|
}]);
|
|
});
|
|
|
|
test('main, detail and SKU OCR images are globally deduplicated', () => {
|
|
const rows = collectPddAttributeImages(createItem());
|
|
assert.deepEqual(rows.map((row) => [row.kind, row.index, row.url]), [
|
|
['main', 1, 'https://img.example/shared.jpg'],
|
|
['main', 2, 'https://img.example/main-2.jpg'],
|
|
['detail', 1, 'https://img.example/detail-1.jpg'],
|
|
['sku', 1, 'https://img.example/sku-shared.jpg'],
|
|
]);
|
|
});
|
|
|
|
test('OCR request enables X-Image-OCR and extracts data.ocr', async () => {
|
|
let request;
|
|
const result = await recognizeImageText({
|
|
kind: 'detail',
|
|
label: '详情图',
|
|
index: 1,
|
|
url: 'https://img.example/detail.jpg',
|
|
}, {
|
|
config: { endpointUrl: 'https://ocr.example/query', token: 'secret', timeoutMs: 1000 },
|
|
httpClient: {
|
|
post: async (url, body, config) => {
|
|
request = { url, body, config };
|
|
return {
|
|
status: 200,
|
|
data: {
|
|
success: true,
|
|
request_id: 'request-1',
|
|
data: { ocr: { requested: true, success: true, fullText: '实木', textCount: 1 } },
|
|
},
|
|
};
|
|
},
|
|
},
|
|
});
|
|
assert.equal(request.config.headers['X-Image-OCR'], 'true');
|
|
assert.equal(request.body.image_url, 'https://img.example/detail.jpg');
|
|
assert.equal(result.fullText, '实木');
|
|
assert.equal(result.requestId, 'request-1');
|
|
});
|
|
|
|
test('attribute workflow reports every image before AI confirmation', async () => {
|
|
const progress = [];
|
|
const result = await preparePddDraftAttributes({
|
|
item: createItem(),
|
|
categoryName: '普通餐桌',
|
|
categoryPathNames: ['住宅家具', '桌子', '普通餐桌'],
|
|
productTitle: '现代简约实木餐桌',
|
|
sourceCategory: '餐桌',
|
|
sourceAttributes: [{ name: '主材质', value: '实木' }],
|
|
categoryRule: {
|
|
properties: [{ refPid: 373, name: '主材质', values: [{ vid: 12859, value: '木' }] }],
|
|
},
|
|
}, {
|
|
config: { endpointUrl: 'https://ocr.example/query', token: 'secret', timeoutMs: 1000 },
|
|
httpClient: {
|
|
post: async () => ({
|
|
status: 200,
|
|
data: { success: true, data: { ocr: { requested: true, success: true, fullText: '实木' } } },
|
|
}),
|
|
},
|
|
updateProgress: async (row) => progress.push(row),
|
|
generateRecommendations: async () => ({ model: 'qwen3.8-max', properties: [] }),
|
|
});
|
|
assert.equal(progress.filter((row) => row.stage === 'ocr' && row.currentImage).length, 4);
|
|
assert.equal(progress.at(-1).stage, 'confirmation');
|
|
assert.equal(result.properties[0].source, 'excel');
|
|
});
|
|
|
|
test('publishing shop list exposes only safe fields and resolves credentials server-side', async () => {
|
|
const row = {
|
|
shopName: '拼多多-HX旗舰店107',
|
|
nick: 'pdd86127727389',
|
|
tokenExpiredTime: new Date('2027-01-01T00:00:00Z'),
|
|
mallId: '861277273',
|
|
accessToken: 'access-token',
|
|
refreshToken: 'refresh-token',
|
|
clientId: 'client-id',
|
|
clientSecret: 'client-secret',
|
|
serviceId: 639,
|
|
};
|
|
const calls = [];
|
|
const query = async (sql, params) => {
|
|
calls.push({ sql, params });
|
|
return [row];
|
|
};
|
|
const list = await listPddPublishingShops({ query });
|
|
assert.equal(list.rows[0].shopName, row.shopName);
|
|
assert.equal(list.rows[0].accessToken, undefined);
|
|
assert.match(calls[0].sql, /shop_status = N'正常'/);
|
|
assert.match(calls[0].sql, /NOT LIKE N'%已关闭%'/);
|
|
|
|
const resolved = await resolvePddPublishingShop({ shopId: row.nick }, { query });
|
|
assert.equal(resolved.shop.shopId, row.nick);
|
|
assert.equal(resolved.credential.accessToken, 'access-token');
|
|
assert.equal(resolved.credential.clientSecret, 'client-secret');
|
|
});
|