spring boot +微信小程序项目,通过微信公众号实现指定用户消息长期推送

编程入门 行业动态 更新时间:2024-10-09 14:26:55

spring boot +微信小程序项目,通过微信<a href=https://www.elefans.com/category/jswz/34/1769853.html style=公众号实现指定用户消息长期推送"/>

spring boot +微信小程序项目,通过微信公众号实现指定用户消息长期推送

流程

用户登录小程序,后台记录用户的小程序openId和用户唯一的UnionId。然后用户触发公众号事件(关注公众号或者发送指定消息),后台获取到用户公众号的openId,再调用接口通过公众号的openId查询用户的UnionId,再和数据库里的UnionId进行匹配,将用户的公众号openId存入数据库。此后即可通过userId找到公众号openId 实现公众号消息推送。

1.开通公众号

这个直接操作就好了

2.配置公众号验签接口

添加验签接口

	/*** 服务器有效性验证*/@GetMapping("connect")public String verifyToken(HttpServletRequest request) {String signature = request.getParameter("signature");String timestamp = request.getParameter("timestamp");String nonce = request.getParameter("nonce");String echostr = request.getParameter("echostr");log.info("signature: {} nonce: {} echostr: {} timestamp: {}", signature, nonce, echostr, timestamp);if (this.checkSignature(signature, timestamp, nonce)) {log.info("token ok");return echostr;}return echostr;}

将接口配置到公众号上

3.设置用户关注、取关以及发送消息事件处理

接口名称和验签接口相同 只是验签是GET请求 事件处理是POST请求

	/*** 用户公众号关注/取关事件回调*/@PostMapping(value = "/connect")public void getXmlInfo(HttpServletRequest req, HttpServletResponse resp) throws Exception {req.setCharacterEncoding("UTF-8"); // 接收请求时的编码。resp.setCharacterEncoding("UTF-8"); // 响应给浏览器的编码。Map<String, String> map = XMLUtil.parseXml(req);log.info(JSONObject.toJSONString(map));if ( map.get("MsgType").equals("text")){if (map.get("Content").equals("求购留言")){String result = wechatOfficialAccountService.replySubscribeInfo(map);resp.setCharacterEncoding("UTF-8");resp.getWriter().println(result);}}else {if (map.get("Event").equals("subscribe")){wechatOfficialAccountService.subscribeInfo(map);} else if (map.containsKey("Event") && map.get("Event").equals("unsubscribe")) {wechatOfficialAccountService.unSubscribeInfo(map);}}}

关注时给用户绑定openId

	public void subscribeInfo(Map<String, String> map){WeChatEventInfo eventInfo = new WeChatEventInfo().setToUserName(map.get("ToUserName")).setFromUserName(map.get("FromUserName")).setCreateTime(map.get("CreateTime")).setMsgType(map.get("MsgType")).setEvent(map.get("Event"));String accessToken = getToken();log.info("accessToken:{}",accessToken);String unionId = getUserInfo(accessToken,eventInfo.getFromUserName());if(StrUtil.isNotBlank(unionId)){wxCustomerService.bindOfficialAccountOpenId(unionId,eventInfo.getFromUserName());}}

回复指定消息时给用户绑定openId

/*** 回复求购留言进行关注*/public String replySubscribeInfo(Map<String, String> map) throws Exception {WeChatEventInfo eventInfo = new WeChatEventInfo().setToUserName(map.get("ToUserName")).setFromUserName(map.get("FromUserName")).setCreateTime(map.get("CreateTime")).setMsgType(map.get("MsgType")).setContent(map.get("content"));String accessToken = getToken();log.info("accessToken:{}",accessToken);String unionId = getUserInfo(accessToken,eventInfo.getFromUserName());ReplyMessage result = new ReplyMessage().setToUserName(eventInfo.getFromUserName()).setFromUserName(eventInfo.getToUserName()).setCreateTime(System.currentTimeMillis()).setMsgType("text");if(StrUtil.isNotBlank(unionId)){wxCustomerService.bindOfficialAccountOpenId(unionId,eventInfo.getFromUserName());result.setContent("已为您开通求购留言通知");}else {result.setContent("开通失败,暂未获取到您的用户信息");}return XMLUtil.textMessageToXml(result);}

取关时解绑用户openId

 public void unSubscribeInfo(Map<String, String> map){WeChatEventInfo eventInfo = new WeChatEventInfo().setToUserName(map.get("ToUserName")).setFromUserName(map.get("FromUserName")).setCreateTime(map.get("CreateTime")).setMsgType(map.get("MsgType")).setEvent(map.get("Event"));wxCustomerService.unSubscribeInfo(eventInfo.getFromUserName());}

4.申请消息模板

5.给指定用户发送消息

前三步代码

依赖

		<dependency><groupId>com.github.binarywang</groupId><artifactId>weixin-java-mp</artifactId><version>4.0.0</version></dependency><dependency><groupId>dom4j</groupId><artifactId>dom4j</artifactId><version>1.1</version></dependency><!-- java对象转换为xml字符串 --><dependency><groupId>com.thoughtworks.xstream</groupId><artifactId>xstream</artifactId><version>1.4.19</version></dependency>

接口层

接口需要放行

@RestController
@RequestMapping("wechat")
@Slf4j
public class WeChatController {private final WechatOfficialAccountService wechatOfficialAccountService;private static final String TOKEN = "platformYz";public WeChatController(WechatOfficialAccountService wechatOfficialAccountService) {this.wechatOfficialAccountService = wechatOfficialAccountService;}/*** 服务器有效性验证*/@GetMapping("connect")public String verifyToken(HttpServletRequest request) {String signature = request.getParameter("signature");String timestamp = request.getParameter("timestamp");String nonce = request.getParameter("nonce");String echostr = request.getParameter("echostr");log.info("signature: {} nonce: {} echostr: {} timestamp: {}", signature, nonce, echostr, timestamp);if (this.checkSignature(signature, timestamp, nonce)) {log.info("token ok");return echostr;}return echostr;}/*** 用户公众号关注/取关事件回调*/@PostMapping(value = "/connect")public void getXmlInfo(HttpServletRequest req, HttpServletResponse resp) throws Exception {req.setCharacterEncoding("UTF-8"); // 接收请求时的编码。resp.setCharacterEncoding("UTF-8"); // 响应给浏览器的编码。Map<String, String> map = XMLUtil.parseXml(req);log.info(JSONObject.toJSONString(map));if ( map.get("MsgType").equals("text")){if (map.get("Content").equals("求购留言")){String result = wechatOfficialAccountService.replySubscribeInfo(map);resp.setCharacterEncoding("UTF-8");resp.getWriter().println(result);}}else {if (map.get("Event").equals("subscribe")){wechatOfficialAccountService.subscribeInfo(map);} else if (map.containsKey("Event") && map.get("Event").equals("unsubscribe")) {wechatOfficialAccountService.unSubscribeInfo(map);}}}private boolean checkSignature(String signature, String timestamp, String nonce) {String[] str = new String[]{TOKEN, timestamp, nonce};//排序Arrays.sort(str);//拼接字符串StringBuffer buffer = new StringBuffer();for (String s : str) {buffer.append(s);}//进行sha1加密String temp = SHA1.encode(buffer.toString());//与微信提供的signature进行匹对return signature.equals(temp);}
}

service层


import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.ResponseEntity;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;import java.nio.charset.StandardCharsets;
import java.util.Map;
import java.util.Optional;@Service
@Slf4j
public class WechatOfficialAccountService {private final WxCustomerService wxCustomerService;private final RestTemplate restTemplate;private final String appId;private final String appSecret;private static final String USER_INFO_URL = "=%s&openid=%s&lang=zh_CN";private static final String TOKEN_URL = "";public WechatOfficialAccountService(WxCustomerService wxCustomerService, RestTemplate restTemplate,@Value("${wx.gzh.appId}") String appId,@Value("${wx.gzh.secret}") String appSecret) {this.wxCustomerService = wxCustomerService;this.restTemplate = restTemplate;this.appId = appId;this.appSecret = appSecret;}public void subscribeInfo(Map<String, String> map){WeChatEventInfo eventInfo = new WeChatEventInfo().setToUserName(map.get("ToUserName")).setFromUserName(map.get("FromUserName")).setCreateTime(map.get("CreateTime")).setMsgType(map.get("MsgType")).setEvent(map.get("Event"));String accessToken = getToken();log.info("accessToken:{}",accessToken);String unionId = getUserInfo(accessToken,eventInfo.getFromUserName());if(StrUtil.isNotBlank(unionId)){wxCustomerService.bindOfficialAccountOpenId(unionId,eventInfo.getFromUserName());}}/*** 回复求购留言进行关注*/public String replySubscribeInfo(Map<String, String> map) throws Exception {WeChatEventInfo eventInfo = new WeChatEventInfo().setToUserName(map.get("ToUserName")).setFromUserName(map.get("FromUserName")).setCreateTime(map.get("CreateTime")).setMsgType(map.get("MsgType")).setContent(map.get("content"));String accessToken = getToken();log.info("accessToken:{}",accessToken);String unionId = getUserInfo(accessToken,eventInfo.getFromUserName());ReplyMessage result = new ReplyMessage().setToUserName(eventInfo.getFromUserName()).setFromUserName(eventInfo.getToUserName()).setCreateTime(System.currentTimeMillis()).setMsgType("text");if(StrUtil.isNotBlank(unionId)){wxCustomerService.bindOfficialAccountOpenId(unionId,eventInfo.getFromUserName());result.setContent("已为您开通求购留言通知");}else {result.setContent("开通失败,暂未获取到您在玉农智链中的用户信息");}return XMLUtil.textMessageToXml(result);}private String getUserInfo(String accessToken,String openId) {String url = String.format(USER_INFO_URL, accessToken, openId);String json = restTemplate.getForObject(url, String.class);JSONObject jsonObject = JSONObject.parseObject(json);log.info("json:{}",json);return jsonObject.getString("unionid");}private String getToken() {ResponseEntity<JSONObject> responseEntity = restTemplate.postForEntity(TOKEN_URL,new JSONObject().fluentPut("grant_type","client_credential").fluentPut("appid",appId).fluentPut("secret",appSecret).toJSONString(), JSONObject.class);JSONObject json = Optional.ofNullable(responseEntity.getBody()).orElse(new JSONObject());restTemplate.getMessageConverters().set(1, new StringHttpMessageConverter(StandardCharsets.UTF_8));log.info("access_token_json:{}",json.toJSONString());return json.getString("access_token");}public void unSubscribeInfo(Map<String, String> map){WeChatEventInfo eventInfo = new WeChatEventInfo().setToUserName(map.get("ToUserName")).setFromUserName(map.get("FromUserName")).setCreateTime(map.get("CreateTime")).setMsgType(map.get("MsgType")).setEvent(map.get("Event"));wxCustomerService.unSubscribeInfo(eventInfo.getFromUserName());}
}

工具类

import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.core.util.QuickWriter;
import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
import com.thoughtworks.xstream.io.xml.PrettyPrintWriter;
import com.thoughtworks.xstream.io.xml.XppDriver;
import lombok.extern.slf4j.Slf4j;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.io.InputStream;
import java.io.Writer;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@Slf4j
public class XMLUtil {private static final String MESSAGE_TYPE_TEXT = "text";public  static Map<String, String> parseXml(HttpServletRequest request) {Map<String, String> map = new HashMap<>();try(InputStream inputStream = request.getInputStream()){// 读取输入流SAXReader reader = new SAXReader();Document document = reader.read(inputStream);// 得到xml根元素Element root = document.getRootElement();// 得到根元素的所有子节点List<Element> elementList = root.elements();// 遍历所有子节点for (Element e : elementList) {map.put(e.getName(), e.getText());}} catch (IOException |DocumentException e) {log.info("xml信息解析失败");}return map;}/*** 文本消息对象转换成xml** @param textMessage 文本消息对象* @return xml*/public static String textMessageToXml(ReplyMessage textMessage) {XSTREAM.alias("xml", textMessage.getClass());return XSTREAM.toXML(textMessage);}/*** 扩展xstream,使其支持CDATA块*/private static final XStream XSTREAM = new XStream(new XppDriver() {@Overridepublic HierarchicalStreamWriter createWriter(Writer out) {return new PrettyPrintWriter(out) {// 对所有xml节点的转换都增加CDATA标记final boolean cdata = true;@Overrideprotected void writeText(QuickWriter writer, String text) {if (cdata) {writer.write("<![CDATA[");writer.write(text);writer.write("]]>");} else {writer.write(text);}}};}});/*** 获取默认文本消息** @param receiver     接收人* @param officialWxid 官方微信id* @return 文本消息*/public static ReplyMessage getDefaultReplyMessage(String receiver, String officialWxid) {ReplyMessage textMessage = new ReplyMessage();textMessage.setToUserName(receiver);textMessage.setFromUserName(officialWxid);textMessage.setCreateTime(System.currentTimeMillis());textMessage.setMsgType(MESSAGE_TYPE_TEXT);return textMessage;}}

模板消息发送服务类


import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.ResponseEntity;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;import java.nio.charset.StandardCharsets;
import java.util.Objects;
import java.util.Optional;@Component
@Slf4j
public class WechatOfficialAccountMessageServer {private final String appId;private final String miniAppId;private final String appSecret;private final RestTemplate restTemplate;private  String accessToken;private static final String SEND_URL = "=%s";private static final String TOKEN_URL = "";private WechatOfficialAccountMessageServer(@Value("${wx.gzh.appId}") String appId,@Value("${wx.xcx.appId}") String miniAppId,@Value("${wx.gzh.secret}")String appSecret) {this.appId = appId;this.appSecret = appSecret;this.miniAppId = miniAppId;this.restTemplate = new RestTemplate();}/*** 消息推送** @param templateId  消息模板id* @param openId      用户openId* @param param 推送对象*/public void pushMessage(String templateId, String openId, WechatOfficialAccountMessageParam param) {getInstance();param.getMessageDataList().forEach(item->{pushMessage(templateId,openId,item,param.getPage());});}private void getInstance(){//获取access_tokenResponseEntity<JSONObject> responseEntity = restTemplate.postForEntity(TOKEN_URL,new JSONObject().fluentPut("grant_type","client_credential").fluentPut("appid",appId).fluentPut("secret",appSecret).toJSONString(), JSONObject.class);JSONObject json = Optional.ofNullable(responseEntity.getBody()).orElse(new JSONObject());restTemplate.getMessageConverters().set(1, new StringHttpMessageConverter(StandardCharsets.UTF_8));this.accessToken = json.getString("access_token");}/*** 消息推送** @param templateId  消息模板id* @param openId      用户openId* @param messageData 消息体* @param page*/private void pushMessage(String templateId, String openId, JSONObject messageData, String page) {String url = String.format(SEND_URL, this.accessToken);//拼接推送的模版SendTemplateRequest request = new SendTemplateRequest().setTouser(openId).setTemplateId(templateId).setData(messageData);if (StrUtil.isNotBlank(page)){JSONObject miniProgram = new JSONObject().fluentPut("appid", miniAppId).fluentPut("pagepath", page);request.setMiniProgram(miniProgram);}ResponseEntity<SendTemplateResponse> responseEntity = restTemplate.postForEntity(url, JSONObject.toJSONString(request), SendTemplateResponse.class);SendTemplateResponse response = Optional.ofNullable(responseEntity.getBody()).orElse(SendTemplateResponse.getInstance());if (Objects.requireNonNull(response).isSuccess()) {log.info("公众号推送成功");return;}log.error("公众号推送失败:{} {}", response.getErrcode(), response.getErrmsg());throw new DefaultException("公众号推送失败:" + response.getErrcode() + response.getErrmsg(), ResponseEnum.OPERATE_FAIL);}
}

相关实体类

import lombok.Data;
import lombok.experimental.Accessors;/*** 微信公众号关注取消事件信息*/
@Data
@Accessors(chain = true)
public class WeChatEventInfo {private String toUserName;private String fromUserName;private String createTime;private String msgType;private String event;private String content;private String msgId;
}
@Data
@Accessors(chain = true)
public class ReplyMessage {private String ToUserName;private String FromUserName;private Long CreateTime;private String MsgType;private String Content;
}
import com.alibaba.fastjson.annotation.JSONField;
import lombok.Data;
import lombok.experimental.Accessors;@Data
@Accessors(chain = true)
class SendTemplateRequest {/*** 接收者(用户)的 openid*/private String touser;/*** 所需下发的模板消息的id*/@JSONField(name = "template_id")private String templateId;/*** 模板跳转链接(海外账号没有跳转能力)*/@JSONField(name = "url")private String url;/*** 跳小程序所需数据,不需跳小程序可不用传该数据* "miniprogram":{*              "appid":"xiaochengxuappid12345",*              "pagepath":"index?foo=bar"*            }* appid	必填	所需跳转到的小程序appid(该小程序appid必须与发模板消息的公众号是绑定关联关系,暂不支持小游戏)* pagepath	非必填	所需跳转到小程序的具体页面路径,支持带参数,(示例index?foo=bar),要求该小程序已发布,暂不支持小游戏*/@JSONField(name = "miniprogram")private Object miniProgram;/*** 模板数据*/private Object data;/*** 防重入id。对于同一个openid + client_msg_id, 只发送一条消息,10分钟有效,超过10分钟不保证效果。若无防重入需求,可不填*/@JSONField(name = "client_msg_id")private String clientMsgId;
}
import lombok.Data;
import lombok.experimental.Accessors;/*** 消息推送响应对象** @author fenglifei*/
@Data
@Accessors(chain = true)
public class SendTemplateResponse {/*** 错误码* 0 ok* 43116 该模板因滥用被滥用过多,已被限制下发*/private long errcode;/*** 错误信息*/private String errmsg;/*** 错误信息*/private String msgid;public static SendTemplateResponse getInstance(){return new SendTemplateResponse().setErrcode(404L).setErrmsg("");}public boolean isSuccess() {return this.errcode == 0;}
}

import com.alibaba.fastjson.JSONObject;
import lombok.Data;import java.util.List;@Data
public class WechatOfficialAccountMessageParam {private String page;private List<JSONObject> messageDataList;
}

更多推荐

spring boot +微信小程序项目,通过微信公众号实现指定用户消息长期推送

本文发布于:2024-02-07 00:39:56,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1751939.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:公众   消息   程序   项目   用户

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!