diff --git a/.run/ruoyi-monitor-admin.run.xml b/.run/ruoyi-monitor-admin.run.xml index 99818dcca..57a0e1709 100644 --- a/.run/ruoyi-monitor-admin.run.xml +++ b/.run/ruoyi-monitor-admin.run.xml @@ -2,7 +2,7 @@ - diff --git a/.run/ruoyi-server.run.xml b/.run/ruoyi-server.run.xml index 6c3cd5e26..c51b9d5fd 100644 --- a/.run/ruoyi-server.run.xml +++ b/.run/ruoyi-server.run.xml @@ -2,7 +2,7 @@ - diff --git a/.run/ruoyi-xxl-job-admin.run.xml b/.run/ruoyi-xxl-job-admin.run.xml index 98939edbd..a9657b03d 100644 --- a/.run/ruoyi-xxl-job-admin.run.xml +++ b/.run/ruoyi-xxl-job-admin.run.xml @@ -2,7 +2,7 @@ - diff --git a/README.md b/README.md index ebcdd4470..a87d4037d 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,7 @@
- - - + ## 平台简介 [![码云Gitee](https://gitee.com/dromara/RuoYi-Vue-Plus/badge/star.svg?theme=blue)](https://gitee.com/dromara/RuoYi-Vue-Plus) @@ -9,7 +10,7 @@ [![License](https://img.shields.io/badge/License-MIT-blue.svg)](https://gitee.com/dromara/RuoYi-Vue-Plus/blob/master/LICENSE) [![使用IntelliJ IDEA开发维护](https://img.shields.io/badge/IntelliJ%20IDEA-提供支持-blue.svg)](https://www.jetbrains.com/?from=RuoYi-Vue-Plus)
-[![RuoYi-Vue-Plus](https://img.shields.io/badge/RuoYi_Vue_Plus-4.6.0-success.svg)](https://gitee.com/dromara/RuoYi-Vue-Plus) +[![RuoYi-Vue-Plus](https://img.shields.io/badge/RuoYi_Vue_Plus-4.7.0-success.svg)](https://gitee.com/dromara/RuoYi-Vue-Plus) [![Spring Boot](https://img.shields.io/badge/Spring%20Boot-2.7-blue.svg)]() [![JDK-8+](https://img.shields.io/badge/JDK-8-green.svg)]() [![JDK-11](https://img.shields.io/badge/JDK-11-green.svg)]() @@ -161,4 +162,3 @@ | ![输入图片说明](https://foruda.gitee.com/images/1680079274333484664/4dfdc7c0_1766278.png "屏幕截图") | ![输入图片说明](https://foruda.gitee.com/images/1680079290467458224/d6715fcf_1766278.png "屏幕截图") | - diff --git a/pom.xml b/pom.xml index 6a62ac241..16f49a77f 100644 --- a/pom.xml +++ b/pom.xml @@ -6,15 +6,15 @@ com.ruoyi ruoyi-vue-plus - 4.6.0 + 4.7.0 RuoYi-Vue-Plus https://gitee.com/dromara/RuoYi-Vue-Plus RuoYi-Vue-Plus后台管理系统 - 4.6.0 - 2.7.9 + 4.7.0 + 2.7.11 UTF-8 UTF-8 1.8 @@ -27,16 +27,18 @@ 1.34.0 3.5.3.1 3.9.1 - 5.8.15 + 5.8.18 4.10.0 2.7.10 - 3.20.0 + 3.20.1 2.2.3 3.5.2 2.14.2 - 2.3.1 + 2.4.0 1.18.26 1.72 + + 2.7.0 1.33 @@ -258,6 +260,13 @@ ${alibaba-ttl.version} + + + org.lionsoul + ip2region + ${ip2region.version} + + org.yaml diff --git a/ruoyi-admin/pom.xml b/ruoyi-admin/pom.xml index 968969d2a..da22b1419 100644 --- a/ruoyi-admin/pom.xml +++ b/ruoyi-admin/pom.xml @@ -5,7 +5,7 @@ ruoyi-vue-plus com.ruoyi - 4.6.0 + 4.7.0 4.0.0 jar diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/common/CaptchaController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/common/CaptchaController.java index 795359c5b..f5a603a43 100644 --- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/common/CaptchaController.java +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/common/CaptchaController.java @@ -10,10 +10,12 @@ import com.ruoyi.common.constant.Constants; import com.ruoyi.common.core.domain.R; import com.ruoyi.common.enums.CaptchaType; 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.reflect.ReflectUtils; import com.ruoyi.common.utils.spring.SpringUtils; 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.core.SmsTemplate; import com.ruoyi.sms.entity.SmsResult; @@ -47,6 +49,7 @@ public class CaptchaController { private final CaptchaProperties captchaProperties; private final SmsProperties smsProperties; private final ISysConfigService configService; + private final MailProperties mailProperties; /** * 短信验证码 @@ -54,8 +57,7 @@ public class CaptchaController { * @param phonenumber 用户手机号 */ @GetMapping("/captchaSms") - public R smsCaptcha(@NotBlank(message = "{user.phonenumber.not.blank}") - String phonenumber) { + public R smsCaptcha(@NotBlank(message = "{user.phonenumber.not.blank}") String phonenumber) { if (!smsProperties.getEnabled()) { return R.fail("当前系统没有开启短信功能!"); } @@ -75,6 +77,28 @@ public class CaptchaController { return R.ok(); } + /** + * 邮箱验证码 + * + * @param email 邮箱 + */ + @GetMapping("/captchaEmail") + public R 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(); + } + /** * 生成验证码 */ diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/CacheController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/CacheController.java index 74585fc48..f0ecce1ba 100644 --- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/CacheController.java +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/CacheController.java @@ -33,7 +33,6 @@ public class CacheController { private final static List CACHES = new ArrayList<>(); static { - CACHES.add(new SysCache(CacheConstants.LOGIN_TOKEN_KEY, "用户信息")); CACHES.add(new SysCache(CacheConstants.ONLINE_TOKEN_KEY, "在线用户")); CACHES.add(new SysCache(CacheNames.SYS_CONFIG, "配置信息")); CACHES.add(new SysCache(CacheNames.SYS_DICT, "数据字典")); diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/SysUserOnlineController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/SysUserOnlineController.java index b0b574c37..33d425a6a 100644 --- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/SysUserOnlineController.java +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/SysUserOnlineController.java @@ -45,7 +45,7 @@ public class SysUserOnlineController extends BaseController { List keys = StpUtil.searchTokenValue("", 0, -1, false); List userOnlineDTOList = new ArrayList<>(); for (String key : keys) { - String token = key.replace(CacheConstants.LOGIN_TOKEN_KEY, ""); + String token = StringUtils.substringAfterLast(key, ":"); // 如果已经过期则跳过 if (StpUtil.stpLogic.getTokenActivityTimeoutByToken(token) < -1) { continue; diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysLoginController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysLoginController.java index 78c44e305..f982a5fac 100644 --- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysLoginController.java +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysLoginController.java @@ -5,6 +5,7 @@ import com.ruoyi.common.constant.Constants; import com.ruoyi.common.core.domain.R; import com.ruoyi.common.core.domain.entity.SysMenu; 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.LoginUser; import com.ruoyi.common.core.domain.model.SmsLoginBody; @@ -57,7 +58,7 @@ public class SysLoginController { } /** - * 短信登录(示例) + * 短信登录 * * @param smsLoginBody 登录信息 * @return 结果 @@ -72,6 +73,21 @@ public class SysLoginController { return R.ok(ajax); } + /** + * 邮件登录 + * + * @param body 登录信息 + * @return 结果 + */ + @PostMapping("/emailLogin") + public R> emailLogin(@Validated @RequestBody EmailLoginBody body) { + Map ajax = new HashMap<>(); + // 生成令牌 + String token = loginService.emailLogin(body.getEmail(), body.getEmailCode()); + ajax.put(Constants.TOKEN, token); + return R.ok(ajax); + } + /** * 小程序登录(示例) * diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysOssController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysOssController.java index 606895a74..2a7cc11cb 100644 --- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysOssController.java +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysOssController.java @@ -2,10 +2,7 @@ package com.ruoyi.web.controller.system; import cn.dev33.satoken.annotation.SaCheckPermission; -import cn.hutool.core.convert.Convert; 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.core.controller.BaseController; 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.validate.QueryGroup; 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.vo.SysOssVo; import com.ruoyi.system.service.ISysOssService; @@ -80,7 +72,7 @@ public class SysOssController extends BaseController { @PostMapping(value = "/upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) public R> upload(@RequestPart("file") MultipartFile file) { if (ObjectUtil.isNull(file)) { - throw new ServiceException("上传文件不能为空"); + return R.fail("上传文件不能为空"); } SysOssVo oss = iSysOssService.upload(file); Map map = new HashMap<>(2); diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysRoleController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysRoleController.java index 47e157512..f8edb5d81 100644 --- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysRoleController.java +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysRoleController.java @@ -1,22 +1,15 @@ package com.ruoyi.web.controller.system; 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.constant.CacheConstants; -import com.ruoyi.common.constant.UserConstants; import com.ruoyi.common.core.controller.BaseController; import com.ruoyi.common.core.domain.PageQuery; import com.ruoyi.common.core.domain.R; import com.ruoyi.common.core.domain.entity.SysDept; import com.ruoyi.common.core.domain.entity.SysRole; 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.enums.BusinessType; -import com.ruoyi.common.helper.LoginHelper; import com.ruoyi.common.utils.poi.ExcelUtil; import com.ruoyi.system.domain.SysUserRole; import com.ruoyi.system.service.ISysDeptService; @@ -112,25 +105,7 @@ public class SysRoleController extends BaseController { } if (roleService.updateRole(role) > 0) { - List keys = StpUtil.searchTokenValue("", 0, -1, false); - 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) { - } - } - }); + roleService.cleanOnlineUserByRole(role.getRoleId()); return R.ok(); } return R.fail("修改角色'" + role.getRoleName() + "'失败,请联系管理员"); diff --git a/ruoyi-admin/src/main/resources/application.yml b/ruoyi-admin/src/main/resources/application.yml index 6f229d87f..a6617bc6f 100644 --- a/ruoyi-admin/src/main/resources/application.yml +++ b/ruoyi-admin/src/main/resources/application.yml @@ -51,7 +51,7 @@ logging: level: com.ruoyi: @logging.level@ org.springframework: warn - config: classpath:logback.xml + config: classpath:logback-plus.xml # 用户配置 user: diff --git a/ruoyi-admin/src/main/resources/i18n/messages.properties b/ruoyi-admin/src/main/resources/i18n/messages.properties index 4ff55f41a..ffdd8f304 100644 --- a/ruoyi-admin/src/main/resources/i18n/messages.properties +++ b/ruoyi-admin/src/main/resources/i18n/messages.properties @@ -18,6 +18,7 @@ user.password.not.blank=用户密码不能为空 user.password.length.valid=用户密码长度必须在{min}到{max}个字符之间 user.password.not.valid=* 5-50个字符 user.email.not.valid=邮箱格式错误 +user.email.not.blank=邮箱不能为空 user.phonenumber.not.blank=用户手机号不能为空 user.mobile.phone.number.not.valid=手机号格式错误 user.login.success=登录成功 @@ -42,4 +43,7 @@ rate.limiter.message=访问过于频繁,请稍候再试 sms.code.not.blank=短信验证码不能为空 sms.code.retry.limit.count=短信验证码输入错误{0}次 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不能为空 diff --git a/ruoyi-admin/src/main/resources/i18n/messages_en_US.properties b/ruoyi-admin/src/main/resources/i18n/messages_en_US.properties index c0faca93a..c1ca43916 100644 --- a/ruoyi-admin/src/main/resources/i18n/messages_en_US.properties +++ b/ruoyi-admin/src/main/resources/i18n/messages_en_US.properties @@ -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.not.valid=* 5-50 characters 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.mobile.phone.number.not.valid=Phone number format error 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.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 +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 diff --git a/ruoyi-admin/src/main/resources/i18n/messages_zh_CN.properties b/ruoyi-admin/src/main/resources/i18n/messages_zh_CN.properties index 4ff55f41a..ffdd8f304 100644 --- a/ruoyi-admin/src/main/resources/i18n/messages_zh_CN.properties +++ b/ruoyi-admin/src/main/resources/i18n/messages_zh_CN.properties @@ -18,6 +18,7 @@ user.password.not.blank=用户密码不能为空 user.password.length.valid=用户密码长度必须在{min}到{max}个字符之间 user.password.not.valid=* 5-50个字符 user.email.not.valid=邮箱格式错误 +user.email.not.blank=邮箱不能为空 user.phonenumber.not.blank=用户手机号不能为空 user.mobile.phone.number.not.valid=手机号格式错误 user.login.success=登录成功 @@ -42,4 +43,7 @@ rate.limiter.message=访问过于频繁,请稍候再试 sms.code.not.blank=短信验证码不能为空 sms.code.retry.limit.count=短信验证码输入错误{0}次 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不能为空 diff --git a/ruoyi-admin/src/main/resources/ip2region.xdb b/ruoyi-admin/src/main/resources/ip2region.xdb new file mode 100644 index 000000000..31f96a1fb Binary files /dev/null and b/ruoyi-admin/src/main/resources/ip2region.xdb differ diff --git a/ruoyi-admin/src/main/resources/logback.xml b/ruoyi-admin/src/main/resources/logback-plus.xml similarity index 100% rename from ruoyi-admin/src/main/resources/logback.xml rename to ruoyi-admin/src/main/resources/logback-plus.xml diff --git a/ruoyi-common/pom.xml b/ruoyi-common/pom.xml index e6e888eb8..3ac834bf2 100644 --- a/ruoyi-common/pom.xml +++ b/ruoyi-common/pom.xml @@ -5,7 +5,7 @@ ruoyi-vue-plus com.ruoyi - 4.6.0 + 4.7.0 4.0.0 @@ -159,6 +159,12 @@ bcprov-jdk15to18 + + + org.lionsoul + ip2region + + diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/constant/CacheConstants.java b/ruoyi-common/src/main/java/com/ruoyi/common/constant/CacheConstants.java index 1cdf07eec..0fb2c3f97 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/constant/CacheConstants.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/constant/CacheConstants.java @@ -7,11 +7,6 @@ package com.ruoyi.common.constant; */ public interface CacheConstants { - /** - * 登录用户 redis key - */ - String LOGIN_TOKEN_KEY = "Authorization:login:token:"; - /** * 在线用户 redis key */ diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/model/EmailLoginBody.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/model/EmailLoginBody.java new file mode 100644 index 000000000..b7bf81bdb --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/model/EmailLoginBody.java @@ -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; + +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/model/SmsLoginBody.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/model/SmsLoginBody.java index ce774ac7e..b12e74c14 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/model/SmsLoginBody.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/model/SmsLoginBody.java @@ -14,13 +14,13 @@ import javax.validation.constraints.NotBlank; public class SmsLoginBody { /** - * 用户名 + * 手机号 */ @NotBlank(message = "{user.phonenumber.not.blank}") private String phonenumber; /** - * 用户密码 + * 短信code */ @NotBlank(message = "{sms.code.not.blank}") private String smsCode; diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/enums/LoginType.java b/ruoyi-common/src/main/java/com/ruoyi/common/enums/LoginType.java index c91a4b94f..875e4762e 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/enums/LoginType.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/enums/LoginType.java @@ -22,6 +22,11 @@ public enum LoginType { */ SMS("sms.code.retry.limit.exceed", "sms.code.retry.limit.count"), + /** + * 邮箱登录 + */ + EMAIL("email.code.retry.limit.exceed", "email.code.retry.limit.count"), + /** * 小程序登录 */ diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/excel/DefaultExcelListener.java b/ruoyi-common/src/main/java/com/ruoyi/common/excel/DefaultExcelListener.java index 6cd6d2816..2a3fc3b1e 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/excel/DefaultExcelListener.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/excel/DefaultExcelListener.java @@ -42,7 +42,7 @@ public class DefaultExcelListener extends AnalysisEventListener implements private ExcelResult excelResult; public DefaultExcelListener(boolean isValidate) { - this.excelResult = new DefautExcelResult<>(); + this.excelResult = new DefaultExcelResult<>(); this.isValidate = isValidate; } diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/excel/DefautExcelResult.java b/ruoyi-common/src/main/java/com/ruoyi/common/excel/DefaultExcelResult.java similarity index 85% rename from ruoyi-common/src/main/java/com/ruoyi/common/excel/DefautExcelResult.java rename to ruoyi-common/src/main/java/com/ruoyi/common/excel/DefaultExcelResult.java index c852ce65f..5ef65bf84 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/excel/DefautExcelResult.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/excel/DefaultExcelResult.java @@ -12,7 +12,7 @@ import java.util.List; * @author Yjoioooo * @author Lion Li */ -public class DefautExcelResult implements ExcelResult { +public class DefaultExcelResult implements ExcelResult { /** * 数据对象list @@ -26,17 +26,17 @@ public class DefautExcelResult implements ExcelResult { @Setter private List errorList; - public DefautExcelResult() { + public DefaultExcelResult() { this.list = new ArrayList<>(); this.errorList = new ArrayList<>(); } - public DefautExcelResult(List list, List errorList) { + public DefaultExcelResult(List list, List errorList) { this.list = list; this.errorList = errorList; } - public DefautExcelResult(ExcelResult excelResult) { + public DefaultExcelResult(ExcelResult excelResult) { this.list = excelResult.getList(); this.errorList = excelResult.getErrorList(); } diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/helper/DataBaseHelper.java b/ruoyi-common/src/main/java/com/ruoyi/common/helper/DataBaseHelper.java index 196035629..33e0a78fe 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/helper/DataBaseHelper.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/helper/DataBaseHelper.java @@ -66,7 +66,7 @@ public class DataBaseHelper { // instr(',0,100,101,' , ',100,') <> 0 return "instr(','||" + var2 + "||',' , '," + var + ",') <> 0"; } - // find_in_set(100 , '0,100,101') - return "find_in_set(" + var + " , " + var2 + ") <> 0"; + // find_in_set('100' , '0,100,101') + return "find_in_set('" + var + "' , " + var2 + ") <> 0"; } } diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/helper/DataPermissionHelper.java b/ruoyi-common/src/main/java/com/ruoyi/common/helper/DataPermissionHelper.java index e2b1129f5..c3b8b47cd 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/helper/DataPermissionHelper.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/helper/DataPermissionHelper.java @@ -10,6 +10,7 @@ import lombok.NoArgsConstructor; import java.util.HashMap; import java.util.Map; +import java.util.function.Supplier; /** * 数据权限助手 @@ -61,4 +62,32 @@ public class DataPermissionHelper { InterceptorIgnoreHelper.clearIgnoreStrategy(); } + /** + * 在忽略数据权限中执行 + * + * @param handle 处理执行方法 + */ + public static void ignore(Runnable handle) { + enableIgnore(); + try { + handle.run(); + } finally { + disableIgnore(); + } + } + + /** + * 在忽略数据权限中执行 + * + * @param handle 处理执行方法 + */ + public static T ignore(Supplier handle) { + enableIgnore(); + try { + return handle.get(); + } finally { + disableIgnore(); + } + } + } diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/translation/impl/DeptNameTranslationImpl.java b/ruoyi-common/src/main/java/com/ruoyi/common/translation/impl/DeptNameTranslationImpl.java index 69196171b..ada3c253a 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/translation/impl/DeptNameTranslationImpl.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/translation/impl/DeptNameTranslationImpl.java @@ -18,7 +18,8 @@ import org.springframework.stereotype.Component; public class DeptNameTranslationImpl implements TranslationInterface { private final DeptService deptService; - + + @Override public String translation(Object key, String other) { return deptService.selectDeptNameByIds(key.toString()); } diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/translation/impl/DictTypeTranslationImpl.java b/ruoyi-common/src/main/java/com/ruoyi/common/translation/impl/DictTypeTranslationImpl.java index 210312c82..1e97b66ce 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/translation/impl/DictTypeTranslationImpl.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/translation/impl/DictTypeTranslationImpl.java @@ -20,6 +20,7 @@ public class DictTypeTranslationImpl implements TranslationInterface { private final DictService dictService; + @Override public String translation(Object key, String other) { if (key instanceof String && StringUtils.isNotBlank(other)) { return dictService.getDictLabel(other, key.toString()); diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/translation/impl/OssUrlTranslationImpl.java b/ruoyi-common/src/main/java/com/ruoyi/common/translation/impl/OssUrlTranslationImpl.java index 69ebd9afe..863e4d686 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/translation/impl/OssUrlTranslationImpl.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/translation/impl/OssUrlTranslationImpl.java @@ -19,6 +19,7 @@ public class OssUrlTranslationImpl implements TranslationInterface { private final OssService ossService; + @Override public String translation(Object key, String other) { return ossService.selectUrlByIds(key.toString()); } diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/translation/impl/UserNameTranslationImpl.java b/ruoyi-common/src/main/java/com/ruoyi/common/translation/impl/UserNameTranslationImpl.java index 36fd6c399..eccf1c8b5 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/translation/impl/UserNameTranslationImpl.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/translation/impl/UserNameTranslationImpl.java @@ -19,6 +19,7 @@ public class UserNameTranslationImpl implements TranslationInterface { private final UserService userService; + @Override public String translation(Object key, String other) { if (key instanceof Long) { return userService.selectUserNameById((Long) key); diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/EncryptUtils.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/EncryptUtils.java new file mode 100644 index 000000000..691b6c0f4 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/EncryptUtils.java @@ -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 generateSm2Key() { + Map 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 generateRsaKey() { + Map 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); + } + +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/ip/AddressUtils.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/ip/AddressUtils.java index 8d3515e7f..9bf203bf9 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/utils/ip/AddressUtils.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/ip/AddressUtils.java @@ -1,12 +1,7 @@ package com.ruoyi.common.utils.ip; -import cn.hutool.core.lang.Dict; import cn.hutool.core.net.NetUtil; 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 lombok.AccessLevel; import lombok.NoArgsConstructor; @@ -21,40 +16,18 @@ import lombok.extern.slf4j.Slf4j; @NoArgsConstructor(access = AccessLevel.PRIVATE) 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 String getRealAddressByIP(String ip) { - String address = UNKNOWN; 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); if (NetUtil.isInnerIP(ip)) { return "内网IP"; } - if (RuoYiConfig.isAddressEnabled()) { - 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; + return RegionUtils.getCityInfo(ip); } } diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/ip/RegionUtils.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/ip/RegionUtils.java new file mode 100644 index 000000000..581061a51 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/ip/RegionUtils.java @@ -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地址定位工具类,离线方式 + * 参考地址:集成 ip2region 实现离线IP地址定位库 + * + * @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 "未知"; + } + } +} diff --git a/ruoyi-demo/pom.xml b/ruoyi-demo/pom.xml index b64625d34..61425b67b 100644 --- a/ruoyi-demo/pom.xml +++ b/ruoyi-demo/pom.xml @@ -5,7 +5,7 @@ ruoyi-vue-plus com.ruoyi - 4.6.0 + 4.7.0 4.0.0 diff --git a/ruoyi-demo/src/main/java/com/ruoyi/demo/controller/RedisRateLimiterController.java b/ruoyi-demo/src/main/java/com/ruoyi/demo/controller/RedisRateLimiterController.java index ee2e530ba..d0401956e 100644 --- a/ruoyi-demo/src/main/java/com/ruoyi/demo/controller/RedisRateLimiterController.java +++ b/ruoyi-demo/src/main/java/com/ruoyi/demo/controller/RedisRateLimiterController.java @@ -49,4 +49,16 @@ public class RedisRateLimiterController { return R.ok("操作成功", value); } + /** + * 测试请求IP限流(key基于参数获取) + * 同一IP请求受影响 + * + * 简单变量获取 #变量 复杂表达式 #{#变量 != 1 ? 1 : 0} + */ + @RateLimiter(count = 2, time = 10, limitType = LimitType.IP, key = "#value") + @GetMapping("/testObj") + public R testObj(String value) { + return R.ok("操作成功", value); + } + } diff --git a/ruoyi-extend/pom.xml b/ruoyi-extend/pom.xml index 512368cd9..212836acd 100644 --- a/ruoyi-extend/pom.xml +++ b/ruoyi-extend/pom.xml @@ -5,7 +5,7 @@ ruoyi-vue-plus com.ruoyi - 4.6.0 + 4.7.0 4.0.0 ruoyi-extend diff --git a/ruoyi-extend/ruoyi-monitor-admin/pom.xml b/ruoyi-extend/ruoyi-monitor-admin/pom.xml index 8cb17a887..89cf455f2 100644 --- a/ruoyi-extend/ruoyi-monitor-admin/pom.xml +++ b/ruoyi-extend/ruoyi-monitor-admin/pom.xml @@ -5,7 +5,7 @@ ruoyi-extend com.ruoyi - 4.6.0 + 4.7.0 4.0.0 jar diff --git a/ruoyi-extend/ruoyi-monitor-admin/src/main/resources/application.yml b/ruoyi-extend/ruoyi-monitor-admin/src/main/resources/application.yml index 22ef1abfc..1b729ef1b 100644 --- a/ruoyi-extend/ruoyi-monitor-admin/src/main/resources/application.yml +++ b/ruoyi-extend/ruoyi-monitor-admin/src/main/resources/application.yml @@ -6,6 +6,9 @@ spring: profiles: active: @profiles.active@ +logging: + config: classpath:logback-plus.xml + --- # 监控中心服务端配置 spring: security: diff --git a/ruoyi-extend/ruoyi-monitor-admin/src/main/resources/logback.xml b/ruoyi-extend/ruoyi-monitor-admin/src/main/resources/logback-plus.xml similarity index 100% rename from ruoyi-extend/ruoyi-monitor-admin/src/main/resources/logback.xml rename to ruoyi-extend/ruoyi-monitor-admin/src/main/resources/logback-plus.xml diff --git a/ruoyi-extend/ruoyi-xxl-job-admin/pom.xml b/ruoyi-extend/ruoyi-xxl-job-admin/pom.xml index 8e4f135db..56650a2fc 100644 --- a/ruoyi-extend/ruoyi-xxl-job-admin/pom.xml +++ b/ruoyi-extend/ruoyi-xxl-job-admin/pom.xml @@ -4,7 +4,7 @@ ruoyi-extend com.ruoyi - 4.6.0 + 4.7.0 ruoyi-xxl-job-admin jar diff --git a/ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/controller/JobGroupController.java b/ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/controller/JobGroupController.java index 82ec2dee4..5cbf87287 100644 --- a/ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/controller/JobGroupController.java +++ b/ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/controller/JobGroupController.java @@ -1,5 +1,6 @@ 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.XxlJobRegistry; import com.xxl.job.admin.core.util.I18nUtil; @@ -35,12 +36,14 @@ public class JobGroupController { private XxlJobRegistryDao xxlJobRegistryDao; @RequestMapping + @PermissionLimit(adminuser = true) public String index(Model model) { return "jobgroup/jobgroup.index"; } @RequestMapping("/pageList") @ResponseBody + @PermissionLimit(adminuser = true) public Map pageList(HttpServletRequest request, @RequestParam(required = false, defaultValue = "0") int start, @RequestParam(required = false, defaultValue = "10") int length, @@ -60,6 +63,7 @@ public class JobGroupController { @RequestMapping("/save") @ResponseBody + @PermissionLimit(adminuser = true) public ReturnT save(XxlJobGroup xxlJobGroup) { // valid @@ -103,6 +107,7 @@ public class JobGroupController { @RequestMapping("/update") @ResponseBody + @PermissionLimit(adminuser = true) public ReturnT update(XxlJobGroup xxlJobGroup) { // valid if (xxlJobGroup.getAppname() == null || xxlJobGroup.getAppname().trim().length() == 0) { @@ -171,6 +176,7 @@ public class JobGroupController { @RequestMapping("/remove") @ResponseBody + @PermissionLimit(adminuser = true) public ReturnT remove(int id) { // valid @@ -190,6 +196,7 @@ public class JobGroupController { @RequestMapping("/loadById") @ResponseBody + @PermissionLimit(adminuser = true) public ReturnT loadById(int id) { XxlJobGroup jobGroup = xxlJobGroupDao.load(id); return jobGroup != null ? new ReturnT(jobGroup) : new ReturnT(ReturnT.FAIL_CODE, null); diff --git a/ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/controller/JobLogController.java b/ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/controller/JobLogController.java index 3369edb8f..e741c174b 100644 --- a/ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/controller/JobLogController.java +++ b/ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/controller/JobLogController.java @@ -130,22 +130,26 @@ public class JobLogController { model.addAttribute("triggerCode", jobLog.getTriggerCode()); model.addAttribute("handleCode", jobLog.getHandleCode()); - model.addAttribute("executorAddress", jobLog.getExecutorAddress()); - model.addAttribute("triggerTime", jobLog.getTriggerTime().getTime()); model.addAttribute("logId", jobLog.getId()); return "joblog/joblog.detail"; } @RequestMapping("/logDetailCat") @ResponseBody - public ReturnT logDetailCat(String executorAddress, long triggerTime, long logId, int fromLineNum) { + public ReturnT logDetailCat(long logId, int fromLineNum) { try { - ExecutorBiz executorBiz = XxlJobScheduler.getExecutorBiz(executorAddress); - ReturnT logResult = executorBiz.log(new LogParam(triggerTime, logId, fromLineNum)); + // valid + XxlJobLog jobLog = xxlJobLogDao.load(logId); // todo, need to improve performance + if (jobLog == null) { + return new ReturnT(ReturnT.FAIL_CODE, I18nUtil.getString("joblog_logid_unvalid")); + } + + // log cat + ExecutorBiz executorBiz = XxlJobScheduler.getExecutorBiz(jobLog.getExecutorAddress()); + ReturnT logResult = executorBiz.log(new LogParam(jobLog.getTriggerTime().getTime(), logId, fromLineNum)); // is end if (logResult.getContent() != null && logResult.getContent().getFromLineNum() > logResult.getContent().getToLineNum()) { - XxlJobLog jobLog = xxlJobLogDao.load(logId); if (jobLog.getHandleCode() > 0) { logResult.getContent().setEnd(true); } diff --git a/ruoyi-extend/ruoyi-xxl-job-admin/src/main/resources/application.yml b/ruoyi-extend/ruoyi-xxl-job-admin/src/main/resources/application.yml index d4fda939a..6d0829217 100644 --- a/ruoyi-extend/ruoyi-xxl-job-admin/src/main/resources/application.yml +++ b/ruoyi-extend/ruoyi-xxl-job-admin/src/main/resources/application.yml @@ -16,6 +16,9 @@ spring: resources: static-locations: classpath:/static/ +logging: + config: classpath:logback-plus.xml + --- # mybatis 配置 mybatis: mapper-locations: classpath:/mybatis-mapper/*Mapper.xml diff --git a/ruoyi-extend/ruoyi-xxl-job-admin/src/main/resources/i18n/message_en.properties b/ruoyi-extend/ruoyi-xxl-job-admin/src/main/resources/i18n/message_en.properties index 8b3c8014b..881bb8d68 100644 --- a/ruoyi-extend/ruoyi-xxl-job-admin/src/main/resources/i18n/message_en.properties +++ b/ruoyi-extend/ruoyi-xxl-job-admin/src/main/resources/i18n/message_en.properties @@ -1,6 +1,6 @@ admin_name=Scheduling Center admin_name_full=Distributed Task Scheduling Platform XXL-JOB -admin_version=2.3.1 +admin_version=2.4.0 admin_i18n=en ## system diff --git a/ruoyi-extend/ruoyi-xxl-job-admin/src/main/resources/i18n/message_zh_CN.properties b/ruoyi-extend/ruoyi-xxl-job-admin/src/main/resources/i18n/message_zh_CN.properties index b3860e49d..5be17ffe1 100644 --- a/ruoyi-extend/ruoyi-xxl-job-admin/src/main/resources/i18n/message_zh_CN.properties +++ b/ruoyi-extend/ruoyi-xxl-job-admin/src/main/resources/i18n/message_zh_CN.properties @@ -1,6 +1,6 @@ admin_name=任务调度中心 admin_name_full=分布式任务调度平台XXL-JOB -admin_version=2.3.1 +admin_version=2.4.0 admin_i18n= ## system diff --git a/ruoyi-extend/ruoyi-xxl-job-admin/src/main/resources/i18n/message_zh_TC.properties b/ruoyi-extend/ruoyi-xxl-job-admin/src/main/resources/i18n/message_zh_TC.properties index ca069b343..3250f1a9c 100644 --- a/ruoyi-extend/ruoyi-xxl-job-admin/src/main/resources/i18n/message_zh_TC.properties +++ b/ruoyi-extend/ruoyi-xxl-job-admin/src/main/resources/i18n/message_zh_TC.properties @@ -1,6 +1,6 @@ admin_name=任務調度中心 admin_name_full=分布式任務調度平臺XXL-JOB -admin_version=2.3.1 +admin_version=2.4.0 admin_i18n= ## system diff --git a/ruoyi-extend/ruoyi-xxl-job-admin/src/main/resources/logback.xml b/ruoyi-extend/ruoyi-xxl-job-admin/src/main/resources/logback-plus.xml similarity index 100% rename from ruoyi-extend/ruoyi-xxl-job-admin/src/main/resources/logback.xml rename to ruoyi-extend/ruoyi-xxl-job-admin/src/main/resources/logback-plus.xml diff --git a/ruoyi-extend/ruoyi-xxl-job-admin/src/main/resources/static/js/joblog.detail.1.js b/ruoyi-extend/ruoyi-xxl-job-admin/src/main/resources/static/js/joblog.detail.1.js index ddefd4663..0638eee27 100644 --- a/ruoyi-extend/ruoyi-xxl-job-admin/src/main/resources/static/js/joblog.detail.1.js +++ b/ruoyi-extend/ruoyi-xxl-job-admin/src/main/resources/static/js/joblog.detail.1.js @@ -25,8 +25,6 @@ $(function() { async: false, // sync, make log ordered url : base_url + '/joblog/logDetailCat', data : { - "executorAddress":executorAddress, - "triggerTime":triggerTime, "logId":logId, "fromLineNum":fromLineNum }, diff --git a/ruoyi-extend/ruoyi-xxl-job-admin/src/main/resources/templates/joblog/joblog.detail.ftl b/ruoyi-extend/ruoyi-xxl-job-admin/src/main/resources/templates/joblog/joblog.detail.ftl index 3881cfa6a..0ea69c763 100644 --- a/ruoyi-extend/ruoyi-xxl-job-admin/src/main/resources/templates/joblog/joblog.detail.ftl +++ b/ruoyi-extend/ruoyi-xxl-job-admin/src/main/resources/templates/joblog/joblog.detail.ftl @@ -62,11 +62,9 @@ // 参数 var triggerCode = '${triggerCode}'; var handleCode = '${handleCode}'; - var executorAddress = '${executorAddress!}'; - var triggerTime = '${triggerTime?c}'; var logId = '${logId}'; - \ No newline at end of file + diff --git a/ruoyi-framework/pom.xml b/ruoyi-framework/pom.xml index 665a16e0c..43b8056fd 100644 --- a/ruoyi-framework/pom.xml +++ b/ruoyi-framework/pom.xml @@ -5,7 +5,7 @@ ruoyi-vue-plus com.ruoyi - 4.6.0 + 4.7.0 4.0.0 diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/RateLimiterAspect.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/RateLimiterAspect.java index d558a4ce7..a9d61fb60 100644 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/RateLimiterAspect.java +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/RateLimiterAspect.java @@ -18,6 +18,7 @@ import org.redisson.api.RateType; import org.springframework.core.DefaultParameterNameDiscoverer; import org.springframework.core.ParameterNameDiscoverer; import org.springframework.expression.EvaluationContext; +import org.springframework.expression.Expression; import org.springframework.expression.ExpressionParser; import org.springframework.expression.ParserContext; import org.springframework.expression.common.TemplateParserContext; @@ -102,7 +103,14 @@ public class RateLimiterAspect { } // 解析返回给key 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) { throw new ServiceException("限流key解析异常!请联系管理员!"); } diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/encrypt/MybatisDecryptInterceptor.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/encrypt/MybatisDecryptInterceptor.java index 8755e7d46..90b313340 100644 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/encrypt/MybatisDecryptInterceptor.java +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/encrypt/MybatisDecryptInterceptor.java @@ -1,6 +1,6 @@ package com.ruoyi.framework.encrypt; -import cn.hutool.core.collection.CollectionUtil; +import cn.hutool.core.collection.CollUtil; import cn.hutool.core.util.ObjectUtil; import com.ruoyi.common.annotation.EncryptField; import com.ruoyi.common.encrypt.EncryptContext; @@ -62,12 +62,12 @@ public class MybatisDecryptInterceptor implements Interceptor { } if (sourceObject instanceof List) { List sourceList = (List) sourceObject; - if(CollectionUtil.isEmpty(sourceList)) { + if(CollUtil.isEmpty(sourceList)) { return; } // 判断第一个元素是否含有注解。如果没有直接返回,提高效率 Object firstItem = sourceList.get(0); - if (CollectionUtil.isEmpty(encryptorManager.getFieldCache(firstItem.getClass()))) { + if (ObjectUtil.isNull(firstItem) || CollUtil.isEmpty(encryptorManager.getFieldCache(firstItem.getClass()))) { return; } ((List) sourceObject).forEach(this::decryptHandler); @@ -91,6 +91,9 @@ public class MybatisDecryptInterceptor implements Interceptor { * @return 加密后结果 */ private String decryptField(String value, Field field) { + if (ObjectUtil.isNull(value)) { + return null; + } EncryptField encryptField = field.getAnnotation(EncryptField.class); EncryptContext encryptContext = new EncryptContext(); encryptContext.setAlgorithm(encryptField.algorithm() == AlgorithmType.DEFAULT ? defaultProperties.getAlgorithm() : encryptField.algorithm()); diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/encrypt/MybatisEncryptInterceptor.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/encrypt/MybatisEncryptInterceptor.java index e2ce1957b..f5ffb2b12 100644 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/encrypt/MybatisEncryptInterceptor.java +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/encrypt/MybatisEncryptInterceptor.java @@ -1,6 +1,6 @@ package com.ruoyi.framework.encrypt; -import cn.hutool.core.collection.CollectionUtil; +import cn.hutool.core.collection.CollUtil; import cn.hutool.core.util.ObjectUtil; import com.ruoyi.common.annotation.EncryptField; import com.ruoyi.common.encrypt.EncryptContext; @@ -72,12 +72,12 @@ public class MybatisEncryptInterceptor implements Interceptor { } if (sourceObject instanceof List) { List sourceList = (List) sourceObject; - if(CollectionUtil.isEmpty(sourceList)) { + if(CollUtil.isEmpty(sourceList)) { return; } // 判断第一个元素是否含有注解。如果没有直接返回,提高效率 Object firstItem = sourceList.get(0); - if (CollectionUtil.isEmpty(encryptorManager.getFieldCache(firstItem.getClass()))) { + if (ObjectUtil.isNull(firstItem) || CollUtil.isEmpty(encryptorManager.getFieldCache(firstItem.getClass()))) { return; } ((List) sourceObject).forEach(this::encryptHandler); @@ -101,6 +101,9 @@ public class MybatisEncryptInterceptor implements Interceptor { * @return 加密后结果 */ private String encryptField(String value, Field field) { + if (ObjectUtil.isNull(value)) { + return null; + } EncryptField encryptField = field.getAnnotation(EncryptField.class); EncryptContext encryptContext = new EncryptContext(); encryptContext.setAlgorithm(encryptField.algorithm() == AlgorithmType.DEFAULT ? defaultProperties.getAlgorithm() : encryptField.algorithm()); diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/handler/CreateAndUpdateMetaObjectHandler.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/handler/CreateAndUpdateMetaObjectHandler.java index 6d7c977de..3f0e122ec 100644 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/handler/CreateAndUpdateMetaObjectHandler.java +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/handler/CreateAndUpdateMetaObjectHandler.java @@ -73,7 +73,7 @@ public class CreateAndUpdateMetaObjectHandler implements MetaObjectHandler { log.warn("自动注入警告 => 用户未登录"); return null; } - return loginUser.getUsername(); + return ObjectUtil.isNotNull(loginUser) ? loginUser.getUsername() : null; } } diff --git a/ruoyi-generator/pom.xml b/ruoyi-generator/pom.xml index edbd25528..8524a2352 100644 --- a/ruoyi-generator/pom.xml +++ b/ruoyi-generator/pom.xml @@ -5,7 +5,7 @@ ruoyi-vue-plus com.ruoyi - 4.6.0 + 4.7.0 4.0.0 diff --git a/ruoyi-generator/src/main/java/com/ruoyi/generator/service/GenTableServiceImpl.java b/ruoyi-generator/src/main/java/com/ruoyi/generator/service/GenTableServiceImpl.java index 085589fb2..34c456927 100644 --- a/ruoyi-generator/src/main/java/com/ruoyi/generator/service/GenTableServiceImpl.java +++ b/ruoyi-generator/src/main/java/com/ruoyi/generator/service/GenTableServiceImpl.java @@ -317,13 +317,11 @@ public class GenTableServiceImpl implements IGenTableService { column.setIsRequired(prevColumn.getIsRequired()); column.setHtmlType(prevColumn.getHtmlType()); } - genTableColumnMapper.updateById(column); - } else { - genTableColumnMapper.insert(column); } + saveColumns.add(column); }); if (CollUtil.isNotEmpty(saveColumns)) { - genTableColumnMapper.insertBatch(saveColumns); + genTableColumnMapper.insertOrUpdateBatch(saveColumns); } List delColumns = StreamUtils.filter(tableColumns, column -> !dbTableColumnNames.contains(column.getColumnName())); if (CollUtil.isNotEmpty(delColumns)) { diff --git a/ruoyi-job/pom.xml b/ruoyi-job/pom.xml index 364dfa9f3..558451cf7 100644 --- a/ruoyi-job/pom.xml +++ b/ruoyi-job/pom.xml @@ -5,7 +5,7 @@ ruoyi-vue-plus com.ruoyi - 4.6.0 + 4.7.0 4.0.0 jar diff --git a/ruoyi-oss/pom.xml b/ruoyi-oss/pom.xml index c2e1e996c..8da43cbb6 100644 --- a/ruoyi-oss/pom.xml +++ b/ruoyi-oss/pom.xml @@ -5,7 +5,7 @@ ruoyi-vue-plus com.ruoyi - 4.6.0 + 4.7.0 4.0.0 diff --git a/ruoyi-sms/pom.xml b/ruoyi-sms/pom.xml index 387139dff..efd0bbdfe 100644 --- a/ruoyi-sms/pom.xml +++ b/ruoyi-sms/pom.xml @@ -5,7 +5,7 @@ ruoyi-vue-plus com.ruoyi - 4.6.0 + 4.7.0 4.0.0 diff --git a/ruoyi-system/pom.xml b/ruoyi-system/pom.xml index 9c382a39e..57c1017f9 100644 --- a/ruoyi-system/pom.xml +++ b/ruoyi-system/pom.xml @@ -5,7 +5,7 @@ ruoyi-vue-plus com.ruoyi - 4.6.0 + 4.7.0 4.0.0 diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysUserMapper.java b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysUserMapper.java index 9b585e1db..7147198da 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysUserMapper.java +++ b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysUserMapper.java @@ -76,6 +76,14 @@ public interface SysUserMapper extends BaseMapperPlus !BCrypt.checkpw(password, user.getPassword())); @@ -100,6 +98,20 @@ public class SysLoginService { 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) { // xcxCode 为 小程序调用 wx.login 授权后获取 @@ -140,7 +152,6 @@ public class SysLoginService { * @param username 用户名 * @param status 状态 * @param message 消息内容 - * @return */ private void recordLogininfor(String username, String status, String message) { LogininforEvent logininforEvent = new LogininforEvent(); @@ -163,6 +174,18 @@ public class SysLoginService { 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 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 captcha = RedisUtils.getCacheObject(verifyKey); RedisUtils.deleteObject(verifyKey); @@ -212,6 +235,20 @@ public class SysLoginService { return userMapper.selectUserByPhonenumber(phonenumber); } + private SysUser loadUserByEmail(String email) { + SysUser user = userMapper.selectOne(new LambdaQueryWrapper() + .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) { // 使用 openid 查询绑定用户 如未绑定用户 则根据业务自行处理 例如 创建默认用户 // todo 自行实现 userService.selectUserByOpenid(openid); diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/SysRegisterService.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/SysRegisterService.java index 886aec3b8..1a034e569 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/system/service/SysRegisterService.java +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/SysRegisterService.java @@ -3,7 +3,6 @@ package com.ruoyi.system.service; import cn.dev33.satoken.secure.BCrypt; import com.ruoyi.common.constant.CacheConstants; import com.ruoyi.common.constant.Constants; -import com.ruoyi.common.constant.UserConstants; import com.ruoyi.common.core.domain.event.LogininforEvent; import com.ruoyi.common.core.domain.entity.SysUser; import com.ruoyi.common.core.domain.model.RegisterBody; @@ -19,8 +18,6 @@ import com.ruoyi.common.utils.spring.SpringUtils; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; -import javax.servlet.http.HttpServletRequest; - /** * 注册校验方法 * @@ -37,7 +34,6 @@ public class SysRegisterService { * 注册 */ public void register(RegisterBody registerBody) { - HttpServletRequest request = ServletUtils.getRequest(); String username = registerBody.getUsername(); String password = registerBody.getPassword(); // 校验用户类型是否存在 @@ -46,7 +42,7 @@ public class SysRegisterService { boolean captchaEnabled = configService.selectCaptchaEnabled(); // 验证码开关 if (captchaEnabled) { - validateCaptcha(username, registerBody.getCode(), registerBody.getUuid(), request); + validateCaptcha(username, registerBody.getCode(), registerBody.getUuid()); } SysUser sysUser = new SysUser(); sysUser.setUserName(username); @@ -70,9 +66,8 @@ public class SysRegisterService { * @param username 用户名 * @param code 验证码 * @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 captcha = RedisUtils.getCacheObject(verifyKey); RedisUtils.deleteObject(verifyKey); diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysRoleServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysRoleServiceImpl.java index eb2a6df2a..b28df9e98 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysRoleServiceImpl.java +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysRoleServiceImpl.java @@ -1,5 +1,7 @@ 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.util.ObjectUtil; 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.core.domain.PageQuery; 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.exception.ServiceException; 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()) .between(params.get("beginTime") != null && params.get("endTime") != null, "r.create_time", params.get("beginTime"), params.get("endTime")) - .orderByAsc("r.role_sort"); + .orderByAsc("r.role_sort").orderByAsc("r.create_time"); return wrapper; } @@ -362,9 +365,13 @@ public class SysRoleServiceImpl implements ISysRoleService { */ @Override public int deleteAuthUser(SysUserRole userRole) { - return userRoleMapper.delete(new LambdaQueryWrapper() + int rows = userRoleMapper.delete(new LambdaQueryWrapper() .eq(SysUserRole::getRoleId, userRole.getRoleId()) .eq(SysUserRole::getUserId, userRole.getUserId())); + if (rows > 0) { + cleanOnlineUserByRole(userRole.getRoleId()); + } + return rows; } /** @@ -376,9 +383,13 @@ public class SysRoleServiceImpl implements ISysRoleService { */ @Override public int deleteAuthUsers(Long roleId, Long[] userIds) { - return userRoleMapper.delete(new LambdaQueryWrapper() + int rows = userRoleMapper.delete(new LambdaQueryWrapper() .eq(SysUserRole::getRoleId, roleId) .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)) { rows = userRoleMapper.insertBatch(list) ? list.size() : 0; } + if (rows > 0) { + cleanOnlineUserByRole(roleId); + } return rows; } + + @Override + public void cleanOnlineUserByRole(Long roleId) { + List 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) { + } + } + }); + } } diff --git a/ruoyi-system/src/main/resources/mapper/system/SysUserMapper.xml b/ruoyi-system/src/main/resources/mapper/system/SysUserMapper.xml index 06a653efe..e73e715db 100644 --- a/ruoyi-system/src/main/resources/mapper/system/SysUserMapper.xml +++ b/ruoyi-system/src/main/resources/mapper/system/SysUserMapper.xml @@ -128,6 +128,11 @@ where u.del_flag = '0' and u.phonenumber = #{phonenumber} + +