mirror of
https://github.com/dromara/RuoYi-Vue-Plus.git
synced 2025-09-21 18:56:38 +08:00
Merge remote-tracking branch 'origin/dev' into 4.X
# Conflicts: # README.md
This commit is contained in:
commit
c631a084f3
@ -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.6.0" />
|
<option name="imageTag" value="ruoyi/ruoyi-monitor-admin:4.7.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>
|
||||||
|
@ -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.6.0" />
|
<option name="imageTag" value="ruoyi/ruoyi-server:4.7.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>
|
||||||
|
@ -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.6.0" />
|
<option name="imageTag" value="ruoyi/ruoyi-xxl-job-admin:4.7.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>
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
<div style="height: 10px; clear: both;"></div>
|
<div style="height: 10px; clear: both;"></div>
|
||||||
|
|
||||||
- - -
|
- - -
|
||||||
|
|
||||||
## 平台简介
|
## 平台简介
|
||||||
|
|
||||||
[](https://gitee.com/dromara/RuoYi-Vue-Plus)
|
[](https://gitee.com/dromara/RuoYi-Vue-Plus)
|
||||||
@ -9,7 +10,7 @@
|
|||||||
[](https://gitee.com/dromara/RuoYi-Vue-Plus/blob/master/LICENSE)
|
[](https://gitee.com/dromara/RuoYi-Vue-Plus/blob/master/LICENSE)
|
||||||
[](https://www.jetbrains.com/?from=RuoYi-Vue-Plus)
|
[](https://www.jetbrains.com/?from=RuoYi-Vue-Plus)
|
||||||
<br>
|
<br>
|
||||||
[](https://gitee.com/dromara/RuoYi-Vue-Plus)
|
[](https://gitee.com/dromara/RuoYi-Vue-Plus)
|
||||||
[]()
|
[]()
|
||||||
[]()
|
[]()
|
||||||
[]()
|
[]()
|
||||||
@ -161,4 +162,3 @@
|
|||||||
|  |  |
|
|  |  |
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
21
pom.xml
21
pom.xml
@ -6,15 +6,15 @@
|
|||||||
|
|
||||||
<groupId>com.ruoyi</groupId>
|
<groupId>com.ruoyi</groupId>
|
||||||
<artifactId>ruoyi-vue-plus</artifactId>
|
<artifactId>ruoyi-vue-plus</artifactId>
|
||||||
<version>4.6.0</version>
|
<version>4.7.0</version>
|
||||||
|
|
||||||
<name>RuoYi-Vue-Plus</name>
|
<name>RuoYi-Vue-Plus</name>
|
||||||
<url>https://gitee.com/dromara/RuoYi-Vue-Plus</url>
|
<url>https://gitee.com/dromara/RuoYi-Vue-Plus</url>
|
||||||
<description>RuoYi-Vue-Plus后台管理系统</description>
|
<description>RuoYi-Vue-Plus后台管理系统</description>
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
<ruoyi-vue-plus.version>4.6.0</ruoyi-vue-plus.version>
|
<ruoyi-vue-plus.version>4.7.0</ruoyi-vue-plus.version>
|
||||||
<spring-boot.version>2.7.9</spring-boot.version>
|
<spring-boot.version>2.7.11</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>
|
||||||
@ -27,16 +27,18 @@
|
|||||||
<satoken.version>1.34.0</satoken.version>
|
<satoken.version>1.34.0</satoken.version>
|
||||||
<mybatis-plus.version>3.5.3.1</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.15</hutool.version>
|
<hutool.version>5.8.18</hutool.version>
|
||||||
<okhttp.version>4.10.0</okhttp.version>
|
<okhttp.version>4.10.0</okhttp.version>
|
||||||
<spring-boot-admin.version>2.7.10</spring-boot-admin.version>
|
<spring-boot-admin.version>2.7.10</spring-boot-admin.version>
|
||||||
<redisson.version>3.20.0</redisson.version>
|
<redisson.version>3.20.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.4.0</xxl-job.version>
|
||||||
<lombok.version>1.18.26</lombok.version>
|
<lombok.version>1.18.26</lombok.version>
|
||||||
<bouncycastle.version>1.72</bouncycastle.version>
|
<bouncycastle.version>1.72</bouncycastle.version>
|
||||||
|
<!-- 离线IP地址定位库 -->
|
||||||
|
<ip2region.version>2.7.0</ip2region.version>
|
||||||
|
|
||||||
<!-- 临时修复 snakeyaml 漏洞 -->
|
<!-- 临时修复 snakeyaml 漏洞 -->
|
||||||
<snakeyaml.version>1.33</snakeyaml.version>
|
<snakeyaml.version>1.33</snakeyaml.version>
|
||||||
@ -258,6 +260,13 @@
|
|||||||
<version>${alibaba-ttl.version}</version>
|
<version>${alibaba-ttl.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<!-- 离线IP地址定位库 ip2region -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.lionsoul</groupId>
|
||||||
|
<artifactId>ip2region</artifactId>
|
||||||
|
<version>${ip2region.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<!-- 临时修复 snakeyaml 漏洞 -->
|
<!-- 临时修复 snakeyaml 漏洞 -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.yaml</groupId>
|
<groupId>org.yaml</groupId>
|
||||||
|
@ -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.6.0</version>
|
<version>4.7.0</version>
|
||||||
</parent>
|
</parent>
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
<packaging>jar</packaging>
|
<packaging>jar</packaging>
|
||||||
|
@ -10,10 +10,12 @@ import com.ruoyi.common.constant.Constants;
|
|||||||
import com.ruoyi.common.core.domain.R;
|
import com.ruoyi.common.core.domain.R;
|
||||||
import com.ruoyi.common.enums.CaptchaType;
|
import com.ruoyi.common.enums.CaptchaType;
|
||||||
import com.ruoyi.common.utils.StringUtils;
|
import com.ruoyi.common.utils.StringUtils;
|
||||||
|
import com.ruoyi.common.utils.email.MailUtils;
|
||||||
import com.ruoyi.common.utils.redis.RedisUtils;
|
import com.ruoyi.common.utils.redis.RedisUtils;
|
||||||
import com.ruoyi.common.utils.reflect.ReflectUtils;
|
import com.ruoyi.common.utils.reflect.ReflectUtils;
|
||||||
import com.ruoyi.common.utils.spring.SpringUtils;
|
import com.ruoyi.common.utils.spring.SpringUtils;
|
||||||
import com.ruoyi.framework.config.properties.CaptchaProperties;
|
import com.ruoyi.framework.config.properties.CaptchaProperties;
|
||||||
|
import com.ruoyi.framework.config.properties.MailProperties;
|
||||||
import com.ruoyi.sms.config.properties.SmsProperties;
|
import com.ruoyi.sms.config.properties.SmsProperties;
|
||||||
import com.ruoyi.sms.core.SmsTemplate;
|
import com.ruoyi.sms.core.SmsTemplate;
|
||||||
import com.ruoyi.sms.entity.SmsResult;
|
import com.ruoyi.sms.entity.SmsResult;
|
||||||
@ -47,6 +49,7 @@ public class CaptchaController {
|
|||||||
private final CaptchaProperties captchaProperties;
|
private final CaptchaProperties captchaProperties;
|
||||||
private final SmsProperties smsProperties;
|
private final SmsProperties smsProperties;
|
||||||
private final ISysConfigService configService;
|
private final ISysConfigService configService;
|
||||||
|
private final MailProperties mailProperties;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 短信验证码
|
* 短信验证码
|
||||||
@ -54,8 +57,7 @@ public class CaptchaController {
|
|||||||
* @param phonenumber 用户手机号
|
* @param phonenumber 用户手机号
|
||||||
*/
|
*/
|
||||||
@GetMapping("/captchaSms")
|
@GetMapping("/captchaSms")
|
||||||
public R<Void> smsCaptcha(@NotBlank(message = "{user.phonenumber.not.blank}")
|
public R<Void> smsCaptcha(@NotBlank(message = "{user.phonenumber.not.blank}") String phonenumber) {
|
||||||
String phonenumber) {
|
|
||||||
if (!smsProperties.getEnabled()) {
|
if (!smsProperties.getEnabled()) {
|
||||||
return R.fail("当前系统没有开启短信功能!");
|
return R.fail("当前系统没有开启短信功能!");
|
||||||
}
|
}
|
||||||
@ -75,6 +77,28 @@ public class CaptchaController {
|
|||||||
return R.ok();
|
return R.ok();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 邮箱验证码
|
||||||
|
*
|
||||||
|
* @param email 邮箱
|
||||||
|
*/
|
||||||
|
@GetMapping("/captchaEmail")
|
||||||
|
public R<Void> emailCode(@NotBlank(message = "{user.email.not.blank}") String email) {
|
||||||
|
if (!mailProperties.getEnabled()) {
|
||||||
|
return R.fail("当前系统没有开启邮箱功能!");
|
||||||
|
}
|
||||||
|
String key = CacheConstants.CAPTCHA_CODE_KEY + email;
|
||||||
|
String code = RandomUtil.randomNumbers(4);
|
||||||
|
RedisUtils.setCacheObject(key, code, Duration.ofMinutes(Constants.CAPTCHA_EXPIRATION));
|
||||||
|
try {
|
||||||
|
MailUtils.sendText(email, "登录验证码", "您本次验证码为:" + code + ",有效性为" + Constants.CAPTCHA_EXPIRATION + "分钟,请尽快填写。");
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("验证码短信发送异常 => {}", e.getMessage());
|
||||||
|
return R.fail(e.getMessage());
|
||||||
|
}
|
||||||
|
return R.ok();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 生成验证码
|
* 生成验证码
|
||||||
*/
|
*/
|
||||||
|
@ -33,7 +33,6 @@ public class CacheController {
|
|||||||
private final static List<SysCache> CACHES = new ArrayList<>();
|
private final static List<SysCache> CACHES = new ArrayList<>();
|
||||||
|
|
||||||
static {
|
static {
|
||||||
CACHES.add(new SysCache(CacheConstants.LOGIN_TOKEN_KEY, "用户信息"));
|
|
||||||
CACHES.add(new SysCache(CacheConstants.ONLINE_TOKEN_KEY, "在线用户"));
|
CACHES.add(new SysCache(CacheConstants.ONLINE_TOKEN_KEY, "在线用户"));
|
||||||
CACHES.add(new SysCache(CacheNames.SYS_CONFIG, "配置信息"));
|
CACHES.add(new SysCache(CacheNames.SYS_CONFIG, "配置信息"));
|
||||||
CACHES.add(new SysCache(CacheNames.SYS_DICT, "数据字典"));
|
CACHES.add(new SysCache(CacheNames.SYS_DICT, "数据字典"));
|
||||||
|
@ -45,7 +45,7 @@ public class SysUserOnlineController extends BaseController {
|
|||||||
List<String> keys = StpUtil.searchTokenValue("", 0, -1, false);
|
List<String> keys = StpUtil.searchTokenValue("", 0, -1, false);
|
||||||
List<UserOnlineDTO> userOnlineDTOList = new ArrayList<>();
|
List<UserOnlineDTO> userOnlineDTOList = new ArrayList<>();
|
||||||
for (String key : keys) {
|
for (String key : keys) {
|
||||||
String token = key.replace(CacheConstants.LOGIN_TOKEN_KEY, "");
|
String token = StringUtils.substringAfterLast(key, ":");
|
||||||
// 如果已经过期则跳过
|
// 如果已经过期则跳过
|
||||||
if (StpUtil.stpLogic.getTokenActivityTimeoutByToken(token) < -1) {
|
if (StpUtil.stpLogic.getTokenActivityTimeoutByToken(token) < -1) {
|
||||||
continue;
|
continue;
|
||||||
|
@ -5,6 +5,7 @@ import com.ruoyi.common.constant.Constants;
|
|||||||
import com.ruoyi.common.core.domain.R;
|
import com.ruoyi.common.core.domain.R;
|
||||||
import com.ruoyi.common.core.domain.entity.SysMenu;
|
import com.ruoyi.common.core.domain.entity.SysMenu;
|
||||||
import com.ruoyi.common.core.domain.entity.SysUser;
|
import com.ruoyi.common.core.domain.entity.SysUser;
|
||||||
|
import com.ruoyi.common.core.domain.model.EmailLoginBody;
|
||||||
import com.ruoyi.common.core.domain.model.LoginBody;
|
import com.ruoyi.common.core.domain.model.LoginBody;
|
||||||
import com.ruoyi.common.core.domain.model.LoginUser;
|
import com.ruoyi.common.core.domain.model.LoginUser;
|
||||||
import com.ruoyi.common.core.domain.model.SmsLoginBody;
|
import com.ruoyi.common.core.domain.model.SmsLoginBody;
|
||||||
@ -57,7 +58,7 @@ public class SysLoginController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 短信登录(示例)
|
* 短信登录
|
||||||
*
|
*
|
||||||
* @param smsLoginBody 登录信息
|
* @param smsLoginBody 登录信息
|
||||||
* @return 结果
|
* @return 结果
|
||||||
@ -72,6 +73,21 @@ public class SysLoginController {
|
|||||||
return R.ok(ajax);
|
return R.ok(ajax);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 邮件登录
|
||||||
|
*
|
||||||
|
* @param body 登录信息
|
||||||
|
* @return 结果
|
||||||
|
*/
|
||||||
|
@PostMapping("/emailLogin")
|
||||||
|
public R<Map<String, Object>> emailLogin(@Validated @RequestBody EmailLoginBody body) {
|
||||||
|
Map<String, Object> ajax = new HashMap<>();
|
||||||
|
// 生成令牌
|
||||||
|
String token = loginService.emailLogin(body.getEmail(), body.getEmailCode());
|
||||||
|
ajax.put(Constants.TOKEN, token);
|
||||||
|
return R.ok(ajax);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 小程序登录(示例)
|
* 小程序登录(示例)
|
||||||
*
|
*
|
||||||
|
@ -2,10 +2,7 @@ package com.ruoyi.web.controller.system;
|
|||||||
|
|
||||||
|
|
||||||
import cn.dev33.satoken.annotation.SaCheckPermission;
|
import cn.dev33.satoken.annotation.SaCheckPermission;
|
||||||
import cn.hutool.core.convert.Convert;
|
|
||||||
import cn.hutool.core.util.ObjectUtil;
|
import cn.hutool.core.util.ObjectUtil;
|
||||||
import cn.hutool.http.HttpException;
|
|
||||||
import cn.hutool.http.HttpUtil;
|
|
||||||
import com.ruoyi.common.annotation.Log;
|
import com.ruoyi.common.annotation.Log;
|
||||||
import com.ruoyi.common.core.controller.BaseController;
|
import com.ruoyi.common.core.controller.BaseController;
|
||||||
import com.ruoyi.common.core.domain.PageQuery;
|
import com.ruoyi.common.core.domain.PageQuery;
|
||||||
@ -13,11 +10,6 @@ import com.ruoyi.common.core.domain.R;
|
|||||||
import com.ruoyi.common.core.page.TableDataInfo;
|
import com.ruoyi.common.core.page.TableDataInfo;
|
||||||
import com.ruoyi.common.core.validate.QueryGroup;
|
import com.ruoyi.common.core.validate.QueryGroup;
|
||||||
import com.ruoyi.common.enums.BusinessType;
|
import com.ruoyi.common.enums.BusinessType;
|
||||||
import com.ruoyi.common.exception.ServiceException;
|
|
||||||
import com.ruoyi.common.utils.file.FileUtils;
|
|
||||||
import com.ruoyi.oss.core.OssClient;
|
|
||||||
import com.ruoyi.oss.factory.OssFactory;
|
|
||||||
import com.ruoyi.system.domain.SysOss;
|
|
||||||
import com.ruoyi.system.domain.bo.SysOssBo;
|
import com.ruoyi.system.domain.bo.SysOssBo;
|
||||||
import com.ruoyi.system.domain.vo.SysOssVo;
|
import com.ruoyi.system.domain.vo.SysOssVo;
|
||||||
import com.ruoyi.system.service.ISysOssService;
|
import com.ruoyi.system.service.ISysOssService;
|
||||||
@ -80,7 +72,7 @@ public class SysOssController extends BaseController {
|
|||||||
@PostMapping(value = "/upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
|
@PostMapping(value = "/upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
|
||||||
public R<Map<String, String>> upload(@RequestPart("file") MultipartFile file) {
|
public R<Map<String, String>> upload(@RequestPart("file") MultipartFile file) {
|
||||||
if (ObjectUtil.isNull(file)) {
|
if (ObjectUtil.isNull(file)) {
|
||||||
throw new ServiceException("上传文件不能为空");
|
return R.fail("上传文件不能为空");
|
||||||
}
|
}
|
||||||
SysOssVo oss = iSysOssService.upload(file);
|
SysOssVo oss = iSysOssService.upload(file);
|
||||||
Map<String, String> map = new HashMap<>(2);
|
Map<String, String> map = new HashMap<>(2);
|
||||||
|
@ -1,22 +1,15 @@
|
|||||||
package com.ruoyi.web.controller.system;
|
package com.ruoyi.web.controller.system;
|
||||||
|
|
||||||
import cn.dev33.satoken.annotation.SaCheckPermission;
|
import cn.dev33.satoken.annotation.SaCheckPermission;
|
||||||
import cn.dev33.satoken.exception.NotLoginException;
|
|
||||||
import cn.dev33.satoken.stp.StpUtil;
|
|
||||||
import cn.hutool.core.collection.CollUtil;
|
|
||||||
import com.ruoyi.common.annotation.Log;
|
import com.ruoyi.common.annotation.Log;
|
||||||
import com.ruoyi.common.constant.CacheConstants;
|
|
||||||
import com.ruoyi.common.constant.UserConstants;
|
|
||||||
import com.ruoyi.common.core.controller.BaseController;
|
import com.ruoyi.common.core.controller.BaseController;
|
||||||
import com.ruoyi.common.core.domain.PageQuery;
|
import com.ruoyi.common.core.domain.PageQuery;
|
||||||
import com.ruoyi.common.core.domain.R;
|
import com.ruoyi.common.core.domain.R;
|
||||||
import com.ruoyi.common.core.domain.entity.SysDept;
|
import com.ruoyi.common.core.domain.entity.SysDept;
|
||||||
import com.ruoyi.common.core.domain.entity.SysRole;
|
import com.ruoyi.common.core.domain.entity.SysRole;
|
||||||
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.page.TableDataInfo;
|
import com.ruoyi.common.core.page.TableDataInfo;
|
||||||
import com.ruoyi.common.enums.BusinessType;
|
import com.ruoyi.common.enums.BusinessType;
|
||||||
import com.ruoyi.common.helper.LoginHelper;
|
|
||||||
import com.ruoyi.common.utils.poi.ExcelUtil;
|
import com.ruoyi.common.utils.poi.ExcelUtil;
|
||||||
import com.ruoyi.system.domain.SysUserRole;
|
import com.ruoyi.system.domain.SysUserRole;
|
||||||
import com.ruoyi.system.service.ISysDeptService;
|
import com.ruoyi.system.service.ISysDeptService;
|
||||||
@ -112,25 +105,7 @@ public class SysRoleController extends BaseController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (roleService.updateRole(role) > 0) {
|
if (roleService.updateRole(role) > 0) {
|
||||||
List<String> keys = StpUtil.searchTokenValue("", 0, -1, false);
|
roleService.cleanOnlineUserByRole(role.getRoleId());
|
||||||
if (CollUtil.isEmpty(keys)) {
|
|
||||||
return R.ok();
|
|
||||||
}
|
|
||||||
// 角色关联的在线用户量过大会导致redis阻塞卡顿 谨慎操作
|
|
||||||
keys.parallelStream().forEach(key -> {
|
|
||||||
String token = key.replace(CacheConstants.LOGIN_TOKEN_KEY, "");
|
|
||||||
// 如果已经过期则跳过
|
|
||||||
if (StpUtil.stpLogic.getTokenActivityTimeoutByToken(token) < -1) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
LoginUser loginUser = LoginHelper.getLoginUser(token);
|
|
||||||
if (loginUser.getRoles().stream().anyMatch(r -> r.getRoleId().equals(role.getRoleId()))) {
|
|
||||||
try {
|
|
||||||
StpUtil.logoutByTokenValue(token);
|
|
||||||
} catch (NotLoginException ignored) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return R.ok();
|
return R.ok();
|
||||||
}
|
}
|
||||||
return R.fail("修改角色'" + role.getRoleName() + "'失败,请联系管理员");
|
return R.fail("修改角色'" + role.getRoleName() + "'失败,请联系管理员");
|
||||||
|
@ -51,7 +51,7 @@ logging:
|
|||||||
level:
|
level:
|
||||||
com.ruoyi: @logging.level@
|
com.ruoyi: @logging.level@
|
||||||
org.springframework: warn
|
org.springframework: warn
|
||||||
config: classpath:logback.xml
|
config: classpath:logback-plus.xml
|
||||||
|
|
||||||
# 用户配置
|
# 用户配置
|
||||||
user:
|
user:
|
||||||
|
@ -18,6 +18,7 @@ user.password.not.blank=用户密码不能为空
|
|||||||
user.password.length.valid=用户密码长度必须在{min}到{max}个字符之间
|
user.password.length.valid=用户密码长度必须在{min}到{max}个字符之间
|
||||||
user.password.not.valid=* 5-50个字符
|
user.password.not.valid=* 5-50个字符
|
||||||
user.email.not.valid=邮箱格式错误
|
user.email.not.valid=邮箱格式错误
|
||||||
|
user.email.not.blank=邮箱不能为空
|
||||||
user.phonenumber.not.blank=用户手机号不能为空
|
user.phonenumber.not.blank=用户手机号不能为空
|
||||||
user.mobile.phone.number.not.valid=手机号格式错误
|
user.mobile.phone.number.not.valid=手机号格式错误
|
||||||
user.login.success=登录成功
|
user.login.success=登录成功
|
||||||
@ -42,4 +43,7 @@ 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}次,帐户锁定{1}分钟
|
sms.code.retry.limit.exceed=短信验证码输入错误{0}次,帐户锁定{1}分钟
|
||||||
|
email.code.not.blank=邮箱验证码不能为空
|
||||||
|
email.code.retry.limit.count=邮箱验证码输入错误{0}次
|
||||||
|
email.code.retry.limit.exceed=邮箱验证码输入错误{0}次,帐户锁定{1}分钟
|
||||||
xcx.code.not.blank=小程序code不能为空
|
xcx.code.not.blank=小程序code不能为空
|
||||||
|
@ -18,6 +18,7 @@ user.password.not.blank=Password cannot be empty
|
|||||||
user.password.length.valid=Password length must be between {min} and {max} characters
|
user.password.length.valid=Password length must be between {min} and {max} characters
|
||||||
user.password.not.valid=* 5-50 characters
|
user.password.not.valid=* 5-50 characters
|
||||||
user.email.not.valid=Mailbox format error
|
user.email.not.valid=Mailbox format error
|
||||||
|
user.email.not.blank=Mailbox cannot be blank
|
||||||
user.phonenumber.not.blank=Phone number cannot be blank
|
user.phonenumber.not.blank=Phone number cannot be blank
|
||||||
user.mobile.phone.number.not.valid=Phone number format error
|
user.mobile.phone.number.not.valid=Phone number format error
|
||||||
user.login.success=Login successful
|
user.login.success=Login successful
|
||||||
@ -42,4 +43,7 @@ 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 {1} minutes
|
sms.code.retry.limit.exceed=Sms code input error {0} times, account locked for {1} minutes
|
||||||
|
email.code.not.blank=Email code cannot be blank
|
||||||
|
email.code.retry.limit.count=Email code input error {0} times
|
||||||
|
email.code.retry.limit.exceed=Email 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
|
||||||
|
@ -18,6 +18,7 @@ user.password.not.blank=用户密码不能为空
|
|||||||
user.password.length.valid=用户密码长度必须在{min}到{max}个字符之间
|
user.password.length.valid=用户密码长度必须在{min}到{max}个字符之间
|
||||||
user.password.not.valid=* 5-50个字符
|
user.password.not.valid=* 5-50个字符
|
||||||
user.email.not.valid=邮箱格式错误
|
user.email.not.valid=邮箱格式错误
|
||||||
|
user.email.not.blank=邮箱不能为空
|
||||||
user.phonenumber.not.blank=用户手机号不能为空
|
user.phonenumber.not.blank=用户手机号不能为空
|
||||||
user.mobile.phone.number.not.valid=手机号格式错误
|
user.mobile.phone.number.not.valid=手机号格式错误
|
||||||
user.login.success=登录成功
|
user.login.success=登录成功
|
||||||
@ -42,4 +43,7 @@ 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}次,帐户锁定{1}分钟
|
sms.code.retry.limit.exceed=短信验证码输入错误{0}次,帐户锁定{1}分钟
|
||||||
|
email.code.not.blank=邮箱验证码不能为空
|
||||||
|
email.code.retry.limit.count=邮箱验证码输入错误{0}次
|
||||||
|
email.code.retry.limit.exceed=邮箱验证码输入错误{0}次,帐户锁定{1}分钟
|
||||||
xcx.code.not.blank=小程序code不能为空
|
xcx.code.not.blank=小程序code不能为空
|
||||||
|
BIN
ruoyi-admin/src/main/resources/ip2region.xdb
Normal file
BIN
ruoyi-admin/src/main/resources/ip2region.xdb
Normal file
Binary file not shown.
@ -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.6.0</version>
|
<version>4.7.0</version>
|
||||||
</parent>
|
</parent>
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
@ -159,6 +159,12 @@
|
|||||||
<artifactId>bcprov-jdk15to18</artifactId>
|
<artifactId>bcprov-jdk15to18</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<!-- 离线IP地址定位库 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.lionsoul</groupId>
|
||||||
|
<artifactId>ip2region</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
</project>
|
</project>
|
||||||
|
@ -7,11 +7,6 @@ package com.ruoyi.common.constant;
|
|||||||
*/
|
*/
|
||||||
public interface CacheConstants {
|
public interface CacheConstants {
|
||||||
|
|
||||||
/**
|
|
||||||
* 登录用户 redis key
|
|
||||||
*/
|
|
||||||
String LOGIN_TOKEN_KEY = "Authorization:login:token:";
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 在线用户 redis key
|
* 在线用户 redis key
|
||||||
*/
|
*/
|
||||||
|
@ -0,0 +1,30 @@
|
|||||||
|
package com.ruoyi.common.core.domain.model;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import javax.validation.constraints.Email;
|
||||||
|
import javax.validation.constraints.NotBlank;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 短信登录对象
|
||||||
|
*
|
||||||
|
* @author Lion Li
|
||||||
|
*/
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class EmailLoginBody {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 邮箱
|
||||||
|
*/
|
||||||
|
@NotBlank(message = "{user.email.not.blank}")
|
||||||
|
@Email(message = "{user.email.not.valid}")
|
||||||
|
private String email;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 邮箱code
|
||||||
|
*/
|
||||||
|
@NotBlank(message = "{email.code.not.blank}")
|
||||||
|
private String emailCode;
|
||||||
|
|
||||||
|
}
|
@ -14,13 +14,13 @@ import javax.validation.constraints.NotBlank;
|
|||||||
public class SmsLoginBody {
|
public class SmsLoginBody {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 用户名
|
* 手机号
|
||||||
*/
|
*/
|
||||||
@NotBlank(message = "{user.phonenumber.not.blank}")
|
@NotBlank(message = "{user.phonenumber.not.blank}")
|
||||||
private String phonenumber;
|
private String phonenumber;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 用户密码
|
* 短信code
|
||||||
*/
|
*/
|
||||||
@NotBlank(message = "{sms.code.not.blank}")
|
@NotBlank(message = "{sms.code.not.blank}")
|
||||||
private String smsCode;
|
private String smsCode;
|
||||||
|
@ -22,6 +22,11 @@ public enum LoginType {
|
|||||||
*/
|
*/
|
||||||
SMS("sms.code.retry.limit.exceed", "sms.code.retry.limit.count"),
|
SMS("sms.code.retry.limit.exceed", "sms.code.retry.limit.count"),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 邮箱登录
|
||||||
|
*/
|
||||||
|
EMAIL("email.code.retry.limit.exceed", "email.code.retry.limit.count"),
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 小程序登录
|
* 小程序登录
|
||||||
*/
|
*/
|
||||||
|
@ -42,7 +42,7 @@ public class DefaultExcelListener<T> extends AnalysisEventListener<T> implements
|
|||||||
private ExcelResult<T> excelResult;
|
private ExcelResult<T> excelResult;
|
||||||
|
|
||||||
public DefaultExcelListener(boolean isValidate) {
|
public DefaultExcelListener(boolean isValidate) {
|
||||||
this.excelResult = new DefautExcelResult<>();
|
this.excelResult = new DefaultExcelResult<>();
|
||||||
this.isValidate = isValidate;
|
this.isValidate = isValidate;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -12,7 +12,7 @@ import java.util.List;
|
|||||||
* @author Yjoioooo
|
* @author Yjoioooo
|
||||||
* @author Lion Li
|
* @author Lion Li
|
||||||
*/
|
*/
|
||||||
public class DefautExcelResult<T> implements ExcelResult<T> {
|
public class DefaultExcelResult<T> implements ExcelResult<T> {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 数据对象list
|
* 数据对象list
|
||||||
@ -26,17 +26,17 @@ public class DefautExcelResult<T> implements ExcelResult<T> {
|
|||||||
@Setter
|
@Setter
|
||||||
private List<String> errorList;
|
private List<String> errorList;
|
||||||
|
|
||||||
public DefautExcelResult() {
|
public DefaultExcelResult() {
|
||||||
this.list = new ArrayList<>();
|
this.list = new ArrayList<>();
|
||||||
this.errorList = new ArrayList<>();
|
this.errorList = new ArrayList<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public DefautExcelResult(List<T> list, List<String> errorList) {
|
public DefaultExcelResult(List<T> list, List<String> errorList) {
|
||||||
this.list = list;
|
this.list = list;
|
||||||
this.errorList = errorList;
|
this.errorList = errorList;
|
||||||
}
|
}
|
||||||
|
|
||||||
public DefautExcelResult(ExcelResult<T> excelResult) {
|
public DefaultExcelResult(ExcelResult<T> excelResult) {
|
||||||
this.list = excelResult.getList();
|
this.list = excelResult.getList();
|
||||||
this.errorList = excelResult.getErrorList();
|
this.errorList = excelResult.getErrorList();
|
||||||
}
|
}
|
@ -66,7 +66,7 @@ public class DataBaseHelper {
|
|||||||
// instr(',0,100,101,' , ',100,') <> 0
|
// instr(',0,100,101,' , ',100,') <> 0
|
||||||
return "instr(','||" + var2 + "||',' , '," + var + ",') <> 0";
|
return "instr(','||" + var2 + "||',' , '," + var + ",') <> 0";
|
||||||
}
|
}
|
||||||
// find_in_set(100 , '0,100,101')
|
// find_in_set('100' , '0,100,101')
|
||||||
return "find_in_set(" + var + " , " + var2 + ") <> 0";
|
return "find_in_set('" + var + "' , " + var2 + ") <> 0";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,6 +10,7 @@ import lombok.NoArgsConstructor;
|
|||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 数据权限助手
|
* 数据权限助手
|
||||||
@ -61,4 +62,32 @@ public class DataPermissionHelper {
|
|||||||
InterceptorIgnoreHelper.clearIgnoreStrategy();
|
InterceptorIgnoreHelper.clearIgnoreStrategy();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 在忽略数据权限中执行
|
||||||
|
*
|
||||||
|
* @param handle 处理执行方法
|
||||||
|
*/
|
||||||
|
public static void ignore(Runnable handle) {
|
||||||
|
enableIgnore();
|
||||||
|
try {
|
||||||
|
handle.run();
|
||||||
|
} finally {
|
||||||
|
disableIgnore();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 在忽略数据权限中执行
|
||||||
|
*
|
||||||
|
* @param handle 处理执行方法
|
||||||
|
*/
|
||||||
|
public static <T> T ignore(Supplier<T> handle) {
|
||||||
|
enableIgnore();
|
||||||
|
try {
|
||||||
|
return handle.get();
|
||||||
|
} finally {
|
||||||
|
disableIgnore();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -19,6 +19,7 @@ public class DeptNameTranslationImpl implements TranslationInterface<String> {
|
|||||||
|
|
||||||
private final DeptService deptService;
|
private final DeptService deptService;
|
||||||
|
|
||||||
|
@Override
|
||||||
public String translation(Object key, String other) {
|
public String translation(Object key, String other) {
|
||||||
return deptService.selectDeptNameByIds(key.toString());
|
return deptService.selectDeptNameByIds(key.toString());
|
||||||
}
|
}
|
||||||
|
@ -20,6 +20,7 @@ public class DictTypeTranslationImpl implements TranslationInterface<String> {
|
|||||||
|
|
||||||
private final DictService dictService;
|
private final DictService dictService;
|
||||||
|
|
||||||
|
@Override
|
||||||
public String translation(Object key, String other) {
|
public String translation(Object key, String other) {
|
||||||
if (key instanceof String && StringUtils.isNotBlank(other)) {
|
if (key instanceof String && StringUtils.isNotBlank(other)) {
|
||||||
return dictService.getDictLabel(other, key.toString());
|
return dictService.getDictLabel(other, key.toString());
|
||||||
|
@ -19,6 +19,7 @@ public class OssUrlTranslationImpl implements TranslationInterface<String> {
|
|||||||
|
|
||||||
private final OssService ossService;
|
private final OssService ossService;
|
||||||
|
|
||||||
|
@Override
|
||||||
public String translation(Object key, String other) {
|
public String translation(Object key, String other) {
|
||||||
return ossService.selectUrlByIds(key.toString());
|
return ossService.selectUrlByIds(key.toString());
|
||||||
}
|
}
|
||||||
|
@ -19,6 +19,7 @@ public class UserNameTranslationImpl implements TranslationInterface<String> {
|
|||||||
|
|
||||||
private final UserService userService;
|
private final UserService userService;
|
||||||
|
|
||||||
|
@Override
|
||||||
public String translation(Object key, String other) {
|
public String translation(Object key, String other) {
|
||||||
if (key instanceof Long) {
|
if (key instanceof Long) {
|
||||||
return userService.selectUserNameById((Long) key);
|
return userService.selectUserNameById((Long) key);
|
||||||
|
@ -0,0 +1,243 @@
|
|||||||
|
package com.ruoyi.common.utils;
|
||||||
|
|
||||||
|
import cn.hutool.core.codec.Base64;
|
||||||
|
import cn.hutool.core.util.ArrayUtil;
|
||||||
|
import cn.hutool.core.util.StrUtil;
|
||||||
|
import cn.hutool.crypto.SecureUtil;
|
||||||
|
import cn.hutool.crypto.SmUtil;
|
||||||
|
import cn.hutool.crypto.asymmetric.KeyType;
|
||||||
|
import cn.hutool.crypto.asymmetric.RSA;
|
||||||
|
import cn.hutool.crypto.asymmetric.SM2;
|
||||||
|
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 安全相关工具类
|
||||||
|
*
|
||||||
|
* @author 老马
|
||||||
|
*/
|
||||||
|
public class EncryptUtils {
|
||||||
|
/**
|
||||||
|
* 公钥
|
||||||
|
*/
|
||||||
|
public static final String PUBLIC_KEY = "publicKey";
|
||||||
|
/**
|
||||||
|
* 私钥
|
||||||
|
*/
|
||||||
|
public static final String PRIVATE_KEY = "privateKey";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base64加密
|
||||||
|
*
|
||||||
|
* @param data 待加密数据
|
||||||
|
* @return 加密后字符串
|
||||||
|
*/
|
||||||
|
public static String encryptByBase64(String data) {
|
||||||
|
return Base64.encode(data, StandardCharsets.UTF_8);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base64解密
|
||||||
|
*
|
||||||
|
* @param data 待解密数据
|
||||||
|
* @return 解密后字符串
|
||||||
|
*/
|
||||||
|
public static String decryptByBase64(String data) {
|
||||||
|
return Base64.decodeStr(data, StandardCharsets.UTF_8);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* AES加密
|
||||||
|
*
|
||||||
|
* @param data 待解密数据
|
||||||
|
* @param password 秘钥字符串
|
||||||
|
* @return 加密后字符串, 采用Base64编码
|
||||||
|
*/
|
||||||
|
public static String encryptByAes(String data, String password) {
|
||||||
|
if (StrUtil.isBlank(password)) {
|
||||||
|
throw new IllegalArgumentException("AES需要传入秘钥信息");
|
||||||
|
}
|
||||||
|
// aes算法的秘钥要求是16位、24位、32位
|
||||||
|
int[] array = {16, 24, 32};
|
||||||
|
if (!ArrayUtil.contains(array, password.length())) {
|
||||||
|
throw new IllegalArgumentException("AES秘钥长度要求为16位、24位、32位");
|
||||||
|
}
|
||||||
|
return SecureUtil.aes(password.getBytes(StandardCharsets.UTF_8)).encryptBase64(data, StandardCharsets.UTF_8);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* AES解密
|
||||||
|
*
|
||||||
|
* @param data 待解密数据
|
||||||
|
* @param password 秘钥字符串
|
||||||
|
* @return 解密后字符串
|
||||||
|
*/
|
||||||
|
public static String decryptByAes(String data, String password) {
|
||||||
|
if (StrUtil.isBlank(password)) {
|
||||||
|
throw new IllegalArgumentException("AES需要传入秘钥信息");
|
||||||
|
}
|
||||||
|
// aes算法的秘钥要求是16位、24位、32位
|
||||||
|
int[] array = {16, 24, 32};
|
||||||
|
if (!ArrayUtil.contains(array, password.length())) {
|
||||||
|
throw new IllegalArgumentException("AES秘钥长度要求为16位、24位、32位");
|
||||||
|
}
|
||||||
|
return SecureUtil.aes(password.getBytes(StandardCharsets.UTF_8)).decryptStr(data, StandardCharsets.UTF_8);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* sm4加密
|
||||||
|
*
|
||||||
|
* @param data 待加密数据
|
||||||
|
* @param password 秘钥字符串
|
||||||
|
* @return 加密后字符串, 采用Base64编码
|
||||||
|
*/
|
||||||
|
public static String encryptBySm4(String data, String password) {
|
||||||
|
if (StrUtil.isBlank(password)) {
|
||||||
|
throw new IllegalArgumentException("SM4需要传入秘钥信息");
|
||||||
|
}
|
||||||
|
// sm4算法的秘钥要求是16位长度
|
||||||
|
int sm4PasswordLength = 16;
|
||||||
|
if (sm4PasswordLength != password.length()) {
|
||||||
|
throw new IllegalArgumentException("SM4秘钥长度要求为16位");
|
||||||
|
}
|
||||||
|
return SmUtil.sm4(password.getBytes(StandardCharsets.UTF_8)).encryptBase64(data, StandardCharsets.UTF_8);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* sm4解密
|
||||||
|
*
|
||||||
|
* @param data 待解密数据
|
||||||
|
* @param password 秘钥字符串
|
||||||
|
* @return 解密后字符串
|
||||||
|
*/
|
||||||
|
public static String decryptBySm4(String data, String password) {
|
||||||
|
if (StrUtil.isBlank(password)) {
|
||||||
|
throw new IllegalArgumentException("SM4需要传入秘钥信息");
|
||||||
|
}
|
||||||
|
// sm4算法的秘钥要求是16位长度
|
||||||
|
int sm4PasswordLength = 16;
|
||||||
|
if (sm4PasswordLength != password.length()) {
|
||||||
|
throw new IllegalArgumentException("SM4秘钥长度要求为16位");
|
||||||
|
}
|
||||||
|
return SmUtil.sm4(password.getBytes(StandardCharsets.UTF_8)).decryptStr(data, StandardCharsets.UTF_8);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 产生sm2加解密需要的公钥和私钥
|
||||||
|
*
|
||||||
|
* @return 公私钥Map
|
||||||
|
*/
|
||||||
|
public static Map<String, String> generateSm2Key() {
|
||||||
|
Map<String, String> keyMap = new HashMap<>(2);
|
||||||
|
SM2 sm2 = SmUtil.sm2();
|
||||||
|
keyMap.put(PRIVATE_KEY, sm2.getPrivateKeyBase64());
|
||||||
|
keyMap.put(PUBLIC_KEY, sm2.getPublicKeyBase64());
|
||||||
|
return keyMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* sm2公钥加密
|
||||||
|
*
|
||||||
|
* @param data 待加密数据
|
||||||
|
* @param publicKey 公钥
|
||||||
|
* @return 加密后字符串, 采用Base64编码
|
||||||
|
*/
|
||||||
|
public static String encryptBySm2(String data, String publicKey) {
|
||||||
|
if (StrUtil.isBlank(publicKey)) {
|
||||||
|
throw new IllegalArgumentException("SM2需要传入公钥进行加密");
|
||||||
|
}
|
||||||
|
SM2 sm2 = SmUtil.sm2(null, publicKey);
|
||||||
|
return sm2.encryptBase64(data, StandardCharsets.UTF_8, KeyType.PublicKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* sm2私钥解密
|
||||||
|
*
|
||||||
|
* @param data 待加密数据
|
||||||
|
* @param privateKey 私钥
|
||||||
|
* @return 解密后字符串
|
||||||
|
*/
|
||||||
|
public static String decryptBySm2(String data, String privateKey) {
|
||||||
|
if (StrUtil.isBlank(privateKey)) {
|
||||||
|
throw new IllegalArgumentException("SM2需要传入私钥进行解密");
|
||||||
|
}
|
||||||
|
SM2 sm2 = SmUtil.sm2(privateKey, null);
|
||||||
|
return sm2.decryptStr(data, KeyType.PrivateKey, StandardCharsets.UTF_8);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 产生RSA加解密需要的公钥和私钥
|
||||||
|
*
|
||||||
|
* @return 公私钥Map
|
||||||
|
*/
|
||||||
|
public static Map<String, String> generateRsaKey() {
|
||||||
|
Map<String, String> keyMap = new HashMap<>(2);
|
||||||
|
RSA rsa = SecureUtil.rsa();
|
||||||
|
keyMap.put(PRIVATE_KEY, rsa.getPrivateKeyBase64());
|
||||||
|
keyMap.put(PUBLIC_KEY, rsa.getPublicKeyBase64());
|
||||||
|
return keyMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* rsa公钥加密
|
||||||
|
*
|
||||||
|
* @param data 待加密数据
|
||||||
|
* @param publicKey 公钥
|
||||||
|
* @return 加密后字符串, 采用Base64编码
|
||||||
|
*/
|
||||||
|
public static String encryptByRsa(String data, String publicKey) {
|
||||||
|
if (StrUtil.isBlank(publicKey)) {
|
||||||
|
throw new IllegalArgumentException("RSA需要传入公钥进行加密");
|
||||||
|
}
|
||||||
|
RSA rsa = SecureUtil.rsa(null, publicKey);
|
||||||
|
return rsa.encryptBase64(data, StandardCharsets.UTF_8, KeyType.PublicKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* rsa私钥解密
|
||||||
|
*
|
||||||
|
* @param data 待加密数据
|
||||||
|
* @param privateKey 私钥
|
||||||
|
* @return 解密后字符串
|
||||||
|
*/
|
||||||
|
public static String decryptByRsa(String data, String privateKey) {
|
||||||
|
if (StrUtil.isBlank(privateKey)) {
|
||||||
|
throw new IllegalArgumentException("RSA需要传入私钥进行解密");
|
||||||
|
}
|
||||||
|
RSA rsa = SecureUtil.rsa(privateKey, null);
|
||||||
|
return rsa.decryptStr(data, KeyType.PrivateKey, StandardCharsets.UTF_8);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* md5加密
|
||||||
|
*
|
||||||
|
* @param data 待加密数据
|
||||||
|
* @return 加密后字符串, 采用Hex编码
|
||||||
|
*/
|
||||||
|
public static String encryptByMd5(String data) {
|
||||||
|
return SecureUtil.md5(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* sha256加密
|
||||||
|
*
|
||||||
|
* @param data 待加密数据
|
||||||
|
* @return 加密后字符串, 采用Hex编码
|
||||||
|
*/
|
||||||
|
public static String encryptBySha256(String data) {
|
||||||
|
return SecureUtil.sha256(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* sm3加密
|
||||||
|
*
|
||||||
|
* @param data 待加密数据
|
||||||
|
* @return 加密后字符串, 采用Hex编码
|
||||||
|
*/
|
||||||
|
public static String encryptBySm3(String data) {
|
||||||
|
return SmUtil.sm3(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,12 +1,7 @@
|
|||||||
package com.ruoyi.common.utils.ip;
|
package com.ruoyi.common.utils.ip;
|
||||||
|
|
||||||
import cn.hutool.core.lang.Dict;
|
|
||||||
import cn.hutool.core.net.NetUtil;
|
import cn.hutool.core.net.NetUtil;
|
||||||
import cn.hutool.http.HtmlUtil;
|
import cn.hutool.http.HtmlUtil;
|
||||||
import cn.hutool.http.HttpUtil;
|
|
||||||
import com.ruoyi.common.config.RuoYiConfig;
|
|
||||||
import com.ruoyi.common.constant.Constants;
|
|
||||||
import com.ruoyi.common.utils.JsonUtils;
|
|
||||||
import com.ruoyi.common.utils.StringUtils;
|
import com.ruoyi.common.utils.StringUtils;
|
||||||
import lombok.AccessLevel;
|
import lombok.AccessLevel;
|
||||||
import lombok.NoArgsConstructor;
|
import lombok.NoArgsConstructor;
|
||||||
@ -21,40 +16,18 @@ import lombok.extern.slf4j.Slf4j;
|
|||||||
@NoArgsConstructor(access = AccessLevel.PRIVATE)
|
@NoArgsConstructor(access = AccessLevel.PRIVATE)
|
||||||
public class AddressUtils {
|
public class AddressUtils {
|
||||||
|
|
||||||
// IP地址查询
|
|
||||||
public static final String IP_URL = "http://whois.pconline.com.cn/ipJson.jsp";
|
|
||||||
|
|
||||||
// 未知地址
|
// 未知地址
|
||||||
public static final String UNKNOWN = "XX XX";
|
public static final String UNKNOWN = "XX XX";
|
||||||
|
|
||||||
public static String getRealAddressByIP(String ip) {
|
public static String getRealAddressByIP(String ip) {
|
||||||
String address = UNKNOWN;
|
|
||||||
if (StringUtils.isBlank(ip)) {
|
if (StringUtils.isBlank(ip)) {
|
||||||
return address;
|
return UNKNOWN;
|
||||||
}
|
}
|
||||||
// 内网不查询
|
// 内网不查询
|
||||||
ip = "0:0:0:0:0:0:0:1".equals(ip) ? "127.0.0.1" : HtmlUtil.cleanHtmlTag(ip);
|
ip = "0:0:0:0:0:0:0:1".equals(ip) ? "127.0.0.1" : HtmlUtil.cleanHtmlTag(ip);
|
||||||
if (NetUtil.isInnerIP(ip)) {
|
if (NetUtil.isInnerIP(ip)) {
|
||||||
return "内网IP";
|
return "内网IP";
|
||||||
}
|
}
|
||||||
if (RuoYiConfig.isAddressEnabled()) {
|
return RegionUtils.getCityInfo(ip);
|
||||||
try {
|
|
||||||
String rspStr = HttpUtil.createGet(IP_URL)
|
|
||||||
.body("ip=" + ip + "&json=true", Constants.GBK)
|
|
||||||
.execute()
|
|
||||||
.body();
|
|
||||||
if (StringUtils.isEmpty(rspStr)) {
|
|
||||||
log.error("获取地理位置异常 {}", ip);
|
|
||||||
return UNKNOWN;
|
|
||||||
}
|
|
||||||
Dict obj = JsonUtils.parseMap(rspStr);
|
|
||||||
String region = obj.getStr("pro");
|
|
||||||
String city = obj.getStr("city");
|
|
||||||
return String.format("%s %s", region, city);
|
|
||||||
} catch (Exception e) {
|
|
||||||
log.error("获取地理位置异常 {}", ip);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return UNKNOWN;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,66 @@
|
|||||||
|
package com.ruoyi.common.utils.ip;
|
||||||
|
|
||||||
|
import cn.hutool.core.io.FileUtil;
|
||||||
|
import cn.hutool.core.io.resource.ClassPathResource;
|
||||||
|
import cn.hutool.core.util.ObjectUtil;
|
||||||
|
import com.ruoyi.common.exception.ServiceException;
|
||||||
|
import com.ruoyi.common.utils.file.FileUtils;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.lionsoul.ip2region.xdb.Searcher;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据ip地址定位工具类,离线方式
|
||||||
|
* 参考地址:<a href="https://gitee.com/lionsoul/ip2region/tree/master/binding/java">集成 ip2region 实现离线IP地址定位库</a>
|
||||||
|
*
|
||||||
|
* @author lishuyan
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
public class RegionUtils {
|
||||||
|
|
||||||
|
private static final Searcher SEARCHER;
|
||||||
|
|
||||||
|
static {
|
||||||
|
String fileName = "/ip2region.xdb";
|
||||||
|
File existFile = FileUtils.file(FileUtil.getTmpDir() + FileUtil.FILE_SEPARATOR + fileName);
|
||||||
|
if (!FileUtils.exist(existFile)) {
|
||||||
|
ClassPathResource fileStream = new ClassPathResource(fileName);
|
||||||
|
if (ObjectUtil.isEmpty(fileStream.getStream())) {
|
||||||
|
throw new ServiceException("RegionUtils初始化失败,原因:IP地址库数据不存在!");
|
||||||
|
}
|
||||||
|
FileUtils.writeFromStream(fileStream.getStream(), existFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
String dbPath = existFile.getPath();
|
||||||
|
|
||||||
|
// 1、从 dbPath 加载整个 xdb 到内存。
|
||||||
|
byte[] cBuff;
|
||||||
|
try {
|
||||||
|
cBuff = Searcher.loadContentFromFile(dbPath);
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new ServiceException("RegionUtils初始化失败,原因:从ip2region.xdb文件加载内容失败!" + e.getMessage());
|
||||||
|
}
|
||||||
|
// 2、使用上述的 cBuff 创建一个完全基于内存的查询对象。
|
||||||
|
try {
|
||||||
|
SEARCHER = Searcher.newWithBuffer(cBuff);
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new ServiceException("RegionUtils初始化失败,原因:" + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据IP地址离线获取城市
|
||||||
|
*/
|
||||||
|
public static String getCityInfo(String ip) {
|
||||||
|
try {
|
||||||
|
ip = ip.trim();
|
||||||
|
// 3、执行查询
|
||||||
|
String region = SEARCHER.search(ip);
|
||||||
|
return region.replace("0|", "").replace("|0", "");
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("IP地址离线获取城市异常 {}", ip);
|
||||||
|
return "未知";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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.6.0</version>
|
<version>4.7.0</version>
|
||||||
</parent>
|
</parent>
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
@ -49,4 +49,16 @@ public class RedisRateLimiterController {
|
|||||||
return R.ok("操作成功", value);
|
return R.ok("操作成功", value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 测试请求IP限流(key基于参数获取)
|
||||||
|
* 同一IP请求受影响
|
||||||
|
*
|
||||||
|
* 简单变量获取 #变量 复杂表达式 #{#变量 != 1 ? 1 : 0}
|
||||||
|
*/
|
||||||
|
@RateLimiter(count = 2, time = 10, limitType = LimitType.IP, key = "#value")
|
||||||
|
@GetMapping("/testObj")
|
||||||
|
public R<String> testObj(String value) {
|
||||||
|
return R.ok("操作成功", value);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -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.6.0</version>
|
<version>4.7.0</version>
|
||||||
</parent>
|
</parent>
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
<artifactId>ruoyi-extend</artifactId>
|
<artifactId>ruoyi-extend</artifactId>
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<artifactId>ruoyi-extend</artifactId>
|
<artifactId>ruoyi-extend</artifactId>
|
||||||
<groupId>com.ruoyi</groupId>
|
<groupId>com.ruoyi</groupId>
|
||||||
<version>4.6.0</version>
|
<version>4.7.0</version>
|
||||||
</parent>
|
</parent>
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
<packaging>jar</packaging>
|
<packaging>jar</packaging>
|
||||||
|
@ -6,6 +6,9 @@ spring:
|
|||||||
profiles:
|
profiles:
|
||||||
active: @profiles.active@
|
active: @profiles.active@
|
||||||
|
|
||||||
|
logging:
|
||||||
|
config: classpath:logback-plus.xml
|
||||||
|
|
||||||
--- # 监控中心服务端配置
|
--- # 监控中心服务端配置
|
||||||
spring:
|
spring:
|
||||||
security:
|
security:
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<artifactId>ruoyi-extend</artifactId>
|
<artifactId>ruoyi-extend</artifactId>
|
||||||
<groupId>com.ruoyi</groupId>
|
<groupId>com.ruoyi</groupId>
|
||||||
<version>4.6.0</version>
|
<version>4.7.0</version>
|
||||||
</parent>
|
</parent>
|
||||||
<artifactId>ruoyi-xxl-job-admin</artifactId>
|
<artifactId>ruoyi-xxl-job-admin</artifactId>
|
||||||
<packaging>jar</packaging>
|
<packaging>jar</packaging>
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package com.xxl.job.admin.controller;
|
package com.xxl.job.admin.controller;
|
||||||
|
|
||||||
|
import com.xxl.job.admin.controller.annotation.PermissionLimit;
|
||||||
import com.xxl.job.admin.core.model.XxlJobGroup;
|
import com.xxl.job.admin.core.model.XxlJobGroup;
|
||||||
import com.xxl.job.admin.core.model.XxlJobRegistry;
|
import com.xxl.job.admin.core.model.XxlJobRegistry;
|
||||||
import com.xxl.job.admin.core.util.I18nUtil;
|
import com.xxl.job.admin.core.util.I18nUtil;
|
||||||
@ -35,12 +36,14 @@ public class JobGroupController {
|
|||||||
private XxlJobRegistryDao xxlJobRegistryDao;
|
private XxlJobRegistryDao xxlJobRegistryDao;
|
||||||
|
|
||||||
@RequestMapping
|
@RequestMapping
|
||||||
|
@PermissionLimit(adminuser = true)
|
||||||
public String index(Model model) {
|
public String index(Model model) {
|
||||||
return "jobgroup/jobgroup.index";
|
return "jobgroup/jobgroup.index";
|
||||||
}
|
}
|
||||||
|
|
||||||
@RequestMapping("/pageList")
|
@RequestMapping("/pageList")
|
||||||
@ResponseBody
|
@ResponseBody
|
||||||
|
@PermissionLimit(adminuser = true)
|
||||||
public Map<String, Object> pageList(HttpServletRequest request,
|
public Map<String, Object> pageList(HttpServletRequest request,
|
||||||
@RequestParam(required = false, defaultValue = "0") int start,
|
@RequestParam(required = false, defaultValue = "0") int start,
|
||||||
@RequestParam(required = false, defaultValue = "10") int length,
|
@RequestParam(required = false, defaultValue = "10") int length,
|
||||||
@ -60,6 +63,7 @@ public class JobGroupController {
|
|||||||
|
|
||||||
@RequestMapping("/save")
|
@RequestMapping("/save")
|
||||||
@ResponseBody
|
@ResponseBody
|
||||||
|
@PermissionLimit(adminuser = true)
|
||||||
public ReturnT<String> save(XxlJobGroup xxlJobGroup) {
|
public ReturnT<String> save(XxlJobGroup xxlJobGroup) {
|
||||||
|
|
||||||
// valid
|
// valid
|
||||||
@ -103,6 +107,7 @@ public class JobGroupController {
|
|||||||
|
|
||||||
@RequestMapping("/update")
|
@RequestMapping("/update")
|
||||||
@ResponseBody
|
@ResponseBody
|
||||||
|
@PermissionLimit(adminuser = true)
|
||||||
public ReturnT<String> update(XxlJobGroup xxlJobGroup) {
|
public ReturnT<String> update(XxlJobGroup xxlJobGroup) {
|
||||||
// valid
|
// valid
|
||||||
if (xxlJobGroup.getAppname() == null || xxlJobGroup.getAppname().trim().length() == 0) {
|
if (xxlJobGroup.getAppname() == null || xxlJobGroup.getAppname().trim().length() == 0) {
|
||||||
@ -171,6 +176,7 @@ public class JobGroupController {
|
|||||||
|
|
||||||
@RequestMapping("/remove")
|
@RequestMapping("/remove")
|
||||||
@ResponseBody
|
@ResponseBody
|
||||||
|
@PermissionLimit(adminuser = true)
|
||||||
public ReturnT<String> remove(int id) {
|
public ReturnT<String> remove(int id) {
|
||||||
|
|
||||||
// valid
|
// valid
|
||||||
@ -190,6 +196,7 @@ public class JobGroupController {
|
|||||||
|
|
||||||
@RequestMapping("/loadById")
|
@RequestMapping("/loadById")
|
||||||
@ResponseBody
|
@ResponseBody
|
||||||
|
@PermissionLimit(adminuser = true)
|
||||||
public ReturnT<XxlJobGroup> loadById(int id) {
|
public ReturnT<XxlJobGroup> loadById(int id) {
|
||||||
XxlJobGroup jobGroup = xxlJobGroupDao.load(id);
|
XxlJobGroup jobGroup = xxlJobGroupDao.load(id);
|
||||||
return jobGroup != null ? new ReturnT<XxlJobGroup>(jobGroup) : new ReturnT<XxlJobGroup>(ReturnT.FAIL_CODE, null);
|
return jobGroup != null ? new ReturnT<XxlJobGroup>(jobGroup) : new ReturnT<XxlJobGroup>(ReturnT.FAIL_CODE, null);
|
||||||
|
@ -130,22 +130,26 @@ public class JobLogController {
|
|||||||
|
|
||||||
model.addAttribute("triggerCode", jobLog.getTriggerCode());
|
model.addAttribute("triggerCode", jobLog.getTriggerCode());
|
||||||
model.addAttribute("handleCode", jobLog.getHandleCode());
|
model.addAttribute("handleCode", jobLog.getHandleCode());
|
||||||
model.addAttribute("executorAddress", jobLog.getExecutorAddress());
|
|
||||||
model.addAttribute("triggerTime", jobLog.getTriggerTime().getTime());
|
|
||||||
model.addAttribute("logId", jobLog.getId());
|
model.addAttribute("logId", jobLog.getId());
|
||||||
return "joblog/joblog.detail";
|
return "joblog/joblog.detail";
|
||||||
}
|
}
|
||||||
|
|
||||||
@RequestMapping("/logDetailCat")
|
@RequestMapping("/logDetailCat")
|
||||||
@ResponseBody
|
@ResponseBody
|
||||||
public ReturnT<LogResult> logDetailCat(String executorAddress, long triggerTime, long logId, int fromLineNum) {
|
public ReturnT<LogResult> logDetailCat(long logId, int fromLineNum) {
|
||||||
try {
|
try {
|
||||||
ExecutorBiz executorBiz = XxlJobScheduler.getExecutorBiz(executorAddress);
|
// valid
|
||||||
ReturnT<LogResult> logResult = executorBiz.log(new LogParam(triggerTime, logId, fromLineNum));
|
XxlJobLog jobLog = xxlJobLogDao.load(logId); // todo, need to improve performance
|
||||||
|
if (jobLog == null) {
|
||||||
|
return new ReturnT<LogResult>(ReturnT.FAIL_CODE, I18nUtil.getString("joblog_logid_unvalid"));
|
||||||
|
}
|
||||||
|
|
||||||
|
// log cat
|
||||||
|
ExecutorBiz executorBiz = XxlJobScheduler.getExecutorBiz(jobLog.getExecutorAddress());
|
||||||
|
ReturnT<LogResult> logResult = executorBiz.log(new LogParam(jobLog.getTriggerTime().getTime(), logId, fromLineNum));
|
||||||
|
|
||||||
// is end
|
// is end
|
||||||
if (logResult.getContent() != null && logResult.getContent().getFromLineNum() > logResult.getContent().getToLineNum()) {
|
if (logResult.getContent() != null && logResult.getContent().getFromLineNum() > logResult.getContent().getToLineNum()) {
|
||||||
XxlJobLog jobLog = xxlJobLogDao.load(logId);
|
|
||||||
if (jobLog.getHandleCode() > 0) {
|
if (jobLog.getHandleCode() > 0) {
|
||||||
logResult.getContent().setEnd(true);
|
logResult.getContent().setEnd(true);
|
||||||
}
|
}
|
||||||
|
@ -16,6 +16,9 @@ spring:
|
|||||||
resources:
|
resources:
|
||||||
static-locations: classpath:/static/
|
static-locations: classpath:/static/
|
||||||
|
|
||||||
|
logging:
|
||||||
|
config: classpath:logback-plus.xml
|
||||||
|
|
||||||
--- # mybatis 配置
|
--- # mybatis 配置
|
||||||
mybatis:
|
mybatis:
|
||||||
mapper-locations: classpath:/mybatis-mapper/*Mapper.xml
|
mapper-locations: classpath:/mybatis-mapper/*Mapper.xml
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
admin_name=Scheduling Center
|
admin_name=Scheduling Center
|
||||||
admin_name_full=Distributed Task Scheduling Platform XXL-JOB
|
admin_name_full=Distributed Task Scheduling Platform XXL-JOB
|
||||||
admin_version=2.3.1
|
admin_version=2.4.0
|
||||||
admin_i18n=en
|
admin_i18n=en
|
||||||
|
|
||||||
## system
|
## system
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
admin_name=任务调度中心
|
admin_name=任务调度中心
|
||||||
admin_name_full=分布式任务调度平台XXL-JOB
|
admin_name_full=分布式任务调度平台XXL-JOB
|
||||||
admin_version=2.3.1
|
admin_version=2.4.0
|
||||||
admin_i18n=
|
admin_i18n=
|
||||||
|
|
||||||
## system
|
## system
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
admin_name=任務調度中心
|
admin_name=任務調度中心
|
||||||
admin_name_full=分布式任務調度平臺XXL-JOB
|
admin_name_full=分布式任務調度平臺XXL-JOB
|
||||||
admin_version=2.3.1
|
admin_version=2.4.0
|
||||||
admin_i18n=
|
admin_i18n=
|
||||||
|
|
||||||
## system
|
## system
|
||||||
|
@ -25,8 +25,6 @@ $(function() {
|
|||||||
async: false, // sync, make log ordered
|
async: false, // sync, make log ordered
|
||||||
url : base_url + '/joblog/logDetailCat',
|
url : base_url + '/joblog/logDetailCat',
|
||||||
data : {
|
data : {
|
||||||
"executorAddress":executorAddress,
|
|
||||||
"triggerTime":triggerTime,
|
|
||||||
"logId":logId,
|
"logId":logId,
|
||||||
"fromLineNum":fromLineNum
|
"fromLineNum":fromLineNum
|
||||||
},
|
},
|
||||||
|
@ -62,8 +62,6 @@
|
|||||||
// 参数
|
// 参数
|
||||||
var triggerCode = '${triggerCode}';
|
var triggerCode = '${triggerCode}';
|
||||||
var handleCode = '${handleCode}';
|
var handleCode = '${handleCode}';
|
||||||
var executorAddress = '${executorAddress!}';
|
|
||||||
var triggerTime = '${triggerTime?c}';
|
|
||||||
var logId = '${logId}';
|
var logId = '${logId}';
|
||||||
</script>
|
</script>
|
||||||
<script src="${request.contextPath}/static/js/joblog.detail.1.js"></script>
|
<script src="${request.contextPath}/static/js/joblog.detail.1.js"></script>
|
||||||
|
@ -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.6.0</version>
|
<version>4.7.0</version>
|
||||||
</parent>
|
</parent>
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
@ -18,6 +18,7 @@ import org.redisson.api.RateType;
|
|||||||
import org.springframework.core.DefaultParameterNameDiscoverer;
|
import org.springframework.core.DefaultParameterNameDiscoverer;
|
||||||
import org.springframework.core.ParameterNameDiscoverer;
|
import org.springframework.core.ParameterNameDiscoverer;
|
||||||
import org.springframework.expression.EvaluationContext;
|
import org.springframework.expression.EvaluationContext;
|
||||||
|
import org.springframework.expression.Expression;
|
||||||
import org.springframework.expression.ExpressionParser;
|
import org.springframework.expression.ExpressionParser;
|
||||||
import org.springframework.expression.ParserContext;
|
import org.springframework.expression.ParserContext;
|
||||||
import org.springframework.expression.common.TemplateParserContext;
|
import org.springframework.expression.common.TemplateParserContext;
|
||||||
@ -102,7 +103,14 @@ public class RateLimiterAspect {
|
|||||||
}
|
}
|
||||||
// 解析返回给key
|
// 解析返回给key
|
||||||
try {
|
try {
|
||||||
key = parser.parseExpression(key, parserContext).getValue(context, String.class) + ":";
|
Expression expression;
|
||||||
|
if (StringUtils.startsWith(key, parserContext.getExpressionPrefix())
|
||||||
|
&& StringUtils.endsWith(key, parserContext.getExpressionSuffix())) {
|
||||||
|
expression = parser.parseExpression(key, parserContext);
|
||||||
|
} else {
|
||||||
|
expression = parser.parseExpression(key);
|
||||||
|
}
|
||||||
|
key = expression.getValue(context, String.class) + ":";
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw new ServiceException("限流key解析异常!请联系管理员!");
|
throw new ServiceException("限流key解析异常!请联系管理员!");
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
package com.ruoyi.framework.encrypt;
|
package com.ruoyi.framework.encrypt;
|
||||||
|
|
||||||
import cn.hutool.core.collection.CollectionUtil;
|
import cn.hutool.core.collection.CollUtil;
|
||||||
import cn.hutool.core.util.ObjectUtil;
|
import cn.hutool.core.util.ObjectUtil;
|
||||||
import com.ruoyi.common.annotation.EncryptField;
|
import com.ruoyi.common.annotation.EncryptField;
|
||||||
import com.ruoyi.common.encrypt.EncryptContext;
|
import com.ruoyi.common.encrypt.EncryptContext;
|
||||||
@ -62,12 +62,12 @@ public class MybatisDecryptInterceptor implements Interceptor {
|
|||||||
}
|
}
|
||||||
if (sourceObject instanceof List<?>) {
|
if (sourceObject instanceof List<?>) {
|
||||||
List<?> sourceList = (List<?>) sourceObject;
|
List<?> sourceList = (List<?>) sourceObject;
|
||||||
if(CollectionUtil.isEmpty(sourceList)) {
|
if(CollUtil.isEmpty(sourceList)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// 判断第一个元素是否含有注解。如果没有直接返回,提高效率
|
// 判断第一个元素是否含有注解。如果没有直接返回,提高效率
|
||||||
Object firstItem = sourceList.get(0);
|
Object firstItem = sourceList.get(0);
|
||||||
if (CollectionUtil.isEmpty(encryptorManager.getFieldCache(firstItem.getClass()))) {
|
if (ObjectUtil.isNull(firstItem) || CollUtil.isEmpty(encryptorManager.getFieldCache(firstItem.getClass()))) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
((List<?>) sourceObject).forEach(this::decryptHandler);
|
((List<?>) sourceObject).forEach(this::decryptHandler);
|
||||||
@ -91,6 +91,9 @@ public class MybatisDecryptInterceptor implements Interceptor {
|
|||||||
* @return 加密后结果
|
* @return 加密后结果
|
||||||
*/
|
*/
|
||||||
private String decryptField(String value, Field field) {
|
private String decryptField(String value, Field field) {
|
||||||
|
if (ObjectUtil.isNull(value)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
EncryptField encryptField = field.getAnnotation(EncryptField.class);
|
EncryptField encryptField = field.getAnnotation(EncryptField.class);
|
||||||
EncryptContext encryptContext = new EncryptContext();
|
EncryptContext encryptContext = new EncryptContext();
|
||||||
encryptContext.setAlgorithm(encryptField.algorithm() == AlgorithmType.DEFAULT ? defaultProperties.getAlgorithm() : encryptField.algorithm());
|
encryptContext.setAlgorithm(encryptField.algorithm() == AlgorithmType.DEFAULT ? defaultProperties.getAlgorithm() : encryptField.algorithm());
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
package com.ruoyi.framework.encrypt;
|
package com.ruoyi.framework.encrypt;
|
||||||
|
|
||||||
import cn.hutool.core.collection.CollectionUtil;
|
import cn.hutool.core.collection.CollUtil;
|
||||||
import cn.hutool.core.util.ObjectUtil;
|
import cn.hutool.core.util.ObjectUtil;
|
||||||
import com.ruoyi.common.annotation.EncryptField;
|
import com.ruoyi.common.annotation.EncryptField;
|
||||||
import com.ruoyi.common.encrypt.EncryptContext;
|
import com.ruoyi.common.encrypt.EncryptContext;
|
||||||
@ -72,12 +72,12 @@ public class MybatisEncryptInterceptor implements Interceptor {
|
|||||||
}
|
}
|
||||||
if (sourceObject instanceof List<?>) {
|
if (sourceObject instanceof List<?>) {
|
||||||
List<?> sourceList = (List<?>) sourceObject;
|
List<?> sourceList = (List<?>) sourceObject;
|
||||||
if(CollectionUtil.isEmpty(sourceList)) {
|
if(CollUtil.isEmpty(sourceList)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// 判断第一个元素是否含有注解。如果没有直接返回,提高效率
|
// 判断第一个元素是否含有注解。如果没有直接返回,提高效率
|
||||||
Object firstItem = sourceList.get(0);
|
Object firstItem = sourceList.get(0);
|
||||||
if (CollectionUtil.isEmpty(encryptorManager.getFieldCache(firstItem.getClass()))) {
|
if (ObjectUtil.isNull(firstItem) || CollUtil.isEmpty(encryptorManager.getFieldCache(firstItem.getClass()))) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
((List<?>) sourceObject).forEach(this::encryptHandler);
|
((List<?>) sourceObject).forEach(this::encryptHandler);
|
||||||
@ -101,6 +101,9 @@ public class MybatisEncryptInterceptor implements Interceptor {
|
|||||||
* @return 加密后结果
|
* @return 加密后结果
|
||||||
*/
|
*/
|
||||||
private String encryptField(String value, Field field) {
|
private String encryptField(String value, Field field) {
|
||||||
|
if (ObjectUtil.isNull(value)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
EncryptField encryptField = field.getAnnotation(EncryptField.class);
|
EncryptField encryptField = field.getAnnotation(EncryptField.class);
|
||||||
EncryptContext encryptContext = new EncryptContext();
|
EncryptContext encryptContext = new EncryptContext();
|
||||||
encryptContext.setAlgorithm(encryptField.algorithm() == AlgorithmType.DEFAULT ? defaultProperties.getAlgorithm() : encryptField.algorithm());
|
encryptContext.setAlgorithm(encryptField.algorithm() == AlgorithmType.DEFAULT ? defaultProperties.getAlgorithm() : encryptField.algorithm());
|
||||||
|
@ -73,7 +73,7 @@ public class CreateAndUpdateMetaObjectHandler implements MetaObjectHandler {
|
|||||||
log.warn("自动注入警告 => 用户未登录");
|
log.warn("自动注入警告 => 用户未登录");
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return loginUser.getUsername();
|
return ObjectUtil.isNotNull(loginUser) ? loginUser.getUsername() : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -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.6.0</version>
|
<version>4.7.0</version>
|
||||||
</parent>
|
</parent>
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
@ -317,13 +317,11 @@ public class GenTableServiceImpl implements IGenTableService {
|
|||||||
column.setIsRequired(prevColumn.getIsRequired());
|
column.setIsRequired(prevColumn.getIsRequired());
|
||||||
column.setHtmlType(prevColumn.getHtmlType());
|
column.setHtmlType(prevColumn.getHtmlType());
|
||||||
}
|
}
|
||||||
genTableColumnMapper.updateById(column);
|
|
||||||
} else {
|
|
||||||
genTableColumnMapper.insert(column);
|
|
||||||
}
|
}
|
||||||
|
saveColumns.add(column);
|
||||||
});
|
});
|
||||||
if (CollUtil.isNotEmpty(saveColumns)) {
|
if (CollUtil.isNotEmpty(saveColumns)) {
|
||||||
genTableColumnMapper.insertBatch(saveColumns);
|
genTableColumnMapper.insertOrUpdateBatch(saveColumns);
|
||||||
}
|
}
|
||||||
List<GenTableColumn> delColumns = StreamUtils.filter(tableColumns, column -> !dbTableColumnNames.contains(column.getColumnName()));
|
List<GenTableColumn> delColumns = StreamUtils.filter(tableColumns, column -> !dbTableColumnNames.contains(column.getColumnName()));
|
||||||
if (CollUtil.isNotEmpty(delColumns)) {
|
if (CollUtil.isNotEmpty(delColumns)) {
|
||||||
|
@ -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.6.0</version>
|
<version>4.7.0</version>
|
||||||
</parent>
|
</parent>
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
<packaging>jar</packaging>
|
<packaging>jar</packaging>
|
||||||
|
@ -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.6.0</version>
|
<version>4.7.0</version>
|
||||||
</parent>
|
</parent>
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
@ -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.6.0</version>
|
<version>4.7.0</version>
|
||||||
</parent>
|
</parent>
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
@ -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.6.0</version>
|
<version>4.7.0</version>
|
||||||
</parent>
|
</parent>
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
@ -76,6 +76,14 @@ public interface SysUserMapper extends BaseMapperPlus<SysUserMapper, SysUser, Sy
|
|||||||
*/
|
*/
|
||||||
SysUser selectUserByPhonenumber(String phonenumber);
|
SysUser selectUserByPhonenumber(String phonenumber);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通过邮箱查询用户
|
||||||
|
*
|
||||||
|
* @param email 邮箱
|
||||||
|
* @return 用户对象信息
|
||||||
|
*/
|
||||||
|
SysUser selectUserByEmail(String email);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 通过用户ID查询用户
|
* 通过用户ID查询用户
|
||||||
*
|
*
|
||||||
|
@ -176,4 +176,6 @@ public interface ISysRoleService {
|
|||||||
* @return 结果
|
* @return 结果
|
||||||
*/
|
*/
|
||||||
int insertAuthUsers(Long roleId, Long[] userIds);
|
int insertAuthUsers(Long roleId, Long[] userIds);
|
||||||
|
|
||||||
|
void cleanOnlineUserByRole(Long roleId);
|
||||||
}
|
}
|
||||||
|
@ -32,7 +32,6 @@ import lombok.extern.slf4j.Slf4j;
|
|||||||
import org.springframework.beans.factory.annotation.Value;
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
@ -67,11 +66,10 @@ public class SysLoginService {
|
|||||||
* @return 结果
|
* @return 结果
|
||||||
*/
|
*/
|
||||||
public String login(String username, String password, String code, String uuid) {
|
public String login(String username, String password, String code, String uuid) {
|
||||||
HttpServletRequest request = ServletUtils.getRequest();
|
|
||||||
boolean captchaEnabled = configService.selectCaptchaEnabled();
|
boolean captchaEnabled = configService.selectCaptchaEnabled();
|
||||||
// 验证码开关
|
// 验证码开关
|
||||||
if (captchaEnabled) {
|
if (captchaEnabled) {
|
||||||
validateCaptcha(username, code, uuid, request);
|
validateCaptcha(username, code, uuid);
|
||||||
}
|
}
|
||||||
SysUser user = loadUserByUsername(username);
|
SysUser user = loadUserByUsername(username);
|
||||||
checkLogin(LoginType.PASSWORD, username, () -> !BCrypt.checkpw(password, user.getPassword()));
|
checkLogin(LoginType.PASSWORD, username, () -> !BCrypt.checkpw(password, user.getPassword()));
|
||||||
@ -100,6 +98,20 @@ public class SysLoginService {
|
|||||||
return StpUtil.getTokenValue();
|
return StpUtil.getTokenValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String emailLogin(String email, String emailCode) {
|
||||||
|
// 通过手机号查找用户
|
||||||
|
SysUser user = loadUserByEmail(email);
|
||||||
|
|
||||||
|
checkLogin(LoginType.EMAIL, user.getUserName(), () -> !validateEmailCode(email, emailCode));
|
||||||
|
// 此处可根据登录用户的数据不同 自行创建 loginUser
|
||||||
|
LoginUser loginUser = buildLoginUser(user);
|
||||||
|
// 生成token
|
||||||
|
LoginHelper.loginByDevice(loginUser, DeviceType.APP);
|
||||||
|
|
||||||
|
recordLogininfor(user.getUserName(), Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success"));
|
||||||
|
recordLoginInfo(user.getUserId(), user.getUserName());
|
||||||
|
return StpUtil.getTokenValue();
|
||||||
|
}
|
||||||
|
|
||||||
public String xcxLogin(String xcxCode) {
|
public String xcxLogin(String xcxCode) {
|
||||||
// xcxCode 为 小程序调用 wx.login 授权后获取
|
// xcxCode 为 小程序调用 wx.login 授权后获取
|
||||||
@ -140,7 +152,6 @@ public class SysLoginService {
|
|||||||
* @param username 用户名
|
* @param username 用户名
|
||||||
* @param status 状态
|
* @param status 状态
|
||||||
* @param message 消息内容
|
* @param message 消息内容
|
||||||
* @return
|
|
||||||
*/
|
*/
|
||||||
private void recordLogininfor(String username, String status, String message) {
|
private void recordLogininfor(String username, String status, String message) {
|
||||||
LogininforEvent logininforEvent = new LogininforEvent();
|
LogininforEvent logininforEvent = new LogininforEvent();
|
||||||
@ -163,6 +174,18 @@ public class SysLoginService {
|
|||||||
return code.equals(smsCode);
|
return code.equals(smsCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 校验邮箱验证码
|
||||||
|
*/
|
||||||
|
private boolean validateEmailCode(String email, String emailCode) {
|
||||||
|
String code = RedisUtils.getCacheObject(CacheConstants.CAPTCHA_CODE_KEY + email);
|
||||||
|
if (StringUtils.isBlank(code)) {
|
||||||
|
recordLogininfor(email, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.expire"));
|
||||||
|
throw new CaptchaExpireException();
|
||||||
|
}
|
||||||
|
return code.equals(emailCode);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 校验验证码
|
* 校验验证码
|
||||||
*
|
*
|
||||||
@ -170,7 +193,7 @@ public class SysLoginService {
|
|||||||
* @param code 验证码
|
* @param code 验证码
|
||||||
* @param uuid 唯一标识
|
* @param uuid 唯一标识
|
||||||
*/
|
*/
|
||||||
public void validateCaptcha(String username, String code, String uuid, HttpServletRequest request) {
|
public void validateCaptcha(String username, String code, String uuid) {
|
||||||
String verifyKey = CacheConstants.CAPTCHA_CODE_KEY + StringUtils.defaultString(uuid, "");
|
String verifyKey = CacheConstants.CAPTCHA_CODE_KEY + StringUtils.defaultString(uuid, "");
|
||||||
String captcha = RedisUtils.getCacheObject(verifyKey);
|
String captcha = RedisUtils.getCacheObject(verifyKey);
|
||||||
RedisUtils.deleteObject(verifyKey);
|
RedisUtils.deleteObject(verifyKey);
|
||||||
@ -212,6 +235,20 @@ public class SysLoginService {
|
|||||||
return userMapper.selectUserByPhonenumber(phonenumber);
|
return userMapper.selectUserByPhonenumber(phonenumber);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private SysUser loadUserByEmail(String email) {
|
||||||
|
SysUser user = userMapper.selectOne(new LambdaQueryWrapper<SysUser>()
|
||||||
|
.select(SysUser::getPhonenumber, SysUser::getStatus)
|
||||||
|
.eq(SysUser::getEmail, email));
|
||||||
|
if (ObjectUtil.isNull(user)) {
|
||||||
|
log.info("登录用户:{} 不存在.", email);
|
||||||
|
throw new UserException("user.not.exists", email);
|
||||||
|
} else if (UserStatus.DISABLE.getCode().equals(user.getStatus())) {
|
||||||
|
log.info("登录用户:{} 已被停用.", email);
|
||||||
|
throw new UserException("user.blocked", email);
|
||||||
|
}
|
||||||
|
return userMapper.selectUserByEmail(email);
|
||||||
|
}
|
||||||
|
|
||||||
private SysUser loadUserByOpenid(String openid) {
|
private SysUser loadUserByOpenid(String openid) {
|
||||||
// 使用 openid 查询绑定用户 如未绑定用户 则根据业务自行处理 例如 创建默认用户
|
// 使用 openid 查询绑定用户 如未绑定用户 则根据业务自行处理 例如 创建默认用户
|
||||||
// todo 自行实现 userService.selectUserByOpenid(openid);
|
// todo 自行实现 userService.selectUserByOpenid(openid);
|
||||||
|
@ -3,7 +3,6 @@ package com.ruoyi.system.service;
|
|||||||
import cn.dev33.satoken.secure.BCrypt;
|
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.core.domain.event.LogininforEvent;
|
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;
|
||||||
@ -19,8 +18,6 @@ import com.ruoyi.common.utils.spring.SpringUtils;
|
|||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 注册校验方法
|
* 注册校验方法
|
||||||
*
|
*
|
||||||
@ -37,7 +34,6 @@ public class SysRegisterService {
|
|||||||
* 注册
|
* 注册
|
||||||
*/
|
*/
|
||||||
public void register(RegisterBody registerBody) {
|
public void register(RegisterBody registerBody) {
|
||||||
HttpServletRequest request = ServletUtils.getRequest();
|
|
||||||
String username = registerBody.getUsername();
|
String username = registerBody.getUsername();
|
||||||
String password = registerBody.getPassword();
|
String password = registerBody.getPassword();
|
||||||
// 校验用户类型是否存在
|
// 校验用户类型是否存在
|
||||||
@ -46,7 +42,7 @@ public class SysRegisterService {
|
|||||||
boolean captchaEnabled = configService.selectCaptchaEnabled();
|
boolean captchaEnabled = configService.selectCaptchaEnabled();
|
||||||
// 验证码开关
|
// 验证码开关
|
||||||
if (captchaEnabled) {
|
if (captchaEnabled) {
|
||||||
validateCaptcha(username, registerBody.getCode(), registerBody.getUuid(), request);
|
validateCaptcha(username, registerBody.getCode(), registerBody.getUuid());
|
||||||
}
|
}
|
||||||
SysUser sysUser = new SysUser();
|
SysUser sysUser = new SysUser();
|
||||||
sysUser.setUserName(username);
|
sysUser.setUserName(username);
|
||||||
@ -70,9 +66,8 @@ public class SysRegisterService {
|
|||||||
* @param username 用户名
|
* @param username 用户名
|
||||||
* @param code 验证码
|
* @param code 验证码
|
||||||
* @param uuid 唯一标识
|
* @param uuid 唯一标识
|
||||||
* @return 结果
|
|
||||||
*/
|
*/
|
||||||
public void validateCaptcha(String username, String code, String uuid, HttpServletRequest request) {
|
public void validateCaptcha(String username, String code, String uuid) {
|
||||||
String verifyKey = CacheConstants.CAPTCHA_CODE_KEY + StringUtils.defaultString(uuid, "");
|
String verifyKey = CacheConstants.CAPTCHA_CODE_KEY + StringUtils.defaultString(uuid, "");
|
||||||
String captcha = RedisUtils.getCacheObject(verifyKey);
|
String captcha = RedisUtils.getCacheObject(verifyKey);
|
||||||
RedisUtils.deleteObject(verifyKey);
|
RedisUtils.deleteObject(verifyKey);
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
package com.ruoyi.system.service.impl;
|
package com.ruoyi.system.service.impl;
|
||||||
|
|
||||||
|
import cn.dev33.satoken.exception.NotLoginException;
|
||||||
|
import cn.dev33.satoken.stp.StpUtil;
|
||||||
import cn.hutool.core.collection.CollUtil;
|
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;
|
||||||
@ -10,6 +12,7 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
|||||||
import com.ruoyi.common.constant.UserConstants;
|
import com.ruoyi.common.constant.UserConstants;
|
||||||
import com.ruoyi.common.core.domain.PageQuery;
|
import com.ruoyi.common.core.domain.PageQuery;
|
||||||
import com.ruoyi.common.core.domain.entity.SysRole;
|
import com.ruoyi.common.core.domain.entity.SysRole;
|
||||||
|
import com.ruoyi.common.core.domain.model.LoginUser;
|
||||||
import com.ruoyi.common.core.page.TableDataInfo;
|
import com.ruoyi.common.core.page.TableDataInfo;
|
||||||
import com.ruoyi.common.exception.ServiceException;
|
import com.ruoyi.common.exception.ServiceException;
|
||||||
import com.ruoyi.common.helper.LoginHelper;
|
import com.ruoyi.common.helper.LoginHelper;
|
||||||
@ -70,7 +73,7 @@ public class SysRoleServiceImpl implements ISysRoleService {
|
|||||||
.like(StringUtils.isNotBlank(role.getRoleKey()), "r.role_key", role.getRoleKey())
|
.like(StringUtils.isNotBlank(role.getRoleKey()), "r.role_key", role.getRoleKey())
|
||||||
.between(params.get("beginTime") != null && params.get("endTime") != null,
|
.between(params.get("beginTime") != null && params.get("endTime") != null,
|
||||||
"r.create_time", params.get("beginTime"), params.get("endTime"))
|
"r.create_time", params.get("beginTime"), params.get("endTime"))
|
||||||
.orderByAsc("r.role_sort");
|
.orderByAsc("r.role_sort").orderByAsc("r.create_time");
|
||||||
return wrapper;
|
return wrapper;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -362,9 +365,13 @@ public class SysRoleServiceImpl implements ISysRoleService {
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public int deleteAuthUser(SysUserRole userRole) {
|
public int deleteAuthUser(SysUserRole userRole) {
|
||||||
return userRoleMapper.delete(new LambdaQueryWrapper<SysUserRole>()
|
int rows = userRoleMapper.delete(new LambdaQueryWrapper<SysUserRole>()
|
||||||
.eq(SysUserRole::getRoleId, userRole.getRoleId())
|
.eq(SysUserRole::getRoleId, userRole.getRoleId())
|
||||||
.eq(SysUserRole::getUserId, userRole.getUserId()));
|
.eq(SysUserRole::getUserId, userRole.getUserId()));
|
||||||
|
if (rows > 0) {
|
||||||
|
cleanOnlineUserByRole(userRole.getRoleId());
|
||||||
|
}
|
||||||
|
return rows;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -376,9 +383,13 @@ public class SysRoleServiceImpl implements ISysRoleService {
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public int deleteAuthUsers(Long roleId, Long[] userIds) {
|
public int deleteAuthUsers(Long roleId, Long[] userIds) {
|
||||||
return userRoleMapper.delete(new LambdaQueryWrapper<SysUserRole>()
|
int rows = userRoleMapper.delete(new LambdaQueryWrapper<SysUserRole>()
|
||||||
.eq(SysUserRole::getRoleId, roleId)
|
.eq(SysUserRole::getRoleId, roleId)
|
||||||
.in(SysUserRole::getUserId, Arrays.asList(userIds)));
|
.in(SysUserRole::getUserId, Arrays.asList(userIds)));
|
||||||
|
if (rows > 0) {
|
||||||
|
cleanOnlineUserByRole(roleId);
|
||||||
|
}
|
||||||
|
return rows;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -401,6 +412,32 @@ public class SysRoleServiceImpl implements ISysRoleService {
|
|||||||
if (CollUtil.isNotEmpty(list)) {
|
if (CollUtil.isNotEmpty(list)) {
|
||||||
rows = userRoleMapper.insertBatch(list) ? list.size() : 0;
|
rows = userRoleMapper.insertBatch(list) ? list.size() : 0;
|
||||||
}
|
}
|
||||||
|
if (rows > 0) {
|
||||||
|
cleanOnlineUserByRole(roleId);
|
||||||
|
}
|
||||||
return rows;
|
return rows;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void cleanOnlineUserByRole(Long roleId) {
|
||||||
|
List<String> keys = StpUtil.searchTokenValue("", 0, -1, false);
|
||||||
|
if (CollUtil.isEmpty(keys)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 角色关联的在线用户量过大会导致redis阻塞卡顿 谨慎操作
|
||||||
|
keys.parallelStream().forEach(key -> {
|
||||||
|
String token = StringUtils.substringAfterLast(key, ":");
|
||||||
|
// 如果已经过期则跳过
|
||||||
|
if (StpUtil.stpLogic.getTokenActivityTimeoutByToken(token) < -1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
LoginUser loginUser = LoginHelper.getLoginUser(token);
|
||||||
|
if (loginUser.getRoles().stream().anyMatch(r -> r.getRoleId().equals(roleId))) {
|
||||||
|
try {
|
||||||
|
StpUtil.logoutByTokenValue(token);
|
||||||
|
} catch (NotLoginException ignored) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -128,6 +128,11 @@
|
|||||||
where u.del_flag = '0' and u.phonenumber = #{phonenumber}
|
where u.del_flag = '0' and u.phonenumber = #{phonenumber}
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
|
<select id="selectUserByEmail" parameterType="String" resultMap="SysUserResult">
|
||||||
|
<include refid="selectUserVo"/>
|
||||||
|
where u.del_flag = '0' and u.email = #{email}
|
||||||
|
</select>
|
||||||
|
|
||||||
<select id="selectUserById" parameterType="Long" resultMap="SysUserResult">
|
<select id="selectUserById" parameterType="Long" resultMap="SysUserResult">
|
||||||
<include refid="selectUserVo"/>
|
<include refid="selectUserVo"/>
|
||||||
where u.del_flag = '0' and u.user_id = #{userId}
|
where u.del_flag = '0' and u.user_id = #{userId}
|
||||||
|
@ -4,14 +4,16 @@ VUE_APP_TITLE = RuoYi-Vue-Plus后台管理系统
|
|||||||
# 生产环境配置
|
# 生产环境配置
|
||||||
ENV = 'production'
|
ENV = 'production'
|
||||||
|
|
||||||
|
# 若依管理系统/生产环境
|
||||||
|
VUE_APP_BASE_API = '/prod-api'
|
||||||
|
|
||||||
# 应用访问路径 例如使用前缀 /admin/
|
# 应用访问路径 例如使用前缀 /admin/
|
||||||
VUE_APP_CONTEXT_PATH = '/'
|
VUE_APP_CONTEXT_PATH = '/'
|
||||||
|
|
||||||
# 监控地址
|
# 监控地址
|
||||||
VUE_APP_MONITRO_ADMIN = '/admin/login'
|
VUE_APP_MONITRO_ADMIN = '/admin/login'
|
||||||
|
|
||||||
# 监控地址
|
# xxl-job 控制台地址
|
||||||
VUE_APP_XXL_JOB_ADMIN = '/xxl-job-admin'
|
VUE_APP_XXL_JOB_ADMIN = '/xxl-job-admin'
|
||||||
|
|
||||||
# 若依管理系统/生产环境
|
|
||||||
VUE_APP_BASE_API = '/prod-api'
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "ruoyi-vue-plus",
|
"name": "ruoyi-vue-plus",
|
||||||
"version": "4.6.0",
|
"version": "4.7.0",
|
||||||
"description": "RuoYi-Vue-Plus后台管理系统",
|
"description": "RuoYi-Vue-Plus后台管理系统",
|
||||||
"author": "LionLi",
|
"author": "LionLi",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
@ -32,7 +32,7 @@
|
|||||||
],
|
],
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://gitee.com/y_project/RuoYi-Vue.git"
|
"url": "https://gitee.com/dromara/RuoYi-Vue-Plus.git"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@riophae/vue-treeselect": "0.4.0",
|
"@riophae/vue-treeselect": "0.4.0",
|
||||||
|
Binary file not shown.
Before Width: | Height: | Size: 79 KiB After Width: | Height: | Size: 269 KiB |
@ -180,12 +180,3 @@ aside {
|
|||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//refine vue-multiselect plugin
|
|
||||||
.multiselect {
|
|
||||||
line-height: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.multiselect--active {
|
|
||||||
z-index: 1000 !important;
|
|
||||||
}
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
#app {
|
#app {
|
||||||
|
|
||||||
.main-container {
|
.main-container {
|
||||||
min-height: 100%;
|
height: 100%;
|
||||||
transition: margin-left .28s;
|
transition: margin-left .28s;
|
||||||
margin-left: $base-sidebar-width;
|
margin-left: $base-sidebar-width;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
@ -18,10 +18,6 @@
|
|||||||
transition: all .5s;
|
transition: all .5s;
|
||||||
}
|
}
|
||||||
|
|
||||||
.fade-transform-leave-active {
|
|
||||||
position: absolute;
|
|
||||||
}
|
|
||||||
|
|
||||||
.fade-transform-enter {
|
.fade-transform-enter {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
transform: translateX(-30px);
|
transform: translateX(-30px);
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
:key="item.value"
|
:key="item.value"
|
||||||
:index="index"
|
:index="index"
|
||||||
:class="item.raw.cssClass"
|
:class="item.raw.cssClass"
|
||||||
>{{ item.label }}</span
|
>{{ item.label + ' ' }}</span
|
||||||
>
|
>
|
||||||
<el-tag
|
<el-tag
|
||||||
v-else
|
v-else
|
||||||
@ -17,10 +17,13 @@
|
|||||||
:type="item.raw.listClass == 'primary' ? '' : item.raw.listClass"
|
:type="item.raw.listClass == 'primary' ? '' : item.raw.listClass"
|
||||||
:class="item.raw.cssClass"
|
:class="item.raw.cssClass"
|
||||||
>
|
>
|
||||||
{{ item.label }}
|
{{ item.label + ' ' }}
|
||||||
</el-tag>
|
</el-tag>
|
||||||
</template>
|
</template>
|
||||||
</template>
|
</template>
|
||||||
|
<template v-if="unmatch && showValue">
|
||||||
|
{{ unmatchArray | handleArray }}
|
||||||
|
</template>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -33,6 +36,16 @@ export default {
|
|||||||
default: null,
|
default: null,
|
||||||
},
|
},
|
||||||
value: [Number, String, Array],
|
value: [Number, String, Array],
|
||||||
|
// 当未找到匹配的数据时,显示value
|
||||||
|
showValue: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
unmatchArray: [], // 记录未匹配的项
|
||||||
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
values() {
|
values() {
|
||||||
@ -42,7 +55,34 @@ export default {
|
|||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
unmatch(){
|
||||||
|
this.unmatchArray = [];
|
||||||
|
if (this.value !== null && typeof this.value !== 'undefined') {
|
||||||
|
// 传入值为非数组
|
||||||
|
if(!Array.isArray(this.value)){
|
||||||
|
if(this.options.some(v=> v.value == this.value )) return false;
|
||||||
|
this.unmatchArray.push(this.value);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// 传入值为Array
|
||||||
|
this.value.forEach(item => {
|
||||||
|
if (!this.options.some(v=> v.value == item )) this.unmatchArray.push(item)
|
||||||
|
});
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// 没有value不显示
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
|
||||||
},
|
},
|
||||||
|
filters: {
|
||||||
|
handleArray(array) {
|
||||||
|
if(array.length===0) return '';
|
||||||
|
return array.reduce((pre, cur) => {
|
||||||
|
return pre + ' ' + cur;
|
||||||
|
})
|
||||||
|
},
|
||||||
|
}
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
@ -1,13 +1,17 @@
|
|||||||
<!-- @author zhengjie -->
|
<!-- @author zhengjie -->
|
||||||
<template>
|
<template>
|
||||||
<div class="icon-body">
|
<div class="icon-body">
|
||||||
<el-input v-model="name" style="position: relative;" clearable placeholder="请输入图标名称" @clear="filterIcons" @input.native="filterIcons">
|
<el-input v-model="name" class="icon-search" clearable placeholder="请输入图标名称" @clear="filterIcons" @input="filterIcons">
|
||||||
<i slot="suffix" class="el-icon-search el-input__icon" />
|
<i slot="suffix" class="el-icon-search el-input__icon" />
|
||||||
</el-input>
|
</el-input>
|
||||||
<div class="icon-list">
|
<div class="icon-list">
|
||||||
<div v-for="(item, index) in iconList" :key="index" @click="selectedIcon(item)">
|
<div class="list-container">
|
||||||
<svg-icon :icon-class="item" style="height: 30px;width: 16px;" />
|
<div v-for="(item, index) in iconList" class="icon-item-wrapper" :key="index" @click="selectedIcon(item)">
|
||||||
<span>{{ item }}</span>
|
<div :class="['icon-item', { active: activeIcon === item }]">
|
||||||
|
<svg-icon :icon-class="item" class-name="icon" style="height: 25px;width: 16px;"/>
|
||||||
|
<span>{{ item }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -17,6 +21,11 @@
|
|||||||
import icons from './requireIcons'
|
import icons from './requireIcons'
|
||||||
export default {
|
export default {
|
||||||
name: 'IconSelect',
|
name: 'IconSelect',
|
||||||
|
props: {
|
||||||
|
activeIcon: {
|
||||||
|
type: String
|
||||||
|
}
|
||||||
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
name: '',
|
name: '',
|
||||||
@ -46,22 +55,49 @@ export default {
|
|||||||
.icon-body {
|
.icon-body {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
|
.icon-search {
|
||||||
|
position: relative;
|
||||||
|
margin-bottom: 5px;
|
||||||
|
}
|
||||||
.icon-list {
|
.icon-list {
|
||||||
height: 200px;
|
height: 200px;
|
||||||
overflow-y: scroll;
|
overflow: auto;
|
||||||
div {
|
.list-container {
|
||||||
height: 30px;
|
display: flex;
|
||||||
line-height: 30px;
|
flex-wrap: wrap;
|
||||||
margin-bottom: -5px;
|
.icon-item-wrapper {
|
||||||
cursor: pointer;
|
width: calc(100% / 3);
|
||||||
width: 33%;
|
height: 25px;
|
||||||
float: left;
|
line-height: 25px;
|
||||||
}
|
cursor: pointer;
|
||||||
span {
|
display: flex;
|
||||||
display: inline-block;
|
.icon-item {
|
||||||
vertical-align: -0.15em;
|
display: flex;
|
||||||
fill: currentColor;
|
max-width: 100%;
|
||||||
overflow: hidden;
|
height: 100%;
|
||||||
|
padding: 0 5px;
|
||||||
|
&:hover {
|
||||||
|
background: #ececec;
|
||||||
|
border-radius: 5px;
|
||||||
|
}
|
||||||
|
.icon {
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
span {
|
||||||
|
display: inline-block;
|
||||||
|
vertical-align: -0.15em;
|
||||||
|
fill: currentColor;
|
||||||
|
padding-left: 2px;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.icon-item.active {
|
||||||
|
background: #ececec;
|
||||||
|
border-radius: 5px;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -127,7 +127,13 @@ export default {
|
|||||||
window.open(key, "_blank");
|
window.open(key, "_blank");
|
||||||
} else if (!route || !route.children) {
|
} else if (!route || !route.children) {
|
||||||
// 没有子路由路径内部打开
|
// 没有子路由路径内部打开
|
||||||
this.$router.push({ path: key });
|
const routeMenu = this.childrenMenus.find(item => item.path === key);
|
||||||
|
if (routeMenu && routeMenu.query) {
|
||||||
|
let query = JSON.parse(routeMenu.query);
|
||||||
|
this.$router.push({ path: key, query: query });
|
||||||
|
} else {
|
||||||
|
this.$router.push({ path: key });
|
||||||
|
}
|
||||||
this.$store.dispatch('app/toggleSideBarHide', true);
|
this.$store.dispatch('app/toggleSideBarHide', true);
|
||||||
} else {
|
} else {
|
||||||
// 显示左侧联动菜单
|
// 显示左侧联动菜单
|
||||||
|
@ -55,7 +55,21 @@ export default {
|
|||||||
// fix css style bug in open el-dialog
|
// fix css style bug in open el-dialog
|
||||||
.el-popup-parent--hidden {
|
.el-popup-parent--hidden {
|
||||||
.fixed-header {
|
.fixed-header {
|
||||||
padding-right: 17px;
|
padding-right: 6px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
::-webkit-scrollbar {
|
||||||
|
width: 6px;
|
||||||
|
height: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
::-webkit-scrollbar-track {
|
||||||
|
background-color: #f1f1f1;
|
||||||
|
}
|
||||||
|
|
||||||
|
::-webkit-scrollbar-thumb {
|
||||||
|
background-color: #c0c0c0;
|
||||||
|
border-radius: 3px;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
@ -87,7 +87,7 @@ export default {
|
|||||||
bottom: 0px;
|
bottom: 0px;
|
||||||
}
|
}
|
||||||
.el-scrollbar__wrap {
|
.el-scrollbar__wrap {
|
||||||
height: 49px;
|
height: 39px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -182,7 +182,7 @@ export default {
|
|||||||
})
|
})
|
||||||
},
|
},
|
||||||
closeOthersTags() {
|
closeOthersTags() {
|
||||||
this.$router.push(this.selectedTag).catch(()=>{});
|
this.$router.push(this.selectedTag.fullPath).catch(()=>{});
|
||||||
this.$tab.closeOtherPage(this.selectedTag).then(() => {
|
this.$tab.closeOtherPage(this.selectedTag).then(() => {
|
||||||
this.moveToCurrentTag()
|
this.moveToCurrentTag()
|
||||||
})
|
})
|
||||||
|
@ -1,15 +1,15 @@
|
|||||||
<template>
|
<template>
|
||||||
<div :class="classObj" class="app-wrapper" :style="{'--current-color': theme}">
|
<div :class="classObj" class="app-wrapper" :style="{'--current-color': theme}">
|
||||||
<div v-if="device==='mobile'&&sidebar.opened" class="drawer-bg" @click="handleClickOutside"/>
|
<div v-if="device==='mobile'&&sidebar.opened" class="drawer-bg" @click="handleClickOutside"/>
|
||||||
<sidebar v-if="!sidebar.hide" class="sidebar-container" />
|
<sidebar v-if="!sidebar.hide" class="sidebar-container"/>
|
||||||
<div :class="{hasTagsView:needTagsView,sidebarHide:sidebar.hide}" class="main-container">
|
<div :class="{hasTagsView:needTagsView,sidebarHide:sidebar.hide}" class="main-container">
|
||||||
<div :class="{'fixed-header':fixedHeader}">
|
<div :class="{'fixed-header':fixedHeader}">
|
||||||
<navbar />
|
<navbar/>
|
||||||
<tags-view v-if="needTagsView" />
|
<tags-view v-if="needTagsView"/>
|
||||||
</div>
|
</div>
|
||||||
<app-main />
|
<app-main/>
|
||||||
<right-panel>
|
<right-panel>
|
||||||
<settings />
|
<settings/>
|
||||||
</right-panel>
|
</right-panel>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -32,8 +32,12 @@ export default {
|
|||||||
// 关闭指定tab页签
|
// 关闭指定tab页签
|
||||||
closePage(obj) {
|
closePage(obj) {
|
||||||
if (obj === undefined) {
|
if (obj === undefined) {
|
||||||
return store.dispatch('tagsView/delView', router.currentRoute).then(({ lastPath }) => {
|
return store.dispatch('tagsView/delView', router.currentRoute).then(({ visitedViews }) => {
|
||||||
return router.push(lastPath || '/');
|
const latestView = visitedViews.slice(-1)[0]
|
||||||
|
if (latestView) {
|
||||||
|
return router.push(latestView.fullPath)
|
||||||
|
}
|
||||||
|
return router.push('/');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return store.dispatch('tagsView/delView', obj);
|
return store.dispatch('tagsView/delView', obj);
|
||||||
|
@ -166,9 +166,15 @@ export const dynamicRoutes = [
|
|||||||
|
|
||||||
// 防止连续点击多次路由报错
|
// 防止连续点击多次路由报错
|
||||||
let routerPush = Router.prototype.push;
|
let routerPush = Router.prototype.push;
|
||||||
|
let routerReplace = Router.prototype.replace;
|
||||||
|
// push
|
||||||
Router.prototype.push = function push(location) {
|
Router.prototype.push = function push(location) {
|
||||||
return routerPush.call(this, location).catch(err => err)
|
return routerPush.call(this, location).catch(err => err)
|
||||||
}
|
}
|
||||||
|
// replace
|
||||||
|
Router.prototype.replace = function push(location) {
|
||||||
|
return routerReplace.call(this, location).catch(err => err)
|
||||||
|
}
|
||||||
|
|
||||||
export default new Router({
|
export default new Router({
|
||||||
base: process.env.VUE_APP_CONTEXT_PATH,
|
base: process.env.VUE_APP_CONTEXT_PATH,
|
||||||
|
@ -114,7 +114,7 @@ export default {
|
|||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
// 版本号
|
// 版本号
|
||||||
version: "4.6.0",
|
version: "4.7.0",
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
2
ruoyi-ui/src/views/monitor/cache/list.vue
vendored
2
ruoyi-ui/src/views/monitor/cache/list.vue
vendored
@ -187,7 +187,7 @@ export default {
|
|||||||
/** 清理指定名称缓存 */
|
/** 清理指定名称缓存 */
|
||||||
handleClearCacheName(row) {
|
handleClearCacheName(row) {
|
||||||
clearCacheName(row.cacheName).then(response => {
|
clearCacheName(row.cacheName).then(response => {
|
||||||
this.$modal.msgSuccess("清理缓存名称[" + this.nowCacheName + "]成功");
|
this.$modal.msgSuccess("清理缓存名称[" + row.cacheName + "]成功");
|
||||||
this.getCacheKeys();
|
this.getCacheKeys();
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
@ -134,14 +134,13 @@
|
|||||||
trigger="click"
|
trigger="click"
|
||||||
@show="$refs['iconSelect'].reset()"
|
@show="$refs['iconSelect'].reset()"
|
||||||
>
|
>
|
||||||
<IconSelect ref="iconSelect" @selected="selected" />
|
<IconSelect ref="iconSelect" @selected="selected" :active-icon="form.icon" />
|
||||||
<el-input slot="reference" v-model="form.icon" placeholder="点击选择图标" readonly>
|
<el-input slot="reference" v-model="form.icon" placeholder="点击选择图标" readonly>
|
||||||
<svg-icon
|
<svg-icon
|
||||||
v-if="form.icon"
|
v-if="form.icon"
|
||||||
slot="prefix"
|
slot="prefix"
|
||||||
:icon-class="form.icon"
|
:icon-class="form.icon"
|
||||||
class="el-input__icon"
|
style="width: 25px;"
|
||||||
style="height: 32px;width: 16px;"
|
|
||||||
/>
|
/>
|
||||||
<i v-else slot="prefix" class="el-icon-search el-input__icon" />
|
<i v-else slot="prefix" class="el-icon-search el-input__icon" />
|
||||||
</el-input>
|
</el-input>
|
||||||
|
@ -406,9 +406,7 @@ export default {
|
|||||||
}).then(() => {
|
}).then(() => {
|
||||||
this.getList()
|
this.getList()
|
||||||
this.$modal.msgSuccess(text + "成功");
|
this.$modal.msgSuccess(text + "成功");
|
||||||
}).catch(() => {
|
}).catch(() => {})
|
||||||
this.previewListResource = previewListResource !== true;
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -371,19 +371,6 @@ export default {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang='scss'>
|
<style lang='scss'>
|
||||||
body, html{
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
background: #fff;
|
|
||||||
-moz-osx-font-smoothing: grayscale;
|
|
||||||
-webkit-font-smoothing: antialiased;
|
|
||||||
text-rendering: optimizeLegibility;
|
|
||||||
font-family: -apple-system,BlinkMacSystemFont,Segoe UI,Helvetica,Arial,sans-serif,Apple Color Emoji,Segoe UI Emoji;
|
|
||||||
}
|
|
||||||
|
|
||||||
input, textarea{
|
|
||||||
font-family: -apple-system,BlinkMacSystemFont,Segoe UI,Helvetica,Arial,sans-serif,Apple Color Emoji,Segoe UI Emoji;
|
|
||||||
}
|
|
||||||
|
|
||||||
.editor-tabs{
|
.editor-tabs{
|
||||||
background: #121315;
|
background: #121315;
|
||||||
|
@ -67,7 +67,7 @@ services:
|
|||||||
network_mode: "host"
|
network_mode: "host"
|
||||||
|
|
||||||
minio:
|
minio:
|
||||||
image: minio/minio:RELEASE.2022-05-26T05-48-41Z
|
image: minio/minio:RELEASE.2023-03-24T21-41-23Z
|
||||||
container_name: minio
|
container_name: minio
|
||||||
ports:
|
ports:
|
||||||
# api 端口
|
# api 端口
|
||||||
@ -100,7 +100,7 @@ services:
|
|||||||
network_mode: "host"
|
network_mode: "host"
|
||||||
|
|
||||||
ruoyi-server1:
|
ruoyi-server1:
|
||||||
image: ruoyi/ruoyi-server:4.6.0
|
image: ruoyi/ruoyi-server:4.7.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.6.0"
|
image: "ruoyi/ruoyi-server:4.7.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.6.0
|
image: ruoyi/ruoyi-monitor-admin:4.7.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.6.0
|
image: ruoyi/ruoyi-xxl-job-admin:4.7.0
|
||||||
container_name: ruoyi-xxl-job-admin
|
container_name: ruoyi-xxl-job-admin
|
||||||
environment:
|
environment:
|
||||||
# 时区上海
|
# 时区上海
|
||||||
|
@ -531,9 +531,9 @@ create table sys_oper_log (
|
|||||||
);
|
);
|
||||||
|
|
||||||
alter table sys_oper_log add constraint pk_sys_oper_log primary key (oper_id);
|
alter table sys_oper_log add constraint pk_sys_oper_log primary key (oper_id);
|
||||||
create unique index idx_sys_oper_log_bt on sys_oper_log (business_type);
|
create index idx_sys_oper_log_bt on sys_oper_log (business_type);
|
||||||
create unique index idx_sys_oper_log_s on sys_oper_log (status);
|
create index idx_sys_oper_log_s on sys_oper_log (status);
|
||||||
create unique index idx_sys_oper_log_ot on sys_oper_log (oper_time);
|
create index idx_sys_oper_log_ot on sys_oper_log (oper_time);
|
||||||
|
|
||||||
comment on table sys_oper_log is '操作日志记录';
|
comment on table sys_oper_log is '操作日志记录';
|
||||||
comment on column sys_oper_log.oper_id is '日志主键';
|
comment on column sys_oper_log.oper_id is '日志主键';
|
||||||
@ -711,8 +711,8 @@ create table sys_logininfor (
|
|||||||
);
|
);
|
||||||
|
|
||||||
alter table sys_logininfor add constraint pk_sys_logininfor primary key (info_id);
|
alter table sys_logininfor add constraint pk_sys_logininfor primary key (info_id);
|
||||||
create unique index idx_sys_logininfor_s on sys_logininfor (status);
|
create index idx_sys_logininfor_s on sys_logininfor (status);
|
||||||
create unique index idx_sys_logininfor_lt on sys_logininfor (login_time);
|
create index idx_sys_logininfor_lt on sys_logininfor (login_time);
|
||||||
|
|
||||||
comment on table sys_logininfor is '系统访问记录';
|
comment on table sys_logininfor is '系统访问记录';
|
||||||
comment on column sys_logininfor.info_id is '访问ID';
|
comment on column sys_logininfor.info_id is '访问ID';
|
||||||
@ -938,7 +938,7 @@ comment on column sys_oss_config.domain is '自定义域名';
|
|||||||
comment on column sys_oss_config.is_https is '是否https(Y=是,N=否)';
|
comment on column sys_oss_config.is_https is '是否https(Y=是,N=否)';
|
||||||
comment on column sys_oss_config.region is '域';
|
comment on column sys_oss_config.region is '域';
|
||||||
comment on column sys_oss_config.access_policy is '桶权限类型(0=private 1=public 2=custom)';
|
comment on column sys_oss_config.access_policy is '桶权限类型(0=private 1=public 2=custom)';
|
||||||
comment on column sys_oss_config.status is '状态(0=正常,1=停用)';
|
comment on column sys_oss_config.status is '是否默认(0=是,1=否)';
|
||||||
comment on column sys_oss_config.ext1 is '扩展字段';
|
comment on column sys_oss_config.ext1 is '扩展字段';
|
||||||
comment on column sys_oss_config.remark is '备注';
|
comment on column sys_oss_config.remark is '备注';
|
||||||
comment on column sys_oss_config.create_by is '创建者';
|
comment on column sys_oss_config.create_by is '创建者';
|
||||||
|
@ -540,9 +540,9 @@ create table if not exists sys_oper_log
|
|||||||
constraint sys_oper_log_pk primary key (oper_id)
|
constraint sys_oper_log_pk primary key (oper_id)
|
||||||
);
|
);
|
||||||
|
|
||||||
create unique index idx_sys_oper_log_bt ON sys_oper_log (business_type);
|
create index idx_sys_oper_log_bt ON sys_oper_log (business_type);
|
||||||
create unique index idx_sys_oper_log_s ON sys_oper_log (status);
|
create index idx_sys_oper_log_s ON sys_oper_log (status);
|
||||||
create unique index idx_sys_oper_log_ot ON sys_oper_log (oper_time);
|
create index idx_sys_oper_log_ot ON sys_oper_log (oper_time);
|
||||||
|
|
||||||
comment on table sys_oper_log is '操作日志记录';
|
comment on table sys_oper_log is '操作日志记录';
|
||||||
comment on column sys_oper_log.oper_id is '日志主键';
|
comment on column sys_oper_log.oper_id is '日志主键';
|
||||||
@ -726,8 +726,8 @@ create table if not exists sys_logininfor
|
|||||||
constraint sys_logininfor_pk primary key (info_id)
|
constraint sys_logininfor_pk primary key (info_id)
|
||||||
);
|
);
|
||||||
|
|
||||||
create unique index idx_sys_logininfor_s ON sys_logininfor (status);
|
create index idx_sys_logininfor_s ON sys_logininfor (status);
|
||||||
create unique index idx_sys_logininfor_lt ON sys_logininfor (login_time);
|
create index idx_sys_logininfor_lt ON sys_logininfor (login_time);
|
||||||
|
|
||||||
comment on table sys_logininfor is '系统访问记录';
|
comment on table sys_logininfor is '系统访问记录';
|
||||||
comment on column sys_logininfor.info_id is '访问ID';
|
comment on column sys_logininfor.info_id is '访问ID';
|
||||||
@ -954,7 +954,7 @@ comment on column sys_oss_config.domain is '自定义域名';
|
|||||||
comment on column sys_oss_config.is_https is '是否https(Y=是,N=否)';
|
comment on column sys_oss_config.is_https is '是否https(Y=是,N=否)';
|
||||||
comment on column sys_oss_config.region is '域';
|
comment on column sys_oss_config.region is '域';
|
||||||
comment on column sys_oss_config.access_policy is '桶权限类型(0=private 1=public 2=custom)';
|
comment on column sys_oss_config.access_policy is '桶权限类型(0=private 1=public 2=custom)';
|
||||||
comment on column sys_oss_config.status is '状态(0=正常,1=停用)';
|
comment on column sys_oss_config.status is '是否默认(0=是,1=否)';
|
||||||
comment on column sys_oss_config.ext1 is '扩展字段';
|
comment on column sys_oss_config.ext1 is '扩展字段';
|
||||||
comment on column sys_oss_config.create_by is '创建者';
|
comment on column sys_oss_config.create_by is '创建者';
|
||||||
comment on column sys_oss_config.create_time is '创建时间';
|
comment on column sys_oss_config.create_time is '创建时间';
|
||||||
|
@ -679,7 +679,7 @@ create table sys_oss_config (
|
|||||||
is_https char(1) default 'N' comment '是否https(Y=是,N=否)',
|
is_https char(1) default 'N' comment '是否https(Y=是,N=否)',
|
||||||
region varchar(255) default '' comment '域',
|
region varchar(255) default '' comment '域',
|
||||||
access_policy char(1) not null default '1' comment '桶权限类型(0=private 1=public 2=custom)',
|
access_policy char(1) not null default '1' comment '桶权限类型(0=private 1=public 2=custom)',
|
||||||
status char(1) default '1' comment '状态(0=正常,1=停用)',
|
status char(1) default '1' comment '是否默认(0=是,1=否)',
|
||||||
ext1 varchar(255) default '' comment '扩展字段',
|
ext1 varchar(255) default '' comment '扩展字段',
|
||||||
create_by varchar(64) default '' comment '创建者',
|
create_by varchar(64) default '' comment '创建者',
|
||||||
create_time datetime default null comment '创建时间',
|
create_time datetime default null comment '创建时间',
|
||||||
|
@ -2285,7 +2285,7 @@ EXEC sp_addextendedproperty
|
|||||||
'COLUMN', N'access_policy'
|
'COLUMN', N'access_policy'
|
||||||
GO
|
GO
|
||||||
EXEC sp_addextendedproperty
|
EXEC sp_addextendedproperty
|
||||||
'MS_Description', N'状态(0=正常,1=停用)',
|
'MS_Description', N'是否默认(0=是,1=否)',
|
||||||
'SCHEMA', N'dbo',
|
'SCHEMA', N'dbo',
|
||||||
'TABLE', N'sys_oss_config',
|
'TABLE', N'sys_oss_config',
|
||||||
'COLUMN', N'status'
|
'COLUMN', N'status'
|
||||||
|
Loading…
Reference in New Issue
Block a user