You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Node.js CronJob定时任务无限重复发送短信问题如何解决

问题根因

  • Cron表达式配置错误:你写的* 5 * * * *是6位cron规则,对应秒 分 时 日 月 周,意思是每个小时第5分钟的每一秒都执行任务,相当于每到小时的第5分钟就每秒跑一次,自然会重复触发短信。
  • 无发送状态校验:现有逻辑没有记录哪些记录已经发过短信,只要时间差符合条件就触发推送,就算修复了cron表达式,前后两次5分钟间隔的任务也可能命中同一个符合条件的记录。

修复步骤

第一步:修正Cron定时规则

将定时任务的cron表达式改为0 */5 * * * *,实现每5分钟整点执行1次的需求。

第二步:增加发送状态校验

提供两种可选实现方案:

方案A:数据库存储发送状态(推荐,服务重启不丢失数据)

  1. 在clients表新增last_sms_sent DATE类型字段,存储最近一次短信发送日期
  2. 发送短信前增加字段校验,发送成功后更新字段值
    修改后的核心代码示例:
// 修正后的cron表达式
var getTexts = new cronJob('0 */5 * * * *', function() {
    let weekday = ['SUNDAY', 'MONDAY', 'TUESDAY', 'WEDNESDAY', 'THURSDAY', 'FRIDAY', 'SATURDAY'][new Date().getDay()]
    let todayStr = new Date().toISOString().split('T')[0] // 格式化今日日期为YYYY-MM-DD
    // 查询时同时带出最近发送日期
    var viewConformationEmails = `select ${weekday}, last_sms_sent from clients`;
    ibmdb.open(ibmdbconn, function(err, conn) {
        if (err) return console.log(err);
        conn.query(viewConformationEmails, function(err, rows) {
            if (err) {
                console.log(err);
                var today = new Date();
                var time = today.getMinutes() + " " + today.getHours() + " * " + "* " + "*"
                var errorJob = new cronJob(time, function() {
                    client.messages.create({
                        to: "1234567890",
                        from: '12055578708',
                        body: 'Error with your code: ' + err
                    }, function(err, data) {});
                }, null, true);
            }
            for (let i = 0; i < rows.length; i++) {
                let todaysColumn = rows[i][weekday];
                // 今日已经发过的直接跳过
                if(rows[i].LAST_SMS_SENT && new Date(rows[i].LAST_SMS_SENT).toISOString().split('T')[0] === todayStr) {
                    continue
                }
                let userInfoString = todaysColumn.split('|');
                const userPhoneNumber = userInfoString[1]
                const userNameString = userInfoString[2]
                const trainerNameString = userInfoString[3]
                let timeSplit = userInfoString[0].split(' ')
                let minute = timeSplit[0]
                let hour = timeSplit[1] // 同时判断小时避免跨小时的时间差误判
                let today = new Date();
                if (today.getHours() === Number(hour) && Math.abs(today.getMinutes() - Number(minute)) <= 5) {
                    client.messages.create({
                            to: userPhoneNumber,
                            from: '12055578708',
                            body: 'hello ' + userNameString + ', \nYour training session just finished with ' + trainerNameString
                        }, function(err, data) {
                            if (err) {
                                console.log("err: " + err)
                                return
                            }
                            // 发送成功后更新发送日期,将代码中主键相关内容替换为表实际主键字段
                            conn.query(`update clients set last_sms_sent = '${todayStr}' where 主键字段 = ?`, [当前行主键值], function(updateErr) {
                                if(updateErr) console.log(updateErr)
                            })
                            console.log(data)
                        });
                } else {
                    console.log("not working")
                }
            }
            conn.close(function() {});
        });
    });
}, null, true)

方案B:内存缓存存储发送状态(无需改表,适合轻量场景)

安装node-cache依赖后,用缓存记录已发送的标识:

const NodeCache = require('node-cache')
const smsSentCache = new NodeCache({ stdTTL: 86400 }) // 缓存有效期1天

var getTexts = new cronJob('0 */5 * * * *', function() {
    let weekday = ['SUNDAY', 'MONDAY', 'TUESDAY', 'WEDNESDAY', 'THURSDAY', 'FRIDAY', 'SATURDAY'][new Date().getDay()]
    var viewConformationEmails = "select " + weekday + " from clients";
    ibmdb.open(ibmdbconn, function(err, conn) {
        if (err) return console.log(err);
        conn.query(viewConformationEmails, function(err, rows) {
            if (err) {
                console.log(err);
                var today = new Date();
                var time = today.getMinutes() + " " + today.getHours() + " * " + "* " + "*"
                var errorJob = new cronJob(time, function() {
                    client.messages.create({
                        to: "1234567890",
                        from: '12055578708',
                        body: 'Error with your code: ' + err
                    }, function(err, data) {});
                }, null, true);
            }
            for (let i = 0; i < rows.length; i++) {
                let todaysColumn = rows[i][weekday];
                let userInfoString = todaysColumn.split('|');
                const userTextString = userInfoString[0]
                const userPhoneNumber = userInfoString[1]
                const userNameString = userInfoString[2]
                const trainerNameString = userInfoString[3]
                // 生成唯一缓存键:手机号+星期+时间规则
                const cacheKey = `${userPhoneNumber}_${weekday}_${userTextString}`
                // 已经发过的直接跳过
                if(smsSentCache.has(cacheKey)) continue
                let timeSplit = userTextString.split(' ')
                let minute = timeSplit[0]
                let hour = timeSplit[1]
                let today = new Date();
                    if (today.getHours() === Number(hour) && Math.abs(today.getMinutes() - Number(minute)) <= 5) {
                        client.messages.create({
                                to: userPhoneNumber,
                                from: '12055578708',
                                body: 'hello ' + userNameString + ', \nYour training session just finished with ' + trainerNameString
                            }, function(err, data) {
                                if (err) {
                                    console.log("err: " + err)
                                    return
                                }
                                // 发送成功后写入缓存
                                smsSentCache.set(cacheKey, true)
                                console.log(data)
                            });
                    } else {
                        console.log("not working")
                    }
            }
            conn.close(function() {});
        });
    });
}, null, true)

内容的提问来源于stack exchange,提问作者gianluca

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.27 16:57:06