Files
cursor/plans/Plan-b5e97267.plan.md
ray zhou 2dd9f17da9 ok
2026-06-29 14:51:55 +08:00

241 lines
11 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!-- b5e97267-b1d5-4ef9-9217-ebd91392307c -->
---
todos:
- id: "ddl-mail-tables"
content: "在 slot-notification 新增 mail_configSMTP 直字段)/ mail_template / mail_send_log 表 DDL"
status: pending
- id: "mail-module"
content: "实现 app/mail 模块Model、Entity、Cache、MailService、SmtpMail SDK"
status: pending
- id: "notification-api"
content: "新增 EmailController + MailConfigAdminController 与 route 注册"
status: pending
- id: "sdk-client"
content: "扩展 slot-lib / slot-sdk 的 MailService 客户端方法"
status: pending
- id: "console-migrate"
content: "slot-console CommonController 改调 notification废弃本地 MailService"
status: pending
- id: "admin-ui"
content: "slot-admin 后端 + slot-admin-vue 邮件配置管理页"
status: pending
- id: "data-migration"
content: "编写 mail_config 从 slot-console 配置组到表的幂等迁移脚本"
status: pending
- id: "verify"
content: "跑 verify-slot-backend.sh验证发信/验码/后台 CRUD 链路"
status: pending
isProject: false
---
# slot-notification 邮件配置表化(对齐短信)
## 现状与结论
当前两套通知渠道配置方式不一致:
| 能力 | 短信(已收口) | SMTP 邮件(未收口) |
|---|---|---|
| 配置存储 | [`slot-notification/app/sms/model/SmsConfigModel.php`](slot-notification/app/sms/model/SmsConfigModel.php) → `sms_config` 表 | [`slot-console/app/service/lib/MailService.php`](slot-console/app/service/lib/MailService.php) → `ConfigService::getGroupData('mail_config')` |
| 发送服务 | [`slot-notification/app/sms/services/SmsService.php`](slot-notification/app/sms/services/SmsService.php) | `slot-console` 本地 `MailService` + PHPMailer |
| 验证码 API | `/innerapi/sms/sendVerifyCode` | `slot-console` `CommonController::sendEmailCode` |
| 后台管理 | slot-admin → slot-lib → notification `/innerapi/admin/*` | slot-admin 通用配置页 `email_config`(另一套) |
**结论:应该建表。** 你已选择「完整对齐短信」,建议把 SMTP 邮件作为 slot-notification 的第二条外发渠道,与短信保持同一套「配置表 + 发送日志 + innerapi + 后台管理 + SDK 客户端」模式。
注意slot-notification 里现有 [`MailController`](slot-notification/app/innerapi/controller/MailController.php) / [`EmailService`](slot-notification/app/message/service/EmailService.php) 实际是**站内消息**兼容别名,不是 SMTP。新 SMTP 能力应使用独立命名空间(建议 `app/mail/*`)和独立路由前缀(建议 `/innerapi/email/*`),避免与 `/innerapi/mail/*`(站内消息)冲突。
```mermaid
flowchart LR
subgraph before [当前]
lobby1[Lobby] --> console1[slot-console CommonController]
console1 --> config1["ConfigService mail_config"]
console1 --> mail1[MailService PHPMailer]
end
subgraph after [目标]
lobby2[Lobby] --> console2[slot-console 薄代理]
console2 --> sdk[slot-lib MailService]
sdk --> notif[slot-notification EmailController]
notif --> table["mail_config 表"]
notif --> smtp[SmtpMail PHPMailer]
notif --> log["mail_send_log 表"]
end
```
---
## 表结构设计(邮件用直字段,短信保留 JSON
`s_message`(与 `sms_config` 同库)新增两张表。
### 为什么邮件不用 JSON、短信用 JSON
| 对比 | 短信 `sms_config.config` | 邮件 `mail_config` |
|---|---|---|
| 字段形态 | 各平台差异大Buka 要 appId/senderIdAnt/Chuanglan 字段不同) | SMTP 标准字段固定,截图与代码一致 |
| 后台表单 | 按 `platform` 动态展示不同表单项 | 固定 6~8 个输入框,与现有配置页一一对应 |
| 查询/校验 | 结构不统一,适合 JSON | 可直接列级校验、索引、迁移映射 |
**结论:邮件配置用直接字段更好;不必为了「和短信表长得像」而强行 JSON。**
### 当前邮件配置项(来自截图,仅 SMTP 连接)
截图中的 6 项即为 `mail_config` 配置组现有字段,与 [`MailService`](slot-console/app/service/lib/MailService.php) 读取项一致。**这就是邮件配置表应存的全部内容**
| 配置项 | 列名建议 | 说明 |
|---|---|---|
| smtp host | `smtp_host` | 如 `email-smtp.us-east-1.amazonaws.com` |
| 用户名 | `username` | SMTP 登录账号 |
| 密码 | `password` | SMTP 密码/应用专用密码 |
| 端口 | `port` | 如 `587` |
| 发件人地址 | `from_address` | 原 `from` |
| 发件人名称 | `from_name` | 如 `TOGOO` |
加密方式(`tls`/`ssl`**不单独落库**:发送时按 `port` 推断即可(`587` → STARTTLS`465` → SSL与现网行为一致。
### 邮件模板(独立概念,不属于 mail_config
验证码邮件的标题/正文来自 `email_template` 配置组(`title``binding_email`),属于**发送模板**,不是 SMTP 连接配置。
发送邮件时需要两张表配合:
```mermaid
flowchart LR
sendMail[MailService 发信] --> mailConfig["mail_config\nSMTP 连接"]
sendMail --> mailTemplate["mail_template\n标题/正文模板"]
```
### `mail_config`(仅 SMTP 连接,直字段)
| 字段 | 类型 | 说明 |
|---|---|---|
| `id` | bigint PK | 主键 |
| `platform` | varchar(32) | 渠道标识,如 `AwsSes``Zoho` |
| `status` | tinyint | 1 启用 / 2 禁用 |
| `num` | int | 排序,降序优先 |
| `smtp_host` | varchar(255) | SMTP 主机 |
| `username` | varchar(128) | SMTP 用户名 |
| `password` | varchar(512) | SMTP 密码 |
| `port` | int | SMTP 端口 |
| `from_address` | varchar(255) | 发件人邮箱 |
| `from_name` | varchar(128) | 发件人显示名 |
| `create_time` / `update_time` | datetime | 审计字段 |
### `mail_template`(邮件发送模板,独立表)
| 字段 | 类型 | 说明 |
|---|---|---|
| `id` | bigint PK | 主键 |
| `template_code` | varchar(64) | 模板编码,如 `bindingEmail``authEmail` |
| `template_name` | varchar(128) | 模板名称 |
| `title` | varchar(255) | 邮件标题模板 |
| `body` | text | 邮件正文模板,支持 `{code}` 等变量 |
| `status` | tinyint | 1 启用 / 2 禁用 |
| `create_time` / `update_time` | datetime | 审计字段 |
首期迁移:把 `email_template` 配置组中的 `title` + `binding_email` 迁入 `mail_template``template_code=authEmail` 或按业务拆分)。
**注意**`message_template` 是站内消息模板,与 SMTP 外发邮件模板是不同业务,不混用。
### `mail_send_log`(发送日志,放 `s_common`
镜像 [`sms_send_log`](slot-notification/app/sms/model/SmsSendLogModel.php)
- `unique_sn`, `email`, `platform`, `subject`, `content`, `status`, `type`, `retry_num`, `create_time`
---
## 后端实现slot-notification
### 1. 数据层
新建 `app/mail/` 模块,参照 `app/sms/`
- `MailConfigModel` / `MailSendLogModel`
- `MailConfigEntity`(后台 CRUD复用短信 Entity 分页模式)
- 在 [`initDB.php`](slot-notification/app/command/initDB.php) 补充 DDL另提供 `db/mail_config.sql` 便于手工执行
### 2. 发送层
- `composer.json` 增加 `phpmailer/phpmailer`
- `MailService`:从 `mail_config` 取 SMTP 连接,从 `mail_template` 取标题/正文 → PHPMailer 发信 → 写 `mail_send_log`
- `app/mail/services/sdk/BaseMail.php` + `SmtpMail.php`(首期只实现 SMTP结构与 `BaseSms` 一致,便于后续扩展 SendGrid 等)
- `MailCache`:验证码 Redis 缓存(镜像 `SmsCache`key 规则与 slot-console [`EmailCodeLibService`](slot-console/app/service/lib/EmailCodeLibService.php) 保持一致,避免迁移期验证码失效
### 3. API 层
新增路由([`config/route.php`](slot-notification/config/route.php)
- `POST /innerapi/email/send-verify-code`
- `POST /innerapi/email/verify-code`
- `POST /innerapi/admin/mail-config/index|save|update|destroy`
- `POST /innerapi/admin/mail-template/index|save|update|destroy`(模板 CRUD与配置分开
新建 `EmailController`(发送/校验)与 `MailConfigAdminController`(配置 CRUD**不要**复用现有站内消息 `MailController`
### 4. 渠道选择策略
首期与短信一致:按 `status=启用` + `num desc` 取第一条;预留后续 failover注释掉的短信轮询逻辑可一并参考
---
## 跨服务改造
### slot-lib / slot-sdk
在 [`slot-lib/src/services/notification/`](slot-lib/src/services/notification/) 新增 `MailService`(或 `EmailService`
- `sendVerifyCode(email, type)`
- `verifyCode(email, code, type)`
- `getMailConfigPageList` / `saveMailConfig` / `updateMailConfig` / `destroyMailConfig`
同步扩展 [`slot-sdk/src/service/notification/NotificationService.php`](slot-sdk/src/service/notification/NotificationService.php)。
### slot-console调用方改造
[`CommonController::sendEmailCode`](slot-console/app/api/controller/CommonController.php) / `verifyEmailCode`
- 改为调用 slot-lib `MailService`,不再直接依赖本地 `MailService` + `ConfigService`
- 本地 [`MailService`](slot-console/app/service/lib/MailService.php) 标记废弃或删除(确认无其他引用后)
限流、参数校验仍留在 slot-console Controller 层(与现有短信 napi 模式一致)。
### slot-admin + slot-admin-vue
镜像短信后台:
- 后端:[`slot-admin/app/game/controller/SmsController.php`](slot-admin/app/game/controller/SmsController.php) → 新建 `MailController`
- 前端:新建 `mail/config/` 页,仅 6 个 SMTP 字段 + platform/status/num另建 `mail/template/` 页管理标题/正文模板
- 菜单/权限:新增 `mailConfig` 路由与按钮权限
---
## 数据迁移
新增一次性迁移脚本(建议 `slot-notification/app/command/MigrateMailConfig.php`
1.`s_config` 读取 `mail_config` 配置组 → 写入 `mail_config`6 个 SMTP 字段)
2.`s_config` 读取 `email_template` 配置组 → 写入 `mail_template` 表(`title` + `binding_email`
3. 幂等插入(已存在则跳过)
迁移完成后,`slot-console``mail_config` / `email_template` 配置组可保留只读一段时间,确认无回退需求后再清理。
---
## 风险与边界
- **命名冲突**`/innerapi/mail/*` 继续保留给站内消息SMTP 统一走 `/innerapi/email/*`
- **验证码兼容**`MailCache` 的 Redis key 必须与 slot-console 现有 `ShareRedisKeyManagerService::emailCode()` 一致
- **模板范围**:本期只迁移验证码邮件模板(`binding_email`/`title`);充值/提现等 MQ 站内邮件模板(`message_template` 配置组)不在本次范围
- **多 SMTP failover**:表结构支持,首期实现「取第一条启用配置」即可
---
## 验收标准
1. slot-admin 可 CRUD `mail_config`,与短信配置页体验一致
2. Lobby 发/验邮箱验证码链路走 slot-notification功能与迁移前一致
3. 发送成功/失败均写入 `mail_send_log`
4. slot-console 不再读取 `ConfigService::getGroupData('mail_config')`
5. 执行 `verify-slot-backend.sh` 通过