/** * 重新生成所有课程的封面图 * 使用方法: npx ts-node scripts/regenerate-all-covers.ts */ import { PrismaClient } from '@prisma/client'; import { generateCourseCover } from '../src/services/coverImageService'; import { logger } from '../src/utils/logger'; const prisma = new PrismaClient(); async function regenerateAllCovers() { try { // 1. 查找所有未删除的课程(包括已有封面的) const allCourses = await prisma.course.findMany({ where: { deletedAt: null, title: { not: '', }, }, include: { aiContentTask: { select: { generationStyle: true, }, }, }, }); logger.info(`[RegenerateAllCovers] 找到 ${allCourses.length} 个课程,将重新生成所有封面`); if (allCourses.length === 0) { logger.info('[RegenerateAllCovers] 没有需要生成封面的课程'); return; } // 2. 为每个课程重新生成封面 let successCount = 0; let failCount = 0; for (const course of allCourses) { try { const title = course.title || '未命名课程'; // 从任务中获取生成风格,如果没有则默认为 'full' const style = (course.aiContentTask?.generationStyle as 'full' | 'essence' | 'one-page') || 'full'; logger.info(`[RegenerateAllCovers] 正在重新生成封面: ${course.id}, title="${title}", style=${style}`); const coverImagePath = await generateCourseCover( course.id, title, style ); if (coverImagePath) { // 更新数据库 await prisma.course.update({ where: { id: course.id }, data: { coverImage: coverImagePath }, }); logger.info(`[RegenerateAllCovers] ✅ 封面重新生成成功: ${course.id}, path=${coverImagePath}`); successCount++; } else { logger.warn(`[RegenerateAllCovers] ⚠️ 封面生成返回空路径: ${course.id}`); failCount++; } } catch (error: any) { logger.error(`[RegenerateAllCovers] ❌ 为课程重新生成封面失败: ${course.id}`, error); failCount++; } } logger.info(`[RegenerateAllCovers] 完成!成功: ${successCount}, 失败: ${failCount}`); } catch (error: any) { logger.error('[RegenerateAllCovers] 批量重新生成封面失败', error); throw error; } finally { await prisma.$disconnect(); } } // 执行 regenerateAllCovers() .then(() => { console.log('✅ 批量重新生成封面完成'); process.exit(0); }) .catch((error) => { console.error('❌ 批量重新生成封面失败:', error); process.exit(1); });