Push & Message:推送编排与消息历史

2026年1月25日
2 分钟阅读
作者:ixNieStudio

Push & Message:推送编排与消息历史

推送系统最容易被误解成“调用一次微信 API 就结束”。

但如果你真的把它当成“发一下消息”,你会很快遇到三个现实:

  • 目标用户可能不止一个(subscribe 模式)
  • 外部 API 会失败,而且失败原因各不相同
  • 你需要能回答“刚才到底发生了什么”(否则排障会很痛苦)

因此 push.service 的核心价值不是“发消息”,而是编排可观测

推送核心编排在:

  • node-functions/services/push.service.ts

输入极简,内部复杂:这是编排服务该有的样子

push 的输入非常简单:

  • appKey
  • { title, desp }

但内部会把这个极简输入翻译成一条完整的执行链路。

一张流程图:push.service 做了哪些决定

flowchart TD A["/send 接口
收到 appKey + message/"] --> B[生成 pushId] B --> C[按 appKey 找 App] C --> D[列出 App 绑定的 OpenIDs] D --> E[读取 Channel 配置] E --> F{pushMode?} F -->|single| G[取第一个 OpenID] F -->"|"subscribe"|" H[取全部 OpenID] G --> I{messageType?} H --> I I -->"|"template"|" J[sendTemplateMessage] I -->"|"normal"| K[sendCustomMessage] J --> L[汇总 DeliveryResult] K --> L L --> M[写入 Message 历史] M --> N[返回 PushResult]

你会看到这里的“边界”很清楚:

  • push.service 负责决策与编排
  • 平台 service(wechat/enterprise)负责具体发送与 token 维护
  • message.service 负责历史落库

为什么一定要落 Message 历史

推送这类系统,最重要的不是“成功率”,而是“可解释性”。

当你收到用户反馈:

你这个推送没收到。

你必须能回答:

  • 是没有目标用户(没绑定)?
  • 是 token 失效(自愈失败)?
  • 是用户未关注(不可自愈)?
  • 是网络问题(可重试)?

因此 message history 不是“日志”,它是产品能力的一部分。

DeliveryResult:失败语义必须结构化

推送是“批处理投递”,所以返回结构更应该是:

  • 每个接收者的 success/error/msgId

而不是一个 boolean。

这带来的直接收益:

  • UI 可以展示“谁失败了、为什么失败”
  • 后续可以做“只重试失败的那部分”

失败分类:可自愈 vs 不可自愈

项目里典型失败分类:

  • token 失效(可强制刷新并重试一次)
  • 用户未关注(微信错误码 45015 等,不可自愈)
  • 网络请求失败(可重试,但要避免重放风暴)

建议 UI 层把这些分类清晰呈现,否则用户会把所有失败都归因到“系统不稳定”。

启发:编排服务的价值在“把复杂留在内部”

  • 对外:输入越简单越好(title/desp 足够)
  • 对内:需要结构化结果、历史落库、明确责任边界
  • 对未来:有了 message history,你就拥有了演进空间(限流、重试队列、告警)