小程序官方关于模板消息

 

STEP 1:微信后台添加模板消息

1:选用模板  【功能】 ▶ 【模板消息】  ▶ 【模板库】  ▶【选用】

wxjava小程序 模版消息 微信小程序模板消息_小程序消息推送

2:通过配置关键词来定义模板样式 

wxjava小程序 模版消息 微信小程序模板消息_小程序消息推送_02

3:点击提交,配置模板成功(该模板ID很重要,后面需要使用)

wxjava小程序 模版消息 微信小程序模板消息_小程序推送模板消息_03

STEP 2:定义小程序页面相关按钮和触发事件

<form report-submit='true' bindsubmit='pay'>
<button form-type="submit">确认支付</button>
</form>

 点击按钮提交用户的openid和formid

pay: function (e) {
    var that = this;
    console.log(e.detail.formId);
    //将用户的openid和formid提交到后台
    wx.request({
      url: "https://wp.mote.com/wx/pay.do",
      data: {
        "openid": app.globalData.openid,
        "formid": e.detail.formId
      },
      method: 'GET',
      header: {
        'Content-type': 'application/json'
      },
      success: function (res) {
         
      }
    });
  },

STEP 3:推送消息API以及JAVA实现推送逻辑

官方推送消息使用的API:sendTemplateMessage

官方文档API详解:https://developers.weixin.qq.com/miniprogram/dev/api/sendTemplateMessage.html

 

消息推送请求地址

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

请求参数

属性

类型

默认值

必填

说明

access_token

string

 


接口调用凭证

touser

string

 


接收者(用户)的 openid

template_id

string

 


所需下发的模板消息的id

page

string

 


点击模板卡片后的跳转页面,仅限本小程序内的页面。支持带参数,(示例index?foo=bar)。该字段不填则模板无跳转。

form_id

string

 


表单提交场景下,为 submit 事件带上的 formId;支付场景下,为本次支付的 prepay_id

data

Object

 


模板内容,不填则下发空模板。具体格式请参考示例。

emphasis_keyword

string

 


模板需要放大的关键词,不填则默认无放大

1:定时获取 access_token(access_token的有效时间是2小时,所以需要定时去获取)

封装小程序变量类 Constants

public class Constants {
	// 小程序APPID
	public static String AppId = "wxd2c768c44bb8de5b";
	// 小程序密钥
	public static String AppSecret = "9cfcc674463a5762930a99b67e42ab6e";
	// 获取Token接口
	public static String token_url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET";
	// 订单支付成功的模板ID
	public static String formid = "LDJ4K69hOA201Sc1j1uJ9LGeBAerXUwA7t1cOVTlTd8";
	// 用户点击通知后跳转小程序的页面
	public static String page = "pages/index/index";

}

封装HttpClient工具类:

maven依赖:

<dependency>
	<groupId>org.apache.httpcomponents</groupId>
	<artifactId>httpclient</artifactId>
	<version>4.5.3</version>
</dependency>

<dependency>
	<groupId>com.alibaba</groupId>
	<artifactId>fastjson</artifactId>
	<version>1.2.54</version>
</dependency>
import com.alibaba.fastjson.JSONObject;
import org.apache.http.HttpEntity;
import org.apache.http.HttpStatus;
import org.apache.http.client.config.RequestConfig;
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.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;

/**
 * HttpClient工具类
 * 
 * @author hang.luo
 */
public class HttpClientUtils {
	private static Logger logger = LoggerFactory
			.getLogger(HttpClientUtils.class); // 日志记录

	private static RequestConfig requestConfig = null;

	static {
		// 设置请求和传输超时时间
		requestConfig = RequestConfig.custom().setSocketTimeout(2000)
				.setConnectTimeout(2000).build();
	}

	/**
	 * post请求传输json参数
	 * 
	 * @param url
	 *            url地址
	 * @param json
	 *            参数
	 * @return
	 */
	public static JSONObject httpPost(String url, JSONObject jsonParam) {
		// post请求返回结果
		CloseableHttpClient httpClient = HttpClients.createDefault();
		JSONObject jsonResult = null;
		HttpPost httpPost = new HttpPost(url);
		// 设置请求和传输超时时间
		httpPost.setConfig(requestConfig);
		try {
			if (null != jsonParam) {
				// 解决中文乱码问题
				StringEntity entity = new StringEntity(jsonParam.toString(),
						"utf-8");
				entity.setContentEncoding("UTF-8");
				entity.setContentType("application/json");
				httpPost.setEntity(entity);
			}
			CloseableHttpResponse result = httpClient.execute(httpPost);
			// 请求发送成功,并得到响应
			if (result.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
				String str = "";
				try {
					// 读取服务器返回过来的json字符串数据
					str = EntityUtils.toString(result.getEntity(), "utf-8");
					// 把json字符串转换成json对象
					jsonResult = JSONObject.parseObject(str);
				} catch (Exception e) {
					logger.error("post请求提交失败:" + url, e);
				}
			}
		} catch (IOException e) {
			logger.error("post请求提交失败:" + url, e);
		} finally {
			httpPost.releaseConnection();
		}
		return jsonResult;
	}

	/**
	 * post请求传输String参数 例如:name=Jack&sex=1&type=2
	 * Content-type:application/x-www-form-urlencoded
	 * 
	 * @param url
	 *            url地址
	 * @param strParam
	 *            参数
	 * @return
	 */
	public static JSONObject httpPost(String url, String strParam) {
		// post请求返回结果
		CloseableHttpClient httpClient = HttpClients.createDefault();
		JSONObject jsonResult = null;
		HttpPost httpPost = new HttpPost(url);
		httpPost.setConfig(requestConfig);
		try {
			if (null != strParam) {
				// 解决中文乱码问题
				StringEntity entity = new StringEntity(strParam, "utf-8");
				entity.setContentEncoding("UTF-8");
				entity.setContentType("application/x-www-form-urlencoded");
				httpPost.setEntity(entity);
			}
			CloseableHttpResponse result = httpClient.execute(httpPost);
			// 请求发送成功,并得到响应
			if (result.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
				String str = "";
				try {
					// 读取服务器返回过来的json字符串数据
					str = EntityUtils.toString(result.getEntity(), "utf-8");
					// 把json字符串转换成json对象
					jsonResult = JSONObject.parseObject(str);
				} catch (Exception e) {
					logger.error("post请求提交失败:" + url, e);
				}
			}
		} catch (IOException e) {
			logger.error("post请求提交失败:" + url, e);
		} finally {
			httpPost.releaseConnection();
		}
		return jsonResult;
	}

	/**
	 * 发送get请求
	 * 
	 * @param url
	 *            路径
	 * @return
	 */
	public static JSONObject httpGet(String url) {
		// get请求返回结果
		JSONObject jsonResult = null;
		CloseableHttpClient client = HttpClients.createDefault();
		// 发送get请求
		HttpGet request = new HttpGet(url);
		request.setConfig(requestConfig);
		try {
			CloseableHttpResponse response = client.execute(request);

			// 请求发送成功,并得到响应
			if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
				// 读取服务器返回过来的json字符串数据
				HttpEntity entity = response.getEntity();
				String strResult = EntityUtils.toString(entity, "utf-8");
				// 把json字符串转换成json对象
				jsonResult = JSONObject.parseObject(strResult);
			} else {
				logger.error("get请求提交失败:" + url);
			}
		} catch (IOException e) {
			logger.error("get请求提交失败:" + url, e);
		} finally {
			request.releaseConnection();
		}
		return jsonResult;
	}

}

获取access_token

import com.alibaba.fastjson.JSONObject;
import com.mote.utils.HttpClientUtils;

public class FlushToken {

	public static String token;

	/**
	 * 获取接口访问凭证
	 *
	 * @param appid
	 *            凭证
	 * @param appsecret
	 *            密钥
	 * @return
	 */
	public void flushToken() {
		String requestUrl = Constant.token_url.replace("APPID", Constant.AppId)
				.replace("APPSECRET", Constant.AppSecret);
		// 发起GET请求获取凭证
		JSONObject json = HttpClientUtils.httpGet(requestUrl);
		if (null != json) {
			try {
				token = json.getString("access_token");
			} catch (Exception e) {
				e.printStackTrace();
				System.out.println(json.getString("errmsg"));
			}
		}
	}

}

 配置flushToken方法每1小时执行一次

<!-- 定时任务 -->
<bean id="flushToken" class="com.mote.controller.FlushToken"></bean>
<task:scheduled-tasks>
    <task:scheduled ref="flushToken"  method="flushToken"  cron="0 0 0/1 * * ?" />
</task:scheduled-tasks>

2:封装消息推送的其他请求参数pojo类

import java.util.Map;

/**
 * @ClassName: Template
 * @Description:小程序微信推送模版model
 */
public class Template {
	// 消息接收方
	private String touser;
	// 模板id
	private String template_id;
	// 点击模板卡片后的跳转页面,仅限本小程序内的页面。支持带参数,(示例index?foo=bar)。该字段不填则模板无跳转
	private String page;
	// 表单提交场景下,为 submit 事件带上的 formId;支付场景下,为本次支付的 prepay_id
	private String form_id;
	// 模板需要放大的关键词,不填则默认无放大
	private String emphasis_keyword;
	// 参数列表
	private Map<String, TemplateParam> data;//推送文字

	public String getTouser() {
		return touser;
	}

	public void setTouser(String touser) {
		this.touser = touser;
	}

	public String getTemplate_id() {
		return template_id;
	}

	public void setTemplate_id(String template_id) {
		this.template_id = template_id;
	}

	public String getPage() {
		return page;
	}

	public void setPage(String page) {
		this.page = page;
	}

	public String getForm_id() {
		return form_id;
	}

	public void setForm_id(String form_id) {
		this.form_id = form_id;
	}

	public String getEmphasis_keyword() {
		return emphasis_keyword;
	}

	public void setEmphasis_keyword(String emphasis_keyword) {
		this.emphasis_keyword = emphasis_keyword;
	}

	public Map<String, TemplateParam> getData() {
		return data;
	}

	public void setData(Map<String, TemplateParam> data) {
		this.data = data;
	}
}
/**
 * @ClassName: TemplateParam
 * @Description:微信推送模版的关键词
 */
public class TemplateParam {

    // keyword1:物品名称  keyword2:金额  keyword3:下单时间
	private String value;

	public TemplateParam(String value) {
		super();
		this.value = value;
	}

	public String getValue() {
		return value;
	}

	public void setValue(String value) {
		this.value = value;
	}
}

3:mvc逻辑代码

@GetMapping(value = "/pay")
@ResponseBody
public ResponseEntity<Integer> pay(Domain domain) {
	try {

		// 封装推送实体类
		Template tem = new Template();
		// 推送微信用户的openid
		tem.setTouser(domain.getOpenid());
		// 表单ID
		tem.setForm_id(domain.getFormid());
		// 模板id
		tem.setTemplate_id(Constants.templateId);
		// 用户点击通知进入的小程序的页面
		tem.setPage(Constants.page);
		// 设置模板消息关键词
		Map<String, TemplateParam> params = new HashMap<String, TemplateParam>();
		params.put("keyword1", new TemplateParam("华为P20"));
		params.put("keyword2", new TemplateParam("4000.00元"));
		params.put("keyword3", new TemplateParam("2018-11-08 16:00"));
		tem.setData(params);

		// 推送消息
		if (StringUtils.isBlank(FlushToken.token))
			FlushToken.flushToken();
		CommonUtils.sendTemplateMsg(FlushToken.token, tem);

        //ReturnNumb.success实质上就是1,自己封装的,大家随意,别受误导了
		return new ResponseEntity<Integer>(ReturnNumb.option_success,
				HttpStatus.OK);
	} catch (Exception e) {
		e.printStackTrace();
	}
	return new ResponseEntity<Integer>(ReturnNumb.option_fail,
			HttpStatus.INTERNAL_SERVER_ERROR);
}

封装推送消息方法的 CommonUtils:

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.mote.pojo.Template;

public class CommonUtils {

	public static boolean sendTemplateMsg(String token, Template template)
			throws Exception {

		boolean flag = false;

		String requestUrl = "https://api.weixin.qq.com/cgi-bin/message/wxopen/template/send?access_token=ACCESS_TOKEN";
		requestUrl = requestUrl.replace("ACCESS_TOKEN", token);
		
		System.err.println(JSON.toJSONString(template));
		JSONObject jsonResult = HttpClientUtils.httpPost(requestUrl,
				JSON.toJSONString(template));

		if (jsonResult != null) {
			Integer errorCode = jsonResult.getIntValue("errcode");
			String errorMessage = jsonResult.getString("errmsg");
			if (errorCode == 0) {
				flag = true;
			} else {
				System.out
						.println("模板消息发送失败:" + errorCode + "," + errorMessage);
				flag = false;
			}
		}
		return flag;
	}

}

 

通知成功页面:

wxjava小程序 模版消息 微信小程序模板消息_小程序消息推送_04

 

tip1:该功能开发时必须在手机上调试,使用开发者工具调试会报 the formId is a mock one 

tip2:一个formid只能推送一次消息,推送后继续推送报 "errcode":41029,"errmsg":"form id used count reach limit hint: [9mUwja01342277]" 

tip3:formid的有效时间是7天,即获取了formid,7天内的任意时间有且只能推送一条消息给用户

tip4:可能开发者对用户1次触发7天内只能推送1条通知的设定很不爽,那就参考这篇文章突破这个限制吧