From cd490aa0e5c208c90d00fabba3d4951fc8837aee Mon Sep 17 00:00:00 2001
From: ageerle <ageerle@163.com>
Date: 星期三, 30 四月 2025 11:29:23 +0800
Subject: [PATCH] feat: 新增wechat模块

---
 ruoyi-modules/ruoyi-wechat/src/main/java/org/ruoyi/handler/SubscribeHandler.java                |   63 ++++
 ruoyi-modules/ruoyi-wechat/src/main/java/org/ruoyi/handler/LocationHandler.java                 |   44 +++
 ruoyi-modules-api/ruoyi-chat-api/src/main/java/org/ruoyi/service/impl/ChatVxServiceImpl.java    |   37 ++
 ruoyi-modules/ruoyi-wechat/pom.xml                                                              |   28 ++
 ruoyi-modules-api/ruoyi-chat-api/src/main/java/org/ruoyi/service/IChatVxService.java            |   19 +
 ruoyi-modules/ruoyi-wechat/src/main/java/org/ruoyi/builder/TextBuilder.java                     |   22 +
 ruoyi-modules/ruoyi-wechat/src/main/java/org/ruoyi/handler/EnterAgentHandler.java               |   29 ++
 ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/chat/service/chat/impl/SseServiceImpl.java     |   18 -
 ruoyi-modules/ruoyi-wechat/src/main/java/org/ruoyi/handler/AbstractHandler.java                 |    9 
 ruoyi-modules/ruoyi-wechat/src/main/java/org/ruoyi/handler/UnsubscribeHandler.java              |   28 ++
 ruoyi-modules/pom.xml                                                                           |    1 
 ruoyi-modules/ruoyi-wechat/src/main/java/org/ruoyi/handler/MsgHandler.java                      |   43 +++
 ruoyi-modules/ruoyi-wechat/src/main/java/org/ruoyi/controller/wxcplogin/WxPortalController.java |   86 ++++++
 ruoyi-modules/ruoyi-wechat/src/main/java/org/ruoyi/handler/ScanHandler.java                     |    8 
 ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/chat/service/chat/ISseService.java             |    7 
 ruoyi-modules/ruoyi-wechat/src/main/java/org/ruoyi/handler/MenuHandler.java                     |   34 ++
 ruoyi-modules/ruoyi-wechat/src/main/java/org/ruoyi/config/WxCpConfiguration.java                |  130 ++++++++++
 ruoyi-modules/ruoyi-wechat/src/main/java/org/ruoyi/builder/AbstractBuilder.java                 |   12 
 ruoyi-modules/ruoyi-wechat/src/main/java/org/ruoyi/handler/LogHandler.java                      |   26 ++
 ruoyi-modules/ruoyi-wechat/src/main/java/org/ruoyi/config/WxCpProperties.java                   |   45 +++
 ruoyi-modules/ruoyi-wechat/src/main/java/org/ruoyi/builder/ImageBuilder.java                    |   24 +
 ruoyi-modules/ruoyi-wechat/src/main/java/org/ruoyi/handler/NullHandler.java                     |   23 +
 ruoyi-modules/ruoyi-wechat/src/main/java/org/ruoyi/handler/ContactChangeHandler.java            |   34 ++
 23 files changed, 745 insertions(+), 25 deletions(-)

diff --git a/ruoyi-modules-api/ruoyi-chat-api/src/main/java/org/ruoyi/service/IChatVxService.java b/ruoyi-modules-api/ruoyi-chat-api/src/main/java/org/ruoyi/service/IChatVxService.java
new file mode 100644
index 0000000..60ed4ff
--- /dev/null
+++ b/ruoyi-modules-api/ruoyi-chat-api/src/main/java/org/ruoyi/service/IChatVxService.java
@@ -0,0 +1,19 @@
+package org.ruoyi.service;
+
+/**
+ * 浼佷笟寰俊鑱婂ぉ绠$悊Service鎺ュ彛
+ *
+ * @author ageerle
+ * @date 2025-04-08
+ */
+public interface IChatVxService {
+
+
+    /**
+     * 浼佷笟寰俊搴旂敤鍥炲
+     * @param prompt 鎻愮ず璇�
+     * @return 鍥炲鍐呭
+     */
+    String chat(String prompt);
+
+}
diff --git a/ruoyi-modules-api/ruoyi-chat-api/src/main/java/org/ruoyi/service/impl/ChatVxServiceImpl.java b/ruoyi-modules-api/ruoyi-chat-api/src/main/java/org/ruoyi/service/impl/ChatVxServiceImpl.java
new file mode 100644
index 0000000..7b013b5
--- /dev/null
+++ b/ruoyi-modules-api/ruoyi-chat-api/src/main/java/org/ruoyi/service/impl/ChatVxServiceImpl.java
@@ -0,0 +1,37 @@
+package org.ruoyi.service.impl;
+
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.ruoyi.common.chat.entity.chat.ChatCompletion;
+import org.ruoyi.common.chat.entity.chat.ChatCompletionResponse;
+import org.ruoyi.common.chat.entity.chat.Message;
+import org.ruoyi.common.chat.openai.OpenAiStreamClient;
+import org.ruoyi.service.IChatVxService;
+import org.springframework.stereotype.Service;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@Service
+@Slf4j
+@RequiredArgsConstructor
+public class ChatVxServiceImpl implements IChatVxService {
+
+    private final OpenAiStreamClient openAiStreamClient;
+
+    @Override
+    public String chat(String prompt) {
+        List<Message> messageList = new ArrayList<>();
+        Message message = Message.builder().role(Message.Role.USER).content(prompt).build();
+        messageList.add(message);
+        ChatCompletion chatCompletion = ChatCompletion
+            .builder()
+            .messages(messageList)
+            .model("gpt-4o-mini")
+            .stream(false)
+            .build();
+        ChatCompletionResponse chatCompletionResponse = openAiStreamClient.chatCompletion(chatCompletion);
+        return chatCompletionResponse.getChoices().get(0).getMessage().getContent().toString();
+    }
+
+}
diff --git a/ruoyi-modules/pom.xml b/ruoyi-modules/pom.xml
index 1b3ff0c..044e0bc 100644
--- a/ruoyi-modules/pom.xml
+++ b/ruoyi-modules/pom.xml
@@ -21,6 +21,7 @@
         <module>ruoyi-chat</module>
         <module>ruoyi-system</module>
         <module>ruoyi-generator</module>
+        <module>ruoyi-wechat</module>
     </modules>
 
     <properties>
diff --git a/ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/chat/service/chat/ISseService.java b/ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/chat/service/chat/ISseService.java
index c025642..1fb66ba 100644
--- a/ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/chat/service/chat/ISseService.java
+++ b/ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/chat/service/chat/ISseService.java
@@ -48,11 +48,4 @@
     UploadFileResponse upload(MultipartFile file);
 
 
-    /**
-     * 浼佷笟搴旂敤鍥炲
-     * @param prompt 鎻愮ず璇�
-     * @return 鍥炲鍐呭
-     */
-    String wxCpChat(String prompt);
-
 }
diff --git a/ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/chat/service/chat/impl/SseServiceImpl.java b/ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/chat/service/chat/impl/SseServiceImpl.java
index eb0ae4e..b6759f5 100644
--- a/ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/chat/service/chat/impl/SseServiceImpl.java
+++ b/ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/chat/service/chat/impl/SseServiceImpl.java
@@ -14,8 +14,6 @@
 import org.ruoyi.chat.util.SSEUtil;
 import org.ruoyi.common.chat.config.LocalCache;
 import org.ruoyi.common.chat.entity.Tts.TextToSpeech;
-import org.ruoyi.common.chat.entity.chat.ChatCompletion;
-import org.ruoyi.common.chat.entity.chat.ChatCompletionResponse;
 import org.ruoyi.common.chat.entity.chat.Message;
 import org.ruoyi.common.chat.entity.files.UploadFileResponse;
 import org.ruoyi.common.chat.entity.whisper.WhisperResponse;
@@ -269,22 +267,6 @@
             e.printStackTrace();
         }
         return file;
-    }
-
-
-    @Override
-    public String wxCpChat(String prompt) {
-        List<Message> messageList = new ArrayList<>();
-        Message message = Message.builder().role(Message.Role.USER).content(prompt).build();
-        messageList.add(message);
-        ChatCompletion chatCompletion = ChatCompletion
-            .builder()
-            .messages(messageList)
-            .model("gpt-4o-mini")
-            .stream(false)
-            .build();
-        ChatCompletionResponse chatCompletionResponse = openAiStreamClient.chatCompletion(chatCompletion);
-        return chatCompletionResponse.getChoices().get(0).getMessage().getContent().toString();
     }
 
 }
diff --git a/ruoyi-modules/ruoyi-wechat/pom.xml b/ruoyi-modules/ruoyi-wechat/pom.xml
new file mode 100644
index 0000000..8a71665
--- /dev/null
+++ b/ruoyi-modules/ruoyi-wechat/pom.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.ruoyi</groupId>
+        <artifactId>ruoyi-modules</artifactId>
+        <version>${revision}</version>
+        <relativePath>../pom.xml</relativePath>
+    </parent>
+
+    <artifactId>ruoyi-wechat</artifactId>
+
+    <properties>
+        <maven.compiler.source>17</maven.compiler.source>
+        <maven.compiler.target>17</maven.compiler.target>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.ruoyi</groupId>
+            <artifactId>ruoyi-chat-api</artifactId>
+        </dependency>
+    </dependencies>
+
+</project>
diff --git a/ruoyi-modules/ruoyi-wechat/src/main/java/org/ruoyi/builder/AbstractBuilder.java b/ruoyi-modules/ruoyi-wechat/src/main/java/org/ruoyi/builder/AbstractBuilder.java
new file mode 100644
index 0000000..6e97f5d
--- /dev/null
+++ b/ruoyi-modules/ruoyi-wechat/src/main/java/org/ruoyi/builder/AbstractBuilder.java
@@ -0,0 +1,12 @@
+package org.ruoyi.builder;
+
+import me.chanjar.weixin.cp.api.WxCpService;
+import me.chanjar.weixin.cp.bean.message.WxCpXmlMessage;
+import me.chanjar.weixin.cp.bean.message.WxCpXmlOutMessage;
+
+/**
+ *  @author <a href="https://github.com/binarywang">Binary Wang</a>
+ */
+public abstract class AbstractBuilder {
+  public abstract WxCpXmlOutMessage build(String content, WxCpXmlMessage wxMessage, WxCpService service);
+}
diff --git a/ruoyi-modules/ruoyi-wechat/src/main/java/org/ruoyi/builder/ImageBuilder.java b/ruoyi-modules/ruoyi-wechat/src/main/java/org/ruoyi/builder/ImageBuilder.java
new file mode 100644
index 0000000..f6786b0
--- /dev/null
+++ b/ruoyi-modules/ruoyi-wechat/src/main/java/org/ruoyi/builder/ImageBuilder.java
@@ -0,0 +1,24 @@
+package org.ruoyi.builder;
+
+import me.chanjar.weixin.cp.api.WxCpService;
+import me.chanjar.weixin.cp.bean.message.WxCpXmlMessage;
+import me.chanjar.weixin.cp.bean.message.WxCpXmlOutImageMessage;
+import me.chanjar.weixin.cp.bean.message.WxCpXmlOutMessage;
+
+/**
+ *  @author <a href="https://github.com/binarywang">Binary Wang</a>
+ */
+public class ImageBuilder extends AbstractBuilder {
+
+  @Override
+  public WxCpXmlOutMessage build(String content, WxCpXmlMessage wxMessage,
+                                 WxCpService service) {
+
+    WxCpXmlOutImageMessage m = WxCpXmlOutMessage.IMAGE().mediaId(content)
+        .fromUser(wxMessage.getToUserName()).toUser(wxMessage.getFromUserName())
+        .build();
+
+    return m;
+  }
+
+}
diff --git a/ruoyi-modules/ruoyi-wechat/src/main/java/org/ruoyi/builder/TextBuilder.java b/ruoyi-modules/ruoyi-wechat/src/main/java/org/ruoyi/builder/TextBuilder.java
new file mode 100644
index 0000000..707471e
--- /dev/null
+++ b/ruoyi-modules/ruoyi-wechat/src/main/java/org/ruoyi/builder/TextBuilder.java
@@ -0,0 +1,22 @@
+package org.ruoyi.builder;
+
+import me.chanjar.weixin.cp.api.WxCpService;
+import me.chanjar.weixin.cp.bean.message.WxCpXmlMessage;
+import me.chanjar.weixin.cp.bean.message.WxCpXmlOutMessage;
+import me.chanjar.weixin.cp.bean.message.WxCpXmlOutTextMessage;
+
+/**
+ *  @author <a href="https://github.com/binarywang">Binary Wang</a>
+ */
+public class TextBuilder extends AbstractBuilder {
+
+  @Override
+  public WxCpXmlOutMessage build(String content, WxCpXmlMessage wxMessage,
+                                 WxCpService service) {
+    WxCpXmlOutTextMessage m = WxCpXmlOutMessage.TEXT().content(content)
+        .fromUser(wxMessage.getToUserName()).toUser(wxMessage.getFromUserName())
+        .build();
+    return m;
+  }
+
+}
diff --git a/ruoyi-modules/ruoyi-wechat/src/main/java/org/ruoyi/config/WxCpConfiguration.java b/ruoyi-modules/ruoyi-wechat/src/main/java/org/ruoyi/config/WxCpConfiguration.java
new file mode 100644
index 0000000..8a5f0fb
--- /dev/null
+++ b/ruoyi-modules/ruoyi-wechat/src/main/java/org/ruoyi/config/WxCpConfiguration.java
@@ -0,0 +1,130 @@
+package org.ruoyi.config;
+
+
+import com.google.common.collect.Maps;
+import jakarta.annotation.PostConstruct;
+import lombok.val;
+import me.chanjar.weixin.common.api.WxConsts;
+import me.chanjar.weixin.cp.api.WxCpService;
+import me.chanjar.weixin.cp.api.impl.WxCpServiceImpl;
+import me.chanjar.weixin.cp.config.impl.WxCpDefaultConfigImpl;
+import me.chanjar.weixin.cp.constant.WxCpConsts;
+import me.chanjar.weixin.cp.message.WxCpMessageHandler;
+import me.chanjar.weixin.cp.message.WxCpMessageRouter;
+import org.ruoyi.handler.*;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.context.annotation.Configuration;
+
+import java.util.Map;
+import java.util.stream.Collectors;
+
+/**
+ * 鍗曞疄渚嬮厤缃�
+ *
+ * @author <a href="https://github.com/binarywang">Binary Wang</a>
+ */
+@Configuration
+@EnableConfigurationProperties(WxCpProperties.class)
+public class WxCpConfiguration {
+    private final LogHandler logHandler;
+    private NullHandler nullHandler;
+    private LocationHandler locationHandler;
+    private MenuHandler menuHandler;
+    private MsgHandler msgHandler;
+    private final UnsubscribeHandler unsubscribeHandler;
+    private SubscribeHandler subscribeHandler;
+    private WxCpProperties properties;
+
+    private static Map<Integer, WxCpMessageRouter> routers = Maps.newHashMap();
+    private static Map<Integer, WxCpService> cpServices = Maps.newHashMap();
+
+    @Autowired
+    public WxCpConfiguration(LogHandler logHandler, NullHandler nullHandler, LocationHandler locationHandler,
+                             MenuHandler menuHandler, MsgHandler msgHandler, UnsubscribeHandler unsubscribeHandler,
+                             SubscribeHandler subscribeHandler, WxCpProperties properties) {
+        this.logHandler = logHandler;
+        this.nullHandler = nullHandler;
+        this.locationHandler = locationHandler;
+        this.menuHandler = menuHandler;
+        this.msgHandler = msgHandler;
+        this.unsubscribeHandler = unsubscribeHandler;
+        this.subscribeHandler = subscribeHandler;
+        this.properties = properties;
+    }
+
+
+    public static Map<Integer, WxCpMessageRouter> getRouters() {
+        return routers;
+    }
+
+    public static WxCpService getCpService(Integer agentId) {
+        return cpServices.get(agentId);
+    }
+
+    @PostConstruct
+    public void initServices() {
+        cpServices = this.properties.getAppConfigs().stream().map(a -> {
+            val configStorage = new WxCpDefaultConfigImpl();
+            configStorage.setCorpId(this.properties.getCorpId());
+            configStorage.setAgentId(a.getAgentId());
+            configStorage.setCorpSecret(a.getSecret());
+            configStorage.setToken(a.getToken());
+            configStorage.setAesKey(a.getAesKey());
+            val service = new WxCpServiceImpl();
+            service.setWxCpConfigStorage(configStorage);
+            routers.put(a.getAgentId(), this.newRouter(service));
+            return service;
+        }).collect(Collectors.toMap(service -> service.getWxCpConfigStorage().getAgentId(), a -> a));
+    }
+
+    private WxCpMessageRouter newRouter(WxCpService wxCpService) {
+        final val newRouter = new WxCpMessageRouter(wxCpService);
+
+        // 璁板綍鎵�鏈変簨浠剁殑鏃ュ織 锛堝紓姝ユ墽琛岋級
+        newRouter.rule().handler(this.logHandler).next();
+
+        // 鑷畾涔夎彍鍗曚簨浠�
+        newRouter.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT)
+            .event(WxConsts.MenuButtonType.CLICK).handler(this.menuHandler).end();
+
+        // 鐐瑰嚮鑿滃崟閾炬帴浜嬩欢锛堣繖閲屼娇鐢ㄤ簡涓�涓┖鐨勫鐞嗗櫒锛屽彲浠ユ牴鎹嚜宸遍渶瑕佽繘琛屾墿灞曪級
+        newRouter.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT)
+            .event(WxConsts.MenuButtonType.VIEW).handler(this.nullHandler).end();
+
+        // 鍏虫敞浜嬩欢
+        newRouter.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT)
+            .event(WxConsts.EventType.SUBSCRIBE).handler(this.subscribeHandler)
+            .end();
+
+        // 鍙栨秷鍏虫敞浜嬩欢
+        newRouter.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT)
+            .event(WxConsts.EventType.UNSUBSCRIBE)
+            .handler((WxCpMessageHandler) this.unsubscribeHandler).end();
+
+        // 涓婃姤鍦扮悊浣嶇疆浜嬩欢
+        newRouter.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT)
+            .event(WxConsts.EventType.LOCATION).handler(this.locationHandler)
+            .end();
+
+        // 鎺ユ敹鍦扮悊浣嶇疆娑堟伅
+        newRouter.rule().async(false).msgType(WxConsts.XmlMsgType.LOCATION)
+            .handler(this.locationHandler).end();
+
+        // 鎵爜浜嬩欢锛堣繖閲屼娇鐢ㄤ簡涓�涓┖鐨勫鐞嗗櫒锛屽彲浠ユ牴鎹嚜宸遍渶瑕佽繘琛屾墿灞曪級
+        newRouter.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT)
+            .event(WxConsts.EventType.SCAN).handler((WxCpMessageHandler) this.nullHandler).end();
+
+        newRouter.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT)
+            .event(WxCpConsts.EventType.CHANGE_CONTACT).handler(new ContactChangeHandler()).end();
+
+        newRouter.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT)
+            .event(WxCpConsts.EventType.ENTER_AGENT).handler(new EnterAgentHandler()).end();
+
+        // 榛樿
+        newRouter.rule().async(false).handler(this.msgHandler).end();
+
+        return newRouter;
+    }
+
+}
diff --git a/ruoyi-modules/ruoyi-wechat/src/main/java/org/ruoyi/config/WxCpProperties.java b/ruoyi-modules/ruoyi-wechat/src/main/java/org/ruoyi/config/WxCpProperties.java
new file mode 100644
index 0000000..51077d6
--- /dev/null
+++ b/ruoyi-modules/ruoyi-wechat/src/main/java/org/ruoyi/config/WxCpProperties.java
@@ -0,0 +1,45 @@
+package org.ruoyi.config;
+
+import lombok.Data;
+import lombok.Getter;
+import lombok.Setter;
+import java.util.List;
+
+/**
+ * @author <a href="https://github.com/binarywang">Binary Wang</a>
+ */
+@Data
+public class WxCpProperties {
+  /**
+   * 璁剧疆浼佷笟寰俊鐨刢orpId
+   */
+  private String corpId;
+
+  private List<AppConfig> appConfigs;
+
+  @Getter
+  @Setter
+  public static class AppConfig {
+    /**
+     * 璁剧疆浼佷笟寰俊搴旂敤鐨凙gentId
+     */
+    private Integer agentId;
+
+    /**
+     * 璁剧疆浼佷笟寰俊搴旂敤鐨凷ecret
+     */
+    private String secret;
+
+    /**
+     * 璁剧疆浼佷笟寰俊搴旂敤鐨則oken
+     */
+    private String token;
+
+    /**
+     * 璁剧疆浼佷笟寰俊搴旂敤鐨凟ncodingAESKey
+     */
+    private String aesKey;
+
+  }
+
+}
diff --git a/ruoyi-modules/ruoyi-wechat/src/main/java/org/ruoyi/controller/wxcplogin/WxPortalController.java b/ruoyi-modules/ruoyi-wechat/src/main/java/org/ruoyi/controller/wxcplogin/WxPortalController.java
new file mode 100644
index 0000000..2857bc2
--- /dev/null
+++ b/ruoyi-modules/ruoyi-wechat/src/main/java/org/ruoyi/controller/wxcplogin/WxPortalController.java
@@ -0,0 +1,86 @@
+package org.ruoyi.controller.wxcplogin;
+
+import lombok.extern.slf4j.Slf4j;
+import me.chanjar.weixin.cp.api.WxCpService;
+import me.chanjar.weixin.cp.bean.message.WxCpXmlMessage;
+import me.chanjar.weixin.cp.bean.message.WxCpXmlOutMessage;
+import me.chanjar.weixin.cp.util.crypto.WxCpCryptUtil;
+import org.apache.commons.lang3.StringUtils;
+import org.ruoyi.common.core.utils.JsonUtils;
+
+import org.ruoyi.config.WxCpConfiguration;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.web.bind.annotation.*;
+
+/**
+ * @author <a href="https://github.com/binarywang">Binary Wang</a>
+ */
+@RestController
+@RequestMapping("/wx/cp")
+@Slf4j
+public class WxPortalController {
+
+    @Value("${wechat.cp.appConfigs[0].agentId}")
+    private Integer agentId;
+
+
+  @GetMapping(produces = "text/plain;charset=utf-8")
+  public String authGet(
+                        @RequestParam(name = "msg_signature", required = false) String signature,
+                        @RequestParam(name = "timestamp", required = false) String timestamp,
+                        @RequestParam(name = "nonce", required = false) String nonce,
+                        @RequestParam(name = "echostr", required = false) String echostr) {
+    log.info("\n鎺ユ敹鍒版潵鑷井淇℃湇鍔″櫒鐨勮璇佹秷鎭細signature = [{}], timestamp = [{}], nonce = [{}], echostr = [{}]",
+        signature, timestamp, nonce, echostr);
+
+    if (StringUtils.isAnyBlank(signature, timestamp, nonce, echostr)) {
+      throw new IllegalArgumentException("璇锋眰鍙傛暟闈炴硶锛岃鏍稿疄!");
+    }
+
+    final WxCpService wxCpService = WxCpConfiguration.getCpService(agentId);
+    if (wxCpService == null) {
+      throw new IllegalArgumentException(String.format("鏈壘鍒板搴攁gentId=[%d]鐨勯厤缃紝璇锋牳瀹烇紒", agentId));
+    }
+
+    if (wxCpService.checkSignature(signature, timestamp, nonce, echostr)) {
+      return new WxCpCryptUtil(wxCpService.getWxCpConfigStorage()).decrypt(echostr);
+    }
+
+    return "闈炴硶璇锋眰";
+  }
+
+  @PostMapping(produces = "application/xml; charset=UTF-8")
+  public String post(
+                     @RequestBody String requestBody,
+                     @RequestParam("msg_signature") String signature,
+                     @RequestParam("timestamp") String timestamp,
+                     @RequestParam("nonce") String nonce) {
+    log.info("\n鎺ユ敹寰俊璇锋眰锛歔signature=[{}], timestamp=[{}], nonce=[{}], requestBody=[\n{}\n] ",
+        signature, timestamp, nonce, requestBody);
+
+    final WxCpService wxCpService = WxCpConfiguration.getCpService(1000002);
+    WxCpXmlMessage inMessage = WxCpXmlMessage.fromEncryptedXml(requestBody, wxCpService.getWxCpConfigStorage(),
+        timestamp, nonce, signature);
+    log.debug("\n娑堟伅瑙e瘑鍚庡唴瀹逛负锛歕n{} ", JsonUtils.toJson(inMessage));
+    WxCpXmlOutMessage outMessage = this.route(1000002, inMessage);
+    if (outMessage == null) {
+      return "";
+    }
+
+    String out = outMessage.toEncryptedXml(wxCpService.getWxCpConfigStorage());
+    log.debug("\n缁勮鍥炲淇℃伅锛歿}", out);
+    return out;
+  }
+
+  private WxCpXmlOutMessage route(Integer agentId, WxCpXmlMessage message) {
+    try {
+      return WxCpConfiguration.getRouters().get(agentId).route(message);
+    } catch (Exception e) {
+      log.error(e.getMessage(), e);
+    }
+
+    return null;
+  }
+
+
+}
diff --git a/ruoyi-modules/ruoyi-wechat/src/main/java/org/ruoyi/handler/AbstractHandler.java b/ruoyi-modules/ruoyi-wechat/src/main/java/org/ruoyi/handler/AbstractHandler.java
new file mode 100644
index 0000000..7ec2b0c
--- /dev/null
+++ b/ruoyi-modules/ruoyi-wechat/src/main/java/org/ruoyi/handler/AbstractHandler.java
@@ -0,0 +1,9 @@
+package org.ruoyi.handler;
+
+import me.chanjar.weixin.cp.message.WxCpMessageHandler;
+
+/**
+ * @author <a href="https://github.com/binarywang">Binary Wang</a>
+ */
+public abstract class AbstractHandler implements WxCpMessageHandler {
+}
diff --git a/ruoyi-modules/ruoyi-wechat/src/main/java/org/ruoyi/handler/ContactChangeHandler.java b/ruoyi-modules/ruoyi-wechat/src/main/java/org/ruoyi/handler/ContactChangeHandler.java
new file mode 100644
index 0000000..178229d
--- /dev/null
+++ b/ruoyi-modules/ruoyi-wechat/src/main/java/org/ruoyi/handler/ContactChangeHandler.java
@@ -0,0 +1,34 @@
+package org.ruoyi.handler;
+
+
+import lombok.extern.slf4j.Slf4j;
+import me.chanjar.weixin.common.session.WxSessionManager;
+import me.chanjar.weixin.cp.api.WxCpService;
+import me.chanjar.weixin.cp.bean.message.WxCpXmlMessage;
+import me.chanjar.weixin.cp.bean.message.WxCpXmlOutMessage;
+import org.ruoyi.builder.TextBuilder;
+import org.ruoyi.common.core.utils.JsonUtils;
+
+import org.springframework.stereotype.Component;
+
+import java.util.Map;
+
+/**
+ * 閫氳褰曞彉鏇翠簨浠跺鐞嗗櫒.
+ *
+ * @author <a href="https://github.com/binarywang">Binary Wang</a>
+ */
+@Slf4j
+@Component
+public class ContactChangeHandler extends AbstractHandler {
+
+    @Override
+    public WxCpXmlOutMessage handle(WxCpXmlMessage wxMessage, Map<String, Object> context, WxCpService cpService,
+                                    WxSessionManager sessionManager) {
+        String content = "鏀跺埌閫氳褰曞彉鏇翠簨浠讹紝鍐呭锛�" + JsonUtils.toJson(wxMessage);
+        log.info(content);
+
+        return new TextBuilder().build(content, wxMessage, cpService);
+    }
+
+}
diff --git a/ruoyi-modules/ruoyi-wechat/src/main/java/org/ruoyi/handler/EnterAgentHandler.java b/ruoyi-modules/ruoyi-wechat/src/main/java/org/ruoyi/handler/EnterAgentHandler.java
new file mode 100644
index 0000000..c98fe68
--- /dev/null
+++ b/ruoyi-modules/ruoyi-wechat/src/main/java/org/ruoyi/handler/EnterAgentHandler.java
@@ -0,0 +1,29 @@
+package org.ruoyi.handler;
+
+import lombok.extern.slf4j.Slf4j;
+import me.chanjar.weixin.common.error.WxErrorException;
+import me.chanjar.weixin.common.session.WxSessionManager;
+import me.chanjar.weixin.cp.api.WxCpService;
+import me.chanjar.weixin.cp.bean.message.WxCpXmlMessage;
+import me.chanjar.weixin.cp.bean.message.WxCpXmlOutMessage;
+
+import java.util.Map;
+
+/**
+ * <pre>
+ *
+ * Created by Binary Wang on 2018/8/27.
+ * </pre>
+ *
+ * @author <a href="https://github.com/binarywang">Binary Wang</a>
+ */
+@Slf4j
+public class EnterAgentHandler extends AbstractHandler {
+    private static final int TEST_AGENT = 1000002;
+
+    @Override
+    public WxCpXmlOutMessage handle(WxCpXmlMessage wxMessage, Map<String, Object> context, WxCpService wxCpService, WxSessionManager sessionManager) throws WxErrorException {
+        // do something
+        return null;
+    }
+}
diff --git a/ruoyi-modules/ruoyi-wechat/src/main/java/org/ruoyi/handler/LocationHandler.java b/ruoyi-modules/ruoyi-wechat/src/main/java/org/ruoyi/handler/LocationHandler.java
new file mode 100644
index 0000000..56d1fb3
--- /dev/null
+++ b/ruoyi-modules/ruoyi-wechat/src/main/java/org/ruoyi/handler/LocationHandler.java
@@ -0,0 +1,44 @@
+package org.ruoyi.handler;
+
+import lombok.extern.slf4j.Slf4j;
+import me.chanjar.weixin.common.api.WxConsts;
+import me.chanjar.weixin.common.session.WxSessionManager;
+import me.chanjar.weixin.cp.api.WxCpService;
+import me.chanjar.weixin.cp.bean.message.WxCpXmlMessage;
+import me.chanjar.weixin.cp.bean.message.WxCpXmlOutMessage;
+import org.ruoyi.builder.TextBuilder;
+import org.springframework.stereotype.Component;
+
+import java.util.Map;
+
+/**
+ * @author <a href="https://github.com/binarywang">Binary Wang</a>
+ */
+@Slf4j
+@Component
+public class LocationHandler extends AbstractHandler {
+
+    @Override
+    public WxCpXmlOutMessage handle(WxCpXmlMessage wxMessage, Map<String, Object> context, WxCpService cpService,
+                                    WxSessionManager sessionManager) {
+        if (wxMessage.getMsgType().equals(WxConsts.XmlMsgType.LOCATION)) {
+            //TODO 鎺ユ敹澶勭悊鐢ㄦ埛鍙戦�佺殑鍦扮悊浣嶇疆娑堟伅
+            try {
+                String content = "鎰熻阿鍙嶉锛屾偍鐨勭殑鍦扮悊浣嶇疆宸叉敹鍒帮紒";
+                return new TextBuilder().build(content, wxMessage, null);
+            } catch (Exception e) {
+                log.error("浣嶇疆娑堟伅鎺ユ敹澶勭悊澶辫触", e);
+                return null;
+            }
+        }
+
+        //涓婃姤鍦扮悊浣嶇疆浜嬩欢
+        log.info("\n涓婃姤鍦扮悊浣嶇疆锛岀含搴� : {}\n缁忓害 : {}\n绮惧害 : {}",
+            wxMessage.getLatitude(), wxMessage.getLongitude(), String.valueOf(wxMessage.getPrecision()));
+
+        //TODO  鍙互灏嗙敤鎴峰湴鐞嗕綅缃俊鎭繚瀛樺埌鏈湴鏁版嵁搴擄紝浠ヤ究浠ュ悗浣跨敤
+
+        return null;
+    }
+
+}
diff --git a/ruoyi-modules/ruoyi-wechat/src/main/java/org/ruoyi/handler/LogHandler.java b/ruoyi-modules/ruoyi-wechat/src/main/java/org/ruoyi/handler/LogHandler.java
new file mode 100644
index 0000000..ea12c40
--- /dev/null
+++ b/ruoyi-modules/ruoyi-wechat/src/main/java/org/ruoyi/handler/LogHandler.java
@@ -0,0 +1,26 @@
+package org.ruoyi.handler;
+
+import lombok.extern.slf4j.Slf4j;
+import me.chanjar.weixin.common.session.WxSessionManager;
+import me.chanjar.weixin.cp.api.WxCpService;
+import me.chanjar.weixin.cp.bean.message.WxCpXmlMessage;
+import me.chanjar.weixin.cp.bean.message.WxCpXmlOutMessage;
+import org.ruoyi.common.core.utils.JsonUtils;
+import org.springframework.stereotype.Component;
+
+import java.util.Map;
+
+/**
+ * @author <a href="https://github.com/binarywang">Binary Wang</a>
+ */
+@Slf4j
+@Component
+public class LogHandler extends AbstractHandler {
+    @Override
+    public WxCpXmlOutMessage handle(WxCpXmlMessage wxMessage, Map<String, Object> context, WxCpService cpService,
+                                    WxSessionManager sessionManager) {
+        log.info("\n鎺ユ敹鍒拌姹傛秷鎭紝鍐呭锛歿}", JsonUtils.toJson(wxMessage));
+        return null;
+    }
+
+}
diff --git a/ruoyi-modules/ruoyi-wechat/src/main/java/org/ruoyi/handler/MenuHandler.java b/ruoyi-modules/ruoyi-wechat/src/main/java/org/ruoyi/handler/MenuHandler.java
new file mode 100644
index 0000000..803db7f
--- /dev/null
+++ b/ruoyi-modules/ruoyi-wechat/src/main/java/org/ruoyi/handler/MenuHandler.java
@@ -0,0 +1,34 @@
+package org.ruoyi.handler;
+
+import me.chanjar.weixin.common.api.WxConsts.MenuButtonType;
+import me.chanjar.weixin.common.session.WxSessionManager;
+import me.chanjar.weixin.cp.api.WxCpService;
+import me.chanjar.weixin.cp.bean.message.WxCpXmlMessage;
+import me.chanjar.weixin.cp.bean.message.WxCpXmlOutMessage;
+import org.springframework.stereotype.Component;
+
+import java.util.Map;
+
+/**
+ * @author <a href="https://github.com/binarywang">Binary Wang</a>
+ */
+@Component
+public class MenuHandler extends AbstractHandler {
+
+  @Override
+  public WxCpXmlOutMessage handle(WxCpXmlMessage wxMessage, Map<String, Object> context, WxCpService cpService,
+                                  WxSessionManager sessionManager) {
+
+    String msg = String.format("type:%s, event:%s, key:%s",
+        wxMessage.getMsgType(), wxMessage.getEvent(),
+        wxMessage.getEventKey());
+    if (MenuButtonType.VIEW.equals(wxMessage.getEvent())) {
+      return null;
+    }
+
+    return WxCpXmlOutMessage.TEXT().content(msg)
+        .fromUser(wxMessage.getToUserName()).toUser(wxMessage.getFromUserName())
+        .build();
+  }
+
+}
diff --git a/ruoyi-modules/ruoyi-wechat/src/main/java/org/ruoyi/handler/MsgHandler.java b/ruoyi-modules/ruoyi-wechat/src/main/java/org/ruoyi/handler/MsgHandler.java
new file mode 100644
index 0000000..6527371
--- /dev/null
+++ b/ruoyi-modules/ruoyi-wechat/src/main/java/org/ruoyi/handler/MsgHandler.java
@@ -0,0 +1,43 @@
+package org.ruoyi.handler;
+
+import lombok.RequiredArgsConstructor;
+import me.chanjar.weixin.common.api.WxConsts;
+import me.chanjar.weixin.common.session.WxSessionManager;
+import me.chanjar.weixin.cp.api.WxCpService;
+import me.chanjar.weixin.cp.bean.message.WxCpXmlMessage;
+import me.chanjar.weixin.cp.bean.message.WxCpXmlOutMessage;
+
+import org.ruoyi.builder.TextBuilder;
+import org.ruoyi.service.IChatVxService;
+import org.springframework.stereotype.Component;
+
+import java.util.Map;
+
+/**
+ * @author <a href="https://github.com/binarywang">Binary Wang</a>
+ */
+@Component
+@RequiredArgsConstructor
+public class MsgHandler extends AbstractHandler {
+
+    private final IChatVxService chatVxService;
+
+    @Override
+    public WxCpXmlOutMessage handle(WxCpXmlMessage wxMessage, Map<String, Object> context, WxCpService cpService,
+                                    WxSessionManager sessionManager) {
+        final String msgType = wxMessage.getMsgType();
+        if (msgType == null) {
+            // 濡傛灉msgType娌℃湁锛屽氨鑷繁鏍规嵁鍏蜂綋鎶ユ枃鍐呭鍋氬鐞�
+        }
+
+        if (!msgType.equals(WxConsts.XmlMsgType.EVENT)) {
+            //TODO 鍙互閫夋嫨灏嗘秷鎭繚瀛樺埌鏈湴
+        }
+        //TODO 缁勮鍥炲娑堟伅
+        String content = chatVxService.chat(wxMessage.getContent());
+
+        return new TextBuilder().build(content, wxMessage, cpService);
+
+    }
+
+}
diff --git a/ruoyi-modules/ruoyi-wechat/src/main/java/org/ruoyi/handler/NullHandler.java b/ruoyi-modules/ruoyi-wechat/src/main/java/org/ruoyi/handler/NullHandler.java
new file mode 100644
index 0000000..2677d73
--- /dev/null
+++ b/ruoyi-modules/ruoyi-wechat/src/main/java/org/ruoyi/handler/NullHandler.java
@@ -0,0 +1,23 @@
+package org.ruoyi.handler;
+
+import me.chanjar.weixin.common.session.WxSessionManager;
+import me.chanjar.weixin.cp.api.WxCpService;
+import me.chanjar.weixin.cp.bean.message.WxCpXmlMessage;
+import me.chanjar.weixin.cp.bean.message.WxCpXmlOutMessage;
+import org.springframework.stereotype.Component;
+
+import java.util.Map;
+
+/**
+ * @author <a href="https://github.com/binarywang">Binary Wang</a>
+ */
+@Component
+public class NullHandler extends AbstractHandler {
+
+  @Override
+  public WxCpXmlOutMessage handle(WxCpXmlMessage wxMessage, Map<String, Object> context, WxCpService cpService,
+                                  WxSessionManager sessionManager) {
+    return null;
+  }
+
+}
diff --git a/ruoyi-modules/ruoyi-wechat/src/main/java/org/ruoyi/handler/ScanHandler.java b/ruoyi-modules/ruoyi-wechat/src/main/java/org/ruoyi/handler/ScanHandler.java
new file mode 100644
index 0000000..4d9a502
--- /dev/null
+++ b/ruoyi-modules/ruoyi-wechat/src/main/java/org/ruoyi/handler/ScanHandler.java
@@ -0,0 +1,8 @@
+package org.ruoyi.handler;
+
+/**
+ * @author <a href="https://github.com/binarywang">Binary Wang</a>
+ */
+public abstract class ScanHandler extends AbstractHandler {
+
+}
diff --git a/ruoyi-modules/ruoyi-wechat/src/main/java/org/ruoyi/handler/SubscribeHandler.java b/ruoyi-modules/ruoyi-wechat/src/main/java/org/ruoyi/handler/SubscribeHandler.java
new file mode 100644
index 0000000..c5e42fb
--- /dev/null
+++ b/ruoyi-modules/ruoyi-wechat/src/main/java/org/ruoyi/handler/SubscribeHandler.java
@@ -0,0 +1,63 @@
+package org.ruoyi.handler;
+
+import lombok.extern.slf4j.Slf4j;
+import me.chanjar.weixin.common.error.WxErrorException;
+import me.chanjar.weixin.common.session.WxSessionManager;
+import me.chanjar.weixin.cp.api.WxCpService;
+import me.chanjar.weixin.cp.bean.WxCpUser;
+import me.chanjar.weixin.cp.bean.message.WxCpXmlMessage;
+import me.chanjar.weixin.cp.bean.message.WxCpXmlOutMessage;
+import org.ruoyi.builder.TextBuilder;
+import org.springframework.stereotype.Component;
+
+import java.util.Map;
+
+/**
+ * @author <a href="https://github.com/binarywang">Binary Wang</a>
+ */
+@Slf4j
+@Component
+public class SubscribeHandler extends AbstractHandler {
+
+    @Override
+    public WxCpXmlOutMessage handle(WxCpXmlMessage wxMessage, Map<String, Object> context, WxCpService cpService,
+                                    WxSessionManager sessionManager) throws WxErrorException {
+
+        log.info("鏂板叧娉ㄧ敤鎴� OPENID: " + wxMessage.getFromUserName());
+
+        // 鑾峰彇寰俊鐢ㄦ埛鍩烘湰淇℃伅
+        WxCpUser userWxInfo = cpService.getUserService().getById(wxMessage.getFromUserName());
+
+        if (userWxInfo != null) {
+            // TODO 鍙互娣诲姞鍏虫敞鐢ㄦ埛鍒版湰鍦�
+        }
+
+        WxCpXmlOutMessage responseResult = null;
+        try {
+            responseResult = handleSpecial(wxMessage);
+        } catch (Exception e) {
+            log.error(e.getMessage(), e);
+        }
+
+        if (responseResult != null) {
+            return responseResult;
+        }
+
+        try {
+            return new TextBuilder().build("鎰熻阿鍏虫敞", wxMessage, cpService);
+        } catch (Exception e) {
+            log.error(e.getMessage(), e);
+        }
+
+        return null;
+    }
+
+    /**
+     * 澶勭悊鐗规畩璇锋眰锛屾瘮濡傚鏋滄槸鎵爜杩涙潵鐨勶紝鍙互鍋氱浉搴斿鐞�
+     */
+    private WxCpXmlOutMessage handleSpecial(WxCpXmlMessage wxMessage) {
+        //TODO
+        return null;
+    }
+
+}
diff --git a/ruoyi-modules/ruoyi-wechat/src/main/java/org/ruoyi/handler/UnsubscribeHandler.java b/ruoyi-modules/ruoyi-wechat/src/main/java/org/ruoyi/handler/UnsubscribeHandler.java
new file mode 100644
index 0000000..7793346
--- /dev/null
+++ b/ruoyi-modules/ruoyi-wechat/src/main/java/org/ruoyi/handler/UnsubscribeHandler.java
@@ -0,0 +1,28 @@
+package org.ruoyi.handler;
+
+import lombok.extern.slf4j.Slf4j;
+import me.chanjar.weixin.common.session.WxSessionManager;
+import me.chanjar.weixin.cp.api.WxCpService;
+import me.chanjar.weixin.cp.bean.message.WxCpXmlMessage;
+import me.chanjar.weixin.cp.bean.message.WxCpXmlOutMessage;
+import org.springframework.stereotype.Component;
+
+import java.util.Map;
+
+/**
+ * @author <a href="https://github.com/binarywang">Binary Wang</a>
+ */
+@Slf4j
+@Component
+public class UnsubscribeHandler extends AbstractHandler {
+
+    @Override
+    public WxCpXmlOutMessage handle(WxCpXmlMessage wxMessage, Map<String, Object> context, WxCpService cpService,
+                                    WxSessionManager sessionManager) {
+        String openId = wxMessage.getFromUserName();
+        log.info("鍙栨秷鍏虫敞鐢ㄦ埛 OPENID: " + openId);
+        // TODO 鍙互鏇存柊鏈湴鏁版嵁搴撲负鍙栨秷鍏虫敞鐘舵��
+        return null;
+    }
+
+}

--
Gitblit v1.9.3