forked from lunanting/cangku
81 lines
2.6 KiB
JavaScript
81 lines
2.6 KiB
JavaScript
'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,
|
|
};
|