# 通用 EDM 业务流程说明

更新时间：2026-05-26

## 1. 文档目标

本文用于新的 EDM 子系统设计或重构，目标是在功能保持一致的前提下，将现有 EDM 业务抽象成通用流程，便于后续拆分服务、设计数据模型、规划 Kafka 消费链路、接入邮件发送通道和处理邮件客服工单。

## 2. 业务范围

通用 EDM 子系统建议分为三条业务线：

| 业务线 | 说明 |
| --- | --- |
| 批量营销邮件 | 管理后台创建邮件任务，按标签、站点、产品、用户状态筛选目标用户，生成待发送邮件记录，通过队列异步发送 |
| 自动 / 实时策略邮件 | 根据用户注册、访问、在线、站点、产品、行为、无消息等规则自动筛选用户，并生成策略邮件 |
| 邮件工单 | 用户来信、表单提交或外部收信服务进入后台后，生成或更新邮件工单，由客服处理、回复、转发、关闭 |

如果新系统还需要普通邮箱功能，可以作为独立模块处理。普通邮箱收发不一定进入 EDM 工单链路，是否合并需要单独确认。

## 3. 总体架构

```mermaid
flowchart TD
    A["管理后台"] --> B["EDM 任务服务"]
    B --> C["目标用户筛选服务"]
    C --> D["邮件记录生成服务"]
    D --> E["Kafka / 队列"]
    E --> F["邮件发送消费者"]
    F --> G["外部发送通道"]
    G --> H["AWS SES / SMTP / Gmail / Microsoft"]
    H --> I["事件回调"]
    I --> J["事件处理服务"]
    J --> K["统计与黑名单"]

    L["用户来信 / 表单提交"] --> M["入站接收服务"]
    M --> N["Kafka / 入站队列"]
    N --> O["EDM 工单服务"]
    O --> P["客服工作台"]
    P --> E
```

核心组件职责：

| 组件 | 职责 |
| --- | --- |
| 管理后台 | 创建邮件任务、审核任务、查看统计、处理邮件工单 |
| 任务服务 | 保存任务配置、正文、模板、发送时间、审核状态 |
| 用户筛选服务 | 根据标签、站点、产品、黑名单、订阅状态、发送频率等规则筛选目标用户 |
| 邮件记录服务 | 按用户生成单封待发送邮件记录和正文快照 |
| Kafka / 队列 | 解耦任务生成、邮件发送、入站消息、事件统计 |
| 发送消费者 | 消费待发送邮件，调用外部发送通道，并保存发送结果 |
| 入站接收服务 | 接收表单、用户来信或外部邮件服务回调，写入入站队列 |
| 工单服务 | 根据来信生成或更新邮件工单，维护状态、负责人、未读数和处理记录 |
| 事件处理服务 | 处理送达、打开、点击、退信、投诉、拒信等邮件事件 |
| Redis / 缓存 | 保存并发锁、游标、限流计数、近期任务统计、临时筛选集合 |

## 4. 核心数据模型

新子系统建议至少抽象以下对象：

| 对象 | 说明 |
| --- | --- |
| 邮件任务 EmailTask | 批量营销或策略邮件任务，保存任务名称、类型、发送时间、审核状态、目标条件 |
| 邮件内容 EmailContent | 任务级正文、标题、模板、发件人、回复地址、附件配置 |
| 目标用户 TaskRecipient | 任务命中的用户关系，便于统计和去重 |
| 单封邮件 EmailMessage | 最终发送或接收的一封邮件记录，包含方向、收件人、发件人、状态、message_id、工单 ID |
| 邮件正文 EmailBody | 单封邮件正文快照，避免模板后续变化影响历史邮件 |
| 工单 EmailTicket | 用户来信或客服主动发起的一次处理过程 |
| 分配记录 Assignment | 工单分配、移交、释放、代班等操作记录 |
| 节点日志 NodeLog | 创建、分配、首次回复、关闭、未解决、转化中等关键节点 |
| 发送事件 EmailEvent | 送达、打开、点击、退信、投诉、拒信、渲染失败等事件 |
| 黑名单 / 退订名单 Suppression | 退信、投诉、退订、风险用户等不可发送或需谨慎发送的人群 |

## 5. 批量营销邮件流程

```mermaid
flowchart TD
    A["后台创建邮件任务"] --> B["校验任务配置"]
    B --> C["写入任务、内容、标签条件"]
    C --> D["进入待审核"]
    D --> E{"审核结果"}
    E -->|通过| F["进入待执行"]
    E -->|驳回| G["记录驳回原因并结束"]
    F --> H["到达发送时间"]
    H --> I["筛选目标用户"]
    I --> J["生成单封待发送邮件记录"]
    J --> K["投递 Kafka"]
    K --> L["发送消费者调用外部邮件通道"]
    L --> M["更新发送状态和 message_id"]
```

创建任务时建议校验：

1. 任务名称不能重复。
2. 邮件模板或正文必须存在。
3. 发件邮箱必须存在并可用。
4. 发件域名必须在允许范围内。
5. 必须选择目标人群或策略条件。
6. 发送时间必须符合业务规则。
7. 如果绑定活动，发送时间需要满足活动时间约束。
8. 目标人数需要预估，避免误发全量用户。

目标用户筛选建议包含：

1. 标签包含和标签排除。
2. 站点、产品、品牌、语言、地区。
3. 订阅状态、退订状态、黑名单、投诉用户、永久退信用户。
4. 近期发送频率限制，避免短时间重复触达。
5. 任务级去重，避免同一用户重复生成同一任务邮件。

## 6. 自动 / 实时策略邮件流程

```mermaid
flowchart TD
    A["策略配置"] --> B["定时任务生成当日策略任务"]
    B --> C["实时策略扫描"]
    C --> D["按用户行为和条件筛选"]
    D --> E["应用黑名单、退订、频率控制"]
    E --> F["生成待发送邮件"]
    F --> G["投递发送队列"]
    G --> H["发送消费者调用邮件通道"]
    H --> I["事件回调更新统计"]
```

策略邮件与批量邮件的区别：

1. 批量邮件通常由运营手动创建，发送时间明确。
2. 策略邮件通常由系统按规则自动生成，可能按分钟或按天扫描。
3. 策略邮件更依赖幂等和频率控制，避免同一用户在同一策略下反复触发。
4. 策略邮件应记录策略 ID、触发原因、触发时间，便于归因。

建议策略执行时做并发锁，避免多个任务实例重复生成邮件。

## 7. 邮件发送链路

通用发送链路：

```mermaid
flowchart TD
    A["待发送邮件记录"] --> B["写入 Kafka"]
    B --> C["发送消费者"]
    C --> D["读取发件通道配置"]
    D --> E{"发送通道"}
    E -->|批量营销| F["AWS SES 或批量发送通道"]
    E -->|客服回复| G["SMTP / Gmail / Microsoft"]
    F --> H["保存发送结果"]
    G --> H
    H --> I["通知前端或更新统计"]
```

发送消费者需要处理：

1. 队列消息反序列化。
2. 邮件正文、标题、收件人、发件人、回复地址、附件组装。
3. 发送通道选择。
4. 调用外部服务。
5. 成功后保存 `message_id`、发送时间和成功状态。
6. 失败后保存错误信息、失败状态和重试次数。

发送通道建议按场景区分：

| 场景 | 推荐处理 |
| --- | --- |
| 批量营销邮件 | 走支持批量和事件回调的邮件服务，例如 AWS SES |
| 策略邮件 | 可复用批量发送通道，但必须做频率和幂等控制 |
| 工单客服回复 | 按发件邮箱配置选择 SMTP、Gmail API 或 Microsoft Graph |
| 普通邮箱回复 | 可独立于工单链路，同步或异步发送均可 |

## 8. 邮件事件回调与统计

邮件发送后，外部服务会产生事件。通用事件包括：

| 事件 | 处理建议 |
| --- | --- |
| Delivery / 送达 | 标记邮件已送达，记录送达时间和发送 IP |
| Bounce / 退信 | 区分永久退信和临时退信，更新任务统计；永久退信可加入黑名单 |
| Open / 打开 | 标记打开时间，更新任务打开统计 |
| Click / 点击 | 记录点击链接和点击时间，更新点击统计 |
| Complaint / 投诉 | 记录投诉，加入抑制名单或黑名单 |
| Subscription / 订阅变更 | 更新订阅或退订状态 |
| Reject / 拒信 | 记录拒信原因，更新失败统计 |
| Rendering Failure / 渲染失败 | 记录模板或内容渲染失败 |
| DeliveryDelay / 延迟 | 可记录延迟事件，是否统计需业务确认 |

事件处理要点：

1. 事件必须通过 `message_id` 或自定义追踪 ID 关联到本地邮件记录。
2. 同一事件可能重复回调，需要幂等处理。
3. 打开和点击事件存在图片加载、隐私保护、客户端屏蔽等不确定性，统计只能作为参考指标。
4. 投诉、退订、永久退信应优先进入发送抑制规则。

## 9. 入站邮件 / 表单进入工单流程

入站来源可以有多种：

1. 网站表单提交。
2. 用户真实邮件来信。
3. 外部收信服务回调。
4. IM 或其他渠道转入邮件客服。

通用流程：

```mermaid
flowchart TD
    A["用户来信或表单提交"] --> B["入站接收服务"]
    B --> C["写入 Kafka 入站队列"]
    C --> D["EDM 工单消费者"]
    D --> E["保存入站邮件和正文"]
    E --> F{"是否存在未关闭工单"}
    F -->|否| G["创建新工单"]
    F -->|是| H["绑定到原工单并更新未读数"]
    G --> I["写入节点日志"]
    H --> I
    I --> J["通知客服工作台"]
```

创建或更新工单时建议：

1. 以发件邮箱、收件邮箱、业务用户 ID、会话标识等组合判断是否复用未关闭工单。
2. 新工单记录来源、用户邮箱、发件邮箱、团队、状态、未读数、最后来信时间。
3. 已有工单更新最后来信时间、未读数、用户来信数。
4. 如果当前客服离线，可以释放负责人，让工单重新进入分配池。
5. 入站正文应保存原始内容和清洗后的展示内容。

## 10. 工单客服处理流程

### 10.1 工单状态

通用状态建议：

| 状态 | 说明 |
| --- | --- |
| 待处理 | 新入站邮件或表单生成工单，等待客服处理 |
| 服务中 | 客服已接手并正在处理 |
| 未解决 | 客服标记暂未解决，需要后续跟进 |
| 转化中 | 进入销售或转化跟进阶段 |
| 已关闭 | 本次邮件工单处理结束 |

状态值可以由新系统自行定义，但需要保证列表筛选、统计、自动关闭和权限校验口径统一。

### 10.2 自动分配

自动分配建议流程：

1. 找到待处理且未分配的工单。
2. 根据收件邮箱、团队、站点、语言或业务线确定可服务团队。
3. 获取在线客服。
4. 按接单上限、当前处理数、最近分配时间选择客服。
5. 更新工单负责人。
6. 写入分配记录和节点日志。
7. 通知客服工作台。

### 10.3 客服回复

```mermaid
flowchart TD
    A["客服点击发送"] --> B["校验客服在线、权限、工单状态"]
    B --> C["写入待发送邮件记录和正文"]
    C --> D["更新工单未读数、首次响应、回复耗时"]
    D --> E["投递客服回复队列"]
    E --> F["发送消费者选择 SMTP / Gmail / Microsoft"]
    F --> G["保存发送成功或失败结果"]
    G --> H["通知客服工作台"]
```

发送前建议校验：

1. 客服必须在线。
2. 工单必须存在且未关闭。
3. 当前客服必须是工单处理人，或具备接手权限。
4. 工单必须属于当前客服可处理团队。
5. 主题、正文、收件人、回复地址必须合法。
6. 附件大小、类型、数量需要符合业务规则和发送通道限制。

### 10.4 转发和主动开工单

转发：

1. 需要填写新的收件人。
2. 转发邮件可以不绑定到原工单作为普通回复。
3. 原邮件和转发邮件需要建立关联，方便追溯。
4. 发送链路仍可复用客服回复队列。

主动开工单：

1. 客服选择发件邮箱和目标用户邮箱。
2. 系统校验发件邮箱归属团队。
3. 如果同一发件邮箱和用户邮箱已有未关闭工单，应拒绝重复创建或要求接手原工单。
4. 创建服务中工单，负责人为当前客服。
5. 写入节点日志和分配记录。
6. 发送第一封邮件。

## 11. 工单辅助任务

新子系统可按需要保留以下后台任务：

| 任务 | 说明 |
| --- | --- |
| 自动分配 | 将未分配待处理工单分配给在线客服 |
| 自动移交 | 当前负责人离线且有新来信时，按代班或团队规则重新分配 |
| DDL 释放 | 工单分配后超过配置时间未处理，释放为未分配 |
| 未回复提醒 | 用户新来信超过配置时间未回复，提醒负责人 |
| 自动关闭 | 服务中工单超过配置时间无新用户来信时自动关闭 |
| 未分配告警 | 未分配工单数量超过阈值时通知团队管理员 |
| 统计同步 | 定时刷新任务发送数、回复数、打开数、点击数等统计 |

具体调度频率和启用范围需要按新系统 SLA 确认。

## 12. 通用限制与风控点

### 12.1 任务和发送限制

建议配置化管理：

1. 单任务最大目标人数。
2. 单轮投递队列数量。
3. 单发件邮箱每分钟、每小时、每天发送上限。
4. 单用户每天或一段时间内最大触达次数。
5. 单域名发送上限。
6. 批量邮件和客服回复是否共享额度。

### 12.2 内容限制

建议校验：

1. 邮件主题最大长度。
2. 正文最小和最大长度。
3. 附件大小、类型、数量。
4. 发件邮箱和回复邮箱格式。
5. 链接合法性和追踪参数。
6. 必要的退订入口和合规声明。

### 12.3 人群抑制

发送前应排除：

1. 退订用户。
2. 投诉用户。
3. 永久退信用户。
4. 风险用户。
5. 明确不允许触达的用户。
6. 已达到频率上限的用户。

### 12.4 幂等和重试

需要幂等的场景：

1. 任务生成邮件记录。
2. 邮件记录投递 Kafka。
3. Kafka 消费发送。
4. 外部事件回调。
5. 入站邮件生成工单。

失败重试建议区分：

1. 可重试：网络超时、临时服务不可用、临时退信、限流。
2. 不可重试：邮箱格式错误、发件权限错误、账号不存在、永久退信、投诉抑制。

## 13. 可观测性

建议至少记录以下指标：

| 指标 | 说明 |
| --- | --- |
| 任务创建数 | 按类型、状态统计 |
| 目标用户数 | 预估人数、实际生成邮件数、过滤人数 |
| 队列积压 | 批量发送队列、客服回复队列、入站队列 |
| 发送成功率 | 按通道、发件邮箱、任务统计 |
| 失败原因分布 | 发送失败、退信、拒信、限流 |
| 送达率、打开率、点击率 | 以事件回调统计，注意打开和点击有误差 |
| 投诉率、退订率 | 作为发送风控核心指标 |
| 工单响应时长 | 首次响应、最近响应、关闭时长 |
| 未分配工单数 | 用于团队容量和告警 |


## 14. 参考代码位置

以下为现有项目中可参考的代码位置，重构时可按新架构重新命名和拆分：

| 模块 | 现有参考 |
| --- | --- |
| 邮件任务后台入口 | `app/admin/controller/MailTaskController.php` |
| 邮件任务服务 | `app/service/MailTaskService.php` |
| 批量邮件生成与投递参考 | `app/service/UserService.php` |
| 批量邮件发送消费者 | `app/command/kafkaConsumer/BatchMailCommand.php` |
| 工单后台入口 | `app/admin/controller/EdmChatController.php` |
| 工单服务 | `app/service/EdmChatService.php` |
| 客服回复消费者 | `app/command/kafkaConsumer/MailSendCommand.php` |
| 表单数据消费者 | `app/command/kafkaConsumer/QaForm.php` |
| 表单创建工单 | `app/service/WebFormDataService.php` |
| 邮件事件回调 | `app/controller/AmazonEmailController.php` |
| 普通邮箱服务 | `app/admin/controller/MailboxController.php`、`app/service/MailboxService.php` |
| 邮件发送封装 | `app/service/Mail.php` |
| Microsoft 邮件服务 | `app/service/MicrosoftService.php` |
| 策略邮件命令 | `app/command/EdmDoRealTimeStrategy.php`、`app/command/EdmSendRealTimeStrategy.php`、`app/command/EdmDayCurrentTask.php` |
| 工单辅助命令 | `app/command/EdmAllocate.php`、`app/command/EdmMove.php`、`app/command/EdmDealLine.php`、`app/command/EdmTimeout.php`、`app/command/EdmWorkOrderAutoClose.php` |

