React Email与PostgreSQL:关系型邮件事务处理方案

【免费下载链接】react-email 💌 Build and send emails using React 【免费下载链接】react-email 项目地址: https://gitcode.com/GitHub_Trending/re/react-email

痛点:现代邮件系统的数据一致性挑战

在当今的数字化时代,邮件系统已成为企业通信的核心基础设施。然而,传统的邮件发送方案往往面临以下痛点:

  • 数据不一致:邮件发送状态与业务数据分离,导致状态同步困难
  • 事务处理缺失:邮件发送失败时,业务操作无法回滚
  • 性能瓶颈:高并发场景下,邮件队列处理效率低下
  • 监控困难:缺乏完整的邮件发送生命周期追踪

解决方案架构设计

系统架构图

mermaid

数据库表结构设计

-- 邮件模板表
CREATE TABLE email_templates (
    id SERIAL PRIMARY KEY,
    name VARCHAR(255) NOT NULL,
    subject VARCHAR(500) NOT NULL,
    react_component TEXT NOT NULL,
    variables JSONB DEFAULT '{}',
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

-- 邮件发送队列表
CREATE TABLE email_queue (
    id SERIAL PRIMARY KEY,
    template_id INTEGER REFERENCES email_templates(id),
    recipient_email VARCHAR(255) NOT NULL,
    recipient_data JSONB DEFAULT '{}',
    status VARCHAR(50) DEFAULT 'pending',
    send_attempts INTEGER DEFAULT 0,
    last_attempt_at TIMESTAMP,
    scheduled_for TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    sent_at TIMESTAMP,
    error_message TEXT
);

-- 邮件发送日志表
CREATE TABLE email_logs (
    id SERIAL PRIMARY KEY,
    queue_id INTEGER REFERENCES email_queue(id),
    provider VARCHAR(100),
    message_id VARCHAR(255),
    status VARCHAR(50),
    response JSONB,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

核心实现方案

1. React Email组件集成

import { render } from '@react-email/render';
import { WelcomeEmail } from '../emails/welcome-email';
import { db } from '../lib/database';

export async function sendWelcomeEmail(userId: number, userEmail: string) {
  const client = await db.connect();
  
  try {
    await client.query('BEGIN');
    
    // 1. 获取用户数据
    const userResult = await client.query(
      'SELECT name, activation_token FROM users WHERE id = $1',
      [userId]
    );
    
    if (userResult.rows.length === 0) {
      throw new Error('用户不存在');
    }
    
    const user = userResult.rows[0];
    
    // 2. 渲染React Email组件
    const emailComponent = WelcomeEmail({
      name: user.name,
      activationLink: `https://example.com/activate/${user.activation_token}`
    });
    
    const htmlContent = await render(emailComponent);
    const plainTextContent = await render(emailComponent, { plainText: true });
    
    // 3. 插入邮件队列记录
    const queueResult = await client.query(
      `INSERT INTO email_queue 
       (template_id, recipient_email, recipient_data, status)
       VALUES ($1, $2, $3, 'pending')
       RETURNING id`,
      [1, userEmail, JSON.stringify({ userId, userName: user.name })]
    );
    
    const queueId = queueResult.rows[0].id;
    
    // 4. 发送邮件(事务内操作)
    const emailService = getEmailService();
    const result = await emailService.send({
      to: userEmail,
      subject: '欢迎加入我们!',
      html: htmlContent,
      text: plainTextContent
    });
    
    // 5. 更新邮件状态
    await client.query(
      `UPDATE email_queue 
       SET status = 'sent', sent_at = NOW(), message_id = $1
       WHERE id = $2`,
      [result.messageId, queueId]
    );
    
    // 6. 记录发送日志
    await client.query(
      `INSERT INTO email_logs 
       (queue_id, provider, message_id, status, response)
       VALUES ($1, $2, $3, 'delivered', $4)`,
      [queueId, 'resend', result.messageId, JSON.stringify(result)]
    );
    
    await client.query('COMMIT');
    
    return { success: true, messageId: result.messageId };
    
  } catch (error) {
    await client.query('ROLLBACK');
    
    // 记录失败状态
    if (queueId) {
      await client.query(
        `UPDATE email_queue 
         SET status = 'failed', error_message = $1
         WHERE id = $2`,
        [error.message, queueId]
      );
    }
    
    throw error;
  } finally {
    client.release();
  }
}

2. 邮件模板管理系统

// email-templates/welcome-email.tsx
import {
  Body,
  Container,
  Head,
  Heading,
  Html,
  Preview,
  Text,
  Button,
  Section,
} from '@react-email/components';

interface WelcomeEmailProps {
  name: string;
  activationLink: string;
}

export const WelcomeEmail = ({ name, activationLink }: WelcomeEmailProps) => (
  <Html>
    <Head />
    <Preview>欢迎加入我们的平台!</Preview>
    <Body style={main}>
      <Container style={container}>
        <Heading style={h1}>欢迎,{name}!</Heading>
        <Text style={text}>
          感谢您注册我们的服务。请点击下面的按钮激活您的账户。
        </Text>
        <Section style={btnContainer}>
          <Button style={button} href={activationLink}>
            激活账户
          </Button>
        </Section>
        <Text style={text}>
          如果您没有请求此邮件,请忽略它。
        </Text>
      </Container>
    </Body>
  </Html>
);

const main = {
  backgroundColor: '#ffffff',
  fontFamily: '-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Oxygen-Sans,Ubuntu,Cantarell,"Helvetica Neue",sans-serif',
};

const container = {
  margin: '0 auto',
  padding: '20px 0 48px',
  width: '580px',
};

const h1 = {
  color: '#333',
  fontSize: '24px',
  fontWeight: 'bold',
  margin: '30px 0',
  padding: '0',
};

const text = {
  color: '#333',
  fontSize: '16px',
  lineHeight: '24px',
  margin: '16px 0',
};

const btnContainer = {
  textAlign: 'center' as const,
  margin: '32px 0',
};

const button = {
  backgroundColor: '#0066ff',
  borderRadius: '6px',
  color: '#fff',
  fontSize: '16px',
  fontWeight: 'bold',
  textDecoration: 'none',
  textAlign: 'center' as const,
  padding: '12px 24px',
};

3. 事务性邮件处理工作流

mermaid

高级特性实现

1. 批量邮件发送优化

export async function sendBulkEmails(
  templateId: number,
  recipients: Array<{ email: string; data: any }>,
  batchSize: number = 100
) {
  const totalBatches = Math.ceil(recipients.length / batchSize);
  
  for (let batchIndex = 0; batchIndex < totalBatches; batchIndex++) {
    const batch = recipients.slice(
      batchIndex * batchSize,
      (batchIndex + 1) * batchSize
    );
    
    await processEmailBatch(templateId, batch);
  }
}

async function processEmailBatch(templateId: number, batch: Array<{ email: string; data: any }>) {
  const client = await db.connect();
  
  try {
    await client.query('BEGIN');
    
    const template = await getEmailTemplate(templateId);
    const emailService = getEmailService();
    
    for (const recipient of batch) {
      const renderedEmail = await renderEmailTemplate(template, recipient.data);
      
      const queueResult = await client.query(
        `INSERT INTO email_queue (...) VALUES (...) RETURNING id`,
        [templateId, recipient.email, JSON.stringify(recipient.data)]
      );
      
      const result = await emailService.send({
        to: recipient.email,
        subject: template.subject,
        html: renderedEmail.html,
        text: renderedEmail.text
      });
      
      await updateEmailStatus(client, queueResult.rows[0].id, result);
    }
    
    await client.query('COMMIT');
  } catch (error) {
    await client.query('ROLLBACK');
    throw error;
  } finally {
    client.release();
  }
}

2. 邮件状态监控与重试机制

-- 重试失败邮件的函数
CREATE OR REPLACE FUNCTION retry_failed_emails(max_attempts INT DEFAULT 3)
RETURNS INT AS $$
DECLARE
    retry_count INT := 0;
    queue_record RECORD;
BEGIN
    FOR queue_record IN 
        SELECT * FROM email_queue 
        WHERE status = 'failed' 
        AND send_attempts < max_attempts
        AND last_attempt_at < NOW() - INTERVAL '5 minutes'
    LOOP
        BEGIN
            PERFORM retry_single_email(queue_record.id);
            retry_count := retry_count + 1;
        EXCEPTION WHEN OTHERS THEN
            RAISE NOTICE '重试邮件 % 失败: %', queue_record.id, SQLERRM;
        END;
    END LOOP;
    
    RETURN retry_count;
END;
$$ LANGUAGE plpgsql;

性能优化策略

数据库索引优化

-- 为频繁查询的字段创建索引
CREATE INDEX idx_email_queue_status ON email_queue(status);
CREATE INDEX idx_email_queue_recipient ON email_queue(recipient_email);
CREATE INDEX idx_email_queue_scheduled ON email_queue(scheduled_for);
CREATE INDEX idx_email_logs_queue_id ON email_logs(queue_id);
CREATE INDEX idx_email_logs_created_at ON email_logs(created_at);

-- 部分索引优化
CREATE INDEX idx_pending_emails ON email_queue(id) 
WHERE status = 'pending' AND scheduled_for <= NOW();

连接池与并发控制

import { Pool } from 'pg';

const dbPool = new Pool({
  host: process.env.DB_HOST,
  port: parseInt(process.env.DB_PORT || '5432'),
  database: process.env.DB_NAME,
  user: process.env.DB_USER,
  password: process.env.DB_PASSWORD,
  max: 20, // 最大连接数
  idleTimeoutMillis: 30000,
  connectionTimeoutMillis: 2000,
});

// 使用连接池处理高并发
async function processHighVolumeEmails() {
  const promises = emailList.map(async (email) => {
    const client = await dbPool.connect();
    try {
      await processSingleEmail(client, email);
    } finally {
      client.release();
    }
  });
  
  await Promise.all(promises);
}

监控与告警系统

邮件发送统计查询

-- 每日邮件发送统计
SELECT 
    DATE(created_at) as send_date,
    status,
    COUNT(*) as count,
    COUNT(*) * 100.0 / SUM(COUNT(*)) OVER (PARTITION BY DATE(created_at)) as percentage
FROM email_queue 
WHERE created_at >= CURRENT_DATE - INTERVAL '7 days'
GROUP BY DATE(created_at), status
ORDER BY send_date DESC, status;

-- 邮件提供商性能统计
SELECT 
    provider,
    COUNT(*) as total_emails,
    AVG(EXTRACT(EPOCH FROM (sent_at - created_at))) as avg_delivery_time_seconds,
    SUM(CASE WHEN status = 'delivered' THEN 1 ELSE 0 END) * 100.0 / COUNT(*) as delivery_rate
FROM email_logs 
GROUP BY provider
ORDER BY delivery_rate DESC;

部署与运维最佳实践

Docker容器化部署

FROM node:18-alpine

WORKDIR /app

COPY package*.json ./
RUN npm ci --only=production

COPY . .

# 健康检查
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
  CMD node healthcheck.js

EXPOSE 3000

CMD ["node", "dist/index.js"]

环境变量配置

# 数据库配置
DB_HOST=postgres
DB_PORT=5432
DB_NAME=email_service
DB_USER=email_user
DB_PASSWORD=secure_password

# 邮件服务配置
EMAIL_PROVIDER=resend
RESEND_API_KEY=your_resend_api_key

# 性能配置
MAX_CONCURRENT_EMAILS=50
EMAIL_BATCH_SIZE=100
RETRY_ATTEMPTS=3

总结与展望

React Email与PostgreSQL的结合为现代邮件系统提供了强大的事务处理能力。通过这种架构,我们实现了:

  1. 数据一致性:ACID事务保证邮件发送与业务操作的原子性
  2. 高可靠性:完善的错误处理和重试机制
  3. 可扩展性:支持高并发邮件发送场景
  4. 可观测性:完整的邮件发送生命周期追踪

未来可以进一步优化的方向包括:

  • 引入Redis缓存提升模板渲染性能
  • 实现基于WebSocket的实时邮件状态推送
  • 集成机器学习算法优化邮件发送时机
  • 增加A/B测试功能优化邮件模板效果

这种架构方案特别适合电商、SaaS、金融等对数据一致性要求极高的场景,为企业级邮件通信提供了可靠的技术保障。

【免费下载链接】react-email 💌 Build and send emails using React 【免费下载链接】react-email 项目地址: https://gitcode.com/GitHub_Trending/re/react-email

更多推荐