编辑代码

const cron = require('node-cron');
const logger = require('../utils/logger');
const WeChatService = require('./WeChatService');
const WebhookService = require('./WebhookService');
const { getDatabase } = require('../config/database');

class SchedulerService {
    constructor() {
        this.jobs = new Map();
        this.isRunning = false;
    }

    // 初始化调度服务
    init() {
        logger.info('初始化任务调度服务');
        this.loadActiveConfigs();
    }

    // 加载活跃的配置并启动调度
    async loadActiveConfigs() {
        try {
            const configs = await this.getActiveConfigs();
            
            for (const config of configs) {
                await this.startJob(config);
            }
            
            if (configs.length > 0) {
                this.isRunning = true;
                logger.info(`已启动 ${configs.length} 个定时任务`);
            }
            
        } catch (error) {
            logger.error('加载配置失败:', error);
        }
    }

    // 获取活跃的配置
    getActiveConfigs() {
        const db = getDatabase();
        
        return new Promise((resolve, reject) => {
            const sql = 'SELECT * FROM configs WHERE is_active = 1';
            
            db.all(sql, (err, rows) => {
                if (err) {
                    reject(err);
                } else {
                    resolve(rows);
                }
            });
        });
    }

    // 启动单个任务
    async startJob(config) {
        const jobId = `config_${config.id}`;
        
        // 如果任务已存在,先停止
        if (this.jobs.has(jobId)) {
            this.stopJob(jobId);
        }

        // 创建cron表达式(每N分钟执行一次)
        const cronExpression = `*/${config.interval_minutes} * * * *`;
        
        const job = cron.schedule(cronExpression, async () => {
            await this.executeTokenRefresh(config);
        }, {
            scheduled: false // 先不启动,稍后手动启动
        });

        this.jobs.set(jobId, {
            job: job,
            config: config
        });

        job.start();
        
        logger.info(`任务已启动 - AppID: ${config.appid}, 间隔: ${config.interval_minutes}分钟`);
        
        // 立即执行一次
        await this.executeTokenRefresh(config);
    }

    // 停止单个任务
    stopJob(jobId) {
        if (this.jobs.has(jobId)) {
            const jobInfo = this.jobs.get(jobId);
            jobInfo.job.stop();
            this.jobs.delete(jobId);
            
            logger.info(`任务已停止 - JobID: ${jobId}`);
        }
    }

    // 停止所有任务
    stop() {
        for (const [jobId, jobInfo] of this.jobs) {
            jobInfo.job.stop();
        }
        this.jobs.clear();
        this.isRunning = false;
        
        logger.info('所有定时任务已停止');
    }

    // 执行Token刷新
    async executeTokenRefresh(config) {
        try {
            logger.info(`执行定时任务 - AppID: ${config.appid}`);
            
            // 记录操作日志
            await this.logOperation('token_refresh', `开始获取AccessToken - AppID: ${config.appid}`, 'running');
            
            // 获取AccessToken
            const tokenData = await WeChatService.getAccessToken(config.appid, config.appsecret);
            
            // 如果配置了Webhook,进行推送
            if (config.webhook_url) {
                await WebhookService.push(config.webhook_url, {
                    appid: config.appid,
                    access_token: tokenData.access_token,
                    expires_in: tokenData.expires_in,
                    expire_time: tokenData.expire_time
                });
            }
            
            // 记录成功日志
            await this.logOperation('token_refresh', `AccessToken获取成功 - AppID: ${config.appid}`, 'success', {
                token_length: tokenData.access_token.length,
                expires_in: tokenData.expires_in
            });
            
        } catch (error) {
            logger.error(`定时任务执行失败 - AppID: ${config.appid}:`, error);
            
            // 记录失败日志
            await this.logOperation('token_refresh', `AccessToken获取失败 - AppID: ${config.appid}: ${error.message}`, 'error', {
                error: error.message
            });
        }
    }

    // 手动刷新Token
    async manualRefresh(configId) {
        const config = await this.getConfigById(configId);
        if (!config) {
            throw new Error('配置不存在');
        }

        await this.executeTokenRefresh(config);
        return { success: true, message: '手动刷新完成' };
    }

    // 根据ID获取配置
    getConfigById(configId) {
        const db = getDatabase();
        
        return new Promise((resolve, reject) => {
            const sql = 'SELECT * FROM configs WHERE id = ?';
            
            db.get(sql, [configId], (err, row) => {
                if (err) {
                    reject(err);
                } else {
                    resolve(row);
                }
            });
        });
    }

    // 重新加载配置
    async reloadConfig(configId) {
        const config = await this.getConfigById(configId);
        if (!config) {
            throw new Error('配置不存在');
        }

        const jobId = `config_${configId}`;
        
        if (config.is_active) {
            await this.startJob(config);
        } else {
            this.stopJob(jobId);
        }

        return { success: true, message: '配置已重新加载' };
    }

    // 记录操作日志
    async logOperation(type, message, status, details = null) {
        const db = getDatabase();
        
        return new Promise((resolve, reject) => {
            const sql = `
                INSERT INTO operation_logs (type, message, status, details)
                VALUES (?, ?, ?, ?)
            `;
            
            db.run(sql, [
                type,
                message,
                status,
                details ? JSON.stringify(details) : null
            ], function(err) {
                if (err) {
                    logger.error('记录操作日志失败:', err);
                    reject(err);
                } else {
                    resolve(this.lastID);
                }
            });
        });
    }

    // 获取服务状态
    getStatus() {
        return {
            isRunning: this.isRunning,
            activeJobs: this.jobs.size,
            jobs: Array.from(this.jobs.entries()).map(([jobId, jobInfo]) => ({
                jobId,
                appid: jobInfo.config.appid,
                interval: jobInfo.config.interval_minutes,
                webhookUrl: jobInfo.config.webhook_url
            }))
        };
    }
}

module.exports = new SchedulerService();