const node_schedule = require("node-schedule"); const logs = require('../tool/logs_proxy'); class Schedule { // 执行标记,防止并发执行 constructor() { this.running_flags = { }; } /** * 执行带锁的任务 * @param {string} task_name - 任务名称 * @param {Function} task_fn - 任务函数 */ async execute_with_lock(task_name, task_fn) { // 如果正在执行,跳过本次执行 if (this.running_flags[task_name]) { logs.log(`[定时任务] ${task_name} 正在执行中,跳过本次执行`); return; } // 设置执行标记 this.running_flags[task_name] = true; try { await task_fn(); } catch (error) { logs.error(`[定时任务] ${task_name} 执行失败:`, error); } finally { // 清除执行标记 this.running_flags[task_name] = false; } } async init() { const bizSubscriptionLogic = require("../api/service/biz_subscription_logic"); const usageSvc = require("../api/service/biz_usage_service"); const baseModel = require("../middleware/baseModel"); node_schedule.scheduleJob("10 0 * * *", async () => { await this.execute_with_lock("biz_subscription_expire", async () => { const n = await bizSubscriptionLogic.expireDueSubscriptions(); logs.log(`[定时任务] 订阅到期扫描完成,更新行数: ${n}`); }); }); node_schedule.scheduleJob("0 9 * * *", async () => { await this.execute_with_lock("biz_subscription_renew_remind", async () => { const subs = await baseModel.biz_subscription.findAll({ where: { status: "active" } }); const now = Date.now(); for (const s of subs) { const end = new Date(s.end_time).getTime(); const days = Math.ceil((end - now) / 86400000); if (days >= 0 && [7, 3, 1].includes(days)) { logs.log( `[续费提醒] subscription_id=${s.id} user_id=${s.user_id} 约 ${days} 天后到期` ); } } }); }); node_schedule.scheduleJob("30 0 1 * *", async () => { await this.execute_with_lock("biz_usage_monthly_init", async () => { const n = await usageSvc.ensureUsageRowsForCurrentMonth(); logs.log(`[定时任务] 月用量行初始化/补齐,处理订阅数: ${n}`); }); }); } } const schedule = new Schedule(); module.exports = schedule;