Java实现微信公众号给关注用户推送信息实现详解

需求背景:小程序下单完成之后,通过微信公众号给关注的用户推送信息、小程序和公众号的同一ID为unionId

前置条件

(1)在公众号开放平台绑定小程序和公众号是同一主体【公众账号和小程序】

(2)开通服务号模版消息接口授权使用(一般需要两到三天)

(3)公众号配置通知地址

提交配置的时候,可能会提示token无效,那是因为接口验证没有通过、代码验签实现如下:

【因为接收事件推送消息的数据包是XML格式,所以需要倒入解析的jar包】

  • 接收的数据格式
<xml>
    <ToUserName>< ![CDATA[toUser] ]></ToUserName>
    <FromUserName>< ![CDATA[FromUser] ]></FromUserName>
    <CreateTime>123456789</CreateTime>
    <MsgType>< ![CDATA[event] ]></MsgType>
    <Event>< ![CDATA[subscribe] ]></Event>
</xml>
  • 导入的jar包
<dependency>
   <groupId>dom4j</groupId>
   <artifactId>dom4j</artifactId>
   <version>1.6.1</version>
</dependency>

代码实现

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import org.apache.commons.lang3.StringUtils;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.DocumentHelper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import java.io.*;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;

/**
 * @version 1.0
 * @description: 公众号订阅通知
 */
@RestController
public class SubscribeNoticeController {

    private static final Logger log = LoggerFactory.getLogger(SubscribeNoticeController.class);

    /**
     * @GetMapping 和 @PostMapping 路径名称是一样的,一个用来验证token、一个用来接收数据
     */

    /**
     * token 验签
     */
    @GetMapping("/noticeInfo")
    Long checkParams(@RequestParam("signature") String signature,
                       @RequestParam("timestamp") String timestamp,
                       @RequestParam("nonce") String nonce,
                       @RequestParam("echostr") String echostr){
        String token = "和你在公众号配置中的token保持一致";
        String[] arr = {token, timestamp, nonce};
        Arrays.sort(arr);

        StringBuilder content = new StringBuilder();
        for (int i = 0; i < arr.length; i++) {
            content.append(arr[i]);
        }

        //sha1Hex 加密
        MessageDigest md = null;
        String temp = null;
        try {
            md = MessageDigest.getInstance("SHA-1");
            byte[] digest = md.digest(content.toString().getBytes());
            temp = byteToStr(digest);
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        if ((temp.toLowerCase()).equals(signature)){
            /**
             * 重点:要把接收的这个随机码返回
             */
            return Long.valueOf(echostr);
        }
        return null;
    }

    /**
     * 接收推送的数据
     */
    @PostMapping("/noticeInfo")
    @ResponseBody
    public String subscribeProcessor(HttpServletRequest request) throws Exception {
        BufferedReader br = request.getReader();
        String str="";
        String paramInfo = "";
        while((str = br.readLine()) != null){
            paramInfo += str;
        }
        if (StringUtils.isNotBlank(paramInfo)){
            JSONObject jsonObject = JSON.parseObject(paramInfo);
            String xmlInfo = jsonObject.getString("xmlInfo");
            paramInfo = xmlInfo;
        }
        log.info("微信公众号接收信息:{}",paramInfo);
        paramInfo = paramInfo.replaceAll(" ", "");
        Document document = DocumentHelper.parseText(paramInfo);
        Element root = document.getRootElement();
        String toUserName = root.elementText("ToUserName");
        String fromUserName = root.elementText("FromUserName");
        String createTime = root.elementText("CreateTime");
        String msgType = root.elementText("MsgType");
        String event = root.elementText("Event");
        log.info("微信公众号接收信息:{},{},{},{},{}",toUserName,fromUserName,createTime,msgType,event);
        /**
         * 处理后续的逻辑信息...
         */
        return null;
    }

    private static String byteToStr(byte[] byteArray){
        String strDigest = "";
        for (int i = 0; i < byteArray.length; i++) {
            strDigest += byteToHexStr(byteArray[i]);
        }
        return strDigest;
    }

    private static String byteToHexStr(byte mByte){
        char[] Digit = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A','B', 'C', 'D', 'E', 'F' };
        char[] tempArr = new char[2];
        tempArr[0] = Digit[(mByte >>> 4)& 0X0F];
        tempArr[1] = Digit[mByte & 0X0F];
        String s = new String(tempArr);
        return s;
    }
}

至此就可以接收公众号推送的信息了、比如用户的订阅、取关事件都能有通知。

实现推送消息给用户

前面代码已经可以监听到用户的订阅、取关的事件了,因为可以通过用户订阅的事件拿到用户的FromUserName(用户的openId)信息、通过这个字段,就能查到用户的unionId、然后把openId和unionId保存到数据库、相关实现方法:

    /**
     * 获取AccessToken
     * @return
     */
    public static String obtainAccessToken(){
        String G_APPID = “公众号的APPID”;
        String G_SECRET = “公众号的SECRET”;
        String tokenData = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid="+G_APPID+"&secret="+G_SECRET;
        // 返回的用户信息json字符串
        JSONObject jsonObject = httpRequestUtil(tokenData);
        return String.valueOf(jsonObject.get("access_token"));
    }

    /**
     * 获取订阅用户的openid和unionid
     * @param accessToken
     * @param openId
     * @return
     */
    public static OpenUnionIdRelationDO obtainUserDetail(String accessToken,String openId){
        String openInfoUrl = "https://api.weixin.qq.com/cgi-bin/user/info?access_token="+accessToken+"&openid="+openId+"&lang=zh_CN";
        JSONObject userDetail = httpRequestUtil(openInfoUrl);
        String openid = userDetail.getString("openid");
        Integer subscribeTime = userDetail.getInteger("subscribe_time");
        String unionid = userDetail.getString("unionid");

        OpenUnionIdRelationDO openDO = new OpenUnionIdRelationDO();
        openDO.setOpenId(openid);
        openDO.setUnionId(unionid);
        // 时间类型转换 10位时间戳转 yyyy-MM-dd HH:mm:ss
        String st = "";
        if (null != subscribeTime){
            st = DateUtils.timestampToString(subscribeTime);
        }
        openDO.setSubscribeTime(st);
        openDO.setCreateTime(new Date());
        return openDO;
    }

给用户推送信息

前面已经维护好了用户的公众号ID和公众号小程序的一致的unionid,小程序就可以通过unionid查询数据库中的openid给公众号用户推送信息了

/**
     * 给关注该公众好的用户发送信息
     * @throws Exception
     */
    public static void sendTemplateInfo() throws Exception{

        //获取小程序accessToken
        String accessToken = obtainAccessToken();
        //消息推送接口
        String path = "https://api.weixin.qq.com/cgi-bin/message/template/send?access_token=" + accessToken;

        //封装参数
        JSONObject jsonData = new JSONObject();
        jsonData.put("touser", "公众号openid");
        jsonData.put("template_id", "公众号消息模板id");
        JSONObject miniprogram = new JSONObject();
        miniprogram.put("appid","小程序ID");
        miniprogram.put("pagepath","小程序调转路径"); 
        jsonData.put("miniprogram",miniprogram);

        JSONObject data = new JSONObject();

        JSONObject first = new JSONObject();
        first.put("value","测试购票支付详情!");
        first.put("color","#173177");

        JSONObject keyword1 = new JSONObject();
        keyword1.put("value","2112323832748239");
        keyword1.put("color","#173177");

        JSONObject keyword2 = new JSONObject();
        keyword2.put("value","2022-12-22 00:00:00");
        keyword2.put("color","#173177");

        JSONObject keyword3 = new JSONObject();
        keyword3.put("value","199.56元");
        keyword3.put("color","#173177");

        JSONObject keyword4 = new JSONObject();
        keyword4.put("value","微信支付");
        keyword4.put("color","#173177");

        JSONObject remark = new JSONObject();
        remark.put("value","温馨提示:请不要爽约哦!");
        remark.put("color","#173177");

        data.put("first",first);
        data.put("keyword1",keyword1);
        data.put("keyword2",keyword2);
        data.put("keyword3",keyword3);
        data.put("keyword4",keyword4);
        data.put("remark",remark);
        jsonData.put("data",data);
        HttpUtil.post(path, jsonData.toJSONString());
    }

效果展示:

猜你喜欢

转载自blog.csdn.net/amosjob/article/details/121303468