夜雨聆风学习资料网

ARTICLE · 1089287

微信公众号模板消息通知:若依框架集成实战

微信公众号模板消息通知:若依框架集成实战
订单通知、打卡提醒这类业务,绕不开微信公众号的模板消息。这篇文章用一个测试号,在若依框架上集成微信通知。
一、准备工作
1、申请测试号
打开浏览器,搜索微信公众平台测试号,或者直接访问https://mp.weixin.qq.com/debug/cgi-bin/sandbox)。
用微信扫码登录,登录成功后,就可以看到appID和appsecret,先保存下来。
它们是调用微信接口的凭证。测试号的appID和appsecret是系统自动生成的,不需要自己申请。
2、关注测试号
在测试号管理页面往下滚动,找到一个二维码。用你的个人微信扫描这个二维码,点击关注。
关注成功后,可以在用户列表看到你的昵称和微信号。这个微信号就是后面发消息的收件人,复制下来保存好。
注意:测试号的openId和正式公众号的openId不通用。测试阶段用测试号的,上线后要换成正式号重新获取。
3、配置模板
继续下拉页面,找到模板消息接口区域,点击新增测试模板。
弹出表单,填写模板标题和内容。
模板标题:随便写,例如:订单通知,你自己知道模板的含义
模板内容:按微信规定的格式填写,每个变量用 {{xxx.DATA}} 表示:
订单号:{{orderNo.DATA}}
金额:{{amount.DATA}}
备注:{{remark.DATA}}
点击提交,就会在模板消息接口区域下方出现一条新记录,包含一个模板ID,形如abcdefg1234567890,复制下来存好。
都完成后,准备工作就完成了,这3个缺一不可。
二、核心代码
1、添加配置
打开若依项目的ruoyi-admin/src/main/resources/application.yml,在文件末尾加上:
wx:
  appId: 你的AppID
  appSecret: 你的appsecret
  templateId: 你的模板ID
  token: 你的token
2、后端编码
在若依的ruoyi-admin模块下,找到com.ruoyi.web.controller包,新建一个wx子包,在里面创建WxController.java:
package com.ruoyi.web.controller.wx;import com.alibaba.fastjson2.JSONObject;import com.ruoyi.common.core.redis.RedisCache;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.beans.factory.annotation.Value;import org.springframework.web.bind.annotation.*;import org.springframework.web.client.RestTemplate;import java.security.MessageDigest;import java.util.*;import java.util.concurrent.TimeUnit;@RestController@RequestMapping("/wx")public class WxController {    @Value("${wx.appId}")    private String appId;    @Value("${wx.appSecret}")    private String appSecret;    @Value("${wx.templateId}")    private String templateId;    // 服务器配置里的 Token,必须和公众号后台填的完全一致    @Value("${wx.token}")    private String token;    @Autowired    private RedisCache redisCache;    private final RestTemplate restTemplate = new RestTemplate();    /**     * 微信服务器配置验证接口     * 微信提交配置时会发 GET 请求过来     * 校验通过必须原样返回 echostr     */    @GetMapping("/check")    public String check(@RequestParam("signature") String signature,                        @RequestParam("timestamp") String timestamp,                        @RequestParam("nonce") String nonce,                        @RequestParam("echostr") String echostr) {        // 1. 将 token、timestamp、nonce 三个参数进行字典序排序        String[] arr = {token, timestamp, nonce};        Arrays.sort(arr);        // 2. 将三个参数字符串拼接成一个字符串进行 sha1 加密        StringBuilder sb = new StringBuilder();        for (String s : arr) {            sb.append(s);        }        String sha1 = sha1(sb.toString());        // 3. 开发者获得加密后的字符串可与 signature 对比,标识该请求来源于微信        if (sha1 != null && sha1.equals(signature)) {            return echostr;        }        return "error";    }    /**     * SHA1 加密工具方法     */    private String sha1(String str) {        try {            MessageDigest md = MessageDigest.getInstance("SHA-1");            byte[] digest = md.digest(str.getBytes("UTF-8"));            StringBuilder hex = new StringBuilder();            for (byte b : digest) {                String s = Integer.toHexString(b & 0xFF);                if (s.length() == 1) {                    hex.append("0");                }                hex.append(s);            }            return hex.toString();        } catch (Exception e) {            e.printStackTrace();            return null;        }    }    /**     * 获取 access_token     * 微信规定这个 token 有效期 7200 秒,且全局唯一     * 每次发消息都重新获取会被限流,必须缓存     */    private String getAccessToken() {        String cacheKey = "wx:access_token";        // 先看 Redis 里有没有        String token = redisCache.getCacheObject(cacheKey);        if (token != null) {            return token;        }        // 没有就请求微信        String url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential"                + "&appid=" + appId + "&secret=" + appSecret;        JSONObject result = restTemplate.getForObject(url, JSONObject.class);        token = result.getString("access_token");        // 缓存 7000 秒,比微信给的 7200 秒少 200 秒,提前刷新        redisCache.setCacheObject(cacheKey, token, 7000, TimeUnit.SECONDS);        return token;    }    /**     * 发送模板消息     * 访问方式:GET /wx/send?openid=xxx     */    @GetMapping("/send")    public String send(@RequestParamString openid) {        String accessToken = getAccessToken();        String url = "https://api.weixin.qq.com/cgi-bin/message/template/send?access_token=" + accessToken;        // 组装 data        // 微信要求的格式:{"key": {"value": "实际内容"}}        // key 必须和模板里 {{xxx.DATA}} 的 xxx 完全一致        Map<String, Object> data = new HashMap<>();        data.put("orderNo", Collections.singletonMap("value", "20240923001"));        data.put("amount", Collections.singletonMap("value", "99.00"));        data.put("remark", Collections.singletonMap("value", "感谢购买"));        // 组装请求体        Map<String, Object> body = new HashMap<>();        body.put("touser", openid);          // 收件人 OpenID        body.put("template_id", templateId); // 模板 ID        body.put("data", data);              // 填充内容        // 发送请求        JSONObject result = restTemplate.postForObject(url, body, JSONObject.class);        return result.toJSONString();    }}
这里有2个接口,一个与微信通信校验,一个是发送微信消息的。
3、启动测试
启动项目,打开浏览器,访问:http://localhost:8080/wx/send?openid=你的微信号。把微信号换成关注公众号拿到的那个。
对了,若依这里要配置下接口白名单,找到SecurityConfig文件,找到下面这行,添加接口白名单:
.antMatchers("/wx/check", "/wx/send").permitAll()
添加后重启服务应该就可以了,如果返回下图信息,说明发送成功。
打开手机,微信里应该已经收到一条订单通知了。
如果返回错误码,对照排查:
errcode
含义
排查方向
40001
access_token无效
appsecret 错误,或缓存失效后未正确刷新
40003
不合法的openID用户未关注公众号,或传入了其他公众号的openID
40037
不合法的template_id模板ID错误、已删除,或属于其他公众号
41030page不正确/用户未互动用户未在48小时内与公众号互动,超出模板消息发送窗口期
43004需要接收者关注用户已取消关注公众号
45009
接口调用超过限制
调用频率超限(默认每日限额100万次)
43101
用户拒绝接收消息
用户在消息通知设置中点击了“拒收该模板消息”
三、前端授权
微信规定,发模板消息之前必须由用户主动点击授权,允许接收消息。
在uni-app项目里,新建一个页面或者找一个已有页面:
<template>  <view>    <button @tap="handleOrder">下单</button>  </view></template><script>export default {  methods: {    handleOrder() {      // 必须在用户点击时调,自动触发会被微信拦截      uni.requestSubscribeMessage({        tmplIds: ['你的模板ID'],        success: (res) => {          // res 的 key 是模板ID,值是授权状态          // accept = 同意,reject = 拒绝,ban = 已被封禁          if (res['你的模板ID'] === 'accept') {            console.log('用户同意订阅');            // 这里可以调后端接口,把 openid 和订阅状态存起来          }        },        fail: (err) => {          console.error('订阅失败', err);        }      });      this.submitOrder();    },    submitOrder() {      // 你的下单逻辑    }  }}</script>
四、流程总结
① 用户点击下单按钮
② uni.requestSubscribeMessage弹出授权确认框
③ 用户点击同意
④ 前端调用后端接口,把openid和订阅状态存储起来
⑤ 订单支付成功,后端调用/wx/send
⑥ 后端从Redis获取access_token(没有就重新请求)
⑦ 后端调用微信template/send接口
⑧ 用户收到模板消息
先把测试链路跑通,收到消息之后,再去申请生产模板(下一期介绍)。跑不通的时候,上面这些代码足够排查了。
官方指南:https://developers.weixin.qq.com/doc/service/guide/product/template_message/Template_Message_Interface.html

往期回顾

NO.1

01

零成本搞定微信开发调试!Sunny-Ngrok + 公众号测试号实战指南

02

若依(RuoYi) + 腾讯云COS终极实战:告别服务器带宽焦虑,解锁前端直连下载与打包

相关学习资料