← 返回博客
电商事务邮件Node.js订单

电商订单邮件:从触发到送达的完整实现

电商系统订单确认邮件的完整实现,包括异步触发、幂等性处理、模板要点和常见坑。

更新于 2026年10月12日
电商订单邮件:从触发到送达的完整实现

电商系统的订单邮件:从触发到送达的完整实现

每次用户下单,系统要在几秒内发出确认邮件。延迟或丢失直接影响用户信心。本文以 Node.js + Rovela API 为例,梳理电商订单邮件的完整链路。

触发时机

不要在支付回调里同步发邮件——支付网关的 webhook 可能重复投递,同步发送也会拖慢响应。正确做法是把"发邮件"写入一个任务队列,支付确认后入队,队列消费者异步处理。

// 支付成功后
await queue.add('send-order-email', {
  orderId: order.id,
  userId: order.userId,
  type: 'order_confirmed',
});

用 Rovela 发送

const { Rovela } = require('@rovela/sdk');
const rovela = new Rovela({ apiKey: process.env.ROVELA_API_KEY });

async function sendOrderConfirmed(order, user) {
  await rovela.emails.send({
    from: 'orders@yourdomain.com',
    to: user.email,
    subject: `订单确认 #${order.id}`,
    html: renderOrderTemplate(order),
  });
}

模板要包含什么

订单确认邮件至少要有:订单号、商品列表(名称+数量+单价)、收货地址、预计送达时间、客服联系方式。金额合计和支付方式也要写清楚,防止用户疑惑。

幂等性处理

队列任务重试时可能重复调用发送接口。在数据库里记录已发送的订单 ID,发送前先查重:

const sent = await db.query(
  'SELECT 1 FROM email_log WHERE order_id = $1 AND type = $2',
  [order.id, 'order_confirmed']
);
if (sent.rows.length > 0) return; // 已发过,跳过

常见坑

  • HTML 不要用内联 style 的 display:none:部分邮件客户端会忽略它,导致隐藏内容显示出来。
  • 图片用绝对 URL:邮件客户端无法解析相对路径。
  • 纯文本备选:同时提供 text 字段,防止 HTML 被过滤。
  • 退款、发货、签收 都应发邮件,而不只是下单确认。

Rovela 支持模板功能,把 HTML 结构存为模板,每次传变量即可,维护比在代码里拼 HTML 方便得多。