当前位置: 首页 > article >正文

【微信小程序】订阅消息

概述

微信小程序订阅消息允许开发者在用户主动触发订阅后,向用户发送服务通知。这有助于提升用户体验和业务转化率。订阅消息分为一次性订阅和长期订阅两种类型,开发者需根据业务需求合理选择。

微信小程序的订阅消息分“一次性订阅”和“长期订阅”两种方式:

1、一次性订阅就是要用户点击同意一次消息订阅,服务端才能发送一次信息。点击多少次就能发送多少次。

2、长期订阅。服务端可以无限,但是教育、交通、医疗等行业才有长期订阅。

流程实现

1、申请消息模版

  • 登录微信公众平台,选择小程序登录,进入“设置”-“基础功能”-“订阅消息”-“订阅消息模板管理”。
  • 点击“添加模板”,选择合适的模板并添加,获取模板ID。

2、获取用户订阅权限

  1. 在小程序端,引导用户订阅消息。可以通过wx.requestSubscribeMessage API请求用户授权订阅特定模板消息。

3、后台订阅微信消息(可选)

配置服务器地址:

找到“开发与服务”-“开发管理”-“消息推送”,开启消息推送,配置业务服务器地址。

配置的地址需要支持外网访问,并同时需要GET和POST两种方式的API

GET方式API被微信服务器回调,验证上面配置的签名,Token等信息:

public boolean checkSignature(String signature, String timestamp, String nonce) {
        String[] array = new String[]{ token, timestamp, nonce};
        //先对这三个字符串字典排序,然后拼接
        Arrays.sort(array);
        StringBuilder sb = new StringBuilder();
        for (String s : array) {
            sb.append(s);
        }
        //使用SHA-1算法对拼接字符串加密
        MessageDigest messageDigest;
        String hexStr = null;
        try {
            messageDigest = MessageDigest.getInstance("SHA-1");
            byte[] digest = messageDigest.digest(sb.toString().getBytes());
            //将加密后的字符串转换成16进制字符串
            hexStr = CommonUtils.bytesToHexStr(digest);

        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        //返回校验结果
        return signature.equalsIgnoreCase(hexStr);
    }

POST方法被微信回调,可以处理以下相关事件(微信文档):

  • 当用户触发订阅消息弹框后,用户的相关行为事件结果会推送至开发者所配置的服务器地址 
  • 当用户在手机端服务通知里消息卡片右上角“...”管理消息时,相应的行为事件会推送至开发者所配置的服务器地址
  • 调用订阅消息接口发送消息给用户的最终结果,会推送下发结果事件至开发者所配置的服务器地址

业务服务器可以将用户订阅消息的状态做记录,后期发送消息时,可以过滤掉没有接受订阅的用户,或通过其他手段对该部分用户单独处理。

 @PostMapping("/subscribeMessageCallback")
    public R<String> handleSubscribeMessageCallback(HttpServletRequest request,  HttpServletRequest httpRequest) {

        BufferedReader reader;
        StringBuilder requestContent = new StringBuilder();
        //消息类型
        try {
            reader = new BufferedReader(new InputStreamReader(request.getInputStream()));
            String line;
            while ((line = reader.readLine()) != null) {
                requestContent.append(line);
            }
            reader.close();
            String jsonStr = requestContent.toString();
            log.info("wechat callback message=[{}]", jsonStr);
            JSONObject rootObject = JSONUtil.parseObj(jsonStr);
            // 在这里处理你的订阅消息事件,例如获取OpenID、模板ID和事件数据等
            String openId = rootObject.getStr("FromUserName");
            // 注意:这里的MsgType可能需要根据实际情况调整,因为订阅消息的回调中可能没有直接的MsgType字段,而是其他表示事件类型的字段
            String eventType = rootObject.getStr("MsgType");
            if("event".equals(eventType)){
                String event = rootObject.getStr("Event");
                String list = rootObject.getStr("List");
                // 小程序用户接受,或改变订阅状态事件
                if("subscribe_msg_popup_event".equals(event)
                || "subscribe_msg_change_event".equals(event)){
                    //小程序用户是否确定接受订阅消息事件
                    if(StrUtil.isNotBlank(list)){
                        JSONArray listArray = JSONUtil.parseArray(list);
                        listArray.forEach(item -> {
                            JSONObject jsonObject = JSONUtil.parseObj(item);
                            String subscribeStatusString = jsonObject.getStr("SubscribeStatusString");
                            String templateId = jsonObject.getStr("TemplateId");
                            // 判断用户是否接受订阅消消息
                            boolean accept = "accept".equals(subscribeStatusString);
                            MiniSubscribeUser plaMiniSubscribeUser = new MiniSubscribeUser();
                            plaMiniSubscribeUser.setOpenId(openId);
                            plaMiniSubscribeUser.setTemplateId(templateId);
                            plaMiniSubscribeUser.setAcceptState(accept ? "1" : "2");
                            plaMiniSubscribeUserService.saveOrUpdateSubscribeUser(plaMiniSubscribeUser);
                        });
                    }
                } else if ("subscribe_msg_sent_event".equals(event)) {
                    //小程序订阅消息是否发送成功事件
                    /**
                     *  "List": {
                     *         "TemplateId": "BEwX0BO-T3MqK3Uc5oTU3CGBqzjpndk2jzUf7VfExd8",
                     *         "MsgID": "1864323726461255680",
                     *         "ErrorCode": "0",
                     *         "ErrorStatus": "success"
                     *       }
                     */
                    JSONArray listArray = JSONUtil.parseArray(list);
                    listArray.forEach(item -> {
                        JSONObject jsonObject = JSONUtil.parseObj(item);
                        String msgId = jsonObject.getStr("MsgID");
                        String templateId = jsonObject.getStr("TemplateId");
                        String errorCode = jsonObject.getStr("ErrorCode");
                        String errorStatus = jsonObject.getStr("ErrorStatus");
                        log.info("小程序用户{},发送{}消息结果{}", openId, templateId, errorStatus);
                    });
                }
            }
            // 返回给微信服务器的响应(通常是一个简单的成功响应)
            return R.ok("success");
        } catch (Exception e) {
            e.printStackTrace();
            return R.fail(HttpStatus.BAD_REQUEST, e.getMessage());
        }
    }

 

4、实现发送订阅消息的接口

在需要发送订阅消息的地方,调用微信提供的发送订阅消息接口。

POST https://api.weixin.qq.com/cgi-bin/message/subscribe/send?access_token=ACCESS_TOKEN 

这通常涉及到调用微信的API,传递必要的参数如模板ID、用户OpenID、消息内容等。封装一个消息发送的对象:

public class WeChatSubscribeMsg {

    /**
     * GQ7x0v7gelxp7C8AmT2EcJMmop8evA8P2qSiIr7QEyw
     * {
     *   "touser": "OPENID",
     *   "template_id": "TEMPLATE_ID",
     *   "page": "index",
     *   "data": {
     *       "name01": {
     *           "value": "某某"
     *       },
     *       "amount01": {
     *           "value": "¥100"
     *       },
     *       "thing01": {
     *           "value": "广州至北京"
     *       } ,
     *       "date01": {
     *           "value": "2018-01-01"
     *       }
     *   }
     * }
     */

    private String touser;

    private String userId;

    private String template_id;

    private String page;

    private TreeMap<String,DataValue> data;

    @Data
    public static class DataValue{
        public String value;
    }

根据小程序appid和secret 获取小程序token 发送消息:

public Boolean sendSubscribeMessage(WeChatSubscribeMsg weChatSubscribeMsg) {
        String touser = weChatSubscribeMsg.getTouser();
        String templateId = weChatSubscribeMsg.getTemplate_id();
        MiniSubscribeUser miniSubscribeUser = getMiniSubscribeUser(templateId, touser);
        if (miniSubscribeUser == null ) {
            log.info("用户未关注小程序");
            return false;
        }
        if(miniSubscribeUser.getAcceptState().equals("2")){
            log.info("用户{}已拒绝接收订阅消息", touser);
            return false;
        }

        String postData = JSONUtil.toJsonStr(weChatSubscribeMsg);
        val accessToken = wechatService.getMiniAccessToken();
        String uniformSend = "https://api.weixin.qq.com/cgi-bin/message/subscribe/send?access_token=ACCESS_TOKEN";
        uniformSend = uniformSend.replace("ACCESS_TOKEN", accessToken);
        log.info("send Wechat subscribe msg Url :{} postData:{} ", uniformSend, postData);
        if (null != postData) {
            //http 调用第三方接口
            String result = HttpUtil.post(uniformSend, postData);
            log.info("send wechat subscribe msg result:" + result);
            JSONObject jsonObject = JSONUtil.parseObj(result);
            if (jsonObject.getInt("errcode") == 0) {
                return true;
            }
        }
        return false;
    }


http://www.kler.cn/a/553764.html

相关文章:

  • 【DeepSeek服务器部署全攻略】Linux服务器部署DeepSeek R1模型、实现API调用、搭建Web页面以及专属知识库
  • 【数据挖掘】ARFF格式与数据收集
  • 健康养生:从生活细节开启活力之旅
  • Windows 启动 SSH 服务
  • android社畜模拟器
  • 信息学奥赛c++语言:数组逆序重存放
  • JavaScript 前端面试 3(等于、全等、instanceof、typeof 、原型、原型链)
  • 当一个后端下载了一个VUE开源项目,怎么开始着手玩一下
  • Spring中的日志
  • Linux的基础指令和环境部署,项目部署实战(下)
  • 【DeepSeek】Mac m1电脑部署DeepSeek
  • 校园网架构设计与部署实战
  • Linux学习笔记之进程切换
  • 微服务SpringCloudAlibaba组件Spring Cloud Gateway网关教程【详解gatway网关以及各种过滤器配置使用,附有示例+代码】
  • 一个简单的ubuntu/开发板初始化脚本
  • HTML/CSS中并集选择器
  • open-webui安装
  • 基于Java+MySQL实现的院的实验课选课及实验室管理系统
  • 鸿蒙NEXT开发-应用状态
  • IDEA CodeGPT 使用教程