| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674 |
- /**
- * 书籍生成模块 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<Book> {
- const result = await request<{ book: Book }>(`${BASE_URL}/books`, { method: 'POST', data: { ...data } });
- return result.book;
- }
- /**
- * 获取所有书籍
- */
- export async function getBooks(): Promise<Book[]> {
- const result = await request<{ books: Book[] }>(`${BASE_URL}/books`);
- return result.books || [];
- }
- /**
- * 获取公开书籍列表(首页专用)
- */
- export async function getPublicBooks(): Promise<Book[]> {
- const result = await request<{ books: Book[] }>(`${BASE_URL}/public-books`);
- return result.books || [];
- }
- /**
- * 获取书籍详情
- */
- export async function getBook(id: string): Promise<Book> {
- const result = await request<{ book: Book }>(`${BASE_URL}/books/${id}`);
- return result.book;
- }
- /**
- * 获取单个章节详情(从专辑控制器获取,只返回单章节内容)
- */
- export async function getChapter(chapterId: string): Promise<any> {
- return await request<any>(`/book-generator/books/chapters/${chapterId}`);
- }
- /**
- * 删除书籍
- */
- export async function deleteBook(id: string): Promise<void> {
- await request(`${BASE_URL}/books/${id}`, { method: 'DELETE' });
- }
- /**
- * 生成大纲
- */
- export async function generateOutline(id: string): Promise<BookOutline> {
- 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<Chapter> {
- 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<Chapter[]> {
- const result = await request<{ chapters: Chapter[] }>(`${BASE_URL}/books/${id}/chapters`, { method: 'POST' });
- return result.chapters || [];
- }
- /**
- * 生成前言
- */
- export async function generateForeword(id: string): Promise<string> {
- const result = await request<{ foreword: string }>(`${BASE_URL}/books/${id}/foreword`, { method: 'POST' });
- return result.foreword;
- }
- /**
- * 生成后记
- */
- export async function generateAfterword(id: string): Promise<string> {
- 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<any>(`${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<any> {
- const result = await request<any>(`${BASE_URL}/books/${bookId}/interactive/plan`, { method: 'GET', config: { timeout: 5000 } });
- return result;
- }
- /**
- * 保存用户修改后的 Plan
- */
- export async function updateInteractivePlan(bookId: string, plan: string): Promise<void> {
- 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<any>(`${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<any> {
- const result = await request<any>(`${BASE_URL}/books/${bookId}/interactive/outline`, { method: 'GET', config: { timeout: 5000 } });
- return result;
- }
- /**
- * 保存用户修改后的大纲
- */
- export async function updateInteractiveOutline(bookId: string, outline: any): Promise<void> {
- 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<any>(`${BASE_URL}/books/${bookId}/interactive/generate`, { method: 'POST', config: { timeout: 10000 } });
- return result;
- }
- /**
- * 创建书籍但不自动生成(交互模式 Step 1 使用)
- */
- export async function createBookInteractive(data: CreateBookRequest): Promise<Book> {
- 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<string> {
- const result = await request<{ content: string }>(`${BASE_URL}/books/${id}/full-content`);
- return result.content;
- }
- /**
- * 获取生成进度
- */
- export async function getProgress(id: string): Promise<ProgressResponse> {
- const result = await request<ProgressResponse>(`${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<WorkflowProgress | null> {
- const result = await request<WorkflowProgress>(`${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<any> {
- return await request<any>(`/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);
- }
|