Skip to content

架构设计

本文档详细说明 MagicPush 的架构设计,包括分层架构、渠道适配器模式和推送核心流程。


目录


1. 分层架构

1.1 整体结构

请求 → 路由层 (routes/) → 中间件层 (middleware/) → 控制器层 (controllers/) → 服务层 (services/) → 模型层 (models/)

                                                                                         SQLite 数据库

1.2 各层职责

目录职责核心原则
路由层routes/URL 到控制器的映射,参数提取不包含业务逻辑
中间件层middleware/认证、限流、校验、错误处理洋葱模型,按序执行
控制器层controllers/接收请求、调用 Service、返回响应每个方法都用 try/catch
服务层services/核心业务逻辑编排通过 throw Error 报告异常
模型层models/SQL 封装(CRUD)、数据持久化同步调用(better-sqlite3)

1.3 数据流转示例

以「通过 Token 推送消息」为例:

POST /api/push/{token}


[路由层] push.routes.js        提取 token 参数


[中间件] 无需认证              (Token 本身就是凭证)


[控制器] push.controller.js     try { result = await PushService.pushByToken(...) }
    │                            catch (e) { return ResponseUtil.serverError(...) }

[服务层] push.service.js        1. Token 校验 → 2. 关键词过滤 → 3. DND 检查
                                4. 获取关联渠道 → 5. 遍历发送 → 6. 记录日志


[模型层] endpoint.model.js      findByToken() / updateLastUsed()
         pushLog.model.js       create() / updateStatus()


[数据库] SQLite                 INSERT / UPDATE / SELECT

2. 渠道适配器模式(策略模式)

这是本项目最核心的设计模式。每个通知渠道是一个独立的适配器类,统一继承自 BaseChannel 基类,实现开闭原则——对扩展开放、对修改关闭。

2.1 类层次结构

BaseChannel (抽象基类) — base.channel.js

  ├── send(message)              -- 发送消息(必须实现)
  ├── validate(config)           -- 验证配置(必须实现)
  ├── test()                     -- 测试连接(必须实现)
  ├── getName()                  -- 渠道名称(静态方法,必须实现)
  ├── getDescription()           -- 渠道描述(静态方法,可选覆盖)
  ├── getConfigFields()          -- 动态表单字段定义(静态方法,必须实现)
  └── createProxyAgent(proxyUrl) -- 创建代理 Agent(已内置实现)

        ├── WechatclawbotChannel   (微信龙虾机器人)
        ├── YuanbaobotChannel     (元宝 Bot)
        ├── WecomChannel          (企业微信群机器人)
        ├── TelegramChannel       (Telegram Bot)
        ├── PushPlusChannel       (PushPlus 推加)
        ├── WxPusherChannel       (WxPusher 微信推送)
        ├── FeishuChannel         (飞书)
        ├── DingtalkChannel       (钉钉)
        ├── WebhookChannel        (通用 Webhook)
        ├── WechatOfficialChannel (微信公众号模板消息)
        ├── ServerChanChannel     (Server酱)
        ├── SmtpChannel           (SMTP 邮件)
        ├── GotifyChannel         (Gotify 自托管推送)
        ├── MeowChannel           (喵喵推送)
        ├── WecomappChannel       (企业微信应用消息)
        ├── BarkChannel           (Bark iOS 推送)
        ├── PushMeChannel         (PushMe 推推)
        ├── XizhiChannel          (息知)
        └── QqbotChannel          (QQ 机器人 — 开发中未启用)

2.2 BaseChannel 接口定义

javascript
class BaseChannel {
  constructor(config)

  // ========== 必须实现的抽象方法 ==========

  // 发送消息
  // @param {Object} message - { title, content, type(text/markdown/html), url }
  // @returns {Promise<Object>} 发送结果(会被记录到 push_logs.response)
  async send(message)

  // 验证渠道配置是否合法
  // @param {Object} config - 用户填写的配置值
  // @returns {Object} { valid: boolean, message: string }
  validate(config)

  // 测试渠道连通性
  // @returns {Promise<Object>} { success: boolean, message: string }
  async test()

  // ========== 必须实现的静态方法 ==========

  // 渠道的唯一类型标识(用于数据库存储和工厂查找)
  // @returns {string} 如 'telegram', 'feishu' 等(小写英文)
  static getName()

  // 获取配置字段的动态表单定义(前端根据此渲染配置表单)
  // @returns {Array<Object>} 字段定义数组
  static getConfigFields()

  // ========== 可选覆写的方法 ==========

  // 渠道描述文案(显示在前端渠道选择列表)
  static getDescription()

  // 已内置:代理支持(HTTP/SOCKS5)
  createProxyAgent(proxyUrl)

2.3 设计优势

特点说明
前端零改动配置字段通过 getConfigFields() 声明,前端自动动态渲染表单
数据库零改动渠道配置以 JSON 格式存储在 channels 表的 config 字段中
统一接口所有渠道实现相同的 send / validate / test 方法
即插即用只需创建文件并注册,无需修改其他模块
易测试每个渠道可独立进行单元测试

3. 推送核心流程

3.1 完整流程图

外部推送请求


┌─────────────────────┐
│ Token 校验 / 认证校验  │  ← pushByToken / pushByEndpoint / pushByChannel 三种入口
└──────────┬──────────┘

┌─────────────────────┐
│ 关键词过滤检查        │  ← KeywordFilterService(黑名单/白名单模式)
│  命中则拒绝并返回错误  │
└──────────┬──────────┘

┌─────────────────────┐
│ 更新接口最后使用时间    │  ← EndpointModel.updateLastUsed()
└──────────┬──────────┘

┌─────────────────────┐
│ 获取关联的所有渠道     │  ← EndpointModel.getChannels()
└──────────┬──────────┘

    ┌──────┴──────┐
    │ 遍历每个渠道  │
    └──────┬──────┘

┌─────────────────────┐
│ 免打扰(DND)检查       │  ← DoNotDisturbService.shouldMute()
│  全局开关 + 每接口     │     最多5个时间段配置
│  独立的时间段配置       │     匹配时记录 skipped_dnd 日志,跳过推送
└──────────┬──────────┘

┌─────────────────────┐
│ 创建 push_log        │  ← status = 'pending'
└──────────┬──────────┘

┌─────────────────────┐
│ 获取渠道适配器        │  ← getChannelAdapter(type, config, channelId)
│ 调用 adapter.send()  │
└──────────┬──────────┘

    ┌──────┴──────┐
    │ 成功?         │
    └──┬────────┬─┘
       ▼        ▼
   status=   status=
   success   failed
       │        │
       └───┬────┘

   汇总结果返回调用方
   { success, total, successCount, failedCount, results[] }

3.2 三种推送入口

入口方式路径认证方式场景
Token 推送POST /api/push/:tokenToken 鉴权外部系统/脚本调用
接口ID推送POST /api/push/by-endpoint/:idJWT 认证前端手动触发
渠道ID推送POST /api/push/by-channel/:idJWT 认证指定渠道调试

4. 中间件体系

采用 Express 洋葱模型的中间件链:

4.1 全局中间件(app.js 级别)

中间件功能
CORS跨域访问控制
express.json()JSON 请求体解析(limit: 10mb)
errorMiddleware全局错误捕获(404/500 兜底)

4.2 路由级中间件

中间件文件功能
auth middlewareauth.middleware.jsJWT access token 校验,解析用户身份
rate limit middlewarerateLimit.middleware.js三层限流:全局/IP级/健康检查
validator middlewarevalidator.middleware.js基于 express-validator 的请求参数校验规则

4.3 执行顺序图

请求进入

  ├─→ CORS 中间件
  ├─→ express.json() 解析

  ├─→ 路由匹配
  │     │
  │     ├─→ auth middleware(如需要认证)
  │     ├─→ rate limit middleware
  │     ├─→ validator middleware(如有校验规则)
  │     │
  │     └─→ Controller 处理

  └─→ errorMiddleware(未捕获的异常兜底)

4.4 限流层级

层级默认限制说明
全局限流100 req/min所有请求的全局上限
IP 级限流60 req/min按 IP 地址限制
健康检查30 req/min/api/health 独立限流

管理员可通过 API 动态调整限额,无需重启服务。


5. 服务层职责划分

服务文件职责协作对象
push.service.js核心:推送调度引擎,编排 DND→创建日志→遍历渠道→发送→更新状态ChannelService, EndpointModel, DoNotDisturbService
channel.service.js渠道的增删改查逻辑,配置的序列化/反序列化ChannelModel
endpoint.service.js接口管理,渠道绑定/解绑、Token 管理EndpointModel, ChannelModel
inbound.service.js入站 Webhook 接收后,根据模板提取 title/content 并转发JsonPathUtil, PushService
keywordFilter.service.js关键词过滤:黑名单(命中拦截)和白名单(命中放行)-
doNotDisturb.service.js免打扰判断:最多 5 个时间段,支持跨天(如 23:00-06:00)-
rateLimitConfig.service.js限流额度的动态读写(仅管理员可操作)SystemSettingsModel
auth.service.js登录/注册/刷新 token 的业务逻辑UserModel, TokenUtil
log.service.js推送记录的多条件查询(分页、筛选、排序)PushLogModel

基于 MIT 许可证开源