From 5476a4b0b7a30b05233af79f06dadcb56008b9b9 Mon Sep 17 00:00:00 2001
From: ageer <ageerle@163.com>
Date: 星期三, 30 四月 2025 19:34:09 +0800
Subject: [PATCH] fix: 公众号登录功能同步

---
 ruoyi-modules/ruoyi-system/src/main/java/org/ruoyi/system/service/SysLoginService.java     |   64 -----
 ruoyi-modules/ruoyi-wechat/src/main/java/org/ruoyi/util/WeixinMsgUtil.java                 |   66 +++++
 ruoyi-modules/ruoyi-wechat/pom.xml                                                         |    4 
 ruoyi-modules/ruoyi-wechat/src/main/java/org/ruoyi/service/impl/WeixinUserServiceImpl.java |   65 +++++
 ruoyi-admin/pom.xml                                                                        |    4 
 ruoyi-modules/ruoyi-wechat/src/main/java/org/ruoyi/util/WeixinApiUtil.java                 |   81 ++++++
 pom.xml                                                                                    |   10 
 ruoyi-modules/ruoyi-wechat/src/main/java/org/ruoyi/domin/ReceiveMessage.java               |   58 ++++
 ruoyi-modules/ruoyi-wechat/src/main/java/org/ruoyi/controller/WeixinServerController.java  |   48 ++++
 ruoyi-modules/ruoyi-wechat/src/main/java/org/ruoyi/controller/WxPortalController.java      |    2 
 ruoyi-modules/ruoyi-wechat/src/main/java/org/ruoyi/controller/WeixinUserController.java    |   52 ++++
 ruoyi-modules/ruoyi-wechat/src/main/java/org/ruoyi/domin/WeixinQrCode.java                 |   15 +
 ruoyi-modules/ruoyi-wechat/src/main/java/org/ruoyi/util/WeixinQrCodeCacheUtil.java         |   34 ++
 ruoyi-modules/ruoyi-wechat/src/main/java/org/ruoyi/util/KeyUtils.java                      |   24 ++
 ruoyi-modules/ruoyi-wechat/src/main/java/org/ruoyi/util/XmlUtil.java                       |   28 ++
 ruoyi-modules/ruoyi-wechat/src/main/java/org/ruoyi/config/WxCpProperties.java              |    3 
 ruoyi-modules/ruoyi-wechat/src/main/java/org/ruoyi/service/WeixinUserService.java          |   10 
 ruoyi-modules/ruoyi-wechat/src/main/java/org/ruoyi/service/VxLoginService.java             |  108 +++++++++
 18 files changed, 609 insertions(+), 67 deletions(-)

diff --git a/pom.xml b/pom.xml
index fde8430..fada46e 100644
--- a/pom.xml
+++ b/pom.xml
@@ -337,11 +337,11 @@
                 <version>${revision}</version>
             </dependency>
 
-<!--            <dependency>-->
-<!--                <groupId>org.ruoyi</groupId>-->
-<!--                <artifactId>ruoyi-demo</artifactId>-->
-<!--                <version>${revision}</version>-->
-<!--            </dependency>-->
+            <dependency>
+                <groupId>org.ruoyi</groupId>
+                <artifactId>ruoyi-wechat</artifactId>
+                <version>${revision}</version>
+            </dependency>
 
         </dependencies>
     </dependencyManagement>
diff --git a/ruoyi-admin/pom.xml b/ruoyi-admin/pom.xml
index 1cee87a..8ae7ca7 100644
--- a/ruoyi-admin/pom.xml
+++ b/ruoyi-admin/pom.xml
@@ -57,6 +57,10 @@
             <artifactId>ruoyi-generator</artifactId>
         </dependency>
 
+        <dependency>
+            <groupId>org.ruoyi</groupId>
+            <artifactId>ruoyi-wechat</artifactId>
+        </dependency>
 
     </dependencies>
 
diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/ruoyi/system/service/SysLoginService.java b/ruoyi-modules/ruoyi-system/src/main/java/org/ruoyi/system/service/SysLoginService.java
index d2558b6..af88f61 100644
--- a/ruoyi-modules/ruoyi-system/src/main/java/org/ruoyi/system/service/SysLoginService.java
+++ b/ruoyi-modules/ruoyi-system/src/main/java/org/ruoyi/system/service/SysLoginService.java
@@ -19,12 +19,10 @@
 import org.ruoyi.common.core.domain.dto.RoleDTO;
 import org.ruoyi.common.core.domain.model.LoginUser;
 import org.ruoyi.common.core.domain.model.VisitorLoginBody;
-import org.ruoyi.common.core.domain.model.VisitorLoginUser;
 import org.ruoyi.common.core.enums.*;
 import org.ruoyi.common.core.exception.user.CaptchaException;
 import org.ruoyi.common.core.exception.user.CaptchaExpireException;
 import org.ruoyi.common.core.exception.user.UserException;
-import org.ruoyi.common.core.service.ConfigService;
 import org.ruoyi.common.core.utils.*;
 import org.ruoyi.common.log.event.LogininforEvent;
 import org.ruoyi.common.redis.utils.RedisUtils;
@@ -32,8 +30,7 @@
 import org.ruoyi.common.tenant.exception.TenantException;
 import org.ruoyi.common.tenant.helper.TenantHelper;
 import org.ruoyi.system.domain.SysUser;
-import org.ruoyi.system.domain.bo.SysUserBo;
-import org.ruoyi.system.domain.vo.LoginVo;
+
 import org.ruoyi.system.domain.vo.SysTenantVo;
 import org.ruoyi.system.domain.vo.SysUserVo;
 import org.ruoyi.system.mapper.SysUserMapper;
@@ -43,7 +40,6 @@
 import java.time.Duration;
 import java.util.Date;
 import java.util.List;
-import java.util.UUID;
 import java.util.function.Supplier;
 
 /**
@@ -60,8 +56,6 @@
     private final WxMaService wxMaService;
     private final ISysPermissionService permissionService;
     private final ISysTenantService tenantService;
-    private final ISysUserService userService;
-    private final ConfigService configService;
 
     @Value("${user.password.maxRetryCount}")
     private Integer maxRetryCount;
@@ -69,7 +63,7 @@
     private Integer lockTime;
 
     /**
-     * 鑾峰彇寰俊
+     * 鑾峰彇寰俊code
      * @param xcxCode 鑾峰彇xcxCode
     */
     public String getOpenidFromCode(String xcxCode) {
@@ -137,9 +131,8 @@
         return StpUtil.getTokenValue();
     }
 
-
     /**
-     * 娓稿鐧诲綍
+     * 寰俊灏忕▼搴忕櫥褰�
      *
      * @param loginBody
      * @return String
@@ -163,57 +156,6 @@
             }
         }
     }
-
-    public LoginVo mpLogin(String openid) {
-        // 浣跨敤 openid 鏌ヨ缁戝畾鐢ㄦ埛 濡傛湭缁戝畾鐢ㄦ埛 鍒欐牴鎹笟鍔¤嚜琛屽鐞� 渚嬪 鍒涘缓榛樿鐢ㄦ埛
-        SysUserVo user = userService.selectUserByOpenId(openid);
-        VisitorLoginUser loginUser = new VisitorLoginUser();
-        if (ObjectUtil.isNull(user)) {
-            SysUserBo sysUser = new SysUserBo();
-            // 鏀逛负鑷
-            String name = "鐢ㄦ埛" + UUID.randomUUID().toString().replace("-", "");
-            // 璁剧疆榛樿鐢ㄦ埛鍚�
-            sysUser.setUserName(name);
-            // 璁剧疆榛樿鏄电О
-            sysUser.setNickName(name);
-            // 璁剧疆榛樿瀵嗙爜
-            sysUser.setPassword(BCrypt.hashpw("123456"));
-            // 璁剧疆寰俊openId
-            sysUser.setOpenId(openid);
-            String configValue = configService.getConfigValue("mail", "amount");
-            // 璁剧疆榛樿浣欓
-            sysUser.setUserBalance(NumberUtils.toDouble(configValue, 1));
-            // 娉ㄥ唽鐢ㄦ埛,璁剧疆榛樿绉熸埛涓�0
-            SysUser registerUser = userService.registerUser(sysUser, "0");
-
-            // 鏋勫缓鐧诲綍鐢ㄦ埛淇℃伅
-            loginUser.setTenantId("0");
-            loginUser.setUserId(registerUser.getUserId());
-            loginUser.setUsername(registerUser.getUserName());
-            loginUser.setUserType(UserType.APP_USER.getUserType());
-            loginUser.setOpenid(openid);
-            loginUser.setNickName(registerUser.getNickName());
-
-        } else {
-            // 姝ゅ鍙牴鎹櫥褰曠敤鎴风殑鏁版嵁涓嶅悓 鑷鍒涘缓 loginUser
-            loginUser.setTenantId(user.getTenantId());
-            loginUser.setUserId(user.getUserId());
-            loginUser.setUsername(user.getUserName());
-            loginUser.setUserType(user.getUserType());
-            loginUser.setNickName(user.getNickName());
-            loginUser.setAvatar(user.getWxAvatar());
-            loginUser.setOpenid(openid);
-        }
-        // 鐢熸垚token
-        LoginHelper.loginByDevice(loginUser, DeviceType.XCX);
-        recordLogininfor(loginUser.getTenantId(), loginUser.getUsername(), Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success"));
-        LoginVo loginVo = new LoginVo();
-        // 鐢熸垚浠ょ墝
-        loginVo.setToken(StpUtil.getTokenValue());
-        loginVo.setUserInfo(loginUser);
-        return loginVo;
-    }
-
 
     /**
      * 閫�鍑虹櫥褰�
diff --git a/ruoyi-modules/ruoyi-wechat/pom.xml b/ruoyi-modules/ruoyi-wechat/pom.xml
index 8a71665..7e3cdfe 100644
--- a/ruoyi-modules/ruoyi-wechat/pom.xml
+++ b/ruoyi-modules/ruoyi-wechat/pom.xml
@@ -23,6 +23,10 @@
             <groupId>org.ruoyi</groupId>
             <artifactId>ruoyi-chat-api</artifactId>
         </dependency>
+        <dependency>
+            <groupId>org.ruoyi</groupId>
+            <artifactId>ruoyi-system-api</artifactId>
+        </dependency>
     </dependencies>
 
 </project>
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
index 51077d6..0588447 100644
--- 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
@@ -3,12 +3,15 @@
 import lombok.Data;
 import lombok.Getter;
 import lombok.Setter;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+
 import java.util.List;
 
 /**
  * @author <a href="https://github.com/binarywang">Binary Wang</a>
  */
 @Data
+@ConfigurationProperties(prefix = "wechat.cp")
 public class WxCpProperties {
   /**
    * 璁剧疆浼佷笟寰俊鐨刢orpId
diff --git a/ruoyi-modules/ruoyi-wechat/src/main/java/org/ruoyi/controller/WeixinServerController.java b/ruoyi-modules/ruoyi-wechat/src/main/java/org/ruoyi/controller/WeixinServerController.java
new file mode 100644
index 0000000..86767af
--- /dev/null
+++ b/ruoyi-modules/ruoyi-wechat/src/main/java/org/ruoyi/controller/WeixinServerController.java
@@ -0,0 +1,48 @@
+package org.ruoyi.controller;
+
+
+import jakarta.servlet.http.HttpServletRequest;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.ruoyi.service.WeixinUserService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+/**
+ * @author https://www.wdbyte.com
+ */
+@Slf4j
+@RestController
+public class WeixinServerController {
+
+    @Autowired
+    private WeixinUserService weixinUserService;
+
+    @GetMapping(value = "/weixin/check")
+    public String weixinCheck(HttpServletRequest request) {
+        String signature = request.getParameter("signature");
+        String timestamp = request.getParameter("timestamp");
+        String nonce = request.getParameter("nonce");
+        String echostr = request.getParameter("echostr");
+
+        if (StringUtils.isEmpty(signature) || StringUtils.isEmpty(timestamp) || StringUtils.isEmpty(nonce)) {
+            return "";
+        }
+        weixinUserService.checkSignature(signature, timestamp, nonce);
+        return echostr;
+    }
+
+    @PostMapping(value = "/weixin/check")
+    public String weixinMsg(@RequestBody String requestBody, @RequestParam("signature") String signature,
+        @RequestParam("timestamp") String timestamp, @RequestParam("nonce") String nonce) {
+
+        log.debug("requestBody:{}", requestBody);
+        log.debug("signature:{}", signature);
+        log.debug("timestamp:{}", timestamp);
+        log.debug("nonce:{}", nonce);
+
+        weixinUserService.checkSignature(signature, timestamp, nonce);
+        return weixinUserService.handleWeixinMsg(requestBody);
+    }
+
+}
diff --git a/ruoyi-modules/ruoyi-wechat/src/main/java/org/ruoyi/controller/WeixinUserController.java b/ruoyi-modules/ruoyi-wechat/src/main/java/org/ruoyi/controller/WeixinUserController.java
new file mode 100644
index 0000000..777c180
--- /dev/null
+++ b/ruoyi-modules/ruoyi-wechat/src/main/java/org/ruoyi/controller/WeixinUserController.java
@@ -0,0 +1,52 @@
+package org.ruoyi.controller;
+
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.ruoyi.common.core.domain.R;
+import org.ruoyi.domin.WeixinQrCode;
+import org.ruoyi.service.VxLoginService;
+import org.ruoyi.system.domain.vo.LoginVo;
+import org.ruoyi.util.WeixinApiUtil;
+import org.ruoyi.util.WeixinQrCodeCacheUtil;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ * @author https://www.wdbyte.com
+ */
+@Slf4j
+@RestController
+public class WeixinUserController {
+
+    @Autowired
+    private WeixinApiUtil weixinApiUtil;
+
+    @Autowired
+    private VxLoginService loginService;
+
+    @GetMapping(value = "/user/qrcode")
+    public R<WeixinQrCode> getQrCode() {
+        WeixinQrCode qrCode = weixinApiUtil.getQrCode();
+        qrCode.setUrl(null);
+        qrCode.setExpireSeconds(null);
+        return R.ok(qrCode);
+    }
+
+    /**
+     * 鏍¢獙鏄惁鎵弿瀹屾垚
+     * 瀹屾垚锛岃繑鍥� JWT
+     * 鏈畬鎴愶紝杩斿洖 check failed
+     */
+    @GetMapping(value = "/user/login/qrcode")
+    public R<LoginVo> userLogin(String ticket) {
+        String openId = WeixinQrCodeCacheUtil.get(ticket);
+        if (StringUtils.isNotEmpty(openId)) {
+            log.info("login success,open id:{}", openId);
+            LoginVo loginVo = loginService.mpLogin(openId);
+            return R.ok(loginVo);
+        }
+        log.info("login error,ticket:{}", ticket);
+        return R.fail("check failed");
+    }
+}
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/WxPortalController.java
similarity index 98%
rename from ruoyi-modules/ruoyi-wechat/src/main/java/org/ruoyi/controller/wxcplogin/WxPortalController.java
rename to ruoyi-modules/ruoyi-wechat/src/main/java/org/ruoyi/controller/WxPortalController.java
index 2857bc2..aba5896 100644
--- 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/WxPortalController.java
@@ -1,4 +1,4 @@
-package org.ruoyi.controller.wxcplogin;
+package org.ruoyi.controller;
 
 import lombok.extern.slf4j.Slf4j;
 import me.chanjar.weixin.cp.api.WxCpService;
diff --git a/ruoyi-modules/ruoyi-wechat/src/main/java/org/ruoyi/domin/ReceiveMessage.java b/ruoyi-modules/ruoyi-wechat/src/main/java/org/ruoyi/domin/ReceiveMessage.java
new file mode 100644
index 0000000..9bc6dcb
--- /dev/null
+++ b/ruoyi-modules/ruoyi-wechat/src/main/java/org/ruoyi/domin/ReceiveMessage.java
@@ -0,0 +1,58 @@
+package org.ruoyi.domin;
+
+import lombok.Data;
+
+@Data
+public class ReceiveMessage {
+    /**
+     * 寮�鍙戣�呭井淇″彿
+     */
+    private String toUserName;
+    /**
+     * 鍙戦�佹柟璐﹀彿(涓�涓猳penid锛�
+     */
+    private String fromUserName;
+    /**
+     * 娑堟伅鍒涘缓鏃堕棿锛堟暣褰級
+     */
+    private String createTime;
+    /**
+     * 娑堟伅绫诲瀷
+     */
+    private String msgType;
+    /**
+     * 鏂囨湰娑堟伅鍐呭
+     */
+    private String content;
+    /**
+     * 娑堟伅ID 64浣�
+     */
+    String msgId;
+    /**
+     * 娑堟伅鐨勬暟鎹甀D 娑堟伅鏉ヨ嚜鏂囩珷鎵嶆湁
+     */
+    private String msgDataId;
+    /**
+     * 澶氬浘鏂囨椂绗嚑绡囨枃绔狅紝浠�1寮�濮� 娑堟伅濡傛灉鏉ヨ嚜鏂囩珷鎵嶆湁
+     */
+    private String idx;
+    /**
+     * 璁㈤槄浜嬩欢 subscribe 璁㈤槄 unsbscribe 鍙栨秷璁㈤槄
+     */
+    private String event;
+    /**
+     * 鎵爜 - ticket
+     */
+    private String ticket;
+
+    public String getReplyTextMsg(String msg) {
+        String xml = "<xml>\n"
+            + "       <ToUserName><![CDATA[" + getFromUserName() + "]]></ToUserName>\n"
+            + "       <FromUserName><![CDATA[" + getToUserName() + "]]></FromUserName>\n"
+            + "       <CreateTime>" + System.currentTimeMillis() + "</CreateTime>\n"
+            + "       <MsgType><![CDATA[text]]></MsgType>\n"
+            + "       <Content><![CDATA[" + msg + "]]></Content>\n"
+            + "     </xml>";
+        return xml;
+    }
+}
diff --git a/ruoyi-modules/ruoyi-wechat/src/main/java/org/ruoyi/domin/WeixinQrCode.java b/ruoyi-modules/ruoyi-wechat/src/main/java/org/ruoyi/domin/WeixinQrCode.java
new file mode 100644
index 0000000..0363b3b
--- /dev/null
+++ b/ruoyi-modules/ruoyi-wechat/src/main/java/org/ruoyi/domin/WeixinQrCode.java
@@ -0,0 +1,15 @@
+package org.ruoyi.domin;
+
+import lombok.Data;
+
+/**
+ * @author https://www.wdbyte.com
+ */
+@Data
+public class WeixinQrCode {
+
+    private String ticket;
+    private Long expireSeconds;
+    private String url;
+    private String qrCodeUrl;
+}
diff --git a/ruoyi-modules/ruoyi-wechat/src/main/java/org/ruoyi/service/VxLoginService.java b/ruoyi-modules/ruoyi-wechat/src/main/java/org/ruoyi/service/VxLoginService.java
new file mode 100644
index 0000000..31e6876
--- /dev/null
+++ b/ruoyi-modules/ruoyi-wechat/src/main/java/org/ruoyi/service/VxLoginService.java
@@ -0,0 +1,108 @@
+package org.ruoyi.service;
+
+import cn.dev33.satoken.secure.BCrypt;
+import cn.dev33.satoken.stp.StpUtil;
+import cn.hutool.core.util.ObjectUtil;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.math.NumberUtils;
+import org.ruoyi.common.core.constant.Constants;
+import org.ruoyi.common.core.domain.model.VisitorLoginUser;
+import org.ruoyi.common.core.enums.DeviceType;
+import org.ruoyi.common.core.enums.UserType;
+import org.ruoyi.common.core.service.ConfigService;
+import org.ruoyi.common.core.utils.MessageUtils;
+import org.ruoyi.common.core.utils.ServletUtils;
+import org.ruoyi.common.core.utils.SpringUtils;
+import org.ruoyi.common.log.event.LogininforEvent;
+import org.ruoyi.common.satoken.utils.LoginHelper;
+import org.ruoyi.system.domain.SysUser;
+import org.ruoyi.system.domain.bo.SysUserBo;
+import org.ruoyi.system.domain.vo.LoginVo;
+import org.ruoyi.system.domain.vo.SysUserVo;
+import org.ruoyi.system.service.ISysUserService;
+import org.springframework.stereotype.Service;
+
+import java.util.UUID;
+
+/**
+ * 鎻忚堪锛氬井淇″叕浼楀彿鐧诲綍
+ *
+ * @author ageerle@163.com
+ * date 2025/4/30
+ */
+@Slf4j
+@Service
+@RequiredArgsConstructor
+public class VxLoginService {
+
+    private final ISysUserService userService;
+
+    private final ConfigService configService;
+
+    public LoginVo mpLogin(String openid) {
+        // 浣跨敤 openid 鏌ヨ缁戝畾鐢ㄦ埛 濡傛湭缁戝畾鐢ㄦ埛 鍒欐牴鎹笟鍔¤嚜琛屽鐞� 渚嬪 鍒涘缓榛樿鐢ㄦ埛
+        SysUserVo user = userService.selectUserByOpenId(openid);
+        VisitorLoginUser loginUser = new VisitorLoginUser();
+        if (ObjectUtil.isNull(user)) {
+            SysUserBo sysUser = new SysUserBo();
+            String name = "鐢ㄦ埛" + UUID.randomUUID().toString().replace("-", "");
+            // 璁剧疆榛樿鐢ㄦ埛鍚�
+            sysUser.setUserName(name);
+            // 璁剧疆榛樿鏄电О
+            sysUser.setNickName(name);
+            // 璁剧疆榛樿瀵嗙爜
+            sysUser.setPassword(BCrypt.hashpw("123456"));
+            // 璁剧疆寰俊openId
+            sysUser.setOpenId(openid);
+            String configValue = configService.getConfigValue("mail", "amount");
+            // 璁剧疆榛樿浣欓
+            sysUser.setUserBalance(NumberUtils.toDouble(configValue, 1));
+            // 娉ㄥ唽鐢ㄦ埛,璁剧疆榛樿绉熸埛涓�0
+            SysUser registerUser = userService.registerUser(sysUser, "0");
+
+            // 鏋勫缓鐧诲綍鐢ㄦ埛淇℃伅
+            loginUser.setTenantId("0");
+            loginUser.setUserId(registerUser.getUserId());
+            loginUser.setUsername(registerUser.getUserName());
+            loginUser.setUserType(UserType.APP_USER.getUserType());
+            loginUser.setOpenid(openid);
+            loginUser.setNickName(registerUser.getNickName());
+        } else {
+            // 姝ゅ鍙牴鎹櫥褰曠敤鎴风殑鏁版嵁涓嶅悓 鑷鍒涘缓 loginUser
+            loginUser.setTenantId(user.getTenantId());
+            loginUser.setUserId(user.getUserId());
+            loginUser.setUsername(user.getUserName());
+            loginUser.setUserType(user.getUserType());
+            loginUser.setNickName(user.getNickName());
+            loginUser.setAvatar(user.getWxAvatar());
+            loginUser.setOpenid(openid);
+        }
+        // 鐢熸垚token
+        LoginHelper.loginByDevice(loginUser, DeviceType.XCX);
+        recordLogininfor(loginUser.getTenantId(), loginUser.getUsername(), Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success"));
+        LoginVo loginVo = new LoginVo();
+        // 鐢熸垚浠ょ墝
+        loginVo.setToken(StpUtil.getTokenValue());
+        loginVo.setUserInfo(loginUser);
+        return loginVo;
+    }
+
+    /**
+     * 璁板綍鐧诲綍淇℃伅
+     *
+     * @param tenantId 绉熸埛ID
+     * @param username 鐢ㄦ埛鍚�
+     * @param status   鐘舵��
+     * @param message  娑堟伅鍐呭
+     */
+    private void recordLogininfor(String tenantId, String username, String status, String message) {
+        LogininforEvent logininforEvent = new LogininforEvent();
+        logininforEvent.setTenantId(tenantId);
+        logininforEvent.setUsername(username);
+        logininforEvent.setStatus(status);
+        logininforEvent.setMessage(message);
+        logininforEvent.setRequest(ServletUtils.getRequest());
+        SpringUtils.context().publishEvent(logininforEvent);
+    }
+}
diff --git a/ruoyi-modules/ruoyi-wechat/src/main/java/org/ruoyi/service/WeixinUserService.java b/ruoyi-modules/ruoyi-wechat/src/main/java/org/ruoyi/service/WeixinUserService.java
new file mode 100644
index 0000000..c999679
--- /dev/null
+++ b/ruoyi-modules/ruoyi-wechat/src/main/java/org/ruoyi/service/WeixinUserService.java
@@ -0,0 +1,10 @@
+package org.ruoyi.service;
+
+
+public interface WeixinUserService {
+
+    void checkSignature(String signature, String timestamp, String nonce);
+
+    String handleWeixinMsg(String body);
+
+}
diff --git a/ruoyi-modules/ruoyi-wechat/src/main/java/org/ruoyi/service/impl/WeixinUserServiceImpl.java b/ruoyi-modules/ruoyi-wechat/src/main/java/org/ruoyi/service/impl/WeixinUserServiceImpl.java
new file mode 100644
index 0000000..57fcdfb
--- /dev/null
+++ b/ruoyi-modules/ruoyi-wechat/src/main/java/org/ruoyi/service/impl/WeixinUserServiceImpl.java
@@ -0,0 +1,65 @@
+package org.ruoyi.service.impl;
+
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.codec.digest.DigestUtils;
+import org.ruoyi.domin.ReceiveMessage;
+import org.ruoyi.service.WeixinUserService;
+import org.ruoyi.util.WeixinMsgUtil;
+import org.ruoyi.util.WeixinQrCodeCacheUtil;
+import org.springframework.stereotype.Service;
+
+import java.util.Arrays;
+
+
+@Slf4j
+@Service
+public class WeixinUserServiceImpl implements WeixinUserService {
+
+    private String token = "panda";
+
+    @Override
+    public void checkSignature(String signature, String timestamp, String nonce) {
+        String[] arr = new String[] {token, timestamp, nonce};
+        Arrays.sort(arr);
+        StringBuilder content = new StringBuilder();
+        for (String str : arr) {
+            content.append(str);
+        }
+        String tmpStr = DigestUtils.sha1Hex(content.toString());
+        if (tmpStr.equals(signature)) {
+            log.info("check success");
+            return;
+        }
+        log.error("check fail");
+        throw new RuntimeException("check fail");
+    }
+
+    @Override
+    public String handleWeixinMsg(String requestBody) {
+        ReceiveMessage receiveMessage = WeixinMsgUtil.msgToReceiveMessage(requestBody);
+        // 鎵爜鐧诲綍
+        if (WeixinMsgUtil.isScanQrCode(receiveMessage)) {
+            return handleScanLogin(receiveMessage);
+        }
+        // 鍏虫敞
+        if (WeixinMsgUtil.isEventAndSubscribe(receiveMessage)) {
+            return receiveMessage.getReplyTextMsg("鎰熻阿鎮ㄧ殑鍏虫敞锛�");
+        }
+        return receiveMessage.getReplyTextMsg("鏀跺埌锛堣嚜鍔ㄥ洖澶嶏級");
+    }
+
+    /**
+     * 澶勭悊鎵爜鐧诲綍
+     *
+     * @param receiveMessage
+     * @return
+     */
+    private String handleScanLogin(ReceiveMessage receiveMessage) {
+        String qrCodeTicket = WeixinMsgUtil.getQrCodeTicket(receiveMessage);
+        if (WeixinQrCodeCacheUtil.get(qrCodeTicket) == null) {
+            String openId = receiveMessage.getFromUserName();
+            WeixinQrCodeCacheUtil.put(qrCodeTicket, openId);
+        }
+        return receiveMessage.getReplyTextMsg("浣犲凡鎴愬姛鐧诲綍锛�");
+    }
+}
diff --git a/ruoyi-modules/ruoyi-wechat/src/main/java/org/ruoyi/util/KeyUtils.java b/ruoyi-modules/ruoyi-wechat/src/main/java/org/ruoyi/util/KeyUtils.java
new file mode 100644
index 0000000..6bbf917
--- /dev/null
+++ b/ruoyi-modules/ruoyi-wechat/src/main/java/org/ruoyi/util/KeyUtils.java
@@ -0,0 +1,24 @@
+package org.ruoyi.util;
+
+import org.apache.commons.lang3.RandomStringUtils;
+
+import java.util.UUID;
+
+/**
+ * @author https://www.wdbyte.com
+ */
+public class KeyUtils {
+
+    public synchronized static String key6() {
+        return RandomStringUtils.randomAlphanumeric(6);
+    }
+
+    public synchronized static String key16() {
+        return RandomStringUtils.randomAlphanumeric(16);
+    }
+
+    public static String uuid32() {
+        return UUID.randomUUID().toString().replace("-", "");
+    }
+
+}
diff --git a/ruoyi-modules/ruoyi-wechat/src/main/java/org/ruoyi/util/WeixinApiUtil.java b/ruoyi-modules/ruoyi-wechat/src/main/java/org/ruoyi/util/WeixinApiUtil.java
new file mode 100644
index 0000000..1aa9c39
--- /dev/null
+++ b/ruoyi-modules/ruoyi-wechat/src/main/java/org/ruoyi/util/WeixinApiUtil.java
@@ -0,0 +1,81 @@
+package org.ruoyi.util;
+
+import cn.hutool.http.HttpUtil;
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONObject;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.ruoyi.common.core.service.ConfigService;
+import org.ruoyi.domin.WeixinQrCode;
+import org.springframework.stereotype.Component;
+
+import java.net.URI;
+import java.time.LocalDateTime;
+
+/**
+ * @author https://www.wdbyte.com
+ */
+@Slf4j
+@Component
+@RequiredArgsConstructor
+public class WeixinApiUtil {
+
+    private final ConfigService configService;
+
+    private static String QR_CODE_URL_PREFIX = "https://mp.weixin.qq.com/cgi-bin/showqrcode?ticket=";
+
+    private static String ACCESS_TOKEN = null;
+    private static LocalDateTime ACCESS_TOKEN_EXPIRE_TIME = null;
+    /**
+     * 浜岀淮鐮� Ticket 杩囨湡鏃堕棿
+     */
+    private static int QR_CODE_TICKET_TIMEOUT = 10 * 60;
+
+    /**
+     * 鑾峰彇 access token
+     *
+     * @return
+     */
+    public synchronized String getAccessToken() {
+        if (ACCESS_TOKEN != null && ACCESS_TOKEN_EXPIRE_TIME.isAfter(LocalDateTime.now())) {
+            return ACCESS_TOKEN;
+        }
+        String api = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=" + getKey("appid") + "&secret="
+            + getKey("secret");
+        String result = HttpUtil.get(api);
+        JSONObject jsonObject = JSON.parseObject(result);
+        ACCESS_TOKEN = jsonObject.getString("access_token");
+        ACCESS_TOKEN_EXPIRE_TIME = LocalDateTime.now().plusSeconds(jsonObject.getLong("expires_in") - 10);
+        return ACCESS_TOKEN;
+    }
+
+    /**
+     * 鑾峰彇浜岀淮鐮� Ticket
+     *
+     * https://developers.weixin.qq.com/doc/offiaccount/Account_Management/Generating_a_Parametric_QR_Code.html
+     *
+     * @return
+     */
+    public WeixinQrCode getQrCode() {
+        String api = "https://api.weixin.qq.com/cgi-bin/qrcode/create?access_token=" + getAccessToken();
+        String jsonBody = String.format("{\n"
+            + "  \"expire_seconds\": %d,\n"
+            + "  \"action_name\": \"QR_STR_SCENE\",\n"
+            + "  \"action_info\": {\n"
+            + "    \"scene\": {\n"
+            + "      \"scene_str\": \"%s\"\n"
+            + "    }\n"
+            + "  }\n"
+            + "}", QR_CODE_TICKET_TIMEOUT, KeyUtils.uuid32());
+        String result = HttpUtil.post(api, jsonBody);
+        log.info("get qr code params:{}", jsonBody);
+        log.info("get qr code result:{}", result);
+        WeixinQrCode weixinQrCode = JSON.parseObject(result, WeixinQrCode.class);
+        weixinQrCode.setQrCodeUrl(QR_CODE_URL_PREFIX + URI.create(weixinQrCode.getTicket()).toASCIIString());
+        return weixinQrCode;
+    }
+
+    public String getKey(String key) {
+        return configService.getConfigValue("weixin", key);
+    }
+}
diff --git a/ruoyi-modules/ruoyi-wechat/src/main/java/org/ruoyi/util/WeixinMsgUtil.java b/ruoyi-modules/ruoyi-wechat/src/main/java/org/ruoyi/util/WeixinMsgUtil.java
new file mode 100644
index 0000000..e1fd520
--- /dev/null
+++ b/ruoyi-modules/ruoyi-wechat/src/main/java/org/ruoyi/util/WeixinMsgUtil.java
@@ -0,0 +1,66 @@
+package org.ruoyi.util;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONObject;
+import org.apache.commons.lang3.StringUtils;
+import org.ruoyi.domin.ReceiveMessage;
+
+/**
+ * @author https://www.wdbyte.com
+ */
+public class WeixinMsgUtil {
+
+    // 浜嬩欢-鍏虫敞
+    private static String EVENT_SUBSCRIBE = "subscribe";
+
+    /**
+     * 寰俊娑堟伅杞璞�
+     *
+     * @param xml
+     * @return
+     */
+    public static ReceiveMessage msgToReceiveMessage(String xml) {
+        JSONObject jsonObject = JSON.parseObject(XmlUtil.xml2json(xml));
+        ReceiveMessage receiveMessage = new ReceiveMessage();
+        receiveMessage.setToUserName(jsonObject.getString("ToUserName"));
+        receiveMessage.setFromUserName(jsonObject.getString("FromUserName"));
+        receiveMessage.setCreateTime(jsonObject.getString("CreateTime"));
+        receiveMessage.setMsgType(jsonObject.getString("MsgType"));
+        receiveMessage.setContent(jsonObject.getString("Content"));
+        receiveMessage.setMsgId(jsonObject.getString("MsgId"));
+        receiveMessage.setEvent(jsonObject.getString("Event"));
+        receiveMessage.setTicket(jsonObject.getString("Ticket"));
+        return receiveMessage;
+    }
+
+    /**
+     * 鏄惁鏄闃呬簨浠�
+     *
+     * @param receiveMessage
+     * @return
+     */
+    public static boolean isEventAndSubscribe(ReceiveMessage receiveMessage) {
+        return StringUtils.equals(receiveMessage.getEvent(), EVENT_SUBSCRIBE);
+    }
+
+    /**
+     * 鏄惁鏄簩缁寸爜鎵弿浜嬩欢
+     *
+     * @param receiveMessage
+     * @return
+     */
+    public static boolean isScanQrCode(ReceiveMessage receiveMessage) {
+        return StringUtils.isNotEmpty(receiveMessage.getTicket());
+    }
+
+    /**
+     * 鑾峰彇鎵弿鐨勪簩缁寸爜 Ticket
+     *
+     * @param receiveMessage
+     * @return
+     */
+    public static String getQrCodeTicket(ReceiveMessage receiveMessage) {
+        return receiveMessage.getTicket();
+    }
+
+}
diff --git a/ruoyi-modules/ruoyi-wechat/src/main/java/org/ruoyi/util/WeixinQrCodeCacheUtil.java b/ruoyi-modules/ruoyi-wechat/src/main/java/org/ruoyi/util/WeixinQrCodeCacheUtil.java
new file mode 100644
index 0000000..aaebd1e
--- /dev/null
+++ b/ruoyi-modules/ruoyi-wechat/src/main/java/org/ruoyi/util/WeixinQrCodeCacheUtil.java
@@ -0,0 +1,34 @@
+package org.ruoyi.util;
+
+import java.util.LinkedHashMap;
+
+/**
+ * 寰俊浜岀淮鐮佺紦瀛樺伐鍏风被
+ *
+ * @author https://www.wdbyte.com
+ */
+public class WeixinQrCodeCacheUtil {
+    private static long MAX_CACHE_SIZE = 10000;
+    private static LinkedHashMap<String, String> QR_CODE_TICKET_MAP = new LinkedHashMap<>();
+
+    /**
+     * 澧炲姞涓�涓� Ticket
+     * 棣栨 put锛歷alue 涓� ""
+     * 鍐嶆 put: value 鏈� openId锛岃嫢openId宸茬粡瀛樺湪锛屽垯宸茶鎵爜
+     *
+     * @param key
+     * @param value
+     */
+    public synchronized static void put(String key, String value) {
+        QR_CODE_TICKET_MAP.put(key, value);
+        if (QR_CODE_TICKET_MAP.size() > MAX_CACHE_SIZE) {
+            String first = QR_CODE_TICKET_MAP.keySet().stream().findFirst().get();
+            QR_CODE_TICKET_MAP.remove(first);
+        }
+    }
+
+    public synchronized static String get(String key) {
+        return QR_CODE_TICKET_MAP.remove(key);
+    }
+
+}
diff --git a/ruoyi-modules/ruoyi-wechat/src/main/java/org/ruoyi/util/XmlUtil.java b/ruoyi-modules/ruoyi-wechat/src/main/java/org/ruoyi/util/XmlUtil.java
new file mode 100644
index 0000000..d6e58f6
--- /dev/null
+++ b/ruoyi-modules/ruoyi-wechat/src/main/java/org/ruoyi/util/XmlUtil.java
@@ -0,0 +1,28 @@
+package org.ruoyi.util;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.dataformat.xml.XmlMapper;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+
+/**
+ * @author https://www.wdbyte.com
+ */
+@Slf4j
+public class XmlUtil {
+
+    public static String xml2json(String requestBody) {
+        requestBody = StringUtils.trim(requestBody);
+        XmlMapper xmlMapper = new XmlMapper();
+        JsonNode node = null;
+        try {
+            node = xmlMapper.readTree(requestBody.getBytes());
+            ObjectMapper jsonMapper = new ObjectMapper();
+            return jsonMapper.writeValueAsString(node);
+        } catch (Exception e) {
+            log.error("xml 2 json error,msg:" + e.getMessage(), e);
+        }
+        return null;
+    }
+}

--
Gitblit v1.9.3