完整的邮件营销工作流程,包含转化追踪和试用激活。适用于(1)创建邮件营销活动,(2)发送营销邮件...
电子邮件活动管理 - 完整工作流程. 目的:可重新使用的工作流程,用于创建、跟踪和管理电子邮件营销活动,并配有转换跟踪、提醒电子邮件和试验a是一项面向实际任务的技能。它将相关步骤、工具调用和结果整理方式集中到统一流程中,帮助使用者更快完成目标并减少重复操作。
实际使用前应先确认任务范围、数据来源、运行环境、必要权限和关键参数,再依据技能说明逐步执行;若输入条件不完整,应先补齐信息或采用保守配置,避免因错误假设导致结果偏离需求。
执行过程中需要关注工具调用是否成功、接口或依赖是否可用、输出格式是否符合预期,并对异常提示、缺失字段和边界情况进行处理;涉及批量任务时,还应保存进度,避免中断后重复操作。
目的:可复用的工作流,用于创建、追踪和管理邮件营销活动,支持转化追踪、提醒邮件发送及试用期激活。
适用范围:增长营销、邮件活动、转化追踪、试用期激活、活动数据分析
campaigns 表CREATE TABLE campaigns (
id UUID PRIMARY KEY,
campaign_id VARCHAR(255) UNIQUE NOT NULL, -- 例如 "ny2026", "summer2026"
name VARCHAR(255) NOT NULL,
description TEXT,
trial_days INTEGER,
tier VARCHAR(50), -- ENTERPRISE, SCALE 等
plan_name TEXT,
offer_expires_at TIMESTAMP,
created_at TIMESTAMP DEFAULT NOW(),
updated_at TIMESTAMP DEFAULT NOW()
);
campaign_clicks 表CREATE TABLE campaign_clicks (
id UUID PRIMARY KEY,
campaign_id VARCHAR(255) NOT NULL,
email VARCHAR(255) NOT NULL,
click_source VARCHAR(100), -- 例如 "cta_button", "events_click"
utm_source VARCHAR(100),
utm_medium VARCHAR(100),
utm_campaign VARCHAR(100),
converted BOOLEAN DEFAULT FALSE,
converted_at TIMESTAMP,
user_id UUID,
created_at TIMESTAMP DEFAULT NOW()
);
SELECT
COUNT(*) as total_clicks,
COUNT(DISTINCT email) as unique_users,
COUNT(CASE WHEN converted = TRUE THEN 1 END) as converted_count,
COUNT(CASE WHEN converted = FALSE THEN 1 END) as pending_count,
ROUND(100.0 * COUNT(CASE WHEN converted = TRUE THEN 1 END) / COUNT(*), 2) as conversion_rate
FROM campaign_clicks
WHERE campaign_id = 'campaign-id-here';
SELECT
COUNT(CASE WHEN u.id IS NOT NULL THEN 1 END) as registered_users,
COUNT(CASE WHEN u.id IS NULL THEN 1 END) as non_registered_users,
COUNT(CASE WHEN s.tier != 'free' AND s.status = 'active' THEN 1 END) as already_paid_users
FROM campaign_clicks cc
LEFT JOIN users u ON u.email = cc.email
LEFT JOIN subscriptions s ON s.user_id = u.id
WHERE cc.campaign_id = 'campaign-id-here'
AND cc.converted = FALSE;
Resend 免费版限制:每秒 2 次请求(注意:不是每分钟 100 次!)
正确实现方式:
import time
RATE_LIMIT_DELAY = 0.5 # 每秒发送 2 封邮件
for email in emails:
send_email(email)
if i < len(emails):
time.sleep(RATE_LIMIT_DELAY) # 等待 0.5 秒
错误实现方式(将导致 429 错误):
# 切勿如此操作 —— 发送速度过快
RATE_LIMIT = 100 # 每分钟 100 封邮件
for email in emails:
send_email(email)
# 每 100 封才暂停一次 —— 为时已晚!
if i % RATE_LIMIT == 0:
time.sleep(60)
import os
import requests
RESEND_API_KEY = os.getenv("RESEND_API_KEY")
RESEND_API_URL = "https://api.resend.com/emails"
FROM_EMAIL = "no-reply@ainative.studio"
def send_campaign_email(email: str, campaign_id: str, template_html: str):
"""通过 Resend 发送活动邮件。"""
# 替换模板变量
html = template_html.replace("{{email}}", email)
html = html.replace("{{campaign_id}}", campaign_id)
payload = {
"from": FROM_EMAIL,
"to": [email],
"subject": "Your campaign subject here",
"html": html,
"tags": [
{"name": "campaign", "value": campaign_id},
{"name": "type", "value": "reminder"}
]
}
response = requests.post(
RESEND_API_URL,
headers={
"Authorization": f"Bearer {RESEND_API_KEY}",
"Content-Type": "application/json"
},
json=payload,
timeout=10
)
if response.status_code in [200, 201]:
return True, response.json().get("id")
else:
return False, response.text
def activate_trial(user, campaign_id: str, trial_days: int, plan_name: str):
"""为用户激活 ENTERPRISE 试用期。"""
trial_end = datetime.utcnow() + timedelta(days=trial_days)
# 关键:PostgreSQL 枚举值为大写
# 'enterprise' → 'ENTERPRISE'
# 'active' 而非 'trial'(不存在 trial 状态)
if user['subscription_id']:
# 更新已有订阅
cur.execute("""
UPDATE subscriptions
SET tier = 'ENTERPRISE',
status = 'active',
trial_ends_at = %s,
plan_name = %s,
plan_price = 0,
current_period_end = %s,
updated_at = NOW()
WHERE id = %s
""", (trial_end, plan_name, trial_end, user['subscription_id']))
else:
# 创建新订阅
cur.execute("""
INSERT INTO subscriptions (
user_id, tier, status,
trial_ends_at, plan_name, plan_price,
billing_email, start_date,
current_period_start, current_period_end,
max_users, max_projects, monthly_token_limit,
max_ai_requests_per_day, max_models,
created_at, updated_at
) VALUES (
%s, 'ENTERPRISE', 'active',
%s, %s, 0,
%s, NOW(),
NOW(), %s,
10, 15, 1000000,
10000, 100,
NOW(), NOW()
)
""", (
str(user['user_id']),
trial_end,
plan_name,
user['email'],
trial_end
))
# 标记点击记录为已转化
cur.execute("""
UPDATE campaign_clicks
SET converted = TRUE,
converted_at = NOW(),
user_id = %s
WHERE id = %s
""", (str(user['user_id']), user['click_id']))
conn.commit()
问题:'enterprise' 报错 “invalid input value for enum”
解决方案:始终使用大写形式:'ENTERPRISE'、'SCALE'、'FREE'
问题:使用 status = 'trial'(该状态不存在)
解决方案:使用 status = 'active' 并配合 trial_ends_at 字段
问题:将速率限制设为每分钟 100 次,导致 429 错误 解决方案:采用每秒 2 次(两次发送之间延迟 0.5 秒)
问题:部分用户无 organization_id,引发 NULL 约束错误
解决方案:INSERT 前检查 organization_id 是否存在;若为 NULL,则单独执行查询
问题:生产环境模板中残留测试邮箱
解决方案:使用 {{email}} 模板变量,并在发送时动态替换
问题:“too many clients already” 错误 解决方案:始终使用 try/finally 确保连接被关闭
所有活动邮件模板必须存放在:
src/backend/app/services/templates/{campaign_id}_{type}.html
示例文件名:
ny2026_gift.html —— 初始活动邮件ny2026_reminder.html —— 面向未注册用户的提醒邮件summer2026_welcome.html —— 欢迎邮件Claim Your Trial
/* 基础配色 */
body {
font-family: 'Poppins', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
background-color: #131726;
}
.email-container {
background-color: #22263c;
border-radius: 16px;
}
/* 头部渐变 */
.header {
background: linear-gradient(135deg, #4B6FED 0%, #5867EF 100%);
padding: 50px 40px;
}
/* 主要操作按钮(CTA) */
.cta-button {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: #ffffff;
padding: 18px 48px;
border-radius: 12px;
}
railway run -s "AINative- Core -Production" psql -c "
SELECT
COUNT(*) as total_clicks,
COUNT(DISTINCT email) as unique_users,
COUNT(CASE WHEN converted THEN 1 END) as converted
FROM campaign_clicks
WHERE campaign_id = 'your-campaign-id';
"
railway run -s "AINative- Core -Production"
python3 scripts/send_{campaign_id}_reminder.py --yes
VALID_CAMPAIGNS 配置项相关专题
热门下载
相关下载
精品课程
共6课时 | 54.7万人学习
共89课时 | 134万人学习
共49课时 | 82.5万人学习