Browse Source

Merge remote-tracking branch 'origin/dev' into 4.X

疯狂的狮子li 2 years ago
parent
commit
1a70cf658c
66 changed files with 673 additions and 593 deletions
  1. 1 1
      .run/ruoyi-monitor-admin.run.xml
  2. 1 1
      .run/ruoyi-server.run.xml
  3. 1 1
      .run/ruoyi-xxl-job-admin.run.xml
  4. 1 1
      README.md
  5. 49 62
      pom.xml
  6. 1 1
      ruoyi-admin/pom.xml
  7. 9 19
      ruoyi-admin/src/main/java/com/ruoyi/web/controller/common/CaptchaController.java
  8. 0 2
      ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysLoginController.java
  9. 6 0
      ruoyi-admin/src/main/resources/application.yml
  10. 1 1
      ruoyi-admin/src/main/resources/i18n/messages.properties
  11. 1 1
      ruoyi-admin/src/main/resources/i18n/messages_en_US.properties
  12. 1 1
      ruoyi-admin/src/main/resources/i18n/messages_zh_CN.properties
  13. 1 5
      ruoyi-common/pom.xml
  14. 0 1
      ruoyi-common/src/main/java/com/ruoyi/common/core/domain/dto/UserOnlineDTO.java
  15. 44 0
      ruoyi-common/src/main/java/com/ruoyi/common/core/domain/event/LogininforEvent.java
  16. 3 3
      ruoyi-common/src/main/java/com/ruoyi/common/core/domain/event/OperLogEvent.java
  17. 8 45
      ruoyi-common/src/main/java/com/ruoyi/common/core/mapper/BaseMapperPlus.java
  18. 0 14
      ruoyi-common/src/main/java/com/ruoyi/common/core/service/LogininforService.java
  19. 0 15
      ruoyi-common/src/main/java/com/ruoyi/common/core/service/OperLogService.java
  20. 1 1
      ruoyi-common/src/main/java/com/ruoyi/common/excel/CellMergeStrategy.java
  21. 3 3
      ruoyi-common/src/main/java/com/ruoyi/common/filter/XssHttpServletRequestWrapper.java
  22. 10 4
      ruoyi-common/src/main/java/com/ruoyi/common/helper/LoginHelper.java
  23. 113 51
      ruoyi-common/src/main/java/com/ruoyi/common/utils/poi/ExcelUtil.java
  24. 13 13
      ruoyi-common/src/main/java/com/ruoyi/common/utils/redis/RedisUtils.java
  25. 9 0
      ruoyi-common/src/main/java/com/ruoyi/common/utils/spring/SpringUtils.java
  26. 1 1
      ruoyi-demo/pom.xml
  27. 1 2
      ruoyi-demo/src/main/java/com/ruoyi/demo/controller/queue/PriorityDemo.java
  28. 1 1
      ruoyi-extend/pom.xml
  29. 1 1
      ruoyi-extend/ruoyi-monitor-admin/pom.xml
  30. 1 1
      ruoyi-extend/ruoyi-xxl-job-admin/pom.xml
  31. 1 1
      ruoyi-framework/pom.xml
  32. 6 7
      ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/LogAspect.java
  33. 1 1
      ruoyi-framework/src/main/java/com/ruoyi/framework/config/AsyncConfig.java
  34. 1 2
      ruoyi-framework/src/main/java/com/ruoyi/framework/config/RedisConfig.java
  35. 5 1
      ruoyi-framework/src/main/java/com/ruoyi/framework/config/SwaggerConfig.java
  36. 3 3
      ruoyi-framework/src/main/java/com/ruoyi/framework/handler/PlusDataPermissionHandler.java
  37. 1 1
      ruoyi-generator/pom.xml
  38. 1 1
      ruoyi-generator/src/main/java/com/ruoyi/generator/config/GenConfig.java
  39. 4 6
      ruoyi-generator/src/main/java/com/ruoyi/generator/service/GenTableServiceImpl.java
  40. 18 15
      ruoyi-generator/src/main/resources/vm/vue/index-tree.vue.vm
  41. 21 17
      ruoyi-generator/src/main/resources/vm/vue/index.vue.vm
  42. 23 20
      ruoyi-generator/src/main/resources/vm/vue/v3/index-tree.vue.vm
  43. 22 18
      ruoyi-generator/src/main/resources/vm/vue/v3/index.vue.vm
  44. 1 1
      ruoyi-job/pom.xml
  45. 1 1
      ruoyi-oss/pom.xml
  46. 1 1
      ruoyi-sms/pom.xml
  47. 1 1
      ruoyi-system/pom.xml
  48. 46 33
      ruoyi-system/src/main/java/com/ruoyi/system/service/SysLoginService.java
  49. 23 5
      ruoyi-system/src/main/java/com/ruoyi/system/service/SysRegisterService.java
  50. 4 0
      ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysConfigServiceImpl.java
  51. 15 17
      ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysLogininforServiceImpl.java
  52. 7 7
      ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysOperLogServiceImpl.java
  53. 20 14
      ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysOssConfigServiceImpl.java
  54. 2 2
      ruoyi-ui/package.json
  55. 4 0
      ruoyi-ui/src/assets/styles/ruoyi.scss
  56. 0 1
      ruoyi-ui/src/components/HeaderSearch/index.vue
  57. 0 6
      ruoyi-ui/src/components/RightPanel/index.vue
  58. 1 1
      ruoyi-ui/src/plugins/download.js
  59. 1 1
      ruoyi-ui/src/views/dashboard/BarChart.vue
  60. 1 1
      ruoyi-ui/src/views/dashboard/LineChart.vue
  61. 1 1
      ruoyi-ui/src/views/dashboard/PieChart.vue
  62. 1 1
      ruoyi-ui/src/views/dashboard/RaddarChart.vue
  63. 1 1
      ruoyi-ui/src/views/index.vue
  64. 146 146
      ruoyi-ui/src/views/monitor/cache/index.vue
  65. 1 1
      ruoyi-ui/src/views/register.vue
  66. 6 6
      script/docker/docker-compose.yml

+ 1 - 1
.run/ruoyi-monitor-admin.run.xml

@@ -2,7 +2,7 @@
   <configuration default="false" name="ruoyi-monitor-admin" type="docker-deploy" factoryName="dockerfile" server-name="Docker">
     <deployment type="dockerfile">
       <settings>
-        <option name="imageTag" value="ruoyi/ruoyi-monitor-admin:4.4.0" />
+        <option name="imageTag" value="ruoyi/ruoyi-monitor-admin:4.5.0" />
         <option name="buildOnly" value="true" />
         <option name="sourceFilePath" value="ruoyi-extend/ruoyi-monitor-admin/Dockerfile" />
       </settings>

+ 1 - 1
.run/ruoyi-server.run.xml

@@ -2,7 +2,7 @@
   <configuration default="false" name="ruoyi-server" type="docker-deploy" factoryName="dockerfile" server-name="Docker">
     <deployment type="dockerfile">
       <settings>
-        <option name="imageTag" value="ruoyi/ruoyi-server:4.4.0" />
+        <option name="imageTag" value="ruoyi/ruoyi-server:4.5.0" />
         <option name="buildOnly" value="true" />
         <option name="sourceFilePath" value="ruoyi-admin/Dockerfile" />
       </settings>

+ 1 - 1
.run/ruoyi-xxl-job-admin.run.xml

@@ -2,7 +2,7 @@
   <configuration default="false" name="ruoyi-xxl-job-admin" type="docker-deploy" factoryName="dockerfile" server-name="Docker">
     <deployment type="dockerfile">
       <settings>
-        <option name="imageTag" value="ruoyi/ruoyi-xxl-job-admin:4.4.0" />
+        <option name="imageTag" value="ruoyi/ruoyi-xxl-job-admin:4.5.0" />
         <option name="buildOnly" value="true" />
         <option name="sourceFilePath" value="ruoyi-extend/ruoyi-xxl-job-admin/Dockerfile" />
       </settings>

+ 1 - 1
README.md

@@ -4,7 +4,7 @@
 [![License](https://img.shields.io/badge/License-MIT-blue.svg)](https://gitee.com/JavaLionLi/RuoYi-Vue-Plus/blob/master/LICENSE)
 [![使用IntelliJ IDEA开发维护](https://img.shields.io/badge/IntelliJ%20IDEA-提供支持-blue.svg)](https://www.jetbrains.com/?from=RuoYi-Vue-Plus)
 <br>
-[![RuoYi-Vue-Plus](https://img.shields.io/badge/RuoYi_Vue_Plus-4.4.0-success.svg)](https://gitee.com/JavaLionLi/RuoYi-Vue-Plus)
+[![RuoYi-Vue-Plus](https://img.shields.io/badge/RuoYi_Vue_Plus-4.5.0-success.svg)](https://gitee.com/JavaLionLi/RuoYi-Vue-Plus)
 [![Spring Boot](https://img.shields.io/badge/Spring%20Boot-2.7-blue.svg)]()
 [![JDK-8+](https://img.shields.io/badge/JDK-8-green.svg)]()
 [![JDK-11](https://img.shields.io/badge/JDK-11-green.svg)]()

+ 49 - 62
pom.xml

@@ -6,49 +6,77 @@
 
     <groupId>com.ruoyi</groupId>
     <artifactId>ruoyi-vue-plus</artifactId>
-    <version>4.4.0</version>
+    <version>4.5.0</version>
 
     <name>RuoYi-Vue-Plus</name>
     <url>https://gitee.com/JavaLionLi/RuoYi-Vue-Plus</url>
     <description>RuoYi-Vue-Plus后台管理系统</description>
 
     <properties>
-        <ruoyi-vue-plus.version>4.4.0</ruoyi-vue-plus.version>
-        <spring-boot.version>2.7.6</spring-boot.version>
+        <ruoyi-vue-plus.version>4.5.0</ruoyi-vue-plus.version>
+        <spring-boot.version>2.7.7</spring-boot.version>
         <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
         <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
         <java.version>1.8</java.version>
         <maven-jar-plugin.version>3.2.2</maven-jar-plugin.version>
         <spring-boot.mybatis>2.2.2</spring-boot.mybatis>
-        <springdoc.version>1.6.13</springdoc.version>
+        <springdoc.version>1.6.14</springdoc.version>
         <poi.version>5.2.3</poi.version>
-        <easyexcel.version>3.1.3</easyexcel.version>
+        <easyexcel.version>3.1.5</easyexcel.version>
         <velocity.version>2.3</velocity.version>
-        <satoken.version>1.33.0</satoken.version>
-        <mybatis-plus.version>3.5.2</mybatis-plus.version>
+        <satoken.version>1.34.0</satoken.version>
+        <mybatis-plus.version>3.5.3.1</mybatis-plus.version>
         <p6spy.version>3.9.1</p6spy.version>
-        <hutool.version>5.8.10</hutool.version>
+        <hutool.version>5.8.11</hutool.version>
         <okhttp.version>4.10.0</okhttp.version>
-        <spring-boot-admin.version>2.7.7</spring-boot-admin.version>
-        <redisson.version>3.18.0</redisson.version>
+        <spring-boot-admin.version>2.7.10</spring-boot-admin.version>
+        <redisson.version>3.19.1</redisson.version>
         <lock4j.version>2.2.3</lock4j.version>
         <dynamic-ds.version>3.5.2</dynamic-ds.version>
         <alibaba-ttl.version>2.14.2</alibaba-ttl.version>
         <xxl-job.version>2.3.1</xxl-job.version>
         <lombok.version>1.18.24</lombok.version>
 
-        <!-- 统一 guava 版本 解决隐式漏洞问题 -->
-        <guava.version>31.1-jre</guava.version>
         <!-- 临时修复 snakeyaml 漏洞 -->
-        <snakeyaml.version>1.32</snakeyaml.version>
+        <snakeyaml.version>1.33</snakeyaml.version>
 
         <!-- OSS 配置 -->
-        <aws-java-sdk-s3.version>1.12.349</aws-java-sdk-s3.version>
+        <aws-java-sdk-s3.version>1.12.373</aws-java-sdk-s3.version>
         <!-- SMS 配置 -->
-        <aliyun.sms.version>2.0.22</aliyun.sms.version>
-        <tencent.sms.version>3.1.635</tencent.sms.version>
+        <aliyun.sms.version>2.0.23</aliyun.sms.version>
+        <tencent.sms.version>3.1.660</tencent.sms.version>
     </properties>
 
+    <profiles>
+        <profile>
+            <id>local</id>
+            <properties>
+                <!-- 环境标识,需要与配置文件的名称相对应 -->
+                <profiles.active>local</profiles.active>
+                <logging.level>debug</logging.level>
+            </properties>
+        </profile>
+        <profile>
+            <id>dev</id>
+            <properties>
+                <!-- 环境标识,需要与配置文件的名称相对应 -->
+                <profiles.active>dev</profiles.active>
+                <logging.level>debug</logging.level>
+            </properties>
+            <activation>
+                <!-- 默认环境 -->
+                <activeByDefault>true</activeByDefault>
+            </activation>
+        </profile>
+        <profile>
+            <id>prod</id>
+            <properties>
+                <profiles.active>prod</profiles.active>
+                <logging.level>warn</logging.level>
+            </properties>
+        </profile>
+    </profiles>
+
     <!-- 依赖声明 -->
     <dependencyManagement>
         <dependencies>
@@ -149,11 +177,7 @@
                 <artifactId>mybatis-plus-boot-starter</artifactId>
                 <version>${mybatis-plus.version}</version>
             </dependency>
-            <dependency>
-                <groupId>com.baomidou</groupId>
-                <artifactId>mybatis-plus-extension</artifactId>
-                <version>${mybatis-plus.version}</version>
-            </dependency>
+
             <!-- sql性能分析插件 -->
             <dependency>
                 <groupId>p6spy</groupId>
@@ -221,13 +245,6 @@
                 <version>${alibaba-ttl.version}</version>
             </dependency>
 
-            <!-- 统一 guava 版本 解决隐式漏洞问题 -->
-            <dependency>
-                <groupId>com.google.guava</groupId>
-                <artifactId>guava</artifactId>
-                <version>${guava.version}</version>
-            </dependency>
-
             <!-- 临时修复 snakeyaml 漏洞 -->
             <dependency>
                 <groupId>org.yaml</groupId>
@@ -374,8 +391,8 @@
     <repositories>
         <repository>
             <id>public</id>
-            <name>huawei nexus</name>
-            <url>https://mirrors.huaweicloud.com/repository/maven/</url>
+            <name>aliyun nexus</name>
+            <url>https://maven.aliyun.com/repository/public/</url>
             <releases>
                 <enabled>true</enabled>
             </releases>
@@ -385,8 +402,8 @@
     <pluginRepositories>
         <pluginRepository>
             <id>public</id>
-            <name>huawei nexus</name>
-            <url>https://mirrors.huaweicloud.com/repository/maven/</url>
+            <name>aliyun nexus</name>
+            <url>https://maven.aliyun.com/repository/public/</url>
             <releases>
                 <enabled>true</enabled>
             </releases>
@@ -396,36 +413,6 @@
         </pluginRepository>
     </pluginRepositories>
 
-    <profiles>
-        <profile>
-            <id>local</id>
-            <properties>
-                <!-- 环境标识,需要与配置文件的名称相对应 -->
-                <profiles.active>local</profiles.active>
-                <logging.level>debug</logging.level>
-            </properties>
-        </profile>
-        <profile>
-            <id>dev</id>
-            <properties>
-                <!-- 环境标识,需要与配置文件的名称相对应 -->
-                <profiles.active>dev</profiles.active>
-                <logging.level>debug</logging.level>
-            </properties>
-            <activation>
-                <!-- 默认环境 -->
-                <activeByDefault>true</activeByDefault>
-            </activation>
-        </profile>
-        <profile>
-            <id>prod</id>
-            <properties>
-                <profiles.active>prod</profiles.active>
-                <logging.level>warn</logging.level>
-            </properties>
-        </profile>
-    </profiles>
-
 </project>
 
 

+ 1 - 1
ruoyi-admin/pom.xml

@@ -5,7 +5,7 @@
     <parent>
         <artifactId>ruoyi-vue-plus</artifactId>
         <groupId>com.ruoyi</groupId>
-        <version>4.4.0</version>
+        <version>4.5.0</version>
     </parent>
     <modelVersion>4.0.0</modelVersion>
     <packaging>jar</packaging>

+ 9 - 19
ruoyi-admin/src/main/java/com/ruoyi/web/controller/common/CaptchaController.java

@@ -3,7 +3,6 @@ package com.ruoyi.web.controller.common;
 import cn.dev33.satoken.annotation.SaIgnore;
 import cn.hutool.captcha.AbstractCaptcha;
 import cn.hutool.captcha.generator.CodeGenerator;
-import cn.hutool.core.convert.Convert;
 import cn.hutool.core.util.IdUtil;
 import cn.hutool.core.util.RandomUtil;
 import com.ruoyi.common.constant.CacheConstants;
@@ -21,6 +20,9 @@ import com.ruoyi.sms.entity.SmsResult;
 import com.ruoyi.system.service.ISysConfigService;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
+import org.springframework.expression.Expression;
+import org.springframework.expression.ExpressionParser;
+import org.springframework.expression.spel.standard.SpelExpressionParser;
 import org.springframework.validation.annotation.Validated;
 import org.springframework.web.bind.annotation.GetMapping;
 import org.springframework.web.bind.annotation.RestController;
@@ -95,28 +97,16 @@ public class CaptchaController {
         AbstractCaptcha captcha = SpringUtils.getBean(captchaProperties.getCategory().getClazz());
         captcha.setGenerator(codeGenerator);
         captcha.createCode();
-        String code = isMath ? getCodeResult(captcha.getCode()) : captcha.getCode();
+        String code = captcha.getCode();
+        if (isMath) {
+            ExpressionParser parser = new SpelExpressionParser();
+            Expression exp = parser.parseExpression(StringUtils.remove(code, "="));
+            code = exp.getValue(String.class);
+        }
         RedisUtils.setCacheObject(verifyKey, code, Duration.ofMinutes(Constants.CAPTCHA_EXPIRATION));
         ajax.put("uuid", uuid);
         ajax.put("img", captcha.getImageBase64());
         return R.ok(ajax);
     }
 
-    private String getCodeResult(String capStr) {
-        int numberLength = captchaProperties.getNumberLength();
-        int a = Convert.toInt(StringUtils.substring(capStr, 0, numberLength).trim());
-        char operator = capStr.charAt(numberLength);
-        int b = Convert.toInt(StringUtils.substring(capStr, numberLength + 1, numberLength + 1 + numberLength).trim());
-        switch (operator) {
-            case '*':
-                return Convert.toStr(a * b);
-            case '+':
-                return Convert.toStr(a + b);
-            case '-':
-                return Convert.toStr(a - b);
-            default:
-                return StringUtils.EMPTY;
-        }
-    }
-
 }

+ 0 - 2
ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysLoginController.java

@@ -13,7 +13,6 @@ import com.ruoyi.system.domain.vo.RouterVo;
 import com.ruoyi.system.service.ISysMenuService;
 import com.ruoyi.system.service.ISysUserService;
 import com.ruoyi.system.service.SysLoginService;
-import com.ruoyi.system.service.SysPermissionService;
 import lombok.RequiredArgsConstructor;
 import org.springframework.validation.annotation.Validated;
 import org.springframework.web.bind.annotation.GetMapping;
@@ -39,7 +38,6 @@ public class SysLoginController {
     private final SysLoginService loginService;
     private final ISysMenuService menuService;
     private final ISysUserService userService;
-    private final SysPermissionService permissionService;
 
     /**
      * 登录方法

+ 6 - 0
ruoyi-admin/src/main/resources/application.yml

@@ -83,6 +83,9 @@ spring:
     restart:
       # 热部署开关
       enabled: true
+  mvc:
+    format:
+      date-time: yyyy-MM-dd HH:mm:ss
   jackson:
     # 日期格式化
     date-format: yyyy-MM-dd HH:mm:ss
@@ -202,6 +205,9 @@ swagger:
         name: ${sa-token.token-name}
 
 springdoc:
+  swagger-ui:
+    # 持久化认证数据
+    persistAuthorization: true
   #这里定义了两个分组,可定义多个,也可以不定义
   group-configs:
     - group: 1.演示模块

+ 1 - 1
ruoyi-admin/src/main/resources/i18n/messages.properties

@@ -41,5 +41,5 @@ repeat.submit.message=不允许重复提交,请稍候再试
 rate.limiter.message=访问过于频繁,请稍候再试
 sms.code.not.blank=短信验证码不能为空
 sms.code.retry.limit.count=短信验证码输入错误{0}次
-sms.code.retry.limit.exceed=短信验证码输入错误{0}次,帐户锁定{0}分钟
+sms.code.retry.limit.exceed=短信验证码输入错误{0}次,帐户锁定{1}分钟
 xcx.code.not.blank=小程序code不能为空

+ 1 - 1
ruoyi-admin/src/main/resources/i18n/messages_en_US.properties

@@ -41,5 +41,5 @@ repeat.submit.message=Repeat submit is not allowed, please try again later
 rate.limiter.message=Visit too frequently, please try again later
 sms.code.not.blank=Sms code cannot be blank
 sms.code.retry.limit.count=Sms code input error {0} times
-sms.code.retry.limit.exceed=Sms code input error {0} times, account locked for {0} minutes
+sms.code.retry.limit.exceed=Sms code input error {0} times, account locked for {1} minutes
 xcx.code.not.blank=Mini program code cannot be blank

+ 1 - 1
ruoyi-admin/src/main/resources/i18n/messages_zh_CN.properties

@@ -41,5 +41,5 @@ repeat.submit.message=不允许重复提交,请稍候再试
 rate.limiter.message=访问过于频繁,请稍候再试
 sms.code.not.blank=短信验证码不能为空
 sms.code.retry.limit.count=短信验证码输入错误{0}次
-sms.code.retry.limit.exceed=短信验证码输入错误{0}次,帐户锁定{0}分钟
+sms.code.retry.limit.exceed=短信验证码输入错误{0}次,帐户锁定{1}分钟
 xcx.code.not.blank=小程序code不能为空

+ 1 - 5
ruoyi-common/pom.xml

@@ -5,7 +5,7 @@
     <parent>
         <artifactId>ruoyi-vue-plus</artifactId>
         <groupId>com.ruoyi</groupId>
-        <version>4.4.0</version>
+        <version>4.5.0</version>
     </parent>
     <modelVersion>4.0.0</modelVersion>
 
@@ -79,10 +79,6 @@
             <groupId>com.baomidou</groupId>
             <artifactId>mybatis-plus-boot-starter</artifactId>
         </dependency>
-        <dependency>
-            <groupId>com.baomidou</groupId>
-            <artifactId>mybatis-plus-extension</artifactId>
-        </dependency>
 
         <!-- dynamic-datasource 多数据源-->
         <dependency>

+ 0 - 1
ruoyi-common/src/main/java/com/ruoyi/common/core/domain/dto/UserOnlineDTO.java

@@ -1,6 +1,5 @@
 package com.ruoyi.common.core.domain.dto;
 
-import lombok.Builder;
 import lombok.Data;
 import lombok.NoArgsConstructor;
 

+ 44 - 0
ruoyi-common/src/main/java/com/ruoyi/common/core/domain/event/LogininforEvent.java

@@ -0,0 +1,44 @@
+package com.ruoyi.common.core.domain.event;
+
+import lombok.Data;
+
+import javax.servlet.http.HttpServletRequest;
+import java.io.Serializable;
+
+/**
+ * 登录事件
+ *
+ * @author Lion Li
+ */
+
+@Data
+public class LogininforEvent implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 用户账号
+     */
+    private String username;
+
+    /**
+     * 登录状态 0成功 1失败
+     */
+    private String status;
+
+    /**
+     * 提示消息
+     */
+    private String message;
+
+    /**
+     * 请求体
+     */
+    private HttpServletRequest request;
+
+    /**
+     * 其他参数
+     */
+    private Object[] args;
+
+}

+ 3 - 3
ruoyi-common/src/main/java/com/ruoyi/common/core/domain/dto/OperLogDTO.java → ruoyi-common/src/main/java/com/ruoyi/common/core/domain/event/OperLogEvent.java

@@ -1,4 +1,4 @@
-package com.ruoyi.common.core.domain.dto;
+package com.ruoyi.common.core.domain.event;
 
 import lombok.Data;
 
@@ -6,13 +6,13 @@ import java.io.Serializable;
 import java.util.Date;
 
 /**
- * 通用操作日志实体
+ * 操作日志事件
  *
  * @author Lion Li
  */
 
 @Data
-public class OperLogDTO implements Serializable {
+public class OperLogEvent implements Serializable {
 
     private static final long serialVersionUID = 1L;
 

+ 8 - 45
ruoyi-common/src/main/java/com/ruoyi/common/core/mapper/BaseMapperPlus.java

@@ -4,16 +4,12 @@ import cn.hutool.core.collection.CollUtil;
 import cn.hutool.core.util.ObjectUtil;
 import com.baomidou.mybatisplus.core.conditions.Wrapper;
 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
-import com.baomidou.mybatisplus.core.enums.SqlMethod;
 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
 import com.baomidou.mybatisplus.core.metadata.IPage;
-import com.baomidou.mybatisplus.core.metadata.TableInfo;
-import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
 import com.baomidou.mybatisplus.core.toolkit.*;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
-import com.baomidou.mybatisplus.extension.toolkit.SqlHelper;
+import com.baomidou.mybatisplus.extension.toolkit.Db;
 import com.ruoyi.common.utils.BeanCopyUtils;
-import org.apache.ibatis.binding.MapperMethod;
 import org.apache.ibatis.logging.Log;
 import org.apache.ibatis.logging.LogFactory;
 
@@ -21,7 +17,6 @@ import java.io.Serializable;
 import java.util.Collection;
 import java.util.List;
 import java.util.Map;
-import java.util.Objects;
 
 /**
  * 自定义 Mapper 接口, 实现 自定义扩展
@@ -37,8 +32,6 @@ public interface BaseMapperPlus<M, T, V> extends BaseMapper<T> {
 
     Log log = LogFactory.getLog(BaseMapperPlus.class);
 
-    int DEFAULT_BATCH_SIZE = 1000;
-
     default Class<V> currentVoClass() {
         return (Class<V>) ReflectionKit.getSuperClassGenericType(this.getClass(), BaseMapperPlus.class, 2);
     }
@@ -59,79 +52,49 @@ public interface BaseMapperPlus<M, T, V> extends BaseMapper<T> {
      * 批量插入
      */
     default boolean insertBatch(Collection<T> entityList) {
-        return insertBatch(entityList, DEFAULT_BATCH_SIZE);
+        return Db.saveBatch(entityList);
     }
 
     /**
      * 批量更新
      */
     default boolean updateBatchById(Collection<T> entityList) {
-        return updateBatchById(entityList, DEFAULT_BATCH_SIZE);
+        return Db.updateBatchById(entityList);
     }
 
     /**
      * 批量插入或更新
      */
     default boolean insertOrUpdateBatch(Collection<T> entityList) {
-        return insertOrUpdateBatch(entityList, DEFAULT_BATCH_SIZE);
+        return Db.saveOrUpdateBatch(entityList);
     }
 
     /**
      * 批量插入(包含限制条数)
      */
     default boolean insertBatch(Collection<T> entityList, int batchSize) {
-        String sqlStatement = SqlHelper.getSqlStatement(this.currentMapperClass(), SqlMethod.INSERT_ONE);
-        return SqlHelper.executeBatch(this.currentModelClass(), log, entityList, batchSize,
-            (sqlSession, entity) -> sqlSession.insert(sqlStatement, entity));
+        return Db.saveBatch(entityList, batchSize);
     }
 
     /**
      * 批量更新(包含限制条数)
      */
     default boolean updateBatchById(Collection<T> entityList, int batchSize) {
-        String sqlStatement = SqlHelper.getSqlStatement(this.currentMapperClass(), SqlMethod.UPDATE_BY_ID);
-        return SqlHelper.executeBatch(this.currentModelClass(), log, entityList, batchSize,
-            (sqlSession, entity) -> {
-                MapperMethod.ParamMap<T> param = new MapperMethod.ParamMap<>();
-                param.put(Constants.ENTITY, entity);
-                sqlSession.update(sqlStatement, param);
-            });
+        return Db.updateBatchById(entityList, batchSize);
     }
 
     /**
      * 批量插入或更新(包含限制条数)
      */
     default boolean insertOrUpdateBatch(Collection<T> entityList, int batchSize) {
-        TableInfo tableInfo = TableInfoHelper.getTableInfo(this.currentModelClass());
-        Assert.notNull(tableInfo, "error: can not execute. because can not find cache of TableInfo for entity!");
-        String keyProperty = tableInfo.getKeyProperty();
-        Assert.notEmpty(keyProperty, "error: can not execute. because can not find column for id from entity!");
-        return SqlHelper.saveOrUpdateBatch(this.currentModelClass(), this.currentMapperClass(), log, entityList, batchSize, (sqlSession, entity) -> {
-            Object idVal = tableInfo.getPropertyValue(entity, keyProperty);
-            String sqlStatement = SqlHelper.getSqlStatement(this.currentMapperClass(), SqlMethod.SELECT_BY_ID);
-            return StringUtils.checkValNull(idVal)
-                || CollectionUtils.isEmpty(sqlSession.selectList(sqlStatement, entity));
-        }, (sqlSession, entity) -> {
-            MapperMethod.ParamMap<T> param = new MapperMethod.ParamMap<>();
-            param.put(Constants.ENTITY, entity);
-            String sqlStatement = SqlHelper.getSqlStatement(this.currentMapperClass(), SqlMethod.UPDATE_BY_ID);
-            sqlSession.update(sqlStatement, param);
-        });
+        return Db.saveOrUpdateBatch(entityList, batchSize);
     }
 
     /**
      * 插入或更新(包含限制条数)
      */
     default boolean insertOrUpdate(T entity) {
-        if (null != entity) {
-            TableInfo tableInfo = TableInfoHelper.getTableInfo(this.currentModelClass());
-            Assert.notNull(tableInfo, "error: can not execute. because can not find cache of TableInfo for entity!");
-            String keyProperty = tableInfo.getKeyProperty();
-            Assert.notEmpty(keyProperty, "error: can not execute. because can not find column for id from entity!");
-            Object idVal = tableInfo.getPropertyValue(entity, tableInfo.getKeyProperty());
-            return StringUtils.checkValNull(idVal) || Objects.isNull(selectById((Serializable) idVal)) ? insert(entity) > 0 : updateById(entity) > 0;
-        }
-        return false;
+        return Db.saveOrUpdate(entity);
     }
 
     default V selectVoById(Serializable id) {

+ 0 - 14
ruoyi-common/src/main/java/com/ruoyi/common/core/service/LogininforService.java

@@ -1,14 +0,0 @@
-package com.ruoyi.common.core.service;
-
-import javax.servlet.http.HttpServletRequest;
-
-/**
- * 通用 系统访问日志
- *
- * @author Lion Li
- */
-public interface LogininforService {
-
-    void recordLogininfor(String username, String status, String message,
-                          HttpServletRequest request, Object... args);
-}

+ 0 - 15
ruoyi-common/src/main/java/com/ruoyi/common/core/service/OperLogService.java

@@ -1,15 +0,0 @@
-package com.ruoyi.common.core.service;
-
-import com.ruoyi.common.core.domain.dto.OperLogDTO;
-import org.springframework.scheduling.annotation.Async;
-
-/**
- * 通用 操作日志
- *
- * @author Lion Li
- */
-public interface OperLogService {
-
-    @Async
-    void recordOper(OperLogDTO operLogDTO);
-}

+ 1 - 1
ruoyi-common/src/main/java/com/ruoyi/common/excel/CellMergeStrategy.java

@@ -86,7 +86,7 @@ public class CellMergeStrategy extends AbstractMergeStrategy {
 						// 空值跳过不合并
 						continue;
 					}
-					if (cellValue != val) {
+					if (!cellValue.equals(val)) {
 						if (i - repeatCell.getCurrent() > 1) {
 							cellList.add(new CellRangeAddress(repeatCell.getCurrent() + rowIndex, i + rowIndex - 1, colNum, colNum));
 						}

+ 3 - 3
ruoyi-common/src/main/java/com/ruoyi/common/filter/XssHttpServletRequestWrapper.java

@@ -33,12 +33,12 @@ public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper {
         String[] values = super.getParameterValues(name);
         if (values != null) {
             int length = values.length;
-            String[] escapseValues = new String[length];
+            String[] escapesValues = new String[length];
             for (int i = 0; i < length; i++) {
                 // 防xss攻击和过滤前后空格
-                escapseValues[i] = HtmlUtil.cleanHtmlTag(values[i]).trim();
+                escapesValues[i] = HtmlUtil.cleanHtmlTag(values[i]).trim();
             }
-            return escapseValues;
+            return escapesValues;
         }
         return super.getParameterValues(name);
     }

+ 10 - 4
ruoyi-common/src/main/java/com/ruoyi/common/helper/LoginHelper.java

@@ -81,12 +81,18 @@ public class LoginHelper {
         LoginUser loginUser = getLoginUser();
         if (ObjectUtil.isNull(loginUser)) {
             String loginId = StpUtil.getLoginIdAsString();
-            String[] strs = StringUtils.split(loginId, JOIN_CODE);
-            if (!ArrayUtil.containsAny(strs, UserType.values())) {
+            String userId = null;
+            for (UserType value : UserType.values()) {
+                if (StringUtils.contains(loginId, value.getUserType())) {
+                    String[] strs = StringUtils.split(loginId, JOIN_CODE);
+                    // 用户id在总是在最后
+                    userId = strs[strs.length - 1];
+                }
+            }
+            if (StringUtils.isBlank(userId)) {
                 throw new UtilException("登录用户: LoginId异常 => " + loginId);
             }
-            // 用户id在总是在最后
-            return Long.parseLong(strs[strs.length - 1]);
+            return Long.parseLong(userId);
         }
         return loginUser.getUserId();
     }

+ 113 - 51
ruoyi-common/src/main/java/com/ruoyi/common/utils/poi/ExcelUtil.java

@@ -24,6 +24,7 @@ import javax.servlet.ServletOutputStream;
 import javax.servlet.http.HttpServletResponse;
 import java.io.IOException;
 import java.io.InputStream;
+import java.io.OutputStream;
 import java.io.UnsupportedEncodingException;
 import java.util.Collection;
 import java.util.List;
@@ -84,7 +85,13 @@ public class ExcelUtil {
      * @param response  响应体
      */
     public static <T> void exportExcel(List<T> list, String sheetName, Class<T> clazz, HttpServletResponse response) {
-        exportExcel(list, sheetName, clazz, false, response);
+        try {
+            resetResponse(sheetName, response);
+            ServletOutputStream os = response.getOutputStream();
+            exportExcel(list, sheetName, clazz, false, os);
+        } catch (IOException e) {
+            throw new RuntimeException("导出Excel异常");
+        }
     }
 
     /**
@@ -100,24 +107,49 @@ public class ExcelUtil {
         try {
             resetResponse(sheetName, response);
             ServletOutputStream os = response.getOutputStream();
-            ExcelWriterSheetBuilder builder = EasyExcel.write(os, clazz)
-                .autoCloseStream(false)
-                // 自动适配
-                .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy())
-                // 大数值自动转换 防止失真
-                .registerConverter(new ExcelBigNumberConvert())
-                .sheet(sheetName);
-            if (merge) {
-                // 合并处理器
-                builder.registerWriteHandler(new CellMergeStrategy(list, true));
-            }
-            builder.doWrite(list);
+            exportExcel(list, sheetName, clazz, merge, os);
         } catch (IOException e) {
             throw new RuntimeException("导出Excel异常");
         }
     }
 
     /**
+     * 导出excel
+     *
+     * @param list      导出数据集合
+     * @param sheetName 工作表的名称
+     * @param clazz     实体类
+     * @param os        输出流
+     */
+    public static <T> void exportExcel(List<T> list, String sheetName, Class<T> clazz, OutputStream os) {
+        exportExcel(list, sheetName, clazz, false, os);
+    }
+
+    /**
+     * 导出excel
+     *
+     * @param list      导出数据集合
+     * @param sheetName 工作表的名称
+     * @param clazz     实体类
+     * @param merge     是否合并单元格
+     * @param os        输出流
+     */
+    public static <T> void exportExcel(List<T> list, String sheetName, Class<T> clazz, boolean merge, OutputStream os) {
+        ExcelWriterSheetBuilder builder = EasyExcel.write(os, clazz)
+            .autoCloseStream(false)
+            // 自动适配
+            .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy())
+            // 大数值自动转换 防止失真
+            .registerConverter(new ExcelBigNumberConvert())
+            .sheet(sheetName);
+        if (merge) {
+            // 合并处理器
+            builder.registerWriteHandler(new CellMergeStrategy(list, true));
+        }
+        builder.doWrite(list);
+    }
+
+    /**
      * 单表多数据模板导出 模板格式为 {.属性}
      *
      * @param filename     文件名
@@ -125,32 +157,47 @@ public class ExcelUtil {
      *                     例如: excel/temp.xlsx
      *                     重点: 模板文件必须放置到启动类对应的 resource 目录下
      * @param data         模板需要的数据
+     * @param response     响应体
      */
     public static void exportTemplate(List<Object> data, String filename, String templatePath, HttpServletResponse response) {
         try {
             resetResponse(filename, response);
-            ClassPathResource templateResource = new ClassPathResource(templatePath);
-            ExcelWriter excelWriter = EasyExcel.write(response.getOutputStream())
-                .withTemplate(templateResource.getStream())
-                .autoCloseStream(false)
-                // 大数值自动转换 防止失真
-                .registerConverter(new ExcelBigNumberConvert())
-                .build();
-            WriteSheet writeSheet = EasyExcel.writerSheet().build();
-            if (CollUtil.isEmpty(data)) {
-                throw new IllegalArgumentException("数据为空");
-            }
-            // 单表多数据导出 模板格式为 {.属性}
-            for (Object d : data) {
-                excelWriter.fill(d, writeSheet);
-            }
-            excelWriter.finish();
+            ServletOutputStream os = response.getOutputStream();
+            exportTemplate(data, templatePath, os);
         } catch (IOException e) {
             throw new RuntimeException("导出Excel异常");
         }
     }
 
     /**
+     * 单表多数据模板导出 模板格式为 {.属性}
+     *
+     * @param templatePath 模板路径 resource 目录下的路径包括模板文件名
+     *                     例如: excel/temp.xlsx
+     *                     重点: 模板文件必须放置到启动类对应的 resource 目录下
+     * @param data         模板需要的数据
+     * @param os           输出流
+     */
+    public static void exportTemplate(List<Object> data, String templatePath, OutputStream os) {
+        ClassPathResource templateResource = new ClassPathResource(templatePath);
+        ExcelWriter excelWriter = EasyExcel.write(os)
+            .withTemplate(templateResource.getStream())
+            .autoCloseStream(false)
+            // 大数值自动转换 防止失真
+            .registerConverter(new ExcelBigNumberConvert())
+            .build();
+        WriteSheet writeSheet = EasyExcel.writerSheet().build();
+        if (CollUtil.isEmpty(data)) {
+            throw new IllegalArgumentException("数据为空");
+        }
+        // 单表多数据导出 模板格式为 {.属性}
+        for (Object d : data) {
+            excelWriter.fill(d, writeSheet);
+        }
+        excelWriter.finish();
+    }
+
+    /**
      * 多表多数据模板导出 模板格式为 {key.属性}
      *
      * @param filename     文件名
@@ -158,38 +205,53 @@ public class ExcelUtil {
      *                     例如: excel/temp.xlsx
      *                     重点: 模板文件必须放置到启动类对应的 resource 目录下
      * @param data         模板需要的数据
+     * @param response     响应体
      */
     public static void exportTemplateMultiList(Map<String, Object> data, String filename, String templatePath, HttpServletResponse response) {
         try {
             resetResponse(filename, response);
-            ClassPathResource templateResource = new ClassPathResource(templatePath);
-            ExcelWriter excelWriter = EasyExcel.write(response.getOutputStream())
-                .withTemplate(templateResource.getStream())
-                .autoCloseStream(false)
-                // 大数值自动转换 防止失真
-                .registerConverter(new ExcelBigNumberConvert())
-                .build();
-            WriteSheet writeSheet = EasyExcel.writerSheet().build();
-            if (CollUtil.isEmpty(data)) {
-                throw new IllegalArgumentException("数据为空");
-            }
-            for (Map.Entry<String, Object> map : data.entrySet()) {
-                // 设置列表后续还有数据
-                FillConfig fillConfig = FillConfig.builder().forceNewRow(Boolean.TRUE).build();
-                if (map.getValue() instanceof Collection) {
-                    // 多表导出必须使用 FillWrapper
-                    excelWriter.fill(new FillWrapper(map.getKey(), (Collection<?>) map.getValue()), fillConfig, writeSheet);
-                } else {
-                    excelWriter.fill(map.getValue(), writeSheet);
-                }
-            }
-            excelWriter.finish();
+            ServletOutputStream os = response.getOutputStream();
+            exportTemplateMultiList(data, templatePath, os);
         } catch (IOException e) {
             throw new RuntimeException("导出Excel异常");
         }
     }
 
     /**
+     * 多表多数据模板导出 模板格式为 {key.属性}
+     *
+     * @param templatePath 模板路径 resource 目录下的路径包括模板文件名
+     *                     例如: excel/temp.xlsx
+     *                     重点: 模板文件必须放置到启动类对应的 resource 目录下
+     * @param data         模板需要的数据
+     * @param os           输出流
+     */
+    public static void exportTemplateMultiList(Map<String, Object> data, String templatePath, OutputStream os) {
+        ClassPathResource templateResource = new ClassPathResource(templatePath);
+        ExcelWriter excelWriter = EasyExcel.write(os)
+            .withTemplate(templateResource.getStream())
+            .autoCloseStream(false)
+            // 大数值自动转换 防止失真
+            .registerConverter(new ExcelBigNumberConvert())
+            .build();
+        WriteSheet writeSheet = EasyExcel.writerSheet().build();
+        if (CollUtil.isEmpty(data)) {
+            throw new IllegalArgumentException("数据为空");
+        }
+        for (Map.Entry<String, Object> map : data.entrySet()) {
+            // 设置列表后续还有数据
+            FillConfig fillConfig = FillConfig.builder().forceNewRow(Boolean.TRUE).build();
+            if (map.getValue() instanceof Collection) {
+                // 多表导出必须使用 FillWrapper
+                excelWriter.fill(new FillWrapper(map.getKey(), (Collection<?>) map.getValue()), fillConfig, writeSheet);
+            } else {
+                excelWriter.fill(map.getValue(), writeSheet);
+            }
+        }
+        excelWriter.finish();
+    }
+
+    /**
      * 重置响应体
      */
     private static void resetResponse(String sheetName, HttpServletResponse response) throws UnsupportedEncodingException {

+ 13 - 13
ruoyi-common/src/main/java/com/ruoyi/common/utils/redis/RedisUtils.java

@@ -4,7 +4,6 @@ import com.ruoyi.common.utils.spring.SpringUtils;
 import lombok.AccessLevel;
 import lombok.NoArgsConstructor;
 import org.redisson.api.*;
-import org.redisson.config.Config;
 
 import java.time.Duration;
 import java.util.Collection;
@@ -27,14 +26,6 @@ public class RedisUtils {
 
     private static final RedissonClient CLIENT = SpringUtils.getBean(RedissonClient.class);
 
-    public static NameMapper getNameMapper() {
-        Config config = CLIENT.getConfig();
-        if (config.isClusterConfig()) {
-            return config.useClusterServers().getNameMapper();
-        }
-        return config.useSingleServer().getNameMapper();
-    }
-
     /**
      * 限流
      *
@@ -219,6 +210,15 @@ public class RedisUtils {
     }
 
     /**
+     * 检查缓存对象是否存在
+     *
+     * @param key 缓存的键值
+     */
+    public static boolean isExistsObject(final String key) {
+        return CLIENT.getBucket(key).isExists();
+    }
+
+    /**
      * 缓存List数据
      *
      * @param key      缓存的键值
@@ -437,8 +437,8 @@ public class RedisUtils {
      * @return 对象列表
      */
     public static Collection<String> keys(final String pattern) {
-        Stream<String> stream = CLIENT.getKeys().getKeysStreamByPattern(getNameMapper().map(pattern));
-        return stream.map(key -> getNameMapper().unmap(key)).collect(Collectors.toList());
+        Stream<String> stream = CLIENT.getKeys().getKeysStreamByPattern(pattern);
+        return stream.collect(Collectors.toList());
     }
 
     /**
@@ -447,7 +447,7 @@ public class RedisUtils {
      * @param pattern 字符串前缀
      */
     public static void deleteKeys(final String pattern) {
-        CLIENT.getKeys().deleteByPattern(getNameMapper().map(pattern));
+        CLIENT.getKeys().deleteByPattern(pattern);
     }
 
     /**
@@ -457,6 +457,6 @@ public class RedisUtils {
      */
     public static Boolean hasKey(String key) {
         RKeys rKeys = CLIENT.getKeys();
-        return rKeys.countExists(getNameMapper().map(key)) > 0;
+        return rKeys.countExists(key) > 0;
     }
 }

+ 9 - 0
ruoyi-common/src/main/java/com/ruoyi/common/utils/spring/SpringUtils.java

@@ -3,6 +3,7 @@ package com.ruoyi.common.utils.spring;
 import cn.hutool.extra.spring.SpringUtil;
 import org.springframework.aop.framework.AopContext;
 import org.springframework.beans.factory.NoSuchBeanDefinitionException;
+import org.springframework.context.ApplicationContext;
 import org.springframework.stereotype.Component;
 
 /**
@@ -62,4 +63,12 @@ public final class SpringUtils extends SpringUtil {
         return (T) AopContext.currentProxy();
     }
 
+
+    /**
+     * 获取spring上下文
+     */
+    public static ApplicationContext context() {
+        return getApplicationContext();
+    }
+
 }

+ 1 - 1
ruoyi-demo/pom.xml

@@ -5,7 +5,7 @@
     <parent>
         <artifactId>ruoyi-vue-plus</artifactId>
         <groupId>com.ruoyi</groupId>
-        <version>4.4.0</version>
+        <version>4.5.0</version>
     </parent>
     <modelVersion>4.0.0</modelVersion>
 

+ 1 - 2
ruoyi-demo/src/main/java/com/ruoyi/demo/controller/queue/PriorityDemo.java

@@ -2,7 +2,6 @@ package com.ruoyi.demo.controller.queue;
 
 import lombok.Data;
 import lombok.NoArgsConstructor;
-import org.jetbrains.annotations.NotNull;
 
 /**
  * 实体类 注意不允许使用内部类 否则会找不到类
@@ -17,7 +16,7 @@ public class PriorityDemo implements Comparable<PriorityDemo> {
     private Integer orderNum;
 
     @Override
-    public int compareTo(@NotNull PriorityDemo other) {
+    public int compareTo(PriorityDemo other) {
         return Integer.compare(getOrderNum(), other.getOrderNum());
     }
 }

+ 1 - 1
ruoyi-extend/pom.xml

@@ -5,7 +5,7 @@
     <parent>
         <artifactId>ruoyi-vue-plus</artifactId>
         <groupId>com.ruoyi</groupId>
-        <version>4.4.0</version>
+        <version>4.5.0</version>
     </parent>
     <modelVersion>4.0.0</modelVersion>
     <artifactId>ruoyi-extend</artifactId>

+ 1 - 1
ruoyi-extend/ruoyi-monitor-admin/pom.xml

@@ -5,7 +5,7 @@
     <parent>
         <artifactId>ruoyi-extend</artifactId>
         <groupId>com.ruoyi</groupId>
-        <version>4.4.0</version>
+        <version>4.5.0</version>
     </parent>
     <modelVersion>4.0.0</modelVersion>
     <packaging>jar</packaging>

+ 1 - 1
ruoyi-extend/ruoyi-xxl-job-admin/pom.xml

@@ -4,7 +4,7 @@
     <parent>
         <artifactId>ruoyi-extend</artifactId>
         <groupId>com.ruoyi</groupId>
-        <version>4.4.0</version>
+        <version>4.5.0</version>
     </parent>
     <artifactId>ruoyi-xxl-job-admin</artifactId>
     <packaging>jar</packaging>

+ 1 - 1
ruoyi-framework/pom.xml

@@ -5,7 +5,7 @@
     <parent>
         <artifactId>ruoyi-vue-plus</artifactId>
         <groupId>com.ruoyi</groupId>
-        <version>4.4.0</version>
+        <version>4.5.0</version>
     </parent>
     <modelVersion>4.0.0</modelVersion>
 

+ 6 - 7
ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/LogAspect.java

@@ -4,8 +4,7 @@ import cn.hutool.core.lang.Dict;
 import cn.hutool.core.map.MapUtil;
 import cn.hutool.core.util.ObjectUtil;
 import com.ruoyi.common.annotation.Log;
-import com.ruoyi.common.core.domain.dto.OperLogDTO;
-import com.ruoyi.common.core.service.OperLogService;
+import com.ruoyi.common.core.domain.event.OperLogEvent;
 import com.ruoyi.common.enums.BusinessStatus;
 import com.ruoyi.common.enums.HttpMethod;
 import com.ruoyi.common.helper.LoginHelper;
@@ -67,7 +66,7 @@ public class LogAspect {
         try {
 
             // *========数据库日志=========*//
-            OperLogDTO operLog = new OperLogDTO();
+            OperLogEvent operLog = new OperLogEvent();
             operLog.setStatus(BusinessStatus.SUCCESS.ordinal());
             // 请求的地址
             String ip = ServletUtils.getClientIP();
@@ -87,8 +86,8 @@ public class LogAspect {
             operLog.setRequestMethod(ServletUtils.getRequest().getMethod());
             // 处理设置注解上的参数
             getControllerMethodDescription(joinPoint, controllerLog, operLog, jsonResult);
-            // 保存数据库
-            SpringUtils.getBean(OperLogService.class).recordOper(operLog);
+            // 发布事件保存数据库
+            SpringUtils.context().publishEvent(operLog);
         } catch (Exception exp) {
             // 记录本地异常日志
             log.error("异常信息:{}", exp.getMessage());
@@ -103,7 +102,7 @@ public class LogAspect {
      * @param operLog 操作日志
      * @throws Exception
      */
-    public void getControllerMethodDescription(JoinPoint joinPoint, Log log, OperLogDTO operLog, Object jsonResult) throws Exception {
+    public void getControllerMethodDescription(JoinPoint joinPoint, Log log, OperLogEvent operLog, Object jsonResult) throws Exception {
         // 设置action动作
         operLog.setBusinessType(log.businessType().ordinal());
         // 设置标题
@@ -127,7 +126,7 @@ public class LogAspect {
      * @param operLog 操作日志
      * @throws Exception 异常
      */
-    private void setRequestValue(JoinPoint joinPoint, OperLogDTO operLog) throws Exception {
+    private void setRequestValue(JoinPoint joinPoint, OperLogEvent operLog) throws Exception {
         String requestMethod = operLog.getRequestMethod();
         if (HttpMethod.PUT.name().equals(requestMethod) || HttpMethod.POST.name().equals(requestMethod)) {
             String params = argsArrayToString(joinPoint.getArgs());

+ 1 - 1
ruoyi-framework/src/main/java/com/ruoyi/framework/config/AsyncConfig.java

@@ -18,7 +18,7 @@ import java.util.concurrent.ScheduledExecutorService;
  *
  * @author Lion Li
  */
-@EnableAsync
+@EnableAsync(proxyTargetClass = true)
 @Configuration
 public class AsyncConfig extends AsyncConfigurerSupport {
 

+ 1 - 2
ruoyi-framework/src/main/java/com/ruoyi/framework/config/RedisConfig.java

@@ -11,7 +11,6 @@ import org.redisson.spring.starter.RedissonAutoConfigurationCustomizer;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.boot.context.properties.EnableConfigurationProperties;
 import org.springframework.cache.CacheManager;
-import org.springframework.cache.annotation.CachingConfigurerSupport;
 import org.springframework.cache.annotation.EnableCaching;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
@@ -25,7 +24,7 @@ import org.springframework.context.annotation.Configuration;
 @Configuration
 @EnableCaching
 @EnableConfigurationProperties(RedissonProperties.class)
-public class RedisConfig extends CachingConfigurerSupport {
+public class RedisConfig {
 
     @Autowired
     private RedissonProperties redissonProperties;

+ 5 - 1
ruoyi-framework/src/main/java/com/ruoyi/framework/config/SwaggerConfig.java

@@ -23,6 +23,7 @@ import org.springframework.context.annotation.Configuration;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Optional;
+import java.util.Set;
 
 /**
  * Swagger 文档配置
@@ -51,8 +52,11 @@ public class SwaggerConfig {
         openApi.tags(swaggerProperties.getTags());
         openApi.paths(swaggerProperties.getPaths());
         openApi.components(swaggerProperties.getComponents());
+        Set<String> keySet = swaggerProperties.getComponents().getSecuritySchemes().keySet();
         List<SecurityRequirement> list = new ArrayList<>();
-        list.add(new SecurityRequirement().addList("apikey"));
+        SecurityRequirement securityRequirement = new SecurityRequirement();
+        keySet.forEach(securityRequirement::addList);
+        list.add(securityRequirement);
         openApi.security(list);
 
         return openApi;

+ 3 - 3
ruoyi-framework/src/main/java/com/ruoyi/framework/handler/PlusDataPermissionHandler.java

@@ -54,7 +54,7 @@ public class PlusDataPermissionHandler {
     /**
      * 无效注解方法缓存用于快速返回
      */
-    private final Set<String> inavlidCacheSet = new ConcurrentHashSet<>();
+    private final Set<String> invalidCacheSet = new ConcurrentHashSet<>();
 
     /**
      * spel 解析器
@@ -70,7 +70,7 @@ public class PlusDataPermissionHandler {
     public Expression getSqlSegment(Expression where, String mappedStatementId, boolean isSelect) {
         DataColumn[] dataColumns = findAnnotation(mappedStatementId);
         if (ArrayUtil.isEmpty(dataColumns)) {
-            inavlidCacheSet.add(mappedStatementId);
+            invalidCacheSet.add(mappedStatementId);
             return where;
         }
         LoginUser currentUser = DataPermissionHelper.getVariable("user");
@@ -194,6 +194,6 @@ public class PlusDataPermissionHandler {
      * 是否为无效方法 无数据权限
      */
     public boolean isInvalid(String mappedStatementId) {
-        return inavlidCacheSet.contains(mappedStatementId);
+        return invalidCacheSet.contains(mappedStatementId);
     }
 }

+ 1 - 1
ruoyi-generator/pom.xml

@@ -5,7 +5,7 @@
     <parent>
         <artifactId>ruoyi-vue-plus</artifactId>
         <groupId>com.ruoyi</groupId>
-        <version>4.4.0</version>
+        <version>4.5.0</version>
     </parent>
     <modelVersion>4.0.0</modelVersion>
 

+ 1 - 1
ruoyi-generator/src/main/java/com/ruoyi/generator/config/GenConfig.java

@@ -12,7 +12,7 @@ import org.springframework.stereotype.Component;
  */
 @Component
 @ConfigurationProperties(prefix = "gen")
-@PropertySource(value = {"classpath:generator.yml"})
+@PropertySource(value = {"classpath:generator.yml"}, encoding = "UTF-8")
 public class GenConfig {
 
     /**

+ 4 - 6
ruoyi-generator/src/main/java/com/ruoyi/generator/service/GenTableServiceImpl.java

@@ -3,12 +3,11 @@ package com.ruoyi.generator.service;
 import cn.hutool.core.collection.CollUtil;
 import cn.hutool.core.io.IoUtil;
 import cn.hutool.core.lang.Dict;
-import cn.hutool.core.lang.Snowflake;
-import cn.hutool.core.util.IdUtil;
 import cn.hutool.core.util.ObjectUtil;
 import com.baomidou.dynamic.datasource.annotation.DS;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.core.incrementer.IdentifierGenerator;
 import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.ruoyi.common.constant.Constants;
@@ -58,6 +57,7 @@ public class GenTableServiceImpl implements IGenTableService {
 
     private final GenTableMapper baseMapper;
     private final GenTableColumnMapper genTableColumnMapper;
+    private final IdentifierGenerator identifierGenerator;
 
     /**
      * 查询业务字段列表
@@ -205,10 +205,9 @@ public class GenTableServiceImpl implements IGenTableService {
         Map<String, String> dataMap = new LinkedHashMap<>();
         // 查询表信息
         GenTable table = baseMapper.selectGenTableById(tableId);
-        Snowflake snowflake = IdUtil.getSnowflake();
         List<Long> menuIds = new ArrayList<>();
         for (int i = 0; i < 6; i++) {
-            menuIds.add(snowflake.nextId());
+            menuIds.add(identifierGenerator.nextId(null).longValue());
         }
         table.setMenuIds(menuIds);
         // 设置主子表信息
@@ -356,10 +355,9 @@ public class GenTableServiceImpl implements IGenTableService {
     private void generatorCode(String tableName, ZipOutputStream zip) {
         // 查询表信息
         GenTable table = baseMapper.selectGenTableByName(tableName);
-        Snowflake snowflake = IdUtil.getSnowflake();
         List<Long> menuIds = new ArrayList<>();
         for (int i = 0; i < 6; i++) {
-            menuIds.add(snowflake.nextId());
+            menuIds.add(identifierGenerator.nextId(null).longValue());
         }
         table.setMenuIds(menuIds);
         // 设置主子表信息

+ 18 - 15
ruoyi-generator/src/main/resources/vm/vue/index-tree.vue.vm

@@ -186,11 +186,11 @@
           <el-input v-model="form.${field}" placeholder="请输入${comment}" />
         </el-form-item>
 #elseif($column.htmlType == "imageUpload")
-        <el-form-item label="${comment}">
+        <el-form-item label="${comment}" prop="${field}">
           <image-upload v-model="form.${field}"/>
         </el-form-item>
 #elseif($column.htmlType == "fileUpload")
-        <el-form-item label="${comment}">
+        <el-form-item label="${comment}" prop="${field}">
           <file-upload v-model="form.${field}"/>
         </el-form-item>
 #elseif($column.htmlType == "editor")
@@ -204,8 +204,11 @@
               v-for="dict in dict.type.${dictType}"
               :key="dict.value"
               :label="dict.label"
-              #if($column.javaType == "Integer" || $column.javaType == "Long"):value="parseInt(dict.value)"#else:value="dict.value"#end
-
+#if($column.javaType == "Integer" || $column.javaType == "Long")
+              :value="parseInt(dict.value)"
+#else
+              :value="dict.value"
+#end
             ></el-option>
           </el-select>
         </el-form-item>
@@ -216,7 +219,7 @@
           </el-select>
         </el-form-item>
 #elseif($column.htmlType == "checkbox" && "" != $dictType)
-        <el-form-item label="${comment}">
+        <el-form-item label="${comment}" prop="${field}">
           <el-checkbox-group v-model="form.${field}">
             <el-checkbox
               v-for="dict in dict.type.${dictType}"
@@ -227,24 +230,27 @@
           </el-checkbox-group>
         </el-form-item>
 #elseif($column.htmlType == "checkbox" && $dictType)
-        <el-form-item label="${comment}">
+        <el-form-item label="${comment}" prop="${field}">
           <el-checkbox-group v-model="form.${field}">
             <el-checkbox>请选择字典生成</el-checkbox>
           </el-checkbox-group>
         </el-form-item>
 #elseif($column.htmlType == "radio" && "" != $dictType)
-        <el-form-item label="${comment}">
+        <el-form-item label="${comment}" prop="${field}">
           <el-radio-group v-model="form.${field}">
             <el-radio
               v-for="dict in dict.type.${dictType}"
               :key="dict.value"
-              #if($column.javaType == "Integer" || $column.javaType == "Long"):label="parseInt(dict.value)"#else:label="dict.value"#end
-
+#if($column.javaType == "Integer" || $column.javaType == "Long")
+              :label="parseInt(dict.value)"
+#else
+              :label="dict.value"
+#end
             >{{dict.label}}</el-radio>
           </el-radio-group>
         </el-form-item>
 #elseif($column.htmlType == "radio" && $dictType)
-        <el-form-item label="${comment}">
+        <el-form-item label="${comment}" prop="${field}">
           <el-radio-group v-model="form.${field}">
             <el-radio label="1">请选择字典生成</el-radio>
           </el-radio-group>
@@ -335,7 +341,7 @@ export default {
 #set($comment=$column.columnComment)
 #end
         $column.javaField: [
-          { required: true, message: "$comment不能为空", trigger: #if($column.htmlType == "select")"change"#else"blur"#end }
+          { required: true, message: "$comment不能为空", trigger: #if($column.htmlType == "select" || $column.htmlType == "radio")"change"#else"blur"#end }
         ]#if($foreach.count != $columns.size()),#end
 #end
 #end
@@ -398,10 +404,7 @@ export default {
     reset() {
       this.form = {
 #foreach ($column in $columns)
-#if($column.htmlType == "radio")
-        $column.javaField: #if($column.javaType == "Integer" || $column.javaType == "Long")0#else"0"#end#if($foreach.count != $columns.size()),#end
-
-#elseif($column.htmlType == "checkbox")
+#if($column.htmlType == "checkbox")
         $column.javaField: []#if($foreach.count != $columns.size()),#end
 #else
         $column.javaField: null#if($foreach.count != $columns.size()),#end

+ 21 - 17
ruoyi-generator/src/main/resources/vm/vue/index.vue.vm

@@ -133,9 +133,9 @@
       </el-table-column>
 #elseif($column.list && $column.htmlType == "imageUpload")
       <el-table-column label="${comment}" align="center" prop="${javaField}" width="100">
-          <template slot-scope="scope">
-              <image-preview :src="scope.row.${javaField}" :width="50" :height="50"/>
-          </template>
+        <template slot-scope="scope">
+          <image-preview :src="scope.row.${javaField}" :width="50" :height="50"/>
+        </template>
       </el-table-column>
 #elseif($column.list && $column.dictType && "" != $column.dictType)
       <el-table-column label="${comment}" align="center" prop="${javaField}">
@@ -197,11 +197,11 @@
           <el-input v-model="form.${field}" placeholder="请输入${comment}" />
         </el-form-item>
 #elseif($column.htmlType == "imageUpload")
-        <el-form-item label="${comment}">
+        <el-form-item label="${comment}" prop="${field}">
           <image-upload v-model="form.${field}"/>
         </el-form-item>
 #elseif($column.htmlType == "fileUpload")
-        <el-form-item label="${comment}">
+        <el-form-item label="${comment}" prop="${field}">
           <file-upload v-model="form.${field}"/>
         </el-form-item>
 #elseif($column.htmlType == "editor")
@@ -215,8 +215,11 @@
               v-for="dict in dict.type.${dictType}"
               :key="dict.value"
               :label="dict.label"
-              #if($column.javaType == "Integer" || $column.javaType == "Long"):value="parseInt(dict.value)"#else:value="dict.value"#end
-
+#if($column.javaType == "Integer" || $column.javaType == "Long")
+              :value="parseInt(dict.value)"
+#else
+              :value="dict.value"
+#end
             ></el-option>
           </el-select>
         </el-form-item>
@@ -227,7 +230,7 @@
           </el-select>
         </el-form-item>
 #elseif($column.htmlType == "checkbox" && "" != $dictType)
-        <el-form-item label="${comment}">
+        <el-form-item label="${comment}" prop="${field}">
           <el-checkbox-group v-model="form.${field}">
             <el-checkbox
               v-for="dict in dict.type.${dictType}"
@@ -238,24 +241,27 @@
           </el-checkbox-group>
         </el-form-item>
 #elseif($column.htmlType == "checkbox" && $dictType)
-        <el-form-item label="${comment}">
+        <el-form-item label="${comment}" prop="${field}">
           <el-checkbox-group v-model="form.${field}">
             <el-checkbox>请选择字典生成</el-checkbox>
           </el-checkbox-group>
         </el-form-item>
 #elseif($column.htmlType == "radio" && "" != $dictType)
-        <el-form-item label="${comment}">
+        <el-form-item label="${comment}" prop="${field}">
           <el-radio-group v-model="form.${field}">
             <el-radio
               v-for="dict in dict.type.${dictType}"
               :key="dict.value"
-              #if($column.javaType == "Integer" || $column.javaType == "Long"):label="parseInt(dict.value)"#else:label="dict.value"#end
-
+#if($column.javaType == "Integer" || $column.javaType == "Long")
+              :label="parseInt(dict.value)"
+#else
+              :label="dict.value"
+#end
             >{{dict.label}}</el-radio>
           </el-radio-group>
         </el-form-item>
 #elseif($column.htmlType == "radio" && $dictType)
-        <el-form-item label="${comment}">
+        <el-form-item label="${comment}" prop="${field}">
           <el-radio-group v-model="form.${field}">
             <el-radio label="1">请选择字典生成</el-radio>
           </el-radio-group>
@@ -412,7 +418,7 @@ export default {
 #set($comment=$column.columnComment)
 #end
         $column.javaField: [
-          { required: true, message: "$comment不能为空", trigger: #if($column.htmlType == "select")"change"#else"blur"#end }
+          { required: true, message: "$comment不能为空", trigger: #if($column.htmlType == "select" || $column.htmlType == "radio")"change"#else"blur"#end }
         ]#if($foreach.count != $columns.size()),#end
 #end
 #end
@@ -456,9 +462,7 @@ export default {
     reset() {
       this.form = {
 #foreach ($column in $columns)
-#if($column.htmlType == "radio")
-        $column.javaField: #if($column.javaType == "Integer" || $column.javaType == "Long")0#else"0"#end#if($foreach.count != $columns.size()),#end
-#elseif($column.htmlType == "checkbox")
+#if($column.htmlType == "checkbox")
         $column.javaField: []#if($foreach.count != $columns.size()),#end
 #else
         $column.javaField: undefined#if($foreach.count != $columns.size()),#end

+ 23 - 20
ruoyi-generator/src/main/resources/vm/vue/v3/index-tree.vue.vm

@@ -135,11 +135,11 @@
 #end
 #end
       <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
-        <template #default="scope">
-            <el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['${moduleName}:${businessName}:edit']">修改</el-button>
-            <el-button link type="primary" icon="Plus" @click="handleAdd(scope.row)" v-hasPermi="['${moduleName}:${businessName}:add']">新增</el-button>
-            <el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['${moduleName}:${businessName}:remove']">删除</el-button>
-        </template>
+          <template #default="scope">
+              <el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['${moduleName}:${businessName}:edit']">修改</el-button>
+              <el-button link type="primary" icon="Plus" @click="handleAdd(scope.row)" v-hasPermi="['${moduleName}:${businessName}:add']">新增</el-button>
+              <el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['${moduleName}:${businessName}:remove']">删除</el-button>
+          </template>
       </el-table-column>
     </el-table>
 
@@ -172,11 +172,11 @@
           <el-input v-model="form.${field}" placeholder="请输入${comment}" />
         </el-form-item>
 #elseif($column.htmlType == "imageUpload")
-        <el-form-item label="${comment}">
+        <el-form-item label="${comment}" prop="${field}">
           <image-upload v-model="form.${field}"/>
         </el-form-item>
 #elseif($column.htmlType == "fileUpload")
-        <el-form-item label="${comment}">
+        <el-form-item label="${comment}" prop="${field}">
           <file-upload v-model="form.${field}"/>
         </el-form-item>
 #elseif($column.htmlType == "editor")
@@ -190,8 +190,11 @@
               v-for="dict in ${dictType}"
               :key="dict.value"
               :label="dict.label"
-              #if($column.javaType == "Integer" || $column.javaType == "Long"):value="parseInt(dict.value)"#else:value="dict.value"#end
-
+#if($column.javaType == "Integer" || $column.javaType == "Long")
+              :value="parseInt(dict.value)"
+#else
+              :value="dict.value"
+#end
             ></el-option>
           </el-select>
         </el-form-item>
@@ -202,7 +205,7 @@
           </el-select>
         </el-form-item>
 #elseif($column.htmlType == "checkbox" && "" != $dictType)
-        <el-form-item label="${comment}">
+        <el-form-item label="${comment}" prop="${field}">
           <el-checkbox-group v-model="form.${field}">
             <el-checkbox
               v-for="dict in ${dictType}"
@@ -213,24 +216,27 @@
           </el-checkbox-group>
         </el-form-item>
 #elseif($column.htmlType == "checkbox" && $dictType)
-        <el-form-item label="${comment}">
+        <el-form-item label="${comment}" prop="${field}">
           <el-checkbox-group v-model="form.${field}">
             <el-checkbox>请选择字典生成</el-checkbox>
           </el-checkbox-group>
         </el-form-item>
 #elseif($column.htmlType == "radio" && "" != $dictType)
-        <el-form-item label="${comment}">
+        <el-form-item label="${comment}" prop="${field}">
           <el-radio-group v-model="form.${field}">
             <el-radio
               v-for="dict in ${dictType}"
               :key="dict.value"
-              #if($column.javaType == "Integer" || $column.javaType == "Long"):label="parseInt(dict.value)"#else:label="dict.value"#end
-
+#if($column.javaType == "Integer" || $column.javaType == "Long")
+              :label="parseInt(dict.value)"
+#else
+              :label="dict.value"
+#end
             >{{dict.label}}</el-radio>
           </el-radio-group>
         </el-form-item>
 #elseif($column.htmlType == "radio" && $dictType)
-        <el-form-item label="${comment}">
+        <el-form-item label="${comment}" prop="${field}">
           <el-radio-group v-model="form.${field}">
             <el-radio label="1">请选择字典生成</el-radio>
           </el-radio-group>
@@ -306,7 +312,7 @@ const data = reactive({
 #set($comment=$column.columnComment)
 #end
     $column.javaField: [
-      { required: true, message: "$comment不能为空", trigger: #if($column.htmlType == "select")"change"#else"blur"#end }
+      { required: true, message: "$comment不能为空", trigger: #if($column.htmlType == "select" || $column.htmlType == "radio")"change"#else"blur"#end }
     ]#if($foreach.count != $columns.size()),#end
 #end
 #end
@@ -359,10 +365,7 @@ function cancel() {
 function reset() {
   form.value = {
 #foreach ($column in $columns)
-#if($column.htmlType == "radio")
-    $column.javaField: #if($column.javaType == "Integer" || $column.javaType == "Long")0#else"0"#end#if($foreach.count != $columns.size()),#end
-
-#elseif($column.htmlType == "checkbox")
+#if($column.htmlType == "checkbox")
     $column.javaField: []#if($foreach.count != $columns.size()),#end
 #else
     $column.javaField: null#if($foreach.count != $columns.size()),#end

+ 22 - 18
ruoyi-generator/src/main/resources/vm/vue/v3/index.vue.vm

@@ -147,10 +147,10 @@
 #end
 #end
       <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
-        <template #default="scope">
-            <el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['${moduleName}:${businessName}:edit']">修改</el-button>
-            <el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['${moduleName}:${businessName}:remove']">删除</el-button>
-        </template>
+          <template #default="scope">
+              <el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['${moduleName}:${businessName}:edit']">修改</el-button>
+              <el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['${moduleName}:${businessName}:remove']">删除</el-button>
+          </template>
       </el-table-column>
     </el-table>
 
@@ -180,11 +180,11 @@
           <el-input v-model="form.${field}" placeholder="请输入${comment}" />
         </el-form-item>
 #elseif($column.htmlType == "imageUpload")
-        <el-form-item label="${comment}">
+        <el-form-item label="${comment}" prop="${field}">
           <image-upload v-model="form.${field}"/>
         </el-form-item>
 #elseif($column.htmlType == "fileUpload")
-        <el-form-item label="${comment}">
+        <el-form-item label="${comment}" prop="${field}">
           <file-upload v-model="form.${field}"/>
         </el-form-item>
 #elseif($column.htmlType == "editor")
@@ -198,8 +198,11 @@
               v-for="dict in ${dictType}"
               :key="dict.value"
               :label="dict.label"
-              #if($column.javaType == "Integer" || $column.javaType == "Long"):value="parseInt(dict.value)"#else:value="dict.value"#end
-
+#if($column.javaType == "Integer" || $column.javaType == "Long")
+              :value="parseInt(dict.value)"
+#else
+              :value="dict.value"
+#end
             ></el-option>
           </el-select>
         </el-form-item>
@@ -210,7 +213,7 @@
           </el-select>
         </el-form-item>
 #elseif($column.htmlType == "checkbox" && "" != $dictType)
-        <el-form-item label="${comment}">
+        <el-form-item label="${comment}" prop="${field}">
           <el-checkbox-group v-model="form.${field}">
             <el-checkbox
               v-for="dict in ${dictType}"
@@ -221,24 +224,27 @@
           </el-checkbox-group>
         </el-form-item>
 #elseif($column.htmlType == "checkbox" && $dictType)
-        <el-form-item label="${comment}">
+        <el-form-item label="${comment}" prop="${field}">
           <el-checkbox-group v-model="form.${field}">
             <el-checkbox>请选择字典生成</el-checkbox>
           </el-checkbox-group>
         </el-form-item>
 #elseif($column.htmlType == "radio" && "" != $dictType)
-        <el-form-item label="${comment}">
+        <el-form-item label="${comment}" prop="${field}">
           <el-radio-group v-model="form.${field}">
             <el-radio
               v-for="dict in ${dictType}"
               :key="dict.value"
-              #if($column.javaType == "Integer" || $column.javaType == "Long"):label="parseInt(dict.value)"#else:label="dict.value"#end
-
+#if($column.javaType == "Integer" || $column.javaType == "Long")
+              :label="parseInt(dict.value)"
+#else
+              :label="dict.value"
+#end
             >{{dict.label}}</el-radio>
           </el-radio-group>
         </el-form-item>
 #elseif($column.htmlType == "radio" && $dictType)
-        <el-form-item label="${comment}">
+        <el-form-item label="${comment}" prop="${field}">
           <el-radio-group v-model="form.${field}">
             <el-radio label="1">请选择字典生成</el-radio>
           </el-radio-group>
@@ -387,7 +393,7 @@ const data = reactive({
 #set($comment=$column.columnComment)
 #end
     $column.javaField: [
-      { required: true, message: "$comment不能为空", trigger: #if($column.htmlType == "select")"change"#else"blur"#end }
+      { required: true, message: "$comment不能为空", trigger: #if($column.htmlType == "select" || $column.htmlType == "radio")"change"#else"blur"#end }
     ]#if($foreach.count != $columns.size()),#end
 #end
 #end
@@ -431,9 +437,7 @@ function cancel() {
 function reset() {
   form.value = {
 #foreach ($column in $columns)
-#if($column.htmlType == "radio")
-    $column.javaField: #if($column.javaType == "Integer" || $column.javaType == "Long")0#else"0"#end#if($foreach.count != $columns.size()),#end
-#elseif($column.htmlType == "checkbox")
+#if($column.htmlType == "checkbox")
     $column.javaField: []#if($foreach.count != $columns.size()),#end
 #else
     $column.javaField: null#if($foreach.count != $columns.size()),#end

+ 1 - 1
ruoyi-job/pom.xml

@@ -5,7 +5,7 @@
     <parent>
         <artifactId>ruoyi-vue-plus</artifactId>
         <groupId>com.ruoyi</groupId>
-        <version>4.4.0</version>
+        <version>4.5.0</version>
     </parent>
     <modelVersion>4.0.0</modelVersion>
     <packaging>jar</packaging>

+ 1 - 1
ruoyi-oss/pom.xml

@@ -5,7 +5,7 @@
     <parent>
         <artifactId>ruoyi-vue-plus</artifactId>
         <groupId>com.ruoyi</groupId>
-        <version>4.4.0</version>
+        <version>4.5.0</version>
     </parent>
     <modelVersion>4.0.0</modelVersion>
 

+ 1 - 1
ruoyi-sms/pom.xml

@@ -5,7 +5,7 @@
     <parent>
         <artifactId>ruoyi-vue-plus</artifactId>
         <groupId>com.ruoyi</groupId>
-        <version>4.4.0</version>
+        <version>4.5.0</version>
     </parent>
     <modelVersion>4.0.0</modelVersion>
 

+ 1 - 1
ruoyi-system/pom.xml

@@ -5,7 +5,7 @@
     <parent>
         <artifactId>ruoyi-vue-plus</artifactId>
         <groupId>com.ruoyi</groupId>
-        <version>4.4.0</version>
+        <version>4.5.0</version>
     </parent>
     <modelVersion>4.0.0</modelVersion>
 

+ 46 - 33
ruoyi-system/src/main/java/com/ruoyi/system/service/SysLoginService.java

@@ -5,23 +5,28 @@ import cn.dev33.satoken.secure.BCrypt;
 import cn.dev33.satoken.stp.StpUtil;
 import cn.hutool.core.bean.BeanUtil;
 import cn.hutool.core.util.ObjectUtil;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.ruoyi.common.constant.CacheConstants;
 import com.ruoyi.common.constant.Constants;
+import com.ruoyi.common.core.domain.event.LogininforEvent;
 import com.ruoyi.common.core.domain.dto.RoleDTO;
 import com.ruoyi.common.core.domain.entity.SysUser;
 import com.ruoyi.common.core.domain.model.LoginUser;
 import com.ruoyi.common.core.domain.model.XcxLoginUser;
-import com.ruoyi.common.core.service.LogininforService;
 import com.ruoyi.common.enums.DeviceType;
 import com.ruoyi.common.enums.LoginType;
 import com.ruoyi.common.enums.UserStatus;
-import com.ruoyi.common.exception.user.*;
+import com.ruoyi.common.exception.user.CaptchaException;
+import com.ruoyi.common.exception.user.CaptchaExpireException;
+import com.ruoyi.common.exception.user.UserException;
 import com.ruoyi.common.helper.LoginHelper;
 import com.ruoyi.common.utils.DateUtils;
 import com.ruoyi.common.utils.MessageUtils;
 import com.ruoyi.common.utils.ServletUtils;
 import com.ruoyi.common.utils.StringUtils;
 import com.ruoyi.common.utils.redis.RedisUtils;
+import com.ruoyi.common.utils.spring.SpringUtils;
+import com.ruoyi.system.mapper.SysUserMapper;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.factory.annotation.Value;
@@ -42,9 +47,8 @@ import java.util.function.Supplier;
 @Service
 public class SysLoginService {
 
-    private final ISysUserService userService;
+    private final SysUserMapper userMapper;
     private final ISysConfigService configService;
-    private final LogininforService asyncService;
     private final SysPermissionService permissionService;
 
     @Value("${user.password.maxRetryCount}")
@@ -76,7 +80,7 @@ public class SysLoginService {
         // 生成token
         LoginHelper.loginByDevice(loginUser, DeviceType.PC);
 
-        asyncService.recordLogininfor(username, Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success"), request);
+        recordLogininfor(username, Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success"));
         recordLoginInfo(user.getUserId(), username);
         return StpUtil.getTokenValue();
     }
@@ -85,21 +89,19 @@ public class SysLoginService {
         // 通过手机号查找用户
         SysUser user = loadUserByPhonenumber(phonenumber);
 
-        HttpServletRequest request = ServletUtils.getRequest();
-        checkLogin(LoginType.SMS, user.getUserName(), () -> !validateSmsCode(phonenumber, smsCode, request));
+        checkLogin(LoginType.SMS, user.getUserName(), () -> !validateSmsCode(phonenumber, smsCode));
         // 此处可根据登录用户的数据不同 自行创建 loginUser
         LoginUser loginUser = buildLoginUser(user);
         // 生成token
         LoginHelper.loginByDevice(loginUser, DeviceType.APP);
 
-        asyncService.recordLogininfor(user.getUserName(), Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success"), request);
+        recordLogininfor(user.getUserName(), Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success"));
         recordLoginInfo(user.getUserId(), user.getUserName());
         return StpUtil.getTokenValue();
     }
 
 
     public String xcxLogin(String xcxCode) {
-        HttpServletRequest request = ServletUtils.getRequest();
         // xcxCode 为 小程序调用 wx.login 授权后获取
         // todo 以下自行实现
         // 校验 appid + appsrcret + xcxCode 调用登录凭证校验接口 获取 session_key 与 openid
@@ -115,7 +117,7 @@ public class SysLoginService {
         // 生成token
         LoginHelper.loginByDevice(loginUser, DeviceType.XCX);
 
-        asyncService.recordLogininfor(user.getUserName(), Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success"), request);
+        recordLogininfor(user.getUserName(), Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success"));
         recordLoginInfo(user.getUserId(), user.getUserName());
         return StpUtil.getTokenValue();
     }
@@ -127,18 +129,35 @@ public class SysLoginService {
         try {
             LoginUser loginUser = LoginHelper.getLoginUser();
             StpUtil.logout();
-            asyncService.recordLogininfor(loginUser.getUsername(), Constants.LOGOUT, MessageUtils.message("user.logout.success"), ServletUtils.getRequest());
+            recordLogininfor(loginUser.getUsername(), Constants.LOGOUT, MessageUtils.message("user.logout.success"));
         } catch (NotLoginException ignored) {
         }
     }
 
     /**
+     * 记录登录信息
+     *
+     * @param username 用户名
+     * @param status   状态
+     * @param message  消息内容
+     * @return
+     */
+    private void recordLogininfor(String username, String status, String message) {
+        LogininforEvent logininforEvent = new LogininforEvent();
+        logininforEvent.setUsername(username);
+        logininforEvent.setStatus(status);
+        logininforEvent.setMessage(message);
+        logininforEvent.setRequest(ServletUtils.getRequest());
+        SpringUtils.context().publishEvent(logininforEvent);
+    }
+
+    /**
      * 校验短信验证码
      */
-    private boolean validateSmsCode(String phonenumber, String smsCode, HttpServletRequest request) {
+    private boolean validateSmsCode(String phonenumber, String smsCode) {
         String code = RedisUtils.getCacheObject(CacheConstants.CAPTCHA_CODE_KEY + phonenumber);
         if (StringUtils.isBlank(code)) {
-            asyncService.recordLogininfor(phonenumber, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.expire"), request);
+            recordLogininfor(phonenumber, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.expire"));
             throw new CaptchaExpireException();
         }
         return code.equals(smsCode);
@@ -156,43 +175,41 @@ public class SysLoginService {
         String captcha = RedisUtils.getCacheObject(verifyKey);
         RedisUtils.deleteObject(verifyKey);
         if (captcha == null) {
-            asyncService.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.expire"), request);
+            recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.expire"));
             throw new CaptchaExpireException();
         }
         if (!code.equalsIgnoreCase(captcha)) {
-            asyncService.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.error"), request);
+            recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.error"));
             throw new CaptchaException();
         }
     }
 
     private SysUser loadUserByUsername(String username) {
-        SysUser user = userService.selectUserByUserName(username);
+        SysUser user = userMapper.selectOne(new LambdaQueryWrapper<SysUser>()
+            .select(SysUser::getUserName, SysUser::getStatus)
+            .eq(SysUser::getUserName, username));
         if (ObjectUtil.isNull(user)) {
             log.info("登录用户:{} 不存在.", username);
             throw new UserException("user.not.exists", username);
-        } else if (UserStatus.DELETED.getCode().equals(user.getDelFlag())) {
-            log.info("登录用户:{} 已被删除.", username);
-            throw new UserException("user.password.delete", username);
         } else if (UserStatus.DISABLE.getCode().equals(user.getStatus())) {
             log.info("登录用户:{} 已被停用.", username);
             throw new UserException("user.blocked", username);
         }
-        return user;
+        return userMapper.selectUserByUserName(username);
     }
 
     private SysUser loadUserByPhonenumber(String phonenumber) {
-        SysUser user = userService.selectUserByPhonenumber(phonenumber);
+        SysUser user = userMapper.selectOne(new LambdaQueryWrapper<SysUser>()
+            .select(SysUser::getPhonenumber, SysUser::getStatus)
+            .eq(SysUser::getPhonenumber, phonenumber));
         if (ObjectUtil.isNull(user)) {
             log.info("登录用户:{} 不存在.", phonenumber);
             throw new UserException("user.not.exists", phonenumber);
-        } else if (UserStatus.DELETED.getCode().equals(user.getDelFlag())) {
-            log.info("登录用户:{} 已被删除.", phonenumber);
-            throw new UserException("user.password.delete", phonenumber);
         } else if (UserStatus.DISABLE.getCode().equals(user.getStatus())) {
             log.info("登录用户:{} 已被停用.", phonenumber);
             throw new UserException("user.blocked", phonenumber);
         }
-        return user;
+        return userMapper.selectUserByPhonenumber(phonenumber);
     }
 
     private SysUser loadUserByOpenid(String openid) {
@@ -202,9 +219,6 @@ public class SysLoginService {
         if (ObjectUtil.isNull(user)) {
             log.info("登录用户:{} 不存在.", openid);
             // todo 用户不存在 业务逻辑自行实现
-        } else if (UserStatus.DELETED.getCode().equals(user.getDelFlag())) {
-            log.info("登录用户:{} 已被删除.", openid);
-            // todo 用户已被删除 业务逻辑自行实现
         } else if (UserStatus.DISABLE.getCode().equals(user.getStatus())) {
             log.info("登录用户:{} 已被停用.", openid);
             // todo 用户已被停用 业务逻辑自行实现
@@ -240,14 +254,13 @@ public class SysLoginService {
         sysUser.setLoginIp(ServletUtils.getClientIP());
         sysUser.setLoginDate(DateUtils.getNowDate());
         sysUser.setUpdateBy(username);
-        userService.updateUserProfile(sysUser);
+        userMapper.updateById(sysUser);
     }
 
     /**
      * 登录校验
      */
     private void checkLogin(LoginType loginType, String username, Supplier<Boolean> supplier) {
-        HttpServletRequest request = ServletUtils.getRequest();
         String errorKey = CacheConstants.PWD_ERR_CNT_KEY + username;
         String loginFail = Constants.LOGIN_FAIL;
 
@@ -255,7 +268,7 @@ public class SysLoginService {
         Integer errorNumber = RedisUtils.getCacheObject(errorKey);
         // 锁定时间内登录 则踢出
         if (ObjectUtil.isNotNull(errorNumber) && errorNumber.equals(maxRetryCount)) {
-            asyncService.recordLogininfor(username, loginFail, MessageUtils.message(loginType.getRetryLimitExceed(), maxRetryCount, lockTime), request);
+            recordLogininfor(username, loginFail, MessageUtils.message(loginType.getRetryLimitExceed(), maxRetryCount, lockTime));
             throw new UserException(loginType.getRetryLimitExceed(), maxRetryCount, lockTime);
         }
 
@@ -265,12 +278,12 @@ public class SysLoginService {
             // 达到规定错误次数 则锁定登录
             if (errorNumber.equals(maxRetryCount)) {
                 RedisUtils.setCacheObject(errorKey, errorNumber, Duration.ofMinutes(lockTime));
-                asyncService.recordLogininfor(username, loginFail, MessageUtils.message(loginType.getRetryLimitExceed(), maxRetryCount, lockTime), request);
+                recordLogininfor(username, loginFail, MessageUtils.message(loginType.getRetryLimitExceed(), maxRetryCount, lockTime));
                 throw new UserException(loginType.getRetryLimitExceed(), maxRetryCount, lockTime);
             } else {
                 // 未达到规定错误次数 则递增
                 RedisUtils.setCacheObject(errorKey, errorNumber);
-                asyncService.recordLogininfor(username, loginFail, MessageUtils.message(loginType.getRetryLimitCount(), errorNumber), request);
+                recordLogininfor(username, loginFail, MessageUtils.message(loginType.getRetryLimitCount(), errorNumber));
                 throw new UserException(loginType.getRetryLimitCount(), errorNumber);
             }
         }

+ 23 - 5
ruoyi-system/src/main/java/com/ruoyi/system/service/SysRegisterService.java

@@ -4,9 +4,9 @@ import cn.dev33.satoken.secure.BCrypt;
 import com.ruoyi.common.constant.CacheConstants;
 import com.ruoyi.common.constant.Constants;
 import com.ruoyi.common.constant.UserConstants;
+import com.ruoyi.common.core.domain.event.LogininforEvent;
 import com.ruoyi.common.core.domain.entity.SysUser;
 import com.ruoyi.common.core.domain.model.RegisterBody;
-import com.ruoyi.common.core.service.LogininforService;
 import com.ruoyi.common.enums.UserType;
 import com.ruoyi.common.exception.user.CaptchaException;
 import com.ruoyi.common.exception.user.CaptchaExpireException;
@@ -15,6 +15,7 @@ import com.ruoyi.common.utils.MessageUtils;
 import com.ruoyi.common.utils.ServletUtils;
 import com.ruoyi.common.utils.StringUtils;
 import com.ruoyi.common.utils.redis.RedisUtils;
+import com.ruoyi.common.utils.spring.SpringUtils;
 import lombok.RequiredArgsConstructor;
 import org.springframework.stereotype.Service;
 
@@ -31,7 +32,6 @@ public class SysRegisterService {
 
     private final ISysUserService userService;
     private final ISysConfigService configService;
-    private final LogininforService asyncService;
 
     /**
      * 注册
@@ -61,7 +61,7 @@ public class SysRegisterService {
         if (!regFlag) {
             throw new UserException("user.register.error");
         }
-        asyncService.recordLogininfor(username, Constants.REGISTER, MessageUtils.message("user.register.success"), request);
+        recordLogininfor(username, Constants.REGISTER, MessageUtils.message("user.register.success"));
     }
 
     /**
@@ -77,12 +77,30 @@ public class SysRegisterService {
         String captcha = RedisUtils.getCacheObject(verifyKey);
         RedisUtils.deleteObject(verifyKey);
         if (captcha == null) {
-            asyncService.recordLogininfor(username, Constants.REGISTER, MessageUtils.message("user.jcaptcha.expire"), request);
+            recordLogininfor(username, Constants.REGISTER, MessageUtils.message("user.jcaptcha.expire"));
             throw new CaptchaExpireException();
         }
         if (!code.equalsIgnoreCase(captcha)) {
-            asyncService.recordLogininfor(username, Constants.REGISTER, MessageUtils.message("user.jcaptcha.error"), request);
+            recordLogininfor(username, Constants.REGISTER, MessageUtils.message("user.jcaptcha.error"));
             throw new CaptchaException();
         }
     }
+
+    /**
+     * 记录登录信息
+     *
+     * @param username 用户名
+     * @param status   状态
+     * @param message  消息内容
+     * @return
+     */
+    private void recordLogininfor(String username, String status, String message) {
+        LogininforEvent logininforEvent = new LogininforEvent();
+        logininforEvent.setUsername(username);
+        logininforEvent.setStatus(status);
+        logininforEvent.setMessage(message);
+        logininforEvent.setRequest(ServletUtils.getRequest());
+        SpringUtils.context().publishEvent(logininforEvent);
+    }
+
 }

+ 4 - 0
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysConfigServiceImpl.java

@@ -138,6 +138,10 @@ public class SysConfigServiceImpl implements ISysConfigService, ConfigService {
     public String updateConfig(SysConfig config) {
         int row = 0;
         if (config.getConfigId() != null) {
+            SysConfig temp = baseMapper.selectById(config.getConfigId());
+            if (!StringUtils.equals(temp.getConfigKey(), config.getConfigKey())) {
+                CacheUtils.evict(CacheNames.SYS_CONFIG, temp.getConfigKey());
+            }
             row = baseMapper.updateById(config);
         } else {
             row = baseMapper.update(config, new LambdaQueryWrapper<SysConfig>()

+ 15 - 17
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysLogininforServiceImpl.java

@@ -6,8 +6,8 @@ import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.ruoyi.common.constant.Constants;
 import com.ruoyi.common.core.domain.PageQuery;
+import com.ruoyi.common.core.domain.event.LogininforEvent;
 import com.ruoyi.common.core.page.TableDataInfo;
-import com.ruoyi.common.core.service.LogininforService;
 import com.ruoyi.common.utils.ServletUtils;
 import com.ruoyi.common.utils.StringUtils;
 import com.ruoyi.common.utils.ip.AddressUtils;
@@ -16,6 +16,7 @@ import com.ruoyi.system.mapper.SysLogininforMapper;
 import com.ruoyi.system.service.ISysLogininforService;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
+import org.springframework.context.event.EventListener;
 import org.springframework.scheduling.annotation.Async;
 import org.springframework.stereotype.Service;
 
@@ -33,22 +34,19 @@ import java.util.Map;
 @RequiredArgsConstructor
 @Slf4j
 @Service
-public class SysLogininforServiceImpl implements ISysLogininforService, LogininforService {
+public class SysLogininforServiceImpl implements ISysLogininforService {
 
     private final SysLogininforMapper baseMapper;
 
     /**
      * 记录登录信息
      *
-     * @param username 用户名
-     * @param status   状态
-     * @param message  消息
-     * @param args     列表
+     * @param logininforEvent 登录事件
      */
     @Async
-    @Override
-    public void recordLogininfor(final String username, final String status, final String message,
-                                 HttpServletRequest request, final Object... args) {
+    @EventListener
+    public void recordLogininfor(LogininforEvent logininforEvent) {
+        HttpServletRequest request = logininforEvent.getRequest();
         final UserAgent userAgent = UserAgentUtil.parse(request.getHeader("User-Agent"));
         final String ip = ServletUtils.getClientIP(request);
 
@@ -56,27 +54,27 @@ public class SysLogininforServiceImpl implements ISysLogininforService, Logininf
         StringBuilder s = new StringBuilder();
         s.append(getBlock(ip));
         s.append(address);
-        s.append(getBlock(username));
-        s.append(getBlock(status));
-        s.append(getBlock(message));
+        s.append(getBlock(logininforEvent.getUsername()));
+        s.append(getBlock(logininforEvent.getStatus()));
+        s.append(getBlock(logininforEvent.getMessage()));
         // 打印信息到日志
-        log.info(s.toString(), args);
+        log.info(s.toString(), logininforEvent.getArgs());
         // 获取客户端操作系统
         String os = userAgent.getOs().getName();
         // 获取客户端浏览器
         String browser = userAgent.getBrowser().getName();
         // 封装对象
         SysLogininfor logininfor = new SysLogininfor();
-        logininfor.setUserName(username);
+        logininfor.setUserName(logininforEvent.getUsername());
         logininfor.setIpaddr(ip);
         logininfor.setLoginLocation(address);
         logininfor.setBrowser(browser);
         logininfor.setOs(os);
-        logininfor.setMsg(message);
+        logininfor.setMsg(logininforEvent.getMessage());
         // 日志状态
-        if (StringUtils.equalsAny(status, Constants.LOGIN_SUCCESS, Constants.LOGOUT, Constants.REGISTER)) {
+        if (StringUtils.equalsAny(logininforEvent.getStatus(), Constants.LOGIN_SUCCESS, Constants.LOGOUT, Constants.REGISTER)) {
             logininfor.setStatus(Constants.SUCCESS);
-        } else if (Constants.LOGIN_FAIL.equals(status)) {
+        } else if (Constants.LOGIN_FAIL.equals(logininforEvent.getStatus())) {
             logininfor.setStatus(Constants.FAIL);
         }
         // 插入数据

+ 7 - 7
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysOperLogServiceImpl.java

@@ -5,15 +5,15 @@ import cn.hutool.core.util.ArrayUtil;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.ruoyi.common.core.domain.PageQuery;
-import com.ruoyi.common.core.domain.dto.OperLogDTO;
+import com.ruoyi.common.core.domain.event.OperLogEvent;
 import com.ruoyi.common.core.page.TableDataInfo;
-import com.ruoyi.common.core.service.OperLogService;
 import com.ruoyi.common.utils.StringUtils;
 import com.ruoyi.common.utils.ip.AddressUtils;
 import com.ruoyi.system.domain.SysOperLog;
 import com.ruoyi.system.mapper.SysOperLogMapper;
 import com.ruoyi.system.service.ISysOperLogService;
 import lombok.RequiredArgsConstructor;
+import org.springframework.context.event.EventListener;
 import org.springframework.scheduling.annotation.Async;
 import org.springframework.stereotype.Service;
 
@@ -29,19 +29,19 @@ import java.util.Map;
  */
 @RequiredArgsConstructor
 @Service
-public class SysOperLogServiceImpl implements ISysOperLogService, OperLogService {
+public class SysOperLogServiceImpl implements ISysOperLogService {
 
     private final SysOperLogMapper baseMapper;
 
     /**
      * 操作日志记录
      *
-     * @param operLogDTO 操作日志信息
+     * @param operLogEvent 操作日志事件
      */
     @Async
-    @Override
-    public void recordOper(final OperLogDTO operLogDTO) {
-        SysOperLog operLog = BeanUtil.toBean(operLogDTO, SysOperLog.class);
+    @EventListener
+    public void recordOper(OperLogEvent operLogEvent) {
+        SysOperLog operLog = BeanUtil.toBean(operLogEvent, SysOperLog.class);
         // 远程查询操作地点
         operLog.setOperLocation(AddressUtils.getRealAddressByIP(operLog.getOperIp()));
         insertOperlog(operLog);

+ 20 - 14
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysOssConfigServiceImpl.java

@@ -16,6 +16,7 @@ import com.ruoyi.common.utils.JsonUtils;
 import com.ruoyi.common.utils.StringUtils;
 import com.ruoyi.common.utils.redis.CacheUtils;
 import com.ruoyi.common.utils.redis.RedisUtils;
+import com.ruoyi.common.utils.spring.SpringUtils;
 import com.ruoyi.oss.constant.OssConstant;
 import com.ruoyi.oss.factory.OssFactory;
 import com.ruoyi.system.domain.SysOssConfig;
@@ -25,6 +26,7 @@ import com.ruoyi.system.mapper.SysOssConfigMapper;
 import com.ruoyi.system.service.ISysOssConfigService;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
+import org.springframework.context.event.EventListener;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 
@@ -57,7 +59,7 @@ public class SysOssConfigServiceImpl implements ISysOssConfigService {
             if ("0".equals(config.getStatus())) {
                 RedisUtils.setCacheObject(OssConstant.DEFAULT_CONFIG_KEY, configKey);
             }
-            setConfigCache(true, config);
+            SpringUtils.context().publishEvent(config);
         }
         // 初始化OSS工厂
         OssFactory.init();
@@ -88,7 +90,11 @@ public class SysOssConfigServiceImpl implements ISysOssConfigService {
     public Boolean insertByBo(SysOssConfigBo bo) {
         SysOssConfig config = BeanUtil.toBean(bo, SysOssConfig.class);
         validEntityBeforeSave(config);
-        return setConfigCache(baseMapper.insert(config) > 0, config);
+        boolean flag = baseMapper.insert(config) > 0;
+        if (flag) {
+            SpringUtils.context().publishEvent(config);
+        }
+        return flag;
     }
 
     @Override
@@ -101,7 +107,11 @@ public class SysOssConfigServiceImpl implements ISysOssConfigService {
         luw.set(ObjectUtil.isNull(config.getExt1()), SysOssConfig::getExt1, "");
         luw.set(ObjectUtil.isNull(config.getRemark()), SysOssConfig::getRemark, "");
         luw.eq(SysOssConfig::getOssConfigId, config.getOssConfigId());
-        return setConfigCache(baseMapper.update(config, luw) > 0, config);
+        boolean flag = baseMapper.update(config, luw) > 0;
+        if (flag) {
+            SpringUtils.context().publishEvent(config);
+        }
+        return flag;
     }
 
     /**
@@ -165,19 +175,15 @@ public class SysOssConfigServiceImpl implements ISysOssConfigService {
     }
 
     /**
-     * 如果操作成功 则更新缓存
+     * 更新配置缓存
      *
-     * @param flag   操作状态
      * @param config 配置
-     * @return 返回操作状态
      */
-    private boolean setConfigCache(boolean flag, SysOssConfig config) {
-        if (flag) {
-            CacheUtils.put(CacheNames.SYS_OSS_CONFIG, config.getConfigKey(), JsonUtils.toJsonString(config));
-            RedisUtils.publish(OssConstant.DEFAULT_CONFIG_KEY, config.getConfigKey(), msg -> {
-                log.info("发布刷新OSS配置 => " + msg);
-            });
-        }
-        return flag;
+    @EventListener
+    public void updateConfigCache(SysOssConfig config) {
+        CacheUtils.put(CacheNames.SYS_OSS_CONFIG, config.getConfigKey(), JsonUtils.toJsonString(config));
+        RedisUtils.publish(OssConstant.DEFAULT_CONFIG_KEY, config.getConfigKey(), msg -> {
+            log.info("发布刷新OSS配置 => " + msg);
+        });
     }
 }

+ 2 - 2
ruoyi-ui/package.json

@@ -1,6 +1,6 @@
 {
   "name": "ruoyi-vue-plus",
-  "version": "4.4.0",
+  "version": "4.5.0",
   "description": "RuoYi-Vue-Plus后台管理系统",
   "author": "LionLi",
   "license": "MIT",
@@ -39,7 +39,7 @@
     "axios": "0.24.0",
     "clipboard": "2.0.8",
     "core-js": "3.25.3",
-    "echarts": "4.9.0",
+    "echarts": "5.4.0",
     "element-ui": "2.15.10",
     "file-saver": "2.0.5",
     "fuse.js": "6.4.3",

+ 4 - 0
ruoyi-ui/src/assets/styles/ruoyi.scss

@@ -73,6 +73,10 @@
   color: inherit;
 }
 
+.el-message-box__status + .el-message-box__message{
+  word-break: break-word;
+}
+
 .el-dialog:not(.is-fullscreen) {
   margin-top: 6vh !important;
 }

+ 0 - 1
ruoyi-ui/src/components/HeaderSearch/index.vue

@@ -90,7 +90,6 @@ export default {
         threshold: 0.4,
         location: 0,
         distance: 100,
-        maxPatternLength: 32,
         minMatchCharLength: 1,
         keys: [{
           name: 'title',

+ 0 - 6
ruoyi-ui/src/components/RightPanel/index.vue

@@ -39,7 +39,6 @@ export default {
     }
   },
   mounted() {
-    this.insertToBody()
     this.addEventClick()
   },
   beforeDestroy() {
@@ -56,11 +55,6 @@ export default {
         this.show = false
         window.removeEventListener('click', this.closeSidebar)
       }
-    },
-    insertToBody() {
-      const elx = this.$refs.rightPanel
-      const body = document.querySelector('body')
-      body.insertBefore(elx, body.firstChild)
     }
   }
 }

+ 1 - 1
ruoyi-ui/src/plugins/download.js

@@ -21,7 +21,7 @@ export default {
       const isLogin = await blobValidate(res.data);
       if (isLogin) {
         const blob = new Blob([res.data], { type: 'application/octet-stream' })
-        this.saveAs(blob, decodeURI(res.headers['download-filename']))
+        this.saveAs(blob, decodeURIComponent(res.headers['download-filename']))
       } else {
         this.printErrMsg(res.data);
       }

+ 1 - 1
ruoyi-ui/src/views/dashboard/BarChart.vue

@@ -3,7 +3,7 @@
 </template>
 
 <script>
-import echarts from 'echarts'
+import * as echarts from 'echarts'
 require('echarts/theme/macarons') // echarts theme
 import resize from './mixins/resize'
 

+ 1 - 1
ruoyi-ui/src/views/dashboard/LineChart.vue

@@ -3,7 +3,7 @@
 </template>
 
 <script>
-import echarts from 'echarts'
+import * as echarts from 'echarts'
 require('echarts/theme/macarons') // echarts theme
 import resize from './mixins/resize'
 

+ 1 - 1
ruoyi-ui/src/views/dashboard/PieChart.vue

@@ -3,7 +3,7 @@
 </template>
 
 <script>
-import echarts from 'echarts'
+import * as echarts from 'echarts'
 require('echarts/theme/macarons') // echarts theme
 import resize from './mixins/resize'
 

+ 1 - 1
ruoyi-ui/src/views/dashboard/RaddarChart.vue

@@ -3,7 +3,7 @@
 </template>
 
 <script>
-import echarts from 'echarts'
+import * as echarts from 'echarts'
 require('echarts/theme/macarons') // echarts theme
 import resize from './mixins/resize'
 

+ 1 - 1
ruoyi-ui/src/views/index.vue

@@ -114,7 +114,7 @@ export default {
   data() {
     return {
       // 版本号
-      version: "4.4.0",
+      version: "4.5.0",
     };
   },
   methods: {

+ 146 - 146
ruoyi-ui/src/views/monitor/cache/index.vue

@@ -1,146 +1,146 @@
-<template>
-  <div class="app-container">
-    <el-row>
-      <el-col :span="24" class="card-box">
-        <el-card>
-          <div slot="header"><span>基本信息</span></div>
-          <div class="el-table el-table--enable-row-hover el-table--medium">
-            <table cellspacing="0" style="width: 100%">
-              <tbody>
-                <tr>
-                  <td class="el-table__cell is-leaf"><div class="cell">Redis版本</div></td>
-                  <td class="el-table__cell is-leaf"><div class="cell" v-if="cache.info">{{ cache.info.redis_version }}</div></td>
-                  <td class="el-table__cell is-leaf"><div class="cell">运行模式</div></td>
-                  <td class="el-table__cell is-leaf"><div class="cell" v-if="cache.info">{{ cache.info.redis_mode == "standalone" ? "单机" : "集群" }}</div></td>
-                  <td class="el-table__cell is-leaf"><div class="cell">端口</div></td>
-                  <td class="el-table__cell is-leaf"><div class="cell" v-if="cache.info">{{ cache.info.tcp_port }}</div></td>
-                  <td class="el-table__cell is-leaf"><div class="cell">客户端数</div></td>
-                  <td class="el-table__cell is-leaf"><div class="cell" v-if="cache.info">{{ cache.info.connected_clients }}</div></td>
-                </tr>
-                <tr>
-                  <td class="el-table__cell is-leaf"><div class="cell">运行时间(天)</div></td>
-                  <td class="el-table__cell is-leaf"><div class="cell" v-if="cache.info">{{ cache.info.uptime_in_days }}</div></td>
-                  <td class="el-table__cell is-leaf"><div class="cell">使用内存</div></td>
-                  <td class="el-table__cell is-leaf"><div class="cell" v-if="cache.info">{{ cache.info.used_memory_human }}</div></td>
-                  <td class="el-table__cell is-leaf"><div class="cell">使用CPU</div></td>
-                  <td class="el-table__cell is-leaf"><div class="cell" v-if="cache.info">{{ parseFloat(cache.info.used_cpu_user_children).toFixed(2) }}</div></td>
-                  <td class="el-table__cell is-leaf"><div class="cell">内存配置</div></td>
-                  <td class="el-table__cell is-leaf"><div class="cell" v-if="cache.info">{{ cache.info.maxmemory_human }}</div></td>
-                </tr>
-                <tr>
-                  <td class="el-table__cell is-leaf"><div class="cell">AOF是否开启</div></td>
-                  <td class="el-table__cell is-leaf"><div class="cell" v-if="cache.info">{{ cache.info.aof_enabled == "0" ? "否" : "是" }}</div></td>
-                  <td class="el-table__cell is-leaf"><div class="cell">RDB是否成功</div></td>
-                  <td class="el-table__cell is-leaf"><div class="cell" v-if="cache.info">{{ cache.info.rdb_last_bgsave_status }}</div></td>
-                  <td class="el-table__cell is-leaf"><div class="cell">Key数量</div></td>
-                  <td class="el-table__cell is-leaf"><div class="cell" v-if="cache.dbSize">{{ cache.dbSize }} </div></td>
-                  <td class="el-table__cell is-leaf"><div class="cell">网络入口/出口</div></td>
-                  <td class="el-table__cell is-leaf"><div class="cell" v-if="cache.info">{{ cache.info.instantaneous_input_kbps }}kps/{{cache.info.instantaneous_output_kbps}}kps</div></td>
-                </tr>
-              </tbody>
-            </table>
-          </div>
-        </el-card>
-      </el-col>
-
-      <el-col :span="12" class="card-box">
-        <el-card>
-          <div slot="header"><span>命令统计</span></div>
-          <div class="el-table el-table--enable-row-hover el-table--medium">
-            <div ref="commandstats" style="height: 420px" />
-          </div>
-        </el-card>
-      </el-col>
-
-      <el-col :span="12" class="card-box">
-        <el-card>
-          <div slot="header">
-            <span>内存信息</span>
-          </div>
-          <div class="el-table el-table--enable-row-hover el-table--medium">
-            <div ref="usedmemory" style="height: 420px" />
-          </div>
-        </el-card>
-      </el-col>
-    </el-row>
-  </div>
-</template>
-
-<script>
-import { getCache } from "@/api/monitor/cache";
-import echarts from "echarts";
-
-export default {
-  name: "Cache",
-  data() {
-    return {
-      // 统计命令信息
-      commandstats: null,
-      // 使用内存
-      usedmemory: null,
-      // cache信息
-      cache: []
-    }
-  },
-  created() {
-    this.getList();
-    this.openLoading();
-  },
-  methods: {
-    /** 查缓存询信息 */
-    getList() {
-      getCache().then((response) => {
-        this.cache = response.data;
-        this.$modal.closeLoading();
-
-        this.commandstats = echarts.init(this.$refs.commandstats, "macarons");
-        this.commandstats.setOption({
-          tooltip: {
-            trigger: "item",
-            formatter: "{a} <br/>{b} : {c} ({d}%)",
-          },
-          series: [
-            {
-              name: "命令",
-              type: "pie",
-              roseType: "radius",
-              radius: [15, 95],
-              center: ["50%", "38%"],
-              data: response.data.commandStats,
-              animationEasing: "cubicInOut",
-              animationDuration: 1000,
-            }
-          ]
-        });
-        this.usedmemory = echarts.init(this.$refs.usedmemory, "macarons");
-        this.usedmemory.setOption({
-          tooltip: {
-            formatter: "{b} <br/>{a} : " + this.cache.info.used_memory_human,
-          },
-          series: [
-            {
-              name: "峰值",
-              type: "gauge",
-              min: 0,
-              max: 1000,
-              detail: {
-                formatter: this.cache.info.used_memory_human,
-              },
-              data: [
-                {
-                  value: parseFloat(this.cache.info.used_memory_human),
-                  name: "内存消耗",
-                }
-              ]
-            }
-          ]
-        });
-      });
-    },
-    // 打开加载层
-    openLoading() {
-      this.$modal.loading("正在加载缓存监控数据,请稍候!");
-    }
-  }
-};
-</script>
+<template>
+  <div class="app-container">
+    <el-row>
+      <el-col :span="24" class="card-box">
+        <el-card>
+          <div slot="header"><span>基本信息</span></div>
+          <div class="el-table el-table--enable-row-hover el-table--medium">
+            <table cellspacing="0" style="width: 100%">
+              <tbody>
+                <tr>
+                  <td class="el-table__cell is-leaf"><div class="cell">Redis版本</div></td>
+                  <td class="el-table__cell is-leaf"><div class="cell" v-if="cache.info">{{ cache.info.redis_version }}</div></td>
+                  <td class="el-table__cell is-leaf"><div class="cell">运行模式</div></td>
+                  <td class="el-table__cell is-leaf"><div class="cell" v-if="cache.info">{{ cache.info.redis_mode == "standalone" ? "单机" : "集群" }}</div></td>
+                  <td class="el-table__cell is-leaf"><div class="cell">端口</div></td>
+                  <td class="el-table__cell is-leaf"><div class="cell" v-if="cache.info">{{ cache.info.tcp_port }}</div></td>
+                  <td class="el-table__cell is-leaf"><div class="cell">客户端数</div></td>
+                  <td class="el-table__cell is-leaf"><div class="cell" v-if="cache.info">{{ cache.info.connected_clients }}</div></td>
+                </tr>
+                <tr>
+                  <td class="el-table__cell is-leaf"><div class="cell">运行时间(天)</div></td>
+                  <td class="el-table__cell is-leaf"><div class="cell" v-if="cache.info">{{ cache.info.uptime_in_days }}</div></td>
+                  <td class="el-table__cell is-leaf"><div class="cell">使用内存</div></td>
+                  <td class="el-table__cell is-leaf"><div class="cell" v-if="cache.info">{{ cache.info.used_memory_human }}</div></td>
+                  <td class="el-table__cell is-leaf"><div class="cell">使用CPU</div></td>
+                  <td class="el-table__cell is-leaf"><div class="cell" v-if="cache.info">{{ parseFloat(cache.info.used_cpu_user_children).toFixed(2) }}</div></td>
+                  <td class="el-table__cell is-leaf"><div class="cell">内存配置</div></td>
+                  <td class="el-table__cell is-leaf"><div class="cell" v-if="cache.info">{{ cache.info.maxmemory_human }}</div></td>
+                </tr>
+                <tr>
+                  <td class="el-table__cell is-leaf"><div class="cell">AOF是否开启</div></td>
+                  <td class="el-table__cell is-leaf"><div class="cell" v-if="cache.info">{{ cache.info.aof_enabled == "0" ? "否" : "是" }}</div></td>
+                  <td class="el-table__cell is-leaf"><div class="cell">RDB是否成功</div></td>
+                  <td class="el-table__cell is-leaf"><div class="cell" v-if="cache.info">{{ cache.info.rdb_last_bgsave_status }}</div></td>
+                  <td class="el-table__cell is-leaf"><div class="cell">Key数量</div></td>
+                  <td class="el-table__cell is-leaf"><div class="cell" v-if="cache.dbSize">{{ cache.dbSize }} </div></td>
+                  <td class="el-table__cell is-leaf"><div class="cell">网络入口/出口</div></td>
+                  <td class="el-table__cell is-leaf"><div class="cell" v-if="cache.info">{{ cache.info.instantaneous_input_kbps }}kps/{{cache.info.instantaneous_output_kbps}}kps</div></td>
+                </tr>
+              </tbody>
+            </table>
+          </div>
+        </el-card>
+      </el-col>
+
+      <el-col :span="12" class="card-box">
+        <el-card>
+          <div slot="header"><span>命令统计</span></div>
+          <div class="el-table el-table--enable-row-hover el-table--medium">
+            <div ref="commandstats" style="height: 420px" />
+          </div>
+        </el-card>
+      </el-col>
+
+      <el-col :span="12" class="card-box">
+        <el-card>
+          <div slot="header">
+            <span>内存信息</span>
+          </div>
+          <div class="el-table el-table--enable-row-hover el-table--medium">
+            <div ref="usedmemory" style="height: 420px" />
+          </div>
+        </el-card>
+      </el-col>
+    </el-row>
+  </div>
+</template>
+
+<script>
+import { getCache } from "@/api/monitor/cache";
+import * as echarts from "echarts";
+
+export default {
+  name: "Cache",
+  data() {
+    return {
+      // 统计命令信息
+      commandstats: null,
+      // 使用内存
+      usedmemory: null,
+      // cache信息
+      cache: []
+    }
+  },
+  created() {
+    this.getList();
+    this.openLoading();
+  },
+  methods: {
+    /** 查缓存询信息 */
+    getList() {
+      getCache().then((response) => {
+        this.cache = response.data;
+        this.$modal.closeLoading();
+
+        this.commandstats = echarts.init(this.$refs.commandstats, "macarons");
+        this.commandstats.setOption({
+          tooltip: {
+            trigger: "item",
+            formatter: "{a} <br/>{b} : {c} ({d}%)",
+          },
+          series: [
+            {
+              name: "命令",
+              type: "pie",
+              roseType: "radius",
+              radius: [15, 95],
+              center: ["50%", "38%"],
+              data: response.data.commandStats,
+              animationEasing: "cubicInOut",
+              animationDuration: 1000,
+            }
+          ]
+        });
+        this.usedmemory = echarts.init(this.$refs.usedmemory, "macarons");
+        this.usedmemory.setOption({
+          tooltip: {
+            formatter: "{b} <br/>{a} : " + this.cache.info.used_memory_human,
+          },
+          series: [
+            {
+              name: "峰值",
+              type: "gauge",
+              min: 0,
+              max: 1000,
+              detail: {
+                formatter: this.cache.info.used_memory_human,
+              },
+              data: [
+                {
+                  value: parseFloat(this.cache.info.used_memory_human),
+                  name: "内存消耗",
+                }
+              ]
+            }
+          ]
+        });
+      });
+    },
+    // 打开加载层
+    openLoading() {
+      this.$modal.loading("正在加载缓存监控数据,请稍候!");
+    }
+  }
+};
+</script>

+ 1 - 1
ruoyi-ui/src/views/register.vue

@@ -87,7 +87,7 @@ export default {
         confirmPassword: "",
         code: "",
         uuid: "",
-        user_type: "sys_user"
+        userType: "sys_user"
       },
       registerRules: {
         username: [

+ 6 - 6
script/docker/docker-compose.yml

@@ -78,9 +78,9 @@ services:
       # 时区上海
       TZ: Asia/Shanghai
       # 管理后台用户名
-      MINIO_ACCESS_KEY: ruoyi
+      MINIO_ROOT_USER: ruoyi
       # 管理后台密码,最小8个字符
-      MINIO_SECRET_KEY: ruoyi123
+      MINIO_ROOT_PASSWORD: ruoyi123
       # https需要指定域名
       #MINIO_SERVER_URL: "https://xxx.com:9000"
       #MINIO_BROWSER_REDIRECT_URL: "https://xxx.com:9001"
@@ -100,7 +100,7 @@ services:
     network_mode: "host"
 
   ruoyi-server1:
-    image: ruoyi/ruoyi-server:4.4.0
+    image: ruoyi/ruoyi-server:4.5.0
     container_name: ruoyi-server1
     environment:
       # 时区上海
@@ -115,7 +115,7 @@ services:
     network_mode: "host"
 
   ruoyi-server2:
-    image: "ruoyi/ruoyi-server:4.4.0"
+    image: "ruoyi/ruoyi-server:4.5.0"
     container_name: ruoyi-server2
     environment:
       # 时区上海
@@ -130,7 +130,7 @@ services:
     network_mode: "host"
 
   ruoyi-monitor-admin:
-    image: ruoyi/ruoyi-monitor-admin:4.4.0
+    image: ruoyi/ruoyi-monitor-admin:4.5.0
     container_name: ruoyi-monitor-admin
     environment:
       # 时区上海
@@ -142,7 +142,7 @@ services:
     network_mode: "host"
 
   ruoyi-xxl-job-admin:
-    image: ruoyi/ruoyi-xxl-job-admin:4.4.0
+    image: ruoyi/ruoyi-xxl-job-admin:4.5.0
     container_name: ruoyi-xxl-job-admin
     environment:
       # 时区上海