소스 검색

feat: 新增wechat模块

ageerle 1 개월 전
부모
커밋
cd490aa0e5
23개의 변경된 파일745개의 추가작업 그리고 25개의 파일을 삭제
  1. 19 0
      ruoyi-modules-api/ruoyi-chat-api/src/main/java/org/ruoyi/service/IChatVxService.java
  2. 37 0
      ruoyi-modules-api/ruoyi-chat-api/src/main/java/org/ruoyi/service/impl/ChatVxServiceImpl.java
  3. 1 0
      ruoyi-modules/pom.xml
  4. 0 7
      ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/chat/service/chat/ISseService.java
  5. 0 18
      ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/chat/service/chat/impl/SseServiceImpl.java
  6. 28 0
      ruoyi-modules/ruoyi-wechat/pom.xml
  7. 12 0
      ruoyi-modules/ruoyi-wechat/src/main/java/org/ruoyi/builder/AbstractBuilder.java
  8. 24 0
      ruoyi-modules/ruoyi-wechat/src/main/java/org/ruoyi/builder/ImageBuilder.java
  9. 22 0
      ruoyi-modules/ruoyi-wechat/src/main/java/org/ruoyi/builder/TextBuilder.java
  10. 130 0
      ruoyi-modules/ruoyi-wechat/src/main/java/org/ruoyi/config/WxCpConfiguration.java
  11. 45 0
      ruoyi-modules/ruoyi-wechat/src/main/java/org/ruoyi/config/WxCpProperties.java
  12. 86 0
      ruoyi-modules/ruoyi-wechat/src/main/java/org/ruoyi/controller/wxcplogin/WxPortalController.java
  13. 9 0
      ruoyi-modules/ruoyi-wechat/src/main/java/org/ruoyi/handler/AbstractHandler.java
  14. 34 0
      ruoyi-modules/ruoyi-wechat/src/main/java/org/ruoyi/handler/ContactChangeHandler.java
  15. 29 0
      ruoyi-modules/ruoyi-wechat/src/main/java/org/ruoyi/handler/EnterAgentHandler.java
  16. 44 0
      ruoyi-modules/ruoyi-wechat/src/main/java/org/ruoyi/handler/LocationHandler.java
  17. 26 0
      ruoyi-modules/ruoyi-wechat/src/main/java/org/ruoyi/handler/LogHandler.java
  18. 34 0
      ruoyi-modules/ruoyi-wechat/src/main/java/org/ruoyi/handler/MenuHandler.java
  19. 43 0
      ruoyi-modules/ruoyi-wechat/src/main/java/org/ruoyi/handler/MsgHandler.java
  20. 23 0
      ruoyi-modules/ruoyi-wechat/src/main/java/org/ruoyi/handler/NullHandler.java
  21. 8 0
      ruoyi-modules/ruoyi-wechat/src/main/java/org/ruoyi/handler/ScanHandler.java
  22. 63 0
      ruoyi-modules/ruoyi-wechat/src/main/java/org/ruoyi/handler/SubscribeHandler.java
  23. 28 0
      ruoyi-modules/ruoyi-wechat/src/main/java/org/ruoyi/handler/UnsubscribeHandler.java

+ 19 - 0
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);
+
+}

+ 37 - 0
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();
+    }
+
+}

+ 1 - 0
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>

+ 0 - 7
ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/chat/service/chat/ISseService.java

@@ -48,11 +48,4 @@ public interface ISseService {
     UploadFileResponse upload(MultipartFile file);
 
 
-    /**
-     * 企业应用回复
-     * @param prompt 提示词
-     * @return 回复内容
-     */
-    String wxCpChat(String prompt);
-
 }

+ 0 - 18
ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/chat/service/chat/impl/SseServiceImpl.java

@@ -14,8 +14,6 @@ import org.ruoyi.chat.util.IpUtil;
 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;
@@ -271,20 +269,4 @@ public class SseServiceImpl implements ISseService {
         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();
-    }
-
 }

+ 28 - 0
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>

+ 12 - 0
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);
+}

+ 24 - 0
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;
+  }
+
+}

+ 22 - 0
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;
+  }
+
+}

+ 130 - 0
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;
+    }
+
+}

+ 45 - 0
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 {
+  /**
+   * 设置企业微信的corpId
+   */
+  private String corpId;
+
+  private List<AppConfig> appConfigs;
+
+  @Getter
+  @Setter
+  public static class AppConfig {
+    /**
+     * 设置企业微信应用的AgentId
+     */
+    private Integer agentId;
+
+    /**
+     * 设置企业微信应用的Secret
+     */
+    private String secret;
+
+    /**
+     * 设置企业微信应用的token
+     */
+    private String token;
+
+    /**
+     * 设置企业微信应用的EncodingAESKey
+     */
+    private String aesKey;
+
+  }
+
+}

+ 86 - 0
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("未找到对应agentId=[%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消息解密后内容为:\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;
+  }
+
+
+}

+ 9 - 0
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 {
+}

+ 34 - 0
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);
+    }
+
+}

+ 29 - 0
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;
+    }
+}

+ 44 - 0
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;
+    }
+
+}

+ 26 - 0
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;
+    }
+
+}

+ 34 - 0
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();
+  }
+
+}

+ 43 - 0
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);
+
+    }
+
+}

+ 23 - 0
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;
+  }
+
+}

+ 8 - 0
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 {
+
+}

+ 63 - 0
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;
+    }
+
+}

+ 28 - 0
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;
+    }
+
+}