跳转到正文

Read this guide in English

任务邮件通知 ​

I12 的第一阶段提供可选的任务完成 / 失败邮件、投递记录和有限重试。站内任务状态继续沿用原任务工作台。第二阶段站主运营频道和已核实付款通知见独立教程;真实外部投递与云端维护仍待验,不能将本阶段视为整个 I12 完成。

配置与升级 ​

公开配置位于 packages/core/website.ts:

ts
notifications: {
  taskEmail: false,
  preferenceVersion: "2026-10-11",
  retentionDays: 30,
  operator: { enabled: false, since: null },
}

默认关闭。启用前完整升级迁移至 0022_task_email_language.sql,同步构建 API、工作台与公共页,再确认已有邮件配置与定时入口。0020 新增任务邮件选择和投递表,0022 增加语言字段,没有修改已应用的旧迁移;正常预览主库仍停在 0012,两张通知表尚未应用。关闭时界面不显示卡片,偏好 / 投递列表和定时入口不读取通知表,旧库仍能运行。

使用现有 RESEND_API_KEY、RESEND_EMAIL_FROM、APP_NAME、APP_ORIGIN 与 BETTER_AUTH_SECRET,不需要新的供应商密钥。preferenceVersion 为 1—80 字符,保留期为 1—90 天。改变通知用途时升级版本,让用户重新明确选择。新站继承该配置文件,但选择和投递记录属于各自数据库;仍需检查启用状态、独立数据库、品牌、发信地址和认证密钥。

用户选择与权限 ​

启用后,设置页出现任务邮件卡片。默认不勾选,当前账号必须有已验证邮箱;勾选并保存成功后才允许新通知。取消勾选也需要保存,失败会显示未确认,并提供重新读取按钮。每次修改带当前 revision,旧标签页不能覆盖较新的选择;重复保存相同有效选择不延长起点。

服务端从新鲜数据库状态检查账号、封禁和邮箱,接收人只取当前账号,客户端不能指定邮箱、所有者或外发 URL。变更要求本站 Origin,并有限频;选择与列表属于个人账号,团队切换不会把任务邮件发送给团队其他成员。这里的选择不授权营销订阅或活动统计。

选择只保存最新状态、邮件语言、版本、起点和 revision,不作为完整同意历史。接收地址以认证密钥派生的 HMAC 摘要绑定,不在通知表复制邮箱。邮箱或认证密钥变化后,需要重新保存当前邮箱的选择;旧通知不会改投新邮箱。撤回后尚未领取的旧通知会跳过,再次开启不补发先前完成的任务。已经进入发送阶段的邮件可能仍会到达,撤回无法收回已外发请求。

邮件语言与升级兼容 ​

设置页提供独立的“邮件语言”选择。默认英文,只能开启本站 i18n.locales 已启用的语言;选择后仍需保存。页面语言、活动团队或请求头不会自动改写邮件偏好。已保存西班牙语的账号可以在英文页面查看并保留西班牙语偏好;未提交的选择在页面语言切换或刷新时不保留。

新投递将当前偏好的语言保存到自己的记录,成功 / 失败的主题、HTML / 纯文本、任务与设置链接按该语言生成。任务 ID、所有权、结果字节和文件保留期不变,西班牙语邮件使用 /es/tasks/:id 与 /es/settings。内容摘要包含新的模板版本及记录语言,重试、租约恢复和 Worker 重启仍使用同一载荷与请求键。

显式保存不同语言会增加 revision 并重新设置选择起点,尚未领取的旧邮件会跳过,不为同一终态重复建立通知。已经领取的请求可能仍按原语言发出;更改语言不能收回正在发送的邮件。站点关闭西班牙语后,其已保存偏好仍如实显示,用户可以改成英文或撤回;排队的西班牙语请求停止,不以同一幂等键改发英文。之后重新启用语言不会补发已跳过的记录。

0022 给旧偏好填入英文,新建投递会保存明确语言;旧投递的语言字段保留空值,继续使用原英文模板和 task-mail-v2 内容摘要,不改写已尝试的请求。新投递使用 task-mail-v3。两种渲染路径暂时共存,旧 HTML / 纯文本逐字节兼容测试必须保留,不能仅把空字段补成英文后用新版模板发送。此兼容规则只用于旧任务邮件;营销邮件的语言迁移仍在后续阶段。

已提交结果与去重 ​

复用现有每 15 分钟定时维护入口,从已提交的任务终态派生投递记录。没有增加第二条任务队列、公开发送探针或新的通知执行引擎。任务消费者、租约恢复、死信或管理员恢复产生的真实终态都可以被补偿扫描发现;任务成功不等待邮件。

每轮最多扫描 20 个尚无记录的终态,稳定事件为任务 ID、执行租约版本与成功 / 失败结果。数据库唯一约束处理重放和并发。仅纳入当前明确选择起点之后、保留期内完成的任务;取消、中间重试和需人工核对的任务不发送失败邮件。失效接收人保存跳过记录,避免连续占据扫描预算。扫描有积压或维护延迟时,通知可能晚于 15 分钟。

手动重试会使旧未发失败通知失效;新一轮真实终态形成新事件。发送前重新检查任务结果、租约版本、选择、邮箱和配置。通知只有任务 ID、成功 / 失败说明、本站任务页及设置页链接;没有原始输入、文件名、结果附件、签名下载地址或供应商密钥。结果只在原工作台通过登录与所有权校验下载,邮件不延长文件保留期。

投递状态与重试 ​

状态含义与处理
等待发送已保存通知,尚未领取
发送中一次两分钟租约正在处理,其他消费者不能同时领取
等待重试供应商接收未确认,15 分钟后可再次领取
供应商已接收已得到有效邮件请求回执;不等于送达收件箱或用户阅读
接收未确认,停止重试耗尽三次尝试或超过第一次尝试的 23 小时范围;保留排查状态,不自动换键重发
已跳过选择、账号、任务或发信配置变化;不改投新地址

邮件复用 sendEmail 的 SDK 边界,同时生成 HTML 与纯文本。通知请求超时为十秒,邮件 ID 必须有效。相同投递记录使用固定供应商幂等键,保持内容与接收人一致。接收人、品牌、Origin、发信地址、供应商密钥或环境变化会停止旧请求,避免在新地址或新供应商账号重新发送。

Resend 官方幂等说明在 2026-10-11 核对:键保留 24 小时。本模块最多尝试三次,第一次尝试后仅在 23 小时内重试,包括超时租约恢复;不会跨窗口重新使用旧键。未知返回、供应商错误或回执保存失败都不能证明邮件没有发出,不承诺绝对无重复。接收成功后数据库写入还需匹配本次未过期租约,迟到响应不能覆盖另一消费者的记录。

设置页显示本人最近 20 条、保留期内的投递状态、实际记录语言、次数与下一次可重试时间,可主动刷新。失败只保存固定原因,不记录供应商原始错误、邮箱、正文或秘密。当前不提供普通用户强制重发或管理员重发命令;未确认记录需要结合供应商回执排查后再决定处理,避免随意再发。

保留与本地验收 ​

旧记录按事件时间进入清理资格,每轮最多删除 100 条;积压、关闭模块或维护故障可能延迟物理删除。列表不显示超过保留期的记录,扫描也不会把清理掉的旧任务重新入队。最新选择保留至账号删除,删除账号级联删除选择与投递;删除任务也会删除其投递记录,不影响财务审计自身的保留规则。

本地 Worker 的 LOCAL_MAIL 捕获全部邮件,即使环境里有真实 Resend 密钥也不会外发。捕获回执只证明本地接口完成,不能证明真实送达;开发捕获文件可能包含认证邮件和合成接收地址,不应提交仓库或公开分享。

bash
bun --cwd apps/api validate:notifications

该验收只建立独立 loopback 临时数据库,应用完整迁移,运行实际 Worker、Queue、R2 和定时入口,执行成功 / 失败任务、重放、捕获故障及恢复、撤回与再次开启,并销毁全部临时资源。--languages 额外开启英 / 西双语,核对真实西班牙语邮件、故障后的 Worker 重启与原请求恢复;--ui 额外构建专用工作台供桌面 / 手机验收,不改变正常预览配置或数据库。组合 --languages --ui 时按提示依次保存英文、保存西班牙语、撤回,再创建 .local/notifications-ui-stop 结束验收;只检查页面但不完成这些写入,会明确失败。

真实发信域名、供应商故障、邮箱投递与云端定时维护仍需独立验收。站主渠道及支付事件通知已有独立配置与投递边界,接收者、内容与授权规则不复用个人任务邮件选择,见站主运营通知。

供语言模型读取:llms.txt · llms-full.txt
采用 MIT 许可证。