|
@@ -0,0 +1,292 @@
|
|
|
+package com.caimei365.wechat.service.impl;
|
|
|
+
|
|
|
+import com.alibaba.fastjson.JSONObject;
|
|
|
+import com.caimei365.wechat.dao.WeChatDao;
|
|
|
+import com.caimei365.wechat.entity.WeChatConstant;
|
|
|
+import com.caimei365.wechat.entity.WechatArticleDetail;
|
|
|
+import com.caimei365.wechat.entity.WechatReply;
|
|
|
+import com.caimei365.wechat.service.WechatServerService;
|
|
|
+import com.caimei365.wechat.utils.MessageUtil;
|
|
|
+import com.caimei365.wechat.utils.SignUtil;
|
|
|
+import lombok.extern.slf4j.Slf4j;
|
|
|
+import org.springframework.beans.factory.annotation.Value;
|
|
|
+import org.springframework.stereotype.Service;
|
|
|
+import org.springframework.util.CollectionUtils;
|
|
|
+import org.springframework.util.StringUtils;
|
|
|
+import org.springframework.web.client.RestTemplate;
|
|
|
+
|
|
|
+import javax.annotation.Resource;
|
|
|
+import javax.servlet.http.HttpServletRequest;
|
|
|
+import java.io.UnsupportedEncodingException;
|
|
|
+import java.net.URLEncoder;
|
|
|
+import java.util.ArrayList;
|
|
|
+import java.util.List;
|
|
|
+import java.util.Map;
|
|
|
+
|
|
|
+/**
|
|
|
+ * 微信公众号服务配置
|
|
|
+ *
|
|
|
+ * @author : Charles
|
|
|
+ * @date : 2022/1/13
|
|
|
+ */
|
|
|
+@Slf4j
|
|
|
+@Service
|
|
|
+public class WechatServerServiceImpl implements WechatServerService {
|
|
|
+
|
|
|
+ @Value("${caimei.wwwDomain}")
|
|
|
+ private String wwwDomain;
|
|
|
+ @Value("${caimei.coreDomain}")
|
|
|
+ private String coreDomain;
|
|
|
+ @Value("${caimei.imageDomain}")
|
|
|
+ private String imageDomain;
|
|
|
+ @Value("${wechat.caimei.appid}")
|
|
|
+ private String caimeiAppid;
|
|
|
+ @Value("${wechat.caimei.secret}")
|
|
|
+ private String caimeiSecret;
|
|
|
+
|
|
|
+ @Resource
|
|
|
+ private WeChatDao weChatDao;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 验证消息的确来自微信服务器
|
|
|
+ *
|
|
|
+ * 通过检验signature对请求进行校验。
|
|
|
+ * 若确认此次GET请求来自微信服务器,请原样返回echostr参数内容,则接入生效,成为开发者成功,否则接入失败。
|
|
|
+ * 加密/校验流程如下:
|
|
|
+ * 1)将token、timestamp、nonce三个参数进行字典序排序
|
|
|
+ * 2)将三个参数字符串拼接成一个字符串进行sha1加密
|
|
|
+ * 3)开发者获得加密后的字符串可与signature对比,标识该请求来源于微信
|
|
|
+ */
|
|
|
+ @Override
|
|
|
+ public String validateSignature(HttpServletRequest request) {
|
|
|
+ String signature = request.getParameter("signature");
|
|
|
+ String timestamp = request.getParameter("timestamp");
|
|
|
+ String nonce = request.getParameter("nonce");
|
|
|
+ String echostr = request.getParameter("echostr");
|
|
|
+ boolean flag = SignUtil.checkSignature(signature, timestamp, nonce);
|
|
|
+ if (flag) {
|
|
|
+ return echostr;
|
|
|
+ } else {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 处理接收的文本消息
|
|
|
+ * <xml>
|
|
|
+ * <ToUserName><![CDATA[toUser]]></ToUserName>
|
|
|
+ * <FromUserName><![CDATA[fromUser]]></FromUserName>
|
|
|
+ * <CreateTime>12345678</CreateTime>
|
|
|
+ * <MsgType><![CDATA[text]]></MsgType>
|
|
|
+ * <Content><![CDATA[你好]]></Content>
|
|
|
+ * </xml>
|
|
|
+ */
|
|
|
+ @Override
|
|
|
+ public String handleTextMsg(Map<String, String> requestMap) {
|
|
|
+ // 根据接收的关键字回复
|
|
|
+ String keyword = requestMap.get(WeChatConstant.CONTENT);
|
|
|
+ // 用户openid
|
|
|
+ String openid = requestMap.get(WeChatConstant.FROM_USER_NAME);
|
|
|
+ // 微信号公众号Id
|
|
|
+ String wxType = requestMap.get(WeChatConstant.TO_USER_NAME);
|
|
|
+ log.info(">>>>>>>>>>处理接收的文本消息,keyword:" + keyword + ",openid:" + openid + ",wxType:" + wxType);
|
|
|
+ // 根据口令获取用户openid
|
|
|
+ if("caimei:get_openid".equals(keyword)) {
|
|
|
+ return MessageUtil.setTextXml(openid, wxType, openid);
|
|
|
+ }
|
|
|
+ // 根据keyword匹配数据库回复配置
|
|
|
+ WechatReply reply = weChatDao.getReplyByParams("input", keyword, wxType);
|
|
|
+// if (null == reply) {
|
|
|
+// reply = weChatDao.getReplyByParams("input", "autoReply", wxType);
|
|
|
+// }
|
|
|
+// if (null == reply) {
|
|
|
+// // 将消息转发到到客服系统
|
|
|
+// return MessageUtil.setCustomerXml(openid, wxType);
|
|
|
+// }
|
|
|
+ log.info(">>>>>>>>>>匹配已配置关键词:" + keyword);
|
|
|
+ String replyXml = setXmlByDatabaseReply(openid, wxType, reply);
|
|
|
+ if (replyXml != null) {
|
|
|
+ return replyXml;
|
|
|
+ }
|
|
|
+ log.info(">>>>>>>>>>模糊搜索:" + keyword);
|
|
|
+ WechatArticleDetail article = null;
|
|
|
+ RestTemplate restTemplate = new RestTemplate();
|
|
|
+ // https://core.caimei365.com/commodity/search/query/product?keyword=
|
|
|
+ String uri = coreDomain + "commodity/search/query/product?keyword="+keyword;
|
|
|
+ JSONObject forObject = restTemplate.getForObject(uri, JSONObject.class);
|
|
|
+ if(forObject != null){
|
|
|
+ String data = forObject.getString("data");
|
|
|
+ if(StringUtils.hasLength(data)){
|
|
|
+ JSONObject parse = JSONObject.parseObject(data);
|
|
|
+ if(parse != null){
|
|
|
+ Integer total = parse.getInteger("total");
|
|
|
+ if(total>0){
|
|
|
+ article = new WechatArticleDetail();
|
|
|
+ article.setTitle("帮您匹配到"+ total + "个商品,点击查看");
|
|
|
+ article.setPicUrl(imageDomain + "/group1/M00/03/EC/rB-lGGHg9ZOAe8VdAAJevlD1ofg562.png");
|
|
|
+ String url = wwwDomain + "product/list.html?keyword="+keyword;
|
|
|
+ try {
|
|
|
+ url = URLEncoder.encode(url, "UTF-8");
|
|
|
+ } catch (UnsupportedEncodingException ignored) {}
|
|
|
+ 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");
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (null != article) {
|
|
|
+ List<WechatArticleDetail> articles = new ArrayList<>();
|
|
|
+ articles.add(article);
|
|
|
+ return MessageUtil.setArticleXml(openid, wxType, articles);
|
|
|
+ }
|
|
|
+ log.info(">>>>>>>>>>默认无关键字匹配回复图文1220");
|
|
|
+ List<WechatArticleDetail> defaultList = weChatDao.getArticleDetailList(1220);
|
|
|
+ if (CollectionUtils.isEmpty(defaultList)) {
|
|
|
+ return MessageUtil.setArticleXml(openid, wxType, defaultList);
|
|
|
+ }
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 点击自定义菜单事件
|
|
|
+ *
|
|
|
+ * 点击菜单拉取消息时的事件推送:
|
|
|
+ * <xml>
|
|
|
+ * <ToUserName><![CDATA[toUser]]></ToUserName>
|
|
|
+ * <FromUserName><![CDATA[FromUser]]></FromUserName>
|
|
|
+ * <CreateTime>123456789</CreateTime>
|
|
|
+ * <MsgType><![CDATA[event]]></MsgType>
|
|
|
+ * <Event><![CDATA[CLICK]]></Event>
|
|
|
+ * <EventKey><![CDATA[EVENTKEY]]></EventKey>
|
|
|
+ * </xml>
|
|
|
+ *
|
|
|
+ * 点击菜单跳转链接时的事件推送:
|
|
|
+ * <xml>
|
|
|
+ * <ToUserName><![CDATA[toUser]]></ToUserName>
|
|
|
+ * <FromUserName><![CDATA[FromUser]]></FromUserName>
|
|
|
+ * <CreateTime>123456789</CreateTime>
|
|
|
+ * <MsgType><![CDATA[event]]></MsgType>
|
|
|
+ * <Event><![CDATA[VIEW]]></Event>
|
|
|
+ * <EventKey><![CDATA[www.qq.com]]></EventKey>
|
|
|
+ * </xml>
|
|
|
+ */
|
|
|
+ @Override
|
|
|
+ public String handleClickMenuMsg(Map<String, String> requestMap) {
|
|
|
+ // 用户openid
|
|
|
+ String openid = requestMap.get(WeChatConstant.FROM_USER_NAME);
|
|
|
+ // 微信号公众号Id
|
|
|
+ String wxType = requestMap.get(WeChatConstant.TO_USER_NAME);
|
|
|
+ // 事件类型
|
|
|
+ String event = requestMap.get(WeChatConstant.EVENT);
|
|
|
+ // 事件KEY值,设置的跳转URL/与自定义菜单接口中KEY值对应
|
|
|
+ String eventKey = requestMap.get(WeChatConstant.EVENT_KEY);
|
|
|
+ log.info(">>>>>>>>>>处理点击自定义菜单事件,event:" + event + ",eventKey:" + eventKey + ",openid:" + openid + ",wxType:" + wxType);
|
|
|
+ // 匹配数据库回复配置
|
|
|
+ WechatReply reply = weChatDao.getReplyByParams("click", eventKey, wxType);
|
|
|
+ return setXmlByDatabaseReply(openid, wxType, reply);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 上报地理位置
|
|
|
+ * <xml>
|
|
|
+ * <ToUserName><![CDATA[toUser]]></ToUserName>
|
|
|
+ * <FromUserName><![CDATA[fromUser]]></FromUserName>
|
|
|
+ * <CreateTime>123456789</CreateTime>
|
|
|
+ * <MsgType><![CDATA[event]]></MsgType>
|
|
|
+ * <Event><![CDATA[LOCATION]]></Event>
|
|
|
+ * <Latitude>23.137466</Latitude>
|
|
|
+ * <Longitude>113.352425</Longitude>
|
|
|
+ * <Precision>119.385040</Precision>
|
|
|
+ * </xml>
|
|
|
+ *
|
|
|
+ * * <纬度>23.137466</纬度>
|
|
|
+ * * <经度>113.352425</经度>
|
|
|
+ * * <精度>119.385040</精度>
|
|
|
+ */
|
|
|
+ @Override
|
|
|
+ public String handleLocationMsg(Map<String, String> requestMap) {
|
|
|
+ // 用户openid
|
|
|
+ String openid = requestMap.get(WeChatConstant.FROM_USER_NAME);
|
|
|
+ // 微信号公众号Id
|
|
|
+ String wxType = requestMap.get(WeChatConstant.TO_USER_NAME);
|
|
|
+ //Longitude 地理位置经度
|
|
|
+ String longitude = requestMap.get(WeChatConstant.LONGITUDE);
|
|
|
+ //Latitude 地理位置纬度
|
|
|
+ String latitude = requestMap.get(WeChatConstant.LATITUDE);
|
|
|
+ //Precision 地理位置精度
|
|
|
+ String precision = requestMap.get(WeChatConstant.PRECISION);
|
|
|
+ String content = "您当前位置,经度:" + longitude + ",纬度:" + latitude + ",精度:" + precision;
|
|
|
+ log.info(">>>>>>>>>>处理上报地理位置事件," + content + ",openid:" + openid + ",wxType:" + wxType);
|
|
|
+ return MessageUtil.setTextXml(openid, wxType, content);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * subscribe(订阅/关注)
|
|
|
+ * <xml>
|
|
|
+ * <ToUserName><![CDATA[toUser]]></ToUserName>
|
|
|
+ * <FromUserName><![CDATA[FromUser]]></FromUserName>
|
|
|
+ * <CreateTime>123456789</CreateTime>
|
|
|
+ * <MsgType><![CDATA[event]]></MsgType>
|
|
|
+ * <Event><![CDATA[subscribe]]></Event>
|
|
|
+ * </xml>
|
|
|
+ */
|
|
|
+ @Override
|
|
|
+ public String handleSubscribeMsg(Map<String, String> requestMap) {
|
|
|
+ // 用户openid
|
|
|
+ String openid = requestMap.get(WeChatConstant.FROM_USER_NAME);
|
|
|
+ // 微信号公众号Id
|
|
|
+ String wxType = requestMap.get(WeChatConstant.TO_USER_NAME);
|
|
|
+ log.info(">>>>>>>>>>处理接收的订阅关注事件,openid:" + openid + ",wxType:" + wxType);
|
|
|
+ // 匹配数据库回复配置
|
|
|
+ WechatReply reply = weChatDao.getReplyByParams("subscribe", null, wxType);
|
|
|
+ return setXmlByDatabaseReply(openid, wxType, reply);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 扫描带参数二维码事件
|
|
|
+ * <xml>
|
|
|
+ * <ToUserName><![CDATA[toUser]]></ToUserName>
|
|
|
+ * <FromUserName><![CDATA[FromUser]]></FromUserName>
|
|
|
+ * <CreateTime>123456789</CreateTime>
|
|
|
+ * <MsgType><![CDATA[event]]></MsgType>
|
|
|
+ * <Event><![CDATA[subscribe]]></Event>
|
|
|
+ * <EventKey><![CDATA[qrscene_123123]]></EventKey>
|
|
|
+ * <Ticket><![CDATA[TICKET]]></Ticket>
|
|
|
+ * </xml>
|
|
|
+ */
|
|
|
+ @Override
|
|
|
+ public String handleScanMsg(Map<String, String> requestMap) {
|
|
|
+ // 用户openid
|
|
|
+ String openid = requestMap.get(WeChatConstant.FROM_USER_NAME);
|
|
|
+ // 微信号公众号Id
|
|
|
+ String wxType = requestMap.get(WeChatConstant.TO_USER_NAME);
|
|
|
+ // 扫码参数
|
|
|
+ String eventKey = requestMap.get(WeChatConstant.EVENT_KEY);
|
|
|
+ log.info(">>>>>>>>>>处理扫描带参数二维码事件,二维码参数:" + eventKey + ",openid:" + openid + ",wxType:" + wxType);
|
|
|
+ //todo 用户绑定微信
|
|
|
+
|
|
|
+
|
|
|
+ // 匹配数据库回复配置
|
|
|
+ WechatReply reply = weChatDao.getReplyByParams("subscribe", null, wxType);
|
|
|
+ return setXmlByDatabaseReply(openid, wxType, reply);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 根据数据库回复配置设置
|
|
|
+ */
|
|
|
+ private String setXmlByDatabaseReply(String openid, String wxType, WechatReply reply) {
|
|
|
+ if ("news".equals(reply.getResponseType())) {
|
|
|
+ // 回复图文
|
|
|
+ List<WechatArticleDetail> articleList = weChatDao.getArticleDetailList(reply.getRelateId());
|
|
|
+ if (CollectionUtils.isEmpty(articleList)) {
|
|
|
+ return MessageUtil.setArticleXml(openid, wxType, articleList);
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ // 回复文本
|
|
|
+ String textContent = weChatDao.getTextContent(reply.getRelateId());
|
|
|
+ if (StringUtils.hasLength(textContent)) {
|
|
|
+ return MessageUtil.setTextXml(openid, wxType, textContent);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+}
|