广丰卷烟厂数采质量分析系统
zhuguifei
2026-03-02 80ff784bf60637cd348ae665fc907f7b1e527dd8
首次提交
已添加1,464个文件
151207 ■■■■■ 文件已修改
RuoYi-Vue-Plus/.DS_Store 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/.flattened-pom.xml 419 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/LICENSE 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/README.md 189 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/pom.xml 506 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-admin/.DS_Store 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-admin/.flattened-pom.xml 98 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-admin/Dockerfile 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-admin/pom.xml 159 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-admin/src/main/java/org/dromara/DromaraApplication.java 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-admin/src/main/java/org/dromara/DromaraServletInitializer.java 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-admin/src/main/java/org/dromara/web/controller/AuthController.java 243 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-admin/src/main/java/org/dromara/web/controller/CaptchaController.java 160 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-admin/src/main/java/org/dromara/web/controller/IndexController.java 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-admin/src/main/java/org/dromara/web/domain/vo/CaptchaVo.java 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-admin/src/main/java/org/dromara/web/domain/vo/LoginTenantVo.java 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-admin/src/main/java/org/dromara/web/domain/vo/LoginVo.java 54 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-admin/src/main/java/org/dromara/web/domain/vo/TenantListVo.java 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-admin/src/main/java/org/dromara/web/listener/UserActionListener.java 163 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-admin/src/main/java/org/dromara/web/service/IAuthStrategy.java 46 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-admin/src/main/java/org/dromara/web/service/SysLoginService.java 251 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-admin/src/main/java/org/dromara/web/service/SysRegisterService.java 115 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-admin/src/main/java/org/dromara/web/service/impl/EmailAuthStrategy.java 102 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-admin/src/main/java/org/dromara/web/service/impl/PasswordAuthStrategy.java 123 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-admin/src/main/java/org/dromara/web/service/impl/SmsAuthStrategy.java 102 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-admin/src/main/java/org/dromara/web/service/impl/SocialAuthStrategy.java 119 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-admin/src/main/java/org/dromara/web/service/impl/XcxAuthStrategy.java 111 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-admin/src/main/resources/application-dev.yml 276 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-admin/src/main/resources/application-prod.yml 272 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-admin/src/main/resources/application.yml 263 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-admin/src/main/resources/banner.txt 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-admin/src/main/resources/i18n/messages.properties 62 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-admin/src/main/resources/i18n/messages_en_US.properties 62 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-admin/src/main/resources/i18n/messages_zh_CN.properties 62 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-admin/src/main/resources/ip2region_v4.xdb 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-admin/src/main/resources/logback-plus.xml 129 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-admin/src/test/java/org/dromara/test/AssertUnitTest.java 45 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-admin/src/test/java/org/dromara/test/DemoUnitTest.java 70 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-admin/src/test/java/org/dromara/test/ParamUnitTest.java 72 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-admin/src/test/java/org/dromara/test/TagUnitTest.java 54 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/.DS_Store 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/.flattened-pom.xml 41 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/pom.xml 46 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-bom/pom.xml 185 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-core/.flattened-pom.xml 73 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-core/pom.xml 99 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/config/ApplicationConfig.java 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/config/ThreadPoolConfig.java 109 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/config/ValidatorConfig.java 41 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/constant/CacheConstants.java 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/constant/CacheNames.java 89 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/constant/Constants.java 81 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/constant/GlobalConstants.java 34 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/constant/HttpStatus.java 93 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/constant/RegexConstants.java 59 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/constant/SystemConstants.java 91 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/constant/TenantConstants.java 35 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/R.java 110 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/dto/CompleteTaskDTO.java 77 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/dto/DeptDTO.java 36 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/dto/DictDataDTO.java 41 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/dto/DictTypeDTO.java 41 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/dto/FlowCopyDTO.java 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/dto/FlowInstanceBizExtDTO.java 45 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/dto/OssDTO.java 46 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/dto/PostDTO.java 46 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/dto/RoleDTO.java 42 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/dto/StartProcessDTO.java 63 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/dto/StartProcessReturnDTO.java 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/dto/TaskAssigneeDTO.java 101 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/dto/UserDTO.java 73 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/dto/UserOnlineDTO.java 72 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/event/ProcessDeleteEvent.java 34 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/event/ProcessEvent.java 70 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/event/ProcessTaskEvent.java 70 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/model/EmailLoginBody.java 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/model/LoginBody.java 48 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/model/LoginUser.java 148 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/model/PasswordLoginBody.java 32 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/model/RegisterBody.java 37 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/model/SmsLoginBody.java 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/model/SocialLoginBody.java 35 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/model/TaskAssigneeBody.java 56 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/model/XcxLoginBody.java 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/model/XcxLoginUser.java 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/enums/BusinessStatusEnum.java 215 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/enums/DeviceType.java 39 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/enums/FormatsType.java 146 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/enums/LoginType.java 44 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/enums/UserStatus.java 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/enums/UserType.java 39 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/exception/ServiceException.java 67 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/exception/SseException.java 62 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/exception/base/BaseException.java 74 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/exception/file/FileException.java 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/exception/file/FileNameLengthLimitExceededException.java 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/exception/file/FileSizeLimitExceededException.java 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/exception/user/CaptchaException.java 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/exception/user/CaptchaExpireException.java 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/exception/user/UserException.java 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/factory/RegexPatternPoolFactory.java 52 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/factory/YmlPropertySourceFactory.java 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/service/ConfigService.java 100 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/service/DeptService.java 46 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/service/DictService.java 87 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/service/OssService.java 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/service/PermissionService.java 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/service/PostService.java 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/service/RoleService.java 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/service/TaskAssigneeService.java 45 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/service/UserService.java 103 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/service/WorkflowService.java 105 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/DateUtils.java 378 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/DesensitizedUtils.java 87 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/MapstructUtils.java 93 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/MessageUtils.java 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/NetUtils.java 84 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/ObjectUtils.java 60 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/ServletUtils.java 289 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/SpringUtils.java 67 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/StreamUtils.java 328 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/StringUtils.java 384 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/TreeBuildUtils.java 123 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/ValidatorUtils.java 35 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/file/FileUtils.java 43 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/file/MimeTypeUtils.java 40 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/ip/AddressUtils.java 43 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/ip/RegionUtils.java 155 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/reflect/ReflectUtils.java 56 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/regex/RegexUtils.java 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/regex/RegexValidator.java 105 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/sql/SqlUtil.java 56 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/validate/AddGroup.java 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/validate/EditGroup.java 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/validate/QueryGroup.java 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/validate/dicts/DictPattern.java 40 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/validate/dicts/DictPatternValidator.java 55 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/validate/enumd/EnumPattern.java 48 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/validate/enumd/EnumPatternValidator.java 37 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/xss/Xss.java 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/xss/XssValidator.java 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-core/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-doc/.flattened-pom.xml 32 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-doc/pom.xml 41 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-doc/src/main/java/org/dromara/common/doc/config/SpringDocConfig.java 127 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-doc/src/main/java/org/dromara/common/doc/config/properties/SpringDocProperties.java 94 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-doc/src/main/java/org/dromara/common/doc/handler/OpenApiHandler.java 253 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-doc/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-encrypt/.flattened-pom.xml 43 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-encrypt/pom.xml 54 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/annotation/ApiEncrypt.java 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/annotation/EncryptField.java 44 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/config/ApiDecryptAutoConfiguration.java 34 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/config/EncryptorAutoConfiguration.java 49 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/core/EncryptContext.java 41 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/core/EncryptorManager.java 168 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/core/IEncryptor.java 35 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/core/encryptor/AbstractEncryptor.java 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/core/encryptor/AesEncryptor.java 55 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/core/encryptor/Base64Encryptor.java 48 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/core/encryptor/RsaEncryptor.java 62 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/core/encryptor/Sm2Encryptor.java 61 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/core/encryptor/Sm4Encryptor.java 55 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/enumd/AlgorithmType.java 48 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/enumd/EncodeType.java 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/filter/CryptoFilter.java 110 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/filter/DecryptRequestBodyWrapper.java 94 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/filter/EncryptResponseBodyWrapper.java 123 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/interceptor/MybatisDecryptInterceptor.java 132 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/interceptor/MybatisEncryptInterceptor.java 124 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/properties/ApiDecryptProperties.java 34 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/properties/EncryptorProperties.java 48 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/utils/EncryptUtils.java 313 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-encrypt/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-excel/.flattened-pom.xml 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-excel/pom.xml 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/annotation/CellMerge.java 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/annotation/ExcelDictFormat.java 32 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/annotation/ExcelDynamicOptions.java 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/annotation/ExcelEnumFormat.java 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/annotation/ExcelNotation.java 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/annotation/ExcelRequired.java 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/convert/ExcelBigNumberConvert.java 52 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/convert/ExcelDictConvert.java 73 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/convert/ExcelEnumConvert.java 87 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/core/CellMergeHandler.java 217 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/core/CellMergeStrategy.java 65 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/core/DefaultExcelListener.java 104 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/core/DefaultExcelResult.java 73 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/core/DropDownOptions.java 150 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/core/ExcelDownHandler.java 412 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/core/ExcelListener.java 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/core/ExcelOptionsProvider.java 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/core/ExcelResult.java 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/handler/DataWriteHandler.java 123 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/utils/ExcelUtil.java 479 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/utils/ExcelWriterWrapper.java 127 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-idempotent/.flattened-pom.xml 32 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-idempotent/pom.xml 41 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-idempotent/src/main/java/org/dromara/common/idempotent/annotation/RepeatSubmit.java 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-idempotent/src/main/java/org/dromara/common/idempotent/aspectj/RepeatSubmitAspect.java 146 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-idempotent/src/main/java/org/dromara/common/idempotent/config/IdempotentConfig.java 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-idempotent/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-job/.flattened-pom.xml 36 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-job/pom.xml 46 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-job/src/main/java/org/dromara/common/job/config/SnailJobConfig.java 37 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-job/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-json/.flattened-pom.xml 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-json/pom.xml 37 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-json/src/main/java/org/dromara/common/json/config/JacksonConfig.java 55 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-json/src/main/java/org/dromara/common/json/handler/BigNumberSerializer.java 42 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-json/src/main/java/org/dromara/common/json/handler/CustomDateDeserializer.java 37 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-json/src/main/java/org/dromara/common/json/utils/JsonUtils.java 225 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-json/src/main/java/org/dromara/common/json/validate/JsonPattern.java 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-json/src/main/java/org/dromara/common/json/validate/JsonPatternValidator.java 51 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-json/src/main/java/org/dromara/common/json/validate/JsonType.java 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-json/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-log/.flattened-pom.xml 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-log/pom.xml 32 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-log/src/main/java/org/dromara/common/log/annotation/Log.java 48 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-log/src/main/java/org/dromara/common/log/aspect/LogAspect.java 226 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-log/src/main/java/org/dromara/common/log/enums/BusinessStatus.java 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-log/src/main/java/org/dromara/common/log/enums/BusinessType.java 58 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-log/src/main/java/org/dromara/common/log/enums/OperatorType.java 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-log/src/main/java/org/dromara/common/log/event/LogininforEvent.java 52 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-log/src/main/java/org/dromara/common/log/event/OperLogEvent.java 115 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-log/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-mail/.flattened-pom.xml 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-mail/pom.xml 34 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-mail/src/main/java/org/dromara/common/mail/config/MailConfig.java 37 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-mail/src/main/java/org/dromara/common/mail/config/properties/MailProperties.java 75 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-mail/src/main/java/org/dromara/common/mail/utils/MailUtils.java 469 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-mail/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-mybatis/.flattened-pom.xml 40 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-mybatis/pom.xml 52 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/annotation/DataColumn.java 40 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/annotation/DataPermission.java 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/aspect/DataPermissionAdvice.java 54 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/aspect/DataPermissionPointcut.java 39 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/aspect/DataPermissionPointcutAdvisor.java 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/config/MybatisPlusConfig.java 142 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/core/domain/BaseEntity.java 70 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/core/mapper/BaseMapperPlus.java 334 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/core/page/PageQuery.java 127 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/core/page/TableDataInfo.java 107 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/enums/DataBaseType.java 87 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/enums/DataScopeType.java 87 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/handler/InjectionMetaObjectHandler.java 113 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/handler/MybatisExceptionHandler.java 89 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/handler/PlusDataPermissionHandler.java 261 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/handler/PlusPostInitTableInfoHandler.java 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/helper/DataBaseHelper.java 82 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/helper/DataPermissionHelper.java 176 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/interceptor/PlusDataPermissionInterceptor.java 172 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/utils/IdGeneratorUtil.java 129 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-mybatis/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-mybatis/src/main/resources/common-mybatis.yml 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-mybatis/src/main/resources/spy.properties 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-oss/.flattened-pom.xml 50 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-oss/pom.xml 66 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-oss/src/main/java/org/dromara/common/oss/constant/OssConstant.java 40 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-oss/src/main/java/org/dromara/common/oss/core/OssClient.java 564 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-oss/src/main/java/org/dromara/common/oss/core/WriteOutSubscriber.java 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-oss/src/main/java/org/dromara/common/oss/entity/UploadResult.java 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-oss/src/main/java/org/dromara/common/oss/enums/AccessPolicyType.java 56 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-oss/src/main/java/org/dromara/common/oss/exception/OssException.java 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-oss/src/main/java/org/dromara/common/oss/factory/OssFactory.java 73 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-oss/src/main/java/org/dromara/common/oss/properties/OssProperties.java 63 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-ratelimiter/.flattened-pom.xml 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-ratelimiter/pom.xml 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-ratelimiter/src/main/java/org/dromara/common/ratelimiter/annotation/RateLimiter.java 47 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-ratelimiter/src/main/java/org/dromara/common/ratelimiter/aspectj/RateLimiterAspect.java 112 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-ratelimiter/src/main/java/org/dromara/common/ratelimiter/config/RateLimiterConfig.java 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-ratelimiter/src/main/java/org/dromara/common/ratelimiter/enums/LimitType.java 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-ratelimiter/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-ratelimiter/src/main/resources/spel-extension.json 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-redis/.flattened-pom.xml 41 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-redis/pom.xml 55 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/config/CacheConfig.java 45 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/config/RedisConfig.java 160 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/config/properties/RedissonProperties.java 135 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/handler/KeyPrefixHandler.java 50 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/handler/RedisExceptionHandler.java 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/manager/CaffeineCacheDecorator.java 97 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/manager/PlusSpringCacheManager.java 202 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/utils/CacheUtils.java 61 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/utils/QueueUtils.java 239 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/utils/RedisUtils.java 581 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/utils/SequenceUtils.java 351 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-redis/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-satoken/.flattened-pom.xml 35 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-satoken/pom.xml 46 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-satoken/src/main/java/org/dromara/common/satoken/config/SaTokenConfig.java 54 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-satoken/src/main/java/org/dromara/common/satoken/core/dao/PlusSaTokenDao.java 192 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-satoken/src/main/java/org/dromara/common/satoken/core/service/SaPermissionImpl.java 86 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-satoken/src/main/java/org/dromara/common/satoken/handler/SaTokenExceptionHandler.java 52 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-satoken/src/main/java/org/dromara/common/satoken/utils/LoginHelper.java 217 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-satoken/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-satoken/src/main/resources/common-satoken.yml 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-security/.flattened-pom.xml 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-security/pom.xml 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-security/src/main/java/org/dromara/common/security/config/SecurityConfig.java 108 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-security/src/main/java/org/dromara/common/security/config/properties/SecurityProperties.java 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-security/src/main/java/org/dromara/common/security/handler/AllUrlHandler.java 39 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-security/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-sensitive/.flattened-pom.xml 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-sensitive/pom.xml 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-sensitive/src/main/java/org/dromara/common/sensitive/annotation/Sensitive.java 34 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-sensitive/src/main/java/org/dromara/common/sensitive/core/SensitiveService.java 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-sensitive/src/main/java/org/dromara/common/sensitive/core/SensitiveStrategy.java 113 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-sensitive/src/main/java/org/dromara/common/sensitive/handler/SensitiveHandler.java 58 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-sms/.flattened-pom.xml 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-sms/pom.xml 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-sms/src/main/java/org/dromara/common/sms/config/SmsAutoConfiguration.java 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-sms/src/main/java/org/dromara/common/sms/core/dao/PlusSmsDao.java 72 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-sms/src/main/java/org/dromara/common/sms/handler/SmsExceptionHandler.java 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-sms/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-social/.flattened-pom.xml 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-social/pom.xml 34 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-social/src/main/java/me/zhyd/oauth/request/AbstractAuthWeChatEnterpriseRequest.java 154 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-social/src/main/java/me/zhyd/oauth/request/AuthDingTalkV2Request.java 109 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-social/src/main/java/org/dromara/common/social/config/SocialAutoConfiguration.java 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-social/src/main/java/org/dromara/common/social/config/properties/SocialLoginConfigProperties.java 80 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-social/src/main/java/org/dromara/common/social/config/properties/SocialProperties.java 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-social/src/main/java/org/dromara/common/social/gitea/AuthGiteaRequest.java 92 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-social/src/main/java/org/dromara/common/social/gitea/AuthGiteaSource.java 50 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-social/src/main/java/org/dromara/common/social/maxkey/AuthMaxKeyRequest.java 80 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-social/src/main/java/org/dromara/common/social/maxkey/AuthMaxKeySource.java 52 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-social/src/main/java/org/dromara/common/social/topiam/AuthTopIamRequest.java 113 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-social/src/main/java/org/dromara/common/social/topiam/AuthTopIamSource.java 51 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-social/src/main/java/org/dromara/common/social/utils/AuthRedisStateCache.java 61 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-social/src/main/java/org/dromara/common/social/utils/SocialUtils.java 75 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-social/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-sse/.flattened-pom.xml 32 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-sse/pom.xml 36 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-sse/src/main/java/org/dromara/common/sse/config/SseAutoConfiguration.java 36 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-sse/src/main/java/org/dromara/common/sse/config/SseProperties.java 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-sse/src/main/java/org/dromara/common/sse/controller/SseController.java 88 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-sse/src/main/java/org/dromara/common/sse/core/SseEmitterManager.java 230 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-sse/src/main/java/org/dromara/common/sse/dto/SseMessageDto.java 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-sse/src/main/java/org/dromara/common/sse/listener/SseTopicListener.java 48 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-sse/src/main/java/org/dromara/common/sse/utils/SseMessageUtils.java 84 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-sse/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-tenant/.flattened-pom.xml 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-tenant/pom.xml 32 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-tenant/src/main/java/org/dromara/common/tenant/config/TenantConfig.java 86 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-tenant/src/main/java/org/dromara/common/tenant/core/TenantEntity.java 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-tenant/src/main/java/org/dromara/common/tenant/core/TenantSaTokenDao.java 158 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-tenant/src/main/java/org/dromara/common/tenant/exception/TenantException.java 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-tenant/src/main/java/org/dromara/common/tenant/handle/PlusTenantLineHandler.java 56 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-tenant/src/main/java/org/dromara/common/tenant/handle/TenantKeyPrefixHandler.java 83 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-tenant/src/main/java/org/dromara/common/tenant/helper/TenantHelper.java 231 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-tenant/src/main/java/org/dromara/common/tenant/manager/TenantSpringCacheManager.java 41 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-tenant/src/main/java/org/dromara/common/tenant/properties/TenantProperties.java 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-tenant/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-translation/.flattened-pom.xml 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-translation/pom.xml 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-translation/src/main/java/org/dromara/common/translation/annotation/Translation.java 39 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-translation/src/main/java/org/dromara/common/translation/annotation/TranslationType.java 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-translation/src/main/java/org/dromara/common/translation/config/TranslationConfig.java 50 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-translation/src/main/java/org/dromara/common/translation/constant/TransConstant.java 35 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-translation/src/main/java/org/dromara/common/translation/core/TranslationInterface.java 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-translation/src/main/java/org/dromara/common/translation/core/handler/TranslationBeanSerializerModifier.java 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-translation/src/main/java/org/dromara/common/translation/core/handler/TranslationHandler.java 71 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-translation/src/main/java/org/dromara/common/translation/core/impl/DeptNameTranslationImpl.java 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-translation/src/main/java/org/dromara/common/translation/core/impl/DictTypeTranslationImpl.java 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-translation/src/main/java/org/dromara/common/translation/core/impl/NicknameTranslationImpl.java 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-translation/src/main/java/org/dromara/common/translation/core/impl/OssUrlTranslationImpl.java 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-translation/src/main/java/org/dromara/common/translation/core/impl/UserNameTranslationImpl.java 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-translation/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-web/.flattened-pom.xml 46 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-web/pom.xml 57 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/config/CaptchaConfig.java 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/config/FilterConfig.java 41 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/config/I18nConfig.java 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/config/ResourcesConfig.java 79 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/config/UndertowConfig.java 63 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/config/properties/CaptchaProperties.java 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/config/properties/XssProperties.java 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/core/BaseController.java 40 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/core/I18nLocaleResolver.java 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/core/WaveAndCircleCaptcha.java 197 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/filter/RepeatableFilter.java 40 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/filter/RepeatedlyRequestWrapper.java 68 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/filter/XssFilter.java 59 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/filter/XssHttpServletRequestWrapper.java 134 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/handler/GlobalExceptionHandler.java 235 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/interceptor/PlusWebInvokeTimeInterceptor.java 127 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-web/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-websocket/.flattened-pom.xml 42 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-websocket/pom.xml 46 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-websocket/src/main/java/org/dromara/common/websocket/config/WebSocketConfig.java 63 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-websocket/src/main/java/org/dromara/common/websocket/config/properties/WebSocketProperties.java 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-websocket/src/main/java/org/dromara/common/websocket/constant/WebSocketConstants.java 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-websocket/src/main/java/org/dromara/common/websocket/dto/WebSocketMessageDto.java 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-websocket/src/main/java/org/dromara/common/websocket/handler/PlusWebSocketHandler.java 123 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-websocket/src/main/java/org/dromara/common/websocket/holder/WebSocketSessionHolder.java 74 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-websocket/src/main/java/org/dromara/common/websocket/interceptor/PlusWebSocketInterceptor.java 75 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-websocket/src/main/java/org/dromara/common/websocket/listener/WebSocketTopicListener.java 50 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-websocket/src/main/java/org/dromara/common/websocket/utils/WebSocketUtils.java 127 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-websocket/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-extend/.DS_Store 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-extend/.flattened-pom.xml 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-extend/pom.xml 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-extend/ruoyi-monitor-admin/.flattened-pom.xml 62 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-extend/ruoyi-monitor-admin/Dockerfile 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-extend/ruoyi-monitor-admin/pom.xml 73 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-extend/ruoyi-monitor-admin/src/main/java/org/dromara/monitor/admin/MonitorAdminApplication.java 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-extend/ruoyi-monitor-admin/src/main/java/org/dromara/monitor/admin/config/SecurityConfig.java 54 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-extend/ruoyi-monitor-admin/src/main/java/org/dromara/monitor/admin/notifier/CustomNotifier.java 55 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-extend/ruoyi-monitor-admin/src/main/resources/application.yml 51 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-extend/ruoyi-monitor-admin/src/main/resources/banner.txt 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-extend/ruoyi-monitor-admin/src/main/resources/logback-plus.xml 34 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-extend/ruoyi-snailjob-server/.flattened-pom.xml 53 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-extend/ruoyi-snailjob-server/Dockerfile 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-extend/ruoyi-snailjob-server/pom.xml 58 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-extend/ruoyi-snailjob-server/src/main/java/com/aizuda/snailjob/server/starter/filter/ActuatorAuthFilter.java 64 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-extend/ruoyi-snailjob-server/src/main/java/com/aizuda/snailjob/server/starter/filter/SecurityConfig.java 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-extend/ruoyi-snailjob-server/src/main/java/org/dromara/snailjob/SnailJobServerApplication.java 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-extend/ruoyi-snailjob-server/src/main/resources/application-dev.yml 51 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-extend/ruoyi-snailjob-server/src/main/resources/application-prod.yml 51 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-extend/ruoyi-snailjob-server/src/main/resources/application.yml 39 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-extend/ruoyi-snailjob-server/src/main/resources/banner.txt 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-extend/ruoyi-snailjob-server/src/main/resources/logback-plus.xml 93 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/.DS_Store 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/.flattened-pom.xml 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/pom.xml 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-demo/.flattened-pom.xml 84 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-demo/pom.xml 108 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/controller/MailSendController.java 69 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/controller/RedisCacheController.java 92 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/controller/RedisLockController.java 64 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/controller/RedisPubSubController.java 47 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/controller/RedisRateLimiterController.java 64 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/controller/SmsController.java 82 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/controller/Swagger3DemoController.java 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/controller/TestBatchController.java 90 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/controller/TestDemoController.java 147 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/controller/TestEncryptController.java 55 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/controller/TestExcelController.java 172 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/controller/TestI18nController.java 71 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/controller/TestSensitiveController.java 76 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/controller/TestTreeController.java 107 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/controller/WebSocketController.java 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/controller/package-info.java 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/controller/queue/BoundedQueueController.java 92 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/controller/queue/DelayedQueueController.java 97 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/controller/queue/PriorityDemo.java 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/controller/queue/PriorityQueueController.java 91 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/domain/TestDemo.java 68 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/domain/TestDemoEncrypt.java 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/domain/TestTree.java 65 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/domain/bo/TestDemoBo.java 67 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/domain/bo/TestDemoImportVo.java 53 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/domain/bo/TestTreeBo.java 54 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/domain/package-info.java 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/domain/vo/ExportDemoVo.java 122 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/domain/vo/TestDemoVo.java 119 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/domain/vo/TestTreeVo.java 64 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/listener/ExportDemoListener.java 68 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/mapper/TestDemoEncryptMapper.java 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/mapper/TestDemoMapper.java 64 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/mapper/TestTreeMapper.java 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/mapper/package-info.java 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/service/IExportExcelService.java 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/service/ITestDemoService.java 71 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/service/ITestTreeService.java 52 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/service/impl/ExportExcelServiceImpl.java 297 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/service/impl/TestDemoServiceImpl.java 118 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/service/impl/TestTreeServiceImpl.java 90 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/service/impl/package-info.java 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/service/package-info.java 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-demo/src/main/resources/excel/单列表.xlsx 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-demo/src/main/resources/excel/多sheet列表.xlsx 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-demo/src/main/resources/excel/多列表.xlsx 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-demo/src/main/resources/mapper/demo/TestDemoMapper.xml 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-demo/src/main/resources/mapper/demo/TestTreeMapper.xml 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-demo/src/main/resources/mapper/package-info.md 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-generator/.flattened-pom.xml 59 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-generator/pom.xml 89 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-generator/src/main/java/org/dromara/generator/config/GenConfig.java 73 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-generator/src/main/java/org/dromara/generator/config/MyBatisDataSourceMonitor.java 105 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-generator/src/main/java/org/dromara/generator/constant/GenConstants.java 186 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-generator/src/main/java/org/dromara/generator/controller/GenController.java 222 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-generator/src/main/java/org/dromara/generator/domain/GenTable.java 196 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-generator/src/main/java/org/dromara/generator/domain/GenTableColumn.java 222 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-generator/src/main/java/org/dromara/generator/mapper/GenTableColumnMapper.java 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-generator/src/main/java/org/dromara/generator/mapper/GenTableMapper.java 51 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-generator/src/main/java/org/dromara/generator/service/GenTableServiceImpl.java 575 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-generator/src/main/java/org/dromara/generator/service/IGenTableService.java 141 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-generator/src/main/java/org/dromara/generator/util/GenUtils.java 219 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-generator/src/main/java/org/dromara/generator/util/VelocityInitializer.java 35 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-generator/src/main/java/org/dromara/generator/util/VelocityUtils.java 389 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-generator/src/main/java/org/dromara/generator/util/VelocityUtils2.java 343 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-generator/src/main/resources/.DS_Store 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-generator/src/main/resources/generator.yml 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-generator/src/main/resources/mapper/generator/GenTableColumnMapper.xml 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-generator/src/main/resources/mapper/generator/GenTableMapper.xml 42 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-generator/src/main/resources/mapper/package-info.md 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-generator/src/main/resources/vm/.DS_Store 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-generator/src/main/resources/vm/java/bo.java.vm 50 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-generator/src/main/resources/vm/java/controller.java.vm 115 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-generator/src/main/resources/vm/java/domain.java.vm 60 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-generator/src/main/resources/vm/java/mapper.java.vm 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-generator/src/main/resources/vm/java/service.java.vm 72 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-generator/src/main/resources/vm/java/serviceImpl.java.vm 160 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-generator/src/main/resources/vm/java/vo.java.vm 66 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-generator/src/main/resources/vm/soy/api/api.ts.vm 41 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-generator/src/main/resources/vm/soy/index-tree.vue.vm 232 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-generator/src/main/resources/vm/soy/index.vue.vm 198 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-generator/src/main/resources/vm/soy/modules/operate-drawer.vue.vm 257 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-generator/src/main/resources/vm/soy/modules/search.vue.vm 130 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-generator/src/main/resources/vm/soy/typings/api.d.ts.vm 51 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-generator/src/main/resources/vm/sql/oracle/sql.vm 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-generator/src/main/resources/vm/sql/postgres/sql.vm 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-generator/src/main/resources/vm/sql/sql.vm 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-generator/src/main/resources/vm/sql/sqlserver/sql.vm 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-generator/src/main/resources/vm/ts/api.ts.vm 63 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-generator/src/main/resources/vm/ts/types.ts.vm 61 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-generator/src/main/resources/vm/vue/index-tree.vue.vm 499 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-generator/src/main/resources/vm/vue/index.vue.vm 459 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-generator/src/main/resources/vm/xml/mapper.xml.vm 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-job/.flattened-pom.xml 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-job/pom.xml 34 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-job/src/main/java/org/dromara/job/entity/BillDto.java 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-job/src/main/java/org/dromara/job/package-info.java 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-job/src/main/java/org/dromara/job/snailjob/AlipayBillTask.java 42 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-job/src/main/java/org/dromara/job/snailjob/SummaryBillTask.java 45 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-job/src/main/java/org/dromara/job/snailjob/TestAnnoJobExecutor.java 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-job/src/main/java/org/dromara/job/snailjob/TestBroadcastJob.java 37 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-job/src/main/java/org/dromara/job/snailjob/TestClassJobExecutor.java 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-job/src/main/java/org/dromara/job/snailjob/TestMapJobAnnotation.java 53 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-job/src/main/java/org/dromara/job/snailjob/TestMapReduceAnnotation1.java 60 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-job/src/main/java/org/dromara/job/snailjob/TestStaticShardingJob.java 37 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-job/src/main/java/org/dromara/job/snailjob/WechatBillTask.java 43 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/.flattened-pom.xml 84 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/pom.xml 108 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/readMe.md 499 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/.DS_Store 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/.DS_Store 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/.DS_Store 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/controller/HoisterTimeDataController.java 106 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/controller/PackerTimeDataController.java 106 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/controller/RollerTimeDataController.java 120 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/domain/BoxTimeData.java 59 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/domain/FeedmatchTimeData.java 154 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/domain/HoisterTimeData.java 222 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/domain/MakeupTimeData.java 84 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/domain/PackerTimeData.java 147 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/domain/RollerTimeData.java 126 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/domain/TransTimeData.java 159 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/domain/bo/HoisterTimeDataBo.java 256 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/domain/bo/PackerTimeDataBo.java 149 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/domain/bo/RollerTimeDataBo.java 127 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/domain/vo/HoisterTimeDataVo.java 301 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/domain/vo/PackerTimeDataVo.java 179 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/domain/vo/RollerTimeDataVo.java 164 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/mapper/HoisterTimeDataMapper.java 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/mapper/PackerTimeDataMapper.java 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/mapper/RollerTimeDataMapper.java 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/service/IHoisterTimeDataService.java 69 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/service/IPackerTimeDataService.java 69 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/service/IRollerTimeDataService.java 78 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/service/impl/HoisterTimeDataServiceImpl.java 138 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/service/impl/PackerTimeDataServiceImpl.java 157 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/service/impl/RollerTimeDataServiceImpl.java 162 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/resources/mapper/qa/PackerTimeDataMapper.xml 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/resources/mapper/qa/RollerTimeDataMapper.xml 69 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/.flattened-pom.xml 80 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/pom.xml 105 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/monitor/CacheController.java 65 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/monitor/SysLogininforController.java 93 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/monitor/SysOperlogController.java 77 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/monitor/SysUserOnlineController.java 134 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysClientController.java 122 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysConfigController.java 142 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysDeptController.java 146 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysDictDataController.java 127 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysDictTypeController.java 131 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysMenuController.java 213 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysNoticeController.java 93 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysOssConfigController.java 106 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysOssController.java 104 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysPostController.java 151 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysProfileController.java 149 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysRoleController.java 246 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysSocialController.java 38 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysTenantController.java 211 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysTenantPackageController.java 143 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysUserController.java 308 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/SysCache.java 47 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/SysClient.java 77 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/SysConfig.java 51 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/SysDept.java 92 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/SysDictData.java 71 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/SysDictType.java 41 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/SysLogininfor.java 85 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/SysMenu.java 191 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/SysNotice.java 51 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/SysOperLog.java 115 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/SysOss.java 55 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/SysOssConfig.java 89 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/SysOssExt.java 75 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/SysPost.java 61 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/SysRole.java 79 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/SysRoleDept.java 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/SysRoleMenu.java 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/SysSocial.java 136 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/SysTenant.java 103 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/SysTenantPackage.java 60 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/SysUser.java 115 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/SysUserOnline.java 63 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/SysUserPost.java 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/SysUserRole.java 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/SysClientBo.java 80 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/SysConfigBo.java 59 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/SysDeptBo.java 81 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/SysDictDataBo.java 80 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/SysDictTypeBo.java 50 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/SysLogininforBo.java 87 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/SysMenuBo.java 113 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/SysNoticeBo.java 61 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/SysOperLogBo.java 127 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/SysOssBo.java 54 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/SysOssConfigBo.java 109 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/SysPostBo.java 75 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/SysRoleBo.java 94 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/SysSocialBo.java 142 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/SysTenantBo.java 115 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/SysTenantPackageBo.java 59 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/SysUserBo.java 124 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/SysUserPasswordBo.java 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/SysUserProfileBo.java 53 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/MetaVo.java 78 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/ProfileUserVo.java 90 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/RouterVo.java 62 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysClientVo.java 90 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysConfigVo.java 72 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysDeptVo.java 109 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysDictDataVo.java 88 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysDictTypeVo.java 59 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysLogininforVo.java 106 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysMenuVo.java 116 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysNoticeVo.java 73 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysOperLogVo.java 144 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysOssConfigVo.java 97 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysOssUploadVo.java 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysOssVo.java 77 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysPostVo.java 91 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysRoleVo.java 100 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysSocialVo.java 144 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysTenantPackageVo.java 66 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysTenantVo.java 115 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysUserExportVo.java 96 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysUserImportVo.java 76 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysUserInfoVo.java 40 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysUserVo.java 142 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/UserInfoVo.java 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/listener/SysUserImportListener.java 127 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysClientMapper.java 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysConfigMapper.java 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysDeptMapper.java 143 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysDictDataMapper.java 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysDictTypeMapper.java 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysLogininforMapper.java 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysMenuMapper.java 147 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysNoticeMapper.java 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysOperLogMapper.java 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysOssConfigMapper.java 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysOssMapper.java 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysPostMapper.java 75 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysRoleDeptMapper.java 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysRoleMapper.java 105 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysRoleMenuMapper.java 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysSocialMapper.java 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysTenantMapper.java 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysTenantPackageMapper.java 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysUserMapper.java 131 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysUserPostMapper.java 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysUserRoleMapper.java 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/runner/SystemApplicationRunner.java 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysClientService.java 66 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysConfigService.java 93 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysDataScopeService.java 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysDeptService.java 137 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysDictDataService.java 82 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysDictTypeService.java 101 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysLogininforService.java 53 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysMenuService.java 172 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysNoticeService.java 73 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysOperLogService.java 61 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysOssConfigService.java 64 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysOssService.java 80 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysPermissionService.java 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysPostService.java 136 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysRoleService.java 236 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysSocialService.java 53 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysTenantPackageService.java 62 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysTenantService.java 92 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysUserService.java 231 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysClientServiceImpl.java 155 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysConfigServiceImpl.java 268 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysDataScopeServiceImpl.java 72 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysDeptServiceImpl.java 426 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysDictDataServiceImpl.java 159 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysDictTypeServiceImpl.java 304 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysLogininforServiceImpl.java 182 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysMenuServiceImpl.java 440 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysNoticeServiceImpl.java 131 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysOperLogServiceImpl.java 141 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysOssConfigServiceImpl.java 177 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysOssServiceImpl.java 284 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysPermissionServiceImpl.java 62 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysPostServiceImpl.java 277 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysRoleServiceImpl.java 614 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysSensitiveServiceImpl.java 47 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysSocialServiceImpl.java 112 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysTaskAssigneeServiceImpl.java 138 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysTenantPackageServiceImpl.java 149 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysTenantServiceImpl.java 567 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysUserServiceImpl.java 772 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/resources/mapper/package-info.md 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysClientMapper.xml 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysConfigMapper.xml 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysDeptMapper.xml 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysDictDataMapper.xml 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysDictTypeMapper.xml 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysLogininforMapper.xml 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysMenuMapper.xml 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysNoticeMapper.xml 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysOperLogMapper.xml 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysOssConfigMapper.xml 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysOssMapper.xml 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysPostMapper.xml 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysRoleDeptMapper.xml 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysRoleMapper.xml 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysRoleMenuMapper.xml 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysSocialMapper.xml 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysTenantMapper.xml 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysTenantPackageMapper.xml 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysUserMapper.xml 42 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysUserPostMapper.xml 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysUserRoleMapper.xml 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-workflow/.flattened-pom.xml 72 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-workflow/pom.xml 84 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/common/ConditionalOnEnable.java 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/common/constant/FlowConstant.java 111 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/common/enums/ButtonPermissionEnum.java 65 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/common/enums/CopySettingEnum.java 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/common/enums/MessageTypeEnum.java 53 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/common/enums/NodeExtEnum.java 32 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/common/enums/TaskAssigneeEnum.java 140 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/common/enums/TaskAssigneeType.java 49 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/common/enums/TaskStatusEnum.java 114 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/common/enums/VariablesEnum.java 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/config/WarmFlowConfig.java 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/controller/FlwCategoryController.java 134 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/controller/FlwDefinitionController.java 195 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/controller/FlwInstanceController.java 187 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/controller/FlwSpelController.java 93 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/controller/FlwTaskController.java 225 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/controller/TestLeaveController.java 119 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/FlowCategory.java 67 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/FlowInstanceBizExt.java 59 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/FlowSpel.java 69 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/TestLeave.java 68 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/BackProcessBo.java 69 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/CompleteTaskBo.java 85 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/FlowCancelBo.java 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/FlowCategoryBo.java 47 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/FlowCopyBo.java 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/FlowInstanceBo.java 55 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/FlowInvalidBo.java 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/FlowNextNodeBo.java 38 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/FlowSpelBo.java 60 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/FlowTaskBo.java 69 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/FlowTerminationBo.java 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/FlowUrgeTaskBo.java 38 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/FlowVariableBo.java 39 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/StartProcessBo.java 68 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/TaskOperationBo.java 48 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/TestLeaveBo.java 92 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/ButtonPermissionVo.java 43 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/FlowCategoryVo.java 69 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/FlowCopyVo.java 36 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/FlowDefinitionVo.java 104 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/FlowHisTaskVo.java 260 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/FlowInstanceVo.java 149 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/FlowSpelVo.java 79 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/FlowTaskVo.java 219 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/NodeExtVo.java 45 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/TestLeaveVo.java 77 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/handler/FlowProcessEventHandler.java 96 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/handler/WorkflowPermissionHandler.java 65 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/listener/WorkflowGlobalListener.java 272 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/mapper/FlwCategoryMapper.java 49 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/mapper/FlwInstanceBizExtMapper.java 61 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/mapper/FlwInstanceMapper.java 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/mapper/FlwSpelMapper.java 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/mapper/FlwTaskMapper.java 47 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/mapper/TestLeaveMapper.java 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/rule/SpelRuleComponent.java 38 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IFlwCategoryService.java 95 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IFlwCommonService.java 41 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IFlwDefinitionService.java 78 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IFlwInstanceService.java 168 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IFlwNodeExtService.java 35 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IFlwSpelService.java 88 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IFlwTaskAssigneeService.java 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IFlwTaskService.java 217 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/ITestLeaveService.java 52 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/CategoryNameTranslationImpl.java 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwCategoryServiceImpl.java 261 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwChartExtServiceImpl.java 273 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwCommonServiceImpl.java 124 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwDefinitionServiceImpl.java 270 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwInstanceServiceImpl.java 469 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwNodeExtServiceImpl.java 354 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwSpelServiceImpl.java 190 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwTaskAssigneeServiceImpl.java 291 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwTaskServiceImpl.java 859 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/TestLeaveServiceImpl.java 242 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/WorkflowServiceImpl.java 178 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-workflow/src/main/resources/mapper/package-info.md 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-workflow/src/main/resources/mapper/workflow/FlwCategoryMapper.xml 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-workflow/src/main/resources/mapper/workflow/FlwInstanceBizExtMapper.xml 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-workflow/src/main/resources/mapper/workflow/FlwInstanceMapper.xml 39 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-workflow/src/main/resources/mapper/workflow/FlwSpelMapper.xml 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-workflow/src/main/resources/mapper/workflow/FlwTaskMapper.xml 127 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/ruoyi-modules/ruoyi-workflow/src/main/resources/mapper/workflow/TestLeaveMapper.xml 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/script/bin/ry.bat 68 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/script/bin/ry.sh 86 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/script/docker/database.yml 59 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/script/docker/docker-compose.yml 157 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/script/docker/nginx/conf/nginx.conf 156 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/script/docker/redis/conf/redis.conf 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/script/docker/redis/data/README.md 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/script/leave/leave1.json 129 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/script/leave/leave2.json 187 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/script/leave/leave3.json 211 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/script/leave/leave4.json 154 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/script/leave/leave5.json 211 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/script/leave/leave6.json 368 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/script/sql/oracle/oracle_ry_job.sql 940 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/script/sql/oracle/oracle_ry_vue_5.X.sql 1407 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/script/sql/oracle/oracle_ry_workflow.sql 528 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/script/sql/postgres/postgres_ry_job.sql 871 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/script/sql/postgres/postgres_ry_vue_5.X.sql 1398 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/script/sql/postgres/postgres_ry_workflow.sql 507 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/script/sql/ry_job.sql 535 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/script/sql/ry_vue_5.X.sql 976 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/script/sql/ry_workflow.sql 319 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/script/sql/sqlserver/sqlserver_ry_job.sql 2891 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/script/sql/sqlserver/sqlserver_ry_vue_5.X.sql 3641 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/script/sql/sqlserver/sqlserver_ry_workflow.sql 1677 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/script/sql/update/oracle/update_5.0-5.1.sql 151 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/script/sql/update/oracle/update_5.1.0-5.1.1.sql 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/script/sql/update/oracle/update_5.1.1-5.1.2.sql 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/script/sql/update/oracle/update_5.1.2-5.2.0.sql 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/script/sql/update/oracle/update_5.3.0-5.3.1.sql 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/script/sql/update/oracle/update_5.3.1-5.4.0.sql 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/script/sql/update/oracle/update_5.4.1-5.5.0.sql 97 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/script/sql/update/oracle/update_5.5.0-5.5.1.sql 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/script/sql/update/oracle/update_5.5.1-5.5.2.sql 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/script/sql/update/postgres/update_5.0-5.1.sql 150 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/script/sql/update/postgres/update_5.1.0-5.1.1.sql 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/script/sql/update/postgres/update_5.1.1-5.1.2.sql 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/script/sql/update/postgres/update_5.1.2-5.2.0.sql 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/script/sql/update/postgres/update_5.3.0-5.3.1.sql 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/script/sql/update/postgres/update_5.3.1-5.4.0.sql 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/script/sql/update/postgres/update_5.4.1-5.5.0.sql 99 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/script/sql/update/postgres/update_5.5.0-5.5.1.sql 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/script/sql/update/postgres/update_5.5.1-5.5.2.sql 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/script/sql/update/sqlserver/update_5.0-5.1.sql 409 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/script/sql/update/sqlserver/update_5.1.0-5.1.1.sql 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/script/sql/update/sqlserver/update_5.1.1-5.1.2.sql 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/script/sql/update/sqlserver/update_5.1.2-5.2.0.sql 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/script/sql/update/sqlserver/update_5.3.0-5.3.1.sql 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/script/sql/update/sqlserver/update_5.3.1-5.4.0.sql 63 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/script/sql/update/sqlserver/update_5.4.1-5.5.0.sql 294 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/script/sql/update/sqlserver/update_5.5.0-5.5.1.sql 99 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/script/sql/update/sqlserver/update_5.5.1-5.5.2.sql 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/script/sql/update/update_5.0-5.1.sql 101 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/script/sql/update/update_5.1.0-5.1.1.sql 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/script/sql/update/update_5.1.1-5.1.2.sql 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/script/sql/update/update_5.1.2-5.2.0.sql 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/script/sql/update/update_5.3.0-5.3.1.sql 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/script/sql/update/update_5.3.1-5.4.0.sql 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/script/sql/update/update_5.4.1-5.5.0.sql 67 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/script/sql/update/update_5.5.0-5.5.1.sql 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/script/sql/update/update_5.5.1-5.5.2.sql 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/开发日志.md 119 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/.drone.yml 73 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/.editorconfig 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/.env 63 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/.env.dev 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/.env.prod 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/.env.test 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/.gitattributes 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/.gitignore 35 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/.npmrc 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/CHANGELOG.md 501 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/LICENSE 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/README.md 391 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/build/config/index.ts 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/build/config/proxy.ts 56 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/build/config/time.ts 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/build/plugins/devtools.ts 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/build/plugins/html.ts 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/build/plugins/index.ts 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/build/plugins/router.ts 44 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/build/plugins/unocss.ts 32 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/build/plugins/unplugin.ts 47 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/docs/README.md 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/docs/java/VelocityUtils.java 389 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/docs/sql/sys_dict_data.sql 59 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/docs/sql/sys_menu.sql 43 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/docs/template/api/api.ts.vm 41 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/docs/template/index-tree.vue.vm 232 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/docs/template/index.vue.vm 198 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/docs/template/modules/operate-drawer.vue.vm 257 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/docs/template/modules/search.vue.vm 130 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/docs/template/typings/api.d.ts.vm 51 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/eslint.config.js 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/index.html 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/package.json 127 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/packages/alova/package.json 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/packages/alova/src/client.ts 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/packages/alova/src/constant.ts 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/packages/alova/src/fetch.ts 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/packages/alova/src/index.ts 77 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/packages/alova/src/mock.ts 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/packages/alova/src/type.ts 52 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/packages/alova/tsconfig.json 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/packages/axios/package.json 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/packages/axios/src/constant.ts 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/packages/axios/src/index.ts 179 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/packages/axios/src/options.ts 60 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/packages/axios/src/shared.ts 79 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/packages/axios/src/type.ts 130 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/packages/axios/tsconfig.json 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/packages/color/package.json 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/packages/color/src/constant/index.ts 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/packages/color/src/constant/name.ts 1579 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/packages/color/src/constant/palette.ts 356 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/packages/color/src/index.ts 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/packages/color/src/palette/antd.ts 176 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/packages/color/src/palette/index.ts 45 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/packages/color/src/palette/recommend.ts 152 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/packages/color/src/shared/colord.ts 93 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/packages/color/src/shared/index.ts 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/packages/color/src/shared/name.ts 49 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/packages/color/src/types/index.ts 58 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/packages/color/tsconfig.json 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/packages/hooks/package.json 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/packages/hooks/src/index.ts 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/packages/hooks/src/use-boolean.ts 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/packages/hooks/src/use-context.ts 96 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/packages/hooks/src/use-count-down.ts 68 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/packages/hooks/src/use-loading.ts 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/packages/hooks/src/use-request.ts 82 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/packages/hooks/src/use-svg-icon-render.ts 50 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/packages/hooks/src/use-table.ts 131 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/packages/hooks/tsconfig.json 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/packages/materials/package.json 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/packages/materials/src/index.ts 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/packages/materials/src/libs/admin-layout/index.module.css 63 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/packages/materials/src/libs/admin-layout/index.module.css.d.ts 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/packages/materials/src/libs/admin-layout/index.ts 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/packages/materials/src/libs/admin-layout/index.vue 236 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/packages/materials/src/libs/admin-layout/shared.ts 68 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/packages/materials/src/libs/page-tab/button-tab.vue 53 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/packages/materials/src/libs/page-tab/chrome-tab-bg.vue 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/packages/materials/src/libs/page-tab/chrome-tab.vue 58 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/packages/materials/src/libs/page-tab/index.module.css 121 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/packages/materials/src/libs/page-tab/index.module.css.d.ts 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/packages/materials/src/libs/page-tab/index.ts 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/packages/materials/src/libs/page-tab/index.vue 77 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/packages/materials/src/libs/page-tab/shared.ts 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/packages/materials/src/libs/page-tab/slider-tab.vue 53 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/packages/materials/src/libs/page-tab/svg-close.vue 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/packages/materials/src/libs/simple-scrollbar/index.ts 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/packages/materials/src/libs/simple-scrollbar/index.vue 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/packages/materials/src/types/index.ts 291 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/packages/materials/tsconfig.json 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/packages/scripts/bin.ts 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/packages/scripts/package.json 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/packages/scripts/src/commands/changelog.ts 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/packages/scripts/src/commands/cleanup.ts 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/packages/scripts/src/commands/git-commit.ts 84 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/packages/scripts/src/commands/index.ts 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/packages/scripts/src/commands/release.ts 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/packages/scripts/src/commands/router.ts 90 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/packages/scripts/src/commands/update-pkg.ts 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/packages/scripts/src/config/index.ts 39 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/packages/scripts/src/index.ts 109 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/packages/scripts/src/locales/index.ts 82 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/packages/scripts/src/shared/index.ts 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/packages/scripts/src/types/index.ts 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/packages/scripts/tsconfig.json 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/packages/uno-preset/package.json 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/packages/uno-preset/src/index.ts 55 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/packages/uno-preset/tsconfig.json 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/packages/utils/package.json 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/packages/utils/src/crypto.ts 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/packages/utils/src/index.ts 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/packages/utils/src/klona.ts 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/packages/utils/src/nanoid.ts 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/packages/utils/src/storage.ts 77 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/packages/utils/tsconfig.json 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/pnpm-lock.yaml 10758 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/pnpm-workspace.yaml 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/public/favicon.svg 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/public/streamsaver/mitm.html 179 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/public/streamsaver/sw.js 132 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/App.vue 59 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/assets/imgs/soybean.jpg 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/assets/svg-icon/activity.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/assets/svg-icon/at-sign.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/assets/svg-icon/avatar.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/assets/svg-icon/banner.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/assets/svg-icon/bell.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/assets/svg-icon/cast.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/assets/svg-icon/chrome.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/assets/svg-icon/copy.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/assets/svg-icon/custom-icon.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/assets/svg-icon/empty-data.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/assets/svg-icon/expectation.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/assets/svg-icon/heart.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/assets/svg-icon/login-background.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/assets/svg-icon/logo.svg 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/assets/svg-icon/maxkey.svg 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/assets/svg-icon/menu/404.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/assets/svg-icon/menu/bug.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/assets/svg-icon/menu/build.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/assets/svg-icon/menu/button.svg 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/assets/svg-icon/menu/caret-back.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/assets/svg-icon/menu/caret-forward.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/assets/svg-icon/menu/cascader.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/assets/svg-icon/menu/category.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/assets/svg-icon/menu/chart.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/assets/svg-icon/menu/checkbox.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/assets/svg-icon/menu/clipboard.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/assets/svg-icon/menu/code.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/assets/svg-icon/menu/color.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/assets/svg-icon/menu/company.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/assets/svg-icon/menu/component.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/assets/svg-icon/menu/dashboard.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/assets/svg-icon/menu/date-range.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/assets/svg-icon/menu/date.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/assets/svg-icon/menu/dict.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/assets/svg-icon/menu/documentation.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/assets/svg-icon/menu/download.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/assets/svg-icon/menu/drag.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/assets/svg-icon/menu/druid.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/assets/svg-icon/menu/edit.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/assets/svg-icon/menu/education.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/assets/svg-icon/menu/email.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/assets/svg-icon/menu/example.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/assets/svg-icon/menu/excel.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/assets/svg-icon/menu/exit-fullscreen.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/assets/svg-icon/menu/eye-open.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/assets/svg-icon/menu/eye.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/assets/svg-icon/menu/finish.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/assets/svg-icon/menu/form.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/assets/svg-icon/menu/fullscreen.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/assets/svg-icon/menu/gitee.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/assets/svg-icon/menu/github.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/assets/svg-icon/menu/guide.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/assets/svg-icon/menu/icon.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/assets/svg-icon/menu/input.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/assets/svg-icon/menu/international.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/assets/svg-icon/menu/job.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/assets/svg-icon/menu/language.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/assets/svg-icon/menu/link.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/assets/svg-icon/menu/list.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/assets/svg-icon/menu/lock.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/assets/svg-icon/menu/log.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/assets/svg-icon/menu/logininfor.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/assets/svg-icon/menu/message.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/assets/svg-icon/menu/model.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/assets/svg-icon/menu/money.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/assets/svg-icon/menu/monitor.svg 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/assets/svg-icon/menu/my-copy.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/assets/svg-icon/menu/my-task.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/assets/svg-icon/menu/nested.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/assets/svg-icon/menu/number.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/assets/svg-icon/menu/online.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/assets/svg-icon/menu/password.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/assets/svg-icon/menu/pdf.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/assets/svg-icon/menu/people.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/assets/svg-icon/menu/peoples.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/assets/svg-icon/menu/phone.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/assets/svg-icon/menu/post.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/assets/svg-icon/menu/process-definition.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/assets/svg-icon/menu/qq.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/assets/svg-icon/menu/question.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/assets/svg-icon/menu/radio.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/assets/svg-icon/menu/rate.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/assets/svg-icon/menu/redis-list.svg 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/assets/svg-icon/menu/redis.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/assets/svg-icon/menu/row.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/assets/svg-icon/menu/search.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/assets/svg-icon/menu/select.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/assets/svg-icon/menu/server.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/assets/svg-icon/menu/shopping.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/assets/svg-icon/menu/size.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/assets/svg-icon/menu/skill.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/assets/svg-icon/menu/slider.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/assets/svg-icon/menu/star.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/assets/svg-icon/menu/swagger.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/assets/svg-icon/menu/switch.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/assets/svg-icon/menu/system.svg 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/assets/svg-icon/menu/tab.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/assets/svg-icon/menu/table.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/assets/svg-icon/menu/textarea.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/assets/svg-icon/menu/theme.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/assets/svg-icon/menu/time-range.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/assets/svg-icon/menu/time.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/assets/svg-icon/menu/tool.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/assets/svg-icon/menu/tree-table.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/assets/svg-icon/menu/tree.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/assets/svg-icon/menu/upload.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/assets/svg-icon/menu/user.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/assets/svg-icon/menu/validCode.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/assets/svg-icon/menu/waiting.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/assets/svg-icon/menu/wechat.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/assets/svg-icon/menu/workflow.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/assets/svg-icon/menu/zip.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/assets/svg-icon/network-error.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/assets/svg-icon/no-icon.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/assets/svg-icon/no-permission.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/assets/svg-icon/not-found.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/assets/svg-icon/service-error.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/assets/svg-icon/topiam.svg 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/assets/svg-icon/wind.svg 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/components/advanced/table-column-setting.vue 54 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/components/advanced/table-header-operation.vue 97 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/components/advanced/table-row-check-alert.vue 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/components/advanced/table-sider-layout.vue 109 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/components/common/app-provider.vue 56 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/components/common/dark-mode-container.vue 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/components/common/data-table.vue 35 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/components/common/exception-base.vue 43 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/components/common/full-screen.vue 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/components/common/icon-tooltip.vue 42 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/components/common/lang-switch.vue 61 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/components/common/menu-toggler.vue 53 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/components/common/pin-toggler.vue 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/components/common/reload-button.vue 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/components/common/system-logo.vue 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/components/common/theme-schema-switch.vue 56 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/components/custom/better-scroll.vue 53 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/components/custom/boolean-tag.vue 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/components/custom/button-icon.vue 91 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/components/custom/count-to.vue 88 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/components/custom/dept-tree-select.vue 51 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/components/custom/dept-tree.vue 154 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/components/custom/dict-checkbox.vue 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/components/custom/dict-radio.vue 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/components/custom/dict-select.vue 37 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/components/custom/dict-tag.vue 62 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/components/custom/file-upload.vue 212 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/components/custom/form-tip.vue 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/components/custom/json-preview.vue 52 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/components/custom/look-forward.vue 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/components/custom/menu-tree-select.vue 82 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/components/custom/menu-tree.vue 222 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/components/custom/oss-upload.vue 69 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/components/custom/post-select.vue 63 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/components/custom/role-select.vue 52 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/components/custom/soybean-avatar.vue 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/components/custom/status-switch.vue 61 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/components/custom/svg-icon.vue 54 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/components/custom/tenant-select.vue 103 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/components/custom/umo-doc-editor.vue 104 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/components/custom/user-select.vue 52 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/components/custom/wave-bg.vue 524 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/constants/app.ts 79 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/constants/business.ts 138 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/constants/common.ts 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/constants/reg.ts 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/enum/business.ts 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/enum/index.ts 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/hooks/business/auth.ts 46 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/hooks/business/captcha.ts 71 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/hooks/business/dict.ts 85 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/hooks/business/download.ts 166 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/hooks/common/echarts.ts 230 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/hooks/common/form.ts 112 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/hooks/common/icon.ts 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/hooks/common/loading.ts 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/hooks/common/router.ts 116 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/hooks/common/table.ts 477 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/layouts/base-layout/index.vue 170 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/layouts/blank-layout/index.vue 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/layouts/modules/global-breadcrumb/index.vue 47 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/layouts/modules/global-content/index.vue 63 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/layouts/modules/global-footer/index.vue 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/layouts/modules/global-header/components/message-button.vue 152 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/layouts/modules/global-header/components/theme-button.vue 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/layouts/modules/global-header/components/user-avatar.vue 142 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/layouts/modules/global-header/index.vue 68 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/layouts/modules/global-logo/index.vue 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/layouts/modules/global-menu/components/first-level-menu.vue 109 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/layouts/modules/global-menu/context/index.ts 215 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/layouts/modules/global-menu/index.vue 40 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/layouts/modules/global-menu/modules/horizontal-menu.vue 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/layouts/modules/global-menu/modules/top-hybrid-header-first.vue 89 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/layouts/modules/global-menu/modules/top-hybrid-sidebar-first.vue 64 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/layouts/modules/global-menu/modules/vertical-hybrid-header-first.vue 173 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/layouts/modules/global-menu/modules/vertical-menu.vue 63 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/layouts/modules/global-menu/modules/vertical-mix-menu.vue 127 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/layouts/modules/global-search/components/search-footer.vue 36 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/layouts/modules/global-search/components/search-modal.vue 123 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/layouts/modules/global-search/components/search-result.vue 56 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/layouts/modules/global-search/index.vue 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/layouts/modules/global-sider/index.vue 36 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/layouts/modules/global-tab/context-menu.vue 146 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/layouts/modules/global-tab/index.vue 233 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/layouts/modules/theme-drawer/components/layout-mode-card.vue 101 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/layouts/modules/theme-drawer/components/setting-item.vue 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/layouts/modules/theme-drawer/index.vue 66 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/layouts/modules/theme-drawer/modules/appearance/index.vue 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/layouts/modules/theme-drawer/modules/appearance/modules/theme-color.vue 83 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/layouts/modules/theme-drawer/modules/appearance/modules/theme-radius.vue 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/layouts/modules/theme-drawer/modules/appearance/modules/theme-schema.vue 76 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/layouts/modules/theme-drawer/modules/config-operation.vue 58 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/layouts/modules/theme-drawer/modules/general/index.vue 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/layouts/modules/theme-drawer/modules/general/modules/global-settings.vue 39 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/layouts/modules/theme-drawer/modules/general/modules/watermark-settings.vue 66 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/layouts/modules/theme-drawer/modules/layout/index.vue 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/layouts/modules/theme-drawer/modules/layout/modules/content-settings.vue 64 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/layouts/modules/theme-drawer/modules/layout/modules/footer-settings.vue 61 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/layouts/modules/theme-drawer/modules/layout/modules/header-settings.vue 47 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/layouts/modules/theme-drawer/modules/layout/modules/layout-mode.vue 84 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/layouts/modules/theme-drawer/modules/layout/modules/sider-settings.vue 60 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/layouts/modules/theme-drawer/modules/layout/modules/tab-settings.vue 62 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/layouts/modules/theme-drawer/modules/layout/modules/table-settings.vue 44 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/layouts/modules/theme-drawer/modules/preset/index.vue 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/layouts/modules/theme-drawer/modules/preset/modules/theme-preset.vue 156 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/layouts/modules/theme-drawer/modules/table-props.vue 44 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/locales/dayjs.ts 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/locales/index.ts 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/locales/langs/en-us.ts 1279 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/locales/langs/zh-cn.ts 1271 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/locales/locale.ts 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/locales/naive.ts 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/main.ts 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/plugins/app.ts 108 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/plugins/assets.ts 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/plugins/dayjs.ts 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/plugins/iconify.ts 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/plugins/index.ts 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/plugins/loading.ts 34 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/plugins/nprogress.ts 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/router/elegant/imports.ts 51 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/router/elegant/routes.ts 437 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/router/elegant/transform.ts 228 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/router/guard/index.ts 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/router/guard/progress.ts 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/router/guard/route.ts 192 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/router/guard/title.ts 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/router/index.ts 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/router/routes/builtin.ts 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/router/routes/index.ts 156 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/service/api/analy/hoister-data.ts 35 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/service/api/analy/packer-data.ts 35 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/service/api/analy/roller-data.ts 46 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/service/api/auth.ts 78 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/service/api/demo/demo.ts 36 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/service/api/demo/index.ts 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/service/api/demo/tree.ts 36 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/service/api/index.ts 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/service/api/monitor/cache.ts 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/service/api/monitor/index.ts 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/service/api/monitor/login-infor.ts 34 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/service/api/monitor/online.ts 42 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/service/api/monitor/oper-log.ts 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/service/api/route.ts 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/service/api/system/client.ts 45 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/service/api/system/config.ts 69 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/service/api/system/dept.ts 51 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/service/api/system/dict-data.ts 36 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/service/api/system/dict.ts 59 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/service/api/system/index.ts 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/service/api/system/menu.ts 69 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/service/api/system/notice.ts 36 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/service/api/system/oss-config.ts 45 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/service/api/system/oss.ts 40 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/service/api/system/post.ts 52 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/service/api/system/role.ts 98 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/service/api/system/social.ts 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/service/api/system/tenant-package.ts 52 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/service/api/system/tenant.ts 81 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/service/api/system/user.ts 136 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/service/api/tool/gen.ts 101 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/service/api/tool/index.ts 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/service/request/index.ts 225 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/service/request/shared.ts 63 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/service/request/type.ts 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/store/index.ts 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/store/modules/app/index.ts 166 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/store/modules/auth/index.ts 199 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/store/modules/auth/shared.ts 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/store/modules/dict/index.ts 42 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/store/modules/notice/index.ts 50 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/store/modules/route/index.ts 424 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/store/modules/route/shared.ts 335 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/store/modules/tab/index.ts 395 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/store/modules/tab/shared.ts 263 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/store/modules/theme/index.ts 303 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/store/modules/theme/shared.ts 269 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/store/plugins/index.ts 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/styles/css/global.css 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/styles/css/nprogress.css 83 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/styles/css/reset.css 385 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/styles/css/transition.css 82 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/styles/scss/custom.scss 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/styles/scss/global.scss 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/styles/scss/loading.scss 105 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/styles/scss/scrollbar.scss 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/theme/preset/azir.json 53 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/theme/preset/compact.json 41 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/theme/preset/dark.json 92 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/theme/preset/default.json 100 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/theme/preset/soybean.json 91 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/theme/settings.ts 104 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/theme/vars.ts 35 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/typings/api/analy.hoister-data.api.d.ts 155 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/typings/api/analy.packer-data.api.d.ts 131 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/typings/api/analy.roller-data.api.d.ts 123 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/typings/api/api.d.ts 88 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/typings/api/auth.d.ts 110 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/typings/api/demo.api.d.ts 83 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/typings/api/monitor.api.d.ts 170 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/typings/api/route.d.ts 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/typings/api/system.api.d.ts 830 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/typings/api/tool.api.d.ts 190 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/typings/app.d.ts 1130 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/typings/common.d.ts 46 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/typings/components.d.ts 312 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/typings/elegant-router.d.ts 319 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/typings/global.d.ts 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/typings/naive-ui.d.ts 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/typings/router.d.ts 72 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/typings/storage.d.ts 52 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/typings/union-key.d.ts 165 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/typings/vite-env.d.ts 128 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/utils/agent.ts 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/utils/common.ts 210 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/utils/copy.ts 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/utils/crypto.ts 70 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/utils/icon-tag-format.ts 62 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/utils/icon.ts 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/utils/jsencrypt.ts 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/utils/service.ts 75 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/utils/sse.ts 58 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/utils/storage.ts 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/utils/websocket.ts 60 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/views/_builtin/403/index.vue 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/views/_builtin/404/index.vue 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/views/_builtin/500/index.vue 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/views/_builtin/iframe-page/[url].vue 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/views/_builtin/login/index.vue 107 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/views/_builtin/login/modules/bind-wechat.vue 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/views/_builtin/login/modules/code-login.vue 87 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/views/_builtin/login/modules/pwd-login.vue 258 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/views/_builtin/login/modules/register.vue 194 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/views/_builtin/login/modules/reset-pwd.vue 105 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/views/_builtin/social-callback/index.vue 125 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/views/_builtin/user-center/index.vue 244 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/views/_builtin/user-center/modules/online-table.vue 125 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/views/_builtin/user-center/modules/social-card.vue 114 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/views/_builtin/user-center/modules/user-avatar.vue 211 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/views/about/index.vue 110 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/views/analy/hoister/index.vue 413 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/views/analy/hoister/modules/hoister-data-operate-drawer.vue 285 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/views/analy/hoister/modules/hoister-data-search.vue 147 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/views/analy/output-analy/index.vue 269 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/views/analy/output-analy/modules/roller-data-line-chart.vue 312 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/views/analy/output-analy/modules/roller-data-operate-drawer.vue 213 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/views/analy/output-analy/modules/roller-data-search.vue 147 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/views/analy/packer/index.vue 344 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/views/analy/packer/modules/packer-data-operate-drawer.vue 240 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/views/analy/packer/modules/packer-data-search.vue 147 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/views/analy/roller/index.vue 304 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/views/analy/roller/modules/roller-data-operate-drawer.vue 209 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/views/analy/roller/modules/roller-data-search.vue 147 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/views/demo/demo/index.vue 225 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/views/demo/demo/modules/demo-operate-drawer.vue 140 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/views/demo/demo/modules/demo-search.vue 80 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/views/demo/tree/index.vue 233 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/views/demo/tree/modules/tree-operate-drawer.vue 166 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/views/demo/tree/modules/tree-search.vue 77 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/views/home/index.vue 43 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/views/home/modules/card-data.vue 115 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/views/home/modules/creativity-banner.vue 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/views/home/modules/header-banner.vue 71 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/views/home/modules/line-chart.vue 152 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/views/home/modules/pie-chart.vue 109 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/views/home/modules/project-news.vue 40 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/views/monitor/cache/index.vue 690 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/views/monitor/logininfor/index.vue 279 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/views/monitor/logininfor/modules/login-infor-search.vue 101 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/views/monitor/logininfor/modules/login-infor-view-drawer.vue 71 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/views/monitor/online/index.vue 172 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/views/monitor/online/modules/online-search.vue 74 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/views/monitor/operlog/index.vue 225 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/views/monitor/operlog/modules/oper-log-search.vue 112 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/views/monitor/operlog/modules/oper-log-view-drawer.vue 73 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/views/system/client/index.vue 275 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/views/system/client/modules/client-operate-drawer.vue 221 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/views/system/client/modules/client-search.vue 95 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/views/system/config/index.vue 240 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/views/system/config/modules/config-operate-drawer.vue 145 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/views/system/config/modules/config-search.vue 122 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/views/system/dept/index.vue 218 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/views/system/dept/modules/dept-operate-drawer.vue 248 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/views/system/dept/modules/dept-search.vue 82 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/views/system/dict/index.vue 519 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/views/system/dict/modules/dict-data-operate-drawer.vue 206 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/views/system/dict/modules/dict-data-search.vue 77 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/views/system/dict/modules/dict-type-operate-drawer.vue 132 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/views/system/menu/index.vue 581 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/views/system/menu/modules/menu-cascade-delete-modal.vue 118 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/views/system/menu/modules/menu-operate-drawer.vue 511 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/views/system/notice/index.vue 200 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/views/system/notice/modules/notice-operate-drawer.vue 145 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/views/system/notice/modules/notice-search.vue 74 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/views/system/oss-config/index.vue 260 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/views/system/oss-config/modules/oss-config-operate-drawer.vue 226 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/views/system/oss-config/modules/oss-config-search.vue 92 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/views/system/oss/index.vue 360 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/views/system/oss/modules/oss-search.vue 102 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/views/system/oss/modules/oss-upload-modal.vue 67 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/views/system/post/index.vue 358 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/views/system/post/modules/post-operate-drawer.vue 169 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/views/system/post/modules/post-search.vue 86 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/views/system/role/index.vue 293 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/views/system/role/modules/role-auth-user-drawer.vue 275 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/views/system/role/modules/role-data-scope-drawer.vue 154 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/views/system/role/modules/role-operate-drawer.vue 198 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/views/system/role/modules/role-search.vue 109 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/views/system/tenant-package/index.vue 237 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/views/system/tenant-package/modules/tenant-package-operate-drawer.vue 174 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/views/system/tenant-package/modules/tenant-package-search.vue 95 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/views/system/tenant/index.vue 302 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/views/system/tenant/modules/tenant-operate-drawer.vue 302 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/views/system/tenant/modules/tenant-search.vue 80 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/views/system/user/index.vue 442 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/views/system/user/modules/user-import-modal.vue 162 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/views/system/user/modules/user-operate-drawer.vue 248 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/views/system/user/modules/user-password-drawer.vue 115 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/views/system/user/modules/user-search.vue 122 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/views/tool/gen/index.vue 317 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/views/tool/gen/modules/gen-table-db-search.vue 76 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/views/tool/gen/modules/gen-table-import-drawer.vue 148 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/views/tool/gen/modules/gen-table-operate-drawer.vue 507 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/views/tool/gen/modules/gen-table-preview-drawer.vue 240 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/src/views/tool/gen/modules/gen-table-search.vue 103 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/tsconfig.json 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/uno.config.ts 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-plus-soybean/vite.config.ts 51 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RuoYi-Vue-Plus/.DS_Store
Binary files differ
RuoYi-Vue-Plus/.flattened-pom.xml
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,419 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <modelVersion>4.0.0</modelVersion>
  <groupId>org.dromara</groupId>
  <artifactId>ruoyi-vue-plus</artifactId>
  <version>5.5.3</version>
  <packaging>pom</packaging>
  <name>RuoYi-Vue-Plus</name>
  <description>Dromara RuoYi-Vue-Plus多租户管理系统</description>
  <url>https://gitee.com/dromara/RuoYi-Vue-Plus</url>
  <modules>
    <module>ruoyi-admin</module>
    <module>ruoyi-common</module>
    <module>ruoyi-extend</module>
    <module>ruoyi-modules</module>
  </modules>
  <properties>
    <spring-boot-admin.version>3.5.6</spring-boot-admin.version>
    <dynamic-ds.version>4.3.1</dynamic-ds.version>
    <aws.sdk.version>2.28.22</aws.sdk.version>
    <hutool.version>5.8.43</hutool.version>
    <spring-boot.version>3.5.10</spring-boot.version>
    <p6spy.version>3.9.1</p6spy.version>
    <flatten-maven-plugin.version>1.3.0</flatten-maven-plugin.version>
    <snailjob.version>1.9.0</snailjob.version>
    <springdoc.version>2.8.15</springdoc.version>
    <mapstruct-plus.version>1.5.0</mapstruct-plus.version>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <fastjson.version>1.2.83</fastjson.version>
    <skipTests>true</skipTests>
    <anyline.version>8.7.3-20251210</anyline.version>
    <therapi-javadoc.version>0.15.0</therapi-javadoc.version>
    <lock4j.version>2.2.7</lock4j.version>
    <warm-flow.version>1.8.4</warm-flow.version>
    <sms4j.version>3.3.5</sms4j.version>
    <mybatis.version>3.5.19</mybatis.version>
    <maven-jar-plugin.version>3.4.2</maven-jar-plugin.version>
    <java.version>17</java.version>
    <bouncycastle.version>1.80</bouncycastle.version>
    <maven-compiler-plugin.version>3.14.0</maven-compiler-plugin.version>
    <fastexcel.version>1.3.0</fastexcel.version>
    <mybatis-plus.version>3.5.16</mybatis-plus.version>
    <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
    <revision>5.5.3</revision>
    <lombok.version>1.18.42</lombok.version>
    <maven-surefire-plugin.version>3.5.3</maven-surefire-plugin.version>
    <mapstruct-plus.lombok.version>0.2.0</mapstruct-plus.lombok.version>
    <ip2region.version>3.3.2</ip2region.version>
    <satoken.version>1.44.0</satoken.version>
    <redisson.version>3.52.0</redisson.version>
    <justauth.version>1.16.7</justauth.version>
    <maven-war-plugin.version>3.4.0</maven-war-plugin.version>
    <velocity.version>2.3</velocity.version>
  </properties>
  <dependencyManagement>
    <dependencies>
      <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-dependencies</artifactId>
        <version>${spring-boot.version}</version>
        <type>pom</type>
        <scope>import</scope>
      </dependency>
      <dependency>
        <groupId>cn.hutool</groupId>
        <artifactId>hutool-bom</artifactId>
        <version>${hutool.version}</version>
        <type>pom</type>
        <scope>import</scope>
      </dependency>
      <dependency>
        <groupId>org.dromara</groupId>
        <artifactId>ruoyi-common-bom</artifactId>
        <version>5.5.3</version>
        <type>pom</type>
        <scope>import</scope>
      </dependency>
      <dependency>
        <groupId>org.springdoc</groupId>
        <artifactId>springdoc-openapi-starter-webmvc-api</artifactId>
        <version>${springdoc.version}</version>
      </dependency>
      <dependency>
        <groupId>com.github.therapi</groupId>
        <artifactId>therapi-runtime-javadoc</artifactId>
        <version>${therapi-javadoc.version}</version>
      </dependency>
      <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>${lombok.version}</version>
      </dependency>
      <dependency>
        <groupId>cn.idev.excel</groupId>
        <artifactId>fastexcel</artifactId>
        <version>${fastexcel.version}</version>
      </dependency>
      <dependency>
        <groupId>org.apache.velocity</groupId>
        <artifactId>velocity-engine-core</artifactId>
        <version>${velocity.version}</version>
      </dependency>
      <dependency>
        <groupId>cn.dev33</groupId>
        <artifactId>sa-token-spring-boot3-starter</artifactId>
        <version>${satoken.version}</version>
      </dependency>
      <dependency>
        <groupId>cn.dev33</groupId>
        <artifactId>sa-token-jwt</artifactId>
        <version>${satoken.version}</version>
        <exclusions>
          <exclusion>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
          </exclusion>
        </exclusions>
      </dependency>
      <dependency>
        <groupId>cn.dev33</groupId>
        <artifactId>sa-token-core</artifactId>
        <version>${satoken.version}</version>
      </dependency>
      <dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>dynamic-datasource-spring-boot3-starter</artifactId>
        <version>${dynamic-ds.version}</version>
      </dependency>
      <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis</artifactId>
        <version>${mybatis.version}</version>
      </dependency>
      <dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>mybatis-plus-spring-boot3-starter</artifactId>
        <version>${mybatis-plus.version}</version>
      </dependency>
      <dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>mybatis-plus-jsqlparser</artifactId>
        <version>${mybatis-plus.version}</version>
      </dependency>
      <dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>mybatis-plus-annotation</artifactId>
        <version>${mybatis-plus.version}</version>
      </dependency>
      <dependency>
        <groupId>p6spy</groupId>
        <artifactId>p6spy</artifactId>
        <version>${p6spy.version}</version>
      </dependency>
      <dependency>
        <groupId>software.amazon.awssdk</groupId>
        <artifactId>s3</artifactId>
        <version>${aws.sdk.version}</version>
      </dependency>
      <dependency>
        <groupId>software.amazon.awssdk</groupId>
        <artifactId>s3-transfer-manager</artifactId>
        <version>${aws.sdk.version}</version>
      </dependency>
      <dependency>
        <groupId>software.amazon.awssdk</groupId>
        <artifactId>netty-nio-client</artifactId>
        <version>${aws.sdk.version}</version>
      </dependency>
      <dependency>
        <groupId>org.dromara.sms4j</groupId>
        <artifactId>sms4j-spring-boot-starter</artifactId>
        <version>${sms4j.version}</version>
      </dependency>
      <dependency>
        <groupId>de.codecentric</groupId>
        <artifactId>spring-boot-admin-starter-server</artifactId>
        <version>${spring-boot-admin.version}</version>
      </dependency>
      <dependency>
        <groupId>de.codecentric</groupId>
        <artifactId>spring-boot-admin-starter-client</artifactId>
        <version>${spring-boot-admin.version}</version>
      </dependency>
      <dependency>
        <groupId>org.redisson</groupId>
        <artifactId>redisson-spring-boot-starter</artifactId>
        <version>${redisson.version}</version>
      </dependency>
      <dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>lock4j-redisson-spring-boot-starter</artifactId>
        <version>${lock4j.version}</version>
      </dependency>
      <dependency>
        <groupId>com.aizuda</groupId>
        <artifactId>snail-job-client-starter</artifactId>
        <version>${snailjob.version}</version>
      </dependency>
      <dependency>
        <groupId>com.aizuda</groupId>
        <artifactId>snail-job-client-job-core</artifactId>
        <version>${snailjob.version}</version>
      </dependency>
      <dependency>
        <groupId>org.bouncycastle</groupId>
        <artifactId>bcprov-jdk15to18</artifactId>
        <version>${bouncycastle.version}</version>
      </dependency>
      <dependency>
        <groupId>io.github.linpeilie</groupId>
        <artifactId>mapstruct-plus-spring-boot-starter</artifactId>
        <version>${mapstruct-plus.version}</version>
      </dependency>
      <dependency>
        <groupId>org.dromara.warm</groupId>
        <artifactId>warm-flow-mybatis-plus-sb3-starter</artifactId>
        <version>${warm-flow.version}</version>
      </dependency>
      <dependency>
        <groupId>org.dromara.warm</groupId>
        <artifactId>warm-flow-plugin-ui-sb-web</artifactId>
        <version>${warm-flow.version}</version>
      </dependency>
      <dependency>
        <groupId>me.zhyd.oauth</groupId>
        <artifactId>JustAuth</artifactId>
        <version>${justauth.version}</version>
      </dependency>
      <dependency>
        <groupId>org.lionsoul</groupId>
        <artifactId>ip2region</artifactId>
        <version>${ip2region.version}</version>
      </dependency>
      <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>fastjson</artifactId>
        <version>${fastjson.version}</version>
      </dependency>
      <dependency>
        <groupId>org.dromara</groupId>
        <artifactId>ruoyi-system</artifactId>
        <version>5.5.3</version>
      </dependency>
      <dependency>
        <groupId>org.dromara</groupId>
        <artifactId>ruoyi-job</artifactId>
        <version>5.5.3</version>
      </dependency>
      <dependency>
        <groupId>org.dromara</groupId>
        <artifactId>ruoyi-generator</artifactId>
        <version>5.5.3</version>
      </dependency>
      <dependency>
        <groupId>org.dromara</groupId>
        <artifactId>ruoyi-demo</artifactId>
        <version>5.5.3</version>
      </dependency>
      <dependency>
        <groupId>org.dromara</groupId>
        <artifactId>ruoyi-qa</artifactId>
        <version>5.5.3</version>
      </dependency>
      <dependency>
        <groupId>org.dromara</groupId>
        <artifactId>ruoyi-workflow</artifactId>
        <version>5.5.3</version>
      </dependency>
    </dependencies>
  </dependencyManagement>
  <repositories>
    <repository>
      <releases>
        <enabled>true</enabled>
      </releases>
      <id>public</id>
      <name>huawei nexus</name>
      <url>https://mirrors.huaweicloud.com/repository/maven/</url>
    </repository>
  </repositories>
  <pluginRepositories>
    <pluginRepository>
      <releases>
        <enabled>true</enabled>
      </releases>
      <snapshots>
        <enabled>false</enabled>
      </snapshots>
      <id>public</id>
      <name>huawei nexus</name>
      <url>https://mirrors.huaweicloud.com/repository/maven/</url>
    </pluginRepository>
  </pluginRepositories>
  <build>
    <resources>
      <resource>
        <filtering>false</filtering>
        <directory>src/main/resources</directory>
      </resource>
      <resource>
        <filtering>true</filtering>
        <directory>src/main/resources</directory>
        <includes>
          <include>application*</include>
          <include>bootstrap*</include>
          <include>banner*</include>
        </includes>
      </resource>
    </resources>
    <plugins>
      <plugin>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>${maven-compiler-plugin.version}</version>
        <configuration>
          <source>${java.version}</source>
          <target>${java.version}</target>
          <encoding>${project.build.sourceEncoding}</encoding>
          <annotationProcessorPaths>
            <path>
              <groupId>com.github.therapi</groupId>
              <artifactId>therapi-runtime-javadoc-scribe</artifactId>
              <version>${therapi-javadoc.version}</version>
            </path>
            <path>
              <groupId>org.projectlombok</groupId>
              <artifactId>lombok</artifactId>
              <version>${lombok.version}</version>
            </path>
            <path>
              <groupId>org.springframework.boot</groupId>
              <artifactId>spring-boot-configuration-processor</artifactId>
              <version>${spring-boot.version}</version>
            </path>
            <path>
              <groupId>io.github.linpeilie</groupId>
              <artifactId>mapstruct-plus-processor</artifactId>
              <version>${mapstruct-plus.version}</version>
            </path>
            <path>
              <groupId>org.projectlombok</groupId>
              <artifactId>lombok-mapstruct-binding</artifactId>
              <version>${mapstruct-plus.lombok.version}</version>
            </path>
          </annotationProcessorPaths>
          <compilerArgs>
            <arg>-parameters</arg>
          </compilerArgs>
        </configuration>
      </plugin>
      <plugin>
        <artifactId>maven-surefire-plugin</artifactId>
        <version>${maven-surefire-plugin.version}</version>
        <configuration>
          <argLine>-Dfile.encoding=UTF-8</argLine>
          <groups>${profiles.active}</groups>
          <excludedGroups>exclude</excludedGroups>
        </configuration>
      </plugin>
      <plugin>
        <groupId>org.codehaus.mojo</groupId>
        <artifactId>flatten-maven-plugin</artifactId>
        <version>${flatten-maven-plugin.version}</version>
        <executions>
          <execution>
            <id>flatten</id>
            <phase>process-resources</phase>
            <goals>
              <goal>flatten</goal>
            </goals>
          </execution>
          <execution>
            <id>flatten.clean</id>
            <phase>clean</phase>
            <goals>
              <goal>clean</goal>
            </goals>
          </execution>
        </executions>
        <configuration>
          <updatePomFile>true</updatePomFile>
          <flattenMode>resolveCiFriendliesOnly</flattenMode>
        </configuration>
      </plugin>
    </plugins>
  </build>
  <profiles>
    <profile>
      <id>local</id>
      <properties>
        <monitor.username>ruoyi</monitor.username>
        <profiles.active>local</profiles.active>
        <monitor.password>123456</monitor.password>
        <logging.level>info</logging.level>
      </properties>
    </profile>
    <profile>
      <id>dev</id>
      <activation>
        <activeByDefault>true</activeByDefault>
      </activation>
      <properties>
        <monitor.username>ruoyi</monitor.username>
        <profiles.active>dev</profiles.active>
        <monitor.password>123456</monitor.password>
        <logging.level>info</logging.level>
      </properties>
    </profile>
    <profile>
      <id>prod</id>
      <properties>
        <monitor.username>ruoyi</monitor.username>
        <profiles.active>prod</profiles.active>
        <monitor.password>123456</monitor.password>
        <logging.level>warn</logging.level>
      </properties>
    </profile>
  </profiles>
</project>
RuoYi-Vue-Plus/LICENSE
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,20 @@
The MIT License (MIT)
Copyright (c) 2019 RuoYi-Vue-Plus
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
RuoYi-Vue-Plus/README.md
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,189 @@
<img src="https://foruda.gitee.com/images/1679673773341074847/178e8451_1766278.png" width="50%" height="50%">
<div style="height: 10px; clear: both;"></div>
- - -
## å¹³å°ç®€ä»‹
[![码云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/dromara/RuoYi-Vue-Plus.svg?style=social&label=Stars)](https://github.com/dromara/RuoYi-Vue-Plus)
[![Star](https://gitcode.com/dromara/RuoYi-Vue-Plus/star/badge.svg)](https://gitcode.com/dromara/RuoYi-Vue-Plus)
[![License](https://img.shields.io/badge/License-MIT-blue.svg)](https://gitee.com/dromara/RuoYi-Vue-Plus/blob/5.X/LICENSE)
<br>
[![RuoYi-Vue-Plus](https://img.shields.io/badge/RuoYi_Vue_Plus-5.5.3-success.svg)](https://gitee.com/dromara/RuoYi-Vue-Plus)
[![Spring Boot](https://img.shields.io/badge/Spring%20Boot-3.5-blue.svg)]()
[![JDK-17](https://img.shields.io/badge/JDK-17-green.svg)]()
[![JDK-21](https://img.shields.io/badge/JDK-21-green.svg)]()
> Dromara RuoYi-Vue-Plus æ˜¯é‡å†™ RuoYi-Vue é’ˆå¯¹ `分布式集群与多租户` åœºæ™¯å…¨æ–¹ä½å‡çº§(不兼容原框架)
> é¡¹ç›®ä»£ç ã€æ–‡æ¡£ å‡å¼€æºå…è´¹å¯å•†ç”¨ éµå¾ªå¼€æºåè®®åœ¨é¡¹ç›®ä¸­ä¿ç•™å¼€æºåè®®æ–‡ä»¶å³å¯<br>
活到老写到老 ä¸ºå…´è¶£è€Œå¼€æº ä¸ºå­¦ä¹ è€Œå¼€æº ä¸ºè®©å¤§å®¶çœŸæ­£å¯ä»¥å­¦åˆ°æŠ€æœ¯è€Œå¼€æº
> ç³»ç»Ÿæ¼”示: [传送门](https://plus-doc.dromara.org/#/common/demo_system)
> å®˜æ–¹å‰ç«¯é¡¹ç›®åœ°å€: [gitee](https://gitee.com/JavaLionLi/plus-ui) - [github](https://github.com/JavaLionLi/plus-ui) - [gitcode](https://gitcode.com/dromara/plus-ui)<br>
> æˆå‘˜å‰ç«¯é¡¹ç›®åœ°å€: åŸºäºŽvben5 [ruoyi-plus-vben5](https://gitee.com/dapppp/ruoyi-plus-vben5)<br>
> æˆå‘˜å‰ç«¯é¡¹ç›®åœ°å€: åŸºäºŽsoybean [ruoyi-plus-soybean](https://gitee.com/xlsea/ruoyi-plus-soybean)<br>
> æˆå‘˜é¡¹ç›®åœ°å€: åˆ é™¤å¤šç§Ÿæˆ·ä¸Žå·¥ä½œæµ [RuoYi-Vue-Plus-Single](https://gitee.com/ColorDreams/RuoYi-Vue-Plus-Single)<br>
> æ–‡æ¡£åœ°å€: [plus-doc](https://plus-doc.dromara.org) å›½å†…加速: [plus-doc.top](https://plus-doc.top)
## èµžåЩ商
MaxKey ä¸šç•Œé¢†å…ˆå•点登录产品 - https://gitee.com/dromara/MaxKey <br>
CCFlow é©°è˜ä½Žä»£ç -流程-表单 - https://gitee.com/opencc/RuoYi-JFlow <br>
数舵科技 è½¯ä»¶å®šåˆ¶å¼€å‘APP小程序等 - https://www.shuduokeji.com/ <br>
引迈信息 è½¯ä»¶å¼€å‘平台 - https://www.jnpfsoft.com/index.html?from=plus-doc <br>
Mall4J é«˜è´¨é‡Java商城系统 - https://www.mall4j.com/cn/?statId=11 <br>
aizuda flowlong å·¥ä½œæµ - https://gitee.com/aizuda/flowlong <br>
Ruoyi-Plus-Uniapp - https://ruoyi.plus <br>
Topiam IAM/IDaaS身份管理平台 - https://www.topiam.cn/ <br>
[如何成为赞助商 åŠ ç¾¤è”ç³»ä½œè€…è¯¦è°ˆ æ¯æ—¥PV2500-3000 IP1700-2500](https://plus-doc.dromara.org/#/common/add_group)
# æœ¬æ¡†æž¶ä¸ŽRuoYi的功能差异
| åŠŸèƒ½          | æœ¬æ¡†æž¶                                                                                                               | RuoYi                                                                              |
|-------------|-------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------|
| å‰ç«¯é¡¹ç›®        | é‡‡ç”¨ Vue3 + TS + ElementPlus é‡å†™                                                                                     | åŸºäºŽVue2/Vue3 + JS                                                                   |
| åŽç«¯é¡¹ç›®ç»“æž„      | é‡‡ç”¨æ’件化 + æ‰©å±•包形式 ç»“构解耦 æ˜“于扩展                                                                                           | æ¨¡å—相互注入耦合严重难以扩展                                                                     |
| åŽç«¯ä»£ç é£Žæ ¼      | ä¸¥æ ¼éµå®ˆAlibaba规范与项目统一配置的代码格式化                                                                                        | ä»£ç ä¹¦å†™ä¸Žå¸¸è§„结构不同阅读障碍大                                                                   |
| Web容器       | é‡‡ç”¨ Undertow åŸºäºŽ XNIO çš„高性能容器                                                                                        | é‡‡ç”¨ Tomcat                                                                          |
| æƒé™è®¤è¯        | é‡‡ç”¨ Sa-Token、Jwt é™æ€ä½¿ç”¨åŠŸèƒ½é½å…¨ ä½Žè€¦åˆ é«˜æ‰©å±•                                                                                  | Spring Security é…ç½®ç¹çæ‰©å±•性极差                                                          |
| æƒé™æ³¨è§£        | é‡‡ç”¨ Sa-Token æ”¯æŒæ³¨è§£ ç™»å½•校验、角色校验、权限校验、二级认证校验、HttpBasic校验、忽略校验<br/>角色与权限校验支持多种条件 å¦‚ `AND` `OR` æˆ– `权限 OR è§’色` ç­‰å¤æ‚表达式        | åªæ”¯æŒæ˜¯å¦å­˜åœ¨åŒ¹é…                                                                          |
| ä¸‰æ–¹é‰´æƒ        | é‡‡ç”¨ JustAuth ç¬¬ä¸‰æ–¹ç™»å½•组件 æ”¯æŒå¾®ä¿¡ã€é’‰é’‰ç­‰æ•°åç§ä¸‰æ–¹è®¤è¯                                                                               | æ—                                                                                   |
| å…³ç³»æ•°æ®åº“支持     | åŽŸç”Ÿæ”¯æŒ MySQL、Oracle、PostgreSQL、SQLServer<br/>可同时使用异构切换(支持其他 mybatis-plus æ”¯æŒçš„æ‰€æœ‰æ•°æ®åº“ åªéœ€è¦å¢žåŠ jdbc依赖即可使用 è¾¾æ¢¦é‡‘仓等均有成功案例)      | æ”¯æŒ Mysql、Oracle ä¸æ”¯æŒåŒæ—¶ä½¿ç”¨ã€ä¸æ”¯æŒå¼‚构切换                                                    |
| ç¼“存数据库       | æ”¯æŒ Redis 5-7 æ”¯æŒå¤§éƒ¨åˆ†æ–°åŠŸèƒ½ç‰¹æ€§ å¦‚ åˆ†å¸ƒå¼é™æµã€åˆ†å¸ƒå¼é˜Ÿåˆ—                                                                             | Redis ç®€å• get set æ”¯æŒ                                                                |
| Redis客户端    | é‡‡ç”¨ Redisson Redis官方推荐 åŸºäºŽNetty的客户端工具<br/>支持Redis 90%以上的命令 åº•层优化规避很多不正确的用法 ä¾‹å¦‚: keys被转换为scan<br/>支持单机、哨兵、单主集群、多主集群等模式 | Lettuce + RedisTemplate æ”¯æŒæ¨¡å¼å°‘ å·¥å…·ä½¿ç”¨ç¹ç<br/>连接池采用 common-pool Bug多经常性出问题              |
| ç¼“存注解        | é‡‡ç”¨ Spring-Cache æ³¨è§£ å¯¹å…¶æ‰©å±•了实现支持了更多功能<br/>例如 è¿‡æœŸæ—¶é—´ æœ€å¤§ç©ºé—²æ—¶é—´ ç»„最大长度等 åªéœ€ä¸€ä¸ªæ³¨è§£å³å¯å®Œæˆæ•°æ®è‡ªåŠ¨ç¼“å­˜                                      | éœ€æ‰‹åŠ¨ç¼–å†™Redis代码逻辑                                                                     |
| ORM框架       | é‡‡ç”¨ Mybatis-Plus åŸºäºŽå¯¹è±¡å‡ ä¹Žä¸ç”¨å†™SQL全java操作 åŠŸèƒ½å¼ºå¤§æ’ä»¶ä¼—å¤š<br/>例如多租户插件 åˆ†é¡µæ’ä»¶ ä¹è§‚锁插件等等                                             | é‡‡ç”¨ Mybatis åŸºäºŽXML需要手写SQL                                                            |
| SQL监控       | é‡‡ç”¨ p6spy å¯è¾“出完整SQL与执行时间监控                                                                                          | log输出 éœ€æ‰‹åŠ¨æ‹¼æŽ¥sql与参数无法快速查看调试问题                                                        |
| æ•°æ®åˆ†é¡µ        | é‡‡ç”¨ Mybatis-Plus åˆ†é¡µæ’ä»¶<br/>框架对其进行了扩展 å¯¹è±¡åŒ–分页对象 æ”¯æŒå¤šç§æ–¹å¼ä¼ å‚ æ”¯æŒå‰ç«¯å¤šæŽ’序 å¤æ‚排序                                                  | é‡‡ç”¨ PageHelper ä»…支持单查询分页 å‚数只能从paramä¼  åªèƒ½å•排序 åŠŸèƒ½æ‰©å±•æ€§å·® ä½“验不好                               |
| æ•°æ®æƒé™        | é‡‡ç”¨ Mybatis-Plus æ’ä»¶ è‡ªè¡Œåˆ†æžæ‹¼æŽ¥SQL æ— æ„Ÿå¼è¿‡æ»¤<br/>只需为Mapper设置好注解条件 æ”¯æŒå¤šç§è‡ªå®šä¹‰ ä¸é™äºŽéƒ¨é—¨è§’色                                           | é‡‡ç”¨ æ³¨è§£+aop å®žçް åŸºäºŽéƒ¨é—¨è§’色 ç”Ÿæˆçš„sql兼容性差 ä¸æ”¯æŒå…¶ä»–业务扩展<br/>生成sql后需手动拼接到具体业务sql上 å¯¹äºŽå¤šä¸ªMapper查询不起作用 |
| æ•°æ®è„±æ•        | é‡‡ç”¨ æ³¨è§£ + jackson åºåˆ—化期间脱敏 æ”¯æŒä¸åŒæ¨¡å—不同的脱敏条件<br/>支持多种策略 å¦‚身份证、手机号、地址、邮箱、银行卡等 å¯è‡ªè¡Œæ‰©å±•                                        | æ—                                                                                   |
| æ•°æ®åŠ è§£å¯†       | é‡‡ç”¨ æ³¨è§£ + mybatis æ‹¦æˆªå™¨ å¯¹å­˜å–数据期间自动加解密<br/>支持多种策略 å¦‚BASE64、AES、RSA、SM2、SM4等                                              | æ—                                                                                   |
| æŽ¥å£ä¼ è¾“加密      | é‡‡ç”¨ åŠ¨æ€ AES + RSA åŠ å¯†è¯·æ±‚ body æ¯ä¸€æ¬¡è¯·æ±‚秘钥都不同大幅度降低可破解性                                                                     | æ—                                                                                   |
| æ•°æ®ç¿»è¯‘        | é‡‡ç”¨ æ³¨è§£ + jackson åºåˆ—化期间动态修改数据 æ•°æ®è¿›è¡Œç¿»è¯‘<br/>支持多种模式: `映射翻译` `直接翻译` `其他扩展条件翻译` æŽ¥å£åŒ–两步即可完成自定义扩展 å†…置多种翻译实现                   | æ—                                                                                   |
| å¤šæ•°æ®æºæ¡†æž¶      | é‡‡ç”¨ dynamic-datasource æ”¯æŒå¸‚面大部分数据库<br/>通过yml配置即可动态管理异构不同种类的数据库 ä¹Ÿå¯é€šè¿‡å‰ç«¯é¡µé¢æ·»åŠ æ•°æ®æº<br/>支持spel表达式从请求头参数等条件切换数据源            | åŸºäºŽ druid æ‰‹åŠ¨ç¼–å†™ä»£ç é…ç½®æ•°æ®æº é…ç½®ç¹ç æ”¯æŒæ€§å·®                                                     |
| å¤šæ•°æ®æºäº‹åŠ¡      | é‡‡ç”¨ dynamic-datasource æ”¯æŒå¤šæ•°æ®æºä¸åŒç§ç±»çš„æ•°æ®åº“事务回滚                                                                          | ä¸æ”¯æŒ                                                                                |
| æ•°æ®åº“连接池      | é‡‡ç”¨ HikariCP Spring官方内置连接池 é…ç½®ç®€å• ä»¥æ€§èƒ½ä¸Žç¨³å®šæ€§é—»åå¤©ä¸‹                                                                        | é‡‡ç”¨ druid bug众多 ç¤¾åŒºç»´æŠ¤å·® æ´»è·ƒåº¦ä½Ž é…ç½®ä¼—多繁琐性能一般                                               |
| æ•°æ®åº“主键       | é‡‡ç”¨ é›ªèбID åŸºäºŽæ—¶é—´æˆ³çš„ æœ‰åºå¢žé•¿ å”¯ä¸€ID å†ä¹Ÿä¸ç”¨ä¸ºåˆ†åº“分表 æ•°æ®åˆå¹¶ä¸»é”®å†²çªé‡å¤è€Œå‘愁                                                                  | é‡‡ç”¨ æ•°æ®åº“自增ID æ”¯æŒæ•°æ®é‡æœ‰é™ ä¸æ”¯æŒå¤šæ•°æ®æºä¸»é”®å”¯ä¸€                                                     |
| WebSocket协议 | åŸºäºŽ Spring å°è£…çš„ WebSocket åè®® æ‰©å±•了Token鉴权与分布式会话同步 ä¸å†åªæ˜¯åŸºäºŽå•机的废物                                                         | æ—                                                                                   |
| SSE推送       | é‡‡ç”¨ Spring SSE å®žçް æ‰©å±•了Token鉴权与分布式会话同步                                                                               | æ—                                                                                   |
| åºåˆ—化         | é‡‡ç”¨ Jackson Spring官方内置序列化 é è°±!!!                                                                                    | é‡‡ç”¨ fastjson bugjson è¿œè¿‘闻名                                                           |
| åˆ†å¸ƒå¼å¹‚ç­‰       | å‚考美团GTIS防重系统简化实现(细节可看文档)                                                                                          | æ‰‹åŠ¨ç¼–å†™æ³¨è§£åŸºäºŽaop实现                                                                      |
| åˆ†å¸ƒå¼é”        | é‡‡ç”¨ Lock4j åº•层基于 Redisson                                                                                           | æ—                                                                                   |
| åˆ†å¸ƒå¼ä»»åŠ¡è°ƒåº¦     | é‡‡ç”¨ SnailJob å¤©ç”Ÿæ”¯æŒåˆ†å¸ƒå¼ ç»Ÿä¸€çš„管理中心 æ”¯æŒå¤šç§æ•°æ®åº“ æ”¯æŒåˆ†ç‰‡é‡è¯•DAG任务流等                                                                 | é‡‡ç”¨ Quartz åŸºäºŽæ•°æ®åº“锁性能差 é›†ç¾¤éœ€è¦åšå¾ˆå¤šé…ç½®ä¸Žæ”¹é€                                                    |
| æ–‡ä»¶å­˜å‚¨        | é‡‡ç”¨ Minio åˆ†å¸ƒå¼æ–‡ä»¶å­˜å‚¨ å¤©ç”Ÿæ”¯æŒå¤šæœºã€å¤šç¡¬ç›˜ã€å¤šåˆ†ç‰‡ã€å¤šå‰¯æœ¬å­˜å‚¨<br/>支持权限管理 å®‰å…¨å¯é  æ–‡ä»¶å¯åŠ å¯†å­˜å‚¨                                                     | é‡‡ç”¨ æœ¬æœºæ–‡ä»¶å­˜å‚¨ æ–‡ä»¶è£¸æ¼ æ˜“丢失泄漏 ä¸æ”¯æŒé›†ç¾¤æœ‰å•点效应                                                    |
| äº‘存储         | é‡‡ç”¨ AWS S3 åè®®å®¢æˆ·ç«¯ æ”¯æŒ ä¸ƒç‰›ã€é˜¿é‡Œã€è…¾è®¯ ç­‰ä¸€åˆ‡æ”¯æŒS3协议的厂家                                                                          | ä¸æ”¯æŒ                                                                                |
| çŸ­ä¿¡          | é‡‡ç”¨ sms4j çŸ­ä¿¡èžåˆåŒ… æ”¯æŒæ•°åç§çŸ­ä¿¡åނ家 åªéœ€åœ¨yml配置好厂家密钥即可使用 å¯å¤šåŽ‚å®¶å…±ç”¨                                                                 | ä¸æ”¯æŒ                                                                                |
| é‚®ä»¶          | é‡‡ç”¨ mail-api é€šç”¨åè®®æ”¯æŒå¤§éƒ¨åˆ†é‚®ä»¶åނ商                                                                                         | ä¸æ”¯æŒ                                                                                |
| æŽ¥å£æ–‡æ¡£        | é‡‡ç”¨ SpringDoc、javadoc æ— æ³¨è§£é›¶å…¥ä¾µåŸºäºŽjava注释<br/>只需把注释写好 æ— éœ€å†å†™ä¸€å¤§å †çš„æ–‡æ¡£æ³¨è§£äº†                                                     | é‡‡ç”¨ Springfox å·²åœæ­¢ç»´æŠ¤ éœ€è¦ç¼–写大量的注解来支持文档生成                                                |
| æ ¡éªŒæ¡†æž¶        | é‡‡ç”¨ Validation æ”¯æŒæ³¨è§£ä¸Žå·¥å…·ç±»æ ¡éªŒ æ³¨è§£æ”¯æŒå›½é™…化                                                                                  | ä»…支持注解 ä¸”注解不支持国际化                                                                    |
| Excel框架     | é‡‡ç”¨ FastExcel(原Alibaba EasyExcel) åŸºäºŽæ’件化<br/>框架对其增加了很多功能 ä¾‹å¦‚ è‡ªåŠ¨åˆå¹¶ç›¸åŒå†…å®¹ è‡ªåŠ¨æŽ’åˆ—å¸ƒå±€ å­—典翻译等                                   | åŸºäºŽ POI æ‰‹å†™å®žçް åŠŸèƒ½æœ‰é™ å¤æ‚ æ‰©å±•性差                                                           |
| å·¥ä½œæµæ”¯æŒ       | æ”¯æŒå„种复杂审批 è½¬åŠž å§”æ´¾ åŠ å‡ç­¾ ä¼šç­¾ æˆ–ç­¾ ç¥¨ç­¾ ç­‰åŠŸèƒ½                                                                                   | æ—                                                                                   |
| å·¥å…·ç±»æ¡†æž¶       | é‡‡ç”¨ Hutool、Lombok ä¸Šç™¾ç§å·¥å…·è¦†ç›–90%的使用需求 åŸºäºŽæ³¨è§£è‡ªåŠ¨ç”Ÿæˆ get set ç­‰ç®€åŒ–框架大量代码                                                       | æ‰‹å†™å·¥å…·ç¨³å®šæ€§å·®æ˜“出问题 å·¥å…·æ•°é‡æœ‰é™ ä»£ç è‡ƒè‚¿éœ€è‡ªå·±æ‰‹å†™ get set ç­‰                                            |
| ç›‘控框架        | é‡‡ç”¨ SpringBoot-Admin åŸºäºŽSpringBoot官方 actuator æŽ¢é’ˆæœºåˆ¶<br/>实时监控服务状态 æ¡†æž¶è¿˜ä¸ºå…¶æ‰©å±•了在线日志查看监控                                    | æ—                                                                                   |
| é“¾è·¯è¿½è¸ª        | é‡‡ç”¨ Apache SkyWalking è¿˜åœ¨ä¸ºè¯·æ±‚不知道去哪了 åˆ°å“ªå‡ºäº†é—®é¢˜è€Œçƒ¦æ¼å—<br/>用了它即可实时查看请求经过的每一处每一个节点                                            | æ—                                                                                   |
| ä»£ç ç”Ÿæˆå™¨       | åªéœ€è®¾è®¡å¥½è¡¨ç»“æž„ ä¸€é”®ç”Ÿæˆæ‰€æœ‰crud代码与页面<br/>降低80%的开发量 æŠŠç²¾åŠ›éƒ½æŠ•å…¥åˆ°ä¸šåŠ¡è®¾è®¡ä¸Š<br/>框架为其适配MP、SpringDoc规范化代码 åŒæ—¶æ”¯æŒåŠ¨æ€å¤šæ•°æ®æºä»£ç ç”Ÿæˆ                    | ä»£ç ç”ŸæˆåŽŸç”Ÿç»“æž„ åªæ”¯æŒå•数据源生成                                                                 |
| éƒ¨ç½²æ–¹å¼        | æ”¯æŒ Docker ç¼–排 ä¸€é”®æ­å»ºæ‰€æœ‰çŽ¯å¢ƒ è®©å¼€å‘人员从此不再为搭建环境而烦恼                                                                           | åŽŸç”Ÿjar部署 å…¶ä»–环境需手动下载安装 è‡ªè¡Œæ­å»º                                                           |
| é¡¹ç›®è·¯å¾„修改      | æä¾›è¯¦ç»†çš„修改方案文档 å¹¶ä¸ºå…¶åšäº†ä¸€äº›æ”¹åЍ éžå¸¸ç®€å•即可修改成自己想要的                                                                              | éœ€è¦åšå¾ˆå¤šæ”¹é€  æ–‡æ¡£è¯´æ˜Žæœ‰é™                                                                     |
| å›½é™…化         | åŸºäºŽè¯·æ±‚头动态返回不同语种的文本内容 å¼€å‘难度低 æœ‰å¯¹åº”的工具类 æ”¯æŒå¤§éƒ¨åˆ†æ³¨è§£å†…容国际化                                                                     | åªæä¾›åŸºç¡€åŠŸèƒ½ å…¶ä»–需自行编写扩展                                                                  |
| ä»£ç å•例测试      | æä¾›å•例测试 ä½¿ç”¨æ–¹å¼ç¼–写方法与maven多环境单测插件                                                                                      | åªæä¾›åŸºç¡€åŠŸèƒ½ å…¶ä»–需自行编写扩展                                                                  |
| Demo案例      | æä¾›æ¡†æž¶åŠŸèƒ½çš„å®žé™…ä½¿ç”¨æ¡ˆä¾‹ å•独一个模块提供了很多很全                                                                                       | æ—                                                                                   |
## æœ¬æ¡†æž¶ä¸ŽRuoYi的业务差异
| ä¸šåŠ¡     | åŠŸèƒ½è¯´æ˜Ž                                                                 | æœ¬æ¡†æž¶ | RuoYi            |
|--------|----------------------------------------------------------------------|-----|------------------|
| ç§Ÿæˆ·ç®¡ç†   | ç³»ç»Ÿå†…租户的管理 å¦‚:租户套餐、过期时间、用户数量、企业信息等                                      | æ”¯æŒ  | æ—                 |
| ç§Ÿæˆ·å¥—餐管理 | ç³»ç»Ÿå†…租户所能使用的套餐管理 å¦‚:套餐内所包含的菜单等                                          | æ”¯æŒ  | æ—                 |
| å®¢æˆ·ç«¯ç®¡ç†  | ç³»ç»Ÿå†…对接的所有客户端管理 å¦‚: pc端、小程序端等<br>支持动态授权登录方式 å¦‚: çŸ­ä¿¡ç™»å½•、密码登录等 æ”¯æŒåŠ¨æ€æŽ§åˆ¶token时效 | æ”¯æŒ  | æ—                 |
| ç”¨æˆ·ç®¡ç†   | ç”¨æˆ·çš„管理配置 å¦‚:新增用户、分配用户所属部门、角色、岗位等                                       | æ”¯æŒ  | æ”¯æŒ               |
| éƒ¨é—¨ç®¡ç†   | é…ç½®ç³»ç»Ÿç»„织机构(公司、部门、小组) æ ‘结构展现支持数据权限                                       | æ”¯æŒ  | æ”¯æŒ               |
| å²—位管理   | é…ç½®ç³»ç»Ÿç”¨æˆ·æ‰€å±žæ‹…任职务                                                         | æ”¯æŒ  | æ”¯æŒ               |
| èœå•管理   | é…ç½®ç³»ç»Ÿèœå•、操作权限、按钮权限标识等                                                  | æ”¯æŒ  | æ”¯æŒ               |
| è§’色管理   | è§’色菜单权限分配、设置角色按机构进行数据范围权限划分                                           | æ”¯æŒ  | æ”¯æŒ               |
| å­—典管理   | å¯¹ç³»ç»Ÿä¸­ç»å¸¸ä½¿ç”¨çš„一些较为固定的数据进行维护                                               | æ”¯æŒ  | æ”¯æŒ               |
| å‚数管理   | å¯¹ç³»ç»ŸåŠ¨æ€é…ç½®å¸¸ç”¨å‚æ•°                                                          | æ”¯æŒ  | æ”¯æŒ               |
| é€šçŸ¥å…¬å‘Š   | ç³»ç»Ÿé€šçŸ¥å…¬å‘Šä¿¡æ¯å‘布维护                                                         | æ”¯æŒ  | æ”¯æŒ               |
| æ“ä½œæ—¥å¿—   | ç³»ç»Ÿæ­£å¸¸æ“ä½œæ—¥å¿—记录和查询 ç³»ç»Ÿå¼‚常信息日志记录和查询                                          | æ”¯æŒ  | æ”¯æŒ               |
| ç™»å½•日志   | ç³»ç»Ÿç™»å½•日志记录查询包含登录异常                                                     | æ”¯æŒ  | æ”¯æŒ               |
| æ–‡ä»¶ç®¡ç†   | ç³»ç»Ÿæ–‡ä»¶å±•示、上传、下载、删除等管理                                                   | æ”¯æŒ  | æ—                 |
| æ–‡ä»¶é…ç½®ç®¡ç† | ç³»ç»Ÿæ–‡ä»¶ä¸Šä¼ ã€ä¸‹è½½æ‰€éœ€è¦çš„配置信息动态添加、修改、删除等管理                                       | æ”¯æŒ  | æ—                 |
| åœ¨çº¿ç”¨æˆ·ç®¡ç† | å·²ç™»å½•系统的在线用户信息监控与强制踢出操作                                                | æ”¯æŒ  | æ”¯æŒ               |
| å®šæ—¶ä»»åŠ¡   | è¿è¡ŒæŠ¥è¡¨ã€ä»»åŠ¡ç®¡ç†(添加、修改、删除)、日志管理、执行器管理等                                      | æ”¯æŒ  | ä»…支持任务与日志管理       |
| ä»£ç ç”Ÿæˆ   | å¤šæ•°æ®æºå‰åŽç«¯ä»£ç çš„生成(java、html、xml、sql)支持CRUD下载                              | æ”¯æŒ  | ä»…支持单数据源          |
| ç³»ç»ŸæŽ¥å£   | æ ¹æ®ä¸šåŠ¡ä»£ç è‡ªåŠ¨ç”Ÿæˆç›¸å…³çš„api接口文档                                                 | æ”¯æŒ  | æ”¯æŒ               |
| æœåŠ¡ç›‘æŽ§   | ç›‘视集群系统CPU、内存、磁盘、堆栈、在线日志、Spring相关配置等                                  | æ”¯æŒ  | ä»…支持单机CPU、内存、磁盘监控 |
| ç¼“存监控   | å¯¹ç³»ç»Ÿçš„缓存信息查询,命令统计等。                                                    | æ”¯æŒ  | æ”¯æŒ               |
| ä½¿ç”¨æ¡ˆä¾‹   | ç³»ç»Ÿçš„一些功能案例                                                            | æ”¯æŒ  | ä¸æ”¯æŒ              |
## å‚考文档
使用框架前请仔细阅读文档重点注意事项
<br>
>[初始化项目 å¿…看](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://plus-doc.dromara.org/#/common/column)
>>[https://plus-doc.dromara.org/#/common/column](https://plus-doc.dromara.org/#/common/column)
>
>[部署项目 å¿…看](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)
>
>[如何加群](https://plus-doc.dromara.org/#/common/add_group)
>>[https://plus-doc.dromara.org/#/common/add_group](https://plus-doc.dromara.org/#/common/add_group)
>
>[参考文档 Wiki](https://plus-doc.dromara.org)
>>[https://plus-doc.dromara.org](https://plus-doc.dromara.org)
## è½¯ä»¶æž¶æž„图
![Plus部署架构图](https://foruda.gitee.com/images/1678981882624240692/ae2a3f3e_1766278.png "Plus部署架构图.png")
## å¦‚何参与贡献
[参与贡献的方式 https://plus-doc.dromara.org/#/common/contribution](https://plus-doc.dromara.org/#/common/contribution)
## æçŒ®ä½œè€…
作者为兼职做开源,平时还需要工作,如果帮到了您可以请作者吃个盒饭
<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" />
## æ¼”示图例
|                                                                                            |                                                                                            |
|--------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------|
| ![输入图片说明](https://foruda.gitee.com/images/1680077524361362822/270bb429_1766278.png "屏幕截图") | ![输入图片说明](https://foruda.gitee.com/images/1680077619939771291/989bf9b6_1766278.png "屏幕截图") |
| ![输入图片说明](https://foruda.gitee.com/images/1680077681751513929/1c27c5bd_1766278.png "屏幕截图") | ![输入图片说明](https://foruda.gitee.com/images/1680077721559267315/74d63e23_1766278.png "屏幕截图") |
| ![输入图片说明](https://foruda.gitee.com/images/1680077765638904515/1b75d4a6_1766278.png "屏幕截图") | ![输入图片说明](https://foruda.gitee.com/images/1680078026375951297/eded7a4b_1766278.png "屏幕截图") |
| ![输入图片说明](https://foruda.gitee.com/images/1680078237104531207/0eb1b6a7_1766278.png "屏幕截图") | ![输入图片说明](https://foruda.gitee.com/images/1680078254306078709/5931e22f_1766278.png "屏幕截图") |
| ![输入图片说明](https://foruda.gitee.com/images/1680078287971528493/0b9af60a_1766278.png "屏幕截图") | ![输入图片说明](https://foruda.gitee.com/images/1680078308138770249/8d3b6696_1766278.png "屏幕截图") |
| ![输入图片说明](https://foruda.gitee.com/images/1680078352553634393/db5ef880_1766278.png "屏幕截图") | ![输入图片说明](https://foruda.gitee.com/images/1680078378238393374/601e4357_1766278.png "屏幕截图") |
| ![输入图片说明](https://foruda.gitee.com/images/1680078414983206024/2aae27c1_1766278.png "屏幕截图") | ![输入图片说明](https://foruda.gitee.com/images/1680078446738419874/ecce7d59_1766278.png "屏幕截图") |
| ![输入图片说明](https://foruda.gitee.com/images/1680078475971341775/149e8634_1766278.png "屏幕截图") | ![输入图片说明](https://foruda.gitee.com/images/1680078491666717143/3fadece7_1766278.png "屏幕截图") |
| ![输入图片说明](https://foruda.gitee.com/images/1680078558863188826/fb8ced2a_1766278.png "屏幕截图") | ![输入图片说明](https://foruda.gitee.com/images/1680078574561685461/ae68a0b2_1766278.png "屏幕截图") |
| ![输入图片说明](https://foruda.gitee.com/images/1680078594932772013/9d8bfec6_1766278.png "屏幕截图") | ![输入图片说明](https://foruda.gitee.com/images/1680078626493093532/fcfe4ff6_1766278.png "屏幕截图") |
| ![输入图片说明](https://foruda.gitee.com/images/1680078643608812515/0295bd4f_1766278.png "屏幕截图") | ![输入图片说明](https://foruda.gitee.com/images/1680078685196286463/d7612c81_1766278.png "屏幕截图") |
| ![输入图片说明](https://foruda.gitee.com/images/1680078703877318597/56fce0bc_1766278.png "屏幕截图") | ![输入图片说明](https://foruda.gitee.com/images/1680078716586545643/b6dbd68f_1766278.png "屏幕截图") |
| ![输入图片说明](https://foruda.gitee.com/images/1680078734103217688/eb1e6aa6_1766278.png "屏幕截图") | ![输入图片说明](https://foruda.gitee.com/images/1680078759131415480/73c525d8_1766278.png "屏幕截图") |
| ![输入图片说明](https://foruda.gitee.com/images/1680078779416197879/75e3ed02_1766278.png "屏幕截图") | ![输入图片说明](https://foruda.gitee.com/images/1680078802329118061/77e10915_1766278.png "屏幕截图") |
| ![输入图片说明](https://foruda.gitee.com/images/1680078893627848351/34a1c342_1766278.png "屏幕截图") | ![输入图片说明](https://foruda.gitee.com/images/1680078928175016986/f126ec4a_1766278.png "屏幕截图") |
| ![输入图片说明](https://foruda.gitee.com/images/1680078941718318363/b68a0f72_1766278.png "屏幕截图") | ![输入图片说明](https://foruda.gitee.com/images/1680078963175518631/3bb769a1_1766278.png "屏幕截图") |
| ![输入图片说明](https://foruda.gitee.com/images/1735829153637063344/3c21fd4c_1419627.png "屏幕截图") | ![输入图片说明](https://foruda.gitee.com/images/1735829181303499815/4522cefa_1419627.png "屏幕截图") |
| ![输入图片说明](https://foruda.gitee.com/images/1735829377205259767/76a705d7_1419627.png "屏幕截图") | ![输入图片说明](https://foruda.gitee.com/images/1722959592856812900/e2d0d342_1419627.png "屏幕截图") |
| ![输入图片说明](https://foruda.gitee.com/images/1680079274333484664/4dfdc7c0_1766278.png "屏幕截图") | ![输入图片说明](https://foruda.gitee.com/images/1680079290467458224/d6715fcf_1766278.png "屏幕截图") |
RuoYi-Vue-Plus/pom.xml
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,506 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>org.dromara</groupId>
    <artifactId>ruoyi-vue-plus</artifactId>
    <version>${revision}</version>
    <name>RuoYi-Vue-Plus</name>
    <url>https://gitee.com/dromara/RuoYi-Vue-Plus</url>
    <description>Dromara RuoYi-Vue-Plus多租户管理系统</description>
    <properties>
        <revision>5.5.3</revision>
        <spring-boot.version>3.5.10</spring-boot.version>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>17</java.version>
        <mybatis.version>3.5.19</mybatis.version>
        <springdoc.version>2.8.15</springdoc.version>
        <therapi-javadoc.version>0.15.0</therapi-javadoc.version>
        <fastexcel.version>1.3.0</fastexcel.version>
        <velocity.version>2.3</velocity.version>
        <satoken.version>1.44.0</satoken.version>
        <mybatis-plus.version>3.5.16</mybatis-plus.version>
        <p6spy.version>3.9.1</p6spy.version>
        <hutool.version>5.8.43</hutool.version>
        <spring-boot-admin.version>3.5.6</spring-boot-admin.version>
        <redisson.version>3.52.0</redisson.version>
        <lock4j.version>2.2.7</lock4j.version>
        <dynamic-ds.version>4.3.1</dynamic-ds.version>
        <snailjob.version>1.9.0</snailjob.version>
        <mapstruct-plus.version>1.5.0</mapstruct-plus.version>
        <mapstruct-plus.lombok.version>0.2.0</mapstruct-plus.lombok.version>
        <lombok.version>1.18.42</lombok.version>
        <bouncycastle.version>1.80</bouncycastle.version>
        <justauth.version>1.16.7</justauth.version>
        <!-- ç¦»çº¿IP地址定位库 -->
        <ip2region.version>3.3.2</ip2region.version>
        <!-- OSS é…ç½® -->
        <aws.sdk.version>2.28.22</aws.sdk.version>
        <!-- SMS é…ç½® -->
        <sms4j.version>3.3.5</sms4j.version>
        <!-- é™åˆ¶æ¡†æž¶ä¸­çš„fastjson版本 -->
        <fastjson.version>1.2.83</fastjson.version>
        <!-- é¢å‘运行时的D-ORM依赖 -->
        <anyline.version>8.7.3-20251210</anyline.version>
        <!-- å·¥ä½œæµé…ç½® -->
        <warm-flow.version>1.8.4</warm-flow.version>
        <!-- æ’件版本 -->
        <maven-jar-plugin.version>3.4.2</maven-jar-plugin.version>
        <maven-war-plugin.version>3.4.0</maven-war-plugin.version>
        <maven-compiler-plugin.version>3.14.0</maven-compiler-plugin.version>
        <maven-surefire-plugin.version>3.5.3</maven-surefire-plugin.version>
        <flatten-maven-plugin.version>1.3.0</flatten-maven-plugin.version>
        <!-- æ‰“包默认跳过测试 -->
        <skipTests>true</skipTests>
    </properties>
    <profiles>
        <profile>
            <id>local</id>
            <properties>
                <!-- çŽ¯å¢ƒæ ‡è¯†ï¼Œéœ€è¦ä¸Žé…ç½®æ–‡ä»¶çš„åç§°ç›¸å¯¹åº” -->
                <profiles.active>local</profiles.active>
                <logging.level>info</logging.level>
                <monitor.username>ruoyi</monitor.username>
                <monitor.password>123456</monitor.password>
            </properties>
        </profile>
        <profile>
            <id>dev</id>
            <properties>
                <!-- çŽ¯å¢ƒæ ‡è¯†ï¼Œéœ€è¦ä¸Žé…ç½®æ–‡ä»¶çš„åç§°ç›¸å¯¹åº” -->
                <profiles.active>dev</profiles.active>
                <logging.level>info</logging.level>
                <monitor.username>ruoyi</monitor.username>
                <monitor.password>123456</monitor.password>
            </properties>
            <activation>
                <!-- é»˜è®¤çŽ¯å¢ƒ -->
                <activeByDefault>true</activeByDefault>
            </activation>
        </profile>
        <profile>
            <id>prod</id>
            <properties>
                <profiles.active>prod</profiles.active>
                <logging.level>warn</logging.level>
                <monitor.username>ruoyi</monitor.username>
                <monitor.password>123456</monitor.password>
            </properties>
        </profile>
    </profiles>
    <!-- ä¾èµ–声明 -->
    <dependencyManagement>
        <dependencies>
            <!-- SpringBoot的依赖配置-->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>${spring-boot.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <!-- hutool çš„依赖配置-->
            <dependency>
                <groupId>cn.hutool</groupId>
                <artifactId>hutool-bom</artifactId>
                <version>${hutool.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <!-- common çš„依赖配置-->
            <dependency>
                <groupId>org.dromara</groupId>
                <artifactId>ruoyi-common-bom</artifactId>
                <version>${revision}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <dependency>
                <groupId>org.springdoc</groupId>
                <artifactId>springdoc-openapi-starter-webmvc-api</artifactId>
                <version>${springdoc.version}</version>
            </dependency>
            <dependency>
                <groupId>com.github.therapi</groupId>
                <artifactId>therapi-runtime-javadoc</artifactId>
                <version>${therapi-javadoc.version}</version>
            </dependency>
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <version>${lombok.version}</version>
            </dependency>
            <dependency>
                <groupId>cn.idev.excel</groupId>
                <artifactId>fastexcel</artifactId>
                <version>${fastexcel.version}</version>
            </dependency>
            <!-- velocity代码生成使用模板 -->
            <dependency>
                <groupId>org.apache.velocity</groupId>
                <artifactId>velocity-engine-core</artifactId>
                <version>${velocity.version}</version>
            </dependency>
            <!-- Sa-Token æƒé™è®¤è¯, åœ¨çº¿æ–‡æ¡£ï¼šhttp://sa-token.dev33.cn/ -->
            <dependency>
                <groupId>cn.dev33</groupId>
                <artifactId>sa-token-spring-boot3-starter</artifactId>
                <version>${satoken.version}</version>
            </dependency>
            <!-- Sa-Token æ•´åˆ jwt -->
            <dependency>
                <groupId>cn.dev33</groupId>
                <artifactId>sa-token-jwt</artifactId>
                <version>${satoken.version}</version>
                <exclusions>
                    <exclusion>
                        <groupId>cn.hutool</groupId>
                        <artifactId>hutool-all</artifactId>
                    </exclusion>
                </exclusions>
            </dependency>
            <dependency>
                <groupId>cn.dev33</groupId>
                <artifactId>sa-token-core</artifactId>
                <version>${satoken.version}</version>
            </dependency>
            <!-- dynamic-datasource å¤šæ•°æ®æº-->
            <dependency>
                <groupId>com.baomidou</groupId>
                <artifactId>dynamic-datasource-spring-boot3-starter</artifactId>
                <version>${dynamic-ds.version}</version>
            </dependency>
            <dependency>
                <groupId>org.mybatis</groupId>
                <artifactId>mybatis</artifactId>
                <version>${mybatis.version}</version>
            </dependency>
            <dependency>
                <groupId>com.baomidou</groupId>
                <artifactId>mybatis-plus-spring-boot3-starter</artifactId>
                <version>${mybatis-plus.version}</version>
            </dependency>
            <dependency>
                <groupId>com.baomidou</groupId>
                <artifactId>mybatis-plus-jsqlparser</artifactId>
                <version>${mybatis-plus.version}</version>
            </dependency>
            <dependency>
                <groupId>com.baomidou</groupId>
                <artifactId>mybatis-plus-annotation</artifactId>
                <version>${mybatis-plus.version}</version>
            </dependency>
            <!-- sql性能分析插件 -->
            <dependency>
                <groupId>p6spy</groupId>
                <artifactId>p6spy</artifactId>
                <version>${p6spy.version}</version>
            </dependency>
            <!--  AWS SDK for Java 2.x  -->
            <dependency>
                <groupId>software.amazon.awssdk</groupId>
                <artifactId>s3</artifactId>
                <version>${aws.sdk.version}</version>
            </dependency>
            <!-- å®¢æˆ·ç«¯çš„æ€§èƒ½å¢žå¼ºä¼ è¾“管理器 -->
            <dependency>
                <groupId>software.amazon.awssdk</groupId>
                <artifactId>s3-transfer-manager</artifactId>
                <version>${aws.sdk.version}</version>
            </dependency>
            <!-- é€‚用于 Netty çš„客户端 -->
            <dependency>
                <groupId>software.amazon.awssdk</groupId>
                <artifactId>netty-nio-client</artifactId>
                <version>${aws.sdk.version}</version>
            </dependency>
            <!--短信sms4j-->
            <dependency>
                <groupId>org.dromara.sms4j</groupId>
                <artifactId>sms4j-spring-boot-starter</artifactId>
                <version>${sms4j.version}</version>
            </dependency>
            <dependency>
                <groupId>de.codecentric</groupId>
                <artifactId>spring-boot-admin-starter-server</artifactId>
                <version>${spring-boot-admin.version}</version>
            </dependency>
            <dependency>
                <groupId>de.codecentric</groupId>
                <artifactId>spring-boot-admin-starter-client</artifactId>
                <version>${spring-boot-admin.version}</version>
            </dependency>
            <!--redisson-->
            <dependency>
                <groupId>org.redisson</groupId>
                <artifactId>redisson-spring-boot-starter</artifactId>
                <version>${redisson.version}</version>
            </dependency>
            <dependency>
                <groupId>com.baomidou</groupId>
                <artifactId>lock4j-redisson-spring-boot-starter</artifactId>
                <version>${lock4j.version}</version>
            </dependency>
            <!-- SnailJob Client -->
            <dependency>
                <groupId>com.aizuda</groupId>
                <artifactId>snail-job-client-starter</artifactId>
                <version>${snailjob.version}</version>
            </dependency>
            <dependency>
                <groupId>com.aizuda</groupId>
                <artifactId>snail-job-client-job-core</artifactId>
                <version>${snailjob.version}</version>
            </dependency>
            <!-- åŠ å¯†åŒ…å¼•å…¥ -->
            <dependency>
                <groupId>org.bouncycastle</groupId>
                <artifactId>bcprov-jdk15to18</artifactId>
                <version>${bouncycastle.version}</version>
            </dependency>
            <dependency>
                <groupId>io.github.linpeilie</groupId>
                <artifactId>mapstruct-plus-spring-boot-starter</artifactId>
                <version>${mapstruct-plus.version}</version>
            </dependency>
            <!-- Warm-Flow国产工作流引擎, åœ¨çº¿æ–‡æ¡£ï¼šhttp://warm-flow.cn/ -->
            <dependency>
                <groupId>org.dromara.warm</groupId>
                <artifactId>warm-flow-mybatis-plus-sb3-starter</artifactId>
                <version>${warm-flow.version}</version>
            </dependency>
            <dependency>
                <groupId>org.dromara.warm</groupId>
                <artifactId>warm-flow-plugin-ui-sb-web</artifactId>
                <version>${warm-flow.version}</version>
            </dependency>
            <!-- JustAuth çš„依赖配置-->
            <dependency>
                <groupId>me.zhyd.oauth</groupId>
                <artifactId>JustAuth</artifactId>
                <version>${justauth.version}</version>
            </dependency>
            <!-- ç¦»çº¿IP地址定位库 ip2region -->
            <dependency>
                <groupId>org.lionsoul</groupId>
                <artifactId>ip2region</artifactId>
                <version>${ip2region.version}</version>
            </dependency>
            <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>fastjson</artifactId>
                <version>${fastjson.version}</version>
            </dependency>
            <dependency>
                <groupId>org.dromara</groupId>
                <artifactId>ruoyi-system</artifactId>
                <version>${revision}</version>
            </dependency>
            <dependency>
                <groupId>org.dromara</groupId>
                <artifactId>ruoyi-job</artifactId>
                <version>${revision}</version>
            </dependency>
            <dependency>
                <groupId>org.dromara</groupId>
                <artifactId>ruoyi-generator</artifactId>
                <version>${revision}</version>
            </dependency>
            <dependency>
                <groupId>org.dromara</groupId>
                <artifactId>ruoyi-demo</artifactId>
                <version>${revision}</version>
            </dependency>
            <dependency>
                <groupId>org.dromara</groupId>
                <artifactId>ruoyi-qa</artifactId>
                <version>${revision}</version>
            </dependency>
            <!--  å·¥ä½œæµæ¨¡å—  -->
            <dependency>
                <groupId>org.dromara</groupId>
                <artifactId>ruoyi-workflow</artifactId>
                <version>${revision}</version>
            </dependency>
        </dependencies>
    </dependencyManagement>
    <modules>
        <module>ruoyi-admin</module>
        <module>ruoyi-common</module>
        <module>ruoyi-extend</module>
        <module>ruoyi-modules</module>
    </modules>
    <packaging>pom</packaging>
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>${maven-compiler-plugin.version}</version>
                <configuration>
                    <source>${java.version}</source>
                    <target>${java.version}</target>
                    <encoding>${project.build.sourceEncoding}</encoding>
                    <annotationProcessorPaths>
                        <path>
                            <groupId>com.github.therapi</groupId>
                            <artifactId>therapi-runtime-javadoc-scribe</artifactId>
                            <version>${therapi-javadoc.version}</version>
                        </path>
                        <path>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                            <version>${lombok.version}</version>
                        </path>
                        <path>
                            <groupId>org.springframework.boot</groupId>
                            <artifactId>spring-boot-configuration-processor</artifactId>
                            <version>${spring-boot.version}</version>
                        </path>
                        <path>
                            <groupId>io.github.linpeilie</groupId>
                            <artifactId>mapstruct-plus-processor</artifactId>
                            <version>${mapstruct-plus.version}</version>
                        </path>
                        <path>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok-mapstruct-binding</artifactId>
                            <version>${mapstruct-plus.lombok.version}</version>
                        </path>
                    </annotationProcessorPaths>
                    <compilerArgs>
                        <arg>-parameters</arg>
                    </compilerArgs>
                </configuration>
            </plugin>
            <!-- å•元测试使用 -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-surefire-plugin</artifactId>
                <version>${maven-surefire-plugin.version}</version>
                <configuration>
                    <argLine>-Dfile.encoding=UTF-8</argLine>
                    <!-- æ ¹æ®æ‰“包环境执行对应的@Tag测试方法 -->
                    <groups>${profiles.active}</groups>
                    <!-- æŽ’除标签 -->
                    <excludedGroups>exclude</excludedGroups>
                </configuration>
            </plugin>
            <!-- ç»Ÿä¸€ç‰ˆæœ¬å·ç®¡ç† -->
            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>flatten-maven-plugin</artifactId>
                <version>${flatten-maven-plugin.version}</version>
                <configuration>
                    <updatePomFile>true</updatePomFile>
                    <flattenMode>resolveCiFriendliesOnly</flattenMode>
                </configuration>
                <executions>
                    <execution>
                        <id>flatten</id>
                        <phase>process-resources</phase>
                        <goals>
                            <goal>flatten</goal>
                        </goals>
                    </execution>
                    <execution>
                        <id>flatten.clean</id>
                        <phase>clean</phase>
                        <goals>
                            <goal>clean</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
        <resources>
            <resource>
                <directory>src/main/resources</directory>
                <!-- å…³é—­è¿‡æ»¤ -->
                <filtering>false</filtering>
            </resource>
            <resource>
                <directory>src/main/resources</directory>
                <!-- å¼•入所有 åŒ¹é…æ–‡ä»¶è¿›è¡Œè¿‡æ»¤ -->
                <includes>
                    <include>application*</include>
                    <include>bootstrap*</include>
                    <include>banner*</include>
                </includes>
                <!-- å¯ç”¨è¿‡æ»¤ å³è¯¥èµ„源中的变量将会被过滤器中的值替换 -->
                <filtering>true</filtering>
            </resource>
        </resources>
    </build>
    <repositories>
        <repository>
            <id>public</id>
            <name>huawei nexus</name>
            <url>https://mirrors.huaweicloud.com/repository/maven/</url>
            <releases>
                <enabled>true</enabled>
            </releases>
        </repository>
    </repositories>
    <pluginRepositories>
        <pluginRepository>
            <id>public</id>
            <name>huawei nexus</name>
            <url>https://mirrors.huaweicloud.com/repository/maven/</url>
            <releases>
                <enabled>true</enabled>
            </releases>
            <snapshots>
                <enabled>false</enabled>
            </snapshots>
        </pluginRepository>
    </pluginRepositories>
</project>
RuoYi-Vue-Plus/ruoyi-admin/.DS_Store
Binary files differ
RuoYi-Vue-Plus/ruoyi-admin/.flattened-pom.xml
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,98 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <modelVersion>4.0.0</modelVersion>
  <parent>
    <groupId>org.dromara</groupId>
    <artifactId>ruoyi-vue-plus</artifactId>
    <version>5.5.3</version>
  </parent>
  <groupId>org.dromara</groupId>
  <artifactId>ruoyi-admin</artifactId>
  <version>5.5.3</version>
  <description>web服务入口</description>
  <dependencies>
    <dependency>
      <groupId>org.postgresql</groupId>
      <artifactId>postgresql</artifactId>
    </dependency>
    <dependency>
      <groupId>org.dromara</groupId>
      <artifactId>ruoyi-common-doc</artifactId>
    </dependency>
    <dependency>
      <groupId>org.dromara</groupId>
      <artifactId>ruoyi-common-social</artifactId>
    </dependency>
    <dependency>
      <groupId>org.dromara</groupId>
      <artifactId>ruoyi-common-ratelimiter</artifactId>
    </dependency>
    <dependency>
      <groupId>org.dromara</groupId>
      <artifactId>ruoyi-common-mail</artifactId>
    </dependency>
    <dependency>
      <groupId>org.dromara</groupId>
      <artifactId>ruoyi-system</artifactId>
    </dependency>
    <dependency>
      <groupId>org.dromara</groupId>
      <artifactId>ruoyi-job</artifactId>
    </dependency>
    <dependency>
      <groupId>org.dromara</groupId>
      <artifactId>ruoyi-generator</artifactId>
    </dependency>
    <dependency>
      <groupId>org.dromara</groupId>
      <artifactId>ruoyi-demo</artifactId>
    </dependency>
    <dependency>
      <groupId>org.dromara</groupId>
      <artifactId>ruoyi-qa</artifactId>
    </dependency>
    <dependency>
      <groupId>org.dromara</groupId>
      <artifactId>ruoyi-workflow</artifactId>
    </dependency>
    <dependency>
      <groupId>de.codecentric</groupId>
      <artifactId>spring-boot-admin-starter-client</artifactId>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-test</artifactId>
      <scope>test</scope>
    </dependency>
  </dependencies>
  <build>
    <finalName>${project.artifactId}</finalName>
    <plugins>
      <plugin>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-maven-plugin</artifactId>
        <version>${spring-boot.version}</version>
        <executions>
          <execution>
            <goals>
              <goal>repackage</goal>
            </goals>
          </execution>
        </executions>
      </plugin>
      <plugin>
        <artifactId>maven-jar-plugin</artifactId>
        <version>${maven-jar-plugin.version}</version>
      </plugin>
      <plugin>
        <artifactId>maven-war-plugin</artifactId>
        <version>${maven-war-plugin.version}</version>
        <configuration>
          <failOnMissingWebXml>false</failOnMissingWebXml>
          <warName>${project.artifactId}</warName>
        </configuration>
      </plugin>
    </plugins>
  </build>
</project>
RuoYi-Vue-Plus/ruoyi-admin/Dockerfile
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,31 @@
# è´å°”实验室 Spring å®˜æ–¹æŽ¨èé•œåƒ JDK下载地址 https://bell-sw.com/pages/downloads/
FROM bellsoft/liberica-openjdk-rocky:17.0.16-cds
#FROM bellsoft/liberica-openjdk-rocky:21.0.8-cds
#FROM findepi/graalvm:java17-native
LABEL maintainer="Lion Li"
RUN mkdir -p /ruoyi/server/logs \
    /ruoyi/server/temp \
    /ruoyi/skywalking/agent
WORKDIR /ruoyi/server
ENV SERVER_PORT=8080 SNAIL_PORT=28080 LANG=C.UTF-8 LC_ALL=C.UTF-8 JAVA_OPTS=""
EXPOSE ${SERVER_PORT}
# æš´éœ² snail job å®¢æˆ·ç«¯ç«¯å£ ç”¨äºŽå®šæ—¶ä»»åŠ¡è°ƒåº¦ä¸­å¿ƒé€šä¿¡
EXPOSE ${SNAIL_PORT}
ADD ./target/ruoyi-admin.jar ./app.jar
SHELL ["/bin/bash", "-c"]
ENTRYPOINT java -Djava.security.egd=file:/dev/./urandom -Dserver.port=${SERVER_PORT} \
           -Dsnail-job.port=${SNAIL_PORT} \
           # åº”用名称 å¦‚果想区分集群节点监控 æ”¹æˆä¸åŒçš„名称即可
           #-Dskywalking.agent.service_name=ruoyi-server \
           #-javaagent:/ruoyi/skywalking/agent/skywalking-agent.jar \
           -XX:+HeapDumpOnOutOfMemoryError -XX:+UseZGC ${JAVA_OPTS} \
           -jar app.jar
RuoYi-Vue-Plus/ruoyi-admin/pom.xml
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,159 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>ruoyi-vue-plus</artifactId>
        <groupId>org.dromara</groupId>
        <version>${revision}</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>
    <packaging>jar</packaging>
    <artifactId>ruoyi-admin</artifactId>
    <description>
        web服务入口
    </description>
    <dependencies>
        <!-- Mysql驱动包 -->
<!--        <dependency>-->
<!--            <groupId>com.mysql</groupId>-->
<!--            <artifactId>mysql-connector-j</artifactId>-->
<!--        </dependency>-->
<!--        &lt;!&ndash; mp支持的数据库均支持 åªéœ€è¦å¢žåŠ å¯¹åº”çš„jdbc依赖即可 &ndash;&gt;-->
<!--        &lt;!&ndash; Oracle &ndash;&gt;-->
<!--        <dependency>-->
<!--            <groupId>com.oracle.database.jdbc</groupId>-->
<!--            <artifactId>ojdbc8</artifactId>-->
<!--        </dependency>-->
<!--        &lt;!&ndash; å…¼å®¹oracle低版本 &ndash;&gt;-->
<!--        <dependency>-->
<!--            <groupId>com.oracle.database.nls</groupId>-->
<!--            <artifactId>orai18n</artifactId>-->
<!--        </dependency>-->
        <!-- PostgreSql -->
        <dependency>
            <groupId>org.postgresql</groupId>
            <artifactId>postgresql</artifactId>
        </dependency>
<!--        &lt;!&ndash; SqlServer &ndash;&gt;-->
<!--        <dependency>-->
<!--            <groupId>com.microsoft.sqlserver</groupId>-->
<!--            <artifactId>mssql-jdbc</artifactId>-->
<!--        </dependency>-->
        <dependency>
            <groupId>org.dromara</groupId>
            <artifactId>ruoyi-common-doc</artifactId>
        </dependency>
        <dependency>
            <groupId>org.dromara</groupId>
            <artifactId>ruoyi-common-social</artifactId>
        </dependency>
        <dependency>
            <groupId>org.dromara</groupId>
            <artifactId>ruoyi-common-ratelimiter</artifactId>
        </dependency>
        <dependency>
            <groupId>org.dromara</groupId>
            <artifactId>ruoyi-common-mail</artifactId>
        </dependency>
        <dependency>
            <groupId>org.dromara</groupId>
            <artifactId>ruoyi-system</artifactId>
        </dependency>
        <dependency>
            <groupId>org.dromara</groupId>
            <artifactId>ruoyi-job</artifactId>
        </dependency>
        <!-- ä»£ç ç”Ÿæˆ-->
        <dependency>
            <groupId>org.dromara</groupId>
            <artifactId>ruoyi-generator</artifactId>
        </dependency>
        <!--  demo模块  -->
        <dependency>
            <groupId>org.dromara</groupId>
            <artifactId>ruoyi-demo</artifactId>
        </dependency>
        <!--  è´¨é‡åˆ†æžæ¨¡å—  -->
        <dependency>
            <groupId>org.dromara</groupId>
            <artifactId>ruoyi-qa</artifactId>
        </dependency>
        <!--  å·¥ä½œæµæ¨¡å—  -->
        <dependency>
            <groupId>org.dromara</groupId>
            <artifactId>ruoyi-workflow</artifactId>
        </dependency>
        <dependency>
            <groupId>de.codecentric</groupId>
            <artifactId>spring-boot-admin-starter-client</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <!-- skywalking æ•´åˆ logback -->
<!--        <dependency>-->
<!--            <groupId>org.apache.skywalking</groupId>-->
<!--            <artifactId>apm-toolkit-logback-1.x</artifactId>-->
<!--            <version>${与你的agent探针版本保持一致}</version>-->
<!--        </dependency>-->
<!--        <dependency>-->
<!--            <groupId>org.apache.skywalking</groupId>-->
<!--            <artifactId>apm-toolkit-trace</artifactId>-->
<!--            <version>${与你的agent探针版本保持一致}</version>-->
<!--        </dependency>-->
    </dependencies>
    <build>
        <finalName>${project.artifactId}</finalName>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <version>${spring-boot.version}</version>
                <executions>
                    <execution>
                        <goals>
                            <goal>repackage</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-jar-plugin</artifactId>
                <version>${maven-jar-plugin.version}</version>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-war-plugin</artifactId>
                <version>${maven-war-plugin.version}</version>
                <configuration>
                    <failOnMissingWebXml>false</failOnMissingWebXml>
                    <warName>${project.artifactId}</warName>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>
RuoYi-Vue-Plus/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-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-Vue-Plus/ruoyi-admin/src/main/java/org/dromara/web/controller/AuthController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,243 @@
package org.dromara.web.controller;
import cn.dev33.satoken.annotation.SaIgnore;
import cn.dev33.satoken.exception.NotLoginException;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.codec.Base64;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import me.zhyd.oauth.model.AuthResponse;
import me.zhyd.oauth.model.AuthUser;
import me.zhyd.oauth.request.AuthRequest;
import me.zhyd.oauth.utils.AuthStateUtils;
import org.dromara.common.core.constant.SystemConstants;
import org.dromara.common.core.domain.R;
import org.dromara.common.core.domain.model.LoginBody;
import org.dromara.common.core.domain.model.RegisterBody;
import org.dromara.common.core.domain.model.SocialLoginBody;
import org.dromara.common.core.utils.*;
import org.dromara.common.encrypt.annotation.ApiEncrypt;
import org.dromara.common.json.utils.JsonUtils;
import org.dromara.common.ratelimiter.annotation.RateLimiter;
import org.dromara.common.ratelimiter.enums.LimitType;
import org.dromara.common.satoken.utils.LoginHelper;
import org.dromara.common.social.config.properties.SocialLoginConfigProperties;
import org.dromara.common.social.config.properties.SocialProperties;
import org.dromara.common.social.utils.SocialUtils;
import org.dromara.common.sse.dto.SseMessageDto;
import org.dromara.common.sse.utils.SseMessageUtils;
import org.dromara.common.tenant.helper.TenantHelper;
import org.dromara.system.domain.bo.SysTenantBo;
import org.dromara.system.domain.vo.SysClientVo;
import org.dromara.system.domain.vo.SysTenantVo;
import org.dromara.system.service.ISysClientService;
import org.dromara.system.service.ISysConfigService;
import org.dromara.system.service.ISysSocialService;
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.IAuthStrategy;
import org.dromara.web.service.SysLoginService;
import org.dromara.web.service.SysRegisterService;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
/**
 * è®¤è¯
 *
 * @author Lion Li
 */
@Slf4j
@SaIgnore
@RequiredArgsConstructor
@RestController
@RequestMapping("/auth")
public class AuthController {
    private final SocialProperties socialProperties;
    private final SysLoginService loginService;
    private final SysRegisterService registerService;
    private final ISysConfigService configService;
    private final ISysTenantService tenantService;
    private final ISysSocialService socialUserService;
    private final ISysClientService clientService;
    private final ScheduledExecutorService scheduledExecutorService;
    /**
     * ç™»å½•方法
     *
     * @param body ç™»å½•信息
     * @return ç»“æžœ
     */
    @ApiEncrypt
    @PostMapping("/login")
    public R<LoginVo> login(@RequestBody String body) {
        LoginBody loginBody = JsonUtils.parseObject(body, LoginBody.class);
        ValidatorUtils.validate(loginBody);
        // æŽˆæƒç±»åž‹å’Œå®¢æˆ·ç«¯id
        String clientId = loginBody.getClientId();
        String grantType = loginBody.getGrantType();
        SysClientVo client = clientService.queryByClientId(clientId);
        // æŸ¥è¯¢ä¸åˆ° client æˆ– client å†…不包含 grantType
        if (ObjectUtil.isNull(client) || !StringUtils.contains(client.getGrantType(), grantType)) {
            log.info("客户端id: {} è®¤è¯ç±»åž‹ï¼š{} å¼‚常!.", clientId, grantType);
            return R.fail(MessageUtils.message("auth.grant.type.error"));
        } else if (!SystemConstants.NORMAL.equals(client.getStatus())) {
            return R.fail(MessageUtils.message("auth.grant.type.blocked"));
        }
        // æ ¡éªŒç§Ÿæˆ·
        loginService.checkTenant(loginBody.getTenantId());
        // ç™»å½•
        LoginVo loginVo = IAuthStrategy.login(body, client, grantType);
        Long userId = LoginHelper.getUserId();
        scheduledExecutorService.schedule(() -> {
            SseMessageDto dto = new SseMessageDto();
            dto.setMessage(DateUtils.getTodayHour(new Date()) + "好,欢迎登录 è´¨é‡åˆ†æžç®¡ç†ç³»ç»Ÿ");
            dto.setUserIds(List.of(userId));
            SseMessageUtils.publishMessage(dto);
        }, 5, TimeUnit.SECONDS);
        return R.ok(loginVo);
    }
    /**
     * èŽ·å–è·³è½¬URL
     *
     * @param source ç™»å½•来源
     * @return ç»“æžœ
     */
    @GetMapping("/binding/{source}")
    public R<String> authBinding(@PathVariable("source") String source,
                                 @RequestParam String tenantId, @RequestParam String domain) {
        SocialLoginConfigProperties obj = socialProperties.getType().get(source);
        if (ObjectUtil.isNull(obj)) {
            return R.fail(source + "平台账号暂不支持");
        }
        AuthRequest authRequest = SocialUtils.getAuthRequest(source, socialProperties);
        Map<String, String> map = new HashMap<>();
        map.put("tenantId", tenantId);
        map.put("domain", domain);
        map.put("state", AuthStateUtils.createState());
        String authorizeUrl = authRequest.authorize(Base64.encode(JsonUtils.toJsonString(map), StandardCharsets.UTF_8));
        return R.ok("操作成功", authorizeUrl);
    }
    /**
     * å‰ç«¯å›žè°ƒç»‘定授权(需要token)
     *
     * @param loginBody è¯·æ±‚体
     * @return ç»“æžœ
     */
    @PostMapping("/social/callback")
    public R<Void> socialCallback(@RequestBody SocialLoginBody loginBody) {
        // æ ¡éªŒtoken
        StpUtil.checkLogin();
        // èŽ·å–ç¬¬ä¸‰æ–¹ç™»å½•ä¿¡æ¯
        AuthResponse<AuthUser> response = SocialUtils.loginAuth(
            loginBody.getSource(), loginBody.getSocialCode(),
            loginBody.getSocialState(), socialProperties);
        AuthUser authUserData = response.getData();
        // åˆ¤æ–­æŽˆæƒå“åº”是否成功
        if (!response.ok()) {
            return R.fail(response.getMsg());
        }
        loginService.socialRegister(authUserData);
        return R.ok();
    }
    /**
     * å–消授权(需要token)
     *
     * @param socialId socialId
     */
    @DeleteMapping(value = "/unlock/{socialId}")
    public R<Void> unlockSocial(@PathVariable Long socialId) {
        // æ ¡éªŒtoken
        StpUtil.checkLogin();
        Boolean rows = socialUserService.deleteWithValidById(socialId);
        return rows ? R.ok() : R.fail("取消授权失败");
    }
    /**
     * é€€å‡ºç™»å½•
     */
    @PostMapping("/logout")
    public R<Void> logout() {
        loginService.logout();
        return R.ok("退出成功");
    }
    /**
     * ç”¨æˆ·æ³¨å†Œ
     */
    @ApiEncrypt
    @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 ç§Ÿæˆ·åˆ—表
     */
    @RateLimiter(time = 60, count = 20, limitType = LimitType.IP)
    @GetMapping("/tenant/list")
    public R<LoginTenantVo> tenantList(HttpServletRequest request) throws Exception {
        // è¿”回对象
        LoginTenantVo result = new LoginTenantVo();
        boolean enable = TenantHelper.isEnable();
        result.setTenantEnabled(enable);
        // å¦‚果未开启租户这直接返回
        if (!enable) {
            return R.ok(result);
        }
        List<SysTenantVo> tenantList = tenantService.queryList(new SysTenantBo());
        List<TenantListVo> voList = MapstructUtils.convert(tenantList, TenantListVo.class);
        try {
            // å¦‚果只超管返回所有租户
            if (LoginHelper.isSuperAdmin()) {
                result.setVoList(voList);
                return R.ok(result);
            }
        } catch (NotLoginException ignored) {
        }
        // èŽ·å–åŸŸå
        String host;
        String referer = request.getHeader("referer");
        if (StringUtils.isNotBlank(referer)) {
            // è¿™é‡Œä»Žreferer中取值是为了本地使用hosts添加虚拟域名,方便本地环境调试
            host = referer.split("//")[1].split("/")[0];
        } else {
            host = new URL(request.getRequestURL().toString()).getHost();
        }
        // æ ¹æ®åŸŸåè¿›è¡Œç­›é€‰
        List<TenantListVo> list = StreamUtils.filter(voList, vo ->
            StringUtils.equalsIgnoreCase(vo.getDomain(), host));
        result.setVoList(CollUtil.isNotEmpty(list) ? list : voList);
        return R.ok(result);
    }
}
RuoYi-Vue-Plus/ruoyi-admin/src/main/java/org/dromara/web/controller/CaptchaController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,160 @@
package org.dromara.web.controller;
import cn.dev33.satoken.annotation.SaIgnore;
import cn.hutool.captcha.generator.CodeGenerator;
import cn.hutool.captcha.generator.MathGenerator;
import cn.hutool.captcha.generator.RandomGenerator;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.RandomUtil;
import jakarta.validation.constraints.NotBlank;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
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.exception.ServiceException;
import org.dromara.common.core.utils.SpringUtils;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.mail.config.properties.MailProperties;
import org.dromara.common.mail.utils.MailUtils;
import org.dromara.common.ratelimiter.annotation.RateLimiter;
import org.dromara.common.ratelimiter.enums.LimitType;
import org.dromara.common.redis.utils.RedisUtils;
import org.dromara.common.web.core.WaveAndCircleCaptcha;
import org.dromara.common.web.config.properties.CaptchaProperties;
import org.dromara.sms4j.api.SmsBlend;
import org.dromara.sms4j.api.entity.SmsResponse;
import org.dromara.sms4j.core.factory.SmsFactory;
import org.dromara.web.domain.vo.CaptchaVo;
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.awt.*;
import java.time.Duration;
import java.util.LinkedHashMap;
/**
 * éªŒè¯ç æ“ä½œå¤„理
 *
 * @author Lion Li
 */
@SaIgnore
@Slf4j
@Validated
@RequiredArgsConstructor
@RestController
public class CaptchaController {
    private final CaptchaProperties captchaProperties;
    private final MailProperties mailProperties;
    /**
     * çŸ­ä¿¡éªŒè¯ç 
     *
     * @param phonenumber ç”¨æˆ·æ‰‹æœºå·
     */
    @RateLimiter(key = "#phonenumber", time = 60, count = 1)
    @GetMapping("/resource/sms/code")
    public R<Void> smsCode(@NotBlank(message = "{user.phonenumber.not.blank}") String phonenumber) {
        String key = GlobalConstants.CAPTCHA_CODE_KEY + phonenumber;
        String code = RandomUtil.randomNumbers(4);
        RedisUtils.setCacheObject(key, code, Duration.ofMinutes(Constants.CAPTCHA_EXPIRATION));
        // éªŒè¯ç æ¨¡æ¿id è‡ªè¡Œå¤„理 (查数据库或写死均可)
        String templateId = "";
        LinkedHashMap<String, String> map = new LinkedHashMap<>(1);
        map.put("code", code);
        SmsBlend smsBlend = SmsFactory.getSmsBlend("config1");
        SmsResponse smsResponse = smsBlend.sendMessage(phonenumber, templateId, map);
        if (!smsResponse.isSuccess()) {
            log.error("验证码短信发送异常 => {}", smsResponse);
            return R.fail(smsResponse.getData().toString());
        }
        return R.ok();
    }
    /**
     * é‚®ç®±éªŒè¯ç 
     *
     * @param email é‚®ç®±
     */
    @GetMapping("/resource/email/code")
    public R<Void> emailCode(@NotBlank(message = "{user.email.not.blank}") String email) {
        if (!mailProperties.getEnabled()) {
            return R.fail("当前系统没有开启邮箱功能!");
        }
        SpringUtils.getAopProxy(this).emailCodeImpl(email);
        return R.ok();
    }
    /**
     * é‚®ç®±éªŒè¯ç 
     * ç‹¬ç«‹æ–¹æ³•避免验证码关闭之后仍然走限流
     */
    @RateLimiter(key = "#email", time = 60, count = 1)
    public void emailCodeImpl(String email) {
        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());
            throw new ServiceException(e.getMessage());
        }
    }
    /**
     * ç”ŸæˆéªŒè¯ç 
     */
    @GetMapping("/auth/code")
    public R<CaptchaVo> getCode() {
        boolean captchaEnabled = captchaProperties.getEnable();
        if (!captchaEnabled) {
            CaptchaVo captchaVo = new CaptchaVo();
            captchaVo.setCaptchaEnabled(false);
            return R.ok(captchaVo);
        }
        return R.ok(SpringUtils.getAopProxy(this).getCodeImpl());
    }
    /**
     * ç”ŸæˆéªŒè¯ç 
     * ç‹¬ç«‹æ–¹æ³•避免验证码关闭之后仍然走限流
     */
    @RateLimiter(time = 60, count = 10, limitType = LimitType.IP)
    public CaptchaVo getCodeImpl() {
        // ä¿å­˜éªŒè¯ç ä¿¡æ¯
        String uuid = IdUtil.simpleUUID();
        String verifyKey = GlobalConstants.CAPTCHA_CODE_KEY + uuid;
        // ç”ŸæˆéªŒè¯ç 
        String captchaType = captchaProperties.getType();
        CodeGenerator codeGenerator;
        if ("math".equals(captchaType)) {
            codeGenerator = new MathGenerator(captchaProperties.getNumberLength(), false);
        } else {
            codeGenerator = new RandomGenerator(captchaProperties.getCharLength());
        }
        WaveAndCircleCaptcha captcha = new WaveAndCircleCaptcha(160, 60);
        // captcha.setBackground(Color.WHITE); // ä¸è®¾ç½®å°±æ˜¯é€æ˜Žåº•
        captcha.setFont(new Font("Arial", Font.BOLD, 45));
        captcha.setGenerator(codeGenerator);
        captcha.createCode();
        // å¦‚果是数学验证码,使用SpEL表达式处理验证码结果
        String code = captcha.getCode();
        if ("math".equals(captchaType)) {
            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 captchaVo = new CaptchaVo();
        captchaVo.setUuid(uuid);
        captchaVo.setImg(captcha.getImageBase64());
        return captchaVo;
    }
}
RuoYi-Vue-Plus/ruoyi-admin/src/main/java/org/dromara/web/controller/IndexController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,28 @@
package org.dromara.web.controller;
import cn.dev33.satoken.annotation.SaIgnore;
import lombok.RequiredArgsConstructor;
import org.dromara.common.core.utils.SpringUtils;
import org.dromara.common.core.utils.StringUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
/**
 * é¦–页
 *
 * @author Lion Li
 */
@SaIgnore
@RequiredArgsConstructor
@RestController
public class IndexController {
    /**
     * è®¿é—®é¦–页,提示语
     */
    @GetMapping("/")
    public String index() {
        return StringUtils.format("欢迎使用{}后台管理框架,请通过前端地址访问。", SpringUtils.getApplicationName());
    }
}
RuoYi-Vue-Plus/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-Vue-Plus/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-Vue-Plus/ruoyi-admin/src/main/java/org/dromara/web/domain/vo/LoginVo.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,54 @@
package org.dromara.web.domain.vo;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
/**
 * ç™»å½•验证信息
 *
 * @author Michelle.Chung
 */
@Data
public class LoginVo {
    /**
     * æŽˆæƒä»¤ç‰Œ
     */
    @JsonProperty("access_token")
    private String accessToken;
    /**
     * åˆ·æ–°ä»¤ç‰Œ
     */
    @JsonProperty("refresh_token")
    private String refreshToken;
    /**
     * æŽˆæƒä»¤ç‰Œ access_token çš„æœ‰æ•ˆæœŸ
     */
    @JsonProperty("expire_in")
    private Long expireIn;
    /**
     * åˆ·æ–°ä»¤ç‰Œ refresh_token çš„æœ‰æ•ˆæœŸ
     */
    @JsonProperty("refresh_expire_in")
    private Long refreshExpireIn;
    /**
     * åº”用id
     */
    @JsonProperty("client_id")
    private String clientId;
    /**
     * ä»¤ç‰Œæƒé™
     */
    private String scope;
    /**
     * ç”¨æˆ· openid
     */
    private String openid;
}
RuoYi-Vue-Plus/ruoyi-admin/src/main/java/org/dromara/web/domain/vo/TenantListVo.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,31 @@
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-Vue-Plus/ruoyi-admin/src/main/java/org/dromara/web/listener/UserActionListener.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,163 @@
package org.dromara.web.listener;
import cn.dev33.satoken.listener.SaTokenListener;
import cn.dev33.satoken.stp.StpUtil;
import cn.dev33.satoken.stp.parameter.SaLoginParameter;
import cn.hutool.core.convert.Convert;
import cn.hutool.http.useragent.UserAgent;
import cn.hutool.http.useragent.UserAgentUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.dromara.common.core.constant.CacheConstants;
import org.dromara.common.core.constant.Constants;
import org.dromara.common.core.domain.dto.UserOnlineDTO;
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.ip.AddressUtils;
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.helper.TenantHelper;
import org.dromara.web.service.SysLoginService;
import org.springframework.stereotype.Component;
import java.time.Duration;
/**
 * ç”¨æˆ·è¡Œä¸º ä¾¦å¬å™¨çš„实现
 *
 * @author Lion Li
 */
@RequiredArgsConstructor
@Component
@Slf4j
public class UserActionListener implements SaTokenListener {
    private final SysLoginService loginService;
    /**
     * æ¯æ¬¡ç™»å½•时触发
     */
    @Override
    public void doLogin(String loginType, Object loginId, String tokenValue, SaLoginParameter loginParameter) {
        UserAgent userAgent = UserAgentUtil.parse(ServletUtils.getRequest().getHeader("User-Agent"));
        String ip = ServletUtils.getClientIP();
        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);
        String username = (String) loginParameter.getExtra(LoginHelper.USER_NAME_KEY);
        String tenantId = (String) loginParameter.getExtra(LoginHelper.TENANT_KEY);
        dto.setUserName(username);
        dto.setClientKey((String) loginParameter.getExtra(LoginHelper.CLIENT_KEY));
        dto.setDeviceType(loginParameter.getDeviceType());
        dto.setDeptName((String) loginParameter.getExtra(LoginHelper.DEPT_NAME_KEY));
        TenantHelper.dynamic(tenantId, () -> {
            if(loginParameter.getTimeout() == -1) {
                RedisUtils.setCacheObject(CacheConstants.ONLINE_TOKEN_KEY + tokenValue, dto);
            } else {
                RedisUtils.setCacheObject(CacheConstants.ONLINE_TOKEN_KEY + tokenValue, dto, Duration.ofSeconds(loginParameter.getTimeout()));
            }
        });
        // è®°å½•登录日志
        LogininforEvent logininforEvent = new LogininforEvent();
        logininforEvent.setTenantId(tenantId);
        logininforEvent.setUsername(username);
        logininforEvent.setStatus(Constants.LOGIN_SUCCESS);
        logininforEvent.setMessage(MessageUtils.message("user.login.success"));
        logininforEvent.setRequest(ServletUtils.getRequest());
        SpringUtils.context().publishEvent(logininforEvent);
        // æ›´æ–°ç™»å½•信息
        loginService.recordLoginInfo((Long) loginParameter.getExtra(LoginHelper.USER_KEY), ip);
        log.info("user doLogin, userId:{}, token:{}", loginId, tokenValue);
    }
    /**
     * æ¯æ¬¡æ³¨é”€æ—¶è§¦å‘
     */
    @Override
    public void doLogout(String loginType, Object loginId, String tokenValue) {
        String tenantId = Convert.toStr(StpUtil.getExtra(tokenValue, LoginHelper.TENANT_KEY));
        TenantHelper.dynamic(tenantId, () -> {
            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) {
        String tenantId = Convert.toStr(StpUtil.getExtra(tokenValue, LoginHelper.TENANT_KEY));
        TenantHelper.dynamic(tenantId, () -> {
            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) {
        String tenantId = Convert.toStr(StpUtil.getExtra(tokenValue, LoginHelper.TENANT_KEY));
        TenantHelper.dynamic(tenantId, () -> {
            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 loginType, Object loginId, String tokenValue, long timeout) {
    }
}
RuoYi-Vue-Plus/ruoyi-admin/src/main/java/org/dromara/web/service/IAuthStrategy.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,46 @@
package org.dromara.web.service;
import org.dromara.common.core.exception.ServiceException;
import org.dromara.common.core.utils.SpringUtils;
import org.dromara.system.domain.SysClient;
import org.dromara.system.domain.vo.SysClientVo;
import org.dromara.web.domain.vo.LoginVo;
/**
 * æŽˆæƒç­–ç•¥
 *
 * @author Michelle.Chung
 */
public interface IAuthStrategy {
    String BASE_NAME = "AuthStrategy";
    /**
     * ç™»å½•
     *
     * @param body      ç™»å½•对象
     * @param client    æŽˆæƒç®¡ç†è§†å›¾å¯¹è±¡
     * @param grantType æŽˆæƒç±»åž‹
     * @return ç™»å½•验证信息
     */
    static LoginVo login(String body, SysClientVo client, String grantType) {
        // æŽˆæƒç±»åž‹å’Œå®¢æˆ·ç«¯id
        String beanName = grantType + BASE_NAME;
        if (!SpringUtils.containsBean(beanName)) {
            throw new ServiceException("授权类型不正确!");
        }
        IAuthStrategy instance = SpringUtils.getBean(beanName);
        return instance.login(body, client);
    }
    /**
     * ç™»å½•
     *
     * @param body   ç™»å½•对象
     * @param client æŽˆæƒç®¡ç†è§†å›¾å¯¹è±¡
     * @return ç™»å½•验证信息
     */
    LoginVo login(String body, SysClientVo client);
}
RuoYi-Vue-Plus/ruoyi-admin/src/main/java/org/dromara/web/service/SysLoginService.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,251 @@
package org.dromara.web.service;
import cn.dev33.satoken.exception.NotLoginException;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Opt;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.lock.annotation.Lock4j;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import me.zhyd.oauth.model.AuthUser;
import org.dromara.common.core.constant.CacheConstants;
import org.dromara.common.core.constant.Constants;
import org.dromara.common.core.constant.SystemConstants;
import org.dromara.common.core.constant.TenantConstants;
import org.dromara.common.core.domain.dto.PostDTO;
import org.dromara.common.core.domain.dto.RoleDTO;
import org.dromara.common.core.domain.model.LoginUser;
import org.dromara.common.core.enums.LoginType;
import org.dromara.common.core.exception.ServiceException;
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.mybatis.helper.DataPermissionHelper;
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.system.domain.SysUser;
import org.dromara.system.domain.bo.SysSocialBo;
import org.dromara.system.domain.vo.*;
import org.dromara.system.mapper.SysUserMapper;
import org.dromara.system.service.*;
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 {
    @Value("${user.password.maxRetryCount}")
    private Integer maxRetryCount;
    @Value("${user.password.lockTime}")
    private Integer lockTime;
    private final ISysTenantService tenantService;
    private final ISysPermissionService permissionService;
    private final ISysSocialService sysSocialService;
    private final ISysRoleService roleService;
    private final ISysDeptService deptService;
    private final ISysPostService postService;
    private final SysUserMapper userMapper;
    /**
     * ç»‘定第三方用户
     *
     * @param authUserData æŽˆæƒå“åº”实体
     */
    @Lock4j
    public void socialRegister(AuthUser authUserData) {
        String authId = authUserData.getSource() + authUserData.getUuid();
        // ç¬¬ä¸‰æ–¹ç”¨æˆ·ä¿¡æ¯
        SysSocialBo bo = BeanUtil.toBean(authUserData, SysSocialBo.class);
        BeanUtil.copyProperties(authUserData.getToken(), bo);
        Long userId = LoginHelper.getUserId();
        bo.setUserId(userId);
        bo.setAuthId(authId);
        bo.setOpenId(authUserData.getUuid());
        bo.setUserName(authUserData.getUsername());
        bo.setNickName(authUserData.getNickname());
        List<SysSocialVo> checkList = sysSocialService.selectByAuthId(authId);
        if (CollUtil.isNotEmpty(checkList)) {
            throw new ServiceException("此三方账号已经被绑定!");
        }
        // æŸ¥è¯¢æ˜¯å¦å·²ç»ç»‘定用户
        SysSocialBo params = new SysSocialBo();
        params.setUserId(userId);
        params.setSource(bo.getSource());
        List<SysSocialVo> list = sysSocialService.queryList(params);
        if (CollUtil.isEmpty(list)) {
            // æ²¡æœ‰ç»‘定用户, æ–°å¢žç”¨æˆ·ä¿¡æ¯
            sysSocialService.insertByBo(bo);
        } else {
            // æ›´æ–°ç”¨æˆ·ä¿¡æ¯
            bo.setId(list.get(0).getId());
            sysSocialService.updateByBo(bo);
            // å¦‚果要绑定的平台账号已经被绑定过了 æ˜¯å¦æŠ›å¼‚常自行决断
            // throw new ServiceException("此平台账号已经被绑定!");
        }
    }
    /**
     * é€€å‡ºç™»å½•
     */
    public void logout() {
        try {
            LoginUser loginUser = LoginHelper.getLoginUser();
            if (ObjectUtil.isNull(loginUser)) {
                return;
            }
            if (TenantHelper.isEnable() && LoginHelper.isSuperAdmin()) {
                // è¶…级管理员 ç™»å‡ºæ¸…除动态租户
                TenantHelper.clearDynamic();
            }
            recordLogininfor(loginUser.getTenantId(), loginUser.getUsername(), Constants.LOGOUT, MessageUtils.message("user.logout.success"));
        } catch (NotLoginException ignored) {
        } finally {
            try {
                StpUtil.logout();
            } catch (NotLoginException ignored) {
            }
        }
    }
    /**
     * è®°å½•登录信息
     *
     * @param tenantId ç§Ÿæˆ·ID
     * @param username ç”¨æˆ·å
     * @param status   çŠ¶æ€
     * @param message  æ¶ˆæ¯å†…容
     */
    public 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);
    }
    /**
     * æž„建登录用户
     */
    public LoginUser buildLoginUser(SysUserVo user) {
        LoginUser loginUser = new LoginUser();
        Long userId = user.getUserId();
        loginUser.setTenantId(user.getTenantId());
        loginUser.setUserId(userId);
        loginUser.setDeptId(user.getDeptId());
        loginUser.setUsername(user.getUserName());
        loginUser.setNickname(user.getNickName());
        loginUser.setUserType(user.getUserType());
        loginUser.setMenuPermission(permissionService.getMenuPermission(userId));
        loginUser.setRolePermission(permissionService.getRolePermission(userId));
        if (ObjectUtil.isNotNull(user.getDeptId())) {
            Opt<SysDeptVo> deptOpt = Opt.of(user.getDeptId()).map(deptService::selectDeptById);
            loginUser.setDeptName(deptOpt.map(SysDeptVo::getDeptName).orElse(StringUtils.EMPTY));
            loginUser.setDeptCategory(deptOpt.map(SysDeptVo::getDeptCategory).orElse(StringUtils.EMPTY));
        }
        List<SysRoleVo> roles = roleService.selectRolesByUserId(userId);
        List<SysPostVo> posts = postService.selectPostsByUserId(userId);
        loginUser.setRoles(BeanUtil.copyToList(roles, RoleDTO.class));
        loginUser.setPosts(BeanUtil.copyToList(posts, PostDTO.class));
        return loginUser;
    }
    /**
     * è®°å½•登录信息
     *
     * @param userId ç”¨æˆ·ID
     */
    public void recordLoginInfo(Long userId, String ip) {
        SysUser sysUser = new SysUser();
        sysUser.setUserId(userId);
        sysUser.setLoginIp(ip);
        sysUser.setLoginDate(DateUtils.getNowDate());
        sysUser.setUpdateBy(userId);
        DataPermissionHelper.ignore(() -> userMapper.updateById(sysUser));
    }
    /**
     * ç™»å½•校验
     */
    public void checkLogin(LoginType loginType, String tenantId, String username, Supplier<Boolean> supplier) {
        String errorKey = CacheConstants.PWD_ERR_CNT_KEY + username;
        String loginFail = Constants.LOGIN_FAIL;
        // èŽ·å–ç”¨æˆ·ç™»å½•é”™è¯¯æ¬¡æ•°ï¼Œé»˜è®¤ä¸º0 (可自定义限制策略 ä¾‹å¦‚: key + username + ip)
        int errorNumber = ObjectUtil.defaultIfNull(RedisUtils.getCacheObject(errorKey), 0);
        // é”å®šæ—¶é—´å†…登录 åˆ™è¸¢å‡º
        if (errorNumber >= maxRetryCount) {
            recordLogininfor(tenantId, username, loginFail, MessageUtils.message(loginType.getRetryLimitExceed(), maxRetryCount, lockTime));
            throw new UserException(loginType.getRetryLimitExceed(), maxRetryCount, lockTime);
        }
        if (supplier.get()) {
            // é”™è¯¯æ¬¡æ•°é€’增
            errorNumber++;
            RedisUtils.setCacheObject(errorKey, errorNumber, Duration.ofMinutes(lockTime));
            // è¾¾åˆ°è§„定错误次数 åˆ™é”å®šç™»å½•
            if (errorNumber >= maxRetryCount) {
                recordLogininfor(tenantId, username, loginFail, MessageUtils.message(loginType.getRetryLimitExceed(), maxRetryCount, lockTime));
                throw new UserException(loginType.getRetryLimitExceed(), maxRetryCount, lockTime);
            } else {
                // æœªè¾¾åˆ°è§„定错误次数
                recordLogininfor(tenantId, username, loginFail, MessageUtils.message(loginType.getRetryLimitCount(), errorNumber));
                throw new UserException(loginType.getRetryLimitCount(), errorNumber);
            }
        }
        // ç™»å½•成功 æ¸…空错误次数
        RedisUtils.deleteObject(errorKey);
    }
    /**
     * æ ¡éªŒç§Ÿæˆ·
     *
     * @param tenantId ç§Ÿæˆ·ID
     */
    public void checkTenant(String tenantId) {
        if (!TenantHelper.isEnable()) {
            return;
        }
        if (StringUtils.isBlank(tenantId)) {
            throw new TenantException("tenant.number.not.blank");
        }
        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 (SystemConstants.DISABLE.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-Vue-Plus/ruoyi-admin/src/main/java/org/dromara/web/service/SysRegisterService.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,115 @@
package org.dromara.web.service;
import cn.hutool.crypto.digest.BCrypt;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import lombok.RequiredArgsConstructor;
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.tenant.helper.TenantHelper;
import org.dromara.common.web.config.properties.CaptchaProperties;
import org.dromara.system.domain.SysUser;
import org.dromara.system.domain.bo.SysUserBo;
import org.dromara.system.mapper.SysUserMapper;
import org.dromara.system.service.ISysUserService;
import org.springframework.stereotype.Service;
/**
 * æ³¨å†Œæ ¡éªŒæ–¹æ³•
 *
 * @author Lion Li
 */
@RequiredArgsConstructor
@Service
public class SysRegisterService {
    private final ISysUserService userService;
    private final SysUserMapper userMapper;
    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);
        boolean exist = TenantHelper.dynamic(tenantId, () -> {
            return userMapper.exists(new LambdaQueryWrapper<SysUser>()
                .eq(SysUser::getUserName, sysUser.getUserName()));
        });
        if (exist) {
            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.blankToDefault(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 (!StringUtils.equalsIgnoreCase(code, captcha)) {
            recordLogininfor(tenantId, username, Constants.LOGIN_FAIL, 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-Vue-Plus/ruoyi-admin/src/main/java/org/dromara/web/service/impl/EmailAuthStrategy.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,102 @@
package org.dromara.web.service.impl;
import cn.dev33.satoken.stp.StpUtil;
import cn.dev33.satoken.stp.parameter.SaLoginParameter;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.dromara.common.core.constant.Constants;
import org.dromara.common.core.constant.GlobalConstants;
import org.dromara.common.core.constant.SystemConstants;
import org.dromara.common.core.domain.model.EmailLoginBody;
import org.dromara.common.core.domain.model.LoginUser;
import org.dromara.common.core.enums.LoginType;
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.StringUtils;
import org.dromara.common.core.utils.ValidatorUtils;
import org.dromara.common.json.utils.JsonUtils;
import org.dromara.common.redis.utils.RedisUtils;
import org.dromara.common.satoken.utils.LoginHelper;
import org.dromara.common.tenant.helper.TenantHelper;
import org.dromara.system.domain.SysUser;
import org.dromara.system.domain.vo.SysClientVo;
import org.dromara.system.domain.vo.SysUserVo;
import org.dromara.system.mapper.SysUserMapper;
import org.dromara.web.domain.vo.LoginVo;
import org.dromara.web.service.IAuthStrategy;
import org.dromara.web.service.SysLoginService;
import org.springframework.stereotype.Service;
/**
 * é‚®ä»¶è®¤è¯ç­–ç•¥
 *
 * @author Michelle.Chung
 */
@Slf4j
@Service("email" + IAuthStrategy.BASE_NAME)
@RequiredArgsConstructor
public class EmailAuthStrategy implements IAuthStrategy {
    private final SysLoginService loginService;
    private final SysUserMapper userMapper;
    @Override
    public LoginVo login(String body, SysClientVo client) {
        EmailLoginBody loginBody = JsonUtils.parseObject(body, EmailLoginBody.class);
        ValidatorUtils.validate(loginBody);
        String tenantId = loginBody.getTenantId();
        String email = loginBody.getEmail();
        String emailCode = loginBody.getEmailCode();
        LoginUser loginUser = TenantHelper.dynamic(tenantId, () -> {
            SysUserVo user = loadUserByEmail(email);
            loginService.checkLogin(LoginType.EMAIL, tenantId, user.getUserName(), () -> !validateEmailCode(tenantId, email, emailCode));
            // æ­¤å¤„可根据登录用户的数据不同 è‡ªè¡Œåˆ›å»º loginUser å±žæ€§ä¸å¤Ÿç”¨ç»§æ‰¿æ‰©å±•就行了
            return loginService.buildLoginUser(user);
        });
        loginUser.setClientKey(client.getClientKey());
        loginUser.setDeviceType(client.getDeviceType());
        SaLoginParameter model = new SaLoginParameter();
        model.setDeviceType(client.getDeviceType());
        // è‡ªå®šä¹‰åˆ†é… ä¸åŒç”¨æˆ·ä½“ç³» ä¸åŒ token æŽˆæƒæ—¶é—´ ä¸è®¾ç½®é»˜è®¤èµ°å…¨å±€ yml é…ç½®
        // ä¾‹å¦‚: åŽå°ç”¨æˆ·30分钟过期 app用户1天过期
        model.setTimeout(client.getTimeout());
        model.setActiveTimeout(client.getActiveTimeout());
        model.setExtra(LoginHelper.CLIENT_KEY, client.getClientId());
        // ç”Ÿæˆtoken
        LoginHelper.login(loginUser, model);
        LoginVo loginVo = new LoginVo();
        loginVo.setAccessToken(StpUtil.getTokenValue());
        loginVo.setExpireIn(StpUtil.getTokenTimeout());
        loginVo.setClientId(client.getClientId());
        return loginVo;
    }
    /**
     * æ ¡éªŒé‚®ç®±éªŒè¯ç 
     */
    private boolean validateEmailCode(String tenantId, String email, String emailCode) {
        String code = RedisUtils.getCacheObject(GlobalConstants.CAPTCHA_CODE_KEY + email);
        if (StringUtils.isBlank(code)) {
            loginService.recordLogininfor(tenantId, email, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.expire"));
            throw new CaptchaExpireException();
        }
        return code.equals(emailCode);
    }
    private SysUserVo loadUserByEmail(String email) {
        SysUserVo user = userMapper.selectVoOne(new LambdaQueryWrapper<SysUser>().eq(SysUser::getEmail, email));
        if (ObjectUtil.isNull(user)) {
            log.info("登录用户:{} ä¸å­˜åœ¨.", email);
            throw new UserException("user.not.exists", email);
        } else if (SystemConstants.DISABLE.equals(user.getStatus())) {
            log.info("登录用户:{} å·²è¢«åœç”¨.", email);
            throw new UserException("user.blocked", email);
        }
        return user;
    }
}
RuoYi-Vue-Plus/ruoyi-admin/src/main/java/org/dromara/web/service/impl/PasswordAuthStrategy.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,123 @@
package org.dromara.web.service.impl;
import cn.dev33.satoken.stp.StpUtil;
import cn.dev33.satoken.stp.parameter.SaLoginParameter;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.crypto.digest.BCrypt;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.dromara.common.core.constant.Constants;
import org.dromara.common.core.constant.GlobalConstants;
import org.dromara.common.core.constant.SystemConstants;
import org.dromara.common.core.domain.model.LoginUser;
import org.dromara.common.core.domain.model.PasswordLoginBody;
import org.dromara.common.core.enums.LoginType;
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.StringUtils;
import org.dromara.common.core.utils.ValidatorUtils;
import org.dromara.common.json.utils.JsonUtils;
import org.dromara.common.redis.utils.RedisUtils;
import org.dromara.common.satoken.utils.LoginHelper;
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.SysClientVo;
import org.dromara.system.domain.vo.SysUserVo;
import org.dromara.system.mapper.SysUserMapper;
import org.dromara.web.domain.vo.LoginVo;
import org.dromara.web.service.IAuthStrategy;
import org.dromara.web.service.SysLoginService;
import org.springframework.stereotype.Service;
/**
 * å¯†ç è®¤è¯ç­–ç•¥
 *
 * @author Michelle.Chung
 */
@Slf4j
@Service("password" + IAuthStrategy.BASE_NAME)
@RequiredArgsConstructor
public class PasswordAuthStrategy implements IAuthStrategy {
    private final CaptchaProperties captchaProperties;
    private final SysLoginService loginService;
    private final SysUserMapper userMapper;
    @Override
    public LoginVo login(String body, SysClientVo client) {
        PasswordLoginBody loginBody = JsonUtils.parseObject(body, PasswordLoginBody.class);
        ValidatorUtils.validate(loginBody);
        String tenantId = loginBody.getTenantId();
        String username = loginBody.getUsername();
        String password = loginBody.getPassword();
        String code = loginBody.getCode();
        String uuid = loginBody.getUuid();
        boolean captchaEnabled = captchaProperties.getEnable();
        // éªŒè¯ç å¼€å…³
        if (captchaEnabled) {
            validateCaptcha(tenantId, username, code, uuid);
        }
        LoginUser loginUser = TenantHelper.dynamic(tenantId, () -> {
            SysUserVo user = loadUserByUsername(username);
            loginService.checkLogin(LoginType.PASSWORD, tenantId, username, () -> !BCrypt.checkpw(password, user.getPassword()));
            // æ­¤å¤„可根据登录用户的数据不同 è‡ªè¡Œåˆ›å»º loginUser
            return loginService.buildLoginUser(user);
        });
        loginUser.setClientKey(client.getClientKey());
        loginUser.setDeviceType(client.getDeviceType());
        SaLoginParameter model = new SaLoginParameter();
        model.setDeviceType(client.getDeviceType());
        // è‡ªå®šä¹‰åˆ†é… ä¸åŒç”¨æˆ·ä½“ç³» ä¸åŒ token æŽˆæƒæ—¶é—´ ä¸è®¾ç½®é»˜è®¤èµ°å…¨å±€ yml é…ç½®
        // ä¾‹å¦‚: åŽå°ç”¨æˆ·30分钟过期 app用户1天过期
        model.setTimeout(client.getTimeout());
        model.setActiveTimeout(client.getActiveTimeout());
        model.setExtra(LoginHelper.CLIENT_KEY, client.getClientId());
        // ç”Ÿæˆtoken
        LoginHelper.login(loginUser, model);
        LoginVo loginVo = new LoginVo();
        loginVo.setAccessToken(StpUtil.getTokenValue());
        loginVo.setExpireIn(StpUtil.getTokenTimeout());
        loginVo.setClientId(client.getClientId());
        return loginVo;
    }
    /**
     * æ ¡éªŒéªŒè¯ç 
     *
     * @param username ç”¨æˆ·å
     * @param code     éªŒè¯ç 
     * @param uuid     å”¯ä¸€æ ‡è¯†
     */
    private void validateCaptcha(String tenantId, String username, String code, String uuid) {
        String verifyKey = GlobalConstants.CAPTCHA_CODE_KEY + StringUtils.blankToDefault(uuid, "");
        String captcha = RedisUtils.getCacheObject(verifyKey);
        RedisUtils.deleteObject(verifyKey);
        if (captcha == null) {
            loginService.recordLogininfor(tenantId, username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.expire"));
            throw new CaptchaExpireException();
        }
        if (!StringUtils.equalsIgnoreCase(code, captcha)) {
            loginService.recordLogininfor(tenantId, username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.error"));
            throw new CaptchaException();
        }
    }
    private SysUserVo loadUserByUsername(String username) {
        SysUserVo user = userMapper.selectVoOne(new LambdaQueryWrapper<SysUser>().eq(SysUser::getUserName, username));
        if (ObjectUtil.isNull(user)) {
            log.info("登录用户:{} ä¸å­˜åœ¨.", username);
            throw new UserException("user.not.exists", username);
        } else if (SystemConstants.DISABLE.equals(user.getStatus())) {
            log.info("登录用户:{} å·²è¢«åœç”¨.", username);
            throw new UserException("user.blocked", username);
        }
        return user;
    }
}
RuoYi-Vue-Plus/ruoyi-admin/src/main/java/org/dromara/web/service/impl/SmsAuthStrategy.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,102 @@
package org.dromara.web.service.impl;
import cn.dev33.satoken.stp.StpUtil;
import cn.dev33.satoken.stp.parameter.SaLoginParameter;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.dromara.common.core.constant.Constants;
import org.dromara.common.core.constant.GlobalConstants;
import org.dromara.common.core.constant.SystemConstants;
import org.dromara.common.core.domain.model.LoginUser;
import org.dromara.common.core.domain.model.SmsLoginBody;
import org.dromara.common.core.enums.LoginType;
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.StringUtils;
import org.dromara.common.core.utils.ValidatorUtils;
import org.dromara.common.json.utils.JsonUtils;
import org.dromara.common.redis.utils.RedisUtils;
import org.dromara.common.satoken.utils.LoginHelper;
import org.dromara.common.tenant.helper.TenantHelper;
import org.dromara.system.domain.SysUser;
import org.dromara.system.domain.vo.SysClientVo;
import org.dromara.system.domain.vo.SysUserVo;
import org.dromara.system.mapper.SysUserMapper;
import org.dromara.web.domain.vo.LoginVo;
import org.dromara.web.service.IAuthStrategy;
import org.dromara.web.service.SysLoginService;
import org.springframework.stereotype.Service;
/**
 * çŸ­ä¿¡è®¤è¯ç­–ç•¥
 *
 * @author Michelle.Chung
 */
@Slf4j
@Service("sms" + IAuthStrategy.BASE_NAME)
@RequiredArgsConstructor
public class SmsAuthStrategy implements IAuthStrategy {
    private final SysLoginService loginService;
    private final SysUserMapper userMapper;
    @Override
    public LoginVo login(String body, SysClientVo client) {
        SmsLoginBody loginBody = JsonUtils.parseObject(body, SmsLoginBody.class);
        ValidatorUtils.validate(loginBody);
        String tenantId = loginBody.getTenantId();
        String phonenumber = loginBody.getPhonenumber();
        String smsCode = loginBody.getSmsCode();
        LoginUser loginUser = TenantHelper.dynamic(tenantId, () -> {
            SysUserVo user = loadUserByPhonenumber(phonenumber);
            loginService.checkLogin(LoginType.SMS, tenantId, user.getUserName(), () -> !validateSmsCode(tenantId, phonenumber, smsCode));
            // æ­¤å¤„可根据登录用户的数据不同 è‡ªè¡Œåˆ›å»º loginUser å±žæ€§ä¸å¤Ÿç”¨ç»§æ‰¿æ‰©å±•就行了
            return loginService.buildLoginUser(user);
        });
        loginUser.setClientKey(client.getClientKey());
        loginUser.setDeviceType(client.getDeviceType());
        SaLoginParameter model = new SaLoginParameter();
        model.setDeviceType(client.getDeviceType());
        // è‡ªå®šä¹‰åˆ†é… ä¸åŒç”¨æˆ·ä½“ç³» ä¸åŒ token æŽˆæƒæ—¶é—´ ä¸è®¾ç½®é»˜è®¤èµ°å…¨å±€ yml é…ç½®
        // ä¾‹å¦‚: åŽå°ç”¨æˆ·30分钟过期 app用户1天过期
        model.setTimeout(client.getTimeout());
        model.setActiveTimeout(client.getActiveTimeout());
        model.setExtra(LoginHelper.CLIENT_KEY, client.getClientId());
        // ç”Ÿæˆtoken
        LoginHelper.login(loginUser, model);
        LoginVo loginVo = new LoginVo();
        loginVo.setAccessToken(StpUtil.getTokenValue());
        loginVo.setExpireIn(StpUtil.getTokenTimeout());
        loginVo.setClientId(client.getClientId());
        return loginVo;
    }
    /**
     * æ ¡éªŒçŸ­ä¿¡éªŒè¯ç 
     */
    private boolean validateSmsCode(String tenantId, String phonenumber, String smsCode) {
        String code = RedisUtils.getCacheObject(GlobalConstants.CAPTCHA_CODE_KEY + phonenumber);
        if (StringUtils.isBlank(code)) {
            loginService.recordLogininfor(tenantId, phonenumber, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.expire"));
            throw new CaptchaExpireException();
        }
        return code.equals(smsCode);
    }
    private SysUserVo loadUserByPhonenumber(String phonenumber) {
        SysUserVo user = userMapper.selectVoOne(new LambdaQueryWrapper<SysUser>().eq(SysUser::getPhonenumber, phonenumber));
        if (ObjectUtil.isNull(user)) {
            log.info("登录用户:{} ä¸å­˜åœ¨.", phonenumber);
            throw new UserException("user.not.exists", phonenumber);
        } else if (SystemConstants.DISABLE.equals(user.getStatus())) {
            log.info("登录用户:{} å·²è¢«åœç”¨.", phonenumber);
            throw new UserException("user.blocked", phonenumber);
        }
        return user;
    }
}
RuoYi-Vue-Plus/ruoyi-admin/src/main/java/org/dromara/web/service/impl/SocialAuthStrategy.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,119 @@
package org.dromara.web.service.impl;
import cn.dev33.satoken.stp.StpUtil;
import cn.dev33.satoken.stp.parameter.SaLoginParameter;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import me.zhyd.oauth.model.AuthResponse;
import me.zhyd.oauth.model.AuthUser;
import org.dromara.common.core.constant.SystemConstants;
import org.dromara.common.core.domain.model.LoginUser;
import org.dromara.common.core.domain.model.SocialLoginBody;
import org.dromara.common.core.exception.ServiceException;
import org.dromara.common.core.exception.user.UserException;
import org.dromara.common.core.utils.StreamUtils;
import org.dromara.common.core.utils.ValidatorUtils;
import org.dromara.common.json.utils.JsonUtils;
import org.dromara.common.satoken.utils.LoginHelper;
import org.dromara.common.social.config.properties.SocialProperties;
import org.dromara.common.social.utils.SocialUtils;
import org.dromara.common.tenant.helper.TenantHelper;
import org.dromara.system.domain.vo.SysClientVo;
import org.dromara.system.domain.vo.SysSocialVo;
import org.dromara.system.domain.vo.SysUserVo;
import org.dromara.system.mapper.SysUserMapper;
import org.dromara.system.service.ISysSocialService;
import org.dromara.web.domain.vo.LoginVo;
import org.dromara.web.service.IAuthStrategy;
import org.dromara.web.service.SysLoginService;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Optional;
/**
 * ç¬¬ä¸‰æ–¹æŽˆæƒç­–ç•¥
 *
 * @author thiszhc is ä¸‰ä¸‰
 */
@Slf4j
@Service("social" + IAuthStrategy.BASE_NAME)
@RequiredArgsConstructor
public class SocialAuthStrategy implements IAuthStrategy {
    private final SocialProperties socialProperties;
    private final ISysSocialService sysSocialService;
    private final SysUserMapper userMapper;
    private final SysLoginService loginService;
    /**
     * ç™»å½•-第三方授权登录
     *
     * @param body     ç™»å½•信息
     * @param client   å®¢æˆ·ç«¯ä¿¡æ¯
     */
    @Override
    public LoginVo login(String body, SysClientVo client) {
        SocialLoginBody loginBody = JsonUtils.parseObject(body, SocialLoginBody.class);
        ValidatorUtils.validate(loginBody);
        AuthResponse<AuthUser> response = SocialUtils.loginAuth(
                loginBody.getSource(), loginBody.getSocialCode(),
                loginBody.getSocialState(), socialProperties);
        if (!response.ok()) {
            throw new ServiceException(response.getMsg());
        }
        AuthUser authUserData = response.getData();
        List<SysSocialVo> list = sysSocialService.selectByAuthId(authUserData.getSource() + authUserData.getUuid());
        if (CollUtil.isEmpty(list)) {
            throw new ServiceException("你还没有绑定第三方账号,绑定后才可以登录!");
        }
        SysSocialVo social;
        if (TenantHelper.isEnable()) {
            Optional<SysSocialVo> opt = StreamUtils.findAny(list, x -> x.getTenantId().equals(loginBody.getTenantId()));
            if (opt.isEmpty()) {
                throw new ServiceException("对不起,你没有权限登录当前租户!");
            }
            social = opt.get();
        } else {
            social = list.get(0);
        }
        LoginUser loginUser = TenantHelper.dynamic(social.getTenantId(), () -> {
            SysUserVo user = loadUser(social.getUserId());
            // æ­¤å¤„可根据登录用户的数据不同 è‡ªè¡Œåˆ›å»º loginUser å±žæ€§ä¸å¤Ÿç”¨ç»§æ‰¿æ‰©å±•就行了
            return loginService.buildLoginUser(user);
        });
        loginUser.setClientKey(client.getClientKey());
        loginUser.setDeviceType(client.getDeviceType());
        SaLoginParameter model = new SaLoginParameter();
        model.setDeviceType(client.getDeviceType());
        // è‡ªå®šä¹‰åˆ†é… ä¸åŒç”¨æˆ·ä½“ç³» ä¸åŒ token æŽˆæƒæ—¶é—´ ä¸è®¾ç½®é»˜è®¤èµ°å…¨å±€ yml é…ç½®
        // ä¾‹å¦‚: åŽå°ç”¨æˆ·30分钟过期 app用户1天过期
        model.setTimeout(client.getTimeout());
        model.setActiveTimeout(client.getActiveTimeout());
        model.setExtra(LoginHelper.CLIENT_KEY, client.getClientId());
        // ç”Ÿæˆtoken
        LoginHelper.login(loginUser, model);
        LoginVo loginVo = new LoginVo();
        loginVo.setAccessToken(StpUtil.getTokenValue());
        loginVo.setExpireIn(StpUtil.getTokenTimeout());
        loginVo.setClientId(client.getClientId());
        return loginVo;
    }
    private SysUserVo loadUser(Long userId) {
        SysUserVo user = userMapper.selectVoById(userId);
        if (ObjectUtil.isNull(user)) {
            log.info("登录用户:{} ä¸å­˜åœ¨.", "");
            throw new UserException("user.not.exists", "");
        } else if (SystemConstants.DISABLE.equals(user.getStatus())) {
            log.info("登录用户:{} å·²è¢«åœç”¨.", "");
            throw new UserException("user.blocked", "");
        }
        return user;
    }
}
RuoYi-Vue-Plus/ruoyi-admin/src/main/java/org/dromara/web/service/impl/XcxAuthStrategy.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,111 @@
package org.dromara.web.service.impl;
import cn.dev33.satoken.stp.StpUtil;
import cn.dev33.satoken.stp.parameter.SaLoginParameter;
import cn.hutool.core.util.ObjectUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import me.zhyd.oauth.config.AuthConfig;
import me.zhyd.oauth.model.AuthCallback;
import me.zhyd.oauth.model.AuthResponse;
import me.zhyd.oauth.model.AuthToken;
import me.zhyd.oauth.model.AuthUser;
import me.zhyd.oauth.request.AuthRequest;
import me.zhyd.oauth.request.AuthWechatMiniProgramRequest;
import org.dromara.common.core.constant.SystemConstants;
import org.dromara.common.core.domain.model.XcxLoginBody;
import org.dromara.common.core.domain.model.XcxLoginUser;
import org.dromara.common.core.exception.ServiceException;
import org.dromara.common.core.utils.ValidatorUtils;
import org.dromara.common.json.utils.JsonUtils;
import org.dromara.common.satoken.utils.LoginHelper;
import org.dromara.system.domain.vo.SysClientVo;
import org.dromara.system.domain.vo.SysUserVo;
import org.dromara.web.domain.vo.LoginVo;
import org.dromara.web.service.IAuthStrategy;
import org.dromara.web.service.SysLoginService;
import org.springframework.stereotype.Service;
/**
 * å°ç¨‹åºè®¤è¯ç­–ç•¥
 *
 * @author Michelle.Chung
 */
@Slf4j
@Service("xcx" + IAuthStrategy.BASE_NAME)
@RequiredArgsConstructor
public class XcxAuthStrategy implements IAuthStrategy {
    private final SysLoginService loginService;
    @Override
    public LoginVo login(String body, SysClientVo client) {
        XcxLoginBody loginBody = JsonUtils.parseObject(body, XcxLoginBody.class);
        ValidatorUtils.validate(loginBody);
        // xcxCode ä¸º å°ç¨‹åºè°ƒç”¨ wx.login æŽˆæƒåŽèŽ·å–
        String xcxCode = loginBody.getXcxCode();
        // å¤šä¸ªå°ç¨‹åºè¯†åˆ«ä½¿ç”¨
        String appid = loginBody.getAppid();
        // æ ¡éªŒ appid + appsrcret + xcxCode è°ƒç”¨ç™»å½•凭证校验接口 èŽ·å– session_key ä¸Ž openid
        AuthRequest authRequest = new AuthWechatMiniProgramRequest(AuthConfig.builder()
            .clientId(appid).clientSecret("自行填写密钥 å¯æ ¹æ®ä¸åŒappid填入不同密钥")
            .ignoreCheckRedirectUri(true).ignoreCheckState(true).build());
        AuthCallback authCallback = new AuthCallback();
        authCallback.setCode(xcxCode);
        AuthResponse<AuthUser> resp = authRequest.login(authCallback);
        String openid, unionId;
        if (resp.ok()) {
            AuthToken token = resp.getData().getToken();
            openid = token.getOpenId();
            // å¾®ä¿¡å°ç¨‹åºåªæœ‰å…³è”到微信开放平台下之后才能获取到 unionId,因此unionId不一定能返回。
            unionId = token.getUnionId();
        } else {
            throw new ServiceException(resp.getMsg());
        }
        // æ¡†æž¶ç™»å½•不限制从什么表查询 åªè¦æœ€ç»ˆæž„建出 LoginUser å³å¯
        SysUserVo user = loadUserByOpenid(openid);
        // æ­¤å¤„可根据登录用户的数据不同 è‡ªè¡Œåˆ›å»º loginUser å±žæ€§ä¸å¤Ÿç”¨ç»§æ‰¿æ‰©å±•就行了
        XcxLoginUser loginUser = new XcxLoginUser();
        loginUser.setTenantId(user.getTenantId());
        loginUser.setUserId(user.getUserId());
        loginUser.setUsername(user.getUserName());
        loginUser.setNickname(user.getNickName());
        loginUser.setUserType(user.getUserType());
        loginUser.setClientKey(client.getClientKey());
        loginUser.setDeviceType(client.getDeviceType());
        loginUser.setOpenid(openid);
        SaLoginParameter model = new SaLoginParameter();
        model.setDeviceType(client.getDeviceType());
        // è‡ªå®šä¹‰åˆ†é… ä¸åŒç”¨æˆ·ä½“ç³» ä¸åŒ token æŽˆæƒæ—¶é—´ ä¸è®¾ç½®é»˜è®¤èµ°å…¨å±€ yml é…ç½®
        // ä¾‹å¦‚: åŽå°ç”¨æˆ·30分钟过期 app用户1天过期
        model.setTimeout(client.getTimeout());
        model.setActiveTimeout(client.getActiveTimeout());
        model.setExtra(LoginHelper.CLIENT_KEY, client.getClientId());
        // ç”Ÿæˆtoken
        LoginHelper.login(loginUser, model);
        LoginVo loginVo = new LoginVo();
        loginVo.setAccessToken(StpUtil.getTokenValue());
        loginVo.setExpireIn(StpUtil.getTokenTimeout());
        loginVo.setClientId(client.getClientId());
        loginVo.setOpenid(openid);
        return loginVo;
    }
    private SysUserVo loadUserByOpenid(String openid) {
        // ä½¿ç”¨ openid æŸ¥è¯¢ç»‘定用户 å¦‚未绑定用户 åˆ™æ ¹æ®ä¸šåŠ¡è‡ªè¡Œå¤„ç† ä¾‹å¦‚ åˆ›å»ºé»˜è®¤ç”¨æˆ·
        // todo è‡ªè¡Œå®žçް userService.selectUserByOpenid(openid);
        SysUserVo user = new SysUserVo();
        if (ObjectUtil.isNull(user)) {
            log.info("登录用户:{} ä¸å­˜åœ¨.", openid);
            // todo ç”¨æˆ·ä¸å­˜åœ¨ ä¸šåŠ¡é€»è¾‘è‡ªè¡Œå®žçŽ°
        } else if (SystemConstants.DISABLE.equals(user.getStatus())) {
            log.info("登录用户:{} å·²è¢«åœç”¨.", openid);
            // todo ç”¨æˆ·å·²è¢«åœç”¨ ä¸šåŠ¡é€»è¾‘è‡ªè¡Œå®žçŽ°
        }
        return user;
    }
}
RuoYi-Vue-Plus/ruoyi-admin/src/main/resources/application-dev.yml
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,276 @@
--- # ç›‘控中心配置
spring.boot.admin.client:
  # å¢žåŠ å®¢æˆ·ç«¯å¼€å…³
  enabled: false
  url: http://localhost:9090/admin
  instance:
    service-host-type: IP
    metadata:
      username: ${spring.boot.admin.client.username}
      userpassword: ${spring.boot.admin.client.password}
  username: @monitor.username@
  password: @monitor.password@
--- # snail-job é…ç½®
snail-job:
  enabled: false
  # éœ€è¦åœ¨ SnailJob åŽå°ç»„管理创建对应名称的组,然后创建任务的时候选择对应的组,才能正确分派任务
  group: "ruoyi_group"
  # SnailJob æŽ¥å…¥éªŒè¯ä»¤ç‰Œ è¯¦è§ script/sql/ry_job.sql `sj_group_config` è¡¨
  token: "SJ_cKqBTPzCsWA3VyuCfFoccmuIEGXjr5KT"
  server:
    host: 127.0.0.1
    port: 17888
  # å‘½åç©ºé—´UUID è¯¦è§ script/sql/ry_job.sql `sj_namespace`表`unique_id`字段
  namespace: ${spring.profiles.active}
  # éšä¸»åº”用端口漂移
  port: 2${server.port}
  # å®¢æˆ·ç«¯ip指定
  host:
--- # æ•°æ®æºé…ç½®
spring:
  datasource:
    type: com.zaxxer.hikari.HikariDataSource
    # åŠ¨æ€æ•°æ®æºæ–‡æ¡£ https://www.kancloud.cn/tracy5546/dynamic-datasource/content
    dynamic:
      # æ€§èƒ½åˆ†æžæ’ä»¶(有性能损耗 ä¸å»ºè®®ç”Ÿäº§çŽ¯å¢ƒä½¿ç”¨)
      p6spy: true
      # è®¾ç½®é»˜è®¤çš„æ•°æ®æºæˆ–者数据源组,默认值即为 master
      primary: master
      # ä¸¥æ ¼æ¨¡å¼ åŒ¹é…ä¸åˆ°æ•°æ®æºåˆ™æŠ¥é”™
      strict: true
      datasource:
        # ä¸»åº“数据源
        master:
          type: ${spring.datasource.type}
          driverClassName: org.postgresql.Driver
          url: jdbc:postgresql://192.168.2.26:5432/postgres?currentSchema=public&serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf8&useSSL=true&autoReconnect=true&reWriteBatchedInserts=true
          username: postgres
          password: 123456
      #        master:
#          type: ${spring.datasource.type}
#          driverClassName: com.mysql.cj.jdbc.Driver
#          # jdbc æ‰€æœ‰å‚数配置参考 https://lionli.blog.csdn.net/article/details/122018562
#          # rewriteBatchedStatements=true æ‰¹å¤„理优化 å¤§å¹…提升批量插入更新删除性能(对数据库有性能损耗 ä½¿ç”¨æ‰¹é‡æ“ä½œåº”考虑性能问题)
#          url: jdbc:mysql://localhost:3306/ry-vue?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true
#          username: root
#          password: 123456
#        # ä»Žåº“数据源
#        slave:
#          lazy: true
#          type: ${spring.datasource.type}
#          driverClassName: com.mysql.cj.jdbc.Driver
#          url: jdbc:mysql://localhost:3306/ry-vue?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true
#          username:
#          password:
#        oracle:
#          type: ${spring.datasource.type}
#          driverClassName: oracle.jdbc.OracleDriver
#          url: jdbc:oracle:thin:@//localhost:1521/XE
#          username: ROOT
#          password: root
#        postgres:
#          type: ${spring.datasource.type}
#          driverClassName: org.postgresql.Driver
#          url: jdbc:postgresql://localhost:5432/postgres?useUnicode=true&characterEncoding=utf8&useSSL=true&autoReconnect=true&reWriteBatchedInserts=true
#          username: root
#          password: root
#        sqlserver:
#          type: ${spring.datasource.type}
#          driverClassName: com.microsoft.sqlserver.jdbc.SQLServerDriver
#          url: jdbc:sqlserver://localhost:1433;DatabaseName=tempdb;SelectMethod=cursor;encrypt=false;rewriteBatchedStatements=true
#          username: SA
#          password: root
      hikari:
        # æœ€å¤§è¿žæŽ¥æ± æ•°é‡
        maxPoolSize: 20
        # æœ€å°ç©ºé—²çº¿ç¨‹æ•°é‡
        minIdle: 10
        # é…ç½®èŽ·å–è¿žæŽ¥ç­‰å¾…è¶…æ—¶çš„æ—¶é—´
        connectionTimeout: 30000
        # æ ¡éªŒè¶…æ—¶æ—¶é—´
        validationTimeout: 5000
        # ç©ºé—²è¿žæŽ¥å­˜æ´»æœ€å¤§æ—¶é—´ï¼Œé»˜è®¤10分钟
        idleTimeout: 600000
        # æ­¤å±žæ€§æŽ§åˆ¶æ± ä¸­è¿žæŽ¥çš„æœ€é•¿ç”Ÿå‘½å‘¨æœŸï¼Œå€¼0表示无限生命周期,默认30分钟
        maxLifetime: 1800000
        # å¤šä¹…检查一次连接的活性
        keepaliveTime: 30000
--- # redis å•机配置(单机与集群只能开启一个另一个需要注释掉)
spring.data:
  redis:
    # åœ°å€
    host: localhost
    # ç«¯å£ï¼Œé»˜è®¤ä¸º6677
    port: 6379
    # æ•°æ®åº“索引
    database: 0
    # redis å¯†ç å¿…须配置
#    password:
    # è¿žæŽ¥è¶…æ—¶æ—¶é—´
    timeout: 10s
    # æ˜¯å¦å¼€å¯ssl
    ssl.enabled: false
# redisson é…ç½®
redisson:
  # redis key前缀
  keyPrefix:
  # çº¿ç¨‹æ± æ•°é‡
  threads: 4
  # Netty线程池数量
  nettyThreads: 8
  # å•节点配置
  singleServerConfig:
    # å®¢æˆ·ç«¯åç§° ä¸èƒ½ç”¨ä¸­æ–‡
    clientName: RuoYi-Vue-Plus
    # æœ€å°ç©ºé—²è¿žæŽ¥æ•°
    connectionMinimumIdleSize: 8
    # è¿žæŽ¥æ± å¤§å°
    connectionPoolSize: 32
    # è¿žæŽ¥ç©ºé—²è¶…时,单位:毫秒
    idleConnectionTimeout: 10000
    # å‘½ä»¤ç­‰å¾…超时,单位:毫秒
    timeout: 3000
    # å‘布和订阅连接池大小
    subscriptionConnectionPoolSize: 50
--- # mail é‚®ä»¶å‘送
mail:
  enabled: false
  host: smtp.163.com
  port: 465
  # æ˜¯å¦éœ€è¦ç”¨æˆ·åå¯†ç éªŒè¯
  auth: true
  # å‘送方,遵循RFC-822标准
  from: xxx@163.com
  # ç”¨æˆ·åï¼ˆæ³¨æ„ï¼šå¦‚果使用foxmail邮箱,此处user为qq号)
  user: xxx@163.com
  # å¯†ç ï¼ˆæ³¨æ„ï¼ŒæŸäº›é‚®ç®±éœ€è¦ä¸ºSMTP服务单独设置密码,详情查看相关帮助)
  pass: xxxxxxxxxx
  # ä½¿ç”¨ STARTTLS安全连接,STARTTLS是对纯文本通信协议的扩展。
  starttlsEnable: true
  # ä½¿ç”¨SSL安全连接
  sslEnable: true
  # SMTP超时时长,单位毫秒,缺省值不超时
  timeout: 0
  # Socket连接超时值,单位毫秒,缺省值不超时
  connectionTimeout: 0
--- # sms çŸ­ä¿¡ æ”¯æŒ é˜¿é‡Œäº‘ è…¾è®¯äº‘ äº‘片 ç­‰ç­‰å„式各样的短信服务商
# https://sms4j.com/doc3/ å·®å¼‚配置文档地址 æ”¯æŒå•厂商多配置,可以配置多个同时使用
sms:
  # é…ç½®æºç±»åž‹ç”¨äºŽæ ‡å®šé…ç½®æ¥æº(interface,yaml)
  config-type: yaml
  # ç”¨äºŽæ ‡å®šyml中的配置是否开启短信拦截,接口配置不受此限制
  restricted: true
  # çŸ­ä¿¡æ‹¦æˆªé™åˆ¶å•手机号每分钟最大发送,只对开启了拦截的配置有效
  minute-max: 1
  # çŸ­ä¿¡æ‹¦æˆªé™åˆ¶å•手机号每日最大发送量,只对开启了拦截的配置有效
  account-max: 30
  # ä»¥ä¸‹é…ç½®æ¥è‡ªäºŽ org.dromara.sms4j.provider.config.BaseConfig类中
  blends:
    # å”¯ä¸€ID ç”¨äºŽå‘送短信寻找具体配置 éšä¾¿å®šä¹‰åˆ«ç”¨ä¸­æ–‡å³å¯
    # å¯ä»¥åŒæ—¶å­˜åœ¨ä¸¤ä¸ªç›¸åŒåނ商 ä¾‹å¦‚: ali1 ali2 ä¸¤ä¸ªä¸åŒçš„阿里短信账号 ä¹Ÿå¯ç”¨äºŽåŒºåˆ†ç§Ÿæˆ·
    config1:
      # æ¡†æž¶å®šä¹‰çš„厂商名称标识,标定此配置是哪个厂商,详细请看厂商标识介绍部分
      supplier: alibaba
      # æœ‰äº›ç§°ä¸ºaccessKey有些称之为apiKey,也有称为sdkKey或者appId。
      access-key-id: æ‚¨çš„accessKey
      # ç§°ä¸ºaccessSecret有些称之为apiSecret
      access-key-secret: æ‚¨çš„accessKeySecret
      signature: æ‚¨çš„短信签名
      sdk-app-id: æ‚¨çš„sdkAppId
    config2:
      # åŽ‚å•†æ ‡è¯†ï¼Œæ ‡å®šæ­¤é…ç½®æ˜¯å“ªä¸ªåŽ‚å•†ï¼Œè¯¦ç»†è¯·çœ‹åŽ‚å•†æ ‡è¯†ä»‹ç»éƒ¨åˆ†
      supplier: tencent
      access-key-id: æ‚¨çš„accessKey
      access-key-secret: æ‚¨çš„accessKeySecret
      signature: æ‚¨çš„短信签名
      sdk-app-id: æ‚¨çš„sdkAppId
--- # ä¸‰æ–¹æŽˆæƒ
justauth:
  # å‰ç«¯å¤–网访问地址
  address: http://localhost:80
  type:
    maxkey:
      # maxkey æœåŠ¡å™¨åœ°å€
      # æ³¨æ„ å¦‚下均配置均不需要修改 maxkey å·²ç»å†…置好了数据
      server-url: http://sso.maxkey.top
      client-id: 876892492581044224
      client-secret: x1Y5MTMwNzIwMjMxNTM4NDc3Mzche8
      redirect-uri: ${justauth.address}/social-callback?source=maxkey
    topiam:
      # topiam æœåŠ¡å™¨åœ°å€
      server-url: http://127.0.0.1:1898/api/v1/authorize/y0q************spq***********8ol
      client-id: 449c4*********937************759
      client-secret: ac7***********1e0************28d
      redirect-uri: ${justauth.address}/social-callback?source=topiam
      scopes: [openid, email, phone, profile]
    qq:
      client-id: 10**********6
      client-secret: 1f7d08**********5b7**********29e
      redirect-uri: ${justauth.address}/social-callback?source=qq
      union-id: false
    weibo:
      client-id: 10**********6
      client-secret: 1f7d08**********5b7**********29e
      redirect-uri: ${justauth.address}/social-callback?source=weibo
    gitee:
      client-id: 91436b7940090d09c72c7daf85b959cfd5f215d67eea73acbf61b6b590751a98
      client-secret: 02c6fcfd70342980cd8dd2f2c06c1a350645d76c754d7a264c4e125f9ba915ac
      redirect-uri: ${justauth.address}/social-callback?source=gitee
    dingtalk:
      client-id: 10**********6
      client-secret: 1f7d08**********5b7**********29e
      redirect-uri: ${justauth.address}/social-callback?source=dingtalk
    baidu:
      client-id: 10**********6
      client-secret: 1f7d08**********5b7**********29e
      redirect-uri: ${justauth.address}/social-callback?source=baidu
    csdn:
      client-id: 10**********6
      client-secret: 1f7d08**********5b7**********29e
      redirect-uri: ${justauth.address}/social-callback?source=csdn
    coding:
      client-id: 10**********6
      client-secret: 1f7d08**********5b7**********29e
      redirect-uri: ${justauth.address}/social-callback?source=coding
      coding-group-name: xx
    oschina:
      client-id: 10**********6
      client-secret: 1f7d08**********5b7**********29e
      redirect-uri: ${justauth.address}/social-callback?source=oschina
    alipay_wallet:
      client-id: 10**********6
      client-secret: 1f7d08**********5b7**********29e
      redirect-uri: ${justauth.address}/social-callback?source=alipay_wallet
      alipay-public-key: MIIB**************DAQAB
    wechat_open:
      client-id: 10**********6
      client-secret: 1f7d08**********5b7**********29e
      redirect-uri: ${justauth.address}/social-callback?source=wechat_open
    wechat_mp:
      client-id: 10**********6
      client-secret: 1f7d08**********5b7**********29e
      redirect-uri: ${justauth.address}/social-callback?source=wechat_mp
    wechat_enterprise:
      client-id: 10**********6
      client-secret: 1f7d08**********5b7**********29e
      redirect-uri: ${justauth.address}/social-callback?source=wechat_enterprise
      agent-id: 1000002
    gitlab:
      client-id: 10**********6
      client-secret: 1f7d08**********5b7**********29e
      redirect-uri: ${justauth.address}/social-callback?source=gitlab
    gitea:
      # å‰ç«¯æ”¹åЍ https://gitee.com/JavaLionLi/plus-ui/pulls/204
      # gitea æœåŠ¡å™¨åœ°å€
      server-url: https://demo.gitea.com
      client-id: 10**********6
      client-secret: 1f7d08**********5b7**********29e
      redirect-uri: ${justauth.address}/social-callback?source=gitea
RuoYi-Vue-Plus/ruoyi-admin/src/main/resources/application-prod.yml
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,272 @@
--- # ä¸´æ—¶æ–‡ä»¶å­˜å‚¨ä½ç½® é¿å…ä¸´æ—¶æ–‡ä»¶è¢«ç³»ç»Ÿæ¸…理报错
spring.servlet.multipart.location: /ruoyi/server/temp
--- # ç›‘控中心配置
spring.boot.admin.client:
  # å¢žåŠ å®¢æˆ·ç«¯å¼€å…³
  enabled: true
  url: http://localhost:9090/admin
  instance:
    service-host-type: IP
    metadata:
      username: ${spring.boot.admin.client.username}
      userpassword: ${spring.boot.admin.client.password}
  username: @monitor.username@
  password: @monitor.password@
--- # snail-job é…ç½®
snail-job:
  enabled: true
  # éœ€è¦åœ¨ SnailJob åŽå°ç»„管理创建对应名称的组,然后创建任务的时候选择对应的组,才能正确分派任务
  group: "ruoyi_group"
  # SnailJob æŽ¥å…¥éªŒè¯ä»¤ç‰Œ è¯¦è§ script/sql/ry_job.sql `sj_group_config`表
  token: "SJ_cKqBTPzCsWA3VyuCfFoccmuIEGXjr5KT"
  server:
    host: 127.0.0.1
    port: 17888
  # å‘½åç©ºé—´UUID è¯¦è§ script/sql/ry_job.sql `sj_namespace`表`unique_id`字段
  namespace: ${spring.profiles.active}
  # éšä¸»åº”用端口漂移
  port: 2${server.port}
  # å®¢æˆ·ç«¯ip指定
  host:
--- # æ•°æ®æºé…ç½®
spring:
  datasource:
    type: com.zaxxer.hikari.HikariDataSource
    # åŠ¨æ€æ•°æ®æºæ–‡æ¡£ https://www.kancloud.cn/tracy5546/dynamic-datasource/content
    dynamic:
      # æ€§èƒ½åˆ†æžæ’ä»¶(有性能损耗 ä¸å»ºè®®ç”Ÿäº§çŽ¯å¢ƒä½¿ç”¨)
      p6spy: false
      # è®¾ç½®é»˜è®¤çš„æ•°æ®æºæˆ–者数据源组,默认值即为 master
      primary: master
      # ä¸¥æ ¼æ¨¡å¼ åŒ¹é…ä¸åˆ°æ•°æ®æºåˆ™æŠ¥é”™
      strict: true
      datasource:
        # ä¸»åº“数据源
        master:
          type: ${spring.datasource.type}
          driverClassName: com.mysql.cj.jdbc.Driver
          # jdbc æ‰€æœ‰å‚数配置参考 https://lionli.blog.csdn.net/article/details/122018562
          # rewriteBatchedStatements=true æ‰¹å¤„理优化 å¤§å¹…提升批量插入更新删除性能(对数据库有性能损耗 ä½¿ç”¨æ‰¹é‡æ“ä½œåº”考虑性能问题)
          url: jdbc:mysql://localhost:3306/ry-vue?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true
          username: root
          password: root
#        # ä»Žåº“数据源
#        slave:
#          lazy: true
#          type: ${spring.datasource.type}
#          driverClassName: com.mysql.cj.jdbc.Driver
#          url: jdbc:mysql://localhost:3306/ry-vue?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true
#          username:
#          password:
#        oracle:
#          type: ${spring.datasource.type}
#          driverClassName: oracle.jdbc.OracleDriver
#          url: jdbc:oracle:thin:@//localhost:1521/XE
#          username: ROOT
#          password: root
#        postgres:
#          type: ${spring.datasource.type}
#          driverClassName: org.postgresql.Driver
#          url: jdbc:postgresql://localhost:5432/postgres?useUnicode=true&characterEncoding=utf8&useSSL=true&autoReconnect=true&reWriteBatchedInserts=true
#          username: root
#          password: root
#        sqlserver:
#          type: ${spring.datasource.type}
#          driverClassName: com.microsoft.sqlserver.jdbc.SQLServerDriver
#          url: jdbc:sqlserver://localhost:1433;DatabaseName=tempdb;SelectMethod=cursor;encrypt=false;rewriteBatchedStatements=true
#          username: SA
#          password: root
      hikari:
        # æœ€å¤§è¿žæŽ¥æ± æ•°é‡
        maxPoolSize: 20
        # æœ€å°ç©ºé—²çº¿ç¨‹æ•°é‡
        minIdle: 10
        # é…ç½®èŽ·å–è¿žæŽ¥ç­‰å¾…è¶…æ—¶çš„æ—¶é—´
        connectionTimeout: 30000
        # æ ¡éªŒè¶…æ—¶æ—¶é—´
        validationTimeout: 5000
        # ç©ºé—²è¿žæŽ¥å­˜æ´»æœ€å¤§æ—¶é—´ï¼Œé»˜è®¤10分钟
        idleTimeout: 600000
        # æ­¤å±žæ€§æŽ§åˆ¶æ± ä¸­è¿žæŽ¥çš„æœ€é•¿ç”Ÿå‘½å‘¨æœŸï¼Œå€¼0表示无限生命周期,默认30分钟
        maxLifetime: 1800000
        # å¤šä¹…检查一次连接的活性
        keepaliveTime: 30000
--- # redis å•机配置(单机与集群只能开启一个另一个需要注释掉)
spring.data:
  redis:
    # åœ°å€
    host: localhost
    # ç«¯å£ï¼Œé»˜è®¤ä¸º6379
    port: 6379
    # æ•°æ®åº“索引
    database: 0
    # redis å¯†ç å¿…须配置
    password: ruoyi123
    # è¿žæŽ¥è¶…æ—¶æ—¶é—´
    timeout: 10s
    # æ˜¯å¦å¼€å¯ssl
    ssl.enabled: false
# redisson é…ç½®
redisson:
  # redis key前缀
  keyPrefix:
  # çº¿ç¨‹æ± æ•°é‡
  threads: 16
  # Netty线程池数量
  nettyThreads: 32
  # å•节点配置
  singleServerConfig:
    # å®¢æˆ·ç«¯åç§° ä¸èƒ½ç”¨ä¸­æ–‡
    clientName: RuoYi-Vue-Plus
    # æœ€å°ç©ºé—²è¿žæŽ¥æ•°
    connectionMinimumIdleSize: 32
    # è¿žæŽ¥æ± å¤§å°
    connectionPoolSize: 64
    # è¿žæŽ¥ç©ºé—²è¶…时,单位:毫秒
    idleConnectionTimeout: 10000
    # å‘½ä»¤ç­‰å¾…超时,单位:毫秒
    timeout: 3000
    # å‘布和订阅连接池大小
    subscriptionConnectionPoolSize: 50
--- # mail é‚®ä»¶å‘送
mail:
  enabled: false
  host: smtp.163.com
  port: 465
  # æ˜¯å¦éœ€è¦ç”¨æˆ·åå¯†ç éªŒè¯
  auth: true
  # å‘送方,遵循RFC-822标准
  from: xxx@163.com
  # ç”¨æˆ·åï¼ˆæ³¨æ„ï¼šå¦‚果使用foxmail邮箱,此处user为qq号)
  user: xxx@163.com
  # å¯†ç ï¼ˆæ³¨æ„ï¼ŒæŸäº›é‚®ç®±éœ€è¦ä¸ºSMTP服务单独设置密码,详情查看相关帮助)
  pass: xxxxxxxxxx
  # ä½¿ç”¨ STARTTLS安全连接,STARTTLS是对纯文本通信协议的扩展。
  starttlsEnable: true
  # ä½¿ç”¨SSL安全连接
  sslEnable: true
  # SMTP超时时长,单位毫秒,缺省值不超时
  timeout: 0
  # Socket连接超时值,单位毫秒,缺省值不超时
  connectionTimeout: 0
--- # sms çŸ­ä¿¡ æ”¯æŒ é˜¿é‡Œäº‘ è…¾è®¯äº‘ äº‘片 ç­‰ç­‰å„式各样的短信服务商
# https://sms4j.com/doc3/ å·®å¼‚配置文档地址 æ”¯æŒå•厂商多配置,可以配置多个同时使用
sms:
  # é…ç½®æºç±»åž‹ç”¨äºŽæ ‡å®šé…ç½®æ¥æº(interface,yaml)
  config-type: yaml
  # ç”¨äºŽæ ‡å®šyml中的配置是否开启短信拦截,接口配置不受此限制
  restricted: true
  # çŸ­ä¿¡æ‹¦æˆªé™åˆ¶å•手机号每分钟最大发送,只对开启了拦截的配置有效
  minute-max: 1
  # çŸ­ä¿¡æ‹¦æˆªé™åˆ¶å•手机号每日最大发送量,只对开启了拦截的配置有效
  account-max: 30
  # ä»¥ä¸‹é…ç½®æ¥è‡ªäºŽ org.dromara.sms4j.provider.config.BaseConfig类中
  blends:
    # å”¯ä¸€ID ç”¨äºŽå‘送短信寻找具体配置 éšä¾¿å®šä¹‰åˆ«ç”¨ä¸­æ–‡å³å¯
    # å¯ä»¥åŒæ—¶å­˜åœ¨ä¸¤ä¸ªç›¸åŒåނ商 ä¾‹å¦‚: ali1 ali2 ä¸¤ä¸ªä¸åŒçš„阿里短信账号 ä¹Ÿå¯ç”¨äºŽåŒºåˆ†ç§Ÿæˆ·
    config1:
      # æ¡†æž¶å®šä¹‰çš„厂商名称标识,标定此配置是哪个厂商,详细请看厂商标识介绍部分
      supplier: alibaba
      # æœ‰äº›ç§°ä¸ºaccessKey有些称之为apiKey,也有称为sdkKey或者appId。
      access-key-id: æ‚¨çš„accessKey
      # ç§°ä¸ºaccessSecret有些称之为apiSecret
      access-key-secret: æ‚¨çš„accessKeySecret
      signature: æ‚¨çš„短信签名
      sdk-app-id: æ‚¨çš„sdkAppId
    config2:
      # åŽ‚å•†æ ‡è¯†ï¼Œæ ‡å®šæ­¤é…ç½®æ˜¯å“ªä¸ªåŽ‚å•†ï¼Œè¯¦ç»†è¯·çœ‹åŽ‚å•†æ ‡è¯†ä»‹ç»éƒ¨åˆ†
      supplier: tencent
      access-key-id: æ‚¨çš„accessKey
      access-key-secret: æ‚¨çš„accessKeySecret
      signature: æ‚¨çš„短信签名
      sdk-app-id: æ‚¨çš„sdkAppId
--- # ä¸‰æ–¹æŽˆæƒ
justauth:
  # å‰ç«¯å¤–网访问地址
  address: http://localhost:80
  type:
    maxkey:
      # maxkey æœåŠ¡å™¨åœ°å€
      # æ³¨æ„ å¦‚下均配置均不需要修改 maxkey å·²ç»å†…置好了数据
      server-url: http://sso.maxkey.top
      client-id: 876892492581044224
      client-secret: x1Y5MTMwNzIwMjMxNTM4NDc3Mzche8
      redirect-uri: ${justauth.address}/social-callback?source=maxkey
    topiam:
      # topiam æœåŠ¡å™¨åœ°å€
      server-url: http://127.0.0.1:1989/api/v1/authorize/y0q************spq***********8ol
      client-id: 449c4*********937************759
      client-secret: ac7***********1e0************28d
      redirect-uri: ${justauth.address}/social-callback?source=topiam
      scopes: [ openid, email, phone, profile ]
    qq:
      client-id: 10**********6
      client-secret: 1f7d08**********5b7**********29e
      redirect-uri: ${justauth.address}/social-callback?source=qq
      union-id: false
    weibo:
      client-id: 10**********6
      client-secret: 1f7d08**********5b7**********29e
      redirect-uri: ${justauth.address}/social-callback?source=weibo
    gitee:
      client-id: 91436b7940090d09c72c7daf85b959cfd5f215d67eea73acbf61b6b590751a98
      client-secret: 02c6fcfd70342980cd8dd2f2c06c1a350645d76c754d7a264c4e125f9ba915ac
      redirect-uri: ${justauth.address}/social-callback?source=gitee
    dingtalk:
      client-id: 10**********6
      client-secret: 1f7d08**********5b7**********29e
      redirect-uri: ${justauth.address}/social-callback?source=dingtalk
    baidu:
      client-id: 10**********6
      client-secret: 1f7d08**********5b7**********29e
      redirect-uri: ${justauth.address}/social-callback?source=baidu
    csdn:
      client-id: 10**********6
      client-secret: 1f7d08**********5b7**********29e
      redirect-uri: ${justauth.address}/social-callback?source=csdn
    coding:
      client-id: 10**********6
      client-secret: 1f7d08**********5b7**********29e
      redirect-uri: ${justauth.address}/social-callback?source=coding
      coding-group-name: xx
    oschina:
      client-id: 10**********6
      client-secret: 1f7d08**********5b7**********29e
      redirect-uri: ${justauth.address}/social-callback?source=oschina
    alipay_wallet:
      client-id: 10**********6
      client-secret: 1f7d08**********5b7**********29e
      redirect-uri: ${justauth.address}/social-callback?source=alipay_wallet
      alipay-public-key: MIIB**************DAQAB
    wechat_open:
      client-id: 10**********6
      client-secret: 1f7d08**********5b7**********29e
      redirect-uri: ${justauth.address}/social-callback?source=wechat_open
    wechat_mp:
      client-id: 10**********6
      client-secret: 1f7d08**********5b7**********29e
      redirect-uri: ${justauth.address}/social-callback?source=wechat_mp
    wechat_enterprise:
      client-id: 10**********6
      client-secret: 1f7d08**********5b7**********29e
      redirect-uri: ${justauth.address}/social-callback?source=wechat_enterprise
      agent-id: 1000002
    gitlab:
      client-id: 10**********6
      client-secret: 1f7d08**********5b7**********29e
      redirect-uri: ${justauth.address}/social-callback?source=gitlab
    gitea:
      # å‰ç«¯æ”¹åЍ https://gitee.com/JavaLionLi/plus-ui/pulls/204
      # gitea æœåŠ¡å™¨åœ°å€
      server-url: https://demo.gitea.com
      client-id: 10**********6
      client-secret: 1f7d08**********5b7**********29e
      redirect-uri: ${justauth.address}/social-callback?source=gitea
RuoYi-Vue-Plus/ruoyi-admin/src/main/resources/application.yml
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,263 @@
# å¼€å‘环境配置
server:
  # æœåŠ¡å™¨çš„HTTP端口,默认为8080
  port: 8080
  servlet:
    # åº”用的访问路径
    context-path: /
  # undertow é…ç½®
  undertow:
    # HTTP post内容的最大大小。当值为-1时,默认值为大小是无限的
    max-http-post-size: -1
    # ä»¥ä¸‹çš„配置会影响buffer,这些buffer会用于服务器连接的IO操作,有点类似netty的池化内存管理
    # æ¯å—buffer的空间大小,越小的空间被利用越充分
    buffer-size: 512
    # æ˜¯å¦åˆ†é…çš„直接内存
    direct-buffers: true
    threads:
      # è®¾ç½®IO线程数, å®ƒä¸»è¦æ‰§è¡Œéžé˜»å¡žçš„任务,它们会负责多个连接, é»˜è®¤è®¾ç½®æ¯ä¸ªCPU核心一个线程
      io: 8
      # é˜»å¡žä»»åŠ¡çº¿ç¨‹æ± , å½“执行类似servlet请求阻塞操作, undertow会从这个线程池中取得线程,它的值设置取决于系统的负载
      worker: 256
captcha:
  # æ˜¯å¦å¯ç”¨éªŒè¯ç æ ¡éªŒ
  enable: false
  # éªŒè¯ç ç±»åž‹ math æ•°ç»„计算 char å­—符验证
  type: math
  # æ•°å­—验证码位数
  numberLength: 1
  # å­—符验证码长度
  charLength: 4
# æ—¥å¿—配置
logging:
  level:
    org.dromara: @logging.level@
    org.springframework: warn
    org.mybatis.spring.mapper: error
    org.apache.fury: warn
  config: classpath:logback-plus.xml
# ç”¨æˆ·é…ç½®
user:
  password:
    # å¯†ç æœ€å¤§é”™è¯¯æ¬¡æ•°
    maxRetryCount: 5
    # å¯†ç é”å®šæ—¶é—´ï¼ˆé»˜è®¤10分钟)
    lockTime: 10
# Spring配置
spring:
  application:
    name: RuoYi-Vue-Plus
  threads:
    # å¼€å¯è™šæ‹Ÿçº¿ç¨‹ ä»…jdk21可用
    virtual:
      enabled: false
  task:
    execution:
      # ä»Ž springboot 3.5 å¼€å§‹ spring自带线程池
      # ä¸å†éœ€è¦ AsyncConfig与ThreadPoolConfig å¯ç›´æŽ¥æ³¨å…¥çº¿ç¨‹æ± ä½¿ç”¨
      thread-name-prefix: async-
      # ç”±spring自己初始化线程池
      mode: force
  # èµ„源信息
  messages:
    # å›½é™…化资源文件路径
    basename: i18n/messages
  profiles:
    active: @profiles.active@
  # æ–‡ä»¶ä¸Šä¼ 
  servlet:
    multipart:
      # å•个文件大小
      max-file-size: 10MB
      # è®¾ç½®æ€»ä¸Šä¼ çš„æ–‡ä»¶å¤§å°
      max-request-size: 20MB
  mvc:
    # è®¾ç½®é™æ€èµ„源路径 é˜²æ­¢æ‰€æœ‰è¯·æ±‚都去查静态资源
    static-path-pattern: /static/**
    format:
      date-time: yyyy-MM-dd HH:mm:ss
  jackson:
    # æ—¥æœŸæ ¼å¼åŒ–
    date-format: yyyy-MM-dd HH:mm:ss
    serialization:
      # æ ¼å¼åŒ–输出
      indent_output: false
      # å¿½ç•¥æ— æ³•转换的对象
      fail_on_empty_beans: false
    deserialization:
      # å…è®¸å¯¹è±¡å¿½ç•¥json中不存在的属性
      fail_on_unknown_properties: false
# Sa-Token配置
sa-token:
  # token名称 (同时也是cookie名称)
  token-name: Authorization
  # æ˜¯å¦å…è®¸åŒä¸€è´¦å·å¹¶å‘登录 (为true时允许一起登录, ä¸ºfalse时新登录挤掉旧登录)
  is-concurrent: true
  # åœ¨å¤šäººç™»å½•同一账号时,是否共用一个token (为true时所有登录共用一个token, ä¸ºfalse时每次登录新建一个token)
  is-share: false
  # jwt秘钥
  jwt-secret-key: abcdefghijklmnopqrstuvwxyz
# security配置
security:
  # æŽ’除路径
  excludes:
    - /*.html
    - /**/*.html
    - /**/*.css
    - /**/*.js
    - /favicon.ico
    - /error
    - /*/api-docs
    - /*/api-docs/**
    - /warm-flow-ui/config
# å¤šç§Ÿæˆ·é…ç½®
tenant:
  # æ˜¯å¦å¼€å¯
  enable: false
  # æŽ’除表
  excludes:
    - sys_menu
    - sys_tenant
    - sys_tenant_package
    - sys_role_dept
    - sys_role_menu
    - sys_user_post
    - sys_user_role
    - sys_client
    - sys_oss_config
    - flow_spel
# MyBatisPlus配置
# https://baomidou.com/config/
mybatis-plus:
  # è‡ªå®šä¹‰é…ç½® æ˜¯å¦å…¨å±€å¼€å¯é€»è¾‘删除 å…³é—­åŽ æ‰€æœ‰é€»è¾‘删除功能将失效
  enableLogicDelete: true
  # å¤šåŒ…名使用 ä¾‹å¦‚ org.dromara.**.mapper,org.xxx.**.mapper
  mapperPackage: org.dromara.**.mapper
  # å¯¹åº”çš„ XML æ–‡ä»¶ä½ç½®
  mapperLocations: classpath*:mapper/**/*Mapper.xml
  # å®žä½“扫描,多个package用逗号或者分号分隔
  typeAliasesPackage: org.dromara.**.domain
  global-config:
    dbConfig:
      # ä¸»é”®ç±»åž‹
      # AUTO è‡ªå¢ž NONE ç©º INPUT ç”¨æˆ·è¾“å…¥ ASSIGN_ID é›ªèб ASSIGN_UUID å”¯ä¸€ UUID
      # å¦‚需改为自增 éœ€è¦å°†æ•°æ®åº“表全部设置为自增
      idType: ASSIGN_ID
# æ•°æ®åР坆
mybatis-encryptor:
  # æ˜¯å¦å¼€å¯åР坆
  enable: false
  # é»˜è®¤åŠ å¯†ç®—æ³•
  algorithm: BASE64
  # ç¼–码方式 BASE64/HEX。默认BASE64
  encode: BASE64
  # å®‰å…¨ç§˜é’¥ å¯¹ç§°ç®—法的秘钥 å¦‚:AES,SM4
  password:
  # å…¬ç§é’¥ éžå¯¹ç§°ç®—法的公私钥 å¦‚:SM2,RSA
  publicKey:
  privateKey:
# api接口加密
api-decrypt:
  # æ˜¯å¦å¼€å¯å…¨å±€æŽ¥å£åР坆
  enabled: false
  # AES åŠ å¯†å¤´æ ‡è¯†
  headerFlag: encrypt-key
  # å“åº”加密公钥 éžå¯¹ç§°ç®—法的公私钥 å¦‚:SM2,RSA ä½¿ç”¨è€…请自行更换
  # å¯¹åº”前端解密私钥 MIIBVAIBADANBgkqhkiG9w0BAQEFAASCAT4wggE6AgEAAkEAmc3CuPiGL/LcIIm7zryCEIbl1SPzBkr75E2VMtxegyZ1lYRD+7TZGAPkvIsBcaMs6Nsy0L78n2qh+lIZMpLH8wIDAQABAkEAk82Mhz0tlv6IVCyIcw/s3f0E+WLmtPFyR9/WtV3Y5aaejUkU60JpX4m5xNR2VaqOLTZAYjW8Wy0aXr3zYIhhQQIhAMfqR9oFdYw1J9SsNc+CrhugAvKTi0+BF6VoL6psWhvbAiEAxPPNTmrkmrXwdm/pQQu3UOQmc2vCZ5tiKpW10CgJi8kCIFGkL6utxw93Ncj4exE/gPLvKcT+1Emnoox+O9kRXss5AiAMtYLJDaLEzPrAWcZeeSgSIzbL+ecokmFKSDDcRske6QIgSMkHedwND1olF8vlKsJUGK3BcdtM8w4Xq7BpSBwsloE=
  publicKey: MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAJnNwrj4hi/y3CCJu868ghCG5dUj8wZK++RNlTLcXoMmdZWEQ/u02RgD5LyLAXGjLOjbMtC+/J9qofpSGTKSx/MCAwEAAQ==
  # è¯·æ±‚解密私钥 éžå¯¹ç§°ç®—法的公私钥 å¦‚:SM2,RSA ä½¿ç”¨è€…请自行更换
  # å¯¹åº”前端加密公钥 MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAKoR8mX0rGKLqzcWmOzbfj64K8ZIgOdHnzkXSOVOZbFu/TJhZ7rFAN+eaGkl3C4buccQd/EjEsj9ir7ijT7h96MCAwEAAQ==
  privateKey: MIIBVAIBADANBgkqhkiG9w0BAQEFAASCAT4wggE6AgEAAkEAqhHyZfSsYourNxaY7Nt+PrgrxkiA50efORdI5U5lsW79MmFnusUA355oaSXcLhu5xxB38SMSyP2KvuKNPuH3owIDAQABAkAfoiLyL+Z4lf4Myxk6xUDgLaWGximj20CUf+5BKKnlrK+Ed8gAkM0HqoTt2UZwA5E2MzS4EI2gjfQhz5X28uqxAiEA3wNFxfrCZlSZHb0gn2zDpWowcSxQAgiCstxGUoOqlW8CIQDDOerGKH5OmCJ4Z21v+F25WaHYPxCFMvwxpcw99EcvDQIgIdhDTIqD2jfYjPTY8Jj3EDGPbH2HHuffvflECt3Ek60CIQCFRlCkHpi7hthhYhovyloRYsM+IS9h/0BzlEAuO0ktMQIgSPT3aFAgJYwKpqRYKlLDVcflZFCKY7u3UP8iWi1Qw0Y=
springdoc:
  api-docs:
    # æ˜¯å¦å¼€å¯æŽ¥å£æ–‡æ¡£
    enabled: true
  info:
    # æ ‡é¢˜
    title: '标题:RuoYi-Vue-Plus多租户管理系统_接口文档'
    # æè¿°
    description: '描述:用于管理集团旗下公司的人员信息,具体包括XXX,XXX模块...'
    # ç‰ˆæœ¬
    version: '版本号: ${project.version}'
    # ä½œè€…信息
    contact:
      name: Lion Li
      email: crazylionli@163.com
      url: https://gitee.com/dromara/RuoYi-Vue-Plus
  #这里定义了两个分组,可定义多个,也可以不定义
  group-configs:
    - group: 1.演示模块
      packages-to-scan: org.dromara.demo
    - group: 2.通用模块
      packages-to-scan: org.dromara.web
    - group: 3.系统模块
      packages-to-scan: org.dromara.system
    - group: 4.代码生成模块
      packages-to-scan: org.dromara.generator
    - group: 5.工作流模块
      packages-to-scan: org.dromara.workflow
# é˜²æ­¢XSS攻击
xss:
  # è¿‡æ»¤å¼€å…³
  enabled: true
  # æŽ’除链接
  excludeUrls:
    - /system/notice
--- # åˆ†å¸ƒå¼é” lock4j å…¨å±€é…ç½®
lock4j:
  # èŽ·å–åˆ†å¸ƒå¼é”è¶…æ—¶æ—¶é—´ï¼Œé»˜è®¤ä¸º 3000 æ¯«ç§’
  acquire-timeout: 3000
  # åˆ†å¸ƒå¼é”çš„超时时间,默认为 30 ç§’
  expire: 30000
--- # Actuator ç›‘控端点的配置项
management:
  endpoints:
    web:
      exposure:
        include: '*'
  endpoint:
    health:
      show-details: ALWAYS
    logfile:
      external-file: ./logs/sys-console.log
--- # é»˜è®¤/推荐使用sse推送
sse:
  enabled: true
  path: /resource/sse
--- # websocket
websocket:
  # å¦‚果关闭 éœ€è¦å’Œå‰ç«¯å¼€å…³ä¸€èµ·å…³é—­
  enabled: false
  # è·¯å¾„
  path: /resource/websocket
  # è®¾ç½®è®¿é—®æºåœ°å€
  allowedOrigins: '*'
--- # warm-flow工作流配置
warm-flow:
  # æ˜¯å¦å¼€å¯å·¥ä½œæµï¼Œé»˜è®¤true
  enabled: false
  # æ˜¯å¦å¼€å¯è®¾è®¡å™¨ui
  ui: true
  # æ˜¯å¦æ˜¾ç¤ºæµç¨‹å›¾é¡¶éƒ¨æ–‡å­—
  top-text-show: true
  # æ˜¯å¦æ¸²æŸ“节点悬浮提示,默认true
  node-tooltip: true
  # é»˜è®¤Authorization,如果有多个token,用逗号分隔
  token-name: ${sa-token.token-name},clientid
RuoYi-Vue-Plus/ruoyi-admin/src/main/resources/banner.txt
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,8 @@
Application Version: ${revision}
Spring Boot Version: ${spring-boot.version}
__________            _____.___.__         ____   ____                     __________.__
\______   \__ __  ____\__  |   |__|        \   \ /   /_ __   ____          \______   \  |  __ __  ______
 |       _/  |  \/  _ \/   |   |  |  ______ \   Y   /  |  \_/ __ \   ______ |     ___/  | |  |  \/  ___/
 |    |   \  |  (  <_> )____   |  | /_____/  \     /|  |  /\  ___/  /_____/ |    |   |  |_|  |  /\___ \
 |____|_  /____/ \____// ______|__|           \___/ |____/  \___  >         |____|   |____/____//____  >
        \/             \/                                       \/                                   \/
RuoYi-Vue-Plus/ruoyi-admin/src/main/resources/i18n/messages.properties
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,62 @@
#错误消息
not.null=* å¿…须填写
user.jcaptcha.error=验证码错误
user.jcaptcha.expire=验证码已失效
user.not.exists=对不起, æ‚¨çš„账号:{0} ä¸å­˜åœ¨.
user.password.not.match=用户不存在/密码错误
user.password.retry.limit.count=密码输入错误{0}次
user.password.retry.limit.exceed=密码输入错误{0}次,账户锁定{1}分钟
user.password.delete=对不起,您的账号:{0} å·²è¢«åˆ é™¤
user.blocked=对不起,您的账号:{0} å·²ç¦ç”¨ï¼Œè¯·è”系管理员
role.blocked=角色已封禁,请联系管理员
user.logout.success=退出成功
length.not.valid=长度必须在{min}到{max}个字符之间
user.username.not.blank=用户名不能为空
user.username.not.valid=* 2到20个汉字、字母、数字或下划线组成,且必须以非数字开头
user.username.length.valid=账户长度必须在{min}到{max}个字符之间
user.password.not.blank=用户密码不能为空
user.password.length.valid=用户密码长度必须在{min}到{max}个字符之间
user.password.not.valid=* 5-50个字符
user.password.format.valid=密码必须包含大写字母、小写字母、数字和特殊字符
user.email.not.valid=邮箱格式错误
user.email.not.blank=邮箱不能为空
user.phonenumber.not.blank=用户手机号不能为空
user.mobile.phone.number.not.valid=手机号格式错误
user.login.success=登录成功
user.register.success=注册成功
user.register.save.error=保存用户 {0} å¤±è´¥ï¼Œæ³¨å†Œè´¦å·å·²å­˜åœ¨
user.register.error=注册失败,请联系系统管理人员
user.notfound=请重新登录
user.forcelogout=管理员强制退出,请重新登录
user.unknown.error=未知错误,请重新登录
auth.grant.type.error=认证权限类型错误
auth.grant.type.blocked=认证权限类型已禁用
auth.grant.type.not.blank=认证权限类型不能为空
auth.clientid.not.blank=认证客户端id不能为空
##文件上传消息
upload.exceed.maxSize=上传的文件大小超出限制的文件大小!<br/>允许的文件最大大小是:{0}MB!
upload.filename.exceed.length=上传的文件名最长{0}个字符
##权限
no.permission=您没有数据的权限,请联系管理员添加权限 [{0}]
no.create.permission=您没有创建数据的权限,请联系管理员添加权限 [{0}]
no.update.permission=您没有修改数据的权限,请联系管理员添加权限 [{0}]
no.delete.permission=您没有删除数据的权限,请联系管理员添加权限 [{0}]
no.export.permission=您没有导出数据的权限,请联系管理员添加权限 [{0}]
no.view.permission=您没有查看数据的权限,请联系管理员添加权限 [{0}]
repeat.submit.message=不允许重复提交,请稍候再试
rate.limiter.message=访问过于频繁,请稍候再试
sms.code.not.blank=短信验证码不能为空
sms.code.retry.limit.count=短信验证码输入错误{0}次
sms.code.retry.limit.exceed=短信验证码输入错误{0}次,账户锁定{1}分钟
email.code.not.blank=邮箱验证码不能为空
email.code.retry.limit.count=邮箱验证码输入错误{0}次
email.code.retry.limit.exceed=邮箱验证码输入错误{0}次,账户锁定{1}分钟
xcx.code.not.blank=小程序[code]不能为空
social.source.not.blank=第三方登录平台[source]不能为空
social.code.not.blank=第三方登录平台[code]不能为空
social.state.not.blank=第三方登录平台[state]不能为空
##租户
tenant.number.not.blank=租户编号不能为空
tenant.not.exists=对不起, æ‚¨çš„租户不存在,请联系管理员
tenant.blocked=对不起,您的租户已禁用,请联系管理员
tenant.expired=对不起,您的租户已过期,请联系管理员
RuoYi-Vue-Plus/ruoyi-admin/src/main/resources/i18n/messages_en_US.properties
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,62 @@
#错误消息
not.null=* Required fill in
user.jcaptcha.error=Captcha error
user.jcaptcha.expire=Captcha invalid
user.not.exists=Sorry, your account: {0} does not exist
user.password.not.match=User does not exist/Password error
user.password.retry.limit.count=Password input error {0} times
user.password.retry.limit.exceed=Password input error {0} times, account locked for {1} minutes
user.password.delete=Sorry, your account:{0} has been deleted
user.blocked=Sorry, your account: {0} has been disabled. Please contact the administrator
role.blocked=Role disabled,please contact administrators
user.logout.success=Exit successful
length.not.valid=The length must be between {min} and {max} characters
user.username.not.blank=Username cannot be blank
user.username.not.valid=* 2 to 20 chinese characters, letters, numbers or underscores, and must start with a non number
user.username.length.valid=Account length must be between {min} and {max} characters
user.password.not.blank=Password cannot be empty
user.password.length.valid=Password length must be between {min} and {max} characters
user.password.not.valid=* 5-50 characters
user.password.format.valid=Password must contain uppercase, lowercase, digit, and special character
user.email.not.valid=Mailbox format error
user.email.not.blank=Mailbox cannot be blank
user.phonenumber.not.blank=Phone number cannot be blank
user.mobile.phone.number.not.valid=Phone number format error
user.login.success=Login successful
user.register.success=Register successful
user.register.save.error=Failed to save user {0}, The registered account already exists
user.register.error=Register failed, please contact system administrator
user.notfound=Please login again
user.forcelogout=The administrator is forced to exit,please login again
user.unknown.error=Unknown error, please login again
auth.grant.type.error=Auth grant type error
auth.grant.type.blocked=Auth grant type disabled
auth.grant.type.not.blank=Auth grant type cannot be blank
auth.clientid.not.blank=Auth clientid cannot be blank
##文件上传消息
upload.exceed.maxSize=The uploaded file size exceeds the limit file size!<br/>the maximum allowed file size is:{0}MB!
upload.filename.exceed.length=The maximum length of uploaded file name is {0} characters
##权限
no.permission=You do not have permission to the data,please contact your administrator to add permissions [{0}]
no.create.permission=You do not have permission to create data,please contact your administrator to add permissions [{0}]
no.update.permission=You do not have permission to modify data,please contact your administrator to add permissions [{0}]
no.delete.permission=You do not have permission to delete data,please contact your administrator to add permissions [{0}]
no.export.permission=You do not have permission to export data,please contact your administrator to add permissions [{0}]
no.view.permission=You do not have permission to view data,please contact your administrator to add permissions [{0}]
repeat.submit.message=Repeat submit is not allowed, please try again later
rate.limiter.message=Visit too frequently, please try again later
sms.code.not.blank=Sms code cannot be blank
sms.code.retry.limit.count=Sms code input error {0} times
sms.code.retry.limit.exceed=Sms code input error {0} times, account locked for {1} minutes
email.code.not.blank=Email code cannot be blank
email.code.retry.limit.count=Email code input error {0} times
email.code.retry.limit.exceed=Email code input error {0} times, account locked for {1} minutes
xcx.code.not.blank=Mini program [code] cannot be blank
social.source.not.blank=Social login platform [source] cannot be blank
social.code.not.blank=Social login platform [code] cannot be blank
social.state.not.blank=Social login platform [state] cannot be blank
##租户
tenant.number.not.blank=Tenant number cannot be blank
tenant.not.exists=Sorry, your tenant does not exist. Please contact the administrator
tenant.blocked=Sorry, your tenant is disabled. Please contact the administrator
tenant.expired=Sorry, your tenant has expired. Please contact the administrator.
RuoYi-Vue-Plus/ruoyi-admin/src/main/resources/i18n/messages_zh_CN.properties
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,62 @@
#错误消息
not.null=* å¿…须填写
user.jcaptcha.error=验证码错误
user.jcaptcha.expire=验证码已失效
user.not.exists=对不起, æ‚¨çš„账号:{0} ä¸å­˜åœ¨.
user.password.not.match=用户不存在/密码错误
user.password.retry.limit.count=密码输入错误{0}次
user.password.retry.limit.exceed=密码输入错误{0}次,账户锁定{1}分钟
user.password.delete=对不起,您的账号:{0} å·²è¢«åˆ é™¤
user.blocked=对不起,您的账号:{0} å·²ç¦ç”¨ï¼Œè¯·è”系管理员
role.blocked=角色已封禁,请联系管理员
user.logout.success=退出成功
length.not.valid=长度必须在{min}到{max}个字符之间
user.username.not.blank=用户名不能为空
user.username.not.valid=* 2到20个汉字、字母、数字或下划线组成,且必须以非数字开头
user.username.length.valid=账户长度必须在{min}到{max}个字符之间
user.password.not.blank=用户密码不能为空
user.password.length.valid=用户密码长度必须在{min}到{max}个字符之间
user.password.not.valid=* 5-50个字符
user.password.format.valid=密码必须包含大写字母、小写字母、数字和特殊字符
user.email.not.valid=邮箱格式错误
user.email.not.blank=邮箱不能为空
user.phonenumber.not.blank=用户手机号不能为空
user.mobile.phone.number.not.valid=手机号格式错误
user.login.success=登录成功
user.register.success=注册成功
user.register.save.error=保存用户 {0} å¤±è´¥ï¼Œæ³¨å†Œè´¦å·å·²å­˜åœ¨
user.register.error=注册失败,请联系系统管理人员
user.notfound=请重新登录
user.forcelogout=管理员强制退出,请重新登录
user.unknown.error=未知错误,请重新登录
auth.grant.type.error=认证权限类型错误
auth.grant.type.blocked=认证权限类型已禁用
auth.grant.type.not.blank=认证权限类型不能为空
auth.clientid.not.blank=认证客户端id不能为空
##文件上传消息
upload.exceed.maxSize=上传的文件大小超出限制的文件大小!<br/>允许的文件最大大小是:{0}MB!
upload.filename.exceed.length=上传的文件名最长{0}个字符
##权限
no.permission=您没有数据的权限,请联系管理员添加权限 [{0}]
no.create.permission=您没有创建数据的权限,请联系管理员添加权限 [{0}]
no.update.permission=您没有修改数据的权限,请联系管理员添加权限 [{0}]
no.delete.permission=您没有删除数据的权限,请联系管理员添加权限 [{0}]
no.export.permission=您没有导出数据的权限,请联系管理员添加权限 [{0}]
no.view.permission=您没有查看数据的权限,请联系管理员添加权限 [{0}]
repeat.submit.message=不允许重复提交,请稍候再试
rate.limiter.message=访问过于频繁,请稍候再试
sms.code.not.blank=短信验证码不能为空
sms.code.retry.limit.count=短信验证码输入错误{0}次
sms.code.retry.limit.exceed=短信验证码输入错误{0}次,账户锁定{1}分钟
email.code.not.blank=邮箱验证码不能为空
email.code.retry.limit.count=邮箱验证码输入错误{0}次
email.code.retry.limit.exceed=邮箱验证码输入错误{0}次,账户锁定{1}分钟
xcx.code.not.blank=小程序[code]不能为空
social.source.not.blank=第三方登录平台[source]不能为空
social.code.not.blank=第三方登录平台[code]不能为空
social.state.not.blank=第三方登录平台[state]不能为空
##租户
tenant.number.not.blank=租户编号不能为空
tenant.not.exists=对不起, æ‚¨çš„租户不存在,请联系管理员
tenant.blocked=对不起,您的租户已禁用,请联系管理员
tenant.expired=对不起,您的租户已过期,请联系管理员
RuoYi-Vue-Plus/ruoyi-admin/src/main/resources/ip2region_v4.xdb
Binary files differ
RuoYi-Vue-Plus/ruoyi-admin/src/main/resources/logback-plus.xml
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,129 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <property name="log.path" value="./logs"/>
    <property name="console.log.pattern"
              value="%cyan(%d{yyyy-MM-dd HH:mm:ss}) %green([%thread]) %highlight(%-5level) %boldMagenta(%logger{36}%n) - %msg%n"/>
    <property name="log.pattern" value="%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n"/>
    <!-- æŽ§åˆ¶å°è¾“出 -->
    <appender name="console" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>${console.log.pattern}</pattern>
            <charset>utf-8</charset>
        </encoder>
    </appender>
    <!-- æŽ§åˆ¶å°è¾“出 -->
    <appender name="file_console" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>${log.path}/sys-console.log</file>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!-- æ—¥å¿—文件名格式 -->
            <fileNamePattern>${log.path}/sys-console.%d{yyyy-MM-dd}.log</fileNamePattern>
            <!-- æ—¥å¿—最大 1天 -->
            <maxHistory>1</maxHistory>
        </rollingPolicy>
        <encoder>
            <pattern>${log.pattern}</pattern>
            <charset>utf-8</charset>
        </encoder>
        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
            <!-- è¿‡æ»¤çš„级别 -->
            <level>INFO</level>
        </filter>
    </appender>
    <!-- ç³»ç»Ÿæ—¥å¿—输出 -->
    <appender name="file_info" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>${log.path}/sys-info.log</file>
        <!-- å¾ªçŽ¯æ”¿ç­–ï¼šåŸºäºŽæ—¶é—´åˆ›å»ºæ—¥å¿—æ–‡ä»¶ -->
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!-- æ—¥å¿—文件名格式 -->
            <fileNamePattern>${log.path}/sys-info.%d{yyyy-MM-dd}.log.gz</fileNamePattern>
            <!-- æ—¥å¿—最大的历史 60天 -->
            <maxHistory>60</maxHistory>
        </rollingPolicy>
        <encoder>
            <pattern>${log.pattern}</pattern>
        </encoder>
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <!-- è¿‡æ»¤çš„级别 -->
            <level>INFO</level>
            <!-- åŒ¹é…æ—¶çš„æ“ä½œï¼šæŽ¥æ”¶ï¼ˆè®°å½•) -->
            <onMatch>ACCEPT</onMatch>
            <!-- ä¸åŒ¹é…æ—¶çš„æ“ä½œï¼šæ‹’绝(不记录) -->
            <onMismatch>DENY</onMismatch>
        </filter>
    </appender>
    <appender name="file_error" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>${log.path}/sys-error.log</file>
        <!-- å¾ªçŽ¯æ”¿ç­–ï¼šåŸºäºŽæ—¶é—´åˆ›å»ºæ—¥å¿—æ–‡ä»¶ -->
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!-- æ—¥å¿—文件名格式 -->
            <fileNamePattern>${log.path}/sys-error.%d{yyyy-MM-dd}.log.gz</fileNamePattern>
            <!-- æ—¥å¿—最大的历史 60天 -->
            <maxHistory>60</maxHistory>
        </rollingPolicy>
        <encoder>
            <pattern>${log.pattern}</pattern>
        </encoder>
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <!-- è¿‡æ»¤çš„级别 -->
            <level>ERROR</level>
            <!-- åŒ¹é…æ—¶çš„æ“ä½œï¼šæŽ¥æ”¶ï¼ˆè®°å½•) -->
            <onMatch>ACCEPT</onMatch>
            <!-- ä¸åŒ¹é…æ—¶çš„æ“ä½œï¼šæ‹’绝(不记录) -->
            <onMismatch>DENY</onMismatch>
        </filter>
    </appender>
    <!-- info异步输出 -->
    <appender name="async_info" class="ch.qos.logback.classic.AsyncAppender">
        <!-- ä¸ä¸¢å¤±æ—¥å¿—.默认的,如果队列的80%已满,则会丢弃TRACT、DEBUG、INFO级别的日志 -->
        <discardingThreshold>0</discardingThreshold>
        <!-- æ›´æ”¹é»˜è®¤çš„队列的深度,该值会影响性能.默认值为256 -->
        <queueSize>512</queueSize>
        <!-- æ·»åŠ é™„åŠ çš„appender,最多只能添加一个 -->
        <appender-ref ref="file_info"/>
    </appender>
    <!-- error异步输出 -->
    <appender name="async_error" class="ch.qos.logback.classic.AsyncAppender">
        <!-- ä¸ä¸¢å¤±æ—¥å¿—.默认的,如果队列的80%已满,则会丢弃TRACT、DEBUG、INFO级别的日志 -->
        <discardingThreshold>0</discardingThreshold>
        <!-- æ›´æ”¹é»˜è®¤çš„队列的深度,该值会影响性能.默认值为256 -->
        <queueSize>512</queueSize>
        <!-- æ·»åŠ é™„åŠ çš„appender,最多只能添加一个 -->
        <appender-ref ref="file_error"/>
    </appender>
    <!-- æ•´åˆ skywalking æŽ§åˆ¶å°è¾“出 tid -->
<!--    <appender name="console" class="ch.qos.logback.core.ConsoleAppender">-->
<!--        <encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">-->
<!--            <layout class="org.apache.skywalking.apm.toolkit.log.logback.v1.x.TraceIdPatternLogbackLayout">-->
<!--                <pattern>[%tid] ${console.log.pattern}</pattern>-->
<!--            </layout>-->
<!--            <charset>utf-8</charset>-->
<!--        </encoder>-->
<!--    </appender>-->
    <!-- æ•´åˆ skywalking æŽ¨é€é‡‡é›†æ—¥å¿— -->
<!--    <appender name="sky_log" class="org.apache.skywalking.apm.toolkit.log.logback.v1.x.log.GRPCLogClientAppender">-->
<!--        <encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">-->
<!--            <layout class="org.apache.skywalking.apm.toolkit.log.logback.v1.x.TraceIdPatternLogbackLayout">-->
<!--                <pattern>[%tid] ${console.log.pattern}</pattern>-->
<!--            </layout>-->
<!--            <charset>utf-8</charset>-->
<!--        </encoder>-->
<!--    </appender>-->
    <!--系统操作日志-->
    <root level="info">
        <appender-ref ref="console" />
        <appender-ref ref="async_info" />
        <appender-ref ref="async_error" />
        <appender-ref ref="file_console" />
<!--        <appender-ref ref="sky_log"/>-->
    </root>
</configuration>
RuoYi-Vue-Plus/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-Vue-Plus/ruoyi-admin/src/test/java/org/dromara/test/DemoUnitTest.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,70 @@
package org.dromara.test;
import org.dromara.common.web.config.properties.CaptchaProperties;
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 CaptchaProperties captchaProperties;
    @DisplayName("测试 @SpringBootTest @Test @DisplayName æ³¨è§£")
    @Test
    public void testTest() {
        System.out.println(captchaProperties);
    }
    @Disabled
    @DisplayName("测试 @Disabled æ³¨è§£")
    @Test
    public void testDisabled() {
        System.out.println(captchaProperties);
    }
    @Timeout(value = 2L, unit = TimeUnit.SECONDS)
    @DisplayName("测试 @Timeout æ³¨è§£")
    @Test
    public void testTimeout() throws InterruptedException {
        Thread.sleep(3000);
        System.out.println(captchaProperties);
    }
    @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-Vue-Plus/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-Vue-Plus/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-Vue-Plus/ruoyi-common/.DS_Store
Binary files differ
RuoYi-Vue-Plus/ruoyi-common/.flattened-pom.xml
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,41 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <modelVersion>4.0.0</modelVersion>
  <parent>
    <groupId>org.dromara</groupId>
    <artifactId>ruoyi-vue-plus</artifactId>
    <version>5.5.3</version>
  </parent>
  <groupId>org.dromara</groupId>
  <artifactId>ruoyi-common</artifactId>
  <version>5.5.3</version>
  <packaging>pom</packaging>
  <description>common é€šç”¨æ¨¡å—</description>
  <modules>
    <module>ruoyi-common-bom</module>
    <module>ruoyi-common-social</module>
    <module>ruoyi-common-core</module>
    <module>ruoyi-common-doc</module>
    <module>ruoyi-common-excel</module>
    <module>ruoyi-common-idempotent</module>
    <module>ruoyi-common-job</module>
    <module>ruoyi-common-log</module>
    <module>ruoyi-common-mail</module>
    <module>ruoyi-common-mybatis</module>
    <module>ruoyi-common-oss</module>
    <module>ruoyi-common-ratelimiter</module>
    <module>ruoyi-common-redis</module>
    <module>ruoyi-common-satoken</module>
    <module>ruoyi-common-security</module>
    <module>ruoyi-common-sms</module>
    <module>ruoyi-common-web</module>
    <module>ruoyi-common-translation</module>
    <module>ruoyi-common-sensitive</module>
    <module>ruoyi-common-json</module>
    <module>ruoyi-common-encrypt</module>
    <module>ruoyi-common-tenant</module>
    <module>ruoyi-common-websocket</module>
    <module>ruoyi-common-sse</module>
  </modules>
</project>
RuoYi-Vue-Plus/ruoyi-common/pom.xml
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,46 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>ruoyi-vue-plus</artifactId>
        <groupId>org.dromara</groupId>
        <version>${revision}</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>
    <modules>
        <module>ruoyi-common-bom</module>
        <module>ruoyi-common-social</module>
        <module>ruoyi-common-core</module>
        <module>ruoyi-common-doc</module>
        <module>ruoyi-common-excel</module>
        <module>ruoyi-common-idempotent</module>
        <module>ruoyi-common-job</module>
        <module>ruoyi-common-log</module>
        <module>ruoyi-common-mail</module>
        <module>ruoyi-common-mybatis</module>
        <module>ruoyi-common-oss</module>
        <module>ruoyi-common-ratelimiter</module>
        <module>ruoyi-common-redis</module>
        <module>ruoyi-common-satoken</module>
        <module>ruoyi-common-security</module>
        <module>ruoyi-common-sms</module>
        <module>ruoyi-common-web</module>
        <module>ruoyi-common-translation</module>
        <module>ruoyi-common-sensitive</module>
        <module>ruoyi-common-json</module>
        <module>ruoyi-common-encrypt</module>
        <module>ruoyi-common-tenant</module>
        <module>ruoyi-common-websocket</module>
        <module>ruoyi-common-sse</module>
    </modules>
    <artifactId>ruoyi-common</artifactId>
    <packaging>pom</packaging>
    <description>
        common é€šç”¨æ¨¡å—
    </description>
</project>
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-bom/pom.xml
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,185 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>org.dromara</groupId>
    <artifactId>ruoyi-common-bom</artifactId>
    <version>${revision}</version>
    <packaging>pom</packaging>
    <description>
        ruoyi-common-bom common依赖项
    </description>
    <properties>
        <revision>5.5.3</revision>
    </properties>
    <dependencyManagement>
        <dependencies>
            <!-- æ ¸å¿ƒæ¨¡å— -->
            <dependency>
                <groupId>org.dromara</groupId>
                <artifactId>ruoyi-common-core</artifactId>
                <version>${revision}</version>
            </dependency>
            <!-- æŽ¥å£æ¨¡å— -->
            <dependency>
                <groupId>org.dromara</groupId>
                <artifactId>ruoyi-common-doc</artifactId>
                <version>${revision}</version>
            </dependency>
            <!-- excel -->
            <dependency>
                <groupId>org.dromara</groupId>
                <artifactId>ruoyi-common-excel</artifactId>
                <version>${revision}</version>
            </dependency>
            <!-- å¹‚ç­‰ -->
            <dependency>
                <groupId>org.dromara</groupId>
                <artifactId>ruoyi-common-idempotent</artifactId>
                <version>${revision}</version>
            </dependency>
            <!-- è°ƒåº¦æ¨¡å— -->
            <dependency>
                <groupId>org.dromara</groupId>
                <artifactId>ruoyi-common-job</artifactId>
                <version>${revision}</version>
            </dependency>
            <!-- æ—¥å¿—记录 -->
            <dependency>
                <groupId>org.dromara</groupId>
                <artifactId>ruoyi-common-log</artifactId>
                <version>${revision}</version>
            </dependency>
            <!-- é‚®ä»¶æœåŠ¡ -->
            <dependency>
                <groupId>org.dromara</groupId>
                <artifactId>ruoyi-common-mail</artifactId>
                <version>${revision}</version>
            </dependency>
            <!-- æ•°æ®åº“服务 -->
            <dependency>
                <groupId>org.dromara</groupId>
                <artifactId>ruoyi-common-mybatis</artifactId>
                <version>${revision}</version>
            </dependency>
            <!-- OSS -->
            <dependency>
                <groupId>org.dromara</groupId>
                <artifactId>ruoyi-common-oss</artifactId>
                <version>${revision}</version>
            </dependency>
            <!-- é™æµ -->
            <dependency>
                <groupId>org.dromara</groupId>
                <artifactId>ruoyi-common-ratelimiter</artifactId>
                <version>${revision}</version>
            </dependency>
            <!-- ç¼“存服务 -->
            <dependency>
                <groupId>org.dromara</groupId>
                <artifactId>ruoyi-common-redis</artifactId>
                <version>${revision}</version>
            </dependency>
            <!-- satoken -->
            <dependency>
                <groupId>org.dromara</groupId>
                <artifactId>ruoyi-common-satoken</artifactId>
                <version>${revision}</version>
            </dependency>
            <!-- å®‰å…¨æ¨¡å— -->
            <dependency>
                <groupId>org.dromara</groupId>
                <artifactId>ruoyi-common-security</artifactId>
                <version>${revision}</version>
            </dependency>
            <!-- çŸ­ä¿¡æ¨¡å— -->
            <dependency>
                <groupId>org.dromara</groupId>
                <artifactId>ruoyi-common-sms</artifactId>
                <version>${revision}</version>
            </dependency>
            <dependency>
                <groupId>org.dromara</groupId>
                <artifactId>ruoyi-common-social</artifactId>
                <version>${revision}</version>
            </dependency>
            <!-- web服务 -->
            <dependency>
                <groupId>org.dromara</groupId>
                <artifactId>ruoyi-common-web</artifactId>
                <version>${revision}</version>
            </dependency>
            <!-- ç¿»è¯‘模块 -->
            <dependency>
                <groupId>org.dromara</groupId>
                <artifactId>ruoyi-common-translation</artifactId>
                <version>${revision}</version>
            </dependency>
            <!-- è„±æ•æ¨¡å— -->
            <dependency>
                <groupId>org.dromara</groupId>
                <artifactId>ruoyi-common-sensitive</artifactId>
                <version>${revision}</version>
            </dependency>
            <!-- åºåˆ—化模块 -->
            <dependency>
                <groupId>org.dromara</groupId>
                <artifactId>ruoyi-common-json</artifactId>
                <version>${revision}</version>
            </dependency>
            <!-- æ•°æ®åº“加解密模块 -->
            <dependency>
                <groupId>org.dromara</groupId>
                <artifactId>ruoyi-common-encrypt</artifactId>
                <version>${revision}</version>
            </dependency>
            <!-- ç§Ÿæˆ·æ¨¡å— -->
            <dependency>
                <groupId>org.dromara</groupId>
                <artifactId>ruoyi-common-tenant</artifactId>
                <version>${revision}</version>
            </dependency>
            <!-- WebSocket模块 -->
            <dependency>
                <groupId>org.dromara</groupId>
                <artifactId>ruoyi-common-websocket</artifactId>
                <version>${revision}</version>
            </dependency>
            <!-- SSE模块 -->
            <dependency>
                <groupId>org.dromara</groupId>
                <artifactId>ruoyi-common-sse</artifactId>
                <version>${revision}</version>
            </dependency>
        </dependencies>
    </dependencyManagement>
</project>
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-core/.flattened-pom.xml
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,73 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <modelVersion>4.0.0</modelVersion>
  <parent>
    <groupId>org.dromara</groupId>
    <artifactId>ruoyi-common</artifactId>
    <version>5.5.3</version>
  </parent>
  <groupId>org.dromara</groupId>
  <artifactId>ruoyi-common-core</artifactId>
  <version>5.5.3</version>
  <description>ruoyi-common-core æ ¸å¿ƒæ¨¡å—</description>
  <dependencies>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context-support</artifactId>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-web</artifactId>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-validation</artifactId>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-aop</artifactId>
    </dependency>
    <dependency>
      <groupId>org.apache.commons</groupId>
      <artifactId>commons-lang3</artifactId>
    </dependency>
    <dependency>
      <groupId>jakarta.servlet</groupId>
      <artifactId>jakarta.servlet-api</artifactId>
    </dependency>
    <dependency>
      <groupId>cn.hutool</groupId>
      <artifactId>hutool-core</artifactId>
    </dependency>
    <dependency>
      <groupId>cn.hutool</groupId>
      <artifactId>hutool-http</artifactId>
    </dependency>
    <dependency>
      <groupId>cn.hutool</groupId>
      <artifactId>hutool-extra</artifactId>
    </dependency>
    <dependency>
      <groupId>org.projectlombok</groupId>
      <artifactId>lombok</artifactId>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-configuration-processor</artifactId>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-properties-migrator</artifactId>
      <scope>runtime</scope>
    </dependency>
    <dependency>
      <groupId>io.github.linpeilie</groupId>
      <artifactId>mapstruct-plus-spring-boot-starter</artifactId>
    </dependency>
    <dependency>
      <groupId>org.lionsoul</groupId>
      <artifactId>ip2region</artifactId>
    </dependency>
  </dependencies>
</project>
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-core/pom.xml
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,99 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <groupId>org.dromara</groupId>
        <artifactId>ruoyi-common</artifactId>
        <version>${revision}</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>
    <artifactId>ruoyi-common-core</artifactId>
    <description>
        ruoyi-common-core æ ¸å¿ƒæ¨¡å—
    </description>
    <dependencies>
        <!-- Spring框架基本的核心工具 -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context-support</artifactId>
        </dependency>
        <!-- SpringWeb模块 -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-web</artifactId>
        </dependency>
        <!-- è‡ªå®šä¹‰éªŒè¯æ³¨è§£ -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-validation</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>
        <!--常用工具类 -->
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
        </dependency>
        <!-- servlet包 -->
        <dependency>
            <groupId>jakarta.servlet</groupId>
            <artifactId>jakarta.servlet-api</artifactId>
        </dependency>
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-core</artifactId>
        </dependency>
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-http</artifactId>
        </dependency>
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-extra</artifactId>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
        <!--  è‡ªåŠ¨ç”ŸæˆYML配置关联JSON文件  -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-properties-migrator</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>io.github.linpeilie</groupId>
            <artifactId>mapstruct-plus-spring-boot-starter</artifactId>
        </dependency>
        <!-- ç¦»çº¿IP地址定位库 -->
        <dependency>
            <groupId>org.lionsoul</groupId>
            <artifactId>ip2region</artifactId>
        </dependency>
    </dependencies>
</project>
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/config/ApplicationConfig.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,17 @@
package org.dromara.common.core.config;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.scheduling.annotation.EnableAsync;
/**
 * ç¨‹åºæ³¨è§£é…ç½®
 *
 * @author Lion Li
 */
@AutoConfiguration
@EnableAspectJAutoProxy
@EnableAsync(proxyTargetClass = true)
public class ApplicationConfig {
}
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/config/ThreadPoolConfig.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,109 @@
package org.dromara.common.core.config;
import jakarta.annotation.PreDestroy;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.concurrent.BasicThreadFactory;
import org.dromara.common.core.utils.SpringUtils;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.core.task.VirtualThreadTaskExecutor;
import java.util.concurrent.*;
/**
 * çº¿ç¨‹æ± é…ç½®
 *
 * @author Lion Li
 **/
@Slf4j
@AutoConfiguration
public class ThreadPoolConfig {
    /**
     * æ ¸å¿ƒçº¿ç¨‹æ•° = cpu æ ¸å¿ƒæ•° + 1
     */
    private final int core = Runtime.getRuntime().availableProcessors() + 1;
    private ScheduledExecutorService scheduledExecutorService;
    /**
     * æ‰§è¡Œå‘¨æœŸæ€§æˆ–定时任务
     */
    @Bean(name = "scheduledExecutorService")
    protected ScheduledExecutorService scheduledExecutorService() {
        // daemon å¿…须为 true
        BasicThreadFactory.Builder builder = new BasicThreadFactory.Builder().daemon(true);
        if (SpringUtils.isVirtual()) {
            builder.namingPattern("virtual-schedule-pool-%d").wrappedFactory(new VirtualThreadTaskExecutor().getVirtualThreadFactory());
        } else {
            builder.namingPattern("schedule-pool-%d");
        }
        ScheduledThreadPoolExecutor scheduledThreadPoolExecutor = new ScheduledThreadPoolExecutor(core,
            builder.build(),
            new ThreadPoolExecutor.CallerRunsPolicy()) {
            @Override
            protected void afterExecute(Runnable r, Throwable t) {
                super.afterExecute(r, t);
                printException(r, t);
            }
        };
        this.scheduledExecutorService = scheduledThreadPoolExecutor;
        return scheduledThreadPoolExecutor;
    }
    /**
     * é”€æ¯äº‹ä»¶
     * åœæ­¢çº¿ç¨‹æ± 
     * å…ˆä½¿ç”¨shutdown, åœæ­¢æŽ¥æ”¶æ–°ä»»åŠ¡å¹¶å°è¯•å®Œæˆæ‰€æœ‰å·²å­˜åœ¨ä»»åŠ¡.
     * å¦‚果超时, åˆ™è°ƒç”¨shutdownNow, å–消在workQueue中Pending的任务,并中断所有阻塞函数.
     * å¦‚果仍然超時,則強制退出.
     * å¦å¯¹åœ¨shutdown时线程本身被调用中断做了处理.
     */
    @PreDestroy
    public void destroy() {
        try {
            log.info("====关闭后台任务任务线程池====");
            ScheduledExecutorService pool = scheduledExecutorService;
            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();
                }
            }
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
    }
    /**
     * æ‰“印线程异常信息
     */
    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-Vue-Plus/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/config/ValidatorConfig.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,41 @@
package org.dromara.common.core.config;
import jakarta.validation.Validator;
import org.hibernate.validator.HibernateValidator;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.validation.ValidationAutoConfiguration;
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(before = ValidationAutoConfiguration.class)
public class ValidatorConfig {
    /**
     * é…ç½®æ ¡éªŒæ¡†æž¶ å¿«é€Ÿå¤±è´¥æ¨¡å¼
     */
    @Bean
    public Validator validator(MessageSource messageSource) {
        try (LocalValidatorFactoryBean factoryBean = new LocalValidatorFactoryBean()) {
            // å›½é™…化
            factoryBean.setValidationMessageSource(messageSource);
            // è®¾ç½®ä½¿ç”¨ HibernateValidator æ ¡éªŒå™¨
            factoryBean.setProviderClass(HibernateValidator.class);
            Properties properties = new Properties();
            // è®¾ç½®å¿«é€Ÿå¤±è´¥æ¨¡å¼ï¼ˆfail-fast),即校验过程中一旦遇到失败,立即停止并返回错误
            properties.setProperty("hibernate.validator.fail_fast", "true");
            factoryBean.setValidationProperties(properties);
            // åŠ è½½é…ç½®
            factoryBean.afterPropertiesSet();
            return factoryBean.getValidator();
        }
    }
}
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/constant/CacheConstants.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,30 @@
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:";
    /**
     * ç™»å½•账户密码错误次数 redis key
     */
    String PWD_ERR_CNT_KEY = "pwd_err_cnt:";
}
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/constant/CacheNames.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,89 @@
package org.dromara.common.core.constant;
/**
 * ç¼“存组名称常量
 * <p>
 * key æ ¼å¼ä¸º cacheNames#ttl#maxIdleTime#maxSize#local
 * <p>
 * ttl è¿‡æœŸæ—¶é—´ å¦‚果设置为0则不过期 é»˜è®¤ä¸º0
 * maxIdleTime æœ€å¤§ç©ºé—²æ—¶é—´ æ ¹æ®LRU算法清理空闲数据 å¦‚果设置为0则不检测 é»˜è®¤ä¸º0
 * maxSize ç»„最大长度 æ ¹æ®LRU算法清理溢出数据 å¦‚果设置为0则无限长 é»˜è®¤ä¸º0
 * local é»˜è®¤å¼€å¯æœ¬åœ°ç¼“存为1 å…³é—­æœ¬åœ°ç¼“存为0
 * <p>
 * ä¾‹å­: test#60s、test#0#60s、test#0#1m#1000、test#1h#0#500、test#1h#0#500#0
 *
 * @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_DICT_TYPE = "sys_dict_type";
    /**
     * ç§Ÿæˆ·
     */
    String SYS_TENANT = GlobalConstants.GLOBAL_REDIS_KEY + "sys_tenant#30d";
    /**
     * å®¢æˆ·ç«¯
     */
    String SYS_CLIENT = GlobalConstants.GLOBAL_REDIS_KEY + "sys_client#30d";
    /**
     * ç”¨æˆ·è´¦æˆ·
     */
    String SYS_USER_NAME = "sys_user_name#30d";
    /**
     * ç”¨æˆ·åç§°
     */
    String SYS_NICKNAME = "sys_nickname#30d";
    /**
     * éƒ¨é—¨
     */
    String SYS_DEPT = "sys_dept#30d";
    /**
     * OSS内容
     */
    String SYS_OSS = "sys_oss#30d";
    /**
     * è§’色自定义权限
     */
    String SYS_ROLE_CUSTOM = "sys_role_custom#30d";
    /**
     * éƒ¨é—¨åŠä»¥ä¸‹æƒé™
     */
    String SYS_DEPT_AND_CHILD = "sys_dept_and_child#30d";
    /**
     * OSS配置
     */
    String SYS_OSS_CONFIG = GlobalConstants.GLOBAL_REDIS_KEY + "sys_oss_config";
    /**
     * åœ¨çº¿ç”¨æˆ·
     */
    String ONLINE_TOKEN = "online_tokens";
}
RuoYi-Vue-Plus/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;
    /**
     * é¡¶çº§çˆ¶çº§id
     */
    Long TOP_PARENT_ID = 0L;
    /**
     * åР坆头
     */
    String ENCRYPT_HEADER = "ENC_";
}
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/constant/GlobalConstants.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,34 @@
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 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 SOCIAL_AUTH_CODE_KEY = GLOBAL_REDIS_KEY + "social_auth_codes:";
}
RuoYi-Vue-Plus/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-Vue-Plus/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/constant/RegexConstants.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,59 @@
package org.dromara.common.core.constant;
import cn.hutool.core.lang.RegexPool;
/**
 * å¸¸ç”¨æ­£åˆ™è¡¨è¾¾å¼å­—符串
 * <p>
 * å¸¸ç”¨æ­£åˆ™è¡¨è¾¾å¼é›†åˆï¼Œæ›´å¤šæ­£åˆ™è§: https://any86.github.io/any-rule/
 *
 * @author Feng
 */
public interface RegexConstants extends RegexPool {
    /**
     * å­—典类型必须以字母开头,且只能为(小写字母,数字,下滑线)
     */
    String DICTIONARY_TYPE = "^[a-z][a-z0-9_]*$";
    /**
     * æƒé™æ ‡è¯†å¿…须符合以下格式:
     * 1. æ ‡å‡†æ ¼å¼ï¼šxxx:yyy:zzz
     * - ç¬¬ä¸€éƒ¨åˆ†ï¼ˆxxx):只能包含字母、数字和下划线(_),不能使用 `*`
     * - ç¬¬äºŒéƒ¨åˆ†ï¼ˆyyy):可以包含字母、数字、下划线(_)和 `*`
     * - ç¬¬ä¸‰éƒ¨åˆ†ï¼ˆzzz):可以包含字母、数字、下划线(_)和 `*`
     * 2. å…è®¸ç©ºå­—符串(""),表示没有权限标识
     */
    String PERMISSION_STRING = "^$|^[a-zA-Z0-9_]+:[a-zA-Z0-9_*]+:[a-zA-Z0-9_*]+$";
    /**
     * èº«ä»½è¯å·ç ï¼ˆåŽ6位)
     */
    String ID_CARD_LAST_6 = "^(([0-2][1-9])|10|20|30|31)\\d{3}[0-9Xx]$";
    /**
     * QQ号码
     */
    String QQ_NUMBER = "^[1-9][0-9]\\d{4,9}$";
    /**
     * é‚®æ”¿ç¼–码
     */
    String POSTAL_CODE = "^[1-9]\\d{5}$";
    /**
     * æ³¨å†Œè´¦å·
     */
    String ACCOUNT = "^[a-zA-Z][a-zA-Z0-9_]{4,15}$";
    /**
     * å¯†ç ï¼šåŒ…含至少8个字符,包括大写字母、小写字母、数字和特殊字符
     */
    String PASSWORD = "^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)(?=.*[@$!%*?&])[A-Za-z\\d@$!%*?&]{8,}$";
    /**
     * é€šç”¨çŠ¶æ€ï¼ˆ0表示正常,1表示停用)
     */
    String STATUS = "^[01]$";
}
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/constant/SystemConstants.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,91 @@
package org.dromara.common.core.constant;
/**
 * ç³»ç»Ÿå¸¸é‡ä¿¡æ¯
 *
 * @author Lion Li
 */
public interface SystemConstants {
    /**
     * æ­£å¸¸çŠ¶æ€
     */
    String NORMAL = "0";
    /**
     * å¼‚常状态
     */
    String DISABLE = "1";
    /**
     * æ˜¯å¦ä¸ºç³»ç»Ÿé»˜è®¤ï¼ˆæ˜¯ï¼‰
     */
    String YES = "Y";
    /**
     * æ˜¯å¦ä¸ºç³»ç»Ÿé»˜è®¤ï¼ˆå¦ï¼‰
     */
    String NO = "N";
    /**
     * æ˜¯å¦èœå•外链(是)
     */
    String YES_FRAME = "0";
    /**
     * æ˜¯å¦èœå•外链(否)
     */
    String NO_FRAME = "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";
    /**
     * è¶…级管理员ID
     */
    Long SUPER_ADMIN_ID = 1L;
    /**
     * æ ¹éƒ¨é—¨ç¥–级列表
     */
    String ROOT_DEPT_ANCESTORS = "0";
    /**
     * é»˜è®¤éƒ¨é—¨ ID
     */
    Long DEFAULT_DEPT_ID = 100L;
    /**
     * æŽ’除敏感属性字段
     */
    String[] EXCLUDE_PROPERTIES = { "password", "oldPassword", "newPassword", "confirmPassword" };
}
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/constant/TenantConstants.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,35 @@
package org.dromara.common.core.constant;
/**
 * ç§Ÿæˆ·å¸¸é‡ä¿¡æ¯
 *
 * @author Lion Li
 */
public interface TenantConstants {
    /**
     * è¶…级管理员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-Vue-Plus/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-Vue-Plus/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/dto/CompleteTaskDTO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,77 @@
package org.dromara.common.core.domain.dto;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
/**
 * åŠžç†ä»»åŠ¡è¯·æ±‚å¯¹è±¡
 *
 * @author may
 */
@Data
public class CompleteTaskDTO implements Serializable {
    @Serial
    private static final long serialVersionUID = 1L;
    /**
     * ä»»åŠ¡id
     */
    private Long taskId;
    /**
     * é™„ä»¶id
     */
    private String fileId;
    /**
     * æŠ„送人员
     */
    private List<FlowCopyDTO> flowCopyList;
    /**
     * æ¶ˆæ¯ç±»åž‹
     */
    private List<String> messageType;
    /**
     * åŠžç†æ„è§
     */
    private String message;
    /**
     * æ¶ˆæ¯é€šçŸ¥
     */
    private String notice;
    /**
     * åŠžç†äºº(可不填 ç”¨äºŽè¦†ç›–当前节点办理人)
     */
    private String handler;
    /**
     * æµç¨‹å˜é‡
     */
    private Map<String, Object> variables;
    /**
     * æ‰©å±•变量(此处为逗号分隔的ossId)
     */
    private String ext;
    public Map<String, Object> getVariables() {
        if (variables == null) {
            variables = new HashMap<>(16);
            return variables;
        }
        variables.entrySet().removeIf(entry -> Objects.isNull(entry.getValue()));
        return variables;
    }
}
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/dto/DeptDTO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,36 @@
package org.dromara.common.core.domain.dto;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serial;
import java.io.Serializable;
/**
 * éƒ¨é—¨
 *
 * @author AprilWind
 */
@Data
@NoArgsConstructor
public class DeptDTO implements Serializable {
    @Serial
    private static final long serialVersionUID = 1L;
    /**
     * éƒ¨é—¨ID
     */
    private Long deptId;
    /**
     * çˆ¶éƒ¨é—¨ID
     */
    private Long parentId;
    /**
     * éƒ¨é—¨åç§°
     */
    private String deptName;
}
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/dto/DictDataDTO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,41 @@
package org.dromara.common.core.domain.dto;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serial;
import java.io.Serializable;
/**
 * å­—典数据DTO
 *
 * @author AprilWind
 */
@Data
@NoArgsConstructor
public class DictDataDTO implements Serializable {
    @Serial
    private static final long serialVersionUID = 1L;
    /**
     * å­—典标签
     */
    private String dictLabel;
    /**
     * å­—典键值
     */
    private String dictValue;
    /**
     * æ˜¯å¦é»˜è®¤ï¼ˆY是 N否)
     */
    private String isDefault;
    /**
     * å¤‡æ³¨
     */
    private String remark;
}
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/dto/DictTypeDTO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,41 @@
package org.dromara.common.core.domain.dto;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serial;
import java.io.Serializable;
/**
 * å­—典类型DTO
 *
 * @author AprilWind
 */
@Data
@NoArgsConstructor
public class DictTypeDTO implements Serializable {
    @Serial
    private static final long serialVersionUID = 1L;
    /**
     * å­—典主键
     */
    private Long dictId;
    /**
     * å­—典名称
     */
    private String dictName;
    /**
     * å­—典类型
     */
    private String dictType;
    /**
     * å¤‡æ³¨
     */
    private String remark;
}
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/dto/FlowCopyDTO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,30 @@
package org.dromara.common.core.domain.dto;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
/**
 * æŠ„送
 *
 * @author may
 */
@Data
public class FlowCopyDTO implements Serializable {
    @Serial
    private static final long serialVersionUID = 1L;
    /**
     * ç”¨æˆ·id
     */
    private Long userId;
    /**
     * ç”¨æˆ·åç§°
     */
    private String userName;
}
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/dto/FlowInstanceBizExtDTO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,45 @@
package org.dromara.common.core.domain.dto;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
/**
 * æµç¨‹å®žä¾‹ä¸šåŠ¡æ‰©å±•å¯¹è±¡
 *
 * @author may
 * @date 2025-08-05
 */
@Data
public class FlowInstanceBizExtDTO implements Serializable {
    @Serial
    private static final long serialVersionUID = 1L;
    /**
     * ä¸»é”®
     */
    private Long id;
    /**
     * æµç¨‹å®žä¾‹ID
     */
    private Long instanceId;
    /**
     * ä¸šåŠ¡ID
     */
    private String businessId;
    /**
     * ä¸šåŠ¡ç¼–ç 
     */
    private String businessCode;
    /**
     * ä¸šåŠ¡æ ‡é¢˜
     */
    private String businessTitle;
}
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/dto/OssDTO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,46 @@
package org.dromara.common.core.domain.dto;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serial;
import java.io.Serializable;
/**
 * OSS对象
 *
 * @author Lion Li
 */
@Data
@NoArgsConstructor
public class OssDTO implements Serializable {
    @Serial
    private static final long serialVersionUID = 1L;
    /**
     * å¯¹è±¡å­˜å‚¨ä¸»é”®
     */
    private Long ossId;
    /**
     * æ–‡ä»¶å
     */
    private String fileName;
    /**
     * åŽŸå
     */
    private String originalName;
    /**
     * æ–‡ä»¶åŽç¼€å
     */
    private String fileSuffix;
    /**
     * URL地址
     */
    private String url;
}
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/dto/PostDTO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,46 @@
package org.dromara.common.core.domain.dto;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serial;
import java.io.Serializable;
/**
 * å²—位
 *
 * @author AprilWind
 */
@Data
@NoArgsConstructor
public class PostDTO implements Serializable {
    @Serial
    private static final long serialVersionUID = 1L;
    /**
     * å²—位ID
     */
    private Long postId;
    /**
     * éƒ¨é—¨id
     */
    private Long deptId;
    /**
     * å²—位编码
     */
    private String postCode;
    /**
     * å²—位名称
     */
    private String postName;
    /**
     * å²—位类别编码
     */
    private String postCategory;
}
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/dto/RoleDTO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,42 @@
package org.dromara.common.core.domain.dto;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serial;
import java.io.Serializable;
/**
 * è§’色
 *
 * @author Lion Li
 */
@Data
@NoArgsConstructor
public class RoleDTO implements Serializable {
    @Serial
    private static final long serialVersionUID = 1L;
    /**
     * è§’色ID
     */
    private Long roleId;
    /**
     * è§’色名称
     */
    private String roleName;
    /**
     * è§’色权限
     */
    private String roleKey;
    /**
     * æ•°æ®èŒƒå›´ï¼ˆ1:全部数据权限 2:自定数据权限 3:本部门数据权限 4:本部门及以下数据权限 5:仅本人数据权限 6:部门及以下或本人数据权限)
     */
    private String dataScope;
}
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/dto/StartProcessDTO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,63 @@
package org.dromara.common.core.domain.dto;
import cn.hutool.core.util.ObjectUtil;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
/**
 * å¯åŠ¨æµç¨‹å¯¹è±¡
 *
 * @author may
 */
@Data
public class StartProcessDTO implements Serializable {
    @Serial
    private static final long serialVersionUID = 1L;
    /**
     * ä¸šåС唝䏀值id
     */
    private String businessId;
    /**
     * æµç¨‹å®šä¹‰ç¼–码
     */
    private String flowCode;
    /**
     * åŠžç†äºº(可不填 ç”¨äºŽè¦†ç›–当前节点办理人)
     */
    private String handler;
    /**
     * æµç¨‹å˜é‡ï¼Œå‰ç«¯ä¼šæäº¤ä¸€ä¸ªå…ƒç´ {'entity': {业务详情数据对象}}
     */
    private Map<String, Object> variables;
    /**
     * æµç¨‹ä¸šåŠ¡æ‰©å±•ä¿¡æ¯
     */
    private FlowInstanceBizExtDTO bizExt;
    public Map<String, Object> getVariables() {
        if (variables == null) {
            return new HashMap<>(16);
        }
        variables.entrySet().removeIf(entry -> Objects.isNull(entry.getValue()));
        return variables;
    }
    public FlowInstanceBizExtDTO getBizExt() {
        if (ObjectUtil.isNull(bizExt)) {
            bizExt = new FlowInstanceBizExtDTO();
        }
        return bizExt;
    }
}
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/dto/StartProcessReturnDTO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,30 @@
package org.dromara.common.core.domain.dto;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
/**
 * å¯åŠ¨æµç¨‹è¿”å›žå¯¹è±¡
 *
 * @author Lion Li
 */
@Data
public class StartProcessReturnDTO implements Serializable {
    @Serial
    private static final long serialVersionUID = 1L;
    /**
     * æµç¨‹å®žä¾‹id
     */
    private Long processInstanceId;
    /**
     * ä»»åŠ¡id
     */
    private Long taskId;
}
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/dto/TaskAssigneeDTO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,101 @@
package org.dromara.common.core.domain.dto;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serial;
import java.io.Serializable;
import java.util.Date;
import java.util.List;
import java.util.function.Function;
import java.util.stream.Collectors;
/**
 * ä»»åŠ¡å—è®©äºº
 *
 * @author AprilWind
 */
@Data
@NoArgsConstructor
public class TaskAssigneeDTO implements Serializable {
    @Serial
    private static final long serialVersionUID = 1L;
    /**
     * æ€»å¤§å°
     */
    private Long total = 0L;
    /**
     *
     */
    private List<TaskHandler> list;
    public TaskAssigneeDTO(Long total, List<TaskHandler> list) {
        this.total = total;
        this.list = list;
    }
    /**
     * å°†æºåˆ—表转换为 TaskHandler åˆ—表
     *
     * @param <T>              é€šç”¨ç±»åž‹
     * @param sourceList       å¾…转换的源列表
     * @param storageId        æå– storageId çš„函数
     * @param handlerCode      æå– handlerCode çš„函数
     * @param handlerName      æå– handlerName çš„函数
     * @param groupName        æå– groupName çš„函数
     * @param createTimeMapper æå– createTime çš„函数
     * @return è½¬æ¢åŽçš„ TaskHandler åˆ—表
     */
    public static <T> List<TaskHandler> convertToHandlerList(
        List<T> sourceList,
        Function<T, String> storageId,
        Function<T, String> handlerCode,
        Function<T, String> handlerName,
        Function<T, String> groupName,
        Function<T, Date> createTimeMapper) {
        return sourceList.stream()
            .map(item -> new TaskHandler(
                storageId.apply(item),
                handlerCode.apply(item),
                handlerName.apply(item),
                groupName.apply(item),
                createTimeMapper.apply(item)
            )).collect(Collectors.toList());
    }
    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    public static class TaskHandler {
        /**
         * ä¸»é”®
         */
        private String storageId;
        /**
         * æƒé™ç¼–码
         */
        private String handlerCode;
        /**
         * æƒé™åç§°
         */
        private String handlerName;
        /**
         * æƒé™åˆ†ç»„
         */
        private String groupName;
        /**
         * åˆ›å»ºæ—¶é—´
         */
        private Date createTime;
    }
}
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/dto/UserDTO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,73 @@
package org.dromara.common.core.domain.dto;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serial;
import java.io.Serializable;
import java.util.Date;
/**
 * ç”¨æˆ·
 *
 * @author Michelle.Chung
 */
@Data
@NoArgsConstructor
public class UserDTO implements Serializable {
    @Serial
    private static final long serialVersionUID = 1L;
    /**
     * ç”¨æˆ·ID
     */
    private Long userId;
    /**
     * éƒ¨é—¨ID
     */
    private Long deptId;
    /**
     * ç”¨æˆ·è´¦å·
     */
    private String userName;
    /**
     * ç”¨æˆ·æ˜µç§°
     */
    private String nickName;
    /**
     * ç”¨æˆ·ç±»åž‹ï¼ˆsys_user系统用户)
     */
    private String userType;
    /**
     * ç”¨æˆ·é‚®ç®±
     */
    private String email;
    /**
     * æ‰‹æœºå·ç 
     */
    private String phonenumber;
    /**
     * ç”¨æˆ·æ€§åˆ«ï¼ˆ0男 1女 2未知)
     */
    private String sex;
    /**
     * è´¦å·çŠ¶æ€ï¼ˆ0正常 1停用)
     */
    private String status;
    /**
     * åˆ›å»ºæ—¶é—´
     */
    private Date createTime;
}
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/dto/UserOnlineDTO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,72 @@
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;
    /**
     * å®¢æˆ·ç«¯
     */
    private String clientKey;
    /**
     * è®¾å¤‡ç±»åž‹
     */
    private String deviceType;
    /**
     * ç™»å½•IP地址
     */
    private String ipaddr;
    /**
     * ç™»å½•地址
     */
    private String loginLocation;
    /**
     * æµè§ˆå™¨ç±»åž‹
     */
    private String browser;
    /**
     * æ“ä½œç³»ç»Ÿ
     */
    private String os;
    /**
     * ç™»å½•æ—¶é—´
     */
    private Long loginTime;
}
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/event/ProcessDeleteEvent.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,34 @@
package org.dromara.common.core.domain.event;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
/**
 * åˆ é™¤æµç¨‹ç›‘听
 *
 * @author AprilWind
 */
@Data
public class ProcessDeleteEvent implements Serializable {
    @Serial
    private static final long serialVersionUID = 1L;
    /**
     * ç§Ÿæˆ·ID
     */
    private String tenantId;
    /**
     * æµç¨‹å®šä¹‰ç¼–码
     */
    private String flowCode;
    /**
     * ä¸šåŠ¡id
     */
    private String businessId;
}
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/event/ProcessEvent.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,70 @@
package org.dromara.common.core.domain.event;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
import java.util.Map;
/**
 * æ€»ä½“流程监听
 *
 * @author may
 */
@Data
public class ProcessEvent implements Serializable {
    @Serial
    private static final long serialVersionUID = 1L;
    /**
     * ç§Ÿæˆ·ID
     */
    private String tenantId;
    /**
     * æµç¨‹å®šä¹‰ç¼–码
     */
    private String flowCode;
    /**
     * å®žä¾‹id
     */
    private Long instanceId;
    /**
     * ä¸šåŠ¡id
     */
    private String businessId;
    /**
     * èŠ‚ç‚¹ç±»åž‹ï¼ˆ0开始节点 1中间节点 2结束节点 3互斥网关 4并行网关)
     */
    private Integer nodeType;
    /**
     * æµç¨‹èŠ‚ç‚¹ç¼–ç 
     */
    private String nodeCode;
    /**
     * æµç¨‹èŠ‚ç‚¹åç§°
     */
    private String nodeName;
    /**
     * æµç¨‹çŠ¶æ€
     */
    private String status;
    /**
     * åŠžç†å‚æ•°
     */
    private Map<String, Object> params;
    /**
     * å½“为true时为申请人节点办理
     */
    private Boolean submit;
}
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/event/ProcessTaskEvent.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,70 @@
package org.dromara.common.core.domain.event;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
import java.util.Map;
/**
 * æµç¨‹ä»»åŠ¡ç›‘å¬
 *
 * @author may
 */
@Data
public class ProcessTaskEvent implements Serializable {
    @Serial
    private static final long serialVersionUID = 1L;
    /**
     * ç§Ÿæˆ·ID
     */
    private String tenantId;
    /**
     * æµç¨‹å®šä¹‰ç¼–码
     */
    private String flowCode;
    /**
     * èŠ‚ç‚¹ç±»åž‹ï¼ˆ0开始节点 1中间节点 2结束节点 3互斥网关 4并行网关)
     */
    private Integer nodeType;
    /**
     * æµç¨‹èŠ‚ç‚¹ç¼–ç 
     */
    private String nodeCode;
    /**
     * æµç¨‹èŠ‚ç‚¹åç§°
     */
    private String nodeName;
    /**
     * ä»»åŠ¡id
     */
    private Long taskId;
    /**
     * å®žä¾‹id
     */
    private Long instanceId;
    /**
     * ä¸šåŠ¡id
     */
    private String businessId;
    /**
     * æµç¨‹çŠ¶æ€
     */
    private String status;
    /**
     * åŠžç†å‚æ•°
     */
    private Map<String, Object> params;
}
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/model/EmailLoginBody.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,31 @@
package org.dromara.common.core.domain.model;
import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.NotBlank;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
 * é‚®ä»¶ç™»å½•对象
 *
 * @author Lion Li
 */
@Data
@EqualsAndHashCode(callSuper = true)
public class EmailLoginBody extends LoginBody {
    /**
     * é‚®ç®±
     */
    @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-Vue-Plus/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 jakarta.validation.constraints.NotBlank;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
/**
 * ç”¨æˆ·ç™»å½•对象
 *
 * @author Lion Li
 */
@Data
public class LoginBody implements Serializable {
    @Serial
    private static final long serialVersionUID = 1L;
    /**
     * å®¢æˆ·ç«¯id
     */
    @NotBlank(message = "{auth.clientid.not.blank}")
    private String clientId;
    /**
     * æŽˆæƒç±»åž‹
     */
    @NotBlank(message = "{auth.grant.type.not.blank}")
    private String grantType;
    /**
     * ç§Ÿæˆ·ID
     */
    private String tenantId;
    /**
     * éªŒè¯ç 
     */
    private String code;
    /**
     * å”¯ä¸€æ ‡è¯†
     */
    private String uuid;
}
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/model/LoginUser.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,148 @@
package org.dromara.common.core.domain.model;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.dromara.common.core.domain.dto.PostDTO;
import org.dromara.common.core.domain.dto.RoleDTO;
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 deptCategory;
    /**
     * éƒ¨é—¨å
     */
    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 String nickname;
    /**
     * è§’色对象
     */
    private List<RoleDTO> roles;
    /**
     * å²—位对象
     */
    private List<PostDTO> posts;
    /**
     * æ•°æ®æƒé™ å½“前角色ID
     */
    private Long roleId;
    /**
     * å®¢æˆ·ç«¯
     */
    private String clientKey;
    /**
     * è®¾å¤‡ç±»åž‹
     */
    private String deviceType;
    /**
     * èŽ·å–ç™»å½•id
     */
    public String getLoginId() {
        if (userType == null) {
            throw new IllegalArgumentException("用户类型不能为空");
        }
        if (userId == null) {
            throw new IllegalArgumentException("用户ID不能为空");
        }
        return userType + ":" + userId;
    }
}
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/model/PasswordLoginBody.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,32 @@
package org.dromara.common.core.domain.model;
import jakarta.validation.constraints.NotBlank;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.hibernate.validator.constraints.Length;
/**
 * å¯†ç ç™»å½•对象
 *
 * @author Lion Li
 */
@Data
@EqualsAndHashCode(callSuper = true)
public class PasswordLoginBody extends LoginBody {
    /**
     * ç”¨æˆ·å
     */
    @NotBlank(message = "{user.username.not.blank}")
    @Length(min = 2, max = 30, message = "{user.username.length.valid}")
    private String username;
    /**
     * ç”¨æˆ·å¯†ç 
     */
    @NotBlank(message = "{user.password.not.blank}")
    @Length(min = 5, max = 30, message = "{user.password.length.valid}")
//    @Pattern(regexp = RegexConstants.PASSWORD, message = "{user.password.format.valid}")
    private String password;
}
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/model/RegisterBody.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,37 @@
package org.dromara.common.core.domain.model;
import jakarta.validation.constraints.NotBlank;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.hibernate.validator.constraints.Length;
/**
 * ç”¨æˆ·æ³¨å†Œå¯¹è±¡
 *
 * @author Lion Li
 */
@Data
@EqualsAndHashCode(callSuper = true)
public class RegisterBody extends LoginBody {
    /**
     * ç”¨æˆ·å
     */
    @NotBlank(message = "{user.username.not.blank}")
    @Length(min = 2, max = 30, message = "{user.username.length.valid}")
    private String username;
    /**
     * ç”¨æˆ·å¯†ç 
     */
    @NotBlank(message = "{user.password.not.blank}")
    @Length(min = 5, max = 30, message = "{user.password.length.valid}")
//    @Pattern(regexp = RegexConstants.PASSWORD, message = "{user.password.format.valid}")
    private String password;
    /**
     * ç”¨æˆ·ç±»åž‹
     */
    private String userType;
}
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/model/SmsLoginBody.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,29 @@
package org.dromara.common.core.domain.model;
import jakarta.validation.constraints.NotBlank;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
 * çŸ­ä¿¡ç™»å½•对象
 *
 * @author Lion Li
 */
@Data
@EqualsAndHashCode(callSuper = true)
public class SmsLoginBody extends LoginBody {
    /**
     * æ‰‹æœºå·
     */
    @NotBlank(message = "{user.phonenumber.not.blank}")
    private String phonenumber;
    /**
     * çŸ­ä¿¡code
     */
    @NotBlank(message = "{sms.code.not.blank}")
    private String smsCode;
}
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/model/SocialLoginBody.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,35 @@
package org.dromara.common.core.domain.model;
import jakarta.validation.constraints.NotBlank;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
 * ä¸‰æ–¹ç™»å½•对象
 *
 * @author Lion Li
 */
@Data
@EqualsAndHashCode(callSuper = true)
public class SocialLoginBody extends LoginBody {
    /**
     * ç¬¬ä¸‰æ–¹ç™»å½•平台
     */
    @NotBlank(message = "{social.source.not.blank}")
    private String source;
    /**
     * ç¬¬ä¸‰æ–¹ç™»å½•code
     */
    @NotBlank(message = "{social.code.not.blank}")
    private String socialCode;
    /**
     * ç¬¬ä¸‰æ–¹ç™»å½•socialState
     */
    @NotBlank(message = "{social.state.not.blank}")
    private String socialState;
}
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/model/TaskAssigneeBody.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,56 @@
package org.dromara.common.core.domain.model;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serial;
import java.io.Serializable;
/**
 * ä»»åŠ¡å—è®©äºº
 *
 * @author AprilWind
 */
@Data
@NoArgsConstructor
public class TaskAssigneeBody implements Serializable {
    @Serial
    private static final long serialVersionUID = 1L;
    /**
     * æƒé™ç¼–码
     */
    private String handlerCode;
    /**
     * æƒé™åç§°
     */
    private String handlerName;
    /**
     * æƒé™åˆ†ç»„
     */
    private String groupId;
    /**
     * å¼€å§‹æ—¶é—´
     */
    private String beginTime;
    /**
     * ç»“束时间
     */
    private String endTime;
    /**
     * å½“前页
     */
    private Integer pageNum = 1;
    /**
     * æ¯é¡µæ˜¾ç¤ºæ¡æ•°
     */
    private Integer pageSize = 10;
}
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/model/XcxLoginBody.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,28 @@
package org.dromara.common.core.domain.model;
import jakarta.validation.constraints.NotBlank;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
 * ä¸‰æ–¹ç™»å½•对象
 *
 * @author Lion Li
 */
@Data
@EqualsAndHashCode(callSuper = true)
public class XcxLoginBody extends LoginBody {
    /**
     * å°ç¨‹åºid(多个小程序时使用)
     */
    private String appid;
    /**
     * å°ç¨‹åºcode
     */
    @NotBlank(message = "{xcx.code.not.blank}")
    private String xcxCode;
}
RuoYi-Vue-Plus/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-Vue-Plus/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/enums/BusinessStatusEnum.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,215 @@
package org.dromara.common.core.enums;
import cn.hutool.core.util.StrUtil;
import lombok.AllArgsConstructor;
import lombok.Getter;
import org.dromara.common.core.exception.ServiceException;
import org.dromara.common.core.utils.StringUtils;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
/**
 * ä¸šåŠ¡çŠ¶æ€æžšä¸¾
 *
 * @author may
 */
@Getter
@AllArgsConstructor
public enum BusinessStatusEnum {
    /**
     * å·²æ’¤é”€
     */
    CANCEL("cancel", "已撤销"),
    /**
     * è‰ç¨¿
     */
    DRAFT("draft", "草稿"),
    /**
     * å¾…审核
     */
    WAITING("waiting", "待审核"),
    /**
     * å·²å®Œæˆ
     */
    FINISH("finish", "已完成"),
    /**
     * å·²ä½œåºŸ
     */
    INVALID("invalid", "已作废"),
    /**
     * å·²é€€å›ž
     */
    BACK("back", "已退回"),
    /**
     * å·²ç»ˆæ­¢
     */
    TERMINATION("termination", "已终止");
    /**
     * çŠ¶æ€
     */
    private final String status;
    /**
     * æè¿°
     */
    private final String desc;
    private static final Map<String, BusinessStatusEnum> STATUS_MAP = Arrays.stream(BusinessStatusEnum.values())
        .collect(Collectors.toConcurrentMap(BusinessStatusEnum::getStatus, Function.identity()));
    /**
     * æ ¹æ®çŠ¶æ€èŽ·å–å¯¹åº”çš„ BusinessStatusEnum æžšä¸¾
     *
     * @param status ä¸šåŠ¡çŠ¶æ€ç 
     * @return å¯¹åº”çš„ BusinessStatusEnum æžšä¸¾ï¼Œå¦‚果找不到则返回 null
     */
    public static BusinessStatusEnum getByStatus(String status) {
        // ä½¿ç”¨ STATUS_MAP èŽ·å–å¯¹åº”çš„æžšä¸¾ï¼Œè‹¥æ‰¾ä¸åˆ°åˆ™è¿”å›ž null
        return STATUS_MAP.get(status);
    }
    /**
     * æ ¹æ®çŠ¶æ€èŽ·å–å¯¹åº”çš„ä¸šåŠ¡çŠ¶æ€æè¿°ä¿¡æ¯
     *
     * @param status ä¸šåŠ¡çŠ¶æ€ç 
     * @return è¿”回业务状态描述,若状态码为空或未找到对应的枚举,返回空字符串
     */
    public static String findByStatus(String status) {
        if (StringUtils.isBlank(status)) {
            return StrUtil.EMPTY;
        }
        BusinessStatusEnum statusEnum = STATUS_MAP.get(status);
        return (statusEnum != null) ? statusEnum.getDesc() : StrUtil.EMPTY;
    }
    /**
     * åˆ¤æ–­æ˜¯å¦ä¸ºæŒ‡å®šçš„状态之一:草稿、已撤销或已退回
     *
     * @param status è¦æ£€æŸ¥çš„状态
     * @return å¦‚果状态为草稿、已撤销或已退回之一,则返回 true;否则返回 false
     */
    public static boolean isDraftOrCancelOrBack(String status) {
        return DRAFT.status.equals(status) || CANCEL.status.equals(status) || BACK.status.equals(status);
    }
    /**
     * åˆ¤æ–­æ˜¯å¦ä¸ºæ’¤é”€ï¼Œé€€å›žï¼Œä½œåºŸï¼Œç»ˆæ­¢
     *
     * @param status status
     * @return ç»“æžœ
     */
    public static boolean initialState(String status) {
        return CANCEL.status.equals(status) || BACK.status.equals(status) || INVALID.status.equals(status) || TERMINATION.status.equals(status);
    }
    /**
     * èŽ·å–è¿è¡Œä¸­çš„å®žä¾‹çŠ¶æ€åˆ—è¡¨
     *
     * @return åŒ…含运行中实例状态的不可变列表
     * ï¼ˆåŒ…含 DRAFT、WAITING、BACK å’Œ CANCEL çŠ¶æ€ï¼‰
     */
    public static List<String> runningStatus() {
        return Arrays.asList(DRAFT.status, WAITING.status, BACK.status, CANCEL.status);
    }
    /**
     * èŽ·å–ç»“æŸå®žä¾‹çš„çŠ¶æ€åˆ—è¡¨
     *
     * @return åŒ…含结束实例状态的不可变列表
     * ï¼ˆåŒ…含 FINISH、INVALID å’Œ TERMINATION çŠ¶æ€ï¼‰
     */
    public static List<String> finishStatus() {
        return Arrays.asList(FINISH.status, INVALID.status, TERMINATION.status);
    }
    /**
     * å¯åŠ¨æµç¨‹æ ¡éªŒ
     *
     * @param status çŠ¶æ€
     */
    public static void checkStartStatus(String status) {
        if (WAITING.getStatus().equals(status)) {
            throw new ServiceException("该单据已提交过申请,正在审批中!");
        } else if (FINISH.getStatus().equals(status)) {
            throw new ServiceException("该单据已完成申请!");
        } else if (INVALID.getStatus().equals(status)) {
            throw new ServiceException("该单据已作废!");
        } else if (TERMINATION.getStatus().equals(status)) {
            throw new ServiceException("该单据已终止!");
        } else if (StringUtils.isBlank(status)) {
            throw new ServiceException("流程状态为空!");
        }
    }
    /**
     * æ’¤é”€æµç¨‹æ ¡éªŒ
     *
     * @param status çŠ¶æ€
     */
    public static void checkCancelStatus(String status) {
        if (CANCEL.getStatus().equals(status)) {
            throw new ServiceException("该单据已撤销!");
        } else if (FINISH.getStatus().equals(status)) {
            throw new ServiceException("该单据已完成申请!");
        } else if (INVALID.getStatus().equals(status)) {
            throw new ServiceException("该单据已作废!");
        } else if (TERMINATION.getStatus().equals(status)) {
            throw new ServiceException("该单据已终止!");
        } else if (BACK.getStatus().equals(status)) {
            throw new ServiceException("该单据已退回!");
        } else if (StringUtils.isBlank(status)) {
            throw new ServiceException("流程状态为空!");
        }
    }
    /**
     * é©³å›žæµç¨‹æ ¡éªŒ
     *
     * @param status çŠ¶æ€
     */
    public static void checkBackStatus(String status) {
        if (BACK.getStatus().equals(status)) {
            throw new ServiceException("该单据已退回!");
        } else if (FINISH.getStatus().equals(status)) {
            throw new ServiceException("该单据已完成申请!");
        } else if (INVALID.getStatus().equals(status)) {
            throw new ServiceException("该单据已作废!");
        } else if (TERMINATION.getStatus().equals(status)) {
            throw new ServiceException("该单据已终止!");
        } else if (CANCEL.getStatus().equals(status)) {
            throw new ServiceException("该单据已撤销!");
        } else if (StringUtils.isBlank(status)) {
            throw new ServiceException("流程状态为空!");
        }
    }
    /**
     * ä½œåºŸ,终止流程校验
     *
     * @param status çŠ¶æ€
     */
    public static void checkInvalidStatus(String status) {
        if (FINISH.getStatus().equals(status)) {
            throw new ServiceException("该单据已完成申请!");
        } else if (INVALID.getStatus().equals(status)) {
            throw new ServiceException("该单据已作废!");
        } else if (TERMINATION.getStatus().equals(status)) {
            throw new ServiceException("该单据已终止!");
        } else if (StringUtils.isBlank(status)) {
            throw new ServiceException("流程状态为空!");
        }
    }
}
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/enums/DeviceType.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,39 @@
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"),
    /**
     * ç¬¬ä¸‰æ–¹ç¤¾äº¤ç™»å½•平台
     */
    SOCIAL("social");
    /**
     * è®¾å¤‡æ ‡è¯†
     */
    private final String device;
}
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/enums/FormatsType.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,146 @@
package org.dromara.common.core.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
import org.dromara.common.core.utils.StringUtils;
/*
 * æ—¥æœŸæ ¼å¼
 * "yyyy":4位数的年份,例如:2023年表示为"2023"。
 * "yy":2位数的年份,例如:2023年表示为"23"。
 * "MM":2位数的月份,取值范围为01到12,例如:7月表示为"07"。
 * "M":不带前导零的月份,取值范围为1到12,例如:7月表示为"7"。
 * "dd":2位数的日期,取值范围为01到31,例如:22日表示为"22"。
 * "d":不带前导零的日期,取值范围为1到31,例如:22日表示为"22"。
 * "EEEE":星期的全名,例如:星期三表示为"Wednesday"。
 * "E":星期的缩写,例如:星期三表示为"Wed"。
 * "DDD" æˆ– "D":一年中的第几天,取值范围为001到366,例如:第200天表示为"200"。
 * æ—¶é—´æ ¼å¼
 * "HH":24小时制的小时数,取值范围为00到23,例如:下午5点表示为"17"。
 * "hh":12小时制的小时数,取值范围为01到12,例如:下午5点表示为"05"。
 * "mm":分钟数,取值范围为00到59,例如:30分钟表示为"30"。
 * "ss":秒数,取值范围为00到59,例如:45秒表示为"45"。
 * "SSS":毫秒数,取值范围为000到999,例如:123毫秒表示为"123"。
 */
/**
 * æ—¥æœŸæ ¼å¼ä¸Žæ—¶é—´æ ¼å¼æžšä¸¾
 */
@Getter
@AllArgsConstructor
public enum FormatsType {
    /**
     * ä¾‹å¦‚:2023年表示为"23"
     */
    YY("yy"),
    /**
     * ä¾‹å¦‚:2023年表示为"2023"
     */
    YYYY("yyyy"),
    /**
     * ä¾‹ä¾‹å¦‚,2023å¹´7月可以表示为 "2023-07"
     */
    YYYY_MM("yyyy-MM"),
    /**
     * ä¾‹å¦‚,日期 "2023å¹´7月22日" å¯ä»¥è¡¨ç¤ºä¸º "2023-07-22"
     */
    YYYY_MM_DD("yyyy-MM-dd"),
    /**
     * ä¾‹å¦‚,当前时间如果是 "2023å¹´7月22日下午3点30分",则可以表示为 "2023-07-22 15:30"
     */
    YYYY_MM_DD_HH_MM("yyyy-MM-dd HH:mm"),
    /**
     * ä¾‹å¦‚,当前时间如果是 "2023å¹´7月22日下午3点30分45秒",则可以表示为 "2023-07-22 15:30:45"
     */
    YYYY_MM_DD_HH_MM_SS("yyyy-MM-dd HH:mm:ss"),
    /**
     * ä¾‹å¦‚:下午3点30分45秒,表示为 "15:30:45"
     */
    HH_MM_SS("HH:mm:ss"),
    /**
     * ä¾‹ä¾‹å¦‚,2023å¹´7月可以表示为 "2023/07"
     */
    YYYY_MM_SLASH("yyyy/MM"),
    /**
     * ä¾‹å¦‚,日期 "2023å¹´7月22日" å¯ä»¥è¡¨ç¤ºä¸º "2023/07/22"
     */
    YYYY_MM_DD_SLASH("yyyy/MM/dd"),
    /**
     * ä¾‹å¦‚,当前时间如果是 "2023å¹´7月22日下午3点30分45秒",则可以表示为 "2023/07/22 15:30:45"
     */
    YYYY_MM_DD_HH_MM_SLASH("yyyy/MM/dd HH:mm"),
    /**
     * ä¾‹å¦‚,当前时间如果是 "2023å¹´7月22日下午3点30分45秒",则可以表示为 "2023/07/22 15:30:45"
     */
    YYYY_MM_DD_HH_MM_SS_SLASH("yyyy/MM/dd HH:mm:ss"),
    /**
     * ä¾‹ä¾‹å¦‚,2023å¹´7月可以表示为 "2023.07"
     */
    YYYY_MM_DOT("yyyy.MM"),
    /**
     * ä¾‹å¦‚,日期 "2023å¹´7月22日" å¯ä»¥è¡¨ç¤ºä¸º "2023.07.22"
     */
    YYYY_MM_DD_DOT("yyyy.MM.dd"),
    /**
     * ä¾‹å¦‚,当前时间如果是 "2023å¹´7月22日下午3点30分",则可以表示为 "2023.07.22 15:30"
     */
    YYYY_MM_DD_HH_MM_DOT("yyyy.MM.dd HH:mm"),
    /**
     * ä¾‹å¦‚,当前时间如果是 "2023å¹´7月22日下午3点30分45秒",则可以表示为 "2023.07.22 15:30:45"
     */
    YYYY_MM_DD_HH_MM_SS_DOT("yyyy.MM.dd HH:mm:ss"),
    /**
     * ä¾‹å¦‚,2023å¹´7月可以表示为 "202307"
     */
    YYYYMM("yyyyMM"),
    /**
     * ä¾‹å¦‚,2023å¹´7月22日可以表示为 "20230722"
     */
    YYYYMMDD("yyyyMMdd"),
    /**
     * ä¾‹å¦‚,2023å¹´7月22日下午3点可以表示为 "2023072215"
     */
    YYYYMMDDHH("yyyyMMddHH"),
    /**
     * ä¾‹å¦‚,2023å¹´7月22日下午3点30分可以表示为 "202307221530"
     */
    YYYYMMDDHHMM("yyyyMMddHHmm"),
    /**
     * ä¾‹å¦‚,2023å¹´7月22日下午3点30分45秒可以表示为 "20230722153045"
     */
    YYYYMMDDHHMMSS("yyyyMMddHHmmss");
    /**
     * æ—¶é—´æ ¼å¼
     */
    private final String timeFormat;
    public static FormatsType getFormatsType(String str) {
        for (FormatsType value : values()) {
            if (StringUtils.contains(str, value.getTimeFormat())) {
                return value;
            }
        }
        throw new RuntimeException("'FormatsType' not found By " + str);
    }
}
RuoYi-Vue-Plus/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-Vue-Plus/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-Vue-Plus/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/enums/UserType.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,39 @@
package org.dromara.common.core.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
import org.dromara.common.core.utils.StringUtils;
/**
 * ç”¨æˆ·ç±»åž‹
 *
 * @author Lion Li
 */
@Getter
@AllArgsConstructor
public enum UserType {
    /**
     * åŽå°ç³»ç»Ÿç”¨æˆ·
     */
    SYS_USER("sys_user"),
    /**
     * ç§»åŠ¨å®¢æˆ·ç«¯ç”¨æˆ·
     */
    APP_USER("app_user");
    /**
     * ç”¨æˆ·ç±»åž‹æ ‡è¯†ï¼ˆç”¨äºŽ token、权限识别等)
     */
    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-Vue-Plus/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 cn.hutool.core.text.StrFormatter;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import java.io.Serial;
/**
 * ä¸šåŠ¡å¼‚å¸¸ï¼ˆæ”¯æŒå ä½ç¬¦ {} ï¼‰
 *
 * @author ruoyi
 */
@Data
@EqualsAndHashCode(callSuper = true)
@NoArgsConstructor
@AllArgsConstructor
public final class ServiceException extends RuntimeException {
    @Serial
    private static final long serialVersionUID = 1L;
    /**
     * é”™è¯¯ç 
     */
    private Integer code;
    /**
     * é”™è¯¯æç¤º
     */
    private String message;
    /**
     * é”™è¯¯æ˜Žç»†ï¼Œå†…部调试错误
     */
    private String detailMessage;
    public ServiceException(String message) {
        this.message = message;
    }
    public ServiceException(String message, Integer code) {
        this.message = message;
        this.code = code;
    }
    public ServiceException(String message, Object... args) {
        this.message = StrFormatter.format(message, args);
    }
    @Override
    public String getMessage() {
        return message;
    }
    public ServiceException setMessage(String message) {
        this.message = message;
        return this;
    }
    public ServiceException setDetailMessage(String detailMessage) {
        this.detailMessage = detailMessage;
        return this;
    }
}
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/exception/SseException.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,62 @@
package org.dromara.common.core.exception;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import java.io.Serial;
/**
 * sse ç‰¹åˆ¶å¼‚常
 *
 * @author LionLi
 */
@Data
@EqualsAndHashCode(callSuper = true)
@NoArgsConstructor
@AllArgsConstructor
public final class SseException extends RuntimeException {
    @Serial
    private static final long serialVersionUID = 1L;
    /**
     * é”™è¯¯ç 
     */
    private Integer code;
    /**
     * é”™è¯¯æç¤º
     */
    private String message;
    /**
     * é”™è¯¯æ˜Žç»†ï¼Œå†…部调试错误
     */
    private String detailMessage;
    public SseException(String message) {
        this.message = message;
    }
    public SseException(String message, Integer code) {
        this.message = message;
        this.code = code;
    }
    @Override
    public String getMessage() {
        return message;
    }
    public SseException setMessage(String message) {
        this.message = message;
        return this;
    }
    public SseException setDetailMessage(String detailMessage) {
        this.detailMessage = detailMessage;
        return this;
    }
}
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/exception/base/BaseException.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,74 @@
package org.dromara.common.core.exception.base;
import lombok.AllArgsConstructor;
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
@AllArgsConstructor
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) {
        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-Vue-Plus/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-Vue-Plus/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-Vue-Plus/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-Vue-Plus/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-Vue-Plus/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-Vue-Plus/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-Vue-Plus/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/factory/RegexPatternPoolFactory.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,52 @@
package org.dromara.common.core.factory;
import cn.hutool.core.lang.PatternPool;
import org.dromara.common.core.constant.RegexConstants;
import java.util.regex.Pattern;
/**
 * æ­£åˆ™è¡¨è¾¾å¼æ¨¡å¼æ± å·¥åŽ‚
 * <p>初始化的时候将正则表达式加入缓存池当中</p>
 * <p>提高正则表达式的性能,避免重复编译相同的正则表达式</p>
 *
 * @author 21001
 */
public class RegexPatternPoolFactory extends PatternPool {
    /**
     * å­—典类型必须以字母开头,且只能为(小写字母,数字,下滑线)
     */
    public static final Pattern DICTIONARY_TYPE = get(RegexConstants.DICTIONARY_TYPE);
    /**
     * èº«ä»½è¯å·ç ï¼ˆåŽ6位)
     */
    public static final Pattern ID_CARD_LAST_6 = get(RegexConstants.ID_CARD_LAST_6);
    /**
     * QQ号码
     */
    public static final Pattern QQ_NUMBER = get(RegexConstants.QQ_NUMBER);
    /**
     * é‚®æ”¿ç¼–码
     */
    public static final Pattern POSTAL_CODE = get(RegexConstants.POSTAL_CODE);
    /**
     * æ³¨å†Œè´¦å·
     */
    public static final Pattern ACCOUNT = get(RegexConstants.ACCOUNT);
    /**
     * å¯†ç ï¼šåŒ…含至少8个字符,包括大写字母、小写字母、数字和特殊字符
     */
    public static final Pattern PASSWORD = get(RegexConstants.PASSWORD);
    /**
     * é€šç”¨çŠ¶æ€ï¼ˆ0表示正常,1表示停用)
     */
    public static final Pattern STATUS = get(RegexConstants.STATUS);
}
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/factory/YmlPropertySourceFactory.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,31 @@
package org.dromara.common.core.factory;
import org.dromara.common.core.utils.StringUtils;
import org.springframework.beans.factory.config.YamlPropertiesFactoryBean;
import org.springframework.core.env.PropertiesPropertySource;
import org.springframework.core.env.PropertySource;
import org.springframework.core.io.support.DefaultPropertySourceFactory;
import org.springframework.core.io.support.EncodedResource;
import java.io.IOException;
/**
 * yml é…ç½®æºå·¥åŽ‚
 *
 * @author Lion Li
 */
public class YmlPropertySourceFactory extends DefaultPropertySourceFactory {
    @Override
    public PropertySource<?> createPropertySource(String name, EncodedResource resource) throws IOException {
        String sourceName = resource.getResource().getFilename();
        if (StringUtils.isNotBlank(sourceName) && StringUtils.endsWithAny(sourceName, ".yml", ".yaml")) {
            YamlPropertiesFactoryBean factory = new YamlPropertiesFactoryBean();
            factory.setResources(resource.getResource());
            factory.afterPropertiesSet();
            return new PropertiesPropertySource(sourceName, factory.getObject());
        }
        return super.createPropertySource(name, resource);
    }
}
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/service/ConfigService.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,100 @@
package org.dromara.common.core.service;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.lang.Dict;
import java.math.BigDecimal;
import java.util.List;
/**
 * é€šç”¨ å‚数配置服务
 *
 * @author Lion Li
 */
public interface ConfigService {
    /**
     * æ ¹æ®å‚æ•° key èŽ·å–å‚æ•°å€¼
     *
     * @param configKey å‚æ•° key
     * @return å‚数值
     */
    String getConfigValue(String configKey);
    /**
     * æ ¹æ®å‚æ•° key èŽ·å–å¸ƒå°”å€¼
     *
     * @param configKey å‚æ•° key
     * @return Boolean å€¼
     */
    default Boolean getConfigBool(String configKey) {
        return Convert.toBool(getConfigValue(configKey));
    }
    /**
     * æ ¹æ®å‚æ•° key èŽ·å–æ•´æ•°å€¼
     *
     * @param configKey å‚æ•° key
     * @return Integer å€¼
     */
    default Integer getConfigInt(String configKey) {
        return Convert.toInt(getConfigValue(configKey));
    }
    /**
     * æ ¹æ®å‚æ•° key èŽ·å–é•¿æ•´åž‹å€¼
     *
     * @param configKey å‚æ•° key
     * @return Long å€¼
     */
    default Long getConfigLong(String configKey) {
        return Convert.toLong(getConfigValue(configKey));
    }
    /**
     * æ ¹æ®å‚æ•° key èŽ·å– BigDecimal å€¼
     *
     * @param configKey å‚æ•° key
     * @return BigDecimal å€¼
     */
    default BigDecimal getConfigDecimal(String configKey) {
        return Convert.toBigDecimal(getConfigValue(configKey));
    }
    /**
     * æ ¹æ®å‚æ•° key èŽ·å– Map ç±»åž‹çš„配置
     *
     * @param configKey å‚æ•° key
     * @return Dict å¯¹è±¡ï¼Œå¦‚果配置为空或无法解析,返回空 Dict
     */
    Dict getConfigMap(String configKey);
    /**
     * æ ¹æ®å‚æ•° key èŽ·å– Map ç±»åž‹çš„配置列表
     *
     * @param configKey å‚æ•° key
     * @return Dict åˆ—表,如果配置为空或无法解析,返回空列表
     */
    List<Dict> getConfigArrayMap(String configKey);
    /**
     * æ ¹æ®å‚æ•° key èŽ·å–æŒ‡å®šç±»åž‹çš„é…ç½®å¯¹è±¡
     *
     * @param configKey å‚æ•° key
     * @param clazz     ç›®æ ‡å¯¹è±¡ç±»åž‹
     * @param <T>       ç›®æ ‡å¯¹è±¡æ³›åž‹
     * @return å¯¹è±¡å®žä¾‹ï¼Œå¦‚果配置为空或无法解析,返回 null
     */
    <T> T getConfigObject(String configKey, Class<T> clazz);
    /**
     * æ ¹æ®å‚æ•° key èŽ·å–æŒ‡å®šç±»åž‹çš„é…ç½®åˆ—è¡¨
     *
     * @param configKey å‚æ•° key
     * @param clazz     ç›®æ ‡å…ƒç´ ç±»åž‹
     * @param <T>       å…ƒç´ ç±»åž‹æ³›åž‹
     * @return æŒ‡å®šç±»åž‹åˆ—表,如果配置为空或无法解析,返回空列表
     */
    <T> List<T> getConfigArray(String configKey, Class<T> clazz);
}
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/service/DeptService.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,46 @@
package org.dromara.common.core.service;
import org.dromara.common.core.domain.dto.DeptDTO;
import java.util.List;
import java.util.Map;
/**
 * é€šç”¨ éƒ¨é—¨æœåŠ¡
 *
 * @author Lion Li
 */
public interface DeptService {
    /**
     * é€šè¿‡éƒ¨é—¨ID查询部门名称
     *
     * @param deptIds éƒ¨é—¨ID串逗号分隔
     * @return éƒ¨é—¨åç§°ä¸²é€—号分隔
     */
    String selectDeptNameByIds(String deptIds);
    /**
     * æ ¹æ®éƒ¨é—¨ID查询部门负责人
     *
     * @param deptId éƒ¨é—¨ID,用于指定需要查询的部门
     * @return è¿”回该部门的负责人ID
     */
    Long selectDeptLeaderById(Long deptId);
    /**
     * æŸ¥è¯¢éƒ¨é—¨
     *
     * @return éƒ¨é—¨åˆ—表
     */
    List<DeptDTO> selectDeptsByList();
    /**
     * æ ¹æ®éƒ¨é—¨ ID åˆ—表查询部门名称映射关系
     *
     * @param deptIds éƒ¨é—¨ ID åˆ—表
     * @return Map,其中 key ä¸ºéƒ¨é—¨ ID,value ä¸ºå¯¹åº”的部门名称
     */
    Map<Long, String> selectDeptNamesByIds(List<Long> deptIds);
}
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/service/DictService.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,87 @@
package org.dromara.common.core.service;
import org.dromara.common.core.domain.dto.DictDataDTO;
import org.dromara.common.core.domain.dto.DictTypeDTO;
import java.util.List;
import java.util.Map;
/**
 * é€šç”¨ å­—典服务
 *
 * @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);
    /**
     * èŽ·å–å­—å…¸ä¸‹æ‰€æœ‰çš„å­—å…¸å€¼ä¸Žæ ‡ç­¾
     *
     * @param dictType å­—典类型
     * @return dictValue为key,dictLabel为值组成的Map
     */
    Map<String, String> getAllDictByDictType(String dictType);
    /**
     * æ ¹æ®å­—典类型查询详细信息
     *
     * @param dictType å­—典类型
     * @return å­—典类型详细信息
     */
    DictTypeDTO getDictType(String dictType);
    /**
     * æ ¹æ®å­—典类型查询字典数据列表
     *
     * @param dictType å­—典类型
     * @return å­—典数据列表
     */
    List<DictDataDTO> getDictData(String dictType);
}
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/service/OssService.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,29 @@
package org.dromara.common.core.service;
import org.dromara.common.core.domain.dto.OssDTO;
import java.util.List;
/**
 * é€šç”¨ OSS服务
 *
 * @author Lion Li
 */
public interface OssService {
    /**
     * é€šè¿‡ossId查询对应的url
     *
     * @param ossIds ossId串逗号分隔
     * @return url串逗号分隔
     */
    String selectUrlByIds(String ossIds);
    /**
     * é€šè¿‡ossId查询列表
     *
     * @param ossIds ossId串逗号分隔
     * @return åˆ—表
     */
    List<OssDTO> selectByIds(String ossIds);
}
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/service/PermissionService.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,28 @@
package org.dromara.common.core.service;
import java.util.Set;
/**
 * ç”¨æˆ·æƒé™å¤„理
 *
 * @author Lion Li
 */
public interface PermissionService {
    /**
     * èŽ·å–è§’è‰²æ•°æ®æƒé™
     *
     * @param userId  ç”¨æˆ·id
     * @return è§’色权限信息
     */
    Set<String> getRolePermission(Long userId);
    /**
     * èŽ·å–èœå•æ•°æ®æƒé™
     *
     * @param userId  ç”¨æˆ·id
     * @return èœå•权限信息
     */
    Set<String> getMenuPermission(Long userId);
}
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/service/PostService.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,21 @@
package org.dromara.common.core.service;
import java.util.List;
import java.util.Map;
/**
 * é€šç”¨ å²—位服务
 *
 * @author AprilWind
 */
public interface PostService {
    /**
     * æ ¹æ®å²—位 ID åˆ—表查询岗位名称映射关系
     *
     * @param postIds å²—位 ID åˆ—表
     * @return Map,其中 key ä¸ºå²—位 ID,value ä¸ºå¯¹åº”的岗位名称
     */
    Map<Long, String> selectPostNamesByIds(List<Long> postIds);
}
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/service/RoleService.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,21 @@
package org.dromara.common.core.service;
import java.util.List;
import java.util.Map;
/**
 * é€šç”¨ è§’色服务
 *
 * @author AprilWind
 */
public interface RoleService {
    /**
     * æ ¹æ®è§’色 ID åˆ—表查询角色名称映射关系
     *
     * @param roleIds è§’色 ID åˆ—表
     * @return Map,其中 key ä¸ºè§’色 ID,value ä¸ºå¯¹åº”的角色名称
     */
    Map<Long, String> selectRoleNamesByIds(List<Long> roleIds);
}
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/service/TaskAssigneeService.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,45 @@
package org.dromara.common.core.service;
import org.dromara.common.core.domain.dto.TaskAssigneeDTO;
import org.dromara.common.core.domain.model.TaskAssigneeBody;
/**
 * å·¥ä½œæµè®¾è®¡å™¨èŽ·å–ä»»åŠ¡æ‰§è¡Œäºº
 *
 * @author Lion Li
 */
public interface TaskAssigneeService {
    /**
     * æŸ¥è¯¢è§’色并返回任务指派的列表,支持分页
     *
     * @param taskQuery æŸ¥è¯¢æ¡ä»¶
     * @return åŠžç†äºº
     */
    TaskAssigneeDTO selectRolesByTaskAssigneeList(TaskAssigneeBody taskQuery);
    /**
     * æŸ¥è¯¢å²—位并返回任务指派的列表,支持分页
     *
     * @param taskQuery æŸ¥è¯¢æ¡ä»¶
     * @return åŠžç†äºº
     */
    TaskAssigneeDTO selectPostsByTaskAssigneeList(TaskAssigneeBody taskQuery);
    /**
     * æŸ¥è¯¢éƒ¨é—¨å¹¶è¿”回任务指派的列表,支持分页
     *
     * @param taskQuery æŸ¥è¯¢æ¡ä»¶
     * @return åŠžç†äºº
     */
    TaskAssigneeDTO selectDeptsByTaskAssigneeList(TaskAssigneeBody taskQuery);
    /**
     * æŸ¥è¯¢ç”¨æˆ·å¹¶è¿”回任务指派的列表,支持分页
     *
     * @param taskQuery æŸ¥è¯¢æ¡ä»¶
     * @return åŠžç†äºº
     */
    TaskAssigneeDTO selectUsersByTaskAssigneeList(TaskAssigneeBody taskQuery);
}
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/service/UserService.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,103 @@
package org.dromara.common.core.service;
import org.dromara.common.core.domain.dto.UserDTO;
import java.util.List;
import java.util.Map;
/**
 * é€šç”¨ ç”¨æˆ·æœåŠ¡
 *
 * @author Lion Li
 */
public interface UserService {
    /**
     * é€šè¿‡ç”¨æˆ·ID查询用户账户
     *
     * @param userId ç”¨æˆ·ID
     * @return ç”¨æˆ·è´¦æˆ·
     */
    String selectUserNameById(Long userId);
    /**
     * é€šè¿‡ç”¨æˆ·ID查询用户账户
     *
     * @param userId ç”¨æˆ·ID
     * @return ç”¨æˆ·åç§°
     */
    String selectNicknameById(Long userId);
    /**
     * é€šè¿‡ç”¨æˆ·ID查询用户账户
     *
     * @param userIds ç”¨æˆ·ID å¤šä¸ªç”¨é€—号隔开
     * @return ç”¨æˆ·åç§°
     */
    String selectNicknameByIds(String userIds);
    /**
     * é€šè¿‡ç”¨æˆ·ID查询用户手机号
     *
     * @param userId ç”¨æˆ·id
     * @return ç”¨æˆ·æ‰‹æœºå·
     */
    String selectPhonenumberById(Long userId);
    /**
     * é€šè¿‡ç”¨æˆ·ID查询用户邮箱
     *
     * @param userId ç”¨æˆ·id
     * @return ç”¨æˆ·é‚®ç®±
     */
    String selectEmailById(Long userId);
    /**
     * é€šè¿‡ç”¨æˆ·ID查询用户列表
     *
     * @param userIds ç”¨æˆ·ids
     * @return ç”¨æˆ·åˆ—表
     */
    List<UserDTO> selectListByIds(List<Long> userIds);
    /**
     * é€šè¿‡è§’色ID查询用户ID
     *
     * @param roleIds è§’色ids
     * @return ç”¨æˆ·ids
     */
    List<Long> selectUserIdsByRoleIds(List<Long> roleIds);
    /**
     * é€šè¿‡è§’色ID查询用户
     *
     * @param roleIds è§’色ids
     * @return ç”¨æˆ·
     */
    List<UserDTO> selectUsersByRoleIds(List<Long> roleIds);
    /**
     * é€šè¿‡éƒ¨é—¨ID查询用户
     *
     * @param deptIds éƒ¨é—¨ids
     * @return ç”¨æˆ·
     */
    List<UserDTO> selectUsersByDeptIds(List<Long> deptIds);
    /**
     * é€šè¿‡å²—位ID查询用户
     *
     * @param postIds å²—位ids
     * @return ç”¨æˆ·
     */
    List<UserDTO> selectUsersByPostIds(List<Long> postIds);
    /**
     * æ ¹æ®ç”¨æˆ· ID åˆ—表查询用户名称映射关系
     *
     * @param userIds ç”¨æˆ· ID åˆ—表
     * @return Map,其中 key ä¸ºç”¨æˆ· ID,value ä¸ºå¯¹åº”的用户名称
     */
    Map<Long, String> selectUserNamesByIds(List<Long> userIds);
}
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/service/WorkflowService.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,105 @@
package org.dromara.common.core.service;
import org.dromara.common.core.domain.dto.CompleteTaskDTO;
import org.dromara.common.core.domain.dto.StartProcessDTO;
import org.dromara.common.core.domain.dto.StartProcessReturnDTO;
import java.util.List;
import java.util.Map;
/**
 * é€šç”¨ å·¥ä½œæµæœåŠ¡
 *
 * @author may
 */
public interface WorkflowService {
    /**
     * è¿è¡Œä¸­çš„实例 åˆ é™¤ç¨‹å®žä¾‹ï¼Œåˆ é™¤åŽ†å²è®°å½•ï¼Œåˆ é™¤ä¸šåŠ¡ä¸Žæµç¨‹å…³è”ä¿¡æ¯
     *
     * @param businessIds ä¸šåŠ¡id
     * @return ç»“æžœ
     */
    boolean deleteInstance(List<String> businessIds);
    /**
     * èŽ·å–å½“å‰æµç¨‹çŠ¶æ€
     *
     * @param taskId ä»»åŠ¡id
     * @return çŠ¶æ€
     */
    String getBusinessStatusByTaskId(Long taskId);
    /**
     * èŽ·å–å½“å‰æµç¨‹çŠ¶æ€
     *
     * @param businessId ä¸šåŠ¡id
     * @return çŠ¶æ€
     */
    String getBusinessStatus(String businessId);
    /**
     * è®¾ç½®æµç¨‹å˜é‡
     *
     * @param instanceId æµç¨‹å®žä¾‹id
     * @param variable   æµç¨‹å˜é‡
     */
    void setVariable(Long instanceId, Map<String, Object> variable);
    /**
     * èŽ·å–æµç¨‹å˜é‡
     *
     * @param instanceId æµç¨‹å®žä¾‹id
     */
    Map<String, Object> instanceVariable(Long instanceId);
    /**
     * æŒ‰ç…§ä¸šåŠ¡id查询流程实例id
     *
     * @param businessId ä¸šåŠ¡id
     * @return ç»“æžœ
     */
    Long getInstanceIdByBusinessId(String businessId);
    /**
     * æ–°å¢žç§Ÿæˆ·æµç¨‹å®šä¹‰
     *
     * @param tenantId ç§Ÿæˆ·id
     */
    void syncDef(String tenantId);
    /**
     * å¯åŠ¨æµç¨‹
     *
     * @param startProcess å‚æ•°
     * @return ç»“æžœ
     */
    StartProcessReturnDTO startWorkFlow(StartProcessDTO startProcess);
    /**
     * åŠžç†ä»»åŠ¡
     * ç³»ç»ŸåŽå°å‘起审批 æ— ç”¨æˆ·ä¿¡æ¯ éœ€è¦å¿½ç•¥æƒé™
     * completeTask.getVariables().put("ignore", true);
     *
     * @param completeTask å‚æ•°
     * @return ç»“æžœ
     */
    boolean completeTask(CompleteTaskDTO completeTask);
    /**
     * åŠžç†ä»»åŠ¡
     *
     * @param taskId  ä»»åŠ¡ID
     * @param message åŠžç†æ„è§
     * @return ç»“æžœ
     */
    boolean completeTask(Long taskId, String message);
    /**
     * å¯åŠ¨æµç¨‹å¹¶åŠžç†ç¬¬ä¸€ä¸ªä»»åŠ¡
     *
     * @param startProcess å‚æ•°
     * @return ç»“æžœ
     */
    boolean startCompleteTask(StartProcessDTO startProcess);
}
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/DateUtils.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,378 @@
package org.dromara.common.core.utils;
import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.dromara.common.core.enums.FormatsType;
import org.dromara.common.core.exception.ServiceException;
import java.lang.management.ManagementFactory;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.*;
import java.util.Date;
import java.util.concurrent.TimeUnit;
/**
 * æ—¶é—´å·¥å…·ç±»
 *
 * @author ruoyi
 */
public class DateUtils extends org.apache.commons.lang3.time.DateUtils {
    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"};
    @Deprecated
    private DateUtils() {
    }
    /**
     * èŽ·å–å½“å‰æ—¥æœŸå’Œæ—¶é—´
     *
     * @return å½“前日期和时间的 Date å¯¹è±¡è¡¨ç¤º
     */
    public static Date getNowDate() {
        return new Date();
    }
    /**
     * èŽ·å–å½“å‰æ—¥æœŸçš„å­—ç¬¦ä¸²è¡¨ç¤ºï¼Œæ ¼å¼ä¸ºYYYY-MM-DD
     *
     * @return å½“前日期的字符串表示
     */
    public static String getDate() {
        return dateTimeNow(FormatsType.YYYY_MM_DD);
    }
    /**
     * èŽ·å–å½“å‰æ—¥æœŸçš„å­—ç¬¦ä¸²è¡¨ç¤ºï¼Œæ ¼å¼ä¸ºyyyyMMdd
     *
     * @return å½“前日期的字符串表示
     */
    public static String getCurrentDate() {
        return DateFormatUtils.format(new Date(), FormatsType.YYYYMMDD.getTimeFormat());
    }
    /**
     * èŽ·å–å½“å‰æ—¥æœŸçš„è·¯å¾„æ ¼å¼å­—ç¬¦ä¸²ï¼Œæ ¼å¼ä¸º"yyyy/MM/dd"
     *
     * @return å½“前日期的路径格式字符串
     */
    public static String datePath() {
        Date now = new Date();
        return DateFormatUtils.format(now, FormatsType.YYYY_MM_DD_SLASH.getTimeFormat());
    }
    /**
     * èŽ·å–å½“å‰æ—¶é—´çš„å­—ç¬¦ä¸²è¡¨ç¤ºï¼Œæ ¼å¼ä¸ºYYYY-MM-DD HH:MM:SS
     *
     * @return å½“前时间的字符串表示
     */
    public static String getTime() {
        return dateTimeNow(FormatsType.YYYY_MM_DD_HH_MM_SS);
    }
    /**
     * èŽ·å–å½“å‰æ—¶é—´çš„å­—ç¬¦ä¸²è¡¨ç¤ºï¼Œæ ¼å¼ä¸º "HH:MM:SS"
     *
     * @return å½“前时间的字符串表示,格式为 "HH:MM:SS"
     */
    public static String getTimeWithHourMinuteSecond() {
        return dateTimeNow(FormatsType.HH_MM_SS);
    }
    /**
     * èŽ·å–å½“å‰æ—¥æœŸå’Œæ—¶é—´çš„å­—ç¬¦ä¸²è¡¨ç¤ºï¼Œæ ¼å¼ä¸ºYYYYMMDDHHMMSS
     *
     * @return å½“前日期和时间的字符串表示
     */
    public static String dateTimeNow() {
        return dateTimeNow(FormatsType.YYYYMMDDHHMMSS);
    }
    /**
     * èŽ·å–å½“å‰æ—¥æœŸå’Œæ—¶é—´çš„æŒ‡å®šæ ¼å¼çš„å­—ç¬¦ä¸²è¡¨ç¤º
     *
     * @param format æ—¥æœŸæ—¶é—´æ ¼å¼ï¼Œä¾‹å¦‚"YYYY-MM-DD HH:MM:SS"
     * @return å½“前日期和时间的字符串表示
     */
    public static String dateTimeNow(final FormatsType format) {
        return parseDateToStr(format, new Date());
    }
    /**
     * å°†æŒ‡å®šæ—¥æœŸæ ¼å¼åŒ–为 YYYY-MM-DD æ ¼å¼çš„字符串
     *
     * @param date è¦æ ¼å¼åŒ–的日期对象
     * @return æ ¼å¼åŒ–后的日期字符串
     */
    public static String formatDate(final Date date) {
        return parseDateToStr(FormatsType.YYYY_MM_DD, date);
    }
    /**
     * å°†æŒ‡å®šæ—¥æœŸæ ¼å¼åŒ–为 YYYY-MM-DD HH:MM:SS æ ¼å¼çš„字符串
     *
     * @param date è¦æ ¼å¼åŒ–的日期对象
     * @return æ ¼å¼åŒ–后的日期时间字符串
     */
    public static String formatDateTime(final Date date) {
        return parseDateToStr(FormatsType.YYYY_MM_DD_HH_MM_SS, date);
    }
    /**
     * å°†æŒ‡å®šæ—¥æœŸæŒ‰ç…§æŒ‡å®šæ ¼å¼è¿›è¡Œæ ¼å¼åŒ–
     *
     * @param format è¦ä½¿ç”¨çš„æ—¥æœŸæ—¶é—´æ ¼å¼ï¼Œä¾‹å¦‚"YYYY-MM-DD HH:MM:SS"
     * @param date   è¦æ ¼å¼åŒ–的日期对象
     * @return æ ¼å¼åŒ–后的日期时间字符串
     */
    public static String parseDateToStr(final FormatsType format, final Date date) {
        return new SimpleDateFormat(format.getTimeFormat()).format(date);
    }
    /**
     * å°†æŒ‡å®šæ ¼å¼çš„æ—¥æœŸæ—¶é—´å­—符串转换为 Date å¯¹è±¡
     *
     * @param format è¦è§£æžçš„æ—¥æœŸæ—¶é—´æ ¼å¼ï¼Œä¾‹å¦‚"YYYY-MM-DD HH:MM:SS"
     * @param ts     è¦è§£æžçš„æ—¥æœŸæ—¶é—´å­—符串
     * @return è§£æžåŽçš„ Date å¯¹è±¡
     * @throws RuntimeException å¦‚果解析过程中发生异常
     */
    public static Date parseDateTime(final FormatsType format, final String ts) {
        try {
            return new SimpleDateFormat(format.getTimeFormat()).parse(ts);
        } catch (ParseException e) {
            throw new RuntimeException(e);
        }
    }
    /**
     * å°†å¯¹è±¡è½¬æ¢ä¸ºæ—¥æœŸå¯¹è±¡
     *
     * @param str è¦è½¬æ¢çš„对象,通常是字符串
     * @return è½¬æ¢åŽçš„æ—¥æœŸå¯¹è±¡ï¼Œå¦‚果转换失败或输入为null,则返回null
     */
    public static Date parseDate(Object str) {
        if (str == null) {
            return null;
        }
        try {
            return parseDate(str.toString(), PARSE_PATTERNS);
        } catch (ParseException e) {
            return null;
        }
    }
    /**
     * èŽ·å–æœåŠ¡å™¨å¯åŠ¨æ—¶é—´
     *
     * @return æœåŠ¡å™¨å¯åŠ¨æ—¶é—´çš„ Date å¯¹è±¡è¡¨ç¤º
     */
    public static Date getServerStartDate() {
        long time = ManagementFactory.getRuntimeMXBean().getStartTime();
        return new Date(time);
    }
    /**
     * è®¡ç®—两个时间之间的时间差,并以指定单位返回(绝对值)
     *
     * @param start èµ·å§‹æ—¶é—´
     * @param end   ç»“束时间
     * @param unit  æ‰€éœ€è¿”回的时间单位(DAYS、HOURS、MINUTES、SECONDS、MILLISECONDS、MICROSECONDS、NANOSECONDS)
     * @return æ—¶é—´å·®çš„绝对值,以指定单位表示
     */
    public static long difference(Date start, Date end, TimeUnit unit) {
        // è®¡ç®—时间差,单位为毫秒,取绝对值避免负数
        long diffInMillis = Math.abs(end.getTime() - start.getTime());
        // æ ¹æ®ç›®æ ‡å•位转换时间差
        return switch (unit) {
            case DAYS -> diffInMillis / TimeUnit.DAYS.toMillis(1);
            case HOURS -> diffInMillis / TimeUnit.HOURS.toMillis(1);
            case MINUTES -> diffInMillis / TimeUnit.MINUTES.toMillis(1);
            case SECONDS -> diffInMillis / TimeUnit.SECONDS.toMillis(1);
            case MILLISECONDS -> diffInMillis;
            case MICROSECONDS -> TimeUnit.MILLISECONDS.toMicros(diffInMillis);
            case NANOSECONDS -> TimeUnit.MILLISECONDS.toNanos(diffInMillis);
        };
    }
    /**
     * è®¡ç®—两个日期之间的时间差,并以天、小时和分钟的格式返回
     *
     * @param endDate ç»“束日期
     * @param nowDate å½“前日期
     * @return è¡¨ç¤ºæ—¶é—´å·®çš„字符串,格式为"天 å°æ—¶ åˆ†é’Ÿ"
     */
    public static String getDatePoor(Date endDate, Date nowDate) {
        long diffInMillis = endDate.getTime() - nowDate.getTime();
        long day = TimeUnit.MILLISECONDS.toDays(diffInMillis);
        long hour = TimeUnit.MILLISECONDS.toHours(diffInMillis) % 24;
        long min = TimeUnit.MILLISECONDS.toMinutes(diffInMillis) % 60;
        return String.format("%d天 %d小时 %d分钟", day, hour, min);
    }
    /**
     * è®¡ç®—两个时间点的差值(天、小时、分钟、秒),当值为0时不显示该单位
     *
     * @param endDate ç»“束时间
     * @param nowDate å½“前时间
     * @return æ—¶é—´å·®å­—符串,格式为 "x天 x小时 x分钟 x秒",若为 0 åˆ™ä¸æ˜¾ç¤º
     */
    public static String getTimeDifference(Date endDate, Date nowDate) {
        long diffInMillis = endDate.getTime() - nowDate.getTime();
        long day = TimeUnit.MILLISECONDS.toDays(diffInMillis);
        long hour = TimeUnit.MILLISECONDS.toHours(diffInMillis) % 24;
        long min = TimeUnit.MILLISECONDS.toMinutes(diffInMillis) % 60;
        long sec = TimeUnit.MILLISECONDS.toSeconds(diffInMillis) % 60;
        // æž„建时间差字符串,条件是值不为0才显示
        StringBuilder result = new StringBuilder();
        if (day > 0) {
            result.append(String.format("%d天 ", day));
        }
        if (hour > 0) {
            result.append(String.format("%d小时 ", hour));
        }
        if (min > 0) {
            result.append(String.format("%d分钟 ", min));
        }
        if (sec > 0) {
            result.append(String.format("%d秒", sec));
        }
        return result.length() > 0 ? result.toString().trim() : "0秒";
    }
    /**
     * å°† LocalDateTime å¯¹è±¡è½¬æ¢ä¸º Date å¯¹è±¡
     *
     * @param temporalAccessor è¦è½¬æ¢çš„ LocalDateTime å¯¹è±¡
     * @return è½¬æ¢åŽçš„ Date å¯¹è±¡
     */
    public static Date toDate(LocalDateTime temporalAccessor) {
        ZonedDateTime zdt = temporalAccessor.atZone(ZoneId.systemDefault());
        return Date.from(zdt.toInstant());
    }
    /**
     * å°† LocalDate å¯¹è±¡è½¬æ¢ä¸º Date å¯¹è±¡
     *
     * @param temporalAccessor è¦è½¬æ¢çš„ LocalDate å¯¹è±¡
     * @return è½¬æ¢åŽçš„ 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());
    }
    /**
     * æ ¡éªŒæ—¥æœŸèŒƒå›´
     *
     * @param startDate å¼€å§‹æ—¥æœŸ
     * @param endDate   ç»“束日期
     * @param maxValue  æœ€å¤§æ—¶é—´è·¨åº¦çš„限制值
     * @param unit      æ—¶é—´è·¨åº¦çš„单位,可选择 "DAYS"、"HOURS" æˆ– "MINUTES"
     */
    public static void validateDateRange(Date startDate, Date endDate, int maxValue, TimeUnit unit) {
        // æ ¡éªŒç»“束日期不能早于开始日期
        if (endDate.before(startDate)) {
            throw new ServiceException("结束日期不能早于开始日期");
        }
        // è®¡ç®—时间跨度
        long diffInMillis = endDate.getTime() - startDate.getTime();
        // æ ¹æ®å•位转换时间跨度
        long diff = switch (unit) {
            case DAYS -> TimeUnit.MILLISECONDS.toDays(diffInMillis);
            case HOURS -> TimeUnit.MILLISECONDS.toHours(diffInMillis);
            case MINUTES -> TimeUnit.MILLISECONDS.toMinutes(diffInMillis);
            default -> throw new IllegalArgumentException("不支持的时间单位");
        };
        // æ ¡éªŒæ—¶é—´è·¨åº¦ä¸è¶…过最大限制
        if (diff > maxValue) {
            throw new ServiceException("最大时间跨度为 {} {}", maxValue, unit.toString().toLowerCase());
        }
    }
    /**
     * æ ¹æ®æŒ‡å®šæ—¥æœŸæ—¶é—´èŽ·å–æ—¶é—´æ®µï¼ˆå‡Œæ™¨ / ä¸Šåˆ / ä¸­åˆ / ä¸‹åˆ / æ™šä¸Šï¼‰
     *
     * @param date æ—¥æœŸæ—¶é—´
     * @return æ—¶é—´æ®µæè¿°
     */
    public static String getTodayHour(Date date) {
        int hour = DateUtil.hour(date, true);
        if (hour <= 6) {
            return "凌晨";
        } else if (hour < 12) {
            return "上午";
        } else if (hour == 12) {
            return "中午";
        } else if (hour <= 18) {
            return "下午";
        } else {
            return "晚上";
        }
    }
    /**
     * å°†æ—¥æœŸæ ¼å¼åŒ–为仿微信的友好时间
     * <p>
     * è§„则说明:
     * 1. æœªæ¥æ—¶é—´ï¼šyyyy-MM-dd HH:mm
     * 2. ä»Šå¤©ï¼š
     * - 1 åˆ†é’Ÿå†…:刚刚
     * - 1 å°æ—¶å†…:X åˆ†é’Ÿå‰
     * - è¶…过 1 å°æ—¶ï¼šå‡Œæ™¨/上午/中午/下午/晚上 HH:mm
     * 3. æ˜¨å¤©ï¼šæ˜¨å¤© HH:mm
     * 4. æœ¬å‘¨ï¼šå‘¨X HH:mm
     * 5. ä»Šå¹´å†…:MM-dd HH:mm
     * 6. éžä»Šå¹´ï¼šyyyy-MM-dd HH:mm
     *
     * @param date æ—¥æœŸæ—¶é—´
     * @return æ ¼å¼åŒ–后的时间描述
     */
    public static String formatFriendlyTime(Date date) {
        if (date == null) {
            return "";
        }
        Date now = DateUtil.date();
        // æœªæ¥æ—¶é—´æˆ–非今年
        if (date.after(now) || DateUtil.year(date) != DateUtil.year(now)) {
            return parseDateToStr(FormatsType.YYYY_MM_DD_HH_MM, date);
        }
        // ä»Šå¤©
        if (DateUtil.isSameDay(date, now)) {
            long minutes = DateUtil.between(date, now, DateUnit.MINUTE);
            if (minutes < 1) {
                return "刚刚";
            }
            if (minutes < 60) {
                return minutes + "分钟前";
            }
            return getTodayHour(date) + " " + DateUtil.format(date, "HH:mm");
        }
        // æ˜¨å¤©
        if (DateUtil.isSameDay(date, DateUtil.yesterday())) {
            return "昨天 " + DateUtil.format(date, "HH:mm");
        }
        // æœ¬å‘¨
        if (DateUtil.isSameWeek(date, now, true)) {
            return DateUtil.dayOfWeekEnum(date).toChinese("周")
                + " " + DateUtil.format(date, "HH:mm");
        }
        // ä»Šå¹´å†…其它时间
        return DateUtil.format(date, "MM-dd HH:mm");
    }
}
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/DesensitizedUtils.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,87 @@
package org.dromara.common.core.utils;
import cn.hutool.core.util.DesensitizedUtil;
import cn.hutool.core.util.StrUtil;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
/**
 * è„±æ•å·¥å…·ç±»
 *
 * @author AprilWind
 */
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class DesensitizedUtils extends DesensitizedUtil {
    /**
     * çµæ´»è„±æ•æ–¹æ³•
     *
     * @param value         åŽŸå§‹å­—ç¬¦ä¸²
     * @param prefixVisible å‰é¢å¯è§é•¿åº¦
     * @param suffixVisible åŽé¢å¯è§é•¿åº¦
     * @param maskLength    ä¸­é—´æŽ©ç é•¿åº¦ï¼ˆå›ºå®šæ˜¾ç¤ºå¤šå°‘ *,如果总长度不足则自动缩减)
     * @return è„±æ•åŽå­—符串
     */
    public static String mask(String value, int prefixVisible, int suffixVisible, int maskLength) {
        if (StrUtil.isBlank(value)) {
            return value;
        }
        int len = value.length();
        int prefixMaskLimit = prefixVisible + maskLength;
        int fullLimit = prefixMaskLimit + suffixVisible;
        // è§„则 1:长度 <= ä¸­é—´æŽ©ç é•¿åº¦ â†’ å…¨æŽ©ç 
        if (len <= maskLength) {
            return StrUtil.repeat('*', len);
        }
        String mask = StrUtil.repeat('*', maskLength);
        // è§„则 2:长度 <= å‰ç¼€ + ä¸­é—´æŽ©ç 
        if (len <= prefixMaskLimit) {
            return value.substring(0, len - maskLength) + mask;
        }
        String prefix = value.substring(0, prefixVisible);
        // è§„则 3:长度 <= å‰ç¼€ + ä¸­é—´æŽ©ç  + åŽç¼€
        if (len <= fullLimit) {
            int suffixLen = len - prefixMaskLimit;
            return prefix + mask + value.substring(len - suffixLen);
        }
        // è§„则 4:标准形态
        return prefix + mask + value.substring(len - suffixVisible);
    }
    /**
     * é«˜å®‰å…¨çº§åˆ«è„±æ•æ–¹æ³•(Token / ç§é’¥ï¼‰
     *
     * @param value         åŽŸå§‹å­—ç¬¦ä¸²
     * @param prefixVisible å‰é¢å¯è§é•¿åº¦ï¼ˆæŽ¨è0~4)
     * @param suffixVisible åŽé¢å¯è§é•¿åº¦ï¼ˆæŽ¨è0~4)
     * @return è„±æ•åŽå­—符串
     */
    public static String maskHighSecurity(String value, int prefixVisible, int suffixVisible) {
        if (StrUtil.isBlank(value)) {
            return value;
        }
        int len = value.length();
        // è§„则1:长度 <= å‰ç¼€å¯è§é•¿åº¦ â†’ å…¨éƒ¨æŽ©ç 
        if (len <= prefixVisible) {
            return StrUtil.repeat('*', len);
        }
        // è§„则2:长度 <= å‰ç¼€ + åŽç¼€å¯è§é•¿åº¦ â†’ ä¼˜å…ˆæŽ©ç åŽé¢
        if (len <= prefixVisible + suffixVisible) {
            return value.substring(0, len - prefixVisible) + StrUtil.repeat('*', prefixVisible);
        }
        // è§„则3:标准形态 â†’ å‰åŽå¯è§ï¼Œä¸­é—´å…¨éƒ¨æŽ©ç 
        return value.substring(0, prefixVisible)
            + StrUtil.repeat('*', len - prefixVisible - suffixVisible)
            + value.substring(len - suffixVisible);
    }
}
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/MapstructUtils.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,93 @@
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/introduction/quick-start.html">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-Vue-Plus/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/MessageUtils.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,33 @@
package org.dromara.common.core.utils;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import org.springframework.context.MessageSource;
import org.springframework.context.NoSuchMessageException;
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) {
        try {
            return MESSAGE_SOURCE.getMessage(code, args, LocaleContextHolder.getLocale());
        } catch (NoSuchMessageException e) {
            return code;
        }
    }
}
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/NetUtils.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,84 @@
package org.dromara.common.core.utils;
import cn.hutool.core.lang.PatternPool;
import cn.hutool.core.net.NetUtil;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.dromara.common.core.utils.regex.RegexUtils;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.UnknownHostException;
/**
 * å¢žå¼ºç½‘络相关工具类
 *
 * @author ç§‹è¾žæœªå¯’
 */
@Slf4j
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class NetUtils extends NetUtil {
    /**
     * åˆ¤æ–­æ˜¯å¦ä¸ºIPv6地址
     *
     * @param ip IP地址
     * @return æ˜¯å¦ä¸ºIPv6地址
     */
    public static boolean isIPv6(String ip) {
        try {
            // åˆ¤æ–­æ˜¯å¦ä¸ºIPv6地址
            return InetAddress.getByName(ip) instanceof Inet6Address;
        } catch (UnknownHostException e) {
            return false;
        }
    }
    /**
     * åˆ¤æ–­IPv6地址是否为内网地址
     * <br><br>
     * ä»¥ä¸‹åœ°å€å°†å½’类为本地地址,如有业务场景有需要,请根据需求自行处理:
     * <pre>
     * é€šé…ç¬¦åœ°å€ 0:0:0:0:0:0:0:0
     * é“¾è·¯æœ¬åœ°åœ°å€ fe80::/10
     * å”¯ä¸€æœ¬åœ°åœ°å€ fec0::/10
     * çŽ¯å›žåœ°å€ ::1
     * </pre>
     *
     * @param ip IP地址
     * @return æ˜¯å¦ä¸ºå†…网地址
     */
    public static boolean isInnerIPv6(String ip) {
        try {
            // åˆ¤æ–­æ˜¯å¦ä¸ºIPv6地址
            if (InetAddress.getByName(ip) instanceof Inet6Address inet6Address) {
                // isAnyLocalAddress åˆ¤æ–­æ˜¯å¦ä¸ºé€šé…ç¬¦åœ°å€ï¼Œé€šå¸¸ä¸ä¼šå°†å…¶è§†ä¸ºå†…网地址,根据业务场景自行处理判断
                // isLinkLocalAddress åˆ¤æ–­æ˜¯å¦ä¸ºé“¾è·¯æœ¬åœ°åœ°å€ï¼Œé€šå¸¸ä¸ç®—内网地址,是否划分归属于内网需要根据业务场景自行处理判断
                // isLoopbackAddress åˆ¤æ–­æ˜¯å¦ä¸ºçŽ¯å›žåœ°å€ï¼Œä¸ŽIPv4的 127.0.0.1 åŒç†ï¼Œç”¨äºŽè¡¨ç¤ºæœ¬æœº
                // isSiteLocalAddress åˆ¤æ–­æ˜¯å¦ä¸ºæœ¬åœ°ç«™ç‚¹åœ°å€ï¼ŒIPv6唯一本地地址(Unique Local Addresses,简称ULA)
                if (inet6Address.isAnyLocalAddress()
                    || inet6Address.isLinkLocalAddress()
                    || inet6Address.isLoopbackAddress()
                    || inet6Address.isSiteLocalAddress()) {
                    return true;
                }
            }
        } catch (UnknownHostException e) {
            // æ³¨æ„ï¼ŒisInnerIPv6方法和isIPv6方法的适用范围不同,所以此处不能忽略其异常信息。
            throw new IllegalArgumentException("Invalid IPv6 address!", e);
        }
        return false;
    }
    /**
     * åˆ¤æ–­æ˜¯å¦ä¸ºIPv4地址
     *
     * @param ip IP地址
     * @return æ˜¯å¦ä¸ºIPv4地址
     */
    public static boolean isIPv4(String ip) {
        return RegexUtils.isMatch(PatternPool.IPV4, ip);
    }
}
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/ObjectUtils.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,60 @@
package org.dromara.common.core.utils;
import cn.hutool.core.util.ObjectUtil;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import java.util.function.Function;
/**
 * å¯¹è±¡å·¥å…·ç±»
 *
 * @author ç§‹è¾žæœªå¯’
 */
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class ObjectUtils extends ObjectUtil {
    /**
     * å¦‚果对象不为空,则获取对象中的某个字段 ObjectUtils.notNullGetter(user, User::getName);
     *
     * @param obj å¯¹è±¡
     * @param func èŽ·å–æ–¹æ³•
     * @return å¯¹è±¡å­—段
     */
    public static <T, E> E notNullGetter(T obj, Function<T, E> func) {
        if (isNotNull(obj) && isNotNull(func)) {
            return func.apply(obj);
        }
        return null;
    }
    /**
     * å¦‚果对象不为空,则获取对象中的某个字段,否则返回默认值
     *
     * @param obj          å¯¹è±¡
     * @param func         èŽ·å–æ–¹æ³•
     * @param defaultValue é»˜è®¤å€¼
     * @return å¯¹è±¡å­—段
     */
    public static <T, E> E notNullGetter(T obj, Function<T, E> func, E defaultValue) {
        if (isNotNull(obj) && isNotNull(func)) {
            return func.apply(obj);
        }
        return defaultValue;
    }
    /**
     * å¦‚果值不为空,则返回值,否则返回默认值
     *
     * @param obj          å¯¹è±¡
     * @param defaultValue é»˜è®¤å€¼
     * @return å¯¹è±¡å­—段
     */
    public static <T> T notNull(T obj, T defaultValue) {
        if (isNotNull(obj)) {
            return obj;
        }
        return defaultValue;
    }
}
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/ServletUtils.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,289 @@
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.util.LinkedCaseInsensitiveMap;
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.Enumeration;
import java.util.HashMap;
import java.util.Map;
/**
 * å®¢æˆ·ç«¯å·¥å…·ç±»ï¼Œæä¾›èŽ·å–è¯·æ±‚å‚æ•°ã€å“åº”å¤„ç†ã€å¤´éƒ¨ä¿¡æ¯ç­‰å¸¸ç”¨æ“ä½œ
 *
 * @author ruoyi
 */
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class ServletUtils extends JakartaServletUtil {
    /**
     * èŽ·å–æŒ‡å®šåç§°çš„ String ç±»åž‹çš„请求参数
     *
     * @param name å‚数名
     * @return å‚数值
     */
    public static String getParameter(String name) {
        return getRequest().getParameter(name);
    }
    /**
     * èŽ·å–æŒ‡å®šåç§°çš„ String ç±»åž‹çš„请求参数,若参数不存在,则返回默认值
     *
     * @param name         å‚数名
     * @param defaultValue é»˜è®¤å€¼
     * @return å‚数值或默认值
     */
    public static String getParameter(String name, String defaultValue) {
        return Convert.toStr(getRequest().getParameter(name), defaultValue);
    }
    /**
     * èŽ·å–æŒ‡å®šåç§°çš„ Integer ç±»åž‹çš„请求参数
     *
     * @param name å‚数名
     * @return å‚数值
     */
    public static Integer getParameterToInt(String name) {
        return Convert.toInt(getRequest().getParameter(name));
    }
    /**
     * èŽ·å–æŒ‡å®šåç§°çš„ Integer ç±»åž‹çš„请求参数,若参数不存在,则返回默认值
     *
     * @param name         å‚数名
     * @param defaultValue é»˜è®¤å€¼
     * @return å‚数值或默认值
     */
    public static Integer getParameterToInt(String name, Integer defaultValue) {
        return Convert.toInt(getRequest().getParameter(name), defaultValue);
    }
    /**
     * èŽ·å–æŒ‡å®šåç§°çš„ Boolean ç±»åž‹çš„请求参数
     *
     * @param name å‚数名
     * @return å‚数值
     */
    public static Boolean getParameterToBool(String name) {
        return Convert.toBool(getRequest().getParameter(name));
    }
    /**
     * èŽ·å–æŒ‡å®šåç§°çš„ Boolean ç±»åž‹çš„请求参数,若参数不存在,则返回默认值
     *
     * @param name         å‚数名
     * @param defaultValue é»˜è®¤å€¼
     * @return å‚数值或默认值
     */
    public static Boolean getParameterToBool(String name, Boolean defaultValue) {
        return Convert.toBool(getRequest().getParameter(name), defaultValue);
    }
    /**
     * èŽ·å–æ‰€æœ‰è¯·æ±‚å‚æ•°ï¼ˆä»¥ Map çš„形式返回)
     *
     * @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);
    }
    /**
     * èŽ·å–æ‰€æœ‰è¯·æ±‚å‚æ•°ï¼ˆä»¥ 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.joinComma(entry.getValue()));
        }
        return params;
    }
    /**
     * èŽ·å–å½“å‰ HTTP è¯·æ±‚对象
     *
     * @return å½“前 HTTP è¯·æ±‚对象
     */
    public static HttpServletRequest getRequest() {
        try {
            return getRequestAttributes().getRequest();
        } catch (Exception e) {
            return null;
        }
    }
    /**
     * èŽ·å–å½“å‰ HTTP å“åº”对象
     *
     * @return å½“前 HTTP å“åº”对象
     */
    public static HttpServletResponse getResponse() {
        try {
            return getRequestAttributes().getResponse();
        } catch (Exception e) {
            return null;
        }
    }
    /**
     * èŽ·å–å½“å‰è¯·æ±‚çš„ HttpSession å¯¹è±¡
     * <p>
     * å¦‚果当前请求已经关联了一个会话(即已经存在有效的 session ID),
     * åˆ™è¿”回该会话对象;如果没有关联会话,则会创建一个新的会话对象并返回。
     * <p>
     * HttpSession ç”¨äºŽå­˜å‚¨ä¼šè¯çº§åˆ«çš„æ•°æ®ï¼Œå¦‚用户登录信息、购物车内容等,
     * å¯ä»¥åœ¨å¤šä¸ªè¯·æ±‚之间共享会话数据
     *
     * @return å½“前请求的 HttpSession å¯¹è±¡
     */
    public static HttpSession getSession() {
        return getRequest().getSession();
    }
    /**
     * èŽ·å–å½“å‰è¯·æ±‚çš„è¯·æ±‚å±žæ€§
     *
     * @return {@link ServletRequestAttributes} è¯·æ±‚属性对象
     */
    public static ServletRequestAttributes getRequestAttributes() {
        try {
            RequestAttributes attributes = RequestContextHolder.getRequestAttributes();
            return (ServletRequestAttributes) attributes;
        } catch (Exception e) {
            return null;
        }
    }
    /**
     * èŽ·å–æŒ‡å®šè¯·æ±‚å¤´çš„å€¼ï¼Œå¦‚æžœå¤´éƒ¨ä¸ºç©ºåˆ™è¿”å›žç©ºå­—ç¬¦ä¸²
     *
     * @param request è¯·æ±‚对象
     * @param name    å¤´éƒ¨åç§°
     * @return å¤´éƒ¨å€¼
     */
    public static String getHeader(HttpServletRequest request, String name) {
        String value = request.getHeader(name);
        if (StringUtils.isEmpty(value)) {
            return StringUtils.EMPTY;
        }
        return urlDecode(value);
    }
    /**
     * èŽ·å–æ‰€æœ‰è¯·æ±‚å¤´çš„ Map,键为头部名称,值为头部值
     *
     * @param request è¯·æ±‚对象
     * @return è¯·æ±‚头的 Map
     */
    public static Map<String, String> getHeaders(HttpServletRequest request) {
        Map<String, String> map = new LinkedCaseInsensitiveMap<>();
        Enumeration<String> enumeration = request.getHeaderNames();
        if (enumeration != null) {
            while (enumeration.hasMoreElements()) {
                String key = enumeration.nextElement();
                String value = request.getHeader(key);
                map.put(key, value);
            }
        }
        return map;
    }
    /**
     * å°†å­—符串渲染到客户端(以 JSON æ ¼å¼è¿”回)
     *
     * @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 è¯·æ±‚对象
     * @return æ˜¯å¦ä¸º Ajax è¯·æ±‚
     */
    public static boolean isAjaxRequest(HttpServletRequest request) {
        // åˆ¤æ–­ Accept å¤´éƒ¨æ˜¯å¦åŒ…含 application/json
        String accept = request.getHeader("accept");
        if (accept != null && accept.contains(MediaType.APPLICATION_JSON_VALUE)) {
            return true;
        }
        // åˆ¤æ–­ X-Requested-With å¤´éƒ¨æ˜¯å¦åŒ…含 XMLHttpRequest
        String xRequestedWith = request.getHeader("X-Requested-With");
        if (xRequestedWith != null && xRequestedWith.contains("XMLHttpRequest")) {
            return true;
        }
        // åˆ¤æ–­ URI åŽç¼€æ˜¯å¦ä¸º .json æˆ– .xml
        String uri = request.getRequestURI();
        if (StringUtils.equalsAnyIgnoreCase(uri, ".json", ".xml")) {
            return true;
        }
        // åˆ¤æ–­è¯·æ±‚参数 __ajax æ˜¯å¦ä¸º json æˆ– xml
        String ajax = request.getParameter("__ajax");
        return StringUtils.equalsAnyIgnoreCase(ajax, "json", "xml");
    }
    /**
     * èŽ·å–å®¢æˆ·ç«¯ IP åœ°å€
     *
     * @return å®¢æˆ·ç«¯ IP åœ°å€
     */
    public static String getClientIP() {
        return getClientIP(getRequest());
    }
    /**
     * å¯¹å†…容进行 URL ç¼–码
     *
     * @param str å†…容
     * @return ç¼–码后的内容
     */
    public static String urlEncode(String str) {
        return URLEncoder.encode(str, StandardCharsets.UTF_8);
    }
    /**
     * å¯¹å†…容进行 URL è§£ç 
     *
     * @param str å†…容
     * @return è§£ç åŽçš„内容
     */
    public static String urlDecode(String str) {
        return URLDecoder.decode(str, StandardCharsets.UTF_8);
    }
}
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/SpringUtils.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,67 @@
package org.dromara.common.core.utils;
import cn.hutool.extra.spring.SpringUtil;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.boot.autoconfigure.thread.Threading;
import org.springframework.context.ApplicationContext;
import org.springframework.core.env.Environment;
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) getBean(invoker.getClass());
    }
    /**
     * èŽ·å–spring上下文
     */
    public static ApplicationContext context() {
        return getApplicationContext();
    }
    public static boolean isVirtual() {
        return Threading.VIRTUAL.isActive(getBean(Environment.class));
    }
}
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/StreamUtils.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,328 @@
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();
        }
        return collection.stream()
            .filter(function)
            // æ³¨æ„æ­¤å¤„不要使用 .toList() æ–°è¯­æ³• å› ä¸ºè¿”回的是不可变List ä¼šå¯¼è‡´åºåˆ—化问题
            .collect(Collectors.toList());
    }
    /**
     * æ‰¾åˆ°æµä¸­æ»¡è¶³æ¡ä»¶çš„第一个元素
     *
     * @param collection éœ€è¦æŸ¥è¯¢çš„集合
     * @param function   è¿‡æ»¤æ–¹æ³•
     * @return æ‰¾åˆ°ç¬¦åˆæ¡ä»¶çš„第一个元素,没有则返回 Optional.empty()
     */
    public static <E> Optional<E> findFirst(Collection<E> collection, Predicate<E> function) {
        if (CollUtil.isEmpty(collection)) {
            return Optional.empty();
        }
        return collection.stream()
            .filter(function)
            .findFirst();
    }
    /**
     * æ‰¾åˆ°æµä¸­æ»¡è¶³æ¡ä»¶çš„第一个元素值
     *
     * @param collection éœ€è¦æŸ¥è¯¢çš„集合
     * @param function   è¿‡æ»¤æ–¹æ³•
     * @return æ‰¾åˆ°ç¬¦åˆæ¡ä»¶çš„第一个元素,没有则返回 null
     */
    public static <E> E findFirstValue(Collection<E> collection, Predicate<E> function) {
        return findFirst(collection,function).orElse(null);
    }
    /**
     * æ‰¾åˆ°æµä¸­ä»»æ„ä¸€ä¸ªæ»¡è¶³æ¡ä»¶çš„元素
     *
     * @param collection éœ€è¦æŸ¥è¯¢çš„集合
     * @param function   è¿‡æ»¤æ–¹æ³•
     * @return æ‰¾åˆ°ç¬¦åˆæ¡ä»¶çš„任意一个元素,没有则返回 Optional.empty()
     */
    public static <E> Optional<E> findAny(Collection<E> collection, Predicate<E> function) {
        if (CollUtil.isEmpty(collection)) {
            return Optional.empty();
        }
        return collection.stream()
            .filter(function)
            .findAny();
    }
    /**
     * æ‰¾åˆ°æµä¸­ä»»æ„ä¸€ä¸ªæ»¡è¶³æ¡ä»¶çš„元素值
     *
     * @param collection éœ€è¦æŸ¥è¯¢çš„集合
     * @param function   è¿‡æ»¤æ–¹æ³•
     * @return æ‰¾åˆ°ç¬¦åˆæ¡ä»¶çš„任意一个元素,没有则返回null
     */
    public static <E> E findAnyValue(Collection<E> collection, Predicate<E> function) {
        return findAny(collection,function).orElse(null);
    }
    /**
     * å°†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();
        }
        return collection.stream()
            .filter(Objects::nonNull)
            .sorted(comparing)
            // æ³¨æ„æ­¤å¤„不要使用 .toList() æ–°è¯­æ³• å› ä¸ºè¿”回的是不可变List ä¼šå¯¼è‡´åºåˆ—化问题
            .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()
            .filter(Objects::nonNull)
            .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()
            .filter(Objects::nonNull)
            .collect(Collectors.toMap(key, value, (l, r) -> l));
    }
    /**
     * èŽ·å– map ä¸­çš„æ•°æ®ä½œä¸ºæ–° Map çš„ value ï¼Œkey ä¸å˜
     * @param map éœ€è¦å¤„理的map
     * @param take å–值函数
     * @param <K> map中的key类型
     * @param <E> map中的value类型
     * @param <V> æ–°map中的value类型
     * @return æ–°çš„map
     */
    public static <K, E, V> Map<K, V> toMap(Map<K, E> map, BiFunction<K, E, V> take) {
        if (CollUtil.isEmpty(map)) {
            return MapUtil.newHashMap();
        }
        return toMap(map.entrySet(), Map.Entry::getKey, entry -> take.apply(entry.getKey(), entry.getValue()));
    }
    /**
     * å°†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()
            .filter(Objects::nonNull)
            .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()
            .filter(Objects::nonNull)
            .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)) {
            return MapUtil.newHashMap();
        }
        return collection.stream()
            .filter(Objects::nonNull)
            .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)) {
            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 (CollUtil.isEmpty(map1) && CollUtil.isEmpty(map2)) {
            // å¦‚果两个 map éƒ½ä¸ºç©ºï¼Œåˆ™ç›´æŽ¥è¿”回空的 map
            return MapUtil.newHashMap();
        } else if (CollUtil.isEmpty(map1)) {
            // å¦‚æžœ map1 ä¸ºç©ºï¼Œåˆ™ç›´æŽ¥å¤„理返回 map2
            return toMap(map2.entrySet(), Map.Entry::getKey, entry -> merge.apply(null, entry.getValue()));
        } else if (CollUtil.isEmpty(map2)) {
            // å¦‚æžœ map2 ä¸ºç©ºï¼Œåˆ™ç›´æŽ¥å¤„理返回 map1
            return toMap(map1.entrySet(), Map.Entry::getKey, entry -> merge.apply(entry.getValue(), null));
        }
        Set<K> keySet = new HashSet<>();
        keySet.addAll(map1.keySet());
        keySet.addAll(map2.keySet());
        return toMap(keySet, key -> key, key -> merge.apply(map1.get(key), map2.get(key)));
    }
}
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/StringUtils.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,384 @@
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 org.springframework.util.AntPathMatcher;
import java.nio.charset.Charset;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
/**
 * å­—符串工具类
 *
 * @author Lion Li
 */
public class StringUtils extends org.apache.commons.lang3.StringUtils {
    public static final String SEPARATOR = ",";
    public static final String SLASH = "/";
    @Deprecated
    private StringUtils() {
    }
    /**
     * èŽ·å–å‚æ•°ä¸ä¸ºç©ºå€¼
     *
     * @param str defaultValue è¦åˆ¤æ–­çš„value
     * @return value è¿”回值
     */
    public static String blankToDefault(String str, String defaultValue) {
        return StrUtil.blankToDefault(str, defaultValue);
    }
    /**
     * * åˆ¤æ–­ä¸€ä¸ªå­—符串是否为空串
     *
     * @param str String
     * @return true:为空 false:非空
     */
    public static boolean isEmpty(String str) {
        return StrUtil.isEmpty(str);
    }
    /**
     * * åˆ¤æ–­ä¸€ä¸ªå­—符串是否为非空串
     *
     * @param str String
     * @return true:非空串 false:空串
     */
    public static boolean isNotEmpty(String str) {
        return !isEmpty(str);
    }
    /**
     * åŽ»ç©ºæ ¼
     */
    public static String trim(String str) {
        return StrUtil.trim(str);
    }
    /**
     * æˆªå–字符串
     *
     * @param str   å­—符串
     * @param start å¼€å§‹
     * @return ç»“æžœ
     */
    public static String substring(final String str, int start) {
        return substring(str, start, str.length());
    }
    /**
     * æˆªå–字符串
     *
     * @param str   å­—符串
     * @param start å¼€å§‹
     * @param end   ç»“束
     * @return ç»“æžœ
     */
    public static String substring(final String str, int start, int end) {
        return StrUtil.sub(str, start, end);
    }
    /**
     * æ ¼å¼åŒ–文本, {} è¡¨ç¤ºå ä½ç¬¦<br>
     * æ­¤æ–¹æ³•只是简单将占位符 {} æŒ‰ç…§é¡ºåºæ›¿æ¢ä¸ºå‚æ•°<br>
     * å¦‚果想输出 {} ä½¿ç”¨ \\转义 { å³å¯ï¼Œå¦‚果想输出 {} ä¹‹å‰çš„ \ ä½¿ç”¨åŒè½¬ä¹‰ç¬¦ \\\\ å³å¯<br>
     * ä¾‹ï¼š<br>
     * é€šå¸¸ä½¿ç”¨ï¼šformat("this is {} for {}", "a", "b") -> this is a for b<br>
     * è½¬ä¹‰{}: format("this is \\{} for {}", "a", "b") -> this is {} for a<br>
     * è½¬ä¹‰\: format("this is \\\\{} for {}", "a", "b") -> this is \a for b<br>
     *
     * @param template æ–‡æœ¬æ¨¡æ¿ï¼Œè¢«æ›¿æ¢çš„部分用 {} è¡¨ç¤º
     * @param params   å‚数值
     * @return æ ¼å¼åŒ–后的文本
     */
    public static String format(String template, Object... params) {
        return StrUtil.format(template, params);
    }
    /**
     * æ˜¯å¦ä¸ºhttp(s)://开头
     *
     * @param link é“¾æŽ¥
     * @return ç»“æžœ
     */
    public static boolean ishttp(String link) {
        return Validator.isUrl(link);
    }
    /**
     * å­—符串转set
     *
     * @param str å­—符串
     * @param sep åˆ†éš”符
     * @return set集合
     */
    public static Set<String> str2Set(String str, String sep) {
        return new HashSet<>(str2List(str, sep, true, false));
    }
    /**
     * å­—符串转list
     *
     * @param str         å­—符串
     * @param sep         åˆ†éš”符
     * @param filterBlank è¿‡æ»¤çº¯ç©ºç™½
     * @param trim        åŽ»æŽ‰é¦–å°¾ç©ºç™½
     * @return list集合
     */
    public static List<String> str2List(String str, String sep, boolean filterBlank, boolean trim) {
        List<String> list = new ArrayList<>();
        if (isEmpty(str)) {
            return list;
        }
        // è¿‡æ»¤ç©ºç™½å­—符串
        if (filterBlank && isBlank(str)) {
            return list;
        }
        String[] split = str.split(sep);
        for (String string : split) {
            if (filterBlank && isBlank(string)) {
                continue;
            }
            if (trim) {
                string = trim(string);
            }
            list.add(string);
        }
        return list;
    }
    /**
     * æŸ¥æ‰¾æŒ‡å®šå­—符串是否包含指定字符串列表中的任意一个字符串同时串忽略大小写
     *
     * @param cs                  æŒ‡å®šå­—符串
     * @param searchCharSequences éœ€è¦æ£€æŸ¥çš„字符串数组
     * @return æ˜¯å¦åŒ…含任意一个字符串
     */
    public static boolean containsAnyIgnoreCase(CharSequence cs, CharSequence... searchCharSequences) {
        return StrUtil.containsAnyIgnoreCase(cs, searchCharSequences);
    }
    /**
     * é©¼å³°è½¬ä¸‹åˆ’线命名
     */
    public static String toUnderScoreCase(String str) {
        return StrUtil.toUnderlineCase(str);
    }
    /**
     * æ˜¯å¦åŒ…含字符串
     *
     * @param str  éªŒè¯å­—符串
     * @param strs å­—符串组
     * @return åŒ…含返回true
     */
    public static boolean inStringIgnoreCase(String str, String... strs) {
        return StrUtil.equalsAnyIgnoreCase(str, strs);
    }
    /**
     * å°†ä¸‹åˆ’线大写方式命名的字符串转换为驼峰式。如果转换前的下划线大写方式命名的字符串为空,则返回空字符串。 ä¾‹å¦‚:HELLO_WORLD->HelloWorld
     *
     * @param name è½¬æ¢å‰çš„下划线大写方式命名的字符串
     * @return è½¬æ¢åŽçš„驼峰式命名的字符串
     */
    public static String convertToCamelCase(String name) {
        return StrUtil.upperFirst(StrUtil.toCamelCase(name));
    }
    /**
     * é©¼å³°å¼å‘½åæ³• ä¾‹å¦‚:user_name->userName
     */
    public static String toCamelCase(String s) {
        return StrUtil.toCamelCase(s);
    }
    /**
     * æŸ¥æ‰¾æŒ‡å®šå­—符串是否匹配指定字符串列表中的任意一个字符串
     *
     * @param str  æŒ‡å®šå­—符串
     * @param strs éœ€è¦æ£€æŸ¥çš„字符串数组
     * @return æ˜¯å¦åŒ¹é…
     */
    public static boolean matches(String str, List<String> strs) {
        if (isEmpty(str) || CollUtil.isEmpty(strs)) {
            return false;
        }
        for (String pattern : strs) {
            if (isMatch(pattern, str)) {
                return true;
            }
        }
        return false;
    }
    /**
     * åˆ¤æ–­url是否与规则配置:
     * ? è¡¨ç¤ºå•个字符;
     * * è¡¨ç¤ºä¸€å±‚路径内的任意字符串,不可跨层级;
     * ** è¡¨ç¤ºä»»æ„å±‚路径;
     *
     * @param pattern åŒ¹é…è§„则
     * @param url     éœ€è¦åŒ¹é…çš„url
     */
    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(Convert.toStr(c).repeat(size - len));
                sb.append(s);
            } else {
                return s.substring(len - size, len);
            }
        } else {
            sb.append(Convert.toStr(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)
            .filter(Objects::nonNull)
            .collect(Collectors.toList());
    }
    /**
     * ä¸åŒºåˆ†å¤§å°å†™æ£€æŸ¥ CharSequence æ˜¯å¦ä»¥æŒ‡å®šçš„前缀开头。
     *
     * @param str     è¦æ£€æŸ¥çš„ CharSequence å¯èƒ½ä¸º null
     * @param prefixs è¦æŸ¥æ‰¾çš„前缀可能为 null
     * @return æ˜¯å¦åŒ…含
     */
    public static boolean startWithAnyIgnoreCase(CharSequence str, CharSequence... prefixs) {
        // åˆ¤æ–­æ˜¯å¦æ˜¯ä»¥æŒ‡å®šå­—符串开头
        for (CharSequence prefix : prefixs) {
            if (StringUtils.startsWithIgnoreCase(str, prefix)) {
                return true;
            }
        }
        return false;
    }
    /**
     * å°†å­—符串从源字符集转换为目标字符集
     *
     * @param input       åŽŸå§‹å­—ç¬¦ä¸²
     * @param fromCharset æºå­—符集
     * @param toCharset   ç›®æ ‡å­—符集
     * @return è½¬æ¢åŽçš„字符串
     */
    public static String convert(String input, Charset fromCharset, Charset toCharset) {
        if (isBlank(input)) {
            return input;
        }
        try {
            // ä»Žæºå­—符集获取字节
            byte[] bytes = input.getBytes(fromCharset);
            // ä½¿ç”¨ç›®æ ‡å­—符集解码
            return new String(bytes, toCharset);
        } catch (Exception e) {
            return input;
        }
    }
    /**
     * å°†å¯è¿­ä»£å¯¹è±¡ä¸­çš„元素使用逗号拼接成字符串
     *
     * @param iterable å¯è¿­ä»£å¯¹è±¡ï¼Œå¦‚ List、Set ç­‰
     * @return æ‹¼æŽ¥åŽçš„字符串
     */
    public static String joinComma(Iterable<?> iterable) {
        return StringUtils.join(iterable, SEPARATOR);
    }
    /**
     * å°†æ•°ç»„中的元素使用逗号拼接成字符串
     *
     * @param array ä»»æ„ç±»åž‹çš„æ•°ç»„
     * @return æ‹¼æŽ¥åŽçš„字符串
     */
    public static String joinComma(Object[] array) {
        return StringUtils.join(array, SEPARATOR);
    }
}
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/TreeBuildUtils.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,123 @@
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 lombok.AccessLevel;
import lombok.NoArgsConstructor;
import org.dromara.common.core.utils.reflect.ReflectUtils;
import java.util.List;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
 * æ‰©å±• 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");
    /**
     * æž„建树形结构
     *
     * @param <T>        è¾“入节点的类型
     * @param <K>        èŠ‚ç‚¹ID的类型
     * @param list       èŠ‚ç‚¹åˆ—è¡¨ï¼Œå…¶ä¸­åŒ…å«äº†è¦æž„å»ºæ ‘å½¢ç»“æž„çš„æ‰€æœ‰èŠ‚ç‚¹
     * @param nodeParser è§£æžå™¨ï¼Œç”¨äºŽå°†è¾“入节点转换为树节点
     * @return æž„建好的树形结构列表
     */
    public static <T, K> List<Tree<K>> build(List<T> list, NodeParser<T, K> nodeParser) {
        if (CollUtil.isEmpty(list)) {
            return CollUtil.newArrayList();
        }
        K k = ReflectUtils.invokeGetter(list.get(0), "parentId");
        return TreeUtil.build(list, k, DEFAULT_CONFIG, nodeParser);
    }
    /**
     * æž„建树形结构
     *
     * @param <T>        è¾“入节点的类型
     * @param <K>        èŠ‚ç‚¹ID的类型
     * @param parentId   é¡¶çº§èŠ‚ç‚¹
     * @param list       èŠ‚ç‚¹åˆ—è¡¨ï¼Œå…¶ä¸­åŒ…å«äº†è¦æž„å»ºæ ‘å½¢ç»“æž„çš„æ‰€æœ‰èŠ‚ç‚¹
     * @param nodeParser è§£æžå™¨ï¼Œç”¨äºŽå°†è¾“入节点转换为树节点
     * @return æž„建好的树形结构列表
     */
    public static <T, K> List<Tree<K>> build(List<T> list, K parentId, NodeParser<T, K> nodeParser) {
        if (CollUtil.isEmpty(list)) {
            return CollUtil.newArrayList();
        }
        return TreeUtil.build(list, parentId, DEFAULT_CONFIG, nodeParser);
    }
    /**
     * æž„建多根节点的树结构(支持多个顶级节点)
     *
     * @param list        åŽŸå§‹æ•°æ®åˆ—è¡¨
     * @param getId       èŽ·å–èŠ‚ç‚¹ ID çš„æ–¹æ³•引用,例如:node -> node.getId()
     * @param getParentId èŽ·å–èŠ‚ç‚¹çˆ¶çº§ ID çš„æ–¹æ³•引用,例如:node -> node.getParentId()
     * @param parser      æ ‘节点属性映射器,用于将原始节点 T è½¬ä¸º Tree èŠ‚ç‚¹
     * @param <T>         åŽŸå§‹æ•°æ®ç±»åž‹ï¼ˆå¦‚å®žä½“ç±»ã€DTO ç­‰ï¼‰
     * @param <K>         èŠ‚ç‚¹ ID ç±»åž‹ï¼ˆå¦‚ Long、String)
     * @return æž„建完成的树形结构(可能包含多个顶级根节点)
     */
    public static <T, K> List<Tree<K>> buildMultiRoot(List<T> list, Function<T, K> getId, Function<T, K> getParentId, NodeParser<T, K> parser) {
        if (CollUtil.isEmpty(list)) {
            return CollUtil.newArrayList();
        }
        Set<K> rootParentIds = StreamUtils.toSet(list, getParentId);
        rootParentIds.removeAll(StreamUtils.toSet(list, getId));
        // æž„建每一个根 parentId ä¸‹çš„æ ‘,并合并成最终结果列表
        return rootParentIds.stream()
            .flatMap(rootParentId -> TreeUtil.build(list, rootParentId, parser).stream())
            .collect(Collectors.toList());
    }
    /**
     * èŽ·å–èŠ‚ç‚¹åˆ—è¡¨ä¸­æ‰€æœ‰èŠ‚ç‚¹çš„å¶å­èŠ‚ç‚¹
     *
     * @param <K>   èŠ‚ç‚¹ID的类型
     * @param nodes èŠ‚ç‚¹åˆ—è¡¨
     * @return åŒ…含所有叶子节点的列表
     */
    public static <K> List<Tree<K>> getLeafNodes(List<Tree<K>> nodes) {
        if (CollUtil.isEmpty(nodes)) {
            return CollUtil.newArrayList();
        }
        return nodes.stream()
            .flatMap(TreeBuildUtils::extractLeafNodes)
            .collect(Collectors.toList());
    }
    /**
     * èŽ·å–æŒ‡å®šèŠ‚ç‚¹ä¸‹çš„æ‰€æœ‰å¶å­èŠ‚ç‚¹
     *
     * @param <K>  èŠ‚ç‚¹ID的类型
     * @param node è¦æŸ¥æ‰¾å¶å­èŠ‚ç‚¹çš„æ ¹èŠ‚ç‚¹
     * @return åŒ…含所有叶子节点的列表
     */
    private static <K> Stream<Tree<K>> extractLeafNodes(Tree<K> node) {
        if (!node.hasChild()) {
            return Stream.of(node);
        } else {
            // é€’归调用,获取所有子节点的叶子节点
            return node.getChildren().stream()
                .flatMap(TreeBuildUtils::extractLeafNodes);
        }
    }
}
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/ValidatorUtils.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,35 @@
package org.dromara.common.core.utils;
import jakarta.validation.ConstraintViolation;
import jakarta.validation.ConstraintViolationException;
import jakarta.validation.Validator;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import java.util.Set;
/**
 * Validator æ ¡éªŒæ¡†æž¶å·¥å…·
 *
 * @author Lion Li
 */
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class ValidatorUtils {
    private static final Validator VALID = SpringUtils.getBean(Validator.class);
    /**
     * å¯¹ç»™å®šå¯¹è±¡è¿›è¡Œå‚数校验,并根据指定的校验组进行校验
     *
     * @param object è¦è¿›è¡Œæ ¡éªŒçš„对象
     * @param groups æ ¡éªŒç»„
     * @throws ConstraintViolationException å¦‚果校验不通过,则抛出参数校验异常
     */
    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-Vue-Plus/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-Vue-Plus/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-Vue-Plus/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/ip/AddressUtils.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,43 @@
package org.dromara.common.core.utils.ip;
import cn.hutool.http.HtmlUtil;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.dromara.common.core.utils.NetUtils;
import org.dromara.common.core.utils.StringUtils;
/**
 * èŽ·å–åœ°å€ç±»
 *
 * @author Lion Li
 */
@Slf4j
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class AddressUtils {
    // æœªçŸ¥IP
    public static final String UNKNOWN_IP = "XX XX";
    // å†…网地址
    public static final String LOCAL_ADDRESS = "内网IP";
    public static String getRealAddressByIP(String ip) {
        // å¤„理空串并过滤HTML标签
        ip = HtmlUtil.cleanHtmlTag(StringUtils.blankToDefault(ip,""));
        // åˆ¤æ–­æ˜¯å¦ä¸ºIPv4
        boolean isIPv4 = NetUtils.isIPv4(ip);
        // åˆ¤æ–­æ˜¯å¦ä¸ºIPv6
        boolean isIPv6 = NetUtils.isIPv6(ip);
        // å¦‚果不是IPv4或IPv6,则返回未知IP
        if (!isIPv4 && !isIPv6) {
            return UNKNOWN_IP;
        }
        // å†…网不查询
        if ((isIPv4 && NetUtils.isInnerIP(ip)) || (isIPv6 && NetUtils.isInnerIPv6(ip))) {
            return LOCAL_ADDRESS;
        }
        // Tips:Ip2Region æä¾›äº†ç²¾ç®€çš„IPv6地址库,精简的IPv6地址库并不能完全支持IPv6地址的查询,且准确度上可能会存在问题,如需要准确的IPv6地址查询,建议自行实现
        return RegionUtils.getRegion(ip);
    }
}
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/ip/RegionUtils.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,155 @@
package org.dromara.common.core.utils.ip;
import cn.hutool.core.io.resource.ResourceUtil;
import lombok.extern.slf4j.Slf4j;
import org.dromara.common.core.exception.ServiceException;
import org.dromara.common.core.utils.StringUtils;
import org.lionsoul.ip2region.service.Config;
import org.lionsoul.ip2region.service.Ip2Region;
import org.lionsoul.ip2region.xdb.Util;
import java.io.InputStream;
import java.time.Duration;
/**
 * IP地址行政区域工具类
 * å‚考地址:<a href="https://gitee.com/lionsoul/ip2region/tree/master/binding/java">ip2region xdb java æŸ¥è¯¢å®¢æˆ·ç«¯å®žçް</a>
 * xdb数据库文件下载:<a href="https://gitee.com/lionsoul/ip2region/tree/master/data">ip2region data</a>
 *
 * @author ç§‹è¾žæœªå¯’
 */
@Slf4j
public class RegionUtils {
    // é»˜è®¤IPv4地址库文件路径
    // ä¸‹è½½åœ°å€ï¼šhttps://gitee.com/lionsoul/ip2region/blob/master/data/ip2region_v4.xdb
    public static final String DEFAULT_IPV4_XDB_PATH = "ip2region_v4.xdb";
    // é»˜è®¤IPv6地址库文件路径
    // ä¸‹è½½åœ°å€ï¼šhttps://gitee.com/lionsoul/ip2region/blob/master/data/ip2region_v6.xdb
    public static final String DEFAULT_IPV6_XDB_PATH = "ip2region_v6.xdb";
    // é»˜è®¤ç¼“存切片大小为15MB(仅针对BufferCache全量读取有效,如果你的xdb数据库很大,合理设置该值可以有效提升BufferCache模式下的查询效率,具体可以查看Ip2Region的README)
    // æ³¨æ„ï¼šè®¾ç½®è¿‡å¤§çš„值可能会申请内存时,因内存不足而导致OOM,请合理设置该值。
    // README:https://gitee.com/lionsoul/ip2region/tree/master/binding/java
    public static final int DEFAULT_CACHE_SLICE_BYTES = 1024 * 1024 * 15;
    // æœªçŸ¥åœ°å€
    public static final String UNKNOWN_ADDRESS = "未知";
    // Ip2Region服务实例
    private static Ip2Region ip2Region;
    // åˆå§‹åŒ–Ip2Region服务实例
    static {
        try {
            // æ³¨æ„ï¼šIp2Region çš„xdb文件加载策略 CachePolicy æœ‰ä¸‰ç§ï¼Œåˆ†åˆ«æ˜¯ï¼šBufferCache(全量读取xdb到内存中)、VIndexCache(默认策略,按需读取并缓存)、NoCache(实时读取)
            // æœ¬é¡¹ç›®å·¥å…·ä½¿ç”¨çš„ CachePolicy ä¸º BufferCache,BufferCache会加载整个xdb文件到内存中,setXdbInputStream ä»…支持 BufferCache ç­–略。
            // å› ä¸ºåŠ è½½æ•´ä¸ªxdb文件会耗费非常大的内存,如果你不希望加载整个xdb到内存中,更推荐使用 VIndexCache æˆ– NoCache(即实时读取文件)策略和 setXdbPath/setXdbFile åŠ è½½æ–¹æ³•ï¼ˆéœ€è¦æ³¨æ„çš„ä¸€ç‚¹ï¼ŒsetXdbPath å’Œ setXdbFile ä¸æ”¯æŒè¯»å–ClassPath(即源码和resource目录)中的文件)。
            // ä¸€èˆ¬è€Œè¨€ï¼Œæ›´å»ºè®®æŠŠxdb数据库放到一个指定的文件目录中(即不打包进jar包中),然后使用 VIndexCache + é…åˆSearcherPool的并发池读取数据,更方便随时更新xdb数据库
            InputStream v4InputStream = ResourceUtil.getStream(DEFAULT_IPV4_XDB_PATH);
            // IPv4配置
            Config v4Config = Config.custom()
                .setCachePolicy(Config.BufferCache)
                //.setXdbFile(v4TempXdb)
                .setXdbInputStream(v4InputStream)
                //
                .setCacheSliceBytes(DEFAULT_CACHE_SLICE_BYTES)
                .asV4();
            // IPv6配置
            Config v6Config = null;
            InputStream v6XdbInputStream = ResourceUtil.getStreamSafe(DEFAULT_IPV6_XDB_PATH);
            if (v6XdbInputStream == null) {
                log.warn("未加载 IPv6 åœ°å€åº“:未在类路径下找到文件 {}。当前仅启用 IPv4 æŸ¥è¯¢ã€‚如需启用 IPv6,请将 ip2region_v6.xdb æ”¾ç½®åˆ° resources ç›®å½•", DEFAULT_IPV6_XDB_PATH);
            } else {
                v6Config = Config.custom()
                    .setCachePolicy(Config.BufferCache)
                    //.setXdbFile(v6TempXdb)
                    .setXdbInputStream(v6XdbInputStream)
                    .setCacheSliceBytes(DEFAULT_CACHE_SLICE_BYTES)
                    .asV6();
            }
            // åˆå§‹åŒ–Ip2Region实例
            RegionUtils.ip2Region = Ip2Region.create(v4Config, v6Config);
            log.debug("IP工具初始化成功,加载IP地址库数据成功!");
        } catch (Exception e) {
            throw new ServiceException("RegionUtils初始化失败,原因:{}", e.getMessage());
        }
    }
    /**
     * æ ¹æ®IP地址离线获取城市
     *
     * @param ipString ip地址字符串
     */
    public static String getRegion(String ipString) {
        try {
            String region = ip2Region.search(ipString);
            if (StringUtils.isBlank(region)) {
                region = UNKNOWN_ADDRESS;
            }
            return region;
        } catch (Exception e) {
            log.error("IP地址离线获取城市异常 {}", ipString);
            return UNKNOWN_ADDRESS;
        }
    }
    /**
     * æ ¹æ®IP地址离线获取城市
     *
     * @param ipBytes ip地址字节数组
     */
    public static String getRegion(byte[] ipBytes) {
        try {
            String region = ip2Region.search(ipBytes);
            if (StringUtils.isBlank(region)) {
                region = UNKNOWN_ADDRESS;
            }
            return region;
        } catch (Exception e) {
            log.error("IP地址离线获取城市异常 {}", Util.ipToString(ipBytes));
            return UNKNOWN_ADDRESS;
        }
    }
    /**
     * å…³é—­Ip2Region服务
     */
    public static void close() {
        if (ip2Region == null) {
            return;
        }
        try {
            ip2Region.close(10000);
        } catch (Exception e) {
            log.error("Ip2Region服务关闭异常", e);
        }
    }
    /**
     * å…³é—­Ip2Region服务
     *
     * @param timeout å…³é—­è¶…æ—¶æ—¶é—´
     */
    public static void close(final Duration timeout) {
        if (ip2Region == null) {
            return;
        }
        if (timeout == null) {
            close();
            return;
        }
        try {
            ip2Region.close(timeout.toMillis());
        } catch (Exception e) {
            log.error("Ip2Region服务关闭异常", e);
        }
    }
}
RuoYi-Vue-Plus/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-Vue-Plus/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/regex/RegexUtils.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,31 @@
package org.dromara.common.core.utils.regex;
import cn.hutool.core.util.ReUtil;
import org.dromara.common.core.constant.RegexConstants;
/**
 * æ­£åˆ™ç›¸å…³å·¥å…·ç±»
 *
 * @author Feng
 */
public final class RegexUtils extends ReUtil {
    /**
     * ä»Žè¾“入字符串中提取匹配的部分,如果没有匹配则返回默认值
     *
     * @param input        è¦æå–的输入字符串
     * @param regex        ç”¨äºŽåŒ¹é…çš„æ­£åˆ™è¡¨è¾¾å¼ï¼Œå¯ä»¥ä½¿ç”¨ {@link RegexConstants} ä¸­å®šä¹‰çš„常量
     * @param defaultInput å¦‚果没有匹配时返回的默认值
     * @return å¦‚果找到匹配的部分,则返回匹配的部分,否则返回默认值
     */
    public static String extractFromString(String input, String regex, String defaultInput) {
        try {
            String str = ReUtil.get(regex, input, 1);
            return str == null ? defaultInput : str;
        } catch (Exception e) {
            return defaultInput;
        }
    }
}
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/regex/RegexValidator.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,105 @@
package org.dromara.common.core.utils.regex;
import cn.hutool.core.exceptions.ValidateException;
import cn.hutool.core.lang.Validator;
import org.dromara.common.core.factory.RegexPatternPoolFactory;
import java.util.regex.Pattern;
/**
 * æ­£åˆ™å­—段校验器
 * ä¸»è¦éªŒè¯å­—段非空、是否为满足指定格式等
 *
 * @author Feng
 */
public class RegexValidator extends Validator {
    /**
     * å­—典类型必须以字母开头,且只能为(小写字母,数字,下滑线)
     */
    public static final Pattern DICTIONARY_TYPE = RegexPatternPoolFactory.DICTIONARY_TYPE;
    /**
     * èº«ä»½è¯å·ç ï¼ˆåŽ6位)
     */
    public static final Pattern ID_CARD_LAST_6 = RegexPatternPoolFactory.ID_CARD_LAST_6;
    /**
     * QQ号码
     */
    public static final Pattern QQ_NUMBER = RegexPatternPoolFactory.QQ_NUMBER;
    /**
     * é‚®æ”¿ç¼–码
     */
    public static final Pattern POSTAL_CODE = RegexPatternPoolFactory.POSTAL_CODE;
    /**
     * æ³¨å†Œè´¦å·
     */
    public static final Pattern ACCOUNT = RegexPatternPoolFactory.ACCOUNT;
    /**
     * å¯†ç ï¼šåŒ…含至少8个字符,包括大写字母、小写字母、数字和特殊字符
     */
    public static final Pattern PASSWORD = RegexPatternPoolFactory.PASSWORD;
    /**
     * é€šç”¨çŠ¶æ€ï¼ˆ0表示正常,1表示停用)
     */
    public static final Pattern STATUS = RegexPatternPoolFactory.STATUS;
    /**
     * æ£€æŸ¥è¾“入的账号是否匹配预定义的规则
     *
     * @param value è¦éªŒè¯çš„账号
     * @return å¦‚果账号符合规则,返回 true;否则,返回 false。
     */
    public static boolean isAccount(CharSequence value) {
        return isMatchRegex(ACCOUNT, value);
    }
    /**
     * éªŒè¯è¾“入的账号是否符合规则,如果不符合,则抛出 ValidateException å¼‚常
     *
     * @param value    è¦éªŒè¯çš„账号
     * @param errorMsg éªŒè¯å¤±è´¥æ—¶æŠ›å‡ºçš„异常消息
     * @param <T>      CharSequence çš„子类型
     * @return å¦‚果验证通过,返回输入的账号
     * @throws ValidateException å¦‚果验证失败
     */
    public static <T extends CharSequence> T validateAccount(T value, String errorMsg) throws ValidateException {
        if (!isAccount(value)) {
            throw new ValidateException(errorMsg);
        }
        return value;
    }
    /**
     * æ£€æŸ¥è¾“入的状态是否匹配预定义的规则
     *
     * @param value è¦éªŒè¯çš„状态
     * @return å¦‚果状态符合规则,返回 true;否则,返回 false。
     */
    public static boolean isStatus(CharSequence value) {
        return isMatchRegex(STATUS, value);
    }
    /**
     * éªŒè¯è¾“入的状态是否符合规则,如果不符合,则抛出 ValidateException å¼‚常
     *
     * @param value    è¦éªŒè¯çš„状态
     * @param errorMsg éªŒè¯å¤±è´¥æ—¶æŠ›å‡ºçš„异常消息
     * @param <T>      CharSequence çš„子类型
     * @return å¦‚果验证通过,返回输入的状态
     * @throws ValidateException å¦‚果验证失败
     */
    public static <T extends CharSequence> T validateStatus(T value, String errorMsg) throws ValidateException {
        if (!isStatus(value)) {
            throw new ValidateException(errorMsg);
        }
        return value;
    }
}
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/sql/SqlUtil.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,56 @@
package org.dromara.common.core.utils.sql;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import org.dromara.common.core.utils.StringUtils;
/**
 * sql操作工具类
 *
 * @author ruoyi
 */
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class SqlUtil {
    /**
     * å®šä¹‰å¸¸ç”¨çš„ sql关键字
     */
    public static String SQL_REGEX = "\u000B|and |extractvalue|updatexml|sleep|exec |insert |select |delete |update |drop |count |chr |mid |master |truncate |char |declare |or |union |like |+|/*|user()";
    /**
     * ä»…支持字母、数字、下划线、空格、逗号、小数点(支持多个字段排序)
     */
    public static final String SQL_PATTERN = "[a-zA-Z0-9_\\ \\,\\.]+";
    /**
     * æ£€æŸ¥å­—符,防止注入绕过
     */
    public static String escapeOrderBySql(String value) {
        if (StringUtils.isNotEmpty(value) && !isValidOrderBySql(value)) {
            throw new IllegalArgumentException("参数不符合规范,不能进行查询");
        }
        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 IllegalArgumentException("参数存在SQL注入风险");
            }
        }
    }
}
RuoYi-Vue-Plus/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-Vue-Plus/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-Vue-Plus/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-Vue-Plus/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/validate/dicts/DictPattern.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,40 @@
package org.dromara.common.core.validate.dicts;
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;
/**
 * å­—典项校验注解
 *
 * @author AprilWind
 */
@Constraint(validatedBy = DictPatternValidator.class)
@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
public @interface DictPattern {
    /**
     * å­—典类型,如 "sys_user_sex"
     */
    String dictType();
    /**
     * åˆ†éš”符
     */
    String separator();
    /**
     * é»˜è®¤æ ¡éªŒå¤±è´¥æç¤ºä¿¡æ¯
     */
    String message() default "字典值无效";
    Class<?>[] groups() default {};
    Class<? extends Payload>[] payload() default {};
}
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/validate/dicts/DictPatternValidator.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,55 @@
package org.dromara.common.core.validate.dicts;
import jakarta.validation.ConstraintValidator;
import jakarta.validation.ConstraintValidatorContext;
import org.dromara.common.core.service.DictService;
import org.dromara.common.core.utils.SpringUtils;
import org.dromara.common.core.utils.StringUtils;
/**
 * è‡ªå®šä¹‰å­—典值校验器
 *
 * @author AprilWind
 */
public class DictPatternValidator implements ConstraintValidator<DictPattern, String> {
    /**
     * å­—典类型
     */
    private String dictType;
    /**
     * åˆ†éš”符
     */
    private String separator = ",";
    /**
     * åˆå§‹åŒ–校验器,提取注解上的字典类型
     *
     * @param annotation æ³¨è§£å®žä¾‹
     */
    @Override
    public void initialize(DictPattern annotation) {
        this.dictType = annotation.dictType();
        if (StringUtils.isNotBlank(annotation.separator())) {
            this.separator = annotation.separator();
        }
    }
    /**
     * æ ¡éªŒå­—段值是否为指定字典类型中的合法值
     *
     * @param value   è¢«æ ¡éªŒçš„字段值
     * @param context æ ¡éªŒä¸Šä¸‹æ–‡ï¼ˆå¯ç”¨äºŽæž„建错误信息)
     * @return true è¡¨ç¤ºæ ¡éªŒé€šè¿‡ï¼ˆåˆæ³•字典值),false è¡¨ç¤ºä¸é€šè¿‡
     */
    @Override
    public boolean isValid(String value, ConstraintValidatorContext context) {
        if (StringUtils.isBlank(dictType) || StringUtils.isBlank(value)) {
            return false;
        }
        String dictLabel = SpringUtils.getBean(DictService.class).getDictLabel(dictType, value, separator);
        return StringUtils.isNotBlank(dictLabel);
    }
}
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/validate/enumd/EnumPattern.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,48 @@
package org.dromara.common.core.validate.enumd;
import jakarta.validation.Constraint;
import jakarta.validation.Payload;
import java.lang.annotation.*;
import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
/**
 * è‡ªå®šä¹‰æžšä¸¾æ ¡éªŒ
 *
 * @author ç§‹è¾žæœªå¯’
 * @date 2024-12-09
 */
@Documented
@Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE})
@Retention(RUNTIME)
@Repeatable(EnumPattern.List.class) // å…è®¸åœ¨åŒä¸€å…ƒç´ ä¸Šå¤šæ¬¡ä½¿ç”¨è¯¥æ³¨è§£
@Constraint(validatedBy = {EnumPatternValidator.class})
public @interface EnumPattern {
    /**
     * éœ€è¦æ ¡éªŒçš„æžšä¸¾ç±»åž‹
     */
    Class<? extends Enum<?>> type();
    /**
     * æžšä¸¾ç±»åž‹æ ¡éªŒå€¼å­—段名称
     * éœ€ç¡®ä¿è¯¥å­—段实现了 getter æ–¹æ³•
     */
    String fieldName();
    String message() default "输入值不在枚举范围内";
    Class<?>[] groups() default {};
    Class<? extends Payload>[] payload() default {};
    @Documented
    @Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE})
    @Retention(RUNTIME)
    @interface List {
        EnumPattern[] value();
    }
}
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/validate/enumd/EnumPatternValidator.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,37 @@
package org.dromara.common.core.validate.enumd;
import jakarta.validation.ConstraintValidator;
import jakarta.validation.ConstraintValidatorContext;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.core.utils.reflect.ReflectUtils;
/**
 * è‡ªå®šä¹‰æžšä¸¾æ ¡éªŒæ³¨è§£å®žçް
 *
 * @author ç§‹è¾žæœªå¯’
 * @date 2024-12-09
 */
public class EnumPatternValidator implements ConstraintValidator<EnumPattern, String> {
    private EnumPattern annotation;
    @Override
    public void initialize(EnumPattern annotation) {
        ConstraintValidator.super.initialize(annotation);
        this.annotation = annotation;
    }
    @Override
    public boolean isValid(String value, ConstraintValidatorContext constraintValidatorContext) {
        if (StringUtils.isNotBlank(value)) {
            String fieldName = annotation.fieldName();
            for (Object e : annotation.type().getEnumConstants()) {
                if (value.equals(ReflectUtils.invokeGetter(e, fieldName))) {
                    return true;
                }
            }
        }
        return false;
    }
}
RuoYi-Vue-Plus/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-Vue-Plus/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-Vue-Plus/ruoyi-common/ruoyi-common-core/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,4 @@
org.dromara.common.core.config.ApplicationConfig
org.dromara.common.core.config.ThreadPoolConfig
org.dromara.common.core.config.ValidatorConfig
org.dromara.common.core.utils.SpringUtils
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-doc/.flattened-pom.xml
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,32 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <modelVersion>4.0.0</modelVersion>
  <parent>
    <groupId>org.dromara</groupId>
    <artifactId>ruoyi-common</artifactId>
    <version>5.5.3</version>
  </parent>
  <groupId>org.dromara</groupId>
  <artifactId>ruoyi-common-doc</artifactId>
  <version>5.5.3</version>
  <description>ruoyi-common-doc ç³»ç»ŸæŽ¥å£</description>
  <dependencies>
    <dependency>
      <groupId>org.dromara</groupId>
      <artifactId>ruoyi-common-core</artifactId>
    </dependency>
    <dependency>
      <groupId>org.springdoc</groupId>
      <artifactId>springdoc-openapi-starter-webmvc-api</artifactId>
    </dependency>
    <dependency>
      <groupId>com.github.therapi</groupId>
      <artifactId>therapi-runtime-javadoc</artifactId>
    </dependency>
    <dependency>
      <groupId>com.fasterxml.jackson.module</groupId>
      <artifactId>jackson-module-kotlin</artifactId>
    </dependency>
  </dependencies>
</project>
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-doc/pom.xml
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,41 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <groupId>org.dromara</groupId>
        <artifactId>ruoyi-common</artifactId>
        <version>${revision}</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>
    <artifactId>ruoyi-common-doc</artifactId>
    <description>
        ruoyi-common-doc ç³»ç»ŸæŽ¥å£
    </description>
    <dependencies>
        <dependency>
            <groupId>org.dromara</groupId>
            <artifactId>ruoyi-common-core</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springdoc</groupId>
            <artifactId>springdoc-openapi-starter-webmvc-api</artifactId>
        </dependency>
        <dependency>
            <groupId>com.github.therapi</groupId>
            <artifactId>therapi-runtime-javadoc</artifactId>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.module</groupId>
            <artifactId>jackson-module-kotlin</artifactId>
        </dependency>
    </dependencies>
</project>
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-doc/src/main/java/org/dromara/common/doc/config/SpringDocConfig.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,127 @@
package org.dromara.common.doc.config;
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.dromara.common.core.utils.StringUtils;
import org.dromara.common.doc.config.properties.SpringDocProperties;
import org.dromara.common.doc.handler.OpenApiHandler;
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;
/**
 * æŽ¥å£æ–‡æ¡£é…ç½®
 *
 * @author Lion Li
 */
@RequiredArgsConstructor
@AutoConfiguration(before = SpringDocConfiguration.class)
@EnableConfigurationProperties(SpringDocProperties.class)
@ConditionalOnProperty(name = "springdoc.api-docs.enabled", havingValue = "true", matchIfMissing = true)
public class SpringDocConfig {
    private final ServerProperties serverProperties;
    @Bean
    @ConditionalOnMissingBean(OpenAPI.class)
    public OpenAPI openApi(SpringDocProperties properties) {
        OpenAPI openApi = new OpenAPI();
        // æ–‡æ¡£åŸºæœ¬ä¿¡æ¯
        SpringDocProperties.InfoProperties infoProperties = properties.getInfo();
        Info info = convertInfo(infoProperties);
        openApi.info(info);
        // æ‰©å±•文档信息
        openApi.externalDocs(properties.getExternalDocs());
        openApi.tags(properties.getTags());
        openApi.paths(properties.getPaths());
        if (properties.getComponents() != null) {
            openApi.components(properties.getComponents());
            Set<String> keySet = properties.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(SpringDocProperties.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 openApiCustomizer() {
        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-Vue-Plus/ruoyi-common/ruoyi-common-doc/src/main/java/org/dromara/common/doc/config/properties/SpringDocProperties.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 = "springdoc")
public class SpringDocProperties {
    /**
     * æ–‡æ¡£åŸºæœ¬ä¿¡æ¯
     */
    @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-Vue-Plus/ruoyi-common/ruoyi-common-doc/src/main/java/org/dromara/common/doc/handler/OpenApiHandler.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,253 @@
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.dromara.common.core.utils.StreamUtils;
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(StreamUtils.toSet(methodTags, tag -> propertyResolverUtils.resolve(tag.name(), locale)));
            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-Vue-Plus/ruoyi-common/ruoyi-common-doc/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1 @@
org.dromara.common.doc.config.SpringDocConfig
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-encrypt/.flattened-pom.xml
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,43 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <modelVersion>4.0.0</modelVersion>
  <parent>
    <groupId>org.dromara</groupId>
    <artifactId>ruoyi-common</artifactId>
    <version>5.5.3</version>
  </parent>
  <groupId>org.dromara</groupId>
  <artifactId>ruoyi-common-encrypt</artifactId>
  <version>5.5.3</version>
  <description>ruoyi-common-encrypt æ•°æ®åŠ è§£å¯†æ¨¡å—</description>
  <dependencies>
    <dependency>
      <groupId>org.dromara</groupId>
      <artifactId>ruoyi-common-core</artifactId>
    </dependency>
    <dependency>
      <groupId>org.bouncycastle</groupId>
      <artifactId>bcprov-jdk15to18</artifactId>
    </dependency>
    <dependency>
      <groupId>cn.hutool</groupId>
      <artifactId>hutool-crypto</artifactId>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-webmvc</artifactId>
    </dependency>
    <dependency>
      <groupId>com.baomidou</groupId>
      <artifactId>mybatis-plus-spring-boot3-starter</artifactId>
      <exclusions>
        <exclusion>
          <groupId>org.mybatis</groupId>
          <artifactId>mybatis-spring</artifactId>
        </exclusion>
      </exclusions>
      <optional>true</optional>
    </dependency>
  </dependencies>
</project>
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-encrypt/pom.xml
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,54 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <groupId>org.dromara</groupId>
        <artifactId>ruoyi-common</artifactId>
        <version>${revision}</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>
    <artifactId>ruoyi-common-encrypt</artifactId>
    <description>
        ruoyi-common-encrypt æ•°æ®åŠ è§£å¯†æ¨¡å—
    </description>
    <dependencies>
        <dependency>
            <groupId>org.dromara</groupId>
            <artifactId>ruoyi-common-core</artifactId>
        </dependency>
        <dependency>
            <groupId>org.bouncycastle</groupId>
            <artifactId>bcprov-jdk15to18</artifactId>
        </dependency>
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-crypto</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
        </dependency>
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-spring-boot3-starter</artifactId>
            <optional>true</optional>
            <exclusions>
                <exclusion>
                    <groupId>org.mybatis</groupId>
                    <artifactId>mybatis-spring</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
    </dependencies>
</project>
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/annotation/ApiEncrypt.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,20 @@
package org.dromara.common.encrypt.annotation;
import java.lang.annotation.*;
/**
 * å¼ºåˆ¶åŠ å¯†æ³¨è§£
 *
 * @author Michelle.Chung
 */
@Documented
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface ApiEncrypt {
    /**
     * å“åº”加密忽略,默认不加密,为 true æ—¶åР坆
     */
    boolean response() default false;
}
RuoYi-Vue-Plus/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-Vue-Plus/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/config/ApiDecryptAutoConfiguration.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,34 @@
package org.dromara.common.encrypt.config;
import jakarta.servlet.DispatcherType;
import org.dromara.common.encrypt.filter.CryptoFilter;
import org.dromara.common.encrypt.properties.ApiDecryptProperties;
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.FilterRegistration;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
/**
 * api è§£å¯†è‡ªåŠ¨é…ç½®
 *
 * @author wdhcr
 */
@AutoConfiguration
@EnableConfigurationProperties(ApiDecryptProperties.class)
@ConditionalOnProperty(value = "api-decrypt.enabled", havingValue = "true")
public class ApiDecryptAutoConfiguration {
    @Bean
    @FilterRegistration(
        name = "cryptoFilter",
        urlPatterns = "/*",
        order = FilterRegistrationBean.HIGHEST_PRECEDENCE,
        dispatcherTypes = DispatcherType.REQUEST
    )
    public CryptoFilter cryptoFilter(ApiDecryptProperties properties) {
        return new CryptoFilter(properties);
    }
}
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/config/EncryptorAutoConfiguration.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,49 @@
package org.dromara.common.encrypt.config;
import com.baomidou.mybatisplus.autoconfigure.MybatisPlusAutoConfiguration;
import com.baomidou.mybatisplus.autoconfigure.MybatisPlusProperties;
import lombok.extern.slf4j.Slf4j;
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(after = MybatisPlusAutoConfiguration.class)
@EnableConfigurationProperties(EncryptorProperties.class)
@ConditionalOnProperty(value = "mybatis-encryptor.enable", havingValue = "true")
@Slf4j
public class EncryptorAutoConfiguration {
    @Autowired
    private EncryptorProperties properties;
    @Bean
    public EncryptorManager encryptorManager(MybatisPlusProperties mybatisPlusProperties) {
        return new EncryptorManager(mybatisPlusProperties.getTypeAliasesPackage());
    }
    @Bean
    public MybatisEncryptInterceptor mybatisEncryptInterceptor(EncryptorManager encryptorManager) {
        return new MybatisEncryptInterceptor(encryptorManager, properties);
    }
    @Bean
    public MybatisDecryptInterceptor mybatisDecryptInterceptor(EncryptorManager encryptorManager) {
        return new MybatisDecryptInterceptor(encryptorManager, properties);
    }
}
RuoYi-Vue-Plus/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-Vue-Plus/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/core/EncryptorManager.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,168 @@
package org.dromara.common.encrypt.core;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ReflectUtil;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.io.Resources;
import org.dromara.common.core.constant.Constants;
import org.dromara.common.core.utils.ObjectUtils;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.encrypt.annotation.EncryptField;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.core.type.ClassMetadata;
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
import org.springframework.util.ClassUtils;
import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
/**
 * åŠ å¯†ç®¡ç†ç±»
 *
 * @author è€é©¬
 * @version 4.6.0
 */
@Slf4j
@NoArgsConstructor
public class EncryptorManager {
    /**
     * ç¼“存加密器
     */
    Map<Integer, IEncryptor> encryptorMap = new ConcurrentHashMap<>();
    /**
     * ç±»åŠ å¯†å­—æ®µç¼“å­˜
     */
    Map<Class<?>, Set<Field>> fieldCache = new ConcurrentHashMap<>();
    /**
     * æž„造方法传入类加密字段缓存
     *
     * @param typeAliasesPackage å®žä½“类包
     */
    public EncryptorManager(String typeAliasesPackage) {
        scanEncryptClasses(typeAliasesPackage);
    }
    /**
     * èŽ·å–ç±»åŠ å¯†å­—æ®µç¼“å­˜
     */
    public Set<Field> getFieldCache(Class<?> sourceClazz) {
        return ObjectUtils.notNullGetter(fieldCache, f -> f.get(sourceClazz));
    }
    /**
     * æ³¨å†ŒåŠ å¯†æ‰§è¡Œè€…åˆ°ç¼“å­˜
     *
     * @param encryptContext åŠ å¯†æ‰§è¡Œè€…éœ€è¦çš„ç›¸å…³é…ç½®å‚æ•°
     */
    public IEncryptor registAndGetEncryptor(EncryptContext encryptContext) {
        int key = encryptContext.hashCode();
        if (encryptorMap.containsKey(key)) {
            return encryptorMap.get(key);
        }
        IEncryptor encryptor = ReflectUtil.newInstance(encryptContext.getAlgorithm().getClazz(), encryptContext);
        encryptorMap.put(key, encryptor);
        return encryptor;
    }
    /**
     * ç§»é™¤ç¼“存中的加密执行者
     *
     * @param encryptContext åŠ å¯†æ‰§è¡Œè€…éœ€è¦çš„ç›¸å…³é…ç½®å‚æ•°
     */
    public void removeEncryptor(EncryptContext encryptContext) {
        this.encryptorMap.remove(encryptContext.hashCode());
    }
    /**
     * æ ¹æ®é…ç½®è¿›è¡ŒåŠ å¯†ã€‚ä¼šè¿›è¡Œæœ¬åœ°ç¼“å­˜å¯¹åº”çš„ç®—æ³•å’Œå¯¹åº”çš„ç§˜é’¥ä¿¡æ¯ã€‚
     *
     * @param value          å¾…加密的值
     * @param encryptContext åŠ å¯†ç›¸å…³çš„é…ç½®ä¿¡æ¯
     */
    public String encrypt(String value, EncryptContext encryptContext) {
        if (StringUtils.startsWith(value, Constants.ENCRYPT_HEADER)) {
            return value;
        }
        IEncryptor encryptor = this.registAndGetEncryptor(encryptContext);
        String encrypt = encryptor.encrypt(value, encryptContext.getEncode());
        return Constants.ENCRYPT_HEADER + encrypt;
    }
    /**
     * æ ¹æ®é…ç½®è¿›è¡Œè§£å¯†
     *
     * @param value          å¾…解密的值
     * @param encryptContext åŠ å¯†ç›¸å…³çš„é…ç½®ä¿¡æ¯
     */
    public String decrypt(String value, EncryptContext encryptContext) {
        if (!StringUtils.startsWith(value, Constants.ENCRYPT_HEADER)) {
            return value;
        }
        IEncryptor encryptor = this.registAndGetEncryptor(encryptContext);
        String str = StringUtils.removeStart(value, Constants.ENCRYPT_HEADER);
        return encryptor.decrypt(str);
    }
    /**
     * é€šè¿‡ typeAliasesPackage è®¾ç½®çš„æ‰«æåŒ… æ‰«æç¼“存实体
     */
    private void scanEncryptClasses(String typeAliasesPackage) {
        PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
        CachingMetadataReaderFactory factory = new CachingMetadataReaderFactory();
        String[] packagePatternArray = StringUtils.splitPreserveAllTokens(typeAliasesPackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
        String classpath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX;
        try {
            for (String packagePattern : packagePatternArray) {
                String path = ClassUtils.convertClassNameToResourcePath(packagePattern);
                Resource[] resources = resolver.getResources(classpath + path + "/*.class");
                for (Resource resource : resources) {
                    ClassMetadata classMetadata = factory.getMetadataReader(resource).getClassMetadata();
                    Class<?> clazz = Resources.classForName(classMetadata.getClassName());
                    Set<Field> encryptFieldSet = getEncryptFieldSetFromClazz(clazz);
                    if (CollUtil.isNotEmpty(encryptFieldSet)) {
                        fieldCache.put(clazz, encryptFieldSet);
                    }
                }
            }
        } catch (Exception e) {
            log.error("初始化数据安全缓存时出错:{}", e.getMessage());
        }
    }
    /**
     * èŽ·å¾—ä¸€ä¸ªç±»çš„åŠ å¯†å­—æ®µé›†åˆ
     */
    private Set<Field> getEncryptFieldSetFromClazz(Class<?> clazz) {
        Set<Field> fieldSet = new HashSet<>();
        // åˆ¤æ–­clazz如果是接口,内部类,匿名类就直接返回
        if (clazz.isInterface() || clazz.isMemberClass() || clazz.isAnonymousClass()) {
            return fieldSet;
        }
        while (clazz != null) {
            Field[] fields = clazz.getDeclaredFields();
            fieldSet.addAll(Arrays.asList(fields));
            clazz = clazz.getSuperclass();
        }
        fieldSet = fieldSet.stream().filter(field ->
                field.isAnnotationPresent(EncryptField.class) && field.getType() == String.class)
            .collect(Collectors.toSet());
        for (Field field : fieldSet) {
            field.setAccessible(true);
        }
        return fieldSet;
    }
}
RuoYi-Vue-Plus/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-Vue-Plus/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-Vue-Plus/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/core/encryptor/AesEncryptor.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,55 @@
package org.dromara.common.encrypt.core.encryptor;
import org.dromara.common.encrypt.core.EncryptContext;
import org.dromara.common.encrypt.enumd.AlgorithmType;
import org.dromara.common.encrypt.enumd.EncodeType;
import org.dromara.common.encrypt.utils.EncryptUtils;
/**
 * AES算法实现
 *
 * @author è€é©¬
 * @version 4.6.0
 */
public class AesEncryptor extends AbstractEncryptor {
    private final EncryptContext context;
    public AesEncryptor(EncryptContext context) {
        super(context);
        this.context = context;
    }
    /**
     * èŽ·å¾—å½“å‰ç®—æ³•
     */
    @Override
    public AlgorithmType algorithm() {
        return AlgorithmType.AES;
    }
    /**
     * åР坆
     *
     * @param value      å¾…加密字符串
     * @param encodeType åŠ å¯†åŽçš„ç¼–ç æ ¼å¼
     */
    @Override
    public String encrypt(String value, EncodeType encodeType) {
        if (encodeType == EncodeType.HEX) {
            return EncryptUtils.encryptByAesHex(value, context.getPassword());
        } else {
            return EncryptUtils.encryptByAes(value, context.getPassword());
        }
    }
    /**
     * è§£å¯†
     *
     * @param value      å¾…加密字符串
     */
    @Override
    public String decrypt(String value) {
        return EncryptUtils.decryptByAes(value, context.getPassword());
    }
}
RuoYi-Vue-Plus/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 org.dromara.common.encrypt.core.EncryptContext;
import org.dromara.common.encrypt.enumd.AlgorithmType;
import org.dromara.common.encrypt.enumd.EncodeType;
import org.dromara.common.encrypt.utils.EncryptUtils;
/**
 * 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 EncryptUtils.encryptByBase64(value);
    }
    /**
     * è§£å¯†
     *
     * @param value      å¾…加密字符串
     */
    @Override
    public String decrypt(String value) {
        return EncryptUtils.decryptByBase64(value);
    }
}
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/core/encryptor/RsaEncryptor.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,62 @@
package org.dromara.common.encrypt.core.encryptor;
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;
import org.dromara.common.encrypt.utils.EncryptUtils;
/**
 * RSA算法实现
 *
 * @author è€é©¬
 * @version 4.6.0
 */
public class RsaEncryptor extends AbstractEncryptor {
    private final EncryptContext context;
    public RsaEncryptor(EncryptContext context) {
        super(context);
        String privateKey = context.getPrivateKey();
        String publicKey = context.getPublicKey();
        if (StringUtils.isAnyEmpty(privateKey, publicKey)) {
            throw new IllegalArgumentException("RSA公私钥均需要提供,公钥加密,私钥解密。");
        }
        this.context = context;
    }
    /**
     * èŽ·å¾—å½“å‰ç®—æ³•
     */
    @Override
    public AlgorithmType algorithm() {
        return AlgorithmType.RSA;
    }
    /**
     * åР坆
     *
     * @param value      å¾…加密字符串
     * @param encodeType åŠ å¯†åŽçš„ç¼–ç æ ¼å¼
     */
    @Override
    public String encrypt(String value, EncodeType encodeType) {
        if (encodeType == EncodeType.HEX) {
            return EncryptUtils.encryptByRsaHex(value, context.getPublicKey());
        } else {
            return EncryptUtils.encryptByRsa(value, context.getPublicKey());
        }
    }
    /**
     * è§£å¯†
     *
     * @param value      å¾…加密字符串
     */
    @Override
    public String decrypt(String value) {
        return EncryptUtils.decryptByRsa(value, context.getPrivateKey());
    }
}
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/core/encryptor/Sm2Encryptor.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,61 @@
package org.dromara.common.encrypt.core.encryptor;
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;
import org.dromara.common.encrypt.utils.EncryptUtils;
/**
 * sm2算法实现
 *
 * @author è€é©¬
 * @version 4.6.0
 */
public class Sm2Encryptor extends AbstractEncryptor {
    private final EncryptContext context;
    public Sm2Encryptor(EncryptContext context) {
        super(context);
        String privateKey = context.getPrivateKey();
        String publicKey = context.getPublicKey();
        if (StringUtils.isAnyEmpty(privateKey, publicKey)) {
            throw new IllegalArgumentException("SM2公私钥均需要提供,公钥加密,私钥解密。");
        }
        this.context = context;
    }
    /**
     * èŽ·å¾—å½“å‰ç®—æ³•
     */
    @Override
    public AlgorithmType algorithm() {
        return AlgorithmType.SM2;
    }
    /**
     * åР坆
     *
     * @param value      å¾…加密字符串
     * @param encodeType åŠ å¯†åŽçš„ç¼–ç æ ¼å¼
     */
    @Override
    public String encrypt(String value, EncodeType encodeType) {
        if (encodeType == EncodeType.HEX) {
            return EncryptUtils.encryptBySm2Hex(value, context.getPublicKey());
        } else {
            return EncryptUtils.encryptBySm2(value, context.getPublicKey());
        }
    }
    /**
     * è§£å¯†
     *
     * @param value      å¾…加密字符串
     */
    @Override
    public String decrypt(String value) {
        return EncryptUtils.decryptBySm2(value, context.getPrivateKey());
    }
}
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/core/encryptor/Sm4Encryptor.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,55 @@
package org.dromara.common.encrypt.core.encryptor;
import org.dromara.common.encrypt.core.EncryptContext;
import org.dromara.common.encrypt.enumd.AlgorithmType;
import org.dromara.common.encrypt.enumd.EncodeType;
import org.dromara.common.encrypt.utils.EncryptUtils;
/**
 * sm4算法实现
 *
 * @author è€é©¬
 * @version 4.6.0
 */
public class Sm4Encryptor extends AbstractEncryptor {
    private final EncryptContext context;
    public Sm4Encryptor(EncryptContext context) {
        super(context);
        this.context = context;
    }
    /**
     * èŽ·å¾—å½“å‰ç®—æ³•
     */
    @Override
    public AlgorithmType algorithm() {
        return AlgorithmType.SM4;
    }
    /**
     * åР坆
     *
     * @param value      å¾…加密字符串
     * @param encodeType åŠ å¯†åŽçš„ç¼–ç æ ¼å¼
     */
    @Override
    public String encrypt(String value, EncodeType encodeType) {
        if (encodeType == EncodeType.HEX) {
            return EncryptUtils.encryptBySm4Hex(value, context.getPassword());
        } else {
            return EncryptUtils.encryptBySm4(value, context.getPassword());
        }
    }
    /**
     * è§£å¯†
     *
     * @param value      å¾…加密字符串
     */
    @Override
    public String decrypt(String value) {
        return EncryptUtils.decryptBySm4(value, context.getPassword());
    }
}
RuoYi-Vue-Plus/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-Vue-Plus/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-Vue-Plus/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/filter/CryptoFilter.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,110 @@
package org.dromara.common.encrypt.filter;
import cn.hutool.core.util.ObjectUtil;
import jakarta.servlet.*;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.dromara.common.core.constant.HttpStatus;
import org.dromara.common.core.exception.ServiceException;
import org.dromara.common.core.utils.SpringUtils;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.encrypt.annotation.ApiEncrypt;
import org.dromara.common.encrypt.properties.ApiDecryptProperties;
import org.springframework.http.HttpMethod;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.HandlerExecutionChain;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
import java.io.IOException;
/**
 * Crypto è¿‡æ»¤å™¨
 *
 * @author wdhcr
 */
public class CryptoFilter implements Filter {
    private final ApiDecryptProperties properties;
    public CryptoFilter(ApiDecryptProperties properties) {
        this.properties = properties;
    }
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        HttpServletRequest servletRequest = (HttpServletRequest) request;
        HttpServletResponse servletResponse = (HttpServletResponse) response;
        // èŽ·å–åŠ å¯†æ³¨è§£
        ApiEncrypt apiEncrypt = this.getApiEncryptAnnotation(servletRequest);
        boolean responseFlag = apiEncrypt != null && apiEncrypt.response();
        ServletRequest requestWrapper = null;
        ServletResponse responseWrapper = null;
        EncryptResponseBodyWrapper responseBodyWrapper = null;
        // æ˜¯å¦ä¸º put æˆ–者 post è¯·æ±‚
        if (HttpMethod.PUT.matches(servletRequest.getMethod()) || HttpMethod.POST.matches(servletRequest.getMethod())) {
            // æ˜¯å¦å­˜åœ¨åŠ å¯†æ ‡å¤´
            String headerValue = servletRequest.getHeader(properties.getHeaderFlag());
            if (StringUtils.isNotBlank(headerValue)) {
                // è¯·æ±‚解密
                requestWrapper = new DecryptRequestBodyWrapper(servletRequest, properties.getPrivateKey(), properties.getHeaderFlag());
            } else {
                // æ˜¯å¦æœ‰æ³¨è§£ï¼Œæœ‰å°±æŠ¥é”™ï¼Œæ²¡æœ‰æ”¾è¡Œ
                if (ObjectUtil.isNotNull(apiEncrypt)) {
                    HandlerExceptionResolver exceptionResolver = SpringUtils.getBean("handlerExceptionResolver", HandlerExceptionResolver.class);
                    exceptionResolver.resolveException(
                        servletRequest, servletResponse, null,
                        new ServiceException("没有访问权限,请联系管理员授权", HttpStatus.FORBIDDEN));
                    return;
                }
            }
        }
        // åˆ¤æ–­æ˜¯å¦å“åº”加密
        if (responseFlag) {
            responseBodyWrapper = new EncryptResponseBodyWrapper(servletResponse);
            responseWrapper = responseBodyWrapper;
        }
        chain.doFilter(
            ObjectUtil.defaultIfNull(requestWrapper, request),
            ObjectUtil.defaultIfNull(responseWrapper, response));
        if (responseFlag) {
            servletResponse.reset();
            // å¯¹åŽŸå§‹å†…å®¹åŠ å¯†
            String encryptContent = responseBodyWrapper.getEncryptContent(
                servletResponse, properties.getPublicKey(), properties.getHeaderFlag());
            // å¯¹åŠ å¯†åŽçš„å†…å®¹å†™å‡º
            servletResponse.getWriter().write(encryptContent);
        }
    }
    /**
     * èŽ·å– ApiEncrypt æ³¨è§£
     */
    private ApiEncrypt getApiEncryptAnnotation(HttpServletRequest servletRequest) {
        RequestMappingHandlerMapping handlerMapping = SpringUtils.getBean("requestMappingHandlerMapping", RequestMappingHandlerMapping.class);
        // èŽ·å–æ³¨è§£
        try {
            HandlerExecutionChain mappingHandler = handlerMapping.getHandler(servletRequest);
            if (ObjectUtil.isNotNull(mappingHandler)) {
                Object handler = mappingHandler.getHandler();
                if (ObjectUtil.isNotNull(handler)) {
                    // ä»Žhandler获取注解
                    if (handler instanceof HandlerMethod handlerMethod) {
                        return handlerMethod.getMethodAnnotation(ApiEncrypt.class);
                    }
                }
            }
        } catch (Exception e) {
            return null;
        }
        return null;
    }
    @Override
    public void destroy() {
    }
}
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/filter/DecryptRequestBodyWrapper.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,94 @@
package org.dromara.common.encrypt.filter;
import cn.hutool.core.io.IoUtil;
import jakarta.servlet.ReadListener;
import jakarta.servlet.ServletInputStream;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletRequestWrapper;
import org.dromara.common.core.constant.Constants;
import org.dromara.common.encrypt.utils.EncryptUtils;
import org.springframework.http.MediaType;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
/**
 * è§£å¯†è¯·æ±‚参数工具类
 *
 * @author wdhcr
 */
public class DecryptRequestBodyWrapper extends HttpServletRequestWrapper {
    private final byte[] body;
    public DecryptRequestBodyWrapper(HttpServletRequest request, String privateKey, String headerFlag) throws IOException {
        super(request);
        // èŽ·å– AES å¯†ç  é‡‡ç”¨ RSA åР坆
        String headerRsa = request.getHeader(headerFlag);
        String decryptAes = EncryptUtils.decryptByRsa(headerRsa, privateKey);
        // è§£å¯† AES å¯†ç 
        String aesPassword = EncryptUtils.decryptByBase64(decryptAes);
        request.setCharacterEncoding(Constants.UTF8);
        byte[] readBytes = IoUtil.readBytes(request.getInputStream(), false);
        String requestBody = new String(readBytes, StandardCharsets.UTF_8);
        // è§£å¯† body é‡‡ç”¨ AES åР坆
        String decryptBody = EncryptUtils.decryptByAes(requestBody, aesPassword);
        body = decryptBody.getBytes(StandardCharsets.UTF_8);
    }
    @Override
    public BufferedReader getReader() {
        return new BufferedReader(new InputStreamReader(getInputStream()));
    }
    @Override
    public int getContentLength() {
        return body.length;
    }
    @Override
    public long getContentLengthLong() {
        return body.length;
    }
    @Override
    public String getContentType() {
        return MediaType.APPLICATION_JSON_VALUE;
    }
    @Override
    public ServletInputStream getInputStream() {
        final ByteArrayInputStream bais = new ByteArrayInputStream(body);
        return new ServletInputStream() {
            @Override
            public int read() {
                return bais.read();
            }
            @Override
            public int available() {
                return body.length;
            }
            @Override
            public boolean isFinished() {
                return false;
            }
            @Override
            public boolean isReady() {
                return false;
            }
            @Override
            public void setReadListener(ReadListener readListener) {
            }
        };
    }
}
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/filter/EncryptResponseBodyWrapper.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,123 @@
package org.dromara.common.encrypt.filter;
import cn.hutool.core.util.RandomUtil;
import jakarta.servlet.ServletOutputStream;
import jakarta.servlet.WriteListener;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpServletResponseWrapper;
import org.dromara.common.encrypt.utils.EncryptUtils;
import java.io.*;
import java.nio.charset.StandardCharsets;
/**
 * åŠ å¯†å“åº”å‚æ•°åŒ…è£…ç±»
 *
 * @author Michelle.Chung
 */
public class EncryptResponseBodyWrapper extends HttpServletResponseWrapper {
    private final ByteArrayOutputStream byteArrayOutputStream;
    private final ServletOutputStream servletOutputStream;
    private final PrintWriter printWriter;
    public EncryptResponseBodyWrapper(HttpServletResponse response) throws IOException {
        super(response);
        this.byteArrayOutputStream = new ByteArrayOutputStream();
        this.servletOutputStream = this.getOutputStream();
        this.printWriter = new PrintWriter(new OutputStreamWriter(byteArrayOutputStream));
    }
    @Override
    public PrintWriter getWriter() {
        return printWriter;
    }
    @Override
    public void flushBuffer() throws IOException {
        if (servletOutputStream != null) {
            servletOutputStream.flush();
        }
        if (printWriter != null) {
            printWriter.flush();
        }
    }
    @Override
    public void reset() {
        byteArrayOutputStream.reset();
    }
    public byte[] getResponseData() throws IOException {
        flushBuffer();
        return byteArrayOutputStream.toByteArray();
    }
    public String getContent() throws IOException {
        flushBuffer();
        return byteArrayOutputStream.toString();
    }
    /**
     * èŽ·å–åŠ å¯†å†…å®¹
     *
     * @param servletResponse response
     * @param publicKey       RSA公钥 (用于加密 AES ç§˜é’¥)
     * @param headerFlag      è¯·æ±‚头标志
     * @return åР坆内容
     * @throws IOException
     */
    public String getEncryptContent(HttpServletResponse servletResponse, String publicKey, String headerFlag) throws IOException {
        // ç”Ÿæˆç§˜é’¥
        String aesPassword = RandomUtil.randomString(32);
        // ç§˜é’¥ä½¿ç”¨ Base64 ç¼–码
        String encryptAes = EncryptUtils.encryptByBase64(aesPassword);
        // Rsa å…¬é’¥åР坆 Base64 ç¼–码
        String encryptPassword = EncryptUtils.encryptByRsa(encryptAes, publicKey);
        // è®¾ç½®å“åº”头
        // vue版本需要设置
        servletResponse.addHeader("Access-Control-Expose-Headers", headerFlag);
        servletResponse.setHeader("Access-Control-Allow-Origin", "*");
        servletResponse.setHeader("Access-Control-Allow-Methods", "*");
        servletResponse.setHeader(headerFlag, encryptPassword);
        servletResponse.setCharacterEncoding(StandardCharsets.UTF_8.toString());
        // èŽ·å–åŽŸå§‹å†…å®¹
        String originalBody = this.getContent();
        // å¯¹å†…容进行加密
        return EncryptUtils.encryptByAes(originalBody, aesPassword);
    }
    @Override
    public ServletOutputStream getOutputStream() throws IOException {
        return new ServletOutputStream() {
            @Override
            public boolean isReady() {
                return false;
            }
            @Override
            public void setWriteListener(WriteListener writeListener) {
            }
            @Override
            public void write(int b) throws IOException {
                byteArrayOutputStream.write(b);
            }
            @Override
            public void write(byte[] b) throws IOException {
                byteArrayOutputStream.write(b);
            }
            @Override
            public void write(byte[] b, int off, int len) throws IOException {
                byteArrayOutputStream.write(b, off, len);
            }
        };
    }
}
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/interceptor/MybatisDecryptInterceptor.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,132 @@
package org.dromara.common.encrypt.interceptor;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.ObjectUtil;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.executor.parameter.ParameterHandler;
import org.apache.ibatis.executor.resultset.ResultSetHandler;
import org.apache.ibatis.plugin.*;
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 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 {
        // å¼€å§‹è¿›è¡Œå‚数解密
        ResultSetHandler resultSetHandler = (ResultSetHandler) invocation.getTarget();
        Field parameterHandlerField = resultSetHandler.getClass().getDeclaredField("parameterHandler");
        parameterHandlerField.setAccessible(true);
        Object target = parameterHandlerField.get(resultSetHandler);
        if (target instanceof ParameterHandler parameterHandler) {
            Object parameterObject = parameterHandler.getParameterObject();
            if (ObjectUtil.isNotNull(parameterObject) && !(parameterObject instanceof String)) {
                this.decryptHandler(parameterObject);
            }
        }
        // èŽ·å–æ‰§è¡Œmysql执行结果
        Object result = invocation.proceed();
        if (result == null) {
            return null;
        }
        this.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(CollUtil.isEmpty(list)) {
                return;
            }
            // åˆ¤æ–­ç¬¬ä¸€ä¸ªå…ƒç´ æ˜¯å¦å«æœ‰æ³¨è§£ã€‚如果没有直接返回,提高效率
            Object firstItem = list.get(0);
            if (ObjectUtil.isNull(firstItem) || CollUtil.isEmpty(encryptorManager.getFieldCache(firstItem.getClass()))) {
                return;
            }
            list.forEach(this::decryptHandler);
            return;
        }
        // ä¸åœ¨ç¼“存中的类,就是没有加密注解的类(当然也有可能是typeAliasesPackage写错)
        Set<Field> fields = encryptorManager.getFieldCache(sourceObject.getClass());
        if(ObjectUtil.isNull(fields)){
            return;
        }
        try {
            for (Field field : fields) {
                field.set(sourceObject, this.decryptField(Convert.toStr(field.get(sourceObject)), field));
            }
        } catch (Exception e) {
            log.error("处理解密字段时出错", e);
        }
    }
    /**
     * å­—段值进行加密。通过字段的批注注册新的加密算法
     *
     * @param value å¾…加密的值
     * @param field å¾…加密字段
     * @return åŠ å¯†åŽç»“æžœ
     */
    private String decryptField(String value, Field field) {
        if (ObjectUtil.isNull(value)) {
            return null;
        }
        EncryptField encryptField = field.getAnnotation(EncryptField.class);
        EncryptContext encryptContext = new EncryptContext();
        encryptContext.setAlgorithm(encryptField.algorithm() == AlgorithmType.DEFAULT ? defaultProperties.getAlgorithm() : encryptField.algorithm());
        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-Vue-Plus/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/interceptor/MybatisEncryptInterceptor.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,124 @@
package org.dromara.common.encrypt.interceptor;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.ObjectUtil;
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 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 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(CollUtil.isEmpty(list)) {
                return;
            }
            // åˆ¤æ–­ç¬¬ä¸€ä¸ªå…ƒç´ æ˜¯å¦å«æœ‰æ³¨è§£ã€‚如果没有直接返回,提高效率
            Object firstItem = list.get(0);
            if (ObjectUtil.isNull(firstItem) || CollUtil.isEmpty(encryptorManager.getFieldCache(firstItem.getClass()))) {
                return;
            }
            list.forEach(this::encryptHandler);
            return;
        }
        // ä¸åœ¨ç¼“存中的类,就是没有加密注解的类(当然也有可能是typeAliasesPackage写错)
        Set<Field> fields = encryptorManager.getFieldCache(sourceObject.getClass());
        if(ObjectUtil.isNull(fields)){
            return;
        }
        try {
            for (Field field : fields) {
                field.set(sourceObject, this.encryptField(Convert.toStr(field.get(sourceObject)), field));
            }
        } catch (Exception e) {
            log.error("处理加密字段时出错", e);
        }
    }
    /**
     * å­—段值进行加密。通过字段的批注注册新的加密算法
     *
     * @param value å¾…加密的值
     * @param field å¾…加密字段
     * @return åŠ å¯†åŽç»“æžœ
     */
    private String encryptField(String value, Field field) {
        if (ObjectUtil.isNull(value)) {
            return null;
        }
        EncryptField encryptField = field.getAnnotation(EncryptField.class);
        EncryptContext encryptContext = new EncryptContext();
        encryptContext.setAlgorithm(encryptField.algorithm() == AlgorithmType.DEFAULT ? defaultProperties.getAlgorithm() : encryptField.algorithm());
        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-Vue-Plus/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/properties/ApiDecryptProperties.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,34 @@
package org.dromara.common.encrypt.properties;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
 * api解密属性配置类
 * @author wdhcr
 */
@Data
@ConfigurationProperties(prefix = "api-decrypt")
public class ApiDecryptProperties {
    /**
     * åР坆开关
     */
    private Boolean enabled;
    /**
     * å¤´éƒ¨æ ‡è¯†
     */
    private String headerFlag;
    /**
     * å“åº”加密公钥
     */
    private String publicKey;
    /**
     * è¯·æ±‚解密私钥
     */
    private String privateKey;
}
RuoYi-Vue-Plus/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-Vue-Plus/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/utils/EncryptUtils.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,313 @@
package org.dromara.common.encrypt.utils;
import cn.hutool.core.codec.Base64;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.crypto.SmUtil;
import cn.hutool.crypto.asymmetric.KeyType;
import cn.hutool.crypto.asymmetric.RSA;
import cn.hutool.crypto.asymmetric.SM2;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
/**
 * å®‰å…¨ç›¸å…³å·¥å…·ç±»
 *
 * @author è€é©¬
 */
public class EncryptUtils {
    /**
     * å…¬é’¥
     */
    public static final String PUBLIC_KEY = "publicKey";
    /**
     * ç§é’¥
     */
    public static final String PRIVATE_KEY = "privateKey";
    /**
     * Base64加密
     *
     * @param data å¾…加密数据
     * @return åŠ å¯†åŽå­—ç¬¦ä¸²
     */
    public static String encryptByBase64(String data) {
        return Base64.encode(data, StandardCharsets.UTF_8);
    }
    /**
     * Base64解密
     *
     * @param data å¾…解密数据
     * @return è§£å¯†åŽå­—符串
     */
    public static String decryptByBase64(String data) {
        return Base64.decodeStr(data, StandardCharsets.UTF_8);
    }
    /**
     * AES加密
     *
     * @param data     å¾…加密数据
     * @param password ç§˜é’¥å­—符串
     * @return åŠ å¯†åŽå­—ç¬¦ä¸², é‡‡ç”¨Base64编码
     */
    public static String encryptByAes(String data, String password) {
        if (StrUtil.isBlank(password)) {
            throw new IllegalArgumentException("AES需要传入秘钥信息");
        }
        // aes算法的秘钥要求是16位、24位、32位
        int[] array = {16, 24, 32};
        if (!ArrayUtil.contains(array, password.length())) {
            throw new IllegalArgumentException("AES秘钥长度要求为16位、24位、32位");
        }
        return SecureUtil.aes(password.getBytes(StandardCharsets.UTF_8)).encryptBase64(data, StandardCharsets.UTF_8);
    }
    /**
     * AES加密
     *
     * @param data     å¾…加密数据
     * @param password ç§˜é’¥å­—符串
     * @return åŠ å¯†åŽå­—ç¬¦ä¸², é‡‡ç”¨Hex编码
     */
    public static String encryptByAesHex(String data, String password) {
        if (StrUtil.isBlank(password)) {
            throw new IllegalArgumentException("AES需要传入秘钥信息");
        }
        // aes算法的秘钥要求是16位、24位、32位
        int[] array = {16, 24, 32};
        if (!ArrayUtil.contains(array, password.length())) {
            throw new IllegalArgumentException("AES秘钥长度要求为16位、24位、32位");
        }
        return SecureUtil.aes(password.getBytes(StandardCharsets.UTF_8)).encryptHex(data, StandardCharsets.UTF_8);
    }
    /**
     * AES解密
     *
     * @param data     å¾…解密数据
     * @param password ç§˜é’¥å­—符串
     * @return è§£å¯†åŽå­—符串
     */
    public static String decryptByAes(String data, String password) {
        if (StrUtil.isBlank(password)) {
            throw new IllegalArgumentException("AES需要传入秘钥信息");
        }
        // aes算法的秘钥要求是16位、24位、32位
        int[] array = {16, 24, 32};
        if (!ArrayUtil.contains(array, password.length())) {
            throw new IllegalArgumentException("AES秘钥长度要求为16位、24位、32位");
        }
        return SecureUtil.aes(password.getBytes(StandardCharsets.UTF_8)).decryptStr(data, StandardCharsets.UTF_8);
    }
    /**
     * SM4加密(Base64编码)
     *
     * @param data     å¾…加密数据
     * @param password ç§˜é’¥å­—符串
     * @return åŠ å¯†åŽå­—ç¬¦ä¸², é‡‡ç”¨Base64编码
     */
    public static String encryptBySm4(String data, String password) {
        if (StrUtil.isBlank(password)) {
            throw new IllegalArgumentException("SM4需要传入秘钥信息");
        }
        // sm4算法的秘钥要求是16位长度
        int sm4PasswordLength = 16;
        if (sm4PasswordLength != password.length()) {
            throw new IllegalArgumentException("SM4秘钥长度要求为16位");
        }
        return SmUtil.sm4(password.getBytes(StandardCharsets.UTF_8)).encryptBase64(data, StandardCharsets.UTF_8);
    }
    /**
     * SM4加密(Hex编码)
     *
     * @param data     å¾…加密数据
     * @param password ç§˜é’¥å­—符串
     * @return åŠ å¯†åŽå­—ç¬¦ä¸², é‡‡ç”¨Hex编码
     */
    public static String encryptBySm4Hex(String data, String password) {
        if (StrUtil.isBlank(password)) {
            throw new IllegalArgumentException("SM4需要传入秘钥信息");
        }
        // sm4算法的秘钥要求是16位长度
        int sm4PasswordLength = 16;
        if (sm4PasswordLength != password.length()) {
            throw new IllegalArgumentException("SM4秘钥长度要求为16位");
        }
        return SmUtil.sm4(password.getBytes(StandardCharsets.UTF_8)).encryptHex(data, StandardCharsets.UTF_8);
    }
    /**
     * sm4解密
     *
     * @param data     å¾…解密数据(可以是Base64或Hex编码)
     * @param password ç§˜é’¥å­—符串
     * @return è§£å¯†åŽå­—符串
     */
    public static String decryptBySm4(String data, String password) {
        if (StrUtil.isBlank(password)) {
            throw new IllegalArgumentException("SM4需要传入秘钥信息");
        }
        // sm4算法的秘钥要求是16位长度
        int sm4PasswordLength = 16;
        if (sm4PasswordLength != password.length()) {
            throw new IllegalArgumentException("SM4秘钥长度要求为16位");
        }
        return SmUtil.sm4(password.getBytes(StandardCharsets.UTF_8)).decryptStr(data, StandardCharsets.UTF_8);
    }
    /**
     * äº§ç”Ÿsm2加解密需要的公钥和私钥
     *
     * @return å…¬ç§é’¥Map
     */
    public static Map<String, String> generateSm2Key() {
        Map<String, String> keyMap = new HashMap<>(2);
        SM2 sm2 = SmUtil.sm2();
        keyMap.put(PRIVATE_KEY, sm2.getPrivateKeyBase64());
        keyMap.put(PUBLIC_KEY, sm2.getPublicKeyBase64());
        return keyMap;
    }
    /**
     * sm2公钥加密
     *
     * @param data      å¾…加密数据
     * @param publicKey å…¬é’¥
     * @return åŠ å¯†åŽå­—ç¬¦ä¸², é‡‡ç”¨Base64编码
     */
    public static String encryptBySm2(String data, String publicKey) {
        if (StrUtil.isBlank(publicKey)) {
            throw new IllegalArgumentException("SM2需要传入公钥进行加密");
        }
        SM2 sm2 = SmUtil.sm2(null, publicKey);
        return sm2.encryptBase64(data, StandardCharsets.UTF_8, KeyType.PublicKey);
    }
    /**
     * sm2公钥加密
     *
     * @param data      å¾…加密数据
     * @param publicKey å…¬é’¥
     * @return åŠ å¯†åŽå­—ç¬¦ä¸², é‡‡ç”¨Hex编码
     */
    public static String encryptBySm2Hex(String data, String publicKey) {
        if (StrUtil.isBlank(publicKey)) {
            throw new IllegalArgumentException("SM2需要传入公钥进行加密");
        }
        SM2 sm2 = SmUtil.sm2(null, publicKey);
        return sm2.encryptHex(data, StandardCharsets.UTF_8, KeyType.PublicKey);
    }
    /**
     * sm2私钥解密
     *
     * @param data       å¾…解密数据
     * @param privateKey ç§é’¥
     * @return è§£å¯†åŽå­—符串
     */
    public static String decryptBySm2(String data, String privateKey) {
        if (StrUtil.isBlank(privateKey)) {
            throw new IllegalArgumentException("SM2需要传入私钥进行解密");
        }
        SM2 sm2 = SmUtil.sm2(privateKey, null);
        return sm2.decryptStr(data, KeyType.PrivateKey, StandardCharsets.UTF_8);
    }
    /**
     * äº§ç”ŸRSA加解密需要的公钥和私钥
     *
     * @return å…¬ç§é’¥Map
     */
    public static Map<String, String> generateRsaKey() {
        Map<String, String> keyMap = new HashMap<>(2);
        RSA rsa = SecureUtil.rsa();
        keyMap.put(PRIVATE_KEY, rsa.getPrivateKeyBase64());
        keyMap.put(PUBLIC_KEY, rsa.getPublicKeyBase64());
        return keyMap;
    }
    /**
     * rsa公钥加密
     *
     * @param data      å¾…加密数据
     * @param publicKey å…¬é’¥
     * @return åŠ å¯†åŽå­—ç¬¦ä¸², é‡‡ç”¨Base64编码
     */
    public static String encryptByRsa(String data, String publicKey) {
        if (StrUtil.isBlank(publicKey)) {
            throw new IllegalArgumentException("RSA需要传入公钥进行加密");
        }
        RSA rsa = SecureUtil.rsa(null, publicKey);
        return rsa.encryptBase64(data, StandardCharsets.UTF_8, KeyType.PublicKey);
    }
    /**
     * rsa公钥加密
     *
     * @param data      å¾…加密数据
     * @param publicKey å…¬é’¥
     * @return åŠ å¯†åŽå­—ç¬¦ä¸², é‡‡ç”¨Hex编码
     */
    public static String encryptByRsaHex(String data, String publicKey) {
        if (StrUtil.isBlank(publicKey)) {
            throw new IllegalArgumentException("RSA需要传入公钥进行加密");
        }
        RSA rsa = SecureUtil.rsa(null, publicKey);
        return rsa.encryptHex(data, StandardCharsets.UTF_8, KeyType.PublicKey);
    }
    /**
     * rsa私钥解密
     *
     * @param data       å¾…解密数据
     * @param privateKey ç§é’¥
     * @return è§£å¯†åŽå­—符串
     */
    public static String decryptByRsa(String data, String privateKey) {
        if (StrUtil.isBlank(privateKey)) {
            throw new IllegalArgumentException("RSA需要传入私钥进行解密");
        }
        RSA rsa = SecureUtil.rsa(privateKey, null);
        return rsa.decryptStr(data, KeyType.PrivateKey, StandardCharsets.UTF_8);
    }
    /**
     * md5加密
     *
     * @param data å¾…加密数据
     * @return åŠ å¯†åŽå­—ç¬¦ä¸², é‡‡ç”¨Hex编码
     */
    public static String encryptByMd5(String data) {
        return SecureUtil.md5(data);
    }
    /**
     * sha256加密
     *
     * @param data å¾…加密数据
     * @return åŠ å¯†åŽå­—ç¬¦ä¸², é‡‡ç”¨Hex编码
     */
    public static String encryptBySha256(String data) {
        return SecureUtil.sha256(data);
    }
    /**
     * sm3加密
     *
     * @param data å¾…加密数据
     * @return åŠ å¯†åŽå­—ç¬¦ä¸², é‡‡ç”¨Hex编码
     */
    public static String encryptBySm3(String data) {
        return SmUtil.sm3(data);
    }
}
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-encrypt/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,3 @@
org.dromara.common.encrypt.config.EncryptorAutoConfiguration
org.dromara.common.encrypt.config.ApiDecryptAutoConfiguration
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-excel/.flattened-pom.xml
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,24 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <modelVersion>4.0.0</modelVersion>
  <parent>
    <groupId>org.dromara</groupId>
    <artifactId>ruoyi-common</artifactId>
    <version>5.5.3</version>
  </parent>
  <groupId>org.dromara</groupId>
  <artifactId>ruoyi-common-excel</artifactId>
  <version>5.5.3</version>
  <description>ruoyi-common-excel</description>
  <dependencies>
    <dependency>
      <groupId>org.dromara</groupId>
      <artifactId>ruoyi-common-json</artifactId>
    </dependency>
    <dependency>
      <groupId>cn.idev.excel</groupId>
      <artifactId>fastexcel</artifactId>
    </dependency>
  </dependencies>
</project>
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-excel/pom.xml
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,30 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <groupId>org.dromara</groupId>
        <artifactId>ruoyi-common</artifactId>
        <version>${revision}</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>
    <artifactId>ruoyi-common-excel</artifactId>
    <description>
        ruoyi-common-excel
    </description>
    <dependencies>
        <dependency>
            <groupId>org.dromara</groupId>
            <artifactId>ruoyi-common-json</artifactId>
        </dependency>
        <dependency>
            <groupId>cn.idev.excel</groupId>
            <artifactId>fastexcel</artifactId>
        </dependency>
    </dependencies>
</project>
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/annotation/CellMerge.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,29 @@
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;
    /**
     * åˆå¹¶éœ€è¦ä¾èµ–的其他字段名称
     */
    String[] mergeBy() default {};
}
RuoYi-Vue-Plus/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-Vue-Plus/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/annotation/ExcelDynamicOptions.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,23 @@
package org.dromara.common.excel.annotation;
import org.dromara.common.excel.core.ExcelOptionsProvider;
import java.lang.annotation.*;
/**
 * Excel动态下拉选项注解
 *
 * @author Angus
 */
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface ExcelDynamicOptions {
    /**
     * æä¾›è€…类全限定名
     * <p>
     * {@link org.dromara.common.excel.core.ExcelOptionsProvider} æŽ¥å£å®žçŽ°ç±» class
     */
    Class<? extends ExcelOptionsProvider> providerClass();
}
RuoYi-Vue-Plus/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-Vue-Plus/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/annotation/ExcelNotation.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,20 @@
package org.dromara.common.excel.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
 * æ‰¹æ³¨ æ­¤æ³¨è§£ä»…用于单表头 ä¸æ”¯æŒå¤šå±‚级表头
 * @author guzhouyanyu
 */
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface ExcelNotation {
    /**
     * æ‰¹æ³¨å†…容
     */
    String value() default "";
}
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/annotation/ExcelRequired.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,22 @@
package org.dromara.common.excel.annotation;
import org.apache.poi.ss.usermodel.IndexedColors;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
 * æ˜¯å¦å¿…å¡« æ­¤æ³¨è§£ä»…用于单表头 ä¸æ”¯æŒå¤šå±‚级表头
 * @author guzhouyanyu
 */
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface ExcelRequired {
    /**
     * å­—体颜色
     */
    IndexedColors fontColor() default IndexedColors.RED;
}
RuoYi-Vue-Plus/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 cn.idev.excel.converters.Converter;
import cn.idev.excel.enums.CellDataTypeEnum;
import cn.idev.excel.metadata.GlobalConfiguration;
import cn.idev.excel.metadata.data.ReadCellData;
import cn.idev.excel.metadata.data.WriteCellData;
import cn.idev.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 null;
    }
    @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-Vue-Plus/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 cn.idev.excel.converters.Converter;
import cn.idev.excel.enums.CellDataTypeEnum;
import cn.idev.excel.metadata.GlobalConfiguration;
import cn.idev.excel.metadata.data.ReadCellData;
import cn.idev.excel.metadata.data.WriteCellData;
import cn.idev.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-Vue-Plus/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/convert/ExcelEnumConvert.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,87 @@
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 cn.idev.excel.converters.Converter;
import cn.idev.excel.enums.CellDataTypeEnum;
import cn.idev.excel.metadata.GlobalConfiguration;
import cn.idev.excel.metadata.data.ReadCellData;
import cn.idev.excel.metadata.data.WriteCellData;
import cn.idev.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) {
        cellData.checkEmpty();
        // Excel中填入的是枚举中指定的描述
        Object textValue = switch (cellData.getType()) {
            case STRING, DIRECT_STRING, RICH_TEXT_STRING -> cellData.getStringValue();
            case NUMBER -> cellData.getNumberValue();
            case BOOLEAN -> cellData.getBooleanValue();
            default -> throw new IllegalArgumentException("单元格类型异常!");
        };
        // å¦‚果是空值
        if (ObjectUtil.isNull(textValue)) {
            return null;
        }
        Map<Object, String> enumCodeToTextMap = beforeConvert(contentProperty);
        // ä»ŽJava输出至Excel是code转text
        // å› æ­¤ä»ŽExcel转Java应该将text与code对调
        Map<Object, Object> enumTextToCodeMap = new HashMap<>();
        enumCodeToTextMap.forEach((key, value) -> enumTextToCodeMap.put(value, key));
        // åº”该从text -> code中查找
        Object codeValue = enumTextToCodeMap.get(textValue);
        return Convert.convert(contentProperty.getField().getType(), codeValue);
    }
    @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-Vue-Plus/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/core/CellMergeHandler.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,217 @@
package org.dromara.common.excel.core;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import cn.idev.excel.annotation.ExcelIgnore;
import cn.idev.excel.annotation.ExcelIgnoreUnannotated;
import cn.idev.excel.annotation.ExcelProperty;
import lombok.SneakyThrows;
import org.apache.poi.ss.util.CellRangeAddress;
import org.dromara.common.core.utils.reflect.ReflectUtils;
import org.dromara.common.excel.annotation.CellMerge;
import java.lang.reflect.Field;
import java.util.*;
/**
 * å•元格合并处理器
 *
 * @author Lion Li
 */
public class CellMergeHandler {
    private final boolean hasTitle;
    private int rowIndex;
    private CellMergeHandler(final boolean hasTitle) {
        this.hasTitle = hasTitle;
        // è¡Œåˆå¹¶å¼€å§‹ä¸‹æ ‡
        this.rowIndex = hasTitle ? 1 : 0;
    }
    private CellMergeHandler(final boolean hasTitle, final int rowIndex) {
        this.hasTitle = hasTitle;
        this.rowIndex = hasTitle ? rowIndex : 0;
    }
    @SneakyThrows
    public List<CellRangeAddress> handle(List<?> rows) {
        // å¦‚果入参为空集合则返回空集
        if (CollUtil.isEmpty(rows)) {
            return Collections.emptyList();
        }
        // èŽ·å–æœ‰åˆå¹¶æ³¨è§£çš„å­—æ®µ
        Map<Field, FieldColumnIndex> mergeFields = getFieldColumnIndexMap(rows.get(0).getClass());
        // å¦‚果没有需要合并的字段则返回空集
        if (CollUtil.isEmpty(mergeFields)) {
            return Collections.emptyList();
        }
        // ç»“果集
        List<CellRangeAddress> result = new ArrayList<>();
        // ç”Ÿæˆä¸¤ä¸¤åˆå¹¶å•元格
        Map<Field, RepeatCell> rowRepeatCellMap = new HashMap<>();
        for (Map.Entry<Field, FieldColumnIndex> item : mergeFields.entrySet()) {
            Field field = item.getKey();
            FieldColumnIndex itemValue = item.getValue();
            int colNum = itemValue.colIndex();
            CellMerge cellMerge = itemValue.cellMerge();
            for (int i = 0; i < rows.size(); i++) {
                // å½“前行数据
                Object currentRowObj = rows.get(i);
                // å½“前行数据字段值
                Object currentRowObjFieldVal = ReflectUtils.invokeGetter(currentRowObj, field.getName());
                // ç©ºå€¼è·³è¿‡ä¸å¤„理
                if (currentRowObjFieldVal == null || "".equals(currentRowObjFieldVal)) {
                    continue;
                }
                // å•元格合并Map是否存在数据,如果不存在则添加当前行的字段值
                if (!rowRepeatCellMap.containsKey(field)) {
                    rowRepeatCellMap.put(field, RepeatCell.of(currentRowObjFieldVal, i));
                    continue;
                }
                // èŽ·å– å•元格合并Map ä¸­å­—段值
                RepeatCell repeatCell = rowRepeatCellMap.get(field);
                Object cellValue = repeatCell.value();
                int current = repeatCell.current();
                // æ£€æŸ¥æ˜¯å¦æ»¡è¶³åˆå¹¶æ¡ä»¶
                // currentRowObj å½“前行数据
                // rows.get(i - 1) ä¸Šä¸€è¡Œæ•°æ® æ³¨ï¼šç”±äºŽ if (!rowRepeatCellMap.containsKey(field)) æ¡ä»¶çš„存在,所以该 i å¿…不可能小于1
                // cellMerge å½“前行字段合并注解
                boolean merge = isMerge(currentRowObj, rows.get(i - 1), cellMerge);
                // æ˜¯å¦æ·»åŠ åˆ°ç»“æžœé›†
                boolean isAddResult = false;
                // æœ€æ–°è¡Œ
                int lastRow = i + rowIndex - 1;
                // å¦‚果当前行字段值和缓存中的字段值不相等,或不满足合并条件,则替换
                if (!currentRowObjFieldVal.equals(cellValue) || !merge) {
                    rowRepeatCellMap.put(field, RepeatCell.of(currentRowObjFieldVal, i));
                    isAddResult = true;
                }
                // å¦‚果最后一行不能合并,检查之前的数据是否需要合并;如果最后一行可以合并,则直接合并到最后
                if (i == rows.size() - 1) {
                    isAddResult = true;
                    if (i > current) {
                        lastRow = i + rowIndex;
                    }
                }
                if (isAddResult && i > current) {
                    //如果是同一行,则跳过合并
                    if (current + rowIndex == lastRow) {
                        continue;
                    }
                    result.add(new CellRangeAddress(current + rowIndex, lastRow, colNum, colNum));
                }
            }
        }
        return result;
    }
    /**
     * èŽ·å–å¸¦æœ‰åˆå¹¶æ³¨è§£çš„å­—æ®µåˆ—ç´¢å¼•å’Œåˆå¹¶æ³¨è§£ä¿¡æ¯Map集
     */
    private Map<Field, FieldColumnIndex> getFieldColumnIndexMap(Class<?> clazz) {
        boolean annotationPresent = clazz.isAnnotationPresent(ExcelIgnoreUnannotated.class);
        Field[] fields = ReflectUtils.getFields(clazz, field -> {
            if ("serialVersionUID".equals(field.getName())) {
                return false;
            }
            if (field.isAnnotationPresent(ExcelIgnore.class)) {
                return false;
            }
            return !annotationPresent || field.isAnnotationPresent(ExcelProperty.class);
        });
        // æœ‰æ³¨è§£çš„字段
        Map<Field, FieldColumnIndex> mergeFields = new HashMap<>();
        for (int i = 0; i < fields.length; i++) {
            Field field = fields[i];
            if (!field.isAnnotationPresent(CellMerge.class)) {
                continue;
            }
            CellMerge cm = field.getAnnotation(CellMerge.class);
            int index = cm.index() == -1 ? i : cm.index();
            mergeFields.put(field, FieldColumnIndex.of(index, cm));
            if (hasTitle) {
                ExcelProperty property = field.getAnnotation(ExcelProperty.class);
                rowIndex = Math.max(rowIndex, property.value().length);
            }
        }
        return mergeFields;
    }
    private boolean isMerge(Object currentRow, Object preRow, CellMerge cellMerge) {
        final String[] mergeBy = cellMerge.mergeBy();
        if (StrUtil.isAllNotBlank(mergeBy)) {
            // æ¯”对当前行和上一行的各个属性值一一比对 å¦‚果全为真 åˆ™ä¸ºçœŸ
            for (String fieldName : mergeBy) {
                final Object valCurrent = ReflectUtil.getFieldValue(currentRow, fieldName);
                final Object valPre = ReflectUtil.getFieldValue(preRow, fieldName);
                if (!Objects.equals(valPre, valCurrent)) {
                    // ä¾èµ–字段如有任一不等值,则标记为不可合并
                    return false;
                }
            }
        }
        return true;
    }
    /**
     * å•元格合并
     */
    record RepeatCell(Object value, int current) {
        static RepeatCell of(Object value, int current) {
            return new RepeatCell(value, current);
        }
    }
    /**
     * å­—段列索引和合并注解信息
     */
    record FieldColumnIndex(int colIndex, CellMerge cellMerge) {
        static FieldColumnIndex of(int colIndex, CellMerge cellMerge) {
            return new FieldColumnIndex(colIndex, cellMerge);
        }
    }
    /**
     * åˆ›å»ºä¸€ä¸ªå•元格合并处理器实例
     *
     * @param hasTitle æ˜¯å¦åˆå¹¶æ ‡é¢˜
     * @param rowIndex è¡Œç´¢å¼•
     * @return å•元格合并处理器
     */
    public static CellMergeHandler of(final boolean hasTitle, final int rowIndex) {
        return new CellMergeHandler(hasTitle, rowIndex);
    }
    /**
     * åˆ›å»ºä¸€ä¸ªå•元格合并处理器实例
     *
     * @param hasTitle æ˜¯å¦åˆå¹¶æ ‡é¢˜
     * @return å•元格合并处理器
     */
    public static CellMergeHandler of(final boolean hasTitle) {
        return new CellMergeHandler(hasTitle);
    }
    /**
     * åˆ›å»ºä¸€ä¸ªå•元格合并处理器实例(默认不合并标题)
     *
     * @return å•元格合并处理器
     */
    public static CellMergeHandler of() {
        return new CellMergeHandler(false);
    }
}
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/core/CellMergeStrategy.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,65 @@
package org.dromara.common.excel.core;
import cn.hutool.core.collection.CollUtil;
import cn.idev.excel.metadata.Head;
import cn.idev.excel.write.handler.SheetWriteHandler;
import cn.idev.excel.write.merge.AbstractMergeStrategy;
import cn.idev.excel.write.metadata.holder.WriteSheetHolder;
import cn.idev.excel.write.metadata.holder.WriteWorkbookHolder;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.util.CellRangeAddress;
import java.util.List;
/**
 * åˆ—值重复合并策略
 *
 * @author Lion Li
 */
@Slf4j
public class CellMergeStrategy extends AbstractMergeStrategy implements SheetWriteHandler {
    private final List<CellRangeAddress> cellList;
    public CellMergeStrategy(List<CellRangeAddress> cellList) {
        this.cellList = cellList;
    }
    public CellMergeStrategy(List<?> list, boolean hasTitle) {
        this.cellList = CellMergeHandler.of(hasTitle).handle(list);
    }
    public CellMergeStrategy(List<?> list, boolean hasTitle, int rowIndex) {
        this.cellList = CellMergeHandler.of(hasTitle, rowIndex).handle(list);
    }
    @Override
    protected void merge(Sheet sheet, Cell cell, Head head, Integer relativeRowIndex) {
        if (CollUtil.isEmpty(cellList)) {
            return;
        }
        // å•元格写入了,遍历合并区域,如果该Cell在区域内,但非首行,则清空
        final int rowIndex = cell.getRowIndex();
        for (CellRangeAddress cellAddresses : cellList) {
            final int firstRow = cellAddresses.getFirstRow();
            if (cellAddresses.isInRange(cell) && rowIndex != firstRow) {
                cell.setBlank();
            }
        }
    }
    @Override
    public void afterSheetCreate(final WriteWorkbookHolder writeWorkbookHolder, final WriteSheetHolder writeSheetHolder) {
        if (CollUtil.isEmpty(cellList)) {
            return;
        }
        // åœ¨ Sheet åˆ›å»ºæ—¶æå‰å†™å…¥åˆå¹¶åŒºåŸŸï¼›åŽç»­å†™å…¥åªä¼šå½±å“é¦–格,不会移除合并
        final Sheet sheet = writeSheetHolder.getSheet();
        for (CellRangeAddress item : cellList) {
            sheet.addMergedRegion(item);
        }
    }
}
RuoYi-Vue-Plus/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 cn.idev.excel.context.AnalysisContext;
import cn.idev.excel.event.AnalysisEventListener;
import cn.idev.excel.exception.ExcelAnalysisException;
import cn.idev.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 DefaultExcelResult<>();
        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-Vue-Plus/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/core/DefaultExcelResult.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 DefaultExcelResult<T> implements ExcelResult<T> {
    /**
     * æ•°æ®å¯¹è±¡list
     */
    @Setter
    private List<T> list;
    /**
     * é”™è¯¯ä¿¡æ¯åˆ—表
     */
    @Setter
    private List<String> errorList;
    public DefaultExcelResult() {
        this.list = new ArrayList<>();
        this.errorList = new ArrayList<>();
    }
    public DefaultExcelResult(List<T> list, List<String> errorList) {
        this.list = list;
        this.errorList = errorList;
    }
    public DefaultExcelResult(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-Vue-Plus/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/core/DropDownOptions.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,150 @@
package org.dromara.common.excel.core;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.StrUtil;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.dromara.common.core.exception.ServiceException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
/**
 * <h1>Excel下拉可选项</h1>
 * æ³¨æ„ï¼šä¸ºç¡®ä¿ä¸‹æ‹‰æ¡†è§£æžæ­£ç¡®ï¼Œä¼ å€¼åŠ¡å¿…ä½¿ç”¨createOptionValue()做为值的拼接
 *
 * @author Emil.Zhang
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
@SuppressWarnings("unused")
public class DropDownOptions {
    /**
     * ä¸€çº§ä¸‹æ‹‰æ‰€åœ¨åˆ—index,从0开始算
     */
    private int index = 0;
    /**
     * äºŒçº§ä¸‹æ‹‰æ‰€åœ¨çš„index,从0开始算,不能与一级相同
     */
    private int nextIndex = 0;
    /**
     * ä¸€çº§ä¸‹æ‹‰æ‰€åŒ…含的数据
     */
    private List<String> options = new ArrayList<>();
    /**
     * äºŒçº§ä¸‹æ‹‰æ‰€åŒ…含的数据Map
     * <p>以每一个一级选项值为Key,每个一级选项对应的二级数据为Value</p>
     */
    private Map<String, List<String>> nextOptions = new HashMap<>();
    /**
     * åˆ†éš”符
     */
    private static final String DELIMITER = "_";
    /**
     * åˆ›å»ºåªæœ‰ä¸€çº§çš„下拉选
     */
    public DropDownOptions(int index, List<String> options) {
        this.index = index;
        this.options = options;
    }
    /**
     * <h2>创建每个选项可选值</h2>
     * <p>注意:不能以数字,特殊符号开头,选项中不可以包含任何运算符号</p>
     *
     * @param vars å¯é€‰å€¼å†…包含的参数
     * @return åˆè§„的可选值
     */
    public static String createOptionValue(Object... vars) {
        StringBuilder stringBuffer = new StringBuilder();
        String regex = "^[\\S\\d\\u4e00-\\u9fa5]+$";
        for (int i = 0; i < vars.length; i++) {
            String var = StrUtil.trimToEmpty(Convert.toStr(vars[i]));
            if (!var.matches(regex)) {
                throw new ServiceException("选项数据不符合规则,仅允许使用中英文字符以及数字");
            }
            stringBuffer.append(var);
            if (i < vars.length - 1) {
                // ç›´è‡³æœ€åŽä¸€ä¸ªå‰ï¼Œéƒ½ä»¥_作为切割线
                stringBuffer.append(DELIMITER);
            }
        }
        if (stringBuffer.toString().matches("^\\d_*$")) {
            throw new ServiceException("禁止以数字开头");
        }
        return stringBuffer.toString();
    }
    /**
     * å°†å¤„理后合理的可选值解析为原始的参数
     *
     * @param option ç»è¿‡å¤„理后的合理的可选项
     * @return åŽŸå§‹çš„å‚æ•°
     */
    public static List<String> analyzeOptionValue(String option) {
        return StrUtil.split(option, DELIMITER, true, true);
    }
    /**
     * åˆ›å»ºçº§è”下拉选项
     *
     * @param parentList                  çˆ¶å®žä½“可选项原始数据
     * @param parentIndex                 çˆ¶ä¸‹æ‹‰é€‰ä½ç½®
     * @param sonList                     å­å®žä½“可选项原始数据
     * @param sonIndex                    å­ä¸‹æ‹‰é€‰ä½ç½®
     * @param parentHowToGetIdFunction    çˆ¶ç±»å¦‚何获取唯一标识
     * @param sonHowToGetParentIdFunction å­ç±»å¦‚何获取父类的唯一标识
     * @param howToBuildEveryOption       å¦‚何生成下拉选内容
     * @return çº§è”下拉选项
     */
    public static <T> DropDownOptions buildLinkedOptions(List<T> parentList,
                                                         int parentIndex,
                                                         List<T> sonList,
                                                         int sonIndex,
                                                         Function<T, Number> parentHowToGetIdFunction,
                                                         Function<T, Number> sonHowToGetParentIdFunction,
                                                         Function<T, String> howToBuildEveryOption) {
        DropDownOptions parentLinkSonOptions = new DropDownOptions();
        // å…ˆåˆ›å»ºçˆ¶ç±»çš„下拉
        parentLinkSonOptions.setIndex(parentIndex);
        parentLinkSonOptions.setOptions(
            parentList.stream()
                .map(howToBuildEveryOption)
                .collect(Collectors.toList())
        );
        // æå–父-子级联下拉
        Map<String, List<String>> sonOptions = new HashMap<>();
        // çˆ¶çº§ä¾æ®è‡ªå·±çš„ID分组
        Map<Number, List<T>> parentGroupByIdMap =
            parentList.stream().collect(Collectors.groupingBy(parentHowToGetIdFunction));
        // éåŽ†æ¯ä¸ªå­é›†ï¼Œæå–åˆ°Map中
        sonList.forEach(everySon -> {
            if (parentGroupByIdMap.containsKey(sonHowToGetParentIdFunction.apply(everySon))) {
                // æ‰¾åˆ°å¯¹åº”的上级
                T parentObj = parentGroupByIdMap.get(sonHowToGetParentIdFunction.apply(everySon)).get(0);
                // æå–名称和ID作为Key
                String key = howToBuildEveryOption.apply(parentObj);
                // Key对应的Value
                List<String> thisParentSonOptionList;
                if (sonOptions.containsKey(key)) {
                    thisParentSonOptionList = sonOptions.get(key);
                } else {
                    thisParentSonOptionList = new ArrayList<>();
                    sonOptions.put(key, thisParentSonOptionList);
                }
                // å¾€Value中添加当前子集选项
                thisParentSonOptionList.add(howToBuildEveryOption.apply(everySon));
            }
        });
        parentLinkSonOptions.setNextIndex(sonIndex);
        parentLinkSonOptions.setNextOptions(sonOptions);
        return parentLinkSonOptions;
    }
}
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/core/ExcelDownHandler.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,412 @@
package org.dromara.common.excel.core;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.EnumUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.idev.excel.metadata.FieldCache;
import cn.idev.excel.metadata.FieldWrapper;
import cn.idev.excel.util.ClassUtils;
import cn.idev.excel.write.handler.SheetWriteHandler;
import cn.idev.excel.write.metadata.holder.WriteSheetHolder;
import cn.idev.excel.write.metadata.holder.WriteWorkbookHolder;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.CellRangeAddressList;
import org.apache.poi.ss.util.WorkbookUtil;
import org.apache.poi.xssf.usermodel.XSSFDataValidation;
import org.dromara.common.core.exception.ServiceException;
import org.dromara.common.core.service.DictService;
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.excel.annotation.ExcelDictFormat;
import org.dromara.common.excel.annotation.ExcelDynamicOptions;
import org.dromara.common.excel.annotation.ExcelEnumFormat;
import java.lang.reflect.Field;
import java.util.*;
/**
 * <h1>Excel表格下拉选操作</h1>
 * è€ƒè™‘到下拉选过多可能导致Excel打开缓慢的问题,只校验前1000行
 * <p>
 * å³åªæœ‰å‰1000行的数据可以用下拉框,超出的自行通过限制数据量的形式,第二次输出
 *
 * @author Emil.Zhang
 */
@Slf4j
public class ExcelDownHandler implements SheetWriteHandler {
    /**
     * Excel表格中的列名英文
     * ä»…为了解析列英文,禁止修改
     */
    private static final String EXCEL_COLUMN_NAME = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
    /**
     * å•选数据Sheet名
     */
    private static final String OPTIONS_SHEET_NAME = "options";
    /**
     * è”动选择数据Sheet名的头
     */
    private static final String LINKED_OPTIONS_SHEET_NAME = "linkedOptions";
    /**
     * ä¸‹æ‹‰å¯é€‰é¡¹
     */
    private final List<DropDownOptions> dropDownOptions;
    private final DictService dictService;
    /**
     * å½“前单选进度
     */
    private int currentOptionsColumnIndex;
    /**
     * å½“前联动选择进度
     */
    private int currentLinkedOptionsSheetIndex;
    public ExcelDownHandler(List<DropDownOptions> options) {
        this.dropDownOptions = options;
        this.currentOptionsColumnIndex = 0;
        this.currentLinkedOptionsSheetIndex = 0;
        this.dictService = SpringUtils.getBean(DictService.class);
    }
    /**
     * <h2>开始创建下拉数据</h2>
     * 1.通过解析传入的@ExcelProperty同级是否标注有@DropDown选项
     * å¦‚果有且设置了value值,则将其直接置为下拉可选项
     * <p>
     * 2.或者在调用ExcelUtil时指定了可选项,将依据传入的可选项做下拉
     * <p>
     * 3.二者并存,注意调用方式
     */
    @Override
    public void afterSheetCreate(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder) {
        Sheet sheet = writeSheetHolder.getSheet();
        // å¼€å§‹è®¾ç½®ä¸‹æ‹‰æ¡† HSSFWorkbook
        DataValidationHelper helper = sheet.getDataValidationHelper();
        Workbook workbook = writeWorkbookHolder.getWorkbook();
        FieldCache fieldCache = ClassUtils.declaredFields(writeWorkbookHolder.getClazz(), writeWorkbookHolder);
        for (Map.Entry<Integer, FieldWrapper> entry : fieldCache.getSortedFieldMap().entrySet()) {
            Integer index = entry.getKey();
            FieldWrapper wrapper = entry.getValue();
            Field field = wrapper.getField();
            // å¾ªçŽ¯å®žä½“ä¸­çš„æ¯ä¸ªå±žæ€§
            // å¯é€‰çš„下拉值
            List<String> options = new ArrayList<>();
            if (field.isAnnotationPresent(ExcelDictFormat.class)) {
                // å¦‚果指定了@ExcelDictFormat,则使用字典的逻辑
                ExcelDictFormat format = field.getDeclaredAnnotation(ExcelDictFormat.class);
                String dictType = format.dictType();
                String converterExp = format.readConverterExp();
                if (StringUtils.isNotBlank(dictType)) {
                    // å¦‚果传递了字典名,则依据字典建立下拉
                    Collection<String> values = Optional.ofNullable(dictService.getAllDictByDictType(dictType))
                        .orElseThrow(() -> new ServiceException("字典 {} ä¸å­˜åœ¨", dictType))
                        .values();
                    options = new ArrayList<>(values);
                } else if (StringUtils.isNotBlank(converterExp)) {
                    // å¦‚果指定了确切的值,则直接解析确切的值
                    List<String> strList = StringUtils.splitList(converterExp, format.separator());
                    options = StreamUtils.toList(strList, s -> StringUtils.split(s, "=")[1]);
                }
            } else if (field.isAnnotationPresent(ExcelEnumFormat.class)) {
                // å¦åˆ™å¦‚果指定了@ExcelEnumFormat,则使用枚举的逻辑
                ExcelEnumFormat format = field.getDeclaredAnnotation(ExcelEnumFormat.class);
                List<Object> values = EnumUtil.getFieldValues(format.enumClass(), format.textField());
                options = StreamUtils.toList(values, Convert::toStr);
            } else if (field.isAnnotationPresent(ExcelDynamicOptions.class)) {
                // å¤„理动态下拉选项
                ExcelDynamicOptions dynamicOptions = field.getDeclaredAnnotation(ExcelDynamicOptions.class);
                // èŽ·å–æä¾›è€…å®žä¾‹
                ExcelOptionsProvider provider = SpringUtils.getBean(dynamicOptions.providerClass());
                Set<String> providerOptions = provider.getOptions();
                if (CollUtil.isNotEmpty(providerOptions)) {
                    options = new ArrayList<>(providerOptions);
                }
            }
            if (ObjectUtil.isNotEmpty(options)) {
                // ä»…当下拉可选项不为空时执行
                if (options.size() > 20) {
                    // è¿™é‡Œé™åˆ¶å¦‚果可选项大于20,则使用额外表形式
                    dropDownWithSheet(helper, workbook, sheet, index, options);
                } else {
                    // å¦åˆ™ä½¿ç”¨å›ºå®šå€¼å½¢å¼
                    dropDownWithSimple(helper, sheet, index, options);
                }
            }
        }
        if (CollUtil.isEmpty(dropDownOptions)) {
            return;
        }
        dropDownOptions.forEach(everyOptions -> {
            // å¦‚果传递了下拉框选择器参数
            if (!everyOptions.getNextOptions().isEmpty()) {
                // å½“二级选项不为空时,使用额外关联表的形式
                dropDownLinkedOptions(helper, workbook, sheet, everyOptions);
            } else if (everyOptions.getOptions().size() > 10) {
                // å½“一级选项参数个数大于10,使用额外表的形式
                dropDownWithSheet(helper, workbook, sheet, everyOptions.getIndex(), everyOptions.getOptions());
            } else {
                // å¦åˆ™ä½¿ç”¨é»˜è®¤å½¢å¼
                dropDownWithSimple(helper, sheet, everyOptions.getIndex(), everyOptions.getOptions());
            }
        });
    }
    /**
     * <h2>简单下拉框</h2>
     * ç›´æŽ¥å°†å¯é€‰é¡¹æ‹¼æŽ¥ä¸ºæŒ‡å®šåˆ—的数据校验值
     *
     * @param celIndex åˆ—index
     * @param value    ä¸‹æ‹‰é€‰å¯é€‰å€¼
     */
    private void dropDownWithSimple(DataValidationHelper helper, Sheet sheet, Integer celIndex, List<String> value) {
        if (ObjectUtil.isEmpty(value)) {
            return;
        }
        this.markOptionsToSheet(helper, sheet, celIndex, helper.createExplicitListConstraint(ArrayUtil.toArray(value, String.class)));
    }
    /**
     * <h2>额外表格形式的级联下拉框</h2>
     *
     * @param options é¢å¤–表格形式存储的下拉可选项
     */
    private void dropDownLinkedOptions(DataValidationHelper helper, Workbook workbook, Sheet sheet, DropDownOptions options) {
        String linkedOptionsSheetName = String.format("%s_%d", LINKED_OPTIONS_SHEET_NAME, currentLinkedOptionsSheetIndex);
        // åˆ›å»ºè”动下拉数据表
        Sheet linkedOptionsDataSheet = workbook.createSheet(WorkbookUtil.createSafeSheetName(linkedOptionsSheetName));
        // å°†ä¸‹æ‹‰è¡¨éšè—
        workbook.setSheetHidden(workbook.getSheetIndex(linkedOptionsDataSheet), true);
        // é€‰é¡¹æ•°æ®
        List<String> firstOptions = options.getOptions();
        Map<String, List<String>> secoundOptionsMap = options.getNextOptions();
        // é‡‡ç”¨æŒ‰è¡Œå¡«å……数据的方式,避免出现数据无法写入的问题
        // Attempting to write a row in the range that is already written to disk
        // ä½¿ç”¨ArrayList记载数据,防止乱序
        List<String> columnNames = new ArrayList<>();
        // å†™å…¥ç¬¬ä¸€è¡Œï¼Œå³ç¬¬ä¸€çº§çš„æ•°æ®
        Row firstRow = linkedOptionsDataSheet.createRow(0);
        for (int columnIndex = 0; columnIndex < firstOptions.size(); columnIndex++) {
            String columnName = firstOptions.get(columnIndex);
            firstRow.createCell(columnIndex)
                .setCellValue(columnName);
            columnNames.add(columnName);
        }
        // åˆ›å»ºåç§°ç®¡ç†å™¨
        Name name = workbook.createName();
        // è®¾ç½®åç§°ç®¡ç†å™¨çš„别名
        name.setNameName(linkedOptionsSheetName);
        // ä»¥æ¨ªå‘第一行创建一级下拉拼接引用位置
        String firstOptionsFunction = String.format("%s!$%s$1:$%s$1",
            linkedOptionsSheetName,
            getExcelColumnName(0),
            getExcelColumnName(firstOptions.size())
        );
        // è®¾ç½®åç§°ç®¡ç†å™¨çš„引用位置
        name.setRefersToFormula(firstOptionsFunction);
        // è®¾ç½®æ•°æ®æ ¡éªŒä¸ºåºåˆ—模式,引用的是名称管理器中的别名
        this.markOptionsToSheet(helper, sheet, options.getIndex(), helper.createFormulaListConstraint(linkedOptionsSheetName));
        // åˆ›å»ºäºŒçº§é€‰é¡¹çš„名称管理器
        for (int columIndex = 0; columIndex < columnNames.size(); columIndex++) {
            // åˆ—名
            String firstOptionsColumnName = getExcelColumnName(columIndex);
            // å¯¹åº”的一级值
            String thisFirstOptionsValue = columnNames.get(columIndex);
            // ä»¥è¯¥ä¸€çº§é€‰é¡¹å€¼åˆ›å»ºå­åç§°ç®¡ç†å™¨
            Name sonName = workbook.createName();
            // è®¾ç½®åç§°ç®¡ç†å™¨çš„别名
            sonName.setNameName(thisFirstOptionsValue);
            // ä»¥ç¬¬äºŒè¡Œè¯¥åˆ—数据拼接引用位置
            String sonFunction = String.format("%s!$%s$2:$%s$%d",
                linkedOptionsSheetName,
                firstOptionsColumnName,
                firstOptionsColumnName,
                // äºŒçº§é€‰é¡¹å­˜åœ¨åˆ™è®¾ç½®ä¸º(选项个数+1)行,否则设置为2行
                Math.max(Optional.ofNullable(secoundOptionsMap.get(thisFirstOptionsValue))
                    .orElseGet(ArrayList::new).size(), 1) + 1
            );
            // è®¾ç½®åç§°ç®¡ç†å™¨çš„引用位置
            sonName.setRefersToFormula(sonFunction);
            // æ•°æ®éªŒè¯ä¸ºåºåˆ—模式,引用到每一个主表中的二级选项位置
            // åˆ›å»ºå­é¡¹çš„名称管理器,只是为了使得Excel可以识别到数据
            String mainSheetFirstOptionsColumnName = getExcelColumnName(options.getIndex());
            for (int i = 0; i < 100; i++) {
                // ä»¥ä¸€çº§é€‰é¡¹å¯¹åº”的主体所在位置创建二级下拉
                String secondOptionsFunction = String.format("=INDIRECT(%s%d)", mainSheetFirstOptionsColumnName, i + 1);
                // äºŒçº§åªèƒ½ä¸»è¡¨æ¯ä¸€è¡Œçš„æ¯ä¸€åˆ—添加二级校验
                markLinkedOptionsToSheet(helper, sheet, i, options.getNextIndex(), helper.createFormulaListConstraint(secondOptionsFunction));
            }
        }
        // å°†äºŒçº§æ•°æ®å¤„理为按行区分
        Map<Integer, List<String>> columnValueMap = new HashMap<>();
        int currentRow = 1;
        while (currentRow >= 0) {
            boolean flag = false;
            List<String> rowData = new ArrayList<>();
            for (String columnName : columnNames) {
                List<String> data = secoundOptionsMap.get(columnName);
                if (CollUtil.isEmpty(data)) {
                    // æ·»åŠ ç©ºå­—ç¬¦ä¸²å¡«å……ä½ç½®
                    rowData.add(" ");
                    continue;
                }
                // å–第一个
                String str = data.get(0);
                rowData.add(str);
                // é€šè¿‡ç§»é™¤çš„æ–¹å¼é¿å…é‡å¤
                data.remove(0);
                // è®¾ç½®å¯ä»¥ç»§ç»­
                flag = true;
            }
            columnValueMap.put(currentRow, rowData);
            // å¯ä»¥ç»§ç»­ï¼Œåˆ™å¢žåŠ è¡Œæ•°ï¼Œå¦åˆ™ç½®ä¸ºè´Ÿæ•°è·³å‡ºå¾ªçŽ¯
            if (flag) {
                currentRow++;
            } else {
                currentRow = -1;
            }
        }
        // å¡«å……第二级选项数据
        columnValueMap.forEach((rowIndex, rowValues) -> {
            Row row = linkedOptionsDataSheet.createRow(rowIndex);
            for (int columnIndex = 0; columnIndex < rowValues.size(); columnIndex++) {
                String rowValue = rowValues.get(columnIndex);
                // å¡«å……位置的部分不渲染
                if (StrUtil.isNotBlank(rowValue)) {
                    row.createCell(columnIndex)
                        .setCellValue(rowValue);
                }
            }
        });
        currentLinkedOptionsSheetIndex++;
    }
    /**
     * <h2>额外表格形式的普通下拉框</h2>
     * ç”±äºŽä¸‹æ‹‰æ¡†å¯é€‰å€¼æ•°é‡è¿‡å¤šï¼Œä¸ºæå‡Excel打开效率,使用额外表格形式做下拉
     *
     * @param celIndex ä¸‹æ‹‰é€‰
     * @param value    ä¸‹æ‹‰é€‰å¯é€‰å€¼
     */
    private void dropDownWithSheet(DataValidationHelper helper, Workbook workbook, Sheet sheet, Integer celIndex, List<String> value) {
        //由于poi的写出相关问题,超过100个会被临时写进硬盘,导致后续内存合并会出Attempting to write a row[] in the range [] that is already written to disk
        String tmpOptionsSheetName = OPTIONS_SHEET_NAME + "_" + currentOptionsColumnIndex;
        // åˆ›å»ºä¸‹æ‹‰æ•°æ®è¡¨
        Sheet simpleDataSheet = Optional.ofNullable(workbook.getSheet(WorkbookUtil.createSafeSheetName(tmpOptionsSheetName)))
            .orElseGet(() -> workbook.createSheet(WorkbookUtil.createSafeSheetName(tmpOptionsSheetName)));
        // å°†ä¸‹æ‹‰è¡¨éšè—
        workbook.setSheetHidden(workbook.getSheetIndex(simpleDataSheet), true);
        // å®Œå–„纵向的一级选项数据表
        for (int i = 0; i < value.size(); i++) {
            int finalI = i;
            // èŽ·å–æ¯ä¸€é€‰é¡¹è¡Œï¼Œå¦‚æžœæ²¡æœ‰åˆ™åˆ›å»º
            Row row = Optional.ofNullable(simpleDataSheet.getRow(i))
                .orElseGet(() -> simpleDataSheet.createRow(finalI));
            // èŽ·å–æœ¬çº§é€‰é¡¹å¯¹åº”çš„é€‰é¡¹åˆ—ï¼Œå¦‚æžœæ²¡æœ‰åˆ™åˆ›å»ºã€‚ä¸Šè¿°é‡‡ç”¨å¤šä¸ªsheet,默认索引为1列
            Cell cell = Optional.ofNullable(row.getCell(0))
                .orElseGet(() -> row.createCell(0));
            // è®¾ç½®å€¼
            cell.setCellValue(value.get(i));
        }
        // åˆ›å»ºåç§°ç®¡ç†å™¨
        Name name = workbook.createName();
        // è®¾ç½®åç§°ç®¡ç†å™¨çš„别名
        String nameName = String.format("%s_%d", tmpOptionsSheetName, celIndex);
        name.setNameName(nameName);
        // ä»¥çºµå‘第一列创建一级下拉拼接引用位置
        String function = String.format("%s!$%s$1:$%s$%d",
            tmpOptionsSheetName,
            getExcelColumnName(0),
            getExcelColumnName(0),
            value.size());
        // è®¾ç½®åç§°ç®¡ç†å™¨çš„引用位置
        name.setRefersToFormula(function);
        // è®¾ç½®æ•°æ®æ ¡éªŒä¸ºåºåˆ—模式,引用的是名称管理器中的别名
        this.markOptionsToSheet(helper, sheet, celIndex, helper.createFormulaListConstraint(nameName));
        currentOptionsColumnIndex++;
    }
    /**
     * æŒ‚载下拉的列,仅限一级选项
     */
    private void markOptionsToSheet(DataValidationHelper helper, Sheet sheet, Integer celIndex,
                                    DataValidationConstraint constraint) {
        // è®¾ç½®æ•°æ®æœ‰æ•ˆæ€§åŠ è½½åœ¨å“ªä¸ªå•å…ƒæ ¼ä¸Š,四个参数分别是:起始行、终止行、起始列、终止列
        CellRangeAddressList addressList = new CellRangeAddressList(1, 1000, celIndex, celIndex);
        markDataValidationToSheet(helper, sheet, constraint, addressList);
    }
    /**
     * æŒ‚载下拉的列,仅限二级选项
     */
    private void markLinkedOptionsToSheet(DataValidationHelper helper, Sheet sheet, Integer rowIndex,
                                          Integer celIndex, DataValidationConstraint constraint) {
        // è®¾ç½®æ•°æ®æœ‰æ•ˆæ€§åŠ è½½åœ¨å“ªä¸ªå•å…ƒæ ¼ä¸Š,四个参数分别是:起始行、终止行、起始列、终止列
        CellRangeAddressList addressList = new CellRangeAddressList(rowIndex, rowIndex, celIndex, celIndex);
        markDataValidationToSheet(helper, sheet, constraint, addressList);
    }
    /**
     * åº”用数据校验
     */
    private void markDataValidationToSheet(DataValidationHelper helper, Sheet sheet,
                                           DataValidationConstraint constraint, CellRangeAddressList addressList) {
        // æ•°æ®æœ‰æ•ˆæ€§å¯¹è±¡
        DataValidation dataValidation = helper.createValidation(constraint, addressList);
        // å¤„理Excel兼容性问题
        if (dataValidation instanceof XSSFDataValidation) {
            //数据校验
            dataValidation.setSuppressDropDownArrow(true);
            //错误提示
            dataValidation.setErrorStyle(DataValidation.ErrorStyle.STOP);
            dataValidation.createErrorBox("提示", "此值与单元格定义数据不一致");
            dataValidation.setShowErrorBox(true);
            //选定提示
            dataValidation.createPromptBox("填写说明:", "填写内容只能为下拉中数据,其他数据将导致导入失败");
            dataValidation.setShowPromptBox(true);
            sheet.addValidationData(dataValidation);
        } else {
            dataValidation.setSuppressDropDownArrow(false);
        }
        sheet.addValidationData(dataValidation);
    }
    /**
     * <h2>依据列index获取列名英文</h2>
     * ä¾æ®åˆ—index转换为Excel中的列名英文
     * <p>例如第1列,index为0,解析出来为A列</p>
     * ç¬¬27列,index为26,解析为AA列
     * <p>第28列,index为27,解析为AB列</p>
     *
     * @param columnIndex åˆ—index
     * @return åˆ—index所在得英文名
     */
    private String getExcelColumnName(int columnIndex) {
        // 26一循环的次数
        int columnCircleCount = columnIndex / 26;
        // 26一循环内的位置
        int thisCircleColumnIndex = columnIndex % 26;
        // 26一循环的次数大于0,则视为栏名至少两位
        String columnPrefix = columnCircleCount == 0
            ? StrUtil.EMPTY
            : StrUtil.subWithLength(EXCEL_COLUMN_NAME, columnCircleCount - 1, 1);
        // ä»Ž26一循环内取对应的栏位名
        String columnNext = StrUtil.subWithLength(EXCEL_COLUMN_NAME, thisCircleColumnIndex, 1);
        // å°†äºŒè€…拼接即为最终的栏位名
        return columnPrefix + columnNext;
    }
}
RuoYi-Vue-Plus/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 cn.idev.excel.read.listener.ReadListener;
/**
 * Excel å¯¼å…¥ç›‘听
 *
 * @author Lion Li
 */
public interface ExcelListener<T> extends ReadListener<T> {
    ExcelResult<T> getExcelResult();
}
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/core/ExcelOptionsProvider.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,19 @@
package org.dromara.common.excel.core;
import java.util.Set;
/**
 * Excel下拉选项数据提供接口
 *
 * @author Angus
 */
public interface ExcelOptionsProvider {
    /**
     * èŽ·å–ä¸‹æ‹‰é€‰é¡¹æ•°æ®
     *
     * @return ä¸‹æ‹‰é€‰é¡¹åˆ—表
     */
    Set<String> getOptions();
}
RuoYi-Vue-Plus/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-Vue-Plus/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/handler/DataWriteHandler.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,123 @@
package org.dromara.common.excel.handler;
import cn.hutool.core.collection.CollUtil;
import cn.idev.excel.annotation.ExcelProperty;
import cn.idev.excel.metadata.data.DataFormatData;
import cn.idev.excel.metadata.data.WriteCellData;
import cn.idev.excel.util.StyleUtil;
import cn.idev.excel.write.handler.CellWriteHandler;
import cn.idev.excel.write.handler.SheetWriteHandler;
import cn.idev.excel.write.handler.context.CellWriteHandlerContext;
import cn.idev.excel.write.metadata.holder.WriteSheetHolder;
import cn.idev.excel.write.metadata.style.WriteCellStyle;
import cn.idev.excel.write.metadata.style.WriteFont;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFClientAnchor;
import org.apache.poi.xssf.usermodel.XSSFRichTextString;
import org.dromara.common.excel.annotation.ExcelNotation;
import org.dromara.common.excel.annotation.ExcelRequired;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;
/**
 * æ‰¹æ³¨ã€å¿…å¡«
 *
 * @author guzhouyanyu
 */
public class DataWriteHandler implements SheetWriteHandler, CellWriteHandler {
    /**
     * æ‰¹æ³¨
     */
    private final Map<String, String> notationMap;
    /**
     * å¤´åˆ—字体颜色
     */
    private final Map<String, Short> headColumnMap;
    public DataWriteHandler(Class<?> clazz) {
        notationMap = getNotationMap(clazz);
        headColumnMap = getRequiredMap(clazz);
    }
    @Override
    public void afterCellDispose(CellWriteHandlerContext context) {
        if (CollUtil.isEmpty(notationMap) && CollUtil.isEmpty(headColumnMap)) {
            return;
        }
        // ç¬¬ä¸€è¡Œ
        WriteCellData<?> cellData = context.getFirstCellData();
        // ç¬¬ä¸€ä¸ªæ ¼å­
        WriteCellStyle writeCellStyle = cellData.getOrCreateStyle();
        if (context.getHead()) {
            DataFormatData dataFormatData = new DataFormatData();
            // å•元格设置为文本格式
            dataFormatData.setIndex((short) 49);
            writeCellStyle.setDataFormatData(dataFormatData);
            Cell cell = context.getCell();
            WriteSheetHolder writeSheetHolder = context.getWriteSheetHolder();
            Sheet sheet = writeSheetHolder.getSheet();
            Workbook workbook = writeSheetHolder.getSheet().getWorkbook();
            Drawing<?> drawing = sheet.createDrawingPatriarch();
            // è®¾ç½®æ ‡é¢˜å­—体样式
            WriteFont headWriteFont = new WriteFont();
            // åŠ ç²—
            headWriteFont.setBold(true);
            if (CollUtil.isNotEmpty(headColumnMap) && headColumnMap.containsKey(cell.getStringCellValue())) {
                // è®¾ç½®å­—体颜色
                headWriteFont.setColor(headColumnMap.get(cell.getStringCellValue()));
            }
            writeCellStyle.setWriteFont(headWriteFont);
            CellStyle cellStyle = StyleUtil.buildCellStyle(workbook, null, writeCellStyle);
            cell.setCellStyle(cellStyle);
            if (CollUtil.isNotEmpty(notationMap) && notationMap.containsKey(cell.getStringCellValue())) {
                // æ‰¹æ³¨å†…容
                String notationContext = notationMap.get(cell.getStringCellValue());
                // åˆ›å»ºç»˜å›¾å¯¹è±¡
                Comment comment = drawing.createCellComment(new XSSFClientAnchor(0, 0, 0, 0, (short) cell.getColumnIndex(), 0, (short) 5, 5));
                comment.setString(new XSSFRichTextString(notationContext));
                cell.setCellComment(comment);
            }
        }
    }
    /**
     * èŽ·å–å¿…å¡«åˆ—
     */
    private static Map<String, Short> getRequiredMap(Class<?> clazz) {
        Map<String, Short> requiredMap = new HashMap<>();
        Field[] fields = clazz.getDeclaredFields();
        for (Field field : fields) {
            if (!field.isAnnotationPresent(ExcelRequired.class)) {
                continue;
            }
            ExcelRequired excelRequired = field.getAnnotation(ExcelRequired.class);
            ExcelProperty excelProperty = field.getAnnotation(ExcelProperty.class);
            requiredMap.put(excelProperty.value()[0], excelRequired.fontColor().getIndex());
        }
        return requiredMap;
    }
    /**
     * èŽ·å–æ‰¹æ³¨
     */
    private static Map<String, String> getNotationMap(Class<?> clazz) {
        Map<String, String> notationMap = new HashMap<>();
        Field[] fields = clazz.getDeclaredFields();
        for (Field field : fields) {
            if (!field.isAnnotationPresent(ExcelNotation.class)) {
                continue;
            }
            ExcelNotation excelNotation = field.getAnnotation(ExcelNotation.class);
            ExcelProperty excelProperty = field.getAnnotation(ExcelProperty.class);
            notationMap.put(excelProperty.value()[0], excelNotation.value());
        }
        return notationMap;
    }
}
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/utils/ExcelUtil.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,479 @@
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 cn.idev.excel.FastExcel;
import cn.idev.excel.ExcelWriter;
import cn.idev.excel.write.builder.ExcelWriterSheetBuilder;
import cn.idev.excel.write.metadata.WriteSheet;
import cn.idev.excel.write.metadata.fill.FillConfig;
import cn.idev.excel.write.metadata.fill.FillWrapper;
import cn.idev.excel.write.style.column.LongestMatchColumnWidthStyleStrategy;
import jakarta.servlet.ServletOutputStream;
import jakarta.servlet.http.HttpServletResponse;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
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.*;
import org.dromara.common.excel.handler.DataWriteHandler;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
/**
 * 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 FastExcel.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);
        FastExcel.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) {
        FastExcel.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, null);
        } catch (IOException e) {
            throw new RuntimeException("导出Excel异常");
        }
    }
    /**
     * å¯¼å‡ºexcel
     *
     * @param list      å¯¼å‡ºæ•°æ®é›†åˆ
     * @param sheetName å·¥ä½œè¡¨çš„名称
     * @param clazz     å®žä½“ç±»
     * @param response  å“åº”体
     * @param options   çº§è”下拉选
     */
    public static <T> void exportExcel(List<T> list, String sheetName, Class<T> clazz, HttpServletResponse response, List<DropDownOptions> options) {
        try {
            resetResponse(sheetName, response);
            ServletOutputStream os = response.getOutputStream();
            exportExcel(list, sheetName, clazz, false, os, options);
        } 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, null);
        } catch (IOException e) {
            throw new RuntimeException("导出Excel异常");
        }
    }
    /**
     * å¯¼å‡ºexcel
     *
     * @param list      å¯¼å‡ºæ•°æ®é›†åˆ
     * @param sheetName å·¥ä½œè¡¨çš„名称
     * @param clazz     å®žä½“ç±»
     * @param merge     æ˜¯å¦åˆå¹¶å•元格
     * @param response  å“åº”体
     * @param options   çº§è”下拉选
     */
    public static <T> void exportExcel(List<T> list, String sheetName, Class<T> clazz, boolean merge, HttpServletResponse response, List<DropDownOptions> options) {
        try {
            resetResponse(sheetName, response);
            ServletOutputStream os = response.getOutputStream();
            exportExcel(list, sheetName, clazz, merge, os, options);
        } 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, null);
    }
    /**
     * å¯¼å‡ºexcel
     *
     * @param list      å¯¼å‡ºæ•°æ®é›†åˆ
     * @param sheetName å·¥ä½œè¡¨çš„名称
     * @param clazz     å®žä½“ç±»
     * @param os        è¾“出流
     * @param options   çº§è”下拉选内容
     */
    public static <T> void exportExcel(List<T> list, String sheetName, Class<T> clazz, OutputStream os, List<DropDownOptions> options) {
        exportExcel(list, sheetName, clazz, false, os, options);
    }
    /**
     * å¯¼å‡º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, List<DropDownOptions> options) {
        ExcelWriterSheetBuilder builder = FastExcel.write(os, clazz)
            .autoCloseStream(false)
            // è‡ªåŠ¨é€‚é…
            .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy())
            // å¤§æ•°å€¼è‡ªåŠ¨è½¬æ¢ é˜²æ­¢å¤±çœŸ
            .registerConverter(new ExcelBigNumberConvert())
            .registerWriteHandler(new DataWriteHandler(clazz))
            .sheet(sheetName);
        if (merge) {
            // åˆå¹¶å¤„理器
            builder.registerWriteHandler(new CellMergeStrategy(list, true));
        }
        // æ·»åŠ ä¸‹æ‹‰æ¡†æ“ä½œ
        builder.registerWriteHandler(new ExcelDownHandler(options));
        builder.doWrite(list);
    }
    /**
     * å¯¼å‡ºexcel
     *
     * @param headType å¸¦Excel注解的类型
     * @param os       è¾“出流
     * @param options  Excel下拉可选项
     * @param consumer å¯¼å‡ºåŠ©æ‰‹æ¶ˆè´¹å‡½æ•°
     */
    public static <T> void exportExcel(Class<T> headType, OutputStream os, List<DropDownOptions> options, Consumer<ExcelWriterWrapper<T>> consumer) {
        try (ExcelWriter writer = FastExcel.write(os, headType)
            .autoCloseStream(false)
            // è‡ªåŠ¨é€‚é…
            .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy())
            // å¤§æ•°å€¼è‡ªåŠ¨è½¬æ¢ é˜²æ­¢å¤±çœŸ
            .registerConverter(new ExcelBigNumberConvert())
            // æ‰¹æ³¨å¿…填项处理
            .registerWriteHandler(new DataWriteHandler(headType))
            // æ·»åŠ ä¸‹æ‹‰æ¡†æ“ä½œ
            .registerWriteHandler(new ExcelDownHandler(options))
            .build()) {
            // æ‰§è¡Œæ¶ˆè´¹å‡½æ•°
            consumer.accept(ExcelWriterWrapper.of(writer));
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
    /**
     * å¯¼å‡ºexcel
     *
     * @param headType å¸¦Excel注解的类型
     * @param os       è¾“出流
     * @param consumer å¯¼å‡ºåŠ©æ‰‹æ¶ˆè´¹å‡½æ•°
     */
    public static <T> void exportExcel(Class<T> headType, OutputStream os, Consumer<ExcelWriterWrapper<T>> consumer) {
        exportExcel(headType, os, null, consumer);
    }
    /**
     * å•表多数据模板导出 æ¨¡æ¿æ ¼å¼ä¸º {.属性}
     *
     * @param filename     æ–‡ä»¶å
     * @param templatePath æ¨¡æ¿è·¯å¾„ resource ç›®å½•下的路径包括模板文件名
     *                     ä¾‹å¦‚: excel/temp.xlsx
     *                     é‡ç‚¹: æ¨¡æ¿æ–‡ä»¶å¿…须放置到启动类对应的 resource ç›®å½•下
     * @param data         æ¨¡æ¿éœ€è¦çš„æ•°æ®
     * @param response     å“åº”体
     */
    public static <T> void exportTemplate(List<T> data, String filename, String templatePath, HttpServletResponse response) {
        try {
            if (CollUtil.isEmpty(data)) {
                throw new IllegalArgumentException("数据为空");
            }
            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 <T> void exportTemplate(List<T> data, String templatePath, OutputStream os) {
        ClassPathResource templateResource = new ClassPathResource(templatePath);
        ExcelWriter excelWriter = FastExcel.write(os)
            .withTemplate(templateResource.getStream())
            .autoCloseStream(false)
            // å¤§æ•°å€¼è‡ªåŠ¨è½¬æ¢ é˜²æ­¢å¤±çœŸ
            .registerConverter(new ExcelBigNumberConvert())
            .registerWriteHandler(new DataWriteHandler(data.get(0).getClass()))
            .build();
        WriteSheet writeSheet = FastExcel.writerSheet().build();
        FillConfig fillConfig = FillConfig.builder().forceNewRow(Boolean.TRUE).build();
        // å•表多数据导出 æ¨¡æ¿æ ¼å¼ä¸º {.属性}
        for (T d : data) {
            excelWriter.fill(d, fillConfig, 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 {
            if (CollUtil.isEmpty(data)) {
                throw new IllegalArgumentException("数据为空");
            }
            resetResponse(filename, response);
            ServletOutputStream os = response.getOutputStream();
            exportTemplateMultiList(data, templatePath, os);
        } catch (IOException e) {
            throw new RuntimeException("导出Excel异常");
        }
    }
    /**
     * å¤šsheet模板导出 æ¨¡æ¿æ ¼å¼ä¸º {key.属性}
     *
     * @param filename     æ–‡ä»¶å
     * @param templatePath æ¨¡æ¿è·¯å¾„ resource ç›®å½•下的路径包括模板文件名
     *                     ä¾‹å¦‚: excel/temp.xlsx
     *                     é‡ç‚¹: æ¨¡æ¿æ–‡ä»¶å¿…须放置到启动类对应的 resource ç›®å½•下
     * @param data         æ¨¡æ¿éœ€è¦çš„æ•°æ®
     * @param response     å“åº”体
     */
    public static void exportTemplateMultiSheet(List<Map<String, Object>> data, String filename, String templatePath, HttpServletResponse response) {
        try {
            if (CollUtil.isEmpty(data)) {
                throw new IllegalArgumentException("数据为空");
            }
            resetResponse(filename, response);
            ServletOutputStream os = response.getOutputStream();
            exportTemplateMultiSheet(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 = FastExcel.write(os)
            .withTemplate(templateResource.getStream())
            .autoCloseStream(false)
            // å¤§æ•°å€¼è‡ªåŠ¨è½¬æ¢ é˜²æ­¢å¤±çœŸ
            .registerConverter(new ExcelBigNumberConvert())
            .build();
        WriteSheet writeSheet = FastExcel.writerSheet().build();
        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(), fillConfig, writeSheet);
            }
        }
        excelWriter.finish();
    }
    /**
     * å¤šsheet模板导出 æ¨¡æ¿æ ¼å¼ä¸º {key.属性}
     *
     * @param templatePath æ¨¡æ¿è·¯å¾„ resource ç›®å½•下的路径包括模板文件名
     *                     ä¾‹å¦‚: excel/temp.xlsx
     *                     é‡ç‚¹: æ¨¡æ¿æ–‡ä»¶å¿…须放置到启动类对应的 resource ç›®å½•下
     * @param data         æ¨¡æ¿éœ€è¦çš„æ•°æ®
     * @param os           è¾“出流
     */
    public static void exportTemplateMultiSheet(List<Map<String, Object>> data, String templatePath, OutputStream os) {
        ClassPathResource templateResource = new ClassPathResource(templatePath);
        ExcelWriter excelWriter = FastExcel.write(os)
            .withTemplate(templateResource.getStream())
            .autoCloseStream(false)
            // å¤§æ•°å€¼è‡ªåŠ¨è½¬æ¢ é˜²æ­¢å¤±çœŸ
            .registerConverter(new ExcelBigNumberConvert())
            .build();
        for (int i = 0; i < data.size(); i++) {
            WriteSheet writeSheet = FastExcel.writerSheet(i).build();
            for (Map.Entry<String, Object> map : data.get(i).entrySet()) {
                // è®¾ç½®åˆ—表后续还有数据
                FillConfig fillConfig = FillConfig.builder().forceNewRow(Boolean.TRUE).build();
                if (map.getValue() instanceof Collection) {
                    // å¤šè¡¨å¯¼å‡ºå¿…须使用 FillWrapper
                    excelWriter.fill(new FillWrapper(map.getKey(), (Collection<?>) map.getValue()), fillConfig, writeSheet);
                } else {
                    excelWriter.fill(map.getValue(), writeSheet);
                }
            }
        }
        excelWriter.finish();
    }
    /**
     * é‡ç½®å“åº”体
     */
    private static void resetResponse(String sheetName, HttpServletResponse response) throws UnsupportedEncodingException {
        String filename = encodingFilename(sheetName);
        FileUtils.setAttachmentResponseHeader(response, filename);
        response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=UTF-8");
    }
    /**
     * è§£æžå¯¼å‡ºå€¼ 0=男,1=女,2=未知
     *
     * @param propertyValue å‚数值
     * @param converterExp  ç¿»è¯‘注解
     * @param separator     åˆ†éš”符
     * @return è§£æžåŽå€¼
     */
    public static String convertByExp(String propertyValue, String converterExp, String separator) {
        StringBuilder propertyString = new StringBuilder();
        String[] convertSource = converterExp.split(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] + 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] + 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-Vue-Plus/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/utils/ExcelWriterWrapper.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,127 @@
package org.dromara.common.excel.utils;
import cn.idev.excel.ExcelWriter;
import cn.idev.excel.FastExcel;
import cn.idev.excel.context.WriteContext;
import cn.idev.excel.write.builder.ExcelWriterSheetBuilder;
import cn.idev.excel.write.builder.ExcelWriterTableBuilder;
import cn.idev.excel.write.metadata.WriteSheet;
import cn.idev.excel.write.metadata.WriteTable;
import cn.idev.excel.write.metadata.fill.FillConfig;
import java.util.Collection;
import java.util.function.Supplier;
/**
 * ExcelWriterWrapper Excel写出包装器
 * <br>
 * æä¾›äº†ä¸€ç»„与 ExcelWriter ä¸€ä¸€å¯¹åº”的写出方法,避免直接提供 ExcelWriter è€Œå¯¼è‡´çš„一些不可控问题(比如提前关闭了IO流等)
 *
 * @author ç§‹è¾žæœªå¯’
 * @see ExcelWriter
 */
public record ExcelWriterWrapper<T>(ExcelWriter excelWriter) {
    public void write(Collection<T> data, WriteSheet writeSheet) {
        excelWriter.write(data, writeSheet);
    }
    public void write(Supplier<Collection<T>> supplier, WriteSheet writeSheet) {
        excelWriter.write(supplier.get(), writeSheet);
    }
    public void write(Collection<T> data, WriteSheet writeSheet, WriteTable writeTable) {
        excelWriter.write(data, writeSheet, writeTable);
    }
    public void write(Supplier<Collection<T>> supplier, WriteSheet writeSheet, WriteTable writeTable) {
        excelWriter.write(supplier.get(), writeSheet, writeTable);
    }
    public void fill(Object data, WriteSheet writeSheet) {
        excelWriter.fill(data, writeSheet);
    }
    public void fill(Object data, FillConfig fillConfig, WriteSheet writeSheet) {
        excelWriter.fill(data, fillConfig, writeSheet);
    }
    public void fill(Supplier<Object> supplier, WriteSheet writeSheet) {
        excelWriter.fill(supplier, writeSheet);
    }
    public void fill(Supplier<Object> supplier, FillConfig fillConfig, WriteSheet writeSheet) {
        excelWriter.fill(supplier, fillConfig, writeSheet);
    }
    public WriteContext writeContext() {
        return excelWriter.writeContext();
    }
    /**
     * åˆ›å»ºä¸€ä¸ª ExcelWriterWrapper
     *
     * @param excelWriter ExcelWriter
     * @return ExcelWriterWrapper
     */
    public static  <T> ExcelWriterWrapper<T> of(ExcelWriter excelWriter) {
        return new ExcelWriterWrapper<>(excelWriter);
    }
    // -------------------------------- sheet start
    public static WriteSheet buildSheet(Integer sheetNo, String sheetName) {
        return sheetBuilder(sheetNo, sheetName).build();
    }
    public static WriteSheet buildSheet(Integer sheetNo) {
        return sheetBuilder(sheetNo).build();
    }
    public static WriteSheet buildSheet(String sheetName) {
        return sheetBuilder(sheetName).build();
    }
    public static WriteSheet buildSheet() {
        return sheetBuilder().build();
    }
    public static ExcelWriterSheetBuilder sheetBuilder(Integer sheetNo, String sheetName) {
        return FastExcel.writerSheet(sheetNo, sheetName);
    }
    public static ExcelWriterSheetBuilder sheetBuilder(Integer sheetNo) {
        return FastExcel.writerSheet(sheetNo);
    }
    public static ExcelWriterSheetBuilder sheetBuilder(String sheetName) {
        return FastExcel.writerSheet(sheetName);
    }
    public static ExcelWriterSheetBuilder sheetBuilder() {
        return FastExcel.writerSheet();
    }
    // -------------------------------- sheet end
    // -------------------------------- table start
    public static WriteTable buildTable(Integer tableNo) {
        return tableBuilder(tableNo).build();
    }
    public static WriteTable buildTable() {
        return tableBuilder().build();
    }
    public static ExcelWriterTableBuilder tableBuilder(Integer tableNo) {
        return FastExcel.writerTable(tableNo);
    }
    public static ExcelWriterTableBuilder tableBuilder() {
        return FastExcel.writerTable();
    }
    // -------------------------------- table end
}
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-idempotent/.flattened-pom.xml
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,32 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <modelVersion>4.0.0</modelVersion>
  <parent>
    <groupId>org.dromara</groupId>
    <artifactId>ruoyi-common</artifactId>
    <version>5.5.3</version>
  </parent>
  <groupId>org.dromara</groupId>
  <artifactId>ruoyi-common-idempotent</artifactId>
  <version>5.5.3</version>
  <description>ruoyi-common-idempotent å¹‚等功能</description>
  <dependencies>
    <dependency>
      <groupId>org.dromara</groupId>
      <artifactId>ruoyi-common-json</artifactId>
    </dependency>
    <dependency>
      <groupId>org.dromara</groupId>
      <artifactId>ruoyi-common-redis</artifactId>
    </dependency>
    <dependency>
      <groupId>cn.hutool</groupId>
      <artifactId>hutool-crypto</artifactId>
    </dependency>
    <dependency>
      <groupId>cn.dev33</groupId>
      <artifactId>sa-token-core</artifactId>
    </dependency>
  </dependencies>
</project>
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-idempotent/pom.xml
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,41 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <groupId>org.dromara</groupId>
        <artifactId>ruoyi-common</artifactId>
        <version>${revision}</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>
    <artifactId>ruoyi-common-idempotent</artifactId>
    <description>
        ruoyi-common-idempotent å¹‚等功能
    </description>
    <dependencies>
        <dependency>
            <groupId>org.dromara</groupId>
            <artifactId>ruoyi-common-json</artifactId>
        </dependency>
        <dependency>
            <groupId>org.dromara</groupId>
            <artifactId>ruoyi-common-redis</artifactId>
        </dependency>
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-crypto</artifactId>
        </dependency>
        <dependency>
            <groupId>cn.dev33</groupId>
            <artifactId>sa-token-core</artifactId>
        </dependency>
    </dependencies>
</project>
RuoYi-Vue-Plus/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-Vue-Plus/ruoyi-common/ruoyi-common-idempotent/src/main/java/org/dromara/common/idempotent/aspectj/RepeatSubmitAspect.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,146 @@
package org.dromara.common.idempotent.aspectj;
import cn.dev33.satoken.SaManager;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.crypto.SecureUtil;
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.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 org.springframework.validation.BindingResult;
import org.springframework.web.multipart.MultipartFile;
import java.time.Duration;
import java.util.Collection;
import java.util.Map;
import java.util.StringJoiner;
/**
 * é˜²æ­¢é‡å¤æäº¤(参考美团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 = 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;
        if (RedisUtils.setObjectIfAbsent(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) {
        StringJoiner params = new StringJoiner(" ");
        if (ArrayUtil.isEmpty(paramsArray)) {
            return params.toString();
        }
        for (Object o : paramsArray) {
            if (ObjectUtil.isNotNull(o) && !isFilterObject(o)) {
                params.add(JsonUtils.toJsonString(o));
            }
        }
        return params.toString();
    }
    /**
     * åˆ¤æ–­æ˜¯å¦éœ€è¦è¿‡æ»¤çš„对象。
     *
     * @param o å¯¹è±¡ä¿¡æ¯ã€‚
     * @return å¦‚果是需要过滤的对象,则返回true;否则返回false。
     */
    @SuppressWarnings("rawtypes")
    public boolean isFilterObject(final Object o) {
        Class<?> clazz = o.getClass();
        if (clazz.isArray()) {
            return MultipartFile.class.isAssignableFrom(clazz.getComponentType());
        } 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.values()) {
                return value instanceof MultipartFile;
            }
        }
        return o instanceof MultipartFile || o instanceof HttpServletRequest || o instanceof HttpServletResponse
               || o instanceof BindingResult;
    }
}
RuoYi-Vue-Plus/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-Vue-Plus/ruoyi-common/ruoyi-common-idempotent/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1 @@
org.dromara.common.idempotent.config.IdempotentConfig
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-job/.flattened-pom.xml
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,36 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <modelVersion>4.0.0</modelVersion>
  <parent>
    <groupId>org.dromara</groupId>
    <artifactId>ruoyi-common</artifactId>
    <version>5.5.3</version>
  </parent>
  <groupId>org.dromara</groupId>
  <artifactId>ruoyi-common-job</artifactId>
  <version>5.5.3</version>
  <description>ruoyi-common-job å®šæ—¶ä»»åŠ¡</description>
  <dependencies>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-autoconfigure</artifactId>
    </dependency>
    <dependency>
      <groupId>com.aizuda</groupId>
      <artifactId>snail-job-client-starter</artifactId>
    </dependency>
    <dependency>
      <groupId>com.aizuda</groupId>
      <artifactId>snail-job-client-job-core</artifactId>
    </dependency>
    <dependency>
      <groupId>org.projectlombok</groupId>
      <artifactId>lombok</artifactId>
    </dependency>
    <dependency>
      <groupId>org.dromara</groupId>
      <artifactId>ruoyi-common-core</artifactId>
    </dependency>
  </dependencies>
</project>
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-job/pom.xml
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,46 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         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>org.dromara</groupId>
        <artifactId>ruoyi-common</artifactId>
        <version>${revision}</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>
    <artifactId>ruoyi-common-job</artifactId>
    <description>
        ruoyi-common-job å®šæ—¶ä»»åŠ¡
    </description>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-autoconfigure</artifactId>
        </dependency>
        <!-- SnailJob client -->
        <dependency>
            <groupId>com.aizuda</groupId>
            <artifactId>snail-job-client-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>com.aizuda</groupId>
            <artifactId>snail-job-client-job-core</artifactId>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
        <dependency>
            <groupId>org.dromara</groupId>
            <artifactId>ruoyi-common-core</artifactId>
        </dependency>
    </dependencies>
</project>
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-job/src/main/java/org/dromara/common/job/config/SnailJobConfig.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,37 @@
package org.dromara.common.job.config;
import ch.qos.logback.classic.Logger;
import ch.qos.logback.classic.LoggerContext;
import ch.qos.logback.classic.spi.ILoggingEvent;
import com.aizuda.snailjob.client.common.appender.SnailLogbackAppender;
import com.aizuda.snailjob.client.common.event.SnailClientStartingEvent;
import com.aizuda.snailjob.client.starter.EnableSnailJob;
import org.slf4j.LoggerFactory;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.event.EventListener;
import org.springframework.scheduling.annotation.EnableScheduling;
/**
 * å¯åŠ¨å®šæ—¶ä»»åŠ¡
 *
 * @author opensnail
 * @date 2024-05-17
 */
@AutoConfiguration
@ConditionalOnProperty(prefix = "snail-job", name = "enabled", havingValue = "true")
@EnableScheduling
@EnableSnailJob
public class SnailJobConfig {
    @EventListener(SnailClientStartingEvent.class)
    public void onStarting(SnailClientStartingEvent event) {
        LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory();
        SnailLogbackAppender<ILoggingEvent> ca = new SnailLogbackAppender<>();
        ca.setName("snail_log_appender");
        ca.start();
        Logger rootLogger = lc.getLogger(Logger.ROOT_LOGGER_NAME);
        rootLogger.addAppender(ca);
    }
}
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-job/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1 @@
org.dromara.common.job.config.SnailJobConfig
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-json/.flattened-pom.xml
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,28 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <modelVersion>4.0.0</modelVersion>
  <parent>
    <groupId>org.dromara</groupId>
    <artifactId>ruoyi-common</artifactId>
    <version>5.5.3</version>
  </parent>
  <groupId>org.dromara</groupId>
  <artifactId>ruoyi-common-json</artifactId>
  <version>5.5.3</version>
  <description>ruoyi-common-json åºåˆ—化模块</description>
  <dependencies>
    <dependency>
      <groupId>org.dromara</groupId>
      <artifactId>ruoyi-common-core</artifactId>
    </dependency>
    <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-databind</artifactId>
    </dependency>
    <dependency>
      <groupId>com.fasterxml.jackson.datatype</groupId>
      <artifactId>jackson-datatype-jsr310</artifactId>
    </dependency>
  </dependencies>
</project>
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-json/pom.xml
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,37 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <groupId>org.dromara</groupId>
        <artifactId>ruoyi-common</artifactId>
        <version>${revision}</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>
    <artifactId>ruoyi-common-json</artifactId>
    <description>
        ruoyi-common-json åºåˆ—化模块
    </description>
    <dependencies>
        <dependency>
            <groupId>org.dromara</groupId>
            <artifactId>ruoyi-common-core</artifactId>
        </dependency>
        <!-- JSON工具类 -->
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.datatype</groupId>
            <artifactId>jackson-datatype-jsr310</artifactId>
        </dependency>
    </dependencies>
</project>
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-json/src/main/java/org/dromara/common/json/config/JacksonConfig.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,55 @@
package org.dromara.common.json.config;
import com.fasterxml.jackson.databind.Module;
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 lombok.extern.slf4j.Slf4j;
import org.dromara.common.json.handler.BigNumberSerializer;
import org.dromara.common.json.handler.CustomDateDeserializer;
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.Date;
import java.util.TimeZone;
/**
 * jackson é…ç½®
 *
 * @author Lion Li
 */
@Slf4j
@AutoConfiguration(before = JacksonAutoConfiguration.class)
public class JacksonConfig {
    @Bean
    public Module registerJavaTimeModule() {
        // å…¨å±€é…ç½®åºåˆ—化返回 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));
        javaTimeModule.addDeserializer(Date.class, new CustomDateDeserializer());
        return javaTimeModule;
    }
    @Bean
    public Jackson2ObjectMapperBuilderCustomizer customizer() {
        return builder -> {
            builder.timeZone(TimeZone.getDefault());
            log.info("初始化 jackson é…ç½®");
        };
    }
}
RuoYi-Vue-Plus/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-Vue-Plus/ruoyi-common/ruoyi-common-json/src/main/java/org/dromara/common/json/handler/CustomDateDeserializer.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,37 @@
package org.dromara.common.json.handler;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import org.dromara.common.core.utils.ObjectUtils;
import java.io.IOException;
import java.util.Date;
/**
 * è‡ªå®šä¹‰ Date ç±»åž‹ååºåˆ—化处理器(支持多种格式)
 *
 * @author AprilWind
 */
public class CustomDateDeserializer extends JsonDeserializer<Date> {
    /**
     * ååºåˆ—化逻辑:将字符串转换为 Date å¯¹è±¡
     *
     * @param p    JSON è§£æžå™¨ï¼Œç”¨äºŽèŽ·å–å­—ç¬¦ä¸²å€¼
     * @param ctxt ä¸Šä¸‹æ–‡çŽ¯å¢ƒï¼ˆå¯ç”¨äºŽèŽ·å–æ›´å¤šé…ç½®ï¼‰
     * @return è½¬æ¢åŽçš„ Date å¯¹è±¡ï¼Œè‹¥ä¸ºç©ºå­—符串返回 null
     * @throws IOException å½“字符串格式非法或转换失败时抛出
     */
    @Override
    public Date deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
        DateTime parse = DateUtil.parse(p.getText());
        if (ObjectUtils.isNull(parse)) {
            return null;
        }
        return parse.toJdkDate();
    }
}
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-json/src/main/java/org/dromara/common/json/utils/JsonUtils.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,225 @@
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.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.exc.MismatchedInputException;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import org.dromara.common.core.utils.SpringUtils;
import org.dromara.common.core.utils.StringUtils;
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;
    }
    /**
     * å°†å¯¹è±¡è½¬æ¢ä¸ºJSON格式的字符串
     *
     * @param object è¦è½¬æ¢çš„对象
     * @return JSON格式的字符串,如果对象为null,则返回null
     * @throws RuntimeException å¦‚果转换过程中发生JSON处理异常,则抛出运行时异常
     */
    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);
        }
    }
    /**
     * å°†JSON格式的字符串转换为指定类型的对象
     *
     * @param text  JSON格式的字符串
     * @param clazz è¦è½¬æ¢çš„目标对象类型
     * @param <T>   ç›®æ ‡å¯¹è±¡çš„æ³›åž‹ç±»åž‹
     * @return è½¬æ¢åŽçš„对象,如果字符串为空则返回null
     * @throws RuntimeException å¦‚果转换过程中发生IO异常,则抛出运行时异常
     */
    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);
        }
    }
    /**
     * å°†å­—节数组转换为指定类型的对象
     *
     * @param bytes å­—节数组
     * @param clazz è¦è½¬æ¢çš„目标对象类型
     * @param <T>   ç›®æ ‡å¯¹è±¡çš„æ³›åž‹ç±»åž‹
     * @return è½¬æ¢åŽçš„对象,如果字节数组为空则返回null
     * @throws RuntimeException å¦‚果转换过程中发生IO异常,则抛出运行时异常
     */
    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);
        }
    }
    /**
     * å°†JSON格式的字符串转换为指定类型的对象,支持复杂类型
     *
     * @param text          JSON格式的字符串
     * @param typeReference æŒ‡å®šç±»åž‹çš„TypeReference对象
     * @param <T>           ç›®æ ‡å¯¹è±¡çš„æ³›åž‹ç±»åž‹
     * @return è½¬æ¢åŽçš„对象,如果字符串为空则返回null
     * @throws RuntimeException å¦‚果转换过程中发生IO异常,则抛出运行时异常
     */
    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);
        }
    }
    /**
     * å°†JSON格式的字符串转换为Dict对象
     *
     * @param text JSON格式的字符串
     * @return è½¬æ¢åŽçš„Dict对象,如果字符串为空或者不是JSON格式则返回null
     * @throws RuntimeException å¦‚果转换过程中发生IO异常,则抛出运行时异常
     */
    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);
        }
    }
    /**
     * å°†JSON格式的字符串转换为Dict对象的列表
     *
     * @param text JSON格式的字符串
     * @return è½¬æ¢åŽçš„Dict对象的列表,如果字符串为空则返回null
     * @throws RuntimeException å¦‚果转换过程中发生IO异常,则抛出运行时异常
     */
    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);
        }
    }
    /**
     * å°†JSON格式的字符串转换为指定类型对象的列表
     *
     * @param text  JSON格式的字符串
     * @param clazz è¦è½¬æ¢çš„目标对象类型
     * @param <T>   ç›®æ ‡å¯¹è±¡çš„æ³›åž‹ç±»åž‹
     * @return è½¬æ¢åŽçš„对象的列表,如果字符串为空则返回空列表
     * @throws RuntimeException å¦‚果转换过程中发生IO异常,则抛出运行时异常
     */
    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);
        }
    }
    /**
     * åˆ¤æ–­å­—符串是否为合法 JSON(对象或数组)
     *
     * @param str å¾…校验字符串
     * @return true = åˆæ³• JSON,false = éžæ³•或空
     */
    public static boolean isJson(String str) {
        if (StringUtils.isBlank(str)) {
            return false;
        }
        try {
            OBJECT_MAPPER.readTree(str);
            return true;
        } catch (Exception e) {
            return false;
        }
    }
    /**
     * åˆ¤æ–­å­—符串是否为 JSON å¯¹è±¡ï¼ˆ{})
     *
     * @param str å¾…校验字符串
     * @return true = JSON å¯¹è±¡
     */
    public static boolean isJsonObject(String str) {
        if (StringUtils.isBlank(str)) {
            return false;
        }
        try {
            JsonNode node = OBJECT_MAPPER.readTree(str);
            return node.isObject();
        } catch (Exception e) {
            return false;
        }
    }
    /**
     * åˆ¤æ–­å­—符串是否为 JSON æ•°ç»„([])
     *
     * @param str å¾…校验字符串
     * @return true = JSON æ•°ç»„
     */
    public static boolean isJsonArray(String str) {
        if (StringUtils.isBlank(str)) {
            return false;
        }
        try {
            JsonNode node = OBJECT_MAPPER.readTree(str);
            return node.isArray();
        } catch (Exception e) {
            return false;
        }
    }
}
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-json/src/main/java/org/dromara/common/json/validate/JsonPattern.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,33 @@
package org.dromara.common.json.validate;
import jakarta.validation.Constraint;
import jakarta.validation.Payload;
import java.lang.annotation.*;
/**
 * JSON æ ¼å¼æ ¡éªŒæ³¨è§£
 *
 * @author AprilWind
 */
@Documented
@Target({ElementType.METHOD, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = JsonPatternValidator.class)
public @interface JsonPattern {
    /**
     * é™åˆ¶ JSON ç±»åž‹ï¼Œé»˜è®¤ä¸º {@link JsonType#ANY},即对象或数组都允许
     */
    JsonType type() default JsonType.ANY;
    /**
     * æ ¡éªŒå¤±è´¥æ—¶çš„æç¤ºæ¶ˆæ¯
     */
    String message() default "不是有效的 JSON æ ¼å¼";
    Class<?>[] groups() default {};
    Class<? extends Payload>[] payload() default {};
}
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-json/src/main/java/org/dromara/common/json/validate/JsonPatternValidator.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,51 @@
package org.dromara.common.json.validate;
import jakarta.validation.ConstraintValidator;
import jakarta.validation.ConstraintValidatorContext;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.json.utils.JsonUtils;
/**
 * JSON æ ¼å¼æ ¡éªŒå™¨
 *
 * @author AprilWind
 */
public class JsonPatternValidator implements ConstraintValidator<JsonPattern, String> {
    /**
     * æ³¨è§£ä¸­æŒ‡å®šçš„ JSON ç±»åž‹æžšä¸¾
     */
    private JsonType jsonType;
    /**
     * åˆå§‹åŒ–校验器,从注解中提取 JSON ç±»åž‹
     *
     * @param annotation æ³¨è§£å®žä¾‹
     */
    @Override
    public void initialize(JsonPattern annotation) {
        this.jsonType = annotation.type();
    }
    /**
     * æ ¡éªŒå­—符串是否为合法 JSON
     *
     * @param value   å¾…校验字符串
     * @param context æ ¡éªŒä¸Šä¸‹æ–‡ï¼Œå¯ç”¨äºŽè‡ªå®šä¹‰é”™è¯¯ä¿¡æ¯
     * @return true = åˆæ³• JSON æˆ–为空,false = éžæ³• JSON
     */
    @Override
    public boolean isValid(String value, ConstraintValidatorContext context) {
        if (StringUtils.isBlank(value)) {
            // äº¤ç»™ @NotBlank æˆ– @NotNull æŽ§åˆ¶æ˜¯å¦å…è®¸ä¸ºç©º
            return true;
        }
        // æ ¹æ® JSON ç±»åž‹è¿›è¡Œä¸åŒçš„æ ¡éªŒ
        return switch (jsonType) {
            case ANY -> JsonUtils.isJson(value);
            case OBJECT -> JsonUtils.isJsonObject(value);
            case ARRAY -> JsonUtils.isJsonArray(value);
        };
    }
}
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-json/src/main/java/org/dromara/common/json/validate/JsonType.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,30 @@
package org.dromara.common.json.validate;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
 * JSON ç±»åž‹æžšä¸¾
 *
 * @author AprilWind
 */
@Getter
@AllArgsConstructor
public enum JsonType {
    /**
     * JSON å¯¹è±¡ï¼Œä¾‹å¦‚ {"a":1}
     */
    OBJECT,
    /**
     * JSON æ•°ç»„,例如 [1,2,3]
     */
    ARRAY,
    /**
     * ä»»æ„ JSON ç±»åž‹ï¼Œå¯¹è±¡æˆ–数组都可以
     */
    ANY
}
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-json/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1 @@
org.dromara.common.json.config.JacksonConfig
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-log/.flattened-pom.xml
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,24 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <modelVersion>4.0.0</modelVersion>
  <parent>
    <groupId>org.dromara</groupId>
    <artifactId>ruoyi-common</artifactId>
    <version>5.5.3</version>
  </parent>
  <groupId>org.dromara</groupId>
  <artifactId>ruoyi-common-log</artifactId>
  <version>5.5.3</version>
  <description>ruoyi-common-log æ—¥å¿—记录</description>
  <dependencies>
    <dependency>
      <groupId>org.dromara</groupId>
      <artifactId>ruoyi-common-satoken</artifactId>
    </dependency>
    <dependency>
      <groupId>org.dromara</groupId>
      <artifactId>ruoyi-common-json</artifactId>
    </dependency>
  </dependencies>
</project>
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-log/pom.xml
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,32 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <groupId>org.dromara</groupId>
        <artifactId>ruoyi-common</artifactId>
        <version>${revision}</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>
    <artifactId>ruoyi-common-log</artifactId>
    <description>
        ruoyi-common-log æ—¥å¿—记录
    </description>
    <dependencies>
        <dependency>
            <groupId>org.dromara</groupId>
            <artifactId>ruoyi-common-satoken</artifactId>
        </dependency>
        <dependency>
            <groupId>org.dromara</groupId>
            <artifactId>ruoyi-common-json</artifactId>
        </dependency>
    </dependencies>
</project>
RuoYi-Vue-Plus/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-Vue-Plus/ruoyi-common/ruoyi-common-log/src/main/java/org/dromara/common/log/aspect/LogAspect.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,226 @@
package org.dromara.common.log.aspect;
import cn.hutool.core.lang.Dict;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.ObjectUtil;
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.dromara.common.core.constant.SystemConstants;
import org.dromara.common.core.domain.model.LoginUser;
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 org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.http.HttpMethod;
import org.springframework.validation.BindingResult;
import org.springframework.web.multipart.MultipartFile;
import java.util.*;
/**
 * æ“ä½œæ—¥å¿—记录处理
 *
 * @author Lion Li
 */
@Slf4j
@Aspect
@AutoConfiguration
public class LogAspect {
    /**
     * è®¡æ—¶ key
     */
    private static final ThreadLocal<StopWatch> KEY_CACHE = new ThreadLocal<>();
    /**
     * å¤„理请求前执行
     */
    @Before(value = "@annotation(controllerLog)")
    public void doBefore(JoinPoint joinPoint, Log controllerLog) {
        StopWatch stopWatch = new StopWatch();
        KEY_CACHE.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));
            LoginUser loginUser = LoginHelper.getLoginUser();
            operLog.setOperName(loginUser.getUsername());
            operLog.setDeptName(loginUser.getDeptName());
            if (e != null) {
                operLog.setStatus(BusinessStatus.FAIL.ordinal());
                operLog.setErrorMsg(StringUtils.substring(e.getMessage(), 0, 3800));
            }
            // è®¾ç½®æ–¹æ³•名称
            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 = KEY_CACHE.get();
            stopWatch.stop();
            operLog.setCostTime(stopWatch.getDuration().toMillis());
            // å‘布事件保存数据库
            SpringUtils.context().publishEvent(operLog);
        } catch (Exception exp) {
            // è®°å½•本地异常日志
            log.error("异常信息:{}", exp.getMessage());
        } finally {
            KEY_CACHE.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, 3800));
        }
    }
    /**
     * èŽ·å–è¯·æ±‚çš„å‚æ•°ï¼Œæ”¾åˆ°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) && StringUtils.equalsAny(requestMethod, HttpMethod.PUT.name(), HttpMethod.POST.name(), HttpMethod.DELETE.name())) {
            String params = argsArrayToString(joinPoint.getArgs(), excludeParamNames);
            operLog.setOperParam(StringUtils.substring(params, 0, 3800));
        } else {
            MapUtil.removeAny(paramsMap, SystemConstants.EXCLUDE_PROPERTIES);
            MapUtil.removeAny(paramsMap, excludeParamNames);
            operLog.setOperParam(StringUtils.substring(JsonUtils.toJsonString(paramsMap), 0, 3800));
        }
    }
    /**
     * å‚数拼装
     */
    private String argsArrayToString(Object[] paramsArray, String[] excludeParamNames) {
        StringJoiner params = new StringJoiner(" ");
        if (ArrayUtil.isEmpty(paramsArray)) {
            return params.toString();
        }
        String[] exclude = ArrayUtil.addAll(excludeParamNames, SystemConstants.EXCLUDE_PROPERTIES);
        for (Object o : paramsArray) {
            if (ObjectUtil.isNotNull(o) && !isFilterObject(o)) {
                String str = "";
                if (o instanceof List<?> list) {
                    List<Dict> list1 = new ArrayList<>();
                    for (Object obj : list) {
                        String str1 = JsonUtils.toJsonString(obj);
                        Dict dict = JsonUtils.parseMap(str1);
                        if (MapUtil.isNotEmpty(dict)) {
                            MapUtil.removeAny(dict, exclude);
                            list1.add(dict);
                        }
                    }
                    str = JsonUtils.toJsonString(list1);
                } else {
                    str = JsonUtils.toJsonString(o);
                    Dict dict = JsonUtils.parseMap(str);
                    if (MapUtil.isNotEmpty(dict)) {
                        MapUtil.removeAny(dict, exclude);
                        str = JsonUtils.toJsonString(dict);
                    }
                }
                params.add(str);
            }
        }
        return params.toString();
    }
    /**
     * åˆ¤æ–­æ˜¯å¦éœ€è¦è¿‡æ»¤çš„对象。
     *
     * @param o å¯¹è±¡ä¿¡æ¯ã€‚
     * @return å¦‚果是需要过滤的对象,则返回true;否则返回false。
     */
    @SuppressWarnings("rawtypes")
    public boolean isFilterObject(final Object o) {
        Class<?> clazz = o.getClass();
        if (clazz.isArray()) {
            return MultipartFile.class.isAssignableFrom(clazz.getComponentType());
        } 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.values()) {
                return value instanceof MultipartFile;
            }
        }
        return o instanceof MultipartFile || o instanceof HttpServletRequest || o instanceof HttpServletResponse
               || o instanceof BindingResult;
    }
}
RuoYi-Vue-Plus/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-Vue-Plus/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-Vue-Plus/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-Vue-Plus/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-Vue-Plus/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-Vue-Plus/ruoyi-common/ruoyi-common-log/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1 @@
org.dromara.common.log.aspect.LogAspect
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-mail/.flattened-pom.xml
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,28 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <modelVersion>4.0.0</modelVersion>
  <parent>
    <groupId>org.dromara</groupId>
    <artifactId>ruoyi-common</artifactId>
    <version>5.5.3</version>
  </parent>
  <groupId>org.dromara</groupId>
  <artifactId>ruoyi-common-mail</artifactId>
  <version>5.5.3</version>
  <description>ruoyi-common-mail é‚®ä»¶æ¨¡å—</description>
  <dependencies>
    <dependency>
      <groupId>org.dromara</groupId>
      <artifactId>ruoyi-common-core</artifactId>
    </dependency>
    <dependency>
      <groupId>jakarta.mail</groupId>
      <artifactId>jakarta.mail-api</artifactId>
    </dependency>
    <dependency>
      <groupId>org.eclipse.angus</groupId>
      <artifactId>jakarta.mail</artifactId>
    </dependency>
  </dependencies>
</project>
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-mail/pom.xml
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,34 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <groupId>org.dromara</groupId>
        <artifactId>ruoyi-common</artifactId>
        <version>${revision}</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>
    <artifactId>ruoyi-common-mail</artifactId>
    <description>
        ruoyi-common-mail é‚®ä»¶æ¨¡å—
    </description>
    <dependencies>
        <dependency>
            <groupId>org.dromara</groupId>
            <artifactId>ruoyi-common-core</artifactId>
        </dependency>
        <dependency>
            <groupId>jakarta.mail</groupId>
            <artifactId>jakarta.mail-api</artifactId>
        </dependency>
        <dependency>
            <groupId>org.eclipse.angus</groupId>
            <artifactId>jakarta.mail</artifactId>
        </dependency>
    </dependencies>
</project>
RuoYi-Vue-Plus/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-Vue-Plus/ruoyi-common/ruoyi-common-mail/src/main/java/org/dromara/common/mail/config/properties/MailProperties.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,75 @@
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标准<br>
     * å‘件人可以是以下形式:
     *
     * <pre>
     * 1. user@xxx.xx
     * 2.  name &lt;user@xxx.xx&gt;
     * </pre>
     */
    private String from;
    /**
     * ä½¿ç”¨ STARTTLS安全连接,STARTTLS是对纯文本通信协议的扩展。它将纯文本连接升级为加密连接(TLS或SSL), è€Œä¸æ˜¯ä½¿ç”¨ä¸€ä¸ªå•独的加密通信端口。
     */
    private Boolean starttlsEnable;
    /**
     * ä½¿ç”¨ SSL安全连接
     */
    private Boolean sslEnable;
    /**
     * SMTP超时时长,单位毫秒,缺省值不超时
     */
    private Long timeout;
    /**
     * Socket连接超时值,单位毫秒,缺省值不超时
     */
    private Long connectionTimeout;
}
RuoYi-Vue-Plus/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.JakartaMail;
import cn.hutool.extra.mail.JakartaUserPassAuthenticator;
import cn.hutool.extra.mail.MailAccount;
import jakarta.mail.Authenticator;
import jakarta.mail.Session;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import org.dromara.common.core.utils.SpringUtils;
import org.dromara.common.core.utils.StringUtils;
import java.io.File;
import java.io.InputStream;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
/**
 * é‚®ä»¶å·¥å…·ç±»
 */
@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 JakartaUserPassAuthenticator(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 JakartaMail mail = JakartaMail.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 (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-Vue-Plus/ruoyi-common/ruoyi-common-mail/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1 @@
org.dromara.common.mail.config.MailConfig
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-mybatis/.flattened-pom.xml
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,40 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <modelVersion>4.0.0</modelVersion>
  <parent>
    <groupId>org.dromara</groupId>
    <artifactId>ruoyi-common</artifactId>
    <version>5.5.3</version>
  </parent>
  <groupId>org.dromara</groupId>
  <artifactId>ruoyi-common-mybatis</artifactId>
  <version>5.5.3</version>
  <description>ruoyi-common-mybatis æ•°æ®åº“服务</description>
  <dependencies>
    <dependency>
      <groupId>org.dromara</groupId>
      <artifactId>ruoyi-common-core</artifactId>
    </dependency>
    <dependency>
      <groupId>org.dromara</groupId>
      <artifactId>ruoyi-common-satoken</artifactId>
    </dependency>
    <dependency>
      <groupId>com.baomidou</groupId>
      <artifactId>dynamic-datasource-spring-boot3-starter</artifactId>
    </dependency>
    <dependency>
      <groupId>com.baomidou</groupId>
      <artifactId>mybatis-plus-spring-boot3-starter</artifactId>
    </dependency>
    <dependency>
      <groupId>com.baomidou</groupId>
      <artifactId>mybatis-plus-jsqlparser</artifactId>
    </dependency>
    <dependency>
      <groupId>p6spy</groupId>
      <artifactId>p6spy</artifactId>
    </dependency>
  </dependencies>
</project>
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-mybatis/pom.xml
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,52 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <groupId>org.dromara</groupId>
        <artifactId>ruoyi-common</artifactId>
        <version>${revision}</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>
    <artifactId>ruoyi-common-mybatis</artifactId>
    <description>
        ruoyi-common-mybatis æ•°æ®åº“服务
    </description>
    <dependencies>
        <dependency>
            <groupId>org.dromara</groupId>
            <artifactId>ruoyi-common-core</artifactId>
        </dependency>
        <dependency>
            <groupId>org.dromara</groupId>
            <artifactId>ruoyi-common-satoken</artifactId>
        </dependency>
        <!-- dynamic-datasource å¤šæ•°æ®æº-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>dynamic-datasource-spring-boot3-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-spring-boot3-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-jsqlparser</artifactId>
        </dependency>
        <!-- sql性能分析插件 -->
        <dependency>
            <groupId>p6spy</groupId>
            <artifactId>p6spy</artifactId>
        </dependency>
    </dependencies>
</project>
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/annotation/DataColumn.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,40 @@
package org.dromara.common.mybatis.annotation;
import java.lang.annotation.*;
/**
 * æ•°æ®æƒé™æ³¨è§£ï¼Œç”¨äºŽæ ‡è®°æ•°æ®æƒé™çš„占位符关键字和替换值
 * <p>
 * ä¸€ä¸ªæ³¨è§£åªèƒ½å¯¹åº”一个模板
 * </p>
 *
 * @author Lion Li
 * @version 3.5.0
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DataColumn {
    /**
     * æ•°æ®æƒé™æ¨¡æ¿çš„占位符关键字,默认为 "deptName"
     *
     * @return å ä½ç¬¦å…³é”®å­—数组
     */
    String[] key() default "deptName";
    /**
     * æ•°æ®æƒé™æ¨¡æ¿çš„占位符替换值,默认为 "dept_id"
     *
     * @return å ä½ç¬¦æ›¿æ¢å€¼æ•°ç»„
     */
    String[] value() default "dept_id";
    /**
     * æƒé™æ ‡è¯†ç¬¦ ç”¨äºŽé€šè¿‡èœå•权限标识符来获取数据权限
     * æ‹¥æœ‰æ­¤æ ‡è¯†ç¬¦çš„角色 å°†ä¸ä¼šæ‹¼æŽ¥æ­¤è§’色的数据过滤sql
     *
     * @return æƒé™æ ‡è¯†ç¬¦
     */
    String permission() default "";
}
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/annotation/DataPermission.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,30 @@
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 {
    /**
     * æ•°æ®æƒé™é…ç½®æ•°ç»„,用于指定数据权限的占位符关键字和替换值
     *
     * @return æ•°æ®æƒé™é…ç½®æ•°ç»„
     */
    DataColumn[] value();
    /**
     * æƒé™æ‹¼æŽ¥æ ‡è¯†ç¬¦(用于指定连接语句的sql符号)
     * å¦‚不填 é»˜è®¤ select ç”¨ OR å…¶ä»–语句用 AND
     * å†…容 OR æˆ–者 AND
     */
    String joinStr() default "";
}
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/aspect/DataPermissionAdvice.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,54 @@
package org.dromara.common.mybatis.aspect;
import lombok.extern.slf4j.Slf4j;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.dromara.common.mybatis.annotation.DataPermission;
import org.dromara.common.mybatis.helper.DataPermissionHelper;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
 * æ•°æ®æƒé™æ³¨è§£Advice
 *
 * @author ç§‹è¾žæœªå¯’
 */
@Slf4j
public class DataPermissionAdvice implements MethodInterceptor {
    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
        Object target = invocation.getThis();
        Method method = invocation.getMethod();
        Object[] args = invocation.getArguments();
        // è®¾ç½®æƒé™æ³¨è§£
        DataPermissionHelper.setPermission(getDataPermissionAnnotation(target, method, args));
        try {
            // æ‰§è¡Œä»£ç†æ–¹æ³•
            return invocation.proceed();
        } finally {
            // æ¸…除权限注解
            DataPermissionHelper.removePermission();
        }
    }
    /**
     * èŽ·å–æ•°æ®æƒé™æ³¨è§£
     */
    private DataPermission getDataPermissionAnnotation(Object target, Method method,Object[] args){
        DataPermission dataPermission = method.getAnnotation(DataPermission.class);
        // ä¼˜å…ˆèŽ·å–æ–¹æ³•ä¸Šçš„æ³¨è§£
        if (dataPermission != null) {
            return dataPermission;
        }
        // æ–¹æ³•上没有注解,则获取类上的注解
        Class<?> targetClass = target.getClass();
        // å¦‚果是 JDK åŠ¨æ€ä»£ç†ï¼Œåˆ™èŽ·å–çœŸå®žçš„Class实例
        if (Proxy.isProxyClass(targetClass)) {
            targetClass = targetClass.getInterfaces()[0];
        }
        dataPermission = targetClass.getAnnotation(DataPermission.class);
        return dataPermission;
    }
}
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/aspect/DataPermissionPointcut.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,39 @@
package org.dromara.common.mybatis.aspect;
import lombok.extern.slf4j.Slf4j;
import org.dromara.common.mybatis.annotation.DataPermission;
import org.springframework.aop.support.StaticMethodMatcherPointcut;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
 * æ•°æ®æƒé™åŒ¹é…åˆ‡ç‚¹
 *
 * @author ç§‹è¾žæœªå¯’
 */
@Slf4j
@SuppressWarnings("all")
public class DataPermissionPointcut extends StaticMethodMatcherPointcut {
    @Override
    public boolean matches(Method method, Class<?> targetClass) {
        // ä¼˜å…ˆåŒ¹é…æ–¹æ³•
        // æ•°æ®æƒé™æ³¨è§£ä¸å¯¹ç»§æ‰¿ç”Ÿæ•ˆï¼Œæ‰€ä»¥æ£€æŸ¥å½“前方法是否有注解即可,不再往上匹配父类或接口
        if (method.isAnnotationPresent(DataPermission.class)) {
            return true;
        }
        // MyBatis çš„ Mapper å°±æ˜¯é€šè¿‡ JDK åŠ¨æ€ä»£ç†å®žçŽ°çš„ï¼Œæ‰€ä»¥è¿™é‡Œéœ€è¦æ£€æŸ¥æ˜¯å¦åŒ¹é… JDK çš„动态代理
        Class<?> targetClassRef = targetClass;
        if (Proxy.isProxyClass(targetClassRef)) {
            // æ•°æ®æƒé™æ³¨è§£ä¸å¯¹ç»§æ‰¿ç”Ÿæ•ˆï¼Œä½†ç”±äºŽ SpringIOC å®¹å™¨æ‹¿åˆ°çš„实际上是 MyBatis ä»£ç†è¿‡åŽçš„ Mapper,而 targetClass.isAnnotationPresent å®žé™…匹配的是 Proxy ç±»çš„æ³¨è§£ï¼Œä¸ä¼šæŸ¥æ‰¾ä»£ç†ç±»ã€‚
            // æ‰€ä»¥è¿™é‡Œä¸èƒ½ç”¨ targetClass.isAnnotationPresent,只能用 AnnotatedElementUtils.hasAnnotation æˆ– targetClass.getInterfaces()[0].isAnnotationPresent åŽ»åšåŒ¹é…ï¼Œä»¥æ£€æŸ¥è¢«ä»£ç†çš„ MapperClass æ˜¯å¦å…·æœ‰æ³¨è§£
            // åŽŸç†ï¼šJDK åŠ¨æ€ä»£ç†æœ¬è´¨ä¸Šå°±æ˜¯å¯¹æŽ¥å£è¿›è¡Œå®žçŽ°ç„¶åŽå¯¹å…·ä½“çš„æŽ¥å£å®žçŽ°åšä»£ç†ï¼Œæ‰€ä»¥ç›´æŽ¥é€šè¿‡æŽ¥å£å¯ä»¥æ‹¿åˆ°å®žé™…çš„ MapperClass
            targetClassRef = targetClass.getInterfaces()[0];
        }
        return targetClassRef.isAnnotationPresent(DataPermission.class);
    }
}
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/aspect/DataPermissionPointcutAdvisor.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,33 @@
package org.dromara.common.mybatis.aspect;
import org.aopalliance.aop.Advice;
import org.springframework.aop.Pointcut;
import org.springframework.aop.support.AbstractPointcutAdvisor;
/**
 * æ•°æ®æƒé™æ³¨è§£åˆ‡é¢å®šä¹‰
 *
 * @author ç§‹è¾žæœªå¯’
 */
@SuppressWarnings("all")
public class DataPermissionPointcutAdvisor extends AbstractPointcutAdvisor {
    private final Advice advice;
    private final Pointcut pointcut;
    public DataPermissionPointcutAdvisor() {
        this.advice = new DataPermissionAdvice();
        this.pointcut =  new DataPermissionPointcut();
    }
    @Override
    public Pointcut getPointcut() {
        return this.pointcut;
    }
    @Override
    public Advice getAdvice() {
        return this.advice;
    }
}
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/config/MybatisPlusConfig.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,142 @@
package org.dromara.common.mybatis.config;
import cn.hutool.core.net.NetUtil;
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import com.baomidou.mybatisplus.core.handlers.PostInitTableInfoHandler;
import com.baomidou.mybatisplus.core.incrementer.DefaultIdentifierGenerator;
import com.baomidou.mybatisplus.core.incrementer.IdentifierGenerator;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.TenantLineInnerInterceptor;
import org.dromara.common.core.factory.YmlPropertySourceFactory;
import org.dromara.common.core.utils.SpringUtils;
import org.dromara.common.mybatis.aspect.DataPermissionPointcutAdvisor;
import org.dromara.common.mybatis.handler.InjectionMetaObjectHandler;
import org.dromara.common.mybatis.handler.MybatisExceptionHandler;
import org.dromara.common.mybatis.handler.PlusPostInitTableInfoHandler;
import org.dromara.common.mybatis.interceptor.PlusDataPermissionInterceptor;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.PropertySource;
import org.springframework.context.annotation.Role;
import org.springframework.transaction.annotation.EnableTransactionManagement;
/**
 * mybatis-plus配置类(下方注释有插件介绍)
 *
 * @author Lion Li
 */
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
@EnableTransactionManagement(proxyTargetClass = true)
@MapperScan("${mybatis-plus.mapperPackage}")
@PropertySource(value = "classpath:common-mybatis.yml", factory = YmlPropertySourceFactory.class)
public class MybatisPlusConfig {
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        // å¤šç§Ÿæˆ·æ’ä»¶ å¿…须放到第一位
        try {
            TenantLineInnerInterceptor tenant = SpringUtils.getBean(TenantLineInnerInterceptor.class);
            interceptor.addInnerInterceptor(tenant);
        } catch (BeansException ignore) {
        }
        // æ•°æ®æƒé™å¤„理
        interceptor.addInnerInterceptor(dataPermissionInterceptor());
        // åˆ†é¡µæ’ä»¶
        interceptor.addInnerInterceptor(paginationInnerInterceptor());
        // ä¹è§‚锁插件
        interceptor.addInnerInterceptor(optimisticLockerInnerInterceptor());
        return interceptor;
    }
    /**
     * æ•°æ®æƒé™æ‹¦æˆªå™¨
     */
    public PlusDataPermissionInterceptor dataPermissionInterceptor() {
        return new PlusDataPermissionInterceptor();
    }
    /**
     * æ•°æ®æƒé™åˆ‡é¢å¤„理器
     */
    @Bean
    @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
    public DataPermissionPointcutAdvisor dataPermissionPointcutAdvisor() {
        return new DataPermissionPointcutAdvisor();
    }
    /**
     * åˆ†é¡µæ’件,自动识别数据库类型
     */
    public PaginationInnerInterceptor paginationInnerInterceptor() {
        PaginationInnerInterceptor paginationInnerInterceptor = new PaginationInnerInterceptor();
        // åˆ†é¡µåˆç†åŒ–
        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());
    }
    /**
     * å¼‚常处理器
     */
    @Bean
    public MybatisExceptionHandler mybatisExceptionHandler() {
        return new MybatisExceptionHandler();
    }
    /**
     * åˆå§‹åŒ–表对象处理器
     */
    @Bean
    public PostInitTableInfoHandler postInitTableInfoHandler() {
        return new PlusPostInitTableInfoHandler();
    }
    /**
     * 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-Vue-Plus/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/core/domain/BaseEntity.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,70 @@
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-Vue-Plus/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/core/mapper/BaseMapperPlus.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,334 @@
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.reflect.GenericTypeUtils;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.toolkit.Db;
import org.apache.ibatis.logging.Log;
import org.apache.ibatis.logging.LogFactory;
import org.dromara.common.core.utils.MapstructUtils;
import org.dromara.common.core.utils.StreamUtils;
import java.io.Serializable;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
/**
 * è‡ªå®šä¹‰ 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);
    /**
     * èŽ·å–å½“å‰å®žä¾‹å¯¹è±¡å…³è”çš„æ³›åž‹ç±»åž‹ V çš„ Class å¯¹è±¡
     *
     * @return è¿”回当前实例对象关联的泛型类型 V çš„ Class å¯¹è±¡
     */
    default Class<V> currentVoClass() {
        return (Class<V>) GenericTypeUtils.resolveTypeArguments(this.getClass(), BaseMapperPlus.class)[1];
    }
    /**
     * èŽ·å–å½“å‰å®žä¾‹å¯¹è±¡å…³è”çš„æ³›åž‹ç±»åž‹ T çš„ Class å¯¹è±¡
     *
     * @return è¿”回当前实例对象关联的泛型类型 T çš„ Class å¯¹è±¡
     */
    default Class<T> currentModelClass() {
        return (Class<T>) GenericTypeUtils.resolveTypeArguments(this.getClass(), BaseMapperPlus.class)[0];
    }
    /**
     * ä½¿ç”¨é»˜è®¤çš„æŸ¥è¯¢æ¡ä»¶æŸ¥è¯¢å¹¶è¿”回结果列表
     *
     * @return è¿”回查询结果的列表
     */
    default List<T> selectList() {
        return this.selectList(new QueryWrapper<>());
    }
    /**
     * æ‰¹é‡æ’入实体对象集合
     *
     * @param entityList å®žä½“对象集合
     * @return æ’入操作是否成功的布尔值
     */
    default boolean insertBatch(Collection<T> entityList) {
        return Db.saveBatch(entityList);
    }
    /**
     * æ‰¹é‡æ ¹æ®ID更新实体对象集合
     *
     * @param entityList å®žä½“对象集合
     * @return æ›´æ–°æ“ä½œæ˜¯å¦æˆåŠŸçš„å¸ƒå°”å€¼
     */
    default boolean updateBatchById(Collection<T> entityList) {
        return Db.updateBatchById(entityList);
    }
    /**
     * æ‰¹é‡æ’入或更新实体对象集合
     *
     * @param entityList å®žä½“对象集合
     * @return æ’入或更新操作是否成功的布尔值
     */
    default boolean insertOrUpdateBatch(Collection<T> entityList) {
        return Db.saveOrUpdateBatch(entityList);
    }
    /**
     * æ‰¹é‡æ’入实体对象集合并指定批处理大小
     *
     * @param entityList å®žä½“对象集合
     * @param batchSize  æ‰¹å¤„理大小
     * @return æ’入操作是否成功的布尔值
     */
    default boolean insertBatch(Collection<T> entityList, int batchSize) {
        return Db.saveBatch(entityList, batchSize);
    }
    /**
     * æ‰¹é‡æ ¹æ®ID更新实体对象集合并指定批处理大小
     *
     * @param entityList å®žä½“对象集合
     * @param batchSize  æ‰¹å¤„理大小
     * @return æ›´æ–°æ“ä½œæ˜¯å¦æˆåŠŸçš„å¸ƒå°”å€¼
     */
    default boolean updateBatchById(Collection<T> entityList, int batchSize) {
        return Db.updateBatchById(entityList, batchSize);
    }
    /**
     * æ‰¹é‡æ’入或更新实体对象集合并指定批处理大小
     *
     * @param entityList å®žä½“对象集合
     * @param batchSize  æ‰¹å¤„理大小
     * @return æ’入或更新操作是否成功的布尔值
     */
    default boolean insertOrUpdateBatch(Collection<T> entityList, int batchSize) {
        return Db.saveOrUpdateBatch(entityList, batchSize);
    }
    /**
     * æ ¹æ®ID查询单个VO对象
     *
     * @param id ä¸»é”®ID
     * @return æŸ¥è¯¢åˆ°çš„单个VO对象
     */
    default V selectVoById(Serializable id) {
        return selectVoById(id, this.currentVoClass());
    }
    /**
     * æ ¹æ®ID查询单个VO对象并将其转换为指定的VOç±»
     *
     * @param id      ä¸»é”®ID
     * @param voClass è¦è½¬æ¢çš„VO类的Class对象
     * @param <C>     VO类的类型
     * @return æŸ¥è¯¢åˆ°çš„单个VO对象,经过转换为指定的VO类后返回
     */
    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);
    }
    /**
     * æ ¹æ®ID集合批量查询VO对象列表
     *
     * @param idList ä¸»é”®ID集合
     * @return æŸ¥è¯¢åˆ°çš„VO对象列表
     */
    default List<V> selectVoByIds(Collection<? extends Serializable> idList) {
        return selectVoByIds(idList, this.currentVoClass());
    }
    /**
     * æ ¹æ®ID集合批量查询实体对象列表,并将其转换为指定的VO对象列表
     *
     * @param idList  ä¸»é”®ID集合
     * @param voClass è¦è½¬æ¢çš„VO类的Class对象
     * @param <C>     VO类的类型
     * @return æŸ¥è¯¢åˆ°çš„VO对象列表,经过转换为指定的VO类后返回
     */
    default <C> List<C> selectVoByIds(Collection<? extends Serializable> idList, Class<C> voClass) {
        List<T> list = this.selectByIds(idList);
        if (CollUtil.isEmpty(list)) {
            return CollUtil.newArrayList();
        }
        return MapstructUtils.convert(list, voClass);
    }
    /**
     * æ ¹æ®æŸ¥è¯¢æ¡ä»¶Map查询VO对象列表
     *
     * @param map æŸ¥è¯¢æ¡ä»¶Map
     * @return æŸ¥è¯¢åˆ°çš„VO对象列表
     */
    default List<V> selectVoByMap(Map<String, Object> map) {
        return selectVoByMap(map, this.currentVoClass());
    }
    /**
     * æ ¹æ®æŸ¥è¯¢æ¡ä»¶Map查询实体对象列表,并将其转换为指定的VO对象列表
     *
     * @param map     æŸ¥è¯¢æ¡ä»¶Map
     * @param voClass è¦è½¬æ¢çš„VO类的Class对象
     * @param <C>     VO类的类型
     * @return æŸ¥è¯¢åˆ°çš„VO对象列表,经过转换为指定的VO类后返回
     */
    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);
    }
    /**
     * æ ¹æ®æ¡ä»¶æŸ¥è¯¢å•个VO对象
     *
     * @param wrapper æŸ¥è¯¢æ¡ä»¶Wrapper
     * @return æŸ¥è¯¢åˆ°çš„单个VO对象
     */
    default V selectVoOne(Wrapper<T> wrapper) {
        return selectVoOne(wrapper, this.currentVoClass());
    }
    /**
     * æ ¹æ®æ¡ä»¶æŸ¥è¯¢å•个VO对象,并根据需要决定是否抛出异常
     *
     * @param wrapper æŸ¥è¯¢æ¡ä»¶Wrapper
     * @param throwEx æ˜¯å¦æŠ›å‡ºå¼‚常的标志
     * @return æŸ¥è¯¢åˆ°çš„单个VO对象
     */
    default V selectVoOne(Wrapper<T> wrapper, boolean throwEx) {
        return selectVoOne(wrapper, this.currentVoClass(), throwEx);
    }
    /**
     * æ ¹æ®æ¡ä»¶æŸ¥è¯¢å•个VO对象,并指定返回的VO对象的类型
     *
     * @param wrapper æŸ¥è¯¢æ¡ä»¶Wrapper
     * @param voClass è¿”回的VO对象的Class对象
     * @param <C>     è¿”回的VO对象的类型
     * @return æŸ¥è¯¢åˆ°çš„单个VO对象,经过类型转换为指定的VO类后返回
     */
    default <C> C selectVoOne(Wrapper<T> wrapper, Class<C> voClass) {
        return selectVoOne(wrapper, voClass, true);
    }
    /**
     * æ ¹æ®æ¡ä»¶æŸ¥è¯¢å•个实体对象,并将其转换为指定的VO对象
     *
     * @param wrapper æŸ¥è¯¢æ¡ä»¶Wrapper
     * @param voClass è¦è½¬æ¢çš„VO类的Class对象
     * @param throwEx æ˜¯å¦æŠ›å‡ºå¼‚常的标志
     * @param <C>     VO类的类型
     * @return æŸ¥è¯¢åˆ°çš„单个VO对象,经过转换为指定的VO类后返回
     */
    default <C> C selectVoOne(Wrapper<T> wrapper, Class<C> voClass, boolean throwEx) {
        T obj = this.selectOne(wrapper, throwEx);
        if (ObjectUtil.isNull(obj)) {
            return null;
        }
        return MapstructUtils.convert(obj, voClass);
    }
    /**
     * æŸ¥è¯¢æ‰€æœ‰VO对象列表
     *
     * @return æŸ¥è¯¢åˆ°çš„VO对象列表
     */
    default List<V> selectVoList() {
        return selectVoList(new QueryWrapper<>(), this.currentVoClass());
    }
    /**
     * æ ¹æ®æ¡ä»¶æŸ¥è¯¢VO对象列表
     *
     * @param wrapper æŸ¥è¯¢æ¡ä»¶Wrapper
     * @return æŸ¥è¯¢åˆ°çš„VO对象列表
     */
    default List<V> selectVoList(Wrapper<T> wrapper) {
        return selectVoList(wrapper, this.currentVoClass());
    }
    /**
     * æ ¹æ®æ¡ä»¶æŸ¥è¯¢å®žä½“对象列表,并将其转换为指定的VO对象列表
     *
     * @param wrapper æŸ¥è¯¢æ¡ä»¶Wrapper
     * @param voClass è¦è½¬æ¢çš„VO类的Class对象
     * @param <C>     VO类的类型
     * @return æŸ¥è¯¢åˆ°çš„VO对象列表,经过转换为指定的VO类后返回
     */
    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);
    }
    /**
     * æ ¹æ®æ¡ä»¶åˆ†é¡µæŸ¥è¯¢VO对象列表
     *
     * @param page    åˆ†é¡µä¿¡æ¯
     * @param wrapper æŸ¥è¯¢æ¡ä»¶Wrapper
     * @return æŸ¥è¯¢åˆ°çš„VO对象分页列表
     */
    default <P extends IPage<V>> P selectVoPage(IPage<T> page, Wrapper<T> wrapper) {
        return selectVoPage(page, wrapper, this.currentVoClass());
    }
    /**
     * æ ¹æ®æ¡ä»¶åˆ†é¡µæŸ¥è¯¢å®žä½“对象列表,并将其转换为指定的VO对象分页列表
     *
     * @param page    åˆ†é¡µä¿¡æ¯
     * @param wrapper æŸ¥è¯¢æ¡ä»¶Wrapper
     * @param voClass è¦è½¬æ¢çš„VO类的Class对象
     * @param <C>     VO类的类型
     * @param <P>     VO对象分页列表的类型
     * @return æŸ¥è¯¢åˆ°çš„VO对象分页列表,经过转换为指定的VO类后返回
     */
    default <C, P extends IPage<C>> P selectVoPage(IPage<T> page, Wrapper<T> wrapper, Class<C> voClass) {
        // æ ¹æ®æ¡ä»¶åˆ†é¡µæŸ¥è¯¢å®žä½“对象列表
        List<T> list = this.selectList(page, wrapper);
        // åˆ›å»ºä¸€ä¸ªæ–°çš„VO对象分页列表,并设置分页信息
        IPage<C> voPage = new Page<>(page.getCurrent(), page.getSize(), page.getTotal());
        if (CollUtil.isEmpty(list)) {
            return (P) voPage;
        }
        voPage.setRecords(MapstructUtils.convert(list, voClass));
        return (P) voPage;
    }
    /**
     * æ ¹æ®æ¡ä»¶æŸ¥è¯¢ç¬¦åˆæ¡ä»¶çš„对象,并将其转换为指定类型的对象列表
     *
     * @param wrapper æŸ¥è¯¢æ¡ä»¶Wrapper
     * @param mapper  è½¬æ¢å‡½æ•°ï¼Œç”¨äºŽå°†æŸ¥è¯¢åˆ°çš„对象转换为指定类型的对象
     * @param <C>     è¦è½¬æ¢çš„对象的类型
     * @return æŸ¥è¯¢åˆ°çš„符合条件的对象列表,经过转换为指定类型的对象后返回
     */
    default <C> List<C> selectObjs(Wrapper<T> wrapper, Function<? super Object, C> mapper) {
        return StreamUtils.toList(this.selectObjs(wrapper), mapper);
    }
}
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/core/page/PageQuery.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,127 @@
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 com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.Data;
import org.dromara.common.core.exception.ServiceException;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.core.utils.sql.SqlUtil;
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;
    }
    @JsonIgnore
    public Integer getFirstNum() {
        return (pageNum - 1) * pageSize;
    }
    public PageQuery(Integer pageSize, Integer pageNum) {
        this.pageSize = pageSize;
        this.pageNum = pageNum;
    }
}
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/core/page/TableDataInfo.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,107 @@
package org.dromara.common.mybatis.core.page;
import cn.hutool.core.collection.CollUtil;
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;
        this.code = HttpStatus.HTTP_OK;
        this.msg = "查询成功";
    }
    /**
     * æ ¹æ®åˆ†é¡µå¯¹è±¡æž„建表格分页数据对象
     */
    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;
    }
    /**
     * æ ¹æ®åŽŸå§‹æ•°æ®åˆ—è¡¨å’Œåˆ†é¡µå‚æ•°ï¼Œæž„å»ºè¡¨æ ¼åˆ†é¡µæ•°æ®å¯¹è±¡ï¼ˆç”¨äºŽå‡åˆ†é¡µï¼‰
     *
     * @param list åŽŸå§‹æ•°æ®åˆ—è¡¨ï¼ˆå…¨éƒ¨æ•°æ®ï¼‰
     * @param page åˆ†é¡µå‚数对象(包含当前页码、每页大小等)
     * @return æž„造好的分页结果 TableDataInfo<T>
     */
    public static <T> TableDataInfo<T> build(List<T> list, IPage<T> page) {
        if (CollUtil.isEmpty(list)) {
            return TableDataInfo.build();
        }
        List<T> pageList = CollUtil.page((int) page.getCurrent() - 1, (int) page.getSize(), list);
        return new TableDataInfo<>(pageList, list.size());
    }
}
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/enums/DataBaseType.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,87 @@
package org.dromara.common.mybatis.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
import org.dromara.common.core.utils.StringUtils;
/**
 * æ•°æ®åº“类型
 *
 * @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;
    /**
     * æ ¹æ®æ•°æ®åº“产品名称查找对应的数据库类型
     *
     * @param databaseProductName æ•°æ®åº“产品名称
     * @return å¯¹åº”的数据库类型枚举值
     */
    public static DataBaseType find(String databaseProductName) {
        if (StringUtils.isBlank(databaseProductName)) {
            return MY_SQL;
        }
        for (DataBaseType type : values()) {
            if (type.getType().equals(databaseProductName)) {
                return type;
            }
        }
        return MY_SQL;
    }
    /**
     * åˆ¤æ–­æ˜¯å¦ä¸º MySQL ç±»åž‹
     */
    public boolean isMySql() {
        return this == MY_SQL;
    }
    /**
     * åˆ¤æ–­æ˜¯å¦ä¸º Oracle ç±»åž‹
     */
    public boolean isOracle() {
        return this == ORACLE;
    }
    /**
     * åˆ¤æ–­æ˜¯å¦ä¸º PostgreSQL ç±»åž‹
     */
    public boolean isPostgreSql() {
        return this == POSTGRE_SQL;
    }
    /**
     * åˆ¤æ–­æ˜¯å¦ä¸º SQL Server ç±»åž‹
     */
    public boolean isSqlServer() {
        return this == SQL_SERVER;
    }
}
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/enums/DataScopeType.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,87 @@
package org.dromara.common.mybatis.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
import org.dromara.common.core.domain.model.LoginUser;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.mybatis.helper.DataPermissionHelper;
/**
 * æ•°æ®æƒé™ç±»åž‹æžšä¸¾
 * <p>
 * æ”¯æŒä½¿ç”¨ SpEL æ¨¡æ¿è¡¨è¾¾å¼å®šä¹‰ SQL æŸ¥è¯¢æ¡ä»¶
 * å†…置数据:
 * - {@code user}: å½“前登录用户信息,参考 {@link LoginUser}
 * å†…置服务:
 * - {@code sdss}: ç³»ç»Ÿæ•°æ®æƒé™æœåŠ¡ï¼Œå‚è€ƒ ISysDataScopeService
 * å¦‚需扩展数据,可以通过 {@link DataPermissionHelper} è¿›è¡Œæ“ä½œ
 * å¦‚需扩展服务,可以通过 ISysDataScopeService è‡ªè¡Œç¼–写
 * </p>
 *
 * @author Lion Li
 * @version 3.5.0
 */
@Getter
@AllArgsConstructor
public enum DataScopeType {
    /**
     * å…¨éƒ¨æ•°æ®æƒé™
     */
    ALL("1", "", ""),
    /**
     * è‡ªå®šæ•°æ®æƒé™
     */
    CUSTOM("2", " #{#deptName} IN ( #{@sdss.getRoleCustom( #user.roleId )} ) ", " 1 = 0 "),
    /**
     * éƒ¨é—¨æ•°æ®æƒé™
     */
    DEPT("3", " #{#deptName} = #{#user.deptId} ", " 1 = 0 "),
    /**
     * éƒ¨é—¨åŠä»¥ä¸‹æ•°æ®æƒé™
     */
    DEPT_AND_CHILD("4", " #{#deptName} IN ( #{@sdss.getDeptAndChild( #user.deptId )} )", " 1 = 0 "),
    /**
     * ä»…本人数据权限
     */
    SELF("5", " #{#userName} = #{#user.userId} ", " 1 = 0 "),
    /**
     * éƒ¨é—¨åŠä»¥ä¸‹æˆ–本人数据权限
     */
    DEPT_AND_CHILD_OR_SELF("6", " #{#deptName} IN ( #{@sdss.getDeptAndChild( #user.deptId )} ) OR #{#userName} = #{#user.userId} ", " 1 = 0 ");
    private final String code;
    /**
     * SpEL æ¨¡æ¿è¡¨è¾¾å¼ï¼Œç”¨äºŽæž„建 SQL æŸ¥è¯¢æ¡ä»¶
     */
    private final String sqlTemplate;
    /**
     * å¦‚果不满足 {@code sqlTemplate} çš„æ¡ä»¶ï¼Œåˆ™ä½¿ç”¨æ­¤é»˜è®¤ SQL è¡¨è¾¾å¼
     */
    private final String elseSql;
    /**
     * æ ¹æ®æžšä¸¾ä»£ç æŸ¥æ‰¾å¯¹åº”的枚举值
     *
     * @param code æžšä¸¾ä»£ç 
     * @return å¯¹åº”的枚举值,如果未找到则返回 null
     */
    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-Vue-Plus/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/handler/InjectionMetaObjectHandler.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,113 @@
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 lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.reflection.MetaObject;
import org.dromara.common.core.domain.model.LoginUser;
import org.dromara.common.core.exception.ServiceException;
import org.dromara.common.core.utils.ObjectUtils;
import org.dromara.common.mybatis.core.domain.BaseEntity;
import org.dromara.common.satoken.utils.LoginHelper;
import java.util.Date;
/**
 * MP注入处理器
 *
 * @author Lion Li
 * @date 2021/4/25
 */
@Slf4j
public class InjectionMetaObjectHandler implements MetaObjectHandler {
    /**
     * å¦‚果用户不存在默认注入-1代表无用户
     */
    private static final Long DEFAULT_USER_ID = -1L;
    /**
     * æ’入填充方法,用于在插入数据时自动填充实体对象中的创建时间、更新时间、创建人、更新人等信息
     *
     * @param metaObject å…ƒå¯¹è±¡ï¼Œç”¨äºŽèŽ·å–åŽŸå§‹å¯¹è±¡å¹¶è¿›è¡Œå¡«å……
     */
    @Override
    public void insertFill(MetaObject metaObject) {
        try {
            if (ObjectUtil.isNotNull(metaObject) && metaObject.getOriginalObject() instanceof BaseEntity baseEntity) {
                // èŽ·å–å½“å‰æ—¶é—´ä½œä¸ºåˆ›å»ºæ—¶é—´å’Œæ›´æ–°æ—¶é—´ï¼Œå¦‚æžœåˆ›å»ºæ—¶é—´ä¸ä¸ºç©ºï¼Œåˆ™ä½¿ç”¨åˆ›å»ºæ—¶é—´ï¼Œå¦åˆ™ä½¿ç”¨å½“å‰æ—¶é—´
                Date current = ObjectUtils.notNull(baseEntity.getCreateTime(), new Date());
                baseEntity.setCreateTime(current);
                baseEntity.setUpdateTime(current);
                // å¦‚果创建人为空,则填充当前登录用户的信息
                if (ObjectUtil.isNull(baseEntity.getCreateBy())) {
                    LoginUser loginUser = getLoginUser();
                    if (ObjectUtil.isNotNull(loginUser)) {
                        Long userId = loginUser.getUserId();
                        // å¡«å……创建人、更新人和创建部门信息
                        baseEntity.setCreateBy(userId);
                        baseEntity.setUpdateBy(userId);
                        baseEntity.setCreateDept(ObjectUtils.notNull(baseEntity.getCreateDept(), loginUser.getDeptId()));
                    } else {
                        // å¡«å……创建人、更新人和创建部门信息
                        baseEntity.setCreateBy(DEFAULT_USER_ID);
                        baseEntity.setUpdateBy(DEFAULT_USER_ID);
                        baseEntity.setCreateDept(ObjectUtils.notNull(baseEntity.getCreateDept(), DEFAULT_USER_ID));
                    }
                }
            } else {
                Date date = new Date();
                this.strictInsertFill(metaObject, "createTime", Date.class, date);
                this.strictInsertFill(metaObject, "updateTime", Date.class, date);
            }
        } catch (Exception e) {
            throw new ServiceException("自动注入异常 => " + e.getMessage(), HttpStatus.HTTP_UNAUTHORIZED);
        }
    }
    /**
     * æ›´æ–°å¡«å……方法,用于在更新数据时自动填充实体对象中的更新时间和更新人信息
     *
     * @param metaObject å…ƒå¯¹è±¡ï¼Œç”¨äºŽèŽ·å–åŽŸå§‹å¯¹è±¡å¹¶è¿›è¡Œå¡«å……
     */
    @Override
    public void updateFill(MetaObject metaObject) {
        try {
            if (ObjectUtil.isNotNull(metaObject) && metaObject.getOriginalObject() instanceof BaseEntity baseEntity) {
                // èŽ·å–å½“å‰æ—¶é—´ä½œä¸ºæ›´æ–°æ—¶é—´ï¼Œæ— è®ºåŽŸå§‹å¯¹è±¡ä¸­çš„æ›´æ–°æ—¶é—´æ˜¯å¦ä¸ºç©ºéƒ½å¡«å……
                Date current = new Date();
                baseEntity.setUpdateTime(current);
                // èŽ·å–å½“å‰ç™»å½•ç”¨æˆ·çš„ID,并填充更新人信息
                Long userId = LoginHelper.getUserId();
                if (ObjectUtil.isNotNull(userId)) {
                    baseEntity.setUpdateBy(userId);
                } else {
                    baseEntity.setUpdateBy(DEFAULT_USER_ID);
                }
            } else {
                this.strictUpdateFill(metaObject, "updateTime", Date.class, new Date());
            }
        } catch (Exception e) {
            throw new ServiceException("自动注入异常 => " + e.getMessage(), HttpStatus.HTTP_UNAUTHORIZED);
        }
    }
    /**
     * èŽ·å–å½“å‰ç™»å½•ç”¨æˆ·ä¿¡æ¯
     *
     * @return å½“前登录用户的信息,如果用户未登录则返回 null
     */
    private LoginUser getLoginUser() {
        LoginUser loginUser;
        try {
            loginUser = LoginHelper.getLoginUser();
        } catch (Exception e) {
            return null;
        }
        return loginUser;
    }
}
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/handler/MybatisExceptionHandler.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,89 @@
package org.dromara.common.mybatis.handler;
import cn.dev33.satoken.exception.NotLoginException;
import cn.hutool.http.HttpStatus;
import com.baomidou.dynamic.datasource.exception.CannotFindDataSourceException;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.dromara.common.core.domain.R;
import org.mybatis.spring.MyBatisSystemException;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
/**
 * 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(HttpStatus.HTTP_CONFLICT, "数据库中已存在该记录,请联系管理员确认");
    }
    /**
     * Mybatis系统异常 é€šç”¨å¤„理
     */
    @ExceptionHandler(MyBatisSystemException.class)
    public R<Void> handleCannotFindDataSourceException(MyBatisSystemException e, HttpServletRequest request) {
        String requestURI = request.getRequestURI();
        Throwable root = getRootCause(e);
        if (root instanceof NotLoginException) {
            log.error("请求地址'{}',认证失败'{}',无法访问系统资源", requestURI, root.getMessage());
            return R.fail(HttpStatus.HTTP_UNAUTHORIZED, "认证失败,无法访问系统资源");
        }
        if (root instanceof CannotFindDataSourceException) {
            log.error("请求地址'{}', æœªæ‰¾åˆ°æ•°æ®æº", requestURI);
            return R.fail(HttpStatus.HTTP_INTERNAL_ERROR, "未找到数据源,请联系管理员确认");
        }
        log.error("请求地址'{}', Mybatis系统异常", requestURI, e);
        return R.fail(HttpStatus.HTTP_INTERNAL_ERROR, e.getMessage());
    }
    /**
     * èŽ·å–å¼‚å¸¸çš„æ ¹å› ï¼ˆé€’å½’æŸ¥æ‰¾ï¼‰
     *
     * @param e å½“前异常
     * @return æ ¹å› å¼‚常(最底层的 cause)
     * <p>
     * é€»è¾‘说明:
     * 1. å¦‚æžœ e æ²¡æœ‰ cause,说明 e æœ¬èº«å°±æ˜¯æ ¹å› ï¼Œç›´æŽ¥è¿”回
     * 2. å¦‚æžœ e çš„ cause å’Œè‡ªèº«ç›¸åŒï¼ˆé˜²æ­¢å¾ªçŽ¯å¼•ç”¨ï¼‰ï¼Œä¹Ÿè¿”å›ž e
     * 3. å¦åˆ™é€’归调用,继续向下寻找最底层的 cause
     */
    public static Throwable getRootCause(Throwable e) {
        Throwable cause = e.getCause();
        if (cause == null || cause == e) {
            return e;
        }
        return getRootCause(cause);
    }
    /**
     * åœ¨å¼‚常链中查找指定类型的异常
     *
     * @param e     å½“前异常
     * @param clazz ç›®æ ‡å¼‚常类
     * @return æ‰¾åˆ°çš„æŒ‡å®šç±»åž‹å¼‚常,如果没有找到返回 null
     */
    public static Throwable findCause(Throwable e, Class<? extends Throwable> clazz) {
        Throwable t = e;
        while (t != null && t != t.getCause()) {
            if (clazz.isInstance(t)) {
                return t;
            }
            t = t.getCause();
        }
        return null;
    }
}
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/handler/PlusDataPermissionHandler.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,261 @@
package org.dromara.common.mybatis.handler;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import net.sf.jsqlparser.JSQLParserException;
import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.expression.operators.conditional.AndExpression;
import net.sf.jsqlparser.expression.operators.relational.ParenthesedExpressionList;
import net.sf.jsqlparser.parser.CCJSqlParserUtil;
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 org.springframework.context.expression.BeanFactoryResolver;
import org.springframework.expression.*;
import org.springframework.expression.common.TemplateParserContext;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import java.util.*;
import java.util.function.Function;
/**
 * æ•°æ®æƒé™è¿‡æ»¤
 *
 * @author Lion Li
 * @version 3.5.0
 */
@Slf4j
public class PlusDataPermissionHandler {
    /**
     * spel è§£æžå™¨
     */
    private final ExpressionParser parser = new SpelExpressionParser();
    private final ParserContext parserContext = new TemplateParserContext();
    /**
     * bean解析器 ç”¨äºŽå¤„理 spel è¡¨è¾¾å¼ä¸­å¯¹ bean çš„调用
     */
    private final BeanResolver beanResolver = new BeanFactoryResolver(SpringUtils.getBeanFactory());
    /**
     * èŽ·å–æ•°æ®è¿‡æ»¤æ¡ä»¶çš„ SQL ç‰‡æ®µ
     *
     * @param where             åŽŸå§‹çš„æŸ¥è¯¢æ¡ä»¶è¡¨è¾¾å¼
     * @param isSelect          æ˜¯å¦ä¸ºæŸ¥è¯¢è¯­å¥
     * @return æ•°æ®è¿‡æ»¤æ¡ä»¶çš„ SQL ç‰‡æ®µ
     */
    public Expression getSqlSegment(Expression where, boolean isSelect) {
        try {
            // èŽ·å–æ•°æ®æƒé™é…ç½®
            DataPermission dataPermission = getDataPermission();
            // èŽ·å–å½“å‰ç™»å½•ç”¨æˆ·ä¿¡æ¯
            LoginUser currentUser = DataPermissionHelper.getVariable("user");
            if (ObjectUtil.isNull(currentUser)) {
                currentUser = LoginHelper.getLoginUser();
                DataPermissionHelper.setVariable("user", currentUser);
            }
            // å¦‚果是超级管理员或租户管理员,则不过滤数据
            if (LoginHelper.isSuperAdmin() || LoginHelper.isTenantAdmin()) {
                return where;
            }
            // æž„造数据过滤条件的 SQL ç‰‡æ®µ
            String dataFilterSql = buildDataFilter(dataPermission, isSelect);
            if (StringUtils.isBlank(dataFilterSql)) {
                return where;
            }
            Expression expression = CCJSqlParserUtil.parseExpression(dataFilterSql);
            // æ•°æ®æƒé™ä½¿ç”¨å•独的括号 é˜²æ­¢ä¸Žå…¶ä»–条件冲突
            ParenthesedExpressionList<Expression> parenthesis = new ParenthesedExpressionList<>(expression);
            if (ObjectUtil.isNotNull(where)) {
                return new AndExpression(where, parenthesis);
            } else {
                return parenthesis;
            }
        } catch (JSQLParserException e) {
            throw new ServiceException("数据权限解析异常 => " + e.getMessage());
        } finally {
            DataPermissionHelper.removePermission();
        }
    }
    /**
     * æž„建数据过滤条件的 SQL è¯­å¥
     *
     * @param dataPermission æ•°æ®æƒé™æ³¨è§£
     * @param isSelect       æ ‡å¿—当前操作是否为查询操作,查询操作和更新或删除操作在处理过滤条件时会有不同的处理方式
     * @return æž„建的数据过滤条件的 SQL è¯­å¥
     * @throws ServiceException å¦‚果角色的数据范围异常或者 key ä¸Ž value çš„长度不匹配,则抛出 ServiceException å¼‚常
     */
    private String buildDataFilter(DataPermission dataPermission, boolean isSelect) {
        // æ›´æ–°æˆ–删除需满足所有条件
        String joinStr = isSelect ? " OR " : " AND ";
        if (StringUtils.isNotBlank(dataPermission.joinStr())) {
            joinStr = " " + dataPermission.joinStr() + " ";
        }
        LoginUser user = DataPermissionHelper.getVariable("user");
        Object defaultValue = "-1";
        NullSafeStandardEvaluationContext context = new NullSafeStandardEvaluationContext(defaultValue);
        context.addPropertyAccessor(new NullSafePropertyAccessor(context.getPropertyAccessors().get(0), defaultValue));
        context.setBeanResolver(beanResolver);
        DataPermissionHelper.getContext().forEach(context::setVariable);
        Set<String> conditions = new HashSet<>();
        // ä¼˜å…ˆè®¾ç½®å˜é‡
        List<String> keys = new ArrayList<>();
        Map<DataColumn, Boolean> ignoreMap = new HashMap<>();
        for (DataColumn dataColumn : dataPermission.value()) {
            if (dataColumn.key().length != dataColumn.value().length) {
                throw new ServiceException("角色数据范围异常 => key与value长度不匹配");
            }
            // åŒ…含权限标识符 è¿™ç›´æŽ¥è·³è¿‡
            if (StringUtils.isNotBlank(dataColumn.permission()) &&
                CollUtil.contains(user.getMenuPermission(), dataColumn.permission())
            ) {
                ignoreMap.put(dataColumn, Boolean.TRUE);
                continue;
            }
            // è®¾ç½®æ³¨è§£å˜é‡ key ä¸ºè¡¨è¾¾å¼å˜é‡ value ä¸ºå˜é‡å€¼
            for (int i = 0; i < dataColumn.key().length; i++) {
                context.setVariable(dataColumn.key()[i], dataColumn.value()[i]);
            }
            keys.addAll(Arrays.stream(dataColumn.key()).map(key -> "#" + key).toList());
        }
        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 StringUtils.EMPTY;
            }
            boolean isSuccess = false;
            for (DataColumn dataColumn : dataPermission.value()) {
                // åŒ…含权限标识符 è¿™ç›´æŽ¥è·³è¿‡
                if (ignoreMap.containsKey(dataColumn)) {
                    // ä¿®å¤å¤šè§’色与权限标识符共用问题 https://gitee.com/dromara/RuoYi-Vue-Plus/issues/IB4CS4
                    conditions.add(joinStr + " 1 = 1 ");
                    isSuccess = true;
                    continue;
                }
                // ä¸åŒ…含 key å˜é‡ åˆ™ä¸å¤„理
                if (!StringUtils.containsAny(type.getSqlTemplate(), keys.toArray(String[]::new))) {
                    continue;
                }
                // å½“前注解不满足模板 ä¸å¤„理
                if (!StringUtils.containsAny(type.getSqlTemplate(), dataColumn.key())) {
                    continue;
                }
                // å¿½ç•¥æ•°æ®æƒé™ é˜²æ­¢spel表达式内有其他sql查询导致死循环调用
                String sql = DataPermissionHelper.ignore(() ->
                    parser.parseExpression(type.getSqlTemplate(), parserContext).getValue(context, String.class)
                );
                // è§£æžsql模板并填充
                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 StringUtils.EMPTY;
    }
    /**
     * æ ¹æ®æ˜ å°„语句 ID æˆ–类名获取对应的 DataPermission æ³¨è§£å¯¹è±¡
     *
     * @return DataPermission æ³¨è§£å¯¹è±¡ï¼Œå¦‚果不存在则返回 null
     */
    public DataPermission getDataPermission() {
        return DataPermissionHelper.getPermission();
    }
    /**
     * æ£€æŸ¥ç»™å®šçš„æ˜ å°„语句 ID æ˜¯å¦æœ‰æ•ˆï¼Œå³æ˜¯å¦èƒ½å¤Ÿæ‰¾åˆ°å¯¹åº”çš„ DataPermission æ³¨è§£å¯¹è±¡
     *
     * @return å¦‚果找到对应的 DataPermission æ³¨è§£å¯¹è±¡ï¼Œåˆ™è¿”回 false;否则返回 true
     */
    public boolean invalid() {
        return getDataPermission() == null;
    }
    /**
     * å¯¹æ‰€æœ‰null变量找不到的变量返回默认值
     */
    @AllArgsConstructor
    private static class NullSafeStandardEvaluationContext extends StandardEvaluationContext {
        private final Object defaultValue;
        @Override
        public Object lookupVariable(String name) {
            Object obj = super.lookupVariable(name);
            // å¦‚果读取到的值是 null,则返回默认值
            if (obj == null) {
                return defaultValue;
            }
            return obj;
        }
    }
    /**
     * å¯¹æ‰€æœ‰null变量找不到的变量返回默认值 å§”托模式 å°†ä¸éœ€è¦å¤„理的方法委托给原处理器
     */
    @AllArgsConstructor
    private static class NullSafePropertyAccessor implements PropertyAccessor {
        private final PropertyAccessor delegate;
        private final Object defaultValue;
        @Override
        public Class<?>[] getSpecificTargetClasses() {
            return delegate.getSpecificTargetClasses();
        }
        @Override
        public boolean canRead(EvaluationContext context, Object target, String name) throws AccessException {
            return delegate.canRead(context, target, name);
        }
        @Override
        public TypedValue read(EvaluationContext context, Object target, String name) throws AccessException {
            TypedValue value = delegate.read(context, target, name);
            // å¦‚果读取到的值是 null,则返回默认值
            if (value.getValue() == null) {
                return new TypedValue(defaultValue);
            }
            return value;
        }
        @Override
        public boolean canWrite(EvaluationContext context, Object target, String name) throws AccessException {
            return delegate.canWrite(context, target, name);
        }
        @Override
        public void write(EvaluationContext context, Object target, String name, Object newValue) throws AccessException {
            delegate.write(context, target, name, newValue);
        }
    }
}
在上述文件截断后对比
RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/handler/PlusPostInitTableInfoHandler.java RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/helper/DataBaseHelper.java RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/helper/DataPermissionHelper.java RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/interceptor/PlusDataPermissionInterceptor.java RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/utils/IdGeneratorUtil.java RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-mybatis/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-mybatis/src/main/resources/common-mybatis.yml RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-mybatis/src/main/resources/spy.properties RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-oss/.flattened-pom.xml RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-oss/pom.xml RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-oss/src/main/java/org/dromara/common/oss/constant/OssConstant.java RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-oss/src/main/java/org/dromara/common/oss/core/OssClient.java RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-oss/src/main/java/org/dromara/common/oss/core/WriteOutSubscriber.java RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-oss/src/main/java/org/dromara/common/oss/entity/UploadResult.java RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-oss/src/main/java/org/dromara/common/oss/enums/AccessPolicyType.java RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-oss/src/main/java/org/dromara/common/oss/exception/OssException.java RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-oss/src/main/java/org/dromara/common/oss/factory/OssFactory.java RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-oss/src/main/java/org/dromara/common/oss/properties/OssProperties.java RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-ratelimiter/.flattened-pom.xml RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-ratelimiter/pom.xml RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-ratelimiter/src/main/java/org/dromara/common/ratelimiter/annotation/RateLimiter.java RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-ratelimiter/src/main/java/org/dromara/common/ratelimiter/aspectj/RateLimiterAspect.java RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-ratelimiter/src/main/java/org/dromara/common/ratelimiter/config/RateLimiterConfig.java RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-ratelimiter/src/main/java/org/dromara/common/ratelimiter/enums/LimitType.java RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-ratelimiter/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-ratelimiter/src/main/resources/spel-extension.json RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-redis/.flattened-pom.xml RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-redis/pom.xml RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/config/CacheConfig.java RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/config/RedisConfig.java RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/config/properties/RedissonProperties.java RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/handler/KeyPrefixHandler.java RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/handler/RedisExceptionHandler.java RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/manager/CaffeineCacheDecorator.java RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/manager/PlusSpringCacheManager.java RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/utils/CacheUtils.java RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/utils/QueueUtils.java RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/utils/RedisUtils.java RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/utils/SequenceUtils.java RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-redis/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-satoken/.flattened-pom.xml RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-satoken/pom.xml RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-satoken/src/main/java/org/dromara/common/satoken/config/SaTokenConfig.java RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-satoken/src/main/java/org/dromara/common/satoken/core/dao/PlusSaTokenDao.java RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-satoken/src/main/java/org/dromara/common/satoken/core/service/SaPermissionImpl.java RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-satoken/src/main/java/org/dromara/common/satoken/handler/SaTokenExceptionHandler.java RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-satoken/src/main/java/org/dromara/common/satoken/utils/LoginHelper.java RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-satoken/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-satoken/src/main/resources/common-satoken.yml RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-security/.flattened-pom.xml RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-security/pom.xml RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-security/src/main/java/org/dromara/common/security/config/SecurityConfig.java RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-security/src/main/java/org/dromara/common/security/config/properties/SecurityProperties.java RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-security/src/main/java/org/dromara/common/security/handler/AllUrlHandler.java RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-security/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-sensitive/.flattened-pom.xml RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-sensitive/pom.xml RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-sensitive/src/main/java/org/dromara/common/sensitive/annotation/Sensitive.java RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-sensitive/src/main/java/org/dromara/common/sensitive/core/SensitiveService.java RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-sensitive/src/main/java/org/dromara/common/sensitive/core/SensitiveStrategy.java RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-sensitive/src/main/java/org/dromara/common/sensitive/handler/SensitiveHandler.java RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-sms/.flattened-pom.xml RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-sms/pom.xml RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-sms/src/main/java/org/dromara/common/sms/config/SmsAutoConfiguration.java RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-sms/src/main/java/org/dromara/common/sms/core/dao/PlusSmsDao.java RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-sms/src/main/java/org/dromara/common/sms/handler/SmsExceptionHandler.java RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-sms/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-social/.flattened-pom.xml RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-social/pom.xml RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-social/src/main/java/me/zhyd/oauth/request/AbstractAuthWeChatEnterpriseRequest.java RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-social/src/main/java/me/zhyd/oauth/request/AuthDingTalkV2Request.java RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-social/src/main/java/org/dromara/common/social/config/SocialAutoConfiguration.java RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-social/src/main/java/org/dromara/common/social/config/properties/SocialLoginConfigProperties.java RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-social/src/main/java/org/dromara/common/social/config/properties/SocialProperties.java RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-social/src/main/java/org/dromara/common/social/gitea/AuthGiteaRequest.java RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-social/src/main/java/org/dromara/common/social/gitea/AuthGiteaSource.java RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-social/src/main/java/org/dromara/common/social/maxkey/AuthMaxKeyRequest.java RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-social/src/main/java/org/dromara/common/social/maxkey/AuthMaxKeySource.java RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-social/src/main/java/org/dromara/common/social/topiam/AuthTopIamRequest.java RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-social/src/main/java/org/dromara/common/social/topiam/AuthTopIamSource.java RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-social/src/main/java/org/dromara/common/social/utils/AuthRedisStateCache.java RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-social/src/main/java/org/dromara/common/social/utils/SocialUtils.java RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-social/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-sse/.flattened-pom.xml RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-sse/pom.xml RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-sse/src/main/java/org/dromara/common/sse/config/SseAutoConfiguration.java RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-sse/src/main/java/org/dromara/common/sse/config/SseProperties.java RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-sse/src/main/java/org/dromara/common/sse/controller/SseController.java RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-sse/src/main/java/org/dromara/common/sse/core/SseEmitterManager.java RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-sse/src/main/java/org/dromara/common/sse/dto/SseMessageDto.java RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-sse/src/main/java/org/dromara/common/sse/listener/SseTopicListener.java RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-sse/src/main/java/org/dromara/common/sse/utils/SseMessageUtils.java RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-sse/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-tenant/.flattened-pom.xml RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-tenant/pom.xml RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-tenant/src/main/java/org/dromara/common/tenant/config/TenantConfig.java RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-tenant/src/main/java/org/dromara/common/tenant/core/TenantEntity.java RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-tenant/src/main/java/org/dromara/common/tenant/core/TenantSaTokenDao.java RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-tenant/src/main/java/org/dromara/common/tenant/exception/TenantException.java RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-tenant/src/main/java/org/dromara/common/tenant/handle/PlusTenantLineHandler.java RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-tenant/src/main/java/org/dromara/common/tenant/handle/TenantKeyPrefixHandler.java RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-tenant/src/main/java/org/dromara/common/tenant/helper/TenantHelper.java RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-tenant/src/main/java/org/dromara/common/tenant/manager/TenantSpringCacheManager.java RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-tenant/src/main/java/org/dromara/common/tenant/properties/TenantProperties.java RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-tenant/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-translation/.flattened-pom.xml RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-translation/pom.xml RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-translation/src/main/java/org/dromara/common/translation/annotation/Translation.java RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-translation/src/main/java/org/dromara/common/translation/annotation/TranslationType.java RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-translation/src/main/java/org/dromara/common/translation/config/TranslationConfig.java RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-translation/src/main/java/org/dromara/common/translation/constant/TransConstant.java RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-translation/src/main/java/org/dromara/common/translation/core/TranslationInterface.java RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-translation/src/main/java/org/dromara/common/translation/core/handler/TranslationBeanSerializerModifier.java RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-translation/src/main/java/org/dromara/common/translation/core/handler/TranslationHandler.java RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-translation/src/main/java/org/dromara/common/translation/core/impl/DeptNameTranslationImpl.java RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-translation/src/main/java/org/dromara/common/translation/core/impl/DictTypeTranslationImpl.java RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-translation/src/main/java/org/dromara/common/translation/core/impl/NicknameTranslationImpl.java RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-translation/src/main/java/org/dromara/common/translation/core/impl/OssUrlTranslationImpl.java RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-translation/src/main/java/org/dromara/common/translation/core/impl/UserNameTranslationImpl.java RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-translation/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-web/.flattened-pom.xml RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-web/pom.xml RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/config/CaptchaConfig.java RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/config/FilterConfig.java RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/config/I18nConfig.java RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/config/ResourcesConfig.java RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/config/UndertowConfig.java RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/config/properties/CaptchaProperties.java RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/config/properties/XssProperties.java RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/core/BaseController.java RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/core/I18nLocaleResolver.java RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/core/WaveAndCircleCaptcha.java RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/filter/RepeatableFilter.java RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/filter/RepeatedlyRequestWrapper.java RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/filter/XssFilter.java RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/filter/XssHttpServletRequestWrapper.java RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/handler/GlobalExceptionHandler.java RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/interceptor/PlusWebInvokeTimeInterceptor.java RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-web/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-websocket/.flattened-pom.xml RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-websocket/pom.xml RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-websocket/src/main/java/org/dromara/common/websocket/config/WebSocketConfig.java RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-websocket/src/main/java/org/dromara/common/websocket/config/properties/WebSocketProperties.java RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-websocket/src/main/java/org/dromara/common/websocket/constant/WebSocketConstants.java RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-websocket/src/main/java/org/dromara/common/websocket/dto/WebSocketMessageDto.java RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-websocket/src/main/java/org/dromara/common/websocket/handler/PlusWebSocketHandler.java RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-websocket/src/main/java/org/dromara/common/websocket/holder/WebSocketSessionHolder.java RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-websocket/src/main/java/org/dromara/common/websocket/interceptor/PlusWebSocketInterceptor.java RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-websocket/src/main/java/org/dromara/common/websocket/listener/WebSocketTopicListener.java RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-websocket/src/main/java/org/dromara/common/websocket/utils/WebSocketUtils.java RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-websocket/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports RuoYi-Vue-Plus/ruoyi-extend/.DS_Store RuoYi-Vue-Plus/ruoyi-extend/.flattened-pom.xml RuoYi-Vue-Plus/ruoyi-extend/pom.xml RuoYi-Vue-Plus/ruoyi-extend/ruoyi-monitor-admin/.flattened-pom.xml RuoYi-Vue-Plus/ruoyi-extend/ruoyi-monitor-admin/Dockerfile RuoYi-Vue-Plus/ruoyi-extend/ruoyi-monitor-admin/pom.xml RuoYi-Vue-Plus/ruoyi-extend/ruoyi-monitor-admin/src/main/java/org/dromara/monitor/admin/MonitorAdminApplication.java RuoYi-Vue-Plus/ruoyi-extend/ruoyi-monitor-admin/src/main/java/org/dromara/monitor/admin/config/SecurityConfig.java RuoYi-Vue-Plus/ruoyi-extend/ruoyi-monitor-admin/src/main/java/org/dromara/monitor/admin/notifier/CustomNotifier.java RuoYi-Vue-Plus/ruoyi-extend/ruoyi-monitor-admin/src/main/resources/application.yml RuoYi-Vue-Plus/ruoyi-extend/ruoyi-monitor-admin/src/main/resources/banner.txt RuoYi-Vue-Plus/ruoyi-extend/ruoyi-monitor-admin/src/main/resources/logback-plus.xml RuoYi-Vue-Plus/ruoyi-extend/ruoyi-snailjob-server/.flattened-pom.xml RuoYi-Vue-Plus/ruoyi-extend/ruoyi-snailjob-server/Dockerfile RuoYi-Vue-Plus/ruoyi-extend/ruoyi-snailjob-server/pom.xml RuoYi-Vue-Plus/ruoyi-extend/ruoyi-snailjob-server/src/main/java/com/aizuda/snailjob/server/starter/filter/ActuatorAuthFilter.java RuoYi-Vue-Plus/ruoyi-extend/ruoyi-snailjob-server/src/main/java/com/aizuda/snailjob/server/starter/filter/SecurityConfig.java RuoYi-Vue-Plus/ruoyi-extend/ruoyi-snailjob-server/src/main/java/org/dromara/snailjob/SnailJobServerApplication.java RuoYi-Vue-Plus/ruoyi-extend/ruoyi-snailjob-server/src/main/resources/application-dev.yml RuoYi-Vue-Plus/ruoyi-extend/ruoyi-snailjob-server/src/main/resources/application-prod.yml RuoYi-Vue-Plus/ruoyi-extend/ruoyi-snailjob-server/src/main/resources/application.yml RuoYi-Vue-Plus/ruoyi-extend/ruoyi-snailjob-server/src/main/resources/banner.txt RuoYi-Vue-Plus/ruoyi-extend/ruoyi-snailjob-server/src/main/resources/logback-plus.xml RuoYi-Vue-Plus/ruoyi-modules/.DS_Store RuoYi-Vue-Plus/ruoyi-modules/.flattened-pom.xml RuoYi-Vue-Plus/ruoyi-modules/pom.xml RuoYi-Vue-Plus/ruoyi-modules/ruoyi-demo/.flattened-pom.xml RuoYi-Vue-Plus/ruoyi-modules/ruoyi-demo/pom.xml RuoYi-Vue-Plus/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/controller/MailSendController.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/controller/RedisCacheController.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/controller/RedisLockController.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/controller/RedisPubSubController.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/controller/RedisRateLimiterController.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/controller/SmsController.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/controller/Swagger3DemoController.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/controller/TestBatchController.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/controller/TestDemoController.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/controller/TestEncryptController.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/controller/TestExcelController.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/controller/TestI18nController.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/controller/TestSensitiveController.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/controller/TestTreeController.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/controller/WebSocketController.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/controller/package-info.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/controller/queue/BoundedQueueController.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/controller/queue/DelayedQueueController.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/controller/queue/PriorityDemo.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/controller/queue/PriorityQueueController.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/domain/TestDemo.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/domain/TestDemoEncrypt.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/domain/TestTree.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/domain/bo/TestDemoBo.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/domain/bo/TestDemoImportVo.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/domain/bo/TestTreeBo.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/domain/package-info.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/domain/vo/ExportDemoVo.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/domain/vo/TestDemoVo.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/domain/vo/TestTreeVo.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/listener/ExportDemoListener.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/mapper/TestDemoEncryptMapper.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/mapper/TestDemoMapper.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/mapper/TestTreeMapper.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/mapper/package-info.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/service/IExportExcelService.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/service/ITestDemoService.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/service/ITestTreeService.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/service/impl/ExportExcelServiceImpl.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/service/impl/TestDemoServiceImpl.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/service/impl/TestTreeServiceImpl.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/service/impl/package-info.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/service/package-info.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-demo/src/main/resources/excel/单列表.xlsx RuoYi-Vue-Plus/ruoyi-modules/ruoyi-demo/src/main/resources/excel/多sheet列表.xlsx RuoYi-Vue-Plus/ruoyi-modules/ruoyi-demo/src/main/resources/excel/多列表.xlsx RuoYi-Vue-Plus/ruoyi-modules/ruoyi-demo/src/main/resources/mapper/demo/TestDemoMapper.xml RuoYi-Vue-Plus/ruoyi-modules/ruoyi-demo/src/main/resources/mapper/demo/TestTreeMapper.xml RuoYi-Vue-Plus/ruoyi-modules/ruoyi-demo/src/main/resources/mapper/package-info.md RuoYi-Vue-Plus/ruoyi-modules/ruoyi-generator/.flattened-pom.xml RuoYi-Vue-Plus/ruoyi-modules/ruoyi-generator/pom.xml RuoYi-Vue-Plus/ruoyi-modules/ruoyi-generator/src/main/java/org/dromara/generator/config/GenConfig.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-generator/src/main/java/org/dromara/generator/config/MyBatisDataSourceMonitor.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-generator/src/main/java/org/dromara/generator/constant/GenConstants.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-generator/src/main/java/org/dromara/generator/controller/GenController.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-generator/src/main/java/org/dromara/generator/domain/GenTable.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-generator/src/main/java/org/dromara/generator/domain/GenTableColumn.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-generator/src/main/java/org/dromara/generator/mapper/GenTableColumnMapper.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-generator/src/main/java/org/dromara/generator/mapper/GenTableMapper.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-generator/src/main/java/org/dromara/generator/service/GenTableServiceImpl.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-generator/src/main/java/org/dromara/generator/service/IGenTableService.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-generator/src/main/java/org/dromara/generator/util/GenUtils.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-generator/src/main/java/org/dromara/generator/util/VelocityInitializer.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-generator/src/main/java/org/dromara/generator/util/VelocityUtils.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-generator/src/main/java/org/dromara/generator/util/VelocityUtils2.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-generator/src/main/resources/.DS_Store RuoYi-Vue-Plus/ruoyi-modules/ruoyi-generator/src/main/resources/generator.yml RuoYi-Vue-Plus/ruoyi-modules/ruoyi-generator/src/main/resources/mapper/generator/GenTableColumnMapper.xml RuoYi-Vue-Plus/ruoyi-modules/ruoyi-generator/src/main/resources/mapper/generator/GenTableMapper.xml RuoYi-Vue-Plus/ruoyi-modules/ruoyi-generator/src/main/resources/mapper/package-info.md RuoYi-Vue-Plus/ruoyi-modules/ruoyi-generator/src/main/resources/vm/.DS_Store RuoYi-Vue-Plus/ruoyi-modules/ruoyi-generator/src/main/resources/vm/java/bo.java.vm RuoYi-Vue-Plus/ruoyi-modules/ruoyi-generator/src/main/resources/vm/java/controller.java.vm RuoYi-Vue-Plus/ruoyi-modules/ruoyi-generator/src/main/resources/vm/java/domain.java.vm RuoYi-Vue-Plus/ruoyi-modules/ruoyi-generator/src/main/resources/vm/java/mapper.java.vm RuoYi-Vue-Plus/ruoyi-modules/ruoyi-generator/src/main/resources/vm/java/service.java.vm RuoYi-Vue-Plus/ruoyi-modules/ruoyi-generator/src/main/resources/vm/java/serviceImpl.java.vm RuoYi-Vue-Plus/ruoyi-modules/ruoyi-generator/src/main/resources/vm/java/vo.java.vm RuoYi-Vue-Plus/ruoyi-modules/ruoyi-generator/src/main/resources/vm/soy/api/api.ts.vm RuoYi-Vue-Plus/ruoyi-modules/ruoyi-generator/src/main/resources/vm/soy/index-tree.vue.vm RuoYi-Vue-Plus/ruoyi-modules/ruoyi-generator/src/main/resources/vm/soy/index.vue.vm RuoYi-Vue-Plus/ruoyi-modules/ruoyi-generator/src/main/resources/vm/soy/modules/operate-drawer.vue.vm RuoYi-Vue-Plus/ruoyi-modules/ruoyi-generator/src/main/resources/vm/soy/modules/search.vue.vm RuoYi-Vue-Plus/ruoyi-modules/ruoyi-generator/src/main/resources/vm/soy/typings/api.d.ts.vm RuoYi-Vue-Plus/ruoyi-modules/ruoyi-generator/src/main/resources/vm/sql/oracle/sql.vm RuoYi-Vue-Plus/ruoyi-modules/ruoyi-generator/src/main/resources/vm/sql/postgres/sql.vm RuoYi-Vue-Plus/ruoyi-modules/ruoyi-generator/src/main/resources/vm/sql/sql.vm RuoYi-Vue-Plus/ruoyi-modules/ruoyi-generator/src/main/resources/vm/sql/sqlserver/sql.vm RuoYi-Vue-Plus/ruoyi-modules/ruoyi-generator/src/main/resources/vm/ts/api.ts.vm RuoYi-Vue-Plus/ruoyi-modules/ruoyi-generator/src/main/resources/vm/ts/types.ts.vm RuoYi-Vue-Plus/ruoyi-modules/ruoyi-generator/src/main/resources/vm/vue/index-tree.vue.vm RuoYi-Vue-Plus/ruoyi-modules/ruoyi-generator/src/main/resources/vm/vue/index.vue.vm RuoYi-Vue-Plus/ruoyi-modules/ruoyi-generator/src/main/resources/vm/xml/mapper.xml.vm RuoYi-Vue-Plus/ruoyi-modules/ruoyi-job/.flattened-pom.xml RuoYi-Vue-Plus/ruoyi-modules/ruoyi-job/pom.xml RuoYi-Vue-Plus/ruoyi-modules/ruoyi-job/src/main/java/org/dromara/job/entity/BillDto.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-job/src/main/java/org/dromara/job/package-info.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-job/src/main/java/org/dromara/job/snailjob/AlipayBillTask.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-job/src/main/java/org/dromara/job/snailjob/SummaryBillTask.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-job/src/main/java/org/dromara/job/snailjob/TestAnnoJobExecutor.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-job/src/main/java/org/dromara/job/snailjob/TestBroadcastJob.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-job/src/main/java/org/dromara/job/snailjob/TestClassJobExecutor.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-job/src/main/java/org/dromara/job/snailjob/TestMapJobAnnotation.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-job/src/main/java/org/dromara/job/snailjob/TestMapReduceAnnotation1.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-job/src/main/java/org/dromara/job/snailjob/TestStaticShardingJob.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-job/src/main/java/org/dromara/job/snailjob/WechatBillTask.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/.flattened-pom.xml RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/pom.xml RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/readMe.md RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/.DS_Store RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/.DS_Store RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/.DS_Store RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/controller/HoisterTimeDataController.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/controller/PackerTimeDataController.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/controller/RollerTimeDataController.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/domain/BoxTimeData.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/domain/FeedmatchTimeData.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/domain/HoisterTimeData.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/domain/MakeupTimeData.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/domain/PackerTimeData.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/domain/RollerTimeData.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/domain/TransTimeData.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/domain/bo/HoisterTimeDataBo.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/domain/bo/PackerTimeDataBo.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/domain/bo/RollerTimeDataBo.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/domain/vo/HoisterTimeDataVo.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/domain/vo/PackerTimeDataVo.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/domain/vo/RollerTimeDataVo.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/mapper/HoisterTimeDataMapper.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/mapper/PackerTimeDataMapper.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/mapper/RollerTimeDataMapper.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/service/IHoisterTimeDataService.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/service/IPackerTimeDataService.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/service/IRollerTimeDataService.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/service/impl/HoisterTimeDataServiceImpl.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/service/impl/PackerTimeDataServiceImpl.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/java/org/dromara/qa/service/impl/RollerTimeDataServiceImpl.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/resources/mapper/qa/PackerTimeDataMapper.xml RuoYi-Vue-Plus/ruoyi-modules/ruoyi-qa/src/main/resources/mapper/qa/RollerTimeDataMapper.xml RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/.flattened-pom.xml RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/pom.xml RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/monitor/CacheController.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/monitor/SysLogininforController.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/monitor/SysOperlogController.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/monitor/SysUserOnlineController.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysClientController.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysConfigController.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysDeptController.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysDictDataController.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysDictTypeController.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysMenuController.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysNoticeController.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysOssConfigController.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysOssController.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysPostController.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysProfileController.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysRoleController.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysSocialController.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysTenantController.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysTenantPackageController.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysUserController.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/SysCache.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/SysClient.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/SysConfig.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/SysDept.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/SysDictData.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/SysDictType.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/SysLogininfor.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/SysMenu.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/SysNotice.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/SysOperLog.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/SysOss.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/SysOssConfig.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/SysOssExt.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/SysPost.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/SysRole.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/SysRoleDept.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/SysRoleMenu.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/SysSocial.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/SysTenant.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/SysTenantPackage.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/SysUser.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/SysUserOnline.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/SysUserPost.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/SysUserRole.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/SysClientBo.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/SysConfigBo.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/SysDeptBo.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/SysDictDataBo.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/SysDictTypeBo.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/SysLogininforBo.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/SysMenuBo.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/SysNoticeBo.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/SysOperLogBo.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/SysOssBo.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/SysOssConfigBo.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/SysPostBo.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/SysRoleBo.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/SysSocialBo.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/SysTenantBo.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/SysTenantPackageBo.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/SysUserBo.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/SysUserPasswordBo.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/SysUserProfileBo.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/MetaVo.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/ProfileUserVo.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/RouterVo.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysClientVo.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysConfigVo.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysDeptVo.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysDictDataVo.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysDictTypeVo.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysLogininforVo.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysMenuVo.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysNoticeVo.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysOperLogVo.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysOssConfigVo.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysOssUploadVo.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysOssVo.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysPostVo.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysRoleVo.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysSocialVo.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysTenantPackageVo.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysTenantVo.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysUserExportVo.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysUserImportVo.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysUserInfoVo.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysUserVo.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/UserInfoVo.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/listener/SysUserImportListener.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysClientMapper.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysConfigMapper.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysDeptMapper.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysDictDataMapper.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysDictTypeMapper.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysLogininforMapper.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysMenuMapper.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysNoticeMapper.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysOperLogMapper.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysOssConfigMapper.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysOssMapper.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysPostMapper.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysRoleDeptMapper.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysRoleMapper.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysRoleMenuMapper.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysSocialMapper.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysTenantMapper.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysTenantPackageMapper.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysUserMapper.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysUserPostMapper.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysUserRoleMapper.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/runner/SystemApplicationRunner.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysClientService.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysConfigService.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysDataScopeService.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysDeptService.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysDictDataService.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysDictTypeService.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysLogininforService.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysMenuService.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysNoticeService.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysOperLogService.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysOssConfigService.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysOssService.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysPermissionService.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysPostService.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysRoleService.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysSocialService.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysTenantPackageService.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysTenantService.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysUserService.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysClientServiceImpl.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysConfigServiceImpl.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysDataScopeServiceImpl.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysDeptServiceImpl.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysDictDataServiceImpl.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysDictTypeServiceImpl.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysLogininforServiceImpl.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysMenuServiceImpl.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysNoticeServiceImpl.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysOperLogServiceImpl.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysOssConfigServiceImpl.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysOssServiceImpl.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysPermissionServiceImpl.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysPostServiceImpl.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysRoleServiceImpl.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysSensitiveServiceImpl.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysSocialServiceImpl.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysTaskAssigneeServiceImpl.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysTenantPackageServiceImpl.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysTenantServiceImpl.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysUserServiceImpl.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/resources/mapper/package-info.md RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysClientMapper.xml RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysConfigMapper.xml RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysDeptMapper.xml RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysDictDataMapper.xml RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysDictTypeMapper.xml RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysLogininforMapper.xml RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysMenuMapper.xml RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysNoticeMapper.xml RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysOperLogMapper.xml RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysOssConfigMapper.xml RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysOssMapper.xml RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysPostMapper.xml RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysRoleDeptMapper.xml RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysRoleMapper.xml RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysRoleMenuMapper.xml RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysSocialMapper.xml RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysTenantMapper.xml RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysTenantPackageMapper.xml RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysUserMapper.xml RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysUserPostMapper.xml RuoYi-Vue-Plus/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysUserRoleMapper.xml RuoYi-Vue-Plus/ruoyi-modules/ruoyi-workflow/.flattened-pom.xml RuoYi-Vue-Plus/ruoyi-modules/ruoyi-workflow/pom.xml RuoYi-Vue-Plus/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/common/ConditionalOnEnable.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/common/constant/FlowConstant.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/common/enums/ButtonPermissionEnum.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/common/enums/CopySettingEnum.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/common/enums/MessageTypeEnum.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/common/enums/NodeExtEnum.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/common/enums/TaskAssigneeEnum.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/common/enums/TaskAssigneeType.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/common/enums/TaskStatusEnum.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/common/enums/VariablesEnum.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/config/WarmFlowConfig.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/controller/FlwCategoryController.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/controller/FlwDefinitionController.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/controller/FlwInstanceController.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/controller/FlwSpelController.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/controller/FlwTaskController.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/controller/TestLeaveController.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/FlowCategory.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/FlowInstanceBizExt.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/FlowSpel.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/TestLeave.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/BackProcessBo.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/CompleteTaskBo.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/FlowCancelBo.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/FlowCategoryBo.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/FlowCopyBo.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/FlowInstanceBo.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/FlowInvalidBo.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/FlowNextNodeBo.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/FlowSpelBo.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/FlowTaskBo.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/FlowTerminationBo.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/FlowUrgeTaskBo.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/FlowVariableBo.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/StartProcessBo.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/TaskOperationBo.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/TestLeaveBo.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/ButtonPermissionVo.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/FlowCategoryVo.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/FlowCopyVo.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/FlowDefinitionVo.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/FlowHisTaskVo.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/FlowInstanceVo.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/FlowSpelVo.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/FlowTaskVo.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/NodeExtVo.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/TestLeaveVo.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/handler/FlowProcessEventHandler.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/handler/WorkflowPermissionHandler.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/listener/WorkflowGlobalListener.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/mapper/FlwCategoryMapper.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/mapper/FlwInstanceBizExtMapper.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/mapper/FlwInstanceMapper.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/mapper/FlwSpelMapper.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/mapper/FlwTaskMapper.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/mapper/TestLeaveMapper.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/rule/SpelRuleComponent.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IFlwCategoryService.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IFlwCommonService.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IFlwDefinitionService.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IFlwInstanceService.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IFlwNodeExtService.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IFlwSpelService.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IFlwTaskAssigneeService.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IFlwTaskService.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/ITestLeaveService.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/CategoryNameTranslationImpl.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwCategoryServiceImpl.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwChartExtServiceImpl.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwCommonServiceImpl.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwDefinitionServiceImpl.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwInstanceServiceImpl.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwNodeExtServiceImpl.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwSpelServiceImpl.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwTaskAssigneeServiceImpl.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwTaskServiceImpl.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/TestLeaveServiceImpl.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/WorkflowServiceImpl.java RuoYi-Vue-Plus/ruoyi-modules/ruoyi-workflow/src/main/resources/mapper/package-info.md RuoYi-Vue-Plus/ruoyi-modules/ruoyi-workflow/src/main/resources/mapper/workflow/FlwCategoryMapper.xml RuoYi-Vue-Plus/ruoyi-modules/ruoyi-workflow/src/main/resources/mapper/workflow/FlwInstanceBizExtMapper.xml RuoYi-Vue-Plus/ruoyi-modules/ruoyi-workflow/src/main/resources/mapper/workflow/FlwInstanceMapper.xml RuoYi-Vue-Plus/ruoyi-modules/ruoyi-workflow/src/main/resources/mapper/workflow/FlwSpelMapper.xml RuoYi-Vue-Plus/ruoyi-modules/ruoyi-workflow/src/main/resources/mapper/workflow/FlwTaskMapper.xml RuoYi-Vue-Plus/ruoyi-modules/ruoyi-workflow/src/main/resources/mapper/workflow/TestLeaveMapper.xml RuoYi-Vue-Plus/script/bin/ry.bat RuoYi-Vue-Plus/script/bin/ry.sh RuoYi-Vue-Plus/script/docker/database.yml RuoYi-Vue-Plus/script/docker/docker-compose.yml RuoYi-Vue-Plus/script/docker/nginx/conf/nginx.conf RuoYi-Vue-Plus/script/docker/redis/conf/redis.conf RuoYi-Vue-Plus/script/docker/redis/data/README.md RuoYi-Vue-Plus/script/leave/leave1.json RuoYi-Vue-Plus/script/leave/leave2.json RuoYi-Vue-Plus/script/leave/leave3.json RuoYi-Vue-Plus/script/leave/leave4.json RuoYi-Vue-Plus/script/leave/leave5.json RuoYi-Vue-Plus/script/leave/leave6.json RuoYi-Vue-Plus/script/sql/oracle/oracle_ry_job.sql RuoYi-Vue-Plus/script/sql/oracle/oracle_ry_vue_5.X.sql RuoYi-Vue-Plus/script/sql/oracle/oracle_ry_workflow.sql RuoYi-Vue-Plus/script/sql/postgres/postgres_ry_job.sql RuoYi-Vue-Plus/script/sql/postgres/postgres_ry_vue_5.X.sql RuoYi-Vue-Plus/script/sql/postgres/postgres_ry_workflow.sql RuoYi-Vue-Plus/script/sql/ry_job.sql RuoYi-Vue-Plus/script/sql/ry_vue_5.X.sql RuoYi-Vue-Plus/script/sql/ry_workflow.sql RuoYi-Vue-Plus/script/sql/sqlserver/sqlserver_ry_job.sql RuoYi-Vue-Plus/script/sql/sqlserver/sqlserver_ry_vue_5.X.sql RuoYi-Vue-Plus/script/sql/sqlserver/sqlserver_ry_workflow.sql RuoYi-Vue-Plus/script/sql/update/oracle/update_5.0-5.1.sql RuoYi-Vue-Plus/script/sql/update/oracle/update_5.1.0-5.1.1.sql RuoYi-Vue-Plus/script/sql/update/oracle/update_5.1.1-5.1.2.sql RuoYi-Vue-Plus/script/sql/update/oracle/update_5.1.2-5.2.0.sql RuoYi-Vue-Plus/script/sql/update/oracle/update_5.3.0-5.3.1.sql RuoYi-Vue-Plus/script/sql/update/oracle/update_5.3.1-5.4.0.sql RuoYi-Vue-Plus/script/sql/update/oracle/update_5.4.1-5.5.0.sql RuoYi-Vue-Plus/script/sql/update/oracle/update_5.5.0-5.5.1.sql RuoYi-Vue-Plus/script/sql/update/oracle/update_5.5.1-5.5.2.sql RuoYi-Vue-Plus/script/sql/update/postgres/update_5.0-5.1.sql RuoYi-Vue-Plus/script/sql/update/postgres/update_5.1.0-5.1.1.sql RuoYi-Vue-Plus/script/sql/update/postgres/update_5.1.1-5.1.2.sql RuoYi-Vue-Plus/script/sql/update/postgres/update_5.1.2-5.2.0.sql RuoYi-Vue-Plus/script/sql/update/postgres/update_5.3.0-5.3.1.sql RuoYi-Vue-Plus/script/sql/update/postgres/update_5.3.1-5.4.0.sql RuoYi-Vue-Plus/script/sql/update/postgres/update_5.4.1-5.5.0.sql RuoYi-Vue-Plus/script/sql/update/postgres/update_5.5.0-5.5.1.sql RuoYi-Vue-Plus/script/sql/update/postgres/update_5.5.1-5.5.2.sql RuoYi-Vue-Plus/script/sql/update/sqlserver/update_5.0-5.1.sql RuoYi-Vue-Plus/script/sql/update/sqlserver/update_5.1.0-5.1.1.sql RuoYi-Vue-Plus/script/sql/update/sqlserver/update_5.1.1-5.1.2.sql RuoYi-Vue-Plus/script/sql/update/sqlserver/update_5.1.2-5.2.0.sql RuoYi-Vue-Plus/script/sql/update/sqlserver/update_5.3.0-5.3.1.sql RuoYi-Vue-Plus/script/sql/update/sqlserver/update_5.3.1-5.4.0.sql RuoYi-Vue-Plus/script/sql/update/sqlserver/update_5.4.1-5.5.0.sql RuoYi-Vue-Plus/script/sql/update/sqlserver/update_5.5.0-5.5.1.sql RuoYi-Vue-Plus/script/sql/update/sqlserver/update_5.5.1-5.5.2.sql RuoYi-Vue-Plus/script/sql/update/update_5.0-5.1.sql RuoYi-Vue-Plus/script/sql/update/update_5.1.0-5.1.1.sql RuoYi-Vue-Plus/script/sql/update/update_5.1.1-5.1.2.sql RuoYi-Vue-Plus/script/sql/update/update_5.1.2-5.2.0.sql RuoYi-Vue-Plus/script/sql/update/update_5.3.0-5.3.1.sql RuoYi-Vue-Plus/script/sql/update/update_5.3.1-5.4.0.sql RuoYi-Vue-Plus/script/sql/update/update_5.4.1-5.5.0.sql RuoYi-Vue-Plus/script/sql/update/update_5.5.0-5.5.1.sql RuoYi-Vue-Plus/script/sql/update/update_5.5.1-5.5.2.sql RuoYi-Vue-Plus/开发日志.md ruoyi-plus-soybean/.drone.yml ruoyi-plus-soybean/.editorconfig ruoyi-plus-soybean/.env ruoyi-plus-soybean/.env.dev ruoyi-plus-soybean/.env.prod ruoyi-plus-soybean/.env.test ruoyi-plus-soybean/.gitattributes ruoyi-plus-soybean/.gitignore ruoyi-plus-soybean/.npmrc ruoyi-plus-soybean/CHANGELOG.md ruoyi-plus-soybean/LICENSE ruoyi-plus-soybean/README.md ruoyi-plus-soybean/build/config/index.ts ruoyi-plus-soybean/build/config/proxy.ts ruoyi-plus-soybean/build/config/time.ts ruoyi-plus-soybean/build/plugins/devtools.ts ruoyi-plus-soybean/build/plugins/html.ts ruoyi-plus-soybean/build/plugins/index.ts ruoyi-plus-soybean/build/plugins/router.ts ruoyi-plus-soybean/build/plugins/unocss.ts ruoyi-plus-soybean/build/plugins/unplugin.ts ruoyi-plus-soybean/docs/README.md ruoyi-plus-soybean/docs/java/VelocityUtils.java ruoyi-plus-soybean/docs/sql/sys_dict_data.sql ruoyi-plus-soybean/docs/sql/sys_menu.sql ruoyi-plus-soybean/docs/template/api/api.ts.vm ruoyi-plus-soybean/docs/template/index-tree.vue.vm ruoyi-plus-soybean/docs/template/index.vue.vm ruoyi-plus-soybean/docs/template/modules/operate-drawer.vue.vm ruoyi-plus-soybean/docs/template/modules/search.vue.vm ruoyi-plus-soybean/docs/template/typings/api.d.ts.vm ruoyi-plus-soybean/eslint.config.js ruoyi-plus-soybean/index.html ruoyi-plus-soybean/package.json ruoyi-plus-soybean/packages/alova/package.json ruoyi-plus-soybean/packages/alova/src/client.ts ruoyi-plus-soybean/packages/alova/src/constant.ts ruoyi-plus-soybean/packages/alova/src/fetch.ts ruoyi-plus-soybean/packages/alova/src/index.ts ruoyi-plus-soybean/packages/alova/src/mock.ts ruoyi-plus-soybean/packages/alova/src/type.ts ruoyi-plus-soybean/packages/alova/tsconfig.json ruoyi-plus-soybean/packages/axios/package.json ruoyi-plus-soybean/packages/axios/src/constant.ts ruoyi-plus-soybean/packages/axios/src/index.ts ruoyi-plus-soybean/packages/axios/src/options.ts ruoyi-plus-soybean/packages/axios/src/shared.ts ruoyi-plus-soybean/packages/axios/src/type.ts ruoyi-plus-soybean/packages/axios/tsconfig.json ruoyi-plus-soybean/packages/color/package.json ruoyi-plus-soybean/packages/color/src/constant/index.ts ruoyi-plus-soybean/packages/color/src/constant/name.ts ruoyi-plus-soybean/packages/color/src/constant/palette.ts ruoyi-plus-soybean/packages/color/src/index.ts ruoyi-plus-soybean/packages/color/src/palette/antd.ts ruoyi-plus-soybean/packages/color/src/palette/index.ts ruoyi-plus-soybean/packages/color/src/palette/recommend.ts ruoyi-plus-soybean/packages/color/src/shared/colord.ts ruoyi-plus-soybean/packages/color/src/shared/index.ts ruoyi-plus-soybean/packages/color/src/shared/name.ts ruoyi-plus-soybean/packages/color/src/types/index.ts ruoyi-plus-soybean/packages/color/tsconfig.json ruoyi-plus-soybean/packages/hooks/package.json ruoyi-plus-soybean/packages/hooks/src/index.ts ruoyi-plus-soybean/packages/hooks/src/use-boolean.ts ruoyi-plus-soybean/packages/hooks/src/use-context.ts ruoyi-plus-soybean/packages/hooks/src/use-count-down.ts ruoyi-plus-soybean/packages/hooks/src/use-loading.ts ruoyi-plus-soybean/packages/hooks/src/use-request.ts ruoyi-plus-soybean/packages/hooks/src/use-svg-icon-render.ts ruoyi-plus-soybean/packages/hooks/src/use-table.ts ruoyi-plus-soybean/packages/hooks/tsconfig.json ruoyi-plus-soybean/packages/materials/package.json ruoyi-plus-soybean/packages/materials/src/index.ts ruoyi-plus-soybean/packages/materials/src/libs/admin-layout/index.module.css ruoyi-plus-soybean/packages/materials/src/libs/admin-layout/index.module.css.d.ts ruoyi-plus-soybean/packages/materials/src/libs/admin-layout/index.ts ruoyi-plus-soybean/packages/materials/src/libs/admin-layout/index.vue ruoyi-plus-soybean/packages/materials/src/libs/admin-layout/shared.ts ruoyi-plus-soybean/packages/materials/src/libs/page-tab/button-tab.vue ruoyi-plus-soybean/packages/materials/src/libs/page-tab/chrome-tab-bg.vue ruoyi-plus-soybean/packages/materials/src/libs/page-tab/chrome-tab.vue ruoyi-plus-soybean/packages/materials/src/libs/page-tab/index.module.css ruoyi-plus-soybean/packages/materials/src/libs/page-tab/index.module.css.d.ts ruoyi-plus-soybean/packages/materials/src/libs/page-tab/index.ts ruoyi-plus-soybean/packages/materials/src/libs/page-tab/index.vue ruoyi-plus-soybean/packages/materials/src/libs/page-tab/shared.ts ruoyi-plus-soybean/packages/materials/src/libs/page-tab/slider-tab.vue ruoyi-plus-soybean/packages/materials/src/libs/page-tab/svg-close.vue ruoyi-plus-soybean/packages/materials/src/libs/simple-scrollbar/index.ts ruoyi-plus-soybean/packages/materials/src/libs/simple-scrollbar/index.vue ruoyi-plus-soybean/packages/materials/src/types/index.ts ruoyi-plus-soybean/packages/materials/tsconfig.json ruoyi-plus-soybean/packages/scripts/bin.ts ruoyi-plus-soybean/packages/scripts/package.json ruoyi-plus-soybean/packages/scripts/src/commands/changelog.ts ruoyi-plus-soybean/packages/scripts/src/commands/cleanup.ts ruoyi-plus-soybean/packages/scripts/src/commands/git-commit.ts ruoyi-plus-soybean/packages/scripts/src/commands/index.ts ruoyi-plus-soybean/packages/scripts/src/commands/release.ts ruoyi-plus-soybean/packages/scripts/src/commands/router.ts ruoyi-plus-soybean/packages/scripts/src/commands/update-pkg.ts ruoyi-plus-soybean/packages/scripts/src/config/index.ts ruoyi-plus-soybean/packages/scripts/src/index.ts ruoyi-plus-soybean/packages/scripts/src/locales/index.ts ruoyi-plus-soybean/packages/scripts/src/shared/index.ts ruoyi-plus-soybean/packages/scripts/src/types/index.ts ruoyi-plus-soybean/packages/scripts/tsconfig.json ruoyi-plus-soybean/packages/uno-preset/package.json ruoyi-plus-soybean/packages/uno-preset/src/index.ts ruoyi-plus-soybean/packages/uno-preset/tsconfig.json ruoyi-plus-soybean/packages/utils/package.json ruoyi-plus-soybean/packages/utils/src/crypto.ts ruoyi-plus-soybean/packages/utils/src/index.ts ruoyi-plus-soybean/packages/utils/src/klona.ts ruoyi-plus-soybean/packages/utils/src/nanoid.ts ruoyi-plus-soybean/packages/utils/src/storage.ts ruoyi-plus-soybean/packages/utils/tsconfig.json ruoyi-plus-soybean/pnpm-lock.yaml ruoyi-plus-soybean/pnpm-workspace.yaml ruoyi-plus-soybean/public/favicon.svg ruoyi-plus-soybean/public/streamsaver/mitm.html ruoyi-plus-soybean/public/streamsaver/sw.js ruoyi-plus-soybean/src/App.vue ruoyi-plus-soybean/src/assets/imgs/soybean.jpg ruoyi-plus-soybean/src/assets/svg-icon/activity.svg ruoyi-plus-soybean/src/assets/svg-icon/at-sign.svg ruoyi-plus-soybean/src/assets/svg-icon/avatar.svg ruoyi-plus-soybean/src/assets/svg-icon/banner.svg ruoyi-plus-soybean/src/assets/svg-icon/bell.svg ruoyi-plus-soybean/src/assets/svg-icon/cast.svg ruoyi-plus-soybean/src/assets/svg-icon/chrome.svg ruoyi-plus-soybean/src/assets/svg-icon/copy.svg ruoyi-plus-soybean/src/assets/svg-icon/custom-icon.svg ruoyi-plus-soybean/src/assets/svg-icon/empty-data.svg ruoyi-plus-soybean/src/assets/svg-icon/expectation.svg ruoyi-plus-soybean/src/assets/svg-icon/heart.svg ruoyi-plus-soybean/src/assets/svg-icon/login-background.svg ruoyi-plus-soybean/src/assets/svg-icon/logo.svg ruoyi-plus-soybean/src/assets/svg-icon/maxkey.svg ruoyi-plus-soybean/src/assets/svg-icon/menu/404.svg ruoyi-plus-soybean/src/assets/svg-icon/menu/bug.svg ruoyi-plus-soybean/src/assets/svg-icon/menu/build.svg ruoyi-plus-soybean/src/assets/svg-icon/menu/button.svg ruoyi-plus-soybean/src/assets/svg-icon/menu/caret-back.svg ruoyi-plus-soybean/src/assets/svg-icon/menu/caret-forward.svg ruoyi-plus-soybean/src/assets/svg-icon/menu/cascader.svg ruoyi-plus-soybean/src/assets/svg-icon/menu/category.svg ruoyi-plus-soybean/src/assets/svg-icon/menu/chart.svg ruoyi-plus-soybean/src/assets/svg-icon/menu/checkbox.svg ruoyi-plus-soybean/src/assets/svg-icon/menu/clipboard.svg ruoyi-plus-soybean/src/assets/svg-icon/menu/code.svg ruoyi-plus-soybean/src/assets/svg-icon/menu/color.svg ruoyi-plus-soybean/src/assets/svg-icon/menu/company.svg ruoyi-plus-soybean/src/assets/svg-icon/menu/component.svg ruoyi-plus-soybean/src/assets/svg-icon/menu/dashboard.svg ruoyi-plus-soybean/src/assets/svg-icon/menu/date-range.svg ruoyi-plus-soybean/src/assets/svg-icon/menu/date.svg ruoyi-plus-soybean/src/assets/svg-icon/menu/dict.svg ruoyi-plus-soybean/src/assets/svg-icon/menu/documentation.svg ruoyi-plus-soybean/src/assets/svg-icon/menu/download.svg ruoyi-plus-soybean/src/assets/svg-icon/menu/drag.svg ruoyi-plus-soybean/src/assets/svg-icon/menu/druid.svg ruoyi-plus-soybean/src/assets/svg-icon/menu/edit.svg ruoyi-plus-soybean/src/assets/svg-icon/menu/education.svg ruoyi-plus-soybean/src/assets/svg-icon/menu/email.svg ruoyi-plus-soybean/src/assets/svg-icon/menu/example.svg ruoyi-plus-soybean/src/assets/svg-icon/menu/excel.svg ruoyi-plus-soybean/src/assets/svg-icon/menu/exit-fullscreen.svg ruoyi-plus-soybean/src/assets/svg-icon/menu/eye-open.svg ruoyi-plus-soybean/src/assets/svg-icon/menu/eye.svg ruoyi-plus-soybean/src/assets/svg-icon/menu/finish.svg ruoyi-plus-soybean/src/assets/svg-icon/menu/form.svg ruoyi-plus-soybean/src/assets/svg-icon/menu/fullscreen.svg ruoyi-plus-soybean/src/assets/svg-icon/menu/gitee.svg ruoyi-plus-soybean/src/assets/svg-icon/menu/github.svg ruoyi-plus-soybean/src/assets/svg-icon/menu/guide.svg ruoyi-plus-soybean/src/assets/svg-icon/menu/icon.svg ruoyi-plus-soybean/src/assets/svg-icon/menu/input.svg ruoyi-plus-soybean/src/assets/svg-icon/menu/international.svg ruoyi-plus-soybean/src/assets/svg-icon/menu/job.svg ruoyi-plus-soybean/src/assets/svg-icon/menu/language.svg ruoyi-plus-soybean/src/assets/svg-icon/menu/link.svg ruoyi-plus-soybean/src/assets/svg-icon/menu/list.svg ruoyi-plus-soybean/src/assets/svg-icon/menu/lock.svg ruoyi-plus-soybean/src/assets/svg-icon/menu/log.svg ruoyi-plus-soybean/src/assets/svg-icon/menu/logininfor.svg ruoyi-plus-soybean/src/assets/svg-icon/menu/message.svg ruoyi-plus-soybean/src/assets/svg-icon/menu/model.svg ruoyi-plus-soybean/src/assets/svg-icon/menu/money.svg ruoyi-plus-soybean/src/assets/svg-icon/menu/monitor.svg ruoyi-plus-soybean/src/assets/svg-icon/menu/my-copy.svg ruoyi-plus-soybean/src/assets/svg-icon/menu/my-task.svg ruoyi-plus-soybean/src/assets/svg-icon/menu/nested.svg ruoyi-plus-soybean/src/assets/svg-icon/menu/number.svg ruoyi-plus-soybean/src/assets/svg-icon/menu/online.svg ruoyi-plus-soybean/src/assets/svg-icon/menu/password.svg ruoyi-plus-soybean/src/assets/svg-icon/menu/pdf.svg ruoyi-plus-soybean/src/assets/svg-icon/menu/people.svg ruoyi-plus-soybean/src/assets/svg-icon/menu/peoples.svg ruoyi-plus-soybean/src/assets/svg-icon/menu/phone.svg ruoyi-plus-soybean/src/assets/svg-icon/menu/post.svg ruoyi-plus-soybean/src/assets/svg-icon/menu/process-definition.svg ruoyi-plus-soybean/src/assets/svg-icon/menu/qq.svg ruoyi-plus-soybean/src/assets/svg-icon/menu/question.svg ruoyi-plus-soybean/src/assets/svg-icon/menu/radio.svg ruoyi-plus-soybean/src/assets/svg-icon/menu/rate.svg ruoyi-plus-soybean/src/assets/svg-icon/menu/redis-list.svg ruoyi-plus-soybean/src/assets/svg-icon/menu/redis.svg ruoyi-plus-soybean/src/assets/svg-icon/menu/row.svg ruoyi-plus-soybean/src/assets/svg-icon/menu/search.svg ruoyi-plus-soybean/src/assets/svg-icon/menu/select.svg ruoyi-plus-soybean/src/assets/svg-icon/menu/server.svg ruoyi-plus-soybean/src/assets/svg-icon/menu/shopping.svg ruoyi-plus-soybean/src/assets/svg-icon/menu/size.svg ruoyi-plus-soybean/src/assets/svg-icon/menu/skill.svg ruoyi-plus-soybean/src/assets/svg-icon/menu/slider.svg ruoyi-plus-soybean/src/assets/svg-icon/menu/star.svg ruoyi-plus-soybean/src/assets/svg-icon/menu/swagger.svg ruoyi-plus-soybean/src/assets/svg-icon/menu/switch.svg ruoyi-plus-soybean/src/assets/svg-icon/menu/system.svg ruoyi-plus-soybean/src/assets/svg-icon/menu/tab.svg ruoyi-plus-soybean/src/assets/svg-icon/menu/table.svg ruoyi-plus-soybean/src/assets/svg-icon/menu/textarea.svg ruoyi-plus-soybean/src/assets/svg-icon/menu/theme.svg ruoyi-plus-soybean/src/assets/svg-icon/menu/time-range.svg ruoyi-plus-soybean/src/assets/svg-icon/menu/time.svg ruoyi-plus-soybean/src/assets/svg-icon/menu/tool.svg ruoyi-plus-soybean/src/assets/svg-icon/menu/tree-table.svg ruoyi-plus-soybean/src/assets/svg-icon/menu/tree.svg ruoyi-plus-soybean/src/assets/svg-icon/menu/upload.svg ruoyi-plus-soybean/src/assets/svg-icon/menu/user.svg ruoyi-plus-soybean/src/assets/svg-icon/menu/validCode.svg ruoyi-plus-soybean/src/assets/svg-icon/menu/waiting.svg ruoyi-plus-soybean/src/assets/svg-icon/menu/wechat.svg ruoyi-plus-soybean/src/assets/svg-icon/menu/workflow.svg ruoyi-plus-soybean/src/assets/svg-icon/menu/zip.svg ruoyi-plus-soybean/src/assets/svg-icon/network-error.svg ruoyi-plus-soybean/src/assets/svg-icon/no-icon.svg ruoyi-plus-soybean/src/assets/svg-icon/no-permission.svg ruoyi-plus-soybean/src/assets/svg-icon/not-found.svg ruoyi-plus-soybean/src/assets/svg-icon/service-error.svg ruoyi-plus-soybean/src/assets/svg-icon/topiam.svg ruoyi-plus-soybean/src/assets/svg-icon/wind.svg ruoyi-plus-soybean/src/components/advanced/table-column-setting.vue ruoyi-plus-soybean/src/components/advanced/table-header-operation.vue ruoyi-plus-soybean/src/components/advanced/table-row-check-alert.vue ruoyi-plus-soybean/src/components/advanced/table-sider-layout.vue ruoyi-plus-soybean/src/components/common/app-provider.vue ruoyi-plus-soybean/src/components/common/dark-mode-container.vue ruoyi-plus-soybean/src/components/common/data-table.vue ruoyi-plus-soybean/src/components/common/exception-base.vue ruoyi-plus-soybean/src/components/common/full-screen.vue ruoyi-plus-soybean/src/components/common/icon-tooltip.vue ruoyi-plus-soybean/src/components/common/lang-switch.vue ruoyi-plus-soybean/src/components/common/menu-toggler.vue ruoyi-plus-soybean/src/components/common/pin-toggler.vue ruoyi-plus-soybean/src/components/common/reload-button.vue ruoyi-plus-soybean/src/components/common/system-logo.vue ruoyi-plus-soybean/src/components/common/theme-schema-switch.vue ruoyi-plus-soybean/src/components/custom/better-scroll.vue ruoyi-plus-soybean/src/components/custom/boolean-tag.vue ruoyi-plus-soybean/src/components/custom/button-icon.vue ruoyi-plus-soybean/src/components/custom/count-to.vue ruoyi-plus-soybean/src/components/custom/dept-tree-select.vue ruoyi-plus-soybean/src/components/custom/dept-tree.vue ruoyi-plus-soybean/src/components/custom/dict-checkbox.vue ruoyi-plus-soybean/src/components/custom/dict-radio.vue ruoyi-plus-soybean/src/components/custom/dict-select.vue ruoyi-plus-soybean/src/components/custom/dict-tag.vue ruoyi-plus-soybean/src/components/custom/file-upload.vue ruoyi-plus-soybean/src/components/custom/form-tip.vue ruoyi-plus-soybean/src/components/custom/json-preview.vue ruoyi-plus-soybean/src/components/custom/look-forward.vue ruoyi-plus-soybean/src/components/custom/menu-tree-select.vue ruoyi-plus-soybean/src/components/custom/menu-tree.vue ruoyi-plus-soybean/src/components/custom/oss-upload.vue ruoyi-plus-soybean/src/components/custom/post-select.vue ruoyi-plus-soybean/src/components/custom/role-select.vue ruoyi-plus-soybean/src/components/custom/soybean-avatar.vue ruoyi-plus-soybean/src/components/custom/status-switch.vue ruoyi-plus-soybean/src/components/custom/svg-icon.vue ruoyi-plus-soybean/src/components/custom/tenant-select.vue ruoyi-plus-soybean/src/components/custom/umo-doc-editor.vue ruoyi-plus-soybean/src/components/custom/user-select.vue ruoyi-plus-soybean/src/components/custom/wave-bg.vue ruoyi-plus-soybean/src/constants/app.ts ruoyi-plus-soybean/src/constants/business.ts ruoyi-plus-soybean/src/constants/common.ts ruoyi-plus-soybean/src/constants/reg.ts ruoyi-plus-soybean/src/enum/business.ts ruoyi-plus-soybean/src/enum/index.ts ruoyi-plus-soybean/src/hooks/business/auth.ts ruoyi-plus-soybean/src/hooks/business/captcha.ts ruoyi-plus-soybean/src/hooks/business/dict.ts ruoyi-plus-soybean/src/hooks/business/download.ts ruoyi-plus-soybean/src/hooks/common/echarts.ts ruoyi-plus-soybean/src/hooks/common/form.ts ruoyi-plus-soybean/src/hooks/common/icon.ts ruoyi-plus-soybean/src/hooks/common/loading.ts ruoyi-plus-soybean/src/hooks/common/router.ts ruoyi-plus-soybean/src/hooks/common/table.ts ruoyi-plus-soybean/src/layouts/base-layout/index.vue ruoyi-plus-soybean/src/layouts/blank-layout/index.vue ruoyi-plus-soybean/src/layouts/modules/global-breadcrumb/index.vue ruoyi-plus-soybean/src/layouts/modules/global-content/index.vue ruoyi-plus-soybean/src/layouts/modules/global-footer/index.vue ruoyi-plus-soybean/src/layouts/modules/global-header/components/message-button.vue ruoyi-plus-soybean/src/layouts/modules/global-header/components/theme-button.vue ruoyi-plus-soybean/src/layouts/modules/global-header/components/user-avatar.vue ruoyi-plus-soybean/src/layouts/modules/global-header/index.vue ruoyi-plus-soybean/src/layouts/modules/global-logo/index.vue ruoyi-plus-soybean/src/layouts/modules/global-menu/components/first-level-menu.vue ruoyi-plus-soybean/src/layouts/modules/global-menu/context/index.ts ruoyi-plus-soybean/src/layouts/modules/global-menu/index.vue ruoyi-plus-soybean/src/layouts/modules/global-menu/modules/horizontal-menu.vue ruoyi-plus-soybean/src/layouts/modules/global-menu/modules/top-hybrid-header-first.vue ruoyi-plus-soybean/src/layouts/modules/global-menu/modules/top-hybrid-sidebar-first.vue ruoyi-plus-soybean/src/layouts/modules/global-menu/modules/vertical-hybrid-header-first.vue ruoyi-plus-soybean/src/layouts/modules/global-menu/modules/vertical-menu.vue ruoyi-plus-soybean/src/layouts/modules/global-menu/modules/vertical-mix-menu.vue ruoyi-plus-soybean/src/layouts/modules/global-search/components/search-footer.vue ruoyi-plus-soybean/src/layouts/modules/global-search/components/search-modal.vue ruoyi-plus-soybean/src/layouts/modules/global-search/components/search-result.vue ruoyi-plus-soybean/src/layouts/modules/global-search/index.vue ruoyi-plus-soybean/src/layouts/modules/global-sider/index.vue ruoyi-plus-soybean/src/layouts/modules/global-tab/context-menu.vue ruoyi-plus-soybean/src/layouts/modules/global-tab/index.vue ruoyi-plus-soybean/src/layouts/modules/theme-drawer/components/layout-mode-card.vue ruoyi-plus-soybean/src/layouts/modules/theme-drawer/components/setting-item.vue ruoyi-plus-soybean/src/layouts/modules/theme-drawer/index.vue ruoyi-plus-soybean/src/layouts/modules/theme-drawer/modules/appearance/index.vue ruoyi-plus-soybean/src/layouts/modules/theme-drawer/modules/appearance/modules/theme-color.vue ruoyi-plus-soybean/src/layouts/modules/theme-drawer/modules/appearance/modules/theme-radius.vue ruoyi-plus-soybean/src/layouts/modules/theme-drawer/modules/appearance/modules/theme-schema.vue ruoyi-plus-soybean/src/layouts/modules/theme-drawer/modules/config-operation.vue ruoyi-plus-soybean/src/layouts/modules/theme-drawer/modules/general/index.vue ruoyi-plus-soybean/src/layouts/modules/theme-drawer/modules/general/modules/global-settings.vue ruoyi-plus-soybean/src/layouts/modules/theme-drawer/modules/general/modules/watermark-settings.vue ruoyi-plus-soybean/src/layouts/modules/theme-drawer/modules/layout/index.vue ruoyi-plus-soybean/src/layouts/modules/theme-drawer/modules/layout/modules/content-settings.vue ruoyi-plus-soybean/src/layouts/modules/theme-drawer/modules/layout/modules/footer-settings.vue ruoyi-plus-soybean/src/layouts/modules/theme-drawer/modules/layout/modules/header-settings.vue ruoyi-plus-soybean/src/layouts/modules/theme-drawer/modules/layout/modules/layout-mode.vue ruoyi-plus-soybean/src/layouts/modules/theme-drawer/modules/layout/modules/sider-settings.vue ruoyi-plus-soybean/src/layouts/modules/theme-drawer/modules/layout/modules/tab-settings.vue ruoyi-plus-soybean/src/layouts/modules/theme-drawer/modules/layout/modules/table-settings.vue ruoyi-plus-soybean/src/layouts/modules/theme-drawer/modules/preset/index.vue ruoyi-plus-soybean/src/layouts/modules/theme-drawer/modules/preset/modules/theme-preset.vue ruoyi-plus-soybean/src/layouts/modules/theme-drawer/modules/table-props.vue ruoyi-plus-soybean/src/locales/dayjs.ts ruoyi-plus-soybean/src/locales/index.ts ruoyi-plus-soybean/src/locales/langs/en-us.ts ruoyi-plus-soybean/src/locales/langs/zh-cn.ts ruoyi-plus-soybean/src/locales/locale.ts ruoyi-plus-soybean/src/locales/naive.ts ruoyi-plus-soybean/src/main.ts ruoyi-plus-soybean/src/plugins/app.ts ruoyi-plus-soybean/src/plugins/assets.ts ruoyi-plus-soybean/src/plugins/dayjs.ts ruoyi-plus-soybean/src/plugins/iconify.ts ruoyi-plus-soybean/src/plugins/index.ts ruoyi-plus-soybean/src/plugins/loading.ts ruoyi-plus-soybean/src/plugins/nprogress.ts ruoyi-plus-soybean/src/router/elegant/imports.ts ruoyi-plus-soybean/src/router/elegant/routes.ts ruoyi-plus-soybean/src/router/elegant/transform.ts ruoyi-plus-soybean/src/router/guard/index.ts ruoyi-plus-soybean/src/router/guard/progress.ts ruoyi-plus-soybean/src/router/guard/route.ts ruoyi-plus-soybean/src/router/guard/title.ts ruoyi-plus-soybean/src/router/index.ts ruoyi-plus-soybean/src/router/routes/builtin.ts ruoyi-plus-soybean/src/router/routes/index.ts ruoyi-plus-soybean/src/service/api/analy/hoister-data.ts ruoyi-plus-soybean/src/service/api/analy/packer-data.ts ruoyi-plus-soybean/src/service/api/analy/roller-data.ts ruoyi-plus-soybean/src/service/api/auth.ts ruoyi-plus-soybean/src/service/api/demo/demo.ts ruoyi-plus-soybean/src/service/api/demo/index.ts ruoyi-plus-soybean/src/service/api/demo/tree.ts ruoyi-plus-soybean/src/service/api/index.ts ruoyi-plus-soybean/src/service/api/monitor/cache.ts ruoyi-plus-soybean/src/service/api/monitor/index.ts ruoyi-plus-soybean/src/service/api/monitor/login-infor.ts ruoyi-plus-soybean/src/service/api/monitor/online.ts ruoyi-plus-soybean/src/service/api/monitor/oper-log.ts ruoyi-plus-soybean/src/service/api/route.ts ruoyi-plus-soybean/src/service/api/system/client.ts ruoyi-plus-soybean/src/service/api/system/config.ts ruoyi-plus-soybean/src/service/api/system/dept.ts ruoyi-plus-soybean/src/service/api/system/dict-data.ts ruoyi-plus-soybean/src/service/api/system/dict.ts ruoyi-plus-soybean/src/service/api/system/index.ts ruoyi-plus-soybean/src/service/api/system/menu.ts ruoyi-plus-soybean/src/service/api/system/notice.ts ruoyi-plus-soybean/src/service/api/system/oss-config.ts ruoyi-plus-soybean/src/service/api/system/oss.ts ruoyi-plus-soybean/src/service/api/system/post.ts ruoyi-plus-soybean/src/service/api/system/role.ts ruoyi-plus-soybean/src/service/api/system/social.ts ruoyi-plus-soybean/src/service/api/system/tenant-package.ts ruoyi-plus-soybean/src/service/api/system/tenant.ts ruoyi-plus-soybean/src/service/api/system/user.ts ruoyi-plus-soybean/src/service/api/tool/gen.ts ruoyi-plus-soybean/src/service/api/tool/index.ts ruoyi-plus-soybean/src/service/request/index.ts ruoyi-plus-soybean/src/service/request/shared.ts ruoyi-plus-soybean/src/service/request/type.ts ruoyi-plus-soybean/src/store/index.ts ruoyi-plus-soybean/src/store/modules/app/index.ts ruoyi-plus-soybean/src/store/modules/auth/index.ts ruoyi-plus-soybean/src/store/modules/auth/shared.ts ruoyi-plus-soybean/src/store/modules/dict/index.ts ruoyi-plus-soybean/src/store/modules/notice/index.ts ruoyi-plus-soybean/src/store/modules/route/index.ts ruoyi-plus-soybean/src/store/modules/route/shared.ts ruoyi-plus-soybean/src/store/modules/tab/index.ts ruoyi-plus-soybean/src/store/modules/tab/shared.ts ruoyi-plus-soybean/src/store/modules/theme/index.ts ruoyi-plus-soybean/src/store/modules/theme/shared.ts ruoyi-plus-soybean/src/store/plugins/index.ts ruoyi-plus-soybean/src/styles/css/global.css ruoyi-plus-soybean/src/styles/css/nprogress.css ruoyi-plus-soybean/src/styles/css/reset.css ruoyi-plus-soybean/src/styles/css/transition.css ruoyi-plus-soybean/src/styles/scss/custom.scss ruoyi-plus-soybean/src/styles/scss/global.scss ruoyi-plus-soybean/src/styles/scss/loading.scss ruoyi-plus-soybean/src/styles/scss/scrollbar.scss ruoyi-plus-soybean/src/theme/preset/azir.json ruoyi-plus-soybean/src/theme/preset/compact.json ruoyi-plus-soybean/src/theme/preset/dark.json ruoyi-plus-soybean/src/theme/preset/default.json ruoyi-plus-soybean/src/theme/preset/soybean.json ruoyi-plus-soybean/src/theme/settings.ts ruoyi-plus-soybean/src/theme/vars.ts ruoyi-plus-soybean/src/typings/api/analy.hoister-data.api.d.ts ruoyi-plus-soybean/src/typings/api/analy.packer-data.api.d.ts ruoyi-plus-soybean/src/typings/api/analy.roller-data.api.d.ts ruoyi-plus-soybean/src/typings/api/api.d.ts ruoyi-plus-soybean/src/typings/api/auth.d.ts ruoyi-plus-soybean/src/typings/api/demo.api.d.ts ruoyi-plus-soybean/src/typings/api/monitor.api.d.ts ruoyi-plus-soybean/src/typings/api/route.d.ts ruoyi-plus-soybean/src/typings/api/system.api.d.ts ruoyi-plus-soybean/src/typings/api/tool.api.d.ts ruoyi-plus-soybean/src/typings/app.d.ts ruoyi-plus-soybean/src/typings/common.d.ts ruoyi-plus-soybean/src/typings/components.d.ts ruoyi-plus-soybean/src/typings/elegant-router.d.ts ruoyi-plus-soybean/src/typings/global.d.ts ruoyi-plus-soybean/src/typings/naive-ui.d.ts ruoyi-plus-soybean/src/typings/router.d.ts ruoyi-plus-soybean/src/typings/storage.d.ts ruoyi-plus-soybean/src/typings/union-key.d.ts ruoyi-plus-soybean/src/typings/vite-env.d.ts ruoyi-plus-soybean/src/utils/agent.ts ruoyi-plus-soybean/src/utils/common.ts ruoyi-plus-soybean/src/utils/copy.ts ruoyi-plus-soybean/src/utils/crypto.ts ruoyi-plus-soybean/src/utils/icon-tag-format.ts ruoyi-plus-soybean/src/utils/icon.ts ruoyi-plus-soybean/src/utils/jsencrypt.ts ruoyi-plus-soybean/src/utils/service.ts ruoyi-plus-soybean/src/utils/sse.ts ruoyi-plus-soybean/src/utils/storage.ts ruoyi-plus-soybean/src/utils/websocket.ts ruoyi-plus-soybean/src/views/_builtin/403/index.vue ruoyi-plus-soybean/src/views/_builtin/404/index.vue ruoyi-plus-soybean/src/views/_builtin/500/index.vue ruoyi-plus-soybean/src/views/_builtin/iframe-page/[url].vue ruoyi-plus-soybean/src/views/_builtin/login/index.vue ruoyi-plus-soybean/src/views/_builtin/login/modules/bind-wechat.vue ruoyi-plus-soybean/src/views/_builtin/login/modules/code-login.vue ruoyi-plus-soybean/src/views/_builtin/login/modules/pwd-login.vue ruoyi-plus-soybean/src/views/_builtin/login/modules/register.vue ruoyi-plus-soybean/src/views/_builtin/login/modules/reset-pwd.vue ruoyi-plus-soybean/src/views/_builtin/social-callback/index.vue ruoyi-plus-soybean/src/views/_builtin/user-center/index.vue ruoyi-plus-soybean/src/views/_builtin/user-center/modules/online-table.vue ruoyi-plus-soybean/src/views/_builtin/user-center/modules/social-card.vue ruoyi-plus-soybean/src/views/_builtin/user-center/modules/user-avatar.vue ruoyi-plus-soybean/src/views/about/index.vue ruoyi-plus-soybean/src/views/analy/hoister/index.vue ruoyi-plus-soybean/src/views/analy/hoister/modules/hoister-data-operate-drawer.vue ruoyi-plus-soybean/src/views/analy/hoister/modules/hoister-data-search.vue ruoyi-plus-soybean/src/views/analy/output-analy/index.vue ruoyi-plus-soybean/src/views/analy/output-analy/modules/roller-data-line-chart.vue ruoyi-plus-soybean/src/views/analy/output-analy/modules/roller-data-operate-drawer.vue ruoyi-plus-soybean/src/views/analy/output-analy/modules/roller-data-search.vue ruoyi-plus-soybean/src/views/analy/packer/index.vue ruoyi-plus-soybean/src/views/analy/packer/modules/packer-data-operate-drawer.vue ruoyi-plus-soybean/src/views/analy/packer/modules/packer-data-search.vue ruoyi-plus-soybean/src/views/analy/roller/index.vue ruoyi-plus-soybean/src/views/analy/roller/modules/roller-data-operate-drawer.vue ruoyi-plus-soybean/src/views/analy/roller/modules/roller-data-search.vue ruoyi-plus-soybean/src/views/demo/demo/index.vue ruoyi-plus-soybean/src/views/demo/demo/modules/demo-operate-drawer.vue ruoyi-plus-soybean/src/views/demo/demo/modules/demo-search.vue ruoyi-plus-soybean/src/views/demo/tree/index.vue ruoyi-plus-soybean/src/views/demo/tree/modules/tree-operate-drawer.vue ruoyi-plus-soybean/src/views/demo/tree/modules/tree-search.vue ruoyi-plus-soybean/src/views/home/index.vue ruoyi-plus-soybean/src/views/home/modules/card-data.vue ruoyi-plus-soybean/src/views/home/modules/creativity-banner.vue ruoyi-plus-soybean/src/views/home/modules/header-banner.vue ruoyi-plus-soybean/src/views/home/modules/line-chart.vue ruoyi-plus-soybean/src/views/home/modules/pie-chart.vue ruoyi-plus-soybean/src/views/home/modules/project-news.vue ruoyi-plus-soybean/src/views/monitor/cache/index.vue ruoyi-plus-soybean/src/views/monitor/logininfor/index.vue ruoyi-plus-soybean/src/views/monitor/logininfor/modules/login-infor-search.vue ruoyi-plus-soybean/src/views/monitor/logininfor/modules/login-infor-view-drawer.vue ruoyi-plus-soybean/src/views/monitor/online/index.vue ruoyi-plus-soybean/src/views/monitor/online/modules/online-search.vue ruoyi-plus-soybean/src/views/monitor/operlog/index.vue ruoyi-plus-soybean/src/views/monitor/operlog/modules/oper-log-search.vue ruoyi-plus-soybean/src/views/monitor/operlog/modules/oper-log-view-drawer.vue ruoyi-plus-soybean/src/views/system/client/index.vue ruoyi-plus-soybean/src/views/system/client/modules/client-operate-drawer.vue ruoyi-plus-soybean/src/views/system/client/modules/client-search.vue ruoyi-plus-soybean/src/views/system/config/index.vue ruoyi-plus-soybean/src/views/system/config/modules/config-operate-drawer.vue ruoyi-plus-soybean/src/views/system/config/modules/config-search.vue ruoyi-plus-soybean/src/views/system/dept/index.vue ruoyi-plus-soybean/src/views/system/dept/modules/dept-operate-drawer.vue ruoyi-plus-soybean/src/views/system/dept/modules/dept-search.vue ruoyi-plus-soybean/src/views/system/dict/index.vue ruoyi-plus-soybean/src/views/system/dict/modules/dict-data-operate-drawer.vue ruoyi-plus-soybean/src/views/system/dict/modules/dict-data-search.vue ruoyi-plus-soybean/src/views/system/dict/modules/dict-type-operate-drawer.vue ruoyi-plus-soybean/src/views/system/menu/index.vue ruoyi-plus-soybean/src/views/system/menu/modules/menu-cascade-delete-modal.vue ruoyi-plus-soybean/src/views/system/menu/modules/menu-operate-drawer.vue ruoyi-plus-soybean/src/views/system/notice/index.vue ruoyi-plus-soybean/src/views/system/notice/modules/notice-operate-drawer.vue ruoyi-plus-soybean/src/views/system/notice/modules/notice-search.vue ruoyi-plus-soybean/src/views/system/oss-config/index.vue ruoyi-plus-soybean/src/views/system/oss-config/modules/oss-config-operate-drawer.vue ruoyi-plus-soybean/src/views/system/oss-config/modules/oss-config-search.vue ruoyi-plus-soybean/src/views/system/oss/index.vue ruoyi-plus-soybean/src/views/system/oss/modules/oss-search.vue ruoyi-plus-soybean/src/views/system/oss/modules/oss-upload-modal.vue ruoyi-plus-soybean/src/views/system/post/index.vue ruoyi-plus-soybean/src/views/system/post/modules/post-operate-drawer.vue ruoyi-plus-soybean/src/views/system/post/modules/post-search.vue ruoyi-plus-soybean/src/views/system/role/index.vue ruoyi-plus-soybean/src/views/system/role/modules/role-auth-user-drawer.vue ruoyi-plus-soybean/src/views/system/role/modules/role-data-scope-drawer.vue ruoyi-plus-soybean/src/views/system/role/modules/role-operate-drawer.vue ruoyi-plus-soybean/src/views/system/role/modules/role-search.vue ruoyi-plus-soybean/src/views/system/tenant-package/index.vue ruoyi-plus-soybean/src/views/system/tenant-package/modules/tenant-package-operate-drawer.vue ruoyi-plus-soybean/src/views/system/tenant-package/modules/tenant-package-search.vue ruoyi-plus-soybean/src/views/system/tenant/index.vue ruoyi-plus-soybean/src/views/system/tenant/modules/tenant-operate-drawer.vue ruoyi-plus-soybean/src/views/system/tenant/modules/tenant-search.vue ruoyi-plus-soybean/src/views/system/user/index.vue ruoyi-plus-soybean/src/views/system/user/modules/user-import-modal.vue ruoyi-plus-soybean/src/views/system/user/modules/user-operate-drawer.vue ruoyi-plus-soybean/src/views/system/user/modules/user-password-drawer.vue ruoyi-plus-soybean/src/views/system/user/modules/user-search.vue ruoyi-plus-soybean/src/views/tool/gen/index.vue ruoyi-plus-soybean/src/views/tool/gen/modules/gen-table-db-search.vue ruoyi-plus-soybean/src/views/tool/gen/modules/gen-table-import-drawer.vue ruoyi-plus-soybean/src/views/tool/gen/modules/gen-table-operate-drawer.vue ruoyi-plus-soybean/src/views/tool/gen/modules/gen-table-preview-drawer.vue ruoyi-plus-soybean/src/views/tool/gen/modules/gen-table-search.vue ruoyi-plus-soybean/tsconfig.json ruoyi-plus-soybean/uno.config.ts ruoyi-plus-soybean/vite.config.ts