您的当前位置:首页正文

微信小程序订阅消息推送 --Springboot实现

2024-11-29 来源:个人技术集锦

背景

最近做个排号叫号的微信小程序,想用户在微信小程序上进行排号,商家在小程序上进行叫号,叫号的通知发送到用户微信里.这里就要用到订阅消息.

先看效果图

1.创建模板

登录微信公众平台

发送订阅消息的三个步骤.

一.获取用户的openid

用户的openid的获取,我是在用户使用微信登录时进行获取的,具体可以查看我微信登录的文章:

二.获取access_token

我们首先来看看access_token是什么,官方的说明是:

简单来说,access_token就是小程序官方给我们提供的一个凭证,如果要调用官方的接口,就必须先获取凭证,所以我们先来谈谈怎么获取access_token.

还是先看官方文档

参数是否必须说明
grant_type获取access_token填写client_credential
appid第三方用户唯一凭证
secret第三方用户唯一凭证密钥,即appsecret

grant_type是一个固定的值,appid和secret是需要我们填入的,这两个值在我们的小程序后台就可以拿到,具体可以查看我微信登录的文章:
可以在微信小程序后台去寻找appid和secret

Java代码部分

具体思路就是先向微信提供的这个url发送一个get请求.
微信官方给的正常返回值为

import lombok.Data;

/**
 * @author zty
 * @date 2020/4/23 下午2:26
 * @description:
 */
@Data
public class getAccessTokenModel {
    private String access_token;
    private Integer expires_in;
}

然后利用一个http工具类发送请求,一个json转model工具类来转换一下.这两个工具类具体代码后面贴出
这就是我们获取access_token的代码了,获取之后将他存入redis,设置有效时间,超时就重新获取.

/**
     * 获取AccessToken
     *
     * @return
     */
    public String getAccessToken() {
        if (redisUtil.get("access_token") != null) {
            return (String) redisUtil.get("access_token");
        }
        //GET https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET
        String url = "https://api.weixin.qq.com/cgi-bin/token";
        Map<String, String> param = new HashMap<>();
        param.put("grant_type", "client_credential");
        param.put("appid", "wx7e139fc4dec9fd08");
        param.put("secret", "d483c36cf9f93500a17aa0cb788a0f48");

        String vxResult = HttpClientUtil.doGet(url, param);
        log.info(vxResult);

        getAccessTokenModel accessTokenModel = JsonUtils.jsonToPojo(vxResult, getAccessTokenModel.class);

        redisUtil.set("access_token", accessTokenModel.getAccess_token(), accessTokenModel.getExpires_in());
        return accessTokenModel.getAccess_token();
        //POST https://api.weixin.qq.com/cgi-bin/message/subscribe/send?access_token=ACCESS_TOKEN
    }

使用这两个工具类需要引入俩个maven依赖

<!-- https://mvnrepository.com/artifact/org.apache.httpcomponents/httpclient -->
        <dependency>
            <groupId>org.apache.httpcomponents</groupId>
            <artifactId>httpclient</artifactId>
            <version>4.5.7</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/com.alibaba/fastjson -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.56</version>
        </dependency>
package com.vx.utils;

import java.io.IOException;
import java.net.URI;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import org.apache.http.NameValuePair;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;

/**
 * @author zty
 */
public class HttpClientUtil {

	public static String doGet(String url, Map<String, String> param) {

		// 创建Httpclient对象
		CloseableHttpClient httpclient = HttpClients.createDefault();

		String resultString = "";
		CloseableHttpResponse response = null;
		try {
			// 创建uri
			URIBuilder builder = new URIBuilder(url);
			if (param != null) {
				for (String key : param.keySet()) {
					builder.addParameter(key, param.get(key));
				}
			}
			URI uri = builder.build();

			// 创建http GET请求
			HttpGet httpGet = new HttpGet(uri);

			// 执行请求
			response = httpclient.execute(httpGet);
			// 判断返回状态是否为200
			if (response.getStatusLine().getStatusCode() == 200) {
				resultString = EntityUtils.toString(response.getEntity(), "UTF-8");
			}
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			try {
				if (response != null) {
					response.close();
				}
				httpclient.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		return resultString;
	}

	public static String doGet(String url) {
		return doGet(url, null);
	}

	public static String doPost(String url, Map<String, String> param) {
		// 创建Httpclient对象
		CloseableHttpClient httpClient = HttpClients.createDefault();
		CloseableHttpResponse response = null;
		String resultString = "";
		try {
			// 创建Http Post请求
			HttpPost httpPost = new HttpPost(url);
			// 创建参数列表
			if (param != null) {
				List<NameValuePair> paramList = new ArrayList<>();
				for (String key : param.keySet()) {
					paramList.add(new BasicNameValuePair(key, param.get(key)));
				}
				// 模拟表单
				UrlEncodedFormEntity entity = new UrlEncodedFormEntity(paramList);
				httpPost.setEntity(entity);
			}
			// 执行http请求
			response = httpClient.execute(httpPost);
			resultString = EntityUtils.toString(response.getEntity(), "utf-8");
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			try {
				response.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}

		return resultString;
	}
	
	public static String doPost(String url) {
		return doPost(url, null);
	}
	
	public static String doPostJson(String url, String json) {
		// 创建Httpclient对象
		CloseableHttpClient httpClient = HttpClients.createDefault();
		CloseableHttpResponse response = null;
		String resultString = "";
		try {
			// 创建Http Post请求
			HttpPost httpPost = new HttpPost(url);
			// 创建请求内容
			StringEntity entity = new StringEntity(json, ContentType.APPLICATION_JSON);
			httpPost.setEntity(entity);
			// 执行http请求
			response = httpClient.execute(httpPost);
			resultString = EntityUtils.toString(response.getEntity(), "utf-8");
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			try {
				response.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}

		return resultString;
	}
}
package com.vx.utils;

import java.util.List;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;

/**
 * 
 * @Title: JsonUtils.java
 * @Package com.lee.utils
 * @Description: 自定义响应结构, 转换类
 * Copyright: Copyright (c) 2016
 * Company:Nathan.Lee.Salvatore
 * 
 * @author zty
 * @date 2020年4月15日 下午11:05:03
 * @version V1.0
 */
public class JsonUtils {

    // 定义jackson对象
    private static final ObjectMapper MAPPER = new ObjectMapper();

    /**
     * 将对象转换成json字符串。
     * <p>Title: pojoToJson</p>
     * <p>Description: </p>
     * @param data
     * @return
     */
    public static String objectToJson(Object data) {
    	try {
			String string = MAPPER.writeValueAsString(data);
			return string;
		} catch (JsonProcessingException e) {
			e.printStackTrace();
		}
    	return null;
    }
    
    /**
     * 将json结果集转化为对象
     * 
     * @param jsonData json数据
     * @param clazz 对象中的object类型
     * @return
     */
    public static <T> T jsonToPojo(String jsonData, Class<T> beanType) {
        try {
            T t = MAPPER.readValue(jsonData, beanType);
            return t;
        } catch (Exception e) {
        	e.printStackTrace();
        }
        return null;
    }
    
    /**
     * 将json数据转换成pojo对象list
     * <p>Title: jsonToList</p>
     * <p>Description: </p>
     * @param jsonData
     * @param beanType
     * @return
     */
    public static <T>List<T> jsonToList(String jsonData, Class<T> beanType) {
    	JavaType javaType = MAPPER.getTypeFactory().constructParametricType(List.class, beanType);
    	try {
    		List<T> list = MAPPER.readValue(jsonData, javaType);
    		return list;
		} catch (Exception e) {
			e.printStackTrace();
		}
    	
    	return null;
    }
    
}

好了,这样我我们就获取到了access_token,注意我是存在redis里的

三.发送消息到微信

小程序端获取授权我这里就不在介绍了,具体看微信的官方文档
我们通过上面第二步,成功的获取到了access_token。下面就要调用小程序官方为我们提供的发送消息的接口了,先看下官方文档。

正常来说,一次授权只允许发送一条消息.

java代码

先创建一个类,注意Thing要与你模板对应

import lombok.AllArgsConstructor;
import lombok.Data;

/**
 * @author zty
 * @date 2020/4/23 下午8:31
 * @description:
 */
@Data
public class SubscribeMessageVO {
    //具体的订阅消息的key {{thing4.DATA}} 则key为thing4
    private Thing4 thing4;
    private Thing6 thing6;
    private Thing7 thing7;
    @Data
    @AllArgsConstructor
    public static class Thing4{
        private String value;
    }
    @Data
    @AllArgsConstructor
    public static class Thing6{
        private String value;
    }
    @Data
    @AllArgsConstructor
    public static class Thing7{
        private String value;
    }
}

我这里发送用到了消息队列,可以忽略

/**
     * 发送订阅消息
     *
     * @param peopleSet
     */
    @RabbitListener(bindings = {
            @QueueBinding(
                    value = @Queue,//创建临时队列
                    exchange = @Exchange(value = "updateHistory", type = "fanout")
            )
    })
    public void sendMessage(String peopleSet) {
        String[] temp;
        temp = peopleSet.split("\\+");
        String p1 = temp[0];
        Long p2 = Long.valueOf(temp[1]);
        Long p3 = Long.valueOf(temp[2]);
        ActivityUserHistory history = activityUserHistoryMapper.selectByOpenId2(getUserIdByOpenId(p1), p2, p3);
        SubscribeMessageVO bean = new SubscribeMessageVO();
        bean.setThing4(new SubscribeMessageVO.Thing4(history.getActivityName() + ":" + history.getName()));
        bean.setThing6(new SubscribeMessageVO.Thing6(history.getAddress()));
        bean.setThing7(new SubscribeMessageVO.Thing7("请到服务处联系工作人员"));
        WxMssVO wxMssVO = new WxMssVO();
        wxMssVO.setTouser(p1);
        wxMssVO.setTemplate_id("***");//模板id号码
        wxMssVO.setData(bean);
        push(wxMssVO);
    }
       public void push(WxMssVO wxMssVO) {
        String url = "https://api.weixin.qq.com/cgi-bin/message/subscribe/send?access_token=" + getAccessToken();
        String json = JsonUtils.objectToJson(wxMssVO);
        String vxResult = HttpClientUtil.doPostJson(url, json);
        log.info("返回的内容:" + vxResult);
    }

将信息加入SubscribeMessageVO这个类的一个对象中,然后转为json,继续调用我们刚刚的http工具类发送.这样就能得到我们开始时的订阅消息了

显示全文