taskService.cjs 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114
  1. const crypto = require('node:crypto');
  2. const { runBidAnalysisTask } = require('./bidAnalysisTask.cjs');
  3. const { runContentGenerationTask } = require('./contentGenerationTask.cjs');
  4. const { runOutlineGenerationTask } = require('./outlineGenerationTask.cjs');
  5. const taskFields = {
  6. 'bid-analysis': 'bidAnalysisTask',
  7. 'outline-generation': 'outlineGenerationTask',
  8. 'content-generation': 'contentGenerationTask',
  9. };
  10. function now() {
  11. return new Date().toISOString();
  12. }
  13. function createTask(type) {
  14. return {
  15. task_id: crypto.randomUUID(),
  16. type,
  17. status: 'running',
  18. progress: 0,
  19. logs: [],
  20. started_at: now(),
  21. updated_at: now(),
  22. };
  23. }
  24. function createTaskService({ aiService, workspaceStore, knowledgeBaseService }) {
  25. const subscribers = new Set();
  26. const activeTasks = new Map();
  27. function emit(task, technicalPlan) {
  28. const event = { task, technicalPlan };
  29. for (const webContents of subscribers) {
  30. if (!webContents.isDestroyed()) {
  31. webContents.send('tasks:event', event);
  32. }
  33. }
  34. }
  35. function subscribe(webContents) {
  36. subscribers.add(webContents);
  37. const technicalPlan = workspaceStore.loadTechnicalPlan();
  38. for (const task of activeTasks.values()) {
  39. if (!webContents.isDestroyed()) {
  40. webContents.send('tasks:event', { task, technicalPlan });
  41. }
  42. }
  43. webContents.once('destroyed', () => subscribers.delete(webContents));
  44. }
  45. function getTaskField(type) {
  46. return taskFields[type];
  47. }
  48. function startTask(type, payload, runner, initialPartial = {}) {
  49. const existingTask = activeTasks.get(type);
  50. if (existingTask?.status === 'running') {
  51. emit(existingTask, workspaceStore.loadTechnicalPlan());
  52. return existingTask;
  53. }
  54. const task = createTask(type);
  55. activeTasks.set(type, task);
  56. const taskField = getTaskField(type);
  57. let currentTask = task;
  58. const updateTask = (partial, technicalPlan) => {
  59. currentTask = {
  60. ...currentTask,
  61. ...partial,
  62. logs: partial.logs ? partial.logs : currentTask.logs,
  63. updated_at: now(),
  64. };
  65. activeTasks.set(type, currentTask);
  66. if (technicalPlan) emit(currentTask, technicalPlan);
  67. return currentTask;
  68. };
  69. const technicalPlan = workspaceStore.updateTechnicalPlan({ ...initialPartial, [taskField]: currentTask });
  70. emit(currentTask, technicalPlan);
  71. runner({ aiService, workspaceStore, knowledgeBaseService, updateTask, payload }).catch((error) => {
  72. const failedTask = updateTask({ status: 'error', error: error.message || '任务执行失败' });
  73. const nextPlan = workspaceStore.updateTechnicalPlan({ [taskField]: failedTask });
  74. emit(failedTask, nextPlan);
  75. }).finally(() => {
  76. activeTasks.delete(type);
  77. });
  78. return currentTask;
  79. }
  80. return {
  81. subscribe,
  82. startBidAnalysis(payload) {
  83. return startTask('bid-analysis', payload, runBidAnalysisTask);
  84. },
  85. startOutlineGeneration(payload) {
  86. return startTask('outline-generation', payload, runOutlineGenerationTask, {
  87. outlineMode: payload?.mode,
  88. referenceKnowledgeDocumentIds: Array.isArray(payload?.reference_knowledge_document_ids) ? payload.reference_knowledge_document_ids : [],
  89. });
  90. },
  91. startContentGeneration(payload) {
  92. return startTask('content-generation', payload, runContentGenerationTask);
  93. },
  94. getActiveTasks() {
  95. return Array.from(activeTasks.values());
  96. },
  97. };
  98. }
  99. module.exports = { createTaskService };