book-generator.controller.js 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494
  1. "use strict";
  2. /**
  3. * 书籍生成控制器 - API 路由
  4. */
  5. var __importDefault = (this && this.__importDefault) || function (mod) {
  6. return (mod && mod.__esModule) ? mod : { "default": mod };
  7. };
  8. Object.defineProperty(exports, "__esModule", { value: true });
  9. const router_1 = __importDefault(require("@koa/router"));
  10. const book_generator_service_1 = require("./book-generator.service");
  11. const book_generator_store_1 = require("./book-generator.store");
  12. const book_generator_workflow_1 = require("./book-generator.workflow");
  13. const router = new router_1.default();
  14. /**
  15. * POST /book-generator/books
  16. * 创建新书籍
  17. */
  18. router.post('/books', async (ctx) => {
  19. try {
  20. const request = ctx.request.body;
  21. if (!request.title || !request.description) {
  22. ctx.status = 400;
  23. ctx.body = {
  24. code: 1,
  25. message: '书名和描述不能为空',
  26. };
  27. return;
  28. }
  29. const book = await book_generator_service_1.bookGeneratorService.createBook(request);
  30. ctx.body = {
  31. code: 0,
  32. message: 'success',
  33. data: { book },
  34. };
  35. }
  36. catch (error) {
  37. console.error('创建书籍失败:', error);
  38. ctx.status = 500;
  39. ctx.body = {
  40. code: 1,
  41. message: error instanceof Error ? error.message : '创建失败',
  42. };
  43. }
  44. });
  45. /**
  46. * GET /api/book-generator/books
  47. * 获取所有书籍列表
  48. */
  49. router.get('/books', async (ctx) => {
  50. try {
  51. const books = await book_generator_store_1.bookStore.getAllByUser();
  52. ctx.body = {
  53. code: 0,
  54. message: 'success',
  55. data: {
  56. books: books.map((b) => ({
  57. id: b.id,
  58. title: b.title,
  59. subtitle: b.subtitle,
  60. description: b.description,
  61. status: b.status,
  62. progress: b.progress,
  63. totalChapters: b.totalChapters,
  64. completedChapters: b.chapters.filter((c) => c.status === 'completed').length,
  65. createdAt: b.createdAt,
  66. })),
  67. },
  68. };
  69. }
  70. catch (error) {
  71. ctx.status = 500;
  72. ctx.body = {
  73. code: 1,
  74. message: error instanceof Error ? error.message : '获取失败',
  75. };
  76. }
  77. });
  78. /**
  79. * GET /api/book-generator/books/:id
  80. * 获取书籍详情
  81. */
  82. router.get('/books/:id', async (ctx) => {
  83. try {
  84. const bookId = ctx.params.id;
  85. const book = await book_generator_store_1.bookStore.getById(bookId);
  86. if (!book) {
  87. ctx.status = 404;
  88. ctx.body = {
  89. code: 1,
  90. message: '书籍不存在',
  91. };
  92. return;
  93. }
  94. ctx.body = {
  95. code: 0,
  96. message: 'success',
  97. data: { book },
  98. };
  99. }
  100. catch (error) {
  101. ctx.status = 500;
  102. ctx.body = {
  103. code: 1,
  104. message: error instanceof Error ? error.message : '获取失败',
  105. };
  106. }
  107. });
  108. /**
  109. * DELETE /api/book-generator/books/:id
  110. * 删除书籍
  111. */
  112. router.delete('/books/:id', async (ctx) => {
  113. try {
  114. const bookId = ctx.params.id;
  115. const deleted = await book_generator_store_1.bookStore.delete(bookId);
  116. if (!deleted) {
  117. ctx.status = 404;
  118. ctx.body = {
  119. code: 1,
  120. message: '书籍不存在',
  121. };
  122. return;
  123. }
  124. ctx.body = {
  125. code: 0,
  126. message: '删除成功',
  127. };
  128. }
  129. catch (error) {
  130. ctx.status = 500;
  131. ctx.body = {
  132. code: 1,
  133. message: error instanceof Error ? error.message : '删除失败',
  134. };
  135. }
  136. });
  137. /**
  138. * POST /api/book-generator/books/:id/outline
  139. * 生成大纲
  140. */
  141. router.post('/books/:id/outline', async (ctx) => {
  142. try {
  143. const bookId = ctx.params.id;
  144. const outline = await book_generator_service_1.bookGeneratorService.generateOutline(bookId);
  145. ctx.body = {
  146. code: 0,
  147. message: 'success',
  148. data: { outline },
  149. };
  150. }
  151. catch (error) {
  152. console.error('生成大纲失败:', error);
  153. ctx.status = 500;
  154. ctx.body = {
  155. code: 1,
  156. message: error instanceof Error ? error.message : '生成大纲失败',
  157. };
  158. }
  159. });
  160. /**
  161. * POST /api/book-generator/books/:id/chapters
  162. * 生成章节
  163. */
  164. router.post('/books/:id/chapters', async (ctx) => {
  165. try {
  166. const bookId = ctx.params.id;
  167. const body = ctx.request.body;
  168. if (body.chapterNumber) {
  169. // 生成单个章节
  170. const chapter = await book_generator_service_1.bookGeneratorService.generateChapter(bookId, body.chapterNumber);
  171. ctx.body = {
  172. code: 0,
  173. message: 'success',
  174. data: { chapter },
  175. };
  176. }
  177. else {
  178. // 生成全部章节
  179. const chapters = await book_generator_service_1.bookGeneratorService.generateAllChapters(bookId);
  180. ctx.body = {
  181. code: 0,
  182. message: 'success',
  183. data: { chapters },
  184. };
  185. }
  186. }
  187. catch (error) {
  188. console.error('生成章节失败:', error);
  189. ctx.status = 500;
  190. ctx.body = {
  191. code: 1,
  192. message: error instanceof Error ? error.message : '生成章节失败',
  193. };
  194. }
  195. });
  196. /**
  197. * GET /api/book-generator/books/:id/chapters/:chapterNumber
  198. * 获取指定章节
  199. */
  200. router.get('/books/:id/chapters/:chapterNumber', async (ctx) => {
  201. try {
  202. const bookId = ctx.params.id;
  203. const book = await book_generator_store_1.bookStore.getById(bookId);
  204. if (!book) {
  205. ctx.status = 404;
  206. ctx.body = {
  207. code: 1,
  208. message: '书籍不存在',
  209. };
  210. return;
  211. }
  212. const chapterNumber = parseInt(ctx.params.chapterNumber);
  213. const chapter = book.chapters.find((c) => c.number === chapterNumber);
  214. if (!chapter) {
  215. ctx.status = 404;
  216. ctx.body = {
  217. code: 1,
  218. message: '章节不存在',
  219. };
  220. return;
  221. }
  222. ctx.body = {
  223. code: 0,
  224. message: 'success',
  225. data: { chapter },
  226. };
  227. }
  228. catch (error) {
  229. ctx.status = 500;
  230. ctx.body = {
  231. code: 1,
  232. message: error instanceof Error ? error.message : '获取章节失败',
  233. };
  234. }
  235. });
  236. /**
  237. * POST /api/book-generator/books/:id/foreword
  238. * 生成前言
  239. */
  240. router.post('/books/:id/foreword', async (ctx) => {
  241. try {
  242. const bookId = ctx.params.id;
  243. const foreword = await book_generator_service_1.bookGeneratorService.generateForeword(bookId);
  244. ctx.body = {
  245. code: 0,
  246. message: 'success',
  247. data: { foreword },
  248. };
  249. }
  250. catch (error) {
  251. ctx.status = 500;
  252. ctx.body = {
  253. code: 1,
  254. message: error instanceof Error ? error.message : '生成前言失败',
  255. };
  256. }
  257. });
  258. /**
  259. * POST /api/book-generator/books/:id/afterword
  260. * 生成后记
  261. */
  262. router.post('/books/:id/afterword', async (ctx) => {
  263. try {
  264. const bookId = ctx.params.id;
  265. const afterword = await book_generator_service_1.bookGeneratorService.generateAfterword(bookId);
  266. ctx.body = {
  267. code: 0,
  268. message: 'success',
  269. data: { afterword },
  270. };
  271. }
  272. catch (error) {
  273. ctx.status = 500;
  274. ctx.body = {
  275. code: 1,
  276. message: error instanceof Error ? error.message : '生成后记失败',
  277. };
  278. }
  279. });
  280. /**
  281. * GET /api/book-generator/books/:id/full-content
  282. * 获取完整书籍内容
  283. */
  284. router.get('/books/:id/full-content', async (ctx) => {
  285. try {
  286. const bookId = ctx.params.id;
  287. const content = await book_generator_service_1.bookGeneratorService.getFullContent(bookId);
  288. ctx.body = {
  289. code: 0,
  290. message: 'success',
  291. data: { content },
  292. };
  293. }
  294. catch (error) {
  295. ctx.status = 500;
  296. ctx.body = {
  297. code: 1,
  298. message: error instanceof Error ? error.message : '获取内容失败',
  299. };
  300. }
  301. });
  302. /**
  303. * GET /api/book-generator/books/:id/progress
  304. * 获取生成进度
  305. */
  306. router.get('/books/:id/progress', async (ctx) => {
  307. try {
  308. const bookId = ctx.params.id;
  309. const progress = await book_generator_service_1.bookGeneratorService.getProgress(bookId);
  310. if (!progress) {
  311. ctx.status = 404;
  312. ctx.body = {
  313. code: 1,
  314. message: '书籍不存在',
  315. };
  316. return;
  317. }
  318. ctx.body = {
  319. code: 0,
  320. message: 'success',
  321. data: progress,
  322. };
  323. }
  324. catch (error) {
  325. ctx.status = 500;
  326. ctx.body = {
  327. code: 1,
  328. message: error instanceof Error ? error.message : '获取进度失败',
  329. };
  330. }
  331. });
  332. /**
  333. * POST /api/book-generator/books/:id/generate
  334. * 一键生成整本书(异步,后端立即返回任务ID,前端轮询进度)
  335. */
  336. router.post('/books/:id/generate', async (ctx) => {
  337. try {
  338. const bookId = ctx.params.id;
  339. const body = ctx.request.body;
  340. // 立即返回任务ID,不等待生成完成
  341. const taskId = `task_${bookId}_${Date.now()}`;
  342. // 启动异步生成任务(不阻塞,立即返回)
  343. book_generator_service_1.bookGeneratorService.generateBookAsync(bookId, {
  344. generateForeword: body.generateForeword ?? true,
  345. generateAfterword: body.generateAfterword ?? true,
  346. }).catch(err => {
  347. console.error('异步生成失败:', err);
  348. });
  349. ctx.body = {
  350. code: 0,
  351. message: '生成任务已启动',
  352. data: {
  353. taskId,
  354. bookId,
  355. status: 'started',
  356. },
  357. };
  358. }
  359. catch (error) {
  360. console.error('启动生成失败:', error);
  361. ctx.status = 500;
  362. ctx.body = {
  363. code: 1,
  364. message: error instanceof Error ? error.message : '启动失败',
  365. };
  366. }
  367. });
  368. /**
  369. * GET /api/book-generator/workflow/:bookId
  370. * 获取工作流状态
  371. */
  372. router.get('/workflow/:bookId', (ctx) => {
  373. try {
  374. const bookId = ctx.params.bookId;
  375. const progress = book_generator_workflow_1.workflowEngine.getProgress(bookId);
  376. if (!progress) {
  377. ctx.status = 404;
  378. ctx.body = {
  379. code: 1,
  380. message: '工作流不存在',
  381. };
  382. return;
  383. }
  384. ctx.body = {
  385. code: 0,
  386. message: 'success',
  387. data: progress,
  388. };
  389. }
  390. catch (error) {
  391. ctx.status = 500;
  392. ctx.body = {
  393. code: 1,
  394. message: error instanceof Error ? error.message : '获取工作流状态失败',
  395. };
  396. }
  397. });
  398. /**
  399. * POST /api/book-generator/books/:id/chapters/:chapterNumber/audio
  400. * 生成单个章节音频
  401. */
  402. router.post('/books/:id/chapters/:chapterNumber/audio', async (ctx) => {
  403. try {
  404. const bookId = ctx.params.id;
  405. const chapterNumber = parseInt(ctx.params.chapterNumber);
  406. const { voiceId } = ctx.request.body;
  407. const result = await book_generator_service_1.bookGeneratorService.generateChapterAudio(bookId, chapterNumber, voiceId || 'cherry');
  408. ctx.body = {
  409. code: 0,
  410. message: '音频生成任务已启动',
  411. data: result,
  412. };
  413. }
  414. catch (error) {
  415. console.error('生成章节音频失败:', error);
  416. ctx.status = 500;
  417. ctx.body = {
  418. code: 1,
  419. message: error instanceof Error ? error.message : '生成音频失败',
  420. };
  421. }
  422. });
  423. /**
  424. * POST /api/book-generator/books/:id/audio
  425. * 批量生成书籍所有章节音频
  426. */
  427. router.post('/books/:id/audio', async (ctx) => {
  428. try {
  429. const bookId = ctx.params.id;
  430. const { voiceId } = ctx.request.body;
  431. const result = await book_generator_service_1.bookGeneratorService.generateAllChaptersAudio(bookId, voiceId || 'cherry');
  432. ctx.body = {
  433. code: 0,
  434. message: '批量音频生成任务已启动',
  435. data: result,
  436. };
  437. }
  438. catch (error) {
  439. console.error('批量生成音频失败:', error);
  440. ctx.status = 500;
  441. ctx.body = {
  442. code: 1,
  443. message: error instanceof Error ? error.message : '批量生成音频失败',
  444. };
  445. }
  446. });
  447. /**
  448. * POST /api/book-generator/books/:id/chapters/:chapterNumber/video
  449. * 生成单个章节视频(从音频转视频)
  450. */
  451. router.post('/books/:id/chapters/:chapterNumber/video', async (ctx) => {
  452. try {
  453. const bookId = ctx.params.id;
  454. const chapterNumber = parseInt(ctx.params.chapterNumber);
  455. const result = await book_generator_service_1.bookGeneratorService.generateChapterVideo(bookId, chapterNumber);
  456. ctx.body = {
  457. code: 0,
  458. message: '视频生成任务已启动',
  459. data: result,
  460. };
  461. }
  462. catch (error) {
  463. console.error('生成章节视频失败:', error);
  464. ctx.status = 500;
  465. ctx.body = {
  466. code: 1,
  467. message: error instanceof Error ? error.message : '生成视频失败',
  468. };
  469. }
  470. });
  471. /**
  472. * POST /api/book-generator/books/:id/videos
  473. * 批量生成书籍所有章节视频
  474. */
  475. router.post('/books/:id/videos', async (ctx) => {
  476. try {
  477. const bookId = ctx.params.id;
  478. const result = await book_generator_service_1.bookGeneratorService.generateAllChaptersVideo(bookId);
  479. ctx.body = {
  480. code: 0,
  481. message: '批量视频生成任务已启动',
  482. data: result,
  483. };
  484. }
  485. catch (error) {
  486. console.error('批量生成视频失败:', error);
  487. ctx.status = 500;
  488. ctx.body = {
  489. code: 1,
  490. message: error instanceof Error ? error.message : '批量生成视频失败',
  491. };
  492. }
  493. });
  494. exports.default = router;