Ver Fonte

Merge branch 'master' of ssh://192.168.8.213:10022/miaxis/twzd

Althars123 há 3 anos atrás
pai
commit
e3fc6277f3
31 ficheiros alterados com 1464 adições e 307 exclusões
  1. 56 291
      twzd-admin/src/main/java/com/miaxis/system/controller/test/TestController.java
  2. 101 0
      twzd-admin/src/main/java/com/miaxis/system/controller/wx/WxgzhController.java
  3. 17 0
      twzd-admin/src/main/resources/application-dev.yml
  4. 20 0
      twzd-admin/src/main/resources/application-prod.yml
  5. 63 0
      twzd-common/src/main/java/com/miaxis/common/config/CosConfig.java
  6. 9 0
      twzd-common/src/main/java/com/miaxis/common/constant/Constants.java
  7. 161 0
      twzd-common/src/main/java/com/miaxis/common/utils/XmlUtil.java
  8. 73 4
      twzd-common/src/main/java/com/miaxis/common/utils/http/HttpUtils.java
  9. 98 0
      twzd-common/src/main/java/com/miaxis/common/utils/wx/MessageUtil.java
  10. 11 0
      twzd-service/pom.xml
  11. 1 1
      twzd-service/src/main/java/com/miaxis/feign/dto/WxQrTicket.java
  12. 0 2
      twzd-service/src/main/java/com/miaxis/feign/service/IWxMpService.java
  13. 60 6
      twzd-service/src/main/java/com/miaxis/feign/service/IWxSendService.java
  14. 61 0
      twzd-service/src/main/java/com/miaxis/file/domain/FileInfo.java
  15. 16 0
      twzd-service/src/main/java/com/miaxis/file/mapper/FileInfoMapper.java
  16. 15 0
      twzd-service/src/main/java/com/miaxis/file/service/IFileInfoService.java
  17. 23 0
      twzd-service/src/main/java/com/miaxis/file/service/impl/FileInfoServiceImpl.java
  18. 39 0
      twzd-service/src/main/java/com/miaxis/spread/domain/WxSpreadRelation.java
  19. 13 0
      twzd-service/src/main/java/com/miaxis/spread/mapper/WxSpreadRelationMapper.java
  20. 13 0
      twzd-service/src/main/java/com/miaxis/spread/service/IWxSpreadRelationService.java
  21. 22 0
      twzd-service/src/main/java/com/miaxis/spread/service/impl/WxSpreadRelationServiceImpl.java
  22. 69 0
      twzd-service/src/main/java/com/miaxis/wx/domain/WxMenu.java
  23. 47 0
      twzd-service/src/main/java/com/miaxis/wx/domain/WxTicket.java
  24. 13 0
      twzd-service/src/main/java/com/miaxis/wx/mapper/WxMenuMapper.java
  25. 13 0
      twzd-service/src/main/java/com/miaxis/wx/mapper/WxTicketMapper.java
  26. 21 0
      twzd-service/src/main/java/com/miaxis/wx/service/IWxGzhService.java
  27. 31 0
      twzd-service/src/main/java/com/miaxis/wx/service/IWxMessageEvenService.java
  28. 13 0
      twzd-service/src/main/java/com/miaxis/wx/service/IWxTicketService.java
  29. 163 3
      twzd-service/src/main/java/com/miaxis/wx/service/impl/WxGzhServiceImpl.java
  30. 200 0
      twzd-service/src/main/java/com/miaxis/wx/service/impl/WxMessageEvenServiceImpl.java
  31. 22 0
      twzd-service/src/main/java/com/miaxis/wx/service/impl/WxTicketServiceImpl.java

+ 56 - 291
twzd-admin/src/main/java/com/miaxis/system/controller/test/TestController.java

@@ -1,44 +1,32 @@
 package com.miaxis.system.controller.test;
 
-import cn.hutool.core.io.FileUtil;
 import cn.hutool.extra.qrcode.QrCodeUtil;
-import cn.hutool.json.XML;
-import com.alibaba.fastjson.JSONObject;
 import com.miaxis.common.constant.Constants;
 import com.miaxis.common.core.domain.Response;
-import com.miaxis.common.utils.wx.SignUtil;
-import com.miaxis.feign.dto.WxTicket;
 import com.miaxis.feign.service.IWxMpService;
 import com.miaxis.feign.service.IWxSendService;
 import com.miaxis.wx.service.IWxGzhService;
+import com.qcloud.cos.COSClient;
+import com.qcloud.cos.model.ObjectMetadata;
+import com.qcloud.cos.model.PutObjectRequest;
+import com.qcloud.cos.model.PutObjectResult;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
 import lombok.RequiredArgsConstructor;
-import org.apache.commons.io.IOUtils;
-import org.apache.commons.lang3.StringUtils;
 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.web.bind.annotation.GetMapping;
 import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RequestMethod;
 import org.springframework.web.bind.annotation.RestController;
-import org.w3c.dom.Node;
-import org.w3c.dom.NodeList;
 
+import javax.imageio.ImageIO;
 import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-import javax.xml.parsers.DocumentBuilder;
-import javax.xml.parsers.DocumentBuilderFactory;
-import javax.xml.transform.OutputKeys;
-import javax.xml.transform.Transformer;
-import javax.xml.transform.TransformerFactory;
-import javax.xml.transform.dom.DOMSource;
-import javax.xml.transform.stream.StreamResult;
-import java.io.*;
-import java.util.Date;
-import java.util.HashMap;
-import java.util.Map;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.InputStream;
+import java.util.Calendar;
 
 /**
  * @author wwl
@@ -65,285 +53,62 @@ public class TestController {
     @Autowired
     HttpServletRequest request;
 
-    /**
-     * 生成带参数的二维码
-     */
-    @GetMapping(value = "/generateTicket")
-    @ApiOperation("生成带参数的二维码")
-    public Response generateTicket() throws Exception{
-        String xcxMessageToken = wxGzhService.getGzhToken();
-        WxTicket wxTicket = new WxTicket();
-        wxTicket.setExpire_seconds(86400);//一天
-        wxTicket.setAction_name("QR_SCENE");
-        JSONObject jsonObject1 = new JSONObject();
-        JSONObject jsonObject2 = new JSONObject();
-        jsonObject1.put("scene_id","123");
-        jsonObject2.put("scene",jsonObject1);
-        wxTicket.setAction_info(jsonObject2);
-        String result = wxSendService.generateTicket(xcxMessageToken,wxTicket);
-        System.out.println("生成ticket:" + result);
-        JSONObject jsonStr = JSONObject.parseObject(result);
-
-        QrCodeUtil.generate(
-                jsonStr.getString("url"), //二维码内容
-                300, 300,
-                //QrConfig.create().setImg("C:\\Users\\wwl\\Desktop\\二维码\\微信图片_20211021120216.jpg")
-                FileUtil.file("/data/test/qrimg.jpg")//写出到的文件
-        );
-
-        return Response.success();
-    }
-
-
-
-    /**
-     * 处理服务器推送消息
-     * @param request
-     * @param response
-     * @return
-     */
-    @RequestMapping(value = "/notifyMsg",method = { RequestMethod.GET, RequestMethod.POST })
-    @ApiOperation("处理服务器推送消息")
-    public String checkSign(HttpServletRequest request, HttpServletResponse response) {
-        log.info("-----------进入---------");
-        try {
-            String signature = request.getParameter("signature");
-            String timestamp = request.getParameter("timestamp");
-            String nonce = request.getParameter("nonce");
-            String echostr = request.getParameter("echostr");
-            log.info("本身" + signature);
-            if(request.getMethod().equals("GET")){
-                log.info("-----------微信验签---------");
-                return SignUtil.checkSignature(signature, timestamp, nonce) == true ? echostr : null;
-            }else{
-                log.info("-----------推送消息处理---------");
-                return handlePublicMsg(request);
-            }
-        } catch (Exception e) {
-            log.error("验证公众号token失败", e);
-        }
-        return null;
-    }
-
-
-    //微信功能处理
-    public String WexHandeler(HttpServletRequest request) throws Exception {
-        try {
-            cn.hutool.json.JSONObject info = XML.toJSONObject(IOUtils.toString(request.getInputStream()));
-            log.info("info:" + info);
-            if (info.get("xml") != null) {
-                JSONObject event = (JSONObject) info.get("xml");
-                if (event.get("MsgType") != null) {
-                    String Event = (String) event.get("Event");//事件类型 关注事件还是取消关注等
-                    //如果是事件
-                    if (!StringUtils.isEmpty(Event)) try {
-                        if ("subscribe".equals(Event)) {  //带参数二维码的关注事件
-                            //关注方法
-                            String toUserName = (String) event.get("ToUserName");//开发者微信号
-                            String fromUserName = (String) event.get("FromUserName");//发送方帐号(一个OpenID)
-                            String eventKey = (String) event.get("EventKey");//事件KEY值,是一个32位无符号整数,即创建二维码时的二维码scene_id
-                            String ticket = (String) event.get("Ticket");//二维码的ticket,可用来换取二维码图片
-                            log.info(">>>>>>>>>>>>>>>>>>接受的数据为:");
-                            log.info("toUserName:" + toUserName);
-                            log.info("fromUserName:" + fromUserName);
-                            log.info("scene_id:" + eventKey);
-                            log.info("ticket:" + ticket);
-                            //解析完执行操作
-
-                        } else if ("SCAN".equals(Event)) {//扫描带参数二维码事件 用户已关注时的事件推送
-                            String toUserName = (String) event.get("ToUserName");//开发者微信号
-                            String fromUserName = (String) event.get("FromUserName");//发送方帐号(一个OpenID)
-                            String eventKey = (String) event.get("EventKey");//事件KEY值,是一个32位无符号整数,即创建二维码时的二维码scene_id
-                            String ticket = (String) event.get("Ticket");//二维码的ticket,可用来换取二维码图片
-                            log.info(">>>>>>>>>>>>>>>>>>接受的数据为:");
-                            log.info("toUserName:" + toUserName);
-                            log.info("fromUserName:" + fromUserName);
-                            log.info("scene_id:" + eventKey);
-                            log.info("ticket:" + ticket);
-                            //解析完执行操作
-
-                        } else if ("unsubscribe".equals(Event)) { //取消订阅事件
-
-                        }
-                    } catch (Exception e) {
-                        return e.getMessage();
-                    }
-                }
-            }
-        } catch (Exception e) {
-            return request.getParameter("echostr");
-        }
-        return request.getParameter("echostr");
-    }
-
-
+    @Autowired
+    private COSClient cosClient;
 
-    /**
-     * 处理微信公众号请求信息
-     */
-    public String handlePublicMsg(HttpServletRequest request) throws Exception {
-        log.info("1....");
-        // 获得微信端返回的xml数据
-        InputStream is = null;
-        InputStreamReader isr = null;
-        BufferedReader br = null;
-        try {
-            is = request.getInputStream();
-            isr = new InputStreamReader(is, "utf-8");
-            br = new BufferedReader(isr);
-            log.info("2....");
-            String str = null;
-            StringBuffer returnXml= new StringBuffer();
-            while ((str = br.readLine()) != null) {
-                log.info("3....");
-                //返回的是xml数据
-                returnXml.append(str);
-            }
-//            Map<String, String> decryptMap = xmlToMap(returnXml.toString());
-            Map<String, String> decryptMap = xmlToMap(returnXml.toString());
-            log.info("-----decryptMap------"+decryptMap);
-//            // 得到公众号传来的加密信息并解密,得到的是明文xml数据
-//            String decryptXml = WXPublicUtils.decrypt(encryptMap.get("Encrypt"));
-            // 将xml数据转换为map
-            //Map<String, String> decryptMap = xmlToMap(encryptMap);
-            log.info("5....");
+    @Value("${cos.bucketName}")
+    private String bucketName;
+    @Value("${cos.path}")
+    private String path;
+    @Value("${cos.preffix}")
+    private String preffix;
 
-            // 区分消息类型
-            String msgType = decryptMap.get("MsgType");
-            log.info("5.1...");
-            // 普通消息
-            if ("text".equals(msgType)) { // 文本消息
-                // todo 处理文本消息
-            } else if ("image".equals(msgType)) { // 图片消息
-                // todo 处理图片消息
-            }
+    @Value("${file.ticketPath}")
+    private String ticketPath;
 
-            // 事件推送
-            else if ("event".equals(msgType)) { // 事件消息
-                // 区分事件推送
-                String event = decryptMap.get("Event");
-                if ("subscribe".equals(event)) { // 订阅事件 或 未关注扫描二维码事件
-                    log.info("5.2...");
-                    // 返回消息时ToUserName的值与FromUserName的互换
-                    Map<String, String> returnMap = new HashMap<>();
-                    returnMap.put("ToUserName", decryptMap.get("FromUserName"));
-                    returnMap.put("FromUserName", decryptMap.get("ToUserName"));
-                    returnMap.put("CreateTime", new Date().getTime()+"");
-                    returnMap.put("MsgType", "text");
-                    returnMap.put("Content", "测试哈哈哈哈");
-                    log.info("5.2...----returnMap-----"+returnMap);
-                    String encryptMsg = mapToXml(returnMap);
-                    return encryptMsg;
-                }  else if ("unsubscribe".equals(event)) { // 取消订阅事件
-                    // todo 处理取消订阅事件
-                } else if ("SCAN".equals(event)) { // 已关注扫描二维码事件
-                    log.info("5.3...");
-                    // 返回消息时ToUserName的值与FromUserName的互换
-                    Map<String, String> returnMap = new HashMap<>();
-                    returnMap.put("ToUserName", decryptMap.get("FromUserName"));
-                    returnMap.put("FromUserName", decryptMap.get("ToUserName"));
-                    returnMap.put("CreateTime", new Date().getTime()+"");
-                    returnMap.put("MsgType", "text");
-                    returnMap.put("Content", "测试哈哈哈哈");
-                    log.info("5.3...----returnMap-----"+returnMap);
-                    String encryptMsg = mapToXml(returnMap);
-                    return encryptMsg;
-                } else if ("LOCATION".equals(event)) { // 上报地理位置事件
-                    // todo 处理上报地理位置事件
-                } else if ("CLICK".equals(event)) { // 点击菜单拉取消息时的事件推送事件
-                    // todo 处理点击菜单拉取消息时的事件推送事件
-                } else if ("VIEW".equals(event)) { // 点击菜单跳转链接时的事件推送
-                    // todo 处理点击菜单跳转链接时的事件推送
-                }
-            }
-        } catch (Exception e) {
-            log.error("处理微信公众号请求信息,失败", e);
-        }
-        return null;
-    }
+    @Value("${wxgzh.materialUrl}")
+    private String materialUrl;
 
 
 
     /**
-     * XML格式字符串转换为Map
-     *
-     * @param strXML XML字符串
-     * @return XML数据转换后的Map
-     * @throws Exception
+     * cos上传二维码
      */
-    public static Map<String, String> xmlToMap(String strXML) throws Exception {
-        log.info("4....");
-        try {
-            Map<String, String> data = new HashMap<>();
-            DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
-            DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
-            InputStream stream = new ByteArrayInputStream(strXML.getBytes("UTF-8"));
-            org.w3c.dom.Document doc = documentBuilder.parse(stream);
-            doc.getDocumentElement().normalize();
-            NodeList nodeList = doc.getDocumentElement().getChildNodes();
-            for (int idx = 0; idx < nodeList.getLength(); ++idx) {
-                Node node = nodeList.item(idx);
-                if (node.getNodeType() == Node.ELEMENT_NODE) {
-                    org.w3c.dom.Element element = (org.w3c.dom.Element) node;
-                    data.put(element.getNodeName(), element.getTextContent());
-                }
-            }
-            try {
-                stream.close();
-            } catch (Exception ex) {
-                // do nothing
-            }
-            return data;
-        } catch (Exception ex) {
-            throw ex;
-        }
+    @GetMapping(value = "/test01")
+    @ApiOperation("cos上传二维码")
+    public Response test01() throws Exception{
+
+        ByteArrayOutputStream os = new ByteArrayOutputStream();
+
+        ImageIO.write(QrCodeUtil.generate(
+                "二维码内容", //二维码内容
+                300, 300
+        ), "JPG", os);
+        InputStream inputStream = new ByteArrayInputStream(os.toByteArray());
+
+        ObjectMetadata objectMetadata = new ObjectMetadata();
+        // 从输入流上传必须制定content length, 否则http客户端可能会缓存所有数据,存在内存OOM的情况
+        objectMetadata.setContentLength(os.size());
+
+        // bucket的命名规则为{name}-{appid} ,此处填写的存储桶名称必须为此格式
+        String bucketName = this.bucketName;
+
+        Calendar cal = Calendar.getInstance();
+        int year = cal.get(Calendar.YEAR);
+        int month=cal.get(Calendar.MONTH)+ 1;
+        int day=cal.get(Calendar.DATE);
+
+        //存储的文件名
+        String storagefileName = System.currentTimeMillis() + ".png";
+        // 指定要上传到 COS 上的路径
+        String key = "/"+this.preffix+"/"+ "testTicket"+"/"+year+"/"+month+"/"+day+"/"+storagefileName;
+        PutObjectRequest putObjectRequest = new PutObjectRequest(bucketName, key, inputStream,objectMetadata);
+        PutObjectResult putObjectResult = cosClient.putObject(putObjectRequest);
+        String fileUrl=this.path + putObjectRequest.getKey();
+
+        return Response.success(fileUrl);
     }
 
-    /**
-     * 将Map转换为XML格式的字符串
-     *
-     * @param data Map类型数据
-     * @return XML格式的字符串
-     * @throws Exception
-     */
-    public static String mapToXml(Map<String, String> data) throws Exception {
-        log.info("----mapToXml-----"+data);
-        DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
-        DocumentBuilder documentBuilder= documentBuilderFactory.newDocumentBuilder();
-        org.w3c.dom.Document document = documentBuilder.newDocument();
-        org.w3c.dom.Element root = document.createElement("xml");
-        document.appendChild(root);
-        for (String key: data.keySet()) {
-            String value = data.get(key);
-            if (value == null) {
-                value = "";
-            }
-            value = value.trim();
-            org.w3c.dom.Element filed = document.createElement(key);
-            filed.appendChild(document.createTextNode(value));
-            root.appendChild(filed);
-        }
-        TransformerFactory tf = TransformerFactory.newInstance();
-        Transformer transformer = tf.newTransformer();
-        DOMSource source = new DOMSource(document);
-        transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
-        transformer.setOutputProperty(OutputKeys.INDENT, "yes");
-        StringWriter writer = new StringWriter();
-        StreamResult result = new StreamResult(writer);
-        transformer.transform(source, result);
-        String output = writer.getBuffer().toString(); //.replaceAll("\n|\r", "");
-        try {
-            writer.close();
-        } catch (Exception ex) {
-
-        }
-        log.info("----mapToXml-return-----"+output);
-        return output;
-    }
-
-
-
-
 
 
 }

+ 101 - 0
twzd-admin/src/main/java/com/miaxis/system/controller/wx/WxgzhController.java

@@ -0,0 +1,101 @@
+package com.miaxis.system.controller.wx;
+
+import com.alibaba.fastjson.JSONObject;
+import com.miaxis.common.constant.Constants;
+import com.miaxis.common.core.domain.Response;
+import com.miaxis.common.exception.CustomException;
+import com.miaxis.common.utils.wx.SignUtil;
+import com.miaxis.feign.service.IWxMpService;
+import com.miaxis.feign.service.IWxSendService;
+import com.miaxis.wx.service.IWxGzhService;
+import com.qcloud.cos.COSClient;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.RestController;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+/**
+ * 微信公众号相关
+ * @author wwl
+ * @version 1.0
+ * @date 2021/10/26 9:28
+ */
+@RestController
+@RequiredArgsConstructor
+@RequestMapping(Constants.OPEN_PREFIX + "/wx/gzh")
+@Api(tags={"wx微信公众号相关"})
+@Slf4j
+public class WxgzhController {
+
+    private final IWxSendService wxSendService;
+
+    private final IWxGzhService wxGzhService;
+
+    private final HttpServletRequest request;
+
+    @Value("${cos.bucketName}")
+    private String bucketName;
+    @Value("${cos.path}")
+    private String path;
+    @Value("${cos.preffix}")
+    private String preffix;
+
+
+    /**{"errcode":40017,"errmsg":"invalid button type rid: 61777f8d-226e0008-464b894c"}
+     * 获取微信公众号菜单
+     * @return
+     */
+    @GetMapping(value = "sendWxGzptMenu")
+    @ApiOperation("获取微信公众号菜单")
+    public Response sendWxGzptMenu(){
+        try{
+            String token = wxGzhService.getGzhToken();
+            JSONObject wxMenuJson = wxGzhService.getWxMenuJson();
+            log.info("--------"+wxMenuJson);
+            String result = wxSendService.createMenu(token,wxMenuJson);
+            return Response.success(result);
+        }catch (Exception e){
+            throw new CustomException("系统异常");
+        }
+    }
+
+
+    /**
+     * 处理服务器推送消息
+     * @param request
+     * @param response
+     * @return
+     */
+    @RequestMapping(value = "/notifyMsg",method = { RequestMethod.GET, RequestMethod.POST })
+    @ApiOperation("处理服务器推送消息")
+    public String checkSign(HttpServletRequest request, HttpServletResponse response) {
+        log.info("-----------进入---------");
+        try {
+            String signature = request.getParameter("signature");
+            String timestamp = request.getParameter("timestamp");
+            String nonce = request.getParameter("nonce");
+            String echostr = request.getParameter("echostr");
+            log.info("本身" + signature);
+            if(request.getMethod().equals("GET")){
+                log.info("-----------微信验签---------");
+                return SignUtil.checkSignature(signature, timestamp, nonce) == true ? echostr : null;
+            }else{
+                log.info("-----------推送消息处理---------");
+                return wxGzhService.handlePublicMsg(request);
+            }
+        } catch (Exception e) {
+            log.error("验证公众号token失败", e);
+        }
+        return null;
+    }
+
+
+}

+ 17 - 0
twzd-admin/src/main/resources/application-dev.yml

@@ -97,4 +97,21 @@ wxpay:
     notifyUrlRefund: http://218.85.55.253:65535/twzd-admin/open-api/wx/notify/refund
 
 
+# 腾讯cos
+cos:
+    secretId: AKIDwISNOFsJXYGjy89FJI9UnzuZFgTtRgFe
+    secretKey: IK5af8MJzPoKbdQxDCtKWR5T5PSEkyDB
+    bucket: ap-shanghai
+    bucketName: twzd-1305573081
+    path: https://twzd-1305573081.cos.ap-shanghai.myqcloud.com
+    preffix: twzd-test
 
+
+# 分销二维码生成路径
+file:
+    ticketPath: /data/test/
+
+
+# 公众号上传永久素材url
+wxgzh:
+    materialUrl: https://api.weixin.qq.com/cgi-bin/material/add_material?access_token=

+ 20 - 0
twzd-admin/src/main/resources/application-prod.yml

@@ -92,3 +92,23 @@ wxpay:
     v3key: 8604f2a6eb6338cfa64e7df4ec2c08b3
     notifyUrl: http://	jpcj-h5.zzxcx.net.zzxcx.net/prod-api/open-api/wx/notify/wxpay
     notifyUrlRefund: http://	jpcj-h5.zzxcx.net.zzxcx.net/prod-api/open-api/wx/notify/refund
+
+
+# 腾讯cos
+cos:
+    secretId: AKIDwISNOFsJXYGjy89FJI9UnzuZFgTtRgFe
+    secretKey: IK5af8MJzPoKbdQxDCtKWR5T5PSEkyDB
+    bucket: ap-shanghai
+    bucketName: twzd-1305573081
+    path: https://twzd-1305573081.cos.ap-shanghai.myqcloud.com
+    preffix: twzd-prod
+
+
+# 带参数的二维码生成路径
+file:
+    ticketPath: /data/test/
+
+
+# 公众号上传永久素材url
+wxgzh:
+    materialUrl: https://api.weixin.qq.com/cgi-bin/material/add_material?access_token=

+ 63 - 0
twzd-common/src/main/java/com/miaxis/common/config/CosConfig.java

@@ -0,0 +1,63 @@
+package com.miaxis.common.config;
+
+import com.qcloud.cos.COSClient;
+import com.qcloud.cos.ClientConfig;
+import com.qcloud.cos.auth.BasicCOSCredentials;
+import com.qcloud.cos.auth.COSCredentials;
+import com.qcloud.cos.http.HttpProtocol;
+import com.qcloud.cos.region.Region;
+import com.wechat.pay.contrib.apache.httpclient.auth.AutoUpdateCertificatesVerifier;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * cos对象存储
+ * @author wwl
+ * @version 1.0
+ * @date 2021/10/25 15:40
+ */
+@Configuration
+public class CosConfig {
+
+    @Value("${cos.secretId}")
+    private String secretId;
+    @Value("${cos.secretKey}")
+    private String secretKey;
+    @Value("${cos.bucket}")
+    private String bucket;
+    @Value("${cos.bucketName}")
+    private String bucketName;
+    @Value("${cos.path}")
+    private String path;
+    @Value("${cos.preffix}")
+    private String preffix;
+
+
+    /**
+     * 腾讯云 COS
+     * @return
+     */
+
+    @Bean
+    public COSClient COSClientconfigBean() {
+        // 1 初始化用户身份信息(secretId, secretKey)。
+        // SECRETID和SECRETKEY请登录访问管理控制台 https://console.cloud.tencent.com/cam/capi 进行查看和管理
+        String secretId = this.secretId;
+        String secretKey = this.secretKey;
+        COSCredentials cred = new BasicCOSCredentials(secretId, secretKey);
+        // 2 设置 bucket 的地域, COS 地域的简称请参照 https://cloud.tencent.com/document/product/436/6224
+        // clientConfig 中包含了设置 region, https(默认 http), 超时, 代理等 set 方法, 使用可参见源码或者常见问题 Java SDK 部分。
+        Region region = new Region(this.bucket);
+        ClientConfig clientConfig = new ClientConfig(region);
+        // 这里建议设置使用 https 协议
+        // 从 5.6.54 版本开始,默认使用了 https
+        clientConfig.setHttpProtocol(HttpProtocol.https);
+        // 3 生成 cos 客户端。
+        COSClient cosClient = new COSClient(cred, clientConfig);
+        return cosClient;
+    }
+
+
+}

+ 9 - 0
twzd-common/src/main/java/com/miaxis/common/constant/Constants.java

@@ -78,6 +78,15 @@ public class Constants
      */
     public static final String GZH_MESSAGE_TOKEN = "gzh:message:token:";
 
+    /**
+     * 公众号推广码ticket
+     */
+    public static final String GZH_TICKET_KEY = "gzh:ticket:";
+    /**
+     * 公众号推广码素材信息
+     */
+    public static final String GZH_MATERIAL_KEY = "gzh:material:";
+
 
     /**
      * 登录用户 redis key

+ 161 - 0
twzd-common/src/main/java/com/miaxis/common/utils/XmlUtil.java

@@ -0,0 +1,161 @@
+package com.miaxis.common.utils;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.transform.OutputKeys;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.stream.StreamResult;
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.io.StringWriter;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * xml util
+ * @author wwl
+ * @version 1.0
+ * @date 2021/10/26 14:55
+ */
+public class XmlUtil {
+
+    private static final Logger log = LoggerFactory.getLogger(XmlUtil.class);
+
+    /**
+     * 将Map转换为XML格式的字符串
+     *
+     * @param data Map类型数据
+     * @return XML格式的字符串
+     * @throws Exception
+     */
+    public static String mapToXml(Map<String, String> data) throws Exception {
+        log.info("----mapToXml-----"+data);
+        DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
+        DocumentBuilder documentBuilder= documentBuilderFactory.newDocumentBuilder();
+        org.w3c.dom.Document document = documentBuilder.newDocument();
+        org.w3c.dom.Element root = document.createElement("xml");
+        document.appendChild(root);
+        for (String key: data.keySet()) {
+            String value = data.get(key);
+            if (value == null) {
+                value = "";
+            }
+            value = value.trim();
+            org.w3c.dom.Element filed = document.createElement(key);
+            filed.appendChild(document.createTextNode(value));
+            root.appendChild(filed);
+        }
+        TransformerFactory tf = TransformerFactory.newInstance();
+        Transformer transformer = tf.newTransformer();
+        DOMSource source = new DOMSource(document);
+        transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
+        transformer.setOutputProperty(OutputKeys.INDENT, "yes");
+        StringWriter writer = new StringWriter();
+        StreamResult result = new StreamResult(writer);
+        transformer.transform(source, result);
+        String output = writer.getBuffer().toString(); //.replaceAll("\n|\r", "");
+        try {
+            writer.close();
+        } catch (Exception ex) {
+
+        }
+        log.info("----mapToXml-return-----"+output);
+        return output;
+    }
+
+
+    /**
+     * 微信回复图片 map转换xml
+     * @param data
+     * @return
+     * @throws Exception
+     */
+    public static String mapToXmlSpecial(Map<String, String> data) throws Exception {
+        log.info("----mapToXml-----"+data);
+        DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
+        DocumentBuilder documentBuilder= documentBuilderFactory.newDocumentBuilder();
+        org.w3c.dom.Document document = documentBuilder.newDocument();
+        org.w3c.dom.Element root = document.createElement("xml");
+        document.appendChild(root);
+
+        org.w3c.dom.Element toUserName = document.createElement("ToUserName");
+        toUserName.setTextContent(data.get("ToUserName"));
+        root.appendChild(toUserName);
+        org.w3c.dom.Element fromUserName = document.createElement("FromUserName");
+        fromUserName.setTextContent(data.get("FromUserName"));
+        root.appendChild(fromUserName);
+        org.w3c.dom.Element createTime = document.createElement("CreateTime");
+        createTime.setTextContent(data.get("CreateTime"));
+        root.appendChild(createTime);
+        org.w3c.dom.Element msgType = document.createElement("MsgType");
+        msgType.setTextContent(data.get("MsgType"));
+        root.appendChild(msgType);
+        org.w3c.dom.Element image = document.createElement("Image");
+        root.appendChild(image);
+        org.w3c.dom.Element mediaId = document.createElement("MediaId");
+        mediaId.setTextContent(data.get("MediaId"));
+        image.appendChild(mediaId);
+
+        TransformerFactory tf = TransformerFactory.newInstance();
+        Transformer transformer = tf.newTransformer();
+        DOMSource source = new DOMSource(document);
+        transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
+        transformer.setOutputProperty(OutputKeys.INDENT, "yes");
+        StringWriter writer = new StringWriter();
+        StreamResult result = new StreamResult(writer);
+        transformer.transform(source, result);
+        String output = writer.getBuffer().toString(); //.replaceAll("\n|\r", "");
+        try {
+            writer.close();
+        } catch (Exception ex) {
+
+        }
+        log.info("----mapToXml-return-----"+output);
+        return output;
+    }
+
+
+    /**
+     * XML格式字符串转换为Map
+     *
+     * @param strXML XML字符串
+     * @return XML数据转换后的Map
+     * @throws Exception
+     */
+    public static Map<String, String> xmlToMap(String strXML) throws Exception {
+        log.info("4....");
+        try {
+            Map<String, String> data = new HashMap<>();
+            DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
+            DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
+            InputStream stream = new ByteArrayInputStream(strXML.getBytes("UTF-8"));
+            org.w3c.dom.Document doc = documentBuilder.parse(stream);
+            doc.getDocumentElement().normalize();
+            NodeList nodeList = doc.getDocumentElement().getChildNodes();
+            for (int idx = 0; idx < nodeList.getLength(); ++idx) {
+                Node node = nodeList.item(idx);
+                if (node.getNodeType() == Node.ELEMENT_NODE) {
+                    org.w3c.dom.Element element = (org.w3c.dom.Element) node;
+                    data.put(element.getNodeName(), element.getTextContent());
+                }
+            }
+            try {
+                stream.close();
+            } catch (Exception ex) {
+                // do nothing
+            }
+            return data;
+        } catch (Exception ex) {
+            throw ex;
+        }
+    }
+
+
+}

+ 73 - 4
twzd-common/src/main/java/com/miaxis/common/utils/http/HttpUtils.java

@@ -6,10 +6,7 @@ import org.slf4j.LoggerFactory;
 
 import javax.net.ssl.*;
 import java.io.*;
-import java.net.ConnectException;
-import java.net.SocketTimeoutException;
-import java.net.URL;
-import java.net.URLConnection;
+import java.net.*;
 import java.security.cert.X509Certificate;
 
 /**
@@ -251,4 +248,76 @@ public class HttpUtils
             return true;
         }
     }
+
+    public static  String connectHttpsByPost(String path, File file) throws Exception{
+        URL urlObj = new URL(path);
+        //连接
+        HttpURLConnection con = (HttpURLConnection) urlObj.openConnection();
+        String result = null;
+        con.setDoInput(true);
+        con.setDoOutput(true);
+        con.setUseCaches(false); // post方式不能使用缓存
+
+        // 设置请求头信息
+        con.setRequestProperty("Connection", "Keep-Alive");
+        con.setRequestProperty("Charset", "UTF-8");
+        // 设置边界
+        String BOUNDARY = "----------" + System.currentTimeMillis();
+        con.setRequestProperty("Content-Type",
+                "multipart/form-data; boundary="
+                        + BOUNDARY);
+
+        // 请求正文信息
+        // 第一部分:
+        StringBuilder sb = new StringBuilder();
+        sb.append("--"); // 必须多两道线
+        sb.append(BOUNDARY);
+        sb.append("\r\n");
+        sb.append("Content-Disposition: form-data;name=\"media\";filelength=\"" + file.length() + "\";filename=\""
+                + file.getName() + "\"\r\n");
+        sb.append("Content-Type:application/octet-stream\r\n\r\n");
+        byte[] head = sb.toString().getBytes("utf-8");
+        // 获得输出流
+        OutputStream out = new DataOutputStream(con.getOutputStream());
+        // 输出表头
+        out.write(head);
+
+        // 文件正文部分
+        // 把文件已流文件的方式 推入到url中
+        DataInputStream in = new DataInputStream(new FileInputStream(file));
+        int bytes = 0;
+        byte[] bufferOut = new byte[1024];
+        while ((bytes = in.read(bufferOut)) != -1) {
+            out.write(bufferOut, 0, bytes);
+        }
+        in.close();
+        // 结尾部分
+        byte[] foot = ("\r\n--" + BOUNDARY + "--\r\n").getBytes("utf-8");// 定义最后数据分隔线
+        out.write(foot);
+        out.flush();
+        out.close();
+        StringBuffer buffer = new StringBuffer();
+        BufferedReader reader = null;
+        try {
+            // 定义BufferedReader输入流来读取URL的响应
+            reader = new BufferedReader(new InputStreamReader(con.getInputStream()));
+            String line = null;
+            while ((line = reader.readLine()) != null) {
+                buffer.append(line);
+            }
+            if (result == null) {
+                result = buffer.toString();
+            }
+        } catch (IOException e) {
+            System.out.println("发送POST请求出现异常!" + e);
+            e.printStackTrace();
+        } finally {
+            if (reader != null) {
+                reader.close();
+            }
+        }
+        return result;
+    }
+
+
 }

+ 98 - 0
twzd-common/src/main/java/com/miaxis/common/utils/wx/MessageUtil.java

@@ -0,0 +1,98 @@
+package com.miaxis.common.utils.wx;
+
+import com.miaxis.common.utils.XmlUtil;
+
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * 微信消息工具
+ * @author wwl
+ * @version 1.0
+ * @date 2021/10/25 13:19
+ */
+public class MessageUtil {
+
+    public static final String MESSAGE_TEXT="text";//文本消息
+    public static final String MESSAGE_IMAGE="image";//图片消息
+    public static final String MESSAGE_VOICE="voice";//语音消息
+    public static final String MESSAGE_VIDEO="video";//视频消息
+    public static final String MESSAGE_LOCATION="LOCATION";//地理位置消息
+    public static final String MESSAGE_EVENT="event";//事件消息
+    public static final String MESSAGE_SUBSCRIBE="subscribe";//关注
+    public static final String MESSAGE_UNSUBSCRIBE="unsubscribe";//取消关注
+    public static final String MESSAGE_SCAN="SCAN";//已关注扫码消息
+    public static final String MESSAGE_CLICK="CLICK";//点击菜单拉取消息
+    public static final String MESSAGE_VIEW="VIEW";//点击菜单跳转链接消息
+    public static final String MESSAGE_VIEW_MINIPROGRAM="view_miniprogram";//点击菜单跳转小程序的消息
+
+
+
+    /**
+     * 回复文本消息
+     * @param fromUserName
+     * @param toUserName
+     * @param content 回复的消息内容(换行:在content中能够换行,微信客户端就支持换行显示)
+     * @return
+     */
+    public static String initText(String fromUserName, String toUserName, String content) throws Exception{
+        // 返回消息时ToUserName的值与FromUserName的互换
+        Map<String, String> returnMap = new HashMap<>();
+        returnMap.put("ToUserName", fromUserName);
+        returnMap.put("FromUserName", toUserName);
+        returnMap.put("CreateTime", new Date().getTime()+"");
+        returnMap.put("MsgType", "text");
+        returnMap.put("Content", content);
+        return XmlUtil.mapToXml(returnMap);
+    }
+
+
+    /**
+     * 回复图片消息
+     * @param fromUserName
+     * @param toUserName
+     * @param mediaId 通过素材管理中的接口上传多媒体文件,得到的id。
+     * @return
+     */
+    public static String initImage(String fromUserName, String toUserName, String mediaId) throws Exception{
+        // 返回消息时ToUserName的值与FromUserName的互换
+        Map<String, String> returnMap = new HashMap<>();
+        returnMap.put("ToUserName", fromUserName);
+        returnMap.put("FromUserName", toUserName);
+        returnMap.put("CreateTime", new Date().getTime()+"");
+        returnMap.put("MsgType", "image");
+        returnMap.put("MediaId", mediaId);
+        return XmlUtil.mapToXmlSpecial(returnMap);
+    }
+
+
+    /**
+     * 回复图文消息
+     * @param fromUserName 开发者微信号
+     * @param toUserName 接收方帐号(收到的OpenID)
+     * @param articles 图文消息信息,注意,如果图文数超过限制,则将只发限制内的条数
+     * @param title 图文消息标题
+     * @param description 图文消息描述
+     * @param picUrl 图片链接,支持JPG、PNG格式,较好的效果为大图360*200,小图200*200
+     * @param url 点击图文消息跳转链接
+     * @return
+     */
+    public static String initNews(String fromUserName, String toUserName, String articles, String title, String description, String picUrl, String url)  throws Exception{
+        // 返回消息时ToUserName的值与FromUserName的互换
+        Map<String, String> returnMap = new HashMap<>();
+        returnMap.put("ToUserName", fromUserName);
+        returnMap.put("FromUserName", toUserName);
+        returnMap.put("CreateTime", new Date().getTime()+"");
+        returnMap.put("MsgType", "news");
+        returnMap.put("ArticleCount", "1");
+        returnMap.put("Content", articles);
+        returnMap.put("Title", title);
+        returnMap.put("Description", description);
+        returnMap.put("PicUrl", picUrl);
+        returnMap.put("Url", url);
+        return XmlUtil.mapToXml(returnMap);
+    }
+
+
+}

+ 11 - 0
twzd-service/pom.xml

@@ -49,6 +49,17 @@
             <version>0.2.1</version>
         </dependency>
 
+        <!--谷歌二维码生成-->
+        <dependency>
+            <groupId>com.google.zxing</groupId>
+            <artifactId>core</artifactId>
+        </dependency>
+
+        <!--hutool工具包-->
+        <dependency>
+            <groupId>cn.hutool</groupId>
+            <artifactId>hutool-all</artifactId>
+        </dependency>
 
     </dependencies>
 

+ 1 - 1
twzd-service/src/main/java/com/miaxis/feign/dto/WxTicket.java → twzd-service/src/main/java/com/miaxis/feign/dto/WxQrTicket.java

@@ -8,7 +8,7 @@ import lombok.Data;
  * @date 2021/10/20 15:08
  */
 @Data
-public class WxTicket {
+public class WxQrTicket {
 
     private Integer expire_seconds;  //该二维码有效时间,以秒为单位。 最大不超过2592000(即30天),此字段如果不填,则默认有效期为60秒。
     private String action_name;  //二维码类型,QR_SCENE为临时的整型参数值,QR_STR_SCENE为临时的字符串参数值,QR_LIMIT_SCENE为永久的整型参数值,QR_LIMIT_STR_SCENE为永久的字符串参数值

+ 0 - 2
twzd-service/src/main/java/com/miaxis/feign/service/IWxMpService.java

@@ -5,8 +5,6 @@ import org.springframework.cloud.openfeign.FeignClient;
 import org.springframework.web.bind.annotation.GetMapping;
 import org.springframework.web.bind.annotation.RequestParam;
 
-import javax.servlet.annotation.HandlesTypes;
-
 /**
  * @author wwl
  * @version 1.0

+ 60 - 6
twzd-service/src/main/java/com/miaxis/feign/service/IWxSendService.java

@@ -1,11 +1,15 @@
 package com.miaxis.feign.service;
 
+import com.alibaba.fastjson.JSONObject;
 import com.miaxis.common.config.FeignConfig;
-import com.miaxis.feign.dto.WxTicket;
+import com.miaxis.feign.dto.WxQrTicket;
+import feign.Headers;
+
+import feign.Param;
+import feign.form.FormData;
 import org.springframework.cloud.openfeign.FeignClient;
-import org.springframework.web.bind.annotation.GetMapping;
-import org.springframework.web.bind.annotation.PostMapping;
-import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.*;
+
 
 /**
  * 微信公众号接口
@@ -35,11 +39,61 @@ public interface IWxSendService {
      * 生成带参数二维码接口
      *      -文档链接:https://developers.weixin.qq.com/doc/offiaccount/Account_Management/Generating_a_Parametric_QR_Code.html
      * @param accessToken
-     * @param wxTicket:二维码参数
+     * @param wxQrTicket:二维码参数
      * @return
      */
     @PostMapping(value = "/qrcode/create")
-    String generateTicket(@RequestParam("access_token")String accessToken, WxTicket wxTicket);
+    String generateTicket(@RequestParam("access_token")String accessToken, WxQrTicket wxQrTicket);
+
+
+    /**
+     * 创建菜单接口
+     *      -文档链接:https://developers.weixin.qq.com/doc/offiaccount/Custom_Menus/Creating_Custom-Defined_Menu.html
+     * @param accessToken
+     * @param jsonObject json
+     * @return
+     */
+    @PostMapping(value = "/menu/create")
+    @ResponseBody
+    String createMenu(@RequestParam("access_token")String accessToken, JSONObject jsonObject);
+
+
+    /**
+     * 新增永久图文素材
+     *      -文档链接:https://developers.weixin.qq.com/doc/offiaccount/Asset_Management/Adding_Permanent_Assets.html
+     * @param accessToken
+     * @param jsonObject json
+     * @return
+     */
+    @PostMapping(value = "/material/add_news")
+    @ResponseBody
+    String materialAddNews(@RequestParam("access_token")String accessToken, JSONObject jsonObject);
+
+
+    /**
+     * 新增其他类型永久素材
+     *      -文档链接:https://developers.weixin.qq.com/doc/offiaccount/Asset_Management/Adding_Permanent_Assets.html
+     * @param accessToken
+     * @param type 媒体文件类型,分别有图片(image)、语音(voice)、视频(video)和缩略图(thumb)
+     * @param media form-data中媒体文件标识,有filename、filelength、content-type等信息
+     * @return
+     */
+    @PostMapping(value = "/material/add_material")
+    @Headers("Content-Type: multipart/form-data")
+    String materialAddByType(@RequestParam("access_token")String accessToken, @RequestParam("type")String type, @Param("photo") FormData media);
+
+
+    /**
+     * 获取用户信息
+     * @param token 调用接口凭证
+     * @param openid 普通用户的标识,对当前公众号唯一
+     * @param lang 返回国家地区语言版本,zh_CN 简体,zh_TW 繁体,en 英语
+     * @return
+     */
+    @GetMapping(value = "/user/info")
+    String userInfo(@RequestParam("access_token") String token,
+                    @RequestParam("openid") String openid,
+                    @RequestParam("lang") String lang);
 
 
 }

+ 61 - 0
twzd-service/src/main/java/com/miaxis/file/domain/FileInfo.java

@@ -0,0 +1,61 @@
+package com.miaxis.file.domain;
+
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.miaxis.common.annotation.Excel;
+import com.miaxis.common.core.domain.BaseBusinessEntity;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+/**
+ * 文件对象 file_info
+ *
+ * @author wwl
+ * @version 1.0
+ * @date 2021/10/28 14:30
+ */
+@Data
+@TableName("file_info")
+@ApiModel(value = "FileInfo", description = "文件对象 file_info")
+public class FileInfo extends BaseBusinessEntity {
+    private static final long serialVersionUID = 1L;
+
+    /** 自增id */
+    @TableId(value = "file_id")
+    @ApiModelProperty(value = "自增id")
+    private Long fileId;
+
+    /** 顺序 */
+    @Excel(name = "顺序")
+    @TableField("seq")
+    @ApiModelProperty(value = "顺序")
+    private Long seq;
+
+    /** 文件业务类型 */
+    @Excel(name = "文件业务类型")
+    @TableField("file_type")
+    @ApiModelProperty(value = "文件业务类型")
+    private String fileType;
+
+    /** 备注 */
+    @Excel(name = "备注")
+    @TableField("remark")
+    @ApiModelProperty(value = "备注")
+    private String remark;
+
+    /** 文件url(访问地址) */
+    @Excel(name = "文件url(访问地址)")
+    @TableField("file_url")
+    @ApiModelProperty(value = "文件url(访问地址)")
+    private String fileUrl;
+
+    /** 文件路径 */
+    @Excel(name = "文件路径")
+    @TableField("file_path")
+    @ApiModelProperty(value = "文件路径")
+    private String filePath;
+
+
+}

+ 16 - 0
twzd-service/src/main/java/com/miaxis/file/mapper/FileInfoMapper.java

@@ -0,0 +1,16 @@
+package com.miaxis.file.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.miaxis.file.domain.FileInfo;
+
+
+/**
+ * 文件Mapper接口
+ *
+ * @author wwl
+ * @version 1.0
+ * @date 2021/10/28 14:36
+ */
+public interface FileInfoMapper extends BaseMapper<FileInfo> {
+
+}

+ 15 - 0
twzd-service/src/main/java/com/miaxis/file/service/IFileInfoService.java

@@ -0,0 +1,15 @@
+package com.miaxis.file.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.miaxis.file.domain.FileInfo;
+
+/**
+ * 文件Service接口
+ *
+ * @author wwl
+ * @version 1.0
+ * @date 2021/10/28 14:33
+ */
+public interface IFileInfoService extends IService<FileInfo>{
+
+}

+ 23 - 0
twzd-service/src/main/java/com/miaxis/file/service/impl/FileInfoServiceImpl.java

@@ -0,0 +1,23 @@
+package com.miaxis.file.service.impl;
+
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.miaxis.file.domain.FileInfo;
+import com.miaxis.file.mapper.FileInfoMapper;
+import com.miaxis.file.service.IFileInfoService;
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Service;
+
+/**
+ * 文件Service业务层处理
+ *
+ * @author wwl
+ * @version 1.0
+ * @date 2021/10/28 14:35
+ */
+@Service
+@RequiredArgsConstructor
+public class FileInfoServiceImpl extends ServiceImpl<FileInfoMapper, FileInfo> implements IFileInfoService {
+
+    private final  FileInfoMapper fileInfoMapper;
+
+}

+ 39 - 0
twzd-service/src/main/java/com/miaxis/spread/domain/WxSpreadRelation.java

@@ -0,0 +1,39 @@
+package com.miaxis.spread.domain;
+
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.miaxis.common.core.domain.BaseBusinessEntity;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+/**
+ * wx推广关系对象 wx_spread_relation
+ * @author wwl
+ * @version 1.0
+ * @date 2021/10/29 9:39
+ */
+@Data
+@TableName("wx_spread_relation")
+@ApiModel(value = "WxSpreadRelation", description = "wx推广关系对象 wx_spread_relation")
+public class WxSpreadRelation extends BaseBusinessEntity {
+    private static final long serialVersionUID = 1L;
+
+    /** 主键 */
+    @TableId(value = "id")
+    @ApiModelProperty(value = "主键")
+    private Long id;
+
+    @TableField("openid")
+    @ApiModelProperty(value = "用户唯一标识")
+    private String openid;
+
+    @TableField("parent_openid")
+    @ApiModelProperty(value = "用户唯一标识(父级)")
+    private String parentOpenid;
+
+    @TableField("status")
+    @ApiModelProperty(value = "状态  0:有效(默认)、1:失效 (伪删除)")
+    private String status;
+}

+ 13 - 0
twzd-service/src/main/java/com/miaxis/spread/mapper/WxSpreadRelationMapper.java

@@ -0,0 +1,13 @@
+package com.miaxis.spread.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.miaxis.spread.domain.WxSpreadRelation;
+
+/**
+ * wx推广关系Mapper接口
+ * @author wwl
+ * @version 1.0
+ * @date 2021/10/29 9:41
+ */
+public interface WxSpreadRelationMapper extends BaseMapper<WxSpreadRelation> {
+}

+ 13 - 0
twzd-service/src/main/java/com/miaxis/spread/service/IWxSpreadRelationService.java

@@ -0,0 +1,13 @@
+package com.miaxis.spread.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.miaxis.spread.domain.WxSpreadRelation;
+
+/**
+ * wx推广关系Service接口
+ * @author wwl
+ * @version 1.0
+ * @date 2021/10/29 9:42
+ */
+public interface IWxSpreadRelationService extends IService<WxSpreadRelation> {
+}

+ 22 - 0
twzd-service/src/main/java/com/miaxis/spread/service/impl/WxSpreadRelationServiceImpl.java

@@ -0,0 +1,22 @@
+package com.miaxis.spread.service.impl;
+
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.miaxis.spread.domain.WxSpreadRelation;
+import com.miaxis.spread.mapper.WxSpreadRelationMapper;
+import com.miaxis.spread.service.IWxSpreadRelationService;
+import lombok.AllArgsConstructor;
+import org.springframework.stereotype.Service;
+
+/**
+ * wx推广关系Service业务层处理
+ * @author wwl
+ * @version 1.0
+ * @date 2021/10/29 9:42
+ */
+@Service
+@AllArgsConstructor
+public class WxSpreadRelationServiceImpl extends ServiceImpl<WxSpreadRelationMapper, WxSpreadRelation> implements IWxSpreadRelationService {
+
+    private final WxSpreadRelationMapper wxSpreadRelationMapper;
+
+}

+ 69 - 0
twzd-service/src/main/java/com/miaxis/wx/domain/WxMenu.java

@@ -0,0 +1,69 @@
+package com.miaxis.wx.domain;
+
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.miaxis.common.core.domain.BaseBusinessEntity;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+/**
+ * 微信公众号菜单对象 wx_menu
+ * @author wwl
+ * @version 1.0
+ * @date 2021/10/26 10:06
+ */
+@Data
+@TableName("wx_menu")
+@ApiModel(value = "WxMenu", description = "微信公众号菜单对象 wx_menu")
+public class WxMenu extends BaseBusinessEntity {
+
+    private static final long serialVersionUID = 1L;
+
+    /** 主键 */
+    @TableId(value = "id")
+    @ApiModelProperty(value = "主键")
+    private Long id;
+
+    @TableField("parent_id")
+    @ApiModelProperty(value = "父节点")
+    private Integer parentId;
+
+    @TableField("type")
+    @ApiModelProperty(value = "菜单的响应动作类型,view表示网页类型,click表示点击类型,miniprogram表示小程序类型")
+    private String type;
+
+    @TableField("menu_name")
+    @ApiModelProperty(value = "菜单标题")
+    private String menuName;
+
+    @TableField("click_key")
+    @ApiModelProperty(value = "click等点击类型必须,菜单KEY值,用于消息接口推送,不超过128字节")
+    private String clickKey;
+
+    @TableField("url")
+    @ApiModelProperty(value = "view、miniprogram类型必须,网页 链接,用户点击菜单可打开链接,不超过1024字节。 type为miniprogram时,不支持小程序的老版本客户端将打开本url。")
+    private String url;
+
+    @TableField("media_id")
+    @ApiModelProperty(value = "media_id类型和view_limited类型必须,调用新增永久素材接口返回的合法media_id")
+    private String mediaId;
+
+    @TableField("appid")
+    @ApiModelProperty(value = "miniprogram类型必须,小程序的appid(仅认证公众号可配置)")
+    private String appid;
+
+    @TableField("page_path")
+    @ApiModelProperty(value = "miniprogram类型必须,小程序的页面路径")
+    private String pagePath;
+
+    @TableField("article_id")
+    @ApiModelProperty(value = "发布后获得的合法 article_id")
+    private String articleId;
+
+    @TableField("sort")
+    @ApiModelProperty(value = "排序")
+    private Integer sort;
+
+}

+ 47 - 0
twzd-service/src/main/java/com/miaxis/wx/domain/WxTicket.java

@@ -0,0 +1,47 @@
+package com.miaxis.wx.domain;
+
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.miaxis.common.core.domain.BaseBusinessEntity;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+/**
+ * 微信推广二维码对象 wx_ticket
+ * @author wwl
+ * @version 1.0
+ * @date 2021/10/28 16:22
+ */
+@Data
+@TableName("wx_ticket")
+@ApiModel(value = "WxQrTicket", description = "微信推广二维码对象 wx_ticket")
+public class WxTicket extends BaseBusinessEntity {
+    private static final long serialVersionUID = 1L;
+
+    /** 主键 */
+    @TableId(value = "id")
+    @ApiModelProperty(value = "主键")
+    private Long id;
+
+    @TableField("openid")
+    @ApiModelProperty(value = "用户唯一标识(微信)")
+    private String openid;
+
+    @TableField("ticket")
+    @ApiModelProperty(value = "获取的二维码ticket")
+    private String ticket;
+
+    @TableField("ticket_file_id")
+    @ApiModelProperty(value = "永久二维码图片id(关联file_info文件表)")
+    private Long ticketFileId;
+
+    @TableField("media_id")
+    @ApiModelProperty(value = "微信永久素材media_id(可根据接口获取图片、视频、语音等)")
+    private String mediaId;
+
+    @TableField("status")
+    @ApiModelProperty(value = "状态  0:有效(默认)、1:失效 (伪删除)")
+    private String status;
+}

+ 13 - 0
twzd-service/src/main/java/com/miaxis/wx/mapper/WxMenuMapper.java

@@ -0,0 +1,13 @@
+package com.miaxis.wx.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.miaxis.wx.domain.WxMenu;
+
+/**
+ * 微信公众号菜单Mapper接口
+ * @author wwl
+ * @version 1.0
+ * @date 2021/10/26 10:12
+ */
+public interface WxMenuMapper extends BaseMapper<WxMenu> {
+}

+ 13 - 0
twzd-service/src/main/java/com/miaxis/wx/mapper/WxTicketMapper.java

@@ -0,0 +1,13 @@
+package com.miaxis.wx.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.miaxis.wx.domain.WxTicket;
+
+/**
+ * 微信推广二维码Mapper接口
+ * @author wwl
+ * @version 1.0
+ * @date 2021/10/28 16:31
+ */
+public interface WxTicketMapper extends BaseMapper<WxTicket> {
+}

+ 21 - 0
twzd-service/src/main/java/com/miaxis/wx/service/IWxGzhService.java

@@ -1,6 +1,11 @@
 package com.miaxis.wx.service;
 
+import com.alibaba.fastjson.JSONObject;
+
+import javax.servlet.http.HttpServletRequest;
+
 /**
+ * 微信公众号服务
  * @author wwl
  * @version 1.0
  * @date 2021/10/20 14:56
@@ -12,4 +17,20 @@ public interface IWxGzhService {
      * @return
      */
     String getGzhToken();
+
+    /**
+     * 查询微信菜单数据
+     * @return
+     */
+    JSONObject getWxMenuJson();
+
+
+    /**
+     * 微信推送消息处理
+     */
+    String handlePublicMsg(HttpServletRequest request);
+
+
+
+
 }

+ 31 - 0
twzd-service/src/main/java/com/miaxis/wx/service/IWxMessageEvenService.java

@@ -0,0 +1,31 @@
+package com.miaxis.wx.service;
+
+/**
+ * 微信消息事件处理
+ * @author wwl
+ * @version 1.0
+ * @date 2021/10/27 14:33
+ */
+public interface IWxMessageEvenService {
+
+    //关注事件
+//    String subscribeEvent();
+
+    /**
+     * 扫码关注事件
+     * @param fromUserName 发送方帐号(一个OpenID)
+     * @param ticket 二维码的ticket
+     * @return
+     */
+    String scanSubscribeEvent(String fromUserName,String ticket);
+
+    /**
+     * click获取分销二维码事件
+     * @param fromUserName 发送方帐号(一个OpenID)
+     * @param token 凭证
+     * @return
+     */
+    String gainTicketEvent(String fromUserName,String token);
+
+
+}

+ 13 - 0
twzd-service/src/main/java/com/miaxis/wx/service/IWxTicketService.java

@@ -0,0 +1,13 @@
+package com.miaxis.wx.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.miaxis.wx.domain.WxTicket;
+
+/**
+ * 微信推广二维码Service接口
+ * @author wwl
+ * @version 1.0
+ * @date 2021/10/28 16:24
+ */
+public interface IWxTicketService extends IService<WxTicket> {
+}

+ 163 - 3
twzd-service/src/main/java/com/miaxis/wx/service/impl/WxGzhServiceImpl.java

@@ -1,18 +1,29 @@
 package com.miaxis.wx.service.impl;
 
+import cn.hutool.json.XML;
+import com.alibaba.fastjson.JSONArray;
 import com.alibaba.fastjson.JSONObject;
 import com.miaxis.common.constant.Constants;
-import com.miaxis.common.core.redis.RedisCache;
+import com.miaxis.common.sign.VerifyUtil;
 import com.miaxis.common.utils.StringUtils;
+import com.miaxis.common.utils.wx.MessageUtil;
+import com.miaxis.feign.service.IWxMpService;
 import com.miaxis.feign.service.IWxSendService;
+import com.miaxis.wx.domain.WxMenu;
+import com.miaxis.wx.mapper.WxMenuMapper;
 import com.miaxis.wx.service.IWxGzhService;
+import com.miaxis.wx.service.IWxMessageEvenService;
+import com.qcloud.cos.COSClient;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.io.IOUtils;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.data.redis.core.RedisTemplate;
 import org.springframework.stereotype.Service;
 
-import javax.annotation.Resource;
+import javax.servlet.http.HttpServletRequest;
+import java.util.HashMap;
+import java.util.List;
 import java.util.concurrent.TimeUnit;
 
 /**
@@ -28,7 +39,13 @@ public class WxGzhServiceImpl implements IWxGzhService {
 
     private final RedisTemplate redisTemplate;
 
-    private final  IWxSendService wxSendService;
+    private final IWxSendService wxSendService;
+
+    private final WxMenuMapper wxMenuMapper;
+
+    private final COSClient cosClient;
+
+    private final IWxMessageEvenService wxMessageEvenService;
 
     @Value("${app.appid}")
     private String appid;
@@ -37,6 +54,10 @@ public class WxGzhServiceImpl implements IWxGzhService {
     private String secret;
 
 
+    /**
+     * 获取微信公众号token
+     * @return
+     */
     @Override
     public String getGzhToken() {
         String token = "";
@@ -58,4 +79,143 @@ public class WxGzhServiceImpl implements IWxGzhService {
 
         return token;
     }
+
+
+    /**
+     * 查询微信菜单数据
+     * @return
+     */
+    @Override
+    public JSONObject getWxMenuJson() {
+
+        JSONObject menuJson = new JSONObject();
+        //获取父类
+        List<WxMenu> menuList = wxMenuMapper.selectByMap(new HashMap<String,Object>(){{
+            put("parent_id",0);
+        }});
+        JSONArray button=new JSONArray();
+        for(WxMenu menu : menuList){
+            if(!VerifyUtil.verifyString(menu.getType())){
+                JSONObject childButton = new JSONObject();
+                childButton.put("name", menu.getMenuName());
+                //父类
+                JSONArray sub_button = new JSONArray();
+                List<WxMenu> childMenuList = wxMenuMapper.selectByMap(new HashMap<String,Object>(){{
+                    put("parent_id",menu.getId());
+                }});
+
+                for (WxMenu childMenu : childMenuList) {
+                    sub_button.add(getChildMenuJson(childMenu));
+                }
+                childButton.put("sub_button", sub_button);
+                button.add(childButton);
+            }else{
+                button.add(getChildMenuJson(menu));
+            }
+        }
+
+        menuJson.put("button", button);
+        return menuJson;
+    }
+
+    private JSONObject getChildMenuJson(WxMenu menu){
+        JSONObject menuJson = new JSONObject();
+        menuJson.put("type", menu.getType());
+        menuJson.put("name", menu.getMenuName());
+        if(menu.getType().equals("view")){
+            menuJson.put("url", menu.getUrl());
+        }else if(menu.getType().equals("click")){
+            menuJson.put("key", menu.getClickKey());
+        }else if(menu.getType().equals("miniprogram")){
+            menuJson.put("url", menu.getUrl());
+            menuJson.put("key", menu.getClickKey());
+            menuJson.put("appid", menu.getAppid());
+            menuJson.put("pagepath", menu.getPagePath());
+        }
+        return menuJson;
+    }
+
+
+    /**
+     * 微信推送消息处理
+     * @param request
+     * @return
+     */
+    @Override
+    public String handlePublicMsg(HttpServletRequest request) {
+        try {
+            log.info("1....");
+            cn.hutool.json.JSONObject decryptMap = XML.toJSONObject(IOUtils.toString(request.getInputStream()));
+            cn.hutool.json.JSONObject jsonObjectData = decryptMap.getJSONObject("xml");
+            log.info("2-----jsonObjectData------"+jsonObjectData);
+
+            //开发者微信号
+            String toUserName = jsonObjectData.getStr("ToUserName");
+            //发送方帐号(一个OpenID)
+            String fromUserName = jsonObjectData.getStr("FromUserName");
+            // 区分消息类型
+            String msgType = jsonObjectData.getStr("MsgType");
+
+            // 普通消息
+            if (MessageUtil.MESSAGE_TEXT.equals(msgType)) { // 文本消息
+                log.info("2.1...");
+                String content = jsonObjectData.getStr("Content");
+                if (content.startsWith("你好")){
+                    return MessageUtil.initText(fromUserName, toUserName, "你好啊");
+                }else if (content.startsWith("傻逼")){
+                    return MessageUtil.initText(fromUserName, toUserName, "你才是煞笔");
+                }else {
+                    return MessageUtil.initText(fromUserName, toUserName, "文本消息-默认回复信息");
+                }
+
+            } else if (MessageUtil.MESSAGE_IMAGE.equals(msgType)) { // 图片消息
+                log.info("2.2...");
+                return MessageUtil.initText(fromUserName, toUserName, "抱歉,暂时无法识别图片信息!");
+            }else if (MessageUtil.MESSAGE_EVENT.equals(msgType)) { // 事件消息
+                log.info("3....");
+                // 区分事件推送
+                String event = jsonObjectData.getStr("Event");
+                if (MessageUtil.MESSAGE_SUBSCRIBE.equals(event)) { // 关注事件 或 扫描二维码关注事件
+                    log.info("3.1...");
+                    //存在Ticket为扫码关注
+                    if (org.apache.commons.lang3.StringUtils.isNotEmpty(jsonObjectData.getStr("Ticket"))){
+                        log.info("3.1.2..");
+                        //根据Ticket  推送绑定信息到上级用户
+                        //从缓存中获取Ticket用户信息
+                        String subscribeEvent = wxMessageEvenService.scanSubscribeEvent(fromUserName, jsonObjectData.getStr("Ticket"));
+                        return MessageUtil.initText(fromUserName,toUserName,subscribeEvent);
+
+                    }else {
+                        return MessageUtil.initText(fromUserName, toUserName, "关注事件");
+                    }
+
+                }  else if (MessageUtil.MESSAGE_UNSUBSCRIBE.equals(event)) { // 取消订阅事件
+                    // todo 处理取消订阅事件
+
+                } else if (MessageUtil.MESSAGE_SCAN.equals(event)) { // 已关注扫描二维码事件
+                    log.info("3.2...");
+                    return MessageUtil.initText(fromUserName, toUserName, "已关注扫描二维码事件");
+                    
+                } else if (MessageUtil.MESSAGE_LOCATION.equals(event)) { // 上报地理位置事件
+                    // todo 处理上报地理位置事件
+
+                } else if (MessageUtil.MESSAGE_CLICK.equals(event)) { // 点击菜单拉取消息时的事件推送事件
+                    log.info("3.3.....click:" + jsonObjectData.get("EventKey"));
+                    //判断事件KEY值,与自定义菜单接口中KEY值对应
+                    if ("generateTicket".equals(jsonObjectData.get("EventKey"))){ //获取分销二维码
+                        String mediaId = wxMessageEvenService.gainTicketEvent(fromUserName, this.getGzhToken());
+                        return MessageUtil.initImage(fromUserName,toUserName,mediaId);
+                    }
+
+                } else if (MessageUtil.MESSAGE_VIEW.equals(event)) { // 点击菜单跳转链接时的事件推送
+                    // todo 处理点击菜单跳转链接时的事件推送
+                }
+            }
+        } catch (Exception e) {
+            log.error("处理微信公众号请求信息,失败", e);
+        }
+        return null;
+    }
+
+
 }

+ 200 - 0
twzd-service/src/main/java/com/miaxis/wx/service/impl/WxMessageEvenServiceImpl.java

@@ -0,0 +1,200 @@
+package com.miaxis.wx.service.impl;
+
+import cn.hutool.core.date.DateField;
+import cn.hutool.core.date.DateTime;
+import cn.hutool.core.date.DateUtil;
+import cn.hutool.core.img.ImgUtil;
+import cn.hutool.core.io.FileUtil;
+import cn.hutool.extra.qrcode.QrCodeUtil;
+import cn.hutool.extra.qrcode.QrConfig;
+import com.alibaba.fastjson.JSONObject;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.miaxis.common.constant.Constants;
+import com.miaxis.common.utils.http.HttpUtils;
+import com.miaxis.feign.dto.WxQrTicket;
+import com.miaxis.feign.service.IWxSendService;
+import com.miaxis.spread.domain.WxSpreadRelation;
+import com.miaxis.spread.service.IWxSpreadRelationService;
+import com.miaxis.wx.service.IWxMessageEvenService;
+import com.miaxis.wx.service.IWxTicketService;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.stereotype.Service;
+
+import java.awt.*;
+import java.io.File;
+import java.util.Date;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * @author wwl
+ * @version 1.0
+ * @date 2021/10/27 14:35
+ */
+@Service
+@Slf4j
+@RequiredArgsConstructor
+public class WxMessageEvenServiceImpl implements IWxMessageEvenService {
+
+    private final RedisTemplate redisTemplate;
+
+    private final IWxSendService wxSendService;
+
+    private final IWxTicketService wxTicketService;
+
+    private final IWxSpreadRelationService wxSpreadRelationService;
+
+    @Value("${file.ticketPath}")
+    private String ticketPath;
+
+    @Value("${wxgzh.materialUrl}")
+    private String materialUrl;
+
+    @Value("${cos.bucketName}")
+    private String bucketName;
+
+    @Value("${cos.path}")
+    private String path;
+
+    @Value("${cos.preffix}")
+    private String preffix;
+
+
+    /**
+     * 扫码关注事件
+     * @param fromUserName 发送方帐号(一个OpenID)
+     * @param ticket 二维码的ticket
+     * @return
+     */
+    @Override
+    public String scanSubscribeEvent(String fromUserName, String ticket) {
+
+        String message = "";
+        //判断是否存在父级推广关系
+        List<WxSpreadRelation> spreadRelations = wxSpreadRelationService.list(new QueryWrapper<WxSpreadRelation>().eq("openid", fromUserName));
+        if (spreadRelations.isEmpty()){
+            String openid = (String) redisTemplate.opsForValue().get(Constants.GZH_TICKET_KEY + ticket);
+            //保存推广关系表
+            WxSpreadRelation wxSpreadRelation = new WxSpreadRelation();
+            wxSpreadRelation.setOpenid(fromUserName);
+            wxSpreadRelation.setParentOpenid(openid);
+            wxSpreadRelationService.save(wxSpreadRelation);
+            message = "已成功绑定推广关系!";
+        }else {
+            message = "已存在推广关系!";
+        }
+        return message;
+    }
+
+
+    /**
+     * click获取推广二维码事件
+     * @param fromUserName 发送方帐号(一个OpenID)
+     * @param token 凭证
+     * @return
+     */
+    @Override
+    public String gainTicketEvent(String fromUserName,String token) {
+
+        // 根据fromUserName查询缓存 判断用户推广码是否过期
+        if (redisTemplate.hasKey(Constants.GZH_MATERIAL_KEY + fromUserName)){
+            String mediaId = (String) redisTemplate.opsForValue().get(Constants.GZH_MATERIAL_KEY + fromUserName);
+            return mediaId;
+        }
+
+        //生成带参数二维码ticket(永久)
+        WxQrTicket wxQrTicket = new WxQrTicket();
+        wxQrTicket.setExpire_seconds(259200);//3天
+        wxQrTicket.setAction_name("QR_SCENE");//临时
+        JSONObject jsonObject1 = new JSONObject();
+        JSONObject jsonObject2 = new JSONObject();
+        jsonObject1.put("scene_id","123");
+        jsonObject2.put("scene",jsonObject1);
+        wxQrTicket.setAction_info(jsonObject2);
+        String wxTicketResult = wxSendService.generateTicket(token, wxQrTicket);
+        JSONObject jsonStr = JSONObject.parseObject(wxTicketResult);
+        log.info("---------ticket-------" + jsonStr);
+
+        //获取用户信息
+        String userInfoResult = wxSendService.userInfo(token, fromUserName, "zh_CN");
+        JSONObject userInfo = JSONObject.parseObject(userInfoResult);
+        log.info("---------userInfo-------" + userInfo);
+
+        //根据ticket生成二维码图片存储
+        File ticketFile = QrCodeUtil.generate(
+                jsonStr.getString("url"), //二维码内容
+                QrConfig.create().setImg(ticketPath + "logo.jpg"), //附带logo
+                FileUtil.file(ticketPath + fromUserName + ".jpg")//写出到的文件
+        );
+
+        ImgUtil.pressImage(
+                FileUtil.file(ticketPath+"jinpai.jpg"),
+                FileUtil.file(ticketPath+fromUserName+"_img.jpg"),
+                ImgUtil.read(FileUtil.file(ticketPath+fromUserName+".jpg")), //水印图片
+                0, //x坐标修正值。 默认在中间,偏移量相对于中间偏移
+                -360, //y坐标修正值。 默认在中间,偏移量相对于中间偏移
+                1.0f
+        );
+
+        DateTime date = DateUtil.date();
+        Date newDate = DateUtil.offsetDay(date, 3);
+        String dateStr = DateUtil.format(newDate, "yyyy-MM-dd");
+        log.info(dateStr);
+
+        String text = userInfo.getString("nickname")+"  此二维码有效期至:"+dateStr;
+        ImgUtil.pressText(
+                FileUtil.file(ticketPath+fromUserName+"_img.jpg"),
+                FileUtil.file(ticketPath+fromUserName+"_text.jpg"),
+                text, Color.red, //文字
+                new Font("黑体", Font.BOLD, 30), //字体
+                0, //x坐标修正值。 默认在中间,偏移量相对于中间偏移
+                400, //y坐标修正值。 默认在中间,偏移量相对于中间偏移
+                0.8f//透明度:alpha 必须是范围 [0.0, 1.0] 之内(包含边界值)的一个浮点数字
+        );
+
+
+        try {
+            File textFile = new File(ticketPath + fromUserName+"_text.jpg");
+            //上传素材
+            String path = materialUrl + token + "&type=image";
+            String result = HttpUtils.connectHttpsByPost(path,textFile);
+            result = result.replaceAll("[\\\\]", "");
+            JSONObject resultJSON = JSONObject.parseObject(result);
+            log.info("------result:" + resultJSON);
+
+            //缓存用户推广码相关信息
+            redisTemplate.opsForValue().set(Constants.GZH_TICKET_KEY+jsonStr.getString("ticket"),fromUserName,259200, TimeUnit.SECONDS);
+            redisTemplate.opsForValue().set(Constants.GZH_MATERIAL_KEY+fromUserName,resultJSON.getString("media_id"),259200, TimeUnit.SECONDS);
+
+            //删除服务器文件
+            File imgFile = new File(ticketPath + fromUserName+"_img.jpg");
+            if (ticketFile.exists() && imgFile.exists() && textFile.exists()) {
+                if (ticketFile.delete()) {
+                    log.info("删除:" + ticketFile.getName() + "成功!");
+                } else {
+                    log.info("删除:" + ticketFile.getName() + "失败!");
+                }
+                if (imgFile.delete()) {
+                    log.info("删除:" + imgFile.getName() + "成功!");
+                } else {
+                    log.info("删除:" + imgFile.getName() + "失败!");
+                }
+                if (textFile.delete()) {
+                    log.info("删除:" + textFile.getName() + "成功!");
+                } else {
+                    log.info("删除:" + textFile.getName() + "失败!");
+                }
+            }
+
+            return resultJSON.getString("media_id");
+        } catch (Exception e) {
+            log.info("-----异常------click获取推广二维码事件");
+        }
+        return null;
+    }
+
+
+}

+ 22 - 0
twzd-service/src/main/java/com/miaxis/wx/service/impl/WxTicketServiceImpl.java

@@ -0,0 +1,22 @@
+package com.miaxis.wx.service.impl;
+
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.miaxis.wx.domain.WxTicket;
+import com.miaxis.wx.mapper.WxTicketMapper;
+import com.miaxis.wx.service.IWxTicketService;
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Service;
+
+/**
+ * 微信推广二维码Service业务层处理
+ * @author wwl
+ * @version 1.0
+ * @date 2021/10/28 16:30
+ */
+@Service
+@RequiredArgsConstructor
+public class WxTicketServiceImpl extends ServiceImpl<WxTicketMapper, WxTicket> implements IWxTicketService {
+
+    private final WxTicketMapper wxTicketMapper;
+
+}