book-generator.store.js 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268
  1. "use strict";
  2. /**
  3. * 书籍生成模块 - Prisma 数据库存储
  4. */
  5. Object.defineProperty(exports, "__esModule", { value: true });
  6. exports.bookStore = exports.BookStore = void 0;
  7. const models_1 = require("../../models");
  8. const tts_service_1 = require("../tts/tts.service");
  9. // ============ 类型转换 ============
  10. function parseOutlineJson(jsonStr) {
  11. if (!jsonStr)
  12. return null;
  13. try {
  14. return JSON.parse(jsonStr);
  15. }
  16. catch {
  17. return null;
  18. }
  19. }
  20. function chaptersFromDb(dbChapters, bookId) {
  21. return dbChapters.map((c) => ({
  22. id: String(c.id),
  23. bookId: String(c.bookId),
  24. number: c.number,
  25. title: c.title,
  26. content: c.content || '',
  27. wordCount: c.wordCount,
  28. status: c.status,
  29. summary: c.summary || undefined,
  30. generatedAt: c.generatedAt || undefined,
  31. error: c.errorMsg || undefined,
  32. audioUrl: c.audioUrl || undefined,
  33. audioDuration: c.audioDuration || 0,
  34. videoUrl: c.videoUrl || undefined,
  35. videoDuration: c.videoDuration || undefined,
  36. }));
  37. }
  38. function outlineChapterFromDb(dbChapter) {
  39. return {
  40. number: dbChapter.number,
  41. title: dbChapter.title,
  42. summary: dbChapter.summary || '',
  43. keyPoints: dbChapter.keyPoints ? JSON.parse(dbChapter.keyPoints) : [],
  44. estimatedWords: dbChapter.estimatedWords,
  45. };
  46. }
  47. // ============ 存储类 ============
  48. class BookStore {
  49. /**
  50. * 创建书籍
  51. */
  52. async create(data) {
  53. const book = await models_1.prisma.book.create({
  54. data: {
  55. userId: data.userId,
  56. title: data.title,
  57. subtitle: data.subtitle,
  58. description: data.description,
  59. targetAudience: data.targetAudience || '通用',
  60. style: data.style || '专业严谨',
  61. totalChapters: data.totalChapters || 10,
  62. status: 'draft',
  63. progress: 0,
  64. isPublished: false, // 预发布:等书籍完成后再发布
  65. },
  66. include: { chapters: true },
  67. });
  68. return this.toBook(book);
  69. }
  70. /**
  71. * 获取书籍
  72. */
  73. async getById(id) {
  74. const book = await models_1.prisma.book.findUnique({
  75. where: { id: parseInt(id) },
  76. include: { chapters: { orderBy: { number: 'asc' } } },
  77. });
  78. return book ? this.toBook(book) : null;
  79. }
  80. /**
  81. * 获取用户的所有书籍
  82. */
  83. async getAllByUser(userId) {
  84. const books = await models_1.prisma.book.findMany({
  85. where: userId ? { userId } : {},
  86. include: { chapters: true },
  87. orderBy: { createdAt: 'desc' },
  88. });
  89. return books.map((b) => this.toBook(b));
  90. }
  91. /**
  92. * 更新书籍
  93. */
  94. async update(id, data) {
  95. const book = await models_1.prisma.book.update({
  96. where: { id: parseInt(id) },
  97. data: {
  98. ...data,
  99. updatedAt: new Date(),
  100. },
  101. include: { chapters: { orderBy: { number: 'asc' } } },
  102. });
  103. return this.toBook(book);
  104. }
  105. /**
  106. * 删除书籍
  107. */
  108. async delete(id) {
  109. try {
  110. await models_1.prisma.book.delete({ where: { id: parseInt(id) } });
  111. return true;
  112. }
  113. catch {
  114. return false;
  115. }
  116. }
  117. /**
  118. * 创建章节
  119. */
  120. async createChapter(data) {
  121. await models_1.prisma.bookChapter.create({
  122. data: {
  123. bookId: parseInt(data.bookId),
  124. number: data.number,
  125. title: data.title,
  126. summary: data.summary,
  127. keyPoints: data.keyPoints ? JSON.stringify(data.keyPoints) : null,
  128. estimatedWords: data.estimatedWords || 1000,
  129. status: 'pending',
  130. },
  131. });
  132. }
  133. /**
  134. * 批量创建章节
  135. */
  136. async createChapters(bookId, chapters) {
  137. await models_1.prisma.bookChapter.createMany({
  138. data: chapters.map((c) => ({
  139. bookId: parseInt(bookId),
  140. number: c.number,
  141. title: c.title,
  142. summary: c.summary,
  143. keyPoints: c.keyPoints ? JSON.stringify(c.keyPoints) : null,
  144. estimatedWords: c.estimatedWords || 1000,
  145. status: 'pending',
  146. })),
  147. });
  148. }
  149. /**
  150. * 更新章节内容
  151. */
  152. async updateChapter(bookId, chapterNumber, data) {
  153. const chapter = await models_1.prisma.bookChapter.findFirst({
  154. where: {
  155. bookId: parseInt(bookId),
  156. number: chapterNumber,
  157. },
  158. });
  159. if (!chapter)
  160. return null;
  161. const updated = await models_1.prisma.bookChapter.update({
  162. where: { id: chapter.id },
  163. data: {
  164. ...data,
  165. generatedAt: data.content ? new Date() : undefined,
  166. },
  167. });
  168. return {
  169. id: String(updated.id),
  170. bookId: String(updated.bookId),
  171. number: updated.number,
  172. title: updated.title,
  173. content: updated.content || '',
  174. wordCount: updated.wordCount,
  175. status: updated.status,
  176. summary: updated.summary || undefined,
  177. generatedAt: updated.generatedAt || undefined,
  178. error: updated.errorMsg || undefined,
  179. };
  180. }
  181. /**
  182. * 获取书籍的章节
  183. */
  184. async getChapters(bookId) {
  185. const chapters = await models_1.prisma.bookChapter.findMany({
  186. where: { bookId: parseInt(bookId) },
  187. orderBy: { number: 'asc' },
  188. });
  189. return chaptersFromDb(chapters, parseInt(bookId));
  190. }
  191. /**
  192. * 统计书籍完成章节数
  193. */
  194. async countCompletedChapters(bookId) {
  195. return models_1.prisma.bookChapter.count({
  196. where: {
  197. bookId: parseInt(bookId),
  198. status: 'completed',
  199. },
  200. });
  201. }
  202. /**
  203. * 发布书籍(将 isPublished 设为 true)
  204. */
  205. async publishAlbum(bookId) {
  206. await models_1.prisma.book.update({
  207. where: { id: parseInt(bookId) },
  208. data: { isPublished: true },
  209. });
  210. }
  211. /**
  212. * 为书籍章节生成音频并关联(更新 BookChapter.audioUrl)
  213. */
  214. async generateChapterAudio(bookId, chapterNumber, userId) {
  215. const chapter = await models_1.prisma.bookChapter.findFirst({
  216. where: { bookId: parseInt(bookId), number: chapterNumber },
  217. include: { book: true },
  218. });
  219. if (!chapter || !chapter.content) {
  220. return null;
  221. }
  222. // 生成音频(异步模式,通过回调更新章节)
  223. const result = await (0, tts_service_1.generateAudio)(userId ? String(userId) : String(chapter.book?.userId || '0'), chapter.content, 'default', { speed: 1.0, pitch: 0, volume: 50 }, async (audioUrl, duration) => {
  224. // 音频生成完成后更新章节
  225. await models_1.prisma.bookChapter.update({
  226. where: { id: chapter.id },
  227. data: {
  228. audioUrl,
  229. audioDuration: duration,
  230. },
  231. });
  232. console.log(`✅ 章节${chapterNumber}音频生成完成:`, audioUrl);
  233. });
  234. return {
  235. audioUrl: result.audioUrl, // 初始为空字符串,实际URL通过回调更新
  236. };
  237. }
  238. /**
  239. * 转换数据库模型到 Book 类型
  240. */
  241. toBook(dbBook) {
  242. const outline = parseOutlineJson(dbBook.outlineJson);
  243. return {
  244. id: String(dbBook.id),
  245. title: dbBook.title,
  246. subtitle: dbBook.subtitle || undefined,
  247. description: dbBook.description,
  248. targetAudience: dbBook.targetAudience,
  249. style: dbBook.style,
  250. totalChapters: dbBook.totalChapters,
  251. estimatedWords: dbBook.estimatedWords,
  252. status: dbBook.status,
  253. progress: dbBook.progress,
  254. chapters: chaptersFromDb(dbBook.chapters, dbBook.id),
  255. outline: outline || undefined,
  256. metadata: {
  257. foreword: dbBook.foreword || undefined,
  258. afterword: dbBook.afterword || undefined,
  259. },
  260. error: dbBook.errorMsg || undefined,
  261. createdAt: dbBook.createdAt,
  262. updatedAt: dbBook.updatedAt,
  263. };
  264. }
  265. }
  266. exports.BookStore = BookStore;
  267. // 导出单例
  268. exports.bookStore = new BookStore();