微信订阅通知实战
前言
一、微信訂閱通知是什么?
二、使用步驟
1.開通訂閱通知服務
2.獲取access_token
我們微信業務和內部業務項目是分離的,所以只在微信項目中實現接口供內部項目調用即可.具體的接口如下
接口完成后,在業務項目中調用接口即可.
總結
前言
截止至2021-11-2, 微信的訂閱通知還在灰度測試當中,傳說微信的模板消息即將下線,所以采用微信的訂閱通知來發送通知.??本文為實戰中的微信訂閱通知功能開發記錄, 目前已經在線上測試使用,僅供參考.
一、微信訂閱通知是什么?
模板消息被濫用, 很多用戶被騷擾, 微信為了管理,引入訂閱通知, 分為一次性訂閱,和長期訂閱兩種,一次性訂閱: 訂閱一次,能給用戶發送一條消息.? 長期訂閱: 訂閱一次, 目前來看無限次發送消息. 本文為長期訂閱案例, 一次性訂閱對于我們來說沒有價值.
二、使用步驟
1.開通訂閱通知服務
根據微信文檔開通即可, 需要提供公司的 <<資質授權文件>>,審核幾個小時一般就會有結果,通過后只能選取微信提供的模板,長期訂閱的模板很少,就幾個,不一定符合業務需求,只能湊合用.
2.獲取access_token
? ? ?必須操作!
? ? ?2.??AccessToken 類接收 "獲取access_token接口"
@Data public class AccessToken {private String accessToken;private Long expiresIn; }? ? ? 3.WeixinToken? 內存中保存access_token?
/*** @Title: WeixinToken* @Description: 持有微信Access_token的對象* @date 2021-10-25 11:09*/ public class WeixinToken {public static String token;}? ? ? ? 4.WeixinAccessTokenUtil 獲取Access_token
@Component public class WeixinAccessTokenUtil {private Logger logger = LoggerFactory.getLogger(WeixinAccessTokenUtil.class);public AccessToken getToken(String appid, String appSecrect) {AccessToken token = new AccessToken();String url = "https://api.weixin.qq.com/cgi-bin/token" + "?grant_type=client_credential&appid=" + appid+ "&secret=" + appSecrect;HttpClient httpClient = HttpClientBuilder.create().build();HttpGet httpGet = new HttpGet(url);ResponseHandler<String> responseHandler = new BasicResponseHandler();try {String response = httpClient.execute(httpGet,responseHandler);JsonObject returnData = new JsonParser().parse(response).getAsJsonObject();if(returnData != null){token.setAccessToken(returnData.get("access_token").getAsString());token.setExpiresIn(returnData.get("expires_in").getAsLong());}} catch (IOException e) {token = null;e.printStackTrace();logger.error("系統獲取access_token出錯了!");}return token;}}5.開啟spring定時任務? 加入@EnableScheduling 注解
@SpringBootApplication @ServletComponentScan @EnableScheduling public class WeixinApplication {public static void main(String[] args) {SpringApplication.run(WeixinApplication.class, args);}}6.定時任務獲取access_token (倆小時失效,必須倆小時之內更新access_token)
package com.qqrw.wx.service.impl;import com.qqrw.wx.entity.WeixinToken; import com.qqrw.wx.util.WeixinAccessTokenUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component;/*** @Title: AccessTokenTask* @Description: 獲取微信access_token 定時任務* @date 2021-10-25 11:10*/ @Component public class AccessTokenTask {private Logger logger = LoggerFactory.getLogger(AccessTokenTask.class);@Autowiredprivate WeixinAccessTokenUtil weixinAccessTokenUtil;@Value("${wechat.appId}")private String appId;@Value("${wechat.appSecret}")private String appSecret;@Scheduled(initialDelay = 1000, fixedDelay = 7000 * 1000)public void getWeixinAccessToken() {try {String token = weixinAccessTokenUtil.getToken(appId, appSecret).getAccessToken();WeixinToken.token = token;logger.info("獲取到的微信accessToken為" + token);} catch (Exception e) {logger.error("獲取微信adcessToken出錯,信息如下");e.printStackTrace();}}@Scheduled(initialDelay = 3000, fixedDelay = 1000 * 1000)public void testWeixinAccessT() {logger.info(WeixinToken.token);} }7.其中http發送用的是httpclient, 如果沒遺漏的話,就不需要其他依賴了.
<dependency><groupId>org.apache.httpcomponents</groupId><artifactId>httpclient</artifactId><version>${httpClient.version}</version></dependency>我們微信業務和內部業務項目是分離的,所以只在微信項目中實現接口供內部項目調用.具體的接口如下
1.?WeixinMsg 接口用來接收業務相關數據
@Data public class WeixinMsg {private String userCode;private String templateId;private Map<String,Object> map;public String getUserCode() {return userCode;}public void setUserCode(String userCode) {this.userCode = userCode;}public String getTemplateId() {return templateId;}public void setTemplateId(String templateId) {this.templateId = templateId;}public Map<String, Object> getMap() {return map;}public void setMap(Map<String, Object> map) {this.map = map;} }2.Controller中 , 其中R是自定義的返回結果實體,替換為你自己的返回結果Result即可!
//發送微信訂閱消息@CrossOrigin(maxAge = 3600, origins = "*")@PostMapping("/subscribe/sendWeixinMsg")public R sendWeixinMsg(@RequestBody WeixinMsg weixinMsg) {Assert.isTrue(null != weixinMsg.getUserCode() && null != weixinMsg.getTemplateId() && null != weixinMsg.getMap(), "參數不正確");return weixinSendMsgService.send(weixinMsg);}3.接口
public interface WeixinSendMsgService {R send(WeixinMsg weixinMsg);}4.service代碼,
UserWechatMapper 和 其中String openId = userWechatMapper.queryOpenIdByUserCode(userCode);是我們有專門頁面,實現微信openId和業務之間Id的綁定.?可根據實際情況, 只要知道微信用戶的openId即可,具體獲取openId的功能,參考官方文檔!
ImSmsWeixinMapper 和下方的private方法是業務代碼,記錄發送的日志,屏蔽或改成你自己的業務代碼即可.
@Service("weixinSendMsgService") public class WeixinSendMsgServiceImpl implements WeixinSendMsgService {private Logger logger = LoggerFactory.getLogger(WeixinSendMsgServiceImpl.class);@Resourceprivate UserWechatMapper userWechatMapper;@Resourceprivate ImSmsWeixinMapper imSmsWeixinMapper;@Overridepublic R send(WeixinMsg weixinMsg) {System.out.println(weixinMsg);Date now = new Date();String userCode = weixinMsg.getUserCode();String openId = userWechatMapper.queryOpenIdByUserCode(userCode);String templateId = weixinMsg.getTemplateId();Map<String, Object> map = weixinMsg.getMap();Map<String, Map<String, Object>> data = new HashMap<>();if (StringUtils.isBlank(openId)) {logger.info("對用戶{}發送模板{}消息失敗,用戶未綁定無法獲取openId!", userCode, templateId);imSmsWeixinMapper.insertSelective(getFailLog(now, userCode, openId, templateId, data, "用戶未綁定,無法發送模板消息"));return R.fail("微信消息發送失敗,用戶未綁定");}for (Map.Entry<String, Object> entry : map.entrySet()) {HashMap<String, Object> one = new HashMap<>();one.put("value", entry.getValue());data.put(entry.getKey(), one);}System.out.println(openId);System.out.println(data);String url = "https://api.weixin.qq.com/cgi-bin/message/subscribe/bizsend?access_token=" + WeixinToken.token;try {HttpClient httpClient = HttpClientBuilder.create().build();HttpPost httpPost = new HttpPost(url);httpPost.setHeader("Accept", "application/json");httpPost.setHeader("Content-Type", "application/json");String charSet = "UTF-8";Map<String, Object> params = new HashMap<>();params.put("access_token", WeixinToken.token);params.put("touser", openId);params.put("template_id", templateId);params.put("data", data);StringEntity entity = new StringEntity(JsonUtils.toJson(params), charSet);httpPost.setEntity(entity);ResponseHandler<String> responseHandler = new BasicResponseHandler();String response = httpClient.execute(httpPost, responseHandler);JsonObject returnData = new JsonParser().parse(response).getAsJsonObject();int errcode = returnData.get("errcode").getAsInt();String errmsg = returnData.get("errmsg").getAsString();if ("ok".equals(errmsg)) {imSmsWeixinMapper.insertSelective(getSuccessLog(now, userCode, openId, templateId, data));logger.info("對用戶{}發送模板{}消息成功!", userCode, templateId);} else {logger.error("對用戶{}發送模板{}消息異常!", userCode, templateId);logger.error("發送數據為:[{}]!", data);logger.error("錯誤碼:[{}]!", errcode);logger.error("錯誤信息:[{}]!", errmsg);imSmsWeixinMapper.insertSelective(getFailLog(now, userCode, openId, templateId, data, errmsg));return R.fail("微信消息發送失敗,錯誤碼" + errcode);}} catch (IOException e) {e.printStackTrace();logger.error("對用戶{}發送模板{}消息異常!", userCode, templateId);logger.error("發送數據為:[{}]!", data);imSmsWeixinMapper.insertSelective(getFailLog(now, userCode, openId, templateId, data, "程序發送出現了異常,請檢查" + e.getMessage()));}return R.ok();}private ImSmsWeixin getSuccessLog(Date now, String userCode, String openId, String templateId, Map<String, Map<String, Object>> data) {ImSmsWeixin imSmsWeixin = genLog(now, userCode, openId, templateId, data);imSmsWeixin.setState(1);return imSmsWeixin;}private ImSmsWeixin getFailLog(Date now, String userCode, String openId, String templateId, Map<String, Map<String, Object>> data, String errorMsg) {ImSmsWeixin imSmsWeixin = genLog(now, userCode, openId, templateId, data);imSmsWeixin.setState(2);imSmsWeixin.setErrorMsg(errorMsg);return imSmsWeixin;}private ImSmsWeixin genLog(Date now, String userCode, String openId, String templateId, Map<String, Map<String, Object>> data) {ImSmsWeixin imSmsWeixin = new ImSmsWeixin();imSmsWeixin.setUserCode(userCode);imSmsWeixin.setOpenId(openId);imSmsWeixin.setTemplateId(templateId);imSmsWeixin.setData(JsonUtils.toJson(data));imSmsWeixin.setSendTime(now);return imSmsWeixin;}}5. 一個問題:??開發過程中遇到? ? ?接口提示{"errcode":47001,"errmsg":"data.......}的問題, 檢查過json, 網上說單引號雙引號的問題,? ?我測試不是引號的問題,只要json格式正確即可.? 主要是發送的方式問題.? 開始的時候用的是UrlEncodeEntity這種方式發送的, 導致出現了47001問題, 上面代碼發送方式完全沒問題
6. 我在官網上找了,沒找到可以提供測試的接口和方法, 只有線上測試了,? 發送一個圖文消息,? 在文章末尾插入訂閱消息插件 如圖
?7.用戶在文章中點擊訂閱通知,就能選擇相關訂閱, 訂閱后,再點擊訂閱就不再彈出訂閱框了.
接口完成后,在業務項目中調用接口即可.
public class WeixinMsgUtil {private static HttpClient httpClient = new DefaultHttpClient();public static void sendWeixinSms(String userCode, String templateId, Map data) {HashMap<String, Object> params = new HashMap<>();params.put("userCode",userCode);params.put("templateId",templateId);params.put("map",data);String url = "http://123.456.12.12:8080/api/subscribe/sendWeixinMsg";HttpPost httpPost = new HttpPost(url);httpPost.setHeader("Accept", "application/json");httpPost.setHeader("Content-Type", "application/json");String charSet = "UTF-8";StringEntity entity = new StringEntity(JsonUtil.toJson(params), charSet);httpPost.setEntity(entity);ResponseHandler<String> responseHandler = new BasicResponseHandler();try {httpClient.execute(httpPost, responseHandler);} catch (IOException e) {e.printStackTrace();}}}總結
- 上一篇: am3358——GPMC——参考网上驱动
- 下一篇: Mixly 触摸开关的使用