'use strict'; const cron = require('node-cron'); const database = require('../config/database'); const { syncFollowUpMonitorDailySnapshot, DEFAULT_REQUEST_TIMEOUT_MS, } = require('../services/followUpMonitorSync'); let running = false; const JOB_LOCK_WAIT_SECONDS = Math.max(parseInt(process.env.FOLLOW_UP_MONITOR_LOCK_WAIT_SECONDS || '1', 10), 0); const JOB_ENABLED = String(process.env.FOLLOW_UP_MONITOR_ENABLED || 'true').toLowerCase() !== 'false'; const JOB_CRON = process.env.FOLLOW_UP_MONITOR_CRON || '20 6 * * *'; async function withGlobalJobLock(lockKey, trigger, runner) { const connection = await database.local.pool.getConnection(); let lockAcquired = false; try { const [rows] = await connection.query('SELECT GET_LOCK(?, ?) AS lockStatus', [ lockKey, JOB_LOCK_WAIT_SECONDS ]); const lockStatus = rows && rows[0] ? Number(rows[0].lockStatus) : 0; lockAcquired = lockStatus === 1; if (!lockAcquired) { console.log(`[FollowUpMonitor] global lock not acquired, skip trigger=${trigger}, lockKey=${lockKey}`); return { success: false, skipped: true, reason: 'global_lock_not_acquired', trigger, lockKey }; } return runner(); } finally { try { if (lockAcquired) { await connection.query('DO RELEASE_LOCK(?)', [ lockKey ]); } } finally { connection.release(); } } } async function runFollowUpMonitorJob(trigger = 'manual', options = {}) { if (running) { console.log(`[FollowUpMonitor] job is already running, skip trigger=${trigger}`); return { success: false, skipped: true, reason: 'running', trigger }; } running = true; try { return await withGlobalJobLock('follow_up_monitor_daily', trigger, async () => { const result = await syncFollowUpMonitorDailySnapshot({ targetDate: options.targetDate || null, goodsIds: Array.isArray(options.goodsIds) ? options.goodsIds : null, requestTimeout: options.requestTimeout || DEFAULT_REQUEST_TIMEOUT_MS, }); return { ...result, trigger }; }); } finally { running = false; } } function startFollowUpMonitorSchedulers() { if (!JOB_ENABLED) { console.log('[Scheduler] 跟进监控日任务未启用'); return; } cron.schedule(JOB_CRON, () => { runFollowUpMonitorJob('daily_schedule').catch((error) => { console.error('[FollowUpMonitor] scheduled job failed', error); }); }, { timezone: 'Asia/Shanghai' }); console.log(`[Scheduler] 跟进监控日任务已注册: ${JOB_CRON} (Asia/Shanghai)`); } module.exports = { startFollowUpMonitorSchedulers, runFollowUpMonitorJob, };