公众号推送消息是十分重要又基础的功能,这里主要记录一下微信公众号推送模板开发的相关流程和代码。
首先:要申请模板消息功能,选择主营行业和副营行业,并输入申请理由(相应的例子网上一大堆,这里不赘述了);
申请完了之后就感觉你选择的行业去模板库里面找类似需要的模板类型,模板库其实还是蛮全的,实在没有找到也可以自己申请新的模板,不过每个月有上限;
先上代码:
先选择一个模板(这里放一个简单的模板):
{{first.DATA}}
退款原因:{{reason.DATA}}
退款金额:{{refund.DATA}}
{{remark.DATA}}
原理就是按照模板的数据格式往里面传值,具体情况看代码
结果是这样的:
controller:
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
import com.edgecdj.domain.JsonResult;
import com.edgecdj.wxpublic.service.WxInfoService;
import com.edgecdj.wxpublic.utils.wxtemplate.ParkTemplate;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
/**
* 微信公众号相关接口
*
* @author cdj
*
*/
@Api("微信公众号相关接口")
@RestController("/WxPublic")
public class WxPublicController {
private Log log = LogFactory.getLog(WxPublicController.class);
@Autowired
ParkTemplate parkTemplate;
@ApiOperation("微信发送模板信息")
@ApiImplicitParams({
@ApiImplicitParam(paramType = "query", name = "openId", value = "openId", required = true, dataType = "String")
})
@PostMapping("/parkTemplateTest")
public JsonResult parkTemplateTest(String openId) {
try {
parkTemplate.parkRefundTemplate(openId, "reason--退款原因", "refund--退款金额");
return JsonResult.success("发送成功");
} catch (Exception e) {
// TODO: handle exception
log.error(e.getMessage());
return JsonResult.failMsg(e.getMessage());
}
}
}
建立实体类:
WechatTemplate:
package com.edgecdj.wxpublic.entity.wechattemplate;
import java.util.Map;
/**
* 微信消息模板实体类
* @author cdj
*/
public class WechatTemplate {
private String touser;
private String template_id;
private String url;
private Map<String, TemplateData> 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 getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public Map<String, TemplateData> getData() {
return data;
}
public void setData(Map<String, TemplateData> data) {
this.data = data;
}
}
TemplateData:
package com.edgecdj.wxpublic.entity.wechattemplate;
/**
* 模板数据
*
* @author cdj
*/
public class TemplateData {
private String value;
private String color;
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
}
新建工具类:
ParkTemplateMap:(key一定要和模板的可以一 一对应)
import com.edgecdj.wxpublic.entity.wechattemplate.TemplateData;
/**
* 消息模板获取
*
* @author cdj
* @date 2018年7月30日 上午8:38:04
*/
public class ParkTemplateMap {
/**
* 退款通知
{{first.DATA}}
退款原因:{{reason.DATA}}
退款金额:{{refund.DATA}}
{{remark.DATA}}
*/
public static HashMap<String, TemplateData> getParkRefundTemplateMap(String title, String reason, String refund,
String remarkstr) {
HashMap<String, TemplateData> mapdata = new HashMap<>();
// 封装模板数据
TemplateData first = new TemplateData();
first.setValue(title);
first.setColor("#173177");
mapdata.put("first", first);
TemplateData keyword1 = new TemplateData();
keyword1.setValue(reason);
keyword1.setColor("#173177");
mapdata.put("reason", keyword1);
TemplateData keyword2 = new TemplateData();
keyword2.setValue(refund);
keyword2.setColor("#173177");
mapdata.put("refund", keyword2);
TemplateData remark = new TemplateData();
remark.setValue(remarkstr);
remark.setColor("#173177");
mapdata.put("remark", remark);
return mapdata;
}
}
ParkTemplate:
import java.util.HashMap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.edgecdj.constant.WxPublicProperties;
import com.edgecdj.paydemo.wxpay.utils.http.HttpsUtil;
import com.edgecdj.wxpublic.entity.WxToken;
import com.edgecdj.wxpublic.entity.wechattemplate.TemplateData;
import com.edgecdj.wxpublic.entity.wechattemplate.WechatTemplate;
import com.edgecdj.wxpublic.utils.UserInfoUtil;
import com.edgecdj.wxpublic.utils.WxTemplateProperties;
/**
* 微信公众号车辆进出通知模板
*
* @author cdj
*/
@Component
public class ParkTemplate {
private Logger logger = LoggerFactory.getLogger(getClass());
/**
* 停车退款通知
*
* @param openId 用户openid
* @param reason 退款原因
* @param refund 退款金额
*/
public void parkRefundTemplate(String openId, String reason, String refund) {
String jssdkAcceTokenUrl = UserInfoUtil.getJssdkAcceTokenUrl(WxPublicProperties.APPID,
WxPublicProperties.APPSCREAT);
String accesstoken = HttpsUtil.httpsRequestToString(jssdkAcceTokenUrl, "GET", null);
WxToken accToken = JSONObject.parseObject(accesstoken, WxToken.class);
String templateSendUrl = UserInfoUtil.getTemplateSendUrl(accToken.getAccessToken());
WechatTemplate wechatTemplate = new WechatTemplate();
wechatTemplate.setTemplate_id(WxTemplateProperties.TEMPLATE_ID);
wechatTemplate.setTouser(openId);
//设置url的话会点击模板消息会跳到指定的url
wechatTemplate.setUrl(WxTemplateProperties.REFUND_URL);
HashMap<String, TemplateData> mapdata = new HashMap<>();
String title = "停车退款消息提醒";
String remark = "如有疑问,请联系客服。";
// 封装模板数据
mapdata = ParkTemplateMap.getParkRefundTemplateMap(title, reason, refund, remark);
wechatTemplate.setData(mapdata);
logger.info(JSON.toJSONString(wechatTemplate));
String resultJson = HttpsUtil.httpsRequestToString(templateSendUrl, "POST", JSON.toJSONString(wechatTemplate));
JSONObject resultJsonObject = JSONObject.parseObject(resultJson);
logger.info(JSON.toJSONString(resultJsonObject));
int result = 0;
if (null != resultJsonObject) {
if (0 != resultJsonObject.getIntValue("errcode")) {
result = resultJsonObject.getIntValue("errcode");
logger.error("错误 errcode:{} errmsg:{}", resultJsonObject.getIntValue("errcode"),
resultJsonObject.get("errmsg").toString());
}
}
}
}
-------------------------------------------------------- 手动分割 --------------------------------------------------------
还有几个方法前面我有发过的,这次再发一次好了
UserInfoUtil:
package com.edgecdj.wxpublic.utils;
import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Formatter;
import java.util.HashMap;
import java.util.UUID;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class UserInfoUtil {
private Logger logger = LoggerFactory.getLogger(getClass());
// 1.获取code的请求地址
public static String Get_Code = "https://open.weixin.qq.com/connect/oauth2/authorize?appid=%s&redirect_uri=%s&response_type=code&scope=%s&state=STAT#wechat_redirect";
// 替换字符串
public static String getCode(String APPID, String REDIRECT_URI, String SCOPE) {
return String.format(Get_Code, APPID, REDIRECT_URI, SCOPE);
}
// 2.获取Web_access_tokenhttps的请求地址
public static String Web_access_tokenhttps = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=%s&secret=%s&code=%s&grant_type=authorization_code";
// 替换字符串
public static String getWebAccess(String APPID, String SECRET, String CODE) {
return String.format(Web_access_tokenhttps, APPID, SECRET, CODE);
}
// 3.拉取用户信息的请求地址
public static String User_Message = "https://api.weixin.qq.com/sns/userinfo?access_token=%s&openid=%s&lang=zh_CN";
// 替换字符串
public static String getUserMessage(String access_token, String openid) {
return String.format(User_Message, access_token, openid);
}
// 微信JSSDK的AccessToken请求URL地址(全局access_token)
public final static String weixin_jssdk_acceToken_url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=%s&secret=%s";
// 微信JSSDK的ticket请求URL地址
public final static String weixin_jssdk_ticket_url = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=%s&type=jsapi";
// 微信发送消息模板请求的url地址
public final static String template_send_url = "https://api.weixin.qq.com/cgi-bin/message/template/send?access_token=%s";
// public final static String weixin_jssdk_ticket_url = "https://qyapi.weixin.qq.com/cgi-bin/get_jsapi_ticket?access_token=%s";
// 微信公众号素材列表获取
public final static String MATERIAL_URL = "https://api.weixin.qq.com/cgi-bin/material/batchget_material?access_token=%s";
// 微信公众号素材列表获取
public final static String GETMATERIAL_BYID_URL = "https://api.weixin.qq.com/cgi-bin/media/get?access_token=%s&media_id=%s";
// 微信公众号创建菜单按钮
public final static String CREATEBUTTON_URL = "https://api.weixin.qq.com/cgi-bin/menu/create?access_token=%s";
/**
* 获取微信公众创建菜单的url
*/
public static String getCreateButton_Url(String accessToken) {
return String.format(CREATEBUTTON_URL, accessToken);
}
/**
* MATERIAL_URL获取(素材列表)
*/
public static String getMaterial_Url(String accessToken) {
return String.format(MATERIAL_URL, accessToken);
}
public static String getMaterialById_Url(String accessToken, String media_id) {
return String.format(GETMATERIAL_BYID_URL, accessToken, media_id);
}
/**
* JSSDK AccessToken 获取(全局access_token)
*/
public static String getJssdkAcceTokenUrl(String appid, String secret) {
return String.format(weixin_jssdk_acceToken_url, appid, secret);
}
/**
* JSSDK ticket url 获取
*/
public static String getJsSdkTicketUrl(String access_token) {
return String.format(weixin_jssdk_ticket_url, access_token);
}
/**
* 微信发送消息模板请求的url地址 获取
*/
public static String getTemplateSendUrl(String access_token) {
return String.format(template_send_url, access_token);
}
/** JSSDK 获取签名等参数 */
public static HashMap<String, String> sign(String jsapi_ticket, String url) {
HashMap<String, String> ret = new HashMap<String, String>();
String nonce_str = create_nonce_str();
String timestamp = create_timestamp();
String string1;
String signature = "";
// 注意这里参数名必须全部小写,且必须有序
string1 = "jsapi_ticket=" + jsapi_ticket + "&noncestr=" + nonce_str + "×tamp=" + timestamp + "&url=" + url;
System.out.println(string1);
try {
// signature = new SHA1().getDigestOfString(string1.getBytes("utf-8"));
MessageDigest crypt = MessageDigest.getInstance("SHA-1");
crypt.reset();
crypt.update(string1.getBytes("UTF-8"));
signature = byteToHex(crypt.digest());
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
ret.put("url", url);
ret.put("jsapi_ticket", jsapi_ticket);
ret.put("nonceStr", nonce_str);
ret.put("timestamp", timestamp);
ret.put("signature", signature);
return ret;
}
private static String byteToHex(final byte[] hash) {
Formatter formatter = new Formatter();
for (byte b : hash) {
formatter.format("%02x", b);
}
String result = formatter.toString();
formatter.close();
return result;
}
private static String create_nonce_str() {
return UUID.randomUUID().toString();
}
private static String create_timestamp() {
return Long.toString(System.currentTimeMillis() / 1000);
}
}
HttpsUtil:
package com.edgecdj.paydemo.wxpay.utils.http;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.util.EntityUtils;
import java.io.*;
import java.net.URL;
public class HttpsUtil {
/**
* 以https方式发送请求并将请求响应内容以String方式返回
*
* @param path 请求路径
* @param method 请求方法
* @param body 请求数据体
* @return 请求响应内容转换成字符串信息
*/
public static String httpsRequestToString(String path, String method, String body) {
if (path == null || method == null) {
return null;
}
String response = null;
InputStream inputStream = null;
InputStreamReader inputStreamReader = null;
BufferedReader bufferedReader = null;
HttpsURLConnection conn = null;
try {
// 创建SSLConrext对象,并使用我们指定的信任管理器初始化
TrustManager[] tm = {new JEEWeiXinX509TrustManager()};
SSLContext sslContext = SSLContext.getInstance("SSL", "SunJSSE");
sslContext.init(null, tm, new java.security.SecureRandom());
// 从上述对象中的到SSLSocketFactory
SSLSocketFactory ssf = sslContext.getSocketFactory();
System.out.println(path);
URL url = new URL(path);
conn = (HttpsURLConnection) url.openConnection();
conn.setSSLSocketFactory(ssf);
conn.setDoOutput(true);
conn.setDoInput(true);
conn.setUseCaches(false);
//设置请求方式(git|post)
conn.setRequestMethod(method);
//有数据提交时
if (null != body) {
OutputStream outputStream = conn.getOutputStream();
outputStream.write(body.getBytes("UTF-8"));
outputStream.close();
}
// 将返回的输入流转换成字符串
inputStream = conn.getInputStream();
inputStreamReader = new InputStreamReader(inputStream, "UTF-8");
bufferedReader = new BufferedReader(inputStreamReader);
String str = null;
StringBuffer buffer = new StringBuffer();
while ((str = bufferedReader.readLine()) != null) {
buffer.append(str);
}
response = buffer.toString();
} catch (Exception e) {
} finally {
if (conn != null) {
conn.disconnect();
}
try {
bufferedReader.close();
inputStreamReader.close();
inputStream.close();
} catch (IOException execption) {
}
}
return response;
}
/**
* GET 请求 获得图片反馈
*/
public static byte[] getFileStream(String url){
HttpClient client = HttpClientBuilder.create().build();
RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(3000).setConnectTimeout(10000).build();
HttpGet request = new HttpGet(url);
request.setConfig(requestConfig);
byte[] res = null;
try {
HttpResponse response = client.execute(request);
byte[] fileStream = EntityUtils.toByteArray(response.getEntity());
return fileStream;
}catch (Exception ex){
}
return res;
}
}
应该就这些了,结果是这样的