5e850e96f5
build / build (push) Successful in 2m18s
1) 发送记录读回(确定性回归):Sends() 的 SELECT 只有 6 列,Scan 却收 7 个字段(多出 message_id)。只要历史里存在任意一条发送记录,整个列表就返回 ErrUnavailable —— 发送 成功后页面刷新、以及“完成读真实发送记录”都会失败。改为查询带 message_id,并检查 rows.Err(),不把截断的列表当成完整历史。 2) 未知投递不再当成可重试失败:DATA 已提交但没收到最终 250(连接中断/超时)时,邮件可能 已被服务器接受;旧实现记 failed 并允许下一分钟重发,会重复投递。现在由 SMTPMailer 在 writer.Close() 失败处抛出窄类型 unknownDeliveryError,deliver 保留 sending 并记录原因, 自动/手动路径都不再重发(手动返回 ERRDeliveryUnknown,HTTP 409 说明原因)。连接建立前 失败、STARTTLS/AUTH/MAIL/RCPT/DATA 命令被拒、正文写入失败仍是确定失败,可如实 failed 并 重试一次。 3) SMTP 交互加有限期限:整体 90 秒(随请求 ctx 收紧),并用 context.AfterFunc 在取消时打断 阻塞读写,避免服务器接受 DATA 后不响应时挂死 worker 与发送行。 验证:真实隔离 PG 先 RED 后 GREEN(非空 sent+failed 历史读回成功并带真实 message_id; HTTP GET /weekly-mail/sends 非空历史 200);本机 TLS SMTP 桩 + 真实 SMTPMailer 覆盖成功、 RCPT 550、DATA 后丢最终 250、DATA 阶段卡死(ctx 800ms 内返回)四类;真实 deliver 在未知 结果时留 sending 且不重发,在明确拒绝时 failed 并可重试一次成功。make verify 17 项全绿。