fix-scheduler-autostart.js 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206
  1. #!/usr/bin/env node
  2. /**
  3. * 修复调度器自动启动问题
  4. * 1. 修改PrintTriggerService,在创建延迟任务时自动启动调度器
  5. * 2. 修复时间计算使用数据库时间
  6. */
  7. const fs = require('fs');
  8. const path = require('path');
  9. console.log('=== 修复调度器自动启动问题 ===\n');
  10. // 1. 修复PrintTriggerService - 在handleOrderPaymentSuccess中自动启动调度器
  11. const printTriggerServicePath = path.join(__dirname, 'packages/feie-printer-module-mt/src/services/print-trigger.service.ts');
  12. console.log(`1. 修复 ${printTriggerServicePath}`);
  13. let printTriggerContent = fs.readFileSync(printTriggerServicePath, 'utf8');
  14. // 在handleOrderPaymentSuccess方法中添加调度器自动启动逻辑
  15. const searchPattern = `console.debug(\`[租户\${tenantId}] 订单支付成功打印任务已创建,订单ID: \${orderId}, 延迟时间: \${delaySeconds}秒\`);`;
  16. const replacement = `console.debug(\`[租户\${tenantId}] 订单支付成功打印任务已创建,订单ID: \${orderId}, 延迟时间: \${delaySeconds}秒\`);
  17. // 7. 自动启动调度器(如果未运行)
  18. try {
  19. console.debug(\`[租户\${tenantId}] 检查并启动延迟调度器...\`);
  20. const delaySchedulerService = new DelaySchedulerService(this.dataSource, this.feieConfig, tenantId);
  21. // 检查调度器状态
  22. const status = delaySchedulerService.getStatus();
  23. if (!status.isRunning) {
  24. await delaySchedulerService.start();
  25. console.debug(\`[租户\${tenantId}] 延迟调度器已启动\`);
  26. } else {
  27. console.debug(\`[租户\${tenantId}] 延迟调度器已在运行中\`);
  28. }
  29. // 立即处理一次任务(确保及时执行)
  30. const result = await delaySchedulerService.triggerManualProcess(tenantId);
  31. if (result.success) {
  32. console.debug(\`[租户\${tenantId}] 立即处理任务成功: \${result.message}\`);
  33. }
  34. } catch (error) {
  35. console.warn(\`[租户\${tenantId}] 启动调度器失败:\`, error);
  36. // 不抛出错误,避免影响主流程
  37. }`;
  38. if (printTriggerContent.includes(searchPattern)) {
  39. printTriggerContent = printTriggerContent.replace(searchPattern, replacement);
  40. fs.writeFileSync(printTriggerServicePath, printTriggerContent, 'utf8');
  41. console.log(' ✅ PrintTriggerService修复完成');
  42. } else {
  43. console.log(' ⚠️ 未找到匹配的代码,可能文件已修改');
  44. }
  45. // 2. 修复PrintTaskService - 使用数据库时间计算scheduledAt
  46. const printTaskServicePath = path.join(__dirname, 'packages/feie-printer-module-mt/src/services/print-task.service.ts');
  47. console.log(`\n2. 修复 ${printTaskServicePath}`);
  48. let printTaskContent = fs.readFileSync(printTaskServicePath, 'utf8');
  49. // 查找createPrintTask方法中的时间计算逻辑
  50. // 注意:这里使用字符串搜索,因为正则表达式在替换时有问题
  51. const timeReplacement = `// 计算计划打印时间
  52. let scheduledAt: Date | null = null;
  53. let printStatus = PrintStatus.DELAYED; // 所有通过此方法创建的任务都标记为DELAYED状态
  54. // 如果delaySeconds未定义或小于0,视为0
  55. const delaySeconds = taskDto.delaySeconds || 0;
  56. // 使用数据库时间计算计划时间,避免时间差异问题
  57. try {
  58. // 获取当前数据库时间
  59. const dbTimeResult = await this.dataSource.query('SELECT NOW() as db_time');
  60. const dbNow = new Date(dbTimeResult[0].db_time);
  61. if (delaySeconds >= 0) {
  62. scheduledAt = new Date(dbNow.getTime() + delaySeconds * 1000);
  63. } else {
  64. // 如果delaySeconds为负数,立即执行
  65. scheduledAt = dbNow;
  66. }
  67. } catch (error) {
  68. console.warn('获取数据库时间失败,使用本地时间:', error);
  69. // 回退到本地时间
  70. if (delaySeconds >= 0) {
  71. scheduledAt = new Date(Date.now() + delaySeconds * 1000);
  72. } else {
  73. scheduledAt = new Date();
  74. }
  75. }`;
  76. if (printTaskContent.includes('// 计算计划打印时间')) {
  77. // 使用更精确的替换
  78. const lines = printTaskContent.split('\n');
  79. let inTimeSection = false;
  80. let timeSectionStart = -1;
  81. let timeSectionEnd = -1;
  82. for (let i = 0; i < lines.length; i++) {
  83. if (lines[i].includes('// 计算计划打印时间')) {
  84. inTimeSection = true;
  85. timeSectionStart = i;
  86. }
  87. if (inTimeSection && lines[i].includes('scheduledAt = new Date(Date.now() + delaySeconds * 1000);')) {
  88. timeSectionEnd = i;
  89. break;
  90. }
  91. }
  92. if (timeSectionStart !== -1 && timeSectionEnd !== -1) {
  93. const before = lines.slice(0, timeSectionStart).join('\n');
  94. const after = lines.slice(timeSectionEnd + 1).join('\n');
  95. printTaskContent = before + '\n' + timeReplacement + '\n' + after;
  96. fs.writeFileSync(printTaskServicePath, printTaskContent, 'utf8');
  97. console.log(' ✅ PrintTaskService时间计算修复完成');
  98. } else {
  99. console.log(' ⚠️ 未找到时间计算代码,可能文件结构已变化');
  100. }
  101. } else {
  102. console.log(' ⚠️ 未找到时间计算注释');
  103. }
  104. // 3. 创建应用启动时自动启动调度器的脚本
  105. const autoStartScriptPath = path.join(__dirname, 'packages/feie-printer-module-mt/src/auto-start-scheduler.ts');
  106. console.log(`\n3. 创建自动启动脚本: ${autoStartScriptPath}`);
  107. const autoStartScript = `/**
  108. * 自动启动延迟调度器
  109. * 在应用启动时自动启动所有租户的延迟调度器
  110. */
  111. import { DataSource } from 'typeorm';
  112. import { DelaySchedulerService } from './services/delay-scheduler.service';
  113. import { FeieApiConfig } from './types/feie.types';
  114. export async function autoStartSchedulers(dataSource: DataSource): Promise<void> {
  115. console.log('=== 自动启动延迟调度器 ===');
  116. try {
  117. // 获取飞鹅API配置(简化版,实际应从配置服务获取)
  118. const feieConfig: FeieApiConfig = {
  119. baseUrl: 'https://api.feieyun.cn/Api/Open/',
  120. user: '',
  121. ukey: '',
  122. timeout: 10000,
  123. maxRetries: 3
  124. };
  125. // 获取所有有延迟任务的租户
  126. const tenantResults = await dataSource.query(\`
  127. SELECT DISTINCT tenant_id
  128. FROM feie_print_task_mt
  129. WHERE print_status IN ('DELAYED', 'PENDING')
  130. AND scheduled_at <= NOW() + INTERVAL '1 hour' -- 包括未来1小时内的任务
  131. ORDER BY tenant_id
  132. \`);
  133. const tenantIds = tenantResults.map((row: any) => row.tenant_id);
  134. if (tenantIds.length === 0) {
  135. console.log('没有找到需要调度器处理的租户');
  136. return;
  137. }
  138. console.log(\`找到 \${tenantIds.length} 个需要调度器的租户: \${tenantIds.join(', ')}\`);
  139. // 为每个租户启动调度器
  140. for (const tenantId of tenantIds) {
  141. try {
  142. const scheduler = new DelaySchedulerService(dataSource, feieConfig, tenantId);
  143. // 检查是否已运行
  144. const status = scheduler.getStatus();
  145. if (!status.isRunning) {
  146. await scheduler.start();
  147. console.log(\`租户 \${tenantId} 调度器已启动\`);
  148. } else {
  149. console.log(\`租户 \${tenantId} 调度器已在运行中\`);
  150. }
  151. // 立即处理一次积压任务
  152. const result = await scheduler.triggerManualProcess(tenantId);
  153. if (result.success && result.processedTasks > 0) {
  154. console.log(\`租户 \${tenantId} 处理了 \${result.processedTasks} 个积压任务\`);
  155. }
  156. } catch (error) {
  157. console.error(\`租户 \${tenantId} 调度器启动失败:\`, error);
  158. }
  159. }
  160. console.log('=== 延迟调度器自动启动完成 ===');
  161. } catch (error) {
  162. console.error('自动启动调度器失败:', error);
  163. }
  164. }`;
  165. fs.writeFileSync(autoStartScriptPath, autoStartScript, 'utf8');
  166. console.log(' ✅ 自动启动脚本创建完成');
  167. console.log('\n=== 修复完成 ===');
  168. console.log('\n下一步操作:');
  169. console.log('1. 重新构建飞鹅打印模块: pnpm build:feie');
  170. console.log('2. 重启应用服务');
  171. console.log('3. 检查调度器是否自动启动');
  172. console.log('4. 验证延迟任务是否被处理');