疯狂的狮子Li
2023-01-18 4ce596aa9c2d74e58cb0789e30162a27fbd8701d
!277 项目结构重构
Merge pull request !277 from MichelleChung/5.X
已删除280个文件
已添加310个文件
已重命名76个文件
已修改9个文件
51804 ■■■■ 文件已修改
pom.xml 85 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/pom.xml 22 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/java/com/ruoyi/web/controller/CaptchaController.java 22 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/java/com/ruoyi/web/controller/SysIndexController.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/java/com/ruoyi/web/controller/SysLoginController.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/java/com/ruoyi/web/controller/SysRegisterController.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/test/java/com/ruoyi/test/DemoUnitTest.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/test/java/com/ruoyi/test/ParamUnitTest.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/pom.xml 163 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-bom/pom.xml 129 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/pom.xml 115 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/annotation/Sensitive.java 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/config/ApplicationConfig.java 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/config/AsyncConfig.java 54 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/config/JacksonConfig.java 47 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/config/RuoYiConfig.java 54 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/config/ThreadPoolConfig.java 59 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/config/ValidatorConfig.java 43 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/config/properties/ThreadPoolProperties.java 32 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/constant/CacheConstants.java 49 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/constant/CacheNames.java 48 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/constant/Constants.java 76 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/constant/GenConstants.java 188 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/constant/HttpStatus.java 93 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/constant/UserConstants.java 138 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/domain/R.java 110 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/domain/dto/RoleDTO.java 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/domain/dto/UserOnlineDTO.java 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/domain/model/LoginBody.java 42 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/domain/model/LoginUser.java 118 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/domain/model/RegisterBody.java 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/domain/model/SmsLoginBody.java 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/domain/model/XcxLoginUser.java 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/enums/DeviceType.java 32 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/enums/LoginType.java 39 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/enums/SensitiveStrategy.java 49 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/enums/UserStatus.java 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/enums/UserType.java 37 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/exception/DemoModeException.java 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/exception/GlobalException.java 53 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/exception/ServiceException.java 67 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/exception/UtilException.java 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/exception/base/BaseException.java 79 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/exception/file/FileException.java 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/exception/file/FileNameLengthLimitExceededException.java 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/exception/file/FileSizeLimitExceededException.java 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/exception/user/CaptchaException.java 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/exception/user/CaptchaExpireException.java 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/exception/user/UserException.java 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/exception/user/UserPasswordNotMatchException.java 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/exception/user/UserPasswordRetryLimitExceedException.java 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/jackson/BigNumberSerializer.java 42 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/jackson/SensitiveJsonSerializer.java 54 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/manager/ShutdownManager.java 41 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/service/ConfigService.java 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/service/DictService.java 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/service/SensitiveService.java 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/BeanCopyUtils.java 204 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/DateUtils.java 168 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/JsonUtils.java 111 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/MessageUtils.java 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/ServletUtils.java 203 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/SpringUtils.java 74 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/StreamUtils.java 251 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/StringUtils.java 273 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/Threads.java 75 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/TreeBuildUtils.java 35 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/ValidatorUtils.java 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/file/FileUtils.java 52 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/file/MimeTypeUtils.java 40 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/ip/AddressUtils.java 60 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/reflect/ReflectUtils.java 56 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/sql/SqlUtil.java 57 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/validate/AddGroup.java 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/validate/EditGroup.java 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/validate/QueryGroup.java 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/web/controller/BaseController.java 40 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/web/domain/BaseEntity.java 65 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/web/domain/TreeEntity.java 41 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/xss/Xss.java 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/xss/XssValidator.java 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-dict/pom.xml 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-dict/src/main/java/com/ruoyi/common/dict/annotation/DictDataMapper.java 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-dict/src/main/java/com/ruoyi/common/dict/jackson/DictDataJsonSerializer.java 56 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-doc/pom.xml 36 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-doc/src/main/java/com/ruoyi/common/doc/config/SwaggerConfig.java 125 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-doc/src/main/java/com/ruoyi/common/doc/config/properties/SwaggerProperties.java 101 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-doc/src/main/java/com/ruoyi/common/doc/handler/OpenApiHandler.java 252 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-doc/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-excel/pom.xml 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-excel/src/main/java/com/ruoyi/common/excel/annotation/CellMerge.java 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-excel/src/main/java/com/ruoyi/common/excel/annotation/ExcelDictFormat.java 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-excel/src/main/java/com/ruoyi/common/excel/convert/ExcelBigNumberConvert.java 52 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-excel/src/main/java/com/ruoyi/common/excel/convert/ExcelDictConvert.java 73 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-excel/src/main/java/com/ruoyi/common/excel/core/CellMergeStrategy.java 114 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-excel/src/main/java/com/ruoyi/common/excel/core/DefaultExcelListener.java 106 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-excel/src/main/java/com/ruoyi/common/excel/core/DefautExcelResult.java 73 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-excel/src/main/java/com/ruoyi/common/excel/core/ExcelListener.java 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-excel/src/main/java/com/ruoyi/common/excel/core/ExcelResult.java 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-excel/src/main/java/com/ruoyi/common/excel/utils/ExcelUtil.java 328 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-idempotent/pom.xml 42 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-idempotent/src/main/java/com/ruoyi/common/idempotent/annotation/RepeatSubmit.java 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-idempotent/src/main/java/com/ruoyi/common/idempotent/aspectj/RepeatSubmitAspect.java 153 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-idempotent/src/main/java/com/ruoyi/common/idempotent/config/IdempotentConfig.java 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-idempotent/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-log/pom.xml 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-log/src/main/java/com/ruoyi/common/log/annotation/Log.java 41 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-log/src/main/java/com/ruoyi/common/log/aspect/LogAspect.java 192 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-log/src/main/java/com/ruoyi/common/log/enums/BusinessStatus.java 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-log/src/main/java/com/ruoyi/common/log/enums/BusinessType.java 58 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-log/src/main/java/com/ruoyi/common/log/enums/OperatorType.java 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-log/src/main/java/com/ruoyi/common/log/event/LogininforEvent.java 47 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-log/src/main/java/com/ruoyi/common/log/event/OperLogEvent.java 106 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-log/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-mail/pom.xml 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-mail/src/main/java/com/ruoyi/common/mail/config/MailConfig.java 37 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-mail/src/main/java/com/ruoyi/common/mail/config/properties/MailProperties.java 71 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-mail/src/main/java/com/ruoyi/common/mail/utils/MailUtils.java 468 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-mail/src/main/java/com/ruoyi/common/mail/utils/UserPassAuthenticator.java 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-mail/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-mybatis/pom.xml 48 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-mybatis/src/main/java/com/baomidou/dynamic/datasource/processor/jakarta/DsJakartaHeaderProcessor.java 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-mybatis/src/main/java/com/baomidou/dynamic/datasource/processor/jakarta/DsJakartaSessionProcessor.java 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-mybatis/src/main/java/com/ruoyi/common/mybatis/annotation/DataColumn.java 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-mybatis/src/main/java/com/ruoyi/common/mybatis/annotation/DataPermission.java 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-mybatis/src/main/java/com/ruoyi/common/mybatis/config/MybatisPlusConfig.java 102 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-mybatis/src/main/java/com/ruoyi/common/mybatis/core/mapper/BaseMapperPlus.java 192 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-mybatis/src/main/java/com/ruoyi/common/mybatis/core/page/PageQuery.java 114 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-mybatis/src/main/java/com/ruoyi/common/mybatis/core/page/TableDataInfo.java 81 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-mybatis/src/main/java/com/ruoyi/common/mybatis/enums/DataBaseType.java 49 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-mybatis/src/main/java/com/ruoyi/common/mybatis/enums/DataScopeType.java 72 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-mybatis/src/main/java/com/ruoyi/common/mybatis/handler/CreateAndUpdateMetaObjectHandler.java 79 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-mybatis/src/main/java/com/ruoyi/common/mybatis/handler/PlusDataPermissionHandler.java 199 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-mybatis/src/main/java/com/ruoyi/common/mybatis/helper/DataBaseHelper.java 72 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-mybatis/src/main/java/com/ruoyi/common/mybatis/helper/DataPermissionHelper.java 47 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-mybatis/src/main/java/com/ruoyi/common/mybatis/helper/MybatisExceptionHandler.java 46 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-mybatis/src/main/java/com/ruoyi/common/mybatis/interceptor/PlusDataPermissionInterceptor.java 108 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-mybatis/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-oss/pom.xml 36 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-oss/src/main/java/com/ruoyi/common/oss/constant/OssConstant.java 38 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-oss/src/main/java/com/ruoyi/common/oss/core/OssClient.java 240 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-oss/src/main/java/com/ruoyi/common/oss/entity/UploadResult.java 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-oss/src/main/java/com/ruoyi/common/oss/enumd/AccessPolicyType.java 55 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-oss/src/main/java/com/ruoyi/common/oss/enumd/PolicyType.java 35 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-oss/src/main/java/com/ruoyi/common/oss/exception/OssException.java 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-oss/src/main/java/com/ruoyi/common/oss/factory/OssFactory.java 79 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-oss/src/main/java/com/ruoyi/common/oss/properties/OssProperties.java 58 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-ratelimiter/pom.xml 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-ratelimiter/src/main/java/com/ruoyi/common/ratelimiter/annotation/RateLimiter.java 36 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-ratelimiter/src/main/java/com/ruoyi/common/ratelimiter/aspectj/RateLimiterAspect.java 64 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-ratelimiter/src/main/java/com/ruoyi/common/ratelimiter/config/RateLimiterConfig.java 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-ratelimiter/src/main/java/com/ruoyi/common/ratelimiter/enums/LimitType.java 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-ratelimiter/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-redis/pom.xml 38 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-redis/src/main/java/com/ruoyi/common/redis/config/RedisConfig.java 130 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-redis/src/main/java/com/ruoyi/common/redis/config/properties/RedissonProperties.java 137 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-redis/src/main/java/com/ruoyi/common/redis/handler/KeyPrefixHandler.java 50 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-redis/src/main/java/com/ruoyi/common/redis/manager/PlusSpringCacheManager.java 191 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-redis/src/main/java/com/ruoyi/common/redis/utils/CacheUtils.java 75 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-redis/src/main/java/com/ruoyi/common/redis/utils/QueueUtils.java 180 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-redis/src/main/java/com/ruoyi/common/redis/utils/RedisUtils.java 462 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-redis/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-satoken/pom.xml 39 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-satoken/src/main/java/com/ruoyi/common/satoken/config/SaTokenConfig.java 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-satoken/src/main/java/com/ruoyi/common/satoken/core/dao/PlusSaTokenDao.java 178 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-satoken/src/main/java/com/ruoyi/common/satoken/core/service/SaPermissionImpl.java 49 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-satoken/src/main/java/com/ruoyi/common/satoken/listener/UserActionListener.java 139 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-satoken/src/main/java/com/ruoyi/common/satoken/utils/LoginHelper.java 135 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-satoken/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-security/pom.xml 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-security/src/main/java/com/ruoyi/common/security/config/SecurityConfig.java 53 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-security/src/main/java/com/ruoyi/common/security/config/properties/SecurityProperties.java 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-security/src/main/java/com/ruoyi/common/security/handler/GlobalExceptionHandler.java 141 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-security/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-sms/pom.xml 37 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-sms/src/main/java/com/ruoyi/common/sms/config/SmsConfig.java 48 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-sms/src/main/java/com/ruoyi/common/sms/config/properties/SmsProperties.java 47 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-sms/src/main/java/com/ruoyi/common/sms/core/AliyunSmsTemplate.java 66 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-sms/src/main/java/com/ruoyi/common/sms/core/SmsTemplate.java 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-sms/src/main/java/com/ruoyi/common/sms/core/TencentSmsTemplate.java 82 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-sms/src/main/java/com/ruoyi/common/sms/entity/SmsResult.java 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-sms/src/main/java/com/ruoyi/common/sms/exception/SmsException.java 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-sms/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-web/pom.xml 58 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-web/src/main/java/com/ruoyi/common/web/config/CaptchaConfig.java 62 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-web/src/main/java/com/ruoyi/common/web/config/FilterConfig.java 55 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-web/src/main/java/com/ruoyi/common/web/config/I18nConfig.java 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-web/src/main/java/com/ruoyi/common/web/config/ResourcesConfig.java 52 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-web/src/main/java/com/ruoyi/common/web/config/UndertowConfig.java 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-web/src/main/java/com/ruoyi/common/web/config/properties/CaptchaProperties.java 38 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-web/src/main/java/com/ruoyi/common/web/config/properties/XssProperties.java 32 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-web/src/main/java/com/ruoyi/common/web/core/I18nLocaleResolver.java 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-web/src/main/java/com/ruoyi/common/web/enums/CaptchaCategory.java 35 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-web/src/main/java/com/ruoyi/common/web/enums/CaptchaType.java 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-web/src/main/java/com/ruoyi/common/web/filter/RepeatableFilter.java 40 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-web/src/main/java/com/ruoyi/common/web/filter/RepeatedlyRequestWrapper.java 67 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-web/src/main/java/com/ruoyi/common/web/filter/XssFilter.java 62 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-web/src/main/java/com/ruoyi/common/web/filter/XssHttpServletRequestWrapper.java 97 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-web/src/main/java/com/ruoyi/common/web/interceptor/PlusWebInvokeTimeInterceptor.java 94 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-web/src/main/java/com/ruoyi/common/web/utils/UnsignedMathGenerator.java 88 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-web/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/annotation/CellMerge.java 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/annotation/DataColumn.java 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/annotation/DataPermission.java 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/annotation/DictDataMapper.java 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/annotation/ExcelDictFormat.java 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/annotation/Log.java 41 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/annotation/RateLimiter.java 36 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/annotation/RepeatSubmit.java 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/annotation/Sensitive.java 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/captcha/UnsignedMathGenerator.java 88 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/config/RuoYiConfig.java 54 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/constant/CacheConstants.java 49 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/constant/CacheNames.java 48 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/constant/Constants.java 76 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/constant/GenConstants.java 188 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/constant/HttpStatus.java 93 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/constant/UserConstants.java 138 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/convert/ExcelBigNumberConvert.java 52 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/convert/ExcelDictConvert.java 73 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/core/controller/BaseController.java 69 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/core/domain/BaseEntity.java 65 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/core/domain/PageQuery.java 114 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/core/domain/R.java 110 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/core/domain/TreeEntity.java 41 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/core/domain/event/LogininforEvent.java 47 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/core/domain/event/OperLogEvent.java 106 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/core/domain/model/LoginBody.java 42 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/core/domain/model/LoginUser.java 119 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/core/mapper/BaseMapperPlus.java 192 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/core/page/TableDataInfo.java 81 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/enums/BusinessStatus.java 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/enums/BusinessType.java 58 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/enums/CaptchaCategory.java 35 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/enums/CaptchaType.java 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/enums/DataBaseType.java 49 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/enums/DataScopeType.java 72 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/enums/DeviceType.java 32 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/enums/HttpMethod.java 32 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/enums/LimitType.java 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/enums/LoginType.java 39 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/enums/OperatorType.java 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/enums/SensitiveStrategy.java 49 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/enums/UserStatus.java 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/enums/UserType.java 37 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/excel/CellMergeStrategy.java 114 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/excel/DefaultExcelListener.java 106 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/excel/DefautExcelResult.java 73 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/excel/ExcelListener.java 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/excel/ExcelResult.java 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/exception/DemoModeException.java 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/exception/GlobalException.java 53 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/exception/ServiceException.java 67 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/exception/UtilException.java 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/exception/base/BaseException.java 79 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/exception/file/FileException.java 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/exception/file/FileNameLengthLimitExceededException.java 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/exception/file/FileSizeLimitExceededException.java 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/exception/user/CaptchaException.java 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/exception/user/CaptchaExpireException.java 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/exception/user/UserException.java 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/exception/user/UserPasswordNotMatchException.java 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/exception/user/UserPasswordRetryLimitExceedException.java 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/filter/RepeatableFilter.java 40 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/filter/RepeatedlyRequestWrapper.java 67 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/filter/XssFilter.java 62 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/filter/XssHttpServletRequestWrapper.java 97 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/helper/DataBaseHelper.java 72 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/helper/DataPermissionHelper.java 47 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/helper/LoginHelper.java 136 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/jackson/DictDataJsonSerializer.java 56 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/jackson/SensitiveJsonSerializer.java 54 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/utils/BeanCopyUtils.java 204 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/utils/DateUtils.java 168 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/utils/JsonUtils.java 112 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/utils/MessageUtils.java 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/utils/ServletUtils.java 203 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/utils/StreamUtils.java 251 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/utils/StringUtils.java 273 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/utils/Threads.java 75 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/utils/TreeBuildUtils.java 35 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/utils/ValidatorUtils.java 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/utils/email/MailUtils.java 468 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/utils/email/UserPassAuthenticator.java 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/utils/file/FileUtils.java 52 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/utils/file/MimeTypeUtils.java 40 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/utils/ip/AddressUtils.java 60 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/utils/poi/ExcelUtil.java 328 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/utils/redis/CacheUtils.java 75 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/utils/redis/QueueUtils.java 180 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/utils/redis/RedisUtils.java 462 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/utils/reflect/ReflectUtils.java 56 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/utils/spring/SpringUtils.java 74 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/utils/sql/SqlUtil.java 57 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/xss/Xss.java 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/xss/XssValidator.java 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-demo/pom.xml 45 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-demo/src/main/java/com/ruoyi/demo/controller/MailController.java 52 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-demo/src/main/java/com/ruoyi/demo/controller/RedisCacheController.java 95 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-demo/src/main/java/com/ruoyi/demo/controller/RedisPubSubController.java 47 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-demo/src/main/java/com/ruoyi/demo/controller/RedisRateLimiterController.java 52 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-demo/src/main/java/com/ruoyi/demo/controller/SmsController.java 76 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-demo/src/main/java/com/ruoyi/demo/controller/TestBatchController.java 90 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-demo/src/main/java/com/ruoyi/demo/controller/TestDemoController.java 148 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-demo/src/main/java/com/ruoyi/demo/controller/TestExcelController.java 97 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-demo/src/main/java/com/ruoyi/demo/controller/TestI18nController.java 71 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-demo/src/main/java/com/ruoyi/demo/controller/TestSensitiveController.java 75 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-demo/src/main/java/com/ruoyi/demo/controller/TestTreeController.java 107 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-demo/src/main/java/com/ruoyi/demo/controller/queue/BoundedQueueController.java 90 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-demo/src/main/java/com/ruoyi/demo/controller/queue/DelayedQueueController.java 90 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-demo/src/main/java/com/ruoyi/demo/controller/queue/PriorityQueueController.java 89 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-demo/src/main/java/com/ruoyi/demo/domain/TestDemo.java 68 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-demo/src/main/java/com/ruoyi/demo/domain/TestTree.java 60 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-demo/src/main/java/com/ruoyi/demo/domain/bo/TestDemoBo.java 59 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-demo/src/main/java/com/ruoyi/demo/domain/bo/TestTreeBo.java 47 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-demo/src/main/java/com/ruoyi/demo/mapper/TestDemoMapper.java 58 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-demo/src/main/java/com/ruoyi/demo/mapper/TestTreeMapper.java 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-demo/src/main/java/com/ruoyi/demo/service/ITestDemoService.java 71 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-demo/src/main/java/com/ruoyi/demo/service/impl/TestDemoServiceImpl.java 110 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-demo/src/main/java/com/ruoyi/demo/service/impl/TestTreeServiceImpl.java 87 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-framework/pom.xml 73 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/LogAspect.java 192 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/RateLimiterAspect.java 66 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/RepeatSubmitAspect.java 159 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-framework/src/main/java/com/ruoyi/framework/config/ApplicationConfig.java 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-framework/src/main/java/com/ruoyi/framework/config/AsyncConfig.java 54 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-framework/src/main/java/com/ruoyi/framework/config/CaptchaConfig.java 62 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-framework/src/main/java/com/ruoyi/framework/config/FilterConfig.java 55 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-framework/src/main/java/com/ruoyi/framework/config/I18nConfig.java 46 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-framework/src/main/java/com/ruoyi/framework/config/JacksonConfig.java 46 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-framework/src/main/java/com/ruoyi/framework/config/MailConfig.java 35 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-framework/src/main/java/com/ruoyi/framework/config/MybatisPlusConfig.java 102 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-framework/src/main/java/com/ruoyi/framework/config/RedisConfig.java 130 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-framework/src/main/java/com/ruoyi/framework/config/ResourcesConfig.java 52 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-framework/src/main/java/com/ruoyi/framework/config/SaTokenConfig.java 62 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-framework/src/main/java/com/ruoyi/framework/config/SwaggerConfig.java 127 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-framework/src/main/java/com/ruoyi/framework/config/ThreadPoolConfig.java 59 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-framework/src/main/java/com/ruoyi/framework/config/UndertowConfig.java 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-framework/src/main/java/com/ruoyi/framework/config/ValidatorConfig.java 43 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-framework/src/main/java/com/ruoyi/framework/config/properties/CaptchaProperties.java 38 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-framework/src/main/java/com/ruoyi/framework/config/properties/MailProperties.java 71 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-framework/src/main/java/com/ruoyi/framework/config/properties/RedissonProperties.java 137 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-framework/src/main/java/com/ruoyi/framework/config/properties/SecurityProperties.java 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-framework/src/main/java/com/ruoyi/framework/config/properties/SwaggerProperties.java 101 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-framework/src/main/java/com/ruoyi/framework/config/properties/ThreadPoolProperties.java 32 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-framework/src/main/java/com/ruoyi/framework/config/properties/XssProperties.java 32 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-framework/src/main/java/com/ruoyi/framework/handler/CreateAndUpdateMetaObjectHandler.java 79 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-framework/src/main/java/com/ruoyi/framework/handler/KeyPrefixHandler.java 50 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-framework/src/main/java/com/ruoyi/framework/handler/OpenApiHandler.java 252 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-framework/src/main/java/com/ruoyi/framework/handler/PlusDataPermissionHandler.java 199 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-framework/src/main/java/com/ruoyi/framework/interceptor/PlusDataPermissionInterceptor.java 108 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-framework/src/main/java/com/ruoyi/framework/interceptor/PlusWebInvokeTimeInterceptor.java 94 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-framework/src/main/java/com/ruoyi/framework/jackson/BigNumberSerializer.java 42 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-framework/src/main/java/com/ruoyi/framework/listener/UserActionListener.java 139 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-framework/src/main/java/com/ruoyi/framework/manager/PlusSpringCacheManager.java 191 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-framework/src/main/java/com/ruoyi/framework/manager/ShutdownManager.java 41 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-framework/src/main/java/com/ruoyi/framework/satoken/dao/PlusSaTokenDao.java 178 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-framework/src/main/java/com/ruoyi/framework/satoken/service/SaPermissionImpl.java 49 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-framework/src/main/java/com/ruoyi/framework/web/exception/GlobalExceptionHandler.java 168 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-generator/pom.xml 35 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-generator/src/main/java/com/ruoyi/generator/controller/GenController.java 207 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-generator/src/main/java/com/ruoyi/generator/domain/GenTable.java 195 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-generator/src/main/java/com/ruoyi/generator/domain/GenTableColumn.java 223 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-generator/src/main/java/com/ruoyi/generator/mapper/GenTableColumnMapper.java 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-generator/src/main/java/com/ruoyi/generator/mapper/GenTableMapper.java 58 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-generator/src/main/java/com/ruoyi/generator/service/GenTableServiceImpl.java 461 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-generator/src/main/java/com/ruoyi/generator/service/IGenTableService.java 133 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-generator/src/main/java/com/ruoyi/generator/util/GenUtils.java 232 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-generator/src/main/java/com/ruoyi/generator/util/VelocityInitializer.java 35 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-generator/src/main/java/com/ruoyi/generator/util/VelocityUtils.java 335 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-generator/src/main/resources/generator.yml 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-generator/src/main/resources/mapper/generator/GenTableColumnMapper.xml 92 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-generator/src/main/resources/mapper/generator/GenTableMapper.xml 220 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-generator/src/main/resources/vm/java/bo.java.vm 60 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-generator/src/main/resources/vm/java/controller.java.vm 118 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-generator/src/main/resources/vm/java/domain.java.vm 57 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-generator/src/main/resources/vm/java/mapper.java.vm 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-generator/src/main/resources/vm/java/service.java.vm 53 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-generator/src/main/resources/vm/java/serviceImpl.java.vm 134 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-generator/src/main/resources/vm/java/vo.java.vm 56 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-job/pom.xml 36 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/pom.xml 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-demo/pom.xml 75 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-demo/src/main/java/com/ruoyi/demo/controller/MailController.java 52 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-demo/src/main/java/com/ruoyi/demo/controller/RedisCacheController.java 95 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-demo/src/main/java/com/ruoyi/demo/controller/RedisLockController.java 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-demo/src/main/java/com/ruoyi/demo/controller/RedisPubSubController.java 47 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-demo/src/main/java/com/ruoyi/demo/controller/RedisRateLimiterController.java 52 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-demo/src/main/java/com/ruoyi/demo/controller/SmsController.java 76 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-demo/src/main/java/com/ruoyi/demo/controller/Swagger3DemoController.java 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-demo/src/main/java/com/ruoyi/demo/controller/TestBatchController.java 90 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-demo/src/main/java/com/ruoyi/demo/controller/TestDemoController.java 148 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-demo/src/main/java/com/ruoyi/demo/controller/TestExcelController.java 97 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-demo/src/main/java/com/ruoyi/demo/controller/TestI18nController.java 71 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-demo/src/main/java/com/ruoyi/demo/controller/TestSensitiveController.java 75 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-demo/src/main/java/com/ruoyi/demo/controller/TestTreeController.java 107 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-demo/src/main/java/com/ruoyi/demo/controller/package-info.java 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-demo/src/main/java/com/ruoyi/demo/controller/queue/BoundedQueueController.java 90 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-demo/src/main/java/com/ruoyi/demo/controller/queue/DelayedQueueController.java 90 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-demo/src/main/java/com/ruoyi/demo/controller/queue/PriorityDemo.java 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-demo/src/main/java/com/ruoyi/demo/controller/queue/PriorityQueueController.java 89 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-demo/src/main/java/com/ruoyi/demo/domain/TestDemo.java 68 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-demo/src/main/java/com/ruoyi/demo/domain/TestTree.java 60 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-demo/src/main/java/com/ruoyi/demo/domain/bo/TestDemoBo.java 59 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-demo/src/main/java/com/ruoyi/demo/domain/bo/TestDemoImportVo.java 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-demo/src/main/java/com/ruoyi/demo/domain/bo/TestTreeBo.java 47 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-demo/src/main/java/com/ruoyi/demo/domain/package-info.java 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-demo/src/main/java/com/ruoyi/demo/domain/vo/TestDemoVo.java 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-demo/src/main/java/com/ruoyi/demo/domain/vo/TestTreeVo.java 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-demo/src/main/java/com/ruoyi/demo/mapper/TestDemoMapper.java 58 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-demo/src/main/java/com/ruoyi/demo/mapper/TestTreeMapper.java 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-demo/src/main/java/com/ruoyi/demo/mapper/package-info.java 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-demo/src/main/java/com/ruoyi/demo/service/ITestDemoService.java 71 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-demo/src/main/java/com/ruoyi/demo/service/ITestTreeService.java 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-demo/src/main/java/com/ruoyi/demo/service/impl/TestDemoServiceImpl.java 110 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-demo/src/main/java/com/ruoyi/demo/service/impl/TestTreeServiceImpl.java 87 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-demo/src/main/java/com/ruoyi/demo/service/impl/package-info.java 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-demo/src/main/java/com/ruoyi/demo/service/package-info.java 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-demo/src/main/resources/excel/单列表.xlsx 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-demo/src/main/resources/excel/多列表.xlsx 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-demo/src/main/resources/mapper/demo/TestDemoMapper.xml 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-demo/src/main/resources/mapper/demo/TestTreeMapper.xml 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-demo/src/main/resources/mapper/package-info.md 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-generator/pom.xml 48 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-generator/src/main/java/com/ruoyi/generator/config/GenConfig.java 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-generator/src/main/java/com/ruoyi/generator/controller/GenController.java 207 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-generator/src/main/java/com/ruoyi/generator/domain/GenTable.java 192 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-generator/src/main/java/com/ruoyi/generator/domain/GenTableColumn.java 223 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-generator/src/main/java/com/ruoyi/generator/mapper/GenTableColumnMapper.java 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-generator/src/main/java/com/ruoyi/generator/mapper/GenTableMapper.java 58 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-generator/src/main/java/com/ruoyi/generator/service/GenTableServiceImpl.java 461 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-generator/src/main/java/com/ruoyi/generator/service/IGenTableService.java 133 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-generator/src/main/java/com/ruoyi/generator/util/GenUtils.java 232 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-generator/src/main/java/com/ruoyi/generator/util/VelocityInitializer.java 35 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-generator/src/main/java/com/ruoyi/generator/util/VelocityUtils.java 336 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-generator/src/main/resources/generator.yml 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-generator/src/main/resources/mapper/generator/GenTableColumnMapper.xml 92 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-generator/src/main/resources/mapper/generator/GenTableMapper.xml 220 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-generator/src/main/resources/mapper/package-info.md 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-generator/src/main/resources/vm/java/bo.java.vm 60 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-generator/src/main/resources/vm/java/controller.java.vm 117 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-generator/src/main/resources/vm/java/domain.java.vm 57 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-generator/src/main/resources/vm/java/mapper.java.vm 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-generator/src/main/resources/vm/java/service.java.vm 53 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-generator/src/main/resources/vm/java/serviceImpl.java.vm 134 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-generator/src/main/resources/vm/java/vo.java.vm 56 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-generator/src/main/resources/vm/js/api.js.vm 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-generator/src/main/resources/vm/sql/oracle/sql.vm 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-generator/src/main/resources/vm/sql/postgres/sql.vm 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-generator/src/main/resources/vm/sql/sql.vm 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-generator/src/main/resources/vm/sql/sqlserver/sql.vm 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-generator/src/main/resources/vm/vue/index-tree.vue.vm 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-generator/src/main/resources/vm/vue/index.vue.vm 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-generator/src/main/resources/vm/vue/v3/index-tree.vue.vm 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-generator/src/main/resources/vm/vue/v3/index.vue.vm 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-generator/src/main/resources/vm/vue/v3/readme.txt 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-generator/src/main/resources/vm/xml/mapper.xml.vm 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-job/pom.xml 36 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-job/src/main/java/com/ruoyi/job/config/XxlJobConfig.java 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-job/src/main/java/com/ruoyi/job/config/properties/XxlJobProperties.java 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-job/src/main/java/com/ruoyi/job/service/SampleService.java 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/pom.xml 80 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/controller/monitor/CacheController.java 169 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/controller/monitor/SysLogininforController.java 88 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/controller/monitor/SysOperlogController.java 74 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/controller/monitor/SysUserOnlineController.java 90 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/controller/system/SysConfigController.java 137 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/controller/system/SysDeptController.java 119 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/controller/system/SysDictDataController.java 116 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/controller/system/SysDictTypeController.java 125 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/controller/system/SysMenuController.java 128 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/controller/system/SysNoticeController.java 80 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/controller/system/SysOssConfigController.java 105 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/controller/system/SysOssController.java 114 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/controller/system/SysPostController.java 115 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/controller/system/SysProfileController.java 127 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/controller/system/SysRoleController.java 237 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/controller/system/SysUserController.java 253 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysCache.java 47 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysConfig.java 71 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysDept.java 83 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysDictData.java 100 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysDictType.java 65 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysLogininfor.java 94 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysMenu.java 104 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysNotice.java 58 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysOperLog.java 144 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysOss.java 50 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysOssConfig.java 89 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysPost.java 78 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysRole.java 131 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysRoleDept.java 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysRoleMenu.java 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysUser.java 160 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysUserOnline.java 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysUserPost.java 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysUserRole.java 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/bo/SysOssBo.java 37 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/bo/SysOssConfigBo.java 107 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/MetaVo.java 61 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/RouterVo.java 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/SysOssConfigVo.java 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/SysOssVo.java 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/SysUserExportVo.java 94 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/SysUserImportVo.java 76 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/listener/SysUserImportListener.java 118 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysConfigMapper.java 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysDeptMapper.java 40 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysDictDataMapper.java 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysDictTypeMapper.java 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysLogininforMapper.java 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysMenuMapper.java 82 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysNoticeMapper.java 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysOperLogMapper.java 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysOssConfigMapper.java 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysOssMapper.java 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysPostMapper.java 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysRoleDeptMapper.java 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysRoleMapper.java 62 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysRoleMenuMapper.java 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysUserMapper.java 87 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysUserPostMapper.java 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysUserRoleMapper.java 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/runner/SystemApplicationRunner.java 41 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysConfigService.java 96 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysDataScopeService.java 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysDeptService.java 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysDictDataService.java 66 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysDictTypeService.java 104 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysLogininforService.java 46 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysMenuService.java 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysNoticeService.java 66 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysOperLogService.java 53 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysOssConfigService.java 65 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysOssService.java 34 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysPostService.java 105 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysRoleService.java 179 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysUserService.java 211 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/SysLoginService.java 294 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/SysPermissionService.java 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/SysRegisterService.java 106 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysConfigServiceImpl.java 227 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysDataScopeServiceImpl.java 61 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysDeptServiceImpl.java 278 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysDictDataServiceImpl.java 130 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysDictTypeServiceImpl.java 285 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysLogininforServiceImpl.java 155 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysMenuServiceImpl.java 446 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysNoticeServiceImpl.java 106 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysOperLogServiceImpl.java 141 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysOssConfigServiceImpl.java 189 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysOssServiceImpl.java 161 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysPostServiceImpl.java 184 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysRoleServiceImpl.java 412 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysSensitiveServiceImpl.java 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysUserServiceImpl.java 488 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/resources/mapper/package-info.md 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysConfigMapper.xml 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysDeptMapper.xml 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysDictDataMapper.xml 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysDictTypeMapper.xml 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysLogininforMapper.xml 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysMenuMapper.xml 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysNoticeMapper.xml 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysOperLogMapper.xml 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysOssConfigMapper.xml 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysOssMapper.xml 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysPostMapper.xml 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysRoleDeptMapper.xml 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysRoleMapper.xml 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysRoleMenuMapper.xml 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysUserMapper.xml 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysUserPostMapper.xml 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysUserRoleMapper.xml 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-oss/pom.xml 34 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-oss/src/main/java/com/ruoyi/oss/constant/OssConstant.java 38 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-oss/src/main/java/com/ruoyi/oss/core/OssClient.java 240 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-oss/src/main/java/com/ruoyi/oss/entity/UploadResult.java 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-oss/src/main/java/com/ruoyi/oss/enumd/AccessPolicyType.java 55 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-oss/src/main/java/com/ruoyi/oss/enumd/PolicyType.java 35 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-oss/src/main/java/com/ruoyi/oss/exception/OssException.java 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-oss/src/main/java/com/ruoyi/oss/factory/OssFactory.java 79 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-oss/src/main/java/com/ruoyi/oss/properties/OssProperties.java 58 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-sms/pom.xml 41 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-sms/src/main/java/com/ruoyi/sms/config/SmsConfig.java 45 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-sms/src/main/java/com/ruoyi/sms/config/properties/SmsProperties.java 47 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-sms/src/main/java/com/ruoyi/sms/core/AliyunSmsTemplate.java 66 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-sms/src/main/java/com/ruoyi/sms/core/SmsTemplate.java 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-sms/src/main/java/com/ruoyi/sms/core/TencentSmsTemplate.java 82 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-sms/src/main/java/com/ruoyi/sms/entity/SmsResult.java 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-sms/src/main/java/com/ruoyi/sms/exception/SmsException.java 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/pom.xml 41 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/controller/monitor/CacheController.java 169 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/controller/monitor/SysLogininforController.java 88 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/controller/monitor/SysOperlogController.java 74 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/controller/monitor/SysUserOnlineController.java 90 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/controller/system/SysConfigController.java 137 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/controller/system/SysDeptController.java 119 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/controller/system/SysDictDataController.java 116 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/controller/system/SysDictTypeController.java 125 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/controller/system/SysMenuController.java 127 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/controller/system/SysNoticeController.java 80 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/controller/system/SysOssConfigController.java 105 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/controller/system/SysOssController.java 117 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/controller/system/SysPostController.java 115 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/controller/system/SysProfileController.java 127 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/controller/system/SysRoleController.java 237 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/controller/system/SysUserController.java 253 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/domain/SysCache.java 47 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/domain/SysConfig.java 71 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/domain/SysDept.java 83 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/domain/SysDictData.java 100 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/domain/SysDictType.java 65 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/domain/SysLogininfor.java 94 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/domain/SysMenu.java 104 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/domain/SysNotice.java 58 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/domain/SysOperLog.java 144 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/domain/SysOss.java 50 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/domain/SysOssConfig.java 89 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/domain/SysPost.java 78 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/domain/SysRole.java 131 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/domain/SysUser.java 160 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/domain/bo/SysOssBo.java 37 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/domain/bo/SysOssConfigBo.java 107 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/MetaVo.java 61 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/SysUserExportVo.java 94 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/SysUserImportVo.java 76 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/listener/SysUserImportListener.java 118 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysConfigMapper.java 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysDeptMapper.java 40 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysDictDataMapper.java 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysDictTypeMapper.java 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysLogininforMapper.java 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysMenuMapper.java 82 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysNoticeMapper.java 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysOperLogMapper.java 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysOssConfigMapper.java 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysOssMapper.java 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysPostMapper.java 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysRoleDeptMapper.java 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysRoleMapper.java 62 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysRoleMenuMapper.java 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysUserMapper.java 87 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysUserPostMapper.java 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysUserRoleMapper.java 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/runner/SystemApplicationRunner.java 41 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/service/ISysConfigService.java 96 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/service/ISysDictDataService.java 66 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/service/ISysDictTypeService.java 104 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/service/ISysLogininforService.java 46 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/service/ISysNoticeService.java 66 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/service/ISysOperLogService.java 53 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/service/ISysOssConfigService.java 65 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/service/ISysOssService.java 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/service/ISysPostService.java 105 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/service/ISysRoleService.java 179 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/service/ISysUserService.java 211 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/service/SysLoginService.java 294 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/service/SysRegisterService.java 106 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysConfigServiceImpl.java 227 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysDataScopeServiceImpl.java 61 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysDeptServiceImpl.java 278 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysDictDataServiceImpl.java 130 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysDictTypeServiceImpl.java 285 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysLogininforServiceImpl.java 155 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysMenuServiceImpl.java 446 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysNoticeServiceImpl.java 106 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysOperLogServiceImpl.java 141 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysOssConfigServiceImpl.java 189 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysOssServiceImpl.java 161 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysPostServiceImpl.java 184 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysRoleServiceImpl.java 412 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysSensitiveServiceImpl.java 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysUserServiceImpl.java 488 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pom.xml
@@ -106,6 +106,15 @@
                <scope>import</scope>
            </dependency>
            <!-- common çš„依赖配置-->
            <dependency>
                <groupId>com.ruoyi</groupId>
                <artifactId>ruoyi-common-bom</artifactId>
                <version>${revision}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <dependency>
                <groupId>org.springdoc</groupId>
                <artifactId>springdoc-openapi-starter-webmvc-api</artifactId>
@@ -171,6 +180,11 @@
                    </exclusion>
                </exclusions>
            </dependency>
            <dependency>
                <groupId>cn.dev33</groupId>
                <artifactId>sa-token-core</artifactId>
                <version>${satoken.version}</version>
            </dependency>
            <!-- dynamic-datasource å¤šæ•°æ®æº-->
            <dependency>
@@ -188,6 +202,12 @@
            <dependency>
                <groupId>com.baomidou</groupId>
                <artifactId>mybatis-plus-boot-starter</artifactId>
                <version>${mybatis-plus.version}</version>
            </dependency>
            <dependency>
                <groupId>com.baomidou</groupId>
                <artifactId>mybatis-plus-annotation</artifactId>
                <version>${mybatis-plus.version}</version>
            </dependency>
@@ -265,77 +285,14 @@
                <artifactId>snakeyaml</artifactId>
                <version>${snakeyaml.version}</version>
            </dependency>
            <!-- å®šæ—¶ä»»åŠ¡ -->
            <dependency>
                <groupId>com.ruoyi</groupId>
                <artifactId>ruoyi-job</artifactId>
                <version>${revision}</version>
            </dependency>
            <!-- ä»£ç ç”Ÿæˆ-->
            <dependency>
                <groupId>com.ruoyi</groupId>
                <artifactId>ruoyi-generator</artifactId>
                <version>${revision}</version>
            </dependency>
            <!-- æ ¸å¿ƒæ¨¡å—-->
            <dependency>
                <groupId>com.ruoyi</groupId>
                <artifactId>ruoyi-framework</artifactId>
                <version>${revision}</version>
            </dependency>
            <!-- ç³»ç»Ÿæ¨¡å—-->
            <dependency>
                <groupId>com.ruoyi</groupId>
                <artifactId>ruoyi-system</artifactId>
                <version>${revision}</version>
            </dependency>
            <!-- é€šç”¨å·¥å…·-->
            <dependency>
                <groupId>com.ruoyi</groupId>
                <artifactId>ruoyi-common</artifactId>
                <version>${revision}</version>
            </dependency>
            <!-- OSS对象存储模块 -->
            <dependency>
                <groupId>com.ruoyi</groupId>
                <artifactId>ruoyi-oss</artifactId>
                <version>${revision}</version>
            </dependency>
            <!-- SMS短信模块 -->
            <dependency>
                <groupId>com.ruoyi</groupId>
                <artifactId>ruoyi-sms</artifactId>
                <version>${revision}</version>
            </dependency>
            <!-- demo模块 -->
            <dependency>
                <groupId>com.ruoyi</groupId>
                <artifactId>ruoyi-demo</artifactId>
                <version>${revision}</version>
            </dependency>
        </dependencies>
    </dependencyManagement>
    <modules>
        <module>ruoyi-admin</module>
        <module>ruoyi-framework</module>
        <module>ruoyi-system</module>
        <module>ruoyi-job</module>
        <module>ruoyi-generator</module>
        <module>ruoyi-common</module>
        <module>ruoyi-demo</module>
        <module>ruoyi-extend</module>
        <module>ruoyi-oss</module>
        <module>ruoyi-sms</module>
        <module>ruoyi-modules</module>
    </modules>
    <packaging>pom</packaging>
ruoyi-admin/pom.xml
@@ -46,15 +46,24 @@
            <artifactId>mssql-jdbc</artifactId>
        </dependency>
        <!-- æ ¸å¿ƒæ¨¡å—-->
        <dependency>
            <groupId>com.ruoyi</groupId>
            <artifactId>ruoyi-framework</artifactId>
            <artifactId>ruoyi-system</artifactId>
        </dependency>
        <dependency>
            <groupId>com.ruoyi</groupId>
            <artifactId>ruoyi-system</artifactId>
            <artifactId>ruoyi-common-doc</artifactId>
        </dependency>
        <dependency>
            <groupId>com.ruoyi</groupId>
            <artifactId>ruoyi-common-satoken</artifactId>
        </dependency>
        <dependency>
            <groupId>com.ruoyi</groupId>
            <artifactId>ruoyi-common-web</artifactId>
        </dependency>
        <dependency>
@@ -64,7 +73,12 @@
        <dependency>
            <groupId>com.ruoyi</groupId>
            <artifactId>ruoyi-oss</artifactId>
            <artifactId>ruoyi-common-log</artifactId>
        </dependency>
        <dependency>
            <groupId>com.ruoyi</groupId>
            <artifactId>ruoyi-common-oss</artifactId>
        </dependency>
        <!-- ä»£ç ç”Ÿæˆ-->
ruoyi-admin/src/main/java/com/ruoyi/web/controller/CaptchaController.java
@@ -5,18 +5,18 @@
import cn.hutool.captcha.generator.CodeGenerator;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.RandomUtil;
import com.ruoyi.common.constant.CacheConstants;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.core.constant.CacheConstants;
import com.ruoyi.common.core.constant.Constants;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.enums.CaptchaType;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.redis.RedisUtils;
import com.ruoyi.common.utils.reflect.ReflectUtils;
import com.ruoyi.common.utils.spring.SpringUtils;
import com.ruoyi.framework.config.properties.CaptchaProperties;
import com.ruoyi.sms.config.properties.SmsProperties;
import com.ruoyi.sms.core.SmsTemplate;
import com.ruoyi.sms.entity.SmsResult;
import com.ruoyi.common.core.utils.SpringUtils;
import com.ruoyi.common.core.utils.StringUtils;
import com.ruoyi.common.core.utils.reflect.ReflectUtils;
import com.ruoyi.common.redis.utils.RedisUtils;
import com.ruoyi.common.sms.config.properties.SmsProperties;
import com.ruoyi.common.sms.core.SmsTemplate;
import com.ruoyi.common.sms.entity.SmsResult;
import com.ruoyi.common.web.config.properties.CaptchaProperties;
import com.ruoyi.common.web.enums.CaptchaType;
import com.ruoyi.system.service.ISysConfigService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
ruoyi-admin/src/main/java/com/ruoyi/web/controller/SysIndexController.java
@@ -1,8 +1,8 @@
package com.ruoyi.web.controller;
import cn.dev33.satoken.annotation.SaIgnore;
import com.ruoyi.common.config.RuoYiConfig;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.core.config.RuoYiConfig;
import com.ruoyi.common.core.utils.StringUtils;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
ruoyi-admin/src/main/java/com/ruoyi/web/controller/SysLoginController.java
@@ -1,14 +1,14 @@
package com.ruoyi.web.controller;
import cn.dev33.satoken.annotation.SaIgnore;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.core.constant.Constants;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.satoken.utils.LoginHelper;
import com.ruoyi.system.domain.SysMenu;
import com.ruoyi.system.domain.SysUser;
import com.ruoyi.common.core.domain.model.LoginBody;
import com.ruoyi.common.core.domain.model.LoginUser;
import com.ruoyi.common.core.domain.model.SmsLoginBody;
import com.ruoyi.common.helper.LoginHelper;
import com.ruoyi.system.domain.vo.RouterVo;
import com.ruoyi.system.service.ISysMenuService;
import com.ruoyi.system.service.ISysUserService;
ruoyi-admin/src/main/java/com/ruoyi/web/controller/SysRegisterController.java
@@ -1,7 +1,7 @@
package com.ruoyi.web.controller;
import cn.dev33.satoken.annotation.SaIgnore;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.web.controller.BaseController;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.core.domain.model.RegisterBody;
import com.ruoyi.system.service.ISysConfigService;
ruoyi-admin/src/test/java/com/ruoyi/test/DemoUnitTest.java
@@ -1,6 +1,6 @@
package com.ruoyi.test;
import com.ruoyi.common.config.RuoYiConfig;
import com.ruoyi.common.core.config.RuoYiConfig;
import org.junit.jupiter.api.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
ruoyi-admin/src/test/java/com/ruoyi/test/ParamUnitTest.java
@@ -1,6 +1,6 @@
package com.ruoyi.test;
import com.ruoyi.common.enums.UserType;
import com.ruoyi.common.core.enums.UserType;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
ruoyi-common/pom.xml
@@ -10,151 +10,30 @@
    </parent>
    <modelVersion>4.0.0</modelVersion>
    <modules>
        <module>ruoyi-common-bom</module>
        <module>ruoyi-common-core</module>
        <module>ruoyi-common-dict</module>
        <module>ruoyi-common-doc</module>
        <module>ruoyi-common-excel</module>
        <module>ruoyi-common-idempotent</module>
        <module>ruoyi-common-log</module>
        <module>ruoyi-common-mail</module>
        <module>ruoyi-common-mybatis</module>
        <module>ruoyi-common-oss</module>
        <module>ruoyi-common-ratelimiter</module>
        <module>ruoyi-common-redis</module>
        <module>ruoyi-common-satoken</module>
        <module>ruoyi-common-security</module>
        <module>ruoyi-common-sms</module>
        <module>ruoyi-common-web</module>
    </modules>
    <artifactId>ruoyi-common</artifactId>
    <packaging>pom</packaging>
    <description>
        common通用工具
        common é€šç”¨æ¨¡å—
    </description>
    <dependencies>
        <!-- Spring框架基本的核心工具 -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context-support</artifactId>
        </dependency>
        <!-- SpringWeb模块 -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-web</artifactId>
        </dependency>
        <!-- Sa-Token æƒé™è®¤è¯, åœ¨çº¿æ–‡æ¡£ï¼šhttp://sa-token.dev33.cn/ -->
        <dependency>
            <groupId>cn.dev33</groupId>
            <artifactId>sa-token-spring-boot3-starter</artifactId>
        </dependency>
        <!-- Sa-Token æ•´åˆ jwt -->
        <dependency>
            <groupId>cn.dev33</groupId>
            <artifactId>sa-token-jwt</artifactId>
        </dependency>
        <!-- è‡ªå®šä¹‰éªŒè¯æ³¨è§£ -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-validation</artifactId>
        </dependency>
        <!--常用工具类 -->
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
        </dependency>
        <!-- JSON工具类 -->
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>easyexcel</artifactId>
        </dependency>
        <!-- yml解析器 -->
        <dependency>
            <groupId>org.yaml</groupId>
            <artifactId>snakeyaml</artifactId>
        </dependency>
        <!-- servlet包 -->
        <dependency>
            <groupId>jakarta.servlet</groupId>
            <artifactId>jakarta.servlet-api</artifactId>
        </dependency>
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
        </dependency>
        <!-- dynamic-datasource å¤šæ•°æ®æº-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>dynamic-datasource-spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-core</artifactId>
        </dependency>
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-http</artifactId>
        </dependency>
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-captcha</artifactId>
        </dependency>
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-jwt</artifactId>
        </dependency>
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-extra</artifactId>
        </dependency>
        <dependency>
            <groupId>jakarta.mail</groupId>
            <artifactId>jakarta.mail-api</artifactId>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springdoc</groupId>
            <artifactId>springdoc-openapi-starter-webmvc-api</artifactId>
        </dependency>
        <dependency>
            <groupId>com.github.therapi</groupId>
            <artifactId>therapi-runtime-javadoc</artifactId>
        </dependency>
        <!--redisson-->
        <dependency>
            <groupId>org.redisson</groupId>
            <artifactId>redisson-spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>lock4j-redisson-spring-boot-starter</artifactId>
        </dependency>
        <!--  è‡ªåŠ¨ç”ŸæˆYML配置关联JSON文件  -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-properties-migrator</artifactId>
            <scope>runtime</scope>
        </dependency>
    </dependencies>
</project>
ruoyi-common/ruoyi-common-bom/pom.xml
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,129 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.ruoyi</groupId>
    <artifactId>ruoyi-common-bom</artifactId>
    <version>${revision}</version>
    <packaging>pom</packaging>
    <description>
        ruoyi-common-bom common依赖项
    </description>
    <properties>
        <revision>5.0.0-SNAPSHOT</revision>
    </properties>
    <dependencyManagement>
        <dependencies>
            <!-- æ ¸å¿ƒæ¨¡å— -->
            <dependency>
                <groupId>com.ruoyi</groupId>
                <artifactId>ruoyi-common-core</artifactId>
                <version>${revision}</version>
            </dependency>
            <!-- å­—å…¸ -->
            <dependency>
                <groupId>com.ruoyi</groupId>
                <artifactId>ruoyi-common-dict</artifactId>
                <version>${revision}</version>
            </dependency>
            <!-- æŽ¥å£æ¨¡å— -->
            <dependency>
                <groupId>com.ruoyi</groupId>
                <artifactId>ruoyi-common-doc</artifactId>
                <version>${revision}</version>
            </dependency>
            <!-- excel -->
            <dependency>
                <groupId>com.ruoyi</groupId>
                <artifactId>ruoyi-common-excel</artifactId>
                <version>${revision}</version>
            </dependency>
            <!-- å¹‚ç­‰ -->
            <dependency>
                <groupId>com.ruoyi</groupId>
                <artifactId>ruoyi-common-idempotent</artifactId>
                <version>${revision}</version>
            </dependency>
            <!-- æ—¥å¿—记录 -->
            <dependency>
                <groupId>com.ruoyi</groupId>
                <artifactId>ruoyi-common-log</artifactId>
                <version>${revision}</version>
            </dependency>
            <!-- é‚®ä»¶æœåŠ¡ -->
            <dependency>
                <groupId>com.ruoyi</groupId>
                <artifactId>ruoyi-common-mail</artifactId>
                <version>${revision}</version>
            </dependency>
            <!-- æ•°æ®åº“服务 -->
            <dependency>
                <groupId>com.ruoyi</groupId>
                <artifactId>ruoyi-common-mybatis</artifactId>
                <version>${revision}</version>
            </dependency>
            <!-- OSS -->
            <dependency>
                <groupId>com.ruoyi</groupId>
                <artifactId>ruoyi-common-oss</artifactId>
                <version>${revision}</version>
            </dependency>
            <!-- é™æµ -->
            <dependency>
                <groupId>com.ruoyi</groupId>
                <artifactId>ruoyi-common-ratelimiter</artifactId>
                <version>${revision}</version>
            </dependency>
            <!-- ç¼“存服务 -->
            <dependency>
                <groupId>com.ruoyi</groupId>
                <artifactId>ruoyi-common-redis</artifactId>
                <version>${revision}</version>
            </dependency>
            <!-- satoken -->
            <dependency>
                <groupId>com.ruoyi</groupId>
                <artifactId>ruoyi-common-satoken</artifactId>
                <version>${revision}</version>
            </dependency>
            <!-- å®‰å…¨æ¨¡å— -->
            <dependency>
                <groupId>com.ruoyi</groupId>
                <artifactId>ruoyi-common-security</artifactId>
                <version>${revision}</version>
            </dependency>
            <!-- çŸ­ä¿¡æ¨¡å— -->
            <dependency>
                <groupId>com.ruoyi</groupId>
                <artifactId>ruoyi-common-sms</artifactId>
                <version>${revision}</version>
            </dependency>
            <!-- web服务 -->
            <dependency>
                <groupId>com.ruoyi</groupId>
                <artifactId>ruoyi-common-web</artifactId>
                <version>${revision}</version>
            </dependency>
        </dependencies>
    </dependencyManagement>
</project>
ruoyi-common/ruoyi-common-core/pom.xml
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,115 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <groupId>com.ruoyi</groupId>
        <artifactId>ruoyi-common</artifactId>
        <version>${revision}</version>
        <relativePath>../pom.xml</relativePath>
    </parent>
    <modelVersion>4.0.0</modelVersion>
    <artifactId>ruoyi-common-core</artifactId>
    <description>
        ruoyi-common-core æ ¸å¿ƒæ¨¡å—
    </description>
    <dependencies>
        <!-- Spring框架基本的核心工具 -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context-support</artifactId>
        </dependency>
        <!-- SpringWeb模块 -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-web</artifactId>
        </dependency>
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-annotation</artifactId>
        </dependency>
        <!-- è‡ªå®šä¹‰éªŒè¯æ³¨è§£ -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-validation</artifactId>
        </dependency>
        <!--常用工具类 -->
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
        </dependency>
        <!-- JSON工具类 -->
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.datatype</groupId>
            <artifactId>jackson-datatype-jsr310</artifactId>
        </dependency>
        <!-- yml解析器 -->
        <dependency>
            <groupId>org.yaml</groupId>
            <artifactId>snakeyaml</artifactId>
        </dependency>
        <!-- servlet包 -->
        <dependency>
            <groupId>jakarta.servlet</groupId>
            <artifactId>jakarta.servlet-api</artifactId>
        </dependency>
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-core</artifactId>
        </dependency>
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-http</artifactId>
        </dependency>
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-captcha</artifactId>
        </dependency>
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-jwt</artifactId>
        </dependency>
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-extra</artifactId>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
        <!--  è‡ªåŠ¨ç”ŸæˆYML配置关联JSON文件  -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-properties-migrator</artifactId>
            <scope>runtime</scope>
        </dependency>
    </dependencies>
</project>
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/annotation/Sensitive.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,24 @@
package com.ruoyi.common.core.annotation;
import com.fasterxml.jackson.annotation.JacksonAnnotationsInside;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.ruoyi.common.core.enums.SensitiveStrategy;
import com.ruoyi.common.core.jackson.SensitiveJsonSerializer;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
 * æ•°æ®è„±æ•æ³¨è§£
 *
 * @author zhujie
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
@JacksonAnnotationsInside
@JsonSerialize(using = SensitiveJsonSerializer.class)
public @interface Sensitive {
    SensitiveStrategy strategy();
}
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/config/ApplicationConfig.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,16 @@
package com.ruoyi.common.core.config;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
/**
 * ç¨‹åºæ³¨è§£é…ç½®
 *
 * @author Lion Li
 */
@AutoConfiguration
// è¡¨ç¤ºé€šè¿‡aop框架暴露该代理对象,AopContext能够访问
@EnableAspectJAutoProxy(exposeProxy = true)
public class ApplicationConfig {
}
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/config/AsyncConfig.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,54 @@
package com.ruoyi.common.core.config;
import cn.hutool.core.util.ArrayUtil;
import com.ruoyi.common.core.exception.ServiceException;
import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.scheduling.annotation.AsyncConfigurer;
import org.springframework.scheduling.annotation.EnableAsync;
import java.util.Arrays;
import java.util.concurrent.Executor;
import java.util.concurrent.ScheduledExecutorService;
/**
 * å¼‚步配置
 *
 * @author Lion Li
 */
@EnableAsync(proxyTargetClass = true)
@AutoConfiguration
public class AsyncConfig implements AsyncConfigurer {
    @Autowired
    @Qualifier("scheduledExecutorService")
    private ScheduledExecutorService scheduledExecutorService;
    /**
     * è‡ªå®šä¹‰ @Async æ³¨è§£ä½¿ç”¨ç³»ç»Ÿçº¿ç¨‹æ± 
     */
    @Override
    public Executor getAsyncExecutor() {
        return scheduledExecutorService;
    }
    /**
     * å¼‚步执行异常处理
     */
    @Override
    public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
        return (throwable, method, objects) -> {
            throwable.printStackTrace();
            StringBuilder sb = new StringBuilder();
            sb.append("Exception message - ").append(throwable.getMessage())
                .append(", Method name - ").append(method.getName());
            if (ArrayUtil.isNotEmpty(objects)) {
                sb.append(", Parameter value - ").append(Arrays.toString(objects));
            }
            throw new ServiceException(sb.toString());
        };
    }
}
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/config/JacksonConfig.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,47 @@
package com.ruoyi.common.core.config;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import com.ruoyi.common.core.jackson.BigNumberSerializer;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer;
import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration;
import org.springframework.context.annotation.Bean;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.TimeZone;
/**
 * jackson é…ç½®
 *
 * @author Lion Li
 */
@Slf4j
@AutoConfiguration(before = JacksonAutoConfiguration.class)
public class JacksonConfig {
    @Bean
    public Jackson2ObjectMapperBuilderCustomizer customizer() {
        return builder -> {
            // å…¨å±€é…ç½®åºåˆ—化返回 JSON å¤„理
            JavaTimeModule javaTimeModule = new JavaTimeModule();
            javaTimeModule.addSerializer(Long.class, BigNumberSerializer.INSTANCE);
            javaTimeModule.addSerializer(Long.TYPE, BigNumberSerializer.INSTANCE);
            javaTimeModule.addSerializer(BigInteger.class, BigNumberSerializer.INSTANCE);
            javaTimeModule.addSerializer(BigDecimal.class, ToStringSerializer.instance);
            DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
            javaTimeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(formatter));
            javaTimeModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(formatter));
            builder.modules(javaTimeModule);
            builder.timeZone(TimeZone.getDefault());
            log.info("初始化 jackson é…ç½®");
        };
    }
}
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/config/RuoYiConfig.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,54 @@
package com.ruoyi.common.core.config;
import lombok.Data;
import lombok.Getter;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
/**
 * è¯»å–项目相关配置
 *
 * @author Lion Li
 */
@Data
@Component
@ConfigurationProperties(prefix = "ruoyi")
public class RuoYiConfig {
    /**
     * é¡¹ç›®åç§°
     */
    private String name;
    /**
     * ç‰ˆæœ¬
     */
    private String version;
    /**
     * ç‰ˆæƒå¹´ä»½
     */
    private String copyrightYear;
    /**
     * å®žä¾‹æ¼”示开关
     */
    private boolean demoEnabled;
    /**
     * ç¼“存懒加载
     */
    private boolean cacheLazy;
    /**
     * èŽ·å–åœ°å€å¼€å…³
     */
    @Getter
    private static boolean addressEnabled;
    public void setAddressEnabled(boolean addressEnabled) {
        RuoYiConfig.addressEnabled = addressEnabled;
    }
}
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/config/ThreadPoolConfig.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,59 @@
package com.ruoyi.common.core.config;
import com.ruoyi.common.core.config.properties.ThreadPoolProperties;
import com.ruoyi.common.core.utils.Threads;
import org.apache.commons.lang3.concurrent.BasicThreadFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.ThreadPoolExecutor;
/**
 * çº¿ç¨‹æ± é…ç½®
 *
 * @author Lion Li
 **/
@AutoConfiguration
public class ThreadPoolConfig {
    /**
     * æ ¸å¿ƒçº¿ç¨‹æ•° = cpu æ ¸å¿ƒæ•° + 1
     */
    private final int core = Runtime.getRuntime().availableProcessors() + 1;
    @Autowired
    private ThreadPoolProperties threadPoolProperties;
    @Bean(name = "threadPoolTaskExecutor")
    @ConditionalOnProperty(prefix = "thread-pool", name = "enabled", havingValue = "true")
    public ThreadPoolTaskExecutor threadPoolTaskExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(core);
        executor.setMaxPoolSize(core * 2);
        executor.setQueueCapacity(threadPoolProperties.getQueueCapacity());
        executor.setKeepAliveSeconds(threadPoolProperties.getKeepAliveSeconds());
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        return executor;
    }
    /**
     * æ‰§è¡Œå‘¨æœŸæ€§æˆ–定时任务
     */
    @Bean(name = "scheduledExecutorService")
    protected ScheduledExecutorService scheduledExecutorService() {
        return new ScheduledThreadPoolExecutor(core,
            new BasicThreadFactory.Builder().namingPattern("schedule-pool-%d").daemon(true).build(),
            new ThreadPoolExecutor.CallerRunsPolicy()) {
            @Override
            protected void afterExecute(Runnable r, Throwable t) {
                super.afterExecute(r, t);
                Threads.printException(r, t);
            }
        };
    }
}
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/config/ValidatorConfig.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,43 @@
package com.ruoyi.common.core.config;
import org.hibernate.validator.HibernateValidator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.context.MessageSource;
import org.springframework.context.annotation.Bean;
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
import jakarta.validation.Validator;
import java.util.Properties;
/**
 * æ ¡éªŒæ¡†æž¶é…ç½®ç±»
 *
 * @author Lion Li
 */
@AutoConfiguration
public class ValidatorConfig {
    @Autowired
    private MessageSource messageSource;
    /**
     * é…ç½®æ ¡éªŒæ¡†æž¶ å¿«é€Ÿè¿”回模式
     */
    @Bean
    public Validator validator() {
        LocalValidatorFactoryBean factoryBean = new LocalValidatorFactoryBean();
        // å›½é™…化
        factoryBean.setValidationMessageSource(messageSource);
        // è®¾ç½®ä½¿ç”¨ HibernateValidator æ ¡éªŒå™¨
        factoryBean.setProviderClass(HibernateValidator.class);
        Properties properties = new Properties();
        // è®¾ç½® å¿«é€Ÿå¼‚常返回
        properties.setProperty("hibernate.validator.fail_fast", "true");
        factoryBean.setValidationProperties(properties);
        // åŠ è½½é…ç½®
        factoryBean.afterPropertiesSet();
        return factoryBean.getValidator();
    }
}
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/config/properties/ThreadPoolProperties.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,32 @@
package com.ruoyi.common.core.config.properties;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
/**
 * çº¿ç¨‹æ±  é…ç½®å±žæ€§
 *
 * @author Lion Li
 */
@Data
@Component
@ConfigurationProperties(prefix = "thread-pool")
public class ThreadPoolProperties {
    /**
     * æ˜¯å¦å¼€å¯çº¿ç¨‹æ± 
     */
    private boolean enabled;
    /**
     * é˜Ÿåˆ—最大长度
     */
    private int queueCapacity;
    /**
     * çº¿ç¨‹æ± ç»´æŠ¤çº¿ç¨‹æ‰€å…è®¸çš„空闲时间
     */
    private int keepAliveSeconds;
}
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/constant/CacheConstants.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,49 @@
package com.ruoyi.common.core.constant;
/**
 * ç¼“存的key å¸¸é‡
 *
 * @author ruoyi
 */
public interface CacheConstants {
    /**
     * ç™»å½•用户 redis key
     */
    String LOGIN_TOKEN_KEY = "Authorization:login:token:";
    /**
     * åœ¨çº¿ç”¨æˆ· redis key
     */
    String ONLINE_TOKEN_KEY = "online_tokens:";
    /**
     * éªŒè¯ç  redis key
     */
    String CAPTCHA_CODE_KEY = "captcha_codes:";
    /**
     * å‚数管理 cache key
     */
    String SYS_CONFIG_KEY = "sys_config:";
    /**
     * å­—典管理 cache key
     */
    String SYS_DICT_KEY = "sys_dict:";
    /**
     * é˜²é‡æäº¤ redis key
     */
    String REPEAT_SUBMIT_KEY = "repeat_submit:";
    /**
     * é™æµ redis key
     */
    String RATE_LIMIT_KEY = "rate_limit:";
    /**
     * ç™»å½•账户密码错误次数 redis key
     */
    String PWD_ERR_CNT_KEY = "pwd_err_cnt:";
}
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/constant/CacheNames.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,48 @@
package com.ruoyi.common.core.constant;
/**
 * ç¼“存组名称常量
 * <p>
 * key æ ¼å¼ä¸º cacheNames#ttl#maxIdleTime#maxSize
 * <p>
 * ttl è¿‡æœŸæ—¶é—´ å¦‚果设置为0则不过期 é»˜è®¤ä¸º0
 * maxIdleTime æœ€å¤§ç©ºé—²æ—¶é—´ æ ¹æ®LRU算法清理空闲数据 å¦‚果设置为0则不检测 é»˜è®¤ä¸º0
 * maxSize ç»„最大长度 æ ¹æ®LRU算法清理溢出数据 å¦‚果设置为0则无限长 é»˜è®¤ä¸º0
 * <p>
 * ä¾‹å­: test#60s、test#0#60s、test#0#1m#1000、test#1h#0#500
 *
 * @author Lion Li
 */
public interface CacheNames {
    /**
     * æ¼”示案例
     */
    String DEMO_CACHE = "demo:cache#60s#10m#20";
    /**
     * ç³»ç»Ÿé…ç½®
     */
    String SYS_CONFIG = "sys_config";
    /**
     * æ•°æ®å­—å…¸
     */
    String SYS_DICT = "sys_dict";
    /**
     * OSS内容
     */
    String SYS_OSS = "sys_oss#30d";
    /**
     * OSS配置
     */
    String SYS_OSS_CONFIG = "sys_oss_config";
    /**
     * åœ¨çº¿ç”¨æˆ·
     */
    String ONLINE_TOKEN = "online_tokens";
}
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/constant/Constants.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,76 @@
package com.ruoyi.common.core.constant;
/**
 * é€šç”¨å¸¸é‡ä¿¡æ¯
 *
 * @author ruoyi
 */
public interface Constants {
    /**
     * UTF-8 å­—符集
     */
    String UTF8 = "UTF-8";
    /**
     * GBK å­—符集
     */
    String GBK = "GBK";
    /**
     * www主域
     */
    String WWW = "www.";
    /**
     * http请求
     */
    String HTTP = "http://";
    /**
     * https请求
     */
    String HTTPS = "https://";
    /**
     * é€šç”¨æˆåŠŸæ ‡è¯†
     */
    String SUCCESS = "0";
    /**
     * é€šç”¨å¤±è´¥æ ‡è¯†
     */
    String FAIL = "1";
    /**
     * ç™»å½•成功
     */
    String LOGIN_SUCCESS = "Success";
    /**
     * æ³¨é”€
     */
    String LOGOUT = "Logout";
    /**
     * æ³¨å†Œ
     */
    String REGISTER = "Register";
    /**
     * ç™»å½•失败
     */
    String LOGIN_FAIL = "Error";
    /**
     * éªŒè¯ç æœ‰æ•ˆæœŸï¼ˆåˆ†é’Ÿï¼‰
     */
    Integer CAPTCHA_EXPIRATION = 2;
    /**
     * ä»¤ç‰Œ
     */
    String TOKEN = "token";
}
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/constant/GenConstants.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,188 @@
package com.ruoyi.common.core.constant;
/**
 * ä»£ç ç”Ÿæˆé€šç”¨å¸¸é‡
 *
 * @author ruoyi
 */
public interface GenConstants {
    /**
     * å•表(增删改查)
     */
    String TPL_CRUD = "crud";
    /**
     * æ ‘表(增删改查)
     */
    String TPL_TREE = "tree";
    /**
     * æ ‘编码字段
     */
    String TREE_CODE = "treeCode";
    /**
     * æ ‘父编码字段
     */
    String TREE_PARENT_CODE = "treeParentCode";
    /**
     * æ ‘名称字段
     */
    String TREE_NAME = "treeName";
    /**
     * ä¸Šçº§èœå•ID字段
     */
    String PARENT_MENU_ID = "parentMenuId";
    /**
     * ä¸Šçº§èœå•名称字段
     */
    String PARENT_MENU_NAME = "parentMenuName";
    /**
     * æ•°æ®åº“字符串类型
     */
    String[] COLUMNTYPE_STR = {"char", "varchar", "nvarchar", "varchar2"};
    /**
     * æ•°æ®åº“文本类型
     */
    String[] COLUMNTYPE_TEXT = {"tinytext", "text", "mediumtext", "longtext"};
    /**
     * æ•°æ®åº“时间类型
     */
    String[] COLUMNTYPE_TIME = {"datetime", "time", "date", "timestamp"};
    /**
     * æ•°æ®åº“数字类型
     */
    String[] COLUMNTYPE_NUMBER = {"tinyint", "smallint", "mediumint", "int", "number", "integer",
        "bit", "bigint", "float", "double", "decimal"};
    /**
     * BO对象 ä¸éœ€è¦æ·»åŠ å­—æ®µ
     */
    String[] COLUMNNAME_NOT_ADD = {"create_by", "create_time", "del_flag", "update_by",
        "update_time", "version"};
    /**
     * BO对象 ä¸éœ€è¦ç¼–辑字段
     */
    String[] COLUMNNAME_NOT_EDIT = {"create_by", "create_time", "del_flag", "update_by",
        "update_time", "version"};
    /**
     * VO对象 ä¸éœ€è¦è¿”回字段
     */
    String[] COLUMNNAME_NOT_LIST = {"create_by", "create_time", "del_flag", "update_by",
        "update_time", "version"};
    /**
     * BO对象 ä¸éœ€è¦æŸ¥è¯¢å­—段
     */
    String[] COLUMNNAME_NOT_QUERY = {"id", "create_by", "create_time", "del_flag", "update_by",
        "update_time", "remark", "version"};
    /**
     * Entity基类字段
     */
    String[] BASE_ENTITY = {"createBy", "createTime", "updateBy", "updateTime"};
    /**
     * Tree基类字段
     */
    String[] TREE_ENTITY = {"parentName", "parentId", "children"};
    /**
     * æ–‡æœ¬æ¡†
     */
    String HTML_INPUT = "input";
    /**
     * æ–‡æœ¬åŸŸ
     */
    String HTML_TEXTAREA = "textarea";
    /**
     * ä¸‹æ‹‰æ¡†
     */
    String HTML_SELECT = "select";
    /**
     * å•选框
     */
    String HTML_RADIO = "radio";
    /**
     * å¤é€‰æ¡†
     */
    String HTML_CHECKBOX = "checkbox";
    /**
     * æ—¥æœŸæŽ§ä»¶
     */
    String HTML_DATETIME = "datetime";
    /**
     * å›¾ç‰‡ä¸Šä¼ æŽ§ä»¶
     */
    String HTML_IMAGE_UPLOAD = "imageUpload";
    /**
     * æ–‡ä»¶ä¸Šä¼ æŽ§ä»¶
     */
    String HTML_FILE_UPLOAD = "fileUpload";
    /**
     * å¯Œæ–‡æœ¬æŽ§ä»¶
     */
    String HTML_EDITOR = "editor";
    /**
     * å­—符串类型
     */
    String TYPE_STRING = "String";
    /**
     * æ•´åž‹
     */
    String TYPE_INTEGER = "Integer";
    /**
     * é•¿æ•´åž‹
     */
    String TYPE_LONG = "Long";
    /**
     * æµ®ç‚¹åž‹
     */
    String TYPE_DOUBLE = "Double";
    /**
     * é«˜ç²¾åº¦è®¡ç®—类型
     */
    String TYPE_BIGDECIMAL = "BigDecimal";
    /**
     * æ—¶é—´ç±»åž‹
     */
    String TYPE_DATE = "Date";
    /**
     * æ¨¡ç³ŠæŸ¥è¯¢
     */
    String QUERY_LIKE = "LIKE";
    /**
     * ç›¸ç­‰æŸ¥è¯¢
     */
    String QUERY_EQ = "EQ";
    /**
     * éœ€è¦
     */
    String REQUIRE = "1";
}
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/constant/HttpStatus.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,93 @@
package com.ruoyi.common.core.constant;
/**
 * è¿”回状态码
 *
 * @author Lion Li
 */
public interface HttpStatus {
    /**
     * æ“ä½œæˆåŠŸ
     */
    int SUCCESS = 200;
    /**
     * å¯¹è±¡åˆ›å»ºæˆåŠŸ
     */
    int CREATED = 201;
    /**
     * è¯·æ±‚已经被接受
     */
    int ACCEPTED = 202;
    /**
     * æ“ä½œå·²ç»æ‰§è¡ŒæˆåŠŸï¼Œä½†æ˜¯æ²¡æœ‰è¿”å›žæ•°æ®
     */
    int NO_CONTENT = 204;
    /**
     * èµ„源已被移除
     */
    int MOVED_PERM = 301;
    /**
     * é‡å®šå‘
     */
    int SEE_OTHER = 303;
    /**
     * èµ„源没有被修改
     */
    int NOT_MODIFIED = 304;
    /**
     * å‚数列表错误(缺少,格式不匹配)
     */
    int BAD_REQUEST = 400;
    /**
     * æœªæŽˆæƒ
     */
    int UNAUTHORIZED = 401;
    /**
     * è®¿é—®å—限,授权过期
     */
    int FORBIDDEN = 403;
    /**
     * èµ„源,服务未找到
     */
    int NOT_FOUND = 404;
    /**
     * ä¸å…è®¸çš„http方法
     */
    int BAD_METHOD = 405;
    /**
     * èµ„源冲突,或者资源被锁
     */
    int CONFLICT = 409;
    /**
     * ä¸æ”¯æŒçš„æ•°æ®ï¼Œåª’体类型
     */
    int UNSUPPORTED_TYPE = 415;
    /**
     * ç³»ç»Ÿå†…部错误
     */
    int ERROR = 500;
    /**
     * æŽ¥å£æœªå®žçް
     */
    int NOT_IMPLEMENTED = 501;
    /**
     * ç³»ç»Ÿè­¦å‘Šæ¶ˆæ¯
     */
    int WARN = 601;
}
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/constant/UserConstants.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,138 @@
package com.ruoyi.common.core.constant;
/**
 * ç”¨æˆ·å¸¸é‡ä¿¡æ¯
 *
 * @author ruoyi
 */
public interface UserConstants {
    /**
     * å¹³å°å†…系统用户的唯一标志
     */
    String SYS_USER = "SYS_USER";
    /**
     * æ­£å¸¸çŠ¶æ€
     */
    String NORMAL = "0";
    /**
     * å¼‚常状态
     */
    String EXCEPTION = "1";
    /**
     * ç”¨æˆ·æ­£å¸¸çŠ¶æ€
     */
    String USER_NORMAL = "0";
    /**
     * ç”¨æˆ·å°ç¦çŠ¶æ€
     */
    String USER_DISABLE = "1";
    /**
     * è§’色正常状态
     */
    String ROLE_NORMAL = "0";
    /**
     * è§’色封禁状态
     */
    String ROLE_DISABLE = "1";
    /**
     * éƒ¨é—¨æ­£å¸¸çŠ¶æ€
     */
    String DEPT_NORMAL = "0";
    /**
     * éƒ¨é—¨åœç”¨çŠ¶æ€
     */
    String DEPT_DISABLE = "1";
    /**
     * å­—典正常状态
     */
    String DICT_NORMAL = "0";
    /**
     * æ˜¯å¦ä¸ºç³»ç»Ÿé»˜è®¤ï¼ˆæ˜¯ï¼‰
     */
    String YES = "Y";
    /**
     * æ˜¯å¦èœå•外链(是)
     */
    String YES_FRAME = "0";
    /**
     * æ˜¯å¦èœå•外链(否)
     */
    String NO_FRAME = "1";
    /**
     * èœå•正常状态
     */
    String MENU_NORMAL = "0";
    /**
     * èœå•停用状态
     */
    String MENU_DISABLE = "1";
    /**
     * èœå•类型(目录)
     */
    String TYPE_DIR = "M";
    /**
     * èœå•类型(菜单)
     */
    String TYPE_MENU = "C";
    /**
     * èœå•类型(按钮)
     */
    String TYPE_BUTTON = "F";
    /**
     * Layout组件标识
     */
    String LAYOUT = "Layout";
    /**
     * ParentView组件标识
     */
    String PARENT_VIEW = "ParentView";
    /**
     * InnerLink组件标识
     */
    String INNER_LINK = "InnerLink";
    /**
     * æ ¡éªŒè¿”回结果码
     */
    String UNIQUE = "0";
    String NOT_UNIQUE = "1";
    /**
     * ç”¨æˆ·åé•¿åº¦é™åˆ¶
     */
    int USERNAME_MIN_LENGTH = 2;
    int USERNAME_MAX_LENGTH = 20;
    /**
     * å¯†ç é•¿åº¦é™åˆ¶
     */
    int PASSWORD_MIN_LENGTH = 5;
    int PASSWORD_MAX_LENGTH = 20;
    /**
     * ç®¡ç†å‘˜ID
     */
    Long ADMIN_ID = 1L;
}
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/domain/R.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,110 @@
package com.ruoyi.common.core.domain;
import com.ruoyi.common.core.constant.HttpStatus;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serial;
import java.io.Serializable;
/**
 * å“åº”信息主体
 *
 * @author Lion Li
 */
@Data
@NoArgsConstructor
public class R<T> implements Serializable {
    @Serial
    private static final long serialVersionUID = 1L;
    /**
     * æˆåŠŸ
     */
    public static final int SUCCESS = 200;
    /**
     * å¤±è´¥
     */
    public static final int FAIL = 500;
    private int code;
    private String msg;
    private T data;
    public static <T> R<T> ok() {
        return restResult(null, SUCCESS, "操作成功");
    }
    public static <T> R<T> ok(T data) {
        return restResult(data, SUCCESS, "操作成功");
    }
    public static <T> R<T> ok(String msg) {
        return restResult(null, SUCCESS, msg);
    }
    public static <T> R<T> ok(String msg, T data) {
        return restResult(data, SUCCESS, msg);
    }
    public static <T> R<T> fail() {
        return restResult(null, FAIL, "操作失败");
    }
    public static <T> R<T> fail(String msg) {
        return restResult(null, FAIL, msg);
    }
    public static <T> R<T> fail(T data) {
        return restResult(data, FAIL, "操作失败");
    }
    public static <T> R<T> fail(String msg, T data) {
        return restResult(data, FAIL, msg);
    }
    public static <T> R<T> fail(int code, String msg) {
        return restResult(null, code, msg);
    }
    /**
     * è¿”回警告消息
     *
     * @param msg è¿”回内容
     * @return è­¦å‘Šæ¶ˆæ¯
     */
    public static <T> R<T> warn(String msg) {
        return restResult(null, HttpStatus.WARN, msg);
    }
    /**
     * è¿”回警告消息
     *
     * @param msg è¿”回内容
     * @param data æ•°æ®å¯¹è±¡
     * @return è­¦å‘Šæ¶ˆæ¯
     */
    public static <T> R<T> warn(String msg, T data) {
        return restResult(data, HttpStatus.WARN, msg);
    }
    private static <T> R<T> restResult(T data, int code, String msg) {
        R<T> r = new R<>();
        r.setCode(code);
        r.setData(data);
        r.setMsg(msg);
        return r;
    }
    public static <T> Boolean isError(R<T> ret) {
        return !isSuccess(ret);
    }
    public static <T> Boolean isSuccess(R<T> ret) {
        return R.SUCCESS == ret.getCode();
    }
}
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/domain/dto/RoleDTO.java
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/domain/dto/UserOnlineDTO.java
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/domain/model/LoginBody.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,42 @@
package com.ruoyi.common.core.domain.model;
import com.ruoyi.common.core.constant.UserConstants;
import lombok.Data;
import org.hibernate.validator.constraints.Length;
import jakarta.validation.constraints.NotBlank;
/**
 * ç”¨æˆ·ç™»å½•对象
 *
 * @author Lion Li
 */
@Data
public class LoginBody {
    /**
     * ç”¨æˆ·å
     */
    @NotBlank(message = "{user.username.not.blank}")
    @Length(min = UserConstants.USERNAME_MIN_LENGTH, max = UserConstants.USERNAME_MAX_LENGTH, message = "{user.username.length.valid}")
    private String username;
    /**
     * ç”¨æˆ·å¯†ç 
     */
    @NotBlank(message = "{user.password.not.blank}")
    @Length(min = UserConstants.PASSWORD_MIN_LENGTH, max = UserConstants.PASSWORD_MAX_LENGTH, message = "{user.password.length.valid}")
    private String password;
    /**
     * éªŒè¯ç 
     */
    private String code;
    /**
     * å”¯ä¸€æ ‡è¯†
     */
    private String uuid;
}
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/domain/model/LoginUser.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,118 @@
package com.ruoyi.common.core.domain.model;
import com.ruoyi.common.core.domain.dto.RoleDTO;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serial;
import java.io.Serializable;
import java.util.List;
import java.util.Set;
/**
 * ç™»å½•用户身份权限
 *
 * @author Lion Li
 */
@Data
@NoArgsConstructor
public class LoginUser implements Serializable {
    @Serial
    private static final long serialVersionUID = 1L;
    /**
     * ç”¨æˆ·ID
     */
    private Long userId;
    /**
     * éƒ¨é—¨ID
     */
    private Long deptId;
    /**
     * éƒ¨é—¨å
     */
    private String deptName;
    /**
     * ç”¨æˆ·å”¯ä¸€æ ‡è¯†
     */
    private String token;
    /**
     * ç”¨æˆ·ç±»åž‹
     */
    private String userType;
    /**
     * ç™»å½•æ—¶é—´
     */
    private Long loginTime;
    /**
     * è¿‡æœŸæ—¶é—´
     */
    private Long expireTime;
    /**
     * ç™»å½•IP地址
     */
    private String ipaddr;
    /**
     * ç™»å½•地点
     */
    private String loginLocation;
    /**
     * æµè§ˆå™¨ç±»åž‹
     */
    private String browser;
    /**
     * æ“ä½œç³»ç»Ÿ
     */
    private String os;
    /**
     * èœå•权限
     */
    private Set<String> menuPermission;
    /**
     * è§’色权限
     */
    private Set<String> rolePermission;
    /**
     * ç”¨æˆ·å
     */
    private String username;
    /**
     * è§’色对象
     */
    private List<RoleDTO> roles;
    /**
     * æ•°æ®æƒé™ å½“前角色ID
     */
    private Long roleId;
    /**
     * èŽ·å–ç™»å½•id
     */
    public String getLoginId() {
        if (userType == null) {
            throw new IllegalArgumentException("用户类型不能为空");
        }
        if (userId == null) {
            throw new IllegalArgumentException("用户ID不能为空");
        }
        return userType + ":" + userId;
    }
}
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/domain/model/RegisterBody.java
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/domain/model/SmsLoginBody.java
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/domain/model/XcxLoginUser.java
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/enums/DeviceType.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,32 @@
package com.ruoyi.common.core.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
 * è®¾å¤‡ç±»åž‹
 * é’ˆå¯¹ä¸€å¥— ç”¨æˆ·ä½“ç³»
 *
 * @author Lion Li
 */
@Getter
@AllArgsConstructor
public enum DeviceType {
    /**
     * pc端
     */
    PC("pc"),
    /**
     * app端
     */
    APP("app"),
    /**
     * å°ç¨‹åºç«¯
     */
    XCX("xcx");
    private final String device;
}
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/enums/LoginType.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,39 @@
package com.ruoyi.common.core.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
 * ç™»å½•类型
 *
 * @author Lion Li
 */
@Getter
@AllArgsConstructor
public enum LoginType {
    /**
     * å¯†ç ç™»å½•
     */
    PASSWORD("user.password.retry.limit.exceed", "user.password.retry.limit.count"),
    /**
     * çŸ­ä¿¡ç™»å½•
     */
    SMS("sms.code.retry.limit.exceed", "sms.code.retry.limit.count"),
    /**
     * å°ç¨‹åºç™»å½•
     */
    XCX("", "");
    /**
     * ç™»å½•重试超出限制提示
     */
    final String retryLimitExceed;
    /**
     * ç™»å½•重试限制计数提示
     */
    final String retryLimitCount;
}
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/enums/SensitiveStrategy.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,49 @@
package com.ruoyi.common.core.enums;
import cn.hutool.core.util.DesensitizedUtil;
import lombok.AllArgsConstructor;
import java.util.function.Function;
/**
 * è„±æ•ç­–ç•¥
 *
 * @author Yjoioooo
 * @version 3.6.0
 */
@AllArgsConstructor
public enum SensitiveStrategy {
    /**
     * èº«ä»½è¯è„±æ•
     */
    ID_CARD(s -> DesensitizedUtil.idCardNum(s, 3, 4)),
    /**
     * æ‰‹æœºå·è„±æ•
     */
    PHONE(DesensitizedUtil::mobilePhone),
    /**
     * åœ°å€è„±æ•
     */
    ADDRESS(s -> DesensitizedUtil.address(s, 8)),
    /**
     * é‚®ç®±è„±æ•
     */
    EMAIL(DesensitizedUtil::email),
    /**
     * é“¶è¡Œå¡
     */
    BANK_CARD(DesensitizedUtil::bankCard);
    //可自行添加其他脱敏策略
    private final Function<String, String> desensitizer;
    public Function<String, String> desensitizer() {
        return desensitizer;
    }
}
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/enums/UserStatus.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,26 @@
package com.ruoyi.common.core.enums;
/**
 * ç”¨æˆ·çŠ¶æ€
 *
 * @author ruoyi
 */
public enum UserStatus {
    OK("0", "正常"), DISABLE("1", "停用"), DELETED("2", "删除");
    private final String code;
    private final String info;
    UserStatus(String code, String info) {
        this.code = code;
        this.info = info;
    }
    public String getCode() {
        return code;
    }
    public String getInfo() {
        return info;
    }
}
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/enums/UserType.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,37 @@
package com.ruoyi.common.core.enums;
import com.ruoyi.common.core.utils.StringUtils;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
 * è®¾å¤‡ç±»åž‹
 * é’ˆå¯¹å¤šå¥— ç”¨æˆ·ä½“ç³»
 *
 * @author Lion Li
 */
@Getter
@AllArgsConstructor
public enum UserType {
    /**
     * pc端
     */
    SYS_USER("sys_user"),
    /**
     * app端
     */
    APP_USER("app_user");
    private final String userType;
    public static UserType getUserType(String str) {
        for (UserType value : values()) {
            if (StringUtils.contains(str, value.getUserType())) {
                return value;
            }
        }
        throw new RuntimeException("'UserType' not found By " + str);
    }
}
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/exception/DemoModeException.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,17 @@
package com.ruoyi.common.core.exception;
import java.io.Serial;
/**
 * æ¼”示模式异常
 *
 * @author ruoyi
 */
public class DemoModeException extends RuntimeException {
    @Serial
    private static final long serialVersionUID = 1L;
    public DemoModeException() {
    }
}
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/exception/GlobalException.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,53 @@
package com.ruoyi.common.core.exception;
import java.io.Serial;
/**
 * å…¨å±€å¼‚常
 *
 * @author ruoyi
 */
public class GlobalException extends RuntimeException {
    @Serial
    private static final long serialVersionUID = 1L;
    /**
     * é”™è¯¯æç¤º
     */
    private String message;
    /**
     * é”™è¯¯æ˜Žç»†ï¼Œå†…部调试错误
     */
    private String detailMessage;
    /**
     * ç©ºæž„造方法,避免反序列化问题
     */
    public GlobalException() {
    }
    public GlobalException(String message) {
        this.message = message;
    }
    public String getDetailMessage() {
        return detailMessage;
    }
    public GlobalException setDetailMessage(String detailMessage) {
        this.detailMessage = detailMessage;
        return this;
    }
    @Override
    public String getMessage() {
        return message;
    }
    public GlobalException setMessage(String message) {
        this.message = message;
        return this;
    }
}
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/exception/ServiceException.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,67 @@
package com.ruoyi.common.core.exception;
import java.io.Serial;
/**
 * ä¸šåС异叏
 *
 * @author ruoyi
 */
public final class ServiceException extends RuntimeException {
    @Serial
    private static final long serialVersionUID = 1L;
    /**
     * é”™è¯¯ç 
     */
    private Integer code;
    /**
     * é”™è¯¯æç¤º
     */
    private String message;
    /**
     * é”™è¯¯æ˜Žç»†ï¼Œå†…部调试错误
     */
    private String detailMessage;
    /**
     * ç©ºæž„造方法,避免反序列化问题
     */
    public ServiceException() {
    }
    public ServiceException(String message) {
        this.message = message;
    }
    public ServiceException(String message, Integer code) {
        this.message = message;
        this.code = code;
    }
    public String getDetailMessage() {
        return detailMessage;
    }
    @Override
    public String getMessage() {
        return message;
    }
    public Integer getCode() {
        return code;
    }
    public ServiceException setMessage(String message) {
        this.message = message;
        return this;
    }
    public ServiceException setDetailMessage(String detailMessage) {
        this.detailMessage = detailMessage;
        return this;
    }
}
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/exception/UtilException.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,26 @@
package com.ruoyi.common.core.exception;
import java.io.Serial;
/**
 * å·¥å…·ç±»å¼‚常
 *
 * @author ruoyi
 */
public class UtilException extends RuntimeException {
    @Serial
    private static final long serialVersionUID = 8247610319171014183L;
    public UtilException(Throwable e) {
        super(e.getMessage(), e);
    }
    public UtilException(String message) {
        super(message);
    }
    public UtilException(String message, Throwable throwable) {
        super(message, throwable);
    }
}
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/exception/base/BaseException.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,79 @@
package com.ruoyi.common.core.exception.base;
import com.ruoyi.common.core.utils.MessageUtils;
import com.ruoyi.common.core.utils.StringUtils;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import java.io.Serial;
/**
 * åŸºç¡€å¼‚常
 *
 * @author ruoyi
 */
@Data
@EqualsAndHashCode(callSuper = true)
@NoArgsConstructor
public class BaseException extends RuntimeException {
    @Serial
    private static final long serialVersionUID = 1L;
    /**
     * æ‰€å±žæ¨¡å—
     */
    private String module;
    /**
     * é”™è¯¯ç 
     */
    private String code;
    /**
     * é”™è¯¯ç å¯¹åº”的参数
     */
    private Object[] args;
    /**
     * é”™è¯¯æ¶ˆæ¯
     */
    private String defaultMessage;
    public BaseException(String module, String code, Object[] args, String defaultMessage) {
        this.module = module;
        this.code = code;
        this.args = args;
        this.defaultMessage = defaultMessage;
    }
    public BaseException(String module, String code, Object[] args) {
        this(module, code, args, null);
    }
    public BaseException(String module, String defaultMessage) {
        this(module, null, null, defaultMessage);
    }
    public BaseException(String code, Object[] args) {
        this(null, code, args, null);
    }
    public BaseException(String defaultMessage) {
        this(null, null, null, defaultMessage);
    }
    @Override
    public String getMessage() {
        String message = null;
        if (!StringUtils.isEmpty(code)) {
            message = MessageUtils.message(code, args);
        }
        if (message == null) {
            message = defaultMessage;
        }
        return message;
    }
}
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/exception/file/FileException.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,21 @@
package com.ruoyi.common.core.exception.file;
import com.ruoyi.common.core.exception.base.BaseException;
import java.io.Serial;
/**
 * æ–‡ä»¶ä¿¡æ¯å¼‚常类
 *
 * @author ruoyi
 */
public class FileException extends BaseException {
    @Serial
    private static final long serialVersionUID = 1L;
    public FileException(String code, Object[] args) {
        super("file", code, args, null);
    }
}
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/exception/file/FileNameLengthLimitExceededException.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,18 @@
package com.ruoyi.common.core.exception.file;
import java.io.Serial;
/**
 * æ–‡ä»¶åç§°è¶…长限制异常类
 *
 * @author ruoyi
 */
public class FileNameLengthLimitExceededException extends FileException {
    @Serial
    private static final long serialVersionUID = 1L;
    public FileNameLengthLimitExceededException(int defaultFileNameLength) {
        super("upload.filename.exceed.length", new Object[]{defaultFileNameLength});
    }
}
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/exception/file/FileSizeLimitExceededException.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,18 @@
package com.ruoyi.common.core.exception.file;
import java.io.Serial;
/**
 * æ–‡ä»¶åå¤§å°é™åˆ¶å¼‚常类
 *
 * @author ruoyi
 */
public class FileSizeLimitExceededException extends FileException {
    @Serial
    private static final long serialVersionUID = 1L;
    public FileSizeLimitExceededException(long defaultMaxSize) {
        super("upload.exceed.maxSize", new Object[]{defaultMaxSize});
    }
}
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/exception/user/CaptchaException.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,18 @@
package com.ruoyi.common.core.exception.user;
import java.io.Serial;
/**
 * éªŒè¯ç é”™è¯¯å¼‚常类
 *
 * @author ruoyi
 */
public class CaptchaException extends UserException {
    @Serial
    private static final long serialVersionUID = 1L;
    public CaptchaException() {
        super("user.jcaptcha.error");
    }
}
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/exception/user/CaptchaExpireException.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,18 @@
package com.ruoyi.common.core.exception.user;
import java.io.Serial;
/**
 * éªŒè¯ç å¤±æ•ˆå¼‚常类
 *
 * @author ruoyi
 */
public class CaptchaExpireException extends UserException {
    @Serial
    private static final long serialVersionUID = 1L;
    public CaptchaExpireException() {
        super("user.jcaptcha.expire");
    }
}
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/exception/user/UserException.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,20 @@
package com.ruoyi.common.core.exception.user;
import com.ruoyi.common.core.exception.base.BaseException;
import java.io.Serial;
/**
 * ç”¨æˆ·ä¿¡æ¯å¼‚常类
 *
 * @author ruoyi
 */
public class UserException extends BaseException {
    @Serial
    private static final long serialVersionUID = 1L;
    public UserException(String code, Object... args) {
        super("user", code, args, null);
    }
}
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/exception/user/UserPasswordNotMatchException.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,18 @@
package com.ruoyi.common.core.exception.user;
import java.io.Serial;
/**
 * ç”¨æˆ·å¯†ç ä¸æ­£ç¡®æˆ–不符合规范异常类
 *
 * @author ruoyi
 */
public class UserPasswordNotMatchException extends UserException {
    @Serial
    private static final long serialVersionUID = 1L;
    public UserPasswordNotMatchException() {
        super("user.password.not.match");
    }
}
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/exception/user/UserPasswordRetryLimitExceedException.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,19 @@
package com.ruoyi.common.core.exception.user;
import java.io.Serial;
/**
 * ç”¨æˆ·é”™è¯¯æœ€å¤§æ¬¡æ•°å¼‚常类
 *
 * @author ruoyi
 */
public class UserPasswordRetryLimitExceedException extends UserException {
    @Serial
    private static final long serialVersionUID = 1L;
    public UserPasswordRetryLimitExceedException(int retryLimitCount, int lockTime) {
        super("user.password.retry.limit.exceed", retryLimitCount, lockTime);
    }
}
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/jackson/BigNumberSerializer.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,42 @@
package com.ruoyi.common.core.jackson;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.annotation.JacksonStdImpl;
import com.fasterxml.jackson.databind.ser.std.NumberSerializer;
import java.io.IOException;
/**
 * è¶…出 JS æœ€å¤§æœ€å°å€¼ å¤„理
 *
 * @author Lion Li
 */
@JacksonStdImpl
public class BigNumberSerializer extends NumberSerializer {
    /**
     * æ ¹æ® JS Number.MAX_SAFE_INTEGER ä¸Ž Number.MIN_SAFE_INTEGER å¾—来
     */
    private static final long MAX_SAFE_INTEGER = 9007199254740991L;
    private static final long MIN_SAFE_INTEGER = -9007199254740991L;
    /**
     * æä¾›å®žä¾‹
     */
    public static final BigNumberSerializer INSTANCE = new BigNumberSerializer(Number.class);
    public BigNumberSerializer(Class<? extends Number> rawType) {
        super(rawType);
    }
    @Override
    public void serialize(Number value, JsonGenerator gen, SerializerProvider provider) throws IOException {
        // è¶…出范围 åºåˆ—化位字符串
        if (value.longValue() > MIN_SAFE_INTEGER && value.longValue() < MAX_SAFE_INTEGER) {
            super.serialize(value, gen, provider);
        } else {
            gen.writeString(value.toString());
        }
    }
}
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/jackson/SensitiveJsonSerializer.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,54 @@
package com.ruoyi.common.core.jackson;
import cn.hutool.core.util.ObjectUtil;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.BeanProperty;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.ser.ContextualSerializer;
import com.ruoyi.common.core.annotation.Sensitive;
import com.ruoyi.common.core.enums.SensitiveStrategy;
import com.ruoyi.common.core.service.SensitiveService;
import com.ruoyi.common.core.utils.SpringUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import java.io.IOException;
import java.util.Objects;
/**
 * æ•°æ®è„±æ•json序列化工具
 *
 * @author Yjoioooo
 */
@Slf4j
public class SensitiveJsonSerializer extends JsonSerializer<String> implements ContextualSerializer {
    private SensitiveStrategy strategy;
    @Override
    public void serialize(String value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
        try {
            SensitiveService sensitiveService = SpringUtils.getBean(SensitiveService.class);
            if (ObjectUtil.isNotNull(sensitiveService) && sensitiveService.isSensitive()) {
                gen.writeString(strategy.desensitizer().apply(value));
            } else {
                gen.writeString(value);
            }
        } catch (BeansException e) {
            log.error("脱敏实现不存在, é‡‡ç”¨é»˜è®¤å¤„理 => {}", e.getMessage());
            gen.writeString(value);
        }
    }
    @Override
    public JsonSerializer<?> createContextual(SerializerProvider prov, BeanProperty property) throws JsonMappingException {
        Sensitive annotation = property.getAnnotation(Sensitive.class);
        if (Objects.nonNull(annotation) && Objects.equals(String.class, property.getType().getRawClass())) {
            this.strategy = annotation.strategy();
            return this;
        }
        return prov.findValueSerializer(property.getType(), property);
    }
}
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/manager/ShutdownManager.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,41 @@
package com.ruoyi.common.core.manager;
import com.ruoyi.common.core.utils.Threads;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;
import jakarta.annotation.PreDestroy;
import java.util.concurrent.ScheduledExecutorService;
/**
 * ç¡®ä¿åº”用退出时能关闭后台线程
 *
 * @author Lion Li
 */
@Slf4j
@Component
public class ShutdownManager {
    @Autowired
    @Qualifier("scheduledExecutorService")
    private ScheduledExecutorService scheduledExecutorService;
    @PreDestroy
    public void destroy() {
        shutdownAsyncManager();
    }
    /**
     * åœæ­¢å¼‚步执行任务
     */
    private void shutdownAsyncManager() {
        try {
            log.info("====关闭后台任务任务线程池====");
            Threads.shutdownAndAwaitTermination(scheduledExecutorService);
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
    }
}
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/service/ConfigService.java
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/service/DictService.java
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/service/SensitiveService.java
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/BeanCopyUtils.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,204 @@
package com.ruoyi.common.core.utils;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.SimpleCache;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import org.springframework.cglib.beans.BeanCopier;
import org.springframework.cglib.beans.BeanMap;
import org.springframework.cglib.core.Converter;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
/**
 * bean深拷贝工具(基于 cglib æ€§èƒ½ä¼˜å¼‚)
 * <p>
 * é‡ç‚¹ cglib ä¸æ”¯æŒ æ‹·è´åˆ°é“¾å¼å¯¹è±¡
 * ä¾‹å¦‚: æºå¯¹è±¡ æ‹·è´åˆ° ç›®æ ‡(链式对象)
 * è¯·åŒºåˆ†å¥½`浅拷贝`和`深拷贝`再做使用
 *
 * @author Lion Li
 */
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class BeanCopyUtils {
    /**
     * å•对象基于class创建拷贝
     *
     * @param source æ•°æ®æ¥æºå®žä½“
     * @param desc   æè¿°å¯¹è±¡ è½¬æ¢åŽçš„对象
     * @return desc
     */
    public static <T, V> V copy(T source, Class<V> desc) {
        if (ObjectUtil.isNull(source)) {
            return null;
        }
        if (ObjectUtil.isNull(desc)) {
            return null;
        }
        final V target = ReflectUtil.newInstanceIfPossible(desc);
        return copy(source, target);
    }
    /**
     * å•对象基于对象创建拷贝
     *
     * @param source æ•°æ®æ¥æºå®žä½“
     * @param desc   è½¬æ¢åŽçš„对象
     * @return desc
     */
    public static <T, V> V copy(T source, V desc) {
        if (ObjectUtil.isNull(source)) {
            return null;
        }
        if (ObjectUtil.isNull(desc)) {
            return null;
        }
        BeanCopier beanCopier = BeanCopierCache.INSTANCE.get(source.getClass(), desc.getClass(), null);
        beanCopier.copy(source, desc, null);
        return desc;
    }
    /**
     * åˆ—表对象基于class创建拷贝
     *
     * @param sourceList æ•°æ®æ¥æºå®žä½“列表
     * @param desc       æè¿°å¯¹è±¡ è½¬æ¢åŽçš„对象
     * @return desc
     */
    public static <T, V> List<V> copyList(List<T> sourceList, Class<V> desc) {
        if (ObjectUtil.isNull(sourceList)) {
            return null;
        }
        if (CollUtil.isEmpty(sourceList)) {
            return CollUtil.newArrayList();
        }
        return StreamUtils.toList(sourceList, source -> {
            V target = ReflectUtil.newInstanceIfPossible(desc);
            copy(source, target);
            return target;
        });
    }
    /**
     * bean拷贝到map
     *
     * @param bean æ•°æ®æ¥æºå®žä½“
     * @return map对象
     */
    @SuppressWarnings("unchecked")
    public static <T> Map<String, Object> copyToMap(T bean) {
        if (ObjectUtil.isNull(bean)) {
            return null;
        }
        return BeanMap.create(bean);
    }
    /**
     * map拷贝到bean
     *
     * @param map       æ•°æ®æ¥æº
     * @param beanClass beanç±»
     * @return bean对象
     */
    public static <T> T mapToBean(Map<String, Object> map, Class<T> beanClass) {
        if (MapUtil.isEmpty(map)) {
            return null;
        }
        if (ObjectUtil.isNull(beanClass)) {
            return null;
        }
        T bean = ReflectUtil.newInstanceIfPossible(beanClass);
        return mapToBean(map, bean);
    }
    /**
     * map拷贝到bean
     *
     * @param map  æ•°æ®æ¥æº
     * @param bean bean对象
     * @return bean对象
     */
    public static <T> T mapToBean(Map<String, Object> map, T bean) {
        if (MapUtil.isEmpty(map)) {
            return null;
        }
        if (ObjectUtil.isNull(bean)) {
            return null;
        }
        BeanMap.create(bean).putAll(map);
        return bean;
    }
    /**
     * map拷贝到map
     *
     * @param map   æ•°æ®æ¥æº
     * @param clazz è¿”回的对象类型
     * @return map对象
     */
    public static <T, V> Map<String, V> mapToMap(Map<String, T> map, Class<V> clazz) {
        if (MapUtil.isEmpty(map)) {
            return null;
        }
        if (ObjectUtil.isNull(clazz)) {
            return null;
        }
        Map<String, V> copyMap = new LinkedHashMap<>(map.size());
        map.forEach((k, v) -> copyMap.put(k, copy(v, clazz)));
        return copyMap;
    }
    /**
     * BeanCopier属性缓存<br>
     * ç¼“存用于防止多次反射造成的性能问题
     *
     * @author Looly
     * @since 5.4.1
     */
    public enum BeanCopierCache {
        /**
         * BeanCopier属性缓存单例
         */
        INSTANCE;
        private final SimpleCache<String, BeanCopier> cache = new SimpleCache<>();
        /**
         * èŽ·å¾—ç±»ä¸Žè½¬æ¢å™¨ç”Ÿæˆçš„key在{@link BeanCopier}的Map中对应的元素
         *
         * @param srcClass    æºBean的类
         * @param targetClass ç›®æ ‡Bean的类
         * @param converter   è½¬æ¢å™¨
         * @return Map中对应的BeanCopier
         */
        public BeanCopier get(Class<?> srcClass, Class<?> targetClass, Converter converter) {
            final String key = genKey(srcClass, targetClass, converter);
            return cache.get(key, () -> BeanCopier.create(srcClass, targetClass, converter != null));
        }
        /**
         * èŽ·å¾—ç±»ä¸Žè½¬æ¢å™¨ç”Ÿæˆçš„key
         *
         * @param srcClass    æºBean的类
         * @param targetClass ç›®æ ‡Bean的类
         * @param converter   è½¬æ¢å™¨
         * @return å±žæ€§åå’ŒMap映射的key
         */
        private String genKey(Class<?> srcClass, Class<?> targetClass, Converter converter) {
            final StringBuilder key = StrUtil.builder()
                .append(srcClass.getName()).append('#').append(targetClass.getName());
            if (null != converter) {
                key.append('#').append(converter.getClass().getName());
            }
            return key.toString();
        }
    }
}
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/DateUtils.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,168 @@
package com.ruoyi.common.core.utils;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import org.apache.commons.lang3.time.DateFormatUtils;
import java.lang.management.ManagementFactory;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.Date;
/**
 * æ—¶é—´å·¥å…·ç±»
 *
 * @author ruoyi
 */
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class DateUtils extends org.apache.commons.lang3.time.DateUtils {
    public static final String YYYY = "yyyy";
    public static final String YYYY_MM = "yyyy-MM";
    public static final String YYYY_MM_DD = "yyyy-MM-dd";
    public static final String YYYYMMDDHHMMSS = "yyyyMMddHHmmss";
    public static final String YYYY_MM_DD_HH_MM_SS = "yyyy-MM-dd HH:mm:ss";
    private static final String[] PARSE_PATTERNS = {
        "yyyy-MM-dd", "yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd HH:mm", "yyyy-MM",
        "yyyy/MM/dd", "yyyy/MM/dd HH:mm:ss", "yyyy/MM/dd HH:mm", "yyyy/MM",
        "yyyy.MM.dd", "yyyy.MM.dd HH:mm:ss", "yyyy.MM.dd HH:mm", "yyyy.MM"};
    /**
     * èŽ·å–å½“å‰Date型日期
     *
     * @return Date() å½“前日期
     */
    public static Date getNowDate() {
        return new Date();
    }
    /**
     * èŽ·å–å½“å‰æ—¥æœŸ, é»˜è®¤æ ¼å¼ä¸ºyyyy-MM-dd
     *
     * @return String
     */
    public static String getDate() {
        return dateTimeNow(YYYY_MM_DD);
    }
    public static String getTime() {
        return dateTimeNow(YYYY_MM_DD_HH_MM_SS);
    }
    public static String dateTimeNow() {
        return dateTimeNow(YYYYMMDDHHMMSS);
    }
    public static String dateTimeNow(final String format) {
        return parseDateToStr(format, new Date());
    }
    public static String dateTime(final Date date) {
        return parseDateToStr(YYYY_MM_DD, date);
    }
    public static String parseDateToStr(final String format, final Date date) {
        return new SimpleDateFormat(format).format(date);
    }
    public static Date dateTime(final String format, final String ts) {
        try {
            return new SimpleDateFormat(format).parse(ts);
        } catch (ParseException e) {
            throw new RuntimeException(e);
        }
    }
    /**
     * æ—¥æœŸè·¯å¾„ å³å¹´/月/日 å¦‚2018/08/08
     */
    public static String datePath() {
        Date now = new Date();
        return DateFormatUtils.format(now, "yyyy/MM/dd");
    }
    /**
     * æ—¥æœŸè·¯å¾„ å³å¹´/月/日 å¦‚20180808
     */
    public static String dateTime() {
        Date now = new Date();
        return DateFormatUtils.format(now, "yyyyMMdd");
    }
    /**
     * æ—¥æœŸåž‹å­—符串转化为日期 æ ¼å¼
     */
    public static Date parseDate(Object str) {
        if (str == null) {
            return null;
        }
        try {
            return parseDate(str.toString(), PARSE_PATTERNS);
        } catch (ParseException e) {
            return null;
        }
    }
    /**
     * èŽ·å–æœåŠ¡å™¨å¯åŠ¨æ—¶é—´
     */
    public static Date getServerStartDate() {
        long time = ManagementFactory.getRuntimeMXBean().getStartTime();
        return new Date(time);
    }
    /**
     * è®¡ç®—相差天数
     */
    public static int differentDaysByMillisecond(Date date1, Date date2) {
        return Math.abs((int) ((date2.getTime() - date1.getTime()) / (1000 * 3600 * 24)));
    }
    /**
     * è®¡ç®—两个时间差
     */
    public static String getDatePoor(Date endDate, Date nowDate) {
        long nd = 1000 * 24 * 60 * 60;
        long nh = 1000 * 60 * 60;
        long nm = 1000 * 60;
        // long ns = 1000;
        // èŽ·å¾—ä¸¤ä¸ªæ—¶é—´çš„æ¯«ç§’æ—¶é—´å·®å¼‚
        long diff = endDate.getTime() - nowDate.getTime();
        // è®¡ç®—差多少天
        long day = diff / nd;
        // è®¡ç®—差多少小时
        long hour = diff % nd / nh;
        // è®¡ç®—差多少分钟
        long min = diff % nd % nh / nm;
        // è®¡ç®—差多少秒//输出结果
        // long sec = diff % nd % nh % nm / ns;
        return day + "天" + hour + "小时" + min + "分钟";
    }
    /**
     * å¢žåŠ  LocalDateTime ==> Date
     */
    public static Date toDate(LocalDateTime temporalAccessor) {
        ZonedDateTime zdt = temporalAccessor.atZone(ZoneId.systemDefault());
        return Date.from(zdt.toInstant());
    }
    /**
     * å¢žåŠ  LocalDate ==> Date
     */
    public static Date toDate(LocalDate temporalAccessor) {
        LocalDateTime localDateTime = LocalDateTime.of(temporalAccessor, LocalTime.of(0, 0, 0));
        ZonedDateTime zdt = localDateTime.atZone(ZoneId.systemDefault());
        return Date.from(zdt.toInstant());
    }
}
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/JsonUtils.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,111 @@
package com.ruoyi.common.core.utils;
import cn.hutool.core.lang.Dict;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.ObjectUtil;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.exc.MismatchedInputException;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
/**
 * JSON å·¥å…·ç±»
 *
 * @author èŠ‹é“æºç 
 */
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class JsonUtils {
    private static final ObjectMapper OBJECT_MAPPER = SpringUtils.getBean(ObjectMapper.class);
    public static ObjectMapper getObjectMapper() {
        return OBJECT_MAPPER;
    }
    public static String toJsonString(Object object) {
        if (ObjectUtil.isNull(object)) {
            return null;
        }
        try {
            return OBJECT_MAPPER.writeValueAsString(object);
        } catch (JsonProcessingException e) {
            throw new RuntimeException(e);
        }
    }
    public static <T> T parseObject(String text, Class<T> clazz) {
        if (StringUtils.isEmpty(text)) {
            return null;
        }
        try {
            return OBJECT_MAPPER.readValue(text, clazz);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
    public static <T> T parseObject(byte[] bytes, Class<T> clazz) {
        if (ArrayUtil.isEmpty(bytes)) {
            return null;
        }
        try {
            return OBJECT_MAPPER.readValue(bytes, clazz);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
    public static <T> T parseObject(String text, TypeReference<T> typeReference) {
        if (StringUtils.isBlank(text)) {
            return null;
        }
        try {
            return OBJECT_MAPPER.readValue(text, typeReference);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
    public static Dict parseMap(String text) {
        if (StringUtils.isBlank(text)) {
            return null;
        }
        try {
            return OBJECT_MAPPER.readValue(text, OBJECT_MAPPER.getTypeFactory().constructType(Dict.class));
        } catch (MismatchedInputException e) {
            // ç±»åž‹ä¸åŒ¹é…è¯´æ˜Žä¸æ˜¯json
            return null;
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
    public static List<Dict> parseArrayMap(String text) {
        if (StringUtils.isBlank(text)) {
            return null;
        }
        try {
            return OBJECT_MAPPER.readValue(text, OBJECT_MAPPER.getTypeFactory().constructCollectionType(List.class, Dict.class));
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
    public static <T> List<T> parseArray(String text, Class<T> clazz) {
        if (StringUtils.isEmpty(text)) {
            return new ArrayList<>();
        }
        try {
            return OBJECT_MAPPER.readValue(text, OBJECT_MAPPER.getTypeFactory().constructCollectionType(List.class, clazz));
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
}
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/MessageUtils.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,28 @@
package com.ruoyi.common.core.utils;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import org.springframework.context.MessageSource;
import org.springframework.context.i18n.LocaleContextHolder;
/**
 * èŽ·å–i18n资源文件
 *
 * @author Lion Li
 */
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class MessageUtils {
    private static final MessageSource MESSAGE_SOURCE = SpringUtils.getBean(MessageSource.class);
    /**
     * æ ¹æ®æ¶ˆæ¯é”®å’Œå‚æ•° èŽ·å–æ¶ˆæ¯ å§”托给spring messageSource
     *
     * @param code æ¶ˆæ¯é”®
     * @param args å‚æ•°
     * @return èŽ·å–å›½é™…åŒ–ç¿»è¯‘å€¼
     */
    public static String message(String code, Object... args) {
        return MESSAGE_SOURCE.getMessage(code, args, LocaleContextHolder.getLocale());
    }
}
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/ServletUtils.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,203 @@
package com.ruoyi.common.core.utils;
import cn.hutool.core.convert.Convert;
import cn.hutool.extra.servlet.JakartaServletUtil;
import cn.hutool.http.HttpStatus;
import com.ruoyi.common.core.constant.Constants;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpSession;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import org.springframework.http.MediaType;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
/**
 * å®¢æˆ·ç«¯å·¥å…·ç±»
 *
 * @author ruoyi
 */
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class ServletUtils extends JakartaServletUtil {
    /**
     * èŽ·å–String参数
     */
    public static String getParameter(String name) {
        return getRequest().getParameter(name);
    }
    /**
     * èŽ·å–String参数
     */
    public static String getParameter(String name, String defaultValue) {
        return Convert.toStr(getRequest().getParameter(name), defaultValue);
    }
    /**
     * èŽ·å–Integer参数
     */
    public static Integer getParameterToInt(String name) {
        return Convert.toInt(getRequest().getParameter(name));
    }
    /**
     * èŽ·å–Integer参数
     */
    public static Integer getParameterToInt(String name, Integer defaultValue) {
        return Convert.toInt(getRequest().getParameter(name), defaultValue);
    }
    /**
     * èŽ·å–Boolean参数
     */
    public static Boolean getParameterToBool(String name) {
        return Convert.toBool(getRequest().getParameter(name));
    }
    /**
     * èŽ·å–Boolean参数
     */
    public static Boolean getParameterToBool(String name, Boolean defaultValue) {
        return Convert.toBool(getRequest().getParameter(name), defaultValue);
    }
    /**
     * èŽ·å¾—æ‰€æœ‰è¯·æ±‚å‚æ•°
     *
     * @param request è¯·æ±‚对象{@link ServletRequest}
     * @return Map
     */
    public static Map<String, String[]> getParams(ServletRequest request) {
        final Map<String, String[]> map = request.getParameterMap();
        return Collections.unmodifiableMap(map);
    }
    /**
     * èŽ·å¾—æ‰€æœ‰è¯·æ±‚å‚æ•°
     *
     * @param request è¯·æ±‚对象{@link ServletRequest}
     * @return Map
     */
    public static Map<String, String> getParamMap(ServletRequest request) {
        Map<String, String> params = new HashMap<>();
        for (Map.Entry<String, String[]> entry : getParams(request).entrySet()) {
            params.put(entry.getKey(), StringUtils.join(entry.getValue(), ","));
        }
        return params;
    }
    /**
     * èŽ·å–request
     */
    public static HttpServletRequest getRequest() {
        return getRequestAttributes().getRequest();
    }
    /**
     * èŽ·å–response
     */
    public static HttpServletResponse getResponse() {
        return getRequestAttributes().getResponse();
    }
    /**
     * èŽ·å–session
     */
    public static HttpSession getSession() {
        return getRequest().getSession();
    }
    public static ServletRequestAttributes getRequestAttributes() {
        RequestAttributes attributes = RequestContextHolder.getRequestAttributes();
        return (ServletRequestAttributes) attributes;
    }
    /**
     * å°†å­—符串渲染到客户端
     *
     * @param response æ¸²æŸ“对象
     * @param string   å¾…渲染的字符串
     */
    public static void renderString(HttpServletResponse response, String string) {
        try {
            response.setStatus(HttpStatus.HTTP_OK);
            response.setContentType(MediaType.APPLICATION_JSON_VALUE);
            response.setCharacterEncoding(StandardCharsets.UTF_8.toString());
            response.getWriter().print(string);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    /**
     * æ˜¯å¦æ˜¯Ajax异步请求
     *
     * @param request
     */
    public static boolean isAjaxRequest(HttpServletRequest request) {
        String accept = request.getHeader("accept");
        if (accept != null && accept.contains(MediaType.APPLICATION_JSON_VALUE)) {
            return true;
        }
        String xRequestedWith = request.getHeader("X-Requested-With");
        if (xRequestedWith != null && xRequestedWith.contains("XMLHttpRequest")) {
            return true;
        }
        String uri = request.getRequestURI();
        if (StringUtils.equalsAnyIgnoreCase(uri, ".json", ".xml")) {
            return true;
        }
        String ajax = request.getParameter("__ajax");
        return StringUtils.equalsAnyIgnoreCase(ajax, "json", "xml");
    }
    public static String getClientIP() {
        return getClientIP(getRequest());
    }
    /**
     * å†…容编码
     *
     * @param str å†…容
     * @return ç¼–码后的内容
     */
    public static String urlEncode(String str) {
        try {
            return URLEncoder.encode(str, Constants.UTF8);
        } catch (UnsupportedEncodingException e) {
            return StringUtils.EMPTY;
        }
    }
    /**
     * å†…容解码
     *
     * @param str å†…容
     * @return è§£ç åŽçš„内容
     */
    public static String urlDecode(String str) {
        try {
            return URLDecoder.decode(str, Constants.UTF8);
        } catch (UnsupportedEncodingException e) {
            return StringUtils.EMPTY;
        }
    }
}
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/SpringUtils.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,74 @@
package com.ruoyi.common.core.utils;
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;
/**
 * spring工具类
 *
 * @author Lion Li
 */
@Component
public final class SpringUtils extends SpringUtil {
    /**
     * å¦‚æžœBeanFactory包含一个与所给名称匹配的bean定义,则返回true
     *
     * @param name
     * @return boolean
     */
    public static boolean containsBean(String name) {
        return getBeanFactory().containsBean(name);
    }
    /**
     * åˆ¤æ–­ä»¥ç»™å®šåå­—注册的bean定义是一个singleton还是一个prototype。
     * å¦‚果与给定名字相应的bean定义没有被找到,将会抛出一个异常(NoSuchBeanDefinitionException)
     *
     * @param name
     * @return boolean
     */
    public static boolean isSingleton(String name) throws NoSuchBeanDefinitionException {
        return getBeanFactory().isSingleton(name);
    }
    /**
     * @param name
     * @return Class æ³¨å†Œå¯¹è±¡çš„类型
     */
    public static Class<?> getType(String name) throws NoSuchBeanDefinitionException {
        return getBeanFactory().getType(name);
    }
    /**
     * å¦‚果给定的bean名字在bean定义中有别名,则返回这些别名
     *
     * @param name
     */
    public static String[] getAliases(String name) throws NoSuchBeanDefinitionException {
        return getBeanFactory().getAliases(name);
    }
    /**
     * èŽ·å–aop代理对象
     *
     * @param invoker
     * @return
     */
    @SuppressWarnings("unchecked")
    public static <T> T getAopProxy(T invoker) {
        return (T) AopContext.currentProxy();
    }
    /**
     * èŽ·å–spring上下文
     */
    public static ApplicationContext context() {
        return getApplicationContext();
    }
}
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/StreamUtils.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,251 @@
package com.ruoyi.common.core.utils;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.map.MapUtil;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import java.util.*;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;
/**
 * stream æµå·¥å…·ç±»
 *
 * @author Lion Li
 */
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class StreamUtils {
    /**
     * å°†collection过滤
     *
     * @param collection éœ€è¦è½¬åŒ–的集合
     * @param function   è¿‡æ»¤æ–¹æ³•
     * @return è¿‡æ»¤åŽçš„list
     */
    public static <E> List<E> filter(Collection<E> collection, Predicate<E> function) {
        if (CollUtil.isEmpty(collection)) {
            return CollUtil.newArrayList();
        }
        return collection.stream().filter(function).collect(Collectors.toList());
    }
    /**
     * å°†collection拼接
     *
     * @param collection éœ€è¦è½¬åŒ–的集合
     * @param function   æ‹¼æŽ¥æ–¹æ³•
     * @return æ‹¼æŽ¥åŽçš„list
     */
    public static <E> String join(Collection<E> collection, Function<E, String> function) {
        return join(collection, function, ",");
    }
    /**
     * å°†collection拼接
     *
     * @param collection éœ€è¦è½¬åŒ–的集合
     * @param function   æ‹¼æŽ¥æ–¹æ³•
     * @param delimiter  æ‹¼æŽ¥ç¬¦
     * @return æ‹¼æŽ¥åŽçš„list
     */
    public static <E> String join(Collection<E> collection, Function<E, String> function, CharSequence delimiter) {
        if (CollUtil.isEmpty(collection)) {
            return StringUtils.EMPTY;
        }
        return collection.stream().map(function).filter(Objects::nonNull).collect(Collectors.joining(delimiter));
    }
    /**
     * å°†collection排序
     *
     * @param collection éœ€è¦è½¬åŒ–的集合
     * @param comparing  æŽ’序方法
     * @return æŽ’序后的list
     */
    public static <E> List<E> sorted(Collection<E> collection, Comparator<E> comparing) {
        if (CollUtil.isEmpty(collection)) {
            return CollUtil.newArrayList();
        }
        return collection.stream().sorted(comparing).collect(Collectors.toList());
    }
    /**
     * å°†collection转化为类型不变的map<br>
     * <B>{@code Collection<V>  ---->  Map<K,V>}</B>
     *
     * @param collection éœ€è¦è½¬åŒ–的集合
     * @param key        V类型转化为K类型的lambda方法
     * @param <V>        collection中的泛型
     * @param <K>        map中的key类型
     * @return è½¬åŒ–后的map
     */
    public static <V, K> Map<K, V> toIdentityMap(Collection<V> collection, Function<V, K> key) {
        if (CollUtil.isEmpty(collection)) {
            return MapUtil.newHashMap();
        }
        return collection.stream().collect(Collectors.toMap(key, Function.identity(), (l, r) -> l));
    }
    /**
     * å°†Collection转化为map(value类型与collection的泛型不同)<br>
     * <B>{@code Collection<E> -----> Map<K,V>  }</B>
     *
     * @param collection éœ€è¦è½¬åŒ–的集合
     * @param key        E类型转化为K类型的lambda方法
     * @param value      E类型转化为V类型的lambda方法
     * @param <E>        collection中的泛型
     * @param <K>        map中的key类型
     * @param <V>        map中的value类型
     * @return è½¬åŒ–后的map
     */
    public static <E, K, V> Map<K, V> toMap(Collection<E> collection, Function<E, K> key, Function<E, V> value) {
        if (CollUtil.isEmpty(collection)) {
            return MapUtil.newHashMap();
        }
        return collection.stream().collect(Collectors.toMap(key, value, (l, r) -> l));
    }
    /**
     * å°†collection按照规则(比如有相同的班级id)分类成map<br>
     * <B>{@code Collection<E> -------> Map<K,List<E>> } </B>
     *
     * @param collection éœ€è¦åˆ†ç±»çš„集合
     * @param key        åˆ†ç±»çš„规则
     * @param <E>        collection中的泛型
     * @param <K>        map中的key类型
     * @return åˆ†ç±»åŽçš„map
     */
    public static <E, K> Map<K, List<E>> groupByKey(Collection<E> collection, Function<E, K> key) {
        if (CollUtil.isEmpty(collection)) {
            return MapUtil.newHashMap();
        }
        return collection
            .stream()
            .collect(Collectors.groupingBy(key, LinkedHashMap::new, Collectors.toList()));
    }
    /**
     * å°†collection按照两个规则(比如有相同的年级id,班级id)分类成双层map<br>
     * <B>{@code Collection<E>  --->  Map<T,Map<U,List<E>>> } </B>
     *
     * @param collection éœ€è¦åˆ†ç±»çš„集合
     * @param key1       ç¬¬ä¸€ä¸ªåˆ†ç±»çš„规则
     * @param key2       ç¬¬äºŒä¸ªåˆ†ç±»çš„规则
     * @param <E>        é›†åˆå…ƒç´ ç±»åž‹
     * @param <K>        ç¬¬ä¸€ä¸ªmap中的key类型
     * @param <U>        ç¬¬äºŒä¸ªmap中的key类型
     * @return åˆ†ç±»åŽçš„map
     */
    public static <E, K, U> Map<K, Map<U, List<E>>> groupBy2Key(Collection<E> collection, Function<E, K> key1, Function<E, U> key2) {
        if (CollUtil.isEmpty(collection)) {
            return MapUtil.newHashMap();
        }
        return collection
            .stream()
            .collect(Collectors.groupingBy(key1, LinkedHashMap::new, Collectors.groupingBy(key2, LinkedHashMap::new, Collectors.toList())));
    }
    /**
     * å°†collection按照两个规则(比如有相同的年级id,班级id)分类成双层map<br>
     * <B>{@code Collection<E>  --->  Map<T,Map<U,E>> } </B>
     *
     * @param collection éœ€è¦åˆ†ç±»çš„集合
     * @param key1       ç¬¬ä¸€ä¸ªåˆ†ç±»çš„规则
     * @param key2       ç¬¬äºŒä¸ªåˆ†ç±»çš„规则
     * @param <T>        ç¬¬ä¸€ä¸ªmap中的key类型
     * @param <U>        ç¬¬äºŒä¸ªmap中的key类型
     * @param <E>        collection中的泛型
     * @return åˆ†ç±»åŽçš„map
     */
    public static <E, T, U> Map<T, Map<U, E>> group2Map(Collection<E> collection, Function<E, T> key1, Function<E, U> key2) {
        if (CollUtil.isEmpty(collection) || key1 == null || key2 == null) {
            return MapUtil.newHashMap();
        }
        return collection
            .stream()
            .collect(Collectors.groupingBy(key1, LinkedHashMap::new, Collectors.toMap(key2, Function.identity(), (l, r) -> l)));
    }
    /**
     * å°†collection转化为List集合,但是两者的泛型不同<br>
     * <B>{@code Collection<E>  ------>  List<T> } </B>
     *
     * @param collection éœ€è¦è½¬åŒ–的集合
     * @param function   collection中的泛型转化为list泛型的lambda表达式
     * @param <E>        collection中的泛型
     * @param <T>        List中的泛型
     * @return è½¬åŒ–后的list
     */
    public static <E, T> List<T> toList(Collection<E> collection, Function<E, T> function) {
        if (CollUtil.isEmpty(collection)) {
            return CollUtil.newArrayList();
        }
        return collection
            .stream()
            .map(function)
            .filter(Objects::nonNull)
            .collect(Collectors.toList());
    }
    /**
     * å°†collection转化为Set集合,但是两者的泛型不同<br>
     * <B>{@code Collection<E>  ------>  Set<T> } </B>
     *
     * @param collection éœ€è¦è½¬åŒ–的集合
     * @param function   collection中的泛型转化为set泛型的lambda表达式
     * @param <E>        collection中的泛型
     * @param <T>        Set中的泛型
     * @return è½¬åŒ–后的Set
     */
    public static <E, T> Set<T> toSet(Collection<E> collection, Function<E, T> function) {
        if (CollUtil.isEmpty(collection) || function == null) {
            return CollUtil.newHashSet();
        }
        return collection
            .stream()
            .map(function)
            .filter(Objects::nonNull)
            .collect(Collectors.toSet());
    }
    /**
     * åˆå¹¶ä¸¤ä¸ªç›¸åŒkey类型的map
     *
     * @param map1  ç¬¬ä¸€ä¸ªéœ€è¦åˆå¹¶çš„ map
     * @param map2  ç¬¬äºŒä¸ªéœ€è¦åˆå¹¶çš„ map
     * @param merge åˆå¹¶çš„lambda,将key  value1 value2合并成最终的类型,注意value可能为空的情况
     * @param <K>   map中的key类型
     * @param <X>   ç¬¬ä¸€ä¸ª map的value类型
     * @param <Y>   ç¬¬äºŒä¸ª map的value类型
     * @param <V>   æœ€ç»ˆmap的value类型
     * @return åˆå¹¶åŽçš„map
     */
    public static <K, X, Y, V> Map<K, V> merge(Map<K, X> map1, Map<K, Y> map2, BiFunction<X, Y, V> merge) {
        if (MapUtil.isEmpty(map1) && MapUtil.isEmpty(map2)) {
            return MapUtil.newHashMap();
        } else if (MapUtil.isEmpty(map1)) {
            map1 = MapUtil.newHashMap();
        } else if (MapUtil.isEmpty(map2)) {
            map2 = MapUtil.newHashMap();
        }
        Set<K> key = new HashSet<>();
        key.addAll(map1.keySet());
        key.addAll(map2.keySet());
        Map<K, V> map = new HashMap<>();
        for (K t : key) {
            X x = map1.get(t);
            Y y = map2.get(t);
            V z = merge.apply(x, y);
            if (z != null) {
                map.put(t, z);
            }
        }
        return map;
    }
}
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/StringUtils.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,273 @@
package com.ruoyi.common.core.utils;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Validator;
import cn.hutool.core.util.StrUtil;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import org.springframework.util.AntPathMatcher;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
 * å­—符串工具类
 *
 * @author Lion Li
 */
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class StringUtils extends org.apache.commons.lang3.StringUtils {
    /**
     * èŽ·å–å‚æ•°ä¸ä¸ºç©ºå€¼
     *
     * @param str defaultValue è¦åˆ¤æ–­çš„value
     * @return value è¿”回值
     */
    public static String blankToDefault(String str, String defaultValue) {
        return StrUtil.blankToDefault(str, defaultValue);
    }
    /**
     * * åˆ¤æ–­ä¸€ä¸ªå­—符串是否为空串
     *
     * @param str String
     * @return true:为空 false:非空
     */
    public static boolean isEmpty(String str) {
        return StrUtil.isEmpty(str);
    }
    /**
     * * åˆ¤æ–­ä¸€ä¸ªå­—符串是否为非空串
     *
     * @param str String
     * @return true:非空串 false:空串
     */
    public static boolean isNotEmpty(String str) {
        return !isEmpty(str);
    }
    /**
     * åŽ»ç©ºæ ¼
     */
    public static String trim(String str) {
        return StrUtil.trim(str);
    }
    /**
     * æˆªå–字符串
     *
     * @param str   å­—符串
     * @param start å¼€å§‹
     * @return ç»“æžœ
     */
    public static String substring(final String str, int start) {
        return substring(str, start, str.length());
    }
    /**
     * æˆªå–字符串
     *
     * @param str   å­—符串
     * @param start å¼€å§‹
     * @param end   ç»“束
     * @return ç»“æžœ
     */
    public static String substring(final String str, int start, int end) {
        return StrUtil.sub(str, start, end);
    }
    /**
     * æ ¼å¼åŒ–文本, {} è¡¨ç¤ºå ä½ç¬¦<br>
     * æ­¤æ–¹æ³•只是简单将占位符 {} æŒ‰ç…§é¡ºåºæ›¿æ¢ä¸ºå‚æ•°<br>
     * å¦‚果想输出 {} ä½¿ç”¨ \\转义 { å³å¯ï¼Œå¦‚果想输出 {} ä¹‹å‰çš„ \ ä½¿ç”¨åŒè½¬ä¹‰ç¬¦ \\\\ å³å¯<br>
     * ä¾‹ï¼š<br>
     * é€šå¸¸ä½¿ç”¨ï¼šformat("this is {} for {}", "a", "b") -> this is a for b<br>
     * è½¬ä¹‰{}: format("this is \\{} for {}", "a", "b") -> this is {} for a<br>
     * è½¬ä¹‰\: format("this is \\\\{} for {}", "a", "b") -> this is \a for b<br>
     *
     * @param template æ–‡æœ¬æ¨¡æ¿ï¼Œè¢«æ›¿æ¢çš„部分用 {} è¡¨ç¤º
     * @param params   å‚数值
     * @return æ ¼å¼åŒ–后的文本
     */
    public static String format(String template, Object... params) {
        return StrUtil.format(template, params);
    }
    /**
     * æ˜¯å¦ä¸ºhttp(s)://开头
     *
     * @param link é“¾æŽ¥
     * @return ç»“æžœ
     */
    public static boolean ishttp(String link) {
        return Validator.isUrl(link);
    }
    /**
     * å­—符串转set
     *
     * @param str å­—符串
     * @param sep åˆ†éš”符
     * @return set集合
     */
    public static Set<String> str2Set(String str, String sep) {
        return new HashSet<>(str2List(str, sep, true, false));
    }
    /**
     * å­—符串转list
     *
     * @param str         å­—符串
     * @param sep         åˆ†éš”符
     * @param filterBlank è¿‡æ»¤çº¯ç©ºç™½
     * @param trim        åŽ»æŽ‰é¦–å°¾ç©ºç™½
     * @return list集合
     */
    public static List<String> str2List(String str, String sep, boolean filterBlank, boolean trim) {
        List<String> list = new ArrayList<>();
        if (isEmpty(str)) {
            return list;
        }
        // è¿‡æ»¤ç©ºç™½å­—符串
        if (filterBlank && isBlank(str)) {
            return list;
        }
        String[] split = str.split(sep);
        for (String string : split) {
            if (filterBlank && isBlank(string)) {
                continue;
            }
            if (trim) {
                string = trim(string);
            }
            list.add(string);
        }
        return list;
    }
    /**
     * æŸ¥æ‰¾æŒ‡å®šå­—符串是否包含指定字符串列表中的任意一个字符串同时串忽略大小写
     *
     * @param cs                  æŒ‡å®šå­—符串
     * @param searchCharSequences éœ€è¦æ£€æŸ¥çš„字符串数组
     * @return æ˜¯å¦åŒ…含任意一个字符串
     */
    public static boolean containsAnyIgnoreCase(CharSequence cs, CharSequence... searchCharSequences) {
        return StrUtil.containsAnyIgnoreCase(cs, searchCharSequences);
    }
    /**
     * é©¼å³°è½¬ä¸‹åˆ’线命名
     */
    public static String toUnderScoreCase(String str) {
        return StrUtil.toUnderlineCase(str);
    }
    /**
     * æ˜¯å¦åŒ…含字符串
     *
     * @param str  éªŒè¯å­—符串
     * @param strs å­—符串组
     * @return åŒ…含返回true
     */
    public static boolean inStringIgnoreCase(String str, String... strs) {
        return StrUtil.equalsAnyIgnoreCase(str, strs);
    }
    /**
     * å°†ä¸‹åˆ’线大写方式命名的字符串转换为驼峰式。如果转换前的下划线大写方式命名的字符串为空,则返回空字符串。 ä¾‹å¦‚:HELLO_WORLD->HelloWorld
     *
     * @param name è½¬æ¢å‰çš„下划线大写方式命名的字符串
     * @return è½¬æ¢åŽçš„驼峰式命名的字符串
     */
    public static String convertToCamelCase(String name) {
        return StrUtil.upperFirst(StrUtil.toCamelCase(name));
    }
    /**
     * é©¼å³°å¼å‘½åæ³• ä¾‹å¦‚:user_name->userName
     */
    public static String toCamelCase(String s) {
        return StrUtil.toCamelCase(s);
    }
    /**
     * æŸ¥æ‰¾æŒ‡å®šå­—符串是否匹配指定字符串列表中的任意一个字符串
     *
     * @param str  æŒ‡å®šå­—符串
     * @param strs éœ€è¦æ£€æŸ¥çš„字符串数组
     * @return æ˜¯å¦åŒ¹é…
     */
    public static boolean matches(String str, List<String> strs) {
        if (isEmpty(str) || CollUtil.isEmpty(strs)) {
            return false;
        }
        for (String pattern : strs) {
            if (isMatch(pattern, str)) {
                return true;
            }
        }
        return false;
    }
    /**
     * åˆ¤æ–­url是否与规则配置:
     * ? è¡¨ç¤ºå•个字符;
     * * è¡¨ç¤ºä¸€å±‚路径内的任意字符串,不可跨层级;
     * ** è¡¨ç¤ºä»»æ„å±‚路径;
     *
     * @param pattern åŒ¹é…è§„则
     * @param url     éœ€è¦åŒ¹é…çš„url
     * @return
     */
    public static boolean isMatch(String pattern, String url) {
        AntPathMatcher matcher = new AntPathMatcher();
        return matcher.match(pattern, url);
    }
    /**
     * æ•°å­—左边补齐0,使之达到指定长度。注意,如果数字转换为字符串后,长度大于size,则只保留 æœ€åŽsize个字符。
     *
     * @param num æ•°å­—对象
     * @param size å­—符串指定长度
     * @return è¿”回数字的字符串格式,该字符串为指定长度。
     */
    public static final String padl(final Number num, final int size) {
        return padl(num.toString(), size, '0');
    }
    /**
     * å­—符串左补齐。如果原始字符串s长度大于size,则只保留最后size个字符。
     *
     * @param s åŽŸå§‹å­—ç¬¦ä¸²
     * @param size å­—符串指定长度
     * @param c ç”¨äºŽè¡¥é½çš„字符
     * @return è¿”回指定长度的字符串,由原字符串左补齐或截取得到。
     */
    public static final String padl(final String s, final int size, final char c) {
        final StringBuilder sb = new StringBuilder(size);
        if (s != null) {
            final int len = s.length();
            if (s.length() <= size) {
                for (int i = size - len; i > 0; i--) {
                    sb.append(c);
                }
                sb.append(s);
            } else {
                return s.substring(len - size, len);
            }
        } else {
            for (int i = size; i > 0; i--) {
                sb.append(c);
            }
        }
        return sb.toString();
    }
}
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/Threads.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,75 @@
package com.ruoyi.common.core.utils;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.*;
/**
 * çº¿ç¨‹ç›¸å…³å·¥å…·ç±».
 *
 * @author ruoyi
 */
@Slf4j
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class Threads {
    /**
     * sleep等待,单位为毫秒
     */
    public static void sleep(long milliseconds) {
        try {
            Thread.sleep(milliseconds);
        } catch (InterruptedException e) {
            return;
        }
    }
    /**
     * åœæ­¢çº¿ç¨‹æ± 
     * å…ˆä½¿ç”¨shutdown, åœæ­¢æŽ¥æ”¶æ–°ä»»åŠ¡å¹¶å°è¯•å®Œæˆæ‰€æœ‰å·²å­˜åœ¨ä»»åŠ¡.
     * å¦‚果超时, åˆ™è°ƒç”¨shutdownNow, å–消在workQueue中Pending的任务,并中断所有阻塞函数.
     * å¦‚果仍然超時,則強制退出.
     * å¦å¯¹åœ¨shutdown时线程本身被调用中断做了处理.
     */
    public static void shutdownAndAwaitTermination(ExecutorService pool) {
        if (pool != null && !pool.isShutdown()) {
            pool.shutdown();
            try {
                if (!pool.awaitTermination(120, TimeUnit.SECONDS)) {
                    pool.shutdownNow();
                    if (!pool.awaitTermination(120, TimeUnit.SECONDS)) {
                        log.info("Pool did not terminate");
                    }
                }
            } catch (InterruptedException ie) {
                pool.shutdownNow();
                Thread.currentThread().interrupt();
            }
        }
    }
    /**
     * æ‰“印线程异常信息
     */
    public static void printException(Runnable r, Throwable t) {
        if (t == null && r instanceof Future<?>) {
            try {
                Future<?> future = (Future<?>) r;
                if (future.isDone()) {
                    future.get();
                }
            } catch (CancellationException ce) {
                t = ce;
            } catch (ExecutionException ee) {
                t = ee.getCause();
            } catch (InterruptedException ie) {
                Thread.currentThread().interrupt();
            }
        }
        if (t != null) {
            log.error(t.getMessage(), t);
        }
    }
}
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/TreeBuildUtils.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,35 @@
package com.ruoyi.common.core.utils;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.tree.Tree;
import cn.hutool.core.lang.tree.TreeNodeConfig;
import cn.hutool.core.lang.tree.TreeUtil;
import cn.hutool.core.lang.tree.parser.NodeParser;
import com.ruoyi.common.core.utils.reflect.ReflectUtils;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import java.util.List;
/**
 * æ‰©å±• hutool TreeUtil å°è£…系统树构建
 *
 * @author Lion Li
 */
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class TreeBuildUtils extends TreeUtil {
    /**
     * æ ¹æ®å‰ç«¯å®šåˆ¶å·®å¼‚化字段
     */
    public static final TreeNodeConfig DEFAULT_CONFIG = TreeNodeConfig.DEFAULT_CONFIG.setNameKey("label");
    public static <T, K> List<Tree<K>> build(List<T> list, NodeParser<T, K> nodeParser) {
        if (CollUtil.isEmpty(list)) {
            return null;
        }
        K k = ReflectUtils.invokeGetter(list.get(0), "parentId");
        return TreeUtil.build(list, k, DEFAULT_CONFIG, nodeParser);
    }
}
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/ValidatorUtils.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,28 @@
package com.ruoyi.common.core.utils;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import jakarta.validation.ConstraintViolation;
import jakarta.validation.ConstraintViolationException;
import jakarta.validation.Validator;
import java.util.Set;
/**
 * Validator æ ¡éªŒæ¡†æž¶å·¥å…·
 *
 * @author Lion Li
 */
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class ValidatorUtils {
    private static final Validator VALID = SpringUtils.getBean(Validator.class);
    public static <T> void validate(T object, Class<?>... groups) {
        Set<ConstraintViolation<T>> validate = VALID.validate(object, groups);
        if (!validate.isEmpty()) {
            throw new ConstraintViolationException("参数校验异常", validate);
        }
    }
}
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/file/FileUtils.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,52 @@
package com.ruoyi.common.core.utils.file;
import cn.hutool.core.io.FileUtil;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import jakarta.servlet.http.HttpServletResponse;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
/**
 * æ–‡ä»¶å¤„理工具类
 *
 * @author Lion Li
 */
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class FileUtils extends FileUtil {
    /**
     * ä¸‹è½½æ–‡ä»¶åé‡æ–°ç¼–码
     *
     * @param response     å“åº”对象
     * @param realFileName çœŸå®žæ–‡ä»¶å
     */
    public static void setAttachmentResponseHeader(HttpServletResponse response, String realFileName) throws UnsupportedEncodingException {
        String percentEncodedFileName = percentEncode(realFileName);
        StringBuilder contentDispositionValue = new StringBuilder();
        contentDispositionValue.append("attachment; filename=")
            .append(percentEncodedFileName)
            .append(";")
            .append("filename*=")
            .append("utf-8''")
            .append(percentEncodedFileName);
        response.addHeader("Access-Control-Expose-Headers", "Content-Disposition,download-filename");
        response.setHeader("Content-disposition", contentDispositionValue.toString());
        response.setHeader("download-filename", percentEncodedFileName);
    }
    /**
     * ç™¾åˆ†å·ç¼–码工具方法
     *
     * @param s éœ€è¦ç™¾åˆ†å·ç¼–码的字符串
     * @return ç™¾åˆ†å·ç¼–码后的字符串
     */
    public static String percentEncode(String s) throws UnsupportedEncodingException {
        String encode = URLEncoder.encode(s, StandardCharsets.UTF_8.toString());
        return encode.replaceAll("\\+", "%20");
    }
}
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/file/MimeTypeUtils.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,40 @@
package com.ruoyi.common.core.utils.file;
/**
 * åª’体类型工具类
 *
 * @author ruoyi
 */
public class MimeTypeUtils {
    public static final String IMAGE_PNG = "image/png";
    public static final String IMAGE_JPG = "image/jpg";
    public static final String IMAGE_JPEG = "image/jpeg";
    public static final String IMAGE_BMP = "image/bmp";
    public static final String IMAGE_GIF = "image/gif";
    public static final String[] IMAGE_EXTENSION = {"bmp", "gif", "jpg", "jpeg", "png"};
    public static final String[] FLASH_EXTENSION = {"swf", "flv"};
    public static final String[] MEDIA_EXTENSION = {"swf", "flv", "mp3", "wav", "wma", "wmv", "mid", "avi", "mpg",
        "asf", "rm", "rmvb"};
    public static final String[] VIDEO_EXTENSION = {"mp4", "avi", "rmvb"};
    public static final String[] DEFAULT_ALLOWED_EXTENSION = {
        // å›¾ç‰‡
        "bmp", "gif", "jpg", "jpeg", "png",
        // word excel powerpoint
        "doc", "docx", "xls", "xlsx", "ppt", "pptx", "html", "htm", "txt",
        // åŽ‹ç¼©æ–‡ä»¶
        "rar", "zip", "gz", "bz2",
        // è§†é¢‘格式
        "mp4", "avi", "rmvb",
        // pdf
        "pdf"};
}
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/ip/AddressUtils.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,60 @@
package com.ruoyi.common.core.utils.ip;
import cn.hutool.core.lang.Dict;
import cn.hutool.core.net.NetUtil;
import cn.hutool.http.HtmlUtil;
import cn.hutool.http.HttpUtil;
import com.ruoyi.common.core.config.RuoYiConfig;
import com.ruoyi.common.core.constant.Constants;
import com.ruoyi.common.core.utils.JsonUtils;
import com.ruoyi.common.core.utils.StringUtils;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
/**
 * èŽ·å–åœ°å€ç±»
 *
 * @author Lion Li
 */
@Slf4j
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class AddressUtils {
    // IP地址查询
    public static final String IP_URL = "http://whois.pconline.com.cn/ipJson.jsp";
    // æœªçŸ¥åœ°å€
    public static final String UNKNOWN = "XX XX";
    public static String getRealAddressByIP(String ip) {
        String address = UNKNOWN;
        if (StringUtils.isBlank(ip)) {
            return address;
        }
        // å†…网不查询
        ip = "0:0:0:0:0:0:0:1".equals(ip) ? "127.0.0.1" : HtmlUtil.cleanHtmlTag(ip);
        if (NetUtil.isInnerIP(ip)) {
            return "内网IP";
        }
        if (RuoYiConfig.isAddressEnabled()) {
            try {
                String rspStr = HttpUtil.createGet(IP_URL)
                    .body("ip=" + ip + "&json=true", Constants.GBK)
                    .execute()
                    .body();
                if (StringUtils.isEmpty(rspStr)) {
                    log.error("获取地理位置异常 {}", ip);
                    return UNKNOWN;
                }
                Dict obj = JsonUtils.parseMap(rspStr);
                String region = obj.getStr("pro");
                String city = obj.getStr("city");
                return String.format("%s %s", region, city);
            } catch (Exception e) {
                log.error("获取地理位置异常 {}", ip);
            }
        }
        return UNKNOWN;
    }
}
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/reflect/ReflectUtils.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,56 @@
package com.ruoyi.common.core.utils.reflect;
import cn.hutool.core.util.ReflectUtil;
import com.ruoyi.common.core.utils.StringUtils;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import java.lang.reflect.Method;
/**
 * åå°„工具类. æä¾›è°ƒç”¨getter/setter方法, è®¿é—®ç§æœ‰å˜é‡, è°ƒç”¨ç§æœ‰æ–¹æ³•, èŽ·å–æ³›åž‹ç±»åž‹Class, è¢«AOP过的真实类等工具函数.
 *
 * @author Lion Li
 */
@SuppressWarnings("rawtypes")
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class ReflectUtils extends ReflectUtil {
    private static final String SETTER_PREFIX = "set";
    private static final String GETTER_PREFIX = "get";
    /**
     * è°ƒç”¨Getter方法.
     * æ”¯æŒå¤šçº§ï¼Œå¦‚:对象名.对象名.方法
     */
    @SuppressWarnings("unchecked")
    public static <E> E invokeGetter(Object obj, String propertyName) {
        Object object = obj;
        for (String name : StringUtils.split(propertyName, ".")) {
            String getterMethodName = GETTER_PREFIX + StringUtils.capitalize(name);
            object = invoke(object, getterMethodName);
        }
        return (E) object;
    }
    /**
     * è°ƒç”¨Setter方法, ä»…匹配方法名。
     * æ”¯æŒå¤šçº§ï¼Œå¦‚:对象名.对象名.方法
     */
    public static <E> void invokeSetter(Object obj, String propertyName, E value) {
        Object object = obj;
        String[] names = StringUtils.split(propertyName, ".");
        for (int i = 0; i < names.length; i++) {
            if (i < names.length - 1) {
                String getterMethodName = GETTER_PREFIX + StringUtils.capitalize(names[i]);
                object = invoke(object, getterMethodName);
            } else {
                String setterMethodName = SETTER_PREFIX + StringUtils.capitalize(names[i]);
                Method method = getMethodByName(object.getClass(), setterMethodName);
                invoke(object, method, value);
            }
        }
    }
}
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/sql/SqlUtil.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,57 @@
package com.ruoyi.common.core.utils.sql;
import com.ruoyi.common.core.exception.UtilException;
import com.ruoyi.common.core.utils.StringUtils;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
/**
 * sql操作工具类
 *
 * @author ruoyi
 */
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class SqlUtil {
    /**
     * å®šä¹‰å¸¸ç”¨çš„ sql关键字
     */
    public static final String SQL_REGEX = "select |insert |delete |update |drop |count |exec |chr |mid |master |truncate |char |and |declare ";
    /**
     * ä»…支持字母、数字、下划线、空格、逗号、小数点(支持多个字段排序)
     */
    public static final String SQL_PATTERN = "[a-zA-Z0-9_\\ \\,\\.]+";
    /**
     * æ£€æŸ¥å­—符,防止注入绕过
     */
    public static String escapeOrderBySql(String value) {
        if (StringUtils.isNotEmpty(value) && !isValidOrderBySql(value)) {
            throw new UtilException("参数不符合规范,不能进行查询");
        }
        return value;
    }
    /**
     * éªŒè¯ order by è¯­æ³•是否符合规范
     */
    public static boolean isValidOrderBySql(String value) {
        return value.matches(SQL_PATTERN);
    }
    /**
     * SQL关键字检查
     */
    public static void filterKeyword(String value) {
        if (StringUtils.isEmpty(value)) {
            return;
        }
        String[] sqlKeywords = StringUtils.split(SQL_REGEX, "\\|");
        for (String sqlKeyword : sqlKeywords) {
            if (StringUtils.indexOfIgnoreCase(value, sqlKeyword) > -1) {
                throw new UtilException("参数存在SQL注入风险");
            }
        }
    }
}
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/validate/AddGroup.java
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/validate/EditGroup.java
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/validate/QueryGroup.java
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/web/controller/BaseController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,40 @@
package com.ruoyi.common.core.web.controller;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.core.utils.StringUtils;
/**
 * web层通用数据处理
 *
 * @author Lion Li
 */
public class BaseController {
    /**
     * å“åº”返回结果
     *
     * @param rows å½±å“è¡Œæ•°
     * @return æ“ä½œç»“æžœ
     */
    protected R<Void> toAjax(int rows) {
        return rows > 0 ? R.ok() : R.fail();
    }
    /**
     * å“åº”返回结果
     *
     * @param result ç»“æžœ
     * @return æ“ä½œç»“æžœ
     */
    protected R<Void> toAjax(boolean result) {
        return result ? R.ok() : R.fail();
    }
    /**
     * é¡µé¢è·³è½¬
     */
    public String redirect(String url) {
        return StringUtils.format("redirect:{}", url);
    }
}
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/web/domain/BaseEntity.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,65 @@
package com.ruoyi.common.core.web.domain;
import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.TableField;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonInclude;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
/**
 * Entity基类
 *
 * @author Lion Li
 */
@Data
public class BaseEntity implements Serializable {
    @Serial
    private static final long serialVersionUID = 1L;
    /**
     * æœç´¢å€¼
     */
    @JsonIgnore
    @TableField(exist = false)
    private String searchValue;
    /**
     * åˆ›å»ºè€…
     */
    @TableField(fill = FieldFill.INSERT)
    private String createBy;
    /**
     * åˆ›å»ºæ—¶é—´
     */
    @TableField(fill = FieldFill.INSERT)
    private Date createTime;
    /**
     * æ›´æ–°è€…
     */
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private String updateBy;
    /**
     * æ›´æ–°æ—¶é—´
     */
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private Date updateTime;
    /**
     * è¯·æ±‚参数
     */
    @JsonInclude(JsonInclude.Include.NON_EMPTY)
    @TableField(exist = false)
    private Map<String, Object> params = new HashMap<>();
}
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/web/domain/TreeEntity.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,41 @@
package com.ruoyi.common.core.web.domain;
import com.baomidou.mybatisplus.annotation.TableField;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.io.Serial;
import java.util.ArrayList;
import java.util.List;
/**
 * Tree基类
 *
 * @author Lion Li
 */
@Data
@EqualsAndHashCode(callSuper = true)
public class TreeEntity<T> extends BaseEntity {
    @Serial
    private static final long serialVersionUID = 1L;
    /**
     * çˆ¶èœå•名称
     */
    @TableField(exist = false)
    private String parentName;
    /**
     * çˆ¶èœå•ID
     */
    private Long parentId;
    /**
     * å­éƒ¨é—¨
     */
    @TableField(exist = false)
    private List<T> children = new ArrayList<>();
}
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/xss/Xss.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,26 @@
package com.ruoyi.common.core.xss;
import jakarta.validation.Constraint;
import jakarta.validation.Payload;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
 * è‡ªå®šä¹‰xss校验注解
 *
 * @author Lion Li
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(value = {ElementType.METHOD, ElementType.FIELD, ElementType.CONSTRUCTOR, ElementType.PARAMETER})
@Constraint(validatedBy = {XssValidator.class})
public @interface Xss {
    String message() default "不允许任何脚本运行";
    Class<?>[] groups() default {};
    Class<? extends Payload>[] payload() default {};
}
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/xss/XssValidator.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,21 @@
package com.ruoyi.common.core.xss;
import cn.hutool.core.util.ReUtil;
import cn.hutool.http.HtmlUtil;
import jakarta.validation.ConstraintValidator;
import jakarta.validation.ConstraintValidatorContext;
/**
 * è‡ªå®šä¹‰xss校验注解实现
 *
 * @author Lion Li
 */
public class XssValidator implements ConstraintValidator<Xss, String> {
    @Override
    public boolean isValid(String value, ConstraintValidatorContext constraintValidatorContext) {
        return !ReUtil.contains(HtmlUtil.RE_HTML_MARK, value);
    }
}
ruoyi-common/ruoyi-common-core/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,7 @@
com.ruoyi.common.core.config.ApplicationConfig
com.ruoyi.common.core.config.AsyncConfig
com.ruoyi.common.core.config.JacksonConfig
com.ruoyi.common.core.config.RuoYiConfig
com.ruoyi.common.core.config.ThreadPoolConfig
com.ruoyi.common.core.config.ValidatorConfig
com.ruoyi.common.core.utils.SpringUtils
ruoyi-common/ruoyi-common-dict/pom.xml
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <groupId>com.ruoyi</groupId>
        <artifactId>ruoyi-common</artifactId>
        <version>${revision}</version>
        <relativePath>../pom.xml</relativePath>
    </parent>
    <modelVersion>4.0.0</modelVersion>
    <artifactId>ruoyi-common-dict</artifactId>
    <description>
        ruoyi-common-dict å­—å…¸
    </description>
    <dependencies>
        <dependency>
            <groupId>com.ruoyi</groupId>
            <artifactId>ruoyi-common-core</artifactId>
        </dependency>
    </dependencies>
</project>
ruoyi-common/ruoyi-common-dict/src/main/java/com/ruoyi/common/dict/annotation/DictDataMapper.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,27 @@
package com.ruoyi.common.dict.annotation;
import com.fasterxml.jackson.annotation.JacksonAnnotationsInside;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.ruoyi.common.dict.jackson.DictDataJsonSerializer;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
 * å­—典数据映射注解
 *
 * @author itino
 */
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.METHOD})
@JacksonAnnotationsInside
@JsonSerialize(using = DictDataJsonSerializer.class)
public @interface DictDataMapper {
    /**
     * è®¾ç½®å­—典的type值 (如: sys_user_sex)
     */
    String dictType() default "";
}
ruoyi-common/ruoyi-common-dict/src/main/java/com/ruoyi/common/dict/jackson/DictDataJsonSerializer.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,56 @@
package com.ruoyi.common.dict.jackson;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.BeanProperty;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.ser.ContextualSerializer;
import com.ruoyi.common.core.service.DictService;
import com.ruoyi.common.core.utils.SpringUtils;
import com.ruoyi.common.core.utils.StringUtils;
import com.ruoyi.common.dict.annotation.DictDataMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import java.io.IOException;
import java.util.Objects;
/**
 * å­—典数据json序列化工具
 *
 * @author itino
 */
@Slf4j
public class DictDataJsonSerializer extends JsonSerializer<String> implements ContextualSerializer {
    private String dictType;
    @Override
    public void serialize(String value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
        try {
            DictService dictService = SpringUtils.getBean(DictService.class);
            if (ObjectUtil.isNotNull(dictService)) {
                String label = dictService.getDictLabel(dictType, value);
                gen.writeString(StringUtils.isNotBlank(label) ? label : value);
            } else {
                gen.writeString(value);
            }
        } catch (BeansException e) {
            log.error("字典数据未查到, é‡‡ç”¨é»˜è®¤å¤„理 => {}", e.getMessage());
            gen.writeString(value);
        }
    }
    @Override
    public JsonSerializer<?> createContextual(SerializerProvider prov, BeanProperty property) throws JsonMappingException {
        DictDataMapper anno = property.getAnnotation(DictDataMapper.class);
        if (Objects.nonNull(anno) && StrUtil.isNotBlank(anno.dictType())) {
            this.dictType = anno.dictType();
            return this;
        }
        return prov.findValueSerializer(property.getType(), property);
    }
}
ruoyi-common/ruoyi-common-doc/pom.xml
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,36 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <groupId>com.ruoyi</groupId>
        <artifactId>ruoyi-common</artifactId>
        <version>${revision}</version>
        <relativePath>../pom.xml</relativePath>
    </parent>
    <modelVersion>4.0.0</modelVersion>
    <artifactId>ruoyi-common-doc</artifactId>
    <description>
        ruoyi-common-doc ç³»ç»ŸæŽ¥å£
    </description>
    <dependencies>
        <dependency>
            <groupId>com.ruoyi</groupId>
            <artifactId>ruoyi-common-core</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springdoc</groupId>
            <artifactId>springdoc-openapi-starter-webmvc-api</artifactId>
        </dependency>
        <dependency>
            <groupId>com.github.therapi</groupId>
            <artifactId>therapi-runtime-javadoc</artifactId>
        </dependency>
    </dependencies>
</project>
ruoyi-common/ruoyi-common-doc/src/main/java/com/ruoyi/common/doc/config/SwaggerConfig.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,125 @@
package com.ruoyi.common.doc.config;
import com.ruoyi.common.core.utils.StringUtils;
import com.ruoyi.common.doc.config.properties.SwaggerProperties;
import com.ruoyi.common.doc.handler.OpenApiHandler;
import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.Paths;
import io.swagger.v3.oas.models.info.Info;
import io.swagger.v3.oas.models.security.SecurityRequirement;
import lombok.RequiredArgsConstructor;
import org.springdoc.core.configuration.SpringDocConfiguration;
import org.springdoc.core.customizers.OpenApiBuilderCustomizer;
import org.springdoc.core.customizers.OpenApiCustomizer;
import org.springdoc.core.customizers.ServerBaseUrlCustomizer;
import org.springdoc.core.properties.SpringDocConfigProperties;
import org.springdoc.core.providers.JavadocProvider;
import org.springdoc.core.service.OpenAPIService;
import org.springdoc.core.service.SecurityService;
import org.springdoc.core.utils.PropertyResolverUtils;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.web.ServerProperties;
import org.springframework.context.annotation.Bean;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.Set;
/**
 * Swagger æ–‡æ¡£é…ç½®
 *
 * @author Lion Li
 */
@RequiredArgsConstructor
@AutoConfiguration(before = SpringDocConfiguration.class)
@ConditionalOnProperty(name = "swagger.enabled", havingValue = "true", matchIfMissing = true)
public class SwaggerConfig {
    private final SwaggerProperties swaggerProperties;
    private final ServerProperties serverProperties;
    @Bean
    @ConditionalOnMissingBean(OpenAPI.class)
    public OpenAPI openApi() {
        OpenAPI openApi = new OpenAPI();
        // æ–‡æ¡£åŸºæœ¬ä¿¡æ¯
        SwaggerProperties.InfoProperties infoProperties = swaggerProperties.getInfo();
        Info info = convertInfo(infoProperties);
        openApi.info(info);
        // æ‰©å±•文档信息
        openApi.externalDocs(swaggerProperties.getExternalDocs());
        openApi.tags(swaggerProperties.getTags());
        openApi.paths(swaggerProperties.getPaths());
        openApi.components(swaggerProperties.getComponents());
        Set<String> keySet = swaggerProperties.getComponents().getSecuritySchemes().keySet();
        List<SecurityRequirement> list = new ArrayList<>();
        SecurityRequirement securityRequirement = new SecurityRequirement();
        keySet.forEach(securityRequirement::addList);
        list.add(securityRequirement);
        openApi.security(list);
        return openApi;
    }
    private Info convertInfo(SwaggerProperties.InfoProperties infoProperties) {
        Info info = new Info();
        info.setTitle(infoProperties.getTitle());
        info.setDescription(infoProperties.getDescription());
        info.setContact(infoProperties.getContact());
        info.setLicense(infoProperties.getLicense());
        info.setVersion(infoProperties.getVersion());
        return info;
    }
    /**
     * è‡ªå®šä¹‰ openapi å¤„理器
     */
    @Bean
    public OpenAPIService openApiBuilder(Optional<OpenAPI> openAPI,
                                         SecurityService securityParser,
                                         SpringDocConfigProperties springDocConfigProperties, PropertyResolverUtils propertyResolverUtils,
                                         Optional<List<OpenApiBuilderCustomizer>> openApiBuilderCustomisers,
                                         Optional<List<ServerBaseUrlCustomizer>> serverBaseUrlCustomisers, Optional<JavadocProvider> javadocProvider) {
        return new OpenApiHandler(openAPI, securityParser, springDocConfigProperties, propertyResolverUtils, openApiBuilderCustomisers, serverBaseUrlCustomisers, javadocProvider);
    }
    /**
     * å¯¹å·²ç»ç”Ÿæˆå¥½çš„ OpenApi è¿›è¡Œè‡ªå®šä¹‰æ“ä½œ
     */
    @Bean
    public OpenApiCustomizer openApiCustomiser() {
        String contextPath = serverProperties.getServlet().getContextPath();
        String finalContextPath;
        if (StringUtils.isBlank(contextPath) || "/".equals(contextPath)) {
            finalContextPath = "";
        } else {
            finalContextPath = contextPath;
        }
        // å¯¹æ‰€æœ‰è·¯å¾„增加前置上下文路径
        return openApi -> {
            Paths oldPaths = openApi.getPaths();
            if (oldPaths instanceof PlusPaths) {
                return;
            }
            PlusPaths newPaths = new PlusPaths();
            oldPaths.forEach((k, v) -> newPaths.addPathItem(finalContextPath + k, v));
            openApi.setPaths(newPaths);
        };
    }
    /**
     * å•独使用一个类便于判断 è§£å†³springdoc路径拼接重复问题
     *
     * @author Lion Li
     */
    static class PlusPaths extends Paths {
        public PlusPaths() {
            super();
        }
    }
}
ruoyi-common/ruoyi-common-doc/src/main/java/com/ruoyi/common/doc/config/properties/SwaggerProperties.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,101 @@
package com.ruoyi.common.doc.config.properties;
import io.swagger.v3.oas.models.Components;
import io.swagger.v3.oas.models.ExternalDocumentation;
import io.swagger.v3.oas.models.Paths;
import io.swagger.v3.oas.models.info.Contact;
import io.swagger.v3.oas.models.info.License;
import io.swagger.v3.oas.models.tags.Tag;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.NestedConfigurationProperty;
import org.springframework.stereotype.Component;
import java.util.List;
/**
 * swagger é…ç½®å±žæ€§
 *
 * @author Lion Li
 */
@Data
@Component
@ConfigurationProperties(prefix = "swagger")
public class SwaggerProperties {
    /**
     * æ˜¯å¦å¼€å¯ openApi æ–‡æ¡£
     */
    private Boolean enabled = true;
    /**
     * æ–‡æ¡£åŸºæœ¬ä¿¡æ¯
     */
    @NestedConfigurationProperty
    private InfoProperties info = new InfoProperties();
    /**
     * æ‰©å±•文档地址
     */
    @NestedConfigurationProperty
    private ExternalDocumentation externalDocs;
    /**
     * æ ‡ç­¾
     */
    private List<Tag> tags = null;
    /**
     * è·¯å¾„
     */
    @NestedConfigurationProperty
    private Paths paths = null;
    /**
     * ç»„ä»¶
     */
    @NestedConfigurationProperty
    private Components components = null;
    /**
     * <p>
     * æ–‡æ¡£çš„基础属性信息
     * </p>
     *
     * @see io.swagger.v3.oas.models.info.Info
     *
     * ä¸ºäº† springboot è‡ªåŠ¨ç”Ÿäº§é…ç½®æç¤ºä¿¡æ¯ï¼Œæ‰€ä»¥è¿™é‡Œå¤åˆ¶ä¸€ä¸ªç±»å‡ºæ¥
     */
    @Data
    public static class InfoProperties {
        /**
         * æ ‡é¢˜
         */
        private String title = null;
        /**
         * æè¿°
         */
        private String description = null;
        /**
         * è”系人信息
         */
        @NestedConfigurationProperty
        private Contact contact = null;
        /**
         * è®¸å¯è¯
         */
        @NestedConfigurationProperty
        private License license = null;
        /**
         * ç‰ˆæœ¬
         */
        private String version = null;
    }
}
ruoyi-common/ruoyi-common-doc/src/main/java/com/ruoyi/common/doc/handler/OpenApiHandler.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,252 @@
package com.ruoyi.common.doc.handler;
import cn.hutool.core.io.IoUtil;
import io.swagger.v3.core.jackson.TypeNameResolver;
import io.swagger.v3.core.util.AnnotationsUtils;
import io.swagger.v3.oas.annotations.tags.Tags;
import io.swagger.v3.oas.models.Components;
import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.Operation;
import io.swagger.v3.oas.models.Paths;
import io.swagger.v3.oas.models.tags.Tag;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springdoc.core.customizers.OpenApiBuilderCustomizer;
import org.springdoc.core.customizers.ServerBaseUrlCustomizer;
import org.springdoc.core.properties.SpringDocConfigProperties;
import org.springdoc.core.providers.JavadocProvider;
import org.springdoc.core.service.OpenAPIService;
import org.springdoc.core.service.SecurityService;
import org.springdoc.core.utils.PropertyResolverUtils;
import org.springframework.context.ApplicationContext;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.util.CollectionUtils;
import org.springframework.web.method.HandlerMethod;
import java.io.StringReader;
import java.lang.reflect.Method;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
 * è‡ªå®šä¹‰ openapi å¤„理器
 * å¯¹æºç åŠŸèƒ½è¿›è¡Œä¿®æ”¹ å¢žå¼ºä½¿ç”¨
 */
@Slf4j
@SuppressWarnings("all")
public class OpenApiHandler extends OpenAPIService {
    /**
     * The Basic error controller.
     */
    private static Class<?> basicErrorController;
    /**
     * The Security parser.
     */
    private final SecurityService securityParser;
    /**
     * The Mappings map.
     */
    private final Map<String, Object> mappingsMap = new HashMap<>();
    /**
     * The Springdoc tags.
     */
    private final Map<HandlerMethod, Tag> springdocTags = new HashMap<>();
    /**
     * The Open api builder customisers.
     */
    private final Optional<List<OpenApiBuilderCustomizer>> openApiBuilderCustomisers;
    /**
     * The server base URL customisers.
     */
    private final Optional<List<ServerBaseUrlCustomizer>> serverBaseUrlCustomizers;
    /**
     * The Spring doc config properties.
     */
    private final SpringDocConfigProperties springDocConfigProperties;
    /**
     * The Cached open api map.
     */
    private final Map<String, OpenAPI> cachedOpenAPI = new HashMap<>();
    /**
     * The Property resolver utils.
     */
    private final PropertyResolverUtils propertyResolverUtils;
    /**
     * The javadoc provider.
     */
    private final Optional<JavadocProvider> javadocProvider;
    /**
     * The Context.
     */
    private ApplicationContext context;
    /**
     * The Open api.
     */
    private OpenAPI openAPI;
    /**
     * The Is servers present.
     */
    private boolean isServersPresent;
    /**
     * The Server base url.
     */
    private String serverBaseUrl;
    /**
     * Instantiates a new Open api builder.
     *
     * @param openAPI                   the open api
     * @param securityParser            the security parser
     * @param springDocConfigProperties the spring doc config properties
     * @param propertyResolverUtils     the property resolver utils
     * @param openApiBuilderCustomizers the open api builder customisers
     * @param serverBaseUrlCustomizers  the server base url customizers
     * @param javadocProvider           the javadoc provider
     */
    public OpenApiHandler(Optional<OpenAPI> openAPI, SecurityService securityParser,
                          SpringDocConfigProperties springDocConfigProperties, PropertyResolverUtils propertyResolverUtils,
                          Optional<List<OpenApiBuilderCustomizer>> openApiBuilderCustomizers,
                          Optional<List<ServerBaseUrlCustomizer>> serverBaseUrlCustomizers,
                          Optional<JavadocProvider> javadocProvider) {
        super(openAPI, securityParser, springDocConfigProperties, propertyResolverUtils, openApiBuilderCustomizers, serverBaseUrlCustomizers, javadocProvider);
        if (openAPI.isPresent()) {
            this.openAPI = openAPI.get();
            if (this.openAPI.getComponents() == null)
                this.openAPI.setComponents(new Components());
            if (this.openAPI.getPaths() == null)
                this.openAPI.setPaths(new Paths());
            if (!CollectionUtils.isEmpty(this.openAPI.getServers()))
                this.isServersPresent = true;
        }
        this.propertyResolverUtils = propertyResolverUtils;
        this.securityParser = securityParser;
        this.springDocConfigProperties = springDocConfigProperties;
        this.openApiBuilderCustomisers = openApiBuilderCustomizers;
        this.serverBaseUrlCustomizers = serverBaseUrlCustomizers;
        this.javadocProvider = javadocProvider;
        if (springDocConfigProperties.isUseFqn())
            TypeNameResolver.std.setUseFqn(true);
    }
    @Override
    public Operation buildTags(HandlerMethod handlerMethod, Operation operation, OpenAPI openAPI, Locale locale) {
        Set<Tag> tags = new HashSet<>();
        Set<String> tagsStr = new HashSet<>();
        buildTagsFromMethod(handlerMethod.getMethod(), tags, tagsStr, locale);
        buildTagsFromClass(handlerMethod.getBeanType(), tags, tagsStr, locale);
        if (!CollectionUtils.isEmpty(tagsStr))
            tagsStr = tagsStr.stream()
                .map(str -> propertyResolverUtils.resolve(str, locale))
                .collect(Collectors.toSet());
        if (springdocTags.containsKey(handlerMethod)) {
            io.swagger.v3.oas.models.tags.Tag tag = springdocTags.get(handlerMethod);
            tagsStr.add(tag.getName());
            if (openAPI.getTags() == null || !openAPI.getTags().contains(tag)) {
                openAPI.addTagsItem(tag);
            }
        }
        if (!CollectionUtils.isEmpty(tagsStr)) {
            if (CollectionUtils.isEmpty(operation.getTags()))
                operation.setTags(new ArrayList<>(tagsStr));
            else {
                Set<String> operationTagsSet = new HashSet<>(operation.getTags());
                operationTagsSet.addAll(tagsStr);
                operation.getTags().clear();
                operation.getTags().addAll(operationTagsSet);
            }
        }
        if (isAutoTagClasses(operation)) {
            if (javadocProvider.isPresent()) {
                String description = javadocProvider.get().getClassJavadoc(handlerMethod.getBeanType());
                if (StringUtils.isNotBlank(description)) {
                    io.swagger.v3.oas.models.tags.Tag tag = new io.swagger.v3.oas.models.tags.Tag();
                    // è‡ªå®šä¹‰éƒ¨åˆ† ä¿®æ”¹ä½¿ç”¨java注释当tag名
                    List<String> list = IoUtil.readLines(new StringReader(description), new ArrayList<>());
                    // tag.setName(tagAutoName);
                    tag.setName(list.get(0));
                    operation.addTagsItem(list.get(0));
                    tag.setDescription(description);
                    if (openAPI.getTags() == null || !openAPI.getTags().contains(tag)) {
                        openAPI.addTagsItem(tag);
                    }
                }
            } else {
                String tagAutoName = splitCamelCase(handlerMethod.getBeanType().getSimpleName());
                operation.addTagsItem(tagAutoName);
            }
        }
        if (!CollectionUtils.isEmpty(tags)) {
            // Existing tags
            List<io.swagger.v3.oas.models.tags.Tag> openApiTags = openAPI.getTags();
            if (!CollectionUtils.isEmpty(openApiTags))
                tags.addAll(openApiTags);
            openAPI.setTags(new ArrayList<>(tags));
        }
        // Handle SecurityRequirement at operation level
        io.swagger.v3.oas.annotations.security.SecurityRequirement[] securityRequirements = securityParser
            .getSecurityRequirements(handlerMethod);
        if (securityRequirements != null) {
            if (securityRequirements.length == 0)
                operation.setSecurity(Collections.emptyList());
            else
                securityParser.buildSecurityRequirement(securityRequirements, operation);
        }
        return operation;
    }
    private void buildTagsFromMethod(Method method, Set<io.swagger.v3.oas.models.tags.Tag> tags, Set<String> tagsStr, Locale locale) {
        // method tags
        Set<Tags> tagsSet = AnnotatedElementUtils
            .findAllMergedAnnotations(method, Tags.class);
        Set<io.swagger.v3.oas.annotations.tags.Tag> methodTags = tagsSet.stream()
            .flatMap(x -> Stream.of(x.value())).collect(Collectors.toSet());
        methodTags.addAll(AnnotatedElementUtils.findAllMergedAnnotations(method, io.swagger.v3.oas.annotations.tags.Tag.class));
        if (!CollectionUtils.isEmpty(methodTags)) {
            tagsStr.addAll(methodTags.stream().map(tag -> propertyResolverUtils.resolve(tag.name(), locale)).collect(Collectors.toSet()));
            List<io.swagger.v3.oas.annotations.tags.Tag> allTags = new ArrayList<>(methodTags);
            addTags(allTags, tags, locale);
        }
    }
    private void addTags(List<io.swagger.v3.oas.annotations.tags.Tag> sourceTags, Set<io.swagger.v3.oas.models.tags.Tag> tags, Locale locale) {
        Optional<Set<io.swagger.v3.oas.models.tags.Tag>> optionalTagSet = AnnotationsUtils
            .getTags(sourceTags.toArray(new io.swagger.v3.oas.annotations.tags.Tag[0]), true);
        optionalTagSet.ifPresent(tagsSet -> {
            tagsSet.forEach(tag -> {
                tag.name(propertyResolverUtils.resolve(tag.getName(), locale));
                tag.description(propertyResolverUtils.resolve(tag.getDescription(), locale));
                if (tags.stream().noneMatch(t -> t.getName().equals(tag.getName())))
                    tags.add(tag);
            });
        });
    }
}
ruoyi-common/ruoyi-common-doc/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1 @@
com.ruoyi.common.doc.config.SwaggerConfig
ruoyi-common/ruoyi-common-excel/pom.xml
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,31 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <groupId>com.ruoyi</groupId>
        <artifactId>ruoyi-common</artifactId>
        <version>${revision}</version>
        <relativePath>../pom.xml</relativePath>
    </parent>
    <modelVersion>4.0.0</modelVersion>
    <artifactId>ruoyi-common-excel</artifactId>
    <description>
        ruoyi-common-excel
    </description>
    <dependencies>
        <dependency>
            <groupId>com.ruoyi</groupId>
            <artifactId>ruoyi-common-core</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>easyexcel</artifactId>
        </dependency>
    </dependencies>
</project>
ruoyi-common/ruoyi-common-excel/src/main/java/com/ruoyi/common/excel/annotation/CellMerge.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,24 @@
package com.ruoyi.common.excel.annotation;
import com.ruoyi.common.excel.core.CellMergeStrategy;
import java.lang.annotation.*;
/**
 * excel åˆ—单元格合并(合并列相同项)
 *
 * éœ€æ­é… {@link CellMergeStrategy} ç­–略使用
 *
 * @author Lion Li
 */
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface CellMerge {
    /**
     * col index
     */
    int index() default -1;
}
ruoyi-common/ruoyi-common-excel/src/main/java/com/ruoyi/common/excel/annotation/ExcelDictFormat.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,30 @@
package com.ruoyi.common.excel.annotation;
import java.lang.annotation.*;
/**
 * å­—典格式化
 *
 * @author Lion Li
 */
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface ExcelDictFormat {
    /**
     * å¦‚果是字典类型,请设置字典的type值 (如: sys_user_sex)
     */
    String dictType() default "";
    /**
     * è¯»å–内容转表达式 (如: 0=男,1=女,2=未知)
     */
    String readConverterExp() default "";
    /**
     * åˆ†éš”符,读取字符串组内容
     */
    String separator() default ",";
}
ruoyi-common/ruoyi-common-excel/src/main/java/com/ruoyi/common/excel/convert/ExcelBigNumberConvert.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,52 @@
package com.ruoyi.common.excel.convert;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.ObjectUtil;
import com.alibaba.excel.converters.Converter;
import com.alibaba.excel.enums.CellDataTypeEnum;
import com.alibaba.excel.metadata.GlobalConfiguration;
import com.alibaba.excel.metadata.data.ReadCellData;
import com.alibaba.excel.metadata.data.WriteCellData;
import com.alibaba.excel.metadata.property.ExcelContentProperty;
import lombok.extern.slf4j.Slf4j;
import java.math.BigDecimal;
/**
 * å¤§æ•°å€¼è½¬æ¢
 * Excel æ•°å€¼é•¿åº¦ä½15位 å¤§äºŽ15位的数值转换位字符串
 *
 * @author Lion Li
 */
@Slf4j
public class ExcelBigNumberConvert implements Converter<Long> {
    @Override
    public Class<Long> supportJavaTypeKey() {
        return Long.class;
    }
    @Override
    public CellDataTypeEnum supportExcelTypeKey() {
        return CellDataTypeEnum.STRING;
    }
    @Override
    public Long convertToJavaData(ReadCellData<?> cellData, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) {
        return Convert.toLong(cellData.getData());
    }
    @Override
    public WriteCellData<Object> convertToExcelData(Long object, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) {
        if (ObjectUtil.isNotNull(object)) {
            String str = Convert.toStr(object);
            if (str.length() > 15) {
                return new WriteCellData<>(str);
            }
        }
        WriteCellData<Object> cellData = new WriteCellData<>(new BigDecimal(object));
        cellData.setType(CellDataTypeEnum.NUMBER);
        return cellData;
    }
}
ruoyi-common/ruoyi-common-excel/src/main/java/com/ruoyi/common/excel/convert/ExcelDictConvert.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,73 @@
package com.ruoyi.common.excel.convert;
import cn.hutool.core.annotation.AnnotationUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.ObjectUtil;
import com.alibaba.excel.converters.Converter;
import com.alibaba.excel.enums.CellDataTypeEnum;
import com.alibaba.excel.metadata.GlobalConfiguration;
import com.alibaba.excel.metadata.data.ReadCellData;
import com.alibaba.excel.metadata.data.WriteCellData;
import com.alibaba.excel.metadata.property.ExcelContentProperty;
import com.ruoyi.common.excel.annotation.ExcelDictFormat;
import com.ruoyi.common.core.service.DictService;
import com.ruoyi.common.core.utils.SpringUtils;
import com.ruoyi.common.core.utils.StringUtils;
import com.ruoyi.common.excel.utils.ExcelUtil;
import lombok.extern.slf4j.Slf4j;
import java.lang.reflect.Field;
/**
 * å­—典格式化转换处理
 *
 * @author Lion Li
 */
@Slf4j
public class ExcelDictConvert implements Converter<Object> {
    @Override
    public Class<Object> supportJavaTypeKey() {
        return Object.class;
    }
    @Override
    public CellDataTypeEnum supportExcelTypeKey() {
        return null;
    }
    @Override
    public Object convertToJavaData(ReadCellData<?> cellData, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) {
        ExcelDictFormat anno = getAnnotation(contentProperty.getField());
        String type = anno.dictType();
        String label = cellData.getStringValue();
        String value;
        if (StringUtils.isBlank(type)) {
            value = ExcelUtil.reverseByExp(label, anno.readConverterExp(), anno.separator());
        } else {
            value = SpringUtils.getBean(DictService.class).getDictValue(type, label, anno.separator());
        }
        return Convert.convert(contentProperty.getField().getType(), value);
    }
    @Override
    public WriteCellData<String> convertToExcelData(Object object, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) {
        if (ObjectUtil.isNull(object)) {
            return new WriteCellData<>("");
        }
        ExcelDictFormat anno = getAnnotation(contentProperty.getField());
        String type = anno.dictType();
        String value = Convert.toStr(object);
        String label;
        if (StringUtils.isBlank(type)) {
            label = ExcelUtil.convertByExp(value, anno.readConverterExp(), anno.separator());
        } else {
            label = SpringUtils.getBean(DictService.class).getDictLabel(type, value, anno.separator());
        }
        return new WriteCellData<>(label);
    }
    private ExcelDictFormat getAnnotation(Field field) {
        return AnnotationUtil.getAnnotation(field, ExcelDictFormat.class);
    }
}
ruoyi-common/ruoyi-common-excel/src/main/java/com/ruoyi/common/excel/core/CellMergeStrategy.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,114 @@
package com.ruoyi.common.excel.core;
import com.alibaba.excel.metadata.Head;
import com.alibaba.excel.write.merge.AbstractMergeStrategy;
import com.ruoyi.common.excel.annotation.CellMerge;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.util.CellRangeAddress;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
 * åˆ—值重复合并策略
 *
 * @author Lion Li
 */
@AllArgsConstructor
@Slf4j
public class CellMergeStrategy extends AbstractMergeStrategy {
    private List<?> list;
    private boolean hasTitle;
    @Override
    protected void merge(Sheet sheet, Cell cell, Head head, Integer relativeRowIndex) {
        List<CellRangeAddress> cellList = handle(list, hasTitle);
        // judge the list is not null
        if (CollectionUtils.isNotEmpty(cellList)) {
            // the judge is necessary
            if (cell.getRowIndex() == 1 && cell.getColumnIndex() == 0) {
                for (CellRangeAddress item : cellList) {
                    sheet.addMergedRegion(item);
                }
            }
        }
    }
    @SneakyThrows
    private static List<CellRangeAddress> handle(List<?> list, boolean hasTitle) {
        List<CellRangeAddress> cellList = new ArrayList<>();
        if (CollectionUtils.isEmpty(list)) {
            return cellList;
        }
        Class<?> clazz = list.get(0).getClass();
        Field[] fields = clazz.getDeclaredFields();
        // æœ‰æ³¨è§£çš„字段
        List<Field> mergeFields = new ArrayList<>();
        List<Integer> mergeFieldsIndex = new ArrayList<>();
        for (int i = 0; i < fields.length; i++) {
            Field field = fields[i];
            if (field.isAnnotationPresent(CellMerge.class)) {
                CellMerge cm = field.getAnnotation(CellMerge.class);
                mergeFields.add(field);
                mergeFieldsIndex.add(cm.index() == -1 ? i : cm.index());
            }
        }
        // è¡Œåˆå¹¶å¼€å§‹ä¸‹æ ‡
        int rowIndex = hasTitle ? 1 : 0;
        Map<Field, RepeatCell> map = new HashMap<>();
        // ç”Ÿæˆä¸¤ä¸¤åˆå¹¶å•元格
        for (int i = 0; i < list.size(); i++) {
            for (int j = 0; j < mergeFields.size(); j++) {
                Field field = mergeFields.get(j);
                String name = field.getName();
                String methodName = "get" + name.substring(0, 1).toUpperCase() + name.substring(1);
                Method readMethod = clazz.getMethod(methodName);
                Object val = readMethod.invoke(list.get(i));
                int colNum = mergeFieldsIndex.get(j);
                if (!map.containsKey(field)) {
                    map.put(field, new RepeatCell(val, i));
                } else {
                    RepeatCell repeatCell = map.get(field);
                    Object cellValue = repeatCell.getValue();
                    if (cellValue == null || "".equals(cellValue)) {
                        // ç©ºå€¼è·³è¿‡ä¸åˆå¹¶
                        continue;
                    }
                    if (!cellValue.equals(val)) {
                        if (i - repeatCell.getCurrent() > 1) {
                            cellList.add(new CellRangeAddress(repeatCell.getCurrent() + rowIndex, i + rowIndex - 1, colNum, colNum));
                        }
                        map.put(field, new RepeatCell(val, i));
                    } else if (i == list.size() - 1) {
                        if (i > repeatCell.getCurrent()) {
                            cellList.add(new CellRangeAddress(repeatCell.getCurrent() + rowIndex, i + rowIndex, colNum, colNum));
                        }
                    }
                }
            }
        }
        return cellList;
    }
    @Data
    @AllArgsConstructor
    static class RepeatCell {
        private Object value;
        private int current;
    }
}
ruoyi-common/ruoyi-common-excel/src/main/java/com/ruoyi/common/excel/core/DefaultExcelListener.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,106 @@
package com.ruoyi.common.excel.core;
import cn.hutool.core.util.StrUtil;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.alibaba.excel.exception.ExcelAnalysisException;
import com.alibaba.excel.exception.ExcelDataConvertException;
import com.ruoyi.common.core.utils.JsonUtils;
import com.ruoyi.common.core.utils.StreamUtils;
import com.ruoyi.common.core.utils.ValidatorUtils;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import jakarta.validation.ConstraintViolation;
import jakarta.validation.ConstraintViolationException;
import java.util.Map;
import java.util.Set;
/**
 * Excel å¯¼å…¥ç›‘听
 *
 * @author Yjoioooo
 * @author Lion Li
 */
@Slf4j
@NoArgsConstructor
public class DefaultExcelListener<T> extends AnalysisEventListener<T> implements ExcelListener<T> {
    /**
     * æ˜¯å¦Validator检验,默认为是
     */
    private Boolean isValidate = Boolean.TRUE;
    /**
     * excel è¡¨å¤´æ•°æ®
     */
    private Map<Integer, String> headMap;
    /**
     * å¯¼å…¥å›žæ‰§
     */
    private ExcelResult<T> excelResult;
    public DefaultExcelListener(boolean isValidate) {
        this.excelResult = new DefautExcelResult<>();
        this.isValidate = isValidate;
    }
    /**
     * å¤„理异常
     *
     * @param exception ExcelDataConvertException
     * @param context   Excel ä¸Šä¸‹æ–‡
     */
    @Override
    public void onException(Exception exception, AnalysisContext context) throws Exception {
        String errMsg = null;
        if (exception instanceof ExcelDataConvertException) {
            // å¦‚果是某一个单元格的转换异常 èƒ½èŽ·å–åˆ°å…·ä½“è¡Œå·
            ExcelDataConvertException excelDataConvertException = (ExcelDataConvertException) exception;
            Integer rowIndex = excelDataConvertException.getRowIndex();
            Integer columnIndex = excelDataConvertException.getColumnIndex();
            errMsg = StrUtil.format("第{}行-第{}列-表头{}: è§£æžå¼‚常<br/>",
                rowIndex + 1, columnIndex + 1, headMap.get(columnIndex));
            if (log.isDebugEnabled()) {
                log.error(errMsg);
            }
        }
        if (exception instanceof ConstraintViolationException) {
            ConstraintViolationException constraintViolationException = (ConstraintViolationException) exception;
            Set<ConstraintViolation<?>> constraintViolations = constraintViolationException.getConstraintViolations();
            String constraintViolationsMsg = StreamUtils.join(constraintViolations, ConstraintViolation::getMessage, ", ");
            errMsg = StrUtil.format("第{}行数据校验异常: {}", context.readRowHolder().getRowIndex() + 1, constraintViolationsMsg);
            if (log.isDebugEnabled()) {
                log.error(errMsg);
            }
        }
        excelResult.getErrorList().add(errMsg);
        throw new ExcelAnalysisException(errMsg);
    }
    @Override
    public void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) {
        this.headMap = headMap;
        log.debug("解析到一条表头数据: {}", JsonUtils.toJsonString(headMap));
    }
    @Override
    public void invoke(T data, AnalysisContext context) {
        if (isValidate) {
            ValidatorUtils.validate(data);
        }
        excelResult.getList().add(data);
    }
    @Override
    public void doAfterAllAnalysed(AnalysisContext context) {
        log.debug("所有数据解析完成!");
    }
    @Override
    public ExcelResult<T> getExcelResult() {
        return excelResult;
    }
}
ruoyi-common/ruoyi-common-excel/src/main/java/com/ruoyi/common/excel/core/DefautExcelResult.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,73 @@
package com.ruoyi.common.excel.core;
import cn.hutool.core.util.StrUtil;
import lombok.Setter;
import java.util.ArrayList;
import java.util.List;
/**
 * é»˜è®¤excel返回对象
 *
 * @author Yjoioooo
 * @author Lion Li
 */
public class DefautExcelResult<T> implements ExcelResult<T> {
    /**
     * æ•°æ®å¯¹è±¡list
     */
    @Setter
    private List<T> list;
    /**
     * é”™è¯¯ä¿¡æ¯åˆ—表
     */
    @Setter
    private List<String> errorList;
    public DefautExcelResult() {
        this.list = new ArrayList<>();
        this.errorList = new ArrayList<>();
    }
    public DefautExcelResult(List<T> list, List<String> errorList) {
        this.list = list;
        this.errorList = errorList;
    }
    public DefautExcelResult(ExcelResult<T> excelResult) {
        this.list = excelResult.getList();
        this.errorList = excelResult.getErrorList();
    }
    @Override
    public List<T> getList() {
        return list;
    }
    @Override
    public List<String> getErrorList() {
        return errorList;
    }
    /**
     * èŽ·å–å¯¼å…¥å›žæ‰§
     *
     * @return å¯¼å…¥å›žæ‰§
     */
    @Override
    public String getAnalysis() {
        int successCount = list.size();
        int errorCount = errorList.size();
        if (successCount == 0) {
            return "读取失败,未解析到数据";
        } else {
            if (errorCount == 0) {
                return StrUtil.format("恭喜您,全部读取成功!共{}条", successCount);
            } else {
                return "";
            }
        }
    }
}
ruoyi-common/ruoyi-common-excel/src/main/java/com/ruoyi/common/excel/core/ExcelListener.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,14 @@
package com.ruoyi.common.excel.core;
import com.alibaba.excel.read.listener.ReadListener;
/**
 * Excel å¯¼å…¥ç›‘听
 *
 * @author Lion Li
 */
public interface ExcelListener<T> extends ReadListener<T> {
    ExcelResult<T> getExcelResult();
}
ruoyi-common/ruoyi-common-excel/src/main/java/com/ruoyi/common/excel/core/ExcelResult.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,26 @@
package com.ruoyi.common.excel.core;
import java.util.List;
/**
 * excel返回对象
 *
 * @author Lion Li
 */
public interface ExcelResult<T> {
    /**
     * å¯¹è±¡åˆ—表
     */
    List<T> getList();
    /**
     * é”™è¯¯åˆ—表
     */
    List<String> getErrorList();
    /**
     * å¯¼å…¥å›žæ‰§
     */
    String getAnalysis();
}
ruoyi-common/ruoyi-common-excel/src/main/java/com/ruoyi/common/excel/utils/ExcelUtil.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,328 @@
package com.ruoyi.common.excel.utils;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.io.resource.ClassPathResource;
import cn.hutool.core.util.IdUtil;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.write.builder.ExcelWriterSheetBuilder;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.alibaba.excel.write.metadata.fill.FillConfig;
import com.alibaba.excel.write.metadata.fill.FillWrapper;
import com.alibaba.excel.write.style.column.LongestMatchColumnWidthStyleStrategy;
import com.ruoyi.common.core.utils.StringUtils;
import com.ruoyi.common.core.utils.file.FileUtils;
import com.ruoyi.common.excel.convert.ExcelBigNumberConvert;
import com.ruoyi.common.excel.core.CellMergeStrategy;
import com.ruoyi.common.excel.core.DefaultExcelListener;
import com.ruoyi.common.excel.core.ExcelListener;
import com.ruoyi.common.excel.core.ExcelResult;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import jakarta.servlet.ServletOutputStream;
import jakarta.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;
import java.util.Map;
/**
 * Excel相关处理
 *
 * @author Lion Li
 */
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class ExcelUtil {
    /**
     * åŒæ­¥å¯¼å…¥(适用于小数据量)
     *
     * @param is è¾“入流
     * @return è½¬æ¢åŽé›†åˆ
     */
    public static <T> List<T> importExcel(InputStream is, Class<T> clazz) {
        return EasyExcel.read(is).head(clazz).autoCloseStream(false).sheet().doReadSync();
    }
    /**
     * ä½¿ç”¨æ ¡éªŒç›‘听器 å¼‚步导入 åŒæ­¥è¿”回
     *
     * @param is         è¾“入流
     * @param clazz      å¯¹è±¡ç±»åž‹
     * @param isValidate æ˜¯å¦ Validator æ£€éªŒ é»˜è®¤ä¸ºæ˜¯
     * @return è½¬æ¢åŽé›†åˆ
     */
    public static <T> ExcelResult<T> importExcel(InputStream is, Class<T> clazz, boolean isValidate) {
        DefaultExcelListener<T> listener = new DefaultExcelListener<>(isValidate);
        EasyExcel.read(is, clazz, listener).sheet().doRead();
        return listener.getExcelResult();
    }
    /**
     * ä½¿ç”¨è‡ªå®šä¹‰ç›‘听器 å¼‚步导入 è‡ªå®šä¹‰è¿”回
     *
     * @param is       è¾“入流
     * @param clazz    å¯¹è±¡ç±»åž‹
     * @param listener è‡ªå®šä¹‰ç›‘听器
     * @return è½¬æ¢åŽé›†åˆ
     */
    public static <T> ExcelResult<T> importExcel(InputStream is, Class<T> clazz, ExcelListener<T> listener) {
        EasyExcel.read(is, clazz, listener).sheet().doRead();
        return listener.getExcelResult();
    }
    /**
     * å¯¼å‡ºexcel
     *
     * @param list      å¯¼å‡ºæ•°æ®é›†åˆ
     * @param sheetName å·¥ä½œè¡¨çš„名称
     * @param clazz     å®žä½“ç±»
     * @param response  å“åº”体
     */
    public static <T> void exportExcel(List<T> list, String sheetName, Class<T> clazz, HttpServletResponse response) {
        try {
            resetResponse(sheetName, response);
            ServletOutputStream os = response.getOutputStream();
            exportExcel(list, sheetName, clazz, false, os);
        } catch (IOException e) {
            throw new RuntimeException("导出Excel异常");
        }
    }
    /**
     * å¯¼å‡ºexcel
     *
     * @param list      å¯¼å‡ºæ•°æ®é›†åˆ
     * @param sheetName å·¥ä½œè¡¨çš„名称
     * @param clazz     å®žä½“ç±»
     * @param merge     æ˜¯å¦åˆå¹¶å•元格
     * @param response  å“åº”体
     */
    public static <T> void exportExcel(List<T> list, String sheetName, Class<T> clazz, boolean merge, HttpServletResponse response) {
        try {
            resetResponse(sheetName, response);
            ServletOutputStream os = response.getOutputStream();
            exportExcel(list, sheetName, clazz, merge, os);
        } catch (IOException e) {
            throw new RuntimeException("导出Excel异常");
        }
    }
    /**
     * å¯¼å‡ºexcel
     *
     * @param list      å¯¼å‡ºæ•°æ®é›†åˆ
     * @param sheetName å·¥ä½œè¡¨çš„名称
     * @param clazz     å®žä½“ç±»
     * @param os        è¾“出流
     */
    public static <T> void exportExcel(List<T> list, String sheetName, Class<T> clazz, OutputStream os) {
        exportExcel(list, sheetName, clazz, false, os);
    }
    /**
     * å¯¼å‡ºexcel
     *
     * @param list      å¯¼å‡ºæ•°æ®é›†åˆ
     * @param sheetName å·¥ä½œè¡¨çš„名称
     * @param clazz     å®žä½“ç±»
     * @param merge     æ˜¯å¦åˆå¹¶å•元格
     * @param os        è¾“出流
     */
    public static <T> void exportExcel(List<T> list, String sheetName, Class<T> clazz, boolean merge, OutputStream os) {
        ExcelWriterSheetBuilder builder = EasyExcel.write(os, clazz)
            .autoCloseStream(false)
            // è‡ªåŠ¨é€‚é…
            .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy())
            // å¤§æ•°å€¼è‡ªåŠ¨è½¬æ¢ é˜²æ­¢å¤±çœŸ
            .registerConverter(new ExcelBigNumberConvert())
            .sheet(sheetName);
        if (merge) {
            // åˆå¹¶å¤„理器
            builder.registerWriteHandler(new CellMergeStrategy(list, true));
        }
        builder.doWrite(list);
    }
    /**
     * å•表多数据模板导出 æ¨¡æ¿æ ¼å¼ä¸º {.属性}
     *
     * @param filename     æ–‡ä»¶å
     * @param templatePath æ¨¡æ¿è·¯å¾„ resource ç›®å½•下的路径包括模板文件名
     *                     ä¾‹å¦‚: excel/temp.xlsx
     *                     é‡ç‚¹: æ¨¡æ¿æ–‡ä»¶å¿…须放置到启动类对应的 resource ç›®å½•下
     * @param data         æ¨¡æ¿éœ€è¦çš„æ•°æ®
     * @param response     å“åº”体
     */
    public static void exportTemplate(List<Object> data, String filename, String templatePath, HttpServletResponse response) {
        try {
            resetResponse(filename, response);
            ServletOutputStream os = response.getOutputStream();
            exportTemplate(data, templatePath, os);
        } catch (IOException e) {
            throw new RuntimeException("导出Excel异常");
        }
    }
    /**
     * å•表多数据模板导出 æ¨¡æ¿æ ¼å¼ä¸º {.属性}
     *
     * @param templatePath æ¨¡æ¿è·¯å¾„ resource ç›®å½•下的路径包括模板文件名
     *                     ä¾‹å¦‚: excel/temp.xlsx
     *                     é‡ç‚¹: æ¨¡æ¿æ–‡ä»¶å¿…须放置到启动类对应的 resource ç›®å½•下
     * @param data         æ¨¡æ¿éœ€è¦çš„æ•°æ®
     * @param os           è¾“出流
     */
    public static void exportTemplate(List<Object> data, String templatePath, OutputStream os) {
        ClassPathResource templateResource = new ClassPathResource(templatePath);
        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.属性}
     *
     * @param filename     æ–‡ä»¶å
     * @param templatePath æ¨¡æ¿è·¯å¾„ resource ç›®å½•下的路径包括模板文件名
     *                     ä¾‹å¦‚: excel/temp.xlsx
     *                     é‡ç‚¹: æ¨¡æ¿æ–‡ä»¶å¿…须放置到启动类对应的 resource ç›®å½•下
     * @param data         æ¨¡æ¿éœ€è¦çš„æ•°æ®
     * @param response     å“åº”体
     */
    public static void exportTemplateMultiList(Map<String, Object> data, String filename, String templatePath, HttpServletResponse response) {
        try {
            resetResponse(filename, response);
            ServletOutputStream os = response.getOutputStream();
            exportTemplateMultiList(data, templatePath, os);
        } catch (IOException e) {
            throw new RuntimeException("导出Excel异常");
        }
    }
    /**
     * å¤šè¡¨å¤šæ•°æ®æ¨¡æ¿å¯¼å‡º æ¨¡æ¿æ ¼å¼ä¸º {key.属性}
     *
     * @param templatePath æ¨¡æ¿è·¯å¾„ resource ç›®å½•下的路径包括模板文件名
     *                     ä¾‹å¦‚: excel/temp.xlsx
     *                     é‡ç‚¹: æ¨¡æ¿æ–‡ä»¶å¿…须放置到启动类对应的 resource ç›®å½•下
     * @param data         æ¨¡æ¿éœ€è¦çš„æ•°æ®
     * @param os           è¾“出流
     */
    public static void exportTemplateMultiList(Map<String, Object> data, String templatePath, OutputStream os) {
        ClassPathResource templateResource = new ClassPathResource(templatePath);
        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<String, Object> 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();
    }
    /**
     * é‡ç½®å“åº”体
     */
    private static void resetResponse(String sheetName, HttpServletResponse response) throws UnsupportedEncodingException {
        String filename = encodingFilename(sheetName);
        FileUtils.setAttachmentResponseHeader(response, filename);
        response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=UTF-8");
    }
    /**
     * è§£æžå¯¼å‡ºå€¼ 0=男,1=女,2=未知
     *
     * @param propertyValue å‚数值
     * @param converterExp  ç¿»è¯‘注解
     * @param separator     åˆ†éš”符
     * @return è§£æžåŽå€¼
     */
    public static String convertByExp(String propertyValue, String converterExp, String separator) {
        StringBuilder propertyString = new StringBuilder();
        String[] convertSource = converterExp.split(",");
        for (String item : convertSource) {
            String[] itemArray = item.split("=");
            if (StringUtils.containsAny(propertyValue, separator)) {
                for (String value : propertyValue.split(separator)) {
                    if (itemArray[0].equals(value)) {
                        propertyString.append(itemArray[1] + separator);
                        break;
                    }
                }
            } else {
                if (itemArray[0].equals(propertyValue)) {
                    return itemArray[1];
                }
            }
        }
        return StringUtils.stripEnd(propertyString.toString(), separator);
    }
    /**
     * åå‘解析值 ç”·=0,女=1,未知=2
     *
     * @param propertyValue å‚数值
     * @param converterExp  ç¿»è¯‘注解
     * @param separator     åˆ†éš”符
     * @return è§£æžåŽå€¼
     */
    public static String reverseByExp(String propertyValue, String converterExp, String separator) {
        StringBuilder propertyString = new StringBuilder();
        String[] convertSource = converterExp.split(",");
        for (String item : convertSource) {
            String[] itemArray = item.split("=");
            if (StringUtils.containsAny(propertyValue, separator)) {
                for (String value : propertyValue.split(separator)) {
                    if (itemArray[1].equals(value)) {
                        propertyString.append(itemArray[0] + separator);
                        break;
                    }
                }
            } else {
                if (itemArray[1].equals(propertyValue)) {
                    return itemArray[0];
                }
            }
        }
        return StringUtils.stripEnd(propertyString.toString(), separator);
    }
    /**
     * ç¼–码文件名
     */
    public static String encodingFilename(String filename) {
        return IdUtil.fastSimpleUUID() + "_" + filename + ".xlsx";
    }
}
ruoyi-common/ruoyi-common-idempotent/pom.xml
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,42 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <groupId>com.ruoyi</groupId>
        <artifactId>ruoyi-common</artifactId>
        <version>${revision}</version>
        <relativePath>../pom.xml</relativePath>
    </parent>
    <modelVersion>4.0.0</modelVersion>
    <artifactId>ruoyi-common-idempotent</artifactId>
    <description>
        ruoyi-common-idempotent å¹‚等功能
    </description>
    <dependencies>
        <dependency>
            <groupId>com.ruoyi</groupId>
            <artifactId>ruoyi-common-core</artifactId>
        </dependency>
        <dependency>
            <groupId>com.ruoyi</groupId>
            <artifactId>ruoyi-common-redis</artifactId>
        </dependency>
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-crypto</artifactId>
        </dependency>
        <dependency>
            <groupId>cn.dev33</groupId>
            <artifactId>sa-token-core</artifactId>
        </dependency>
    </dependencies>
</project>
ruoyi-common/ruoyi-common-idempotent/src/main/java/com/ruoyi/common/idempotent/annotation/RepeatSubmit.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,29 @@
package com.ruoyi.common.idempotent.annotation;
import java.lang.annotation.*;
import java.util.concurrent.TimeUnit;
/**
 * è‡ªå®šä¹‰æ³¨è§£é˜²æ­¢è¡¨å•重复提交
 *
 * @author Lion Li
 */
@Inherited
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RepeatSubmit {
    /**
     * é—´é𔿗¶é—´(ms),小于此时间视为重复提交
     */
    int interval() default 5000;
    TimeUnit timeUnit() default TimeUnit.MILLISECONDS;
    /**
     * æç¤ºæ¶ˆæ¯ æ”¯æŒå›½é™…化 æ ¼å¼ä¸º {code}
     */
    String message() default "{repeat.submit.message}";
}
ruoyi-common/ruoyi-common-idempotent/src/main/java/com/ruoyi/common/idempotent/aspectj/RepeatSubmitAspect.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,153 @@
package com.ruoyi.common.idempotent.aspectj;
import cn.dev33.satoken.SaManager;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.crypto.SecureUtil;
import com.ruoyi.common.core.constant.CacheConstants;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.core.exception.ServiceException;
import com.ruoyi.common.core.utils.JsonUtils;
import com.ruoyi.common.core.utils.MessageUtils;
import com.ruoyi.common.core.utils.ServletUtils;
import com.ruoyi.common.core.utils.StringUtils;
import com.ruoyi.common.idempotent.annotation.RepeatSubmit;
import com.ruoyi.common.redis.utils.RedisUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.validation.BindingResult;
import org.springframework.web.multipart.MultipartFile;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.time.Duration;
import java.util.Collection;
import java.util.Map;
/**
 * é˜²æ­¢é‡å¤æäº¤(参考美团GTIS防重系统)
 *
 * @author Lion Li
 */
@Aspect
public class RepeatSubmitAspect {
    private static final ThreadLocal<String> KEY_CACHE = new ThreadLocal<>();
    @Before("@annotation(repeatSubmit)")
    public void doBefore(JoinPoint point, RepeatSubmit repeatSubmit) throws Throwable {
        // å¦‚果注解不为0 åˆ™ä½¿ç”¨æ³¨è§£æ•°å€¼
        long interval = 0;
        if (repeatSubmit.interval() > 0) {
            interval = repeatSubmit.timeUnit().toMillis(repeatSubmit.interval());
        }
        if (interval < 1000) {
            throw new ServiceException("重复提交间隔时间不能小于'1'秒");
        }
        HttpServletRequest request = ServletUtils.getRequest();
        String nowParams = argsArrayToString(point.getArgs());
        // è¯·æ±‚地址(作为存放cache的key值)
        String url = request.getRequestURI();
        // å”¯ä¸€å€¼ï¼ˆæ²¡æœ‰æ¶ˆæ¯å¤´åˆ™ä½¿ç”¨è¯·æ±‚地址)
        String submitKey = StringUtils.trimToEmpty(request.getHeader(SaManager.getConfig().getTokenName()));
        submitKey = SecureUtil.md5(submitKey + ":" + nowParams);
        // å”¯ä¸€æ ‡è¯†ï¼ˆæŒ‡å®škey + url + æ¶ˆæ¯å¤´ï¼‰
        String cacheRepeatKey = CacheConstants.REPEAT_SUBMIT_KEY + url + submitKey;
        String key = RedisUtils.getCacheObject(cacheRepeatKey);
        if (key == null) {
            RedisUtils.setCacheObject(cacheRepeatKey, "", Duration.ofMillis(interval));
            KEY_CACHE.set(cacheRepeatKey);
        } else {
            String message = repeatSubmit.message();
            if (StringUtils.startsWith(message, "{") && StringUtils.endsWith(message, "}")) {
                message = MessageUtils.message(StringUtils.substring(message, 1, message.length() - 1));
            }
            throw new ServiceException(message);
        }
    }
    /**
     * å¤„理完请求后执行
     *
     * @param joinPoint åˆ‡ç‚¹
     */
    @AfterReturning(pointcut = "@annotation(repeatSubmit)", returning = "jsonResult")
    public void doAfterReturning(JoinPoint joinPoint, RepeatSubmit repeatSubmit, Object jsonResult) {
        if (jsonResult instanceof R) {
            try {
                R<?> r = (R<?>) jsonResult;
                // æˆåŠŸåˆ™ä¸åˆ é™¤redis数据 ä¿è¯åœ¨æœ‰æ•ˆæ—¶é—´å†…无法重复提交
                if (r.getCode() == R.SUCCESS) {
                    return;
                }
                RedisUtils.deleteObject(KEY_CACHE.get());
            } finally {
                KEY_CACHE.remove();
            }
        }
    }
    /**
     * æ‹¦æˆªå¼‚常操作
     *
     * @param joinPoint åˆ‡ç‚¹
     * @param e         å¼‚常
     */
    @AfterThrowing(value = "@annotation(repeatSubmit)", throwing = "e")
    public void doAfterThrowing(JoinPoint joinPoint, RepeatSubmit repeatSubmit, Exception e) {
        RedisUtils.deleteObject(KEY_CACHE.get());
        KEY_CACHE.remove();
    }
    /**
     * å‚数拼装
     */
    private String argsArrayToString(Object[] paramsArray) {
        StringBuilder params = new StringBuilder();
        if (paramsArray != null && paramsArray.length > 0) {
            for (Object o : paramsArray) {
                if (ObjectUtil.isNotNull(o) && !isFilterObject(o)) {
                    try {
                        params.append(JsonUtils.toJsonString(o)).append(" ");
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }
        }
        return params.toString().trim();
    }
    /**
     * åˆ¤æ–­æ˜¯å¦éœ€è¦è¿‡æ»¤çš„对象。
     *
     * @param o å¯¹è±¡ä¿¡æ¯ã€‚
     * @return å¦‚果是需要过滤的对象,则返回true;否则返回false。
     */
    @SuppressWarnings("rawtypes")
    public boolean isFilterObject(final Object o) {
        Class<?> clazz = o.getClass();
        if (clazz.isArray()) {
            return clazz.getComponentType().isAssignableFrom(MultipartFile.class);
        } else if (Collection.class.isAssignableFrom(clazz)) {
            Collection collection = (Collection) o;
            for (Object value : collection) {
                return value instanceof MultipartFile;
            }
        } else if (Map.class.isAssignableFrom(clazz)) {
            Map map = (Map) o;
            for (Object value : map.entrySet()) {
                Map.Entry entry = (Map.Entry) value;
                return entry.getValue() instanceof MultipartFile;
            }
        }
        return o instanceof MultipartFile || o instanceof HttpServletRequest || o instanceof HttpServletResponse
            || o instanceof BindingResult;
    }
}
ruoyi-common/ruoyi-common-idempotent/src/main/java/com/ruoyi/common/idempotent/config/IdempotentConfig.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,21 @@
package com.ruoyi.common.idempotent.config;
import com.ruoyi.common.idempotent.aspectj.RepeatSubmitAspect;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.data.redis.connection.RedisConfiguration;
/**
 * å¹‚等功能配置
 *
 * @author Lion Li
 */
@AutoConfiguration(after = RedisConfiguration.class)
public class IdempotentConfig {
    @Bean
    public RepeatSubmitAspect repeatSubmitAspect() {
        return new RepeatSubmitAspect();
    }
}
ruoyi-common/ruoyi-common-idempotent/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1 @@
com.ruoyi.common.idempotent.config.IdempotentConfig
ruoyi-common/ruoyi-common-log/pom.xml
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <groupId>com.ruoyi</groupId>
        <artifactId>ruoyi-common</artifactId>
        <version>${revision}</version>
        <relativePath>../pom.xml</relativePath>
    </parent>
    <modelVersion>4.0.0</modelVersion>
    <artifactId>ruoyi-common-log</artifactId>
    <description>
        ruoyi-common-log æ—¥å¿—记录
    </description>
    <dependencies>
        <dependency>
            <groupId>com.ruoyi</groupId>
            <artifactId>ruoyi-common-satoken</artifactId>
        </dependency>
    </dependencies>
</project>
ruoyi-common/ruoyi-common-log/src/main/java/com/ruoyi/common/log/annotation/Log.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,41 @@
package com.ruoyi.common.log.annotation;
import com.ruoyi.common.log.enums.BusinessType;
import com.ruoyi.common.log.enums.OperatorType;
import java.lang.annotation.*;
/**
 * è‡ªå®šä¹‰æ“ä½œæ—¥å¿—记录注解
 *
 * @author ruoyi
 */
@Target({ElementType.PARAMETER, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Log {
    /**
     * æ¨¡å—
     */
    String title() default "";
    /**
     * åŠŸèƒ½
     */
    BusinessType businessType() default BusinessType.OTHER;
    /**
     * æ“ä½œäººç±»åˆ«
     */
    OperatorType operatorType() default OperatorType.MANAGE;
    /**
     * æ˜¯å¦ä¿å­˜è¯·æ±‚的参数
     */
    boolean isSaveRequestData() default true;
    /**
     * æ˜¯å¦ä¿å­˜å“åº”的参数
     */
    boolean isSaveResponseData() default true;
}
ruoyi-common/ruoyi-common-log/src/main/java/com/ruoyi/common/log/aspect/LogAspect.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,192 @@
package com.ruoyi.common.log.aspect;
import cn.hutool.core.lang.Dict;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ObjectUtil;
import com.ruoyi.common.core.utils.JsonUtils;
import com.ruoyi.common.core.utils.ServletUtils;
import com.ruoyi.common.core.utils.SpringUtils;
import com.ruoyi.common.core.utils.StringUtils;
import com.ruoyi.common.log.annotation.Log;
import com.ruoyi.common.log.enums.BusinessStatus;
import com.ruoyi.common.log.event.OperLogEvent;
import com.ruoyi.common.satoken.utils.LoginHelper;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.http.HttpMethod;
import org.springframework.validation.BindingResult;
import org.springframework.web.multipart.MultipartFile;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.util.Collection;
import java.util.Map;
/**
 * æ“ä½œæ—¥å¿—记录处理
 *
 * @author Lion Li
 */
@Slf4j
@Aspect
@AutoConfiguration
public class LogAspect {
    /**
     * æŽ’除敏感属性字段
     */
    public static final String[] EXCLUDE_PROPERTIES = { "password", "oldPassword", "newPassword", "confirmPassword" };
    /**
     * å¤„理完请求后执行
     *
     * @param joinPoint åˆ‡ç‚¹
     */
    @AfterReturning(pointcut = "@annotation(controllerLog)", returning = "jsonResult")
    public void doAfterReturning(JoinPoint joinPoint, Log controllerLog, Object jsonResult) {
        handleLog(joinPoint, controllerLog, null, jsonResult);
    }
    /**
     * æ‹¦æˆªå¼‚常操作
     *
     * @param joinPoint åˆ‡ç‚¹
     * @param e         å¼‚常
     */
    @AfterThrowing(value = "@annotation(controllerLog)", throwing = "e")
    public void doAfterThrowing(JoinPoint joinPoint, Log controllerLog, Exception e) {
        handleLog(joinPoint, controllerLog, e, null);
    }
    protected void handleLog(final JoinPoint joinPoint, Log controllerLog, final Exception e, Object jsonResult) {
        try {
            // *========数据库日志=========*//
            OperLogEvent operLog = new OperLogEvent();
            operLog.setStatus(BusinessStatus.SUCCESS.ordinal());
            // è¯·æ±‚的地址
            String ip = ServletUtils.getClientIP();
            operLog.setOperIp(ip);
            operLog.setOperUrl(StringUtils.substring(ServletUtils.getRequest().getRequestURI(), 0, 255));
            operLog.setOperName(LoginHelper.getUsername());
            if (e != null) {
                operLog.setStatus(BusinessStatus.FAIL.ordinal());
                operLog.setErrorMsg(StringUtils.substring(e.getMessage(), 0, 2000));
            }
            // è®¾ç½®æ–¹æ³•名称
            String className = joinPoint.getTarget().getClass().getName();
            String methodName = joinPoint.getSignature().getName();
            operLog.setMethod(className + "." + methodName + "()");
            // è®¾ç½®è¯·æ±‚方式
            operLog.setRequestMethod(ServletUtils.getRequest().getMethod());
            // å¤„理设置注解上的参数
            getControllerMethodDescription(joinPoint, controllerLog, operLog, jsonResult);
            // å‘布事件保存数据库
            SpringUtils.context().publishEvent(operLog);
        } catch (Exception exp) {
            // è®°å½•本地异常日志
            log.error("异常信息:{}", exp.getMessage());
            exp.printStackTrace();
        }
    }
    /**
     * èŽ·å–æ³¨è§£ä¸­å¯¹æ–¹æ³•çš„æè¿°ä¿¡æ¯ ç”¨äºŽController层注解
     *
     * @param log     æ—¥å¿—
     * @param operLog æ“ä½œæ—¥å¿—
     * @throws Exception
     */
    public void getControllerMethodDescription(JoinPoint joinPoint, Log log, OperLogEvent operLog, Object jsonResult) throws Exception {
        // è®¾ç½®action动作
        operLog.setBusinessType(log.businessType().ordinal());
        // è®¾ç½®æ ‡é¢˜
        operLog.setTitle(log.title());
        // è®¾ç½®æ“ä½œäººç±»åˆ«
        operLog.setOperatorType(log.operatorType().ordinal());
        // æ˜¯å¦éœ€è¦ä¿å­˜request,参数和值
        if (log.isSaveRequestData()) {
            // èŽ·å–å‚æ•°çš„ä¿¡æ¯ï¼Œä¼ å…¥åˆ°æ•°æ®åº“ä¸­ã€‚
            setRequestValue(joinPoint, operLog);
        }
        // æ˜¯å¦éœ€è¦ä¿å­˜response,参数和值
        if (log.isSaveResponseData() && ObjectUtil.isNotNull(jsonResult)) {
            operLog.setJsonResult(StringUtils.substring(JsonUtils.toJsonString(jsonResult), 0, 2000));
        }
    }
    /**
     * èŽ·å–è¯·æ±‚çš„å‚æ•°ï¼Œæ”¾åˆ°log中
     *
     * @param 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());
            operLog.setOperParam(StringUtils.substring(params, 0, 2000));
        } else {
            Map<String, String> paramsMap = ServletUtils.getParamMap(ServletUtils.getRequest());
            MapUtil.removeAny(paramsMap, EXCLUDE_PROPERTIES);
            operLog.setOperParam(StringUtils.substring(JsonUtils.toJsonString(paramsMap), 0, 2000));
        }
    }
    /**
     * å‚数拼装
     */
    private String argsArrayToString(Object[] paramsArray) {
        StringBuilder params = new StringBuilder();
        if (paramsArray != null && paramsArray.length > 0) {
            for (Object o : paramsArray) {
                if (ObjectUtil.isNotNull(o) && !isFilterObject(o)) {
                    try {
                        String str = JsonUtils.toJsonString(o);
                        Dict dict = JsonUtils.parseMap(str);
                        if (MapUtil.isNotEmpty(dict)) {
                            MapUtil.removeAny(dict, EXCLUDE_PROPERTIES);
                            str = JsonUtils.toJsonString(dict);
                        }
                        params.append(str).append(" ");
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }
        }
        return params.toString().trim();
    }
    /**
     * åˆ¤æ–­æ˜¯å¦éœ€è¦è¿‡æ»¤çš„对象。
     *
     * @param o å¯¹è±¡ä¿¡æ¯ã€‚
     * @return å¦‚果是需要过滤的对象,则返回true;否则返回false。
     */
    @SuppressWarnings("rawtypes")
    public boolean isFilterObject(final Object o) {
        Class<?> clazz = o.getClass();
        if (clazz.isArray()) {
            return clazz.getComponentType().isAssignableFrom(MultipartFile.class);
        } else if (Collection.class.isAssignableFrom(clazz)) {
            Collection collection = (Collection) o;
            for (Object value : collection) {
                return value instanceof MultipartFile;
            }
        } else if (Map.class.isAssignableFrom(clazz)) {
            Map map = (Map) o;
            for (Object value : map.entrySet()) {
                Map.Entry entry = (Map.Entry) value;
                return entry.getValue() instanceof MultipartFile;
            }
        }
        return o instanceof MultipartFile || o instanceof HttpServletRequest || o instanceof HttpServletResponse
            || o instanceof BindingResult;
    }
}
ruoyi-common/ruoyi-common-log/src/main/java/com/ruoyi/common/log/enums/BusinessStatus.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,18 @@
package com.ruoyi.common.log.enums;
/**
 * æ“ä½œçŠ¶æ€
 *
 * @author ruoyi
 */
public enum BusinessStatus {
    /**
     * æˆåŠŸ
     */
    SUCCESS,
    /**
     * å¤±è´¥
     */
    FAIL,
}
ruoyi-common/ruoyi-common-log/src/main/java/com/ruoyi/common/log/enums/BusinessType.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,58 @@
package com.ruoyi.common.log.enums;
/**
 * ä¸šåŠ¡æ“ä½œç±»åž‹
 *
 * @author ruoyi
 */
public enum BusinessType {
    /**
     * å…¶å®ƒ
     */
    OTHER,
    /**
     * æ–°å¢ž
     */
    INSERT,
    /**
     * ä¿®æ”¹
     */
    UPDATE,
    /**
     * åˆ é™¤
     */
    DELETE,
    /**
     * æŽˆæƒ
     */
    GRANT,
    /**
     * å¯¼å‡º
     */
    EXPORT,
    /**
     * å¯¼å…¥
     */
    IMPORT,
    /**
     * å¼ºé€€
     */
    FORCE,
    /**
     * ç”Ÿæˆä»£ç 
     */
    GENCODE,
    /**
     * æ¸…空数据
     */
    CLEAN,
}
ruoyi-common/ruoyi-common-log/src/main/java/com/ruoyi/common/log/enums/OperatorType.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,23 @@
package com.ruoyi.common.log.enums;
/**
 * æ“ä½œäººç±»åˆ«
 *
 * @author ruoyi
 */
public enum OperatorType {
    /**
     * å…¶å®ƒ
     */
    OTHER,
    /**
     * åŽå°ç”¨æˆ·
     */
    MANAGE,
    /**
     * æ‰‹æœºç«¯ç”¨æˆ·
     */
    MOBILE
}
ruoyi-common/ruoyi-common-log/src/main/java/com/ruoyi/common/log/event/LogininforEvent.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,47 @@
package com.ruoyi.common.log.event;
import lombok.Data;
import jakarta.servlet.http.HttpServletRequest;
import java.io.Serial;
import java.io.Serializable;
/**
 * ç™»å½•事件
 *
 * @author Lion Li
 */
@Data
public class LogininforEvent implements Serializable {
    @Serial
    private static final long serialVersionUID = 1L;
    /**
     * ç”¨æˆ·è´¦å·
     */
    private String username;
    /**
     * ç™»å½•状态 0成功 1失败
     */
    private String status;
    /**
     * æç¤ºæ¶ˆæ¯
     */
    private String message;
    /**
     * è¯·æ±‚体
     */
    private HttpServletRequest request;
    /**
     * å…¶ä»–参数
     */
    private Object[] args;
}
ruoyi-common/ruoyi-common-log/src/main/java/com/ruoyi/common/log/event/OperLogEvent.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,106 @@
package com.ruoyi.common.log.event;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
import java.util.Date;
/**
 * æ“ä½œæ—¥å¿—事件
 *
 * @author Lion Li
 */
@Data
public class OperLogEvent implements Serializable {
    @Serial
    private static final long serialVersionUID = 1L;
    /**
     * æ—¥å¿—主键
     */
    private Long operId;
    /**
     * æ“ä½œæ¨¡å—
     */
    private String title;
    /**
     * ä¸šåŠ¡ç±»åž‹ï¼ˆ0其它 1新增 2修改 3删除)
     */
    private Integer businessType;
    /**
     * ä¸šåŠ¡ç±»åž‹æ•°ç»„
     */
    private Integer[] businessTypes;
    /**
     * è¯·æ±‚方法
     */
    private String method;
    /**
     * è¯·æ±‚方式
     */
    private String requestMethod;
    /**
     * æ“ä½œç±»åˆ«ï¼ˆ0其它 1后台用户 2手机端用户)
     */
    private Integer operatorType;
    /**
     * æ“ä½œäººå‘˜
     */
    private String operName;
    /**
     * éƒ¨é—¨åç§°
     */
    private String deptName;
    /**
     * è¯·æ±‚url
     */
    private String operUrl;
    /**
     * æ“ä½œåœ°å€
     */
    private String operIp;
    /**
     * æ“ä½œåœ°ç‚¹
     */
    private String operLocation;
    /**
     * è¯·æ±‚参数
     */
    private String operParam;
    /**
     * è¿”回参数
     */
    private String jsonResult;
    /**
     * æ“ä½œçŠ¶æ€ï¼ˆ0正常 1异常)
     */
    private Integer status;
    /**
     * é”™è¯¯æ¶ˆæ¯
     */
    private String errorMsg;
    /**
     * æ“ä½œæ—¶é—´
     */
    private Date operTime;
}
ruoyi-common/ruoyi-common-log/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1 @@
com.ruoyi.common.log.aspect.LogAspect
ruoyi-common/ruoyi-common-mail/pom.xml
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,31 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <groupId>com.ruoyi</groupId>
        <artifactId>ruoyi-common</artifactId>
        <version>${revision}</version>
        <relativePath>../pom.xml</relativePath>
    </parent>
    <modelVersion>4.0.0</modelVersion>
    <artifactId>ruoyi-common-mail</artifactId>
    <description>
        ruoyi-common-mail é‚®ä»¶æ¨¡å—
    </description>
    <dependencies>
        <dependency>
            <groupId>com.ruoyi</groupId>
            <artifactId>ruoyi-common-core</artifactId>
        </dependency>
        <dependency>
            <groupId>jakarta.mail</groupId>
            <artifactId>jakarta.mail-api</artifactId>
        </dependency>
    </dependencies>
</project>
ruoyi-common/ruoyi-common-mail/src/main/java/com/ruoyi/common/mail/config/MailConfig.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,37 @@
package com.ruoyi.common.mail.config;
import cn.hutool.extra.mail.MailAccount;
import com.ruoyi.common.mail.config.properties.MailProperties;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
/**
 * JavaMail é…ç½®
 *
 * @author Michelle.Chung
 */
@AutoConfiguration
@ConditionalOnProperty(value = "mail.enabled", havingValue = "true")
@EnableConfigurationProperties(MailProperties.class)
public class MailConfig {
    @Bean
    public MailAccount mailAccount(MailProperties mailProperties) {
        MailAccount account = new MailAccount();
        account.setHost(mailProperties.getHost());
        account.setPort(mailProperties.getPort());
        account.setAuth(mailProperties.getAuth());
        account.setFrom(mailProperties.getFrom());
        account.setUser(mailProperties.getUser());
        account.setPass(mailProperties.getPass());
        account.setSocketFactoryPort(mailProperties.getPort());
        account.setStarttlsEnable(mailProperties.getStarttlsEnable());
        account.setSslEnable(mailProperties.getSslEnable());
        account.setTimeout(mailProperties.getTimeout());
        account.setConnectionTimeout(mailProperties.getConnectionTimeout());
        return account;
    }
}
ruoyi-common/ruoyi-common-mail/src/main/java/com/ruoyi/common/mail/config/properties/MailProperties.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,71 @@
package com.ruoyi.common.mail.config.properties;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
/**
 * JavaMail é…ç½®å±žæ€§
 *
 * @author Michelle.Chung
 */
@Data
@Component
@ConfigurationProperties(prefix = "mail")
public class MailProperties {
    /**
     * è¿‡æ»¤å¼€å…³
     */
    private Boolean enabled;
    /**
     * SMTP服务器域名
     */
    private String host;
    /**
     * SMTP服务端口
     */
    private Integer port;
    /**
     * æ˜¯å¦éœ€è¦ç”¨æˆ·åå¯†ç éªŒè¯
     */
    private Boolean auth;
    /**
     * ç”¨æˆ·å
     */
    private String user;
    /**
     * å¯†ç 
     */
    private String pass;
    /**
     * å‘送方,遵循RFC-822标准
     */
    private String from;
    /**
     * ä½¿ç”¨ STARTTLS安全连接,STARTTLS是对纯文本通信协议的扩展。它将纯文本连接升级为加密连接(TLS或SSL), è€Œä¸æ˜¯ä½¿ç”¨ä¸€ä¸ªå•独的加密通信端口。
     */
    private Boolean starttlsEnable;
    /**
     * ä½¿ç”¨ SSL安全连接
     */
    private Boolean sslEnable;
    /**
     * SMTP超时时长,单位毫秒,缺省值不超时
     */
    private Long timeout;
    /**
     * Socket连接超时值,单位毫秒,缺省值不超时
     */
    private Long connectionTimeout;
}
ruoyi-common/ruoyi-common-mail/src/main/java/com/ruoyi/common/mail/utils/MailUtils.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,468 @@
package com.ruoyi.common.mail.utils;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.CharUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.mail.*;
import com.ruoyi.common.core.utils.SpringUtils;
import com.ruoyi.common.core.utils.StringUtils;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import jakarta.mail.Authenticator;
import jakarta.mail.Session;
import java.io.File;
import java.io.InputStream;
import java.util.Collection;
import java.util.List;
import java.util.Map;
/**
 * é‚®ä»¶å·¥å…·ç±»
 */
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class MailUtils {
    private static final MailAccount ACCOUNT = SpringUtils.getBean(MailAccount.class);
    /**
     * èŽ·å–é‚®ä»¶å‘é€å®žä¾‹
     */
    public static MailAccount getMailAccount() {
        return ACCOUNT;
    }
    /**
     * èŽ·å–é‚®ä»¶å‘é€å®žä¾‹ (自定义发送人以及授权码)
     *
     * @param user å‘送人
     * @param pass æŽˆæƒç 
     */
    public static MailAccount getMailAccount(String from, String user, String pass) {
        ACCOUNT.setFrom(StringUtils.blankToDefault(from, ACCOUNT.getFrom()));
        ACCOUNT.setUser(StringUtils.blankToDefault(user, ACCOUNT.getUser()));
        ACCOUNT.setPass(StringUtils.blankToDefault(pass, ACCOUNT.getPass()));
        return ACCOUNT;
    }
    /**
     * ä½¿ç”¨é…ç½®æ–‡ä»¶ä¸­è®¾ç½®çš„账户发送文本邮件,发送给单个或多个收件人<br>
     * å¤šä¸ªæ”¶ä»¶äººå¯ä»¥ä½¿ç”¨é€—号“,”分隔,也可以通过分号“;”分隔
     *
     * @param to      æ”¶ä»¶äºº
     * @param subject æ ‡é¢˜
     * @param content æ­£æ–‡
     * @param files   é™„件列表
     * @return message-id
     * @since 3.2.0
     */
    public static String sendText(String to, String subject, String content, File... files) {
        return send(to, subject, content, false, files);
    }
    /**
     * ä½¿ç”¨é…ç½®æ–‡ä»¶ä¸­è®¾ç½®çš„账户发送HTML邮件,发送给单个或多个收件人<br>
     * å¤šä¸ªæ”¶ä»¶äººå¯ä»¥ä½¿ç”¨é€—号“,”分隔,也可以通过分号“;”分隔
     *
     * @param to      æ”¶ä»¶äºº
     * @param subject æ ‡é¢˜
     * @param content æ­£æ–‡
     * @param files   é™„件列表
     * @return message-id
     * @since 3.2.0
     */
    public static String sendHtml(String to, String subject, String content, File... files) {
        return send(to, subject, content, true, files);
    }
    /**
     * ä½¿ç”¨é…ç½®æ–‡ä»¶ä¸­è®¾ç½®çš„账户发送邮件,发送单个或多个收件人<br>
     * å¤šä¸ªæ”¶ä»¶äººå¯ä»¥ä½¿ç”¨é€—号“,”分隔,也可以通过分号“;”分隔
     *
     * @param to      æ”¶ä»¶äºº
     * @param subject æ ‡é¢˜
     * @param content æ­£æ–‡
     * @param isHtml  æ˜¯å¦ä¸ºHTML
     * @param files   é™„件列表
     * @return message-id
     */
    public static String send(String to, String subject, String content, boolean isHtml, File... files) {
        return send(splitAddress(to), subject, content, isHtml, files);
    }
    /**
     * ä½¿ç”¨é…ç½®æ–‡ä»¶ä¸­è®¾ç½®çš„账户发送邮件,发送单个或多个收件人<br>
     * å¤šä¸ªæ”¶ä»¶äººã€æŠ„送人、密送人可以使用逗号“,”分隔,也可以通过分号“;”分隔
     *
     * @param to      æ”¶ä»¶äººï¼Œå¯ä»¥ä½¿ç”¨é€—号“,”分隔,也可以通过分号“;”分隔
     * @param cc      æŠ„送人,可以使用逗号“,”分隔,也可以通过分号“;”分隔
     * @param bcc     å¯†é€äººï¼Œå¯ä»¥ä½¿ç”¨é€—号“,”分隔,也可以通过分号“;”分隔
     * @param subject æ ‡é¢˜
     * @param content æ­£æ–‡
     * @param isHtml  æ˜¯å¦ä¸ºHTML
     * @param files   é™„件列表
     * @return message-id
     * @since 4.0.3
     */
    public static String send(String to, String cc, String bcc, String subject, String content, boolean isHtml, File... files) {
        return send(splitAddress(to), splitAddress(cc), splitAddress(bcc), subject, content, isHtml, files);
    }
    /**
     * ä½¿ç”¨é…ç½®æ–‡ä»¶ä¸­è®¾ç½®çš„账户发送文本邮件,发送给多人
     *
     * @param tos     æ”¶ä»¶äººåˆ—表
     * @param subject æ ‡é¢˜
     * @param content æ­£æ–‡
     * @param files   é™„件列表
     * @return message-id
     */
    public static String sendText(Collection<String> tos, String subject, String content, File... files) {
        return send(tos, subject, content, false, files);
    }
    /**
     * ä½¿ç”¨é…ç½®æ–‡ä»¶ä¸­è®¾ç½®çš„账户发送HTML邮件,发送给多人
     *
     * @param tos     æ”¶ä»¶äººåˆ—表
     * @param subject æ ‡é¢˜
     * @param content æ­£æ–‡
     * @param files   é™„件列表
     * @return message-id
     * @since 3.2.0
     */
    public static String sendHtml(Collection<String> tos, String subject, String content, File... files) {
        return send(tos, subject, content, true, files);
    }
    /**
     * ä½¿ç”¨é…ç½®æ–‡ä»¶ä¸­è®¾ç½®çš„账户发送邮件,发送给多人
     *
     * @param tos     æ”¶ä»¶äººåˆ—表
     * @param subject æ ‡é¢˜
     * @param content æ­£æ–‡
     * @param isHtml  æ˜¯å¦ä¸ºHTML
     * @param files   é™„件列表
     * @return message-id
     */
    public static String send(Collection<String> tos, String subject, String content, boolean isHtml, File... files) {
        return send(tos, null, null, subject, content, isHtml, files);
    }
    /**
     * ä½¿ç”¨é…ç½®æ–‡ä»¶ä¸­è®¾ç½®çš„账户发送邮件,发送给多人
     *
     * @param tos     æ”¶ä»¶äººåˆ—表
     * @param ccs     æŠ„送人列表,可以为null或空
     * @param bccs    å¯†é€äººåˆ—表,可以为null或空
     * @param subject æ ‡é¢˜
     * @param content æ­£æ–‡
     * @param isHtml  æ˜¯å¦ä¸ºHTML
     * @param files   é™„件列表
     * @return message-id
     * @since 4.0.3
     */
    public static String send(Collection<String> tos, Collection<String> ccs, Collection<String> bccs, String subject, String content, boolean isHtml, File... files) {
        return send(getMailAccount(), true, tos, ccs, bccs, subject, content, null, isHtml, files);
    }
    // ------------------------------------------------------------------------------------------------------------------------------- Custom MailAccount
    /**
     * å‘送邮件给多人
     *
     * @param mailAccount é‚®ä»¶è®¤è¯å¯¹è±¡
     * @param to          æ”¶ä»¶äººï¼Œå¤šä¸ªæ”¶ä»¶äººé€—号或者分号隔开
     * @param subject     æ ‡é¢˜
     * @param content     æ­£æ–‡
     * @param isHtml      æ˜¯å¦ä¸ºHTML格式
     * @param files       é™„件列表
     * @return message-id
     * @since 3.2.0
     */
    public static String send(MailAccount mailAccount, String to, String subject, String content, boolean isHtml, File... files) {
        return send(mailAccount, splitAddress(to), subject, content, isHtml, files);
    }
    /**
     * å‘送邮件给多人
     *
     * @param mailAccount é‚®ä»¶å¸æˆ·ä¿¡æ¯
     * @param tos         æ”¶ä»¶äººåˆ—表
     * @param subject     æ ‡é¢˜
     * @param content     æ­£æ–‡
     * @param isHtml      æ˜¯å¦ä¸ºHTML格式
     * @param files       é™„件列表
     * @return message-id
     */
    public static String send(MailAccount mailAccount, Collection<String> tos, String subject, String content, boolean isHtml, File... files) {
        return send(mailAccount, tos, null, null, subject, content, isHtml, files);
    }
    /**
     * å‘送邮件给多人
     *
     * @param mailAccount é‚®ä»¶å¸æˆ·ä¿¡æ¯
     * @param tos         æ”¶ä»¶äººåˆ—表
     * @param ccs         æŠ„送人列表,可以为null或空
     * @param bccs        å¯†é€äººåˆ—表,可以为null或空
     * @param subject     æ ‡é¢˜
     * @param content     æ­£æ–‡
     * @param isHtml      æ˜¯å¦ä¸ºHTML格式
     * @param files       é™„件列表
     * @return message-id
     * @since 4.0.3
     */
    public static String send(MailAccount mailAccount, Collection<String> tos, Collection<String> ccs, Collection<String> bccs, String subject, String content, boolean isHtml, File... files) {
        return send(mailAccount, false, tos, ccs, bccs, subject, content, null, isHtml, files);
    }
    /**
     * ä½¿ç”¨é…ç½®æ–‡ä»¶ä¸­è®¾ç½®çš„账户发送HTML邮件,发送给单个或多个收件人<br>
     * å¤šä¸ªæ”¶ä»¶äººå¯ä»¥ä½¿ç”¨é€—号“,”分隔,也可以通过分号“;”分隔
     *
     * @param to       æ”¶ä»¶äºº
     * @param subject  æ ‡é¢˜
     * @param content  æ­£æ–‡
     * @param imageMap å›¾ç‰‡ä¸Žå ä½ç¬¦ï¼Œå ä½ç¬¦æ ¼å¼ä¸ºcid:$IMAGE_PLACEHOLDER
     * @param files    é™„件列表
     * @return message-id
     * @since 3.2.0
     */
    public static String sendHtml(String to, String subject, String content, Map<String, InputStream> imageMap, File... files) {
        return send(to, subject, content, imageMap, true, files);
    }
    /**
     * ä½¿ç”¨é…ç½®æ–‡ä»¶ä¸­è®¾ç½®çš„账户发送邮件,发送单个或多个收件人<br>
     * å¤šä¸ªæ”¶ä»¶äººå¯ä»¥ä½¿ç”¨é€—号“,”分隔,也可以通过分号“;”分隔
     *
     * @param to       æ”¶ä»¶äºº
     * @param subject  æ ‡é¢˜
     * @param content  æ­£æ–‡
     * @param imageMap å›¾ç‰‡ä¸Žå ä½ç¬¦ï¼Œå ä½ç¬¦æ ¼å¼ä¸ºcid:$IMAGE_PLACEHOLDER
     * @param isHtml   æ˜¯å¦ä¸ºHTML
     * @param files    é™„件列表
     * @return message-id
     */
    public static String send(String to, String subject, String content, Map<String, InputStream> imageMap, boolean isHtml, File... files) {
        return send(splitAddress(to), subject, content, imageMap, isHtml, files);
    }
    /**
     * ä½¿ç”¨é…ç½®æ–‡ä»¶ä¸­è®¾ç½®çš„账户发送邮件,发送单个或多个收件人<br>
     * å¤šä¸ªæ”¶ä»¶äººã€æŠ„送人、密送人可以使用逗号“,”分隔,也可以通过分号“;”分隔
     *
     * @param to       æ”¶ä»¶äººï¼Œå¯ä»¥ä½¿ç”¨é€—号“,”分隔,也可以通过分号“;”分隔
     * @param cc       æŠ„送人,可以使用逗号“,”分隔,也可以通过分号“;”分隔
     * @param bcc      å¯†é€äººï¼Œå¯ä»¥ä½¿ç”¨é€—号“,”分隔,也可以通过分号“;”分隔
     * @param subject  æ ‡é¢˜
     * @param content  æ­£æ–‡
     * @param imageMap å›¾ç‰‡ä¸Žå ä½ç¬¦ï¼Œå ä½ç¬¦æ ¼å¼ä¸ºcid:$IMAGE_PLACEHOLDER
     * @param isHtml   æ˜¯å¦ä¸ºHTML
     * @param files    é™„件列表
     * @return message-id
     * @since 4.0.3
     */
    public static String send(String to, String cc, String bcc, String subject, String content, Map<String, InputStream> imageMap, boolean isHtml, File... files) {
        return send(splitAddress(to), splitAddress(cc), splitAddress(bcc), subject, content, imageMap, isHtml, files);
    }
    /**
     * ä½¿ç”¨é…ç½®æ–‡ä»¶ä¸­è®¾ç½®çš„账户发送HTML邮件,发送给多人
     *
     * @param tos      æ”¶ä»¶äººåˆ—表
     * @param subject  æ ‡é¢˜
     * @param content  æ­£æ–‡
     * @param imageMap å›¾ç‰‡ä¸Žå ä½ç¬¦ï¼Œå ä½ç¬¦æ ¼å¼ä¸ºcid:$IMAGE_PLACEHOLDER
     * @param files    é™„件列表
     * @return message-id
     * @since 3.2.0
     */
    public static String sendHtml(Collection<String> tos, String subject, String content, Map<String, InputStream> imageMap, File... files) {
        return send(tos, subject, content, imageMap, true, files);
    }
    /**
     * ä½¿ç”¨é…ç½®æ–‡ä»¶ä¸­è®¾ç½®çš„账户发送邮件,发送给多人
     *
     * @param tos      æ”¶ä»¶äººåˆ—表
     * @param subject  æ ‡é¢˜
     * @param content  æ­£æ–‡
     * @param imageMap å›¾ç‰‡ä¸Žå ä½ç¬¦ï¼Œå ä½ç¬¦æ ¼å¼ä¸ºcid:$IMAGE_PLACEHOLDER
     * @param isHtml   æ˜¯å¦ä¸ºHTML
     * @param files    é™„件列表
     * @return message-id
     */
    public static String send(Collection<String> tos, String subject, String content, Map<String, InputStream> imageMap, boolean isHtml, File... files) {
        return send(tos, null, null, subject, content, imageMap, isHtml, files);
    }
    /**
     * ä½¿ç”¨é…ç½®æ–‡ä»¶ä¸­è®¾ç½®çš„账户发送邮件,发送给多人
     *
     * @param tos      æ”¶ä»¶äººåˆ—表
     * @param ccs      æŠ„送人列表,可以为null或空
     * @param bccs     å¯†é€äººåˆ—表,可以为null或空
     * @param subject  æ ‡é¢˜
     * @param content  æ­£æ–‡
     * @param imageMap å›¾ç‰‡ä¸Žå ä½ç¬¦ï¼Œå ä½ç¬¦æ ¼å¼ä¸ºcid:$IMAGE_PLACEHOLDER
     * @param isHtml   æ˜¯å¦ä¸ºHTML
     * @param files    é™„件列表
     * @return message-id
     * @since 4.0.3
     */
    public static String send(Collection<String> tos, Collection<String> ccs, Collection<String> bccs, String subject, String content, Map<String, InputStream> imageMap, boolean isHtml, File... files) {
        return send(getMailAccount(), true, tos, ccs, bccs, subject, content, imageMap, isHtml, files);
    }
    // ------------------------------------------------------------------------------------------------------------------------------- Custom MailAccount
    /**
     * å‘送邮件给多人
     *
     * @param mailAccount é‚®ä»¶è®¤è¯å¯¹è±¡
     * @param to          æ”¶ä»¶äººï¼Œå¤šä¸ªæ”¶ä»¶äººé€—号或者分号隔开
     * @param subject     æ ‡é¢˜
     * @param content     æ­£æ–‡
     * @param imageMap    å›¾ç‰‡ä¸Žå ä½ç¬¦ï¼Œå ä½ç¬¦æ ¼å¼ä¸ºcid:$IMAGE_PLACEHOLDER
     * @param isHtml      æ˜¯å¦ä¸ºHTML格式
     * @param files       é™„件列表
     * @return message-id
     * @since 3.2.0
     */
    public static String send(MailAccount mailAccount, String to, String subject, String content, Map<String, InputStream> imageMap, boolean isHtml, File... files) {
        return send(mailAccount, splitAddress(to), subject, content, imageMap, isHtml, files);
    }
    /**
     * å‘送邮件给多人
     *
     * @param mailAccount é‚®ä»¶å¸æˆ·ä¿¡æ¯
     * @param tos         æ”¶ä»¶äººåˆ—表
     * @param subject     æ ‡é¢˜
     * @param content     æ­£æ–‡
     * @param imageMap    å›¾ç‰‡ä¸Žå ä½ç¬¦ï¼Œå ä½ç¬¦æ ¼å¼ä¸ºcid:$IMAGE_PLACEHOLDER
     * @param isHtml      æ˜¯å¦ä¸ºHTML格式
     * @param files       é™„件列表
     * @return message-id
     * @since 4.6.3
     */
    public static String send(MailAccount mailAccount, Collection<String> tos, String subject, String content, Map<String, InputStream> imageMap, boolean isHtml, File... files) {
        return send(mailAccount, tos, null, null, subject, content, imageMap, isHtml, files);
    }
    /**
     * å‘送邮件给多人
     *
     * @param mailAccount é‚®ä»¶å¸æˆ·ä¿¡æ¯
     * @param tos         æ”¶ä»¶äººåˆ—表
     * @param ccs         æŠ„送人列表,可以为null或空
     * @param bccs        å¯†é€äººåˆ—表,可以为null或空
     * @param subject     æ ‡é¢˜
     * @param content     æ­£æ–‡
     * @param imageMap    å›¾ç‰‡ä¸Žå ä½ç¬¦ï¼Œå ä½ç¬¦æ ¼å¼ä¸ºcid:$IMAGE_PLACEHOLDER
     * @param isHtml      æ˜¯å¦ä¸ºHTML格式
     * @param files       é™„件列表
     * @return message-id
     * @since 4.6.3
     */
    public static String send(MailAccount mailAccount, Collection<String> tos, Collection<String> ccs, Collection<String> bccs, String subject, String content, Map<String, InputStream> imageMap,
                              boolean isHtml, File... files) {
        return send(mailAccount, false, tos, ccs, bccs, subject, content, imageMap, isHtml, files);
    }
    /**
     * æ ¹æ®é…ç½®æ–‡ä»¶ï¼ŒèŽ·å–é‚®ä»¶å®¢æˆ·ç«¯ä¼šè¯
     *
     * @param mailAccount é‚®ä»¶è´¦æˆ·é…ç½®
     * @param isSingleton æ˜¯å¦å•例(全局共享会话)
     * @return {@link Session}
     * @since 5.5.7
     */
    public static Session getSession(MailAccount mailAccount, boolean isSingleton) {
        Authenticator authenticator = null;
        if (mailAccount.isAuth()) {
            authenticator = new UserPassAuthenticator(mailAccount.getUser(), mailAccount.getPass());
        }
        return isSingleton ? Session.getDefaultInstance(mailAccount.getSmtpProps(), authenticator) //
            : Session.getInstance(mailAccount.getSmtpProps(), authenticator);
    }
    // ------------------------------------------------------------------------------------------------------------------------ Private method start
    /**
     * å‘送邮件给多人
     *
     * @param mailAccount      é‚®ä»¶å¸æˆ·ä¿¡æ¯
     * @param useGlobalSession æ˜¯å¦å…¨å±€å…±äº«Session
     * @param tos              æ”¶ä»¶äººåˆ—表
     * @param ccs              æŠ„送人列表,可以为null或空
     * @param bccs             å¯†é€äººåˆ—表,可以为null或空
     * @param subject          æ ‡é¢˜
     * @param content          æ­£æ–‡
     * @param imageMap         å›¾ç‰‡ä¸Žå ä½ç¬¦ï¼Œå ä½ç¬¦æ ¼å¼ä¸ºcid:${cid}
     * @param isHtml           æ˜¯å¦ä¸ºHTML格式
     * @param files            é™„件列表
     * @return message-id
     * @since 4.6.3
     */
    private static String send(MailAccount mailAccount, boolean useGlobalSession, Collection<String> tos, Collection<String> ccs, Collection<String> bccs, String subject, String content,
                               Map<String, InputStream> imageMap, boolean isHtml, File... files) {
        final Mail mail = Mail.create(mailAccount).setUseGlobalSession(useGlobalSession);
        // å¯é€‰æŠ„送人
        if (CollUtil.isNotEmpty(ccs)) {
            mail.setCcs(ccs.toArray(new String[0]));
        }
        // å¯é€‰å¯†é€äºº
        if (CollUtil.isNotEmpty(bccs)) {
            mail.setBccs(bccs.toArray(new String[0]));
        }
        mail.setTos(tos.toArray(new String[0]));
        mail.setTitle(subject);
        mail.setContent(content);
        mail.setHtml(isHtml);
        mail.setFiles(files);
        // å›¾ç‰‡
        if (MapUtil.isNotEmpty(imageMap)) {
            for (Map.Entry<String, InputStream> entry : imageMap.entrySet()) {
                mail.addImage(entry.getKey(), entry.getValue());
                // å…³é—­æµ
                IoUtil.close(entry.getValue());
            }
        }
        return mail.send();
    }
    /**
     * å°†å¤šä¸ªè”系人转为列表,分隔符为逗号或者分号
     *
     * @param addresses å¤šä¸ªè”系人,如果为空返回null
     * @return è”系人列表
     */
    private static List<String> splitAddress(String addresses) {
        if (StrUtil.isBlank(addresses)) {
            return null;
        }
        List<String> result;
        if (StrUtil.contains(addresses, CharUtil.COMMA)) {
            result = StrUtil.splitTrim(addresses, CharUtil.COMMA);
        } else if (StrUtil.contains(addresses, ';')) {
            result = StrUtil.splitTrim(addresses, ';');
        } else {
            result = CollUtil.newArrayList(addresses);
        }
        return result;
    }
    // ------------------------------------------------------------------------------------------------------------------------ Private method end
}
ruoyi-common/ruoyi-common-mail/src/main/java/com/ruoyi/common/mail/utils/UserPassAuthenticator.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,33 @@
package com.ruoyi.common.mail.utils;
import jakarta.mail.Authenticator;
import jakarta.mail.PasswordAuthentication;
/**
 * ç”¨æˆ·åå¯†ç éªŒè¯å™¨
 *
 * @author looly
 * @since 3.1.2
 */
public class UserPassAuthenticator extends Authenticator {
    private final String user;
    private final String pass;
    /**
     * æž„造
     *
     * @param user ç”¨æˆ·å
     * @param pass å¯†ç 
     */
    public UserPassAuthenticator(String user, String pass) {
        this.user = user;
        this.pass = pass;
    }
    @Override
    protected PasswordAuthentication getPasswordAuthentication() {
        return new PasswordAuthentication(this.user, this.pass);
    }
}
ruoyi-common/ruoyi-common-mail/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1 @@
com.ruoyi.common.mail.config.MailConfig
ruoyi-common/ruoyi-common-mybatis/pom.xml
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,48 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <groupId>com.ruoyi</groupId>
        <artifactId>ruoyi-common</artifactId>
        <version>${revision}</version>
        <relativePath>../pom.xml</relativePath>
    </parent>
    <modelVersion>4.0.0</modelVersion>
    <artifactId>ruoyi-common-mybatis</artifactId>
    <description>
        ruoyi-common-mybatis æ•°æ®åº“服务
    </description>
    <dependencies>
        <dependency>
            <groupId>com.ruoyi</groupId>
            <artifactId>ruoyi-common-core</artifactId>
        </dependency>
        <dependency>
            <groupId>com.ruoyi</groupId>
            <artifactId>ruoyi-common-satoken</artifactId>
        </dependency>
        <!-- dynamic-datasource å¤šæ•°æ®æº-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>dynamic-datasource-spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
        </dependency>
        <!-- sql性能分析插件 -->
        <dependency>
            <groupId>p6spy</groupId>
            <artifactId>p6spy</artifactId>
        </dependency>
    </dependencies>
</project>
ruoyi-common/ruoyi-common-mybatis/src/main/java/com/baomidou/dynamic/datasource/processor/jakarta/DsJakartaHeaderProcessor.java
ruoyi-common/ruoyi-common-mybatis/src/main/java/com/baomidou/dynamic/datasource/processor/jakarta/DsJakartaSessionProcessor.java
ruoyi-common/ruoyi-common-mybatis/src/main/java/com/ruoyi/common/mybatis/annotation/DataColumn.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,28 @@
package com.ruoyi.common.mybatis.annotation;
import java.lang.annotation.*;
/**
 * æ•°æ®æƒé™
 *
 * ä¸€ä¸ªæ³¨è§£åªèƒ½å¯¹åº”一个模板
 *
 * @author Lion Li
 * @version 3.5.0
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DataColumn {
    /**
     * å ä½ç¬¦å…³é”®å­—
     */
    String[] key() default "deptName";
    /**
     * å ä½ç¬¦æ›¿æ¢å€¼
     */
    String[] value() default "dept_id";
}
ruoyi-common/ruoyi-common-mybatis/src/main/java/com/ruoyi/common/mybatis/annotation/DataPermission.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,18 @@
package com.ruoyi.common.mybatis.annotation;
import java.lang.annotation.*;
/**
 * æ•°æ®æƒé™ç»„
 *
 * @author Lion Li
 * @version 3.5.0
 */
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DataPermission {
    DataColumn[] value();
}
ruoyi-common/ruoyi-common-mybatis/src/main/java/com/ruoyi/common/mybatis/config/MybatisPlusConfig.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,102 @@
package com.ruoyi.common.mybatis.config;
import cn.hutool.core.net.NetUtil;
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import com.baomidou.mybatisplus.core.incrementer.DefaultIdentifierGenerator;
import com.baomidou.mybatisplus.core.incrementer.IdentifierGenerator;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import com.ruoyi.common.mybatis.handler.CreateAndUpdateMetaObjectHandler;
import com.ruoyi.common.mybatis.interceptor.PlusDataPermissionInterceptor;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.transaction.annotation.EnableTransactionManagement;
/**
 * mybatis-plus配置类(下方注释有插件介绍)
 *
 * @author Lion Li
 */
@EnableTransactionManagement(proxyTargetClass = true)
@AutoConfiguration
@MapperScan("${mybatis-plus.mapperPackage}")
public class MybatisPlusConfig {
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        // æ•°æ®æƒé™å¤„理
        interceptor.addInnerInterceptor(dataPermissionInterceptor());
        // åˆ†é¡µæ’ä»¶
        interceptor.addInnerInterceptor(paginationInnerInterceptor());
        // ä¹è§‚锁插件
        interceptor.addInnerInterceptor(optimisticLockerInnerInterceptor());
        return interceptor;
    }
    /**
     * æ•°æ®æƒé™æ‹¦æˆªå™¨
     */
    public PlusDataPermissionInterceptor dataPermissionInterceptor() {
        return new PlusDataPermissionInterceptor();
    }
    /**
     * åˆ†é¡µæ’件,自动识别数据库类型
     */
    public PaginationInnerInterceptor paginationInnerInterceptor() {
        PaginationInnerInterceptor paginationInnerInterceptor = new PaginationInnerInterceptor();
        // è®¾ç½®æœ€å¤§å•页限制数量,默认 500 æ¡ï¼Œ-1 ä¸å—限制
        paginationInnerInterceptor.setMaxLimit(-1L);
        // åˆ†é¡µåˆç†åŒ–
        paginationInnerInterceptor.setOverflow(true);
        return paginationInnerInterceptor;
    }
    /**
     * ä¹è§‚锁插件
     */
    public OptimisticLockerInnerInterceptor optimisticLockerInnerInterceptor() {
        return new OptimisticLockerInnerInterceptor();
    }
    /**
     * å…ƒå¯¹è±¡å­—段填充控制器
     */
    @Bean
    public MetaObjectHandler metaObjectHandler() {
        return new CreateAndUpdateMetaObjectHandler();
    }
    /**
     * ä½¿ç”¨ç½‘卡信息绑定雪花生成器
     * é˜²æ­¢é›†ç¾¤é›ªèбID重复
     */
    @Bean
    public IdentifierGenerator idGenerator() {
        return new DefaultIdentifierGenerator(NetUtil.getLocalhost());
    }
    /**
     * PaginationInnerInterceptor åˆ†é¡µæ’件,自动识别数据库类型
     * https://baomidou.com/pages/97710a/
     * OptimisticLockerInnerInterceptor ä¹è§‚锁插件
     * https://baomidou.com/pages/0d93c0/
     * MetaObjectHandler å…ƒå¯¹è±¡å­—段填充控制器
     * https://baomidou.com/pages/4c6bcf/
     * ISqlInjector sql注入器
     * https://baomidou.com/pages/42ea4a/
     * BlockAttackInnerInterceptor å¦‚果是对全表的删除或更新操作,就会终止该操作
     * https://baomidou.com/pages/f9a237/
     * IllegalSQLInnerInterceptor sql性能规范插件(垃圾SQL拦截)
     * IdentifierGenerator è‡ªå®šä¹‰ä¸»é”®ç­–ç•¥
     * https://baomidou.com/pages/568eb2/
     * TenantLineInnerInterceptor å¤šç§Ÿæˆ·æ’ä»¶
     * https://baomidou.com/pages/aef2f2/
     * DynamicTableNameInnerInterceptor åŠ¨æ€è¡¨åæ’ä»¶
     * https://baomidou.com/pages/2a45ff/
     */
}
ruoyi-common/ruoyi-common-mybatis/src/main/java/com/ruoyi/common/mybatis/core/mapper/BaseMapperPlus.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,192 @@
package com.ruoyi.common.mybatis.core.mapper;
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.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.*;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.toolkit.Db;
import com.ruoyi.common.core.utils.BeanCopyUtils;
import org.apache.ibatis.logging.Log;
import org.apache.ibatis.logging.LogFactory;
import java.io.Serializable;
import java.util.Collection;
import java.util.List;
import java.util.Map;
/**
 * è‡ªå®šä¹‰ Mapper æŽ¥å£, å®žçް è‡ªå®šä¹‰æ‰©å±•
 *
 * @param <M> mapper æ³›åž‹
 * @param <T> table æ³›åž‹
 * @param <V> vo æ³›åž‹
 * @author Lion Li
 * @since 2021-05-13
 */
@SuppressWarnings("unchecked")
public interface BaseMapperPlus<M, T, V> extends BaseMapper<T> {
    Log log = LogFactory.getLog(BaseMapperPlus.class);
    default Class<V> currentVoClass() {
        return (Class<V>) ReflectionKit.getSuperClassGenericType(this.getClass(), BaseMapperPlus.class, 2);
    }
    default Class<T> currentModelClass() {
        return (Class<T>) ReflectionKit.getSuperClassGenericType(this.getClass(), BaseMapperPlus.class, 1);
    }
    default Class<M> currentMapperClass() {
        return (Class<M>) ReflectionKit.getSuperClassGenericType(this.getClass(), BaseMapperPlus.class, 0);
    }
    default List<T> selectList() {
        return this.selectList(new QueryWrapper<>());
    }
    /**
     * æ‰¹é‡æ’å…¥
     */
    default boolean insertBatch(Collection<T> entityList) {
        return Db.saveBatch(entityList);
    }
    /**
     * æ‰¹é‡æ›´æ–°
     */
    default boolean updateBatchById(Collection<T> entityList) {
        return Db.updateBatchById(entityList);
    }
    /**
     * æ‰¹é‡æ’入或更新
     */
    default boolean insertOrUpdateBatch(Collection<T> entityList) {
        return Db.saveOrUpdateBatch(entityList);
    }
    /**
     * æ‰¹é‡æ’å…¥(包含限制条数)
     */
    default boolean insertBatch(Collection<T> entityList, int batchSize) {
        return Db.saveBatch(entityList, batchSize);
    }
    /**
     * æ‰¹é‡æ›´æ–°(包含限制条数)
     */
    default boolean updateBatchById(Collection<T> entityList, int batchSize) {
        return Db.updateBatchById(entityList, batchSize);
    }
    /**
     * æ‰¹é‡æ’入或更新(包含限制条数)
     */
    default boolean insertOrUpdateBatch(Collection<T> entityList, int batchSize) {
        return Db.saveOrUpdateBatch(entityList, batchSize);
    }
    /**
     * æ’入或更新(包含限制条数)
     */
    default boolean insertOrUpdate(T entity) {
        return Db.saveOrUpdate(entity);
    }
    default V selectVoById(Serializable id) {
        return selectVoById(id, this.currentVoClass());
    }
    /**
     * æ ¹æ® ID æŸ¥è¯¢
     */
    default <C> C selectVoById(Serializable id, Class<C> voClass) {
        T obj = this.selectById(id);
        if (ObjectUtil.isNull(obj)) {
            return null;
        }
        return BeanCopyUtils.copy(obj, voClass);
    }
    default List<V> selectVoBatchIds(Collection<? extends Serializable> idList) {
        return selectVoBatchIds(idList, this.currentVoClass());
    }
    /**
     * æŸ¥è¯¢ï¼ˆæ ¹æ®ID æ‰¹é‡æŸ¥è¯¢ï¼‰
     */
    default <C> List<C> selectVoBatchIds(Collection<? extends Serializable> idList, Class<C> voClass) {
        List<T> list = this.selectBatchIds(idList);
        if (CollUtil.isEmpty(list)) {
            return CollUtil.newArrayList();
        }
        return BeanCopyUtils.copyList(list, voClass);
    }
    default List<V> selectVoByMap(Map<String, Object> map) {
        return selectVoByMap(map, this.currentVoClass());
    }
    /**
     * æŸ¥è¯¢ï¼ˆæ ¹æ® columnMap æ¡ä»¶ï¼‰
     */
    default <C> List<C> selectVoByMap(Map<String, Object> map, Class<C> voClass) {
        List<T> list = this.selectByMap(map);
        if (CollUtil.isEmpty(list)) {
            return CollUtil.newArrayList();
        }
        return BeanCopyUtils.copyList(list, voClass);
    }
    default V selectVoOne(Wrapper<T> wrapper) {
        return selectVoOne(wrapper, this.currentVoClass());
    }
    /**
     * æ ¹æ® entity æ¡ä»¶ï¼ŒæŸ¥è¯¢ä¸€æ¡è®°å½•
     */
    default <C> C selectVoOne(Wrapper<T> wrapper, Class<C> voClass) {
        T obj = this.selectOne(wrapper);
        if (ObjectUtil.isNull(obj)) {
            return null;
        }
        return BeanCopyUtils.copy(obj, voClass);
    }
    default List<V> selectVoList(Wrapper<T> wrapper) {
        return selectVoList(wrapper, this.currentVoClass());
    }
    /**
     * æ ¹æ® entity æ¡ä»¶ï¼ŒæŸ¥è¯¢å…¨éƒ¨è®°å½•
     */
    default <C> List<C> selectVoList(Wrapper<T> wrapper, Class<C> voClass) {
        List<T> list = this.selectList(wrapper);
        if (CollUtil.isEmpty(list)) {
            return CollUtil.newArrayList();
        }
        return BeanCopyUtils.copyList(list, voClass);
    }
    default <P extends IPage<V>> P selectVoPage(IPage<T> page, Wrapper<T> wrapper) {
        return selectVoPage(page, wrapper, this.currentVoClass());
    }
    /**
     * åˆ†é¡µæŸ¥è¯¢VO
     */
    default <C, P extends IPage<C>> P selectVoPage(IPage<T> page, Wrapper<T> wrapper, Class<C> voClass) {
        IPage<T> pageData = this.selectPage(page, wrapper);
        IPage<C> voPage = new Page<>(pageData.getCurrent(), pageData.getSize(), pageData.getTotal());
        if (CollUtil.isEmpty(pageData.getRecords())) {
            return (P) voPage;
        }
        voPage.setRecords(BeanCopyUtils.copyList(pageData.getRecords(), voClass));
        return (P) voPage;
    }
}
ruoyi-common/ruoyi-common-mybatis/src/main/java/com/ruoyi/common/mybatis/core/page/PageQuery.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,114 @@
package com.ruoyi.common.mybatis.core.page;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.metadata.OrderItem;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.common.core.exception.ServiceException;
import com.ruoyi.common.core.utils.StringUtils;
import com.ruoyi.common.core.utils.sql.SqlUtil;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
/**
 * åˆ†é¡µæŸ¥è¯¢å®žä½“ç±»
 *
 * @author Lion Li
 */
@Data
public class PageQuery implements Serializable {
    @Serial
    private static final long serialVersionUID = 1L;
    /**
     * åˆ†é¡µå¤§å°
     */
    private Integer pageSize;
    /**
     * å½“前页数
     */
    private Integer pageNum;
    /**
     * æŽ’序列
     */
    private String orderByColumn;
    /**
     * æŽ’序的方向desc或者asc
     */
    private String isAsc;
    /**
     * å½“前记录起始索引 é»˜è®¤å€¼
     */
    public static final int DEFAULT_PAGE_NUM = 1;
    /**
     * æ¯é¡µæ˜¾ç¤ºè®°å½•æ•° é»˜è®¤å€¼ é»˜è®¤æŸ¥å…¨éƒ¨
     */
    public static final int DEFAULT_PAGE_SIZE = Integer.MAX_VALUE;
    public <T> Page<T> build() {
        Integer pageNum = ObjectUtil.defaultIfNull(getPageNum(), DEFAULT_PAGE_NUM);
        Integer pageSize = ObjectUtil.defaultIfNull(getPageSize(), DEFAULT_PAGE_SIZE);
        if (pageNum <= 0) {
            pageNum = DEFAULT_PAGE_NUM;
        }
        Page<T> page = new Page<>(pageNum, pageSize);
        List<OrderItem> orderItems = buildOrderItem();
        if (CollUtil.isNotEmpty(orderItems)) {
            page.addOrder(orderItems);
        }
        return page;
    }
    /**
     * æž„建排序
     *
     * æ”¯æŒçš„用法如下:
     * {isAsc:"asc",orderByColumn:"id"} order by id asc
     * {isAsc:"asc",orderByColumn:"id,createTime"} order by id asc,create_time asc
     * {isAsc:"desc",orderByColumn:"id,createTime"} order by id desc,create_time desc
     * {isAsc:"asc,desc",orderByColumn:"id,createTime"} order by id asc,create_time desc
     */
    private List<OrderItem> buildOrderItem() {
        if (StringUtils.isBlank(orderByColumn) || StringUtils.isBlank(isAsc)) {
            return null;
        }
        String orderBy = SqlUtil.escapeOrderBySql(orderByColumn);
        orderBy = StringUtils.toUnderScoreCase(orderBy);
        // å…¼å®¹å‰ç«¯æŽ’序类型
        isAsc = StringUtils.replaceEach(isAsc, new String[]{"ascending", "descending"}, new String[]{"asc", "desc"});
        String[] orderByArr = orderBy.split(",");
        String[] isAscArr = isAsc.split(",");
        if (isAscArr.length != 1 && isAscArr.length != orderByArr.length) {
            throw new ServiceException("排序参数有误");
        }
        List<OrderItem> list = new ArrayList<>();
        // æ¯ä¸ªå­—段各自排序
        for (int i = 0; i < orderByArr.length; i++) {
            String orderByStr = orderByArr[i];
            String isAscStr = isAscArr.length == 1 ? isAscArr[0] : isAscArr[i];
            if ("asc".equals(isAscStr)) {
                list.add(OrderItem.asc(orderByStr));
            } else if ("desc".equals(isAscStr)) {
                list.add(OrderItem.desc(orderByStr));
            } else {
                throw new ServiceException("排序参数有误");
            }
        }
        return list;
    }
}
ruoyi-common/ruoyi-common-mybatis/src/main/java/com/ruoyi/common/mybatis/core/page/TableDataInfo.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,81 @@
package com.ruoyi.common.mybatis.core.page;
import cn.hutool.http.HttpStatus;
import com.baomidou.mybatisplus.core.metadata.IPage;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serial;
import java.io.Serializable;
import java.util.List;
/**
 * è¡¨æ ¼åˆ†é¡µæ•°æ®å¯¹è±¡
 *
 * @author Lion Li
 */
@Data
@NoArgsConstructor
public class TableDataInfo<T> implements Serializable {
    @Serial
    private static final long serialVersionUID = 1L;
    /**
     * æ€»è®°å½•æ•°
     */
    private long total;
    /**
     * åˆ—表数据
     */
    private List<T> rows;
    /**
     * æ¶ˆæ¯çŠ¶æ€ç 
     */
    private int code;
    /**
     * æ¶ˆæ¯å†…容
     */
    private String msg;
    /**
     * åˆ†é¡µ
     *
     * @param list  åˆ—表数据
     * @param total æ€»è®°å½•æ•°
     */
    public TableDataInfo(List<T> list, long total) {
        this.rows = list;
        this.total = total;
    }
    public static <T> TableDataInfo<T> build(IPage<T> page) {
        TableDataInfo<T> rspData = new TableDataInfo<>();
        rspData.setCode(HttpStatus.HTTP_OK);
        rspData.setMsg("查询成功");
        rspData.setRows(page.getRecords());
        rspData.setTotal(page.getTotal());
        return rspData;
    }
    public static <T> TableDataInfo<T> build(List<T> list) {
        TableDataInfo<T> rspData = new TableDataInfo<>();
        rspData.setCode(HttpStatus.HTTP_OK);
        rspData.setMsg("查询成功");
        rspData.setRows(list);
        rspData.setTotal(list.size());
        return rspData;
    }
    public static <T> TableDataInfo<T> build() {
        TableDataInfo<T> rspData = new TableDataInfo<>();
        rspData.setCode(HttpStatus.HTTP_OK);
        rspData.setMsg("查询成功");
        return rspData;
    }
}
ruoyi-common/ruoyi-common-mybatis/src/main/java/com/ruoyi/common/mybatis/enums/DataBaseType.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,49 @@
package com.ruoyi.common.mybatis.enums;
import com.ruoyi.common.core.utils.StringUtils;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
 * æ•°æ®åº“类型
 *
 * @author Lion Li
 */
@Getter
@AllArgsConstructor
public enum DataBaseType {
    /**
     * MySQL
     */
    MY_SQL("MySQL"),
    /**
     * Oracle
     */
    ORACLE("Oracle"),
    /**
     * PostgreSQL
     */
    POSTGRE_SQL("PostgreSQL"),
    /**
     * SQL Server
     */
    SQL_SERVER("Microsoft SQL Server");
    private final String type;
    public static DataBaseType find(String databaseProductName) {
        if (StringUtils.isBlank(databaseProductName)) {
            return null;
        }
        for (DataBaseType type : values()) {
            if (type.getType().equals(databaseProductName)) {
                return type;
            }
        }
        return null;
    }
}
ruoyi-common/ruoyi-common-mybatis/src/main/java/com/ruoyi/common/mybatis/enums/DataScopeType.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,72 @@
package com.ruoyi.common.mybatis.enums;
import com.ruoyi.common.core.utils.StringUtils;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
 * æ•°æ®æƒé™ç±»åž‹
 * <p>
 * è¯­æ³•支持 spel æ¨¡æ¿è¡¨è¾¾å¼
 * <p>
 * å†…置数据 user å½“前用户 å†…容参考 LoginUser
 * å¦‚需扩展数据 å¯ä½¿ç”¨ {@link com.ruoyi.common.mybatis.helper.DataPermissionHelper} æ“ä½œ
 * å†…置服务 sdss ç³»ç»Ÿæ•°æ®æƒé™æœåŠ¡ å†…容参考 SysDataScopeService
 * å¦‚需扩展更多自定义服务 å¯ä»¥å‚考 sdss è‡ªè¡Œç¼–写
 *
 * @author Lion Li
 * @version 3.5.0
 */
@Getter
@AllArgsConstructor
public enum DataScopeType {
    /**
     * å…¨éƒ¨æ•°æ®æƒé™
     */
    ALL("1", "", ""),
    /**
     * è‡ªå®šæ•°æ®æƒé™
     */
    CUSTOM("2", " #{#deptName} IN ( #{@sdss.getRoleCustom( #user.roleId )} ) ", ""),
    /**
     * éƒ¨é—¨æ•°æ®æƒé™
     */
    DEPT("3", " #{#deptName} = #{#user.deptId} ", ""),
    /**
     * éƒ¨é—¨åŠä»¥ä¸‹æ•°æ®æƒé™
     */
    DEPT_AND_CHILD("4", " #{#deptName} IN ( #{@sdss.getDeptAndChild( #user.deptId )} )", ""),
    /**
     * ä»…本人数据权限
     */
    SELF("5", " #{#userName} = #{#user.userId} ", " 1 = 0 ");
    private final String code;
    /**
     * è¯­æ³• é‡‡ç”¨ spel æ¨¡æ¿è¡¨è¾¾å¼
     */
    private final String sqlTemplate;
    /**
     * ä¸æ»¡è¶³ sqlTemplate åˆ™å¡«å……
     */
    private final String elseSql;
    public static DataScopeType findCode(String code) {
        if (StringUtils.isBlank(code)) {
            return null;
        }
        for (DataScopeType type : values()) {
            if (type.getCode().equals(code)) {
                return type;
            }
        }
        return null;
    }
}
ruoyi-common/ruoyi-common-mybatis/src/main/java/com/ruoyi/common/mybatis/handler/CreateAndUpdateMetaObjectHandler.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,79 @@
package com.ruoyi.common.mybatis.handler;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.http.HttpStatus;
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import com.ruoyi.common.core.domain.model.LoginUser;
import com.ruoyi.common.core.exception.ServiceException;
import com.ruoyi.common.core.utils.StringUtils;
import com.ruoyi.common.core.web.domain.BaseEntity;
import com.ruoyi.common.satoken.utils.LoginHelper;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.reflection.MetaObject;
import java.util.Date;
/**
 * MP注入处理器
 *
 * @author Lion Li
 * @date 2021/4/25
 */
@Slf4j
public class CreateAndUpdateMetaObjectHandler implements MetaObjectHandler {
    @Override
    public void insertFill(MetaObject metaObject) {
        try {
            if (ObjectUtil.isNotNull(metaObject) && metaObject.getOriginalObject() instanceof BaseEntity) {
                BaseEntity baseEntity = (BaseEntity) metaObject.getOriginalObject();
                Date current = ObjectUtil.isNotNull(baseEntity.getCreateTime())
                    ? baseEntity.getCreateTime() : new Date();
                baseEntity.setCreateTime(current);
                baseEntity.setUpdateTime(current);
                String username = StringUtils.isNotBlank(baseEntity.getCreateBy())
                    ? baseEntity.getCreateBy() : getLoginUsername();
                // å½“前已登录 ä¸” åˆ›å»ºäººä¸ºç©º åˆ™å¡«å……
                baseEntity.setCreateBy(username);
                // å½“前已登录 ä¸” æ›´æ–°äººä¸ºç©º åˆ™å¡«å……
                baseEntity.setUpdateBy(username);
            }
        } catch (Exception e) {
            throw new ServiceException("自动注入异常 => " + e.getMessage(), HttpStatus.HTTP_UNAUTHORIZED);
        }
    }
    @Override
    public void updateFill(MetaObject metaObject) {
        try {
            if (ObjectUtil.isNotNull(metaObject) && metaObject.getOriginalObject() instanceof BaseEntity) {
                BaseEntity baseEntity = (BaseEntity) metaObject.getOriginalObject();
                Date current = new Date();
                // æ›´æ–°æ—¶é—´å¡«å……(不管为不为空)
                baseEntity.setUpdateTime(current);
                String username = getLoginUsername();
                // å½“前已登录 æ›´æ–°äººå¡«å……(不管为不为空)
                if (StringUtils.isNotBlank(username)) {
                    baseEntity.setUpdateBy(username);
                }
            }
        } catch (Exception e) {
            throw new ServiceException("自动注入异常 => " + e.getMessage(), HttpStatus.HTTP_UNAUTHORIZED);
        }
    }
    /**
     * èŽ·å–ç™»å½•ç”¨æˆ·å
     */
    private String getLoginUsername() {
        LoginUser loginUser;
        try {
            loginUser = LoginHelper.getLoginUser();
        } catch (Exception e) {
            log.warn("自动注入警告 => ç”¨æˆ·æœªç™»å½•");
            return null;
        }
        return loginUser.getUsername();
    }
}
ruoyi-common/ruoyi-common-mybatis/src/main/java/com/ruoyi/common/mybatis/handler/PlusDataPermissionHandler.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,199 @@
package com.ruoyi.common.mybatis.handler;
import cn.hutool.core.annotation.AnnotationUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.ConcurrentHashSet;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.ClassUtil;
import cn.hutool.core.util.ObjectUtil;
import com.ruoyi.common.core.domain.dto.RoleDTO;
import com.ruoyi.common.core.domain.model.LoginUser;
import com.ruoyi.common.core.exception.ServiceException;
import com.ruoyi.common.core.utils.SpringUtils;
import com.ruoyi.common.core.utils.StreamUtils;
import com.ruoyi.common.core.utils.StringUtils;
import com.ruoyi.common.mybatis.annotation.DataColumn;
import com.ruoyi.common.mybatis.annotation.DataPermission;
import com.ruoyi.common.mybatis.enums.DataScopeType;
import com.ruoyi.common.mybatis.helper.DataPermissionHelper;
import com.ruoyi.common.satoken.utils.LoginHelper;
import lombok.extern.slf4j.Slf4j;
import net.sf.jsqlparser.JSQLParserException;
import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.expression.Parenthesis;
import net.sf.jsqlparser.expression.operators.conditional.AndExpression;
import net.sf.jsqlparser.parser.CCJSqlParserUtil;
import org.springframework.context.expression.BeanFactoryResolver;
import org.springframework.expression.BeanResolver;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.ParserContext;
import org.springframework.expression.common.TemplateParserContext;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import java.lang.reflect.Method;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
import java.util.stream.Collectors;
/**
 * æ•°æ®æƒé™è¿‡æ»¤
 *
 * @author Lion Li
 * @version 3.5.0
 */
@Slf4j
public class PlusDataPermissionHandler {
    /**
     * æ–¹æ³•或类(名称) ä¸Ž æ³¨è§£çš„æ˜ å°„关系缓存
     */
    private final Map<String, DataPermission> dataPermissionCacheMap = new ConcurrentHashMap<>();
    /**
     * æ— æ•ˆæ³¨è§£æ–¹æ³•缓存用于快速返回
     */
    private final Set<String> invalidCacheSet = new ConcurrentHashSet<>();
    /**
     * spel è§£æžå™¨
     */
    private final ExpressionParser parser = new SpelExpressionParser();
    private final ParserContext parserContext = new TemplateParserContext();
    /**
     * bean解析器 ç”¨äºŽå¤„理 spel è¡¨è¾¾å¼ä¸­å¯¹ bean çš„调用
     */
    private final BeanResolver beanResolver = new BeanFactoryResolver(SpringUtils.getBeanFactory());
    public Expression getSqlSegment(Expression where, String mappedStatementId, boolean isSelect) {
        DataColumn[] dataColumns = findAnnotation(mappedStatementId);
        if (ArrayUtil.isEmpty(dataColumns)) {
            invalidCacheSet.add(mappedStatementId);
            return where;
        }
        LoginUser currentUser = DataPermissionHelper.getVariable("user");
        if (ObjectUtil.isNull(currentUser)) {
            currentUser = LoginHelper.getLoginUser();
            DataPermissionHelper.setVariable("user", currentUser);
        }
        // å¦‚果是超级管理员,则不过滤数据
        if (LoginHelper.isAdmin()) {
            return where;
        }
        String dataFilterSql = buildDataFilter(dataColumns, isSelect);
        if (StringUtils.isBlank(dataFilterSql)) {
            return where;
        }
        try {
            Expression expression = CCJSqlParserUtil.parseExpression(dataFilterSql);
            // æ•°æ®æƒé™ä½¿ç”¨å•独的括号 é˜²æ­¢ä¸Žå…¶ä»–条件冲突
            Parenthesis parenthesis = new Parenthesis(expression);
            if (ObjectUtil.isNotNull(where)) {
                return new AndExpression(where, parenthesis);
            } else {
                return parenthesis;
            }
        } catch (JSQLParserException e) {
            throw new ServiceException("数据权限解析异常 => " + e.getMessage());
        }
    }
    /**
     * æž„造数据过滤sql
     */
    private String buildDataFilter(DataColumn[] dataColumns, boolean isSelect) {
        // æ›´æ–°æˆ–删除需满足所有条件
        String joinStr = isSelect ? " OR " : " AND ";
        LoginUser user = DataPermissionHelper.getVariable("user");
        StandardEvaluationContext context = new StandardEvaluationContext();
        context.setBeanResolver(beanResolver);
        DataPermissionHelper.getContext().forEach(context::setVariable);
        Set<String> conditions = new HashSet<>();
        for (RoleDTO role : user.getRoles()) {
            user.setRoleId(role.getRoleId());
            // èŽ·å–è§’è‰²æƒé™æ³›åž‹
            DataScopeType type = DataScopeType.findCode(role.getDataScope());
            if (ObjectUtil.isNull(type)) {
                throw new ServiceException("角色数据范围异常 => " + role.getDataScope());
            }
            // å…¨éƒ¨æ•°æ®æƒé™ç›´æŽ¥è¿”回
            if (type == DataScopeType.ALL) {
                return "";
            }
            boolean isSuccess = false;
            for (DataColumn dataColumn : dataColumns) {
                if (dataColumn.key().length != dataColumn.value().length) {
                    throw new ServiceException("角色数据范围异常 => key与value长度不匹配");
                }
                // ä¸åŒ…含 key å˜é‡ åˆ™ä¸å¤„理
                if (!StringUtils.containsAny(type.getSqlTemplate(),
                    Arrays.stream(dataColumn.key()).map(key -> "#" + key).toArray(String[]::new)
                )) {
                    continue;
                }
                // è®¾ç½®æ³¨è§£å˜é‡ key ä¸ºè¡¨è¾¾å¼å˜é‡ value ä¸ºå˜é‡å€¼
                for (int i = 0; i < dataColumn.key().length; i++) {
                    context.setVariable(dataColumn.key()[i], dataColumn.value()[i]);
                }
                // è§£æžsql模板并填充
                String sql = parser.parseExpression(type.getSqlTemplate(), parserContext).getValue(context, String.class);
                conditions.add(joinStr + sql);
                isSuccess = true;
            }
            // æœªå¤„理成功则填充兜底方案
            if (!isSuccess && StringUtils.isNotBlank(type.getElseSql())) {
                conditions.add(joinStr + type.getElseSql());
            }
        }
        if (CollUtil.isNotEmpty(conditions)) {
            String sql = StreamUtils.join(conditions, Function.identity(), "");
            return sql.substring(joinStr.length());
        }
        return "";
    }
    private DataColumn[] findAnnotation(String mappedStatementId) {
        StringBuilder sb = new StringBuilder(mappedStatementId);
        int index = sb.lastIndexOf(".");
        String clazzName = sb.substring(0, index);
        String methodName = sb.substring(index + 1, sb.length());
        Class<?> clazz = ClassUtil.loadClass(clazzName);
        List<Method> methods = Arrays.stream(ClassUtil.getDeclaredMethods(clazz))
            .filter(method -> method.getName().equals(methodName)).collect(Collectors.toList());
        DataPermission dataPermission;
        // èŽ·å–æ–¹æ³•æ³¨è§£
        for (Method method : methods) {
            dataPermission = dataPermissionCacheMap.get(mappedStatementId);
            if (ObjectUtil.isNotNull(dataPermission)) {
                return dataPermission.value();
            }
            if (AnnotationUtil.hasAnnotation(method, DataPermission.class)) {
                dataPermission = AnnotationUtil.getAnnotation(method, DataPermission.class);
                dataPermissionCacheMap.put(mappedStatementId, dataPermission);
                return dataPermission.value();
            }
        }
        dataPermission = dataPermissionCacheMap.get(clazz.getName());
        if (ObjectUtil.isNotNull(dataPermission)) {
            return dataPermission.value();
        }
        // èŽ·å–ç±»æ³¨è§£
        if (AnnotationUtil.hasAnnotation(clazz, DataPermission.class)) {
            dataPermission = AnnotationUtil.getAnnotation(clazz, DataPermission.class);
            dataPermissionCacheMap.put(clazz.getName(), dataPermission);
            return dataPermission.value();
        }
        return null;
    }
    /**
     * æ˜¯å¦ä¸ºæ— æ•ˆæ–¹æ³• æ— æ•°æ®æƒé™
     */
    public boolean isInvalid(String mappedStatementId) {
        return invalidCacheSet.contains(mappedStatementId);
    }
}
ruoyi-common/ruoyi-common-mybatis/src/main/java/com/ruoyi/common/mybatis/helper/DataBaseHelper.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,72 @@
package com.ruoyi.common.mybatis.helper;
import cn.hutool.core.convert.Convert;
import com.baomidou.dynamic.datasource.DynamicRoutingDataSource;
import com.ruoyi.common.core.exception.ServiceException;
import com.ruoyi.common.core.utils.SpringUtils;
import com.ruoyi.common.mybatis.enums.DataBaseType;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.SQLException;
/**
 * æ•°æ®åº“助手
 *
 * @author Lion Li
 */
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class DataBaseHelper {
    private static final DynamicRoutingDataSource DS = SpringUtils.getBean(DynamicRoutingDataSource.class);
    /**
     * èŽ·å–å½“å‰æ•°æ®åº“ç±»åž‹
     */
    public static DataBaseType getDataBaseType() {
        DataSource dataSource = DS.determineDataSource();
        try (Connection conn = dataSource.getConnection()) {
            DatabaseMetaData metaData = conn.getMetaData();
            String databaseProductName = metaData.getDatabaseProductName();
            return DataBaseType.find(databaseProductName);
        } catch (SQLException e) {
            throw new ServiceException(e.getMessage());
        }
    }
    public static boolean isMySql() {
        return DataBaseType.MY_SQL == getDataBaseType();
    }
    public static boolean isOracle() {
        return DataBaseType.ORACLE == getDataBaseType();
    }
    public static boolean isPostgerSql() {
        return DataBaseType.POSTGRE_SQL == getDataBaseType();
    }
    public static boolean isSqlServer() {
        return DataBaseType.SQL_SERVER == getDataBaseType();
    }
    public static String findInSet(Object var1, String var2) {
        DataBaseType dataBasyType = getDataBaseType();
        String var = Convert.toStr(var1);
        if (dataBasyType == DataBaseType.SQL_SERVER) {
            // charindex(',100,' , ',0,100,101,') <> 0
            return "charindex('," + var + ",' , ','+" + var2 + "+',') <> 0";
        } else if (dataBasyType == DataBaseType.POSTGRE_SQL) {
            // (select position(',100,' in ',0,100,101,')) <> 0
            return "(select position('," + var + ",' in ','||" + var2 + "||',')) <> 0";
        } else if (dataBasyType == DataBaseType.ORACLE) {
            // instr(',0,100,101,' , ',100,') <> 0
            return "instr(','||" + var2 + "||',' , '," + var + ",') <> 0";
        }
        // find_in_set(100 , '0,100,101')
        return "find_in_set(" + var + " , " + var2 + ") <> 0";
    }
}
ruoyi-common/ruoyi-common-mybatis/src/main/java/com/ruoyi/common/mybatis/helper/DataPermissionHelper.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,47 @@
package com.ruoyi.common.mybatis.helper;
import cn.dev33.satoken.context.SaHolder;
import cn.dev33.satoken.context.model.SaStorage;
import cn.hutool.core.util.ObjectUtil;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import java.util.HashMap;
import java.util.Map;
/**
 * æ•°æ®æƒé™åŠ©æ‰‹
 *
 * @author Lion Li
 * @version 3.5.0
 */
@NoArgsConstructor(access = AccessLevel.PRIVATE)
@SuppressWarnings("unchecked cast")
public class DataPermissionHelper {
    private static final String DATA_PERMISSION_KEY = "data:permission";
    public static <T> T getVariable(String key) {
        Map<String, Object> context = getContext();
        return (T) context.get(key);
    }
    public static void setVariable(String key, Object value) {
        Map<String, Object> context = getContext();
        context.put(key, value);
    }
    public static Map<String, Object> getContext() {
        SaStorage saStorage = SaHolder.getStorage();
        Object attribute = saStorage.get(DATA_PERMISSION_KEY);
        if (ObjectUtil.isNull(attribute)) {
            saStorage.set(DATA_PERMISSION_KEY, new HashMap<>());
            attribute = saStorage.get(DATA_PERMISSION_KEY);
        }
        if (attribute instanceof Map) {
            return (Map<String, Object>) attribute;
        }
        throw new NullPointerException("data permission context type exception");
    }
}
ruoyi-common/ruoyi-common-mybatis/src/main/java/com/ruoyi/common/mybatis/helper/MybatisExceptionHandler.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,46 @@
package com.ruoyi.common.mybatis.helper;
import com.ruoyi.common.core.domain.R;
import lombok.extern.slf4j.Slf4j;
import org.mybatis.spring.MyBatisSystemException;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import jakarta.servlet.http.HttpServletRequest;
/**
 * Mybatis异常处理器
 *
 * @author Lion Li
 */
@Slf4j
@RestControllerAdvice
public class MybatisExceptionHandler {
    /**
     * ä¸»é”®æˆ–UNIQUE索引,数据重复异常
     */
    @ExceptionHandler(DuplicateKeyException.class)
    public R<Void> handleDuplicateKeyException(DuplicateKeyException e, HttpServletRequest request) {
        String requestURI = request.getRequestURI();
        log.error("请求地址'{}',数据库中已存在记录'{}'", requestURI, e.getMessage());
        return R.fail("数据库中已存在该记录,请联系管理员确认");
    }
    /**
     * Mybatis系统异常 é€šç”¨å¤„理
     */
    @ExceptionHandler(MyBatisSystemException.class)
    public R<Void> handleCannotFindDataSourceException(MyBatisSystemException e, HttpServletRequest request) {
        String requestURI = request.getRequestURI();
        String message = e.getMessage();
        if (message.contains("CannotFindDataSourceException")) {
            log.error("请求地址'{}', æœªæ‰¾åˆ°æ•°æ®æº", requestURI);
            return R.fail("未找到数据源,请联系管理员确认");
        }
        log.error("请求地址'{}', Mybatis系统异常", requestURI, e);
        return R.fail(message);
    }
}
ruoyi-common/ruoyi-common-mybatis/src/main/java/com/ruoyi/common/mybatis/interceptor/PlusDataPermissionInterceptor.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,108 @@
package com.ruoyi.common.mybatis.interceptor;
import com.baomidou.mybatisplus.core.plugins.InterceptorIgnoreHelper;
import com.baomidou.mybatisplus.core.toolkit.PluginUtils;
import com.baomidou.mybatisplus.extension.parser.JsqlParserSupport;
import com.baomidou.mybatisplus.extension.plugins.inner.InnerInterceptor;
import com.ruoyi.common.mybatis.handler.PlusDataPermissionHandler;
import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.statement.delete.Delete;
import net.sf.jsqlparser.statement.select.PlainSelect;
import net.sf.jsqlparser.statement.select.Select;
import net.sf.jsqlparser.statement.select.SelectBody;
import net.sf.jsqlparser.statement.select.SetOperationList;
import net.sf.jsqlparser.statement.update.Update;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.List;
/**
 * æ•°æ®æƒé™æ‹¦æˆªå™¨
 *
 * @author Lion Li
 * @version 3.5.0
 */
public class PlusDataPermissionInterceptor extends JsqlParserSupport implements InnerInterceptor {
    private final PlusDataPermissionHandler dataPermissionHandler = new PlusDataPermissionHandler();
    @Override
    public void beforeQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
        // æ£€æŸ¥å¿½ç•¥æ³¨è§£
        if (InterceptorIgnoreHelper.willIgnoreDataPermission(ms.getId())) {
            return;
        }
        // æ£€æŸ¥æ˜¯å¦æ— æ•ˆ æ— æ•°æ®æƒé™æ³¨è§£
        if (dataPermissionHandler.isInvalid(ms.getId())) {
            return;
        }
        // è§£æž sql åˆ†é…å¯¹åº”方法
        PluginUtils.MPBoundSql mpBs = PluginUtils.mpBoundSql(boundSql);
        mpBs.sql(parserSingle(mpBs.sql(), ms.getId()));
    }
    @Override
    public void beforePrepare(StatementHandler sh, Connection connection, Integer transactionTimeout) {
        PluginUtils.MPStatementHandler mpSh = PluginUtils.mpStatementHandler(sh);
        MappedStatement ms = mpSh.mappedStatement();
        SqlCommandType sct = ms.getSqlCommandType();
        if (sct == SqlCommandType.UPDATE || sct == SqlCommandType.DELETE) {
            if (InterceptorIgnoreHelper.willIgnoreDataPermission(ms.getId())) {
                return;
            }
            PluginUtils.MPBoundSql mpBs = mpSh.mPBoundSql();
            mpBs.sql(parserMulti(mpBs.sql(), ms.getId()));
        }
    }
    @Override
    protected void processSelect(Select select, int index, String sql, Object obj) {
        SelectBody selectBody = select.getSelectBody();
        if (selectBody instanceof PlainSelect) {
            this.setWhere((PlainSelect) selectBody, (String) obj);
        } else if (selectBody instanceof SetOperationList) {
            SetOperationList setOperationList = (SetOperationList) selectBody;
            List<SelectBody> selectBodyList = setOperationList.getSelects();
            selectBodyList.forEach(s -> this.setWhere((PlainSelect) s, (String) obj));
        }
    }
    @Override
    protected void processUpdate(Update update, int index, String sql, Object obj) {
        Expression sqlSegment = dataPermissionHandler.getSqlSegment(update.getWhere(), (String) obj, false);
        if (null != sqlSegment) {
            update.setWhere(sqlSegment);
        }
    }
    @Override
    protected void processDelete(Delete delete, int index, String sql, Object obj) {
        Expression sqlSegment = dataPermissionHandler.getSqlSegment(delete.getWhere(), (String) obj, false);
        if (null != sqlSegment) {
            delete.setWhere(sqlSegment);
        }
    }
    /**
     * è®¾ç½® where æ¡ä»¶
     *
     * @param plainSelect       æŸ¥è¯¢å¯¹è±¡
     * @param mappedStatementId æ‰§è¡Œæ–¹æ³•id
     */
    protected void setWhere(PlainSelect plainSelect, String mappedStatementId) {
        Expression sqlSegment = dataPermissionHandler.getSqlSegment(plainSelect.getWhere(), mappedStatementId, true);
        if (null != sqlSegment) {
            plainSelect.setWhere(sqlSegment);
        }
    }
}
ruoyi-common/ruoyi-common-mybatis/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1 @@
com.ruoyi.common.mybatis.config.MybatisPlusConfig
ruoyi-common/ruoyi-common-oss/pom.xml
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,36 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <groupId>com.ruoyi</groupId>
        <artifactId>ruoyi-common</artifactId>
        <version>${revision}</version>
        <relativePath>../pom.xml</relativePath>
    </parent>
    <modelVersion>4.0.0</modelVersion>
    <artifactId>ruoyi-common-oss</artifactId>
    <description>
        ruoyi-common-oss oss服务
    </description>
    <dependencies>
        <dependency>
            <groupId>com.ruoyi</groupId>
            <artifactId>ruoyi-common-core</artifactId>
        </dependency>
        <dependency>
            <groupId>com.ruoyi</groupId>
            <artifactId>ruoyi-common-redis</artifactId>
        </dependency>
        <dependency>
            <groupId>com.amazonaws</groupId>
            <artifactId>aws-java-sdk-s3</artifactId>
        </dependency>
    </dependencies>
</project>
ruoyi-common/ruoyi-common-oss/src/main/java/com/ruoyi/common/oss/constant/OssConstant.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,38 @@
package com.ruoyi.common.oss.constant;
import java.util.Arrays;
import java.util.List;
/**
 * å¯¹è±¡å­˜å‚¨å¸¸é‡
 *
 * @author Lion Li
 */
public interface OssConstant {
    /**
     * é»˜è®¤é…ç½®KEY
     */
    String DEFAULT_CONFIG_KEY = "sys_oss:default_config";
    /**
     * é¢„览列表资源开关Key
     */
    String PEREVIEW_LIST_RESOURCE_KEY = "sys.oss.previewListResource";
    /**
     * ç³»ç»Ÿæ•°æ®ids
     */
    List<Long> SYSTEM_DATA_IDS = Arrays.asList(1L, 2L, 3L, 4L);
    /**
     * äº‘服务商
     */
    String[] CLOUD_SERVICE = new String[] {"aliyun", "qcloud", "qiniu", "obs"};
    /**
     * https çŠ¶æ€
     */
    String IS_HTTPS = "Y";
}
ruoyi-common/ruoyi-common-oss/src/main/java/com/ruoyi/common/oss/core/OssClient.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,240 @@
package com.ruoyi.common.oss.core;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.IdUtil;
import com.amazonaws.ClientConfiguration;
import com.amazonaws.HttpMethod;
import com.amazonaws.Protocol;
import com.amazonaws.auth.AWSCredentials;
import com.amazonaws.auth.AWSCredentialsProvider;
import com.amazonaws.auth.AWSStaticCredentialsProvider;
import com.amazonaws.auth.BasicAWSCredentials;
import com.amazonaws.client.builder.AwsClientBuilder;
import com.amazonaws.services.s3.AmazonS3;
import com.amazonaws.services.s3.AmazonS3Client;
import com.amazonaws.services.s3.AmazonS3ClientBuilder;
import com.amazonaws.services.s3.model.*;
import com.ruoyi.common.core.utils.DateUtils;
import com.ruoyi.common.core.utils.StringUtils;
import com.ruoyi.common.oss.constant.OssConstant;
import com.ruoyi.common.oss.entity.UploadResult;
import com.ruoyi.common.oss.enumd.AccessPolicyType;
import com.ruoyi.common.oss.enumd.PolicyType;
import com.ruoyi.common.oss.exception.OssException;
import com.ruoyi.common.oss.properties.OssProperties;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.net.URL;
import java.util.Date;
/**
 * S3 å­˜å‚¨åè®® æ‰€æœ‰å…¼å®¹S3协议的云厂商均支持
 * é˜¿é‡Œäº‘ è…¾è®¯äº‘ ä¸ƒç‰›äº‘ minio
 *
 * @author Lion Li
 */
public class OssClient {
    private final String configKey;
    private final OssProperties properties;
    private final AmazonS3 client;
    public OssClient(String configKey, OssProperties ossProperties) {
        this.configKey = configKey;
        this.properties = ossProperties;
        try {
            AwsClientBuilder.EndpointConfiguration endpointConfig =
                new AwsClientBuilder.EndpointConfiguration(properties.getEndpoint(), properties.getRegion());
            AWSCredentials credentials = new BasicAWSCredentials(properties.getAccessKey(), properties.getSecretKey());
            AWSCredentialsProvider credentialsProvider = new AWSStaticCredentialsProvider(credentials);
            ClientConfiguration clientConfig = new ClientConfiguration();
            if (OssConstant.IS_HTTPS.equals(properties.getIsHttps())) {
                clientConfig.setProtocol(Protocol.HTTPS);
            } else {
                clientConfig.setProtocol(Protocol.HTTP);
            }
            AmazonS3ClientBuilder build = AmazonS3Client.builder()
                .withEndpointConfiguration(endpointConfig)
                .withClientConfiguration(clientConfig)
                .withCredentials(credentialsProvider)
                .disableChunkedEncoding();
            if (!StringUtils.containsAny(properties.getEndpoint(), OssConstant.CLOUD_SERVICE)) {
                // minio ä½¿ç”¨https限制使用域名访问 éœ€è¦æ­¤é…ç½® ç«™ç‚¹å¡«åŸŸå
                build.enablePathStyleAccess();
            }
            this.client = build.build();
            createBucket();
        } catch (Exception e) {
            if (e instanceof OssException) {
                throw e;
            }
            throw new OssException("配置错误! è¯·æ£€æŸ¥ç³»ç»Ÿé…ç½®:[" + e.getMessage() + "]");
        }
    }
    public void createBucket() {
        try {
            String bucketName = properties.getBucketName();
            if (client.doesBucketExistV2(bucketName)) {
                return;
            }
            CreateBucketRequest createBucketRequest = new CreateBucketRequest(bucketName);
            AccessPolicyType accessPolicy = getAccessPolicy();
            createBucketRequest.setCannedAcl(accessPolicy.getAcl());
            client.createBucket(createBucketRequest);
            client.setBucketPolicy(bucketName, getPolicy(bucketName, accessPolicy.getPolicyType()));
        } catch (Exception e) {
            throw new OssException("创建Bucket失败, è¯·æ ¸å¯¹é…ç½®ä¿¡æ¯:[" + e.getMessage() + "]");
        }
    }
    public UploadResult upload(byte[] data, String path, String contentType) {
        return upload(new ByteArrayInputStream(data), path, contentType);
    }
    public UploadResult upload(InputStream inputStream, String path, String contentType) {
        if (!(inputStream instanceof ByteArrayInputStream)) {
            inputStream = new ByteArrayInputStream(IoUtil.readBytes(inputStream));
        }
        try {
            ObjectMetadata metadata = new ObjectMetadata();
            metadata.setContentType(contentType);
            metadata.setContentLength(inputStream.available());
            PutObjectRequest putObjectRequest = new PutObjectRequest(properties.getBucketName(), path, inputStream, metadata);
            // è®¾ç½®ä¸Šä¼ å¯¹è±¡çš„ Acl ä¸ºå…¬å…±è¯»
            putObjectRequest.setCannedAcl(getAccessPolicy().getAcl());
            client.putObject(putObjectRequest);
        } catch (Exception e) {
            throw new OssException("上传文件失败,请检查配置信息:[" + e.getMessage() + "]");
        }
        return UploadResult.builder().url(getUrl() + "/" + path).filename(path).build();
    }
    public void delete(String path) {
        path = path.replace(getUrl() + "/", "");
        try {
            client.deleteObject(properties.getBucketName(), path);
        } catch (Exception e) {
            throw new OssException("删除文件失败,请检查配置信息:[" + e.getMessage() + "]");
        }
    }
    public UploadResult uploadSuffix(byte[] data, String suffix, String contentType) {
        return upload(data, getPath(properties.getPrefix(), suffix), contentType);
    }
    public UploadResult uploadSuffix(InputStream inputStream, String suffix, String contentType) {
        return upload(inputStream, getPath(properties.getPrefix(), suffix), contentType);
    }
    /**
     * èŽ·å–æ–‡ä»¶å…ƒæ•°æ®
     *
     * @param path å®Œæ•´æ–‡ä»¶è·¯å¾„
     */
    public ObjectMetadata getObjectMetadata(String path) {
        path = path.replace(getUrl() + "/", "");
        S3Object object = client.getObject(properties.getBucketName(), path);
        return object.getObjectMetadata();
    }
    public InputStream getObjectContent(String path) {
        path = path.replace(getUrl() + "/", "");
        S3Object object = client.getObject(properties.getBucketName(), path);
        return object.getObjectContent();
    }
    public String getUrl() {
        String domain = properties.getDomain();
        String endpoint = properties.getEndpoint();
        String header = OssConstant.IS_HTTPS.equals(properties.getIsHttps()) ? "https://" : "http://";
        // äº‘服务商直接返回
        if (StringUtils.containsAny(endpoint, OssConstant.CLOUD_SERVICE)) {
            if (StringUtils.isNotBlank(domain)) {
                return header + domain;
            }
            return header + properties.getBucketName() + "." + endpoint;
        }
        // minio å•独处理
        if (StringUtils.isNotBlank(domain)) {
            return header + domain + "/" + properties.getBucketName();
        }
        return header + endpoint + "/" + properties.getBucketName();
    }
    public String getPath(String prefix, String suffix) {
        // ç”Ÿæˆuuid
        String uuid = IdUtil.fastSimpleUUID();
        // æ–‡ä»¶è·¯å¾„
        String path = DateUtils.datePath() + "/" + uuid;
        if (StringUtils.isNotBlank(prefix)) {
            path = prefix + "/" + path;
        }
        return path + suffix;
    }
    public String getConfigKey() {
        return configKey;
    }
    public String getPrivateUrl(String objectKey, Integer second) {
        GeneratePresignedUrlRequest generatePresignedUrlRequest =
            new GeneratePresignedUrlRequest(properties.getBucketName(), objectKey)
                .withMethod(HttpMethod.GET)
                .withExpiration(new Date(System.currentTimeMillis() + 1000L * second));
        URL url = client.generatePresignedUrl(generatePresignedUrlRequest);
        return url.toString();
    }
    /**
     * èŽ·å–å½“å‰æ¡¶æƒé™ç±»åž‹
     *
     * @return å½“前桶权限类型code
     */
    public AccessPolicyType getAccessPolicy() {
        return AccessPolicyType.getByType(properties.getAccessPolicy());
    }
    private static String getPolicy(String bucketName, PolicyType policyType) {
        StringBuilder builder = new StringBuilder();
        builder.append("{\n\"Statement\": [\n{\n\"Action\": [\n");
        if (policyType == PolicyType.WRITE) {
            builder.append("\"s3:GetBucketLocation\",\n\"s3:ListBucketMultipartUploads\"\n");
        } else if (policyType == PolicyType.READ_WRITE) {
            builder.append("\"s3:GetBucketLocation\",\n\"s3:ListBucket\",\n\"s3:ListBucketMultipartUploads\"\n");
        } else {
            builder.append("\"s3:GetBucketLocation\"\n");
        }
        builder.append("],\n\"Effect\": \"Allow\",\n\"Principal\": \"*\",\n\"Resource\": \"arn:aws:s3:::");
        builder.append(bucketName);
        builder.append("\"\n},\n");
        if (policyType == PolicyType.READ) {
            builder.append("{\n\"Action\": [\n\"s3:ListBucket\"\n],\n\"Effect\": \"Deny\",\n\"Principal\": \"*\",\n\"Resource\": \"arn:aws:s3:::");
            builder.append(bucketName);
            builder.append("\"\n},\n");
        }
        builder.append("{\n\"Action\": ");
        switch (policyType) {
            case WRITE:
                builder.append("[\n\"s3:AbortMultipartUpload\",\n\"s3:DeleteObject\",\n\"s3:ListMultipartUploadParts\",\n\"s3:PutObject\"\n],\n");
                break;
            case READ_WRITE:
                builder.append("[\n\"s3:AbortMultipartUpload\",\n\"s3:DeleteObject\",\n\"s3:GetObject\",\n\"s3:ListMultipartUploadParts\",\n\"s3:PutObject\"\n],\n");
                break;
            default:
                builder.append("\"s3:GetObject\",\n");
                break;
        }
        builder.append("\"Effect\": \"Allow\",\n\"Principal\": \"*\",\n\"Resource\": \"arn:aws:s3:::");
        builder.append(bucketName);
        builder.append("/*\"\n}\n],\n\"Version\": \"2012-10-17\"\n}\n");
        return builder.toString();
    }
}
ruoyi-common/ruoyi-common-oss/src/main/java/com/ruoyi/common/oss/entity/UploadResult.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,24 @@
package com.ruoyi.common.oss.entity;
import lombok.Builder;
import lombok.Data;
/**
 * ä¸Šä¼ è¿”回体
 *
 * @author Lion Li
 */
@Data
@Builder
public class UploadResult {
    /**
     * æ–‡ä»¶è·¯å¾„
     */
    private String url;
    /**
     * æ–‡ä»¶å
     */
    private String filename;
}
ruoyi-common/ruoyi-common-oss/src/main/java/com/ruoyi/common/oss/enumd/AccessPolicyType.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,55 @@
package com.ruoyi.common.oss.enumd;
import com.amazonaws.services.s3.model.CannedAccessControlList;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
 * æ¡¶è®¿é—®ç­–略配置
 *
 * @author é™ˆè³
 */
@Getter
@AllArgsConstructor
public enum AccessPolicyType {
    /**
     * private
     */
    PRIVATE("0", CannedAccessControlList.Private, PolicyType.WRITE),
    /**
     * public
     */
    PUBLIC("1", CannedAccessControlList.PublicRead, PolicyType.READ),
    /**
     * custom
     */
    CUSTOM("2",CannedAccessControlList.PublicRead, PolicyType.READ);
    /**
     * æ¡¶ æƒé™ç±»åž‹
     */
    private final String type;
    /**
     * æ–‡ä»¶å¯¹è±¡ æƒé™ç±»åž‹
     */
    private final CannedAccessControlList acl;
    /**
     * æ¡¶ç­–略类型
     */
    private final PolicyType policyType;
    public static AccessPolicyType getByType(String type) {
        for (AccessPolicyType value : values()) {
            if (value.getType().equals(type)) {
                return value;
            }
        }
        throw new RuntimeException("'type' not found By " + type);
    }
}
ruoyi-common/ruoyi-common-oss/src/main/java/com/ruoyi/common/oss/enumd/PolicyType.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,35 @@
package com.ruoyi.common.oss.enumd;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
 * minio策略配置
 *
 * @author Lion Li
 */
@Getter
@AllArgsConstructor
public enum PolicyType {
    /**
     * åªè¯»
     */
    READ("read-only"),
    /**
     * åªå†™
     */
    WRITE("write-only"),
    /**
     * è¯»å†™
     */
    READ_WRITE("read-write");
    /**
     * ç±»åž‹
     */
    private final String type;
}
ruoyi-common/ruoyi-common-oss/src/main/java/com/ruoyi/common/oss/exception/OssException.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,19 @@
package com.ruoyi.common.oss.exception;
import java.io.Serial;
/**
 * OSS异常类
 *
 * @author Lion Li
 */
public class OssException extends RuntimeException {
    @Serial
    private static final long serialVersionUID = 1L;
    public OssException(String msg) {
        super(msg);
    }
}
ruoyi-common/ruoyi-common-oss/src/main/java/com/ruoyi/common/oss/factory/OssFactory.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,79 @@
package com.ruoyi.common.oss.factory;
import com.ruoyi.common.core.constant.CacheNames;
import com.ruoyi.common.core.utils.JsonUtils;
import com.ruoyi.common.core.utils.StringUtils;
import com.ruoyi.common.oss.constant.OssConstant;
import com.ruoyi.common.oss.core.OssClient;
import com.ruoyi.common.oss.exception.OssException;
import com.ruoyi.common.oss.properties.OssProperties;
import com.ruoyi.common.redis.utils.CacheUtils;
import com.ruoyi.common.redis.utils.RedisUtils;
import lombok.extern.slf4j.Slf4j;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
 * æ–‡ä»¶ä¸Šä¼ Factory
 *
 * @author Lion Li
 */
@Slf4j
public class OssFactory {
    private static final Map<String, OssClient> CLIENT_CACHE = new ConcurrentHashMap<>();
    /**
     * åˆå§‹åŒ–工厂
     */
    public static void init() {
        log.info("初始化OSS工厂");
        RedisUtils.subscribe(OssConstant.DEFAULT_CONFIG_KEY, String.class, configKey -> {
            OssClient client = getClient(configKey);
            // æœªåˆå§‹åŒ–不处理
            if (client != null) {
                refresh(configKey);
                log.info("订阅刷新OSS配置 => " + configKey);
            }
        });
    }
    /**
     * èŽ·å–é»˜è®¤å®žä¾‹
     */
    public static OssClient instance() {
        // èŽ·å–redis é»˜è®¤ç±»åž‹
        String configKey = RedisUtils.getCacheObject(OssConstant.DEFAULT_CONFIG_KEY);
        if (StringUtils.isEmpty(configKey)) {
            throw new OssException("文件存储服务类型无法找到!");
        }
        return instance(configKey);
    }
    /**
     * æ ¹æ®ç±»åž‹èŽ·å–å®žä¾‹
     */
    public static OssClient instance(String configKey) {
        OssClient client = getClient(configKey);
        if (client == null) {
            refresh(configKey);
            return getClient(configKey);
        }
        return client;
    }
    private static void refresh(String configKey) {
        String json = CacheUtils.get(CacheNames.SYS_OSS_CONFIG, configKey);
        if (json == null) {
            throw new OssException("系统异常, '" + configKey + "'配置信息不存在!");
        }
        OssProperties properties = JsonUtils.parseObject(json, OssProperties.class);
        CLIENT_CACHE.put(configKey, new OssClient(configKey, properties));
    }
    private static OssClient getClient(String configKey) {
        return CLIENT_CACHE.get(configKey);
    }
}
ruoyi-common/ruoyi-common-oss/src/main/java/com/ruoyi/common/oss/properties/OssProperties.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,58 @@
package com.ruoyi.common.oss.properties;
import lombok.Data;
/**
 * OSS对象存储 é…ç½®å±žæ€§
 *
 * @author Lion Li
 */
@Data
public class OssProperties {
    /**
     * è®¿é—®ç«™ç‚¹
     */
    private String endpoint;
    /**
     * è‡ªå®šä¹‰åŸŸå
     */
    private String domain;
    /**
     * å‰ç¼€
     */
    private String prefix;
    /**
     * ACCESS_KEY
     */
    private String accessKey;
    /**
     * SECRET_KEY
     */
    private String secretKey;
    /**
     * å­˜å‚¨ç©ºé—´å
     */
    private String bucketName;
    /**
     * å­˜å‚¨åŒºåŸŸ
     */
    private String region;
    /**
     * æ˜¯å¦https(Y=是,N=否)
     */
    private String isHttps;
    /**
     * æ¡¶æƒé™ç±»åž‹(0private 1public 2custom)
     */
    private String accessPolicy;
}
ruoyi-common/ruoyi-common-ratelimiter/pom.xml
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,31 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <groupId>com.ruoyi</groupId>
        <artifactId>ruoyi-common</artifactId>
        <version>${revision}</version>
        <relativePath>../pom.xml</relativePath>
    </parent>
    <modelVersion>4.0.0</modelVersion>
    <artifactId>ruoyi-common-ratelimiter</artifactId>
    <description>
        ruoyi-common-ratelimiter é™æµåŠŸèƒ½
    </description>
    <dependencies>
        <dependency>
            <groupId>com.ruoyi</groupId>
            <artifactId>ruoyi-common-core</artifactId>
        </dependency>
        <dependency>
            <groupId>com.ruoyi</groupId>
            <artifactId>ruoyi-common-redis</artifactId>
        </dependency>
    </dependencies>
</project>
ruoyi-common/ruoyi-common-ratelimiter/src/main/java/com/ruoyi/common/ratelimiter/annotation/RateLimiter.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,36 @@
package com.ruoyi.common.ratelimiter.annotation;
import com.ruoyi.common.core.constant.CacheConstants;
import com.ruoyi.common.ratelimiter.enums.LimitType;
import java.lang.annotation.*;
/**
 * é™æµæ³¨è§£
 *
 * @author Lion Li
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RateLimiter {
    /**
     * é™æµkey
     */
    String key() default CacheConstants.RATE_LIMIT_KEY;
    /**
     * é™æµæ—¶é—´,单位秒
     */
    int time() default 60;
    /**
     * é™æµæ¬¡æ•°
     */
    int count() default 100;
    /**
     * é™æµç±»åž‹
     */
    LimitType limitType() default LimitType.DEFAULT;
}
ruoyi-common/ruoyi-common-ratelimiter/src/main/java/com/ruoyi/common/ratelimiter/aspectj/RateLimiterAspect.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,64 @@
package com.ruoyi.common.ratelimiter.aspectj;
import com.ruoyi.common.core.exception.ServiceException;
import com.ruoyi.common.core.utils.MessageUtils;
import com.ruoyi.common.core.utils.ServletUtils;
import com.ruoyi.common.ratelimiter.annotation.RateLimiter;
import com.ruoyi.common.ratelimiter.enums.LimitType;
import com.ruoyi.common.redis.utils.RedisUtils;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.reflect.MethodSignature;
import org.redisson.api.RateType;
import java.lang.reflect.Method;
/**
 * é™æµå¤„理
 *
 * @author Lion Li
 */
@Slf4j
@Aspect
public class RateLimiterAspect {
    @Before("@annotation(rateLimiter)")
    public void doBefore(JoinPoint point, RateLimiter rateLimiter) throws Throwable {
        int time = rateLimiter.time();
        int count = rateLimiter.count();
        String combineKey = getCombineKey(rateLimiter, point);
        try {
            RateType rateType = RateType.OVERALL;
            if (rateLimiter.limitType() == LimitType.CLUSTER) {
                rateType = RateType.PER_CLIENT;
            }
            long number = RedisUtils.rateLimiter(combineKey, rateType, count, time);
            if (number == -1) {
                throw new ServiceException(MessageUtils.message("rate.limiter.message"));
            }
            log.info("限制令牌 => {}, å‰©ä½™ä»¤ç‰Œ => {}, ç¼“å­˜key => '{}'", count, number, combineKey);
        } catch (ServiceException e) {
            throw e;
        } catch (Exception e) {
            throw new RuntimeException("服务器限流异常,请稍候再试");
        }
    }
    public String getCombineKey(RateLimiter rateLimiter, JoinPoint point) {
        StringBuilder stringBuffer = new StringBuilder(rateLimiter.key());
        if (rateLimiter.limitType() == LimitType.IP) {
            // èŽ·å–è¯·æ±‚ip
            stringBuffer.append(ServletUtils.getClientIP()).append("-");
        } else if (rateLimiter.limitType() == LimitType.CLUSTER) {
            // èŽ·å–å®¢æˆ·ç«¯å®žä¾‹id
            stringBuffer.append(RedisUtils.getClient().getId()).append("-");
        }
        MethodSignature signature = (MethodSignature) point.getSignature();
        Method method = signature.getMethod();
        Class<?> targetClass = method.getDeclaringClass();
        stringBuffer.append(targetClass.getName()).append("-").append(method.getName());
        return stringBuffer.toString();
    }
}
ruoyi-common/ruoyi-common-ratelimiter/src/main/java/com/ruoyi/common/ratelimiter/config/RateLimiterConfig.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,20 @@
package com.ruoyi.common.ratelimiter.config;
import com.ruoyi.common.ratelimiter.aspectj.RateLimiterAspect;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.data.redis.connection.RedisConfiguration;
/**
 * @author guangxin
 * @date 2023/1/18
 */
@AutoConfiguration(after = RedisConfiguration.class)
public class RateLimiterConfig {
    @Bean
    public RateLimiterAspect rateLimiterAspect() {
        return new RateLimiterAspect();
    }
}
ruoyi-common/ruoyi-common-ratelimiter/src/main/java/com/ruoyi/common/ratelimiter/enums/LimitType.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,24 @@
package com.ruoyi.common.ratelimiter.enums;
/**
 * é™æµç±»åž‹
 *
 * @author ruoyi
 */
public enum LimitType {
    /**
     * é»˜è®¤ç­–略全局限流
     */
    DEFAULT,
    /**
     * æ ¹æ®è¯·æ±‚者IP进行限流
     */
    IP,
    /**
     * å®žä¾‹é™æµ(集群多后端实例)
     */
    CLUSTER
}
ruoyi-common/ruoyi-common-ratelimiter/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1 @@
com.ruoyi.common.ratelimiter.config.RateLimiterConfig
ruoyi-common/ruoyi-common-redis/pom.xml
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,38 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <groupId>com.ruoyi</groupId>
        <artifactId>ruoyi-common</artifactId>
        <version>${revision}</version>
        <relativePath>../pom.xml</relativePath>
    </parent>
    <modelVersion>4.0.0</modelVersion>
    <artifactId>ruoyi-common-redis</artifactId>
    <description>
        ruoyi-common-redis ç¼“存服务
    </description>
    <dependencies>
        <!-- RuoYi Common Core-->
        <dependency>
            <groupId>com.ruoyi</groupId>
            <artifactId>ruoyi-common-core</artifactId>
        </dependency>
        <!--redisson-->
        <dependency>
            <groupId>org.redisson</groupId>
            <artifactId>redisson-spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>lock4j-redisson-spring-boot-starter</artifactId>
        </dependency>
    </dependencies>
</project>
ruoyi-common/ruoyi-common-redis/src/main/java/com/ruoyi/common/redis/config/RedisConfig.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,130 @@
package com.ruoyi.common.redis.config;
import cn.hutool.core.util.ObjectUtil;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.ruoyi.common.redis.config.properties.RedissonProperties;
import com.ruoyi.common.redis.handler.KeyPrefixHandler;
import com.ruoyi.common.redis.manager.PlusSpringCacheManager;
import lombok.extern.slf4j.Slf4j;
import org.redisson.codec.JsonJacksonCodec;
import org.redisson.spring.starter.RedissonAutoConfigurationCustomizer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
/**
 * redis配置
 *
 * @author Lion Li
 */
@Slf4j
@AutoConfiguration
@EnableCaching
@EnableConfigurationProperties(RedissonProperties.class)
public class RedisConfig {
    @Autowired
    private RedissonProperties redissonProperties;
    @Autowired
    private ObjectMapper objectMapper;
    @Bean
    public RedissonAutoConfigurationCustomizer redissonCustomizer() {
        return config -> {
            config.setThreads(redissonProperties.getThreads())
                .setNettyThreads(redissonProperties.getNettyThreads())
                .setCodec(new JsonJacksonCodec(objectMapper));
            RedissonProperties.SingleServerConfig singleServerConfig = redissonProperties.getSingleServerConfig();
            if (ObjectUtil.isNotNull(singleServerConfig)) {
                // ä½¿ç”¨å•机模式
                config.useSingleServer()
                    //设置redis key前缀
                    .setNameMapper(new KeyPrefixHandler(redissonProperties.getKeyPrefix()))
                    .setTimeout(singleServerConfig.getTimeout())
                    .setClientName(singleServerConfig.getClientName())
                    .setIdleConnectionTimeout(singleServerConfig.getIdleConnectionTimeout())
                    .setSubscriptionConnectionPoolSize(singleServerConfig.getSubscriptionConnectionPoolSize())
                    .setConnectionMinimumIdleSize(singleServerConfig.getConnectionMinimumIdleSize())
                    .setConnectionPoolSize(singleServerConfig.getConnectionPoolSize());
            }
            // é›†ç¾¤é…ç½®æ–¹å¼ å‚考下方注释
            RedissonProperties.ClusterServersConfig clusterServersConfig = redissonProperties.getClusterServersConfig();
            if (ObjectUtil.isNotNull(clusterServersConfig)) {
                config.useClusterServers()
                    //设置redis key前缀
                    .setNameMapper(new KeyPrefixHandler(redissonProperties.getKeyPrefix()))
                    .setTimeout(clusterServersConfig.getTimeout())
                    .setClientName(clusterServersConfig.getClientName())
                    .setIdleConnectionTimeout(clusterServersConfig.getIdleConnectionTimeout())
                    .setSubscriptionConnectionPoolSize(clusterServersConfig.getSubscriptionConnectionPoolSize())
                    .setMasterConnectionMinimumIdleSize(clusterServersConfig.getMasterConnectionMinimumIdleSize())
                    .setMasterConnectionPoolSize(clusterServersConfig.getMasterConnectionPoolSize())
                    .setSlaveConnectionMinimumIdleSize(clusterServersConfig.getSlaveConnectionMinimumIdleSize())
                    .setSlaveConnectionPoolSize(clusterServersConfig.getSlaveConnectionPoolSize())
                    .setReadMode(clusterServersConfig.getReadMode())
                    .setSubscriptionMode(clusterServersConfig.getSubscriptionMode());
            }
            log.info("初始化 redis é…ç½®");
        };
    }
    /**
     * è‡ªå®šä¹‰ç¼“存管理器 æ•´åˆspring-cache
     */
    @Bean
    public CacheManager cacheManager() {
        return new PlusSpringCacheManager();
    }
    /**
     * redis集群配置 yml
     *
     * --- # redis é›†ç¾¤é…ç½®(单机与集群只能开启一个另一个需要注释掉)
     * spring:
     *   redis:
     *     cluster:
     *       nodes:
     *         - 192.168.0.100:6379
     *         - 192.168.0.101:6379
     *         - 192.168.0.102:6379
     *     # å¯†ç 
     *     password:
     *     # è¿žæŽ¥è¶…æ—¶æ—¶é—´
     *     timeout: 10s
     *     # æ˜¯å¦å¼€å¯ssl
     *     ssl: false
     *
     * redisson:
     *   # çº¿ç¨‹æ± æ•°é‡
     *   threads: 16
     *   # Netty线程池数量
     *   nettyThreads: 32
     *   # é›†ç¾¤é…ç½®
     *   clusterServersConfig:
     *     # å®¢æˆ·ç«¯åç§°
     *     clientName: ${ruoyi.name}
     *     # master最小空闲连接数
     *     masterConnectionMinimumIdleSize: 32
     *     # master连接池大小
     *     masterConnectionPoolSize: 64
     *     # slave最小空闲连接数
     *     slaveConnectionMinimumIdleSize: 32
     *     # slave连接池大小
     *     slaveConnectionPoolSize: 64
     *     # è¿žæŽ¥ç©ºé—²è¶…时,单位:毫秒
     *     idleConnectionTimeout: 10000
     *     # å‘½ä»¤ç­‰å¾…超时,单位:毫秒
     *     timeout: 3000
     *     # å‘布和订阅连接池大小
     *     subscriptionConnectionPoolSize: 50
     *     # è¯»å–模式
     *     readMode: "SLAVE"
     *     # è®¢é˜…模式
     *     subscriptionMode: "MASTER"
     */
}
ruoyi-common/ruoyi-common-redis/src/main/java/com/ruoyi/common/redis/config/properties/RedissonProperties.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,137 @@
package com.ruoyi.common.redis.config.properties;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.redisson.config.ReadMode;
import org.redisson.config.SubscriptionMode;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
/**
 * Redisson é…ç½®å±žæ€§
 *
 * @author Lion Li
 */
@Data
@Component
@ConfigurationProperties(prefix = "redisson")
public class RedissonProperties {
    /**
     * redis缓存key前缀
     */
    private String keyPrefix;
    /**
     * çº¿ç¨‹æ± æ•°é‡,默认值 = å½“前处理核数量 * 2
     */
    private int threads;
    /**
     * Netty线程池数量,默认值 = å½“前处理核数量 * 2
     */
    private int nettyThreads;
    /**
     * å•机服务配置
     */
    private SingleServerConfig singleServerConfig;
    /**
     * é›†ç¾¤æœåŠ¡é…ç½®
     */
    private ClusterServersConfig clusterServersConfig;
    @Data
    @NoArgsConstructor
    public static class SingleServerConfig {
        /**
         * å®¢æˆ·ç«¯åç§°
         */
        private String clientName;
        /**
         * æœ€å°ç©ºé—²è¿žæŽ¥æ•°
         */
        private int connectionMinimumIdleSize;
        /**
         * è¿žæŽ¥æ± å¤§å°
         */
        private int connectionPoolSize;
        /**
         * è¿žæŽ¥ç©ºé—²è¶…时,单位:毫秒
         */
        private int idleConnectionTimeout;
        /**
         * å‘½ä»¤ç­‰å¾…超时,单位:毫秒
         */
        private int timeout;
        /**
         * å‘布和订阅连接池大小
         */
        private int subscriptionConnectionPoolSize;
    }
    @Data
    @NoArgsConstructor
    public static class ClusterServersConfig {
        /**
         * å®¢æˆ·ç«¯åç§°
         */
        private String clientName;
        /**
         * master最小空闲连接数
         */
        private int masterConnectionMinimumIdleSize;
        /**
         * master连接池大小
         */
        private int masterConnectionPoolSize;
        /**
         * slave最小空闲连接数
         */
        private int slaveConnectionMinimumIdleSize;
        /**
         * slave连接池大小
         */
        private int slaveConnectionPoolSize;
        /**
         * è¿žæŽ¥ç©ºé—²è¶…时,单位:毫秒
         */
        private int idleConnectionTimeout;
        /**
         * å‘½ä»¤ç­‰å¾…超时,单位:毫秒
         */
        private int timeout;
        /**
         * å‘布和订阅连接池大小
         */
        private int subscriptionConnectionPoolSize;
        /**
         * è¯»å–模式
         */
        private ReadMode readMode;
        /**
         * è®¢é˜…模式
         */
        private SubscriptionMode subscriptionMode;
    }
}
ruoyi-common/ruoyi-common-redis/src/main/java/com/ruoyi/common/redis/handler/KeyPrefixHandler.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,50 @@
package com.ruoyi.common.redis.handler;
import com.ruoyi.common.core.utils.StringUtils;
import org.redisson.api.NameMapper;
/**
 * redis缓存key前缀处理
 *
 * @author ye
 * @date 2022/7/14 17:44
 * @since 4.3.0
 */
public class KeyPrefixHandler implements NameMapper {
    private final String keyPrefix;
    public KeyPrefixHandler(String keyPrefix) {
        //前缀为空 åˆ™è¿”回空前缀
        this.keyPrefix = StringUtils.isBlank(keyPrefix) ? "" : keyPrefix + ":";
    }
    /**
     * å¢žåŠ å‰ç¼€
     */
    @Override
    public String map(String name) {
        if (StringUtils.isBlank(name)) {
            return null;
        }
        if (StringUtils.isNotBlank(keyPrefix) && !name.startsWith(keyPrefix)) {
            return keyPrefix + name;
        }
        return name;
    }
    /**
     * åŽ»é™¤å‰ç¼€
     */
    @Override
    public String unmap(String name) {
        if (StringUtils.isBlank(name)) {
            return null;
        }
        if (StringUtils.isNotBlank(keyPrefix) && name.startsWith(keyPrefix)) {
            return name.substring(keyPrefix.length());
        }
        return name;
    }
}
ruoyi-common/ruoyi-common-redis/src/main/java/com/ruoyi/common/redis/manager/PlusSpringCacheManager.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,191 @@
/**
 * Copyright (c) 2013-2021 Nikita Koksharov
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.ruoyi.common.redis.manager;
import com.ruoyi.common.redis.utils.RedisUtils;
import org.redisson.api.RMap;
import org.redisson.api.RMapCache;
import org.redisson.spring.cache.CacheConfig;
import org.redisson.spring.cache.RedissonCache;
import org.springframework.boot.convert.DurationStyle;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.cache.transaction.TransactionAwareCacheDecorator;
import org.springframework.util.StringUtils;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
/**
 * A {@link org.springframework.cache.CacheManager} implementation
 * backed by Redisson instance.
 * <p>
 * ä¿®æ”¹ RedissonSpringCacheManager æºç 
 * é‡å†™ cacheName å¤„理方法 æ”¯æŒå¤šå‚æ•°
 *
 * @author Nikita Koksharov
 *
 */
@SuppressWarnings("unchecked")
public class PlusSpringCacheManager implements CacheManager {
    private boolean dynamic = true;
    private boolean allowNullValues = true;
    private boolean transactionAware = true;
    Map<String, CacheConfig> configMap = new ConcurrentHashMap<>();
    ConcurrentMap<String, Cache> instanceMap = new ConcurrentHashMap<>();
    /**
     * Creates CacheManager supplied by Redisson instance
     */
    public PlusSpringCacheManager() {
    }
    /**
     * Defines possibility of storing {@code null} values.
     * <p>
     * Default is <code>true</code>
     *
     * @param allowNullValues stores if <code>true</code>
     */
    public void setAllowNullValues(boolean allowNullValues) {
        this.allowNullValues = allowNullValues;
    }
    /**
     * Defines if cache aware of Spring-managed transactions.
     * If {@code true} put/evict operations are executed only for successful transaction in after-commit phase.
     * <p>
     * Default is <code>false</code>
     *
     * @param transactionAware cache is transaction aware if <code>true</code>
     */
    public void setTransactionAware(boolean transactionAware) {
        this.transactionAware = transactionAware;
    }
    /**
     * Defines 'fixed' cache names.
     * A new cache instance will not be created in dynamic for non-defined names.
     * <p>
     * `null` parameter setups dynamic mode
     *
     * @param names of caches
     */
    public void setCacheNames(Collection<String> names) {
        if (names != null) {
            for (String name : names) {
                getCache(name);
            }
            dynamic = false;
        } else {
            dynamic = true;
        }
    }
    /**
     * Set cache config mapped by cache name
     *
     * @param config object
     */
    public void setConfig(Map<String, ? extends CacheConfig> config) {
        this.configMap = (Map<String, CacheConfig>) config;
    }
    protected CacheConfig createDefaultConfig() {
        return new CacheConfig();
    }
    @Override
    public Cache getCache(String name) {
        Cache cache = instanceMap.get(name);
        if (cache != null) {
            return cache;
        }
        if (!dynamic) {
            return cache;
        }
        CacheConfig config = configMap.get(name);
        if (config == null) {
            config = createDefaultConfig();
            configMap.put(name, config);
        }
        // é‡å†™ cacheName æ”¯æŒå¤šå‚æ•°
        String[] array = StringUtils.delimitedListToStringArray(name, "#");
        name = array[0];
        if (array.length > 1) {
            config.setTTL(DurationStyle.detectAndParse(array[1]).toMillis());
        }
        if (array.length > 2) {
            config.setMaxIdleTime(DurationStyle.detectAndParse(array[2]).toMillis());
        }
        if (array.length > 3) {
            config.setMaxSize(Integer.parseInt(array[3]));
        }
        if (config.getMaxIdleTime() == 0 && config.getTTL() == 0 && config.getMaxSize() == 0) {
            return createMap(name, config);
        }
        return createMapCache(name, config);
    }
    private Cache createMap(String name, CacheConfig config) {
        RMap<Object, Object> map = RedisUtils.getClient().getMap(name);
        Cache cache = new RedissonCache(map, allowNullValues);
        if (transactionAware) {
            cache = new TransactionAwareCacheDecorator(cache);
        }
        Cache oldCache = instanceMap.putIfAbsent(name, cache);
        if (oldCache != null) {
            cache = oldCache;
        }
        return cache;
    }
    private Cache createMapCache(String name, CacheConfig config) {
        RMapCache<Object, Object> map = RedisUtils.getClient().getMapCache(name);
        Cache cache = new RedissonCache(map, config, allowNullValues);
        if (transactionAware) {
            cache = new TransactionAwareCacheDecorator(cache);
        }
        Cache oldCache = instanceMap.putIfAbsent(name, cache);
        if (oldCache != null) {
            cache = oldCache;
        } else {
            map.setMaxSize(config.getMaxSize());
        }
        return cache;
    }
    @Override
    public Collection<String> getCacheNames() {
        return Collections.unmodifiableSet(configMap.keySet());
    }
}
ruoyi-common/ruoyi-common-redis/src/main/java/com/ruoyi/common/redis/utils/CacheUtils.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,75 @@
package com.ruoyi.common.redis.utils;
import com.ruoyi.common.core.utils.SpringUtils;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import org.redisson.api.RMap;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import java.util.Set;
/**
 * ç¼“存操作工具类 {@link }
 *
 * @author Michelle.Chung
 * @date 2022/8/13
 */
@NoArgsConstructor(access = AccessLevel.PRIVATE)
@SuppressWarnings(value = {"unchecked"})
public class CacheUtils {
    private static final CacheManager CACHE_MANAGER = SpringUtils.getBean(CacheManager.class);
    /**
     * èŽ·å–ç¼“å­˜ç»„å†…æ‰€æœ‰çš„KEY
     *
     * @param cacheNames ç¼“存组名称
     */
    public static Set<Object> keys(String cacheNames) {
        RMap<Object, Object> rmap = (RMap<Object, Object>) CACHE_MANAGER.getCache(cacheNames).getNativeCache();
        return rmap.keySet();
    }
    /**
     * èŽ·å–ç¼“å­˜å€¼
     *
     * @param cacheNames ç¼“存组名称
     * @param key        ç¼“å­˜key
     */
    public static <T> T get(String cacheNames, Object key) {
        Cache.ValueWrapper wrapper = CACHE_MANAGER.getCache(cacheNames).get(key);
        return wrapper != null ? (T) wrapper.get() : null;
    }
    /**
     * ä¿å­˜ç¼“存值
     *
     * @param cacheNames ç¼“存组名称
     * @param key        ç¼“å­˜key
     * @param value      ç¼“存值
     */
    public static void put(String cacheNames, Object key, Object value) {
        CACHE_MANAGER.getCache(cacheNames).put(key, value);
    }
    /**
     * åˆ é™¤ç¼“存值
     *
     * @param cacheNames ç¼“存组名称
     * @param key        ç¼“å­˜key
     */
    public static void evict(String cacheNames, Object key) {
        CACHE_MANAGER.getCache(cacheNames).evict(key);
    }
    /**
     * æ¸…空缓存值
     *
     * @param cacheNames ç¼“存组名称
     */
    public static void clear(String cacheNames) {
        CACHE_MANAGER.getCache(cacheNames).clear();
    }
}
ruoyi-common/ruoyi-common-redis/src/main/java/com/ruoyi/common/redis/utils/QueueUtils.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,180 @@
package com.ruoyi.common.redis.utils;
import com.ruoyi.common.core.utils.SpringUtils;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import org.redisson.api.*;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
/**
 * åˆ†å¸ƒå¼é˜Ÿåˆ—工具
 * è½»é‡çº§é˜Ÿåˆ— é‡é‡çº§æ•°æ®é‡ è¯·ä½¿ç”¨ MQ
 * è¦æ±‚ redis 5.X ä»¥ä¸Š
 *
 * @author Lion Li
 * @version 3.6.0 æ–°å¢ž
 */
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class QueueUtils {
    private static final RedissonClient CLIENT = SpringUtils.getBean(RedissonClient.class);
    /**
     * èŽ·å–å®¢æˆ·ç«¯å®žä¾‹
     */
    public static RedissonClient getClient() {
        return CLIENT;
    }
    /**
     * æ·»åŠ æ™®é€šé˜Ÿåˆ—æ•°æ®
     *
     * @param queueName é˜Ÿåˆ—名
     * @param data      æ•°æ®
     */
    public static <T> boolean addQueueObject(String queueName, T data) {
        RBlockingQueue<T> queue = CLIENT.getBlockingQueue(queueName);
        return queue.offer(data);
    }
    /**
     * é€šç”¨èŽ·å–ä¸€ä¸ªé˜Ÿåˆ—æ•°æ® æ²¡æœ‰æ•°æ®è¿”回 null(不支持延迟队列)
     *
     * @param queueName é˜Ÿåˆ—名
     */
    public static <T> T getQueueObject(String queueName) {
        RBlockingQueue<T> queue = CLIENT.getBlockingQueue(queueName);
        return queue.poll();
    }
    /**
     * é€šç”¨åˆ é™¤é˜Ÿåˆ—数据(不支持延迟队列)
     */
    public static <T> boolean removeQueueObject(String queueName, T data) {
        RBlockingQueue<T> queue = CLIENT.getBlockingQueue(queueName);
        return queue.remove(data);
    }
    /**
     * é€šç”¨é”€æ¯é˜Ÿåˆ— æ‰€æœ‰é˜»å¡žç›‘听 æŠ¥é”™(不支持延迟队列)
     */
    public static <T> boolean destroyQueue(String queueName) {
        RBlockingQueue<T> queue = CLIENT.getBlockingQueue(queueName);
        return queue.delete();
    }
    /**
     * æ·»åŠ å»¶è¿Ÿé˜Ÿåˆ—æ•°æ® é»˜è®¤æ¯«ç§’
     *
     * @param queueName é˜Ÿåˆ—名
     * @param data      æ•°æ®
     * @param time      å»¶è¿Ÿæ—¶é—´
     */
    public static <T> void addDelayedQueueObject(String queueName, T data, long time) {
        addDelayedQueueObject(queueName, data, time, TimeUnit.MILLISECONDS);
    }
    /**
     * æ·»åŠ å»¶è¿Ÿé˜Ÿåˆ—æ•°æ®
     *
     * @param queueName é˜Ÿåˆ—名
     * @param data      æ•°æ®
     * @param time      å»¶è¿Ÿæ—¶é—´
     * @param timeUnit  å•位
     */
    public static <T> void addDelayedQueueObject(String queueName, T data, long time, TimeUnit timeUnit) {
        RBlockingQueue<T> queue = CLIENT.getBlockingQueue(queueName);
        RDelayedQueue<T> delayedQueue = CLIENT.getDelayedQueue(queue);
        delayedQueue.offer(data, time, timeUnit);
    }
    /**
     * èŽ·å–ä¸€ä¸ªå»¶è¿Ÿé˜Ÿåˆ—æ•°æ® æ²¡æœ‰æ•°æ®è¿”回 null
     *
     * @param queueName é˜Ÿåˆ—名
     */
    public static <T> T getDelayedQueueObject(String queueName) {
        RBlockingQueue<T> queue = CLIENT.getBlockingQueue(queueName);
        RDelayedQueue<T> delayedQueue = CLIENT.getDelayedQueue(queue);
        return delayedQueue.poll();
    }
    /**
     * åˆ é™¤å»¶è¿Ÿé˜Ÿåˆ—数据
     */
    public static <T> boolean removeDelayedQueueObject(String queueName, T data) {
        RBlockingQueue<T> queue = CLIENT.getBlockingQueue(queueName);
        RDelayedQueue<T> delayedQueue = CLIENT.getDelayedQueue(queue);
        return delayedQueue.remove(data);
    }
    /**
     * é”€æ¯å»¶è¿Ÿé˜Ÿåˆ— æ‰€æœ‰é˜»å¡žç›‘听 æŠ¥é”™
     */
    public static <T> void destroyDelayedQueue(String queueName) {
        RBlockingQueue<T> queue = CLIENT.getBlockingQueue(queueName);
        RDelayedQueue<T> delayedQueue = CLIENT.getDelayedQueue(queue);
        delayedQueue.destroy();
    }
    /**
     * æ·»åŠ ä¼˜å…ˆé˜Ÿåˆ—æ•°æ®
     *
     * @param queueName é˜Ÿåˆ—名
     * @param data      æ•°æ®
     */
    public static <T> boolean addPriorityQueueObject(String queueName, T data) {
        RPriorityBlockingQueue<T> priorityBlockingQueue = CLIENT.getPriorityBlockingQueue(queueName);
        return priorityBlockingQueue.offer(data);
    }
    /**
     * å°è¯•设置 æœ‰ç•Œé˜Ÿåˆ— å®¹é‡ ç”¨äºŽé™åˆ¶æ•°é‡
     *
     * @param queueName é˜Ÿåˆ—名
     * @param capacity  å®¹é‡
     */
    public static <T> boolean trySetBoundedQueueCapacity(String queueName, int capacity) {
        RBoundedBlockingQueue<T> boundedBlockingQueue = CLIENT.getBoundedBlockingQueue(queueName);
        return boundedBlockingQueue.trySetCapacity(capacity);
    }
    /**
     * å°è¯•设置 æœ‰ç•Œé˜Ÿåˆ— å®¹é‡ ç”¨äºŽé™åˆ¶æ•°é‡
     *
     * @param queueName é˜Ÿåˆ—名
     * @param capacity  å®¹é‡
     * @param destroy   å·²å­˜åœ¨æ˜¯å¦é”€æ¯
     */
    public static <T> boolean trySetBoundedQueueCapacity(String queueName, int capacity, boolean destroy) {
        RBoundedBlockingQueue<T> boundedBlockingQueue = CLIENT.getBoundedBlockingQueue(queueName);
        if (boundedBlockingQueue.isExists() && destroy) {
            destroyQueue(queueName);
        }
        return boundedBlockingQueue.trySetCapacity(capacity);
    }
    /**
     * æ·»åŠ æœ‰ç•Œé˜Ÿåˆ—æ•°æ®
     *
     * @param queueName é˜Ÿåˆ—名
     * @param data      æ•°æ®
     * @return æ·»åŠ æˆåŠŸ true å·²è¾¾åˆ°ç•Œé™ false
     */
    public static <T> boolean addBoundedQueueObject(String queueName, T data) {
        RBoundedBlockingQueue<T> boundedBlockingQueue = CLIENT.getBoundedBlockingQueue(queueName);
        return boundedBlockingQueue.offer(data);
    }
    /**
     * è®¢é˜…阻塞队列(可订阅所有实现类 ä¾‹å¦‚: å»¶è¿Ÿ ä¼˜å…ˆ æœ‰ç•Œ ç­‰)
     */
    public static <T> void subscribeBlockingQueue(String queueName, Consumer<T> consumer) {
        RBlockingQueue<T> queue = CLIENT.getBlockingQueue(queueName);
        queue.subscribeOnElements(consumer);
    }
}
ruoyi-common/ruoyi-common-redis/src/main/java/com/ruoyi/common/redis/utils/RedisUtils.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,462 @@
package com.ruoyi.common.redis.utils;
import com.ruoyi.common.core.utils.SpringUtils;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import org.redisson.api.*;
import java.time.Duration;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Consumer;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
 * redis å·¥å…·ç±»
 *
 * @author Lion Li
 * @version 3.1.0 æ–°å¢ž
 */
@NoArgsConstructor(access = AccessLevel.PRIVATE)
@SuppressWarnings(value = {"unchecked", "rawtypes"})
public class RedisUtils {
    private static final RedissonClient CLIENT = SpringUtils.getBean(RedissonClient.class);
    /**
     * é™æµ
     *
     * @param key          é™æµkey
     * @param rateType     é™æµç±»åž‹
     * @param rate         é€Ÿçއ
     * @param rateInterval é€ŸçŽ‡é—´éš”
     * @return -1 è¡¨ç¤ºå¤±è´¥
     */
    public static long rateLimiter(String key, RateType rateType, int rate, int rateInterval) {
        RRateLimiter rateLimiter = CLIENT.getRateLimiter(key);
        rateLimiter.trySetRate(rateType, rate, rateInterval, RateIntervalUnit.SECONDS);
        if (rateLimiter.tryAcquire()) {
            return rateLimiter.availablePermits();
        } else {
            return -1L;
        }
    }
    /**
     * èŽ·å–å®¢æˆ·ç«¯å®žä¾‹
     */
    public static RedissonClient getClient() {
        return CLIENT;
    }
    /**
     * å‘布通道消息
     *
     * @param channelKey é€šé“key
     * @param msg        å‘送数据
     * @param consumer   è‡ªå®šä¹‰å¤„理
     */
    public static <T> void publish(String channelKey, T msg, Consumer<T> consumer) {
        RTopic topic = CLIENT.getTopic(channelKey);
        topic.publish(msg);
        consumer.accept(msg);
    }
    public static <T> void publish(String channelKey, T msg) {
        RTopic topic = CLIENT.getTopic(channelKey);
        topic.publish(msg);
    }
    /**
     * è®¢é˜…通道接收消息
     *
     * @param channelKey é€šé“key
     * @param clazz      æ¶ˆæ¯ç±»åž‹
     * @param consumer   è‡ªå®šä¹‰å¤„理
     */
    public static <T> void subscribe(String channelKey, Class<T> clazz, Consumer<T> consumer) {
        RTopic topic = CLIENT.getTopic(channelKey);
        topic.addListener(clazz, (channel, msg) -> consumer.accept(msg));
    }
    /**
     * ç¼“存基本的对象,Integer、String、实体类等
     *
     * @param key   ç¼“存的键值
     * @param value ç¼“存的值
     */
    public static <T> void setCacheObject(final String key, final T value) {
        setCacheObject(key, value, false);
    }
    /**
     * ç¼“存基本的对象,保留当前对象 TTL æœ‰æ•ˆæœŸ
     *
     * @param key       ç¼“存的键值
     * @param value     ç¼“存的值
     * @param isSaveTtl æ˜¯å¦ä¿ç•™TTL有效期(例如: set之前ttl剩余90 set之后还是为90)
     * @since Redis 6.X ä»¥ä¸Šä½¿ç”¨ setAndKeepTTL å…¼å®¹ 5.X æ–¹æ¡ˆ
     */
    public static <T> void setCacheObject(final String key, final T value, final boolean isSaveTtl) {
        RBucket<T> bucket = CLIENT.getBucket(key);
        if (isSaveTtl) {
            try {
                bucket.setAndKeepTTL(value);
            } catch (Exception e) {
                long timeToLive = bucket.remainTimeToLive();
                setCacheObject(key, value, Duration.ofMillis(timeToLive));
            }
        } else {
            bucket.set(value);
        }
    }
    /**
     * ç¼“存基本的对象,Integer、String、实体类等
     *
     * @param key      ç¼“存的键值
     * @param value    ç¼“存的值
     * @param duration æ—¶é—´
     */
    public static <T> void setCacheObject(final String key, final T value, final Duration duration) {
        RBatch batch = CLIENT.createBatch();
        RBucketAsync<T> bucket = batch.getBucket(key);
        bucket.setAsync(value);
        bucket.expireAsync(duration);
        batch.execute();
    }
    /**
     * æ³¨å†Œå¯¹è±¡ç›‘听器
     * <p>
     * key ç›‘听器需开启 `notify-keyspace-events` ç­‰ redis ç›¸å…³é…ç½®
     *
     * @param key      ç¼“存的键值
     * @param listener ç›‘听器配置
     */
    public static <T> void addObjectListener(final String key, final ObjectListener listener) {
        RBucket<T> result = CLIENT.getBucket(key);
        result.addListener(listener);
    }
    /**
     * è®¾ç½®æœ‰æ•ˆæ—¶é—´
     *
     * @param key     Redis键
     * @param timeout è¶…æ—¶æ—¶é—´
     * @return true=设置成功;false=设置失败
     */
    public static boolean expire(final String key, final long timeout) {
        return expire(key, Duration.ofSeconds(timeout));
    }
    /**
     * è®¾ç½®æœ‰æ•ˆæ—¶é—´
     *
     * @param key      Redis键
     * @param duration è¶…æ—¶æ—¶é—´
     * @return true=设置成功;false=设置失败
     */
    public static boolean expire(final String key, final Duration duration) {
        RBucket rBucket = CLIENT.getBucket(key);
        return rBucket.expire(duration);
    }
    /**
     * èŽ·å¾—ç¼“å­˜çš„åŸºæœ¬å¯¹è±¡ã€‚
     *
     * @param key ç¼“存键值
     * @return ç¼“存键值对应的数据
     */
    public static <T> T getCacheObject(final String key) {
        RBucket<T> rBucket = CLIENT.getBucket(key);
        return rBucket.get();
    }
    /**
     * èŽ·å¾—key剩余存活时间
     *
     * @param key ç¼“存键值
     * @return å‰©ä½™å­˜æ´»æ—¶é—´
     */
    public static <T> long getTimeToLive(final String key) {
        RBucket<T> rBucket = CLIENT.getBucket(key);
        return rBucket.remainTimeToLive();
    }
    /**
     * åˆ é™¤å•个对象
     *
     * @param key ç¼“存的键值
     */
    public static boolean deleteObject(final String key) {
        return CLIENT.getBucket(key).delete();
    }
    /**
     * åˆ é™¤é›†åˆå¯¹è±¡
     *
     * @param collection å¤šä¸ªå¯¹è±¡
     */
    public static void deleteObject(final Collection collection) {
        RBatch batch = CLIENT.createBatch();
        collection.forEach(t -> {
            batch.getBucket(t.toString()).deleteAsync();
        });
        batch.execute();
    }
    /**
     * æ£€æŸ¥ç¼“存对象是否存在
     *
     * @param key ç¼“存的键值
     */
    public static boolean isExistsObject(final String key) {
        return CLIENT.getBucket(key).isExists();
    }
    /**
     * ç¼“å­˜List数据
     *
     * @param key      ç¼“存的键值
     * @param dataList å¾…缓存的List数据
     * @return ç¼“存的对象
     */
    public static <T> boolean setCacheList(final String key, final List<T> dataList) {
        RList<T> rList = CLIENT.getList(key);
        return rList.addAll(dataList);
    }
    /**
     * æ³¨å†ŒList监听器
     * <p>
     * key ç›‘听器需开启 `notify-keyspace-events` ç­‰ redis ç›¸å…³é…ç½®
     *
     * @param key      ç¼“存的键值
     * @param listener ç›‘听器配置
     */
    public static <T> void addListListener(final String key, final ObjectListener listener) {
        RList<T> rList = CLIENT.getList(key);
        rList.addListener(listener);
    }
    /**
     * èŽ·å¾—ç¼“å­˜çš„list对象
     *
     * @param key ç¼“存的键值
     * @return ç¼“存键值对应的数据
     */
    public static <T> List<T> getCacheList(final String key) {
        RList<T> rList = CLIENT.getList(key);
        return rList.readAll();
    }
    /**
     * ç¼“å­˜Set
     *
     * @param key     ç¼“存键值
     * @param dataSet ç¼“存的数据
     * @return ç¼“存数据的对象
     */
    public static <T> boolean setCacheSet(final String key, final Set<T> dataSet) {
        RSet<T> rSet = CLIENT.getSet(key);
        return rSet.addAll(dataSet);
    }
    /**
     * æ³¨å†ŒSet监听器
     * <p>
     * key ç›‘听器需开启 `notify-keyspace-events` ç­‰ redis ç›¸å…³é…ç½®
     *
     * @param key      ç¼“存的键值
     * @param listener ç›‘听器配置
     */
    public static <T> void addSetListener(final String key, final ObjectListener listener) {
        RSet<T> rSet = CLIENT.getSet(key);
        rSet.addListener(listener);
    }
    /**
     * èŽ·å¾—ç¼“å­˜çš„set
     *
     * @param key ç¼“存的key
     * @return set对象
     */
    public static <T> Set<T> getCacheSet(final String key) {
        RSet<T> rSet = CLIENT.getSet(key);
        return rSet.readAll();
    }
    /**
     * ç¼“å­˜Map
     *
     * @param key     ç¼“存的键值
     * @param dataMap ç¼“存的数据
     */
    public static <T> void setCacheMap(final String key, final Map<String, T> dataMap) {
        if (dataMap != null) {
            RMap<String, T> rMap = CLIENT.getMap(key);
            rMap.putAll(dataMap);
        }
    }
    /**
     * æ³¨å†ŒMap监听器
     * <p>
     * key ç›‘听器需开启 `notify-keyspace-events` ç­‰ redis ç›¸å…³é…ç½®
     *
     * @param key      ç¼“存的键值
     * @param listener ç›‘听器配置
     */
    public static <T> void addMapListener(final String key, final ObjectListener listener) {
        RMap<String, T> rMap = CLIENT.getMap(key);
        rMap.addListener(listener);
    }
    /**
     * èŽ·å¾—ç¼“å­˜çš„Map
     *
     * @param key ç¼“存的键值
     * @return map对象
     */
    public static <T> Map<String, T> getCacheMap(final String key) {
        RMap<String, T> rMap = CLIENT.getMap(key);
        return rMap.getAll(rMap.keySet());
    }
    /**
     * èŽ·å¾—ç¼“å­˜Map的key列表
     *
     * @param key ç¼“存的键值
     * @return key列表
     */
    public static <T> Set<String> getCacheMapKeySet(final String key) {
        RMap<String, T> rMap = CLIENT.getMap(key);
        return rMap.keySet();
    }
    /**
     * å¾€Hash中存入数据
     *
     * @param key   Redis键
     * @param hKey  Hash键
     * @param value å€¼
     */
    public static <T> void setCacheMapValue(final String key, final String hKey, final T value) {
        RMap<String, T> rMap = CLIENT.getMap(key);
        rMap.put(hKey, value);
    }
    /**
     * èŽ·å–Hash中的数据
     *
     * @param key  Redis键
     * @param hKey Hash键
     * @return Hash中的对象
     */
    public static <T> T getCacheMapValue(final String key, final String hKey) {
        RMap<String, T> rMap = CLIENT.getMap(key);
        return rMap.get(hKey);
    }
    /**
     * åˆ é™¤Hash中的数据
     *
     * @param key  Redis键
     * @param hKey Hash键
     * @return Hash中的对象
     */
    public static <T> T delCacheMapValue(final String key, final String hKey) {
        RMap<String, T> rMap = CLIENT.getMap(key);
        return rMap.remove(hKey);
    }
    /**
     * èŽ·å–å¤šä¸ªHash中的数据
     *
     * @param key   Redis键
     * @param hKeys Hash键集合
     * @return Hash对象集合
     */
    public static <K, V> Map<K, V> getMultiCacheMapValue(final String key, final Set<K> hKeys) {
        RMap<K, V> rMap = CLIENT.getMap(key);
        return rMap.getAll(hKeys);
    }
    /**
     * è®¾ç½®åŽŸå­å€¼
     *
     * @param key   Redis键
     * @param value å€¼
     */
    public static void setAtomicValue(String key, long value) {
        RAtomicLong atomic = CLIENT.getAtomicLong(key);
        atomic.set(value);
    }
    /**
     * èŽ·å–åŽŸå­å€¼
     *
     * @param key Redis键
     * @return å½“前值
     */
    public static long getAtomicValue(String key) {
        RAtomicLong atomic = CLIENT.getAtomicLong(key);
        return atomic.get();
    }
    /**
     * é€’增原子值
     *
     * @param key Redis键
     * @return å½“前值
     */
    public static long incrAtomicValue(String key) {
        RAtomicLong atomic = CLIENT.getAtomicLong(key);
        return atomic.incrementAndGet();
    }
    /**
     * é€’减原子值
     *
     * @param key Redis键
     * @return å½“前值
     */
    public static long decrAtomicValue(String key) {
        RAtomicLong atomic = CLIENT.getAtomicLong(key);
        return atomic.decrementAndGet();
    }
    /**
     * èŽ·å¾—ç¼“å­˜çš„åŸºæœ¬å¯¹è±¡åˆ—è¡¨
     *
     * @param pattern å­—符串前缀
     * @return å¯¹è±¡åˆ—表
     */
    public static Collection<String> keys(final String pattern) {
        Stream<String> stream = CLIENT.getKeys().getKeysStreamByPattern(pattern);
        return stream.collect(Collectors.toList());
    }
    /**
     * åˆ é™¤ç¼“存的基本对象列表
     *
     * @param pattern å­—符串前缀
     */
    public static void deleteKeys(final String pattern) {
        CLIENT.getKeys().deleteByPattern(pattern);
    }
    /**
     * æ£€æŸ¥redis中是否存在key
     *
     * @param key é”®
     */
    public static Boolean hasKey(String key) {
        RKeys rKeys = CLIENT.getKeys();
        return rKeys.countExists(key) > 0;
    }
}
ruoyi-common/ruoyi-common-redis/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1 @@
com.ruoyi.common.redis.config.RedisConfig
ruoyi-common/ruoyi-common-satoken/pom.xml
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,39 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <groupId>com.ruoyi</groupId>
        <artifactId>ruoyi-common</artifactId>
        <version>${revision}</version>
        <relativePath>../pom.xml</relativePath>
    </parent>
    <modelVersion>4.0.0</modelVersion>
    <artifactId>ruoyi-common-satoken</artifactId>
    <dependencies>
        <dependency>
            <groupId>com.ruoyi</groupId>
            <artifactId>ruoyi-common-core</artifactId>
        </dependency>
        <!-- RuoYi Common Redis-->
        <dependency>
            <groupId>com.ruoyi</groupId>
            <artifactId>ruoyi-common-redis</artifactId>
        </dependency>
        <!-- Sa-Token æƒé™è®¤è¯, åœ¨çº¿æ–‡æ¡£ï¼šhttp://sa-token.dev33.cn/ -->
        <dependency>
            <groupId>cn.dev33</groupId>
            <artifactId>sa-token-spring-boot3-starter</artifactId>
        </dependency>
        <!-- Sa-Token æ•´åˆ jwt -->
        <dependency>
            <groupId>cn.dev33</groupId>
            <artifactId>sa-token-jwt</artifactId>
        </dependency>
    </dependencies>
</project>
ruoyi-common/ruoyi-common-satoken/src/main/java/com/ruoyi/common/satoken/config/SaTokenConfig.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,23 @@
package com.ruoyi.common.satoken.config;
import cn.dev33.satoken.jwt.StpLogicJwtForSimple;
import cn.dev33.satoken.stp.StpLogic;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/**
 * sa-token é…ç½®
 *
 * @author Lion Li
 */
@AutoConfiguration
public class SaTokenConfig implements WebMvcConfigurer {
    @Bean
    public StpLogic getStpLogicJwt() {
        // Sa-Token æ•´åˆ jwt (简单模式)
        return new StpLogicJwtForSimple();
    }
}
ruoyi-common/ruoyi-common-satoken/src/main/java/com/ruoyi/common/satoken/core/dao/PlusSaTokenDao.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,178 @@
package com.ruoyi.common.satoken.core.dao;
import cn.dev33.satoken.dao.SaTokenDao;
import cn.dev33.satoken.util.SaFoxUtil;
import com.ruoyi.common.redis.utils.RedisUtils;
import org.springframework.stereotype.Component;
import java.time.Duration;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
/**
 * Sa-Token持久层接口(使用框架自带RedisUtils实现 åè®®ç»Ÿä¸€)
 *
 * @author Lion Li
 */
@Component
public class PlusSaTokenDao implements SaTokenDao {
    /**
     * èŽ·å–Value,如无返空
     */
    @Override
    public String get(String key) {
        return RedisUtils.getCacheObject(key);
    }
    /**
     * å†™å…¥Value,并设定存活时间 (单位: ç§’)
     */
    @Override
    public void set(String key, String value, long timeout) {
        if (timeout == 0 || timeout <= NOT_VALUE_EXPIRE) {
            return;
        }
        // åˆ¤æ–­æ˜¯å¦ä¸ºæ°¸ä¸è¿‡æœŸ
        if (timeout == NEVER_EXPIRE) {
            RedisUtils.setCacheObject(key, value);
        } else {
            RedisUtils.setCacheObject(key, value, Duration.ofSeconds(timeout));
        }
    }
    /**
     * ä¿®ä¿®æ”¹æŒ‡å®škey-value键值对 (过期时间不变)
     */
    @Override
    public void update(String key, String value) {
        long expire = getTimeout(key);
        // -2 = æ— æ­¤é”®
        if (expire == NOT_VALUE_EXPIRE) {
            return;
        }
        this.set(key, value, expire);
    }
    /**
     * åˆ é™¤Value
     */
    @Override
    public void delete(String key) {
        RedisUtils.deleteObject(key);
    }
    /**
     * èŽ·å–Value的剩余存活时间 (单位: ç§’)
     */
    @Override
    public long getTimeout(String key) {
        long timeout = RedisUtils.getTimeToLive(key);
        return timeout < 0 ? timeout : timeout / 1000;
    }
    /**
     * ä¿®æ”¹Value的剩余存活时间 (单位: ç§’)
     */
    @Override
    public void updateTimeout(String key, long timeout) {
        // åˆ¤æ–­æ˜¯å¦æƒ³è¦è®¾ç½®ä¸ºæ°¸ä¹…
        if (timeout == NEVER_EXPIRE) {
            long expire = getTimeout(key);
            if (expire == NEVER_EXPIRE) {
                // å¦‚果其已经被设置为永久,则不作任何处理
            } else {
                // å¦‚果尚未被设置为永久,那么再次set一次
                this.set(key, this.get(key), timeout);
            }
            return;
        }
        RedisUtils.expire(key, Duration.ofSeconds(timeout));
    }
    /**
     * èŽ·å–Object,如无返空
     */
    @Override
    public Object getObject(String key) {
        return RedisUtils.getCacheObject(key);
    }
    /**
     * å†™å…¥Object,并设定存活时间 (单位: ç§’)
     */
    @Override
    public void setObject(String key, Object object, long timeout) {
        if (timeout == 0 || timeout <= NOT_VALUE_EXPIRE) {
            return;
        }
        // åˆ¤æ–­æ˜¯å¦ä¸ºæ°¸ä¸è¿‡æœŸ
        if (timeout == NEVER_EXPIRE) {
            RedisUtils.setCacheObject(key, object);
        } else {
            RedisUtils.setCacheObject(key, object, Duration.ofSeconds(timeout));
        }
    }
    /**
     * æ›´æ–°Object (过期时间不变)
     */
    @Override
    public void updateObject(String key, Object object) {
        long expire = getObjectTimeout(key);
        // -2 = æ— æ­¤é”®
        if (expire == NOT_VALUE_EXPIRE) {
            return;
        }
        this.setObject(key, object, expire);
    }
    /**
     * åˆ é™¤Object
     */
    @Override
    public void deleteObject(String key) {
        RedisUtils.deleteObject(key);
    }
    /**
     * èŽ·å–Object的剩余存活时间 (单位: ç§’)
     */
    @Override
    public long getObjectTimeout(String key) {
        long timeout = RedisUtils.getTimeToLive(key);
        return timeout < 0 ? timeout : timeout / 1000;
    }
    /**
     * ä¿®æ”¹Object的剩余存活时间 (单位: ç§’)
     */
    @Override
    public void updateObjectTimeout(String key, long timeout) {
        // åˆ¤æ–­æ˜¯å¦æƒ³è¦è®¾ç½®ä¸ºæ°¸ä¹…
        if (timeout == NEVER_EXPIRE) {
            long expire = getObjectTimeout(key);
            if (expire == NEVER_EXPIRE) {
                // å¦‚果其已经被设置为永久,则不作任何处理
            } else {
                // å¦‚果尚未被设置为永久,那么再次set一次
                this.setObject(key, this.getObject(key), timeout);
            }
            return;
        }
        RedisUtils.expire(key, Duration.ofSeconds(timeout));
    }
    /**
     * æœç´¢æ•°æ®
     */
    @Override
    public List<String> searchData(String prefix, String keyword, int start, int size, boolean sortType) {
        Collection<String> keys = RedisUtils.keys(prefix + "*" + keyword + "*");
        List<String> list = new ArrayList<>(keys);
        return SaFoxUtil.searchList(list, start, size, sortType);
    }
}
ruoyi-common/ruoyi-common-satoken/src/main/java/com/ruoyi/common/satoken/core/service/SaPermissionImpl.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,49 @@
package com.ruoyi.common.satoken.core.service;
import cn.dev33.satoken.stp.StpInterface;
import com.ruoyi.common.core.domain.model.LoginUser;
import com.ruoyi.common.core.enums.UserType;
import com.ruoyi.common.satoken.utils.LoginHelper;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.List;
/**
 * sa-token æƒé™ç®¡ç†å®žçŽ°ç±»
 *
 * @author Lion Li
 */
@Component
public class SaPermissionImpl implements StpInterface {
    /**
     * èŽ·å–èœå•æƒé™åˆ—è¡¨
     */
    @Override
    public List<String> getPermissionList(Object loginId, String loginType) {
        LoginUser loginUser = LoginHelper.getLoginUser();
        UserType userType = UserType.getUserType(loginUser.getUserType());
        if (userType == UserType.SYS_USER) {
            return new ArrayList<>(loginUser.getMenuPermission());
        } else if (userType == UserType.APP_USER) {
            // å…¶ä»–端 è‡ªè¡Œæ ¹æ®ä¸šåŠ¡ç¼–å†™
        }
        return new ArrayList<>();
    }
    /**
     * èŽ·å–è§’è‰²æƒé™åˆ—è¡¨
     */
    @Override
    public List<String> getRoleList(Object loginId, String loginType) {
        LoginUser loginUser = LoginHelper.getLoginUser();
        UserType userType = UserType.getUserType(loginUser.getUserType());
        if (userType == UserType.SYS_USER) {
            return new ArrayList<>(loginUser.getRolePermission());
        } else if (userType == UserType.APP_USER) {
            // å…¶ä»–端 è‡ªè¡Œæ ¹æ®ä¸šåŠ¡ç¼–å†™
        }
        return new ArrayList<>();
    }
}
ruoyi-common/ruoyi-common-satoken/src/main/java/com/ruoyi/common/satoken/listener/UserActionListener.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,139 @@
package com.ruoyi.common.satoken.listener;
import cn.dev33.satoken.config.SaTokenConfig;
import cn.dev33.satoken.listener.SaTokenListener;
import cn.dev33.satoken.stp.SaLoginModel;
import cn.hutool.http.useragent.UserAgent;
import cn.hutool.http.useragent.UserAgentUtil;
import com.ruoyi.common.core.constant.CacheConstants;
import com.ruoyi.common.core.domain.dto.UserOnlineDTO;
import com.ruoyi.common.core.domain.model.LoginUser;
import com.ruoyi.common.core.enums.UserType;
import com.ruoyi.common.core.utils.ServletUtils;
import com.ruoyi.common.core.utils.ip.AddressUtils;
import com.ruoyi.common.redis.utils.RedisUtils;
import com.ruoyi.common.satoken.utils.LoginHelper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import java.time.Duration;
/**
 * ç”¨æˆ·è¡Œä¸º ä¾¦å¬å™¨çš„实现
 *
 * @author Lion Li
 */
@RequiredArgsConstructor
@Component
@Slf4j
public class UserActionListener implements SaTokenListener {
    private final SaTokenConfig tokenConfig;
    /**
     * æ¯æ¬¡ç™»å½•时触发
     */
    @Override
    public void doLogin(String loginType, Object loginId, String tokenValue, SaLoginModel loginModel) {
        UserType userType = UserType.getUserType(loginId.toString());
        if (userType == UserType.SYS_USER) {
            UserAgent userAgent = UserAgentUtil.parse(ServletUtils.getRequest().getHeader("User-Agent"));
            String ip = ServletUtils.getClientIP();
            LoginUser user = LoginHelper.getLoginUser();
            UserOnlineDTO dto = new UserOnlineDTO();
            dto.setIpaddr(ip);
            dto.setLoginLocation(AddressUtils.getRealAddressByIP(ip));
            dto.setBrowser(userAgent.getBrowser().getName());
            dto.setOs(userAgent.getOs().getName());
            dto.setLoginTime(System.currentTimeMillis());
            dto.setTokenId(tokenValue);
            dto.setUserName(user.getUsername());
            dto.setDeptName(user.getDeptName());
            if(tokenConfig.getTimeout() == -1) {
                RedisUtils.setCacheObject(CacheConstants.ONLINE_TOKEN_KEY + tokenValue, dto);
            } else {
                RedisUtils.setCacheObject(CacheConstants.ONLINE_TOKEN_KEY + tokenValue, dto, Duration.ofSeconds(tokenConfig.getTimeout()));
            }
            log.info("user doLogin, userId:{}, token:{}", loginId, tokenValue);
        } else if (userType == UserType.APP_USER) {
            // app端 è‡ªè¡Œæ ¹æ®ä¸šåŠ¡ç¼–å†™
        }
    }
    /**
     * æ¯æ¬¡æ³¨é”€æ—¶è§¦å‘
     */
    @Override
    public void doLogout(String loginType, Object loginId, String tokenValue) {
        RedisUtils.deleteObject(CacheConstants.ONLINE_TOKEN_KEY + tokenValue);
        log.info("user doLogout, userId:{}, token:{}", loginId, tokenValue);
    }
    /**
     * æ¯æ¬¡è¢«è¸¢ä¸‹çº¿æ—¶è§¦å‘
     */
    @Override
    public void doKickout(String loginType, Object loginId, String tokenValue) {
        RedisUtils.deleteObject(CacheConstants.ONLINE_TOKEN_KEY + tokenValue);
        log.info("user doLogoutByLoginId, userId:{}, token:{}", loginId, tokenValue);
    }
    /**
     * æ¯æ¬¡è¢«é¡¶ä¸‹çº¿æ—¶è§¦å‘
     */
    @Override
    public void doReplaced(String loginType, Object loginId, String tokenValue) {
        RedisUtils.deleteObject(CacheConstants.ONLINE_TOKEN_KEY + tokenValue);
        log.info("user doReplaced, userId:{}, token:{}", loginId, tokenValue);
    }
    /**
     * æ¯æ¬¡è¢«å°ç¦æ—¶è§¦å‘
     */
    @Override
    public void doDisable(String loginType, Object loginId, String service, int level, long disableTime) {
    }
    /**
     * æ¯æ¬¡è¢«è§£å°æ—¶è§¦å‘
     */
    @Override
    public void doUntieDisable(String loginType, Object loginId, String service) {
    }
    /**
     * æ¯æ¬¡æ‰“开二级认证时触发
     */
    @Override
    public void doOpenSafe(String loginType, String tokenValue, String service, long safeTime) {
    }
    /**
     * æ¯æ¬¡åˆ›å»ºSession时触发
     */
    @Override
    public void doCloseSafe(String loginType, String tokenValue, String service) {
    }
    /**
     * æ¯æ¬¡åˆ›å»ºSession时触发
     */
    @Override
    public void doCreateSession(String id) {
    }
    /**
     * æ¯æ¬¡æ³¨é”€Session时触发
     */
    @Override
    public void doLogoutSession(String id) {
    }
    /**
     * æ¯æ¬¡Token续期时触发
     */
    @Override
    public void doRenewTimeout(String tokenValue, Object loginId, long timeout) {
    }
}
ruoyi-common/ruoyi-common-satoken/src/main/java/com/ruoyi/common/satoken/utils/LoginHelper.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,135 @@
package com.ruoyi.common.satoken.utils;
import cn.dev33.satoken.context.SaHolder;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.util.ObjectUtil;
import com.ruoyi.common.core.constant.UserConstants;
import com.ruoyi.common.core.domain.model.LoginUser;
import com.ruoyi.common.core.enums.DeviceType;
import com.ruoyi.common.core.enums.UserType;
import com.ruoyi.common.core.exception.UtilException;
import com.ruoyi.common.core.utils.StringUtils;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
/**
 * ç™»å½•鉴权助手
 *
 * user_type ä¸º ç”¨æˆ·ç±»åž‹ åŒä¸€ä¸ªç”¨æˆ·è¡¨ å¯ä»¥æœ‰å¤šç§ç”¨æˆ·ç±»åž‹ ä¾‹å¦‚ pc,app
 * deivce ä¸º è®¾å¤‡ç±»åž‹ åŒä¸€ä¸ªç”¨æˆ·ç±»åž‹ å¯ä»¥æœ‰ å¤šç§è®¾å¤‡ç±»åž‹ ä¾‹å¦‚ web,ios
 * å¯ä»¥ç»„成 ç”¨æˆ·ç±»åž‹ä¸Žè®¾å¤‡ç±»åž‹å¤šå¯¹å¤šçš„ æƒé™çµæ´»æŽ§åˆ¶
 *
 * å¤šç”¨æˆ·ä½“ç³» é’ˆå¯¹ å¤šç§ç”¨æˆ·ç±»åž‹ ä½†æƒé™æŽ§åˆ¶ä¸ä¸€è‡´
 * å¯ä»¥ç»„成 å¤šç”¨æˆ·ç±»åž‹è¡¨ä¸Žå¤šè®¾å¤‡ç±»åž‹ åˆ†åˆ«æŽ§åˆ¶æƒé™
 *
 * @author Lion Li
 */
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class LoginHelper {
    public static final String JOIN_CODE = ":";
    public static final String LOGIN_USER_KEY = "loginUser";
    /**
     * ç™»å½•系统
     *
     * @param loginUser ç™»å½•用户信息
     */
    public static void login(LoginUser loginUser) {
        SaHolder.getStorage().set(LOGIN_USER_KEY, loginUser);
        StpUtil.login(loginUser.getLoginId());
        setLoginUser(loginUser);
    }
    /**
     * ç™»å½•系统 åŸºäºŽ è®¾å¤‡ç±»åž‹
     * é’ˆå¯¹ç›¸åŒç”¨æˆ·ä½“系不同设备
     *
     * @param loginUser ç™»å½•用户信息
     */
    public static void loginByDevice(LoginUser loginUser, DeviceType deviceType) {
        SaHolder.getStorage().set(LOGIN_USER_KEY, loginUser);
        StpUtil.login(loginUser.getLoginId(), deviceType.getDevice());
        setLoginUser(loginUser);
    }
    /**
     * è®¾ç½®ç”¨æˆ·æ•°æ®(多级缓存)
     */
    public static void setLoginUser(LoginUser loginUser) {
        StpUtil.getTokenSession().set(LOGIN_USER_KEY, loginUser);
    }
    /**
     * èŽ·å–ç”¨æˆ·(多级缓存)
     */
    public static LoginUser getLoginUser() {
        LoginUser loginUser = (LoginUser) SaHolder.getStorage().get(LOGIN_USER_KEY);
        if (loginUser != null) {
            return loginUser;
        }
        loginUser = (LoginUser) StpUtil.getTokenSession().get(LOGIN_USER_KEY);
        SaHolder.getStorage().set(LOGIN_USER_KEY, loginUser);
        return loginUser;
    }
    /**
     * èŽ·å–ç”¨æˆ·id
     */
    public static Long getUserId() {
        LoginUser loginUser = getLoginUser();
        if (ObjectUtil.isNull(loginUser)) {
            String loginId = StpUtil.getLoginIdAsString();
            String userId = null;
            for (UserType value : UserType.values()) {
                if (StringUtils.contains(loginId, value.getUserType())) {
                    String[] strs = StringUtils.split(loginId, JOIN_CODE);
                    // ç”¨æˆ·id在总是在最后
                    userId = strs[strs.length - 1];
                }
            }
            if (StringUtils.isBlank(userId)) {
                throw new UtilException("登录用户: LoginId异常 => " + loginId);
            }
            return Long.parseLong(userId);
        }
        return loginUser.getUserId();
    }
    /**
     * èŽ·å–éƒ¨é—¨ID
     */
    public static Long getDeptId() {
        return getLoginUser().getDeptId();
    }
    /**
     * èŽ·å–ç”¨æˆ·è´¦æˆ·
     */
    public static String getUsername() {
        return getLoginUser().getUsername();
    }
    /**
     * èŽ·å–ç”¨æˆ·ç±»åž‹
     */
    public static UserType getUserType() {
        String loginId = StpUtil.getLoginIdAsString();
        return UserType.getUserType(loginId);
    }
    /**
     * æ˜¯å¦ä¸ºç®¡ç†å‘˜
     *
     * @param userId ç”¨æˆ·ID
     * @return ç»“æžœ
     */
    public static boolean isAdmin(Long userId) {
        return UserConstants.ADMIN_ID.equals(userId);
    }
    public static boolean isAdmin() {
        return isAdmin(getUserId());
    }
}
ruoyi-common/ruoyi-common-satoken/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,2 @@
com.ruoyi.common.satoken.core.dao.PlusSaTokenDao
com.ruoyi.common.satoken.core.service.SaPermissionImpl
ruoyi-common/ruoyi-common-security/pom.xml
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,33 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <groupId>com.ruoyi</groupId>
        <artifactId>ruoyi-common</artifactId>
        <version>${revision}</version>
        <relativePath>../pom.xml</relativePath>
    </parent>
    <modelVersion>4.0.0</modelVersion>
    <artifactId>ruoyi-common-security</artifactId>
    <description>
        ruoyi-common-security å®‰å…¨æ¨¡å—
    </description>
    <dependencies>
        <dependency>
            <groupId>com.ruoyi</groupId>
            <artifactId>ruoyi-common-satoken</artifactId>
        </dependency>
        <!-- Sa-Token æƒé™è®¤è¯, åœ¨çº¿æ–‡æ¡£ï¼šhttp://sa-token.dev33.cn/ -->
        <dependency>
            <groupId>cn.dev33</groupId>
            <artifactId>sa-token-spring-boot3-starter</artifactId>
            <version>${satoken.version}</version>
        </dependency>
    </dependencies>
</project>
ruoyi-common/ruoyi-common-security/src/main/java/com/ruoyi/common/security/config/SecurityConfig.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,53 @@
package com.ruoyi.common.security.config;
import cn.dev33.satoken.interceptor.SaInterceptor;
import cn.dev33.satoken.router.SaRouter;
import cn.dev33.satoken.stp.StpUtil;
import com.ruoyi.common.security.config.properties.SecurityProperties;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/**
 * æƒé™å®‰å…¨é…ç½®
 *
 * @author Lion Li
 */
@RequiredArgsConstructor
@Slf4j
@AutoConfiguration
public class SecurityConfig implements WebMvcConfigurer {
    private final SecurityProperties securityProperties;
    /**
     * æ³¨å†Œsa-token的拦截器
     */
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        // æ³¨å†Œè·¯ç”±æ‹¦æˆªå™¨ï¼Œè‡ªå®šä¹‰éªŒè¯è§„则
        registry.addInterceptor(new SaInterceptor(handler -> {
            // ç™»å½•验证 -- æŽ’除多个路径
            SaRouter
                // èŽ·å–æ‰€æœ‰çš„
                .match("/**")
                // å¯¹æœªæŽ’除的路径进行检查
                .check(() -> {
                    // æ£€æŸ¥æ˜¯å¦ç™»å½• æ˜¯å¦æœ‰token
                    StpUtil.checkLogin();
                    // æœ‰æ•ˆçŽ‡å½±å“ ç”¨äºŽä¸´æ—¶æµ‹è¯•
                    // if (log.isDebugEnabled()) {
                    //     log.debug("剩余有效时间: {}", StpUtil.getTokenTimeout());
                    //     log.debug("临时有效时间: {}", StpUtil.getTokenActivityTimeout());
                    // }
                });
        })).addPathPatterns("/**")
            // æŽ’除不需要拦截的路径
            .excludePathPatterns(securityProperties.getExcludes());
    }
}
ruoyi-common/ruoyi-common-security/src/main/java/com/ruoyi/common/security/config/properties/SecurityProperties.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,23 @@
package com.ruoyi.common.security.config.properties;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
/**
 * Security é…ç½®å±žæ€§
 *
 * @author Lion Li
 */
@Data
@Component
@ConfigurationProperties(prefix = "security")
public class SecurityProperties {
    /**
     * æŽ’除路径
     */
    private String[] excludes;
}
ruoyi-common/ruoyi-common-security/src/main/java/com/ruoyi/common/security/handler/GlobalExceptionHandler.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,141 @@
package com.ruoyi.common.security.handler;
import cn.dev33.satoken.exception.NotLoginException;
import cn.dev33.satoken.exception.NotPermissionException;
import cn.dev33.satoken.exception.NotRoleException;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.http.HttpStatus;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.core.exception.DemoModeException;
import com.ruoyi.common.core.exception.ServiceException;
import com.ruoyi.common.core.utils.StreamUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.support.DefaultMessageSourceResolvable;
import org.springframework.validation.BindException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.validation.ConstraintViolation;
import jakarta.validation.ConstraintViolationException;
/**
 * å…¨å±€å¼‚常处理器
 *
 * @author Lion Li
 */
@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {
    /**
     * æƒé™ç å¼‚常
     */
    @ExceptionHandler(NotPermissionException.class)
    public R<Void> handleNotPermissionException(NotPermissionException e, HttpServletRequest request) {
        String requestURI = request.getRequestURI();
        log.error("请求地址'{}',权限码校验失败'{}'", requestURI, e.getMessage());
        return R.fail(HttpStatus.HTTP_FORBIDDEN, "没有访问权限,请联系管理员授权");
    }
    /**
     * è§’色权限异常
     */
    @ExceptionHandler(NotRoleException.class)
    public R<Void> handleNotRoleException(NotRoleException e, HttpServletRequest request) {
        String requestURI = request.getRequestURI();
        log.error("请求地址'{}',角色权限校验失败'{}'", requestURI, e.getMessage());
        return R.fail(HttpStatus.HTTP_FORBIDDEN, "没有访问权限,请联系管理员授权");
    }
    /**
     * è®¤è¯å¤±è´¥
     */
    @ExceptionHandler(NotLoginException.class)
    public R<Void> handleNotLoginException(NotLoginException e, HttpServletRequest request) {
        String requestURI = request.getRequestURI();
        log.error("请求地址'{}',认证失败'{}',无法访问系统资源", requestURI, e.getMessage());
        return R.fail(HttpStatus.HTTP_UNAUTHORIZED, "认证失败,无法访问系统资源");
    }
    /**
     * è¯·æ±‚方式不支持
     */
    @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
    public R<Void> handleHttpRequestMethodNotSupported(HttpRequestMethodNotSupportedException e,
                                                                HttpServletRequest request) {
        String requestURI = request.getRequestURI();
        log.error("请求地址'{}',不支持'{}'请求", requestURI, e.getMethod());
        return R.fail(e.getMessage());
    }
    /**
     * ä¸šåС异叏
     */
    @ExceptionHandler(ServiceException.class)
    public R<Void> handleServiceException(ServiceException e, HttpServletRequest request) {
        log.error(e.getMessage(), e);
        Integer code = e.getCode();
        return ObjectUtil.isNotNull(code) ? R.fail(code, e.getMessage()) : R.fail(e.getMessage());
    }
    /**
     * æ‹¦æˆªæœªçŸ¥çš„运行时异常
     */
    @ExceptionHandler(RuntimeException.class)
    public R<Void> handleRuntimeException(RuntimeException e, HttpServletRequest request) {
        String requestURI = request.getRequestURI();
        log.error("请求地址'{}',发生未知异常.", requestURI, e);
        return R.fail(e.getMessage());
    }
    /**
     * ç³»ç»Ÿå¼‚常
     */
    @ExceptionHandler(Exception.class)
    public R<Void> handleException(Exception e, HttpServletRequest request) {
        String requestURI = request.getRequestURI();
        log.error("请求地址'{}',发生系统异常.", requestURI, e);
        return R.fail(e.getMessage());
    }
    /**
     * è‡ªå®šä¹‰éªŒè¯å¼‚常
     */
    @ExceptionHandler(BindException.class)
    public R<Void> handleBindException(BindException e) {
        log.error(e.getMessage(), e);
        String message = StreamUtils.join(e.getAllErrors(), DefaultMessageSourceResolvable::getDefaultMessage, ", ");
        return R.fail(message);
    }
    /**
     * è‡ªå®šä¹‰éªŒè¯å¼‚常
     */
    @ExceptionHandler(ConstraintViolationException.class)
    public R<Void> constraintViolationException(ConstraintViolationException e) {
        log.error(e.getMessage(), e);
        String message = StreamUtils.join(e.getConstraintViolations(), ConstraintViolation::getMessage, ", ");
        return R.fail(message);
    }
    /**
     * è‡ªå®šä¹‰éªŒè¯å¼‚常
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public R<Void> handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {
        log.error(e.getMessage(), e);
        String message = e.getBindingResult().getFieldError().getDefaultMessage();
        return R.fail(message);
    }
    /**
     * æ¼”示模式异常
     */
    @ExceptionHandler(DemoModeException.class)
    public R<Void> handleDemoModeException(DemoModeException e) {
        return R.fail("演示模式,不允许操作");
    }
}
ruoyi-common/ruoyi-common-security/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,2 @@
com.ruoyi.common.security.handler.GlobalExceptionHandler
com.ruoyi.common.security.config.SecurityConfig
ruoyi-common/ruoyi-common-sms/pom.xml
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,37 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <groupId>com.ruoyi</groupId>
        <artifactId>ruoyi-common</artifactId>
        <version>${revision}</version>
        <relativePath>../pom.xml</relativePath>
    </parent>
    <modelVersion>4.0.0</modelVersion>
    <artifactId>ruoyi-common-sms</artifactId>
    <description>
        ruoyi-common-sms çŸ­ä¿¡æ¨¡å—
    </description>
    <dependencies>
        <dependency>
            <groupId>com.ruoyi</groupId>
            <artifactId>ruoyi-common-core</artifactId>
        </dependency>
        <dependency>
            <groupId>com.aliyun</groupId>
            <artifactId>dysmsapi20170525</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>com.tencentcloudapi</groupId>
            <artifactId>tencentcloud-sdk-java-sms</artifactId>
        </dependency>
    </dependencies>
</project>
ruoyi-common/ruoyi-common-sms/src/main/java/com/ruoyi/common/sms/config/SmsConfig.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,48 @@
package com.ruoyi.common.sms.config;
import com.ruoyi.common.sms.config.properties.SmsProperties;
import com.ruoyi.common.sms.core.AliyunSmsTemplate;
import com.ruoyi.common.sms.core.SmsTemplate;
import com.ruoyi.common.sms.core.TencentSmsTemplate;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
 * çŸ­ä¿¡é…ç½®ç±»
 *
 * @author Lion Li
 * @version 4.2.0
 */
@AutoConfiguration
@EnableConfigurationProperties(SmsProperties.class)
public class SmsConfig {
    @Configuration
    @ConditionalOnProperty(value = "sms.enabled", havingValue = "true")
    @ConditionalOnClass(com.aliyun.dysmsapi20170525.Client.class)
    static class AliyunSmsConfig {
        @Bean
        public SmsTemplate aliyunSmsTemplate(SmsProperties smsProperties) {
            return new AliyunSmsTemplate(smsProperties);
        }
    }
    @Configuration
    @ConditionalOnProperty(value = "sms.enabled", havingValue = "true")
    @ConditionalOnClass(com.tencentcloudapi.sms.v20190711.SmsClient.class)
    static class TencentSmsConfig {
        @Bean
        public SmsTemplate tencentSmsTemplate(SmsProperties smsProperties) {
            return new TencentSmsTemplate(smsProperties);
        }
    }
}
ruoyi-common/ruoyi-common-sms/src/main/java/com/ruoyi/common/sms/config/properties/SmsProperties.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,47 @@
package com.ruoyi.common.sms.config.properties;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
/**
 * SMS短信 é…ç½®å±žæ€§
 *
 * @author Lion Li
 * @version 4.2.0
 */
@Data
@Component
@ConfigurationProperties(prefix = "sms")
public class SmsProperties {
    private Boolean enabled;
    /**
     * é…ç½®èŠ‚ç‚¹
     * é˜¿é‡Œäº‘ dysmsapi.aliyuncs.com
     * è…¾è®¯äº‘ sms.tencentcloudapi.com
     */
    private String endpoint;
    /**
     * key
     */
    private String accessKeyId;
    /**
     * å¯†åŒ™
     */
    private String accessKeySecret;
    /*
     * çŸ­ä¿¡ç­¾å
     */
    private String signName;
    /**
     * çŸ­ä¿¡åº”用ID (腾讯专属)
     */
    private String sdkAppId;
}
ruoyi-common/ruoyi-common-sms/src/main/java/com/ruoyi/common/sms/core/AliyunSmsTemplate.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,66 @@
package com.ruoyi.common.sms.core;
import com.aliyun.dysmsapi20170525.Client;
import com.aliyun.dysmsapi20170525.models.SendSmsRequest;
import com.aliyun.dysmsapi20170525.models.SendSmsResponse;
import com.aliyun.teaopenapi.models.Config;
import com.ruoyi.common.core.utils.JsonUtils;
import com.ruoyi.common.core.utils.StringUtils;
import com.ruoyi.common.sms.config.properties.SmsProperties;
import com.ruoyi.common.sms.entity.SmsResult;
import com.ruoyi.common.sms.exception.SmsException;
import lombok.SneakyThrows;
import java.util.Map;
/**
 * Aliyun çŸ­ä¿¡æ¨¡æ¿
 *
 * @author Lion Li
 * @version 4.2.0
 */
public class AliyunSmsTemplate implements SmsTemplate {
    private SmsProperties properties;
    private Client client;
    @SneakyThrows(Exception.class)
    public AliyunSmsTemplate(SmsProperties smsProperties) {
        this.properties = smsProperties;
        Config config = new Config()
            // æ‚¨çš„AccessKey ID
            .setAccessKeyId(smsProperties.getAccessKeyId())
            // æ‚¨çš„AccessKey Secret
            .setAccessKeySecret(smsProperties.getAccessKeySecret())
            // è®¿é—®çš„域名
            .setEndpoint(smsProperties.getEndpoint());
        this.client = new Client(config);
    }
    @Override
    public SmsResult send(String phones, String templateId, Map<String, String> param) {
        if (StringUtils.isBlank(phones)) {
            throw new SmsException("手机号不能为空");
        }
        if (StringUtils.isBlank(templateId)) {
            throw new SmsException("模板ID不能为空");
        }
        SendSmsRequest req = new SendSmsRequest()
            .setPhoneNumbers(phones)
            .setSignName(properties.getSignName())
            .setTemplateCode(templateId)
            .setTemplateParam(JsonUtils.toJsonString(param));
        try {
            SendSmsResponse resp = client.sendSms(req);
            return SmsResult.builder()
                .isSuccess("OK".equals(resp.getBody().getCode()))
                .message(resp.getBody().getMessage())
                .response(JsonUtils.toJsonString(resp))
                .build();
        } catch (Exception e) {
            throw new SmsException(e.getMessage());
        }
    }
}
ruoyi-common/ruoyi-common-sms/src/main/java/com/ruoyi/common/sms/core/SmsTemplate.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,26 @@
package com.ruoyi.common.sms.core;
import com.ruoyi.common.sms.entity.SmsResult;
import java.util.Map;
/**
 * çŸ­ä¿¡æ¨¡æ¿
 *
 * @author Lion Li
 * @version 4.2.0
 */
public interface SmsTemplate {
    /**
     * å‘送短信
     *
     * @param phones     ç”µè¯å·(多个逗号分割)
     * @param templateId æ¨¡æ¿id
     * @param param      æ¨¡æ¿å¯¹åº”参数
     *                   é˜¿é‡Œ éœ€ä½¿ç”¨ æ¨¡æ¿å˜é‡åç§°å¯¹åº”内容 ä¾‹å¦‚: code=1234
     *                   è…¾è®¯ éœ€ä½¿ç”¨ æ¨¡æ¿å˜é‡é¡ºåºå¯¹åº”内容 ä¾‹å¦‚: 1=1234, 1为模板内第一个参数
     */
    SmsResult send(String phones, String templateId, Map<String, String> param);
}
ruoyi-common/ruoyi-common-sms/src/main/java/com/ruoyi/common/sms/core/TencentSmsTemplate.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,82 @@
package com.ruoyi.common.sms.core;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ArrayUtil;
import com.ruoyi.common.core.utils.JsonUtils;
import com.ruoyi.common.core.utils.StringUtils;
import com.ruoyi.common.sms.config.properties.SmsProperties;
import com.ruoyi.common.sms.entity.SmsResult;
import com.ruoyi.common.sms.exception.SmsException;
import com.tencentcloudapi.common.Credential;
import com.tencentcloudapi.common.profile.ClientProfile;
import com.tencentcloudapi.common.profile.HttpProfile;
import com.tencentcloudapi.sms.v20190711.SmsClient;
import com.tencentcloudapi.sms.v20190711.models.SendSmsRequest;
import com.tencentcloudapi.sms.v20190711.models.SendSmsResponse;
import com.tencentcloudapi.sms.v20190711.models.SendStatus;
import lombok.SneakyThrows;
import java.util.Arrays;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
/**
 * Tencent çŸ­ä¿¡æ¨¡æ¿
 *
 * @author Lion Li
 * @version 4.2.0
 */
public class TencentSmsTemplate implements SmsTemplate {
    private SmsProperties properties;
    private SmsClient client;
    @SneakyThrows(Exception.class)
    public TencentSmsTemplate(SmsProperties smsProperties) {
        this.properties = smsProperties;
        Credential credential = new Credential(smsProperties.getAccessKeyId(), smsProperties.getAccessKeySecret());
        HttpProfile httpProfile = new HttpProfile();
        httpProfile.setEndpoint(smsProperties.getEndpoint());
        ClientProfile clientProfile = new ClientProfile();
        clientProfile.setHttpProfile(httpProfile);
        this.client = new SmsClient(credential, "", clientProfile);
    }
    @Override
    public SmsResult send(String phones, String templateId, Map<String, String> param) {
        if (StringUtils.isBlank(phones)) {
            throw new SmsException("手机号不能为空");
        }
        if (StringUtils.isBlank(templateId)) {
            throw new SmsException("模板ID不能为空");
        }
        SendSmsRequest req = new SendSmsRequest();
        Set<String> set = Arrays.stream(phones.split(",")).map(p -> "+86" + p).collect(Collectors.toSet());
        req.setPhoneNumberSet(ArrayUtil.toArray(set, String.class));
        if (CollUtil.isNotEmpty(param)) {
            req.setTemplateParamSet(ArrayUtil.toArray(param.values(), String.class));
        }
        req.setTemplateID(templateId);
        req.setSign(properties.getSignName());
        req.setSmsSdkAppid(properties.getSdkAppId());
        try {
            SendSmsResponse resp = client.SendSms(req);
            SmsResult.SmsResultBuilder builder = SmsResult.builder()
                .isSuccess(true)
                .message("send success")
                .response(JsonUtils.toJsonString(resp));
            for (SendStatus sendStatus : resp.getSendStatusSet()) {
                if (!"Ok".equals(sendStatus.getCode())) {
                    builder.isSuccess(false).message(sendStatus.getMessage());
                    break;
                }
            }
            return builder.build();
        } catch (Exception e) {
            throw new SmsException(e.getMessage());
        }
    }
}
ruoyi-common/ruoyi-common-sms/src/main/java/com/ruoyi/common/sms/entity/SmsResult.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,31 @@
package com.ruoyi.common.sms.entity;
import lombok.Builder;
import lombok.Data;
/**
 * ä¸Šä¼ è¿”回体
 *
 * @author Lion Li
 */
@Data
@Builder
public class SmsResult {
    /**
     * æ˜¯å¦æˆåŠŸ
     */
    private boolean isSuccess;
    /**
     * å“åº”消息
     */
    private String message;
    /**
     * å®žé™…响应体
     * <p>
     * å¯è‡ªè¡Œè½¬æ¢ä¸º SDK å¯¹åº”çš„ SendSmsResponse
     */
    private String response;
}
ruoyi-common/ruoyi-common-sms/src/main/java/com/ruoyi/common/sms/exception/SmsException.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,19 @@
package com.ruoyi.common.sms.exception;
import java.io.Serial;
/**
 * Sms异常类
 *
 * @author Lion Li
 */
public class SmsException extends RuntimeException {
    @Serial
    private static final long serialVersionUID = 1L;
    public SmsException(String msg) {
        super(msg);
    }
}
ruoyi-common/ruoyi-common-sms/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1 @@
com.ruoyi.common.sms.config.SmsConfig
ruoyi-common/ruoyi-common-web/pom.xml
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,58 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <groupId>com.ruoyi</groupId>
        <artifactId>ruoyi-common</artifactId>
        <version>${revision}</version>
        <relativePath>../pom.xml</relativePath>
    </parent>
    <modelVersion>4.0.0</modelVersion>
    <artifactId>ruoyi-common-web</artifactId>
    <description>
        ruoyi-common-web web服务
    </description>
    <dependencies>
        <dependency>
            <groupId>com.ruoyi</groupId>
            <artifactId>ruoyi-common-core</artifactId>
        </dependency>
        <dependency>
            <groupId>com.ruoyi</groupId>
            <artifactId>ruoyi-common-redis</artifactId>
        </dependency>
        <!-- SpringBoot Web容器 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <exclusions>
                <exclusion>
                    <artifactId>spring-boot-starter-tomcat</artifactId>
                    <groupId>org.springframework.boot</groupId>
                </exclusion>
            </exclusions>
        </dependency>
        <!-- web å®¹å™¨ä½¿ç”¨ undertow æ€§èƒ½æ›´å¼º -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-undertow</artifactId>
        </dependency>
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-crypto</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>transmittable-thread-local</artifactId>
        </dependency>
    </dependencies>
</project>
ruoyi-common/ruoyi-common-web/src/main/java/com/ruoyi/common/web/config/CaptchaConfig.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,62 @@
package com.ruoyi.common.web.config;
import cn.hutool.captcha.CaptchaUtil;
import cn.hutool.captcha.CircleCaptcha;
import cn.hutool.captcha.LineCaptcha;
import cn.hutool.captcha.ShearCaptcha;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Lazy;
import java.awt.*;
/**
 * éªŒè¯ç é…ç½®
 *
 * @author Lion Li
 */
@AutoConfiguration
public class CaptchaConfig {
    private static final int WIDTH = 160;
    private static final int HEIGHT = 60;
    private static final Color BACKGROUND = Color.PINK;
    private static final Font FONT = new Font("Arial", Font.BOLD, 48);
    /**
     * åœ†åœˆå¹²æ‰°éªŒè¯ç 
     */
    @Lazy
    @Bean
    public CircleCaptcha circleCaptcha() {
        CircleCaptcha captcha = CaptchaUtil.createCircleCaptcha(WIDTH, HEIGHT);
        captcha.setBackground(BACKGROUND);
        captcha.setFont(FONT);
        return captcha;
    }
    /**
     * çº¿æ®µå¹²æ‰°çš„验证码
     */
    @Lazy
    @Bean
    public LineCaptcha lineCaptcha() {
        LineCaptcha captcha = CaptchaUtil.createLineCaptcha(WIDTH, HEIGHT);
        captcha.setBackground(BACKGROUND);
        captcha.setFont(FONT);
        return captcha;
    }
    /**
     * æ‰­æ›²å¹²æ‰°éªŒè¯ç 
     */
    @Lazy
    @Bean
    public ShearCaptcha shearCaptcha() {
        ShearCaptcha captcha = CaptchaUtil.createShearCaptcha(WIDTH, HEIGHT);
        captcha.setBackground(BACKGROUND);
        captcha.setFont(FONT);
        return captcha;
    }
}
ruoyi-common/ruoyi-common-web/src/main/java/com/ruoyi/common/web/config/FilterConfig.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,55 @@
package com.ruoyi.common.web.config;
import com.ruoyi.common.core.utils.StringUtils;
import com.ruoyi.common.web.config.properties.XssProperties;
import com.ruoyi.common.web.filter.RepeatableFilter;
import com.ruoyi.common.web.filter.XssFilter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import jakarta.servlet.DispatcherType;
import java.util.HashMap;
import java.util.Map;
/**
 * Filter配置
 *
 * @author Lion Li
 */
@AutoConfiguration
public class FilterConfig {
    @Autowired
    private XssProperties xssProperties;
    @SuppressWarnings({"rawtypes", "unchecked"})
    @Bean
    @ConditionalOnProperty(value = "xss.enabled", havingValue = "true")
    public FilterRegistrationBean xssFilterRegistration() {
        FilterRegistrationBean registration = new FilterRegistrationBean();
        registration.setDispatcherTypes(DispatcherType.REQUEST);
        registration.setFilter(new XssFilter());
        registration.addUrlPatterns(StringUtils.split(xssProperties.getUrlPatterns(), ","));
        registration.setName("xssFilter");
        registration.setOrder(FilterRegistrationBean.HIGHEST_PRECEDENCE);
        Map<String, String> initParameters = new HashMap<String, String>();
        initParameters.put("excludes", xssProperties.getExcludes());
        registration.setInitParameters(initParameters);
        return registration;
    }
    @SuppressWarnings({"rawtypes", "unchecked"})
    @Bean
    public FilterRegistrationBean someFilterRegistration() {
        FilterRegistrationBean registration = new FilterRegistrationBean();
        registration.setFilter(new RepeatableFilter());
        registration.addUrlPatterns("/*");
        registration.setName("repeatableFilter");
        registration.setOrder(FilterRegistrationBean.LOWEST_PRECEDENCE);
        return registration;
    }
}
ruoyi-common/ruoyi-common-web/src/main/java/com/ruoyi/common/web/config/I18nConfig.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,22 @@
package com.ruoyi.common.web.config;
import com.ruoyi.common.web.core.I18nLocaleResolver;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.web.servlet.LocaleResolver;
/**
 * å›½é™…化配置
 *
 * @author Lion Li
 */
@AutoConfiguration(before = WebMvcAutoConfiguration.class)
public class I18nConfig {
    @Bean
    public LocaleResolver localeResolver() {
        return new I18nLocaleResolver();
    }
}
ruoyi-common/ruoyi-common-web/src/main/java/com/ruoyi/common/web/config/ResourcesConfig.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,52 @@
package com.ruoyi.common.web.config;
import com.ruoyi.common.web.interceptor.PlusWebInvokeTimeInterceptor;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/**
 * é€šç”¨é…ç½®
 *
 * @author Lion Li
 */
@AutoConfiguration
public class ResourcesConfig implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        // å…¨å±€è®¿é—®æ€§èƒ½æ‹¦æˆª
        registry.addInterceptor(new PlusWebInvokeTimeInterceptor());
    }
    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
    }
    /**
     * è·¨åŸŸé…ç½®
     */
    @Bean
    public CorsFilter corsFilter() {
        CorsConfiguration config = new CorsConfiguration();
        config.setAllowCredentials(true);
        // è®¾ç½®è®¿é—®æºåœ°å€
        config.addAllowedOriginPattern("*");
        // è®¾ç½®è®¿é—®æºè¯·æ±‚头
        config.addAllowedHeader("*");
        // è®¾ç½®è®¿é—®æºè¯·æ±‚方法
        config.addAllowedMethod("*");
        // æœ‰æ•ˆæœŸ 1800秒
        config.setMaxAge(1800L);
        // æ·»åŠ æ˜ å°„è·¯å¾„ï¼Œæ‹¦æˆªä¸€åˆ‡è¯·æ±‚
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", config);
        // è¿”回新的CorsFilter
        return new CorsFilter(source);
    }
}
ruoyi-common/ruoyi-common-web/src/main/java/com/ruoyi/common/web/config/UndertowConfig.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,30 @@
package com.ruoyi.common.web.config;
import io.undertow.server.DefaultByteBufferPool;
import io.undertow.websockets.jsr.WebSocketDeploymentInfo;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.web.embedded.undertow.UndertowServletWebServerFactory;
import org.springframework.boot.web.server.WebServerFactoryCustomizer;
/**
 * Undertow è‡ªå®šä¹‰é…ç½®
 *
 * @author Lion Li
 */
@AutoConfiguration
public class UndertowConfig implements WebServerFactoryCustomizer<UndertowServletWebServerFactory> {
    /**
     * è®¾ç½® Undertow çš„ websocket ç¼“冲池
     */
    @Override
    public void customize(UndertowServletWebServerFactory factory) {
        // é»˜è®¤ä¸ç›´æŽ¥åˆ†é…å†…å­˜ å¦‚果项目中使用了 websocket å»ºè®®ç›´æŽ¥åˆ†é…
        factory.addDeploymentInfoCustomizers(deploymentInfo -> {
            WebSocketDeploymentInfo webSocketDeploymentInfo = new WebSocketDeploymentInfo();
            webSocketDeploymentInfo.setBuffers(new DefaultByteBufferPool(false, 512));
            deploymentInfo.addServletContextAttribute("io.undertow.websockets.jsr.WebSocketDeploymentInfo", webSocketDeploymentInfo);
        });
    }
}
ruoyi-common/ruoyi-common-web/src/main/java/com/ruoyi/common/web/config/properties/CaptchaProperties.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,38 @@
package com.ruoyi.common.web.config.properties;
import com.ruoyi.common.web.enums.CaptchaCategory;
import com.ruoyi.common.web.enums.CaptchaType;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
/**
 * éªŒè¯ç  é…ç½®å±žæ€§
 *
 * @author Lion Li
 */
@Data
@Component
@ConfigurationProperties(prefix = "captcha")
public class CaptchaProperties {
    /**
     * éªŒè¯ç ç±»åž‹
     */
    private CaptchaType type;
    /**
     * éªŒè¯ç ç±»åˆ«
     */
    private CaptchaCategory category;
    /**
     * æ•°å­—验证码位数
     */
    private Integer numberLength;
    /**
     * å­—符验证码长度
     */
    private Integer charLength;
}
ruoyi-common/ruoyi-common-web/src/main/java/com/ruoyi/common/web/config/properties/XssProperties.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,32 @@
package com.ruoyi.common.web.config.properties;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
/**
 * xss过滤 é…ç½®å±žæ€§
 *
 * @author Lion Li
 */
@Data
@Component
@ConfigurationProperties(prefix = "xss")
public class XssProperties {
    /**
     * è¿‡æ»¤å¼€å…³
     */
    private String enabled;
    /**
     * æŽ’除链接(多个用逗号分隔)
     */
    private String excludes;
    /**
     * åŒ¹é…é“¾æŽ¥
     */
    private String urlPatterns;
}
ruoyi-common/ruoyi-common-web/src/main/java/com/ruoyi/common/web/core/I18nLocaleResolver.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,31 @@
package com.ruoyi.common.web.core;
import org.springframework.web.servlet.LocaleResolver;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.util.Locale;
/**
 * èŽ·å–è¯·æ±‚å¤´å›½é™…åŒ–ä¿¡æ¯
 *
 * @author Lion Li
 */
public class I18nLocaleResolver implements LocaleResolver {
    @Override
    public Locale resolveLocale(HttpServletRequest httpServletRequest) {
        String language = httpServletRequest.getHeader("content-language");
        Locale locale = Locale.getDefault();
        if (language != null && language.length() > 0) {
            String[] split = language.split("_");
            locale = new Locale(split[0], split[1]);
        }
        return locale;
    }
    @Override
    public void setLocale(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Locale locale) {
    }
}
ruoyi-common/ruoyi-common-web/src/main/java/com/ruoyi/common/web/enums/CaptchaCategory.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,35 @@
package com.ruoyi.common.web.enums;
import cn.hutool.captcha.AbstractCaptcha;
import cn.hutool.captcha.CircleCaptcha;
import cn.hutool.captcha.LineCaptcha;
import cn.hutool.captcha.ShearCaptcha;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
 * éªŒè¯ç ç±»åˆ«
 *
 * @author Lion Li
 */
@Getter
@AllArgsConstructor
public enum CaptchaCategory {
    /**
     * çº¿æ®µå¹²æ‰°
     */
    LINE(LineCaptcha.class),
    /**
     * åœ†åœˆå¹²æ‰°
     */
    CIRCLE(CircleCaptcha.class),
    /**
     * æ‰­æ›²å¹²æ‰°
     */
    SHEAR(ShearCaptcha.class);
    private final Class<? extends AbstractCaptcha> clazz;
}
ruoyi-common/ruoyi-common-web/src/main/java/com/ruoyi/common/web/enums/CaptchaType.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,29 @@
package com.ruoyi.common.web.enums;
import cn.hutool.captcha.generator.CodeGenerator;
import cn.hutool.captcha.generator.RandomGenerator;
import com.ruoyi.common.web.utils.UnsignedMathGenerator;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
 * éªŒè¯ç ç±»åž‹
 *
 * @author Lion Li
 */
@Getter
@AllArgsConstructor
public enum CaptchaType {
    /**
     * æ•°å­—
     */
    MATH(UnsignedMathGenerator.class),
    /**
     * å­—符
     */
    CHAR(RandomGenerator.class);
    private final Class<? extends CodeGenerator> clazz;
}
ruoyi-common/ruoyi-common-web/src/main/java/com/ruoyi/common/web/filter/RepeatableFilter.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,40 @@
package com.ruoyi.common.web.filter;
import com.ruoyi.common.core.utils.StringUtils;
import org.springframework.http.MediaType;
import jakarta.servlet.*;
import jakarta.servlet.http.HttpServletRequest;
import java.io.IOException;
/**
 * Repeatable è¿‡æ»¤å™¨
 *
 * @author ruoyi
 */
public class RepeatableFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
    }
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
        throws IOException, ServletException {
        ServletRequest requestWrapper = null;
        if (request instanceof HttpServletRequest
            && StringUtils.startsWithIgnoreCase(request.getContentType(), MediaType.APPLICATION_JSON_VALUE)) {
            requestWrapper = new RepeatedlyRequestWrapper((HttpServletRequest) request, response);
        }
        if (null == requestWrapper) {
            chain.doFilter(request, response);
        } else {
            chain.doFilter(requestWrapper, response);
        }
    }
    @Override
    public void destroy() {
    }
}
ruoyi-common/ruoyi-common-web/src/main/java/com/ruoyi/common/web/filter/RepeatedlyRequestWrapper.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,67 @@
package com.ruoyi.common.web.filter;
import cn.hutool.core.io.IoUtil;
import com.ruoyi.common.core.constant.Constants;
import jakarta.servlet.ReadListener;
import jakarta.servlet.ServletInputStream;
import jakarta.servlet.ServletResponse;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletRequestWrapper;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
/**
 * æž„建可重复读取inputStream的request
 *
 * @author ruoyi
 */
public class RepeatedlyRequestWrapper extends HttpServletRequestWrapper {
    private final byte[] body;
    public RepeatedlyRequestWrapper(HttpServletRequest request, ServletResponse response) throws IOException {
        super(request);
        request.setCharacterEncoding(Constants.UTF8);
        response.setCharacterEncoding(Constants.UTF8);
        body = IoUtil.readBytes(request.getInputStream(), false);
    }
    @Override
    public BufferedReader getReader() throws IOException {
        return new BufferedReader(new InputStreamReader(getInputStream()));
    }
    @Override
    public ServletInputStream getInputStream() throws IOException {
        final ByteArrayInputStream bais = new ByteArrayInputStream(body);
        return new ServletInputStream() {
            @Override
            public int read() throws IOException {
                return bais.read();
            }
            @Override
            public int available() throws IOException {
                return body.length;
            }
            @Override
            public boolean isFinished() {
                return false;
            }
            @Override
            public boolean isReady() {
                return false;
            }
            @Override
            public void setReadListener(ReadListener readListener) {
            }
        };
    }
}
ruoyi-common/ruoyi-common-web/src/main/java/com/ruoyi/common/web/filter/XssFilter.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,62 @@
package com.ruoyi.common.web.filter;
import com.ruoyi.common.core.utils.StringUtils;
import org.springframework.http.HttpMethod;
import jakarta.servlet.*;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
/**
 * é˜²æ­¢XSS攻击的过滤器
 *
 * @author ruoyi
 */
public class XssFilter implements Filter {
    /**
     * æŽ’除链接
     */
    public List<String> excludes = new ArrayList<>();
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        String tempExcludes = filterConfig.getInitParameter("excludes");
        if (StringUtils.isNotEmpty(tempExcludes)) {
            String[] url = tempExcludes.split(",");
            for (int i = 0; url != null && i < url.length; i++) {
                excludes.add(url[i]);
            }
        }
    }
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
        throws IOException, ServletException {
        HttpServletRequest req = (HttpServletRequest) request;
        HttpServletResponse resp = (HttpServletResponse) response;
        if (handleExcludeURL(req, resp)) {
            chain.doFilter(request, response);
            return;
        }
        XssHttpServletRequestWrapper xssRequest = new XssHttpServletRequestWrapper((HttpServletRequest) request);
        chain.doFilter(xssRequest, response);
    }
    private boolean handleExcludeURL(HttpServletRequest request, HttpServletResponse response) {
        String url = request.getServletPath();
        String method = request.getMethod();
        // GET DELETE ä¸è¿‡æ»¤
        if (method == null || HttpMethod.GET.matches(method) || HttpMethod.DELETE.matches(method)) {
            return true;
        }
        return StringUtils.matches(url, excludes);
    }
    @Override
    public void destroy() {
    }
}
ruoyi-common/ruoyi-common-web/src/main/java/com/ruoyi/common/web/filter/XssHttpServletRequestWrapper.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,97 @@
package com.ruoyi.common.web.filter;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HtmlUtil;
import com.ruoyi.common.core.utils.StringUtils;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import jakarta.servlet.ReadListener;
import jakarta.servlet.ServletInputStream;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletRequestWrapper;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
/**
 * XSS过滤处理
 *
 * @author ruoyi
 */
public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper {
    /**
     * @param request
     */
    public XssHttpServletRequestWrapper(HttpServletRequest request) {
        super(request);
    }
    @Override
    public String[] getParameterValues(String name) {
        String[] values = super.getParameterValues(name);
        if (values != null) {
            int length = values.length;
            String[] escapseValues = new String[length];
            for (int i = 0; i < length; i++) {
                // é˜²xss攻击和过滤前后空格
                escapseValues[i] = HtmlUtil.cleanHtmlTag(values[i]).trim();
            }
            return escapseValues;
        }
        return super.getParameterValues(name);
    }
    @Override
    public ServletInputStream getInputStream() throws IOException {
        // éžjson类型,直接返回
        if (!isJsonRequest()) {
            return super.getInputStream();
        }
        // ä¸ºç©ºï¼Œç›´æŽ¥è¿”回
        String json = StrUtil.str(IoUtil.readBytes(super.getInputStream(), false), StandardCharsets.UTF_8);
        if (StringUtils.isEmpty(json)) {
            return super.getInputStream();
        }
        // xss过滤
        json = HtmlUtil.cleanHtmlTag(json).trim();
        byte[] jsonBytes = json.getBytes(StandardCharsets.UTF_8);
        final ByteArrayInputStream bis = IoUtil.toStream(jsonBytes);
        return new ServletInputStream() {
            @Override
            public boolean isFinished() {
                return true;
            }
            @Override
            public boolean isReady() {
                return true;
            }
            @Override
            public int available() throws IOException {
                return jsonBytes.length;
            }
            @Override
            public void setReadListener(ReadListener readListener) {
            }
            @Override
            public int read() throws IOException {
                return bis.read();
            }
        };
    }
    /**
     * æ˜¯å¦æ˜¯Json请求
     */
    public boolean isJsonRequest() {
        String header = super.getHeader(HttpHeaders.CONTENT_TYPE);
        return StringUtils.startsWithIgnoreCase(header, MediaType.APPLICATION_JSON_VALUE);
    }
}
ruoyi-common/ruoyi-common-web/src/main/java/com/ruoyi/common/web/interceptor/PlusWebInvokeTimeInterceptor.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,94 @@
package com.ruoyi.common.web.interceptor;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.map.MapUtil;
import com.alibaba.ttl.TransmittableThreadLocal;
import com.ruoyi.common.core.utils.JsonUtils;
import com.ruoyi.common.core.utils.SpringUtils;
import com.ruoyi.common.core.utils.StringUtils;
import com.ruoyi.common.web.filter.RepeatedlyRequestWrapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.time.StopWatch;
import org.springframework.http.MediaType;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.util.Map;
/**
 * web的调用时间统计拦截器
 * dev环境有效
 *
 * @author Lion Li
 * @since 3.3.0
 */
@Slf4j
public class PlusWebInvokeTimeInterceptor implements HandlerInterceptor {
    private final String prodProfile = "prod";
    private final TransmittableThreadLocal<StopWatch> invokeTimeTL = new TransmittableThreadLocal<>();
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        if (!prodProfile.equals(SpringUtils.getActiveProfile())) {
            String url = request.getMethod() + " " + request.getRequestURI();
            // æ‰“印请求参数
            if (isJsonRequest(request)) {
                String jsonParam = "";
                if (request instanceof RepeatedlyRequestWrapper) {
                    BufferedReader reader = request.getReader();
                    jsonParam = IoUtil.read(reader);
                }
                log.debug("[PLUS]开始请求 => URL[{}],参数类型[json],参数:[{}]", url, jsonParam);
            } else {
                Map<String, String[]> parameterMap = request.getParameterMap();
                if (MapUtil.isNotEmpty(parameterMap)) {
                    String parameters = JsonUtils.toJsonString(parameterMap);
                    log.debug("[PLUS]开始请求 => URL[{}],参数类型[param],参数:[{}]", url, parameters);
                } else {
                    log.debug("[PLUS]开始请求 => URL[{}],无参数", url);
                }
            }
            StopWatch stopWatch = new StopWatch();
            invokeTimeTL.set(stopWatch);
            stopWatch.start();
        }
        return true;
    }
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
    }
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        if (!prodProfile.equals(SpringUtils.getActiveProfile())) {
            StopWatch stopWatch = invokeTimeTL.get();
            stopWatch.stop();
            log.debug("[PLUS]结束请求 => URL[{}],耗时:[{}]毫秒", request.getMethod() + " " + request.getRequestURI(), stopWatch.getTime());
            invokeTimeTL.remove();
        }
    }
    /**
     * åˆ¤æ–­æœ¬æ¬¡è¯·æ±‚的数据类型是否为json
     *
     * @param request request
     * @return boolean
     */
    private boolean isJsonRequest(HttpServletRequest request) {
        String contentType = request.getContentType();
        if (contentType != null) {
            return StringUtils.startsWithIgnoreCase(contentType, MediaType.APPLICATION_JSON_VALUE);
        }
        return false;
    }
}
ruoyi-common/ruoyi-common-web/src/main/java/com/ruoyi/common/web/utils/UnsignedMathGenerator.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,88 @@
package com.ruoyi.common.web.utils;
import cn.hutool.captcha.generator.CodeGenerator;
import cn.hutool.core.math.Calculator;
import cn.hutool.core.util.CharUtil;
import cn.hutool.core.util.RandomUtil;
import com.ruoyi.common.core.utils.StringUtils;
import java.io.Serial;
/**
 * æ— ç¬¦å·è®¡ç®—生成器
 *
 * @author Lion Li
 */
public class UnsignedMathGenerator implements CodeGenerator {
    @Serial
    private static final long serialVersionUID = -5514819971774091076L;
    private static final String OPERATORS = "+-*";
    /**
     * å‚与计算数字最大长度
     */
    private final int numberLength;
    /**
     * æž„造
     */
    public UnsignedMathGenerator() {
        this(2);
    }
    /**
     * æž„造
     *
     * @param numberLength å‚与计算最大数字位数
     */
    public UnsignedMathGenerator(int numberLength) {
        this.numberLength = numberLength;
    }
    @Override
    public String generate() {
        final int limit = getLimit();
        int a = RandomUtil.randomInt(limit);
        int b = RandomUtil.randomInt(limit);
        String max = Integer.toString(Math.max(a,b));
        String min = Integer.toString(Math.min(a,b));
        max = StringUtils.rightPad(max, this.numberLength, CharUtil.SPACE);
        min = StringUtils.rightPad(min, this.numberLength, CharUtil.SPACE);
        return max + RandomUtil.randomChar(OPERATORS) + min + '=';
    }
    @Override
    public boolean verify(String code, String userInputCode) {
        int result;
        try {
            result = Integer.parseInt(userInputCode);
        } catch (NumberFormatException e) {
            // ç”¨æˆ·è¾“入非数字
            return false;
        }
        final int calculateResult = (int) Calculator.conversion(code);
        return result == calculateResult;
    }
    /**
     * èŽ·å–éªŒè¯ç é•¿åº¦
     *
     * @return éªŒè¯ç é•¿åº¦
     */
    public int getLength() {
        return this.numberLength * 2 + 2;
    }
    /**
     * æ ¹æ®é•¿åº¦èŽ·å–å‚ä¸Žè®¡ç®—æ•°å­—æœ€å¤§å€¼
     *
     * @return æœ€å¤§å€¼
     */
    private int getLimit() {
        return Integer.parseInt("1" + StringUtils.repeat('0', this.numberLength));
    }
}
ruoyi-common/ruoyi-common-web/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,5 @@
com.ruoyi.common.web.config.CaptchaConfig
com.ruoyi.common.web.config.FilterConfig
com.ruoyi.common.web.config.I18nConfig
com.ruoyi.common.web.config.ResourcesConfig
com.ruoyi.common.web.config.UndertowConfig
ruoyi-common/src/main/java/com/ruoyi/common/annotation/CellMerge.java
ÎļþÒÑɾ³ý
ruoyi-common/src/main/java/com/ruoyi/common/annotation/DataColumn.java
ÎļþÒÑɾ³ý
ruoyi-common/src/main/java/com/ruoyi/common/annotation/DataPermission.java
ÎļþÒÑɾ³ý
ruoyi-common/src/main/java/com/ruoyi/common/annotation/DictDataMapper.java
ÎļþÒÑɾ³ý
ruoyi-common/src/main/java/com/ruoyi/common/annotation/ExcelDictFormat.java
ÎļþÒÑɾ³ý
ruoyi-common/src/main/java/com/ruoyi/common/annotation/Log.java
ÎļþÒÑɾ³ý
ruoyi-common/src/main/java/com/ruoyi/common/annotation/RateLimiter.java
ÎļþÒÑɾ³ý
ruoyi-common/src/main/java/com/ruoyi/common/annotation/RepeatSubmit.java
ÎļþÒÑɾ³ý
ruoyi-common/src/main/java/com/ruoyi/common/annotation/Sensitive.java
ÎļþÒÑɾ³ý
ruoyi-common/src/main/java/com/ruoyi/common/captcha/UnsignedMathGenerator.java
ÎļþÒÑɾ³ý
ruoyi-common/src/main/java/com/ruoyi/common/config/RuoYiConfig.java
ÎļþÒÑɾ³ý
ruoyi-common/src/main/java/com/ruoyi/common/constant/CacheConstants.java
ÎļþÒÑɾ³ý
ruoyi-common/src/main/java/com/ruoyi/common/constant/CacheNames.java
ÎļþÒÑɾ³ý
ruoyi-common/src/main/java/com/ruoyi/common/constant/Constants.java
ÎļþÒÑɾ³ý
ruoyi-common/src/main/java/com/ruoyi/common/constant/GenConstants.java
ÎļþÒÑɾ³ý
ruoyi-common/src/main/java/com/ruoyi/common/constant/HttpStatus.java
ÎļþÒÑɾ³ý
ruoyi-common/src/main/java/com/ruoyi/common/constant/UserConstants.java
ÎļþÒÑɾ³ý
ruoyi-common/src/main/java/com/ruoyi/common/convert/ExcelBigNumberConvert.java
ÎļþÒÑɾ³ý
ruoyi-common/src/main/java/com/ruoyi/common/convert/ExcelDictConvert.java
ÎļþÒÑɾ³ý
ruoyi-common/src/main/java/com/ruoyi/common/core/controller/BaseController.java
ÎļþÒÑɾ³ý
ruoyi-common/src/main/java/com/ruoyi/common/core/domain/BaseEntity.java
ÎļþÒÑɾ³ý
ruoyi-common/src/main/java/com/ruoyi/common/core/domain/PageQuery.java
ÎļþÒÑɾ³ý
ruoyi-common/src/main/java/com/ruoyi/common/core/domain/R.java
ÎļþÒÑɾ³ý
ruoyi-common/src/main/java/com/ruoyi/common/core/domain/TreeEntity.java
ÎļþÒÑɾ³ý
ruoyi-common/src/main/java/com/ruoyi/common/core/domain/event/LogininforEvent.java
ÎļþÒÑɾ³ý
ruoyi-common/src/main/java/com/ruoyi/common/core/domain/event/OperLogEvent.java
ÎļþÒÑɾ³ý
ruoyi-common/src/main/java/com/ruoyi/common/core/domain/model/LoginBody.java
ÎļþÒÑɾ³ý
ruoyi-common/src/main/java/com/ruoyi/common/core/domain/model/LoginUser.java
ÎļþÒÑɾ³ý
ruoyi-common/src/main/java/com/ruoyi/common/core/mapper/BaseMapperPlus.java
ÎļþÒÑɾ³ý
ruoyi-common/src/main/java/com/ruoyi/common/core/page/TableDataInfo.java
ÎļþÒÑɾ³ý
ruoyi-common/src/main/java/com/ruoyi/common/enums/BusinessStatus.java
ÎļþÒÑɾ³ý
ruoyi-common/src/main/java/com/ruoyi/common/enums/BusinessType.java
ÎļþÒÑɾ³ý
ruoyi-common/src/main/java/com/ruoyi/common/enums/CaptchaCategory.java
ÎļþÒÑɾ³ý
ruoyi-common/src/main/java/com/ruoyi/common/enums/CaptchaType.java
ÎļþÒÑɾ³ý
ruoyi-common/src/main/java/com/ruoyi/common/enums/DataBaseType.java
ÎļþÒÑɾ³ý
ruoyi-common/src/main/java/com/ruoyi/common/enums/DataScopeType.java
ÎļþÒÑɾ³ý
ruoyi-common/src/main/java/com/ruoyi/common/enums/DeviceType.java
ÎļþÒÑɾ³ý
ruoyi-common/src/main/java/com/ruoyi/common/enums/HttpMethod.java
ÎļþÒÑɾ³ý
ruoyi-common/src/main/java/com/ruoyi/common/enums/LimitType.java
ÎļþÒÑɾ³ý
ruoyi-common/src/main/java/com/ruoyi/common/enums/LoginType.java
ÎļþÒÑɾ³ý
ruoyi-common/src/main/java/com/ruoyi/common/enums/OperatorType.java
ÎļþÒÑɾ³ý
ruoyi-common/src/main/java/com/ruoyi/common/enums/SensitiveStrategy.java
ÎļþÒÑɾ³ý
ruoyi-common/src/main/java/com/ruoyi/common/enums/UserStatus.java
ÎļþÒÑɾ³ý
ruoyi-common/src/main/java/com/ruoyi/common/enums/UserType.java
ÎļþÒÑɾ³ý
ruoyi-common/src/main/java/com/ruoyi/common/excel/CellMergeStrategy.java
ÎļþÒÑɾ³ý
ruoyi-common/src/main/java/com/ruoyi/common/excel/DefaultExcelListener.java
ÎļþÒÑɾ³ý
ruoyi-common/src/main/java/com/ruoyi/common/excel/DefautExcelResult.java
ÎļþÒÑɾ³ý
ruoyi-common/src/main/java/com/ruoyi/common/excel/ExcelListener.java
ÎļþÒÑɾ³ý
ruoyi-common/src/main/java/com/ruoyi/common/excel/ExcelResult.java
ÎļþÒÑɾ³ý
ruoyi-common/src/main/java/com/ruoyi/common/exception/DemoModeException.java
ÎļþÒÑɾ³ý
ruoyi-common/src/main/java/com/ruoyi/common/exception/GlobalException.java
ÎļþÒÑɾ³ý
ruoyi-common/src/main/java/com/ruoyi/common/exception/ServiceException.java
ÎļþÒÑɾ³ý
ruoyi-common/src/main/java/com/ruoyi/common/exception/UtilException.java
ÎļþÒÑɾ³ý
ruoyi-common/src/main/java/com/ruoyi/common/exception/base/BaseException.java
ÎļþÒÑɾ³ý
ruoyi-common/src/main/java/com/ruoyi/common/exception/file/FileException.java
ÎļþÒÑɾ³ý
ruoyi-common/src/main/java/com/ruoyi/common/exception/file/FileNameLengthLimitExceededException.java
ÎļþÒÑɾ³ý
ruoyi-common/src/main/java/com/ruoyi/common/exception/file/FileSizeLimitExceededException.java
ÎļþÒÑɾ³ý
ruoyi-common/src/main/java/com/ruoyi/common/exception/user/CaptchaException.java
ÎļþÒÑɾ³ý
ruoyi-common/src/main/java/com/ruoyi/common/exception/user/CaptchaExpireException.java
ÎļþÒÑɾ³ý
ruoyi-common/src/main/java/com/ruoyi/common/exception/user/UserException.java
ÎļþÒÑɾ³ý
ruoyi-common/src/main/java/com/ruoyi/common/exception/user/UserPasswordNotMatchException.java
ÎļþÒÑɾ³ý
ruoyi-common/src/main/java/com/ruoyi/common/exception/user/UserPasswordRetryLimitExceedException.java
ÎļþÒÑɾ³ý
ruoyi-common/src/main/java/com/ruoyi/common/filter/RepeatableFilter.java
ÎļþÒÑɾ³ý
ruoyi-common/src/main/java/com/ruoyi/common/filter/RepeatedlyRequestWrapper.java
ÎļþÒÑɾ³ý
ruoyi-common/src/main/java/com/ruoyi/common/filter/XssFilter.java
ÎļþÒÑɾ³ý
ruoyi-common/src/main/java/com/ruoyi/common/filter/XssHttpServletRequestWrapper.java
ÎļþÒÑɾ³ý
ruoyi-common/src/main/java/com/ruoyi/common/helper/DataBaseHelper.java
ÎļþÒÑɾ³ý
ruoyi-common/src/main/java/com/ruoyi/common/helper/DataPermissionHelper.java
ÎļþÒÑɾ³ý
ruoyi-common/src/main/java/com/ruoyi/common/helper/LoginHelper.java
ÎļþÒÑɾ³ý
ruoyi-common/src/main/java/com/ruoyi/common/jackson/DictDataJsonSerializer.java
ÎļþÒÑɾ³ý
ruoyi-common/src/main/java/com/ruoyi/common/jackson/SensitiveJsonSerializer.java
ÎļþÒÑɾ³ý
ruoyi-common/src/main/java/com/ruoyi/common/utils/BeanCopyUtils.java
ÎļþÒÑɾ³ý
ruoyi-common/src/main/java/com/ruoyi/common/utils/DateUtils.java
ÎļþÒÑɾ³ý
ruoyi-common/src/main/java/com/ruoyi/common/utils/JsonUtils.java
ÎļþÒÑɾ³ý
ruoyi-common/src/main/java/com/ruoyi/common/utils/MessageUtils.java
ÎļþÒÑɾ³ý
ruoyi-common/src/main/java/com/ruoyi/common/utils/ServletUtils.java
ÎļþÒÑɾ³ý
ruoyi-common/src/main/java/com/ruoyi/common/utils/StreamUtils.java
ÎļþÒÑɾ³ý
ruoyi-common/src/main/java/com/ruoyi/common/utils/StringUtils.java
ÎļþÒÑɾ³ý
ruoyi-common/src/main/java/com/ruoyi/common/utils/Threads.java
ÎļþÒÑɾ³ý
ruoyi-common/src/main/java/com/ruoyi/common/utils/TreeBuildUtils.java
ÎļþÒÑɾ³ý
ruoyi-common/src/main/java/com/ruoyi/common/utils/ValidatorUtils.java
ÎļþÒÑɾ³ý
ruoyi-common/src/main/java/com/ruoyi/common/utils/email/MailUtils.java
ÎļþÒÑɾ³ý
ruoyi-common/src/main/java/com/ruoyi/common/utils/email/UserPassAuthenticator.java
ÎļþÒÑɾ³ý
ruoyi-common/src/main/java/com/ruoyi/common/utils/file/FileUtils.java
ÎļþÒÑɾ³ý
ruoyi-common/src/main/java/com/ruoyi/common/utils/file/MimeTypeUtils.java
ÎļþÒÑɾ³ý
ruoyi-common/src/main/java/com/ruoyi/common/utils/ip/AddressUtils.java
ÎļþÒÑɾ³ý
ruoyi-common/src/main/java/com/ruoyi/common/utils/poi/ExcelUtil.java
ÎļþÒÑɾ³ý
ruoyi-common/src/main/java/com/ruoyi/common/utils/redis/CacheUtils.java
ÎļþÒÑɾ³ý
ruoyi-common/src/main/java/com/ruoyi/common/utils/redis/QueueUtils.java
ÎļþÒÑɾ³ý
ruoyi-common/src/main/java/com/ruoyi/common/utils/redis/RedisUtils.java
ÎļþÒÑɾ³ý
ruoyi-common/src/main/java/com/ruoyi/common/utils/reflect/ReflectUtils.java
ÎļþÒÑɾ³ý
ruoyi-common/src/main/java/com/ruoyi/common/utils/spring/SpringUtils.java
ÎļþÒÑɾ³ý
ruoyi-common/src/main/java/com/ruoyi/common/utils/sql/SqlUtil.java
ÎļþÒÑɾ³ý
ruoyi-common/src/main/java/com/ruoyi/common/xss/Xss.java
ÎļþÒÑɾ³ý
ruoyi-common/src/main/java/com/ruoyi/common/xss/XssValidator.java
ÎļþÒÑɾ³ý
ruoyi-demo/pom.xml
ÎļþÒÑɾ³ý
ruoyi-demo/src/main/java/com/ruoyi/demo/controller/MailController.java
ÎļþÒÑɾ³ý
ruoyi-demo/src/main/java/com/ruoyi/demo/controller/RedisCacheController.java
ÎļþÒÑɾ³ý
ruoyi-demo/src/main/java/com/ruoyi/demo/controller/RedisPubSubController.java
ÎļþÒÑɾ³ý
ruoyi-demo/src/main/java/com/ruoyi/demo/controller/RedisRateLimiterController.java
ÎļþÒÑɾ³ý
ruoyi-demo/src/main/java/com/ruoyi/demo/controller/SmsController.java
ÎļþÒÑɾ³ý
ruoyi-demo/src/main/java/com/ruoyi/demo/controller/TestBatchController.java
ÎļþÒÑɾ³ý
ruoyi-demo/src/main/java/com/ruoyi/demo/controller/TestDemoController.java
ÎļþÒÑɾ³ý
ruoyi-demo/src/main/java/com/ruoyi/demo/controller/TestExcelController.java
ÎļþÒÑɾ³ý
ruoyi-demo/src/main/java/com/ruoyi/demo/controller/TestI18nController.java
ÎļþÒÑɾ³ý
ruoyi-demo/src/main/java/com/ruoyi/demo/controller/TestSensitiveController.java
ÎļþÒÑɾ³ý
ruoyi-demo/src/main/java/com/ruoyi/demo/controller/TestTreeController.java
ÎļþÒÑɾ³ý
ruoyi-demo/src/main/java/com/ruoyi/demo/controller/queue/BoundedQueueController.java
ÎļþÒÑɾ³ý
ruoyi-demo/src/main/java/com/ruoyi/demo/controller/queue/DelayedQueueController.java
ÎļþÒÑɾ³ý
ruoyi-demo/src/main/java/com/ruoyi/demo/controller/queue/PriorityQueueController.java
ÎļþÒÑɾ³ý
ruoyi-demo/src/main/java/com/ruoyi/demo/domain/TestDemo.java
ÎļþÒÑɾ³ý
ruoyi-demo/src/main/java/com/ruoyi/demo/domain/TestTree.java
ÎļþÒÑɾ³ý
ruoyi-demo/src/main/java/com/ruoyi/demo/domain/bo/TestDemoBo.java
ÎļþÒÑɾ³ý
ruoyi-demo/src/main/java/com/ruoyi/demo/domain/bo/TestTreeBo.java
ÎļþÒÑɾ³ý
ruoyi-demo/src/main/java/com/ruoyi/demo/mapper/TestDemoMapper.java
ÎļþÒÑɾ³ý
ruoyi-demo/src/main/java/com/ruoyi/demo/mapper/TestTreeMapper.java
ÎļþÒÑɾ³ý
ruoyi-demo/src/main/java/com/ruoyi/demo/service/ITestDemoService.java
ÎļþÒÑɾ³ý
ruoyi-demo/src/main/java/com/ruoyi/demo/service/impl/TestDemoServiceImpl.java
ÎļþÒÑɾ³ý
ruoyi-demo/src/main/java/com/ruoyi/demo/service/impl/TestTreeServiceImpl.java
ÎļþÒÑɾ³ý
ruoyi-framework/pom.xml
ÎļþÒÑɾ³ý
ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/LogAspect.java
ÎļþÒÑɾ³ý
ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/RateLimiterAspect.java
ÎļþÒÑɾ³ý
ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/RepeatSubmitAspect.java
ÎļþÒÑɾ³ý
ruoyi-framework/src/main/java/com/ruoyi/framework/config/ApplicationConfig.java
ÎļþÒÑɾ³ý
ruoyi-framework/src/main/java/com/ruoyi/framework/config/AsyncConfig.java
ÎļþÒÑɾ³ý
ruoyi-framework/src/main/java/com/ruoyi/framework/config/CaptchaConfig.java
ÎļþÒÑɾ³ý
ruoyi-framework/src/main/java/com/ruoyi/framework/config/FilterConfig.java
ÎļþÒÑɾ³ý
ruoyi-framework/src/main/java/com/ruoyi/framework/config/I18nConfig.java
ÎļþÒÑɾ³ý
ruoyi-framework/src/main/java/com/ruoyi/framework/config/JacksonConfig.java
ÎļþÒÑɾ³ý
ruoyi-framework/src/main/java/com/ruoyi/framework/config/MailConfig.java
ÎļþÒÑɾ³ý
ruoyi-framework/src/main/java/com/ruoyi/framework/config/MybatisPlusConfig.java
ÎļþÒÑɾ³ý
ruoyi-framework/src/main/java/com/ruoyi/framework/config/RedisConfig.java
ÎļþÒÑɾ³ý
ruoyi-framework/src/main/java/com/ruoyi/framework/config/ResourcesConfig.java
ÎļþÒÑɾ³ý
ruoyi-framework/src/main/java/com/ruoyi/framework/config/SaTokenConfig.java
ÎļþÒÑɾ³ý
ruoyi-framework/src/main/java/com/ruoyi/framework/config/SwaggerConfig.java
ÎļþÒÑɾ³ý
ruoyi-framework/src/main/java/com/ruoyi/framework/config/ThreadPoolConfig.java
ÎļþÒÑɾ³ý
ruoyi-framework/src/main/java/com/ruoyi/framework/config/UndertowConfig.java
ÎļþÒÑɾ³ý
ruoyi-framework/src/main/java/com/ruoyi/framework/config/ValidatorConfig.java
ÎļþÒÑɾ³ý
ruoyi-framework/src/main/java/com/ruoyi/framework/config/properties/CaptchaProperties.java
ÎļþÒÑɾ³ý
ruoyi-framework/src/main/java/com/ruoyi/framework/config/properties/MailProperties.java
ÎļþÒÑɾ³ý
ruoyi-framework/src/main/java/com/ruoyi/framework/config/properties/RedissonProperties.java
ÎļþÒÑɾ³ý
ruoyi-framework/src/main/java/com/ruoyi/framework/config/properties/SecurityProperties.java
ÎļþÒÑɾ³ý
ruoyi-framework/src/main/java/com/ruoyi/framework/config/properties/SwaggerProperties.java
ÎļþÒÑɾ³ý
ruoyi-framework/src/main/java/com/ruoyi/framework/config/properties/ThreadPoolProperties.java
ÎļþÒÑɾ³ý
ruoyi-framework/src/main/java/com/ruoyi/framework/config/properties/XssProperties.java
ÎļþÒÑɾ³ý
ruoyi-framework/src/main/java/com/ruoyi/framework/handler/CreateAndUpdateMetaObjectHandler.java
ÎļþÒÑɾ³ý
ruoyi-framework/src/main/java/com/ruoyi/framework/handler/KeyPrefixHandler.java
ÎļþÒÑɾ³ý
ruoyi-framework/src/main/java/com/ruoyi/framework/handler/OpenApiHandler.java
ÎļþÒÑɾ³ý
ruoyi-framework/src/main/java/com/ruoyi/framework/handler/PlusDataPermissionHandler.java
ÎļþÒÑɾ³ý
ruoyi-framework/src/main/java/com/ruoyi/framework/interceptor/PlusDataPermissionInterceptor.java
ÎļþÒÑɾ³ý
ruoyi-framework/src/main/java/com/ruoyi/framework/interceptor/PlusWebInvokeTimeInterceptor.java
ÎļþÒÑɾ³ý
ruoyi-framework/src/main/java/com/ruoyi/framework/jackson/BigNumberSerializer.java
ÎļþÒÑɾ³ý
ruoyi-framework/src/main/java/com/ruoyi/framework/listener/UserActionListener.java
ÎļþÒÑɾ³ý
ruoyi-framework/src/main/java/com/ruoyi/framework/manager/PlusSpringCacheManager.java
ÎļþÒÑɾ³ý
ruoyi-framework/src/main/java/com/ruoyi/framework/manager/ShutdownManager.java
ÎļþÒÑɾ³ý
ruoyi-framework/src/main/java/com/ruoyi/framework/satoken/dao/PlusSaTokenDao.java
ÎļþÒÑɾ³ý
ruoyi-framework/src/main/java/com/ruoyi/framework/satoken/service/SaPermissionImpl.java
ÎļþÒÑɾ³ý
ruoyi-framework/src/main/java/com/ruoyi/framework/web/exception/GlobalExceptionHandler.java
ÎļþÒÑɾ³ý
ruoyi-generator/pom.xml
ÎļþÒÑɾ³ý
ruoyi-generator/src/main/java/com/ruoyi/generator/controller/GenController.java
ÎļþÒÑɾ³ý
ruoyi-generator/src/main/java/com/ruoyi/generator/domain/GenTable.java
ÎļþÒÑɾ³ý
ruoyi-generator/src/main/java/com/ruoyi/generator/domain/GenTableColumn.java
ÎļþÒÑɾ³ý
ruoyi-generator/src/main/java/com/ruoyi/generator/mapper/GenTableColumnMapper.java
ÎļþÒÑɾ³ý
ruoyi-generator/src/main/java/com/ruoyi/generator/mapper/GenTableMapper.java
ÎļþÒÑɾ³ý
ruoyi-generator/src/main/java/com/ruoyi/generator/service/GenTableServiceImpl.java
ÎļþÒÑɾ³ý
ruoyi-generator/src/main/java/com/ruoyi/generator/service/IGenTableService.java
ÎļþÒÑɾ³ý
ruoyi-generator/src/main/java/com/ruoyi/generator/util/GenUtils.java
ÎļþÒÑɾ³ý
ruoyi-generator/src/main/java/com/ruoyi/generator/util/VelocityInitializer.java
ÎļþÒÑɾ³ý
ruoyi-generator/src/main/java/com/ruoyi/generator/util/VelocityUtils.java
ÎļþÒÑɾ³ý
ruoyi-generator/src/main/resources/generator.yml
ÎļþÒÑɾ³ý
ruoyi-generator/src/main/resources/mapper/generator/GenTableColumnMapper.xml
ÎļþÒÑɾ³ý
ruoyi-generator/src/main/resources/mapper/generator/GenTableMapper.xml
ÎļþÒÑɾ³ý
ruoyi-generator/src/main/resources/vm/java/bo.java.vm
ÎļþÒÑɾ³ý
ruoyi-generator/src/main/resources/vm/java/controller.java.vm
ÎļþÒÑɾ³ý
ruoyi-generator/src/main/resources/vm/java/domain.java.vm
ÎļþÒÑɾ³ý
ruoyi-generator/src/main/resources/vm/java/mapper.java.vm
ÎļþÒÑɾ³ý
ruoyi-generator/src/main/resources/vm/java/service.java.vm
ÎļþÒÑɾ³ý
ruoyi-generator/src/main/resources/vm/java/serviceImpl.java.vm
ÎļþÒÑɾ³ý
ruoyi-generator/src/main/resources/vm/java/vo.java.vm
ÎļþÒÑɾ³ý
ruoyi-job/pom.xml
ÎļþÒÑɾ³ý
ruoyi-modules/pom.xml
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>ruoyi-vue-plus</artifactId>
        <groupId>com.ruoyi</groupId>
        <version>${revision}</version>
        <relativePath>../pom.xml</relativePath>
    </parent>
    <modelVersion>4.0.0</modelVersion>
    <modules>
        <module>ruoyi-demo</module>
        <module>ruoyi-generator</module>
        <module>ruoyi-job</module>
        <module>ruoyi-system</module>
    </modules>
    <artifactId>ruoyi-modules</artifactId>
    <packaging>pom</packaging>
    <description>
        ruoyi-modules ä¸šåŠ¡æ¨¡å—
    </description>
</project>
ruoyi-modules/ruoyi-demo/pom.xml
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,75 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <groupId>com.ruoyi</groupId>
        <artifactId>ruoyi-modules</artifactId>
        <version>${revision}</version>
        <relativePath>../pom.xml</relativePath>
    </parent>
    <modelVersion>4.0.0</modelVersion>
    <artifactId>ruoyi-demo</artifactId>
    <description>
        demo模块
    </description>
    <dependencies>
        <!-- é€šç”¨å·¥å…·-->
        <dependency>
            <groupId>com.ruoyi</groupId>
            <artifactId>ruoyi-common-core</artifactId>
        </dependency>
        <dependency>
            <groupId>com.ruoyi</groupId>
            <artifactId>ruoyi-common-sms</artifactId>
        </dependency>
        <dependency>
            <groupId>com.ruoyi</groupId>
            <artifactId>ruoyi-common-mail</artifactId>
        </dependency>
        <dependency>
            <groupId>com.ruoyi</groupId>
            <artifactId>ruoyi-common-redis</artifactId>
        </dependency>
        <dependency>
            <groupId>com.ruoyi</groupId>
            <artifactId>ruoyi-common-idempotent</artifactId>
        </dependency>
        <dependency>
            <groupId>com.ruoyi</groupId>
            <artifactId>ruoyi-common-mybatis</artifactId>
        </dependency>
        <dependency>
            <groupId>com.ruoyi</groupId>
            <artifactId>ruoyi-common-log</artifactId>
        </dependency>
        <dependency>
            <groupId>com.ruoyi</groupId>
            <artifactId>ruoyi-common-excel</artifactId>
        </dependency>
        <dependency>
            <groupId>com.ruoyi</groupId>
            <artifactId>ruoyi-common-web</artifactId>
        </dependency>
        <dependency>
            <groupId>com.ruoyi</groupId>
            <artifactId>ruoyi-common-ratelimiter</artifactId>
        </dependency>
        <!-- çŸ­ä¿¡ ç”¨å“ªä¸ªå¯¼å…¥å“ªä¸ªä¾èµ– -->
<!--        <dependency>-->
<!--            <groupId>com.aliyun</groupId>-->
<!--            <artifactId>dysmsapi20170525</artifactId>-->
<!--        </dependency>-->
<!--        <dependency>-->
<!--            <groupId>com.tencentcloudapi</groupId>-->
<!--            <artifactId>tencentcloud-sdk-java-sms</artifactId>-->
<!--        </dependency>-->
    </dependencies>
</project>
ruoyi-modules/ruoyi-demo/src/main/java/com/ruoyi/demo/controller/MailController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,52 @@
package com.ruoyi.demo.controller;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.mail.utils.MailUtils;
import lombok.RequiredArgsConstructor;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.io.File;
/**
 * é‚®ä»¶å‘送案例
 *
 * @author Michelle.Chung
 */
@Validated
@RequiredArgsConstructor
@RestController
@RequestMapping("/demo/mail")
public class MailController {
    /**
     * å‘送邮件
     *
     * @param to      æŽ¥æ”¶äºº
     * @param subject æ ‡é¢˜
     * @param text    å†…容
     */
    @GetMapping("/sendSimpleMessage")
    public R<Void> sendSimpleMessage(String to, String subject, String text) {
        MailUtils.sendText(to, subject, text);
        return R.ok();
    }
    /**
     * å‘送邮件(带附件)
     *
     * @param to       æŽ¥æ”¶äºº
     * @param subject  æ ‡é¢˜
     * @param text     å†…容
     * @param filePath é™„件路径
     */
    @GetMapping("/sendMessageWithAttachment")
    public R<Void> sendMessageWithAttachment(String to, String subject, String text, String filePath) {
        MailUtils.sendText(to, subject, text, new File(filePath));
        return R.ok();
    }
}
ruoyi-modules/ruoyi-demo/src/main/java/com/ruoyi/demo/controller/RedisCacheController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,95 @@
package com.ruoyi.demo.controller;
import com.ruoyi.common.core.constant.CacheNames;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.redis.utils.RedisUtils;
import lombok.RequiredArgsConstructor;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.time.Duration;
/**
 * spring-cache æ¼”示案例
 *
 * @author Lion Li
 */
// ç±»çº§åˆ« ç¼“存统一配置
//@CacheConfig(cacheNames = CacheNames.DEMO_CACHE)
@RequiredArgsConstructor
@RestController
@RequestMapping("/demo/cache")
public class RedisCacheController {
    /**
     * æµ‹è¯• @Cacheable
     * <p>
     * è¡¨ç¤ºè¿™ä¸ªæ–¹æ³•有了缓存的功能,方法的返回值会被缓存下来
     * ä¸‹ä¸€æ¬¡è°ƒç”¨è¯¥æ–¹æ³•前,会去检查是否缓存中已经有值
     * å¦‚果有就直接返回,不调用方法
     * å¦‚果没有,就调用方法,然后把结果缓存起来
     * è¿™ä¸ªæ³¨è§£ã€Œä¸€èˆ¬ç”¨åœ¨æŸ¥è¯¢æ–¹æ³•上」
     * <p>
     * é‡ç‚¹è¯´æ˜Ž: ç¼“存注解严谨与其他筛选数据功能一起使用
     * ä¾‹å¦‚: æ•°æ®æƒé™æ³¨è§£ ä¼šé€ æˆ ç¼“存击穿 ä¸Ž æ•°æ®ä¸ä¸€è‡´é—®é¢˜
     * <p>
     * cacheNames å‘½åè§„则 æŸ¥çœ‹ {@link CacheNames} æ³¨é‡Š æ”¯æŒå¤šå‚æ•°
     */
    @Cacheable(cacheNames = "demo:cache#60s#10m#20", key = "#key", condition = "#key != null")
    @GetMapping("/test1")
    public R<String> test1(String key, String value) {
        return R.ok("操作成功", value);
    }
    /**
     * æµ‹è¯• @CachePut
     * <p>
     * åŠ äº†@CachePut注解的方法,会把方法的返回值put到缓存里面缓存起来,供其它地方使用
     * å®ƒã€Œé€šå¸¸ç”¨åœ¨æ–°å¢žæˆ–者实时更新方法上」
     * <p>
     * cacheNames å‘½åè§„则 æŸ¥çœ‹ {@link CacheNames} æ³¨é‡Š æ”¯æŒå¤šå‚æ•°
     */
    @CachePut(cacheNames = CacheNames.DEMO_CACHE, key = "#key", condition = "#key != null")
    @GetMapping("/test2")
    public R<String> test2(String key, String value) {
        return R.ok("操作成功", value);
    }
    /**
     * æµ‹è¯• @CacheEvict
     * <p>
     * ä½¿ç”¨äº†CacheEvict注解的方法,会清空指定缓存
     * ã€Œä¸€èˆ¬ç”¨åœ¨åˆ é™¤çš„æ–¹æ³•上」
     * <p>
     * cacheNames å‘½åè§„则 æŸ¥çœ‹ {@link CacheNames} æ³¨é‡Š æ”¯æŒå¤šå‚æ•°
     */
    @CacheEvict(cacheNames = CacheNames.DEMO_CACHE, key = "#key", condition = "#key != null")
    @GetMapping("/test3")
    public R<String> test3(String key, String value) {
        return R.ok("操作成功", value);
    }
    /**
     * æµ‹è¯•设置过期时间
     * æ‰‹åŠ¨è®¾ç½®è¿‡æœŸæ—¶é—´10秒
     * 11秒后获取 åˆ¤æ–­æ˜¯å¦ç›¸ç­‰
     */
    @GetMapping("/test6")
    public R<Boolean> test6(String key, String value) {
        RedisUtils.setCacheObject(key, value);
        boolean flag = RedisUtils.expire(key, Duration.ofSeconds(10));
        System.out.println("***********" + flag);
        try {
            Thread.sleep(11 * 1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        Object obj = RedisUtils.getCacheObject(key);
        return R.ok(value.equals(obj));
    }
}
ruoyi-modules/ruoyi-demo/src/main/java/com/ruoyi/demo/controller/RedisLockController.java
ruoyi-modules/ruoyi-demo/src/main/java/com/ruoyi/demo/controller/RedisPubSubController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,47 @@
package com.ruoyi.demo.controller;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.redis.utils.RedisUtils;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
 * Redis å‘布订阅 æ¼”示案例
 *
 * @author Lion Li
 */
@RequiredArgsConstructor
@RestController
@RequestMapping("/demo/redis/pubsub")
public class RedisPubSubController {
    /**
     * å‘布消息
     *
     * @param key   é€šé“Key
     * @param value å‘送内容
     */
    @GetMapping("/pub")
    public R<Void> pub(String key, String value) {
        RedisUtils.publish(key, value, consumer -> {
            System.out.println("发布通道 => " + key + ", å‘送值 => " + value);
        });
        return R.ok("操作成功");
    }
    /**
     * è®¢é˜…消息
     *
     * @param key é€šé“Key
     */
    @GetMapping("/sub")
    public R<Void> sub(String key) {
        RedisUtils.subscribe(key, String.class, msg -> {
            System.out.println("订阅通道 => " + key + ", æŽ¥æ”¶å€¼ => " + msg);
        });
        return R.ok("操作成功");
    }
}
ruoyi-modules/ruoyi-demo/src/main/java/com/ruoyi/demo/controller/RedisRateLimiterController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,52 @@
package com.ruoyi.demo.controller;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.ratelimiter.annotation.RateLimiter;
import com.ruoyi.common.ratelimiter.enums.LimitType;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
 * æµ‹è¯•分布式限流样例
 *
 * @author Lion Li
 */
@Slf4j
@RestController
@RequestMapping("/demo/rateLimiter")
public class RedisRateLimiterController {
    /**
     * æµ‹è¯•全局限流
     * å…¨å±€å½±å“
     */
    @RateLimiter(count = 2, time = 10)
    @GetMapping("/test")
    public R<String> test(String value) {
        return R.ok("操作成功", value);
    }
    /**
     * æµ‹è¯•请求IP限流
     * åŒä¸€IP请求受影响
     */
    @RateLimiter(count = 2, time = 10, limitType = LimitType.IP)
    @GetMapping("/testip")
    public R<String> testip(String value) {
        return R.ok("操作成功", value);
    }
    /**
     * æµ‹è¯•集群实例限流
     * å¯åŠ¨ä¸¤ä¸ªåŽç«¯æœåŠ¡äº’ä¸å½±å“
     */
    @RateLimiter(count = 2, time = 10, limitType = LimitType.CLUSTER)
    @GetMapping("/testcluster")
    public R<String> testcluster(String value) {
        return R.ok("操作成功", value);
    }
}
ruoyi-modules/ruoyi-demo/src/main/java/com/ruoyi/demo/controller/SmsController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,76 @@
package com.ruoyi.demo.controller;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.core.utils.SpringUtils;
import com.ruoyi.common.sms.config.properties.SmsProperties;
import com.ruoyi.common.sms.core.SmsTemplate;
import lombok.RequiredArgsConstructor;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.HashMap;
import java.util.Map;
/**
 * çŸ­ä¿¡æ¼”示案例
 * è¯·å…ˆé˜…读文档 å¦åˆ™æ— æ³•使用
 *
 * @author Lion Li
 * @version 4.2.0
 */
@Validated
@RequiredArgsConstructor
@RestController
@RequestMapping("/demo/sms")
public class SmsController {
    private final SmsProperties smsProperties;
//    private final SmsTemplate smsTemplate; // å¯ä»¥ä½¿ç”¨spring注入
//    private final AliyunSmsTemplate smsTemplate; // ä¹Ÿå¯ä»¥æ³¨å…¥æŸä¸ªåŽ‚å®¶çš„æ¨¡æ¿å·¥å…·
    /**
     * å‘送短信Aliyun
     *
     * @param phones     ç”µè¯å·
     * @param templateId æ¨¡æ¿ID
     */
    @GetMapping("/sendAliyun")
    public R<Object> sendAliyun(String phones, String templateId) {
        if (!smsProperties.getEnabled()) {
            return R.fail("当前系统没有开启短信功能!");
        }
        if (!SpringUtils.containsBean("aliyunSmsTemplate")) {
            return R.fail("阿里云依赖未引入!");
        }
        SmsTemplate smsTemplate = SpringUtils.getBean(SmsTemplate.class);
        Map<String, String> map = new HashMap<>(1);
        map.put("code", "1234");
        Object send = smsTemplate.send(phones, templateId, map);
        return R.ok(send);
    }
    /**
     * å‘送短信Tencent
     *
     * @param phones     ç”µè¯å·
     * @param templateId æ¨¡æ¿ID
     */
    @GetMapping("/sendTencent")
    public R<Object> sendTencent(String phones, String templateId) {
        if (!smsProperties.getEnabled()) {
            return R.fail("当前系统没有开启短信功能!");
        }
        if (!SpringUtils.containsBean("tencentSmsTemplate")) {
            return R.fail("腾讯云依赖未引入!");
        }
        SmsTemplate smsTemplate = SpringUtils.getBean(SmsTemplate.class);
        Map<String, String> map = new HashMap<>(1);
//        map.put("2", "测试测试");
        map.put("1", "1234");
        Object send = smsTemplate.send(phones, templateId, map);
        return R.ok(send);
    }
}
ruoyi-modules/ruoyi-demo/src/main/java/com/ruoyi/demo/controller/Swagger3DemoController.java
ruoyi-modules/ruoyi-demo/src/main/java/com/ruoyi/demo/controller/TestBatchController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,90 @@
package com.ruoyi.demo.controller;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.core.web.controller.BaseController;
import com.ruoyi.demo.domain.TestDemo;
import com.ruoyi.demo.mapper.TestDemoMapper;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.ArrayList;
import java.util.List;
/**
 * æµ‹è¯•批量方法
 *
 * @author Lion Li
 * @date 2021-05-30
 */
@RequiredArgsConstructor
@RestController
@RequestMapping("/demo/batch")
public class TestBatchController extends BaseController {
    /**
     * ä¸ºäº†ä¾¿äºŽæµ‹è¯• ç›´æŽ¥å¼•å…¥mapper
     */
    private final TestDemoMapper testDemoMapper;
    /**
     * æ–°å¢žæ‰¹é‡æ–¹æ³• å¯å®Œç¾Žæ›¿ä»£ saveBatch ç§’级插入上万数据 (对mysql负荷较大)
     * <p>
     * 3.5.0 ç‰ˆæœ¬ å¢žåŠ  rewriteBatchedStatements=true æ‰¹å¤„理参数 ä½¿ MP åŽŸç”Ÿæ‰¹å¤„ç†å¯ä»¥è¾¾åˆ°åŒæ ·çš„é€Ÿåº¦
     */
    @PostMapping("/add")
//    @DS("slave")
    public R<Void> add() {
        List<TestDemo> list = new ArrayList<>();
        for (int i = 0; i < 1000; i++) {
            TestDemo testDemo = new TestDemo();
            testDemo.setOrderNum(-1);
            testDemo.setTestKey("批量新增");
            testDemo.setValue("测试新增");
            list.add(testDemo);
        }
        return toAjax(testDemoMapper.insertBatch(list));
    }
    /**
     * æ–°å¢žæˆ–æ›´æ–° å¯å®Œç¾Žæ›¿ä»£ saveOrUpdateBatch é«˜æ€§èƒ½
     * <p>
     * 3.5.0 ç‰ˆæœ¬ å¢žåŠ  rewriteBatchedStatements=true æ‰¹å¤„理参数 ä½¿ MP åŽŸç”Ÿæ‰¹å¤„ç†å¯ä»¥è¾¾åˆ°åŒæ ·çš„é€Ÿåº¦
     */
    @PostMapping("/addOrUpdate")
//    @DS("slave")
    public R<Void> addOrUpdate() {
        List<TestDemo> list = new ArrayList<>();
        for (int i = 0; i < 1000; i++) {
            TestDemo testDemo = new TestDemo();
            testDemo.setOrderNum(-1);
            testDemo.setTestKey("批量新增");
            testDemo.setValue("测试新增");
            list.add(testDemo);
        }
        testDemoMapper.insertBatch(list);
        for (int i = 0; i < list.size(); i++) {
            TestDemo testDemo = list.get(i);
            testDemo.setTestKey("批量新增或修改");
            testDemo.setValue("批量新增或修改");
            if (i % 2 == 0) {
                testDemo.setId(null);
            }
        }
        return toAjax(testDemoMapper.insertOrUpdateBatch(list));
    }
    /**
     * åˆ é™¤æ‰¹é‡æ–¹æ³•
     */
    @DeleteMapping()
//    @DS("slave")
    public R<Void> remove() {
        return toAjax(testDemoMapper.delete(new LambdaQueryWrapper<TestDemo>()
            .eq(TestDemo::getOrderNum, -1L)));
    }
}
ruoyi-modules/ruoyi-demo/src/main/java/com/ruoyi/demo/controller/TestDemoController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,148 @@
package com.ruoyi.demo.controller;
import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.hutool.core.bean.BeanUtil;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.core.utils.ValidatorUtils;
import com.ruoyi.common.core.validate.AddGroup;
import com.ruoyi.common.core.validate.EditGroup;
import com.ruoyi.common.core.validate.QueryGroup;
import com.ruoyi.common.core.web.controller.BaseController;
import com.ruoyi.common.idempotent.annotation.RepeatSubmit;
import com.ruoyi.common.mybatis.core.page.PageQuery;
import com.ruoyi.common.mybatis.core.page.TableDataInfo;
import com.ruoyi.common.excel.core.ExcelResult;
import com.ruoyi.common.excel.utils.ExcelUtil;
import com.ruoyi.common.log.annotation.Log;
import com.ruoyi.common.log.enums.BusinessType;
import com.ruoyi.demo.domain.TestDemo;
import com.ruoyi.demo.domain.bo.TestDemoBo;
import com.ruoyi.demo.domain.bo.TestDemoImportVo;
import com.ruoyi.demo.domain.vo.TestDemoVo;
import com.ruoyi.demo.service.ITestDemoService;
import lombok.RequiredArgsConstructor;
import org.springframework.http.MediaType;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.TimeUnit;
/**
 * æµ‹è¯•单表Controller
 *
 * @author Lion Li
 * @date 2021-07-26
 */
@Validated
@RequiredArgsConstructor
@RestController
@RequestMapping("/demo/demo")
public class TestDemoController extends BaseController {
    private final ITestDemoService iTestDemoService;
    /**
     * æŸ¥è¯¢æµ‹è¯•单表列表
     */
    @SaCheckPermission("demo:demo:list")
    @GetMapping("/list")
    public TableDataInfo<TestDemoVo> list(@Validated(QueryGroup.class) TestDemoBo bo, PageQuery pageQuery) {
        return iTestDemoService.queryPageList(bo, pageQuery);
    }
    /**
     * è‡ªå®šä¹‰åˆ†é¡µæŸ¥è¯¢
     */
    @SaCheckPermission("demo:demo:list")
    @GetMapping("/page")
    public TableDataInfo<TestDemoVo> page(@Validated(QueryGroup.class) TestDemoBo bo, PageQuery pageQuery) {
        return iTestDemoService.customPageList(bo, pageQuery);
    }
    /**
     * å¯¼å…¥æ•°æ®
     *
     * @param file å¯¼å…¥æ–‡ä»¶
     */
    @Log(title = "测试单表", businessType = BusinessType.IMPORT)
    @SaCheckPermission("demo:demo:import")
    @PostMapping(value = "/importData", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
    public R<Void> importData(@RequestPart("file") MultipartFile file) throws Exception {
        ExcelResult<TestDemoImportVo> excelResult = ExcelUtil.importExcel(file.getInputStream(), TestDemoImportVo.class, true);
        List<TestDemoImportVo> volist = excelResult.getList();
        List<TestDemo> list = BeanUtil.copyToList(volist, TestDemo.class);
        iTestDemoService.saveBatch(list);
        return R.ok(excelResult.getAnalysis());
    }
    /**
     * å¯¼å‡ºæµ‹è¯•单表列表
     */
    @SaCheckPermission("demo:demo:export")
    @Log(title = "测试单表", businessType = BusinessType.EXPORT)
    @PostMapping("/export")
    public void export(@Validated TestDemoBo bo, HttpServletResponse response) {
        List<TestDemoVo> list = iTestDemoService.queryList(bo);
        // æµ‹è¯•雪花id导出
//        for (TestDemoVo vo : list) {
//            vo.setId(1234567891234567893L);
//        }
        ExcelUtil.exportExcel(list, "测试单表", TestDemoVo.class, response);
    }
    /**
     * èŽ·å–æµ‹è¯•å•è¡¨è¯¦ç»†ä¿¡æ¯
     *
     * @param id æµ‹è¯•ID
     */
    @SaCheckPermission("demo:demo:query")
    @GetMapping("/{id}")
    public R<TestDemoVo> getInfo(@NotNull(message = "主键不能为空")
                                 @PathVariable("id") Long id) {
        return R.ok(iTestDemoService.queryById(id));
    }
    /**
     * æ–°å¢žæµ‹è¯•单表
     */
    @SaCheckPermission("demo:demo:add")
    @Log(title = "测试单表", businessType = BusinessType.INSERT)
    @RepeatSubmit(interval = 2, timeUnit = TimeUnit.SECONDS, message = "{repeat.submit.message}")
    @PostMapping()
    public R<Void> add(@RequestBody TestDemoBo bo) {
        // ä½¿ç”¨æ ¡éªŒå·¥å…·å¯¹æ ‡ @Validated(AddGroup.class) æ³¨è§£
        // ç”¨äºŽåœ¨éž Controller çš„地方校验对象
        ValidatorUtils.validate(bo, AddGroup.class);
        return toAjax(iTestDemoService.insertByBo(bo));
    }
    /**
     * ä¿®æ”¹æµ‹è¯•单表
     */
    @SaCheckPermission("demo:demo:edit")
    @Log(title = "测试单表", businessType = BusinessType.UPDATE)
    @RepeatSubmit
    @PutMapping()
    public R<Void> edit(@Validated(EditGroup.class) @RequestBody TestDemoBo bo) {
        return toAjax(iTestDemoService.updateByBo(bo));
    }
    /**
     * åˆ é™¤æµ‹è¯•单表
     *
     * @param ids æµ‹è¯•ID串
     */
    @SaCheckPermission("demo:demo:remove")
    @Log(title = "测试单表", businessType = BusinessType.DELETE)
    @DeleteMapping("/{ids}")
    public R<Void> remove(@NotEmpty(message = "主键不能为空")
                          @PathVariable Long[] ids) {
        return toAjax(iTestDemoService.deleteWithValidByIds(Arrays.asList(ids), true));
    }
}
ruoyi-modules/ruoyi-demo/src/main/java/com/ruoyi/demo/controller/TestExcelController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,97 @@
package com.ruoyi.demo.controller;
import cn.hutool.core.collection.CollUtil;
import com.ruoyi.common.excel.utils.ExcelUtil;
import lombok.AllArgsConstructor;
import lombok.Data;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import jakarta.servlet.http.HttpServletResponse;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
 * æµ‹è¯•Excel功能
 *
 * @author Lion Li
 */
@RestController
@RequestMapping("/demo/excel")
public class TestExcelController {
    /**
     * å•列表多数据
     */
    @GetMapping("/exportTemplateOne")
    public void exportTemplateOne(HttpServletResponse response) {
        Map<String, String> map = new HashMap<>();
        map.put("title", "单列表多数据");
        map.put("test1", "数据测试1");
        map.put("test2", "数据测试2");
        map.put("test3", "数据测试3");
        map.put("test4", "数据测试4");
        map.put("testTest", "666");
        List<TestObj> list = new ArrayList<>();
        list.add(new TestObj("单列表测试1", "列表测试1", "列表测试2", "列表测试3", "列表测试4"));
        list.add(new TestObj("单列表测试2", "列表测试5", "列表测试6", "列表测试7", "列表测试8"));
        list.add(new TestObj("单列表测试3", "列表测试9", "列表测试10", "列表测试11", "列表测试12"));
        ExcelUtil.exportTemplate(CollUtil.newArrayList(map, list), "单列表.xlsx", "excel/单列表.xlsx", response);
    }
    /**
     * å¤šåˆ—表多数据
     */
    @GetMapping("/exportTemplateMuliti")
    public void exportTemplateMuliti(HttpServletResponse response) {
        Map<String, String> map = new HashMap<>();
        map.put("title1", "标题1");
        map.put("title2", "标题2");
        map.put("title3", "标题3");
        map.put("title4", "标题4");
        map.put("author", "Lion Li");
        List<TestObj1> list1 = new ArrayList<>();
        list1.add(new TestObj1("list1测试1", "list1测试2", "list1测试3"));
        list1.add(new TestObj1("list1测试4", "list1测试5", "list1测试6"));
        list1.add(new TestObj1("list1测试7", "list1测试8", "list1测试9"));
        List<TestObj1> list2 = new ArrayList<>();
        list2.add(new TestObj1("list2测试1", "list2测试2", "list2测试3"));
        list2.add(new TestObj1("list2测试4", "list2测试5", "list2测试6"));
        List<TestObj1> list3 = new ArrayList<>();
        list3.add(new TestObj1("list3测试1", "list3测试2", "list3测试3"));
        List<TestObj1> list4 = new ArrayList<>();
        list4.add(new TestObj1("list4测试1", "list4测试2", "list4测试3"));
        list4.add(new TestObj1("list4测试4", "list4测试5", "list4测试6"));
        list4.add(new TestObj1("list4测试7", "list4测试8", "list4测试9"));
        list4.add(new TestObj1("list4测试10", "list4测试11", "list4测试12"));
        Map<String, Object> multiListMap = new HashMap<>();
        multiListMap.put("map", map);
        multiListMap.put("data1", list1);
        multiListMap.put("data2", list2);
        multiListMap.put("data3", list3);
        multiListMap.put("data4", list4);
        ExcelUtil.exportTemplateMultiList(multiListMap, "多列表.xlsx", "excel/多列表.xlsx", response);
    }
    @Data
    @AllArgsConstructor
    static class TestObj1 {
        private String test1;
        private String test2;
        private String test3;
    }
    @Data
    @AllArgsConstructor
    static class TestObj {
        private String name;
        private String list1;
        private String list2;
        private String list3;
        private String list4;
    }
}
ruoyi-modules/ruoyi-demo/src/main/java/com/ruoyi/demo/controller/TestI18nController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,71 @@
package com.ruoyi.demo.controller;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.core.utils.MessageUtils;
import lombok.Data;
import org.hibernate.validator.constraints.Range;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
/**
 * æµ‹è¯•国际化
 *
 * @author Lion Li
 */
@Validated
@RestController
@RequestMapping("/demo/i18n")
public class TestI18nController {
    /**
     * é€šè¿‡code获取国际化内容
     * code为 messages.properties ä¸­çš„ key
     * <p>
     * æµ‹è¯•使用 user.register.success
     *
     * @param code å›½é™…化code
     */
    @GetMapping()
    public R<Void> get(String code) {
        return R.ok(MessageUtils.message(code));
    }
    /**
     * Validator æ ¡éªŒå›½é™…化
     * ä¸ä¼ å€¼ åˆ†åˆ«æŸ¥çœ‹å¼‚常返回
     * <p>
     * æµ‹è¯•使用 not.null
     */
    @GetMapping("/test1")
    public R<Void> test1(@NotBlank(message = "{not.null}") String str) {
        return R.ok(str);
    }
    /**
     * Bean æ ¡éªŒå›½é™…化
     * ä¸ä¼ å€¼ åˆ†åˆ«æŸ¥çœ‹å¼‚常返回
     * <p>
     * æµ‹è¯•使用 not.null
     */
    @GetMapping("/test2")
    public R<TestI18nBo> test2(@Validated TestI18nBo bo) {
        return R.ok(bo);
    }
    @Data
    public static class TestI18nBo {
        @NotBlank(message = "{not.null}")
        private String name;
        @NotNull(message = "{not.null}")
        @Range(min = 0, max = 100, message = "{length.not.valid}")
        private Integer age;
    }
}
ruoyi-modules/ruoyi-demo/src/main/java/com/ruoyi/demo/controller/TestSensitiveController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,75 @@
package com.ruoyi.demo.controller;
import com.ruoyi.common.core.annotation.Sensitive;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.core.enums.SensitiveStrategy;
import com.ruoyi.common.core.web.controller.BaseController;
import lombok.Data;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
 * æµ‹è¯•数据脱敏控制器
 * <p>
 * é»˜è®¤ç®¡ç†å‘˜ä¸è¿‡æ»¤
 * éœ€è‡ªè¡Œæ ¹æ®ä¸šåŠ¡é‡å†™å®žçŽ°
 *
 * @author Lion Li
 * @version 3.6.0
 * @see com.ruoyi.common.core.service.SensitiveService
 */
@RestController
@RequestMapping("/demo/sensitive")
public class TestSensitiveController extends BaseController {
    /**
     * æµ‹è¯•数据脱敏
     */
    @GetMapping("/test")
    public R<TestSensitive> test() {
        TestSensitive testSensitive = new TestSensitive();
        testSensitive.setIdCard("210397198608215431");
        testSensitive.setPhone("17640125371");
        testSensitive.setAddress("北京市朝阳区某某四合院1203室");
        testSensitive.setEmail("17640125371@163.com");
        testSensitive.setBankCard("6226456952351452853");
        return R.ok(testSensitive);
    }
    @Data
    static class TestSensitive {
        /**
         * èº«ä»½è¯
         */
        @Sensitive(strategy = SensitiveStrategy.ID_CARD)
        private String idCard;
        /**
         * ç”µè¯
         */
        @Sensitive(strategy = SensitiveStrategy.PHONE)
        private String phone;
        /**
         * åœ°å€
         */
        @Sensitive(strategy = SensitiveStrategy.ADDRESS)
        private String address;
        /**
         * é‚®ç®±
         */
        @Sensitive(strategy = SensitiveStrategy.EMAIL)
        private String email;
        /**
         * é“¶è¡Œå¡
         */
        @Sensitive(strategy = SensitiveStrategy.BANK_CARD)
        private String bankCard;
    }
}
ruoyi-modules/ruoyi-demo/src/main/java/com/ruoyi/demo/controller/TestTreeController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,107 @@
package com.ruoyi.demo.controller;
import cn.dev33.satoken.annotation.SaCheckPermission;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.core.validate.AddGroup;
import com.ruoyi.common.core.validate.EditGroup;
import com.ruoyi.common.core.validate.QueryGroup;
import com.ruoyi.common.core.web.controller.BaseController;
import com.ruoyi.common.excel.utils.ExcelUtil;
import com.ruoyi.common.idempotent.annotation.RepeatSubmit;
import com.ruoyi.common.log.annotation.Log;
import com.ruoyi.common.log.enums.BusinessType;
import com.ruoyi.demo.domain.bo.TestTreeBo;
import com.ruoyi.demo.domain.vo.TestTreeVo;
import com.ruoyi.demo.service.ITestTreeService;
import lombok.RequiredArgsConstructor;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import java.util.Arrays;
import java.util.List;
/**
 * æµ‹è¯•树表Controller
 *
 * @author Lion Li
 * @date 2021-07-26
 */
@Validated
@RequiredArgsConstructor
@RestController
@RequestMapping("/demo/tree")
public class TestTreeController extends BaseController {
    private final ITestTreeService iTestTreeService;
    /**
     * æŸ¥è¯¢æµ‹è¯•树表列表
     */
    @SaCheckPermission("demo:tree:list")
    @GetMapping("/list")
    public R<List<TestTreeVo>> list(@Validated(QueryGroup.class) TestTreeBo bo) {
        List<TestTreeVo> list = iTestTreeService.queryList(bo);
        return R.ok(list);
    }
    /**
     * å¯¼å‡ºæµ‹è¯•树表列表
     */
    @SaCheckPermission("demo:tree:export")
    @Log(title = "测试树表", businessType = BusinessType.EXPORT)
    @GetMapping("/export")
    public void export(@Validated TestTreeBo bo, HttpServletResponse response) {
        List<TestTreeVo> list = iTestTreeService.queryList(bo);
        ExcelUtil.exportExcel(list, "测试树表", TestTreeVo.class, response);
    }
    /**
     * èŽ·å–æµ‹è¯•æ ‘è¡¨è¯¦ç»†ä¿¡æ¯
     *
     * @param id æµ‹è¯•æ ‘ID
     */
    @SaCheckPermission("demo:tree:query")
    @GetMapping("/{id}")
    public R<TestTreeVo> getInfo(@NotNull(message = "主键不能为空")
                                 @PathVariable("id") Long id) {
        return R.ok(iTestTreeService.queryById(id));
    }
    /**
     * æ–°å¢žæµ‹è¯•树表
     */
    @SaCheckPermission("demo:tree:add")
    @Log(title = "测试树表", businessType = BusinessType.INSERT)
    @RepeatSubmit
    @PostMapping()
    public R<Void> add(@Validated(AddGroup.class) @RequestBody TestTreeBo bo) {
        return toAjax(iTestTreeService.insertByBo(bo));
    }
    /**
     * ä¿®æ”¹æµ‹è¯•树表
     */
    @SaCheckPermission("demo:tree:edit")
    @Log(title = "测试树表", businessType = BusinessType.UPDATE)
    @RepeatSubmit
    @PutMapping()
    public R<Void> edit(@Validated(EditGroup.class) @RequestBody TestTreeBo bo) {
        return toAjax(iTestTreeService.updateByBo(bo));
    }
    /**
     * åˆ é™¤æµ‹è¯•树表
     *
     * @param ids æµ‹è¯•æ ‘ID串
     */
    @SaCheckPermission("demo:tree:remove")
    @Log(title = "测试树表", businessType = BusinessType.DELETE)
    @DeleteMapping("/{ids}")
    public R<Void> remove(@NotEmpty(message = "主键不能为空")
                          @PathVariable Long[] ids) {
        return toAjax(iTestTreeService.deleteWithValidByIds(Arrays.asList(ids), true));
    }
}
ruoyi-modules/ruoyi-demo/src/main/java/com/ruoyi/demo/controller/package-info.java
ruoyi-modules/ruoyi-demo/src/main/java/com/ruoyi/demo/controller/queue/BoundedQueueController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,90 @@
package com.ruoyi.demo.controller.queue;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.redis.utils.QueueUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
 * æœ‰ç•Œé˜Ÿåˆ— æ¼”示案例
 * <p>
 * è½»é‡çº§é˜Ÿåˆ— é‡é‡çº§æ•°æ®é‡ è¯·ä½¿ç”¨ MQ
 * <p>
 * é›†ç¾¤æµ‹è¯•通过 åŒä¸€ä¸ªæ•°æ®åªä¼šè¢«æ¶ˆè´¹ä¸€æ¬¡ åšå¥½äº‹åŠ¡è¡¥å¿
 * é›†ç¾¤æµ‹è¯•流程 åœ¨å…¶ä¸­ä¸€å°å‘送数据 ä¸¤ç«¯åˆ†åˆ«è°ƒç”¨èŽ·å–æŽ¥å£ ä¸€æ¬¡èŽ·å–ä¸€æ¡
 *
 * @author Lion Li
 * @version 3.6.0
 */
@Slf4j
@RequiredArgsConstructor
@RestController
@RequestMapping("/demo/queue/bounded")
public class BoundedQueueController {
    /**
     * æ·»åŠ é˜Ÿåˆ—æ•°æ®
     *
     * @param queueName é˜Ÿåˆ—名
     * @param capacity  å®¹é‡
     */
    @GetMapping("/add")
    public R<Void> add(String queueName, int capacity) {
        // ç”¨å®Œäº†ä¸€å®šè¦é”€æ¯ å¦åˆ™ä¼šä¸€ç›´å­˜åœ¨
        boolean b = QueueUtils.destroyQueue(queueName);
        log.info("通道: {} , åˆ é™¤: {}", queueName, b);
        // åˆå§‹åŒ–设置一次即可
        if (QueueUtils.trySetBoundedQueueCapacity(queueName, capacity)) {
            log.info("通道: {} , è®¾ç½®å®¹é‡: {}", queueName, capacity);
        } else {
            log.info("通道: {} , è®¾ç½®å®¹é‡å¤±è´¥", queueName);
            return R.fail("操作失败");
        }
        for (int i = 0; i < 11; i++) {
            String data = "data-" + i;
            boolean flag = QueueUtils.addBoundedQueueObject(queueName, data);
            if (flag == false) {
                log.info("通道: {} , å‘送数据: {} å¤±è´¥, é€šé“已满", queueName, data);
            } else {
                log.info("通道: {} , å‘送数据: {}", queueName, data);
            }
        }
        return R.ok("操作成功");
    }
    /**
     * åˆ é™¤é˜Ÿåˆ—数据
     *
     * @param queueName é˜Ÿåˆ—名
     */
    @GetMapping("/remove")
    public R<Void> remove(String queueName) {
        String data = "data-" + 5;
        if (QueueUtils.removeQueueObject(queueName, data)) {
            log.info("通道: {} , åˆ é™¤æ•°æ®: {}", queueName, data);
        } else {
            return R.fail("操作失败");
        }
        return R.ok("操作成功");
    }
    /**
     * èŽ·å–é˜Ÿåˆ—æ•°æ®
     *
     * @param queueName é˜Ÿåˆ—名
     */
    @GetMapping("/get")
    public R<Void> get(String queueName) {
        String data;
        do {
            data = QueueUtils.getQueueObject(queueName);
            log.info("通道: {} , èŽ·å–æ•°æ®: {}", queueName, data);
        } while (data != null);
        return R.ok("操作成功");
    }
}
ruoyi-modules/ruoyi-demo/src/main/java/com/ruoyi/demo/controller/queue/DelayedQueueController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,90 @@
package com.ruoyi.demo.controller.queue;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.redis.utils.QueueUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.concurrent.TimeUnit;
/**
 * å»¶è¿Ÿé˜Ÿåˆ— æ¼”示案例
 * <p>
 * è½»é‡çº§é˜Ÿåˆ— é‡é‡çº§æ•°æ®é‡ è¯·ä½¿ç”¨ MQ
 * ä¾‹å¦‚: åˆ›å»ºè®¢å•30分钟后过期处理
 * <p>
 * é›†ç¾¤æµ‹è¯•通过 åŒä¸€ä¸ªæ•°æ®åªä¼šè¢«æ¶ˆè´¹ä¸€æ¬¡ åšå¥½äº‹åŠ¡è¡¥å¿
 * é›†ç¾¤æµ‹è¯•流程 ä¸¤å°é›†ç¾¤åˆ†åˆ«å¼€å¯è®¢é˜… åœ¨å…¶ä¸­ä¸€å°å‘送数据 è§‚察接收消息的规律
 *
 * @author Lion Li
 * @version 3.6.0
 */
@Slf4j
@RequiredArgsConstructor
@RestController
@RequestMapping("/demo/queue/delayed")
public class DelayedQueueController {
    /**
     * è®¢é˜…队列
     *
     * @param queueName é˜Ÿåˆ—名
     */
    @GetMapping("/subscribe")
    public R<Void> subscribe(String queueName) {
        log.info("通道: {} ç›‘听中......", queueName);
        // é¡¹ç›®åˆå§‹åŒ–设置一次即可
        QueueUtils.subscribeBlockingQueue(queueName, (String orderNum) -> {
            // è§‚察接收时间
            log.info("通道: {}, æ”¶åˆ°æ•°æ®: {}", queueName, orderNum);
        });
        return R.ok("操作成功");
    }
    /**
     * æ·»åŠ é˜Ÿåˆ—æ•°æ®
     *
     * @param queueName é˜Ÿåˆ—名
     * @param orderNum  è®¢å•号
     * @param time      å»¶è¿Ÿæ—¶é—´(秒)
     */
    @GetMapping("/add")
    public R<Void> add(String queueName, String orderNum, Long time) {
        QueueUtils.addDelayedQueueObject(queueName, orderNum, time, TimeUnit.SECONDS);
        // è§‚察发送时间
        log.info("通道: {} , å‘送数据: {}", queueName, orderNum);
        return R.ok("操作成功");
    }
    /**
     * åˆ é™¤é˜Ÿåˆ—数据
     *
     * @param queueName é˜Ÿåˆ—名
     * @param orderNum  è®¢å•号
     */
    @GetMapping("/remove")
    public R<Void> remove(String queueName, String orderNum) {
        if (QueueUtils.removeDelayedQueueObject(queueName, orderNum)) {
            log.info("通道: {} , åˆ é™¤æ•°æ®: {}", queueName, orderNum);
        } else {
            return R.fail("操作失败");
        }
        return R.ok("操作成功");
    }
    /**
     * é”€æ¯é˜Ÿåˆ—
     *
     * @param queueName é˜Ÿåˆ—名
     */
    @GetMapping("/destroy")
    public R<Void> destroy(String queueName) {
        // ç”¨å®Œäº†ä¸€å®šè¦é”€æ¯ å¦åˆ™ä¼šä¸€ç›´å­˜åœ¨
        QueueUtils.destroyDelayedQueue(queueName);
        return R.ok("操作成功");
    }
}
ruoyi-modules/ruoyi-demo/src/main/java/com/ruoyi/demo/controller/queue/PriorityDemo.java
ruoyi-modules/ruoyi-demo/src/main/java/com/ruoyi/demo/controller/queue/PriorityQueueController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,89 @@
package com.ruoyi.demo.controller.queue;
import cn.hutool.core.util.RandomUtil;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.redis.utils.QueueUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
 * ä¼˜å…ˆé˜Ÿåˆ— æ¼”示案例
 * <p>
 * è½»é‡çº§é˜Ÿåˆ— é‡é‡çº§æ•°æ®é‡ è¯·ä½¿ç”¨ MQ
 * <p>
 * é›†ç¾¤æµ‹è¯•通过 åŒä¸€ä¸ªæ¶ˆæ¯åªä¼šè¢«æ¶ˆè´¹ä¸€æ¬¡ åšå¥½äº‹åŠ¡è¡¥å¿
 * é›†ç¾¤æµ‹è¯•流程 åœ¨å…¶ä¸­ä¸€å°å‘送数据 ä¸¤ç«¯åˆ†åˆ«è°ƒç”¨èŽ·å–æŽ¥å£ ä¸€æ¬¡èŽ·å–ä¸€æ¡
 *
 * @author Lion Li
 * @version 3.6.0
 */
@Slf4j
@RequiredArgsConstructor
@RestController
@RequestMapping("/demo/queue/priority")
public class PriorityQueueController {
    /**
     * æ·»åŠ é˜Ÿåˆ—æ•°æ®
     *
     * @param queueName é˜Ÿåˆ—名
     */
    @GetMapping("/add")
    public R<Void> add(String queueName) {
        // ç”¨å®Œäº†ä¸€å®šè¦é”€æ¯ å¦åˆ™ä¼šä¸€ç›´å­˜åœ¨
        boolean b = QueueUtils.destroyQueue(queueName);
        log.info("通道: {} , åˆ é™¤: {}", queueName, b);
        for (int i = 0; i < 10; i++) {
            int randomNum = RandomUtil.randomInt(10);
            PriorityDemo data = new PriorityDemo();
            data.setName("data-" + i);
            data.setOrderNum(randomNum);
            if (QueueUtils.addPriorityQueueObject(queueName, data)) {
                log.info("通道: {} , å‘送数据: {}", queueName, data);
            } else {
                log.info("通道: {} , å‘送数据: {}, å‘送失败", queueName, data);
            }
        }
        return R.ok("操作成功");
    }
    /**
     * åˆ é™¤é˜Ÿåˆ—数据
     *
     * @param queueName é˜Ÿåˆ—名
     * @param name      å¯¹è±¡å
     * @param orderNum  æŽ’序号
     */
    @GetMapping("/remove")
    public R<Void> remove(String queueName, String name, Integer orderNum) {
        PriorityDemo data = new PriorityDemo();
        data.setName(name);
        data.setOrderNum(orderNum);
        if (QueueUtils.removeQueueObject(queueName, data)) {
            log.info("通道: {} , åˆ é™¤æ•°æ®: {}", queueName, data);
        } else {
            return R.fail("操作失败");
        }
        return R.ok("操作成功");
    }
    /**
     * èŽ·å–é˜Ÿåˆ—æ•°æ®
     *
     * @param queueName é˜Ÿåˆ—名
     */
    @GetMapping("/get")
    public R<Void> get(String queueName) {
        PriorityDemo data;
        do {
            data = QueueUtils.getQueueObject(queueName);
            log.info("通道: {} , èŽ·å–æ•°æ®: {}", queueName, data);
        } while (data != null);
        return R.ok("操作成功");
    }
}
ruoyi-modules/ruoyi-demo/src/main/java/com/ruoyi/demo/domain/TestDemo.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,68 @@
package com.ruoyi.demo.domain;
import com.baomidou.mybatisplus.annotation.*;
import com.ruoyi.common.core.web.domain.BaseEntity;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.io.Serial;
/**
 * æµ‹è¯•单表对象 test_demo
 *
 * @author Lion Li
 * @date 2021-07-26
 */
@Data
@EqualsAndHashCode(callSuper = true)
@TableName("test_demo")
public class TestDemo extends BaseEntity {
    @Serial
    private static final long serialVersionUID = 1L;
    /**
     * ä¸»é”®
     */
    @TableId(value = "id")
    private Long id;
    /**
     * éƒ¨é—¨id
     */
    private Long deptId;
    /**
     * ç”¨æˆ·id
     */
    private Long userId;
    /**
     * æŽ’序号
     */
    @OrderBy(asc = false, sort = 1)
    private Integer orderNum;
    /**
     * key键
     */
    private String testKey;
    /**
     * å€¼
     */
    private String value;
    /**
     * ç‰ˆæœ¬
     */
    @Version
    private Long version;
    /**
     * åˆ é™¤æ ‡å¿—
     */
    @TableLogic
    private Long delFlag;
}
ruoyi-modules/ruoyi-demo/src/main/java/com/ruoyi/demo/domain/TestTree.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,60 @@
package com.ruoyi.demo.domain;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableLogic;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.annotation.Version;
import com.ruoyi.common.core.web.domain.TreeEntity;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.io.Serial;
/**
 * æµ‹è¯•树表对象 test_tree
 *
 * @author Lion Li
 * @date 2021-07-26
 */
@Data
@EqualsAndHashCode(callSuper = true)
@TableName("test_tree")
public class TestTree extends TreeEntity<TestTree> {
    @Serial
    private static final long serialVersionUID = 1L;
    /**
     * ä¸»é”®
     */
    @TableId(value = "id")
    private Long id;
    /**
     * éƒ¨é—¨id
     */
    private Long deptId;
    /**
     * ç”¨æˆ·id
     */
    private Long userId;
    /**
     * æ ‘节点名
     */
    private String treeName;
    /**
     * ç‰ˆæœ¬
     */
    @Version
    private Long version;
    /**
     * åˆ é™¤æ ‡å¿—
     */
    @TableLogic
    private Long delFlag;
}
ruoyi-modules/ruoyi-demo/src/main/java/com/ruoyi/demo/domain/bo/TestDemoBo.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,59 @@
package com.ruoyi.demo.domain.bo;
import com.ruoyi.common.core.validate.AddGroup;
import com.ruoyi.common.core.validate.EditGroup;
import com.ruoyi.common.core.web.domain.BaseEntity;
import lombok.Data;
import lombok.EqualsAndHashCode;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
/**
 * æµ‹è¯•单表业务对象 test_demo
 *
 * @author Lion Li
 * @date 2021-07-26
 */
@Data
@EqualsAndHashCode(callSuper = true)
public class TestDemoBo extends BaseEntity {
    /**
     * ä¸»é”®
     */
    @NotNull(message = "主键不能为空", groups = {EditGroup.class})
    private Long id;
    /**
     * éƒ¨é—¨id
     */
    @NotNull(message = "部门id不能为空", groups = {AddGroup.class, EditGroup.class})
    private Long deptId;
    /**
     * ç”¨æˆ·id
     */
    @NotNull(message = "用户id不能为空", groups = {AddGroup.class, EditGroup.class})
    private Long userId;
    /**
     * æŽ’序号
     */
    @NotNull(message = "排序号不能为空", groups = {AddGroup.class, EditGroup.class})
    private Integer orderNum;
    /**
     * key键
     */
    @NotBlank(message = "key键不能为空", groups = {AddGroup.class, EditGroup.class})
    private String testKey;
    /**
     * å€¼
     */
    @NotBlank(message = "值不能为空", groups = {AddGroup.class, EditGroup.class})
    private String value;
}
ruoyi-modules/ruoyi-demo/src/main/java/com/ruoyi/demo/domain/bo/TestDemoImportVo.java
ruoyi-modules/ruoyi-demo/src/main/java/com/ruoyi/demo/domain/bo/TestTreeBo.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,47 @@
package com.ruoyi.demo.domain.bo;
import com.ruoyi.common.core.validate.AddGroup;
import com.ruoyi.common.core.validate.EditGroup;
import com.ruoyi.common.core.web.domain.TreeEntity;
import lombok.Data;
import lombok.EqualsAndHashCode;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
/**
 * æµ‹è¯•树表业务对象 test_tree
 *
 * @author Lion Li
 * @date 2021-07-26
 */
@Data
@EqualsAndHashCode(callSuper = true)
public class TestTreeBo extends TreeEntity<TestTreeBo> {
    /**
     * ä¸»é”®
     */
    @NotNull(message = "主键不能为空", groups = {EditGroup.class})
    private Long id;
    /**
     * éƒ¨é—¨id
     */
    @NotNull(message = "部门id不能为空", groups = {AddGroup.class, EditGroup.class})
    private Long deptId;
    /**
     * ç”¨æˆ·id
     */
    @NotNull(message = "用户id不能为空", groups = {AddGroup.class, EditGroup.class})
    private Long userId;
    /**
     * æ ‘节点名
     */
    @NotBlank(message = "树节点名不能为空", groups = {AddGroup.class, EditGroup.class})
    private String treeName;
}
ruoyi-modules/ruoyi-demo/src/main/java/com/ruoyi/demo/domain/package-info.java
ruoyi-modules/ruoyi-demo/src/main/java/com/ruoyi/demo/domain/vo/TestDemoVo.java
ruoyi-modules/ruoyi-demo/src/main/java/com/ruoyi/demo/domain/vo/TestTreeVo.java
ruoyi-modules/ruoyi-demo/src/main/java/com/ruoyi/demo/mapper/TestDemoMapper.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,58 @@
package com.ruoyi.demo.mapper;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Constants;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.common.mybatis.annotation.DataColumn;
import com.ruoyi.common.mybatis.annotation.DataPermission;
import com.ruoyi.common.mybatis.core.mapper.BaseMapperPlus;
import com.ruoyi.demo.domain.TestDemo;
import com.ruoyi.demo.domain.vo.TestDemoVo;
import org.apache.ibatis.annotations.Param;
import java.util.Collection;
import java.util.List;
/**
 * æµ‹è¯•单表Mapper接口
 *
 * @author Lion Li
 * @date 2021-07-26
 */
public interface TestDemoMapper extends BaseMapperPlus<TestDemoMapper, TestDemo, TestDemoVo> {
    @DataPermission({
        @DataColumn(key = "deptName", value = "dept_id"),
        @DataColumn(key = "userName", value = "user_id")
    })
    Page<TestDemoVo> customPageList(@Param("page") Page<TestDemo> page, @Param("ew") Wrapper<TestDemo> wrapper);
    @Override
    @DataPermission({
        @DataColumn(key = "deptName", value = "dept_id"),
        @DataColumn(key = "userName", value = "user_id")
    })
    <P extends IPage<TestDemo>> P selectPage(P page, @Param(Constants.WRAPPER) Wrapper<TestDemo> queryWrapper);
    @Override
    @DataPermission({
        @DataColumn(key = "deptName", value = "dept_id"),
        @DataColumn(key = "userName", value = "user_id")
    })
    List<TestDemo> selectList(@Param(Constants.WRAPPER) Wrapper<TestDemo> queryWrapper);
    @Override
    @DataPermission({
        @DataColumn(key = "deptName", value = "dept_id"),
        @DataColumn(key = "userName", value = "user_id")
    })
    int updateById(@Param(Constants.ENTITY) TestDemo entity);
    @Override
    @DataPermission({
        @DataColumn(key = "deptName", value = "dept_id"),
        @DataColumn(key = "userName", value = "user_id")
    })
    int deleteBatchIds(@Param(Constants.COLL) Collection<?> idList);
}
ruoyi-modules/ruoyi-demo/src/main/java/com/ruoyi/demo/mapper/TestTreeMapper.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,21 @@
package com.ruoyi.demo.mapper;
import com.ruoyi.common.mybatis.annotation.DataColumn;
import com.ruoyi.common.mybatis.annotation.DataPermission;
import com.ruoyi.common.mybatis.core.mapper.BaseMapperPlus;
import com.ruoyi.demo.domain.TestTree;
import com.ruoyi.demo.domain.vo.TestTreeVo;
/**
 * æµ‹è¯•树表Mapper接口
 *
 * @author Lion Li
 * @date 2021-07-26
 */
@DataPermission({
    @DataColumn(key = "deptName", value = "dept_id"),
    @DataColumn(key = "userName", value = "user_id")
})
public interface TestTreeMapper extends BaseMapperPlus<TestTreeMapper, TestTree, TestTreeVo> {
}
ruoyi-modules/ruoyi-demo/src/main/java/com/ruoyi/demo/mapper/package-info.java
ruoyi-modules/ruoyi-demo/src/main/java/com/ruoyi/demo/service/ITestDemoService.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,71 @@
package com.ruoyi.demo.service;
import com.ruoyi.common.mybatis.core.page.PageQuery;
import com.ruoyi.common.mybatis.core.page.TableDataInfo;
import com.ruoyi.demo.domain.TestDemo;
import com.ruoyi.demo.domain.bo.TestDemoBo;
import com.ruoyi.demo.domain.vo.TestDemoVo;
import java.util.Collection;
import java.util.List;
/**
 * æµ‹è¯•单表Service接口
 *
 * @author Lion Li
 * @date 2021-07-26
 */
public interface ITestDemoService {
    /**
     * æŸ¥è¯¢å•个
     *
     * @return
     */
    TestDemoVo queryById(Long id);
    /**
     * æŸ¥è¯¢åˆ—表
     */
    TableDataInfo<TestDemoVo> queryPageList(TestDemoBo bo, PageQuery pageQuery);
    /**
     * è‡ªå®šä¹‰åˆ†é¡µæŸ¥è¯¢
     */
    TableDataInfo<TestDemoVo> customPageList(TestDemoBo bo, PageQuery pageQuery);
    /**
     * æŸ¥è¯¢åˆ—表
     */
    List<TestDemoVo> queryList(TestDemoBo bo);
    /**
     * æ ¹æ®æ–°å¢žä¸šåŠ¡å¯¹è±¡æ’å…¥æµ‹è¯•å•è¡¨
     *
     * @param bo æµ‹è¯•单表新增业务对象
     * @return
     */
    Boolean insertByBo(TestDemoBo bo);
    /**
     * æ ¹æ®ç¼–辑业务对象修改测试单表
     *
     * @param bo æµ‹è¯•单表编辑业务对象
     * @return
     */
    Boolean updateByBo(TestDemoBo bo);
    /**
     * æ ¡éªŒå¹¶åˆ é™¤æ•°æ®
     *
     * @param ids     ä¸»é”®é›†åˆ
     * @param isValid æ˜¯å¦æ ¡éªŒ,true-删除前校验,false-不校验
     * @return
     */
    Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);
    /**
     * æ‰¹é‡ä¿å­˜
     */
    Boolean saveBatch(List<TestDemo> list);
}
ruoyi-modules/ruoyi-demo/src/main/java/com/ruoyi/demo/service/ITestTreeService.java
ruoyi-modules/ruoyi-demo/src/main/java/com/ruoyi/demo/service/impl/TestDemoServiceImpl.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,110 @@
package com.ruoyi.demo.service.impl;
import cn.hutool.core.bean.BeanUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.common.core.utils.StringUtils;
import com.ruoyi.common.mybatis.core.page.PageQuery;
import com.ruoyi.common.mybatis.core.page.TableDataInfo;
import com.ruoyi.demo.domain.TestDemo;
import com.ruoyi.demo.domain.bo.TestDemoBo;
import com.ruoyi.demo.domain.vo.TestDemoVo;
import com.ruoyi.demo.mapper.TestDemoMapper;
import com.ruoyi.demo.service.ITestDemoService;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import java.util.Collection;
import java.util.List;
import java.util.Map;
/**
 * æµ‹è¯•单表Service业务层处理
 *
 * @author Lion Li
 * @date 2021-07-26
 */
@RequiredArgsConstructor
@Service
public class TestDemoServiceImpl implements ITestDemoService {
    private final TestDemoMapper baseMapper;
    @Override
    public TestDemoVo queryById(Long id) {
        return baseMapper.selectVoById(id);
    }
    @Override
    public TableDataInfo<TestDemoVo> queryPageList(TestDemoBo bo, PageQuery pageQuery) {
        LambdaQueryWrapper<TestDemo> lqw = buildQueryWrapper(bo);
        Page<TestDemoVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
        return TableDataInfo.build(result);
    }
    /**
     * è‡ªå®šä¹‰åˆ†é¡µæŸ¥è¯¢
     */
    @Override
    public TableDataInfo<TestDemoVo> customPageList(TestDemoBo bo, PageQuery pageQuery) {
        LambdaQueryWrapper<TestDemo> lqw = buildQueryWrapper(bo);
        Page<TestDemoVo> result = baseMapper.customPageList(pageQuery.build(), lqw);
        return TableDataInfo.build(result);
    }
    @Override
    public List<TestDemoVo> queryList(TestDemoBo bo) {
        return baseMapper.selectVoList(buildQueryWrapper(bo));
    }
    private LambdaQueryWrapper<TestDemo> buildQueryWrapper(TestDemoBo bo) {
        Map<String, Object> params = bo.getParams();
        LambdaQueryWrapper<TestDemo> lqw = Wrappers.lambdaQuery();
        lqw.like(StringUtils.isNotBlank(bo.getTestKey()), TestDemo::getTestKey, bo.getTestKey());
        lqw.eq(StringUtils.isNotBlank(bo.getValue()), TestDemo::getValue, bo.getValue());
        lqw.between(params.get("beginCreateTime") != null && params.get("endCreateTime") != null,
            TestDemo::getCreateTime, params.get("beginCreateTime"), params.get("endCreateTime"));
        return lqw;
    }
    @Override
    public Boolean insertByBo(TestDemoBo bo) {
        TestDemo add = BeanUtil.toBean(bo, TestDemo.class);
        validEntityBeforeSave(add);
        boolean flag = baseMapper.insert(add) > 0;
        if (flag) {
            bo.setId(add.getId());
        }
        return flag;
    }
    @Override
    public Boolean updateByBo(TestDemoBo bo) {
        TestDemo update = BeanUtil.toBean(bo, TestDemo.class);
        validEntityBeforeSave(update);
        return baseMapper.updateById(update) > 0;
    }
    /**
     * ä¿å­˜å‰çš„æ•°æ®æ ¡éªŒ
     *
     * @param entity å®žä½“类数据
     */
    private void validEntityBeforeSave(TestDemo entity) {
        //TODO åšä¸€äº›æ•°æ®æ ¡éªŒ,如唯一约束
    }
    @Override
    public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
        if (isValid) {
            //TODO åšä¸€äº›ä¸šåŠ¡ä¸Šçš„æ ¡éªŒ,判断是否需要校验
        }
        return baseMapper.deleteBatchIds(ids) > 0;
    }
    @Override
    public Boolean saveBatch(List<TestDemo> list) {
        return baseMapper.insertBatch(list);
    }
}
ruoyi-modules/ruoyi-demo/src/main/java/com/ruoyi/demo/service/impl/TestTreeServiceImpl.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,87 @@
package com.ruoyi.demo.service.impl;
import cn.hutool.core.bean.BeanUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.ruoyi.common.core.utils.StringUtils;
import com.ruoyi.demo.domain.TestTree;
import com.ruoyi.demo.domain.bo.TestTreeBo;
import com.ruoyi.demo.domain.vo.TestTreeVo;
import com.ruoyi.demo.mapper.TestTreeMapper;
import com.ruoyi.demo.service.ITestTreeService;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import java.util.Collection;
import java.util.List;
import java.util.Map;
/**
 * æµ‹è¯•树表Service业务层处理
 *
 * @author Lion Li
 * @date 2021-07-26
 */
// @DS("slave") // åˆ‡æ¢ä»Žåº“查询
@RequiredArgsConstructor
@Service
public class TestTreeServiceImpl implements ITestTreeService {
    private final TestTreeMapper baseMapper;
    @Override
    public TestTreeVo queryById(Long id) {
        return baseMapper.selectVoById(id);
    }
    // @DS("slave") // åˆ‡æ¢ä»Žåº“查询
    @Override
    public List<TestTreeVo> queryList(TestTreeBo bo) {
        LambdaQueryWrapper<TestTree> lqw = buildQueryWrapper(bo);
        return baseMapper.selectVoList(lqw);
    }
    private LambdaQueryWrapper<TestTree> buildQueryWrapper(TestTreeBo bo) {
        Map<String, Object> params = bo.getParams();
        LambdaQueryWrapper<TestTree> lqw = Wrappers.lambdaQuery();
        lqw.like(StringUtils.isNotBlank(bo.getTreeName()), TestTree::getTreeName, bo.getTreeName());
        lqw.between(params.get("beginCreateTime") != null && params.get("endCreateTime") != null,
            TestTree::getCreateTime, params.get("beginCreateTime"), params.get("endCreateTime"));
        return lqw;
    }
    @Override
    public Boolean insertByBo(TestTreeBo bo) {
        TestTree add = BeanUtil.toBean(bo, TestTree.class);
        validEntityBeforeSave(add);
        boolean flag = baseMapper.insert(add) > 0;
        if (flag) {
            bo.setId(add.getId());
        }
        return flag;
    }
    @Override
    public Boolean updateByBo(TestTreeBo bo) {
        TestTree update = BeanUtil.toBean(bo, TestTree.class);
        validEntityBeforeSave(update);
        return baseMapper.updateById(update) > 0;
    }
    /**
     * ä¿å­˜å‰çš„æ•°æ®æ ¡éªŒ
     *
     * @param entity å®žä½“类数据
     */
    private void validEntityBeforeSave(TestTree entity) {
        //TODO åšä¸€äº›æ•°æ®æ ¡éªŒ,如唯一约束
    }
    @Override
    public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
        if (isValid) {
            //TODO åšä¸€äº›ä¸šåŠ¡ä¸Šçš„æ ¡éªŒ,判断是否需要校验
        }
        return baseMapper.deleteBatchIds(ids) > 0;
    }
}
ruoyi-modules/ruoyi-demo/src/main/java/com/ruoyi/demo/service/impl/package-info.java
ruoyi-modules/ruoyi-demo/src/main/java/com/ruoyi/demo/service/package-info.java
ruoyi-modules/ruoyi-demo/src/main/resources/excel/µ¥Áбí.xlsx
Binary files differ
ruoyi-modules/ruoyi-demo/src/main/resources/excel/¶àÁбí.xlsx
Binary files differ
ruoyi-modules/ruoyi-demo/src/main/resources/mapper/demo/TestDemoMapper.xml
ruoyi-modules/ruoyi-demo/src/main/resources/mapper/demo/TestTreeMapper.xml
ruoyi-modules/ruoyi-demo/src/main/resources/mapper/package-info.md
ruoyi-modules/ruoyi-generator/pom.xml
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,48 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <groupId>com.ruoyi</groupId>
        <artifactId>ruoyi-modules</artifactId>
        <version>${revision}</version>
        <relativePath>../pom.xml</relativePath>
    </parent>
    <modelVersion>4.0.0</modelVersion>
    <artifactId>ruoyi-generator</artifactId>
    <description>
        generator ä»£ç ç”Ÿæˆ
    </description>
    <dependencies>
        <!-- é€šç”¨å·¥å…·-->
        <dependency>
            <groupId>com.ruoyi</groupId>
            <artifactId>ruoyi-common-core</artifactId>
        </dependency>
        <dependency>
            <groupId>com.ruoyi</groupId>
            <artifactId>ruoyi-common-mybatis</artifactId>
        </dependency>
        <dependency>
            <groupId>com.ruoyi</groupId>
            <artifactId>ruoyi-common-web</artifactId>
        </dependency>
        <dependency>
            <groupId>com.ruoyi</groupId>
            <artifactId>ruoyi-common-log</artifactId>
        </dependency>
        <!--velocity代码生成使用模板 -->
        <dependency>
            <groupId>org.apache.velocity</groupId>
            <artifactId>velocity-engine-core</artifactId>
        </dependency>
    </dependencies>
</project>
ruoyi-modules/ruoyi-generator/src/main/java/com/ruoyi/generator/config/GenConfig.java
ruoyi-modules/ruoyi-generator/src/main/java/com/ruoyi/generator/controller/GenController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,207 @@
package com.ruoyi.generator.controller;
import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.io.IoUtil;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.core.web.controller.BaseController;
import com.ruoyi.common.mybatis.core.page.PageQuery;
import com.ruoyi.common.mybatis.core.page.TableDataInfo;
import com.ruoyi.common.log.annotation.Log;
import com.ruoyi.common.log.enums.BusinessType;
import com.ruoyi.generator.domain.GenTable;
import com.ruoyi.generator.domain.GenTableColumn;
import com.ruoyi.generator.service.IGenTableService;
import lombok.RequiredArgsConstructor;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
 * ä»£ç ç”Ÿæˆ æ“ä½œå¤„理
 *
 * @author Lion Li
 */
@Validated
@RequiredArgsConstructor
@RestController
@RequestMapping("/tool/gen")
public class GenController extends BaseController {
    private final IGenTableService genTableService;
    /**
     * æŸ¥è¯¢ä»£ç ç”Ÿæˆåˆ—表
     */
    @SaCheckPermission("tool:gen:list")
    @GetMapping("/list")
    public TableDataInfo<GenTable> genList(GenTable genTable, PageQuery pageQuery) {
        return genTableService.selectPageGenTableList(genTable, pageQuery);
    }
    /**
     * ä¿®æ”¹ä»£ç ç”Ÿæˆä¸šåŠ¡
     *
     * @param tableId è¡¨ID
     */
    @SaCheckPermission("tool:gen:query")
    @GetMapping(value = "/{tableId}")
    public R<Map<String, Object>> getInfo(@PathVariable Long tableId) {
        GenTable table = genTableService.selectGenTableById(tableId);
        List<GenTable> tables = genTableService.selectGenTableAll();
        List<GenTableColumn> list = genTableService.selectGenTableColumnListByTableId(tableId);
        Map<String, Object> map = new HashMap<String, Object>();
        map.put("info", table);
        map.put("rows", list);
        map.put("tables", tables);
        return R.ok(map);
    }
    /**
     * æŸ¥è¯¢æ•°æ®åº“列表
     */
    @SaCheckPermission("tool:gen:list")
    @GetMapping("/db/list")
    public TableDataInfo<GenTable> dataList(GenTable genTable, PageQuery pageQuery) {
        return genTableService.selectPageDbTableList(genTable, pageQuery);
    }
    /**
     * æŸ¥è¯¢æ•°æ®è¡¨å­—段列表
     *
     * @param tableId è¡¨ID
     */
    @SaCheckPermission("tool:gen:list")
    @GetMapping(value = "/column/{tableId}")
    public TableDataInfo<GenTableColumn> columnList(Long tableId) {
        TableDataInfo<GenTableColumn> dataInfo = new TableDataInfo<>();
        List<GenTableColumn> list = genTableService.selectGenTableColumnListByTableId(tableId);
        dataInfo.setRows(list);
        dataInfo.setTotal(list.size());
        return dataInfo;
    }
    /**
     * å¯¼å…¥è¡¨ç»“构(保存)
     *
     * @param tables è¡¨åä¸²
     */
    @SaCheckPermission("tool:gen:import")
    @Log(title = "代码生成", businessType = BusinessType.IMPORT)
    @PostMapping("/importTable")
    public R<Void> importTableSave(String tables) {
        String[] tableNames = Convert.toStrArray(tables);
        // æŸ¥è¯¢è¡¨ä¿¡æ¯
        List<GenTable> tableList = genTableService.selectDbTableListByNames(tableNames);
        genTableService.importGenTable(tableList);
        return R.ok();
    }
    /**
     * ä¿®æ”¹ä¿å­˜ä»£ç ç”Ÿæˆä¸šåŠ¡
     */
    @SaCheckPermission("tool:gen:edit")
    @Log(title = "代码生成", businessType = BusinessType.UPDATE)
    @PutMapping
    public R<Void> editSave(@Validated @RequestBody GenTable genTable) {
        genTableService.validateEdit(genTable);
        genTableService.updateGenTable(genTable);
        return R.ok();
    }
    /**
     * åˆ é™¤ä»£ç ç”Ÿæˆ
     *
     * @param tableIds è¡¨ID串
     */
    @SaCheckPermission("tool:gen:remove")
    @Log(title = "代码生成", businessType = BusinessType.DELETE)
    @DeleteMapping("/{tableIds}")
    public R<Void> remove(@PathVariable Long[] tableIds) {
        genTableService.deleteGenTableByIds(tableIds);
        return R.ok();
    }
    /**
     * é¢„览代码
     *
     * @param tableId è¡¨ID
     */
    @SaCheckPermission("tool:gen:preview")
    @GetMapping("/preview/{tableId}")
    public R<Map<String, String>> preview(@PathVariable("tableId") Long tableId) throws IOException {
        Map<String, String> dataMap = genTableService.previewCode(tableId);
        return R.ok(dataMap);
    }
    /**
     * ç”Ÿæˆä»£ç ï¼ˆä¸‹è½½æ–¹å¼ï¼‰
     *
     * @param tableName è¡¨å
     */
    @SaCheckPermission("tool:gen:code")
    @Log(title = "代码生成", businessType = BusinessType.GENCODE)
    @GetMapping("/download/{tableName}")
    public void download(HttpServletResponse response, @PathVariable("tableName") String tableName) throws IOException {
        byte[] data = genTableService.downloadCode(tableName);
        genCode(response, data);
    }
    /**
     * ç”Ÿæˆä»£ç ï¼ˆè‡ªå®šä¹‰è·¯å¾„)
     *
     * @param tableName è¡¨å
     */
    @SaCheckPermission("tool:gen:code")
    @Log(title = "代码生成", businessType = BusinessType.GENCODE)
    @GetMapping("/genCode/{tableName}")
    public R<Void> genCode(@PathVariable("tableName") String tableName) {
        genTableService.generatorCode(tableName);
        return R.ok();
    }
    /**
     * åŒæ­¥æ•°æ®åº“
     *
     * @param tableName è¡¨å
     */
    @SaCheckPermission("tool:gen:edit")
    @Log(title = "代码生成", businessType = BusinessType.UPDATE)
    @GetMapping("/synchDb/{tableName}")
    public R<Void> synchDb(@PathVariable("tableName") String tableName) {
        genTableService.synchDb(tableName);
        return R.ok();
    }
    /**
     * æ‰¹é‡ç”Ÿæˆä»£ç 
     *
     * @param tables è¡¨åä¸²
     */
    @SaCheckPermission("tool:gen:code")
    @Log(title = "代码生成", businessType = BusinessType.GENCODE)
    @GetMapping("/batchGenCode")
    public void batchGenCode(HttpServletResponse response, String tables) throws IOException {
        String[] tableNames = Convert.toStrArray(tables);
        byte[] data = genTableService.downloadCode(tableNames);
        genCode(response, data);
    }
    /**
     * ç”Ÿæˆzip文件
     */
    private void genCode(HttpServletResponse response, byte[] data) throws IOException {
        response.reset();
        response.addHeader("Access-Control-Allow-Origin", "*");
        response.addHeader("Access-Control-Expose-Headers", "Content-Disposition");
        response.setHeader("Content-Disposition", "attachment; filename=\"ruoyi.zip\"");
        response.addHeader("Content-Length", "" + data.length);
        response.setContentType("application/octet-stream; charset=UTF-8");
        IoUtil.write(response.getOutputStream(), false, data);
    }
}
ruoyi-modules/ruoyi-generator/src/main/java/com/ruoyi/generator/domain/GenTable.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,192 @@
package com.ruoyi.generator.domain;
import com.baomidou.mybatisplus.annotation.*;
import com.ruoyi.common.core.constant.GenConstants;
import com.ruoyi.common.core.utils.StringUtils;
import com.ruoyi.common.core.web.domain.BaseEntity;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotBlank;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.apache.commons.lang3.ArrayUtils;
import java.util.List;
/**
 * ä¸šåŠ¡è¡¨ gen_table
 *
 * @author Lion Li
 */
@Data
@EqualsAndHashCode(callSuper = true)
@TableName("gen_table")
public class GenTable extends BaseEntity {
    /**
     * ç¼–号
     */
    @TableId(value = "table_id")
    private Long tableId;
    /**
     * è¡¨åç§°
     */
    @NotBlank(message = "表名称不能为空")
    private String tableName;
    /**
     * è¡¨æè¿°
     */
    @NotBlank(message = "表描述不能为空")
    private String tableComment;
    /**
     * å…³è”父表的表名
     */
    private String subTableName;
    /**
     * æœ¬è¡¨å…³è”父表的外键名
     */
    private String subTableFkName;
    /**
     * å®žä½“类名称(首字母大写)
     */
    @NotBlank(message = "实体类名称不能为空")
    private String className;
    /**
     * ä½¿ç”¨çš„æ¨¡æ¿ï¼ˆcrud单表操作 tree树表操作 sub主子表操作)
     */
    private String tplCategory;
    /**
     * ç”ŸæˆåŒ…路径
     */
    @NotBlank(message = "生成包路径不能为空")
    private String packageName;
    /**
     * ç”Ÿæˆæ¨¡å—名
     */
    @NotBlank(message = "生成模块名不能为空")
    private String moduleName;
    /**
     * ç”Ÿæˆä¸šåŠ¡å
     */
    @NotBlank(message = "生成业务名不能为空")
    private String businessName;
    /**
     * ç”ŸæˆåŠŸèƒ½å
     */
    @NotBlank(message = "生成功能名不能为空")
    private String functionName;
    /**
     * ç”Ÿæˆä½œè€…
     */
    @NotBlank(message = "作者不能为空")
    private String functionAuthor;
    /**
     * ç”Ÿæˆä»£ç æ–¹å¼ï¼ˆ0zip压缩包 1自定义路径)
     */
    private String genType;
    /**
     * ç”Ÿæˆè·¯å¾„(不填默认项目路径)
     */
    @TableField(updateStrategy = FieldStrategy.NOT_EMPTY)
    private String genPath;
    /**
     * ä¸»é”®ä¿¡æ¯
     */
    @TableField(exist = false)
    private GenTableColumn pkColumn;
    /**
     * è¡¨åˆ—信息
     */
    @Valid
    @TableField(exist = false)
    private List<GenTableColumn> columns;
    /**
     * å…¶å®ƒç”Ÿæˆé€‰é¡¹
     */
    private String options;
    /**
     * å¤‡æ³¨
     */
    private String remark;
    /**
     * æ ‘编码字段
     */
    @TableField(exist = false)
    private String treeCode;
    /**
     * æ ‘父编码字段
     */
    @TableField(exist = false)
    private String treeParentCode;
    /**
     * æ ‘名称字段
     */
    @TableField(exist = false)
    private String treeName;
    /*
     * èœå•id列表
     */
    @TableField(exist = false)
    private List<Long> menuIds;
    /**
     * ä¸Šçº§èœå•ID字段
     */
    @TableField(exist = false)
    private String parentMenuId;
    /**
     * ä¸Šçº§èœå•名称字段
     */
    @TableField(exist = false)
    private String parentMenuName;
    public boolean isTree() {
        return isTree(this.tplCategory);
    }
    public static boolean isTree(String tplCategory) {
        return tplCategory != null && StringUtils.equals(GenConstants.TPL_TREE, tplCategory);
    }
    public boolean isCrud() {
        return isCrud(this.tplCategory);
    }
    public static boolean isCrud(String tplCategory) {
        return tplCategory != null && StringUtils.equals(GenConstants.TPL_CRUD, tplCategory);
    }
    public boolean isSuperColumn(String javaField) {
        return isSuperColumn(this.tplCategory, javaField);
    }
    public static boolean isSuperColumn(String tplCategory, String javaField) {
        if (isTree(tplCategory)) {
            return StringUtils.equalsAnyIgnoreCase(javaField,
                ArrayUtils.addAll(GenConstants.TREE_ENTITY, GenConstants.BASE_ENTITY));
        }
        return StringUtils.equalsAnyIgnoreCase(javaField, GenConstants.BASE_ENTITY);
    }
}
ruoyi-modules/ruoyi-generator/src/main/java/com/ruoyi/generator/domain/GenTableColumn.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,223 @@
package com.ruoyi.generator.domain;
import com.baomidou.mybatisplus.annotation.FieldStrategy;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.ruoyi.common.core.utils.StringUtils;
import com.ruoyi.common.core.web.domain.BaseEntity;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.apache.ibatis.type.JdbcType;
import jakarta.validation.constraints.NotBlank;
/**
 * ä»£ç ç”Ÿæˆä¸šåŠ¡å­—æ®µè¡¨ gen_table_column
 *
 * @author Lion Li
 */
@Data
@EqualsAndHashCode(callSuper = true)
@TableName("gen_table_column")
public class GenTableColumn extends BaseEntity {
    /**
     * ç¼–号
     */
    @TableId(value = "column_id")
    private Long columnId;
    /**
     * å½’属表编号
     */
    private Long tableId;
    /**
     * åˆ—名称
     */
    private String columnName;
    /**
     * åˆ—描述
     */
    @TableField(updateStrategy = FieldStrategy.IGNORED, jdbcType = JdbcType.VARCHAR)
    private String columnComment;
    /**
     * åˆ—类型
     */
    private String columnType;
    /**
     * JAVA类型
     */
    private String javaType;
    /**
     * JAVA字段名
     */
    @NotBlank(message = "Java属性不能为空")
    private String javaField;
    /**
     * æ˜¯å¦ä¸»é”®ï¼ˆ1是)
     */
    @TableField(updateStrategy = FieldStrategy.IGNORED, jdbcType = JdbcType.VARCHAR)
    private String isPk;
    /**
     * æ˜¯å¦è‡ªå¢žï¼ˆ1是)
     */
    @TableField(updateStrategy = FieldStrategy.IGNORED, jdbcType = JdbcType.VARCHAR)
    private String isIncrement;
    /**
     * æ˜¯å¦å¿…填(1是)
     */
    @TableField(updateStrategy = FieldStrategy.IGNORED, jdbcType = JdbcType.VARCHAR)
    private String isRequired;
    /**
     * æ˜¯å¦ä¸ºæ’入字段(1是)
     */
    @TableField(updateStrategy = FieldStrategy.IGNORED, jdbcType = JdbcType.VARCHAR)
    private String isInsert;
    /**
     * æ˜¯å¦ç¼–辑字段(1是)
     */
    @TableField(updateStrategy = FieldStrategy.IGNORED, jdbcType = JdbcType.VARCHAR)
    private String isEdit;
    /**
     * æ˜¯å¦åˆ—表字段(1是)
     */
    @TableField(updateStrategy = FieldStrategy.IGNORED, jdbcType = JdbcType.VARCHAR)
    private String isList;
    /**
     * æ˜¯å¦æŸ¥è¯¢å­—段(1是)
     */
    @TableField(updateStrategy = FieldStrategy.IGNORED, jdbcType = JdbcType.VARCHAR)
    private String isQuery;
    /**
     * æŸ¥è¯¢æ–¹å¼ï¼ˆEQ等于、NE不等于、GT大于、LT小于、LIKE模糊、BETWEEN范围)
     */
    private String queryType;
    /**
     * æ˜¾ç¤ºç±»åž‹ï¼ˆinput文本框、textarea文本域、select下拉框、checkbox复选框、radio单选框、datetime日期控件、image图片上传控件、upload文件上传控件、editor富文本控件)
     */
    private String htmlType;
    /**
     * å­—典类型
     */
    private String dictType;
    /**
     * æŽ’序
     */
    private Integer sort;
    public String getCapJavaField() {
        return StringUtils.capitalize(javaField);
    }
    public boolean isPk() {
        return isPk(this.isPk);
    }
    public boolean isPk(String isPk) {
        return isPk != null && StringUtils.equals("1", isPk);
    }
    public boolean isIncrement() {
        return isIncrement(this.isIncrement);
    }
    public boolean isIncrement(String isIncrement) {
        return isIncrement != null && StringUtils.equals("1", isIncrement);
    }
    public boolean isRequired() {
        return isRequired(this.isRequired);
    }
    public boolean isRequired(String isRequired) {
        return isRequired != null && StringUtils.equals("1", isRequired);
    }
    public boolean isInsert() {
        return isInsert(this.isInsert);
    }
    public boolean isInsert(String isInsert) {
        return isInsert != null && StringUtils.equals("1", isInsert);
    }
    public boolean isEdit() {
        return isInsert(this.isEdit);
    }
    public boolean isEdit(String isEdit) {
        return isEdit != null && StringUtils.equals("1", isEdit);
    }
    public boolean isList() {
        return isList(this.isList);
    }
    public boolean isList(String isList) {
        return isList != null && StringUtils.equals("1", isList);
    }
    public boolean isQuery() {
        return isQuery(this.isQuery);
    }
    public boolean isQuery(String isQuery) {
        return isQuery != null && StringUtils.equals("1", isQuery);
    }
    public boolean isSuperColumn() {
        return isSuperColumn(this.javaField);
    }
    public static boolean isSuperColumn(String javaField) {
        return StringUtils.equalsAnyIgnoreCase(javaField,
            // BaseEntity
            "createBy", "createTime", "updateBy", "updateTime",
            // TreeEntity
            "parentName", "parentId");
    }
    public boolean isUsableColumn() {
        return isUsableColumn(javaField);
    }
    public static boolean isUsableColumn(String javaField) {
        // isSuperColumn()中的名单用于避免生成多余Domain属性,若某些属性在生成页面时需要用到不能忽略,则放在此处白名单
        return StringUtils.equalsAnyIgnoreCase(javaField, "parentId", "orderNum", "remark");
    }
    public String readConverterExp() {
        String remarks = StringUtils.substringBetween(this.columnComment, "(", ")");
        StringBuffer sb = new StringBuffer();
        if (StringUtils.isNotEmpty(remarks)) {
            for (String value : remarks.split(" ")) {
                if (StringUtils.isNotEmpty(value)) {
                    Object startStr = value.subSequence(0, 1);
                    String endStr = value.substring(1);
                    sb.append("").append(startStr).append("=").append(endStr).append(",");
                }
            }
            return sb.deleteCharAt(sb.length() - 1).toString();
        } else {
            return this.columnComment;
        }
    }
}
ruoyi-modules/ruoyi-generator/src/main/java/com/ruoyi/generator/mapper/GenTableColumnMapper.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,24 @@
package com.ruoyi.generator.mapper;
import com.baomidou.mybatisplus.annotation.InterceptorIgnore;
import com.ruoyi.common.mybatis.core.mapper.BaseMapperPlus;
import com.ruoyi.generator.domain.GenTableColumn;
import java.util.List;
/**
 * ä¸šåŠ¡å­—æ®µ æ•°æ®å±‚
 *
 * @author Lion Li
 */
@InterceptorIgnore(dataPermission = "true")
public interface GenTableColumnMapper extends BaseMapperPlus<GenTableColumnMapper, GenTableColumn, GenTableColumn> {
    /**
     * æ ¹æ®è¡¨åç§°æŸ¥è¯¢åˆ—信息
     *
     * @param tableName è¡¨åç§°
     * @return åˆ—信息
     */
    List<GenTableColumn> selectDbTableColumnsByName(String tableName);
}
ruoyi-modules/ruoyi-generator/src/main/java/com/ruoyi/generator/mapper/GenTableMapper.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,58 @@
package com.ruoyi.generator.mapper;
import com.baomidou.mybatisplus.annotation.InterceptorIgnore;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.common.mybatis.core.mapper.BaseMapperPlus;
import com.ruoyi.generator.domain.GenTable;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/**
 * ä¸šåŠ¡ æ•°æ®å±‚
 *
 * @author Lion Li
 */
@InterceptorIgnore(dataPermission = "true")
public interface GenTableMapper extends BaseMapperPlus<GenTableMapper, GenTable, GenTable> {
    /**
     * æŸ¥è¯¢æ®åº“列表
     *
     * @param genTable æŸ¥è¯¢æ¡ä»¶
     * @return æ•°æ®åº“表集合
     */
    Page<GenTable> selectPageDbTableList(@Param("page") Page<GenTable> page, @Param("genTable") GenTable genTable);
    /**
     * æŸ¥è¯¢æ®åº“列表
     *
     * @param tableNames è¡¨åç§°ç»„
     * @return æ•°æ®åº“表集合
     */
    List<GenTable> selectDbTableListByNames(String[] tableNames);
    /**
     * æŸ¥è¯¢æ‰€æœ‰è¡¨ä¿¡æ¯
     *
     * @return è¡¨ä¿¡æ¯é›†åˆ
     */
    List<GenTable> selectGenTableAll();
    /**
     * æŸ¥è¯¢è¡¨ID业务信息
     *
     * @param id ä¸šåŠ¡ID
     * @return ä¸šåŠ¡ä¿¡æ¯
     */
    GenTable selectGenTableById(Long id);
    /**
     * æŸ¥è¯¢è¡¨åç§°ä¸šåŠ¡ä¿¡æ¯
     *
     * @param tableName è¡¨åç§°
     * @return ä¸šåŠ¡ä¿¡æ¯
     */
    GenTable selectGenTableByName(String tableName);
}
ruoyi-modules/ruoyi-generator/src/main/java/com/ruoyi/generator/service/GenTableServiceImpl.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,461 @@
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.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.core.constant.Constants;
import com.ruoyi.common.core.constant.GenConstants;
import com.ruoyi.common.core.exception.ServiceException;
import com.ruoyi.common.core.utils.JsonUtils;
import com.ruoyi.common.core.utils.StreamUtils;
import com.ruoyi.common.core.utils.StringUtils;
import com.ruoyi.common.core.utils.file.FileUtils;
import com.ruoyi.common.mybatis.core.page.PageQuery;
import com.ruoyi.common.mybatis.core.page.TableDataInfo;
import com.ruoyi.common.satoken.utils.LoginHelper;
import com.ruoyi.generator.domain.GenTable;
import com.ruoyi.generator.domain.GenTableColumn;
import com.ruoyi.generator.mapper.GenTableColumnMapper;
import com.ruoyi.generator.mapper.GenTableMapper;
import com.ruoyi.generator.util.GenUtils;
import com.ruoyi.generator.util.VelocityInitializer;
import com.ruoyi.generator.util.VelocityUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.velocity.Template;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.Velocity;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.StringWriter;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
/**
 * ä¸šåŠ¡ æœåŠ¡å±‚å®žçŽ°
 *
 * @author Lion Li
 */
@DS("#header.datasource")
@Slf4j
@RequiredArgsConstructor
@Service
public class GenTableServiceImpl implements IGenTableService {
    private final GenTableMapper baseMapper;
    private final GenTableColumnMapper genTableColumnMapper;
    private final IdentifierGenerator identifierGenerator;
    /**
     * æŸ¥è¯¢ä¸šåŠ¡å­—æ®µåˆ—è¡¨
     *
     * @param tableId ä¸šåŠ¡å­—æ®µç¼–å·
     * @return ä¸šåŠ¡å­—æ®µé›†åˆ
     */
    @Override
    public List<GenTableColumn> selectGenTableColumnListByTableId(Long tableId) {
        return genTableColumnMapper.selectList(new LambdaQueryWrapper<GenTableColumn>()
            .eq(GenTableColumn::getTableId, tableId)
            .orderByAsc(GenTableColumn::getSort));
    }
    /**
     * æŸ¥è¯¢ä¸šåŠ¡ä¿¡æ¯
     *
     * @param id ä¸šåŠ¡ID
     * @return ä¸šåŠ¡ä¿¡æ¯
     */
    @Override
    public GenTable selectGenTableById(Long id) {
        GenTable genTable = baseMapper.selectGenTableById(id);
        setTableFromOptions(genTable);
        return genTable;
    }
    @Override
    public TableDataInfo<GenTable> selectPageGenTableList(GenTable genTable, PageQuery pageQuery) {
        Page<GenTable> page = baseMapper.selectPage(pageQuery.build(), this.buildGenTableQueryWrapper(genTable));
        return TableDataInfo.build(page);
    }
    private QueryWrapper<GenTable> buildGenTableQueryWrapper(GenTable genTable) {
        Map<String, Object> params = genTable.getParams();
        QueryWrapper<GenTable> wrapper = Wrappers.query();
        wrapper.like(StringUtils.isNotBlank(genTable.getTableName()), "lower(table_name)", StringUtils.lowerCase(genTable.getTableName()))
            .like(StringUtils.isNotBlank(genTable.getTableComment()), "lower(table_comment)", StringUtils.lowerCase(genTable.getTableComment()))
            .between(params.get("beginTime") != null && params.get("endTime") != null,
                "create_time", params.get("beginTime"), params.get("endTime"));
        return wrapper;
    }
    @Override
    public TableDataInfo<GenTable> selectPageDbTableList(GenTable genTable, PageQuery pageQuery) {
        Page<GenTable> page = baseMapper.selectPageDbTableList(pageQuery.build(), genTable);
        return TableDataInfo.build(page);
    }
    /**
     * æŸ¥è¯¢æ®åº“列表
     *
     * @param tableNames è¡¨åç§°ç»„
     * @return æ•°æ®åº“表集合
     */
    @Override
    public List<GenTable> selectDbTableListByNames(String[] tableNames) {
        return baseMapper.selectDbTableListByNames(tableNames);
    }
    /**
     * æŸ¥è¯¢æ‰€æœ‰è¡¨ä¿¡æ¯
     *
     * @return è¡¨ä¿¡æ¯é›†åˆ
     */
    @Override
    public List<GenTable> selectGenTableAll() {
        return baseMapper.selectGenTableAll();
    }
    /**
     * ä¿®æ”¹ä¸šåŠ¡
     *
     * @param genTable ä¸šåŠ¡ä¿¡æ¯
     * @return ç»“æžœ
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void updateGenTable(GenTable genTable) {
        String options = JsonUtils.toJsonString(genTable.getParams());
        genTable.setOptions(options);
        int row = baseMapper.updateById(genTable);
        if (row > 0) {
            for (GenTableColumn cenTableColumn : genTable.getColumns()) {
                genTableColumnMapper.updateById(cenTableColumn);
            }
        }
    }
    /**
     * åˆ é™¤ä¸šåŠ¡å¯¹è±¡
     *
     * @param tableIds éœ€è¦åˆ é™¤çš„æ•°æ®ID
     * @return ç»“æžœ
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void deleteGenTableByIds(Long[] tableIds) {
        List<Long> ids = Arrays.asList(tableIds);
        baseMapper.deleteBatchIds(ids);
        genTableColumnMapper.delete(new LambdaQueryWrapper<GenTableColumn>().in(GenTableColumn::getTableId, ids));
    }
    /**
     * å¯¼å…¥è¡¨ç»“æž„
     *
     * @param tableList å¯¼å…¥è¡¨åˆ—表
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void importGenTable(List<GenTable> tableList) {
        String operName = LoginHelper.getUsername();
        try {
            for (GenTable table : tableList) {
                String tableName = table.getTableName();
                GenUtils.initTable(table, operName);
                int row = baseMapper.insert(table);
                if (row > 0) {
                    // ä¿å­˜åˆ—信息
                    List<GenTableColumn> genTableColumns = genTableColumnMapper.selectDbTableColumnsByName(tableName);
                    List<GenTableColumn> saveColumns = new ArrayList<>();
                    for (GenTableColumn column : genTableColumns) {
                        GenUtils.initColumnField(column, table);
                        saveColumns.add(column);
                    }
                    if (CollUtil.isNotEmpty(saveColumns)) {
                        genTableColumnMapper.insertBatch(saveColumns);
                    }
                }
            }
        } catch (Exception e) {
            throw new ServiceException("导入失败:" + e.getMessage());
        }
    }
    /**
     * é¢„览代码
     *
     * @param tableId è¡¨ç¼–号
     * @return é¢„览数据列表
     */
    @Override
    public Map<String, String> previewCode(Long tableId) {
        Map<String, String> dataMap = new LinkedHashMap<>();
        // æŸ¥è¯¢è¡¨ä¿¡æ¯
        GenTable table = baseMapper.selectGenTableById(tableId);
        List<Long> menuIds = new ArrayList<>();
        for (int i = 0; i < 6; i++) {
            menuIds.add(identifierGenerator.nextId(null).longValue());
        }
        table.setMenuIds(menuIds);
        // è®¾ç½®ä¸»é”®åˆ—信息
        setPkColumn(table);
        VelocityInitializer.initVelocity();
        VelocityContext context = VelocityUtils.prepareContext(table);
        // èŽ·å–æ¨¡æ¿åˆ—è¡¨
        List<String> templates = VelocityUtils.getTemplateList(table.getTplCategory());
        for (String template : templates) {
            // æ¸²æŸ“模板
            StringWriter sw = new StringWriter();
            Template tpl = Velocity.getTemplate(template, Constants.UTF8);
            tpl.merge(context, sw);
            dataMap.put(template, sw.toString());
        }
        return dataMap;
    }
    /**
     * ç”Ÿæˆä»£ç ï¼ˆä¸‹è½½æ–¹å¼ï¼‰
     *
     * @param tableName è¡¨åç§°
     * @return æ•°æ®
     */
    @Override
    public byte[] downloadCode(String tableName) {
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        ZipOutputStream zip = new ZipOutputStream(outputStream);
        generatorCode(tableName, zip);
        IoUtil.close(zip);
        return outputStream.toByteArray();
    }
    /**
     * ç”Ÿæˆä»£ç ï¼ˆè‡ªå®šä¹‰è·¯å¾„)
     *
     * @param tableName è¡¨åç§°
     */
    @Override
    public void generatorCode(String tableName) {
        // æŸ¥è¯¢è¡¨ä¿¡æ¯
        GenTable table = baseMapper.selectGenTableByName(tableName);
        // è®¾ç½®ä¸»é”®åˆ—信息
        setPkColumn(table);
        VelocityInitializer.initVelocity();
        VelocityContext context = VelocityUtils.prepareContext(table);
        // èŽ·å–æ¨¡æ¿åˆ—è¡¨
        List<String> templates = VelocityUtils.getTemplateList(table.getTplCategory());
        for (String template : templates) {
            if (!StringUtils.containsAny(template, "sql.vm", "api.js.vm", "index.vue.vm", "index-tree.vue.vm")) {
                // æ¸²æŸ“模板
                StringWriter sw = new StringWriter();
                Template tpl = Velocity.getTemplate(template, Constants.UTF8);
                tpl.merge(context, sw);
                try {
                    String path = getGenPath(table, template);
                    FileUtils.writeUtf8String(sw.toString(), path);
                } catch (Exception e) {
                    throw new ServiceException("渲染模板失败,表名:" + table.getTableName());
                }
            }
        }
    }
    /**
     * åŒæ­¥æ•°æ®åº“
     *
     * @param tableName è¡¨åç§°
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void synchDb(String tableName) {
        GenTable table = baseMapper.selectGenTableByName(tableName);
        List<GenTableColumn> tableColumns = table.getColumns();
        Map<String, GenTableColumn> tableColumnMap = StreamUtils.toIdentityMap(tableColumns, GenTableColumn::getColumnName);
        List<GenTableColumn> dbTableColumns = genTableColumnMapper.selectDbTableColumnsByName(tableName);
        if (CollUtil.isEmpty(dbTableColumns)) {
            throw new ServiceException("同步数据失败,原表结构不存在");
        }
        List<String> dbTableColumnNames = StreamUtils.toList(dbTableColumns, GenTableColumn::getColumnName);
        List<GenTableColumn> saveColumns = new ArrayList<>();
        dbTableColumns.forEach(column -> {
            GenUtils.initColumnField(column, table);
            if (tableColumnMap.containsKey(column.getColumnName())) {
                GenTableColumn prevColumn = tableColumnMap.get(column.getColumnName());
                column.setColumnId(prevColumn.getColumnId());
                if (column.isList()) {
                    // å¦‚果是列表,继续保留查询方式/字典类型选项
                    column.setDictType(prevColumn.getDictType());
                    column.setQueryType(prevColumn.getQueryType());
                }
                if (StringUtils.isNotEmpty(prevColumn.getIsRequired()) && !column.isPk()
                    && (column.isInsert() || column.isEdit())
                    && ((column.isUsableColumn()) || (!column.isSuperColumn()))) {
                    // å¦‚果是(新增/修改&非主键/非忽略及父属性),继续保留必填/显示类型选项
                    column.setIsRequired(prevColumn.getIsRequired());
                    column.setHtmlType(prevColumn.getHtmlType());
                }
                genTableColumnMapper.updateById(column);
            } else {
                genTableColumnMapper.insert(column);
            }
        });
        if (CollUtil.isNotEmpty(saveColumns)) {
            genTableColumnMapper.insertBatch(saveColumns);
        }
        List<GenTableColumn> delColumns = StreamUtils.filter(tableColumns, column -> !dbTableColumnNames.contains(column.getColumnName()));
        if (CollUtil.isNotEmpty(delColumns)) {
            List<Long> ids = StreamUtils.toList(delColumns, GenTableColumn::getColumnId);
            genTableColumnMapper.deleteBatchIds(ids);
        }
    }
    /**
     * æ‰¹é‡ç”Ÿæˆä»£ç ï¼ˆä¸‹è½½æ–¹å¼ï¼‰
     *
     * @param tableNames è¡¨æ•°ç»„
     * @return æ•°æ®
     */
    @Override
    public byte[] downloadCode(String[] tableNames) {
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        ZipOutputStream zip = new ZipOutputStream(outputStream);
        for (String tableName : tableNames) {
            generatorCode(tableName, zip);
        }
        IoUtil.close(zip);
        return outputStream.toByteArray();
    }
    /**
     * æŸ¥è¯¢è¡¨ä¿¡æ¯å¹¶ç”Ÿæˆä»£ç 
     */
    private void generatorCode(String tableName, ZipOutputStream zip) {
        // æŸ¥è¯¢è¡¨ä¿¡æ¯
        GenTable table = baseMapper.selectGenTableByName(tableName);
        List<Long> menuIds = new ArrayList<>();
        for (int i = 0; i < 6; i++) {
            menuIds.add(identifierGenerator.nextId(null).longValue());
        }
        table.setMenuIds(menuIds);
        // è®¾ç½®ä¸»é”®åˆ—信息
        setPkColumn(table);
        VelocityInitializer.initVelocity();
        VelocityContext context = VelocityUtils.prepareContext(table);
        // èŽ·å–æ¨¡æ¿åˆ—è¡¨
        List<String> templates = VelocityUtils.getTemplateList(table.getTplCategory());
        for (String template : templates) {
            // æ¸²æŸ“模板
            StringWriter sw = new StringWriter();
            Template tpl = Velocity.getTemplate(template, Constants.UTF8);
            tpl.merge(context, sw);
            try {
                // æ·»åŠ åˆ°zip
                zip.putNextEntry(new ZipEntry(VelocityUtils.getFileName(template, table)));
                IoUtil.write(zip, StandardCharsets.UTF_8, false, sw.toString());
                IoUtil.close(sw);
                zip.flush();
                zip.closeEntry();
            } catch (IOException e) {
                log.error("渲染模板失败,表名:" + table.getTableName(), e);
            }
        }
    }
    /**
     * ä¿®æ”¹ä¿å­˜å‚数校验
     *
     * @param genTable ä¸šåŠ¡ä¿¡æ¯
     */
    @Override
    public void validateEdit(GenTable genTable) {
        if (GenConstants.TPL_TREE.equals(genTable.getTplCategory())) {
            String options = JsonUtils.toJsonString(genTable.getParams());
            Dict paramsObj = JsonUtils.parseMap(options);
            if (StringUtils.isEmpty(paramsObj.getStr(GenConstants.TREE_CODE))) {
                throw new ServiceException("树编码字段不能为空");
            } else if (StringUtils.isEmpty(paramsObj.getStr(GenConstants.TREE_PARENT_CODE))) {
                throw new ServiceException("树父编码字段不能为空");
            } else if (StringUtils.isEmpty(paramsObj.getStr(GenConstants.TREE_NAME))) {
                throw new ServiceException("树名称字段不能为空");
            }
        }
    }
    /**
     * è®¾ç½®ä¸»é”®åˆ—信息
     *
     * @param table ä¸šåŠ¡è¡¨ä¿¡æ¯
     */
    public void setPkColumn(GenTable table) {
        for (GenTableColumn column : table.getColumns()) {
            if (column.isPk()) {
                table.setPkColumn(column);
                break;
            }
        }
        if (ObjectUtil.isNull(table.getPkColumn())) {
            table.setPkColumn(table.getColumns().get(0));
        }
    }
    /**
     * è®¾ç½®ä»£ç ç”Ÿæˆå…¶ä»–选项值
     *
     * @param genTable è®¾ç½®åŽçš„生成对象
     */
    public void setTableFromOptions(GenTable genTable) {
        Dict paramsObj = JsonUtils.parseMap(genTable.getOptions());
        if (ObjectUtil.isNotNull(paramsObj)) {
            String treeCode = paramsObj.getStr(GenConstants.TREE_CODE);
            String treeParentCode = paramsObj.getStr(GenConstants.TREE_PARENT_CODE);
            String treeName = paramsObj.getStr(GenConstants.TREE_NAME);
            String parentMenuId = paramsObj.getStr(GenConstants.PARENT_MENU_ID);
            String parentMenuName = paramsObj.getStr(GenConstants.PARENT_MENU_NAME);
            genTable.setTreeCode(treeCode);
            genTable.setTreeParentCode(treeParentCode);
            genTable.setTreeName(treeName);
            genTable.setParentMenuId(parentMenuId);
            genTable.setParentMenuName(parentMenuName);
        }
    }
    /**
     * èŽ·å–ä»£ç ç”Ÿæˆåœ°å€
     *
     * @param table    ä¸šåŠ¡è¡¨ä¿¡æ¯
     * @param template æ¨¡æ¿æ–‡ä»¶è·¯å¾„
     * @return ç”Ÿæˆåœ°å€
     */
    public static String getGenPath(GenTable table, String template) {
        String genPath = table.getGenPath();
        if (StringUtils.equals(genPath, "/")) {
            return System.getProperty("user.dir") + File.separator + "src" + File.separator + VelocityUtils.getFileName(template, table);
        }
        return genPath + File.separator + VelocityUtils.getFileName(template, table);
    }
}
ruoyi-modules/ruoyi-generator/src/main/java/com/ruoyi/generator/service/IGenTableService.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,133 @@
package com.ruoyi.generator.service;
import com.ruoyi.common.mybatis.core.page.PageQuery;
import com.ruoyi.common.mybatis.core.page.TableDataInfo;
import com.ruoyi.generator.domain.GenTable;
import com.ruoyi.generator.domain.GenTableColumn;
import java.util.List;
import java.util.Map;
/**
 * ä¸šåŠ¡ æœåС层
 *
 * @author Lion Li
 */
public interface IGenTableService {
    /**
     * æŸ¥è¯¢ä¸šåŠ¡å­—æ®µåˆ—è¡¨
     *
     * @param tableId ä¸šåŠ¡å­—æ®µç¼–å·
     * @return ä¸šåŠ¡å­—æ®µé›†åˆ
     */
    List<GenTableColumn> selectGenTableColumnListByTableId(Long tableId);
    /**
     * æŸ¥è¯¢ä¸šåŠ¡åˆ—è¡¨
     *
     * @param genTable ä¸šåŠ¡ä¿¡æ¯
     * @return ä¸šåŠ¡é›†åˆ
     */
    TableDataInfo<GenTable> selectPageGenTableList(GenTable genTable, PageQuery pageQuery);
    /**
     * æŸ¥è¯¢æ®åº“列表
     *
     * @param genTable ä¸šåŠ¡ä¿¡æ¯
     * @return æ•°æ®åº“表集合
     */
    TableDataInfo<GenTable> selectPageDbTableList(GenTable genTable, PageQuery pageQuery);
    /**
     * æŸ¥è¯¢æ®åº“列表
     *
     * @param tableNames è¡¨åç§°ç»„
     * @return æ•°æ®åº“表集合
     */
    List<GenTable> selectDbTableListByNames(String[] tableNames);
    /**
     * æŸ¥è¯¢æ‰€æœ‰è¡¨ä¿¡æ¯
     *
     * @return è¡¨ä¿¡æ¯é›†åˆ
     */
    List<GenTable> selectGenTableAll();
    /**
     * æŸ¥è¯¢ä¸šåŠ¡ä¿¡æ¯
     *
     * @param id ä¸šåŠ¡ID
     * @return ä¸šåŠ¡ä¿¡æ¯
     */
    GenTable selectGenTableById(Long id);
    /**
     * ä¿®æ”¹ä¸šåŠ¡
     *
     * @param genTable ä¸šåŠ¡ä¿¡æ¯
     * @return ç»“æžœ
     */
    void updateGenTable(GenTable genTable);
    /**
     * åˆ é™¤ä¸šåŠ¡ä¿¡æ¯
     *
     * @param tableIds éœ€è¦åˆ é™¤çš„表数据ID
     * @return ç»“æžœ
     */
    void deleteGenTableByIds(Long[] tableIds);
    /**
     * å¯¼å…¥è¡¨ç»“æž„
     *
     * @param tableList å¯¼å…¥è¡¨åˆ—表
     */
    void importGenTable(List<GenTable> tableList);
    /**
     * é¢„览代码
     *
     * @param tableId è¡¨ç¼–号
     * @return é¢„览数据列表
     */
    Map<String, String> previewCode(Long tableId);
    /**
     * ç”Ÿæˆä»£ç ï¼ˆä¸‹è½½æ–¹å¼ï¼‰
     *
     * @param tableName è¡¨åç§°
     * @return æ•°æ®
     */
    byte[] downloadCode(String tableName);
    /**
     * ç”Ÿæˆä»£ç ï¼ˆè‡ªå®šä¹‰è·¯å¾„)
     *
     * @param tableName è¡¨åç§°
     * @return æ•°æ®
     */
    void generatorCode(String tableName);
    /**
     * åŒæ­¥æ•°æ®åº“
     *
     * @param tableName è¡¨åç§°
     */
    void synchDb(String tableName);
    /**
     * æ‰¹é‡ç”Ÿæˆä»£ç ï¼ˆä¸‹è½½æ–¹å¼ï¼‰
     *
     * @param tableNames è¡¨æ•°ç»„
     * @return æ•°æ®
     */
    byte[] downloadCode(String[] tableNames);
    /**
     * ä¿®æ”¹ä¿å­˜å‚数校验
     *
     * @param genTable ä¸šåŠ¡ä¿¡æ¯
     */
    void validateEdit(GenTable genTable);
}
ruoyi-modules/ruoyi-generator/src/main/java/com/ruoyi/generator/util/GenUtils.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,232 @@
package com.ruoyi.generator.util;
import com.ruoyi.common.core.constant.GenConstants;
import com.ruoyi.common.core.utils.StringUtils;
import com.ruoyi.generator.config.GenConfig;
import com.ruoyi.generator.domain.GenTable;
import com.ruoyi.generator.domain.GenTableColumn;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import org.apache.commons.lang3.RegExUtils;
import java.util.Arrays;
/**
 * ä»£ç ç”Ÿæˆå™¨ å·¥å…·ç±»
 *
 * @author ruoyi
 */
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class GenUtils {
    /**
     * åˆå§‹åŒ–表信息
     */
    public static void initTable(GenTable genTable, String operName) {
        genTable.setClassName(convertClassName(genTable.getTableName()));
        genTable.setPackageName(GenConfig.getPackageName());
        genTable.setModuleName(getModuleName(GenConfig.getPackageName()));
        genTable.setBusinessName(getBusinessName(genTable.getTableName()));
        genTable.setFunctionName(replaceText(genTable.getTableComment()));
        genTable.setFunctionAuthor(GenConfig.getAuthor());
        genTable.setCreateBy(operName);
    }
    /**
     * åˆå§‹åŒ–列属性字段
     */
    public static void initColumnField(GenTableColumn column, GenTable table) {
        String dataType = getDbType(column.getColumnType());
        String columnName = column.getColumnName();
        column.setTableId(table.getTableId());
        column.setCreateBy(table.getCreateBy());
        // è®¾ç½®java字段名
        column.setJavaField(StringUtils.toCamelCase(columnName));
        // è®¾ç½®é»˜è®¤ç±»åž‹
        column.setJavaType(GenConstants.TYPE_STRING);
        column.setQueryType(GenConstants.QUERY_EQ);
        if (arraysContains(GenConstants.COLUMNTYPE_STR, dataType) || arraysContains(GenConstants.COLUMNTYPE_TEXT, dataType)) {
            // å­—符串长度超过500设置为文本域
            Integer columnLength = getColumnLength(column.getColumnType());
            String htmlType = columnLength >= 500 || arraysContains(GenConstants.COLUMNTYPE_TEXT, dataType) ? GenConstants.HTML_TEXTAREA : GenConstants.HTML_INPUT;
            column.setHtmlType(htmlType);
        } else if (arraysContains(GenConstants.COLUMNTYPE_TIME, dataType)) {
            column.setJavaType(GenConstants.TYPE_DATE);
            column.setHtmlType(GenConstants.HTML_DATETIME);
        } else if (arraysContains(GenConstants.COLUMNTYPE_NUMBER, dataType)) {
            column.setHtmlType(GenConstants.HTML_INPUT);
            // å¦‚果是浮点型 ç»Ÿä¸€ç”¨BigDecimal
            String[] str = StringUtils.split(StringUtils.substringBetween(column.getColumnType(), "(", ")"), ",");
            if (str != null && str.length == 2 && Integer.parseInt(str[1]) > 0) {
                column.setJavaType(GenConstants.TYPE_BIGDECIMAL);
            }
            // å¦‚果是整形
            else if (str != null && str.length == 1 && Integer.parseInt(str[0]) <= 10) {
                column.setJavaType(GenConstants.TYPE_INTEGER);
            }
            // é•¿æ•´å½¢
            else {
                column.setJavaType(GenConstants.TYPE_LONG);
            }
        }
        // BO对象 é»˜è®¤æ’入勾选
        if (!arraysContains(GenConstants.COLUMNNAME_NOT_ADD, columnName) && !column.isPk()) {
            column.setIsInsert(GenConstants.REQUIRE);
        }
        // BO对象 é»˜è®¤ç¼–辑勾选
        if (!arraysContains(GenConstants.COLUMNNAME_NOT_EDIT, columnName)) {
            column.setIsEdit(GenConstants.REQUIRE);
        }
        // BO对象 é»˜è®¤æ˜¯å¦å¿…填勾选
        if (!arraysContains(GenConstants.COLUMNNAME_NOT_EDIT, columnName)) {
            column.setIsRequired(GenConstants.REQUIRE);
        }
        // VO对象 é»˜è®¤è¿”回勾选
        if (!arraysContains(GenConstants.COLUMNNAME_NOT_LIST, columnName)) {
            column.setIsList(GenConstants.REQUIRE);
        }
        // BO对象 é»˜è®¤æŸ¥è¯¢å‹¾é€‰
        if (!arraysContains(GenConstants.COLUMNNAME_NOT_QUERY, columnName) && !column.isPk()) {
            column.setIsQuery(GenConstants.REQUIRE);
        }
        // æŸ¥è¯¢å­—段类型
        if (StringUtils.endsWithIgnoreCase(columnName, "name")) {
            column.setQueryType(GenConstants.QUERY_LIKE);
        }
        // çŠ¶æ€å­—æ®µè®¾ç½®å•é€‰æ¡†
        if (StringUtils.endsWithIgnoreCase(columnName, "status")) {
            column.setHtmlType(GenConstants.HTML_RADIO);
        }
        // ç±»åž‹&性别字段设置下拉框
        else if (StringUtils.endsWithIgnoreCase(columnName, "type")
            || StringUtils.endsWithIgnoreCase(columnName, "sex")) {
            column.setHtmlType(GenConstants.HTML_SELECT);
        }
        // å›¾ç‰‡å­—段设置图片上传控件
        else if (StringUtils.endsWithIgnoreCase(columnName, "image")) {
            column.setHtmlType(GenConstants.HTML_IMAGE_UPLOAD);
        }
        // æ–‡ä»¶å­—段设置文件上传控件
        else if (StringUtils.endsWithIgnoreCase(columnName, "file")) {
            column.setHtmlType(GenConstants.HTML_FILE_UPLOAD);
        }
        // å†…容字段设置富文本控件
        else if (StringUtils.endsWithIgnoreCase(columnName, "content")) {
            column.setHtmlType(GenConstants.HTML_EDITOR);
        }
    }
    /**
     * æ ¡éªŒæ•°ç»„是否包含指定值
     *
     * @param arr         æ•°ç»„
     * @param targetValue å€¼
     * @return æ˜¯å¦åŒ…含
     */
    public static boolean arraysContains(String[] arr, String targetValue) {
        return Arrays.asList(arr).contains(targetValue);
    }
    /**
     * èŽ·å–æ¨¡å—å
     *
     * @param packageName åŒ…名
     * @return æ¨¡å—名
     */
    public static String getModuleName(String packageName) {
        int lastIndex = packageName.lastIndexOf(".");
        int nameLength = packageName.length();
        return StringUtils.substring(packageName, lastIndex + 1, nameLength);
    }
    /**
     * èŽ·å–ä¸šåŠ¡å
     *
     * @param tableName è¡¨å
     * @return ä¸šåŠ¡å
     */
    public static String getBusinessName(String tableName) {
        int firstIndex = tableName.indexOf("_");
        int nameLength = tableName.length();
        String businessName = StringUtils.substring(tableName, firstIndex + 1, nameLength);
        businessName = StringUtils.toCamelCase(businessName);
        return businessName;
    }
    /**
     * è¡¨åè½¬æ¢æˆJava类名
     *
     * @param tableName è¡¨åç§°
     * @return ç±»å
     */
    public static String convertClassName(String tableName) {
        boolean autoRemovePre = GenConfig.getAutoRemovePre();
        String tablePrefix = GenConfig.getTablePrefix();
        if (autoRemovePre && StringUtils.isNotEmpty(tablePrefix)) {
            String[] searchList = StringUtils.split(tablePrefix, ",");
            tableName = replaceFirst(tableName, searchList);
        }
        return StringUtils.convertToCamelCase(tableName);
    }
    /**
     * æ‰¹é‡æ›¿æ¢å‰ç¼€
     *
     * @param replacementm æ›¿æ¢å€¼
     * @param searchList   æ›¿æ¢åˆ—表
     * @return
     */
    public static String replaceFirst(String replacementm, String[] searchList) {
        String text = replacementm;
        for (String searchString : searchList) {
            if (replacementm.startsWith(searchString)) {
                text = replacementm.replaceFirst(searchString, "");
                break;
            }
        }
        return text;
    }
    /**
     * å…³é”®å­—替换
     *
     * @param text éœ€è¦è¢«æ›¿æ¢çš„名字
     * @return æ›¿æ¢åŽçš„名字
     */
    public static String replaceText(String text) {
        return RegExUtils.replaceAll(text, "(?:表|若依)", "");
    }
    /**
     * èŽ·å–æ•°æ®åº“ç±»åž‹å­—æ®µ
     *
     * @param columnType åˆ—类型
     * @return æˆªå–后的列类型
     */
    public static String getDbType(String columnType) {
        if (StringUtils.indexOf(columnType, '(') > 0) {
            return StringUtils.substringBefore(columnType, "(");
        } else {
            return columnType;
        }
    }
    /**
     * èŽ·å–å­—æ®µé•¿åº¦
     *
     * @param columnType åˆ—类型
     * @return æˆªå–后的列类型
     */
    public static Integer getColumnLength(String columnType) {
        if (StringUtils.indexOf(columnType, '(') > 0) {
            String length = StringUtils.substringBetween(columnType, "(", ")");
            return Integer.valueOf(length);
        } else {
            return 0;
        }
    }
}
ruoyi-modules/ruoyi-generator/src/main/java/com/ruoyi/generator/util/VelocityInitializer.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,35 @@
package com.ruoyi.generator.util;
import com.ruoyi.common.core.constant.Constants;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import org.apache.velocity.app.Velocity;
import java.util.Properties;
/**
 * VelocityEngine工厂
 *
 * @author ruoyi
 */
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class VelocityInitializer {
    /**
     * åˆå§‹åŒ–vm方法
     */
    public static void initVelocity() {
        Properties p = new Properties();
        try {
            // åŠ è½½classpath目录下的vm文件
            p.setProperty("resource.loader.file.class", "org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader");
            // å®šä¹‰å­—符集
            p.setProperty(Velocity.INPUT_ENCODING, Constants.UTF8);
            // åˆå§‹åŒ–Velocity引擎,指定配置Properties
            Velocity.init(p);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}
ruoyi-modules/ruoyi-generator/src/main/java/com/ruoyi/generator/util/VelocityUtils.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,336 @@
package com.ruoyi.generator.util;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.lang.Dict;
import cn.hutool.core.util.ObjectUtil;
import com.ruoyi.common.core.constant.GenConstants;
import com.ruoyi.common.core.utils.DateUtils;
import com.ruoyi.common.core.utils.JsonUtils;
import com.ruoyi.common.core.utils.StringUtils;
import com.ruoyi.common.mybatis.helper.DataBaseHelper;
import com.ruoyi.generator.domain.GenTable;
import com.ruoyi.generator.domain.GenTableColumn;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import org.apache.velocity.VelocityContext;
import java.util.*;
/**
 * æ¨¡æ¿å¤„理工具类
 *
 * @author ruoyi
 */
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class VelocityUtils {
    /**
     * é¡¹ç›®ç©ºé—´è·¯å¾„
     */
    private static final String PROJECT_PATH = "main/java";
    /**
     * mybatis空间路径
     */
    private static final String MYBATIS_PATH = "main/resources/mapper";
    /**
     * é»˜è®¤ä¸Šçº§èœå•,系统工具
     */
    private static final String DEFAULT_PARENT_MENU_ID = "3";
    /**
     * è®¾ç½®æ¨¡æ¿å˜é‡ä¿¡æ¯
     *
     * @return æ¨¡æ¿åˆ—表
     */
    public static VelocityContext prepareContext(GenTable genTable) {
        String moduleName = genTable.getModuleName();
        String businessName = genTable.getBusinessName();
        String packageName = genTable.getPackageName();
        String tplCategory = genTable.getTplCategory();
        String functionName = genTable.getFunctionName();
        VelocityContext velocityContext = new VelocityContext();
        velocityContext.put("tplCategory", genTable.getTplCategory());
        velocityContext.put("tableName", genTable.getTableName());
        velocityContext.put("functionName", StringUtils.isNotEmpty(functionName) ? functionName : "【请填写功能名称】");
        velocityContext.put("ClassName", genTable.getClassName());
        velocityContext.put("className", StringUtils.uncapitalize(genTable.getClassName()));
        velocityContext.put("moduleName", genTable.getModuleName());
        velocityContext.put("BusinessName", StringUtils.capitalize(genTable.getBusinessName()));
        velocityContext.put("businessName", genTable.getBusinessName());
        velocityContext.put("basePackage", getPackagePrefix(packageName));
        velocityContext.put("packageName", packageName);
        velocityContext.put("author", genTable.getFunctionAuthor());
        velocityContext.put("datetime", DateUtils.getDate());
        velocityContext.put("pkColumn", genTable.getPkColumn());
        velocityContext.put("importList", getImportList(genTable));
        velocityContext.put("permissionPrefix", getPermissionPrefix(moduleName, businessName));
        velocityContext.put("columns", genTable.getColumns());
        velocityContext.put("table", genTable);
        velocityContext.put("dicts", getDicts(genTable));
        setMenuVelocityContext(velocityContext, genTable);
        if (GenConstants.TPL_TREE.equals(tplCategory)) {
            setTreeVelocityContext(velocityContext, genTable);
        }
        return velocityContext;
    }
    public static void setMenuVelocityContext(VelocityContext context, GenTable genTable) {
        String options = genTable.getOptions();
        Dict paramsObj = JsonUtils.parseMap(options);
        String parentMenuId = getParentMenuId(paramsObj);
        context.put("parentMenuId", parentMenuId);
    }
    public static void setTreeVelocityContext(VelocityContext context, GenTable genTable) {
        String options = genTable.getOptions();
        Dict paramsObj = JsonUtils.parseMap(options);
        String treeCode = getTreecode(paramsObj);
        String treeParentCode = getTreeParentCode(paramsObj);
        String treeName = getTreeName(paramsObj);
        context.put("treeCode", treeCode);
        context.put("treeParentCode", treeParentCode);
        context.put("treeName", treeName);
        context.put("expandColumn", getExpandColumn(genTable));
        if (paramsObj.containsKey(GenConstants.TREE_PARENT_CODE)) {
            context.put("tree_parent_code", paramsObj.get(GenConstants.TREE_PARENT_CODE));
        }
        if (paramsObj.containsKey(GenConstants.TREE_NAME)) {
            context.put("tree_name", paramsObj.get(GenConstants.TREE_NAME));
        }
    }
    /**
     * èŽ·å–æ¨¡æ¿ä¿¡æ¯
     *
     * @return æ¨¡æ¿åˆ—表
     */
    public static List<String> getTemplateList(String tplCategory) {
        List<String> templates = new ArrayList<String>();
        templates.add("vm/java/domain.java.vm");
        templates.add("vm/java/vo.java.vm");
        templates.add("vm/java/bo.java.vm");
        templates.add("vm/java/mapper.java.vm");
        templates.add("vm/java/service.java.vm");
        templates.add("vm/java/serviceImpl.java.vm");
        templates.add("vm/java/controller.java.vm");
        templates.add("vm/xml/mapper.xml.vm");
        if (DataBaseHelper.isOracle()) {
            templates.add("vm/sql/oracle/sql.vm");
        } else if (DataBaseHelper.isPostgerSql()) {
            templates.add("vm/sql/postgres/sql.vm");
        } else if (DataBaseHelper.isSqlServer()) {
            templates.add("vm/sql/sqlserver/sql.vm");
        } else {
            templates.add("vm/sql/sql.vm");
        }
        templates.add("vm/js/api.js.vm");
        if (GenConstants.TPL_CRUD.equals(tplCategory)) {
            templates.add("vm/vue/index.vue.vm");
        } else if (GenConstants.TPL_TREE.equals(tplCategory)) {
            templates.add("vm/vue/index-tree.vue.vm");
        }
        return templates;
    }
    /**
     * èŽ·å–æ–‡ä»¶å
     */
    public static String getFileName(String template, GenTable genTable) {
        // æ–‡ä»¶åç§°
        String fileName = "";
        // åŒ…路径
        String packageName = genTable.getPackageName();
        // æ¨¡å—名
        String moduleName = genTable.getModuleName();
        // å¤§å†™ç±»å
        String className = genTable.getClassName();
        // ä¸šåŠ¡åç§°
        String businessName = genTable.getBusinessName();
        String javaPath = PROJECT_PATH + "/" + StringUtils.replace(packageName, ".", "/");
        String mybatisPath = MYBATIS_PATH + "/" + moduleName;
        String vuePath = "vue";
        if (template.contains("domain.java.vm")) {
            fileName = StringUtils.format("{}/domain/{}.java", javaPath, className);
        }
        if (template.contains("vo.java.vm")) {
            fileName = StringUtils.format("{}/domain/vo/{}Vo.java", javaPath, className);
        }
        if (template.contains("bo.java.vm")) {
            fileName = StringUtils.format("{}/domain/bo/{}Bo.java", javaPath, className);
        }
        if (template.contains("mapper.java.vm")) {
            fileName = StringUtils.format("{}/mapper/{}Mapper.java", javaPath, className);
        } else if (template.contains("service.java.vm")) {
            fileName = StringUtils.format("{}/service/I{}Service.java", javaPath, className);
        } else if (template.contains("serviceImpl.java.vm")) {
            fileName = StringUtils.format("{}/service/impl/{}ServiceImpl.java", javaPath, className);
        } else if (template.contains("controller.java.vm")) {
            fileName = StringUtils.format("{}/controller/{}Controller.java", javaPath, className);
        } else if (template.contains("mapper.xml.vm")) {
            fileName = StringUtils.format("{}/{}Mapper.xml", mybatisPath, className);
        } else if (template.contains("sql.vm")) {
            fileName = businessName + "Menu.sql";
        } else if (template.contains("api.js.vm")) {
            fileName = StringUtils.format("{}/api/{}/{}.js", vuePath, moduleName, businessName);
        } else if (template.contains("index.vue.vm")) {
            fileName = StringUtils.format("{}/views/{}/{}/index.vue", vuePath, moduleName, businessName);
        } else if (template.contains("index-tree.vue.vm")) {
            fileName = StringUtils.format("{}/views/{}/{}/index.vue", vuePath, moduleName, businessName);
        }
        return fileName;
    }
    /**
     * èŽ·å–åŒ…å‰ç¼€
     *
     * @param packageName åŒ…名称
     * @return åŒ…前缀名称
     */
    public static String getPackagePrefix(String packageName) {
        int lastIndex = packageName.lastIndexOf(".");
        return StringUtils.substring(packageName, 0, lastIndex);
    }
    /**
     * æ ¹æ®åˆ—类型获取导入包
     *
     * @param genTable ä¸šåŠ¡è¡¨å¯¹è±¡
     * @return è¿”回需要导入的包列表
     */
    public static HashSet<String> getImportList(GenTable genTable) {
        List<GenTableColumn> columns = genTable.getColumns();
        HashSet<String> importList = new HashSet<String>();
        for (GenTableColumn column : columns) {
            if (!column.isSuperColumn() && GenConstants.TYPE_DATE.equals(column.getJavaType())) {
                importList.add("java.util.Date");
                importList.add("com.fasterxml.jackson.annotation.JsonFormat");
            } else if (!column.isSuperColumn() && GenConstants.TYPE_BIGDECIMAL.equals(column.getJavaType())) {
                importList.add("java.math.BigDecimal");
            }
        }
        return importList;
    }
    /**
     * æ ¹æ®åˆ—类型获取字典组
     *
     * @param genTable ä¸šåŠ¡è¡¨å¯¹è±¡
     * @return è¿”回字典组
     */
    public static String getDicts(GenTable genTable) {
        List<GenTableColumn> columns = genTable.getColumns();
        Set<String> dicts = new HashSet<String>();
        addDicts(dicts, columns);
        return StringUtils.join(dicts, ", ");
    }
    /**
     * æ·»åŠ å­—å…¸åˆ—è¡¨
     *
     * @param dicts å­—典列表
     * @param columns åˆ—集合
     */
    public static void addDicts(Set<String> dicts, List<GenTableColumn> columns) {
        for (GenTableColumn column : columns) {
            if (!column.isSuperColumn() && StringUtils.isNotEmpty(column.getDictType()) && StringUtils.equalsAny(
                column.getHtmlType(),
                new String[] { GenConstants.HTML_SELECT, GenConstants.HTML_RADIO, GenConstants.HTML_CHECKBOX })) {
                dicts.add("'" + column.getDictType() + "'");
            }
        }
    }
    /**
     * èŽ·å–æƒé™å‰ç¼€
     *
     * @param moduleName   æ¨¡å—名称
     * @param businessName ä¸šåŠ¡åç§°
     * @return è¿”回权限前缀
     */
    public static String getPermissionPrefix(String moduleName, String businessName) {
        return StringUtils.format("{}:{}", moduleName, businessName);
    }
    /**
     * èŽ·å–ä¸Šçº§èœå•ID字段
     *
     * @param paramsObj ç”Ÿæˆå…¶ä»–选项
     * @return ä¸Šçº§èœå•ID字段
     */
    public static String getParentMenuId(Dict paramsObj) {
        if (CollUtil.isNotEmpty(paramsObj) && paramsObj.containsKey(GenConstants.PARENT_MENU_ID)
            && StringUtils.isNotEmpty(paramsObj.getStr(GenConstants.PARENT_MENU_ID))) {
            return paramsObj.getStr(GenConstants.PARENT_MENU_ID);
        }
        return DEFAULT_PARENT_MENU_ID;
    }
    /**
     * èŽ·å–æ ‘ç¼–ç 
     *
     * @param paramsObj ç”Ÿæˆå…¶ä»–选项
     * @return æ ‘编码
     */
    public static String getTreecode(Map<String, Object> paramsObj) {
        if (CollUtil.isNotEmpty(paramsObj) && paramsObj.containsKey(GenConstants.TREE_CODE)) {
            return StringUtils.toCamelCase(Convert.toStr(paramsObj.get(GenConstants.TREE_CODE)));
        }
        return StringUtils.EMPTY;
    }
    /**
     * èŽ·å–æ ‘çˆ¶ç¼–ç 
     *
     * @param paramsObj ç”Ÿæˆå…¶ä»–选项
     * @return æ ‘父编码
     */
    public static String getTreeParentCode(Dict paramsObj) {
        if (CollUtil.isNotEmpty(paramsObj) && paramsObj.containsKey(GenConstants.TREE_PARENT_CODE)) {
            return StringUtils.toCamelCase(paramsObj.getStr(GenConstants.TREE_PARENT_CODE));
        }
        return StringUtils.EMPTY;
    }
    /**
     * èŽ·å–æ ‘åç§°
     *
     * @param paramsObj ç”Ÿæˆå…¶ä»–选项
     * @return æ ‘名称
     */
    public static String getTreeName(Dict paramsObj) {
        if (CollUtil.isNotEmpty(paramsObj) && paramsObj.containsKey(GenConstants.TREE_NAME)) {
            return StringUtils.toCamelCase(paramsObj.getStr(GenConstants.TREE_NAME));
        }
        return StringUtils.EMPTY;
    }
    /**
     * èŽ·å–éœ€è¦åœ¨å“ªä¸€åˆ—ä¸Šé¢æ˜¾ç¤ºå±•å¼€æŒ‰é’®
     *
     * @param genTable ä¸šåŠ¡è¡¨å¯¹è±¡
     * @return å±•开按钮列序号
     */
    public static int getExpandColumn(GenTable genTable) {
        String options = genTable.getOptions();
        Dict paramsObj = JsonUtils.parseMap(options);
        String treeName = paramsObj.getStr(GenConstants.TREE_NAME);
        int num = 0;
        for (GenTableColumn column : genTable.getColumns()) {
            if (column.isList()) {
                num++;
                String columnName = column.getColumnName();
                if (columnName.equals(treeName)) {
                    break;
                }
            }
        }
        return num;
    }
}
ruoyi-modules/ruoyi-generator/src/main/resources/generator.yml
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,10 @@
# ä»£ç ç”Ÿæˆ
gen:
  # ä½œè€…
  author: ruoyi
  # é»˜è®¤ç”ŸæˆåŒ…路径 system éœ€æ”¹æˆè‡ªå·±çš„æ¨¡å—名称 å¦‚ system monitor tool
  packageName: com.ruoyi.system
  # è‡ªåŠ¨åŽ»é™¤è¡¨å‰ç¼€ï¼Œé»˜è®¤æ˜¯false
  autoRemovePre: false
  # è¡¨å‰ç¼€ï¼ˆç”Ÿæˆç±»åä¸ä¼šåŒ…含表前缀,多个用逗号分隔)
  tablePrefix: sys_
ruoyi-modules/ruoyi-generator/src/main/resources/mapper/generator/GenTableColumnMapper.xml
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,92 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ruoyi.generator.mapper.GenTableColumnMapper">
    <resultMap type="com.ruoyi.generator.domain.GenTableColumn" id="GenTableColumnResult">
    </resultMap>
    <select id="selectDbTableColumnsByName" parameterType="String" resultMap="GenTableColumnResult">
        <if test="@com.ruoyi.common.mybatis.helper.DataBaseHelper@isMySql()">
            select column_name,
                   (case when (is_nullable = 'no' <![CDATA[ && ]]> column_key != 'PRI') then '1' else null end) as is_required,
                   (case when column_key = 'PRI' then '1' else '0' end) as is_pk,
                   ordinal_position as sort,
                   column_comment,
                   (case when extra = 'auto_increment' then '1' else '0' end) as is_increment,
                   column_type
            from information_schema.columns where table_schema = (select database()) and table_name = (#{tableName})
            order by ordinal_position
        </if>
        <if test="@com.ruoyi.common.mybatis.helper.DataBaseHelper@isOracle()">
            select lower(temp.column_name) as column_name,
                    (case when (temp.nullable = 'N'  and  temp.constraint_type != 'P') then '1' else null end) as is_required,
                    (case when temp.constraint_type = 'P' then '1' else '0' end) as is_pk,
                    temp.column_id as sort,
                    temp.comments as column_comment,
                    (case when temp.constraint_type = 'P' then '1' else '0' end) as is_increment,
                    lower(temp.data_type) as column_type
            from (
                select col.column_id, col.column_name,col.nullable, col.data_type, colc.comments, uc.constraint_type, row_number()
                    over (partition by col.column_name order by uc.constraint_type desc) as row_flg
                from user_tab_columns col
                left join user_col_comments colc on colc.table_name = col.table_name and colc.column_name = col.column_name
                left join user_cons_columns ucc on ucc.table_name = col.table_name and ucc.column_name = col.column_name
                left join user_constraints uc on uc.constraint_name = ucc.constraint_name
                where col.table_name = upper(#{tableName})
            ) temp
            WHERE temp.row_flg = 1
            ORDER BY temp.column_id
        </if>
        <if test="@com.ruoyi.common.mybatis.helper.DataBaseHelper@isPostgerSql()">
            SELECT column_name, is_required, is_pk, sort, column_comment, is_increment, column_type
            FROM (
                SELECT c.relname AS table_name,
                       a.attname AS column_name,
                       d.description AS column_comment,
                       CASE WHEN a.attnotnull AND con.conname IS NULL THEN 1 ELSE 0
                       END AS is_required,
                       CASE WHEN con.conname IS NOT NULL THEN 1 ELSE 0
                       END AS is_pk,
                       a.attnum AS sort,
                       CASE WHEN "position"(pg_get_expr(ad.adbin, ad.adrelid),
                           ((c.relname::text || '_'::text) || a.attname::text) || '_seq'::text) > 0 THEN 1 ELSE 0
                       END AS is_increment,
                       btrim(
                           CASE WHEN t.typelem <![CDATA[ <> ]]> 0::oid AND t.typlen = '-1'::integer THEN 'ARRAY'::text ELSE
                                CASE WHEN t.typtype = 'd'::"char" THEN format_type(t.typbasetype, NULL::integer)
                                ELSE format_type(a.atttypid, NULL::integer) END
                           END, '"'::text
                       ) AS column_type
                FROM pg_attribute a
                    JOIN (pg_class c JOIN pg_namespace n ON c.relnamespace = n.oid) ON a.attrelid = c.oid
                    LEFT JOIN pg_description d ON d.objoid = c.oid AND a.attnum = d.objsubid
                    LEFT JOIN pg_constraint con ON con.conrelid = c.oid AND (a.attnum = ANY (con.conkey))
                    LEFT JOIN pg_attrdef ad ON a.attrelid = ad.adrelid AND a.attnum = ad.adnum
                    LEFT JOIN pg_type t ON a.atttypid = t.oid
                WHERE (c.relkind = ANY (ARRAY ['r'::"char", 'p'::"char"]))
                    AND a.attnum > 0
                    AND n.nspname = 'public'::name
                ORDER BY c.relname, a.attnum
            ) temp
            WHERE table_name = (#{tableName})
        </if>
        <if test="@com.ruoyi.common.mybatis.helper.DataBaseHelper@isSqlServer()">
            SELECT
                cast(A.NAME as nvarchar) as column_name,
                cast(B.NAME as nvarchar) + (case when B.NAME = 'numeric' then '(' + cast(A.prec as nvarchar) + ',' + cast(A.scale as nvarchar) + ')' else '' end) as column_type,
                cast(G.[VALUE] as nvarchar) as column_comment,
                (SELECT 1 FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE Z WHERE TABLE_NAME = D.NAME and A.NAME = Z.column_name  ) as is_pk,
                colorder as sort
            FROM SYSCOLUMNS A
                LEFT JOIN SYSTYPES B ON A.XTYPE = B.XUSERTYPE
                INNER JOIN SYSOBJECTS D ON A.ID = D.ID AND D.XTYPE='U' AND D.NAME != 'DTPROPERTIES'
                LEFT JOIN SYS.EXTENDED_PROPERTIES G ON A.ID = G.MAJOR_ID AND A.COLID = G.MINOR_ID
                LEFT JOIN SYS.EXTENDED_PROPERTIES F ON D.ID = F.MAJOR_ID AND F.MINOR_ID = 0
            WHERE D.NAME = #{tableName}
            ORDER BY A.COLORDER
        </if>
    </select>
</mapper>
ruoyi-modules/ruoyi-generator/src/main/resources/mapper/generator/GenTableMapper.xml
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,220 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ruoyi.generator.mapper.GenTableMapper">
    <!-- å¤šç»“构嵌套自动映射需带上每个实体的主键id å¦åˆ™æ˜ å°„会失败 -->
    <resultMap type="com.ruoyi.generator.domain.GenTable" id="GenTableResult">
        <id property="tableId" column="table_id" />
        <collection property="columns" javaType="java.util.List" resultMap="GenTableColumnResult" />
    </resultMap>
    <resultMap type="com.ruoyi.generator.domain.GenTableColumn" id="GenTableColumnResult">
        <id property="columnId" column="column_id"/>
    </resultMap>
    <select id="selectPageDbTableList" resultMap="GenTableResult">
        <if test="@com.ruoyi.common.mybatis.helper.DataBaseHelper@isMySql()">
            select table_name, table_comment, create_time, update_time
            from information_schema.tables
            where table_schema = (select database())
            AND table_name NOT LIKE 'xxl_job_%' AND table_name NOT LIKE 'gen_%'
            AND table_name NOT IN (select table_name from gen_table)
            <if test="genTable.tableName != null and genTable.tableName != ''">
                AND lower(table_name) like lower(concat('%', #{genTable.tableName}, '%'))
            </if>
            <if test="genTable.tableComment != null and genTable.tableComment != ''">
                AND lower(table_comment) like lower(concat('%', #{genTable.tableComment}, '%'))
            </if>
            order by create_time desc
        </if>
        <if test="@com.ruoyi.common.mybatis.helper.DataBaseHelper@isOracle()">
            select lower(dt.table_name) as table_name, dtc.comments as table_comment, uo.created as create_time, uo.last_ddl_time as update_time
            from user_tables dt, user_tab_comments dtc, user_objects uo
            where dt.table_name = dtc.table_name
            and dt.table_name = uo.object_name
            and uo.object_type = 'TABLE'
            AND dt.table_name NOT LIKE 'XXL_JOB_%' AND dt.table_name NOT LIKE 'GEN_%'
            AND lower(dt.table_name) NOT IN (select table_name from gen_table)
            <if test="genTable.tableName != null and genTable.tableName != ''">
                AND lower(dt.table_name) like lower(concat(concat('%', #{genTable.tableName}), '%'))
            </if>
            <if test="genTable.tableComment != null and genTable.tableComment != ''">
                AND lower(dtc.comments) like lower(concat(concat('%', #{genTable.tableComment}), '%'))
            </if>
            order by create_time desc
        </if>
        <if test="@com.ruoyi.common.mybatis.helper.DataBaseHelper@isPostgerSql()">
            select table_name, table_comment, create_time, update_time
            from (
                SELECT c.relname AS table_name,
                        obj_description(c.oid) AS table_comment,
                        CURRENT_TIMESTAMP AS create_time,
                        CURRENT_TIMESTAMP AS update_time
                FROM pg_class c
                    LEFT JOIN pg_namespace n ON n.oid = c.relnamespace
                WHERE (c.relkind = ANY (ARRAY ['r'::"char", 'p'::"char"]))
                    AND c.relname != 'spatial_%'::text
                    AND n.nspname = 'public'::name
                    AND n.nspname <![CDATA[ <> ]]> ''::name
            ) list_table
            where table_name NOT LIKE 'xxl_job_%' AND table_name NOT LIKE 'gen_%'
            AND table_name NOT IN (select table_name from gen_table)
            <if test="genTable.tableName != null and genTable.tableName != ''">
                AND lower(table_name) like lower(concat('%', #{genTable.tableName}, '%'))
            </if>
            <if test="genTable.tableComment != null and genTable.tableComment != ''">
                AND lower(table_comment) like lower(concat('%', #{genTable.tableComment}, '%'))
            </if>
            order by create_time desc
        </if>
        <if test="@com.ruoyi.common.mybatis.helper.DataBaseHelper@isSqlServer()">
            SELECT cast(D.NAME as nvarchar) as table_name,
                   cast(F.VALUE as nvarchar) as table_comment,
                   crdate as create_time,
                   refdate as update_time
            FROM SYSOBJECTS D
                INNER JOIN SYS.EXTENDED_PROPERTIES F ON D.ID = F.MAJOR_ID
                    AND F.MINOR_ID = 0 AND D.XTYPE = 'U' AND D.NAME != 'DTPROPERTIES'
                    AND D.NAME NOT LIKE 'xxl_job_%' AND D.NAME NOT LIKE 'gen_%'
                    AND D.NAME NOT IN (select table_name from gen_table)
            <if test="genTable.tableName != null and genTable.tableName != ''">
                AND lower(D.NAME) like lower(concat(N'%', N'${genTable.tableName}', N'%'))
            </if>
            <if test="genTable.tableComment != null and genTable.tableComment != ''">
                AND lower(CAST(F.VALUE AS nvarchar)) like lower(concat(N'%', N'${genTable.tableComment}', N'%'))
            </if>
            order by crdate desc
        </if>
    </select>
    <select id="selectDbTableListByNames" resultMap="GenTableResult">
        <if test="@com.ruoyi.common.mybatis.helper.DataBaseHelper@isMySql()">
            select table_name, table_comment, create_time, update_time from information_schema.tables
            where table_name NOT LIKE 'xxl_job_%' and table_name NOT LIKE 'gen_%' and table_schema = (select database())
            and table_name in
            <foreach collection="array" item="name" open="(" separator="," close=")">
                 #{name}
            </foreach>
        </if>
        <if test="@com.ruoyi.common.mybatis.helper.DataBaseHelper@isOracle()">
            select lower(dt.table_name) as table_name, dtc.comments as table_comment, uo.created as create_time, uo.last_ddl_time as update_time
            from user_tables dt, user_tab_comments dtc, user_objects uo
            where dt.table_name = dtc.table_name
            and dt.table_name = uo.object_name
            and uo.object_type = 'TABLE'
            AND dt.table_name NOT LIKE 'XXL_JOB_%' AND dt.table_name NOT LIKE 'GEN_%'
            AND dt.table_name NOT IN (select table_name from gen_table)
            and lower(dt.table_name) in
            <foreach collection="array" item="name" open="(" separator="," close=")">
                #{name}
            </foreach>
        </if>
        <if test="@com.ruoyi.common.mybatis.helper.DataBaseHelper@isPostgerSql()">
            select table_name, table_comment, create_time, update_time
            from (
                SELECT c.relname AS table_name,
                        obj_description(c.oid) AS table_comment,
                        CURRENT_TIMESTAMP AS create_time,
                        CURRENT_TIMESTAMP AS update_time
                FROM pg_class c
                    LEFT JOIN pg_namespace n ON n.oid = c.relnamespace
                WHERE (c.relkind = ANY (ARRAY ['r'::"char", 'p'::"char"]))
                    AND c.relname != 'spatial_%'::text
                    AND n.nspname = 'public'::name
                    AND n.nspname <![CDATA[ <> ]]> ''::name
            ) list_table
            where table_name NOT LIKE 'xxl_job_%' and table_name NOT LIKE 'gen_%'
            and table_name in
            <foreach collection="array" item="name" open="(" separator="," close=")">
                #{name}
            </foreach>
        </if>
        <if test="@com.ruoyi.common.mybatis.helper.DataBaseHelper@isSqlServer()">
            SELECT cast(D.NAME as nvarchar) as table_name,
                   cast(F.VALUE as nvarchar) as table_comment,
                   crdate as create_time,
                   refdate as update_time
            FROM SYSOBJECTS D
                INNER JOIN SYS.EXTENDED_PROPERTIES F ON D.ID = F.MAJOR_ID
                    AND F.MINOR_ID = 0 AND D.XTYPE = 'U' AND D.NAME != 'DTPROPERTIES'
                    AND D.NAME NOT LIKE 'xxl_job_%' AND D.NAME NOT LIKE 'gen_%'
                    AND D.NAME in
            <foreach collection="array" item="name" open="(" separator="," close=")">
                #{name}
            </foreach>
        </if>
    </select>
    <select id="selectTableByName" parameterType="String" resultMap="GenTableResult">
        <if test="@com.ruoyi.common.mybatis.helper.DataBaseHelper@isMySql()">
            select table_name, table_comment, create_time, update_time from information_schema.tables
            where table_name NOT LIKE 'xxl_job_%' and table_name NOT LIKE 'gen_%' and table_schema = (select database())
            and table_name = #{tableName}
        </if>
        <if test="@com.ruoyi.common.mybatis.helper.DataBaseHelper@isOracle()">
            select lower(dt.table_name) as table_name, dtc.comments as table_comment, uo.created as create_time, uo.last_ddl_time as update_time
            from user_tables dt, user_tab_comments dtc, user_objects uo
            where dt.table_name = dtc.table_name
            and dt.table_name = uo.object_name
            and uo.object_type = 'TABLE'
            AND dt.table_name NOT LIKE 'XXL_JOB_%' AND dt.table_name NOT LIKE 'GEN_%'
            AND dt.table_name NOT IN (select table_name from gen_table)
            and lower(dt.table_name) = #{tableName}
        </if>
        <if test="@com.ruoyi.common.mybatis.helper.DataBaseHelper@isPostgerSql()">
            select table_name, table_comment, create_time, update_time
            from (
                SELECT c.relname AS table_name,
                        obj_description(c.oid) AS table_comment,
                        CURRENT_TIMESTAMP AS create_time,
                        CURRENT_TIMESTAMP AS update_time
                FROM pg_class c
                    LEFT JOIN pg_namespace n ON n.oid = c.relnamespace
                WHERE (c.relkind = ANY (ARRAY ['r'::"char", 'p'::"char"]))
                    AND c.relname != 'spatial_%'::text
                    AND n.nspname = 'public'::name
                    AND n.nspname <![CDATA[ <> ]]> ''::name
            ) list_table
            where table_name NOT LIKE 'xxl_job_%' and table_name NOT LIKE 'gen_%'
            and table_name = #{tableName}
        </if>
        <if test="@com.ruoyi.common.mybatis.helper.DataBaseHelper@isSqlServer()">
            SELECT cast(D.NAME as nvarchar) as table_name,
                   cast(F.VALUE as nvarchar) as table_comment,
                   crdate as create_time,
                   refdate as update_time
            FROM SYSOBJECTS D
                INNER JOIN SYS.EXTENDED_PROPERTIES F ON D.ID = F.MAJOR_ID
                    AND F.MINOR_ID = 0 AND D.XTYPE = 'U' AND D.NAME != 'DTPROPERTIES'
                    AND D.NAME NOT LIKE 'xxl_job_%' AND D.NAME NOT LIKE 'gen_%'
                    AND D.NAME = #{tableName}
        </if>
    </select>
    <select id="selectGenTableById" parameterType="Long" resultMap="GenTableResult">
        SELECT t.table_id, t.table_name, t.table_comment, t.sub_table_name, t.sub_table_fk_name, t.class_name, t.tpl_category, t.package_name, t.module_name, t.business_name, t.function_name, t.function_author, t.gen_type, t.gen_path, t.options, t.remark,
               c.column_id, c.column_name, c.column_comment, c.column_type, c.java_type, c.java_field, c.is_pk, c.is_increment, c.is_required, c.is_insert, c.is_edit, c.is_list, c.is_query, c.query_type, c.html_type, c.dict_type, c.sort
        FROM gen_table t
             LEFT JOIN gen_table_column c ON t.table_id = c.table_id
        where t.table_id = #{tableId} order by c.sort
    </select>
    <select id="selectGenTableByName" parameterType="String" resultMap="GenTableResult">
        SELECT t.table_id, t.table_name, t.table_comment, t.sub_table_name, t.sub_table_fk_name, t.class_name, t.tpl_category, t.package_name, t.module_name, t.business_name, t.function_name, t.function_author, t.gen_type, t.gen_path, t.options, t.remark,
               c.column_id, c.column_name, c.column_comment, c.column_type, c.java_type, c.java_field, c.is_pk, c.is_increment, c.is_required, c.is_insert, c.is_edit, c.is_list, c.is_query, c.query_type, c.html_type, c.dict_type, c.sort
        FROM gen_table t
             LEFT JOIN gen_table_column c ON t.table_id = c.table_id
        where t.table_name = #{tableName} order by c.sort
    </select>
    <select id="selectGenTableAll" parameterType="String" resultMap="GenTableResult">
        SELECT t.table_id, t.table_name, t.table_comment, t.sub_table_name, t.sub_table_fk_name, t.class_name, t.tpl_category, t.package_name, t.module_name, t.business_name, t.function_name, t.function_author, t.options, t.remark,
               c.column_id, c.column_name, c.column_comment, c.column_type, c.java_type, c.java_field, c.is_pk, c.is_increment, c.is_required, c.is_insert, c.is_edit, c.is_list, c.is_query, c.query_type, c.html_type, c.dict_type, c.sort
        FROM gen_table t
             LEFT JOIN gen_table_column c ON t.table_id = c.table_id
        order by c.sort
    </select>
</mapper>
ruoyi-modules/ruoyi-generator/src/main/resources/mapper/package-info.md
ruoyi-modules/ruoyi-generator/src/main/resources/vm/java/bo.java.vm
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,60 @@
package ${packageName}.domain.bo;
import com.ruoyi.common.core.validate.AddGroup;
import com.ruoyi.common.core.validate.EditGroup;
import lombok.Data;
import lombok.EqualsAndHashCode;
import jakarta.validation.constraints.*;
import java.util.Date;
#foreach ($import in $importList)
import ${import};
#end
#if($table.crud || $table.sub)
import com.ruoyi.common.core.web.domain.BaseEntity;
#elseif($table.tree)
import com.ruoyi.common.core.web.domain.TreeEntity;
#end
/**
 * ${functionName}业务对象 ${tableName}
 *
 * @author ${author}
 * @date ${datetime}
 */
#if($table.crud || $table.sub)
#set($Entity="BaseEntity")
#elseif($table.tree)
#set($Entity="TreeEntity<${ClassName}Bo>")
#end
@Data
@EqualsAndHashCode(callSuper = true)
public class ${ClassName}Bo extends ${Entity} {
#foreach ($column in $columns)
#if(!$table.isSuperColumn($column.javaField) && ($column.query || $column.isInsert || $column.isEdit))
    /**
     * $column.columnComment
     */
#if($column.isInsert && $column.isEdit)
#set($Group="AddGroup.class, EditGroup.class")
#elseif($column.isInsert)
#set($Group="AddGroup.class")
#elseif($column.isEdit)
#set($Group="EditGroup.class")
#end
#if($column.isRequired == 1)
#if($column.javaType == 'String')
    @NotBlank(message = "$column.columnComment不能为空", groups = { $Group })
#else
    @NotNull(message = "$column.columnComment不能为空", groups = { $Group })
#end
#end
    private $column.javaType $column.javaField;
#end
#end
}
ruoyi-modules/ruoyi-generator/src/main/resources/vm/java/controller.java.vm
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,117 @@
package ${packageName}.controller;
import java.util.List;
import java.util.Arrays;
import java.util.concurrent.TimeUnit;
import lombok.RequiredArgsConstructor;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.constraints.*;
import cn.dev33.satoken.annotation.SaCheckPermission;
import org.springframework.web.bind.annotation.*;
import org.springframework.validation.annotation.Validated;
import com.ruoyi.common.web.annotation.RepeatSubmit;
import com.ruoyi.common.log.annotation.Log;
import com.ruoyi.common.core.web.controller.BaseController;
import com.ruoyi.common.mybatis.core.page.PageQuery;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.core.validate.AddGroup;
import com.ruoyi.common.core.validate.EditGroup;
import com.ruoyi.common.log.enums.BusinessType;
import com.ruoyi.common.excel.utils.ExcelUtil;
import ${packageName}.domain.vo.${ClassName}Vo;
import ${packageName}.domain.bo.${ClassName}Bo;
import ${packageName}.service.I${ClassName}Service;
#if($table.crud || $table.sub)
import com.ruoyi.common.mybatis.core.page.TableDataInfo;
#elseif($table.tree)
#end
/**
 * ${functionName}
 *
 * @author ${author}
 * @date ${datetime}
 */
@Validated
@RequiredArgsConstructor
@RestController
@RequestMapping("/${moduleName}/${businessName}")
public class ${ClassName}Controller extends BaseController {
    private final I${ClassName}Service i${ClassName}Service;
    /**
     * æŸ¥è¯¢${functionName}列表
     */
    @SaCheckPermission("${permissionPrefix}:list")
    @GetMapping("/list")
#if($table.crud || $table.sub)
    public TableDataInfo<${ClassName}Vo> list(${ClassName}Bo bo, PageQuery pageQuery) {
        return i${ClassName}Service.queryPageList(bo, pageQuery);
    }
#elseif($table.tree)
    public R<List<${ClassName}Vo>> list(${ClassName}Bo bo) {
        List<${ClassName}Vo> list = i${ClassName}Service.queryList(bo);
        return R.ok(list);
    }
#end
    /**
     * å¯¼å‡º${functionName}列表
     */
    @SaCheckPermission("${permissionPrefix}:export")
    @Log(title = "${functionName}", businessType = BusinessType.EXPORT)
    @PostMapping("/export")
    public void export(${ClassName}Bo bo, HttpServletResponse response) {
        List<${ClassName}Vo> list = i${ClassName}Service.queryList(bo);
        ExcelUtil.exportExcel(list, "${functionName}", ${ClassName}Vo.class, response);
    }
    /**
     * èŽ·å–${functionName}详细信息
     *
     * @param ${pkColumn.javaField} ä¸»é”®
     */
    @SaCheckPermission("${permissionPrefix}:query")
    @GetMapping("/{${pkColumn.javaField}}")
    public R<${ClassName}Vo> getInfo(@NotNull(message = "主键不能为空")
                                     @PathVariable ${pkColumn.javaType} ${pkColumn.javaField}) {
        return R.ok(i${ClassName}Service.queryById(${pkColumn.javaField}));
    }
    /**
     * æ–°å¢ž${functionName}
     */
    @SaCheckPermission("${permissionPrefix}:add")
    @Log(title = "${functionName}", businessType = BusinessType.INSERT)
    @RepeatSubmit()
    @PostMapping()
    public R<Void> add(@Validated(AddGroup.class) @RequestBody ${ClassName}Bo bo) {
        return toAjax(i${ClassName}Service.insertByBo(bo));
    }
    /**
     * ä¿®æ”¹${functionName}
     */
    @SaCheckPermission("${permissionPrefix}:edit")
    @Log(title = "${functionName}", businessType = BusinessType.UPDATE)
    @RepeatSubmit()
    @PutMapping()
    public R<Void> edit(@Validated(EditGroup.class) @RequestBody ${ClassName}Bo bo) {
        return toAjax(i${ClassName}Service.updateByBo(bo));
    }
    /**
     * åˆ é™¤${functionName}
     *
     * @param ${pkColumn.javaField}s ä¸»é”®ä¸²
     */
    @SaCheckPermission("${permissionPrefix}:remove")
    @Log(title = "${functionName}", businessType = BusinessType.DELETE)
    @DeleteMapping("/{${pkColumn.javaField}s}")
    public R<Void> remove(@NotEmpty(message = "主键不能为空")
                          @PathVariable ${pkColumn.javaType}[] ${pkColumn.javaField}s) {
        return toAjax(i${ClassName}Service.deleteWithValidByIds(Arrays.asList(${pkColumn.javaField}s), true));
    }
}
ruoyi-modules/ruoyi-generator/src/main/resources/vm/java/domain.java.vm
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,57 @@
package ${packageName}.domain;
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.io.Serial;
import java.io.Serializable;
import java.util.Date;
import java.math.BigDecimal;
#foreach ($import in $importList)
import ${import};
#end
#if($table.crud || $table.sub)
import com.ruoyi.common.core.web.domain.BaseEntity;
#elseif($table.tree)
import com.ruoyi.common.core.web.domain.TreeEntity;
#end
/**
 * ${functionName}对象 ${tableName}
 *
 * @author ${author}
 * @date ${datetime}
 */
#if($table.crud || $table.sub)
    #set($Entity="BaseEntity")
#elseif($table.tree)
    #set($Entity="TreeEntity<${ClassName}>")
#end
@Data
@EqualsAndHashCode(callSuper = true)
@TableName("${tableName}")
public class ${ClassName} extends ${Entity} {
    @Serial
    private static final long serialVersionUID = 1L;
#foreach ($column in $columns)
#if(!$table.isSuperColumn($column.javaField))
    /**
     * $column.columnComment
     */
#if($column.javaField=='delFlag')
    @TableLogic
#end
#if($column.javaField=='version')
    @Version
#end
#if($column.isPk==1)
    @TableId(value = "$column.columnName")
#end
    private $column.javaType $column.javaField;
#end
#end
}
ruoyi-modules/ruoyi-generator/src/main/resources/vm/java/mapper.java.vm
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,15 @@
package ${packageName}.mapper;
import ${packageName}.domain.${ClassName};
import ${packageName}.domain.vo.${ClassName}Vo;
import com.ruoyi.common.mybatis.core.mapper.BaseMapperPlus;
/**
 * ${functionName}Mapper接口
 *
 * @author ${author}
 * @date ${datetime}
 */
public interface ${ClassName}Mapper extends BaseMapperPlus<${ClassName}Mapper, ${ClassName}, ${ClassName}Vo> {
}
ruoyi-modules/ruoyi-generator/src/main/resources/vm/java/service.java.vm
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,53 @@
package ${packageName}.service;
import ${packageName}.domain.${ClassName};
import ${packageName}.domain.vo.${ClassName}Vo;
import ${packageName}.domain.bo.${ClassName}Bo;
#if($table.crud || $table.sub)
import com.ruoyi.common.mybatis.core.page.TableDataInfo;
import com.ruoyi.common.mybatis.core.page.PageQuery;
#end
import java.util.Collection;
import java.util.List;
/**
 * ${functionName}Service接口
 *
 * @author ${author}
 * @date ${datetime}
 */
public interface I${ClassName}Service {
    /**
     * æŸ¥è¯¢${functionName}
     */
    ${ClassName}Vo queryById(${pkColumn.javaType} ${pkColumn.javaField});
#if($table.crud || $table.sub)
    /**
     * æŸ¥è¯¢${functionName}列表
     */
    TableDataInfo<${ClassName}Vo> queryPageList(${ClassName}Bo bo, PageQuery pageQuery);
#end
    /**
     * æŸ¥è¯¢${functionName}列表
     */
    List<${ClassName}Vo> queryList(${ClassName}Bo bo);
    /**
     * æ–°å¢ž${functionName}
     */
    Boolean insertByBo(${ClassName}Bo bo);
    /**
     * ä¿®æ”¹${functionName}
     */
    Boolean updateByBo(${ClassName}Bo bo);
    /**
     * æ ¡éªŒå¹¶æ‰¹é‡åˆ é™¤${functionName}信息
     */
    Boolean deleteWithValidByIds(Collection<${pkColumn.javaType}> ids, Boolean isValid);
}
ruoyi-modules/ruoyi-generator/src/main/resources/vm/java/serviceImpl.java.vm
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,134 @@
package ${packageName}.service.impl;
import cn.hutool.core.bean.BeanUtil;
import com.ruoyi.common.core.utils.StringUtils;
#if($table.crud || $table.sub)
import com.ruoyi.common.mybatis.core.page.TableDataInfo;
import com.ruoyi.common.mybatis.core.page.PageQuery;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
#end
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import ${packageName}.domain.bo.${ClassName}Bo;
import ${packageName}.domain.vo.${ClassName}Vo;
import ${packageName}.domain.${ClassName};
import ${packageName}.mapper.${ClassName}Mapper;
import ${packageName}.service.I${ClassName}Service;
import java.util.List;
import java.util.Map;
import java.util.Collection;
/**
 * ${functionName}Service业务层处理
 *
 * @author ${author}
 * @date ${datetime}
 */
@RequiredArgsConstructor
@Service
public class ${ClassName}ServiceImpl implements I${ClassName}Service {
    private final ${ClassName}Mapper baseMapper;
    /**
     * æŸ¥è¯¢${functionName}
     */
    @Override
    public ${ClassName}Vo queryById(${pkColumn.javaType} ${pkColumn.javaField}){
        return baseMapper.selectVoById(${pkColumn.javaField});
    }
#if($table.crud || $table.sub)
    /**
     * æŸ¥è¯¢${functionName}列表
     */
    @Override
    public TableDataInfo<${ClassName}Vo> queryPageList(${ClassName}Bo bo, PageQuery pageQuery) {
        LambdaQueryWrapper<${ClassName}> lqw = buildQueryWrapper(bo);
        Page<${ClassName}Vo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
        return TableDataInfo.build(result);
    }
#end
    /**
     * æŸ¥è¯¢${functionName}列表
     */
    @Override
    public List<${ClassName}Vo> queryList(${ClassName}Bo bo) {
        LambdaQueryWrapper<${ClassName}> lqw = buildQueryWrapper(bo);
        return baseMapper.selectVoList(lqw);
    }
    private LambdaQueryWrapper<${ClassName}> buildQueryWrapper(${ClassName}Bo bo) {
        Map<String, Object> params = bo.getParams();
        LambdaQueryWrapper<${ClassName}> lqw = Wrappers.lambdaQuery();
#foreach($column in $columns)
#if($column.query)
#set($queryType=$column.queryType)
#set($javaField=$column.javaField)
#set($javaType=$column.javaType)
#set($columnName=$column.columnName)
#set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})
#set($mpMethod=$column.queryType.toLowerCase())
#if($queryType != 'BETWEEN')
#if($javaType == 'String')
#set($condition='StringUtils.isNotBlank(bo.get'+$AttrName+'())')
#else
#set($condition='bo.get'+$AttrName+'() != null')
#end
        lqw.$mpMethod($condition, ${ClassName}::get$AttrName, bo.get$AttrName());
#else
        lqw.between(params.get("begin$AttrName") != null && params.get("end$AttrName") != null,
            ${ClassName}::get$AttrName ,params.get("begin$AttrName"), params.get("end$AttrName"));
#end
#end
#end
        return lqw;
    }
    /**
     * æ–°å¢ž${functionName}
     */
    @Override
    public Boolean insertByBo(${ClassName}Bo bo) {
        ${ClassName} add = BeanUtil.toBean(bo, ${ClassName}.class);
        validEntityBeforeSave(add);
        boolean flag = baseMapper.insert(add) > 0;
#set($pk=$pkColumn.javaField.substring(0,1).toUpperCase() + ${pkColumn.javaField.substring(1)})
        if (flag) {
            bo.set$pk(add.get$pk());
        }
        return flag;
    }
    /**
     * ä¿®æ”¹${functionName}
     */
    @Override
    public Boolean updateByBo(${ClassName}Bo bo) {
        ${ClassName} update = BeanUtil.toBean(bo, ${ClassName}.class);
        validEntityBeforeSave(update);
        return baseMapper.updateById(update) > 0;
    }
    /**
     * ä¿å­˜å‰çš„æ•°æ®æ ¡éªŒ
     */
    private void validEntityBeforeSave(${ClassName} entity){
        //TODO åšä¸€äº›æ•°æ®æ ¡éªŒ,如唯一约束
    }
    /**
     * æ‰¹é‡åˆ é™¤${functionName}
     */
    @Override
    public Boolean deleteWithValidByIds(Collection<${pkColumn.javaType}> ids, Boolean isValid) {
        if(isValid){
            //TODO åšä¸€äº›ä¸šåŠ¡ä¸Šçš„æ ¡éªŒ,判断是否需要校验
        }
        return baseMapper.deleteBatchIds(ids) > 0;
    }
}
ruoyi-modules/ruoyi-generator/src/main/resources/vm/java/vo.java.vm
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,56 @@
package ${packageName}.domain.vo;
#foreach ($import in $importList)
import ${import};
#end
import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
import com.alibaba.excel.annotation.ExcelProperty;
import com.ruoyi.common.excel.annotation.ExcelDictFormat;
import com.ruoyi.common.excel.convert.ExcelDictConvert;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
import java.util.Date;
/**
 * ${functionName}视图对象 ${tableName}
 *
 * @author ${author}
 * @date ${datetime}
 */
@Data
@ExcelIgnoreUnannotated
public class ${ClassName}Vo implements Serializable {
    @Serial
    private static final long serialVersionUID = 1L;
#foreach ($column in $columns)
#if($column.isList)
    /**
     * $column.columnComment
     */
#set($parentheseIndex=$column.columnComment.indexOf("("))
#if($parentheseIndex != -1)
#set($comment=$column.columnComment.substring(0, $parentheseIndex))
#else
#set($comment=$column.columnComment)
#end
#if(${column.dictType} && ${column.dictType} != '')
    @ExcelProperty(value = "${comment}", converter = ExcelDictConvert.class)
    @ExcelDictFormat(dictType = "${column.dictType}")
#elseif($parentheseIndex != -1)
    @ExcelProperty(value = "${comment}", converter = ExcelDictConvert.class)
    @ExcelDictFormat(readConverterExp = "$column.readConverterExp()")
#else
    @ExcelProperty(value = "${comment}")
#end
    private $column.javaType $column.javaField;
#end
#end
}
ruoyi-modules/ruoyi-generator/src/main/resources/vm/js/api.js.vm
ruoyi-modules/ruoyi-generator/src/main/resources/vm/sql/oracle/sql.vm
ruoyi-modules/ruoyi-generator/src/main/resources/vm/sql/postgres/sql.vm
ruoyi-modules/ruoyi-generator/src/main/resources/vm/sql/sql.vm
ruoyi-modules/ruoyi-generator/src/main/resources/vm/sql/sqlserver/sql.vm
ruoyi-modules/ruoyi-generator/src/main/resources/vm/vue/index-tree.vue.vm
ruoyi-modules/ruoyi-generator/src/main/resources/vm/vue/index.vue.vm
ruoyi-modules/ruoyi-generator/src/main/resources/vm/vue/v3/index-tree.vue.vm
ruoyi-modules/ruoyi-generator/src/main/resources/vm/vue/v3/index.vue.vm
ruoyi-modules/ruoyi-generator/src/main/resources/vm/vue/v3/readme.txt
ruoyi-modules/ruoyi-generator/src/main/resources/vm/xml/mapper.xml.vm
ruoyi-modules/ruoyi-job/pom.xml
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,36 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <groupId>com.ruoyi</groupId>
        <artifactId>ruoyi-modules</artifactId>
        <version>${revision}</version>
        <relativePath>../pom.xml</relativePath>
    </parent>
    <modelVersion>4.0.0</modelVersion>
    <packaging>jar</packaging>
    <artifactId>ruoyi-job</artifactId>
    <description>
        ä»»åŠ¡è°ƒåº¦
    </description>
    <dependencies>
        <!-- é€šç”¨å·¥å…·-->
        <dependency>
            <groupId>com.ruoyi</groupId>
            <artifactId>ruoyi-common-core</artifactId>
        </dependency>
        <!-- xxl-job-core -->
        <dependency>
            <groupId>com.xuxueli</groupId>
            <artifactId>xxl-job-core</artifactId>
        </dependency>
    </dependencies>
</project>
ruoyi-modules/ruoyi-job/src/main/java/com/ruoyi/job/config/XxlJobConfig.java
ruoyi-modules/ruoyi-job/src/main/java/com/ruoyi/job/config/properties/XxlJobProperties.java
ruoyi-modules/ruoyi-job/src/main/java/com/ruoyi/job/service/SampleService.java
ruoyi-modules/ruoyi-system/pom.xml
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,80 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <groupId>com.ruoyi</groupId>
        <artifactId>ruoyi-modules</artifactId>
        <version>${revision}</version>
        <relativePath>../pom.xml</relativePath>
    </parent>
    <modelVersion>4.0.0</modelVersion>
    <artifactId>ruoyi-system</artifactId>
    <description>
        system系统模块
    </description>
    <dependencies>
        <!-- é€šç”¨å·¥å…·-->
        <dependency>
            <groupId>com.ruoyi</groupId>
            <artifactId>ruoyi-common-core</artifactId>
        </dependency>
        <dependency>
            <groupId>com.ruoyi</groupId>
            <artifactId>ruoyi-common-mybatis</artifactId>
        </dependency>
        <dependency>
            <groupId>com.ruoyi</groupId>
            <artifactId>ruoyi-common-dict</artifactId>
        </dependency>
        <!-- OSS功能模块 -->
        <dependency>
            <groupId>com.ruoyi</groupId>
            <artifactId>ruoyi-common-oss</artifactId>
        </dependency>
        <dependency>
            <groupId>com.ruoyi</groupId>
            <artifactId>ruoyi-common-log</artifactId>
        </dependency>
        <!-- excel-->
        <dependency>
            <groupId>com.ruoyi</groupId>
            <artifactId>ruoyi-common-excel</artifactId>
        </dependency>
        <!-- SMS功能模块 -->
        <dependency>
            <groupId>com.ruoyi</groupId>
            <artifactId>ruoyi-common-sms</artifactId>
        </dependency>
        <dependency>
            <groupId>com.ruoyi</groupId>
            <artifactId>ruoyi-common-satoken</artifactId>
        </dependency>
        <dependency>
            <groupId>com.ruoyi</groupId>
            <artifactId>ruoyi-common-security</artifactId>
        </dependency>
        <dependency>
            <groupId>com.ruoyi</groupId>
            <artifactId>ruoyi-common-web</artifactId>
        </dependency>
        <dependency>
            <groupId>com.ruoyi</groupId>
            <artifactId>ruoyi-common-idempotent</artifactId>
        </dependency>
    </dependencies>
</project>
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/controller/monitor/CacheController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,169 @@
package com.ruoyi.system.controller.monitor;
import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.hutool.core.collection.CollUtil;
import com.ruoyi.common.core.constant.CacheConstants;
import com.ruoyi.common.core.constant.CacheNames;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.core.utils.JsonUtils;
import com.ruoyi.common.core.utils.StringUtils;
import com.ruoyi.common.redis.utils.CacheUtils;
import com.ruoyi.common.redis.utils.RedisUtils;
import com.ruoyi.system.domain.SysCache;
import lombok.RequiredArgsConstructor;
import org.redisson.spring.data.connection.RedissonConnectionFactory;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.web.bind.annotation.*;
import java.util.*;
import java.util.stream.Collectors;
/**
 * ç¼“存监控
 *
 * @author Lion Li
 */
@RequiredArgsConstructor
@RestController
@RequestMapping("/monitor/cache")
public class CacheController {
    private final RedissonConnectionFactory connectionFactory;
    private final static List<SysCache> CACHES = new ArrayList<>();
    static {
        CACHES.add(new SysCache(CacheConstants.LOGIN_TOKEN_KEY, "用户信息"));
        CACHES.add(new SysCache(CacheConstants.ONLINE_TOKEN_KEY, "在线用户"));
        CACHES.add(new SysCache(CacheNames.SYS_CONFIG, "配置信息"));
        CACHES.add(new SysCache(CacheNames.SYS_DICT, "数据字典"));
        CACHES.add(new SysCache(CacheConstants.CAPTCHA_CODE_KEY, "验证码"));
        CACHES.add(new SysCache(CacheConstants.REPEAT_SUBMIT_KEY, "防重提交"));
        CACHES.add(new SysCache(CacheConstants.RATE_LIMIT_KEY, "限流处理"));
        CACHES.add(new SysCache(CacheNames.SYS_OSS_CONFIG, "OSS配置"));
        CACHES.add(new SysCache(CacheConstants.PWD_ERR_CNT_KEY, "密码错误次数"));
    }
    /**
     * èŽ·å–ç¼“å­˜ç›‘æŽ§åˆ—è¡¨
     */
    @SaCheckPermission("monitor:cache:list")
    @GetMapping()
    public R<Map<String, Object>> getInfo() throws Exception {
        RedisConnection connection = connectionFactory.getConnection();
        Properties info = connection.commands().info();
        Properties commandStats = connection.commands().info("commandstats");
        Long dbSize = connection.commands().dbSize();
        Map<String, Object> result = new HashMap<>(3);
        result.put("info", info);
        result.put("dbSize", dbSize);
        List<Map<String, String>> pieList = new ArrayList<>();
        if (commandStats != null) {
            commandStats.stringPropertyNames().forEach(key -> {
                Map<String, String> data = new HashMap<>(2);
                String property = commandStats.getProperty(key);
                data.put("name", StringUtils.removeStart(key, "cmdstat_"));
                data.put("value", StringUtils.substringBetween(property, "calls=", ",usec"));
                pieList.add(data);
            });
        }
        result.put("commandStats", pieList);
        return R.ok(result);
    }
    /**
     * èŽ·å–ç¼“å­˜ç›‘æŽ§ç¼“å­˜ååˆ—è¡¨
     */
    @SaCheckPermission("monitor:cache:list")
    @GetMapping("/getNames")
    public R<List<SysCache>> cache() {
        return R.ok(CACHES);
    }
    /**
     * èŽ·å–ç¼“å­˜ç›‘æŽ§Key列表
     *
     * @param cacheName ç¼“存名
     */
    @SaCheckPermission("monitor:cache:list")
    @GetMapping("/getKeys/{cacheName}")
    public R<Collection<String>> getCacheKeys(@PathVariable String cacheName) {
        Collection<String> cacheKeys = new HashSet<>(0);
        if (isCacheNames(cacheName)) {
            Set<Object> keys = CacheUtils.keys(cacheName);
            if (CollUtil.isNotEmpty(keys)) {
                cacheKeys = keys.stream().map(Object::toString).collect(Collectors.toList());
            }
        } else {
            cacheKeys = RedisUtils.keys(cacheName + "*");
        }
        return R.ok(cacheKeys);
    }
    /**
     * èŽ·å–ç¼“å­˜ç›‘æŽ§ç¼“å­˜å€¼è¯¦æƒ…
     *
     * @param cacheName ç¼“存名
     * @param cacheKey  ç¼“å­˜key
     */
    @SaCheckPermission("monitor:cache:list")
    @GetMapping("/getValue/{cacheName}/{cacheKey}")
    public R<SysCache> getCacheValue(@PathVariable String cacheName, @PathVariable String cacheKey) {
        Object cacheValue;
        if (isCacheNames(cacheName)) {
            cacheValue = CacheUtils.get(cacheName, cacheKey);
        } else {
            cacheValue = RedisUtils.getCacheObject(cacheKey);
        }
        SysCache sysCache = new SysCache(cacheName, cacheKey, JsonUtils.toJsonString(cacheValue));
        return R.ok(sysCache);
    }
    /**
     * æ¸…理缓存监控缓存名
     *
     * @param cacheName ç¼“存名
     */
    @SaCheckPermission("monitor:cache:list")
    @DeleteMapping("/clearCacheName/{cacheName}")
    public R<Void> clearCacheName(@PathVariable String cacheName) {
        if (isCacheNames(cacheName)) {
            CacheUtils.clear(cacheName);
        } else {
            RedisUtils.deleteKeys(cacheName + "*");
        }
        return R.ok();
    }
    /**
     * æ¸…理缓存监控Key
     *
     * @param cacheKey key名
     */
    @SaCheckPermission("monitor:cache:list")
    @DeleteMapping("/clearCacheKey/{cacheName}/{cacheKey}")
    public R<Void> clearCacheKey(@PathVariable String cacheName, @PathVariable String cacheKey) {
        if (isCacheNames(cacheName)) {
            CacheUtils.evict(cacheName, cacheKey);
        } else {
            RedisUtils.deleteObject(cacheKey);
        }
        return R.ok();
    }
    /**
     * æ¸…理全部缓存监控
     */
    @SaCheckPermission("monitor:cache:list")
    @DeleteMapping("/clearCacheAll")
    public R<Void> clearCacheAll() {
        RedisUtils.deleteKeys("*");
        return R.ok();
    }
    private boolean isCacheNames(String cacheName) {
        return !StringUtils.contains(cacheName, ":");
    }
}
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/controller/monitor/SysLogininforController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,88 @@
package com.ruoyi.system.controller.monitor;
import cn.dev33.satoken.annotation.SaCheckPermission;
import com.ruoyi.common.core.constant.CacheConstants;
import com.ruoyi.common.mybatis.core.page.PageQuery;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.mybatis.core.page.TableDataInfo;
import com.ruoyi.common.core.web.controller.BaseController;
import com.ruoyi.common.excel.utils.ExcelUtil;
import com.ruoyi.common.log.annotation.Log;
import com.ruoyi.common.log.enums.BusinessType;
import com.ruoyi.common.redis.utils.RedisUtils;
import com.ruoyi.system.domain.SysLogininfor;
import com.ruoyi.system.service.ISysLogininforService;
import lombok.RequiredArgsConstructor;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import jakarta.servlet.http.HttpServletResponse;
import java.util.List;
/**
 * ç³»ç»Ÿè®¿é—®è®°å½•
 *
 * @author Lion Li
 */
@Validated
@RequiredArgsConstructor
@RestController
@RequestMapping("/monitor/logininfor")
public class SysLogininforController extends BaseController {
    private final ISysLogininforService logininforService;
    /**
     * èŽ·å–ç³»ç»Ÿè®¿é—®è®°å½•åˆ—è¡¨
     */
    @SaCheckPermission("monitor:logininfor:list")
    @GetMapping("/list")
    public TableDataInfo<SysLogininfor> list(SysLogininfor logininfor, PageQuery pageQuery) {
        return logininforService.selectPageLogininforList(logininfor, pageQuery);
    }
    /**
     * å¯¼å‡ºç³»ç»Ÿè®¿é—®è®°å½•列表
     */
    @Log(title = "登录日志", businessType = BusinessType.EXPORT)
    @SaCheckPermission("monitor:logininfor:export")
    @PostMapping("/export")
    public void export(SysLogininfor logininfor, HttpServletResponse response) {
        List<SysLogininfor> list = logininforService.selectLogininforList(logininfor);
        ExcelUtil.exportExcel(list, "登录日志", SysLogininfor.class, response);
    }
    /**
     * æ‰¹é‡åˆ é™¤ç™»å½•日志
     * @param infoIds æ—¥å¿—ids
     */
    @SaCheckPermission("monitor:logininfor:remove")
    @Log(title = "登录日志", businessType = BusinessType.DELETE)
    @DeleteMapping("/{infoIds}")
    public R<Void> remove(@PathVariable Long[] infoIds) {
        return toAjax(logininforService.deleteLogininforByIds(infoIds));
    }
    /**
     * æ¸…理系统访问记录
     */
    @SaCheckPermission("monitor:logininfor:remove")
    @Log(title = "登录日志", businessType = BusinessType.CLEAN)
    @DeleteMapping("/clean")
    public R<Void> clean() {
        logininforService.cleanLogininfor();
        return R.ok();
    }
    @SaCheckPermission("monitor:logininfor:unlock")
    @Log(title = "账户解锁", businessType = BusinessType.OTHER)
    @GetMapping("/unlock/{userName}")
    public R<Void> unlock(@PathVariable("userName") String userName) {
        String loginName = CacheConstants.PWD_ERR_CNT_KEY + userName;
        if (RedisUtils.hasKey(loginName)) {
            RedisUtils.deleteObject(loginName);
        }
        return R.ok();
    }
}
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/controller/monitor/SysOperlogController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,74 @@
package com.ruoyi.system.controller.monitor;
import cn.dev33.satoken.annotation.SaCheckPermission;
import com.ruoyi.common.log.annotation.Log;
import com.ruoyi.common.core.web.controller.BaseController;
import com.ruoyi.common.mybatis.core.page.PageQuery;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.mybatis.core.page.TableDataInfo;
import com.ruoyi.common.log.enums.BusinessType;
import com.ruoyi.common.excel.utils.ExcelUtil;
import com.ruoyi.system.domain.SysOperLog;
import com.ruoyi.system.service.ISysOperLogService;
import lombok.RequiredArgsConstructor;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import jakarta.servlet.http.HttpServletResponse;
import java.util.List;
/**
 * æ“ä½œæ—¥å¿—记录
 *
 * @author Lion Li
 */
@Validated
@RequiredArgsConstructor
@RestController
@RequestMapping("/monitor/operlog")
public class SysOperlogController extends BaseController {
    private final ISysOperLogService operLogService;
    /**
     * èŽ·å–æ“ä½œæ—¥å¿—è®°å½•åˆ—è¡¨
     */
    @SaCheckPermission("monitor:operlog:list")
    @GetMapping("/list")
    public TableDataInfo<SysOperLog> list(SysOperLog operLog, PageQuery pageQuery) {
        return operLogService.selectPageOperLogList(operLog, pageQuery);
    }
    /**
     * å¯¼å‡ºæ“ä½œæ—¥å¿—记录列表
     */
    @Log(title = "操作日志", businessType = BusinessType.EXPORT)
    @SaCheckPermission("monitor:operlog:export")
    @PostMapping("/export")
    public void export(SysOperLog operLog, HttpServletResponse response) {
        List<SysOperLog> list = operLogService.selectOperLogList(operLog);
        ExcelUtil.exportExcel(list, "操作日志", SysOperLog.class, response);
    }
    /**
     * æ‰¹é‡åˆ é™¤æ“ä½œæ—¥å¿—记录
     * @param operIds æ—¥å¿—ids
     */
    @Log(title = "操作日志", businessType = BusinessType.DELETE)
    @SaCheckPermission("monitor:operlog:remove")
    @DeleteMapping("/{operIds}")
    public R<Void> remove(@PathVariable Long[] operIds) {
        return toAjax(operLogService.deleteOperLogByIds(operIds));
    }
    /**
     * æ¸…理操作日志记录
     */
    @Log(title = "操作日志", businessType = BusinessType.CLEAN)
    @SaCheckPermission("monitor:operlog:remove")
    @DeleteMapping("/clean")
    public R<Void> clean() {
        operLogService.cleanOperLog();
        return R.ok();
    }
}
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/controller/monitor/SysUserOnlineController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,90 @@
package com.ruoyi.system.controller.monitor;
import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.dev33.satoken.exception.NotLoginException;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.bean.BeanUtil;
import com.ruoyi.common.log.annotation.Log;
import com.ruoyi.common.core.constant.CacheConstants;
import com.ruoyi.common.core.web.controller.BaseController;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.core.domain.dto.UserOnlineDTO;
import com.ruoyi.common.mybatis.core.page.TableDataInfo;
import com.ruoyi.common.log.enums.BusinessType;
import com.ruoyi.common.core.utils.StreamUtils;
import com.ruoyi.common.core.utils.StringUtils;
import com.ruoyi.common.redis.utils.RedisUtils;
import com.ruoyi.system.domain.SysUserOnline;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
 * åœ¨çº¿ç”¨æˆ·ç›‘控
 *
 * @author Lion Li
 */
@RequiredArgsConstructor
@RestController
@RequestMapping("/monitor/online")
public class SysUserOnlineController extends BaseController {
    /**
     * èŽ·å–åœ¨çº¿ç”¨æˆ·ç›‘æŽ§åˆ—è¡¨
     *
     * @param ipaddr   IP地址
     * @param userName ç”¨æˆ·å
     */
    @SaCheckPermission("monitor:online:list")
    @GetMapping("/list")
    public TableDataInfo<SysUserOnline> list(String ipaddr, String userName) {
        // èŽ·å–æ‰€æœ‰æœªè¿‡æœŸçš„ token
        List<String> keys = StpUtil.searchTokenValue("", 0, -1, false);
        List<UserOnlineDTO> userOnlineDTOList = new ArrayList<>();
        for (String key : keys) {
            String token = key.replace(CacheConstants.LOGIN_TOKEN_KEY, "");
            // å¦‚果已经过期则跳过
            if (StpUtil.stpLogic.getTokenActivityTimeoutByToken(token) < -1) {
                continue;
            }
            userOnlineDTOList.add(RedisUtils.getCacheObject(CacheConstants.ONLINE_TOKEN_KEY + token));
        }
        if (StringUtils.isNotEmpty(ipaddr) && StringUtils.isNotEmpty(userName)) {
            userOnlineDTOList = StreamUtils.filter(userOnlineDTOList, userOnline ->
                StringUtils.equals(ipaddr, userOnline.getIpaddr()) &&
                    StringUtils.equals(userName, userOnline.getUserName())
            );
        } else if (StringUtils.isNotEmpty(ipaddr)) {
            userOnlineDTOList = StreamUtils.filter(userOnlineDTOList, userOnline ->
                StringUtils.equals(ipaddr, userOnline.getIpaddr())
            );
        } else if (StringUtils.isNotEmpty(userName)) {
            userOnlineDTOList = StreamUtils.filter(userOnlineDTOList, userOnline ->
                StringUtils.equals(userName, userOnline.getUserName())
            );
        }
        Collections.reverse(userOnlineDTOList);
        userOnlineDTOList.removeAll(Collections.singleton(null));
        List<SysUserOnline> userOnlineList = BeanUtil.copyToList(userOnlineDTOList, SysUserOnline.class);
        return TableDataInfo.build(userOnlineList);
    }
    /**
     * å¼ºé€€ç”¨æˆ·
     *
     * @param tokenId token值
     */
    @SaCheckPermission("monitor:online:forceLogout")
    @Log(title = "在线用户", businessType = BusinessType.FORCE)
    @DeleteMapping("/{tokenId}")
    public R<Void> forceLogout(@PathVariable String tokenId) {
        try {
            StpUtil.kickoutByTokenValue(tokenId);
        } catch (NotLoginException ignored) {
        }
        return R.ok();
    }
}
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/controller/system/SysConfigController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,137 @@
package com.ruoyi.system.controller.system;
import cn.dev33.satoken.annotation.SaCheckPermission;
import com.ruoyi.common.log.annotation.Log;
import com.ruoyi.common.core.constant.UserConstants;
import com.ruoyi.common.core.web.controller.BaseController;
import com.ruoyi.common.mybatis.core.page.PageQuery;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.mybatis.core.page.TableDataInfo;
import com.ruoyi.common.log.enums.BusinessType;
import com.ruoyi.common.excel.utils.ExcelUtil;
import com.ruoyi.system.domain.SysConfig;
import com.ruoyi.system.service.ISysConfigService;
import lombok.RequiredArgsConstructor;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import jakarta.servlet.http.HttpServletResponse;
import java.util.List;
/**
 * å‚数配置 ä¿¡æ¯æ“ä½œå¤„理
 *
 * @author Lion Li
 */
@Validated
@RequiredArgsConstructor
@RestController
@RequestMapping("/system/config")
public class SysConfigController extends BaseController {
    private final ISysConfigService configService;
    /**
     * èŽ·å–å‚æ•°é…ç½®åˆ—è¡¨
     */
    @SaCheckPermission("system:config:list")
    @GetMapping("/list")
    public TableDataInfo<SysConfig> list(SysConfig config, PageQuery pageQuery) {
        return configService.selectPageConfigList(config, pageQuery);
    }
    /**
     * å¯¼å‡ºå‚数配置列表
     */
    @Log(title = "参数管理", businessType = BusinessType.EXPORT)
    @SaCheckPermission("system:config:export")
    @PostMapping("/export")
    public void export(SysConfig config, HttpServletResponse response) {
        List<SysConfig> list = configService.selectConfigList(config);
        ExcelUtil.exportExcel(list, "参数数据", SysConfig.class, response);
    }
    /**
     * æ ¹æ®å‚数编号获取详细信息
     *
     * @param configId å‚æ•°ID
     */
    @SaCheckPermission("system:config:query")
    @GetMapping(value = "/{configId}")
    public R<SysConfig> getInfo(@PathVariable Long configId) {
        return R.ok(configService.selectConfigById(configId));
    }
    /**
     * æ ¹æ®å‚数键名查询参数值
     *
     * @param configKey å‚æ•°Key
     */
    @GetMapping(value = "/configKey/{configKey}")
    public R<Void> getConfigKey(@PathVariable String configKey) {
        return R.ok(configService.selectConfigByKey(configKey));
    }
    /**
     * æ–°å¢žå‚数配置
     */
    @SaCheckPermission("system:config:add")
    @Log(title = "参数管理", businessType = BusinessType.INSERT)
    @PostMapping
    public R<Void> add(@Validated @RequestBody SysConfig config) {
        if (UserConstants.NOT_UNIQUE.equals(configService.checkConfigKeyUnique(config))) {
            return R.fail("新增参数'" + config.getConfigName() + "'失败,参数键名已存在");
        }
        configService.insertConfig(config);
        return R.ok();
    }
    /**
     * ä¿®æ”¹å‚数配置
     */
    @SaCheckPermission("system:config:edit")
    @Log(title = "参数管理", businessType = BusinessType.UPDATE)
    @PutMapping
    public R<Void> edit(@Validated @RequestBody SysConfig config) {
        if (UserConstants.NOT_UNIQUE.equals(configService.checkConfigKeyUnique(config))) {
            return R.fail("修改参数'" + config.getConfigName() + "'失败,参数键名已存在");
        }
        configService.updateConfig(config);
        return R.ok();
    }
    /**
     * æ ¹æ®å‚数键名修改参数配置
     */
    @SaCheckPermission("system:config:edit")
    @Log(title = "参数管理", businessType = BusinessType.UPDATE)
    @PutMapping("/updateByKey")
    public R<Void> updateByKey(@RequestBody SysConfig config) {
        configService.updateConfig(config);
        return R.ok();
    }
    /**
     * åˆ é™¤å‚数配置
     *
     * @param configIds å‚æ•°ID串
     */
    @SaCheckPermission("system:config:remove")
    @Log(title = "参数管理", businessType = BusinessType.DELETE)
    @DeleteMapping("/{configIds}")
    public R<Void> remove(@PathVariable Long[] configIds) {
        configService.deleteConfigByIds(configIds);
        return R.ok();
    }
    /**
     * åˆ·æ–°å‚数缓存
     */
    @SaCheckPermission("system:config:remove")
    @Log(title = "参数管理", businessType = BusinessType.CLEAN)
    @DeleteMapping("/refreshCache")
    public R<Void> refreshCache() {
        configService.resetConfigCache();
        return R.ok();
    }
}
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/controller/system/SysDeptController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,119 @@
package com.ruoyi.system.controller.system;
import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.hutool.core.util.ArrayUtil;
import com.ruoyi.common.log.annotation.Log;
import com.ruoyi.common.core.constant.UserConstants;
import com.ruoyi.common.core.web.controller.BaseController;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.system.domain.SysDept;
import com.ruoyi.common.log.enums.BusinessType;
import com.ruoyi.common.core.utils.StringUtils;
import com.ruoyi.system.service.ISysDeptService;
import lombok.RequiredArgsConstructor;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
 * éƒ¨é—¨ä¿¡æ¯
 *
 * @author Lion Li
 */
@Validated
@RequiredArgsConstructor
@RestController
@RequestMapping("/system/dept")
public class SysDeptController extends BaseController {
    private final ISysDeptService deptService;
    /**
     * èŽ·å–éƒ¨é—¨åˆ—è¡¨
     */
    @SaCheckPermission("system:dept:list")
    @GetMapping("/list")
    public R<List<SysDept>> list(SysDept dept) {
        List<SysDept> depts = deptService.selectDeptList(dept);
        return R.ok(depts);
    }
    /**
     * æŸ¥è¯¢éƒ¨é—¨åˆ—表(排除节点)
     *
     * @param deptId éƒ¨é—¨ID
     */
    @SaCheckPermission("system:dept:list")
    @GetMapping("/list/exclude/{deptId}")
    public R<List<SysDept>> excludeChild(@PathVariable(value = "deptId", required = false) Long deptId) {
        List<SysDept> depts = deptService.selectDeptList(new SysDept());
        depts.removeIf(d -> d.getDeptId().equals(deptId)
            || ArrayUtil.contains(StringUtils.split(d.getAncestors(), ","), deptId + ""));
        return R.ok(depts);
    }
    /**
     * æ ¹æ®éƒ¨é—¨ç¼–号获取详细信息
     *
     * @param deptId éƒ¨é—¨ID
     */
    @SaCheckPermission("system:dept:query")
    @GetMapping(value = "/{deptId}")
    public R<SysDept> getInfo(@PathVariable Long deptId) {
        deptService.checkDeptDataScope(deptId);
        return R.ok(deptService.selectDeptById(deptId));
    }
    /**
     * æ–°å¢žéƒ¨é—¨
     */
    @SaCheckPermission("system:dept:add")
    @Log(title = "部门管理", businessType = BusinessType.INSERT)
    @PostMapping
    public R<Void> add(@Validated @RequestBody SysDept dept) {
        if (UserConstants.NOT_UNIQUE.equals(deptService.checkDeptNameUnique(dept))) {
            return R.fail("新增部门'" + dept.getDeptName() + "'失败,部门名称已存在");
        }
        return toAjax(deptService.insertDept(dept));
    }
    /**
     * ä¿®æ”¹éƒ¨é—¨
     */
    @SaCheckPermission("system:dept:edit")
    @Log(title = "部门管理", businessType = BusinessType.UPDATE)
    @PutMapping
    public R<Void> edit(@Validated @RequestBody SysDept dept) {
        Long deptId = dept.getDeptId();
        deptService.checkDeptDataScope(deptId);
        if (UserConstants.NOT_UNIQUE.equals(deptService.checkDeptNameUnique(dept))) {
            return R.fail("修改部门'" + dept.getDeptName() + "'失败,部门名称已存在");
        } else if (dept.getParentId().equals(deptId)) {
            return R.fail("修改部门'" + dept.getDeptName() + "'失败,上级部门不能是自己");
        } else if (StringUtils.equals(UserConstants.DEPT_DISABLE, dept.getStatus())
            && deptService.selectNormalChildrenDeptById(deptId) > 0) {
            return R.fail("该部门包含未停用的子部门!");
        }
        return toAjax(deptService.updateDept(dept));
    }
    /**
     * åˆ é™¤éƒ¨é—¨
     *
     * @param deptId éƒ¨é—¨ID
     */
    @SaCheckPermission("system:dept:remove")
    @Log(title = "部门管理", businessType = BusinessType.DELETE)
    @DeleteMapping("/{deptId}")
    public R<Void> remove(@PathVariable Long deptId) {
        if (deptService.hasChildByDeptId(deptId)) {
            return R.warn("存在下级部门,不允许删除");
        }
        if (deptService.checkDeptExistUser(deptId)) {
            return R.warn("部门存在用户,不允许删除");
        }
        deptService.checkDeptDataScope(deptId);
        return toAjax(deptService.deleteDeptById(deptId));
    }
}
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/controller/system/SysDictDataController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,116 @@
package com.ruoyi.system.controller.system;
import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.hutool.core.util.ObjectUtil;
import com.ruoyi.common.log.annotation.Log;
import com.ruoyi.common.core.web.controller.BaseController;
import com.ruoyi.common.mybatis.core.page.PageQuery;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.system.domain.SysDictData;
import com.ruoyi.common.mybatis.core.page.TableDataInfo;
import com.ruoyi.common.log.enums.BusinessType;
import com.ruoyi.common.excel.utils.ExcelUtil;
import com.ruoyi.system.service.ISysDictDataService;
import com.ruoyi.system.service.ISysDictTypeService;
import lombok.RequiredArgsConstructor;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import jakarta.servlet.http.HttpServletResponse;
import java.util.ArrayList;
import java.util.List;
/**
 * æ•°æ®å­—典信息
 *
 * @author Lion Li
 */
@Validated
@RequiredArgsConstructor
@RestController
@RequestMapping("/system/dict/data")
public class SysDictDataController extends BaseController {
    private final ISysDictDataService dictDataService;
    private final ISysDictTypeService dictTypeService;
    /**
     * æŸ¥è¯¢å­—典数据列表
     */
    @SaCheckPermission("system:dict:list")
    @GetMapping("/list")
    public TableDataInfo<SysDictData> list(SysDictData dictData, PageQuery pageQuery) {
        return dictDataService.selectPageDictDataList(dictData, pageQuery);
    }
    /**
     * å¯¼å‡ºå­—典数据列表
     */
    @Log(title = "字典数据", businessType = BusinessType.EXPORT)
    @SaCheckPermission("system:dict:export")
    @PostMapping("/export")
    public void export(SysDictData dictData, HttpServletResponse response) {
        List<SysDictData> list = dictDataService.selectDictDataList(dictData);
        ExcelUtil.exportExcel(list, "字典数据", SysDictData.class, response);
    }
    /**
     * æŸ¥è¯¢å­—典数据详细
     *
     * @param dictCode å­—å…¸code
     */
    @SaCheckPermission("system:dict:query")
    @GetMapping(value = "/{dictCode}")
    public R<SysDictData> getInfo(@PathVariable Long dictCode) {
        return R.ok(dictDataService.selectDictDataById(dictCode));
    }
    /**
     * æ ¹æ®å­—典类型查询字典数据信息
     *
     * @param dictType å­—典类型
     */
    @GetMapping(value = "/type/{dictType}")
    public R<List<SysDictData>> dictType(@PathVariable String dictType) {
        List<SysDictData> data = dictTypeService.selectDictDataByType(dictType);
        if (ObjectUtil.isNull(data)) {
            data = new ArrayList<>();
        }
        return R.ok(data);
    }
    /**
     * æ–°å¢žå­—典类型
     */
    @SaCheckPermission("system:dict:add")
    @Log(title = "字典数据", businessType = BusinessType.INSERT)
    @PostMapping
    public R<Void> add(@Validated @RequestBody SysDictData dict) {
        dictDataService.insertDictData(dict);
        return R.ok();
    }
    /**
     * ä¿®æ”¹ä¿å­˜å­—典类型
     */
    @SaCheckPermission("system:dict:edit")
    @Log(title = "字典数据", businessType = BusinessType.UPDATE)
    @PutMapping
    public R<Void> edit(@Validated @RequestBody SysDictData dict) {
        dictDataService.updateDictData(dict);
        return R.ok();
    }
    /**
     * åˆ é™¤å­—典类型
     *
     * @param dictCodes å­—å…¸code串
     */
    @SaCheckPermission("system:dict:remove")
    @Log(title = "字典类型", businessType = BusinessType.DELETE)
    @DeleteMapping("/{dictCodes}")
    public R<Void> remove(@PathVariable Long[] dictCodes) {
        dictDataService.deleteDictDataByIds(dictCodes);
        return R.ok();
    }
}
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/controller/system/SysDictTypeController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,125 @@
package com.ruoyi.system.controller.system;
import cn.dev33.satoken.annotation.SaCheckPermission;
import com.ruoyi.common.log.annotation.Log;
import com.ruoyi.common.core.constant.UserConstants;
import com.ruoyi.common.core.web.controller.BaseController;
import com.ruoyi.common.mybatis.core.page.PageQuery;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.system.domain.SysDictType;
import com.ruoyi.common.mybatis.core.page.TableDataInfo;
import com.ruoyi.common.log.enums.BusinessType;
import com.ruoyi.common.excel.utils.ExcelUtil;
import com.ruoyi.system.service.ISysDictTypeService;
import lombok.RequiredArgsConstructor;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import jakarta.servlet.http.HttpServletResponse;
import java.util.List;
/**
 * æ•°æ®å­—典信息
 *
 * @author Lion Li
 */
@Validated
@RequiredArgsConstructor
@RestController
@RequestMapping("/system/dict/type")
public class SysDictTypeController extends BaseController {
    private final ISysDictTypeService dictTypeService;
    /**
     * æŸ¥è¯¢å­—典类型列表
     */
    @SaCheckPermission("system:dict:list")
    @GetMapping("/list")
    public TableDataInfo<SysDictType> list(SysDictType dictType, PageQuery pageQuery) {
        return dictTypeService.selectPageDictTypeList(dictType, pageQuery);
    }
    /**
     * å¯¼å‡ºå­—典类型列表
     */
    @Log(title = "字典类型", businessType = BusinessType.EXPORT)
    @SaCheckPermission("system:dict:export")
    @PostMapping("/export")
    public void export(SysDictType dictType, HttpServletResponse response) {
        List<SysDictType> list = dictTypeService.selectDictTypeList(dictType);
        ExcelUtil.exportExcel(list, "字典类型", SysDictType.class, response);
    }
    /**
     * æŸ¥è¯¢å­—典类型详细
     *
     * @param dictId å­—å…¸ID
     */
    @SaCheckPermission("system:dict:query")
    @GetMapping(value = "/{dictId}")
    public R<SysDictType> getInfo(@PathVariable Long dictId) {
        return R.ok(dictTypeService.selectDictTypeById(dictId));
    }
    /**
     * æ–°å¢žå­—典类型
     */
    @SaCheckPermission("system:dict:add")
    @Log(title = "字典类型", businessType = BusinessType.INSERT)
    @PostMapping
    public R<Void> add(@Validated @RequestBody SysDictType dict) {
        if (UserConstants.NOT_UNIQUE.equals(dictTypeService.checkDictTypeUnique(dict))) {
            return R.fail("新增字典'" + dict.getDictName() + "'失败,字典类型已存在");
        }
        dictTypeService.insertDictType(dict);
        return R.ok();
    }
    /**
     * ä¿®æ”¹å­—典类型
     */
    @SaCheckPermission("system:dict:edit")
    @Log(title = "字典类型", businessType = BusinessType.UPDATE)
    @PutMapping
    public R<Void> edit(@Validated @RequestBody SysDictType dict) {
        if (UserConstants.NOT_UNIQUE.equals(dictTypeService.checkDictTypeUnique(dict))) {
            return R.fail("修改字典'" + dict.getDictName() + "'失败,字典类型已存在");
        }
        dictTypeService.updateDictType(dict);
        return R.ok();
    }
    /**
     * åˆ é™¤å­—典类型
     *
     * @param dictIds å­—å…¸ID串
     */
    @SaCheckPermission("system:dict:remove")
    @Log(title = "字典类型", businessType = BusinessType.DELETE)
    @DeleteMapping("/{dictIds}")
    public R<Void> remove(@PathVariable Long[] dictIds) {
        dictTypeService.deleteDictTypeByIds(dictIds);
        return R.ok();
    }
    /**
     * åˆ·æ–°å­—典缓存
     */
    @SaCheckPermission("system:dict:remove")
    @Log(title = "字典类型", businessType = BusinessType.CLEAN)
    @DeleteMapping("/refreshCache")
    public R<Void> refreshCache() {
        dictTypeService.resetDictCache();
        return R.ok();
    }
    /**
     * èŽ·å–å­—å…¸é€‰æ‹©æ¡†åˆ—è¡¨
     */
    @GetMapping("/optionselect")
    public R<List<SysDictType>> optionselect() {
        List<SysDictType> dictTypes = dictTypeService.selectDictTypeAll();
        return R.ok(dictTypes);
    }
}
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/controller/system/SysMenuController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,128 @@
package com.ruoyi.system.controller.system;
import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.hutool.core.lang.tree.Tree;
import com.ruoyi.common.log.annotation.Log;
import com.ruoyi.common.core.constant.UserConstants;
import com.ruoyi.common.core.web.controller.BaseController;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.satoken.utils.LoginHelper;
import com.ruoyi.system.domain.SysMenu;
import com.ruoyi.common.log.enums.BusinessType;
import com.ruoyi.common.core.utils.StringUtils;
import com.ruoyi.system.service.ISysMenuService;
import lombok.RequiredArgsConstructor;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
 * èœå•信息
 *
 * @author Lion Li
 */
@Validated
@RequiredArgsConstructor
@RestController
@RequestMapping("/system/menu")
public class SysMenuController extends BaseController {
    private final ISysMenuService menuService;
    /**
     * èŽ·å–èœå•åˆ—è¡¨
     */
    @SaCheckPermission("system:menu:list")
    @GetMapping("/list")
    public R<List<SysMenu>> list(SysMenu menu) {
        List<SysMenu> menus = menuService.selectMenuList(menu, LoginHelper.getUserId());
        return R.ok(menus);
    }
    /**
     * æ ¹æ®èœå•编号获取详细信息
     *
     * @param menuId èœå•ID
     */
    @SaCheckPermission("system:menu:query")
    @GetMapping(value = "/{menuId}")
    public R<SysMenu> getInfo(@PathVariable Long menuId) {
        return R.ok(menuService.selectMenuById(menuId));
    }
    /**
     * èŽ·å–èœå•ä¸‹æ‹‰æ ‘åˆ—è¡¨
     */
    @GetMapping("/treeselect")
    public R<List<Tree<Long>>> treeselect(SysMenu menu) {
        List<SysMenu> menus = menuService.selectMenuList(menu, LoginHelper.getUserId());
        return R.ok(menuService.buildMenuTreeSelect(menus));
    }
    /**
     * åŠ è½½å¯¹åº”è§’è‰²èœå•åˆ—è¡¨æ ‘
     *
     * @param roleId è§’色ID
     */
    @GetMapping(value = "/roleMenuTreeselect/{roleId}")
    public R<Map<String, Object>> roleMenuTreeselect(@PathVariable("roleId") Long roleId) {
        List<SysMenu> menus = menuService.selectMenuList(LoginHelper.getUserId());
        Map<String, Object> ajax = new HashMap<>();
        ajax.put("checkedKeys", menuService.selectMenuListByRoleId(roleId));
        ajax.put("menus", menuService.buildMenuTreeSelect(menus));
        return R.ok(ajax);
    }
    /**
     * æ–°å¢žèœå•
     */
    @SaCheckPermission("system:menu:add")
    @Log(title = "菜单管理", businessType = BusinessType.INSERT)
    @PostMapping
    public R<Void> add(@Validated @RequestBody SysMenu menu) {
        if (UserConstants.NOT_UNIQUE.equals(menuService.checkMenuNameUnique(menu))) {
            return R.fail("新增菜单'" + menu.getMenuName() + "'失败,菜单名称已存在");
        } else if (UserConstants.YES_FRAME.equals(menu.getIsFrame()) && !StringUtils.ishttp(menu.getPath())) {
            return R.fail("新增菜单'" + menu.getMenuName() + "'失败,地址必须以http(s)://开头");
        }
        return toAjax(menuService.insertMenu(menu));
    }
    /**
     * ä¿®æ”¹èœå•
     */
    @SaCheckPermission("system:menu:edit")
    @Log(title = "菜单管理", businessType = BusinessType.UPDATE)
    @PutMapping
    public R<Void> edit(@Validated @RequestBody SysMenu menu) {
        if (UserConstants.NOT_UNIQUE.equals(menuService.checkMenuNameUnique(menu))) {
            return R.fail("修改菜单'" + menu.getMenuName() + "'失败,菜单名称已存在");
        } else if (UserConstants.YES_FRAME.equals(menu.getIsFrame()) && !StringUtils.ishttp(menu.getPath())) {
            return R.fail("修改菜单'" + menu.getMenuName() + "'失败,地址必须以http(s)://开头");
        } else if (menu.getMenuId().equals(menu.getParentId())) {
            return R.fail("修改菜单'" + menu.getMenuName() + "'失败,上级菜单不能选择自己");
        }
        return toAjax(menuService.updateMenu(menu));
    }
    /**
     * åˆ é™¤èœå•
     *
     * @param menuId èœå•ID
     */
    @SaCheckPermission("system:menu:remove")
    @Log(title = "菜单管理", businessType = BusinessType.DELETE)
    @DeleteMapping("/{menuId}")
    public R<Void> remove(@PathVariable("menuId") Long menuId) {
        if (menuService.hasChildByMenuId(menuId)) {
            return R.warn("存在子菜单,不允许删除");
        }
        if (menuService.checkMenuExistRole(menuId)) {
            return R.warn("菜单已分配,不允许删除");
        }
        return toAjax(menuService.deleteMenuById(menuId));
    }
}
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/controller/system/SysNoticeController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,80 @@
package com.ruoyi.system.controller.system;
import cn.dev33.satoken.annotation.SaCheckPermission;
import com.ruoyi.common.log.annotation.Log;
import com.ruoyi.common.core.web.controller.BaseController;
import com.ruoyi.common.mybatis.core.page.PageQuery;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.mybatis.core.page.TableDataInfo;
import com.ruoyi.common.log.enums.BusinessType;
import com.ruoyi.system.domain.SysNotice;
import com.ruoyi.system.service.ISysNoticeService;
import lombok.RequiredArgsConstructor;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
/**
 * å…¬å‘Š ä¿¡æ¯æ“ä½œå¤„ç†
 *
 * @author Lion Li
 */
@Validated
@RequiredArgsConstructor
@RestController
@RequestMapping("/system/notice")
public class SysNoticeController extends BaseController {
    private final ISysNoticeService noticeService;
    /**
     * èŽ·å–é€šçŸ¥å…¬å‘Šåˆ—è¡¨
     */
    @SaCheckPermission("system:notice:list")
    @GetMapping("/list")
    public TableDataInfo<SysNotice> list(SysNotice notice, PageQuery pageQuery) {
        return noticeService.selectPageNoticeList(notice, pageQuery);
    }
    /**
     * æ ¹æ®é€šçŸ¥å…¬å‘Šç¼–号获取详细信息
     *
     * @param noticeId å…¬å‘ŠID
     */
    @SaCheckPermission("system:notice:query")
    @GetMapping(value = "/{noticeId}")
    public R<SysNotice> getInfo(@PathVariable Long noticeId) {
        return R.ok(noticeService.selectNoticeById(noticeId));
    }
    /**
     * æ–°å¢žé€šçŸ¥å…¬å‘Š
     */
    @SaCheckPermission("system:notice:add")
    @Log(title = "通知公告", businessType = BusinessType.INSERT)
    @PostMapping
    public R<Void> add(@Validated @RequestBody SysNotice notice) {
        return toAjax(noticeService.insertNotice(notice));
    }
    /**
     * ä¿®æ”¹é€šçŸ¥å…¬å‘Š
     */
    @SaCheckPermission("system:notice:edit")
    @Log(title = "通知公告", businessType = BusinessType.UPDATE)
    @PutMapping
    public R<Void> edit(@Validated @RequestBody SysNotice notice) {
        return toAjax(noticeService.updateNotice(notice));
    }
    /**
     * åˆ é™¤é€šçŸ¥å…¬å‘Š
     *
     * @param noticeIds å…¬å‘ŠID串
     */
    @SaCheckPermission("system:notice:remove")
    @Log(title = "通知公告", businessType = BusinessType.DELETE)
    @DeleteMapping("/{noticeIds}")
    public R<Void> remove(@PathVariable Long[] noticeIds) {
        return toAjax(noticeService.deleteNoticeByIds(noticeIds));
    }
}
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/controller/system/SysOssConfigController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,105 @@
package com.ruoyi.system.controller.system;
import cn.dev33.satoken.annotation.SaCheckPermission;
import com.ruoyi.common.idempotent.annotation.RepeatSubmit;
import com.ruoyi.common.log.annotation.Log;
import com.ruoyi.common.core.web.controller.BaseController;
import com.ruoyi.common.mybatis.core.page.PageQuery;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.mybatis.core.page.TableDataInfo;
import com.ruoyi.common.core.validate.AddGroup;
import com.ruoyi.common.core.validate.EditGroup;
import com.ruoyi.common.core.validate.QueryGroup;
import com.ruoyi.common.log.enums.BusinessType;
import com.ruoyi.system.domain.bo.SysOssConfigBo;
import com.ruoyi.system.domain.vo.SysOssConfigVo;
import com.ruoyi.system.service.ISysOssConfigService;
import lombok.RequiredArgsConstructor;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import java.util.Arrays;
/**
 * å¯¹è±¡å­˜å‚¨é…ç½®
 *
 * @author Lion Li
 * @author å­¤èˆŸçƒŸé›¨
 * @date 2021-08-13
 */
@Validated
@RequiredArgsConstructor
@RestController
@RequestMapping("/system/oss/config")
public class SysOssConfigController extends BaseController {
    private final ISysOssConfigService iSysOssConfigService;
    /**
     * æŸ¥è¯¢å¯¹è±¡å­˜å‚¨é…ç½®åˆ—表
     */
    @SaCheckPermission("system:oss:list")
    @GetMapping("/list")
    public TableDataInfo<SysOssConfigVo> list(@Validated(QueryGroup.class) SysOssConfigBo bo, PageQuery pageQuery) {
        return iSysOssConfigService.queryPageList(bo, pageQuery);
    }
    /**
     * èŽ·å–å¯¹è±¡å­˜å‚¨é…ç½®è¯¦ç»†ä¿¡æ¯
     *
     * @param ossConfigId OSS配置ID
     */
    @SaCheckPermission("system:oss:query")
    @GetMapping("/{ossConfigId}")
    public R<SysOssConfigVo> getInfo(@NotNull(message = "主键不能为空")
                                     @PathVariable Long ossConfigId) {
        return R.ok(iSysOssConfigService.queryById(ossConfigId));
    }
    /**
     * æ–°å¢žå¯¹è±¡å­˜å‚¨é…ç½®
     */
    @SaCheckPermission("system:oss:add")
    @Log(title = "对象存储配置", businessType = BusinessType.INSERT)
    @RepeatSubmit()
    @PostMapping()
    public R<Void> add(@Validated(AddGroup.class) @RequestBody SysOssConfigBo bo) {
        return toAjax(iSysOssConfigService.insertByBo(bo));
    }
    /**
     * ä¿®æ”¹å¯¹è±¡å­˜å‚¨é…ç½®
     */
    @SaCheckPermission("system:oss:edit")
    @Log(title = "对象存储配置", businessType = BusinessType.UPDATE)
    @RepeatSubmit()
    @PutMapping()
    public R<Void> edit(@Validated(EditGroup.class) @RequestBody SysOssConfigBo bo) {
        return toAjax(iSysOssConfigService.updateByBo(bo));
    }
    /**
     * åˆ é™¤å¯¹è±¡å­˜å‚¨é…ç½®
     *
     * @param ossConfigIds OSS配置ID串
     */
    @SaCheckPermission("system:oss:remove")
    @Log(title = "对象存储配置", businessType = BusinessType.DELETE)
    @DeleteMapping("/{ossConfigIds}")
    public R<Void> remove(@NotEmpty(message = "主键不能为空")
                          @PathVariable Long[] ossConfigIds) {
        return toAjax(iSysOssConfigService.deleteWithValidByIds(Arrays.asList(ossConfigIds), true));
    }
    /**
     * çŠ¶æ€ä¿®æ”¹
     */
    @SaCheckPermission("system:oss:edit")
    @Log(title = "对象存储状态修改", businessType = BusinessType.UPDATE)
    @PutMapping("/changeStatus")
    public R<Void> changeStatus(@RequestBody SysOssConfigBo bo) {
        return toAjax(iSysOssConfigService.updateOssConfigStatus(bo));
    }
}
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/controller/system/SysOssController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,114 @@
package com.ruoyi.system.controller.system;
import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.http.HttpException;
import cn.hutool.http.HttpUtil;
import com.ruoyi.common.core.exception.ServiceException;
import com.ruoyi.common.log.annotation.Log;
import com.ruoyi.common.core.web.controller.BaseController;
import com.ruoyi.common.mybatis.core.page.PageQuery;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.mybatis.core.page.TableDataInfo;
import com.ruoyi.common.core.validate.QueryGroup;
import com.ruoyi.common.log.enums.BusinessType;
import com.ruoyi.common.core.utils.file.FileUtils;
import com.ruoyi.system.domain.bo.SysOssBo;
import com.ruoyi.system.domain.vo.SysOssVo;
import com.ruoyi.system.service.ISysOssService;
import lombok.RequiredArgsConstructor;
import org.springframework.http.MediaType;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.constraints.NotEmpty;
import java.io.IOException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
 * æ–‡ä»¶ä¸Šä¼  æŽ§åˆ¶å±‚
 *
 * @author Lion Li
 */
@Validated
@RequiredArgsConstructor
@RestController
@RequestMapping("/system/oss")
public class SysOssController extends BaseController {
    private final ISysOssService iSysOssService;
    /**
     * æŸ¥è¯¢OSS对象存储列表
     */
    @SaCheckPermission("system:oss:list")
    @GetMapping("/list")
    public TableDataInfo<SysOssVo> list(@Validated(QueryGroup.class) SysOssBo bo, PageQuery pageQuery) {
        return iSysOssService.queryPageList(bo, pageQuery);
    }
    /**
     * æŸ¥è¯¢OSS对象基于id串
     *
     * @param ossIds OSS对象ID串
     */
    @SaCheckPermission("system:oss:list")
    @GetMapping("/listByIds/{ossIds}")
    public R<List<SysOssVo>> listByIds(@NotEmpty(message = "主键不能为空")
                                       @PathVariable Long[] ossIds) {
        List<SysOssVo> list = iSysOssService.listByIds(Arrays.asList(ossIds));
        return R.ok(list);
    }
    /**
     * ä¸Šä¼ OSS对象存储
     *
     * @param file æ–‡ä»¶
     */
    @SaCheckPermission("system:oss:upload")
    @Log(title = "OSS对象存储", businessType = BusinessType.INSERT)
    @PostMapping(value = "/upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
    public R<Map<String, String>> upload(@RequestPart("file") MultipartFile file) {
        if (ObjectUtil.isNull(file)) {
            throw new ServiceException("上传文件不能为空");
        }
        SysOssVo oss = iSysOssService.upload(file);
        Map<String, String> map = new HashMap<>(2);
        map.put("url", oss.getUrl());
        map.put("fileName", oss.getOriginalName());
        map.put("ossId", oss.getOssId().toString());
        return R.ok(map);
    }
    /**
     * ä¸‹è½½OSS对象
     *
     * @param ossId OSS对象ID
     */
    @SaCheckPermission("system:oss:download")
    @GetMapping("/download/{ossId}")
    public void download(@PathVariable Long ossId, HttpServletResponse response) throws IOException {
        iSysOssService.download(ossId,response);
    }
    /**
     * åˆ é™¤OSS对象存储
     *
     * @param ossIds OSS对象ID串
     */
    @SaCheckPermission("system:oss:remove")
    @Log(title = "OSS对象存储", businessType = BusinessType.DELETE)
    @DeleteMapping("/{ossIds}")
    public R<Void> remove(@NotEmpty(message = "主键不能为空")
                          @PathVariable Long[] ossIds) {
        return toAjax(iSysOssService.deleteWithValidByIds(Arrays.asList(ossIds), true));
    }
}
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/controller/system/SysPostController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,115 @@
package com.ruoyi.system.controller.system;
import cn.dev33.satoken.annotation.SaCheckPermission;
import com.ruoyi.common.log.annotation.Log;
import com.ruoyi.common.core.constant.UserConstants;
import com.ruoyi.common.core.web.controller.BaseController;
import com.ruoyi.common.mybatis.core.page.PageQuery;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.mybatis.core.page.TableDataInfo;
import com.ruoyi.common.log.enums.BusinessType;
import com.ruoyi.common.excel.utils.ExcelUtil;
import com.ruoyi.system.domain.SysPost;
import com.ruoyi.system.service.ISysPostService;
import lombok.RequiredArgsConstructor;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import jakarta.servlet.http.HttpServletResponse;
import java.util.List;
/**
 * å²—位信息操作处理
 *
 * @author Lion Li
 */
@Validated
@RequiredArgsConstructor
@RestController
@RequestMapping("/system/post")
public class SysPostController extends BaseController {
    private final ISysPostService postService;
    /**
     * èŽ·å–å²—ä½åˆ—è¡¨
     */
    @SaCheckPermission("system:post:list")
    @GetMapping("/list")
    public TableDataInfo<SysPost> list(SysPost post, PageQuery pageQuery) {
        return postService.selectPagePostList(post, pageQuery);
    }
    /**
     * å¯¼å‡ºå²—位列表
     */
    @Log(title = "岗位管理", businessType = BusinessType.EXPORT)
    @SaCheckPermission("system:post:export")
    @PostMapping("/export")
    public void export(SysPost post, HttpServletResponse response) {
        List<SysPost> list = postService.selectPostList(post);
        ExcelUtil.exportExcel(list, "岗位数据", SysPost.class, response);
    }
    /**
     * æ ¹æ®å²—位编号获取详细信息
     *
     * @param postId å²—位ID
     */
    @SaCheckPermission("system:post:query")
    @GetMapping(value = "/{postId}")
    public R<SysPost> getInfo(@PathVariable Long postId) {
        return R.ok(postService.selectPostById(postId));
    }
    /**
     * æ–°å¢žå²—位
     */
    @SaCheckPermission("system:post:add")
    @Log(title = "岗位管理", businessType = BusinessType.INSERT)
    @PostMapping
    public R<Void> add(@Validated @RequestBody SysPost post) {
        if (UserConstants.NOT_UNIQUE.equals(postService.checkPostNameUnique(post))) {
            return R.fail("新增岗位'" + post.getPostName() + "'失败,岗位名称已存在");
        } else if (UserConstants.NOT_UNIQUE.equals(postService.checkPostCodeUnique(post))) {
            return R.fail("新增岗位'" + post.getPostName() + "'失败,岗位编码已存在");
        }
        return toAjax(postService.insertPost(post));
    }
    /**
     * ä¿®æ”¹å²—位
     */
    @SaCheckPermission("system:post:edit")
    @Log(title = "岗位管理", businessType = BusinessType.UPDATE)
    @PutMapping
    public R<Void> edit(@Validated @RequestBody SysPost post) {
        if (UserConstants.NOT_UNIQUE.equals(postService.checkPostNameUnique(post))) {
            return R.fail("修改岗位'" + post.getPostName() + "'失败,岗位名称已存在");
        } else if (UserConstants.NOT_UNIQUE.equals(postService.checkPostCodeUnique(post))) {
            return R.fail("修改岗位'" + post.getPostName() + "'失败,岗位编码已存在");
        }
        return toAjax(postService.updatePost(post));
    }
    /**
     * åˆ é™¤å²—位
     *
     * @param postIds å²—位ID串
     */
    @SaCheckPermission("system:post:remove")
    @Log(title = "岗位管理", businessType = BusinessType.DELETE)
    @DeleteMapping("/{postIds}")
    public R<Void> remove(@PathVariable Long[] postIds) {
        return toAjax(postService.deletePostByIds(postIds));
    }
    /**
     * èŽ·å–å²—ä½é€‰æ‹©æ¡†åˆ—è¡¨
     */
    @GetMapping("/optionselect")
    public R<List<SysPost>> optionselect() {
        List<SysPost> posts = postService.selectPostAll();
        return R.ok(posts);
    }
}
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/controller/system/SysProfileController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,127 @@
package com.ruoyi.system.controller.system;
import cn.dev33.satoken.secure.BCrypt;
import cn.hutool.core.io.FileUtil;
import com.ruoyi.common.log.annotation.Log;
import com.ruoyi.common.core.constant.UserConstants;
import com.ruoyi.common.core.web.controller.BaseController;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.satoken.utils.LoginHelper;
import com.ruoyi.system.domain.SysUser;
import com.ruoyi.common.log.enums.BusinessType;
import com.ruoyi.common.core.utils.StringUtils;
import com.ruoyi.common.core.utils.file.MimeTypeUtils;
import com.ruoyi.system.domain.vo.SysOssVo;
import com.ruoyi.system.service.ISysOssService;
import com.ruoyi.system.service.ISysUserService;
import lombok.RequiredArgsConstructor;
import org.springframework.http.MediaType;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
/**
 * ä¸ªäººä¿¡æ¯ ä¸šåŠ¡å¤„ç†
 *
 * @author Lion Li
 */
@Validated
@RequiredArgsConstructor
@RestController
@RequestMapping("/system/user/profile")
public class SysProfileController extends BaseController {
    private final ISysUserService userService;
    private final ISysOssService iSysOssService;
    /**
     * ä¸ªäººä¿¡æ¯
     */
    @GetMapping
    public R<Map<String, Object>> profile() {
        SysUser user = userService.selectUserById(LoginHelper.getUserId());
        Map<String, Object> ajax = new HashMap<>();
        ajax.put("user", user);
        ajax.put("roleGroup", userService.selectUserRoleGroup(user.getUserName()));
        ajax.put("postGroup", userService.selectUserPostGroup(user.getUserName()));
        return R.ok(ajax);
    }
    /**
     * ä¿®æ”¹ç”¨æˆ·
     */
    @Log(title = "个人信息", businessType = BusinessType.UPDATE)
    @PutMapping
    public R<Void> updateProfile(@RequestBody SysUser user) {
        if (StringUtils.isNotEmpty(user.getPhonenumber())
            && UserConstants.NOT_UNIQUE.equals(userService.checkPhoneUnique(user))) {
            return R.fail("修改用户'" + user.getUserName() + "'失败,手机号码已存在");
        }
        if (StringUtils.isNotEmpty(user.getEmail())
            && UserConstants.NOT_UNIQUE.equals(userService.checkEmailUnique(user))) {
            return R.fail("修改用户'" + user.getUserName() + "'失败,邮箱账号已存在");
        }
        user.setUserId(LoginHelper.getUserId());
        user.setUserName(null);
        user.setPassword(null);
        user.setAvatar(null);
        user.setDeptId(null);
        if (userService.updateUserProfile(user) > 0) {
            return R.ok();
        }
        return R.fail("修改个人信息异常,请联系管理员");
    }
    /**
     * é‡ç½®å¯†ç 
     *
     * @param newPassword æ—§å¯†ç 
     * @param oldPassword æ–°å¯†ç 
     */
    @Log(title = "个人信息", businessType = BusinessType.UPDATE)
    @PutMapping("/updatePwd")
    public R<Void> updatePwd(String oldPassword, String newPassword) {
        SysUser user = userService.selectUserById(LoginHelper.getUserId());
        String userName = user.getUserName();
        String password = user.getPassword();
        if (!BCrypt.checkpw(oldPassword, password)) {
            return R.fail("修改密码失败,旧密码错误");
        }
        if (BCrypt.checkpw(newPassword, password)) {
            return R.fail("新密码不能与旧密码相同");
        }
        if (userService.resetUserPwd(userName, BCrypt.hashpw(newPassword)) > 0) {
            return R.ok();
        }
        return R.fail("修改密码异常,请联系管理员");
    }
    /**
     * å¤´åƒä¸Šä¼ 
     *
     * @param avatarfile ç”¨æˆ·å¤´åƒ
     */
    @Log(title = "用户头像", businessType = BusinessType.UPDATE)
    @PostMapping(value = "/avatar", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
    public R<Map<String, Object>> avatar(@RequestPart("avatarfile") MultipartFile avatarfile) {
        Map<String, Object> ajax = new HashMap<>();
        if (!avatarfile.isEmpty()) {
            String extension = FileUtil.extName(avatarfile.getOriginalFilename());
            if (!StringUtils.equalsAnyIgnoreCase(extension, MimeTypeUtils.IMAGE_EXTENSION)) {
                return R.fail("文件格式不正确,请上传" + Arrays.toString(MimeTypeUtils.IMAGE_EXTENSION) + "格式");
            }
            SysOssVo oss = iSysOssService.upload(avatarfile);
            String avatar = oss.getUrl();
            if (userService.updateUserAvatar(LoginHelper.getUsername(), avatar)) {
                ajax.put("imgUrl", avatar);
                return R.ok(ajax);
            }
        }
        return R.fail("上传图片异常,请联系管理员");
    }
}
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/controller/system/SysRoleController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,237 @@
package com.ruoyi.system.controller.system;
import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.hutool.core.util.ObjectUtil;
import com.ruoyi.common.log.annotation.Log;
import com.ruoyi.common.core.constant.UserConstants;
import com.ruoyi.common.core.web.controller.BaseController;
import com.ruoyi.common.mybatis.core.page.PageQuery;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.satoken.utils.LoginHelper;
import com.ruoyi.system.domain.SysDept;
import com.ruoyi.system.domain.SysRole;
import com.ruoyi.system.domain.SysUser;
import com.ruoyi.common.core.domain.model.LoginUser;
import com.ruoyi.common.mybatis.core.page.TableDataInfo;
import com.ruoyi.common.log.enums.BusinessType;
import com.ruoyi.common.excel.utils.ExcelUtil;
import com.ruoyi.system.domain.SysUserRole;
import com.ruoyi.system.service.ISysDeptService;
import com.ruoyi.system.service.ISysRoleService;
import com.ruoyi.system.service.ISysUserService;
import com.ruoyi.system.service.SysPermissionService;
import lombok.RequiredArgsConstructor;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import jakarta.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
 * è§’色信息
 *
 * @author Lion Li
 */
@Validated
@RequiredArgsConstructor
@RestController
@RequestMapping("/system/role")
public class SysRoleController extends BaseController {
    private final ISysRoleService roleService;
    private final ISysUserService userService;
    private final ISysDeptService deptService;
    private final SysPermissionService permissionService;
    /**
     * èŽ·å–è§’è‰²ä¿¡æ¯åˆ—è¡¨
     */
    @SaCheckPermission("system:role:list")
    @GetMapping("/list")
    public TableDataInfo<SysRole> list(SysRole role, PageQuery pageQuery) {
        return roleService.selectPageRoleList(role, pageQuery);
    }
    /**
     * å¯¼å‡ºè§’色信息列表
     */
    @Log(title = "角色管理", businessType = BusinessType.EXPORT)
    @SaCheckPermission("system:role:export")
    @PostMapping("/export")
    public void export(SysRole role, HttpServletResponse response) {
        List<SysRole> list = roleService.selectRoleList(role);
        ExcelUtil.exportExcel(list, "角色数据", SysRole.class, response);
    }
    /**
     * æ ¹æ®è§’色编号获取详细信息
     *
     * @param roleId è§’色ID
     */
    @SaCheckPermission("system:role:query")
    @GetMapping(value = "/{roleId}")
    public R<SysRole> getInfo(@PathVariable Long roleId) {
        roleService.checkRoleDataScope(roleId);
        return R.ok(roleService.selectRoleById(roleId));
    }
    /**
     * æ–°å¢žè§’色
     */
    @SaCheckPermission("system:role:add")
    @Log(title = "角色管理", businessType = BusinessType.INSERT)
    @PostMapping
    public R<Void> add(@Validated @RequestBody SysRole role) {
        if (UserConstants.NOT_UNIQUE.equals(roleService.checkRoleNameUnique(role))) {
            return R.fail("新增角色'" + role.getRoleName() + "'失败,角色名称已存在");
        } else if (UserConstants.NOT_UNIQUE.equals(roleService.checkRoleKeyUnique(role))) {
            return R.fail("新增角色'" + role.getRoleName() + "'失败,角色权限已存在");
        }
        return toAjax(roleService.insertRole(role));
    }
    /**
     * ä¿®æ”¹ä¿å­˜è§’色
     */
    @SaCheckPermission("system:role:edit")
    @Log(title = "角色管理", businessType = BusinessType.UPDATE)
    @PutMapping
    public R<Void> edit(@Validated @RequestBody SysRole role) {
        roleService.checkRoleAllowed(role);
        roleService.checkRoleDataScope(role.getRoleId());
        if (UserConstants.NOT_UNIQUE.equals(roleService.checkRoleNameUnique(role))) {
            return R.fail("修改角色'" + role.getRoleName() + "'失败,角色名称已存在");
        } else if (UserConstants.NOT_UNIQUE.equals(roleService.checkRoleKeyUnique(role))) {
            return R.fail("修改角色'" + role.getRoleName() + "'失败,角色权限已存在");
        }
        if (roleService.updateRole(role) > 0) {
            // æ›´æ–°ç¼“存用户权限
            LoginUser loginUser = LoginHelper.getLoginUser();
            SysUser sysUser = userService.selectUserById(loginUser.getUserId());
            if (ObjectUtil.isNotNull(sysUser) && !sysUser.isAdmin()) {
                loginUser.setMenuPermission(permissionService.getMenuPermission(sysUser));
                LoginHelper.setLoginUser(loginUser);
            }
            return R.ok();
        }
        return R.fail("修改角色'" + role.getRoleName() + "'失败,请联系管理员");
    }
    /**
     * ä¿®æ”¹ä¿å­˜æ•°æ®æƒé™
     */
    @SaCheckPermission("system:role:edit")
    @Log(title = "角色管理", businessType = BusinessType.UPDATE)
    @PutMapping("/dataScope")
    public R<Void> dataScope(@RequestBody SysRole role) {
        roleService.checkRoleAllowed(role);
        roleService.checkRoleDataScope(role.getRoleId());
        return toAjax(roleService.authDataScope(role));
    }
    /**
     * çŠ¶æ€ä¿®æ”¹
     */
    @SaCheckPermission("system:role:edit")
    @Log(title = "角色管理", businessType = BusinessType.UPDATE)
    @PutMapping("/changeStatus")
    public R<Void> changeStatus(@RequestBody SysRole role) {
        roleService.checkRoleAllowed(role);
        roleService.checkRoleDataScope(role.getRoleId());
        return toAjax(roleService.updateRoleStatus(role));
    }
    /**
     * åˆ é™¤è§’色
     *
     * @param roleIds è§’色ID串
     */
    @SaCheckPermission("system:role:remove")
    @Log(title = "角色管理", businessType = BusinessType.DELETE)
    @DeleteMapping("/{roleIds}")
    public R<Void> remove(@PathVariable Long[] roleIds) {
        return toAjax(roleService.deleteRoleByIds(roleIds));
    }
    /**
     * èŽ·å–è§’è‰²é€‰æ‹©æ¡†åˆ—è¡¨
     */
    @SaCheckPermission("system:role:query")
    @GetMapping("/optionselect")
    public R<List<SysRole>> optionselect() {
        return R.ok(roleService.selectRoleAll());
    }
    /**
     * æŸ¥è¯¢å·²åˆ†é…ç”¨æˆ·è§’色列表
     */
    @SaCheckPermission("system:role:list")
    @GetMapping("/authUser/allocatedList")
    public TableDataInfo<SysUser> allocatedList(SysUser user, PageQuery pageQuery) {
        return userService.selectAllocatedList(user, pageQuery);
    }
    /**
     * æŸ¥è¯¢æœªåˆ†é…ç”¨æˆ·è§’色列表
     */
    @SaCheckPermission("system:role:list")
    @GetMapping("/authUser/unallocatedList")
    public TableDataInfo<SysUser> unallocatedList(SysUser user, PageQuery pageQuery) {
        return userService.selectUnallocatedList(user, pageQuery);
    }
    /**
     * å–消授权用户
     */
    @SaCheckPermission("system:role:edit")
    @Log(title = "角色管理", businessType = BusinessType.GRANT)
    @PutMapping("/authUser/cancel")
    public R<Void> cancelAuthUser(@RequestBody SysUserRole userRole) {
        return toAjax(roleService.deleteAuthUser(userRole));
    }
    /**
     * æ‰¹é‡å–消授权用户
     *
     * @param roleId  è§’色ID
     * @param userIds ç”¨æˆ·ID串
     */
    @SaCheckPermission("system:role:edit")
    @Log(title = "角色管理", businessType = BusinessType.GRANT)
    @PutMapping("/authUser/cancelAll")
    public R<Void> cancelAuthUserAll(Long roleId, Long[] userIds) {
        return toAjax(roleService.deleteAuthUsers(roleId, userIds));
    }
    /**
     * æ‰¹é‡é€‰æ‹©ç”¨æˆ·æŽˆæƒ
     *
     * @param roleId  è§’色ID
     * @param userIds ç”¨æˆ·ID串
     */
    @SaCheckPermission("system:role:edit")
    @Log(title = "角色管理", businessType = BusinessType.GRANT)
    @PutMapping("/authUser/selectAll")
    public R<Void> selectAuthUserAll(Long roleId, Long[] userIds) {
        roleService.checkRoleDataScope(roleId);
        return toAjax(roleService.insertAuthUsers(roleId, userIds));
    }
    /**
     * èŽ·å–å¯¹åº”è§’è‰²éƒ¨é—¨æ ‘åˆ—è¡¨
     *
     * @param roleId è§’色ID
     */
    @SaCheckPermission("system:role:list")
    @GetMapping(value = "/deptTree/{roleId}")
    public R<Map<String, Object>> roleDeptTreeselect(@PathVariable("roleId") Long roleId) {
        Map<String, Object> ajax = new HashMap<>();
        ajax.put("checkedKeys", deptService.selectDeptListByRoleId(roleId));
        ajax.put("depts", deptService.selectDeptTreeList(new SysDept()));
        return R.ok(ajax);
    }
}
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/controller/system/SysUserController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,253 @@
package com.ruoyi.system.controller.system;
import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.dev33.satoken.secure.BCrypt;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.lang.tree.Tree;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.ObjectUtil;
import com.ruoyi.common.excel.core.ExcelResult;
import com.ruoyi.common.log.annotation.Log;
import com.ruoyi.common.core.constant.UserConstants;
import com.ruoyi.common.core.web.controller.BaseController;
import com.ruoyi.common.mybatis.core.page.PageQuery;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.satoken.utils.LoginHelper;
import com.ruoyi.system.domain.SysDept;
import com.ruoyi.system.domain.SysRole;
import com.ruoyi.system.domain.SysUser;
import com.ruoyi.common.mybatis.core.page.TableDataInfo;
import com.ruoyi.common.log.enums.BusinessType;
import com.ruoyi.common.core.utils.StreamUtils;
import com.ruoyi.common.core.utils.StringUtils;
import com.ruoyi.common.excel.utils.ExcelUtil;
import com.ruoyi.system.domain.vo.SysUserExportVo;
import com.ruoyi.system.domain.vo.SysUserImportVo;
import com.ruoyi.system.listener.SysUserImportListener;
import com.ruoyi.system.service.ISysDeptService;
import com.ruoyi.system.service.ISysPostService;
import com.ruoyi.system.service.ISysRoleService;
import com.ruoyi.system.service.ISysUserService;
import lombok.RequiredArgsConstructor;
import org.springframework.http.MediaType;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import jakarta.servlet.http.HttpServletResponse;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
 * ç”¨æˆ·ä¿¡æ¯
 *
 * @author Lion Li
 */
@Validated
@RequiredArgsConstructor
@RestController
@RequestMapping("/system/user")
public class SysUserController extends BaseController {
    private final ISysUserService userService;
    private final ISysRoleService roleService;
    private final ISysPostService postService;
    private final ISysDeptService deptService;
    /**
     * èŽ·å–ç”¨æˆ·åˆ—è¡¨
     */
    @SaCheckPermission("system:user:list")
    @GetMapping("/list")
    public TableDataInfo<SysUser> list(SysUser user, PageQuery pageQuery) {
        return userService.selectPageUserList(user, pageQuery);
    }
    /**
     * å¯¼å‡ºç”¨æˆ·åˆ—表
     */
    @Log(title = "用户管理", businessType = BusinessType.EXPORT)
    @SaCheckPermission("system:user:export")
    @PostMapping("/export")
    public void export(SysUser user, HttpServletResponse response) {
        List<SysUser> list = userService.selectUserList(user);
        List<SysUserExportVo> listVo = BeanUtil.copyToList(list, SysUserExportVo.class);
        for (int i = 0; i < list.size(); i++) {
            SysDept dept = list.get(i).getDept();
            SysUserExportVo vo = listVo.get(i);
            if (ObjectUtil.isNotEmpty(dept)) {
                vo.setDeptName(dept.getDeptName());
                vo.setLeader(dept.getLeader());
            }
        }
        ExcelUtil.exportExcel(listVo, "用户数据", SysUserExportVo.class, response);
    }
    /**
     * å¯¼å…¥æ•°æ®
     *
     * @param file          å¯¼å…¥æ–‡ä»¶
     * @param updateSupport æ˜¯å¦æ›´æ–°å·²å­˜åœ¨æ•°æ®
     */
    @Log(title = "用户管理", businessType = BusinessType.IMPORT)
    @SaCheckPermission("system:user:import")
    @PostMapping(value = "/importData", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
    public R<Void> importData(@RequestPart("file") MultipartFile file, boolean updateSupport) throws Exception {
        ExcelResult<SysUserImportVo> result = ExcelUtil.importExcel(file.getInputStream(), SysUserImportVo.class, new SysUserImportListener(updateSupport));
        return R.ok(result.getAnalysis());
    }
    /**
     * èŽ·å–å¯¼å…¥æ¨¡æ¿
     */
    @PostMapping("/importTemplate")
    public void importTemplate(HttpServletResponse response) {
        ExcelUtil.exportExcel(new ArrayList<>(), "用户数据", SysUserImportVo.class, response);
    }
    /**
     * æ ¹æ®ç”¨æˆ·ç¼–号获取详细信息
     *
     * @param userId ç”¨æˆ·ID
     */
    @SaCheckPermission("system:user:query")
    @GetMapping(value = {"/", "/{userId}"})
    public R<Map<String, Object>> getInfo(@PathVariable(value = "userId", required = false) Long userId) {
        userService.checkUserDataScope(userId);
        Map<String, Object> ajax = new HashMap<>();
        List<SysRole> roles = roleService.selectRoleAll();
        ajax.put("roles", LoginHelper.isAdmin(userId) ? roles : StreamUtils.filter(roles, r -> !r.isAdmin()));
        ajax.put("posts", postService.selectPostAll());
        if (ObjectUtil.isNotNull(userId)) {
            SysUser sysUser = userService.selectUserById(userId);
            ajax.put("user", sysUser);
            ajax.put("postIds", postService.selectPostListByUserId(userId));
            ajax.put("roleIds", StreamUtils.toList(sysUser.getRoles(), SysRole::getRoleId));
        }
        return R.ok(ajax);
    }
    /**
     * æ–°å¢žç”¨æˆ·
     */
    @SaCheckPermission("system:user:add")
    @Log(title = "用户管理", businessType = BusinessType.INSERT)
    @PostMapping
    public R<Void> add(@Validated @RequestBody SysUser user) {
        if (UserConstants.NOT_UNIQUE.equals(userService.checkUserNameUnique(user))) {
            return R.fail("新增用户'" + user.getUserName() + "'失败,登录账号已存在");
        } else if (StringUtils.isNotEmpty(user.getPhonenumber())
            && UserConstants.NOT_UNIQUE.equals(userService.checkPhoneUnique(user))) {
            return R.fail("新增用户'" + user.getUserName() + "'失败,手机号码已存在");
        } else if (StringUtils.isNotEmpty(user.getEmail())
            && UserConstants.NOT_UNIQUE.equals(userService.checkEmailUnique(user))) {
            return R.fail("新增用户'" + user.getUserName() + "'失败,邮箱账号已存在");
        }
        user.setPassword(BCrypt.hashpw(user.getPassword()));
        return toAjax(userService.insertUser(user));
    }
    /**
     * ä¿®æ”¹ç”¨æˆ·
     */
    @SaCheckPermission("system:user:edit")
    @Log(title = "用户管理", businessType = BusinessType.UPDATE)
    @PutMapping
    public R<Void> edit(@Validated @RequestBody SysUser user) {
        userService.checkUserAllowed(user);
        userService.checkUserDataScope(user.getUserId());
        if (UserConstants.NOT_UNIQUE.equals(userService.checkUserNameUnique(user))) {
            return R.fail("修改用户'" + user.getUserName() + "'失败,登录账号已存在");
        } else if (StringUtils.isNotEmpty(user.getPhonenumber())
            && UserConstants.NOT_UNIQUE.equals(userService.checkPhoneUnique(user))) {
            return R.fail("修改用户'" + user.getUserName() + "'失败,手机号码已存在");
        } else if (StringUtils.isNotEmpty(user.getEmail())
            && UserConstants.NOT_UNIQUE.equals(userService.checkEmailUnique(user))) {
            return R.fail("修改用户'" + user.getUserName() + "'失败,邮箱账号已存在");
        }
        return toAjax(userService.updateUser(user));
    }
    /**
     * åˆ é™¤ç”¨æˆ·
     *
     * @param userIds è§’色ID串
     */
    @SaCheckPermission("system:user:remove")
    @Log(title = "用户管理", businessType = BusinessType.DELETE)
    @DeleteMapping("/{userIds}")
    public R<Void> remove(@PathVariable Long[] userIds) {
        if (ArrayUtil.contains(userIds, LoginHelper.getUserId())) {
            return R.fail("当前用户不能删除");
        }
        return toAjax(userService.deleteUserByIds(userIds));
    }
    /**
     * é‡ç½®å¯†ç 
     */
    @SaCheckPermission("system:user:resetPwd")
    @Log(title = "用户管理", businessType = BusinessType.UPDATE)
    @PutMapping("/resetPwd")
    public R<Void> resetPwd(@RequestBody SysUser user) {
        userService.checkUserAllowed(user);
        userService.checkUserDataScope(user.getUserId());
        user.setPassword(BCrypt.hashpw(user.getPassword()));
        return toAjax(userService.resetPwd(user));
    }
    /**
     * çŠ¶æ€ä¿®æ”¹
     */
    @SaCheckPermission("system:user:edit")
    @Log(title = "用户管理", businessType = BusinessType.UPDATE)
    @PutMapping("/changeStatus")
    public R<Void> changeStatus(@RequestBody SysUser user) {
        userService.checkUserAllowed(user);
        userService.checkUserDataScope(user.getUserId());
        return toAjax(userService.updateUserStatus(user));
    }
    /**
     * æ ¹æ®ç”¨æˆ·ç¼–号获取授权角色
     *
     * @param userId ç”¨æˆ·ID
     */
    @SaCheckPermission("system:user:query")
    @GetMapping("/authRole/{userId}")
    public R<Map<String, Object>> authRole(@PathVariable Long userId) {
        SysUser user = userService.selectUserById(userId);
        List<SysRole> roles = roleService.selectRolesByUserId(userId);
        Map<String, Object> ajax = new HashMap<>();
        ajax.put("user", user);
        ajax.put("roles", LoginHelper.isAdmin(userId) ? roles : StreamUtils.filter(roles, r -> !r.isAdmin()));
        return R.ok(ajax);
    }
    /**
     * ç”¨æˆ·æŽˆæƒè§’色
     *
     * @param userId  ç”¨æˆ·Id
     * @param roleIds è§’色ID串
     */
    @SaCheckPermission("system:user:edit")
    @Log(title = "用户管理", businessType = BusinessType.GRANT)
    @PutMapping("/authRole")
    public R<Void> insertAuthRole(Long userId, Long[] roleIds) {
        userService.checkUserDataScope(userId);
        userService.insertUserAuth(userId, roleIds);
        return R.ok();
    }
    /**
     * èŽ·å–éƒ¨é—¨æ ‘åˆ—è¡¨
     */
    @SaCheckPermission("system:user:list")
    @GetMapping("/deptTree")
    public R<List<Tree<Long>>> deptTree(SysDept dept) {
        return R.ok(deptService.selectDeptTreeList(dept));
    }
}
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysCache.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,47 @@
package com.ruoyi.system.domain;
import com.ruoyi.common.core.utils.StringUtils;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
 * ç¼“存信息
 *
 * @author Lion Li
 */
@Data
@NoArgsConstructor
public class SysCache {
    /**
     * ç¼“存名称
     */
    private String cacheName = "";
    /**
     * ç¼“存键名
     */
    private String cacheKey = "";
    /**
     * ç¼“存内容
     */
    private String cacheValue = "";
    /**
     * å¤‡æ³¨
     */
    private String remark = "";
    public SysCache(String cacheName, String remark) {
        this.cacheName = cacheName;
        this.remark = remark;
    }
    public SysCache(String cacheName, String cacheKey, String cacheValue) {
        this.cacheName = StringUtils.replace(cacheName, ":", "");
        this.cacheKey = StringUtils.replace(cacheKey, cacheName, "");
        this.cacheValue = cacheValue;
    }
}
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysConfig.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,71 @@
package com.ruoyi.system.domain;
import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
import com.alibaba.excel.annotation.ExcelProperty;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.ruoyi.common.core.web.domain.BaseEntity;
import com.ruoyi.common.excel.annotation.ExcelDictFormat;
import com.ruoyi.common.excel.convert.ExcelDictConvert;
import lombok.Data;
import lombok.EqualsAndHashCode;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;
/**
 * å‚数配置表 sys_config
 *
 * @author Lion Li
 */
@Data
@EqualsAndHashCode(callSuper = true)
@TableName("sys_config")
@ExcelIgnoreUnannotated
public class SysConfig extends BaseEntity {
    /**
     * å‚数主键
     */
    @ExcelProperty(value = "参数主键")
    @TableId(value = "config_id")
    private Long configId;
    /**
     * å‚数名称
     */
    @ExcelProperty(value = "参数名称")
    @NotBlank(message = "参数名称不能为空")
    @Size(min = 0, max = 100, message = "参数名称不能超过{max}个字符")
    private String configName;
    /**
     * å‚数键名
     */
    @ExcelProperty(value = "参数键名")
    @NotBlank(message = "参数键名长度不能为空")
    @Size(min = 0, max = 100, message = "参数键名长度不能超过{max}个字符")
    private String configKey;
    /**
     * å‚数键值
     */
    @ExcelProperty(value = "参数键值")
    @NotBlank(message = "参数键值不能为空")
    @Size(min = 0, max = 500, message = "参数键值长度不能超过{max}个字符")
    private String configValue;
    /**
     * ç³»ç»Ÿå†…置(Y是 N否)
     */
    @ExcelProperty(value = "系统内置", converter = ExcelDictConvert.class)
    @ExcelDictFormat(dictType = "sys_yes_no")
    private String configType;
    /**
     * å¤‡æ³¨
     */
    private String remark;
}
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysDept.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,83 @@
package com.ruoyi.system.domain;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableLogic;
import com.baomidou.mybatisplus.annotation.TableName;
import com.ruoyi.common.core.web.domain.TreeEntity;
import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.io.Serial;
/**
 * éƒ¨é—¨è¡¨ sys_dept
 *
 * @author Lion Li
 */
@Data
@EqualsAndHashCode(callSuper = true)
@TableName("sys_dept")
public class SysDept extends TreeEntity<SysDept> {
    @Serial
    private static final long serialVersionUID = 1L;
    /**
     * éƒ¨é—¨ID
     */
    @TableId(value = "dept_id")
    private Long deptId;
    /**
     * éƒ¨é—¨åç§°
     */
    @NotBlank(message = "部门名称不能为空")
    @Size(min = 0, max = 30, message = "部门名称长度不能超过{max}个字符")
    private String deptName;
    /**
     * æ˜¾ç¤ºé¡ºåº
     */
    @NotNull(message = "显示顺序不能为空")
    private Integer orderNum;
    /**
     * è´Ÿè´£äºº
     */
    private String leader;
    /**
     * è”系电话
     */
    @Size(min = 0, max = 11, message = "联系电话长度不能超过{max}个字符")
    private String phone;
    /**
     * é‚®ç®±
     */
    @Email(message = "邮箱格式不正确")
    @Size(min = 0, max = 50, message = "邮箱长度不能超过{max}个字符")
    private String email;
    /**
     * éƒ¨é—¨çŠ¶æ€:0正常,1停用
     */
    private String status;
    /**
     * åˆ é™¤æ ‡å¿—(0代表存在 2代表删除)
     */
    @TableLogic
    private String delFlag;
    /**
     * ç¥–级列表
     */
    private String ancestors;
}
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysDictData.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,100 @@
package com.ruoyi.system.domain;
import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
import com.alibaba.excel.annotation.ExcelProperty;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.ruoyi.common.core.constant.UserConstants;
import com.ruoyi.common.core.web.domain.BaseEntity;
import com.ruoyi.common.excel.annotation.ExcelDictFormat;
import com.ruoyi.common.excel.convert.ExcelDictConvert;
import lombok.Data;
import lombok.EqualsAndHashCode;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;
/**
 * å­—典数据表 sys_dict_data
 *
 * @author Lion Li
 */
@Data
@EqualsAndHashCode(callSuper = true)
@TableName("sys_dict_data")
@ExcelIgnoreUnannotated
public class SysDictData extends BaseEntity {
    /**
     * å­—典编码
     */
    @ExcelProperty(value = "字典编码")
    @TableId(value = "dict_code")
    private Long dictCode;
    /**
     * å­—典排序
     */
    @ExcelProperty(value = "字典排序")
    private Integer dictSort;
    /**
     * å­—典标签
     */
    @ExcelProperty(value = "字典标签")
    @NotBlank(message = "字典标签不能为空")
    @Size(min = 0, max = 100, message = "字典标签长度不能超过{max}个字符")
    private String dictLabel;
    /**
     * å­—典键值
     */
    @ExcelProperty(value = "字典键值")
    @NotBlank(message = "字典键值不能为空")
    @Size(min = 0, max = 100, message = "字典键值长度不能超过{max}个字符")
    private String dictValue;
    /**
     * å­—典类型
     */
    @ExcelProperty(value = "字典类型")
    @NotBlank(message = "字典类型不能为空")
    @Size(min = 0, max = 100, message = "字典类型长度不能超过{max}个字符")
    private String dictType;
    /**
     * æ ·å¼å±žæ€§ï¼ˆå…¶ä»–样式扩展)
     */
    @Size(min = 0, max = 100, message = "样式属性长度不能超过{max}个字符")
    private String cssClass;
    /**
     * è¡¨æ ¼å­—典样式
     */
    private String listClass;
    /**
     * æ˜¯å¦é»˜è®¤ï¼ˆY是 N否)
     */
    @ExcelProperty(value = "是否默认", converter = ExcelDictConvert.class)
    @ExcelDictFormat(dictType = "sys_yes_no")
    private String isDefault;
    /**
     * çŠ¶æ€ï¼ˆ0正常 1停用)
     */
    @ExcelProperty(value = "状态", converter = ExcelDictConvert.class)
    @ExcelDictFormat(dictType = "sys_normal_disable")
    private String status;
    /**
     * å¤‡æ³¨
     */
    private String remark;
    public boolean getDefault() {
        return UserConstants.YES.equals(this.isDefault);
    }
}
在上述文件截断后对比
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysDictType.java ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysLogininfor.java ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysMenu.java ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysNotice.java ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysOperLog.java ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysOss.java ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysOssConfig.java ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysPost.java ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysRole.java ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysRoleDept.java ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysRoleMenu.java ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysUser.java ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysUserOnline.java ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysUserPost.java ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysUserRole.java ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/bo/SysOssBo.java ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/bo/SysOssConfigBo.java ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/MetaVo.java ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/RouterVo.java ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/SysOssConfigVo.java ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/SysOssVo.java ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/SysUserExportVo.java ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/SysUserImportVo.java ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/listener/SysUserImportListener.java ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysConfigMapper.java ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysDeptMapper.java ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysDictDataMapper.java ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysDictTypeMapper.java ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysLogininforMapper.java ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysMenuMapper.java ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysNoticeMapper.java ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysOperLogMapper.java ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysOssConfigMapper.java ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysOssMapper.java ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysPostMapper.java ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysRoleDeptMapper.java ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysRoleMapper.java ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysRoleMenuMapper.java ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysUserMapper.java ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysUserPostMapper.java ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysUserRoleMapper.java ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/runner/SystemApplicationRunner.java ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysConfigService.java ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysDataScopeService.java ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysDeptService.java ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysDictDataService.java ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysDictTypeService.java ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysLogininforService.java ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysMenuService.java ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysNoticeService.java ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysOperLogService.java ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysOssConfigService.java ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysOssService.java ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysPostService.java ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysRoleService.java ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysUserService.java ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/SysLoginService.java ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/SysPermissionService.java ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/SysRegisterService.java ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysConfigServiceImpl.java ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysDataScopeServiceImpl.java ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysDeptServiceImpl.java ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysDictDataServiceImpl.java ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysDictTypeServiceImpl.java ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysLogininforServiceImpl.java ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysMenuServiceImpl.java ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysNoticeServiceImpl.java ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysOperLogServiceImpl.java ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysOssConfigServiceImpl.java ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysOssServiceImpl.java ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysPostServiceImpl.java ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysRoleServiceImpl.java ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysSensitiveServiceImpl.java ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysUserServiceImpl.java ruoyi-modules/ruoyi-system/src/main/resources/mapper/package-info.md ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysConfigMapper.xml ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysDeptMapper.xml ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysDictDataMapper.xml ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysDictTypeMapper.xml ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysLogininforMapper.xml ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysMenuMapper.xml ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysNoticeMapper.xml ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysOperLogMapper.xml ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysOssConfigMapper.xml ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysOssMapper.xml ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysPostMapper.xml ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysRoleDeptMapper.xml ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysRoleMapper.xml ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysRoleMenuMapper.xml ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysUserMapper.xml ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysUserPostMapper.xml ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysUserRoleMapper.xml ruoyi-oss/pom.xml (已删除) ruoyi-oss/src/main/java/com/ruoyi/oss/constant/OssConstant.java (已删除) ruoyi-oss/src/main/java/com/ruoyi/oss/core/OssClient.java (已删除) ruoyi-oss/src/main/java/com/ruoyi/oss/entity/UploadResult.java (已删除) ruoyi-oss/src/main/java/com/ruoyi/oss/enumd/AccessPolicyType.java (已删除) ruoyi-oss/src/main/java/com/ruoyi/oss/enumd/PolicyType.java (已删除) ruoyi-oss/src/main/java/com/ruoyi/oss/exception/OssException.java (已删除) ruoyi-oss/src/main/java/com/ruoyi/oss/factory/OssFactory.java (已删除) ruoyi-oss/src/main/java/com/ruoyi/oss/properties/OssProperties.java (已删除) ruoyi-sms/pom.xml (已删除) ruoyi-sms/src/main/java/com/ruoyi/sms/config/SmsConfig.java (已删除) ruoyi-sms/src/main/java/com/ruoyi/sms/config/properties/SmsProperties.java (已删除) ruoyi-sms/src/main/java/com/ruoyi/sms/core/AliyunSmsTemplate.java (已删除) ruoyi-sms/src/main/java/com/ruoyi/sms/core/SmsTemplate.java (已删除) ruoyi-sms/src/main/java/com/ruoyi/sms/core/TencentSmsTemplate.java (已删除) ruoyi-sms/src/main/java/com/ruoyi/sms/entity/SmsResult.java (已删除) ruoyi-sms/src/main/java/com/ruoyi/sms/exception/SmsException.java (已删除) ruoyi-system/pom.xml (已删除) ruoyi-system/src/main/java/com/ruoyi/system/controller/monitor/CacheController.java (已删除) ruoyi-system/src/main/java/com/ruoyi/system/controller/monitor/SysLogininforController.java (已删除) ruoyi-system/src/main/java/com/ruoyi/system/controller/monitor/SysOperlogController.java (已删除) ruoyi-system/src/main/java/com/ruoyi/system/controller/monitor/SysUserOnlineController.java (已删除) ruoyi-system/src/main/java/com/ruoyi/system/controller/system/SysConfigController.java (已删除) ruoyi-system/src/main/java/com/ruoyi/system/controller/system/SysDeptController.java (已删除) ruoyi-system/src/main/java/com/ruoyi/system/controller/system/SysDictDataController.java (已删除) ruoyi-system/src/main/java/com/ruoyi/system/controller/system/SysDictTypeController.java (已删除) ruoyi-system/src/main/java/com/ruoyi/system/controller/system/SysMenuController.java (已删除) ruoyi-system/src/main/java/com/ruoyi/system/controller/system/SysNoticeController.java (已删除) ruoyi-system/src/main/java/com/ruoyi/system/controller/system/SysOssConfigController.java (已删除) ruoyi-system/src/main/java/com/ruoyi/system/controller/system/SysOssController.java (已删除) ruoyi-system/src/main/java/com/ruoyi/system/controller/system/SysPostController.java (已删除) ruoyi-system/src/main/java/com/ruoyi/system/controller/system/SysProfileController.java (已删除) ruoyi-system/src/main/java/com/ruoyi/system/controller/system/SysRoleController.java (已删除) ruoyi-system/src/main/java/com/ruoyi/system/controller/system/SysUserController.java (已删除) ruoyi-system/src/main/java/com/ruoyi/system/domain/SysCache.java (已删除) ruoyi-system/src/main/java/com/ruoyi/system/domain/SysConfig.java (已删除) ruoyi-system/src/main/java/com/ruoyi/system/domain/SysDept.java (已删除) ruoyi-system/src/main/java/com/ruoyi/system/domain/SysDictData.java (已删除) ruoyi-system/src/main/java/com/ruoyi/system/domain/SysDictType.java (已删除) ruoyi-system/src/main/java/com/ruoyi/system/domain/SysLogininfor.java (已删除) ruoyi-system/src/main/java/com/ruoyi/system/domain/SysMenu.java (已删除) ruoyi-system/src/main/java/com/ruoyi/system/domain/SysNotice.java (已删除) ruoyi-system/src/main/java/com/ruoyi/system/domain/SysOperLog.java (已删除) ruoyi-system/src/main/java/com/ruoyi/system/domain/SysOss.java (已删除) ruoyi-system/src/main/java/com/ruoyi/system/domain/SysOssConfig.java (已删除) ruoyi-system/src/main/java/com/ruoyi/system/domain/SysPost.java (已删除) ruoyi-system/src/main/java/com/ruoyi/system/domain/SysRole.java (已删除) ruoyi-system/src/main/java/com/ruoyi/system/domain/SysUser.java (已删除) ruoyi-system/src/main/java/com/ruoyi/system/domain/bo/SysOssBo.java (已删除) ruoyi-system/src/main/java/com/ruoyi/system/domain/bo/SysOssConfigBo.java (已删除) ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/MetaVo.java (已删除) ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/SysUserExportVo.java (已删除) ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/SysUserImportVo.java (已删除) ruoyi-system/src/main/java/com/ruoyi/system/listener/SysUserImportListener.java (已删除) ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysConfigMapper.java (已删除) ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysDeptMapper.java (已删除) ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysDictDataMapper.java (已删除) ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysDictTypeMapper.java (已删除) ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysLogininforMapper.java (已删除) ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysMenuMapper.java (已删除) ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysNoticeMapper.java (已删除) ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysOperLogMapper.java (已删除) ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysOssConfigMapper.java (已删除) ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysOssMapper.java (已删除) ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysPostMapper.java (已删除) ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysRoleDeptMapper.java (已删除) ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysRoleMapper.java (已删除) ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysRoleMenuMapper.java (已删除) ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysUserMapper.java (已删除) ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysUserPostMapper.java (已删除) ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysUserRoleMapper.java (已删除) ruoyi-system/src/main/java/com/ruoyi/system/runner/SystemApplicationRunner.java (已删除) ruoyi-system/src/main/java/com/ruoyi/system/service/ISysConfigService.java (已删除) ruoyi-system/src/main/java/com/ruoyi/system/service/ISysDictDataService.java (已删除) ruoyi-system/src/main/java/com/ruoyi/system/service/ISysDictTypeService.java (已删除) ruoyi-system/src/main/java/com/ruoyi/system/service/ISysLogininforService.java (已删除) ruoyi-system/src/main/java/com/ruoyi/system/service/ISysNoticeService.java (已删除) ruoyi-system/src/main/java/com/ruoyi/system/service/ISysOperLogService.java (已删除) ruoyi-system/src/main/java/com/ruoyi/system/service/ISysOssConfigService.java (已删除) ruoyi-system/src/main/java/com/ruoyi/system/service/ISysOssService.java (已删除) ruoyi-system/src/main/java/com/ruoyi/system/service/ISysPostService.java (已删除) ruoyi-system/src/main/java/com/ruoyi/system/service/ISysRoleService.java (已删除) ruoyi-system/src/main/java/com/ruoyi/system/service/ISysUserService.java (已删除) ruoyi-system/src/main/java/com/ruoyi/system/service/SysLoginService.java (已删除) ruoyi-system/src/main/java/com/ruoyi/system/service/SysRegisterService.java (已删除) ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysConfigServiceImpl.java (已删除) ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysDataScopeServiceImpl.java (已删除) ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysDeptServiceImpl.java (已删除) ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysDictDataServiceImpl.java (已删除) ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysDictTypeServiceImpl.java (已删除) ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysLogininforServiceImpl.java (已删除) ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysMenuServiceImpl.java (已删除) ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysNoticeServiceImpl.java (已删除) ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysOperLogServiceImpl.java (已删除) ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysOssConfigServiceImpl.java (已删除) ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysOssServiceImpl.java (已删除) ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysPostServiceImpl.java (已删除) ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysRoleServiceImpl.java (已删除) ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysSensitiveServiceImpl.java (已删除) ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysUserServiceImpl.java (已删除)