360 lines
11 KiB
JavaScript
360 lines
11 KiB
JavaScript
/**
|
||
* 测试脚本:使用素材图片生成电商图片
|
||
* 运行方式: node test-generate.js
|
||
*/
|
||
|
||
require('dotenv').config();
|
||
const axios = require('axios');
|
||
const fs = require('fs');
|
||
const path = require('path');
|
||
const { S3Client, PutObjectCommand } = require('@aws-sdk/client-s3');
|
||
|
||
// 配置
|
||
const SERVER_URL = 'http://localhost:3000';
|
||
const OUTPUT_DIR = path.join(__dirname, 'output');
|
||
const MATERIAL_DIR = path.join(__dirname, '素材/素材/已有的素材');
|
||
|
||
// R2客户端
|
||
const r2Client = new S3Client({
|
||
region: 'auto',
|
||
endpoint: `https://${process.env.R2_ACCOUNT_ID}.r2.cloudflarestorage.com`,
|
||
credentials: {
|
||
accessKeyId: process.env.R2_ACCESS_KEY_ID,
|
||
secretAccessKey: process.env.R2_SECRET_ACCESS_KEY,
|
||
},
|
||
forcePathStyle: true,
|
||
});
|
||
|
||
/**
|
||
* 上传图片到R2
|
||
*/
|
||
async function uploadToR2(filePath) {
|
||
const fileName = `test-${Date.now()}-${path.basename(filePath)}`;
|
||
const fileBuffer = fs.readFileSync(filePath);
|
||
const contentType = filePath.endsWith('.png') ? 'image/png' : 'image/jpeg';
|
||
|
||
const command = new PutObjectCommand({
|
||
Bucket: process.env.R2_BUCKET_NAME || 'ai-flow',
|
||
Key: fileName,
|
||
Body: fileBuffer,
|
||
ContentType: contentType,
|
||
});
|
||
|
||
await r2Client.send(command);
|
||
|
||
const publicUrl = process.env.R2_PUBLIC_DOMAIN
|
||
? `${process.env.R2_PUBLIC_DOMAIN}/${fileName}`
|
||
: `https://pub-${process.env.R2_ACCOUNT_ID}.r2.dev/${fileName}`;
|
||
|
||
console.log(` 上传成功: ${fileName}`);
|
||
return publicUrl;
|
||
}
|
||
|
||
/**
|
||
* 下载图片到本地
|
||
*/
|
||
async function downloadImage(url, outputPath) {
|
||
try {
|
||
const response = await axios.get(url, { responseType: 'arraybuffer', timeout: 60000 });
|
||
fs.writeFileSync(outputPath, response.data);
|
||
console.log(` 下载成功: ${path.basename(outputPath)}`);
|
||
return true;
|
||
} catch (error) {
|
||
console.error(` 下载失败: ${error.message}`);
|
||
return false;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 主函数
|
||
*/
|
||
async function main() {
|
||
console.log('\n========================================');
|
||
console.log('Touchdog 电商图片生成测试');
|
||
console.log('========================================\n');
|
||
|
||
// 确保输出目录存在
|
||
if (!fs.existsSync(OUTPUT_DIR)) {
|
||
fs.mkdirSync(OUTPUT_DIR, { recursive: true });
|
||
}
|
||
|
||
// 1. 获取素材图片
|
||
console.log('📁 Step 1: 读取素材图片...');
|
||
const materialFiles = fs.readdirSync(MATERIAL_DIR)
|
||
.filter(f => /\.(jpg|jpeg|png)$/i.test(f))
|
||
.slice(0, 3); // 取前3张作为参考
|
||
|
||
console.log(` 找到 ${materialFiles.length} 张素材图片`);
|
||
|
||
// 2. 上传素材到R2
|
||
console.log('\n📤 Step 2: 上传素材到R2...');
|
||
const refImageUrls = [];
|
||
|
||
for (const file of materialFiles) {
|
||
try {
|
||
const filePath = path.join(MATERIAL_DIR, file);
|
||
const url = await uploadToR2(filePath);
|
||
refImageUrls.push(url);
|
||
} catch (error) {
|
||
console.error(` 上传失败 ${file}: ${error.message}`);
|
||
}
|
||
}
|
||
|
||
if (refImageUrls.length === 0) {
|
||
console.error('❌ 没有成功上传任何参考图片,退出');
|
||
process.exit(1);
|
||
}
|
||
|
||
console.log(` 成功上传 ${refImageUrls.length} 张参考图`);
|
||
|
||
// 3. 构建SKU数据
|
||
console.log('\n📝 Step 3: 构建SKU数据...');
|
||
const sku = {
|
||
sku_id: 'TD-EC-TEST-001',
|
||
product_name: 'Cat Soft Cone Collar',
|
||
product_name_cn: '伊丽莎白圈',
|
||
brand: 'Touchdog',
|
||
color: {
|
||
name: 'Ice Blue',
|
||
name_cn: '冰蓝色',
|
||
series: 'Iridescent',
|
||
hex: '#B0E0E6',
|
||
edge_color: '#7FDBDB'
|
||
},
|
||
selling_points: [
|
||
{
|
||
key: 'lightweight',
|
||
title_en: 'LIGHTER THAN AN EGG',
|
||
title_cn: '极致轻盈',
|
||
value: '65g',
|
||
description_en: 'Cloud-light comfort at only 65g',
|
||
visual_prompt: 'product shown next to an egg for size comparison'
|
||
},
|
||
{
|
||
key: 'waterproof',
|
||
title_en: 'WATERPROOF & EASY WIPE',
|
||
title_cn: '防水易清洁',
|
||
description_en: 'Waterproof PU fabric, stains wipe clean instantly',
|
||
visual_prompt: 'water droplets beading on the surface'
|
||
},
|
||
{
|
||
key: 'breathable',
|
||
title_en: 'BREATHABLE COTTON LINING',
|
||
title_cn: '舒适亲肤',
|
||
description_en: 'Soft cotton inner layer, comfortable for extended wear',
|
||
visual_prompt: 'close-up of soft cotton inner lining'
|
||
},
|
||
{
|
||
key: 'adjustable',
|
||
title_en: 'ADJUSTABLE STRAP',
|
||
title_cn: '可调节',
|
||
description_en: '3-second on/off, adjustable velcro',
|
||
visual_prompt: 'velcro strap being adjusted'
|
||
}
|
||
],
|
||
specs: {
|
||
weight: '65g',
|
||
depth_cm: '24.5',
|
||
sizes: ['XS', 'S', 'M', 'L', 'XL'],
|
||
materials: {
|
||
outer: 'Durable Waterproof PU',
|
||
inner: '95% Cotton + 5% Spandex'
|
||
}
|
||
},
|
||
use_cases: [
|
||
{ name_en: 'Postoperative Care', name_cn: '术后恢复' },
|
||
{ name_en: 'Eye Drop Application', name_cn: '驱虫护理' },
|
||
{ name_en: 'Nail Trimming', name_cn: '指甲修剪' },
|
||
{ name_en: 'Grooming', name_cn: '美容护理' }
|
||
],
|
||
ref_images: refImageUrls
|
||
};
|
||
|
||
console.log(' SKU数据准备完成');
|
||
console.log(` 参考图: ${refImageUrls.length} 张`);
|
||
|
||
// 4. 调用生成API (只生成前3张测试)
|
||
console.log('\n🎨 Step 4: 调用AI生图API...');
|
||
console.log(' (为节省时间,仅生成3张测试图)\n');
|
||
|
||
// 直接调用图像生成API进行测试
|
||
const testPrompts = [
|
||
{
|
||
id: 'Main_01',
|
||
type: '场景首图+卖点摘要',
|
||
prompt: `Professional Amazon main image for Touchdog Cat Soft Cone Collar, 1:1 square format.
|
||
|
||
COMPOSITION:
|
||
- TOP 60%: A beautiful Ragdoll cat wearing the Ice Blue (#B0E0E6) soft cone collar in a cozy modern home interior with warm wood shelving. Natural soft lighting from window. Cat looks comfortable and relaxed. The cone collar is soft fabric with petal-like segments.
|
||
|
||
- BOTTOM 40%: Light blue (#B0E0E6) rounded banner with title "DESIGNED FOR COMFORTABLE RECOVERY" in navy blue text. Below: three small circular images showing:
|
||
1. Product next to an egg showing 65g weight - "LIGHTER THAN AN EGG"
|
||
2. Water droplets on surface - "WATERPROOF & EASY WIPE"
|
||
3. Soft cotton lining detail - "BREATHABLE COTTON LINING"
|
||
|
||
PRODUCT REQUIREMENTS:
|
||
- Soft cone collar with petal-like fabric segments, NOT rigid plastic
|
||
- Color: Ice Blue (#B0E0E6) with teal edge binding (#7FDBDB)
|
||
- Embroidered "TOUCHDOG®" logo visible on right petal (2-3 o'clock position)
|
||
- Waterproof PU outer, soft cotton inner visible at edges
|
||
|
||
STYLE: Clean, professional Amazon listing photo. Warm home aesthetic. High-end pet product photography.
|
||
|
||
--ar 1:1`
|
||
},
|
||
{
|
||
id: 'Main_02',
|
||
type: '产品平铺+细节放大',
|
||
prompt: `Professional Amazon product detail image for Touchdog Cat Soft Cone Collar, 1:1 square format.
|
||
|
||
COMPOSITION:
|
||
- CENTER: Ice Blue (#B0E0E6) soft cone collar displayed flat from above on clean white background. Shows full petal-like segment structure.
|
||
- Two circular detail callout bubbles with thin lines pointing to product:
|
||
1. Left callout: "DURABLE WATERPROOF PU LAYER" - showing the smooth outer texture
|
||
2. Right callout: "DOUBLE-LAYER COMFORT" - showing the soft ribbed edge binding in teal (#7FDBDB)
|
||
|
||
PRODUCT REQUIREMENTS:
|
||
- Soft fabric cone with 8-10 petal segments radiating from center
|
||
- Visible velcro closure strap
|
||
- "TOUCHDOG®" embroidered logo on right side
|
||
- Color: Ice Blue (#B0E0E6) main, teal binding
|
||
|
||
STYLE: Clean white background product photography. Professional Amazon listing style. Even studio lighting.
|
||
|
||
--ar 1:1`
|
||
},
|
||
{
|
||
id: 'APlus_01',
|
||
type: '品牌Banner',
|
||
prompt: `Professional Amazon A+ brand banner image for Touchdog Cat Soft Cone Collar, landscape 970x600 format.
|
||
|
||
COMPOSITION:
|
||
- FULL WIDTH: Warm modern living room scene with a beautiful white cat wearing the Ice Blue soft cone collar, standing gracefully on a light wood surface.
|
||
- LEFT SIDE: Large stylized "TOUCHDOG" brand text in coral/pink color with playful font, paw print decorations
|
||
- BELOW: "CAT SOFT CONE COLLAR" product name text
|
||
|
||
PRODUCT REQUIREMENTS:
|
||
- Cat wearing Ice Blue (#B0E0E6) soft cone collar
|
||
- Petal-like fabric segments visible
|
||
- Collar looks soft and comfortable, not rigid
|
||
|
||
ENVIRONMENT:
|
||
- Cozy modern home interior
|
||
- Soft natural lighting
|
||
- Warm neutral tones (beige, cream, light wood)
|
||
- Professional lifestyle photography feel
|
||
|
||
BRAND ELEMENTS:
|
||
- Touchdog logo with heart-shaped dog icon in red
|
||
- "wow pretty" tagline
|
||
|
||
--ar 3:2`
|
||
}
|
||
];
|
||
|
||
const results = [];
|
||
|
||
for (const testPrompt of testPrompts) {
|
||
console.log(` 生成 ${testPrompt.id}: ${testPrompt.type}...`);
|
||
|
||
try {
|
||
const payload = {
|
||
model: 'gemini-3-pro-image-preview',
|
||
prompt: testPrompt.prompt,
|
||
n: 1,
|
||
size: '1K',
|
||
aspect_ratio: testPrompt.id.startsWith('APlus') ? '3:2' : '1:1',
|
||
image_urls: refImageUrls
|
||
};
|
||
|
||
const response = await axios.post(
|
||
'https://api2img.shubiaobiao.com/v1/images/generations',
|
||
payload,
|
||
{
|
||
headers: {
|
||
'Content-Type': 'application/json',
|
||
'Authorization': `Bearer ${process.env.API_KEY}`
|
||
},
|
||
timeout: 180000
|
||
}
|
||
);
|
||
|
||
// 处理响应 - 可能是URL或base64
|
||
const imageData = response.data.data?.[0];
|
||
const outputPath = path.join(OUTPUT_DIR, `${sku.sku_id}-${testPrompt.id}.jpg`);
|
||
|
||
if (imageData?.b64_json) {
|
||
// Base64格式 - 直接保存
|
||
const imageBuffer = Buffer.from(imageData.b64_json, 'base64');
|
||
fs.writeFileSync(outputPath, imageBuffer);
|
||
console.log(` 保存成功: ${path.basename(outputPath)}`);
|
||
|
||
results.push({
|
||
id: testPrompt.id,
|
||
type: testPrompt.type,
|
||
localPath: outputPath,
|
||
status: 'success'
|
||
});
|
||
|
||
console.log(` ✓ ${testPrompt.id} 生成成功`);
|
||
} else if (imageData?.url) {
|
||
// URL格式 - 下载保存
|
||
await downloadImage(imageData.url, outputPath);
|
||
|
||
results.push({
|
||
id: testPrompt.id,
|
||
type: testPrompt.type,
|
||
url: imageData.url,
|
||
localPath: outputPath,
|
||
status: 'success'
|
||
});
|
||
|
||
console.log(` ✓ ${testPrompt.id} 生成成功`);
|
||
} else {
|
||
throw new Error('No image data in response');
|
||
}
|
||
|
||
// 延迟避免限流
|
||
await new Promise(r => setTimeout(r, 2000));
|
||
|
||
} catch (error) {
|
||
console.error(` ✗ ${testPrompt.id} 生成失败: ${error.message}`);
|
||
results.push({
|
||
id: testPrompt.id,
|
||
type: testPrompt.type,
|
||
status: 'failed',
|
||
error: error.message
|
||
});
|
||
}
|
||
}
|
||
|
||
// 5. 输出结果
|
||
console.log('\n========================================');
|
||
console.log('生成结果汇总');
|
||
console.log('========================================');
|
||
|
||
const successCount = results.filter(r => r.status === 'success').length;
|
||
console.log(`\n成功: ${successCount}/${results.length}`);
|
||
|
||
results.forEach(r => {
|
||
const status = r.status === 'success' ? '✓' : '✗';
|
||
console.log(` ${status} ${r.id} - ${r.type}`);
|
||
if (r.localPath) {
|
||
console.log(` 保存位置: ${r.localPath}`);
|
||
}
|
||
});
|
||
|
||
console.log(`\n📁 输出目录: ${OUTPUT_DIR}`);
|
||
console.log('\n========================================\n');
|
||
|
||
// 保存结果JSON
|
||
fs.writeFileSync(
|
||
path.join(OUTPUT_DIR, 'generation-results.json'),
|
||
JSON.stringify({ sku, results }, null, 2)
|
||
);
|
||
}
|
||
|
||
// 运行
|
||
main().catch(console.error);
|
||
|