瀏覽代碼

feat: 从数据库构建三级大纲树形结构 - 前端可显示完整三级标题

MyFramework User 4 月之前
父節點
當前提交
5c57b4808f
共有 2 個文件被更改,包括 129 次插入1 次删除
  1. 48 0
      server/fix-book-status.js
  2. 81 1
      server/src/modules/book-generator/book-generator.store.ts

+ 48 - 0
server/fix-book-status.js

@@ -0,0 +1,48 @@
+const { PrismaClient } = require('@prisma/client');
+const prisma = new PrismaClient();
+
+async function fixBookStatus() {
+  try {
+    // 更新bookId=1的所有level=1的章,将status从pending改为completed
+    const result = await prisma.bookChapter.updateMany({
+      where: {
+        bookId: 1,
+        level: 1,
+        status: 'pending'
+      },
+      data: {
+        status: 'completed'
+      }
+    });
+
+    console.log(`✅ 已修复 ${result.count} 个章的状态: pending → completed`);
+
+    // 验证结果
+    const book = await prisma.book.findUnique({
+      where: { id: 1 },
+      include: {
+        chapters: true
+      }
+    });
+
+    const level1Chapters = book.chapters.filter(c => c.level === 1);
+    const statusCount = {
+      pending: level1Chapters.filter(c => c.status === 'pending').length,
+      completed: level1Chapters.filter(c => c.status === 'completed').length,
+      failed: level1Chapters.filter(c => c.status === 'failed').length,
+    };
+
+    console.log('\n📊 验证结果:');
+    console.log(`  章总数: ${level1Chapters.length}`);
+    console.log(`  pending: ${statusCount.pending}`);
+    console.log(`  completed: ${statusCount.completed}`);
+    console.log(`  failed: ${statusCount.failed}`);
+
+  } catch (error) {
+    console.error('错误:', error);
+  } finally {
+    await prisma.$disconnect();
+  }
+}
+
+fixBookStatus();

+ 81 - 1
server/src/modules/book-generator/book-generator.store.ts

@@ -53,6 +53,71 @@ function outlineChapterFromDb(dbChapter: any) {
 // ============ 存储类 ============
 
 export class BookStore {
+  /**
+   * 从数据库章节记录构建树形结构的outline
+   */
+  private buildOutlineFromChapters(chapters: any[]): BookOutline | null {
+    if (!chapters || chapters.length === 0) return null;
+
+    // 获取所有level=1的章
+    const level1Chapters = chapters.filter(c => c.level === 1);
+    if (level1Chapters.length === 0) return null;
+
+    // 构建映射表
+    const chapterMap = new Map<number, any>();
+    const sectionMap = new Map<number, any>();
+
+    chapters.forEach(c => {
+      if (c.level === 1) {
+        chapterMap.set(c.id, {
+          number: c.number,
+          title: c.title,
+          summary: c.summary || '',
+          keyPoints: c.keyPoints ? JSON.parse(c.keyPoints) : [],
+          estimatedWords: c.estimatedWords,
+          sections: []
+        });
+      } else if (c.level === 2) {
+        sectionMap.set(c.id, {
+          number: c.number,
+          title: c.title,
+          summary: c.summary || '',
+          keyPoints: c.keyPoints ? JSON.parse(c.keyPoints) : [],
+          estimatedWords: c.estimatedWords,
+          subsections: []
+        });
+      }
+    });
+
+    // 构建节和小节的关系
+    chapters.forEach(c => {
+      if (c.level === 2 && c.parentId) {
+        const chapter = chapterMap.get(c.parentId);
+        const section = sectionMap.get(c.id);
+        if (chapter && section) {
+          chapter.sections.push(section);
+        }
+      } else if (c.level === 3 && c.parentId) {
+        const section = sectionMap.get(c.parentId);
+        if (section) {
+          section.subsections.push({
+            number: c.number,
+            title: c.title,
+            summary: c.summary || '',
+            keyPoints: c.keyPoints ? JSON.parse(c.keyPoints) : [],
+            estimatedWords: c.estimatedWords
+          });
+        }
+      }
+    });
+
+    return {
+      mainTheme: '',
+      structureLogic: '',
+      chapters: Array.from(chapterMap.values())
+    };
+  }
+
   /**
    * 创建书籍
    */
@@ -95,13 +160,28 @@ export class BookStore {
   async getById(id: string, filterPublic: boolean = false, userId?: number): Promise<Book | null> {
     const book = await prisma.book.findUnique({
       where: { id: parseInt(id) },
-      include: { chapters: { where: { level: 1 }, orderBy: { number: 'asc' } } },
+      include: { 
+        chapters: { 
+          orderBy: [
+            { level: 'asc' },
+            { number: 'asc' }
+          ] 
+        } 
+      },
     });
     
     if (!book) return null;
     
+    // 构建树形结构的outline(从数据库章节记录构建)
+    const outline = this.buildOutlineFromChapters(book.chapters);
+    
     let result = this.toBook(book);
     
+    // 用数据库构建的outline替换outlineJson解析的
+    if (outline) {
+      result.outline = outline;
+    }
+    
     // 如果需要过滤公开音频
     if (filterPublic && userId) {
       const isOwner = book.userId === userId;