1, What is wechat subscription notification?
1. Subscribe to notification service
After the interface is completed, the interface can be invoked in the business project.
preface
As of November 2, 2021, the subscription notice of wechat is still under gray scale test. It is said that the template message of wechat will be offline soon, so the subscription notice of wechat is used to send the notice This article is the development record of wechat subscription notification function in practice. At present, it has been tested online for reference only
1, What is wechat subscription notification?
Template messages are abused and many users are harassed. In order to manage, wechat introduces subscription notifications, which are divided into one-time subscription and long-term subscription. One-time subscription: one subscription can send a message to users Long term subscription: Subscribe once. At present, send messages indefinitely. This article is a case of long-term subscription. One-time subscription is of no value to us
2, Use steps
1. Subscribe to notification service
According to the wechat document, the company's < < Qualification Authorization Document > > needs to be provided. Generally, there will be results after a few hours of review. After passing, only the templates provided by wechat can be selected. There are few long-term subscription templates, which do not necessarily meet the business needs and can only be used together
2. Get access_token
Must operate!
- Add to yaml file
wechat: appId: wx005a........6ff appSecret: 18000b98b...........71b3e058
two AccessToken class receives "get access_token interface"
@Data public class AccessToken { private String accessToken; private Long expiresIn; }
3.WeixinToken Save access in memory_ token
/** * @Title: WeixinToken * @Description: Hold wechat Access_token object * @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("System acquisition access_token Something went wrong!"); } return token; } }
5. Start the spring scheduled task Add @ enableshcheduling annotation
@SpringBootApplication @ServletComponentScan @EnableScheduling public class WeixinApplication { public static void main(String[] args) { SpringApplication.run(WeixinApplication.class, args); } }
6. Obtain access for scheduled tasks_ Token (invalid within two hours, and the access_token must be updated within two hours)
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: Get wechat access_token timed task * @date 2021-10-25 11:10 */ @Component public class AccessTokenTask { private Logger logger = LoggerFactory.getLogger(AccessTokenTask.class); @Autowired private WeixinAccessTokenUtil weixinAccessTokenUtil; @Value("$") private String appId; @Value("$") 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("Wechat obtained accessToken by" + token); } catch (Exception e) { logger.error("Get wechat adcessToken Error, the information is as follows"); e.printStackTrace(); } } @Scheduled(initialDelay = 3000, fixedDelay = 1000 * 1000) public void testWeixinAccessT() { logger.info(WeixinToken.token); } }
7. http is sent with httpclient. If there is no omission, there is no need for other dependencies
<dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpclient</artifactId> <version>$</version> </dependency>
Our wechat business and internal business projects are separated, so we only implement interfaces in wechat projects for internal projects to call. The specific interfaces are as follows
one The WeixinMsg interface is used to receive service related data
@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. In the controller, where R is a user-defined return Result entity, replace it with your own return Result!
//Send wechat subscription message @CrossOrigin(maxAge = 3600, origins = "*") @PostMapping("/subscribe/sendWeixinMsg") public R sendWeixinMsg(@RequestBody WeixinMsg weixinMsg) { Assert.isTrue(null != weixinMsg.getUserCode() && null != weixinMsg.getTemplateId() && null != weixinMsg.getMap(), "parameter is incorrect"); return weixinSendMsgService.send(weixinMsg); }
3. Interface
public interface WeixinSendMsgService { R send(WeixinMsg weixinMsg); }
4.service code,
UserWechatMapper and where String openId = userWechatMapper.queryOpenIdByUserCode(userCode); Yes, we have a special page to realize the binding between wechat openId and business Id According to the actual situation, just know the openId of wechat users. For specific functions of openId, please refer to the official documents!
ImSmsWeixinMapper and the private method below are business codes. Record the sending log, mask or change it to your own business code
@Service("weixinSendMsgService") public class WeixinSendMsgServiceImpl implements WeixinSendMsgService { private Logger logger = LoggerFactory.getLogger(WeixinSendMsgServiceImpl.class); @Resource private UserWechatMapper userWechatMapper; @Resource private ImSmsWeixinMapper imSmsWeixinMapper; @Override public 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("For users{}Send template{}Message failed,The user is not bound and cannot be obtained openId!", userCode, templateId); imSmsWeixinMapper.insertSelective(getFailLog(now, userCode, openId, templateId, data, "User not bound,Unable to send template message")); return R.fail("Wechat message sending failed,User not bound"); } 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("For users{}Send template{}Message succeeded!", userCode, templateId); } else { logger.error("For users{}Send template{}Message exception!", userCode, templateId); logger.error("Send data as:[{}]!", data); logger.error("Error code:[{}]!", errcode); logger.error("error message:[{}]!", errmsg); imSmsWeixinMapper.insertSelective(getFailLog(now, userCode, openId, templateId, data, errmsg)); return R.fail("Wechat message sending failed,Error code" + errcode); } } catch (IOException e) { e.printStackTrace(); logger.error("For users{}Send template{}Message exception!", userCode, templateId); logger.error("Send data as:[{}]!", data); imSmsWeixinMapper.insertSelective(getFailLog(now, userCode, openId, templateId, data, "The program sent an exception,Please check" + 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. One question: Encountered during development The interface prompts {"errcode":47001,"errmsg":"data....}. I checked json and said single quotation marks and double quotation marks on the Internet, My test is not about quotes, as long as the json format is correct It's mainly about the way of sending At the beginning, it was sent in the way of UrlEncodeEntity, resulting in 47001 problem. The above code sending method is completely OK
6. I found it on the official website, but I couldn't find the interface and method that can provide testing. I only had online testing, Send a text message, Insert the subscription message plug-in at the end of the article, as shown in the figure
7. Click the subscription notice in the article to select the relevant subscription. After subscribing, click the subscription again to stop the subscription box from popping up
After the interface is completed, the interface can be invoked in the business project.
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(); } } }