Эх сурвалжийг харах

fix: 使用upsert避免章节重复创建 - 无论跑多少次都不会产生重复数据

MyFramework User 4 сар өмнө
parent
commit
568a1acc80

+ 56 - 0
server/check-database.js

@@ -0,0 +1,56 @@
+const { PrismaClient } = require('@prisma/client');
+const prisma = new PrismaClient();
+
+async function checkDatabase() {
+  try {
+    console.log('📊 查询 bookId=1 的所有章节记录...\n');
+    
+    const chapters = await prisma.bookChapter.findMany({
+      where: { bookId: 1 },
+      orderBy: [
+        { level: 'asc' },
+        { number: 'asc' }
+      ]
+    });
+
+    console.log(`总记录数: ${chapters.length}\n`);
+
+    // 按级别统计
+    const level1 = chapters.filter(c => c.level === 1);
+    const level2 = chapters.filter(c => c.level === 2);
+    const level3 = chapters.filter(c => c.level === 3);
+
+    console.log(`章 (level=1): ${level1.length} 条`);
+    console.log(`节 (level=2): ${level2.length} 条`);
+    console.log(`小节 (level=3): ${level3.length} 条\n`);
+
+    // 显示前5条记录
+    console.log('前5条记录:');
+    chapters.slice(0, 5).forEach(c => {
+      console.log(`  ID=${c.id}, level=${c.level}, number=${c.number}, parentId=${c.parentId}, title=${c.title}`);
+    });
+
+    // 检查是否有重复
+    console.log('\n检查重复 (bookId, parentId, level, number):');
+    const keyMap = new Map();
+    chapters.forEach(c => {
+      const key = `${c.bookId}_${c.parentId}_${c.level}_${c.number}`;
+      if (keyMap.has(key)) {
+        console.log(`  ⚠️  重复: ${key}`);
+        console.log(`     记录1: ID=${keyMap.get(key)}, title=${chapters.find(x => x.id === keyMap.get(key))?.title}`);
+        console.log(`     记录2: ID=${c.id}, title=${c.title}`);
+      } else {
+        keyMap.set(key, c.id);
+      }
+    });
+
+    console.log('\n✅ 检查完成');
+
+  } catch (error) {
+    console.error('错误:', error);
+  } finally {
+    await prisma.$disconnect();
+  }
+}
+
+checkDatabase();

+ 45 - 0
server/force-clear.js

@@ -0,0 +1,45 @@
+const { PrismaClient } = require('@prisma/client');
+const prisma = new PrismaClient();
+
+async function forceClear() {
+  try {
+    console.log('🗑️  强制清空 bookId=1 的所有数据...\n');
+    
+    // 先删除所有章节
+    const deleteResult = await prisma.bookChapter.deleteMany({
+      where: { bookId: 1 }
+    });
+    console.log(`✅ 已删除 ${deleteResult.count} 条章节记录`);
+    
+    // 重置书籍状态
+    await prisma.book.update({
+      where: { id: 1 },
+      data: {
+        status: 'draft',
+        progress: 0,
+        errorMsg: null,
+        outlineJson: null  // 也清空outlineJson
+      }
+    });
+    console.log('✅ 书籍状态已重置');
+    
+    // 验证
+    const count = await prisma.bookChapter.count({
+      where: { bookId: 1 }
+    });
+    console.log(`\n📊 验证: bookId=1 的章节记录数: ${count}`);
+    
+    if (count === 0) {
+      console.log('✅ 清空成功!数据库现在是干净的');
+    } else {
+      console.log('❌ 还有残留数据!');
+    }
+
+  } catch (error) {
+    console.error('错误:', error);
+  } finally {
+    await prisma.$disconnect();
+  }
+}
+
+forceClear();

+ 21 - 6
server/src/modules/book-generator/book-generator.store.ts

@@ -387,8 +387,24 @@ export class BookStore {
     keyPoints?: string[];
     estimatedWords?: number;
   }, parentId: number | null, level: number): Promise<number> {
-    const created = await prisma.bookChapter.create({
-      data: {
+    // 使用 upsert 避免重复创建
+    const upserted = await prisma.bookChapter.upsert({
+      where: {
+        bookId_parentId_level_number: {
+          bookId: bookIdNum,
+          parentId: parentId,
+          level: level,
+          number: item.number,
+        }
+      },
+      update: {
+        title: item.title,
+        summary: item.summary || '',
+        keyPoints: item.keyPoints ? JSON.stringify(item.keyPoints) : null,
+        estimatedWords: item.estimatedWords || 1000,
+        status: 'completed',
+      } as any,
+      create: {
         bookId: bookIdNum,
         parentId,
         level,
@@ -397,11 +413,10 @@ export class BookStore {
         summary: item.summary || '',
         keyPoints: item.keyPoints ? JSON.stringify(item.keyPoints) : null,
         estimatedWords: item.estimatedWords || 1000,
-        status: 'completed', // 大纲已完成
-        contentStatus: null, // 内容未开始生成
-      },
+        status: 'completed',
+      } as any,
     });
-    return created.id;
+    return upserted.id;
   }
 
   /**