Channel 领域:渠道配置与校验(微信/企业微信)
Channel 是这个项目里“最像外部世界”的一个对象:它承载了凭证、权限、错误码、网络不稳定……
如果说 KV/服务层的复杂度是你自己能掌控的,那 Channel 带来的复杂度是你必须“消化掉”的。
这篇文章想讲的不是“它有哪些字段”,而是:
- 为什么 Channel 是推送系统的地基?
- 为什么必须把微信/企业微信的差异隔离在 service 层?
- 为什么要把 token 维护做成一种“可观测状态”,而不是“失败了再说”?
Channel 的角色:把“平台差异”挡在业务外面
在业务模型里:
- Channel 描述“通过哪个平台发送消息”(微信测试号 / 企业微信等)
- 一个 Channel 下可以挂多个 App(不同 AppKey、不同推送策略)
从架构角度看,它扮演的是“适配器”:
- 业务层只关心“我要把消息送出去”
- 平台层负责“怎么拿 token、怎么发、失败怎么解释”
关键约束:渠道是共享资源,不是一次性配置
因为多个 App 可能共用同一个 Channel,所以它必须满足一些约束:
- 渠道被 App 引用时,禁止删除(否则会产生悬挂引用)
- 渠道配置变更需要触发:
- token 状态更新
- API 可用性校验
对应逻辑通常集中在:
node-functions/services/channel.service.ts
为什么微信/企业微信要拆成两套 service
项目里把两类平台拆成两套 service:
wechat.service.tsenterprise-wechat.service.ts
这不是“代码洁癖”,而是为了避免一个常见的工程灾难:
所有平台差异都堆在
push.service里,用 if/else 写到最后没人敢改。
两者差异很实际:
- token 获取接口不同
- 消息发送接口不同(企业微信支持 textcard 等)
- 用户体系不同(OpenID vs UserID/部门)
- 错误码集合完全不同
拆分后的收益是可量化的:
- 错误码映射集中、可维护
- token 缓存策略清晰
- 更容易写单元测试(平台服务可以被单独验证)
一张图:服务层怎么分工
flowchart TB
P["push.service
编排"] -->|send message| WS[wechat.service] P -->"|"send message"|" EWS[enterprise-wechat.service] WS -->"|"get/put token status"| KV["(configKV)"] EWS -->|get/put token status| KV WS -->"|"HTTPS"|" WXAPI[WeChat API] EWS -->"|"HTTPS"| QYAPI[WeCom API]
编排"] -->|send message| WS[wechat.service] P -->"|"send message"|" EWS[enterprise-wechat.service] WS -->"|"get/put token status"| KV["(configKV)"] EWS -->|get/put token status| KV WS -->"|"HTTPS"|" WXAPI[WeChat API] EWS -->"|"HTTPS"| QYAPI[WeCom API]
你会注意到:
- push.service 不知道 token 是怎么来的
- push.service 也不应该知道“40001 到底是什么意思”
Token 维护状态:把不稳定变成可观测
两套 service 都实现了“token 状态缓存”(本质是把 token 维护过程显式化):
validlastRefreshAtlastRefreshSuccessexpiresAterror / errorCode
它的价值在于:
- UI 能展示渠道健康度(这比“点一下试试看”靠谱得多)
- 排障时能快速判断:
- 是凭证错(AppSecret/CorpSecret 不对)
- 还是网络错(临时失败)
- 还是权限不足(API 未授权)
常见坑与排障
-
渠道验证失败但凭证看起来没问题:
- 很多时候是“权限不足”(比如测试号/未授权接口),看 errorCode 比猜更快。
-
偶发发送失败:
- 先看是否 token 过期触发了刷新;如果刷新失败,问题通常不在“发消息”,而在“获取 token”。
启发:把平台当成“依赖”,别当成“业务的一部分”
- 平台差异要被适配器隔离,否则业务代码会被错误码/鉴权方式污染。
- token 状态要被显式化,否则你只能靠“重试几次试试”这种玄学。
