小程序官方关于模板消息
STEP 1:微信后台添加模板消息
1:选用模板 【功能】 ▶ 【模板消息】 ▶ 【模板库】 ▶【选用】
2:通过配置关键词来定义模板样式
3:点击提交,配置模板成功(该模板ID很重要,后面需要使用)
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 | | 是 | |
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;
}
}
通知成功页面:
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条通知的设定很不爽,那就参考这篇文章突破这个限制吧