diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml deleted file mode 100644 index fbcab775dee974473e2f48129ecf4657fba6344e..0000000000000000000000000000000000000000 --- a/.github/FUNDING.yml +++ /dev/null @@ -1 +0,0 @@ -custom: http://doc.ruoyi.vip/ruoyi-vue/other/donate.html diff --git a/README.md b/README.md index 0b762d483acd9a0e712aa23991ce234934dfe838..bccf1b8e7ca8fa84f12ee5eab490fdcba35a1795 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,8 @@ ## 在线演示 演示服务不限制CURD操作,希望大家按需使用,不要恶意添加脏数据或对服务器进行攻击等操作。(将不定期清理数据) -[RuoYi-Flowable-Plus 在线演示](http://www.konbai.work:1024) + +[RuoYi-Flowable-Plus 在线演示](http://159.75.158.189/) | | 账号 | 密码 | |---------------- | ----- | -------- | @@ -32,11 +33,10 @@ | 数据监控中心 | ruoyi | 123456 | ## 技术交流群 - -| 交流①群(已满) | 开发②群 | -| ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- |------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| [![加入QQ群](https://img.shields.io/badge/QQ群-1007207992-blue.svg?style=flat)](https://jq.qq.com/?_wv=1027\&k=PYDZa1tA) | [![加入QQ群](https://img.shields.io/badge/QQ群-725502135-blue.svg?style=flat)](https://jq.qq.com/?_wv=1027&k=J4zeZaKo) | -| | | + +交流1群 🈵️ [![加入QQ群](https://img.shields.io/badge/QQ群-1007207992-blue.svg?style=flat)](https://jq.qq.com/?_wv=1027\&k=PYDZa1tA)
+交流2群 [![加入QQ群](https://img.shields.io/badge/QQ群-725502135-blue.svg?style=flat)](https://jq.qq.com/?_wv=1027&k=J4zeZaKo)
+交流3群 [![加入QQ群](https://img.shields.io/badge/QQ群-860980043-blue.svg?style=flat)](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=NfqIsFMASOvIC6yHYwY6bnaSfdgcD1La&authKey=SeFDA4oFkb%2FkdvnI%2FJ3aJTJZkyzDaz8v8gybpzUATAilnKSCmyKhCE6R2jkXc5e2&noverify=0&group_code=860980043)
## 参与开源 - 如遇到问题,欢迎提交到 [issues](https://gitee.com/KonBAI-Q/ruoyi-flowable-plus/issues)(请按模版进行填写信息)。 diff --git a/pom.xml b/pom.xml index 50131753468a0705f140acbffa86278540f7b532..cd9f6afcd86c604cb43d62ab1092dfddc413b648 100644 --- a/pom.xml +++ b/pom.xml @@ -14,7 +14,7 @@ 0.8.3 - 2.7.9 + 2.7.11 UTF-8 UTF-8 1.8 @@ -27,17 +27,19 @@ 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 6.8.0 + + 2.7.0 1.33 @@ -259,6 +261,13 @@ ${alibaba-ttl.version} + + + org.lionsoul + ip2region + ${ip2region.version} + + org.yaml 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 795359c5b29867bd75a43709b11c6716647d3215..f5a603a43c6e6a343b57d6f10ce376c80cf1ee96 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 74585fc48d1db35f9c2a1e3d676a8049f6b9f899..f0ecce1ba2ee063a8bc9869978ab4d18193ebc7f 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 b0b574c3709608288f9666c9d1541d6b2ec97164..33d425a6ace7c70e4377ec5b6017f9bd8bc657b9 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 78c44e30531bc829c292ec41ab4043016056752c..f982a5fac875843a651b66443712e7b2086c85f7 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 606895a740db1f0d6e1b25e6fae6f18a1cc6dc28..2a7cc11cb89394898572bee2683dc919e32c9149 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 47e157512960d2577bc33abdc47a9f5ec2797544..f8edb5d81c5ed2941ff13cd013ee4d74d8b53cf9 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 c554e65e433d03bf5e04aa39f6d5d8a742f3992c..558d583393ea261b45600adf6703f9f5a256fa0e 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: @@ -278,4 +278,9 @@ flowable: # 库与数据库表结构不一致时,会自动将数据库表结构升级至新版本。 database-schema-update: true idm: + # 关闭idm引擎 数据库不会创建act_id_*表,流程流转不会使用act_id_*相关的表 enabled: false + # 关闭流程定义文件自动检查 + check-process-definitions: false + # 关闭历史任务定时任务job + async-history-executor-activate: false diff --git a/ruoyi-admin/src/main/resources/i18n/messages.properties b/ruoyi-admin/src/main/resources/i18n/messages.properties index 4ff55f41ac8874efbf371398155013d199951ff2..ffdd8f304df51c1914e1023ff42c5a8dc9f301d8 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 c0faca93a69f6d637046311424275355d6b89fb6..c1ca43916dda231fb57f747787142ac9f5f7749c 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 4ff55f41ac8874efbf371398155013d199951ff2..ffdd8f304df51c1914e1023ff42c5a8dc9f301d8 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 0000000000000000000000000000000000000000..31f96a1fb1695b14c86a73a0cc14fa6c600263c1 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 505579a49ad4f9adb5e9645ba7cc72d7184ae992..664d4d4cdb321d9d8e84d6f247b23f3e85895b4b 100644 --- a/ruoyi-common/pom.xml +++ b/ruoyi-common/pom.xml @@ -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 1cdf07eecadb58d34d74b893ee6212ab554f3130..0fb2c3f9799b4ff9d8e4af1cd43646144ff32cbf 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/constant/CacheNames.java b/ruoyi-common/src/main/java/com/ruoyi/common/constant/CacheNames.java index 1396b6d18248278b2ad3931c432f203e92edaa53..7d4164b61d0a1d148b037be5d0f47cf29d641030 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/constant/CacheNames.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/constant/CacheNames.java @@ -35,6 +35,11 @@ public interface CacheNames { */ String SYS_USER_NAME = "sys_user_name#30d"; + /** + * 用户昵称 + */ + String SYS_NICK_NAME = "sys_nick_name#30d"; + /** * 部门 */ 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 0000000000000000000000000000000000000000..b7bf81bdb54552496626bff4f4930ce6577b41e4 --- /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 ce774ac7e006f4975d5f93ef4ce08e49a072472d..b12e74c1473834a7fbcfda69eb1ea0c505803d3b 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/core/service/UserService.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/service/UserService.java index d2206c8b0173ca4633c43e3695cf4717a8803609..0d9824077f0793d0db2325df7d81d1728d36a3d9 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/core/service/UserService.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/service/UserService.java @@ -15,4 +15,12 @@ public interface UserService { */ String selectUserNameById(Long userId); + /** + * 通过用户ID查询用户昵称 + * + * @param userId 用户ID + * @return 用户昵称 + */ + String selectNickNameById(Long userId); + } 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 c91a4b94f4b9280c5e4ad61de3ab7e79ebd67cf4..875e4762ef61135ffa5dd7571757b073a229e7b2 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 6cd6d281669fffad6394d056a57b14a042cd1953..2a3fc3b1e022e90e06b8c866dc41e19742152857 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 c852ce65fd53cf9d9c86bbda6f22491f9004b0ad..5ef65bf84aec1541438da50a719d543977aa1ca3 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 19603562998fb7e7bd24a7b8fcd653939f0b8008..33e0a78fe0fa81f329679566e671587e311662ec 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 e2b1129f5f5d2a8d64ded6a0554269968f31cf26..c3b8b47cdb531de5f7f08f691321f7b02301420f 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 69196171b7caa7c9a619680774e1855cc46394d4..ada3c253a7bf8991dbbb1444bb59e35bb75ccc38 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 210312c82bfe0d1877617717a27e501c43edc5cc..1e97b66ce33ccaa2b9d2edb576919ae07fabf9fb 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 69ebd9afe0c96a9eec0081d90156a2733d2f2d28..863e4d68614f78812a38dca80f73639a2e32c4a9 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 36fd6c399a323a03b68c19a52c5a060bb92fc363..eccf1c8b5beb260fb0b789f631efea70fe308de7 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 0000000000000000000000000000000000000000..691b6c0f4a6d8fdbf1bf7e5213b6092d13155d67 --- /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 8d3515e7fea1c88e1fd2fc88b2608865aef7d0b1..9bf203bf92d35e71e4683979f9419393387be3b4 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 0000000000000000000000000000000000000000..581061a51e615ce5cd9787b396a290358c7c3eda --- /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/src/main/java/com/ruoyi/demo/controller/RedisRateLimiterController.java b/ruoyi-demo/src/main/java/com/ruoyi/demo/controller/RedisRateLimiterController.java index ee2e530bac64517ec1835cf1a36d6c16fe4d1367..d0401956ebb82382edf9031ca337f4d91fdacec2 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/ruoyi-monitor-admin/src/main/resources/application.yml b/ruoyi-extend/ruoyi-monitor-admin/src/main/resources/application.yml index 22ef1abfc7a1df23eac1b55d1716653992d620e3..1b729ef1b8d24219b60dddca9e402b9807a4ec3a 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/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 82ec2dee4605d3bee0012ca489c76813e508cfed..5cbf872878dc4b214f910a246a9ccbed8e670a47 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 3369edb8f33acc9e0a1fa935611795b695c3490d..e741c174bc463af4340b3d94c1420a917cf2cc90 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 d4fda939a6ef5e2b848e2b43d49303313c515210..6d08292173df4a090b95acb625c176fa4573fb77 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 8b3c8014b45b09d593c99b2603f0335dc3614ccb..881bb8d687759b2ae41c33339ccb6bf17690d8e9 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 b3860e49d119ecda9291a96c7384c6f4000c49c3..5be17ffe16dcd0057e5c7b20435f7490df6a3123 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 ca069b343c4c361d6158be9f3765c0907a192ce3..3250f1a9c5116527c6e5206e3e070dcb99a31327 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 ddefd46631a54e3cc0bcde085a73ba3409d0985f..0638eee275739bfdf038ae8b8197c7ece968dae4 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 3881cfa6ae491b7f21ff84196ad1a444be630ddc..0ea69c7635950181f7acb0717c987baf884e5edb 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/src/main/java/com/ruoyi/framework/aspectj/RateLimiterAspect.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/RateLimiterAspect.java index d558a4ce7afabb2af9c5d6be485ca4e754c20ec6..a9d61fb609b10767f380087289bc38cc7284a534 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 8755e7d4677953778f81d2d5e0258dbaf9aa70a0..90b31334012a6ae834cba42a4acdd5e9ea0710cc 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 e2ce1957b6b2b7aab01ab3c3aca8dfe3cde149dd..f5ffb2b12636266096e8e9432d220c3ff7f33ecb 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 6d7c977de643da6ecdc93d6b69727405899f9b8f..3f0e122ec3e4a89808ccb5ef58a8ada5ffb3e56a 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/src/main/java/com/ruoyi/generator/service/GenTableServiceImpl.java b/ruoyi-generator/src/main/java/com/ruoyi/generator/service/GenTableServiceImpl.java index 085589fb25347026b604d6e82c8d180f6087d879..34c4569273e0d3b0c35a6041d7c71329f34cd3ce 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-system/src/main/java/com/ruoyi/system/mapper/SysUserMapper.java b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysUserMapper.java index 9b585e1dbb7d27e68c05aa511f146d8da5631afe..7147198daee9268ff029d9c37b4739241ba5b7be 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 886aec3b8a0e1f056c33088bc456cb3ce9e59650..1a034e56944e102211ce7af1f5e1151ab6462098 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 eb2a6df2a3bc3ad3a66a262ac6bbe63b1ce2b07c..b28df9e98bb6f65cad6947aa1a0bcc60b2aea363 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/java/com/ruoyi/system/service/impl/SysUserServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysUserServiceImpl.java index b4105870c9dab87f06b34dbd07b8b3d904e6b1e3..3e0626ffea5b59c6efefcca02ac7ce594184b140 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysUserServiceImpl.java +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysUserServiceImpl.java @@ -482,4 +482,12 @@ public class SysUserServiceImpl implements ISysUserService, UserService { return ObjectUtil.isNull(sysUser) ? null : sysUser.getUserName(); } + @Cacheable(cacheNames = CacheNames.SYS_NICK_NAME, key = "#userId") + @Override + public String selectNickNameById(Long userId) { + SysUser sysUser = baseMapper.selectOne(new LambdaQueryWrapper() + .select(SysUser::getNickName).eq(SysUser::getUserId, userId)); + return ObjectUtil.isNull(sysUser) ? null : sysUser.getNickName(); + } + } diff --git a/ruoyi-system/src/main/java/com/ruoyi/workflow/domain/vo/WfTaskVo.java b/ruoyi-system/src/main/java/com/ruoyi/workflow/domain/vo/WfTaskVo.java index 04518cbae9d9c9d50a8cef87bf81f20f3c91587a..6ffef1ba77989046d6dd34bb8bb110dd8414f799 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/workflow/domain/vo/WfTaskVo.java +++ b/ruoyi-system/src/main/java/com/ruoyi/workflow/domain/vo/WfTaskVo.java @@ -38,10 +38,12 @@ public class WfTaskVo implements Serializable { /** * 部门名称 */ + @Deprecated private String deptName; /** * 流程发起人部门名称 */ + @Deprecated private String startDeptName; /** * 任务执行人名称 @@ -50,7 +52,7 @@ public class WfTaskVo implements Serializable { /** * 流程发起人Id */ - private String startUserId; + private Long startUserId; /** * 流程发起人名称 */ diff --git a/ruoyi-system/src/main/java/com/ruoyi/workflow/service/impl/WfInstanceServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/workflow/service/impl/WfInstanceServiceImpl.java index de0362e6cc252cd79463b6e95ee231073e0b83a7..703615fcbc9f35711b933fc390cab8dcfc5e4a8a 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/workflow/service/impl/WfInstanceServiceImpl.java +++ b/ruoyi-system/src/main/java/com/ruoyi/workflow/service/impl/WfInstanceServiceImpl.java @@ -7,7 +7,7 @@ import cn.hutool.core.date.DateUtil; import cn.hutool.core.util.ObjectUtil; 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.service.UserService; import com.ruoyi.common.exception.ServiceException; import com.ruoyi.common.utils.JsonUtils; import com.ruoyi.common.utils.StringUtils; @@ -15,7 +15,6 @@ import com.ruoyi.flowable.common.constant.TaskConstants; import com.ruoyi.flowable.factory.FlowServiceFactory; import com.ruoyi.system.service.ISysDeptService; import com.ruoyi.system.service.ISysRoleService; -import com.ruoyi.system.service.ISysUserService; import com.ruoyi.workflow.domain.bo.WfTaskBo; import com.ruoyi.workflow.domain.vo.WfFormVo; import com.ruoyi.workflow.domain.vo.WfTaskVo; @@ -45,7 +44,7 @@ import java.util.*; public class WfInstanceServiceImpl extends FlowServiceFactory implements IWfInstanceService { private final IWfDeployFormService deployFormService; - private final ISysUserService userService; + private final UserService userService; private final ISysRoleService roleService; private final ISysDeptService deptService; @@ -143,10 +142,10 @@ public class WfInstanceServiceImpl extends FlowServiceFactory implements IWfInst taskVo.setCreateTime(taskInstance.getStartTime()); taskVo.setFinishTime(taskInstance.getEndTime()); if (StringUtils.isNotBlank(taskInstance.getAssignee())) { - SysUser user = userService.selectUserById(Long.parseLong(taskInstance.getAssignee())); - taskVo.setAssigneeId(user.getUserId()); - taskVo.setAssigneeName(user.getNickName()); - taskVo.setDeptName(user.getDept().getDeptName()); + Long userId = Long.parseLong(taskInstance.getAssignee()); + String nickName = userService.selectNickNameById(userId); + taskVo.setAssigneeId(userId); + taskVo.setAssigneeName(nickName); } // 展示审批人员 List linksForTask = historyService.getHistoricIdentityLinksForTask(taskInstance.getId()); @@ -154,8 +153,9 @@ public class WfInstanceServiceImpl extends FlowServiceFactory implements IWfInst for (HistoricIdentityLink identityLink : linksForTask) { if ("candidate".equals(identityLink.getType())) { if (StringUtils.isNotBlank(identityLink.getUserId())) { - SysUser user = userService.selectUserById(Long.parseLong(identityLink.getUserId())); - stringBuilder.append(user.getNickName()).append(","); + Long userId = Long.parseLong(identityLink.getUserId()); + String nickName = userService.selectNickNameById(userId); + stringBuilder.append(nickName).append(","); } if (StringUtils.isNotBlank(identityLink.getGroupId())) { if (identityLink.getGroupId().startsWith(TaskConstants.ROLE_GROUP_PREFIX)) { diff --git a/ruoyi-system/src/main/java/com/ruoyi/workflow/service/impl/WfModelServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/workflow/service/impl/WfModelServiceImpl.java index e74d444152057ab5b94cb824df4f4d01d425e537..ab66dd7bd269f0ab055aece1dd04c7d967edafd7 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/workflow/service/impl/WfModelServiceImpl.java +++ b/ruoyi-system/src/main/java/com/ruoyi/workflow/service/impl/WfModelServiceImpl.java @@ -1,5 +1,6 @@ package com.ruoyi.workflow.service.impl; +import cn.hutool.core.util.ArrayUtil; import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.StrUtil; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; @@ -319,6 +320,9 @@ public class WfModelServiceImpl extends FlowServiceFactory implements IWfModelSe } // 获取流程图 byte[] bpmnBytes = repositoryService.getModelEditorSource(modelId); + if (ArrayUtil.isEmpty(bpmnBytes)) { + throw new RuntimeException("请先设计流程图!"); + } String bpmnXml = StringUtils.toEncodedString(bpmnBytes, StandardCharsets.UTF_8); BpmnModel bpmnModel = ModelUtils.getBpmnModel(bpmnXml); String processName = model.getName() + ProcessConstants.SUFFIX; diff --git a/ruoyi-system/src/main/java/com/ruoyi/workflow/service/impl/WfProcessServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/workflow/service/impl/WfProcessServiceImpl.java index be272d30578e7edd5e2644fb5085f2926ce96aa5..10ce59a38da5db1eb208d2dac71834145281c302 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/workflow/service/impl/WfProcessServiceImpl.java +++ b/ruoyi-system/src/main/java/com/ruoyi/workflow/service/impl/WfProcessServiceImpl.java @@ -13,8 +13,8 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.ruoyi.common.core.domain.PageQuery; 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.page.TableDataInfo; +import com.ruoyi.common.core.service.UserService; import com.ruoyi.common.exception.ServiceException; import com.ruoyi.common.utils.DateUtils; import com.ruoyi.common.utils.JsonUtils; @@ -32,7 +32,6 @@ import com.ruoyi.flowable.utils.ProcessUtils; import com.ruoyi.flowable.utils.TaskUtils; import com.ruoyi.system.service.ISysDeptService; import com.ruoyi.system.service.ISysRoleService; -import com.ruoyi.system.service.ISysUserService; import com.ruoyi.workflow.domain.WfDeployForm; import com.ruoyi.workflow.domain.vo.*; import com.ruoyi.workflow.mapper.WfDeployFormMapper; @@ -73,7 +72,7 @@ import java.util.stream.Collectors; public class WfProcessServiceImpl extends FlowServiceFactory implements IWfProcessService { private final IWfTaskService wfTaskService; - private final ISysUserService userService; + private final UserService userService; private final ISysRoleService roleService; private final ISysDeptService deptService; private final WfDeployFormMapper deployFormMapper; @@ -290,10 +289,10 @@ public class WfProcessServiceImpl extends FlowServiceFactory implements IWfProce HistoricProcessInstance historicProcessInstance = historyService.createHistoricProcessInstanceQuery() .processInstanceId(task.getProcessInstanceId()) .singleResult(); - SysUser startUser = userService.selectUserById(Long.parseLong(historicProcessInstance.getStartUserId())); - flowTask.setStartUserId(startUser.getNickName()); - flowTask.setStartUserName(startUser.getNickName()); - flowTask.setStartDeptName(startUser.getDept().getDeptName()); + Long userId = Long.parseLong(historicProcessInstance.getStartUserId()); + String nickName = userService.selectNickNameById(userId); + flowTask.setStartUserId(userId); + flowTask.setStartUserName(nickName); // 流程变量 flowTask.setProcVars(task.getProcessVariables()); @@ -337,12 +336,10 @@ public class WfProcessServiceImpl extends FlowServiceFactory implements IWfProce HistoricProcessInstance historicProcessInstance = historyService.createHistoricProcessInstanceQuery() .processInstanceId(task.getProcessInstanceId()) .singleResult(); - SysUser startUser = userService.selectUserById(Long.parseLong(historicProcessInstance.getStartUserId())); - taskVo.setStartUserId(startUser.getNickName()); - taskVo.setStartUserName(startUser.getNickName()); - taskVo.setStartDeptName(startUser.getDept().getDeptName()); - // 流程变量 - taskVo.setProcVars(task.getProcessVariables()); + Long userId = Long.parseLong(historicProcessInstance.getStartUserId()); + String nickName = userService.selectNickNameById(userId); + taskVo.setStartUserId(userId); + taskVo.setStartUserName(nickName); taskVoList.add(taskVo); } @@ -385,10 +382,10 @@ public class WfProcessServiceImpl extends FlowServiceFactory implements IWfProce HistoricProcessInstance historicProcessInstance = historyService.createHistoricProcessInstanceQuery() .processInstanceId(task.getProcessInstanceId()) .singleResult(); - SysUser startUser = userService.selectUserById(Long.parseLong(historicProcessInstance.getStartUserId())); - flowTask.setStartUserId(startUser.getNickName()); - flowTask.setStartUserName(startUser.getNickName()); - flowTask.setStartDeptName(startUser.getDept().getDeptName()); + Long userId = Long.parseLong(historicProcessInstance.getStartUserId()); + String nickName = userService.selectNickNameById(userId); + flowTask.setStartUserId(userId); + flowTask.setStartUserName(nickName); flowList.add(flowTask); } @@ -429,10 +426,10 @@ public class WfProcessServiceImpl extends FlowServiceFactory implements IWfProce HistoricProcessInstance historicProcessInstance = historyService.createHistoricProcessInstanceQuery() .processInstanceId(task.getProcessInstanceId()) .singleResult(); - SysUser startUser = userService.selectUserById(Long.parseLong(historicProcessInstance.getStartUserId())); - flowTask.setStartUserId(startUser.getNickName()); - flowTask.setStartUserName(startUser.getNickName()); - flowTask.setStartDeptName(startUser.getDept().getDeptName()); + Long userId = Long.parseLong(historicProcessInstance.getStartUserId()); + String nickName = userService.selectNickNameById(userId); + flowTask.setStartUserId(userId); + flowTask.setStartUserName(nickName); flowList.add(flowTask); } @@ -479,10 +476,10 @@ public class WfProcessServiceImpl extends FlowServiceFactory implements IWfProce HistoricProcessInstance historicProcessInstance = historyService.createHistoricProcessInstanceQuery() .processInstanceId(histTask.getProcessInstanceId()) .singleResult(); - SysUser startUser = userService.selectUserById(Long.parseLong(historicProcessInstance.getStartUserId())); - flowTask.setStartUserId(startUser.getNickName()); - flowTask.setStartUserName(startUser.getNickName()); - flowTask.setStartDeptName(startUser.getDept().getDeptName()); + Long userId = Long.parseLong(historicProcessInstance.getStartUserId()); + String nickName = userService.selectNickNameById(userId); + flowTask.setStartUserId(userId); + flowTask.setStartUserName(nickName); // 流程变量 flowTask.setProcVars(histTask.getProcessVariables()); @@ -535,10 +532,10 @@ public class WfProcessServiceImpl extends FlowServiceFactory implements IWfProce HistoricProcessInstance historicProcessInstance = historyService.createHistoricProcessInstanceQuery() .processInstanceId(histTask.getProcessInstanceId()) .singleResult(); - SysUser startUser = userService.selectUserById(Long.parseLong(historicProcessInstance.getStartUserId())); - flowTask.setStartUserId(startUser.getNickName()); - flowTask.setStartUserName(startUser.getNickName()); - flowTask.setStartDeptName(startUser.getDept().getDeptName()); + Long userId = Long.parseLong(historicProcessInstance.getStartUserId()); + String nickName = userService.selectNickNameById(userId); + flowTask.setStartUserId(userId); + flowTask.setStartUserName(nickName); // 流程变量 flowTask.setProcVars(histTask.getProcessVariables()); @@ -891,17 +888,18 @@ public class WfProcessServiceImpl extends FlowServiceFactory implements IWfProce if (BpmnXMLConstants.ELEMENT_EVENT_START.equals(activityInstance.getActivityType())) { if (ObjectUtil.isNotNull(historicProcIns)) { Long userId = Long.parseLong(historicProcIns.getStartUserId()); - SysUser user = userService.selectUserById(userId); - if (user != null) { - elementVo.setAssigneeId(user.getUserId()); - elementVo.setAssigneeName(user.getNickName()); + String nickName = userService.selectNickNameById(userId); + if (nickName != null) { + elementVo.setAssigneeId(userId); + elementVo.setAssigneeName(nickName); } } } else if (BpmnXMLConstants.ELEMENT_TASK_USER.equals(activityInstance.getActivityType())) { if (StringUtils.isNotBlank(activityInstance.getAssignee())) { - SysUser user = userService.selectUserById(Long.parseLong(activityInstance.getAssignee())); - elementVo.setAssigneeId(user.getUserId()); - elementVo.setAssigneeName(user.getNickName()); + Long userId = Long.parseLong(activityInstance.getAssignee()); + String nickName = userService.selectNickNameById(userId); + elementVo.setAssigneeId(userId); + elementVo.setAssigneeName(nickName); } // 展示审批人员 List linksForTask = historyService.getHistoricIdentityLinksForTask(activityInstance.getTaskId()); @@ -909,8 +907,9 @@ public class WfProcessServiceImpl extends FlowServiceFactory implements IWfProce for (HistoricIdentityLink identityLink : linksForTask) { if ("candidate".equals(identityLink.getType())) { if (StringUtils.isNotBlank(identityLink.getUserId())) { - SysUser user = userService.selectUserById(Long.parseLong(identityLink.getUserId())); - stringBuilder.append(user.getNickName()).append(","); + Long userId = Long.parseLong(identityLink.getUserId()); + String nickName = userService.selectNickNameById(userId); + stringBuilder.append(nickName).append(","); } if (StringUtils.isNotBlank(identityLink.getGroupId())) { if (identityLink.getGroupId().startsWith(TaskConstants.ROLE_GROUP_PREFIX)) { diff --git a/ruoyi-system/src/main/java/com/ruoyi/workflow/service/impl/WfTaskServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/workflow/service/impl/WfTaskServiceImpl.java index be5ffa307a4078396301b5206d493cfa51acc761..164546246adf9e3b1286c7151f5b7d5e51902c32 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/workflow/service/impl/WfTaskServiceImpl.java +++ b/ruoyi-system/src/main/java/com/ruoyi/workflow/service/impl/WfTaskServiceImpl.java @@ -4,7 +4,7 @@ import cn.hutool.core.collection.CollUtil; import cn.hutool.core.convert.Convert; import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.StrUtil; -import com.ruoyi.common.core.domain.entity.SysUser; +import com.ruoyi.common.core.service.UserService; import com.ruoyi.common.exception.ServiceException; import com.ruoyi.common.helper.LoginHelper; import com.ruoyi.common.utils.StringUtils; @@ -17,7 +17,6 @@ import com.ruoyi.flowable.flow.CustomProcessDiagramGenerator; import com.ruoyi.flowable.flow.FlowableUtils; import com.ruoyi.flowable.utils.ModelUtils; import com.ruoyi.flowable.utils.TaskUtils; -import com.ruoyi.system.service.ISysUserService; import com.ruoyi.workflow.domain.bo.WfTaskBo; import com.ruoyi.workflow.service.IWfCopyService; import com.ruoyi.workflow.service.IWfTaskService; @@ -56,7 +55,7 @@ import java.util.stream.Collectors; @Slf4j public class WfTaskServiceImpl extends FlowServiceFactory implements IWfTaskService { - private final ISysUserService sysUserService; + private final UserService sysUserService; private final IWfCopyService copyService; @@ -129,6 +128,8 @@ public class WfTaskServiceImpl extends FlowServiceFactory implements IWfTaskServ .processDefinitionId(task.getProcessDefinitionId()) .singleResult(); + // 设置处理人 + taskService.setAssignee(taskBo.getTaskId(), TaskUtils.getUserId()); // 添加审批意见 taskService.addComment(taskBo.getTaskId(), taskBo.getProcInsId(), FlowComment.REJECT.getType(), taskBo.getComment()); // 设置流程状态为已终结 @@ -311,9 +312,9 @@ public class WfTaskServiceImpl extends FlowServiceFactory implements IWfTaskServ } StringBuilder commentBuilder = new StringBuilder(LoginHelper.getNickName()) .append("->"); - SysUser user = sysUserService.selectUserById(Long.parseLong(bo.getUserId())); - if (ObjectUtil.isNotNull(user)) { - commentBuilder.append(user.getNickName()); + String nickName = sysUserService.selectNickNameById(Long.parseLong(bo.getUserId())); + if (StringUtils.isNotBlank(nickName)) { + commentBuilder.append(nickName); } else { commentBuilder.append(bo.getUserId()); } @@ -350,9 +351,9 @@ public class WfTaskServiceImpl extends FlowServiceFactory implements IWfTaskServ } StringBuilder commentBuilder = new StringBuilder(LoginHelper.getNickName()) .append("->"); - SysUser user = sysUserService.selectUserById(Long.parseLong(bo.getUserId())); - if (ObjectUtil.isNotNull(user)) { - commentBuilder.append(user.getNickName()); + String nickName = sysUserService.selectNickNameById(Long.parseLong(bo.getUserId())); + if (StringUtils.isNotBlank(nickName)) { + commentBuilder.append(nickName); } else { commentBuilder.append(bo.getUserId()); } diff --git a/ruoyi-system/src/main/resources/mapper/system/SysUserMapper.xml b/ruoyi-system/src/main/resources/mapper/system/SysUserMapper.xml index 06a653efe1b87e406fd15c114e01e76bff4c839b..e73e715db614d84e6dea4e4ddac6514a58c8ece1 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} + +