基于Node.js栈,如何用Nodemailer实现年度日期提前一周自动发提醒邮件?
嘿,作为编程新手能想到做这个功能超棒的!我来一步步帮你拆解,每个环节都尽量讲得直白,跟着做就能搞定:
首先打开终端,在你的项目文件夹里安装需要的包:
npm install express mongoose nodemailer node-schedule dotenv
给你简单说下每个包的作用:
express:用来搭建Web服务器,处理表单提交请求mongoose:帮你连接和操作MongoDB数据库nodemailer:专门用来发送邮件的工具node-schedule:实现定时任务,比如每天自动检查要发的提醒dotenv:用来存邮箱密码、数据库链接这类敏感信息,避免硬编码在代码里
推荐的项目结构大概是这样(不用完全照搬,能理清逻辑就行):
/models:放MongoDB的数据模型/routes:处理表单提交的路由/controllers:处理具体的业务逻辑/utils:放邮件发送、定时任务这类工具函数.env:存配置信息server.js:项目入口文件
我们需要把用户输入的邮箱和重要日期存到MongoDB里,所以先写一个数据模型。在/models/UserReminder.js里写:
const mongoose = require('mongoose'); const userReminderSchema = new mongoose.Schema({ email: { type: String, required: true, unique: true, // 避免同一个邮箱重复提交 trim: true, lowercase: true // 统一转小写,防止大小写不同导致重复 }, importantDate: { type: Date, required: true } }); module.exports = mongoose.model('UserReminder', userReminderSchema);
首先在server.js里搭建基础的Express服务器,连接MongoDB:
require('dotenv').config(); const express = require('express'); const mongoose = require('mongoose'); const reminderRoutes = require('./routes/reminders'); const path = require('path'); const app = express(); // 中间件:解析表单数据和JSON app.use(express.urlencoded({ extended: true })); app.use(express.json()); // 托管静态页面(比如我们的表单页面) app.use(express.static(path.join(__dirname, 'public'))); // 连接MongoDB,记得在.env里填你的数据库链接 mongoose.connect(process.env.MONGO_URI) .then(() => console.log('成功连接MongoDB!')) .catch(err => console.error('MongoDB连接失败:', err)); // 挂载路由 app.use('/api/reminders', reminderRoutes); // 启动服务器 const PORT = process.env.PORT || 3000; app.listen(PORT, () => console.log(`服务器跑在端口${PORT}啦!`));
然后写路由文件/routes/reminders.js:
const express = require('express'); const router = express.Router(); const { createReminder } = require('../controllers/reminderController'); // 处理表单提交的POST请求 router.post('/', createReminder); module.exports = router;
接着写控制器/controllers/reminderController.js,处理表单提交的逻辑:
const UserReminder = require('../models/UserReminder'); exports.createReminder = async (req, res) => { try { const { email, importantDate } = req.body; // 先检查用户有没有填全 if (!email || !importantDate) { return res.status(400).send('邮箱和日期都不能为空哦!'); } // 检查这个邮箱是不是已经设置过提醒了 const existingReminder = await UserReminder.findOne({ email }); if (existingReminder) { return res.status(400).send('这个邮箱已经设置过提醒啦!'); } // 把数据存到数据库 const reminder = new UserReminder({ email, importantDate }); await reminder.save(); res.status(201).send('提醒设置成功!'); } catch (err) { console.error('创建提醒出错:', err); res.status(500).send('服务器出小问题啦,稍后再试吧!'); } };
最后写一个简单的表单页面/public/index.html,让用户能输入信息:
<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <title>设置重要日期提醒</title> </head> <body> <h1>设置你的重要日期提醒</h1> <form action="/api/reminders" method="POST"> <div> <label for="email">你的邮箱:</label> <input type="email" id="email" name="email" required> </div> <div style="margin: 10px 0;"> <label for="importantDate">重要日期:</label> <input type="date" id="importantDate" name="importantDate" required> </div> <button type="submit">确认设置提醒</button> </form> </body> </html>
首先在.env文件里填你的邮箱配置(以Gmail为例,其他邮箱类似):
MONGO_URI=mongodb://localhost:27017/reminderApp PORT=3000 EMAIL_USER=你的gmail邮箱地址 EMAIL_PASS=你的gmail应用密码(注意:要开启两步验证才能生成应用密码) EMAIL_SERVICE=gmail
敲黑板!如果用Gmail,一定要先开启两步验证,然后去Google账号里生成“应用密码”,不能直接用你的登录密码。如果是QQ邮箱,要开启POP3/SMTP服务,获取授权码当密码用。
然后写邮件工具函数/utils/email.js:
const nodemailer = require('nodemailer'); require('dotenv').config(); // 创建邮件传输器 const transporter = nodemailer.createTransport({ service: process.env.EMAIL_SERVICE, auth: { user: process.env.EMAIL_USER, pass: process.env.EMAIL_PASS } }); // 发送提醒邮件的函数 exports.sendReminderEmail = async (email, importantDate) => { // 把日期格式化成用户易读的样子 const formattedDate = importantDate.toLocaleDateString('zh-CN', { year: 'numeric', month: 'long', day: 'numeric' }); const mailOptions = { from: `日期提醒助手 <${process.env.EMAIL_USER}>`, to: email, subject: '📅 重要日期即将到来!', text: `您好!您设置的重要日期${formattedDate}还有一周就要到啦,请记得做好准备哦!`, html: `<p>您好!</p><p>您设置的重要日期<strong>${formattedDate}</strong>还有一周就要到啦,请记得做好准备哦!</p>` }; try { await transporter.sendMail(mailOptions); console.log(`提醒邮件已成功发送到:${email}`); } catch (err) { console.error(`发送邮件失败到${email}:`, err); } };
用node-schedule来设置每天自动检查数据库,看看哪些用户的重要日期在一周后,然后发送邮件。写/utils/scheduler.js:
const schedule = require('node-schedule'); const UserReminder = require('../models/UserReminder'); const { sendReminderEmail } = require('./email'); // 设置每天凌晨1点执行检查任务(cron表达式:分 时 日 月 周) const reminderJob = schedule.scheduleJob('0 1 * * *', async () => { console.log('开始检查需要发送提醒的用户...'); try { const today = new Date(); // 计算一周后的日期 const oneWeekLater = new Date(today); oneWeekLater.setDate(today.getDate() + 7); // 取出所有用户的提醒记录 const allReminders = await UserReminder.find(); for (const reminder of allReminders) { const userImportantDate = reminder.importantDate; // 生成今年的重要日期(因为要每年提醒) let targetDate = new Date(today.getFullYear(), userImportantDate.getMonth(), userImportantDate.getDate()); // 如果今年的日期已经过了,就用明年的 if (targetDate < today) { targetDate.setFullYear(today.getFullYear() + 1); } // 检查目标日期和一周后的日期是不是同一天(允许一天的误差,避免时区问题) const isMatch = targetDate.toDateString() === oneWeekLater.toDateString(); if (isMatch) { await sendReminderEmail(reminder.email, targetDate); } } console.log('检查完成!'); } catch (err) { console.error('定时任务执行出错:', err); } }); module.exports = reminderJob;
然后在server.js里引入这个定时任务,加一行:
require('./utils/scheduler');
这样服务器启动后,定时任务就会自动运行啦!如果想测试,可以把cron表达式改成*/10 * * * * *,这样每10秒执行一次,测试完再改回凌晨1点的。
- 本地测试的时候,要确保MongoDB服务已经启动哦!
- 时区问题要注意:MongoDB存的是UTC时间,用户输入的是本地时间,所以代码里用
toDateString()来比较,避免时区差异导致提醒时间不对。 - 后续可以扩展功能:比如让用户修改、删除提醒,或者查看自己的提醒记录。
- 如果部署到服务器上,要确保服务器一直运行,比如用
pm2来管理Node.js进程,避免服务器重启后任务停止。
内容的提问来源于stack exchange,提问作者klaurtar

