☰
GoWind Admin|风行 — 开箱即用的企业级全栈中后台框架:通知域实战
2026/10/1 7:57:49 网站建设 项目流程

GoWind Admin|风行 — 开箱即用的企业级全栈中后台框架:通知域实战

大多数管理系统的"通知"长成这样:站内信是一张表、邮件发送散落在某个 service 里、Webhook 是临时加的一段 curl——三个互不相认的半成品。想回答"这条通知发了没有、为什么失败"时,只能翻日志。风行(GoWind Admin)把通知做成了一个完整的域:业务代码一个入口发通知,路由规则决定走哪个渠道,投递台账记录每一次的成败与原因。本文拆解这套统一投递体系。

一、痛点对话:两个互不相认的半个域

风行在设计通知域之前,面对的正是多数项目的现状:

  • 站内信成熟,但只能站内投递;
  • 通知渠道(SMTP 邮箱配置)能发站外,但没有调度层——谁在什么时候该发什么,没人管。

收敛后的通知域回答三个问题:

  1. 业务代码想"通知某个人"时,唯一的入口是什么;
  2. 一条通知走了哪些渠道、成功没有、失败为什么——台账在哪;
  3. 站内信与站外发送如何收敛成同一套渠道抽象,已有的收件箱不必重写。

二、统一入口:业务只依赖一个接口

业务 service(找回密码验证码、邮箱换绑验证码、未来的告警与审批……)只依赖一个Notifier接口,由SendDirect实现:

业务 service │ 只依赖 Notifier 接口 ▼ NotificationService.SendDirect ├─ 查路由:事件类型 → 渠道 + 同步/异步(sys_notification_rules) ├─ 落台账:先记 SENDING(带 request_id 幂等锚),再发送 └─ 按规则的派发方式分流: ├─ 异步:预检通过 → 任务队列入队 → 立刻返回(调用方不等待 SMTP) └─ 同步:当场发送 + 回写结论(测试邮件、站内信走这条路)

一个容易被忽略、但做对了很重要的细节:发送是系统上下文,不是操作人上下文。租户到期扫描、定时任务、审计日报发起的通知没有"操作人"可言,找回密码更是免鉴权入口——把通知发送绑在登录态上,这些场景全会哑火。

三、路由规则:事件到渠道的映射是数据,不是代码

“密码重置码走邮件异步发”“站内信事件走站内信同步发”——这些映射存在sys_notification_rules表里,管理页「通知管理 → 通知路由规则」可视化维护:


每条规则三个要素:业务事件 → 投递渠道 → 派发方式(同步/异步)+ 启用开关。空表启动时自动播种全集(找回密码码、联系人绑定码、渠道测试邮件、站内信四条),业务代码零配置即可跑通;改路由是管理页改一行的操作,不发版。

内置渠道三类:

  • 邮件(SMTP):渠道管理页维护多个 SMTP 配置(服务器/端口/加密方式/发件人),支持测试发送——测试邮件的产物就是 SMTP 报错原文,配错当场看见;
  • Webhook:出站回调,支持载荷模板;拨号期按解析后的真实 IP 拦截内网地址,SSRF 防护做在最后一跳;
  • 站内信:复用既有收件箱,不重写。

四、投递台账:每一次发送都有账可查

sys_notification_deliveries是整个域的账本:一条通知 × 一个渠道 × 一个收件人 = 一行。

设计里有几个值得抄走的决策:

  • 状态机四态:SENDING / SENT / FAILED / SKIPPED。SENDING不是永久态——常驻清扫任务把超期未结算的行定案为FAILED并写明原因,台账里不存在"永远在发送中"的僵尸行;
  • request_id 幂等锚:一次业务调用产生的多条投递(比如同时发邮件 + 站内信)用同一个请求号串起来,复合唯一索引(request_id, channel)保证重试不重复记账;
  • 失败也落渠道配置 ID:FAILED的行照样记录"当时选中的是哪个渠道配置",排障时不用猜"它到底用的哪个 SMTP";
  • 投递目标脱敏:邮箱存o***@example.com形态——台账是永久数据,敏感目标不留全量;
  • 不存正文快照:验证码类通知的正文就是 OTP 本身,快照进永久台账等于建了一张明文验证码表。台账只存related_id(按事件类型解释的业务对象 ID),要追溯走关联回跳;
  • 台账只读:没有 Update/Delete 业务方法——改一条已发生的投递等于伪造事实,只有结果回写这一个入口。

五、异步投递:预检 + 队列 + 清扫

验证码邮件要"立刻知道渠道没配没发出去",日报类通知要"发了就行不用等"——两类诉求靠规则行上的派发方式分流:

  • 异步路径:发送前先过渠道预检(配置是否可用,不拨号)→ 预检通过入任务队列,接口立刻返回 → 消费者真实投递、每次尝试记attempts;预检不过当场结台账,调用方拿到的结论与同步路径一字不差;
  • 同步路径:当场发送当场回写,attempts=1与结论写在同一条更新里。

六、试投递:发出去才算配好了

渠道配置页的「测试发送」与规则页的「测试投递」,产物就是投递台账里的一行——用系统自己渲染的样例文案(只含规则 ID 与事件名,不带任何用户数据),验证"这条规则选的渠道现在真的能通"。站内信渠道不走这个口(要有真实正文,去站内信页发),邮件渠道收件地址留空直接拒绝——测试入口收窄成"验证配置",不是"给任意邮箱发信"的口子。

结语

通知域的设计哲学与风行其他横切系统一脉相承:入口收敛成接口、决策外置成数据、过程留痕成台账、边界收口在 fail-closed。业务代码从"怎么发"里解放出来只说"发什么",运维在管理页看到每一次投递的成败与原因——这就是把"通知"当域做和当功能做的差别。

项目地址:https://github.com/tx7do/go-wind-admin / https://gitee.com/tx7do/go-wind-admin
在线演示:https://demo.admin.gowind.cloud(前端)/ https://api.demo.admin.gowind.cloud/docs/(后端 Swagger)

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询