/** * POC Workflow V6: 优化Prompt控制策略 * * 核心改进: * 1. 修复素材选择(使用猫咪图而非狗狗图) * 2. 优化Prompt - 更精准地指示AI保持主体不变 * 3. 分类处理不同复杂度的图片 */ require('dotenv').config(); const axios = require('axios'); const fs = require('fs'); const path = require('path'); const sharp = require('sharp'); const { S3Client, PutObjectCommand } = require('@aws-sdk/client-s3'); const imageProcessor = require('./lib/image-processor'); // ============================================================ // 配置 // ============================================================ const API_KEY = 'G9rXx3Ag2Xfa7Gs8zou6t6HqeZ'; const API_BASE = 'https://api.wuyinkeji.com/api'; // 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, }); // ============================================================ // 工具函数 // ============================================================ async function uploadToR2(buffer, filename) { const fileName = `v6-${Date.now()}-${filename}`; await r2Client.send(new PutObjectCommand({ Bucket: process.env.R2_BUCKET_NAME || 'ai-flow', Key: fileName, Body: buffer, ContentType: filename.endsWith('.png') ? 'image/png' : 'image/jpeg', })); return process.env.R2_PUBLIC_DOMAIN ? `${process.env.R2_PUBLIC_DOMAIN}/${fileName}` : `https://${process.env.R2_ACCOUNT_ID}.r2.cloudflarestorage.com/${process.env.R2_BUCKET_NAME}/${fileName}`; } async function submitImageTask(prompt, refImageUrl = null, aspectRatio = '1:1') { const payload = { key: API_KEY, prompt: prompt, aspectRatio: aspectRatio, imageSize: '1K' }; if (refImageUrl) { payload.img_url = refImageUrl; } const response = await axios.post(`${API_BASE}/img/nanoBanana-pro`, payload, { timeout: 30000 }); const taskId = response.data.data?.id; if (!taskId) { throw new Error('No task ID returned'); } return taskId; } async function pollImageResult(taskId) { let attempts = 0; const maxAttempts = 90; while (attempts < maxAttempts) { try { const response = await axios.get(`${API_BASE}/img/drawDetail`, { params: { key: API_KEY, id: taskId }, timeout: 10000 }); const data = response.data.data; if (data && data.status === 2 && data.image_url) { return data.image_url; } else if (data && data.status === 3) { throw new Error('Generation failed: ' + (data.fail_reason || 'Unknown')); } process.stdout.write('.'); await new Promise(r => setTimeout(r, 2000)); attempts++; } catch (error) { if (error.message.includes('Generation failed')) throw error; process.stdout.write('x'); await new Promise(r => setTimeout(r, 3000)); attempts++; } } throw new Error('Timeout'); } async function downloadImage(url) { const response = await axios.get(url, { responseType: 'arraybuffer', timeout: 30000 }); return Buffer.from(response.data); } async function generateAIImage(prompt, refImageUrl = null, aspectRatio = '1:1') { console.log(' 提交AI任务...'); const taskId = await submitImageTask(prompt, refImageUrl, aspectRatio); console.log(` Task ID: ${taskId}`); const imageUrl = await pollImageResult(taskId); console.log('\n ✓ AI生成完成'); return await downloadImage(imageUrl); } // ============================================================ // Prompt模板 - 核心优化 // ============================================================ /** * 构建精准的编辑prompt * 核心策略:明确告诉AI这是"编辑"任务,精确描述要保留的元素 */ function buildEditPrompt(config) { const { subjectDescription, // 主体描述(产品+宠物) preserveElements, // 必须保留的元素 editTasks, // 编辑任务 layoutDescription, // 排版描述 styleGuide, // 风格指南 aspectRatio // 比例 } = config; return ` [IMAGE EDITING TASK - NOT GENERATION] REFERENCE IMAGE ANALYSIS: The reference image shows: ${subjectDescription} CRITICAL PRESERVATION REQUIREMENTS (DO NOT MODIFY): ${preserveElements.map((e, i) => `${i + 1}. ${e}`).join('\n')} EDITING TASKS (ONLY THESE CHANGES ARE ALLOWED): ${editTasks.map((t, i) => `${i + 1}. ${t}`).join('\n')} LAYOUT SPECIFICATION: ${layoutDescription} STYLE GUIDE: ${styleGuide} OUTPUT: ${aspectRatio} ratio, professional Amazon listing quality FINAL CHECK: Before output, verify that the pet and product in the result match the reference image EXACTLY in: - Species, breed, fur color and pattern - Product color (#B5E5E8 ice blue), shape (flower/petal cone), brand text "TOUCHDOG" - Pose and positioning relative to original `.trim(); } // ============================================================ // 12张图的生成逻辑 // ============================================================ /** * Main_01: 场景首图+卖点 */ async function generateMain01(materials, config, outputDir) { console.log('\n🎨 [Main_01] 场景首图+卖点'); const wornBuffer = fs.readFileSync(materials.catWornImage); const wornUrl = await uploadToR2(wornBuffer, 'cat-worn.jpg'); const prompt = buildEditPrompt({ subjectDescription: `A Ragdoll cat (white/cream fur with light brown markings, blue eyes) wearing an ice-blue soft recovery cone collar (brand: TOUCHDOG). The cat is in a home environment.`, preserveElements: [ `The cat MUST remain a Ragdoll with blue eyes, white/cream fur, brown ear markings - EXACTLY as shown`, `The cone collar MUST remain ice-blue (#B5E5E8), flower/petal shaped, with "TOUCHDOG" branding`, `The cat's pose, expression, and the way the collar sits on the cat`, `The product's texture, stitching pattern, and velcro closure detail` ], editTasks: [ `Enhance background to warm, cozy home interior (fireplace, blanket, wooden furniture)`, `Add soft warm lighting with gentle shadows`, `Add a curved blue banner (#4A7C9B) across middle area with text: "DESIGNED FOR COMFORTABLE RECOVERY"`, `Add 3 white feature boxes at bottom with icons and text: - Egg icon + "LIGHTER THAN AN EGG" - Water drop icon + "WATERPROOF & EASY WIPE" - Cloud icon + "BREATHABLE COTTON LINING"`, `Add subtle paw print watermarks in light blue on the banner area` ], layoutDescription: ` - Top 60%: Cat wearing product in enhanced home scene - Middle: Curved banner with main tagline - Bottom 25%: Three feature callout boxes in a row`, styleGuide: `Professional Amazon main image style. Warm color palette. Text should be crisp and readable. The cat and product should be the clear focal point.`, aspectRatio: '1:1 square' }); const result = await generateAIImage(prompt, wornUrl, '1:1'); await imageProcessor.saveImage(result, path.join(outputDir, 'Main_01.jpg')); // 保存prompt供调试 fs.writeFileSync(path.join(outputDir, 'Main_01_prompt.txt'), prompt); return true; } /** * Main_03: 功能调节展示 (参考效果最好的那张) */ async function generateMain03(materials, config, outputDir) { console.log('\n🎨 [Main_03] 功能调节展示'); // 使用平铺图作为主参考 const flatBuffer = fs.readFileSync(materials.flatImage); const flatUrl = await uploadToR2(flatBuffer, 'flat.png'); const prompt = buildEditPrompt({ subjectDescription: `An ice-blue soft pet recovery cone collar (TOUCHDOG brand) shown in flat lay position. The product has a flower/petal shape with 8 segments, velcro closure, and green inner rim.`, preserveElements: [ `The product MUST remain EXACTLY as shown: ice-blue color (#B5E5E8), flower shape, TOUCHDOG branding`, `The velcro strap detail on the left side`, `The green/teal inner rim around the neck hole`, `The 8-petal segmented design with visible stitching` ], editTasks: [ `Place the product on a soft blue background (#6B9AC4) that complements the product color`, `Add title text at top-left: "ADJUSTABLE STRAP FOR A SECURE FIT" in white, bold`, `Add 2 circular detail callout images on the right side: - Top circle: Close-up of the velcro strap, caption "SECURE THE ADJUSTABLE STRAP" - Bottom circle: Product being worn by a pet showing fit, caption "ADJUST FOR A SNUG FIT"`, `Add decorative white paw prints scattered in the background`, `Add thin white circular borders around the detail callouts` ], layoutDescription: ` - Left side (60%): Main product image, slightly angled - Top-left corner: Title text - Right side (40%): Two stacked circular detail images with captions - Scattered paw prints as decoration`, styleGuide: `Clean infographic style. Blue color scheme matching the product. Professional Amazon A+ content quality. High contrast text for readability.`, aspectRatio: '1:1 square' }); const result = await generateAIImage(prompt, flatUrl, '1:1'); await imageProcessor.saveImage(result, path.join(outputDir, 'Main_03.jpg')); fs.writeFileSync(path.join(outputDir, 'Main_03_prompt.txt'), prompt); return true; } /** * Main_04: 多场景使用 - 关键优化 * 这是最难的图,需要在4个场景中保持同一产品和宠物 */ async function generateMain04(materials, config, outputDir) { console.log('\n🎨 [Main_04] 多场景使用 (4宫格)'); const wornBuffer = fs.readFileSync(materials.catWornImage); const wornUrl = await uploadToR2(wornBuffer, 'cat-worn-grid.jpg'); const prompt = buildEditPrompt({ subjectDescription: `A Ragdoll cat (white/cream long fur, light brown markings on ears and face, distinctive blue eyes, pink nose) wearing an ice-blue TOUCHDOG soft recovery cone collar.`, preserveElements: [ `ALL 4 SCENES MUST SHOW THE EXACT SAME CAT: Ragdoll breed, blue eyes, white/cream fur, brown ear markings`, `ALL 4 SCENES MUST SHOW THE EXACT SAME PRODUCT: Ice-blue (#B5E5E8) flower-shaped soft cone, TOUCHDOG brand`, `The cat's fur texture and coloring must be consistent across all 4 images`, `The product's color, shape, and branding must be identical in all 4 scenes` ], editTasks: [ `Create a 2x2 grid layout with 4 scenes, each in a rounded rectangle frame`, `TOP-LEFT scene: Cat standing alert, wearing the cone - Caption: "• HYGIENIC & EASY TO CLEAN"`, `TOP-RIGHT scene: Cat eating/drinking from a bowl while wearing cone - Caption: "• UNRESTRICTED EATING/DRINKING"`, `BOTTOM-LEFT scene: Cat stretching or playing while wearing cone - Caption: "• REVERSIBLE WEAR"`, `BOTTOM-RIGHT scene: Cat sleeping peacefully curled up with cone - Caption: "• 360° COMFORT"`, `Add light paw print watermarks in corners of the overall image` ], layoutDescription: ` - Background: Warm beige/cream (#F5EBE0) - 2x2 grid of equal-sized rounded rectangles - Each scene has the same cat in different poses - Caption text below each scene in brown (#8B7355) - Subtle paw prints in corners`, styleGuide: ` Lifestyle photography style. Warm, inviting colors. The cat should look comfortable and happy in all scenes. Consistent lighting across all 4 scenes. CRITICAL: The cat must be recognizably the SAME individual cat in all scenes.`, aspectRatio: '1:1 square' }); const result = await generateAIImage(prompt, wornUrl, '1:1'); await imageProcessor.saveImage(result, path.join(outputDir, 'Main_04.jpg')); fs.writeFileSync(path.join(outputDir, 'Main_04_prompt.txt'), prompt); return true; } /** * Main_05: 尺寸图 */ async function generateMain05(materials, config, outputDir) { console.log('\n🎨 [Main_05] 尺寸图'); const flatBuffer = fs.readFileSync(materials.flatImage); const flatUrl = await uploadToR2(flatBuffer, 'flat-size.png'); const prompt = buildEditPrompt({ subjectDescription: `An ice-blue TOUCHDOG soft pet recovery cone collar in flat lay position, showing its flower/petal shape design.`, preserveElements: [ `The product MUST remain EXACTLY as shown: ice-blue color, flower shape, TOUCHDOG branding`, `The product's proportions and shape`, `The velcro closure and inner rim details` ], editTasks: [ `Place product on warm beige background (#F5EDE4)`, `Add title "PRODUCT SIZE" at top center in dark text`, `Add a size chart table below the product with columns: SIZE | NECK | DEPTH`, `Table data: XS: 5.6-6.8IN | 3.2IN S: 7.2-8.4IN | 4IN M: 8.8-10.4IN | 5IN L: 10.8-12.4IN | 6IN XL: 12.8-14.4IN | 7IN`, `Add note in coral/salmon color: "NOTE: ALWAYS MEASURE YOUR PET'S NECK BEFORE SELECTING A SIZE"`, `Add subtle paw print decorations in beige tones` ], layoutDescription: ` - Top: Title - Center: Product image (main focus) - Bottom: Size chart table with clean design`, styleGuide: `Clean, informative infographic style. Easy to read table. Warm neutral background. Professional Amazon listing quality.`, aspectRatio: '1:1 square' }); const result = await generateAIImage(prompt, flatUrl, '1:1'); await imageProcessor.saveImage(result, path.join(outputDir, 'Main_05.jpg')); fs.writeFileSync(path.join(outputDir, 'Main_05_prompt.txt'), prompt); return true; } /** * Main_02: 白底平铺+细节放大 */ async function generateMain02(materials, config, outputDir) { console.log('\n🎨 [Main_02] 白底平铺+细节放大'); const flatBuffer = fs.readFileSync(materials.flatImage); const flatUrl = await uploadToR2(flatBuffer, 'flat-detail.png'); const prompt = buildEditPrompt({ subjectDescription: `An ice-blue TOUCHDOG soft pet recovery cone collar in flat lay position on black background. The product has a flower/petal shape with 8 segments, velcro closure on left, and green inner rim.`, preserveElements: [ `The product MUST remain EXACTLY as shown: ice-blue color (#B5E5E8), flower shape, 8 petal segments`, `The TOUCHDOG branding text on the product`, `The velcro strap detail and green/teal inner rim`, `The product's proportions and stitching pattern` ], editTasks: [ `Change background from black to clean white`, `Add a dark green (#2D4A3E) banner at top with text: "DURABLE WATERPROOF PU LAYER"`, `Add 2 circular magnified detail callouts at bottom: - Left circle: Close-up of outer material texture, label "DURABLE WATERPROOF PU LAYER" - Right circle: Close-up of inner lining, label "DOUBLE-LAYER COMFORT"`, `Add thin green borders around detail circles`, `Keep layout clean and professional` ], layoutDescription: ` - Top: Dark green banner with title - Center: Main product image on white background - Bottom: Two circular detail magnifications with labels`, styleGuide: `Clean white background product photography. Professional Amazon main image style. High contrast, sharp details.`, aspectRatio: '1:1 square' }); const result = await generateAIImage(prompt, flatUrl, '1:1'); await imageProcessor.saveImage(result, path.join(outputDir, 'Main_02.jpg')); fs.writeFileSync(path.join(outputDir, 'Main_02_prompt.txt'), prompt); return true; } /** * Main_06: 多角度展示 */ async function generateMain06(materials, config, outputDir) { console.log('\n🎨 [Main_06] 多角度展示'); const wornBuffer = fs.readFileSync(materials.catWornImage); const wornUrl = await uploadToR2(wornBuffer, 'multi-angle.jpg'); const prompt = buildEditPrompt({ subjectDescription: `A Ragdoll cat (white/cream fur, brown ear markings, blue eyes) wearing an ice-blue TOUCHDOG soft recovery cone collar.`, preserveElements: [ `The cat MUST remain a Ragdoll: blue eyes, white/cream fur, brown markings`, `The product MUST remain ice-blue (#B5E5E8), flower-shaped, TOUCHDOG branded`, `Both views must show the EXACT SAME cat and product` ], editTasks: [ `Create a split image with curved decorative divider in the middle`, `LEFT SIDE: Front view of cat wearing the cone (from reference)`, `RIGHT SIDE: Side/profile view of the same cat wearing the same cone`, `Warm home interior background in both halves`, `NO text overlays - pure lifestyle imagery` ], layoutDescription: ` - Split layout with elegant curved divider - Left: Front view - Right: Side view - Consistent warm lighting across both`, styleGuide: `Lifestyle photography. Warm, cozy atmosphere. The cat should look comfortable. NO text.`, aspectRatio: '1:1 square' }); const result = await generateAIImage(prompt, wornUrl, '1:1'); await imageProcessor.saveImage(result, path.join(outputDir, 'Main_06.jpg')); fs.writeFileSync(path.join(outputDir, 'Main_06_prompt.txt'), prompt); return true; } /** * APlus_01: 品牌横幅 */ async function generateAPlus01(materials, config, outputDir) { console.log('\n🎨 [APlus_01] 品牌横幅'); const wornBuffer = fs.readFileSync(materials.catWornImage); const wornUrl = await uploadToR2(wornBuffer, 'brand-banner.jpg'); const prompt = buildEditPrompt({ subjectDescription: `A Ragdoll cat wearing an ice-blue TOUCHDOG soft recovery cone collar in a home setting.`, preserveElements: [ `The cat MUST remain a Ragdoll with blue eyes and white/cream fur`, `The product MUST remain ice-blue, flower-shaped, TOUCHDOG branded` ], editTasks: [ `Create a horizontal banner layout (3:2 ratio)`, `LEFT 40%: Brand text area with: - "TOUCHDOG" in playful coral/salmon color (#E8A87C) - Small paw print icons around the brand name - "CAT SOFT CONE COLLAR" below in gray`, `RIGHT 60%: The cat wearing the product in a warm home setting`, `Soft, warm color palette throughout` ], layoutDescription: ` - Left side: Brand name and product title - Right side: Hero image of cat with product - Warm, cohesive color scheme`, styleGuide: `Amazon A+ brand story style. Warm and inviting. Professional yet friendly.`, aspectRatio: '3:2 landscape' }); const result = await generateAIImage(prompt, wornUrl, '3:2'); await imageProcessor.saveImage(result, path.join(outputDir, 'APlus_01.jpg')); fs.writeFileSync(path.join(outputDir, 'APlus_01_prompt.txt'), prompt); return true; } /** * APlus_02: 对比图 */ async function generateAPlus02(materials, config, outputDir) { console.log('\n🎨 [APlus_02] 对比图'); const wornBuffer = fs.readFileSync(materials.catWornImage); const wornUrl = await uploadToR2(wornBuffer, 'comparison.jpg'); const prompt = buildEditPrompt({ subjectDescription: `A Ragdoll cat wearing an ice-blue TOUCHDOG soft recovery cone collar.`, preserveElements: [ `LEFT SIDE: The cat and product MUST match the reference - Ragdoll cat, ice-blue soft cone`, `The product color (#B5E5E8), flower shape, and TOUCHDOG branding on left side` ], editTasks: [ `Create a side-by-side comparison layout`, `LEFT SIDE (60% width, colorful): - Happy Ragdoll cat wearing our ice-blue soft cone (from reference) - Green checkmark icon - "OUR" label on coral/orange banner - 3 benefits in white: "CLOUD-LIGHT COMFORT", "WIDER & CLEARER", "FOLDABLE & PORTABLE"`, `RIGHT SIDE (40% width, grayscale): - Sad-looking cat wearing a DIFFERENT product: hard plastic transparent cone (traditional e-collar) - Red X icon - "OTHER" label on gray banner - 3 drawbacks in gray: "HEAVY & BULKY", "BLOCKS VISION & MOVEMENT", "HARD TO STORE"`, `Warm beige background with paw print watermarks` ], layoutDescription: ` - Split layout with curved divider - Left side larger, full color, positive mood - Right side smaller, desaturated, negative mood - Text overlays on each side`, styleGuide: ` Clear visual contrast between "us" and "them". Left side warm and inviting, right side cold and uncomfortable. IMPORTANT: Right side must show a DIFFERENT type of cone (hard plastic), not our product.`, aspectRatio: '3:2 landscape' }); const result = await generateAIImage(prompt, wornUrl, '3:2'); await imageProcessor.saveImage(result, path.join(outputDir, 'APlus_02.jpg')); fs.writeFileSync(path.join(outputDir, 'APlus_02_prompt.txt'), prompt); return true; } /** * APlus_03: 功能细节展示 */ async function generateAPlus03(materials, config, outputDir) { console.log('\n🎨 [APlus_03] 功能细节展示'); const wornBuffer = fs.readFileSync(materials.catWornImage); const wornUrl = await uploadToR2(wornBuffer, 'feature-detail.jpg'); const prompt = buildEditPrompt({ subjectDescription: `A Ragdoll cat wearing an ice-blue TOUCHDOG soft recovery cone collar.`, preserveElements: [ `The cat MUST remain a Ragdoll with blue eyes`, `The product MUST remain ice-blue, flower-shaped, TOUCHDOG branded` ], editTasks: [ `Create horizontal layout (3:2) with title and 3 detail panels`, `Title at top: "ENGINEERED FOR UNCOMPROMISED COMFORT" in dark text`, `3 detail images in a row below: - Panel 1: Close-up of inner cotton lining texture, caption "STURDY AND BREATHABLE" - Panel 2: Cat wearing the product looking comfortable, caption "EASY TO CLEAN, STYLISH" - Panel 3: Close-up of stitching detail, caption "REINFORCED STITCHING"`, `Warm beige background (#F5EBE0) with subtle paw prints` ], layoutDescription: ` - Top: Title banner - Bottom: Three equal-width panels with captions`, styleGuide: `Amazon A+ feature module style. Clean, informative. Warm color palette.`, aspectRatio: '3:2 landscape' }); const result = await generateAIImage(prompt, wornUrl, '3:2'); await imageProcessor.saveImage(result, path.join(outputDir, 'APlus_03.jpg')); fs.writeFileSync(path.join(outputDir, 'APlus_03_prompt.txt'), prompt); return true; } /** * APlus_04: 多场景横版 */ async function generateAPlus04(materials, config, outputDir) { console.log('\n🎨 [APlus_04] 多场景横版'); const wornBuffer = fs.readFileSync(materials.catWornImage); const wornUrl = await uploadToR2(wornBuffer, 'scenes-horizontal.jpg'); const prompt = buildEditPrompt({ subjectDescription: `A Ragdoll cat (white/cream fur, blue eyes, brown ear markings) wearing an ice-blue TOUCHDOG soft recovery cone collar.`, preserveElements: [ `ALL 4 SCENES MUST SHOW THE EXACT SAME CAT: Ragdoll breed, blue eyes, white/cream fur`, `ALL 4 SCENES MUST SHOW THE EXACT SAME PRODUCT: Ice-blue flower-shaped soft cone`, `Consistent cat appearance and product across all panels` ], editTasks: [ `Create horizontal layout (3:2) with 4 scenes in a row`, `Scene 1: Cat standing - "HYGIENIC & EASY TO CLEAN"`, `Scene 2: Cat eating from bowl - "UNRESTRICTED EATING"`, `Scene 3: Cat playing/stretching - "REVERSIBLE WEAR"`, `Scene 4: Cat sleeping curled up - "360° COMFORT"`, `Each scene in a rounded rectangle frame`, `Warm beige background with paw print decorations` ], layoutDescription: ` - 4 equal panels arranged horizontally - Each panel has image + caption below - Consistent warm lighting across all`, styleGuide: `Lifestyle photography. Same cat in all scenes. Warm, cozy feel.`, aspectRatio: '3:2 landscape' }); const result = await generateAIImage(prompt, wornUrl, '3:2'); await imageProcessor.saveImage(result, path.join(outputDir, 'APlus_04.jpg')); fs.writeFileSync(path.join(outputDir, 'APlus_04_prompt.txt'), prompt); return true; } /** * APlus_05: 多角度横版 */ async function generateAPlus05(materials, config, outputDir) { console.log('\n🎨 [APlus_05] 多角度横版'); const wornBuffer = fs.readFileSync(materials.catWornImage); const wornUrl = await uploadToR2(wornBuffer, 'angles-horizontal.jpg'); const prompt = buildEditPrompt({ subjectDescription: `A Ragdoll cat wearing an ice-blue TOUCHDOG soft recovery cone collar.`, preserveElements: [ `Both views must show the SAME Ragdoll cat with blue eyes`, `Both views must show the SAME ice-blue TOUCHDOG cone` ], editTasks: [ `Create horizontal split layout (3:2)`, `LEFT: Front view of cat wearing cone`, `RIGHT: Side/profile view of same cat with cone`, `Elegant curved divider between the two views`, `Warm home background in both`, `NO text - pure visual showcase` ], layoutDescription: ` - Two equal halves with curved divider - Left: Front angle - Right: Side angle - Warm consistent lighting`, styleGuide: `High-end lifestyle photography. No text. Warm atmosphere.`, aspectRatio: '3:2 landscape' }); const result = await generateAIImage(prompt, wornUrl, '3:2'); await imageProcessor.saveImage(result, path.join(outputDir, 'APlus_05.jpg')); fs.writeFileSync(path.join(outputDir, 'APlus_05_prompt.txt'), prompt); return true; } /** * APlus_06: 尺寸表横版 * 优化:明确描述产品的C形开口特征 */ async function generateAPlus06(materials, config, outputDir) { console.log('\n🎨 [APlus_06] 尺寸表横版'); const flatBuffer = fs.readFileSync(materials.flatImage); const flatUrl = await uploadToR2(flatBuffer, 'size-chart-h.png'); const prompt = buildEditPrompt({ subjectDescription: `An ice-blue TOUCHDOG soft pet recovery cone collar in flat lay position. CRITICAL SHAPE DETAIL: The product has a C-SHAPED OPENING (not a closed circle) - there is a GAP on the left side where the velcro strap attaches. This opening allows the collar to wrap around the pet's neck.`, preserveElements: [ `The product MUST keep its C-SHAPED OPENING - DO NOT close the gap into a full circle`, `The velcro strap visible on the left side of the opening`, `Ice-blue color (#B5E5E8), flower/petal shape with 8 segments`, `TOUCHDOG brand text on the product`, `Green/teal inner rim around the neck hole` ], editTasks: [ `Change background from black to warm beige (#F5EDE4)`, `Add title "PRODUCT SIZE" at top center in dark text`, `Add dimension labels: "NECK" pointing to inner circle, "DEPTH" pointing outward`, `Add size chart table on the right: SIZE | NECK | DEPTH XS | 5.6-6.8IN | 3.2IN S | 7.2-8.4IN | 4IN M | 8.8-10.4IN | 5IN L | 10.8-12.4IN | 6IN XL | 12.8-14.4IN | 7IN`, `Add note in coral: "ALWAYS MEASURE YOUR PET'S NECK BEFORE SELECTING A SIZE"`, `Add subtle paw print watermarks` ], layoutDescription: ` - Left 45%: Product image maintaining C-shape opening - Right 55%: Size chart table - Top: Title - Bottom: Note text`, styleGuide: `Clean infographic style. The product's C-shaped opening must be clearly visible - this is a key feature showing how it wraps around the neck.`, aspectRatio: '3:2 landscape' }); const result = await generateAIImage(prompt, flatUrl, '3:2'); await imageProcessor.saveImage(result, path.join(outputDir, 'APlus_06.jpg')); fs.writeFileSync(path.join(outputDir, 'APlus_06_prompt.txt'), prompt); return true; } // ============================================================ // 主流程 // ============================================================ async function main() { const args = process.argv.slice(2); let materialDir = path.join(__dirname, '素材/素材/已有的素材'); let skuName = 'Touchdog冰蓝色伊丽莎白圈'; // 可选:只生成指定的图 let onlyGenerate = null; // e.g., ['Main_01', 'Main_04'] for (const arg of args) { if (arg.startsWith('--material-dir=')) { materialDir = arg.split('=')[1]; } else if (arg.startsWith('--sku-name=')) { skuName = arg.split('=')[1]; } else if (arg.startsWith('--only=')) { onlyGenerate = arg.split('=')[1].split(','); } } const timestamp = new Date().toISOString().slice(0, 10); const timeStr = new Date().toTimeString().slice(0, 8).replace(/:/g, '-'); const safeName = skuName.replace(/[^a-zA-Z0-9\u4e00-\u9fa5]/g, '_').slice(0, 20); const outputDir = path.join(__dirname, `output_v6_${safeName}_${timestamp}-${timeStr}`); console.log('\n' + '═'.repeat(70)); console.log('🚀 POC Workflow V6 - 优化Prompt控制策略'); console.log('═'.repeat(70)); console.log('\n📋 核心策略: 精准Prompt控制,明确保留元素,限定编辑范围'); console.log(' 素材目录:', materialDir); console.log(' 输出目录:', outputDir); if (!fs.existsSync(outputDir)) { fs.mkdirSync(outputDir, { recursive: true }); } // 扫描素材 const files = fs.readdirSync(materialDir); const images = files.filter(f => /\.(jpg|jpeg|png)$/i.test(f)); console.log('\n📁 可用素材:'); images.forEach(img => console.log(` - ${img}`)); // ========== 修复:正确识别素材 ========== // 平铺图:IMG_5683.png(黑底抠图) const flatImage = images.find(i => i.includes('5683')) || images.find(i => i.endsWith('.png')); // 猫咪佩戴图:IMG_6216 或 IMG_6229(不是6514的狗狗图!) const catWornImage = images.find(i => i.includes('6216')) || images.find(i => i.includes('6229')) || images.find(i => i.toLowerCase().includes('cat')); // 狗狗佩戴图(备用) const dogWornImage = images.find(i => i.includes('6514')); if (!flatImage) { console.error('❌ 找不到平铺图素材'); return; } if (!catWornImage) { console.error('❌ 找不到猫咪佩戴图素材'); console.log(' 可用文件:', images); return; } const materials = { flatImage: path.join(materialDir, flatImage), catWornImage: path.join(materialDir, catWornImage), dogWornImage: dogWornImage ? path.join(materialDir, dogWornImage) : null }; console.log('\n✅ 素材选择:'); console.log(' 平铺图:', flatImage); console.log(' 猫咪佩戴图:', catWornImage, '← 使用这个'); if (dogWornImage) console.log(' 狗狗佩戴图:', dogWornImage, '(备用)'); const config = { brandName: 'TOUCHDOG', productName: 'CAT SOFT CONE COLLAR' }; const results = []; // ========== 生成图片 ========== console.log('\n' + '─'.repeat(70)); console.log('🎨 开始生成图片'); console.log('─'.repeat(70)); const generators = { 'Main_01': () => generateMain01(materials, config, outputDir), 'Main_02': () => generateMain02(materials, config, outputDir), 'Main_03': () => generateMain03(materials, config, outputDir), 'Main_04': () => generateMain04(materials, config, outputDir), 'Main_05': () => generateMain05(materials, config, outputDir), 'Main_06': () => generateMain06(materials, config, outputDir), 'APlus_01': () => generateAPlus01(materials, config, outputDir), 'APlus_02': () => generateAPlus02(materials, config, outputDir), 'APlus_03': () => generateAPlus03(materials, config, outputDir), 'APlus_04': () => generateAPlus04(materials, config, outputDir), 'APlus_05': () => generateAPlus05(materials, config, outputDir), 'APlus_06': () => generateAPlus06(materials, config, outputDir), }; for (const [id, generator] of Object.entries(generators)) { if (onlyGenerate && !onlyGenerate.includes(id)) { console.log(`\n⏭️ [${id}] 跳过`); continue; } try { results.push({ id, success: await generator() }); } catch (e) { console.error(`\n❌ ${id} 失败:`, e.message); results.push({ id, success: false, error: e.message }); } } // ========== 总结 ========== console.log('\n' + '═'.repeat(70)); console.log('📊 生成完成'); console.log('═'.repeat(70)); const successCount = results.filter(r => r.success).length; console.log(`\n✅ 成功: ${successCount}/${results.length}`); if (successCount < results.length) { console.log('\n❌ 失败:'); results.filter(r => !r.success).forEach(r => console.log(` - ${r.id}: ${r.error}`)); } console.log(`\n📁 输出目录: ${outputDir}`); console.log(' 每张图都保存了对应的 _prompt.txt 供调试'); // 保存所有prompt到一个文件 const allPrompts = {}; for (const id of Object.keys(generators)) { const promptFile = path.join(outputDir, `${id}_prompt.txt`); if (fs.existsSync(promptFile)) { allPrompts[id] = fs.readFileSync(promptFile, 'utf-8'); } } fs.writeFileSync(path.join(outputDir, 'all-prompts.json'), JSON.stringify(allPrompts, null, 2)); fs.writeFileSync(path.join(outputDir, 'results.json'), JSON.stringify(results, null, 2)); } main().catch(console.error);