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

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

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

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

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

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

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

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

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

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

markdown 复制代码
业务 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。业务代码从"怎么发"里解放出来只说"发什么",运维在管理页看到每一次投递的成败与原因------这就是把"通知"当域做和当功能做的差别。

项目地址 :github.com/tx7do/go-wi... / gitee.com/tx7do/go-wi...

在线演示 :demo.admin.gowind.cloud(前端)/ api.demo.admin.gowind.cloud/docs/(后端 Swagger)

相关推荐
小小张说故事1 小时前
SHAP 可解释性入门:模型为什么这么判?Python 实战 + 4 个最常见的误读
后端·python·机器学习
喵个咪1 小时前
GoWind Admin|风行 — 开箱即用的企业级全栈中后台框架:认证与会话管理实战
后端·go
喵个咪2 小时前
GoWind Admin|风行 — 开箱即用的企业级全栈中后台框架:六类审计日志与等保合规
后端·安全·go
Sam_Deep_Thinking2 小时前
什么是CountDownLatch?
java·后端·面试·程序员
小蒜学长2 小时前
基于SpringBoot和Vue的低卡食品销售系统的设计与实现(代码+数据库+LW)
java·后端·springboot·健康管理·低卡食品销售系统
白起那么早2 小时前
idea 插件-把数据库的表画出来
数据库·后端·intellij idea
蓝宝石的傻话2 小时前
onvif-go(mickeyzzc/onvif-go v2)完整参考手册:从建工程到写出自己的 NVR 接入
开发语言·后端·golang
喵个咪2 小时前
GoWind Admin|风行 — 开箱即用的企业级全栈中后台框架:AI 模块
后端·go·ai编程
喵个咪2 小时前
GoWind Admin|风行 — 开箱即用的企业级全栈中后台框架:AK/SK 机器凭证
后端·安全·架构