
离双11还有一段时间预售邮件可以先设计不能提前写成活动已经结束的复盘。一封“最后机会”发错时间比少发一封邮件更容易损害信任。我会先写发送状态再写标题用户是否订阅是否已经购买是否退订当前阶段是否到达。文案再漂亮也不能绕过这些条件。从日期列表改成资格判断按日历排“预热、提醒、结束”只是第一层。买过的人不应该继续收到催购买退订的人不能因为导入了旧名单又进入流程。延迟任务执行时也要重新读取资格不能只相信入队时的快照。Stripe webhook文档说明事件交付不保证生成顺序重复交付也可能发生。因此订阅或支付事件不能简单按到达先后覆盖状态。具体支付对象需要按业务规则读取权威状态下面只演示已经确认状态之后的邮件资格判断。资格闸门放在发送之前而不是只放在排期界面。图里被抑制的分支应留记录方便解释某封邮件为什么没有发出。先用本地样本验证闸门const assert require(node:assert/strict); function eligible(user, campaign, now) { if (!Number.isFinite(now) || !Number.isFinite(campaign.start) || !Number.isFinite(campaign.end) || campaign.end campaign.start) throw new TypeError(invalid campaign window); return user.subscribed true user.unsubscribed ! true user.purchased ! true now campaign.start now campaign.end; } const campaign { start: 100, end: 200 }; const user { subscribed: true, unsubscribed: false, purchased: false }; assert.equal(eligible(user, campaign, 100), true); assert.equal(eligible(user, campaign, 200), false); assert.equal(eligible({ ...user, unsubscribed: true }, campaign, 150), false); assert.equal(eligible({ ...user, purchased: true }, campaign, 150), false); console.log(mail-gate: 4 assertions passed);保存为mail-gate.cjs运行node mail-gate.cjs。时间100和200只是模拟刻度真实活动使用带时区的绝对时间。代码不调用邮件平台不证明送达率或退订同步可靠。失败重试不能变成重复轰炸每个用户、活动与阶段应有稳定发送标识。发送成功后保存供应商消息ID超时则先查询已知状态或进入人工核查避免仅因为客户端没收到响应就再发一遍。频率上限也要跨活动生效。一个人可能同时处于欢迎、产品更新、促销三个序列单条序列看起来克制合起来却每天数封。前端应提供收件人预览和抑制原因不只显示“预计发送1000人”。倒计时只用于真实的截止条件。延期时同步活动页、邮件落地页和结算逻辑避免邮件显示已结束、网站仍按原价收款。这个设计没有实测营销效果更可靠的起点是先保证每封邮件对当前收件人都有发送理由。