feat(ai-image): AI生图开发
This commit is contained in:
@@ -4,7 +4,7 @@ import {
|
||||
isOzonProductPage,
|
||||
type OzonPageJsonFetcher,
|
||||
} from './ozon-parser.js';
|
||||
import { collectPlatformDomAssets } from './product-media.js';
|
||||
import { collectPlatformDomAssets, normaliseImageUrl } from './product-media.js';
|
||||
import { formatCollectedDescription } from './description-format.js';
|
||||
|
||||
type SourcePlatform = '1688' | 'taobao' | 'tmall' | 'ozon';
|
||||
@@ -49,7 +49,10 @@ export async function collectCurrentProduct(
|
||||
|
||||
function collect1688(root: Document, pageUrl: string, bridgedState?: unknown): CollectedProductPayload {
|
||||
const state = [bridgedState, ...pageState(root)];
|
||||
const skuMap = findFirst(state, 'skuMapOriginal');
|
||||
const skuInfoMap = findFirst(state, 'skuInfoMap');
|
||||
const skuMap = isRecord(skuInfoMap) && Object.keys(skuInfoMap).length
|
||||
? skuInfoMap
|
||||
: findFirst(state, 'skuMapOriginal');
|
||||
const skuProps = findFirst(state, 'skuProps');
|
||||
const axes = parse1688Axes(skuProps);
|
||||
const evidence: CollectedProductPayload['evidence'] = [
|
||||
@@ -70,32 +73,29 @@ function collect1688(root: Document, pageUrl: string, bridgedState?: unknown): C
|
||||
evidence.push({
|
||||
ref: '1688:sku-map',
|
||||
kind: 'state',
|
||||
path: 'skuMapOriginal',
|
||||
path: isRecord(skuInfoMap) ? 'skuModel.skuInfoMap' : 'skuMapOriginal',
|
||||
raw_value: `组合记录 ${skuRows.length} 条`,
|
||||
});
|
||||
for (const [combination, row] of skuRows) {
|
||||
if (!isRecord(row)) continue;
|
||||
const sourceSku = stringAt(row, ['skuId', 'sku_id', 'skuCode']);
|
||||
// A skuMapOriginal entry itself is a source-provided sellable-combination
|
||||
// record. Never expand it with the Cartesian product of skuProps.
|
||||
const specAttrs = stringAt(row, ['specAttrs', 'propPath']) ?? combination;
|
||||
const selections = selectionsFor1688Combination(specAttrs, axes);
|
||||
// 来源记录本身证明可售组合,不能用规格轴的笛卡尔积扩充 SKU。
|
||||
const sourceVariantKey = sourceSku ?? `1688:${combination}`;
|
||||
if (!combination || combination === 'undefined') continue;
|
||||
variants.push({
|
||||
sourceVariantKey,
|
||||
...(sourceSku ? { sourceSku } : {}),
|
||||
displayName:
|
||||
selectionsForCombination(combination, axes)
|
||||
selections
|
||||
.map((item) => item.valueName)
|
||||
.join(' / ') || `1688 SKU ${sourceSku ?? combination}`,
|
||||
selections: selectionsForCombination(combination, axes),
|
||||
...(moneyAt(row, ['discountPrice', 'price', 'salePrice'])
|
||||
.join(' / ') || specAttrs,
|
||||
selections,
|
||||
...(moneyAt(row, ['price'])
|
||||
? {
|
||||
sourcePrice: {
|
||||
amount: moneyAt(row, [
|
||||
'discountPrice',
|
||||
'price',
|
||||
'salePrice',
|
||||
])!,
|
||||
amount: moneyAt(row, ['price'])!,
|
||||
currencyCode: 'CNY',
|
||||
},
|
||||
}
|
||||
@@ -105,7 +105,7 @@ function collect1688(root: Document, pageUrl: string, bridgedState?: unknown): C
|
||||
});
|
||||
}
|
||||
}
|
||||
return basePayload(
|
||||
const payload = basePayload(
|
||||
'1688',
|
||||
offerId(pageUrl),
|
||||
root,
|
||||
@@ -113,9 +113,29 @@ function collect1688(root: Document, pageUrl: string, bridgedState?: unknown): C
|
||||
axes,
|
||||
uniqueVariants(variants),
|
||||
evidence,
|
||||
'1688-structured-2.0.1',
|
||||
'1688-structured-2.1.0',
|
||||
state,
|
||||
);
|
||||
// SKU 图片由规格值和真实可售组合关联,独立于页面当时选中的 DOM 状态。
|
||||
for (const variant of payload.variants) {
|
||||
for (const selection of variant.selections) {
|
||||
const rawUrl = axes.find((axis) => axis.key === selection.axisKey)
|
||||
?.values.find((value) => value.key === selection.valueKey)?.image_url;
|
||||
const url = rawUrl && normaliseImageUrl(rawUrl, pageUrl, '1688');
|
||||
if (!url || payload.assets.length >= 500) continue;
|
||||
if (payload.assets.some((asset) => asset.variantSourceKey === variant.sourceVariantKey && asset.url === url)) continue;
|
||||
payload.assets.push({
|
||||
url,
|
||||
mediaType: 'image',
|
||||
role: 'source_sku',
|
||||
relation: 'axis_value',
|
||||
variantSourceKey: variant.sourceVariantKey,
|
||||
sortOrder: payload.assets.length,
|
||||
evidenceRef: '1688:sku-map',
|
||||
});
|
||||
}
|
||||
}
|
||||
return payload;
|
||||
}
|
||||
|
||||
function collectTaobao(
|
||||
@@ -411,13 +431,14 @@ function pageState(root: Document) {
|
||||
}
|
||||
}
|
||||
for (const marker of [
|
||||
'skuInfoMap',
|
||||
'skuMapOriginal',
|
||||
'skuBase',
|
||||
'skuCore',
|
||||
'skuProps',
|
||||
]) {
|
||||
const object = objectAfterMarker(text, marker);
|
||||
if (object) all.push(object);
|
||||
if (object) all.push({ [marker]: object });
|
||||
}
|
||||
}
|
||||
return all;
|
||||
@@ -589,6 +610,19 @@ function selectionsForCombination(combination: string, axes: Axis[]) {
|
||||
: [];
|
||||
});
|
||||
}
|
||||
function selectionsFor1688Combination(combination: string, axes: Axis[]) {
|
||||
const selected = selectionsForCombination(combination, axes);
|
||||
if (selected.length === axes.length) return selected;
|
||||
const parts = combination.split(/[;,&|]/).map((part) => part.trim()).filter(Boolean);
|
||||
return axes.flatMap((axis) => {
|
||||
const existing = selected.find((item) => item.axisKey === axis.key);
|
||||
if (existing) return [existing];
|
||||
const value = axis.values.find((item) =>
|
||||
parts.some((part) => part === item.name || part === `${axis.name}:${item.name}`)
|
||||
);
|
||||
return value ? [{ axisKey: axis.key, valueKey: value.key, valueName: value.name }] : [];
|
||||
});
|
||||
}
|
||||
function stringAt(value: Json, keys: string[]) {
|
||||
for (const key of keys)
|
||||
if (typeof value[key] === 'string' || typeof value[key] === 'number')
|
||||
|
||||
Reference in New Issue
Block a user