You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
wx.sstbc.com/doc/邮件打开率与回执统计实现方案.md

132 lines
6.7 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

# 邮件打开率统计功能实现方案
## 一、需求概述
在现有邮件发送能力(`EmailRecord` / `EmailRecordUser`、定时任务 `send_email`)基础上,增加:
- **打开率统计**:统计每封邮件是否被收件人「打开」(基于追踪像素),并在后台展示汇总与明细。
便于评估邮件触达效果,为后续运营与内容优化提供数据支撑。
## 二、现状简要分析
| 模块 | 现状 |
|------|------|
| 发送记录 | `email_records`:主题、模版、发送时间、状态等 |
| 收件人明细 | `email_record_users`邮箱、status0 待发送 / 1 成功 / 2 失败、send_time、reason |
| 发送方式 | `SendEmail` 命令 + `EmailRecordUser::email()`,使用 Laravel `Mail::send()` |
| 统计 | 仅有「成功/失败」数量,无打开相关字段与逻辑 |
## 三、技术方案
### 打开率统计
**思路**:采用「追踪像素 + 唯一链接」方式。
1. **追踪像素Open Tracking**
在 HTML 邮件正文末尾插入一张 1×1 透明图片,`src` 指向本站追踪接口,并携带 `email_record_user_id` 作为唯一标识。
2. **接口行为**
- 收到 GET 请求后,根据参数识别到对应的 `email_record_user`
- 若该条尚未记录打开,则写入「首次打开时间」并计数;可同时记录打开次数、最近打开时间(视需求扩展)。
- 返回 1×1 透明 GIF 图片响应(或 204 No Content避免影响邮件展示。
3. **打开率计算**
- 单条邮件任务:`打开率 = 有过打开记录的 email_record_user 数 / 发送成功的 email_record_user 数`。
- 列表/汇总:在 `EmailRecord` 维度聚合「已打开人数」「发送成功人数」后计算百分比。
**注意**
- 部分邮件客户端或隐私保护会屏蔽远程图片,打开率会偏保守。
- 需做好参数校验与防刷(同一 user 可多次请求只计一次或按业务规则限频)。
## 四、数据库设计
在现有表结构上做最小扩展,便于与当前 `EmailRecord` / `EmailRecordUser` 一致。
### 4.1 `email_record_users` 表增加字段(推荐)
| 字段名 | 类型 | 说明 |
|--------|------|------|
| `opened_at` | `dateTime` nullable | 首次打开时间(用于判断是否「已打开」) |
| `open_count` | `int` default 0 | 打开次数(可选,用于重复打开统计) |
**说明**
- 仅当 status = 1发送成功才参与打开率计算。
- 若只需「是否打开」,仅 `opened_at` 即可;`open_count` 可用于「最近打开时间」等扩展。
### 4.2 迁移示例
```php
// database/migrations/xxxx_add_open_tracking_to_email_record_users_table.php
Schema::table('email_record_users', function (Blueprint $table) {
$table->dateTime('opened_at')->nullable()->after('send_time')->comment('首次打开时间');
$table->unsignedInteger('open_count')->default(0)->after('opened_at')->comment('打开次数');
});
```
无需新建表即可满足「按人维度打开 + 按邮件记录聚合打开率」的需求。
## 五、后端实现要点
### 5.1 追踪像素接口(公开,无需登录)
- **路由**:例如 `GET /api/email/open/{id}``GET /track/email/open?id=xxx`,其中 `id``email_record_user_id`
- **逻辑**
1. 根据 `email_record_user_id` 查出 `EmailRecordUser`,校验 status = 1发送成功
2.`opened_at` 为空,则写入 `opened_at = now()`,并可选 `open_count += 1`;若已存在,可按策略只更新 `open_count` 或忽略。
3. 返回 1×1 透明 GIF可把一张静态 GIF 放在 `public``storage`,或使用二进制输出)。
### 5.2 邮件内容中插入追踪像素
-`SendEmail` 命令(或统一封装「生成邮件 HTML」的地方对每条 `EmailRecordUser`
- 生成该条记录对应的追踪 URL`email_record_user_id`),如:`https://your-domain.com/api/email/open/{{ $recordUser->id }}`。
- 在 HTML 正文末尾追加:`<img src="{{ $trackingUrl }}" width="1" height="1" alt="" style="display:block" />`。
- 若当前邮件是纯文本,可考虑转为 multiparttext + html仅在 html 部分加像素;若暂时只发 HTML则直接拼接即可。
### 5.3 统计查询
- **列表EmailRecord 维度)**
- 已有:`withCount('emailRecordUsers')`、成功数、失败数。
- 新增:`withCount(['emailRecordUsers as opened_count' => function ($q) { $q->where('status', 1)->whereNotNull('opened_at'); }])`,或单独查询「发送成功数」与「已打开数」,再算打开率。
- **详情**:在 `emailRecordUsers` 中直接展示 `opened_at`、`open_count`,列表页可展示「已打开 / 已发送」与打开率百分比。
### 5.4 接口返回建议
- 列表接口在每条 `EmailRecord` 上增加:
- `sent_count`发送成功数status=1
- `opened_count`已打开数status=1 且 opened_at 非空)。
- `open_rate`:百分比,如 `sent_count > 0 ? round(opened_count / sent_count * 100, 2) : 0`
- 详情接口在每条 `EmailRecordUser` 上增加 `opened_at`、`open_count`(若存在)。
## 六、前端展示建议
- **邮件记录列表**
- 增加列:「发送成功数」「已打开数」「打开率%(如 65.00%)」。
- 可选:筛选「打开率 &gt; 某值」或「未打开」。
- **邮件记录详情(收件人明细)**
- 表格列:邮箱、发送状态、发送时间、**是否已打开**、**首次打开时间**、打开次数。
- 可导出为 Excel便于线下分析。
- **仪表盘/统计页(可选)**
- 按时间范围汇总:总发送数、总打开数、平均打开率;或按某次邮件任务排行。
## 七、安全与隐私
- **防刷**:同一 `email_record_user_id` 多次请求只记一次「打开」或按业务限频,避免恶意请求虚高打开率。
- **隐私**:追踪像素仅记录「是否/何时打开」,不在方案中记录 IP、User-Agent 等(若后续需要可单独评估合规性)。
## 八、实施步骤建议
| 步骤 | 内容 |
|------|------|
| 1 | 新增迁移:`email_record_users` 增加 `opened_at`、`open_count` |
| 2 | 实现追踪接口(路由 + 控制器,按 `email_record_user_id` 查询并更新 + 返回 1×1 图片) |
| 3 | 在发送逻辑中为每条收件人生成追踪 URL并在 HTML 邮件末尾插入追踪像素 |
| 4 | 在 `EmailRecordController` 的 index/show 中增加 `opened_count`、`open_rate` 及明细字段的查询与返回 |
| 5 | 前端:列表与详情页增加打开率、已打开数、首次打开时间等展示与导出 |
| 6 | (可选)增加简单防刷与限频、日志 |
按上述步骤即可在现有项目上完成邮件打开率统计功能。