You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何在Node.js中实现固定日期前24小时自动发送邮件?

嘿,这个需求在Node.js里实现起来其实挺清晰的,尤其是你已经有持续运行的VPS环境,我来给你一步步拆解靠谱的实现方案,兼顾稳定性和灵活性~

核心思路

首先得明确我们要做的几件事:

  1. 从数据库读取目标日期,计算出提前24小时的触发时间
  2. 用可靠的定时任务工具调度邮件发送逻辑
  3. 保证VPS重启后任务不丢失,进程崩溃能自动恢复
工具选择
  • 邮件发送:用nodemailer,这是Node.js生态里最成熟的邮件库,支持各种SMTP服务(Gmail、SendGrid、阿里云邮箱都可以)
  • 定时任务:如果你的数据库是MongoDB,首推agenda——它会把任务持久化到MongoDB里,服务器重启后能自动恢复任务;如果用其他数据库,可以用node-schedule,但需要自己做任务持久化
  • 进程管理:用pm2,保证你的Node.js进程在VPS上持续运行,崩溃自动重启
具体实现步骤

1. 配置邮件发送服务

先搞定邮件发送的基础功能,这里以SMTP为例:

const nodemailer = require('nodemailer');

// 初始化邮件传输器(替换成你的邮箱服务商配置)
const transporter = nodemailer.createTransport({
  host: 'smtp.your-provider.com',
  port: 587,
  secure: false, // true对应465端口,false对应其他
  auth: {
    user: 'your-email@example.com',
    pass: 'your-app-password-or-email-password' // 注意:Gmail需要用应用专用密码
  }
});

// 批量发送提醒邮件的函数
async function sendReminderEmails(recipients) {
  const baseMailOptions = {
    from: '"Your App" <your-email@example.com>',
    subject: 'Event Reminder',
    text: 'Hi! Just a heads up—your event is tomorrow.',
    html: '<p>Hi! Just a heads up—your event is <strong>tomorrow</strong>.</p>'
  };

  // 逐个发送(避免批量收件人互相看到邮箱)
  for (const recipient of recipients) {
    try {
      await transporter.sendMail({
        ...baseMailOptions,
        to: recipient.email,
        // 可以根据收件人定制内容,比如加上姓名
        html: `<p>Hi ${recipient.name}! Just a heads up—your event is <strong>tomorrow</strong>.</p>`
      });
      console.log(`✅ 邮件已发送到 ${recipient.email}`);
    } catch (err) {
      console.error(`❌ 发送邮件到 ${recipient.email} 失败:`, err);
      // 可选:添加重试逻辑,比如失败后5分钟再试一次
    }
  }
}

module.exports = sendReminderEmails;

2. 用Agenda实现持久化定时任务(推荐)

Agenda适合需要持久化任务的场景,重启服务器不会丢失任务:

安装依赖

npm install agenda mongoose

代码实现

const Agenda = require('agenda');
const mongoose = require('mongoose');
const sendReminderEmails = require('./email-service');

// 连接MongoDB(Agenda依赖MongoDB存储任务)
mongoose.connect('mongodb://localhost:27017/your-db-name')
  .then(() => console.log('✅ 连接MongoDB成功'))
  .catch(err => console.error('❌ MongoDB连接失败:', err));

// 初始化Agenda
const agenda = new Agenda({ db: { address: 'mongodb://localhost:27017/your-db-name' } });

// 定义"发送提醒邮件"的任务类型
agenda.define('send event reminders', async (job) => {
  const { recipients } = job.attrs.data;
  await sendReminderEmails(recipients);
});

// 启动Agenda并创建定时任务
(async function initScheduler() {
  await agenda.start();

  // 从数据库读取目标事件(假设你有Event模型)
  const Event = require('./models/Event');
  const targetEvent = await Event.findOne({ /* 你的查询条件,比如找未处理的事件 */ });

  if (!targetEvent) {
    console.log('⚠️ 未找到需要设置提醒的事件');
    return;
  }

  // 计算提前24小时的触发时间
  const triggerTime = new Date(targetEvent.targetDate);
  triggerTime.setHours(triggerTime.getHours() - 24);

  // 检查触发时间是否在未来,避免创建已过期的任务
  if (triggerTime <= new Date()) {
    console.log('⚠️ 触发时间已过,跳过任务创建');
    return;
  }

  // 创建定时任务
  await agenda.schedule(triggerTime, 'send event reminders', {
    recipients: targetEvent.recipients // 从DB取收件人列表
  });

  console.log(`✅ 已安排提醒任务,将在 ${triggerTime} 执行`);
})();

3. 用Node-Schedule实现轻量定时任务

如果不用MongoDB,用node-schedule也可以,但要自己处理任务持久化:

安装依赖

npm install node-schedule

代码实现

const schedule = require('node-schedule');
const sendReminderEmails = require('./email-service');
const Event = require('./models/Event');

async function setupReminderJob() {
  const targetEvent = await Event.findOne({ /* 查询条件 */ });

  if (!targetEvent) {
    console.log('⚠️ 未找到目标事件');
    return;
  }

  const triggerTime = new Date(targetEvent.targetDate);
  triggerTime.setHours(triggerTime.getHours() - 24);

  if (triggerTime <= new Date()) {
    console.log('⚠️ 触发时间已过期');
    return;
  }

  // 创建定时任务
  const job = schedule.scheduleJob(triggerTime, async () => {
    await sendReminderEmails(targetEvent.recipients);
    console.log('✅ 提醒邮件发送完成');
  });

  console.log(`✅ 已创建定时任务,ID: ${job.name},执行时间: ${triggerTime}`);

  // 可选:把任务信息存到数据库,服务器重启后重新加载
  // await JobLog.create({ jobName: job.name, triggerTime, recipients: targetEvent.recipients });
}

// 启动时执行一次,也可以定期执行(比如每天检查新事件)
setupReminderJob();
VPS上的运行保障

用PM2管理你的Node.js进程,确保它持续运行:

  1. 安装PM2:
npm install -g pm2
  1. 启动应用:
pm2 start app.js --name email-reminder-scheduler
  1. 保存PM2配置,重启VPS后自动恢复进程:
pm2 save
pm2 startup # 按照终端提示执行生成的命令
  1. 查看日志和进程状态:
pm2 logs email-reminder-scheduler # 查看实时日志
pm2 status # 查看进程状态
关键注意事项
  • 时区问题:确保数据库里的日期和Node.js应用的时区一致,建议统一用UTC时间处理,避免计算错误。比如:
    // 如果DB里的日期是UTC,直接转换
    const triggerTime = new Date(Date.parse(targetEvent.targetDate) - 24 * 60 * 60 * 1000);
    
  • 任务重试:如果邮件发送失败,Agenda可以通过job.retry()设置重试次数,或者在邮件函数里手动添加重试逻辑。
  • 定期检查新事件:如果你的数据库会新增事件,建议每天定时执行一次查询,自动为新事件创建提醒任务(比如用Agenda的every('0 0 * * *', 'check new events'))。

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

火山引擎 最新活动