From 558ec6eb693354f175b94d888ee9ab41d0da8f1d Mon Sep 17 00:00:00 2001 From: konbai <1527468660@qq.com> Date: Thu, 2 Feb 2023 23:16:20 +0800 Subject: [PATCH] =?UTF-8?q?sync:=20=E5=90=8C=E6=AD=A5=20RuoYi-Vue-Plus?= =?UTF-8?q?=EF=BC=88v4.5.0=EF=BC=89=20=E6=9B=B4=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [重大更新] 使用 spring 事件发布机制 重构登录日志与操作日志 支持多事件监听无入侵扩展 --- .gitee/ISSUE_TEMPLATE.zh-CN.md | 38 ++- .gitee/PULL_REQUEST_TEMPLATE.zh-CN.md | 2 +- pom.xml | 119 ++++--- .../controller/common/CaptchaController.java | 28 +- .../controller/system/SysLoginController.java | 2 - .../src/main/resources/application.yml | 6 + .../main/resources/i18n/messages.properties | 2 +- .../resources/i18n/messages_en_US.properties | 2 +- .../resources/i18n/messages_zh_CN.properties | 2 +- ruoyi-common/pom.xml | 10 +- .../common/core/domain/dto/UserOnlineDTO.java | 1 - .../core/domain/event/LogininforEvent.java | 44 +++ .../OperLogEvent.java} | 6 +- .../common/core/mapper/BaseMapperPlus.java | 53 +--- .../core/service/LogininforService.java | 14 - .../common/core/service/OperLogService.java | 15 - .../ruoyi/common/excel/CellMergeStrategy.java | 2 +- .../filter/XssHttpServletRequestWrapper.java | 6 +- .../com/ruoyi/common/utils/poi/ExcelUtil.java | 164 +++++++--- .../ruoyi/common/utils/redis/RedisUtils.java | 26 +- .../common/utils/spring/SpringUtils.java | 9 + .../demo/controller/queue/PriorityDemo.java | 3 +- .../ruoyi/framework/aspectj/LogAspect.java | 13 +- .../ruoyi/framework/config/AsyncConfig.java | 2 +- .../ruoyi/framework/config/RedisConfig.java | 3 +- .../ruoyi/framework/config/SwaggerConfig.java | 6 +- .../handler/PlusDataPermissionHandler.java | 6 +- .../com/ruoyi/generator/config/GenConfig.java | 2 +- .../service/GenTableServiceImpl.java | 10 +- .../main/resources/vm/vue/index-tree.vue.vm | 33 +- .../src/main/resources/vm/vue/index.vue.vm | 38 ++- .../resources/vm/vue/v3/index-tree.vue.vm | 43 +-- .../src/main/resources/vm/vue/v3/index.vue.vm | 40 +-- ruoyi-job/pom.xml | 1 + .../ruoyi/system/service/SysLoginService.java | 79 +++-- .../system/service/SysRegisterService.java | 28 +- .../service/impl/SysConfigServiceImpl.java | 4 + .../impl/SysLogininforServiceImpl.java | 32 +- .../service/impl/SysOperLogServiceImpl.java | 14 +- .../service/impl/SysOssConfigServiceImpl.java | 34 +- ruoyi-ui/package.json | 2 +- ruoyi-ui/src/assets/styles/ruoyi.scss | 4 + .../src/components/HeaderSearch/index.vue | 1 - ruoyi-ui/src/components/RightPanel/index.vue | 6 - ruoyi-ui/src/plugins/download.js | 2 +- ruoyi-ui/src/utils/generator/html.js | 226 ++++++-------- ruoyi-ui/src/utils/generator/js.js | 189 +++++------- ruoyi-ui/src/views/dashboard/BarChart.vue | 2 +- ruoyi-ui/src/views/dashboard/LineChart.vue | 2 +- ruoyi-ui/src/views/dashboard/PieChart.vue | 2 +- ruoyi-ui/src/views/dashboard/RaddarChart.vue | 2 +- ruoyi-ui/src/views/monitor/cache/index.vue | 292 +++++++++--------- ruoyi-ui/src/views/register.vue | 2 +- script/docker/docker-compose.yml | 4 +- 54 files changed, 866 insertions(+), 812 deletions(-) create mode 100644 ruoyi-common/src/main/java/com/ruoyi/common/core/domain/event/LogininforEvent.java rename ruoyi-common/src/main/java/com/ruoyi/common/core/domain/{dto/OperLogDTO.java => event/OperLogEvent.java} (92%) delete mode 100644 ruoyi-common/src/main/java/com/ruoyi/common/core/service/LogininforService.java delete mode 100644 ruoyi-common/src/main/java/com/ruoyi/common/core/service/OperLogService.java diff --git a/.gitee/ISSUE_TEMPLATE.zh-CN.md b/.gitee/ISSUE_TEMPLATE.zh-CN.md index b6fcbea8..cf82237f 100644 --- a/.gitee/ISSUE_TEMPLATE.zh-CN.md +++ b/.gitee/ISSUE_TEMPLATE.zh-CN.md @@ -1,13 +1,49 @@ ### 使用版本(未按照模板填写直接删除) +- jdk版本(带上尾号): 例如 1.8.0_202 +- 框架版本(项目启动时输出的版本号): 例如 4.4.0 +- 其他依赖版本(你觉得有必要的): + +### 问题前提 + +> 功能不好用 不会用 是否已经看过项目文档 +> 项目运行报错 是否已经拿着报错信息去百度 常见报错百度百度足以 +> 是否搜索过其他issue 一些已经解决的问题 会在issue内留下解决方法 +> 无法线上解决或者与框架无关的问题的欢迎加VIP群跟作者一对一谈 + +### 异常模块 + +> 此报错都涉及到那些系统模块 + +例如 ruoyi-system ruoyi-auth 等等 ### 问题描述 +> 越详细越容易直击问题所在 + +已知: XXX功能不好用 或 XXX数据不正常 等等 ### 希望结果 +> 想知道你觉得怎么样是正常或者合理的 + +希望功能可以有XXX结果 或者 XXX现象 ### 重现步骤 +> 作者并不知道这个问题是如何出现的 -### 报错信息(截图为主 请勿发混乱格式) \ No newline at end of file +- 1 +- 2 +- 3 + +### 相关代码与报错信息(请勿发混乱格式) + +> 代码可按照如下形式提供或者截图均可 越详细越好 +> 大多数问题都是 代码编写错误问题 逻辑问题 或者用法错误等问题 + +```java +public class XXX { + +} +``` \ No newline at end of file diff --git a/.gitee/PULL_REQUEST_TEMPLATE.zh-CN.md b/.gitee/PULL_REQUEST_TEMPLATE.zh-CN.md index f5ef7c15..ef11b85b 100644 --- a/.gitee/PULL_REQUEST_TEMPLATE.zh-CN.md +++ b/.gitee/PULL_REQUEST_TEMPLATE.zh-CN.md @@ -1,7 +1,7 @@ ### 更改目的 解决了什么问题(请提交到dev分支) -### 描述 做了哪些改动 +### 改动逻辑 这么写的思路(让作者更好的理解你的意图) ### 测试 都做了哪些测试(未经过测试不采纳) \ No newline at end of file diff --git a/pom.xml b/pom.xml index 86f8eeb7..dc78fd2e 100644 --- a/pom.xml +++ b/pom.xml @@ -14,23 +14,23 @@ 0.8.0 - 2.7.6 + 2.7.7 UTF-8 UTF-8 1.8 3.2.2 2.2.2 - 1.6.13 + 1.6.14 5.2.3 - 3.1.3 + 3.1.5 2.3 - 1.33.0 - 3.5.2 + 1.34.0 + 3.5.3.1 3.9.1 - 5.8.10 + 5.8.11 4.10.0 - 2.7.7 - 3.18.0 + 2.7.10 + 3.19.1 2.2.3 3.5.2 2.14.2 @@ -38,18 +38,46 @@ 1.18.24 6.7.2 - - 31.1-jre - 1.32 + 1.33 - 1.12.349 + 1.12.373 - 2.0.22 - 3.1.635 + 2.0.23 + 3.1.660 + + + local + + + local + debug + + + + dev + + + dev + debug + + + + true + + + + prod + + prod + warn + + + + @@ -150,11 +178,7 @@ mybatis-plus-boot-starter ${mybatis-plus.version} - - com.baomidou - mybatis-plus-extension - ${mybatis-plus.version} - + p6spy @@ -202,7 +226,19 @@ org.redisson redisson-spring-boot-starter ${redisson.version} + + + org.redisson + redisson-spring-data-30 + + + + org.redisson + redisson-spring-data-27 + ${redisson.version} + + com.baomidou lock4j-redisson-spring-boot-starter @@ -222,13 +258,6 @@ ${alibaba-ttl.version} - - - com.google.guava - guava - ${guava.version} - - org.yaml @@ -390,8 +419,8 @@ public - huawei nexus - https://mirrors.huaweicloud.com/repository/maven/ + aliyun nexus + https://maven.aliyun.com/repository/public/ true @@ -401,8 +430,8 @@ public - huawei nexus - https://mirrors.huaweicloud.com/repository/maven/ + aliyun nexus + https://maven.aliyun.com/repository/public/ true @@ -412,34 +441,4 @@ - - - local - - - local - debug - - - - dev - - - dev - debug - - - - true - - - - prod - - prod - warn - - - - 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 94630e2e..795359c5 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 @@ -3,7 +3,6 @@ package com.ruoyi.web.controller.common; import cn.dev33.satoken.annotation.SaIgnore; import cn.hutool.captcha.AbstractCaptcha; import cn.hutool.captcha.generator.CodeGenerator; -import cn.hutool.core.convert.Convert; import cn.hutool.core.util.IdUtil; import cn.hutool.core.util.RandomUtil; import com.ruoyi.common.constant.CacheConstants; @@ -21,6 +20,9 @@ import com.ruoyi.sms.entity.SmsResult; import com.ruoyi.system.service.ISysConfigService; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.springframework.expression.Expression; +import org.springframework.expression.ExpressionParser; +import org.springframework.expression.spel.standard.SpelExpressionParser; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @@ -95,28 +97,16 @@ public class CaptchaController { AbstractCaptcha captcha = SpringUtils.getBean(captchaProperties.getCategory().getClazz()); captcha.setGenerator(codeGenerator); captcha.createCode(); - String code = isMath ? getCodeResult(captcha.getCode()) : captcha.getCode(); + String code = captcha.getCode(); + if (isMath) { + ExpressionParser parser = new SpelExpressionParser(); + Expression exp = parser.parseExpression(StringUtils.remove(code, "=")); + code = exp.getValue(String.class); + } RedisUtils.setCacheObject(verifyKey, code, Duration.ofMinutes(Constants.CAPTCHA_EXPIRATION)); ajax.put("uuid", uuid); ajax.put("img", captcha.getImageBase64()); return R.ok(ajax); } - private String getCodeResult(String capStr) { - int numberLength = captchaProperties.getNumberLength(); - int a = Convert.toInt(StringUtils.substring(capStr, 0, numberLength).trim()); - char operator = capStr.charAt(numberLength); - int b = Convert.toInt(StringUtils.substring(capStr, numberLength + 1, numberLength + 1 + numberLength).trim()); - switch (operator) { - case '*': - return Convert.toStr(a * b); - case '+': - return Convert.toStr(a + b); - case '-': - return Convert.toStr(a - b); - default: - return StringUtils.EMPTY; - } - } - } 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 a65cb625..78c44e30 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 @@ -13,7 +13,6 @@ import com.ruoyi.system.domain.vo.RouterVo; import com.ruoyi.system.service.ISysMenuService; import com.ruoyi.system.service.ISysUserService; import com.ruoyi.system.service.SysLoginService; -import com.ruoyi.system.service.SysPermissionService; import lombok.RequiredArgsConstructor; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.GetMapping; @@ -39,7 +38,6 @@ public class SysLoginController { private final SysLoginService loginService; private final ISysMenuService menuService; private final ISysUserService userService; - private final SysPermissionService permissionService; /** * 登录方法 diff --git a/ruoyi-admin/src/main/resources/application.yml b/ruoyi-admin/src/main/resources/application.yml index 176cb938..7e2724c3 100644 --- a/ruoyi-admin/src/main/resources/application.yml +++ b/ruoyi-admin/src/main/resources/application.yml @@ -83,6 +83,9 @@ spring: restart: # 热部署开关 enabled: true + mvc: + format: + date-time: yyyy-MM-dd HH:mm:ss jackson: # 日期格式化 date-format: yyyy-MM-dd HH:mm:ss @@ -202,6 +205,9 @@ swagger: name: ${sa-token.token-name} springdoc: + swagger-ui: + # 持久化认证数据 + persistAuthorization: true #这里定义了两个分组,可定义多个,也可以不定义 group-configs: - group: 1.演示模块 diff --git a/ruoyi-admin/src/main/resources/i18n/messages.properties b/ruoyi-admin/src/main/resources/i18n/messages.properties index e1423a8f..4ff55f41 100644 --- a/ruoyi-admin/src/main/resources/i18n/messages.properties +++ b/ruoyi-admin/src/main/resources/i18n/messages.properties @@ -41,5 +41,5 @@ repeat.submit.message=不允许重复提交,请稍候再试 rate.limiter.message=访问过于频繁,请稍候再试 sms.code.not.blank=短信验证码不能为空 sms.code.retry.limit.count=短信验证码输入错误{0}次 -sms.code.retry.limit.exceed=短信验证码输入错误{0}次,帐户锁定{0}分钟 +sms.code.retry.limit.exceed=短信验证码输入错误{0}次,帐户锁定{1}分钟 xcx.code.not.blank=小程序code不能为空 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 04a4aa37..c0faca93 100644 --- a/ruoyi-admin/src/main/resources/i18n/messages_en_US.properties +++ b/ruoyi-admin/src/main/resources/i18n/messages_en_US.properties @@ -41,5 +41,5 @@ repeat.submit.message=Repeat submit is not allowed, please try again later rate.limiter.message=Visit too frequently, please try again later sms.code.not.blank=Sms code cannot be blank sms.code.retry.limit.count=Sms code input error {0} times -sms.code.retry.limit.exceed=Sms code input error {0} times, account locked for {0} minutes +sms.code.retry.limit.exceed=Sms code input error {0} times, account locked for {1} minutes xcx.code.not.blank=Mini program code cannot be blank 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 e1423a8f..4ff55f41 100644 --- a/ruoyi-admin/src/main/resources/i18n/messages_zh_CN.properties +++ b/ruoyi-admin/src/main/resources/i18n/messages_zh_CN.properties @@ -41,5 +41,5 @@ repeat.submit.message=不允许重复提交,请稍候再试 rate.limiter.message=访问过于频繁,请稍候再试 sms.code.not.blank=短信验证码不能为空 sms.code.retry.limit.count=短信验证码输入错误{0}次 -sms.code.retry.limit.exceed=短信验证码输入错误{0}次,帐户锁定{0}分钟 +sms.code.retry.limit.exceed=短信验证码输入错误{0}次,帐户锁定{1}分钟 xcx.code.not.blank=小程序code不能为空 diff --git a/ruoyi-common/pom.xml b/ruoyi-common/pom.xml index e5bc00d4..a39f35a4 100644 --- a/ruoyi-common/pom.xml +++ b/ruoyi-common/pom.xml @@ -79,10 +79,6 @@ com.baomidou mybatis-plus-boot-starter - - com.baomidou - mybatis-plus-extension - @@ -147,6 +143,12 @@ redisson-spring-boot-starter + + org.redisson + redisson-spring-data-27 + ${redisson.version} + + com.baomidou lock4j-redisson-spring-boot-starter diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/dto/UserOnlineDTO.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/dto/UserOnlineDTO.java index 29ce1d1e..cdd3905a 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/dto/UserOnlineDTO.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/dto/UserOnlineDTO.java @@ -1,6 +1,5 @@ package com.ruoyi.common.core.domain.dto; -import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/event/LogininforEvent.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/event/LogininforEvent.java new file mode 100644 index 00000000..733f97f9 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/event/LogininforEvent.java @@ -0,0 +1,44 @@ +package com.ruoyi.common.core.domain.event; + +import lombok.Data; + +import javax.servlet.http.HttpServletRequest; +import java.io.Serializable; + +/** + * 登录事件 + * + * @author Lion Li + */ + +@Data +public class LogininforEvent implements Serializable { + + private static final long serialVersionUID = 1L; + + /** + * 用户账号 + */ + private String username; + + /** + * 登录状态 0成功 1失败 + */ + private String status; + + /** + * 提示消息 + */ + private String message; + + /** + * 请求体 + */ + private HttpServletRequest request; + + /** + * 其他参数 + */ + private Object[] args; + +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/dto/OperLogDTO.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/event/OperLogEvent.java similarity index 92% rename from ruoyi-common/src/main/java/com/ruoyi/common/core/domain/dto/OperLogDTO.java rename to ruoyi-common/src/main/java/com/ruoyi/common/core/domain/event/OperLogEvent.java index 26bf6744..3a299781 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/dto/OperLogDTO.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/event/OperLogEvent.java @@ -1,4 +1,4 @@ -package com.ruoyi.common.core.domain.dto; +package com.ruoyi.common.core.domain.event; import lombok.Data; @@ -6,13 +6,13 @@ import java.io.Serializable; import java.util.Date; /** - * 通用操作日志实体 + * 操作日志事件 * * @author Lion Li */ @Data -public class OperLogDTO implements Serializable { +public class OperLogEvent implements Serializable { private static final long serialVersionUID = 1L; diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/mapper/BaseMapperPlus.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/mapper/BaseMapperPlus.java index 862887d1..0a8539ad 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/core/mapper/BaseMapperPlus.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/mapper/BaseMapperPlus.java @@ -4,16 +4,12 @@ import cn.hutool.core.collection.CollUtil; import cn.hutool.core.util.ObjectUtil; import com.baomidou.mybatisplus.core.conditions.Wrapper; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; -import com.baomidou.mybatisplus.core.enums.SqlMethod; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.baomidou.mybatisplus.core.metadata.IPage; -import com.baomidou.mybatisplus.core.metadata.TableInfo; -import com.baomidou.mybatisplus.core.metadata.TableInfoHelper; import com.baomidou.mybatisplus.core.toolkit.*; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; -import com.baomidou.mybatisplus.extension.toolkit.SqlHelper; +import com.baomidou.mybatisplus.extension.toolkit.Db; import com.ruoyi.common.utils.BeanCopyUtils; -import org.apache.ibatis.binding.MapperMethod; import org.apache.ibatis.logging.Log; import org.apache.ibatis.logging.LogFactory; @@ -21,7 +17,6 @@ import java.io.Serializable; import java.util.Collection; import java.util.List; import java.util.Map; -import java.util.Objects; /** * 自定义 Mapper 接口, 实现 自定义扩展 @@ -37,8 +32,6 @@ public interface BaseMapperPlus extends BaseMapper { Log log = LogFactory.getLog(BaseMapperPlus.class); - int DEFAULT_BATCH_SIZE = 1000; - default Class currentVoClass() { return (Class) ReflectionKit.getSuperClassGenericType(this.getClass(), BaseMapperPlus.class, 2); } @@ -59,79 +52,49 @@ public interface BaseMapperPlus extends BaseMapper { * 批量插入 */ default boolean insertBatch(Collection entityList) { - return insertBatch(entityList, DEFAULT_BATCH_SIZE); + return Db.saveBatch(entityList); } /** * 批量更新 */ default boolean updateBatchById(Collection entityList) { - return updateBatchById(entityList, DEFAULT_BATCH_SIZE); + return Db.updateBatchById(entityList); } /** * 批量插入或更新 */ default boolean insertOrUpdateBatch(Collection entityList) { - return insertOrUpdateBatch(entityList, DEFAULT_BATCH_SIZE); + return Db.saveOrUpdateBatch(entityList); } /** * 批量插入(包含限制条数) */ default boolean insertBatch(Collection entityList, int batchSize) { - String sqlStatement = SqlHelper.getSqlStatement(this.currentMapperClass(), SqlMethod.INSERT_ONE); - return SqlHelper.executeBatch(this.currentModelClass(), log, entityList, batchSize, - (sqlSession, entity) -> sqlSession.insert(sqlStatement, entity)); + return Db.saveBatch(entityList, batchSize); } /** * 批量更新(包含限制条数) */ default boolean updateBatchById(Collection entityList, int batchSize) { - String sqlStatement = SqlHelper.getSqlStatement(this.currentMapperClass(), SqlMethod.UPDATE_BY_ID); - return SqlHelper.executeBatch(this.currentModelClass(), log, entityList, batchSize, - (sqlSession, entity) -> { - MapperMethod.ParamMap param = new MapperMethod.ParamMap<>(); - param.put(Constants.ENTITY, entity); - sqlSession.update(sqlStatement, param); - }); + return Db.updateBatchById(entityList, batchSize); } /** * 批量插入或更新(包含限制条数) */ default boolean insertOrUpdateBatch(Collection entityList, int batchSize) { - TableInfo tableInfo = TableInfoHelper.getTableInfo(this.currentModelClass()); - Assert.notNull(tableInfo, "error: can not execute. because can not find cache of TableInfo for entity!"); - String keyProperty = tableInfo.getKeyProperty(); - Assert.notEmpty(keyProperty, "error: can not execute. because can not find column for id from entity!"); - return SqlHelper.saveOrUpdateBatch(this.currentModelClass(), this.currentMapperClass(), log, entityList, batchSize, (sqlSession, entity) -> { - Object idVal = tableInfo.getPropertyValue(entity, keyProperty); - String sqlStatement = SqlHelper.getSqlStatement(this.currentMapperClass(), SqlMethod.SELECT_BY_ID); - return StringUtils.checkValNull(idVal) - || CollectionUtils.isEmpty(sqlSession.selectList(sqlStatement, entity)); - }, (sqlSession, entity) -> { - MapperMethod.ParamMap param = new MapperMethod.ParamMap<>(); - param.put(Constants.ENTITY, entity); - String sqlStatement = SqlHelper.getSqlStatement(this.currentMapperClass(), SqlMethod.UPDATE_BY_ID); - sqlSession.update(sqlStatement, param); - }); + return Db.saveOrUpdateBatch(entityList, batchSize); } /** * 插入或更新(包含限制条数) */ default boolean insertOrUpdate(T entity) { - if (null != entity) { - TableInfo tableInfo = TableInfoHelper.getTableInfo(this.currentModelClass()); - Assert.notNull(tableInfo, "error: can not execute. because can not find cache of TableInfo for entity!"); - String keyProperty = tableInfo.getKeyProperty(); - Assert.notEmpty(keyProperty, "error: can not execute. because can not find column for id from entity!"); - Object idVal = tableInfo.getPropertyValue(entity, tableInfo.getKeyProperty()); - return StringUtils.checkValNull(idVal) || Objects.isNull(selectById((Serializable) idVal)) ? insert(entity) > 0 : updateById(entity) > 0; - } - return false; + return Db.saveOrUpdate(entity); } default V selectVoById(Serializable id) { diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/service/LogininforService.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/service/LogininforService.java deleted file mode 100644 index 91aee436..00000000 --- a/ruoyi-common/src/main/java/com/ruoyi/common/core/service/LogininforService.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.ruoyi.common.core.service; - -import javax.servlet.http.HttpServletRequest; - -/** - * 通用 系统访问日志 - * - * @author Lion Li - */ -public interface LogininforService { - - void recordLogininfor(String username, String status, String message, - HttpServletRequest request, Object... args); -} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/service/OperLogService.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/service/OperLogService.java deleted file mode 100644 index a3b27e6a..00000000 --- a/ruoyi-common/src/main/java/com/ruoyi/common/core/service/OperLogService.java +++ /dev/null @@ -1,15 +0,0 @@ -package com.ruoyi.common.core.service; - -import com.ruoyi.common.core.domain.dto.OperLogDTO; -import org.springframework.scheduling.annotation.Async; - -/** - * 通用 操作日志 - * - * @author Lion Li - */ -public interface OperLogService { - - @Async - void recordOper(OperLogDTO operLogDTO); -} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/excel/CellMergeStrategy.java b/ruoyi-common/src/main/java/com/ruoyi/common/excel/CellMergeStrategy.java index 04a1bbb8..6102eec5 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/excel/CellMergeStrategy.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/excel/CellMergeStrategy.java @@ -86,7 +86,7 @@ public class CellMergeStrategy extends AbstractMergeStrategy { // 空值跳过不合并 continue; } - if (cellValue != val) { + if (!cellValue.equals(val)) { if (i - repeatCell.getCurrent() > 1) { cellList.add(new CellRangeAddress(repeatCell.getCurrent() + rowIndex, i + rowIndex - 1, colNum, colNum)); } diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/filter/XssHttpServletRequestWrapper.java b/ruoyi-common/src/main/java/com/ruoyi/common/filter/XssHttpServletRequestWrapper.java index 3646f1fe..a7fbd4ca 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/filter/XssHttpServletRequestWrapper.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/filter/XssHttpServletRequestWrapper.java @@ -33,12 +33,12 @@ public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper { String[] values = super.getParameterValues(name); if (values != null) { int length = values.length; - String[] escapseValues = new String[length]; + String[] escapesValues = new String[length]; for (int i = 0; i < length; i++) { // 防xss攻击和过滤前后空格 - escapseValues[i] = HtmlUtil.cleanHtmlTag(values[i]).trim(); + escapesValues[i] = HtmlUtil.cleanHtmlTag(values[i]).trim(); } - return escapseValues; + return escapesValues; } return super.getParameterValues(name); } diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/poi/ExcelUtil.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/poi/ExcelUtil.java index 3ff8fec4..211c6214 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/utils/poi/ExcelUtil.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/poi/ExcelUtil.java @@ -24,6 +24,7 @@ import javax.servlet.ServletOutputStream; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.InputStream; +import java.io.OutputStream; import java.io.UnsupportedEncodingException; import java.util.Collection; import java.util.List; @@ -84,7 +85,13 @@ public class ExcelUtil { * @param response 响应体 */ public static void exportExcel(List list, String sheetName, Class clazz, HttpServletResponse response) { - exportExcel(list, sheetName, clazz, false, response); + try { + resetResponse(sheetName, response); + ServletOutputStream os = response.getOutputStream(); + exportExcel(list, sheetName, clazz, false, os); + } catch (IOException e) { + throw new RuntimeException("导出Excel异常"); + } } /** @@ -100,23 +107,48 @@ public class ExcelUtil { try { resetResponse(sheetName, response); ServletOutputStream os = response.getOutputStream(); - ExcelWriterSheetBuilder builder = EasyExcel.write(os, clazz) - .autoCloseStream(false) - // 自动适配 - .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy()) - // 大数值自动转换 防止失真 - .registerConverter(new ExcelBigNumberConvert()) - .sheet(sheetName); - if (merge) { - // 合并处理器 - builder.registerWriteHandler(new CellMergeStrategy(list, true)); - } - builder.doWrite(list); + exportExcel(list, sheetName, clazz, merge, os); } catch (IOException e) { throw new RuntimeException("导出Excel异常"); } } + /** + * 导出excel + * + * @param list 导出数据集合 + * @param sheetName 工作表的名称 + * @param clazz 实体类 + * @param os 输出流 + */ + public static void exportExcel(List list, String sheetName, Class clazz, OutputStream os) { + exportExcel(list, sheetName, clazz, false, os); + } + + /** + * 导出excel + * + * @param list 导出数据集合 + * @param sheetName 工作表的名称 + * @param clazz 实体类 + * @param merge 是否合并单元格 + * @param os 输出流 + */ + public static void exportExcel(List list, String sheetName, Class clazz, boolean merge, OutputStream os) { + ExcelWriterSheetBuilder builder = EasyExcel.write(os, clazz) + .autoCloseStream(false) + // 自动适配 + .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy()) + // 大数值自动转换 防止失真 + .registerConverter(new ExcelBigNumberConvert()) + .sheet(sheetName); + if (merge) { + // 合并处理器 + builder.registerWriteHandler(new CellMergeStrategy(list, true)); + } + builder.doWrite(list); + } + /** * 单表多数据模板导出 模板格式为 {.属性} * @@ -125,31 +157,46 @@ public class ExcelUtil { * 例如: excel/temp.xlsx * 重点: 模板文件必须放置到启动类对应的 resource 目录下 * @param data 模板需要的数据 + * @param response 响应体 */ public static void exportTemplate(List data, String filename, String templatePath, HttpServletResponse response) { try { resetResponse(filename, response); - ClassPathResource templateResource = new ClassPathResource(templatePath); - ExcelWriter excelWriter = EasyExcel.write(response.getOutputStream()) - .withTemplate(templateResource.getStream()) - .autoCloseStream(false) - // 大数值自动转换 防止失真 - .registerConverter(new ExcelBigNumberConvert()) - .build(); - WriteSheet writeSheet = EasyExcel.writerSheet().build(); - if (CollUtil.isEmpty(data)) { - throw new IllegalArgumentException("数据为空"); - } - // 单表多数据导出 模板格式为 {.属性} - for (Object d : data) { - excelWriter.fill(d, writeSheet); - } - excelWriter.finish(); + ServletOutputStream os = response.getOutputStream(); + exportTemplate(data, templatePath, os); } catch (IOException e) { throw new RuntimeException("导出Excel异常"); } } + /** + * 单表多数据模板导出 模板格式为 {.属性} + * + * @param templatePath 模板路径 resource 目录下的路径包括模板文件名 + * 例如: excel/temp.xlsx + * 重点: 模板文件必须放置到启动类对应的 resource 目录下 + * @param data 模板需要的数据 + * @param os 输出流 + */ + public static void exportTemplate(List data, String templatePath, OutputStream os) { + ClassPathResource templateResource = new ClassPathResource(templatePath); + ExcelWriter excelWriter = EasyExcel.write(os) + .withTemplate(templateResource.getStream()) + .autoCloseStream(false) + // 大数值自动转换 防止失真 + .registerConverter(new ExcelBigNumberConvert()) + .build(); + WriteSheet writeSheet = EasyExcel.writerSheet().build(); + if (CollUtil.isEmpty(data)) { + throw new IllegalArgumentException("数据为空"); + } + // 单表多数据导出 模板格式为 {.属性} + for (Object d : data) { + excelWriter.fill(d, writeSheet); + } + excelWriter.finish(); + } + /** * 多表多数据模板导出 模板格式为 {key.属性} * @@ -158,37 +205,52 @@ public class ExcelUtil { * 例如: excel/temp.xlsx * 重点: 模板文件必须放置到启动类对应的 resource 目录下 * @param data 模板需要的数据 + * @param response 响应体 */ public static void exportTemplateMultiList(Map data, String filename, String templatePath, HttpServletResponse response) { try { resetResponse(filename, response); - ClassPathResource templateResource = new ClassPathResource(templatePath); - ExcelWriter excelWriter = EasyExcel.write(response.getOutputStream()) - .withTemplate(templateResource.getStream()) - .autoCloseStream(false) - // 大数值自动转换 防止失真 - .registerConverter(new ExcelBigNumberConvert()) - .build(); - WriteSheet writeSheet = EasyExcel.writerSheet().build(); - if (CollUtil.isEmpty(data)) { - throw new IllegalArgumentException("数据为空"); - } - for (Map.Entry map : data.entrySet()) { - // 设置列表后续还有数据 - FillConfig fillConfig = FillConfig.builder().forceNewRow(Boolean.TRUE).build(); - if (map.getValue() instanceof Collection) { - // 多表导出必须使用 FillWrapper - excelWriter.fill(new FillWrapper(map.getKey(), (Collection) map.getValue()), fillConfig, writeSheet); - } else { - excelWriter.fill(map.getValue(), writeSheet); - } - } - excelWriter.finish(); + ServletOutputStream os = response.getOutputStream(); + exportTemplateMultiList(data, templatePath, os); } catch (IOException e) { throw new RuntimeException("导出Excel异常"); } } + /** + * 多表多数据模板导出 模板格式为 {key.属性} + * + * @param templatePath 模板路径 resource 目录下的路径包括模板文件名 + * 例如: excel/temp.xlsx + * 重点: 模板文件必须放置到启动类对应的 resource 目录下 + * @param data 模板需要的数据 + * @param os 输出流 + */ + public static void exportTemplateMultiList(Map data, String templatePath, OutputStream os) { + ClassPathResource templateResource = new ClassPathResource(templatePath); + ExcelWriter excelWriter = EasyExcel.write(os) + .withTemplate(templateResource.getStream()) + .autoCloseStream(false) + // 大数值自动转换 防止失真 + .registerConverter(new ExcelBigNumberConvert()) + .build(); + WriteSheet writeSheet = EasyExcel.writerSheet().build(); + if (CollUtil.isEmpty(data)) { + throw new IllegalArgumentException("数据为空"); + } + for (Map.Entry map : data.entrySet()) { + // 设置列表后续还有数据 + FillConfig fillConfig = FillConfig.builder().forceNewRow(Boolean.TRUE).build(); + if (map.getValue() instanceof Collection) { + // 多表导出必须使用 FillWrapper + excelWriter.fill(new FillWrapper(map.getKey(), (Collection) map.getValue()), fillConfig, writeSheet); + } else { + excelWriter.fill(map.getValue(), writeSheet); + } + } + excelWriter.finish(); + } + /** * 重置响应体 */ diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/redis/RedisUtils.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/redis/RedisUtils.java index 80f5f528..16238089 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/utils/redis/RedisUtils.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/redis/RedisUtils.java @@ -4,7 +4,6 @@ import com.ruoyi.common.utils.spring.SpringUtils; import lombok.AccessLevel; import lombok.NoArgsConstructor; import org.redisson.api.*; -import org.redisson.config.Config; import java.time.Duration; import java.util.Collection; @@ -27,14 +26,6 @@ public class RedisUtils { private static final RedissonClient CLIENT = SpringUtils.getBean(RedissonClient.class); - public static NameMapper getNameMapper() { - Config config = CLIENT.getConfig(); - if (config.isClusterConfig()) { - return config.useClusterServers().getNameMapper(); - } - return config.useSingleServer().getNameMapper(); - } - /** * 限流 * @@ -218,6 +209,15 @@ public class RedisUtils { batch.execute(); } + /** + * 检查缓存对象是否存在 + * + * @param key 缓存的键值 + */ + public static boolean isExistsObject(final String key) { + return CLIENT.getBucket(key).isExists(); + } + /** * 缓存List数据 * @@ -437,8 +437,8 @@ public class RedisUtils { * @return 对象列表 */ public static Collection keys(final String pattern) { - Stream stream = CLIENT.getKeys().getKeysStreamByPattern(getNameMapper().map(pattern)); - return stream.map(key -> getNameMapper().unmap(key)).collect(Collectors.toList()); + Stream stream = CLIENT.getKeys().getKeysStreamByPattern(pattern); + return stream.collect(Collectors.toList()); } /** @@ -447,7 +447,7 @@ public class RedisUtils { * @param pattern 字符串前缀 */ public static void deleteKeys(final String pattern) { - CLIENT.getKeys().deleteByPattern(getNameMapper().map(pattern)); + CLIENT.getKeys().deleteByPattern(pattern); } /** @@ -457,6 +457,6 @@ public class RedisUtils { */ public static Boolean hasKey(String key) { RKeys rKeys = CLIENT.getKeys(); - return rKeys.countExists(getNameMapper().map(key)) > 0; + return rKeys.countExists(key) > 0; } } diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/spring/SpringUtils.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/spring/SpringUtils.java index ebc6d27f..8b37e416 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/utils/spring/SpringUtils.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/spring/SpringUtils.java @@ -3,6 +3,7 @@ package com.ruoyi.common.utils.spring; import cn.hutool.extra.spring.SpringUtil; import org.springframework.aop.framework.AopContext; import org.springframework.beans.factory.NoSuchBeanDefinitionException; +import org.springframework.context.ApplicationContext; import org.springframework.stereotype.Component; /** @@ -62,4 +63,12 @@ public final class SpringUtils extends SpringUtil { return (T) AopContext.currentProxy(); } + + /** + * 获取spring上下文 + */ + public static ApplicationContext context() { + return getApplicationContext(); + } + } diff --git a/ruoyi-demo/src/main/java/com/ruoyi/demo/controller/queue/PriorityDemo.java b/ruoyi-demo/src/main/java/com/ruoyi/demo/controller/queue/PriorityDemo.java index 320a640d..c86c92aa 100644 --- a/ruoyi-demo/src/main/java/com/ruoyi/demo/controller/queue/PriorityDemo.java +++ b/ruoyi-demo/src/main/java/com/ruoyi/demo/controller/queue/PriorityDemo.java @@ -2,7 +2,6 @@ package com.ruoyi.demo.controller.queue; import lombok.Data; import lombok.NoArgsConstructor; -import org.jetbrains.annotations.NotNull; /** * 实体类 注意不允许使用内部类 否则会找不到类 @@ -17,7 +16,7 @@ public class PriorityDemo implements Comparable { private Integer orderNum; @Override - public int compareTo(@NotNull PriorityDemo other) { + public int compareTo(PriorityDemo other) { return Integer.compare(getOrderNum(), other.getOrderNum()); } } diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/LogAspect.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/LogAspect.java index 3b6a858a..64ab736a 100644 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/LogAspect.java +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/LogAspect.java @@ -4,8 +4,7 @@ import cn.hutool.core.lang.Dict; import cn.hutool.core.map.MapUtil; import cn.hutool.core.util.ObjectUtil; import com.ruoyi.common.annotation.Log; -import com.ruoyi.common.core.domain.dto.OperLogDTO; -import com.ruoyi.common.core.service.OperLogService; +import com.ruoyi.common.core.domain.event.OperLogEvent; import com.ruoyi.common.enums.BusinessStatus; import com.ruoyi.common.enums.HttpMethod; import com.ruoyi.common.helper.LoginHelper; @@ -67,7 +66,7 @@ public class LogAspect { try { // *========数据库日志=========*// - OperLogDTO operLog = new OperLogDTO(); + OperLogEvent operLog = new OperLogEvent(); operLog.setStatus(BusinessStatus.SUCCESS.ordinal()); // 请求的地址 String ip = ServletUtils.getClientIP(); @@ -87,8 +86,8 @@ public class LogAspect { operLog.setRequestMethod(ServletUtils.getRequest().getMethod()); // 处理设置注解上的参数 getControllerMethodDescription(joinPoint, controllerLog, operLog, jsonResult); - // 保存数据库 - SpringUtils.getBean(OperLogService.class).recordOper(operLog); + // 发布事件保存数据库 + SpringUtils.context().publishEvent(operLog); } catch (Exception exp) { // 记录本地异常日志 log.error("异常信息:{}", exp.getMessage()); @@ -103,7 +102,7 @@ public class LogAspect { * @param operLog 操作日志 * @throws Exception */ - public void getControllerMethodDescription(JoinPoint joinPoint, Log log, OperLogDTO operLog, Object jsonResult) throws Exception { + public void getControllerMethodDescription(JoinPoint joinPoint, Log log, OperLogEvent operLog, Object jsonResult) throws Exception { // 设置action动作 operLog.setBusinessType(log.businessType().ordinal()); // 设置标题 @@ -127,7 +126,7 @@ public class LogAspect { * @param operLog 操作日志 * @throws Exception 异常 */ - private void setRequestValue(JoinPoint joinPoint, OperLogDTO operLog) throws Exception { + private void setRequestValue(JoinPoint joinPoint, OperLogEvent operLog) throws Exception { String requestMethod = operLog.getRequestMethod(); if (HttpMethod.PUT.name().equals(requestMethod) || HttpMethod.POST.name().equals(requestMethod)) { String params = argsArrayToString(joinPoint.getArgs()); diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/AsyncConfig.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/AsyncConfig.java index 47898e3f..d011b583 100644 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/AsyncConfig.java +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/AsyncConfig.java @@ -18,7 +18,7 @@ import java.util.concurrent.ScheduledExecutorService; * * @author Lion Li */ -@EnableAsync +@EnableAsync(proxyTargetClass = true) @Configuration public class AsyncConfig extends AsyncConfigurerSupport { diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/RedisConfig.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/RedisConfig.java index 9a7938a4..8016ab86 100644 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/RedisConfig.java +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/RedisConfig.java @@ -11,7 +11,6 @@ import org.redisson.spring.starter.RedissonAutoConfigurationCustomizer; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.cache.CacheManager; -import org.springframework.cache.annotation.CachingConfigurerSupport; import org.springframework.cache.annotation.EnableCaching; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -25,7 +24,7 @@ import org.springframework.context.annotation.Configuration; @Configuration @EnableCaching @EnableConfigurationProperties(RedissonProperties.class) -public class RedisConfig extends CachingConfigurerSupport { +public class RedisConfig { @Autowired private RedissonProperties redissonProperties; diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/SwaggerConfig.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/SwaggerConfig.java index 7107661c..ab92cf59 100644 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/SwaggerConfig.java +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/SwaggerConfig.java @@ -23,6 +23,7 @@ import org.springframework.context.annotation.Configuration; import java.util.ArrayList; import java.util.List; import java.util.Optional; +import java.util.Set; /** * Swagger 文档配置 @@ -51,8 +52,11 @@ public class SwaggerConfig { openApi.tags(swaggerProperties.getTags()); openApi.paths(swaggerProperties.getPaths()); openApi.components(swaggerProperties.getComponents()); + Set keySet = swaggerProperties.getComponents().getSecuritySchemes().keySet(); List list = new ArrayList<>(); - list.add(new SecurityRequirement().addList("apikey")); + SecurityRequirement securityRequirement = new SecurityRequirement(); + keySet.forEach(securityRequirement::addList); + list.add(securityRequirement); openApi.security(list); return openApi; diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/handler/PlusDataPermissionHandler.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/handler/PlusDataPermissionHandler.java index 63e777de..3825a0ba 100644 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/handler/PlusDataPermissionHandler.java +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/handler/PlusDataPermissionHandler.java @@ -54,7 +54,7 @@ public class PlusDataPermissionHandler { /** * 无效注解方法缓存用于快速返回 */ - private final Set inavlidCacheSet = new ConcurrentHashSet<>(); + private final Set invalidCacheSet = new ConcurrentHashSet<>(); /** * spel 解析器 @@ -70,7 +70,7 @@ public class PlusDataPermissionHandler { public Expression getSqlSegment(Expression where, String mappedStatementId, boolean isSelect) { DataColumn[] dataColumns = findAnnotation(mappedStatementId); if (ArrayUtil.isEmpty(dataColumns)) { - inavlidCacheSet.add(mappedStatementId); + invalidCacheSet.add(mappedStatementId); return where; } LoginUser currentUser = DataPermissionHelper.getVariable("user"); @@ -194,6 +194,6 @@ public class PlusDataPermissionHandler { * 是否为无效方法 无数据权限 */ public boolean isInvalid(String mappedStatementId) { - return inavlidCacheSet.contains(mappedStatementId); + return invalidCacheSet.contains(mappedStatementId); } } diff --git a/ruoyi-generator/src/main/java/com/ruoyi/generator/config/GenConfig.java b/ruoyi-generator/src/main/java/com/ruoyi/generator/config/GenConfig.java index 92c522f0..187426f7 100644 --- a/ruoyi-generator/src/main/java/com/ruoyi/generator/config/GenConfig.java +++ b/ruoyi-generator/src/main/java/com/ruoyi/generator/config/GenConfig.java @@ -12,7 +12,7 @@ import org.springframework.stereotype.Component; */ @Component @ConfigurationProperties(prefix = "gen") -@PropertySource(value = {"classpath:generator.yml"}) +@PropertySource(value = {"classpath:generator.yml"}, encoding = "UTF-8") public class GenConfig { /** 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 7a80229b..085589fb 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 @@ -3,12 +3,11 @@ package com.ruoyi.generator.service; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.io.IoUtil; import cn.hutool.core.lang.Dict; -import cn.hutool.core.lang.Snowflake; -import cn.hutool.core.util.IdUtil; import cn.hutool.core.util.ObjectUtil; import com.baomidou.dynamic.datasource.annotation.DS; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.incrementer.IdentifierGenerator; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.ruoyi.common.constant.Constants; @@ -58,6 +57,7 @@ public class GenTableServiceImpl implements IGenTableService { private final GenTableMapper baseMapper; private final GenTableColumnMapper genTableColumnMapper; + private final IdentifierGenerator identifierGenerator; /** * 查询业务字段列表 @@ -205,10 +205,9 @@ public class GenTableServiceImpl implements IGenTableService { Map dataMap = new LinkedHashMap<>(); // 查询表信息 GenTable table = baseMapper.selectGenTableById(tableId); - Snowflake snowflake = IdUtil.getSnowflake(); List menuIds = new ArrayList<>(); for (int i = 0; i < 6; i++) { - menuIds.add(snowflake.nextId()); + menuIds.add(identifierGenerator.nextId(null).longValue()); } table.setMenuIds(menuIds); // 设置主子表信息 @@ -356,10 +355,9 @@ public class GenTableServiceImpl implements IGenTableService { private void generatorCode(String tableName, ZipOutputStream zip) { // 查询表信息 GenTable table = baseMapper.selectGenTableByName(tableName); - Snowflake snowflake = IdUtil.getSnowflake(); List menuIds = new ArrayList<>(); for (int i = 0; i < 6; i++) { - menuIds.add(snowflake.nextId()); + menuIds.add(identifierGenerator.nextId(null).longValue()); } table.setMenuIds(menuIds); // 设置主子表信息 diff --git a/ruoyi-generator/src/main/resources/vm/vue/index-tree.vue.vm b/ruoyi-generator/src/main/resources/vm/vue/index-tree.vue.vm index 4ad96df3..2bc699a2 100644 --- a/ruoyi-generator/src/main/resources/vm/vue/index-tree.vue.vm +++ b/ruoyi-generator/src/main/resources/vm/vue/index-tree.vue.vm @@ -186,11 +186,11 @@ #elseif($column.htmlType == "imageUpload") - + #elseif($column.htmlType == "fileUpload") - + #elseif($column.htmlType == "editor") @@ -204,8 +204,11 @@ v-for="dict in dict.type.${dictType}" :key="dict.value" :label="dict.label" - #if($column.javaType == "Integer" || $column.javaType == "Long"):value="parseInt(dict.value)"#else:value="dict.value"#end - +#if($column.javaType == "Integer" || $column.javaType == "Long") + :value="parseInt(dict.value)" +#else + :value="dict.value" +#end > @@ -216,7 +219,7 @@ #elseif($column.htmlType == "checkbox" && "" != $dictType) - + #elseif($column.htmlType == "checkbox" && $dictType) - + 请选择字典生成 #elseif($column.htmlType == "radio" && "" != $dictType) - + {{dict.label}} #elseif($column.htmlType == "radio" && $dictType) - + 请选择字典生成 @@ -335,7 +341,7 @@ export default { #set($comment=$column.columnComment) #end $column.javaField: [ - { required: true, message: "$comment不能为空", trigger: #if($column.htmlType == "select")"change"#else"blur"#end } + { required: true, message: "$comment不能为空", trigger: #if($column.htmlType == "select" || $column.htmlType == "radio")"change"#else"blur"#end } ]#if($foreach.count != $columns.size()),#end #end #end @@ -398,10 +404,7 @@ export default { reset() { this.form = { #foreach ($column in $columns) -#if($column.htmlType == "radio") - $column.javaField: #if($column.javaType == "Integer" || $column.javaType == "Long")0#else"0"#end#if($foreach.count != $columns.size()),#end - -#elseif($column.htmlType == "checkbox") +#if($column.htmlType == "checkbox") $column.javaField: []#if($foreach.count != $columns.size()),#end #else $column.javaField: null#if($foreach.count != $columns.size()),#end diff --git a/ruoyi-generator/src/main/resources/vm/vue/index.vue.vm b/ruoyi-generator/src/main/resources/vm/vue/index.vue.vm index 7a57c7fd..e645b6bb 100644 --- a/ruoyi-generator/src/main/resources/vm/vue/index.vue.vm +++ b/ruoyi-generator/src/main/resources/vm/vue/index.vue.vm @@ -133,9 +133,9 @@ #elseif($column.list && $column.htmlType == "imageUpload") - + #elseif($column.list && $column.dictType && "" != $column.dictType) @@ -197,11 +197,11 @@ #elseif($column.htmlType == "imageUpload") - + #elseif($column.htmlType == "fileUpload") - + #elseif($column.htmlType == "editor") @@ -215,8 +215,11 @@ v-for="dict in dict.type.${dictType}" :key="dict.value" :label="dict.label" - #if($column.javaType == "Integer" || $column.javaType == "Long"):value="parseInt(dict.value)"#else:value="dict.value"#end - +#if($column.javaType == "Integer" || $column.javaType == "Long") + :value="parseInt(dict.value)" +#else + :value="dict.value" +#end > @@ -227,7 +230,7 @@ #elseif($column.htmlType == "checkbox" && "" != $dictType) - + #elseif($column.htmlType == "checkbox" && $dictType) - + 请选择字典生成 #elseif($column.htmlType == "radio" && "" != $dictType) - + {{dict.label}} #elseif($column.htmlType == "radio" && $dictType) - + 请选择字典生成 @@ -412,7 +418,7 @@ export default { #set($comment=$column.columnComment) #end $column.javaField: [ - { required: true, message: "$comment不能为空", trigger: #if($column.htmlType == "select")"change"#else"blur"#end } + { required: true, message: "$comment不能为空", trigger: #if($column.htmlType == "select" || $column.htmlType == "radio")"change"#else"blur"#end } ]#if($foreach.count != $columns.size()),#end #end #end @@ -456,9 +462,7 @@ export default { reset() { this.form = { #foreach ($column in $columns) -#if($column.htmlType == "radio") - $column.javaField: #if($column.javaType == "Integer" || $column.javaType == "Long")0#else"0"#end#if($foreach.count != $columns.size()),#end -#elseif($column.htmlType == "checkbox") +#if($column.htmlType == "checkbox") $column.javaField: []#if($foreach.count != $columns.size()),#end #else $column.javaField: undefined#if($foreach.count != $columns.size()),#end diff --git a/ruoyi-generator/src/main/resources/vm/vue/v3/index-tree.vue.vm b/ruoyi-generator/src/main/resources/vm/vue/v3/index-tree.vue.vm index 01a7367f..663893a2 100644 --- a/ruoyi-generator/src/main/resources/vm/vue/v3/index-tree.vue.vm +++ b/ruoyi-generator/src/main/resources/vm/vue/v3/index-tree.vue.vm @@ -135,11 +135,11 @@ #end #end - + @@ -172,11 +172,11 @@ #elseif($column.htmlType == "imageUpload") - + #elseif($column.htmlType == "fileUpload") - + #elseif($column.htmlType == "editor") @@ -190,8 +190,11 @@ v-for="dict in ${dictType}" :key="dict.value" :label="dict.label" - #if($column.javaType == "Integer" || $column.javaType == "Long"):value="parseInt(dict.value)"#else:value="dict.value"#end - +#if($column.javaType == "Integer" || $column.javaType == "Long") + :value="parseInt(dict.value)" +#else + :value="dict.value" +#end > @@ -202,7 +205,7 @@ #elseif($column.htmlType == "checkbox" && "" != $dictType) - + #elseif($column.htmlType == "checkbox" && $dictType) - + 请选择字典生成 #elseif($column.htmlType == "radio" && "" != $dictType) - + {{dict.label}} #elseif($column.htmlType == "radio" && $dictType) - + 请选择字典生成 @@ -306,7 +312,7 @@ const data = reactive({ #set($comment=$column.columnComment) #end $column.javaField: [ - { required: true, message: "$comment不能为空", trigger: #if($column.htmlType == "select")"change"#else"blur"#end } + { required: true, message: "$comment不能为空", trigger: #if($column.htmlType == "select" || $column.htmlType == "radio")"change"#else"blur"#end } ]#if($foreach.count != $columns.size()),#end #end #end @@ -359,10 +365,7 @@ function cancel() { function reset() { form.value = { #foreach ($column in $columns) -#if($column.htmlType == "radio") - $column.javaField: #if($column.javaType == "Integer" || $column.javaType == "Long")0#else"0"#end#if($foreach.count != $columns.size()),#end - -#elseif($column.htmlType == "checkbox") +#if($column.htmlType == "checkbox") $column.javaField: []#if($foreach.count != $columns.size()),#end #else $column.javaField: null#if($foreach.count != $columns.size()),#end diff --git a/ruoyi-generator/src/main/resources/vm/vue/v3/index.vue.vm b/ruoyi-generator/src/main/resources/vm/vue/v3/index.vue.vm index d0f4f8bc..a93922ef 100644 --- a/ruoyi-generator/src/main/resources/vm/vue/v3/index.vue.vm +++ b/ruoyi-generator/src/main/resources/vm/vue/v3/index.vue.vm @@ -147,10 +147,10 @@ #end #end - + @@ -180,11 +180,11 @@ #elseif($column.htmlType == "imageUpload") - + #elseif($column.htmlType == "fileUpload") - + #elseif($column.htmlType == "editor") @@ -198,8 +198,11 @@ v-for="dict in ${dictType}" :key="dict.value" :label="dict.label" - #if($column.javaType == "Integer" || $column.javaType == "Long"):value="parseInt(dict.value)"#else:value="dict.value"#end - +#if($column.javaType == "Integer" || $column.javaType == "Long") + :value="parseInt(dict.value)" +#else + :value="dict.value" +#end > @@ -210,7 +213,7 @@ #elseif($column.htmlType == "checkbox" && "" != $dictType) - + #elseif($column.htmlType == "checkbox" && $dictType) - + 请选择字典生成 #elseif($column.htmlType == "radio" && "" != $dictType) - + {{dict.label}} #elseif($column.htmlType == "radio" && $dictType) - + 请选择字典生成 @@ -387,7 +393,7 @@ const data = reactive({ #set($comment=$column.columnComment) #end $column.javaField: [ - { required: true, message: "$comment不能为空", trigger: #if($column.htmlType == "select")"change"#else"blur"#end } + { required: true, message: "$comment不能为空", trigger: #if($column.htmlType == "select" || $column.htmlType == "radio")"change"#else"blur"#end } ]#if($foreach.count != $columns.size()),#end #end #end @@ -431,9 +437,7 @@ function cancel() { function reset() { form.value = { #foreach ($column in $columns) -#if($column.htmlType == "radio") - $column.javaField: #if($column.javaType == "Integer" || $column.javaType == "Long")0#else"0"#end#if($foreach.count != $columns.size()),#end -#elseif($column.htmlType == "checkbox") +#if($column.htmlType == "checkbox") $column.javaField: []#if($foreach.count != $columns.size()),#end #else $column.javaField: null#if($foreach.count != $columns.size()),#end diff --git a/ruoyi-job/pom.xml b/ruoyi-job/pom.xml index d718ce90..90ed2179 100644 --- a/ruoyi-job/pom.xml +++ b/ruoyi-job/pom.xml @@ -32,3 +32,4 @@ + diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/SysLoginService.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/SysLoginService.java index 6ea4a883..3bb0a081 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/system/service/SysLoginService.java +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/SysLoginService.java @@ -5,23 +5,28 @@ import cn.dev33.satoken.secure.BCrypt; import cn.dev33.satoken.stp.StpUtil; import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.util.ObjectUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.ruoyi.common.constant.CacheConstants; import com.ruoyi.common.constant.Constants; +import com.ruoyi.common.core.domain.event.LogininforEvent; import com.ruoyi.common.core.domain.dto.RoleDTO; import com.ruoyi.common.core.domain.entity.SysUser; import com.ruoyi.common.core.domain.model.LoginUser; import com.ruoyi.common.core.domain.model.XcxLoginUser; -import com.ruoyi.common.core.service.LogininforService; import com.ruoyi.common.enums.DeviceType; import com.ruoyi.common.enums.LoginType; import com.ruoyi.common.enums.UserStatus; -import com.ruoyi.common.exception.user.*; +import com.ruoyi.common.exception.user.CaptchaException; +import com.ruoyi.common.exception.user.CaptchaExpireException; +import com.ruoyi.common.exception.user.UserException; import com.ruoyi.common.helper.LoginHelper; import com.ruoyi.common.utils.DateUtils; import com.ruoyi.common.utils.MessageUtils; import com.ruoyi.common.utils.ServletUtils; import com.ruoyi.common.utils.StringUtils; import com.ruoyi.common.utils.redis.RedisUtils; +import com.ruoyi.common.utils.spring.SpringUtils; +import com.ruoyi.system.mapper.SysUserMapper; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; @@ -42,9 +47,8 @@ import java.util.function.Supplier; @Service public class SysLoginService { - private final ISysUserService userService; + private final SysUserMapper userMapper; private final ISysConfigService configService; - private final LogininforService asyncService; private final SysPermissionService permissionService; @Value("${user.password.maxRetryCount}") @@ -76,7 +80,7 @@ public class SysLoginService { // 生成token LoginHelper.loginByDevice(loginUser, DeviceType.PC); - asyncService.recordLogininfor(username, Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success"), request); + recordLogininfor(username, Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success")); recordLoginInfo(user.getUserId(), username); return StpUtil.getTokenValue(); } @@ -85,21 +89,19 @@ public class SysLoginService { // 通过手机号查找用户 SysUser user = loadUserByPhonenumber(phonenumber); - HttpServletRequest request = ServletUtils.getRequest(); - checkLogin(LoginType.SMS, user.getUserName(), () -> !validateSmsCode(phonenumber, smsCode, request)); + checkLogin(LoginType.SMS, user.getUserName(), () -> !validateSmsCode(phonenumber, smsCode)); // 此处可根据登录用户的数据不同 自行创建 loginUser LoginUser loginUser = buildLoginUser(user); // 生成token LoginHelper.loginByDevice(loginUser, DeviceType.APP); - asyncService.recordLogininfor(user.getUserName(), Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success"), request); + recordLogininfor(user.getUserName(), Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success")); recordLoginInfo(user.getUserId(), user.getUserName()); return StpUtil.getTokenValue(); } public String xcxLogin(String xcxCode) { - HttpServletRequest request = ServletUtils.getRequest(); // xcxCode 为 小程序调用 wx.login 授权后获取 // todo 以下自行实现 // 校验 appid + appsrcret + xcxCode 调用登录凭证校验接口 获取 session_key 与 openid @@ -115,7 +117,7 @@ public class SysLoginService { // 生成token LoginHelper.loginByDevice(loginUser, DeviceType.XCX); - asyncService.recordLogininfor(user.getUserName(), Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success"), request); + recordLogininfor(user.getUserName(), Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success")); recordLoginInfo(user.getUserId(), user.getUserName()); return StpUtil.getTokenValue(); } @@ -127,18 +129,35 @@ public class SysLoginService { try { LoginUser loginUser = LoginHelper.getLoginUser(); StpUtil.logout(); - asyncService.recordLogininfor(loginUser.getUsername(), Constants.LOGOUT, MessageUtils.message("user.logout.success"), ServletUtils.getRequest()); + recordLogininfor(loginUser.getUsername(), Constants.LOGOUT, MessageUtils.message("user.logout.success")); } catch (NotLoginException ignored) { } } + /** + * 记录登录信息 + * + * @param username 用户名 + * @param status 状态 + * @param message 消息内容 + * @return + */ + private void recordLogininfor(String username, String status, String message) { + LogininforEvent logininforEvent = new LogininforEvent(); + logininforEvent.setUsername(username); + logininforEvent.setStatus(status); + logininforEvent.setMessage(message); + logininforEvent.setRequest(ServletUtils.getRequest()); + SpringUtils.context().publishEvent(logininforEvent); + } + /** * 校验短信验证码 */ - private boolean validateSmsCode(String phonenumber, String smsCode, HttpServletRequest request) { + private boolean validateSmsCode(String phonenumber, String smsCode) { String code = RedisUtils.getCacheObject(CacheConstants.CAPTCHA_CODE_KEY + phonenumber); if (StringUtils.isBlank(code)) { - asyncService.recordLogininfor(phonenumber, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.expire"), request); + recordLogininfor(phonenumber, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.expire")); throw new CaptchaExpireException(); } return code.equals(smsCode); @@ -156,43 +175,41 @@ public class SysLoginService { String captcha = RedisUtils.getCacheObject(verifyKey); RedisUtils.deleteObject(verifyKey); if (captcha == null) { - asyncService.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.expire"), request); + recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.expire")); throw new CaptchaExpireException(); } if (!code.equalsIgnoreCase(captcha)) { - asyncService.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.error"), request); + recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.error")); throw new CaptchaException(); } } private SysUser loadUserByUsername(String username) { - SysUser user = userService.selectUserByUserName(username); + SysUser user = userMapper.selectOne(new LambdaQueryWrapper() + .select(SysUser::getUserName, SysUser::getStatus) + .eq(SysUser::getUserName, username)); if (ObjectUtil.isNull(user)) { log.info("登录用户:{} 不存在.", username); throw new UserException("user.not.exists", username); - } else if (UserStatus.DELETED.getCode().equals(user.getDelFlag())) { - log.info("登录用户:{} 已被删除.", username); - throw new UserException("user.password.delete", username); } else if (UserStatus.DISABLE.getCode().equals(user.getStatus())) { log.info("登录用户:{} 已被停用.", username); throw new UserException("user.blocked", username); } - return user; + return userMapper.selectUserByUserName(username); } private SysUser loadUserByPhonenumber(String phonenumber) { - SysUser user = userService.selectUserByPhonenumber(phonenumber); + SysUser user = userMapper.selectOne(new LambdaQueryWrapper() + .select(SysUser::getPhonenumber, SysUser::getStatus) + .eq(SysUser::getPhonenumber, phonenumber)); if (ObjectUtil.isNull(user)) { log.info("登录用户:{} 不存在.", phonenumber); throw new UserException("user.not.exists", phonenumber); - } else if (UserStatus.DELETED.getCode().equals(user.getDelFlag())) { - log.info("登录用户:{} 已被删除.", phonenumber); - throw new UserException("user.password.delete", phonenumber); } else if (UserStatus.DISABLE.getCode().equals(user.getStatus())) { log.info("登录用户:{} 已被停用.", phonenumber); throw new UserException("user.blocked", phonenumber); } - return user; + return userMapper.selectUserByPhonenumber(phonenumber); } private SysUser loadUserByOpenid(String openid) { @@ -202,9 +219,6 @@ public class SysLoginService { if (ObjectUtil.isNull(user)) { log.info("登录用户:{} 不存在.", openid); // todo 用户不存在 业务逻辑自行实现 - } else if (UserStatus.DELETED.getCode().equals(user.getDelFlag())) { - log.info("登录用户:{} 已被删除.", openid); - // todo 用户已被删除 业务逻辑自行实现 } else if (UserStatus.DISABLE.getCode().equals(user.getStatus())) { log.info("登录用户:{} 已被停用.", openid); // todo 用户已被停用 业务逻辑自行实现 @@ -241,14 +255,13 @@ public class SysLoginService { sysUser.setLoginIp(ServletUtils.getClientIP()); sysUser.setLoginDate(DateUtils.getNowDate()); sysUser.setUpdateBy(username); - userService.updateUserProfile(sysUser); + userMapper.updateById(sysUser); } /** * 登录校验 */ private void checkLogin(LoginType loginType, String username, Supplier supplier) { - HttpServletRequest request = ServletUtils.getRequest(); String errorKey = CacheConstants.PWD_ERR_CNT_KEY + username; String loginFail = Constants.LOGIN_FAIL; @@ -256,7 +269,7 @@ public class SysLoginService { Integer errorNumber = RedisUtils.getCacheObject(errorKey); // 锁定时间内登录 则踢出 if (ObjectUtil.isNotNull(errorNumber) && errorNumber.equals(maxRetryCount)) { - asyncService.recordLogininfor(username, loginFail, MessageUtils.message(loginType.getRetryLimitExceed(), maxRetryCount, lockTime), request); + recordLogininfor(username, loginFail, MessageUtils.message(loginType.getRetryLimitExceed(), maxRetryCount, lockTime)); throw new UserException(loginType.getRetryLimitExceed(), maxRetryCount, lockTime); } @@ -266,12 +279,12 @@ public class SysLoginService { // 达到规定错误次数 则锁定登录 if (errorNumber.equals(maxRetryCount)) { RedisUtils.setCacheObject(errorKey, errorNumber, Duration.ofMinutes(lockTime)); - asyncService.recordLogininfor(username, loginFail, MessageUtils.message(loginType.getRetryLimitExceed(), maxRetryCount, lockTime), request); + recordLogininfor(username, loginFail, MessageUtils.message(loginType.getRetryLimitExceed(), maxRetryCount, lockTime)); throw new UserException(loginType.getRetryLimitExceed(), maxRetryCount, lockTime); } else { // 未达到规定错误次数 则递增 RedisUtils.setCacheObject(errorKey, errorNumber); - asyncService.recordLogininfor(username, loginFail, MessageUtils.message(loginType.getRetryLimitCount(), errorNumber), request); + recordLogininfor(username, loginFail, MessageUtils.message(loginType.getRetryLimitCount(), errorNumber)); throw new UserException(loginType.getRetryLimitCount(), errorNumber); } } 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 983173d1..bf9d00d4 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 @@ -4,9 +4,9 @@ import cn.dev33.satoken.secure.BCrypt; import com.ruoyi.common.constant.CacheConstants; import com.ruoyi.common.constant.Constants; import com.ruoyi.common.constant.UserConstants; +import com.ruoyi.common.core.domain.event.LogininforEvent; import com.ruoyi.common.core.domain.entity.SysUser; import com.ruoyi.common.core.domain.model.RegisterBody; -import com.ruoyi.common.core.service.LogininforService; import com.ruoyi.common.enums.UserType; import com.ruoyi.common.exception.user.CaptchaException; import com.ruoyi.common.exception.user.CaptchaExpireException; @@ -15,6 +15,7 @@ import com.ruoyi.common.utils.MessageUtils; import com.ruoyi.common.utils.ServletUtils; import com.ruoyi.common.utils.StringUtils; import com.ruoyi.common.utils.redis.RedisUtils; +import com.ruoyi.common.utils.spring.SpringUtils; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; @@ -31,7 +32,6 @@ public class SysRegisterService { private final ISysUserService userService; private final ISysConfigService configService; - private final LogininforService asyncService; /** * 注册 @@ -61,7 +61,7 @@ public class SysRegisterService { if (!regFlag) { throw new UserException("user.register.error"); } - asyncService.recordLogininfor(username, Constants.REGISTER, MessageUtils.message("user.register.success"), request); + recordLogininfor(username, Constants.REGISTER, MessageUtils.message("user.register.success")); } /** @@ -77,12 +77,30 @@ public class SysRegisterService { String captcha = RedisUtils.getCacheObject(verifyKey); RedisUtils.deleteObject(verifyKey); if (captcha == null) { - asyncService.recordLogininfor(username, Constants.REGISTER, MessageUtils.message("user.jcaptcha.expire"), request); + recordLogininfor(username, Constants.REGISTER, MessageUtils.message("user.jcaptcha.expire")); throw new CaptchaExpireException(); } if (!code.equalsIgnoreCase(captcha)) { - asyncService.recordLogininfor(username, Constants.REGISTER, MessageUtils.message("user.jcaptcha.error"), request); + recordLogininfor(username, Constants.REGISTER, MessageUtils.message("user.jcaptcha.error")); throw new CaptchaException(); } } + + /** + * 记录登录信息 + * + * @param username 用户名 + * @param status 状态 + * @param message 消息内容 + * @return + */ + private void recordLogininfor(String username, String status, String message) { + LogininforEvent logininforEvent = new LogininforEvent(); + logininforEvent.setUsername(username); + logininforEvent.setStatus(status); + logininforEvent.setMessage(message); + logininforEvent.setRequest(ServletUtils.getRequest()); + SpringUtils.context().publishEvent(logininforEvent); + } + } diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysConfigServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysConfigServiceImpl.java index 72dc42bb..c39ec74d 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysConfigServiceImpl.java +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysConfigServiceImpl.java @@ -138,6 +138,10 @@ public class SysConfigServiceImpl implements ISysConfigService, ConfigService { public String updateConfig(SysConfig config) { int row = 0; if (config.getConfigId() != null) { + SysConfig temp = baseMapper.selectById(config.getConfigId()); + if (!StringUtils.equals(temp.getConfigKey(), config.getConfigKey())) { + CacheUtils.evict(CacheNames.SYS_CONFIG, temp.getConfigKey()); + } row = baseMapper.updateById(config); } else { row = baseMapper.update(config, new LambdaQueryWrapper() diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysLogininforServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysLogininforServiceImpl.java index 159db624..1cd176f7 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysLogininforServiceImpl.java +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysLogininforServiceImpl.java @@ -6,8 +6,8 @@ import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.ruoyi.common.constant.Constants; import com.ruoyi.common.core.domain.PageQuery; +import com.ruoyi.common.core.domain.event.LogininforEvent; import com.ruoyi.common.core.page.TableDataInfo; -import com.ruoyi.common.core.service.LogininforService; import com.ruoyi.common.utils.ServletUtils; import com.ruoyi.common.utils.StringUtils; import com.ruoyi.common.utils.ip.AddressUtils; @@ -16,6 +16,7 @@ import com.ruoyi.system.mapper.SysLogininforMapper; import com.ruoyi.system.service.ISysLogininforService; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.springframework.context.event.EventListener; import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service; @@ -33,22 +34,19 @@ import java.util.Map; @RequiredArgsConstructor @Slf4j @Service -public class SysLogininforServiceImpl implements ISysLogininforService, LogininforService { +public class SysLogininforServiceImpl implements ISysLogininforService { private final SysLogininforMapper baseMapper; /** * 记录登录信息 * - * @param username 用户名 - * @param status 状态 - * @param message 消息 - * @param args 列表 + * @param logininforEvent 登录事件 */ @Async - @Override - public void recordLogininfor(final String username, final String status, final String message, - HttpServletRequest request, final Object... args) { + @EventListener + public void recordLogininfor(LogininforEvent logininforEvent) { + HttpServletRequest request = logininforEvent.getRequest(); final UserAgent userAgent = UserAgentUtil.parse(request.getHeader("User-Agent")); final String ip = ServletUtils.getClientIP(request); @@ -56,27 +54,27 @@ public class SysLogininforServiceImpl implements ISysLogininforService, Logininf StringBuilder s = new StringBuilder(); s.append(getBlock(ip)); s.append(address); - s.append(getBlock(username)); - s.append(getBlock(status)); - s.append(getBlock(message)); + s.append(getBlock(logininforEvent.getUsername())); + s.append(getBlock(logininforEvent.getStatus())); + s.append(getBlock(logininforEvent.getMessage())); // 打印信息到日志 - log.info(s.toString(), args); + log.info(s.toString(), logininforEvent.getArgs()); // 获取客户端操作系统 String os = userAgent.getOs().getName(); // 获取客户端浏览器 String browser = userAgent.getBrowser().getName(); // 封装对象 SysLogininfor logininfor = new SysLogininfor(); - logininfor.setUserName(username); + logininfor.setUserName(logininforEvent.getUsername()); logininfor.setIpaddr(ip); logininfor.setLoginLocation(address); logininfor.setBrowser(browser); logininfor.setOs(os); - logininfor.setMsg(message); + logininfor.setMsg(logininforEvent.getMessage()); // 日志状态 - if (StringUtils.equalsAny(status, Constants.LOGIN_SUCCESS, Constants.LOGOUT, Constants.REGISTER)) { + if (StringUtils.equalsAny(logininforEvent.getStatus(), Constants.LOGIN_SUCCESS, Constants.LOGOUT, Constants.REGISTER)) { logininfor.setStatus(Constants.SUCCESS); - } else if (Constants.LOGIN_FAIL.equals(status)) { + } else if (Constants.LOGIN_FAIL.equals(logininforEvent.getStatus())) { logininfor.setStatus(Constants.FAIL); } // 插入数据 diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysOperLogServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysOperLogServiceImpl.java index 8610d974..28ac34cb 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysOperLogServiceImpl.java +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysOperLogServiceImpl.java @@ -5,15 +5,15 @@ import cn.hutool.core.util.ArrayUtil; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.ruoyi.common.core.domain.PageQuery; -import com.ruoyi.common.core.domain.dto.OperLogDTO; +import com.ruoyi.common.core.domain.event.OperLogEvent; import com.ruoyi.common.core.page.TableDataInfo; -import com.ruoyi.common.core.service.OperLogService; import com.ruoyi.common.utils.StringUtils; import com.ruoyi.common.utils.ip.AddressUtils; import com.ruoyi.system.domain.SysOperLog; import com.ruoyi.system.mapper.SysOperLogMapper; import com.ruoyi.system.service.ISysOperLogService; import lombok.RequiredArgsConstructor; +import org.springframework.context.event.EventListener; import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service; @@ -29,19 +29,19 @@ import java.util.Map; */ @RequiredArgsConstructor @Service -public class SysOperLogServiceImpl implements ISysOperLogService, OperLogService { +public class SysOperLogServiceImpl implements ISysOperLogService { private final SysOperLogMapper baseMapper; /** * 操作日志记录 * - * @param operLogDTO 操作日志信息 + * @param operLogEvent 操作日志事件 */ @Async - @Override - public void recordOper(final OperLogDTO operLogDTO) { - SysOperLog operLog = BeanUtil.toBean(operLogDTO, SysOperLog.class); + @EventListener + public void recordOper(OperLogEvent operLogEvent) { + SysOperLog operLog = BeanUtil.toBean(operLogEvent, SysOperLog.class); // 远程查询操作地点 operLog.setOperLocation(AddressUtils.getRealAddressByIP(operLog.getOperIp())); insertOperlog(operLog); diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysOssConfigServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysOssConfigServiceImpl.java index 8ceff7cb..d6269242 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysOssConfigServiceImpl.java +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysOssConfigServiceImpl.java @@ -16,6 +16,7 @@ import com.ruoyi.common.utils.JsonUtils; import com.ruoyi.common.utils.StringUtils; import com.ruoyi.common.utils.redis.CacheUtils; import com.ruoyi.common.utils.redis.RedisUtils; +import com.ruoyi.common.utils.spring.SpringUtils; import com.ruoyi.oss.constant.OssConstant; import com.ruoyi.oss.factory.OssFactory; import com.ruoyi.system.domain.SysOssConfig; @@ -25,6 +26,7 @@ import com.ruoyi.system.mapper.SysOssConfigMapper; import com.ruoyi.system.service.ISysOssConfigService; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.springframework.context.event.EventListener; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -57,7 +59,7 @@ public class SysOssConfigServiceImpl implements ISysOssConfigService { if ("0".equals(config.getStatus())) { RedisUtils.setCacheObject(OssConstant.DEFAULT_CONFIG_KEY, configKey); } - setConfigCache(true, config); + SpringUtils.context().publishEvent(config); } // 初始化OSS工厂 OssFactory.init(); @@ -88,7 +90,11 @@ public class SysOssConfigServiceImpl implements ISysOssConfigService { public Boolean insertByBo(SysOssConfigBo bo) { SysOssConfig config = BeanUtil.toBean(bo, SysOssConfig.class); validEntityBeforeSave(config); - return setConfigCache(baseMapper.insert(config) > 0, config); + boolean flag = baseMapper.insert(config) > 0; + if (flag) { + SpringUtils.context().publishEvent(config); + } + return flag; } @Override @@ -101,7 +107,11 @@ public class SysOssConfigServiceImpl implements ISysOssConfigService { luw.set(ObjectUtil.isNull(config.getExt1()), SysOssConfig::getExt1, ""); luw.set(ObjectUtil.isNull(config.getRemark()), SysOssConfig::getRemark, ""); luw.eq(SysOssConfig::getOssConfigId, config.getOssConfigId()); - return setConfigCache(baseMapper.update(config, luw) > 0, config); + boolean flag = baseMapper.update(config, luw) > 0; + if (flag) { + SpringUtils.context().publishEvent(config); + } + return flag; } /** @@ -165,19 +175,15 @@ public class SysOssConfigServiceImpl implements ISysOssConfigService { } /** - * 如果操作成功 则更新缓存 + * 更新配置缓存 * - * @param flag 操作状态 * @param config 配置 - * @return 返回操作状态 */ - private boolean setConfigCache(boolean flag, SysOssConfig config) { - if (flag) { - CacheUtils.put(CacheNames.SYS_OSS_CONFIG, config.getConfigKey(), JsonUtils.toJsonString(config)); - RedisUtils.publish(OssConstant.DEFAULT_CONFIG_KEY, config.getConfigKey(), msg -> { - log.info("发布刷新OSS配置 => " + msg); - }); - } - return flag; + @EventListener + public void updateConfigCache(SysOssConfig config) { + CacheUtils.put(CacheNames.SYS_OSS_CONFIG, config.getConfigKey(), JsonUtils.toJsonString(config)); + RedisUtils.publish(OssConstant.DEFAULT_CONFIG_KEY, config.getConfigKey(), msg -> { + log.info("发布刷新OSS配置 => " + msg); + }); } } diff --git a/ruoyi-ui/package.json b/ruoyi-ui/package.json index fa30e030..013e8692 100644 --- a/ruoyi-ui/package.json +++ b/ruoyi-ui/package.json @@ -40,7 +40,7 @@ "axios": "0.24.0", "clipboard": "2.0.8", "core-js": "3.25.3", - "echarts": "4.9.0", + "echarts": "5.4.0", "element-ui": "2.15.10", "file-saver": "2.0.5", "fuse.js": "6.4.3", diff --git a/ruoyi-ui/src/assets/styles/ruoyi.scss b/ruoyi-ui/src/assets/styles/ruoyi.scss index 16a4740f..4e298744 100644 --- a/ruoyi-ui/src/assets/styles/ruoyi.scss +++ b/ruoyi-ui/src/assets/styles/ruoyi.scss @@ -73,6 +73,10 @@ color: inherit; } +.el-message-box__status + .el-message-box__message{ + word-break: break-word; +} + .el-dialog:not(.is-fullscreen) { margin-top: 6vh !important; } diff --git a/ruoyi-ui/src/components/HeaderSearch/index.vue b/ruoyi-ui/src/components/HeaderSearch/index.vue index c44eff56..c4d76bbb 100644 --- a/ruoyi-ui/src/components/HeaderSearch/index.vue +++ b/ruoyi-ui/src/components/HeaderSearch/index.vue @@ -90,7 +90,6 @@ export default { threshold: 0.4, location: 0, distance: 100, - maxPatternLength: 32, minMatchCharLength: 1, keys: [{ name: 'title', diff --git a/ruoyi-ui/src/components/RightPanel/index.vue b/ruoyi-ui/src/components/RightPanel/index.vue index 2d6122bc..5abeecb0 100644 --- a/ruoyi-ui/src/components/RightPanel/index.vue +++ b/ruoyi-ui/src/components/RightPanel/index.vue @@ -39,7 +39,6 @@ export default { } }, mounted() { - this.insertToBody() this.addEventClick() }, beforeDestroy() { @@ -56,11 +55,6 @@ export default { this.show = false window.removeEventListener('click', this.closeSidebar) } - }, - insertToBody() { - const elx = this.$refs.rightPanel - const body = document.querySelector('body') - body.insertBefore(elx, body.firstChild) } } } diff --git a/ruoyi-ui/src/plugins/download.js b/ruoyi-ui/src/plugins/download.js index 85138bb1..a1b21dad 100644 --- a/ruoyi-ui/src/plugins/download.js +++ b/ruoyi-ui/src/plugins/download.js @@ -21,7 +21,7 @@ export default { const isLogin = await blobValidate(res.data); if (isLogin) { const blob = new Blob([res.data], { type: 'application/octet-stream' }) - this.saveAs(blob, decodeURI(res.headers['download-filename'])) + this.saveAs(blob, decodeURIComponent(res.headers['download-filename'])) } else { this.printErrMsg(res.data); } diff --git a/ruoyi-ui/src/utils/generator/html.js b/ruoyi-ui/src/utils/generator/html.js index 44f68154..9bcc5361 100644 --- a/ruoyi-ui/src/utils/generator/html.js +++ b/ruoyi-ui/src/utils/generator/html.js @@ -1,5 +1,5 @@ /* eslint-disable max-len */ -import ruleTrigger from './ruleTrigger' +import { trigger } from './config' let confGlobal let someSpanIsNot24 @@ -34,27 +34,27 @@ export function cssStyle(cssStr) { ` } -function buildFormTemplate(scheme, child, type) { +function buildFormTemplate(conf, child, type) { let labelPosition = '' - if (scheme.labelPosition !== 'right') { - labelPosition = `label-position="${scheme.labelPosition}"` + if (conf.labelPosition !== 'right') { + labelPosition = `label-position="${conf.labelPosition}"` } - const disabled = scheme.disabled ? `:disabled="${scheme.disabled}"` : '' - let str = ` + const disabled = conf.disabled ? `:disabled="${conf.disabled}"` : '' + let str = ` ${child} - ${buildFromBtns(scheme, type)} + ${buildFromBtns(conf, type)} ` if (someSpanIsNot24) { - str = ` + str = ` ${str} ` } return str } -function buildFromBtns(scheme, type) { +function buildFromBtns(conf, type) { let str = '' - if (scheme.formBtns && type === 'file') { + if (conf.formBtns && type === 'file') { str = ` 提交 重置 @@ -69,9 +69,9 @@ function buildFromBtns(scheme, type) { } // span不为24的用el-col包裹 -function colWrapper(scheme, str) { - if (someSpanIsNot24 || scheme.__config__.span !== 24) { - return ` +function colWrapper(element, str) { + if (someSpanIsNot24 || element.span !== 24) { + return ` ${str} ` } @@ -79,36 +79,29 @@ function colWrapper(scheme, str) { } const layouts = { - colFormItem(scheme) { - const config = scheme.__config__ + colFormItem(element) { let labelWidth = '' - let label = `label="${config.label}"` - if (config.labelWidth && config.labelWidth !== confGlobal.labelWidth) { - labelWidth = `label-width="${config.labelWidth}px"` + if (element.labelWidth && element.labelWidth !== confGlobal.labelWidth) { + labelWidth = `label-width="${element.labelWidth}px"` } - if (config.showLabel === false) { - labelWidth = 'label-width="0"' - label = '' - } - const required = !ruleTrigger[config.tag] && config.required ? 'required' : '' - const tagDom = tags[config.tag] ? tags[config.tag](scheme) : null - let str = ` + const required = !trigger[element.tag] && element.required ? 'required' : '' + const tagDom = tags[element.tag] ? tags[element.tag](element) : null + let str = ` ${tagDom} ` - str = colWrapper(scheme, str) + str = colWrapper(element, str) return str }, - rowFormItem(scheme) { - const config = scheme.__config__ - const type = scheme.type === 'default' ? '' : `type="${scheme.type}"` - const justify = scheme.type === 'default' ? '' : `justify="${scheme.justify}"` - const align = scheme.type === 'default' ? '' : `align="${scheme.align}"` - const gutter = scheme.gutter ? `:gutter="${scheme.gutter}"` : '' - const children = config.children.map(el => layouts[el.__config__.layout](el)) + rowFormItem(element) { + const type = element.type === 'default' ? '' : `type="${element.type}"` + const justify = element.type === 'default' ? '' : `justify="${element.justify}"` + const align = element.type === 'default' ? '' : `align="${element.align}"` + const gutter = element.gutter ? `gutter="${element.gutter}"` : '' + const children = element.children.map(el => layouts[el.layout](el)) let str = ` ${children.join('\n')} ` - str = colWrapper(scheme, str) + str = colWrapper(element, str) return str } } @@ -120,18 +113,15 @@ const tags = { } = attrBuilder(el) const type = el.type ? `type="${el.type}"` : '' const icon = el.icon ? `icon="${el.icon}"` : '' - const round = el.round ? 'round' : '' const size = el.size ? `size="${el.size}"` : '' - const plain = el.plain ? 'plain' : '' - const circle = el.circle ? 'circle' : '' let child = buildElButtonChild(el) if (child) child = `\n${child}\n` // 换行 - return `<${tag} ${type} ${icon} ${round} ${size} ${plain} ${disabled} ${circle}>${child}` + return `<${el.tag} ${type} ${icon} ${size} ${disabled}>${child}` }, 'el-input': el => { const { - tag, disabled, vModel, clearable, placeholder, width + disabled, vModel, clearable, placeholder, width } = attrBuilder(el) const maxlength = el.maxlength ? `:maxlength="${el.maxlength}"` : '' const showWordLimit = el['show-word-limit'] ? 'show-word-limit' : '' @@ -146,12 +136,10 @@ const tags = { let child = buildElInputChild(el) if (child) child = `\n${child}\n` // 换行 - return `<${tag} ${vModel} ${type} ${placeholder} ${maxlength} ${showWordLimit} ${readonly} ${disabled} ${clearable} ${prefixIcon} ${suffixIcon} ${showPassword} ${autosize} ${width}>${child}` + return `<${el.tag} ${vModel} ${type} ${placeholder} ${maxlength} ${showWordLimit} ${readonly} ${disabled} ${clearable} ${prefixIcon} ${suffixIcon} ${showPassword} ${autosize} ${width}>${child}` }, 'el-input-number': el => { - const { - tag, disabled, vModel, placeholder - } = attrBuilder(el) + const { disabled, vModel, placeholder } = attrBuilder(el) const controlsPosition = el['controls-position'] ? `controls-position=${el['controls-position']}` : '' const min = el.min ? `:min='${el.min}'` : '' const max = el.max ? `:max='${el.max}'` : '' @@ -159,39 +147,39 @@ const tags = { const stepStrictly = el['step-strictly'] ? 'step-strictly' : '' const precision = el.precision ? `:precision='${el.precision}'` : '' - return `<${tag} ${vModel} ${placeholder} ${step} ${stepStrictly} ${precision} ${controlsPosition} ${min} ${max} ${disabled}>` + return `<${el.tag} ${vModel} ${placeholder} ${step} ${stepStrictly} ${precision} ${controlsPosition} ${min} ${max} ${disabled}>` }, 'el-select': el => { const { - tag, disabled, vModel, clearable, placeholder, width + disabled, vModel, clearable, placeholder, width } = attrBuilder(el) const filterable = el.filterable ? 'filterable' : '' const multiple = el.multiple ? 'multiple' : '' let child = buildElSelectChild(el) if (child) child = `\n${child}\n` // 换行 - return `<${tag} ${vModel} ${placeholder} ${disabled} ${multiple} ${filterable} ${clearable} ${width}>${child}` + return `<${el.tag} ${vModel} ${placeholder} ${disabled} ${multiple} ${filterable} ${clearable} ${width}>${child}` }, 'el-radio-group': el => { - const { tag, disabled, vModel } = attrBuilder(el) + const { disabled, vModel } = attrBuilder(el) const size = `size="${el.size}"` let child = buildElRadioGroupChild(el) if (child) child = `\n${child}\n` // 换行 - return `<${tag} ${vModel} ${size} ${disabled}>${child}` + return `<${el.tag} ${vModel} ${size} ${disabled}>${child}` }, 'el-checkbox-group': el => { - const { tag, disabled, vModel } = attrBuilder(el) + const { disabled, vModel } = attrBuilder(el) const size = `size="${el.size}"` const min = el.min ? `:min="${el.min}"` : '' const max = el.max ? `:max="${el.max}"` : '' let child = buildElCheckboxGroupChild(el) if (child) child = `\n${child}\n` // 换行 - return `<${tag} ${vModel} ${min} ${max} ${size} ${disabled}>${child}` + return `<${el.tag} ${vModel} ${min} ${max} ${size} ${disabled}>${child}` }, 'el-switch': el => { - const { tag, disabled, vModel } = attrBuilder(el) + const { disabled, vModel } = attrBuilder(el) const activeText = el['active-text'] ? `active-text="${el['active-text']}"` : '' const inactiveText = el['inactive-text'] ? `inactive-text="${el['inactive-text']}"` : '' const activeColor = el['active-color'] ? `active-color="${el['active-color']}"` : '' @@ -199,33 +187,33 @@ const tags = { const activeValue = el['active-value'] !== true ? `:active-value='${JSON.stringify(el['active-value'])}'` : '' const inactiveValue = el['inactive-value'] !== false ? `:inactive-value='${JSON.stringify(el['inactive-value'])}'` : '' - return `<${tag} ${vModel} ${activeText} ${inactiveText} ${activeColor} ${inactiveColor} ${activeValue} ${inactiveValue} ${disabled}>` + return `<${el.tag} ${vModel} ${activeText} ${inactiveText} ${activeColor} ${inactiveColor} ${activeValue} ${inactiveValue} ${disabled}>` }, 'el-cascader': el => { const { - tag, disabled, vModel, clearable, placeholder, width + disabled, vModel, clearable, placeholder, width } = attrBuilder(el) - const options = el.options ? `:options="${el.__vModel__}Options"` : '' - const props = el.props ? `:props="${el.__vModel__}Props"` : '' + const options = el.options ? `:options="${el.vModel}Options"` : '' + const props = el.props ? `:props="${el.vModel}Props"` : '' const showAllLevels = el['show-all-levels'] ? '' : ':show-all-levels="false"' const filterable = el.filterable ? 'filterable' : '' const separator = el.separator === '/' ? '' : `separator="${el.separator}"` - return `<${tag} ${vModel} ${options} ${props} ${width} ${showAllLevels} ${placeholder} ${separator} ${filterable} ${clearable} ${disabled}>` + return `<${el.tag} ${vModel} ${options} ${props} ${width} ${showAllLevels} ${placeholder} ${separator} ${filterable} ${clearable} ${disabled}>` }, 'el-slider': el => { - const { tag, disabled, vModel } = attrBuilder(el) + const { disabled, vModel } = attrBuilder(el) const min = el.min ? `:min='${el.min}'` : '' const max = el.max ? `:max='${el.max}'` : '' const step = el.step ? `:step='${el.step}'` : '' const range = el.range ? 'range' : '' const showStops = el['show-stops'] ? `:show-stops="${el['show-stops']}"` : '' - return `<${tag} ${min} ${max} ${step} ${vModel} ${range} ${showStops} ${disabled}>` + return `<${el.tag} ${min} ${max} ${step} ${vModel} ${range} ${showStops} ${disabled}>` }, 'el-time-picker': el => { const { - tag, disabled, vModel, clearable, placeholder, width + disabled, vModel, clearable, placeholder, width } = attrBuilder(el) const startPlaceholder = el['start-placeholder'] ? `start-placeholder="${el['start-placeholder']}"` : '' const endPlaceholder = el['end-placeholder'] ? `end-placeholder="${el['end-placeholder']}"` : '' @@ -235,11 +223,11 @@ const tags = { const valueFormat = el['value-format'] ? `value-format="${el['value-format']}"` : '' const pickerOptions = el['picker-options'] ? `:picker-options='${JSON.stringify(el['picker-options'])}'` : '' - return `<${tag} ${vModel} ${isRange} ${format} ${valueFormat} ${pickerOptions} ${width} ${placeholder} ${startPlaceholder} ${endPlaceholder} ${rangeSeparator} ${clearable} ${disabled}>` + return `<${el.tag} ${vModel} ${isRange} ${format} ${valueFormat} ${pickerOptions} ${width} ${placeholder} ${startPlaceholder} ${endPlaceholder} ${rangeSeparator} ${clearable} ${disabled}>` }, 'el-date-picker': el => { const { - tag, disabled, vModel, clearable, placeholder, width + disabled, vModel, clearable, placeholder, width } = attrBuilder(el) const startPlaceholder = el['start-placeholder'] ? `start-placeholder="${el['start-placeholder']}"` : '' const endPlaceholder = el['end-placeholder'] ? `end-placeholder="${el['end-placeholder']}"` : '' @@ -249,54 +237,46 @@ const tags = { const type = el.type === 'date' ? '' : `type="${el.type}"` const readonly = el.readonly ? 'readonly' : '' - return `<${tag} ${type} ${vModel} ${format} ${valueFormat} ${width} ${placeholder} ${startPlaceholder} ${endPlaceholder} ${rangeSeparator} ${clearable} ${readonly} ${disabled}>` + return `<${el.tag} ${type} ${vModel} ${format} ${valueFormat} ${width} ${placeholder} ${startPlaceholder} ${endPlaceholder} ${rangeSeparator} ${clearable} ${readonly} ${disabled}>` }, 'el-rate': el => { - const { tag, disabled, vModel } = attrBuilder(el) + const { disabled, vModel } = attrBuilder(el) const max = el.max ? `:max='${el.max}'` : '' const allowHalf = el['allow-half'] ? 'allow-half' : '' const showText = el['show-text'] ? 'show-text' : '' const showScore = el['show-score'] ? 'show-score' : '' - return `<${tag} ${vModel} ${max} ${allowHalf} ${showText} ${showScore} ${disabled}>` + return `<${el.tag} ${vModel} ${allowHalf} ${showText} ${showScore} ${disabled}>` }, 'el-color-picker': el => { - const { tag, disabled, vModel } = attrBuilder(el) + const { disabled, vModel } = attrBuilder(el) const size = `size="${el.size}"` const showAlpha = el['show-alpha'] ? 'show-alpha' : '' const colorFormat = el['color-format'] ? `color-format="${el['color-format']}"` : '' - return `<${tag} ${vModel} ${size} ${showAlpha} ${colorFormat} ${disabled}>` + return `<${el.tag} ${vModel} ${size} ${showAlpha} ${colorFormat} ${disabled}>` }, 'el-upload': el => { - const { tag } = el.__config__ const disabled = el.disabled ? ':disabled=\'true\'' : '' - const action = el.action ? `:action="${el.__vModel__}Action"` : '' + const action = el.action ? `:action="${el.vModel}Action"` : '' const multiple = el.multiple ? 'multiple' : '' const listType = el['list-type'] !== 'text' ? `list-type="${el['list-type']}"` : '' const accept = el.accept ? `accept="${el.accept}"` : '' const name = el.name !== 'file' ? `name="${el.name}"` : '' const autoUpload = el['auto-upload'] === false ? ':auto-upload="false"' : '' - const beforeUpload = `:before-upload="${el.__vModel__}BeforeUpload"` - const fileList = `:file-list="${el.__vModel__}fileList"` - const ref = `ref="${el.__vModel__}"` + const beforeUpload = `:before-upload="${el.vModel}BeforeUpload"` + const fileList = `:file-list="${el.vModel}fileList"` + const ref = `ref="${el.vModel}"` let child = buildElUploadChild(el) if (child) child = `\n${child}\n` // 换行 - return `<${tag} ${ref} ${fileList} ${action} ${autoUpload} ${multiple} ${beforeUpload} ${listType} ${accept} ${name} ${disabled}>${child}` - }, - tinymce: el => { - const { tag, vModel, placeholder } = attrBuilder(el) - const height = el.height ? `:height="${el.height}"` : '' - const branding = el.branding ? `:branding="${el.branding}"` : '' - return `<${tag} ${vModel} ${placeholder} ${height} ${branding}>` + return `<${el.tag} ${ref} ${fileList} ${action} ${autoUpload} ${multiple} ${beforeUpload} ${listType} ${accept} ${name} ${disabled}>${child}` } } function attrBuilder(el) { return { - tag: el.__config__.tag, - vModel: `v-model="${confGlobal.formModel}.${el.__vModel__}"`, + vModel: `v-model="${confGlobal.formModel}.${el.vModel}"`, clearable: el.clearable ? 'clearable' : '', placeholder: el.placeholder ? `placeholder="${el.placeholder}"` : '', width: el.style && el.style.width ? ':style="{width: \'100%\'}"' : '', @@ -305,92 +285,72 @@ function attrBuilder(el) { } // el-buttin 子级 -function buildElButtonChild(scheme) { +function buildElButtonChild(conf) { const children = [] - const slot = scheme.__slot__ || {} - if (slot.default) { - children.push(slot.default) + if (conf.default) { + children.push(conf.default) } return children.join('\n') } -// el-input 子级 -function buildElInputChild(scheme) { +// el-input innerHTML +function buildElInputChild(conf) { const children = [] - const slot = scheme.__slot__ - if (slot && slot.prepend) { - children.push(``) + if (conf.prepend) { + children.push(``) } - if (slot && slot.append) { - children.push(``) + if (conf.append) { + children.push(``) } return children.join('\n') } -// el-select 子级 -function buildElSelectChild(scheme) { +function buildElSelectChild(conf) { const children = [] - const slot = scheme.__slot__ - if (slot && slot.options && slot.options.length) { - children.push(``) + if (conf.options && conf.options.length) { + children.push(``) } return children.join('\n') } -// el-radio-group 子级 -function buildElRadioGroupChild(scheme) { +function buildElRadioGroupChild(conf) { const children = [] - const slot = scheme.__slot__ - const config = scheme.__config__ - if (slot && slot.options && slot.options.length) { - const tag = config.optionType === 'button' ? 'el-radio-button' : 'el-radio' - const border = config.border ? 'border' : '' - children.push(`<${tag} v-for="(item, index) in ${scheme.__vModel__}Options" :key="index" :label="item.value" :disabled="item.disabled" ${border}>{{item.label}}`) + if (conf.options && conf.options.length) { + const tag = conf.optionType === 'button' ? 'el-radio-button' : 'el-radio' + const border = conf.border ? 'border' : '' + children.push(`<${tag} v-for="(item, index) in ${conf.vModel}Options" :key="index" :label="item.value" :disabled="item.disabled" ${border}>{{item.label}}`) } return children.join('\n') } -// el-checkbox-group 子级 -function buildElCheckboxGroupChild(scheme) { +function buildElCheckboxGroupChild(conf) { const children = [] - const slot = scheme.__slot__ - const config = scheme.__config__ - if (slot && slot.options && slot.options.length) { - const tag = config.optionType === 'button' ? 'el-checkbox-button' : 'el-checkbox' - const border = config.border ? 'border' : '' - children.push(`<${tag} v-for="(item, index) in ${scheme.__vModel__}Options" :key="index" :label="item.value" :disabled="item.disabled" ${border}>{{item.label}}`) + if (conf.options && conf.options.length) { + const tag = conf.optionType === 'button' ? 'el-checkbox-button' : 'el-checkbox' + const border = conf.border ? 'border' : '' + children.push(`<${tag} v-for="(item, index) in ${conf.vModel}Options" :key="index" :label="item.value" :disabled="item.disabled" ${border}>{{item.label}}`) } return children.join('\n') } -// el-upload 子级 -function buildElUploadChild(scheme) { +function buildElUploadChild(conf) { const list = [] - const config = scheme.__config__ - if (scheme['list-type'] === 'picture-card') list.push('') - else list.push(`${config.buttonText}`) - if (config.showTip) list.push(`
只能上传不超过 ${config.fileSize}${config.sizeUnit} 的${scheme.accept}文件
`) + if (conf['list-type'] === 'picture-card') list.push('') + else list.push(`${conf.buttonText}`) + if (conf.showTip) list.push(`
只能上传不超过 ${conf.fileSize}${conf.sizeUnit} 的${conf.accept}文件
`) return list.join('\n') } -/** - * 组装html代码。【入口函数】 - * @param {Object} formConfig 整个表单配置 - * @param {String} type 生成类型,文件或弹窗等 - */ -export function makeUpHtml(formConfig, type) { +export function makeUpHtml(conf, type) { const htmlList = [] - confGlobal = formConfig - // 判断布局是否都沾满了24个栅格,以备后续简化代码结构 - someSpanIsNot24 = formConfig.fields.some(item => item.__config__.span !== 24) - // 遍历渲染每个组件成html - formConfig.fields.forEach(el => { - htmlList.push(layouts[el.__config__.layout](el)) + confGlobal = conf + someSpanIsNot24 = conf.fields.some(item => item.span !== 24) + conf.fields.forEach(el => { + htmlList.push(layouts[el.layout](el)) }) const htmlStr = htmlList.join('\n') - // 将组件代码放进form标签 - let temp = buildFormTemplate(formConfig, htmlStr, type) - // dialog标签包裹代码 + + let temp = buildFormTemplate(conf, htmlStr, type) if (type === 'dialog') { temp = dialogWrapper(temp) } diff --git a/ruoyi-ui/src/utils/generator/js.js b/ruoyi-ui/src/utils/generator/js.js index c4b2a57e..35e3e21a 100644 --- a/ruoyi-ui/src/utils/generator/js.js +++ b/ruoyi-ui/src/utils/generator/js.js @@ -1,6 +1,6 @@ import { isArray } from 'util' -import { exportDefault, titleCase, deepClone } from '@/utils/index' -import ruleTrigger from './ruleTrigger' +import { exportDefault, titleCase } from '@/utils/index' +import { trigger } from './config' const units = { KB: '1024', @@ -13,91 +13,69 @@ const inheritAttrs = { dialog: 'inheritAttrs: false,' } -/** - * 组装js 【入口函数】 - * @param {Object} formConfig 整个表单配置 - * @param {String} type 生成类型,文件或弹窗等 - */ -export function makeUpJs(formConfig, type) { - confGlobal = formConfig = deepClone(formConfig) + +export function makeUpJs(conf, type) { + confGlobal = conf = JSON.parse(JSON.stringify(conf)) const dataList = [] const ruleList = [] const optionsList = [] const propsList = [] const methodList = mixinMethod(type) const uploadVarList = [] - const created = [] - formConfig.fields.forEach(el => { - buildAttributes(el, dataList, ruleList, optionsList, methodList, propsList, uploadVarList, created) + conf.fields.forEach(el => { + buildAttributes(el, dataList, ruleList, optionsList, methodList, propsList, uploadVarList) }) const script = buildexport( - formConfig, + conf, type, dataList.join('\n'), ruleList.join('\n'), optionsList.join('\n'), uploadVarList.join('\n'), propsList.join('\n'), - methodList.join('\n'), - created.join('\n') + methodList.join('\n') ) confGlobal = null return script } -// 构建组件属性 -function buildAttributes(scheme, dataList, ruleList, optionsList, methodList, propsList, uploadVarList, created) { - const config = scheme.__config__ - const slot = scheme.__slot__ - buildData(scheme, dataList) - buildRules(scheme, ruleList) +function buildAttributes(el, dataList, ruleList, optionsList, methodList, propsList, uploadVarList) { + buildData(el, dataList) + buildRules(el, ruleList) - // 特殊处理options属性 - if (scheme.options || (slot && slot.options && slot.options.length)) { - buildOptions(scheme, optionsList) - if (config.dataType === 'dynamic') { - const model = `${scheme.__vModel__}Options` + if (el.options && el.options.length) { + buildOptions(el, optionsList) + if (el.dataType === 'dynamic') { + const model = `${el.vModel}Options` const options = titleCase(model) - const methodName = `get${options}` - buildOptionMethod(methodName, model, methodList, scheme) - callInCreated(methodName, created) + buildOptionMethod(`get${options}`, model, methodList) } } - // 处理props - if (scheme.props && scheme.props.props) { - buildProps(scheme, propsList) + if (el.props && el.props.props) { + buildProps(el, propsList) } - // 处理el-upload的action - if (scheme.action && config.tag === 'el-upload') { + if (el.action && el.tag === 'el-upload') { uploadVarList.push( - `${scheme.__vModel__}Action: '${scheme.action}', - ${scheme.__vModel__}fileList: [],` + `${el.vModel}Action: '${el.action}', + ${el.vModel}fileList: [],` ) - methodList.push(buildBeforeUpload(scheme)) - // 非自动上传时,生成手动上传的函数 - if (!scheme['auto-upload']) { - methodList.push(buildSubmitUpload(scheme)) + methodList.push(buildBeforeUpload(el)) + if (!el['auto-upload']) { + methodList.push(buildSubmitUpload(el)) } } - // 构建子级组件属性 - if (config.children) { - config.children.forEach(item => { - buildAttributes(item, dataList, ruleList, optionsList, methodList, propsList, uploadVarList, created) + if (el.children) { + el.children.forEach(el2 => { + buildAttributes(el2, dataList, ruleList, optionsList, methodList, propsList, uploadVarList) }) } } -// 在Created调用函数 -function callInCreated(methodName, created) { - created.push(`this.${methodName}()`) -} - -// 混入处理函数 function mixinMethod(type) { const list = []; const minxins = { @@ -120,7 +98,7 @@ function mixinMethod(type) { close: `close() { this.$emit('update:visible', false) },`, - handelConfirm: `handelConfirm() { + handleConfirm: `handleConfirm() { this.$refs['${confGlobal.formRef}'].validate(valid => { if(!valid) return this.close() @@ -139,75 +117,73 @@ function mixinMethod(type) { return list } -// 构建data -function buildData(scheme, dataList) { - const config = scheme.__config__ - if (scheme.__vModel__ === undefined) return - const defaultValue = JSON.stringify(config.defaultValue) - dataList.push(`${scheme.__vModel__}: ${defaultValue},`) +function buildData(conf, dataList) { + if (conf.vModel === undefined) return + let defaultValue + if (typeof (conf.defaultValue) === 'string' && !conf.multiple) { + defaultValue = `'${conf.defaultValue}'` + } else { + defaultValue = `${JSON.stringify(conf.defaultValue)}` + } + dataList.push(`${conf.vModel}: ${defaultValue},`) } -// 构建校验规则 -function buildRules(scheme, ruleList) { - const config = scheme.__config__ - if (scheme.__vModel__ === undefined) return +function buildRules(conf, ruleList) { + if (conf.vModel === undefined) return const rules = [] - if (ruleTrigger[config.tag]) { - if (config.required) { - const type = isArray(config.defaultValue) ? 'type: \'array\',' : '' - let message = isArray(config.defaultValue) ? `请至少选择一个${config.label}` : scheme.placeholder - if (message === undefined) message = `${config.label}不能为空` - rules.push(`{ required: true, ${type} message: '${message}', trigger: '${ruleTrigger[config.tag]}' }`) + if (trigger[conf.tag]) { + if (conf.required) { + const type = isArray(conf.defaultValue) ? 'type: \'array\',' : '' + let message = isArray(conf.defaultValue) ? `请至少选择一个${conf.vModel}` : conf.placeholder + if (message === undefined) message = `${conf.label}不能为空` + rules.push(`{ required: true, ${type} message: '${message}', trigger: '${trigger[conf.tag]}' }`) } - if (config.regList && isArray(config.regList)) { - config.regList.forEach(item => { + if (conf.regList && isArray(conf.regList)) { + conf.regList.forEach(item => { if (item.pattern) { - rules.push( - `{ pattern: ${eval(item.pattern)}, message: '${item.message}', trigger: '${ruleTrigger[config.tag]}' }` - ) + rules.push(`{ pattern: ${eval(item.pattern)}, message: '${item.message}', trigger: '${trigger[conf.tag]}' }`) } }) } - ruleList.push(`${scheme.__vModel__}: [${rules.join(',')}],`) + ruleList.push(`${conf.vModel}: [${rules.join(',')}],`) } } -// 构建options -function buildOptions(scheme, optionsList) { - if (scheme.__vModel__ === undefined) return - // el-cascader直接有options属性,其他组件都是定义在slot中,所以有两处判断 - let { options } = scheme - if (!options) options = scheme.__slot__.options - if (scheme.__config__.dataType === 'dynamic') { options = [] } - const str = `${scheme.__vModel__}Options: ${JSON.stringify(options)},` +function buildOptions(conf, optionsList) { + if (conf.vModel === undefined) return + if (conf.dataType === 'dynamic') { conf.options = [] } + const str = `${conf.vModel}Options: ${JSON.stringify(conf.options)},` optionsList.push(str) } -function buildProps(scheme, propsList) { - const str = `${scheme.__vModel__}Props: ${JSON.stringify(scheme.props.props)},` +function buildProps(conf, propsList) { + if (conf.dataType === 'dynamic') { + conf.valueKey !== 'value' && (conf.props.props.value = conf.valueKey) + conf.labelKey !== 'label' && (conf.props.props.label = conf.labelKey) + conf.childrenKey !== 'children' && (conf.props.props.children = conf.childrenKey) + } + const str = `${conf.vModel}Props: ${JSON.stringify(conf.props.props)},` propsList.push(str) } -// el-upload的BeforeUpload -function buildBeforeUpload(scheme) { - const config = scheme.__config__ - const unitNum = units[config.sizeUnit]; let rightSizeCode = ''; let acceptCode = ''; const +function buildBeforeUpload(conf) { + const unitNum = units[conf.sizeUnit]; let rightSizeCode = ''; let acceptCode = ''; const returnList = [] - if (config.fileSize) { - rightSizeCode = `let isRightSize = file.size / ${unitNum} < ${config.fileSize} + if (conf.fileSize) { + rightSizeCode = `let isRightSize = file.size / ${unitNum} < ${conf.fileSize} if(!isRightSize){ - this.$message.error('文件大小超过 ${config.fileSize}${config.sizeUnit}') + this.$message.error('文件大小超过 ${conf.fileSize}${conf.sizeUnit}') }` returnList.push('isRightSize') } - if (scheme.accept) { - acceptCode = `let isAccept = new RegExp('${scheme.accept}').test(file.type) + if (conf.accept) { + acceptCode = `let isAccept = new RegExp('${conf.accept}').test(file.type) if(!isAccept){ - this.$message.error('应该选择${scheme.accept}类型的文件') + this.$message.error('应该选择${conf.accept}类型的文件') }` returnList.push('isAccept') } - const str = `${scheme.__vModel__}BeforeUpload(file) { + const str = `${conf.vModel}BeforeUpload(file) { ${rightSizeCode} ${acceptCode} return ${returnList.join('&&')} @@ -215,31 +191,22 @@ function buildBeforeUpload(scheme) { return returnList.length ? str : '' } -// el-upload的submit -function buildSubmitUpload(scheme) { +function buildSubmitUpload(conf) { const str = `submitUpload() { - this.$refs['${scheme.__vModel__}'].submit() + this.$refs['${conf.vModel}'].submit() },` return str } -function buildOptionMethod(methodName, model, methodList, scheme) { - const config = scheme.__config__ +function buildOptionMethod(methodName, model, methodList) { const str = `${methodName}() { - // 注意:this.$axios是通过Vue.prototype.$axios = axios挂载产生的 - this.$axios({ - method: '${config.method}', - url: '${config.url}' - }).then(resp => { - var { data } = resp - this.${model} = data.${config.dataKey} - }) + // TODO 发起请求获取数据 + this.${model} },` methodList.push(str) } -// js整体拼接 -function buildexport(conf, type, data, rules, selectOptions, uploadVar, props, methods, created) { +function buildexport(conf, type, data, rules, selectOptions, uploadVar, props, methods) { const str = `${exportDefault}{ ${inheritAttrs[type]} components: {}, @@ -259,9 +226,7 @@ function buildexport(conf, type, data, rules, selectOptions, uploadVar, props, m }, computed: {}, watch: {}, - created () { - ${created} - }, + created () {}, mounted () {}, methods: { ${methods} diff --git a/ruoyi-ui/src/views/dashboard/BarChart.vue b/ruoyi-ui/src/views/dashboard/BarChart.vue index be0af34f..88e7ef64 100644 --- a/ruoyi-ui/src/views/dashboard/BarChart.vue +++ b/ruoyi-ui/src/views/dashboard/BarChart.vue @@ -3,7 +3,7 @@ + + + diff --git a/ruoyi-ui/src/views/register.vue b/ruoyi-ui/src/views/register.vue index 1797599c..649c4d8d 100644 --- a/ruoyi-ui/src/views/register.vue +++ b/ruoyi-ui/src/views/register.vue @@ -87,7 +87,7 @@ export default { confirmPassword: "", code: "", uuid: "", - user_type: "sys_user" + userType: "sys_user" }, registerRules: { username: [ diff --git a/script/docker/docker-compose.yml b/script/docker/docker-compose.yml index 2108d454..e8003d02 100644 --- a/script/docker/docker-compose.yml +++ b/script/docker/docker-compose.yml @@ -78,9 +78,9 @@ services: # 时区上海 TZ: Asia/Shanghai # 管理后台用户名 - MINIO_ACCESS_KEY: ruoyi + MINIO_ROOT_USER: ruoyi # 管理后台密码,最小8个字符 - MINIO_SECRET_KEY: ruoyi123 + MINIO_ROOT_PASSWORD: ruoyi123 # https需要指定域名 #MINIO_SERVER_URL: "https://xxx.com:9000" #MINIO_BROWSER_REDIRECT_URL: "https://xxx.com:9001"