🧧发布 4.5.0 新春版

This commit is contained in:
疯狂的狮子li 2023-01-12 09:55:50 +08:00
parent 34574c5bf7
commit 173862360e
60 changed files with 559 additions and 442 deletions

View File

@ -1,13 +1,49 @@
### 使用版本(未按照模板填写直接删除) ### 使用版本(未按照模板填写直接删除)
- jdk版本(带上尾号): 例如 1.8.0_202
- 框架版本(项目启动时输出的版本号): 例如 4.4.0
- 其他依赖版本(你觉得有必要的):
### 问题前提
> 功能不好用 不会用 是否已经看过项目文档
> 项目运行报错 是否已经拿着报错信息去百度 常见报错百度百度足以
> 是否搜索过其他issue 一些已经解决的问题 会在issue内留下解决方法
> 无法线上解决或者与框架无关的问题的欢迎加VIP群跟作者一对一谈
### 异常模块
> 此报错都涉及到那些系统模块
例如 ruoyi-system ruoyi-auth 等等
### 问题描述 ### 问题描述
> 越详细越容易直击问题所在
已知: XXX功能不好用 或 XXX数据不正常 等等
### 希望结果 ### 希望结果
> 想知道你觉得怎么样是正常或者合理的
希望功能可以有XXX结果 或者 XXX现象
### 重现步骤 ### 重现步骤
> 作者并不知道这个问题是如何出现的
### 报错信息(截图为主 请勿发混乱格式) - 1
- 2
- 3
### 相关代码与报错信息(请勿发混乱格式)
> 代码可按照如下形式提供或者截图均可 越详细越好
> 大多数问题都是 代码编写错误问题 逻辑问题 或者用法错误等问题
```java
public class XXX {
}
```

View File

@ -1,7 +1,7 @@
### 更改目的 解决了什么问题(请提交到dev分支) ### 更改目的 解决了什么问题(请提交到dev分支)
### 描述 做了哪些改动 ### 改动逻辑 这么写的思路(让作者更好的理解你的意图)
### 测试 都做了哪些测试(未经过测试不采纳) ### 测试 都做了哪些测试(未经过测试不采纳)

View File

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

View File

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

View File

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

View File

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

111
pom.xml
View File

@ -6,49 +6,77 @@
<groupId>com.ruoyi</groupId> <groupId>com.ruoyi</groupId>
<artifactId>ruoyi-vue-plus</artifactId> <artifactId>ruoyi-vue-plus</artifactId>
<version>4.4.0</version> <version>4.5.0</version>
<name>RuoYi-Vue-Plus</name> <name>RuoYi-Vue-Plus</name>
<url>https://gitee.com/JavaLionLi/RuoYi-Vue-Plus</url> <url>https://gitee.com/JavaLionLi/RuoYi-Vue-Plus</url>
<description>RuoYi-Vue-Plus后台管理系统</description> <description>RuoYi-Vue-Plus后台管理系统</description>
<properties> <properties>
<ruoyi-vue-plus.version>4.4.0</ruoyi-vue-plus.version> <ruoyi-vue-plus.version>4.5.0</ruoyi-vue-plus.version>
<spring-boot.version>2.7.6</spring-boot.version> <spring-boot.version>2.7.7</spring-boot.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version> <java.version>1.8</java.version>
<maven-jar-plugin.version>3.2.2</maven-jar-plugin.version> <maven-jar-plugin.version>3.2.2</maven-jar-plugin.version>
<spring-boot.mybatis>2.2.2</spring-boot.mybatis> <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> <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> <velocity.version>2.3</velocity.version>
<satoken.version>1.33.0</satoken.version> <satoken.version>1.34.0</satoken.version>
<mybatis-plus.version>3.5.2</mybatis-plus.version> <mybatis-plus.version>3.5.3.1</mybatis-plus.version>
<p6spy.version>3.9.1</p6spy.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> <okhttp.version>4.10.0</okhttp.version>
<spring-boot-admin.version>2.7.7</spring-boot-admin.version> <spring-boot-admin.version>2.7.10</spring-boot-admin.version>
<redisson.version>3.18.0</redisson.version> <redisson.version>3.19.1</redisson.version>
<lock4j.version>2.2.3</lock4j.version> <lock4j.version>2.2.3</lock4j.version>
<dynamic-ds.version>3.5.2</dynamic-ds.version> <dynamic-ds.version>3.5.2</dynamic-ds.version>
<alibaba-ttl.version>2.14.2</alibaba-ttl.version> <alibaba-ttl.version>2.14.2</alibaba-ttl.version>
<xxl-job.version>2.3.1</xxl-job.version> <xxl-job.version>2.3.1</xxl-job.version>
<lombok.version>1.18.24</lombok.version> <lombok.version>1.18.24</lombok.version>
<!-- 统一 guava 版本 解决隐式漏洞问题 -->
<guava.version>31.1-jre</guava.version>
<!-- 临时修复 snakeyaml 漏洞 --> <!-- 临时修复 snakeyaml 漏洞 -->
<snakeyaml.version>1.32</snakeyaml.version> <snakeyaml.version>1.33</snakeyaml.version>
<!-- OSS 配置 --> <!-- 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 配置 --> <!-- SMS 配置 -->
<aliyun.sms.version>2.0.22</aliyun.sms.version> <aliyun.sms.version>2.0.23</aliyun.sms.version>
<tencent.sms.version>3.1.635</tencent.sms.version> <tencent.sms.version>3.1.660</tencent.sms.version>
</properties> </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> <dependencyManagement>
<dependencies> <dependencies>
@ -149,11 +177,7 @@
<artifactId>mybatis-plus-boot-starter</artifactId> <artifactId>mybatis-plus-boot-starter</artifactId>
<version>${mybatis-plus.version}</version> <version>${mybatis-plus.version}</version>
</dependency> </dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-extension</artifactId>
<version>${mybatis-plus.version}</version>
</dependency>
<!-- sql性能分析插件 --> <!-- sql性能分析插件 -->
<dependency> <dependency>
<groupId>p6spy</groupId> <groupId>p6spy</groupId>
@ -221,13 +245,6 @@
<version>${alibaba-ttl.version}</version> <version>${alibaba-ttl.version}</version>
</dependency> </dependency>
<!-- 统一 guava 版本 解决隐式漏洞问题 -->
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>${guava.version}</version>
</dependency>
<!-- 临时修复 snakeyaml 漏洞 --> <!-- 临时修复 snakeyaml 漏洞 -->
<dependency> <dependency>
<groupId>org.yaml</groupId> <groupId>org.yaml</groupId>
@ -310,8 +327,8 @@
<repositories> <repositories>
<repository> <repository>
<id>public</id> <id>public</id>
<name>huawei nexus</name> <name>aliyun nexus</name>
<url>https://mirrors.huaweicloud.com/repository/maven/</url> <url>https://maven.aliyun.com/repository/public/</url>
<releases> <releases>
<enabled>true</enabled> <enabled>true</enabled>
</releases> </releases>
@ -321,8 +338,8 @@
<pluginRepositories> <pluginRepositories>
<pluginRepository> <pluginRepository>
<id>public</id> <id>public</id>
<name>huawei nexus</name> <name>aliyun nexus</name>
<url>https://mirrors.huaweicloud.com/repository/maven/</url> <url>https://maven.aliyun.com/repository/public/</url>
<releases> <releases>
<enabled>true</enabled> <enabled>true</enabled>
</releases> </releases>
@ -332,34 +349,4 @@
</pluginRepository> </pluginRepository>
</pluginRepositories> </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> </project>

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -21,7 +21,7 @@ export default {
const isLogin = await blobValidate(res.data); const isLogin = await blobValidate(res.data);
if (isLogin) { if (isLogin) {
const blob = new Blob([res.data], { type: 'application/octet-stream' }) 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 { } else {
this.printErrMsg(res.data); this.printErrMsg(res.data);
} }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -68,7 +68,7 @@
<script> <script>
import { getCache } from "@/api/monitor/cache"; import { getCache } from "@/api/monitor/cache";
import echarts from "echarts"; import * as echarts from "echarts";
export default { export default {
name: "Cache", name: "Cache",

View File

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

View File

@ -5,7 +5,7 @@
<parent> <parent>
<artifactId>ruoyi-vue-plus</artifactId> <artifactId>ruoyi-vue-plus</artifactId>
<groupId>com.ruoyi</groupId> <groupId>com.ruoyi</groupId>
<version>4.4.0</version> <version>4.5.0</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<packaging>jar</packaging> <packaging>jar</packaging>
@ -132,10 +132,7 @@
<groupId>com.baomidou</groupId> <groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId> <artifactId>mybatis-plus-boot-starter</artifactId>
</dependency> </dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-extension</artifactId>
</dependency>
<!-- sql性能分析插件 --> <!-- sql性能分析插件 -->
<dependency> <dependency>
<groupId>p6spy</groupId> <groupId>p6spy</groupId>

View File

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

View File

@ -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;
}

View File

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

View File

@ -4,16 +4,12 @@ import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.Wrapper; import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; 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.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage; 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.core.toolkit.*;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page; 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 com.ruoyi.common.utils.BeanCopyUtils;
import org.apache.ibatis.binding.MapperMethod;
import org.apache.ibatis.logging.Log; import org.apache.ibatis.logging.Log;
import org.apache.ibatis.logging.LogFactory; import org.apache.ibatis.logging.LogFactory;
@ -21,7 +17,6 @@ import java.io.Serializable;
import java.util.Collection; import java.util.Collection;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Objects;
/** /**
* 自定义 Mapper 接口, 实现 自定义扩展 * 自定义 Mapper 接口, 实现 自定义扩展
@ -37,8 +32,6 @@ public interface BaseMapperPlus<M, T, V> extends BaseMapper<T> {
Log log = LogFactory.getLog(BaseMapperPlus.class); Log log = LogFactory.getLog(BaseMapperPlus.class);
int DEFAULT_BATCH_SIZE = 1000;
default Class<V> currentVoClass() { default Class<V> currentVoClass() {
return (Class<V>) ReflectionKit.getSuperClassGenericType(this.getClass(), BaseMapperPlus.class, 2); 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) { default boolean insertBatch(Collection<T> entityList) {
return insertBatch(entityList, DEFAULT_BATCH_SIZE); return Db.saveBatch(entityList);
} }
/** /**
* 批量更新 * 批量更新
*/ */
default boolean updateBatchById(Collection<T> entityList) { default boolean updateBatchById(Collection<T> entityList) {
return updateBatchById(entityList, DEFAULT_BATCH_SIZE); return Db.updateBatchById(entityList);
} }
/** /**
* 批量插入或更新 * 批量插入或更新
*/ */
default boolean insertOrUpdateBatch(Collection<T> 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) { default boolean insertBatch(Collection<T> entityList, int batchSize) {
String sqlStatement = SqlHelper.getSqlStatement(this.currentMapperClass(), SqlMethod.INSERT_ONE); return Db.saveBatch(entityList, batchSize);
return SqlHelper.executeBatch(this.currentModelClass(), log, entityList, batchSize,
(sqlSession, entity) -> sqlSession.insert(sqlStatement, entity));
} }
/** /**
* 批量更新(包含限制条数) * 批量更新(包含限制条数)
*/ */
default boolean updateBatchById(Collection<T> entityList, int batchSize) { default boolean updateBatchById(Collection<T> entityList, int batchSize) {
String sqlStatement = SqlHelper.getSqlStatement(this.currentMapperClass(), SqlMethod.UPDATE_BY_ID); return Db.updateBatchById(entityList, batchSize);
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);
});
} }
/** /**
* 批量插入或更新(包含限制条数) * 批量插入或更新(包含限制条数)
*/ */
default boolean insertOrUpdateBatch(Collection<T> entityList, int batchSize) { default boolean insertOrUpdateBatch(Collection<T> entityList, int batchSize) {
TableInfo tableInfo = TableInfoHelper.getTableInfo(this.currentModelClass()); return Db.saveOrUpdateBatch(entityList, batchSize);
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);
});
} }
/** /**
* 插入或更新(包含限制条数) * 插入或更新(包含限制条数)
*/ */
default boolean insertOrUpdate(T entity) { default boolean insertOrUpdate(T entity) {
if (null != entity) { return Db.saveOrUpdate(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;
} }
default V selectVoById(Serializable id) { default V selectVoById(Serializable id) {

View File

@ -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);
}

View File

@ -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);
}

View File

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

View File

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

View File

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

View File

@ -24,6 +24,7 @@ import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException; import java.io.UnsupportedEncodingException;
import java.util.Collection; import java.util.Collection;
import java.util.List; import java.util.List;
@ -84,7 +85,13 @@ public class ExcelUtil {
* @param response 响应体 * @param response 响应体
*/ */
public static <T> void exportExcel(List<T> list, String sheetName, Class<T> clazz, HttpServletResponse 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,6 +107,34 @@ public class ExcelUtil {
try { try {
resetResponse(sheetName, response); resetResponse(sheetName, response);
ServletOutputStream os = response.getOutputStream(); ServletOutputStream os = response.getOutputStream();
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) ExcelWriterSheetBuilder builder = EasyExcel.write(os, clazz)
.autoCloseStream(false) .autoCloseStream(false)
// 自动适配 // 自动适配
@ -112,9 +147,6 @@ public class ExcelUtil {
builder.registerWriteHandler(new CellMergeStrategy(list, true)); builder.registerWriteHandler(new CellMergeStrategy(list, true));
} }
builder.doWrite(list); builder.doWrite(list);
} catch (IOException e) {
throw new RuntimeException("导出Excel异常");
}
} }
/** /**
@ -125,12 +157,30 @@ public class ExcelUtil {
* 例如: excel/temp.xlsx * 例如: excel/temp.xlsx
* 重点: 模板文件必须放置到启动类对应的 resource 目录下 * 重点: 模板文件必须放置到启动类对应的 resource 目录下
* @param data 模板需要的数据 * @param data 模板需要的数据
* @param response 响应体
*/ */
public static void exportTemplate(List<Object> data, String filename, String templatePath, HttpServletResponse response) { public static void exportTemplate(List<Object> data, String filename, String templatePath, HttpServletResponse response) {
try { try {
resetResponse(filename, response); resetResponse(filename, response);
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); ClassPathResource templateResource = new ClassPathResource(templatePath);
ExcelWriter excelWriter = EasyExcel.write(response.getOutputStream()) ExcelWriter excelWriter = EasyExcel.write(os)
.withTemplate(templateResource.getStream()) .withTemplate(templateResource.getStream())
.autoCloseStream(false) .autoCloseStream(false)
// 大数值自动转换 防止失真 // 大数值自动转换 防止失真
@ -145,9 +195,6 @@ public class ExcelUtil {
excelWriter.fill(d, writeSheet); excelWriter.fill(d, writeSheet);
} }
excelWriter.finish(); excelWriter.finish();
} catch (IOException e) {
throw new RuntimeException("导出Excel异常");
}
} }
/** /**
@ -158,12 +205,30 @@ public class ExcelUtil {
* 例如: excel/temp.xlsx * 例如: excel/temp.xlsx
* 重点: 模板文件必须放置到启动类对应的 resource 目录下 * 重点: 模板文件必须放置到启动类对应的 resource 目录下
* @param data 模板需要的数据 * @param data 模板需要的数据
* @param response 响应体
*/ */
public static void exportTemplateMultiList(Map<String, Object> data, String filename, String templatePath, HttpServletResponse response) { public static void exportTemplateMultiList(Map<String, Object> data, String filename, String templatePath, HttpServletResponse response) {
try { try {
resetResponse(filename, response); resetResponse(filename, response);
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); ClassPathResource templateResource = new ClassPathResource(templatePath);
ExcelWriter excelWriter = EasyExcel.write(response.getOutputStream()) ExcelWriter excelWriter = EasyExcel.write(os)
.withTemplate(templateResource.getStream()) .withTemplate(templateResource.getStream())
.autoCloseStream(false) .autoCloseStream(false)
// 大数值自动转换 防止失真 // 大数值自动转换 防止失真
@ -184,9 +249,6 @@ public class ExcelUtil {
} }
} }
excelWriter.finish(); excelWriter.finish();
} catch (IOException e) {
throw new RuntimeException("导出Excel异常");
}
} }
/** /**

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -4,9 +4,9 @@ import cn.dev33.satoken.secure.BCrypt;
import com.ruoyi.common.constant.CacheConstants; import com.ruoyi.common.constant.CacheConstants;
import com.ruoyi.common.constant.Constants; import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.constant.UserConstants; 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.entity.SysUser;
import com.ruoyi.common.core.domain.model.RegisterBody; 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.enums.UserType;
import com.ruoyi.common.exception.user.CaptchaException; import com.ruoyi.common.exception.user.CaptchaException;
import com.ruoyi.common.exception.user.CaptchaExpireException; 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.ServletUtils;
import com.ruoyi.common.utils.StringUtils; import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.redis.RedisUtils; import com.ruoyi.common.utils.redis.RedisUtils;
import com.ruoyi.common.utils.spring.SpringUtils;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
@ -31,7 +32,6 @@ public class SysRegisterService {
private final ISysUserService userService; private final ISysUserService userService;
private final ISysConfigService configService; private final ISysConfigService configService;
private final LogininforService asyncService;
/** /**
* 注册 * 注册
@ -61,7 +61,7 @@ public class SysRegisterService {
if (!regFlag) { if (!regFlag) {
throw new UserException("user.register.error"); 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); String captcha = RedisUtils.getCacheObject(verifyKey);
RedisUtils.deleteObject(verifyKey); RedisUtils.deleteObject(verifyKey);
if (captcha == null) { 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(); throw new CaptchaExpireException();
} }
if (!code.equalsIgnoreCase(captcha)) { 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(); 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);
}
} }

View File

@ -138,6 +138,10 @@ public class SysConfigServiceImpl implements ISysConfigService, ConfigService {
public String updateConfig(SysConfig config) { public String updateConfig(SysConfig config) {
int row = 0; int row = 0;
if (config.getConfigId() != null) { 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); row = baseMapper.updateById(config);
} else { } else {
row = baseMapper.update(config, new LambdaQueryWrapper<SysConfig>() row = baseMapper.update(config, new LambdaQueryWrapper<SysConfig>()

View File

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

View File

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

View File

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

View File

@ -3,7 +3,6 @@ package com.ruoyi.web.controller.common;
import cn.dev33.satoken.annotation.SaIgnore; import cn.dev33.satoken.annotation.SaIgnore;
import cn.hutool.captcha.AbstractCaptcha; import cn.hutool.captcha.AbstractCaptcha;
import cn.hutool.captcha.generator.CodeGenerator; import cn.hutool.captcha.generator.CodeGenerator;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.IdUtil; import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.RandomUtil; import cn.hutool.core.util.RandomUtil;
import com.ruoyi.common.constant.CacheConstants; import com.ruoyi.common.constant.CacheConstants;
@ -21,6 +20,9 @@ import com.ruoyi.sms.entity.SmsResult;
import com.ruoyi.system.service.ISysConfigService; import com.ruoyi.system.service.ISysConfigService;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; 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.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
@ -95,28 +97,16 @@ public class CaptchaController {
AbstractCaptcha captcha = SpringUtils.getBean(captchaProperties.getCategory().getClazz()); AbstractCaptcha captcha = SpringUtils.getBean(captchaProperties.getCategory().getClazz());
captcha.setGenerator(codeGenerator); captcha.setGenerator(codeGenerator);
captcha.createCode(); 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)); RedisUtils.setCacheObject(verifyKey, code, Duration.ofMinutes(Constants.CAPTCHA_EXPIRATION));
ajax.put("uuid", uuid); ajax.put("uuid", uuid);
ajax.put("img", captcha.getImageBase64()); ajax.put("img", captcha.getImageBase64());
return R.ok(ajax); 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;
}
}
} }

View File

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

View File

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

View File

@ -41,5 +41,5 @@ repeat.submit.message=不允许重复提交,请稍候再试
rate.limiter.message=访问过于频繁,请稍候再试 rate.limiter.message=访问过于频繁,请稍候再试
sms.code.not.blank=短信验证码不能为空 sms.code.not.blank=短信验证码不能为空
sms.code.retry.limit.count=短信验证码输入错误{0}次 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不能为空 xcx.code.not.blank=小程序code不能为空

View File

@ -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 rate.limiter.message=Visit too frequently, please try again later
sms.code.not.blank=Sms code cannot be blank sms.code.not.blank=Sms code cannot be blank
sms.code.retry.limit.count=Sms code input error {0} times 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 xcx.code.not.blank=Mini program code cannot be blank

View File

@ -41,5 +41,5 @@ repeat.submit.message=不允许重复提交,请稍候再试
rate.limiter.message=访问过于频繁,请稍候再试 rate.limiter.message=访问过于频繁,请稍候再试
sms.code.not.blank=短信验证码不能为空 sms.code.not.blank=短信验证码不能为空
sms.code.retry.limit.count=短信验证码输入错误{0}次 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不能为空 xcx.code.not.blank=小程序code不能为空

View File

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

View File

@ -197,11 +197,11 @@
<el-input v-model="form.${field}" placeholder="请输入${comment}" /> <el-input v-model="form.${field}" placeholder="请输入${comment}" />
</el-form-item> </el-form-item>
#elseif($column.htmlType == "imageUpload") #elseif($column.htmlType == "imageUpload")
<el-form-item label="${comment}"> <el-form-item label="${comment}" prop="${field}">
<image-upload v-model="form.${field}"/> <image-upload v-model="form.${field}"/>
</el-form-item> </el-form-item>
#elseif($column.htmlType == "fileUpload") #elseif($column.htmlType == "fileUpload")
<el-form-item label="${comment}"> <el-form-item label="${comment}" prop="${field}">
<file-upload v-model="form.${field}"/> <file-upload v-model="form.${field}"/>
</el-form-item> </el-form-item>
#elseif($column.htmlType == "editor") #elseif($column.htmlType == "editor")
@ -215,8 +215,11 @@
v-for="dict in dict.type.${dictType}" v-for="dict in dict.type.${dictType}"
:key="dict.value" :key="dict.value"
:label="dict.label" :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-option>
</el-select> </el-select>
</el-form-item> </el-form-item>
@ -227,7 +230,7 @@
</el-select> </el-select>
</el-form-item> </el-form-item>
#elseif($column.htmlType == "checkbox" && "" != $dictType) #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-group v-model="form.${field}">
<el-checkbox <el-checkbox
v-for="dict in dict.type.${dictType}" v-for="dict in dict.type.${dictType}"
@ -238,24 +241,27 @@
</el-checkbox-group> </el-checkbox-group>
</el-form-item> </el-form-item>
#elseif($column.htmlType == "checkbox" && $dictType) #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-group v-model="form.${field}">
<el-checkbox>请选择字典生成</el-checkbox> <el-checkbox>请选择字典生成</el-checkbox>
</el-checkbox-group> </el-checkbox-group>
</el-form-item> </el-form-item>
#elseif($column.htmlType == "radio" && "" != $dictType) #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-group v-model="form.${field}">
<el-radio <el-radio
v-for="dict in dict.type.${dictType}" v-for="dict in dict.type.${dictType}"
:key="dict.value" :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> >{{dict.label}}</el-radio>
</el-radio-group> </el-radio-group>
</el-form-item> </el-form-item>
#elseif($column.htmlType == "radio" && $dictType) #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-group v-model="form.${field}">
<el-radio label="1">请选择字典生成</el-radio> <el-radio label="1">请选择字典生成</el-radio>
</el-radio-group> </el-radio-group>
@ -412,7 +418,7 @@ export default {
#set($comment=$column.columnComment) #set($comment=$column.columnComment)
#end #end
$column.javaField: [ $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 ]#if($foreach.count != $columns.size()),#end
#end #end
#end #end
@ -456,9 +462,7 @@ export default {
reset() { reset() {
this.form = { this.form = {
#foreach ($column in $columns) #foreach ($column in $columns)
#if($column.htmlType == "radio") #if($column.htmlType == "checkbox")
$column.javaField: #if($column.javaType == "Integer" || $column.javaType == "Long")0#else"0"#end#if($foreach.count != $columns.size()),#end
#elseif($column.htmlType == "checkbox")
$column.javaField: []#if($foreach.count != $columns.size()),#end $column.javaField: []#if($foreach.count != $columns.size()),#end
#else #else
$column.javaField: undefined#if($foreach.count != $columns.size()),#end $column.javaField: undefined#if($foreach.count != $columns.size()),#end

View File

@ -172,11 +172,11 @@
<el-input v-model="form.${field}" placeholder="请输入${comment}" /> <el-input v-model="form.${field}" placeholder="请输入${comment}" />
</el-form-item> </el-form-item>
#elseif($column.htmlType == "imageUpload") #elseif($column.htmlType == "imageUpload")
<el-form-item label="${comment}"> <el-form-item label="${comment}" prop="${field}">
<image-upload v-model="form.${field}"/> <image-upload v-model="form.${field}"/>
</el-form-item> </el-form-item>
#elseif($column.htmlType == "fileUpload") #elseif($column.htmlType == "fileUpload")
<el-form-item label="${comment}"> <el-form-item label="${comment}" prop="${field}">
<file-upload v-model="form.${field}"/> <file-upload v-model="form.${field}"/>
</el-form-item> </el-form-item>
#elseif($column.htmlType == "editor") #elseif($column.htmlType == "editor")
@ -190,8 +190,11 @@
v-for="dict in ${dictType}" v-for="dict in ${dictType}"
:key="dict.value" :key="dict.value"
:label="dict.label" :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-option>
</el-select> </el-select>
</el-form-item> </el-form-item>
@ -202,7 +205,7 @@
</el-select> </el-select>
</el-form-item> </el-form-item>
#elseif($column.htmlType == "checkbox" && "" != $dictType) #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-group v-model="form.${field}">
<el-checkbox <el-checkbox
v-for="dict in ${dictType}" v-for="dict in ${dictType}"
@ -213,24 +216,27 @@
</el-checkbox-group> </el-checkbox-group>
</el-form-item> </el-form-item>
#elseif($column.htmlType == "checkbox" && $dictType) #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-group v-model="form.${field}">
<el-checkbox>请选择字典生成</el-checkbox> <el-checkbox>请选择字典生成</el-checkbox>
</el-checkbox-group> </el-checkbox-group>
</el-form-item> </el-form-item>
#elseif($column.htmlType == "radio" && "" != $dictType) #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-group v-model="form.${field}">
<el-radio <el-radio
v-for="dict in ${dictType}" v-for="dict in ${dictType}"
:key="dict.value" :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> >{{dict.label}}</el-radio>
</el-radio-group> </el-radio-group>
</el-form-item> </el-form-item>
#elseif($column.htmlType == "radio" && $dictType) #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-group v-model="form.${field}">
<el-radio label="1">请选择字典生成</el-radio> <el-radio label="1">请选择字典生成</el-radio>
</el-radio-group> </el-radio-group>
@ -306,7 +312,7 @@ const data = reactive({
#set($comment=$column.columnComment) #set($comment=$column.columnComment)
#end #end
$column.javaField: [ $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 ]#if($foreach.count != $columns.size()),#end
#end #end
#end #end
@ -359,10 +365,7 @@ function cancel() {
function reset() { function reset() {
form.value = { form.value = {
#foreach ($column in $columns) #foreach ($column in $columns)
#if($column.htmlType == "radio") #if($column.htmlType == "checkbox")
$column.javaField: #if($column.javaType == "Integer" || $column.javaType == "Long")0#else"0"#end#if($foreach.count != $columns.size()),#end
#elseif($column.htmlType == "checkbox")
$column.javaField: []#if($foreach.count != $columns.size()),#end $column.javaField: []#if($foreach.count != $columns.size()),#end
#else #else
$column.javaField: null#if($foreach.count != $columns.size()),#end $column.javaField: null#if($foreach.count != $columns.size()),#end

View File

@ -180,11 +180,11 @@
<el-input v-model="form.${field}" placeholder="请输入${comment}" /> <el-input v-model="form.${field}" placeholder="请输入${comment}" />
</el-form-item> </el-form-item>
#elseif($column.htmlType == "imageUpload") #elseif($column.htmlType == "imageUpload")
<el-form-item label="${comment}"> <el-form-item label="${comment}" prop="${field}">
<image-upload v-model="form.${field}"/> <image-upload v-model="form.${field}"/>
</el-form-item> </el-form-item>
#elseif($column.htmlType == "fileUpload") #elseif($column.htmlType == "fileUpload")
<el-form-item label="${comment}"> <el-form-item label="${comment}" prop="${field}">
<file-upload v-model="form.${field}"/> <file-upload v-model="form.${field}"/>
</el-form-item> </el-form-item>
#elseif($column.htmlType == "editor") #elseif($column.htmlType == "editor")
@ -198,8 +198,11 @@
v-for="dict in ${dictType}" v-for="dict in ${dictType}"
:key="dict.value" :key="dict.value"
:label="dict.label" :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-option>
</el-select> </el-select>
</el-form-item> </el-form-item>
@ -210,7 +213,7 @@
</el-select> </el-select>
</el-form-item> </el-form-item>
#elseif($column.htmlType == "checkbox" && "" != $dictType) #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-group v-model="form.${field}">
<el-checkbox <el-checkbox
v-for="dict in ${dictType}" v-for="dict in ${dictType}"
@ -221,24 +224,27 @@
</el-checkbox-group> </el-checkbox-group>
</el-form-item> </el-form-item>
#elseif($column.htmlType == "checkbox" && $dictType) #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-group v-model="form.${field}">
<el-checkbox>请选择字典生成</el-checkbox> <el-checkbox>请选择字典生成</el-checkbox>
</el-checkbox-group> </el-checkbox-group>
</el-form-item> </el-form-item>
#elseif($column.htmlType == "radio" && "" != $dictType) #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-group v-model="form.${field}">
<el-radio <el-radio
v-for="dict in ${dictType}" v-for="dict in ${dictType}"
:key="dict.value" :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> >{{dict.label}}</el-radio>
</el-radio-group> </el-radio-group>
</el-form-item> </el-form-item>
#elseif($column.htmlType == "radio" && $dictType) #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-group v-model="form.${field}">
<el-radio label="1">请选择字典生成</el-radio> <el-radio label="1">请选择字典生成</el-radio>
</el-radio-group> </el-radio-group>
@ -387,7 +393,7 @@ const data = reactive({
#set($comment=$column.columnComment) #set($comment=$column.columnComment)
#end #end
$column.javaField: [ $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 ]#if($foreach.count != $columns.size()),#end
#end #end
#end #end
@ -431,9 +437,7 @@ function cancel() {
function reset() { function reset() {
form.value = { form.value = {
#foreach ($column in $columns) #foreach ($column in $columns)
#if($column.htmlType == "radio") #if($column.htmlType == "checkbox")
$column.javaField: #if($column.javaType == "Integer" || $column.javaType == "Long")0#else"0"#end#if($foreach.count != $columns.size()),#end
#elseif($column.htmlType == "checkbox")
$column.javaField: []#if($foreach.count != $columns.size()),#end $column.javaField: []#if($foreach.count != $columns.size()),#end
#else #else
$column.javaField: null#if($foreach.count != $columns.size()),#end $column.javaField: null#if($foreach.count != $columns.size()),#end

View File

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