View on GitHub

WePush

专注批量推送的小而美的工具,目前支持:模板消息-公众号、模板消息-小程序、微信客服消息、微信企业号/企业微信消息、阿里云短信、阿里大于模板短信 、腾讯云短信、云片网短信、E-Mail、HTTP请求、钉钉、华为云短信、百度云短信、又拍云短信、七牛云短信

WePush Next 架构与概要设计

1. 文档目的

本文档描述 WePush Next 的目标架构和概要设计,作为后续模块创建、接口设计、技术选型、任务拆分和验收的共同基线。

WePush Next 是独立于当前 Classic 客户端的新产品线。本文档不要求 Classic 按照本架构改造,也不限制 Classic 继续独立增加消息类型和功能。

2. 建设目标

WePush Next 的目标是从单机桌面推送工具发展为同时支持本地使用、用户自建服务化部署和分布式执行的消息推送产品。产品始终由用户自行下载、安装和运维,不建设官方公共 SaaS 或承载用户业务数据的集中平台。

主要目标如下:

3. 非目标

以下项目不属于当前架构目标:

长期产品边界及其变更规则以《产品目标、边界与路线图》为准。

4. 架构原则

4.1 明确执行位置

所有推送动作最终由 Agent 中的 Core Engine 执行。Service 负责任务定义、调度和控制,不直接包含消息渠道的发送业务逻辑。

单机模式可以将 Agent 嵌入 Service 进程,但逻辑边界保持不变。

4.2 依赖指向稳定抽象

Core Engine 依赖 Core API 和 Provider SPI,不依赖 Service、Agent、UI、MyBatis、Spring 或具体 Provider。

4.3 配置和运行快照分离

账号、消息、受众和任务定义可以被修改;一次 Run 启动后必须形成不可变执行快照,避免运行过程中读取到被修改的配置。

4.4 Schema 驱动扩展

Provider 使用 JSON Schema 描述账号、消息和受众配置,UI、API 校验和 SDK 辅助能力围绕同一份 Schema 工作。

4.5 事件驱动运行状态

Core 不直接更新 UI。进度、日志、告警和状态变化都以结构化 Run Event 输出,由 Agent 和 Service 负责传输与持久化。

4.6 默认安全

本地模式默认只监听回环地址;远程模式必须启用认证。Secret 不出现在日志、普通查询响应和运行事件中。

4.7 渐进式分布

先完成 Service 内嵌 Agent 的单机闭环,再引入远程 Agent。分布式能力建立在稳定的运行状态机和租约模型之上。

5. 系统上下文

flowchart LR
    Operator[操作人员] --> WebUI[WebUI]
    Operator --> Desktop[Desktop UI]
    JavaApp[Java 应用] --> SDK[Remote Java SDK]

    WebUI --> API[Service API]
    Desktop --> API
    SDK --> API

    API --> Service[WePush Service<br/>控制面]
    Service --> Database[(业务数据库)]
    Service --> ArtifactStore[(制品存储)]
    Service --> Agent[WePush Agent<br/>执行面]
    Agent --> Engine[Core Engine]
    Engine --> Providers[Provider 插件]
    Providers --> Channels[微信 / 短信 / 邮件 / HTTP 等]

6. 总体组件

组件 核心职责 明确不负责
Core API 执行命令、状态、事件、结果和策略的稳定模型 数据库、HTTP、UI、Provider 实现
Core Engine 执行流水线、并发、限流、重试、取消、结果汇总 调度、用户权限、REST API
Provider SPI Provider 生命周期、配置 Schema、发送和能力接口 具体渠道实现
Provider 渠道协议适配、认证、请求构建和响应解释 Service CRUD、UI 控件
Agent 注册、心跳、任务租约、Core 执行、事件上报 面向用户的业务管理 API
Service 配置管理、调度、Run 编排、Agent 管理、持久化、认证 直接调用渠道发送消息
Remote Java SDK Service API 的类型安全客户端 本地执行引擎
Embedded Java SDK 在 Java 进程内使用 Core 和 Provider 远程 Service 管理能力
WebUI 浏览器管理、配置、监控和 API 调试 直接访问数据库或 Core
Desktop UI 桌面入口、本地 Service 管理、远程 Service 连接 直接执行 Provider
Distribution 三平台安装、服务注册、升级和卸载 业务逻辑

7. 建议目录与模块结构

next/
├── pom.xml
├── docs/
│   ├── architecture-and-high-level-design.md
│   └── adr/
├── platform/
│   └── wepush-bom/
├── core/
│   ├── core-api/
│   ├── provider-spi/
│   └── engine/
├── providers/
│   ├── provider-http/
│   ├── provider-standard/
│   └── ...
├── agent/
│   ├── agent-protocol/
│   ├── agent-runtime/
│   └── agent-app/
├── service/
│   ├── service-api/
│   ├── service-domain/
│   ├── service-application/
│   ├── service-infrastructure/
│   └── service-app/
├── sdk/
│   ├── sdk-java/
│   └── embedded-java/
├── ui/
│   ├── web/
│   └── desktop/
├── distributions/
│   ├── linux/
│   ├── windows/
│   └── macos/
└── tests/
    ├── architecture-tests/
    ├── contract-tests/
    ├── integration-tests/
    └── end-to-end-tests/

模块可以随实现进展合并或拆细,但组件依赖方向不得被破坏。

8. 模块依赖规则

flowchart BT
    CoreAPI[core-api]
    SPI[provider-spi] --> CoreAPI
    Engine[engine] --> CoreAPI
    Engine --> SPI
    Provider[providers/*] --> SPI
    AgentRuntime[agent-runtime] --> Engine
    AgentApp[agent-app] --> AgentRuntime
    AgentApp --> Provider
    ServiceDomain[service-domain] --> CoreAPI
    ServiceApp[service-application] --> ServiceDomain
    ServiceInfra[service-infrastructure] --> ServiceApp
    ServiceBoot[service-app] --> ServiceInfra
    ServiceBoot --> AgentRuntime
    ServiceBoot --> Provider
    SDK[sdk-java] --> ServiceAPI[service-api]
    Embedded[embedded-java] --> Engine

强制规则:

9. 部署形态

9.1 Standalone 单机模式

flowchart LR
    UI[WebUI / Desktop UI] --> Service[Service]
    subgraph Process[单个 Service 进程]
        Service --> EmbeddedAgent[Embedded Agent]
        EmbeddedAgent --> Engine[Core Engine]
    end
    Service --> SQLite[(SQLite)]
    Service --> LocalFiles[(本地制品目录)]

适用个人电脑、开发环境和轻量服务器。Service、调度器和内嵌 Agent 在同一进程内运行,使用 SQLite 和本地文件系统。

9.2 Server + Remote Agent 模式

flowchart TB
    UI[WebUI / SDK] --> LB[HTTP/2 负载均衡器]
    Agent1[Agent A] --> LB
    Agent2[Agent B] --> LB
    Agent3[Agent C] --> LB
    LB --> Service1[Service A]
    LB --> Service2[Service B]
    Service1 --> PostgreSQL[(PostgreSQL 18 HA)]
    Service2 --> PostgreSQL
    Service1 --> ObjectStore[(S3-compatible Artifact Store)]
    Service2 --> ObjectStore
    Agent1 --> Channels[外部渠道]
    Agent2 --> Channels
    Agent3 --> Channels

远程 Agent 主动通过负载均衡器建立出站 gRPC 双向流,便于跨防火墙部署。正式 Server HA 至少包含两个无状态 Service 实例;PostgreSQL 是业务和协调状态的事实源,对象制品不得依赖某个 Service 的本地磁盘。具体约束见 ADR-0006

9.3 Embedded 模式

业务 Java 应用通过 embedded-java 直接创建 Engine,并显式注册所需 Provider。该模式不依赖 Service,但也不自动获得 Service 的用户管理、调度、持久化和 WebUI 能力。

10. Core Engine 概要设计

10.1 核心输入

Engine 接收不可变的 RunExecutionSpec,主要包含:

Engine 不接受数据库主键后自行查询数据库,所有执行所需信息由调用方准备并显式传入。

10.2 执行流水线

flowchart LR
    Validate[校验执行快照] --> Prepare[初始化 Provider]
    Prepare --> Read[流式读取 Recipient]
    Read --> Render[渲染消息]
    Render --> Limit[并发与限流]
    Limit --> Send[Provider 发送]
    Send --> Classify[结果分类]
    Classify --> Retry{是否可重试}
    Retry -- 是 --> Backoff[退避等待]
    Backoff --> Send
    Retry -- 否 --> Persist[写入结果流]
    Persist --> Event[发布进度事件]

10.3 执行策略

Java 21 虚拟线程可以作为 I/O 型发送的默认执行单元,但必须通过并发闸门和限流器限制实际外部请求数量,不能把虚拟线程数量直接等同于渠道并发能力。

10.4 事件接口

Core 通过 RunEventSink 输出结构化事件,例如:

高频进度在 Agent 内聚合后批量上报,避免每条消息产生一次数据库写入。

10.5 结果与内存模型

11. Provider SPI 概要设计

11.1 Provider 能力

每个 Provider 至少暴露:

11.2 Schema 示例

{
  "providerId": "wepush.http",
  "version": "1.0.0",
  "capabilities": ["preview", "dry-run", "response-body"],
  "accountSchema": {"$ref": "schemas/account.json"},
  "messageSchema": {"$ref": "schemas/message.json"},
  "uiSchema": {"$ref": "schemas/ui.json"}
}

敏感字段通过 writeOnlyx-wepush-secret 标识。UI 只显示“已配置”状态,Service 查询接口不得返回原值。

11.3 Provider 生命周期

Provider 实例应按账号和执行上下文创建,禁止使用可变全局单例保存 Token、连接和运行进度。连接池可以共享,但必须具有明确的关闭和隔离策略。

外部 Provider 使用 PF4J 3.15.x 发现,每个插件使用独立 ClassLoader 隔离私有厂商 SDK。正式环境仅装载清单与 Ed25519 签名均通过验证的版本。更新采用版本化目录、Agent Drain、重启、验证和激活流程,多 Agent 场景滚动执行;不在 JVM 内热替换正在使用的 Provider。ClassLoader 隔离不是恶意代码沙箱;如果用户自建安全场景需要运行非受信插件,必须单独评审独立进程 Runner,当前不将其列为既定路线图。详见 ADR-0005

12. Agent 概要设计

12.1 职责

12.2 租约与恢复

sequenceDiagram
    participant A as Agent
    participant S as Service
    A->>S: Connect + Hello(gRPC 双向流)
    S-->>A: Welcome + 协议窗口
    loop 心跳/调度
        A->>S: Heartbeat(capacity, providers)
        S-->>A: LeaseOffer + executionSpecRef
        A->>S: LeaseAck(epoch, fencingToken)
    end
    A->>A: Core 执行
    loop 运行期间
        A->>S: Heartbeat + EventBatch
        S-->>A: EventAck + RunCommand
    end
    A->>S: complete(summary, artifacts)

12.3 Agent 与 Service 协议

远程 Agent 的长期控制协议固定为 HTTP/2 上的 gRPC 双向流,契约使用 Protobuf。连接承载 Hello、心跳、能力、Lease、命令、事件确认和终态;每个方向具有单调 Sequence,Lease 仍使用 Epoch 和 Fencing Token。

Enrollment 和凭据轮换使用 HTTPS REST;WebUI、Desktop UI 和远程 SDK 的 Run 实时事件使用 SSE;大体积 Artifact 通过短期签名 HTTPS URL 传输。正式远程 Agent 不发布长轮询过渡协议,也不默认使用 WebSocket。详见 ADR-0004

13. Service 概要设计

13.1 分层

Service 基线固定为 Java 21 和 Spring Boot 4.1.x,初始实现版本为 4.1.1;数据库迁移使用显式版本化迁移工具,依赖版本由 BOM 统一管理。Spring 只存在于 Service App、Web 和 Infrastructure 层,不进入 Core、Provider SPI、Agent Runtime、Remote Java SDK 或 Embedded SDK。详见 ADR-0002

13.2 主要领域对象

对象 说明
ProviderDescriptor 已安装 Provider、版本、能力和 Schema
Account 渠道账号元数据及 Secret 引用
MessageTemplate 消息模板及版本
Audience 受众定义
AudienceSnapshot 一次物化后的不可变受众版本
JobDefinition 账号、消息、受众和执行策略的组合
Schedule Cron、时区、启停和错过执行策略
Run 一次执行实例和状态摘要
RunSnapshot Run 启动时冻结的完整执行配置
Agent 执行节点身份、能力和状态
AgentLease Agent 对 Run 的限时所有权
RunEvent 状态、进度、日志和控制事件
Artifact 输入快照、结果、日志等大对象引用

13.3 Run 状态机

stateDiagram-v2
    [*] --> PENDING
    PENDING --> LEASED
    LEASED --> RUNNING
    LEASED --> PENDING: 租约未确认
    RUNNING --> PAUSED
    PAUSED --> RUNNING
    RUNNING --> CANCELLING
    PAUSED --> CANCELLING
    CANCELLING --> CANCELLED
    RUNNING --> SUCCEEDED
    RUNNING --> PARTIAL
    RUNNING --> FAILED
    RUNNING --> LOST: Agent 失联
    LOST --> RECOVERING
    RECOVERING --> PENDING: 允许重新执行
    LOST --> FAILED: 无法安全恢复

所有状态变更由应用服务执行并校验前置状态,Controller、UI 和 Agent 不能直接修改数据库状态字段。

13.4 调度

14. 对外 API 设计

14.1 基本约定

14.2 主要资源

GET    /api/v1/providers
GET    /api/v1/providers/{providerId}/schemas

POST   /api/v1/workspaces/{workspaceId}/accounts
GET    /api/v1/workspaces/{workspaceId}/accounts
PATCH  /api/v1/workspaces/{workspaceId}/accounts/{id}
POST   /api/v1/workspaces/{workspaceId}/accounts/{id}/connection-test

POST   /api/v1/workspaces/{workspaceId}/messages
PATCH  /api/v1/workspaces/{workspaceId}/messages/{id}
GET    /api/v1/workspaces/{workspaceId}/messages/{id}/revisions
POST   /api/v1/workspaces/{workspaceId}/audiences
PATCH  /api/v1/workspaces/{workspaceId}/audiences/{id}
POST   /api/v1/workspaces/{workspaceId}/audience-imports
POST   /api/v1/workspaces/{workspaceId}/audience-imports/{id}/commit

POST   /api/v1/workspaces/{workspaceId}/jobs
PATCH  /api/v1/workspaces/{workspaceId}/jobs/{id}
POST   /api/v1/workspaces/{workspaceId}/jobs/{id}/run-confirmation
POST   /api/v1/workspaces/{workspaceId}/jobs/{id}/runs
POST   /api/v1/workspaces/{workspaceId}/schedules

GET    /api/v1/workspaces/{workspaceId}/overview
GET    /api/v1/workspaces/{workspaceId}/runs/{id}
POST   /api/v1/workspaces/{workspaceId}/runs/{id}/retry-confirmation
POST   /api/v1/workspaces/{workspaceId}/runs/{id}/retries
POST   /api/v1/workspaces/{workspaceId}/runs/{id}/commands/cancel
POST   /api/v1/workspaces/{workspaceId}/runs/{id}/commands/pause
POST   /api/v1/workspaces/{workspaceId}/runs/{id}/commands/resume
POST   /api/v1/workspaces/{workspaceId}/runs/{id}/commands/concurrency
GET    /api/v1/workspaces/{workspaceId}/runs/{id}/events
GET    /api/v1/workspaces/{workspaceId}/runs/{id}/artifacts

14.3 实时事件

运行监控首期采用 Server-Sent Events。客户端可以携带事件游标或 Last-Event-ID 断线续传;历史事件仍可通过普通分页 API 查询。

命令通过 REST 提交,事件通过 SSE 下发。浏览器端不默认引入 WebSocket。

14.4 交互式 API 文档

Service 提供:

生产环境中,API 调试页面必须经过权限控制。危险操作需要二次确认,Secret 不得写入浏览器持久化存储或出现在生成的示例中。

15. Remote Java SDK 设计

15.1 远程 SDK

sdk-java 面向 Service API:

15.2 Embedded SDK

embedded-java 面向本地进程内执行:

16. WebUI 概要设计

主要功能区域:

动态表单由 Provider Schema 驱动。JSON Schema 负责数据类型和校验,UI Schema 负责分组、顺序、控件提示和 WePush 扩展控件。

前端固定采用 TypeScript 6.0.x、Vite 8.1.x 和 React 19.2.x,以 Node.js 24 LTS、pnpm 和单一 Lockfile 管理工程。样式采用 Tailwind CSS;shadcn/ui 只在能降低复杂组件成本时按需引入,复制进仓库的组件源码由项目自行维护。WebUI 是纯 SPA,正式运行不依赖 Node.js 服务端。详见 ADR-0002

17. Desktop UI 概要设计

Desktop UI 是 Service API 的薄客户端,不直接依赖 Core 或数据库。

目标能力:

Desktop 外壳固定为 Electron 43.x,并复用 WebUI 的 React 页面、Schema Renderer、API Client 和设计 Token。Main、Preload、Renderer 严格分层;Renderer 禁用 Node.js 集成,启用 Context Isolation 和 Sandbox,只通过固定白名单 IPC 使用本机 Service 状态/启停/日志/诊断、签名插件生命周期和 Token safeStorage,不提供任意命令入口。浏览器 Token 只保存在标签页会话,Desktop 在系统安全后端不可用时拒绝弱持久化。该选择不改变 Desktop 只能通过 Service API 访问业务能力的边界。详见 ADR-0002

18. 数据与存储设计

18.1 数据库

18.2 Artifact Store

Artifact Store 保存:

Standalone 默认使用 LocalFileArtifactStore;Server 默认使用受控 S3-compatible API 的 S3ArtifactStore。Agent 使用 Service 签发的短期 Presigned URL 直传,数据库保存元数据、SHA-256、大小、状态和对象键。对象键按 Workspace 分区;保留期由 Service 清理任务执行,对象存储 Lifecycle 只作兜底。详见 ADR-0007

18.3 Secret Store

密钥轮换默认只重包裹 DEK,不重写业务密文。详见 ADR-0003

19. 安全设计

Workspace 逻辑隔离进入正式 Server 产品范围;Standalone 自动使用隐藏的 Default Workspace。Account、Secret、Message、Audience、Job、Schedule、Run、Artifact、Agent Pool 和 API Token 均必须归属 Workspace,所有 Repository、唯一索引、缓存键和授权检查显式携带 workspaceId。Workspace 只服务于同一用户自建实例内的团队和权限分组,不演进为公共 SaaS 租户;计费、自助注册和恶意租户物理隔离属于长期非目标。详见 ADR-0008

20. 可观测性

Service 和 Agent 应提供:

监控接口默认不公开到非受信网络。

21. 交付与跨平台安装

21.1 Service 制品

21.2 独立标识

Next 必须使用独立于 Classic 的:

安装、升级和卸载不得读取、覆盖或删除 Classic 的文件。

22. 一致性与投递语义

WePush Next 对外声明 At Least Once 为基础投递语义。

当外部渠道已经接受请求,但 Agent 在持久化结果前崩溃时,系统无法可靠判断是否已发送。此类消息应进入 UNKNOWN,由用户或策略决定是否重试。只有 Provider 原生支持幂等键时,才可以提供更强的去重保证。

Run 的状态摘要、事件和结果制品允许短暂最终一致,但终态必须经过校验,确保计数与已持久化结果可追溯。

23. 测试策略

测试类型 重点
单元测试 状态机、模板、重试、限流、结果分类
架构测试 禁止依赖、包边界、Core 纯净性
Provider 契约测试 Schema、配置校验、错误分类、Dry Run
API 契约测试 OpenAPI、错误模型、兼容性和 SDK 生成
集成测试 SQLite、PostgreSQL 18、S3-compatible Artifact、Secret、SSE、Agent gRPC
故障测试 Agent 失联、Service 重启、租约到期、重复上报
性能测试 大受众流式处理、并发、内存上限和事件聚合
E2E 测试 WebUI/SDK 创建任务到结果下载完整链路
安装测试 Linux、Windows、macOS 安装、启动、升级和卸载

发布流水线必须先完成测试和契约校验,再生成平台安装包。安装包构建不得通过跳过测试来代替独立的测试阶段。

24. Classic 数据迁移

迁移是可选辅助能力,不是两个产品线的运行时连接。

25. 版本与兼容策略

26. 首个纵向里程碑

第一阶段不追求创建所有 Provider 和所有 UI 页面,而是完成 HTTP Provider 的完整闭环:

  1. HTTP Provider 提供账号和消息 Schema。
  2. Core Engine 能流式执行、限流、取消并输出事件。
  3. Embedded Agent 能领取并执行 Run。
  4. Service 能管理账号、消息、受众、任务和 Run。
  5. Remote Java SDK 能创建任务、启动 Run 和订阅事件。
  6. WebUI 能动态配置 HTTP Provider、启动 Run 并查看实时结果。
  7. Standalone 安装包能在至少一个平台完成安装和重启恢复。

该纵向链路已完成;0.1.0-alpha.4 进一步交付 SMTP、三类群机器人、阿里云短信、微信公众号、小程序和企业微信应用消息,详见内置 Provider 指南

27. 阶段规划

阶段 A:工程和契约基线

阶段 B:单机运行闭环

阶段 C:产品化单机版本

阶段 D:服务器与分布式执行

阶段 E:Provider 扩展

截至 2026-08-23,阶段 A–D 的 0.1.0 架构基线已经实现并进入持续验证;阶段 E 是按业务优先级持续增加消息类型的长期产品迭代,不阻塞当前 Next 目标架构成立。实现证据见 实现状态,安装、HA、升级和恢复见 部署与运维

28. 已接受的架构决策

这些决策是 Next 初始实现的正式基线。公共 SaaS、计费订阅、外部 Vault/云 KMS/Secret Manager、恶意租户物理隔离和跨地域控制面属于长期非目标,不得通过临时代码或普通功能 ADR 隐式引入。非受信 Provider 的进程级隔离只有在明确服务于用户自建安全场景时才可以单独评审,且不得改变产品定位。

29. 架构验收条件

以下条件用于判断首个架构闭环是否成立:

30. 文档维护

本文档描述当前目标架构,不替代详细设计、API 契约和 ADR。