001project_wildgrowth/backend/scripts/generate-missing-covers.ts

79 lines
2.5 KiB
TypeScript

/**
* 为没有封面的课程批量生成封面
* 使用方法: npx ts-node scripts/generate-missing-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 generateMissingCovers() {
try {
// 1. 查找所有没有封面的课程(⚠️ 只处理没有封面的,不替换已有封面)
// 注意:用户可能已经上传了自己的封面,我们不应该替换它们
const coursesWithoutCover = await prisma.course.findMany({
where: {
deletedAt: null,
coverImage: null,
title: { not: '' },
},
take: 100,
});
logger.info(`[GenerateMissingCovers] 找到 ${coursesWithoutCover.length} 个需要生成/重新生成封面的课程`);
if (coursesWithoutCover.length === 0) {
logger.info('[GenerateMissingCovers] 没有需要生成封面的课程');
return;
}
// 2. 为每个课程生成封面
let successCount = 0;
let failCount = 0;
for (const course of coursesWithoutCover) {
try {
const title = course.title || '未命名课程';
logger.info(`[GenerateMissingCovers] 正在为课程生成封面: ${course.id}, title="${title}"`);
const coverImagePath = await generateCourseCover(course.id, title, 'full');
if (coverImagePath) {
// 更新数据库
await prisma.course.update({
where: { id: course.id },
data: { coverImage: coverImagePath },
});
logger.info(`[GenerateMissingCovers] ✅ 封面生成成功: ${course.id}, path=${coverImagePath}`);
successCount++;
} else {
logger.warn(`[GenerateMissingCovers] ⚠️ 封面生成返回空路径: ${course.id}`);
failCount++;
}
} catch (error: any) {
logger.error(`[GenerateMissingCovers] ❌ 为课程生成封面失败: ${course.id}`, error);
failCount++;
}
}
logger.info(`[GenerateMissingCovers] 完成!成功: ${successCount}, 失败: ${failCount}`);
} catch (error: any) {
logger.error('[GenerateMissingCovers] 批量生成封面失败', error);
throw error;
} finally {
await prisma.$disconnect();
}
}
// 执行
generateMissingCovers()
.then(() => {
console.log('✅ 批量生成封面完成');
process.exit(0);
})
.catch((error) => {
console.error('❌ 批量生成封面失败:', error);
process.exit(1);
});