'use strict'; const superagent = require('superagent'); module.exports.getBooks = async (ctx, next) => { try { const { models } = ctx.fs.dc; const books = await models.Book.findAll({ order: [['createdAt', 'DESC']] }); ctx.status = 200; ctx.body = books; } catch (error) { ctx.logger.log(error); ctx.status = 400; ctx.body = { message: typeof error == 'string' ? error : '获取书籍列表失败' }; } }; module.exports.createBook = async (ctx, next) => { try { const { models } = ctx.fs.dc; const { name, requirements } = ctx.request.body; if (!name) { throw '书籍名称不能为空'; } const book = await models.Book.create({ name, requirements }); ctx.status = 200; ctx.body = book; } catch (error) { ctx.logger.log(error); ctx.status = 400; ctx.body = { message: typeof error == 'string' ? error : '创建书籍失败' }; } }; module.exports.updateBook = async (ctx, next) => { try { const { models } = ctx.fs.dc; const { id } = ctx.params; const { name, requirements } = ctx.request.body; const book = await models.Book.findByPk(id); if (!book) { throw '书籍不存在'; } await book.update({ name, requirements }); ctx.status = 200; ctx.body = book; } catch (error) { ctx.logger.log(error); ctx.status = 400; ctx.body = { message: typeof error == 'string' ? error : '更新书籍失败' }; } }; module.exports.deleteBook = async (ctx, next) => { try { const { models } = ctx.fs.dc; const { id } = ctx.params; const book = await models.Book.findByPk(id); if (!book) { throw '书籍不存在'; } await book.destroy(); ctx.status = 204; ctx.body = {}; } catch (error) { ctx.logger.log(error); ctx.status = 400; ctx.body = { message: typeof error == 'string' ? error : '删除书籍失败' }; } }; module.exports.getBookChapters = async (ctx, next) => { try { const { models } = ctx.fs.dc; const { bookId } = ctx.params; const chapters = await models.BookChapter.findAll({ where: { bookId }, order: [['level', 'ASC'], ['sortOrder', 'ASC'], ['createdAt', 'ASC']], include: [{ model: models.BookChapterContent, as: 'bookChapterContents', separate: true, order: [['version', 'DESC']], limit: 1 }] }); // 构建树状结构 const buildTree = (chapters, parentId = null) => { return chapters .filter(chapter => chapter.parentId === parentId) .map(chapter => ({ ...chapter.toJSON(), children: buildTree(chapters, chapter.id), latestContent: chapter.bookChapterContents?.[0] || null })); }; const treeData = buildTree(chapters); ctx.status = 200; ctx.body = treeData; } catch (error) { ctx.logger.log(error); ctx.status = 400; ctx.body = { message: typeof error == 'string' ? error : '获取章节列表失败' }; } }; module.exports.createChapter = async (ctx, next) => { try { const { models } = ctx.fs.dc; const { bookId } = ctx.params; const { parentId, level, title, requirements, sortOrder = 0 } = ctx.request.body; if (!title) { throw '章节标题不能为空'; } const chapter = await models.BookChapter.create({ bookId, parentId, level, title, requirements, sortOrder }); ctx.status = 200; ctx.body = chapter; } catch (error) { ctx.logger.log(error); ctx.status = 400; ctx.body = { message: typeof error == 'string' ? error : '创建章节失败' }; } }; module.exports.updateChapter = async (ctx, next) => { try { const { models } = ctx.fs.dc; const { bookId, id } = ctx.params; const { title, requirements, sortOrder, parentId, level } = ctx.request.body; const chapter = await models.BookChapter.findByPk(id); if (!chapter) { throw '章节不存在'; } const updateData = {}; if (title !== undefined) updateData.title = title; if (requirements !== undefined) updateData.requirements = requirements; if (sortOrder !== undefined) updateData.sortOrder = sortOrder; if (parentId !== undefined) updateData.parentId = parentId; if (level !== undefined) updateData.level = level; await chapter.update(updateData); ctx.status = 200; ctx.body = chapter; } catch (error) { ctx.logger.log(error); ctx.status = 400; ctx.body = { message: typeof error == 'string' ? error : '更新章节失败' }; } }; module.exports.deleteChapter = async (ctx, next) => { try { const { models } = ctx.fs.dc; const { bookId, id } = ctx.params; const chapter = await models.BookChapter.findByPk(id); if (!chapter) { throw '章节不存在'; } await chapter.destroy(); ctx.status = 204; ctx.body = {}; } catch (error) { ctx.logger.log(error); ctx.status = 400; ctx.body = { message: typeof error == 'string' ? error : '删除章节失败' }; } }; module.exports.generateChapterContent = async (ctx, next) => { try { const { models } = ctx.fs.dc; const { bookId, id } = ctx.params; const chapter = await models.BookChapter.findByPk(id, { include: [{ model: models.Book, as: 'book' }] }); if (!chapter) { throw '章节不存在'; } // 更新状态为生成中 await chapter.update({ generationStatus: 'generating' }); // 立即返回响应,避免前端超时 ctx.status = 200; ctx.body = { message: '已开始生成,请稍后查看', status: 'generating' }; // 异步执行生成逻辑 (async () => { try { // 获取书籍整体要求 const book = chapter.book; // 获取父章节信息 (如果存在) let topChapterName = chapter.title; let topChapterRequirements = chapter.requirements || ''; if (chapter.parentId) { const parentChapter = await models.BookChapter.findByPk(chapter.parentId); if (parentChapter) { topChapterName = parentChapter.title; topChapterRequirements = parentChapter.requirements || ''; } } // 真实调用逻辑 const requestBody = { stream: false, detail: false, variables: { outline: book.requirements || '', topChapterName: topChapterName, topChapterRequirements: topChapterRequirements, chapterRequirements: chapter.requirements || '' }, messages: [{ role: 'user', content: [{ type: 'text', text: chapter.title }] }] }; const response = await superagent .post(`${ctx.app.fs.config.fastGpt.apiUrl}/api/v1/chat/completions`) .send(requestBody) .set({ 'Authorization': `Bearer ${ctx.app.fs.config.fastGpt.bookWriterAppKey}`, 'Content-Type': 'application/json' }); const data = JSON.parse(response.text); const content = data?.choices?.[0]?.message?.content || ''; // 模拟生成延迟 (10秒) // await new Promise(resolve => setTimeout(resolve, 10000)); // const content = `## ${chapter.title}\n\nThis is a simulated generated content for **${chapter.title}**.\n\nTime: ${new Date().toLocaleString()}\n\nRequirements: ${chapter.requirements || 'None'}`; // 创建新内容版本 const latestContent = await models.BookChapterContent.findOne({ where: { chapterId: chapter.id }, order: [['version', 'DESC']] }); const newVersion = latestContent ? latestContent.version + 1 : 1; await models.BookChapterContent.create({ chapterId: chapter.id, content: content, version: newVersion }); // 更新状态为成功 await chapter.update({ generationStatus: 'success' }); } catch (error) { console.error('Generation failed:', error); // 更新状态为失败 try { await chapter.update({ generationStatus: 'failed' }); } catch (e) { console.error('Update status failed:', e); } } })(); } catch (error) { ctx.logger.log(error); ctx.status = 400; ctx.body = { message: typeof error == 'string' ? error : '生成章节内容失败' }; } }; module.exports.getChapterContent = async (ctx, next) => { try { const { models } = ctx.fs.dc; const { bookId, chapterId } = ctx.params; const contents = await models.BookChapterContent.findAll({ where: { chapterId }, order: [['version', 'DESC']] }); ctx.status = 200; ctx.body = contents; } catch (error) { ctx.logger.log(error); ctx.status = 400; ctx.body = { message: typeof error == 'string' ? error : '获取章节内容失败' }; } }; module.exports.polishContent = async (ctx, next) => { try { const { models } = ctx.fs.dc; const { bookId, chapterId } = ctx.params; const { requirements } = ctx.request.body; const chapter = await models.BookChapter.findByPk(chapterId, { include: [{ model: models.BookChapterContent, order: [['version', 'DESC']], limit: 1 }] }); const latestContent = chapter.bookChapterContents?.[0]; if (!chapter) { throw '章节不存在'; } if (!latestContent) { throw '章节内容不存在'; } // 更新状态为正在润色 await chapter.update({ generationStatus: 'polishing' }); // 立即返回响应,避免前端超时 ctx.status = 200; ctx.body = { message: '内容正在润色中', }; // 异步执行润色 (async () => { try { // 真实调用逻辑 const requestBody = { stream: false, detail: false, variables: { requirements: requirements || '', }, messages: [{ role: 'user', content: [{ type: 'text', text: latestContent.content || '' }] }] }; const response = await superagent .post(`${ctx.app.fs.config.fastGpt.apiUrl}/api/v1/chat/completions`) .send(requestBody) .set({ 'Authorization': `Bearer ${ctx.app.fs.config.fastGpt.polishAppKey}`, 'Content-Type': 'application/json' }); const data = JSON.parse(response.text); const polishedContent = data?.choices?.[0]?.message?.content || ''; // 模拟润色延迟 // await new Promise(resolve => setTimeout(resolve, 5000)); // const polishedContent = latestContent.content; // 创建新章节内容 await models.BookChapterContent.create({ chapterId: chapter.id, content: polishedContent, version: latestContent.version + 1 }); // 更新状态为成功 await chapter.update({ generationStatus: 'success' }); } catch (error) { console.error('Polish failed:', error); // 更新状态为失败 try { await chapter.update({ generationStatus: 'polishFailed' }); } catch (e) { console.error('Update status polishFailed:', e); } } })(); } catch (error) { ctx.logger.log(error); ctx.status = 400; ctx.body = { message: typeof error == 'string' ? error : '润色失败' }; } }; module.exports.restoreChapterVersion = async (ctx, next) => { try { const { models } = ctx.fs.dc; const { bookId, chapterId, contentId } = ctx.params; const targetContent = await models.BookChapterContent.findByPk(contentId); if (!targetContent) { throw '指定版本不存在'; } if (parseInt(targetContent.chapterId) !== parseInt(chapterId)) { throw '版本与章节不匹配'; } const latestContent = await models.BookChapterContent.findOne({ where: { chapterId }, order: [['version', 'DESC']] }); const newVersion = latestContent ? latestContent.version + 1 : 1; await models.BookChapterContent.create({ chapterId, content: targetContent.content, version: newVersion }); // 同时也应该把章节状态重置为success,防止处在failed状态 await models.BookChapter.update( { generationStatus: 'success' }, { where: { id: chapterId } } ); ctx.status = 200; ctx.body = { message: '版本回退成功' }; } catch (error) { ctx.logger.log(error); ctx.status = 400; ctx.body = { message: typeof error == 'string' ? error : '版本回退失败' }; } }; module.exports.saveChapterContent = async (ctx, next) => { try { const { models } = ctx.fs.dc; const { bookId, chapterId } = ctx.params; const { content } = ctx.request.body; const chapter = await models.BookChapter.findByPk(chapterId); if (!chapter) { throw '章节不存在'; } const latestContent = await models.BookChapterContent.findOne({ where: { chapterId }, order: [['version', 'DESC']] }); const newVersion = latestContent ? latestContent.version + 1 : 1; await models.BookChapterContent.create({ chapterId, content, version: newVersion }); await chapter.update({ generationStatus: 'success' }); ctx.status = 200; ctx.body = { message: '保存成功', version: newVersion }; } catch (error) { ctx.logger.log(error); ctx.status = 400; ctx.body = { message: typeof error == 'string' ? error : '保存章节内容失败' }; } };