微信小程序—订阅消息学习记录

首先说一下思路
1、在微信小程序后台配置模板
2、在小程序上让用户触发订阅消息提示
将配好的模板id,使用wx.requestSubscribeMessage,使用户同意订阅消息,获取一次可以发订阅的机会
3、后台发送消息subscribeMessage.send

1 小程序后台配置
1.1 小程序后台添加我的模板,添加成功后如图,红色部分就是后面发消息需要用到的模板id
微信小程序—订阅消息学习记录_第1张图片
1.2 模板配好了,需要注意的就是下面图片中的详情内容,关系到你向小程序发送模板时候的data数据,必须遵循这个格式。
微信小程序—订阅消息学习记录_第2张图片
2 小程序前台配置
2.1 小程序前端,调起小程序订阅消息界面,官方文档。
这里解释一个概念,一次性模板和永久模板,订阅状态和发送订阅消息次数的关系。
一次性模板:除了特殊行业和游戏,普通使用的模板都是一次性模板,即每一调用都需要用户同意订阅,如果用户点了 总是则默认同意。
订阅状态:指在弹出的订阅消息界面,用户可以选择同意/不同意订阅【订单发货提醒】和总是保持以上选项,可一次弹出最多三个模板的选择。这是只是用户单方面的选择是否要接受你的消息,如果用户只是点了同意,而没有勾选总是按钮,那个你将获得用户所勾选的模板每个模板一次发送订阅消息的机会;
例如:你放了三个模板【abc】,用户勾选了a,没有勾选bc点击同意。那个你就只能通过a模板推送一条订阅消息。
如果用户点了总是,那个下次将不会弹出提示框而是默认用户之前的选择,同意的自动同意,多一次机会,拒绝的自动拒绝。而且用户只能通过小程序设置里面修改通知,开发者没有办法修改这个,最多是通过订阅状态判断,然后提供授权按钮给客户,诱导客户修改授权。
微信小程序—订阅消息学习记录_第3张图片
订阅消息次数:图上诉所讲,用户每同意一次按钮,你就有一次机会,可以累加。但是如果用户否一天点了拒收此类信息,那么订阅消息次数将会清零。
2.2 前端代码
:调起事件要用bindtap触发。
按钮


.js代码

//定义模板id集合
var tempIdList = ['2rowP6hlpGVa2_GGjS_zPeyEZeRrKMVVRZH851xhqbk','hREKVlK3qkzftKdw_TEVJ7xe3tq2amSWsky5L-B5PNA']
wx.requestSubscribeMessage({
  tmplIds: tempIdList,
  success (res) { 
    // res格式
    //{
      // errMsg: "requestSubscribeMessage:ok",
      //zun-LzcQyW-edafCVvzPkK4de2Rllr1fFpw2A_x0oXE: "accept"
    // }
    if (res["errMsg"] == "requestSubscribeMessage:ok") {
      console.log("调用订阅消息成功!")
    }
    //下面方法判断用户具体同意或拒绝的模板。可以存放数据库或者做其他操作,我这里只是简单的判断输出
    tempIdList.map(function(item, index){
      var state ="";
      if (res[item] == "accept") {
        state = "用户同意订阅";
      }else if(res[item] == "reject"){
        state = "用户不同意订阅";
      }else if (res[item] == "ban"){
        state = "后台被禁用";
      }
      console.log(“模板” + item + “状态” + state);
    })
  },
  fail (){
    console.log("弹出订阅信息失败")
  },
  complete(){
    console.log("申请订阅结束")
  }
})

3 推送订阅消息给用户

3.1获取调用接口凭证Access_token获取AccessToken
通过appid和secret换取,返回token和有效时间(官方描述俩小时),建议存放redis中,设置时间一小时,没有了就重新获取,有的话就取redis的。

/**
  * 小程序获取推送订阅消息需要的AccessToken
   * @return
   */
  public static String getAccessToken(String appid, String secret) {
  	String accesstoken = (String) RedisUtil.GetObjectByTiket("message_accesstoken");
  	if (StringUtils.isBlank(accesstoken)) {
  		String params = "grant_type=" + "client_credential" + "&appid=" + appid + "&secret=" + secret;
      	String sr = HttpRequestHelper.sendGet("https://api.weixin.qq.com/cgi-bin/token", params);
      	//解析相应内容(转换成json对象)
  		JSONObject json = JSONObject.fromObject(sr);
  		//获取会话密钥(session_key)
  		String access_token = json.get("access_token").toString();
  		if (StringUtils.isNotBlank(access_token)) {
		accesstoken = access_token;
		RedisUtil.SetTiketObject("message_accesstoken", access_token, RedisUtil.EXRP_HOUR);
	}
}
  	return accesstoken;
  }

3.2向订阅消息接口推送消息
参数格式一定要按照消息模板中的参数格式去配置,不然的话会报错,请求接口基础参数如下:
touser——用户openid
template_id——模板ID
page——配置则跳转小程序中对应的页面,路径以app.json中配置的路径为准,可以通过?传参;不配置则无跳转
miniprogram_state——代表订阅消息跳转小程序的类型:developer为开发版;trial为体验版;formal为正式版;默认为正式版,
lang——进入小程序查看”的语言类型,支持zh_CN(简体中文)、en_US(英文)、zh_HK(繁体中文)、zh_TW(繁体中文),默认为zh_CN
进入小程序查看”的语言类型,支持zh_CN(简体中文)、en_US(英文)、zh_HK(繁体中文)、zh_TW(繁体中文),默认为zh_CN。
注: 参数设置建议采用JSONObject对象进行封装,我下面的参数结构是亲测成功的,可以正常使用,这里踩了很多坑,好多数据结构都不对,参数错误。
发送成功errcode为0,失败则有具体的errcode。

/**
	 * 订单发货通知
	 * @param openid  购买人openid
	 * @param accesstoken 接口调用凭证
	 * @param orderno 32位以内数字、字母或符号
	 * @param goodsthind 20以内字符
	 * @param expressname 10个以内纯汉字或20个以内纯字母或符号
	 * @param expressno 32位以内数字、字母或符号
	 * @param delivertime 2019年10月1日 15:01
	 * @return 
	 */
	public static MessageResult sendOderFahuoMessage(String miniprogram_state, String messageTempId, String openid,String accesstoken,String orderno,String goodsthind, String expressname, String expressno, String delivertime) {
		
		
		//消息内容
		JSONObject all = new JSONObject();
		all.put("touser", openid);
		all.put("template_id", messageTempId);
		all.put("page", "pages/follipoo/order-info/order-info?orderno="+orderno);
		all.put("miniprogram_state",miniprogram_state);
		all.put("lang", "zh_CN");
		
		JSONObject character_string1 = new JSONObject();
		character_string1.put("value", orderno);
		JSONObject thing2 = new JSONObject();
		thing2.put("value", goodsthind);
		JSONObject name3 = new JSONObject();
		name3.put("value", expressname);
		JSONObject character_string4 = new JSONObject();
		character_string4.put("value", expressno);
		JSONObject date10 = new JSONObject();
		date10.put("value", delivertime);
		
		
		JSONObject data = new JSONObject();
		data.put("character_string1", character_string1);
		data.put("thing2", thing2);
		data.put("name3", name3);
		data.put("character_string4", character_string4);
		data.put("date10", date10);
		all.put("data", data);
		
		
		
		//正式环境无需miniprogram_state
		String params = JsonUtil.objectToJson(all);
		String sr = HttpRequestHelper.sendPost2("https://api.weixin.qq.com/cgi-bin/message/subscribe/send?access_token="+accesstoken, params);
		
		//解析相应内容(转换成json对象)
		JSONObject json = JSONObject.fromObject(sr);
		//获取会话密钥(session_key)
		String errcode = json.get("errcode").toString();
		String errmsg = json.get("errmsg").toString();
		MessageResult result = new MessageResult();
		if (errcode.equals("0")) {
			result.setState(true);
		}else {
			result.setState(false);
			result.setErrCode(errcode);
			if (!errcode.equals("47003")) {
				//这里是我按照官方的错误代码提示自己写了一个根据code区分报错信息的方法,这里就不放出来了。
				errmsg = parseErrCode(errcode);
			}
			result.setErrMsg(errmsg);
		}
		return result;
	};

发送请求的工具
HttpRequestHelper

/**
* 向指定URL发送GET方法的请求
 * 
 * @param url
 *            发送请求的URL
 * @param param
 *            请求参数,请求参数应该是 name1=value1&name2=value2 的形式。
 * @return URL 所代表远程资源的响应结果
 */
public static String sendGet(String url, String param) {
    String result = "";
    BufferedReader in = null;
    try {
    	String urlNameString = url +  (StringHelp.IsNullOrEmpty(param)?"": "?" + param);
        URL realUrl = new URL(urlNameString);
        // 打开和URL之间的连接
        URLConnection connection = realUrl.openConnection();
        // 设置通用的请求属性
        connection.setRequestProperty("accept", "*/*");
        connection.setRequestProperty("connection", "Keep-Alive");
        connection.setRequestProperty("user-agent",
                "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
        // 建立实际的连接
        connection.connect();
        // 获取所有响应头字段
        Map> map = connection.getHeaderFields();
        // 遍历所有的响应头字段
        for (String key : map.keySet()) {
            System.out.println(key + "--->" + map.get(key));
        }
        // 定义 BufferedReader输入流来读取URL的响应
        in = new BufferedReader(new InputStreamReader(
                connection.getInputStream()));
        String line;
        while ((line = in.readLine()) != null) {
            result += line;
        }
    } catch (Exception e) {
        System.out.println("发送GET请求出现异常!" + e);
        e.printStackTrace();
    }
    // 使用finally块来关闭输入流
    finally {
        try {
            if (in != null) {
                in.close();
            }
        } catch (Exception e2) {
            e2.printStackTrace();
        }
    }
    return result;
}
/**
*用于请求订阅消息接口
*注:"connectionType", "application/json"
**/
public static String sendPost2(String url, String param) {
    	PrintWriter out = null;
    	BufferedReader in = null;
    	String result = "";
    	try {
    		URL realUrl = new URL(url);
    		// 打开和URL之间的连接
    		URLConnection conn = realUrl.openConnection();
    		// 设置通用的请求属性
    		conn.setRequestProperty("accept", "*/*");
    		conn.setRequestProperty("connectionType", "application/json");
    		conn.setRequestProperty("connection", "Keep-Alive");
    		conn.setRequestProperty("user-agent",
    				"Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
    		// 发送POST请求必须设置如下两行
    		conn.setDoOutput(true);
    		conn.setDoInput(true);
    		// 获取URLConnection对象对应的输出流
    		out = new PrintWriter(conn.getOutputStream());
    		// 发送请求参数
    		out.print(param);
    		// flush输出流的缓冲
    		out.flush();
    		// 定义BufferedReader输入流来读取URL的响应
    		in = new BufferedReader(new InputStreamReader(conn.getInputStream()));
    		String line;
    		while ((line = in.readLine()) != null) {
    			result += line;
    		}
    	} catch (Exception e) {
    		System.out.println("发送 POST 请求出现异常!"+e);
    		e.printStackTrace();
    	}
    	//使用finally块来关闭输出流、输入流
    	finally{
    		try{
    			if(out!=null){
    				out.close();
    			}
    			if(in!=null){
    				in.close();
    			}
    		}
    		catch(IOException ex){
    			ex.printStackTrace();
    		}
    	}
    	return result;
    }    

最后整理一下遇到的问题:
1、用户未同意订阅消息,则消息推送的错误code为43101,提示用户拒绝接受消息。
2、如果用户选择了不同意订阅消息,并勾选了总是保持以上操作的话,微信是不会再弹出授权订阅消息提示的,只能用户自己手动修改小程序设置里的授权管理。可以通过wx.getString()获取用户点击了总是操作的模板订阅情况。
3、网上有网友遇到过在推送消息时,data里面参数的值value存在空格的话会提示数据类型不满足规范,但是我没有遇到过,单纯记录一下。
4、获取access_token时官方返回接口为 {“access_token”:“ACCESS_TOKEN”,“expires_in”:7200},所以时间是2小时,官方也不建议频繁的去获取,所以我才用redis存放,设置一小时的生命周期。
5、用户每同意一次授权,则同意的模板将会获得一次推送消息的机会,暂时不确定有没有时间限制,次数可以累加;但是如果用户再授权设置中选择拒收此类消息的话推送机会会清零,即使用户重新选择接受。
6、个人建议appid,secret,模板id,发送版本等最好写在配置文件里,然后传给接口,毕竟小程序从开发版-体验版-正式版要经历好几个过程,很多东西需要重新搞。

你可能感兴趣的:(小程序)