Files
ozon-seller-kit/extension-v2/src/api/client.ts
T

105 lines
3.6 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* 后端 HTTP 客户端 —— 仅 background 使用(有 host_permissions,不受 CORS 约束)。
* 契约对齐 server 端 /api/materials(见 docs/v2/api.md)。
*/
import type { ScanResult } from '../collector/scan';
import type { TextEdits } from '../export/builder';
export interface MaterialsPayload {
product_id: string | null;
source: {
platform: string;
itemId: string | null;
url: string;
collectedAt: number;
};
texts: Array<{ kind: string; content: string; pairs?: Array<{ key: string; value: string }> | null }>;
images: Array<{
groupKey: string;
groupName: string;
variantName?: string | null;
url: string;
index: number;
type: string;
dedupeKey?: string | null;
}>;
refererOrigin?: string;
}
/** 用(可能已二次修改的)文本 + 已勾选图片,组装 /api/materials 请求体 */
export function buildMaterialsPayload(
result: ScanResult,
selectedKeys: Set<string>,
edits?: TextEdits,
): MaterialsPayload {
const text = (kind: string) => result.texts.find((t) => t.kind === kind)?.content ?? '';
const title = edits?.title ?? text('title');
const price = edits?.price ?? text('price');
const brand = edits?.brand ?? text('brand');
const desc = edits?.desc ?? text('desc');
const sellingPoints = edits?.sellingPoints ?? text('selling_point');
const params: Array<{ key: string; value: string }> = [
...(edits?.params ?? result.texts.find((t) => t.kind === 'params')?.pairs ?? []),
];
// 包装重量 / 包装尺寸合并进参数(后端存到 raw.paramsstudio 里再映射为 Ozon 字段)
if (edits?.weight) params.push({ key: '包装重量', value: edits.weight });
const dimSuffix = edits?.dimsUnit === 'mm' ? ' mm' : ' cm';
if (edits?.dims?.l) params.push({ key: '包装长度', value: `${edits.dims.l}${dimSuffix}` });
if (edits?.dims?.w) params.push({ key: '包装宽度', value: `${edits.dims.w}${dimSuffix}` });
if (edits?.dims?.h) params.push({ key: '包装高度', value: `${edits.dims.h}${dimSuffix}` });
const texts: MaterialsPayload['texts'] = [];
if (title) texts.push({ kind: 'title', content: title });
if (price) texts.push({ kind: 'price', content: price });
if (brand) texts.push({ kind: 'brand', content: brand });
if (params.length) texts.push({ kind: 'params', content: '', pairs: params });
if (sellingPoints) texts.push({ kind: 'selling_point', content: sellingPoints });
if (desc) texts.push({ kind: 'desc', content: desc });
const images = result.images
.filter((img) => selectedKeys.has(img.key))
.map((img) => ({
groupKey: img.groupKey,
groupName: img.groupName,
variantName: img.variantName ?? null,
url: img.url,
index: img.index,
type: img.type,
dedupeKey: img.url,
}));
return {
product_id: null,
source: {
platform: result.platform,
itemId: result.itemId,
url: result.url,
collectedAt: result.scannedAt,
},
texts,
images,
refererOrigin: 'https://www.ozon.ru',
};
}
export async function uploadMaterials(
baseUrl: string,
token: string,
payload: MaterialsPayload,
): Promise<{ product_id: string; stage: string; assets_queued: number }> {
const headers: Record<string, string> = { 'Content-Type': 'application/json' };
if (token) headers.Authorization = `Bearer ${token}`; // 单用户宽松模式:token 可空
const res = await fetch(`${baseUrl.replace(/\/$/, '')}/api/materials`, {
method: 'POST',
headers,
body: JSON.stringify(payload),
});
const data = await res.json().catch(() => ({}));
if (!res.ok) {
throw new Error(data?.detail || `上传失败 HTTP ${res.status}`);
}
return data;
}