疯狂的狮子li
2023-03-31 df38aad2b56c0f80059e2ae3417182b57926783d
update 更改包名为 org.dromara
update 更改文档地址为 plus-doc.dromara.org
已添加428个文件
已删除428个文件
已修改84个文件
68865 ■■■■ 文件已修改
README.md 35 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pom.xml 12 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/pom.xml 12 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/java/com/ruoyi/RuoYiApplication.java 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/java/com/ruoyi/RuoYiServletInitializer.java 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/java/com/ruoyi/web/controller/AuthController.java 153 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/java/com/ruoyi/web/controller/CaptchaController.java 135 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/java/com/ruoyi/web/controller/IndexController.java 32 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/java/com/ruoyi/web/domain/vo/CaptchaVo.java 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/java/com/ruoyi/web/domain/vo/LoginTenantVo.java 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/java/com/ruoyi/web/domain/vo/LoginVo.java 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/java/com/ruoyi/web/domain/vo/TenantListVo.java 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/java/com/ruoyi/web/service/SysLoginService.java 390 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/java/com/ruoyi/web/service/SysRegisterService.java 106 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/java/org/dromara/DromaraApplication.java 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/java/org/dromara/DromaraServletInitializer.java 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/java/org/dromara/web/controller/AuthController.java 153 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/java/org/dromara/web/controller/CaptchaController.java 135 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/java/org/dromara/web/controller/IndexController.java 32 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/java/org/dromara/web/domain/vo/CaptchaVo.java 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/java/org/dromara/web/domain/vo/LoginTenantVo.java 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/java/org/dromara/web/domain/vo/LoginVo.java 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/java/org/dromara/web/domain/vo/TenantListVo.java 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/java/org/dromara/web/service/SysLoginService.java 391 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/java/org/dromara/web/service/SysRegisterService.java 106 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/resources/application.yml 18 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/test/java/com/ruoyi/test/AssertUnitTest.java 45 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/test/java/com/ruoyi/test/DemoUnitTest.java 70 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/test/java/com/ruoyi/test/ParamUnitTest.java 72 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/test/java/com/ruoyi/test/TagUnitTest.java 54 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/test/java/org/dromara/test/AssertUnitTest.java 45 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/test/java/org/dromara/test/DemoUnitTest.java 70 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/test/java/org/dromara/test/ParamUnitTest.java 72 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/test/java/org/dromara/test/TagUnitTest.java 54 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/pom.xml 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-bom/pom.xml 44 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/pom.xml 2 ●●● 补丁 | 查看 | 原始文档 | 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/RuoYiConfig.java 49 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/config/ThreadPoolConfig.java 57 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/config/ValidatorConfig.java 39 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/config/properties/ThreadPoolProperties.java 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/constant/CacheConstants.java 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/constant/CacheNames.java 63 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/constant/Constants.java 81 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/constant/GlobalConstants.java 39 ●●●●● 补丁 | 查看 | 原始文档 | 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/TenantConstants.java 45 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/constant/UserConstants.java 132 ●●●●● 补丁 | 查看 | 原始文档 | 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 38 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/domain/dto/UserOnlineDTO.java 62 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/domain/model/EmailLoginBody.java 35 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/domain/model/LoginBody.java 48 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/domain/model/LoginUser.java 123 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/domain/model/RegisterBody.java 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/domain/model/SmsLoginBody.java 34 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/domain/model/XcxLoginUser.java 27 ●●●●● 补丁 | 查看 | 原始文档 | 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 44 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/enums/TenantStatus.java 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/enums/UserStatus.java 30 ●●●●● 补丁 | 查看 | 原始文档 | 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/manager/ShutdownManager.java 41 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/service/ConfigService.java 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/service/DeptService.java 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/service/DictService.java 57 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/service/OssService.java 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/service/UserService.java 18 ●●●●● 补丁 | 查看 | 原始文档 | 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/MapstructUtils.java 92 ●●●●● 补丁 | 查看 | 原始文档 | 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 193 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/SpringUtils.java 62 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/StreamUtils.java 254 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/StringUtils.java 321 ●●●●● 补丁 | 查看 | 原始文档 | 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 43 ●●●●● 补丁 | 查看 | 原始文档 | 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 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/ip/RegionUtils.java 67 ●●●●● 补丁 | 查看 | 原始文档 | 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 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/validate/EditGroup.java 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/validate/QueryGroup.java 9 ●●●●● 补丁 | 查看 | 原始文档 | 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/java/org/dromara/common/core/config/ApplicationConfig.java 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/config/AsyncConfig.java 54 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/config/RuoYiConfig.java 49 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/config/ThreadPoolConfig.java 57 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/config/ValidatorConfig.java 39 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/config/properties/ThreadPoolProperties.java 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/constant/CacheConstants.java 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/constant/CacheNames.java 63 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/constant/Constants.java 81 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/constant/GlobalConstants.java 39 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/constant/HttpStatus.java 93 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/constant/TenantConstants.java 45 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/constant/UserConstants.java 132 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/R.java 110 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/dto/RoleDTO.java 38 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/dto/UserOnlineDTO.java 62 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/model/EmailLoginBody.java 35 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/model/LoginBody.java 48 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/model/LoginUser.java 123 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/model/RegisterBody.java 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/model/SmsLoginBody.java 34 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/model/XcxLoginUser.java 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/enums/DeviceType.java 32 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/enums/LoginType.java 44 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/enums/TenantStatus.java 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/enums/UserStatus.java 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/enums/UserType.java 37 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/exception/DemoModeException.java 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/exception/GlobalException.java 53 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/exception/ServiceException.java 67 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/exception/UtilException.java 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/exception/base/BaseException.java 79 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/exception/file/FileException.java 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/exception/file/FileNameLengthLimitExceededException.java 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/exception/file/FileSizeLimitExceededException.java 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/exception/user/CaptchaException.java 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/exception/user/CaptchaExpireException.java 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/exception/user/UserException.java 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/exception/user/UserPasswordNotMatchException.java 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/exception/user/UserPasswordRetryLimitExceedException.java 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/manager/ShutdownManager.java 41 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/service/ConfigService.java 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/service/DeptService.java 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/service/DictService.java 57 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/service/OssService.java 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/service/UserService.java 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/DateUtils.java 168 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/MapstructUtils.java 92 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/MessageUtils.java 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/ServletUtils.java 193 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/SpringUtils.java 62 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/StreamUtils.java 254 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/StringUtils.java 321 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/Threads.java 75 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/TreeBuildUtils.java 35 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/ValidatorUtils.java 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/file/FileUtils.java 43 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/file/MimeTypeUtils.java 40 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/ip/AddressUtils.java 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/ip/RegionUtils.java 67 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/reflect/ReflectUtils.java 56 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/sql/SqlUtil.java 57 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/validate/AddGroup.java 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/validate/EditGroup.java 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/validate/QueryGroup.java 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/xss/Xss.java 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/xss/XssValidator.java 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 12 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-doc/pom.xml 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-doc/src/main/java/com/ruoyi/common/doc/config/SwaggerConfig.java 126 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-doc/src/main/java/com/ruoyi/common/doc/config/properties/SwaggerProperties.java 94 ●●●●● 补丁 | 查看 | 原始文档 | 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/java/org/dromara/common/doc/config/SwaggerConfig.java 126 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-doc/src/main/java/org/dromara/common/doc/config/properties/SwaggerProperties.java 94 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-doc/src/main/java/org/dromara/common/doc/handler/OpenApiHandler.java 252 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-doc/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-encrypt/pom.xml 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-encrypt/src/main/java/com/ruoyi/common/encrypt/annotation/EncryptField.java 44 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-encrypt/src/main/java/com/ruoyi/common/encrypt/config/EncryptorAutoConfiguration.java 41 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-encrypt/src/main/java/com/ruoyi/common/encrypt/core/EncryptContext.java 41 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-encrypt/src/main/java/com/ruoyi/common/encrypt/core/EncryptorManager.java 94 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-encrypt/src/main/java/com/ruoyi/common/encrypt/core/IEncryptor.java 35 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-encrypt/src/main/java/com/ruoyi/common/encrypt/core/encryptor/AbstractEncryptor.java 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-encrypt/src/main/java/com/ruoyi/common/encrypt/core/encryptor/AesEncryptor.java 69 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-encrypt/src/main/java/com/ruoyi/common/encrypt/core/encryptor/Base64Encryptor.java 48 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-encrypt/src/main/java/com/ruoyi/common/encrypt/core/encryptor/RsaEncryptor.java 65 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-encrypt/src/main/java/com/ruoyi/common/encrypt/core/encryptor/Sm2Encryptor.java 64 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-encrypt/src/main/java/com/ruoyi/common/encrypt/core/encryptor/Sm4Encryptor.java 67 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-encrypt/src/main/java/com/ruoyi/common/encrypt/enumd/AlgorithmType.java 48 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-encrypt/src/main/java/com/ruoyi/common/encrypt/enumd/EncodeType.java 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-encrypt/src/main/java/com/ruoyi/common/encrypt/interceptor/MybatisDecryptInterceptor.java 112 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-encrypt/src/main/java/com/ruoyi/common/encrypt/interceptor/MybatisEncryptInterceptor.java 116 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-encrypt/src/main/java/com/ruoyi/common/encrypt/properties/EncryptorProperties.java 48 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/annotation/EncryptField.java 44 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/config/EncryptorAutoConfiguration.java 41 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/core/EncryptContext.java 41 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/core/EncryptorManager.java 94 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/core/IEncryptor.java 35 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/core/encryptor/AbstractEncryptor.java 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/core/encryptor/AesEncryptor.java 69 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/core/encryptor/Base64Encryptor.java 48 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/core/encryptor/RsaEncryptor.java 65 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/core/encryptor/Sm2Encryptor.java 64 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/core/encryptor/Sm4Encryptor.java 67 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/enumd/AlgorithmType.java 48 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/enumd/EncodeType.java 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/interceptor/MybatisDecryptInterceptor.java 112 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/interceptor/MybatisEncryptInterceptor.java 116 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/properties/EncryptorProperties.java 48 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-encrypt/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-excel/pom.xml 4 ●●●● 补丁 | 查看 | 原始文档 | 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 32 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-excel/src/main/java/com/ruoyi/common/excel/annotation/ExcelEnumFormat.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/convert/ExcelEnumConvert.java 75 ●●●●● 补丁 | 查看 | 原始文档 | 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 104 ●●●●● 补丁 | 查看 | 原始文档 | 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 327 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/annotation/CellMerge.java 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/annotation/ExcelDictFormat.java 32 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/annotation/ExcelEnumFormat.java 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/convert/ExcelBigNumberConvert.java 52 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/convert/ExcelDictConvert.java 73 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/convert/ExcelEnumConvert.java 75 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/core/CellMergeStrategy.java 114 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/core/DefaultExcelListener.java 104 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/core/DefautExcelResult.java 73 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/core/ExcelListener.java 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/core/ExcelResult.java 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/utils/ExcelUtil.java 327 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-idempotent/pom.xml 6 ●●●● 补丁 | 查看 | 原始文档 | 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 152 ●●●●● 补丁 | 查看 | 原始文档 | 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/java/org/dromara/common/idempotent/annotation/RepeatSubmit.java 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-idempotent/src/main/java/org/dromara/common/idempotent/aspectj/RepeatSubmitAspect.java 152 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-idempotent/src/main/java/org/dromara/common/idempotent/config/IdempotentConfig.java 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-idempotent/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-job/pom.xml 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-job/src/main/java/com/ruoyi/common/job/config/XxlJobConfig.java 38 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-job/src/main/java/com/ruoyi/common/job/config/properties/XxlJobProperties.java 40 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-job/src/main/java/org/dromara/common/job/config/XxlJobConfig.java 38 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-job/src/main/java/org/dromara/common/job/config/properties/XxlJobProperties.java 40 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-job/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-json/pom.xml 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-json/src/main/java/com/ruoyi/common/json/config/JacksonConfig.java 47 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-json/src/main/java/com/ruoyi/common/json/handler/BigNumberSerializer.java 42 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-json/src/main/java/com/ruoyi/common/json/utils/JsonUtils.java 113 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-json/src/main/java/org/dromara/common/json/config/JacksonConfig.java 47 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-json/src/main/java/org/dromara/common/json/handler/BigNumberSerializer.java 42 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-json/src/main/java/org/dromara/common/json/utils/JsonUtils.java 113 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-json/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-log/pom.xml 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-log/src/main/java/com/ruoyi/common/log/annotation/Log.java 48 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-log/src/main/java/com/ruoyi/common/log/aspect/LogAspect.java 221 ●●●●● 补丁 | 查看 | 原始文档 | 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 52 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-log/src/main/java/com/ruoyi/common/log/event/OperLogEvent.java 115 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-log/src/main/java/org/dromara/common/log/annotation/Log.java 48 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-log/src/main/java/org/dromara/common/log/aspect/LogAspect.java 221 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-log/src/main/java/org/dromara/common/log/enums/BusinessStatus.java 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-log/src/main/java/org/dromara/common/log/enums/BusinessType.java 58 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-log/src/main/java/org/dromara/common/log/enums/OperatorType.java 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-log/src/main/java/org/dromara/common/log/event/LogininforEvent.java 52 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-log/src/main/java/org/dromara/common/log/event/OperLogEvent.java 115 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-log/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-mail/pom.xml 4 ●●●● 补丁 | 查看 | 原始文档 | 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 69 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-mail/src/main/java/com/ruoyi/common/mail/utils/InternalMailUtil.java 108 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-mail/src/main/java/com/ruoyi/common/mail/utils/Mail.java 486 ●●●●● 补丁 | 查看 | 原始文档 | 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/java/org/dromara/common/mail/config/MailConfig.java 37 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-mail/src/main/java/org/dromara/common/mail/config/properties/MailProperties.java 69 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-mail/src/main/java/org/dromara/common/mail/utils/InternalMailUtil.java 108 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-mail/src/main/java/org/dromara/common/mail/utils/Mail.java 486 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-mail/src/main/java/org/dromara/common/mail/utils/MailUtils.java 469 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-mail/src/main/java/org/dromara/common/mail/utils/UserPassAuthenticator.java 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-mail/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-mybatis/pom.xml 6 ●●●● 补丁 | 查看 | 原始文档 | 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/domain/BaseEntity.java 71 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-mybatis/src/main/java/com/ruoyi/common/mybatis/core/mapper/BaseMapperPlus.java 198 ●●●●● 补丁 | 查看 | 原始文档 | 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/InjectionMetaObjectHandler.java 81 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-mybatis/src/main/java/com/ruoyi/common/mybatis/handler/MybatisExceptionHandler.java 46 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-mybatis/src/main/java/com/ruoyi/common/mybatis/handler/PlusDataPermissionHandler.java 198 ●●●●● 补丁 | 查看 | 原始文档 | 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 64 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-mybatis/src/main/java/com/ruoyi/common/mybatis/interceptor/PlusDataPermissionInterceptor.java 107 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/annotation/DataColumn.java 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/annotation/DataPermission.java 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/config/MybatisPlusConfig.java 102 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/core/domain/BaseEntity.java 71 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/core/mapper/BaseMapperPlus.java 198 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/core/page/PageQuery.java 114 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/core/page/TableDataInfo.java 81 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/enums/DataBaseType.java 49 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/enums/DataScopeType.java 73 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/handler/InjectionMetaObjectHandler.java 81 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/handler/MybatisExceptionHandler.java 46 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/handler/PlusDataPermissionHandler.java 198 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/helper/DataBaseHelper.java 72 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/helper/DataPermissionHelper.java 64 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/interceptor/PlusDataPermissionInterceptor.java 107 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-mybatis/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-oss/pom.xml 6 ●●●● 补丁 | 查看 | 原始文档 | 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 245 ●●●●● 补丁 | 查看 | 原始文档 | 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 63 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-oss/src/main/java/com/ruoyi/common/oss/properties/OssProperties.java 58 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-oss/src/main/java/org/dromara/common/oss/constant/OssConstant.java 38 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-oss/src/main/java/org/dromara/common/oss/core/OssClient.java 245 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-oss/src/main/java/org/dromara/common/oss/entity/UploadResult.java 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-oss/src/main/java/org/dromara/common/oss/enumd/AccessPolicyType.java 55 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-oss/src/main/java/org/dromara/common/oss/enumd/PolicyType.java 35 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-oss/src/main/java/org/dromara/common/oss/exception/OssException.java 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-oss/src/main/java/org/dromara/common/oss/factory/OssFactory.java 63 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-oss/src/main/java/org/dromara/common/oss/properties/OssProperties.java 58 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-ratelimiter/pom.xml 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-ratelimiter/src/main/java/com/ruoyi/common/ratelimiter/annotation/RateLimiter.java 41 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-ratelimiter/src/main/java/com/ruoyi/common/ratelimiter/aspectj/RateLimiterAspect.java 119 ●●●●● 补丁 | 查看 | 原始文档 | 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/java/org/dromara/common/ratelimiter/annotation/RateLimiter.java 41 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-ratelimiter/src/main/java/org/dromara/common/ratelimiter/aspectj/RateLimiterAspect.java 119 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-ratelimiter/src/main/java/org/dromara/common/ratelimiter/config/RateLimiterConfig.java 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-ratelimiter/src/main/java/org/dromara/common/ratelimiter/enums/LimitType.java 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-ratelimiter/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-redis/pom.xml 4 ●●●● 补丁 | 查看 | 原始文档 | 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 135 ●●●●● 补丁 | 查看 | 原始文档 | 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/java/org/dromara/common/redis/config/RedisConfig.java 130 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/config/properties/RedissonProperties.java 135 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/handler/KeyPrefixHandler.java 50 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/manager/PlusSpringCacheManager.java 191 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/utils/CacheUtils.java 75 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/utils/QueueUtils.java 180 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/utils/RedisUtils.java 462 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-redis/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-satoken/pom.xml 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-satoken/src/main/java/com/ruoyi/common/satoken/config/SaTokenConfig.java 43 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-satoken/src/main/java/com/ruoyi/common/satoken/core/dao/PlusSaTokenDao.java 176 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-satoken/src/main/java/com/ruoyi/common/satoken/core/service/SaPermissionImpl.java 47 ●●●●● 补丁 | 查看 | 原始文档 | 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 172 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-satoken/src/main/java/org/dromara/common/satoken/config/SaTokenConfig.java 43 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-satoken/src/main/java/org/dromara/common/satoken/core/dao/PlusSaTokenDao.java 176 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-satoken/src/main/java/org/dromara/common/satoken/core/service/SaPermissionImpl.java 47 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-satoken/src/main/java/org/dromara/common/satoken/listener/UserActionListener.java 139 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-satoken/src/main/java/org/dromara/common/satoken/utils/LoginHelper.java 172 ●●●●● 补丁 | 查看 | 原始文档 | 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 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-security/src/main/java/com/ruoyi/common/security/config/SecurityConfig.java 59 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-security/src/main/java/com/ruoyi/common/security/config/properties/SecurityProperties.java 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-security/src/main/java/com/ruoyi/common/security/handler/AllUrlHandler.java 39 ●●●●● 补丁 | 查看 | 原始文档 | 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/java/org/dromara/common/security/config/SecurityConfig.java 59 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-security/src/main/java/org/dromara/common/security/config/properties/SecurityProperties.java 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-security/src/main/java/org/dromara/common/security/handler/AllUrlHandler.java 39 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-security/src/main/java/org/dromara/common/security/handler/GlobalExceptionHandler.java 141 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-security/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-sensitive/pom.xml 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-sensitive/src/main/java/com/ruoyi/common/sensitive/annotation/Sensitive.java 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-sensitive/src/main/java/com/ruoyi/common/sensitive/core/SensitiveService.java 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-sensitive/src/main/java/com/ruoyi/common/sensitive/core/SensitiveStrategy.java 49 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-sensitive/src/main/java/com/ruoyi/common/sensitive/handler/SensitiveHandler.java 54 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-sensitive/src/main/java/org/dromara/common/sensitive/annotation/Sensitive.java 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-sensitive/src/main/java/org/dromara/common/sensitive/core/SensitiveService.java 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-sensitive/src/main/java/org/dromara/common/sensitive/core/SensitiveStrategy.java 49 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-sensitive/src/main/java/org/dromara/common/sensitive/handler/SensitiveHandler.java 54 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-sms/pom.xml 4 ●●●● 补丁 | 查看 | 原始文档 | 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 45 ●●●●● 补丁 | 查看 | 原始文档 | 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/java/org/dromara/common/sms/config/SmsConfig.java 48 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-sms/src/main/java/org/dromara/common/sms/config/properties/SmsProperties.java 45 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-sms/src/main/java/org/dromara/common/sms/core/AliyunSmsTemplate.java 66 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-sms/src/main/java/org/dromara/common/sms/core/SmsTemplate.java 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-sms/src/main/java/org/dromara/common/sms/core/TencentSmsTemplate.java 82 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-sms/src/main/java/org/dromara/common/sms/entity/SmsResult.java 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-sms/src/main/java/org/dromara/common/sms/exception/SmsException.java 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-sms/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-tenant/pom.xml 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-tenant/src/main/java/com/ruoyi/common/tenant/config/TenantConfig.java 100 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-tenant/src/main/java/com/ruoyi/common/tenant/core/TenantEntity.java 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-tenant/src/main/java/com/ruoyi/common/tenant/core/TenantSaTokenDao.java 148 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-tenant/src/main/java/com/ruoyi/common/tenant/exception/TenantException.java 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-tenant/src/main/java/com/ruoyi/common/tenant/handle/PlusTenantLineHandler.java 57 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-tenant/src/main/java/com/ruoyi/common/tenant/handle/TenantKeyPrefixHandler.java 58 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-tenant/src/main/java/com/ruoyi/common/tenant/helper/TenantHelper.java 110 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-tenant/src/main/java/com/ruoyi/common/tenant/manager/TenantSpringCacheManager.java 32 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-tenant/src/main/java/com/ruoyi/common/tenant/properties/TenantProperties.java 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-tenant/src/main/java/org/dromara/common/tenant/config/TenantConfig.java 100 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-tenant/src/main/java/org/dromara/common/tenant/core/TenantEntity.java 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-tenant/src/main/java/org/dromara/common/tenant/core/TenantSaTokenDao.java 148 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-tenant/src/main/java/org/dromara/common/tenant/exception/TenantException.java 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-tenant/src/main/java/org/dromara/common/tenant/handle/PlusTenantLineHandler.java 57 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-tenant/src/main/java/org/dromara/common/tenant/handle/TenantKeyPrefixHandler.java 58 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-tenant/src/main/java/org/dromara/common/tenant/helper/TenantHelper.java 110 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-tenant/src/main/java/org/dromara/common/tenant/manager/TenantSpringCacheManager.java 32 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-tenant/src/main/java/org/dromara/common/tenant/properties/TenantProperties.java 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-tenant/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-translation/pom.xml 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-translation/src/main/java/com/ruoyi/common/translation/annotation/Translation.java 39 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-translation/src/main/java/com/ruoyi/common/translation/annotation/TranslationType.java 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-translation/src/main/java/com/ruoyi/common/translation/config/TranslationConfig.java 50 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-translation/src/main/java/com/ruoyi/common/translation/constant/TransConstant.java 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-translation/src/main/java/com/ruoyi/common/translation/core/TranslationInterface.java 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-translation/src/main/java/com/ruoyi/common/translation/core/handler/TranslationBeanSerializerModifier.java 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-translation/src/main/java/com/ruoyi/common/translation/core/handler/TranslationHandler.java 65 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-translation/src/main/java/com/ruoyi/common/translation/core/impl/DeptNameTranslationImpl.java 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-translation/src/main/java/com/ruoyi/common/translation/core/impl/DictTypeTranslationImpl.java 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-translation/src/main/java/com/ruoyi/common/translation/core/impl/OssUrlTranslationImpl.java 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-translation/src/main/java/com/ruoyi/common/translation/core/impl/UserNameTranslationImpl.java 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-translation/src/main/java/org/dromara/common/translation/annotation/Translation.java 39 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-translation/src/main/java/org/dromara/common/translation/annotation/TranslationType.java 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-translation/src/main/java/org/dromara/common/translation/config/TranslationConfig.java 50 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-translation/src/main/java/org/dromara/common/translation/constant/TransConstant.java 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-translation/src/main/java/org/dromara/common/translation/core/TranslationInterface.java 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-translation/src/main/java/org/dromara/common/translation/core/handler/TranslationBeanSerializerModifier.java 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-translation/src/main/java/org/dromara/common/translation/core/handler/TranslationHandler.java 65 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-translation/src/main/java/org/dromara/common/translation/core/impl/DeptNameTranslationImpl.java 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-translation/src/main/java/org/dromara/common/translation/core/impl/DictTypeTranslationImpl.java 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-translation/src/main/java/org/dromara/common/translation/core/impl/OssUrlTranslationImpl.java 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-translation/src/main/java/org/dromara/common/translation/core/impl/UserNameTranslationImpl.java 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-translation/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 10 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-web/pom.xml 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-web/src/main/java/com/ruoyi/common/web/config/CaptchaConfig.java 65 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-web/src/main/java/com/ruoyi/common/web/config/FilterConfig.java 53 ●●●●● 补丁 | 查看 | 原始文档 | 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 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-web/src/main/java/com/ruoyi/common/web/core/BaseController.java 40 ●●●●● 补丁 | 查看 | 原始文档 | 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/java/org/dromara/common/web/config/CaptchaConfig.java 65 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/config/FilterConfig.java 53 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/config/I18nConfig.java 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/config/ResourcesConfig.java 52 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/config/UndertowConfig.java 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/config/properties/CaptchaProperties.java 38 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/config/properties/XssProperties.java 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/core/BaseController.java 40 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/core/I18nLocaleResolver.java 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/enums/CaptchaCategory.java 35 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/enums/CaptchaType.java 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/filter/RepeatableFilter.java 40 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/filter/RepeatedlyRequestWrapper.java 68 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/filter/XssFilter.java 62 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/filter/XssHttpServletRequestWrapper.java 97 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/interceptor/PlusWebInvokeTimeInterceptor.java 94 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/utils/UnsignedMathGenerator.java 88 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-web/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 10 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-websocket/pom.xml 10 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-websocket/src/main/java/com/ruoyi/common/websocket/config/WebSocketConfig.java 60 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-websocket/src/main/java/com/ruoyi/common/websocket/config/properties/WebSocketProperties.java 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-websocket/src/main/java/com/ruoyi/common/websocket/constant/WebSocketConstants.java 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-websocket/src/main/java/com/ruoyi/common/websocket/dto/WebSocketMessageDto.java 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-websocket/src/main/java/com/ruoyi/common/websocket/handler/PlusWebSocketHandler.java 103 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-websocket/src/main/java/com/ruoyi/common/websocket/holder/WebSocketSessionHolder.java 37 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-websocket/src/main/java/com/ruoyi/common/websocket/interceptor/PlusWebSocketInterceptor.java 51 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-websocket/src/main/java/com/ruoyi/common/websocket/listener/WebSocketTopicListener.java 38 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-websocket/src/main/java/com/ruoyi/common/websocket/utils/WebSocketUtils.java 102 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-websocket/src/main/java/org/dromara/common/websocket/config/WebSocketConfig.java 60 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-websocket/src/main/java/org/dromara/common/websocket/config/properties/WebSocketProperties.java 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-websocket/src/main/java/org/dromara/common/websocket/constant/WebSocketConstants.java 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-websocket/src/main/java/org/dromara/common/websocket/dto/WebSocketMessageDto.java 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-websocket/src/main/java/org/dromara/common/websocket/handler/PlusWebSocketHandler.java 103 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-websocket/src/main/java/org/dromara/common/websocket/holder/WebSocketSessionHolder.java 37 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-websocket/src/main/java/org/dromara/common/websocket/interceptor/PlusWebSocketInterceptor.java 51 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-websocket/src/main/java/org/dromara/common/websocket/listener/WebSocketTopicListener.java 38 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-websocket/src/main/java/org/dromara/common/websocket/utils/WebSocketUtils.java 102 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-websocket/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-extend/pom.xml 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-extend/ruoyi-monitor-admin/pom.xml 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-extend/ruoyi-monitor-admin/src/main/java/com/ruoyi/monitor/admin/MonitorAdminApplication.java 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-extend/ruoyi-monitor-admin/src/main/java/com/ruoyi/monitor/admin/config/AdminServerConfig.java 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-extend/ruoyi-monitor-admin/src/main/java/com/ruoyi/monitor/admin/config/SecurityConfig.java 52 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-extend/ruoyi-monitor-admin/src/main/java/com/ruoyi/monitor/admin/notifier/CustomNotifier.java 40 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-extend/ruoyi-monitor-admin/src/main/java/org/dromara/monitor/admin/MonitorAdminApplication.java 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-extend/ruoyi-monitor-admin/src/main/java/org/dromara/monitor/admin/config/AdminServerConfig.java 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-extend/ruoyi-monitor-admin/src/main/java/org/dromara/monitor/admin/config/SecurityConfig.java 52 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-extend/ruoyi-monitor-admin/src/main/java/org/dromara/monitor/admin/notifier/CustomNotifier.java 40 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-extend/ruoyi-xxl-job-admin/pom.xml 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/pom.xml 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-demo/pom.xml 34 ●●●● 补丁 | 查看 | 原始文档 | 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 71 ●●●●● 补丁 | 查看 | 原始文档 | 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 31 ●●●●● 补丁 | 查看 | 原始文档 | 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 147 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-demo/src/main/java/com/ruoyi/demo/controller/TestEncryptController.java 55 ●●●●● 补丁 | 查看 | 原始文档 | 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/WeSocketController.java 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-demo/src/main/java/com/ruoyi/demo/controller/package-info.java 1 ●●●● 补丁 | 查看 | 原始文档 | 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 22 ●●●●● 补丁 | 查看 | 原始文档 | 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/TestDemoEncrypt.java 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-demo/src/main/java/com/ruoyi/demo/domain/TestTree.java 65 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-demo/src/main/java/com/ruoyi/demo/domain/bo/TestDemoBo.java 62 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-demo/src/main/java/com/ruoyi/demo/domain/bo/TestDemoImportVo.java 53 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-demo/src/main/java/com/ruoyi/demo/domain/bo/TestTreeBo.java 54 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-demo/src/main/java/com/ruoyi/demo/domain/package-info.java 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-demo/src/main/java/com/ruoyi/demo/domain/vo/TestDemoVo.java 104 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-demo/src/main/java/com/ruoyi/demo/domain/vo/TestTreeVo.java 64 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-demo/src/main/java/com/ruoyi/demo/mapper/TestDemoEncryptMapper.java 13 ●●●●● 补丁 | 查看 | 原始文档 | 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 1 ●●●● 补丁 | 查看 | 原始文档 | 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 52 ●●●●● 补丁 | 查看 | 原始文档 | 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 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-demo/src/main/java/com/ruoyi/demo/service/package-info.java 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/controller/MailController.java 52 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/controller/RedisCacheController.java 95 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/controller/RedisLockController.java 71 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/controller/RedisPubSubController.java 47 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/controller/RedisRateLimiterController.java 52 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/controller/SmsController.java 76 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/controller/Swagger3DemoController.java 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/controller/TestBatchController.java 90 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/controller/TestDemoController.java 147 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/controller/TestEncryptController.java 55 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/controller/TestExcelController.java 97 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/controller/TestI18nController.java 71 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/controller/TestSensitiveController.java 76 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/controller/TestTreeController.java 107 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/controller/WeSocketController.java 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/controller/package-info.java 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/controller/queue/BoundedQueueController.java 90 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/controller/queue/DelayedQueueController.java 90 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/controller/queue/PriorityDemo.java 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/controller/queue/PriorityQueueController.java 89 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/domain/TestDemo.java 68 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/domain/TestDemoEncrypt.java 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/domain/TestTree.java 65 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/domain/bo/TestDemoBo.java 62 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/domain/bo/TestDemoImportVo.java 53 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/domain/bo/TestTreeBo.java 54 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/domain/package-info.java 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/domain/vo/TestDemoVo.java 104 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/domain/vo/TestTreeVo.java 64 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/mapper/TestDemoEncryptMapper.java 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/mapper/TestDemoMapper.java 58 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/mapper/TestTreeMapper.java 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/mapper/package-info.java 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/service/ITestDemoService.java 71 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/service/ITestTreeService.java 52 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/service/impl/TestDemoServiceImpl.java 110 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/service/impl/TestTreeServiceImpl.java 87 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/service/impl/package-info.java 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/service/package-info.java 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-demo/src/main/resources/mapper/demo/TestDemoMapper.xml 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-demo/src/main/resources/mapper/demo/TestTreeMapper.xml 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-generator/pom.xml 12 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-generator/src/main/java/com/ruoyi/generator/config/GenConfig.java 73 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-generator/src/main/java/com/ruoyi/generator/constant/GenConstants.java 186 ●●●●● 补丁 | 查看 | 原始文档 | 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 190 ●●●●● 补丁 | 查看 | 原始文档 | 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 459 ●●●●● 补丁 | 查看 | 原始文档 | 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 233 ●●●●● 补丁 | 查看 | 原始文档 | 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 335 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-generator/src/main/java/org/dromara/generator/config/GenConfig.java 73 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-generator/src/main/java/org/dromara/generator/constant/GenConstants.java 186 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-generator/src/main/java/org/dromara/generator/controller/GenController.java 207 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-generator/src/main/java/org/dromara/generator/domain/GenTable.java 190 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-generator/src/main/java/org/dromara/generator/domain/GenTableColumn.java 223 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-generator/src/main/java/org/dromara/generator/mapper/GenTableColumnMapper.java 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-generator/src/main/java/org/dromara/generator/mapper/GenTableMapper.java 58 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-generator/src/main/java/org/dromara/generator/service/GenTableServiceImpl.java 459 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-generator/src/main/java/org/dromara/generator/service/IGenTableService.java 133 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-generator/src/main/java/org/dromara/generator/util/GenUtils.java 233 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-generator/src/main/java/org/dromara/generator/util/VelocityInitializer.java 35 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-generator/src/main/java/org/dromara/generator/util/VelocityUtils.java 335 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-generator/src/main/resources/generator.yml 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-generator/src/main/resources/mapper/generator/GenTableColumnMapper.xml 12 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-generator/src/main/resources/mapper/generator/GenTableMapper.xml 30 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-generator/src/main/resources/vm/java/bo.java.vm 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-generator/src/main/resources/vm/java/controller.java.vm 22 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-generator/src/main/resources/vm/java/domain.java.vm 5 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-generator/src/main/resources/vm/java/mapper.java.vm 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-generator/src/main/resources/vm/java/service.java.vm 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-generator/src/main/resources/vm/java/serviceImpl.java.vm 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-generator/src/main/resources/vm/java/vo.java.vm 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-job/pom.xml 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-job/src/main/java/com/ruoyi/job/service/SampleService.java 252 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-job/src/main/java/org/dromara/job/service/SampleService.java 252 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/pom.xml 28 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/controller/monitor/CacheController.java 55 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/controller/monitor/SysLogininforController.java 89 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/controller/monitor/SysOperlogController.java 75 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/controller/monitor/SysUserOnlineController.java 91 ●●●●● 补丁 | 查看 | 原始文档 | 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 120 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/controller/system/SysDictDataController.java 118 ●●●●● 补丁 | 查看 | 原始文档 | 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 182 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/controller/system/SysNoticeController.java 81 ●●●●● 补丁 | 查看 | 原始文档 | 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 109 ●●●●● 补丁 | 查看 | 原始文档 | 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 123 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/controller/system/SysRoleController.java 250 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/controller/system/SysTenantController.java 176 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/controller/system/SysTenantPackageController.java 124 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/controller/system/SysUserController.java 261 ●●●●● 补丁 | 查看 | 原始文档 | 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 51 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysDept.java 78 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysDictData.java 76 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysDictType.java 46 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysLogininfor.java 75 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysMenu.java 191 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysNotice.java 51 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysOperLog.java 115 ●●●●● 补丁 | 查看 | 原始文档 | 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 51 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysRole.java 79 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysRoleDept.java 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysRoleMenu.java 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysTenant.java 103 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysTenantPackage.java 54 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysUser.java 115 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysUserOnline.java 54 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysUserPost.java 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysUserRole.java 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/bo/SysConfigBo.java 62 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/bo/SysDeptBo.java 73 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/bo/SysDictDataBo.java 88 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/bo/SysDictTypeBo.java 58 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/bo/SysLogininforBo.java 77 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/bo/SysMenuBo.java 111 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/bo/SysNoticeBo.java 65 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/bo/SysOperLogBo.java 127 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/bo/SysOssBo.java 49 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/bo/SysOssConfigBo.java 109 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/bo/SysPostBo.java 62 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/bo/SysRoleBo.java 97 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/bo/SysTenantBo.java 114 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/bo/SysTenantPackageBo.java 59 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/bo/SysUserBo.java 135 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/bo/SysUserProfileBo.java 55 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/AvatarVo.java 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/CacheListInfoVo.java 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/DeptTreeSelectVo.java 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/MenuTreeSelectVo.java 26 ●●●●● 补丁 | 查看 | 原始文档 | 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/ProfileVo.java 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/RouterVo.java 62 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/SysConfigVo.java 72 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/SysDeptVo.java 91 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/SysDictDataVo.java 95 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/SysDictTypeVo.java 66 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/SysLogininforVo.java 93 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/SysMenuVo.java 116 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/SysNoticeVo.java 73 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/SysOperLogVo.java 144 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/SysOssConfigVo.java 97 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/SysOssUploadVo.java 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/SysOssVo.java 72 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/SysPostVo.java 73 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/SysRoleVo.java 100 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/SysTenantPackageVo.java 66 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/SysTenantVo.java 115 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/SysUserExportVo.java 99 ●●●●● 补丁 | 查看 | 原始文档 | 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/domain/vo/SysUserInfoVo.java 40 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/SysUserVo.java 137 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/UserInfoVo.java 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/listener/SysUserImportListener.java 119 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysConfigMapper.java 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysDeptMapper.java 46 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysDictDataMapper.java 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysDictTypeMapper.java 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysLogininforMapper.java 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysMenuMapper.java 83 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysNoticeMapper.java 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysOperLogMapper.java 14 ●●●●● 补丁 | 查看 | 原始文档 | 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 32 ●●●●● 补丁 | 查看 | 原始文档 | 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 68 ●●●●● 补丁 | 查看 | 原始文档 | 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/SysTenantMapper.java 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysTenantPackageMapper.java 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysUserMapper.java 145 ●●●●● 补丁 | 查看 | 原始文档 | 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 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysConfigService.java 87 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysDataScopeService.java 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysDeptService.java 118 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysDictDataService.java 67 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysDictTypeService.java 95 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysLogininforService.java 47 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysMenuService.java 147 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysNoticeService.java 67 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysOperLogService.java 54 ●●●●● 补丁 | 查看 | 原始文档 | 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/ISysPermissionService.java 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysPostService.java 106 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysRoleService.java 181 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysTenantPackageService.java 52 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysTenantService.java 82 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysUserService.java 205 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysConfigServiceImpl.java 216 ●●●●● 补丁 | 查看 | 原始文档 | 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 325 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysDictDataServiceImpl.java 139 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysDictTypeServiceImpl.java 268 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysLogininforServiceImpl.java 160 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysMenuServiceImpl.java 365 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysNoticeServiceImpl.java 122 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysOperLogServiceImpl.java 144 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysOssConfigServiceImpl.java 183 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysOssServiceImpl.java 171 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysPermissionServiceImpl.java 61 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysPostServiceImpl.java 188 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysRoleServiceImpl.java 421 ●●●●● 补丁 | 查看 | 原始文档 | 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/SysTenantPackageServiceImpl.java 139 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysTenantServiceImpl.java 375 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysUserServiceImpl.java 525 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/monitor/CacheController.java 55 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/monitor/SysLogininforController.java 89 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/monitor/SysOperlogController.java 75 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/monitor/SysUserOnlineController.java 91 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysConfigController.java 137 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysDeptController.java 120 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysDictDataController.java 117 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysDictTypeController.java 125 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysMenuController.java 182 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysNoticeController.java 81 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysOssConfigController.java 105 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysOssController.java 108 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysPostController.java 115 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysProfileController.java 123 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysRoleController.java 250 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysTenantController.java 174 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysTenantPackageController.java 124 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysUserController.java 261 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/SysCache.java 47 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/SysConfig.java 51 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/SysDept.java 78 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/SysDictData.java 76 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/SysDictType.java 46 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/SysLogininfor.java 75 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/SysMenu.java 191 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/SysNotice.java 51 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/SysOperLog.java 115 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/SysOss.java 50 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/SysOssConfig.java 89 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/SysPost.java 51 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/SysRole.java 79 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/SysRoleDept.java 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/SysRoleMenu.java 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/SysTenant.java 103 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/SysTenantPackage.java 54 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/SysUser.java 115 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/SysUserOnline.java 54 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/SysUserPost.java 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/SysUserRole.java 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/SysConfigBo.java 62 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/SysDeptBo.java 73 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/SysDictDataBo.java 88 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/SysDictTypeBo.java 58 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/SysLogininforBo.java 77 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/SysMenuBo.java 111 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/SysNoticeBo.java 65 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/SysOperLogBo.java 127 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/SysOssBo.java 49 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/SysOssConfigBo.java 109 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/SysPostBo.java 62 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/SysRoleBo.java 97 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/SysTenantBo.java 114 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/SysTenantPackageBo.java 59 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/SysUserBo.java 134 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/SysUserProfileBo.java 55 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/AvatarVo.java 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/CacheListInfoVo.java 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/DeptTreeSelectVo.java 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/MenuTreeSelectVo.java 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/MetaVo.java 61 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/ProfileVo.java 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/RouterVo.java 62 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysConfigVo.java 72 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysDeptVo.java 91 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysDictDataVo.java 95 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysDictTypeVo.java 66 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysLogininforVo.java 93 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysMenuVo.java 116 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysNoticeVo.java 73 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysOperLogVo.java 144 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysOssConfigVo.java 97 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysOssUploadVo.java 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysOssVo.java 72 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysPostVo.java 73 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysRoleVo.java 100 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysTenantPackageVo.java 66 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysTenantVo.java 115 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysUserExportVo.java 99 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysUserImportVo.java 76 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysUserInfoVo.java 40 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysUserVo.java 137 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/UserInfoVo.java 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/listener/SysUserImportListener.java 119 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysConfigMapper.java 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysDeptMapper.java 46 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysDictDataMapper.java 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysDictTypeMapper.java 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysLogininforMapper.java 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysMenuMapper.java 83 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysNoticeMapper.java 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysOperLogMapper.java 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysOssConfigMapper.java 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysOssMapper.java 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysPostMapper.java 32 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysRoleDeptMapper.java 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysRoleMapper.java 68 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysRoleMenuMapper.java 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysTenantMapper.java 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysTenantPackageMapper.java 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysUserMapper.java 145 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysUserPostMapper.java 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysUserRoleMapper.java 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/runner/SystemApplicationRunner.java 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysConfigService.java 87 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysDataScopeService.java 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysDeptService.java 118 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysDictDataService.java 67 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysDictTypeService.java 95 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysLogininforService.java 47 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysMenuService.java 147 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysNoticeService.java 67 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysOperLogService.java 54 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysOssConfigService.java 65 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysOssService.java 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysPermissionService.java 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysPostService.java 106 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysRoleService.java 181 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysTenantPackageService.java 52 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysTenantService.java 82 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysUserService.java 205 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysConfigServiceImpl.java 216 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysDataScopeServiceImpl.java 61 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysDeptServiceImpl.java 325 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysDictDataServiceImpl.java 139 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysDictTypeServiceImpl.java 268 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysLogininforServiceImpl.java 160 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysMenuServiceImpl.java 365 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysNoticeServiceImpl.java 122 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysOperLogServiceImpl.java 144 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysOssConfigServiceImpl.java 183 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysOssServiceImpl.java 171 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysPermissionServiceImpl.java 61 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysPostServiceImpl.java 188 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysRoleServiceImpl.java 421 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysSensitiveServiceImpl.java 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysTenantPackageServiceImpl.java 139 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysTenantServiceImpl.java 375 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysUserServiceImpl.java 525 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysConfigMapper.xml 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysDeptMapper.xml 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysDictDataMapper.xml 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysDictTypeMapper.xml 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysLogininforMapper.xml 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysMenuMapper.xml 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysNoticeMapper.xml 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysOperLogMapper.xml 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysOssConfigMapper.xml 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysOssMapper.xml 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysPostMapper.xml 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysRoleDeptMapper.xml 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysRoleMapper.xml 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysRoleMenuMapper.xml 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysTenantMapper.xml 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysTenantPackageMapper.xml 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysUserMapper.xml 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysUserPostMapper.xml 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysUserRoleMapper.xml 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
README.md
@@ -5,7 +5,7 @@
## å¹³å°ç®€ä»‹
[![码云Gitee](https://gitee.com/dromara/RuoYi-Vue-Plus/badge/star.svg?theme=blue)](https://gitee.com/dromara/RuoYi-Vue-Plus)
[![GitHub](https://img.shields.io/github/stars/JavaLionLi/RuoYi-Vue-Plus.svg?style=social&label=Stars)](https://github.com/dromara/RuoYi-Vue-Plus)
[![GitHub](https://img.shields.io/github/stars/dromara/RuoYi-Vue-Plus.svg?style=social&label=Stars)](https://github.com/dromara/RuoYi-Vue-Plus)
[![License](https://img.shields.io/badge/License-MIT-blue.svg)](https://gitee.com/dromara/RuoYi-Vue-Plus/blob/master/LICENSE)
[![使用IntelliJ IDEA开发维护](https://img.shields.io/badge/IntelliJ%20IDEA-提供支持-blue.svg)](https://www.jetbrains.com/?from=RuoYi-Vue-Plus)
<br>
@@ -19,7 +19,7 @@
> é¡¹ç›®ä»£ç ã€æ–‡æ¡£ å‡å¼€æºå…è´¹å¯å•†ç”¨ éµå¾ªå¼€æºåè®®åœ¨é¡¹ç›®ä¸­ä¿ç•™å¼€æºåè®®æ–‡ä»¶å³å¯<br>
活到老写到老 ä¸ºå…´è¶£è€Œå¼€æº ä¸ºå­¦ä¹ è€Œå¼€æº ä¸ºè®©å¤§å®¶çœŸæ­£å¯ä»¥å­¦åˆ°æŠ€æœ¯è€Œå¼€æº
> ç³»ç»Ÿæ¼”示: [传送门](https://javalionli.gitee.io/plus-doc/#/common/demo_system)
> ç³»ç»Ÿæ¼”示: [传送门](https://plus-doc.dromara.org/#/common/demo_system)
> å‰ç«¯é¡¹ç›®åœ°å€: [plus-ui](https://gitee.com/JavaLionLi/plus-ui)
@@ -27,7 +27,7 @@
| åŠŸèƒ½          | æœ¬æ¡†æž¶                                                                                                               | RuoYi                                                                              |
|-------------|-------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------|
| å‰ç«¯é¡¹ç›®        | åŸºäºŽvue3-element-admin开源项目重写<br/>Vue3 + TS + ElementPlus                                                            | åŸºäºŽVue2/Vue3 + JS                                                                   |
| å‰ç«¯é¡¹ç›®        | é‡‡ç”¨ Vue3 + TS + ElementPlus é‡å†™                                                                                     | åŸºäºŽVue2/Vue3 + JS                                                                   |
| åŽç«¯é¡¹ç›®ç»“æž„      | é‡‡ç”¨æ’件化 + æ‰©å±•包形式 ç»“构解耦 æ˜“于扩展                                                                                           | æ¨¡å—相互注入耦合严重难以扩展                                                                     | 
| åŽç«¯ä»£ç é£Žæ ¼      | ä¸¥æ ¼éµå®ˆAlibaba规范与项目统一配置的代码格式化                                                                                        | ä»£ç ä¹¦å†™ä¸Žå¸¸è§„结构不同阅读障碍大                                                                   |
| Web容器       | é‡‡ç”¨ Undertow åŸºäºŽ XNIO çš„高性能容器                                                                                        | é‡‡ç”¨ Tomcat                                                                          |
@@ -51,6 +51,7 @@
| WebSocket协议 | åŸºäºŽ Spring å°è£…çš„ WebSocket åè®® æ‰©å±•了Token鉴权与分布式会话同步 ä¸å†åªæ˜¯åŸºäºŽå•机的废物                                                         | æ—                                                                                   |
| åºåˆ—化         | é‡‡ç”¨ Jackson Spring官方内置序列化 é è°±!!!                                                                                    | é‡‡ç”¨ fastjson bugjson è¿œè¿‘闻名                                                           | 
| åˆ†å¸ƒå¼å¹‚ç­‰       | å‚考美团GTIS防重系统简化实现(细节可看文档)                                                                                          | æ‰‹åŠ¨ç¼–å†™æ³¨è§£åŸºäºŽaop实现                                                                      |
| åˆ†å¸ƒå¼é”        | é‡‡ç”¨ Lock4j åº•层基于 Redisson                                                                                           | æ—                                                                                   |
| åˆ†å¸ƒå¼ä»»åŠ¡è°ƒåº¦     | é‡‡ç”¨ Xxl-Job å¤©ç”Ÿæ”¯æŒåˆ†å¸ƒå¼ ç»Ÿä¸€çš„管理中心                                                                                        | é‡‡ç”¨ Quartz åŸºäºŽæ•°æ®åº“锁性能差 é›†ç¾¤éœ€è¦åšå¾ˆå¤šé…ç½®ä¸Žæ”¹é€                                                    | 
| æ–‡ä»¶å­˜å‚¨        | é‡‡ç”¨ Minio åˆ†å¸ƒå¼æ–‡ä»¶å­˜å‚¨ å¤©ç”Ÿæ”¯æŒå¤šæœºã€å¤šç¡¬ç›˜ã€å¤šåˆ†ç‰‡ã€å¤šå‰¯æœ¬å­˜å‚¨<br/>支持权限管理 å®‰å…¨å¯é  æ–‡ä»¶å¯åŠ å¯†å­˜å‚¨                                                     | é‡‡ç”¨ æœ¬æœºæ–‡ä»¶å­˜å‚¨ æ–‡ä»¶è£¸æ¼ æ˜“丢失泄漏 ä¸æ”¯æŒé›†ç¾¤æœ‰å•点效应                                                    |
| äº‘存储         | é‡‡ç”¨ AWS S3 åè®®å®¢æˆ·ç«¯ æ”¯æŒ ä¸ƒç‰›ã€é˜¿é‡Œã€è…¾è®¯ ç­‰ä¸€åˆ‡æ”¯æŒS3协议的厂家                                                                          | ä¸æ”¯æŒ                                                                                |
@@ -101,17 +102,17 @@
使用框架前请仔细阅读文档重点注意事项
<br>
>[初始化项目 å¿…看](https://javalionli.gitee.io/plus-doc/#/ruoyi-vue-plus/quickstart/init)
>>[https://javalionli.gitee.io/plus-doc/#/ruoyi-vue-plus/quickstart/init](https://javalionli.gitee.io/plus-doc/#/ruoyi-vue-plus/quickstart/init)
>[初始化项目 å¿…看](https://plus-doc.dromara.org/#/ruoyi-vue-plus/quickstart/init)
>>[https://plus-doc.dromara.org/#/ruoyi-vue-plus/quickstart/init](https://plus-doc.dromara.org/#/ruoyi-vue-plus/quickstart/init)
>
>[专栏与视频 å…¥é—¨å¿…看](https://javalionli.gitee.io/plus-doc/#/common/column)
>>[https://javalionli.gitee.io/plus-doc/#/common/column](https://javalionli.gitee.io/plus-doc/#/common/column)
>[专栏与视频 å…¥é—¨å¿…看](https://plus-doc.dromara.org/#/common/column)
>>[https://plus-doc.dromara.org/#/common/column](https://plus-doc.dromara.org/#/common/column)
>
>[部署项目 å¿…看](https://javalionli.gitee.io/plus-doc/#/ruoyi-vue-plus/quickstart/deploy)
>>[https://javalionli.gitee.io/plus-doc/#/ruoyi-vue-plus/quickstart/deploy](https://javalionli.gitee.io/plus-doc/#/ruoyi-vue-plus/quickstart/deploy)
>[部署项目 å¿…看](https://plus-doc.dromara.org/#/ruoyi-vue-plus/quickstart/deploy)
>>[https://plus-doc.dromara.org/#/ruoyi-vue-plus/quickstart/deploy](https://plus-doc.dromara.org/#/ruoyi-vue-plus/quickstart/deploy)
>[参考文档 Wiki](https://javalionli.gitee.io/plus-doc)
>>[https://javalionli.gitee.io/plus-doc](https://javalionli.gitee.io/plus-doc)
>[参考文档 Wiki](https://plus-doc.dromara.org)
>>[https://plus-doc.dromara.org](https://plus-doc.dromara.org)
## è½¯ä»¶æž¶æž„图
@@ -119,7 +120,7 @@
## å¦‚何参与贡献
[参与贡献的方式 https://javalionli.gitee.io/plus-doc/#/common/contribution](https://javalionli.gitee.io/plus-doc/#/common/contribution)
[参与贡献的方式 https://plus-doc.dromara.org/#/common/contribution](https://plus-doc.dromara.org/#/common/contribution)
### å…¶ä»–
@@ -127,16 +128,16 @@
* GitHub åœ°å€ [RuoYi-Vue-Plus](https://github.com/dromara/RuoYi-Vue-Plus)
* å¾®æœåŠ¡ åˆ†æ”¯ [RuoYi-Cloud-Plus](https://gitee.com/JavaLionLi/RuoYi-Cloud-Plus)
* å‰ç«¯é¡¹ç›® åœ°å€ [plus-ui](https://gitee.com/JavaLionLi/plus-ui)
* ç”¨æˆ·æ‰©å±•项目 [扩展项目列表](https://javalionli.gitee.io/plus-doc/#/ruoyi-vue-plus/extend-project/list)
* ç”¨æˆ·æ‰©å±•项目 [扩展项目列表](https://plus-doc.dromara.org/#/ruoyi-vue-plus/extend-project/list)
## åŠ ç¾¤ä¸ŽæçŒ®
>[加群与捐献](https://javalionli.gitee.io/plus-doc/#/ruoyi-vue-plus/other/group_chat)
>>[https://javalionli.gitee.io/plus-doc/#/ruoyi-vue-plus/other/group_chat](https://javalionli.gitee.io/plus-doc/#/ruoyi-vue-plus/other/group_chat)
>[加群与捐献](https://plus-doc.dromara.org/#/ruoyi-vue-plus/other/group_chat)
>>[https://plus-doc.dromara.org/#/ruoyi-vue-plus/other/group_chat](https://plus-doc.dromara.org/#/ruoyi-vue-plus/other/group_chat)
## æçŒ®ä½œè€…
作者为兼职做开源,平时还需要工作,如果帮到了您可以请作者吃个盒饭  
<img src="https://images.gitee.com/uploads/images/2022/0218/213734_b1b8197f_1766278.jpeg" width="300px" height="450px" />
<img src="https://images.gitee.com/uploads/images/2021/0525/101713_3d18b119_1766278.jpeg" width="300px" height="450px" />
<img src="https://foruda.gitee.com/images/1678975784848381069/d8661ed9_1766278.png" width="300px" height="450px" />
<img src="https://foruda.gitee.com/images/1678975801230205215/6f96229d_1766278.png" width="300px" height="450px" />
## æ¼”示图例
pom.xml
@@ -4,7 +4,7 @@
         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>
    <groupId>org.dromara</groupId>
    <artifactId>ruoyi-vue-plus</artifactId>
    <version>${revision}</version>
@@ -113,7 +113,7 @@
            <!-- common çš„依赖配置-->
            <dependency>
                <groupId>com.ruoyi</groupId>
                <groupId>org.dromara</groupId>
                <artifactId>ruoyi-common-bom</artifactId>
                <version>${revision}</version>
                <type>pom</type>
@@ -312,25 +312,25 @@
            </dependency>
            <dependency>
                <groupId>com.ruoyi</groupId>
                <groupId>org.dromara</groupId>
                <artifactId>ruoyi-system</artifactId>
                <version>${revision}</version>
            </dependency>
            <dependency>
                <groupId>com.ruoyi</groupId>
                <groupId>org.dromara</groupId>
                <artifactId>ruoyi-job</artifactId>
                <version>${revision}</version>
            </dependency>
            <dependency>
                <groupId>com.ruoyi</groupId>
                <groupId>org.dromara</groupId>
                <artifactId>ruoyi-generator</artifactId>
                <version>${revision}</version>
            </dependency>
            <dependency>
                <groupId>com.ruoyi</groupId>
                <groupId>org.dromara</groupId>
                <artifactId>ruoyi-demo</artifactId>
                <version>${revision}</version>
            </dependency>
ruoyi-admin/pom.xml
@@ -4,7 +4,7 @@
         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>
        <groupId>org.dromara</groupId>
        <version>${revision}</version>
        <relativePath>../pom.xml</relativePath>
    </parent>
@@ -40,29 +40,29 @@
        </dependency>
        <dependency>
            <groupId>com.ruoyi</groupId>
            <groupId>org.dromara</groupId>
            <artifactId>ruoyi-common-doc</artifactId>
        </dependency>
        <dependency>
            <groupId>com.ruoyi</groupId>
            <groupId>org.dromara</groupId>
            <artifactId>ruoyi-system</artifactId>
        </dependency>
        <dependency>
            <groupId>com.ruoyi</groupId>
            <groupId>org.dromara</groupId>
            <artifactId>ruoyi-job</artifactId>
        </dependency>
        <!-- ä»£ç ç”Ÿæˆ-->
        <dependency>
            <groupId>com.ruoyi</groupId>
            <groupId>org.dromara</groupId>
            <artifactId>ruoyi-generator</artifactId>
        </dependency>
        <!--  demo模块  -->
        <dependency>
            <groupId>com.ruoyi</groupId>
            <groupId>org.dromara</groupId>
            <artifactId>ruoyi-demo</artifactId>
        </dependency>
ruoyi-admin/src/main/java/com/ruoyi/RuoYiApplication.java
ÎļþÒÑɾ³ý
ruoyi-admin/src/main/java/com/ruoyi/RuoYiServletInitializer.java
ÎļþÒÑɾ³ý
ruoyi-admin/src/main/java/com/ruoyi/web/controller/AuthController.java
ÎļþÒÑɾ³ý
ruoyi-admin/src/main/java/com/ruoyi/web/controller/CaptchaController.java
ÎļþÒÑɾ³ý
ruoyi-admin/src/main/java/com/ruoyi/web/controller/IndexController.java
ÎļþÒÑɾ³ý
ruoyi-admin/src/main/java/com/ruoyi/web/domain/vo/CaptchaVo.java
ÎļþÒÑɾ³ý
ruoyi-admin/src/main/java/com/ruoyi/web/domain/vo/LoginTenantVo.java
ÎļþÒÑɾ³ý
ruoyi-admin/src/main/java/com/ruoyi/web/domain/vo/LoginVo.java
ÎļþÒÑɾ³ý
ruoyi-admin/src/main/java/com/ruoyi/web/domain/vo/TenantListVo.java
ÎļþÒÑɾ³ý
ruoyi-admin/src/main/java/com/ruoyi/web/service/SysLoginService.java
ÎļþÒÑɾ³ý
ruoyi-admin/src/main/java/com/ruoyi/web/service/SysRegisterService.java
ÎļþÒÑɾ³ý
ruoyi-admin/src/main/java/org/dromara/DromaraApplication.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,23 @@
package org.dromara;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.metrics.buffering.BufferingApplicationStartup;
/**
 * å¯åŠ¨ç¨‹åº
 *
 * @author Lion Li
 */
@SpringBootApplication
public class DromaraApplication {
    public static void main(String[] args) {
        SpringApplication application = new SpringApplication(DromaraApplication.class);
        application.setApplicationStartup(new BufferingApplicationStartup(2048));
        application.run(args);
        System.out.println("(♥◠‿◠)ノ゙  RuoYi-Vue-Plus启动成功   áƒš(´ڡ`ლ)゙");
    }
}
ruoyi-admin/src/main/java/org/dromara/DromaraServletInitializer.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,18 @@
package org.dromara;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
/**
 * web容器中进行部署
 *
 * @author Lion Li
 */
public class DromaraServletInitializer extends SpringBootServletInitializer {
    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
        return application.sources(DromaraApplication.class);
    }
}
ruoyi-admin/src/main/java/org/dromara/web/controller/AuthController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,153 @@
package org.dromara.web.controller;
import cn.dev33.satoken.annotation.SaIgnore;
import cn.hutool.core.collection.CollUtil;
import org.dromara.common.core.domain.R;
import org.dromara.common.core.domain.model.EmailLoginBody;
import org.dromara.common.core.domain.model.LoginBody;
import org.dromara.common.core.domain.model.RegisterBody;
import org.dromara.common.core.domain.model.SmsLoginBody;
import org.dromara.common.core.utils.MapstructUtils;
import org.dromara.common.core.utils.StreamUtils;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.tenant.helper.TenantHelper;
import org.dromara.system.domain.bo.SysTenantBo;
import org.dromara.system.domain.vo.SysTenantVo;
import org.dromara.system.service.ISysConfigService;
import org.dromara.system.service.ISysTenantService;
import org.dromara.web.domain.vo.LoginTenantVo;
import org.dromara.web.domain.vo.LoginVo;
import org.dromara.web.domain.vo.TenantListVo;
import org.dromara.web.service.SysLoginService;
import org.dromara.web.service.SysRegisterService;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.validation.constraints.NotBlank;
import lombok.RequiredArgsConstructor;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.net.URL;
import java.util.List;
/**
 * è®¤è¯
 *
 * @author Lion Li
 */
@SaIgnore
@Validated
@RequiredArgsConstructor
@RestController
@RequestMapping("/auth")
public class AuthController {
    private final SysLoginService loginService;
    private final SysRegisterService registerService;
    private final ISysConfigService configService;
    private final ISysTenantService tenantService;
    /**
     * ç™»å½•方法
     *
     * @param body ç™»å½•信息
     * @return ç»“æžœ
     */
    @PostMapping("/login")
    public R<LoginVo> login(@Validated @RequestBody LoginBody body) {
        LoginVo loginVo = new LoginVo();
        // ç”Ÿæˆä»¤ç‰Œ
        String token = loginService.login(
                body.getTenantId(),
                body.getUsername(), body.getPassword(),
                body.getCode(), body.getUuid());
        loginVo.setToken(token);
        return R.ok(loginVo);
    }
    /**
     * çŸ­ä¿¡ç™»å½•
     *
     * @param body ç™»å½•信息
     * @return ç»“æžœ
     */
    @PostMapping("/smsLogin")
    public R<LoginVo> smsLogin(@Validated @RequestBody SmsLoginBody body) {
        LoginVo loginVo = new LoginVo();
        // ç”Ÿæˆä»¤ç‰Œ
        String token = loginService.smsLogin(body.getTenantId(), body.getPhonenumber(), body.getSmsCode());
        loginVo.setToken(token);
        return R.ok(loginVo);
    }
    /**
     * é‚®ä»¶ç™»å½•
     *
     * @param body ç™»å½•信息
     * @return ç»“æžœ
     */
    @PostMapping("/emailLogin")
    public R<LoginVo> emailLogin(@Validated @RequestBody EmailLoginBody body) {
        LoginVo loginVo = new LoginVo();
        // ç”Ÿæˆä»¤ç‰Œ
        String token = loginService.emailLogin(body.getTenantId(), body.getEmail(), body.getEmailCode());
        loginVo.setToken(token);
        return R.ok(loginVo);
    }
    /**
     * å°ç¨‹åºç™»å½•(示例)
     *
     * @param xcxCode å°ç¨‹åºcode
     * @return ç»“æžœ
     */
    @PostMapping("/xcxLogin")
    public R<LoginVo> xcxLogin(@NotBlank(message = "{xcx.code.not.blank}") String xcxCode) {
        LoginVo loginVo = new LoginVo();
        // ç”Ÿæˆä»¤ç‰Œ
        String token = loginService.xcxLogin(xcxCode);
        loginVo.setToken(token);
        return R.ok(loginVo);
    }
    /**
     * é€€å‡ºç™»å½•
     */
    @PostMapping("/logout")
    public R<Void> logout() {
        loginService.logout();
        return R.ok("退出成功");
    }
    /**
     * ç”¨æˆ·æ³¨å†Œ
     */
    @PostMapping("/register")
    public R<Void> register(@Validated @RequestBody RegisterBody user) {
        if (!configService.selectRegisterEnabled(user.getTenantId())) {
            return R.fail("当前系统没有开启注册功能!");
        }
        registerService.register(user);
        return R.ok();
    }
    /**
     * ç™»å½•页面租户下拉框
     *
     * @return ç§Ÿæˆ·åˆ—表
     */
    @GetMapping("/tenant/list")
    public R<LoginTenantVo> tenantList(HttpServletRequest request) throws Exception {
        List<SysTenantVo> tenantList = tenantService.queryList(new SysTenantBo());
        List<TenantListVo> voList = MapstructUtils.convert(tenantList, TenantListVo.class);
        // èŽ·å–åŸŸå
        String host = new URL(request.getRequestURL().toString()).getHost();
        // æ ¹æ®åŸŸåè¿›è¡Œç­›é€‰
        List<TenantListVo> list = StreamUtils.filter(voList, vo -> StringUtils.equals(vo.getDomain(), host));
        // è¿”回对象
        LoginTenantVo vo = new LoginTenantVo();
        vo.setVoList(CollUtil.isNotEmpty(list) ? list : voList);
        vo.setTenantEnabled(TenantHelper.isEnable());
        return R.ok(vo);
    }
}
ruoyi-admin/src/main/java/org/dromara/web/controller/CaptchaController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,135 @@
package org.dromara.web.controller;
import cn.dev33.satoken.annotation.SaIgnore;
import cn.hutool.captcha.AbstractCaptcha;
import cn.hutool.captcha.generator.CodeGenerator;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.RandomUtil;
import org.dromara.common.core.constant.Constants;
import org.dromara.common.core.constant.GlobalConstants;
import org.dromara.common.core.domain.R;
import org.dromara.common.core.utils.SpringUtils;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.core.utils.reflect.ReflectUtils;
import org.dromara.common.mail.config.properties.MailProperties;
import org.dromara.common.mail.utils.MailUtils;
import org.dromara.common.redis.utils.RedisUtils;
import org.dromara.common.sms.config.properties.SmsProperties;
import org.dromara.common.sms.core.SmsTemplate;
import org.dromara.common.sms.entity.SmsResult;
import org.dromara.common.web.config.properties.CaptchaProperties;
import org.dromara.common.web.enums.CaptchaType;
import org.dromara.web.domain.vo.CaptchaVo;
import jakarta.validation.constraints.NotBlank;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.time.Duration;
import java.util.HashMap;
import java.util.Map;
/**
 * éªŒè¯ç æ“ä½œå¤„理
 *
 * @author Lion Li
 */
@SaIgnore
@Slf4j
@Validated
@RequiredArgsConstructor
@RestController
public class CaptchaController {
    private final CaptchaProperties captchaProperties;
    private final SmsProperties smsProperties;
    private final MailProperties mailProperties;
    /**
     * çŸ­ä¿¡éªŒè¯ç 
     *
     * @param phonenumber ç”¨æˆ·æ‰‹æœºå·
     */
    @GetMapping("/sms/code")
    public R<Void> smsCode(@NotBlank(message = "{user.phonenumber.not.blank}") String phonenumber) {
        if (!smsProperties.getEnabled()) {
            return R.fail("当前系统没有开启短信功能!");
        }
        String key = GlobalConstants.CAPTCHA_CODE_KEY + phonenumber;
        String code = RandomUtil.randomNumbers(4);
        RedisUtils.setCacheObject(key, code, Duration.ofMinutes(Constants.CAPTCHA_EXPIRATION));
        // éªŒè¯ç æ¨¡æ¿id è‡ªè¡Œå¤„理 (查数据库或写死均可)
        String templateId = "";
        Map<String, String> map = new HashMap<>(1);
        map.put("code", code);
        SmsTemplate smsTemplate = SpringUtils.getBean(SmsTemplate.class);
        SmsResult result = smsTemplate.send(phonenumber, templateId, map);
        if (!result.isSuccess()) {
            log.error("验证码短信发送异常 => {}", result);
            return R.fail(result.getMessage());
        }
        return R.ok();
    }
    /**
     * é‚®ç®±éªŒè¯ç 
     *
     * @param email é‚®ç®±
     */
    @GetMapping("/email/code")
    public R<Void> emailCode(@NotBlank(message = "{user.email.not.blank}") String email) {
        if (!mailProperties.getEnabled()) {
            return R.fail("当前系统没有开启邮箱功能!");
        }
        String key = GlobalConstants.CAPTCHA_CODE_KEY + email;
        String code = RandomUtil.randomNumbers(4);
        RedisUtils.setCacheObject(key, code, Duration.ofMinutes(Constants.CAPTCHA_EXPIRATION));
        try {
            MailUtils.sendText(email, "登录验证码", "您本次验证码为:" + code + ",有效性为" + Constants.CAPTCHA_EXPIRATION + "分钟,请尽快填写。");
        } catch (Exception e) {
            log.error("验证码短信发送异常 => {}", e.getMessage());
            return R.fail(e.getMessage());
        }
        return R.ok();
    }
    /**
     * ç”ŸæˆéªŒè¯ç 
     */
    @GetMapping("/code")
    public R<CaptchaVo> getCode() {
        CaptchaVo captchaVo = new CaptchaVo();
        boolean captchaEnabled = captchaProperties.getEnable();
        if (!captchaEnabled) {
            captchaVo.setCaptchaEnabled(false);
            return R.ok(captchaVo);
        }
        // ä¿å­˜éªŒè¯ç ä¿¡æ¯
        String uuid = IdUtil.simpleUUID();
        String verifyKey = GlobalConstants.CAPTCHA_CODE_KEY + uuid;
        // ç”ŸæˆéªŒè¯ç 
        CaptchaType captchaType = captchaProperties.getType();
        boolean isMath = CaptchaType.MATH == captchaType;
        Integer length = isMath ? captchaProperties.getNumberLength() : captchaProperties.getCharLength();
        CodeGenerator codeGenerator = ReflectUtils.newInstance(captchaType.getClazz(), length);
        AbstractCaptcha captcha = SpringUtils.getBean(captchaProperties.getCategory().getClazz());
        captcha.setGenerator(codeGenerator);
        captcha.createCode();
        String code = captcha.getCode();
        if (isMath) {
            ExpressionParser parser = new SpelExpressionParser();
            Expression exp = parser.parseExpression(StringUtils.remove(code, "="));
            code = exp.getValue(String.class);
        }
        RedisUtils.setCacheObject(verifyKey, code, Duration.ofMinutes(Constants.CAPTCHA_EXPIRATION));
        captchaVo.setUuid(uuid);
        captchaVo.setImg(captcha.getImageBase64());
        return R.ok(captchaVo);
    }
}
ruoyi-admin/src/main/java/org/dromara/web/controller/IndexController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,32 @@
package org.dromara.web.controller;
import cn.dev33.satoken.annotation.SaIgnore;
import org.dromara.common.core.config.RuoYiConfig;
import org.dromara.common.core.utils.StringUtils;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
/**
 * é¦–页
 *
 * @author Lion Li
 */
@SaIgnore
@RequiredArgsConstructor
@RestController
public class IndexController {
    /**
     * ç³»ç»ŸåŸºç¡€é…ç½®
     */
    private final RuoYiConfig ruoyiConfig;
    /**
     * è®¿é—®é¦–页,提示语
     */
    @GetMapping("/")
    public String index() {
        return StringUtils.format("欢迎使用{}后台管理框架,当前版本:v{},请通过前端地址访问。", ruoyiConfig.getName(), ruoyiConfig.getVersion());
    }
}
ruoyi-admin/src/main/java/org/dromara/web/domain/vo/CaptchaVo.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,25 @@
package org.dromara.web.domain.vo;
import lombok.Data;
/**
 * éªŒè¯ç ä¿¡æ¯
 *
 * @author Michelle.Chung
 */
@Data
public class CaptchaVo {
    /**
     * æ˜¯å¦å¼€å¯éªŒè¯ç 
     */
    private Boolean captchaEnabled = true;
    private String uuid;
    /**
     * éªŒè¯ç å›¾ç‰‡
     */
    private String img;
}
ruoyi-admin/src/main/java/org/dromara/web/domain/vo/LoginTenantVo.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,25 @@
package org.dromara.web.domain.vo;
import lombok.Data;
import java.util.List;
/**
 * ç™»å½•租户对象
 *
 * @author Michelle.Chung
 */
@Data
public class LoginTenantVo {
    /**
     * ç§Ÿæˆ·å¼€å…³
     */
    private Boolean tenantEnabled;
    /**
     * ç§Ÿæˆ·å¯¹è±¡åˆ—表
     */
    private List<TenantListVo> voList;
}
ruoyi-admin/src/main/java/org/dromara/web/domain/vo/LoginVo.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,15 @@
package org.dromara.web.domain.vo;
import lombok.Data;
/**
 * ç™»å½•验证信息
 *
 * @author Michelle.Chung
 */
@Data
public class LoginVo {
    private String token;
}
ruoyi-admin/src/main/java/org/dromara/web/domain/vo/TenantListVo.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,22 @@
package org.dromara.web.domain.vo;
import org.dromara.system.domain.vo.SysTenantVo;
import io.github.linpeilie.annotations.AutoMapper;
import lombok.Data;
/**
 * ç§Ÿæˆ·åˆ—表
 *
 * @author Lion Li
 */
@Data
@AutoMapper(target = SysTenantVo.class)
public class TenantListVo {
    private String tenantId;
    private String companyName;
    private String domain;
}
ruoyi-admin/src/main/java/org/dromara/web/service/SysLoginService.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,391 @@
package org.dromara.web.service;
import cn.dev33.satoken.exception.NotLoginException;
import cn.dev33.satoken.secure.BCrypt;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import org.dromara.common.core.constant.Constants;
import org.dromara.common.core.constant.GlobalConstants;
import org.dromara.common.core.constant.TenantConstants;
import org.dromara.common.core.domain.dto.RoleDTO;
import org.dromara.common.core.domain.model.LoginUser;
import org.dromara.common.core.domain.model.XcxLoginUser;
import org.dromara.common.core.enums.DeviceType;
import org.dromara.common.core.enums.LoginType;
import org.dromara.common.core.enums.TenantStatus;
import org.dromara.common.core.enums.UserStatus;
import org.dromara.common.core.exception.user.CaptchaException;
import org.dromara.common.core.exception.user.CaptchaExpireException;
import org.dromara.common.core.exception.user.UserException;
import org.dromara.common.core.utils.*;
import org.dromara.common.log.event.LogininforEvent;
import org.dromara.common.redis.utils.RedisUtils;
import org.dromara.common.satoken.utils.LoginHelper;
import org.dromara.common.tenant.exception.TenantException;
import org.dromara.common.tenant.helper.TenantHelper;
import org.dromara.common.web.config.properties.CaptchaProperties;
import org.dromara.system.domain.SysUser;
import org.dromara.system.domain.vo.SysTenantVo;
import org.dromara.system.domain.vo.SysUserVo;
import org.dromara.system.mapper.SysUserMapper;
import org.dromara.system.service.ISysPermissionService;
import org.dromara.system.service.ISysTenantService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.dromara.common.core.utils.*;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import java.time.Duration;
import java.util.Date;
import java.util.List;
import java.util.function.Supplier;
/**
 * ç™»å½•校验方法
 *
 * @author Lion Li
 */
@RequiredArgsConstructor
@Slf4j
@Service
public class SysLoginService {
    private final SysUserMapper userMapper;
    private final CaptchaProperties captchaProperties;
    private final ISysPermissionService permissionService;
    private final ISysTenantService tenantService;
    @Value("${user.password.maxRetryCount}")
    private Integer maxRetryCount;
    @Value("${user.password.lockTime}")
    private Integer lockTime;
    /**
     * ç™»å½•验证
     *
     * @param username ç”¨æˆ·å
     * @param password å¯†ç 
     * @param code     éªŒè¯ç 
     * @param uuid     å”¯ä¸€æ ‡è¯†
     * @return ç»“æžœ
     */
    public String login(String tenantId, String username, String password, String code, String uuid) {
        boolean captchaEnabled = captchaProperties.getEnable();
        // éªŒè¯ç å¼€å…³
        if (captchaEnabled) {
            validateCaptcha(tenantId, username, code, uuid);
        }
        // æ ¡éªŒç§Ÿæˆ·
        checkTenant(tenantId);
        SysUserVo user = loadUserByUsername(tenantId, username);
        checkLogin(LoginType.PASSWORD, tenantId, username, () -> !BCrypt.checkpw(password, user.getPassword()));
        // æ­¤å¤„可根据登录用户的数据不同 è‡ªè¡Œåˆ›å»º loginUser
        LoginUser loginUser = buildLoginUser(user);
        // ç”Ÿæˆtoken
        LoginHelper.loginByDevice(loginUser, DeviceType.PC);
        recordLogininfor(loginUser.getTenantId(), username, Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success"));
        recordLoginInfo(user.getUserId());
        return StpUtil.getTokenValue();
    }
    public String smsLogin(String tenantId, String phonenumber, String smsCode) {
        // æ ¡éªŒç§Ÿæˆ·
        checkTenant(tenantId);
        // é€šè¿‡æ‰‹æœºå·æŸ¥æ‰¾ç”¨æˆ·
        SysUserVo user = loadUserByPhonenumber(tenantId, phonenumber);
        checkLogin(LoginType.SMS, tenantId, user.getUserName(), () -> !validateSmsCode(tenantId, phonenumber, smsCode));
        // æ­¤å¤„可根据登录用户的数据不同 è‡ªè¡Œåˆ›å»º loginUser
        LoginUser loginUser = buildLoginUser(user);
        // ç”Ÿæˆtoken
        LoginHelper.loginByDevice(loginUser, DeviceType.APP);
        recordLogininfor(loginUser.getTenantId(), user.getUserName(), Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success"));
        recordLoginInfo(user.getUserId());
        return StpUtil.getTokenValue();
    }
    public String emailLogin(String tenantId, String email, String emailCode) {
        // æ ¡éªŒç§Ÿæˆ·
        checkTenant(tenantId);
        // é€šè¿‡æ‰‹æœºå·æŸ¥æ‰¾ç”¨æˆ·
        SysUserVo user = loadUserByEmail(tenantId, email);
        checkLogin(LoginType.EMAIL, tenantId, user.getUserName(), () -> !validateEmailCode(tenantId, email, emailCode));
        // æ­¤å¤„可根据登录用户的数据不同 è‡ªè¡Œåˆ›å»º loginUser
        LoginUser loginUser = buildLoginUser(user);
        // ç”Ÿæˆtoken
        LoginHelper.loginByDevice(loginUser, DeviceType.APP);
        recordLogininfor(loginUser.getTenantId(), user.getUserName(), Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success"));
        recordLoginInfo(user.getUserId());
        return StpUtil.getTokenValue();
    }
    public String xcxLogin(String xcxCode) {
        // xcxCode ä¸º å°ç¨‹åºè°ƒç”¨ wx.login æŽˆæƒåŽèŽ·å–
        // todo ä»¥ä¸‹è‡ªè¡Œå®žçް
        // æ ¡éªŒ appid + appsrcret + xcxCode è°ƒç”¨ç™»å½•凭证校验接口 èŽ·å– session_key ä¸Ž openid
        String openid = "";
        SysUserVo user = loadUserByOpenid(openid);
        // æ ¡éªŒç§Ÿæˆ·
        checkTenant(user.getTenantId());
        // æ­¤å¤„可根据登录用户的数据不同 è‡ªè¡Œåˆ›å»º loginUser
        XcxLoginUser loginUser = new XcxLoginUser();
        loginUser.setTenantId(user.getTenantId());
        loginUser.setUserId(user.getUserId());
        loginUser.setUsername(user.getUserName());
        loginUser.setUserType(user.getUserType());
        loginUser.setOpenid(openid);
        // ç”Ÿæˆtoken
        LoginHelper.loginByDevice(loginUser, DeviceType.XCX);
        recordLogininfor(loginUser.getTenantId(), user.getUserName(), Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success"));
        recordLoginInfo(user.getUserId());
        return StpUtil.getTokenValue();
    }
    /**
     * é€€å‡ºç™»å½•
     */
    public void logout() {
        try {
            LoginUser loginUser = LoginHelper.getLoginUser();
            if (TenantHelper.isEnable() && LoginHelper.isSuperAdmin()) {
                // è¶…级管理员 ç™»å‡ºæ¸…除动态租户
                TenantHelper.clearDynamic();
            }
            StpUtil.logout();
            recordLogininfor(loginUser.getTenantId(), loginUser.getUsername(), Constants.LOGOUT, MessageUtils.message("user.logout.success"));
        } catch (NotLoginException ignored) {
        }
    }
    /**
     * è®°å½•登录信息
     *
     * @param tenantId ç§Ÿæˆ·ID
     * @param username ç”¨æˆ·å
     * @param status   çŠ¶æ€
     * @param message  æ¶ˆæ¯å†…容
     */
    private void recordLogininfor(String tenantId, String username, String status, String message) {
        LogininforEvent logininforEvent = new LogininforEvent();
        logininforEvent.setTenantId(tenantId);
        logininforEvent.setUsername(username);
        logininforEvent.setStatus(status);
        logininforEvent.setMessage(message);
        logininforEvent.setRequest(ServletUtils.getRequest());
        SpringUtils.context().publishEvent(logininforEvent);
    }
    /**
     * æ ¡éªŒçŸ­ä¿¡éªŒè¯ç 
     */
    private boolean validateSmsCode(String tenantId, String phonenumber, String smsCode) {
        String code = RedisUtils.getCacheObject(GlobalConstants.CAPTCHA_CODE_KEY + phonenumber);
        if (StringUtils.isBlank(code)) {
            recordLogininfor(tenantId, phonenumber, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.expire"));
            throw new CaptchaExpireException();
        }
        return code.equals(smsCode);
    }
    /**
     * æ ¡éªŒé‚®ç®±éªŒè¯ç 
     */
    private boolean validateEmailCode(String tenantId, String email, String emailCode) {
        String code = RedisUtils.getCacheObject(GlobalConstants.CAPTCHA_CODE_KEY + email);
        if (StringUtils.isBlank(code)) {
            recordLogininfor(tenantId, email, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.expire"));
            throw new CaptchaExpireException();
        }
        return code.equals(emailCode);
    }
    /**
     * æ ¡éªŒéªŒè¯ç 
     *
     * @param username ç”¨æˆ·å
     * @param code     éªŒè¯ç 
     * @param uuid     å”¯ä¸€æ ‡è¯†
     */
    public void validateCaptcha(String tenantId, String username, String code, String uuid) {
        String verifyKey = GlobalConstants.CAPTCHA_CODE_KEY + StringUtils.defaultString(uuid, "");
        String captcha = RedisUtils.getCacheObject(verifyKey);
        RedisUtils.deleteObject(verifyKey);
        if (captcha == null) {
            recordLogininfor(tenantId, username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.expire"));
            throw new CaptchaExpireException();
        }
        if (!code.equalsIgnoreCase(captcha)) {
            recordLogininfor(tenantId, username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.error"));
            throw new CaptchaException();
        }
    }
    private SysUserVo loadUserByUsername(String tenantId, String username) {
        SysUser user = userMapper.selectOne(new LambdaQueryWrapper<SysUser>()
                .select(SysUser::getUserName, SysUser::getStatus)
                .eq(TenantHelper.isEnable(), SysUser::getTenantId, tenantId)
                .eq(SysUser::getUserName, username));
        if (ObjectUtil.isNull(user)) {
            log.info("登录用户:{} ä¸å­˜åœ¨.", username);
            throw new UserException("user.not.exists", username);
        } else if (UserStatus.DISABLE.getCode().equals(user.getStatus())) {
            log.info("登录用户:{} å·²è¢«åœç”¨.", username);
            throw new UserException("user.blocked", username);
        }
        if (TenantHelper.isEnable()) {
            return userMapper.selectTenantUserByUserName(username, tenantId);
        }
        return userMapper.selectUserByUserName(username);
    }
    private SysUserVo loadUserByPhonenumber(String tenantId, String phonenumber) {
        SysUser user = userMapper.selectOne(new LambdaQueryWrapper<SysUser>()
                .select(SysUser::getPhonenumber, SysUser::getStatus)
                .eq(TenantHelper.isEnable(), SysUser::getTenantId, tenantId)
                .eq(SysUser::getPhonenumber, phonenumber));
        if (ObjectUtil.isNull(user)) {
            log.info("登录用户:{} ä¸å­˜åœ¨.", phonenumber);
            throw new UserException("user.not.exists", phonenumber);
        } else if (UserStatus.DISABLE.getCode().equals(user.getStatus())) {
            log.info("登录用户:{} å·²è¢«åœç”¨.", phonenumber);
            throw new UserException("user.blocked", phonenumber);
        }
        if (TenantHelper.isEnable()) {
            return userMapper.selectTenantUserByPhonenumber(phonenumber, tenantId);
        }
        return userMapper.selectUserByPhonenumber(phonenumber);
    }
    private SysUserVo loadUserByEmail(String tenantId, String email) {
        SysUser user = userMapper.selectOne(new LambdaQueryWrapper<SysUser>()
            .select(SysUser::getPhonenumber, SysUser::getStatus)
            .eq(TenantHelper.isEnable(), SysUser::getTenantId, tenantId)
            .eq(SysUser::getEmail, email));
        if (ObjectUtil.isNull(user)) {
            log.info("登录用户:{} ä¸å­˜åœ¨.", email);
            throw new UserException("user.not.exists", email);
        } else if (UserStatus.DISABLE.getCode().equals(user.getStatus())) {
            log.info("登录用户:{} å·²è¢«åœç”¨.", email);
            throw new UserException("user.blocked", email);
        }
        if (TenantHelper.isEnable()) {
            return userMapper.selectTenantUserByEmail(email, tenantId);
        }
        return userMapper.selectUserByEmail(email);
    }
    private SysUserVo loadUserByOpenid(String openid) {
        // ä½¿ç”¨ openid æŸ¥è¯¢ç»‘定用户 å¦‚未绑定用户 åˆ™æ ¹æ®ä¸šåŠ¡è‡ªè¡Œå¤„ç† ä¾‹å¦‚ åˆ›å»ºé»˜è®¤ç”¨æˆ·
        // todo è‡ªè¡Œå®žçް userService.selectUserByOpenid(openid);
        SysUserVo user = new SysUserVo();
        if (ObjectUtil.isNull(user)) {
            log.info("登录用户:{} ä¸å­˜åœ¨.", openid);
            // todo ç”¨æˆ·ä¸å­˜åœ¨ ä¸šåŠ¡é€»è¾‘è‡ªè¡Œå®žçŽ°
        } else if (UserStatus.DISABLE.getCode().equals(user.getStatus())) {
            log.info("登录用户:{} å·²è¢«åœç”¨.", openid);
            // todo ç”¨æˆ·å·²è¢«åœç”¨ ä¸šåŠ¡é€»è¾‘è‡ªè¡Œå®žçŽ°
        }
        return user;
    }
    /**
     * æž„建登录用户
     */
    private LoginUser buildLoginUser(SysUserVo user) {
        LoginUser loginUser = new LoginUser();
        loginUser.setTenantId(user.getTenantId());
        loginUser.setUserId(user.getUserId());
        loginUser.setDeptId(user.getDeptId());
        loginUser.setUsername(user.getUserName());
        loginUser.setUserType(user.getUserType());
        loginUser.setMenuPermission(permissionService.getMenuPermission(user.getUserId()));
        loginUser.setRolePermission(permissionService.getRolePermission(user.getUserId()));
        loginUser.setDeptName(ObjectUtil.isNull(user.getDept()) ? "" : user.getDept().getDeptName());
        List<RoleDTO> roles = BeanUtil.copyToList(user.getRoles(), RoleDTO.class);
        loginUser.setRoles(roles);
        return loginUser;
    }
    /**
     * è®°å½•登录信息
     *
     * @param userId ç”¨æˆ·ID
     */
    public void recordLoginInfo(Long userId) {
        SysUser sysUser = new SysUser();
        sysUser.setUserId(userId);
        sysUser.setLoginIp(ServletUtils.getClientIP());
        sysUser.setLoginDate(DateUtils.getNowDate());
        sysUser.setUpdateBy(userId);
        userMapper.updateById(sysUser);
    }
    /**
     * ç™»å½•校验
     */
    private void checkLogin(LoginType loginType, String tenantId, String username, Supplier<Boolean> supplier) {
        String errorKey = GlobalConstants.PWD_ERR_CNT_KEY + username;
        String loginFail = Constants.LOGIN_FAIL;
        // èŽ·å–ç”¨æˆ·ç™»å½•é”™è¯¯æ¬¡æ•°(可自定义限制策略 ä¾‹å¦‚: key + username + ip)
        Integer errorNumber = RedisUtils.getCacheObject(errorKey);
        // é”å®šæ—¶é—´å†…登录 åˆ™è¸¢å‡º
        if (ObjectUtil.isNotNull(errorNumber) && errorNumber.equals(maxRetryCount)) {
            recordLogininfor(tenantId, username, loginFail, MessageUtils.message(loginType.getRetryLimitExceed(), maxRetryCount, lockTime));
            throw new UserException(loginType.getRetryLimitExceed(), maxRetryCount, lockTime);
        }
        if (supplier.get()) {
            // æ˜¯å¦ç¬¬ä¸€æ¬¡
            errorNumber = ObjectUtil.isNull(errorNumber) ? 1 : errorNumber + 1;
            // è¾¾åˆ°è§„定错误次数 åˆ™é”å®šç™»å½•
            if (errorNumber.equals(maxRetryCount)) {
                RedisUtils.setCacheObject(errorKey, errorNumber, Duration.ofMinutes(lockTime));
                recordLogininfor(tenantId, username, loginFail, MessageUtils.message(loginType.getRetryLimitExceed(), maxRetryCount, lockTime));
                throw new UserException(loginType.getRetryLimitExceed(), maxRetryCount, lockTime);
            } else {
                // æœªè¾¾åˆ°è§„定错误次数 åˆ™é€’增
                RedisUtils.setCacheObject(errorKey, errorNumber);
                recordLogininfor(tenantId, username, loginFail, MessageUtils.message(loginType.getRetryLimitCount(), errorNumber));
                throw new UserException(loginType.getRetryLimitCount(), errorNumber);
            }
        }
        // ç™»å½•成功 æ¸…空错误次数
        RedisUtils.deleteObject(errorKey);
    }
    private void checkTenant(String tenantId) {
        if (!TenantHelper.isEnable()) {
            return;
        }
        if (TenantConstants.DEFAULT_TENANT_ID.equals(tenantId)) {
            return;
        }
        SysTenantVo tenant = tenantService.queryByTenantId(tenantId);
        if (ObjectUtil.isNull(tenant)) {
            log.info("登录租户:{} ä¸å­˜åœ¨.", tenantId);
            throw new TenantException("tenant.not.exists");
        } else if (TenantStatus.DISABLE.getCode().equals(tenant.getStatus())) {
            log.info("登录租户:{} å·²è¢«åœç”¨.", tenantId);
            throw new TenantException("tenant.blocked");
        } else if (ObjectUtil.isNotNull(tenant.getExpireTime())
                && new Date().after(tenant.getExpireTime())) {
            log.info("登录租户:{} å·²è¶…过有效期.", tenantId);
            throw new TenantException("tenant.expired");
        }
    }
}
ruoyi-admin/src/main/java/org/dromara/web/service/SysRegisterService.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,106 @@
package org.dromara.web.service;
import cn.dev33.satoken.secure.BCrypt;
import org.dromara.common.core.constant.Constants;
import org.dromara.common.core.constant.GlobalConstants;
import org.dromara.common.core.domain.model.RegisterBody;
import org.dromara.common.core.enums.UserType;
import org.dromara.common.core.exception.user.CaptchaException;
import org.dromara.common.core.exception.user.CaptchaExpireException;
import org.dromara.common.core.exception.user.UserException;
import org.dromara.common.core.utils.MessageUtils;
import org.dromara.common.core.utils.ServletUtils;
import org.dromara.common.core.utils.SpringUtils;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.log.event.LogininforEvent;
import org.dromara.common.redis.utils.RedisUtils;
import org.dromara.common.web.config.properties.CaptchaProperties;
import org.dromara.system.domain.bo.SysUserBo;
import org.dromara.system.service.ISysUserService;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
/**
 * æ³¨å†Œæ ¡éªŒæ–¹æ³•
 *
 * @author Lion Li
 */
@RequiredArgsConstructor
@Service
public class SysRegisterService {
    private final ISysUserService userService;
    private final CaptchaProperties captchaProperties;
    /**
     * æ³¨å†Œ
     */
    public void register(RegisterBody registerBody) {
        String tenantId = registerBody.getTenantId();
        String username = registerBody.getUsername();
        String password = registerBody.getPassword();
        // æ ¡éªŒç”¨æˆ·ç±»åž‹æ˜¯å¦å­˜åœ¨
        String userType = UserType.getUserType(registerBody.getUserType()).getUserType();
        boolean captchaEnabled = captchaProperties.getEnable();
        // éªŒè¯ç å¼€å…³
        if (captchaEnabled) {
            validateCaptcha(tenantId, username, registerBody.getCode(), registerBody.getUuid());
        }
        SysUserBo sysUser = new SysUserBo();
        sysUser.setUserName(username);
        sysUser.setNickName(username);
        sysUser.setPassword(BCrypt.hashpw(password));
        sysUser.setUserType(userType);
        if (!userService.checkUserNameUnique(sysUser)) {
            throw new UserException("user.register.save.error", username);
        }
        boolean regFlag = userService.registerUser(sysUser, tenantId);
        if (!regFlag) {
            throw new UserException("user.register.error");
        }
        recordLogininfor(tenantId, username, Constants.REGISTER, MessageUtils.message("user.register.success"));
    }
    /**
     * æ ¡éªŒéªŒè¯ç 
     *
     * @param username ç”¨æˆ·å
     * @param code     éªŒè¯ç 
     * @param uuid     å”¯ä¸€æ ‡è¯†
     */
    public void validateCaptcha(String tenantId, String username, String code, String uuid) {
        String verifyKey = GlobalConstants.CAPTCHA_CODE_KEY + StringUtils.defaultString(uuid, "");
        String captcha = RedisUtils.getCacheObject(verifyKey);
        RedisUtils.deleteObject(verifyKey);
        if (captcha == null) {
            recordLogininfor(tenantId, username, Constants.REGISTER, MessageUtils.message("user.jcaptcha.expire"));
            throw new CaptchaExpireException();
        }
        if (!code.equalsIgnoreCase(captcha)) {
            recordLogininfor(tenantId, username, Constants.REGISTER, MessageUtils.message("user.jcaptcha.error"));
            throw new CaptchaException();
        }
    }
    /**
     * è®°å½•登录信息
     *
     * @param tenantId ç§Ÿæˆ·ID
     * @param username ç”¨æˆ·å
     * @param status   çŠ¶æ€
     * @param message  æ¶ˆæ¯å†…容
     * @return
     */
    private void recordLogininfor(String tenantId, String username, String status, String message) {
        LogininforEvent logininforEvent = new LogininforEvent();
        logininforEvent.setTenantId(tenantId);
        logininforEvent.setUsername(username);
        logininforEvent.setStatus(status);
        logininforEvent.setMessage(message);
        logininforEvent.setRequest(ServletUtils.getRequest());
        SpringUtils.context().publishEvent(logininforEvent);
    }
}
ruoyi-admin/src/main/resources/application.yml
@@ -5,7 +5,7 @@
  # ç‰ˆæœ¬
  version: ${revision}
  # ç‰ˆæƒå¹´ä»½
  copyrightYear: 2022
  copyrightYear: 2023
  # å®žä¾‹æ¼”示开关
  demoEnabled: true
  # èŽ·å–ip地址开关
@@ -48,7 +48,7 @@
# æ—¥å¿—配置
logging:
  level:
    com.ruoyi: @logging.level@
    org.dromara: @logging.level@
    org.springframework: warn
  config: classpath:logback.xml
@@ -151,11 +151,11 @@
mybatis-plus:
  # ä¸æ”¯æŒå¤šåŒ…, å¦‚有需要可在注解配置 æˆ– æå‡æ‰«åŒ…等级
  # ä¾‹å¦‚ com.**.**.mapper
  mapperPackage: com.ruoyi.**.mapper
  mapperPackage: org.dromara.**.mapper
  # å¯¹åº”çš„ XML æ–‡ä»¶ä½ç½®
  mapperLocations: classpath*:mapper/**/*Mapper.xml
  # å®žä½“扫描,多个package用逗号或者分号分隔
  typeAliasesPackage: com.ruoyi.**.domain
  typeAliasesPackage: org.dromara.**.domain
  # å¯åŠ¨æ—¶æ˜¯å¦æ£€æŸ¥ MyBatis XML æ–‡ä»¶çš„存在,默认不检查
  checkConfigLocation: false
  configuration:
@@ -212,7 +212,7 @@
    # æè¿°
    description: '描述:用于管理集团旗下公司的人员信息,具体包括XXX,XXX模块...'
    # ç‰ˆæœ¬
    version: '版本号: ${ruoyi-vue-plus.version}'
    version: '版本号: ${ruoyi.version}'
    # ä½œè€…信息
    contact:
      name: Lion Li
@@ -236,13 +236,13 @@
  #这里定义了两个分组,可定义多个,也可以不定义
  group-configs:
    - group: 1.演示模块
      packages-to-scan: com.ruoyi.demo
      packages-to-scan: org.dromara.demo
    - group: 2.通用模块
      packages-to-scan: com.ruoyi.web
      packages-to-scan: org.dromara.web
    - group: 3.系统模块
      packages-to-scan: com.ruoyi.system
      packages-to-scan: org.dromara.system
    - group: 4.代码生成模块
      packages-to-scan: com.ruoyi.generator
      packages-to-scan: org.dromara.generator
# é˜²æ­¢XSS攻击
xss:
ruoyi-admin/src/test/java/com/ruoyi/test/AssertUnitTest.java
ÎļþÒÑɾ³ý
ruoyi-admin/src/test/java/com/ruoyi/test/DemoUnitTest.java
ÎļþÒÑɾ³ý
ruoyi-admin/src/test/java/com/ruoyi/test/ParamUnitTest.java
ÎļþÒÑɾ³ý
ruoyi-admin/src/test/java/com/ruoyi/test/TagUnitTest.java
ÎļþÒÑɾ³ý
ruoyi-admin/src/test/java/org/dromara/test/AssertUnitTest.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,45 @@
package org.dromara.test;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
/**
 * æ–­è¨€å•元测试案例
 *
 * @author Lion Li
 */
@DisplayName("断言单元测试案例")
public class AssertUnitTest {
    @DisplayName("测试 assertEquals æ–¹æ³•")
    @Test
    public void testAssertEquals() {
        Assertions.assertEquals("666", new String("666"));
        Assertions.assertNotEquals("666", new String("666"));
    }
    @DisplayName("测试 assertSame æ–¹æ³•")
    @Test
    public void testAssertSame() {
        Object obj = new Object();
        Object obj1 = obj;
        Assertions.assertSame(obj, obj1);
        Assertions.assertNotSame(obj, obj1);
    }
    @DisplayName("测试 assertTrue æ–¹æ³•")
    @Test
    public void testAssertTrue() {
        Assertions.assertTrue(true);
        Assertions.assertFalse(true);
    }
    @DisplayName("测试 assertNull æ–¹æ³•")
    @Test
    public void testAssertNull() {
        Assertions.assertNull(null);
        Assertions.assertNotNull(null);
    }
}
ruoyi-admin/src/test/java/org/dromara/test/DemoUnitTest.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,70 @@
package org.dromara.test;
import org.dromara.common.core.config.RuoYiConfig;
import org.junit.jupiter.api.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.concurrent.TimeUnit;
/**
 * å•元测试案例
 *
 * @author Lion Li
 */
@SpringBootTest // æ­¤æ³¨è§£åªèƒ½åœ¨ springboot ä¸»åŒ…下使用 éœ€åŒ…含 main æ–¹æ³•与 yml é…ç½®æ–‡ä»¶
@DisplayName("单元测试案例")
public class DemoUnitTest {
    @Autowired
    private RuoYiConfig ruoYiConfig;
    @DisplayName("测试 @SpringBootTest @Test @DisplayName æ³¨è§£")
    @Test
    public void testTest() {
        System.out.println(ruoYiConfig);
    }
    @Disabled
    @DisplayName("测试 @Disabled æ³¨è§£")
    @Test
    public void testDisabled() {
        System.out.println(ruoYiConfig);
    }
    @Timeout(value = 2L, unit = TimeUnit.SECONDS)
    @DisplayName("测试 @Timeout æ³¨è§£")
    @Test
    public void testTimeout() throws InterruptedException {
        Thread.sleep(3000);
        System.out.println(ruoYiConfig);
    }
    @DisplayName("测试 @RepeatedTest æ³¨è§£")
    @RepeatedTest(3)
    public void testRepeatedTest() {
        System.out.println(666);
    }
    @BeforeAll
    public static void testBeforeAll() {
        System.out.println("@BeforeAll ==================");
    }
    @BeforeEach
    public void testBeforeEach() {
        System.out.println("@BeforeEach ==================");
    }
    @AfterEach
    public void testAfterEach() {
        System.out.println("@AfterEach ==================");
    }
    @AfterAll
    public static void testAfterAll() {
        System.out.println("@AfterAll ==================");
    }
}
ruoyi-admin/src/test/java/org/dromara/test/ParamUnitTest.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,72 @@
package org.dromara.test;
import org.dromara.common.core.enums.UserType;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.EnumSource;
import org.junit.jupiter.params.provider.MethodSource;
import org.junit.jupiter.params.provider.NullSource;
import org.junit.jupiter.params.provider.ValueSource;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Stream;
/**
 * å¸¦å‚数单元测试案例
 *
 * @author Lion Li
 */
@DisplayName("带参数单元测试案例")
public class ParamUnitTest {
    @DisplayName("测试 @ValueSource æ³¨è§£")
    @ParameterizedTest
    @ValueSource(strings = {"t1", "t2", "t3"})
    public void testValueSource(String str) {
        System.out.println(str);
    }
    @DisplayName("测试 @NullSource æ³¨è§£")
    @ParameterizedTest
    @NullSource
    public void testNullSource(String str) {
        System.out.println(str);
    }
    @DisplayName("测试 @EnumSource æ³¨è§£")
    @ParameterizedTest
    @EnumSource(UserType.class)
    public void testEnumSource(UserType type) {
        System.out.println(type.getUserType());
    }
    @DisplayName("测试 @MethodSource æ³¨è§£")
    @ParameterizedTest
    @MethodSource("getParam")
    public void testMethodSource(String str) {
        System.out.println(str);
    }
    public static Stream<String> getParam() {
        List<String> list = new ArrayList<>();
        list.add("t1");
        list.add("t2");
        list.add("t3");
        return list.stream();
    }
    @BeforeEach
    public void testBeforeEach() {
        System.out.println("@BeforeEach ==================");
    }
    @AfterEach
    public void testAfterEach() {
        System.out.println("@AfterEach ==================");
    }
}
ruoyi-admin/src/test/java/org/dromara/test/TagUnitTest.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,54 @@
package org.dromara.test;
import org.junit.jupiter.api.*;
import org.springframework.boot.test.context.SpringBootTest;
/**
 * æ ‡ç­¾å•元测试案例
 *
 * @author Lion Li
 */
@SpringBootTest
@DisplayName("标签单元测试案例")
public class TagUnitTest {
    @Tag("dev")
    @DisplayName("测试 @Tag dev")
    @Test
    public void testTagDev() {
        System.out.println("dev");
    }
    @Tag("prod")
    @DisplayName("测试 @Tag prod")
    @Test
    public void testTagProd() {
        System.out.println("prod");
    }
    @Tag("local")
    @DisplayName("测试 @Tag local")
    @Test
    public void testTagLocal() {
        System.out.println("local");
    }
    @Tag("exclude")
    @DisplayName("测试 @Tag exclude")
    @Test
    public void testTagExclude() {
        System.out.println("exclude");
    }
    @BeforeEach
    public void testBeforeEach() {
        System.out.println("@BeforeEach ==================");
    }
    @AfterEach
    public void testAfterEach() {
        System.out.println("@AfterEach ==================");
    }
}
ruoyi-common/pom.xml
@@ -4,7 +4,7 @@
         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>
        <groupId>org.dromara</groupId>
        <version>${revision}</version>
        <relativePath>../pom.xml</relativePath>
    </parent>
ruoyi-common/ruoyi-common-bom/pom.xml
@@ -4,7 +4,7 @@
         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>
    <groupId>org.dromara</groupId>
    <artifactId>ruoyi-common-bom</artifactId>
    <version>${revision}</version>
    <packaging>pom</packaging>
@@ -21,147 +21,147 @@
        <dependencies>
            <!-- æ ¸å¿ƒæ¨¡å— -->
            <dependency>
                <groupId>com.ruoyi</groupId>
                <groupId>org.dromara</groupId>
                <artifactId>ruoyi-common-core</artifactId>
                <version>${revision}</version>
            </dependency>
            <!-- æŽ¥å£æ¨¡å— -->
            <dependency>
                <groupId>com.ruoyi</groupId>
                <groupId>org.dromara</groupId>
                <artifactId>ruoyi-common-doc</artifactId>
                <version>${revision}</version>
            </dependency>
            <!-- excel -->
            <dependency>
                <groupId>com.ruoyi</groupId>
                <groupId>org.dromara</groupId>
                <artifactId>ruoyi-common-excel</artifactId>
                <version>${revision}</version>
            </dependency>
            <!-- å¹‚ç­‰ -->
            <dependency>
                <groupId>com.ruoyi</groupId>
                <groupId>org.dromara</groupId>
                <artifactId>ruoyi-common-idempotent</artifactId>
                <version>${revision}</version>
            </dependency>
            <!-- è°ƒåº¦æ¨¡å— -->
            <dependency>
                <groupId>com.ruoyi</groupId>
                <groupId>org.dromara</groupId>
                <artifactId>ruoyi-common-job</artifactId>
                <version>${revision}</version>
            </dependency>
            <!-- æ—¥å¿—记录 -->
            <dependency>
                <groupId>com.ruoyi</groupId>
                <groupId>org.dromara</groupId>
                <artifactId>ruoyi-common-log</artifactId>
                <version>${revision}</version>
            </dependency>
            <!-- é‚®ä»¶æœåŠ¡ -->
            <dependency>
                <groupId>com.ruoyi</groupId>
                <groupId>org.dromara</groupId>
                <artifactId>ruoyi-common-mail</artifactId>
                <version>${revision}</version>
            </dependency>
            <!-- æ•°æ®åº“服务 -->
            <dependency>
                <groupId>com.ruoyi</groupId>
                <groupId>org.dromara</groupId>
                <artifactId>ruoyi-common-mybatis</artifactId>
                <version>${revision}</version>
            </dependency>
            <!-- OSS -->
            <dependency>
                <groupId>com.ruoyi</groupId>
                <groupId>org.dromara</groupId>
                <artifactId>ruoyi-common-oss</artifactId>
                <version>${revision}</version>
            </dependency>
            <!-- é™æµ -->
            <dependency>
                <groupId>com.ruoyi</groupId>
                <groupId>org.dromara</groupId>
                <artifactId>ruoyi-common-ratelimiter</artifactId>
                <version>${revision}</version>
            </dependency>
            <!-- ç¼“存服务 -->
            <dependency>
                <groupId>com.ruoyi</groupId>
                <groupId>org.dromara</groupId>
                <artifactId>ruoyi-common-redis</artifactId>
                <version>${revision}</version>
            </dependency>
            <!-- satoken -->
            <dependency>
                <groupId>com.ruoyi</groupId>
                <groupId>org.dromara</groupId>
                <artifactId>ruoyi-common-satoken</artifactId>
                <version>${revision}</version>
            </dependency>
            <!-- å®‰å…¨æ¨¡å— -->
            <dependency>
                <groupId>com.ruoyi</groupId>
                <groupId>org.dromara</groupId>
                <artifactId>ruoyi-common-security</artifactId>
                <version>${revision}</version>
            </dependency>
            <!-- çŸ­ä¿¡æ¨¡å— -->
            <dependency>
                <groupId>com.ruoyi</groupId>
                <groupId>org.dromara</groupId>
                <artifactId>ruoyi-common-sms</artifactId>
                <version>${revision}</version>
            </dependency>
            <!-- web服务 -->
            <dependency>
                <groupId>com.ruoyi</groupId>
                <groupId>org.dromara</groupId>
                <artifactId>ruoyi-common-web</artifactId>
                <version>${revision}</version>
            </dependency>
            <!-- ç¿»è¯‘模块 -->
            <dependency>
                <groupId>com.ruoyi</groupId>
                <groupId>org.dromara</groupId>
                <artifactId>ruoyi-common-translation</artifactId>
                <version>${revision}</version>
            </dependency>
            <!-- è„±æ•æ¨¡å— -->
            <dependency>
                <groupId>com.ruoyi</groupId>
                <groupId>org.dromara</groupId>
                <artifactId>ruoyi-common-sensitive</artifactId>
                <version>${revision}</version>
            </dependency>
            <!-- åºåˆ—化模块 -->
            <dependency>
                <groupId>com.ruoyi</groupId>
                <groupId>org.dromara</groupId>
                <artifactId>ruoyi-common-json</artifactId>
                <version>${revision}</version>
            </dependency>
            <!-- æ•°æ®åº“加解密模块 -->
            <dependency>
                <groupId>com.ruoyi</groupId>
                <groupId>org.dromara</groupId>
                <artifactId>ruoyi-common-encrypt</artifactId>
                <version>${revision}</version>
            </dependency>
            <!-- ç§Ÿæˆ·æ¨¡å— -->
            <dependency>
                <groupId>com.ruoyi</groupId>
                <groupId>org.dromara</groupId>
                <artifactId>ruoyi-common-tenant</artifactId>
                <version>${revision}</version>
            </dependency>
            <!-- WebSocket模块 -->
            <dependency>
                <groupId>com.ruoyi</groupId>
                <groupId>org.dromara</groupId>
                <artifactId>ruoyi-common-websocket</artifactId>
                <version>${revision}</version>
            </dependency>
ruoyi-common/ruoyi-common-core/pom.xml
@@ -3,7 +3,7 @@
         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>
        <groupId>org.dromara</groupId>
        <artifactId>ruoyi-common</artifactId>
        <version>${revision}</version>
        <relativePath>../pom.xml</relativePath>
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/config/ApplicationConfig.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/config/AsyncConfig.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/config/RuoYiConfig.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/config/ThreadPoolConfig.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/config/ValidatorConfig.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/config/properties/ThreadPoolProperties.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/constant/CacheConstants.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/constant/CacheNames.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/constant/Constants.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/constant/GlobalConstants.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/constant/HttpStatus.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/constant/TenantConstants.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/constant/UserConstants.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/domain/R.java
ÎļþÒÑɾ³ý
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/EmailLoginBody.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/domain/model/LoginBody.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/domain/model/LoginUser.java
ÎļþÒÑɾ³ý
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
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/enums/LoginType.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/enums/TenantStatus.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/enums/UserStatus.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/enums/UserType.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/exception/DemoModeException.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/exception/GlobalException.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/exception/ServiceException.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/exception/UtilException.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/exception/base/BaseException.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/exception/file/FileException.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/exception/file/FileNameLengthLimitExceededException.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/exception/file/FileSizeLimitExceededException.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/exception/user/CaptchaException.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/exception/user/CaptchaExpireException.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/exception/user/UserException.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/exception/user/UserPasswordNotMatchException.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/exception/user/UserPasswordRetryLimitExceedException.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/manager/ShutdownManager.java
ÎļþÒÑɾ³ý
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/DeptService.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/OssService.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/service/UserService.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/DateUtils.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/MapstructUtils.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/MessageUtils.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/ServletUtils.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/SpringUtils.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/StreamUtils.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/StringUtils.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/Threads.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/TreeBuildUtils.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/ValidatorUtils.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/file/FileUtils.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/file/MimeTypeUtils.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/ip/AddressUtils.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/ip/RegionUtils.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/reflect/ReflectUtils.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/sql/SqlUtil.java
ÎļþÒÑɾ³ý
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/xss/Xss.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/xss/XssValidator.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/config/ApplicationConfig.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,16 @@
package org.dromara.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/org/dromara/common/core/config/AsyncConfig.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,54 @@
package org.dromara.common.core.config;
import cn.hutool.core.util.ArrayUtil;
import org.dromara.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/org/dromara/common/core/config/RuoYiConfig.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,49 @@
package org.dromara.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;
    /**
     * èŽ·å–åœ°å€å¼€å…³
     */
    @Getter
    private static boolean addressEnabled;
    public void setAddressEnabled(boolean addressEnabled) {
        RuoYiConfig.addressEnabled = addressEnabled;
    }
}
ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/config/ThreadPoolConfig.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,57 @@
package org.dromara.common.core.config;
import org.dromara.common.core.config.properties.ThreadPoolProperties;
import org.dromara.common.core.utils.Threads;
import org.apache.commons.lang3.concurrent.BasicThreadFactory;
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;
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
@EnableConfigurationProperties(ThreadPoolProperties.class)
public class ThreadPoolConfig {
    /**
     * æ ¸å¿ƒçº¿ç¨‹æ•° = cpu æ ¸å¿ƒæ•° + 1
     */
    private final int core = Runtime.getRuntime().availableProcessors() + 1;
    @Bean(name = "threadPoolTaskExecutor")
    @ConditionalOnProperty(prefix = "thread-pool", name = "enabled", havingValue = "true")
    public ThreadPoolTaskExecutor threadPoolTaskExecutor(ThreadPoolProperties threadPoolProperties) {
        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/org/dromara/common/core/config/ValidatorConfig.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,39 @@
package org.dromara.common.core.config;
import jakarta.validation.Validator;
import org.hibernate.validator.HibernateValidator;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.context.MessageSource;
import org.springframework.context.annotation.Bean;
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
import java.util.Properties;
/**
 * æ ¡éªŒæ¡†æž¶é…ç½®ç±»
 *
 * @author Lion Li
 */
@AutoConfiguration
public class ValidatorConfig {
    /**
     * é…ç½®æ ¡éªŒæ¡†æž¶ å¿«é€Ÿè¿”回模式
     */
    @Bean
    public Validator validator(MessageSource messageSource) {
        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/org/dromara/common/core/config/properties/ThreadPoolProperties.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,30 @@
package org.dromara.common.core.config.properties;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
 * çº¿ç¨‹æ±  é…ç½®å±žæ€§
 *
 * @author Lion Li
 */
@Data
@ConfigurationProperties(prefix = "thread-pool")
public class ThreadPoolProperties {
    /**
     * æ˜¯å¦å¼€å¯çº¿ç¨‹æ± 
     */
    private boolean enabled;
    /**
     * é˜Ÿåˆ—最大长度
     */
    private int queueCapacity;
    /**
     * çº¿ç¨‹æ± ç»´æŠ¤çº¿ç¨‹æ‰€å…è®¸çš„空闲时间
     */
    private int keepAliveSeconds;
}
ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/constant/CacheConstants.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,25 @@
package org.dromara.common.core.constant;
/**
 * ç¼“存的key å¸¸é‡
 *
 * @author Lion Li
 */
public interface CacheConstants {
    /**
     * åœ¨çº¿ç”¨æˆ· redis key
     */
    String ONLINE_TOKEN_KEY = "online_tokens:";
    /**
     * å‚数管理 cache key
     */
    String SYS_CONFIG_KEY = "sys_config:";
    /**
     * å­—典管理 cache key
     */
    String SYS_DICT_KEY = "sys_dict:";
}
ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/constant/CacheNames.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,63 @@
package org.dromara.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";
    /**
     * ç§Ÿæˆ·
     */
    String SYS_TENANT = GlobalConstants.GLOBAL_REDIS_KEY + "sys_tenant#30d";
    /**
     * ç”¨æˆ·è´¦æˆ·
     */
    String SYS_USER_NAME = "sys_user_name#30d";
    /**
     * éƒ¨é—¨
     */
    String SYS_DEPT = "sys_dept#30d";
    /**
     * 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/org/dromara/common/core/constant/Constants.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,81 @@
package org.dromara.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";
    /**
     * é¡¶çº§éƒ¨é—¨id
     */
    Long TOP_PARENT_ID = 0L;
}
ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/constant/GlobalConstants.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,39 @@
package org.dromara.common.core.constant;
/**
 * å…¨å±€çš„key常量 (业务无关的key)
 *
 * @author Lion Li
 */
public interface GlobalConstants {
    /**
     * å…¨å±€ redis key (业务无关的key)
     */
    String GLOBAL_REDIS_KEY = "global:";
    /**
     * ç™»å½•用户 redis key
     */
    String LOGIN_TOKEN_KEY = GLOBAL_REDIS_KEY + "Authorization:login:token:";
    /**
     * éªŒè¯ç  redis key
     */
    String CAPTCHA_CODE_KEY = GLOBAL_REDIS_KEY + "captcha_codes:";
    /**
     * é˜²é‡æäº¤ redis key
     */
    String REPEAT_SUBMIT_KEY = GLOBAL_REDIS_KEY + "repeat_submit:";
    /**
     * é™æµ redis key
     */
    String RATE_LIMIT_KEY = GLOBAL_REDIS_KEY + "rate_limit:";
    /**
     * ç™»å½•账户密码错误次数 redis key
     */
    String PWD_ERR_CNT_KEY = GLOBAL_REDIS_KEY + "pwd_err_cnt:";
}
ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/constant/HttpStatus.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,93 @@
package org.dromara.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/org/dromara/common/core/constant/TenantConstants.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,45 @@
package org.dromara.common.core.constant;
/**
 * ç§Ÿæˆ·å¸¸é‡ä¿¡æ¯
 *
 * @author Lion Li
 */
public interface TenantConstants {
    /**
     * ç§Ÿæˆ·æ­£å¸¸çŠ¶æ€
     */
    String NORMAL = "0";
    /**
     * ç§Ÿæˆ·å°ç¦çŠ¶æ€
     */
    String DISABLE = "1";
    /**
     * è¶…级管理员ID
     */
    Long SUPER_ADMIN_ID = 1L;
    /**
     * è¶…级管理员角色 roleKey
     */
    String SUPER_ADMIN_ROLE_KEY = "superadmin";
    /**
     * ç§Ÿæˆ·ç®¡ç†å‘˜è§’色 roleKey
     */
    String TENANT_ADMIN_ROLE_KEY = "admin";
    /**
     * ç§Ÿæˆ·ç®¡ç†å‘˜è§’色名称
     */
    String TENANT_ADMIN_ROLE_NAME = "管理员";
    /**
     * é»˜è®¤ç§Ÿæˆ·ID
     */
    String DEFAULT_TENANT_ID = "000000";
}
ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/constant/UserConstants.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,132 @@
package org.dromara.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";
    /**
     * ç”¨æˆ·åé•¿åº¦é™åˆ¶
     */
    int USERNAME_MIN_LENGTH = 2;
    int USERNAME_MAX_LENGTH = 20;
    /**
     * å¯†ç é•¿åº¦é™åˆ¶
     */
    int PASSWORD_MIN_LENGTH = 5;
    int PASSWORD_MAX_LENGTH = 20;
    /**
     * è¶…级管理员ID
     */
    Long SUPER_ADMIN_ID = 1L;
}
ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/R.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,110 @@
package org.dromara.common.core.domain;
import org.dromara.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/org/dromara/common/core/domain/dto/RoleDTO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,38 @@
package org.dromara.common.core.domain.dto;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
/**
 * è§’色
 *
 * @author Lion Li
 */
@Data
@NoArgsConstructor
public class RoleDTO implements Serializable {
    /**
     * è§’色ID
     */
    private Long roleId;
    /**
     * è§’色名称
     */
    private String roleName;
    /**
     * è§’色权限
     */
    private String roleKey;
    /**
     * æ•°æ®èŒƒå›´ï¼ˆ1:所有数据权限;2:自定义数据权限;3:本部门数据权限;4:本部门及以下数据权限;5:仅本人数据权限)
     */
    private String dataScope;
}
ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/dto/UserOnlineDTO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,62 @@
package org.dromara.common.core.domain.dto;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serial;
import java.io.Serializable;
/**
 * å½“前在线会话
 *
 * @author ruoyi
 */
@Data
@NoArgsConstructor
public class UserOnlineDTO implements Serializable {
    @Serial
    private static final long serialVersionUID = 1L;
    /**
     * ä¼šè¯ç¼–号
     */
    private String tokenId;
    /**
     * éƒ¨é—¨åç§°
     */
    private String deptName;
    /**
     * ç”¨æˆ·åç§°
     */
    private String userName;
    /**
     * ç™»å½•IP地址
     */
    private String ipaddr;
    /**
     * ç™»å½•地址
     */
    private String loginLocation;
    /**
     * æµè§ˆå™¨ç±»åž‹
     */
    private String browser;
    /**
     * æ“ä½œç³»ç»Ÿ
     */
    private String os;
    /**
     * ç™»å½•æ—¶é—´
     */
    private Long loginTime;
}
ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/model/EmailLoginBody.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,35 @@
package org.dromara.common.core.domain.model;
import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.NotBlank;
import lombok.Data;
/**
 * çŸ­ä¿¡ç™»å½•对象
 *
 * @author Lion Li
 */
@Data
public class EmailLoginBody {
    /**
     * ç§Ÿæˆ·ID
     */
    @NotBlank(message = "{tenant.number.not.blank}")
    private String tenantId;
    /**
     * é‚®ç®±
     */
    @NotBlank(message = "{user.email.not.blank}")
    @Email(message = "{user.email.not.valid}")
    private String email;
    /**
     * é‚®ç®±code
     */
    @NotBlank(message = "{email.code.not.blank}")
    private String emailCode;
}
ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/model/LoginBody.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,48 @@
package org.dromara.common.core.domain.model;
import org.dromara.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 {
    /**
     * ç§Ÿæˆ·ID
     */
    @NotBlank(message = "{tenant.number.not.blank}")
    private String tenantId;
    /**
     * ç”¨æˆ·å
     */
    @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/org/dromara/common/core/domain/model/LoginUser.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,123 @@
package org.dromara.common.core.domain.model;
import org.dromara.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 String tenantId;
    /**
     * ç”¨æˆ·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/org/dromara/common/core/domain/model/RegisterBody.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,17 @@
package org.dromara.common.core.domain.model;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
 * ç”¨æˆ·æ³¨å†Œå¯¹è±¡
 *
 * @author Lion Li
 */
@Data
@EqualsAndHashCode(callSuper = true)
public class RegisterBody extends LoginBody {
    private String userType;
}
ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/model/SmsLoginBody.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,34 @@
package org.dromara.common.core.domain.model;
import lombok.Data;
import jakarta.validation.constraints.NotBlank;
/**
 * çŸ­ä¿¡ç™»å½•对象
 *
 * @author Lion Li
 */
@Data
public class SmsLoginBody {
    /**
     * ç§Ÿæˆ·ID
     */
    @NotBlank(message = "{tenant.number.not.blank}")
    private String tenantId;
    /**
     * æ‰‹æœºå·
     */
    @NotBlank(message = "{user.phonenumber.not.blank}")
    private String phonenumber;
    /**
     * çŸ­ä¿¡code
     */
    @NotBlank(message = "{sms.code.not.blank}")
    private String smsCode;
}
ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/model/XcxLoginUser.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,27 @@
package org.dromara.common.core.domain.model;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import java.io.Serial;
/**
 * å°ç¨‹åºç™»å½•用户身份权限
 *
 * @author Lion Li
 */
@Data
@EqualsAndHashCode(callSuper = true)
@NoArgsConstructor
public class XcxLoginUser extends LoginUser {
    @Serial
    private static final long serialVersionUID = 1L;
    /**
     * openid
     */
    private String openid;
}
ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/enums/DeviceType.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,32 @@
package org.dromara.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/org/dromara/common/core/enums/LoginType.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,44 @@
package org.dromara.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"),
    /**
     * é‚®ç®±ç™»å½•
     */
    EMAIL("email.code.retry.limit.exceed", "email.code.retry.limit.count"),
    /**
     * å°ç¨‹åºç™»å½•
     */
    XCX("", "");
    /**
     * ç™»å½•重试超出限制提示
     */
    final String retryLimitExceed;
    /**
     * ç™»å½•重试限制计数提示
     */
    final String retryLimitCount;
}
ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/enums/TenantStatus.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,30 @@
package org.dromara.common.core.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
 * ç”¨æˆ·çŠ¶æ€
 *
 * @author LionLi
 */
@Getter
@AllArgsConstructor
public enum TenantStatus {
    /**
     * æ­£å¸¸
     */
    OK("0", "正常"),
    /**
     * åœç”¨
     */
    DISABLE("1", "停用"),
    /**
     * åˆ é™¤
     */
    DELETED("2", "删除");
    private final String code;
    private final String info;
}
ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/enums/UserStatus.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,30 @@
package org.dromara.common.core.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
 * ç”¨æˆ·çŠ¶æ€
 *
 * @author ruoyi
 */
@Getter
@AllArgsConstructor
public enum UserStatus {
    /**
     * æ­£å¸¸
     */
    OK("0", "正常"),
    /**
     * åœç”¨
     */
    DISABLE("1", "停用"),
    /**
     * åˆ é™¤
     */
    DELETED("2", "删除");
    private final String code;
    private final String info;
}
ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/enums/UserType.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,37 @@
package org.dromara.common.core.enums;
import org.dromara.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/org/dromara/common/core/exception/DemoModeException.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,17 @@
package org.dromara.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/org/dromara/common/core/exception/GlobalException.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,53 @@
package org.dromara.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/org/dromara/common/core/exception/ServiceException.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,67 @@
package org.dromara.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/org/dromara/common/core/exception/UtilException.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,26 @@
package org.dromara.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/org/dromara/common/core/exception/base/BaseException.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,79 @@
package org.dromara.common.core.exception.base;
import org.dromara.common.core.utils.MessageUtils;
import org.dromara.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/org/dromara/common/core/exception/file/FileException.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,21 @@
package org.dromara.common.core.exception.file;
import org.dromara.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/org/dromara/common/core/exception/file/FileNameLengthLimitExceededException.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,18 @@
package org.dromara.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/org/dromara/common/core/exception/file/FileSizeLimitExceededException.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,18 @@
package org.dromara.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/org/dromara/common/core/exception/user/CaptchaException.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,18 @@
package org.dromara.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/org/dromara/common/core/exception/user/CaptchaExpireException.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,18 @@
package org.dromara.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/org/dromara/common/core/exception/user/UserException.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,20 @@
package org.dromara.common.core.exception.user;
import org.dromara.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/org/dromara/common/core/exception/user/UserPasswordNotMatchException.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,18 @@
package org.dromara.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/org/dromara/common/core/exception/user/UserPasswordRetryLimitExceedException.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,19 @@
package org.dromara.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/org/dromara/common/core/manager/ShutdownManager.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,41 @@
package org.dromara.common.core.manager;
import org.dromara.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/org/dromara/common/core/service/ConfigService.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,18 @@
package org.dromara.common.core.service;
/**
 * é€šç”¨ å‚数配置服务
 *
 * @author Lion Li
 */
public interface ConfigService {
    /**
     * æ ¹æ®å‚æ•° key èŽ·å–å‚æ•°å€¼
     *
     * @param configKey å‚æ•° key
     * @return å‚数值
     */
    String getConfigValue(String configKey);
}
ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/service/DeptService.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,18 @@
package org.dromara.common.core.service;
/**
 * é€šç”¨ éƒ¨é—¨æœåŠ¡
 *
 * @author Lion Li
 */
public interface DeptService {
    /**
     * é€šè¿‡éƒ¨é—¨ID查询部门名称
     *
     * @param deptIds éƒ¨é—¨ID串逗号分隔
     * @return éƒ¨é—¨åç§°ä¸²é€—号分隔
     */
    String selectDeptNameByIds(String deptIds);
}
ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/service/DictService.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,57 @@
package org.dromara.common.core.service;
/**
 * é€šç”¨ å­—典服务
 *
 * @author Lion Li
 */
public interface DictService {
    /**
     * åˆ†éš”符
     */
    String SEPARATOR = ",";
    /**
     * æ ¹æ®å­—典类型和字典值获取字典标签
     *
     * @param dictType  å­—典类型
     * @param dictValue å­—典值
     * @return å­—典标签
     */
    default String getDictLabel(String dictType, String dictValue) {
        return getDictLabel(dictType, dictValue, SEPARATOR);
    }
    /**
     * æ ¹æ®å­—典类型和字典标签获取字典值
     *
     * @param dictType  å­—典类型
     * @param dictLabel å­—典标签
     * @return å­—典值
     */
    default String getDictValue(String dictType, String dictLabel) {
        return getDictValue(dictType, dictLabel, SEPARATOR);
    }
    /**
     * æ ¹æ®å­—典类型和字典值获取字典标签
     *
     * @param dictType  å­—典类型
     * @param dictValue å­—典值
     * @param separator åˆ†éš”符
     * @return å­—典标签
     */
    String getDictLabel(String dictType, String dictValue, String separator);
    /**
     * æ ¹æ®å­—典类型和字典标签获取字典值
     *
     * @param dictType  å­—典类型
     * @param dictLabel å­—典标签
     * @param separator åˆ†éš”符
     * @return å­—典值
     */
    String getDictValue(String dictType, String dictLabel, String separator);
}
ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/service/OssService.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,18 @@
package org.dromara.common.core.service;
/**
 * é€šç”¨ OSS服务
 *
 * @author Lion Li
 */
public interface OssService {
    /**
     * é€šè¿‡ossId查询对应的url
     *
     * @param ossIds ossId串逗号分隔
     * @return url串逗号分隔
     */
    String selectUrlByIds(String ossIds);
}
ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/service/UserService.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,18 @@
package org.dromara.common.core.service;
/**
 * é€šç”¨ ç”¨æˆ·æœåŠ¡
 *
 * @author Lion Li
 */
public interface UserService {
    /**
     * é€šè¿‡ç”¨æˆ·ID查询用户账户
     *
     * @param userId ç”¨æˆ·ID
     * @return ç”¨æˆ·è´¦æˆ·
     */
    String selectUserNameById(Long userId);
}
ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/DateUtils.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,168 @@
package org.dromara.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/org/dromara/common/core/utils/MapstructUtils.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,92 @@
package org.dromara.common.core.utils;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ObjectUtil;
import io.github.linpeilie.Converter;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import java.util.List;
import java.util.Map;
/**
 * Mapstruct å·¥å…·ç±»
 * <p>参考文档:<a href="https://mapstruct.plus/guide/quick-start">mapstruct-plus</a></p>
 *
 * @author Michelle.Chung
 */
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class MapstructUtils {
    private final static Converter CONVERTER = SpringUtils.getBean(Converter.class);
    /**
     * å°† T ç±»åž‹å¯¹è±¡ï¼Œè½¬æ¢ä¸º desc ç±»åž‹çš„对象并返回
     *
     * @param source æ•°æ®æ¥æºå®žä½“
     * @param desc   æè¿°å¯¹è±¡ è½¬æ¢åŽçš„对象
     * @return desc
     */
    public static <T, V> V convert(T source, Class<V> desc) {
        if (ObjectUtil.isNull(source)) {
            return null;
        }
        if (ObjectUtil.isNull(desc)) {
            return null;
        }
        return CONVERTER.convert(source, desc);
    }
    /**
     * å°† T ç±»åž‹å¯¹è±¡ï¼ŒæŒ‰ç…§é…ç½®çš„æ˜ å°„字段规则,给 desc ç±»åž‹çš„对象赋值并返回 desc å¯¹è±¡
     *
     * @param source æ•°æ®æ¥æºå®žä½“
     * @param desc   è½¬æ¢åŽçš„对象
     * @return desc
     */
    public static <T, V> V convert(T source, V desc) {
        if (ObjectUtil.isNull(source)) {
            return null;
        }
        if (ObjectUtil.isNull(desc)) {
            return null;
        }
        return CONVERTER.convert(source, desc);
    }
    /**
     * å°† T ç±»åž‹çš„集合,转换为 desc ç±»åž‹çš„集合并返回
     *
     * @param sourceList æ•°æ®æ¥æºå®žä½“列表
     * @param desc       æè¿°å¯¹è±¡ è½¬æ¢åŽçš„对象
     * @return desc
     */
    public static <T, V> List<V> convert(List<T> sourceList, Class<V> desc) {
        if (ObjectUtil.isNull(sourceList)) {
            return null;
        }
        if (CollUtil.isEmpty(sourceList)) {
            return CollUtil.newArrayList();
        }
        return CONVERTER.convert(sourceList, desc);
    }
    /**
     * å°† Map è½¬æ¢ä¸º beanClass ç±»åž‹çš„集合并返回
     *
     * @param map       æ•°æ®æ¥æº
     * @param beanClass beanç±»
     * @return bean对象
     */
    public static <T> T convert(Map<String, Object> map, Class<T> beanClass) {
        if (MapUtil.isEmpty(map)) {
            return null;
        }
        if (ObjectUtil.isNull(beanClass)) {
            return null;
        }
        return CONVERTER.convert(map, beanClass);
    }
}
ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/MessageUtils.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,28 @@
package org.dromara.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/org/dromara/common/core/utils/ServletUtils.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,193 @@
package org.dromara.common.core.utils;
import cn.hutool.core.convert.Convert;
import cn.hutool.extra.servlet.JakartaServletUtil;
import cn.hutool.http.HttpStatus;
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.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(), StringUtils.SEPARATOR));
        }
        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) {
        return URLEncoder.encode(str, StandardCharsets.UTF_8);
    }
    /**
     * å†…容解码
     *
     * @param str å†…容
     * @return è§£ç åŽçš„内容
     */
    public static String urlDecode(String str) {
        return URLDecoder.decode(str, StandardCharsets.UTF_8);
    }
}
ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/SpringUtils.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,62 @@
package org.dromara.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
     */
    public static boolean containsBean(String name) {
        return getBeanFactory().containsBean(name);
    }
    /**
     * åˆ¤æ–­ä»¥ç»™å®šåå­—注册的bean定义是一个singleton还是一个prototype。
     * å¦‚果与给定名字相应的bean定义没有被找到,将会抛出一个异常(NoSuchBeanDefinitionException)
     */
    public static boolean isSingleton(String name) throws NoSuchBeanDefinitionException {
        return getBeanFactory().isSingleton(name);
    }
    /**
     * @return Class æ³¨å†Œå¯¹è±¡çš„类型
     */
    public static Class<?> getType(String name) throws NoSuchBeanDefinitionException {
        return getBeanFactory().getType(name);
    }
    /**
     * å¦‚果给定的bean名字在bean定义中有别名,则返回这些别名
     */
    public static String[] getAliases(String name) throws NoSuchBeanDefinitionException {
        return getBeanFactory().getAliases(name);
    }
    /**
     * èŽ·å–aop代理对象
     */
    @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/org/dromara/common/core/utils/StreamUtils.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,254 @@
package org.dromara.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();
        }
        // æ³¨æ„æ­¤å¤„不要使用 .toList() æ–°è¯­æ³• å› ä¸ºè¿”回的是不可变List ä¼šå¯¼è‡´åºåˆ—化问题
        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, StringUtils.SEPARATOR);
    }
    /**
     * å°†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();
        }
        // æ³¨æ„æ­¤å¤„不要使用 .toList() æ–°è¯­æ³• å› ä¸ºè¿”回的是不可变List ä¼šå¯¼è‡´åºåˆ—化问题
        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)
            // æ³¨æ„æ­¤å¤„不要使用 .toList() æ–°è¯­æ³• å› ä¸ºè¿”回的是不可变List ä¼šå¯¼è‡´åºåˆ—化问题
            .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/org/dromara/common/core/utils/StringUtils.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,321 @@
package org.dromara.common.core.utils;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.convert.Convert;
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.*;
import java.util.function.Function;
import java.util.stream.Collectors;
/**
 * å­—符串工具类
 *
 * @author Lion Li
 */
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class StringUtils extends org.apache.commons.lang3.StringUtils {
    public static final String SEPARATOR = ",";
    /**
     * èŽ·å–å‚æ•°ä¸ä¸ºç©ºå€¼
     *
     * @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
     */
    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 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 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) {
                sb.append(String.valueOf(c).repeat(size - len));
                sb.append(s);
            } else {
                return s.substring(len - size, len);
            }
        } else {
            sb.append(String.valueOf(c).repeat(Math.max(0, size)));
        }
        return sb.toString();
    }
    /**
     * åˆ‡åˆ†å­—符串(分隔符默认逗号)
     *
     * @param str è¢«åˆ‡åˆ†çš„字符串
     * @return åˆ†å‰²åŽçš„æ•°æ®åˆ—表
     */
    public static List<String> splitList(String str) {
        return splitTo(str, Convert::toStr);
    }
    /**
     * åˆ‡åˆ†å­—符串
     *
     * @param str       è¢«åˆ‡åˆ†çš„字符串
     * @param separator åˆ†éš”符
     * @return åˆ†å‰²åŽçš„æ•°æ®åˆ—表
     */
    public static List<String> splitList(String str, String separator) {
        return splitTo(str, separator, Convert::toStr);
    }
    /**
     * åˆ‡åˆ†å­—符串自定义转换(分隔符默认逗号)
     *
     * @param str    è¢«åˆ‡åˆ†çš„字符串
     * @param mapper è‡ªå®šä¹‰è½¬æ¢
     * @return åˆ†å‰²åŽçš„æ•°æ®åˆ—表
     */
    public static <T> List<T> splitTo(String str, Function<? super Object, T> mapper) {
        return splitTo(str, SEPARATOR, mapper);
    }
    /**
     * åˆ‡åˆ†å­—符串自定义转换
     *
     * @param str       è¢«åˆ‡åˆ†çš„字符串
     * @param separator åˆ†éš”符
     * @param mapper    è‡ªå®šä¹‰è½¬æ¢
     * @return åˆ†å‰²åŽçš„æ•°æ®åˆ—表
     */
    public static <T> List<T> splitTo(String str, String separator, Function<? super Object, T> mapper) {
        if (isBlank(str)) {
            return new ArrayList<>(0);
        }
        return StrUtil.split(str, separator)
            .stream()
            .filter(Objects::nonNull)
            .map(mapper)
            .collect(Collectors.toList());
    }
}
ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/Threads.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,75 @@
package org.dromara.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/org/dromara/common/core/utils/TreeBuildUtils.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,35 @@
package org.dromara.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 org.dromara.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/org/dromara/common/core/utils/ValidatorUtils.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,28 @@
package org.dromara.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/org/dromara/common/core/utils/file/FileUtils.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,43 @@
package org.dromara.common.core.utils.file;
import cn.hutool.core.io.FileUtil;
import jakarta.servlet.http.HttpServletResponse;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
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) {
        String percentEncodedFileName = percentEncode(realFileName);
        String contentDispositionValue = "attachment; filename=%s;filename*=utf-8''%s".formatted(percentEncodedFileName, percentEncodedFileName);
        response.addHeader("Access-Control-Expose-Headers", "Content-Disposition,download-filename");
        response.setHeader("Content-disposition", contentDispositionValue);
        response.setHeader("download-filename", percentEncodedFileName);
    }
    /**
     * ç™¾åˆ†å·ç¼–码工具方法
     *
     * @param s éœ€è¦ç™¾åˆ†å·ç¼–码的字符串
     * @return ç™¾åˆ†å·ç¼–码后的字符串
     */
    public static String percentEncode(String s) {
        String encode = URLEncoder.encode(s, StandardCharsets.UTF_8);
        return encode.replaceAll("\\+", "%20");
    }
}
ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/file/MimeTypeUtils.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,40 @@
package org.dromara.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/org/dromara/common/core/utils/ip/AddressUtils.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,33 @@
package org.dromara.common.core.utils.ip;
import cn.hutool.core.net.NetUtil;
import cn.hutool.http.HtmlUtil;
import org.dromara.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 {
    // æœªçŸ¥åœ°å€
    public static final String UNKNOWN = "XX XX";
    public static String getRealAddressByIP(String ip) {
        if (StringUtils.isBlank(ip)) {
            return UNKNOWN;
        }
        // å†…网不查询
        ip = "0:0:0:0:0:0:0:1".equals(ip) ? "127.0.0.1" : HtmlUtil.cleanHtmlTag(ip);
        if (NetUtil.isInnerIP(ip)) {
            return "内网IP";
        }
        return RegionUtils.getCityInfo(ip);
    }
}
ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/ip/RegionUtils.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,67 @@
package org.dromara.common.core.utils.ip;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.resource.ClassPathResource;
import cn.hutool.core.util.ObjectUtil;
import org.dromara.common.core.exception.ServiceException;
import org.dromara.common.core.utils.file.FileUtils;
import lombok.extern.slf4j.Slf4j;
import org.lionsoul.ip2region.xdb.Searcher;
import java.io.File;
/**
 * æ ¹æ®ip地址定位工具类,离线方式
 * å‚考地址:<a href="https://gitee.com/lionsoul/ip2region/tree/master/binding/java">集成 ip2region å®žçŽ°ç¦»çº¿IP地址定位库</a>
 *
 * @author lishuyan
 */
@Slf4j
public class RegionUtils {
    private static final Searcher SEARCHER;
    static {
        String fileName = "/ip2region.xdb";
        File existFile = FileUtils.file(FileUtil.getTmpDir() + FileUtil.FILE_SEPARATOR + fileName);
        if (!FileUtils.exist(existFile)) {
            ClassPathResource fileStream = new ClassPathResource(fileName);
            if (ObjectUtil.isEmpty(fileStream.getStream())) {
                throw new ServiceException("RegionUtils初始化失败,原因:IP地址库数据不存在!");
            }
            FileUtils.writeFromStream(fileStream.getStream(), existFile);
        }
        String dbPath = existFile.getPath();
        // 1、从 dbPath åŠ è½½æ•´ä¸ª xdb åˆ°å†…存。
        byte[] cBuff;
        try {
            cBuff = Searcher.loadContentFromFile(dbPath);
        } catch (Exception e) {
            throw new ServiceException("RegionUtils初始化失败,原因:从ip2region.xdb文件加载内容失败!" + e.getMessage());
        }
        // 2、使用上述的 cBuff åˆ›å»ºä¸€ä¸ªå®Œå…¨åŸºäºŽå†…存的查询对象。
        try {
            SEARCHER = Searcher.newWithBuffer(cBuff);
        } catch (Exception e) {
            throw new ServiceException("RegionUtils初始化失败,原因:" + e.getMessage());
        }
    }
    /**
     * æ ¹æ®IP地址离线获取城市
     */
    public static String getCityInfo(String ip) {
        try {
            ip = ip.trim();
            // 3、执行查询
            String region = SEARCHER.search(ip);
            return region.replace("0|", "").replace("|0", "");
        } catch (Exception e) {
            log.error("IP地址离线获取城市异常 {}", ip);
            return "未知";
        }
    }
}
ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/reflect/ReflectUtils.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,56 @@
package org.dromara.common.core.utils.reflect;
import cn.hutool.core.util.ReflectUtil;
import org.dromara.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/org/dromara/common/core/utils/sql/SqlUtil.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,57 @@
package org.dromara.common.core.utils.sql;
import org.dromara.common.core.exception.UtilException;
import org.dromara.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/org/dromara/common/core/validate/AddGroup.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,9 @@
package org.dromara.common.core.validate;
/**
 * æ ¡éªŒåˆ†ç»„ add
 *
 * @author Lion Li
 */
public interface AddGroup {
}
ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/validate/EditGroup.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,9 @@
package org.dromara.common.core.validate;
/**
 * æ ¡éªŒåˆ†ç»„ edit
 *
 * @author Lion Li
 */
public interface EditGroup {
}
ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/validate/QueryGroup.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,9 @@
package org.dromara.common.core.validate;
/**
 * æ ¡éªŒåˆ†ç»„ query
 *
 * @author Lion Li
 */
public interface QueryGroup {
}
ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/xss/Xss.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,26 @@
package org.dromara.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/org/dromara/common/core/xss/XssValidator.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,21 @@
package org.dromara.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
@@ -1,6 +1,6 @@
com.ruoyi.common.core.config.ApplicationConfig
com.ruoyi.common.core.config.AsyncConfig
com.ruoyi.common.core.config.RuoYiConfig
com.ruoyi.common.core.config.ThreadPoolConfig
com.ruoyi.common.core.config.ValidatorConfig
com.ruoyi.common.core.utils.SpringUtils
org.dromara.common.core.config.ApplicationConfig
org.dromara.common.core.config.AsyncConfig
org.dromara.common.core.config.RuoYiConfig
org.dromara.common.core.config.ThreadPoolConfig
org.dromara.common.core.config.ValidatorConfig
org.dromara.common.core.utils.SpringUtils
ruoyi-common/ruoyi-common-doc/pom.xml
@@ -3,7 +3,7 @@
         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>
        <groupId>org.dromara</groupId>
        <artifactId>ruoyi-common</artifactId>
        <version>${revision}</version>
        <relativePath>../pom.xml</relativePath>
@@ -18,7 +18,7 @@
    <dependencies>
        <dependency>
            <groupId>com.ruoyi</groupId>
            <groupId>org.dromara</groupId>
            <artifactId>ruoyi-common-core</artifactId>
        </dependency>
ruoyi-common/ruoyi-common-doc/src/main/java/com/ruoyi/common/doc/config/SwaggerConfig.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-doc/src/main/java/com/ruoyi/common/doc/config/properties/SwaggerProperties.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-doc/src/main/java/com/ruoyi/common/doc/handler/OpenApiHandler.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-doc/src/main/java/org/dromara/common/doc/config/SwaggerConfig.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,126 @@
package org.dromara.common.doc.config;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.doc.config.properties.SwaggerProperties;
import org.dromara.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.boot.context.properties.EnableConfigurationProperties;
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)
@EnableConfigurationProperties(SwaggerProperties.class)
@ConditionalOnProperty(name = "springdoc.api-docs.enabled", havingValue = "true", matchIfMissing = true)
public class SwaggerConfig {
    private final ServerProperties serverProperties;
    @Bean
    @ConditionalOnMissingBean(OpenAPI.class)
    public OpenAPI openApi(SwaggerProperties swaggerProperties) {
        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/org/dromara/common/doc/config/properties/SwaggerProperties.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,94 @@
package org.dromara.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 java.util.List;
/**
 * swagger é…ç½®å±žæ€§
 *
 * @author Lion Li
 */
@Data
@ConfigurationProperties(prefix = "swagger")
public class SwaggerProperties {
    /**
     * æ–‡æ¡£åŸºæœ¬ä¿¡æ¯
     */
    @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/org/dromara/common/doc/handler/OpenApiHandler.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,252 @@
package org.dromara.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
@@ -1 +1 @@
com.ruoyi.common.doc.config.SwaggerConfig
org.dromara.common.doc.config.SwaggerConfig
ruoyi-common/ruoyi-common-encrypt/pom.xml
@@ -3,7 +3,7 @@
         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>
        <groupId>org.dromara</groupId>
        <artifactId>ruoyi-common</artifactId>
        <version>${revision}</version>
        <relativePath>../pom.xml</relativePath>
@@ -19,7 +19,7 @@
    <dependencies>
        <dependency>
            <groupId>com.ruoyi</groupId>
            <groupId>org.dromara</groupId>
            <artifactId>ruoyi-common-core</artifactId>
        </dependency>
ruoyi-common/ruoyi-common-encrypt/src/main/java/com/ruoyi/common/encrypt/annotation/EncryptField.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-encrypt/src/main/java/com/ruoyi/common/encrypt/config/EncryptorAutoConfiguration.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-encrypt/src/main/java/com/ruoyi/common/encrypt/core/EncryptContext.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-encrypt/src/main/java/com/ruoyi/common/encrypt/core/EncryptorManager.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-encrypt/src/main/java/com/ruoyi/common/encrypt/core/IEncryptor.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-encrypt/src/main/java/com/ruoyi/common/encrypt/core/encryptor/AbstractEncryptor.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-encrypt/src/main/java/com/ruoyi/common/encrypt/core/encryptor/AesEncryptor.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-encrypt/src/main/java/com/ruoyi/common/encrypt/core/encryptor/Base64Encryptor.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-encrypt/src/main/java/com/ruoyi/common/encrypt/core/encryptor/RsaEncryptor.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-encrypt/src/main/java/com/ruoyi/common/encrypt/core/encryptor/Sm2Encryptor.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-encrypt/src/main/java/com/ruoyi/common/encrypt/core/encryptor/Sm4Encryptor.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-encrypt/src/main/java/com/ruoyi/common/encrypt/enumd/AlgorithmType.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-encrypt/src/main/java/com/ruoyi/common/encrypt/enumd/EncodeType.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-encrypt/src/main/java/com/ruoyi/common/encrypt/interceptor/MybatisDecryptInterceptor.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-encrypt/src/main/java/com/ruoyi/common/encrypt/interceptor/MybatisEncryptInterceptor.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-encrypt/src/main/java/com/ruoyi/common/encrypt/properties/EncryptorProperties.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/annotation/EncryptField.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,44 @@
package org.dromara.common.encrypt.annotation;
import org.dromara.common.encrypt.enumd.AlgorithmType;
import org.dromara.common.encrypt.enumd.EncodeType;
import java.lang.annotation.*;
/**
 * å­—段加密注解
 *
 * @author è€é©¬
 */
@Documented
@Inherited
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface EncryptField {
    /**
     * åŠ å¯†ç®—æ³•
     */
    AlgorithmType algorithm() default AlgorithmType.DEFAULT;
    /**
     * ç§˜é’¥ã€‚AES、SM4需要
     */
    String password() default "";
    /**
     * å…¬é’¥ã€‚RSA、SM2需要
     */
    String publicKey() default "";
    /**
     * å…¬é’¥ã€‚RSA、SM2需要
     */
    String privateKey() default "";
    /**
     * ç¼–码方式。对加密算法为BASE64的不起作用
     */
    EncodeType encode() default EncodeType.DEFAULT;
}
ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/config/EncryptorAutoConfiguration.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,41 @@
package org.dromara.common.encrypt.config;
import org.dromara.common.encrypt.core.EncryptorManager;
import org.dromara.common.encrypt.interceptor.MybatisDecryptInterceptor;
import org.dromara.common.encrypt.interceptor.MybatisEncryptInterceptor;
import org.dromara.common.encrypt.properties.EncryptorProperties;
import org.springframework.beans.factory.annotation.Autowired;
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;
/**
 * åŠ è§£å¯†é…ç½®
 *
 * @author è€é©¬
 * @version 4.6.0
 */
@AutoConfiguration
@EnableConfigurationProperties(EncryptorProperties.class)
@ConditionalOnProperty(value = "mybatis-encryptor.enable", havingValue = "true")
public class EncryptorAutoConfiguration {
    @Autowired
    private EncryptorProperties properties;
    @Bean
    public EncryptorManager encryptorManager() {
        return new EncryptorManager();
    }
    @Bean
    public MybatisEncryptInterceptor mybatisEncryptInterceptor(EncryptorManager encryptorManager) {
        return new MybatisEncryptInterceptor(encryptorManager, properties);
    }
    @Bean
    public MybatisDecryptInterceptor mybatisDecryptInterceptor(EncryptorManager encryptorManager) {
        return new MybatisDecryptInterceptor(encryptorManager, properties);
    }
}
ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/core/EncryptContext.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,41 @@
package org.dromara.common.encrypt.core;
import org.dromara.common.encrypt.enumd.AlgorithmType;
import org.dromara.common.encrypt.enumd.EncodeType;
import lombok.Data;
/**
 * åŠ å¯†ä¸Šä¸‹æ–‡ ç”¨äºŽencryptor传递必要的参数。
 *
 * @author è€é©¬
 * @version 4.6.0
 */
@Data
public class EncryptContext {
    /**
     * é»˜è®¤ç®—法
     */
    private AlgorithmType algorithm;
    /**
     * å®‰å…¨ç§˜é’¥
     */
    private String password;
    /**
     * å…¬é’¥
     */
    private String publicKey;
    /**
     * ç§é’¥
     */
    private String privateKey;
    /**
     * ç¼–码方式,base64/hex
     */
    private EncodeType encode;
}
ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/core/EncryptorManager.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,94 @@
package org.dromara.common.encrypt.core;
import cn.hutool.core.util.ReflectUtil;
import org.dromara.common.encrypt.annotation.EncryptField;
import lombok.extern.slf4j.Slf4j;
import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
/**
 * åŠ å¯†ç®¡ç†ç±»
 *
 * @author è€é©¬
 * @version 4.6.0
 */
@Slf4j
public class EncryptorManager {
    /**
     * ç¼“存加密器
     */
    Map<EncryptContext, IEncryptor> encryptorMap = new ConcurrentHashMap<>();
    /**
     * ç±»åŠ å¯†å­—æ®µç¼“å­˜
     */
    Map<Class<?>, Set<Field>> fieldCache = new ConcurrentHashMap<>();
    /**
     * èŽ·å–ç±»åŠ å¯†å­—æ®µç¼“å­˜
     */
    public Set<Field> getFieldCache(Class<?> sourceClazz) {
        return fieldCache.computeIfAbsent(sourceClazz, clazz -> {
            Field[] declaredFields = clazz.getDeclaredFields();
            Set<Field> fieldSet = Arrays.stream(declaredFields).filter(field ->
                    field.isAnnotationPresent(EncryptField.class) && field.getType() == String.class)
                .collect(Collectors.toSet());
            for (Field field : fieldSet) {
                field.setAccessible(true);
            }
            return fieldSet;
        });
    }
    /**
     * æ³¨å†ŒåŠ å¯†æ‰§è¡Œè€…åˆ°ç¼“å­˜
     *
     * @param encryptContext åŠ å¯†æ‰§è¡Œè€…éœ€è¦çš„ç›¸å…³é…ç½®å‚æ•°
     */
    public IEncryptor registAndGetEncryptor(EncryptContext encryptContext) {
        if (encryptorMap.containsKey(encryptContext)) {
            return encryptorMap.get(encryptContext);
        }
        IEncryptor encryptor = ReflectUtil.newInstance(encryptContext.getAlgorithm().getClazz(), encryptContext);
        encryptorMap.put(encryptContext, encryptor);
        return encryptor;
    }
    /**
     * ç§»é™¤ç¼“存中的加密执行者
     *
     * @param encryptContext åŠ å¯†æ‰§è¡Œè€…éœ€è¦çš„ç›¸å…³é…ç½®å‚æ•°
     */
    public void removeEncryptor(EncryptContext encryptContext) {
        this.encryptorMap.remove(encryptContext);
    }
    /**
     * æ ¹æ®é…ç½®è¿›è¡ŒåŠ å¯†ã€‚ä¼šè¿›è¡Œæœ¬åœ°ç¼“å­˜å¯¹åº”çš„ç®—æ³•å’Œå¯¹åº”çš„ç§˜é’¥ä¿¡æ¯ã€‚
     *
     * @param value          å¾…加密的值
     * @param encryptContext åŠ å¯†ç›¸å…³çš„é…ç½®ä¿¡æ¯
     */
    public String encrypt(String value, EncryptContext encryptContext) {
        IEncryptor encryptor = this.registAndGetEncryptor(encryptContext);
        return encryptor.encrypt(value, encryptContext.getEncode());
    }
    /**
     * æ ¹æ®é…ç½®è¿›è¡Œè§£å¯†
     *
     * @param value          å¾…解密的值
     * @param encryptContext åŠ å¯†ç›¸å…³çš„é…ç½®ä¿¡æ¯
     */
    public String decrypt(String value, EncryptContext encryptContext) {
        IEncryptor encryptor = this.registAndGetEncryptor(encryptContext);
        return encryptor.decrypt(value);
    }
}
ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/core/IEncryptor.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,35 @@
package org.dromara.common.encrypt.core;
import org.dromara.common.encrypt.enumd.AlgorithmType;
import org.dromara.common.encrypt.enumd.EncodeType;
/**
 * åŠ è§£è€…
 *
 * @author è€é©¬
 * @version 4.6.0
 */
public interface IEncryptor {
    /**
     * èŽ·å¾—å½“å‰ç®—æ³•
     */
    AlgorithmType algorithm();
    /**
     * åР坆
     *
     * @param value      å¾…加密字符串
     * @param encodeType åŠ å¯†åŽçš„ç¼–ç æ ¼å¼
     * @return åŠ å¯†åŽçš„å­—ç¬¦ä¸²
     */
    String encrypt(String value, EncodeType encodeType);
    /**
     * è§£å¯†
     *
     * @param value      å¾…加密字符串
     * @return è§£å¯†åŽçš„字符串
     */
    String decrypt(String value);
}
ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/core/encryptor/AbstractEncryptor.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,18 @@
package org.dromara.common.encrypt.core.encryptor;
import org.dromara.common.encrypt.core.EncryptContext;
import org.dromara.common.encrypt.core.IEncryptor;
/**
 * æ‰€æœ‰åŠ å¯†æ‰§è¡Œè€…çš„åŸºç±»
 *
 * @author è€é©¬
 * @version 4.6.0
 */
public abstract class AbstractEncryptor implements IEncryptor {
    public AbstractEncryptor(EncryptContext context) {
        // ç”¨æˆ·é…ç½®æ ¡éªŒä¸Žé…ç½®æ³¨å…¥
    }
}
ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/core/encryptor/AesEncryptor.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,69 @@
package org.dromara.common.encrypt.core.encryptor;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.crypto.symmetric.AES;
import org.dromara.common.encrypt.core.EncryptContext;
import org.dromara.common.encrypt.enumd.AlgorithmType;
import org.dromara.common.encrypt.enumd.EncodeType;
import java.nio.charset.StandardCharsets;
/**
 * AES算法实现
 *
 * @author è€é©¬
 * @version 4.6.0
 */
public class AesEncryptor extends AbstractEncryptor {
    private final AES aes;
    public AesEncryptor(EncryptContext context) {
        super(context);
        String password = context.getPassword();
        if (StrUtil.isBlank(password)) {
            throw new IllegalArgumentException("AES没有获得秘钥信息");
        }
        // aes算法的秘钥要求是16位、24位、32位
        int[] array = {16, 24, 32};
        if (!ArrayUtil.contains(array, password.length())) {
            throw new IllegalArgumentException("AES秘钥长度应该为16位、24位、32位,实际为" + password.length() + "位");
        }
        aes = SecureUtil.aes(context.getPassword().getBytes(StandardCharsets.UTF_8));
    }
    /**
     * èŽ·å¾—å½“å‰ç®—æ³•
     */
    @Override
    public AlgorithmType algorithm() {
        return AlgorithmType.AES;
    }
    /**
     * åР坆
     *
     * @param value      å¾…加密字符串
     * @param encodeType åŠ å¯†åŽçš„ç¼–ç æ ¼å¼
     */
    @Override
    public String encrypt(String value, EncodeType encodeType) {
        if (encodeType == EncodeType.HEX) {
            return aes.encryptHex(value);
        } else {
            return aes.encryptBase64(value);
        }
    }
    /**
     * è§£å¯†
     *
     * @param value      å¾…加密字符串
     */
    @Override
    public String decrypt(String value) {
        return this.aes.decryptStr(value);
    }
}
ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/core/encryptor/Base64Encryptor.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,48 @@
package org.dromara.common.encrypt.core.encryptor;
import cn.hutool.core.codec.Base64;
import org.dromara.common.encrypt.core.EncryptContext;
import org.dromara.common.encrypt.enumd.AlgorithmType;
import org.dromara.common.encrypt.enumd.EncodeType;
/**
 * Base64算法实现
 *
 * @author è€é©¬
 * @version 4.6.0
 */
public class Base64Encryptor extends AbstractEncryptor {
    public Base64Encryptor(EncryptContext context) {
        super(context);
    }
    /**
     * èŽ·å¾—å½“å‰ç®—æ³•
     */
    @Override
    public AlgorithmType algorithm() {
        return AlgorithmType.BASE64;
    }
    /**
     * åР坆
     *
     * @param value      å¾…加密字符串
     * @param encodeType åŠ å¯†åŽçš„ç¼–ç æ ¼å¼
     */
    @Override
    public String encrypt(String value, EncodeType encodeType) {
        return Base64.encode(value);
    }
    /**
     * è§£å¯†
     *
     * @param value      å¾…加密字符串
     */
    @Override
    public String decrypt(String value) {
        return Base64.decodeStr(value);
    }
}
ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/core/encryptor/RsaEncryptor.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,65 @@
package org.dromara.common.encrypt.core.encryptor;
import cn.hutool.core.codec.Base64;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.crypto.asymmetric.KeyType;
import cn.hutool.crypto.asymmetric.RSA;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.encrypt.core.EncryptContext;
import org.dromara.common.encrypt.enumd.AlgorithmType;
import org.dromara.common.encrypt.enumd.EncodeType;
/**
 * RSA算法实现
 *
 * @author è€é©¬
 * @version 4.6.0
 */
public class RsaEncryptor extends AbstractEncryptor {
    private final RSA rsa;
    public RsaEncryptor(EncryptContext context) {
        super(context);
        String privateKey = context.getPrivateKey();
        String publicKey = context.getPublicKey();
        if (StringUtils.isAnyEmpty(privateKey, publicKey)) {
            throw new IllegalArgumentException("RSA公私钥均需要提供,公钥加密,私钥解密。");
        }
        this.rsa = SecureUtil.rsa(Base64.decode(privateKey), Base64.decode(publicKey));
    }
    /**
     * èŽ·å¾—å½“å‰ç®—æ³•
     */
    @Override
    public AlgorithmType algorithm() {
        return AlgorithmType.RSA;
    }
    /**
     * åР坆
     *
     * @param value      å¾…加密字符串
     * @param encodeType åŠ å¯†åŽçš„ç¼–ç æ ¼å¼
     */
    @Override
    public String encrypt(String value, EncodeType encodeType) {
        if (encodeType == EncodeType.HEX) {
            return rsa.encryptHex(value, KeyType.PublicKey);
        } else {
            return rsa.encryptBase64(value, KeyType.PublicKey);
        }
    }
    /**
     * è§£å¯†
     *
     * @param value      å¾…加密字符串
     */
    @Override
    public String decrypt(String value) {
        return this.rsa.decryptStr(value, KeyType.PrivateKey);
    }
}
ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/core/encryptor/Sm2Encryptor.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,64 @@
package org.dromara.common.encrypt.core.encryptor;
import cn.hutool.core.codec.Base64;
import cn.hutool.crypto.SmUtil;
import cn.hutool.crypto.asymmetric.KeyType;
import cn.hutool.crypto.asymmetric.SM2;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.encrypt.core.EncryptContext;
import org.dromara.common.encrypt.enumd.AlgorithmType;
import org.dromara.common.encrypt.enumd.EncodeType;
/**
 * sm2算法实现
 *
 * @author è€é©¬
 * @version 4.6.0
 */
public class Sm2Encryptor extends AbstractEncryptor {
    private final SM2 sm2;
    public Sm2Encryptor(EncryptContext context) {
        super(context);
        String privateKey = context.getPrivateKey();
        String publicKey = context.getPublicKey();
        if (StringUtils.isAnyEmpty(privateKey, publicKey)) {
            throw new IllegalArgumentException("SM2公私钥均需要提供,公钥加密,私钥解密。");
        }
        this.sm2 = SmUtil.sm2(Base64.decode(privateKey), Base64.decode(publicKey));
    }
    /**
     * èŽ·å¾—å½“å‰ç®—æ³•
     */
    @Override
    public AlgorithmType algorithm() {
        return AlgorithmType.SM2;
    }
    /**
     * åР坆
     *
     * @param value      å¾…加密字符串
     * @param encodeType åŠ å¯†åŽçš„ç¼–ç æ ¼å¼
     */
    @Override
    public String encrypt(String value, EncodeType encodeType) {
        if (encodeType == EncodeType.HEX) {
            return sm2.encryptHex(value, KeyType.PublicKey);
        } else {
            return sm2.encryptBase64(value, KeyType.PublicKey);
        }
    }
    /**
     * è§£å¯†
     *
     * @param value      å¾…加密字符串
     */
    @Override
    public String decrypt(String value) {
        return this.sm2.decryptStr(value, KeyType.PrivateKey);
    }
}
ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/core/encryptor/Sm4Encryptor.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,67 @@
package org.dromara.common.encrypt.core.encryptor;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.SmUtil;
import cn.hutool.crypto.symmetric.SM4;
import org.dromara.common.encrypt.core.EncryptContext;
import org.dromara.common.encrypt.enumd.AlgorithmType;
import org.dromara.common.encrypt.enumd.EncodeType;
import java.nio.charset.StandardCharsets;
/**
 * sm4算法实现
 *
 * @author è€é©¬
 * @version 4.6.0
 */
public class Sm4Encryptor extends AbstractEncryptor {
    private final SM4 sm4;
    public Sm4Encryptor(EncryptContext context) {
        super(context);
        String password = context.getPassword();
        if (StrUtil.isBlank(password)) {
            throw new IllegalArgumentException("SM4没有获得秘钥信息");
        }
        // sm4算法的秘钥要求是16位长度
        if (16 != password.length()) {
            throw new IllegalArgumentException("SM4秘钥长度应该为16位,实际为" + password.length() + "位");
        }
        this.sm4 = SmUtil.sm4(password.getBytes(StandardCharsets.UTF_8));
    }
    /**
     * èŽ·å¾—å½“å‰ç®—æ³•
     */
    @Override
    public AlgorithmType algorithm() {
        return AlgorithmType.SM4;
    }
    /**
     * åР坆
     *
     * @param value      å¾…加密字符串
     * @param encodeType åŠ å¯†åŽçš„ç¼–ç æ ¼å¼
     */
    @Override
    public String encrypt(String value, EncodeType encodeType) {
        if (encodeType == EncodeType.HEX) {
            return sm4.encryptHex(value);
        } else {
            return sm4.encryptBase64(value);
        }
    }
    /**
     * è§£å¯†
     *
     * @param value      å¾…加密字符串
     */
    @Override
    public String decrypt(String value) {
        return this.sm4.decryptStr(value);
    }
}
ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/enumd/AlgorithmType.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,48 @@
package org.dromara.common.encrypt.enumd;
import lombok.AllArgsConstructor;
import lombok.Getter;
import org.dromara.common.encrypt.core.encryptor.*;
/**
 * ç®—法名称
 *
 * @author è€é©¬
 * @version 4.6.0
 */
@Getter
@AllArgsConstructor
public enum AlgorithmType {
    /**
     * é»˜è®¤èµ°yml配置
     */
    DEFAULT(null),
    /**
     * base64
     */
    BASE64(Base64Encryptor.class),
    /**
     * aes
     */
    AES(AesEncryptor.class),
    /**
     * rsa
     */
    RSA(RsaEncryptor.class),
    /**
     * sm2
     */
    SM2(Sm2Encryptor.class),
    /**
     * sm4
     */
    SM4(Sm4Encryptor.class);
    private final Class<? extends AbstractEncryptor> clazz;
}
ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/enumd/EncodeType.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,26 @@
package org.dromara.common.encrypt.enumd;
/**
 * ç¼–码类型
 *
 * @author è€é©¬
 * @version 4.6.0
 */
public enum EncodeType {
    /**
     * é»˜è®¤ä½¿ç”¨yml配置
     */
    DEFAULT,
    /**
     * base64编码
     */
    BASE64,
    /**
     * 16进制编码
     */
    HEX;
}
ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/interceptor/MybatisDecryptInterceptor.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,112 @@
package org.dromara.common.encrypt.interceptor;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ObjectUtil;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.encrypt.annotation.EncryptField;
import org.dromara.common.encrypt.core.EncryptContext;
import org.dromara.common.encrypt.core.EncryptorManager;
import org.dromara.common.encrypt.enumd.AlgorithmType;
import org.dromara.common.encrypt.enumd.EncodeType;
import org.dromara.common.encrypt.properties.EncryptorProperties;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.executor.resultset.ResultSetHandler;
import org.apache.ibatis.plugin.*;
import java.lang.reflect.Field;
import java.sql.Statement;
import java.util.*;
/**
 * å‡ºå‚解密拦截器
 *
 * @author è€é©¬
 * @version 4.6.0
 */
@Slf4j
@Intercepts({@Signature(
    type = ResultSetHandler.class,
    method = "handleResultSets",
    args = {Statement.class})
})
@AllArgsConstructor
public class MybatisDecryptInterceptor implements Interceptor {
    private final EncryptorManager encryptorManager;
    private final EncryptorProperties defaultProperties;
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        // èŽ·å–æ‰§è¡Œmysql执行结果
        Object result = invocation.proceed();
        if (result == null) {
            return null;
        }
        decryptHandler(result);
        return result;
    }
    /**
     * è§£å¯†å¯¹è±¡
     *
     * @param sourceObject å¾…加密对象
     */
    private void decryptHandler(Object sourceObject) {
        if (ObjectUtil.isNull(sourceObject)) {
            return;
        }
        if (sourceObject instanceof Map<?, ?> map) {
            new HashSet<>(map.values()).forEach(this::decryptHandler);
            return;
        }
        if (sourceObject instanceof List<?> list) {
            if(CollectionUtil.isEmpty(list)) {
                return;
            }
            // åˆ¤æ–­ç¬¬ä¸€ä¸ªå…ƒç´ æ˜¯å¦å«æœ‰æ³¨è§£ã€‚如果没有直接返回,提高效率
            Object firstItem = list.get(0);
            if (CollectionUtil.isEmpty(encryptorManager.getFieldCache(firstItem.getClass()))) {
                return;
            }
            list.forEach(this::decryptHandler);
            return;
        }
        Set<Field> fields = encryptorManager.getFieldCache(sourceObject.getClass());
        try {
            for (Field field : fields) {
                field.set(sourceObject, this.decryptField(String.valueOf(field.get(sourceObject)), field));
            }
        } catch (Exception e) {
            log.error("处理解密字段时出错", e);
        }
    }
    /**
     * å­—段值进行加密。通过字段的批注注册新的加密算法
     *
     * @param value å¾…加密的值
     * @param field å¾…加密字段
     * @return åŠ å¯†åŽç»“æžœ
     */
    private String decryptField(String value, Field field) {
        EncryptField encryptField = field.getAnnotation(EncryptField.class);
        EncryptContext encryptContext = new EncryptContext();
        encryptContext.setAlgorithm(encryptField.algorithm() == AlgorithmType.DEFAULT ? defaultProperties.getAlgorithm() : encryptField.algorithm());
        encryptContext.setEncode(encryptField.encode() == EncodeType.DEFAULT ? defaultProperties.getEncode() : encryptField.encode());
        encryptContext.setPassword(StringUtils.isBlank(encryptField.password()) ? defaultProperties.getPassword() : encryptField.password());
        encryptContext.setPrivateKey(StringUtils.isBlank(encryptField.privateKey()) ? defaultProperties.getPrivateKey() : encryptField.privateKey());
        encryptContext.setPublicKey(StringUtils.isBlank(encryptField.publicKey()) ? defaultProperties.getPublicKey() : encryptField.publicKey());
        return this.encryptorManager.decrypt(value, encryptContext);
    }
    @Override
    public Object plugin(Object target) {
        return Plugin.wrap(target, this);
    }
    @Override
    public void setProperties(Properties properties) {
    }
}
ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/interceptor/MybatisEncryptInterceptor.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,116 @@
package org.dromara.common.encrypt.interceptor;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ObjectUtil;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.encrypt.annotation.EncryptField;
import org.dromara.common.encrypt.core.EncryptContext;
import org.dromara.common.encrypt.core.EncryptorManager;
import org.dromara.common.encrypt.enumd.AlgorithmType;
import org.dromara.common.encrypt.enumd.EncodeType;
import org.dromara.common.encrypt.properties.EncryptorProperties;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.executor.parameter.ParameterHandler;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Signature;
import java.lang.reflect.Field;
import java.sql.PreparedStatement;
import java.util.*;
/**
 * å…¥å‚加密拦截器
 *
 * @author è€é©¬
 * @version 4.6.0
 */
@Slf4j
@Intercepts({@Signature(
    type = ParameterHandler.class,
    method = "setParameters",
    args = {PreparedStatement.class})
})
@AllArgsConstructor
public class MybatisEncryptInterceptor implements Interceptor {
    private final EncryptorManager encryptorManager;
    private final EncryptorProperties defaultProperties;
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        return invocation;
    }
    @Override
    public Object plugin(Object target) {
        if (target instanceof ParameterHandler parameterHandler) {
            // è¿›è¡ŒåŠ å¯†æ“ä½œ
            Object parameterObject = parameterHandler.getParameterObject();
            if (ObjectUtil.isNotNull(parameterObject) && !(parameterObject instanceof String)) {
                this.encryptHandler(parameterObject);
            }
        }
        return target;
    }
    /**
     * åŠ å¯†å¯¹è±¡
     *
     * @param sourceObject å¾…加密对象
     */
    private void encryptHandler(Object sourceObject) {
        if (ObjectUtil.isNull(sourceObject)) {
            return;
        }
        if (sourceObject instanceof Map<?, ?> map) {
            new HashSet<>(map.values()).forEach(this::encryptHandler);
            return;
        }
        if (sourceObject instanceof List<?> list) {
            if(CollectionUtil.isEmpty(list)) {
                return;
            }
            // åˆ¤æ–­ç¬¬ä¸€ä¸ªå…ƒç´ æ˜¯å¦å«æœ‰æ³¨è§£ã€‚如果没有直接返回,提高效率
            Object firstItem = list.get(0);
            if (CollectionUtil.isEmpty(encryptorManager.getFieldCache(firstItem.getClass()))) {
                return;
            }
            list.forEach(this::encryptHandler);
            return;
        }
        Set<Field> fields = encryptorManager.getFieldCache(sourceObject.getClass());
        try {
            for (Field field : fields) {
                field.set(sourceObject, this.encryptField(String.valueOf(field.get(sourceObject)), field));
            }
        } catch (Exception e) {
            log.error("处理加密字段时出错", e);
        }
    }
    /**
     * å­—段值进行加密。通过字段的批注注册新的加密算法
     *
     * @param value å¾…加密的值
     * @param field å¾…加密字段
     * @return åŠ å¯†åŽç»“æžœ
     */
    private String encryptField(String value, Field field) {
        EncryptField encryptField = field.getAnnotation(EncryptField.class);
        EncryptContext encryptContext = new EncryptContext();
        encryptContext.setAlgorithm(encryptField.algorithm() == AlgorithmType.DEFAULT ? defaultProperties.getAlgorithm() : encryptField.algorithm());
        encryptContext.setEncode(encryptField.encode() == EncodeType.DEFAULT ? defaultProperties.getEncode() : encryptField.encode());
        encryptContext.setPassword(StringUtils.isBlank(encryptField.password()) ? defaultProperties.getPassword() : encryptField.password());
        encryptContext.setPrivateKey(StringUtils.isBlank(encryptField.privateKey()) ? defaultProperties.getPrivateKey() : encryptField.privateKey());
        encryptContext.setPublicKey(StringUtils.isBlank(encryptField.publicKey()) ? defaultProperties.getPublicKey() : encryptField.publicKey());
        return this.encryptorManager.encrypt(value, encryptContext);
    }
    @Override
    public void setProperties(Properties properties) {
    }
}
ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/properties/EncryptorProperties.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,48 @@
package org.dromara.common.encrypt.properties;
import org.dromara.common.encrypt.enumd.AlgorithmType;
import org.dromara.common.encrypt.enumd.EncodeType;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
 * åŠ è§£å¯†å±žæ€§é…ç½®ç±»
 *
 * @author è€é©¬
 * @version 4.6.0
 */
@Data
@ConfigurationProperties(prefix = "mybatis-encryptor")
public class EncryptorProperties {
    /**
     * è¿‡æ»¤å¼€å…³
     */
    private Boolean enable;
    /**
     * é»˜è®¤ç®—法
     */
    private AlgorithmType algorithm;
    /**
     * å®‰å…¨ç§˜é’¥
     */
    private String password;
    /**
     * å…¬é’¥
     */
    private String publicKey;
    /**
     * ç§é’¥
     */
    private String privateKey;
    /**
     * ç¼–码方式,base64/hex
     */
    private EncodeType encode;
}
ruoyi-common/ruoyi-common-encrypt/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
@@ -1 +1 @@
com.ruoyi.common.encrypt.config.EncryptorAutoConfiguration
org.dromara.common.encrypt.config.EncryptorAutoConfiguration
ruoyi-common/ruoyi-common-excel/pom.xml
@@ -3,7 +3,7 @@
         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>
        <groupId>org.dromara</groupId>
        <artifactId>ruoyi-common</artifactId>
        <version>${revision}</version>
        <relativePath>../pom.xml</relativePath>
@@ -18,7 +18,7 @@
    <dependencies>
        <dependency>
            <groupId>com.ruoyi</groupId>
            <groupId>org.dromara</groupId>
            <artifactId>ruoyi-common-json</artifactId>
        </dependency>
ruoyi-common/ruoyi-common-excel/src/main/java/com/ruoyi/common/excel/annotation/CellMerge.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-excel/src/main/java/com/ruoyi/common/excel/annotation/ExcelDictFormat.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-excel/src/main/java/com/ruoyi/common/excel/annotation/ExcelEnumFormat.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-excel/src/main/java/com/ruoyi/common/excel/convert/ExcelBigNumberConvert.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-excel/src/main/java/com/ruoyi/common/excel/convert/ExcelDictConvert.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-excel/src/main/java/com/ruoyi/common/excel/convert/ExcelEnumConvert.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-excel/src/main/java/com/ruoyi/common/excel/core/CellMergeStrategy.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-excel/src/main/java/com/ruoyi/common/excel/core/DefaultExcelListener.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-excel/src/main/java/com/ruoyi/common/excel/core/DefautExcelResult.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-excel/src/main/java/com/ruoyi/common/excel/core/ExcelListener.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-excel/src/main/java/com/ruoyi/common/excel/core/ExcelResult.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-excel/src/main/java/com/ruoyi/common/excel/utils/ExcelUtil.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/annotation/CellMerge.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,24 @@
package org.dromara.common.excel.annotation;
import org.dromara.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/org/dromara/common/excel/annotation/ExcelDictFormat.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,32 @@
package org.dromara.common.excel.annotation;
import org.dromara.common.core.utils.StringUtils;
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 StringUtils.SEPARATOR;
}
ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/annotation/ExcelEnumFormat.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,30 @@
package org.dromara.common.excel.annotation;
import java.lang.annotation.*;
/**
 * æžšä¸¾æ ¼å¼åŒ–
 *
 * @author Liang
 */
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface ExcelEnumFormat {
    /**
     * å­—典枚举类型
     */
    Class<? extends Enum<?>> enumClass();
    /**
     * å­—典枚举类中对应的code属性名称,默认为code
     */
    String codeField() default "code";
    /**
     * å­—典枚举类中对应的text属性名称,默认为text
     */
    String textField() default "text";
}
ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/convert/ExcelBigNumberConvert.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,52 @@
package org.dromara.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/org/dromara/common/excel/convert/ExcelDictConvert.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,73 @@
package org.dromara.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 org.dromara.common.excel.annotation.ExcelDictFormat;
import org.dromara.common.core.service.DictService;
import org.dromara.common.core.utils.SpringUtils;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.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/org/dromara/common/excel/convert/ExcelEnumConvert.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,75 @@
package org.dromara.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 org.dromara.common.core.utils.reflect.ReflectUtils;
import org.dromara.common.excel.annotation.ExcelEnumFormat;
import lombok.extern.slf4j.Slf4j;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;
/**
 * æžšä¸¾æ ¼å¼åŒ–转换处理
 *
 * @author Liang
 */
@Slf4j
public class ExcelEnumConvert 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) {
        Object codeValue = cellData.getData();
        // å¦‚果是空值
        if (ObjectUtil.isNull(codeValue)) {
            return null;
        }
        Map<Object, String> enumValueMap = beforeConvert(contentProperty);
        String textValue = enumValueMap.get(codeValue);
        return Convert.convert(contentProperty.getField().getType(), textValue);
    }
    @Override
    public WriteCellData<String> convertToExcelData(Object object, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) {
        if (ObjectUtil.isNull(object)) {
            return new WriteCellData<>("");
        }
        Map<Object, String> enumValueMap = beforeConvert(contentProperty);
        String value = Convert.toStr(enumValueMap.get(object), "");
        return new WriteCellData<>(value);
    }
    private Map<Object, String> beforeConvert(ExcelContentProperty contentProperty) {
        ExcelEnumFormat anno = getAnnotation(contentProperty.getField());
        Map<Object, String> enumValueMap = new HashMap<>();
        Enum<?>[] enumConstants = anno.enumClass().getEnumConstants();
        for (Enum<?> enumConstant : enumConstants) {
            Object codeValue = ReflectUtils.invokeGetter(enumConstant, anno.codeField());
            String textValue = ReflectUtils.invokeGetter(enumConstant, anno.textField());
            enumValueMap.put(codeValue, textValue);
        }
        return enumValueMap;
    }
    private ExcelEnumFormat getAnnotation(Field field) {
        return AnnotationUtil.getAnnotation(field, ExcelEnumFormat.class);
    }
}
ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/core/CellMergeStrategy.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,114 @@
package org.dromara.common.excel.core;
import com.alibaba.excel.metadata.Head;
import com.alibaba.excel.write.merge.AbstractMergeStrategy;
import org.dromara.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/org/dromara/common/excel/core/DefaultExcelListener.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,104 @@
package org.dromara.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 org.dromara.common.core.utils.StreamUtils;
import org.dromara.common.core.utils.ValidatorUtils;
import org.dromara.common.json.utils.JsonUtils;
import jakarta.validation.ConstraintViolation;
import jakarta.validation.ConstraintViolationException;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
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) {
            // å¦‚果是某一个单元格的转换异常 èƒ½èŽ·å–åˆ°å…·ä½“è¡Œå·
            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) {
            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/org/dromara/common/excel/core/DefautExcelResult.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,73 @@
package org.dromara.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/org/dromara/common/excel/core/ExcelListener.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,14 @@
package org.dromara.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/org/dromara/common/excel/core/ExcelResult.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,26 @@
package org.dromara.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/org/dromara/common/excel/utils/ExcelUtil.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,327 @@
package org.dromara.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 org.dromara.common.core.utils.StringUtils;
import org.dromara.common.core.utils.file.FileUtils;
import org.dromara.common.excel.convert.ExcelBigNumberConvert;
import org.dromara.common.excel.core.CellMergeStrategy;
import org.dromara.common.excel.core.DefaultExcelListener;
import org.dromara.common.excel.core.ExcelListener;
import org.dromara.common.excel.core.ExcelResult;
import jakarta.servlet.ServletOutputStream;
import jakarta.servlet.http.HttpServletResponse;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
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) {
        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(StringUtils.SEPARATOR);
        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]).append(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(StringUtils.SEPARATOR);
        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]).append(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
@@ -3,7 +3,7 @@
         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>
        <groupId>org.dromara</groupId>
        <artifactId>ruoyi-common</artifactId>
        <version>${revision}</version>
        <relativePath>../pom.xml</relativePath>
@@ -18,12 +18,12 @@
    <dependencies>
        <dependency>
            <groupId>com.ruoyi</groupId>
            <groupId>org.dromara</groupId>
            <artifactId>ruoyi-common-json</artifactId>
        </dependency>
        <dependency>
            <groupId>com.ruoyi</groupId>
            <groupId>org.dromara</groupId>
            <artifactId>ruoyi-common-redis</artifactId>
        </dependency>
ruoyi-common/ruoyi-common-idempotent/src/main/java/com/ruoyi/common/idempotent/annotation/RepeatSubmit.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-idempotent/src/main/java/com/ruoyi/common/idempotent/aspectj/RepeatSubmitAspect.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-idempotent/src/main/java/com/ruoyi/common/idempotent/config/IdempotentConfig.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-idempotent/src/main/java/org/dromara/common/idempotent/annotation/RepeatSubmit.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,29 @@
package org.dromara.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/org/dromara/common/idempotent/aspectj/RepeatSubmitAspect.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,152 @@
package org.dromara.common.idempotent.aspectj;
import cn.dev33.satoken.SaManager;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.crypto.SecureUtil;
import org.dromara.common.core.constant.GlobalConstants;
import org.dromara.common.core.domain.R;
import org.dromara.common.core.exception.ServiceException;
import org.dromara.common.core.utils.MessageUtils;
import org.dromara.common.core.utils.ServletUtils;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.idempotent.annotation.RepeatSubmit;
import org.dromara.common.json.utils.JsonUtils;
import org.dromara.common.redis.utils.RedisUtils;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
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 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 = GlobalConstants.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 r) {
            try {
                // æˆåŠŸåˆ™ä¸åˆ é™¤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/org/dromara/common/idempotent/config/IdempotentConfig.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,21 @@
package org.dromara.common.idempotent.config;
import org.dromara.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
@@ -1 +1 @@
com.ruoyi.common.idempotent.config.IdempotentConfig
org.dromara.common.idempotent.config.IdempotentConfig
ruoyi-common/ruoyi-common-job/pom.xml
@@ -3,7 +3,7 @@
         xmlns="http://maven.apache.org/POM/4.0.0"
         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>
        <groupId>org.dromara</groupId>
        <artifactId>ruoyi-common</artifactId>
        <version>${revision}</version>
        <relativePath>../pom.xml</relativePath>
@@ -35,7 +35,7 @@
        </dependency>
        <dependency>
            <groupId>com.ruoyi</groupId>
            <groupId>org.dromara</groupId>
            <artifactId>ruoyi-common-core</artifactId>
        </dependency>
ruoyi-common/ruoyi-common-job/src/main/java/com/ruoyi/common/job/config/XxlJobConfig.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-job/src/main/java/com/ruoyi/common/job/config/properties/XxlJobProperties.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-job/src/main/java/org/dromara/common/job/config/XxlJobConfig.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,38 @@
package org.dromara.common.job.config;
import org.dromara.common.job.config.properties.XxlJobProperties;
import com.xxl.job.core.executor.impl.XxlJobSpringExecutor;
import lombok.extern.slf4j.Slf4j;
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;
/**
 * xxl-job config
 *
 * @author Lion Li
 */
@Slf4j
@AutoConfiguration
@EnableConfigurationProperties(XxlJobProperties.class)
@ConditionalOnProperty(prefix = "xxl.job", name = "enabled", havingValue = "true")
public class XxlJobConfig {
    @Bean
    public XxlJobSpringExecutor xxlJobExecutor(XxlJobProperties xxlJobProperties) {
        log.info(">>>>>>>>>>> xxl-job config init.");
        XxlJobSpringExecutor xxlJobSpringExecutor = new XxlJobSpringExecutor();
        xxlJobSpringExecutor.setAdminAddresses(xxlJobProperties.getAdminAddresses());
        xxlJobSpringExecutor.setAccessToken(xxlJobProperties.getAccessToken());
        XxlJobProperties.Executor executor = xxlJobProperties.getExecutor();
        xxlJobSpringExecutor.setAppname(executor.getAppname());
        xxlJobSpringExecutor.setAddress(executor.getAddress());
        xxlJobSpringExecutor.setIp(executor.getIp());
        xxlJobSpringExecutor.setPort(executor.getPort());
        xxlJobSpringExecutor.setLogPath(executor.getLogPath());
        xxlJobSpringExecutor.setLogRetentionDays(executor.getLogRetentionDays());
        return xxlJobSpringExecutor;
    }
}
ruoyi-common/ruoyi-common-job/src/main/java/org/dromara/common/job/config/properties/XxlJobProperties.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,40 @@
package org.dromara.common.job.config.properties;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
 * xxljob配置类
 *
 * @author Lion Li
 */
@Data
@ConfigurationProperties(prefix = "xxl.job")
public class XxlJobProperties {
    private Boolean enabled;
    private String adminAddresses;
    private String accessToken;
    private Executor executor;
    @Data
    @NoArgsConstructor
    public static class Executor {
        private String appname;
        private String address;
        private String ip;
        private int port;
        private String logPath;
        private int logRetentionDays;
    }
}
ruoyi-common/ruoyi-common-job/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
@@ -1 +1 @@
com.ruoyi.common.job.config.XxlJobConfig
org.dromara.common.job.config.XxlJobConfig
ruoyi-common/ruoyi-common-json/pom.xml
@@ -3,7 +3,7 @@
         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>
        <groupId>org.dromara</groupId>
        <artifactId>ruoyi-common</artifactId>
        <version>${revision}</version>
        <relativePath>../pom.xml</relativePath>
@@ -18,7 +18,7 @@
    <dependencies>
        <dependency>
            <groupId>com.ruoyi</groupId>
            <groupId>org.dromara</groupId>
            <artifactId>ruoyi-common-core</artifactId>
        </dependency>
ruoyi-common/ruoyi-common-json/src/main/java/com/ruoyi/common/json/config/JacksonConfig.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-json/src/main/java/com/ruoyi/common/json/handler/BigNumberSerializer.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-json/src/main/java/com/ruoyi/common/json/utils/JsonUtils.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-json/src/main/java/org/dromara/common/json/config/JacksonConfig.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,47 @@
package org.dromara.common.json.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 org.dromara.common.json.handler.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-json/src/main/java/org/dromara/common/json/handler/BigNumberSerializer.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,42 @@
package org.dromara.common.json.handler;
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-json/src/main/java/org/dromara/common/json/utils/JsonUtils.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,113 @@
package org.dromara.common.json.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 org.dromara.common.core.utils.SpringUtils;
import org.dromara.common.core.utils.StringUtils;
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-json/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
@@ -1 +1 @@
com.ruoyi.common.json.config.JacksonConfig
org.dromara.common.json.config.JacksonConfig
ruoyi-common/ruoyi-common-log/pom.xml
@@ -3,7 +3,7 @@
         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>
        <groupId>org.dromara</groupId>
        <artifactId>ruoyi-common</artifactId>
        <version>${revision}</version>
        <relativePath>../pom.xml</relativePath>
@@ -19,12 +19,12 @@
    <dependencies>
        <dependency>
            <groupId>com.ruoyi</groupId>
            <groupId>org.dromara</groupId>
            <artifactId>ruoyi-common-satoken</artifactId>
        </dependency>
        <dependency>
            <groupId>com.ruoyi</groupId>
            <groupId>org.dromara</groupId>
            <artifactId>ruoyi-common-json</artifactId>
        </dependency>
ruoyi-common/ruoyi-common-log/src/main/java/com/ruoyi/common/log/annotation/Log.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-log/src/main/java/com/ruoyi/common/log/aspect/LogAspect.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-log/src/main/java/com/ruoyi/common/log/enums/BusinessStatus.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-log/src/main/java/com/ruoyi/common/log/enums/BusinessType.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-log/src/main/java/com/ruoyi/common/log/enums/OperatorType.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-log/src/main/java/com/ruoyi/common/log/event/LogininforEvent.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-log/src/main/java/com/ruoyi/common/log/event/OperLogEvent.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-log/src/main/java/org/dromara/common/log/annotation/Log.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,48 @@
package org.dromara.common.log.annotation;
import org.dromara.common.log.enums.BusinessType;
import org.dromara.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;
    /**
     * æŽ’除指定的请求参数
     */
    String[] excludeParamNames() default {};
}
ruoyi-common/ruoyi-common-log/src/main/java/org/dromara/common/log/aspect/LogAspect.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,221 @@
package org.dromara.common.log.aspect;
import cn.hutool.core.lang.Dict;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ObjectUtil;
import com.alibaba.ttl.TransmittableThreadLocal;
import org.dromara.common.core.utils.ServletUtils;
import org.dromara.common.core.utils.SpringUtils;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.json.utils.JsonUtils;
import org.dromara.common.log.annotation.Log;
import org.dromara.common.log.enums.BusinessStatus;
import org.dromara.common.log.event.OperLogEvent;
import org.dromara.common.satoken.utils.LoginHelper;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.time.StopWatch;
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.boot.autoconfigure.AutoConfiguration;
import org.springframework.http.HttpMethod;
import org.springframework.validation.BindingResult;
import org.springframework.web.multipart.MultipartFile;
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" };
    /**
     * è®¡ç®—操作消耗时间
     */
    private static final ThreadLocal<StopWatch> TIME_THREADLOCAL = new TransmittableThreadLocal<>();
    /**
     * å¤„理请求前执行
     */
    @Before(value = "@annotation(controllerLog)")
    public void boBefore(JoinPoint joinPoint, Log controllerLog) {
        StopWatch stopWatch = new StopWatch();
        TIME_THREADLOCAL.set(stopWatch);
        stopWatch.start();
    }
    /**
     * å¤„理完请求后执行
     *
     * @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.setTenantId(LoginHelper.getTenantId());
            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);
            // è®¾ç½®æ¶ˆè€—æ—¶é—´
            StopWatch stopWatch = TIME_THREADLOCAL.get();
            stopWatch.stop();
            operLog.setCostTime(stopWatch.getTime());
            // å‘布事件保存数据库
            SpringUtils.context().publishEvent(operLog);
        } catch (Exception exp) {
            // è®°å½•本地异常日志
            log.error("异常信息:{}", exp.getMessage());
            exp.printStackTrace();
        } finally {
            TIME_THREADLOCAL.remove();
        }
    }
    /**
     * èŽ·å–æ³¨è§£ä¸­å¯¹æ–¹æ³•çš„æè¿°ä¿¡æ¯ ç”¨äºŽ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, log.excludeParamNames());
        }
        // æ˜¯å¦éœ€è¦ä¿å­˜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, String[] excludeParamNames) throws Exception {
        Map<String, String> paramsMap = ServletUtils.getParamMap(ServletUtils.getRequest());
        String requestMethod = operLog.getRequestMethod();
        if (MapUtil.isEmpty(paramsMap)
                && HttpMethod.PUT.name().equals(requestMethod) || HttpMethod.POST.name().equals(requestMethod)) {
            String params = argsArrayToString(joinPoint.getArgs(), excludeParamNames);
            operLog.setOperParam(StringUtils.substring(params, 0, 2000));
        } else {
            MapUtil.removeAny(paramsMap, EXCLUDE_PROPERTIES);
            MapUtil.removeAny(paramsMap, excludeParamNames);
            operLog.setOperParam(StringUtils.substring(JsonUtils.toJsonString(paramsMap), 0, 2000));
        }
    }
    /**
     * å‚数拼装
     */
    private String argsArrayToString(Object[] paramsArray, String[] excludeParamNames) {
        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);
                            MapUtil.removeAny(dict, excludeParamNames);
                            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/org/dromara/common/log/enums/BusinessStatus.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,18 @@
package org.dromara.common.log.enums;
/**
 * æ“ä½œçŠ¶æ€
 *
 * @author ruoyi
 */
public enum BusinessStatus {
    /**
     * æˆåŠŸ
     */
    SUCCESS,
    /**
     * å¤±è´¥
     */
    FAIL,
}
ruoyi-common/ruoyi-common-log/src/main/java/org/dromara/common/log/enums/BusinessType.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,58 @@
package org.dromara.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/org/dromara/common/log/enums/OperatorType.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,23 @@
package org.dromara.common.log.enums;
/**
 * æ“ä½œäººç±»åˆ«
 *
 * @author ruoyi
 */
public enum OperatorType {
    /**
     * å…¶å®ƒ
     */
    OTHER,
    /**
     * åŽå°ç”¨æˆ·
     */
    MANAGE,
    /**
     * æ‰‹æœºç«¯ç”¨æˆ·
     */
    MOBILE
}
ruoyi-common/ruoyi-common-log/src/main/java/org/dromara/common/log/event/LogininforEvent.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,52 @@
package org.dromara.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;
    /**
     * ç§Ÿæˆ·ID
     */
    private String tenantId;
    /**
     * ç”¨æˆ·è´¦å·
     */
    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/org/dromara/common/log/event/OperLogEvent.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,115 @@
package org.dromara.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;
    /**
     * ç§Ÿæˆ·ID
     */
    private String tenantId;
    /**
     * æ“ä½œæ¨¡å—
     */
    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;
    /**
     * æ¶ˆè€—æ—¶é—´
     */
    private Long costTime;
}
ruoyi-common/ruoyi-common-log/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
@@ -1 +1 @@
com.ruoyi.common.log.aspect.LogAspect
org.dromara.common.log.aspect.LogAspect
ruoyi-common/ruoyi-common-mail/pom.xml
@@ -3,7 +3,7 @@
         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>
        <groupId>org.dromara</groupId>
        <artifactId>ruoyi-common</artifactId>
        <version>${revision}</version>
        <relativePath>../pom.xml</relativePath>
@@ -18,7 +18,7 @@
    <dependencies>
        <dependency>
            <groupId>com.ruoyi</groupId>
            <groupId>org.dromara</groupId>
            <artifactId>ruoyi-common-core</artifactId>
        </dependency>
ruoyi-common/ruoyi-common-mail/src/main/java/com/ruoyi/common/mail/config/MailConfig.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-mail/src/main/java/com/ruoyi/common/mail/config/properties/MailProperties.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-mail/src/main/java/com/ruoyi/common/mail/utils/InternalMailUtil.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-mail/src/main/java/com/ruoyi/common/mail/utils/Mail.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-mail/src/main/java/com/ruoyi/common/mail/utils/MailUtils.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-mail/src/main/java/com/ruoyi/common/mail/utils/UserPassAuthenticator.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-mail/src/main/java/org/dromara/common/mail/config/MailConfig.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,37 @@
package org.dromara.common.mail.config;
import cn.hutool.extra.mail.MailAccount;
import org.dromara.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
@EnableConfigurationProperties(MailProperties.class)
public class MailConfig {
    @Bean
    @ConditionalOnProperty(value = "mail.enabled", havingValue = "true")
    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/org/dromara/common/mail/config/properties/MailProperties.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,69 @@
package org.dromara.common.mail.config.properties;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
 * JavaMail é…ç½®å±žæ€§
 *
 * @author Michelle.Chung
 */
@Data
@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/org/dromara/common/mail/utils/InternalMailUtil.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,108 @@
package org.dromara.common.mail.utils;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.extra.mail.MailException;
import jakarta.mail.internet.AddressException;
import jakarta.mail.internet.InternetAddress;
import jakarta.mail.internet.MimeUtility;
import java.io.UnsupportedEncodingException;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
 * é‚®ä»¶å†…部工具类
 * @author looly
 * @since 3.2.3
 */
public class InternalMailUtil {
    /**
     * å°†å¤šä¸ªå­—符串邮件地址转为{@link InternetAddress}列表<br>
     * å•个字符串地址可以是多个地址合并的字符串
     *
     * @param addrStrs åœ°å€æ•°ç»„
     * @param charset ç¼–码(主要用于中文用户名的编码)
     * @return åœ°å€æ•°ç»„
     * @since 4.0.3
     */
    public static InternetAddress[] parseAddressFromStrs(String[] addrStrs, Charset charset) {
        final List<InternetAddress> resultList = new ArrayList<>(addrStrs.length);
        InternetAddress[] addrs;
        for (String addrStr : addrStrs) {
            addrs = parseAddress(addrStr, charset);
            if (ArrayUtil.isNotEmpty(addrs)) {
                Collections.addAll(resultList, addrs);
            }
        }
        return resultList.toArray(new InternetAddress[0]);
    }
    /**
     * è§£æžç¬¬ä¸€ä¸ªåœ°å€
     *
     * @param address åœ°å€å­—符串
     * @param charset ç¼–码,{@code null}表示使用系统属性定义的编码或系统编码
     * @return åœ°å€åˆ—表
     */
    public static InternetAddress parseFirstAddress(String address, Charset charset) {
        final InternetAddress[] internetAddresses = parseAddress(address, charset);
        if (ArrayUtil.isEmpty(internetAddresses)) {
            try {
                return new InternetAddress(address);
            } catch (AddressException e) {
                throw new MailException(e);
            }
        }
        return internetAddresses[0];
    }
    /**
     * å°†ä¸€ä¸ªåœ°å€å­—符串解析为多个地址<br>
     * åœ°å€é—´ä½¿ç”¨" "、","、";"分隔
     *
     * @param address åœ°å€å­—符串
     * @param charset ç¼–码,{@code null}表示使用系统属性定义的编码或系统编码
     * @return åœ°å€åˆ—表
     */
    public static InternetAddress[] parseAddress(String address, Charset charset) {
        InternetAddress[] addresses;
        try {
            addresses = InternetAddress.parse(address);
        } catch (AddressException e) {
            throw new MailException(e);
        }
        //编码用户名
        if (ArrayUtil.isNotEmpty(addresses)) {
            final String charsetStr = null == charset ? null : charset.name();
            for (InternetAddress internetAddress : addresses) {
                try {
                    internetAddress.setPersonal(internetAddress.getPersonal(), charsetStr);
                } catch (UnsupportedEncodingException e) {
                    throw new MailException(e);
                }
            }
        }
        return addresses;
    }
    /**
     * ç¼–码中文字符<br>
     * ç¼–码失败返回原字符串
     *
     * @param text è¢«ç¼–码的文本
     * @param charset ç¼–码
     * @return ç¼–码后的结果
     */
    public static String encodeText(String text, Charset charset) {
        try {
            return MimeUtility.encodeText(text, charset.name(), null);
        } catch (UnsupportedEncodingException e) {
            // ignore
        }
        return text;
    }
}
ruoyi-common/ruoyi-common-mail/src/main/java/org/dromara/common/mail/utils/Mail.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,486 @@
package org.dromara.common.mail.utils;
import cn.hutool.core.builder.Builder;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.IORuntimeException;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.mail.*;
import jakarta.activation.DataHandler;
import jakarta.activation.DataSource;
import jakarta.activation.FileDataSource;
import jakarta.activation.FileTypeMap;
import jakarta.mail.*;
import jakarta.mail.internet.MimeBodyPart;
import jakarta.mail.internet.MimeMessage;
import jakarta.mail.internet.MimeMultipart;
import jakarta.mail.internet.MimeUtility;
import jakarta.mail.util.ByteArrayDataSource;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintStream;
import java.nio.charset.Charset;
import java.util.Date;
/**
 * é‚®ä»¶å‘送客户端
 *
 * @author looly
 * @since 3.2.0
 */
public class Mail implements Builder<MimeMessage> {
    private static final long serialVersionUID = 1L;
    /**
     * é‚®ç®±å¸æˆ·ä¿¡æ¯ä»¥åŠä¸€äº›å®¢æˆ·ç«¯é…ç½®ä¿¡æ¯
     */
    private final MailAccount mailAccount;
    /**
     * æ”¶ä»¶äººåˆ—表
     */
    private String[] tos;
    /**
     * æŠ„送人列表(carbon copy)
     */
    private String[] ccs;
    /**
     * å¯†é€äººåˆ—表(blind carbon copy)
     */
    private String[] bccs;
    /**
     * å›žå¤åœ°å€(reply-to)
     */
    private String[] reply;
    /**
     * æ ‡é¢˜
     */
    private String title;
    /**
     * å†…容
     */
    private String content;
    /**
     * æ˜¯å¦ä¸ºHTML
     */
    private boolean isHtml;
    /**
     * æ­£æ–‡ã€é™„件和图片的混合部分
     */
    private final Multipart multipart = new MimeMultipart();
    /**
     * æ˜¯å¦ä½¿ç”¨å…¨å±€ä¼šè¯ï¼Œé»˜è®¤ä¸ºfalse
     */
    private boolean useGlobalSession = false;
    /**
     * debug输出位置,可以自定义debug日志
     */
    private PrintStream debugOutput;
    /**
     * åˆ›å»ºé‚®ä»¶å®¢æˆ·ç«¯
     *
     * @param mailAccount é‚®ä»¶å¸å·
     * @return Mail
     */
    public static Mail create(MailAccount mailAccount) {
        return new Mail(mailAccount);
    }
    /**
     * åˆ›å»ºé‚®ä»¶å®¢æˆ·ç«¯ï¼Œä½¿ç”¨å…¨å±€é‚®ä»¶å¸æˆ·
     *
     * @return Mail
     */
    public static Mail create() {
        return new Mail();
    }
    // --------------------------------------------------------------- Constructor start
    /**
     * æž„造,使用全局邮件帐户
     */
    public Mail() {
        this(GlobalMailAccount.INSTANCE.getAccount());
    }
    /**
     * æž„造
     *
     * @param mailAccount é‚®ä»¶å¸æˆ·ï¼Œå¦‚果为null使用默认配置文件的全局邮件配置
     */
    public Mail(MailAccount mailAccount) {
        mailAccount = (null != mailAccount) ? mailAccount : GlobalMailAccount.INSTANCE.getAccount();
        this.mailAccount = mailAccount.defaultIfEmpty();
    }
    // --------------------------------------------------------------- Constructor end
    // --------------------------------------------------------------- Getters and Setters start
    /**
     * è®¾ç½®æ”¶ä»¶äºº
     *
     * @param tos æ”¶ä»¶äººåˆ—表
     * @return this
     * @see #setTos(String...)
     */
    public Mail to(String... tos) {
        return setTos(tos);
    }
    /**
     * è®¾ç½®å¤šä¸ªæ”¶ä»¶äºº
     *
     * @param tos æ”¶ä»¶äººåˆ—表
     * @return this
     */
    public Mail setTos(String... tos) {
        this.tos = tos;
        return this;
    }
    /**
     * è®¾ç½®å¤šä¸ªæŠ„送人(carbon copy)
     *
     * @param ccs æŠ„送人列表
     * @return this
     * @since 4.0.3
     */
    public Mail setCcs(String... ccs) {
        this.ccs = ccs;
        return this;
    }
    /**
     * è®¾ç½®å¤šä¸ªå¯†é€äººï¼ˆblind carbon copy)
     *
     * @param bccs å¯†é€äººåˆ—表
     * @return this
     * @since 4.0.3
     */
    public Mail setBccs(String... bccs) {
        this.bccs = bccs;
        return this;
    }
    /**
     * è®¾ç½®å¤šä¸ªå›žå¤åœ°å€(reply-to)
     *
     * @param reply å›žå¤åœ°å€(reply-to)列表
     * @return this
     * @since 4.6.0
     */
    public Mail setReply(String... reply) {
        this.reply = reply;
        return this;
    }
    /**
     * è®¾ç½®æ ‡é¢˜
     *
     * @param title æ ‡é¢˜
     * @return this
     */
    public Mail setTitle(String title) {
        this.title = title;
        return this;
    }
    /**
     * è®¾ç½®æ­£æ–‡<br>
     * æ­£æ–‡å¯ä»¥æ˜¯æ™®é€šæ–‡æœ¬ä¹Ÿå¯ä»¥æ˜¯HTML(默认普通文本),可以通过调用{@link #setHtml(boolean)} è®¾ç½®æ˜¯å¦ä¸ºHTML
     *
     * @param content æ­£æ–‡
     * @return this
     */
    public Mail setContent(String content) {
        this.content = content;
        return this;
    }
    /**
     * è®¾ç½®æ˜¯å¦æ˜¯HTML
     *
     * @param isHtml æ˜¯å¦ä¸ºHTML
     * @return this
     */
    public Mail setHtml(boolean isHtml) {
        this.isHtml = isHtml;
        return this;
    }
    /**
     * è®¾ç½®æ­£æ–‡
     *
     * @param content æ­£æ–‡å†…容
     * @param isHtml  æ˜¯å¦ä¸ºHTML
     * @return this
     */
    public Mail setContent(String content, boolean isHtml) {
        setContent(content);
        return setHtml(isHtml);
    }
    /**
     * è®¾ç½®æ–‡ä»¶ç±»åž‹é™„件,文件可以是图片文件,此时自动设置cid(正文中引用图片),默认cid为文件名
     *
     * @param files é™„件文件列表
     * @return this
     */
    public Mail setFiles(File... files) {
        if (ArrayUtil.isEmpty(files)) {
            return this;
        }
        final DataSource[] attachments = new DataSource[files.length];
        for (int i = 0; i < files.length; i++) {
            attachments[i] = new FileDataSource(files[i]);
        }
        return setAttachments(attachments);
    }
    /**
     * å¢žåŠ é™„ä»¶æˆ–å›¾ç‰‡ï¼Œé™„ä»¶ä½¿ç”¨{@link DataSource} å½¢å¼è¡¨ç¤ºï¼Œå¯ä»¥ä½¿ç”¨{@link FileDataSource}包装文件表示文件附件
     *
     * @param attachments é™„件列表
     * @return this
     * @since 4.0.9
     */
    public Mail setAttachments(DataSource... attachments) {
        if (ArrayUtil.isNotEmpty(attachments)) {
            final Charset charset = this.mailAccount.getCharset();
            MimeBodyPart bodyPart;
            String nameEncoded;
            try {
                for (DataSource attachment : attachments) {
                    bodyPart = new MimeBodyPart();
                    bodyPart.setDataHandler(new DataHandler(attachment));
                    nameEncoded = attachment.getName();
                    if (this.mailAccount.isEncodefilename()) {
                        nameEncoded = org.dromara.common.mail.utils.InternalMailUtil.encodeText(nameEncoded, charset);
                    }
                    // æ™®é€šé™„件文件名
                    bodyPart.setFileName(nameEncoded);
                    if (StrUtil.startWith(attachment.getContentType(), "image/")) {
                        // å›¾ç‰‡é™„件,用于正文中引用图片
                        bodyPart.setContentID(nameEncoded);
                    }
                    this.multipart.addBodyPart(bodyPart);
                }
            } catch (MessagingException e) {
                throw new MailException(e);
            }
        }
        return this;
    }
    /**
     * å¢žåŠ å›¾ç‰‡ï¼Œå›¾ç‰‡çš„é”®å¯¹åº”åˆ°é‚®ä»¶æ¨¡æ¿ä¸­çš„å ä½å­—ç¬¦ä¸²ï¼Œå›¾ç‰‡ç±»åž‹é»˜è®¤ä¸º"image/jpeg"
     *
     * @param cid         å›¾ç‰‡ä¸Žå ä½ç¬¦ï¼Œå ä½ç¬¦æ ¼å¼ä¸ºcid:${cid}
     * @param imageStream å›¾ç‰‡æ–‡ä»¶
     * @return this
     * @since 4.6.3
     */
    public Mail addImage(String cid, InputStream imageStream) {
        return addImage(cid, imageStream, null);
    }
    /**
     * å¢žåŠ å›¾ç‰‡ï¼Œå›¾ç‰‡çš„é”®å¯¹åº”åˆ°é‚®ä»¶æ¨¡æ¿ä¸­çš„å ä½å­—ç¬¦ä¸²
     *
     * @param cid         å›¾ç‰‡ä¸Žå ä½ç¬¦ï¼Œå ä½ç¬¦æ ¼å¼ä¸ºcid:${cid}
     * @param imageStream å›¾ç‰‡æµï¼Œä¸å…³é—­
     * @param contentType å›¾ç‰‡ç±»åž‹ï¼Œnull赋值默认的"image/jpeg"
     * @return this
     * @since 4.6.3
     */
    public Mail addImage(String cid, InputStream imageStream, String contentType) {
        ByteArrayDataSource imgSource;
        try {
            imgSource = new ByteArrayDataSource(imageStream, ObjectUtil.defaultIfNull(contentType, "image/jpeg"));
        } catch (IOException e) {
            throw new IORuntimeException(e);
        }
        imgSource.setName(cid);
        return setAttachments(imgSource);
    }
    /**
     * å¢žåŠ å›¾ç‰‡ï¼Œå›¾ç‰‡çš„é”®å¯¹åº”åˆ°é‚®ä»¶æ¨¡æ¿ä¸­çš„å ä½å­—ç¬¦ä¸²
     *
     * @param cid       å›¾ç‰‡ä¸Žå ä½ç¬¦ï¼Œå ä½ç¬¦æ ¼å¼ä¸ºcid:${cid}
     * @param imageFile å›¾ç‰‡æ–‡ä»¶
     * @return this
     * @since 4.6.3
     */
    public Mail addImage(String cid, File imageFile) {
        InputStream in = null;
        try {
            in = FileUtil.getInputStream(imageFile);
            return addImage(cid, in, FileTypeMap.getDefaultFileTypeMap().getContentType(imageFile));
        } finally {
            IoUtil.close(in);
        }
    }
    /**
     * è®¾ç½®å­—符集编码
     *
     * @param charset å­—符集编码
     * @return this
     * @see MailAccount#setCharset(Charset)
     */
    public Mail setCharset(Charset charset) {
        this.mailAccount.setCharset(charset);
        return this;
    }
    /**
     * è®¾ç½®æ˜¯å¦ä½¿ç”¨å…¨å±€ä¼šè¯ï¼Œé»˜è®¤ä¸ºtrue
     *
     * @param isUseGlobalSession æ˜¯å¦ä½¿ç”¨å…¨å±€ä¼šè¯ï¼Œé»˜è®¤ä¸ºtrue
     * @return this
     * @since 4.0.2
     */
    public Mail setUseGlobalSession(boolean isUseGlobalSession) {
        this.useGlobalSession = isUseGlobalSession;
        return this;
    }
    /**
     * è®¾ç½®debug输出位置,可以自定义debug日志
     *
     * @param debugOutput debug输出位置
     * @return this
     * @since 5.5.6
     */
    public Mail setDebugOutput(PrintStream debugOutput) {
        this.debugOutput = debugOutput;
        return this;
    }
    // --------------------------------------------------------------- Getters and Setters end
    @Override
    public MimeMessage build() {
        try {
            return buildMsg();
        } catch (MessagingException e) {
            throw new MailException(e);
        }
    }
    /**
     * å‘送
     *
     * @return message-id
     * @throws MailException é‚®ä»¶å‘送异常
     */
    public String send() throws MailException {
        try {
            return doSend();
        } catch (MessagingException e) {
            if (e instanceof SendFailedException) {
                // å½“地址无效时,显示更加详细的无效地址信息
                final Address[] invalidAddresses = ((SendFailedException) e).getInvalidAddresses();
                final String msg = StrUtil.format("Invalid Addresses: {}", ArrayUtil.toString(invalidAddresses));
                throw new MailException(msg, e);
            }
            throw new MailException(e);
        }
    }
    // --------------------------------------------------------------- Private method start
    /**
     * æ‰§è¡Œå‘送
     *
     * @return message-id
     * @throws MessagingException å‘送异常
     */
    private String doSend() throws MessagingException {
        final MimeMessage mimeMessage = buildMsg();
        Transport.send(mimeMessage);
        return mimeMessage.getMessageID();
    }
    /**
     * æž„建消息
     *
     * @return {@link MimeMessage}消息
     * @throws MessagingException æ¶ˆæ¯å¼‚常
     */
    private MimeMessage buildMsg() throws MessagingException {
        final Charset charset = this.mailAccount.getCharset();
        final MimeMessage msg = new MimeMessage(getSession());
        // å‘件人
        final String from = this.mailAccount.getFrom();
        if (StrUtil.isEmpty(from)) {
            // ç”¨æˆ·æœªæä¾›å‘送方,则从Session中自动获取
            msg.setFrom();
        } else {
            msg.setFrom(org.dromara.common.mail.utils.InternalMailUtil.parseFirstAddress(from, charset));
        }
        // æ ‡é¢˜
        msg.setSubject(this.title, (null == charset) ? null : charset.name());
        // å‘送时间
        msg.setSentDate(new Date());
        // å†…容和附件
        msg.setContent(buildContent(charset));
        // æ”¶ä»¶äºº
        msg.setRecipients(MimeMessage.RecipientType.TO, org.dromara.common.mail.utils.InternalMailUtil.parseAddressFromStrs(this.tos, charset));
        // æŠ„送人
        if (ArrayUtil.isNotEmpty(this.ccs)) {
            msg.setRecipients(MimeMessage.RecipientType.CC, org.dromara.common.mail.utils.InternalMailUtil.parseAddressFromStrs(this.ccs, charset));
        }
        // å¯†é€äºº
        if (ArrayUtil.isNotEmpty(this.bccs)) {
            msg.setRecipients(MimeMessage.RecipientType.BCC, org.dromara.common.mail.utils.InternalMailUtil.parseAddressFromStrs(this.bccs, charset));
        }
        // å›žå¤åœ°å€(reply-to)
        if (ArrayUtil.isNotEmpty(this.reply)) {
            msg.setReplyTo(InternalMailUtil.parseAddressFromStrs(this.reply, charset));
        }
        return msg;
    }
    /**
     * æž„建邮件信息主体
     *
     * @param charset ç¼–码,{@code null}则使用{@link MimeUtility#getDefaultJavaCharset()}
     * @return é‚®ä»¶ä¿¡æ¯ä¸»ä½“
     * @throws MessagingException æ¶ˆæ¯å¼‚常
     */
    private Multipart buildContent(Charset charset) throws MessagingException {
        final String charsetStr = null != charset ? charset.name() : MimeUtility.getDefaultJavaCharset();
        // æ­£æ–‡
        final MimeBodyPart body = new MimeBodyPart();
        body.setContent(content, StrUtil.format("text/{}; charset={}", isHtml ? "html" : "plain", charsetStr));
        this.multipart.addBodyPart(body);
        return this.multipart;
    }
    /**
     * èŽ·å–é»˜è®¤é‚®ä»¶ä¼šè¯<br>
     * å¦‚果为全局单例的会话,则全局只允许一个邮件帐号,否则每次发送邮件会新建一个新的会话
     *
     * @return é‚®ä»¶ä¼šè¯ {@link Session}
     */
    private Session getSession() {
        final Session session = MailUtils.getSession(this.mailAccount, this.useGlobalSession);
        if (null != this.debugOutput) {
            session.setDebugOut(debugOutput);
        }
        return session;
    }
    // --------------------------------------------------------------- Private method end
}
ruoyi-common/ruoyi-common-mail/src/main/java/org/dromara/common/mail/utils/MailUtils.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,469 @@
package org.dromara.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 org.dromara.common.core.utils.SpringUtils;
import org.dromara.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 org.dromara.common.mail.utils.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/org/dromara/common/mail/utils/UserPassAuthenticator.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,33 @@
package org.dromara.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
@@ -1 +1 @@
com.ruoyi.common.mail.config.MailConfig
org.dromara.common.mail.config.MailConfig
ruoyi-common/ruoyi-common-mybatis/pom.xml
@@ -3,7 +3,7 @@
         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>
        <groupId>org.dromara</groupId>
        <artifactId>ruoyi-common</artifactId>
        <version>${revision}</version>
        <relativePath>../pom.xml</relativePath>
@@ -18,12 +18,12 @@
    <dependencies>
        <dependency>
            <groupId>com.ruoyi</groupId>
            <groupId>org.dromara</groupId>
            <artifactId>ruoyi-common-core</artifactId>
        </dependency>
        <dependency>
            <groupId>com.ruoyi</groupId>
            <groupId>org.dromara</groupId>
            <artifactId>ruoyi-common-satoken</artifactId>
        </dependency>
ruoyi-common/ruoyi-common-mybatis/src/main/java/com/ruoyi/common/mybatis/annotation/DataColumn.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-mybatis/src/main/java/com/ruoyi/common/mybatis/annotation/DataPermission.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-mybatis/src/main/java/com/ruoyi/common/mybatis/config/MybatisPlusConfig.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-mybatis/src/main/java/com/ruoyi/common/mybatis/core/domain/BaseEntity.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-mybatis/src/main/java/com/ruoyi/common/mybatis/core/mapper/BaseMapperPlus.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-mybatis/src/main/java/com/ruoyi/common/mybatis/core/page/PageQuery.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-mybatis/src/main/java/com/ruoyi/common/mybatis/core/page/TableDataInfo.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-mybatis/src/main/java/com/ruoyi/common/mybatis/enums/DataBaseType.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-mybatis/src/main/java/com/ruoyi/common/mybatis/enums/DataScopeType.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-mybatis/src/main/java/com/ruoyi/common/mybatis/handler/InjectionMetaObjectHandler.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-mybatis/src/main/java/com/ruoyi/common/mybatis/handler/MybatisExceptionHandler.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-mybatis/src/main/java/com/ruoyi/common/mybatis/handler/PlusDataPermissionHandler.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-mybatis/src/main/java/com/ruoyi/common/mybatis/helper/DataBaseHelper.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-mybatis/src/main/java/com/ruoyi/common/mybatis/helper/DataPermissionHelper.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-mybatis/src/main/java/com/ruoyi/common/mybatis/interceptor/PlusDataPermissionInterceptor.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/annotation/DataColumn.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,28 @@
package org.dromara.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/org/dromara/common/mybatis/annotation/DataPermission.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,18 @@
package org.dromara.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/org/dromara/common/mybatis/config/MybatisPlusConfig.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,102 @@
package org.dromara.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 org.dromara.common.mybatis.handler.InjectionMetaObjectHandler;
import org.dromara.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 InjectionMetaObjectHandler();
    }
    /**
     * ä½¿ç”¨ç½‘卡信息绑定雪花生成器
     * é˜²æ­¢é›†ç¾¤é›ªèб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/org/dromara/common/mybatis/core/domain/BaseEntity.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,71 @@
package org.dromara.common.mybatis.core.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 Long createDept;
    /**
     * åˆ›å»ºè€…
     */
    @TableField(fill = FieldFill.INSERT)
    private Long createBy;
    /**
     * åˆ›å»ºæ—¶é—´
     */
    @TableField(fill = FieldFill.INSERT)
    private Date createTime;
    /**
     * æ›´æ–°è€…
     */
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private Long 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-mybatis/src/main/java/org/dromara/common/mybatis/core/mapper/BaseMapperPlus.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,198 @@
package org.dromara.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.ReflectionKit;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.toolkit.Db;
import org.dromara.common.core.utils.MapstructUtils;
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;
import java.util.Objects;
import java.util.function.Function;
import java.util.stream.Collectors;
/**
 * è‡ªå®šä¹‰ Mapper æŽ¥å£, å®žçް è‡ªå®šä¹‰æ‰©å±•
 *
 * @param <T> table æ³›åž‹
 * @param <V> vo æ³›åž‹
 * @author Lion Li
 * @since 2021-05-13
 */
@SuppressWarnings("unchecked")
public interface BaseMapperPlus<T, V> extends BaseMapper<T> {
    Log log = LogFactory.getLog(BaseMapperPlus.class);
    default Class<V> currentVoClass() {
        return (Class<V>) ReflectionKit.getSuperClassGenericType(this.getClass(), BaseMapperPlus.class, 1);
    }
    default Class<T> currentModelClass() {
        return (Class<T>) 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 MapstructUtils.convert(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 MapstructUtils.convert(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 MapstructUtils.convert(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 MapstructUtils.convert(obj, voClass);
    }
    default List<V> selectVoList() {
        return selectVoList(new QueryWrapper<>(), this.currentVoClass());
    }
    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 MapstructUtils.convert(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(MapstructUtils.convert(pageData.getRecords(), voClass));
        return (P) voPage;
    }
    default <C> List<C> selectObjs(Wrapper<T> wrapper, Function<? super Object, C> mapper) {
        return this.selectObjs(wrapper).stream().filter(Objects::nonNull).map(mapper).collect(Collectors.toList());
    }
}
ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/core/page/PageQuery.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,114 @@
package org.dromara.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 org.dromara.common.core.exception.ServiceException;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.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(StringUtils.SEPARATOR);
        String[] isAscArr = isAsc.split(StringUtils.SEPARATOR);
        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/org/dromara/common/mybatis/core/page/TableDataInfo.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,81 @@
package org.dromara.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/org/dromara/common/mybatis/enums/DataBaseType.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,49 @@
package org.dromara.common.mybatis.enums;
import org.dromara.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/org/dromara/common/mybatis/enums/DataScopeType.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,73 @@
package org.dromara.common.mybatis.enums;
import org.dromara.common.core.utils.StringUtils;
import lombok.AllArgsConstructor;
import lombok.Getter;
import org.dromara.common.mybatis.helper.DataPermissionHelper;
/**
 * æ•°æ®æƒé™ç±»åž‹
 * <p>
 * è¯­æ³•支持 spel æ¨¡æ¿è¡¨è¾¾å¼
 * <p>
 * å†…置数据 user å½“前用户 å†…容参考 LoginUser
 * å¦‚需扩展数据 å¯ä½¿ç”¨ {@link 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/org/dromara/common/mybatis/handler/InjectionMetaObjectHandler.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,81 @@
package org.dromara.common.mybatis.handler;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.http.HttpStatus;
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import org.dromara.common.core.domain.model.LoginUser;
import org.dromara.common.core.exception.ServiceException;
import org.dromara.common.mybatis.core.domain.BaseEntity;
import org.dromara.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 InjectionMetaObjectHandler implements MetaObjectHandler {
    @Override
    public void insertFill(MetaObject metaObject) {
        try {
            if (ObjectUtil.isNotNull(metaObject) && metaObject.getOriginalObject() instanceof BaseEntity baseEntity) {
                Date current = ObjectUtil.isNotNull(baseEntity.getCreateTime())
                    ? baseEntity.getCreateTime() : new Date();
                baseEntity.setCreateTime(current);
                baseEntity.setUpdateTime(current);
                LoginUser loginUser = getLoginUser();
                if (ObjectUtil.isNotNull(loginUser)) {
                    Long userId = ObjectUtil.isNotNull(baseEntity.getCreateBy())
                        ? baseEntity.getCreateBy() : loginUser.getUserId();
                    // å½“前已登录 ä¸” åˆ›å»ºäººä¸ºç©º åˆ™å¡«å……
                    baseEntity.setCreateBy(userId);
                    // å½“前已登录 ä¸” æ›´æ–°äººä¸ºç©º åˆ™å¡«å……
                    baseEntity.setUpdateBy(userId);
                    baseEntity.setCreateDept(ObjectUtil.isNotNull(baseEntity.getCreateDept())
                        ? baseEntity.getCreateDept() : loginUser.getDeptId());
                }
            }
        } 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) {
                Date current = new Date();
                // æ›´æ–°æ—¶é—´å¡«å……(不管为不为空)
                baseEntity.setUpdateTime(current);
                LoginUser loginUser = getLoginUser();
                // å½“前已登录 æ›´æ–°äººå¡«å……(不管为不为空)
                if (ObjectUtil.isNotNull(loginUser)) {
                    baseEntity.setUpdateBy(loginUser.getUserId());
                }
            }
        } catch (Exception e) {
            throw new ServiceException("自动注入异常 => " + e.getMessage(), HttpStatus.HTTP_UNAUTHORIZED);
        }
    }
    /**
     * èŽ·å–ç™»å½•ç”¨æˆ·å
     */
    private LoginUser getLoginUser() {
        LoginUser loginUser;
        try {
            loginUser = LoginHelper.getLoginUser();
        } catch (Exception e) {
            log.warn("自动注入警告 => ç”¨æˆ·æœªç™»å½•");
            return null;
        }
        return loginUser;
    }
}
ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/handler/MybatisExceptionHandler.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,46 @@
package org.dromara.common.mybatis.handler;
import org.dromara.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/org/dromara/common/mybatis/handler/PlusDataPermissionHandler.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,198 @@
package org.dromara.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 org.dromara.common.core.domain.dto.RoleDTO;
import org.dromara.common.core.domain.model.LoginUser;
import org.dromara.common.core.exception.ServiceException;
import org.dromara.common.core.utils.SpringUtils;
import org.dromara.common.core.utils.StreamUtils;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.mybatis.annotation.DataColumn;
import org.dromara.common.mybatis.annotation.DataPermission;
import org.dromara.common.mybatis.enums.DataScopeType;
import org.dromara.common.mybatis.helper.DataPermissionHelper;
import org.dromara.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;
/**
 * æ•°æ®æƒé™è¿‡æ»¤
 *
 * @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.isSuperAdmin() || LoginHelper.isTenantAdmin()) {
            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)).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/org/dromara/common/mybatis/helper/DataBaseHelper.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,72 @@
package org.dromara.common.mybatis.helper;
import cn.hutool.core.convert.Convert;
import com.baomidou.dynamic.datasource.DynamicRoutingDataSource;
import org.dromara.common.core.exception.ServiceException;
import org.dromara.common.core.utils.SpringUtils;
import org.dromara.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(',%s,' , ','+%s+',') <> 0".formatted(var, var2);
        } else if (dataBasyType == DataBaseType.POSTGRE_SQL) {
            // (select position(',100,' in ',0,100,101,')) <> 0
            return "(select position(',%s,' in ','||%s||',')) <> 0".formatted(var, var2);
        } else if (dataBasyType == DataBaseType.ORACLE) {
            // instr(',0,100,101,' , ',100,') <> 0
            return "instr(','||%s||',' , ',%s,') <> 0".formatted(var2, var);
        }
        // find_in_set(100 , '0,100,101')
        return "find_in_set('%s' , %s) <> 0".formatted(var, var2);
    }
}
ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/helper/DataPermissionHelper.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,64 @@
package org.dromara.common.mybatis.helper;
import cn.dev33.satoken.context.SaHolder;
import cn.dev33.satoken.context.model.SaStorage;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.plugins.IgnoreStrategy;
import com.baomidou.mybatisplus.core.plugins.InterceptorIgnoreHelper;
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 map) {
            return map;
        }
        throw new NullPointerException("data permission context type exception");
    }
    /**
     * å¼€å¯å¿½ç•¥æ•°æ®æƒé™(开启后需手动调用 {@link #disableIgnore()} å…³é—­)
     */
    public static void enableIgnore() {
        InterceptorIgnoreHelper.handle(IgnoreStrategy.builder().dataPermission(true).build());
    }
    /**
     * å…³é—­å¿½ç•¥æ•°æ®æƒé™
     */
    public static void disableIgnore() {
        InterceptorIgnoreHelper.clearIgnoreStrategy();
    }
}
ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/interceptor/PlusDataPermissionInterceptor.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,107 @@
package org.dromara.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 org.dromara.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) {
            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
@@ -1 +1 @@
com.ruoyi.common.mybatis.config.MybatisPlusConfig
org.dromara.common.mybatis.config.MybatisPlusConfig
ruoyi-common/ruoyi-common-oss/pom.xml
@@ -3,7 +3,7 @@
         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>
        <groupId>org.dromara</groupId>
        <artifactId>ruoyi-common</artifactId>
        <version>${revision}</version>
        <relativePath>../pom.xml</relativePath>
@@ -18,12 +18,12 @@
    <dependencies>
        <dependency>
            <groupId>com.ruoyi</groupId>
            <groupId>org.dromara</groupId>
            <artifactId>ruoyi-common-json</artifactId>
        </dependency>
        <dependency>
            <groupId>com.ruoyi</groupId>
            <groupId>org.dromara</groupId>
            <artifactId>ruoyi-common-redis</artifactId>
        </dependency>
ruoyi-common/ruoyi-common-oss/src/main/java/com/ruoyi/common/oss/constant/OssConstant.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-oss/src/main/java/com/ruoyi/common/oss/core/OssClient.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-oss/src/main/java/com/ruoyi/common/oss/entity/UploadResult.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-oss/src/main/java/com/ruoyi/common/oss/enumd/AccessPolicyType.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-oss/src/main/java/com/ruoyi/common/oss/enumd/PolicyType.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-oss/src/main/java/com/ruoyi/common/oss/exception/OssException.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-oss/src/main/java/com/ruoyi/common/oss/factory/OssFactory.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-oss/src/main/java/com/ruoyi/common/oss/properties/OssProperties.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-oss/src/main/java/org/dromara/common/oss/constant/OssConstant.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,38 @@
package org.dromara.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/org/dromara/common/oss/core/OssClient.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,245 @@
package org.dromara.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 org.dromara.common.core.utils.DateUtils;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.oss.constant.OssConstant;
import org.dromara.common.oss.entity.UploadResult;
import org.dromara.common.oss.enumd.AccessPolicyType;
import org.dromara.common.oss.enumd.PolicyType;
import org.dromara.common.oss.exception.OssException;
import org.dromara.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;
    }
    /**
     * èŽ·å–ç§æœ‰URL链接
     *
     * @param objectKey å¯¹è±¡KEY
     * @param second    æŽˆæƒæ—¶é—´
     */
    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();
    }
    /**
     * æ£€æŸ¥é…ç½®æ˜¯å¦ç›¸åŒ
     */
    public boolean checkPropertiesSame(OssProperties properties) {
        return this.properties.equals(properties);
    }
    /**
     * èŽ·å–å½“å‰æ¡¶æƒé™ç±»åž‹
     *
     * @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");
        builder.append(switch (policyType) {
            case WRITE -> "\"s3:GetBucketLocation\",\n\"s3:ListBucketMultipartUploads\"\n";
            case READ_WRITE -> "\"s3:GetBucketLocation\",\n\"s3:ListBucket\",\n\"s3:ListBucketMultipartUploads\"\n";
            default -> "\"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\": ");
        builder.append(switch (policyType) {
            case WRITE -> "[\n\"s3:AbortMultipartUpload\",\n\"s3:DeleteObject\",\n\"s3:ListMultipartUploadParts\",\n\"s3:PutObject\"\n],\n";
            case READ_WRITE -> "[\n\"s3:AbortMultipartUpload\",\n\"s3:DeleteObject\",\n\"s3:GetObject\",\n\"s3:ListMultipartUploadParts\",\n\"s3:PutObject\"\n],\n";
            default -> "\"s3:GetObject\",\n";
        });
        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/org/dromara/common/oss/entity/UploadResult.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,24 @@
package org.dromara.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/org/dromara/common/oss/enumd/AccessPolicyType.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,55 @@
package org.dromara.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/org/dromara/common/oss/enumd/PolicyType.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,35 @@
package org.dromara.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/org/dromara/common/oss/exception/OssException.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,19 @@
package org.dromara.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/org/dromara/common/oss/factory/OssFactory.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,63 @@
package org.dromara.common.oss.factory;
import org.dromara.common.core.constant.CacheNames;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.json.utils.JsonUtils;
import org.dromara.common.oss.constant.OssConstant;
import org.dromara.common.oss.core.OssClient;
import org.dromara.common.oss.exception.OssException;
import org.dromara.common.oss.properties.OssProperties;
import org.dromara.common.redis.utils.CacheUtils;
import org.dromara.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 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) {
        String json = CacheUtils.get(CacheNames.SYS_OSS_CONFIG, configKey);
        if (json == null) {
            throw new OssException("系统异常, '" + configKey + "'配置信息不存在!");
        }
        OssProperties properties = JsonUtils.parseObject(json, OssProperties.class);
        OssClient client = CLIENT_CACHE.get(configKey);
        if (client == null) {
            CLIENT_CACHE.put(configKey, new OssClient(configKey, properties));
            log.info("创建OSS实例 key => {}", configKey);
            return CLIENT_CACHE.get(configKey);
        }
        // é…ç½®ä¸ç›¸åŒåˆ™é‡æ–°æž„建
        if (!client.checkPropertiesSame(properties)) {
            CLIENT_CACHE.put(configKey, new OssClient(configKey, properties));
            log.info("重载OSS实例 key => {}", configKey);
            return CLIENT_CACHE.get(configKey);
        }
        return client;
    }
}
ruoyi-common/ruoyi-common-oss/src/main/java/org/dromara/common/oss/properties/OssProperties.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,58 @@
package org.dromara.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
@@ -3,7 +3,7 @@
         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>
        <groupId>org.dromara</groupId>
        <artifactId>ruoyi-common</artifactId>
        <version>${revision}</version>
        <relativePath>../pom.xml</relativePath>
@@ -18,12 +18,12 @@
    <dependencies>
        <dependency>
            <groupId>com.ruoyi</groupId>
            <groupId>org.dromara</groupId>
            <artifactId>ruoyi-common-core</artifactId>
        </dependency>
        <dependency>
            <groupId>com.ruoyi</groupId>
            <groupId>org.dromara</groupId>
            <artifactId>ruoyi-common-redis</artifactId>
        </dependency>
    </dependencies>
ruoyi-common/ruoyi-common-ratelimiter/src/main/java/com/ruoyi/common/ratelimiter/annotation/RateLimiter.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-ratelimiter/src/main/java/com/ruoyi/common/ratelimiter/aspectj/RateLimiterAspect.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-ratelimiter/src/main/java/com/ruoyi/common/ratelimiter/config/RateLimiterConfig.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-ratelimiter/src/main/java/com/ruoyi/common/ratelimiter/enums/LimitType.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-ratelimiter/src/main/java/org/dromara/common/ratelimiter/annotation/RateLimiter.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,41 @@
package org.dromara.common.ratelimiter.annotation;
import org.dromara.common.ratelimiter.enums.LimitType;
import java.lang.annotation.*;
/**
 * é™æµæ³¨è§£
 *
 * @author Lion Li
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RateLimiter {
    /**
     * é™æµkey,支持使用Spring el表达式来动态获取方法上的参数值
     * æ ¼å¼ç±»ä¼¼äºŽ  #code.id #{#code}
     */
    String key() default "";
    /**
     * é™æµæ—¶é—´,单位秒
     */
    int time() default 60;
    /**
     * é™æµæ¬¡æ•°
     */
    int count() default 100;
    /**
     * é™æµç±»åž‹
     */
    LimitType limitType() default LimitType.DEFAULT;
    /**
     * æç¤ºæ¶ˆæ¯ æ”¯æŒå›½é™…化 æ ¼å¼ä¸º {code}
     */
    String message() default "{rate.limiter.message}";
}
ruoyi-common/ruoyi-common-ratelimiter/src/main/java/org/dromara/common/ratelimiter/aspectj/RateLimiterAspect.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,119 @@
package org.dromara.common.ratelimiter.aspectj;
import cn.hutool.core.util.ArrayUtil;
import org.dromara.common.core.constant.GlobalConstants;
import org.dromara.common.core.exception.ServiceException;
import org.dromara.common.core.utils.MessageUtils;
import org.dromara.common.core.utils.ServletUtils;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.ratelimiter.annotation.RateLimiter;
import org.dromara.common.ratelimiter.enums.LimitType;
import org.dromara.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 org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.core.ParameterNameDiscoverer;
import org.springframework.expression.EvaluationContext;
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;
/**
 * é™æµå¤„理
 *
 * @author Lion Li
 */
@Slf4j
@Aspect
public class RateLimiterAspect {
    /**
     * å®šä¹‰spel表达式解析器
     */
    private final ExpressionParser parser = new SpelExpressionParser();
    /**
     * å®šä¹‰spel解析模版
     */
    private final ParserContext parserContext = new TemplateParserContext();
    /**
     * å®šä¹‰spel上下文对象进行解析
     */
    private final EvaluationContext context = new StandardEvaluationContext();
    /**
     * æ–¹æ³•参数解析器
     */
    private final ParameterNameDiscoverer pnd = new DefaultParameterNameDiscoverer();
    @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) {
                String message = rateLimiter.message();
                if (StringUtils.startsWith(message, "{") && StringUtils.endsWith(message, "}")) {
                    message = MessageUtils.message(StringUtils.substring(message, 1, message.length() - 1));
                }
                throw new ServiceException(message);
            }
            log.info("限制令牌 => {}, å‰©ä½™ä»¤ç‰Œ => {}, ç¼“å­˜key => '{}'", count, number, combineKey);
        } catch (Exception e) {
            if (e instanceof ServiceException) {
                throw e;
            } else {
                throw new RuntimeException("服务器限流异常,请稍候再试");
            }
        }
    }
    public String getCombineKey(RateLimiter rateLimiter, JoinPoint point) {
        String key = rateLimiter.key();
        // èŽ·å–æ–¹æ³•(通过方法签名来获取)
        MethodSignature signature = (MethodSignature) point.getSignature();
        Method method = signature.getMethod();
        Class<?> targetClass = method.getDeclaringClass();
        // åˆ¤æ–­æ˜¯å¦æ˜¯spel格式
        if (StringUtils.containsAny(key, "#")) {
            // èŽ·å–å‚æ•°å€¼
            Object[] args = point.getArgs();
            // èŽ·å–æ–¹æ³•ä¸Šå‚æ•°çš„åç§°
            String[] parameterNames = pnd.getParameterNames(method);
            if (ArrayUtil.isEmpty(parameterNames)) {
                throw new ServiceException("限流key解析异常!请联系管理员!");
            }
            for (int i = 0; i < parameterNames.length; i++) {
                context.setVariable(parameterNames[i], args[i]);
            }
            // è§£æžè¿”回给key
            try {
                key = parser.parseExpression(key, parserContext).getValue(context, String.class) + ":";
            } catch (Exception e) {
                throw new ServiceException("限流key解析异常!请联系管理员!");
            }
        }
        StringBuilder stringBuffer = new StringBuilder(GlobalConstants.RATE_LIMIT_KEY);
        stringBuffer.append(ServletUtils.getRequest().getRequestURI()).append(":");
        if (rateLimiter.limitType() == LimitType.IP) {
            // èŽ·å–è¯·æ±‚ip
            stringBuffer.append(ServletUtils.getClientIP()).append(":");
        } else if (rateLimiter.limitType() == LimitType.CLUSTER) {
            // èŽ·å–å®¢æˆ·ç«¯å®žä¾‹id
            stringBuffer.append(RedisUtils.getClient().getId()).append(":");
        }
        return stringBuffer.append(key).toString();
    }
}
ruoyi-common/ruoyi-common-ratelimiter/src/main/java/org/dromara/common/ratelimiter/config/RateLimiterConfig.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,20 @@
package org.dromara.common.ratelimiter.config;
import org.dromara.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/org/dromara/common/ratelimiter/enums/LimitType.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,24 @@
package org.dromara.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
@@ -1 +1 @@
com.ruoyi.common.ratelimiter.config.RateLimiterConfig
org.dromara.common.ratelimiter.config.RateLimiterConfig
ruoyi-common/ruoyi-common-redis/pom.xml
@@ -3,7 +3,7 @@
         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>
        <groupId>org.dromara</groupId>
        <artifactId>ruoyi-common</artifactId>
        <version>${revision}</version>
        <relativePath>../pom.xml</relativePath>
@@ -19,7 +19,7 @@
    <dependencies>
        <!-- RuoYi Common Core-->
        <dependency>
            <groupId>com.ruoyi</groupId>
            <groupId>org.dromara</groupId>
            <artifactId>ruoyi-common-core</artifactId>
        </dependency>
ruoyi-common/ruoyi-common-redis/src/main/java/com/ruoyi/common/redis/config/RedisConfig.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-redis/src/main/java/com/ruoyi/common/redis/config/properties/RedissonProperties.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-redis/src/main/java/com/ruoyi/common/redis/handler/KeyPrefixHandler.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-redis/src/main/java/com/ruoyi/common/redis/manager/PlusSpringCacheManager.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-redis/src/main/java/com/ruoyi/common/redis/utils/CacheUtils.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-redis/src/main/java/com/ruoyi/common/redis/utils/QueueUtils.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-redis/src/main/java/com/ruoyi/common/redis/utils/RedisUtils.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/config/RedisConfig.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,130 @@
package org.dromara.common.redis.config;
import cn.hutool.core.util.ObjectUtil;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.dromara.common.redis.config.properties.RedissonProperties;
import org.dromara.common.redis.handler.KeyPrefixHandler;
import org.dromara.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/org/dromara/common/redis/config/properties/RedissonProperties.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,135 @@
package org.dromara.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;
/**
 * Redisson é…ç½®å±žæ€§
 *
 * @author Lion Li
 */
@Data
@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/org/dromara/common/redis/handler/KeyPrefixHandler.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,50 @@
package org.dromara.common.redis.handler;
import org.dromara.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/org/dromara/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 org.dromara.common.redis.manager;
import org.dromara.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/org/dromara/common/redis/utils/CacheUtils.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,75 @@
package org.dromara.common.redis.utils;
import org.dromara.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/org/dromara/common/redis/utils/QueueUtils.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,180 @@
package org.dromara.common.redis.utils;
import org.dromara.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/org/dromara/common/redis/utils/RedisUtils.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,462 @@
package org.dromara.common.redis.utils;
import org.dromara.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
@@ -1 +1 @@
com.ruoyi.common.redis.config.RedisConfig
org.dromara.common.redis.config.RedisConfig
ruoyi-common/ruoyi-common-satoken/pom.xml
@@ -3,7 +3,7 @@
         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>
        <groupId>org.dromara</groupId>
        <artifactId>ruoyi-common</artifactId>
        <version>${revision}</version>
        <relativePath>../pom.xml</relativePath>
@@ -15,13 +15,13 @@
    <dependencies>
        <dependency>
            <groupId>com.ruoyi</groupId>
            <groupId>org.dromara</groupId>
            <artifactId>ruoyi-common-core</artifactId>
        </dependency>
        <!-- RuoYi Common Redis-->
        <dependency>
            <groupId>com.ruoyi</groupId>
            <groupId>org.dromara</groupId>
            <artifactId>ruoyi-common-redis</artifactId>
        </dependency>
ruoyi-common/ruoyi-common-satoken/src/main/java/com/ruoyi/common/satoken/config/SaTokenConfig.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-satoken/src/main/java/com/ruoyi/common/satoken/core/dao/PlusSaTokenDao.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-satoken/src/main/java/com/ruoyi/common/satoken/core/service/SaPermissionImpl.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-satoken/src/main/java/com/ruoyi/common/satoken/listener/UserActionListener.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-satoken/src/main/java/com/ruoyi/common/satoken/utils/LoginHelper.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-satoken/src/main/java/org/dromara/common/satoken/config/SaTokenConfig.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,43 @@
package org.dromara.common.satoken.config;
import cn.dev33.satoken.dao.SaTokenDao;
import cn.dev33.satoken.jwt.StpLogicJwtForSimple;
import cn.dev33.satoken.stp.StpInterface;
import cn.dev33.satoken.stp.StpLogic;
import org.dromara.common.satoken.core.dao.PlusSaTokenDao;
import org.dromara.common.satoken.core.service.SaPermissionImpl;
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();
    }
    /**
     * æƒé™æŽ¥å£å®žçް(使用bean注入方便用户替换)
     */
    @Bean
    public StpInterface stpInterface() {
        return new SaPermissionImpl();
    }
    /**
     * è‡ªå®šä¹‰dao层存储
     */
    @Bean
    public SaTokenDao saTokenDao() {
        return new PlusSaTokenDao();
    }
}
ruoyi-common/ruoyi-common-satoken/src/main/java/org/dromara/common/satoken/core/dao/PlusSaTokenDao.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,176 @@
package org.dromara.common.satoken.core.dao;
import cn.dev33.satoken.dao.SaTokenDao;
import cn.dev33.satoken.util.SaFoxUtil;
import org.dromara.common.redis.utils.RedisUtils;
import java.time.Duration;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
/**
 * Sa-Token持久层接口(使用框架自带RedisUtils实现 åè®®ç»Ÿä¸€)
 *
 * @author Lion Li
 */
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/org/dromara/common/satoken/core/service/SaPermissionImpl.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,47 @@
package org.dromara.common.satoken.core.service;
import cn.dev33.satoken.stp.StpInterface;
import org.dromara.common.core.domain.model.LoginUser;
import org.dromara.common.core.enums.UserType;
import org.dromara.common.satoken.utils.LoginHelper;
import java.util.ArrayList;
import java.util.List;
/**
 * sa-token æƒé™ç®¡ç†å®žçŽ°ç±»
 *
 * @author Lion Li
 */
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/org/dromara/common/satoken/listener/UserActionListener.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,139 @@
package org.dromara.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 org.dromara.common.core.constant.CacheConstants;
import org.dromara.common.core.domain.dto.UserOnlineDTO;
import org.dromara.common.core.domain.model.LoginUser;
import org.dromara.common.core.enums.UserType;
import org.dromara.common.redis.utils.RedisUtils;
import org.dromara.common.satoken.utils.LoginHelper;
import org.dromara.common.core.utils.ip.AddressUtils;
import org.dromara.common.core.utils.ServletUtils;
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 doKickout, 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/org/dromara/common/satoken/utils/LoginHelper.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,172 @@
package org.dromara.common.satoken.utils;
import cn.dev33.satoken.context.SaHolder;
import cn.dev33.satoken.context.model.SaStorage;
import cn.dev33.satoken.stp.SaLoginModel;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.ObjectUtil;
import org.dromara.common.core.constant.TenantConstants;
import org.dromara.common.core.constant.UserConstants;
import org.dromara.common.core.domain.model.LoginUser;
import org.dromara.common.core.enums.DeviceType;
import org.dromara.common.core.enums.UserType;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import java.util.Set;
/**
 * ç™»å½•鉴权助手
 * <p>
 * user_type ä¸º ç”¨æˆ·ç±»åž‹ åŒä¸€ä¸ªç”¨æˆ·è¡¨ å¯ä»¥æœ‰å¤šç§ç”¨æˆ·ç±»åž‹ ä¾‹å¦‚ pc,app
 * deivce ä¸º è®¾å¤‡ç±»åž‹ åŒä¸€ä¸ªç”¨æˆ·ç±»åž‹ å¯ä»¥æœ‰ å¤šç§è®¾å¤‡ç±»åž‹ ä¾‹å¦‚ web,ios
 * å¯ä»¥ç»„成 ç”¨æˆ·ç±»åž‹ä¸Žè®¾å¤‡ç±»åž‹å¤šå¯¹å¤šçš„ æƒé™çµæ´»æŽ§åˆ¶
 * <p>
 * å¤šç”¨æˆ·ä½“ç³» é’ˆå¯¹ å¤šç§ç”¨æˆ·ç±»åž‹ ä½†æƒé™æŽ§åˆ¶ä¸ä¸€è‡´
 * å¯ä»¥ç»„成 å¤šç”¨æˆ·ç±»åž‹è¡¨ä¸Žå¤šè®¾å¤‡ç±»åž‹ åˆ†åˆ«æŽ§åˆ¶æƒé™
 *
 * @author Lion Li
 */
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class LoginHelper {
    public static final String LOGIN_USER_KEY = "loginUser";
    public static final String TENANT_KEY = "tenantId";
    public static final String USER_KEY = "userId";
    /**
     * ç™»å½•系统
     *
     * @param loginUser ç™»å½•用户信息
     */
    public static void login(LoginUser loginUser) {
        loginByDevice(loginUser, null);
    }
    /**
     * ç™»å½•系统 åŸºäºŽ è®¾å¤‡ç±»åž‹
     * é’ˆå¯¹ç›¸åŒç”¨æˆ·ä½“系不同设备
     *
     * @param loginUser ç™»å½•用户信息
     */
    public static void loginByDevice(LoginUser loginUser, DeviceType deviceType) {
        SaStorage storage = SaHolder.getStorage();
        storage.set(LOGIN_USER_KEY, loginUser);
        storage.set(TENANT_KEY, loginUser.getTenantId());
        storage.set(USER_KEY, loginUser.getUserId());
        SaLoginModel model = new SaLoginModel();
        if (ObjectUtil.isNotNull(deviceType)) {
            model.setDevice(deviceType.getDevice());
        }
        StpUtil.login(loginUser.getLoginId(),
            model.setExtra(TENANT_KEY, loginUser.getTenantId())
                .setExtra(USER_KEY, loginUser.getUserId()));
        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;
    }
    /**
     * èŽ·å–ç”¨æˆ·åŸºäºŽtoken
     */
    public static LoginUser getLoginUser(String token) {
        return (LoginUser) StpUtil.getTokenSessionByToken(token).get(LOGIN_USER_KEY);
    }
    /**
     * èŽ·å–ç”¨æˆ·id
     */
    public static Long getUserId() {
        Long userId;
        try {
            userId = Convert.toLong(SaHolder.getStorage().get(USER_KEY));
            if (ObjectUtil.isNull(userId)) {
                userId = Convert.toLong(StpUtil.getExtra(USER_KEY));
                SaHolder.getStorage().set(USER_KEY, userId);
            }
        } catch (Exception e) {
            return null;
        }
        return userId;
    }
    /**
     * èŽ·å–ç§Ÿæˆ·ID
     */
    public static String getTenantId() {
        String tenantId;
        try {
            tenantId = (String) SaHolder.getStorage().get(TENANT_KEY);
            if (ObjectUtil.isNull(tenantId)) {
                tenantId = (String) StpUtil.getExtra(TENANT_KEY);
                SaHolder.getStorage().set(TENANT_KEY, tenantId);
            }
        } catch (Exception e) {
            return null;
        }
        return tenantId;
    }
    /**
     * èŽ·å–éƒ¨é—¨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 isSuperAdmin(Long userId) {
        return UserConstants.SUPER_ADMIN_ID.equals(userId);
    }
    public static boolean isSuperAdmin() {
        return isSuperAdmin(getUserId());
    }
    /**
     * æ˜¯å¦ä¸ºè¶…级管理员
     *
     * @param rolePermission è§’色权限标识组
     * @return ç»“æžœ
     */
    public static boolean isTenantAdmin(Set<String> rolePermission) {
        return rolePermission.contains(TenantConstants.TENANT_ADMIN_ROLE_KEY);
    }
    public static boolean isTenantAdmin() {
        return isTenantAdmin(getLoginUser().getRolePermission());
    }
}
ruoyi-common/ruoyi-common-satoken/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
@@ -1 +1 @@
com.ruoyi.common.satoken.config.SaTokenConfig
org.dromara.common.satoken.config.SaTokenConfig
ruoyi-common/ruoyi-common-security/pom.xml
@@ -3,7 +3,7 @@
         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>
        <groupId>org.dromara</groupId>
        <artifactId>ruoyi-common</artifactId>
        <version>${revision}</version>
        <relativePath>../pom.xml</relativePath>
@@ -18,7 +18,7 @@
    <dependencies>
        <dependency>
            <groupId>com.ruoyi</groupId>
            <groupId>org.dromara</groupId>
            <artifactId>ruoyi-common-satoken</artifactId>
        </dependency>
ruoyi-common/ruoyi-common-security/src/main/java/com/ruoyi/common/security/config/SecurityConfig.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-security/src/main/java/com/ruoyi/common/security/config/properties/SecurityProperties.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-security/src/main/java/com/ruoyi/common/security/handler/AllUrlHandler.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-security/src/main/java/com/ruoyi/common/security/handler/GlobalExceptionHandler.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-security/src/main/java/org/dromara/common/security/config/SecurityConfig.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,59 @@
package org.dromara.common.security.config;
import cn.dev33.satoken.interceptor.SaInterceptor;
import cn.dev33.satoken.router.SaRouter;
import cn.dev33.satoken.stp.StpUtil;
import org.dromara.common.core.utils.SpringUtils;
import org.dromara.common.security.config.properties.SecurityProperties;
import org.dromara.common.security.handler.AllUrlHandler;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/**
 * æƒé™å®‰å…¨é…ç½®
 *
 * @author Lion Li
 */
@Slf4j
@AutoConfiguration
@EnableConfigurationProperties(SecurityProperties.class)
@RequiredArgsConstructor
public class SecurityConfig implements WebMvcConfigurer {
    private final SecurityProperties securityProperties;
    /**
     * æ³¨å†Œsa-token的拦截器
     */
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        // æ³¨å†Œè·¯ç”±æ‹¦æˆªå™¨ï¼Œè‡ªå®šä¹‰éªŒè¯è§„则
        registry.addInterceptor(new SaInterceptor(handler -> {
            AllUrlHandler allUrlHandler = SpringUtils.getBean(AllUrlHandler.class);
            // ç™»å½•验证 -- æŽ’除多个路径
            SaRouter
                // èŽ·å–æ‰€æœ‰çš„
                .match(allUrlHandler.getUrls())
                // å¯¹æœªæŽ’除的路径进行检查
                .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/org/dromara/common/security/config/properties/SecurityProperties.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,21 @@
package org.dromara.common.security.config.properties;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
 * Security é…ç½®å±žæ€§
 *
 * @author Lion Li
 */
@Data
@ConfigurationProperties(prefix = "security")
public class SecurityProperties {
    /**
     * æŽ’除路径
     */
    private String[] excludes;
}
ruoyi-common/ruoyi-common-security/src/main/java/org/dromara/common/security/handler/AllUrlHandler.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,39 @@
package org.dromara.common.security.handler;
import cn.hutool.core.util.ReUtil;
import org.dromara.common.core.utils.SpringUtils;
import lombok.Data;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
import java.util.*;
import java.util.regex.Pattern;
/**
 * èŽ·å–æ‰€æœ‰Url配置
 *
 * @author Lion Li
 */
@Data
public class AllUrlHandler implements InitializingBean {
    private static final Pattern PATTERN = Pattern.compile("\\{(.*?)\\}");
    private List<String> urls = new ArrayList<>();
    @Override
    public void afterPropertiesSet() {
        Set<String> set = new HashSet<>();
        RequestMappingHandlerMapping mapping = SpringUtils.getBean("requestMappingHandlerMapping", RequestMappingHandlerMapping.class);
        Map<RequestMappingInfo, HandlerMethod> map = mapping.getHandlerMethods();
        map.keySet().forEach(info -> {
            // èŽ·å–æ³¨è§£ä¸Šè¾¹çš„ path æ›¿ä»£ path variable ä¸º *
            Objects.requireNonNull(info.getPathPatternsCondition().getPatterns())
                    .forEach(url -> set.add(ReUtil.replaceAll(url.getPatternString(), PATTERN, "*")));
        });
        urls.addAll(set);
    }
}
ruoyi-common/ruoyi-common-security/src/main/java/org/dromara/common/security/handler/GlobalExceptionHandler.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,141 @@
package org.dromara.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 org.dromara.common.core.domain.R;
import org.dromara.common.core.exception.DemoModeException;
import org.dromara.common.core.exception.ServiceException;
import org.dromara.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
@@ -1,3 +1,3 @@
com.ruoyi.common.security.handler.GlobalExceptionHandler
com.ruoyi.common.security.handler.AllUrlHandler
com.ruoyi.common.security.config.SecurityConfig
org.dromara.common.security.handler.GlobalExceptionHandler
org.dromara.common.security.handler.AllUrlHandler
org.dromara.common.security.config.SecurityConfig
ruoyi-common/ruoyi-common-sensitive/pom.xml
@@ -3,7 +3,7 @@
         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>
        <groupId>org.dromara</groupId>
        <artifactId>ruoyi-common</artifactId>
        <version>${revision}</version>
        <relativePath>../pom.xml</relativePath>
@@ -18,7 +18,7 @@
    <dependencies>
        <dependency>
            <groupId>com.ruoyi</groupId>
            <groupId>org.dromara</groupId>
            <artifactId>ruoyi-common-json</artifactId>
        </dependency>
    </dependencies>
ruoyi-common/ruoyi-common-sensitive/src/main/java/com/ruoyi/common/sensitive/annotation/Sensitive.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-sensitive/src/main/java/com/ruoyi/common/sensitive/core/SensitiveService.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-sensitive/src/main/java/com/ruoyi/common/sensitive/core/SensitiveStrategy.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-sensitive/src/main/java/com/ruoyi/common/sensitive/handler/SensitiveHandler.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-sensitive/src/main/java/org/dromara/common/sensitive/annotation/Sensitive.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,24 @@
package org.dromara.common.sensitive.annotation;
import com.fasterxml.jackson.annotation.JacksonAnnotationsInside;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import org.dromara.common.sensitive.core.SensitiveStrategy;
import org.dromara.common.sensitive.handler.SensitiveHandler;
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 = SensitiveHandler.class)
public @interface Sensitive {
    SensitiveStrategy strategy();
}
ruoyi-common/ruoyi-common-sensitive/src/main/java/org/dromara/common/sensitive/core/SensitiveService.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,18 @@
package org.dromara.common.sensitive.core;
/**
 * è„±æ•æœåŠ¡
 * é»˜è®¤ç®¡ç†å‘˜ä¸è¿‡æ»¤
 * éœ€è‡ªè¡Œæ ¹æ®ä¸šåŠ¡é‡å†™å®žçŽ°
 *
 * @author Lion Li
 * @version 3.6.0
 */
public interface SensitiveService {
    /**
     * æ˜¯å¦è„±æ•
     */
    boolean isSensitive();
}
ruoyi-common/ruoyi-common-sensitive/src/main/java/org/dromara/common/sensitive/core/SensitiveStrategy.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,49 @@
package org.dromara.common.sensitive.core;
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-sensitive/src/main/java/org/dromara/common/sensitive/handler/SensitiveHandler.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,54 @@
package org.dromara.common.sensitive.handler;
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 org.dromara.common.core.utils.SpringUtils;
import org.dromara.common.sensitive.annotation.Sensitive;
import org.dromara.common.sensitive.core.SensitiveService;
import org.dromara.common.sensitive.core.SensitiveStrategy;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import java.io.IOException;
import java.util.Objects;
/**
 * æ•°æ®è„±æ•json序列化工具
 *
 * @author Yjoioooo
 */
@Slf4j
public class SensitiveHandler 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-sms/pom.xml
@@ -3,7 +3,7 @@
         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>
        <groupId>org.dromara</groupId>
        <artifactId>ruoyi-common</artifactId>
        <version>${revision}</version>
        <relativePath>../pom.xml</relativePath>
@@ -18,7 +18,7 @@
    <dependencies>
        <dependency>
            <groupId>com.ruoyi</groupId>
            <groupId>org.dromara</groupId>
            <artifactId>ruoyi-common-json</artifactId>
        </dependency>
ruoyi-common/ruoyi-common-sms/src/main/java/com/ruoyi/common/sms/config/SmsConfig.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-sms/src/main/java/com/ruoyi/common/sms/config/properties/SmsProperties.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-sms/src/main/java/com/ruoyi/common/sms/core/AliyunSmsTemplate.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-sms/src/main/java/com/ruoyi/common/sms/core/SmsTemplate.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-sms/src/main/java/com/ruoyi/common/sms/core/TencentSmsTemplate.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-sms/src/main/java/com/ruoyi/common/sms/entity/SmsResult.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-sms/src/main/java/com/ruoyi/common/sms/exception/SmsException.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-sms/src/main/java/org/dromara/common/sms/config/SmsConfig.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,48 @@
package org.dromara.common.sms.config;
import org.dromara.common.sms.config.properties.SmsProperties;
import org.dromara.common.sms.core.AliyunSmsTemplate;
import org.dromara.common.sms.core.SmsTemplate;
import org.dromara.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/org/dromara/common/sms/config/properties/SmsProperties.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,45 @@
package org.dromara.common.sms.config.properties;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
 * SMS短信 é…ç½®å±žæ€§
 *
 * @author Lion Li
 * @version 4.2.0
 */
@Data
@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/org/dromara/common/sms/core/AliyunSmsTemplate.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,66 @@
package org.dromara.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 org.dromara.common.core.utils.StringUtils;
import org.dromara.common.json.utils.JsonUtils;
import org.dromara.common.sms.config.properties.SmsProperties;
import org.dromara.common.sms.entity.SmsResult;
import org.dromara.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/org/dromara/common/sms/core/SmsTemplate.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,26 @@
package org.dromara.common.sms.core;
import org.dromara.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/org/dromara/common/sms/core/TencentSmsTemplate.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,82 @@
package org.dromara.common.sms.core;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ArrayUtil;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.json.utils.JsonUtils;
import org.dromara.common.sms.config.properties.SmsProperties;
import org.dromara.common.sms.entity.SmsResult;
import org.dromara.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(StringUtils.SEPARATOR)).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/org/dromara/common/sms/entity/SmsResult.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,31 @@
package org.dromara.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/org/dromara/common/sms/exception/SmsException.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,19 @@
package org.dromara.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
@@ -1 +1 @@
com.ruoyi.common.sms.config.SmsConfig
org.dromara.common.sms.config.SmsConfig
ruoyi-common/ruoyi-common-tenant/pom.xml
@@ -3,7 +3,7 @@
         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>
        <groupId>org.dromara</groupId>
        <artifactId>ruoyi-common</artifactId>
        <version>${revision}</version>
        <relativePath>../pom.xml</relativePath>
@@ -18,12 +18,12 @@
    <dependencies>
        <dependency>
            <groupId>com.ruoyi</groupId>
            <groupId>org.dromara</groupId>
            <artifactId>ruoyi-common-mybatis</artifactId>
        </dependency>
        <dependency>
            <groupId>com.ruoyi</groupId>
            <groupId>org.dromara</groupId>
            <artifactId>ruoyi-common-redis</artifactId>
        </dependency>
ruoyi-common/ruoyi-common-tenant/src/main/java/com/ruoyi/common/tenant/config/TenantConfig.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-tenant/src/main/java/com/ruoyi/common/tenant/core/TenantEntity.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-tenant/src/main/java/com/ruoyi/common/tenant/core/TenantSaTokenDao.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-tenant/src/main/java/com/ruoyi/common/tenant/exception/TenantException.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-tenant/src/main/java/com/ruoyi/common/tenant/handle/PlusTenantLineHandler.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-tenant/src/main/java/com/ruoyi/common/tenant/handle/TenantKeyPrefixHandler.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-tenant/src/main/java/com/ruoyi/common/tenant/helper/TenantHelper.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-tenant/src/main/java/com/ruoyi/common/tenant/manager/TenantSpringCacheManager.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-tenant/src/main/java/com/ruoyi/common/tenant/properties/TenantProperties.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-tenant/src/main/java/org/dromara/common/tenant/config/TenantConfig.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,100 @@
package org.dromara.common.tenant.config;
import cn.dev33.satoken.dao.SaTokenDao;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.InnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.TenantLineInnerInterceptor;
import org.dromara.common.core.utils.reflect.ReflectUtils;
import org.dromara.common.mybatis.config.MybatisPlusConfig;
import org.dromara.common.redis.config.RedisConfig;
import org.dromara.common.redis.config.properties.RedissonProperties;
import org.dromara.common.tenant.core.TenantSaTokenDao;
import org.dromara.common.tenant.handle.PlusTenantLineHandler;
import org.dromara.common.tenant.handle.TenantKeyPrefixHandler;
import org.dromara.common.tenant.manager.TenantSpringCacheManager;
import org.dromara.common.tenant.properties.TenantProperties;
import org.redisson.config.ClusterServersConfig;
import org.redisson.config.SingleServerConfig;
import org.redisson.spring.starter.RedissonAutoConfigurationCustomizer;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cache.CacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Primary;
import java.util.ArrayList;
import java.util.List;
/**
 * ç§Ÿæˆ·é…ç½®ç±»
 *
 * @author Lion Li
 */
@EnableConfigurationProperties(TenantProperties.class)
@AutoConfiguration(after = {RedisConfig.class, MybatisPlusConfig.class})
@ConditionalOnProperty(value = "tenant.enable", havingValue = "true")
public class TenantConfig {
    /**
     * åˆå§‹åŒ–租户配置
     */
    @Bean
    public boolean tenantInit(MybatisPlusInterceptor mybatisPlusInterceptor,
                              TenantProperties tenantProperties) {
        List<InnerInterceptor> interceptors = new ArrayList<>();
        // å¤šç§Ÿæˆ·æ’ä»¶ å¿…须放到第一位
        interceptors.add(tenantLineInnerInterceptor(tenantProperties));
        interceptors.addAll(mybatisPlusInterceptor.getInterceptors());
        mybatisPlusInterceptor.setInterceptors(interceptors);
        return true;
    }
    /**
     * å¤šç§Ÿæˆ·æ’ä»¶
     */
    public TenantLineInnerInterceptor tenantLineInnerInterceptor(TenantProperties tenantProperties) {
        return new TenantLineInnerInterceptor(new PlusTenantLineHandler(tenantProperties));
    }
    @Bean
    public RedissonAutoConfigurationCustomizer tenantRedissonCustomizer(RedissonProperties redissonProperties) {
        return config -> {
            TenantKeyPrefixHandler nameMapper = new TenantKeyPrefixHandler(redissonProperties.getKeyPrefix());
            SingleServerConfig singleServerConfig = ReflectUtils.invokeGetter(config, "singleServerConfig");
            if (ObjectUtil.isNotNull(singleServerConfig)) {
                // ä½¿ç”¨å•机模式
                // è®¾ç½®å¤šç§Ÿæˆ· redis key前缀
                singleServerConfig.setNameMapper(nameMapper);
                ReflectUtils.invokeSetter(config, "singleServerConfig", singleServerConfig);
            }
            ClusterServersConfig clusterServersConfig = ReflectUtils.invokeGetter(config, "clusterServersConfig");
            // é›†ç¾¤é…ç½®æ–¹å¼ å‚考下方注释
            if (ObjectUtil.isNotNull(clusterServersConfig)) {
                // è®¾ç½®å¤šç§Ÿæˆ· redis key前缀
                clusterServersConfig.setNameMapper(nameMapper);
                ReflectUtils.invokeSetter(config, "clusterServersConfig", clusterServersConfig);
            }
        };
    }
    /**
     * å¤šç§Ÿæˆ·ç¼“存管理器
     */
    @Primary
    @Bean
    public CacheManager tenantCacheManager() {
        return new TenantSpringCacheManager();
    }
    /**
     * å¤šç§Ÿæˆ·é‰´æƒdao实现
     */
    @Primary
    @Bean
    public SaTokenDao tenantSaTokenDao() {
        return new TenantSaTokenDao();
    }
}
ruoyi-common/ruoyi-common-tenant/src/main/java/org/dromara/common/tenant/core/TenantEntity.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,21 @@
package org.dromara.common.tenant.core;
import org.dromara.common.mybatis.core.domain.BaseEntity;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
 * ç§Ÿæˆ·åŸºç±»
 *
 * @author Michelle.Chung
 */
@Data
@EqualsAndHashCode(callSuper = true)
public class TenantEntity extends BaseEntity {
    /**
     * ç§Ÿæˆ·ç¼–号
     */
    private String tenantId;
}
ruoyi-common/ruoyi-common-tenant/src/main/java/org/dromara/common/tenant/core/TenantSaTokenDao.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,148 @@
package org.dromara.common.tenant.core;
import org.dromara.common.core.constant.GlobalConstants;
import org.dromara.common.redis.utils.RedisUtils;
import org.dromara.common.satoken.core.dao.PlusSaTokenDao;
import java.time.Duration;
import java.util.List;
/**
 * SaToken è®¤è¯æ•°æ®æŒä¹…层 é€‚配多租户
 *
 * @author Lion Li
 */
public class TenantSaTokenDao extends PlusSaTokenDao {
    @Override
    public String get(String key) {
        return super.get(GlobalConstants.GLOBAL_REDIS_KEY + key);
    }
    @Override
    public void set(String key, String value, long timeout) {
        super.set(GlobalConstants.GLOBAL_REDIS_KEY + key, value, 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) {
        super.delete(GlobalConstants.GLOBAL_REDIS_KEY + key);
    }
    /**
     * èŽ·å–Value的剩余存活时间 (单位: ç§’)
     */
    @Override
    public long getTimeout(String key) {
        return super.getTimeout(GlobalConstants.GLOBAL_REDIS_KEY + key);
    }
    /**
     * ä¿®æ”¹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(GlobalConstants.GLOBAL_REDIS_KEY + key, Duration.ofSeconds(timeout));
    }
    /**
     * èŽ·å–Object,如无返空
     */
    @Override
    public Object getObject(String key) {
        return super.getObject(GlobalConstants.GLOBAL_REDIS_KEY + key);
    }
    /**
     * å†™å…¥Object,并设定存活时间 (单位: ç§’)
     */
    @Override
    public void setObject(String key, Object object, long timeout) {
        super.setObject(GlobalConstants.GLOBAL_REDIS_KEY + key, object, 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) {
        super.deleteObject(GlobalConstants.GLOBAL_REDIS_KEY + key);
    }
    /**
     * èŽ·å–Object的剩余存活时间 (单位: ç§’)
     */
    @Override
    public long getObjectTimeout(String key) {
        return super.getObjectTimeout(GlobalConstants.GLOBAL_REDIS_KEY + key);
    }
    /**
     * ä¿®æ”¹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(GlobalConstants.GLOBAL_REDIS_KEY + key, Duration.ofSeconds(timeout));
    }
    /**
     * æœç´¢æ•°æ®
     */
    @Override
    public List<String> searchData(String prefix, String keyword, int start, int size, boolean sortType) {
        return super.searchData(GlobalConstants.GLOBAL_REDIS_KEY + prefix, keyword, start, size, sortType);
    }
}
ruoyi-common/ruoyi-common-tenant/src/main/java/org/dromara/common/tenant/exception/TenantException.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,20 @@
package org.dromara.common.tenant.exception;
import org.dromara.common.core.exception.base.BaseException;
import java.io.Serial;
/**
 * ç§Ÿæˆ·å¼‚常类
 *
 * @author Lion Li
 */
public class TenantException extends BaseException {
    @Serial
    private static final long serialVersionUID = 1L;
    public TenantException(String code, Object... args) {
        super("tenant", code, args, null);
    }
}
ruoyi-common/ruoyi-common-tenant/src/main/java/org/dromara/common/tenant/handle/PlusTenantLineHandler.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,57 @@
package org.dromara.common.tenant.handle;
import com.baomidou.mybatisplus.extension.plugins.handler.TenantLineHandler;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.satoken.utils.LoginHelper;
import org.dromara.common.tenant.helper.TenantHelper;
import org.dromara.common.tenant.properties.TenantProperties;
import lombok.AllArgsConstructor;
import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.expression.NullValue;
import net.sf.jsqlparser.expression.StringValue;
import java.util.List;
/**
 * è‡ªå®šä¹‰ç§Ÿæˆ·å¤„理器
 *
 * @author Lion Li
 */
@AllArgsConstructor
public class PlusTenantLineHandler implements TenantLineHandler {
    private final TenantProperties tenantProperties;
    @Override
    public Expression getTenantId() {
        String tenantId = LoginHelper.getTenantId();
        if (StringUtils.isBlank(tenantId)) {
            return new NullValue();
        }
        String dynamicTenantId = TenantHelper.getDynamic();
        if (StringUtils.isNotBlank(dynamicTenantId)) {
            // è¿”回动态租户
            return new StringValue(dynamicTenantId);
        }
        // è¿”回固定租户
        return new StringValue(tenantId);
    }
    @Override
    public boolean ignoreTable(String tableName) {
        String tenantId = LoginHelper.getTenantId();
        // åˆ¤æ–­æ˜¯å¦æœ‰ç§Ÿæˆ·
        if (StringUtils.isNotBlank(tenantId)) {
            // ä¸éœ€è¦è¿‡æ»¤ç§Ÿæˆ·çš„表
            List<String> excludes = tenantProperties.getExcludes();
            // éžä¸šåŠ¡è¡¨
            excludes.addAll(List.of(
                    "gen_table",
                    "gen_table_column"
            ));
            return excludes.contains(tableName);
        }
        return true;
    }
}
ruoyi-common/ruoyi-common-tenant/src/main/java/org/dromara/common/tenant/handle/TenantKeyPrefixHandler.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,58 @@
package org.dromara.common.tenant.handle;
import org.dromara.common.core.constant.GlobalConstants;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.redis.handler.KeyPrefixHandler;
import org.dromara.common.tenant.helper.TenantHelper;
/**
 * å¤šç§Ÿæˆ·redis缓存key前缀处理
 *
 * @author Lion Li
 */
public class TenantKeyPrefixHandler extends KeyPrefixHandler {
    public TenantKeyPrefixHandler(String keyPrefix) {
        super(keyPrefix);
    }
    /**
     * å¢žåŠ å‰ç¼€
     */
    @Override
    public String map(String name) {
        if (StringUtils.isBlank(name)) {
            return null;
        }
        if (StringUtils.contains(name, GlobalConstants.GLOBAL_REDIS_KEY)) {
            return super.map(name);
        }
        String tenantId = TenantHelper.getTenantId();
        if (StringUtils.startsWith(name, tenantId)) {
            // å¦‚果存在则直接返回
            return super.map(name);
        }
        return super.map(tenantId + ":" + name);
    }
    /**
     * åŽ»é™¤å‰ç¼€
     */
    @Override
    public String unmap(String name) {
        String unmap = super.unmap(name);
        if (StringUtils.isBlank(unmap)) {
            return null;
        }
        if (StringUtils.contains(name, GlobalConstants.GLOBAL_REDIS_KEY)) {
            return super.unmap(name);
        }
        String tenantId = TenantHelper.getTenantId();
        if (StringUtils.startsWith(unmap, tenantId)) {
            // å¦‚果存在则删除
            return unmap.substring((tenantId + ":").length());
        }
        return unmap;
    }
}
ruoyi-common/ruoyi-common-tenant/src/main/java/org/dromara/common/tenant/helper/TenantHelper.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,110 @@
package org.dromara.common.tenant.helper;
import cn.dev33.satoken.context.SaHolder;
import cn.dev33.satoken.spring.SpringMVCUtil;
import cn.hutool.core.convert.Convert;
import com.alibaba.ttl.TransmittableThreadLocal;
import com.baomidou.mybatisplus.core.plugins.IgnoreStrategy;
import com.baomidou.mybatisplus.core.plugins.InterceptorIgnoreHelper;
import org.dromara.common.core.constant.GlobalConstants;
import org.dromara.common.core.utils.SpringUtils;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.redis.utils.RedisUtils;
import org.dromara.common.satoken.utils.LoginHelper;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
/**
 * ç§Ÿæˆ·åŠ©æ‰‹
 *
 * @author Lion Li
 */
@Slf4j
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class TenantHelper {
    private static final String DYNAMIC_TENANT_KEY = GlobalConstants.GLOBAL_REDIS_KEY + "dynamicTenant";
    private static final ThreadLocal<String> TEMP_DYNAMIC_TENANT = new TransmittableThreadLocal<>();
    /**
     * ç§Ÿæˆ·åŠŸèƒ½æ˜¯å¦å¯ç”¨
     */
    public static boolean isEnable() {
        return Convert.toBool(SpringUtils.getProperty("tenant.enable"), false);
    }
    /**
     * å¼€å¯å¿½ç•¥ç§Ÿæˆ·(开启后需手动调用 {@link #disableIgnore()} å…³é—­)
     */
    public static void enableIgnore() {
        InterceptorIgnoreHelper.handle(IgnoreStrategy.builder().tenantLine(true).build());
    }
    /**
     * å…³é—­å¿½ç•¥ç§Ÿæˆ·
     */
    public static void disableIgnore() {
        InterceptorIgnoreHelper.clearIgnoreStrategy();
    }
    /**
     * è®¾ç½®åŠ¨æ€ç§Ÿæˆ·(一直有效 éœ€è¦æ‰‹åŠ¨æ¸…ç†)
     * <p>
     * å¦‚果为非web环境 é‚£ä¹ˆåªåœ¨å½“前线程内生效
     */
    public static void setDynamic(String tenantId) {
        if (!SpringMVCUtil.isWeb()) {
            TEMP_DYNAMIC_TENANT.set(tenantId);
            return;
        }
        String cacheKey = DYNAMIC_TENANT_KEY + ":" + LoginHelper.getUserId();
        RedisUtils.setCacheObject(cacheKey, tenantId);
        SaHolder.getStorage().set(cacheKey, tenantId);
    }
    /**
     * èŽ·å–åŠ¨æ€ç§Ÿæˆ·(一直有效 éœ€è¦æ‰‹åŠ¨æ¸…ç†)
     * <p>
     * å¦‚果为非web环境 é‚£ä¹ˆåªåœ¨å½“前线程内生效
     */
    public static String getDynamic() {
        if (!SpringMVCUtil.isWeb()) {
            return TEMP_DYNAMIC_TENANT.get();
        }
        String cacheKey = DYNAMIC_TENANT_KEY + ":" + LoginHelper.getUserId();
        String tenantId = (String) SaHolder.getStorage().get(cacheKey);
        if (StringUtils.isNotBlank(tenantId)) {
            return tenantId;
        }
        tenantId = RedisUtils.getCacheObject(cacheKey);
        SaHolder.getStorage().set(cacheKey, tenantId);
        return tenantId;
    }
    /**
     * æ¸…除动态租户
     */
    public static void clearDynamic() {
        if (!SpringMVCUtil.isWeb()) {
            TEMP_DYNAMIC_TENANT.remove();
            return;
        }
        String cacheKey = DYNAMIC_TENANT_KEY + ":" + LoginHelper.getUserId();
        RedisUtils.deleteObject(cacheKey);
        SaHolder.getStorage().delete(cacheKey);
    }
    /**
     * èŽ·å–å½“å‰ç§Ÿæˆ·id(动态租户优先)
     */
    public static String getTenantId() {
        String tenantId = TenantHelper.getDynamic();
        if (StringUtils.isBlank(tenantId)) {
            tenantId = LoginHelper.getTenantId();
        }
        return tenantId;
    }
}
ruoyi-common/ruoyi-common-tenant/src/main/java/org/dromara/common/tenant/manager/TenantSpringCacheManager.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,32 @@
package org.dromara.common.tenant.manager;
import org.dromara.common.core.constant.GlobalConstants;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.redis.manager.PlusSpringCacheManager;
import org.dromara.common.tenant.helper.TenantHelper;
import org.springframework.cache.Cache;
/**
 * é‡å†™ cacheName å¤„理方法 æ”¯æŒå¤šç§Ÿæˆ·
 *
 * @author Lion Li
 */
public class TenantSpringCacheManager extends PlusSpringCacheManager {
    public TenantSpringCacheManager() {
    }
    @Override
    public Cache getCache(String name) {
        if (StringUtils.contains(name, GlobalConstants.GLOBAL_REDIS_KEY)) {
            return super.getCache(name);
        }
        String tenantId = TenantHelper.getTenantId();
        if (StringUtils.startsWith(name, tenantId)) {
            // å¦‚果存在则直接返回
            return super.getCache(name);
        }
        return super.getCache(tenantId + ":" + name);
    }
}
ruoyi-common/ruoyi-common-tenant/src/main/java/org/dromara/common/tenant/properties/TenantProperties.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,27 @@
package org.dromara.common.tenant.properties;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import java.util.List;
/**
 * ç§Ÿæˆ· é…ç½®å±žæ€§
 *
 * @author Lion Li
 */
@Data
@ConfigurationProperties(prefix = "tenant")
public class TenantProperties {
    /**
     * æ˜¯å¦å¯ç”¨
     */
    private Boolean enable;
    /**
     * æŽ’除表
     */
    private List<String> excludes;
}
ruoyi-common/ruoyi-common-tenant/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
@@ -1 +1 @@
com.ruoyi.common.tenant.config.TenantConfig
org.dromara.common.tenant.config.TenantConfig
ruoyi-common/ruoyi-common-translation/pom.xml
@@ -3,7 +3,7 @@
         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>
        <groupId>org.dromara</groupId>
        <artifactId>ruoyi-common</artifactId>
        <version>${revision}</version>
        <relativePath>../pom.xml</relativePath>
@@ -19,7 +19,7 @@
    <dependencies>
        <dependency>
            <groupId>com.ruoyi</groupId>
            <groupId>org.dromara</groupId>
            <artifactId>ruoyi-common-json</artifactId>
        </dependency>
ruoyi-common/ruoyi-common-translation/src/main/java/com/ruoyi/common/translation/annotation/Translation.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-translation/src/main/java/com/ruoyi/common/translation/annotation/TranslationType.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-translation/src/main/java/com/ruoyi/common/translation/config/TranslationConfig.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-translation/src/main/java/com/ruoyi/common/translation/constant/TransConstant.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-translation/src/main/java/com/ruoyi/common/translation/core/TranslationInterface.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-translation/src/main/java/com/ruoyi/common/translation/core/handler/TranslationBeanSerializerModifier.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-translation/src/main/java/com/ruoyi/common/translation/core/handler/TranslationHandler.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-translation/src/main/java/com/ruoyi/common/translation/core/impl/DeptNameTranslationImpl.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-translation/src/main/java/com/ruoyi/common/translation/core/impl/DictTypeTranslationImpl.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-translation/src/main/java/com/ruoyi/common/translation/core/impl/OssUrlTranslationImpl.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-translation/src/main/java/com/ruoyi/common/translation/core/impl/UserNameTranslationImpl.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-translation/src/main/java/org/dromara/common/translation/annotation/Translation.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,39 @@
package org.dromara.common.translation.annotation;
import com.fasterxml.jackson.annotation.JacksonAnnotationsInside;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import org.dromara.common.translation.core.handler.TranslationHandler;
import java.lang.annotation.*;
/**
 * é€šç”¨ç¿»è¯‘注解
 *
 * @author Lion Li
 */
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.METHOD})
@Documented
@JacksonAnnotationsInside
@JsonSerialize(using = TranslationHandler.class)
public @interface Translation {
    /**
     * ç±»åž‹ (需与实现类上的 {@link TranslationType} æ³¨è§£type对应)
     * <p>
     * é»˜è®¤å–当前字段的值 å¦‚果设置了 @{@link Translation#mapper()} åˆ™å–映射字段的值
     */
    String type();
    /**
     * æ˜ å°„字段 (如果不为空则取此字段的值)
     */
    String mapper() default "";
    /**
     * å…¶ä»–条件 ä¾‹å¦‚: å­—å…¸type(sys_user_sex)
     */
    String other() default "";
}
ruoyi-common/ruoyi-common-translation/src/main/java/org/dromara/common/translation/annotation/TranslationType.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,23 @@
package org.dromara.common.translation.annotation;
import org.dromara.common.translation.core.TranslationInterface;
import java.lang.annotation.*;
/**
 * ç¿»è¯‘类型注解 (标注到{@link TranslationInterface} çš„实现类)
 *
 * @author Lion Li
 */
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Documented
public @interface TranslationType {
    /**
     * ç±»åž‹
     */
    String type();
}
ruoyi-common/ruoyi-common-translation/src/main/java/org/dromara/common/translation/config/TranslationConfig.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,50 @@
package org.dromara.common.translation.config;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.dromara.common.translation.annotation.TranslationType;
import org.dromara.common.translation.core.TranslationInterface;
import org.dromara.common.translation.core.handler.TranslationBeanSerializerModifier;
import org.dromara.common.translation.core.handler.TranslationHandler;
import jakarta.annotation.PostConstruct;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
 * ç¿»è¯‘模块配置类
 *
 * @author Lion Li
 */
@Slf4j
@AutoConfiguration
public class TranslationConfig {
    @Autowired
    private List<TranslationInterface<?>> list;
    @Autowired
    private ObjectMapper objectMapper;
    @PostConstruct
    public void init() {
        Map<String, TranslationInterface<?>> map = new HashMap<>(list.size());
        for (TranslationInterface<?> trans : list) {
            if (trans.getClass().isAnnotationPresent(TranslationType.class)) {
                TranslationType annotation = trans.getClass().getAnnotation(TranslationType.class);
                map.put(annotation.type(), trans);
            } else {
                log.warn(trans.getClass().getName() + " ç¿»è¯‘实现类未标注 TranslationType æ³¨è§£!");
            }
        }
        TranslationHandler.TRANSLATION_MAPPER.putAll(map);
        // è®¾ç½® Bean åºåˆ—化修改器
        objectMapper.setSerializerFactory(
            objectMapper.getSerializerFactory()
                .withSerializerModifier(new TranslationBeanSerializerModifier()));
    }
}
ruoyi-common/ruoyi-common-translation/src/main/java/org/dromara/common/translation/constant/TransConstant.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,30 @@
package org.dromara.common.translation.constant;
/**
 * ç¿»è¯‘常量
 *
 * @author Lion Li
 */
public interface TransConstant {
    /**
     * ç”¨æˆ·id转账号
     */
    String USER_ID_TO_NAME = "user_id_to_name";
    /**
     * éƒ¨é—¨id转名称
     */
    String DEPT_ID_TO_NAME = "dept_id_to_name";
    /**
     * å­—å…¸type转label
     */
    String DICT_TYPE_TO_LABEL = "dict_type_to_label";
    /**
     * ossId转url
     */
    String OSS_ID_TO_URL = "oss_id_to_url";
}
ruoyi-common/ruoyi-common-translation/src/main/java/org/dromara/common/translation/core/TranslationInterface.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,20 @@
package org.dromara.common.translation.core;
import org.dromara.common.translation.annotation.TranslationType;
/**
 * ç¿»è¯‘接口 (实现类需标注 {@link TranslationType} æ³¨è§£æ ‡æ˜Žç¿»è¯‘类型)
 *
 * @author Lion Li
 */
public interface TranslationInterface<T> {
    /**
     * ç¿»è¯‘
     *
     * @param key   éœ€è¦è¢«ç¿»è¯‘的键(不为空)
     * @param other å…¶ä»–参数
     * @return è¿”回键对应的值
     */
    T translation(Object key, String other);
}
ruoyi-common/ruoyi-common-translation/src/main/java/org/dromara/common/translation/core/handler/TranslationBeanSerializerModifier.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,29 @@
package org.dromara.common.translation.core.handler;
import com.fasterxml.jackson.databind.BeanDescription;
import com.fasterxml.jackson.databind.SerializationConfig;
import com.fasterxml.jackson.databind.ser.BeanPropertyWriter;
import com.fasterxml.jackson.databind.ser.BeanSerializerModifier;
import java.util.List;
/**
 * Bean åºåˆ—化修改器 è§£å†³ Null è¢«å•独处理问题
 *
 * @author Lion Li
 */
public class TranslationBeanSerializerModifier extends BeanSerializerModifier {
    @Override
    public List<BeanPropertyWriter> changeProperties(SerializationConfig config, BeanDescription beanDesc,
                                                     List<BeanPropertyWriter> beanProperties) {
        for (BeanPropertyWriter writer : beanProperties) {
            // å¦‚果序列化器为 TranslationHandler çš„话 å°† Null å€¼ä¹Ÿäº¤ç»™ä»–处理
            if (writer.getSerializer() instanceof TranslationHandler serializer) {
                writer.assignNullSerializer(serializer);
            }
        }
        return beanProperties;
    }
}
ruoyi-common/ruoyi-common-translation/src/main/java/org/dromara/common/translation/core/handler/TranslationHandler.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,65 @@
package org.dromara.common.translation.core.handler;
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 org.dromara.common.core.utils.StringUtils;
import org.dromara.common.core.utils.reflect.ReflectUtils;
import org.dromara.common.translation.annotation.Translation;
import org.dromara.common.translation.core.TranslationInterface;
import lombok.extern.slf4j.Slf4j;
import java.io.IOException;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
/**
 * ç¿»è¯‘处理器
 *
 * @author Lion Li
 */
@Slf4j
public class TranslationHandler extends JsonSerializer<Object> implements ContextualSerializer {
    /**
     * å…¨å±€ç¿»è¯‘实现类映射器
     */
    public static final Map<String, TranslationInterface<?>> TRANSLATION_MAPPER = new ConcurrentHashMap<>();
    private Translation translation;
    @Override
    public void serialize(Object value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
        TranslationInterface<?> trans = TRANSLATION_MAPPER.get(translation.type());
        if (ObjectUtil.isNotNull(trans)) {
            // å¦‚果映射字段不为空 åˆ™å–映射字段的值
            if (StringUtils.isNotBlank(translation.mapper())) {
                value = ReflectUtils.invokeGetter(gen.getCurrentValue(), translation.mapper());
            }
            // å¦‚果为 null ç›´æŽ¥å†™å‡º
            if (ObjectUtil.isNull(value)) {
                gen.writeNull();
                return;
            }
            Object result = trans.translation(value, translation.other());
            gen.writeObject(result);
        } else {
            gen.writeObject(value);
        }
    }
    @Override
    public JsonSerializer<?> createContextual(SerializerProvider prov, BeanProperty property) throws JsonMappingException {
        Translation translation = property.getAnnotation(Translation.class);
        if (Objects.nonNull(translation)) {
            this.translation = translation;
            return this;
        }
        return prov.findValueSerializer(property.getType(), property);
    }
}
ruoyi-common/ruoyi-common-translation/src/main/java/org/dromara/common/translation/core/impl/DeptNameTranslationImpl.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,28 @@
package org.dromara.common.translation.core.impl;
import org.dromara.common.core.service.DeptService;
import org.dromara.common.translation.annotation.TranslationType;
import org.dromara.common.translation.constant.TransConstant;
import org.dromara.common.translation.core.TranslationInterface;
import lombok.AllArgsConstructor;
/**
 * éƒ¨é—¨ç¿»è¯‘实现
 *
 * @author Lion Li
 */
@AllArgsConstructor
@TranslationType(type = TransConstant.DEPT_ID_TO_NAME)
public class DeptNameTranslationImpl implements TranslationInterface<String> {
    private final DeptService deptService;
    public String translation(Object key, String other) {
        if (key instanceof String ids) {
            return deptService.selectDeptNameByIds(ids);
        } else if (key instanceof Long id) {
            return deptService.selectDeptNameByIds(id.toString());
        }
        return null;
    }
}
ruoyi-common/ruoyi-common-translation/src/main/java/org/dromara/common/translation/core/impl/DictTypeTranslationImpl.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,27 @@
package org.dromara.common.translation.core.impl;
import org.dromara.common.core.service.DictService;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.translation.annotation.TranslationType;
import org.dromara.common.translation.constant.TransConstant;
import org.dromara.common.translation.core.TranslationInterface;
import lombok.AllArgsConstructor;
/**
 * å­—典翻译实现
 *
 * @author Lion Li
 */
@AllArgsConstructor
@TranslationType(type = TransConstant.DICT_TYPE_TO_LABEL)
public class DictTypeTranslationImpl implements TranslationInterface<String> {
    private final DictService dictService;
    public String translation(Object key, String other) {
        if (key instanceof String dictValue && StringUtils.isNotBlank(other)) {
            return dictService.getDictLabel(other, dictValue);
        }
        return null;
    }
}
ruoyi-common/ruoyi-common-translation/src/main/java/org/dromara/common/translation/core/impl/OssUrlTranslationImpl.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,28 @@
package org.dromara.common.translation.core.impl;
import org.dromara.common.core.service.OssService;
import org.dromara.common.translation.annotation.TranslationType;
import org.dromara.common.translation.constant.TransConstant;
import org.dromara.common.translation.core.TranslationInterface;
import lombok.AllArgsConstructor;
/**
 * OSS翻译实现
 *
 * @author Lion Li
 */
@AllArgsConstructor
@TranslationType(type = TransConstant.OSS_ID_TO_URL)
public class OssUrlTranslationImpl implements TranslationInterface<String> {
    private final OssService ossService;
    public String translation(Object key, String other) {
        if (key instanceof String ids) {
            return ossService.selectUrlByIds(ids);
        } else if (key instanceof Long id) {
            return ossService.selectUrlByIds(id.toString());
        }
        return null;
    }
}
ruoyi-common/ruoyi-common-translation/src/main/java/org/dromara/common/translation/core/impl/UserNameTranslationImpl.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,26 @@
package org.dromara.common.translation.core.impl;
import org.dromara.common.core.service.UserService;
import org.dromara.common.translation.annotation.TranslationType;
import org.dromara.common.translation.constant.TransConstant;
import org.dromara.common.translation.core.TranslationInterface;
import lombok.AllArgsConstructor;
/**
 * ç”¨æˆ·åç¿»è¯‘实现
 *
 * @author Lion Li
 */
@AllArgsConstructor
@TranslationType(type = TransConstant.USER_ID_TO_NAME)
public class UserNameTranslationImpl implements TranslationInterface<String> {
    private final UserService userService;
    public String translation(Object key, String other) {
        if (key instanceof Long id) {
            return userService.selectUserNameById(id);
        }
        return null;
    }
}
ruoyi-common/ruoyi-common-translation/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
@@ -1,5 +1,5 @@
com.ruoyi.common.translation.config.TranslationConfig
com.ruoyi.common.translation.core.impl.DeptNameTranslationImpl
com.ruoyi.common.translation.core.impl.DictTypeTranslationImpl
com.ruoyi.common.translation.core.impl.OssUrlTranslationImpl
com.ruoyi.common.translation.core.impl.UserNameTranslationImpl
org.dromara.common.translation.config.TranslationConfig
org.dromara.common.translation.core.impl.DeptNameTranslationImpl
org.dromara.common.translation.core.impl.DictTypeTranslationImpl
org.dromara.common.translation.core.impl.OssUrlTranslationImpl
org.dromara.common.translation.core.impl.UserNameTranslationImpl
ruoyi-common/ruoyi-common-web/pom.xml
@@ -3,7 +3,7 @@
         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>
        <groupId>org.dromara</groupId>
        <artifactId>ruoyi-common</artifactId>
        <version>${revision}</version>
        <relativePath>../pom.xml</relativePath>
@@ -18,12 +18,12 @@
    <dependencies>
        <dependency>
            <groupId>com.ruoyi</groupId>
            <groupId>org.dromara</groupId>
            <artifactId>ruoyi-common-json</artifactId>
        </dependency>
        <dependency>
            <groupId>com.ruoyi</groupId>
            <groupId>org.dromara</groupId>
            <artifactId>ruoyi-common-redis</artifactId>
        </dependency>
ruoyi-common/ruoyi-common-web/src/main/java/com/ruoyi/common/web/config/CaptchaConfig.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-web/src/main/java/com/ruoyi/common/web/config/FilterConfig.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-web/src/main/java/com/ruoyi/common/web/config/I18nConfig.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-web/src/main/java/com/ruoyi/common/web/config/ResourcesConfig.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-web/src/main/java/com/ruoyi/common/web/config/UndertowConfig.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-web/src/main/java/com/ruoyi/common/web/config/properties/CaptchaProperties.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-web/src/main/java/com/ruoyi/common/web/config/properties/XssProperties.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-web/src/main/java/com/ruoyi/common/web/core/BaseController.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-web/src/main/java/com/ruoyi/common/web/core/I18nLocaleResolver.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-web/src/main/java/com/ruoyi/common/web/enums/CaptchaCategory.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-web/src/main/java/com/ruoyi/common/web/enums/CaptchaType.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-web/src/main/java/com/ruoyi/common/web/filter/RepeatableFilter.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-web/src/main/java/com/ruoyi/common/web/filter/RepeatedlyRequestWrapper.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-web/src/main/java/com/ruoyi/common/web/filter/XssFilter.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-web/src/main/java/com/ruoyi/common/web/filter/XssHttpServletRequestWrapper.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-web/src/main/java/com/ruoyi/common/web/interceptor/PlusWebInvokeTimeInterceptor.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-web/src/main/java/com/ruoyi/common/web/utils/UnsignedMathGenerator.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/config/CaptchaConfig.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,65 @@
package org.dromara.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.dromara.common.web.config.properties.CaptchaProperties;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Lazy;
import java.awt.*;
/**
 * éªŒè¯ç é…ç½®
 *
 * @author Lion Li
 */
@AutoConfiguration
@EnableConfigurationProperties(CaptchaProperties.class)
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/org/dromara/common/web/config/FilterConfig.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,53 @@
package org.dromara.common.web.config;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.web.config.properties.XssProperties;
import org.dromara.common.web.filter.RepeatableFilter;
import org.dromara.common.web.filter.XssFilter;
import jakarta.servlet.DispatcherType;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import java.util.HashMap;
import java.util.Map;
/**
 * Filter配置
 *
 * @author Lion Li
 */
@AutoConfiguration
@EnableConfigurationProperties(XssProperties.class)
public class FilterConfig {
    @SuppressWarnings({"rawtypes", "unchecked"})
    @Bean
    @ConditionalOnProperty(value = "xss.enabled", havingValue = "true")
    public FilterRegistrationBean xssFilterRegistration(XssProperties xssProperties) {
        FilterRegistrationBean registration = new FilterRegistrationBean();
        registration.setDispatcherTypes(DispatcherType.REQUEST);
        registration.setFilter(new XssFilter());
        registration.addUrlPatterns(StringUtils.split(xssProperties.getUrlPatterns(), StringUtils.SEPARATOR));
        registration.setName("xssFilter");
        registration.setOrder(FilterRegistrationBean.HIGHEST_PRECEDENCE);
        Map<String, String> initParameters = new HashMap<>();
        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/org/dromara/common/web/config/I18nConfig.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,22 @@
package org.dromara.common.web.config;
import org.dromara.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/org/dromara/common/web/config/ResourcesConfig.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,52 @@
package org.dromara.common.web.config;
import org.dromara.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/org/dromara/common/web/config/UndertowConfig.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,30 @@
package org.dromara.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/org/dromara/common/web/config/properties/CaptchaProperties.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,38 @@
package org.dromara.common.web.config.properties;
import org.dromara.common.web.enums.CaptchaCategory;
import org.dromara.common.web.enums.CaptchaType;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
 * éªŒè¯ç  é…ç½®å±žæ€§
 *
 * @author Lion Li
 */
@Data
@ConfigurationProperties(prefix = "captcha")
public class CaptchaProperties {
    private Boolean enable;
    /**
     * éªŒè¯ç ç±»åž‹
     */
    private CaptchaType type;
    /**
     * éªŒè¯ç ç±»åˆ«
     */
    private CaptchaCategory category;
    /**
     * æ•°å­—验证码位数
     */
    private Integer numberLength;
    /**
     * å­—符验证码长度
     */
    private Integer charLength;
}
ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/config/properties/XssProperties.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,30 @@
package org.dromara.common.web.config.properties;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
 * xss过滤 é…ç½®å±žæ€§
 *
 * @author Lion Li
 */
@Data
@ConfigurationProperties(prefix = "xss")
public class XssProperties {
    /**
     * è¿‡æ»¤å¼€å…³
     */
    private String enabled;
    /**
     * æŽ’除链接(多个用逗号分隔)
     */
    private String excludes;
    /**
     * åŒ¹é…é“¾æŽ¥
     */
    private String urlPatterns;
}
ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/core/BaseController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,40 @@
package org.dromara.common.web.core;
import org.dromara.common.core.domain.R;
import org.dromara.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-web/src/main/java/org/dromara/common/web/core/I18nLocaleResolver.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,31 @@
package org.dromara.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/org/dromara/common/web/enums/CaptchaCategory.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,35 @@
package org.dromara.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/org/dromara/common/web/enums/CaptchaType.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,29 @@
package org.dromara.common.web.enums;
import cn.hutool.captcha.generator.CodeGenerator;
import cn.hutool.captcha.generator.RandomGenerator;
import org.dromara.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/org/dromara/common/web/filter/RepeatableFilter.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,40 @@
package org.dromara.common.web.filter;
import org.dromara.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/org/dromara/common/web/filter/RepeatedlyRequestWrapper.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,68 @@
package org.dromara.common.web.filter;
import cn.hutool.core.io.IoUtil;
import org.dromara.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/org/dromara/common/web/filter/XssFilter.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,62 @@
package org.dromara.common.web.filter;
import org.dromara.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(StringUtils.SEPARATOR);
            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/org/dromara/common/web/filter/XssHttpServletRequestWrapper.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,97 @@
package org.dromara.common.web.filter;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HtmlUtil;
import org.dromara.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/org/dromara/common/web/interceptor/PlusWebInvokeTimeInterceptor.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,94 @@
package org.dromara.common.web.interceptor;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.map.MapUtil;
import com.alibaba.ttl.TransmittableThreadLocal;
import org.dromara.common.core.utils.SpringUtils;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.json.utils.JsonUtils;
import org.dromara.common.web.filter.RepeatedlyRequestWrapper;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
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 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/org/dromara/common/web/utils/UnsignedMathGenerator.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,88 @@
package org.dromara.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 org.dromara.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
@@ -1,5 +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
org.dromara.common.web.config.CaptchaConfig
org.dromara.common.web.config.FilterConfig
org.dromara.common.web.config.I18nConfig
org.dromara.common.web.config.ResourcesConfig
org.dromara.common.web.config.UndertowConfig
ruoyi-common/ruoyi-common-websocket/pom.xml
@@ -3,7 +3,7 @@
         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>
        <groupId>org.dromara</groupId>
        <artifactId>ruoyi-common</artifactId>
        <version>${revision}</version>
        <relativePath>../pom.xml</relativePath>
@@ -18,19 +18,19 @@
    <dependencies>
        <dependency>
            <groupId>com.ruoyi</groupId>
            <groupId>org.dromara</groupId>
            <artifactId>ruoyi-common-core</artifactId>
        </dependency>
        <dependency>
            <groupId>com.ruoyi</groupId>
            <groupId>org.dromara</groupId>
            <artifactId>ruoyi-common-redis</artifactId>
        </dependency>
        <dependency>
            <groupId>com.ruoyi</groupId>
            <groupId>org.dromara</groupId>
            <artifactId>ruoyi-common-satoken</artifactId>
        </dependency>
        <dependency>
            <groupId>com.ruoyi</groupId>
            <groupId>org.dromara</groupId>
            <artifactId>ruoyi-common-json</artifactId>
        </dependency>
        <dependency>
ruoyi-common/ruoyi-common-websocket/src/main/java/com/ruoyi/common/websocket/config/WebSocketConfig.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-websocket/src/main/java/com/ruoyi/common/websocket/config/properties/WebSocketProperties.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-websocket/src/main/java/com/ruoyi/common/websocket/constant/WebSocketConstants.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-websocket/src/main/java/com/ruoyi/common/websocket/dto/WebSocketMessageDto.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-websocket/src/main/java/com/ruoyi/common/websocket/handler/PlusWebSocketHandler.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-websocket/src/main/java/com/ruoyi/common/websocket/holder/WebSocketSessionHolder.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-websocket/src/main/java/com/ruoyi/common/websocket/interceptor/PlusWebSocketInterceptor.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-websocket/src/main/java/com/ruoyi/common/websocket/listener/WebSocketTopicListener.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-websocket/src/main/java/com/ruoyi/common/websocket/utils/WebSocketUtils.java
ÎļþÒÑɾ³ý
ruoyi-common/ruoyi-common-websocket/src/main/java/org/dromara/common/websocket/config/WebSocketConfig.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,60 @@
package org.dromara.common.websocket.config;
import cn.hutool.core.util.StrUtil;
import org.dromara.common.websocket.config.properties.WebSocketProperties;
import org.dromara.common.websocket.handler.PlusWebSocketHandler;
import org.dromara.common.websocket.interceptor.PlusWebSocketInterceptor;
import org.dromara.common.websocket.listener.WebSocketTopicListener;
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;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.config.annotation.EnableWebSocket;
import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
import org.springframework.web.socket.server.HandshakeInterceptor;
/**
 * WebSocket é…ç½®
 *
 * @author zendwang
 */
@AutoConfiguration
@ConditionalOnProperty(value = "websocket.enabled", havingValue = "true")
@EnableConfigurationProperties(WebSocketProperties.class)
@EnableWebSocket
public class WebSocketConfig {
    @Bean
    public WebSocketConfigurer webSocketConfigurer(HandshakeInterceptor handshakeInterceptor,
                                                   WebSocketHandler webSocketHandler,
                                                   WebSocketProperties webSocketProperties) {
        if (StrUtil.isBlank(webSocketProperties.getPath())) {
            webSocketProperties.setPath("/websocket");
        }
        if (StrUtil.isBlank(webSocketProperties.getAllowedOrigins())) {
            webSocketProperties.setAllowedOrigins("*");
        }
        return registry -> registry
            .addHandler(webSocketHandler, webSocketProperties.getPath())
            .addInterceptors(handshakeInterceptor)
            .setAllowedOrigins(webSocketProperties.getAllowedOrigins());
    }
    @Bean
    public HandshakeInterceptor handshakeInterceptor() {
        return new PlusWebSocketInterceptor();
    }
    @Bean
    public WebSocketHandler webSocketHandler() {
        return new PlusWebSocketHandler();
    }
    @Bean
    public WebSocketTopicListener topicListener() {
        return new WebSocketTopicListener();
    }
}
ruoyi-common/ruoyi-common-websocket/src/main/java/org/dromara/common/websocket/config/properties/WebSocketProperties.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,26 @@
package org.dromara.common.websocket.config.properties;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
 * WebSocket é…ç½®é¡¹
 *
 * @author zendwang
 */
@ConfigurationProperties("websocket")
@Data
public class WebSocketProperties {
    private Boolean enabled;
    /**
     * è·¯å¾„
     */
    private String path;
    /**
     *  è®¾ç½®è®¿é—®æºåœ°å€
     */
    private String allowedOrigins;
}
ruoyi-common/ruoyi-common-websocket/src/main/java/org/dromara/common/websocket/constant/WebSocketConstants.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,28 @@
package org.dromara.common.websocket.constant;
/**
 * websocket的常量配置
 *
 * @author zendwang
 */
public interface WebSocketConstants {
    /**
     * websocketSession中的参数的key
     */
    String LOGIN_USER_KEY = "loginUser";
    /**
     * è®¢é˜…的频道
     */
    String WEB_SOCKET_TOPIC = "global:websocket";
    /**
     * å‰ç«¯å¿ƒè·³æ£€æŸ¥çš„命令
     */
    String PING = "ping";
    /**
     * æœåŠ¡ç«¯å¿ƒè·³æ¢å¤çš„å­—ç¬¦ä¸²
     */
    String PONG = "pong";
}
ruoyi-common/ruoyi-common-websocket/src/main/java/org/dromara/common/websocket/dto/WebSocketMessageDto.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,29 @@
package org.dromara.common.websocket.dto;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
import java.util.List;
/**
 * æ¶ˆæ¯çš„dto
 *
 * @author zendwang
 */
@Data
public class WebSocketMessageDto implements Serializable {
    @Serial
    private static final long serialVersionUID = 1L;
    /**
     * éœ€è¦æŽ¨é€åˆ°çš„session key åˆ—表
     */
    private List<Long> sessionKeys;
    /**
     * éœ€è¦å‘送的消息
     */
    private String message;
}
ruoyi-common/ruoyi-common-websocket/src/main/java/org/dromara/common/websocket/handler/PlusWebSocketHandler.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,103 @@
package org.dromara.common.websocket.handler;
import org.dromara.common.core.domain.model.LoginUser;
import org.dromara.common.websocket.dto.WebSocketMessageDto;
import org.dromara.common.websocket.holder.WebSocketSessionHolder;
import org.dromara.common.websocket.utils.WebSocketUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.socket.*;
import org.springframework.web.socket.handler.AbstractWebSocketHandler;
import java.util.List;
import static org.dromara.common.websocket.constant.WebSocketConstants.LOGIN_USER_KEY;
/**
 * WebSocketHandler å®žçŽ°ç±»
 *
 * @author zendwang
 */
@Slf4j
public class PlusWebSocketHandler extends AbstractWebSocketHandler {
    /**
     * è¿žæŽ¥æˆåŠŸåŽ
     */
    @Override
    public void afterConnectionEstablished(WebSocketSession session) {
        LoginUser loginUser = (LoginUser) session.getAttributes().get(LOGIN_USER_KEY);
        WebSocketSessionHolder.addSession(loginUser.getUserId(), session);
        log.info("[connect] sessionId: {},userId:{},userType:{}", session.getId(), loginUser.getUserId(), loginUser.getUserType());
    }
    /**
     * å¤„理发送来的文本消息
     *
     * @param session
     * @param message
     * @throws Exception
     */
    @Override
    protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
        LoginUser loginUser = (LoginUser) session.getAttributes().get(LOGIN_USER_KEY);
        log.info("PlusWebSocketHandler, è¿žæŽ¥ï¼š" + session.getId() + ",已收到消息:" + message.getPayload());
        List<Long> userIds = List.of(loginUser.getUserId());
        WebSocketMessageDto webSocketMessageDto = new WebSocketMessageDto();
        webSocketMessageDto.setSessionKeys(userIds);
        webSocketMessageDto.setMessage(message.getPayload());
        WebSocketUtils.publishMessage(webSocketMessageDto);
    }
    @Override
    protected void handleBinaryMessage(WebSocketSession session, BinaryMessage message) throws Exception {
        super.handleBinaryMessage(session, message);
    }
    /**
     * å¿ƒè·³ç›‘测的回复
     *
     * @param session
     * @param message
     * @throws Exception
     */
    @Override
    protected void handlePongMessage(WebSocketSession session, PongMessage message) throws Exception {
        WebSocketUtils.sendPongMessage(session);
    }
    /**
     * è¿žæŽ¥å‡ºé”™æ—¶
     *
     * @param session
     * @param exception
     * @throws Exception
     */
    @Override
    public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {
        log.error("[transport error] sessionId: {} , exception:{}", session.getId(), exception.getMessage());
    }
    /**
     * è¿žæŽ¥å…³é—­åŽ
     *
     * @param session
     * @param status
     */
    @Override
    public void afterConnectionClosed(WebSocketSession session, CloseStatus status) {
        LoginUser loginUser = (LoginUser) session.getAttributes().get(LOGIN_USER_KEY);
        WebSocketSessionHolder.removeSession(loginUser.getUserId());
        log.info("[disconnect] sessionId: {},userId:{},userType:{}", session.getId(), loginUser.getUserId(), loginUser.getUserType());
    }
    /**
     * æ˜¯å¦æ”¯æŒåˆ†ç‰‡æ¶ˆæ¯
     *
     * @return
     */
    @Override
    public boolean supportsPartialMessages() {
        return false;
    }
}
ruoyi-common/ruoyi-common-websocket/src/main/java/org/dromara/common/websocket/holder/WebSocketSessionHolder.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,37 @@
package org.dromara.common.websocket.holder;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import org.springframework.web.socket.WebSocketSession;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
 * WebSocketSession ç”¨äºŽä¿å­˜å½“前所有在线的会话信息
 *
 * @author zendwang
 */
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class WebSocketSessionHolder {
    private static final Map<Long, WebSocketSession> USER_SESSION_MAP = new ConcurrentHashMap<>();
    public static void addSession(Long sessionKey, WebSocketSession session) {
        USER_SESSION_MAP.put(sessionKey, session);
    }
    public static void removeSession(Long sessionKey) {
        if (USER_SESSION_MAP.containsKey(sessionKey)) {
            USER_SESSION_MAP.remove(sessionKey);
        }
    }
    public static WebSocketSession getSessions(Long sessionKey) {
        return USER_SESSION_MAP.get(sessionKey);
    }
    public static Boolean existSession(Long sessionKey) {
        return USER_SESSION_MAP.containsKey(sessionKey);
    }
}
ruoyi-common/ruoyi-common-websocket/src/main/java/org/dromara/common/websocket/interceptor/PlusWebSocketInterceptor.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,51 @@
package org.dromara.common.websocket.interceptor;
import org.dromara.common.core.domain.model.LoginUser;
import org.dromara.common.satoken.utils.LoginHelper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.server.HandshakeInterceptor;
import java.util.Map;
import static org.dromara.common.websocket.constant.WebSocketConstants.LOGIN_USER_KEY;
/**
 * WebSocket握手请求的拦截器
 *
 * @author zendwang
 */
@Slf4j
public class PlusWebSocketInterceptor implements HandshakeInterceptor {
    /**
     * æ¡æ‰‹å‰
     *
     * @param request    request
     * @param response   response
     * @param wsHandler  wsHandler
     * @param attributes attributes
     * @return æ˜¯å¦æ¡æ‰‹æˆåŠŸ
     */
    @Override
    public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Map<String, Object> attributes) {
        LoginUser loginUser = LoginHelper.getLoginUser();
        attributes.put(LOGIN_USER_KEY, loginUser);
        return true;
    }
    /**
     * æ¡æ‰‹åŽ
     *
     * @param request   request
     * @param response  response
     * @param wsHandler wsHandler
     * @param exception å¼‚常
     */
    @Override
    public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Exception exception) {
    }
}
ruoyi-common/ruoyi-common-websocket/src/main/java/org/dromara/common/websocket/listener/WebSocketTopicListener.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,38 @@
package org.dromara.common.websocket.listener;
import cn.hutool.core.collection.CollUtil;
import org.dromara.common.websocket.holder.WebSocketSessionHolder;
import org.dromara.common.websocket.utils.WebSocketUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.core.Ordered;
/**
 * WebSocket ä¸»é¢˜è®¢é˜…监听器
 *
 * @author zendwang
 */
@Slf4j
public class WebSocketTopicListener implements ApplicationRunner, Ordered {
    @Override
    public void run(ApplicationArguments args) throws Exception {
        WebSocketUtils.subscribeMessage((message) -> {
            log.info("WebSocket主题订阅收到消息session keys={}  message={}!", message.getSessionKeys(), message.getMessage());
            if (CollUtil.isNotEmpty(message.getSessionKeys())) {
                message.getSessionKeys().forEach(key -> {
                    if (WebSocketSessionHolder.existSession(key)) {
                        WebSocketUtils.sendMessage(key, message.getMessage());
                    }
                });
            }
        });
        log.info("初始化WebSocket主题订阅监听器成功");
    }
    @Override
    public int getOrder() {
        return -1;
    }
}
ruoyi-common/ruoyi-common-websocket/src/main/java/org/dromara/common/websocket/utils/WebSocketUtils.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,102 @@
package org.dromara.common.websocket.utils;
import cn.hutool.core.collection.CollUtil;
import org.dromara.common.core.domain.model.LoginUser;
import org.dromara.common.redis.utils.RedisUtils;
import org.dromara.common.websocket.dto.WebSocketMessageDto;
import org.dromara.common.websocket.holder.WebSocketSessionHolder;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.socket.PongMessage;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketMessage;
import org.springframework.web.socket.WebSocketSession;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;
import static org.dromara.common.websocket.constant.WebSocketConstants.LOGIN_USER_KEY;
import static org.dromara.common.websocket.constant.WebSocketConstants.WEB_SOCKET_TOPIC;
/**
 * å·¥å…·ç±»
 *
 * @author zendwang
 */
@Slf4j
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class WebSocketUtils {
    /**
     * å‘送消息
     *
     * @param sessionKey session主键 ä¸€èˆ¬ä¸ºç”¨æˆ·id
     * @param message    æ¶ˆæ¯æ–‡æœ¬
     */
    public static void sendMessage(Long sessionKey, String message) {
        WebSocketSession session = WebSocketSessionHolder.getSessions(sessionKey);
        sendMessage(session, message);
    }
    /**
     * è®¢é˜…消息
     *
     * @param consumer è‡ªå®šä¹‰å¤„理
     */
    public static void subscribeMessage(Consumer<WebSocketMessageDto> consumer) {
        RedisUtils.subscribe(WEB_SOCKET_TOPIC, WebSocketMessageDto.class, consumer);
    }
    /**
     * å‘布订阅的消息
     *
     * @param webSocketMessage æ¶ˆæ¯å¯¹è±¡
     */
    public static void publishMessage(WebSocketMessageDto webSocketMessage) {
        List<Long> unsentSessionKeys = new ArrayList<>();
        // å½“前服务内session,直接发送消息
        for (Long sessionKey : webSocketMessage.getSessionKeys()) {
            if (WebSocketSessionHolder.existSession(sessionKey)) {
                WebSocketUtils.sendMessage(sessionKey, webSocketMessage.getMessage());
                continue;
            }
            unsentSessionKeys.add(sessionKey);
        }
        // ä¸åœ¨å½“前服务内session,发布订阅消息
        if (CollUtil.isNotEmpty(unsentSessionKeys)) {
            WebSocketMessageDto broadcastMessage = new WebSocketMessageDto();
            broadcastMessage.setMessage(webSocketMessage.getMessage());
            broadcastMessage.setSessionKeys(unsentSessionKeys);
            RedisUtils.publish(WEB_SOCKET_TOPIC, broadcastMessage, consumer -> {
                log.info(" WebSocket发送主题订阅消息topic:{} session keys:{} message:{}",
                    WEB_SOCKET_TOPIC, unsentSessionKeys, webSocketMessage.getMessage());
            });
        }
    }
    public static void sendPongMessage(WebSocketSession session) {
        sendMessage(session, new PongMessage());
    }
    public static void sendMessage(WebSocketSession session, String message) {
        sendMessage(session, new TextMessage(message));
    }
    private static void sendMessage(WebSocketSession session, WebSocketMessage<?> message) {
        if (session == null || !session.isOpen()) {
            log.error("[send] session会话已经关闭");
        } else {
            try {
                // èŽ·å–å½“å‰ä¼šè¯ä¸­çš„ç”¨æˆ·
                LoginUser loginUser = (LoginUser) session.getAttributes().get(LOGIN_USER_KEY);
                session.sendMessage(message);
                log.info("[send] sessionId: {},userId:{},userType:{},message:{}", session.getId(), loginUser.getUserId(), loginUser.getUserType(), message);
            } catch (IOException e) {
                log.error("[send] session({}) å‘送消息({}) å¼‚常", session, message, e);
            }
        }
    }
}
ruoyi-common/ruoyi-common-websocket/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
@@ -1 +1 @@
com.ruoyi.common.websocket.config.WebSocketConfig
org.dromara.common.websocket.config.WebSocketConfig
ruoyi-extend/pom.xml
@@ -4,7 +4,7 @@
         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>
        <groupId>org.dromara</groupId>
        <version>${revision}</version>
        <relativePath>../pom.xml</relativePath>
    </parent>
ruoyi-extend/ruoyi-monitor-admin/pom.xml
@@ -4,7 +4,7 @@
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>ruoyi-extend</artifactId>
        <groupId>com.ruoyi</groupId>
        <groupId>org.dromara</groupId>
        <version>${revision}</version>
        <relativePath>../pom.xml</relativePath>
    </parent>
ruoyi-extend/ruoyi-monitor-admin/src/main/java/com/ruoyi/monitor/admin/MonitorAdminApplication.java
ÎļþÒÑɾ³ý
ruoyi-extend/ruoyi-monitor-admin/src/main/java/com/ruoyi/monitor/admin/config/AdminServerConfig.java
ÎļþÒÑɾ³ý
ruoyi-extend/ruoyi-monitor-admin/src/main/java/com/ruoyi/monitor/admin/config/SecurityConfig.java
ÎļþÒÑɾ³ý
ruoyi-extend/ruoyi-monitor-admin/src/main/java/com/ruoyi/monitor/admin/notifier/CustomNotifier.java
ÎļþÒÑɾ³ý
ruoyi-extend/ruoyi-monitor-admin/src/main/java/org/dromara/monitor/admin/MonitorAdminApplication.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,19 @@
package org.dromara.monitor.admin;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
 * Admin ç›‘控启动程序
 *
 * @author Lion Li
 */
@SpringBootApplication
public class MonitorAdminApplication {
    public static void main(String[] args) {
        SpringApplication.run(MonitorAdminApplication.class, args);
        System.out.println("Admin ç›‘控启动成功");
    }
}
ruoyi-extend/ruoyi-monitor-admin/src/main/java/org/dromara/monitor/admin/config/AdminServerConfig.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,31 @@
package org.dromara.monitor.admin.config;
import de.codecentric.boot.admin.server.config.EnableAdminServer;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.task.TaskExecutionAutoConfiguration;
import org.springframework.boot.task.TaskExecutorBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import java.util.concurrent.Executor;
/**
 * springboot-admin server配置类
 *
 * @author Lion Li
 */
@Configuration
@EnableAdminServer
public class AdminServerConfig {
    @Lazy
    @Bean(name = TaskExecutionAutoConfiguration.APPLICATION_TASK_EXECUTOR_BEAN_NAME)
    @ConditionalOnMissingBean(Executor.class)
    public ThreadPoolTaskExecutor applicationTaskExecutor(TaskExecutorBuilder builder) {
        return builder.build();
    }
}
ruoyi-extend/ruoyi-monitor-admin/src/main/java/org/dromara/monitor/admin/config/SecurityConfig.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,52 @@
package org.dromara.monitor.admin.config;
import de.codecentric.boot.admin.server.config.AdminServerProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler;
/**
 * admin ç›‘控 å®‰å…¨é…ç½®
 *
 * @author Lion Li
 */
@EnableWebSecurity
@Configuration
public class SecurityConfig {
    private final String adminContextPath;
    public SecurityConfig(AdminServerProperties adminServerProperties) {
        this.adminContextPath = adminServerProperties.getContextPath();
    }
    @Bean
    public SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception {
        SavedRequestAwareAuthenticationSuccessHandler successHandler = new SavedRequestAwareAuthenticationSuccessHandler();
        successHandler.setTargetUrlParameter("redirectTo");
        successHandler.setDefaultTargetUrl(adminContextPath + "/");
        return httpSecurity
                .headers().frameOptions().disable()
                .and().authorizeHttpRequests()
                .requestMatchers(adminContextPath + "/assets/**"
                    , adminContextPath + "/login"
                    , "/actuator"
                    , "/actuator/**"
                ).permitAll()
                .anyRequest().authenticated()
                .and()
                .formLogin().loginPage(adminContextPath + "/login")
                .successHandler(successHandler).and()
                .logout().logoutUrl(adminContextPath + "/logout")
                .and()
                .httpBasic().and()
                .csrf()
                .disable()
                .build();
    }
}
ruoyi-extend/ruoyi-monitor-admin/src/main/java/org/dromara/monitor/admin/notifier/CustomNotifier.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,40 @@
package org.dromara.monitor.admin.notifier;
import de.codecentric.boot.admin.server.domain.entities.Instance;
import de.codecentric.boot.admin.server.domain.entities.InstanceRepository;
import de.codecentric.boot.admin.server.domain.events.InstanceEvent;
import de.codecentric.boot.admin.server.domain.events.InstanceStatusChangedEvent;
import de.codecentric.boot.admin.server.notify.AbstractEventNotifier;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Mono;
/**
 * è‡ªå®šä¹‰äº‹ä»¶é€šçŸ¥å¤„理
 *
 * @author Lion Li
 */
@Slf4j
@Component
public class CustomNotifier extends AbstractEventNotifier {
    protected CustomNotifier(InstanceRepository repository) {
        super(repository);
    }
    @Override
    @SuppressWarnings("all")
    protected Mono<Void> doNotify(InstanceEvent event, Instance instance) {
        return Mono.fromRunnable(() -> {
            // å®žä¾‹çŠ¶æ€æ”¹å˜äº‹ä»¶
            if (event instanceof InstanceStatusChangedEvent) {
                String registName = instance.getRegistration().getName();
                String instanceId = event.getInstance().getValue();
                String status = ((InstanceStatusChangedEvent) event).getStatusInfo().getStatus();
                log.info("Instance Status Change: [{}],[{}],[{}]", registName, instanceId, status);
            }
        });
    }
}
ruoyi-extend/ruoyi-xxl-job-admin/pom.xml
@@ -3,7 +3,7 @@
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <artifactId>ruoyi-extend</artifactId>
        <groupId>com.ruoyi</groupId>
        <groupId>org.dromara</groupId>
        <version>${revision}</version>
        <relativePath>../pom.xml</relativePath>
    </parent>
ruoyi-modules/pom.xml
@@ -4,7 +4,7 @@
         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>
        <groupId>org.dromara</groupId>
        <version>${revision}</version>
        <relativePath>../pom.xml</relativePath>
    </parent>
ruoyi-modules/ruoyi-demo/pom.xml
@@ -3,7 +3,7 @@
         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>
        <groupId>org.dromara</groupId>
        <artifactId>ruoyi-modules</artifactId>
        <version>${revision}</version>
        <relativePath>../pom.xml</relativePath>
@@ -20,82 +20,82 @@
        <!-- é€šç”¨å·¥å…·-->
        <dependency>
            <groupId>com.ruoyi</groupId>
            <groupId>org.dromara</groupId>
            <artifactId>ruoyi-common-core</artifactId>
        </dependency>
        <dependency>
            <groupId>com.ruoyi</groupId>
            <groupId>org.dromara</groupId>
            <artifactId>ruoyi-common-doc</artifactId>
        </dependency>
        <dependency>
            <groupId>com.ruoyi</groupId>
            <groupId>org.dromara</groupId>
            <artifactId>ruoyi-common-sms</artifactId>
        </dependency>
        <dependency>
            <groupId>com.ruoyi</groupId>
            <groupId>org.dromara</groupId>
            <artifactId>ruoyi-common-mail</artifactId>
        </dependency>
        <dependency>
            <groupId>com.ruoyi</groupId>
            <groupId>org.dromara</groupId>
            <artifactId>ruoyi-common-redis</artifactId>
        </dependency>
        <dependency>
            <groupId>com.ruoyi</groupId>
            <groupId>org.dromara</groupId>
            <artifactId>ruoyi-common-idempotent</artifactId>
        </dependency>
        <dependency>
            <groupId>com.ruoyi</groupId>
            <groupId>org.dromara</groupId>
            <artifactId>ruoyi-common-mybatis</artifactId>
        </dependency>
        <dependency>
            <groupId>com.ruoyi</groupId>
            <groupId>org.dromara</groupId>
            <artifactId>ruoyi-common-log</artifactId>
        </dependency>
        <dependency>
            <groupId>com.ruoyi</groupId>
            <groupId>org.dromara</groupId>
            <artifactId>ruoyi-common-excel</artifactId>
        </dependency>
        <dependency>
            <groupId>com.ruoyi</groupId>
            <groupId>org.dromara</groupId>
            <artifactId>ruoyi-common-web</artifactId>
        </dependency>
        <dependency>
            <groupId>com.ruoyi</groupId>
            <groupId>org.dromara</groupId>
            <artifactId>ruoyi-common-ratelimiter</artifactId>
        </dependency>
        <dependency>
            <groupId>com.ruoyi</groupId>
            <groupId>org.dromara</groupId>
            <artifactId>ruoyi-common-translation</artifactId>
        </dependency>
        <dependency>
            <groupId>com.ruoyi</groupId>
            <groupId>org.dromara</groupId>
            <artifactId>ruoyi-common-sensitive</artifactId>
        </dependency>
        <dependency>
            <groupId>com.ruoyi</groupId>
            <groupId>org.dromara</groupId>
            <artifactId>ruoyi-common-encrypt</artifactId>
        </dependency>
        <dependency>
            <groupId>com.ruoyi</groupId>
            <groupId>org.dromara</groupId>
            <artifactId>ruoyi-common-tenant</artifactId>
        </dependency>
        <dependency>
            <groupId>com.ruoyi</groupId>
            <groupId>org.dromara</groupId>
            <artifactId>ruoyi-common-websocket</artifactId>
        </dependency>
        <!-- çŸ­ä¿¡ ç”¨å“ªä¸ªå¯¼å…¥å“ªä¸ªä¾èµ– -->
ruoyi-modules/ruoyi-demo/src/main/java/com/ruoyi/demo/controller/MailController.java
ÎļþÒÑɾ³ý
ruoyi-modules/ruoyi-demo/src/main/java/com/ruoyi/demo/controller/RedisCacheController.java
ÎļþÒÑɾ³ý
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
ÎļþÒÑɾ³ý
ruoyi-modules/ruoyi-demo/src/main/java/com/ruoyi/demo/controller/RedisRateLimiterController.java
ÎļþÒÑɾ³ý
ruoyi-modules/ruoyi-demo/src/main/java/com/ruoyi/demo/controller/SmsController.java
ÎļþÒÑɾ³ý
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
ÎļþÒÑɾ³ý
ruoyi-modules/ruoyi-demo/src/main/java/com/ruoyi/demo/controller/TestDemoController.java
ÎļþÒÑɾ³ý
ruoyi-modules/ruoyi-demo/src/main/java/com/ruoyi/demo/controller/TestEncryptController.java
ÎļþÒÑɾ³ý
ruoyi-modules/ruoyi-demo/src/main/java/com/ruoyi/demo/controller/TestExcelController.java
ÎļþÒÑɾ³ý
ruoyi-modules/ruoyi-demo/src/main/java/com/ruoyi/demo/controller/TestI18nController.java
ÎļþÒÑɾ³ý
ruoyi-modules/ruoyi-demo/src/main/java/com/ruoyi/demo/controller/TestSensitiveController.java
ÎļþÒÑɾ³ý
ruoyi-modules/ruoyi-demo/src/main/java/com/ruoyi/demo/controller/TestTreeController.java
ÎļþÒÑɾ³ý
ruoyi-modules/ruoyi-demo/src/main/java/com/ruoyi/demo/controller/WeSocketController.java
ÎļþÒÑɾ³ý
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
ÎļþÒÑɾ³ý
ruoyi-modules/ruoyi-demo/src/main/java/com/ruoyi/demo/controller/queue/DelayedQueueController.java
ÎļþÒÑɾ³ý
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
ÎļþÒÑɾ³ý
ruoyi-modules/ruoyi-demo/src/main/java/com/ruoyi/demo/domain/TestDemo.java
ÎļþÒÑɾ³ý
ruoyi-modules/ruoyi-demo/src/main/java/com/ruoyi/demo/domain/TestDemoEncrypt.java
ÎļþÒÑɾ³ý
ruoyi-modules/ruoyi-demo/src/main/java/com/ruoyi/demo/domain/TestTree.java
ÎļþÒÑɾ³ý
ruoyi-modules/ruoyi-demo/src/main/java/com/ruoyi/demo/domain/bo/TestDemoBo.java
ÎļþÒÑɾ³ý
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
ÎļþÒÑɾ³ý
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/TestDemoEncryptMapper.java
ÎļþÒÑɾ³ý
ruoyi-modules/ruoyi-demo/src/main/java/com/ruoyi/demo/mapper/TestDemoMapper.java
ÎļþÒÑɾ³ý
ruoyi-modules/ruoyi-demo/src/main/java/com/ruoyi/demo/mapper/TestTreeMapper.java
ÎļþÒÑɾ³ý
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
ÎļþÒÑɾ³ý
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
ÎļþÒÑɾ³ý
ruoyi-modules/ruoyi-demo/src/main/java/com/ruoyi/demo/service/impl/TestTreeServiceImpl.java
ÎļþÒÑɾ³ý
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/java/org/dromara/demo/controller/MailController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,52 @@
package org.dromara.demo.controller;
import org.dromara.common.core.domain.R;
import org.dromara.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/org/dromara/demo/controller/RedisCacheController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,95 @@
package org.dromara.demo.controller;
import org.dromara.common.core.constant.CacheNames;
import org.dromara.common.core.domain.R;
import org.dromara.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/org/dromara/demo/controller/RedisLockController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,71 @@
package org.dromara.demo.controller;
import com.baomidou.lock.LockInfo;
import com.baomidou.lock.LockTemplate;
import com.baomidou.lock.annotation.Lock4j;
import com.baomidou.lock.executor.RedissonLockExecutor;
import org.dromara.common.core.domain.R;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.time.LocalTime;
/**
 * æµ‹è¯•分布式锁的样例
 *
 * @author shenxinquan
 */
@Slf4j
@RestController
@RequestMapping("/demo/redisLock")
public class RedisLockController {
    @Autowired
    private LockTemplate lockTemplate;
    /**
     * æµ‹è¯•lock4j æ³¨è§£
     */
    @Lock4j(keys = {"#key"})
    @GetMapping("/testLock4j")
    public R<String> testLock4j(String key, String value) {
        System.out.println("start:" + key + ",time:" + LocalTime.now().toString());
        try {
            Thread.sleep(10000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("end :" + key + ",time:" + LocalTime.now().toString());
        return R.ok("操作成功", value);
    }
    /**
     * æµ‹è¯•lock4j å·¥å…·
     */
    @GetMapping("/testLock4jLockTemplate")
    public R<String> testLock4jLockTemplate(String key, String value) {
        final LockInfo lockInfo = lockTemplate.lock(key, 30000L, 5000L, RedissonLockExecutor.class);
        if (null == lockInfo) {
            throw new RuntimeException("业务处理中,请稍后再试");
        }
        // èŽ·å–é”æˆåŠŸï¼Œå¤„ç†ä¸šåŠ¡
        try {
            try {
                Thread.sleep(8000);
            } catch (InterruptedException e) {
                //
            }
            System.out.println("执行简单方法1 , å½“前线程:" + Thread.currentThread().getName());
        } finally {
            //释放锁
            lockTemplate.releaseLock(lockInfo);
        }
        //结束
        return R.ok("操作成功", value);
    }
}
ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/controller/RedisPubSubController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,47 @@
package org.dromara.demo.controller;
import org.dromara.common.core.domain.R;
import org.dromara.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/org/dromara/demo/controller/RedisRateLimiterController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,52 @@
package org.dromara.demo.controller;
import org.dromara.common.core.domain.R;
import org.dromara.common.ratelimiter.annotation.RateLimiter;
import org.dromara.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/org/dromara/demo/controller/SmsController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,76 @@
package org.dromara.demo.controller;
import org.dromara.common.core.domain.R;
import org.dromara.common.core.utils.SpringUtils;
import org.dromara.common.sms.config.properties.SmsProperties;
import org.dromara.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/org/dromara/demo/controller/Swagger3DemoController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,31 @@
package org.dromara.demo.controller;
import org.dromara.common.core.domain.R;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestPart;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
/**
 * swagger3 ç”¨æ³•示例
 *
 * @author Lion Li
 */
@RestController
@RequestMapping("/swagger/demo")
public class Swagger3DemoController {
    /**
     * ä¸Šä¼ è¯·æ±‚
     * å¿…须使用 @RequestPart æ³¨è§£æ ‡æ³¨ä¸ºæ–‡ä»¶
     *
     * @param file æ–‡ä»¶
     */
    @PostMapping(value = "/upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
    public R<String> upload(@RequestPart("file") MultipartFile file) {
        return R.ok("操作成功", file.getOriginalFilename());
    }
}
ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/controller/TestBatchController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,90 @@
package org.dromara.demo.controller;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import org.dromara.common.core.domain.R;
import org.dromara.common.web.core.BaseController;
import org.dromara.demo.domain.TestDemo;
import org.dromara.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/org/dromara/demo/controller/TestDemoController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,147 @@
package org.dromara.demo.controller;
import cn.dev33.satoken.annotation.SaCheckPermission;
import org.dromara.common.core.domain.R;
import org.dromara.common.core.utils.MapstructUtils;
import org.dromara.common.core.utils.ValidatorUtils;
import org.dromara.common.core.validate.AddGroup;
import org.dromara.common.core.validate.EditGroup;
import org.dromara.common.core.validate.QueryGroup;
import org.dromara.common.web.core.BaseController;
import org.dromara.common.idempotent.annotation.RepeatSubmit;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.excel.core.ExcelResult;
import org.dromara.common.excel.utils.ExcelUtil;
import org.dromara.common.log.annotation.Log;
import org.dromara.common.log.enums.BusinessType;
import org.dromara.demo.domain.TestDemo;
import org.dromara.demo.domain.bo.TestDemoBo;
import org.dromara.demo.domain.bo.TestDemoImportVo;
import org.dromara.demo.domain.vo.TestDemoVo;
import org.dromara.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.*;
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 testDemoService;
    /**
     * æŸ¥è¯¢æµ‹è¯•单表列表
     */
    @SaCheckPermission("demo:demo:list")
    @GetMapping("/list")
    public TableDataInfo<TestDemoVo> list(@Validated(QueryGroup.class) TestDemoBo bo, PageQuery pageQuery) {
        return testDemoService.queryPageList(bo, pageQuery);
    }
    /**
     * è‡ªå®šä¹‰åˆ†é¡µæŸ¥è¯¢
     */
    @SaCheckPermission("demo:demo:list")
    @GetMapping("/page")
    public TableDataInfo<TestDemoVo> page(@Validated(QueryGroup.class) TestDemoBo bo, PageQuery pageQuery) {
        return testDemoService.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<TestDemo> list = MapstructUtils.convert(excelResult.getList(), TestDemo.class);
        testDemoService.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 = testDemoService.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(testDemoService.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(testDemoService.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(testDemoService.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(testDemoService.deleteWithValidByIds(Arrays.asList(ids), true));
    }
}
ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/controller/TestEncryptController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,55 @@
package org.dromara.demo.controller;
import org.dromara.common.core.domain.R;
import org.dromara.demo.domain.TestDemoEncrypt;
import org.dromara.demo.mapper.TestDemoEncryptMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
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
 */
@Validated
@RestController
@RequestMapping("/demo/encrypt")
public class TestEncryptController {
    @Autowired
    private TestDemoEncryptMapper mapper;
    @Value("${mybatis-encryptor.enable}")
    private Boolean encryptEnable;
    /**
     * æµ‹è¯•数据库加解密
     *
     * @param key   æµ‹è¯•key
     * @param value æµ‹è¯•value
     */
    @GetMapping()
    public R<Map<String, TestDemoEncrypt>> test(String key, String value) {
        if (!encryptEnable) {
            throw new RuntimeException("加密功能未开启!");
        }
        Map<String, TestDemoEncrypt> map = new HashMap<>(2);
        TestDemoEncrypt demo = new TestDemoEncrypt();
        demo.setTestKey(key);
        demo.setValue(value);
        mapper.insert(demo);
        map.put("加密", demo);
        TestDemoEncrypt testDemo = mapper.selectById(demo.getId());
        map.put("解密", testDemo);
        return R.ok(map);
    }
}
ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/controller/TestExcelController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,97 @@
package org.dromara.demo.controller;
import cn.hutool.core.collection.CollUtil;
import org.dromara.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/org/dromara/demo/controller/TestI18nController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,71 @@
package org.dromara.demo.controller;
import org.dromara.common.core.domain.R;
import org.dromara.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/org/dromara/demo/controller/TestSensitiveController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,76 @@
package org.dromara.demo.controller;
import org.dromara.common.core.domain.R;
import org.dromara.common.web.core.BaseController;
import org.dromara.common.sensitive.annotation.Sensitive;
import org.dromara.common.sensitive.core.SensitiveStrategy;
import lombok.Data;
import org.dromara.common.sensitive.core.SensitiveService;
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 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/org/dromara/demo/controller/TestTreeController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,107 @@
package org.dromara.demo.controller;
import cn.dev33.satoken.annotation.SaCheckPermission;
import org.dromara.common.core.domain.R;
import org.dromara.common.core.validate.AddGroup;
import org.dromara.common.core.validate.EditGroup;
import org.dromara.common.core.validate.QueryGroup;
import org.dromara.common.web.core.BaseController;
import org.dromara.common.excel.utils.ExcelUtil;
import org.dromara.common.idempotent.annotation.RepeatSubmit;
import org.dromara.common.log.annotation.Log;
import org.dromara.common.log.enums.BusinessType;
import org.dromara.demo.domain.bo.TestTreeBo;
import org.dromara.demo.domain.vo.TestTreeVo;
import org.dromara.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 testTreeService;
    /**
     * æŸ¥è¯¢æµ‹è¯•树表列表
     */
    @SaCheckPermission("demo:tree:list")
    @GetMapping("/list")
    public R<List<TestTreeVo>> list(@Validated(QueryGroup.class) TestTreeBo bo) {
        List<TestTreeVo> list = testTreeService.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 = testTreeService.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(testTreeService.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(testTreeService.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(testTreeService.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(testTreeService.deleteWithValidByIds(Arrays.asList(ids), true));
    }
}
ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/controller/WeSocketController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,33 @@
package org.dromara.demo.controller;
import org.dromara.common.core.domain.R;
import org.dromara.common.websocket.dto.WebSocketMessageDto;
import org.dromara.common.websocket.utils.WebSocketUtils;
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;
/**
 * WebSocket æ¼”示案例
 *
 * @author zendwang
 */
@RequiredArgsConstructor
@RestController
@RequestMapping("/demo/websocket")
@Slf4j
public class WeSocketController {
    /**
     * å‘布消息
     *
     * @param dto å‘送内容
     */
    @GetMapping("/send")
    public R<Void> send(WebSocketMessageDto dto) throws InterruptedException {
        WebSocketUtils.publishMessage(dto);
        return R.ok("操作成功");
    }
}
ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/controller/package-info.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1 @@
package org.dromara.demo.controller;
ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/controller/queue/BoundedQueueController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,90 @@
package org.dromara.demo.controller.queue;
import org.dromara.common.core.domain.R;
import org.dromara.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/org/dromara/demo/controller/queue/DelayedQueueController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,90 @@
package org.dromara.demo.controller.queue;
import org.dromara.common.core.domain.R;
import org.dromara.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/org/dromara/demo/controller/queue/PriorityDemo.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,22 @@
package org.dromara.demo.controller.queue;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
 * å®žä½“ç±» æ³¨æ„ä¸å…è®¸ä½¿ç”¨å†…部类 å¦åˆ™ä¼šæ‰¾ä¸åˆ°ç±»
 *
 * @author Lion Li
 * @version 3.6.0
 */
@Data
@NoArgsConstructor
public class PriorityDemo implements Comparable<PriorityDemo> {
    private String name;
    private Integer orderNum;
    @Override
    public int compareTo(PriorityDemo other) {
        return Integer.compare(getOrderNum(), other.getOrderNum());
    }
}
ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/controller/queue/PriorityQueueController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,89 @@
package org.dromara.demo.controller.queue;
import cn.hutool.core.util.RandomUtil;
import org.dromara.common.core.domain.R;
import org.dromara.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/org/dromara/demo/domain/TestDemo.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,68 @@
package org.dromara.demo.domain;
import com.baomidou.mybatisplus.annotation.*;
import org.dromara.common.tenant.core.TenantEntity;
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 TenantEntity {
    @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/org/dromara/demo/domain/TestDemoEncrypt.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,29 @@
package org.dromara.demo.domain;
import com.baomidou.mybatisplus.annotation.TableName;
import org.dromara.common.encrypt.annotation.EncryptField;
import org.dromara.common.encrypt.enumd.AlgorithmType;
import lombok.Data;
import lombok.EqualsAndHashCode;
@Data
@EqualsAndHashCode(callSuper = true)
@TableName("test_demo")
public class TestDemoEncrypt extends TestDemo {
    /**
     * key键
     */
    // @EncryptField(algorithm=AlgorithmType.SM2, privateKey = "MIGTAgEAMBMGByqGSM49AgEGCCqBHM9VAYItBHkwdwIBAQQgZSlOvw8FBiH+aFJWLYZP/VRjg9wjfRarTkGBZd/T3N+gCgYIKoEcz1UBgi2hRANCAAR5DGuQwJqkxnbCsP+iPSDoHWIF4RwcR5EsSvT8QPxO1wRkR2IhCkzvRb32x2CUgJFdvoqVqfApFDPZzShqzBwX", publicKey = "MFkwEwYHKoZIzj0CAQYIKoEcz1UBgi0DQgAEeQxrkMCapMZ2wrD/oj0g6B1iBeEcHEeRLEr0/ED8TtcEZEdiIQpM70W99sdglICRXb6KlanwKRQz2c0oaswcFw==")
    @EncryptField(algorithm = AlgorithmType.RSA, privateKey = "MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBANBBEeueWlXlkkj2+WY5l+IWe42d8b5K28g+G/CFKC/yYAEHtqGlCsBOrb+YBkG9mPzmuYA/n9k0NFIc8E8yY5vZQaroyFBrTTWEzG9RY2f7Y3svVyybs6jpXSUs4xff8abo7wL1Y/wUaeatTViamxYnyTvdTmLm3d+JjRij68rxAgMBAAECgYAB0TnhXraSopwIVRfmboea1b0upl+BUdTJcmci412UjrKr5aE695ZLPkXbFXijVu7HJlyyv94NVUdaMACV7Ku/S2RuNB70M7YJm8rAjHFC3/i2ZeIM60h1Ziy4QKv0XM3pRATlDCDNhC1WUrtQCQSgU8kcp6eUUppruOqDzcY04QJBAPm9+sBP9CwDRgy3e5+V8aZtJkwDstb0lVVV/KY890cydVxiCwvX3fqVnxKMlb+x0YtH0sb9v+71xvK2lGobaRECQQDVePU6r/cCEfpc+nkWF6osAH1f8Mux3rYv2DoBGvaPzV2BGfsLed4neRfCwWNCKvGPCdW+L0xMJg8+RwaoBUPhAkAT5kViqXxFPYWJYd1h2+rDXhMdH3ZSlm6HvDBDdrwlWinr0Iwcx3iSjPV93uHXwm118aUj4fg3LDJMCKxOwBxhAkByrQXfvwOMYygBprRBf/j0plazoWFrbd6lGR0f1uI5IfNnFRPdeFw1DEINZ2Hw+6zEUF44SqRMC+4IYJNc02dBAkBCgy7RvfyV/A7N6kKXxTHauY0v6XwSSvpeKtRJkbIcRWOdIYvaHO9L7cklj3vIEdwjSUp9K4VTBYYlmAz1xh03", publicKey = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDQQRHrnlpV5ZJI9vlmOZfiFnuNnfG+StvIPhvwhSgv8mABB7ahpQrATq2/mAZBvZj85rmAP5/ZNDRSHPBPMmOb2UGq6MhQa001hMxvUWNn+2N7L1csm7Oo6V0lLOMX3/Gm6O8C9WP8FGnmrU1YmpsWJ8k73U5i5t3fiY0Yo+vK8QIDAQAB")
    private String testKey;
    /**
     * å€¼
     */
    // @EncryptField // ä»€ä¹ˆä¹Ÿä¸å†™èµ°é»˜è®¤yml配置
    // @EncryptField(algorithm = AlgorithmType.SM4, password = "10rfylhtccpuyke5")
    @EncryptField(algorithm = AlgorithmType.AES, password = "10rfylhtccpuyke5")
    private String value;
}
ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/domain/TestTree.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,65 @@
package org.dromara.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 org.dromara.common.tenant.core.TenantEntity;
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 TenantEntity {
    @Serial
    private static final long serialVersionUID = 1L;
    /**
     * ä¸»é”®
     */
    @TableId(value = "id")
    private Long id;
    /**
     * çˆ¶ID
     */
    private Long parentId;
    /**
     * éƒ¨é—¨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/org/dromara/demo/domain/bo/TestDemoBo.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,62 @@
package org.dromara.demo.domain.bo;
import org.dromara.common.core.validate.AddGroup;
import org.dromara.common.core.validate.EditGroup;
import org.dromara.common.mybatis.core.domain.BaseEntity;
import org.dromara.demo.domain.TestDemo;
import io.github.linpeilie.annotations.AutoMapper;
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)
@AutoMapper(target = TestDemo.class, reverseConvertGenerate = false)
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/org/dromara/demo/domain/bo/TestDemoImportVo.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,53 @@
package org.dromara.demo.domain.bo;
import com.alibaba.excel.annotation.ExcelProperty;
import lombok.Data;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
/**
 * æµ‹è¯•单表业务对象 test_demo
 *
 * @author Lion Li
 * @date 2021-07-26
 */
@Data
public class TestDemoImportVo {
    /**
     * éƒ¨é—¨id
     */
    @NotNull(message = "部门id不能为空")
    @ExcelProperty(value = "部门id")
    private Long deptId;
    /**
     * ç”¨æˆ·id
     */
    @NotNull(message = "用户id不能为空")
    @ExcelProperty(value = "用户id")
    private Long userId;
    /**
     * æŽ’序号
     */
    @NotNull(message = "排序号不能为空")
    @ExcelProperty(value = "排序号")
    private Long orderNum;
    /**
     * key键
     */
    @NotBlank(message = "key键不能为空")
    @ExcelProperty(value = "key键")
    private String testKey;
    /**
     * å€¼
     */
    @NotBlank(message = "值不能为空")
    @ExcelProperty(value = "值")
    private String value;
}
ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/domain/bo/TestTreeBo.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,54 @@
package org.dromara.demo.domain.bo;
import org.dromara.common.core.validate.AddGroup;
import org.dromara.common.core.validate.EditGroup;
import org.dromara.common.mybatis.core.domain.BaseEntity;
import org.dromara.demo.domain.TestTree;
import io.github.linpeilie.annotations.AutoMapper;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
 * æµ‹è¯•树表业务对象 test_tree
 *
 * @author Lion Li
 * @date 2021-07-26
 */
@Data
@EqualsAndHashCode(callSuper = true)
@AutoMapper(target = TestTree.class, reverseConvertGenerate = false)
public class TestTreeBo extends BaseEntity {
    /**
     * ä¸»é”®
     */
    @NotNull(message = "主键不能为空", groups = {EditGroup.class})
    private Long id;
    /**
     * çˆ¶ID
     */
    private Long parentId;
    /**
     * éƒ¨é—¨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/org/dromara/demo/domain/package-info.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1 @@
package org.dromara.demo.domain;
ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/domain/vo/TestDemoVo.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,104 @@
package org.dromara.demo.domain.vo;
import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
import com.alibaba.excel.annotation.ExcelProperty;
import org.dromara.common.translation.annotation.Translation;
import org.dromara.common.translation.constant.TransConstant;
import org.dromara.demo.domain.TestDemo;
import io.github.linpeilie.annotations.AutoMapper;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
import java.util.Date;
/**
 * æµ‹è¯•单表视图对象 test_demo
 *
 * @author Lion Li
 * @date 2021-07-26
 */
@Data
@ExcelIgnoreUnannotated
@AutoMapper(target = TestDemo.class)
public class TestDemoVo implements Serializable {
    @Serial
    private static final long serialVersionUID = 1L;
    /**
     * ä¸»é”®
     */
    @ExcelProperty(value = "主键")
    private Long id;
    /**
     * éƒ¨é—¨id
     */
    @ExcelProperty(value = "部门id")
    private Long deptId;
    /**
     * ç”¨æˆ·id
     */
    @ExcelProperty(value = "用户id")
    private Long userId;
    /**
     * æŽ’序号
     */
    @ExcelProperty(value = "排序号")
    private Integer orderNum;
    /**
     * key键
     */
    @ExcelProperty(value = "key键")
    private String testKey;
    /**
     * å€¼
     */
    @ExcelProperty(value = "值")
    private String value;
    /**
     * åˆ›å»ºæ—¶é—´
     */
    @ExcelProperty(value = "创建时间")
    private Date createTime;
    /**
     * åˆ›å»ºäºº
     */
    @ExcelProperty(value = "创建人")
    private Long createBy;
    /**
     * åˆ›å»ºäººè´¦å·
     */
    @Translation(type = TransConstant.USER_ID_TO_NAME, mapper = "createBy")
    @ExcelProperty(value = "创建人账号")
    private String createByName;
    /**
     * æ›´æ–°æ—¶é—´
     */
    @ExcelProperty(value = "更新时间")
    private Date updateTime;
    /**
     * æ›´æ–°äºº
     */
    @ExcelProperty(value = "更新人")
    private Long updateBy;
    /**
     * æ›´æ–°äººè´¦å·
     */
    @Translation(type = TransConstant.USER_ID_TO_NAME, mapper = "updateBy")
    @ExcelProperty(value = "更新人账号")
    private String updateByName;
}
ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/domain/vo/TestTreeVo.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,64 @@
package org.dromara.demo.domain.vo;
import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
import com.alibaba.excel.annotation.ExcelProperty;
import org.dromara.demo.domain.TestTree;
import io.github.linpeilie.annotations.AutoMapper;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
import java.util.Date;
/**
 * æµ‹è¯•树表视图对象 test_tree
 *
 * @author Lion Li
 * @date 2021-07-26
 */
@Data
@ExcelIgnoreUnannotated
@AutoMapper(target = TestTree.class)
public class TestTreeVo implements Serializable {
    @Serial
    private static final long serialVersionUID = 1L;
    /**
     * ä¸»é”®
     */
    private Long id;
    /**
     * çˆ¶id
     */
    @ExcelProperty(value = "父id")
    private Long parentId;
    /**
     * éƒ¨é—¨id
     */
    @ExcelProperty(value = "部门id")
    private Long deptId;
    /**
     * ç”¨æˆ·id
     */
    @ExcelProperty(value = "用户id")
    private Long userId;
    /**
     * æ ‘节点名
     */
    @ExcelProperty(value = "树节点名")
    private String treeName;
    /**
     * åˆ›å»ºæ—¶é—´
     */
    @ExcelProperty(value = "创建时间")
    private Date createTime;
}
ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/mapper/TestDemoEncryptMapper.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,13 @@
package org.dromara.demo.mapper;
import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
import org.dromara.demo.domain.TestDemoEncrypt;
/**
 * æµ‹è¯•加密功能
 *
 * @author Lion Li
 */
public interface TestDemoEncryptMapper extends BaseMapperPlus<TestDemoEncrypt, TestDemoEncrypt> {
}
ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/mapper/TestDemoMapper.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,58 @@
package org.dromara.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 org.dromara.common.mybatis.annotation.DataColumn;
import org.dromara.common.mybatis.annotation.DataPermission;
import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
import org.dromara.demo.domain.TestDemo;
import org.dromara.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<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/org/dromara/demo/mapper/TestTreeMapper.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,21 @@
package org.dromara.demo.mapper;
import org.dromara.common.mybatis.annotation.DataColumn;
import org.dromara.common.mybatis.annotation.DataPermission;
import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
import org.dromara.demo.domain.TestTree;
import org.dromara.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<TestTree, TestTreeVo> {
}
ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/mapper/package-info.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1 @@
package org.dromara.demo.mapper;
ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/service/ITestDemoService.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,71 @@
package org.dromara.demo.service;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.demo.domain.TestDemo;
import org.dromara.demo.domain.bo.TestDemoBo;
import org.dromara.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/org/dromara/demo/service/ITestTreeService.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,52 @@
package org.dromara.demo.service;
import org.dromara.demo.domain.bo.TestTreeBo;
import org.dromara.demo.domain.vo.TestTreeVo;
import java.util.Collection;
import java.util.List;
/**
 * æµ‹è¯•树表Service接口
 *
 * @author Lion Li
 * @date 2021-07-26
 */
public interface ITestTreeService {
    /**
     * æŸ¥è¯¢å•个
     *
     * @return
     */
    TestTreeVo queryById(Long id);
    /**
     * æŸ¥è¯¢åˆ—表
     */
    List<TestTreeVo> queryList(TestTreeBo bo);
    /**
     * æ ¹æ®æ–°å¢žä¸šåŠ¡å¯¹è±¡æ’å…¥æµ‹è¯•æ ‘è¡¨
     *
     * @param bo æµ‹è¯•树表新增业务对象
     * @return
     */
    Boolean insertByBo(TestTreeBo bo);
    /**
     * æ ¹æ®ç¼–辑业务对象修改测试树表
     *
     * @param bo æµ‹è¯•树表编辑业务对象
     * @return
     */
    Boolean updateByBo(TestTreeBo bo);
    /**
     * æ ¡éªŒå¹¶åˆ é™¤æ•°æ®
     *
     * @param ids     ä¸»é”®é›†åˆ
     * @param isValid æ˜¯å¦æ ¡éªŒ,true-删除前校验,false-不校验
     * @return
     */
    Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);
}
ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/service/impl/TestDemoServiceImpl.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,110 @@
package org.dromara.demo.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.dromara.common.core.utils.MapstructUtils;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.demo.domain.TestDemo;
import org.dromara.demo.domain.bo.TestDemoBo;
import org.dromara.demo.domain.vo.TestDemoVo;
import org.dromara.demo.mapper.TestDemoMapper;
import org.dromara.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 = MapstructUtils.convert(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 = MapstructUtils.convert(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/org/dromara/demo/service/impl/TestTreeServiceImpl.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,87 @@
package org.dromara.demo.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import org.dromara.common.core.utils.MapstructUtils;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.demo.domain.TestTree;
import org.dromara.demo.domain.bo.TestTreeBo;
import org.dromara.demo.domain.vo.TestTreeVo;
import org.dromara.demo.mapper.TestTreeMapper;
import org.dromara.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 = MapstructUtils.convert(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 = MapstructUtils.convert(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/org/dromara/demo/service/impl/package-info.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1 @@
package org.dromara.demo.service.impl;
在上述文件截断后对比
ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/service/package-info.java 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-generator/pom.xml ruoyi-modules/ruoyi-generator/src/main/java/com/ruoyi/generator/config/GenConfig.java (已删除) ruoyi-modules/ruoyi-generator/src/main/java/com/ruoyi/generator/constant/GenConstants.java (已删除) ruoyi-modules/ruoyi-generator/src/main/java/com/ruoyi/generator/controller/GenController.java (已删除) ruoyi-modules/ruoyi-generator/src/main/java/com/ruoyi/generator/domain/GenTable.java (已删除) ruoyi-modules/ruoyi-generator/src/main/java/com/ruoyi/generator/domain/GenTableColumn.java (已删除) ruoyi-modules/ruoyi-generator/src/main/java/com/ruoyi/generator/mapper/GenTableColumnMapper.java (已删除) ruoyi-modules/ruoyi-generator/src/main/java/com/ruoyi/generator/mapper/GenTableMapper.java (已删除) ruoyi-modules/ruoyi-generator/src/main/java/com/ruoyi/generator/service/GenTableServiceImpl.java (已删除) ruoyi-modules/ruoyi-generator/src/main/java/com/ruoyi/generator/service/IGenTableService.java (已删除) ruoyi-modules/ruoyi-generator/src/main/java/com/ruoyi/generator/util/GenUtils.java (已删除) ruoyi-modules/ruoyi-generator/src/main/java/com/ruoyi/generator/util/VelocityInitializer.java (已删除) ruoyi-modules/ruoyi-generator/src/main/java/com/ruoyi/generator/util/VelocityUtils.java (已删除) ruoyi-modules/ruoyi-generator/src/main/java/org/dromara/generator/config/GenConfig.java ruoyi-modules/ruoyi-generator/src/main/java/org/dromara/generator/constant/GenConstants.java ruoyi-modules/ruoyi-generator/src/main/java/org/dromara/generator/controller/GenController.java ruoyi-modules/ruoyi-generator/src/main/java/org/dromara/generator/domain/GenTable.java ruoyi-modules/ruoyi-generator/src/main/java/org/dromara/generator/domain/GenTableColumn.java ruoyi-modules/ruoyi-generator/src/main/java/org/dromara/generator/mapper/GenTableColumnMapper.java ruoyi-modules/ruoyi-generator/src/main/java/org/dromara/generator/mapper/GenTableMapper.java ruoyi-modules/ruoyi-generator/src/main/java/org/dromara/generator/service/GenTableServiceImpl.java ruoyi-modules/ruoyi-generator/src/main/java/org/dromara/generator/service/IGenTableService.java ruoyi-modules/ruoyi-generator/src/main/java/org/dromara/generator/util/GenUtils.java ruoyi-modules/ruoyi-generator/src/main/java/org/dromara/generator/util/VelocityInitializer.java ruoyi-modules/ruoyi-generator/src/main/java/org/dromara/generator/util/VelocityUtils.java ruoyi-modules/ruoyi-generator/src/main/resources/generator.yml ruoyi-modules/ruoyi-generator/src/main/resources/mapper/generator/GenTableColumnMapper.xml ruoyi-modules/ruoyi-generator/src/main/resources/mapper/generator/GenTableMapper.xml ruoyi-modules/ruoyi-generator/src/main/resources/vm/java/bo.java.vm ruoyi-modules/ruoyi-generator/src/main/resources/vm/java/controller.java.vm ruoyi-modules/ruoyi-generator/src/main/resources/vm/java/domain.java.vm ruoyi-modules/ruoyi-generator/src/main/resources/vm/java/mapper.java.vm ruoyi-modules/ruoyi-generator/src/main/resources/vm/java/service.java.vm ruoyi-modules/ruoyi-generator/src/main/resources/vm/java/serviceImpl.java.vm ruoyi-modules/ruoyi-generator/src/main/resources/vm/java/vo.java.vm ruoyi-modules/ruoyi-job/pom.xml ruoyi-modules/ruoyi-job/src/main/java/com/ruoyi/job/service/SampleService.java (已删除) ruoyi-modules/ruoyi-job/src/main/java/org/dromara/job/service/SampleService.java ruoyi-modules/ruoyi-system/pom.xml ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/controller/monitor/CacheController.java (已删除) ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/controller/monitor/SysLogininforController.java (已删除) ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/controller/monitor/SysOperlogController.java (已删除) ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/controller/monitor/SysUserOnlineController.java (已删除) ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/controller/system/SysConfigController.java (已删除) ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/controller/system/SysDeptController.java (已删除) ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/controller/system/SysDictDataController.java (已删除) ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/controller/system/SysDictTypeController.java (已删除) ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/controller/system/SysMenuController.java (已删除) ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/controller/system/SysNoticeController.java (已删除) ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/controller/system/SysOssConfigController.java (已删除) ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/controller/system/SysOssController.java (已删除) ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/controller/system/SysPostController.java (已删除) ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/controller/system/SysProfileController.java (已删除) ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/controller/system/SysRoleController.java (已删除) ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/controller/system/SysTenantController.java (已删除) ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/controller/system/SysTenantPackageController.java (已删除) ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/controller/system/SysUserController.java (已删除) ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysCache.java (已删除) ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysConfig.java (已删除) ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysDept.java (已删除) ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysDictData.java (已删除) 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/SysTenant.java (已删除) ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysTenantPackage.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/SysConfigBo.java (已删除) ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/bo/SysDeptBo.java (已删除) ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/bo/SysDictDataBo.java (已删除) ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/bo/SysDictTypeBo.java (已删除) ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/bo/SysLogininforBo.java (已删除) ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/bo/SysMenuBo.java (已删除) ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/bo/SysNoticeBo.java (已删除) ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/bo/SysOperLogBo.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/bo/SysPostBo.java (已删除) ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/bo/SysRoleBo.java (已删除) ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/bo/SysTenantBo.java (已删除) ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/bo/SysTenantPackageBo.java (已删除) ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/bo/SysUserBo.java (已删除) ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/bo/SysUserProfileBo.java (已删除) ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/AvatarVo.java (已删除) ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/CacheListInfoVo.java (已删除) ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/DeptTreeSelectVo.java (已删除) ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/MenuTreeSelectVo.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/ProfileVo.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/SysConfigVo.java (已删除) ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/SysDeptVo.java (已删除) ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/SysDictDataVo.java (已删除) ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/SysDictTypeVo.java (已删除) ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/SysLogininforVo.java (已删除) ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/SysMenuVo.java (已删除) ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/SysNoticeVo.java (已删除) ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/SysOperLogVo.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/SysOssUploadVo.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/SysPostVo.java (已删除) ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/SysRoleVo.java (已删除) ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/SysTenantPackageVo.java (已删除) ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/SysTenantVo.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/domain/vo/SysUserInfoVo.java (已删除) ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/SysUserVo.java (已删除) ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/UserInfoVo.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/SysTenantMapper.java (已删除) ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysTenantPackageMapper.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/ISysPermissionService.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/ISysTenantPackageService.java (已删除) ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysTenantService.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/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/SysPermissionServiceImpl.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/SysTenantPackageServiceImpl.java (已删除) ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysTenantServiceImpl.java (已删除) ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysUserServiceImpl.java (已删除) ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/monitor/CacheController.java ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/monitor/SysLogininforController.java ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/monitor/SysOperlogController.java ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/monitor/SysUserOnlineController.java ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysConfigController.java ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysDeptController.java ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysDictDataController.java ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysDictTypeController.java ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysMenuController.java ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysNoticeController.java ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysOssConfigController.java ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysOssController.java ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysPostController.java ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysProfileController.java ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysRoleController.java ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysTenantController.java ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysTenantPackageController.java ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysUserController.java ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/SysCache.java ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/SysConfig.java ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/SysDept.java ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/SysDictData.java ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/SysDictType.java ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/SysLogininfor.java ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/SysMenu.java ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/SysNotice.java ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/SysOperLog.java ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/SysOss.java ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/SysOssConfig.java ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/SysPost.java ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/SysRole.java ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/SysRoleDept.java ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/SysRoleMenu.java ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/SysTenant.java ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/SysTenantPackage.java ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/SysUser.java ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/SysUserOnline.java ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/SysUserPost.java ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/SysUserRole.java ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/SysConfigBo.java ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/SysDeptBo.java ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/SysDictDataBo.java ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/SysDictTypeBo.java ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/SysLogininforBo.java ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/SysMenuBo.java ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/SysNoticeBo.java ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/SysOperLogBo.java ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/SysOssBo.java ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/SysOssConfigBo.java ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/SysPostBo.java ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/SysRoleBo.java ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/SysTenantBo.java ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/SysTenantPackageBo.java ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/SysUserBo.java ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/SysUserProfileBo.java ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/AvatarVo.java ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/CacheListInfoVo.java ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/DeptTreeSelectVo.java ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/MenuTreeSelectVo.java ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/MetaVo.java ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/ProfileVo.java ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/RouterVo.java ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysConfigVo.java ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysDeptVo.java ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysDictDataVo.java ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysDictTypeVo.java ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysLogininforVo.java ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysMenuVo.java ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysNoticeVo.java ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysOperLogVo.java ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysOssConfigVo.java ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysOssUploadVo.java ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysOssVo.java ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysPostVo.java ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysRoleVo.java ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysTenantPackageVo.java ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysTenantVo.java ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysUserExportVo.java ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysUserImportVo.java ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysUserInfoVo.java ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysUserVo.java ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/UserInfoVo.java ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/listener/SysUserImportListener.java ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysConfigMapper.java ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysDeptMapper.java ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysDictDataMapper.java ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysDictTypeMapper.java ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysLogininforMapper.java ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysMenuMapper.java ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysNoticeMapper.java ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysOperLogMapper.java ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysOssConfigMapper.java ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysOssMapper.java ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysPostMapper.java ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysRoleDeptMapper.java ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysRoleMapper.java ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysRoleMenuMapper.java ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysTenantMapper.java ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysTenantPackageMapper.java ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysUserMapper.java ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysUserPostMapper.java ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysUserRoleMapper.java ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/runner/SystemApplicationRunner.java ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysConfigService.java ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysDataScopeService.java ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysDeptService.java ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysDictDataService.java ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysDictTypeService.java ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysLogininforService.java ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysMenuService.java ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysNoticeService.java ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysOperLogService.java ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysOssConfigService.java ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysOssService.java ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysPermissionService.java ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysPostService.java ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysRoleService.java ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysTenantPackageService.java ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysTenantService.java ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysUserService.java ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysConfigServiceImpl.java ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysDataScopeServiceImpl.java ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysDeptServiceImpl.java ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysDictDataServiceImpl.java ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysDictTypeServiceImpl.java ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysLogininforServiceImpl.java ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysMenuServiceImpl.java ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysNoticeServiceImpl.java ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysOperLogServiceImpl.java ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysOssConfigServiceImpl.java ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysOssServiceImpl.java ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysPermissionServiceImpl.java ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysPostServiceImpl.java ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysRoleServiceImpl.java ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysSensitiveServiceImpl.java ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysTenantPackageServiceImpl.java ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysTenantServiceImpl.java ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysUserServiceImpl.java 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/SysTenantMapper.xml ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysTenantPackageMapper.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