如何使用Node.js按指定时间规则向多人批量调度发送定时邮件
Node.js 定制化批量邮件调度实现方案
核心实现逻辑
你可以按以下四步完成任务编排:
- 统计总可用发送时长
- 遍历开始日期到结束日期的所有自然日,筛选出属于指定工作日的日期
- 每个符合要求的日期对应的可用发送时长为当日结束发送时间减开始发送时间,示例中9AM到7PM单天可用时长为10小时,可统一换算为毫秒或分钟便于后续计算
- 累加所有符合要求日期的可用时长,得到整个周期内的总可用时长
- 计算单封邮件发送间隔
- 基础间隔 = 总可用时长 ÷ 待发送用户总数
- 可根据邮件服务商发送限制额外设置最小间隔阈值(如1分钟),避免发送频率过高被判定为垃圾邮件
- 若总可用时长小于「用户数×最小间隔」,可抛出异常提示超出最大可发送量
- 生成每个用户的发送时间点
- 初始化时间指针为第一个符合要求工作日的发送开始时间
- 依次为每个用户分配当前指针时间作为发送时间,每分配完成后指针向后累加一个发送间隔
- 若累加后指针超出当日发送结束时间,自动跳转到下一个符合要求工作日的发送开始时间,再继续累加间隔
- 执行调度发送
- 可选用
bullmq等任务队列工具,将所有发送任务按生成的时间点存入队列定时执行 - 也可将所有用户的发送时间预先存入数据库,搭配定时任务每分钟扫描一次待发送任务触发发送
示例参数计算参考
对应你给出的示例参数:
开始日期:2021-10-14 结束日期:2021-11-14 发送时段:9:00-19:00 指定工作日:周一、周二、周四 待发送用户数:500
统计可得周期内符合要求的工作日共14天,总可用时长为14×10=140小时=8400分钟,单封邮件发送间隔约为8400÷500≈17分钟,按该间隔排布即可保证所有用户在周期内各收到1封邮件。
参考代码片段
const { eachDayOfInterval, addMinutes, setHours, setMinutes } = require('date-fns'); // 配置参数 const config = { startDate: new Date(2021, 9, 14), // 月份从0开始计数 endDate: new Date(2021, 10, 14), sendStartTime: { hour: 9, minute: 0 }, sendEndTime: { hour: 19, minute: 0 }, allowedWeekdays: [1, 2, 4], // 0是周日,1周一,2周二,4周四 userCount: 500 }; // 第一步:获取所有符合要求的工作日 const validDays = eachDayOfInterval({ start: config.startDate, end: config.endDate }).filter(day => { return config.allowedWeekdays.includes(day.getDay()); }); // 第二步:计算总可用时长和间隔 const dailyAvailableMinutes = (config.sendEndTime.hour * 60 + config.sendEndTime.minute) - (config.sendStartTime.hour * 60 + config.sendStartTime.minute); const totalAvailableMinutes = validDays.length * dailyAvailableMinutes; const sendInterval = Math.max(Math.ceil(totalAvailableMinutes / config.userCount), 1); // 最小间隔1分钟 // 第三步:生成所有发送时间 const sendTimes = []; let currentDayIndex = 0; let currentTime = setMinutes(setHours(validDays[currentDayIndex], config.sendStartTime.hour), config.sendStartTime.minute); for (let i = 0; i < config.userCount; i++) { sendTimes.push(currentTime); // 计算下一个发送时间 let nextTime = addMinutes(currentTime, sendInterval); const currentDayEnd = setMinutes(setHours(validDays[currentDayIndex], config.sendEndTime.hour), config.sendEndTime.minute); // 如果超出当天结束时间,跳到下一个有效天 if (nextTime > currentDayEnd) { currentDayIndex++; if (currentDayIndex >= validDays.length) throw new Error('可用时长不足,无法安排所有发送任务'); nextTime = setMinutes(setHours(validDays[currentDayIndex], config.sendStartTime.hour), config.sendStartTime.minute); } currentTime = nextTime; } // 后续可基于sendTimes数组进行任务调度
内容的提问来源于stack exchange,提问作者Peeyush Mamodia
相关产品推荐
相关产品推荐

