小程式開發與公眾號使用者關聯推送訊息
最近做了一個類似於日程提醒的小程式,需要把使用者語音識別出的日程或提醒按照設定的提醒日期通過微信公眾號推送給對應的使用者進行提醒這樣子.這是需求背景.
這個專案我已經做完了,但是中間遇到很多坑,所以想出篇帖子,讓大家不浪費爬坑的時間.後面我會盡量把所有需要的程式碼什麼的,都貼上來,儘量做到拿了就能用的程度.
接下來,就是一個又一個的坑需要爬了.首先我說一下小程式與公眾號開發的一個完整流程.與一些細節.
1.小程式開發首先需要appid 與secret 也就是賬號密碼,用來獲取使用者的unionid與openid.(需要根據js_code來獲取)這兩個名叫id的東西,都是使用者的唯一標識,(以前不同的appid獲取到的使用者openid都是一樣的,現在不一樣了) unionid是幹什麼用的,我一會會說.
2.公眾號開發也需要一個appid跟secret,這個跟小程式的不一樣,具體怎麼拿,跟小程式一樣.有了appid與secret我們就可以獲取到公眾號使用者的openid與unionid了(需要根據code來獲取),(獲取之前還需要先獲取assess_token,這是一個微信的介面,用來驗證身份,微信設定一個開發者賬號每天可以請求200次的限制,每個token有7200s的有效時間...所以這裡獲取到的話,需要放到redis或者java快取中去,然後設定一個7200s的過期時間,用的時候從快取取,快取裡沒有再請求.後面我會說怎麼獲取)這裡的openid與小程式的openid是不一樣的,但是unionid可以弄成一樣的.怎麼弄呢,看第三步.
3.這時候應該上微信公眾平臺把小程式繫結到其下.然後再註冊一個微信開放平臺,並且獲取開發者資質,(一年好像是300).然後把你的小程式與公眾號都繫結到開放平臺下,這樣的話,你的小程式獲取到的unionid跟公眾號獲取到的unionid就是一樣的了,這樣就可以用它來進行小程式與公眾號使用者的關聯了.
------------------------------------------------------然後進行程式碼------------------------------------------------------
1.根據js_code獲取小程式使用者的openid與unionid (js_code 是小程式在呼叫wx.login的時候獲取到的.) 程式碼如下:
@RequestMapping(value = "/checkUserByOpenid", method = RequestMethod.GET)
public @ResponseBody Map checkUserByOpenid(HttpServletRequest request,HttpServletResponse response,
@RequestParam("js_code") String js_code) throws Exception {
response.setHeader("Access-Control-Allow-Origin", "*");
/*星號表示所有的域都可以接受,*/
response.setHeader("Access-Control-Allow-Methods", "GET,POST");
HashMap<String, Object> jsonMap = new HashMap<String, Object>();
// if(js_code!=null && !"".equals(js_code)){
String url = "https://api.weixin.qq.com/sns/jscode2session?appid=" + appid + "&secret=" + secret + "&js_code=" + js_code + "&grant_type=" + grant_type;
String httpsRtn = HttpsPostUtil.doPost(url, "UTF-8");
Map<String, Object> json = mapper.readValue(httpsRtn, Map.class);
String openid = (String) json.get("openid");
//用openid獲取unionid等敏感資訊------------小程式
String unionid = (String) json.get("unionid");
//此處開始你的邏輯程式碼
jsonMap.put("message", "success");
// }
return jsonMap;
}
2.httpsclient工具類 傳送請求給微信介面
package com.qs.util;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.util.EntityUtils;
/**
* 利用HttpClient進行post請求的工具類
*
* @author Kevin
* @ClassName: HttpsClientUtil
* @Description: TODO
* @date 2017年2月7日 下午1:43:38
*/
public class HttpsPostUtil {
public static String doPost(String url, String charset) {
HttpClient httpClient = null;
HttpPost httpPost = null;
String result = null;
try {
httpClient = new SSLClient();
httpPost = new HttpPost(url);
HttpResponse response = httpClient.execute(httpPost);
if (response != null) {
HttpEntity resEntity = response.getEntity();
if (resEntity != null) {
result = EntityUtils.toString(resEntity, charset);
}
}
} catch (Exception ex) {
ex.printStackTrace();
}
return result;
}
}
package com.qs.util;
import org.apache.http.conn.ClientConnectionManager;
import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.scheme.SchemeRegistry;
import org.apache.http.conn.ssl.SSLSocketFactory;
import org.apache.http.impl.client.DefaultHttpClient;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
/**
* 用於進行Https請求的HttpClient
*
* @author Kevin
* @ClassName: SSLClient
* @Description: TODO
* @date 2017年2月7日 下午1:42:07
*/
public class SSLClient extends DefaultHttpClient {
public SSLClient() throws Exception {
super();
SSLContext ctx = SSLContext.getInstance("TLS");
X509TrustManager tm = new X509TrustManager() {
public void checkClientTrusted(X509Certificate[] chain,
String authType) throws CertificateException {
}
public void checkServerTrusted(X509Certificate[] chain,
String authType) throws CertificateException {
}
public X509Certificate[] getAcceptedIssuers() {
return null;
}
};
ctx.init(null, new TrustManager[]{tm}, null);
SSLSocketFactory ssf = new SSLSocketFactory(ctx, SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
ClientConnectionManager ccm = this.getConnectionManager();
SchemeRegistry sr = ccm.getSchemeRegistry();
sr.register(new Scheme("https", 443, ssf));
}
}
3.然後再根據code獲取公眾號下使用者的openid與uninonid等個人資訊了,這裡要多說一下,這個code在小程式裡面是無法直接獲取的,我是在小程式wx.login後呼叫了web view網頁彈出 來引導使用者進行授權的,這裡大家可以參考一下微信的官方說明文件,需要配置一個伺服器域名 或 測試用機的IP地址白名單,還要配置一個授權成功後回撥的介面地址,也就是你自己寫的介面,這個介面中就可以獲取到code,code是從你的授權網頁傳遞過來的,在網頁上寫一個onload預設載入,把頁面位址列裡接收到的微信傳來的code引數拿出來,傳到自己的介面中去..下面我把介面中根據code獲取微信公眾平臺使用者的個人資訊程式碼貼上
@RequestMapping(value = "/callBack", method = RequestMethod.GET)
public @ResponseBody void callBack(HttpServletRequest request,HttpServletResponse response) throws Exception {
response.setHeader("Access-Control-Allow-Origin", "*");
/*星號表示所有的域都可以接受,*/
response.setHeader("Access-Control-Allow-Methods", "GET,POST");
//使用code獲取公眾號使用者的openid------------
String urls = "https://api.weixin.qq.com/sns/oauth2/access_token?appid="+appId+"&secret="+appSecret+"&code="+code+"&grant_type=authorization_code";
JSONObject result = WX_HttpsUtil.httpsRequest(urls, "GET");
JSONObject resultJson = new JSONObject(result);
String GZHopenid = (String) resultJson.get("openid");
String errmsg = (String) resultJson.get("errmsg");
//如果code失效 則不新增使用者
if(errmsg==null){
//使用openid獲取unionid
String access_token = WX_TokenUtil.getWXToken().getAccessToken();
String url2 = "https://api.weixin.qq.com/cgi-bin/user/info?access_token="+access_token+"&openid="+GZHopenid+"&lang=zh_CN";
JSONObject result2 = WX_HttpsUtil.httpsRequest(url2, "GET");
JSONObject resultJson2 = new JSONObject(result2);
String GZHunionid = (String) resultJson.get("unionid");
}
}
---------------------------------------------------------------------------------------------
---------------------------------------------------------------------------------------------
到這裡為止,你的公眾號與小程式的使用者的個人資訊就全部獲取到了,可以通過unionid關聯起來了...
下面開始通過微信公眾平臺來給使用者推送訊息.
ps:這裡需要登入微信公眾平臺先設定一個自己需要推送的訊息模板,可以從微信提供的模板庫中選取,如果沒有合適的,就自己申請一個模板,然後把模板ID複製貼上到程式碼中.
如下幾個都是需要的工具類
package weixin.util.wxservlet;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
/**
* 請求校驗工具類
* @author m
*
*/
public class SignUtil {
// 與介面配置資訊中的Token要一致
private static String token = "weixin";
/**
* 驗證簽名
*
* @param signature
* @param timestamp
* @param nonce
* @return
*/
public static boolean checkSignature(String signature, String timestamp, String nonce) {
String[] arr = new String[] { token, timestamp, nonce };
// 將token、timestamp、nonce三個引數進行字典序排序
Arrays.sort(arr);
StringBuilder content = new StringBuilder();
for (int i = 0; i < arr.length; i++) {
content.append(arr[i]);
}
MessageDigest md = null;
String tmpStr = null;
try {
md = MessageDigest.getInstance("SHA-1");
// 將三個引數字串拼接成一個字串進行sha1加密
byte[] digest = md.digest(content.toString().getBytes());
tmpStr = byteToStr(digest);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
content = null;
// 將sha1加密後的字串可與signature對比,標識該請求來源於微信
return tmpStr != null ? tmpStr.equals(signature.toUpperCase()) : false;
}
/**
* 將位元組陣列轉換為十六進位制字串
*
* @param byteArray
* @return
*/
private static String byteToStr(byte[] byteArray) {
String strDigest = "";
for (int i = 0; i < byteArray.length; i++) {
strDigest += byteToHexStr(byteArray[i]);
}
return strDigest;
}
/**
* 將位元組轉換為十六進位制字串
*
* @param mByte
* @return
*/
private static String byteToHexStr(byte mByte) {
char[] Digit = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
char[] tempArr = new char[2];
tempArr[0] = Digit[(mByte >>> 4) & 0X0F];
tempArr[1] = Digit[mByte & 0X0F];
String s = new String(tempArr);
return s;
}
}
package weixin.util.wxservlet;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class WxServlet extends HttpServlet{
public WxServlet() {
// TODO Auto-generated constructor stub
}
/* (non-Javadoc)
* @see javax.servlet.http.HttpServlet#doGet(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
*/
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 微信加密簽名
String signature = request.getParameter("signature");
// 時間戳
String timestamp = request.getParameter("timestamp");
// 隨機數
String nonce = request.getParameter("nonce");
// 隨機字串
String echostr = request.getParameter("echostr");
PrintWriter out = response.getWriter();
// 通過檢驗signature對請求進行校驗,若校驗成功則原樣返回echostr,表示接入成功,否則接入失敗
if (SignUtil.checkSignature(signature, timestamp, nonce)) {
out.print(echostr);
}
out.close();
out = null;
}
/* (non-Javadoc)
* @see javax.servlet.http.HttpServlet#doPost(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
*/
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// TODO Auto-generated method stub
super.doPost(req, resp);
}
}
package weixin.util;
import java.io.Serializable;
/*
*AccessToken 物件
*/
public class AccessToken implements Serializable {
//獲取到的憑證
private String accessToken;
//憑證有效時間,單位:秒
private int expiresin;
/**
* @return the accessToken
*/
public String getAccessToken() {
return accessToken;
}
/**
* @param accessToken the accessToken to set
*/
public void setAccessToken(String accessToken) {
this.accessToken = accessToken;
}
/**
* @return the expiresin
*/
public int getExpiresin() {
return expiresin;
}
/**
* @param expiresin the expiresin to set
*/
public void setExpiresin(int expiresin) {
this.expiresin = expiresin;
}
public AccessToken() {
// TODO Auto-generated constructor stub
}
}
package weixin.util;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import javax.net.ssl.X509TrustManager;
/**
* 微信請求 - 信任管理器
*/
public class MyX509TrustManager implements X509TrustManager{
public MyX509TrustManager() {
// TODO Auto-generated constructor stub
}
public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
}
public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
}
public X509Certificate[] getAcceptedIssuers() {
// return new X509Certificate[0];
return null;
}
}
package weixin.util;
/**
* 模板詳細資訊
* 根據需求自己更改
*/
public class TemplateData {
private String value;
private String color;
public TemplateData(String value,String color){
this.value = value;
this.color = color;
}
/**
* @return the value
*/
public String getValue() {
return value;
}
/**
* @param value the value to set
*/
public void setValue(String value) {
this.value = value;
}
/**
* @return the color
*/
public String getColor() {
return color;
}
/**
* @param color the color to set
*/
public void setColor(String color) {
this.color = color;
}
}
package weixin.util;
import java.util.Map;
/**
* 註冊成功,通知模板訊息實體類
*/
public class TemplateMessage {
private String touser; //使用者OpenID
private String template_id; //模板訊息ID
private String url; //URL置空,在傳送後,點模板訊息進入一個空白頁面(ios),或無法點選(android)。
private String topcolor; //標題顏色
private Map<String, TemplateData> templateData; //模板詳細資訊
public static TemplateMessage New() {
return new TemplateMessage();
}
/**
* @return the touser
*/
public String getTouser() {
return touser;
}
/**
* @param touser the touser to set
*/
public void setTouser(String touser) {
this.touser = touser;
}
/**
* @return the template_id
*/
public String getTemplate_id() {
return template_id;
}
/**
* @param template_id the template_id to set
*/
public void setTemplate_id(String template_id) {
this.template_id = template_id;
}
/**
* @return the url
*/
public String getUrl() {
return url;
}
/**
* @param url the url to set
*/
public void setUrl(String url) {
this.url = url;
}
/**
* @return the topcolor
*/
public String getTopcolor() {
return topcolor;
}
/**
* @param topcolor the topcolor to set
*/
public void setTopcolor(String topcolor) {
this.topcolor = topcolor;
}
/**
* @return the templateData
*/
public Map<String, TemplateData> getTemplateData() {
return templateData;
}
/**
* @param templateData the templateData to set
*/
public void setTemplateData(Map<String, TemplateData> templateData) {
this.templateData = templateData;
}
}
package weixin.util;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.ConnectException;
import java.net.URL;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.alibaba.fastjson.JSONObject;
public class WX_HttpsUtil {
private static Logger log = LoggerFactory.getLogger(WX_HttpsUtil.class);
/**
* 傳送https請求
* @param requestUrl 請求地址
* @param requestMethod 請求方式(GET、POST)
* @param outputStr 提交的資料
* @return JSONObject(通過JSONObject.get(key)的方式獲取json物件的屬性值)
*/
public static JSONObject httpsRequest(String requestUrl, String requestMethod, String outputStr) {
JSONObject jsonObject = null;
try {
// 建立SSLContext物件,並使用我們指定的信任管理器初始化
TrustManager[] tm = { new MyX509TrustManager() };
SSLContext sslContext = SSLContext.getInstance("SSL", "SunJSSE");
sslContext.init(null, tm, new java.security.SecureRandom());
// 從上述SSLContext物件中得到SSLSocketFactory物件
SSLSocketFactory ssf = sslContext.getSocketFactory();
URL url = new URL(requestUrl);
HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();
conn.setSSLSocketFactory(ssf);
conn.setDoOutput(true);
conn.setDoInput(true);
conn.setUseCaches(false);
// 設定請求方式(GET/POST)
conn.setRequestMethod(requestMethod);
// 當outputStr不為null時向輸出流寫資料
if (null != outputStr) {
OutputStream outputStream = conn.getOutputStream();
// 注意編碼格式
outputStream.write(outputStr.getBytes("UTF-8"));
outputStream.close();
}
// 從輸入流讀取返回內容
InputStream inputStream = conn.getInputStream();
InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "utf-8");
BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
String str = null;
StringBuffer buffer = new StringBuffer();
while ((str = bufferedReader.readLine()) != null) {
buffer.append(str);
}
// 釋放資源
bufferedReader.close();
inputStreamReader.close();
inputStream.close();
inputStream = null;
conn.disconnect();
jsonObject = JSONObject.parseObject(buffer.toString());
} catch (ConnectException ce) {
log.error("連線超時:{}", ce);
} catch (Exception e) {
log.error("https請求異常:{}", e);
}
return jsonObject;
}
}
package weixin.util;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.alibaba.fastjson.JSONObject;
public class WX_TemplateMsgUtil {
private static Logger log = LoggerFactory.getLogger(WX_TemplateMsgUtil.class);
/**
* 封裝模板詳細資訊
* @return
*/
public static JSONObject packJsonmsg(Map<String, TemplateData> param) {
JSONObject json = new JSONObject();
for (Map.Entry<String,TemplateData> entry : param.entrySet()) {
JSONObject keyJson = new JSONObject();
TemplateData dta= entry.getValue();
keyJson.put("value",dta.getValue());
keyJson.put("color", dta.getColor());
json.put(entry.getKey(), keyJson);
}
return json;
}
/**
* 根據模板的編號 新增並獲取模板ID
* @param templateSerialNumber 模板庫中模板的 "編號"
* @return 模板ID
*/
public static String getWXTemplateMsgId(String templateSerialNumber){
String tmpurl = "https://api.weixin.qq.com/cgi-bin/template/api_add_template?access_token="+ WX_TokenUtil.getWXToken().getAccessToken();
JSONObject json = new JSONObject();
json.put("template_id_short", templateSerialNumber);
JSONObject result = WX_HttpsUtil.httpsRequest(tmpurl, "POST", json.toString());
JSONObject resultJson = new JSONObject(result);
String errmsg = (String) resultJson.get("errmsg");
log.info("獲取模板編號返回資訊:" + errmsg);
if(!"ok".equals(errmsg)){
return "error";
}
String templateId = (String) resultJson.get("template_id");
return templateId;
}
/**
* 根據模板ID 刪除模板訊息
* @param templateId 模板ID
* @return
*/
public static String deleteWXTemplateMsgById(String templateId){
String tmpurl = "https://api.weixin.qq.com/cgi-bin/template/del_private_template?access_token="+ WX_TokenUtil.getWXToken().getAccessToken();
JSONObject json = new JSONObject();
json.put("template_id", templateId);
try{
JSONObject result = WX_HttpsUtil.httpsRequest(tmpurl, "POST", json.toString());
JSONObject resultJson = new JSONObject(result);
log.info("刪除"+templateId+"模板訊息,返回CODE:"+ resultJson.get("errcode"));
String errmsg = (String) resultJson.get("errmsg");
if(!"ok".equals(errmsg)){
return "error";
}
}catch(Exception e){
e.printStackTrace();
}
return "success";
}
/**
* 傳送微信訊息(模板訊息)
* @param touser 使用者 OpenID
* @param templatId 模板訊息ID
* @param clickurl URL置空,則在傳送後,點選模板訊息會進入一個空白頁面(ios),或無法點選(android)。
* @param topcolor 標題顏色
* @param data 詳細內容
* @return
*/
public static String sendWechatMsgToUser(String touser, String templatId, String clickurl, String topcolor, JSONObject data) {
String tmpurl = "https://api.weixin.qq.com/cgi-bin/message/template/send?access_token="+ WX_TokenUtil.getWXToken().getAccessToken();
JSONObject json = new JSONObject();
json.put("touser", touser);
json.put("template_id", templatId);
json.put("url", clickurl);
json.put("topcolor", topcolor);
json.put("data", data);
try{
JSONObject result = WX_HttpsUtil.httpsRequest(tmpurl, "POST", json.toString());
JSONObject resultJson = new JSONObject(result);
log.info("傳送微信訊息返回資訊:" + resultJson.get("errcode"));
String errmsg = (String) resultJson.get("errmsg");
if(!"ok".equals(errmsg)){ //如果為errmsg為ok,則代表傳送成功,公眾號推送資訊給使用者了。
return "error";
}
}catch(Exception e){
e.printStackTrace();
return "error";
}
// finally {
// if(templatId!=null) {
// //刪除新增的 微信模板
// deleteWXTemplateMsgById(templatId);
// }
// }
return "success";
}
}
package weixin.util;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.alibaba.fastjson.JSONException;
import com.alibaba.fastjson.JSONObject;
public class WX_TokenUtil {
private static Logger log = LoggerFactory.getLogger(WX_TokenUtil.class);
/**
* 獲得微信 AccessToken
* access_token是公眾號的全域性唯一介面呼叫憑據,公眾號呼叫各介面時都需使用access_token。
* 開發者需要access_token的有效期目前為2個小時,需定時重新整理,重複獲取將導致上次獲取
* 的access_token失效。
* (此處我是把token存在java快取裡面了)此程式碼token沒有加入快取,後面附了快取的工具類,根據需求自己新增
*/
public static AccessToken getWXToken() {
String appId="****************";
String appSecret="****************";
String tokenUrl = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid="+ appId+"&secret="+ appSecret;
JSONObject jsonObject = WX_HttpsUtil.httpsRequest(tokenUrl, "GET", null);
System.out.println("jsonObject:"+jsonObject);
AccessToken access_token = new AccessToken();
if (null != jsonObject) {
try {
access_token.setAccessToken(jsonObject.getString("access_token"));
access_token.setExpiresin(jsonObject.getInteger("expires_in"));
} catch (JSONException e) {
access_token = null;
// 獲取token失敗
log.error("獲取token失敗 errcode:{} errmsg:{}", jsonObject.getInteger("errcode"), jsonObject.getString("errmsg"));
}
}
return access_token;
}
}
package weixin.util;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.alibaba.fastjson.JSONObject;
public class WX_UserUtil {
private static Logger log = LoggerFactory.getLogger(WX_UserUtil.class);
/**
* 根據微信openId 獲取使用者是否訂閱
* @param openId 微信openId
* @return 是否訂閱該公眾號標識
*/
public static Integer subscribeState(String openId){
String tmpurl = "https://api.weixin.qq.com/cgi-bin/user/info?access_token="+WX_TokenUtil.getWXToken().getAccessToken() +"&openid="+openId;
JSONObject result = WX_HttpsUtil.httpsRequest(tmpurl, "GET",null);
JSONObject resultJson = new JSONObject(result);
log.error("獲取使用者是否訂閱 errcode:{} errmsg:{}", resultJson.getInteger("errcode"), resultJson.getString("errmsg"));
String errmsg = (String) resultJson.get("errmsg");
System.out.println(errmsg);
if(errmsg==null){
//使用者是否訂閱該公眾號標識(0代表此使用者沒有關注該公眾號 1表示關注了該公眾號)。
Integer subscribe = (Integer) resultJson.get("subscribe");
return subscribe;
}
return 0;
}
public static JSONObject getUserInfo(String openId){
String tmpurl = "https://api.weixin.qq.com/cgi-bin/user/info?access_token="+WX_TokenUtil.getWXToken().getAccessToken() +"&openid="+openId;
JSONObject result = WX_HttpsUtil.httpsRequest(tmpurl, "GET",null);
JSONObject resultJson = new JSONObject(result);
log.error("獲取使用者是否訂閱 errcode:{} errmsg:{}", resultJson.getInteger("errcode"), resultJson.getString("errmsg"));
String errmsg = (String) resultJson.get("errmsg");
return resultJson;
}
}
package cache.util;
import java.util.Calendar;
import java.util.Map;
import java.util.Set;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.ConcurrentHashMap;
/**
* 簡單記憶體快取管理器
* 可設定過期時間,單位毫秒
* 使用Timer定時清理過期資料,每分鐘清理一次,可修改清理週期
* @author Kevin
* @datetime 2018年4月16日 下午5:18:50
*/
public class CacheManager {
@SuppressWarnings("rawtypes")
private static Map<String, CacheData> cache = new ConcurrentHashMap();
/**
* 啟動定時任務清理過期快取,避免記憶體溢位
*/
static {
Timer t = new Timer();
t.schedule(new ClearTimerTask(cache), 0, 7000 * 1000);
}
/**
* 設定快取,不過期
* @param key
* @param t
*/
public static <T> void set(String key, T t) {
cache.put(key, new CacheData(t, 0));
}
/**
* 設定快取,指定過期時間expire(單位毫秒)
* @param key
* @param t
* @param expire 過期時間
*/
public static <T> void set(String key, T t, long expire) {
cache.put(key, new CacheData(t, expire));
}
/**
* 根據key獲取指定快取
* @param key
* @return
*/
@SuppressWarnings("unchecked")
public static <T> T get(String key) {
CacheData<T> data = cache.get(key);
if(null == data) {
return null;
}
if(data.isExpire()) {
remove(key);
return null;
}
return data.getData();
}
/**
* 移除指定key快取
* @param key
*/
public static void remove(String key) {
cache.remove(key);
}
/**
* 移除所有快取
*/
public static void removeAll() {
cache.clear();
}
private static class CacheData<T> {
// 快取資料
private T data;
// 過期時間(單位,毫秒)
private long expireTime;
public CacheData(T t, long expire) {
this.data = t;
if(expire <= 0) {
this.expireTime = 0L;
} else {
this.expireTime = Calendar.getInstance().getTimeInMillis() + expire;
}
}
/**
* 判斷快取資料是否過期
* @return true表示過期,false表示未過期
*/
public boolean isExpire() {
if(expireTime <= 0) {
return false;
}
if(expireTime > Calendar.getInstance().getTimeInMillis()) {
return false;
}
return true;
}
public T getData() {
return data;
}
}
/**
* 清理過期資料定時任務
* @author zsc
* @datetime 2018年2月9日 上午10:41:18
*/
private static class ClearTimerTask extends TimerTask {
@SuppressWarnings("rawtypes")
Map<String, CacheData> cache;
@SuppressWarnings("rawtypes")
public ClearTimerTask(Map<String, CacheData> cache) {
this.cache = cache;
}
@Override
public void run() {
Set<String> keys = cache.keySet();
for(String key : keys) {
CacheData<?> data = cache.get(key);
if(data.expireTime <= 0) {
continue;
}
if(data.expireTime > Calendar.getInstance().getTimeInMillis()) {
continue;
}
cache.remove(key);
}
}
}
}
最後進行測試傳送推送訊息
package weixin.util;
import java.util.HashMap;
import java.util.Map;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
public class TestWX {
public static void main(String[] args) {
//新增使用者成功 - 推送微信訊息
senMsg("openid 以及幾個需要的模板訊息引數,這個根據自己需求來修改");
}
public static void senMsg(String openId,String name,String event,String times,String remark){
//使用者是否訂閱該公眾號標識 (0代表此使用者沒有關注該公眾號 1表示關注了該公眾號)
Integer state= WX_UserUtil.subscribeState(openId);
System.out.println("state:"+state);
// 綁定了微信並且關注了服務號的使用者 , 註冊成功-推送註冊簡訊
if(state==1){
Map<String,TemplateData> param = new HashMap();
param.put("first",new TemplateData("您好,親愛的"+name+"!","#EE0000"));
param.put("keyword1",new TemplateData(event,"#EE0000"));
param.put("keyword2",new TemplateData(times,"#EE0000"));
param.put("remark",new TemplateData(remark,"#EE0000"));
//註冊的微信-模板Id
// String regTempId = WX_TemplateMsgUtil.getWXTemplateMsgId("TM00464");
JSON.toJSONString(param);
JSONObject jsonObject = JSONObject.parseObject(JSON.toJSONString(param));
//呼叫傳送微信訊息給使用者的介面 ********這裡寫自己在微信公眾平臺拿到的模板ID
WX_TemplateMsgUtil.sendWechatMsgToUser(openId, ***********************************, "",
"#000000", jsonObject);
//獲取公眾號的自動回覆規則
String urlinfo="https://api.weixin.qq.com/cgi-bin/get_current_autoreply_info?access_token="+WX_TokenUtil.getWXToken().getAccessToken();
JSONObject joinfo = WX_HttpsUtil.httpsRequest(urlinfo, "GET", null);
Object o=joinfo.get("is_add_friend_reply_open");
// System.out.println("o:"+joinfo);
String getTokenUrl = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid={0}&secret={1}";
JSONObject Token = WX_HttpsUtil.httpsRequest(getTokenUrl, "GET", null);
System.out.println("Token:"+Token);
}
}
}
此時你應該可以成功推送訊息了.如上程式碼我確保都是經過測試的,完全可以使用沒有問題
帖子寫的匆忙,如果任何疑問,請聯絡464744895