全局通知中心
聚合 13 类业务事件为站内通知(持久化 + WS 实时推送 + 外发 bark/tg/webhook),Web/移动端铃铛通知中心。
架构
后端 internal/notice/:
models.Notice(Source事件 tag /Key去重键 /Title/Body/Level(info/success/warning/danger) /Read/CreatedAt),AutoMigrate。oper.NoticeOper:CRUD +ExistsSince(去重查询) +CountUnread(铃铛角标) +Cleanup+ClearAll(一键清空)。notice.Service.Notice(ctx, Input, dedup):dedup=true 按Source+Key当天去重(周期事件);false 一次性。流程:去重 → 写库 → WS 广播 → 外发渠道。- 循环依赖解耦:
Service用Store接口(oper.NoticeOper隐式实现,不 import oper,因 oper→monitor→notice)+Broadcaster func([]byte)回调(app.go 适配ws.Hub.Broadcast,不 import ws,因 ws→oper→monitor→notice)+ 外发用notification.Manager(不用 chain.NotificationChain,避免 chain↔notice)。notice 包只 import db/models + notification。 - WS
notice消息体:{type:"notice",data:{id,source,title,body,level,created_at}}。 - 每日
notice-cleanup清 30 天前已读通知。
13 类事件 Source
| Source | 事件 | 触发点 | 去重 |
|---|---|---|---|
site_anniv | 今日站庆 | savept-refresh cron(AlertCelebration/Info) | 当天 |
new_mail | 新邮件 | messages cron(seen ID diff) | 当天 |
assess_pass | 考核完成 | assessment.assessSite(Status→COMPLETED) | 当天 |
assess_fail | 考核失败 | assessment.assessSite(Status→FAILED) | 当天 |
announce | 新公告 | runAnnouncements(RawText hash diff) | 当天 |
host_offline | 服务器掉线 | monitor Alerter(metric=offline, type=host) | 当天 |
nas_offline | NAS 掉线 | monitor Alerter(metric=offline, type=nas) | 当天 |
reseed_done | 辅种完成 | keepseed.Resolve(Status=added) | 一次性 |
dl_done | 下载完成 | ws.Broadcaster.detectDone(种子状态 diff) | 当天 |
site_offline | 站点掉线 | cookie-keepalive(!LoggedIn) | 当天 |
backup_done | 备份完成 | backup handler Export | 一次性 |
backup_import | 备份导入成功 | backup handler Import | 一次性 |
freeleech | 今日站免 | runFreeleech(Active 站) | 当天 |
API(guarded 组)
| 方法 | 路径 | 说明 |
|---|---|---|
| GET | /notices | 通知列表(?unread=true 仅未读;?limit=) |
| POST | /notices/:id/read | 标单条已读 |
| POST | /notices/read-all | 全部已读 |
| POST | /notices/clear | 一键清空全部通知(含未读) |
| DELETE | /notices/:id | 删除单条 |
去重与 diff 策略
- 周期事件(站庆/站免/公告/掉线/考核/下载完成/新邮件):
dedup=true,Source+Key当天去重,避免 cron 刷屏。 - 一次性事件(备份完成/导入、辅种完成):
dedup=false。 - 新邮件:
messages-checkcron(MESSAGES_CRON)调MessagesAll,seen message ID 集合存SystemConfig(keymail:seen:站名JSON),diff 出新 ID 通知。 - 下载完成:
ws.Broadcaster每 2s 聚合所有种子,内存map[dlID:hash]→donediff(State=completed 或 CompletedAt 非零);冷启动首轮只填充 map 不通知(primed 标记),避免重启把所有已完成种子当新通知。 - 新公告:RawText 的 sha256 存
SystemConfig(keyannounce:seen:站名),变化才通知;首次不通知。 - 考核:
assessSite在 BatchUpsert 前FindBySite读旧 Status,转换到 COMPLETED/FAILED 才通知;首次该任务无旧记录跳过。
配置
| 变量 | 默认 | 说明 |
|---|---|---|
MESSAGES_CRON | */30 * * * * | 站点私信新邮件轮询(通知中心「新邮件」来源) |
外发通道(outbound channels)
外发通道由 internal/notification/ 实现(Channel 接口 + Manager 并发扇出/错误隔离),凭据走 SystemConfig 单 key notify.settings JSON blob(NotificationSettingsProvider 热改 → Manager.Replace 即时生效,回退 env 引导默认)。三端配置入口:Web Settings→通知、移动端 通知设置 页。
共 19 个通道(Name() 即通道标识):
| 通道 | 鉴权 | 说明 |
|---|---|---|
| bark / telegram / qq / wechat / webhook / dingtalk / feishu / email | — | 原有 8 通道(Bark/TG/QQ 开放平台/微信 ClawBot/Webhook/钉钉/飞书/SMTP 邮件) |
| pushplus | token | pushplus.plus,转发微信/邮件子渠道 |
| pushdeer | pushkey(+server) | PushDeer,官方或自建 api2.pushdeer.com |
| ntfy | topic(+token) | ntfy.sh 或自建,按 topic 订阅,级别→Priority 头 |
| gotify | token(+server) | 自建 Gotify 服务 |
| apprise | apikey(+server) | Apprise API 统一转发上百终端 |
| workwechat | webhook url | 企业微信群机器人(区别于个人微信 ClawBot) |
| wxpusher | appToken(+uid/topic_id) | WxPusher 微信推送 |
| lark | webhook url | Lark 国际版飞书(与 feishu 中国版并列) |
| discord | webhook url | Discord 频道 Webhook,2000 字符截断 |
| mqtt | broker+topic(+user/pass) | MQTT 3.1.1 QoS0 发布,纯标准库自实现报文编解码(CONNECT→CONNACK→PUBLISH→DISCONNECT,不引 paho),支持 mqtt:// mqtts:// |
| serverchan | SendKey | Server酱 Turbo(sct.ftqq.com) |
秘密字段(token/key/password 等)在 ApplySettings 时空串=保留旧值(UI 不重输秘密时回填);URL 类(workwechat/lark/discord/webhook,同 dingtalk/feishu 惯例)不脱敏,空串=清空/禁用。GetConfig 返回脱敏值 + *_configured 标记。
三端入口
- Web:
MainLayouttopbar 铃铛(NotificationCenter.vue)+ 未读角标 + 下拉列表(单条已读/删除/全部已读/清空);WSnotice实时追加(useWs.notices)+noticeApi.list初始。 - Mobile:
home_shellFAB 铃铛(ValueListenableBuilder noticeUnread)+NoticePage(滑动删除/全部已读/清空);WsClient.notices流 +refreshNoticeUnread。
注入
noticeSvc 在 app.go notifMgr 后构造,注入:automation.New(+cfgOp) / monitor.NewService / savept cron 闭包 / chain.NewAssessmentChain / chain.NewKeepSeedChain / NewBackupHandler(d.Backup,d.Notice) / ws.Broadcaster.SetNotice(setter,避构造顺序)。