All files / modules/book-generator book-generator.controller.ts

0% Statements 0/141
0% Branches 0/1
0% Functions 0/1
0% Lines 0/141

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199                                                                                                                                                                                                                                                                                                                                                                                                             
/**
 * 书籍生成 - API 路由
 * 包含一键完整生成 API
 */
 
import Router from '@koa/router';
import { Context } from 'koa';
import { bookStore } from './book-generator.store';
import {
  createBatchGenerationTask,
  setCancellationFlag,
  GenerationStep
} from './book-generator.service';
 
// 任务存储(用于取消操作)
const runningTasks = new Map<string, { taskId: string; bookId: string }>();
 
const router = new Router();
 
/**
 * POST /api/book-generator/books/:id/batch-generate
 * 一键完整生成书籍(内容、音频、合并、生成视频、合并视频)
 */
router.post('/books/:id/batch-generate', async (ctx: Context) => {
  try {
    const bookId = ctx.params.id as string;
    const body = ctx.request.body as {
      steps?: GenerationStep[];
    };
 
    // 验证书籍存在
    const book = await bookStore.getById(bookId);
    if (!book) {
      ctx.status = 404;
      ctx.body = { code: 1, message: '书籍不存在' };
      return;
    }
 
    // 默认执行全部步骤
    const steps: GenerationStep[] = body.steps || [
      'generate_content',
      'generate_audio',
      'merge_audio',
      'generate_video',
      'merge_video'
    ];
 
    // 验证步骤
    const validSteps: GenerationStep[] = [
      'generate_content',
      'generate_audio',
      'merge_audio',
      'generate_video',
      'merge_video'
    ];
    for (const step of steps) {
      if (!validSteps.includes(step)) {
        ctx.status = 400;
        ctx.body = { code: 1, message: `无效的步骤: ${step}` };
        return;
      }
    }
 
    // 检查是否有正在运行的批量生成任务
    const existingTask = runningTasks.get(bookId);
    if (existingTask) {
      ctx.status = 400;
      ctx.body = {
        code: 1,
        message: '书籍已有批量生成任务在运行,请先取消后再试',
        data: { taskId: existingTask.taskId }
      };
      return;
    }
 
    // 创建批量生成任务
    const { taskId, orchestrator } = await createBatchGenerationTask(bookId, steps);
 
    // 存储任务信息
    runningTasks.set(bookId, { taskId, bookId });
 
    // 后台执行任务
    (async () => {
      try {
        const result = await orchestrator.execute();
        console.log(`[BatchGen][${taskId}] 任务完成:`, result);
 
        // 发送完成消息
        const { pushBatchGenerationProgress } = await import('../../services/websocket.service.js');
        pushBatchGenerationProgress(taskId, 'completed', 100);
 
      } catch (error: any) {
        console.error(`[BatchGen][${taskId}] 任务异常:`, error);
      } finally {
        // 清理任务
        runningTasks.delete(bookId);
      }
    })();
 
    ctx.body = {
      code: 0,
      message: '批量生成任务已启动',
      data: {
        taskId,
        bookId,
        status: 'started',
        steps
      }
    };
 
  } catch (error) {
    console.error('[BatchGenerate] 启动失败:', error);
    ctx.status = 500;
    ctx.body = {
      code: 1,
      message: error instanceof Error ? error.message : '启动失败'
    };
  }
});
 
/**
 * POST /api/book-generator/books/:id/batch-generate/cancel
 * 取消批量生成任务
 */
router.post('/books/:id/batch-generate/cancel', async (ctx: Context) => {
  try {
    const bookId = ctx.params.id as string;
 
    const task = runningTasks.get(bookId);
    if (!task) {
      ctx.status = 404;
      ctx.body = { code: 1, message: '没有正在运行的批量生成任务' };
      return;
    }
 
    // 设置取消标志
    setCancellationFlag(task.taskId);
 
    ctx.body = {
      code: 0,
      message: '取消指令已发送',
      data: {
        taskId: task.taskId,
        bookId
      }
    };
 
  } catch (error) {
    console.error('[BatchGenerate] 取消失败:', error);
    ctx.status = 500;
    ctx.body = {
      code: 1,
      message: error instanceof Error ? error.message : '取消失败'
    };
  }
});
 
/**
 * GET /api/book-generator/books/:id/batch-generate/status
 * 获取批量生成任务状态
 */
router.get('/books/:id/batch-generate/status', async (ctx: Context) => {
  try {
    const bookId = ctx.params.id as string;
 
    const task = runningTasks.get(bookId);
    if (!task) {
      ctx.body = {
        code: 0,
        message: 'success',
        data: {
          isRunning: false,
          bookId
        }
      };
      return;
    }
 
    ctx.body = {
      code: 0,
      message: 'success',
      data: {
        isRunning: true,
        taskId: task.taskId,
        bookId: task.bookId
      }
    };
 
  } catch (error) {
    console.error('[BatchGenerate] 查询状态失败:', error);
    ctx.status = 500;
    ctx.body = {
      code: 1,
      message: error instanceof Error ? error.message : '查询失败'
    };
  }
});
 
export default router;