Skip to content

plugin-email: sys_email 无附件/自定义 header 列,带附件的邮件拿不到队列投递的持久化保证(#5160 落地后) #5172

Description

@os-zhuang

Blocked-by: #5177(headers 半边先行;本单收窄为只剩附件)

维护者已拍板(2026-08-04,PM 会话内):按「先 3 后 1」拆分执行 ——

  1. headers_json 先行(终态方案,不是过渡) = plugin-email/platform-objects: sys_email 加 headers_json + 有界 attachments_json —— 小附件与自定义 header 取得队列投递资格(#5172 一期) #5177;
  2. 附件走 storage 引用,保留期解耦:内容经 storage capability 存储、行里存引用 + 永久元数据(filename / size / hash / contentType,审计用);附件内容的生命周期 = 行到达终态后可回收(留宽限窗)—— 内容是投递工件,元数据才是审计工件,这一刀把 append-only 审计日志与二进制无限增长解耦。
  3. 明确否决:内容 base64 内联进 sys_email(append-only 表无界增长、MySQL 行大小/包大小风险、N 收件人 N 份拷贝)、内联但设大小上限的折中(一个功能两条路径 + 一个阈值,作者最容易在边界上出错)。
  4. 响亮性约束:队列模式 + 附件 + storage capability 缺失时不得静默 —— 维持退回内联并说明原因的现行为。

本单实现时需要回答的剩余细节(实现方判断,PR 里说明):storage key 的命名/前缀方案、宽限窗长度与回收的触发机制(终态时同步删 vs 清扫式)、上传失败时的语义(退回内联,响亮)。

原文如下(现状与选项分析,选项部分已由上述拍板取代):


#5160(邮件队列投递)落地时暴露,PR 里已按"不丢数据"的方向处理,但限制本身需要单独跟踪。

事实

sys_email(packages/platform-objects/src/audit/sys-email.object.ts)有 to_addresses / from_address / cc_addresses / bcc_addresses / reply_to / subject / body_text / body_html,没有附件列,也没有 header 列rowToNormalized() 因此只能重建这些字段。

队列投递的载荷是 { rowId } —— worker 靠行重建消息。于是 send({ attachments }) 若真的入队,投递出去的邮件没有附件:静默丢数据。

#5160 的处置(不是本单要改的)

attachmentsheaders 的消息在队列模式下退回内联投递,并在 info 级说明原因;有用例钉住。选这条是因为另一个选项是"入队并投出剥掉附件的邮件",那属于 AGENTS.md 反复点名的 declared ≠ delivered。

代价说清楚:这类邮件拿不到本次新增的持久化保证 —— 进程死在投递中途,它们和 #5160 之前一样丢。对"发合同 PDF""发导出报表"这种恰恰最不该丢的邮件,这个洞的形状不太好看。

相关

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions