疯狂的狮子Li
2021-08-18 0375fd319c9f3b08d255c814cb0f8521d8ec641b
!78 同步dev分支
Merge pull request !78 from 疯狂的狮子Li/dev
已修改180个文件
已添加30个文件
已重命名16个文件
已删除10个文件
11160 ■■■■ 文件已修改
README.md 57 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
docker/docker-compose.yml 31 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pom.xml 42 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/pom.xml 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/java/com/ruoyi/web/controller/common/CaptchaController.java 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/java/com/ruoyi/web/controller/common/CommonController.java 88 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/CacheController.java 100 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/SysLogininforController.java 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/SysOperlogController.java 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/SysUserOnlineController.java 17 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysConfigController.java 11 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysDeptController.java 13 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysDictDataController.java 15 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysDictTypeController.java 11 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysIndexController.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysLoginController.java 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysMenuController.java 28 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysNoticeController.java 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysOssConfigController.java 108 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysOssController.java 38 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysPostController.java 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysProfileController.java 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysRegisterController.java 38 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysRoleController.java 17 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysUserController.java 57 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/resources/application-dev.yml 59 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/resources/application-prod.yml 59 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/resources/application.yml 29 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/resources/i18n/messages.properties 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/resources/i18n/messages_en_US.properties 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/resources/i18n/messages_zh_CN.properties 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/resources/spy.properties 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/pom.xml 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/annotation/Excel.java 165 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/annotation/ExcelDictFormat.java 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/annotation/Excels.java 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/annotation/RateLimiter.java 40 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/annotation/RepeatSubmit.java 52 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/config/RuoYiConfig.java 34 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/constant/Constants.java 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/constant/UserConstants.java 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/convert/ExcelDictConvert.java 69 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/core/controller/BaseController.java 38 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/core/domain/BaseEntity.java 56 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/core/domain/TreeEntity.java 94 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysDept.java 146 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysDictData.java 158 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysDictType.java 108 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysMenu.java 169 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysRole.java 190 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysUser.java 240 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/core/domain/model/LoginUser.java 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/core/domain/model/RegisterBody.java 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/core/mybatisplus/methods/InsertAll.java 15 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/enums/LimitType.java 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/exception/CustomException.java 43 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/exception/GlobalException.java 58 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/exception/ServiceException.java 73 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/exception/base/BaseException.java 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/exception/file/FileException.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/exception/user/UserException.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/filter/RepeatableFilter.java 96 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/filter/XssFilter.java 41 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/filter/XssHttpServletRequestWrapper.java 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/utils/DictUtils.java 372 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/utils/JsonUtils.java 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/utils/PageUtils.java 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/utils/SecurityUtils.java 40 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/utils/ServletUtils.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/utils/StringUtils.java 354 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/utils/file/FileTypeUtils.java 76 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/utils/file/FileUploadUtils.java 239 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/utils/file/FileUtils.java 76 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/utils/file/ImageUtils.java 102 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/utils/file/MimeTypeUtils.java 59 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/utils/ip/AddressUtils.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/utils/poi/ExcelUtil.java 1184 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/utils/reflect/ReflectUtils.java 17 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/utils/sql/SqlUtil.java 10 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-demo/pom.xml 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-demo/src/main/java/com/ruoyi/demo/controller/TestBatchController.java 11 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-demo/src/main/java/com/ruoyi/demo/controller/TestDemoController.java 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-demo/src/main/java/com/ruoyi/demo/controller/TestI18nController.java 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-demo/src/main/java/com/ruoyi/demo/controller/TestTreeController.java 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-demo/src/main/java/com/ruoyi/demo/domain/vo/TestDemoVo.java 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-demo/src/main/java/com/ruoyi/demo/domain/vo/TestTreeVo.java 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-demo/src/main/java/com/ruoyi/demo/service/impl/TestDemoServiceImpl.java 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-demo/src/main/java/com/ruoyi/demo/service/impl/TestTreeServiceImpl.java 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-extend/pom.xml 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-extend/ruoyi-monitor-admin/pom.xml 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-framework/pom.xml 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/DataScopeAspect.java 25 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/DataSourceAspect.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/LogAspect.java 11 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/RateLimiterAspect.java 116 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-framework/src/main/java/com/ruoyi/framework/captcha/UnsignedMathGenerator.java 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-framework/src/main/java/com/ruoyi/framework/config/ApplicationConfig.java 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-framework/src/main/java/com/ruoyi/framework/config/AsyncConfig.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-framework/src/main/java/com/ruoyi/framework/config/FilterConfig.java 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-framework/src/main/java/com/ruoyi/framework/config/I18nConfig.java 48 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-framework/src/main/java/com/ruoyi/framework/config/JacksonConfig.java 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-framework/src/main/java/com/ruoyi/framework/config/RedisConfig.java 32 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-framework/src/main/java/com/ruoyi/framework/config/ResourcesConfig.java 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-framework/src/main/java/com/ruoyi/framework/config/SecurityConfig.java 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-framework/src/main/java/com/ruoyi/framework/config/properties/RepeatSubmitProperties.java 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-framework/src/main/java/com/ruoyi/framework/interceptor/RepeatSubmitInterceptor.java 66 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-framework/src/main/java/com/ruoyi/framework/interceptor/impl/SameUrlDataInterceptor.java 164 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-framework/src/main/java/com/ruoyi/framework/jackson/BigNumberSerializer.java 42 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-framework/src/main/java/com/ruoyi/framework/mybatisplus/CreateAndUpdateMetaObjectHandler.java 40 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-framework/src/main/java/com/ruoyi/framework/security/filter/JwtAuthenticationTokenFilter.java 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-framework/src/main/java/com/ruoyi/framework/security/handle/AuthenticationEntryPointImpl.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-framework/src/main/java/com/ruoyi/framework/security/handle/LogoutSuccessHandlerImpl.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-framework/src/main/java/com/ruoyi/framework/web/exception/GlobalExceptionHandler.java 87 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/AsyncService.java 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/PermissionService.java 32 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/SysLoginService.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/SysRegisterService.java 117 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/TokenService.java 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/UserDetailsServiceImpl.java 22 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-generator/pom.xml 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-generator/src/main/java/com/ruoyi/generator/domain/GenTable.java 14 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-generator/src/main/java/com/ruoyi/generator/domain/GenTableColumn.java 28 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-generator/src/main/java/com/ruoyi/generator/service/GenTableServiceImpl.java 69 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-generator/src/main/java/com/ruoyi/generator/util/GenUtils.java 38 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-generator/src/main/java/com/ruoyi/generator/util/VelocityUtils.java 70 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-generator/src/main/resources/vm/java/bo.java.vm 4 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-generator/src/main/resources/vm/java/controller.java.vm 13 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-generator/src/main/resources/vm/java/serviceImpl.java.vm 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-generator/src/main/resources/vm/java/vo.java.vm 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-generator/src/main/resources/vm/js/api.js.vm 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-generator/src/main/resources/vm/vue/index-tree.vue.vm 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-generator/src/main/resources/vm/vue/index.vue.vm 39 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-generator/src/main/resources/vm/xml/mapper.xml.vm 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-oss/pom.xml 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-oss/src/main/java/com/ruoyi/oss/constant/CloudConstant.java 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-oss/src/main/java/com/ruoyi/oss/enumd/CloudServiceEnumd.java 28 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-oss/src/main/java/com/ruoyi/oss/factory/OssFactory.java 64 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-oss/src/main/java/com/ruoyi/oss/properties/CloudStorageProperties.java 184 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-oss/src/main/java/com/ruoyi/oss/service/ICloudStorageStrategy.java 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-oss/src/main/java/com/ruoyi/oss/service/abstractd/AbstractCloudStorageStrategy.java 17 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-oss/src/main/java/com/ruoyi/oss/service/impl/AliyunCloudStorageStrategy.java 36 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-oss/src/main/java/com/ruoyi/oss/service/impl/MinioCloudStorageStrategy.java 33 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-oss/src/main/java/com/ruoyi/oss/service/impl/QcloudCloudStorageStrategy.java 38 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-oss/src/main/java/com/ruoyi/oss/service/impl/QiniuCloudStorageStrategy.java 41 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-quartz/pom.xml 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-quartz/src/main/java/com/ruoyi/quartz/controller/SysJobController.java 45 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-quartz/src/main/java/com/ruoyi/quartz/controller/SysJobLogController.java 170 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-quartz/src/main/java/com/ruoyi/quartz/domain/SysJob.java 35 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-quartz/src/main/java/com/ruoyi/quartz/domain/SysJobLog.java 160 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-quartz/src/main/java/com/ruoyi/quartz/service/impl/SysJobLogServiceImpl.java 27 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-quartz/src/main/java/com/ruoyi/quartz/service/impl/SysJobServiceImpl.java 18 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-quartz/src/main/java/com/ruoyi/quartz/task/RyTask.java 58 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-quartz/src/main/java/com/ruoyi/quartz/util/AbstractQuartzJob.java 217 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-quartz/src/main/java/com/ruoyi/quartz/util/JobInvokeUtil.java 367 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/pom.xml 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/domain/SysConfig.java 21 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/domain/SysLogininfor.java 28 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/domain/SysOperLog.java 44 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/domain/SysOss.java 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/domain/SysOssConfig.java 111 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/domain/SysPost.java 21 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/domain/bo/SysOssBo.java 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/domain/bo/SysOssConfigBo.java 130 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/MetaVo.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/SysOssConfigVo.java 96 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/SysOssVo.java 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/SysUserExportVo.java 93 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/SysUserImportVo.java 73 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysOssConfigMapper.java 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysOssMapper.java 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/service/ISysOssConfigService.java 58 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/service/ISysOssService.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/service/ISysUserService.java 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysConfigServiceImpl.java 41 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysDeptServiceImpl.java 17 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysDictDataServiceImpl.java 14 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysDictTypeServiceImpl.java 31 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysLogininforServiceImpl.java 23 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysMenuServiceImpl.java 36 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysNoticeServiceImpl.java 14 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysOperLogServiceImpl.java 21 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysOssConfigServiceImpl.java 175 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysOssServiceImpl.java 30 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysPostServiceImpl.java 27 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysRoleServiceImpl.java 20 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysUserOnlineServiceImpl.java 13 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysUserServiceImpl.java 45 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/resources/mapper/system/SysOssConfigMapper.xml 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/resources/mapper/system/SysOssMapper.xml 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-ui/bin/run-web.bat 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-ui/package.json 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-ui/src/api/demo/demo.js 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-ui/src/api/demo/tree.js 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-ui/src/api/login.js 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-ui/src/api/monitor/jobLog.js 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-ui/src/api/monitor/logininfor.js 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-ui/src/api/monitor/operlog.js 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-ui/src/api/system/config.js 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-ui/src/api/system/dict/data.js 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-ui/src/api/system/dict/type.js 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-ui/src/api/system/oss.js 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-ui/src/api/system/ossConfig.js 58 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-ui/src/api/system/role.js 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-ui/src/api/system/user.js 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-ui/src/components/Breadcrumb/index.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-ui/src/main.js 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-ui/src/router/index.js 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-ui/src/store/modules/user.js 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-ui/src/utils/download.js 91 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-ui/src/utils/ossdownload.js 39 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-ui/src/utils/request.js 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-ui/src/utils/ruoyi.js 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-ui/src/utils/zipdownload.js 40 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-ui/src/views/demo/demo/index.vue 16 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-ui/src/views/demo/tree/index.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-ui/src/views/index.vue 50 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-ui/src/views/login.vue 12 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-ui/src/views/monitor/job/index.vue 16 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-ui/src/views/monitor/job/log.vue 20 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-ui/src/views/monitor/logininfor/index.vue 16 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-ui/src/views/monitor/operlog/index.vue 16 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-ui/src/views/register.vue 208 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-ui/src/views/system/config/index.vue 16 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-ui/src/views/system/dict/data.vue 16 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-ui/src/views/system/dict/index.vue 16 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-ui/src/views/system/menu/index.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-ui/src/views/system/oss/config.vue 414 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-ui/src/views/system/oss/index.vue 75 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-ui/src/views/system/post/index.vue 16 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-ui/src/views/system/role/index.vue 16 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-ui/src/views/system/role/selectUser.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-ui/src/views/system/user/index.vue 23 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-ui/src/views/system/user/profile/userAvatar.vue 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-ui/src/views/tool/gen/index.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
sql/oss.sql 32 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
sql/ry_20210731.sql 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
README.md
@@ -4,8 +4,8 @@
[![License](https://img.shields.io/badge/License-MIT-blue.svg)](https://gitee.com/JavaLionLi/RuoYi-Vue-Plus/blob/master/LICENSE)
[![使用IntelliJ IDEA开发维护](https://img.shields.io/badge/IntelliJ%20IDEA-提供支持-blue.svg)](https://www.jetbrains.com/?from=RuoYi-Vue-Plus)
<br>
[![RuoYi-Vue-Plus](https://img.shields.io/badge/RuoYi_Vue_Plus-2.6.0-success.svg)](https://gitee.com/JavaLionLi/RuoYi-Vue-Plus)
[![Spring Boot](https://img.shields.io/badge/Spring%20Boot-2.4-blue.svg)]()
[![RuoYi-Vue-Plus](https://img.shields.io/badge/RuoYi_Vue_Plus-3.0.0-success.svg)](https://gitee.com/JavaLionLi/RuoYi-Vue-Plus)
[![Spring Boot](https://img.shields.io/badge/Spring%20Boot-2.5-blue.svg)]()
[![JDK-8+](https://img.shields.io/badge/JDK-8+-green.svg)]()
[![JDK-11](https://img.shields.io/badge/JDK-11-green.svg)]()
@@ -21,51 +21,39 @@
* æƒé™è®¤è¯æ¡†æž¶ Spring Security、Jwt,支持多终端认证系统
* å…³ç³»æ•°æ®åº“ MySQL é€‚配 8.X 
* ç¼“存数据库 Redis é€‚配 6.X
* æ•°æ®åº“开发框架 Mybatis-Plus å¿«é€Ÿ CRUD å¢žåŠ å¼€å‘æ•ˆçŽ‡ æ’件化支持各类需求
* æ•°æ®åº“框架 Mybatis-Plus å¿«é€Ÿ CRUD å¢žåŠ å¼€å‘æ•ˆçŽ‡ æ’件化支持各类需求
* æ•°æ®åº“框架 å¤šæ•°æ®æºæ¡†æž¶ dynamic-datasource æ”¯æŒä¸»ä»Žä¸Žå¤šç§ç±»æ•°æ®åº“异构
* æ•°æ®åº“框架 Redis客户端 é‡‡ç”¨ Redisson æ€§èƒ½æ›´å¼º
* æ•°æ®åº“框架 æ€§èƒ½åˆ†æžæ’ä»¶ p6spy æ›´å¼ºåŠ²çš„ SQL åˆ†æž
* åºåˆ—化框架 ç»Ÿä¸€ä½¿ç”¨ jackson é«˜æ•ˆå¯é 
* ç½‘络框架 Feign、OkHttp3 æŽ¥å£åŒ–管理 HTTP è¯·æ±‚
* å·¥å…·ç±»æ¡†æž¶ Hutool、Lombok å‡å°‘代码冗余 å¢žåŠ å®‰å…¨æ€§
* åˆ†å¸ƒå¼é” Lock4j æ³¨è§£é”ã€å·¥å…·é” å¤šç§å¤šæ ·
* æ–‡ä»¶å­˜å‚¨ OSS å¯¹è±¡å­˜å‚¨æ¨¡å— æ”¯æŒ(Minio、七牛、阿里、腾讯)
* ç›‘控框架 spring-boot-admin å…¨æ–¹ä½æœåŠ¡ç›‘æŽ§
* æ ¡éªŒæ¡†æž¶ validation å¢žå¼ºæŽ¥å£å®‰å…¨æ€§ ä¸¥è°¨æ€§
* Excel框架 Alibaba EasyExcel æ€§èƒ½ä¼˜å¼‚ æ‰©å±•性强
* æ–‡æ¡£æ¡†æž¶ knife4j ç¾ŽåŒ–接口文档
* åºåˆ—化框架 ç»Ÿä¸€ä½¿ç”¨ jackson é«˜æ•ˆå¯é 
* å·¥å…·ç±»æ¡†æž¶ Hutool、Lombok å‡å°‘代码冗余 å¢žåŠ å®‰å…¨æ€§
* ä»£ç ç”Ÿæˆå™¨ ä¸€é”®ç”Ÿæˆå‰åŽç«¯ä»£ç 
* å¤šæ•°æ®æºæ¡†æž¶ dynamic-datasource æ”¯æŒä¸»ä»Žä¸Žå¤šç§ç±»æ•°æ®åº“异构
* Redis客户端 é‡‡ç”¨ Redisson æ€§èƒ½æ›´å¼º
* åˆ†å¸ƒå¼é” Lock4j æ³¨è§£é”ã€å·¥å…·é” å¤šç§å¤šæ ·
* éƒ¨ç½²æ–¹å¼ Docker å®¹å™¨ç¼–排 ä¸€é”®éƒ¨ç½²ä¸šåŠ¡é›†ç¾¤
* æ–‡ä»¶å­˜å‚¨ OSS å¯¹è±¡å­˜å‚¨æ¨¡å— æ”¯æŒ(Minio、七牛、阿里、腾讯)
* å›½é™…化 Spring æ ‡å‡†å›½é™…化方解决方案
## å‚考文档
使用框架前请仔细阅读文档重点注意事项
<br>
>[初始化项目 å¿…看](https://gitee.com/JavaLionLi/RuoYi-Vue-Plus/wikis/关于初始化项目?sort_id=4164117)
>>[https://gitee.com/JavaLionLi/RuoYi-Vue-Plus/wikis/关于初始化项目?sort_id=4164117](https://gitee.com/JavaLionLi/RuoYi-Vue-Plus/wikis/关于初始化项目?sort_id=4164117)
>[部署项目 å¿…看](https://gitee.com/JavaLionLi/RuoYi-Vue-Plus/wikis/关于应用部署?sort_id=4219382)
>>[https://gitee.com/JavaLionLi/RuoYi-Vue-Plus/wikis/关于应用部署?sort_id=4219382](https://gitee.com/JavaLionLi/RuoYi-Vue-Plus/wikis/关于应用部署?sort_id=4219382)
>[参考文档 Wiki](https://gitee.com/JavaLionLi/RuoYi-Vue-Plus/wikis/pages)
>>[https://gitee.com/JavaLionLi/RuoYi-Vue-Plus/wikis/pages](https://gitee.com/JavaLionLi/RuoYi-Vue-Plus/wikis/pages)
## æé—®å››éƒ¨æ›²
### ä¸€ã€æŸ¥é˜…wiki
优先在`wiki->重点事项`,查找是否有相关问题及解决方案,尤其是框架更新后产生的问题,多会在wiki中提及
## è½¯ä»¶æž¶æž„图
> [参考文档 Wiki](https://gitee.com/JavaLionLi/RuoYi-Vue-Plus/wikis/pages)
### äºŒã€å€ŸåŠ©issues
尝试issues中搜索问题关键字(记得选择已完成),看看是否有其他人提出相同问题
- `如果有`那么依据评论中的解决方案自行尝试解决
- `如果没有`那么提交一个新的issues描述清楚你的问题,需要包含以下内容(优质的issues,能够帮助作者更高效的帮你解决问题):
    - å‡ºçŽ°é—®é¢˜çš„æ¨¡å—æˆ–åŠŸèƒ½æˆ–ç±»ï¼Œæ€»ä¹‹ä½ è¦è¯´æ¸…æ¥šåœ¨å“ªå‡ºçš„é—®é¢˜
    - æè¿°äº§ç”Ÿé—®é¢˜çš„相关操作流程,以便复现快速解决
    - æŠ¥é”™çš„æ—¥å¿—截图,一定是截图,不要复制一堆报错的文本
> [issues](https://gitee.com/JavaLionLi/RuoYi-Vue-Plus/issues)
### ä¸‰ã€ç™¾åº¦
大家都懂,不多描述,将关键的报错信息CC->CV到百度中看看大佬们怎么解决的
> [百度](http://www.baidu.com)
### å››ã€åŠ ç¾¤
以上三点已经能解决大家绝大部分问题了,如果还有问题没能通过这几种方式解决,那么加群,大家一起在群里探讨一下
![Plus部署架构图](https://images.gitee.com/uploads/images/2021/0729/112230_4295e5ce_1766278.png "Plus部署架构图.png")
## è´¡çŒ®ä»£ç 
@@ -79,7 +67,8 @@
* ORM框架 ä½¿ç”¨ Mybatis-Plus ç®€åŒ–CRUD (不支持主子表)
* Bean简化 ä½¿ç”¨ Lombok ç®€åŒ– get set toString ç­‰ç­‰
* å®¹å™¨æ”¹åЍ Tomcat æ”¹ä¸º å¹¶å‘性能更好的 undertow
* åˆ†é¡µç§»é™¤ pagehelper æ”¹ä¸º Mybatis-Plus åˆ†é¡µ
* ç§»é™¤ pagehelper æ”¹ä¸º Mybatis-Plus åˆ†é¡µ
* é›†æˆ p6spy æ›´å¼ºåŠ²çš„ SQL åˆ†æž
* å‡çº§ swagger ä¸º knife4j
* é›†æˆ Hutool 5.X å¹¶é‡å†™RuoYi部分功能
* é›†æˆ Feign æŽ¥å£åŒ–管理 Http è¯·æ±‚(如三方请求 æ”¯ä»˜,短信,推送等)
@@ -90,7 +79,8 @@
* é›†æˆ dynamic-datasource å¤šæ•°æ®æº(默认支持MySQL,其他种类需自行适配)
* é›†æˆ Lock4j å®žçŽ°åˆ†å¸ƒå¼ æ³¨è§£é”ã€å·¥å…·é” å¤šç§å¤šæ ·
* å¢žåŠ  Docker å®¹å™¨ç¼–排 æ‰“包插件与部署脚本
* ç§»é™¤ æœ¬åœ°æ–‡ä»¶ä¸Šä¼  æ”¹ä¸º OSS对象存储 æ”¯æŒ(Minio、七牛、阿里、腾讯)
* ç§»é™¤ é€šç”¨ä¸Šä¼ ä¸‹è½½ æ”¹ä¸º OSS对象存储 æ”¯æŒ(Minio、七牛、阿里、腾讯)
* ç§»é™¤ RuoYi自带 Excel å·¥å…· æ”¹ä¸º EasyExcel å·¥å…·
### ä»£ç æ”¹åЍ
@@ -112,8 +102,9 @@
* å•模块 fast åˆ†æ”¯ [RuoYi-Vue-Plus-fast](https://gitee.com/JavaLionLi/RuoYi-Vue-Plus/tree/fast/)
* Oracle æ¨¡å— oracle åˆ†æ”¯ [RuoYi-Vue-Plus-oracle](https://gitee.com/JavaLionLi/RuoYi-Vue-Plus/tree/oracle/)
## æ‰«ç åŠ ç¾¤ ä¸€èµ·äº¤æµ
![输入图片说明](https://images.gitee.com/uploads/images/2021/0625/160026_11d949aa_1766278.jpeg "07f7121fab14e57e03e5f6a35eff6ce.jpg")
## åŠ ç¾¤ä¸ŽæçŒ®
>[加群与捐献](https://gitee.com/JavaLionLi/RuoYi-Vue-Plus/wikis/加群与捐献?sort_id=4104598)
>>[https://gitee.com/JavaLionLi/RuoYi-Vue-Plus/wikis/加群与捐献?sort_id=4104598](https://gitee.com/JavaLionLi/RuoYi-Vue-Plus/wikis/加群与捐献?sort_id=4104598)
## æçŒ®ä½œè€…
作者为兼职做开源,平时还需要工作,如果帮到了您可以请作者吃个盒饭  
docker/docker-compose.yml
@@ -18,8 +18,6 @@
      - /docker/mysql/data/:/var/lib/mysql/
      # é…ç½®æŒ‚è½½
      - /docker/mysql/conf/:/etc/mysql/conf.d/
      # ä¸»æœºæœ¬æœºæ—¶é—´æ–‡ä»¶æ˜ å°„ ä¸Žæœ¬æœºæ—¶é—´åŒæ­¥
      - /etc/localtime:/etc/localtime:ro
    command:
      # å°†mysql8.0默认密码策略 ä¿®æ”¹ä¸º åŽŸå…ˆ ç­–ç•¥ (mysql8.0对其默认策略做了更改 ä¼šå¯¼è‡´å¯†ç æ— æ³•匹配)
      --default-authentication-plugin=mysql_native_password
@@ -37,6 +35,9 @@
    # å¦‚果需要指定版本 å°±æŠŠ latest æ¢æˆç‰ˆæœ¬å·
    image: nginx:latest
    container_name: nginx-web
    environment:
      # æ—¶åŒºä¸Šæµ·
      TZ: Asia/Shanghai
    ports:
      - 80:80
      - 443:443
@@ -49,8 +50,6 @@
      - /docker/nginx/html:/usr/share/nginx/html
      # æ—¥å¿—目录
      - /docker/nginx/log:/var/log/nginx
      # ä¸»æœºæœ¬æœºæ—¶é—´æ–‡ä»¶æ˜ å°„ ä¸Žæœ¬æœºæ—¶é—´åŒæ­¥
      - /etc/localtime:/etc/localtime:ro
    privileged: true
    restart: always
    networks:
@@ -62,16 +61,13 @@
    ports:
      - 6379:6379
    environment:
      # è®¾ç½®çŽ¯å¢ƒå˜é‡ æ—¶åŒºä¸Šæµ· ç¼–码UTF-8
      # æ—¶åŒºä¸Šæµ·
      TZ: Asia/Shanghai
      LANG: en_US.UTF-8
    volumes:
      # é…ç½®æ–‡ä»¶
      - /docker/redis/conf/redis.conf:/redis.conf:rw
      # æ•°æ®æ–‡ä»¶
      - /docker/redis/data:/data:rw
      # ä¸»æœºæœ¬æœºæ—¶é—´æ–‡ä»¶æ˜ å°„ ä¸Žæœ¬æœºæ—¶é—´åŒæ­¥
      - /etc/localtime:/etc/localtime:ro
    command: "redis-server --appendonly yes"
    privileged: true
    restart: always
@@ -88,6 +84,8 @@
      # æŽ§åˆ¶å°ç«¯å£
      - 9001:9001
    environment:
      # æ—¶åŒºä¸Šæµ·
      TZ: Asia/Shanghai
      # ç®¡ç†åŽå°ç”¨æˆ·å
      MINIO_ACCESS_KEY: ruoyi
      # ç®¡ç†åŽå°å¯†ç ï¼Œæœ€å°8个字符
@@ -97,8 +95,6 @@
      - /docker/minio/data:/data
      # æ˜ å°„配置目录
      - /docker/minio/config:/root/.minio/
      # ä¸»æœºæœ¬æœºæ—¶é—´æ–‡ä»¶æ˜ å°„ ä¸Žæœ¬æœºæ—¶é—´åŒæ­¥
      - /etc/localtime:/etc/localtime:ro
    command: server --console-address ':9001' /data  # æŒ‡å®šå®¹å™¨ä¸­çš„目录 /data
    privileged: true
    restart: always
@@ -107,9 +103,10 @@
        ipv4_address: 172.30.0.54
  ruoyi-server1:
    image: "ruoyi/ruoyi-server:2.6.0"
    image: "ruoyi/ruoyi-server:3.0.0"
    environment:
      - TZ=Asia/Shanghai
      # æ—¶åŒºä¸Šæµ·
      TZ: Asia/Shanghai
    volumes:
      # é…ç½®æ–‡ä»¶
      - /docker/server1/logs/:/ruoyi/server/logs/
@@ -121,9 +118,10 @@
        ipv4_address: 172.30.0.60
  ruoyi-server2:
    image: "ruoyi/ruoyi-server:2.6.0"
    image: "ruoyi/ruoyi-server:3.0.0"
    environment:
      - TZ=Asia/Shanghai
      # æ—¶åŒºä¸Šæµ·
      TZ: Asia/Shanghai
    volumes:
      # é…ç½®æ–‡ä»¶
      - /docker/server2/logs/:/ruoyi/server/logs/
@@ -135,9 +133,10 @@
        ipv4_address: 172.30.0.61
  ruoyi-monitor-admin:
    image: "ruoyi/ruoyi-monitor-admin:2.6.0"
    image: "ruoyi/ruoyi-monitor-admin:3.0.0"
    environment:
      - TZ=Asia/Shanghai
      # æ—¶åŒºä¸Šæµ·
      TZ: Asia/Shanghai
    privileged: true
    restart: always
    networks:
pom.xml
@@ -6,14 +6,14 @@
    <groupId>com.ruoyi</groupId>
    <artifactId>ruoyi-vue-plus</artifactId>
    <version>2.6.0</version>
    <version>3.0.0</version>
    <name>RuoYi-Vue-Plus</name>
    <url>https://gitee.com/JavaLionLi/RuoYi-Vue-Plus</url>
    <description>RuoYi-Vue-Plus后台管理系统</description>
    <properties>
        <ruoyi-vue-plus.version>2.6.0</ruoyi-vue-plus.version>
        <ruoyi-vue-plus.version>3.0.0</ruoyi-vue-plus.version>
        <spring-boot.version>2.5.3</spring-boot.version>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
@@ -22,16 +22,19 @@
        <druid.version>1.2.6</druid.version>
        <knife4j.version>3.0.3</knife4j.version>
        <poi.version>4.1.2</poi.version>
        <easyexcel.version>2.2.10</easyexcel.version>
        <velocity.version>1.7</velocity.version>
        <jwt.version>0.9.1</jwt.version>
        <mybatis-plus.version>3.4.3</mybatis-plus.version>
        <hutool.version>5.7.6</hutool.version>
        <p6spy.version>3.9.1</p6spy.version>
        <hutool.version>5.7.7</hutool.version>
        <feign.version>3.0.3</feign.version>
        <feign-okhttp.version>11.0</feign-okhttp.version>
        <spring-boot-admin.version>2.4.3</spring-boot-admin.version>
        <feign-okhttp.version>11.2</feign-okhttp.version>
        <okhttp.version>4.9.1</okhttp.version>
        <spring-boot-admin.version>2.5.0</spring-boot-admin.version>
        <redisson.version>3.16.1</redisson.version>
        <lock4j.version>2.2.1</lock4j.version>
        <datasource.version>3.4.1</datasource.version>
        <dynamic-ds.version>3.4.1</dynamic-ds.version>
        <!-- OSS é…ç½® -->
        <qiniu.version>7.8.0</qiniu.version>
@@ -79,6 +82,12 @@
                <version>${poi.version}</version>
            </dependency>
            <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>easyexcel</artifactId>
                <version>${easyexcel.version}</version>
            </dependency>
            <!-- velocity代码生成使用模板 -->
            <dependency>
                <groupId>org.apache.velocity</groupId>
@@ -97,7 +106,7 @@
            <dependency>
                <groupId>com.baomidou</groupId>
                <artifactId>dynamic-datasource-spring-boot-starter</artifactId>
                <version>${datasource.version}</version>
                <version>${dynamic-ds.version}</version>
            </dependency>
            <dependency>
@@ -109,6 +118,12 @@
                <groupId>com.baomidou</groupId>
                <artifactId>mybatis-plus-extension</artifactId>
                <version>${mybatis-plus.version}</version>
            </dependency>
            <!-- sql性能分析插件 -->
            <dependency>
                <groupId>p6spy</groupId>
                <artifactId>p6spy</artifactId>
                <version>${p6spy.version}</version>
            </dependency>
            <dependency>
@@ -127,6 +142,12 @@
                <groupId>io.github.openfeign</groupId>
                <artifactId>feign-okhttp</artifactId>
                <version>${feign-okhttp.version}</version>
            </dependency>
            <dependency>
                <groupId>com.squareup.okhttp3</groupId>
                <artifactId>okhttp</artifactId>
                <version>${okhttp.version}</version>
            </dependency>
            <dependency>
@@ -248,7 +269,7 @@
        <repository>
            <id>public</id>
            <name>aliyun nexus</name>
            <url>http://maven.aliyun.com/nexus/content/groups/public/</url>
            <url>https://maven.aliyun.com/repository/public/</url>
            <releases>
                <enabled>true</enabled>
            </releases>
@@ -259,7 +280,7 @@
        <pluginRepository>
            <id>public</id>
            <name>aliyun nexus</name>
            <url>http://maven.aliyun.com/nexus/content/groups/public/</url>
            <url>https://maven.aliyun.com/repository/public/</url>
            <releases>
                <enabled>true</enabled>
            </releases>
@@ -276,6 +297,7 @@
                <!-- çŽ¯å¢ƒæ ‡è¯†ï¼Œéœ€è¦ä¸Žé…ç½®æ–‡ä»¶çš„åç§°ç›¸å¯¹åº” -->
                <profiles.active>local</profiles.active>
                <logging.level>debug</logging.level>
                <endpoints.include>'*'</endpoints.include>
            </properties>
        </profile>
        <profile>
@@ -284,6 +306,7 @@
                <!-- çŽ¯å¢ƒæ ‡è¯†ï¼Œéœ€è¦ä¸Žé…ç½®æ–‡ä»¶çš„åç§°ç›¸å¯¹åº” -->
                <profiles.active>dev</profiles.active>
                <logging.level>debug</logging.level>
                <endpoints.include>'*'</endpoints.include>
            </properties>
            <activation>
                <!-- é»˜è®¤çŽ¯å¢ƒ -->
@@ -295,6 +318,7 @@
            <properties>
                <profiles.active>prod</profiles.active>
                <logging.level>warn</logging.level>
                <endpoints.include>health,info</endpoints.include>
            </properties>
        </profile>
ruoyi-admin/pom.xml
@@ -5,7 +5,7 @@
    <parent>
        <artifactId>ruoyi-vue-plus</artifactId>
        <groupId>com.ruoyi</groupId>
        <version>2.6.0</version>
        <version>3.0.0</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>
    <packaging>jar</packaging>
ruoyi-admin/src/main/java/com/ruoyi/web/controller/common/CaptchaController.java
@@ -8,7 +8,7 @@
import cn.hutool.captcha.generator.RandomGenerator;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.redis.RedisCache;
@@ -107,9 +107,9 @@
    private String getCodeResult(String capStr) {
        int numberLength = captchaProperties.getNumberLength();
        int a = Convert.toInt(StrUtil.sub(capStr, 0, numberLength).trim());
        int a = Convert.toInt(StringUtils.substring(capStr, 0, numberLength).trim());
        char operator = capStr.charAt(numberLength);
        int b = Convert.toInt(StrUtil.sub(capStr, numberLength + 1, numberLength + 1 + numberLength).trim());
        int b = Convert.toInt(StringUtils.substring(capStr, numberLength + 1, numberLength + 1 + numberLength).trim());
        switch (operator) {
            case '*':
                return a * b + "";
ruoyi-admin/src/main/java/com/ruoyi/web/controller/common/CommonController.java
ÎļþÒÑɾ³ý
ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/CacheController.java
@@ -1,50 +1,50 @@
package com.ruoyi.web.controller.monitor;
import cn.hutool.core.util.StrUtil;
import com.ruoyi.common.core.domain.AjaxResult;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.*;
/**
 * ç¼“存监控
 *
 * @author ruoyi
 */
@RestController
@RequestMapping("/monitor/cache")
public class CacheController
{
    @Autowired
    private RedisTemplate<String, String> redisTemplate;
    @PreAuthorize("@ss.hasPermi('monitor:cache:list')")
    @GetMapping()
    public AjaxResult getInfo() throws Exception
    {
        Properties info = (Properties) redisTemplate.execute((RedisCallback<Object>) connection -> connection.info());
        Properties commandStats = (Properties) redisTemplate.execute((RedisCallback<Object>) connection -> connection.info("commandstats"));
        Object dbSize = redisTemplate.execute((RedisCallback<Object>) connection -> connection.dbSize());
        Map<String, Object> result = new HashMap<>(3);
        result.put("info", info);
        result.put("dbSize", dbSize);
        List<Map<String, String>> pieList = new ArrayList<>();
        commandStats.stringPropertyNames().forEach(key -> {
            Map<String, String> data = new HashMap<>(2);
            String property = commandStats.getProperty(key);
            data.put("name", StrUtil.removePrefix(key, "cmdstat_"));
            data.put("value", StrUtil.subBetween(property, "calls=", ",usec"));
            pieList.add(data);
        });
        result.put("commandStats", pieList);
        return AjaxResult.success(result);
    }
}
package com.ruoyi.web.controller.monitor;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.core.domain.AjaxResult;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.*;
/**
 * ç¼“存监控
 *
 * @author ruoyi
 */
@RestController
@RequestMapping("/monitor/cache")
public class CacheController
{
    @Autowired
    private RedisTemplate<String, String> redisTemplate;
    @PreAuthorize("@ss.hasPermi('monitor:cache:list')")
    @GetMapping()
    public AjaxResult getInfo() throws Exception
    {
        Properties info = (Properties) redisTemplate.execute((RedisCallback<Object>) connection -> connection.info());
        Properties commandStats = (Properties) redisTemplate.execute((RedisCallback<Object>) connection -> connection.info("commandstats"));
        Object dbSize = redisTemplate.execute((RedisCallback<Object>) connection -> connection.dbSize());
        Map<String, Object> result = new HashMap<>(3);
        result.put("info", info);
        result.put("dbSize", dbSize);
        List<Map<String, String>> pieList = new ArrayList<>();
        commandStats.stringPropertyNames().forEach(key -> {
            Map<String, String> data = new HashMap<>(2);
            String property = commandStats.getProperty(key);
            data.put("name", StringUtils.removeStart(key, "cmdstat_"));
            data.put("value", StringUtils.substringBetween(property, "calls=", ",usec"));
            pieList.add(data);
        });
        result.put("commandStats", pieList);
        return AjaxResult.success(result);
    }
}
ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/SysLogininforController.java
@@ -12,11 +12,12 @@
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletResponse;
import java.util.List;
/**
 * ç³»ç»Ÿè®¿é—®è®°å½•
 *
 *
 * @author ruoyi
 */
@RestController
@@ -36,11 +37,10 @@
    @Log(title = "登录日志", businessType = BusinessType.EXPORT)
    @PreAuthorize("@ss.hasPermi('monitor:logininfor:export')")
    @GetMapping("/export")
    public AjaxResult export(SysLogininfor logininfor)
    public void export(SysLogininfor logininfor, HttpServletResponse response)
    {
        List<SysLogininfor> list = logininforService.selectLogininforList(logininfor);
        ExcelUtil<SysLogininfor> util = new ExcelUtil<SysLogininfor>(SysLogininfor.class);
        return util.exportExcel(list, "登录日志");
        ExcelUtil.exportExcel(list, "登录日志", SysLogininfor.class, response);
    }
    @PreAuthorize("@ss.hasPermi('monitor:logininfor:remove')")
ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/SysOperlogController.java
@@ -12,11 +12,12 @@
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletResponse;
import java.util.List;
/**
 * æ“ä½œæ—¥å¿—记录
 *
 *
 * @author ruoyi
 */
@RestController
@@ -36,11 +37,10 @@
    @Log(title = "操作日志", businessType = BusinessType.EXPORT)
    @PreAuthorize("@ss.hasPermi('monitor:operlog:export')")
    @GetMapping("/export")
    public AjaxResult export(SysOperLog operLog)
    public void export(SysOperLog operLog, HttpServletResponse response)
    {
        List<SysOperLog> list = operLogService.selectOperLogList(operLog);
        ExcelUtil<SysOperLog> util = new ExcelUtil<SysOperLog>(SysOperLog.class);
        return util.exportExcel(list, "操作日志");
        ExcelUtil.exportExcel(list, "操作日志", SysOperLog.class, response);
    }
    @Log(title = "操作日志", businessType = BusinessType.DELETE)
ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/SysUserOnlineController.java
@@ -1,7 +1,5 @@
package com.ruoyi.web.controller.monitor;
import cn.hutool.core.lang.Validator;
import cn.hutool.core.util.StrUtil;
import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.core.controller.BaseController;
@@ -11,6 +9,7 @@
import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.common.utils.PageUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.system.domain.SysUserOnline;
import com.ruoyi.system.service.ISysUserOnlineService;
import org.springframework.beans.factory.annotation.Autowired;
@@ -24,7 +23,7 @@
/**
 * åœ¨çº¿ç”¨æˆ·ç›‘控
 *
 *
 * @author ruoyi
 */
@RestController
@@ -46,23 +45,23 @@
        for (String key : keys)
        {
            LoginUser user = redisCache.getCacheObject(key);
            if (Validator.isNotEmpty(ipaddr) && Validator.isNotEmpty(userName))
            if (StringUtils.isNotEmpty(ipaddr) && StringUtils.isNotEmpty(userName))
            {
                if (StrUtil.equals(ipaddr, user.getIpaddr()) && StrUtil.equals(userName, user.getUsername()))
                if (StringUtils.equals(ipaddr, user.getIpaddr()) && StringUtils.equals(userName, user.getUsername()))
                {
                    userOnlineList.add(userOnlineService.selectOnlineByInfo(ipaddr, userName, user));
                }
            }
            else if (Validator.isNotEmpty(ipaddr))
            else if (StringUtils.isNotEmpty(ipaddr))
            {
                if (StrUtil.equals(ipaddr, user.getIpaddr()))
                if (StringUtils.equals(ipaddr, user.getIpaddr()))
                {
                    userOnlineList.add(userOnlineService.selectOnlineByIpaddr(ipaddr, user));
                }
            }
            else if (Validator.isNotEmpty(userName) && Validator.isNotNull(user.getUser()))
            else if (StringUtils.isNotEmpty(userName) && StringUtils.isNotNull(user.getUser()))
            {
                if (StrUtil.equals(userName, user.getUsername()))
                if (StringUtils.equals(userName, user.getUsername()))
                {
                    userOnlineList.add(userOnlineService.selectOnlineByUserName(userName, user));
                }
ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysConfigController.java
@@ -7,7 +7,6 @@
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.poi.ExcelUtil;
import com.ruoyi.system.domain.SysConfig;
import com.ruoyi.system.service.ISysConfigService;
@@ -16,6 +15,7 @@
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletResponse;
import java.util.List;
/**
@@ -43,11 +43,10 @@
    @Log(title = "参数管理", businessType = BusinessType.EXPORT)
    @PreAuthorize("@ss.hasPermi('system:config:export')")
    @GetMapping("/export")
    public AjaxResult export(SysConfig config)
    public void export(SysConfig config, HttpServletResponse response)
    {
        List<SysConfig> list = configService.selectConfigList(config);
        ExcelUtil<SysConfig> util = new ExcelUtil<SysConfig>(SysConfig.class);
        return util.exportExcel(list, "参数数据");
        ExcelUtil.exportExcel(list, "参数数据", SysConfig.class, response);
    }
    /**
@@ -82,7 +81,7 @@
        {
            return AjaxResult.error("新增参数'" + config.getConfigName() + "'失败,参数键名已存在");
        }
        config.setCreateBy(SecurityUtils.getUsername());
        config.setCreateBy(getUsername());
        return toAjax(configService.insertConfig(config));
    }
@@ -98,7 +97,7 @@
        {
            return AjaxResult.error("修改参数'" + config.getConfigName() + "'失败,参数键名已存在");
        }
        config.setUpdateBy(SecurityUtils.getUsername());
        config.setUpdateBy(getUsername());
        return toAjax(configService.updateConfig(config));
    }
ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysDeptController.java
@@ -1,15 +1,14 @@
package com.ruoyi.web.controller.system;
import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.ArrayUtil;
import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.constant.UserConstants;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.domain.entity.SysDept;
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.system.service.ISysDeptService;
import org.apache.commons.lang3.ArrayUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
@@ -56,7 +55,7 @@
        {
            SysDept d = (SysDept) it.next();
            if (d.getDeptId().intValue() == deptId
                    || ArrayUtils.contains(StrUtil.splitToArray(d.getAncestors(), ","), deptId + ""))
                    || ArrayUtil.contains(StringUtils.split(d.getAncestors(), ","), deptId + ""))
            {
                it.remove();
            }
@@ -109,7 +108,7 @@
        {
            return AjaxResult.error("新增部门'" + dept.getDeptName() + "'失败,部门名称已存在");
        }
        dept.setCreateBy(SecurityUtils.getUsername());
        dept.setCreateBy(getUsername());
        return toAjax(deptService.insertDept(dept));
    }
@@ -129,12 +128,12 @@
        {
            return AjaxResult.error("修改部门'" + dept.getDeptName() + "'失败,上级部门不能是自己");
        }
        else if (StrUtil.equals(UserConstants.DEPT_DISABLE, dept.getStatus())
        else if (StringUtils.equals(UserConstants.DEPT_DISABLE, dept.getStatus())
                && deptService.selectNormalChildrenDeptById(dept.getDeptId()) > 0)
        {
            return AjaxResult.error("该部门包含未停用的子部门!");
        }
        dept.setUpdateBy(SecurityUtils.getUsername());
        dept.setUpdateBy(getUsername());
        return toAjax(deptService.updateDept(dept));
    }
ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysDictDataController.java
@@ -1,13 +1,12 @@
package com.ruoyi.web.controller.system;
import cn.hutool.core.lang.Validator;
import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.domain.entity.SysDictData;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.poi.ExcelUtil;
import com.ruoyi.system.service.ISysDictDataService;
import com.ruoyi.system.service.ISysDictTypeService;
@@ -16,6 +15,7 @@
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletResponse;
import java.util.ArrayList;
import java.util.List;
@@ -44,11 +44,10 @@
    @Log(title = "字典数据", businessType = BusinessType.EXPORT)
    @PreAuthorize("@ss.hasPermi('system:dict:export')")
    @GetMapping("/export")
    public AjaxResult export(SysDictData dictData)
    public void export(SysDictData dictData, HttpServletResponse response)
    {
        List<SysDictData> list = dictDataService.selectDictDataList(dictData);
        ExcelUtil<SysDictData> util = new ExcelUtil<SysDictData>(SysDictData.class);
        return util.exportExcel(list, "字典数据");
        ExcelUtil.exportExcel(list, "字典数据", SysDictData.class, response);
    }
    /**
@@ -68,7 +67,7 @@
    public AjaxResult dictType(@PathVariable String dictType)
    {
        List<SysDictData> data = dictTypeService.selectDictDataByType(dictType);
        if (Validator.isNull(data))
        if (StringUtils.isNull(data))
        {
            data = new ArrayList<SysDictData>();
        }
@@ -83,7 +82,7 @@
    @PostMapping
    public AjaxResult add(@Validated @RequestBody SysDictData dict)
    {
        dict.setCreateBy(SecurityUtils.getUsername());
        dict.setCreateBy(getUsername());
        return toAjax(dictDataService.insertDictData(dict));
    }
@@ -95,7 +94,7 @@
    @PutMapping
    public AjaxResult edit(@Validated @RequestBody SysDictData dict)
    {
        dict.setUpdateBy(SecurityUtils.getUsername());
        dict.setUpdateBy(getUsername());
        return toAjax(dictDataService.updateDictData(dict));
    }
ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysDictTypeController.java
@@ -7,7 +7,6 @@
import com.ruoyi.common.core.domain.entity.SysDictType;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.poi.ExcelUtil;
import com.ruoyi.system.service.ISysDictTypeService;
import org.springframework.beans.factory.annotation.Autowired;
@@ -15,6 +14,7 @@
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletResponse;
import java.util.List;
/**
@@ -39,11 +39,10 @@
    @Log(title = "字典类型", businessType = BusinessType.EXPORT)
    @PreAuthorize("@ss.hasPermi('system:dict:export')")
    @GetMapping("/export")
    public AjaxResult export(SysDictType dictType)
    public void export(SysDictType dictType, HttpServletResponse response)
    {
        List<SysDictType> list = dictTypeService.selectDictTypeList(dictType);
        ExcelUtil<SysDictType> util = new ExcelUtil<SysDictType>(SysDictType.class);
        return util.exportExcel(list, "字典类型");
        ExcelUtil.exportExcel(list, "字典类型", SysDictType.class, response);
    }
    /**
@@ -68,7 +67,7 @@
        {
            return AjaxResult.error("新增字典'" + dict.getDictName() + "'失败,字典类型已存在");
        }
        dict.setCreateBy(SecurityUtils.getUsername());
        dict.setCreateBy(getUsername());
        return toAjax(dictTypeService.insertDictType(dict));
    }
@@ -84,7 +83,7 @@
        {
            return AjaxResult.error("修改字典'" + dict.getDictName() + "'失败,字典类型已存在");
        }
        dict.setUpdateBy(SecurityUtils.getUsername());
        dict.setUpdateBy(getUsername());
        return toAjax(dictTypeService.updateDictType(dict));
    }
ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysIndexController.java
@@ -1,6 +1,6 @@
package com.ruoyi.web.controller.system;
import cn.hutool.core.util.StrUtil;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.config.RuoYiConfig;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
@@ -24,6 +24,6 @@
    @RequestMapping("/")
    public String index()
    {
        return StrUtil.format("欢迎使用{}后台管理框架,当前版本:v{},请通过前端地址访问。", ruoyiConfig.getName(), ruoyiConfig.getVersion());
        return StringUtils.format("欢迎使用{}后台管理框架,当前版本:v{},请通过前端地址访问。", ruoyiConfig.getName(), ruoyiConfig.getVersion());
    }
}
ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysLoginController.java
@@ -6,6 +6,7 @@
import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.core.domain.model.LoginBody;
import com.ruoyi.common.core.domain.model.LoginUser;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.ServletUtils;
import com.ruoyi.framework.web.service.SysLoginService;
import com.ruoyi.framework.web.service.SysPermissionService;
@@ -88,10 +89,8 @@
    @GetMapping("getRouters")
    public AjaxResult getRouters()
    {
        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
        // ç”¨æˆ·ä¿¡æ¯
        SysUser user = loginUser.getUser();
        List<SysMenu> menus = menuService.selectMenuTreeByUserId(user.getUserId());
        Long userId = SecurityUtils.getUserId();
        List<SysMenu> menus = menuService.selectMenuTreeByUserId(userId);
        return AjaxResult.success(menuService.buildMenus(menus));
    }
}
ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysMenuController.java
@@ -1,16 +1,12 @@
package com.ruoyi.web.controller.system;
import cn.hutool.core.lang.Validator;
import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.constant.UserConstants;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.domain.entity.SysMenu;
import com.ruoyi.common.core.domain.model.LoginUser;
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.ServletUtils;
import com.ruoyi.framework.web.service.TokenService;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.system.service.ISysMenuService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
@@ -33,9 +29,6 @@
    @Autowired
    private ISysMenuService menuService;
    @Autowired
    private TokenService tokenService;
    /**
     * èŽ·å–èœå•åˆ—è¡¨
     */
@@ -43,9 +36,7 @@
    @GetMapping("/list")
    public AjaxResult list(SysMenu menu)
    {
        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
        Long userId = loginUser.getUser().getUserId();
        List<SysMenu> menus = menuService.selectMenuList(menu, userId);
        List<SysMenu> menus = menuService.selectMenuList(menu, getUserId());
        return AjaxResult.success(menus);
    }
@@ -65,9 +56,7 @@
    @GetMapping("/treeselect")
    public AjaxResult treeselect(SysMenu menu)
    {
        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
        Long userId = loginUser.getUser().getUserId();
        List<SysMenu> menus = menuService.selectMenuList(menu, userId);
        List<SysMenu> menus = menuService.selectMenuList(menu, getUserId());
        return AjaxResult.success(menuService.buildMenuTreeSelect(menus));
    }
@@ -77,8 +66,7 @@
    @GetMapping(value = "/roleMenuTreeselect/{roleId}")
    public AjaxResult roleMenuTreeselect(@PathVariable("roleId") Long roleId)
    {
        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
        List<SysMenu> menus = menuService.selectMenuList(loginUser.getUser().getUserId());
        List<SysMenu> menus = menuService.selectMenuList(getUserId());
        Map<String,Object> ajax = new HashMap<>();
        ajax.put("checkedKeys", menuService.selectMenuListByRoleId(roleId));
        ajax.put("menus", menuService.buildMenuTreeSelect(menus));
@@ -97,11 +85,11 @@
        {
            return AjaxResult.error("新增菜单'" + menu.getMenuName() + "'失败,菜单名称已存在");
        }
        else if (UserConstants.YES_FRAME.equals(menu.getIsFrame()) && !Validator.isUrl(menu.getPath()))
        else if (UserConstants.YES_FRAME.equals(menu.getIsFrame()) && !StringUtils.ishttp(menu.getPath()))
        {
            return AjaxResult.error("新增菜单'" + menu.getMenuName() + "'失败,地址必须以http(s)://开头");
        }
        menu.setCreateBy(SecurityUtils.getUsername());
        menu.setCreateBy(getUsername());
        return toAjax(menuService.insertMenu(menu));
    }
@@ -117,7 +105,7 @@
        {
            return AjaxResult.error("修改菜单'" + menu.getMenuName() + "'失败,菜单名称已存在");
        }
        else if (UserConstants.YES_FRAME.equals(menu.getIsFrame()) && !Validator.isUrl(menu.getPath()))
        else if (UserConstants.YES_FRAME.equals(menu.getIsFrame()) && !StringUtils.ishttp(menu.getPath()))
        {
            return AjaxResult.error("修改菜单'" + menu.getMenuName() + "'失败,地址必须以http(s)://开头");
        }
@@ -125,7 +113,7 @@
        {
            return AjaxResult.error("修改菜单'" + menu.getMenuName() + "'失败,上级菜单不能选择自己");
        }
        menu.setUpdateBy(SecurityUtils.getUsername());
        menu.setUpdateBy(getUsername());
        return toAjax(menuService.updateMenu(menu));
    }
ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysNoticeController.java
@@ -17,13 +17,12 @@
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.system.domain.SysNotice;
import com.ruoyi.system.service.ISysNoticeService;
/**
 * å…¬å‘Š ä¿¡æ¯æ“ä½œå¤„ç†
 *
 *
 * @author ruoyi
 */
@RestController
@@ -61,7 +60,7 @@
    @PostMapping
    public AjaxResult add(@Validated @RequestBody SysNotice notice)
    {
        notice.setCreateBy(SecurityUtils.getUsername());
        notice.setCreateBy(getUsername());
        return toAjax(noticeService.insertNotice(notice));
    }
@@ -73,7 +72,7 @@
    @PutMapping
    public AjaxResult edit(@Validated @RequestBody SysNotice notice)
    {
        notice.setUpdateBy(SecurityUtils.getUsername());
        notice.setUpdateBy(getUsername());
        return toAjax(noticeService.updateNotice(notice));
    }
ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysOssConfigController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,108 @@
package com.ruoyi.web.controller.system;
import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.annotation.RepeatSubmit;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.core.validate.AddGroup;
import com.ruoyi.common.core.validate.EditGroup;
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.system.domain.bo.SysOssConfigBo;
import com.ruoyi.system.domain.vo.SysOssConfigVo;
import com.ruoyi.system.service.ISysOssConfigService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import java.util.Arrays;
/**
 * äº‘存储配置Controller
 *
 * @author Lion Li
 * @author å­¤èˆŸçƒŸé›¨
 * @date 2021-08-13
 */
@Validated
@Api(value = "云存储配置控制器", tags = {"云存储配置管理"})
@RequiredArgsConstructor(onConstructor_ = @Autowired)
@RestController
@RequestMapping("/system/oss/config")
public class SysOssConfigController extends BaseController {
    private final ISysOssConfigService iSysOssConfigService;
    /**
     * æŸ¥è¯¢äº‘存储配置列表
     */
    @ApiOperation("查询云存储配置列表")
    @PreAuthorize("@ss.hasPermi('system:oss:list')")
    @GetMapping("/list")
    public TableDataInfo<SysOssConfigVo> list(@Validated SysOssConfigBo bo) {
        return iSysOssConfigService.queryPageList(bo);
    }
    /**
     * èŽ·å–äº‘å­˜å‚¨é…ç½®è¯¦ç»†ä¿¡æ¯
     */
    @ApiOperation("获取云存储配置详细信息")
    @PreAuthorize("@ss.hasPermi('system:oss:query')")
    @GetMapping("/{ossConfigId}")
    public AjaxResult<SysOssConfigVo> getInfo(@NotNull(message = "主键不能为空")
                                              @PathVariable("ossConfigId") Integer ossConfigId) {
        return AjaxResult.success(iSysOssConfigService.queryById(ossConfigId));
    }
    /**
     * æ–°å¢žäº‘存储配置
     */
    @ApiOperation("新增云存储配置")
    @PreAuthorize("@ss.hasPermi('system:oss:add')")
    @Log(title = "云存储配置", businessType = BusinessType.INSERT)
    @RepeatSubmit()
    @PostMapping()
    public AjaxResult<Void> add(@Validated(AddGroup.class) @RequestBody SysOssConfigBo bo) {
        return toAjax(iSysOssConfigService.insertByBo(bo) ? 1 : 0);
    }
    /**
     * ä¿®æ”¹äº‘存储配置
     */
    @ApiOperation("修改云存储配置")
    @PreAuthorize("@ss.hasPermi('system:oss:edit')")
    @Log(title = "云存储配置", businessType = BusinessType.UPDATE)
    @RepeatSubmit()
    @PutMapping()
    public AjaxResult<Void> edit(@Validated(EditGroup.class) @RequestBody SysOssConfigBo bo) {
        return toAjax(iSysOssConfigService.updateByBo(bo) ? 1 : 0);
    }
    /**
     * åˆ é™¤äº‘存储配置
     */
    @ApiOperation("删除云存储配置")
    @PreAuthorize("@ss.hasPermi('system:oss:remove')")
    @Log(title = "云存储配置", businessType = BusinessType.DELETE)
    @DeleteMapping("/{ossConfigIds}")
    public AjaxResult<Void> remove(@NotEmpty(message = "主键不能为空")
                                   @PathVariable Long[] ossConfigIds) {
        return toAjax(iSysOssConfigService.deleteWithValidByIds(Arrays.asList(ossConfigIds), true) ? 1 : 0);
    }
    /**
     * çŠ¶æ€ä¿®æ”¹
     */
    @PreAuthorize("@ss.hasPermi('system:oss:edit')")
    @Log(title = "云存储状态修改", businessType = BusinessType.UPDATE)
    @PutMapping("/changeStatus")
    public AjaxResult changeStatus(@RequestBody SysOssConfigBo bo) {
        return toAjax(iSysOssConfigService.updateOssConfigStatus(bo));
    }
}
ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysOssController.java
ÎļþÃû´Ó ruoyi-oss/src/main/java/com/ruoyi/system/controller/SysOssController.java ÐÞ¸Ä
@@ -1,20 +1,26 @@
package com.ruoyi.system.controller;
package com.ruoyi.web.controller.system;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.http.HttpUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.annotation.RepeatSubmit;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.common.exception.CustomException;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.JsonUtils;
import com.ruoyi.common.utils.file.FileUtils;
import com.ruoyi.system.domain.bo.SysOssBo;
import com.ruoyi.oss.constant.CloudConstant;
import com.ruoyi.system.domain.SysConfig;
import com.ruoyi.system.domain.SysOss;
import com.ruoyi.system.service.ISysOssService;
import com.ruoyi.system.domain.bo.SysOssBo;
import com.ruoyi.system.domain.vo.SysOssVo;
import com.ruoyi.system.service.ISysConfigService;
import com.ruoyi.system.service.ISysOssService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
@@ -49,6 +55,7 @@
public class SysOssController extends BaseController {
    private final ISysOssService iSysOssService;
    private final ISysConfigService iSysConfigService;
    /**
     * æŸ¥è¯¢OSS云存储列表
@@ -72,8 +79,8 @@
    @RepeatSubmit
    @PostMapping("/upload")
    public AjaxResult<Map<String, String>> upload(@RequestPart("file") MultipartFile file) {
        if (file.isEmpty()) {
            throw new CustomException("上传文件不能为空");
        if (ObjectUtil.isNull(file)) {
            throw new ServiceException("上传文件不能为空");
        }
        SysOss oss = iSysOssService.upload(file);
        Map<String, String> map = new HashMap<>(2);
@@ -87,8 +94,8 @@
    @GetMapping("/download/{ossId}")
    public void download(@PathVariable Long ossId, HttpServletResponse response) throws IOException {
        SysOss sysOss = iSysOssService.getById(ossId);
        if (sysOss == null) {
            throw new CustomException("文件数据不存在!");
        if (ObjectUtil.isNull(sysOss)) {
            throw new ServiceException("文件数据不存在!");
        }
        response.reset();
        response.addHeader("Access-Control-Allow-Origin", "*");
@@ -111,4 +118,19 @@
        return toAjax(iSysOssService.deleteWithValidByIds(Arrays.asList(ossIds), true) ? 1 : 0);
    }
    /**
     * å˜æ›´å›¾ç‰‡åˆ—表预览状态
     */
    @ApiOperation("变更图片列表预览状态")
    @PreAuthorize("@ss.hasPermi('system:oss:edit')")
    @Log(title = "OSS云存储" , businessType = BusinessType.UPDATE)
    @PutMapping("/changePreviewListResource")
    public AjaxResult<Void> changePreviewListResource(@RequestBody String body) {
        Map<String, Boolean> map = JsonUtils.parseMap(body);
        SysConfig config = iSysConfigService.getOne(new LambdaQueryWrapper<SysConfig>()
            .eq(SysConfig::getConfigKey, CloudConstant.PEREVIEW_LIST_RESOURCE_KEY));
        config.setConfigValue(map.get("previewListResource").toString());
        return toAjax(iSysConfigService.updateConfig(config));
    }
}
ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysPostController.java
@@ -1,31 +1,25 @@
package com.ruoyi.web.controller.system;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.constant.UserConstants;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.poi.ExcelUtil;
import com.ruoyi.system.domain.SysPost;
import com.ruoyi.system.service.ISysPostService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletResponse;
import java.util.List;
/**
 * å²—位信息操作处理
 *
 *
 * @author ruoyi
 */
@RestController
@@ -44,15 +38,14 @@
    {
        return postService.selectPagePostList(post);
    }
    @Log(title = "岗位管理", businessType = BusinessType.EXPORT)
    @PreAuthorize("@ss.hasPermi('system:post:export')")
    @GetMapping("/export")
    public AjaxResult export(SysPost post)
    public void export(SysPost post, HttpServletResponse response)
    {
        List<SysPost> list = postService.selectPostList(post);
        ExcelUtil<SysPost> util = new ExcelUtil<SysPost>(SysPost.class);
        return util.exportExcel(list, "岗位数据");
        ExcelUtil.exportExcel(list, "岗位数据", SysPost.class, response);
    }
    /**
@@ -81,7 +74,7 @@
        {
            return AjaxResult.error("新增岗位'" + post.getPostName() + "'失败,岗位编码已存在");
        }
        post.setCreateBy(SecurityUtils.getUsername());
        post.setCreateBy(getUsername());
        return toAjax(postService.insertPost(post));
    }
@@ -101,7 +94,7 @@
        {
            return AjaxResult.error("修改岗位'" + post.getPostName() + "'失败,岗位编码已存在");
        }
        post.setUpdateBy(SecurityUtils.getUsername());
        post.setUpdateBy(getUsername());
        return toAjax(postService.updatePost(post));
    }
ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysProfileController.java
@@ -1,8 +1,6 @@
package com.ruoyi.web.controller.system;
import cn.hutool.core.util.StrUtil;
import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.config.RuoYiConfig;
import com.ruoyi.common.constant.UserConstants;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
@@ -11,8 +9,10 @@
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.ServletUtils;
import com.ruoyi.common.utils.file.FileUploadUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.framework.web.service.TokenService;
import com.ruoyi.system.domain.SysOss;
import com.ruoyi.system.service.ISysOssService;
import com.ruoyi.system.service.ISysUserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
@@ -37,6 +37,9 @@
    @Autowired
    private TokenService tokenService;
    @Autowired
    private ISysOssService iSysOssService;
    /**
     * ä¸ªäººä¿¡æ¯
     */
@@ -59,12 +62,12 @@
    @PutMapping
    public AjaxResult updateProfile(@RequestBody SysUser user)
    {
        if (StrUtil.isNotEmpty(user.getPhonenumber())
        if (StringUtils.isNotEmpty(user.getPhonenumber())
                && UserConstants.NOT_UNIQUE.equals(userService.checkPhoneUnique(user)))
        {
            return AjaxResult.error("修改用户'" + user.getUserName() + "'失败,手机号码已存在");
        }
        if (StrUtil.isNotEmpty(user.getEmail())
        if (StringUtils.isNotEmpty(user.getEmail())
                && UserConstants.NOT_UNIQUE.equals(userService.checkEmailUnique(user)))
        {
            return AjaxResult.error("修改用户'" + user.getUserName() + "'失败,邮箱账号已存在");
@@ -124,7 +127,8 @@
        if (!file.isEmpty())
        {
            LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
            String avatar = FileUploadUtils.upload(RuoYiConfig.getAvatarPath(), file);
            SysOss oss = iSysOssService.upload(file);
            String avatar = oss.getUrl();
            if (userService.updateUserAvatar(loginUser.getUsername(), avatar))
            {
                Map<String,Object> ajax = new HashMap<>();
ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysRegisterController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,38 @@
package com.ruoyi.web.controller.system;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.domain.model.RegisterBody;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.framework.web.service.SysRegisterService;
import com.ruoyi.system.service.ISysConfigService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
/**
 * æ³¨å†ŒéªŒè¯
 *
 * @author ruoyi
 */
@RestController
public class SysRegisterController extends BaseController
{
    @Autowired
    private SysRegisterService registerService;
    @Autowired
    private ISysConfigService configService;
    @PostMapping("/register")
    public AjaxResult register(@RequestBody RegisterBody user)
    {
        if (!("true".equals(configService.selectConfigByKey("sys.account.registerUser"))))
        {
            return error("当前系统没有开启注册功能!");
        }
        String msg = registerService.register(user);
        return StringUtils.isEmpty(msg) ? success() : error(msg);
    }
}
ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysRoleController.java
@@ -1,6 +1,5 @@
package com.ruoyi.web.controller.system;
import cn.hutool.core.lang.Validator;
import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.constant.UserConstants;
import com.ruoyi.common.core.controller.BaseController;
@@ -10,8 +9,8 @@
import com.ruoyi.common.core.domain.model.LoginUser;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.ServletUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.poi.ExcelUtil;
import com.ruoyi.framework.web.service.SysPermissionService;
import com.ruoyi.framework.web.service.TokenService;
@@ -23,6 +22,7 @@
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletResponse;
import java.util.List;
/**
@@ -56,11 +56,10 @@
    @Log(title = "角色管理", businessType = BusinessType.EXPORT)
    @PreAuthorize("@ss.hasPermi('system:role:export')")
    @GetMapping("/export")
    public AjaxResult export(SysRole role)
    public void export(SysRole role, HttpServletResponse response)
    {
        List<SysRole> list = roleService.selectRoleList(role);
        ExcelUtil<SysRole> util = new ExcelUtil<SysRole>(SysRole.class);
        return util.exportExcel(list, "角色数据");
        ExcelUtil.exportExcel(list, "角色数据", SysRole.class, response);
    }
    /**
@@ -89,7 +88,7 @@
        {
            return AjaxResult.error("新增角色'" + role.getRoleName() + "'失败,角色权限已存在");
        }
        role.setCreateBy(SecurityUtils.getUsername());
        role.setCreateBy(getUsername());
        return toAjax(roleService.insertRole(role));
    }
@@ -111,13 +110,13 @@
        {
            return AjaxResult.error("修改角色'" + role.getRoleName() + "'失败,角色权限已存在");
        }
        role.setUpdateBy(SecurityUtils.getUsername());
        role.setUpdateBy(getUsername());
        if (roleService.updateRole(role) > 0)
        {
            // æ›´æ–°ç¼“存用户权限
            LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
            if (Validator.isNotNull(loginUser.getUser()) && !loginUser.getUser().isAdmin())
            if (StringUtils.isNotNull(loginUser.getUser()) && !loginUser.getUser().isAdmin())
            {
                loginUser.setPermissions(permissionService.getMenuPermission(loginUser.getUser()));
                loginUser.setUser(userService.selectUserByUserName(loginUser.getUser().getUserName()));
@@ -149,7 +148,7 @@
    public AjaxResult changeStatus(@RequestBody SysRole role)
    {
        roleService.checkRoleAllowed(role);
        role.setUpdateBy(SecurityUtils.getUsername());
        role.setUpdateBy(getUsername());
        return toAjax(roleService.updateRoleStatus(role));
    }
ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysUserController.java
@@ -1,10 +1,13 @@
package com.ruoyi.web.controller.system;
import cn.hutool.core.lang.Validator;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.ObjectUtil;
import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.constant.UserConstants;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.domain.entity.SysDept;
import com.ruoyi.common.core.domain.entity.SysRole;
import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.core.domain.model.LoginUser;
@@ -12,8 +15,11 @@
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.ServletUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.poi.ExcelUtil;
import com.ruoyi.framework.web.service.TokenService;
import com.ruoyi.system.domain.vo.SysUserExportVo;
import com.ruoyi.system.domain.vo.SysUserImportVo;
import com.ruoyi.system.service.ISysPostService;
import com.ruoyi.system.service.ISysRoleService;
import com.ruoyi.system.service.ISysUserService;
@@ -23,6 +29,8 @@
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletResponse;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -62,11 +70,19 @@
    @Log(title = "用户管理", businessType = BusinessType.EXPORT)
    @PreAuthorize("@ss.hasPermi('system:user:export')")
    @GetMapping("/export")
    public AjaxResult export(SysUser user)
    public void export(SysUser user, HttpServletResponse response)
    {
        List<SysUser> list = userService.selectUserList(user);
        ExcelUtil<SysUser> util = new ExcelUtil<SysUser>(SysUser.class);
        return util.exportExcel(list, "用户数据");
        List<SysUserExportVo> listVo = BeanUtil.copyToList(list, SysUserExportVo.class);
        for (int i = 0; i < list.size(); i++) {
            SysDept dept = list.get(i).getDept();
            SysUserExportVo vo = listVo.get(i);
            if (ObjectUtil.isNotEmpty(dept)) {
                vo.setDeptName(dept.getDeptName());
                vo.setLeader(dept.getLeader());
            }
        }
        ExcelUtil.exportExcel(listVo, "用户数据", SysUserExportVo.class, response);
    }
    @Log(title = "用户管理", businessType = BusinessType.IMPORT)
@@ -74,19 +90,18 @@
    @PostMapping("/importData")
    public AjaxResult importData(MultipartFile file, boolean updateSupport) throws Exception
    {
        ExcelUtil<SysUser> util = new ExcelUtil<SysUser>(SysUser.class);
        List<SysUser> userList = util.importExcel(file.getInputStream());
        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
        List<SysUserImportVo> userListVo = ExcelUtil.importExcel(file.getInputStream(), SysUserImportVo.class);
        List<SysUser> userList = BeanUtil.copyToList(userListVo, SysUser.class);
        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
        String operName = loginUser.getUsername();
        String message = userService.importUser(userList, updateSupport, operName);
        return AjaxResult.success(message);
    }
    @GetMapping("/importTemplate")
    public AjaxResult importTemplate()
    public void importTemplate(HttpServletResponse response)
    {
        ExcelUtil<SysUser> util = new ExcelUtil<SysUser>(SysUser.class);
        return util.importTemplateExcel("用户数据");
        ExcelUtil.exportExcel(new ArrayList<>(), "用户数据", SysUserImportVo.class, response);
    }
    /**
@@ -100,7 +115,7 @@
        List<SysRole> roles = roleService.selectRoleAll();
        ajax.put("roles", SysUser.isAdmin(userId) ? roles : roles.stream().filter(r -> !r.isAdmin()).collect(Collectors.toList()));
        ajax.put("posts", postService.selectPostAll());
        if (Validator.isNotNull(userId))
        if (StringUtils.isNotNull(userId))
        {
            ajax.put("user", userService.selectUserById(userId));
            ajax.put("postIds", postService.selectPostListByUserId(userId));
@@ -121,17 +136,17 @@
        {
            return AjaxResult.error("新增用户'" + user.getUserName() + "'失败,登录账号已存在");
        }
        else if (Validator.isNotEmpty(user.getPhonenumber())
        else if (StringUtils.isNotEmpty(user.getPhonenumber())
                && UserConstants.NOT_UNIQUE.equals(userService.checkPhoneUnique(user)))
        {
            return AjaxResult.error("新增用户'" + user.getUserName() + "'失败,手机号码已存在");
        }
        else if (Validator.isNotEmpty(user.getEmail())
        else if (StringUtils.isNotEmpty(user.getEmail())
                && UserConstants.NOT_UNIQUE.equals(userService.checkEmailUnique(user)))
        {
            return AjaxResult.error("新增用户'" + user.getUserName() + "'失败,邮箱账号已存在");
        }
        user.setCreateBy(SecurityUtils.getUsername());
        user.setCreateBy(getUsername());
        user.setPassword(SecurityUtils.encryptPassword(user.getPassword()));
        return toAjax(userService.insertUser(user));
    }
@@ -145,17 +160,17 @@
    public AjaxResult edit(@Validated @RequestBody SysUser user)
    {
        userService.checkUserAllowed(user);
        if (Validator.isNotEmpty(user.getPhonenumber())
        if (StringUtils.isNotEmpty(user.getPhonenumber())
                && UserConstants.NOT_UNIQUE.equals(userService.checkPhoneUnique(user)))
        {
            return AjaxResult.error("修改用户'" + user.getUserName() + "'失败,手机号码已存在");
        }
        else if (Validator.isNotEmpty(user.getEmail())
        else if (StringUtils.isNotEmpty(user.getEmail())
                && UserConstants.NOT_UNIQUE.equals(userService.checkEmailUnique(user)))
        {
            return AjaxResult.error("修改用户'" + user.getUserName() + "'失败,邮箱账号已存在");
        }
        user.setUpdateBy(SecurityUtils.getUsername());
        user.setUpdateBy(getUsername());
        return toAjax(userService.updateUser(user));
    }
@@ -167,6 +182,10 @@
    @DeleteMapping("/{userIds}")
    public AjaxResult remove(@PathVariable Long[] userIds)
    {
        if (ArrayUtil.contains(userIds, getUserId()))
        {
            return error("当前用户不能删除");
        }
        return toAjax(userService.deleteUserByIds(userIds));
    }
@@ -180,7 +199,7 @@
    {
        userService.checkUserAllowed(user);
        user.setPassword(SecurityUtils.encryptPassword(user.getPassword()));
        user.setUpdateBy(SecurityUtils.getUsername());
        user.setUpdateBy(getUsername());
        return toAjax(userService.resetPwd(user));
    }
@@ -193,7 +212,7 @@
    public AjaxResult changeStatus(@RequestBody SysUser user)
    {
        userService.checkUserAllowed(user);
        user.setUpdateBy(SecurityUtils.getUsername());
        user.setUpdateBy(getUsername());
        return toAjax(userService.updateUserStatus(user));
    }
ruoyi-admin/src/main/resources/application-dev.yml
@@ -4,7 +4,9 @@
    type: com.alibaba.druid.pool.DruidDataSource
    # åŠ¨æ€æ•°æ®æºæ–‡æ¡£ https://www.kancloud.cn/tracy5546/dynamic-datasource/content
    dynamic:
      #设置默认的数据源或者数据源组,默认值即为 master
      # æ€§èƒ½åˆ†æžæ’ä»¶(有性能损耗 ä¸å»ºè®®ç”Ÿäº§çŽ¯å¢ƒä½¿ç”¨)
      p6spy: true
      # è®¾ç½®é»˜è®¤çš„æ•°æ®æºæˆ–者数据源组,默认值即为 master
      primary: master
      datasource:
        # ä¸»åº“数据源
@@ -122,62 +124,11 @@
    admin:
      # Spring Boot Admin Client å®¢æˆ·ç«¯çš„相关配置
      client:
        # å¢žåŠ å®¢æˆ·ç«¯å¼€å…³
        enabled: true
        # è®¾ç½® Spring Boot Admin Server åœ°å€
        url: http://localhost:9090/admin
        instance:
          prefer-ip: true # æ³¨å†Œå®žä¾‹æ—¶ï¼Œä¼˜å…ˆä½¿ç”¨ IP
        username: ruoyi
        password: 123456
# Actuator ç›‘控端点的配置项
management:
  endpoints:
    web:
      # Actuator æä¾›çš„ API æŽ¥å£çš„æ ¹ç›®å½•。默认为 /actuator
      base-path: /actuator
      exposure:
        # éœ€è¦å¼€æ”¾çš„端点。默认值只打开 health å’Œ info ä¸¤ä¸ªç«¯ç‚¹ã€‚通过设置 * ï¼Œå¯ä»¥å¼€æ”¾æ‰€æœ‰ç«¯ç‚¹ã€‚
        # ç”Ÿäº§çŽ¯å¢ƒä¸å»ºè®®æ”¾å¼€æ‰€æœ‰ æ ¹æ®é¡¹ç›®éœ€æ±‚放开即可
        include: '*'
  endpoint:
    logfile:
      external-file: ./logs/sys-console.log
--- # OSS äº‘存储(界面 <参数设置> å¯åˆ‡æ¢)
cloud-storage:
  # minio配置
  minio:
    endpoint: http://localhost:9000
    accessKey: ruoyi
    secretKey: ruoyi123
    bucketName: ruoyi
  # ä¸ƒç‰›äº‘配置
  qiniu:
    domain: http://XXX.XXXX.com
    prefix:
    accessKey: XXXXXXXXXXXXXXX
    secretKey: XXXXXXXXXXXXXXX
    bucketName: ruoyi
    isHttps: false
    # z0 åŽä¸œ  z1 åŽåŒ—  z2 åŽå—  na0 åŒ—美  as0 ä¸œå—亚
    # ä¸å¡«ä¸ºè‡ªåŠ¨èŽ·å–(性能低 æ˜“出问题)
    region: z0
  # é˜¿é‡Œäº‘配置
  aliyun:
    endpoint: http://oss-cn-beijing.aliyuncs.com
    prefix:
    accessKeyId: XXXXXXXXXXXXXXX
    accessKeySecret: XXXXXXXXXXXXXXX
    bucketName: ruoyi
  # è…¾è®¯äº‘配置
  qcloud:
    endpoint: http://cos.ap-beijing.myqcloud.com
    prefix:
    secretId: XXXXXXXXXXXXXXX
    secretKey: XXXXXXXXXXXXXXX
    # è…¾è®¯äº‘bucket名规则 æ ¼å¼ä¸º BucketName-APPID æ­¤å¤„填写的存储桶名称必须为此格式
    bucketName: ruoyi-1250000000
    isHttps: false
    # åœ°åŸŸåå‚考官方文档
    # https://cloud.tencent.com/document/product/436/6224
    region: ap-beijing
ruoyi-admin/src/main/resources/application-prod.yml
@@ -4,7 +4,9 @@
    type: com.alibaba.druid.pool.DruidDataSource
    # åŠ¨æ€æ•°æ®æºæ–‡æ¡£ https://www.kancloud.cn/tracy5546/dynamic-datasource/content
    dynamic:
      #设置默认的数据源或者数据源组,默认值即为 master
      # æ€§èƒ½åˆ†æžæ’ä»¶(有性能损耗 ä¸å»ºè®®ç”Ÿäº§çŽ¯å¢ƒä½¿ç”¨)
      p6spy: false
      # è®¾ç½®é»˜è®¤çš„æ•°æ®æºæˆ–者数据源组,默认值即为 master
      primary: master
      datasource:
        # ä¸»åº“数据源
@@ -122,62 +124,11 @@
    admin:
      # Spring Boot Admin Client å®¢æˆ·ç«¯çš„相关配置
      client:
        # å¢žåŠ å®¢æˆ·ç«¯å¼€å…³
        enabled: true
        # è®¾ç½® Spring Boot Admin Server åœ°å€
        url: http://172.30.0.90:9090/admin
        instance:
          prefer-ip: true # æ³¨å†Œå®žä¾‹æ—¶ï¼Œä¼˜å…ˆä½¿ç”¨ IP
        username: ruoyi
        password: 123456
# Actuator ç›‘控端点的配置项
management:
  endpoints:
    web:
      # Actuator æä¾›çš„ API æŽ¥å£çš„æ ¹ç›®å½•。默认为 /actuator
      base-path: /actuator
      exposure:
        # éœ€è¦å¼€æ”¾çš„端点。默认值只打开 health å’Œ info ä¸¤ä¸ªç«¯ç‚¹ã€‚通过设置 * ï¼Œå¯ä»¥å¼€æ”¾æ‰€æœ‰ç«¯ç‚¹ã€‚
        # ç”Ÿäº§çŽ¯å¢ƒä¸å»ºè®®æ”¾å¼€æ‰€æœ‰ æ ¹æ®é¡¹ç›®éœ€æ±‚放开即可
        include: health,info
  endpoint:
    logfile:
      external-file: ./logs/sys-console.log
--- # OSS äº‘存储(界面 <参数设置> å¯åˆ‡æ¢)
cloud-storage:
  # minio配置
  minio:
    endpoint: http://172.30.0.54:9000
    accessKey: ruoyi
    secretKey: ruoyi123
    bucketName: ruoyi
  # ä¸ƒç‰›äº‘配置
  qiniu:
    domain: http://XXX.XXXX.com
    prefix:
    accessKey: XXXXXXXXXXXXXXX
    secretKey: XXXXXXXXXXXXXXX
    bucketName: ruoyi
    isHttps: false
    # z0 åŽä¸œ  z1 åŽåŒ—  z2 åŽå—  na0 åŒ—美  as0 ä¸œå—亚
    # ä¸å¡«ä¸ºè‡ªåŠ¨èŽ·å–(性能低 æ˜“出问题)
    region: z0
  # é˜¿é‡Œäº‘配置
  aliyun:
    endpoint: http://oss-cn-beijing.aliyuncs.com
    prefix:
    accessKeyId: XXXXXXXXXXXXXXX
    accessKeySecret: XXXXXXXXXXXXXXX
    bucketName: ruoyi
  # è…¾è®¯äº‘配置
  qcloud:
    endpoint: http://cos.ap-beijing.myqcloud.com
    prefix:
    secretId: XXXXXXXXXXXXXXX
    secretKey: XXXXXXXXXXXXXXX
    # è…¾è®¯äº‘bucket名规则 æ ¼å¼ä¸º BucketName-APPID æ­¤å¤„填写的存储桶名称必须为此格式
    bucketName: ruoyi-1250000000
    isHttps: false
    # åœ°åŸŸåå‚考官方文档
    # https://cloud.tencent.com/document/product/436/6224
    region: ap-beijing
ruoyi-admin/src/main/resources/application.yml
@@ -8,8 +8,6 @@
  copyrightYear: 2021
  # å®žä¾‹æ¼”示开关
  demoEnabled: true
  # æ–‡ä»¶è·¯å¾„
  profile: ./ruoyi/uploadPath
  # èŽ·å–ip地址开关
  addressEnabled: true
@@ -107,6 +105,11 @@
  # ä»¤ç‰Œæœ‰æ•ˆæœŸï¼ˆé»˜è®¤30分钟)
  expireTime: 30
# é‡å¤æäº¤
repeat-submit:
  # å…¨å±€é—´é𔿗¶é—´(毫秒)
  intervalTime: 1000
# MyBatisPlus配置
# https://baomidou.com/config/
mybatis-plus:
@@ -159,8 +162,10 @@
    localCacheScope: SESSION
    # å¼€å¯Mybatis二级缓存,默认为 true
    cacheEnabled: false
    # æ›´è¯¦ç»†çš„æ—¥å¿—输出 ä¼šæœ‰æ€§èƒ½æŸè€—
    # logImpl: org.apache.ibatis.logging.stdout.StdOutImpl
    # æ›´è¯¦ç»†çš„æ—¥å¿—输出 ä¼šæœ‰æ€§èƒ½æŸè€— org.apache.ibatis.logging.stdout.StdOutImpl
    # å…³é—­æ—¥å¿—记录 (可单纯使用 p6spy åˆ†æž) org.apache.ibatis.logging.nologging.NoLoggingImpl
    # é»˜è®¤æ—¥å¿—输出 org.apache.ibatis.logging.slf4j.Slf4jImpl
    logImpl: org.apache.ibatis.logging.nologging.NoLoggingImpl
  global-config:
    # æ˜¯å¦æ‰“印 Logo banner
    banner: true
@@ -223,7 +228,7 @@
  # è¿‡æ»¤å¼€å…³
  enabled: true
  # æŽ’除链接(多个用逗号分隔)
  excludes: /system/notice/*
  excludes: /system/notice
  # åŒ¹é…é“¾æŽ¥
  urlPatterns: /system/*,/monitor/*,/tool/*
@@ -284,6 +289,20 @@
  # åˆ†å¸ƒå¼é”çš„超时时间,默认为 30 æ¯«ç§’
  expire: 30000
--- # Actuator ç›‘控端点的配置项
management:
  endpoints:
    web:
      # Actuator æä¾›çš„ API æŽ¥å£çš„æ ¹ç›®å½•。默认为 /actuator
      base-path: /actuator
      exposure:
        # éœ€è¦å¼€æ”¾çš„端点。默认值只打开 health å’Œ info ä¸¤ä¸ªç«¯ç‚¹ã€‚通过设置 * ï¼Œå¯ä»¥å¼€æ”¾æ‰€æœ‰ç«¯ç‚¹ã€‚
        # ç”Ÿäº§çŽ¯å¢ƒä¸å»ºè®®æ”¾å¼€æ‰€æœ‰ æ ¹æ®é¡¹ç›®éœ€æ±‚放开即可
        include: @endpoints.include@
  endpoint:
    logfile:
      external-file: ./logs/sys-console.log
--- # å®šæ—¶ä»»åŠ¡é…ç½®
spring:
  quartz:
ruoyi-admin/src/main/resources/i18n/messages.properties
@@ -15,10 +15,11 @@
user.username.not.valid=* 2到20个汉字、字母、数字或下划线组成,且必须以非数字开头
user.password.not.valid=* 5-50个字符
user.email.not.valid=邮箱格式错误
user.mobile.phone.number.not.valid=手机号格式错误
user.login.success=登录成功
user.register.success=注册成功
user.notfound=请重新登录
user.forcelogout=管理员强制退出,请重新登录
user.unknown.error=未知错误,请重新登录
ruoyi-admin/src/main/resources/i18n/messages_en_US.properties
@@ -16,8 +16,8 @@
user.email.not.valid=
user.mobile.phone.number.not.valid=
user.login.success=
user.register.success=register success
user.notfound=
user.forcelogout=
user.unknown.error=
##文件上传消息
ruoyi-admin/src/main/resources/i18n/messages_zh_CN.properties
@@ -15,10 +15,11 @@
user.username.not.valid=* 2到20个汉字、字母、数字或下划线组成,且必须以非数字开头
user.password.not.valid=* 5-50个字符
user.email.not.valid=邮箱格式错误
user.mobile.phone.number.not.valid=手机号格式错误
user.login.success=登录成功
user.register.success=注册成功
user.notfound=请重新登录
user.forcelogout=管理员强制退出,请重新登录
user.unknown.error=未知错误,请重新登录
ruoyi-admin/src/main/resources/spy.properties
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,26 @@
# p6spy æ€§èƒ½åˆ†æžæ’件配置文件
modulelist=com.baomidou.mybatisplus.extension.p6spy.MybatisPlusLogFactory,com.p6spy.engine.outage.P6OutageFactory
# è‡ªå®šä¹‰æ—¥å¿—打印
logMessageFormat=com.baomidou.mybatisplus.extension.p6spy.P6SpyLogger
#日志输出到控制台
appender=com.baomidou.mybatisplus.extension.p6spy.StdoutLogger
# ä½¿ç”¨æ—¥å¿—系统记录 sql
#appender=com.p6spy.engine.spy.appender.Slf4JLogger
# è®¾ç½® p6spy driver ä»£ç†
#deregisterdrivers=true
# å–消JDBC URL前缀
useprefix=true
# é…ç½®è®°å½• Log ä¾‹å¤–,可去掉的结果集有error,info,batch,debug,statement,commit,rollback,result,resultset.
excludecategories=info,debug,result,commit,resultset
# æ—¥æœŸæ ¼å¼
dateformat=yyyy-MM-dd HH:mm:ss
# å®žé™…驱动可多个
#driverlist=org.h2.Driver
# æ˜¯å¦å¼€å¯æ…¢SQL记录
outagedetection=true
# æ…¢SQL记录标准 2 ç§’
outagedetectioninterval=2
# æ˜¯å¦è¿‡æ»¤ Log
filter=true
# è¿‡æ»¤ Log æ—¶æ‰€æŽ’除的表名列表,以逗号分隔
exclude=QRTZ_
ruoyi-common/pom.xml
@@ -5,7 +5,7 @@
    <parent>
        <artifactId>ruoyi-vue-plus</artifactId>
        <groupId>com.ruoyi</groupId>
        <version>2.6.0</version>
        <version>3.0.0</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>
@@ -57,6 +57,11 @@
        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi-ooxml</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>easyexcel</artifactId>
        </dependency>
        <!-- yml解析器 -->
@@ -147,6 +152,11 @@
            <groupId>com.baomidou</groupId>
            <artifactId>dynamic-datasource-spring-boot-starter</artifactId>
        </dependency>
        <!-- sql性能分析插件 -->
        <dependency>
            <groupId>p6spy</groupId>
            <artifactId>p6spy</artifactId>
        </dependency>
        <dependency>
            <groupId>com.baomidou</groupId>
ruoyi-common/src/main/java/com/ruoyi/common/annotation/Excel.java
ÎļþÒÑɾ³ý
ruoyi-common/src/main/java/com/ruoyi/common/annotation/ExcelDictFormat.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,30 @@
package com.ruoyi.common.annotation;
import java.lang.annotation.*;
/**
 * å­—典格式化
 *
 * @author Lion Li
 */
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface ExcelDictFormat {
    /**
     * å¦‚果是字典类型,请设置字典的type值 (如: sys_user_sex)
     */
    String dictType() default "";
    /**
     * è¯»å–内容转表达式 (如: 0=男,1=女,2=未知)
     */
    String readConverterExp() default "";
    /**
     * åˆ†éš”符,读取字符串组内容
     */
    String separator() default ",";
}
ruoyi-common/src/main/java/com/ruoyi/common/annotation/Excels.java
ÎļþÒÑɾ³ý
ruoyi-common/src/main/java/com/ruoyi/common/annotation/RateLimiter.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,40 @@
package com.ruoyi.common.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.enums.LimitType;
/**
 * é™æµæ³¨è§£
 *
 * @author ruoyi
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RateLimiter
{
    /**
     * é™æµkey
     */
    public String key() default Constants.RATE_LIMIT_KEY;
    /**
     * é™æµæ—¶é—´,单位秒
     */
    public int time() default 60;
    /**
     * é™æµæ¬¡æ•°
     */
    public int count() default 100;
    /**
     * é™æµç±»åž‹
     */
    public LimitType limitType() default LimitType.DEFAULT;
}
ruoyi-common/src/main/java/com/ruoyi/common/annotation/RepeatSubmit.java
@@ -1,23 +1,29 @@
package com.ruoyi.common.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
 * è‡ªå®šä¹‰æ³¨è§£é˜²æ­¢è¡¨å•重复提交
 *
 * @author ruoyi
 *
 */
@Inherited
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RepeatSubmit
{
}
package com.ruoyi.common.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.concurrent.TimeUnit;
/**
 * è‡ªå®šä¹‰æ³¨è§£é˜²æ­¢è¡¨å•重复提交
 *
 * @author Lion Li
 */
@Inherited
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RepeatSubmit {
    /**
     * é»˜è®¤ä½¿ç”¨å…¨å±€é…ç½®
     */
    int intervalTime() default 0;
    TimeUnit timeUnit() default TimeUnit.MILLISECONDS;
}
ruoyi-common/src/main/java/com/ruoyi/common/config/RuoYiConfig.java
@@ -9,7 +9,7 @@
/**
 * è¯»å–项目相关配置
 *
 *
 * @author ruoyi
 */
@@ -32,45 +32,13 @@
    /** å®žä¾‹æ¼”示开关 */
    private boolean demoEnabled;
    /** ä¸Šä¼ è·¯å¾„ */
    @Getter
    private static String profile;
    /** èŽ·å–åœ°å€å¼€å…³ */
    @Getter
    private static boolean addressEnabled;
    public void setProfile(String profile)
    {
        RuoYiConfig.profile = profile;
    }
    public void setAddressEnabled(boolean addressEnabled)
    {
        RuoYiConfig.addressEnabled = addressEnabled;
    }
    /**
     * èŽ·å–å¤´åƒä¸Šä¼ è·¯å¾„
     */
    public static String getAvatarPath()
    {
        return getProfile() + "/avatar";
    }
    /**
     * èŽ·å–ä¸‹è½½è·¯å¾„
     */
    public static String getDownloadPath()
    {
        return getProfile() + "/download/";
    }
    /**
     * èŽ·å–ä¸Šä¼ è·¯å¾„
     */
    public static String getUploadPath()
    {
        return getProfile() + "/upload";
    }
}
ruoyi-common/src/main/java/com/ruoyi/common/constant/Constants.java
@@ -1,5 +1,7 @@
package com.ruoyi.common.constant;
import io.jsonwebtoken.Claims;
/**
 * é€šç”¨å¸¸é‡ä¿¡æ¯
 *
@@ -48,6 +50,11 @@
    public static final String LOGOUT = "Logout";
    /**
     * æ³¨å†Œ
     */
    public static final String REGISTER = "Register";
    /**
     * ç™»å½•失败
     */
    public static final String LOGIN_FAIL = "Error";
@@ -66,6 +73,11 @@
     * é˜²é‡æäº¤ redis key
     */
    public static final String REPEAT_SUBMIT_KEY = "repeat_submit:";
    /**
     * é™æµ redis key
     */
    public static final String RATE_LIMIT_KEY = "rate_limit:";
    /**
     * éªŒè¯ç æœ‰æ•ˆæœŸï¼ˆåˆ†é’Ÿï¼‰
@@ -95,7 +107,7 @@
    /**
     * ç”¨æˆ·åç§°
     */
    public static final String JWT_USERNAME = "sub";
    public static final String JWT_USERNAME = Claims.SUBJECT;
    /**
     * ç”¨æˆ·å¤´åƒ
@@ -122,18 +134,9 @@
     */
    public static final String SYS_DICT_KEY = "sys_dict:";
    /**
     * èµ„源映射路径 å‰ç¼€
     */
    public static final String RESOURCE_PREFIX = "/profile";
    /**
     * RMI è¿œç¨‹æ–¹æ³•调用
     */
    public static final String LOOKUP_RMI = "rmi://";
    /**
     * èµ„源映射路径 å‰ç¼€
     */
    public static final String REDIS_LOCK_KEY = "redis_lock:";
}
ruoyi-common/src/main/java/com/ruoyi/common/constant/UserConstants.java
@@ -63,4 +63,16 @@
    /** æ ¡éªŒè¿”回结果码 */
    public final static String UNIQUE = "0";
    public final static String NOT_UNIQUE = "1";
    /**
     * ç”¨æˆ·åé•¿åº¦é™åˆ¶
     */
    public static final int USERNAME_MIN_LENGTH = 2;
    public static final int USERNAME_MAX_LENGTH = 20;
    /**
     * å¯†ç é•¿åº¦é™åˆ¶
     */
    public static final int PASSWORD_MIN_LENGTH = 5;
    public static final int PASSWORD_MAX_LENGTH = 20;
}
ruoyi-common/src/main/java/com/ruoyi/common/convert/ExcelDictConvert.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,69 @@
package com.ruoyi.common.convert;
import cn.hutool.core.annotation.AnnotationUtil;
import cn.hutool.core.convert.Convert;
import com.alibaba.excel.converters.Converter;
import com.alibaba.excel.enums.CellDataTypeEnum;
import com.alibaba.excel.metadata.CellData;
import com.alibaba.excel.metadata.GlobalConfiguration;
import com.alibaba.excel.metadata.property.ExcelContentProperty;
import com.ruoyi.common.annotation.ExcelDictFormat;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.poi.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(CellData 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 = ExcelUtil.reverseDictByExp(label, type, anno.separator());
        }
        return Convert.convert(contentProperty.getField().getType(), value);
    }
    @Override
    public CellData<String> convertToExcelData(Object object, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) {
        if (StringUtils.isNull(object)) {
            return new CellData<>("");
        }
        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 = ExcelUtil.convertDictByExp(value, type, anno.separator());
        }
        return new CellData<>(label);
    }
    private ExcelDictFormat getAnnotation(Field field) {
        return AnnotationUtil.getAnnotation(field, ExcelDictFormat.class);
    }
}
ruoyi-common/src/main/java/com/ruoyi/common/core/controller/BaseController.java
@@ -1,7 +1,9 @@
package com.ruoyi.common.core.controller;
import cn.hutool.core.util.StrUtil;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.domain.model.LoginUser;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -73,6 +75,38 @@
     */
    public String redirect(String url)
    {
        return StrUtil.format("redirect:{}", url);
        return StringUtils.format("redirect:{}", url);
    }
    /**
     * èŽ·å–ç”¨æˆ·ç¼“å­˜ä¿¡æ¯
     */
    public LoginUser getLoginUser()
    {
        return SecurityUtils.getLoginUser();
    }
    /**
     * èŽ·å–ç™»å½•ç”¨æˆ·id
     */
    public Long getUserId()
    {
        return getLoginUser().getUserId();
    }
    /**
     * èŽ·å–ç™»å½•éƒ¨é—¨id
     */
    public Long getDeptId()
    {
        return getLoginUser().getDeptId();
    }
    /**
     * èŽ·å–ç™»å½•ç”¨æˆ·å
     */
    public String getUsername()
    {
        return getLoginUser().getUsername();
    }
}
ruoyi-common/src/main/java/com/ruoyi/common/core/domain/BaseEntity.java
@@ -1,5 +1,6 @@
package com.ruoyi.common.core.domain;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
@@ -18,29 +19,50 @@
@Data
@NoArgsConstructor
@Accessors(chain = true)
public class BaseEntity implements Serializable
{
    private static final long serialVersionUID = 1L;
public class BaseEntity implements Serializable {
    /** æœç´¢å€¼ */
    private String searchValue;
    private static final long serialVersionUID = 1L;
    /** åˆ›å»ºè€… */
    private String createBy;
    /**
     * æœç´¢å€¼
     */
    @ApiModelProperty(value = "搜索值")
    private String searchValue;
    /** åˆ›å»ºæ—¶é—´ */
    private Date createTime;
    /**
     * åˆ›å»ºè€…
     */
    @ApiModelProperty(value = "创建者")
    private String createBy;
    /** æ›´æ–°è€… */
    private String updateBy;
    /**
     * åˆ›å»ºæ—¶é—´
     */
    @ApiModelProperty(value = "创建时间")
    private Date createTime;
    /** æ›´æ–°æ—¶é—´ */
    private Date updateTime;
    /**
     * æ›´æ–°è€…
     */
    @ApiModelProperty(value = "更新者")
    private String updateBy;
    /** å¤‡æ³¨ */
    private String remark;
    /**
     * æ›´æ–°æ—¶é—´
     */
    @ApiModelProperty(value = "更新时间")
    private Date updateTime;
    /** è¯·æ±‚参数 */
    private Map<String, Object> params = new HashMap<>();
    /**
     * å¤‡æ³¨
     */
    @ApiModelProperty(value = "备注")
    private String remark;
    /**
     * è¯·æ±‚参数
     */
    @ApiModelProperty(value = "请求参数")
    private Map<String, Object> params = new HashMap<>();
}
ruoyi-common/src/main/java/com/ruoyi/common/core/domain/TreeEntity.java
@@ -1,38 +1,56 @@
package com.ruoyi.common.core.domain;
import lombok.*;
import lombok.experimental.Accessors;
import java.util.ArrayList;
import java.util.List;
/**
 * Tree基类
 *
 * @author ruoyi
 */
@EqualsAndHashCode(callSuper = true)
@Data
@NoArgsConstructor
@Accessors(chain = true)
public class TreeEntity extends BaseEntity
{
    private static final long serialVersionUID = 1L;
    /** çˆ¶èœå•名称 */
    private String parentName;
    /** çˆ¶èœå•ID */
    private Long parentId;
    /** æ˜¾ç¤ºé¡ºåº */
    private Integer orderNum;
    /** ç¥–级列表 */
    private String ancestors;
    /** å­éƒ¨é—¨ */
    private List<?> children = new ArrayList<>();
}
package com.ruoyi.common.core.domain;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
import java.util.ArrayList;
import java.util.List;
/**
 * Tree基类
 *
 * @author ruoyi
 */
@EqualsAndHashCode(callSuper = true)
@Data
@NoArgsConstructor
@Accessors(chain = true)
public class TreeEntity extends BaseEntity {
    private static final long serialVersionUID = 1L;
    /**
     * çˆ¶èœå•名称
     */
    @ApiModelProperty(value = "父菜单名称")
    private String parentName;
    /**
     * çˆ¶èœå•ID
     */
    @ApiModelProperty(value = "父菜单ID")
    private Long parentId;
    /**
     * æ˜¾ç¤ºé¡ºåº
     */
    @ApiModelProperty(value = "显示顺序")
    private Integer orderNum;
    /**
     * ç¥–级列表
     */
    @ApiModelProperty(value = "祖级列表")
    private String ancestors;
    /**
     * å­éƒ¨é—¨
     */
    @ApiModelProperty(value = "子部门")
    private List<?> children = new ArrayList<>();
}
ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysDept.java
@@ -1,7 +1,6 @@
package com.ruoyi.common.core.domain.entity;
import com.baomidou.mybatisplus.annotation.*;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
@@ -14,7 +13,7 @@
/**
 * éƒ¨é—¨è¡¨ sys_dept
 *
 *
 * @author ruoyi
 */
@@ -22,78 +21,107 @@
@NoArgsConstructor
@Accessors(chain = true)
@TableName("sys_dept")
public class SysDept implements Serializable
{
    private static final long serialVersionUID = 1L;
public class SysDept implements Serializable {
    private static final long serialVersionUID = 1L;
    /** éƒ¨é—¨ID */
    @TableId(value = "dept_id",type = IdType.AUTO)
    private Long deptId;
    /**
     * éƒ¨é—¨ID
     */
    @TableId(value = "dept_id", type = IdType.AUTO)
    private Long deptId;
    /** çˆ¶éƒ¨é—¨ID */
    private Long parentId;
    /**
     * çˆ¶éƒ¨é—¨ID
     */
    private Long parentId;
    /** ç¥–级列表 */
    private String ancestors;
    /**
     * ç¥–级列表
     */
    private String ancestors;
    /** éƒ¨é—¨åç§° */
    @NotBlank(message = "部门名称不能为空")
    @Size(min = 0, max = 30, message = "部门名称长度不能超过30个字符")
    private String deptName;
    /**
     * éƒ¨é—¨åç§°
     */
    @NotBlank(message = "部门名称不能为空")
    @Size(min = 0, max = 30, message = "部门名称长度不能超过30个字符")
    private String deptName;
    /** æ˜¾ç¤ºé¡ºåº */
    @NotBlank(message = "显示顺序不能为空")
    private String orderNum;
    /**
     * æ˜¾ç¤ºé¡ºåº
     */
    @NotBlank(message = "显示顺序不能为空")
    private String orderNum;
    /** è´Ÿè´£äºº */
    private String leader;
    /**
     * è´Ÿè´£äºº
     */
    private String leader;
    /** è”系电话 */
    @Size(min = 0, max = 11, message = "联系电话长度不能超过11个字符")
    private String phone;
    /**
     * è”系电话
     */
    @Size(min = 0, max = 11, message = "联系电话长度不能超过11个字符")
    private String phone;
    /** é‚®ç®± */
    @Email(message = "邮箱格式不正确")
    @Size(min = 0, max = 50, message = "邮箱长度不能超过50个字符")
    private String email;
    /**
     * é‚®ç®±
     */
    @Email(message = "邮箱格式不正确")
    @Size(min = 0, max = 50, message = "邮箱长度不能超过50个字符")
    private String email;
    /** éƒ¨é—¨çŠ¶æ€:0正常,1停用 */
    private String status;
    /**
     * éƒ¨é—¨çŠ¶æ€:0正常,1停用
     */
    private String status;
    /** åˆ é™¤æ ‡å¿—(0代表存在 2代表删除) */
    @TableLogic
    private String delFlag;
    /**
     * åˆ é™¤æ ‡å¿—(0代表存在 2代表删除)
     */
    @TableLogic
    private String delFlag;
    /** çˆ¶éƒ¨é—¨åç§° */
    @TableField(exist = false)
    private String parentName;
    /**
     * çˆ¶éƒ¨é—¨åç§°
     */
    @TableField(exist = false)
    private String parentName;
    /** åˆ›å»ºè€… */
    @TableField(fill = FieldFill.INSERT)
    private String createBy;
    /**
     * åˆ›å»ºè€…
     */
    @TableField(fill = FieldFill.INSERT)
    private String createBy;
    /** åˆ›å»ºæ—¶é—´ */
    @TableField(fill = FieldFill.INSERT)
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private Date createTime;
    /**
     * åˆ›å»ºæ—¶é—´
     */
    @TableField(fill = FieldFill.INSERT)
    private Date createTime;
    /** æ›´æ–°è€… */
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private String updateBy;
    /**
     * æ›´æ–°è€…
     */
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private String updateBy;
    /** æ›´æ–°æ—¶é—´ */
    @TableField(fill = FieldFill.INSERT_UPDATE)
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private Date updateTime;
    /** å­éƒ¨é—¨ */
    @TableField(exist = false)
    private List<SysDept> children = new ArrayList<SysDept>();
    /**
     * æ›´æ–°æ—¶é—´
     */
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private Date updateTime;
    /**
     * è¯·æ±‚参数
     */
    @TableField(exist = false)
    private Map<String, Object> params = new HashMap<>();
    /**
     * å­éƒ¨é—¨
     */
    @TableField(exist = false)
    private List<SysDept> children = new ArrayList<SysDept>();
    /**
     * è¯·æ±‚参数
     */
    @TableField(exist = false)
    private Map<String, Object> params = new HashMap<>();
}
ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysDictData.java
@@ -1,10 +1,11 @@
package com.ruoyi.common.core.domain.entity;
import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
import com.alibaba.excel.annotation.ExcelProperty;
import com.baomidou.mybatisplus.annotation.*;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.ruoyi.common.annotation.Excel;
import com.ruoyi.common.annotation.Excel.ColumnType;
import com.ruoyi.common.annotation.ExcelDictFormat;
import com.ruoyi.common.constant.UserConstants;
import com.ruoyi.common.convert.ExcelDictConvert;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
@@ -18,7 +19,7 @@
/**
 * å­—典数据表 sys_dict_data
 *
 *
 * @author ruoyi
 */
@@ -26,82 +27,109 @@
@NoArgsConstructor
@Accessors(chain = true)
@TableName("sys_dict_data")
public class SysDictData implements Serializable
{
    private static final long serialVersionUID = 1L;
@ExcelIgnoreUnannotated
public class SysDictData implements Serializable {
    private static final long serialVersionUID = 1L;
    /** å­—典编码 */
    @Excel(name = "字典编码", cellType = ColumnType.NUMERIC)
    @TableId(value = "dict_code",type = IdType.AUTO)
    private Long dictCode;
    /**
     * å­—典编码
     */
    @ExcelProperty(value = "字典编码")
    @TableId(value = "dict_code", type = IdType.AUTO)
    private Long dictCode;
    /** å­—典排序 */
    @Excel(name = "字典排序", cellType = ColumnType.NUMERIC)
    private Long dictSort;
    /**
     * å­—典排序
     */
    @ExcelProperty(value = "字典排序")
    private Long dictSort;
    /** å­—典标签 */
    @Excel(name = "字典标签")
    @NotBlank(message = "字典标签不能为空")
    @Size(min = 0, max = 100, message = "字典标签长度不能超过100个字符")
    private String dictLabel;
    /**
     * å­—典标签
     */
    @ExcelProperty(value = "字典标签")
    @NotBlank(message = "字典标签不能为空")
    @Size(min = 0, max = 100, message = "字典标签长度不能超过100个字符")
    private String dictLabel;
    /** å­—典键值 */
    @Excel(name = "字典键值")
    @NotBlank(message = "字典键值不能为空")
    @Size(min = 0, max = 100, message = "字典键值长度不能超过100个字符")
    private String dictValue;
    /**
     * å­—典键值
     */
    @ExcelProperty(value = "字典键值")
    @NotBlank(message = "字典键值不能为空")
    @Size(min = 0, max = 100, message = "字典键值长度不能超过100个字符")
    private String dictValue;
    /** å­—典类型 */
    @Excel(name = "字典类型")
    @NotBlank(message = "字典类型不能为空")
    @Size(min = 0, max = 100, message = "字典类型长度不能超过100个字符")
    private String dictType;
    /**
     * å­—典类型
     */
    @ExcelProperty(value = "字典类型")
    @NotBlank(message = "字典类型不能为空")
    @Size(min = 0, max = 100, message = "字典类型长度不能超过100个字符")
    private String dictType;
    /** æ ·å¼å±žæ€§ï¼ˆå…¶ä»–样式扩展) */
    @Size(min = 0, max = 100, message = "样式属性长度不能超过100个字符")
    private String cssClass;
    /**
     * æ ·å¼å±žæ€§ï¼ˆå…¶ä»–样式扩展)
     */
    @Size(min = 0, max = 100, message = "样式属性长度不能超过100个字符")
    private String cssClass;
    /** è¡¨æ ¼å­—典样式 */
    private String listClass;
    /**
     * è¡¨æ ¼å­—典样式
     */
    private String listClass;
    /** æ˜¯å¦é»˜è®¤ï¼ˆY是 N否) */
    @Excel(name = "是否默认", readConverterExp = "Y=是,N=否")
    private String isDefault;
    /**
     * æ˜¯å¦é»˜è®¤ï¼ˆY是 N否)
     */
    @ExcelProperty(value = "是否默认", converter = ExcelDictConvert.class)
    @ExcelDictFormat(dictType = "sys_yes_no")
    private String isDefault;
    /** çŠ¶æ€ï¼ˆ0正常 1停用) */
    @Excel(name = "状态", readConverterExp = "0=正常,1=停用")
    private String status;
    /**
     * çŠ¶æ€ï¼ˆ0正常 1停用)
     */
    @ExcelProperty(value = "状态", converter = ExcelDictConvert.class)
    @ExcelDictFormat(dictType = "sys_common_status")
    private String status;
    /** åˆ›å»ºè€… */
    @TableField(fill = FieldFill.INSERT)
    private String createBy;
    /**
     * åˆ›å»ºè€…
     */
    @TableField(fill = FieldFill.INSERT)
    private String createBy;
    /** åˆ›å»ºæ—¶é—´ */
    @TableField(fill = FieldFill.INSERT)
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private Date createTime;
    /**
     * åˆ›å»ºæ—¶é—´
     */
    @TableField(fill = FieldFill.INSERT)
    private Date createTime;
    /** æ›´æ–°è€… */
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private String updateBy;
    /**
     * æ›´æ–°è€…
     */
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private String updateBy;
    /** æ›´æ–°æ—¶é—´ */
    @TableField(fill = FieldFill.INSERT_UPDATE)
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private Date updateTime;
    /**
     * æ›´æ–°æ—¶é—´
     */
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private Date updateTime;
    /** å¤‡æ³¨ */
    private String remark;
    /**
     * å¤‡æ³¨
     */
    private String remark;
    /**
     * è¯·æ±‚参数
     */
    @TableField(exist = false)
    private Map<String, Object> params = new HashMap<>();
    /**
     * è¯·æ±‚参数
     */
    @TableField(exist = false)
    private Map<String, Object> params = new HashMap<>();
    public boolean getDefault()
    {
        return UserConstants.YES.equals(this.isDefault) ? true : false;
    }
    public boolean getDefault() {
        return UserConstants.YES.equals(this.isDefault) ? true : false;
    }
}
ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysDictType.java
@@ -1,9 +1,10 @@
package com.ruoyi.common.core.domain.entity;
import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
import com.alibaba.excel.annotation.ExcelProperty;
import com.baomidou.mybatisplus.annotation.*;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.ruoyi.common.annotation.Excel;
import com.ruoyi.common.annotation.Excel.ColumnType;
import com.ruoyi.common.annotation.ExcelDictFormat;
import com.ruoyi.common.convert.ExcelDictConvert;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
@@ -17,7 +18,7 @@
/**
 * å­—典类型表 sys_dict_type
 *
 *
 * @author ruoyi
 */
@@ -25,56 +26,73 @@
@NoArgsConstructor
@Accessors(chain = true)
@TableName("sys_dict_type")
public class SysDictType implements Serializable
{
    private static final long serialVersionUID = 1L;
@ExcelIgnoreUnannotated
public class SysDictType implements Serializable {
    private static final long serialVersionUID = 1L;
    /** å­—典主键 */
    @Excel(name = "字典主键", cellType = ColumnType.NUMERIC)
    @TableId(value = "dict_id",type = IdType.AUTO)
    private Long dictId;
    /**
     * å­—典主键
     */
    @ExcelProperty(value = "字典主键")
    @TableId(value = "dict_id", type = IdType.AUTO)
    private Long dictId;
    /** å­—典名称 */
    @Excel(name = "字典名称")
    @NotBlank(message = "字典名称不能为空")
    @Size(min = 0, max = 100, message = "字典类型名称长度不能超过100个字符")
    private String dictName;
    /**
     * å­—典名称
     */
    @ExcelProperty(value = "字典名称")
    @NotBlank(message = "字典名称不能为空")
    @Size(min = 0, max = 100, message = "字典类型名称长度不能超过100个字符")
    private String dictName;
    /** å­—典类型 */
    @Excel(name = "字典类型")
    @NotBlank(message = "字典类型不能为空")
    @Size(min = 0, max = 100, message = "字典类型类型长度不能超过100个字符")
    private String dictType;
    /**
     * å­—典类型
     */
    @ExcelProperty(value = "字典类型")
    @NotBlank(message = "字典类型不能为空")
    @Size(min = 0, max = 100, message = "字典类型类型长度不能超过100个字符")
    private String dictType;
    /** çŠ¶æ€ï¼ˆ0正常 1停用) */
    @Excel(name = "状态", readConverterExp = "0=正常,1=停用")
    private String status;
    /**
     * çŠ¶æ€ï¼ˆ0正常 1停用)
     */
    @ExcelProperty(value = "状态", converter = ExcelDictConvert.class)
    @ExcelDictFormat(dictType = "sys_common_status")
    private String status;
    /** åˆ›å»ºè€… */
    @TableField(fill = FieldFill.INSERT)
    private String createBy;
    /**
     * åˆ›å»ºè€…
     */
    @TableField(fill = FieldFill.INSERT)
    private String createBy;
    /** åˆ›å»ºæ—¶é—´ */
    @TableField(fill = FieldFill.INSERT)
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private Date createTime;
    /**
     * åˆ›å»ºæ—¶é—´
     */
    @TableField(fill = FieldFill.INSERT)
    private Date createTime;
    /** æ›´æ–°è€… */
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private String updateBy;
    /**
     * æ›´æ–°è€…
     */
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private String updateBy;
    /** æ›´æ–°æ—¶é—´ */
    @TableField(fill = FieldFill.INSERT_UPDATE)
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private Date updateTime;
    /**
     * æ›´æ–°æ—¶é—´
     */
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private Date updateTime;
    /** å¤‡æ³¨ */
    private String remark;
    /**
     * å¤‡æ³¨
     */
    private String remark;
    /**
     * è¯·æ±‚参数
     */
    @TableField(exist = false)
    private Map<String, Object> params = new HashMap<>();
    /**
     * è¯·æ±‚参数
     */
    @TableField(exist = false)
    private Map<String, Object> params = new HashMap<>();
}
ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysMenu.java
@@ -13,7 +13,7 @@
/**
 * èœå•权限表 sys_menu
 *
 *
 * @author ruoyi
 */
@@ -21,88 +21,129 @@
@NoArgsConstructor
@Accessors(chain = true)
@TableName("sys_menu")
public class SysMenu implements Serializable
{
    private static final long serialVersionUID = 1L;
public class SysMenu implements Serializable {
    private static final long serialVersionUID = 1L;
    /** èœå•ID */
    @TableId(value = "menu_id",type = IdType.AUTO)
    private Long menuId;
    /**
     * èœå•ID
     */
    @TableId(value = "menu_id", type = IdType.AUTO)
    private Long menuId;
    /** èœå•名称 */
    @NotBlank(message = "菜单名称不能为空")
    @Size(min = 0, max = 50, message = "菜单名称长度不能超过50个字符")
    private String menuName;
    /**
     * èœå•名称
     */
    @NotBlank(message = "菜单名称不能为空")
    @Size(min = 0, max = 50, message = "菜单名称长度不能超过50个字符")
    private String menuName;
    /** çˆ¶èœå•名称 */
    @TableField(exist = false)
    private String parentName;
    /**
     * çˆ¶èœå•名称
     */
    @TableField(exist = false)
    private String parentName;
    /** çˆ¶èœå•ID */
    private Long parentId;
    /**
     * çˆ¶èœå•ID
     */
    private Long parentId;
    /** æ˜¾ç¤ºé¡ºåº */
    @NotBlank(message = "显示顺序不能为空")
    private String orderNum;
    /**
     * æ˜¾ç¤ºé¡ºåº
     */
    @NotBlank(message = "显示顺序不能为空")
    private String orderNum;
    /** è·¯ç”±åœ°å€ */
    @Size(min = 0, max = 200, message = "路由地址不能超过200个字符")
    private String path;
    /**
     * è·¯ç”±åœ°å€
     */
    @Size(min = 0, max = 200, message = "路由地址不能超过200个字符")
    private String path;
    /** ç»„件路径 */
    @Size(min = 0, max = 200, message = "组件路径不能超过255个字符")
    private String component;
    /**
     * ç»„件路径
     */
    @Size(min = 0, max = 200, message = "组件路径不能超过255个字符")
    private String component;
    /** æ˜¯å¦ä¸ºå¤–链(0是 1否) */
    private String isFrame;
    /**
     * æ˜¯å¦ä¸ºå¤–链(0是 1否)
     */
    private String isFrame;
    /** æ˜¯å¦ç¼“存(0缓存 1不缓存) */
    private String isCache;
    /**
     * æ˜¯å¦ç¼“存(0缓存 1不缓存)
     */
    private String isCache;
    /** ç±»åž‹ï¼ˆM目录 C菜单 F按钮) */
    @NotBlank(message = "菜单类型不能为空")
    private String menuType;
    /**
     * ç±»åž‹ï¼ˆM目录 C菜单 F按钮)
     */
    @NotBlank(message = "菜单类型不能为空")
    private String menuType;
    /** æ˜¾ç¤ºçŠ¶æ€ï¼ˆ0显示 1隐藏) */
    private String visible;
    /** èœå•状态(0显示 1隐藏) */
    private String status;
    /**
     * æ˜¾ç¤ºçŠ¶æ€ï¼ˆ0显示 1隐藏)
     */
    private String visible;
    /** æƒé™å­—符串 */
    @Size(min = 0, max = 100, message = "权限标识长度不能超过100个字符")
    private String perms;
    /**
     * èœå•状态(0显示 1隐藏)
     */
    private String status;
    /** èœå•图标 */
    private String icon;
    /**
     * æƒé™å­—符串
     */
    @Size(min = 0, max = 100, message = "权限标识长度不能超过100个字符")
    private String perms;
    /** åˆ›å»ºè€… */
    @TableField(fill = FieldFill.INSERT)
    private String createBy;
    /**
     * èœå•图标
     */
    private String icon;
    /** åˆ›å»ºæ—¶é—´ */
    @TableField(fill = FieldFill.INSERT)
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private Date createTime;
    /**
     * åˆ›å»ºè€…
     */
    @TableField(fill = FieldFill.INSERT)
    private String createBy;
    /** æ›´æ–°è€… */
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private String updateBy;
    /**
     * åˆ›å»ºæ—¶é—´
     */
    @TableField(fill = FieldFill.INSERT)
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private Date createTime;
    /** æ›´æ–°æ—¶é—´ */
    @TableField(fill = FieldFill.INSERT_UPDATE)
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private Date updateTime;
    /**
     * æ›´æ–°è€…
     */
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private String updateBy;
    /** å¤‡æ³¨ */
    private String remark;
    /**
     * æ›´æ–°æ—¶é—´
     */
    @TableField(fill = FieldFill.INSERT_UPDATE)
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private Date updateTime;
    /** è¯·æ±‚参数 */
    @TableField(exist = false)
    private Map<String, Object> params = new HashMap<>();
    /**
     * å¤‡æ³¨
     */
    private String remark;
    /** å­èœå• */
    @TableField(exist = false)
    private List<SysMenu> children = new ArrayList<SysMenu>();
    /**
     * è¯·æ±‚参数
     */
    @TableField(exist = false)
    private Map<String, Object> params = new HashMap<>();
    /**
     * å­èœå•
     */
    @TableField(exist = false)
    private List<SysMenu> children = new ArrayList<SysMenu>();
}
ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysRole.java
@@ -1,9 +1,10 @@
package com.ruoyi.common.core.domain.entity;
import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
import com.alibaba.excel.annotation.ExcelProperty;
import com.baomidou.mybatisplus.annotation.*;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.ruoyi.common.annotation.Excel;
import com.ruoyi.common.annotation.Excel.ColumnType;
import com.ruoyi.common.annotation.ExcelDictFormat;
import com.ruoyi.common.convert.ExcelDictConvert;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
@@ -25,102 +26,133 @@
@NoArgsConstructor
@Accessors(chain = true)
@TableName("sys_role")
public class SysRole implements Serializable
{
    private static final long serialVersionUID = 1L;
@ExcelIgnoreUnannotated
public class SysRole implements Serializable {
    private static final long serialVersionUID = 1L;
    /** è§’色ID */
    @Excel(name = "角色序号", cellType = ColumnType.NUMERIC)
    @TableId(value = "role_id",type = IdType.AUTO)
    private Long roleId;
    /**
     * è§’色ID
     */
    @ExcelProperty(value = "角色序号")
    @TableId(value = "role_id", type = IdType.AUTO)
    private Long roleId;
    /** è§’色名称 */
    @Excel(name = "角色名称")
    @NotBlank(message = "角色名称不能为空")
    @Size(min = 0, max = 30, message = "角色名称长度不能超过30个字符")
    private String roleName;
    /**
     * è§’色名称
     */
    @ExcelProperty(value = "角色名称")
    @NotBlank(message = "角色名称不能为空")
    @Size(min = 0, max = 30, message = "角色名称长度不能超过30个字符")
    private String roleName;
    /** è§’色权限 */
    @Excel(name = "角色权限")
    @NotBlank(message = "权限字符不能为空")
    @Size(min = 0, max = 100, message = "权限字符长度不能超过100个字符")
    private String roleKey;
    /**
     * è§’色权限
     */
    @ExcelProperty(value = "角色权限")
    @NotBlank(message = "权限字符不能为空")
    @Size(min = 0, max = 100, message = "权限字符长度不能超过100个字符")
    private String roleKey;
    /** è§’色排序 */
    @Excel(name = "角色排序")
    @NotBlank(message = "显示顺序不能为空")
    private String roleSort;
    /**
     * è§’色排序
     */
    @ExcelProperty(value = "角色排序")
    @NotBlank(message = "显示顺序不能为空")
    private String roleSort;
    /** æ•°æ®èŒƒå›´ï¼ˆ1:所有数据权限;2:自定义数据权限;3:本部门数据权限;4:本部门及以下数据权限;5:仅本人数据权限) */
    @Excel(name = "数据范围", readConverterExp = "1=所有数据权限,2=自定义数据权限,3=本部门数据权限,4=本部门及以下数据权限,5=仅本人数据权限")
    private String dataScope;
    /**
     * æ•°æ®èŒƒå›´ï¼ˆ1:所有数据权限;2:自定义数据权限;3:本部门数据权限;4:本部门及以下数据权限;5:仅本人数据权限)
     */
    @ExcelProperty(value = "数据范围", converter = ExcelDictConvert.class)
    @ExcelDictFormat(readConverterExp = "1=所有数据权限,2=自定义数据权限,3=本部门数据权限,4=本部门及以下数据权限,5=仅本人数据权限")
    private String dataScope;
    /** èœå•树选择项是否关联显示( 0:父子不互相关联显示 1:父子互相关联显示) */
    private boolean menuCheckStrictly;
    /**
     * èœå•树选择项是否关联显示( 0:父子不互相关联显示 1:父子互相关联显示)
     */
    private boolean menuCheckStrictly;
    /** éƒ¨é—¨æ ‘选择项是否关联显示(0:父子不互相关联显示 1:父子互相关联显示 ï¼‰ */
    private boolean deptCheckStrictly;
    /**
     * éƒ¨é—¨æ ‘选择项是否关联显示(0:父子不互相关联显示 1:父子互相关联显示 ï¼‰
     */
    private boolean deptCheckStrictly;
    /** è§’色状态(0正常 1停用) */
    @Excel(name = "角色状态", readConverterExp = "0=正常,1=停用")
    private String status;
    /**
     * è§’色状态(0正常 1停用)
     */
    @ExcelProperty(value = "角色状态", converter = ExcelDictConvert.class)
    @ExcelDictFormat(dictType = "sys_common_status")
    private String status;
    /** åˆ é™¤æ ‡å¿—(0代表存在 2代表删除) */
    @TableLogic
    private String delFlag;
    /**
     * åˆ é™¤æ ‡å¿—(0代表存在 2代表删除)
     */
    @TableLogic
    private String delFlag;
    /** åˆ›å»ºè€… */
    @TableField(fill = FieldFill.INSERT)
    private String createBy;
    /**
     * åˆ›å»ºè€…
     */
    @TableField(fill = FieldFill.INSERT)
    private String createBy;
    /** åˆ›å»ºæ—¶é—´ */
    @TableField(fill = FieldFill.INSERT)
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private Date createTime;
    /**
     * åˆ›å»ºæ—¶é—´
     */
    @TableField(fill = FieldFill.INSERT)
    private Date createTime;
    /** æ›´æ–°è€… */
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private String updateBy;
    /**
     * æ›´æ–°è€…
     */
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private String updateBy;
    /** æ›´æ–°æ—¶é—´ */
    @TableField(fill = FieldFill.INSERT_UPDATE)
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private Date updateTime;
    /**
     * æ›´æ–°æ—¶é—´
     */
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private Date updateTime;
    /** å¤‡æ³¨ */
    private String remark;
    /**
     * å¤‡æ³¨
     */
    private String remark;
    /**
     * è¯·æ±‚参数
     */
    @TableField(exist = false)
    private Map<String, Object> params = new HashMap<>();
    /**
     * è¯·æ±‚参数
     */
    @TableField(exist = false)
    private Map<String, Object> params = new HashMap<>();
    /** ç”¨æˆ·æ˜¯å¦å­˜åœ¨æ­¤è§’色标识 é»˜è®¤ä¸å­˜åœ¨ */
    @TableField(exist = false)
    private boolean flag = false;
    /**
     * ç”¨æˆ·æ˜¯å¦å­˜åœ¨æ­¤è§’色标识 é»˜è®¤ä¸å­˜åœ¨
     */
    @TableField(exist = false)
    private boolean flag = false;
    /** èœå•组 */
    @TableField(exist = false)
    private Long[] menuIds;
    /**
     * èœå•组
     */
    @TableField(exist = false)
    private Long[] menuIds;
    /** éƒ¨é—¨ç»„(数据权限) */
    @TableField(exist = false)
    private Long[] deptIds;
    /**
     * éƒ¨é—¨ç»„(数据权限)
     */
    @TableField(exist = false)
    private Long[] deptIds;
    public SysRole(Long roleId)
    {
        this.roleId = roleId;
    }
    public SysRole(Long roleId) {
        this.roleId = roleId;
    }
    public boolean isAdmin()
    {
        return isAdmin(this.roleId);
    }
    public boolean isAdmin() {
        return isAdmin(this.roleId);
    }
    public static boolean isAdmin(Long roleId)
    {
        return roleId != null && 1L == roleId;
    }
    public static boolean isAdmin(Long roleId) {
        return roleId != null && 1L == roleId;
    }
}
ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysUser.java
@@ -1,13 +1,8 @@
package com.ruoyi.common.core.domain.entity;
import com.baomidou.mybatisplus.annotation.*;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.ruoyi.common.annotation.Excel;
import com.ruoyi.common.annotation.Excel.ColumnType;
import com.ruoyi.common.annotation.Excel.Type;
import com.ruoyi.common.annotation.Excels;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
@@ -31,140 +26,167 @@
@NoArgsConstructor
@Accessors(chain = true)
@TableName("sys_user")
public class SysUser implements Serializable
{
    private static final long serialVersionUID = 1L;
public class SysUser implements Serializable {
    private static final long serialVersionUID = 1L;
    /** ç”¨æˆ·ID */
    @Excel(name = "用户序号", cellType = ColumnType.NUMERIC, prompt = "用户编号")
    @TableId(value = "user_id",type = IdType.AUTO)
    private Long userId;
    /**
     * ç”¨æˆ·ID
     */
    @TableId(value = "user_id", type = IdType.AUTO)
    private Long userId;
    /** éƒ¨é—¨ID */
    @Excel(name = "部门编号", type = Type.IMPORT)
    private Long deptId;
    /**
     * éƒ¨é—¨ID
     */
    private Long deptId;
    /** ç”¨æˆ·è´¦å· */
    @NotBlank(message = "用户账号不能为空")
    @Size(min = 0, max = 30, message = "用户账号长度不能超过30个字符")
    @Excel(name = "登录名称")
    private String userName;
    /**
     * ç”¨æˆ·è´¦å·
     */
    @NotBlank(message = "用户账号不能为空")
    @Size(min = 0, max = 30, message = "用户账号长度不能超过30个字符")
    private String userName;
    /** ç”¨æˆ·æ˜µç§° */
    @Size(min = 0, max = 30, message = "用户昵称长度不能超过30个字符")
    @Excel(name = "用户名称")
    private String nickName;
    /**
     * ç”¨æˆ·æ˜µç§°
     */
    @Size(min = 0, max = 30, message = "用户昵称长度不能超过30个字符")
    private String nickName;
    /** ç”¨æˆ·é‚®ç®± */
    @Email(message = "邮箱格式不正确")
    @Size(min = 0, max = 50, message = "邮箱长度不能超过50个字符")
    @Excel(name = "用户邮箱")
    private String email;
    /**
     * ç”¨æˆ·é‚®ç®±
     */
    @Email(message = "邮箱格式不正确")
    @Size(min = 0, max = 50, message = "邮箱长度不能超过50个字符")
    private String email;
    /** æ‰‹æœºå·ç  */
    @Size(min = 0, max = 11, message = "手机号码长度不能超过11个字符")
    @Excel(name = "手机号码")
    private String phonenumber;
    /**
     * æ‰‹æœºå·ç 
     */
    private String phonenumber;
    /** ç”¨æˆ·æ€§åˆ« */
    @Excel(name = "用户性别", readConverterExp = "0=男,1=女,2=未知")
    private String sex;
    /**
     * ç”¨æˆ·æ€§åˆ«
     */
    private String sex;
    /** ç”¨æˆ·å¤´åƒ */
    private String avatar;
    /**
     * ç”¨æˆ·å¤´åƒ
     */
    private String avatar;
    /** å¯†ç  */
    private String password;
    /**
     * å¯†ç 
     */
    private String password;
    @JsonIgnore
    @JsonProperty
    public String getPassword() {
        return password;
    }
    @JsonIgnore
    @JsonProperty
    public String getPassword() {
        return password;
    }
    /** ç›åР坆 */
    private String salt;
    /**
     * ç›åР坆
     */
    private String salt;
    /** å¸å·çŠ¶æ€ï¼ˆ0正常 1停用) */
    @Excel(name = "帐号状态", readConverterExp = "0=正常,1=停用")
    private String status;
    /**
     * å¸å·çŠ¶æ€ï¼ˆ0正常 1停用)
     */
    private String status;
    /** åˆ é™¤æ ‡å¿—(0代表存在 2代表删除) */
    @TableLogic
    private String delFlag;
    /**
     * åˆ é™¤æ ‡å¿—(0代表存在 2代表删除)
     */
    @TableLogic
    private String delFlag;
    /** æœ€åŽç™»å½•IP */
    @Excel(name = "最后登录IP", type = Type.EXPORT)
    private String loginIp;
    /**
     * æœ€åŽç™»å½•IP
     */
    private String loginIp;
    /** æœ€åŽç™»å½•æ—¶é—´ */
    @Excel(name = "最后登录时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss", type = Type.EXPORT)
    private Date loginDate;
    /**
     * æœ€åŽç™»å½•æ—¶é—´
     */
    private Date loginDate;
    /** åˆ›å»ºè€… */
    @TableField(fill = FieldFill.INSERT)
    private String createBy;
    /**
     * åˆ›å»ºè€…
     */
    @TableField(fill = FieldFill.INSERT)
    private String createBy;
    /** åˆ›å»ºæ—¶é—´ */
    @TableField(fill = FieldFill.INSERT)
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private Date createTime;
    /**
     * åˆ›å»ºæ—¶é—´
     */
    @TableField(fill = FieldFill.INSERT)
    private Date createTime;
    /** æ›´æ–°è€… */
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private String updateBy;
    /**
     * æ›´æ–°è€…
     */
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private String updateBy;
    /** æ›´æ–°æ—¶é—´ */
    @TableField(fill = FieldFill.INSERT_UPDATE)
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private Date updateTime;
    /**
     * æ›´æ–°æ—¶é—´
     */
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private Date updateTime;
    /** å¤‡æ³¨ */
    private String remark;
    /**
     * å¤‡æ³¨
     */
    private String remark;
    /**
     * è¯·æ±‚参数
     */
    @TableField(exist = false)
    private Map<String, Object> params = new HashMap<>();
    /**
     * è¯·æ±‚参数
     */
    @TableField(exist = false)
    private Map<String, Object> params = new HashMap<>();
    /** éƒ¨é—¨å¯¹è±¡ */
    @Excels({
        @Excel(name = "部门名称", targetAttr = "deptName", type = Type.EXPORT),
        @Excel(name = "部门负责人", targetAttr = "leader", type = Type.EXPORT)
    })
    @TableField(exist = false)
    private SysDept dept;
    /**
     * éƒ¨é—¨å¯¹è±¡
     */
    @TableField(exist = false)
    private SysDept dept;
    /** è§’色对象 */
    @TableField(exist = false)
    private List<SysRole> roles;
    /**
     * è§’色对象
     */
    @TableField(exist = false)
    private List<SysRole> roles;
    /** è§’色组 */
    @TableField(exist = false)
    private Long[] roleIds;
    /**
     * è§’色组
     */
    @TableField(exist = false)
    private Long[] roleIds;
    /** å²—位组 */
    @TableField(exist = false)
    private Long[] postIds;
    /**
     * å²—位组
     */
    @TableField(exist = false)
    private Long[] postIds;
    /** è§’色ID */
    /**
     * è§’色ID
     */
    @TableField(exist = false)
    private Long roleId;
    public SysUser(Long userId)
    {
        this.userId = userId;
    }
    public SysUser(Long userId) {
        this.userId = userId;
    }
    public boolean isAdmin()
    {
        return isAdmin(this.userId);
    }
    public boolean isAdmin() {
        return isAdmin(this.userId);
    }
    public static boolean isAdmin(Long userId)
    {
        return userId != null && 1L == userId;
    }
    public static boolean isAdmin(Long userId) {
        return userId != null && 1L == userId;
    }
}
ruoyi-common/src/main/java/com/ruoyi/common/core/domain/model/LoginUser.java
@@ -24,6 +24,16 @@
    private static final long serialVersionUID = 1L;
    /**
     * ç”¨æˆ·ID
     */
    private Long userId;
    /**
     * éƒ¨é—¨ID
     */
    private Long deptId;
    /**
     * ç”¨æˆ·å”¯ä¸€æ ‡è¯†
     */
    private String token;
@@ -74,6 +84,14 @@
        this.permissions = permissions;
    }
    public LoginUser(Long userId, Long deptId, SysUser user, Set<String> permissions)
    {
        this.userId = userId;
        this.deptId = deptId;
        this.user = user;
        this.permissions = permissions;
    }
    @JsonIgnore
    @Override
    public String getPassword()
@@ -81,7 +99,6 @@
        return user.getPassword();
    }
    @JsonIgnore
    @Override
    public String getUsername()
    {
@@ -134,7 +151,6 @@
        return true;
    }
    @JsonIgnore
    @Override
    public Collection<? extends GrantedAuthority> getAuthorities()
    {
ruoyi-common/src/main/java/com/ruoyi/common/core/domain/model/RegisterBody.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,11 @@
package com.ruoyi.common.core.domain.model;
/**
 * ç”¨æˆ·æ³¨å†Œå¯¹è±¡
 *
 * @author ruoyi
 */
public class RegisterBody extends LoginBody
{
}
ruoyi-common/src/main/java/com/ruoyi/common/core/mybatisplus/methods/InsertAll.java
@@ -1,11 +1,10 @@
package com.ruoyi.common.core.mybatisplus.methods;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.core.enums.SqlMethod;
import com.baomidou.mybatisplus.core.injector.AbstractMethod;
import com.baomidou.mybatisplus.core.metadata.TableInfo;
import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
import com.ruoyi.common.utils.StringUtils;
import org.apache.ibatis.executor.keygen.Jdbc3KeyGenerator;
import org.apache.ibatis.executor.keygen.KeyGenerator;
import org.apache.ibatis.executor.keygen.NoKeyGenerator;
@@ -25,11 +24,11 @@
        final String fieldSql = prepareFieldSql(tableInfo);
        final String valueSql = prepareValuesSqlForMysqlBatch(tableInfo);
        KeyGenerator keyGenerator = new NoKeyGenerator();
        SqlMethod sqlMethod = SqlMethod.INSERT_ONE;
        String sqlMethod = "insertAll";
        String keyProperty = null;
        String keyColumn = null;
        // è¡¨åŒ…含主键处理逻辑,如果不包含主键当普通字段处理
        if (StrUtil.isNotBlank(tableInfo.getKeyProperty())) {
        if (StringUtils.isNotBlank(tableInfo.getKeyProperty())) {
            if (tableInfo.getIdType() == IdType.AUTO) {
                /** è‡ªå¢žä¸»é”® */
                keyGenerator = new Jdbc3KeyGenerator();
@@ -37,7 +36,7 @@
                keyColumn = tableInfo.getKeyColumn();
            } else {
                if (null != tableInfo.getKeySequence()) {
                    keyGenerator = TableInfoHelper.genKeyGenerator(getMethod(sqlMethod), tableInfo, builderAssistant);
                    keyGenerator = TableInfoHelper.genKeyGenerator(sqlMethod, tableInfo, builderAssistant);
                    keyProperty = tableInfo.getKeyProperty();
                    keyColumn = tableInfo.getKeyColumn();
                }
@@ -45,12 +44,12 @@
        }
        final String sqlResult = String.format(sql, tableInfo.getTableName(), fieldSql, valueSql);
        SqlSource sqlSource = languageDriver.createSqlSource(configuration, sqlResult, modelClass);
        return this.addInsertMappedStatement(mapperClass, modelClass, "insertAll", sqlSource, keyGenerator, keyProperty, keyColumn);
        return this.addInsertMappedStatement(mapperClass, modelClass, sqlMethod, sqlSource, keyGenerator, keyProperty, keyColumn);
    }
    private String prepareFieldSql(TableInfo tableInfo) {
        StringBuilder fieldSql = new StringBuilder();
        if (StrUtil.isNotBlank(tableInfo.getKeyColumn())) {
        if (StringUtils.isNotBlank(tableInfo.getKeyColumn())) {
            fieldSql.append(tableInfo.getKeyColumn()).append(",");
        }
        tableInfo.getFieldList().forEach(x -> fieldSql.append(x.getColumn()).append(","));
@@ -63,7 +62,7 @@
    private String prepareValuesSqlForMysqlBatch(TableInfo tableInfo) {
        final StringBuilder valueSql = new StringBuilder();
        valueSql.append("<foreach collection=\"list\" item=\"item\" index=\"index\" open=\"(\" separator=\"),(\" close=\")\">");
        if (StrUtil.isNotBlank(tableInfo.getKeyColumn())) {
        if (StringUtils.isNotBlank(tableInfo.getKeyColumn())) {
            valueSql.append("#{item.").append(tableInfo.getKeyProperty()).append("},");
        }
        tableInfo.getFieldList().forEach(x -> valueSql.append("#{item.").append(x.getProperty()).append("},"));
ruoyi-common/src/main/java/com/ruoyi/common/enums/LimitType.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,20 @@
package com.ruoyi.common.enums;
/**
 * é™æµç±»åž‹
 *
 * @author ruoyi
 */
public enum LimitType
{
    /**
     * é»˜è®¤ç­–略全局限流
     */
    DEFAULT,
    /**
     * æ ¹æ®è¯·æ±‚者IP进行限流
     */
    IP
}
ruoyi-common/src/main/java/com/ruoyi/common/exception/CustomException.java
ÎļþÒÑɾ³ý
ruoyi-common/src/main/java/com/ruoyi/common/exception/GlobalException.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,58 @@
package com.ruoyi.common.exception;
/**
 * å…¨å±€å¼‚常
 *
 * @author ruoyi
 */
public class GlobalException extends RuntimeException
{
    private static final long serialVersionUID = 1L;
    /**
     * é”™è¯¯æç¤º
     */
    private String message;
    /**
     * é”™è¯¯æ˜Žç»†ï¼Œå†…部调试错误
     *
     * å’Œ {@link CommonResult#getDetailMessage()} ä¸€è‡´çš„设计
     */
    private String detailMessage;
    /**
     * ç©ºæž„造方法,避免反序列化问题
     */
    public GlobalException()
    {
    }
    public GlobalException(String message)
    {
        this.message = message;
    }
    public String getDetailMessage()
    {
        return detailMessage;
    }
    public GlobalException setDetailMessage(String detailMessage)
    {
        this.detailMessage = detailMessage;
        return this;
    }
    public String getMessage()
    {
        return message;
    }
    public GlobalException setMessage(String message)
    {
        this.message = message;
        return this;
    }
}
ruoyi-common/src/main/java/com/ruoyi/common/exception/ServiceException.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,73 @@
package com.ruoyi.common.exception;
/**
 * ä¸šåС异叏
 *
 * @author ruoyi
 */
public final class ServiceException extends RuntimeException
{
    private static final long serialVersionUID = 1L;
    /**
     * é”™è¯¯ç 
     */
    private Integer code;
    /**
     * é”™è¯¯æç¤º
     */
    private String message;
    /**
     * é”™è¯¯æ˜Žç»†ï¼Œå†…部调试错误
     *
     * å’Œ {@link CommonResult#getDetailMessage()} ä¸€è‡´çš„设计
     */
    private String detailMessage;
    /**
     * ç©ºæž„造方法,避免反序列化问题
     */
    public ServiceException()
    {
    }
    public ServiceException(String message)
    {
        this.message = message;
    }
    public ServiceException(String message, Integer code)
    {
        this.message = message;
        this.code = code;
    }
    public String getDetailMessage()
    {
        return detailMessage;
    }
    public String getMessage()
    {
        return message;
    }
    public Integer getCode()
    {
        return code;
    }
    public ServiceException setMessage(String message)
    {
        this.message = message;
        return this;
    }
    public ServiceException setDetailMessage(String detailMessage)
    {
        this.detailMessage = detailMessage;
        return this;
    }
}
ruoyi-common/src/main/java/com/ruoyi/common/exception/base/BaseException.java
ÎļþÃû´Ó ruoyi-common/src/main/java/com/ruoyi/common/exception/BaseException.java ÐÞ¸Ä
@@ -1,11 +1,11 @@
package com.ruoyi.common.exception;
package com.ruoyi.common.exception.base;
import cn.hutool.core.lang.Validator;
import com.ruoyi.common.utils.MessageUtils;
import com.ruoyi.common.utils.StringUtils;
/**
 * åŸºç¡€å¼‚常
 *
 *
 * @author ruoyi
 */
public class BaseException extends RuntimeException
@@ -64,7 +64,7 @@
    public String getMessage()
    {
        String message = null;
        if (!Validator.isEmpty(code))
        if (!StringUtils.isEmpty(code))
        {
            message = MessageUtils.message(code, args);
        }
ruoyi-common/src/main/java/com/ruoyi/common/exception/file/FileException.java
@@ -1,10 +1,10 @@
package com.ruoyi.common.exception.file;
import com.ruoyi.common.exception.BaseException;
import com.ruoyi.common.exception.base.BaseException;
/**
 * æ–‡ä»¶ä¿¡æ¯å¼‚常类
 *
 *
 * @author ruoyi
 */
public class FileException extends BaseException
ruoyi-common/src/main/java/com/ruoyi/common/exception/user/UserException.java
@@ -1,10 +1,10 @@
package com.ruoyi.common.exception.user;
import com.ruoyi.common.exception.BaseException;
import com.ruoyi.common.exception.base.BaseException;
/**
 * ç”¨æˆ·ä¿¡æ¯å¼‚常类
 *
 *
 * @author ruoyi
 */
public class UserException extends BaseException
ruoyi-common/src/main/java/com/ruoyi/common/filter/RepeatableFilter.java
@@ -1,48 +1,48 @@
package com.ruoyi.common.filter;
import cn.hutool.core.util.StrUtil;
import org.springframework.http.MediaType;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
/**
 * Repeatable è¿‡æ»¤å™¨
 *
 * @author ruoyi
 */
public class RepeatableFilter implements Filter
{
    @Override
    public void init(FilterConfig filterConfig) throws ServletException
    {
    }
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException
    {
        ServletRequest requestWrapper = null;
        if (request instanceof HttpServletRequest
                && StrUtil.startWithIgnoreCase(request.getContentType(), MediaType.APPLICATION_JSON_VALUE))
        {
            requestWrapper = new RepeatedlyRequestWrapper((HttpServletRequest) request, response);
        }
        if (null == requestWrapper)
        {
            chain.doFilter(request, response);
        }
        else
        {
            chain.doFilter(requestWrapper, response);
        }
    }
    @Override
    public void destroy()
    {
    }
}
package com.ruoyi.common.filter;
import com.ruoyi.common.utils.StringUtils;
import org.springframework.http.MediaType;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
/**
 * Repeatable è¿‡æ»¤å™¨
 *
 * @author ruoyi
 */
public class RepeatableFilter implements Filter
{
    @Override
    public void init(FilterConfig filterConfig) throws ServletException
    {
    }
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException
    {
        ServletRequest requestWrapper = null;
        if (request instanceof HttpServletRequest
                && StringUtils.startsWithIgnoreCase(request.getContentType(), MediaType.APPLICATION_JSON_VALUE))
        {
            requestWrapper = new RepeatedlyRequestWrapper((HttpServletRequest) request, response);
        }
        if (null == requestWrapper)
        {
            chain.doFilter(request, response);
        }
        else
        {
            chain.doFilter(requestWrapper, response);
        }
    }
    @Override
    public void destroy()
    {
    }
}
ruoyi-common/src/main/java/com/ruoyi/common/filter/XssFilter.java
@@ -1,6 +1,6 @@
package com.ruoyi.common.filter;
import cn.hutool.core.util.StrUtil;
import com.ruoyi.common.utils.StringUtils;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
@@ -8,12 +8,10 @@
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
 * é˜²æ­¢XSS攻击的过滤器
 *
 *
 * @author ruoyi
 */
public class XssFilter implements Filter
@@ -23,27 +21,17 @@
     */
    public List<String> excludes = new ArrayList<>();
    /**
     * xss过滤开关
     */
    public boolean enabled = false;
    @Override
    public void init(FilterConfig filterConfig) throws ServletException
    {
        String tempExcludes = filterConfig.getInitParameter("excludes");
        String tempEnabled = filterConfig.getInitParameter("enabled");
        if (StrUtil.isNotEmpty(tempExcludes))
        if (StringUtils.isNotEmpty(tempExcludes))
        {
            String[] url = tempExcludes.split(",");
            for (int i = 0; url != null && i < url.length; i++)
            {
                excludes.add(url[i]);
            }
        }
        if (StrUtil.isNotEmpty(tempEnabled))
        {
            enabled = Boolean.valueOf(tempEnabled);
        }
    }
@@ -64,25 +52,14 @@
    private boolean handleExcludeURL(HttpServletRequest request, HttpServletResponse response)
    {
        if (!enabled)
        String url = request.getServletPath();
        String method = request.getMethod();
        // GET DELETE ä¸è¿‡æ»¤
        if (method == null || method.matches("GET") || method.matches("DELETE"))
        {
            return true;
        }
        if (excludes == null || excludes.isEmpty())
        {
            return false;
        }
        String url = request.getServletPath();
        for (String pattern : excludes)
        {
            Pattern p = Pattern.compile("^" + pattern);
            Matcher m = p.matcher(url);
            if (m.find())
            {
                return true;
            }
        }
        return false;
        return StringUtils.matches(url, excludes);
    }
    @Override
@@ -90,4 +67,4 @@
    {
    }
}
}
ruoyi-common/src/main/java/com/ruoyi/common/filter/XssHttpServletRequestWrapper.java
@@ -1,9 +1,8 @@
package com.ruoyi.common.filter;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.lang.Validator;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HtmlUtil;
import com.ruoyi.common.utils.StringUtils;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
@@ -59,7 +58,7 @@
        // ä¸ºç©ºï¼Œç›´æŽ¥è¿”回
        String json = IoUtil.read(super.getInputStream(), StandardCharsets.UTF_8);
        if (Validator.isEmpty(json))
        if (StringUtils.isEmpty(json))
        {
            return super.getInputStream();
        }
@@ -103,6 +102,6 @@
    public boolean isJsonRequest()
    {
        String header = super.getHeader(HttpHeaders.CONTENT_TYPE);
        return StrUtil.startWithIgnoreCase(header, MediaType.APPLICATION_JSON_VALUE);
        return StringUtils.startsWithIgnoreCase(header, MediaType.APPLICATION_JSON_VALUE);
    }
}
ruoyi-common/src/main/java/com/ruoyi/common/utils/DictUtils.java
@@ -1,187 +1,185 @@
package com.ruoyi.common.utils;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Validator;
import cn.hutool.core.util.StrUtil;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.core.domain.entity.SysDictData;
import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.common.utils.spring.SpringUtils;
import java.util.Collection;
import java.util.List;
/**
 * å­—典工具类
 *
 * @author ruoyi
 */
public class DictUtils
{
    /**
     * åˆ†éš”符
     */
    public static final String SEPARATOR = ",";
    /**
     * è®¾ç½®å­—典缓存
     *
     * @param key å‚æ•°é”®
     * @param dictDatas å­—典数据列表
     */
    public static void setDictCache(String key, List<SysDictData> dictDatas)
    {
        SpringUtils.getBean(RedisCache.class).setCacheObject(getCacheKey(key), dictDatas);
    }
    /**
     * èŽ·å–å­—å…¸ç¼“å­˜
     *
     * @param key å‚æ•°é”®
     * @return dictDatas å­—典数据列表
     */
    public static List<SysDictData> getDictCache(String key)
    {
        Object cacheObj = SpringUtils.getBean(RedisCache.class).getCacheObject(getCacheKey(key));
        if (Validator.isNotNull(cacheObj))
        {
            List<SysDictData> dictDatas = (List<SysDictData>)cacheObj;
            return dictDatas;
        }
        return null;
    }
    /**
     * æ ¹æ®å­—典类型和字典值获取字典标签
     *
     * @param dictType å­—典类型
     * @param dictValue å­—典值
     * @return å­—典标签
     */
    public static String getDictLabel(String dictType, String dictValue)
    {
        return getDictLabel(dictType, dictValue, SEPARATOR);
    }
    /**
     * æ ¹æ®å­—典类型和字典标签获取字典值
     *
     * @param dictType å­—典类型
     * @param dictLabel å­—典标签
     * @return å­—典值
     */
    public static String getDictValue(String dictType, String dictLabel)
    {
        return getDictValue(dictType, dictLabel, SEPARATOR);
    }
    /**
     * æ ¹æ®å­—典类型和字典值获取字典标签
     *
     * @param dictType å­—典类型
     * @param dictValue å­—典值
     * @param separator åˆ†éš”符
     * @return å­—典标签
     */
    public static String getDictLabel(String dictType, String dictValue, String separator)
    {
        StringBuilder propertyString = new StringBuilder();
        List<SysDictData> datas = getDictCache(dictType);
        if (StrUtil.containsAny(dictValue, separator) && CollUtil.isNotEmpty(datas))
        {
            for (SysDictData dict : datas)
            {
                for (String value : dictValue.split(separator))
                {
                    if (value.equals(dict.getDictValue()))
                    {
                        propertyString.append(dict.getDictLabel() + separator);
                        break;
                    }
                }
            }
        }
        else
        {
            for (SysDictData dict : datas)
            {
                if (dictValue.equals(dict.getDictValue()))
                {
                    return dict.getDictLabel();
                }
            }
        }
        return StrUtil.strip(propertyString.toString(), null, separator);
    }
    /**
     * æ ¹æ®å­—典类型和字典标签获取字典值
     *
     * @param dictType å­—典类型
     * @param dictLabel å­—典标签
     * @param separator åˆ†éš”符
     * @return å­—典值
     */
    public static String getDictValue(String dictType, String dictLabel, String separator)
    {
        StringBuilder propertyString = new StringBuilder();
        List<SysDictData> datas = getDictCache(dictType);
        if (StrUtil.containsAny(dictLabel, separator) && CollUtil.isNotEmpty(datas))
        {
            for (SysDictData dict : datas)
            {
                for (String label : dictLabel.split(separator))
                {
                    if (label.equals(dict.getDictLabel()))
                    {
                        propertyString.append(dict.getDictValue() + separator);
                        break;
                    }
                }
            }
        }
        else
        {
            for (SysDictData dict : datas)
            {
                if (dictLabel.equals(dict.getDictLabel()))
                {
                    return dict.getDictValue();
                }
            }
        }
        return StrUtil.strip(propertyString.toString(), null, separator);
    }
    /**
     * åˆ é™¤æŒ‡å®šå­—典缓存
     *
     * @param key å­—典键
     */
    public static void removeDictCache(String key)
    {
        SpringUtils.getBean(RedisCache.class).deleteObject(getCacheKey(key));
    }
    /**
     * æ¸…空字典缓存
     */
    public static void clearDictCache()
    {
        Collection<String> keys = SpringUtils.getBean(RedisCache.class).keys(Constants.SYS_DICT_KEY + "*");
        SpringUtils.getBean(RedisCache.class).deleteObject(keys);
    }
    /**
     * è®¾ç½®cache key
     *
     * @param configKey å‚æ•°é”®
     * @return ç¼“存键key
     */
    public static String getCacheKey(String configKey)
    {
        return Constants.SYS_DICT_KEY + configKey;
    }
}
package com.ruoyi.common.utils;
import cn.hutool.core.collection.CollUtil;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.core.domain.entity.SysDictData;
import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.common.utils.spring.SpringUtils;
import java.util.Collection;
import java.util.List;
/**
 * å­—典工具类
 *
 * @author ruoyi
 */
public class DictUtils
{
    /**
     * åˆ†éš”符
     */
    public static final String SEPARATOR = ",";
    /**
     * è®¾ç½®å­—典缓存
     *
     * @param key å‚æ•°é”®
     * @param dictDatas å­—典数据列表
     */
    public static void setDictCache(String key, List<SysDictData> dictDatas)
    {
        SpringUtils.getBean(RedisCache.class).setCacheObject(getCacheKey(key), dictDatas);
    }
    /**
     * èŽ·å–å­—å…¸ç¼“å­˜
     *
     * @param key å‚æ•°é”®
     * @return dictDatas å­—典数据列表
     */
    public static List<SysDictData> getDictCache(String key)
    {
        Object cacheObj = SpringUtils.getBean(RedisCache.class).getCacheObject(getCacheKey(key));
        if (StringUtils.isNotNull(cacheObj))
        {
            List<SysDictData> dictDatas = (List<SysDictData>)cacheObj;
            return dictDatas;
        }
        return null;
    }
    /**
     * æ ¹æ®å­—典类型和字典值获取字典标签
     *
     * @param dictType å­—典类型
     * @param dictValue å­—典值
     * @return å­—典标签
     */
    public static String getDictLabel(String dictType, String dictValue)
    {
        return getDictLabel(dictType, dictValue, SEPARATOR);
    }
    /**
     * æ ¹æ®å­—典类型和字典标签获取字典值
     *
     * @param dictType å­—典类型
     * @param dictLabel å­—典标签
     * @return å­—典值
     */
    public static String getDictValue(String dictType, String dictLabel)
    {
        return getDictValue(dictType, dictLabel, SEPARATOR);
    }
    /**
     * æ ¹æ®å­—典类型和字典值获取字典标签
     *
     * @param dictType å­—典类型
     * @param dictValue å­—典值
     * @param separator åˆ†éš”符
     * @return å­—典标签
     */
    public static String getDictLabel(String dictType, String dictValue, String separator)
    {
        StringBuilder propertyString = new StringBuilder();
        List<SysDictData> datas = getDictCache(dictType);
        if (StringUtils.containsAny(dictValue, separator) && CollUtil.isNotEmpty(datas))
        {
            for (SysDictData dict : datas)
            {
                for (String value : dictValue.split(separator))
                {
                    if (value.equals(dict.getDictValue()))
                    {
                        propertyString.append(dict.getDictLabel() + separator);
                        break;
                    }
                }
            }
        }
        else
        {
            for (SysDictData dict : datas)
            {
                if (dictValue.equals(dict.getDictValue()))
                {
                    return dict.getDictLabel();
                }
            }
        }
        return StringUtils.stripEnd(propertyString.toString(), separator);
    }
    /**
     * æ ¹æ®å­—典类型和字典标签获取字典值
     *
     * @param dictType å­—典类型
     * @param dictLabel å­—典标签
     * @param separator åˆ†éš”符
     * @return å­—典值
     */
    public static String getDictValue(String dictType, String dictLabel, String separator)
    {
        StringBuilder propertyString = new StringBuilder();
        List<SysDictData> datas = getDictCache(dictType);
        if (StringUtils.containsAny(dictLabel, separator) && CollUtil.isNotEmpty(datas))
        {
            for (SysDictData dict : datas)
            {
                for (String label : dictLabel.split(separator))
                {
                    if (label.equals(dict.getDictLabel()))
                    {
                        propertyString.append(dict.getDictValue() + separator);
                        break;
                    }
                }
            }
        }
        else
        {
            for (SysDictData dict : datas)
            {
                if (dictLabel.equals(dict.getDictLabel()))
                {
                    return dict.getDictValue();
                }
            }
        }
        return StringUtils.stripEnd(propertyString.toString(), separator);
    }
    /**
     * åˆ é™¤æŒ‡å®šå­—典缓存
     *
     * @param key å­—典键
     */
    public static void removeDictCache(String key)
    {
        SpringUtils.getBean(RedisCache.class).deleteObject(getCacheKey(key));
    }
    /**
     * æ¸…空字典缓存
     */
    public static void clearDictCache()
    {
        Collection<String> keys = SpringUtils.getBean(RedisCache.class).keys(Constants.SYS_DICT_KEY + "*");
        SpringUtils.getBean(RedisCache.class).deleteObject(keys);
    }
    /**
     * è®¾ç½®cache key
     *
     * @param configKey å‚æ•°é”®
     * @return ç¼“存键key
     */
    public static String getCacheKey(String configKey)
    {
        return Constants.SYS_DICT_KEY + configKey;
    }
}
ruoyi-common/src/main/java/com/ruoyi/common/utils/JsonUtils.java
@@ -1,8 +1,6 @@
package com.ruoyi.common.utils;
import cn.hutool.core.lang.Validator;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.StrUtil;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
@@ -33,7 +31,7 @@
    }
    public static String toJsonString(Object object) {
        if (Validator.isEmpty(object)) {
        if (StringUtils.isNull(object)) {
            return null;
        }
        try {
@@ -44,7 +42,7 @@
    }
    public static <T> T parseObject(String text, Class<T> clazz) {
        if (StrUtil.isEmpty(text)) {
        if (StringUtils.isEmpty(text)) {
            return null;
        }
        try {
@@ -66,7 +64,7 @@
    }
    public static <T> T parseObject(String text, TypeReference<T> typeReference) {
        if (StrUtil.isBlank(text)) {
        if (StringUtils.isBlank(text)) {
            return null;
        }
        try {
@@ -77,7 +75,7 @@
    }
    public static <T> Map<String, T> parseMap(String text) {
        if (StrUtil.isBlank(text)) {
        if (StringUtils.isBlank(text)) {
            return null;
        }
        try {
@@ -88,7 +86,7 @@
    }
    public static <T> List<T> parseArray(String text, Class<T> clazz) {
        if (StrUtil.isEmpty(text)) {
        if (StringUtils.isEmpty(text)) {
            return new ArrayList<>();
        }
        try {
ruoyi-common/src/main/java/com/ruoyi/common/utils/PageUtils.java
@@ -1,6 +1,5 @@
package com.ruoyi.common.utils;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpStatus;
import com.baomidou.mybatisplus.core.metadata.OrderItem;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
@@ -59,7 +58,7 @@
        String orderByColumn = ServletUtils.getParameter(ORDER_BY_COLUMN);
        String isAsc = ServletUtils.getParameter(IS_ASC);
        PagePlus<T, K> page = new PagePlus<>(pageNum, pageSize);
        if (StrUtil.isNotBlank(orderByColumn)) {
        if (StringUtils.isNotBlank(orderByColumn)) {
            String orderBy = SqlUtil.escapeOrderBySql(orderByColumn);
            if ("asc".equals(isAsc)) {
                page.addOrder(OrderItem.asc(orderBy));
@@ -91,9 +90,9 @@
            isAsc = "desc";
        }
        Page<T> page = new Page<>(pageNum, pageSize);
        if (StrUtil.isNotBlank(orderByColumn)) {
        if (StringUtils.isNotBlank(orderByColumn)) {
            String orderBy = SqlUtil.escapeOrderBySql(orderByColumn);
            orderBy = StrUtil.toUnderlineCase(orderBy);
            orderBy = StringUtils.toUnderScoreCase(orderBy);
            if ("asc".equals(isAsc)) {
                page.addOrder(OrderItem.asc(orderBy));
            } else if ("desc".equals(isAsc)) {
ruoyi-common/src/main/java/com/ruoyi/common/utils/SecurityUtils.java
@@ -5,15 +5,45 @@
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import com.ruoyi.common.core.domain.model.LoginUser;
import com.ruoyi.common.exception.CustomException;
import com.ruoyi.common.exception.ServiceException;
/**
 * å®‰å…¨æœåŠ¡å·¥å…·ç±»
 *
 *
 * @author ruoyi
 */
public class SecurityUtils
{
    /**
     * ç”¨æˆ·ID
     **/
    public static Long getUserId()
    {
        try
        {
            return getLoginUser().getUserId();
        }
        catch (Exception e)
        {
            throw new ServiceException("获取用户ID异常", HttpStatus.HTTP_UNAUTHORIZED);
        }
    }
    /**
     * èŽ·å–éƒ¨é—¨ID
     **/
    public static Long getDeptId()
    {
        try
        {
            return getLoginUser().getDeptId();
        }
        catch (Exception e)
        {
            throw new ServiceException("获取部门ID异常", HttpStatus.HTTP_UNAUTHORIZED);
        }
    }
    /**
     * èŽ·å–ç”¨æˆ·è´¦æˆ·
     **/
@@ -25,7 +55,7 @@
        }
        catch (Exception e)
        {
            throw new CustomException("获取用户账户异常", HttpStatus.HTTP_UNAUTHORIZED);
            throw new ServiceException("获取用户账户异常", HttpStatus.HTTP_UNAUTHORIZED);
        }
    }
@@ -40,7 +70,7 @@
        }
        catch (Exception e)
        {
            throw new CustomException("获取用户信息异常", HttpStatus.HTTP_UNAUTHORIZED);
            throw new ServiceException("获取用户信息异常", HttpStatus.HTTP_UNAUTHORIZED);
        }
    }
@@ -79,7 +109,7 @@
    /**
     * æ˜¯å¦ä¸ºç®¡ç†å‘˜
     *
     *
     * @param userId ç”¨æˆ·ID
     * @return ç»“æžœ
     */
ruoyi-common/src/main/java/com/ruoyi/common/utils/ServletUtils.java
@@ -1,7 +1,6 @@
package com.ruoyi.common.utils;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.servlet.ServletUtil;
import cn.hutool.http.HttpStatus;
import org.springframework.http.MediaType;
@@ -112,12 +111,12 @@
        }
        String uri = request.getRequestURI();
        if (StrUtil.equalsAnyIgnoreCase(uri, ".json", ".xml")) {
        if (StringUtils.equalsAnyIgnoreCase(uri, ".json", ".xml")) {
            return true;
        }
        String ajax = request.getParameter("__ajax");
        if (StrUtil.equalsAnyIgnoreCase(ajax, "json", "xml")) {
        if (StringUtils.equalsAnyIgnoreCase(ajax, "json", "xml")) {
            return true;
        }
        return false;
ruoyi-common/src/main/java/com/ruoyi/common/utils/StringUtils.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,354 @@
package com.ruoyi.common.utils;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Validator;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.ReUtil;
import cn.hutool.core.util.StrUtil;
import java.util.*;
/**
 * å­—符串工具类
 *
 * @author ruoyi
 */
public class StringUtils extends org.apache.commons.lang3.StringUtils {
    /**
     * èŽ·å–å‚æ•°ä¸ä¸ºç©ºå€¼
     *
     * @param value defaultValue è¦åˆ¤æ–­çš„value
     * @return value è¿”回值
     */
    public static <T> T nvl(T value, T defaultValue) {
        return ObjectUtil.defaultIfNull(value, defaultValue);
    }
    /**
     * èŽ·å–å‚æ•°ä¸ä¸ºç©ºå€¼
     *
     * @param str defaultValue è¦åˆ¤æ–­çš„value
     * @return value è¿”回值
     */
    public static String blankToDefault(String str, String defaultValue) {
        return StrUtil.blankToDefault(str, defaultValue);
    }
    /**
     * * åˆ¤æ–­ä¸€ä¸ªCollection是否为空, åŒ…含List,Set,Queue
     *
     * @param coll è¦åˆ¤æ–­çš„Collection
     * @return true:为空 false:非空
     */
    public static boolean isEmpty(Collection<?> coll) {
        return CollUtil.isEmpty(coll);
    }
    /**
     * * åˆ¤æ–­ä¸€ä¸ªCollection是否非空,包含List,Set,Queue
     *
     * @param coll è¦åˆ¤æ–­çš„Collection
     * @return true:非空 false:空
     */
    public static boolean isNotEmpty(Collection<?> coll) {
        return !isEmpty(coll);
    }
    /**
     * * åˆ¤æ–­ä¸€ä¸ªå¯¹è±¡æ•°ç»„是否为空
     *
     * @param objects è¦åˆ¤æ–­çš„对象数组
     *                * @return true:为空 false:非空
     */
    public static boolean isEmpty(Object[] objects) {
        return ArrayUtil.isEmpty(objects);
    }
    /**
     * * åˆ¤æ–­ä¸€ä¸ªå¯¹è±¡æ•°ç»„是否非空
     *
     * @param objects è¦åˆ¤æ–­çš„对象数组
     * @return true:非空 false:空
     */
    public static boolean isNotEmpty(Object[] objects) {
        return !isEmpty(objects);
    }
    /**
     * * åˆ¤æ–­ä¸€ä¸ªå¯¹è±¡æ˜¯å¦ä¸ºç©º
     *
     * @param object è¦åˆ¤æ–­çš„对象数组
     *                * @return true:为空 false:非空
     */
    public static boolean isEmpty(Object object) {
        return ObjectUtil.isEmpty(object);
    }
    /**
     * * åˆ¤æ–­ä¸€ä¸ªå¯¹è±¡æ˜¯å¦éžç©º
     *
     * @param object è¦åˆ¤æ–­çš„对象数组
     * @return true:非空 false:空
     */
    public static boolean isNotEmpty(Object object) {
        return !isEmpty(object);
    }
    /**
     * * åˆ¤æ–­ä¸€ä¸ªMap是否为空
     *
     * @param map è¦åˆ¤æ–­çš„Map
     * @return true:为空 false:非空
     */
    public static boolean isEmpty(Map<?, ?> map) {
        return MapUtil.isEmpty(map);
    }
    /**
     * * åˆ¤æ–­ä¸€ä¸ªMap是否为空
     *
     * @param map è¦åˆ¤æ–­çš„Map
     * @return true:非空 false:空
     */
    public static boolean isNotEmpty(Map<?, ?> map) {
        return !isEmpty(map);
    }
    /**
     * * åˆ¤æ–­ä¸€ä¸ªå­—符串是否为空串
     *
     * @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);
    }
    /**
     * * åˆ¤æ–­ä¸€ä¸ªå¯¹è±¡æ˜¯å¦ä¸ºç©º
     *
     * @param object Object
     * @return true:为空 false:非空
     */
    public static boolean isNull(Object object) {
        return ObjectUtil.isNull(object);
    }
    /**
     * * åˆ¤æ–­ä¸€ä¸ªå¯¹è±¡æ˜¯å¦éžç©º
     *
     * @param object Object
     * @return true:非空 false:空
     */
    public static boolean isNotNull(Object object) {
        return !isNull(object);
    }
    /**
     * * åˆ¤æ–­ä¸€ä¸ªå¯¹è±¡æ˜¯å¦æ˜¯æ•°ç»„类型(Java基本型别的数组)
     *
     * @param object å¯¹è±¡
     * @return true:是数组 false:不是数组
     */
    public static boolean isArray(Object object) {
        return ArrayUtil.isArray(object);
    }
    /**
     * åŽ»ç©ºæ ¼
     */
    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) || isEmpty(strs)) {
            return false;
        }
        for (String pattern : strs) {
            if (isMatch(pattern, str)) {
                return true;
            }
        }
        return false;
    }
    /**
     * åˆ¤æ–­url是否与规则配置:
     * ? è¡¨ç¤ºå•个字符;
     * * è¡¨ç¤ºä¸€å±‚路径内的任意字符串,不可跨层级;
     * ** è¡¨ç¤ºä»»æ„å±‚路径;
     *
     * @param pattern åŒ¹é…è§„则
     * @param url     éœ€è¦åŒ¹é…çš„url
     * @return
     */
    public static boolean isMatch(String pattern, String url) {
        return ReUtil.isMatch(pattern, url);
    }
    @SuppressWarnings("unchecked")
    public static <T> T cast(Object obj) {
        return (T) obj;
    }
}
ruoyi-common/src/main/java/com/ruoyi/common/utils/file/FileTypeUtils.java
ÎļþÒÑɾ³ý
ruoyi-common/src/main/java/com/ruoyi/common/utils/file/FileUploadUtils.java
ÎļþÒÑɾ³ý
ruoyi-common/src/main/java/com/ruoyi/common/utils/file/FileUtils.java
@@ -1,12 +1,9 @@
package com.ruoyi.common.utils.file;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.StrUtil;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
@@ -17,77 +14,6 @@
 */
public class FileUtils extends FileUtil
{
    public static String FILENAME_PATTERN = "[a-zA-Z0-9_\\-\\|\\.\\u4e00-\\u9fa5]+";
    /**
     * æ–‡ä»¶åç§°éªŒè¯
     *
     * @param filename æ–‡ä»¶åç§°
     * @return true æ­£å¸¸ false éžæ³•
     */
    public static boolean isValidFilename(String filename)
    {
        return filename.matches(FILENAME_PATTERN);
    }
    /**
     * æ£€æŸ¥æ–‡ä»¶æ˜¯å¦å¯ä¸‹è½½
     *
     * @param resource éœ€è¦ä¸‹è½½çš„æ–‡ä»¶
     * @return true æ­£å¸¸ false éžæ³•
     */
    public static boolean checkAllowDownload(String resource)
    {
        // ç¦æ­¢ç›®å½•上跳级别
        if (StrUtil.contains(resource, ".."))
        {
            return false;
        }
        // æ£€æŸ¥å…è®¸ä¸‹è½½çš„æ–‡ä»¶è§„则
        if (ArrayUtil.contains(MimeTypeUtils.DEFAULT_ALLOWED_EXTENSION, FileTypeUtils.getFileType(resource)))
        {
            return true;
        }
        // ä¸åœ¨å…è®¸ä¸‹è½½çš„æ–‡ä»¶è§„则
        return false;
    }
    /**
     * ä¸‹è½½æ–‡ä»¶åé‡æ–°ç¼–码
     *
     * @param request è¯·æ±‚对象
     * @param fileName æ–‡ä»¶å
     * @return ç¼–码后的文件名
     */
    public static String setFileDownloadHeader(HttpServletRequest request, String fileName) throws UnsupportedEncodingException
    {
        final String agent = request.getHeader("USER-AGENT");
        String filename = fileName;
        if (agent.contains("MSIE"))
        {
            // IE浏览器
            filename = URLEncoder.encode(filename, StandardCharsets.UTF_8.toString());
            filename = filename.replace("+", " ");
        }
        else if (agent.contains("Firefox"))
        {
            // ç«ç‹æµè§ˆå™¨
            filename = new String(fileName.getBytes(), "ISO8859-1");
        }
        else if (agent.contains("Chrome"))
        {
            // google浏览器
            filename = URLEncoder.encode(filename, StandardCharsets.UTF_8.toString());
        }
        else
        {
            // å…¶å®ƒæµè§ˆå™¨
            filename = URLEncoder.encode(filename, StandardCharsets.UTF_8.toString());
        }
        return filename;
    }
    /**
     * ä¸‹è½½æ–‡ä»¶åé‡æ–°ç¼–码
ruoyi-common/src/main/java/com/ruoyi/common/utils/file/ImageUtils.java
ÎļþÒÑɾ³ý
ruoyi-common/src/main/java/com/ruoyi/common/utils/file/MimeTypeUtils.java
ÎļþÒÑɾ³ý
ruoyi-common/src/main/java/com/ruoyi/common/utils/ip/AddressUtils.java
@@ -1,12 +1,12 @@
package com.ruoyi.common.utils.ip;
import cn.hutool.core.net.NetUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HtmlUtil;
import cn.hutool.http.HttpUtil;
import com.ruoyi.common.config.RuoYiConfig;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.utils.JsonUtils;
import com.ruoyi.common.utils.StringUtils;
import lombok.extern.slf4j.Slf4j;
import java.util.Map;
@@ -38,7 +38,7 @@
                    .body("ip=" + ip + "&json=true", Constants.GBK)
                    .execute()
                    .body();
                if (StrUtil.isEmpty(rspStr)) {
                if (StringUtils.isEmpty(rspStr)) {
                    log.error("获取地理位置异常 {}", ip);
                    return UNKNOWN;
                }
ruoyi-common/src/main/java/com/ruoyi/common/utils/poi/ExcelUtil.java
@@ -1,1072 +1,150 @@
package com.ruoyi.common.utils.poi;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.lang.Validator;
import cn.hutool.core.util.StrUtil;
import com.ruoyi.common.annotation.Excel;
import com.ruoyi.common.annotation.Excel.ColumnType;
import com.ruoyi.common.annotation.Excel.Type;
import com.ruoyi.common.annotation.Excels;
import com.ruoyi.common.config.RuoYiConfig;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.exception.CustomException;
import com.ruoyi.common.utils.DateUtils;
import cn.hutool.core.util.IdUtil;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.write.style.column.LongestMatchColumnWidthStyleStrategy;
import com.ruoyi.common.utils.DictUtils;
import com.ruoyi.common.utils.file.FileTypeUtils;
import com.ruoyi.common.utils.file.ImageUtils;
import com.ruoyi.common.utils.reflect.ReflectUtils;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.CellRangeAddressList;
import org.apache.poi.xssf.streaming.SXSSFWorkbook;
import org.apache.poi.xssf.usermodel.XSSFClientAnchor;
import org.apache.poi.xssf.usermodel.XSSFDataValidation;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.file.FileUtils;
import java.io.*;
import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.text.DecimalFormat;
import java.util.*;
import java.util.stream.Collectors;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.List;
/**
 * Excel相关处理
 *
 * @author ruoyi
 */
public class ExcelUtil<T>
{
    private static final Logger log = LoggerFactory.getLogger(ExcelUtil.class);
public class ExcelUtil {
    /**
     * Excel sheet最大行数,默认65536
     */
    public static final int sheetSize = 65536;
    /**
     * å¯¹excel表单默认第一个索引名转换成list(EasyExcel)
     *
     * @param is è¾“入流
     * @return è½¬æ¢åŽé›†åˆ
     */
    public static <T> List<T> importExcel(InputStream is, Class<T> clazz) {
        return EasyExcel.read(is).autoCloseStream(false).sheet().doReadSync();
    }
    /**
     * å·¥ä½œè¡¨åç§°
     */
    private String sheetName;
    /**
     * å¯¹list数据源将其里面的数据导入到excel表单(EasyExcel)
     *
     * @param list      å¯¼å‡ºæ•°æ®é›†åˆ
     * @param sheetName å·¥ä½œè¡¨çš„名称
     * @return ç»“æžœ
     */
    public static <T> void exportExcel(List<T> list, String sheetName, Class<T> clazz, HttpServletResponse response) {
        try {
            String filename = encodingFilename(sheetName);
            response.reset();
            response.addHeader("Access-Control-Allow-Origin", "*");
            response.addHeader("Access-Control-Expose-Headers", "Content-Disposition");
            FileUtils.setAttachmentResponseHeader(response, URLEncoder.encode(filename, StandardCharsets.UTF_8.toString()));
            response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=UTF-8");
            ServletOutputStream os = response.getOutputStream();
            EasyExcel.write(os, clazz)
                .autoCloseStream(false)
                // è‡ªåŠ¨é€‚é…
                .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy())
                .sheet(sheetName).doWrite(list);
        } catch (IOException e) {
            throw new RuntimeException("导出Excel异常");
        }
    }
    /**
     * å¯¼å‡ºç±»åž‹ï¼ˆEXPORT:导出数据;IMPORT:导入模板)
     */
    private Type type;
    /**
     * è§£æžå¯¼å‡ºå€¼ 0=男,1=女,2=未知
     *
     * @param propertyValue å‚数值
     * @param converterExp  ç¿»è¯‘注解
     * @param separator     åˆ†éš”符
     * @return è§£æžåŽå€¼
     */
    public static String convertByExp(String propertyValue, String converterExp, String separator) {
        StringBuilder propertyString = new StringBuilder();
        String[] convertSource = converterExp.split(",");
        for (String item : convertSource) {
            String[] itemArray = item.split("=");
            if (StringUtils.containsAny(separator, propertyValue)) {
                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);
    }
    /**
     * å·¥ä½œè–„对象
     */
    private Workbook wb;
    /**
     * åå‘解析值 ç”·=0,女=1,未知=2
     *
     * @param propertyValue å‚数值
     * @param converterExp  ç¿»è¯‘注解
     * @param separator     åˆ†éš”符
     * @return è§£æžåŽå€¼
     */
    public static String reverseByExp(String propertyValue, String converterExp, String separator) {
        StringBuilder propertyString = new StringBuilder();
        String[] convertSource = converterExp.split(",");
        for (String item : convertSource) {
            String[] itemArray = item.split("=");
            if (StringUtils.containsAny(separator, propertyValue)) {
                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);
    }
    /**
     * å·¥ä½œè¡¨å¯¹è±¡
     */
    private Sheet sheet;
    /**
     * è§£æžå­—典值
     *
     * @param dictValue å­—典值
     * @param dictType  å­—典类型
     * @param separator åˆ†éš”符
     * @return å­—典标签
     */
    public static String convertDictByExp(String dictValue, String dictType, String separator) {
        return DictUtils.getDictLabel(dictType, dictValue, separator);
    }
    /**
     * æ ·å¼åˆ—表
     */
    private Map<String, CellStyle> styles;
    /**
     * åå‘解析值字典值
     *
     * @param dictLabel å­—典标签
     * @param dictType  å­—典类型
     * @param separator åˆ†éš”符
     * @return å­—典值
     */
    public static String reverseDictByExp(String dictLabel, String dictType, String separator) {
        return DictUtils.getDictValue(dictType, dictLabel, separator);
    }
    /**
     * å¯¼å…¥å¯¼å‡ºæ•°æ®åˆ—表
     */
    private List<T> list;
    /**
     * ç¼–码文件名
     */
    public static String encodingFilename(String filename) {
        return IdUtil.fastSimpleUUID() + "_" + filename + ".xlsx";
    }
    /**
     * æ³¨è§£åˆ—表
     */
    private List<Object[]> fields;
    /**
     * æœ€å¤§é«˜åº¦
     */
    private short maxHeight;
    /**
     * ç»Ÿè®¡åˆ—表
     */
    private Map<Integer, Double> statistics = new HashMap<Integer, Double>();
    /**
     * æ•°å­—格式
     */
    private static final DecimalFormat DOUBLE_FORMAT = new DecimalFormat("######0.00");
    /**
     * å®žä½“对象
     */
    public Class<T> clazz;
    public ExcelUtil(Class<T> clazz)
    {
        this.clazz = clazz;
    }
    public void init(List<T> list, String sheetName, Type type)
    {
        if (list == null)
        {
            list = new ArrayList<T>();
        }
        this.list = list;
        this.sheetName = sheetName;
        this.type = type;
        createExcelField();
        createWorkbook();
    }
    /**
     * å¯¹excel表单默认第一个索引名转换成list
     *
     * @param is è¾“入流
     * @return è½¬æ¢åŽé›†åˆ
     */
    public List<T> importExcel(InputStream is) throws Exception
    {
        return importExcel(StrUtil.EMPTY, is);
    }
    /**
     * å¯¹excel表单指定表格索引名转换成list
     *
     * @param sheetName è¡¨æ ¼ç´¢å¼•名
     * @param is è¾“入流
     * @return è½¬æ¢åŽé›†åˆ
     */
    public List<T> importExcel(String sheetName, InputStream is) throws Exception
    {
        this.type = Type.IMPORT;
        this.wb = WorkbookFactory.create(is);
        List<T> list = new ArrayList<T>();
        Sheet sheet = null;
        if (Validator.isNotEmpty(sheetName))
        {
            // å¦‚果指定sheet名,则取指定sheet中的内容.
            sheet = wb.getSheet(sheetName);
        }
        else
        {
            // å¦‚果传入的sheet名不存在则默认指向第1个sheet.
            sheet = wb.getSheetAt(0);
        }
        if (sheet == null)
        {
            throw new IOException("文件sheet不存在");
        }
        int rows = sheet.getPhysicalNumberOfRows();
        if (rows > 0)
        {
            // å®šä¹‰ä¸€ä¸ªmap用于存放excel列的序号和field.
            Map<String, Integer> cellMap = new HashMap<String, Integer>();
            // èŽ·å–è¡¨å¤´
            Row heard = sheet.getRow(0);
            for (int i = 0; i < heard.getPhysicalNumberOfCells(); i++)
            {
                Cell cell = heard.getCell(i);
                if (Validator.isNotNull(cell))
                {
                    String value = this.getCellValue(heard, i).toString();
                    cellMap.put(value, i);
                }
                else
                {
                    cellMap.put(null, i);
                }
            }
            // æœ‰æ•°æ®æ—¶æ‰å¤„理 å¾—到类的所有field.
            Field[] allFields = clazz.getDeclaredFields();
            // å®šä¹‰ä¸€ä¸ªmap用于存放列的序号和field.
            Map<Integer, Field> fieldsMap = new HashMap<Integer, Field>();
            for (int col = 0; col < allFields.length; col++)
            {
                Field field = allFields[col];
                Excel attr = field.getAnnotation(Excel.class);
                if (attr != null && (attr.type() == Type.ALL || attr.type() == type))
                {
                    // è®¾ç½®ç±»çš„私有字段属性可访问.
                    field.setAccessible(true);
                    Integer column = cellMap.get(attr.name());
                    if (column != null)
                    {
                        fieldsMap.put(column, field);
                    }
                }
            }
            for (int i = 1; i < rows; i++)
            {
                // ä»Žç¬¬2行开始取数据,默认第一行是表头.
                Row row = sheet.getRow(i);
                if(row == null)
                {
                    continue;
                }
                T entity = null;
                for (Map.Entry<Integer, Field> entry : fieldsMap.entrySet())
                {
                    Object val = this.getCellValue(row, entry.getKey());
                    // å¦‚果不存在实例则新建.
                    entity = (entity == null ? clazz.newInstance() : entity);
                    // ä»Žmap中得到对应列的field.
                    Field field = fieldsMap.get(entry.getKey());
                    // å–得类型,并根据对象类型设置值.
                    Class<?> fieldType = field.getType();
                    if (String.class == fieldType)
                    {
                        String s = Convert.toStr(val);
                        if (StrUtil.endWith(s, ".0"))
                        {
                            val = StrUtil.subBefore(s, ".0",false);
                        }
                        else
                        {
                            String dateFormat = field.getAnnotation(Excel.class).dateFormat();
                            if (Validator.isNotEmpty(dateFormat))
                            {
                                val = DateUtils.parseDateToStr(dateFormat, (Date) val);
                            }
                            else
                            {
                                val = Convert.toStr(val);
                            }
                        }
                    }
                    else if ((Integer.TYPE == fieldType || Integer.class == fieldType) && Validator.isNumber(Convert.toStr(val)))
                    {
                        val = Convert.toInt(val);
                    }
                    else if (Long.TYPE == fieldType || Long.class == fieldType)
                    {
                        val = Convert.toLong(val);
                    }
                    else if (Double.TYPE == fieldType || Double.class == fieldType)
                    {
                        val = Convert.toDouble(val);
                    }
                    else if (Float.TYPE == fieldType || Float.class == fieldType)
                    {
                        val = Convert.toFloat(val);
                    }
                    else if (BigDecimal.class == fieldType)
                    {
                        val = Convert.toBigDecimal(val);
                    }
                    else if (Date.class == fieldType)
                    {
                        if (val instanceof String)
                        {
                            val = DateUtils.parseDate(val);
                        }
                        else if (val instanceof Double)
                        {
                            val = DateUtil.getJavaDate((Double) val);
                        }
                    }
                    else if (Boolean.TYPE == fieldType || Boolean.class == fieldType)
                    {
                        val = Convert.toBool(val, false);
                    }
                    if (Validator.isNotNull(fieldType))
                    {
                        Excel attr = field.getAnnotation(Excel.class);
                        String propertyName = field.getName();
                        if (Validator.isNotEmpty(attr.targetAttr()))
                        {
                            propertyName = field.getName() + "." + attr.targetAttr();
                        }
                        else if (Validator.isNotEmpty(attr.readConverterExp()))
                        {
                            val = reverseByExp(Convert.toStr(val), attr.readConverterExp(), attr.separator());
                        }
                        else if (Validator.isNotEmpty(attr.dictType()))
                        {
                            val = reverseDictByExp(Convert.toStr(val), attr.dictType(), attr.separator());
                        }
                        ReflectUtils.invokeSetter(entity, propertyName, val);
                    }
                }
                list.add(entity);
            }
        }
        return list;
    }
    /**
     * å¯¹list数据源将其里面的数据导入到excel表单
     *
     * @param list å¯¼å‡ºæ•°æ®é›†åˆ
     * @param sheetName å·¥ä½œè¡¨çš„名称
     * @return ç»“æžœ
     */
    public AjaxResult exportExcel(List<T> list, String sheetName)
    {
        this.init(list, sheetName, Type.EXPORT);
        return exportExcel();
    }
    /**
     * å¯¹list数据源将其里面的数据导入到excel表单
     *
     * @param sheetName å·¥ä½œè¡¨çš„名称
     * @return ç»“æžœ
     */
    public AjaxResult importTemplateExcel(String sheetName)
    {
        this.init(null, sheetName, Type.IMPORT);
        return exportExcel();
    }
    /**
     * å¯¹list数据源将其里面的数据导入到excel表单
     *
     * @return ç»“æžœ
     */
    public AjaxResult exportExcel()
    {
        OutputStream out = null;
        try
        {
            // å–出一共有多少个sheet.
            double sheetNo = Math.ceil(list.size() / sheetSize);
            for (int index = 0; index <= sheetNo; index++)
            {
                createSheet(sheetNo, index);
                // äº§ç”Ÿä¸€è¡Œ
                Row row = sheet.createRow(0);
                int column = 0;
                // å†™å…¥å„个字段的列头名称
                for (Object[] os : fields)
                {
                    Excel excel = (Excel) os[1];
                    this.createCell(excel, row, column++);
                }
                if (Type.EXPORT.equals(type))
                {
                    fillExcelData(index, row);
                    addStatisticsRow();
                }
            }
            String filename = encodingFilename(sheetName);
            out = new FileOutputStream(getAbsoluteFile(filename));
            wb.write(out);
            return AjaxResult.success(filename);
        }
        catch (Exception e)
        {
            log.error("导出Excel异常{}", e.getMessage());
            throw new CustomException("导出Excel失败,请联系网站管理员!");
        }
        finally
        {
            if (wb != null)
            {
                try
                {
                    wb.close();
                }
                catch (IOException e1)
                {
                    e1.printStackTrace();
                }
            }
            if (out != null)
            {
                try
                {
                    out.close();
                }
                catch (IOException e1)
                {
                    e1.printStackTrace();
                }
            }
        }
    }
    /**
     * å¡«å……excel数据
     *
     * @param index åºå·
     * @param row å•元格行
     */
    public void fillExcelData(int index, Row row)
    {
        int startNo = index * sheetSize;
        int endNo = Math.min(startNo + sheetSize, list.size());
        for (int i = startNo; i < endNo; i++)
        {
            row = sheet.createRow(i + 1 - startNo);
            // å¾—到导出对象.
            T vo = (T) list.get(i);
            int column = 0;
            for (Object[] os : fields)
            {
                Field field = (Field) os[0];
                Excel excel = (Excel) os[1];
                // è®¾ç½®å®žä½“类私有属性可访问
                field.setAccessible(true);
                this.addCell(excel, row, vo, field, column++);
            }
        }
    }
    /**
     * åˆ›å»ºè¡¨æ ¼æ ·å¼
     *
     * @param wb å·¥ä½œè–„对象
     * @return æ ·å¼åˆ—表
     */
    private Map<String, CellStyle> createStyles(Workbook wb)
    {
        // å†™å…¥å„条记录,每条记录对应excel表中的一行
        Map<String, CellStyle> styles = new HashMap<String, CellStyle>();
        CellStyle style = wb.createCellStyle();
        style.setAlignment(HorizontalAlignment.CENTER);
        style.setVerticalAlignment(VerticalAlignment.CENTER);
        style.setBorderRight(BorderStyle.THIN);
        style.setRightBorderColor(IndexedColors.GREY_50_PERCENT.getIndex());
        style.setBorderLeft(BorderStyle.THIN);
        style.setLeftBorderColor(IndexedColors.GREY_50_PERCENT.getIndex());
        style.setBorderTop(BorderStyle.THIN);
        style.setTopBorderColor(IndexedColors.GREY_50_PERCENT.getIndex());
        style.setBorderBottom(BorderStyle.THIN);
        style.setBottomBorderColor(IndexedColors.GREY_50_PERCENT.getIndex());
        Font dataFont = wb.createFont();
        dataFont.setFontName("Arial");
        dataFont.setFontHeightInPoints((short) 10);
        style.setFont(dataFont);
        styles.put("data", style);
        style = wb.createCellStyle();
        style.cloneStyleFrom(styles.get("data"));
        style.setAlignment(HorizontalAlignment.CENTER);
        style.setVerticalAlignment(VerticalAlignment.CENTER);
        style.setFillForegroundColor(IndexedColors.GREY_50_PERCENT.getIndex());
        style.setFillPattern(FillPatternType.SOLID_FOREGROUND);
        Font headerFont = wb.createFont();
        headerFont.setFontName("Arial");
        headerFont.setFontHeightInPoints((short) 10);
        headerFont.setBold(true);
        headerFont.setColor(IndexedColors.WHITE.getIndex());
        style.setFont(headerFont);
        styles.put("header", style);
        style = wb.createCellStyle();
        style.setAlignment(HorizontalAlignment.CENTER);
        style.setVerticalAlignment(VerticalAlignment.CENTER);
        Font totalFont = wb.createFont();
        totalFont.setFontName("Arial");
        totalFont.setFontHeightInPoints((short) 10);
        style.setFont(totalFont);
        styles.put("total", style);
        style = wb.createCellStyle();
        style.cloneStyleFrom(styles.get("data"));
        style.setAlignment(HorizontalAlignment.LEFT);
        styles.put("data1", style);
        style = wb.createCellStyle();
        style.cloneStyleFrom(styles.get("data"));
        style.setAlignment(HorizontalAlignment.CENTER);
        styles.put("data2", style);
        style = wb.createCellStyle();
        style.cloneStyleFrom(styles.get("data"));
        style.setAlignment(HorizontalAlignment.RIGHT);
        styles.put("data3", style);
        return styles;
    }
    /**
     * åˆ›å»ºå•元格
     */
    public Cell createCell(Excel attr, Row row, int column)
    {
        // åˆ›å»ºåˆ—
        Cell cell = row.createCell(column);
        // å†™å…¥åˆ—信息
        cell.setCellValue(attr.name());
        setDataValidation(attr, row, column);
        cell.setCellStyle(styles.get("header"));
        return cell;
    }
    /**
     * è®¾ç½®å•元格信息
     *
     * @param value å•元格值
     * @param attr æ³¨è§£ç›¸å…³
     * @param cell å•元格信息
     */
    public void setCellVo(Object value, Excel attr, Cell cell)
    {
        if (ColumnType.STRING == attr.cellType())
        {
            cell.setCellValue(Validator.isNull(value) ? attr.defaultValue() : value + attr.suffix());
        }
        else if (ColumnType.NUMERIC == attr.cellType())
        {
            if (Validator.isNotNull(value))
            {
                cell.setCellValue(StrUtil.contains(Convert.toStr(value), ".") ? Convert.toDouble(value) : Convert.toInt(value));
            }
        }
        else if (ColumnType.IMAGE == attr.cellType())
        {
            ClientAnchor anchor = new XSSFClientAnchor(0, 0, 0, 0, (short) cell.getColumnIndex(), cell.getRow().getRowNum(), (short) (cell.getColumnIndex() + 1),
                    cell.getRow().getRowNum() + 1);
            String imagePath = Convert.toStr(value);
            if (Validator.isNotEmpty(imagePath))
            {
                byte[] data = ImageUtils.getImage(imagePath);
                getDrawingPatriarch(cell.getSheet()).createPicture(anchor,
                        cell.getSheet().getWorkbook().addPicture(data, getImageType(data)));
            }
        }
    }
    /**
     * èŽ·å–ç”»å¸ƒ
     */
    public static Drawing<?> getDrawingPatriarch(Sheet sheet)
    {
        if (sheet.getDrawingPatriarch() == null)
        {
            sheet.createDrawingPatriarch();
        }
        return sheet.getDrawingPatriarch();
    }
    /**
     * èŽ·å–å›¾ç‰‡ç±»åž‹,设置图片插入类型
     */
    public int getImageType(byte[] value)
    {
        String type = FileTypeUtils.getFileExtendName(value);
        if ("JPG".equalsIgnoreCase(type))
        {
            return Workbook.PICTURE_TYPE_JPEG;
        }
        else if ("PNG".equalsIgnoreCase(type))
        {
            return Workbook.PICTURE_TYPE_PNG;
        }
        return Workbook.PICTURE_TYPE_JPEG;
    }
    /**
     * åˆ›å»ºè¡¨æ ¼æ ·å¼
     */
    public void setDataValidation(Excel attr, Row row, int column)
    {
        if (attr.name().indexOf("注:") >= 0)
        {
            sheet.setColumnWidth(column, 6000);
        }
        else
        {
            // è®¾ç½®åˆ—宽
            sheet.setColumnWidth(column, (int) ((attr.width() + 0.72) * 256));
        }
        // å¦‚果设置了提示信息则鼠标放上去提示.
        if (Validator.isNotEmpty(attr.prompt()))
        {
            // è¿™é‡Œé»˜è®¤è®¾äº†2-101列提示.
            setXSSFPrompt(sheet, "", attr.prompt(), 1, 100, column, column);
        }
        // å¦‚果设置了combo属性则本列只能选择不能输入
        if (attr.combo().length > 0)
        {
            // è¿™é‡Œé»˜è®¤è®¾äº†2-101列只能选择不能输入.
            setXSSFValidation(sheet, attr.combo(), 1, 100, column, column);
        }
    }
    /**
     * æ·»åŠ å•å…ƒæ ¼
     */
    public Cell addCell(Excel attr, Row row, T vo, Field field, int column)
    {
        Cell cell = null;
        try
        {
            // è®¾ç½®è¡Œé«˜
            row.setHeight(maxHeight);
            // æ ¹æ®Excel中设置情况决定是否导出,有些情况需要保持为空,希望用户填写这一列.
            if (attr.isExport())
            {
                // åˆ›å»ºcell
                cell = row.createCell(column);
                int align = attr.align().value();
                cell.setCellStyle(styles.get("data" + (align >= 1 && align <= 3 ? align : "")));
                // ç”¨äºŽè¯»å–对象中的属性
                Object value = getTargetValue(vo, field, attr);
                String dateFormat = attr.dateFormat();
                String readConverterExp = attr.readConverterExp();
                String separator = attr.separator();
                String dictType = attr.dictType();
                if (Validator.isNotEmpty(dateFormat) && Validator.isNotNull(value))
                {
                    cell.setCellValue(DateUtils.parseDateToStr(dateFormat, (Date) value));
                }
                else if (Validator.isNotEmpty(readConverterExp) && Validator.isNotNull(value))
                {
                    cell.setCellValue(convertByExp(Convert.toStr(value), readConverterExp, separator));
                }
                else if (Validator.isNotEmpty(dictType) && Validator.isNotNull(value))
                {
                    cell.setCellValue(convertDictByExp(Convert.toStr(value), dictType, separator));
                }
                else if (value instanceof BigDecimal && -1 != attr.scale())
                {
                    cell.setCellValue((((BigDecimal) value).setScale(attr.scale(), attr.roundingMode())).toString());
                }
                else
                {
                    // è®¾ç½®åˆ—类型
                    setCellVo(value, attr, cell);
                }
                addStatisticsData(column, Convert.toStr(value), attr);
            }
        }
        catch (Exception e)
        {
            log.error("导出Excel失败{}", e);
        }
        return cell;
    }
    /**
     * è®¾ç½® POI XSSFSheet å•元格提示
     *
     * @param sheet è¡¨å•
     * @param promptTitle æç¤ºæ ‡é¢˜
     * @param promptContent æç¤ºå†…容
     * @param firstRow å¼€å§‹è¡Œ
     * @param endRow ç»“束行
     * @param firstCol å¼€å§‹åˆ—
     * @param endCol ç»“束列
     */
    public void setXSSFPrompt(Sheet sheet, String promptTitle, String promptContent, int firstRow, int endRow,
            int firstCol, int endCol)
    {
        DataValidationHelper helper = sheet.getDataValidationHelper();
        DataValidationConstraint constraint = helper.createCustomConstraint("DD1");
        CellRangeAddressList regions = new CellRangeAddressList(firstRow, endRow, firstCol, endCol);
        DataValidation dataValidation = helper.createValidation(constraint, regions);
        dataValidation.createPromptBox(promptTitle, promptContent);
        dataValidation.setShowPromptBox(true);
        sheet.addValidationData(dataValidation);
    }
    /**
     * è®¾ç½®æŸäº›åˆ—的值只能输入预制的数据,显示下拉框.
     *
     * @param sheet è¦è®¾ç½®çš„sheet.
     * @param textlist ä¸‹æ‹‰æ¡†æ˜¾ç¤ºçš„内容
     * @param firstRow å¼€å§‹è¡Œ
     * @param endRow ç»“束行
     * @param firstCol å¼€å§‹åˆ—
     * @param endCol ç»“束列
     * @return è®¾ç½®å¥½çš„sheet.
     */
    public void setXSSFValidation(Sheet sheet, String[] textlist, int firstRow, int endRow, int firstCol, int endCol)
    {
        DataValidationHelper helper = sheet.getDataValidationHelper();
        // åŠ è½½ä¸‹æ‹‰åˆ—è¡¨å†…å®¹
        DataValidationConstraint constraint = helper.createExplicitListConstraint(textlist);
        // è®¾ç½®æ•°æ®æœ‰æ•ˆæ€§åŠ è½½åœ¨å“ªä¸ªå•å…ƒæ ¼ä¸Š,四个参数分别是:起始行、终止行、起始列、终止列
        CellRangeAddressList regions = new CellRangeAddressList(firstRow, endRow, firstCol, endCol);
        // æ•°æ®æœ‰æ•ˆæ€§å¯¹è±¡
        DataValidation dataValidation = helper.createValidation(constraint, regions);
        // å¤„理Excel兼容性问题
        if (dataValidation instanceof XSSFDataValidation)
        {
            dataValidation.setSuppressDropDownArrow(true);
            dataValidation.setShowErrorBox(true);
        }
        else
        {
            dataValidation.setSuppressDropDownArrow(false);
        }
        sheet.addValidationData(dataValidation);
    }
    /**
     * è§£æžå¯¼å‡ºå€¼ 0=男,1=女,2=未知
     *
     * @param propertyValue å‚数值
     * @param converterExp ç¿»è¯‘注解
     * @param separator åˆ†éš”符
     * @return è§£æžåŽå€¼
     */
    public static String convertByExp(String propertyValue, String converterExp, String separator)
    {
        StringBuilder propertyString = new StringBuilder();
        String[] convertSource = converterExp.split(",");
        for (String item : convertSource)
        {
            String[] itemArray = item.split("=");
            if (StrUtil.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 StrUtil.strip(propertyString.toString(), null,separator);
    }
    /**
     * åå‘解析值 ç”·=0,女=1,未知=2
     *
     * @param propertyValue å‚数值
     * @param converterExp ç¿»è¯‘注解
     * @param separator åˆ†éš”符
     * @return è§£æžåŽå€¼
     */
    public static String reverseByExp(String propertyValue, String converterExp, String separator)
    {
        StringBuilder propertyString = new StringBuilder();
        String[] convertSource = converterExp.split(",");
        for (String item : convertSource)
        {
            String[] itemArray = item.split("=");
            if (StrUtil.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 StrUtil.strip(propertyString.toString(), null,separator);
    }
    /**
     * è§£æžå­—典值
     *
     * @param dictValue å­—典值
     * @param dictType å­—典类型
     * @param separator åˆ†éš”符
     * @return å­—典标签
     */
    public static String convertDictByExp(String dictValue, String dictType, String separator)
    {
        return DictUtils.getDictLabel(dictType, dictValue, separator);
    }
    /**
     * åå‘解析值字典值
     *
     * @param dictLabel å­—典标签
     * @param dictType å­—典类型
     * @param separator åˆ†éš”符
     * @return å­—典值
     */
    public static String reverseDictByExp(String dictLabel, String dictType, String separator)
    {
        return DictUtils.getDictValue(dictType, dictLabel, separator);
    }
    /**
     * åˆè®¡ç»Ÿè®¡ä¿¡æ¯
     */
    private void addStatisticsData(Integer index, String text, Excel entity)
    {
        if (entity != null && entity.isStatistics())
        {
            Double temp = 0D;
            if (!statistics.containsKey(index))
            {
                statistics.put(index, temp);
            }
            try
            {
                temp = Double.valueOf(text);
            }
            catch (NumberFormatException e)
            {
            }
            statistics.put(index, statistics.get(index) + temp);
        }
    }
    /**
     * åˆ›å»ºç»Ÿè®¡è¡Œ
     */
    public void addStatisticsRow()
    {
        if (statistics.size() > 0)
        {
            Cell cell = null;
            Row row = sheet.createRow(sheet.getLastRowNum() + 1);
            Set<Integer> keys = statistics.keySet();
            cell = row.createCell(0);
            cell.setCellStyle(styles.get("total"));
            cell.setCellValue("合计");
            for (Integer key : keys)
            {
                cell = row.createCell(key);
                cell.setCellStyle(styles.get("total"));
                cell.setCellValue(DOUBLE_FORMAT.format(statistics.get(key)));
            }
            statistics.clear();
        }
    }
    /**
     * ç¼–码文件名
     */
    public String encodingFilename(String filename)
    {
        filename = UUID.randomUUID().toString() + "_" + filename + ".xlsx";
        return filename;
    }
    /**
     * èŽ·å–ä¸‹è½½è·¯å¾„
     *
     * @param filename æ–‡ä»¶åç§°
     */
    public String getAbsoluteFile(String filename)
    {
        String downloadPath = RuoYiConfig.getDownloadPath() + filename;
        File desc = new File(downloadPath);
        if (!desc.getParentFile().exists())
        {
            desc.getParentFile().mkdirs();
        }
        return downloadPath;
    }
    /**
     * èŽ·å–bean中的属性值
     *
     * @param vo å®žä½“对象
     * @param field å­—段
     * @param excel æ³¨è§£
     * @return æœ€ç»ˆçš„属性值
     * @throws Exception
     */
    private Object getTargetValue(T vo, Field field, Excel excel) throws Exception
    {
        Object o = field.get(vo);
        if (Validator.isNotEmpty(excel.targetAttr()))
        {
            String target = excel.targetAttr();
            if (target.contains("."))
            {
                String[] targets = target.split("[.]");
                for (String name : targets)
                {
                    o = getValue(o, name);
                }
            }
            else
            {
                o = getValue(o, target);
            }
        }
        return o;
    }
    /**
     * ä»¥ç±»çš„属性的get方法方法形式获取值
     *
     * @param o
     * @param name
     * @return value
     * @throws Exception
     */
    private Object getValue(Object o, String name) throws Exception
    {
        if (Validator.isNotNull(o) && Validator.isNotEmpty(name))
        {
            Class<?> clazz = o.getClass();
            Field field = clazz.getDeclaredField(name);
            field.setAccessible(true);
            o = field.get(o);
        }
        return o;
    }
    /**
     * å¾—到所有定义字段
     */
    private void createExcelField()
    {
        this.fields = new ArrayList<Object[]>();
        List<Field> tempFields = new ArrayList<>();
        tempFields.addAll(Arrays.asList(clazz.getSuperclass().getDeclaredFields()));
        tempFields.addAll(Arrays.asList(clazz.getDeclaredFields()));
        for (Field field : tempFields)
        {
            // å•注解
            if (field.isAnnotationPresent(Excel.class))
            {
                putToField(field, field.getAnnotation(Excel.class));
            }
            // å¤šæ³¨è§£
            if (field.isAnnotationPresent(Excels.class))
            {
                Excels attrs = field.getAnnotation(Excels.class);
                Excel[] excels = attrs.value();
                for (Excel excel : excels)
                {
                    putToField(field, excel);
                }
            }
        }
        this.fields = this.fields.stream().sorted(Comparator.comparing(objects -> ((Excel) objects[1]).sort())).collect(Collectors.toList());
        this.maxHeight = getRowHeight();
    }
    /**
     * æ ¹æ®æ³¨è§£èŽ·å–æœ€å¤§è¡Œé«˜
     */
    public short getRowHeight()
    {
        double maxHeight = 0;
        for (Object[] os : this.fields)
        {
            Excel excel = (Excel) os[1];
            maxHeight = maxHeight > excel.height() ? maxHeight : excel.height();
        }
        return (short) (maxHeight * 20);
    }
    /**
     * æ”¾åˆ°å­—段集合中
     */
    private void putToField(Field field, Excel attr)
    {
        if (attr != null && (attr.type() == Type.ALL || attr.type() == type))
        {
            this.fields.add(new Object[] { field, attr });
        }
    }
    /**
     * åˆ›å»ºä¸€ä¸ªå·¥ä½œç°¿
     */
    public void createWorkbook()
    {
        this.wb = new SXSSFWorkbook(500);
    }
    /**
     * åˆ›å»ºå·¥ä½œè¡¨
     *
     * @param sheetNo sheet数量
     * @param index åºå·
     */
    public void createSheet(double sheetNo, int index)
    {
        this.sheet = wb.createSheet();
        this.styles = createStyles(wb);
        // è®¾ç½®å·¥ä½œè¡¨çš„名称.
        if (sheetNo == 0)
        {
            wb.setSheetName(index, sheetName);
        }
        else
        {
            wb.setSheetName(index, sheetName + index);
        }
    }
    /**
     * èŽ·å–å•å…ƒæ ¼å€¼
     *
     * @param row èŽ·å–çš„è¡Œ
     * @param column èŽ·å–å•å…ƒæ ¼åˆ—å·
     * @return å•元格值
     */
    public Object getCellValue(Row row, int column)
    {
        if (row == null)
        {
            return row;
        }
        Object val = "";
        try
        {
            Cell cell = row.getCell(column);
            if (Validator.isNotNull(cell))
            {
                if (cell.getCellType() == CellType.NUMERIC || cell.getCellType() == CellType.FORMULA)
                {
                    val = cell.getNumericCellValue();
                    if (DateUtil.isCellDateFormatted(cell))
                    {
                        val = DateUtil.getJavaDate((Double) val); // POI Excel æ—¥æœŸæ ¼å¼è½¬æ¢
                    }
                    else
                    {
                        if ((Double) val % 1 != 0)
                        {
                            val = new BigDecimal(val.toString());
                        }
                        else
                        {
                            val = new DecimalFormat("0").format(val);
                        }
                    }
                }
                else if (cell.getCellType() == CellType.STRING)
                {
                    val = cell.getStringCellValue();
                }
                else if (cell.getCellType() == CellType.BOOLEAN)
                {
                    val = cell.getBooleanCellValue();
                }
                else if (cell.getCellType() == CellType.ERROR)
                {
                    val = cell.getErrorCellValue();
                }
            }
        }
        catch (Exception e)
        {
            return val;
        }
        return val;
    }
}
ruoyi-common/src/main/java/com/ruoyi/common/utils/reflect/ReflectUtils.java
@@ -1,10 +1,9 @@
package com.ruoyi.common.utils.reflect;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import com.ruoyi.common.utils.StringUtils;
import java.lang.reflect.Method;
import java.util.List;
/**
 * åå°„工具类. æä¾›è°ƒç”¨getter/setter方法, è®¿é—®ç§æœ‰å˜é‡, è°ƒç”¨ç§æœ‰æ–¹æ³•, èŽ·å–æ³›åž‹ç±»åž‹Class, è¢«AOP过的真实类等工具函数.
@@ -25,8 +24,8 @@
    @SuppressWarnings("unchecked")
    public static <E> E invokeGetter(Object obj, String propertyName) {
        Object object = obj;
        for (String name : StrUtil.split(propertyName, ".")) {
            String getterMethodName = GETTER_PREFIX + StrUtil.upperFirst(name);
        for (String name : StringUtils.split(propertyName, ".")) {
            String getterMethodName = GETTER_PREFIX + StringUtils.capitalize(name);
            object = invoke(object, getterMethodName);
        }
        return (E) object;
@@ -38,13 +37,13 @@
     */
    public static <E> void invokeSetter(Object obj, String propertyName, E value) {
        Object object = obj;
        List<String> names = StrUtil.split(propertyName, ".");
        for (int i = 0; i < names.size(); i++) {
            if (i < names.size() - 1) {
                String getterMethodName = GETTER_PREFIX + StrUtil.upperFirst(names.get(i));
        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 + StrUtil.upperFirst(names.get(i));
                String setterMethodName = SETTER_PREFIX + StringUtils.capitalize(names[i]);
                Method method = getMethodByName(object.getClass(), setterMethodName);
                invoke(object, method, value);
            }
ruoyi-common/src/main/java/com/ruoyi/common/utils/sql/SqlUtil.java
@@ -1,11 +1,11 @@
package com.ruoyi.common.utils.sql;
import cn.hutool.core.lang.Validator;
import com.ruoyi.common.exception.BaseException;
import com.ruoyi.common.exception.UtilException;
import com.ruoyi.common.utils.StringUtils;
/**
 * sql操作工具类
 *
 *
 * @author ruoyi
 */
public class SqlUtil
@@ -20,9 +20,9 @@
     */
    public static String escapeOrderBySql(String value)
    {
        if (Validator.isNotEmpty(value) && !isValidOrderBySql(value))
        if (StringUtils.isNotEmpty(value) && !isValidOrderBySql(value))
        {
            throw new BaseException("参数不符合规范,不能进行查询");
            throw new UtilException("参数不符合规范,不能进行查询");
        }
        return value;
    }
ruoyi-demo/pom.xml
@@ -5,7 +5,7 @@
    <parent>
        <artifactId>ruoyi-vue-plus</artifactId>
        <groupId>com.ruoyi</groupId>
        <version>2.6.0</version>
        <version>3.0.0</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>
ruoyi-demo/src/main/java/com/ruoyi/demo/controller/TestBatchController.java
@@ -5,6 +5,8 @@
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.demo.domain.TestDemo;
import com.ruoyi.demo.service.ITestDemoService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.DeleteMapping;
@@ -21,6 +23,7 @@
 * @author Lion Li
 * @date 2021-05-30
 */
@Api(value = "测试批量方法", tags = {"测试批量方法"})
@RequiredArgsConstructor(onConstructor_ = @Autowired)
@RestController
@RequestMapping("/demo/batch")
@@ -31,7 +34,9 @@
    /**
     * æ–°å¢žæ‰¹é‡æ–¹æ³• ( å…¨é‡è¦†ç›–å¡«å…… )
     */
    @ApiOperation(value = "新增批量方法")
    @PostMapping()
//    @DataSource(DataSourceType.SLAVE)
    public AjaxResult<Void> add() {
        List<TestDemo> list = new ArrayList<>();
        for (int i = 0; i < 1000; i++) {
@@ -41,10 +46,12 @@
    }
    /**
     * ä¿®æ”¹æ‰¹é‡æ–¹æ³•
     * åˆ é™¤æ‰¹é‡æ–¹æ³•
     */
    @ApiOperation(value = "删除批量方法")
    @DeleteMapping()
    public AjaxResult<Void> edit() {
//    @DataSource(DataSourceType.SLAVE)
    public AjaxResult<Void> remove() {
        return toAjax(iTestDemoService.remove(new LambdaQueryWrapper<TestDemo>()
            .eq(TestDemo::getOrderNum, -1L)) ? 1 : 0);
    }
ruoyi-demo/src/main/java/com/ruoyi/demo/controller/TestDemoController.java
@@ -20,10 +20,12 @@
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletResponse;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.TimeUnit;
/**
 * æµ‹è¯•单表Controller
@@ -67,10 +69,9 @@
    @PreAuthorize("@ss.hasPermi('demo:demo:export')")
    @Log(title = "测试单表", businessType = BusinessType.EXPORT)
    @GetMapping("/export")
    public AjaxResult<TestDemoVo> export(@Validated TestDemoBo bo) {
    public void export(@Validated TestDemoBo bo, HttpServletResponse response) {
        List<TestDemoVo> list = iTestDemoService.queryList(bo);
        ExcelUtil<TestDemoVo> util = new ExcelUtil<TestDemoVo>(TestDemoVo.class);
        return util.exportExcel(list, "测试单表");
        ExcelUtil.exportExcel(list, "测试单表", TestDemoVo.class, response);
    }
    /**
@@ -90,7 +91,7 @@
    @ApiOperation("新增测试单表")
    @PreAuthorize("@ss.hasPermi('demo:demo:add')")
    @Log(title = "测试单表", businessType = BusinessType.INSERT)
    @RepeatSubmit
    @RepeatSubmit(intervalTime = 2, timeUnit = TimeUnit.SECONDS)
    @PostMapping()
    public AjaxResult<Void> add(@Validated(AddGroup.class) @RequestBody TestDemoBo bo) {
        return toAjax(iTestDemoService.insertByBo(bo) ? 1 : 0);
ruoyi-demo/src/main/java/com/ruoyi/demo/controller/TestI18nController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,29 @@
package com.ruoyi.demo.controller;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.utils.MessageUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
 * æµ‹è¯•国际化
 *
 * @author Lion Li
 */
@RestController
@RequestMapping("/demo/i18n")
public class TestI18nController {
    /**
     * é€šè¿‡code获取国际化内容
     * code为 messages.properties ä¸­çš„ key
     *
     * æµ‹è¯•使用 user.register.success
     */
    @GetMapping()
    public AjaxResult<Void> get(String code) {
        return AjaxResult.success(MessageUtils.message(code));
    }
}
ruoyi-demo/src/main/java/com/ruoyi/demo/controller/TestTreeController.java
@@ -19,6 +19,7 @@
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletResponse;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import java.util.Arrays;
@@ -57,10 +58,9 @@
    @PreAuthorize("@ss.hasPermi('demo:tree:export')")
    @Log(title = "测试树表", businessType = BusinessType.EXPORT)
    @GetMapping("/export")
    public AjaxResult<TestTreeVo> export(@Validated TestTreeBo bo) {
    public void export(@Validated TestTreeBo bo, HttpServletResponse response) {
        List<TestTreeVo> list = iTestTreeService.queryList(bo);
        ExcelUtil<TestTreeVo> util = new ExcelUtil<TestTreeVo>(TestTreeVo.class);
        return util.exportExcel(list, "测试树表");
        ExcelUtil.exportExcel(list, "测试树表", TestTreeVo.class, response);
    }
    /**
ruoyi-demo/src/main/java/com/ruoyi/demo/domain/vo/TestDemoVo.java
@@ -1,9 +1,11 @@
package com.ruoyi.demo.domain.vo;
import com.ruoyi.common.annotation.Excel;
import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
import com.alibaba.excel.annotation.ExcelProperty;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.util.Date;
@@ -16,6 +18,7 @@
 */
@Data
@ApiModel("测试单表视图对象")
@ExcelIgnoreUnannotated
public class TestDemoVo {
    private static final long serialVersionUID = 1L;
@@ -29,63 +32,63 @@
    /**
     * éƒ¨é—¨id
     */
    @Excel(name = "部门id")
    @ExcelProperty(value = "部门id")
    @ApiModelProperty("部门id")
    private Long deptId;
    /**
     * ç”¨æˆ·id
     */
    @Excel(name = "用户id")
    @ExcelProperty(value = "用户id")
    @ApiModelProperty("用户id")
    private Long userId;
    /**
     * æŽ’序号
     */
    @Excel(name = "排序号")
    @ExcelProperty(value = "排序号")
    @ApiModelProperty("排序号")
    private Long orderNum;
    /**
     * key键
     */
    @Excel(name = "key键")
    @ExcelProperty(value = "key键")
    @ApiModelProperty("key键")
    private String testKey;
    /**
     * å€¼
     */
    @Excel(name = "值")
    @ExcelProperty(value = "值")
    @ApiModelProperty("值")
    private String value;
    /**
     * åˆ›å»ºæ—¶é—´
     */
    @Excel(name = "创建时间" , width = 30, dateFormat = "yyyy-MM-dd")
    @ExcelProperty(value = "创建时间")
    @ApiModelProperty("创建时间")
    private Date createTime;
    /**
     * åˆ›å»ºäºº
     */
    @Excel(name = "创建人")
    @ExcelProperty(value = "创建人")
    @ApiModelProperty("创建人")
    private String createBy;
    /**
     * æ›´æ–°æ—¶é—´
     */
    @Excel(name = "更新时间" , width = 30, dateFormat = "yyyy-MM-dd")
    @ExcelProperty(value = "更新时间")
    @ApiModelProperty("更新时间")
    private Date updateTime;
    /**
     * æ›´æ–°äºº
     */
    @Excel(name = "更新人")
    @ExcelProperty(value = "更新人")
    @ApiModelProperty("更新人")
    private String updateBy;
ruoyi-demo/src/main/java/com/ruoyi/demo/domain/vo/TestTreeVo.java
@@ -1,9 +1,11 @@
package com.ruoyi.demo.domain.vo;
import com.ruoyi.common.annotation.Excel;
import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
import com.alibaba.excel.annotation.ExcelProperty;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.util.Date;
@@ -16,6 +18,7 @@
 */
@Data
@ApiModel("测试树表视图对象")
@ExcelIgnoreUnannotated
public class TestTreeVo {
    private static final long serialVersionUID = 1L;
@@ -29,35 +32,35 @@
    /**
     * çˆ¶id
     */
    @Excel(name = "父id")
    @ExcelProperty(value = "父id")
    @ApiModelProperty("父id")
    private Long parentId;
    /**
     * éƒ¨é—¨id
     */
    @Excel(name = "部门id")
    @ExcelProperty(value = "部门id")
    @ApiModelProperty("部门id")
    private Long deptId;
    /**
     * ç”¨æˆ·id
     */
    @Excel(name = "用户id")
    @ExcelProperty(value = "用户id")
    @ApiModelProperty("用户id")
    private Long userId;
    /**
     * æ ‘节点名
     */
    @Excel(name = "树节点名")
    @ExcelProperty(value = "树节点名")
    @ApiModelProperty("树节点名")
    private String treeName;
    /**
     * åˆ›å»ºæ—¶é—´
     */
    @Excel(name = "创建时间" , width = 30, dateFormat = "yyyy-MM-dd")
    @ExcelProperty(value = "创建时间")
    @ApiModelProperty("创建时间")
    private Date createTime;
ruoyi-demo/src/main/java/com/ruoyi/demo/service/impl/TestDemoServiceImpl.java
@@ -1,7 +1,7 @@
package com.ruoyi.demo.service.impl;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.StrUtil;
import com.ruoyi.common.utils.StringUtils;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
@@ -62,11 +62,11 @@
        Map<String, Object> params = bo.getParams();
        Object dataScope = params.get("dataScope");
        LambdaQueryWrapper<TestDemo> lqw = Wrappers.lambdaQuery();
        lqw.like(StrUtil.isNotBlank(bo.getTestKey()), TestDemo::getTestKey, bo.getTestKey());
        lqw.eq(StrUtil.isNotBlank(bo.getValue()), TestDemo::getValue, bo.getValue());
        lqw.like(StringUtils.isNotBlank(bo.getTestKey()), TestDemo::getTestKey, bo.getTestKey());
        lqw.eq(StringUtils.isNotBlank(bo.getValue()), TestDemo::getValue, bo.getValue());
        lqw.between(params.get("beginCreateTime") != null && params.get("endCreateTime") != null,
            TestDemo::getCreateTime, params.get("beginCreateTime"), params.get("endCreateTime"));
        lqw.apply(dataScope != null && StrUtil.isNotBlank(dataScope.toString()),
        lqw.apply(dataScope != null && StringUtils.isNotBlank(dataScope.toString()),
            dataScope != null ? dataScope.toString() : null);
        return lqw;
    }
ruoyi-demo/src/main/java/com/ruoyi/demo/service/impl/TestTreeServiceImpl.java
@@ -1,7 +1,7 @@
package com.ruoyi.demo.service.impl;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.StrUtil;
import com.ruoyi.common.utils.StringUtils;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.ruoyi.common.annotation.DataScope;
@@ -42,10 +42,10 @@
        Map<String, Object> params = bo.getParams();
        Object dataScope = params.get("dataScope");
        LambdaQueryWrapper<TestTree> lqw = Wrappers.lambdaQuery();
        lqw.like(StrUtil.isNotBlank(bo.getTreeName()), TestTree::getTreeName, bo.getTreeName());
        lqw.like(StringUtils.isNotBlank(bo.getTreeName()), TestTree::getTreeName, bo.getTreeName());
        lqw.between(params.get("beginCreateTime") != null && params.get("endCreateTime") != null,
            TestTree::getCreateTime, params.get("beginCreateTime"), params.get("endCreateTime"));
        lqw.apply(dataScope != null && StrUtil.isNotBlank(dataScope.toString()),
        lqw.apply(dataScope != null && StringUtils.isNotBlank(dataScope.toString()),
            dataScope != null ? dataScope.toString() : null);
        return lqw;
    }
ruoyi-extend/pom.xml
@@ -5,7 +5,7 @@
    <parent>
        <artifactId>ruoyi-vue-plus</artifactId>
        <groupId>com.ruoyi</groupId>
        <version>2.6.0</version>
        <version>3.0.0</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>
    <artifactId>ruoyi-extend</artifactId>
ruoyi-extend/ruoyi-monitor-admin/pom.xml
@@ -5,7 +5,7 @@
    <parent>
        <artifactId>ruoyi-extend</artifactId>
        <groupId>com.ruoyi</groupId>
        <version>2.6.0</version>
        <version>3.0.0</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>
    <packaging>jar</packaging>
ruoyi-framework/pom.xml
@@ -5,7 +5,7 @@
    <parent>
        <artifactId>ruoyi-vue-plus</artifactId>
        <groupId>com.ruoyi</groupId>
        <version>2.6.0</version>
        <version>3.0.0</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>
ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/DataScopeAspect.java
@@ -1,13 +1,12 @@
package com.ruoyi.framework.aspectj;
import cn.hutool.core.lang.Validator;
import cn.hutool.core.util.StrUtil;
import com.ruoyi.common.annotation.DataScope;
import com.ruoyi.common.core.domain.BaseEntity;
import com.ruoyi.common.core.domain.entity.SysRole;
import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.core.domain.model.LoginUser;
import com.ruoyi.common.utils.ServletUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.reflect.ReflectUtils;
import com.ruoyi.common.utils.spring.SpringUtils;
import com.ruoyi.framework.web.service.TokenService;
@@ -80,10 +79,10 @@
        }
        // èŽ·å–å½“å‰çš„ç”¨æˆ·
        LoginUser loginUser = SpringUtils.getBean(TokenService.class).getLoginUser(ServletUtils.getRequest());
        if (Validator.isNotNull(loginUser)) {
        if (StringUtils.isNotNull(loginUser)) {
            SysUser currentUser = loginUser.getUser();
            // å¦‚果是超级管理员,则不过滤数据
            if (Validator.isNotNull(currentUser) && !currentUser.isAdmin()) {
            if (StringUtils.isNotNull(currentUser) && !currentUser.isAdmin()) {
                dataScopeFilter(joinPoint, currentUser, controllerDataScope.deptAlias(),
                    controllerDataScope.userAlias(), controllerDataScope.isUser());
            }
@@ -101,8 +100,8 @@
        StringBuilder sqlString = new StringBuilder();
        // å°† "." æå–出,不写别名为单表查询,写别名为多表查询
        deptAlias = StrUtil.isNotBlank(deptAlias) ? deptAlias + "." : "";
        userAlias = StrUtil.isNotBlank(userAlias) ? userAlias + "." : "";
        deptAlias = StringUtils.isNotBlank(deptAlias) ? deptAlias + "." : "";
        userAlias = StringUtils.isNotBlank(userAlias) ? userAlias + "." : "";
        for (SysRole role : user.getRoles()) {
            String dataScope = role.getDataScope();
@@ -110,19 +109,19 @@
                sqlString = new StringBuilder();
                break;
            } else if (DATA_SCOPE_CUSTOM.equals(dataScope)) {
                sqlString.append(StrUtil.format(
                sqlString.append(StringUtils.format(
                    " OR {}dept_id IN ( SELECT dept_id FROM sys_role_dept WHERE role_id = {} ) ",
                    deptAlias, role.getRoleId()));
            } else if (DATA_SCOPE_DEPT.equals(dataScope)) {
                sqlString.append(StrUtil.format(" OR {}dept_id = {} ",
                sqlString.append(StringUtils.format(" OR {}dept_id = {} ",
                    deptAlias, user.getDeptId()));
            } else if (DATA_SCOPE_DEPT_AND_CHILD.equals(dataScope)) {
                sqlString.append(StrUtil.format(
                sqlString.append(StringUtils.format(
                    " OR {}dept_id IN ( SELECT dept_id FROM sys_dept WHERE dept_id = {} or find_in_set( {} , ancestors ) )",
                    deptAlias, user.getDeptId(), user.getDeptId()));
            } else if (DATA_SCOPE_SELF.equals(dataScope)) {
                if (isUser) {
                    sqlString.append(StrUtil.format(" OR {}user_id = {} ",
                    sqlString.append(StringUtils.format(" OR {}user_id = {} ",
                        userAlias, user.getUserId()));
                } else {
                    // æ•°æ®æƒé™ä¸ºä»…本人且没有userAlias别名不查询任何数据
@@ -131,7 +130,7 @@
            }
        }
        if (StrUtil.isNotBlank(sqlString.toString())) {
        if (StringUtils.isNotBlank(sqlString.toString())) {
            putDataScope(joinPoint, sqlString.substring(4));
        }
    }
@@ -155,14 +154,14 @@
     */
    private void clearDataScope(final JoinPoint joinPoint) {
        Object params = joinPoint.getArgs()[0];
        if (Validator.isNotNull(params)) {
        if (StringUtils.isNotNull(params)) {
            putDataScope(joinPoint, "");
        }
    }
    private static void putDataScope(JoinPoint joinPoint, String sql) {
        Object params = joinPoint.getArgs()[0];
        if (Validator.isNotNull(params)) {
        if (StringUtils.isNotNull(params)) {
            if (params instanceof BaseEntity) {
                BaseEntity baseEntity = (BaseEntity) params;
                baseEntity.getParams().put(DATA_SCOPE, sql);
ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/DataSourceAspect.java
@@ -1,8 +1,8 @@
package com.ruoyi.framework.aspectj;
import cn.hutool.core.lang.Validator;
import com.baomidou.dynamic.datasource.toolkit.DynamicDataSourceContextHolder;
import com.ruoyi.common.annotation.DataSource;
import com.ruoyi.common.utils.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
@@ -33,7 +33,7 @@
    public Object around(ProceedingJoinPoint point) throws Throwable {
        DataSource dataSource = getDataSource(point);
        if (Validator.isNotNull(dataSource)) {
        if (StringUtils.isNotNull(dataSource)) {
            DynamicDataSourceContextHolder.poll();
            String source = dataSource.value().getSource();
            DynamicDataSourceContextHolder.push(source);
ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/LogAspect.java
@@ -1,13 +1,12 @@
package com.ruoyi.framework.aspectj;
import cn.hutool.core.lang.Validator;
import cn.hutool.core.util.StrUtil;
import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.core.domain.model.LoginUser;
import com.ruoyi.common.enums.BusinessStatus;
import com.ruoyi.common.enums.HttpMethod;
import com.ruoyi.common.utils.JsonUtils;
import com.ruoyi.common.utils.ServletUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.spring.SpringUtils;
import com.ruoyi.framework.web.service.AsyncService;
import com.ruoyi.framework.web.service.TokenService;
@@ -104,7 +103,7 @@
            if (e != null)
            {
                operLog.setStatus(BusinessStatus.FAIL.ordinal());
                operLog.setErrorMsg(StrUtil.sub(e.getMessage(), 0, 2000));
                operLog.setErrorMsg(StringUtils.substring(e.getMessage(), 0, 2000));
            }
            // è®¾ç½®æ–¹æ³•名称
            String className = joinPoint.getTarget().getClass().getName();
@@ -161,12 +160,12 @@
        if (HttpMethod.PUT.name().equals(requestMethod) || HttpMethod.POST.name().equals(requestMethod))
        {
            String params = argsArrayToString(joinPoint.getArgs());
            operLog.setOperParam(StrUtil.sub(params, 0, 2000));
            operLog.setOperParam(StringUtils.substring(params, 0, 2000));
        }
        else
        {
            Map<?, ?> paramsMap = (Map<?, ?>) ServletUtils.getRequest().getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE);
            operLog.setOperParam(StrUtil.sub(paramsMap.toString(), 0, 2000));
            operLog.setOperParam(StringUtils.substring(paramsMap.toString(), 0, 2000));
        }
    }
@@ -195,7 +194,7 @@
        if (paramsArray != null && paramsArray.length > 0)
        {
            for (Object o : paramsArray) {
                if (Validator.isNotNull(o) && !isFilterObject(o)) {
                if (StringUtils.isNotNull(o) && !isFilterObject(o)) {
                    params.append(JsonUtils.toJsonString(o)).append(" ");
                }
            }
ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/RateLimiterAspect.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,116 @@
package com.ruoyi.framework.aspectj;
import com.ruoyi.common.annotation.RateLimiter;
import com.ruoyi.common.enums.LimitType;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.ServletUtils;
import com.ruoyi.common.utils.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.RedisScript;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
import java.util.Collections;
import java.util.List;
/**
 * é™æµå¤„理
 *
 * @author ruoyi
 */
@Aspect
@Component
public class RateLimiterAspect
{
    private static final Logger log = LoggerFactory.getLogger(RateLimiterAspect.class);
    private RedisTemplate<Object, Object> redisTemplate;
    private RedisScript<Long> limitScript;
    @Autowired
    public void setRedisTemplate1(RedisTemplate<Object, Object> redisTemplate)
    {
        this.redisTemplate = redisTemplate;
    }
    @Autowired
    public void setLimitScript(RedisScript<Long> limitScript)
    {
        this.limitScript = limitScript;
    }
    // é…ç½®ç»‡å…¥ç‚¹
    @Pointcut("@annotation(com.ruoyi.common.annotation.RateLimiter)")
    public void rateLimiterPointCut()
    {
    }
    @Before("rateLimiterPointCut()")
    public void doBefore(JoinPoint point) throws Throwable
    {
        RateLimiter rateLimiter = getAnnotationRateLimiter(point);
        String key = rateLimiter.key();
        int time = rateLimiter.time();
        int count = rateLimiter.count();
        String combineKey = getCombineKey(rateLimiter, point);
        List<Object> keys = Collections.singletonList(combineKey);
        try
        {
            Long number = redisTemplate.execute(limitScript, keys, count, time);
            if (StringUtils.isNull(number) || number.intValue() > count)
            {
                throw new ServiceException("访问过于频繁,请稍后再试");
            }
            log.info("限制请求'{}',当前请求'{}',缓存key'{}'", count, number.intValue(), key);
        }
        catch (ServiceException e)
        {
            throw e;
        }
        catch (Exception e)
        {
            throw new RuntimeException("服务器限流异常,请稍后再试");
        }
    }
    /**
     * æ˜¯å¦å­˜åœ¨æ³¨è§£ï¼Œå¦‚果存在就获取
     */
    private RateLimiter getAnnotationRateLimiter(JoinPoint joinPoint)
    {
        Signature signature = joinPoint.getSignature();
        MethodSignature methodSignature = (MethodSignature) signature;
        Method method = methodSignature.getMethod();
        if (method != null)
        {
            return method.getAnnotation(RateLimiter.class);
        }
        return null;
    }
    public String getCombineKey(RateLimiter rateLimiter, JoinPoint point)
    {
        StringBuffer stringBuffer = new StringBuffer(rateLimiter.key());
        if (rateLimiter.limitType() == LimitType.IP)
        {
            stringBuffer.append(ServletUtils.getClientIP());
        }
        MethodSignature signature = (MethodSignature) point.getSignature();
        Method method = signature.getMethod();
        Class<?> targetClass = method.getDeclaringClass();
        stringBuffer.append("-").append(targetClass.getName()).append("- ").append(method.getName());
        return stringBuffer.toString();
    }
}
ruoyi-framework/src/main/java/com/ruoyi/framework/captcha/UnsignedMathGenerator.java
@@ -4,7 +4,7 @@
import cn.hutool.core.math.Calculator;
import cn.hutool.core.util.CharUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import com.ruoyi.common.utils.StringUtils;
/**
 * æ— ç¬¦å·è®¡ç®—生成器
@@ -45,8 +45,8 @@
        int max = RandomUtil.randomInt(min, limit);
        String number1 = Integer.toString(max);
        String number2 = Integer.toString(min);
        number1 = StrUtil.padAfter(number1, this.numberLength, CharUtil.SPACE);
        number2 = StrUtil.padAfter(number2, this.numberLength, CharUtil.SPACE);
        number1 = StringUtils.rightPad(number1, this.numberLength, CharUtil.SPACE);
        number2 = StringUtils.rightPad(number2, this.numberLength, CharUtil.SPACE);
        return number1 + RandomUtil.randomChar(operators) + number2 + '=';
    }
@@ -80,6 +80,6 @@
     * @return æœ€å¤§å€¼
     */
    private int getLimit() {
        return Integer.parseInt("1" + StrUtil.repeat('0', this.numberLength));
        return Integer.parseInt("1" + StringUtils.repeat('0', this.numberLength));
    }
}
ruoyi-framework/src/main/java/com/ruoyi/framework/config/ApplicationConfig.java
@@ -1,11 +1,7 @@
package com.ruoyi.framework.config;
import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import java.util.TimeZone;
/**
 * ç¨‹åºæ³¨è§£é…ç½®
@@ -16,11 +12,5 @@
// è¡¨ç¤ºé€šè¿‡aop框架暴露该代理对象,AopContext能够访问
@EnableAspectJAutoProxy(exposeProxy = true)
public class ApplicationConfig {
    /**
     * æ—¶åŒºé…ç½®
     */
    @Bean
    public Jackson2ObjectMapperBuilderCustomizer jacksonObjectMapperCustomization() {
        return jacksonObjectMapperBuilder -> jacksonObjectMapperBuilder.timeZone(TimeZone.getDefault());
    }
}
ruoyi-framework/src/main/java/com/ruoyi/framework/config/AsyncConfig.java
@@ -1,6 +1,6 @@
package com.ruoyi.framework.config;
import com.ruoyi.common.exception.CustomException;
import com.ruoyi.common.exception.ServiceException;
import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
@@ -41,7 +41,7 @@
    public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
        return (throwable, method, objects) -> {
            throwable.printStackTrace();
            throw new CustomException(
            throw new ServiceException(
                    "Exception message - " + throwable.getMessage()
                    + ", Method name - " + method.getName()
                    + ", Parameter value - " + Arrays.toString(objects));
ruoyi-framework/src/main/java/com/ruoyi/framework/config/FilterConfig.java
@@ -1,10 +1,11 @@
package com.ruoyi.framework.config;
import cn.hutool.core.util.StrUtil;
import com.ruoyi.common.filter.RepeatableFilter;
import com.ruoyi.common.filter.XssFilter;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.framework.config.properties.XssProperties;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@@ -19,6 +20,7 @@
 * @author Lion Li
 */
@Configuration
@ConditionalOnProperty(value = "xss.enabled", havingValue = "true")
public class FilterConfig {
    @Autowired
@@ -30,12 +32,11 @@
        FilterRegistrationBean registration = new FilterRegistrationBean();
        registration.setDispatcherTypes(DispatcherType.REQUEST);
        registration.setFilter(new XssFilter());
        registration.addUrlPatterns(StrUtil.splitToArray(xssProperties.getUrlPatterns(), ","));
        registration.addUrlPatterns(StringUtils.split(xssProperties.getUrlPatterns(), ","));
        registration.setName("xssFilter");
        registration.setOrder(FilterRegistrationBean.HIGHEST_PRECEDENCE);
        Map<String, String> initParameters = new HashMap<String, String>();
        initParameters.put("excludes", xssProperties.getExcludes());
        initParameters.put("enabled", xssProperties.getEnabled());
        registration.setInitParameters(initParameters);
        return registration;
    }
ruoyi-framework/src/main/java/com/ruoyi/framework/config/I18nConfig.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,48 @@
package com.ruoyi.framework.config;
import cn.hutool.core.util.StrUtil;
import org.jetbrains.annotations.NotNull;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.LocaleResolver;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Locale;
/**
 * å›½é™…化配置
 *
 * @author Lion Li
 */
@Configuration
public class I18nConfig {
    @Bean
    public LocaleResolver localeResolver() {
        return new I18nLocaleResolver();
    }
    /**
     * èŽ·å–è¯·æ±‚å¤´å›½é™…åŒ–ä¿¡æ¯
     */
    static class I18nLocaleResolver implements LocaleResolver {
        @NotNull
        @Override
        public Locale resolveLocale(HttpServletRequest httpServletRequest) {
            String language = httpServletRequest.getHeader("content-language");
            Locale locale = Locale.getDefault();
            if (StrUtil.isNotBlank(language)) {
                String[] split = language.split("_");
                locale = new Locale(split[0], split[1]);
            }
            return locale;
        }
        @Override
        public void setLocale(@NotNull HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Locale locale) {
        }
    }
}
ruoyi-framework/src/main/java/com/ruoyi/framework/config/JacksonConfig.java
@@ -2,15 +2,19 @@
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import com.ruoyi.common.utils.JsonUtils;
import com.ruoyi.framework.jackson.BigNumberSerializer;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.time.LocalDateTime;
import java.util.TimeZone;
@@ -34,6 +38,10 @@
                ObjectMapper objectMapper = (ObjectMapper) bean;
                // å…¨å±€é…ç½®åºåˆ—化返回 JSON å¤„理
                SimpleModule simpleModule = new SimpleModule();
                simpleModule.addSerializer(Long.class, BigNumberSerializer.INSTANCE);
                simpleModule.addSerializer(Long.TYPE, BigNumberSerializer.INSTANCE);
                simpleModule.addSerializer(BigInteger.class, BigNumberSerializer.INSTANCE);
                simpleModule.addSerializer(BigDecimal.class, ToStringSerializer.instance);
                simpleModule.addSerializer(LocalDateTime.class, LocalDateTimeSerializer.INSTANCE);
                simpleModule.addDeserializer(LocalDateTime.class, LocalDateTimeDeserializer.INSTANCE);
                objectMapper.registerModule(simpleModule);
ruoyi-framework/src/main/java/com/ruoyi/framework/config/RedisConfig.java
@@ -1,6 +1,7 @@
package com.ruoyi.framework.config;
import cn.hutool.core.util.StrUtil;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.framework.config.properties.RedissonProperties;
import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
@@ -16,6 +17,7 @@
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import java.io.IOException;
import java.util.HashMap;
@@ -59,7 +61,7 @@
            .setAddress(prefix + redisProperties.getHost() + ":" + redisProperties.getPort())
            .setConnectTimeout(((Long) redisProperties.getTimeout().toMillis()).intValue())
            .setDatabase(redisProperties.getDatabase())
            .setPassword(StrUtil.isNotBlank(redisProperties.getPassword()) ? redisProperties.getPassword() : null)
            .setPassword(StringUtils.isNotBlank(redisProperties.getPassword()) ? redisProperties.getPassword() : null)
            .setTimeout(singleServerConfig.getTimeout())
            .setRetryAttempts(singleServerConfig.getRetryAttempts())
            .setRetryInterval(singleServerConfig.getRetryInterval())
@@ -89,4 +91,32 @@
        return new RedissonSpringCacheManager(redissonClient, config, JsonJacksonCodec.INSTANCE);
    }
    @Bean
    public DefaultRedisScript<Long> limitScript() {
        DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>();
        redisScript.setScriptText(limitScriptText());
        redisScript.setResultType(Long.class);
        return redisScript;
    }
    /**
     * é™æµè„šæœ¬
     */
    private String limitScriptText() {
        return StrUtil.builder()
            .append("local key = KEYS[1]\n")
            .append("local count = tonumber(ARGV[1])\n")
            .append("local time = tonumber(ARGV[2])\n")
            .append("local current = redis.call('get', key);\n")
            .append("if current and tonumber(current) > count then\n")
            .append("    return current;\n")
            .append("end\n")
            .append("current = redis.call('incr', key)\n")
            .append("if tonumber(current) == 1 then\n")
            .append("    redis.call('expire', key, time)\n")
            .append("end\n")
            .append("return current;")
            .toString();
    }
}
ruoyi-framework/src/main/java/com/ruoyi/framework/config/ResourcesConfig.java
@@ -1,5 +1,6 @@
package com.ruoyi.framework.config;
import com.ruoyi.framework.interceptor.RepeatSubmitInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@@ -9,9 +10,6 @@
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import com.ruoyi.common.config.RuoYiConfig;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.framework.interceptor.RepeatSubmitInterceptor;
/**
 * é€šç”¨é…ç½®
@@ -27,8 +25,6 @@
    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry)
    {
        /** æœ¬åœ°æ–‡ä»¶ä¸Šä¼ è·¯å¾„ */
        registry.addResourceHandler(Constants.RESOURCE_PREFIX + "/**").addResourceLocations("file:" + RuoYiConfig.getProfile() + "/");
    }
    /**
ruoyi-framework/src/main/java/com/ruoyi/framework/config/SecurityConfig.java
@@ -96,19 +96,16 @@
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
                // è¿‡æ»¤è¯·æ±‚
                .authorizeRequests()
                // å¯¹äºŽç™»å½•login éªŒè¯ç captchaImage å…è®¸åŒ¿åè®¿é—®
                .antMatchers("/login", "/captchaImage").anonymous()
                // å¯¹äºŽç™»å½•login æ³¨å†Œregister éªŒè¯ç captchaImage å…è®¸åŒ¿åè®¿é—®
                .antMatchers("/login", "/register", "/captchaImage").anonymous()
                .antMatchers(
                        HttpMethod.GET,
                        "/",
                        "/*.html",
                        "/**/*.html",
                        "/**/*.css",
                        "/**/*.js",
                        "/profile/**"
                        "/**/*.js"
                ).permitAll()
                .antMatchers("/common/download**").anonymous()
                .antMatchers("/common/download/resource**").anonymous()
                .antMatchers("/doc.html").anonymous()
                .antMatchers("/swagger-resources/**").anonymous()
                .antMatchers("/webjars/**").anonymous()
@@ -128,7 +125,6 @@
        httpSecurity.addFilterBefore(corsFilter, JwtAuthenticationTokenFilter.class);
        httpSecurity.addFilterBefore(corsFilter, LogoutFilter.class);
    }
    /**
     * å¼ºæ•£åˆ—哈希加密实现
ruoyi-framework/src/main/java/com/ruoyi/framework/config/properties/RepeatSubmitProperties.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,22 @@
package com.ruoyi.framework.config.properties;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
/**
 * é‡å¤æäº¤ é…ç½®å±žæ€§
 *
 * @author Lion Li
 */
@Data
@Component
@ConfigurationProperties(prefix = "repeat-submit")
public class RepeatSubmitProperties {
    /**
     * é—´é𔿗¶é—´(毫秒)
     */
    private int intervalTime;
}
ruoyi-framework/src/main/java/com/ruoyi/framework/interceptor/RepeatSubmitInterceptor.java
@@ -6,7 +6,7 @@
import com.ruoyi.common.utils.ServletUtils;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@@ -15,42 +15,36 @@
/**
 * é˜²æ­¢é‡å¤æäº¤æ‹¦æˆªå™¨
 *
 * @author ruoyi
 * ç§»é™¤ç»§æ‰¿ HandlerInterceptorAdapter è¿‡æœŸç±»
 * æ”¹ä¸ºå®žçް HandlerInterceptor æŽ¥å£(官方推荐写法)
 *
 * @author Lion Li
 */
@Component
public abstract class RepeatSubmitInterceptor extends HandlerInterceptorAdapter
{
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception
    {
        if (handler instanceof HandlerMethod)
        {
            HandlerMethod handlerMethod = (HandlerMethod) handler;
            Method method = handlerMethod.getMethod();
            RepeatSubmit annotation = method.getAnnotation(RepeatSubmit.class);
            if (annotation != null)
            {
                if (this.isRepeatSubmit(request))
                {
                    AjaxResult ajaxResult = AjaxResult.error("不允许重复提交,请稍后再试");
                    ServletUtils.renderString(response, JsonUtils.toJsonString(ajaxResult));
                    return false;
                }
            }
            return true;
        }
        else
        {
            return super.preHandle(request, response, handler);
        }
    }
public abstract class RepeatSubmitInterceptor implements HandlerInterceptor {
    /**
     * éªŒè¯æ˜¯å¦é‡å¤æäº¤ç”±å­ç±»å®žçŽ°å…·ä½“çš„é˜²é‡å¤æäº¤çš„è§„åˆ™
     *
     * @param request
     * @return
     * @throws Exception
     */
    public abstract boolean isRepeatSubmit(HttpServletRequest request);
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
        throws Exception {
        if (handler instanceof HandlerMethod) {
            HandlerMethod handlerMethod = (HandlerMethod) handler;
            Method method = handlerMethod.getMethod();
            RepeatSubmit annotation = method.getAnnotation(RepeatSubmit.class);
            if (annotation != null) {
                if (this.isRepeatSubmit(annotation, request)) {
                    AjaxResult ajaxResult = AjaxResult.error("不允许重复提交,请稍后再试");
                    ServletUtils.renderString(response, JsonUtils.toJsonString(ajaxResult));
                    return false;
                }
            }
            return true;
        } else {
            return HandlerInterceptor.super.preHandle(request, response, handler);
        }
    }
    /**
     * éªŒè¯æ˜¯å¦é‡å¤æäº¤ç”±å­ç±»å®žçŽ°å…·ä½“çš„é˜²é‡å¤æäº¤çš„è§„åˆ™
     */
    public abstract boolean isRepeatSubmit(RepeatSubmit annotation, HttpServletRequest request);
}
ruoyi-framework/src/main/java/com/ruoyi/framework/interceptor/impl/SameUrlDataInterceptor.java
@@ -1,15 +1,19 @@
package com.ruoyi.framework.interceptor.impl;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.lang.Validator;
import com.ruoyi.common.annotation.RepeatSubmit;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.common.filter.RepeatedlyRequestWrapper;
import com.ruoyi.common.utils.JsonUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.framework.config.properties.RepeatSubmitProperties;
import com.ruoyi.framework.config.properties.TokenProperties;
import com.ruoyi.framework.interceptor.RepeatSubmitInterceptor;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
@@ -20,45 +24,34 @@
/**
 * åˆ¤æ–­è¯·æ±‚url和数据是否和上一次相同,
 * å¦‚果和上次相同,则是重复提交表单。 æœ‰æ•ˆæ—¶é—´ä¸º10秒内。
 * å¦‚果和上次相同,则是重复提交表单。
 *
 * @author ruoyi
 * @author Lion Li
 */
@Slf4j
@RequiredArgsConstructor(onConstructor_ = @Autowired)
@Component
public class SameUrlDataInterceptor extends RepeatSubmitInterceptor
{
    public final String REPEAT_PARAMS = "repeatParams";
public class SameUrlDataInterceptor extends RepeatSubmitInterceptor {
    public final String REPEAT_PARAMS = "repeatParams";
    public final String REPEAT_TIME = "repeatTime";
    public final String REPEAT_TIME = "repeatTime";
    // ä»¤ç‰Œè‡ªå®šä¹‰æ ‡è¯†
    @Value("${token.header}")
    private String header;
    private final TokenProperties tokenProperties;
    private final RepeatSubmitProperties repeatSubmitProperties;
    private final RedisCache redisCache;
    @Autowired
    private RedisCache redisCache;
    /**
     * é—´é𔿗¶é—´ï¼Œå•位:秒 é»˜è®¤10秒
     *
     * ä¸¤æ¬¡ç›¸åŒå‚数的请求,如果间隔时间大于该参数,系统不会认定为重复提交的数据
     */
    private int intervalTime = 10;
    public void setIntervalTime(int intervalTime)
    {
        this.intervalTime = intervalTime;
    }
    @SuppressWarnings("unchecked")
    @Override
    public boolean isRepeatSubmit(HttpServletRequest request)
    {
        String nowParams = "";
        if (request instanceof RepeatedlyRequestWrapper)
        {
            RepeatedlyRequestWrapper repeatedlyRequest = (RepeatedlyRequestWrapper) request;
    @SuppressWarnings("unchecked")
    @Override
    public boolean isRepeatSubmit(RepeatSubmit repeatSubmit, HttpServletRequest request) {
        // å¦‚果注解不为0 åˆ™ä½¿ç”¨æ³¨è§£æ•°å€¼
        long intervalTime = repeatSubmitProperties.getIntervalTime();
        if (repeatSubmit.intervalTime() > 0) {
            intervalTime = repeatSubmit.timeUnit().toMillis(repeatSubmit.intervalTime());
        }
        String nowParams = "";
        if (request instanceof RepeatedlyRequestWrapper) {
            RepeatedlyRequestWrapper repeatedlyRequest = (RepeatedlyRequestWrapper) request;
            try {
                nowParams = IoUtil.readUtf8(repeatedlyRequest.getInputStream());
            } catch (IOException e) {
@@ -66,68 +59,57 @@
            }
        }
        // body参数为空,获取Parameter的数据
        if (Validator.isEmpty(nowParams))
        {
            nowParams = JsonUtils.toJsonString(request.getParameterMap());
        }
        Map<String, Object> nowDataMap = new HashMap<String, Object>();
        nowDataMap.put(REPEAT_PARAMS, nowParams);
        nowDataMap.put(REPEAT_TIME, System.currentTimeMillis());
        // body参数为空,获取Parameter的数据
        if (StringUtils.isEmpty(nowParams)) {
            nowParams = JsonUtils.toJsonString(request.getParameterMap());
        }
        Map<String, Object> nowDataMap = new HashMap<String, Object>();
        nowDataMap.put(REPEAT_PARAMS, nowParams);
        nowDataMap.put(REPEAT_TIME, System.currentTimeMillis());
        // è¯·æ±‚地址(作为存放cache的key值)
        String url = request.getRequestURI();
        // è¯·æ±‚地址(作为存放cache的key值)
        String url = request.getRequestURI();
        // å”¯ä¸€å€¼ï¼ˆæ²¡æœ‰æ¶ˆæ¯å¤´åˆ™ä½¿ç”¨è¯·æ±‚地址)
        String submitKey = request.getHeader(header);
        if (Validator.isEmpty(submitKey))
        {
            submitKey = url;
        }
        // å”¯ä¸€å€¼ï¼ˆæ²¡æœ‰æ¶ˆæ¯å¤´åˆ™ä½¿ç”¨è¯·æ±‚地址)
        String submitKey = request.getHeader(tokenProperties.getHeader());
        if (StringUtils.isEmpty(submitKey)) {
            submitKey = url;
        }
        // å”¯ä¸€æ ‡è¯†ï¼ˆæŒ‡å®škey + æ¶ˆæ¯å¤´ï¼‰
        String cacheRepeatKey = Constants.REPEAT_SUBMIT_KEY + submitKey;
        // å”¯ä¸€æ ‡è¯†ï¼ˆæŒ‡å®škey + æ¶ˆæ¯å¤´ï¼‰
        String cacheRepeatKey = Constants.REPEAT_SUBMIT_KEY + submitKey;
        Object sessionObj = redisCache.getCacheObject(cacheRepeatKey);
        if (sessionObj != null)
        {
            Map<String, Object> sessionMap = (Map<String, Object>) sessionObj;
            if (sessionMap.containsKey(url))
            {
                Map<String, Object> preDataMap = (Map<String, Object>) sessionMap.get(url);
                if (compareParams(nowDataMap, preDataMap) && compareTime(nowDataMap, preDataMap))
                {
                    return true;
                }
            }
        }
        Map<String, Object> cacheMap = new HashMap<String, Object>();
        cacheMap.put(url, nowDataMap);
        redisCache.setCacheObject(cacheRepeatKey, cacheMap, intervalTime, TimeUnit.SECONDS);
        return false;
    }
        Object sessionObj = redisCache.getCacheObject(cacheRepeatKey);
        if (sessionObj != null) {
            Map<String, Object> sessionMap = (Map<String, Object>) sessionObj;
            if (sessionMap.containsKey(url)) {
                Map<String, Object> preDataMap = (Map<String, Object>) sessionMap.get(url);
                if (compareParams(nowDataMap, preDataMap) && compareTime(nowDataMap, preDataMap, intervalTime)) {
                    return true;
                }
            }
        }
        Map<String, Object> cacheMap = new HashMap<String, Object>();
        cacheMap.put(url, nowDataMap);
        redisCache.setCacheObject(cacheRepeatKey, cacheMap, Convert.toInt(intervalTime), TimeUnit.MILLISECONDS);
        return false;
    }
    /**
     * åˆ¤æ–­å‚数是否相同
     */
    private boolean compareParams(Map<String, Object> nowMap, Map<String, Object> preMap)
    {
        String nowParams = (String) nowMap.get(REPEAT_PARAMS);
        String preParams = (String) preMap.get(REPEAT_PARAMS);
        return nowParams.equals(preParams);
    }
    /**
     * åˆ¤æ–­å‚数是否相同
     */
    private boolean compareParams(Map<String, Object> nowMap, Map<String, Object> preMap) {
        String nowParams = (String) nowMap.get(REPEAT_PARAMS);
        String preParams = (String) preMap.get(REPEAT_PARAMS);
        return nowParams.equals(preParams);
    }
    /**
     * åˆ¤æ–­ä¸¤æ¬¡é—´é𔿗¶é—´
     */
    private boolean compareTime(Map<String, Object> nowMap, Map<String, Object> preMap)
    {
        long time1 = (Long) nowMap.get(REPEAT_TIME);
        long time2 = (Long) preMap.get(REPEAT_TIME);
        if ((time1 - time2) < (this.intervalTime * 1000))
        {
            return true;
        }
        return false;
    }
    /**
     * åˆ¤æ–­ä¸¤æ¬¡é—´é𔿗¶é—´
     */
    private boolean compareTime(Map<String, Object> nowMap, Map<String, Object> preMap, long intervalTime) {
        long time1 = (Long) nowMap.get(REPEAT_TIME);
        long time2 = (Long) preMap.get(REPEAT_TIME);
        return (time1 - time2) < intervalTime;
    }
}
ruoyi-framework/src/main/java/com/ruoyi/framework/jackson/BigNumberSerializer.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,42 @@
package com.ruoyi.framework.jackson;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.annotation.JacksonStdImpl;
import com.fasterxml.jackson.databind.ser.std.NumberSerializer;
import java.io.IOException;
/**
 * è¶…出 JS æœ€å¤§æœ€å°å€¼ å¤„理
 *
 * @author Lion Li
 */
@JacksonStdImpl
public class BigNumberSerializer extends NumberSerializer {
    /**
     * æ ¹æ® JS Number.MAX_SAFE_INTEGER ä¸Ž Number.MIN_SAFE_INTEGER å¾—来
     */
    private static final long MAX_SAFE_INTEGER = 9007199254740991L;
    private static final long MIN_SAFE_INTEGER = -9007199254740991L;
    /**
     * æä¾›å®žä¾‹
     */
    public static final BigNumberSerializer INSTANCE = new BigNumberSerializer(Number.class);
    public BigNumberSerializer(Class<? extends Number> rawType) {
        super(rawType);
    }
    @Override
    public void serialize(Number value, JsonGenerator gen, SerializerProvider provider) throws IOException {
        // è¶…出范围 åºåˆ—化位字符串
        if (value.longValue() > MIN_SAFE_INTEGER && value.longValue() < MAX_SAFE_INTEGER) {
            super.serialize(value, gen, provider);
        } else {
            gen.writeString(value.toString());
        }
    }
}
ruoyi-framework/src/main/java/com/ruoyi/framework/mybatisplus/CreateAndUpdateMetaObjectHandler.java
@@ -2,8 +2,10 @@
import cn.hutool.http.HttpStatus;
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import com.ruoyi.common.exception.CustomException;
import com.ruoyi.common.core.domain.model.LoginUser;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.SecurityUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.reflection.MetaObject;
import java.util.Date;
@@ -14,6 +16,7 @@
 * @author Lion Li
 * @date 2021/4/25
 */
@Slf4j
public class CreateAndUpdateMetaObjectHandler implements MetaObjectHandler {
    @Override
@@ -21,36 +24,43 @@
        try {
            //根据属性名字设置要填充的值
            if (metaObject.hasGetter("createTime")) {
                if (metaObject.getValue("createTime") == null) {
                    this.setFieldValByName("createTime", new Date(), metaObject);
                }
                this.setFieldValByName("createTime", new Date(), metaObject);
            }
            if (metaObject.hasGetter("createBy")) {
                if (metaObject.getValue("createBy") == null) {
                    this.setFieldValByName("createBy", SecurityUtils.getUsername(), metaObject);
                }
                this.setFieldValByName("createBy", getLoginUsername(), metaObject);
            }
        } catch (Exception e) {
            throw new CustomException("自动注入异常 => " + e.getMessage(), HttpStatus.HTTP_UNAUTHORIZED);
            throw new ServiceException("自动注入异常 => " + e.getMessage(), HttpStatus.HTTP_UNAUTHORIZED);
        }
        updateFill(metaObject);
    }
    @Override
    public void updateFill(MetaObject metaObject) {
        try {
            if (metaObject.hasGetter("updateBy")) {
                if (metaObject.getValue("updateBy") == null) {
                    this.setFieldValByName("updateBy", SecurityUtils.getUsername(), metaObject);
                }
                this.setFieldValByName("updateBy", getLoginUsername(), metaObject);
            }
            if (metaObject.hasGetter("updateTime")) {
                if (metaObject.getValue("updateTime") == null) {
                    this.setFieldValByName("updateTime", new Date(), metaObject);
                }
                this.setFieldValByName("updateTime", new Date(), metaObject);
            }
        } catch (Exception e) {
            throw new CustomException("自动注入异常 => " + e.getMessage(), HttpStatus.HTTP_UNAUTHORIZED);
            throw new ServiceException("自动注入异常 => " + e.getMessage(), HttpStatus.HTTP_UNAUTHORIZED);
        }
    }
    /**
     * èŽ·å–ç™»å½•ç”¨æˆ·å
     */
    private String getLoginUsername() {
        LoginUser loginUser;
        try {
            loginUser = SecurityUtils.getLoginUser();
        } catch (Exception e) {
            log.error("自动注入警告 => ç”¨æˆ·æœªç™»å½•");
            return null;
        }
        return loginUser.getUsername();
    }
}
ruoyi-framework/src/main/java/com/ruoyi/framework/security/filter/JwtAuthenticationTokenFilter.java
@@ -1,8 +1,8 @@
package com.ruoyi.framework.security.filter;
import cn.hutool.core.lang.Validator;
import com.ruoyi.common.core.domain.model.LoginUser;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.framework.web.service.TokenService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
@@ -19,7 +19,7 @@
/**
 * token过滤器 éªŒè¯token有效性
 *
 *
 * @author ruoyi
 */
@Component
@@ -33,7 +33,7 @@
            throws ServletException, IOException
    {
        LoginUser loginUser = tokenService.getLoginUser(request);
        if (Validator.isNotNull(loginUser) && Validator.isNull(SecurityUtils.getAuthentication()))
        if (StringUtils.isNotNull(loginUser) && StringUtils.isNull(SecurityUtils.getAuthentication()))
        {
            tokenService.verifyToken(loginUser);
            UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(loginUser, null, loginUser.getAuthorities());
ruoyi-framework/src/main/java/com/ruoyi/framework/security/handle/AuthenticationEntryPointImpl.java
@@ -1,6 +1,6 @@
package com.ruoyi.framework.security.handle;
import cn.hutool.core.util.StrUtil;
import com.ruoyi.common.utils.StringUtils;
import cn.hutool.http.HttpStatus;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.utils.JsonUtils;
@@ -29,7 +29,7 @@
            throws IOException
    {
        int code = HttpStatus.HTTP_UNAUTHORIZED;
        String msg = StrUtil.format("请求访问:{},认证失败,无法访问系统资源", request.getRequestURI());
        String msg = StringUtils.format("请求访问:{},认证失败,无法访问系统资源", request.getRequestURI());
        ServletUtils.renderString(response, JsonUtils.toJsonString(AjaxResult.error(code, msg)));
    }
}
ruoyi-framework/src/main/java/com/ruoyi/framework/security/handle/LogoutSuccessHandlerImpl.java
@@ -1,12 +1,12 @@
package com.ruoyi.framework.security.handle;
import cn.hutool.core.lang.Validator;
import cn.hutool.http.HttpStatus;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.domain.model.LoginUser;
import com.ruoyi.common.utils.JsonUtils;
import com.ruoyi.common.utils.ServletUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.framework.web.service.AsyncService;
import com.ruoyi.framework.web.service.TokenService;
import org.springframework.beans.factory.annotation.Autowired;
@@ -40,7 +40,7 @@
    public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication)
        throws IOException, ServletException {
        LoginUser loginUser = tokenService.getLoginUser(request);
        if (Validator.isNotNull(loginUser)) {
        if (StringUtils.isNotNull(loginUser)) {
            String userName = loginUser.getUsername();
            // åˆ é™¤ç”¨æˆ·ç¼“存记录
            tokenService.delLoginUser(loginUser.getToken());
ruoyi-framework/src/main/java/com/ruoyi/framework/web/exception/GlobalExceptionHandler.java
@@ -1,27 +1,25 @@
package com.ruoyi.framework.web.exception;
import cn.hutool.core.lang.Validator;
import cn.hutool.http.HttpStatus;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.exception.BaseException;
import com.ruoyi.common.exception.CustomException;
import com.ruoyi.common.exception.DemoModeException;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.authentication.AccountExpiredException;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.validation.BindException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.NoHandlerFoundException;
import javax.servlet.http.HttpServletRequest;
import javax.validation.ConstraintViolationException;
/**
 * å…¨å±€å¼‚常处理器
 *
 *
 * @author ruoyi
 */
@RestControllerAdvice
@@ -30,59 +28,58 @@
    private static final Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class);
    /**
     * åŸºç¡€å¼‚常
     * æƒé™æ ¡éªŒå¼‚常
     */
    @ExceptionHandler(BaseException.class)
    public AjaxResult baseException(BaseException e)
    @ExceptionHandler(AccessDeniedException.class)
    public AjaxResult handleAccessDeniedException(AccessDeniedException e, HttpServletRequest request)
    {
        String requestURI = request.getRequestURI();
        log.error("请求地址'{}',权限校验失败'{}'", requestURI, e.getMessage());
        return AjaxResult.error(HttpStatus.HTTP_FORBIDDEN, "没有权限,请联系管理员授权");
    }
    /**
     * è¯·æ±‚方式不支持
     */
    @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
    public AjaxResult handleHttpRequestMethodNotSupported(HttpRequestMethodNotSupportedException e,
            HttpServletRequest request)
    {
        String requestURI = request.getRequestURI();
        log.error("请求地址'{}',不支持'{}'请求", requestURI, e.getMethod());
        return AjaxResult.error(e.getMessage());
    }
    /**
     * ä¸šåС异叏
     */
    @ExceptionHandler(CustomException.class)
    public AjaxResult businessException(CustomException e)
    {
        if (Validator.isNull(e.getCode()))
        {
            return AjaxResult.error(e.getMessage());
        }
        return AjaxResult.error(e.getCode(), e.getMessage());
    }
    @ExceptionHandler(NoHandlerFoundException.class)
    public AjaxResult handlerNoFoundException(Exception e)
    @ExceptionHandler(ServiceException.class)
    public AjaxResult handleServiceException(ServiceException e, HttpServletRequest request)
    {
        log.error(e.getMessage(), e);
        return AjaxResult.error(HttpStatus.HTTP_NOT_FOUND, "路径不存在,请检查路径是否正确");
        Integer code = e.getCode();
        return StringUtils.isNotNull(code) ? AjaxResult.error(code, e.getMessage()) : AjaxResult.error(e.getMessage());
    }
    @ExceptionHandler(AccessDeniedException.class)
    public AjaxResult handleAuthorizationException(AccessDeniedException e)
    /**
     * æ‹¦æˆªæœªçŸ¥çš„运行时异常
     */
    @ExceptionHandler(RuntimeException.class)
    public AjaxResult handleRuntimeException(RuntimeException e, HttpServletRequest request)
    {
        log.error(e.getMessage());
        return AjaxResult.error(HttpStatus.HTTP_FORBIDDEN, "没有权限,请联系管理员授权");
    }
    @ExceptionHandler(AccountExpiredException.class)
    public AjaxResult handleAccountExpiredException(AccountExpiredException e)
    {
        log.error(e.getMessage(), e);
        String requestURI = request.getRequestURI();
        log.error("请求地址'{}',发生未知异常.", requestURI, e);
        return AjaxResult.error(e.getMessage());
    }
    @ExceptionHandler(UsernameNotFoundException.class)
    public AjaxResult handleUsernameNotFoundException(UsernameNotFoundException e)
    {
        log.error(e.getMessage(), e);
        return AjaxResult.error(e.getMessage());
    }
    /**
     * ç³»ç»Ÿå¼‚常
     */
    @ExceptionHandler(Exception.class)
    public AjaxResult handleException(Exception e)
    public AjaxResult handleException(Exception e, HttpServletRequest request)
    {
        log.error(e.getMessage(), e);
        String requestURI = request.getRequestURI();
        log.error("请求地址'{}',发生系统异常.", requestURI, e);
        return AjaxResult.error(e.getMessage());
    }
@@ -90,7 +87,7 @@
     * è‡ªå®šä¹‰éªŒè¯å¼‚常
     */
    @ExceptionHandler(BindException.class)
    public AjaxResult validatedBindException(BindException e)
    public AjaxResult handleBindException(BindException e)
    {
        log.error(e.getMessage(), e);
        String message = e.getAllErrors().get(0).getDefaultMessage();
@@ -111,7 +108,7 @@
     * è‡ªå®šä¹‰éªŒè¯å¼‚常
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public Object validExceptionHandler(MethodArgumentNotValidException e)
    public Object handleMethodArgumentNotValidException(MethodArgumentNotValidException e)
    {
        log.error(e.getMessage(), e);
        String message = e.getBindingResult().getFieldError().getDefaultMessage();
@@ -122,7 +119,7 @@
     * æ¼”示模式异常
     */
    @ExceptionHandler(DemoModeException.class)
    public AjaxResult demoModeException(DemoModeException e)
    public AjaxResult handleDemoModeException(DemoModeException e)
    {
        return AjaxResult.error("演示模式,不允许操作");
    }
ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/AsyncService.java
@@ -1,5 +1,6 @@
package com.ruoyi.framework.web.service;
import com.ruoyi.common.utils.StringUtils;
import cn.hutool.http.useragent.UserAgent;
import cn.hutool.http.useragent.UserAgentUtil;
import com.ruoyi.common.constant.Constants;
@@ -67,7 +68,7 @@
        logininfor.setOs(os);
        logininfor.setMsg(message);
        // æ—¥å¿—状态
        if (Constants.LOGIN_SUCCESS.equals(status) || Constants.LOGOUT.equals(status)) {
        if (StringUtils.equalsAny(status, Constants.LOGIN_SUCCESS, Constants.LOGOUT, Constants.REGISTER)) {
            logininfor.setStatus(Constants.SUCCESS);
        } else if (Constants.LOGIN_FAIL.equals(status)) {
            logininfor.setStatus(Constants.FAIL);
ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/PermissionService.java
@@ -1,18 +1,18 @@
package com.ruoyi.framework.web.service;
import cn.hutool.core.lang.Validator;
import cn.hutool.core.util.StrUtil;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.core.domain.entity.SysRole;
import com.ruoyi.common.core.domain.model.LoginUser;
import com.ruoyi.common.utils.ServletUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import java.util.Set;
/**
 * RuoYi首创 è‡ªå®šä¹‰æƒé™å®žçŽ°ï¼Œss取自SpringSecurity首字母
 *
 *
 * @author ruoyi
 */
@Service("ss")
@@ -33,18 +33,18 @@
    /**
     * éªŒè¯ç”¨æˆ·æ˜¯å¦å…·å¤‡æŸæƒé™
     *
     *
     * @param permission æƒé™å­—符串
     * @return ç”¨æˆ·æ˜¯å¦å…·å¤‡æŸæƒé™
     */
    public boolean hasPermi(String permission)
    {
        if (Validator.isEmpty(permission))
        if (StringUtils.isEmpty(permission))
        {
            return false;
        }
        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
        if (Validator.isNull(loginUser) || Validator.isEmpty(loginUser.getPermissions()))
        if (StringUtils.isNull(loginUser) || CollectionUtils.isEmpty(loginUser.getPermissions()))
        {
            return false;
        }
@@ -70,12 +70,12 @@
     */
    public boolean hasAnyPermi(String permissions)
    {
        if (Validator.isEmpty(permissions))
        if (StringUtils.isEmpty(permissions))
        {
            return false;
        }
        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
        if (Validator.isNull(loginUser) || Validator.isEmpty(loginUser.getPermissions()))
        if (StringUtils.isNull(loginUser) || CollectionUtils.isEmpty(loginUser.getPermissions()))
        {
            return false;
        }
@@ -92,25 +92,25 @@
    /**
     * åˆ¤æ–­ç”¨æˆ·æ˜¯å¦æ‹¥æœ‰æŸä¸ªè§’色
     *
     *
     * @param role è§’色字符串
     * @return ç”¨æˆ·æ˜¯å¦å…·å¤‡æŸè§’色
     */
    public boolean hasRole(String role)
    {
        if (Validator.isEmpty(role))
        if (StringUtils.isEmpty(role))
        {
            return false;
        }
        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
        if (Validator.isNull(loginUser) || Validator.isEmpty(loginUser.getUser().getRoles()))
        if (StringUtils.isNull(loginUser) || CollectionUtils.isEmpty(loginUser.getUser().getRoles()))
        {
            return false;
        }
        for (SysRole sysRole : loginUser.getUser().getRoles())
        {
            String roleKey = sysRole.getRoleKey();
            if (SUPER_ADMIN.equals(roleKey) || roleKey.equals(StrUtil.trim(role)))
            if (SUPER_ADMIN.equals(roleKey) || roleKey.equals(StringUtils.trim(role)))
            {
                return true;
            }
@@ -137,12 +137,12 @@
     */
    public boolean hasAnyRoles(String roles)
    {
        if (Validator.isEmpty(roles))
        if (StringUtils.isEmpty(roles))
        {
            return false;
        }
        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
        if (Validator.isNull(loginUser) || Validator.isEmpty(loginUser.getUser().getRoles()))
        if (StringUtils.isNull(loginUser) || CollectionUtils.isEmpty(loginUser.getUser().getRoles()))
        {
            return false;
        }
@@ -158,13 +158,13 @@
    /**
     * åˆ¤æ–­æ˜¯å¦åŒ…含权限
     *
     *
     * @param permissions æƒé™åˆ—表
     * @param permission æƒé™å­—符串
     * @return ç”¨æˆ·æ˜¯å¦å…·å¤‡æŸæƒé™
     */
    private boolean hasPermissions(Set<String> permissions, String permission)
    {
        return permissions.contains(ALL_PERMISSION) || permissions.contains(StrUtil.trim(permission));
        return permissions.contains(ALL_PERMISSION) || permissions.contains(StringUtils.trim(permission));
    }
}
ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/SysLoginService.java
@@ -4,7 +4,7 @@
import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.core.domain.model.LoginUser;
import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.common.exception.CustomException;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.exception.user.CaptchaException;
import com.ruoyi.common.exception.user.CaptchaExpireException;
import com.ruoyi.common.exception.user.UserPasswordNotMatchException;
@@ -85,7 +85,7 @@
            else
            {
                asyncService.recordLogininfor(username, Constants.LOGIN_FAIL, e.getMessage(), request);
                throw new CustomException(e.getMessage());
                throw new ServiceException(e.getMessage());
            }
        }
        asyncService.recordLogininfor(username, Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success"), request);
ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/SysRegisterService.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,117 @@
package com.ruoyi.framework.web.service;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.constant.UserConstants;
import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.core.domain.model.RegisterBody;
import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.common.exception.user.CaptchaException;
import com.ruoyi.common.exception.user.CaptchaExpireException;
import com.ruoyi.common.utils.MessageUtils;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.ServletUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.system.service.ISysConfigService;
import com.ruoyi.system.service.ISysUserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
/**
 * æ³¨å†Œæ ¡éªŒæ–¹æ³•
 *
 * @author ruoyi
 */
@Component
public class SysRegisterService
{
    @Autowired
    private ISysUserService userService;
    @Autowired
    private ISysConfigService configService;
    @Autowired
    private RedisCache redisCache;
    @Autowired
    private AsyncService asyncService;
    /**
     * æ³¨å†Œ
     */
    public String register(RegisterBody registerBody)
    {
        String msg = "", username = registerBody.getUsername(), password = registerBody.getPassword();
        boolean captchaOnOff = configService.selectCaptchaOnOff();
        // éªŒè¯ç å¼€å…³
        if (captchaOnOff)
        {
            validateCaptcha(username, registerBody.getCode(), registerBody.getUuid());
        }
        if (StringUtils.isEmpty(username))
        {
            msg = "用户名不能为空";
        }
        else if (StringUtils.isEmpty(password))
        {
            msg = "用户密码不能为空";
        }
        else if (username.length() < UserConstants.USERNAME_MIN_LENGTH
                || username.length() > UserConstants.USERNAME_MAX_LENGTH)
        {
            msg = "账户长度必须在2到20个字符之间";
        }
        else if (password.length() < UserConstants.PASSWORD_MIN_LENGTH
                || password.length() > UserConstants.PASSWORD_MAX_LENGTH)
        {
            msg = "密码长度必须在5到20个字符之间";
        }
        else if (UserConstants.NOT_UNIQUE.equals(userService.checkUserNameUnique(username)))
        {
            msg = "保存用户'" + username + "'失败,注册账号已存在";
        }
        else
        {
            SysUser sysUser = new SysUser();
            sysUser.setUserName(username);
            sysUser.setNickName(username);
            sysUser.setPassword(SecurityUtils.encryptPassword(registerBody.getPassword()));
            boolean regFlag = userService.registerUser(sysUser);
            if (!regFlag)
            {
                msg = "注册失败,请联系系统管理人员";
            }
            else
            {
                asyncService.recordLogininfor(username, Constants.REGISTER,
                        MessageUtils.message("user.register.success"), ServletUtils.getRequest());
            }
        }
        return msg;
    }
    /**
     * æ ¡éªŒéªŒè¯ç 
     *
     * @param username ç”¨æˆ·å
     * @param code éªŒè¯ç 
     * @param uuid å”¯ä¸€æ ‡è¯†
     * @return ç»“æžœ
     */
    public void validateCaptcha(String username, String code, String uuid)
    {
        String verifyKey = Constants.CAPTCHA_CODE_KEY + uuid;
        String captcha = redisCache.getCacheObject(verifyKey);
        redisCache.deleteObject(verifyKey);
        if (captcha == null)
        {
            throw new CaptchaExpireException();
        }
        if (!code.equalsIgnoreCase(captcha))
        {
            throw new CaptchaException();
        }
    }
}
ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/TokenService.java
@@ -1,6 +1,5 @@
package com.ruoyi.framework.web.service;
import cn.hutool.core.lang.Validator;
import cn.hutool.core.util.IdUtil;
import cn.hutool.http.useragent.UserAgent;
import cn.hutool.http.useragent.UserAgentUtil;
@@ -8,6 +7,7 @@
import com.ruoyi.common.core.domain.model.LoginUser;
import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.common.utils.ServletUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.ip.AddressUtils;
import com.ruoyi.framework.config.properties.TokenProperties;
import io.jsonwebtoken.Claims;
@@ -49,13 +49,17 @@
    public LoginUser getLoginUser(HttpServletRequest request) {
        // èŽ·å–è¯·æ±‚æºå¸¦çš„ä»¤ç‰Œ
        String token = getToken(request);
        if (Validator.isNotEmpty(token)) {
            Claims claims = parseToken(token);
            // è§£æžå¯¹åº”的权限以及用户信息
            String uuid = (String) claims.get(Constants.LOGIN_USER_KEY);
            String userKey = getTokenKey(uuid);
            LoginUser user = redisCache.getCacheObject(userKey);
            return user;
        if (StringUtils.isNotEmpty(token)) {
            try {
                Claims claims = parseToken(token);
                // è§£æžå¯¹åº”的权限以及用户信息
                String uuid = (String) claims.get(Constants.LOGIN_USER_KEY);
                String userKey = getTokenKey(uuid);
                LoginUser user = redisCache.getCacheObject(userKey);
                return user;
            } catch (Exception e) {
            }
        }
        return null;
    }
@@ -64,7 +68,7 @@
     * è®¾ç½®ç”¨æˆ·èº«ä»½ä¿¡æ¯
     */
    public void setLoginUser(LoginUser loginUser) {
        if (Validator.isNotNull(loginUser) && Validator.isNotEmpty(loginUser.getToken())) {
        if (StringUtils.isNotNull(loginUser) && StringUtils.isNotEmpty(loginUser.getToken())) {
            refreshToken(loginUser);
        }
    }
@@ -73,7 +77,7 @@
     * åˆ é™¤ç”¨æˆ·èº«ä»½ä¿¡æ¯
     */
    public void delLoginUser(String token) {
        if (Validator.isNotEmpty(token)) {
        if (StringUtils.isNotEmpty(token)) {
            String userKey = getTokenKey(token);
            redisCache.deleteObject(userKey);
        }
@@ -182,7 +186,7 @@
     */
    private String getToken(HttpServletRequest request) {
        String token = request.getHeader(tokenProperties.getHeader());
        if (Validator.isNotEmpty(token) && token.startsWith(Constants.TOKEN_PREFIX)) {
        if (StringUtils.isNotEmpty(token) && token.startsWith(Constants.TOKEN_PREFIX)) {
            token = token.replace(Constants.TOKEN_PREFIX, "");
        }
        return token;
ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/UserDetailsServiceImpl.java
@@ -1,11 +1,5 @@
package com.ruoyi.framework.web.service;
import cn.hutool.core.lang.Validator;
import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.core.domain.model.LoginUser;
import com.ruoyi.common.enums.UserStatus;
import com.ruoyi.common.exception.BaseException;
import com.ruoyi.system.service.ISysUserService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
@@ -13,6 +7,12 @@
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.core.domain.model.LoginUser;
import com.ruoyi.common.enums.UserStatus;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.system.service.ISysUserService;
/**
 * ç”¨æˆ·éªŒè¯å¤„理
@@ -34,20 +34,20 @@
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException
    {
        SysUser user = userService.selectUserByUserName(username);
        if (Validator.isNull(user))
        if (StringUtils.isNull(user))
        {
            log.info("登录用户:{} ä¸å­˜åœ¨.", username);
            throw new UsernameNotFoundException("登录用户:" + username + " ä¸å­˜åœ¨");
            throw new ServiceException("登录用户:" + username + " ä¸å­˜åœ¨");
        }
        else if (UserStatus.DELETED.getCode().equals(user.getDelFlag()))
        {
            log.info("登录用户:{} å·²è¢«åˆ é™¤.", username);
            throw new BaseException("对不起,您的账号:" + username + " å·²è¢«åˆ é™¤");
            throw new ServiceException("对不起,您的账号:" + username + " å·²è¢«åˆ é™¤");
        }
        else if (UserStatus.DISABLE.getCode().equals(user.getStatus()))
        {
            log.info("登录用户:{} å·²è¢«åœç”¨.", username);
            throw new BaseException("对不起,您的账号:" + username + " å·²åœç”¨");
            throw new ServiceException("对不起,您的账号:" + username + " å·²åœç”¨");
        }
        return createLoginUser(user);
@@ -55,6 +55,6 @@
    public UserDetails createLoginUser(SysUser user)
    {
        return new LoginUser(user, permissionService.getMenuPermission(user));
        return new LoginUser(user.getUserId(), user.getDeptId(), user, permissionService.getMenuPermission(user));
    }
}
ruoyi-generator/pom.xml
@@ -5,7 +5,7 @@
    <parent>
        <artifactId>ruoyi-vue-plus</artifactId>
        <groupId>com.ruoyi</groupId>
        <version>2.6.0</version>
        <version>3.0.0</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>
ruoyi-generator/src/main/java/com/ruoyi/generator/domain/GenTable.java
@@ -1,6 +1,6 @@
package com.ruoyi.generator.domain;
import cn.hutool.core.util.StrUtil;
import com.ruoyi.common.utils.StringUtils;
import com.baomidou.mybatisplus.annotation.*;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.ruoyi.common.constant.GenConstants;
@@ -204,7 +204,7 @@
    }
    public static boolean isSub(String tplCategory) {
        return tplCategory != null && StrUtil.equals(GenConstants.TPL_SUB, tplCategory);
        return tplCategory != null && StringUtils.equals(GenConstants.TPL_SUB, tplCategory);
    }
    public boolean isTree() {
@@ -212,7 +212,7 @@
    }
    public static boolean isTree(String tplCategory) {
        return tplCategory != null && StrUtil.equals(GenConstants.TPL_TREE, tplCategory);
        return tplCategory != null && StringUtils.equals(GenConstants.TPL_TREE, tplCategory);
    }
    public boolean isCrud() {
@@ -220,7 +220,7 @@
    }
    public static boolean isCrud(String tplCategory) {
        return tplCategory != null && StrUtil.equals(GenConstants.TPL_CRUD, tplCategory);
        return tplCategory != null && StringUtils.equals(GenConstants.TPL_CRUD, tplCategory);
    }
    public boolean isSuperColumn(String javaField) {
@@ -229,9 +229,9 @@
    public static boolean isSuperColumn(String tplCategory, String javaField) {
        if (isTree(tplCategory)) {
            return StrUtil.equalsAnyIgnoreCase(javaField,
            return StringUtils.equalsAnyIgnoreCase(javaField,
                    ArrayUtils.addAll(GenConstants.TREE_ENTITY, GenConstants.BASE_ENTITY));
        }
        return StrUtil.equalsAnyIgnoreCase(javaField, GenConstants.BASE_ENTITY);
        return StringUtils.equalsAnyIgnoreCase(javaField, GenConstants.BASE_ENTITY);
    }
}
}
ruoyi-generator/src/main/java/com/ruoyi/generator/domain/GenTableColumn.java
@@ -1,6 +1,6 @@
package com.ruoyi.generator.domain;
import cn.hutool.core.util.StrUtil;
import com.ruoyi.common.utils.StringUtils;
import com.baomidou.mybatisplus.annotation.*;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.*;
@@ -150,7 +150,7 @@
    private Map<String, Object> params = new HashMap<>();
    public String getCapJavaField() {
        return StrUtil.upperFirst(javaField);
        return StringUtils.uncapitalize(javaField);
    }
    public boolean isPk() {
@@ -158,7 +158,7 @@
    }
    public boolean isPk(String isPk) {
        return isPk != null && StrUtil.equals("1", isPk);
        return isPk != null && StringUtils.equals("1", isPk);
    }
    public boolean isIncrement() {
@@ -166,7 +166,7 @@
    }
    public boolean isIncrement(String isIncrement) {
        return isIncrement != null && StrUtil.equals("1", isIncrement);
        return isIncrement != null && StringUtils.equals("1", isIncrement);
    }
    public boolean isRequired() {
@@ -174,7 +174,7 @@
    }
    public boolean isRequired(String isRequired) {
        return isRequired != null && StrUtil.equals("1", isRequired);
        return isRequired != null && StringUtils.equals("1", isRequired);
    }
    public boolean isInsert() {
@@ -182,7 +182,7 @@
    }
    public boolean isInsert(String isInsert) {
        return isInsert != null && StrUtil.equals("1", isInsert);
        return isInsert != null && StringUtils.equals("1", isInsert);
    }
    public boolean isEdit() {
@@ -190,7 +190,7 @@
    }
    public boolean isEdit(String isEdit) {
        return isEdit != null && StrUtil.equals("1", isEdit);
        return isEdit != null && StringUtils.equals("1", isEdit);
    }
    public boolean isList() {
@@ -198,7 +198,7 @@
    }
    public boolean isList(String isList) {
        return isList != null && StrUtil.equals("1", isList);
        return isList != null && StringUtils.equals("1", isList);
    }
    public boolean isQuery() {
@@ -206,7 +206,7 @@
    }
    public boolean isQuery(String isQuery) {
        return isQuery != null && StrUtil.equals("1", isQuery);
        return isQuery != null && StringUtils.equals("1", isQuery);
    }
    public boolean isSuperColumn() {
@@ -214,7 +214,7 @@
    }
    public static boolean isSuperColumn(String javaField) {
        return StrUtil.equalsAnyIgnoreCase(javaField,
        return StringUtils.equalsAnyIgnoreCase(javaField,
                // BaseEntity
                "createBy", "createTime", "updateBy", "updateTime", "remark",
                // TreeEntity
@@ -227,15 +227,15 @@
    public static boolean isUsableColumn(String javaField) {
        // isSuperColumn()中的名单用于避免生成多余Domain属性,若某些属性在生成页面时需要用到不能忽略,则放在此处白名单
        return StrUtil.equalsAnyIgnoreCase(javaField, "parentId", "orderNum", "remark");
        return StringUtils.equalsAnyIgnoreCase(javaField, "parentId", "orderNum", "remark");
    }
    public String readConverterExp() {
        String remarks = StrUtil.subBetween(this.columnComment, "(", ")");
        String remarks = StringUtils.substringBetween(this.columnComment, "(", ")");
        StringBuffer sb = new StringBuffer();
        if (StrUtil.isNotEmpty(remarks)) {
        if (StringUtils.isNotEmpty(remarks)) {
            for (String value : remarks.split(" ")) {
                if (StrUtil.isNotEmpty(value)) {
                if (StringUtils.isNotEmpty(value)) {
                    Object startStr = value.subSequence(0, 1);
                    String endStr = value.substring(1);
                    sb.append("").append(startStr).append("=").append(endStr).append(",");
ruoyi-generator/src/main/java/com/ruoyi/generator/service/GenTableServiceImpl.java
@@ -2,15 +2,14 @@
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 com.ruoyi.common.utils.StringUtils;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.constant.GenConstants;
import com.ruoyi.common.core.mybatisplus.core.ServicePlusImpl;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.exception.CustomException;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.JsonUtils;
import com.ruoyi.common.utils.PageUtils;
import com.ruoyi.common.utils.SecurityUtils;
@@ -137,15 +136,15 @@
            for (GenTableColumn cenTableColumn : genTable.getColumns()) {
                genTableColumnMapper.update(cenTableColumn,
                        new LambdaUpdateWrapper<GenTableColumn>()
                            .set(StrUtil.isBlank(cenTableColumn.getColumnComment()), GenTableColumn::getColumnComment, null)
                            .set(StrUtil.isBlank(cenTableColumn.getIsPk()), GenTableColumn::getIsPk, null)
                            .set(StrUtil.isBlank(cenTableColumn.getIsIncrement()), GenTableColumn::getIsIncrement, null)
                            .set(StrUtil.isBlank(cenTableColumn.getIsInsert()), GenTableColumn::getIsInsert, null)
                            .set(StrUtil.isBlank(cenTableColumn.getIsEdit()), GenTableColumn::getIsEdit, null)
                            .set(StrUtil.isBlank(cenTableColumn.getIsList()), GenTableColumn::getIsList, null)
                            .set(StrUtil.isBlank(cenTableColumn.getIsQuery()), GenTableColumn::getIsQuery, null)
                            .set(StrUtil.isBlank(cenTableColumn.getIsRequired()), GenTableColumn::getIsRequired, null)
                            .set(StrUtil.isBlank(cenTableColumn.getDictType()), GenTableColumn::getDictType, "")
                            .set(StringUtils.isBlank(cenTableColumn.getColumnComment()), GenTableColumn::getColumnComment, null)
                            .set(StringUtils.isBlank(cenTableColumn.getIsPk()), GenTableColumn::getIsPk, null)
                            .set(StringUtils.isBlank(cenTableColumn.getIsIncrement()), GenTableColumn::getIsIncrement, null)
                            .set(StringUtils.isBlank(cenTableColumn.getIsInsert()), GenTableColumn::getIsInsert, null)
                            .set(StringUtils.isBlank(cenTableColumn.getIsEdit()), GenTableColumn::getIsEdit, null)
                            .set(StringUtils.isBlank(cenTableColumn.getIsList()), GenTableColumn::getIsList, null)
                            .set(StringUtils.isBlank(cenTableColumn.getIsQuery()), GenTableColumn::getIsQuery, null)
                            .set(StringUtils.isBlank(cenTableColumn.getIsRequired()), GenTableColumn::getIsRequired, null)
                            .set(StringUtils.isBlank(cenTableColumn.getDictType()), GenTableColumn::getDictType, "")
                            .eq(GenTableColumn::getColumnId,cenTableColumn.getColumnId()));
            }
        }
@@ -189,7 +188,7 @@
                }
            }
        } catch (Exception e) {
            throw new CustomException("导入失败:" + e.getMessage());
            throw new ServiceException("导入失败:" + e.getMessage());
        }
    }
@@ -260,13 +259,17 @@
        // èŽ·å–æ¨¡æ¿åˆ—è¡¨
        List<String> templates = VelocityUtils.getTemplateList(table.getTplCategory());
        for (String template : templates) {
            if (!StrUtil.containsAny("sql.vm", "api.js.vm", "index.vue.vm", "index-tree.vue.vm", template)) {
            if (!StringUtils.containsAny(template, "sql.vm", "api.js.vm", "index.vue.vm", "index-tree.vue.vm")) {
                // æ¸²æŸ“模板
                StringWriter sw = new StringWriter();
                Template tpl = Velocity.getTemplate(template, Constants.UTF8);
                tpl.merge(context, sw);
                String path = getGenPath(table, template);
                FileUtils.writeUtf8String(sw.toString(), path);
                try {
                    String path = getGenPath(table, template);
                    FileUtils.writeUtf8String(sw.toString(), path);
                } catch (Exception e) {
                    throw new ServiceException("渲染模板失败,表名:" + table.getTableName());
                }
            }
        }
    }
@@ -284,8 +287,8 @@
        List<String> tableColumnNames = tableColumns.stream().map(GenTableColumn::getColumnName).collect(Collectors.toList());
        List<GenTableColumn> dbTableColumns = genTableColumnMapper.selectDbTableColumnsByName(tableName);
        if (Validator.isEmpty(dbTableColumns)) {
            throw new CustomException("同步数据失败,原表结构不存在");
        if (StringUtils.isEmpty(dbTableColumns)) {
            throw new ServiceException("同步数据失败,原表结构不存在");
        }
        List<String> dbTableColumnNames = dbTableColumns.stream().map(GenTableColumn::getColumnName).collect(Collectors.toList());
@@ -364,17 +367,17 @@
    public void validateEdit(GenTable genTable) {
        if (GenConstants.TPL_TREE.equals(genTable.getTplCategory())) {
            Map<String, Object> paramsObj = genTable.getParams();
            if (Validator.isEmpty(paramsObj.get(GenConstants.TREE_CODE))) {
                throw new CustomException("树编码字段不能为空");
            } else if (Validator.isEmpty(paramsObj.get(GenConstants.TREE_PARENT_CODE))) {
                throw new CustomException("树父编码字段不能为空");
            } else if (Validator.isEmpty(paramsObj.get(GenConstants.TREE_NAME))) {
                throw new CustomException("树名称字段不能为空");
            if (StringUtils.isEmpty(paramsObj.get(GenConstants.TREE_CODE))) {
                throw new ServiceException("树编码字段不能为空");
            } else if (StringUtils.isEmpty(paramsObj.get(GenConstants.TREE_PARENT_CODE))) {
                throw new ServiceException("树父编码字段不能为空");
            } else if (StringUtils.isEmpty(paramsObj.get(GenConstants.TREE_NAME))) {
                throw new ServiceException("树名称字段不能为空");
            } else if (GenConstants.TPL_SUB.equals(genTable.getTplCategory())) {
                if (Validator.isEmpty(genTable.getSubTableName())) {
                    throw new CustomException("关联子表的表名不能为空");
                } else if (Validator.isEmpty(genTable.getSubTableFkName())) {
                    throw new CustomException("子表关联的外键名不能为空");
                if (StringUtils.isEmpty(genTable.getSubTableName())) {
                    throw new ServiceException("关联子表的表名不能为空");
                } else if (StringUtils.isEmpty(genTable.getSubTableFkName())) {
                    throw new ServiceException("子表关联的外键名不能为空");
                }
            }
        }
@@ -392,7 +395,7 @@
                break;
            }
        }
        if (Validator.isNull(table.getPkColumn())) {
        if (StringUtils.isNull(table.getPkColumn())) {
            table.setPkColumn(table.getColumns().get(0));
        }
        if (GenConstants.TPL_SUB.equals(table.getTplCategory())) {
@@ -402,7 +405,7 @@
                    break;
                }
            }
            if (Validator.isNull(table.getSubTable().getPkColumn())) {
            if (StringUtils.isNull(table.getSubTable().getPkColumn())) {
                table.getSubTable().setPkColumn(table.getSubTable().getColumns().get(0));
            }
        }
@@ -415,7 +418,7 @@
     */
    public void setSubTable(GenTable table) {
        String subTableName = table.getSubTableName();
        if (Validator.isNotEmpty(subTableName)) {
        if (StringUtils.isNotEmpty(subTableName)) {
            table.setSubTable(baseMapper.selectGenTableByName(subTableName));
        }
    }
@@ -427,7 +430,7 @@
     */
    public void setTableFromOptions(GenTable genTable) {
        Map<String, Object> paramsObj = JsonUtils.parseMap(genTable.getOptions());
        if (Validator.isNotNull(paramsObj)) {
        if (StringUtils.isNotNull(paramsObj)) {
            String treeCode = Convert.toStr(paramsObj.get(GenConstants.TREE_CODE));
            String treeParentCode = Convert.toStr(paramsObj.get(GenConstants.TREE_PARENT_CODE));
            String treeName = Convert.toStr(paramsObj.get(GenConstants.TREE_NAME));
@@ -451,7 +454,7 @@
     */
    public static String getGenPath(GenTable table, String template) {
        String genPath = table.getGenPath();
        if (StrUtil.equals(genPath, "/")) {
        if (StringUtils.equals(genPath, "/")) {
            return System.getProperty("user.dir") + File.separator + "src" + File.separator + VelocityUtils.getFileName(template, table);
        }
        return genPath + File.separator + VelocityUtils.getFileName(template, table);
ruoyi-generator/src/main/java/com/ruoyi/generator/util/GenUtils.java
@@ -1,6 +1,6 @@
package com.ruoyi.generator.util;
import cn.hutool.core.util.StrUtil;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.constant.GenConstants;
import com.ruoyi.generator.config.GenConfig;
import com.ruoyi.generator.domain.GenTable;
@@ -40,7 +40,7 @@
        column.setTableId(table.getTableId());
        column.setCreateBy(table.getCreateBy());
        // è®¾ç½®java字段名
        column.setJavaField(StrUtil.toCamelCase(columnName));
        column.setJavaField(StringUtils.toCamelCase(columnName));
        // è®¾ç½®é»˜è®¤ç±»åž‹
        column.setJavaType(GenConstants.TYPE_STRING);
@@ -61,7 +61,7 @@
            column.setHtmlType(GenConstants.HTML_INPUT);
            // å¦‚果是浮点型 ç»Ÿä¸€ç”¨BigDecimal
            String[] str = StrUtil.splitToArray(StrUtil.subBetween(column.getColumnType(), "(", ")"), ",");
            String[] str = StringUtils.split(StringUtils.substringBetween(column.getColumnType(), "(", ")"), ",");
            if (str != null && str.length == 2 && Integer.parseInt(str[1]) > 0)
            {
                column.setJavaType(GenConstants.TYPE_BIGDECIMAL);
@@ -98,33 +98,33 @@
        }
        // æŸ¥è¯¢å­—段类型
        if (StrUtil.endWithIgnoreCase(columnName, "name"))
        if (StringUtils.endsWithIgnoreCase(columnName, "name"))
        {
            column.setQueryType(GenConstants.QUERY_LIKE);
        }
        // çŠ¶æ€å­—æ®µè®¾ç½®å•é€‰æ¡†
        if (StrUtil.endWithIgnoreCase(columnName, "status"))
        if (StringUtils.endsWithIgnoreCase(columnName, "status"))
        {
            column.setHtmlType(GenConstants.HTML_RADIO);
        }
        // ç±»åž‹&性别字段设置下拉框
        else if (StrUtil.endWithIgnoreCase(columnName, "type")
                || StrUtil.endWithIgnoreCase(columnName, "sex"))
        else if (StringUtils.endsWithIgnoreCase(columnName, "type")
                || StringUtils.endsWithIgnoreCase(columnName, "sex"))
        {
            column.setHtmlType(GenConstants.HTML_SELECT);
        }
        // å›¾ç‰‡å­—段设置图片上传控件
        else if (StrUtil.endWithIgnoreCase(columnName, "image"))
        else if (StringUtils.endsWithIgnoreCase(columnName, "image"))
        {
            column.setHtmlType(GenConstants.HTML_IMAGE_UPLOAD);
        }
        // æ–‡ä»¶å­—段设置文件上传控件
        else if (StrUtil.endWithIgnoreCase(columnName, "file"))
        else if (StringUtils.endsWithIgnoreCase(columnName, "file"))
        {
            column.setHtmlType(GenConstants.HTML_FILE_UPLOAD);
        }
        // å†…容字段设置富文本控件
        else if (StrUtil.endWithIgnoreCase(columnName, "content"))
        else if (StringUtils.endsWithIgnoreCase(columnName, "content"))
        {
            column.setHtmlType(GenConstants.HTML_EDITOR);
        }
@@ -152,7 +152,7 @@
    {
        int lastIndex = packageName.lastIndexOf(".");
        int nameLength = packageName.length();
        String moduleName = StrUtil.sub(packageName, lastIndex + 1, nameLength);
        String moduleName = StringUtils.substring(packageName, lastIndex + 1, nameLength);
        return moduleName;
    }
@@ -166,7 +166,7 @@
    {
        int lastIndex = tableName.lastIndexOf("_");
        int nameLength = tableName.length();
        String businessName = StrUtil.sub(tableName, lastIndex + 1, nameLength);
        String businessName = StringUtils.substring(tableName, lastIndex + 1, nameLength);
        return businessName;
    }
@@ -180,12 +180,12 @@
    {
        boolean autoRemovePre = GenConfig.getAutoRemovePre();
        String tablePrefix = GenConfig.getTablePrefix();
        if (autoRemovePre && StrUtil.isNotEmpty(tablePrefix))
        if (autoRemovePre && StringUtils.isNotEmpty(tablePrefix))
        {
            String[] searchList = StrUtil.splitToArray(tablePrefix, ",");
            String[] searchList = StringUtils.split(tablePrefix, ",");
            tableName = replaceFirst(tableName, searchList);
        }
        return StrUtil.upperFirst(StrUtil.toCamelCase(tableName));
        return StringUtils.convertToCamelCase(tableName);
    }
    /**
@@ -228,9 +228,9 @@
     */
    public static String getDbType(String columnType)
    {
        if (StrUtil.indexOf(columnType, '(') > 0)
        if (StringUtils.indexOf(columnType, '(') > 0)
        {
            return StrUtil.subBefore(columnType, "(",false);
            return StringUtils.substringBefore(columnType, "(");
        }
        else
        {
@@ -246,9 +246,9 @@
     */
    public static Integer getColumnLength(String columnType)
    {
        if (StrUtil.indexOf(columnType, '(') > 0)
        if (StringUtils.indexOf(columnType, '(') > 0)
        {
            String length = StrUtil.subBetween(columnType, "(", ")");
            String length = StringUtils.substringBetween(columnType, "(", ")");
            return Integer.valueOf(length);
        }
        else
ruoyi-generator/src/main/java/com/ruoyi/generator/util/VelocityUtils.java
@@ -1,11 +1,10 @@
package com.ruoyi.generator.util;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.lang.Validator;
import cn.hutool.core.util.StrUtil;
import com.ruoyi.common.constant.GenConstants;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.JsonUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.generator.domain.GenTable;
import com.ruoyi.generator.domain.GenTableColumn;
import org.apache.velocity.VelocityContext;
@@ -47,11 +46,11 @@
        VelocityContext velocityContext = new VelocityContext();
        velocityContext.put("tplCategory", genTable.getTplCategory());
        velocityContext.put("tableName", genTable.getTableName());
        velocityContext.put("functionName", StrUtil.isNotEmpty(functionName) ? functionName : "【请填写功能名称】");
        velocityContext.put("functionName", StringUtils.isNotEmpty(functionName) ? functionName : "【请填写功能名称】");
        velocityContext.put("ClassName", genTable.getClassName());
        velocityContext.put("className", StrUtil.lowerFirst(genTable.getClassName()));
        velocityContext.put("className", StringUtils.uncapitalize(genTable.getClassName()));
        velocityContext.put("moduleName", genTable.getModuleName());
        velocityContext.put("BusinessName", StrUtil.upperFirst(genTable.getBusinessName()));
        velocityContext.put("BusinessName", StringUtils.capitalize(genTable.getBusinessName()));
        velocityContext.put("businessName", genTable.getBusinessName());
        velocityContext.put("basePackage", getPackagePrefix(packageName));
        velocityContext.put("packageName", packageName);
@@ -110,15 +109,15 @@
        String subTableName = genTable.getSubTableName();
        String subTableFkName = genTable.getSubTableFkName();
        String subClassName = genTable.getSubTable().getClassName();
        String subTableFkClassName = StrUtil.toCamelCase(subTableFkName);
        String subTableFkClassName = StringUtils.convertToCamelCase(subTableFkName);
        context.put("subTable", subTable);
        context.put("subTableName", subTableName);
        context.put("subTableFkName", subTableFkName);
        context.put("subTableFkClassName", subTableFkClassName);
        context.put("subTableFkclassName", StrUtil.lowerFirst(subTableFkClassName));
        context.put("subTableFkclassName", StringUtils.uncapitalize(subTableFkClassName));
        context.put("subClassName", subClassName);
        context.put("subclassName", StrUtil.lowerFirst(subClassName));
        context.put("subclassName", StringUtils.uncapitalize(subClassName));
        context.put("subImportList", getImportList(genTable.getSubTable()));
    }
@@ -172,45 +171,45 @@
        // ä¸šåŠ¡åç§°
        String businessName = genTable.getBusinessName();
        String javaPath = PROJECT_PATH + "/" + StrUtil.replace(packageName, ".", "/");
        String javaPath = PROJECT_PATH + "/" + StringUtils.replace(packageName, ".", "/");
        String mybatisPath = MYBATIS_PATH + "/" + moduleName;
        String vuePath = "vue";
        if (template.contains("domain.java.vm"))
        {
            fileName = StrUtil.format("{}/domain/{}.java", javaPath, className);
            fileName = StringUtils.format("{}/domain/{}.java", javaPath, className);
        }
        if (template.contains("vo.java.vm"))
        {
            fileName = StrUtil.format("{}/domain/vo/{}Vo.java", javaPath, className);
            fileName = StringUtils.format("{}/domain/vo/{}Vo.java", javaPath, className);
        }
        if (template.contains("bo.java.vm"))
        {
            fileName = StrUtil.format("{}/domain/bo/{}Bo.java", javaPath, className);
            fileName = StringUtils.format("{}/domain/bo/{}Bo.java", javaPath, className);
        }
        if (template.contains("sub-domain.java.vm") && StrUtil.equals(GenConstants.TPL_SUB, genTable.getTplCategory()))
        if (template.contains("sub-domain.java.vm") && StringUtils.equals(GenConstants.TPL_SUB, genTable.getTplCategory()))
        {
            fileName = StrUtil.format("{}/domain/{}.java", javaPath, genTable.getSubTable().getClassName());
            fileName = StringUtils.format("{}/domain/{}.java", javaPath, genTable.getSubTable().getClassName());
        }
        else if (template.contains("mapper.java.vm"))
        {
            fileName = StrUtil.format("{}/mapper/{}Mapper.java", javaPath, className);
            fileName = StringUtils.format("{}/mapper/{}Mapper.java", javaPath, className);
        }
        else if (template.contains("service.java.vm"))
        {
            fileName = StrUtil.format("{}/service/I{}Service.java", javaPath, className);
            fileName = StringUtils.format("{}/service/I{}Service.java", javaPath, className);
        }
        else if (template.contains("serviceImpl.java.vm"))
        {
            fileName = StrUtil.format("{}/service/impl/{}ServiceImpl.java", javaPath, className);
            fileName = StringUtils.format("{}/service/impl/{}ServiceImpl.java", javaPath, className);
        }
        else if (template.contains("controller.java.vm"))
        {
            fileName = StrUtil.format("{}/controller/{}Controller.java", javaPath, className);
            fileName = StringUtils.format("{}/controller/{}Controller.java", javaPath, className);
        }
        else if (template.contains("mapper.xml.vm"))
        {
            fileName = StrUtil.format("{}/{}Mapper.xml", mybatisPath, className);
            fileName = StringUtils.format("{}/{}Mapper.xml", mybatisPath, className);
        }
        else if (template.contains("sql.vm"))
        {
@@ -218,15 +217,15 @@
        }
        else if (template.contains("api.js.vm"))
        {
            fileName = StrUtil.format("{}/api/{}/{}.js", vuePath, moduleName, businessName);
            fileName = StringUtils.format("{}/api/{}/{}.js", vuePath, moduleName, businessName);
        }
        else if (template.contains("index.vue.vm"))
        {
            fileName = StrUtil.format("{}/views/{}/{}/index.vue", vuePath, moduleName, businessName);
            fileName = StringUtils.format("{}/views/{}/{}/index.vue", vuePath, moduleName, businessName);
        }
        else if (template.contains("index-tree.vue.vm"))
        {
            fileName = StrUtil.format("{}/views/{}/{}/index.vue", vuePath, moduleName, businessName);
            fileName = StringUtils.format("{}/views/{}/{}/index.vue", vuePath, moduleName, businessName);
        }
        return fileName;
    }
@@ -240,7 +239,7 @@
    public static String getPackagePrefix(String packageName)
    {
        int lastIndex = packageName.lastIndexOf(".");
        String basePackage = StrUtil.sub(packageName, 0, lastIndex);
        String basePackage = StringUtils.substring(packageName, 0, lastIndex);
        return basePackage;
    }
@@ -255,7 +254,7 @@
        List<GenTableColumn> columns = genTable.getColumns();
        GenTable subGenTable = genTable.getSubTable();
        HashSet<String> importList = new HashSet<String>();
        if (Validator.isNotNull(subGenTable))
        if (StringUtils.isNotNull(subGenTable))
        {
            importList.add("java.util.List");
        }
@@ -283,7 +282,7 @@
     */
    public static String getPermissionPrefix(String moduleName, String businessName)
    {
        return StrUtil.format("{}:{}", moduleName, businessName);
        return StringUtils.format("{}:{}", moduleName, businessName);
    }
    /**
@@ -294,7 +293,8 @@
     */
    public static String getParentMenuId(Map<String, Object> paramsObj)
    {
        if (Validator.isNotEmpty(paramsObj) && paramsObj.containsKey(GenConstants.PARENT_MENU_ID))
        if (StringUtils.isNotEmpty(paramsObj) && paramsObj.containsKey(GenConstants.PARENT_MENU_ID)
                && StringUtils.isNotEmpty(Convert.toStr(paramsObj.get(GenConstants.PARENT_MENU_ID))))
        {
            return Convert.toStr(paramsObj.get(GenConstants.PARENT_MENU_ID));
        }
@@ -309,11 +309,11 @@
     */
    public static String getTreecode(Map<String, Object> paramsObj)
    {
        if (Validator.isNotEmpty(paramsObj) && paramsObj.containsKey(GenConstants.TREE_CODE))
        if (StringUtils.isNotEmpty(paramsObj) && paramsObj.containsKey(GenConstants.TREE_CODE))
        {
            return StrUtil.toCamelCase(Convert.toStr(paramsObj.get(GenConstants.TREE_CODE)));
            return StringUtils.toCamelCase(Convert.toStr(paramsObj.get(GenConstants.TREE_CODE)));
        }
        return StrUtil.EMPTY;
        return StringUtils.EMPTY;
    }
    /**
@@ -324,11 +324,11 @@
     */
    public static String getTreeParentCode(Map<String, Object> paramsObj)
    {
        if (Validator.isNotEmpty(paramsObj) && paramsObj.containsKey(GenConstants.TREE_PARENT_CODE))
        if (StringUtils.isNotEmpty(paramsObj) && paramsObj.containsKey(GenConstants.TREE_PARENT_CODE))
        {
            return StrUtil.toCamelCase(Convert.toStr(paramsObj.get(GenConstants.TREE_PARENT_CODE)));
            return StringUtils.toCamelCase(Convert.toStr(paramsObj.get(GenConstants.TREE_PARENT_CODE)));
        }
        return StrUtil.EMPTY;
        return StringUtils.EMPTY;
    }
    /**
@@ -339,11 +339,11 @@
     */
    public static String getTreeName(Map<String, Object> paramsObj)
    {
        if (Validator.isNotEmpty(paramsObj) && paramsObj.containsKey(GenConstants.TREE_NAME))
        if (StringUtils.isNotEmpty(paramsObj) && paramsObj.containsKey(GenConstants.TREE_NAME))
        {
            return StrUtil.toCamelCase(Convert.toStr(paramsObj.get(GenConstants.TREE_NAME)));
            return StringUtils.toCamelCase(Convert.toStr(paramsObj.get(GenConstants.TREE_NAME)));
        }
        return StrUtil.EMPTY;
        return StringUtils.EMPTY;
    }
    /**
ruoyi-generator/src/main/resources/vm/java/bo.java.vm
@@ -41,7 +41,6 @@
    /**
     * $column.columnComment
     */
    @ApiModelProperty("$column.columnComment")
#if($column.isInsert && $column.isEdit)
#set($Group="AddGroup.class, EditGroup.class")
#elseif($column.isInsert)
@@ -50,11 +49,14 @@
#set($Group="EditGroup.class")
#end
#if($column.isRequired == 1)
    @ApiModelProperty(value = "$column.columnComment", required = true)
#if($column.javaType == 'String')
    @NotBlank(message = "$column.columnComment不能为空", groups = { $Group })
#else
    @NotNull(message = "$column.columnComment不能为空", groups = { $Group })
#end
#else
    @ApiModelProperty(value = "$column.columnComment")
#end
    private $column.javaType $column.javaField;
ruoyi-generator/src/main/resources/vm/java/controller.java.vm
@@ -2,8 +2,10 @@
import java.util.List;
import java.util.Arrays;
import java.util.concurrent.TimeUnit;
import lombok.RequiredArgsConstructor;
import javax.servlet.http.HttpServletResponse;
import javax.validation.constraints.*;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.beans.factory.annotation.Autowired;
@@ -16,10 +18,10 @@
import com.ruoyi.common.core.validate.AddGroup;
import com.ruoyi.common.core.validate.EditGroup;
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.common.utils.poi.ExcelUtil;
import ${packageName}.domain.vo.${ClassName}Vo;
import ${packageName}.domain.bo.${ClassName}Bo;
import ${packageName}.service.I${ClassName}Service;
import com.ruoyi.common.utils.poi.ExcelUtil;
#if($table.crud || $table.sub)
import com.ruoyi.common.core.page.TableDataInfo;
#elseif($table.tree)
@@ -66,10 +68,9 @@
    @PreAuthorize("@ss.hasPermi('${permissionPrefix}:export')")
    @Log(title = "${functionName}", businessType = BusinessType.EXPORT)
    @GetMapping("/export")
    public AjaxResult<${ClassName}Vo> export(@Validated ${ClassName}Bo bo) {
    public void export(@Validated ${ClassName}Bo bo, HttpServletResponse response) {
        List<${ClassName}Vo> list = i${ClassName}Service.queryList(bo);
        ExcelUtil<${ClassName}Vo> util = new ExcelUtil<${ClassName}Vo>(${ClassName}Vo.class);
        return util.exportExcel(list, "${functionName}");
        ExcelUtil.exportExcel(list, "${functionName}", ${ClassName}Vo.class, response);
    }
    /**
@@ -89,7 +90,7 @@
    @ApiOperation("新增${functionName}")
    @PreAuthorize("@ss.hasPermi('${permissionPrefix}:add')")
    @Log(title = "${functionName}", businessType = BusinessType.INSERT)
    @RepeatSubmit
    @RepeatSubmit()
    @PostMapping()
    public AjaxResult<Void> add(@Validated(AddGroup.class) @RequestBody ${ClassName}Bo bo) {
        return toAjax(i${ClassName}Service.insertByBo(bo) ? 1 : 0);
@@ -101,7 +102,7 @@
    @ApiOperation("修改${functionName}")
    @PreAuthorize("@ss.hasPermi('${permissionPrefix}:edit')")
    @Log(title = "${functionName}", businessType = BusinessType.UPDATE)
    @RepeatSubmit
    @RepeatSubmit()
    @PutMapping()
    public AjaxResult<Void> edit(@Validated(EditGroup.class) @RequestBody ${ClassName}Bo bo) {
        return toAjax(i${ClassName}Service.updateByBo(bo) ? 1 : 0);
ruoyi-generator/src/main/resources/vm/java/serviceImpl.java.vm
@@ -1,7 +1,7 @@
package ${packageName}.service.impl;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.StrUtil;
import com.ruoyi.common.utils.StringUtils;
#if($table.crud || $table.sub)
import com.ruoyi.common.utils.PageUtils;
import com.ruoyi.common.core.page.PagePlus;
@@ -61,7 +61,7 @@
#set($mpMethod=$column.queryType.toLowerCase())
#if($queryType != 'BETWEEN')
#if($javaType == 'String')
#set($condition='StrUtil.isNotBlank(bo.get'+$AttrName+'())')
#set($condition='StringUtils.isNotBlank(bo.get'+$AttrName+'())')
#else
#set($condition='bo.get'+$AttrName+'() != null')
#end
ruoyi-generator/src/main/resources/vm/java/vo.java.vm
@@ -1,9 +1,12 @@
package ${packageName}.domain.vo;
import com.ruoyi.common.annotation.Excel;
#foreach ($import in $importList)
import ${import};
#end
import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
import com.alibaba.excel.annotation.ExcelProperty;
import com.ruoyi.common.annotation.ExcelDictFormat;
import com.ruoyi.common.convert.ExcelDictConvert;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
@@ -19,6 +22,7 @@
 */
@Data
@ApiModel("${functionName}视图对象")
@ExcelIgnoreUnannotated
public class ${ClassName}Vo {
    private static final long serialVersionUID = 1L;
@@ -40,12 +44,14 @@
#else
#set($comment=$column.columnComment)
#end
#if($parentheseIndex != -1)
    @Excel(name = "${comment}" , readConverterExp = "$column.readConverterExp()")
#elseif($column.javaType == 'Date')
    @Excel(name = "${comment}" , width = 30, dateFormat = "yyyy-MM-dd")
#if(${column.dictType} && ${column.dictType} != '')
    @ExcelProperty(value = "${comment}", converter = ExcelDictConvert.class)
    @ExcelDictFormat(dictType = "${column.dictType}")
#elseif($parentheseIndex != -1)
    @ExcelProperty(value = "${comment}", converter = ExcelDictConvert.class)
    @ExcelDictFormat(readConverterExp = "$column.readConverterExp()")
#else
    @Excel(name = "${comment}")
    @ExcelProperty(value = "${comment}")
#end
    @ApiModelProperty("$column.columnComment")
    private $column.javaType $column.javaField;
ruoyi-generator/src/main/resources/vm/js/api.js.vm
@@ -42,12 +42,3 @@
    method: 'delete'
  })
}
// å¯¼å‡º${functionName}
export function export${BusinessName}(query) {
  return request({
    url: '/${moduleName}/${businessName}/export',
    method: 'get',
    params: query
  })
}
ruoyi-generator/src/main/resources/vm/vue/index-tree.vue.vm
@@ -253,7 +253,7 @@
</template>
<script>
import { list${BusinessName}, get${BusinessName}, del${BusinessName}, add${BusinessName}, update${BusinessName}, export${BusinessName} } from "@/api/${moduleName}/${businessName}";
import { list${BusinessName}, get${BusinessName}, del${BusinessName}, add${BusinessName}, update${BusinessName} } from "@/api/${moduleName}/${businessName}";
import Treeselect from "@riophae/vue-treeselect";
import "@riophae/vue-treeselect/dist/vue-treeselect.css";
@@ -505,7 +505,9 @@
          this.loading = false;
          this.getList();
          this.msgSuccess("删除成功");
        }).catch(() => {});
      }).finally(() => {
              this.loading = false;
      });
    }
  }
};
ruoyi-generator/src/main/resources/vm/vue/index.vue.vm
@@ -108,7 +108,7 @@
          plain
          icon="el-icon-download"
          size="mini"
          :loading="exportLoading"
          :loading="exportLoading"
          @click="handleExport"
          v-hasPermi="['${moduleName}:${businessName}:export']"
        >导出</el-button>
@@ -307,7 +307,8 @@
</template>
<script>
import { list${BusinessName}, get${BusinessName}, del${BusinessName}, add${BusinessName}, update${BusinessName}, export${BusinessName} } from "@/api/${moduleName}/${businessName}";
import { list${BusinessName}, get${BusinessName}, del${BusinessName}, add${BusinessName}, update${BusinessName} } from "@/api/${moduleName}/${businessName}";
import { downLoadExcel } from "@/utils/download";
export default {
  name: "${BusinessName}",
@@ -561,7 +562,9 @@
          this.loading = false;
          this.getList();
          this.msgSuccess("删除成功");
        }).catch(() => {});
      }).finally(() => {
          this.loading = false;
      });
    },
#if($table.sub)
    /** ${subTable.functionName}序号 */
@@ -582,35 +585,23 @@
    /** ${subTable.functionName}删除按钮操作 */
    handleDelete${subClassName}() {
      if (this.checked${subClassName}.length == 0) {
        this.$alert("请先选择要删除的${subTable.functionName}数据", "提示", { confirmButtonText: "确定", });
        this.msgError("请先选择要删除的${subTable.functionName}数据");
      } else {
        this.${subclassName}List.splice(this.checked${subClassName}[0].index - 1, 1);
        const ${subclassName}List = this.${subclassName}List;
        const checked${subClassName} = this.checked${subClassName};
        this.${subclassName}List = ${subclassName}List.filter(function(item) {
          return checked${subClassName}.indexOf(item.index) == -1
        });
      }
    },
    /** å•选框选中数据 */
    /** å¤é€‰æ¡†é€‰ä¸­æ•°æ® */
    handle${subClassName}SelectionChange(selection) {
      if (selection.length > 1) {
        this.$refs.${subclassName}.clearSelection();
        this.$refs.${subclassName}.toggleRowSelection(selection.pop());
      } else {
        this.checked${subClassName} = selection;
      }
      this.checked${subClassName} = selection.map(item => item.index)
    },
#end
    /** å¯¼å‡ºæŒ‰é’®æ“ä½œ */
    handleExport() {
      const queryParams = this.queryParams;
      this.$confirm('是否确认导出所有${functionName}数据项?', "警告", {
          confirmButtonText: "确定",
          cancelButtonText: "取消",
          type: "warning"
        }).then(() => {
          this.exportLoading = true;
          return export${BusinessName}(queryParams);
        }).then(response => {
          this.download(response.msg);
          this.exportLoading = false;
        }).catch(() => {});
      downLoadExcel('/${moduleName}/${businessName}/export', this.queryParams);
    }
  }
};
ruoyi-generator/src/main/resources/vm/xml/mapper.xml.vm
@@ -11,4 +11,4 @@
    </resultMap>
</mapper>
</mapper>
ruoyi-oss/pom.xml
@@ -5,7 +5,7 @@
    <parent>
        <artifactId>ruoyi-vue-plus</artifactId>
        <groupId>com.ruoyi</groupId>
        <version>2.6.0</version>
        <version>3.0.0</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>
@@ -21,10 +21,6 @@
        <dependency>
            <groupId>com.ruoyi</groupId>
            <artifactId>ruoyi-common</artifactId>
        </dependency>
        <dependency>
            <groupId>com.ruoyi</groupId>
            <artifactId>ruoyi-system</artifactId>
        </dependency>
        <dependency>
ruoyi-oss/src/main/java/com/ruoyi/oss/constant/CloudConstant.java
@@ -1,5 +1,8 @@
package com.ruoyi.oss.constant;
import java.util.Arrays;
import java.util.List;
/**
 * äº‘存储常量
 *
@@ -8,13 +11,28 @@
public class CloudConstant {
    /**
     * OSS模块KEY
     */
    public static final String SYS_OSS_KEY = "sys_oss:";
    /**
     * äº‘存储配置KEY
     */
    public final static String CLOUD_STORAGE_CONFIG_KEY = "sys.oss.cloudStorageService";
    public static final String CLOUD_STORAGE_CONFIG_KEY = "CloudStorageConfig";
    /**
     * ç¼“存配置KEY
     */
    public static final String CACHE_CONFIG_KEY = SYS_OSS_KEY + CLOUD_STORAGE_CONFIG_KEY;
    /**
     * é¢„览列表资源开关Key
     */
    public final static String PEREVIEW_LIST_RESOURCE_KEY = "sys.oss.previewListResource";
    public static final String PEREVIEW_LIST_RESOURCE_KEY = "sys.oss.previewListResource";
    /**
     * ç³»ç»Ÿæ•°æ®ids
     */
    public static final List<Integer> SYSTEM_DATA_IDS = Arrays.asList(1, 2, 3, 4);
}
ruoyi-oss/src/main/java/com/ruoyi/oss/enumd/CloudServiceEnumd.java
@@ -1,9 +1,10 @@
package com.ruoyi.oss.enumd;
import com.ruoyi.oss.service.impl.AliyunCloudStorageServiceImpl;
import com.ruoyi.oss.service.impl.MinioCloudStorageServiceImpl;
import com.ruoyi.oss.service.impl.QcloudCloudStorageServiceImpl;
import com.ruoyi.oss.service.impl.QiniuCloudStorageServiceImpl;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.oss.service.impl.AliyunCloudStorageStrategy;
import com.ruoyi.oss.service.impl.MinioCloudStorageStrategy;
import com.ruoyi.oss.service.impl.QcloudCloudStorageStrategy;
import com.ruoyi.oss.service.impl.QiniuCloudStorageStrategy;
import lombok.AllArgsConstructor;
import lombok.Getter;
@@ -19,22 +20,22 @@
    /**
     * ä¸ƒç‰›äº‘
     */
    QINIU("qiniu", QiniuCloudStorageServiceImpl.class),
    QINIU("qiniu", QiniuCloudStorageStrategy.class),
    /**
     * é˜¿é‡Œäº‘
     */
    ALIYUN("aliyun", AliyunCloudStorageServiceImpl.class),
    ALIYUN("aliyun", AliyunCloudStorageStrategy.class),
    /**
     * è…¾è®¯äº‘
     */
    QCLOUD("qcloud", QcloudCloudStorageServiceImpl.class),
    QCLOUD("qcloud", QcloudCloudStorageStrategy.class),
    /**
     * minio
     */
    MINIO("minio", MinioCloudStorageServiceImpl.class);
    MINIO("minio", MinioCloudStorageStrategy.class);
    private final String value;
@@ -48,4 +49,15 @@
        }
        return null;
    }
    public static String getServiceName(String value) {
        for (CloudServiceEnumd clazz : values()) {
            if (clazz.getValue().equals(value)) {
                return StringUtils.uncapitalize(clazz.getServiceClass().getSimpleName());
            }
        }
        return null;
    }
}
ruoyi-oss/src/main/java/com/ruoyi/oss/factory/OssFactory.java
@@ -1,12 +1,18 @@
package com.ruoyi.oss.factory;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.convert.Convert;
import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.common.utils.JsonUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.reflect.ReflectUtils;
import com.ruoyi.common.utils.spring.SpringUtils;
import com.ruoyi.oss.constant.CloudConstant;
import com.ruoyi.oss.enumd.CloudServiceEnumd;
import com.ruoyi.oss.service.ICloudStorageService;
import com.ruoyi.system.service.ISysConfigService;
import com.ruoyi.oss.exception.OssException;
import com.ruoyi.oss.properties.CloudStorageProperties;
import com.ruoyi.oss.service.ICloudStorageStrategy;
import java.util.Date;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
@@ -17,29 +23,55 @@
 */
public class OssFactory {
    private static ISysConfigService sysConfigService;
    private static RedisCache redisCache;
    static {
        OssFactory.sysConfigService = SpringUtils.getBean(ISysConfigService.class);
        OssFactory.redisCache = SpringUtils.getBean(RedisCache.class);
    }
    private static final Map<String, ICloudStorageService> SERVICES = new ConcurrentHashMap<>();
    /**
     * æœåŠ¡å®žä¾‹ç¼“å­˜
     */
    private static final Map<String, ICloudStorageStrategy> SERVICES = new ConcurrentHashMap<>();
    public static ICloudStorageService instance() {
        String type = sysConfigService.selectConfigByKey(CloudConstant.CLOUD_STORAGE_CONFIG_KEY);
    /**
     * æœåŠ¡é…ç½®æ›´æ–°æ—¶é—´ç¼“å­˜
     */
    private static final Map<String, Date> SERVICES_UPDATE_TIME = new ConcurrentHashMap<>();
    /**
     * èŽ·å–é»˜è®¤å®žä¾‹
     */
    public static ICloudStorageStrategy instance() {
        // èŽ·å–redis é»˜è®¤ç±»åž‹
        String type = Convert.toStr(redisCache.getCacheObject(CloudConstant.CACHE_CONFIG_KEY));
        if (StringUtils.isEmpty(type)) {
            throw new OssException("文件存储服务类型无法找到!");
        }
        return instance(type);
    }
    public static ICloudStorageService instance(String type) {
        ICloudStorageService service = SERVICES.get(type);
        if (service == null) {
            service = (ICloudStorageService) SpringUtils.getBean(CloudServiceEnumd.getServiceClass(type));
    /**
     * æ ¹æ®ç±»åž‹èŽ·å–å®žä¾‹
     */
    public static ICloudStorageStrategy instance(String type) {
        ICloudStorageStrategy service = SERVICES.get(type);
        Date oldDate = SERVICES_UPDATE_TIME.get(type);
        Object json = redisCache.getCacheObject(CloudConstant.SYS_OSS_KEY + type);
        CloudStorageProperties properties = JsonUtils.parseObject(json.toString(), CloudStorageProperties.class);
        if (properties == null) {
            throw new OssException("系统异常, '" + type + "'配置信息不存在!");
        }
        Date nowDate = properties.getUpdateTime();
        // æœåŠ¡å­˜åœ¨å¹¶æ›´æ–°æ—¶é—´ç›¸åŒåˆ™è¿”å›ž(使用更新时间确保配置最终一致性)
        if (service != null && oldDate.equals(nowDate)) {
            return service;
        }
        // èŽ·å–redis配置信息 åˆ›å»ºå¯¹è±¡ å¹¶ç¼“å­˜
        service = (ICloudStorageStrategy) ReflectUtils.newInstance(CloudServiceEnumd.getServiceClass(type), properties);
        SERVICES.put(type, service);
        SERVICES_UPDATE_TIME.put(type, nowDate);
        return service;
    }
    public static void register(String type, ICloudStorageService iCloudStorageService) {
        Assert.notNull(type, "type can't be null");
        SERVICES.put(type, iCloudStorageService);
    }
}
ruoyi-oss/src/main/java/com/ruoyi/oss/properties/CloudStorageProperties.java
@@ -1,9 +1,8 @@
package com.ruoyi.oss.properties;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
import java.util.Date;
/**
 * OSS云存储 é…ç½®å±žæ€§
@@ -11,177 +10,46 @@
 * @author Lion Li
 */
@Data
@Component
@ConfigurationProperties(prefix = "cloud-storage")
public class CloudStorageProperties {
    private Boolean previewListImage;
    private QiniuProperties qiniu;
    private AliyunProperties aliyun;
    private QcloudProperties qcloud;
    private MinioProperties minio;
    /**
     * åŸŸå
     */
    private String endpoint;
    /**
     * é˜¿é‡Œäº‘ é…ç½®å±žæ€§
     *
     * @author Lion Li
     * å‰ç¼€
     */
    @Data
    @NoArgsConstructor
    public static class AliyunProperties {
        /**
         * é˜¿é‡Œäº‘绑定的域名
         */
        private String endpoint;
        /**
         * é˜¿é‡Œäº‘路径前缀
         */
        private String prefix;
        /**
         * é˜¿é‡Œäº‘AccessKeyId
         */
        private String accessKeyId;
        /**
         * é˜¿é‡Œäº‘AccessKeySecret
         */
        private String accessKeySecret;
        /**
         * é˜¿é‡Œäº‘BucketName
         */
        private String bucketName;
    }
    private String prefix;
    /**
     * Minio é…ç½®å±žæ€§
     *
     * @author Lion Li
     * ACCESS_KEY
     */
    @Data
    @NoArgsConstructor
    public static class MinioProperties {
        /**
         * minio域名
         */
        private String endpoint;
        /**
         * minio ACCESS_KEY
         */
        private String accessKey;
        /**
         * minio SECRET_KEY
         */
        private String secretKey;
        /**
         * minio å­˜å‚¨ç©ºé—´å
         */
        private String bucketName;
    }
    private String accessKey;
    /**
     * è…¾è®¯äº‘COS é…ç½®å±žæ€§
     *
     * @author Lion Li
     * SECRET_KEY
     */
    @Data
    @NoArgsConstructor
    public static class QcloudProperties {
        /**
         * è…¾è®¯äº‘绑定的域名
         */
        private String endpoint;
        /**
         * è…¾è®¯äº‘路径前缀
         */
        private String prefix;
        /**
         * è…¾è®¯äº‘SecretId
         */
        private String secretId;
        /**
         * è…¾è®¯äº‘SecretKey
         */
        private String secretKey;
        /**
         * è…¾è®¯äº‘BucketName
         */
        private String bucketName;
        /**
         * ä¸ƒç‰›æ˜¯å¦ä½¿ç”¨https
         */
        private Boolean isHttps;
        /**
         * è…¾è®¯äº‘COS所属地区
         */
        private String region;
    }
    private String secretKey;
    /**
     * ä¸ƒç‰›äº‘ é…ç½®å±žæ€§
     *
     * @author Lion Li
     * å­˜å‚¨ç©ºé—´å
     */
    @Data
    @NoArgsConstructor
    public static class QiniuProperties {
    private String bucketName;
        /**
         * ä¸ƒç‰›ç»‘定的域名
         */
        private String domain;
    /**
     * å­˜å‚¨åŒºåŸŸ
     */
    private String region;
        /**
         * ä¸ƒç‰›è·¯å¾„前缀
         */
        private String prefix;
    /**
     * æ˜¯å¦https(Y=是,N=否)
     */
    private String isHttps;
        /**
         * ä¸ƒç‰›ACCESS_KEY
         */
        private String accessKey;
        /**
         * ä¸ƒç‰›SECRET_KEY
         */
        private String secretKey;
        /**
         * ä¸ƒç‰›å­˜å‚¨ç©ºé—´å
         */
        private String bucketName;
        /**
         * ä¸ƒç‰›å­˜å‚¨åŒºåŸŸ
         */
        private String region;
        /**
         * ä¸ƒç‰›æ˜¯å¦ä½¿ç”¨https
         */
        private Boolean isHttps;
    }
    /**
     * æ›´æ–°æ—¶é—´
     */
    private Date updateTime;
}
ruoyi-oss/src/main/java/com/ruoyi/oss/service/ICloudStorageStrategy.java
ÎļþÃû´Ó ruoyi-oss/src/main/java/com/ruoyi/oss/service/ICloudStorageService.java ÐÞ¸Ä
@@ -5,11 +5,11 @@
import java.io.InputStream;
/**
 * äº‘存储服务接口
 * äº‘存储策略
 *
 * @author Lion Li
 */
public interface ICloudStorageService {
public interface ICloudStorageStrategy {
    void createBucket();
@@ -70,5 +70,5 @@
     */
    UploadResult uploadSuffix(InputStream inputStream, String suffix, String contentType);
    String getEndpointLink();
    String getEndpointLink();
}
ruoyi-oss/src/main/java/com/ruoyi/oss/service/abstractd/AbstractCloudStorageStrategy.java
ÎļþÃû´Ó ruoyi-oss/src/main/java/com/ruoyi/oss/service/abstractd/AbstractCloudStorageService.java ÐÞ¸Ä
@@ -3,20 +3,22 @@
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.oss.entity.UploadResult;
import com.ruoyi.oss.service.ICloudStorageService;
import org.springframework.beans.factory.InitializingBean;
import com.ruoyi.oss.properties.CloudStorageProperties;
import com.ruoyi.oss.service.ICloudStorageStrategy;
import java.io.InputStream;
import java.util.Date;
/**
 * äº‘存储(支持七牛、阿里云、腾讯云、minio)
 * äº‘存储策略(支持七牛、阿里云、腾讯云、minio)
 *
 * @author Lion Li
 */
public abstract class AbstractCloudStorageService implements ICloudStorageService, InitializingBean {
public abstract class AbstractCloudStorageStrategy implements ICloudStorageStrategy {
    protected CloudStorageProperties properties;
    @Override
    public abstract void createBucket();
@@ -30,7 +32,7 @@
        String uuid = IdUtil.fastSimpleUUID();
        // æ–‡ä»¶è·¯å¾„
        String path = DateUtil.format(new Date(), "yyyyMMdd") + "/" + uuid;
        if (StrUtil.isNotBlank(prefix)) {
        if (StringUtils.isNotBlank(prefix)) {
            path = prefix + "/" + path;
        }
        return path + suffix;
@@ -53,9 +55,6 @@
    @Override
    public abstract UploadResult uploadSuffix(InputStream inputStream, String suffix, String contentType);
    @Override
    public abstract void afterPropertiesSet() throws Exception;
    @Override
    public abstract String getEndpointLink();
ruoyi-oss/src/main/java/com/ruoyi/oss/service/impl/AliyunCloudStorageStrategy.java
ÎļþÃû´Ó ruoyi-oss/src/main/java/com/ruoyi/oss/service/impl/AliyunCloudStorageServiceImpl.java ÐÞ¸Ä
@@ -1,6 +1,5 @@
package com.ruoyi.oss.service.impl;
import cn.hutool.core.util.StrUtil;
import com.aliyun.oss.ClientConfiguration;
import com.aliyun.oss.OSSClient;
import com.aliyun.oss.common.auth.DefaultCredentialProvider;
@@ -8,42 +7,32 @@
import com.aliyun.oss.model.CreateBucketRequest;
import com.aliyun.oss.model.ObjectMetadata;
import com.aliyun.oss.model.PutObjectRequest;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.oss.entity.UploadResult;
import com.ruoyi.oss.enumd.CloudServiceEnumd;
import com.ruoyi.oss.exception.OssException;
import com.ruoyi.oss.factory.OssFactory;
import com.ruoyi.oss.properties.CloudStorageProperties;
import com.ruoyi.oss.properties.CloudStorageProperties.AliyunProperties;
import com.ruoyi.oss.service.abstractd.AbstractCloudStorageService;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import com.ruoyi.oss.service.abstractd.AbstractCloudStorageStrategy;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
/**
 * é˜¿é‡Œäº‘存储
 * é˜¿é‡Œäº‘存储策略
 *
 * @author Lion Li
 */
@Lazy
@Service
public class AliyunCloudStorageServiceImpl extends AbstractCloudStorageService implements InitializingBean {
public class AliyunCloudStorageStrategy extends AbstractCloudStorageStrategy {
    private final OSSClient client;
    private final AliyunProperties properties;
    @Autowired
    public AliyunCloudStorageServiceImpl(CloudStorageProperties properties) {
        this.properties = properties.getAliyun();
    public AliyunCloudStorageStrategy(CloudStorageProperties cloudStorageProperties) {
        properties = cloudStorageProperties;
        try {
            ClientConfiguration configuration = new ClientConfiguration();
            DefaultCredentialProvider credentialProvider = new DefaultCredentialProvider(
                this.properties.getAccessKeyId(),
                this.properties.getAccessKeySecret());
            client = new OSSClient(this.properties.getEndpoint(), credentialProvider, configuration);
                properties.getAccessKey(), properties.getSecretKey());
            client = new OSSClient(properties.getEndpoint(), credentialProvider, configuration);
            createBucket();
        } catch (Exception e) {
            throw new IllegalArgumentException("阿里云存储配置错误! è¯·æ£€æŸ¥ç³»ç»Ÿé…ç½®!");
@@ -108,17 +97,12 @@
    }
    @Override
    public void afterPropertiesSet() throws Exception {
        OssFactory.register(getServiceType(), this);
    }
    @Override
    public String getEndpointLink() {
        String endpoint = properties.getEndpoint();
        StringBuilder sb = new StringBuilder(endpoint);
        if (StrUtil.containsAnyIgnoreCase(endpoint, "http://")) {
        if (StringUtils.containsAnyIgnoreCase(endpoint, "http://")) {
            sb.insert(7, properties.getBucketName() + ".");
        } else if (StrUtil.containsAnyIgnoreCase(endpoint, "https://")) {
        } else if (StringUtils.containsAnyIgnoreCase(endpoint, "https://")) {
            sb.insert(8, properties.getBucketName() + ".");
        } else {
            throw new OssException("Endpoint配置错误");
ruoyi-oss/src/main/java/com/ruoyi/oss/service/impl/MinioCloudStorageStrategy.java
ÎļþÃû´Ó ruoyi-oss/src/main/java/com/ruoyi/oss/service/impl/MinioCloudStorageServiceImpl.java ÐÞ¸Ä
@@ -1,43 +1,33 @@
package com.ruoyi.oss.service.impl;
import cn.hutool.core.util.StrUtil;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.oss.entity.UploadResult;
import com.ruoyi.oss.enumd.CloudServiceEnumd;
import com.ruoyi.oss.enumd.PolicyType;
import com.ruoyi.oss.exception.OssException;
import com.ruoyi.oss.factory.OssFactory;
import com.ruoyi.oss.properties.CloudStorageProperties;
import com.ruoyi.oss.properties.CloudStorageProperties.MinioProperties;
import com.ruoyi.oss.service.abstractd.AbstractCloudStorageService;
import com.ruoyi.oss.service.abstractd.AbstractCloudStorageStrategy;
import io.minio.*;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Service;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
/**
 * minio存储
 * minio存储策略
 *
 * @author Lion Li
 */
@Lazy
@Service
public class MinioCloudStorageServiceImpl extends AbstractCloudStorageService implements InitializingBean {
public class MinioCloudStorageStrategy extends AbstractCloudStorageStrategy {
    private final MinioClient minioClient;
    private final MinioProperties properties;
    @Autowired
    public MinioCloudStorageServiceImpl(CloudStorageProperties properties) {
        this.properties = properties.getMinio();
    public MinioCloudStorageStrategy(CloudStorageProperties cloudStorageProperties) {
        properties = cloudStorageProperties;
        try {
            minioClient = MinioClient.builder()
                .endpoint(this.properties.getEndpoint())
                .credentials(this.properties.getAccessKey(), this.properties.getSecretKey())
                .endpoint(properties.getEndpoint())
                .credentials(properties.getAccessKey(), properties.getSecretKey())
                .build();
            createBucket();
        } catch (Exception e) {
@@ -80,7 +70,7 @@
            minioClient.putObject(PutObjectArgs.builder()
                .bucket(properties.getBucketName())
                .object(path)
                .contentType(StrUtil.blankToDefault(contentType, MediaType.APPLICATION_OCTET_STREAM_VALUE))
                .contentType(StringUtils.blankToDefault(contentType, MediaType.APPLICATION_OCTET_STREAM_VALUE))
                .stream(inputStream, inputStream.available(), -1)
                .build());
        } catch (Exception e) {
@@ -110,11 +100,6 @@
    @Override
    public UploadResult uploadSuffix(InputStream inputStream, String suffix, String contentType) {
        return upload(inputStream, getPath("", suffix), contentType);
    }
    @Override
    public void afterPropertiesSet() throws Exception {
        OssFactory.register(getServiceType(), this);
    }
    @Override
ruoyi-oss/src/main/java/com/ruoyi/oss/service/impl/QcloudCloudStorageStrategy.java
ÎļþÃû´Ó ruoyi-oss/src/main/java/com/ruoyi/oss/service/impl/QcloudCloudStorageServiceImpl.java ÐÞ¸Ä
@@ -1,6 +1,5 @@
package com.ruoyi.oss.service.impl;
import cn.hutool.core.util.StrUtil;
import com.qcloud.cos.COSClient;
import com.qcloud.cos.ClientConfig;
import com.qcloud.cos.auth.BasicCOSCredentials;
@@ -8,45 +7,35 @@
import com.qcloud.cos.http.HttpProtocol;
import com.qcloud.cos.model.*;
import com.qcloud.cos.region.Region;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.oss.entity.UploadResult;
import com.ruoyi.oss.enumd.CloudServiceEnumd;
import com.ruoyi.oss.exception.OssException;
import com.ruoyi.oss.factory.OssFactory;
import com.ruoyi.oss.properties.CloudStorageProperties;
import com.ruoyi.oss.properties.CloudStorageProperties.QcloudProperties;
import com.ruoyi.oss.service.abstractd.AbstractCloudStorageService;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import com.ruoyi.oss.service.abstractd.AbstractCloudStorageStrategy;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
/**
 * è…¾è®¯äº‘存储
 * è…¾è®¯äº‘存储策略
 *
 * @author Lion Li
 */
@Lazy
@Service
public class QcloudCloudStorageServiceImpl extends AbstractCloudStorageService implements InitializingBean {
public class QcloudCloudStorageStrategy extends AbstractCloudStorageStrategy {
    private final COSClient client;
    private final QcloudProperties properties;
    @Autowired
    public QcloudCloudStorageServiceImpl(CloudStorageProperties properties) {
        this.properties = properties.getQcloud();
    public QcloudCloudStorageStrategy(CloudStorageProperties cloudStorageProperties) {
        properties = cloudStorageProperties;
        try {
            COSCredentials credentials = new BasicCOSCredentials(
                this.properties.getSecretId(),
                this.properties.getSecretKey());
                properties.getAccessKey(), properties.getSecretKey());
            // åˆå§‹åŒ–客户端配置
            ClientConfig clientConfig = new ClientConfig();
            // è®¾ç½®bucket所在的区域,华南:gz åŽåŒ—:tj åŽä¸œï¼šsh
            clientConfig.setRegion(new Region(this.properties.getRegion()));
            if (this.properties.getIsHttps()) {
            clientConfig.setRegion(new Region(properties.getRegion()));
            if ("Y".equals(properties.getIsHttps())) {
                clientConfig.setHttpProtocol(HttpProtocol.https);
            } else {
                clientConfig.setHttpProtocol(HttpProtocol.http);
@@ -116,17 +105,12 @@
    }
    @Override
    public void afterPropertiesSet() throws Exception {
        OssFactory.register(getServiceType(),this);
    }
    @Override
    public String getEndpointLink() {
        String endpoint = properties.getEndpoint();
        StringBuilder sb = new StringBuilder(endpoint);
        if (StrUtil.containsAnyIgnoreCase(endpoint, "http://")) {
        if (StringUtils.containsAnyIgnoreCase(endpoint, "http://")) {
            sb.insert(7, properties.getBucketName() + ".");
        } else if (StrUtil.containsAnyIgnoreCase(endpoint, "https://")) {
        } else if (StringUtils.containsAnyIgnoreCase(endpoint, "https://")) {
            sb.insert(8, properties.getBucketName() + ".");
        } else {
            throw new OssException("Endpoint配置错误");
ruoyi-oss/src/main/java/com/ruoyi/oss/service/impl/QiniuCloudStorageStrategy.java
ÎļþÃû´Ó ruoyi-oss/src/main/java/com/ruoyi/oss/service/impl/QiniuCloudStorageServiceImpl.java ÐÞ¸Ä
@@ -10,51 +10,37 @@
import com.ruoyi.oss.entity.UploadResult;
import com.ruoyi.oss.enumd.CloudServiceEnumd;
import com.ruoyi.oss.exception.OssException;
import com.ruoyi.oss.factory.OssFactory;
import com.ruoyi.oss.properties.CloudStorageProperties;
import com.ruoyi.oss.properties.CloudStorageProperties.QiniuProperties;
import com.ruoyi.oss.service.abstractd.AbstractCloudStorageService;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import com.ruoyi.oss.service.abstractd.AbstractCloudStorageStrategy;
import java.io.InputStream;
/**
 * ä¸ƒç‰›äº‘存储
 * ä¸ƒç‰›äº‘存储策略
 *
 * @author Lion Li
 */
@Lazy
@Service
public class QiniuCloudStorageServiceImpl extends AbstractCloudStorageService implements InitializingBean {
public class QiniuCloudStorageStrategy extends AbstractCloudStorageStrategy {
    private final UploadManager uploadManager;
    private final BucketManager bucketManager;
    private final String token;
    private final QiniuProperties properties;
    @Autowired
    public QiniuCloudStorageServiceImpl(CloudStorageProperties properties) {
        this.properties = properties.getQiniu();
    public QiniuCloudStorageStrategy(CloudStorageProperties cloudStorageProperties) {
        properties = cloudStorageProperties;
        try {
            Configuration config = new Configuration(getRegion(this.properties.getRegion()));
            Configuration config = new Configuration(getRegion(properties.getRegion()));
            // https设置
            config.useHttpsDomains = false;
            if (this.properties.getIsHttps() != null) {
                config.useHttpsDomains = this.properties.getIsHttps();
            }
            config.useHttpsDomains = "Y".equals(properties.getIsHttps());
            uploadManager = new UploadManager(config);
            Auth auth = Auth.create(
                this.properties.getAccessKey(),
                this.properties.getSecretKey());
            String bucketName = this.properties.getBucketName();
            Auth auth = Auth.create(properties.getAccessKey(), properties.getSecretKey());
            String bucketName = properties.getBucketName();
            token = auth.uploadToken(bucketName);
            bucketManager = new BucketManager(auth, config);
            if (!ArrayUtil.contains(bucketManager.buckets(), bucketName)) {
                bucketManager.createBucket(bucketName, this.properties.getRegion());
                bucketManager.createBucket(bucketName, properties.getRegion());
            }
        } catch (Exception e) {
            throw new IllegalArgumentException("七牛云存储配置错误! è¯·æ£€æŸ¥ç³»ç»Ÿé…ç½®!");
@@ -116,13 +102,8 @@
    }
    @Override
    public void afterPropertiesSet() throws Exception {
        OssFactory.register(getServiceType(),this);
    }
    @Override
    public String getEndpointLink() {
        return properties.getDomain();
        return properties.getEndpoint();
    }
    private Region getRegion(String region) {
ruoyi-quartz/pom.xml
@@ -5,7 +5,7 @@
    <parent>
        <artifactId>ruoyi-vue-plus</artifactId>
        <groupId>com.ruoyi</groupId>
        <version>2.6.0</version>
        <version>3.0.0</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>
ruoyi-quartz/src/main/java/com/ruoyi/quartz/controller/SysJobController.java
@@ -1,6 +1,5 @@
package com.ruoyi.quartz.controller;
import cn.hutool.core.util.StrUtil;
import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.core.controller.BaseController;
@@ -8,7 +7,7 @@
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.common.exception.job.TaskException;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.poi.ExcelUtil;
import com.ruoyi.quartz.domain.SysJob;
import com.ruoyi.quartz.service.ISysJobService;
@@ -18,6 +17,7 @@
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletResponse;
import java.util.List;
/**
@@ -48,11 +48,10 @@
    @PreAuthorize("@ss.hasPermi('monitor:job:export')")
    @Log(title = "定时任务", businessType = BusinessType.EXPORT)
    @GetMapping("/export")
    public AjaxResult export(SysJob sysJob)
    public void export(SysJob sysJob, HttpServletResponse response)
    {
        List<SysJob> list = jobService.selectJobList(sysJob);
        ExcelUtil<SysJob> util = new ExcelUtil<SysJob>(SysJob.class);
        return util.exportExcel(list, "定时任务");
        ExcelUtil.exportExcel(list, "定时任务", SysJob.class, response);
    }
    /**
@@ -71,18 +70,22 @@
    @PreAuthorize("@ss.hasPermi('monitor:job:add')")
    @Log(title = "定时任务", businessType = BusinessType.INSERT)
    @PostMapping
    public AjaxResult add(@RequestBody SysJob sysJob) throws SchedulerException, TaskException
    public AjaxResult add(@RequestBody SysJob job) throws SchedulerException, TaskException
    {
        if (!CronUtils.isValid(sysJob.getCronExpression()))
        if (!CronUtils.isValid(job.getCronExpression()))
        {
            return AjaxResult.error("新增任务'" + sysJob.getJobName() + "'失败,Cron表达式不正确");
            return error("新增任务'" + job.getJobName() + "'失败,Cron表达式不正确");
        }
        else if (StrUtil.containsIgnoreCase(sysJob.getInvokeTarget(), Constants.LOOKUP_RMI))
        else if (StringUtils.containsIgnoreCase(job.getInvokeTarget(), Constants.LOOKUP_RMI))
        {
            return AjaxResult.error("新增任务'" + sysJob.getJobName() + "'失败,目标字符串不允许'rmi://'调用");
            return error("新增任务'" + job.getJobName() + "'失败,目标字符串不允许'rmi://'调用");
        }
        sysJob.setCreateBy(SecurityUtils.getUsername());
        return toAjax(jobService.insertJob(sysJob));
        else if (StringUtils.containsAnyIgnoreCase(job.getInvokeTarget(), new String[] { Constants.HTTP, Constants.HTTPS }))
        {
            return error("新增任务'" + job.getJobName() + "'失败,目标字符串不允许'http(s)//'调用");
        }
        job.setCreateBy(getUsername());
        return toAjax(jobService.insertJob(job));
    }
    /**
@@ -91,18 +94,22 @@
    @PreAuthorize("@ss.hasPermi('monitor:job:edit')")
    @Log(title = "定时任务", businessType = BusinessType.UPDATE)
    @PutMapping
    public AjaxResult edit(@RequestBody SysJob sysJob) throws SchedulerException, TaskException
    public AjaxResult edit(@RequestBody SysJob job) throws SchedulerException, TaskException
    {
        if (!CronUtils.isValid(sysJob.getCronExpression()))
        if (!CronUtils.isValid(job.getCronExpression()))
        {
            return AjaxResult.error("修改任务'" + sysJob.getJobName() + "'失败,Cron表达式不正确");
            return error("修改任务'" + job.getJobName() + "'失败,Cron表达式不正确");
        }
        else if (StrUtil.containsIgnoreCase(sysJob.getInvokeTarget(), Constants.LOOKUP_RMI))
        else if (StringUtils.containsIgnoreCase(job.getInvokeTarget(), Constants.LOOKUP_RMI))
        {
            return AjaxResult.error("修改任务'" + sysJob.getJobName() + "'失败,目标字符串不允许'rmi://'调用");
            return error("修改任务'" + job.getJobName() + "'失败,目标字符串不允许'rmi://'调用");
        }
        sysJob.setUpdateBy(SecurityUtils.getUsername());
        return toAjax(jobService.updateJob(sysJob));
        else if (StringUtils.containsAnyIgnoreCase(job.getInvokeTarget(), new String[] { Constants.HTTP, Constants.HTTPS }))
        {
            return error("修改任务'" + job.getJobName() + "'失败,目标字符串不允许'http(s)//'调用");
        }
        job.setUpdateBy(getUsername());
        return toAjax(jobService.updateJob(job));
    }
    /**
ruoyi-quartz/src/main/java/com/ruoyi/quartz/controller/SysJobLogController.java
@@ -1,85 +1,85 @@
package com.ruoyi.quartz.controller;
import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.common.utils.poi.ExcelUtil;
import com.ruoyi.quartz.domain.SysJobLog;
import com.ruoyi.quartz.service.ISysJobLogService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
 * è°ƒåº¦æ—¥å¿—操作处理
 *
 * @author ruoyi
 */
@RestController
@RequestMapping("/monitor/jobLog")
public class SysJobLogController extends BaseController
{
    @Autowired
    private ISysJobLogService jobLogService;
    /**
     * æŸ¥è¯¢å®šæ—¶ä»»åŠ¡è°ƒåº¦æ—¥å¿—åˆ—è¡¨
     */
    @PreAuthorize("@ss.hasPermi('monitor:job:list')")
    @GetMapping("/list")
    public TableDataInfo list(SysJobLog sysJobLog)
    {
        return jobLogService.selectPageJobLogList(sysJobLog);
    }
    /**
     * å¯¼å‡ºå®šæ—¶ä»»åŠ¡è°ƒåº¦æ—¥å¿—åˆ—è¡¨
     */
    @PreAuthorize("@ss.hasPermi('monitor:job:export')")
    @Log(title = "任务调度日志", businessType = BusinessType.EXPORT)
    @GetMapping("/export")
    public AjaxResult export(SysJobLog sysJobLog)
    {
        List<SysJobLog> list = jobLogService.selectJobLogList(sysJobLog);
        ExcelUtil<SysJobLog> util = new ExcelUtil<SysJobLog>(SysJobLog.class);
        return util.exportExcel(list, "调度日志");
    }
    /**
     * æ ¹æ®è°ƒåº¦ç¼–号获取详细信息
     */
    @PreAuthorize("@ss.hasPermi('monitor:job:query')")
    @GetMapping(value = "/{configId}")
    public AjaxResult getInfo(@PathVariable Long jobLogId)
    {
        return AjaxResult.success(jobLogService.selectJobLogById(jobLogId));
    }
    /**
     * åˆ é™¤å®šæ—¶ä»»åŠ¡è°ƒåº¦æ—¥å¿—
     */
    @PreAuthorize("@ss.hasPermi('monitor:job:remove')")
    @Log(title = "定时任务调度日志", businessType = BusinessType.DELETE)
    @DeleteMapping("/{jobLogIds}")
    public AjaxResult remove(@PathVariable Long[] jobLogIds)
    {
        return toAjax(jobLogService.deleteJobLogByIds(jobLogIds));
    }
    /**
     * æ¸…空定时任务调度日志
     */
    @PreAuthorize("@ss.hasPermi('monitor:job:remove')")
    @Log(title = "调度日志", businessType = BusinessType.CLEAN)
    @DeleteMapping("/clean")
    public AjaxResult clean()
    {
        jobLogService.cleanJobLog();
        return AjaxResult.success();
    }
}
package com.ruoyi.quartz.controller;
import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.common.utils.poi.ExcelUtil;
import com.ruoyi.quartz.domain.SysJobLog;
import com.ruoyi.quartz.service.ISysJobLogService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletResponse;
import java.util.List;
/**
 * è°ƒåº¦æ—¥å¿—操作处理
 *
 * @author ruoyi
 */
@RestController
@RequestMapping("/monitor/jobLog")
public class SysJobLogController extends BaseController
{
    @Autowired
    private ISysJobLogService jobLogService;
    /**
     * æŸ¥è¯¢å®šæ—¶ä»»åŠ¡è°ƒåº¦æ—¥å¿—åˆ—è¡¨
     */
    @PreAuthorize("@ss.hasPermi('monitor:job:list')")
    @GetMapping("/list")
    public TableDataInfo list(SysJobLog sysJobLog)
    {
        return jobLogService.selectPageJobLogList(sysJobLog);
    }
    /**
     * å¯¼å‡ºå®šæ—¶ä»»åŠ¡è°ƒåº¦æ—¥å¿—åˆ—è¡¨
     */
    @PreAuthorize("@ss.hasPermi('monitor:job:export')")
    @Log(title = "任务调度日志", businessType = BusinessType.EXPORT)
    @GetMapping("/export")
    public void export(SysJobLog sysJobLog, HttpServletResponse response)
    {
        List<SysJobLog> list = jobLogService.selectJobLogList(sysJobLog);
        ExcelUtil.exportExcel(list, "调度日志", SysJobLog.class, response);
    }
    /**
     * æ ¹æ®è°ƒåº¦ç¼–号获取详细信息
     */
    @PreAuthorize("@ss.hasPermi('monitor:job:query')")
    @GetMapping(value = "/{configId}")
    public AjaxResult getInfo(@PathVariable Long jobLogId)
    {
        return AjaxResult.success(jobLogService.selectJobLogById(jobLogId));
    }
    /**
     * åˆ é™¤å®šæ—¶ä»»åŠ¡è°ƒåº¦æ—¥å¿—
     */
    @PreAuthorize("@ss.hasPermi('monitor:job:remove')")
    @Log(title = "定时任务调度日志", businessType = BusinessType.DELETE)
    @DeleteMapping("/{jobLogIds}")
    public AjaxResult remove(@PathVariable Long[] jobLogIds)
    {
        return toAjax(jobLogService.deleteJobLogByIds(jobLogIds));
    }
    /**
     * æ¸…空定时任务调度日志
     */
    @PreAuthorize("@ss.hasPermi('monitor:job:remove')")
    @Log(title = "调度日志", businessType = BusinessType.CLEAN)
    @DeleteMapping("/clean")
    public AjaxResult clean()
    {
        jobLogService.cleanJobLog();
        return AjaxResult.success();
    }
}
ruoyi-quartz/src/main/java/com/ruoyi/quartz/domain/SysJob.java
@@ -1,11 +1,13 @@
package com.ruoyi.quartz.domain;
import cn.hutool.core.util.StrUtil;
import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
import com.alibaba.excel.annotation.ExcelProperty;
import com.baomidou.mybatisplus.annotation.*;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.ruoyi.common.annotation.Excel;
import com.ruoyi.common.annotation.Excel.ColumnType;
import com.ruoyi.common.annotation.ExcelDictFormat;
import com.ruoyi.common.constant.ScheduleConstants;
import com.ruoyi.common.convert.ExcelDictConvert;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.quartz.util.CronUtils;
import lombok.Data;
import lombok.NoArgsConstructor;
@@ -28,13 +30,14 @@
@NoArgsConstructor
@Accessors(chain = true)
@TableName("sys_job")
@ExcelIgnoreUnannotated
public class SysJob implements Serializable {
    private static final long serialVersionUID = 1L;
    /**
     * ä»»åŠ¡ID
     */
    @Excel(name = "任务序号", cellType = ColumnType.NUMERIC)
    @ExcelProperty(value = "任务序号")
    @TableId(value = "job_id", type = IdType.AUTO)
    private Long jobId;
@@ -43,13 +46,14 @@
     */
    @NotBlank(message = "任务名称不能为空")
    @Size(min = 0, max = 64, message = "任务名称不能超过64个字符")
    @Excel(name = "任务名称")
    @ExcelProperty(value = "任务名称")
    private String jobName;
    /**
     * ä»»åŠ¡ç»„å
     */
    @Excel(name = "任务组名")
    @ExcelProperty(value = "任务组名", converter = ExcelDictConvert.class)
    @ExcelDictFormat(dictType = "sys_job_group")
    private String jobGroup;
    /**
@@ -57,7 +61,7 @@
     */
    @NotBlank(message = "调用目标字符串不能为空")
    @Size(min = 0, max = 500, message = "调用目标字符串长度不能超过500个字符")
    @Excel(name = "调用目标字符串")
    @ExcelProperty(value = "调用目标字符串")
    private String invokeTarget;
    /**
@@ -65,25 +69,28 @@
     */
    @NotBlank(message = "Cron执行表达式不能为空")
    @Size(min = 0, max = 255, message = "Cron执行表达式不能超过255个字符")
    @Excel(name = "执行表达式 ")
    @ExcelProperty(value = "执行表达式")
    private String cronExpression;
    /**
     * cron计划策略
     */
    @Excel(name = "计划策略 ", readConverterExp = "0=默认,1=立即触发执行,2=触发一次执行,3=不触发立即执行")
    private String misfirePolicy = ScheduleConstants.MISFIRE_DEFAULT;
    @ExcelProperty(value = "计划策略 ", converter = ExcelDictConvert.class)
    @ExcelDictFormat(readConverterExp = "0=默认,1=立即触发执行,2=触发一次执行,3=不触发立即执行")
    private String misfirePolicy = ScheduleConstants.MISFIRE_DEFAULT;
    /**
     * æ˜¯å¦å¹¶å‘执行(0允许 1禁止)
     */
    @Excel(name = "并发执行", readConverterExp = "0=允许,1=禁止")
    @ExcelProperty(value = "并发执行", converter = ExcelDictConvert.class)
    @ExcelDictFormat(readConverterExp = "0=允许,1=禁止")
    private String concurrent;
    /**
     * ä»»åŠ¡çŠ¶æ€ï¼ˆ0正常 1暂停)
     */
    @Excel(name = "任务状态", readConverterExp = "0=正常,1=暂停")
    @ExcelProperty(value = "任务状态", converter = ExcelDictConvert.class)
    @ExcelDictFormat(dictType = "sys_job_status")
    private String status;
    /**
@@ -96,7 +103,6 @@
     * åˆ›å»ºæ—¶é—´
     */
    @TableField(fill = FieldFill.INSERT)
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private Date createTime;
    /**
@@ -109,7 +115,6 @@
     * æ›´æ–°æ—¶é—´
     */
    @TableField(fill = FieldFill.INSERT_UPDATE)
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private Date updateTime;
    /**
@@ -125,7 +130,7 @@
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    public Date getNextValidTime() {
        if (StrUtil.isNotEmpty(cronExpression)) {
        if (StringUtils.isNotEmpty(cronExpression)) {
            return CronUtils.getNextExecution(cronExpression);
        }
        return null;
ruoyi-quartz/src/main/java/com/ruoyi/quartz/domain/SysJobLog.java
@@ -1,78 +1,82 @@
package com.ruoyi.quartz.domain;
import com.baomidou.mybatisplus.annotation.*;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.ruoyi.common.annotation.Excel;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
/**
 * å®šæ—¶ä»»åŠ¡è°ƒåº¦æ—¥å¿—è¡¨ sys_job_log
 *
 * @author ruoyi
 */
@Data
@NoArgsConstructor
@Accessors(chain = true)
@TableName("sys_job_log")
public class SysJobLog
{
    private static final long serialVersionUID = 1L;
    /** ID */
    @Excel(name = "日志序号")
    @TableId(value = "job_log_id", type = IdType.AUTO)
    private Long jobLogId;
    /** ä»»åŠ¡åç§° */
    @Excel(name = "任务名称")
    private String jobName;
    /** ä»»åŠ¡ç»„å */
    @Excel(name = "任务组名")
    private String jobGroup;
    /** è°ƒç”¨ç›®æ ‡å­—符串 */
    @Excel(name = "调用目标字符串")
    private String invokeTarget;
    /** æ—¥å¿—信息 */
    @Excel(name = "日志信息")
    private String jobMessage;
    /** æ‰§è¡ŒçŠ¶æ€ï¼ˆ0正常 1失败) */
    @Excel(name = "执行状态", readConverterExp = "0=正常,1=失败")
    private String status;
    /** å¼‚常信息 */
    @Excel(name = "异常信息")
    private String exceptionInfo;
    /**
     * åˆ›å»ºæ—¶é—´
     */
    @TableField(fill = FieldFill.INSERT)
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private Date createTime;
    /**
     * è¯·æ±‚参数
     */
    @TableField(exist = false)
    private Map<String, Object> params = new HashMap<>();
    /** å¼€å§‹æ—¶é—´ */
    @TableField(exist = false)
    private Date startTime;
    /** åœæ­¢æ—¶é—´ */
    @TableField(exist = false)
    private Date stopTime;
}
package com.ruoyi.quartz.domain;
import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
import com.alibaba.excel.annotation.ExcelProperty;
import com.baomidou.mybatisplus.annotation.*;
import com.ruoyi.common.annotation.ExcelDictFormat;
import com.ruoyi.common.convert.ExcelDictConvert;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
/**
 * å®šæ—¶ä»»åŠ¡è°ƒåº¦æ—¥å¿—è¡¨ sys_job_log
 *
 * @author ruoyi
 */
@Data
@NoArgsConstructor
@Accessors(chain = true)
@TableName("sys_job_log")
@ExcelIgnoreUnannotated
public class SysJobLog
{
    private static final long serialVersionUID = 1L;
    /** ID */
    @ExcelProperty(value = "日志序号")
    @TableId(value = "job_log_id", type = IdType.AUTO)
    private Long jobLogId;
    /** ä»»åŠ¡åç§° */
    @ExcelProperty(value = "任务名称")
    private String jobName;
    /** ä»»åŠ¡ç»„å */
    @ExcelProperty(value = "任务组名", converter = ExcelDictConvert.class)
    @ExcelDictFormat(dictType = "sys_job_group")
    private String jobGroup;
    /** è°ƒç”¨ç›®æ ‡å­—符串 */
    @ExcelProperty(value = "调用目标字符串")
    private String invokeTarget;
    /** æ—¥å¿—信息 */
    @ExcelProperty(value = "日志信息")
    private String jobMessage;
    /** æ‰§è¡ŒçŠ¶æ€ï¼ˆ0正常 1失败) */
    @ExcelProperty(value = "执行状态", converter = ExcelDictConvert.class)
    @ExcelDictFormat(dictType = "sys_common_status")
    private String status;
    /** å¼‚常信息 */
    @ExcelProperty(value = "异常信息")
    private String exceptionInfo;
    /**
     * åˆ›å»ºæ—¶é—´
     */
    @TableField(fill = FieldFill.INSERT)
    private Date createTime;
    /**
     * è¯·æ±‚参数
     */
    @TableField(exist = false)
    private Map<String, Object> params = new HashMap<>();
    /** å¼€å§‹æ—¶é—´ */
    @TableField(exist = false)
    private Date startTime;
    /** åœæ­¢æ—¶é—´ */
    @TableField(exist = false)
    private Date stopTime;
}
ruoyi-quartz/src/main/java/com/ruoyi/quartz/service/impl/SysJobLogServiceImpl.java
@@ -1,7 +1,6 @@
package com.ruoyi.quartz.service.impl;
import cn.hutool.core.lang.Validator;
import cn.hutool.core.util.StrUtil;
import com.ruoyi.common.utils.StringUtils;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.ruoyi.common.core.mybatisplus.core.ServicePlusImpl;
import com.ruoyi.common.core.page.TableDataInfo;
@@ -27,14 +26,14 @@
    public TableDataInfo<SysJobLog> selectPageJobLogList(SysJobLog jobLog) {
        Map<String, Object> params = jobLog.getParams();
        LambdaQueryWrapper<SysJobLog> lqw = new LambdaQueryWrapper<SysJobLog>()
                .like(StrUtil.isNotBlank(jobLog.getJobName()), SysJobLog::getJobName, jobLog.getJobName())
                .eq(StrUtil.isNotBlank(jobLog.getJobGroup()), SysJobLog::getJobGroup, jobLog.getJobGroup())
                .eq(StrUtil.isNotBlank(jobLog.getStatus()), SysJobLog::getStatus, jobLog.getStatus())
                .like(StrUtil.isNotBlank(jobLog.getInvokeTarget()), SysJobLog::getInvokeTarget, jobLog.getInvokeTarget())
                .apply(Validator.isNotEmpty(params.get("beginTime")),
                .like(StringUtils.isNotBlank(jobLog.getJobName()), SysJobLog::getJobName, jobLog.getJobName())
                .eq(StringUtils.isNotBlank(jobLog.getJobGroup()), SysJobLog::getJobGroup, jobLog.getJobGroup())
                .eq(StringUtils.isNotBlank(jobLog.getStatus()), SysJobLog::getStatus, jobLog.getStatus())
                .like(StringUtils.isNotBlank(jobLog.getInvokeTarget()), SysJobLog::getInvokeTarget, jobLog.getInvokeTarget())
                .apply(StringUtils.isNotEmpty(params.get("beginTime")),
                        "date_format(create_time,'%y%m%d') >= date_format({0},'%y%m%d')",
                        params.get("beginTime"))
                .apply(Validator.isNotEmpty(params.get("endTime")),
                .apply(StringUtils.isNotEmpty(params.get("endTime")),
                        "date_format(create_time,'%y%m%d') <= date_format({0},'%y%m%d')",
                        params.get("endTime"));
        return PageUtils.buildDataInfo(page(PageUtils.buildPage(), lqw));
@@ -50,14 +49,14 @@
    public List<SysJobLog> selectJobLogList(SysJobLog jobLog) {
        Map<String, Object> params = jobLog.getParams();
        return list(new LambdaQueryWrapper<SysJobLog>()
                .like(StrUtil.isNotBlank(jobLog.getJobName()), SysJobLog::getJobName, jobLog.getJobName())
                .eq(StrUtil.isNotBlank(jobLog.getJobGroup()), SysJobLog::getJobGroup, jobLog.getJobGroup())
                .eq(StrUtil.isNotBlank(jobLog.getStatus()), SysJobLog::getStatus, jobLog.getStatus())
                .like(StrUtil.isNotBlank(jobLog.getInvokeTarget()), SysJobLog::getInvokeTarget, jobLog.getInvokeTarget())
                .apply(Validator.isNotEmpty(params.get("beginTime")),
                .like(StringUtils.isNotBlank(jobLog.getJobName()), SysJobLog::getJobName, jobLog.getJobName())
                .eq(StringUtils.isNotBlank(jobLog.getJobGroup()), SysJobLog::getJobGroup, jobLog.getJobGroup())
                .eq(StringUtils.isNotBlank(jobLog.getStatus()), SysJobLog::getStatus, jobLog.getStatus())
                .like(StringUtils.isNotBlank(jobLog.getInvokeTarget()), SysJobLog::getInvokeTarget, jobLog.getInvokeTarget())
                .apply(StringUtils.isNotEmpty(params.get("beginTime")),
                        "date_format(create_time,'%y%m%d') >= date_format({0},'%y%m%d')",
                        params.get("beginTime"))
                .apply(Validator.isNotEmpty(params.get("endTime")),
                .apply(StringUtils.isNotEmpty(params.get("endTime")),
                        "date_format(create_time,'%y%m%d') <= date_format({0},'%y%m%d')",
                        params.get("endTime")));
    }
ruoyi-quartz/src/main/java/com/ruoyi/quartz/service/impl/SysJobServiceImpl.java
@@ -1,6 +1,6 @@
package com.ruoyi.quartz.service.impl;
import cn.hutool.core.util.StrUtil;
import com.ruoyi.common.utils.StringUtils;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.ruoyi.common.constant.ScheduleConstants;
import com.ruoyi.common.core.mybatisplus.core.ServicePlusImpl;
@@ -48,10 +48,10 @@
    @Override
    public TableDataInfo<SysJob> selectPageJobList(SysJob job) {
        LambdaQueryWrapper<SysJob> lqw = new LambdaQueryWrapper<SysJob>()
                .like(StrUtil.isNotBlank(job.getJobName()), SysJob::getJobName, job.getJobName())
                .eq(StrUtil.isNotBlank(job.getJobGroup()), SysJob::getJobGroup, job.getJobGroup())
                .eq(StrUtil.isNotBlank(job.getStatus()), SysJob::getStatus, job.getStatus())
                .like(StrUtil.isNotBlank(job.getInvokeTarget()), SysJob::getInvokeTarget, job.getInvokeTarget());
                .like(StringUtils.isNotBlank(job.getJobName()), SysJob::getJobName, job.getJobName())
                .eq(StringUtils.isNotBlank(job.getJobGroup()), SysJob::getJobGroup, job.getJobGroup())
                .eq(StringUtils.isNotBlank(job.getStatus()), SysJob::getStatus, job.getStatus())
                .like(StringUtils.isNotBlank(job.getInvokeTarget()), SysJob::getInvokeTarget, job.getInvokeTarget());
        return PageUtils.buildDataInfo(page(PageUtils.buildPage(), lqw));
    }
@@ -64,10 +64,10 @@
    @Override
    public List<SysJob> selectJobList(SysJob job) {
        return list(new LambdaQueryWrapper<SysJob>()
                .like(StrUtil.isNotBlank(job.getJobName()), SysJob::getJobName, job.getJobName())
                .eq(StrUtil.isNotBlank(job.getJobGroup()), SysJob::getJobGroup, job.getJobGroup())
                .eq(StrUtil.isNotBlank(job.getStatus()), SysJob::getStatus, job.getStatus())
                .like(StrUtil.isNotBlank(job.getInvokeTarget()), SysJob::getInvokeTarget, job.getInvokeTarget()));
                .like(StringUtils.isNotBlank(job.getJobName()), SysJob::getJobName, job.getJobName())
                .eq(StringUtils.isNotBlank(job.getJobGroup()), SysJob::getJobGroup, job.getJobGroup())
                .eq(StringUtils.isNotBlank(job.getStatus()), SysJob::getStatus, job.getStatus())
                .like(StringUtils.isNotBlank(job.getInvokeTarget()), SysJob::getInvokeTarget, job.getInvokeTarget()));
    }
    /**
ruoyi-quartz/src/main/java/com/ruoyi/quartz/task/RyTask.java
@@ -1,29 +1,29 @@
package com.ruoyi.quartz.task;
import cn.hutool.core.lang.Console;
import cn.hutool.core.util.StrUtil;
import org.springframework.stereotype.Component;
/**
 * å®šæ—¶ä»»åŠ¡è°ƒåº¦æµ‹è¯•
 *
 * @author ruoyi
 */
@Component("ryTask")
public class RyTask
{
    public void ryMultipleParams(String s, Boolean b, Long l, Double d, Integer i)
    {
        Console.log(StrUtil.format("执行多参方法: å­—符串类型{},布尔类型{},长整型{},浮点型{},整形{}", s, b, l, d, i));
    }
    public void ryParams(String params)
    {
        Console.log("执行有参方法:" + params);
    }
    public void ryNoParams()
    {
        Console.log("执行无参方法");
    }
}
package com.ruoyi.quartz.task;
import cn.hutool.core.lang.Console;
import com.ruoyi.common.utils.StringUtils;
import org.springframework.stereotype.Component;
/**
 * å®šæ—¶ä»»åŠ¡è°ƒåº¦æµ‹è¯•
 *
 * @author ruoyi
 */
@Component("ryTask")
public class RyTask
{
    public void ryMultipleParams(String s, Boolean b, Long l, Double d, Integer i)
    {
        Console.log(StringUtils.format("执行多参方法: å­—符串类型{},布尔类型{},长整型{},浮点型{},整形{}", s, b, l, d, i));
    }
    public void ryParams(String params)
    {
        Console.log("执行有参方法:" + params);
    }
    public void ryNoParams()
    {
        Console.log("执行无参方法");
    }
}
ruoyi-quartz/src/main/java/com/ruoyi/quartz/util/AbstractQuartzJob.java
@@ -1,109 +1,108 @@
package com.ruoyi.quartz.util;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.exceptions.ExceptionUtil;
import cn.hutool.core.lang.Validator;
import cn.hutool.core.util.StrUtil;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.constant.ScheduleConstants;
import com.ruoyi.common.utils.spring.SpringUtils;
import com.ruoyi.quartz.domain.SysJob;
import com.ruoyi.quartz.domain.SysJobLog;
import com.ruoyi.quartz.service.ISysJobLogService;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Date;
/**
 * æŠ½è±¡quartz调用
 *
 * @author ruoyi
 */
public abstract class AbstractQuartzJob implements Job
{
    private static final Logger log = LoggerFactory.getLogger(AbstractQuartzJob.class);
    /**
     * çº¿ç¨‹æœ¬åœ°å˜é‡
     */
    private static ThreadLocal<Date> threadLocal = new ThreadLocal<>();
    @Override
    public void execute(JobExecutionContext context) throws JobExecutionException
    {
        SysJob sysJob = new SysJob();
        BeanUtil.copyProperties(context.getMergedJobDataMap().get(ScheduleConstants.TASK_PROPERTIES),sysJob);
        try
        {
            before(context, sysJob);
            if (Validator.isNotNull(sysJob))
            {
                doExecute(context, sysJob);
            }
            after(context, sysJob, null);
        }
        catch (Exception e)
        {
            log.error("任务执行异常  - ï¼š", e);
            after(context, sysJob, e);
        }
    }
    /**
     * æ‰§è¡Œå‰
     *
     * @param context å·¥ä½œæ‰§è¡Œä¸Šä¸‹æ–‡å¯¹è±¡
     * @param sysJob ç³»ç»Ÿè®¡åˆ’任务
     */
    protected void before(JobExecutionContext context, SysJob sysJob)
    {
        threadLocal.set(new Date());
    }
    /**
     * æ‰§è¡ŒåŽ
     *
     * @param context å·¥ä½œæ‰§è¡Œä¸Šä¸‹æ–‡å¯¹è±¡
     * @param sysJob ç³»ç»Ÿè®¡åˆ’任务
     */
    protected void after(JobExecutionContext context, SysJob sysJob, Exception e)
    {
        Date startTime = threadLocal.get();
        threadLocal.remove();
        final SysJobLog sysJobLog = new SysJobLog();
        sysJobLog.setJobName(sysJob.getJobName());
        sysJobLog.setJobGroup(sysJob.getJobGroup());
        sysJobLog.setInvokeTarget(sysJob.getInvokeTarget());
        sysJobLog.setStartTime(startTime);
        sysJobLog.setStopTime(new Date());
        long runMs = sysJobLog.getStopTime().getTime() - sysJobLog.getStartTime().getTime();
        sysJobLog.setJobMessage(sysJobLog.getJobName() + " æ€»å…±è€—时:" + runMs + "毫秒");
        if (e != null)
        {
            sysJobLog.setStatus(Constants.FAIL);
            String errorMsg = StrUtil.sub(ExceptionUtil.stacktraceToString(e), 0, 2000);
            sysJobLog.setExceptionInfo(errorMsg);
        }
        else
        {
            sysJobLog.setStatus(Constants.SUCCESS);
        }
        // å†™å…¥æ•°æ®åº“当中
        SpringUtils.getBean(ISysJobLogService.class).addJobLog(sysJobLog);
    }
    /**
     * æ‰§è¡Œæ–¹æ³•,由子类重载
     *
     * @param context å·¥ä½œæ‰§è¡Œä¸Šä¸‹æ–‡å¯¹è±¡
     * @param sysJob ç³»ç»Ÿè®¡åˆ’任务
     * @throws Exception æ‰§è¡Œè¿‡ç¨‹ä¸­çš„异常
     */
    protected abstract void doExecute(JobExecutionContext context, SysJob sysJob) throws Exception;
}
package com.ruoyi.quartz.util;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.exceptions.ExceptionUtil;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.constant.ScheduleConstants;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.spring.SpringUtils;
import com.ruoyi.quartz.domain.SysJob;
import com.ruoyi.quartz.domain.SysJobLog;
import com.ruoyi.quartz.service.ISysJobLogService;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Date;
/**
 * æŠ½è±¡quartz调用
 *
 * @author ruoyi
 */
public abstract class AbstractQuartzJob implements Job
{
    private static final Logger log = LoggerFactory.getLogger(AbstractQuartzJob.class);
    /**
     * çº¿ç¨‹æœ¬åœ°å˜é‡
     */
    private static ThreadLocal<Date> threadLocal = new ThreadLocal<>();
    @Override
    public void execute(JobExecutionContext context) throws JobExecutionException
    {
        SysJob sysJob = new SysJob();
        BeanUtil.copyProperties(context.getMergedJobDataMap().get(ScheduleConstants.TASK_PROPERTIES),sysJob);
        try
        {
            before(context, sysJob);
            if (StringUtils.isNotNull(sysJob))
            {
                doExecute(context, sysJob);
            }
            after(context, sysJob, null);
        }
        catch (Exception e)
        {
            log.error("任务执行异常  - ï¼š", e);
            after(context, sysJob, e);
        }
    }
    /**
     * æ‰§è¡Œå‰
     *
     * @param context å·¥ä½œæ‰§è¡Œä¸Šä¸‹æ–‡å¯¹è±¡
     * @param sysJob ç³»ç»Ÿè®¡åˆ’任务
     */
    protected void before(JobExecutionContext context, SysJob sysJob)
    {
        threadLocal.set(new Date());
    }
    /**
     * æ‰§è¡ŒåŽ
     *
     * @param context å·¥ä½œæ‰§è¡Œä¸Šä¸‹æ–‡å¯¹è±¡
     * @param sysJob ç³»ç»Ÿè®¡åˆ’任务
     */
    protected void after(JobExecutionContext context, SysJob sysJob, Exception e)
    {
        Date startTime = threadLocal.get();
        threadLocal.remove();
        final SysJobLog sysJobLog = new SysJobLog();
        sysJobLog.setJobName(sysJob.getJobName());
        sysJobLog.setJobGroup(sysJob.getJobGroup());
        sysJobLog.setInvokeTarget(sysJob.getInvokeTarget());
        sysJobLog.setStartTime(startTime);
        sysJobLog.setStopTime(new Date());
        long runMs = sysJobLog.getStopTime().getTime() - sysJobLog.getStartTime().getTime();
        sysJobLog.setJobMessage(sysJobLog.getJobName() + " æ€»å…±è€—时:" + runMs + "毫秒");
        if (e != null)
        {
            sysJobLog.setStatus(Constants.FAIL);
            String errorMsg = StringUtils.substring(ExceptionUtil.stacktraceToString(e), 0, 2000);
            sysJobLog.setExceptionInfo(errorMsg);
        }
        else
        {
            sysJobLog.setStatus(Constants.SUCCESS);
        }
        // å†™å…¥æ•°æ®åº“当中
        SpringUtils.getBean(ISysJobLogService.class).addJobLog(sysJobLog);
    }
    /**
     * æ‰§è¡Œæ–¹æ³•,由子类重载
     *
     * @param context å·¥ä½œæ‰§è¡Œä¸Šä¸‹æ–‡å¯¹è±¡
     * @param sysJob ç³»ç»Ÿè®¡åˆ’任务
     * @throws Exception æ‰§è¡Œè¿‡ç¨‹ä¸­çš„异常
     */
    protected abstract void doExecute(JobExecutionContext context, SysJob sysJob) throws Exception;
}
ruoyi-quartz/src/main/java/com/ruoyi/quartz/util/JobInvokeUtil.java
@@ -1,184 +1,183 @@
package com.ruoyi.quartz.util;
import cn.hutool.core.lang.Validator;
import cn.hutool.core.util.StrUtil;
import com.ruoyi.common.utils.spring.SpringUtils;
import com.ruoyi.quartz.domain.SysJob;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.LinkedList;
import java.util.List;
/**
 * ä»»åŠ¡æ‰§è¡Œå·¥å…·
 *
 * @author ruoyi
 */
public class JobInvokeUtil
{
    /**
     * æ‰§è¡Œæ–¹æ³•
     *
     * @param sysJob ç³»ç»Ÿä»»åŠ¡
     */
    public static void invokeMethod(SysJob sysJob) throws Exception
    {
        String invokeTarget = sysJob.getInvokeTarget();
        String beanName = getBeanName(invokeTarget);
        String methodName = getMethodName(invokeTarget);
        List<Object[]> methodParams = getMethodParams(invokeTarget);
        if (!isValidClassName(beanName))
        {
            Object bean = SpringUtils.getBean(beanName);
            invokeMethod(bean, methodName, methodParams);
        }
        else
        {
            Object bean = Class.forName(beanName).newInstance();
            invokeMethod(bean, methodName, methodParams);
        }
    }
    /**
     * è°ƒç”¨ä»»åŠ¡æ–¹æ³•
     *
     * @param bean ç›®æ ‡å¯¹è±¡
     * @param methodName æ–¹æ³•名称
     * @param methodParams æ–¹æ³•参数
     */
    private static void invokeMethod(Object bean, String methodName, List<Object[]> methodParams)
            throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException,
            InvocationTargetException
    {
        if (Validator.isNotNull(methodParams) && methodParams.size() > 0)
        {
            Method method = bean.getClass().getDeclaredMethod(methodName, getMethodParamsType(methodParams));
            method.invoke(bean, getMethodParamsValue(methodParams));
        }
        else
        {
            Method method = bean.getClass().getDeclaredMethod(methodName);
            method.invoke(bean);
        }
    }
    /**
     * æ ¡éªŒæ˜¯å¦ä¸ºä¸ºclass包名
     *
     * @param str åç§°
     * @return true是 false否
     */
    public static boolean isValidClassName(String invokeTarget)
    {
        return StrUtil.count(invokeTarget, ".") > 1;
    }
    /**
     * èŽ·å–bean名称
     *
     * @param invokeTarget ç›®æ ‡å­—符串
     * @return bean名称
     */
    public static String getBeanName(String invokeTarget)
    {
        String beanName = StrUtil.subBefore(invokeTarget, "(",false);
        return StrUtil.subBefore(beanName, ".",true);
    }
    /**
     * èŽ·å–bean方法
     *
     * @param invokeTarget ç›®æ ‡å­—符串
     * @return method方法
     */
    public static String getMethodName(String invokeTarget)
    {
        String methodName = StrUtil.subBefore(invokeTarget, "(",false);
        return StrUtil.subAfter(methodName, ".",true);
    }
    /**
     * èŽ·å–method方法参数相关列表
     *
     * @param invokeTarget ç›®æ ‡å­—符串
     * @return method方法相关参数列表
     */
    public static List<Object[]> getMethodParams(String invokeTarget)
    {
        String methodStr = StrUtil.subBetween(invokeTarget, "(", ")");
        if (StrUtil.isEmpty(methodStr))
        {
            return null;
        }
        String[] methodParams = methodStr.split(",");
        List<Object[]> classs = new LinkedList<>();
        for (int i = 0; i < methodParams.length; i++)
        {
            String str = StrUtil.trimToEmpty(methodParams[i]);
            // String字符串类型,包含'
            if (StrUtil.contains(str, "'"))
            {
                classs.add(new Object[] { StrUtil.replace(str, "'", ""), String.class });
            }
            // boolean布尔类型,等于true或者false
            else if (StrUtil.equals(str, "true") || StrUtil.equalsIgnoreCase(str, "false"))
            {
                classs.add(new Object[] { Boolean.valueOf(str), Boolean.class });
            }
            // long长整形,包含L
            else if (StrUtil.containsIgnoreCase(str, "L"))
            {
                classs.add(new Object[] { Long.valueOf(StrUtil.replaceIgnoreCase(str, "L", "")), Long.class });
            }
            // double浮点类型,包含D
            else if (StrUtil.containsIgnoreCase(str, "D"))
            {
                classs.add(new Object[] { Double.valueOf(StrUtil.replaceIgnoreCase(str, "D", "")), Double.class });
            }
            // å…¶ä»–类型归类为整形
            else
            {
                classs.add(new Object[] { Integer.valueOf(str), Integer.class });
            }
        }
        return classs;
    }
    /**
     * èŽ·å–å‚æ•°ç±»åž‹
     *
     * @param methodParams å‚数相关列表
     * @return å‚数类型列表
     */
    public static Class<?>[] getMethodParamsType(List<Object[]> methodParams)
    {
        Class<?>[] classs = new Class<?>[methodParams.size()];
        int index = 0;
        for (Object[] os : methodParams)
        {
            classs[index] = (Class<?>) os[1];
            index++;
        }
        return classs;
    }
    /**
     * èŽ·å–å‚æ•°å€¼
     *
     * @param methodParams å‚数相关列表
     * @return å‚数值列表
     */
    public static Object[] getMethodParamsValue(List<Object[]> methodParams)
    {
        Object[] classs = new Object[methodParams.size()];
        int index = 0;
        for (Object[] os : methodParams)
        {
            classs[index] = (Object) os[0];
            index++;
        }
        return classs;
    }
}
package com.ruoyi.quartz.util;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.spring.SpringUtils;
import com.ruoyi.quartz.domain.SysJob;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.LinkedList;
import java.util.List;
/**
 * ä»»åŠ¡æ‰§è¡Œå·¥å…·
 *
 * @author ruoyi
 */
public class JobInvokeUtil
{
    /**
     * æ‰§è¡Œæ–¹æ³•
     *
     * @param sysJob ç³»ç»Ÿä»»åŠ¡
     */
    public static void invokeMethod(SysJob sysJob) throws Exception
    {
        String invokeTarget = sysJob.getInvokeTarget();
        String beanName = getBeanName(invokeTarget);
        String methodName = getMethodName(invokeTarget);
        List<Object[]> methodParams = getMethodParams(invokeTarget);
        if (!isValidClassName(beanName))
        {
            Object bean = SpringUtils.getBean(beanName);
            invokeMethod(bean, methodName, methodParams);
        }
        else
        {
            Object bean = Class.forName(beanName).newInstance();
            invokeMethod(bean, methodName, methodParams);
        }
    }
    /**
     * è°ƒç”¨ä»»åŠ¡æ–¹æ³•
     *
     * @param bean ç›®æ ‡å¯¹è±¡
     * @param methodName æ–¹æ³•名称
     * @param methodParams æ–¹æ³•参数
     */
    private static void invokeMethod(Object bean, String methodName, List<Object[]> methodParams)
            throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException,
            InvocationTargetException
    {
        if (StringUtils.isNotNull(methodParams) && methodParams.size() > 0)
        {
            Method method = bean.getClass().getDeclaredMethod(methodName, getMethodParamsType(methodParams));
            method.invoke(bean, getMethodParamsValue(methodParams));
        }
        else
        {
            Method method = bean.getClass().getDeclaredMethod(methodName);
            method.invoke(bean);
        }
    }
    /**
     * æ ¡éªŒæ˜¯å¦ä¸ºä¸ºclass包名
     *
     * @param str åç§°
     * @return true是 false否
     */
    public static boolean isValidClassName(String invokeTarget)
    {
        return StringUtils.countMatches(invokeTarget, ".") > 1;
    }
    /**
     * èŽ·å–bean名称
     *
     * @param invokeTarget ç›®æ ‡å­—符串
     * @return bean名称
     */
    public static String getBeanName(String invokeTarget)
    {
        String beanName = StringUtils.substringBefore(invokeTarget, "(");
        return StringUtils.substringBefore(beanName, ".");
    }
    /**
     * èŽ·å–bean方法
     *
     * @param invokeTarget ç›®æ ‡å­—符串
     * @return method方法
     */
    public static String getMethodName(String invokeTarget)
    {
        String methodName = StringUtils.substringBefore(invokeTarget, "(");
        return StringUtils.substringBefore(methodName, ".");
    }
    /**
     * èŽ·å–method方法参数相关列表
     *
     * @param invokeTarget ç›®æ ‡å­—符串
     * @return method方法相关参数列表
     */
    public static List<Object[]> getMethodParams(String invokeTarget)
    {
        String methodStr = StringUtils.substringBetween(invokeTarget, "(", ")");
        if (StringUtils.isEmpty(methodStr))
        {
            return null;
        }
        String[] methodParams = methodStr.split(",");
        List<Object[]> classs = new LinkedList<>();
        for (int i = 0; i < methodParams.length; i++)
        {
            String str = StringUtils.trimToEmpty(methodParams[i]);
            // String字符串类型,包含'
            if (StringUtils.contains(str, "'"))
            {
                classs.add(new Object[] { StringUtils.replace(str, "'", ""), String.class });
            }
            // boolean布尔类型,等于true或者false
            else if (StringUtils.equals(str, "true") || StringUtils.equalsIgnoreCase(str, "false"))
            {
                classs.add(new Object[] { Boolean.valueOf(str), Boolean.class });
            }
            // long长整形,包含L
            else if (StringUtils.containsIgnoreCase(str, "L"))
            {
                classs.add(new Object[] { Long.valueOf(StringUtils.replaceIgnoreCase(str, "L", "")), Long.class });
            }
            // double浮点类型,包含D
            else if (StringUtils.containsIgnoreCase(str, "D"))
            {
                classs.add(new Object[] { Double.valueOf(StringUtils.replaceIgnoreCase(str, "D", "")), Double.class });
            }
            // å…¶ä»–类型归类为整形
            else
            {
                classs.add(new Object[] { Integer.valueOf(str), Integer.class });
            }
        }
        return classs;
    }
    /**
     * èŽ·å–å‚æ•°ç±»åž‹
     *
     * @param methodParams å‚数相关列表
     * @return å‚数类型列表
     */
    public static Class<?>[] getMethodParamsType(List<Object[]> methodParams)
    {
        Class<?>[] classs = new Class<?>[methodParams.size()];
        int index = 0;
        for (Object[] os : methodParams)
        {
            classs[index] = (Class<?>) os[1];
            index++;
        }
        return classs;
    }
    /**
     * èŽ·å–å‚æ•°å€¼
     *
     * @param methodParams å‚数相关列表
     * @return å‚数值列表
     */
    public static Object[] getMethodParamsValue(List<Object[]> methodParams)
    {
        Object[] classs = new Object[methodParams.size()];
        int index = 0;
        for (Object[] os : methodParams)
        {
            classs[index] = (Object) os[0];
            index++;
        }
        return classs;
    }
}
ruoyi-system/pom.xml
@@ -5,7 +5,7 @@
    <parent>
        <artifactId>ruoyi-vue-plus</artifactId>
        <groupId>com.ruoyi</groupId>
        <version>2.6.0</version>
        <version>3.0.0</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>
@@ -23,6 +23,12 @@
            <artifactId>ruoyi-common</artifactId>
        </dependency>
        <!-- OSS功能模块 -->
        <dependency>
            <groupId>com.ruoyi</groupId>
            <artifactId>ruoyi-oss</artifactId>
        </dependency>
    </dependencies>
</project>
ruoyi-system/src/main/java/com/ruoyi/system/domain/SysConfig.java
@@ -1,9 +1,10 @@
package com.ruoyi.system.domain;
import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
import com.alibaba.excel.annotation.ExcelProperty;
import com.baomidou.mybatisplus.annotation.*;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.ruoyi.common.annotation.Excel;
import com.ruoyi.common.annotation.Excel.ColumnType;
import com.ruoyi.common.annotation.ExcelDictFormat;
import com.ruoyi.common.convert.ExcelDictConvert;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
@@ -25,20 +26,21 @@
@NoArgsConstructor
@Accessors(chain = true)
@TableName("sys_config")
@ExcelIgnoreUnannotated
public class SysConfig implements Serializable {
    private static final long serialVersionUID = 1L;
    /**
     * å‚数主键
     */
    @Excel(name = "参数主键", cellType = ColumnType.NUMERIC)
    @ExcelProperty(value = "参数主键")
    @TableId(value = "config_id", type = IdType.AUTO)
    private Long configId;
    /**
     * å‚数名称
     */
    @Excel(name = "参数名称")
    @ExcelProperty(value = "参数名称")
    @NotBlank(message = "参数名称不能为空")
    @Size(min = 0, max = 100, message = "参数名称不能超过100个字符")
    private String configName;
@@ -46,7 +48,7 @@
    /**
     * å‚数键名
     */
    @Excel(name = "参数键名")
    @ExcelProperty(value = "参数键名")
    @NotBlank(message = "参数键名长度不能为空")
    @Size(min = 0, max = 100, message = "参数键名长度不能超过100个字符")
    private String configKey;
@@ -54,7 +56,7 @@
    /**
     * å‚数键值
     */
    @Excel(name = "参数键值")
    @ExcelProperty(value = "参数键值")
    @NotBlank(message = "参数键值不能为空")
    @Size(min = 0, max = 500, message = "参数键值长度不能超过500个字符")
    private String configValue;
@@ -62,7 +64,8 @@
    /**
     * ç³»ç»Ÿå†…置(Y是 N否)
     */
    @Excel(name = "系统内置", readConverterExp = "Y=是,N=否")
    @ExcelProperty(value = "系统内置", converter = ExcelDictConvert.class)
    @ExcelDictFormat(dictType = "sys_yes_no")
    private String configType;
    /**
@@ -75,7 +78,6 @@
     * åˆ›å»ºæ—¶é—´
     */
    @TableField(fill = FieldFill.INSERT)
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private Date createTime;
    /**
@@ -88,7 +90,6 @@
     * æ›´æ–°æ—¶é—´
     */
    @TableField(fill = FieldFill.INSERT_UPDATE)
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private Date updateTime;
    /**
ruoyi-system/src/main/java/com/ruoyi/system/domain/SysLogininfor.java
@@ -1,12 +1,13 @@
package com.ruoyi.system.domain;
import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
import com.alibaba.excel.annotation.ExcelProperty;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.ruoyi.common.annotation.Excel;
import com.ruoyi.common.annotation.Excel.ColumnType;
import com.ruoyi.common.annotation.ExcelDictFormat;
import com.ruoyi.common.convert.ExcelDictConvert;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
@@ -26,63 +27,64 @@
@NoArgsConstructor
@Accessors(chain = true)
@TableName("sys_logininfor")
@ExcelIgnoreUnannotated
public class SysLogininfor implements Serializable {
    private static final long serialVersionUID = 1L;
    /**
     * ID
     */
    @Excel(name = "序号", cellType = ColumnType.NUMERIC)
    @ExcelProperty(value = "序号")
    @TableId(value = "info_id", type = IdType.AUTO)
    private Long infoId;
    /**
     * ç”¨æˆ·è´¦å·
     */
    @Excel(name = "用户账号")
    @ExcelProperty(value = "用户账号")
    private String userName;
    /**
     * ç™»å½•状态 0成功 1失败
     */
    @Excel(name = "登录状态", readConverterExp = "0=成功,1=失败")
    @ExcelProperty(value = "登录状态", converter = ExcelDictConvert.class)
    @ExcelDictFormat(dictType = "sys_common_status")
    private String status;
    /**
     * ç™»å½•IP地址
     */
    @Excel(name = "登录地址")
    @ExcelProperty(value = "登录地址")
    private String ipaddr;
    /**
     * ç™»å½•地点
     */
    @Excel(name = "登录地点")
    @ExcelProperty(value = "登录地点")
    private String loginLocation;
    /**
     * æµè§ˆå™¨ç±»åž‹
     */
    @Excel(name = "浏览器")
    @ExcelProperty(value = "浏览器")
    private String browser;
    /**
     * æ“ä½œç³»ç»Ÿ
     */
    @Excel(name = "操作系统")
    @ExcelProperty(value = "操作系统")
    private String os;
    /**
     * æç¤ºæ¶ˆæ¯
     */
    @Excel(name = "提示消息")
    @ExcelProperty(value = "提示消息")
    private String msg;
    /**
     * è®¿é—®æ—¶é—´
     */
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    @Excel(name = "访问时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
    @ExcelProperty(value = "访问时间")
    private Date loginTime;
    /**
ruoyi-system/src/main/java/com/ruoyi/system/domain/SysOperLog.java
@@ -1,12 +1,13 @@
package com.ruoyi.system.domain;
import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
import com.alibaba.excel.annotation.ExcelProperty;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.ruoyi.common.annotation.Excel;
import com.ruoyi.common.annotation.Excel.ColumnType;
import com.ruoyi.common.annotation.ExcelDictFormat;
import com.ruoyi.common.convert.ExcelDictConvert;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
@@ -26,26 +27,28 @@
@NoArgsConstructor
@Accessors(chain = true)
@TableName("sys_oper_log")
@ExcelIgnoreUnannotated
public class SysOperLog implements Serializable {
    private static final long serialVersionUID = 1L;
    /**
     * æ—¥å¿—主键
     */
    @Excel(name = "操作序号", cellType = ColumnType.NUMERIC)
    @ExcelProperty(value = "操作序号")
    @TableId(value = "oper_id", type = IdType.AUTO)
    private Long operId;
    /**
     * æ“ä½œæ¨¡å—
     */
    @Excel(name = "操作模块")
    @ExcelProperty(value = "操作模块")
    private String title;
    /**
     * ä¸šåŠ¡ç±»åž‹ï¼ˆ0其它 1新增 2修改 3删除)
     */
    @Excel(name = "业务类型", readConverterExp = "0=其它,1=新增,2=修改,3=删除,4=授权,5=导出,6=导入,7=强退,8=生成代码,9=清空数据")
    @ExcelProperty(value = "业务类型", converter = ExcelDictConvert.class)
    @ExcelDictFormat(dictType = "sys_oper_type")
    private Integer businessType;
    /**
@@ -57,80 +60,81 @@
    /**
     * è¯·æ±‚方法
     */
    @Excel(name = "请求方法")
    @ExcelProperty(value = "请求方法")
    private String method;
    /**
     * è¯·æ±‚方式
     */
    @Excel(name = "请求方式")
    @ExcelProperty(value = "请求方式")
    private String requestMethod;
    /**
     * æ“ä½œç±»åˆ«ï¼ˆ0其它 1后台用户 2手机端用户)
     */
    @Excel(name = "操作类别", readConverterExp = "0=其它,1=后台用户,2=手机端用户")
    @ExcelProperty(value = "操作类别", converter = ExcelDictConvert.class)
    @ExcelDictFormat(readConverterExp = "0=其它,1=后台用户,2=手机端用户")
    private Integer operatorType;
    /**
     * æ“ä½œäººå‘˜
     */
    @Excel(name = "操作人员")
    @ExcelProperty(value = "操作人员")
    private String operName;
    /**
     * éƒ¨é—¨åç§°
     */
    @Excel(name = "部门名称")
    @ExcelProperty(value = "部门名称")
    private String deptName;
    /**
     * è¯·æ±‚url
     */
    @Excel(name = "请求地址")
    @ExcelProperty(value = "请求地址")
    private String operUrl;
    /**
     * æ“ä½œåœ°å€
     */
    @Excel(name = "操作地址")
    @ExcelProperty(value = "操作地址")
    private String operIp;
    /**
     * æ“ä½œåœ°ç‚¹
     */
    @Excel(name = "操作地点")
    @ExcelProperty(value = "操作地点")
    private String operLocation;
    /**
     * è¯·æ±‚参数
     */
    @Excel(name = "请求参数")
    @ExcelProperty(value = "请求参数")
    private String operParam;
    /**
     * è¿”回参数
     */
    @Excel(name = "返回参数")
    @ExcelProperty(value = "返回参数")
    private String jsonResult;
    /**
     * æ“ä½œçŠ¶æ€ï¼ˆ0正常 1异常)
     */
    @Excel(name = "状态", readConverterExp = "0=正常,1=异常")
    @ExcelProperty(value = "状态", converter = ExcelDictConvert.class)
    @ExcelDictFormat(dictType = "sys_common_status")
    private Integer status;
    /**
     * é”™è¯¯æ¶ˆæ¯
     */
    @Excel(name = "错误消息")
    @ExcelProperty(value = "错误消息")
    private String errorMsg;
    /**
     * æ“ä½œæ—¶é—´
     */
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    @Excel(name = "操作时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
    @ExcelProperty(value = "操作时间")
    private Date operTime;
    /**
ruoyi-system/src/main/java/com/ruoyi/system/domain/SysOss.java
ruoyi-system/src/main/java/com/ruoyi/system/domain/SysOssConfig.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,111 @@
package com.ruoyi.system.domain;
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
import java.io.Serializable;
import java.util.Date;
import java.math.BigDecimal;
/**
 * äº‘存储配置对象 sys_oss_config
 *
 * @author ruoyi
 * @date 2021-08-11
 */
@Data
@NoArgsConstructor
@Accessors(chain = true)
@TableName("sys_oss_config")
public class SysOssConfig implements Serializable {
    private static final long serialVersionUID=1L;
    /**
     * ä¸»å»º
     */
    @TableId(value = "oss_config_id")
    private Integer ossConfigId;
    /**
     * é…ç½®key
     */
    private String configKey;
    /**
     * accessKey
     */
    private String accessKey;
    /**
     * ç§˜é’¥
     */
    private String secretKey;
    /**
     * æ¡¶åç§°
     */
    private String bucketName;
    /**
     * å‰ç¼€
     */
    private String prefix;
    /**
     * è®¿é—®ç«™ç‚¹
     */
    private String endpoint;
    /**
     * æ˜¯å¦https(0否 1是)
     */
    private String isHttps;
    /**
     * åŸŸ
     */
    private String region;
    /**
     * çŠ¶æ€(0正常 1停用)
     */
    private String status;
    /**
     * æ‰©å±•字段
     */
    private String ext1;
    /**
     * åˆ›å»ºè€…
     */
    @TableField(fill = FieldFill.INSERT)
    private String createBy;
    /**
     * åˆ›å»ºæ—¶é—´
     */
    @TableField(fill = FieldFill.INSERT)
    private Date createTime;
    /**
     * æ›´æ–°è€…
     */
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private String updateBy;
    /**
     * æ›´æ–°æ—¶é—´
     */
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private Date updateTime;
    /**
     * å¤‡æ³¨
     */
    private String remark;
}
ruoyi-system/src/main/java/com/ruoyi/system/domain/SysPost.java
@@ -1,9 +1,10 @@
package com.ruoyi.system.domain;
import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
import com.alibaba.excel.annotation.ExcelProperty;
import com.baomidou.mybatisplus.annotation.*;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.ruoyi.common.annotation.Excel;
import com.ruoyi.common.annotation.Excel.ColumnType;
import com.ruoyi.common.annotation.ExcelDictFormat;
import com.ruoyi.common.convert.ExcelDictConvert;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
@@ -25,20 +26,21 @@
@NoArgsConstructor
@Accessors(chain = true)
@TableName("sys_post")
@ExcelIgnoreUnannotated
public class SysPost implements Serializable {
    private static final long serialVersionUID = 1L;
    /**
     * å²—位序号
     */
    @Excel(name = "岗位序号", cellType = ColumnType.NUMERIC)
    @ExcelProperty(value = "岗位序号")
    @TableId(value = "post_id", type = IdType.AUTO)
    private Long postId;
    /**
     * å²—位编码
     */
    @Excel(name = "岗位编码")
    @ExcelProperty(value = "岗位编码")
    @NotBlank(message = "岗位编码不能为空")
    @Size(min = 0, max = 64, message = "岗位编码长度不能超过64个字符")
    private String postCode;
@@ -46,7 +48,7 @@
    /**
     * å²—位名称
     */
    @Excel(name = "岗位名称")
    @ExcelProperty(value = "岗位名称")
    @NotBlank(message = "岗位名称不能为空")
    @Size(min = 0, max = 50, message = "岗位名称长度不能超过50个字符")
    private String postName;
@@ -54,14 +56,15 @@
    /**
     * å²—位排序
     */
    @Excel(name = "岗位排序")
    @ExcelProperty(value = "岗位排序")
    @NotBlank(message = "显示顺序不能为空")
    private String postSort;
    /**
     * çŠ¶æ€ï¼ˆ0正常 1停用)
     */
    @Excel(name = "状态", readConverterExp = "0=正常,1=停用")
    @ExcelProperty(value = "状态", converter = ExcelDictConvert.class)
    @ExcelDictFormat(dictType = "sys_common_status")
    private String status;
    /**
@@ -74,7 +77,6 @@
     * åˆ›å»ºæ—¶é—´
     */
    @TableField(fill = FieldFill.INSERT)
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private Date createTime;
    /**
@@ -87,7 +89,6 @@
     * æ›´æ–°æ—¶é—´
     */
    @TableField(fill = FieldFill.INSERT_UPDATE)
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private Date updateTime;
    /**
ruoyi-system/src/main/java/com/ruoyi/system/domain/bo/SysOssBo.java
ruoyi-system/src/main/java/com/ruoyi/system/domain/bo/SysOssConfigBo.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,130 @@
package com.ruoyi.system.domain.bo;
import com.ruoyi.common.core.domain.BaseEntity;
import com.ruoyi.common.core.validate.AddGroup;
import com.ruoyi.common.core.validate.EditGroup;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
/**
 * äº‘存储配置业务对象 sys_oss_config
 *
 * @author Lion Li
 * @author å­¤èˆŸçƒŸé›¨
 * @date 2021-08-13
 */
@Data
@EqualsAndHashCode(callSuper = true)
@ApiModel("云存储配置业务对象")
public class SysOssConfigBo extends BaseEntity {
    /**
     * ä¸»å»º
     */
    @ApiModelProperty(value = "主建", required = true)
    @NotNull(message = "主建不能为空", groups = { EditGroup.class })
    private Long ossConfigId;
    /**
     * é…ç½®key
     */
    @ApiModelProperty(value = "configKey", required = true)
    @NotBlank(message = "configKey不能为空", groups = { AddGroup.class, EditGroup.class })
    @Size(min = 2, max = 100, message = "configKey长度必须介于2和20 ä¹‹é—´")
    private String configKey;
    /**
     * accessKey
     */
    @ApiModelProperty(value = "accessKey", required = true)
    @NotBlank(message = "accessKey不能为空", groups = { AddGroup.class, EditGroup.class })
    @Size(min = 2, max = 100, message = "accessKey长度必须介于2和100 ä¹‹é—´")
    private String accessKey;
    /**
     * ç§˜é’¥
     */
    @ApiModelProperty(value = "secretKey", required = true)
    @NotBlank(message = "secretKey不能为空", groups = { AddGroup.class, EditGroup.class })
    @Size(min = 2, max = 100, message = "secretKey长度必须介于2和100 ä¹‹é—´")
    private String secretKey;
    /**
     * æ¡¶åç§°
     */
    @ApiModelProperty(value = "bucketName", required = true)
    @NotBlank(message = "bucketName不能为空", groups = { AddGroup.class, EditGroup.class })
    @Size(min = 2, max = 100, message = "bucketName长度必须介于2和100之间")
    private String bucketName;
    /**
     * å‰ç¼€
     */
    @ApiModelProperty(value = "前缀")
    private String prefix;
    /**
     * è®¿é—®ç«™ç‚¹
     */
    @ApiModelProperty(value = "endpoint", required = true)
    @NotBlank(message = "endpoint不能为空", groups = { AddGroup.class, EditGroup.class })
    @Size(min = 2, max = 100, message = "endpoint长度必须介于2和100之间")
    private String endpoint;
    /**
     * æ˜¯å¦https(Y=是,N=否)
     */
    @ApiModelProperty("是否https(Y=是,N=否)")
    private String isHttps;
    /**
     * çŠ¶æ€ï¼ˆ0=正常,1=停用)
     */
    @ApiModelProperty("状态(0=正常,1=停用)")
    private String status;
    /**
     * åŸŸ
     */
    @ApiModelProperty(value = "region")
    private String region;
    /**
     * æ‰©å±•字段
     */
    @ApiModelProperty(value = "扩展字段")
    private String ext1;
    /**
     * åˆ†é¡µå¤§å°
     */
    @ApiModelProperty("分页大小")
    private Integer pageSize;
    /**
     * å½“前页数
     */
    @ApiModelProperty("当前页数")
    private Integer pageNum;
    /**
     * æŽ’序列
     */
    @ApiModelProperty("排序列")
    private String orderByColumn;
    /**
     * æŽ’序的方向desc或者asc
     */
    @ApiModelProperty(value = "排序的方向", example = "asc,desc")
    private String isAsc;
}
ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/MetaVo.java
@@ -1,6 +1,6 @@
package com.ruoyi.system.domain.vo;
import cn.hutool.core.lang.Validator;
import com.ruoyi.common.utils.StringUtils;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
@@ -56,7 +56,7 @@
        this.title = title;
        this.icon = icon;
        this.noCache = noCache;
        if (Validator.isUrl(link)) {
        if (StringUtils.ishttp(link)) {
            this.link = link;
        }
    }
ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/SysOssConfigVo.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,96 @@
package com.ruoyi.system.domain.vo;
import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
 * äº‘存储配置视图对象 sys_oss_config
 *
 * @author Lion Li
 * @author å­¤èˆŸçƒŸé›¨
 * @date 2021-08-13
 */
@Data
@ApiModel("云存储配置视图对象")
@ExcelIgnoreUnannotated
public class SysOssConfigVo {
    private static final long serialVersionUID = 1L;
    /**
     * ä¸»å»º
     */
    @ApiModelProperty("主建")
    private Long ossConfigId;
    /**
     * é…ç½®key
     */
    @ApiModelProperty("配置key")
    private String configKey;
    /**
     * accessKey
     */
    @ApiModelProperty("accessKey")
    private String accessKey;
    /**
     * ç§˜é’¥
     */
    @ApiModelProperty("secretKey")
    private String secretKey;
    /**
     * æ¡¶åç§°
     */
    @ApiModelProperty("桶名称")
    private String bucketName;
    /**
     * å‰ç¼€
     */
    @ApiModelProperty("前缀")
    private String prefix;
    /**
     * è®¿é—®ç«™ç‚¹
     */
    @ApiModelProperty("访问站点")
    private String endpoint;
    /**
     * æ˜¯å¦https(Y=是,N=否)
     */
    @ApiModelProperty("是否https(Y=是,N=否)")
    private String isHttps;
    /**
     * åŸŸ
     */
    @ApiModelProperty("域")
    private String region;
    /**
     * çŠ¶æ€ï¼ˆ0=正常,1=停用)
     */
    @ApiModelProperty("状态(0=正常,1=停用)")
    private String status;
    /**
     * æ‰©å±•字段
     */
    @ApiModelProperty("扩展字段")
    private String ext1;
    /**
     * å¤‡æ³¨
     */
    @ApiModelProperty("备注")
    private String remark;
}
ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/SysOssVo.java
ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/SysUserExportVo.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,93 @@
package com.ruoyi.system.domain.vo;
import com.alibaba.excel.annotation.ExcelProperty;
import com.ruoyi.common.annotation.ExcelDictFormat;
import com.ruoyi.common.convert.ExcelDictConvert;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
import java.io.Serializable;
import java.util.Date;
/**
 * ç”¨æˆ·å¯¹è±¡å¯¼å‡ºVO
 *
 * @author Lion Li
 */
@Data
@NoArgsConstructor
@Accessors(chain = true)
public class SysUserExportVo implements Serializable {
    private static final long serialVersionUID = 1L;
    /**
     * ç”¨æˆ·ID
     */
    @ExcelProperty(value = "用户序号")
    private Long userId;
    /**
     * ç”¨æˆ·è´¦å·
     */
    @ExcelProperty(value = "登录名称")
    private String userName;
    /**
     * ç”¨æˆ·æ˜µç§°
     */
    @ExcelProperty(value = "用户名称")
    private String nickName;
    /**
     * ç”¨æˆ·é‚®ç®±
     */
    @ExcelProperty(value = "用户邮箱")
    private String email;
    /**
     * æ‰‹æœºå·ç 
     */
    @ExcelProperty(value = "手机号码")
    private String phonenumber;
    /**
     * ç”¨æˆ·æ€§åˆ«
     */
    @ExcelProperty(value = "用户性别", converter = ExcelDictConvert.class)
    @ExcelDictFormat(dictType = "sys_user_sex")
    private String sex;
    /**
     * å¸å·çŠ¶æ€ï¼ˆ0正常 1停用)
     */
    @ExcelProperty(value = "帐号状态", converter = ExcelDictConvert.class)
    @ExcelDictFormat(dictType = "sys_common_status")
    private String status;
    /**
     * æœ€åŽç™»å½•IP
     */
    @ExcelProperty(value = "最后登录IP")
    private String loginIp;
    /**
     * æœ€åŽç™»å½•æ—¶é—´
     */
    @ExcelProperty(value = "最后登录时间")
    private Date loginDate;
    /**
     * éƒ¨é—¨åç§°
     */
    @ExcelProperty(value = "部门名称")
    private String deptName;
    /**
     * è´Ÿè´£äºº
     */
    @ExcelProperty(value = "部门负责人")
    private String leader;
}
ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/SysUserImportVo.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,73 @@
package com.ruoyi.system.domain.vo;
import com.alibaba.excel.annotation.ExcelProperty;
import com.ruoyi.common.annotation.ExcelDictFormat;
import com.ruoyi.common.convert.ExcelDictConvert;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
/**
 * ç”¨æˆ·å¯¹è±¡å¯¼å…¥VO
 *
 * @author Lion Li
 */
@Data
@NoArgsConstructor
// @Accessors(chain = true) // å¯¼å…¥ä¸å…è®¸ä½¿ç”¨ ä¼šæ‰¾ä¸åˆ°set方法
public class SysUserImportVo implements Serializable {
    private static final long serialVersionUID = 1L;
    /**
     * ç”¨æˆ·ID
     */
    @ExcelProperty(value = "用户序号")
    private Long userId;
    /**
     * éƒ¨é—¨ID
     */
    @ExcelProperty(value = "部门编号")
    private Long deptId;
    /**
     * ç”¨æˆ·è´¦å·
     */
    @ExcelProperty(value = "登录名称")
    private String userName;
    /**
     * ç”¨æˆ·æ˜µç§°
     */
    @ExcelProperty(value = "用户名称")
    private String nickName;
    /**
     * ç”¨æˆ·é‚®ç®±
     */
    @ExcelProperty(value = "用户邮箱")
    private String email;
    /**
     * æ‰‹æœºå·ç 
     */
    @ExcelProperty(value = "手机号码")
    private String phonenumber;
    /**
     * ç”¨æˆ·æ€§åˆ«
     */
    @ExcelProperty(value = "用户性别", converter = ExcelDictConvert.class)
    @ExcelDictFormat(dictType = "sys_user_sex")
    private String sex;
    /**
     * å¸å·çŠ¶æ€ï¼ˆ0正常 1停用)
     */
    @ExcelProperty(value = "帐号状态", converter = ExcelDictConvert.class)
    @ExcelDictFormat(dictType = "sys_common_status")
    private String status;
}
ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysOssConfigMapper.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,15 @@
package com.ruoyi.system.mapper;
import com.ruoyi.common.core.mybatisplus.core.BaseMapperPlus;
import com.ruoyi.system.domain.SysOssConfig;
/**
 * äº‘存储配置Mapper接口
 *
 * @author Lion Li
 * @author å­¤èˆŸçƒŸé›¨
 * @date 2021-08-13
 */
public interface SysOssConfigMapper extends BaseMapperPlus<SysOssConfig> {
}
ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysOssMapper.java
ruoyi-system/src/main/java/com/ruoyi/system/service/ISysOssConfigService.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,58 @@
package com.ruoyi.system.service;
import com.ruoyi.common.core.mybatisplus.core.IServicePlus;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.system.domain.SysOssConfig;
import com.ruoyi.system.domain.bo.SysOssConfigBo;
import com.ruoyi.system.domain.vo.SysOssConfigVo;
import java.util.Collection;
/**
 * äº‘存储配置Service接口
 *
 * @author Lion Li
 * @author å­¤èˆŸçƒŸé›¨
 * @date 2021-08-13
 */
public interface ISysOssConfigService extends IServicePlus<SysOssConfig, SysOssConfigVo> {
    /**
     * æŸ¥è¯¢å•个
     */
    SysOssConfigVo queryById(Integer ossConfigId);
    /**
     * æŸ¥è¯¢åˆ—表
     */
    TableDataInfo<SysOssConfigVo> queryPageList(SysOssConfigBo bo);
    /**
     * æ ¹æ®æ–°å¢žä¸šåŠ¡å¯¹è±¡æ’å…¥äº‘å­˜å‚¨é…ç½®
     * @param bo äº‘存储配置新增业务对象
     * @return
     */
    Boolean insertByBo(SysOssConfigBo bo);
    /**
     * æ ¹æ®ç¼–辑业务对象修改云存储配置
     * @param bo äº‘存储配置编辑业务对象
     * @return
     */
    Boolean updateByBo(SysOssConfigBo bo);
    /**
     * æ ¡éªŒå¹¶åˆ é™¤æ•°æ®
     * @param ids ä¸»é”®é›†åˆ
     * @param isValid æ˜¯å¦æ ¡éªŒ,true-删除前校验,false-不校验
     * @return
     */
    Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);
    /**
     * å¯ç”¨åœç”¨çŠ¶æ€
     */
    int updateOssConfigStatus(SysOssConfigBo bo);
}
ruoyi-system/src/main/java/com/ruoyi/system/service/ISysOssService.java
ÎļþÃû´Ó ruoyi-oss/src/main/java/com/ruoyi/system/service/ISysOssService.java ÐÞ¸Ä
@@ -2,8 +2,8 @@
import com.ruoyi.common.core.mybatisplus.core.IServicePlus;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.system.domain.bo.SysOssBo;
import com.ruoyi.system.domain.SysOss;
import com.ruoyi.system.domain.bo.SysOssBo;
import com.ruoyi.system.domain.vo.SysOssVo;
import org.springframework.web.multipart.MultipartFile;
ruoyi-system/src/main/java/com/ruoyi/system/service/ISysUserService.java
@@ -112,6 +112,14 @@
    public int insertUser(SysUser user);
    /**
     * æ³¨å†Œç”¨æˆ·ä¿¡æ¯
     *
     * @param user ç”¨æˆ·ä¿¡æ¯
     * @return ç»“æžœ
     */
    public boolean registerUser(SysUser user);
    /**
     * ä¿®æ”¹ç”¨æˆ·ä¿¡æ¯
     *
     * @param user ç”¨æˆ·ä¿¡æ¯
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysConfigServiceImpl.java
@@ -1,8 +1,7 @@
package com.ruoyi.system.service.impl;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.lang.Validator;
import cn.hutool.core.util.StrUtil;
import com.ruoyi.common.utils.StringUtils;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.ruoyi.common.annotation.DataSource;
import com.ruoyi.common.constant.Constants;
@@ -11,7 +10,7 @@
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.common.enums.DataSourceType;
import com.ruoyi.common.exception.CustomException;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.PageUtils;
import com.ruoyi.system.domain.SysConfig;
import com.ruoyi.system.mapper.SysConfigMapper;
@@ -48,13 +47,13 @@
    public TableDataInfo<SysConfig> selectPageConfigList(SysConfig config) {
        Map<String, Object> params = config.getParams();
        LambdaQueryWrapper<SysConfig> lqw = new LambdaQueryWrapper<SysConfig>()
            .like(StrUtil.isNotBlank(config.getConfigName()), SysConfig::getConfigName, config.getConfigName())
            .eq(StrUtil.isNotBlank(config.getConfigType()), SysConfig::getConfigType, config.getConfigType())
            .like(StrUtil.isNotBlank(config.getConfigKey()), SysConfig::getConfigKey, config.getConfigKey())
            .apply(Validator.isNotEmpty(params.get("beginTime")),
            .like(StringUtils.isNotBlank(config.getConfigName()), SysConfig::getConfigName, config.getConfigName())
            .eq(StringUtils.isNotBlank(config.getConfigType()), SysConfig::getConfigType, config.getConfigType())
            .like(StringUtils.isNotBlank(config.getConfigKey()), SysConfig::getConfigKey, config.getConfigKey())
            .apply(StringUtils.isNotEmpty(params.get("beginTime")),
                "date_format(create_time,'%y%m%d') >= date_format({0},'%y%m%d')",
                params.get("beginTime"))
            .apply(Validator.isNotEmpty(params.get("endTime")),
            .apply(StringUtils.isNotEmpty(params.get("endTime")),
                "date_format(create_time,'%y%m%d') <= date_format({0},'%y%m%d')",
                params.get("endTime"));
        return PageUtils.buildDataInfo(page(PageUtils.buildPage(), lqw));
@@ -81,16 +80,16 @@
    @Override
    public String selectConfigByKey(String configKey) {
        String configValue = Convert.toStr(redisCache.getCacheObject(getCacheKey(configKey)));
        if (Validator.isNotEmpty(configValue)) {
        if (StringUtils.isNotEmpty(configValue)) {
            return configValue;
        }
        SysConfig retConfig = baseMapper.selectOne(new LambdaQueryWrapper<SysConfig>()
            .eq(SysConfig::getConfigKey, configKey));
        if (Validator.isNotNull(retConfig)) {
        if (StringUtils.isNotNull(retConfig)) {
            redisCache.setCacheObject(getCacheKey(configKey), retConfig.getConfigValue());
            return retConfig.getConfigValue();
        }
        return StrUtil.EMPTY;
        return StringUtils.EMPTY;
    }
    /**
@@ -101,7 +100,7 @@
    @Override
    public boolean selectCaptchaOnOff() {
        String captchaOnOff = selectConfigByKey("sys.account.captchaOnOff");
        if (StrUtil.isEmpty(captchaOnOff)) {
        if (StringUtils.isEmpty(captchaOnOff)) {
            return true;
        }
        return Convert.toBool(captchaOnOff);
@@ -117,13 +116,13 @@
    public List<SysConfig> selectConfigList(SysConfig config) {
        Map<String, Object> params = config.getParams();
        LambdaQueryWrapper<SysConfig> lqw = new LambdaQueryWrapper<SysConfig>()
            .like(StrUtil.isNotBlank(config.getConfigName()), SysConfig::getConfigName, config.getConfigName())
            .eq(StrUtil.isNotBlank(config.getConfigType()), SysConfig::getConfigType, config.getConfigType())
            .like(StrUtil.isNotBlank(config.getConfigKey()), SysConfig::getConfigKey, config.getConfigKey())
            .apply(Validator.isNotEmpty(params.get("beginTime")),
            .like(StringUtils.isNotBlank(config.getConfigName()), SysConfig::getConfigName, config.getConfigName())
            .eq(StringUtils.isNotBlank(config.getConfigType()), SysConfig::getConfigType, config.getConfigType())
            .like(StringUtils.isNotBlank(config.getConfigKey()), SysConfig::getConfigKey, config.getConfigKey())
            .apply(StringUtils.isNotEmpty(params.get("beginTime")),
                "date_format(create_time,'%y%m%d') >= date_format({0},'%y%m%d')",
                params.get("beginTime"))
            .apply(Validator.isNotEmpty(params.get("endTime")),
            .apply(StringUtils.isNotEmpty(params.get("endTime")),
                "date_format(create_time,'%y%m%d') <= date_format({0},'%y%m%d')",
                params.get("endTime"));
        return baseMapper.selectList(lqw);
@@ -169,8 +168,8 @@
    public void deleteConfigByIds(Long[] configIds) {
        for (Long configId : configIds) {
            SysConfig config = selectConfigById(configId);
            if (StrUtil.equals(UserConstants.YES, config.getConfigType())) {
                throw new CustomException(String.format("内置参数【%1$s】不能删除 ", config.getConfigKey()));
            if (StringUtils.equals(UserConstants.YES, config.getConfigType())) {
                throw new ServiceException(String.format("内置参数【%1$s】不能删除 ", config.getConfigKey()));
            }
            redisCache.deleteObject(getCacheKey(config.getConfigKey()));
        }
@@ -214,9 +213,9 @@
     */
    @Override
    public String checkConfigKeyUnique(SysConfig config) {
        Long configId = Validator.isNull(config.getConfigId()) ? -1L : config.getConfigId();
        Long configId = StringUtils.isNull(config.getConfigId()) ? -1L : config.getConfigId();
        SysConfig info = baseMapper.selectOne(new LambdaQueryWrapper<SysConfig>().eq(SysConfig::getConfigKey, config.getConfigKey()));
        if (Validator.isNotNull(info) && info.getConfigId().longValue() != configId.longValue()) {
        if (StringUtils.isNotNull(info) && info.getConfigId().longValue() != configId.longValue()) {
            return UserConstants.NOT_UNIQUE;
        }
        return UserConstants.UNIQUE;
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysDeptServiceImpl.java
@@ -1,7 +1,6 @@
package com.ruoyi.system.service.impl;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.lang.Validator;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.ruoyi.common.annotation.DataScope;
@@ -11,7 +10,8 @@
import com.ruoyi.common.core.domain.entity.SysRole;
import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.core.mybatisplus.core.ServicePlusImpl;
import com.ruoyi.common.exception.CustomException;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.system.mapper.SysDeptMapper;
import com.ruoyi.system.mapper.SysRoleMapper;
import com.ruoyi.system.mapper.SysUserMapper;
@@ -159,12 +159,12 @@
     */
    @Override
    public String checkDeptNameUnique(SysDept dept) {
        Long deptId = Validator.isNull(dept.getDeptId()) ? -1L : dept.getDeptId();
        Long deptId = StringUtils.isNull(dept.getDeptId()) ? -1L : dept.getDeptId();
        SysDept info = getOne(new LambdaQueryWrapper<SysDept>()
                .eq(SysDept::getDeptName, dept.getDeptName())
                .eq(SysDept::getParentId, dept.getParentId())
                .last("limit 1"));
        if (Validator.isNotNull(info) && info.getDeptId().longValue() != deptId.longValue()) {
        if (StringUtils.isNotNull(info) && info.getDeptId().longValue() != deptId.longValue()) {
            return UserConstants.NOT_UNIQUE;
        }
        return UserConstants.UNIQUE;
@@ -181,7 +181,7 @@
        SysDept info = getById(dept.getParentId());
        // å¦‚果父节点不为正常状态,则不允许新增子节点
        if (!UserConstants.DEPT_NORMAL.equals(info.getStatus())) {
            throw new CustomException("部门停用,不允许新增");
            throw new ServiceException("部门停用,不允许新增");
        }
        dept.setAncestors(info.getAncestors() + "," + dept.getParentId());
        return baseMapper.insert(dept);
@@ -197,14 +197,15 @@
    public int updateDept(SysDept dept) {
        SysDept newParentDept = getById(dept.getParentId());
        SysDept oldDept = getById(dept.getDeptId());
        if (Validator.isNotNull(newParentDept) && Validator.isNotNull(oldDept)) {
        if (StringUtils.isNotNull(newParentDept) && StringUtils.isNotNull(oldDept)) {
            String newAncestors = newParentDept.getAncestors() + "," + newParentDept.getDeptId();
            String oldAncestors = oldDept.getAncestors();
            dept.setAncestors(newAncestors);
            updateDeptChildren(dept.getDeptId(), newAncestors, oldAncestors);
        }
        int result = baseMapper.updateById(dept);
        if (UserConstants.DEPT_NORMAL.equals(dept.getStatus())) {
        if (UserConstants.DEPT_NORMAL.equals(dept.getStatus()) && StringUtils.isNotEmpty(dept.getAncestors())
            && !StringUtils.equals("0", dept.getAncestors())) {
            // å¦‚果该部门是启用状态,则启用该部门的所有上级部门
            updateParentDeptStatusNormal(dept);
        }
@@ -273,7 +274,7 @@
    private List<SysDept> getChildList(List<SysDept> list, SysDept t) {
        List<SysDept> tlist = new ArrayList<SysDept>();
        for (SysDept n : list) {
            if (Validator.isNotNull(n.getParentId()) && n.getParentId().longValue() == t.getDeptId().longValue()) {
            if (StringUtils.isNotNull(n.getParentId()) && n.getParentId().longValue() == t.getDeptId().longValue()) {
                tlist.add(n);
            }
        }
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysDictDataServiceImpl.java
@@ -1,6 +1,6 @@
package com.ruoyi.system.service.impl;
import cn.hutool.core.util.StrUtil;
import com.ruoyi.common.utils.StringUtils;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.ruoyi.common.core.domain.entity.SysDictData;
import com.ruoyi.common.core.mybatisplus.core.ServicePlusImpl;
@@ -24,9 +24,9 @@
    @Override
    public TableDataInfo<SysDictData> selectPageDictDataList(SysDictData dictData) {
        LambdaQueryWrapper<SysDictData> lqw = new LambdaQueryWrapper<SysDictData>()
            .eq(StrUtil.isNotBlank(dictData.getDictType()), SysDictData::getDictType, dictData.getDictType())
            .like(StrUtil.isNotBlank(dictData.getDictLabel()), SysDictData::getDictLabel, dictData.getDictLabel())
            .eq(StrUtil.isNotBlank(dictData.getStatus()), SysDictData::getStatus, dictData.getStatus())
            .eq(StringUtils.isNotBlank(dictData.getDictType()), SysDictData::getDictType, dictData.getDictType())
            .like(StringUtils.isNotBlank(dictData.getDictLabel()), SysDictData::getDictLabel, dictData.getDictLabel())
            .eq(StringUtils.isNotBlank(dictData.getStatus()), SysDictData::getStatus, dictData.getStatus())
            .orderByAsc(SysDictData::getDictSort);
        return PageUtils.buildDataInfo(page(PageUtils.buildPage(), lqw));
    }
@@ -40,9 +40,9 @@
    @Override
    public List<SysDictData> selectDictDataList(SysDictData dictData) {
        return list(new LambdaQueryWrapper<SysDictData>()
            .eq(StrUtil.isNotBlank(dictData.getDictType()), SysDictData::getDictType, dictData.getDictType())
            .like(StrUtil.isNotBlank(dictData.getDictLabel()), SysDictData::getDictLabel, dictData.getDictLabel())
            .eq(StrUtil.isNotBlank(dictData.getStatus()), SysDictData::getStatus, dictData.getStatus())
            .eq(StringUtils.isNotBlank(dictData.getDictType()), SysDictData::getDictType, dictData.getDictType())
            .like(StringUtils.isNotBlank(dictData.getDictLabel()), SysDictData::getDictLabel, dictData.getDictLabel())
            .eq(StringUtils.isNotBlank(dictData.getStatus()), SysDictData::getStatus, dictData.getStatus())
            .orderByAsc(SysDictData::getDictSort));
    }
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysDictTypeServiceImpl.java
@@ -1,8 +1,7 @@
package com.ruoyi.system.service.impl;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Validator;
import cn.hutool.core.util.StrUtil;
import com.ruoyi.common.utils.StringUtils;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.ruoyi.common.constant.UserConstants;
@@ -10,7 +9,7 @@
import com.ruoyi.common.core.domain.entity.SysDictType;
import com.ruoyi.common.core.mybatisplus.core.ServicePlusImpl;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.exception.CustomException;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.DictUtils;
import com.ruoyi.common.utils.PageUtils;
import com.ruoyi.system.mapper.SysDictDataMapper;
@@ -48,13 +47,13 @@
    public TableDataInfo<SysDictType> selectPageDictTypeList(SysDictType dictType) {
        Map<String, Object> params = dictType.getParams();
        LambdaQueryWrapper<SysDictType> lqw = new LambdaQueryWrapper<SysDictType>()
            .like(StrUtil.isNotBlank(dictType.getDictName()), SysDictType::getDictName, dictType.getDictName())
            .eq(StrUtil.isNotBlank(dictType.getStatus()), SysDictType::getStatus, dictType.getStatus())
            .like(StrUtil.isNotBlank(dictType.getDictType()), SysDictType::getDictType, dictType.getDictType())
            .apply(Validator.isNotEmpty(params.get("beginTime")),
            .like(StringUtils.isNotBlank(dictType.getDictName()), SysDictType::getDictName, dictType.getDictName())
            .eq(StringUtils.isNotBlank(dictType.getStatus()), SysDictType::getStatus, dictType.getStatus())
            .like(StringUtils.isNotBlank(dictType.getDictType()), SysDictType::getDictType, dictType.getDictType())
            .apply(StringUtils.isNotEmpty(params.get("beginTime")),
                "date_format(create_time,'%y%m%d') >= date_format({0},'%y%m%d')",
                params.get("beginTime"))
            .apply(Validator.isNotEmpty(params.get("endTime")),
            .apply(StringUtils.isNotEmpty(params.get("endTime")),
                "date_format(create_time,'%y%m%d') <= date_format({0},'%y%m%d')",
                params.get("endTime"));
        return PageUtils.buildDataInfo(page(PageUtils.buildPage(), lqw));
@@ -70,13 +69,13 @@
    public List<SysDictType> selectDictTypeList(SysDictType dictType) {
        Map<String, Object> params = dictType.getParams();
        return list(new LambdaQueryWrapper<SysDictType>()
            .like(StrUtil.isNotBlank(dictType.getDictName()), SysDictType::getDictName, dictType.getDictName())
            .eq(StrUtil.isNotBlank(dictType.getStatus()), SysDictType::getStatus, dictType.getStatus())
            .like(StrUtil.isNotBlank(dictType.getDictType()), SysDictType::getDictType, dictType.getDictType())
            .apply(Validator.isNotEmpty(params.get("beginTime")),
            .like(StringUtils.isNotBlank(dictType.getDictName()), SysDictType::getDictName, dictType.getDictName())
            .eq(StringUtils.isNotBlank(dictType.getStatus()), SysDictType::getStatus, dictType.getStatus())
            .like(StringUtils.isNotBlank(dictType.getDictType()), SysDictType::getDictType, dictType.getDictType())
            .apply(StringUtils.isNotEmpty(params.get("beginTime")),
                "date_format(create_time,'%y%m%d') >= date_format({0},'%y%m%d')",
                params.get("beginTime"))
            .apply(Validator.isNotEmpty(params.get("endTime")),
            .apply(StringUtils.isNotEmpty(params.get("endTime")),
                "date_format(create_time,'%y%m%d') <= date_format({0},'%y%m%d')",
                params.get("endTime")));
    }
@@ -145,7 +144,7 @@
            SysDictType dictType = selectDictTypeById(dictId);
            if (dictDataMapper.selectCount(new LambdaQueryWrapper<SysDictData>()
                .eq(SysDictData::getDictType, dictType.getDictType())) > 0) {
                throw new CustomException(String.format("%1$s已分配,不能删除", dictType.getDictName()));
                throw new ServiceException(String.format("%1$s已分配,不能删除", dictType.getDictName()));
            }
            DictUtils.removeDictCache(dictType.getDictType());
        }
@@ -225,11 +224,11 @@
     */
    @Override
    public String checkDictTypeUnique(SysDictType dict) {
        Long dictId = Validator.isNull(dict.getDictId()) ? -1L : dict.getDictId();
        Long dictId = StringUtils.isNull(dict.getDictId()) ? -1L : dict.getDictId();
        SysDictType dictType = getOne(new LambdaQueryWrapper<SysDictType>()
            .eq(SysDictType::getDictType, dict.getDictType())
            .last("limit 1"));
        if (Validator.isNotNull(dictType) && dictType.getDictId().longValue() != dictId.longValue()) {
        if (StringUtils.isNotNull(dictType) && dictType.getDictId().longValue() != dictId.longValue()) {
            return UserConstants.NOT_UNIQUE;
        }
        return UserConstants.UNIQUE;
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysLogininforServiceImpl.java
@@ -1,11 +1,10 @@
package com.ruoyi.system.service.impl;
import cn.hutool.core.lang.Validator;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.ruoyi.common.core.mybatisplus.core.ServicePlusImpl;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.utils.PageUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.system.domain.SysLogininfor;
import com.ruoyi.system.mapper.SysLogininforMapper;
import com.ruoyi.system.service.ISysLogininforService;
@@ -28,13 +27,13 @@
    public TableDataInfo<SysLogininfor> selectPageLogininforList(SysLogininfor logininfor) {
        Map<String, Object> params = logininfor.getParams();
        LambdaQueryWrapper<SysLogininfor> lqw = new LambdaQueryWrapper<SysLogininfor>()
                .like(StrUtil.isNotBlank(logininfor.getIpaddr()), SysLogininfor::getIpaddr, logininfor.getIpaddr())
                .eq(StrUtil.isNotBlank(logininfor.getStatus()), SysLogininfor::getStatus, logininfor.getStatus())
                .like(StrUtil.isNotBlank(logininfor.getUserName()), SysLogininfor::getUserName, logininfor.getUserName())
                .apply(Validator.isNotEmpty(params.get("beginTime")),
                .like(StringUtils.isNotBlank(logininfor.getIpaddr()), SysLogininfor::getIpaddr, logininfor.getIpaddr())
                .eq(StringUtils.isNotBlank(logininfor.getStatus()), SysLogininfor::getStatus, logininfor.getStatus())
                .like(StringUtils.isNotBlank(logininfor.getUserName()), SysLogininfor::getUserName, logininfor.getUserName())
                .apply(StringUtils.isNotEmpty(params.get("beginTime")),
                        "date_format(login_time,'%y%m%d') >= date_format({0},'%y%m%d')",
                        params.get("beginTime"))
                .apply(Validator.isNotEmpty(params.get("endTime")),
                .apply(StringUtils.isNotEmpty(params.get("endTime")),
                        "date_format(login_time,'%y%m%d') <= date_format({0},'%y%m%d')",
                        params.get("endTime"));
        return PageUtils.buildDataInfo(page(PageUtils.buildPage("info_id","desc"), lqw));
@@ -61,13 +60,13 @@
    public List<SysLogininfor> selectLogininforList(SysLogininfor logininfor) {
        Map<String, Object> params = logininfor.getParams();
        return list(new LambdaQueryWrapper<SysLogininfor>()
                .like(StrUtil.isNotBlank(logininfor.getIpaddr()),SysLogininfor::getIpaddr,logininfor.getIpaddr())
                .eq(StrUtil.isNotBlank(logininfor.getStatus()),SysLogininfor::getStatus,logininfor.getStatus())
                .like(StrUtil.isNotBlank(logininfor.getUserName()),SysLogininfor::getUserName,logininfor.getUserName())
                .apply(Validator.isNotEmpty(params.get("beginTime")),
                .like(StringUtils.isNotBlank(logininfor.getIpaddr()),SysLogininfor::getIpaddr,logininfor.getIpaddr())
                .eq(StringUtils.isNotBlank(logininfor.getStatus()),SysLogininfor::getStatus,logininfor.getStatus())
                .like(StringUtils.isNotBlank(logininfor.getUserName()),SysLogininfor::getUserName,logininfor.getUserName())
                .apply(StringUtils.isNotEmpty(params.get("beginTime")),
                        "date_format(login_time,'%y%m%d') >= date_format({0},'%y%m%d')",
                        params.get("beginTime"))
                .apply(Validator.isNotEmpty(params.get("endTime")),
                .apply(StringUtils.isNotEmpty(params.get("endTime")),
                        "date_format(login_time,'%y%m%d') <= date_format({0},'%y%m%d')",
                        params.get("endTime"))
                .orderByDesc(SysLogininfor::getInfoId));
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysMenuServiceImpl.java
@@ -1,7 +1,5 @@
package com.ruoyi.system.service.impl;
import cn.hutool.core.lang.Validator;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.constant.UserConstants;
@@ -11,6 +9,7 @@
import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.core.mybatisplus.core.ServicePlusImpl;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.system.domain.SysRoleMenu;
import com.ruoyi.system.domain.vo.MetaVo;
import com.ruoyi.system.domain.vo.RouterVo;
@@ -18,7 +17,6 @@
import com.ruoyi.system.mapper.SysRoleMapper;
import com.ruoyi.system.mapper.SysRoleMenuMapper;
import com.ruoyi.system.service.ISysMenuService;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@@ -63,9 +61,9 @@
        // ç®¡ç†å‘˜æ˜¾ç¤ºæ‰€æœ‰èœå•信息
        if (SysUser.isAdmin(userId)) {
            menuList = list(new LambdaQueryWrapper<SysMenu>()
                    .like(StrUtil.isNotBlank(menu.getMenuName()),SysMenu::getMenuName,menu.getMenuName())
                    .eq(StrUtil.isNotBlank(menu.getVisible()),SysMenu::getVisible,menu.getVisible())
                    .eq(StrUtil.isNotBlank(menu.getStatus()),SysMenu::getStatus,menu.getStatus())
                    .like(StringUtils.isNotBlank(menu.getMenuName()),SysMenu::getMenuName,menu.getMenuName())
                    .eq(StringUtils.isNotBlank(menu.getVisible()),SysMenu::getVisible,menu.getVisible())
                    .eq(StringUtils.isNotBlank(menu.getStatus()),SysMenu::getStatus,menu.getStatus())
                    .orderByAsc(SysMenu::getParentId)
                    .orderByAsc(SysMenu::getOrderNum));
        } else {
@@ -86,7 +84,7 @@
        List<String> perms = baseMapper.selectMenuPermsByUserId(userId);
        Set<String> permsSet = new HashSet<>();
        for (String perm : perms) {
            if (Validator.isNotEmpty(perm)) {
            if (StringUtils.isNotEmpty(perm)) {
                permsSet.addAll(Arrays.asList(perm.trim().split(",")));
            }
        }
@@ -137,7 +135,7 @@
            router.setName(getRouteName(menu));
            router.setPath(getRouterPath(menu));
            router.setComponent(getComponent(menu));
            router.setMeta(new MetaVo(menu.getMenuName(), menu.getIcon(), StrUtil.equals("1", menu.getIsCache()), menu.getPath()));
            router.setMeta(new MetaVo(menu.getMenuName(), menu.getIcon(), StringUtils.equals("1", menu.getIsCache()), menu.getPath()));
            List<SysMenu> cMenus = menu.getChildren();
            if (!cMenus.isEmpty() && UserConstants.TYPE_DIR.equals(menu.getMenuType())) {
                router.setAlwaysShow(true);
@@ -149,8 +147,8 @@
                RouterVo children = new RouterVo();
                children.setPath(menu.getPath());
                children.setComponent(menu.getComponent());
                children.setName(StrUtil.upperFirst(menu.getPath()));
                children.setMeta(new MetaVo(menu.getMenuName(), menu.getIcon(), StrUtil.equals("1", menu.getIsCache()), menu.getPath()));
                children.setName(StringUtils.capitalize(menu.getPath()));
                children.setMeta(new MetaVo(menu.getMenuName(), menu.getIcon(), StringUtils.equals("1", menu.getIsCache()), menu.getPath()));
                childrenList.add(children);
                router.setChildren(childrenList);
            } else if (menu.getParentId().intValue() == 0 && isInnerLink(menu)) {
@@ -161,7 +159,7 @@
                String routerPath = StringUtils.replaceEach(menu.getPath(), new String[] { Constants.HTTP, Constants.HTTPS }, new String[] { "", "" });
                children.setPath(routerPath);
                children.setComponent(UserConstants.INNER_LINK);
                children.setName(StrUtil.upperFirst(routerPath));
                children.setName(StringUtils.capitalize(routerPath));
                children.setMeta(new MetaVo(menu.getMenuName(), menu.getIcon(), menu.getPath()));
                childrenList.add(children);
                router.setChildren(childrenList);
@@ -285,12 +283,12 @@
     */
    @Override
    public String checkMenuNameUnique(SysMenu menu) {
        Long menuId = Validator.isNull(menu.getMenuId()) ? -1L : menu.getMenuId();
        Long menuId = StringUtils.isNull(menu.getMenuId()) ? -1L : menu.getMenuId();
        SysMenu info = getOne(new LambdaQueryWrapper<SysMenu>()
                .eq(SysMenu::getMenuName,menu.getMenuName())
                .eq(SysMenu::getParentId,menu.getParentId())
                .last("limit 1"));
        if (Validator.isNotNull(info) && info.getMenuId().longValue() != menuId.longValue()) {
        if (StringUtils.isNotNull(info) && info.getMenuId().longValue() != menuId.longValue()) {
            return UserConstants.NOT_UNIQUE;
        }
        return UserConstants.UNIQUE;
@@ -303,10 +301,10 @@
     * @return è·¯ç”±åç§°
     */
    public String getRouteName(SysMenu menu) {
        String routerName = StrUtil.upperFirst(menu.getPath());
        String routerName = StringUtils.capitalize(menu.getPath());
        // éžå¤–链并且是一级目录(类型为目录)
        if (isMenuFrame(menu)) {
            routerName = StrUtil.EMPTY;
            routerName = StringUtils.EMPTY;
        }
        return routerName;
    }
@@ -343,11 +341,11 @@
     */
    public String getComponent(SysMenu menu) {
        String component = UserConstants.LAYOUT;
        if (StrUtil.isNotEmpty(menu.getComponent()) && !isMenuFrame(menu)) {
        if (StringUtils.isNotEmpty(menu.getComponent()) && !isMenuFrame(menu)) {
            component = menu.getComponent();
        } else if (StrUtil.isEmpty(menu.getComponent()) && menu.getParentId().intValue() != 0 && isInnerLink(menu)) {
        } else if (StringUtils.isEmpty(menu.getComponent()) && menu.getParentId().intValue() != 0 && isInnerLink(menu)) {
            component = UserConstants.INNER_LINK;
        } else if (StrUtil.isEmpty(menu.getComponent()) && isParentView(menu)) {
        } else if (StringUtils.isEmpty(menu.getComponent()) && isParentView(menu)) {
            component = UserConstants.PARENT_VIEW;
        }
        return component;
@@ -371,7 +369,7 @@
     * @return ç»“æžœ
     */
    public boolean isInnerLink(SysMenu menu) {
        return menu.getIsFrame().equals(UserConstants.NO_FRAME) && Validator.isUrl(menu.getPath());
        return menu.getIsFrame().equals(UserConstants.NO_FRAME) && StringUtils.ishttp(menu.getPath());
    }
    /**
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysNoticeServiceImpl.java
@@ -1,6 +1,6 @@
package com.ruoyi.system.service.impl;
import cn.hutool.core.util.StrUtil;
import com.ruoyi.common.utils.StringUtils;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.ruoyi.common.core.mybatisplus.core.ServicePlusImpl;
import com.ruoyi.common.core.page.TableDataInfo;
@@ -24,9 +24,9 @@
    @Override
    public TableDataInfo<SysNotice> selectPageNoticeList(SysNotice notice) {
        LambdaQueryWrapper<SysNotice> lqw = new LambdaQueryWrapper<SysNotice>()
                .like(StrUtil.isNotBlank(notice.getNoticeTitle()), SysNotice::getNoticeTitle, notice.getNoticeTitle())
                .eq(StrUtil.isNotBlank(notice.getNoticeType()), SysNotice::getNoticeType, notice.getNoticeType())
                .like(StrUtil.isNotBlank(notice.getCreateBy()), SysNotice::getCreateBy, notice.getCreateBy());
                .like(StringUtils.isNotBlank(notice.getNoticeTitle()), SysNotice::getNoticeTitle, notice.getNoticeTitle())
                .eq(StringUtils.isNotBlank(notice.getNoticeType()), SysNotice::getNoticeType, notice.getNoticeType())
                .like(StringUtils.isNotBlank(notice.getCreateBy()), SysNotice::getCreateBy, notice.getCreateBy());
        return PageUtils.buildDataInfo(page(PageUtils.buildPage(),lqw));
    }
@@ -50,9 +50,9 @@
    @Override
    public List<SysNotice> selectNoticeList(SysNotice notice) {
        return list(new LambdaQueryWrapper<SysNotice>()
                .like(StrUtil.isNotBlank(notice.getNoticeTitle()),SysNotice::getNoticeTitle,notice.getNoticeTitle())
                .eq(StrUtil.isNotBlank(notice.getNoticeType()),SysNotice::getNoticeType,notice.getNoticeType())
                .like(StrUtil.isNotBlank(notice.getCreateBy()),SysNotice::getCreateBy,notice.getCreateBy()));
                .like(StringUtils.isNotBlank(notice.getNoticeTitle()),SysNotice::getNoticeTitle,notice.getNoticeTitle())
                .eq(StringUtils.isNotBlank(notice.getNoticeType()),SysNotice::getNoticeType,notice.getNoticeType())
                .like(StringUtils.isNotBlank(notice.getCreateBy()),SysNotice::getCreateBy,notice.getCreateBy()));
    }
    /**
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysOperLogServiceImpl.java
@@ -1,8 +1,7 @@
package com.ruoyi.system.service.impl;
import cn.hutool.core.lang.Validator;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.StrUtil;
import com.ruoyi.common.utils.StringUtils;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.ruoyi.common.core.mybatisplus.core.ServicePlusImpl;
import com.ruoyi.common.core.page.TableDataInfo;
@@ -29,7 +28,7 @@
    public TableDataInfo<SysOperLog> selectPageOperLogList(SysOperLog operLog) {
        Map<String, Object> params = operLog.getParams();
        LambdaQueryWrapper<SysOperLog> lqw = new LambdaQueryWrapper<SysOperLog>()
                .like(StrUtil.isNotBlank(operLog.getTitle()), SysOperLog::getTitle, operLog.getTitle())
                .like(StringUtils.isNotBlank(operLog.getTitle()), SysOperLog::getTitle, operLog.getTitle())
                .eq(operLog.getBusinessType() != null && operLog.getBusinessType() > 0,
                        SysOperLog::getBusinessType, operLog.getBusinessType())
                .func(f -> {
@@ -37,13 +36,13 @@
                        f.in(SysOperLog::getBusinessType, Arrays.asList(operLog.getBusinessTypes()));
                    }
                })
                .eq(operLog.getStatus() != null && operLog.getStatus() > 0,
                .eq(operLog.getStatus() != null,
                        SysOperLog::getStatus, operLog.getStatus())
                .like(StrUtil.isNotBlank(operLog.getOperName()), SysOperLog::getOperName, operLog.getOperName())
                .apply(Validator.isNotEmpty(params.get("beginTime")),
                .like(StringUtils.isNotBlank(operLog.getOperName()), SysOperLog::getOperName, operLog.getOperName())
                .apply(StringUtils.isNotEmpty(params.get("beginTime")),
                        "date_format(oper_time,'%y%m%d') >= date_format({0},'%y%m%d')",
                        params.get("beginTime"))
                .apply(Validator.isNotEmpty(params.get("endTime")),
                .apply(StringUtils.isNotEmpty(params.get("endTime")),
                        "date_format(oper_time,'%y%m%d') <= date_format({0},'%y%m%d')",
                        params.get("endTime"));
        return PageUtils.buildDataInfo(page(PageUtils.buildPage("oper_id","desc"), lqw));
@@ -70,7 +69,7 @@
    public List<SysOperLog> selectOperLogList(SysOperLog operLog) {
        Map<String, Object> params = operLog.getParams();
        return list(new LambdaQueryWrapper<SysOperLog>()
                .like(StrUtil.isNotBlank(operLog.getTitle()),SysOperLog::getTitle,operLog.getTitle())
                .like(StringUtils.isNotBlank(operLog.getTitle()),SysOperLog::getTitle,operLog.getTitle())
                .eq(operLog.getBusinessType() != null && operLog.getBusinessType() > 0,
                        SysOperLog::getBusinessType,operLog.getBusinessType())
                .func(f -> {
@@ -80,11 +79,11 @@
                })
                .eq(operLog.getStatus() != null && operLog.getStatus() > 0,
                        SysOperLog::getStatus,operLog.getStatus())
                .like(StrUtil.isNotBlank(operLog.getOperName()),SysOperLog::getOperName,operLog.getOperName())
                .apply(Validator.isNotEmpty(params.get("beginTime")),
                .like(StringUtils.isNotBlank(operLog.getOperName()),SysOperLog::getOperName,operLog.getOperName())
                .apply(StringUtils.isNotEmpty(params.get("beginTime")),
                        "date_format(oper_time,'%y%m%d') >= date_format({0},'%y%m%d')",
                        params.get("beginTime"))
                .apply(Validator.isNotEmpty(params.get("endTime")),
                .apply(StringUtils.isNotEmpty(params.get("endTime")),
                        "date_format(oper_time,'%y%m%d') <= date_format({0},'%y%m%d')",
                        params.get("endTime"))
                .orderByDesc(SysOperLog::getOperId));
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysOssConfigServiceImpl.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,175 @@
package com.ruoyi.system.service.impl;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.ruoyi.common.constant.UserConstants;
import com.ruoyi.common.core.mybatisplus.core.ServicePlusImpl;
import com.ruoyi.common.core.page.PagePlus;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.JsonUtils;
import com.ruoyi.common.utils.PageUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.oss.constant.CloudConstant;
import com.ruoyi.system.domain.SysOssConfig;
import com.ruoyi.system.domain.bo.SysOssConfigBo;
import com.ruoyi.system.domain.vo.SysOssConfigVo;
import com.ruoyi.system.mapper.SysOssConfigMapper;
import com.ruoyi.system.service.ISysOssConfigService;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.PostConstruct;
import java.util.Collection;
import java.util.List;
/**
 * äº‘存储配置Service业务层处理
 *
 * @author Lion Li
 * @author å­¤èˆŸçƒŸé›¨
 * @date 2021-08-13
 */
@RequiredArgsConstructor(onConstructor_ = @Autowired)
@Service
public class SysOssConfigServiceImpl extends ServicePlusImpl<SysOssConfigMapper, SysOssConfig, SysOssConfigVo> implements ISysOssConfigService {
    private final RedisCache redisCache;
    /**
     * é¡¹ç›®å¯åŠ¨æ—¶ï¼Œåˆå§‹åŒ–å‚æ•°åˆ°ç¼“å­˜ï¼ŒåŠ è½½é…ç½®ç±»
     */
    @PostConstruct
    public void init() {
        List<SysOssConfig> list = list();
        for (SysOssConfig config : list) {
            String configKey = config.getConfigKey();
            if ("0".equals(config.getStatus())) {
                redisCache.setCacheObject(CloudConstant.CACHE_CONFIG_KEY, configKey);
            }
            setConfigCache(true, config);
        }
    }
    @Override
    public SysOssConfigVo queryById(Integer ossConfigId){
        return getVoById(ossConfigId);
    }
    @Override
    public TableDataInfo<SysOssConfigVo> queryPageList(SysOssConfigBo bo) {
        PagePlus<SysOssConfig, SysOssConfigVo> result = pageVo(PageUtils.buildPagePlus(), buildQueryWrapper(bo));
        return PageUtils.buildDataInfo(result);
    }
    private LambdaQueryWrapper<SysOssConfig> buildQueryWrapper(SysOssConfigBo bo) {
        LambdaQueryWrapper<SysOssConfig> lqw = Wrappers.lambdaQuery();
        lqw.eq(StringUtils.isNotBlank(bo.getConfigKey()), SysOssConfig::getConfigKey, bo.getConfigKey());
        lqw.like(StringUtils.isNotBlank(bo.getBucketName()), SysOssConfig::getBucketName, bo.getBucketName());
        lqw.eq(StringUtils.isNotBlank(bo.getStatus()), SysOssConfig::getStatus, bo.getStatus());
        return lqw;
    }
    @Override
    public Boolean insertByBo(SysOssConfigBo bo) {
        SysOssConfig config = BeanUtil.toBean(bo, SysOssConfig.class);
        validEntityBeforeSave(config);
        return setConfigCache(save(config), config);
    }
    @Override
    public Boolean updateByBo(SysOssConfigBo bo) {
        SysOssConfig config = BeanUtil.toBean(bo, SysOssConfig.class);
        validEntityBeforeSave(config);
        return setConfigCache(updateById(config), config);
    }
    /**
     * ä¿å­˜å‰çš„æ•°æ®æ ¡éªŒ
     */
    private void validEntityBeforeSave(SysOssConfig entity){
        if (StringUtils.isNotEmpty(entity.getConfigKey())
            && UserConstants.NOT_UNIQUE.equals(checkConfigKeyUnique(entity))) {
            throw new ServiceException("操作配置'" + entity.getConfigKey() + "'失败, é…ç½®key已存在!");
        }
    }
    @Override
    public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
        if(isValid) {
            if (CollUtil.containsAny(ids, CloudConstant.SYSTEM_DATA_IDS)) {
                throw new ServiceException("系统内置, ä¸å¯åˆ é™¤!");
            }
        }
        boolean flag = removeByIds(ids);
        if (flag) {
            for (Long configId : ids) {
                SysOssConfig config = getById(configId);
                redisCache.deleteObject(getCacheKey(config.getConfigKey()));
            }
        }
        return flag;
    }
    /**
     * åˆ¤æ–­configKey是否唯一
     */
    private String checkConfigKeyUnique(SysOssConfig sysOssConfig) {
        long ossConfigId = StringUtils.isNull(sysOssConfig.getOssConfigId()) ? -1L : sysOssConfig.getOssConfigId();
        SysOssConfig info = getOne(new LambdaQueryWrapper<SysOssConfig>()
            .select(SysOssConfig::getOssConfigId, SysOssConfig::getConfigKey)
            .eq(SysOssConfig::getConfigKey, sysOssConfig.getConfigKey()));
        if (StringUtils.isNotNull(info) && info.getOssConfigId() != ossConfigId) {
            return UserConstants.NOT_UNIQUE;
        }
        return UserConstants.UNIQUE;
    }
    /**
     * å¯ç”¨ç¦ç”¨çŠ¶æ€
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public int updateOssConfigStatus(SysOssConfigBo bo) {
        SysOssConfig sysOssConfig = BeanUtil.toBean(bo, SysOssConfig.class);
        int row = baseMapper.update(null, new LambdaUpdateWrapper<SysOssConfig>()
            .set(SysOssConfig::getStatus, "1"));
        row += baseMapper.updateById(sysOssConfig);
        if (row > 0) {
            redisCache.setCacheObject(CloudConstant.CACHE_CONFIG_KEY, sysOssConfig.getConfigKey());
        }
        return row;
    }
    /**
     * è®¾ç½®cache key
     *
     * @param configKey å‚æ•°é”®
     * @return ç¼“存键key
     */
    private String getCacheKey(String configKey) {
        return CloudConstant.SYS_OSS_KEY + configKey;
    }
    /**
     * å¦‚果操作成功 åˆ™æ›´æ–°ç¼“å­˜
     * @param flag æ“ä½œçŠ¶æ€
     * @param config é…ç½®
     * @return è¿”回操作状态
     */
    private boolean setConfigCache(boolean flag, SysOssConfig config) {
        if (flag) {
            redisCache.setCacheObject(
                getCacheKey(config.getConfigKey()),
                JsonUtils.toJsonString(config));
        }
        return flag;
    }
}
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysOssServiceImpl.java
ÎļþÃû´Ó ruoyi-oss/src/main/java/com/ruoyi/system/service/impl/SysOssServiceImpl.java ÐÞ¸Ä
@@ -1,21 +1,21 @@
package com.ruoyi.system.service.impl;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.ruoyi.common.core.mybatisplus.core.ServicePlusImpl;
import com.ruoyi.common.core.page.PagePlus;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.exception.CustomException;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.PageUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.oss.entity.UploadResult;
import com.ruoyi.oss.factory.OssFactory;
import com.ruoyi.oss.service.ICloudStorageService;
import com.ruoyi.system.domain.bo.SysOssBo;
import com.ruoyi.oss.service.ICloudStorageStrategy;
import com.ruoyi.system.domain.SysOss;
import com.ruoyi.system.domain.bo.SysOssBo;
import com.ruoyi.system.domain.vo.SysOssVo;
import com.ruoyi.system.mapper.SysOssMapper;
import com.ruoyi.system.service.ISysOssService;
import com.ruoyi.system.domain.vo.SysOssVo;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
@@ -41,27 +41,27 @@
    private LambdaQueryWrapper<SysOss> buildQueryWrapper(SysOssBo bo) {
        Map<String, Object> params = bo.getParams();
        LambdaQueryWrapper<SysOss> lqw = Wrappers.lambdaQuery();
        lqw.like(StrUtil.isNotBlank(bo.getFileName()), SysOss::getFileName, bo.getFileName());
        lqw.like(StrUtil.isNotBlank(bo.getOriginalName()), SysOss::getOriginalName, bo.getOriginalName());
        lqw.eq(StrUtil.isNotBlank(bo.getFileSuffix()), SysOss::getFileSuffix, bo.getFileSuffix());
        lqw.eq(StrUtil.isNotBlank(bo.getUrl()), SysOss::getUrl, bo.getUrl());
        lqw.like(StringUtils.isNotBlank(bo.getFileName()), SysOss::getFileName, bo.getFileName());
        lqw.like(StringUtils.isNotBlank(bo.getOriginalName()), SysOss::getOriginalName, bo.getOriginalName());
        lqw.eq(StringUtils.isNotBlank(bo.getFileSuffix()), SysOss::getFileSuffix, bo.getFileSuffix());
        lqw.eq(StringUtils.isNotBlank(bo.getUrl()), SysOss::getUrl, bo.getUrl());
        lqw.between(params.get("beginCreateTime") != null && params.get("endCreateTime") != null,
            SysOss::getCreateTime, params.get("beginCreateTime"), params.get("endCreateTime"));
        lqw.eq(StrUtil.isNotBlank(bo.getCreateBy()), SysOss::getCreateBy, bo.getCreateBy());
        lqw.eq(StrUtil.isNotBlank(bo.getService()), SysOss::getService, bo.getService());
        lqw.eq(StringUtils.isNotBlank(bo.getCreateBy()), SysOss::getCreateBy, bo.getCreateBy());
        lqw.eq(StringUtils.isNotBlank(bo.getService()), SysOss::getService, bo.getService());
        return lqw;
    }
    @Override
    public SysOss upload(MultipartFile file) {
        String originalfileName = file.getOriginalFilename();
        String suffix = StrUtil.sub(originalfileName, originalfileName.lastIndexOf("."), originalfileName.length());
        ICloudStorageService storage = OssFactory.instance();
        String suffix = StringUtils.substring(originalfileName, originalfileName.lastIndexOf("."), originalfileName.length());
        ICloudStorageStrategy storage = OssFactory.instance();
        UploadResult uploadResult;
        try {
            uploadResult = storage.uploadSuffix(file.getBytes(), suffix, file.getContentType());
        } catch (IOException e) {
            throw new CustomException("文件读取异常!!!", e);
            throw new ServiceException(e.getMessage());
        }
        // ä¿å­˜æ–‡ä»¶ä¿¡æ¯
        SysOss oss = new SysOss()
@@ -81,7 +81,7 @@
        }
        List<SysOss> list = listByIds(ids);
        for (SysOss sysOss : list) {
            ICloudStorageService storage = OssFactory.instance(sysOss.getService());
            ICloudStorageStrategy storage = OssFactory.instance(sysOss.getService());
            storage.delete(sysOss.getUrl());
        }
        return removeByIds(ids);
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysPostServiceImpl.java
@@ -1,13 +1,12 @@
package com.ruoyi.system.service.impl;
import cn.hutool.core.lang.Validator;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.ruoyi.common.constant.UserConstants;
import com.ruoyi.common.core.mybatisplus.core.ServicePlusImpl;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.exception.CustomException;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.PageUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.system.domain.SysPost;
import com.ruoyi.system.domain.SysUserPost;
import com.ruoyi.system.mapper.SysPostMapper;
@@ -33,9 +32,9 @@
    @Override
    public TableDataInfo<SysPost> selectPagePostList(SysPost post) {
        LambdaQueryWrapper<SysPost> lqw = new LambdaQueryWrapper<SysPost>()
                .like(StrUtil.isNotBlank(post.getPostCode()), SysPost::getPostCode, post.getPostCode())
                .eq(StrUtil.isNotBlank(post.getStatus()), SysPost::getStatus, post.getStatus())
                .like(StrUtil.isNotBlank(post.getPostName()), SysPost::getPostName, post.getPostName());
                .like(StringUtils.isNotBlank(post.getPostCode()), SysPost::getPostCode, post.getPostCode())
                .eq(StringUtils.isNotBlank(post.getStatus()), SysPost::getStatus, post.getStatus())
                .like(StringUtils.isNotBlank(post.getPostName()), SysPost::getPostName, post.getPostName());
        return PageUtils.buildDataInfo(page(PageUtils.buildPage(),lqw));
    }
@@ -48,9 +47,9 @@
    @Override
    public List<SysPost> selectPostList(SysPost post) {
        return list(new LambdaQueryWrapper<SysPost>()
                .like(StrUtil.isNotBlank(post.getPostCode()), SysPost::getPostCode, post.getPostCode())
                .eq(StrUtil.isNotBlank(post.getStatus()), SysPost::getStatus, post.getStatus())
                .like(StrUtil.isNotBlank(post.getPostName()), SysPost::getPostName, post.getPostName()));
                .like(StringUtils.isNotBlank(post.getPostCode()), SysPost::getPostCode, post.getPostCode())
                .eq(StringUtils.isNotBlank(post.getStatus()), SysPost::getStatus, post.getStatus())
                .like(StringUtils.isNotBlank(post.getPostName()), SysPost::getPostName, post.getPostName()));
    }
    /**
@@ -93,10 +92,10 @@
     */
    @Override
    public String checkPostNameUnique(SysPost post) {
        Long postId = Validator.isNull(post.getPostId()) ? -1L : post.getPostId();
        Long postId = StringUtils.isNull(post.getPostId()) ? -1L : post.getPostId();
        SysPost info = getOne(new LambdaQueryWrapper<SysPost>()
                .eq(SysPost::getPostName, post.getPostName()).last("limit 1"));
        if (Validator.isNotNull(info) && info.getPostId().longValue() != postId.longValue()) {
        if (StringUtils.isNotNull(info) && info.getPostId().longValue() != postId.longValue()) {
            return UserConstants.NOT_UNIQUE;
        }
        return UserConstants.UNIQUE;
@@ -110,10 +109,10 @@
     */
    @Override
    public String checkPostCodeUnique(SysPost post) {
        Long postId = Validator.isNull(post.getPostId()) ? -1L : post.getPostId();
        Long postId = StringUtils.isNull(post.getPostId()) ? -1L : post.getPostId();
        SysPost info = getOne(new LambdaQueryWrapper<SysPost>()
                .eq(SysPost::getPostCode, post.getPostCode()).last("limit 1"));
        if (Validator.isNotNull(info) && info.getPostId().longValue() != postId.longValue()) {
        if (StringUtils.isNotNull(info) && info.getPostId().longValue() != postId.longValue()) {
            return UserConstants.NOT_UNIQUE;
        }
        return UserConstants.UNIQUE;
@@ -153,7 +152,7 @@
        for (Long postId : postIds) {
            SysPost post = selectPostById(postId);
            if (countUserPostById(postId) > 0) {
                throw new CustomException(String.format("%1$s已分配,不能删除", post.getPostName()));
                throw new ServiceException(String.format("%1$s已分配,不能删除", post.getPostName()));
            }
        }
        return baseMapper.deleteBatchIds(Arrays.asList(postIds));
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysRoleServiceImpl.java
@@ -1,14 +1,14 @@
package com.ruoyi.system.service.impl;
import cn.hutool.core.lang.Validator;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.ruoyi.common.annotation.DataScope;
import com.ruoyi.common.constant.UserConstants;
import com.ruoyi.common.core.domain.entity.SysRole;
import com.ruoyi.common.core.mybatisplus.core.ServicePlusImpl;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.exception.CustomException;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.PageUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.spring.SpringUtils;
import com.ruoyi.system.domain.SysRoleDept;
import com.ruoyi.system.domain.SysRoleMenu;
@@ -91,7 +91,7 @@
        List<SysRole> perms = baseMapper.selectRolePermissionByUserId(userId);
        Set<String> permsSet = new HashSet<>();
        for (SysRole perm : perms) {
            if (Validator.isNotNull(perm)) {
            if (StringUtils.isNotNull(perm)) {
                permsSet.addAll(Arrays.asList(perm.getRoleKey().trim().split(",")));
            }
        }
@@ -138,10 +138,10 @@
     */
    @Override
    public String checkRoleNameUnique(SysRole role) {
        Long roleId = Validator.isNull(role.getRoleId()) ? -1L : role.getRoleId();
        Long roleId = StringUtils.isNull(role.getRoleId()) ? -1L : role.getRoleId();
        SysRole info = getOne(new LambdaQueryWrapper<SysRole>()
                .eq(SysRole::getRoleName, role.getRoleName()).last("limit 1"));
        if (Validator.isNotNull(info) && info.getRoleId().longValue() != roleId.longValue()) {
        if (StringUtils.isNotNull(info) && info.getRoleId().longValue() != roleId.longValue()) {
            return UserConstants.NOT_UNIQUE;
        }
        return UserConstants.UNIQUE;
@@ -155,10 +155,10 @@
     */
    @Override
    public String checkRoleKeyUnique(SysRole role) {
        Long roleId = Validator.isNull(role.getRoleId()) ? -1L : role.getRoleId();
        Long roleId = StringUtils.isNull(role.getRoleId()) ? -1L : role.getRoleId();
        SysRole info = getOne(new LambdaQueryWrapper<SysRole>()
                .eq(SysRole::getRoleKey, role.getRoleKey()).last("limit 1"));
        if (Validator.isNotNull(info) && info.getRoleId().longValue() != roleId.longValue()) {
        if (StringUtils.isNotNull(info) && info.getRoleId().longValue() != roleId.longValue()) {
            return UserConstants.NOT_UNIQUE;
        }
        return UserConstants.UNIQUE;
@@ -171,8 +171,8 @@
     */
    @Override
    public void checkRoleAllowed(SysRole role) {
        if (Validator.isNotNull(role.getRoleId()) && role.isAdmin()) {
            throw new CustomException("不允许操作超级管理员角色");
        if (StringUtils.isNotNull(role.getRoleId()) && role.isAdmin()) {
            throw new ServiceException("不允许操作超级管理员角色");
        }
    }
@@ -316,7 +316,7 @@
            checkRoleAllowed(new SysRole(roleId));
            SysRole role = selectRoleById(roleId);
            if (countUserRoleByRoleId(roleId) > 0) {
                throw new CustomException(String.format("%1$s已分配,不能删除", role.getRoleName()));
                throw new ServiceException(String.format("%1$s已分配,不能删除", role.getRoleName()));
            }
        }
        List<Long> ids = Arrays.asList(roleIds);
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysUserOnlineServiceImpl.java
@@ -1,8 +1,7 @@
package com.ruoyi.system.service.impl;
import cn.hutool.core.lang.Validator;
import cn.hutool.core.util.StrUtil;
import com.ruoyi.common.core.domain.model.LoginUser;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.system.domain.SysUserOnline;
import com.ruoyi.system.service.ISysUserOnlineService;
import org.springframework.stereotype.Service;
@@ -23,7 +22,7 @@
     */
    @Override
    public SysUserOnline selectOnlineByIpaddr(String ipaddr, LoginUser user) {
        if (StrUtil.equals(ipaddr, user.getIpaddr())) {
        if (StringUtils.equals(ipaddr, user.getIpaddr())) {
            return loginUserToUserOnline(user);
        }
        return null;
@@ -38,7 +37,7 @@
     */
    @Override
    public SysUserOnline selectOnlineByUserName(String userName, LoginUser user) {
        if (StrUtil.equals(userName, user.getUsername())) {
        if (StringUtils.equals(userName, user.getUsername())) {
            return loginUserToUserOnline(user);
        }
        return null;
@@ -54,7 +53,7 @@
     */
    @Override
    public SysUserOnline selectOnlineByInfo(String ipaddr, String userName, LoginUser user) {
        if (StrUtil.equals(ipaddr, user.getIpaddr()) && StrUtil.equals(userName, user.getUsername())) {
        if (StringUtils.equals(ipaddr, user.getIpaddr()) && StringUtils.equals(userName, user.getUsername())) {
            return loginUserToUserOnline(user);
        }
        return null;
@@ -68,7 +67,7 @@
     */
    @Override
    public SysUserOnline loginUserToUserOnline(LoginUser user) {
        if (Validator.isNull(user) || Validator.isNull(user.getUser())) {
        if (StringUtils.isNull(user) || StringUtils.isNull(user.getUser())) {
            return null;
        }
        SysUserOnline sysUserOnline = new SysUserOnline();
@@ -79,7 +78,7 @@
        sysUserOnline.setBrowser(user.getBrowser());
        sysUserOnline.setOs(user.getOs());
        sysUserOnline.setLoginTime(user.getLoginTime());
        if (Validator.isNotNull(user.getUser().getDept())) {
        if (StringUtils.isNotNull(user.getUser().getDept())) {
            sysUserOnline.setDeptName(user.getUser().getDept().getDeptName());
        }
        return sysUserOnline;
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysUserServiceImpl.java
@@ -1,6 +1,5 @@
package com.ruoyi.system.service.impl;
import cn.hutool.core.lang.Validator;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.ruoyi.common.annotation.DataScope;
@@ -9,9 +8,10 @@
import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.core.mybatisplus.core.ServicePlusImpl;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.exception.CustomException;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.PageUtils;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.system.domain.SysPost;
import com.ruoyi.system.domain.SysUserPost;
import com.ruoyi.system.domain.SysUserRole;
@@ -128,7 +128,7 @@
        for (SysRole role : list) {
            idsStr.append(role.getRoleName()).append(",");
        }
        if (Validator.isNotEmpty(idsStr.toString())) {
        if (StringUtils.isNotEmpty(idsStr.toString())) {
            return idsStr.substring(0, idsStr.length() - 1);
        }
        return idsStr.toString();
@@ -147,7 +147,7 @@
        for (SysPost post : list) {
            idsStr.append(post.getPostName()).append(",");
        }
        if (Validator.isNotEmpty(idsStr.toString())) {
        if (StringUtils.isNotEmpty(idsStr.toString())) {
            return idsStr.substring(0, idsStr.length() - 1);
        }
        return idsStr.toString();
@@ -176,11 +176,11 @@
     */
    @Override
    public String checkPhoneUnique(SysUser user) {
        Long userId = Validator.isNull(user.getUserId()) ? -1L : user.getUserId();
        Long userId = StringUtils.isNull(user.getUserId()) ? -1L : user.getUserId();
        SysUser info = getOne(new LambdaQueryWrapper<SysUser>()
                .select(SysUser::getUserId, SysUser::getPhonenumber)
                .eq(SysUser::getPhonenumber, user.getPhonenumber()).last("limit 1"));
        if (Validator.isNotNull(info) && info.getUserId().longValue() != userId.longValue()) {
        if (StringUtils.isNotNull(info) && info.getUserId().longValue() != userId.longValue()) {
            return UserConstants.NOT_UNIQUE;
        }
        return UserConstants.UNIQUE;
@@ -194,11 +194,11 @@
     */
    @Override
    public String checkEmailUnique(SysUser user) {
        Long userId = Validator.isNull(user.getUserId()) ? -1L : user.getUserId();
        Long userId = StringUtils.isNull(user.getUserId()) ? -1L : user.getUserId();
        SysUser info = getOne(new LambdaQueryWrapper<SysUser>()
                .select(SysUser::getUserId, SysUser::getEmail)
                .eq(SysUser::getEmail, user.getEmail()).last("limit 1"));
        if (Validator.isNotNull(info) && info.getUserId().longValue() != userId.longValue()) {
        if (StringUtils.isNotNull(info) && info.getUserId().longValue() != userId.longValue()) {
            return UserConstants.NOT_UNIQUE;
        }
        return UserConstants.UNIQUE;
@@ -211,8 +211,8 @@
     */
    @Override
    public void checkUserAllowed(SysUser user) {
        if (Validator.isNotNull(user.getUserId()) && user.isAdmin()) {
            throw new CustomException("不允许操作超级管理员用户");
        if (StringUtils.isNotNull(user.getUserId()) && user.isAdmin()) {
            throw new ServiceException("不允许操作超级管理员用户");
        }
    }
@@ -232,6 +232,17 @@
        // æ–°å¢žç”¨æˆ·ä¸Žè§’色管理
        insertUserRole(user);
        return rows;
    }
    /**
     * æ³¨å†Œç”¨æˆ·ä¿¡æ¯
     *
     * @param user ç”¨æˆ·ä¿¡æ¯
     * @return ç»“æžœ
     */
    @Override
    public boolean registerUser(SysUser user) {
        return baseMapper.insert(user) > 0;
    }
    /**
@@ -340,7 +351,7 @@
     */
    public void insertUserRole(SysUser user) {
        Long[] roles = user.getRoleIds();
        if (Validator.isNotNull(roles)) {
        if (StringUtils.isNotNull(roles)) {
            // æ–°å¢žç”¨æˆ·ä¸Žè§’色管理
            List<SysUserRole> list = new ArrayList<SysUserRole>();
            for (Long roleId : roles) {
@@ -362,7 +373,7 @@
     */
    public void insertUserPost(SysUser user) {
        Long[] posts = user.getPostIds();
        if (Validator.isNotNull(posts)) {
        if (StringUtils.isNotNull(posts)) {
            // æ–°å¢žç”¨æˆ·ä¸Žå²—位管理
            List<SysUserPost> list = new ArrayList<SysUserPost>();
            for (Long postId : posts) {
@@ -384,7 +395,7 @@
     * @param roleIds è§’色组
     */
    public void insertUserRole(Long userId, Long[] roleIds) {
        if (Validator.isNotNull(roleIds)) {
        if (StringUtils.isNotNull(roleIds)) {
            // æ–°å¢žç”¨æˆ·ä¸Žè§’色管理
            List<SysUserRole> list = new ArrayList<SysUserRole>();
            for (Long roleId : roleIds) {
@@ -445,8 +456,8 @@
     */
    @Override
    public String importUser(List<SysUser> userList, Boolean isUpdateSupport, String operName) {
        if (Validator.isNull(userList) || userList.size() == 0) {
            throw new CustomException("导入用户数据不能为空!");
        if (StringUtils.isNull(userList) || userList.size() == 0) {
            throw new ServiceException("导入用户数据不能为空!");
        }
        int successNum = 0;
        int failureNum = 0;
@@ -457,7 +468,7 @@
            try {
                // éªŒè¯æ˜¯å¦å­˜åœ¨è¿™ä¸ªç”¨æˆ·
                SysUser u = baseMapper.selectUserByUserName(user.getUserName());
                if (Validator.isNull(u)) {
                if (StringUtils.isNull(u)) {
                    user.setPassword(SecurityUtils.encryptPassword(password));
                    user.setCreateBy(operName);
                    this.insertUser(user);
@@ -481,7 +492,7 @@
        }
        if (failureNum > 0) {
            failureMsg.insert(0, "很抱歉,导入失败!共 " + failureNum + " æ¡æ•°æ®æ ¼å¼ä¸æ­£ç¡®ï¼Œé”™è¯¯å¦‚下:");
            throw new CustomException(failureMsg.toString());
            throw new ServiceException(failureMsg.toString());
        } else {
            successMsg.insert(0, "恭喜您,数据已全部导入成功!共 " + successNum + " æ¡ï¼Œæ•°æ®å¦‚下:");
        }
ruoyi-system/src/main/resources/mapper/system/SysOssConfigMapper.xml
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ruoyi.system.mapper.SysOssConfigMapper">
    <resultMap type="com.ruoyi.system.domain.SysOssConfig" id="SysOssConfigResult">
        <result property="ossConfigId" column="oss_config_id"/>
        <result property="configKey" column="config_key"/>
        <result property="accessKey" column="access_key"/>
        <result property="secretKey" column="secret_key"/>
        <result property="bucketName" column="bucket_name"/>
        <result property="prefix" column="prefix"/>
        <result property="endpoint" column="endpoint"/>
        <result property="isHttps" column="is_https"/>
        <result property="region" column="region"/>
        <result property="status" column="status"/>
        <result property="ext1" column="ext1"/>
        <result property="createBy" column="create_by"/>
        <result property="createTime" column="create_time"/>
        <result property="updateBy" column="update_by"/>
        <result property="updateTime" column="update_time"/>
        <result property="remark" column="remark"/>
    </resultMap>
</mapper>
ruoyi-system/src/main/resources/mapper/system/SysOssMapper.xml
ruoyi-ui/bin/run-web.bat
@@ -1,6 +1,6 @@
@echo off
echo.
echo [信息] ä½¿ç”¨ Vue è¿è¡Œ Web å·¥ç¨‹ã€‚
echo [��Ϣ] Ê¹ï¿½ï¿½ Vue CLI ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ Web ï¿½ï¿½ï¿½Ì¡ï¿½
echo.
%~d0
@@ -9,4 +9,4 @@
cd ..
npm run dev
pause
pause
ruoyi-ui/package.json
@@ -1,6 +1,6 @@
{
  "name": "ruoyi-vue-plus",
  "version": "2.6.0",
  "version": "3.0.0",
  "description": "RuoYi-Vue-Plus后台管理系统",
  "author": "LionLi",
  "license": "MIT",
@@ -41,7 +41,7 @@
    "clipboard": "2.0.6",
    "core-js": "3.8.1",
    "echarts": "4.9.0",
    "element-ui": "2.15.3",
    "element-ui": "2.15.5",
    "file-saver": "2.0.4",
    "fuse.js": "6.4.3",
    "highlight.js": "9.18.5",
ruoyi-ui/src/api/demo/demo.js
@@ -52,11 +52,3 @@
  })
}
// å¯¼å‡ºæµ‹è¯•单表
export function exportDemo(query) {
  return request({
    url: '/demo/demo/export',
    method: 'get',
    params: query
  })
}
ruoyi-ui/src/api/demo/tree.js
@@ -42,12 +42,3 @@
    method: 'delete'
  })
}
// å¯¼å‡ºæµ‹è¯•树表
export function exportTree(query) {
  return request({
    url: '/demo/tree/export',
    method: 'get',
    params: query
  })
}
ruoyi-ui/src/api/login.js
@@ -15,6 +15,18 @@
  })
}
// æ³¨å†Œæ–¹æ³•
export function register(data) {
  return request({
    url: '/register',
    headers: {
      isToken: false
    },
    method: 'post',
    data: data
  })
}
// èŽ·å–ç”¨æˆ·è¯¦ç»†ä¿¡æ¯
export function getInfo() {
  return request({
@@ -37,4 +49,4 @@
    url: '/captchaImage',
    method: 'get'
  })
}
}
ruoyi-ui/src/api/monitor/jobLog.js
@@ -24,12 +24,3 @@
    method: 'delete'
  })
}
// å¯¼å‡ºè°ƒåº¦æ—¥å¿—
export function exportJobLog(query) {
  return request({
    url: '/monitor/jobLog/export',
    method: 'get',
    params: query
  })
}
ruoyi-ui/src/api/monitor/logininfor.js
@@ -24,12 +24,3 @@
    method: 'delete'
  })
}
// å¯¼å‡ºç™»å½•日志
export function exportLogininfor(query) {
  return request({
    url: '/monitor/logininfor/export',
    method: 'get',
    params: query
  })
}
ruoyi-ui/src/api/monitor/operlog.js
@@ -24,12 +24,3 @@
    method: 'delete'
  })
}
// å¯¼å‡ºæ“ä½œæ—¥å¿—
export function exportOperlog(query) {
  return request({
    url: '/monitor/operlog/export',
    method: 'get',
    params: query
  })
}
ruoyi-ui/src/api/system/config.js
@@ -59,11 +59,3 @@
  })
}
// å¯¼å‡ºå‚æ•°
export function exportConfig(query) {
  return request({
    url: '/system/config/export',
    method: 'get',
    params: query
  })
}
ruoyi-ui/src/api/system/dict/data.js
@@ -50,12 +50,3 @@
    method: 'delete'
  })
}
// å¯¼å‡ºå­—典数据
export function exportData(query) {
  return request({
    url: '/system/dict/data/export',
    method: 'get',
    params: query
  })
}
ruoyi-ui/src/api/system/dict/type.js
@@ -51,15 +51,6 @@
  })
}
// å¯¼å‡ºå­—典类型
export function exportType(query) {
  return request({
    url: '/system/dict/type/export',
    method: 'get',
    params: query
  })
}
// èŽ·å–å­—å…¸é€‰æ‹©æ¡†åˆ—è¡¨
export function optionselect() {
  return request({
ruoyi-ui/src/api/system/oss.js
@@ -16,3 +16,14 @@
    method: 'delete'
  })
}
export function changePreviewListResource(previewListResource) {
  const data = {
    previewListResource
  }
  return request({
    url: '/system/oss/changePreviewListResource',
    method: 'put',
    data: data
  })
}
ruoyi-ui/src/api/system/ossConfig.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,58 @@
import request from '@/utils/request'
// æŸ¥è¯¢äº‘存储配置列表
export function listOssConfig(query) {
  return request({
    url: '/system/oss/config/list',
    method: 'get',
    params: query
  })
}
// æŸ¥è¯¢äº‘存储配置详细
export function getOssConfig(ossConfigId) {
  return request({
    url: '/system/oss/config/' + ossConfigId,
    method: 'get'
  })
}
// æ–°å¢žäº‘存储配置
export function addOssConfig(data) {
  return request({
    url: '/system/oss/config',
    method: 'post',
    data: data
  })
}
// ä¿®æ”¹äº‘存储配置
export function updateOssConfig(data) {
  return request({
    url: '/system/oss/config',
    method: 'put',
    data: data
  })
}
// åˆ é™¤äº‘存储配置
export function delOssConfig(ossConfigId) {
  return request({
    url: '/system/oss/config/' + ossConfigId,
    method: 'delete'
  })
}
// ç”¨æˆ·çŠ¶æ€ä¿®æ”¹
export function changeOssConfigStatus(ossConfigId, status, configKey) {
  const data = {
    ossConfigId,
    status,
    configKey
  }
  return request({
    url: '/system/oss/config/changeStatus',
    method: 'put',
    data: data
  })
}
ruoyi-ui/src/api/system/role.js
@@ -65,16 +65,6 @@
  })
}
// å¯¼å‡ºè§’色
export function exportRole(query) {
  return request({
    url: '/system/role/export',
    method: 'get',
    params: query
  })
}
// æŸ¥è¯¢è§’色已授权用户列表
export function allocatedUserList(query) {
  return request({
ruoyi-ui/src/api/system/user.js
@@ -44,15 +44,6 @@
  })
}
// å¯¼å‡ºç”¨æˆ·
export function exportUser(query) {
  return request({
    url: '/system/user/export',
    method: 'get',
    params: query
  })
}
// ç”¨æˆ·å¯†ç é‡ç½®
export function resetUserPwd(userId, password) {
  const data = {
@@ -115,14 +106,6 @@
    url: '/system/user/profile/avatar',
    method: 'post',
    data: data
  })
}
// ä¸‹è½½ç”¨æˆ·å¯¼å…¥æ¨¡æ¿
export function importTemplate() {
  return request({
    url: '/system/user/importTemplate',
    method: 'get'
  })
}
ruoyi-ui/src/components/Breadcrumb/index.vue
@@ -45,7 +45,7 @@
      if (!name) {
        return false
      }
      return name.trim() === '首页'
      return name.trim() === 'Index'
    },
    handleLink(item) {
      const { redirect, path } = item
ruoyi-ui/src/main.js
@@ -16,7 +16,7 @@
import './permission' // permission control
import { getDicts } from "@/api/system/dict/data";
import { getConfigKey } from "@/api/system/config";
import { parseTime, resetForm, addDateRange, selectDictLabel, selectDictLabels, download, handleTree } from "@/utils/ruoyi";
import { parseTime, resetForm, addDateRange, selectDictLabel, selectDictLabels, handleTree } from "@/utils/ruoyi";
import Pagination from "@/components/Pagination";
// è‡ªå®šä¹‰è¡¨æ ¼å·¥å…·ç»„ä»¶
import RightToolbar from "@/components/RightToolbar"
@@ -39,7 +39,6 @@
Vue.prototype.addDateRange = addDateRange
Vue.prototype.selectDictLabel = selectDictLabel
Vue.prototype.selectDictLabels = selectDictLabels
Vue.prototype.download = download
Vue.prototype.handleTree = handleTree
Vue.prototype.msgSuccess = function (msg) {
ruoyi-ui/src/router/index.js
@@ -44,6 +44,11 @@
    hidden: true
  },
  {
    path: '/register',
    component: (resolve) => require(['@/views/register'], resolve),
    hidden: true
  },
  {
    path: '/404',
    component: (resolve) => require(['@/views/error/404'], resolve),
    hidden: true
@@ -61,8 +66,8 @@
      {
        path: 'index',
        component: (resolve) => require(['@/views/index'], resolve),
        name: '首页',
        meta: { title: '首页', icon: 'dashboard', noCache: true, affix: true }
        name: 'Index',
        meta: { title: '首页', icon: 'dashboard', affix: true }
      }
    ]
  },
@@ -120,6 +125,19 @@
    ]
  },
  {
    path: '/system/oss-config',
    component: Layout,
    hidden: true,
    children: [
      {
        path: 'index',
        component: (resolve) => require(['@/views/system/oss/config'], resolve),
        name: 'OssConfig',
        meta: { title: '配置管理', activeMenu: '/system/oss'}
      }
    ]
  },
  {
    path: '/monitor/job-log',
    component: Layout,
    hidden: true,
ruoyi-ui/src/store/modules/user.js
@@ -51,7 +51,7 @@
      return new Promise((resolve, reject) => {
        getInfo().then(res => {
          const user = res.data.user
          const avatar = user.avatar == "" ? require("@/assets/images/profile.jpg") : process.env.VUE_APP_BASE_API + user.avatar;
          const avatar = user.avatar == "" ? require("@/assets/images/profile.jpg") : user.avatar;
          if (res.data.roles && res.data.roles.length > 0) { // éªŒè¯è¿”回的roles是否是一个非空数组
            commit('SET_ROLES', res.data.roles)
            commit('SET_PERMISSIONS', res.data.permissions)
ruoyi-ui/src/utils/download.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,91 @@
import axios from 'axios'
import { getToken } from '@/utils/auth'
const mimeMap = {
  xlsx: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
  zip: 'application/zip',
  oss: 'application/octet-stream'
}
const baseUrl = process.env.VUE_APP_BASE_API
export function downLoadZip(str, filename) {
  var url = baseUrl + str
  axios({
    method: 'get',
    url: url,
    responseType: 'blob',
    headers: { 'Authorization': 'Bearer ' + getToken() }
  }).then(res => {
    resolveBlob(res, mimeMap.zip)
  })
}
export function downLoadOss(ossId) {
  var url = baseUrl + '/system/oss/download/' + ossId
  axios({
    method: 'get',
    url: url,
    responseType: 'blob',
    headers: { 'Authorization': 'Bearer ' + getToken() }
  }).then(res => {
    resolveBlob(res, mimeMap.oss)
  })
}
export function downLoadExcel(url, params) {
  // get请求映射params参数
  if (params) {
    let urlparams = url + '?';
    for (const propName of Object.keys(params)) {
      const value = params[propName];
      var part = encodeURIComponent(propName) + "=";
      if (value !== null && typeof(value) !== "undefined") {
        if (typeof value === 'object') {
          for (const key of Object.keys(value)) {
            if (value[key] !== null && typeof (value[key]) !== 'undefined') {
              let params = propName + '[' + key + ']';
              let subPart = encodeURIComponent(params) + '=';
              urlparams += subPart + encodeURIComponent(value[key]) + '&';
            }
          }
        } else {
          urlparams += part + encodeURIComponent(value) + "&";
        }
      }
    }
    urlparams = urlparams.slice(0, -1);
    url = urlparams;
  }
  url = baseUrl + url
  axios({
    method: 'get',
    url: url,
    responseType: 'blob',
    headers: { 'Authorization': 'Bearer ' + getToken() }
  }).then(res => {
    resolveBlob(res, mimeMap.xlsx)
  })
}
/**
 * è§£æžblob响应内容并下载
 * @param {*} res blob响应内容
 * @param {String} mimeType MIME类型
 */
export function resolveBlob(res, mimeType) {
  const aLink = document.createElement('a')
  var blob = new Blob([res.data], { type: mimeType })
  // //从response的headers中获取filename, åŽç«¯response.setHeader("Content-disposition", "attachment; filename=xxxx.docx") è®¾ç½®çš„æ–‡ä»¶å;
  var patt = new RegExp('filename=([^;]+\\.[^\\.;]+);*')
  var contentDisposition = decodeURI(res.headers['content-disposition'])
  var result = patt.exec(contentDisposition)
  var fileName = result[1]
  fileName = fileName.replace(/\"/g, '')
  aLink.style.display = 'none'
  aLink.href = URL.createObjectURL(blob)
  aLink.setAttribute('download', decodeURI(fileName)) // è®¾ç½®ä¸‹è½½æ–‡ä»¶åç§°
  document.body.appendChild(aLink)
  aLink.click()
  URL.revokeObjectURL(aLink.href);//清除引用
  document.body.removeChild(aLink);
}
ruoyi-ui/src/utils/ossdownload.js
ÎļþÒÑɾ³ý
ruoyi-ui/src/utils/request.js
@@ -28,9 +28,11 @@
      if (value !== null && typeof(value) !== "undefined") {
        if (typeof value === 'object') {
          for (const key of Object.keys(value)) {
            let params = propName + '[' + key + ']';
            var subPart = encodeURIComponent(params) + "=";
            url += subPart + encodeURIComponent(value[key]) + "&";
            if (value[key] !== null && typeof (value[key]) !== 'undefined') {
              let params = propName + '[' + key + ']';
              let subPart = encodeURIComponent(params) + '=';
              url += subPart + encodeURIComponent(value[key]) + '&';
            }
          }
        } else {
          url += part + encodeURIComponent(value) + "&";
@@ -64,7 +66,7 @@
          location.href = '/index';
        })
      }).catch(() => {});
      return Promise.reject('error')
      return Promise.reject('令牌验证失败')
    } else if (code === 500) {
      Message({
        message: msg,
ruoyi-ui/src/utils/ruoyi.js
@@ -55,16 +55,15 @@
// æ·»åŠ æ—¥æœŸèŒƒå›´
export function addDateRange(params, dateRange, propName) {
    var search = params;
    search.params = {};
    if (null != dateRange && '' != dateRange) {
        if (typeof (propName) === "undefined") {
            search.params["beginTime"] = dateRange[0];
            search.params["endTime"] = dateRange[1];
        } else {
            search.params["begin" + propName] = dateRange[0];
            search.params["end" + propName] = dateRange[1];
        }
    let search = params;
    search.params = typeof (search.params) === 'object' && search.params !== null && !Array.isArray(search.params) ? search.params : {};
    dateRange = Array.isArray(dateRange) ? dateRange : [];
    if (typeof (propName) === 'undefined') {
        search.params['beginTime'] = dateRange[0];
        search.params['endTime'] = dateRange[1];
    } else {
        search.params['begin' + propName] = dateRange[0];
        search.params['end' + propName] = dateRange[1];
    }
    return search;
}
@@ -94,11 +93,6 @@
        })
    })
    return actions.join('').substring(0, actions.join('').length - 1);
}
// é€šç”¨ä¸‹è½½æ–¹æ³•
export function download(fileName) {
    window.location.href = baseURL + "/common/download?fileName=" + encodeURI(fileName) + "&delete=" + true;
}
// å­—符串格式化(%s )
ruoyi-ui/src/utils/zipdownload.js
ÎļþÒÑɾ³ý
ruoyi-ui/src/views/demo/demo/index.vue
@@ -169,7 +169,8 @@
</template>
<script>
import { listDemo, pageDemo, getDemo, delDemo, addDemo, updateDemo, exportDemo } from "@/api/demo/demo";
import { listDemo, pageDemo, getDemo, delDemo, addDemo, updateDemo } from "@/api/demo/demo";
import { downLoadExcel } from "@/utils/download";
export default {
  name: "Demo",
@@ -360,18 +361,7 @@
    },
    /** å¯¼å‡ºæŒ‰é’®æ“ä½œ */
    handleExport() {
      const queryParams = this.queryParams;
      this.$confirm('是否确认导出所有测试单表数据项?', "警告", {
        confirmButtonText: "确定",
        cancelButtonText: "取消",
        type: "warning"
      }).then(() => {
        this.exportLoading = true;
        return exportDemo(queryParams);
      }).then(response => {
        this.download(response.msg);
        this.exportLoading = false;
      })
      downLoadExcel('/demo/demo/export', this.queryParams);
    }
  }
};
ruoyi-ui/src/views/demo/tree/index.vue
@@ -110,7 +110,7 @@
</template>
<script>
import { listTree, getTree, delTree, addTree, updateTree, exportTree } from "@/api/demo/tree";
import { listTree, getTree, delTree, addTree, updateTree } from "@/api/demo/tree";
import Treeselect from "@riophae/vue-treeselect";
import "@riophae/vue-treeselect/dist/vue-treeselect.css";
ruoyi-ui/src/views/index.vue
@@ -93,6 +93,54 @@
            <span>更新日志</span>
          </div>
          <el-collapse accordion>
            <el-collapse-item title="v3.0.0 - 2021-8-18">
              <ol>
                <li>add [重大更新]重写 OSS æ¨¡å—相关实现 æ”¯æŒåŠ¨æ€é…ç½®(页面配置)<li>
                <li>add [重大更新]增加 jackson è¶…出 JS æœ€å¤§æ•°å€¼è‡ªåŠ¨è½¬å­—ç¬¦ä¸²(雪花id序列化)处理<li>
                <li>add [重大更新]重写 é˜²é‡æäº¤æ‹¦æˆªå™¨ æ”¯æŒå…¨å±€ä¸Žæ³¨è§£è‡ªå®šä¹‰ æ‹¦æˆªæ—¶é—´é…ç½®é…ç½® ä¼˜åŒ–逻辑<li>
                <li>add [重大更新]新增是否开启用户注册功能<li>
                <li>add [重大更新]增加 easyexcel å·¥å…·ç±»<li>
                <li>add [重大更新]集成 æ€§èƒ½åˆ†æžæ’ä»¶ p6spy æ›´å¼ºåŠ²çš„ SQL åˆ†æž<li>
                <li>add [重大更新]增加 å®Œæ•´å›½é™…化解决方案<li>
                <li>add [重大更新]支持自定义注解实现接口限流<li>
                <li>update feign-okhttp 11.0 => 11.2<li>
                <li>update okhttp 3.19.4 => 4.9.1<li>
                <li>update minio 8.2.0 => 8.3.0<li>
                <li>update hutool 5.7.6 => 5.7.7<li>
                <li>update element-ui 2.15.2 => 2.15.5<li>
                <li>update springboot admin 2.4.3 => 2.5.0 (新增 Quartz ä¸“属监控页)<li>
                <li>add å¢žåŠ  admin ç›‘控客户端开关<li>
                <li>add å¢žåŠ  å›½é™…化演示demo<li>
                <li>update æ›´æ–°è½¯ä»¶æž¶æž„图<li>
                <li>update ä¼˜åŒ–XSS跨站脚本过滤<li>
                <li>update ä¼˜åŒ–BLOB下载时清除URL对象引用<li>
                <li>update æ›´æ–° é˜²é‡æäº¤æ‹¦æˆªå™¨ demo演示案例<li>
                <li>update æ—¥å¸¸å­—符串校验 ç»Ÿä¸€é‡æž„到 StringUtils ä¾¿äºŽç»´æŠ¤æ‰©å±•<li>
                <li>update ä¿®æ”¹ è‡ªåŠ¨æ³¨å…¥å™¨ ç”¨æˆ·æœªç™»å½•异常拦截抛出警告 è¿”回Null<li>
                <li>update é‡æž„ ç»Ÿä¸€ä½¿ç”¨ æµå·¥å…·ä¸‹è½½<li>
                <li>update é‡å†™ æ‰€æœ‰ä¸šåŠ¡å¯¼å‡º é€‚配easyexcel工具<li>
                <li>update ç§»åŠ¨æ–‡ä»¶å­˜å‚¨ä¸šåŠ¡åˆ° system æ¨¡å—<li>
                <li>update ä»£ç ç”Ÿæˆæ¨¡æ¿ é€‚配新excel导出<li>
                <li>update å°† Actuator é…ç½® ç§»åŠ¨åˆ°å…¨å±€é…ç½®<li>
                <li>update ç»Ÿä¸€é•œåƒæ—¶åŒºé…ç½® ç§»é™¤ä¸»æœºæ—¶é—´æ˜ å°„<li>
                <li>update æ›´æ”¹å¤šæ•°æ®æºæ¡†æž¶æ›´æ¸…晰的依赖名<li>
                <li>update æ›´æ–° é˜¿é‡Œäº‘ maven源 æ–°åœ°å€<li>
                <li>update è¡¥å…¨åŸºç¡€å®žä½“ æ–‡æ¡£æ³¨è§£<li>
                <li>update ä»£ç ç”Ÿæˆæ–‡æ¡£æ³¨è§£ å¢žåŠ å¿…å¡«åˆ¤æ–­é…ç½®<li>
                <li>update æ³¨å…¥å™¨ insert å¢žåŠ  update å­—段处理<li>
                <li>update é»˜è®¤é¦–页使用keep-alive缓存<li>
                <li>fix ç”Ÿäº§minio回显问题<li>
                <li>fix ä¿®å¤è§’色分配用户页面接收参数与传递参数类型不一致导致的错误<li>
                <li>fix ä¿®å¤ä»£ç ç”Ÿæˆ åˆ é™¤æŒ‰é’®æŠ¥é”™ loading ä¸å–消问题<li>
                <li>fix è§£å†³ç™»å½•后浏览器后台Breadcrumb组件报错<li>
                <li>fix ä¿®å¤DictUtils方法报错<li>
                <li>fix å¤´åƒä¸Šä¼  æœªèµ°OSS存储问题<li>
                <li>fix oss列表 jpeg ä¸å›žæ˜¾é—®é¢˜<li>
                <li>fix ä¿®å¤æ“ä½œæ—¥å¿—根据状态查询异常问题<li>
                <li>remove ç§»é™¤åŽŸç”Ÿexcel工具<li>
                <li>remove ç§»é™¤é€šç”¨ä¸Šä¼ ä¸‹è½½æŽ¥å£ä¸Žé…ç½®<li>
              </ol>
            </el-collapse-item>
            <el-collapse-item title="v2.6.0 - 2021-7-28">
              <ol>
                <li>add [重大新增] å¢žåŠ  OSS å¯¹è±¡å­˜å‚¨æ¨¡å—</li>
@@ -336,7 +384,7 @@
import config from '../../package.json'
export default {
  name: "index",
  name: "Index",
  data() {
    return {
      // ç‰ˆæœ¬å·
ruoyi-ui/src/views/login.vue
@@ -44,6 +44,9 @@
          <span v-if="!loading">登 å½•</span>
          <span v-else>登 å½• ä¸­...</span>
        </el-button>
        <div style="float: right;" v-if="register">
          <router-link class="link-type" :to="'/register'">立即注册</router-link>
        </div>
      </el-form-item>
    </el-form>
    <!--  åº•部  -->
@@ -73,15 +76,18 @@
      },
      loginRules: {
        username: [
          { required: true, trigger: "blur", message: "用户名不能为空" }
          { required: true, trigger: "blur", message: "请输入您的账号" }
        ],
        password: [
          { required: true, trigger: "blur", message: "密码不能为空" }
          { required: true, trigger: "blur", message: "请输入您的密码" }
        ],
        code: [{ required: true, trigger: "change", message: "验证码不能为空" }]
        code: [{ required: true, trigger: "change", message: "请输入验证码" }]
      },
      loading: false,
      // éªŒè¯ç å¼€å…³
      captchaOnOff: true,
      // æ³¨å†Œå¼€å…³
      register: false,
      redirect: undefined
    };
  },
ruoyi-ui/src/views/monitor/job/index.vue
@@ -280,7 +280,8 @@
</template>
<script>
import { listJob, getJob, delJob, addJob, updateJob, exportJob, runJob, changeJobStatus } from "@/api/monitor/job";
import { listJob, getJob, delJob, addJob, updateJob, runJob, changeJobStatus } from "@/api/monitor/job";
import { downLoadExcel } from "@/utils/download";
export default {
  name: "Job",
@@ -505,18 +506,7 @@
    },
    /** å¯¼å‡ºæŒ‰é’®æ“ä½œ */
    handleExport() {
      const queryParams = this.queryParams;
      this.$confirm("是否确认导出所有定时任务数据项?", "警告", {
          confirmButtonText: "确定",
          cancelButtonText: "取消",
          type: "warning"
        }).then(() => {
          this.exportLoading = true;
          return exportJob(queryParams);
        }).then(response => {
          this.download(response.msg);
          this.exportLoading = false;
        }).catch(() => {});
      downLoadExcel('/monitor/job/export', this.queryParams);
    }
  }
};
ruoyi-ui/src/views/monitor/job/log.vue
@@ -177,8 +177,9 @@
</template>
<script>
import { getJob} from "@/api/monitor/job";
import { listJobLog, delJobLog, exportJobLog, cleanJobLog } from "@/api/monitor/jobLog";
import { getJob } from "@/api/monitor/job";
import { listJobLog, delJobLog, cleanJobLog } from "@/api/monitor/jobLog";
import { downLoadExcel } from "@/utils/download";
export default {
  name: "JobLog",
@@ -310,19 +311,8 @@
    },
    /** å¯¼å‡ºæŒ‰é’®æ“ä½œ */
    handleExport() {
      const queryParams = this.queryParams;
      this.$confirm("是否确认导出所有调度日志数据项?", "警告", {
          confirmButtonText: "确定",
          cancelButtonText: "取消",
          type: "warning"
        }).then(() => {
          this.exportLoading = true;
          return exportJobLog(queryParams);
        }).then(response => {
          this.download(response.msg);
          this.exportLoading = false;
        }).catch(() => {});
      downLoadExcel('/monitor/jobLog/export', this.queryParams);
    }
  }
};
</script>
</script>
ruoyi-ui/src/views/monitor/logininfor/index.vue
@@ -119,7 +119,8 @@
</template>
<script>
import { list, delLogininfor, cleanLogininfor, exportLogininfor } from "@/api/monitor/logininfor";
import { list, delLogininfor, cleanLogininfor } from "@/api/monitor/logininfor";
import { downLoadExcel } from "@/utils/download";
export default {
  name: "Logininfor",
@@ -228,18 +229,7 @@
    },
    /** å¯¼å‡ºæŒ‰é’®æ“ä½œ */
    handleExport() {
      const queryParams = this.queryParams;
      this.$confirm('是否确认导出所有操作日志数据项?', "警告", {
          confirmButtonText: "确定",
          cancelButtonText: "取消",
          type: "warning"
        }).then(() => {
          this.exportLoading = true;
          return exportLogininfor(queryParams);
        }).then(response => {
          this.download(response.msg);
          this.exportLoading = false;
        }).catch(() => {});
      downLoadExcel('/monitor/logininfor/export', this.queryParams);
    }
  }
};
ruoyi-ui/src/views/monitor/operlog/index.vue
@@ -188,7 +188,8 @@
</template>
<script>
import { list, delOperlog, cleanOperlog, exportOperlog } from "@/api/monitor/operlog";
import { list, delOperlog, cleanOperlog } from "@/api/monitor/operlog";
import { downLoadExcel } from "@/utils/download";
export default {
  name: "Operlog",
@@ -316,18 +317,7 @@
    },
    /** å¯¼å‡ºæŒ‰é’®æ“ä½œ */
    handleExport() {
      const queryParams = this.queryParams;
      this.$confirm('是否确认导出所有操作日志数据项?', "警告", {
          confirmButtonText: "确定",
          cancelButtonText: "取消",
          type: "warning"
        }).then(() => {
          this.exportLoading = true;
          return exportOperlog(queryParams);
        }).then(response => {
          this.download(response.msg);
          this.exportLoading = false;
        }).catch(() => {});
      downLoadExcel('/monitor/operlog/export', this.queryParams);
    }
  }
};
ruoyi-ui/src/views/register.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,208 @@
<template>
  <div class="register">
    <el-form ref="registerForm" :model="registerForm" :rules="registerRules" class="register-form">
      <h3 class="title">若依后台管理系统</h3>
      <el-form-item prop="username">
        <el-input v-model="registerForm.username" type="text" auto-complete="off" placeholder="账号">
          <svg-icon slot="prefix" icon-class="user" class="el-input__icon input-icon" />
        </el-input>
      </el-form-item>
      <el-form-item prop="password">
        <el-input
          v-model="registerForm.password"
          type="password"
          auto-complete="off"
          placeholder="密码"
          @keyup.enter.native="handleRegister"
        >
          <svg-icon slot="prefix" icon-class="password" class="el-input__icon input-icon" />
        </el-input>
      </el-form-item>
      <el-form-item prop="confirmPassword">
        <el-input
          v-model="registerForm.confirmPassword"
          type="password"
          auto-complete="off"
          placeholder="确认密码"
          @keyup.enter.native="handleRegister"
        >
          <svg-icon slot="prefix" icon-class="password" class="el-input__icon input-icon" />
        </el-input>
      </el-form-item>
      <el-form-item prop="code" v-if="captchaOnOff">
        <el-input
          v-model="registerForm.code"
          auto-complete="off"
          placeholder="验证码"
          style="width: 63%"
          @keyup.enter.native="handleRegister"
        >
          <svg-icon slot="prefix" icon-class="validCode" class="el-input__icon input-icon" />
        </el-input>
        <div class="register-code">
          <img :src="codeUrl" @click="getCode" class="register-code-img"/>
        </div>
      </el-form-item>
      <el-form-item style="width:100%;">
        <el-button
          :loading="loading"
          size="medium"
          type="primary"
          style="width:100%;"
          @click.native.prevent="handleRegister"
        >
          <span v-if="!loading">注 å†Œ</span>
          <span v-else>注 å†Œ ä¸­...</span>
        </el-button>
        <div style="float: right;">
          <router-link class="link-type" :to="'/login'">使用已有账户登录</router-link>
        </div>
      </el-form-item>
    </el-form>
    <!--  åº•部  -->
    <div class="el-register-footer">
      <span>Copyright Â© 2018-2021 ruoyi.vip All Rights Reserved.</span>
    </div>
  </div>
</template>
<script>
import { getCodeImg, register } from "@/api/login";
export default {
  name: "Register",
  data() {
    const equalToPassword = (rule, value, callback) => {
      if (this.registerForm.password !== value) {
        callback(new Error("两次输入的密码不一致"));
      } else {
        callback();
      }
    };
    return {
      codeUrl: "",
      registerForm: {
        username: "",
        password: "",
        confirmPassword: "",
        code: "",
        uuid: ""
      },
      registerRules: {
        username: [
          { required: true, trigger: "blur", message: "请输入您的账号" },
          { min: 2, max: 20, message: '用户账号长度必须介于 2 å’Œ 20 ä¹‹é—´', trigger: 'blur' }
        ],
        password: [
          { required: true, trigger: "blur", message: "请输入您的密码" },
          { min: 5, max: 20, message: '用户密码长度必须介于 5 å’Œ 20 ä¹‹é—´', trigger: 'blur' }
        ],
        confirmPassword: [
          { required: true, trigger: "blur", message: "请再次输入您的密码" },
          { required: true, validator: equalToPassword, trigger: "blur" }
        ],
        code: [{ required: true, trigger: "change", message: "请输入验证码" }]
      },
      loading: false,
      captchaOnOff: true
    };
  },
  created() {
    this.getCode();
  },
  methods: {
    getCode() {
      getCodeImg().then(res => {
        this.captchaOnOff = res.data.captchaOnOff === undefined ? true : res.data.captchaOnOff;
        if (this.captchaOnOff) {
          this.codeUrl = "data:image/gif;base64," + res.data.img;
          this.registerForm.uuid = res.data.uuid;
        }
      });
    },
    handleRegister() {
      this.$refs.registerForm.validate(valid => {
        if (valid) {
          this.loading = true;
          register(this.registerForm).then(res => {
            const username = this.registerForm.username;
            this.$alert("<font color='red'>恭喜你,您的账号 " + username + " æ³¨å†ŒæˆåŠŸï¼</font>", '系统提示', {
              dangerouslyUseHTMLString: true
            }).then(() => {
              this.$router.push("/login");
            }).catch(() => {});
          }).catch(() => {
            this.loading = false;
            if (this.captchaOnOff) {
              this.getCode();
            }
          })
        }
      });
    }
  }
};
</script>
<style rel="stylesheet/scss" lang="scss">
.register {
  display: flex;
  justify-content: center;
  align-items: center;
  height: 100%;
  background-image: url("../assets/images/login-background.jpg");
  background-size: cover;
}
.title {
  margin: 0px auto 30px auto;
  text-align: center;
  color: #707070;
}
.register-form {
  border-radius: 6px;
  background: #ffffff;
  width: 400px;
  padding: 25px 25px 5px 25px;
  .el-input {
    height: 38px;
    input {
      height: 38px;
    }
  }
  .input-icon {
    height: 39px;
    width: 14px;
    margin-left: 2px;
  }
}
.register-tip {
  font-size: 13px;
  text-align: center;
  color: #bfbfbf;
}
.register-code {
  width: 33%;
  height: 38px;
  float: right;
  img {
    cursor: pointer;
    vertical-align: middle;
  }
}
.el-register-footer {
  height: 40px;
  line-height: 40px;
  position: fixed;
  bottom: 0;
  width: 100%;
  text-align: center;
  color: #fff;
  font-family: Arial;
  font-size: 12px;
  letter-spacing: 1px;
}
.register-code-img {
  height: 38px;
}
</style>
ruoyi-ui/src/views/system/config/index.vue
@@ -181,7 +181,8 @@
</template>
<script>
import { listConfig, getConfig, delConfig, addConfig, updateConfig, exportConfig, refreshCache } from "@/api/system/config";
import { listConfig, getConfig, delConfig, addConfig, updateConfig, refreshCache } from "@/api/system/config";
import { downLoadExcel } from "@/utils/download";
export default {
  name: "Config",
@@ -342,18 +343,7 @@
    },
    /** å¯¼å‡ºæŒ‰é’®æ“ä½œ */
    handleExport() {
      const queryParams = this.queryParams;
      this.$confirm('是否确认导出所有参数数据项?', "警告", {
          confirmButtonText: "确定",
          cancelButtonText: "取消",
          type: "warning"
        }).then(() => {
          this.exportLoading = true;
          return exportConfig(queryParams);
        }).then(response => {
          this.download(response.msg);
          this.exportLoading = false;
        }).catch(() => {});
      downLoadExcel('/system/config/export', this.queryParams);
    },
    /** åˆ·æ–°ç¼“存按钮操作 */
    handleRefreshCache() {
ruoyi-ui/src/views/system/dict/data.vue
@@ -183,8 +183,9 @@
</template>
<script>
import { listData, getData, delData, addData, updateData, exportData } from "@/api/system/dict/data";
import { listData, getData, delData, addData, updateData } from "@/api/system/dict/data";
import { listType, getType } from "@/api/system/dict/type";
import { downLoadExcel } from "@/utils/download";
export default {
  name: "Data",
@@ -388,18 +389,7 @@
    },
    /** å¯¼å‡ºæŒ‰é’®æ“ä½œ */
    handleExport() {
      const queryParams = this.queryParams;
      this.$confirm('是否确认导出所有数据项?', "警告", {
          confirmButtonText: "确定",
          cancelButtonText: "取消",
          type: "warning"
        }).then(() => {
          this.exportLoading = true;
          return exportData(queryParams);
        }).then(response => {
          this.download(response.msg);
          this.exportLoading = false;
        }).catch(() => {});
      downLoadExcel('/system/dict/data/export', this.queryParams);
    }
  }
};
ruoyi-ui/src/views/system/dict/index.vue
@@ -193,7 +193,8 @@
</template>
<script>
import { listType, getType, delType, addType, updateType, exportType, refreshCache } from "@/api/system/dict/type";
import { listType, getType, delType, addType, updateType, refreshCache } from "@/api/system/dict/type";
import { downLoadExcel } from "@/utils/download";
export default {
  name: "Dict",
@@ -346,18 +347,7 @@
    },
    /** å¯¼å‡ºæŒ‰é’®æ“ä½œ */
    handleExport() {
      const queryParams = this.queryParams;
      this.$confirm('是否确认导出所有类型数据项?', "警告", {
          confirmButtonText: "确定",
          cancelButtonText: "取消",
          type: "warning"
        }).then(() => {
          this.exportLoading = true;
          return exportType(queryParams);
        }).then(response => {
          this.download(response.msg);
          this.exportLoading = false;
        }).catch(() => {});
      downLoadExcel('/system/dict/type/export', this.queryParams);
    },
    /** åˆ·æ–°ç¼“存按钮操作 */
    handleRefreshCache() {
ruoyi-ui/src/views/system/menu/index.vue
@@ -88,7 +88,7 @@
    </el-table>
    <!-- æ·»åŠ æˆ–ä¿®æ”¹èœå•å¯¹è¯æ¡† -->
    <el-dialog :title="title" :visible.sync="open" width="600px" append-to-body>
    <el-dialog :title="title" :visible.sync="open" width="680px" append-to-body>
      <el-form ref="form" :model="form" :rules="rules" label-width="100px">
        <el-row>
          <el-col :span="24">
ruoyi-ui/src/views/system/oss/config.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,414 @@
<template>
  <div class="app-container">
    <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px">
      <el-form-item label="配置key" prop="configKey">
        <el-select v-model="queryParams.configKey" placeholder="请选择配置key" clearable size="small">
          <el-option
            v-for="configKey in configKeyOptions"
            :key="configKey.configKey"
            :label="configKey.label"
            :value="configKey.configKey"
          />
        </el-select>
      </el-form-item>
      <el-form-item label="桶名称" prop="bucketName">
        <el-input
          v-model="queryParams.bucketName"
          placeholder="请输入桶名称"
          clearable
          size="small"
          @keyup.enter.native="handleQuery"
        />
      </el-form-item>
      <el-form-item label="状态" prop="status">
        <el-select v-model="queryParams.status" placeholder="请选择状态" clearable size="small">
          <el-option
            v-for="dict in statusOptions"
            :key="dict.dictValue"
            :label="dict.dictLabel"
            :value="dict.dictValue"
          />
        </el-select>
      </el-form-item>
      <el-form-item>
        <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
        <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
      </el-form-item>
    </el-form>
    <el-row :gutter="10" class="mb8">
      <el-col :span="1.5">
        <el-button
          type="primary"
          plain
          icon="el-icon-plus"
          size="mini"
          @click="handleAdd"
          v-hasPermi="['system:oss:add']"
        >新增</el-button>
      </el-col>
      <el-col :span="1.5">
        <el-button
          type="success"
          plain
          icon="el-icon-edit"
          size="mini"
          :disabled="single"
          @click="handleUpdate"
          v-hasPermi="['system:oss:edit']"
        >修改</el-button>
      </el-col>
      <el-col :span="1.5">
        <el-button
          type="danger"
          plain
          icon="el-icon-delete"
          size="mini"
          :disabled="multiple"
          @click="handleDelete"
          v-hasPermi="['system:oss:remove']"
        >删除</el-button>
      </el-col>
      <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
    </el-row>
    <el-table v-loading="loading" :data="ossConfigList" @selection-change="handleSelectionChange">
      <el-table-column type="selection" width="55" align="center" />
      <el-table-column label="主建" align="center" prop="ossConfigId" v-if="false"/>
      <el-table-column label="配置key" align="center" prop="configKey" />
      <el-table-column label="访问站点" align="center" prop="endpoint" width="200" />
      <el-table-column label="桶名称" align="center" prop="bucketName" />
      <el-table-column label="前缀" align="center" prop="prefix" />
      <el-table-column label="域" align="center" prop="region" />
      <el-table-column label="状态" align="center" prop="status">
        <template slot-scope="scope">
          <el-switch
            v-model="scope.row.status"
            active-value="0"
            inactive-value="1"
            @change="handleStatusChange(scope.row)"
          ></el-switch>
        </template>
      </el-table-column>
      <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
        <template slot-scope="scope">
          <el-button
            size="mini"
            type="text"
            icon="el-icon-edit"
            @click="handleUpdate(scope.row)"
            v-hasPermi="['system:oss:edit']"
          >修改</el-button>
          <el-button
            size="mini"
            type="text"
            icon="el-icon-delete"
            @click="handleDelete(scope.row)"
            v-hasPermi="['system:oss:remove']"
          >删除</el-button>
        </template>
      </el-table-column>
    </el-table>
    <pagination
      v-show="total > 0"
      :total="total"
      :page.sync="queryParams.pageNum"
      :limit.sync="queryParams.pageSize"
      @pagination="getList"
    />
    <!-- æ·»åŠ æˆ–ä¿®æ”¹äº‘å­˜å‚¨é…ç½®å¯¹è¯æ¡† -->
    <el-dialog :title="title" :visible.sync="open" width="800px" append-to-body>
      <el-form ref="form" :model="form" :rules="rules" label-width="120px">
        <el-form-item label="配置key" prop="configKey">
          <el-select v-model="form.configKey" placeholder="请选择配置key">
            <el-option
              v-for="configKey in configKeyOptions"
              :key="configKey.configKey"
              :label="configKey.label"
              :value="configKey.configKey"
            />
          </el-select>
        </el-form-item>
        <el-form-item label="访问站点" prop="endpoint">
          <el-input v-model="form.endpoint" placeholder="请输入访问站点" />
        </el-form-item>
        <el-form-item label="accessKey" prop="accessKey">
          <el-input v-model="form.accessKey" placeholder="请输入accessKey" />
        </el-form-item>
        <el-form-item label="secretKey" prop="secretKey">
          <el-input v-model="form.secretKey" placeholder="请输入秘钥" />
        </el-form-item>
        <el-form-item label="桶名称" prop="bucketName">
          <el-input v-model="form.bucketName" placeholder="请输入桶名称" />
        </el-form-item>
        <el-form-item label="前缀" prop="prefix">
          <el-input v-model="form.prefix" placeholder="请输入前缀" />
        </el-form-item>
        <el-form-item label="是否HTTPS">
          <el-radio-group v-model="form.isHttps">
            <el-radio
              v-for="dict in isHttpsOptions"
              :key="dict.dictValue"
              :label="dict.dictValue"
            >{{dict.dictLabel}}</el-radio>
          </el-radio-group>
        </el-form-item>
        <el-form-item label="域" prop="region">
          <el-input v-model="form.region" placeholder="请输入域" />
        </el-form-item>
        <el-form-item label="备注" prop="remark">
          <el-input v-model="form.remark" type="textarea" placeholder="请输入内容" />
        </el-form-item>
      </el-form>
      <div slot="footer" class="dialog-footer">
        <el-button :loading="buttonLoading" type="primary" @click="submitForm">ç¡® å®š</el-button>
        <el-button @click="cancel">取 æ¶ˆ</el-button>
      </div>
    </el-dialog>
  </div>
</template>
<script>
import {
  listOssConfig,
  getOssConfig,
  delOssConfig,
  addOssConfig,
  updateOssConfig,
  changeOssConfigStatus
} from "@/api/system/ossConfig";
export default {
  name: "OssConfig",
  data() {
    return {
      // æŒ‰é’®loading
      buttonLoading: false,
      // é®ç½©å±‚
      loading: true,
      // å¯¼å‡ºé®ç½©å±‚
      exportLoading: false,
      // é€‰ä¸­æ•°ç»„
      ids: [],
      // éžå•个禁用
      single: true,
      // éžå¤šä¸ªç¦ç”¨
      multiple: true,
      // æ˜¾ç¤ºæœç´¢æ¡ä»¶
      showSearch: true,
      // æ€»æ¡æ•°
      total: 0,
      // äº‘存储配置表格数据
      ossConfigList: [],
      // configKeyOptions
      configKeyOptions: [],
      configKeyDatas: [
        { configKey: "minio", label: "Minio" },
        { configKey: "qiniu", label: "七牛云" },
        { configKey: "aliyun", label: "阿里云" },
        { configKey: "qcloud", label: "腾讯云" },
      ],
      // å¼¹å‡ºå±‚标题
      title: "",
      // æ˜¯å¦æ˜¾ç¤ºå¼¹å‡ºå±‚
      open: false,
      // æ˜¯å¦https字典
      isHttpsOptions: [],
      // çŠ¶æ€(0正常 1停用)字典
      statusOptions: [],
      // æŸ¥è¯¢å‚æ•°
      queryParams: {
        pageNum: 1,
        pageSize: 10,
        configKey: undefined,
        bucketName: undefined,
        status: undefined,
      },
      // è¡¨å•参数
      form: {},
      // è¡¨å•校验
      rules: {
        configKey: [
          { required: true, message: "configKey不能为空", trigger: "blur" },
        ],
        accessKey: [
          { required: true, message: "accessKey不能为空", trigger: "blur" },
          {
            min: 2,
            max: 200,
            message: "accessKey长度必须介于 2 å’Œ 100 ä¹‹é—´",
            trigger: "blur",
          },
        ],
        secretKey: [
          { required: true, message: "secretKey不能为空", trigger: "blur" },
          {
            min: 2,
            max: 100,
            message: "secretKey长度必须介于 2 å’Œ 100 ä¹‹é—´",
            trigger: "blur",
          },
        ],
        bucketName: [
          { required: true, message: "bucketName不能为空", trigger: "blur" },
          {
            min: 2,
            max: 100,
            message: "bucketName长度必须介于 2 å’Œ 100 ä¹‹é—´",
            trigger: "blur",
          },
        ],
        endpoint: [
          { required: true, message: "endpoint不能为空", trigger: "blur" },
          {
            min: 2,
            max: 100,
            message: "endpoint名称长度必须介于 2 å’Œ 100 ä¹‹é—´",
            trigger: "blur",
          },
        ],
      },
    };
  },
  created() {
    this.getList();
    this.getDicts("sys_yes_no").then(response => {
      this.isHttpsOptions = response.data;
    });
    this.getDicts("sys_normal_disable").then(response => {
      this.statusOptions = response.data;
    });
    this.configKeyOptions = this.configKeyDatas;
  },
  methods: {
    /** æŸ¥è¯¢äº‘存储配置列表 */
    getList() {
      this.loading = true;
      listOssConfig(this.queryParams).then((response) => {
        this.ossConfigList = response.rows;
        this.total = response.total;
        this.loading = false;
      });
    },
    // å–消按钮
    cancel() {
      this.open = false;
      this.reset();
    },
    // è¡¨å•重置
    reset() {
      this.form = {
        ossConfigId: undefined,
        configKey: undefined,
        accessKey: undefined,
        secretKey: undefined,
        bucketName: undefined,
        prefix: undefined,
        endpoint: undefined,
        isHttps: "N",
        region: undefined,
        status: "1",
        remark: undefined,
      };
      this.resetForm("form");
    },
    /** æœç´¢æŒ‰é’®æ“ä½œ */
    handleQuery() {
      this.queryParams.pageNum = 1;
      this.getList();
    },
    /** é‡ç½®æŒ‰é’®æ“ä½œ */
    resetQuery() {
      this.resetForm("queryForm");
      this.handleQuery();
    },
    // å¤šé€‰æ¡†é€‰ä¸­æ•°æ®
    handleSelectionChange(selection) {
      this.ids = selection.map(item => item.ossConfigId)
      this.single = selection.length!==1
      this.multiple = !selection.length
    },
    /** æ–°å¢žæŒ‰é’®æ“ä½œ */
    handleAdd() {
      this.reset();
      this.open = true;
      this.title = "添加云存储配置";
    },
    /** ä¿®æ”¹æŒ‰é’®æ“ä½œ */
    handleUpdate(row) {
      this.loading = true;
      this.reset();
      const ossConfigId = row.ossConfigId || this.ids;
      getOssConfig(ossConfigId).then((response) => {
        this.loading = false;
        this.form = response.data;
        this.open = true;
        this.title = "修改云存储配置";
      });
    },
    /** æäº¤æŒ‰é’® */
    submitForm() {
      this.$refs["form"].validate(valid => {
        if (valid) {
          this.buttonLoading = true;
          if (this.form.ossConfigId != null) {
            updateOssConfig(this.form).then(response => {
              this.msgSuccess("修改成功");
              this.open = false;
              this.getList();
            }).finally(() => {
              this.buttonLoading = false;
            });
          } else {
            addOssConfig(this.form).then(response => {
              this.msgSuccess("新增成功");
              this.open = false;
              this.getList();
            }).finally(() => {
              this.buttonLoading = false;
            });
          }
        }
      });
    },
    /** åˆ é™¤æŒ‰é’®æ“ä½œ */
    handleDelete(row) {
      const ossConfigIds = row.ossConfigId || this.ids;
      this.$confirm('是否确认删除云存储配置编号为"' + ossConfigIds + '"的数据项?', "警告", {
        confirmButtonText: "确定",
        cancelButtonText: "取消",
        type: "warning"
      }).then(() => {
        this.loading = true;
        return delOssConfig(ossConfigIds);
      }).then(() => {
        this.loading = false;
        this.getList();
        this.msgSuccess("删除成功");
      }).finally(() => {
        this.loading = false;
      });
    },
    // äº‘存储配置状态修改
    handleStatusChange(row) {
      let text = row.status === "0" ? "启用" : "停用";
      this.$confirm(
        '确认要"' + text + '""' + row.configKey + '"配置吗?', "警告", {
          confirmButtonText: "确定",
          cancelButtonText: "取消",
          type: "warning",
      }).then(() => {
        return changeOssConfigStatus(row.ossConfigId, row.status, row.configKey);
      }).then(() => {
        this.getList()
        this.msgSuccess(text + "成功");
      }).catch(() => {
        row.status = row.status === "0" ? "1" : "0";
      })
    }
  }
};
</script>
ruoyi-ui/src/views/system/oss/index.vue
@@ -96,7 +96,25 @@
          v-hasPermi="['system:oss:remove']"
        >删除</el-button>
      </el-col>
      <el-col :span="1.5">
        <el-button
          :type="previewListResource ? 'danger' : 'warning'"
          plain
          size="mini"
          @click="handlePreviewListResource(!previewListResource)"
          v-hasPermi="['system:oss:edit']"
        >预览开关 : {{previewListResource ? "禁用" : "启用"}}</el-button>
      </el-col>
      <el-col :span="1.5">
        <el-button
          type="info"
          plain
          icon="el-icon-s-operation"
          size="mini"
          @click="handleOssConfig"
          v-hasPermi="['system:oss:list']"
        >配置管理</el-button>
      </el-col>
      <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
    </el-row>
@@ -109,12 +127,12 @@
      <el-table-column label="文件展示" align="center" prop="url">
        <template slot-scope="scope">
          <el-image
            v-if="previewListResource && scope.row.fileSuffix.indexOf('png','jpg','jpeg') > 0"
            v-if="previewListResource && checkFileSuffix(scope.row.fileSuffix)"
            style="width: 100px; height: 100px;"
            :src="scope.row.url"
            :preview-src-list="[scope.row.url]"/>
          <span v-text="scope.row.url"
                v-if="scope.row.fileSuffix.indexOf('png','jpg','jpeg') < 0 || !previewListResource"/>
                v-if="!checkFileSuffix(scope.row.fileSuffix) || !previewListResource"/>
        </template>
      </el-table-column>
      <el-table-column label="创建时间" align="center" prop="createTime" width="180">
@@ -169,10 +187,8 @@
</template>
<script>
import { listOss, delOss } from "@/api/system/oss";
import { downLoadOss } from "@/utils/ossdownload";
import { updateConfig } from "@/api/system/config";
import { listOss, delOss, changePreviewListResource } from "@/api/system/oss";
import { downLoadOss } from "@/utils/download";
export default {
  name: "Oss",
@@ -249,6 +265,12 @@
        this.loading = false;
      });
    },
    checkFileSuffix(fileSuffix) {
      let arr = ["png", "jpg", "jpeg"];
      return arr.some(type => {
        return fileSuffix.indexOf(type) > -1;
      });
    },
    // å–消按钮
    cancel() {
      this.open = false;
@@ -277,6 +299,10 @@
      this.ids = selection.map(item => item.ossId)
      this.single = selection.length!==1
      this.multiple = !selection.length
    },
    /** ä»»åŠ¡æ—¥å¿—åˆ—è¡¨æŸ¥è¯¢ */
    handleOssConfig() {
      this.$router.push({ path: '/system/oss-config/index'})
    },
    /** æ–‡ä»¶æŒ‰é’®æ“ä½œ */
    handleFile() {
@@ -308,14 +334,33 @@
          confirmButtonText: "确定",
          cancelButtonText: "取消",
          type: "warning"
        }).then(() => {
          this.loading = true;
          return delOss(ossIds);
        }).then(() => {
          this.loading = false;
          this.getList();
          this.msgSuccess("删除成功");
        }).catch(() => {});
      }).then(() => {
        this.loading = true;
        return delOss(ossIds);
      }).then(() => {
        this.loading = false;
        this.getList();
        this.msgSuccess("删除成功");
      }).finally(() => {
        this.loading = false;
      });
    },
    // é¢„览列表图片状态修改
    handlePreviewListResource(previewListResource) {
      let text = previewListResource ? "启用" : "停用";
      this.$confirm(
        '确认要"' + text + '""预览列表图片"配置吗?', "警告", {
          confirmButtonText: "确定",
          cancelButtonText: "取消",
          type: "warning",
      }).then(() => {
        return changePreviewListResource(previewListResource);
      }).then(() => {
        this.getList()
        this.msgSuccess(text + "成功");
      }).catch(() => {
        this.previewListResource = previewListResource !== true;
      })
    }
  }
};
ruoyi-ui/src/views/system/post/index.vue
@@ -156,7 +156,8 @@
</template>
<script>
import { listPost, getPost, delPost, addPost, updatePost, exportPost } from "@/api/system/post";
import { listPost, getPost, delPost, addPost, updatePost } from "@/api/system/post";
import { downLoadExcel } from "@/utils/download";
export default {
  name: "Post",
@@ -313,18 +314,7 @@
    },
    /** å¯¼å‡ºæŒ‰é’®æ“ä½œ */
    handleExport() {
      const queryParams = this.queryParams;
      this.$confirm('是否确认导出所有岗位数据项?', "警告", {
          confirmButtonText: "确定",
          cancelButtonText: "取消",
          type: "warning"
        }).then(() => {
          this.exportLoading = true;
          return exportPost(queryParams);
        }).then(response => {
          this.download(response.msg);
          this.exportLoading = false;
        }).catch(() => {});
      downLoadExcel('/system/post/export', this.queryParams);
    }
  }
};
ruoyi-ui/src/views/system/role/index.vue
@@ -259,9 +259,10 @@
</template>
<script>
import { listRole, getRole, delRole, addRole, updateRole, exportRole, dataScope, changeRoleStatus } from "@/api/system/role";
import { listRole, getRole, delRole, addRole, updateRole, dataScope, changeRoleStatus } from "@/api/system/role";
import { treeselect as menuTreeselect, roleMenuTreeselect } from "@/api/system/menu";
import { treeselect as deptTreeselect, roleDeptTreeselect } from "@/api/system/dept";
import { downLoadExcel } from "@/utils/download";
export default {
  name: "Role",
@@ -625,18 +626,7 @@
    },
    /** å¯¼å‡ºæŒ‰é’®æ“ä½œ */
    handleExport() {
      const queryParams = this.queryParams;
      this.$confirm('是否确认导出所有角色数据项?', "警告", {
          confirmButtonText: "确定",
          cancelButtonText: "取消",
          type: "warning"
        }).then(() => {
          this.exportLoading = true;
          return exportRole(queryParams);
        }).then(response => {
          this.download(response.msg);
          this.exportLoading = false;
        }).catch(() => {});
      downLoadExcel('/system/role/export', this.queryParams);
    }
  }
};
ruoyi-ui/src/views/system/role/selectUser.vue
@@ -64,7 +64,7 @@
  props: {
    // è§’色编号
    roleId: {
      type: Number
      type: [Number, String]
    }
  },
  data() {
ruoyi-ui/src/views/system/user/index.vue
@@ -346,11 +346,12 @@
</template>
<script>
import { listUser, getUser, delUser, addUser, updateUser, exportUser, resetUserPwd, changeUserStatus, importTemplate } from "@/api/system/user";
import { listUser, getUser, delUser, addUser, updateUser, resetUserPwd, changeUserStatus } from "@/api/system/user";
import { getToken } from "@/utils/auth";
import { treeselect } from "@/api/system/dept";
import Treeselect from "@riophae/vue-treeselect";
import "@riophae/vue-treeselect/dist/vue-treeselect.css";
import { downLoadExcel } from "@/utils/download";
export default {
  name: "User",
@@ -436,7 +437,8 @@
      // è¡¨å•校验
      rules: {
        userName: [
          { required: true, message: "用户名称不能为空", trigger: "blur" }
          { required: true, message: "用户名称不能为空", trigger: "blur" },
          { min: 2, max: 20, message: '用户名称长度必须介于 2 å’Œ 20 ä¹‹é—´', trigger: 'blur' }
        ],
        nickName: [
          { required: true, message: "用户昵称不能为空", trigger: "blur" }
@@ -659,18 +661,7 @@
    },
    /** å¯¼å‡ºæŒ‰é’®æ“ä½œ */
    handleExport() {
      const queryParams = this.queryParams;
      this.$confirm('是否确认导出所有用户数据项?', "警告", {
          confirmButtonText: "确定",
          cancelButtonText: "取消",
          type: "warning"
        }).then(() => {
          this.exportLoading = true;
          return exportUser(queryParams);
        }).then(response => {
          this.download(response.msg);
          this.exportLoading = false;
        }).catch(() => {});
      downLoadExcel('/system/user/export', this.queryParams);
    },
    /** å¯¼å…¥æŒ‰é’®æ“ä½œ */
    handleImport() {
@@ -679,9 +670,7 @@
    },
    /** ä¸‹è½½æ¨¡æ¿æ“ä½œ */
    importTemplate() {
      importTemplate().then(response => {
        this.download(response.msg);
      });
      downLoadExcel('/system/user/importTemplate');
    },
    // æ–‡ä»¶ä¸Šä¼ ä¸­å¤„理
    handleFileUploadProgress(event, file, fileList) {
ruoyi-ui/src/views/system/user/profile/userAvatar.vue
@@ -77,7 +77,8 @@
        autoCrop: true, // æ˜¯å¦é»˜è®¤ç”Ÿæˆæˆªå›¾æ¡†
        autoCropWidth: 200, // é»˜è®¤ç”Ÿæˆæˆªå›¾æ¡†å®½åº¦
        autoCropHeight: 200, // é»˜è®¤ç”Ÿæˆæˆªå›¾æ¡†é«˜åº¦
        fixedBox: true // å›ºå®šæˆªå›¾æ¡†å¤§å° ä¸å…è®¸æ”¹å˜
        fixedBox: true, // å›ºå®šæˆªå›¾æ¡†å¤§å° ä¸å…è®¸æ”¹å˜
        filename: ''
      },
      previews: {}
    };
@@ -116,6 +117,7 @@
        reader.readAsDataURL(file);
        reader.onload = () => {
          this.options.img = reader.result;
          this.options.filename = file.name;
        };
      }
    },
@@ -123,10 +125,11 @@
    uploadImg() {
      this.$refs.cropper.getCropBlob(data => {
        let formData = new FormData();
        formData.append("avatarfile", data);
        console.log(this.options.filename)
        formData.append("avatarfile", data, this.options.filename);
        uploadAvatar(formData).then(response => {
          this.open = false;
          this.options.img = process.env.VUE_APP_BASE_API + response.data.imgUrl;
          this.options.img = response.data.imgUrl;
          store.commit('SET_AVATAR', this.options.img);
          this.msgSuccess("修改成功");
          this.visible = false;
ruoyi-ui/src/views/tool/gen/index.vue
@@ -180,7 +180,7 @@
<script>
import { listTable, previewTable, delTable, genCode, synchDb } from "@/api/tool/gen";
import importTable from "./importTable";
import { downLoadZip } from "@/utils/zipdownload";
import { downLoadZip } from "@/utils/download";
import hljs from "highlight.js/lib/highlight";
import "highlight.js/styles/github-gist.css";
hljs.registerLanguage("java", require("highlight.js/lib/languages/java"));
sql/oss.sql
@@ -16,7 +16,30 @@
    primary key (oss_id)
) engine=innodb comment ='OSS云存储表';
insert into sys_config values(10, 'OSS云存储服务商',       'sys.oss.cloudStorageService',      'minio',          'Y', 'admin', sysdate(), '', null, 'OSS云存储服务商(qiniu:七牛云, aliyun:阿里云, qcloud:腾讯云, minio: Minio)');
-- ----------------------------
-- OSS云存储动态配置表
-- ----------------------------
drop table if exists sys_oss_config;
create table sys_oss_config (
    oss_config_id   bigint(20)   not null auto_increment comment '主建',
    config_key      varchar(255)  not null default ''     comment '配置key',
    access_key      varchar(255)            default ''    comment 'accessKey',
    secret_key      varchar(255)            default ''    comment '秘钥',
    bucket_name     varchar(255)            default ''    comment '桶名称',
    prefix           varchar(255)           default ''     comment '前缀',
    endpoint         varchar(255)           default ''     comment '访问站点',
    is_https         char(1)                default 'N'    comment '是否https(Y=是,N=否)',
    region           varchar(255)           default ''     comment '域',
    status           char(1)                default '1'    comment '状态(0=正常,1=停用)',
    ext1             varchar(255)           default ''      comment '扩展字段',
    create_by       varchar(64)             default ''     comment '创建者',
    create_time     datetime                default null   comment '创建时间',
    update_by       varchar(64)             default ''      comment '更新者',
    update_time     datetime                default null    comment '更新时间',
    remark           varchar(500)           default null    comment '备注',
    primary key (oss_config_id)
) engine=innodb comment='云存储配置表';
insert into sys_config values(11, 'OSS预览列表资源开关',   'sys.oss.previewListResource',      'true',           'Y', 'admin', sysdate(), '', null, 'true:开启, false:关闭');
insert into sys_menu values('118',  '文件管理', '1',   '10', 'oss',     'system/oss/index',      1, 0, 'C', '0', '0', 'system:oss:list',      'upload',       'admin', sysdate(), '', null, '文件管理菜单');
@@ -25,3 +48,10 @@
insert into sys_menu values('1601', '文件上传', '118', '2', '#', '', 1, 0, 'F', '0', '0', 'system:oss:upload',       '#', 'admin', sysdate(), '', null, '');
insert into sys_menu values('1602', '文件下载', '118', '3', '#', '', 1, 0, 'F', '0', '0', 'system:oss:download',     '#', 'admin', sysdate(), '', null, '');
insert into sys_menu values('1603', '文件删除', '118', '4', '#', '', 1, 0, 'F', '0', '0', 'system:oss:remove',       '#', 'admin', sysdate(), '', null, '');
insert into sys_menu values('1604', '配置添加', '118', '5', '#', '', 1, 0, 'F', '0', '0', 'system:oss:add',          '#', 'admin', sysdate(), '', null, '');
insert into sys_menu values('1605', '配置编辑', '118', '6', '#', '', 1, 0, 'F', '0', '0', 'system:oss:edit',         '#', 'admin', sysdate(), '', null, '');
insert into sys_oss_config values (1, 'minio',  'ruoyi',            'ruoyi123',        'ruoyi',             '', 'http://localhost:9000',                'N', '',            '0', '', 'admin', sysdate(), 'admin', sysdate(), NULL);
insert into sys_oss_config values (2, 'qiniu',  'XXXXXXXXXXXXXXX',  'XXXXXXXXXXXXXXX', 'ruoyi',             '', 'http://XXX.XXXX.com',                  'N', 'z0',          '1', '', 'admin', sysdate(), 'admin', sysdate(), NULL);
insert into sys_oss_config values (3, 'aliyun', 'XXXXXXXXXXXXXXX',  'XXXXXXXXXXXXXXX', 'ruoyi',             '', 'http://oss-cn-beijing.aliyuncs.com',   'N', '',            '1', '', 'admin', sysdate(), 'admin', sysdate(), NULL);
insert into sys_oss_config values (4, 'qcloud', 'XXXXXXXXXXXXXXX',  'XXXXXXXXXXXXXXX', 'ruoyi-1250000000',  '', 'http://cos.ap-beijing.myqcloud.com',   'N', 'ap-beijing',  '1', '', 'admin', sysdate(), 'admin', sysdate(), NULL);
sql/ry_20210731.sql
ÎļþÃû´Ó sql/ry_20210713.sql ÐÞ¸Ä
@@ -537,10 +537,11 @@
  primary key (config_id)
) engine=innodb auto_increment=100 comment = '参数配置表';
insert into sys_config values(1, '主框架页-默认皮肤样式名称', 'sys.index.skinName',            'skin-blue',     'Y', 'admin', sysdate(), '', null, '蓝色 skin-blue、绿色 skin-green、紫色 skin-purple、红色 skin-red、黄色 skin-yellow' );
insert into sys_config values(2, '用户管理-账号初始密码',     'sys.user.initPassword',         '123456',        'Y', 'admin', sysdate(), '', null, '初始化密码 123456' );
insert into sys_config values(3, '主框架页-侧边栏主题',       'sys.index.sideTheme',           'theme-dark',    'Y', 'admin', sysdate(), '', null, '深色主题theme-dark,浅色主题theme-light' );
insert into sys_config values(4, '账号自助-验证码开关',       'sys.account.captchaOnOff',      'true',          'Y', 'admin', sysdate(), '', null, '是否开启登录验证码功能(true开启,false关闭)');
insert into sys_config values(1, '主框架页-默认皮肤样式名称',     'sys.index.skinName',            'skin-blue',     'Y', 'admin', sysdate(), '', null, '蓝色 skin-blue、绿色 skin-green、紫色 skin-purple、红色 skin-red、黄色 skin-yellow' );
insert into sys_config values(2, '用户管理-账号初始密码',         'sys.user.initPassword',         '123456',        'Y', 'admin', sysdate(), '', null, '初始化密码 123456' );
insert into sys_config values(3, '主框架页-侧边栏主题',           'sys.index.sideTheme',           'theme-dark',    'Y', 'admin', sysdate(), '', null, '深色主题theme-dark,浅色主题theme-light' );
insert into sys_config values(4, '账号自助-验证码开关',           'sys.account.captchaOnOff',      'true',          'Y', 'admin', sysdate(), '', null, '是否开启验证码功能(true开启,false关闭)');
insert into sys_config values(5, '账号自助-是否开启用户注册功能', 'sys.account.registerUser',      'false',         'Y', 'admin', sysdate(), '', null, '是否开启注册用户功能(true开启,false关闭)');
-- ----------------------------