WechatServerServiceImpl.java 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292
  1. package com.caimei365.wechat.service.impl;
  2. import com.alibaba.fastjson.JSONObject;
  3. import com.caimei365.wechat.dao.WeChatDao;
  4. import com.caimei365.wechat.entity.WeChatConstant;
  5. import com.caimei365.wechat.entity.WechatArticleDetail;
  6. import com.caimei365.wechat.entity.WechatReply;
  7. import com.caimei365.wechat.service.WechatServerService;
  8. import com.caimei365.wechat.utils.MessageUtil;
  9. import com.caimei365.wechat.utils.SignUtil;
  10. import lombok.extern.slf4j.Slf4j;
  11. import org.springframework.beans.factory.annotation.Value;
  12. import org.springframework.stereotype.Service;
  13. import org.springframework.util.CollectionUtils;
  14. import org.springframework.util.StringUtils;
  15. import org.springframework.web.client.RestTemplate;
  16. import javax.annotation.Resource;
  17. import javax.servlet.http.HttpServletRequest;
  18. import java.io.UnsupportedEncodingException;
  19. import java.net.URLEncoder;
  20. import java.util.ArrayList;
  21. import java.util.List;
  22. import java.util.Map;
  23. /**
  24. * 微信公众号服务配置
  25. *
  26. * @author : Charles
  27. * @date : 2022/1/13
  28. */
  29. @Slf4j
  30. @Service
  31. public class WechatServerServiceImpl implements WechatServerService {
  32. @Value("${caimei.wwwDomain}")
  33. private String wwwDomain;
  34. @Value("${caimei.coreDomain}")
  35. private String coreDomain;
  36. @Value("${caimei.imageDomain}")
  37. private String imageDomain;
  38. @Value("${wechat.caimei.appid}")
  39. private String caimeiAppid;
  40. @Value("${wechat.caimei.secret}")
  41. private String caimeiSecret;
  42. @Resource
  43. private WeChatDao weChatDao;
  44. /**
  45. * 验证消息的确来自微信服务器
  46. *
  47. * 通过检验signature对请求进行校验。
  48. * 若确认此次GET请求来自微信服务器,请原样返回echostr参数内容,则接入生效,成为开发者成功,否则接入失败。
  49. * 加密/校验流程如下:
  50. * 1)将token、timestamp、nonce三个参数进行字典序排序
  51. * 2)将三个参数字符串拼接成一个字符串进行sha1加密
  52. * 3)开发者获得加密后的字符串可与signature对比,标识该请求来源于微信
  53. */
  54. @Override
  55. public String validateSignature(HttpServletRequest request) {
  56. String signature = request.getParameter("signature");
  57. String timestamp = request.getParameter("timestamp");
  58. String nonce = request.getParameter("nonce");
  59. String echostr = request.getParameter("echostr");
  60. boolean flag = SignUtil.checkSignature(signature, timestamp, nonce);
  61. if (flag) {
  62. return echostr;
  63. } else {
  64. return null;
  65. }
  66. }
  67. /**
  68. * 处理接收的文本消息
  69. * <xml>
  70. * <ToUserName><![CDATA[toUser]]></ToUserName>
  71. * <FromUserName><![CDATA[fromUser]]></FromUserName>
  72. * <CreateTime>12345678</CreateTime>
  73. * <MsgType><![CDATA[text]]></MsgType>
  74. * <Content><![CDATA[你好]]></Content>
  75. * </xml>
  76. */
  77. @Override
  78. public String handleTextMsg(Map<String, String> requestMap) {
  79. // 根据接收的关键字回复
  80. String keyword = requestMap.get(WeChatConstant.CONTENT);
  81. // 用户openid
  82. String openid = requestMap.get(WeChatConstant.FROM_USER_NAME);
  83. // 微信号公众号Id
  84. String wxType = requestMap.get(WeChatConstant.TO_USER_NAME);
  85. log.info(">>>>>>>>>>处理接收的文本消息,keyword:" + keyword + ",openid:" + openid + ",wxType:" + wxType);
  86. // 根据口令获取用户openid
  87. if("caimei:get_openid".equals(keyword)) {
  88. return MessageUtil.setTextXml(openid, wxType, openid);
  89. }
  90. // 根据keyword匹配数据库回复配置
  91. WechatReply reply = weChatDao.getReplyByParams("input", keyword, wxType);
  92. // if (null == reply) {
  93. // reply = weChatDao.getReplyByParams("input", "autoReply", wxType);
  94. // }
  95. // if (null == reply) {
  96. // // 将消息转发到到客服系统
  97. // return MessageUtil.setCustomerXml(openid, wxType);
  98. // }
  99. log.info(">>>>>>>>>>匹配已配置关键词:" + keyword);
  100. String replyXml = setXmlByDatabaseReply(openid, wxType, reply);
  101. if (replyXml != null) {
  102. return replyXml;
  103. }
  104. log.info(">>>>>>>>>>模糊搜索:" + keyword);
  105. WechatArticleDetail article = null;
  106. RestTemplate restTemplate = new RestTemplate();
  107. // https://core.caimei365.com/commodity/search/query/product?keyword=
  108. String uri = coreDomain + "commodity/search/query/product?keyword="+keyword;
  109. JSONObject forObject = restTemplate.getForObject(uri, JSONObject.class);
  110. if(forObject != null){
  111. String data = forObject.getString("data");
  112. if(StringUtils.hasLength(data)){
  113. JSONObject parse = JSONObject.parseObject(data);
  114. if(parse != null){
  115. Integer total = parse.getInteger("total");
  116. if(total>0){
  117. article = new WechatArticleDetail();
  118. article.setTitle("帮您匹配到"+ total + "个商品,点击查看");
  119. article.setPicUrl(imageDomain + "/group1/M00/03/EC/rB-lGGHg9ZOAe8VdAAJevlD1ofg562.png");
  120. String url = wwwDomain + "product/list.html?keyword="+keyword;
  121. try {
  122. url = URLEncoder.encode(url, "UTF-8");
  123. } catch (UnsupportedEncodingException ignored) {}
  124. article.setLinkUrl("https://open.weixin.qq.com/connect/oauth2/authorize?appid=" + caimeiAppid + "&redirect_uri=" + url +"&response_type=code&scope=snsapi_base&state=reLogin#wechat_redirect");
  125. }
  126. }
  127. }
  128. }
  129. if (null != article) {
  130. List<WechatArticleDetail> articles = new ArrayList<>();
  131. articles.add(article);
  132. return MessageUtil.setArticleXml(openid, wxType, articles);
  133. }
  134. log.info(">>>>>>>>>>默认无关键字匹配回复图文1220");
  135. List<WechatArticleDetail> defaultList = weChatDao.getArticleDetailList(1220);
  136. if (CollectionUtils.isEmpty(defaultList)) {
  137. return MessageUtil.setArticleXml(openid, wxType, defaultList);
  138. }
  139. return null;
  140. }
  141. /**
  142. * 点击自定义菜单事件
  143. *
  144. * 点击菜单拉取消息时的事件推送:
  145. * <xml>
  146. * <ToUserName><![CDATA[toUser]]></ToUserName>
  147. * <FromUserName><![CDATA[FromUser]]></FromUserName>
  148. * <CreateTime>123456789</CreateTime>
  149. * <MsgType><![CDATA[event]]></MsgType>
  150. * <Event><![CDATA[CLICK]]></Event>
  151. * <EventKey><![CDATA[EVENTKEY]]></EventKey>
  152. * </xml>
  153. *
  154. * 点击菜单跳转链接时的事件推送:
  155. * <xml>
  156. * <ToUserName><![CDATA[toUser]]></ToUserName>
  157. * <FromUserName><![CDATA[FromUser]]></FromUserName>
  158. * <CreateTime>123456789</CreateTime>
  159. * <MsgType><![CDATA[event]]></MsgType>
  160. * <Event><![CDATA[VIEW]]></Event>
  161. * <EventKey><![CDATA[www.qq.com]]></EventKey>
  162. * </xml>
  163. */
  164. @Override
  165. public String handleClickMenuMsg(Map<String, String> requestMap) {
  166. // 用户openid
  167. String openid = requestMap.get(WeChatConstant.FROM_USER_NAME);
  168. // 微信号公众号Id
  169. String wxType = requestMap.get(WeChatConstant.TO_USER_NAME);
  170. // 事件类型
  171. String event = requestMap.get(WeChatConstant.EVENT);
  172. // 事件KEY值,设置的跳转URL/与自定义菜单接口中KEY值对应
  173. String eventKey = requestMap.get(WeChatConstant.EVENT_KEY);
  174. log.info(">>>>>>>>>>处理点击自定义菜单事件,event:" + event + ",eventKey:" + eventKey + ",openid:" + openid + ",wxType:" + wxType);
  175. // 匹配数据库回复配置
  176. WechatReply reply = weChatDao.getReplyByParams("click", eventKey, wxType);
  177. return setXmlByDatabaseReply(openid, wxType, reply);
  178. }
  179. /**
  180. * 上报地理位置
  181. * <xml>
  182. * <ToUserName><![CDATA[toUser]]></ToUserName>
  183. * <FromUserName><![CDATA[fromUser]]></FromUserName>
  184. * <CreateTime>123456789</CreateTime>
  185. * <MsgType><![CDATA[event]]></MsgType>
  186. * <Event><![CDATA[LOCATION]]></Event>
  187. * <Latitude>23.137466</Latitude>
  188. * <Longitude>113.352425</Longitude>
  189. * <Precision>119.385040</Precision>
  190. * </xml>
  191. *
  192. * * <纬度>23.137466</纬度>
  193. * * <经度>113.352425</经度>
  194. * * <精度>119.385040</精度>
  195. */
  196. @Override
  197. public String handleLocationMsg(Map<String, String> requestMap) {
  198. // 用户openid
  199. String openid = requestMap.get(WeChatConstant.FROM_USER_NAME);
  200. // 微信号公众号Id
  201. String wxType = requestMap.get(WeChatConstant.TO_USER_NAME);
  202. //Longitude 地理位置经度
  203. String longitude = requestMap.get(WeChatConstant.LONGITUDE);
  204. //Latitude 地理位置纬度
  205. String latitude = requestMap.get(WeChatConstant.LATITUDE);
  206. //Precision 地理位置精度
  207. String precision = requestMap.get(WeChatConstant.PRECISION);
  208. String content = "您当前位置,经度:" + longitude + ",纬度:" + latitude + ",精度:" + precision;
  209. log.info(">>>>>>>>>>处理上报地理位置事件," + content + ",openid:" + openid + ",wxType:" + wxType);
  210. return MessageUtil.setTextXml(openid, wxType, content);
  211. }
  212. /**
  213. * subscribe(订阅/关注)
  214. * <xml>
  215. * <ToUserName><![CDATA[toUser]]></ToUserName>
  216. * <FromUserName><![CDATA[FromUser]]></FromUserName>
  217. * <CreateTime>123456789</CreateTime>
  218. * <MsgType><![CDATA[event]]></MsgType>
  219. * <Event><![CDATA[subscribe]]></Event>
  220. * </xml>
  221. */
  222. @Override
  223. public String handleSubscribeMsg(Map<String, String> requestMap) {
  224. // 用户openid
  225. String openid = requestMap.get(WeChatConstant.FROM_USER_NAME);
  226. // 微信号公众号Id
  227. String wxType = requestMap.get(WeChatConstant.TO_USER_NAME);
  228. log.info(">>>>>>>>>>处理接收的订阅关注事件,openid:" + openid + ",wxType:" + wxType);
  229. // 匹配数据库回复配置
  230. WechatReply reply = weChatDao.getReplyByParams("subscribe", null, wxType);
  231. return setXmlByDatabaseReply(openid, wxType, reply);
  232. }
  233. /**
  234. * 扫描带参数二维码事件
  235. * <xml>
  236. * <ToUserName><![CDATA[toUser]]></ToUserName>
  237. * <FromUserName><![CDATA[FromUser]]></FromUserName>
  238. * <CreateTime>123456789</CreateTime>
  239. * <MsgType><![CDATA[event]]></MsgType>
  240. * <Event><![CDATA[subscribe]]></Event>
  241. * <EventKey><![CDATA[qrscene_123123]]></EventKey>
  242. * <Ticket><![CDATA[TICKET]]></Ticket>
  243. * </xml>
  244. */
  245. @Override
  246. public String handleScanMsg(Map<String, String> requestMap) {
  247. // 用户openid
  248. String openid = requestMap.get(WeChatConstant.FROM_USER_NAME);
  249. // 微信号公众号Id
  250. String wxType = requestMap.get(WeChatConstant.TO_USER_NAME);
  251. // 扫码参数
  252. String eventKey = requestMap.get(WeChatConstant.EVENT_KEY);
  253. log.info(">>>>>>>>>>处理扫描带参数二维码事件,二维码参数:" + eventKey + ",openid:" + openid + ",wxType:" + wxType);
  254. //todo 用户绑定微信
  255. // 匹配数据库回复配置
  256. WechatReply reply = weChatDao.getReplyByParams("subscribe", null, wxType);
  257. return setXmlByDatabaseReply(openid, wxType, reply);
  258. }
  259. /**
  260. * 根据数据库回复配置设置
  261. */
  262. private String setXmlByDatabaseReply(String openid, String wxType, WechatReply reply) {
  263. if ("news".equals(reply.getResponseType())) {
  264. // 回复图文
  265. List<WechatArticleDetail> articleList = weChatDao.getArticleDetailList(reply.getRelateId());
  266. if (CollectionUtils.isEmpty(articleList)) {
  267. return MessageUtil.setArticleXml(openid, wxType, articleList);
  268. }
  269. } else {
  270. // 回复文本
  271. String textContent = weChatDao.getTextContent(reply.getRelateId());
  272. if (StringUtils.hasLength(textContent)) {
  273. return MessageUtil.setTextXml(openid, wxType, textContent);
  274. }
  275. }
  276. return null;
  277. }
  278. }