/** * 书籍生成模块 API */ import { request, get, post } from './request'; // ============ 类型定义 ============ export interface Book { id: string; userId?: number; title: string; subtitle?: string; description: string; targetAudience: string; style: string; bookScale?: string; totalChapters: number; estimatedWords: number; progress: number; chapters: Chapter[]; outline?: BookOutline; metadata?: BookMetadata; error?: string; isPublished?: boolean; genStage?: BookGenStage; // 线性阶段状态(替代旧 status 字段) failedStage?: string; // 失败阶段 createdAt: string; updatedAt: string; } export interface BookOutline { mainTheme: string; structureLogic: string; chapters: OutlineChapter[]; } export interface OutlineSubsection { title: string; summary?: string; } export interface OutlineSection { title: string; summary: string; subsections?: OutlineSubsection[]; } export interface OutlineChapter { number: number; title: string; summary: string; keyPoints: string[]; estimatedWords: number; stories?: string[]; sections?: OutlineSection[]; } export interface Chapter { id: string; bookId: string; number: number; title: string; content: string; summary?: string; wordCount: number; audioUrl?: string; audioDuration?: number; audioStatus?: string; videoUrl?: string; videoDuration?: number; videoStatus?: string; contentStatus?: string; contentError?: string; error?: string; level?: number; // 层级:1=章, 2=节, 3=小节 parentId?: number | null; // 父节点ID(null表示章) genStage?: ChapterGenStage; // 线性阶段状态(替代旧 status 字段) } // ============ 线性阶段类型(与后端 stage-manager.ts 同步) ============ export type BookGenStage = | 'draft' | 'outlining' | 'outline_completed' | 'content_generating' | 'content_completed' | 'audio_generating' | 'audio_completed' | 'video_generating' | 'video_completed' | 'failed'; export type ChapterGenStage = | 'idle' | 'outline_completed' | 'content_generating' | 'content_completed' | 'audio_generating' | 'audio_completed' | 'video_generating' | 'video_completed' | 'failed'; export interface BookMetadata { author?: string; foreword?: string; afterword?: string; } export interface CreateBookRequest { title: string; subtitle?: string; description: string; targetAudience?: string; style?: string; bookScale?: string; bookType?: string; genLevel?: number; language?: string; } export interface ProgressResponse { bookId: string; progress: number; genStage?: string; completedChapters: number; totalChapters: number; currentTask?: { type: string; message: string; }; } // ============ API 函数 ============ const BASE_URL = '/book-generator/langgraph'; /** * 创建书籍 */ export async function createBook(data: CreateBookRequest): Promise { const result = await request<{ book: Book }>(`${BASE_URL}/books`, { method: 'POST', data: { ...data } }); return result.book; } /** * 获取所有书籍 */ export async function getBooks(): Promise { const result = await request<{ books: Book[] }>(`${BASE_URL}/books`); return result.books || []; } /** * 获取公开书籍列表(首页专用) */ export async function getPublicBooks(): Promise { const result = await request<{ books: Book[] }>(`${BASE_URL}/public-books`); return result.books || []; } /** * 获取书籍详情 */ export async function getBook(id: string): Promise { const result = await request<{ book: Book }>(`${BASE_URL}/books/${id}`); return result.book; } /** * 获取单个章节详情(从专辑控制器获取,只返回单章节内容) */ export async function getChapter(chapterId: string): Promise { return await request(`/book-generator/books/chapters/${chapterId}`); } /** * 删除书籍 */ export async function deleteBook(id: string): Promise { await request(`${BASE_URL}/books/${id}`, { method: 'DELETE' }); } /** * 生成大纲 */ export async function generateOutline(id: string): Promise { const result = await request<{ outline: BookOutline }>(`${BASE_URL}/books/${id}/outline`, { method: 'POST' }); return result.outline; } /** * 生成单个章节 */ export async function generateChapter(id: string, chapterNumber: number): Promise { const result = await request<{ chapter: Chapter }>(`${BASE_URL}/books/${id}/chapters`, { method: 'POST', data: { chapterNumber }, }); return result.chapter; } /** * 生成全部章节 */ export async function generateAllChapters(id: string): Promise { const result = await request<{ chapters: Chapter[] }>(`${BASE_URL}/books/${id}/chapters`, { method: 'POST' }); return result.chapters || []; } /** * 生成前言 */ export async function generateForeword(id: string): Promise { const result = await request<{ foreword: string }>(`${BASE_URL}/books/${id}/foreword`, { method: 'POST' }); return result.foreword; } /** * 生成后记 */ export async function generateAfterword(id: string): Promise { const result = await request<{ afterword: string }>(`${BASE_URL}/books/${id}/afterword`, { method: 'POST' }); return result.afterword; } // ============ 交互式生成 API ============ /** * 启动深度规划(异步,立即返回) */ export async function startInteractivePlan(bookId: string, feedback?: string): Promise<{ genStage: string }> { const result = await request(`${BASE_URL}/books/${bookId}/interactive/plan`, { method: 'POST', data: feedback ? { feedback } : undefined, config: { timeout: 10000 }, }); return result; } /** * 轮询 Plan 生成状态与结果 * 返回 { genStage, rawPlan?, planData?, error? } */ export async function pollInteractivePlan(bookId: string): Promise { const result = await request(`${BASE_URL}/books/${bookId}/interactive/plan`, { method: 'GET', config: { timeout: 5000 } }); return result; } /** * 保存用户修改后的 Plan */ export async function updateInteractivePlan(bookId: string, plan: string): Promise { await request(`${BASE_URL}/books/${bookId}/interactive/plan`, { method: 'PUT', data: { plan } }); } /** * 启动大纲生成(异步,立即返回) */ export async function startInteractiveOutline(bookId: string): Promise<{ genStage: string }> { const result = await request(`${BASE_URL}/books/${bookId}/interactive/outline`, { method: 'POST', config: { timeout: 10000 } }); return result; } /** * 轮询 Outline 生成状态与结果 * 返回 { genStage, rawOutline?, outlineData?, error? } */ export async function pollInteractiveOutline(bookId: string): Promise { const result = await request(`${BASE_URL}/books/${bookId}/interactive/outline`, { method: 'GET', config: { timeout: 5000 } }); return result; } /** * 保存用户修改后的大纲 */ export async function updateInteractiveOutline(bookId: string, outline: any): Promise { await request(`${BASE_URL}/books/${bookId}/interactive/outline`, { method: 'PUT', data: { outline } }); } /** * 运行内容生成(交互模式 Step 4) */ export async function generateInteractiveContent(bookId: string): Promise<{ bookId: string; genStage: string; jobId?: string }> { const result = await request(`${BASE_URL}/books/${bookId}/interactive/generate`, { method: 'POST', config: { timeout: 10000 } }); return result; } /** * 创建书籍但不自动生成(交互模式 Step 1 使用) */ export async function createBookInteractive(data: CreateBookRequest): Promise { const result = await request<{ book: Book }>(`${BASE_URL}/books`, { method: 'POST', data: { ...data, immediateGenerate: false } }); return result.book; } /** * 获取完整书籍内容 */ export async function getFullContent(id: string): Promise { const result = await request<{ content: string }>(`${BASE_URL}/books/${id}/full-content`); return result.content; } /** * 获取生成进度 */ export async function getProgress(id: string): Promise { const result = await request(`${BASE_URL}/books/${id}/progress`); return result; } /** * 一键生成整本书 */ export async function generateBook( id: string, options?: { generateForeword?: boolean; generateAfterword?: boolean; } ): Promise<{ success: boolean; completedChapters: number; failedChapters: number; foreword?: string; afterword?: string; errors: string[]; }> { const result = await request<{ success: boolean; completedChapters: number; failedChapters: number; foreword?: string; afterword?: string; errors: string[]; }>(`${BASE_URL}/books/${id}/generate`, { method: 'POST', data: options || {}, }); return result; } /** * 工作流状态 */ export interface WorkflowProgress { phase: 'planning' | 'writing' | 'supplement' | 'done'; currentNode: string; currentChapter: number; completedChapters: number; totalChapters: number; pendingChapters: number[]; failedChapters: number[]; progress: number; } /** * 获取工作流状态 */ export async function getWorkflowProgress(bookId: string): Promise { const result = await request(`${BASE_URL}/workflow/${bookId}`); return result; } // ============ LangGraph API ============ /** * 使用 LangGraph 创建并生成书籍 */ export async function createBookWithLangGraph(data: { title: string; description: string; totalChapters?: number; }): Promise<{ bookId: string; taskId: string }> { const result = await request<{ bookId: string; taskId: string }>(`${BASE_URL}/books`, { method: 'POST', data, }); return result; } /** * 使用 LangGraph 生成已有书籍 */ export async function generateWithLangGraph(bookId: string): Promise<{ bookId: string; taskId: string }> { const result = await request<{ bookId: string; taskId: string }>(`${BASE_URL}/books/${bookId}/generate`, { method: 'POST', }); return result; } // ============ 音频生成 API ============ /** * 生成单个章节音频(实际调用批量生成,后端会生成所有章节) */ export async function generateChapterAudio( bookId: string, chapterNumber: number, voiceId: string = 'voice_01' ): Promise<{ taskId: string; totalSubsections: number }> { // 后端只有批量生成API,所以调用批量生成 const result = await request<{ taskId: string; totalSubsections: number }>( `${BASE_URL}/books/${bookId}/audio`, { method: 'POST', data: { voiceId }, } ); return result; } /** * 批量生成书籍所有章节音频 */ export async function generateAllChaptersAudio( bookId: string, voiceId: string = 'voice_01' ): Promise<{ taskId: string; totalChapters: number }> { const result = await request<{ taskId: string; totalSubsections: number }>( `${BASE_URL}/books/${bookId}/audio`, { method: 'POST', data: { voiceId }, } ); return { taskId: result.taskId, totalChapters: result.totalSubsections, }; } // ============ 视频生成 API ============ /** * 取消书籍所有章节音频生成 */ export async function cancelAudioGeneration( bookId: string ): Promise<{ cancelledCount: number; rolledBackChapters: number[] }> { const result = await request<{ cancelledCount: number; rolledBackChapters: number[] }>( `${BASE_URL}/books/${bookId}/audio/cancel`, { method: 'POST' } ); return result; } /** * 生成单个章节视频(从音频转视频) */ export async function generateChapterVideo( bookId: string, chapterId: string | number ): Promise<{ projectId: number; chapterId: string }> { const result = await request<{ projectId: number; chapterId: string }>( `${BASE_URL}/books/${bookId}/chapters/${chapterId}/video`, { method: 'POST', } ); return result; } /** * 批量生成书籍所有章节视频 */ export async function generateAllChaptersVideo( bookId: string ): Promise<{ taskId: string; totalChapters: number }> { const result = await request<{ taskId: string; totalChapters: number }>( `${BASE_URL}/books/${bookId}/videos`, { method: 'POST', } ); return result; } /** * 合并叶节点音频到上级章节 */ export async function mergeChapterAudios( bookId: string ): Promise<{ taskId: string; processedParents: number; totalLeafNodes: number }> { const result = await request<{ taskId: string; processedParents: number; totalLeafNodes: number }>( `${BASE_URL}/books/${bookId}/merge-audio`, { method: 'POST', } ); return result; } /** * 合并叶节点视频到上级章节 */ export async function mergeChapterVideos( bookId: string ): Promise<{ taskId: string; processedParents: number; totalLeafNodes: number }> { const result = await request<{ taskId: string; processedParents: number; totalLeafNodes: number }>( `${BASE_URL}/books/${bookId}/merge-video`, { method: 'POST', } ); return result; } /** * 获取书籍音频生成状态(用于轮询) */ export async function getAudioStatus(bookId: string): Promise<{ bookId: string; total: number; completed: number; pending: number; allCompleted: boolean; }> { const result = await request<{ bookId: string; total: number; completed: number; pending: number; allCompleted: boolean; }>(`${BASE_URL}/books/${bookId}/audio-status`); return result; } /** * 获取书籍视频生成状态(用于轮询) */ export async function getVideoStatus(bookId: string): Promise<{ bookId: string; total: number; completed: number; pending: number; allCompleted: boolean; }> { const result = await request<{ bookId: string; total: number; completed: number; pending: number; allCompleted: boolean; }>(`${BASE_URL}/books/${bookId}/video-status`); return result; } /** * 重新生成单个章节内容(仅叶节点) */ export async function regenerateChapterContent( bookId: string, chapterId: number ): Promise<{ chapterId: number; jobId: string }> { const result = await request<{ chapterId: number; jobId: string }>( `${BASE_URL}/books/${bookId}/chapters/${chapterId}/regenerate`, { method: 'POST' } ); return result; } /** * 重新生成单个章节音频(仅叶节点) */ export async function regenerateChapterAudio( bookId: string, chapterId: number ): Promise<{ chapterId: number }> { const result = await request<{ chapterId: number }>( `${BASE_URL}/books/${bookId}/chapters/${chapterId}/regenerate-audio`, { method: 'POST' } ); return result; } // ============ 书籍发布 API ============ /** * 切换书籍公开状态 */ export async function toggleBookPublish(bookId: string): Promise<{ isPublished: boolean }> { const result = await request<{ isPublished: boolean }>( `${BASE_URL}/books/${bookId}/publish`, { method: 'PUT' } ); return result; } // ============ 阶段状态辅助函数(与后端 stage-manager.ts 同步) ============ /** * 获取书籍阶段的用户可读描述 */ export function getBookStageDescription(genStage?: string): string { if (!genStage) return '未知'; switch (genStage) { case 'draft': return '草稿'; case 'outlining': return '正在生成大纲...'; case 'outline_completed': return '大纲已完成'; case 'content_generating': return '正在生成内容...'; case 'content_completed': return '内容已完成,等待生成音频'; case 'audio_generating': return '正在生成音频...'; case 'audio_completed': return '音频已完成,等待生成视频'; case 'video_generating': return '正在生成视频...'; case 'video_completed': return '全部完成 ✓'; case 'failed': return '生成失败'; default: return genStage; } } /** * 获取章节阶段的用户可读描述 */ export function getChapterStageDescription(genStage?: string): string { if (!genStage) return '待生成'; switch (genStage) { case 'idle': return '待生成'; case 'outline_completed': return '大纲完成'; case 'content_generating': return '生成中'; case 'content_completed': return '内容完成'; case 'audio_generating': return '生成音频中'; case 'audio_completed': return '音频完成'; case 'video_generating': return '生成视频中'; case 'video_completed': return '已完成'; case 'failed': return '失败'; default: return genStage; } } /** * 判断书籍是否需要显示"生成音频"按钮 */ export function needsAudioGeneration(genStage?: string): boolean { return genStage === 'content_completed'; } /** * 判断书籍是否需要显示"生成视频"按钮 */ export function needsVideoGeneration(genStage?: string): boolean { return genStage === 'audio_completed'; } /** * 判断书籍是否全部完成(最终态) */ export function isBookFullyCompleted(genStage?: string): boolean { return genStage === 'video_completed'; } /** * 获取书籍完整信息(用于编辑) */ export async function getBookForEdit(id: string): Promise { return await request(`/book-generator/books/${id}`); } /** * 更新书籍基础信息 */ export async function updateBook(id: string, data: { title?: string; description?: string; coverUrl?: string; targetAudience?: string; style?: string; bookScale?: string; }): Promise<{ id: number; title: string }> { return post(`/book-generator/books/${id}`, data); }