feat(notify): 微信订阅消息推送(驳回通知已对接)

- 新增 server/services/wechatSubscribeService.ts:access_token 缓存 + 模板配置 + 字段裁剪 + subscribe/send
  · 内置默认模板 SONG_REJECTED(模板 ID erAK**…,审核内容=thing6←歌名,驳回原因=thing4←驳回原因)
  · 环境变量 WECHAT_MINI_SUBSCRIBE_TEMPLATES 可按键覆盖默认模板
  · thing 字段按微信限制裁到 20 字(超长截 19 字 + 省略号),避免 47003
  · 发送永不抛异常;43101(用户没授权/没额度)只记 info
- 新增 server/api/user/wechat-subscribe-templates.get.ts:给小程序返回模板 ID(客户端要拿它去申请授权)
- 新增 server/api/admin/wechat/test-subscribe.post.ts:超管自测接口(token/openid/字段/微信原文一次看清)
- notificationService.ts:歌曲被选中/已播放/被驳回/系统通知 四处顺带发微信订阅消息
- .env.example + 部署流水线:WECHAT_MINI_SUBSCRIBE_TEMPLATES 可用仓库 Secret 下发
This commit is contained in:
2026-09-27 13:55:55 +08:00
parent a080144a86
commit a2ba15c3ca
6 changed files with 445 additions and 0 deletions
+9
View File
@@ -121,3 +121,12 @@ AGGREGATE_OAUTH_ENDPOINT=
# 注意:这与上方聚合登录的 wx 通道不是一回事——小程序 openid 按 AppID 维度生成,两者不通用
WECHAT_MINI_APPID=
WECHAT_MINI_SECRET=
# 微信「订阅消息」模板(可选,但**想让通知发到微信里就必须配**)
# 公众平台「功能 → 订阅消息 → 选用模板」拿到模板 ID 后,按下面的 JSON 填:
# {"SONG_PLAYED":{"id":"模板ID","fields":{"thing1":"songTitle","time2":"time","thing3":"message"}}}
# 键是通知类型(SONG_SELECTED / SONG_PLAYED / SONG_REJECTED / SYSTEM_NOTICE),
# fields 的键是模板里的字段名(thing1/time2/... 选用模板时能看到),值是变量名
# (内置变量:songTitle / message / time)。不配就等于关闭微信推送,其它渠道照常。
# ⚠️ 平台限制:一次性订阅=一次授权只能发一条;且只有绑定了微信的账号才有 openid。
WECHAT_MINI_SUBSCRIBE_TEMPLATES=