MailHub 事件
MailHub 模块在邮件发送和邮件打开时会上报事件,用于邮件投递追踪和数据分析。
1. 事件概览
MailHub 涉及两类事件:
| 事件名称 | 触发时机 | 说明 |
|---|---|---|
email | 邮件发送成功时 | 记录每一次邮件投递的详细信息 |
email_operation | 邮件被打开时 | 通过 Tracking Pixel 追踪邮件是否被用户打开 |
每次邮件投递会生成唯一的 message_id(格式:m + base36 编码的 snowflake ID,如 me9971u344wc1),贯穿发送和打开的全链路,可用于将 1 次发送事件与 N 次打开事件进行关联。
2. email 事件(邮件发送)
邮件发送成功后上报 email 事件。
2.1 事件 Schema
| 字段名称 | 类型 | 说明 |
|---|---|---|
| to | string | 收件人邮箱 |
| from | string | 发件人邮箱 |
| subject | string | 邮件主题 |
| body | string | 邮件正文内容 |
| message_id | string | 邮件投递唯一标识,格式为 m + base36 snowflake ID |
2.2 触发场景
email 事件在以下场景中触发:
API 触发发送
客户端或 Server 调用 /mail_hub/send 接口主动发送邮件时,发送成功后上报。
后台任务自动发送
后台任务根据 strategy 配置的 condition(trigger)轮询满足条件的用户,按 timer 配置的时间间隔自动发送邮件,发送成功后上报。
支持的 condition(trigger type):
| condition | 描述 |
|---|---|
web_sub_no_change | Web 付费用户,未重置密码 |
web_sub_and_change | Web 付费用户,已重置密码 |
web_no_sub | Web 未付费用户 |
user_manual | 用户手动触发,不设条件,调用 API 即发送 |
web_paid_no_login | Web 平台付费但付费后未登录使用的用户 |
事件驱动发送
Asset 模块在付费事件发生后通过 SQS 发送事件,MailHub 后台任务监听事件并根据 strategy 配置发送对应邮件,发送成功后上报。
支持的事件类型:
| event_type | 描述 |
|---|---|
asset.subscription_trial | 进入试用期 |
asset.subscription_first_cycle | 进入第一个订阅周期 |
asset.subscription_renewing_cycle | 续订成功,进入下一个订阅周期 |
Asset 事件格式:
{
"id": "xxx",
"time": 1729752000,
"type": "asset.subscription_first_cycle",
"app_id": "xxx",
"user_id": "xxx",
"email": "user@example.com",
"data": {}
}TIP
对于同一个事件,id 需要保持唯一性,MailHub 发送邮件时根据 id 进行事件去重。
延迟邮件发送
当用户购买时还未绑定邮箱,且 strategy 开启了 enable_pending_email 配置时,邮件会暂存到 Redis。待用户绑定邮箱后,系统自动发送所有待发送邮件并上报事件。
3. email_operation 事件(邮件打开)
通过 Tracking Pixel(1x1 透明 GIF)追踪邮件是否被用户打开。用户打开邮件时,邮件客户端加载追踪图片,触发 email_operation 事件上报。
3.1 事件 Schema
| 字段名称 | 类型 | 说明 |
|---|---|---|
| string | 收件人邮箱 | |
| status | string | 操作状态,固定为 open |
| message_id | string | 邮件投递唯一标识(与 extra 中的 message_id 一致,便于直接查询) |
| extra | string | JSON 格式扩展数据,包含追踪详情 |
3.2 extra 字段结构
extra 是一个 JSON 字符串,包含以下字段:
| 字段名称 | 类型 | 说明 |
|---|---|---|
| string | 收件人邮箱 | |
| user_id | string | 用户 ID |
| message_id | string | 邮件投递唯一标识 |
| user_agent | string | 邮件客户端的 User-Agent |
| ip | string | 请求来源 IP |
3.3 事件示例
{
"email": "user@example.com",
"status": "open",
"message_id": "me9971u344wc1",
"extra": "{\"email\":\"user@example.com\",\"user_id\":\"UXXXX\",\"message_id\":\"me9971u344wc1\",\"user_agent\":\"Mozilla/5.0 ...\",\"ip\":\"203.0.113.1\"}"
}3.4 Tracking Pixel 接口
接口路径:GET /bp/res/i.gif
该接口无需鉴权,嵌入邮件 HTML 中作为 <img> 标签的 src。
请求参数(Query String):
| 参数 | 缩写 | 类型 | 是否必填 | 说明 |
|---|---|---|---|---|
e | string | 是 | 收件人邮箱(触发上报的必须参数) | |
| message_id | mid | string | 是 | 邮件消息 ID |
| user_id | u | string | 否 | 用户 ID,缺省时根据 email 自动查找 |
注意
e 和 mid 同时存在时才会上报打开事件。缺少任一参数仅返回图片,不记录事件。
请求示例:
GET /bp/res/i.gif?e=user%40example.com&u=UXXXX&mid=me9971u344wc1响应:
- HTTP 状态码:200
- Content-Type:
image/gif - 响应体:1x1 透明 GIF(43 bytes)
响应头包含缓存禁止指令,确保每次打开邮件都会触发请求:
Cache-Control: no-store, no-cache, must-revalidate, max-age=0
Pragma: no-cache4. 邮件模板变量
4.1 系统变量
以下变量由系统自动注入,适用于所有邮件模板:
| 变量 | 描述 |
|---|---|
{{.UserName}} | 用户名 |
{{.Email}} | 用户邮箱 |
{{.SessionToken}} | 用户 session token |
{{.TrackingPixelURL}} | 邮件打开追踪图片 URL |
{{.MessageID}} | 邮件消息唯一 ID |
4.2 自定义变量
通过 send 接口的 custom_variables 参数传入,在模板中使用 {{.CustomVariables.xxx}} 引用。
系统变量也可通过 CustomVariables 方式引用:
{{.CustomVariables.TrackingPixelURL}}{{.CustomVariables.MessageID}}
4.3 Asset 事件模板变量
asset.subscription_trial 事件
| 变量名称 | 类型 | 格式及举例 | 说明 |
|---|---|---|---|
| PurchaseDateYear | string | 2023 | 试用期开始时间的年份 |
| PurchaseDateMonth | string | 01, 04, 10, 11 | 试用期开始时间的月份,取值 01-12 |
| PurchaseDateMonthEnglish | string | March, May | 试用期开始时间的月份,英文表达 |
| PurchaseDateDay | string | 01, 02, 27, 31 | 试用期开始时间的日期,取值 01-31 |
| string | abc@example.com | 用户邮箱 | |
| TrialCyclePeriod | string | 1 day, 2 months, 3 years | 试用期时长 |
| TrialCycleStartYear | string | 2023 | 试用期开始时间的年份 |
| TrialCycleStartMonth | string | 01, 04, 10, 11 | 试用期开始时间的月份,取值 01-12 |
| TrialCycleStartMonthEnglish | string | March, May | 试用期开始时间的月份,英文表达 |
| TrialCycleStartDay | string | 01, 02, 27, 31 | 试用期开始时间的日期,取值 01-31 |
| TrialCycleEndYear | string | 2023 | 试用期结束时间的年份 |
| TrialCycleEndMonth | string | 01, 04, 10, 11 | 试用期结束时间的月份,取值 01-12 |
| TrialCycleEndMonthEnglish | string | March, May | 试用期结束时间的月份,英文表达 |
| TrialCycleEndDay | string | 01, 02, 27, 31 | 试用期结束时间的日期,取值 01-31 |
| TrialCyclePrice | string | 3.5 USD, 1.0 HKD | 试用期价格 |
| RegularCyclePeriod | string | 1 day, 2 months, 3 years | 正常周期时长 |
| RegularCycleStartYear | string | 2023 | 第一期正常周期开始时间的年份 |
| RegularCycleStartMonth | string | 01, 04, 10, 11 | 第一期正常周期开始时间的月份,取值 01-12 |
| RegularCycleStartMonthEnglish | string | March, May | 第一期正常周期开始时间的月份,英文表达 |
| RegularCycleStartDay | string | 01, 02, 27, 31 | 第一期正常周期开始时间的日期,取值 01-31 |
| RegularCyclePrice | string | 3.5 USD, 1.0 HKD | 正常周期的价格 |
| Platform | string | STRIPE, PAYPAL | 支付平台 |
| TransactionID | string | in_1N4bGzJeDjdpBmRtPcInXgOs | 支付平台交易 ID |
asset.subscription_first_cycle / asset.subscription_renewing_cycle 事件
| 变量名称 | 类型 | 格式及举例 | 说明 |
|---|---|---|---|
| PurchaseDateYear | string | 2023 | 当期付费时间的年份 |
| PurchaseDateMonth | string | 01, 04, 10, 11 | 当期付费时间的月份,取值 01-12 |
| PurchaseDateMonthEnglish | string | March, May | 当期付费时间的月份,英文表达 |
| PurchaseDateDay | string | 01, 02, 27, 31 | 当期付费时间的日期,取值 01-31 |
| string | abc@example.com | 用户邮箱 | |
| RegularCyclePeriod | string | 1 day, 2 months, 3 years | 正常周期时长 |
| RegularCycleStartYear | string | 2023 | 当前周期开始时间的年份 |
| RegularCycleStartMonth | string | 01, 04, 10, 11 | 当前周期开始时间的月份,取值 01-12 |
| RegularCycleStartMonthEnglish | string | March, May | 当前周期开始时间的月份,英文表达 |
| RegularCycleStartDay | string | 01, 02, 27, 31 | 当前周期开始时间的日期,取值 01-31 |
| RegularCycleEndYear | string | 2023 | 当前周期结束时间的年份 |
| RegularCycleEndMonth | string | 01, 04, 10, 11 | 当前周期结束时间的月份,取值 01-12 |
| RegularCycleEndMonthEnglish | string | March, May | 当前周期结束时间的月份,英文表达 |
| RegularCycleEndDay | string | 01, 02, 27, 31 | 当前周期结束时间的日期,取值 01-31 |
| RegularCyclePrice | string | 3.5 USD, 1.0 HKD | 当前周期的价格 |
| Platform | string | STRIPE, PAYPAL | 支付平台 |
| TransactionID | string | in_1N4bGzJeDjdpBmRtPcInXgOs | 支付平台交易 ID |
TIP
在邮件模板中使用 Go 模板语法引用变量,如 {{.Platform}}、{{.RegularCyclePeriod}}。
5. Strategy 配置
MailHub 通过 Console 配置 strategy 来管理邮件发送规则。Strategy 分为两种类型:
5.1 API 类型
由客户端或服务端调用 API 触发发送,支持 condition 和 timer 配置。
{
"name": "strategy_name",
"type": "api",
"condition": "web_sub_no_change",
"timer": [
{
"interval": "5m",
"repeat": false,
"content_name": "change_password_email"
},
{
"interval": "30m",
"repeat": false,
"content_name": "change_password_email"
}
],
"description": "web 付费用户未重置密码提醒",
"email_from": "noreply@example.com"
}| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| name | string | 是 | 策略名称,API 调用时使用 |
| type | string | 是 | 固定为 api |
| condition | string | 是 | 触发条件(trigger type),见上方 condition 列表 |
| timer | array | 否 | 定时发送配置 |
| timer[].interval | string | 是 | 发送间隔,如 5m、1h、24h |
| timer[].repeat | bool | 是 | 是否重复发送 |
| timer[].content_name | string | 是 | 邮件内容模板名称 |
| description | string | 否 | 策略描述 |
| email_from | string | 否 | 发件人邮箱,不填使用默认配置 |
| enable_api_email | bool | 否 | 是否允许通过 API 指定收件人邮箱,默认 false |
5.2 Event 类型
由 Asset 模块的付费事件驱动发送,不支持 timer 配置。
{
"name": "strategy_name",
"type": "event",
"event_type": "asset.subscription_first_cycle",
"event_email_content_name": "receipt_email",
"description": "首次订阅收据邮件",
"email_from": "noreply@example.com",
"enable_pending_email": true,
"pending_email_delay": "72h"
}| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| name | string | 是 | 策略名称 |
| type | string | 是 | 固定为 event |
| event_type | string | 是 | 事件类型,见上方 event_type 列表 |
| event_email_content_name | string | 是 | 邮件内容模板名称 |
| description | string | 否 | 策略描述 |
| email_from | string | 否 | 发件人邮箱,不填使用默认配置 |
| enable_pending_email | bool | 否 | 是否启用延迟邮件发送,默认 false |
| pending_email_delay | string | 否 | 延迟发送的过期时间,如 72h、2m,超过此时间不再发送 |
TIP
使用 Event 类型 strategy 前,需要在 Console 的 Asset 模块开启 MailHub 事件开关: Asset -> Basic Config -> Mailhub Config -> Enable Mailhub Event
6. 数据关联与分析
6.1 通过 message_id 关联发送与打开
每次邮件投递生成唯一 message_id,同时存在于:
email事件的message_id字段email_operation事件的message_id字段和extraJSON 中
通过 message_id 可以:
- 将 1 次发送(
email)事件与 N 次打开(email_operation)事件关联 - 区分邮件服务器预览与用户真实打开
- 计算邮件打开率
6.2 注意事项
WARNING
- Tracking Pixel 仅在 HTML 格式的邮件中有效。
- 部分邮件客户端(如 Outlook 某些版本)默认不加载外部图片,此时无法追踪。
- 追踪数据为统计参考,不保证 100% 准确。
- AWS SES 邮件发送服务对发送失败率有监控,如果发送过多失败的邮件(如地址错误),可能会被 AWS 暂停服务。
