feat(ai-image): AI生图开发

This commit is contained in:
2026-09-27 22:40:52 +08:00
parent e670d06ca6
commit a29dd92676
105 changed files with 9943 additions and 361 deletions
+77
View File
@@ -132,6 +132,10 @@ describe('商品编辑 M3.0–M3.3 HTTP 集成测试', () => {
'DELETE FROM atlas.collection_asset_transfers WHERE workspace_id = $1',
[workspaceId],
);
await tx.query(
'DELETE FROM atlas.media_aspect_ratio_derivatives WHERE workspace_id = $1',
[workspaceId],
);
await tx.query(
'DELETE FROM atlas.product_image_imports WHERE workspace_id = $1',
[workspaceId],
@@ -1985,6 +1989,79 @@ describe('商品编辑 M3.0–M3.3 HTTP 集成测试', () => {
assert.equal(privateImport.body.error.code, 'PRODUCT_IMAGE_IMPORT_INVALID');
});
it('当前商品的 3:4 处理记录可重新加入 SKU 并保存', async () => {
const before = await request(`/api/products/${first.productId}/editor`, {
headers: headers({ 'X-Atlas-Platform-Id': platformId }),
});
const sourceAssetId = before.body.data.variants[0].media[0].asset_id as string;
const outputAssetId = await database.workspaceTransaction(workspaceId, async (tx) => {
const output = await tx.query<{ id: string }>(
`INSERT INTO atlas.media_assets
(workspace_id, storage_provider, bucket, object_key, content_type,
byte_size, checksum_sha256, width_px, height_px)
VALUES ($1, 'test', 'test', $2, 'image/jpeg', 1024, $3, 1200, 1600)
RETURNING id`,
[workspaceId, `processed/${randomUUID()}.jpg`, randomUUID().replaceAll('-', '').repeat(2)],
);
await tx.query(
`INSERT INTO atlas.media_aspect_ratio_derivatives
(workspace_id, product_id, source_asset_id, output_asset_id, status, completed_at)
VALUES ($1, $2, $3, $4, 'succeeded', now())`,
[workspaceId, first.productId, sourceAssetId, output.rows[0].id],
);
return output.rows[0].id;
});
const snapshot = before.body.data;
const payload = {
ai_source_material: snapshot.source_material,
ai_source_overridden_fields: snapshot.source_material_state.overridden_fields,
common: { ...snapshot.product.common,
default_physical_spec: snapshot.product.default_physical_spec,
default_package_spec: snapshot.product.default_package_spec },
profile: {
target_store_id: snapshot.profile.target_store.id,
fulfillment: snapshot.profile.fulfillment,
title: snapshot.profile.title,
description: snapshot.profile.description,
tags: snapshot.profile.tags,
platform_product_category_id: snapshot.profile.category.id,
category_source: snapshot.profile.category.source,
},
attributes: snapshot.attributes.map((attribute: any) => ({
scope: attribute.scope, variant_id: attribute.variant_id,
platform_attribute_id: attribute.platform_attribute_id,
complex_id: attribute.complex_id, values: attribute.values,
language_code: attribute.language_code, source: attribute.source,
confirmation_status: attribute.confirmation_status,
})),
variants: snapshot.variants.map((item: any) => ({
id: item.id, client_key: null, copy_from_variant_id: null,
display_name: item.display_name, source_sku: item.source_sku,
specification_values: item.specification_values,
purchase_cost_cny: item.purchase_cost_cny,
tags: item.tags, description: item.description,
physical_spec: item.physical_spec, package_spec: item.package_spec,
offer_id: item.listing.offer_id,
media_asset_ids: item.id === snapshot.variants[0].id
? [outputAssetId] : item.media.map((media: any) => media.asset_id),
})),
};
const saved = await request(`/api/products/${first.productId}/editor-draft`, {
method: 'PUT',
headers: headers({
'X-Atlas-Platform-Id': platformId,
'Idempotency-Key': randomUUID(),
'If-Match': `"${snapshot.product.revision}:${snapshot.profile.revision}"`,
}),
body: JSON.stringify(payload),
});
assert.equal(saved.response.status, 200);
const after = await request(`/api/products/${first.productId}/editor`, {
headers: headers({ 'X-Atlas-Platform-Id': platformId }),
});
assert.equal(after.body.data.variants[0].media[0].asset_id, outputAssetId);
});
it('独立保存采集描述会持久化覆盖值并保持幂等', async () => {
const created = await createCollectionItem('独立保存描述测试商品');
const entered = await request(