Переглянути джерело

Merge branch 'system-zjy' of uskycloud/usky-cloud into system-165

hanzhengyi 1 день тому
батько
коміт
33c725a616
14 змінених файлів з 164 додано та 27 видалено
  1. 7 0
      base-modules/service-system/service-system-api/src/main/java/com/usky/system/domain/SysOperLogVO.java
  2. 0 6
      base-modules/service-system/service-system-biz/pom.xml
  3. 10 1
      base-modules/service-system/service-system-biz/src/main/java/com/usky/system/service/impl/MceContentServiceImpl.java
  4. 20 7
      base-modules/service-system/service-system-biz/src/main/java/com/usky/system/service/impl/MceReceiveServiceImpl.java
  5. 2 0
      base-modules/service-system/service-system-biz/src/main/java/com/usky/system/service/impl/SysUserOnlineServiceImpl.java
  6. 88 5
      base-modules/service-system/service-system-biz/src/main/java/com/usky/system/service/util/AsyncFactory.java
  7. 0 2
      base-modules/service-system/service-system-biz/src/main/java/com/usky/system/service/util/AsyncManager.java
  8. 4 0
      usky-common/usky-common-core/src/main/java/com/usky/common/core/constants/SecurityConstants.java
  9. 9 0
      usky-common/usky-common-log/pom.xml
  10. 3 5
      usky-common/usky-common-log/src/main/java/com/usky/common/log/aspect/AddressUtils.java
  11. 3 0
      usky-common/usky-common-log/src/main/java/com/usky/common/log/aspect/LogAspect.java
  12. 1 1
      usky-common/usky-common-log/src/main/java/com/usky/common/log/service/WjConfig.java
  13. 6 0
      usky-common/usky-common-security/pom.xml
  14. 11 0
      usky-common/usky-common-security/src/main/java/com/usky/common/security/service/TokenService.java

+ 7 - 0
base-modules/service-system/service-system-api/src/main/java/com/usky/system/domain/SysOperLogVO.java

@@ -47,6 +47,9 @@ public class SysOperLogVO extends BaseEntity
     /** 操作地址 */
     private String operIp;
 
+    /** 操作地点 */
+    private String operLocation;
+
     /** 请求参数 */
     private String operParam;
 
@@ -195,6 +198,10 @@ public class SysOperLogVO extends BaseEntity
         this.operIp = operIp;
     }
 
+    public String getOperLocation() { return operLocation; }
+
+    public void setOperLocation(String operLocation) { this.operLocation = operLocation; }
+
     public String getOperParam()
     {
         return operParam;

+ 0 - 6
base-modules/service-system/service-system-biz/pom.xml

@@ -59,12 +59,6 @@
             <artifactId>hutool-all</artifactId>
         </dependency>
 
-        <dependency>
-            <groupId>org.json</groupId>
-            <artifactId>json</artifactId>
-            <version>20210307</version>
-        </dependency>
-
         <!-- 监控服务器资源状态 -->
         <dependency>
             <groupId>com.github.oshi</groupId>

+ 10 - 1
base-modules/service-system/service-system-biz/src/main/java/com/usky/system/service/impl/MceContentServiceImpl.java

@@ -73,7 +73,7 @@ public class MceContentServiceImpl extends AbstractCrudService<MceContentMapper,
             jsonObject1.addProperty("moduleId", mceReceiveVO.getId());
             jsonObject.addProperty("cids", cids);
             jsonObject.addProperty("title", mceReceiveVO.getInfoTitle());
-            if ("3".equals(mceReceiveVO.getInfoType())){
+            if ("3".equals(mceReceiveVO.getInfoType())) {
                 jsonObject.addProperty("content", mceReceiveVO.getInfoContent().split("-")[0]);
             }
             jsonObject.addProperty("content", mceReceiveVO.getInfoContent());
@@ -116,6 +116,15 @@ public class MceContentServiceImpl extends AbstractCrudService<MceContentMapper,
             if (StringUtils.isNotEmpty(mceReceiveVO.getOaType())) {
                 sendWeChatMessageRequestVO.setOaType(mceReceiveVO.getOaType());
             }
+            if (StringUtils.isNotEmpty(mceReceiveVO.getDeviceId())) {
+                sendWeChatMessageRequestVO.setDeviceId(mceReceiveVO.getDeviceId());
+            }
+            if (StringUtils.isNotEmpty(mceReceiveVO.getAlarmTime())) {
+                sendWeChatMessageRequestVO.setAlarmTime(mceReceiveVO.getAlarmTime());
+            }
+            if (StringUtils.isNotEmpty(mceReceiveVO.getRemark())) {
+                sendWeChatMessageRequestVO.setRemark(mceReceiveVO.getRemark());
+            }
             mceMbuserService.sendWeChatMessage(sendWeChatMessageRequestVO);
         }
     }

+ 20 - 7
base-modules/service-system/service-system-biz/src/main/java/com/usky/system/service/impl/MceReceiveServiceImpl.java

@@ -350,6 +350,18 @@ public class MceReceiveServiceImpl extends AbstractCrudService<MceReceiveMapper,
                 continue;
             }
 
+            // 获取用户消息配置
+            MceMbuser mbUser = userIdToMbUserMap.get(userId);
+            if (mbUser == null) {
+                continue;
+            }
+
+            // 登录不需要新增消息记录
+            if ("0".equals(mceRequestVO.getInfoType())) {
+                handleMessageDelivery(mbUser, mceRequestVO, 0, appMode, wcMode);
+                return;
+            }
+
             // 创建消息接收记录
             MceReceive mceReceive = createMceReceive(user, contentId, mceRequestVO, userNameToNickNameMap);
             boolean saveResult = this.save(mceReceive);
@@ -358,12 +370,6 @@ public class MceReceiveServiceImpl extends AbstractCrudService<MceReceiveMapper,
                 continue;
             }
 
-            // 获取用户消息配置
-            MceMbuser mbUser = userIdToMbUserMap.get(userId);
-            if (mbUser == null) {
-                continue;
-            }
-
             // 根据消息类型处理消息发送
             handleMessageDelivery(mbUser, mceRequestVO, mceReceive.getId(), appMode, wcMode);
         }
@@ -444,6 +450,8 @@ public class MceReceiveServiceImpl extends AbstractCrudService<MceReceiveMapper,
             Integer isLoginNotify = one.getIsLoginNotify();
             if (isLoginNotify == 1) {
                 sendWeChatMessage(mbUser, mceRequestVO, wcMode, infoType, mceReceiveId);
+            } else {
+                log.info("用户{}未开启登录通知", mbUser.getUserId());
             }
             return;
         }
@@ -504,7 +512,12 @@ public class MceReceiveServiceImpl extends AbstractCrudService<MceReceiveMapper,
      */
     private void sendWeChatMessage(MceMbuser mbUser, MceRequestVO mceRequestVO, JSONObject mode,
                                    String infoType, Integer mceReceiveId) {
-        if (mode.getBoolean(infoType) && mbUser.getOpenid() != null) {
+
+        if (mbUser.getOpenid() == null) {
+            return;
+        }
+
+        if ("0".equals(infoType) || mode.getBoolean(infoType)) {
             try {
                 mceContentService.sendAppNew(mceRequestVO, mbUser.getOpenid(), mceReceiveId, 1);
             } catch (Exception e) {

+ 2 - 0
base-modules/service-system/service-system-biz/src/main/java/com/usky/system/service/impl/SysUserOnlineServiceImpl.java

@@ -90,6 +90,8 @@ public class SysUserOnlineServiceImpl implements ISysUserOnlineService
         sysUserOnline.setIpaddr(user.getIpaddr());
         sysUserOnline.setLoginTime(user.getLoginTime());
         sysUserOnline.setExpireTime(user.getExpireTime());
+        sysUserOnline.setOs(user.getOs());
+        sysUserOnline.setBrowser(user.getBrowser());
         return sysUserOnline;
     }
 }

+ 88 - 5
base-modules/service-system/service-system-biz/src/main/java/com/usky/system/service/util/AsyncFactory.java

@@ -3,24 +3,25 @@ package com.usky.system.service.util;
 
 import com.alibaba.fastjson.JSON;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.ruoyi.common.core.constant.CacheConstants;
 import com.ruoyi.common.core.constant.Constants;
-import com.ruoyi.common.core.utils.ServletUtils;
 import com.ruoyi.common.core.utils.StringUtils;
-import com.ruoyi.common.core.utils.ip.IpUtils;
 import com.usky.common.core.util.SpringContextUtils;
+import com.usky.common.log.aspect.AddressUtils;
+import com.usky.common.redis.core.RedisService;
 import com.usky.system.domain.MceRequestVO;
 import com.usky.system.domain.SysLogininfor;
 import com.usky.system.domain.SysUser;
 import com.usky.system.mapper.SysUserMapper;
+import com.usky.system.model.LoginUser;
 import com.usky.system.service.ISysLogininforService;
-import com.usky.system.service.ISysUserService;
 import com.usky.system.service.MceReceiveService;
 import eu.bitwalker.useragentutils.UserAgent;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import javax.servlet.http.HttpServletRequest;
-import java.util.Collections;
+import java.util.*;
 
 import static com.usky.common.core.utils.ip.IpUtils.getIpAddr;
 
@@ -80,9 +81,11 @@ public class AsyncFactory
         if (StringUtils.equalsAny(status, Constants.LOGIN_SUCCESS, Constants.LOGOUT, Constants.REGISTER)) {
             logininfor.setStatus(String.valueOf(Constants.LOGIN_SUCCESS_STATUS)); // 使用String.valueOf进行转换
 
-        // 如果登录成功,发送微信公众号消息
+        // 如果登录成功,发送微信公众号消息,删除超出规定历史token
             if (Constants.LOGIN_SUCCESS.equals(status)) {
                 sendWeChatMessage(logininfor);
+                // 保留用户相同OS和Browser的最新两条token,删除其他所有token
+                retainLatestToken(username);
             }
         } else if (Constants.LOGIN_FAIL.equals(status)) {
             logininfor.setStatus(String.valueOf(Constants.LOGIN_FAIL_STATUS)); // 使用String.valueOf进行转换
@@ -122,4 +125,84 @@ public class AsyncFactory
         mceRequestVO.setIpAddress(logininfor.getIpaddr());
         mceReceiveService.addMceReceive(mceRequestVO);
     }
+
+    /**
+     * 保留用户相同OS和Browser的最新两条token,删除其他历史token
+     *
+     * @param username 用户名
+     */
+    private static void retainLatestToken(String username) {
+        RedisService redisService = SpringContextUtils.getBean(RedisService.class);
+
+        try {
+            // 获取所有与该用户相关的token键
+            String pattern = CacheConstants.LOGIN_TOKEN_KEY + "*";
+            Collection<String> keys = redisService.keys(pattern);
+            if (keys == null || keys.isEmpty()) {
+                sys_user_logger.warn("Redis密钥为null或为空");
+                return;
+            }
+
+            // 存储该用户的token及其对应的LoginUser对象
+            Map<String, LoginUser> userTokens = new HashMap<>();
+
+            for (String key : keys) {
+                // 检查键是否存在并且未过期
+                if (redisService.hasKey(key)) {
+                    LoginUser user = redisService.getCacheObject(key);
+                    if (user != null && username.equals(user.getUsername())) {
+                        userTokens.put(key, user);
+                    }
+                }
+            }
+
+            if (userTokens.isEmpty()) {
+                sys_user_logger.warn("未找到用户 {} 的任何token", username);
+                return;
+            }
+
+            // 分组逻辑:根据OS和Browser分组
+            Map<String, List<Map.Entry<String, LoginUser>>> groupedTokens = new HashMap<>();
+            for (Map.Entry<String, LoginUser> entry : userTokens.entrySet()) {
+                LoginUser loginUser = entry.getValue();
+                String os = StringUtils.isEmpty(loginUser.getOs()) ? "未知" : loginUser.getOs();
+                String browser = StringUtils.isEmpty(loginUser.getBrowser()) ? "未知" : loginUser.getBrowser();
+                String osBrowserKey = os + "-" + browser;
+                groupedTokens.computeIfAbsent(osBrowserKey, k -> new ArrayList<>()).add(entry);
+            }
+
+            // 删除逻辑:保留最新的两条token,删除其他token
+            List<String> tokensToDelete = new ArrayList<>();
+            for (List<Map.Entry<String, LoginUser>> entryList : groupedTokens.values()) {
+                // 按照登录时间排序,保留最新的两条token
+                entryList.sort((e1, e2) ->
+                        e2.getValue().getLoginTime().compareTo(e1.getValue().getLoginTime())
+                );
+                for (int i = 3; i < entryList.size(); i++) {
+                    String keyToDelete = entryList.get(i).getKey();
+                    tokensToDelete.add(keyToDelete);
+                }
+            }
+
+            // 删除其他token
+            sys_user_logger.info("要删除的token列表: {}", tokensToDelete);
+            for (String keyToDelete : tokensToDelete) {
+                if (keyToDelete != null) {
+                    // 再次检查键是否存在
+                    if (redisService.hasKey(keyToDelete)) {
+                        boolean deleted = redisService.deleteObject(keyToDelete);
+                        sys_user_logger.info("Deleted Token Key: " + keyToDelete + ", Result: " + deleted);
+                    } else {
+                        sys_user_logger.warn("尝试删除的token键 {} 不存在,跳过删除操作", keyToDelete);
+                    }
+                } else {
+                    sys_user_logger.warn("尝试删除的token键为null,跳过删除操作");
+                }
+            }
+
+            sys_user_logger.info("用户 {} 的token已清理,仅保留最新的两条token: 已删除{}个", username, tokensToDelete.size());
+        } catch (Exception ex) {
+            sys_user_logger.error("清理用户 {} 的token时发生错误", username, ex);
+        }
+    }
 }

+ 0 - 2
base-modules/service-system/service-system-biz/src/main/java/com/usky/system/service/util/AsyncManager.java

@@ -3,8 +3,6 @@ package com.usky.system.service.util;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.stereotype.Component;
-import org.springframework.web.context.request.RequestContextHolder;
-import org.springframework.web.context.request.ServletRequestAttributes;
 
 import javax.annotation.PreDestroy;
 import javax.servlet.http.HttpServletRequest;

+ 4 - 0
usky-common/usky-common-core/src/main/java/com/usky/common/core/constants/SecurityConstants.java

@@ -43,4 +43,8 @@ public class SecurityConstants
     public static final String LOGIN_USER = "login_user";
 
     public static final String DETAILS_TENANT_ID = "";
+
+    public static final String DETAILS_OS =  "osInfo";
+
+    public static final String DETAILS_BROWSER = "browserInfo";
 }

+ 9 - 0
usky-common/usky-common-log/pom.xml

@@ -24,6 +24,15 @@
             <groupId>com.usky</groupId>
             <artifactId>usky-common-security</artifactId>
         </dependency>
+        <dependency>
+            <groupId>com.usky</groupId>
+            <artifactId>ruoyi-common-core</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.json</groupId>
+            <artifactId>json</artifactId>
+            <version>20210307</version>
+        </dependency>
 
     </dependencies>
 </project>

+ 3 - 5
base-modules/service-system/service-system-biz/src/main/java/com/usky/system/service/util/AddressUtils.java → usky-common/usky-common-log/src/main/java/com/usky/common/log/aspect/AddressUtils.java

@@ -1,14 +1,12 @@
-package com.usky.system.service.util;
+package com.usky.common.log.aspect;
 
 import cn.hutool.http.HttpUtil;
-import com.alibaba.nacos.shaded.com.google.common.base.Strings;
 import com.ruoyi.common.core.utils.ip.IpUtils;
-import com.usky.system.domain.WjConfig;
+import com.usky.common.log.service.WjConfig;
 import org.json.JSONException;
+import org.json.JSONObject;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
-import org.springframework.util.StringUtils;
-import org.json.JSONObject;
 
 
 /*

+ 3 - 0
usky-common/usky-common-log/src/main/java/com/usky/common/log/aspect/LogAspect.java

@@ -103,6 +103,9 @@ public class LogAspect {
             operLog.setConsumingTime(consumingTime); // 存储耗时
             operLog.setConsumingTimeWithUnit(consumingTimeWithUnit); // 存储带有单位的字符串
 
+            String operLocation = AddressUtils.getRealAddressByIP(ip);
+            operLog.setOperLocation(operLocation);
+
             // 保存数据库
             asyncLogService.saveSysLog(operLog);
         } catch (Exception exp) {

+ 1 - 1
base-modules/service-system/service-system-biz/src/main/java/com/usky/system/domain/WjConfig.java → usky-common/usky-common-log/src/main/java/com/usky/common/log/service/WjConfig.java

@@ -1,4 +1,4 @@
-package com.usky.system.domain;
+package com.usky.common.log.service;
 
 import lombok.Data;
 import org.springframework.boot.context.properties.ConfigurationProperties;

+ 6 - 0
usky-common/usky-common-security/pom.xml

@@ -36,6 +36,12 @@
             <groupId>com.usky</groupId>
             <artifactId>usky-common-redis</artifactId>
         </dependency>
+        <dependency>
+            <groupId>eu.bitwalker</groupId>
+            <artifactId>UserAgentUtils</artifactId>
+            <version>1.21</version>
+            <scope>compile</scope>
+        </dependency>
 
     </dependencies>
 

+ 11 - 0
usky-common/usky-common-security/src/main/java/com/usky/common/security/service/TokenService.java

@@ -6,6 +6,7 @@ import com.usky.common.core.util.*;
 import com.usky.common.redis.core.RedisHelper;
 import com.usky.common.security.utils.SecurityUtils;
 import com.usky.system.model.LoginUser;
+import eu.bitwalker.useragentutils.UserAgent;
 import org.springframework.cloud.commons.util.IdUtils;
 import org.springframework.stereotype.Component;
 
@@ -50,6 +51,14 @@ public class TokenService {
         loginUser.setUserid(userId);
         loginUser.setUsername(userName);
         loginUser.setIpaddr(IpUtils.getIpAddr(ServletUtils.getRequest()));
+        // 获取操作系统信息
+        String userAgentString = ServletUtils.getRequest().getHeader("User-Agent");
+        UserAgent userAgent = UserAgent.parseUserAgentString(userAgentString);
+        String osInfo = userAgent.getOperatingSystem().getName();
+        String browserInfo = userAgent.getBrowser().getName();
+        loginUser.setOs(osInfo);
+        loginUser.setBrowser(browserInfo);
+
         refreshToken(loginUser);
 
         // Jwt存储信息
@@ -58,6 +67,8 @@ public class TokenService {
         claimsMap.put(SecurityConstants.DETAILS_USER_ID, userId);
         claimsMap.put(SecurityConstants.DETAILS_USERNAME, userName);
         claimsMap.put(SecurityConstants.DETAILS_TENANT_ID, tenantId);
+        claimsMap.put(SecurityConstants.DETAILS_OS, osInfo);
+        claimsMap.put(SecurityConstants.DETAILS_BROWSER, browserInfo);
 
         // 接口返回信息
         Map<String, Object> rspMap = new HashMap<String, Object>();