#!/usr/bin/env node /** * 修复调度器自动启动问题 * 1. 修改PrintTriggerService,在创建延迟任务时自动启动调度器 * 2. 修复时间计算使用数据库时间 */ const fs = require('fs'); const path = require('path'); console.log('=== 修复调度器自动启动问题 ===\n'); // 1. 修复PrintTriggerService - 在handleOrderPaymentSuccess中自动启动调度器 const printTriggerServicePath = path.join(__dirname, 'packages/feie-printer-module-mt/src/services/print-trigger.service.ts'); console.log(`1. 修复 ${printTriggerServicePath}`); let printTriggerContent = fs.readFileSync(printTriggerServicePath, 'utf8'); // 在handleOrderPaymentSuccess方法中添加调度器自动启动逻辑 const searchPattern = `console.debug(\`[租户\${tenantId}] 订单支付成功打印任务已创建,订单ID: \${orderId}, 延迟时间: \${delaySeconds}秒\`);`; const replacement = `console.debug(\`[租户\${tenantId}] 订单支付成功打印任务已创建,订单ID: \${orderId}, 延迟时间: \${delaySeconds}秒\`); // 7. 自动启动调度器(如果未运行) try { console.debug(\`[租户\${tenantId}] 检查并启动延迟调度器...\`); const delaySchedulerService = new DelaySchedulerService(this.dataSource, this.feieConfig, tenantId); // 检查调度器状态 const status = delaySchedulerService.getStatus(); if (!status.isRunning) { await delaySchedulerService.start(); console.debug(\`[租户\${tenantId}] 延迟调度器已启动\`); } else { console.debug(\`[租户\${tenantId}] 延迟调度器已在运行中\`); } // 立即处理一次任务(确保及时执行) const result = await delaySchedulerService.triggerManualProcess(tenantId); if (result.success) { console.debug(\`[租户\${tenantId}] 立即处理任务成功: \${result.message}\`); } } catch (error) { console.warn(\`[租户\${tenantId}] 启动调度器失败:\`, error); // 不抛出错误,避免影响主流程 }`; if (printTriggerContent.includes(searchPattern)) { printTriggerContent = printTriggerContent.replace(searchPattern, replacement); fs.writeFileSync(printTriggerServicePath, printTriggerContent, 'utf8'); console.log(' ✅ PrintTriggerService修复完成'); } else { console.log(' ⚠️ 未找到匹配的代码,可能文件已修改'); } // 2. 修复PrintTaskService - 使用数据库时间计算scheduledAt const printTaskServicePath = path.join(__dirname, 'packages/feie-printer-module-mt/src/services/print-task.service.ts'); console.log(`\n2. 修复 ${printTaskServicePath}`); let printTaskContent = fs.readFileSync(printTaskServicePath, 'utf8'); // 查找createPrintTask方法中的时间计算逻辑 // 注意:这里使用字符串搜索,因为正则表达式在替换时有问题 const timeReplacement = `// 计算计划打印时间 let scheduledAt: Date | null = null; let printStatus = PrintStatus.DELAYED; // 所有通过此方法创建的任务都标记为DELAYED状态 // 如果delaySeconds未定义或小于0,视为0 const delaySeconds = taskDto.delaySeconds || 0; // 使用数据库时间计算计划时间,避免时间差异问题 try { // 获取当前数据库时间 const dbTimeResult = await this.dataSource.query('SELECT NOW() as db_time'); const dbNow = new Date(dbTimeResult[0].db_time); if (delaySeconds >= 0) { scheduledAt = new Date(dbNow.getTime() + delaySeconds * 1000); } else { // 如果delaySeconds为负数,立即执行 scheduledAt = dbNow; } } catch (error) { console.warn('获取数据库时间失败,使用本地时间:', error); // 回退到本地时间 if (delaySeconds >= 0) { scheduledAt = new Date(Date.now() + delaySeconds * 1000); } else { scheduledAt = new Date(); } }`; if (printTaskContent.includes('// 计算计划打印时间')) { // 使用更精确的替换 const lines = printTaskContent.split('\n'); let inTimeSection = false; let timeSectionStart = -1; let timeSectionEnd = -1; for (let i = 0; i < lines.length; i++) { if (lines[i].includes('// 计算计划打印时间')) { inTimeSection = true; timeSectionStart = i; } if (inTimeSection && lines[i].includes('scheduledAt = new Date(Date.now() + delaySeconds * 1000);')) { timeSectionEnd = i; break; } } if (timeSectionStart !== -1 && timeSectionEnd !== -1) { const before = lines.slice(0, timeSectionStart).join('\n'); const after = lines.slice(timeSectionEnd + 1).join('\n'); printTaskContent = before + '\n' + timeReplacement + '\n' + after; fs.writeFileSync(printTaskServicePath, printTaskContent, 'utf8'); console.log(' ✅ PrintTaskService时间计算修复完成'); } else { console.log(' ⚠️ 未找到时间计算代码,可能文件结构已变化'); } } else { console.log(' ⚠️ 未找到时间计算注释'); } // 3. 创建应用启动时自动启动调度器的脚本 const autoStartScriptPath = path.join(__dirname, 'packages/feie-printer-module-mt/src/auto-start-scheduler.ts'); console.log(`\n3. 创建自动启动脚本: ${autoStartScriptPath}`); const autoStartScript = `/** * 自动启动延迟调度器 * 在应用启动时自动启动所有租户的延迟调度器 */ import { DataSource } from 'typeorm'; import { DelaySchedulerService } from './services/delay-scheduler.service'; import { FeieApiConfig } from './types/feie.types'; export async function autoStartSchedulers(dataSource: DataSource): Promise { console.log('=== 自动启动延迟调度器 ==='); try { // 获取飞鹅API配置(简化版,实际应从配置服务获取) const feieConfig: FeieApiConfig = { baseUrl: 'https://api.feieyun.cn/Api/Open/', user: '', ukey: '', timeout: 10000, maxRetries: 3 }; // 获取所有有延迟任务的租户 const tenantResults = await dataSource.query(\` SELECT DISTINCT tenant_id FROM feie_print_task_mt WHERE print_status IN ('DELAYED', 'PENDING') AND scheduled_at <= NOW() + INTERVAL '1 hour' -- 包括未来1小时内的任务 ORDER BY tenant_id \`); const tenantIds = tenantResults.map((row: any) => row.tenant_id); if (tenantIds.length === 0) { console.log('没有找到需要调度器处理的租户'); return; } console.log(\`找到 \${tenantIds.length} 个需要调度器的租户: \${tenantIds.join(', ')}\`); // 为每个租户启动调度器 for (const tenantId of tenantIds) { try { const scheduler = new DelaySchedulerService(dataSource, feieConfig, tenantId); // 检查是否已运行 const status = scheduler.getStatus(); if (!status.isRunning) { await scheduler.start(); console.log(\`租户 \${tenantId} 调度器已启动\`); } else { console.log(\`租户 \${tenantId} 调度器已在运行中\`); } // 立即处理一次积压任务 const result = await scheduler.triggerManualProcess(tenantId); if (result.success && result.processedTasks > 0) { console.log(\`租户 \${tenantId} 处理了 \${result.processedTasks} 个积压任务\`); } } catch (error) { console.error(\`租户 \${tenantId} 调度器启动失败:\`, error); } } console.log('=== 延迟调度器自动启动完成 ==='); } catch (error) { console.error('自动启动调度器失败:', error); } }`; fs.writeFileSync(autoStartScriptPath, autoStartScript, 'utf8'); console.log(' ✅ 自动启动脚本创建完成'); console.log('\n=== 修复完成 ==='); console.log('\n下一步操作:'); console.log('1. 重新构建飞鹅打印模块: pnpm build:feie'); console.log('2. 重启应用服务'); console.log('3. 检查调度器是否自动启动'); console.log('4. 验证延迟任务是否被处理');