/** * 状态同步脚本:将现有数据从旧字段同步到 genStage * 使用 stage-manager 的安全函数 * Run: npx ts-node prisma/sync-genstage.ts */ import { PrismaClient } from '@prisma/client'; import { BookGenStage, ChapterGenStage, bookStageIndex, chapterStageIndex, safeTransitionChapter, safeTransitionBook, checkAndRegressBook, tryAdvanceBook, } from '../src/modules/book-generator/stage-manager'; const prisma = new PrismaClient(); async function syncChapter(chapter: any): Promise { const chapterAny = chapter as any; let targetStage: ChapterGenStage; // 根据旧字段计算新的 genStage if (chapterAny.videoStatus === 'completed') { targetStage = 'video_completed'; } else if (chapterAny.videoStatus === 'generating') { targetStage = 'video_generating'; } else if (chapterAny.audioStatus === 'completed') { targetStage = 'audio_completed'; } else if (chapterAny.audioStatus === 'generating') { targetStage = 'audio_generating'; } else if (chapterAny.contentStatus === 'completed') { targetStage = 'content_completed'; } else if (chapterAny.contentStatus === 'generating') { targetStage = 'content_generating'; } else if (chapterAny.status === 'completed') { // 只有章级别的 status targetStage = 'content_completed'; } else if (chapterAny.status === 'failed' || chapterAny.contentStatus === 'failed' || chapterAny.audioStatus === 'failed' || chapterAny.videoStatus === 'failed') { targetStage = 'failed'; } else { targetStage = 'idle'; } const currentStage = chapterAny.genStage as ChapterGenStage; // 如果相同则跳过 if (currentStage === targetStage) { console.log(` Chapter ${chapter.id}: 无需更新 (${currentStage})`); return; } // 比较阶段索引决定用哪个函数 const currentIdx = chapterStageIndex(currentStage); const targetIdx = chapterStageIndex(targetStage); try { if (targetIdx > currentIdx) { // 前进用 safeTransitionChapter(内部已处理乐观锁) await safeTransitionChapter(chapter.id, currentStage, targetStage); } else { // 回退/同级也用 safeTransitionChapter(重新生成场景) await safeTransitionChapter(chapter.id, currentStage, targetStage); } console.log(` Chapter ${chapter.id}: ${currentStage} → ${targetStage}`); } catch (error: any) { // 并发冲突:跳过此记录 console.log(` Chapter ${chapter.id}: 跳过 (${error.message})`); } } async function syncBook(book: any): Promise { let targetStage: BookGenStage; switch (book.status) { case 'draft': targetStage = 'draft'; break; case 'planning': targetStage = 'outline_completed'; break; case 'generating': targetStage = inferBookGenStage(book); break; case 'completed': targetStage = inferBookGenStage(book); break; case 'failed': case 'interrupted': targetStage = 'failed'; break; default: targetStage = 'draft'; } const currentStage = (book.genStage || 'draft') as BookGenStage; if (currentStage === targetStage) { console.log(` Book ${book.id}: 无需更新 (${currentStage})`); return; } try { await safeTransitionBook(book.id, currentStage, targetStage); console.log(` Book ${book.id}: ${currentStage} → ${targetStage}`); } catch (error: any) { console.log(` Book ${book.id}: 跳过 (${error.message})`); } } function inferBookGenStage(book: any): BookGenStage { // 根据 chapters 状态推断 const chapters = book.chapters || []; const hasVideo = chapters.some((c: any) => c.genStage === 'video_completed' || c.videoStatus === 'completed'); if (hasVideo) return 'video_completed'; const hasAudio = chapters.some((c: any) => c.genStage === 'audio_completed' || c.audioStatus === 'completed'); if (hasAudio) return 'audio_completed'; const hasContent = chapters.some((c: any) => c.genStage === 'content_completed' || c.contentStatus === 'completed'); if (hasContent) return 'content_generating'; return 'content_generating'; } async function main() { console.log('开始状态同步...\n'); // 同步 BookChapter console.log('=== 同步 BookChapter ==='); const chapters = await prisma.bookChapter.findMany(); for (const chapter of chapters) { await syncChapter(chapter); } console.log(`BookChapter 同步完成: ${chapters.length} 条\n`); // 同步 Book console.log('=== 同步 Book ==='); const books = await prisma.book.findMany({ include: { chapters: true } }); for (const book of books) { await syncBook(book); } console.log(`Book 同步完成: ${books.length} 条\n`); // 执行 Book 联动回退检查 console.log('=== 检查 Book 联动回退 ==='); for (const book of books) { await checkAndRegressBook(book.id); } console.log('✅ 状态同步完成!'); } main() .catch(console.error) .finally(() => prisma.$disconnect());