feat(edit): 编辑页上报ozon 开发
This commit is contained in:
@@ -61,3 +61,7 @@ test('不存在库存数据和非 HTTPS 图片不能伪装成有效平台数据'
|
||||
assert.equal(product.image_summary.url, null);
|
||||
assert.equal(product.selling_price, null);
|
||||
});
|
||||
|
||||
test('v3 商品详情按官方顶层 items 读取', () => {
|
||||
assert.deepEqual(parseOzonInfoItems({items:[{id:123,offer_id:'a'}]}),[{id:123,offer_id:'a'}]);
|
||||
});
|
||||
|
||||
@@ -56,10 +56,8 @@ test('按 SKU 将 SPU 默认包装规格展开为 Ozon items[]', () => {
|
||||
name: '测试商品',
|
||||
description_category_id: 17028922,
|
||||
type_id: 91234,
|
||||
price: 199.9,
|
||||
price: '199.90',
|
||||
currency_code: 'RUB',
|
||||
description: '商品描述',
|
||||
barcode: '1234567890123',
|
||||
images: ['https://cdn.example.test/a.jpg'],
|
||||
attributes: [
|
||||
{ id: 85, complex_id: 0, values: [{ value: '品牌' }] },
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
import assert from 'node:assert/strict';
|
||||
import { createHash } from 'node:crypto';
|
||||
import { test } from 'node:test';
|
||||
import sharp from 'sharp';
|
||||
import {
|
||||
qiniuPrivateDownloadUrl,
|
||||
prepareOzonImages,
|
||||
} from '../src/ozon-publish-media.ts';
|
||||
|
||||
const config = {
|
||||
provider: 'qiniu_kodo',
|
||||
bucket: 'test-bucket',
|
||||
downloadOrigin: 'https://images.example.test',
|
||||
accessKey: 'MY_ACCESS_KEY',
|
||||
secretKey: 'MY_SECRET_KEY',
|
||||
ttlSeconds: 86400,
|
||||
};
|
||||
test('七牛签名包含秒级有效期,HMAC-SHA1 与独立计算向量一致', () => {
|
||||
assert.equal(
|
||||
qiniuPrivateDownloadUrl(
|
||||
config.downloadOrigin,
|
||||
'resource/flower.jpg',
|
||||
1451491200,
|
||||
config.accessKey,
|
||||
config.secretKey,
|
||||
),
|
||||
'https://images.example.test/resource/flower.jpg?e=1451491200&token=MY_ACCESS_KEY:qUjF3pEnCC9cpm3MaHVikOTEevE=',
|
||||
);
|
||||
assert.throws(
|
||||
() =>
|
||||
qiniuPrivateDownloadUrl(
|
||||
'http://localhost:9000',
|
||||
'a',
|
||||
1451491200,
|
||||
'ak',
|
||||
'sk',
|
||||
),
|
||||
/HTTPS/,
|
||||
);
|
||||
});
|
||||
test('发布 WebP 按 checksum 固定源,转 JPEG 并交付独立发布路径', async () => {
|
||||
const bytes = await sharp({
|
||||
create: {
|
||||
width: 20,
|
||||
height: 20,
|
||||
channels: 4,
|
||||
background: { r: 100, g: 80, b: 60, alpha: 1 },
|
||||
},
|
||||
})
|
||||
.webp()
|
||||
.toBuffer();
|
||||
const checksum = createHash('sha256').update(bytes).digest('hex');
|
||||
const writes = [];
|
||||
const storage = {
|
||||
send: async (command) => {
|
||||
if (command.constructor.name === 'GetObjectCommand')
|
||||
return {
|
||||
Body: (async function* () {
|
||||
yield bytes;
|
||||
})(),
|
||||
};
|
||||
writes.push(command.input);
|
||||
return {};
|
||||
},
|
||||
};
|
||||
const snapshot = {
|
||||
media: [
|
||||
{
|
||||
asset_id: 'asset-a',
|
||||
checksum_sha256: checksum,
|
||||
bucket: 'test-bucket',
|
||||
object_key: 'private/original',
|
||||
storage_provider: 'qiniu_kodo',
|
||||
content_type: 'image/webp',
|
||||
},
|
||||
],
|
||||
};
|
||||
const urls = await prepareOzonImages(
|
||||
storage,
|
||||
snapshot,
|
||||
'workspace-a',
|
||||
'job-a',
|
||||
config,
|
||||
);
|
||||
assert.equal(writes.length, 1);
|
||||
assert.equal(writes[0].ContentType, 'image/jpeg');
|
||||
assert.equal((await sharp(writes[0].Body).metadata()).format, 'jpeg');
|
||||
assert.ok(writes[0].Key.startsWith('publishing/workspace-a/job-a/'));
|
||||
assert.ok(!urls[0].includes('private/original'));
|
||||
assert.ok(new URL(urls[0]).searchParams.get('token'));
|
||||
snapshot.media[0].checksum_sha256 = 'changed';
|
||||
await assert.rejects(
|
||||
prepareOzonImages(storage, snapshot, 'workspace-a', 'job-a', config),
|
||||
/图片内容已变更/,
|
||||
);
|
||||
assert.equal(writes.length, 1);
|
||||
});
|
||||
@@ -1,6 +1,6 @@
|
||||
import assert from 'node:assert/strict';
|
||||
import { test } from 'node:test';
|
||||
import { makeModel, validateCopy, validatePackage } from '../src/product-ai.ts';
|
||||
import { deepSeekJson, makeModel, validateCopy, validatePackage } from '../src/product-ai.ts';
|
||||
|
||||
test('同一商品模型只由类目和稳定商品 ID 确定', () => {
|
||||
const category = { name: '手办', path: '玩具 / 手办' };
|
||||
@@ -113,3 +113,70 @@ test('包装字段名称可作为 1688 分离尺寸和重量证据', () => {
|
||||
};
|
||||
assert.equal(validatePackage(proposal, source)?.height_mm, '30');
|
||||
});
|
||||
|
||||
test('DeepSeek 结构化请求关闭思考,截断后提高输出上限重试', async () => {
|
||||
const originalFetch = globalThis.fetch;
|
||||
const originalKey = process.env.DEEPSEEK_API_KEY;
|
||||
const requests = [];
|
||||
process.env.DEEPSEEK_API_KEY = 'test-key';
|
||||
globalThis.fetch = async (_url, options) => {
|
||||
requests.push(JSON.parse(options.body));
|
||||
const truncated = requests.length === 1;
|
||||
return new Response(
|
||||
JSON.stringify({
|
||||
choices: [
|
||||
{
|
||||
finish_reason: truncated ? 'length' : 'stop',
|
||||
message: { content: truncated ? '' : '{"ok":true}' },
|
||||
},
|
||||
],
|
||||
usage: { total_tokens: 25 },
|
||||
}),
|
||||
{ status: 200 },
|
||||
);
|
||||
};
|
||||
try {
|
||||
assert.deepEqual(await deepSeekJson('输出 JSON', '商品事实'), {
|
||||
data: { ok: true },
|
||||
tokens: 25,
|
||||
});
|
||||
assert.equal(requests.length, 2);
|
||||
assert.deepEqual(requests[0].thinking, { type: 'disabled' });
|
||||
assert.equal(requests[0].max_tokens, 8192);
|
||||
assert.equal(requests[1].max_tokens, 16384);
|
||||
assert.match(requests[1].messages[1].content, /不要只返回空白/);
|
||||
} finally {
|
||||
globalThis.fetch = originalFetch;
|
||||
if (originalKey === undefined) delete process.env.DEEPSEEK_API_KEY;
|
||||
else process.env.DEEPSEEK_API_KEY = originalKey;
|
||||
}
|
||||
});
|
||||
|
||||
test('DeepSeek JSON 模式返回空白内容时调整提示词重试', async () => {
|
||||
const originalFetch = globalThis.fetch;
|
||||
const originalKey = process.env.DEEPSEEK_API_KEY;
|
||||
const requests = [];
|
||||
process.env.DEEPSEEK_API_KEY = 'test-key';
|
||||
globalThis.fetch = async (_url, options) => {
|
||||
requests.push(JSON.parse(options.body));
|
||||
return new Response(
|
||||
JSON.stringify({
|
||||
choices: [{
|
||||
finish_reason: 'stop',
|
||||
message: { content: requests.length === 1 ? ' ' : '{"ok":true}' },
|
||||
}],
|
||||
}),
|
||||
{ status: 200 },
|
||||
);
|
||||
};
|
||||
try {
|
||||
assert.deepEqual((await deepSeekJson('输出 JSON', '商品事实')).data, { ok: true });
|
||||
assert.equal(requests.length, 2);
|
||||
assert.equal(requests[1].max_tokens, 8192);
|
||||
assert.match(requests[1].messages[1].content, /不要只返回空白/);
|
||||
} finally {
|
||||
globalThis.fetch = originalFetch;
|
||||
if (originalKey === undefined) delete process.env.DEEPSEEK_API_KEY;
|
||||
else process.env.DEEPSEEK_API_KEY = originalKey;
|
||||
}
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user