From 8bb3fa53fbb2d2d8d0627fef49aeba5588ca667d Mon Sep 17 00:00:00 2001 From: wyd <475583447@qq.com> Date: Mon, 22 Apr 2024 08:45:46 +0800 Subject: [PATCH] first commit --- .idea/.gitignore | 10 + .idea/compiler.xml | 19 + .idea/encodings.xml | 6 + .idea/inspectionProfiles/Project_Default.xml | 36 + .idea/intellij-javadocs-4.0.1.xml | 204 ++ .idea/jarRepositories.xml | 25 + .idea/misc.xml | 15 + .idea/runConfigurations.xml | 10 + .idea/uiDesigner.xml | 124 ++ .idea/vcs.xml | 6 + README.md | 96 + bin/clean.bat | 12 + bin/package.bat | 12 + bin/run-tomcat.bat | 14 + doc/若依环境使用手册.docx | Bin 0 -> 428430 bytes pom.xml | 286 +++ ry.bat | 67 + ry.sh | 86 + sql/quartz.sql | 174 ++ sql/ry_20231130.sql | 700 +++++++ src/main/java/com/ruoyi/RuoYiApplication.java | 49 + .../com/ruoyi/RuoYiServletInitializer.java | 18 + .../ruoyi/common/constant/CacheConstants.java | 44 + .../com/ruoyi/common/constant/Constants.java | 173 ++ .../ruoyi/common/constant/GenConstants.java | 117 ++ .../com/ruoyi/common/constant/HttpStatus.java | 94 + .../common/constant/ScheduleConstants.java | 50 + .../ruoyi/common/constant/UserConstants.java | 78 + .../ruoyi/common/core/text/CharsetKit.java | 86 + .../com/ruoyi/common/core/text/Convert.java | 1006 +++++++++ .../ruoyi/common/core/text/StrFormatter.java | 92 + .../com/ruoyi/common/enums/HttpMethod.java | 36 + .../com/ruoyi/common/enums/UserStatus.java | 30 + .../common/exception/DemoModeException.java | 15 + .../common/exception/GlobalException.java | 58 + .../common/exception/ServiceException.java | 74 + .../ruoyi/common/exception/UtilException.java | 26 + .../common/exception/base/BaseException.java | 97 + .../common/exception/file/FileException.java | 19 + .../FileNameLengthLimitExceededException.java | 16 + .../file/FileSizeLimitExceededException.java | 16 + .../exception/file/FileUploadException.java | 61 + .../file/InvalidExtensionException.java | 80 + .../common/exception/job/TaskException.java | 34 + .../exception/user/BlackListException.java | 16 + .../exception/user/CaptchaException.java | 16 + .../user/CaptchaExpireException.java | 16 + .../common/exception/user/UserException.java | 18 + .../user/UserNotExistsException.java | 16 + .../user/UserPasswordNotMatchException.java | 16 + ...UserPasswordRetryLimitExceedException.java | 16 + .../filter/PropertyPreExcludeFilter.java | 24 + .../ruoyi/common/filter/RepeatableFilter.java | 52 + .../filter/RepeatedlyRequestWrapper.java | 76 + .../com/ruoyi/common/filter/XssFilter.java | 75 + .../filter/XssHttpServletRequestWrapper.java | 111 + .../java/com/ruoyi/common/utils/Arith.java | 114 ++ .../com/ruoyi/common/utils/DateUtils.java | 191 ++ .../com/ruoyi/common/utils/DictUtils.java | 186 ++ .../com/ruoyi/common/utils/ExceptionUtil.java | 39 + .../java/com/ruoyi/common/utils/LogUtils.java | 18 + .../com/ruoyi/common/utils/MessageUtils.java | 26 + .../com/ruoyi/common/utils/PageUtils.java | 35 + .../com/ruoyi/common/utils/SecurityUtils.java | 176 ++ .../com/ruoyi/common/utils/ServletUtils.java | 218 ++ .../com/ruoyi/common/utils/StringUtils.java | 638 ++++++ .../java/com/ruoyi/common/utils/Threads.java | 99 + .../ruoyi/common/utils/bean/BeanUtils.java | 110 + .../common/utils/bean/BeanValidators.java | 24 + .../common/utils/file/FileTypeUtils.java | 76 + .../common/utils/file/FileUploadUtils.java | 232 +++ .../ruoyi/common/utils/file/FileUtils.java | 291 +++ .../ruoyi/common/utils/file/ImageUtils.java | 98 + .../common/utils/file/MimeTypeUtils.java | 59 + .../ruoyi/common/utils/html/EscapeUtil.java | 167 ++ .../ruoyi/common/utils/html/HTMLFilter.java | 570 ++++++ .../ruoyi/common/utils/http/HttpHelper.java | 55 + .../ruoyi/common/utils/http/HttpUtils.java | 274 +++ .../ruoyi/common/utils/ip/AddressUtils.java | 56 + .../com/ruoyi/common/utils/ip/IpUtils.java | 382 ++++ .../common/utils/job/AbstractQuartzJob.java | 107 + .../com/ruoyi/common/utils/job/CronUtils.java | 63 + .../ruoyi/common/utils/job/JobInvokeUtil.java | 182 ++ .../QuartzDisallowConcurrentExecution.java | 21 + .../common/utils/job/QuartzJobExecution.java | 19 + .../ruoyi/common/utils/job/ScheduleUtils.java | 141 ++ .../common/utils/poi/ExcelHandlerAdapter.java | 24 + .../com/ruoyi/common/utils/poi/ExcelUtil.java | 1796 +++++++++++++++++ .../common/utils/reflect/ReflectUtils.java | 410 ++++ .../com/ruoyi/common/utils/sign/Base64.java | 291 +++ .../com/ruoyi/common/utils/sign/Md5Utils.java | 67 + .../common/utils/spring/SpringUtils.java | 158 ++ .../com/ruoyi/common/utils/sql/SqlUtil.java | 70 + .../com/ruoyi/common/utils/uuid/IdUtils.java | 49 + .../java/com/ruoyi/common/utils/uuid/Seq.java | 86 + .../com/ruoyi/common/utils/uuid/UUID.java | 484 +++++ src/main/java/com/ruoyi/common/xss/Xss.java | 27 + .../com/ruoyi/common/xss/XssValidator.java | 39 + .../framework/aspectj/DataScopeAspect.java | 174 ++ .../framework/aspectj/DataSourceAspect.java | 72 + .../ruoyi/framework/aspectj/LogAspect.java | 255 +++ .../framework/aspectj/RateLimiterAspect.java | 89 + .../aspectj/lang/annotation/Anonymous.java | 19 + .../aspectj/lang/annotation/DataScope.java | 33 + .../aspectj/lang/annotation/DataSource.java | 28 + .../aspectj/lang/annotation/Excel.java | 187 ++ .../aspectj/lang/annotation/Excels.java | 18 + .../aspectj/lang/annotation/Log.java | 51 + .../aspectj/lang/annotation/RateLimiter.java | 40 + .../aspectj/lang/enums/BusinessStatus.java | 20 + .../aspectj/lang/enums/BusinessType.java | 59 + .../aspectj/lang/enums/DataSourceType.java | 19 + .../aspectj/lang/enums/LimitType.java | 20 + .../aspectj/lang/enums/OperatorType.java | 24 + .../framework/config/ApplicationConfig.java | 30 + .../ruoyi/framework/config/CaptchaConfig.java | 83 + .../ruoyi/framework/config/DruidConfig.java | 126 ++ .../config/FastJson2JsonRedisSerializer.java | 52 + .../ruoyi/framework/config/FilterConfig.java | 58 + .../com/ruoyi/framework/config/GenConfig.java | 66 + .../ruoyi/framework/config/I18nConfig.java | 43 + .../framework/config/KaptchaTextCreator.java | 68 + .../framework/config/MybatisPlusConfig.java | 62 + .../ruoyi/framework/config/RedisConfig.java | 69 + .../framework/config/ResourcesConfig.java | 72 + .../ruoyi/framework/config/RuoYiConfig.java | 111 + .../framework/config/ScheduleConfig.java | 57 + .../framework/config/SecurityConfig.java | 148 ++ .../ruoyi/framework/config/ServerConfig.java | 32 + .../ruoyi/framework/config/SwaggerConfig.java | 126 ++ .../framework/config/ThreadPoolConfig.java | 63 + .../config/properties/DruidProperties.java | 89 + .../properties/PermitAllUrlProperties.java | 73 + .../datasource/DynamicDataSource.java | 26 + .../DynamicDataSourceContextHolder.java | 45 + .../interceptor/RepeatSubmitInterceptor.java | 56 + .../interceptor/annotation/RepeatSubmit.java | 31 + .../impl/SameUrlDataInterceptor.java | 110 + .../ruoyi/framework/manager/AsyncManager.java | 55 + .../framework/manager/ShutdownManager.java | 39 + .../manager/factory/AsyncFactory.java | 102 + .../com/ruoyi/framework/redis/RedisCache.java | 268 +++ .../ruoyi/framework/security/LoginBody.java | 69 + .../ruoyi/framework/security/LoginUser.java | 266 +++ .../framework/security/RegisterBody.java | 11 + .../context/AuthenticationContextHolder.java | 28 + .../context/PermissionContextHolder.java | 27 + .../filter/JwtAuthenticationTokenFilter.java | 44 + .../handle/AuthenticationEntryPointImpl.java | 34 + .../handle/LogoutSuccessHandlerImpl.java | 52 + .../security/service/PermissionService.java | 159 ++ .../security/service/SysLoginService.java | 181 ++ .../security/service/SysPasswordService.java | 86 + .../service/SysPermissionService.java | 83 + .../security/service/SysRegisterService.java | 115 ++ .../security/service/TokenService.java | 231 +++ .../service/UserDetailsServiceImpl.java | 65 + .../java/com/ruoyi/framework/task/RyTask.java | 28 + .../web/controller/BaseController.java | 194 ++ .../framework/web/domain/AjaxResult.java | 216 ++ .../framework/web/domain/BaseEntity.java | 118 ++ .../com/ruoyi/framework/web/domain/R.java | 115 ++ .../ruoyi/framework/web/domain/Server.java | 240 +++ .../framework/web/domain/TreeEntity.java | 79 + .../framework/web/domain/TreeSelect.java | 77 + .../framework/web/domain/server/Cpu.java | 101 + .../framework/web/domain/server/Jvm.java | 130 ++ .../framework/web/domain/server/Mem.java | 61 + .../framework/web/domain/server/Sys.java | 84 + .../framework/web/domain/server/SysFile.java | 114 ++ .../web/exception/GlobalExceptionHandler.java | 138 ++ .../ruoyi/framework/web/page/PageDomain.java | 101 + .../framework/web/page/TableDataInfo.java | 85 + .../framework/web/page/TableSupport.java | 56 + .../controller/AboutPartThreeController.java | 81 + .../project/about/domain/AboutPartThree.java | 69 + .../about/mapper/AboutPartThreeMapper.java | 14 + .../about/service/IAboutPartThreeService.java | 15 + .../impl/AboutPartThreeServiceImpl.java | 17 + .../project/common/CaptchaController.java | 98 + .../project/common/CommonController.java | 163 ++ .../monitor/controller/CacheController.java | 120 ++ .../monitor/controller/ServerController.java | 27 + .../monitor/controller/SysJobController.java | 185 ++ .../controller/SysJobLogController.java | 92 + .../controller/SysLogininforController.java | 82 + .../controller/SysOperlogController.java | 69 + .../controller/SysUserOnlineController.java | 83 + .../project/monitor/domain/SysCache.java | 81 + .../ruoyi/project/monitor/domain/SysJob.java | 171 ++ .../project/monitor/domain/SysJobLog.java | 155 ++ .../project/monitor/domain/SysLogininfor.java | 144 ++ .../project/monitor/domain/SysOperLog.java | 269 +++ .../project/monitor/domain/SysUserOnline.java | 113 ++ .../monitor/mapper/SysJobLogMapper.java | 64 + .../project/monitor/mapper/SysJobMapper.java | 67 + .../monitor/mapper/SysLogininforMapper.java | 42 + .../monitor/mapper/SysOperLogMapper.java | 48 + .../monitor/service/ISysJobLogService.java | 56 + .../monitor/service/ISysJobService.java | 102 + .../service/ISysLogininforService.java | 40 + .../monitor/service/ISysOperLogService.java | 48 + .../service/impl/SysJobLogServiceImpl.java | 87 + .../service/impl/SysJobServiceImpl.java | 261 +++ .../impl/SysLogininforServiceImpl.java | 65 + .../service/impl/SysOperLogServiceImpl.java | 76 + .../controller/SysConfigController.java | 133 ++ .../system/controller/SysDeptController.java | 132 ++ .../controller/SysDictDataController.java | 121 ++ .../controller/SysDictTypeController.java | 131 ++ .../system/controller/SysIndexController.java | 29 + .../system/controller/SysLoginController.java | 86 + .../system/controller/SysMenuController.java | 142 ++ .../controller/SysNoticeController.java | 91 + .../system/controller/SysPostController.java | 129 ++ .../controller/SysProfileController.java | 137 ++ .../controller/SysRegisterController.java | 38 + .../system/controller/SysRoleController.java | 262 +++ .../system/controller/SysUserController.java | 251 +++ .../project/system/domain/SysConfig.java | 111 + .../ruoyi/project/system/domain/SysDept.java | 203 ++ .../project/system/domain/SysDictData.java | 176 ++ .../project/system/domain/SysDictType.java | 96 + .../ruoyi/project/system/domain/SysMenu.java | 259 +++ .../project/system/domain/SysNotice.java | 102 + .../ruoyi/project/system/domain/SysPost.java | 124 ++ .../ruoyi/project/system/domain/SysRole.java | 241 +++ .../project/system/domain/SysRoleDept.java | 46 + .../project/system/domain/SysRoleMenu.java | 46 + .../ruoyi/project/system/domain/SysUser.java | 324 +++ .../project/system/domain/SysUserPost.java | 46 + .../project/system/domain/SysUserRole.java | 46 + .../project/system/domain/vo/MetaVo.java | 106 + .../project/system/domain/vo/RouterVo.java | 148 ++ .../system/mapper/SysConfigMapper.java | 76 + .../project/system/mapper/SysDeptMapper.java | 118 ++ .../system/mapper/SysDictDataMapper.java | 95 + .../system/mapper/SysDictTypeMapper.java | 83 + .../project/system/mapper/SysMenuMapper.java | 125 ++ .../system/mapper/SysNoticeMapper.java | 60 + .../project/system/mapper/SysPostMapper.java | 99 + .../system/mapper/SysRoleDeptMapper.java | 44 + .../project/system/mapper/SysRoleMapper.java | 107 + .../system/mapper/SysRoleMenuMapper.java | 44 + .../project/system/mapper/SysUserMapper.java | 129 ++ .../system/mapper/SysUserPostMapper.java | 44 + .../system/mapper/SysUserRoleMapper.java | 62 + .../system/service/ISysConfigService.java | 89 + .../system/service/ISysDeptService.java | 124 ++ .../system/service/ISysDictDataService.java | 60 + .../system/service/ISysDictTypeService.java | 98 + .../system/service/ISysMenuService.java | 144 ++ .../system/service/ISysNoticeService.java | 60 + .../system/service/ISysPostService.java | 99 + .../system/service/ISysRoleService.java | 173 ++ .../system/service/ISysUserOnlineService.java | 48 + .../system/service/ISysUserService.java | 206 ++ .../service/impl/SysConfigServiceImpl.java | 229 +++ .../service/impl/SysDeptServiceImpl.java | 338 ++++ .../service/impl/SysDictDataServiceImpl.java | 111 + .../service/impl/SysDictTypeServiceImpl.java | 223 ++ .../service/impl/SysMenuServiceImpl.java | 531 +++++ .../service/impl/SysNoticeServiceImpl.java | 92 + .../service/impl/SysPostServiceImpl.java | 178 ++ .../service/impl/SysRoleServiceImpl.java | 424 ++++ .../impl/SysUserOnlineServiceImpl.java | 96 + .../service/impl/SysUserServiceImpl.java | 544 +++++ .../tool/gen/controller/GenController.java | 259 +++ .../project/tool/gen/domain/GenTable.java | 385 ++++ .../tool/gen/domain/GenTableColumn.java | 373 ++++ .../tool/gen/mapper/GenTableColumnMapper.java | 60 + .../tool/gen/mapper/GenTableMapper.java | 91 + .../service/GenTableColumnServiceImpl.java | 68 + .../tool/gen/service/GenTableServiceImpl.java | 531 +++++ .../gen/service/IGenTableColumnService.java | 44 + .../tool/gen/service/IGenTableService.java | 130 ++ .../ruoyi/project/tool/gen/util/GenUtils.java | 257 +++ .../tool/gen/util/VelocityInitializer.java | 34 + .../project/tool/gen/util/VelocityUtils.java | 408 ++++ .../project/tool/swagger/TestController.java | 183 ++ .../META-INF/spring-devtools.properties | 1 + src/main/resources/application-druid.yml | 61 + src/main/resources/application.yml | 151 ++ src/main/resources/banner.txt | 18 + src/main/resources/i18n/messages.properties | 38 + src/main/resources/logback.xml | 93 + .../PartThree/AboutPartThreeMapper.xml | 6 + .../mybatis/monitor/SysLogininforMapper.xml | 57 + .../mybatis/monitor/SysOperLogMapper.xml | 87 + src/main/resources/mybatis/mybatis-config.xml | 20 + .../mybatis/system/SysConfigMapper.xml | 117 ++ .../mybatis/system/SysDeptMapper.xml | 159 ++ .../mybatis/system/SysDictDataMapper.xml | 124 ++ .../mybatis/system/SysDictTypeMapper.xml | 105 + .../mybatis/system/SysJobLogMapper.xml | 94 + .../resources/mybatis/system/SysJobMapper.xml | 111 + .../mybatis/system/SysMenuMapper.xml | 202 ++ .../mybatis/system/SysNoticeMapper.xml | 89 + .../mybatis/system/SysPostMapper.xml | 122 ++ .../mybatis/system/SysRoleDeptMapper.xml | 34 + .../mybatis/system/SysRoleMapper.xml | 152 ++ .../mybatis/system/SysRoleMenuMapper.xml | 34 + .../mybatis/system/SysUserMapper.xml | 221 ++ .../mybatis/system/SysUserPostMapper.xml | 34 + .../mybatis/system/SysUserRoleMapper.xml | 44 + .../mybatis/tool/GenTableColumnMapper.xml | 127 ++ .../resources/mybatis/tool/GenTableMapper.xml | 210 ++ src/main/resources/vm/java/controller.java.vm | 84 + src/main/resources/vm/java/domain.java.vm | 65 + src/main/resources/vm/java/mapper.java.vm | 14 + src/main/resources/vm/java/service.java.vm | 15 + .../resources/vm/java/serviceImpl.java.vm | 17 + src/main/resources/vm/java/sub-domain.java.vm | 73 + src/main/resources/vm/js/api.js.vm | 44 + src/main/resources/vm/sql/sql.vm | 22 + src/main/resources/vm/vue/index-tree.vue.vm | 505 +++++ src/main/resources/vm/vue/index.vue.vm | 602 ++++++ .../resources/vm/vue/v3/index-tree.vue.vm | 474 +++++ src/main/resources/vm/vue/v3/index.vue.vm | 590 ++++++ src/main/resources/vm/xml/mapper.xml.vm | 6 + .../META-INF/spring-devtools.properties | 1 + target/classes/application-druid.yml | 61 + target/classes/application.yml | 151 ++ target/classes/banner.txt | 18 + .../classes/com/ruoyi/RuoYiApplication.class | Bin 0 -> 4393 bytes .../com/ruoyi/RuoYiServletInitializer.class | Bin 0 -> 910 bytes .../common/constant/CacheConstants.class | Bin 0 -> 718 bytes .../com/ruoyi/common/constant/Constants.class | Bin 0 -> 2248 bytes .../ruoyi/common/constant/GenConstants.class | Bin 0 -> 2753 bytes .../ruoyi/common/constant/HttpStatus.class | Bin 0 -> 897 bytes .../constant/ScheduleConstants$Status.class | Bin 0 -> 1527 bytes .../common/constant/ScheduleConstants.class | Bin 0 -> 719 bytes .../ruoyi/common/constant/UserConstants.class | Bin 0 -> 1159 bytes .../ruoyi/common/core/text/CharsetKit.class | Bin 0 -> 1891 bytes .../com/ruoyi/common/core/text/Convert.class | Bin 0 -> 14722 bytes .../ruoyi/common/core/text/StrFormatter.class | Bin 0 -> 1890 bytes .../com/ruoyi/common/enums/HttpMethod.class | Bin 0 -> 2271 bytes .../com/ruoyi/common/enums/UserStatus.class | Bin 0 -> 1578 bytes .../common/exception/DemoModeException.class | Bin 0 -> 408 bytes .../common/exception/GlobalException.class | Bin 0 -> 1075 bytes .../common/exception/ServiceException.class | Bin 0 -> 1382 bytes .../common/exception/UtilException.class | Bin 0 -> 872 bytes .../common/exception/base/BaseException.class | Bin 0 -> 2090 bytes .../common/exception/file/FileException.class | Bin 0 -> 667 bytes ...FileNameLengthLimitExceededException.class | Bin 0 -> 744 bytes .../file/FileSizeLimitExceededException.class | Bin 0 -> 701 bytes .../exception/file/FileUploadException.class | Bin 0 -> 1448 bytes ...ption$InvalidFlashExtensionException.class | Bin 0 -> 810 bytes ...ption$InvalidImageExtensionException.class | Bin 0 -> 810 bytes ...ption$InvalidMediaExtensionException.class | Bin 0 -> 810 bytes ...ption$InvalidVideoExtensionException.class | Bin 0 -> 810 bytes .../file/InvalidExtensionException.class | Bin 0 -> 1943 bytes .../exception/job/TaskException$Code.class | Bin 0 -> 1516 bytes .../common/exception/job/TaskException.class | Bin 0 -> 1153 bytes .../exception/user/BlackListException.class | Bin 0 -> 505 bytes .../exception/user/CaptchaException.class | Bin 0 -> 505 bytes .../user/CaptchaExpireException.class | Bin 0 -> 524 bytes .../common/exception/user/UserException.class | Bin 0 -> 667 bytes .../user/UserNotExistsException.class | Bin 0 -> 519 bytes .../user/UserPasswordNotMatchException.class | Bin 0 -> 548 bytes ...serPasswordRetryLimitExceedException.class | Bin 0 -> 777 bytes .../filter/PropertyPreExcludeFilter.class | Bin 0 -> 845 bytes .../common/filter/RepeatableFilter.class | Bin 0 -> 1751 bytes .../filter/RepeatedlyRequestWrapper$1.class | Bin 0 -> 1478 bytes .../filter/RepeatedlyRequestWrapper.class | Bin 0 -> 1979 bytes .../com/ruoyi/common/filter/XssFilter.class | Bin 0 -> 2917 bytes .../XssHttpServletRequestWrapper$1.class | Bin 0 -> 1468 bytes .../filter/XssHttpServletRequestWrapper.class | Bin 0 -> 2357 bytes .../com/ruoyi/common/utils/Arith.class | Bin 0 -> 2054 bytes .../com/ruoyi/common/utils/DateUtils.class | Bin 0 -> 4946 bytes .../com/ruoyi/common/utils/DictUtils.class | Bin 0 -> 4700 bytes .../ruoyi/common/utils/ExceptionUtil.class | Bin 0 -> 1394 bytes .../com/ruoyi/common/utils/LogUtils.class | Bin 0 -> 698 bytes .../com/ruoyi/common/utils/MessageUtils.class | Bin 0 -> 989 bytes .../com/ruoyi/common/utils/PageUtils.class | Bin 0 -> 1394 bytes .../ruoyi/common/utils/SecurityUtils.class | Bin 0 -> 5768 bytes .../com/ruoyi/common/utils/ServletUtils.class | Bin 0 -> 6153 bytes .../com/ruoyi/common/utils/StringUtils.class | Bin 0 -> 9696 bytes .../com/ruoyi/common/utils/Threads.class | Bin 0 -> 2621 bytes .../ruoyi/common/utils/bean/BeanUtils.class | Bin 0 -> 2818 bytes .../common/utils/bean/BeanValidators.class | Bin 0 -> 1265 bytes .../common/utils/file/FileTypeUtils.class | Bin 0 -> 1426 bytes .../common/utils/file/FileUploadUtils.class | Bin 0 -> 6054 bytes .../ruoyi/common/utils/file/FileUtils.class | Bin 0 -> 6540 bytes .../ruoyi/common/utils/file/ImageUtils.class | Bin 0 -> 2945 bytes .../common/utils/file/MimeTypeUtils.class | Bin 0 -> 1923 bytes .../ruoyi/common/utils/html/EscapeUtil.class | Bin 0 -> 3066 bytes .../ruoyi/common/utils/html/HTMLFilter.class | Bin 0 -> 13644 bytes .../ruoyi/common/utils/http/HttpHelper.class | Bin 0 -> 2522 bytes .../ruoyi/common/utils/http/HttpUtils$1.class | Bin 0 -> 231 bytes .../HttpUtils$TrustAnyHostnameVerifier.class | Bin 0 -> 964 bytes .../http/HttpUtils$TrustAnyTrustManager.class | Bin 0 -> 1260 bytes .../ruoyi/common/utils/http/HttpUtils.class | Bin 0 -> 8863 bytes .../ruoyi/common/utils/ip/AddressUtils.class | Bin 0 -> 2228 bytes .../com/ruoyi/common/utils/ip/IpUtils.class | Bin 0 -> 6874 bytes .../common/utils/job/AbstractQuartzJob.class | Bin 0 -> 4039 bytes .../ruoyi/common/utils/job/CronUtils.class | Bin 0 -> 1346 bytes .../common/utils/job/JobInvokeUtil.class | Bin 0 -> 5818 bytes .../QuartzDisallowConcurrentExecution.class | Bin 0 -> 963 bytes .../common/utils/job/QuartzJobExecution.class | Bin 0 -> 835 bytes .../common/utils/job/ScheduleUtils.class | Bin 0 -> 6554 bytes .../utils/poi/ExcelHandlerAdapter.class | Bin 0 -> 364 bytes .../ruoyi/common/utils/poi/ExcelUtil.class | Bin 0 -> 49183 bytes .../common/utils/reflect/ReflectUtils.class | Bin 0 -> 11128 bytes .../com/ruoyi/common/utils/sign/Base64.class | Bin 0 -> 4588 bytes .../ruoyi/common/utils/sign/Md5Utils.class | Bin 0 -> 2168 bytes .../common/utils/spring/SpringUtils.class | Bin 0 -> 3562 bytes .../com/ruoyi/common/utils/sql/SqlUtil.class | Bin 0 -> 1886 bytes .../com/ruoyi/common/utils/uuid/IdUtils.class | Bin 0 -> 713 bytes .../com/ruoyi/common/utils/uuid/Seq.class | Bin 0 -> 1992 bytes .../ruoyi/common/utils/uuid/UUID$Holder.class | Bin 0 -> 569 bytes .../com/ruoyi/common/utils/uuid/UUID.class | Bin 0 -> 6564 bytes target/classes/com/ruoyi/common/xss/Xss.class | Bin 0 -> 807 bytes .../com/ruoyi/common/xss/XssValidator.class | Bin 0 -> 1905 bytes .../framework/aspectj/DataScopeAspect.class | Bin 0 -> 5560 bytes .../framework/aspectj/DataSourceAspect.class | Bin 0 -> 2924 bytes .../ruoyi/framework/aspectj/LogAspect.class | Bin 0 -> 10236 bytes .../framework/aspectj/RateLimiterAspect.class | Bin 0 -> 5121 bytes .../aspectj/lang/annotation/Anonymous.class | Bin 0 -> 467 bytes .../aspectj/lang/annotation/DataScope.class | Bin 0 -> 589 bytes .../aspectj/lang/annotation/DataSource.class | Bin 0 -> 674 bytes .../lang/annotation/Excel$ColumnType.class | Bin 0 -> 1606 bytes .../aspectj/lang/annotation/Excel$Type.class | Bin 0 -> 1513 bytes .../aspectj/lang/annotation/Excel.class | Bin 0 -> 2135 bytes .../aspectj/lang/annotation/Excels.class | Bin 0 -> 473 bytes .../aspectj/lang/annotation/Log.class | Bin 0 -> 991 bytes .../aspectj/lang/annotation/RateLimiter.class | Bin 0 -> 753 bytes .../aspectj/lang/enums/BusinessStatus.class | Bin 0 -> 1215 bytes .../aspectj/lang/enums/BusinessType.class | Bin 0 -> 1632 bytes .../aspectj/lang/enums/DataSourceType.class | Bin 0 -> 1215 bytes .../aspectj/lang/enums/LimitType.class | Bin 0 -> 1178 bytes .../aspectj/lang/enums/OperatorType.class | Bin 0 -> 1254 bytes .../framework/config/ApplicationConfig.class | Bin 0 -> 1909 bytes .../framework/config/CaptchaConfig.class | Bin 0 -> 2435 bytes .../framework/config/DruidConfig$1.class | Bin 0 -> 2167 bytes .../ruoyi/framework/config/DruidConfig.class | Bin 0 -> 4887 bytes .../config/FastJson2JsonRedisSerializer.class | Bin 0 -> 2811 bytes .../ruoyi/framework/config/FilterConfig.class | Bin 0 -> 2386 bytes .../ruoyi/framework/config/GenConfig.class | Bin 0 -> 1358 bytes .../ruoyi/framework/config/I18nConfig.class | Bin 0 -> 1911 bytes .../framework/config/KaptchaTextCreator.class | Bin 0 -> 1721 bytes .../framework/config/MybatisPlusConfig.class | Bin 0 -> 2330 bytes .../ruoyi/framework/config/RedisConfig.class | Bin 0 -> 3130 bytes .../framework/config/ResourcesConfig.class | Bin 0 -> 4109 bytes .../ruoyi/framework/config/RuoYiConfig.class | Bin 0 -> 2091 bytes .../framework/config/SecurityConfig.class | Bin 0 -> 9303 bytes .../ruoyi/framework/config/ServerConfig.class | Bin 0 -> 1453 bytes .../framework/config/SwaggerConfig.class | Bin 0 -> 6675 bytes .../framework/config/ThreadPoolConfig$1.class | Bin 0 -> 1349 bytes .../framework/config/ThreadPoolConfig.class | Bin 0 -> 2314 bytes .../config/properties/DruidProperties.class | Bin 0 -> 2710 bytes .../properties/PermitAllUrlProperties.class | Bin 0 -> 6026 bytes .../datasource/DynamicDataSource.class | Bin 0 -> 1185 bytes .../DynamicDataSourceContextHolder.class | Bin 0 -> 1320 bytes .../interceptor/RepeatSubmitInterceptor.class | Bin 0 -> 2191 bytes .../interceptor/annotation/RepeatSubmit.class | Bin 0 -> 650 bytes .../impl/SameUrlDataInterceptor.class | Bin 0 -> 4672 bytes .../framework/manager/AsyncManager.class | Bin 0 -> 1468 bytes .../framework/manager/ShutdownManager.class | Bin 0 -> 1336 bytes .../manager/factory/AsyncFactory$1.class | Bin 0 -> 3283 bytes .../manager/factory/AsyncFactory$2.class | Bin 0 -> 1266 bytes .../manager/factory/AsyncFactory.class | Bin 0 -> 2109 bytes .../ruoyi/framework/redis/RedisCache.class | Bin 0 -> 8640 bytes .../ruoyi/framework/security/LoginBody.class | Bin 0 -> 1193 bytes .../ruoyi/framework/security/LoginUser.class | Bin 0 -> 4824 bytes .../framework/security/RegisterBody.class | Bin 0 -> 341 bytes .../context/AuthenticationContextHolder.class | Bin 0 -> 1192 bytes .../context/PermissionContextHolder.class | Bin 0 -> 1183 bytes .../filter/JwtAuthenticationTokenFilter.class | Bin 0 -> 2900 bytes .../handle/AuthenticationEntryPointImpl.class | Bin 0 -> 1910 bytes .../handle/LogoutSuccessHandlerImpl.class | Bin 0 -> 2659 bytes .../security/service/PermissionService.class | Bin 0 -> 3610 bytes .../security/service/SysLoginService.class | Bin 0 -> 6595 bytes .../security/service/SysPasswordService.class | Bin 0 -> 3467 bytes .../service/SysPermissionService.class | Bin 0 -> 2686 bytes .../security/service/SysRegisterService.class | Bin 0 -> 4196 bytes .../security/service/TokenService.class | Bin 0 -> 7495 bytes .../service/UserDetailsServiceImpl.class | Bin 0 -> 3323 bytes .../com/ruoyi/framework/task/RyTask.class | Bin 0 -> 1590 bytes .../web/controller/BaseController$1.class | Bin 0 -> 1020 bytes .../web/controller/BaseController.class | Bin 0 -> 4538 bytes .../framework/web/domain/AjaxResult.class | Bin 0 -> 3171 bytes .../framework/web/domain/BaseEntity.class | Bin 0 -> 2888 bytes .../com/ruoyi/framework/web/domain/R.class | Bin 0 -> 3989 bytes .../ruoyi/framework/web/domain/Server.class | Bin 0 -> 8318 bytes .../framework/web/domain/TreeEntity.class | Bin 0 -> 1917 bytes .../framework/web/domain/TreeSelect.class | Bin 0 -> 3559 bytes .../framework/web/domain/server/Cpu.class | Bin 0 -> 1690 bytes .../framework/web/domain/server/Jvm.class | Bin 0 -> 2559 bytes .../framework/web/domain/server/Mem.class | Bin 0 -> 1143 bytes .../framework/web/domain/server/Sys.class | Bin 0 -> 1425 bytes .../framework/web/domain/server/SysFile.class | Bin 0 -> 1825 bytes .../exception/GlobalExceptionHandler.class | Bin 0 -> 7033 bytes .../ruoyi/framework/web/page/PageDomain.class | Bin 0 -> 2373 bytes .../framework/web/page/TableDataInfo.class | Bin 0 -> 1720 bytes .../framework/web/page/TableSupport.class | Bin 0 -> 1526 bytes .../controller/AboutPartThreeController.class | Bin 0 -> 3803 bytes ...AboutPartThree$AboutPartThreeBuilder.class | Bin 0 -> 2465 bytes .../project/about/domain/AboutPartThree.class | Bin 0 -> 6314 bytes .../about/mapper/AboutPartThreeMapper.class | Bin 0 -> 346 bytes .../service/IAboutPartThreeService.class | Bin 0 -> 359 bytes .../impl/AboutPartThreeServiceImpl.class | Bin 0 -> 804 bytes .../project/common/CaptchaController.class | Bin 0 -> 4088 bytes .../project/common/CommonController.class | Bin 0 -> 6665 bytes .../monitor/controller/CacheController.class | Bin 0 -> 7634 bytes .../monitor/controller/ServerController.class | Bin 0 -> 1166 bytes .../monitor/controller/SysJobController.class | Bin 0 -> 7250 bytes .../controller/SysJobLogController.class | Bin 0 -> 3954 bytes .../controller/SysLogininforController.class | Bin 0 -> 4113 bytes .../controller/SysOperlogController.class | Bin 0 -> 3471 bytes .../controller/SysUserOnlineController.class | Bin 0 -> 4357 bytes .../project/monitor/domain/SysCache.class | Bin 0 -> 1873 bytes .../ruoyi/project/monitor/domain/SysJob.class | Bin 0 -> 5075 bytes .../project/monitor/domain/SysJobLog.class | Bin 0 -> 3542 bytes .../monitor/domain/SysLogininfor.class | Bin 0 -> 3238 bytes .../project/monitor/domain/SysOperLog.class | Bin 0 -> 5734 bytes .../monitor/domain/SysUserOnline.class | Bin 0 -> 2107 bytes .../monitor/mapper/SysJobLogMapper.class | Bin 0 -> 893 bytes .../project/monitor/mapper/SysJobMapper.class | Bin 0 -> 837 bytes .../monitor/mapper/SysLogininforMapper.class | Bin 0 -> 634 bytes .../monitor/mapper/SysOperLogMapper.class | Bin 0 -> 715 bytes .../monitor/service/ISysJobLogService.class | Bin 0 -> 773 bytes .../monitor/service/ISysJobService.class | Bin 0 -> 1141 bytes .../service/ISysLogininforService.class | Bin 0 -> 639 bytes .../monitor/service/ISysOperLogService.class | Bin 0 -> 720 bytes .../service/impl/SysJobLogServiceImpl.class | Bin 0 -> 1957 bytes .../service/impl/SysJobServiceImpl.class | Bin 0 -> 6106 bytes .../impl/SysLogininforServiceImpl.class | Bin 0 -> 1611 bytes .../service/impl/SysOperLogServiceImpl.class | Bin 0 -> 1765 bytes .../controller/SysConfigController.class | Bin 0 -> 5640 bytes .../system/controller/SysDeptController.class | Bin 0 -> 6295 bytes .../controller/SysDictDataController.class | Bin 0 -> 5391 bytes .../controller/SysDictTypeController.class | Bin 0 -> 5595 bytes .../controller/SysIndexController.class | Bin 0 -> 1102 bytes .../controller/SysLoginController.class | Bin 0 -> 3455 bytes .../system/controller/SysMenuController.class | Bin 0 -> 5760 bytes .../controller/SysNoticeController.class | Bin 0 -> 3814 bytes .../system/controller/SysPostController.class | Bin 0 -> 5476 bytes .../controller/SysProfileController.class | Bin 0 -> 5902 bytes .../controller/SysRegisterController.class | Bin 0 -> 2052 bytes .../system/controller/SysRoleController.class | Bin 0 -> 10193 bytes .../system/controller/SysUserController.class | Bin 0 -> 12115 bytes .../project/system/domain/SysConfig.class | Bin 0 -> 3629 bytes .../ruoyi/project/system/domain/SysDept.class | Bin 0 -> 5107 bytes .../project/system/domain/SysDictData.class | Bin 0 -> 4929 bytes .../project/system/domain/SysDictType.class | Bin 0 -> 3385 bytes .../ruoyi/project/system/domain/SysMenu.class | Bin 0 -> 6125 bytes .../project/system/domain/SysNotice.class | Bin 0 -> 2881 bytes .../ruoyi/project/system/domain/SysPost.class | Bin 0 -> 3830 bytes .../ruoyi/project/system/domain/SysRole.class | Bin 0 -> 6374 bytes .../project/system/domain/SysRoleDept.class | Bin 0 -> 1354 bytes .../project/system/domain/SysRoleMenu.class | Bin 0 -> 1354 bytes .../ruoyi/project/system/domain/SysUser.class | Bin 0 -> 8257 bytes .../project/system/domain/SysUserPost.class | Bin 0 -> 1354 bytes .../project/system/domain/SysUserRole.class | Bin 0 -> 1354 bytes .../project/system/domain/vo/MetaVo.class | Bin 0 -> 2139 bytes .../project/system/domain/vo/RouterVo.class | Bin 0 -> 3106 bytes .../system/mapper/SysConfigMapper.class | Bin 0 -> 1011 bytes .../project/system/mapper/SysDeptMapper.class | Bin 0 -> 1668 bytes .../system/mapper/SysDictDataMapper.class | Bin 0 -> 1414 bytes .../system/mapper/SysDictTypeMapper.class | Bin 0 -> 1066 bytes .../project/system/mapper/SysMenuMapper.class | Bin 0 -> 1760 bytes .../system/mapper/SysNoticeMapper.class | Bin 0 -> 766 bytes .../project/system/mapper/SysPostMapper.class | Bin 0 -> 1355 bytes .../system/mapper/SysRoleDeptMapper.class | Bin 0 -> 525 bytes .../project/system/mapper/SysRoleMapper.class | Bin 0 -> 1491 bytes .../system/mapper/SysRoleMenuMapper.class | Bin 0 -> 516 bytes .../project/system/mapper/SysUserMapper.class | Bin 0 -> 1385 bytes .../system/mapper/SysUserPostMapper.class | Bin 0 -> 515 bytes .../system/mapper/SysUserRoleMapper.class | Bin 0 -> 829 bytes .../system/service/ISysConfigService.class | Bin 0 -> 1020 bytes .../system/service/ISysDeptService.class | Bin 0 -> 1662 bytes .../system/service/ISysDictDataService.class | Bin 0 -> 860 bytes .../system/service/ISysDictTypeService.class | Bin 0 -> 1318 bytes .../system/service/ISysMenuService.class | Bin 0 -> 1964 bytes .../system/service/ISysNoticeService.class | Bin 0 -> 771 bytes .../system/service/ISysPostService.class | Bin 0 -> 1177 bytes .../system/service/ISysRoleService.class | Bin 0 -> 1908 bytes .../service/ISysUserOnlineService.class | Bin 0 -> 720 bytes .../system/service/ISysUserService.class | Bin 0 -> 2087 bytes .../service/impl/SysConfigServiceImpl.class | Bin 0 -> 6034 bytes .../service/impl/SysDeptServiceImpl.class | Bin 0 -> 10348 bytes .../service/impl/SysDictDataServiceImpl.class | Bin 0 -> 2844 bytes .../service/impl/SysDictTypeServiceImpl.class | Bin 0 -> 7302 bytes .../service/impl/SysMenuServiceImpl.class | Bin 0 -> 14432 bytes .../service/impl/SysNoticeServiceImpl.class | Bin 0 -> 1893 bytes .../service/impl/SysPostServiceImpl.class | Bin 0 -> 3909 bytes .../service/impl/SysRoleServiceImpl.class | Bin 0 -> 9557 bytes .../impl/SysUserOnlineServiceImpl.class | Bin 0 -> 2784 bytes .../service/impl/SysUserServiceImpl.class | Bin 0 -> 13212 bytes .../tool/gen/controller/GenController.class | Bin 0 -> 10380 bytes .../project/tool/gen/domain/GenTable.class | Bin 0 -> 8307 bytes .../tool/gen/domain/GenTableColumn.class | Bin 0 -> 7803 bytes .../gen/mapper/GenTableColumnMapper.class | Bin 0 -> 981 bytes .../tool/gen/mapper/GenTableMapper.class | Bin 0 -> 1259 bytes .../service/GenTableColumnServiceImpl.class | Bin 0 -> 1752 bytes .../gen/service/GenTableServiceImpl.class | Bin 0 -> 16646 bytes .../gen/service/IGenTableColumnService.class | Bin 0 -> 624 bytes .../tool/gen/service/IGenTableService.class | Bin 0 -> 1708 bytes .../project/tool/gen/util/GenUtils.class | Bin 0 -> 5947 bytes .../tool/gen/util/VelocityInitializer.class | Bin 0 -> 1087 bytes .../project/tool/gen/util/VelocityUtils.class | Bin 0 -> 11308 bytes .../project/tool/swagger/TestController.class | Bin 0 -> 4706 bytes .../project/tool/swagger/UserEntity.class | Bin 0 -> 1839 bytes target/classes/i18n/messages.properties | 38 + target/classes/logback.xml | 93 + .../PartThree/AboutPartThreeMapper.xml | 6 + .../mybatis/monitor/SysLogininforMapper.xml | 57 + .../mybatis/monitor/SysOperLogMapper.xml | 87 + target/classes/mybatis/mybatis-config.xml | 20 + .../mybatis/system/SysConfigMapper.xml | 117 ++ .../classes/mybatis/system/SysDeptMapper.xml | 159 ++ .../mybatis/system/SysDictDataMapper.xml | 124 ++ .../mybatis/system/SysDictTypeMapper.xml | 105 + .../mybatis/system/SysJobLogMapper.xml | 94 + .../classes/mybatis/system/SysJobMapper.xml | 111 + .../classes/mybatis/system/SysMenuMapper.xml | 202 ++ .../mybatis/system/SysNoticeMapper.xml | 89 + .../classes/mybatis/system/SysPostMapper.xml | 122 ++ .../mybatis/system/SysRoleDeptMapper.xml | 34 + .../classes/mybatis/system/SysRoleMapper.xml | 152 ++ .../mybatis/system/SysRoleMenuMapper.xml | 34 + .../classes/mybatis/system/SysUserMapper.xml | 221 ++ .../mybatis/system/SysUserPostMapper.xml | 34 + .../mybatis/system/SysUserRoleMapper.xml | 44 + .../mybatis/tool/GenTableColumnMapper.xml | 127 ++ .../classes/mybatis/tool/GenTableMapper.xml | 210 ++ target/classes/vm/java/controller.java.vm | 84 + target/classes/vm/java/domain.java.vm | 65 + target/classes/vm/java/mapper.java.vm | 14 + target/classes/vm/java/service.java.vm | 15 + target/classes/vm/java/serviceImpl.java.vm | 17 + target/classes/vm/java/sub-domain.java.vm | 73 + target/classes/vm/js/api.js.vm | 44 + target/classes/vm/sql/sql.vm | 22 + target/classes/vm/vue/index-tree.vue.vm | 505 +++++ target/classes/vm/vue/index.vue.vm | 602 ++++++ target/classes/vm/vue/v3/index-tree.vue.vm | 474 +++++ target/classes/vm/vue/v3/index.vue.vm | 590 ++++++ target/classes/vm/xml/mapper.xml.vm | 6 + 640 files changed, 44127 insertions(+) create mode 100644 .idea/.gitignore create mode 100644 .idea/compiler.xml create mode 100644 .idea/encodings.xml create mode 100644 .idea/inspectionProfiles/Project_Default.xml create mode 100644 .idea/intellij-javadocs-4.0.1.xml create mode 100644 .idea/jarRepositories.xml create mode 100644 .idea/misc.xml create mode 100644 .idea/runConfigurations.xml create mode 100644 .idea/uiDesigner.xml create mode 100644 .idea/vcs.xml create mode 100644 README.md create mode 100644 bin/clean.bat create mode 100644 bin/package.bat create mode 100644 bin/run-tomcat.bat create mode 100644 doc/若依环境使用手册.docx create mode 100644 pom.xml create mode 100644 ry.bat create mode 100644 ry.sh create mode 100644 sql/quartz.sql create mode 100644 sql/ry_20231130.sql create mode 100644 src/main/java/com/ruoyi/RuoYiApplication.java create mode 100644 src/main/java/com/ruoyi/RuoYiServletInitializer.java create mode 100644 src/main/java/com/ruoyi/common/constant/CacheConstants.java create mode 100644 src/main/java/com/ruoyi/common/constant/Constants.java create mode 100644 src/main/java/com/ruoyi/common/constant/GenConstants.java create mode 100644 src/main/java/com/ruoyi/common/constant/HttpStatus.java create mode 100644 src/main/java/com/ruoyi/common/constant/ScheduleConstants.java create mode 100644 src/main/java/com/ruoyi/common/constant/UserConstants.java create mode 100644 src/main/java/com/ruoyi/common/core/text/CharsetKit.java create mode 100644 src/main/java/com/ruoyi/common/core/text/Convert.java create mode 100644 src/main/java/com/ruoyi/common/core/text/StrFormatter.java create mode 100644 src/main/java/com/ruoyi/common/enums/HttpMethod.java create mode 100644 src/main/java/com/ruoyi/common/enums/UserStatus.java create mode 100644 src/main/java/com/ruoyi/common/exception/DemoModeException.java create mode 100644 src/main/java/com/ruoyi/common/exception/GlobalException.java create mode 100644 src/main/java/com/ruoyi/common/exception/ServiceException.java create mode 100644 src/main/java/com/ruoyi/common/exception/UtilException.java create mode 100644 src/main/java/com/ruoyi/common/exception/base/BaseException.java create mode 100644 src/main/java/com/ruoyi/common/exception/file/FileException.java create mode 100644 src/main/java/com/ruoyi/common/exception/file/FileNameLengthLimitExceededException.java create mode 100644 src/main/java/com/ruoyi/common/exception/file/FileSizeLimitExceededException.java create mode 100644 src/main/java/com/ruoyi/common/exception/file/FileUploadException.java create mode 100644 src/main/java/com/ruoyi/common/exception/file/InvalidExtensionException.java create mode 100644 src/main/java/com/ruoyi/common/exception/job/TaskException.java create mode 100644 src/main/java/com/ruoyi/common/exception/user/BlackListException.java create mode 100644 src/main/java/com/ruoyi/common/exception/user/CaptchaException.java create mode 100644 src/main/java/com/ruoyi/common/exception/user/CaptchaExpireException.java create mode 100644 src/main/java/com/ruoyi/common/exception/user/UserException.java create mode 100644 src/main/java/com/ruoyi/common/exception/user/UserNotExistsException.java create mode 100644 src/main/java/com/ruoyi/common/exception/user/UserPasswordNotMatchException.java create mode 100644 src/main/java/com/ruoyi/common/exception/user/UserPasswordRetryLimitExceedException.java create mode 100644 src/main/java/com/ruoyi/common/filter/PropertyPreExcludeFilter.java create mode 100644 src/main/java/com/ruoyi/common/filter/RepeatableFilter.java create mode 100644 src/main/java/com/ruoyi/common/filter/RepeatedlyRequestWrapper.java create mode 100644 src/main/java/com/ruoyi/common/filter/XssFilter.java create mode 100644 src/main/java/com/ruoyi/common/filter/XssHttpServletRequestWrapper.java create mode 100644 src/main/java/com/ruoyi/common/utils/Arith.java create mode 100644 src/main/java/com/ruoyi/common/utils/DateUtils.java create mode 100644 src/main/java/com/ruoyi/common/utils/DictUtils.java create mode 100644 src/main/java/com/ruoyi/common/utils/ExceptionUtil.java create mode 100644 src/main/java/com/ruoyi/common/utils/LogUtils.java create mode 100644 src/main/java/com/ruoyi/common/utils/MessageUtils.java create mode 100644 src/main/java/com/ruoyi/common/utils/PageUtils.java create mode 100644 src/main/java/com/ruoyi/common/utils/SecurityUtils.java create mode 100644 src/main/java/com/ruoyi/common/utils/ServletUtils.java create mode 100644 src/main/java/com/ruoyi/common/utils/StringUtils.java create mode 100644 src/main/java/com/ruoyi/common/utils/Threads.java create mode 100644 src/main/java/com/ruoyi/common/utils/bean/BeanUtils.java create mode 100644 src/main/java/com/ruoyi/common/utils/bean/BeanValidators.java create mode 100644 src/main/java/com/ruoyi/common/utils/file/FileTypeUtils.java create mode 100644 src/main/java/com/ruoyi/common/utils/file/FileUploadUtils.java create mode 100644 src/main/java/com/ruoyi/common/utils/file/FileUtils.java create mode 100644 src/main/java/com/ruoyi/common/utils/file/ImageUtils.java create mode 100644 src/main/java/com/ruoyi/common/utils/file/MimeTypeUtils.java create mode 100644 src/main/java/com/ruoyi/common/utils/html/EscapeUtil.java create mode 100644 src/main/java/com/ruoyi/common/utils/html/HTMLFilter.java create mode 100644 src/main/java/com/ruoyi/common/utils/http/HttpHelper.java create mode 100644 src/main/java/com/ruoyi/common/utils/http/HttpUtils.java create mode 100644 src/main/java/com/ruoyi/common/utils/ip/AddressUtils.java create mode 100644 src/main/java/com/ruoyi/common/utils/ip/IpUtils.java create mode 100644 src/main/java/com/ruoyi/common/utils/job/AbstractQuartzJob.java create mode 100644 src/main/java/com/ruoyi/common/utils/job/CronUtils.java create mode 100644 src/main/java/com/ruoyi/common/utils/job/JobInvokeUtil.java create mode 100644 src/main/java/com/ruoyi/common/utils/job/QuartzDisallowConcurrentExecution.java create mode 100644 src/main/java/com/ruoyi/common/utils/job/QuartzJobExecution.java create mode 100644 src/main/java/com/ruoyi/common/utils/job/ScheduleUtils.java create mode 100644 src/main/java/com/ruoyi/common/utils/poi/ExcelHandlerAdapter.java create mode 100644 src/main/java/com/ruoyi/common/utils/poi/ExcelUtil.java create mode 100644 src/main/java/com/ruoyi/common/utils/reflect/ReflectUtils.java create mode 100644 src/main/java/com/ruoyi/common/utils/sign/Base64.java create mode 100644 src/main/java/com/ruoyi/common/utils/sign/Md5Utils.java create mode 100644 src/main/java/com/ruoyi/common/utils/spring/SpringUtils.java create mode 100644 src/main/java/com/ruoyi/common/utils/sql/SqlUtil.java create mode 100644 src/main/java/com/ruoyi/common/utils/uuid/IdUtils.java create mode 100644 src/main/java/com/ruoyi/common/utils/uuid/Seq.java create mode 100644 src/main/java/com/ruoyi/common/utils/uuid/UUID.java create mode 100644 src/main/java/com/ruoyi/common/xss/Xss.java create mode 100644 src/main/java/com/ruoyi/common/xss/XssValidator.java create mode 100644 src/main/java/com/ruoyi/framework/aspectj/DataScopeAspect.java create mode 100644 src/main/java/com/ruoyi/framework/aspectj/DataSourceAspect.java create mode 100644 src/main/java/com/ruoyi/framework/aspectj/LogAspect.java create mode 100644 src/main/java/com/ruoyi/framework/aspectj/RateLimiterAspect.java create mode 100644 src/main/java/com/ruoyi/framework/aspectj/lang/annotation/Anonymous.java create mode 100644 src/main/java/com/ruoyi/framework/aspectj/lang/annotation/DataScope.java create mode 100644 src/main/java/com/ruoyi/framework/aspectj/lang/annotation/DataSource.java create mode 100644 src/main/java/com/ruoyi/framework/aspectj/lang/annotation/Excel.java create mode 100644 src/main/java/com/ruoyi/framework/aspectj/lang/annotation/Excels.java create mode 100644 src/main/java/com/ruoyi/framework/aspectj/lang/annotation/Log.java create mode 100644 src/main/java/com/ruoyi/framework/aspectj/lang/annotation/RateLimiter.java create mode 100644 src/main/java/com/ruoyi/framework/aspectj/lang/enums/BusinessStatus.java create mode 100644 src/main/java/com/ruoyi/framework/aspectj/lang/enums/BusinessType.java create mode 100644 src/main/java/com/ruoyi/framework/aspectj/lang/enums/DataSourceType.java create mode 100644 src/main/java/com/ruoyi/framework/aspectj/lang/enums/LimitType.java create mode 100644 src/main/java/com/ruoyi/framework/aspectj/lang/enums/OperatorType.java create mode 100644 src/main/java/com/ruoyi/framework/config/ApplicationConfig.java create mode 100644 src/main/java/com/ruoyi/framework/config/CaptchaConfig.java create mode 100644 src/main/java/com/ruoyi/framework/config/DruidConfig.java create mode 100644 src/main/java/com/ruoyi/framework/config/FastJson2JsonRedisSerializer.java create mode 100644 src/main/java/com/ruoyi/framework/config/FilterConfig.java create mode 100644 src/main/java/com/ruoyi/framework/config/GenConfig.java create mode 100644 src/main/java/com/ruoyi/framework/config/I18nConfig.java create mode 100644 src/main/java/com/ruoyi/framework/config/KaptchaTextCreator.java create mode 100644 src/main/java/com/ruoyi/framework/config/MybatisPlusConfig.java create mode 100644 src/main/java/com/ruoyi/framework/config/RedisConfig.java create mode 100644 src/main/java/com/ruoyi/framework/config/ResourcesConfig.java create mode 100644 src/main/java/com/ruoyi/framework/config/RuoYiConfig.java create mode 100644 src/main/java/com/ruoyi/framework/config/ScheduleConfig.java create mode 100644 src/main/java/com/ruoyi/framework/config/SecurityConfig.java create mode 100644 src/main/java/com/ruoyi/framework/config/ServerConfig.java create mode 100644 src/main/java/com/ruoyi/framework/config/SwaggerConfig.java create mode 100644 src/main/java/com/ruoyi/framework/config/ThreadPoolConfig.java create mode 100644 src/main/java/com/ruoyi/framework/config/properties/DruidProperties.java create mode 100644 src/main/java/com/ruoyi/framework/config/properties/PermitAllUrlProperties.java create mode 100644 src/main/java/com/ruoyi/framework/datasource/DynamicDataSource.java create mode 100644 src/main/java/com/ruoyi/framework/datasource/DynamicDataSourceContextHolder.java create mode 100644 src/main/java/com/ruoyi/framework/interceptor/RepeatSubmitInterceptor.java create mode 100644 src/main/java/com/ruoyi/framework/interceptor/annotation/RepeatSubmit.java create mode 100644 src/main/java/com/ruoyi/framework/interceptor/impl/SameUrlDataInterceptor.java create mode 100644 src/main/java/com/ruoyi/framework/manager/AsyncManager.java create mode 100644 src/main/java/com/ruoyi/framework/manager/ShutdownManager.java create mode 100644 src/main/java/com/ruoyi/framework/manager/factory/AsyncFactory.java create mode 100644 src/main/java/com/ruoyi/framework/redis/RedisCache.java create mode 100644 src/main/java/com/ruoyi/framework/security/LoginBody.java create mode 100644 src/main/java/com/ruoyi/framework/security/LoginUser.java create mode 100644 src/main/java/com/ruoyi/framework/security/RegisterBody.java create mode 100644 src/main/java/com/ruoyi/framework/security/context/AuthenticationContextHolder.java create mode 100644 src/main/java/com/ruoyi/framework/security/context/PermissionContextHolder.java create mode 100644 src/main/java/com/ruoyi/framework/security/filter/JwtAuthenticationTokenFilter.java create mode 100644 src/main/java/com/ruoyi/framework/security/handle/AuthenticationEntryPointImpl.java create mode 100644 src/main/java/com/ruoyi/framework/security/handle/LogoutSuccessHandlerImpl.java create mode 100644 src/main/java/com/ruoyi/framework/security/service/PermissionService.java create mode 100644 src/main/java/com/ruoyi/framework/security/service/SysLoginService.java create mode 100644 src/main/java/com/ruoyi/framework/security/service/SysPasswordService.java create mode 100644 src/main/java/com/ruoyi/framework/security/service/SysPermissionService.java create mode 100644 src/main/java/com/ruoyi/framework/security/service/SysRegisterService.java create mode 100644 src/main/java/com/ruoyi/framework/security/service/TokenService.java create mode 100644 src/main/java/com/ruoyi/framework/security/service/UserDetailsServiceImpl.java create mode 100644 src/main/java/com/ruoyi/framework/task/RyTask.java create mode 100644 src/main/java/com/ruoyi/framework/web/controller/BaseController.java create mode 100644 src/main/java/com/ruoyi/framework/web/domain/AjaxResult.java create mode 100644 src/main/java/com/ruoyi/framework/web/domain/BaseEntity.java create mode 100644 src/main/java/com/ruoyi/framework/web/domain/R.java create mode 100644 src/main/java/com/ruoyi/framework/web/domain/Server.java create mode 100644 src/main/java/com/ruoyi/framework/web/domain/TreeEntity.java create mode 100644 src/main/java/com/ruoyi/framework/web/domain/TreeSelect.java create mode 100644 src/main/java/com/ruoyi/framework/web/domain/server/Cpu.java create mode 100644 src/main/java/com/ruoyi/framework/web/domain/server/Jvm.java create mode 100644 src/main/java/com/ruoyi/framework/web/domain/server/Mem.java create mode 100644 src/main/java/com/ruoyi/framework/web/domain/server/Sys.java create mode 100644 src/main/java/com/ruoyi/framework/web/domain/server/SysFile.java create mode 100644 src/main/java/com/ruoyi/framework/web/exception/GlobalExceptionHandler.java create mode 100644 src/main/java/com/ruoyi/framework/web/page/PageDomain.java create mode 100644 src/main/java/com/ruoyi/framework/web/page/TableDataInfo.java create mode 100644 src/main/java/com/ruoyi/framework/web/page/TableSupport.java create mode 100644 src/main/java/com/ruoyi/project/about/controller/AboutPartThreeController.java create mode 100644 src/main/java/com/ruoyi/project/about/domain/AboutPartThree.java create mode 100644 src/main/java/com/ruoyi/project/about/mapper/AboutPartThreeMapper.java create mode 100644 src/main/java/com/ruoyi/project/about/service/IAboutPartThreeService.java create mode 100644 src/main/java/com/ruoyi/project/about/service/impl/AboutPartThreeServiceImpl.java create mode 100644 src/main/java/com/ruoyi/project/common/CaptchaController.java create mode 100644 src/main/java/com/ruoyi/project/common/CommonController.java create mode 100644 src/main/java/com/ruoyi/project/monitor/controller/CacheController.java create mode 100644 src/main/java/com/ruoyi/project/monitor/controller/ServerController.java create mode 100644 src/main/java/com/ruoyi/project/monitor/controller/SysJobController.java create mode 100644 src/main/java/com/ruoyi/project/monitor/controller/SysJobLogController.java create mode 100644 src/main/java/com/ruoyi/project/monitor/controller/SysLogininforController.java create mode 100644 src/main/java/com/ruoyi/project/monitor/controller/SysOperlogController.java create mode 100644 src/main/java/com/ruoyi/project/monitor/controller/SysUserOnlineController.java create mode 100644 src/main/java/com/ruoyi/project/monitor/domain/SysCache.java create mode 100644 src/main/java/com/ruoyi/project/monitor/domain/SysJob.java create mode 100644 src/main/java/com/ruoyi/project/monitor/domain/SysJobLog.java create mode 100644 src/main/java/com/ruoyi/project/monitor/domain/SysLogininfor.java create mode 100644 src/main/java/com/ruoyi/project/monitor/domain/SysOperLog.java create mode 100644 src/main/java/com/ruoyi/project/monitor/domain/SysUserOnline.java create mode 100644 src/main/java/com/ruoyi/project/monitor/mapper/SysJobLogMapper.java create mode 100644 src/main/java/com/ruoyi/project/monitor/mapper/SysJobMapper.java create mode 100644 src/main/java/com/ruoyi/project/monitor/mapper/SysLogininforMapper.java create mode 100644 src/main/java/com/ruoyi/project/monitor/mapper/SysOperLogMapper.java create mode 100644 src/main/java/com/ruoyi/project/monitor/service/ISysJobLogService.java create mode 100644 src/main/java/com/ruoyi/project/monitor/service/ISysJobService.java create mode 100644 src/main/java/com/ruoyi/project/monitor/service/ISysLogininforService.java create mode 100644 src/main/java/com/ruoyi/project/monitor/service/ISysOperLogService.java create mode 100644 src/main/java/com/ruoyi/project/monitor/service/impl/SysJobLogServiceImpl.java create mode 100644 src/main/java/com/ruoyi/project/monitor/service/impl/SysJobServiceImpl.java create mode 100644 src/main/java/com/ruoyi/project/monitor/service/impl/SysLogininforServiceImpl.java create mode 100644 src/main/java/com/ruoyi/project/monitor/service/impl/SysOperLogServiceImpl.java create mode 100644 src/main/java/com/ruoyi/project/system/controller/SysConfigController.java create mode 100644 src/main/java/com/ruoyi/project/system/controller/SysDeptController.java create mode 100644 src/main/java/com/ruoyi/project/system/controller/SysDictDataController.java create mode 100644 src/main/java/com/ruoyi/project/system/controller/SysDictTypeController.java create mode 100644 src/main/java/com/ruoyi/project/system/controller/SysIndexController.java create mode 100644 src/main/java/com/ruoyi/project/system/controller/SysLoginController.java create mode 100644 src/main/java/com/ruoyi/project/system/controller/SysMenuController.java create mode 100644 src/main/java/com/ruoyi/project/system/controller/SysNoticeController.java create mode 100644 src/main/java/com/ruoyi/project/system/controller/SysPostController.java create mode 100644 src/main/java/com/ruoyi/project/system/controller/SysProfileController.java create mode 100644 src/main/java/com/ruoyi/project/system/controller/SysRegisterController.java create mode 100644 src/main/java/com/ruoyi/project/system/controller/SysRoleController.java create mode 100644 src/main/java/com/ruoyi/project/system/controller/SysUserController.java create mode 100644 src/main/java/com/ruoyi/project/system/domain/SysConfig.java create mode 100644 src/main/java/com/ruoyi/project/system/domain/SysDept.java create mode 100644 src/main/java/com/ruoyi/project/system/domain/SysDictData.java create mode 100644 src/main/java/com/ruoyi/project/system/domain/SysDictType.java create mode 100644 src/main/java/com/ruoyi/project/system/domain/SysMenu.java create mode 100644 src/main/java/com/ruoyi/project/system/domain/SysNotice.java create mode 100644 src/main/java/com/ruoyi/project/system/domain/SysPost.java create mode 100644 src/main/java/com/ruoyi/project/system/domain/SysRole.java create mode 100644 src/main/java/com/ruoyi/project/system/domain/SysRoleDept.java create mode 100644 src/main/java/com/ruoyi/project/system/domain/SysRoleMenu.java create mode 100644 src/main/java/com/ruoyi/project/system/domain/SysUser.java create mode 100644 src/main/java/com/ruoyi/project/system/domain/SysUserPost.java create mode 100644 src/main/java/com/ruoyi/project/system/domain/SysUserRole.java create mode 100644 src/main/java/com/ruoyi/project/system/domain/vo/MetaVo.java create mode 100644 src/main/java/com/ruoyi/project/system/domain/vo/RouterVo.java create mode 100644 src/main/java/com/ruoyi/project/system/mapper/SysConfigMapper.java create mode 100644 src/main/java/com/ruoyi/project/system/mapper/SysDeptMapper.java create mode 100644 src/main/java/com/ruoyi/project/system/mapper/SysDictDataMapper.java create mode 100644 src/main/java/com/ruoyi/project/system/mapper/SysDictTypeMapper.java create mode 100644 src/main/java/com/ruoyi/project/system/mapper/SysMenuMapper.java create mode 100644 src/main/java/com/ruoyi/project/system/mapper/SysNoticeMapper.java create mode 100644 src/main/java/com/ruoyi/project/system/mapper/SysPostMapper.java create mode 100644 src/main/java/com/ruoyi/project/system/mapper/SysRoleDeptMapper.java create mode 100644 src/main/java/com/ruoyi/project/system/mapper/SysRoleMapper.java create mode 100644 src/main/java/com/ruoyi/project/system/mapper/SysRoleMenuMapper.java create mode 100644 src/main/java/com/ruoyi/project/system/mapper/SysUserMapper.java create mode 100644 src/main/java/com/ruoyi/project/system/mapper/SysUserPostMapper.java create mode 100644 src/main/java/com/ruoyi/project/system/mapper/SysUserRoleMapper.java create mode 100644 src/main/java/com/ruoyi/project/system/service/ISysConfigService.java create mode 100644 src/main/java/com/ruoyi/project/system/service/ISysDeptService.java create mode 100644 src/main/java/com/ruoyi/project/system/service/ISysDictDataService.java create mode 100644 src/main/java/com/ruoyi/project/system/service/ISysDictTypeService.java create mode 100644 src/main/java/com/ruoyi/project/system/service/ISysMenuService.java create mode 100644 src/main/java/com/ruoyi/project/system/service/ISysNoticeService.java create mode 100644 src/main/java/com/ruoyi/project/system/service/ISysPostService.java create mode 100644 src/main/java/com/ruoyi/project/system/service/ISysRoleService.java create mode 100644 src/main/java/com/ruoyi/project/system/service/ISysUserOnlineService.java create mode 100644 src/main/java/com/ruoyi/project/system/service/ISysUserService.java create mode 100644 src/main/java/com/ruoyi/project/system/service/impl/SysConfigServiceImpl.java create mode 100644 src/main/java/com/ruoyi/project/system/service/impl/SysDeptServiceImpl.java create mode 100644 src/main/java/com/ruoyi/project/system/service/impl/SysDictDataServiceImpl.java create mode 100644 src/main/java/com/ruoyi/project/system/service/impl/SysDictTypeServiceImpl.java create mode 100644 src/main/java/com/ruoyi/project/system/service/impl/SysMenuServiceImpl.java create mode 100644 src/main/java/com/ruoyi/project/system/service/impl/SysNoticeServiceImpl.java create mode 100644 src/main/java/com/ruoyi/project/system/service/impl/SysPostServiceImpl.java create mode 100644 src/main/java/com/ruoyi/project/system/service/impl/SysRoleServiceImpl.java create mode 100644 src/main/java/com/ruoyi/project/system/service/impl/SysUserOnlineServiceImpl.java create mode 100644 src/main/java/com/ruoyi/project/system/service/impl/SysUserServiceImpl.java create mode 100644 src/main/java/com/ruoyi/project/tool/gen/controller/GenController.java create mode 100644 src/main/java/com/ruoyi/project/tool/gen/domain/GenTable.java create mode 100644 src/main/java/com/ruoyi/project/tool/gen/domain/GenTableColumn.java create mode 100644 src/main/java/com/ruoyi/project/tool/gen/mapper/GenTableColumnMapper.java create mode 100644 src/main/java/com/ruoyi/project/tool/gen/mapper/GenTableMapper.java create mode 100644 src/main/java/com/ruoyi/project/tool/gen/service/GenTableColumnServiceImpl.java create mode 100644 src/main/java/com/ruoyi/project/tool/gen/service/GenTableServiceImpl.java create mode 100644 src/main/java/com/ruoyi/project/tool/gen/service/IGenTableColumnService.java create mode 100644 src/main/java/com/ruoyi/project/tool/gen/service/IGenTableService.java create mode 100644 src/main/java/com/ruoyi/project/tool/gen/util/GenUtils.java create mode 100644 src/main/java/com/ruoyi/project/tool/gen/util/VelocityInitializer.java create mode 100644 src/main/java/com/ruoyi/project/tool/gen/util/VelocityUtils.java create mode 100644 src/main/java/com/ruoyi/project/tool/swagger/TestController.java create mode 100644 src/main/resources/META-INF/spring-devtools.properties create mode 100644 src/main/resources/application-druid.yml create mode 100644 src/main/resources/application.yml create mode 100644 src/main/resources/banner.txt create mode 100644 src/main/resources/i18n/messages.properties create mode 100644 src/main/resources/logback.xml create mode 100644 src/main/resources/mybatis/PartThree/AboutPartThreeMapper.xml create mode 100644 src/main/resources/mybatis/monitor/SysLogininforMapper.xml create mode 100644 src/main/resources/mybatis/monitor/SysOperLogMapper.xml create mode 100644 src/main/resources/mybatis/mybatis-config.xml create mode 100644 src/main/resources/mybatis/system/SysConfigMapper.xml create mode 100644 src/main/resources/mybatis/system/SysDeptMapper.xml create mode 100644 src/main/resources/mybatis/system/SysDictDataMapper.xml create mode 100644 src/main/resources/mybatis/system/SysDictTypeMapper.xml create mode 100644 src/main/resources/mybatis/system/SysJobLogMapper.xml create mode 100644 src/main/resources/mybatis/system/SysJobMapper.xml create mode 100644 src/main/resources/mybatis/system/SysMenuMapper.xml create mode 100644 src/main/resources/mybatis/system/SysNoticeMapper.xml create mode 100644 src/main/resources/mybatis/system/SysPostMapper.xml create mode 100644 src/main/resources/mybatis/system/SysRoleDeptMapper.xml create mode 100644 src/main/resources/mybatis/system/SysRoleMapper.xml create mode 100644 src/main/resources/mybatis/system/SysRoleMenuMapper.xml create mode 100644 src/main/resources/mybatis/system/SysUserMapper.xml create mode 100644 src/main/resources/mybatis/system/SysUserPostMapper.xml create mode 100644 src/main/resources/mybatis/system/SysUserRoleMapper.xml create mode 100644 src/main/resources/mybatis/tool/GenTableColumnMapper.xml create mode 100644 src/main/resources/mybatis/tool/GenTableMapper.xml create mode 100644 src/main/resources/vm/java/controller.java.vm create mode 100644 src/main/resources/vm/java/domain.java.vm create mode 100644 src/main/resources/vm/java/mapper.java.vm create mode 100644 src/main/resources/vm/java/service.java.vm create mode 100644 src/main/resources/vm/java/serviceImpl.java.vm create mode 100644 src/main/resources/vm/java/sub-domain.java.vm create mode 100644 src/main/resources/vm/js/api.js.vm create mode 100644 src/main/resources/vm/sql/sql.vm create mode 100644 src/main/resources/vm/vue/index-tree.vue.vm create mode 100644 src/main/resources/vm/vue/index.vue.vm create mode 100644 src/main/resources/vm/vue/v3/index-tree.vue.vm create mode 100644 src/main/resources/vm/vue/v3/index.vue.vm create mode 100644 src/main/resources/vm/xml/mapper.xml.vm create mode 100644 target/classes/META-INF/spring-devtools.properties create mode 100644 target/classes/application-druid.yml create mode 100644 target/classes/application.yml create mode 100644 target/classes/banner.txt create mode 100644 target/classes/com/ruoyi/RuoYiApplication.class create mode 100644 target/classes/com/ruoyi/RuoYiServletInitializer.class create mode 100644 target/classes/com/ruoyi/common/constant/CacheConstants.class create mode 100644 target/classes/com/ruoyi/common/constant/Constants.class create mode 100644 target/classes/com/ruoyi/common/constant/GenConstants.class create mode 100644 target/classes/com/ruoyi/common/constant/HttpStatus.class create mode 100644 target/classes/com/ruoyi/common/constant/ScheduleConstants$Status.class create mode 100644 target/classes/com/ruoyi/common/constant/ScheduleConstants.class create mode 100644 target/classes/com/ruoyi/common/constant/UserConstants.class create mode 100644 target/classes/com/ruoyi/common/core/text/CharsetKit.class create mode 100644 target/classes/com/ruoyi/common/core/text/Convert.class create mode 100644 target/classes/com/ruoyi/common/core/text/StrFormatter.class create mode 100644 target/classes/com/ruoyi/common/enums/HttpMethod.class create mode 100644 target/classes/com/ruoyi/common/enums/UserStatus.class create mode 100644 target/classes/com/ruoyi/common/exception/DemoModeException.class create mode 100644 target/classes/com/ruoyi/common/exception/GlobalException.class create mode 100644 target/classes/com/ruoyi/common/exception/ServiceException.class create mode 100644 target/classes/com/ruoyi/common/exception/UtilException.class create mode 100644 target/classes/com/ruoyi/common/exception/base/BaseException.class create mode 100644 target/classes/com/ruoyi/common/exception/file/FileException.class create mode 100644 target/classes/com/ruoyi/common/exception/file/FileNameLengthLimitExceededException.class create mode 100644 target/classes/com/ruoyi/common/exception/file/FileSizeLimitExceededException.class create mode 100644 target/classes/com/ruoyi/common/exception/file/FileUploadException.class create mode 100644 target/classes/com/ruoyi/common/exception/file/InvalidExtensionException$InvalidFlashExtensionException.class create mode 100644 target/classes/com/ruoyi/common/exception/file/InvalidExtensionException$InvalidImageExtensionException.class create mode 100644 target/classes/com/ruoyi/common/exception/file/InvalidExtensionException$InvalidMediaExtensionException.class create mode 100644 target/classes/com/ruoyi/common/exception/file/InvalidExtensionException$InvalidVideoExtensionException.class create mode 100644 target/classes/com/ruoyi/common/exception/file/InvalidExtensionException.class create mode 100644 target/classes/com/ruoyi/common/exception/job/TaskException$Code.class create mode 100644 target/classes/com/ruoyi/common/exception/job/TaskException.class create mode 100644 target/classes/com/ruoyi/common/exception/user/BlackListException.class create mode 100644 target/classes/com/ruoyi/common/exception/user/CaptchaException.class create mode 100644 target/classes/com/ruoyi/common/exception/user/CaptchaExpireException.class create mode 100644 target/classes/com/ruoyi/common/exception/user/UserException.class create mode 100644 target/classes/com/ruoyi/common/exception/user/UserNotExistsException.class create mode 100644 target/classes/com/ruoyi/common/exception/user/UserPasswordNotMatchException.class create mode 100644 target/classes/com/ruoyi/common/exception/user/UserPasswordRetryLimitExceedException.class create mode 100644 target/classes/com/ruoyi/common/filter/PropertyPreExcludeFilter.class create mode 100644 target/classes/com/ruoyi/common/filter/RepeatableFilter.class create mode 100644 target/classes/com/ruoyi/common/filter/RepeatedlyRequestWrapper$1.class create mode 100644 target/classes/com/ruoyi/common/filter/RepeatedlyRequestWrapper.class create mode 100644 target/classes/com/ruoyi/common/filter/XssFilter.class create mode 100644 target/classes/com/ruoyi/common/filter/XssHttpServletRequestWrapper$1.class create mode 100644 target/classes/com/ruoyi/common/filter/XssHttpServletRequestWrapper.class create mode 100644 target/classes/com/ruoyi/common/utils/Arith.class create mode 100644 target/classes/com/ruoyi/common/utils/DateUtils.class create mode 100644 target/classes/com/ruoyi/common/utils/DictUtils.class create mode 100644 target/classes/com/ruoyi/common/utils/ExceptionUtil.class create mode 100644 target/classes/com/ruoyi/common/utils/LogUtils.class create mode 100644 target/classes/com/ruoyi/common/utils/MessageUtils.class create mode 100644 target/classes/com/ruoyi/common/utils/PageUtils.class create mode 100644 target/classes/com/ruoyi/common/utils/SecurityUtils.class create mode 100644 target/classes/com/ruoyi/common/utils/ServletUtils.class create mode 100644 target/classes/com/ruoyi/common/utils/StringUtils.class create mode 100644 target/classes/com/ruoyi/common/utils/Threads.class create mode 100644 target/classes/com/ruoyi/common/utils/bean/BeanUtils.class create mode 100644 target/classes/com/ruoyi/common/utils/bean/BeanValidators.class create mode 100644 target/classes/com/ruoyi/common/utils/file/FileTypeUtils.class create mode 100644 target/classes/com/ruoyi/common/utils/file/FileUploadUtils.class create mode 100644 target/classes/com/ruoyi/common/utils/file/FileUtils.class create mode 100644 target/classes/com/ruoyi/common/utils/file/ImageUtils.class create mode 100644 target/classes/com/ruoyi/common/utils/file/MimeTypeUtils.class create mode 100644 target/classes/com/ruoyi/common/utils/html/EscapeUtil.class create mode 100644 target/classes/com/ruoyi/common/utils/html/HTMLFilter.class create mode 100644 target/classes/com/ruoyi/common/utils/http/HttpHelper.class create mode 100644 target/classes/com/ruoyi/common/utils/http/HttpUtils$1.class create mode 100644 target/classes/com/ruoyi/common/utils/http/HttpUtils$TrustAnyHostnameVerifier.class create mode 100644 target/classes/com/ruoyi/common/utils/http/HttpUtils$TrustAnyTrustManager.class create mode 100644 target/classes/com/ruoyi/common/utils/http/HttpUtils.class create mode 100644 target/classes/com/ruoyi/common/utils/ip/AddressUtils.class create mode 100644 target/classes/com/ruoyi/common/utils/ip/IpUtils.class create mode 100644 target/classes/com/ruoyi/common/utils/job/AbstractQuartzJob.class create mode 100644 target/classes/com/ruoyi/common/utils/job/CronUtils.class create mode 100644 target/classes/com/ruoyi/common/utils/job/JobInvokeUtil.class create mode 100644 target/classes/com/ruoyi/common/utils/job/QuartzDisallowConcurrentExecution.class create mode 100644 target/classes/com/ruoyi/common/utils/job/QuartzJobExecution.class create mode 100644 target/classes/com/ruoyi/common/utils/job/ScheduleUtils.class create mode 100644 target/classes/com/ruoyi/common/utils/poi/ExcelHandlerAdapter.class create mode 100644 target/classes/com/ruoyi/common/utils/poi/ExcelUtil.class create mode 100644 target/classes/com/ruoyi/common/utils/reflect/ReflectUtils.class create mode 100644 target/classes/com/ruoyi/common/utils/sign/Base64.class create mode 100644 target/classes/com/ruoyi/common/utils/sign/Md5Utils.class create mode 100644 target/classes/com/ruoyi/common/utils/spring/SpringUtils.class create mode 100644 target/classes/com/ruoyi/common/utils/sql/SqlUtil.class create mode 100644 target/classes/com/ruoyi/common/utils/uuid/IdUtils.class create mode 100644 target/classes/com/ruoyi/common/utils/uuid/Seq.class create mode 100644 target/classes/com/ruoyi/common/utils/uuid/UUID$Holder.class create mode 100644 target/classes/com/ruoyi/common/utils/uuid/UUID.class create mode 100644 target/classes/com/ruoyi/common/xss/Xss.class create mode 100644 target/classes/com/ruoyi/common/xss/XssValidator.class create mode 100644 target/classes/com/ruoyi/framework/aspectj/DataScopeAspect.class create mode 100644 target/classes/com/ruoyi/framework/aspectj/DataSourceAspect.class create mode 100644 target/classes/com/ruoyi/framework/aspectj/LogAspect.class create mode 100644 target/classes/com/ruoyi/framework/aspectj/RateLimiterAspect.class create mode 100644 target/classes/com/ruoyi/framework/aspectj/lang/annotation/Anonymous.class create mode 100644 target/classes/com/ruoyi/framework/aspectj/lang/annotation/DataScope.class create mode 100644 target/classes/com/ruoyi/framework/aspectj/lang/annotation/DataSource.class create mode 100644 target/classes/com/ruoyi/framework/aspectj/lang/annotation/Excel$ColumnType.class create mode 100644 target/classes/com/ruoyi/framework/aspectj/lang/annotation/Excel$Type.class create mode 100644 target/classes/com/ruoyi/framework/aspectj/lang/annotation/Excel.class create mode 100644 target/classes/com/ruoyi/framework/aspectj/lang/annotation/Excels.class create mode 100644 target/classes/com/ruoyi/framework/aspectj/lang/annotation/Log.class create mode 100644 target/classes/com/ruoyi/framework/aspectj/lang/annotation/RateLimiter.class create mode 100644 target/classes/com/ruoyi/framework/aspectj/lang/enums/BusinessStatus.class create mode 100644 target/classes/com/ruoyi/framework/aspectj/lang/enums/BusinessType.class create mode 100644 target/classes/com/ruoyi/framework/aspectj/lang/enums/DataSourceType.class create mode 100644 target/classes/com/ruoyi/framework/aspectj/lang/enums/LimitType.class create mode 100644 target/classes/com/ruoyi/framework/aspectj/lang/enums/OperatorType.class create mode 100644 target/classes/com/ruoyi/framework/config/ApplicationConfig.class create mode 100644 target/classes/com/ruoyi/framework/config/CaptchaConfig.class create mode 100644 target/classes/com/ruoyi/framework/config/DruidConfig$1.class create mode 100644 target/classes/com/ruoyi/framework/config/DruidConfig.class create mode 100644 target/classes/com/ruoyi/framework/config/FastJson2JsonRedisSerializer.class create mode 100644 target/classes/com/ruoyi/framework/config/FilterConfig.class create mode 100644 target/classes/com/ruoyi/framework/config/GenConfig.class create mode 100644 target/classes/com/ruoyi/framework/config/I18nConfig.class create mode 100644 target/classes/com/ruoyi/framework/config/KaptchaTextCreator.class create mode 100644 target/classes/com/ruoyi/framework/config/MybatisPlusConfig.class create mode 100644 target/classes/com/ruoyi/framework/config/RedisConfig.class create mode 100644 target/classes/com/ruoyi/framework/config/ResourcesConfig.class create mode 100644 target/classes/com/ruoyi/framework/config/RuoYiConfig.class create mode 100644 target/classes/com/ruoyi/framework/config/SecurityConfig.class create mode 100644 target/classes/com/ruoyi/framework/config/ServerConfig.class create mode 100644 target/classes/com/ruoyi/framework/config/SwaggerConfig.class create mode 100644 target/classes/com/ruoyi/framework/config/ThreadPoolConfig$1.class create mode 100644 target/classes/com/ruoyi/framework/config/ThreadPoolConfig.class create mode 100644 target/classes/com/ruoyi/framework/config/properties/DruidProperties.class create mode 100644 target/classes/com/ruoyi/framework/config/properties/PermitAllUrlProperties.class create mode 100644 target/classes/com/ruoyi/framework/datasource/DynamicDataSource.class create mode 100644 target/classes/com/ruoyi/framework/datasource/DynamicDataSourceContextHolder.class create mode 100644 target/classes/com/ruoyi/framework/interceptor/RepeatSubmitInterceptor.class create mode 100644 target/classes/com/ruoyi/framework/interceptor/annotation/RepeatSubmit.class create mode 100644 target/classes/com/ruoyi/framework/interceptor/impl/SameUrlDataInterceptor.class create mode 100644 target/classes/com/ruoyi/framework/manager/AsyncManager.class create mode 100644 target/classes/com/ruoyi/framework/manager/ShutdownManager.class create mode 100644 target/classes/com/ruoyi/framework/manager/factory/AsyncFactory$1.class create mode 100644 target/classes/com/ruoyi/framework/manager/factory/AsyncFactory$2.class create mode 100644 target/classes/com/ruoyi/framework/manager/factory/AsyncFactory.class create mode 100644 target/classes/com/ruoyi/framework/redis/RedisCache.class create mode 100644 target/classes/com/ruoyi/framework/security/LoginBody.class create mode 100644 target/classes/com/ruoyi/framework/security/LoginUser.class create mode 100644 target/classes/com/ruoyi/framework/security/RegisterBody.class create mode 100644 target/classes/com/ruoyi/framework/security/context/AuthenticationContextHolder.class create mode 100644 target/classes/com/ruoyi/framework/security/context/PermissionContextHolder.class create mode 100644 target/classes/com/ruoyi/framework/security/filter/JwtAuthenticationTokenFilter.class create mode 100644 target/classes/com/ruoyi/framework/security/handle/AuthenticationEntryPointImpl.class create mode 100644 target/classes/com/ruoyi/framework/security/handle/LogoutSuccessHandlerImpl.class create mode 100644 target/classes/com/ruoyi/framework/security/service/PermissionService.class create mode 100644 target/classes/com/ruoyi/framework/security/service/SysLoginService.class create mode 100644 target/classes/com/ruoyi/framework/security/service/SysPasswordService.class create mode 100644 target/classes/com/ruoyi/framework/security/service/SysPermissionService.class create mode 100644 target/classes/com/ruoyi/framework/security/service/SysRegisterService.class create mode 100644 target/classes/com/ruoyi/framework/security/service/TokenService.class create mode 100644 target/classes/com/ruoyi/framework/security/service/UserDetailsServiceImpl.class create mode 100644 target/classes/com/ruoyi/framework/task/RyTask.class create mode 100644 target/classes/com/ruoyi/framework/web/controller/BaseController$1.class create mode 100644 target/classes/com/ruoyi/framework/web/controller/BaseController.class create mode 100644 target/classes/com/ruoyi/framework/web/domain/AjaxResult.class create mode 100644 target/classes/com/ruoyi/framework/web/domain/BaseEntity.class create mode 100644 target/classes/com/ruoyi/framework/web/domain/R.class create mode 100644 target/classes/com/ruoyi/framework/web/domain/Server.class create mode 100644 target/classes/com/ruoyi/framework/web/domain/TreeEntity.class create mode 100644 target/classes/com/ruoyi/framework/web/domain/TreeSelect.class create mode 100644 target/classes/com/ruoyi/framework/web/domain/server/Cpu.class create mode 100644 target/classes/com/ruoyi/framework/web/domain/server/Jvm.class create mode 100644 target/classes/com/ruoyi/framework/web/domain/server/Mem.class create mode 100644 target/classes/com/ruoyi/framework/web/domain/server/Sys.class create mode 100644 target/classes/com/ruoyi/framework/web/domain/server/SysFile.class create mode 100644 target/classes/com/ruoyi/framework/web/exception/GlobalExceptionHandler.class create mode 100644 target/classes/com/ruoyi/framework/web/page/PageDomain.class create mode 100644 target/classes/com/ruoyi/framework/web/page/TableDataInfo.class create mode 100644 target/classes/com/ruoyi/framework/web/page/TableSupport.class create mode 100644 target/classes/com/ruoyi/project/about/controller/AboutPartThreeController.class create mode 100644 target/classes/com/ruoyi/project/about/domain/AboutPartThree$AboutPartThreeBuilder.class create mode 100644 target/classes/com/ruoyi/project/about/domain/AboutPartThree.class create mode 100644 target/classes/com/ruoyi/project/about/mapper/AboutPartThreeMapper.class create mode 100644 target/classes/com/ruoyi/project/about/service/IAboutPartThreeService.class create mode 100644 target/classes/com/ruoyi/project/about/service/impl/AboutPartThreeServiceImpl.class create mode 100644 target/classes/com/ruoyi/project/common/CaptchaController.class create mode 100644 target/classes/com/ruoyi/project/common/CommonController.class create mode 100644 target/classes/com/ruoyi/project/monitor/controller/CacheController.class create mode 100644 target/classes/com/ruoyi/project/monitor/controller/ServerController.class create mode 100644 target/classes/com/ruoyi/project/monitor/controller/SysJobController.class create mode 100644 target/classes/com/ruoyi/project/monitor/controller/SysJobLogController.class create mode 100644 target/classes/com/ruoyi/project/monitor/controller/SysLogininforController.class create mode 100644 target/classes/com/ruoyi/project/monitor/controller/SysOperlogController.class create mode 100644 target/classes/com/ruoyi/project/monitor/controller/SysUserOnlineController.class create mode 100644 target/classes/com/ruoyi/project/monitor/domain/SysCache.class create mode 100644 target/classes/com/ruoyi/project/monitor/domain/SysJob.class create mode 100644 target/classes/com/ruoyi/project/monitor/domain/SysJobLog.class create mode 100644 target/classes/com/ruoyi/project/monitor/domain/SysLogininfor.class create mode 100644 target/classes/com/ruoyi/project/monitor/domain/SysOperLog.class create mode 100644 target/classes/com/ruoyi/project/monitor/domain/SysUserOnline.class create mode 100644 target/classes/com/ruoyi/project/monitor/mapper/SysJobLogMapper.class create mode 100644 target/classes/com/ruoyi/project/monitor/mapper/SysJobMapper.class create mode 100644 target/classes/com/ruoyi/project/monitor/mapper/SysLogininforMapper.class create mode 100644 target/classes/com/ruoyi/project/monitor/mapper/SysOperLogMapper.class create mode 100644 target/classes/com/ruoyi/project/monitor/service/ISysJobLogService.class create mode 100644 target/classes/com/ruoyi/project/monitor/service/ISysJobService.class create mode 100644 target/classes/com/ruoyi/project/monitor/service/ISysLogininforService.class create mode 100644 target/classes/com/ruoyi/project/monitor/service/ISysOperLogService.class create mode 100644 target/classes/com/ruoyi/project/monitor/service/impl/SysJobLogServiceImpl.class create mode 100644 target/classes/com/ruoyi/project/monitor/service/impl/SysJobServiceImpl.class create mode 100644 target/classes/com/ruoyi/project/monitor/service/impl/SysLogininforServiceImpl.class create mode 100644 target/classes/com/ruoyi/project/monitor/service/impl/SysOperLogServiceImpl.class create mode 100644 target/classes/com/ruoyi/project/system/controller/SysConfigController.class create mode 100644 target/classes/com/ruoyi/project/system/controller/SysDeptController.class create mode 100644 target/classes/com/ruoyi/project/system/controller/SysDictDataController.class create mode 100644 target/classes/com/ruoyi/project/system/controller/SysDictTypeController.class create mode 100644 target/classes/com/ruoyi/project/system/controller/SysIndexController.class create mode 100644 target/classes/com/ruoyi/project/system/controller/SysLoginController.class create mode 100644 target/classes/com/ruoyi/project/system/controller/SysMenuController.class create mode 100644 target/classes/com/ruoyi/project/system/controller/SysNoticeController.class create mode 100644 target/classes/com/ruoyi/project/system/controller/SysPostController.class create mode 100644 target/classes/com/ruoyi/project/system/controller/SysProfileController.class create mode 100644 target/classes/com/ruoyi/project/system/controller/SysRegisterController.class create mode 100644 target/classes/com/ruoyi/project/system/controller/SysRoleController.class create mode 100644 target/classes/com/ruoyi/project/system/controller/SysUserController.class create mode 100644 target/classes/com/ruoyi/project/system/domain/SysConfig.class create mode 100644 target/classes/com/ruoyi/project/system/domain/SysDept.class create mode 100644 target/classes/com/ruoyi/project/system/domain/SysDictData.class create mode 100644 target/classes/com/ruoyi/project/system/domain/SysDictType.class create mode 100644 target/classes/com/ruoyi/project/system/domain/SysMenu.class create mode 100644 target/classes/com/ruoyi/project/system/domain/SysNotice.class create mode 100644 target/classes/com/ruoyi/project/system/domain/SysPost.class create mode 100644 target/classes/com/ruoyi/project/system/domain/SysRole.class create mode 100644 target/classes/com/ruoyi/project/system/domain/SysRoleDept.class create mode 100644 target/classes/com/ruoyi/project/system/domain/SysRoleMenu.class create mode 100644 target/classes/com/ruoyi/project/system/domain/SysUser.class create mode 100644 target/classes/com/ruoyi/project/system/domain/SysUserPost.class create mode 100644 target/classes/com/ruoyi/project/system/domain/SysUserRole.class create mode 100644 target/classes/com/ruoyi/project/system/domain/vo/MetaVo.class create mode 100644 target/classes/com/ruoyi/project/system/domain/vo/RouterVo.class create mode 100644 target/classes/com/ruoyi/project/system/mapper/SysConfigMapper.class create mode 100644 target/classes/com/ruoyi/project/system/mapper/SysDeptMapper.class create mode 100644 target/classes/com/ruoyi/project/system/mapper/SysDictDataMapper.class create mode 100644 target/classes/com/ruoyi/project/system/mapper/SysDictTypeMapper.class create mode 100644 target/classes/com/ruoyi/project/system/mapper/SysMenuMapper.class create mode 100644 target/classes/com/ruoyi/project/system/mapper/SysNoticeMapper.class create mode 100644 target/classes/com/ruoyi/project/system/mapper/SysPostMapper.class create mode 100644 target/classes/com/ruoyi/project/system/mapper/SysRoleDeptMapper.class create mode 100644 target/classes/com/ruoyi/project/system/mapper/SysRoleMapper.class create mode 100644 target/classes/com/ruoyi/project/system/mapper/SysRoleMenuMapper.class create mode 100644 target/classes/com/ruoyi/project/system/mapper/SysUserMapper.class create mode 100644 target/classes/com/ruoyi/project/system/mapper/SysUserPostMapper.class create mode 100644 target/classes/com/ruoyi/project/system/mapper/SysUserRoleMapper.class create mode 100644 target/classes/com/ruoyi/project/system/service/ISysConfigService.class create mode 100644 target/classes/com/ruoyi/project/system/service/ISysDeptService.class create mode 100644 target/classes/com/ruoyi/project/system/service/ISysDictDataService.class create mode 100644 target/classes/com/ruoyi/project/system/service/ISysDictTypeService.class create mode 100644 target/classes/com/ruoyi/project/system/service/ISysMenuService.class create mode 100644 target/classes/com/ruoyi/project/system/service/ISysNoticeService.class create mode 100644 target/classes/com/ruoyi/project/system/service/ISysPostService.class create mode 100644 target/classes/com/ruoyi/project/system/service/ISysRoleService.class create mode 100644 target/classes/com/ruoyi/project/system/service/ISysUserOnlineService.class create mode 100644 target/classes/com/ruoyi/project/system/service/ISysUserService.class create mode 100644 target/classes/com/ruoyi/project/system/service/impl/SysConfigServiceImpl.class create mode 100644 target/classes/com/ruoyi/project/system/service/impl/SysDeptServiceImpl.class create mode 100644 target/classes/com/ruoyi/project/system/service/impl/SysDictDataServiceImpl.class create mode 100644 target/classes/com/ruoyi/project/system/service/impl/SysDictTypeServiceImpl.class create mode 100644 target/classes/com/ruoyi/project/system/service/impl/SysMenuServiceImpl.class create mode 100644 target/classes/com/ruoyi/project/system/service/impl/SysNoticeServiceImpl.class create mode 100644 target/classes/com/ruoyi/project/system/service/impl/SysPostServiceImpl.class create mode 100644 target/classes/com/ruoyi/project/system/service/impl/SysRoleServiceImpl.class create mode 100644 target/classes/com/ruoyi/project/system/service/impl/SysUserOnlineServiceImpl.class create mode 100644 target/classes/com/ruoyi/project/system/service/impl/SysUserServiceImpl.class create mode 100644 target/classes/com/ruoyi/project/tool/gen/controller/GenController.class create mode 100644 target/classes/com/ruoyi/project/tool/gen/domain/GenTable.class create mode 100644 target/classes/com/ruoyi/project/tool/gen/domain/GenTableColumn.class create mode 100644 target/classes/com/ruoyi/project/tool/gen/mapper/GenTableColumnMapper.class create mode 100644 target/classes/com/ruoyi/project/tool/gen/mapper/GenTableMapper.class create mode 100644 target/classes/com/ruoyi/project/tool/gen/service/GenTableColumnServiceImpl.class create mode 100644 target/classes/com/ruoyi/project/tool/gen/service/GenTableServiceImpl.class create mode 100644 target/classes/com/ruoyi/project/tool/gen/service/IGenTableColumnService.class create mode 100644 target/classes/com/ruoyi/project/tool/gen/service/IGenTableService.class create mode 100644 target/classes/com/ruoyi/project/tool/gen/util/GenUtils.class create mode 100644 target/classes/com/ruoyi/project/tool/gen/util/VelocityInitializer.class create mode 100644 target/classes/com/ruoyi/project/tool/gen/util/VelocityUtils.class create mode 100644 target/classes/com/ruoyi/project/tool/swagger/TestController.class create mode 100644 target/classes/com/ruoyi/project/tool/swagger/UserEntity.class create mode 100644 target/classes/i18n/messages.properties create mode 100644 target/classes/logback.xml create mode 100644 target/classes/mybatis/PartThree/AboutPartThreeMapper.xml create mode 100644 target/classes/mybatis/monitor/SysLogininforMapper.xml create mode 100644 target/classes/mybatis/monitor/SysOperLogMapper.xml create mode 100644 target/classes/mybatis/mybatis-config.xml create mode 100644 target/classes/mybatis/system/SysConfigMapper.xml create mode 100644 target/classes/mybatis/system/SysDeptMapper.xml create mode 100644 target/classes/mybatis/system/SysDictDataMapper.xml create mode 100644 target/classes/mybatis/system/SysDictTypeMapper.xml create mode 100644 target/classes/mybatis/system/SysJobLogMapper.xml create mode 100644 target/classes/mybatis/system/SysJobMapper.xml create mode 100644 target/classes/mybatis/system/SysMenuMapper.xml create mode 100644 target/classes/mybatis/system/SysNoticeMapper.xml create mode 100644 target/classes/mybatis/system/SysPostMapper.xml create mode 100644 target/classes/mybatis/system/SysRoleDeptMapper.xml create mode 100644 target/classes/mybatis/system/SysRoleMapper.xml create mode 100644 target/classes/mybatis/system/SysRoleMenuMapper.xml create mode 100644 target/classes/mybatis/system/SysUserMapper.xml create mode 100644 target/classes/mybatis/system/SysUserPostMapper.xml create mode 100644 target/classes/mybatis/system/SysUserRoleMapper.xml create mode 100644 target/classes/mybatis/tool/GenTableColumnMapper.xml create mode 100644 target/classes/mybatis/tool/GenTableMapper.xml create mode 100644 target/classes/vm/java/controller.java.vm create mode 100644 target/classes/vm/java/domain.java.vm create mode 100644 target/classes/vm/java/mapper.java.vm create mode 100644 target/classes/vm/java/service.java.vm create mode 100644 target/classes/vm/java/serviceImpl.java.vm create mode 100644 target/classes/vm/java/sub-domain.java.vm create mode 100644 target/classes/vm/js/api.js.vm create mode 100644 target/classes/vm/sql/sql.vm create mode 100644 target/classes/vm/vue/index-tree.vue.vm create mode 100644 target/classes/vm/vue/index.vue.vm create mode 100644 target/classes/vm/vue/v3/index-tree.vue.vm create mode 100644 target/classes/vm/vue/v3/index.vue.vm create mode 100644 target/classes/vm/xml/mapper.xml.vm diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..81b2fd6 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,10 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml +# Zeppelin ignored files +/ZeppelinRemoteNotebooks/ +# Editor-based HTTP Client requests +/httpRequests/ diff --git a/.idea/compiler.xml b/.idea/compiler.xml new file mode 100644 index 0000000..637f8f1 --- /dev/null +++ b/.idea/compiler.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/encodings.xml b/.idea/encodings.xml new file mode 100644 index 0000000..63e9001 --- /dev/null +++ b/.idea/encodings.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml new file mode 100644 index 0000000..6560a98 --- /dev/null +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -0,0 +1,36 @@ + + + + \ No newline at end of file diff --git a/.idea/intellij-javadocs-4.0.1.xml b/.idea/intellij-javadocs-4.0.1.xml new file mode 100644 index 0000000..0e84319 --- /dev/null +++ b/.idea/intellij-javadocs-4.0.1.xml @@ -0,0 +1,204 @@ + + + + + UPDATE + false + true + + FIELD + TYPE + METHOD + + + PUBLIC + PROTECTED + DEFAULT + + + + + + ^.*(public|protected|private)*.+interface\s+\w+.* + /**\n + * The interface ${name}.\n +<#if element.typeParameters?has_content> * \n +</#if> +<#list element.typeParameters as typeParameter> + * @param <${typeParameter.name}> the type parameter\n +</#list> + */ + + + ^.*(public|protected|private)*.+enum\s+\w+.* + /**\n + * The enum ${name}.\n + */ + + + ^.*(public|protected|private)*.+class\s+\w+.* + /**\n + * The type ${name}.\n +<#if element.typeParameters?has_content> * \n +</#if> +<#list element.typeParameters as typeParameter> + * @param <${typeParameter.name}> the type parameter\n +</#list> + */ + + + .+ + /**\n + * The type ${name}.\n + */ + + + + + .+ + /**\n + * Instantiates a new ${name}.\n +<#if element.parameterList.parameters?has_content> + *\n +</#if> +<#list element.parameterList.parameters as parameter> + * @param ${parameter.name} the ${paramNames[parameter.name]}\n +</#list> +<#if element.throwsList.referenceElements?has_content> + *\n +</#if> +<#list element.throwsList.referenceElements as exception> + * @throws ${exception.referenceName} the ${exceptionNames[exception.referenceName]}\n +</#list> + */ + + + + + ^.*(public|protected|private)*\s*.*(\w(\s*<.+>)*)+\s+get\w+\s*\(.*\).+ + /**\n + * Gets ${partName}.\n +<#if element.typeParameters?has_content> * \n +</#if> +<#list element.typeParameters as typeParameter> + * @param <${typeParameter.name}> the type parameter\n +</#list> +<#if element.parameterList.parameters?has_content> + *\n +</#if> +<#list element.parameterList.parameters as parameter> + * @param ${parameter.name} the ${paramNames[parameter.name]}\n +</#list> +<#if isNotVoid> + *\n + * @return the ${partName}\n +</#if> +<#if element.throwsList.referenceElements?has_content> + *\n +</#if> +<#list element.throwsList.referenceElements as exception> + * @throws ${exception.referenceName} the ${exceptionNames[exception.referenceName]}\n +</#list> + */ + + + ^.*(public|protected|private)*\s*.*(void|\w(\s*<.+>)*)+\s+set\w+\s*\(.*\).+ + /**\n + * Sets ${partName}.\n +<#if element.typeParameters?has_content> * \n +</#if> +<#list element.typeParameters as typeParameter> + * @param <${typeParameter.name}> the type parameter\n +</#list> +<#if element.parameterList.parameters?has_content> + *\n +</#if> +<#list element.parameterList.parameters as parameter> + * @param ${parameter.name} the ${paramNames[parameter.name]}\n +</#list> +<#if isNotVoid> + *\n + * @return the ${partName}\n +</#if> +<#if element.throwsList.referenceElements?has_content> + *\n +</#if> +<#list element.throwsList.referenceElements as exception> + * @throws ${exception.referenceName} the ${exceptionNames[exception.referenceName]}\n +</#list> + */ + + + ^.*((public\s+static)|(static\s+public))\s+void\s+main\s*\(\s*String\s*(\[\s*\]|\.\.\.)\s+\w+\s*\).+ + /**\n + * The entry point of application.\n + + <#if element.parameterList.parameters?has_content> + *\n +</#if> + * @param ${element.parameterList.parameters[0].name} the input arguments\n +<#if element.throwsList.referenceElements?has_content> + *\n +</#if> +<#list element.throwsList.referenceElements as exception> + * @throws ${exception.referenceName} the ${exceptionNames[exception.referenceName]}\n +</#list> + */ + + + .+ + /**\n + * ${name}<#if isNotVoid> ${return}</#if>.\n +<#if element.typeParameters?has_content> * \n +</#if> +<#list element.typeParameters as typeParameter> + * @param <${typeParameter.name}> the type parameter\n +</#list> +<#if element.parameterList.parameters?has_content> + *\n +</#if> +<#list element.parameterList.parameters as parameter> + * @param ${parameter.name} the ${paramNames[parameter.name]}\n +</#list> +<#if isNotVoid> + *\n + * @return the ${return}\n +</#if> +<#if element.throwsList.referenceElements?has_content> + *\n +</#if> +<#list element.throwsList.referenceElements as exception> + * @throws ${exception.referenceName} the ${exceptionNames[exception.referenceName]}\n +</#list> + */ + + + + + ^.*(public|protected|private)*.+static.*(\w\s\w)+.+ + /**\n + * The constant ${element.getName()}.\n + */ + + + ^.*(public|protected|private)*.*(\w\s\w)+.+ + /**\n + <#if element.parent.isInterface()> + * The constant ${element.getName()}.\n +<#else> + * The ${name}.\n +</#if> */ + + + .+ + /**\n + <#if element.parent.isEnum()> + *${name} ${typeName}.\n +<#else> + * The ${name}.\n +</#if>*/ + + + + + \ No newline at end of file diff --git a/.idea/jarRepositories.xml b/.idea/jarRepositories.xml new file mode 100644 index 0000000..055858f --- /dev/null +++ b/.idea/jarRepositories.xml @@ -0,0 +1,25 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..4157c88 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,15 @@ + + + + + + + + + + \ No newline at end of file diff --git a/.idea/runConfigurations.xml b/.idea/runConfigurations.xml new file mode 100644 index 0000000..797acea --- /dev/null +++ b/.idea/runConfigurations.xml @@ -0,0 +1,10 @@ + + + + + + \ No newline at end of file diff --git a/.idea/uiDesigner.xml b/.idea/uiDesigner.xml new file mode 100644 index 0000000..e96534f --- /dev/null +++ b/.idea/uiDesigner.xml @@ -0,0 +1,124 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..94a25f7 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..ac35673 --- /dev/null +++ b/README.md @@ -0,0 +1,96 @@ +

+ logo +

+

RuoYi v3.8.7

+

基于SpringBoot+Vue前后端分离的Java快速开发框架

+

+ + + +

+ +## 平台简介 + +* 本仓库为RuoYi-Vue的单应用版本,保持同步更新。 +* 配套前端代码地址[RuoYi-Vue](https://gitee.com/y_project/RuoYi-Vue/tree/master/ruoyi-ui),技术栈([Vue2](https://cn.vuejs.org) + [Element](https://github.com/ElemeFE/element) + [Vue CLI](https://cli.vuejs.org/zh))。 +* 配套前端代码地址[RuoYi-Vue3](https://github.com/yangzongzhuan/RuoYi-Vue3),技术栈([Vue3](https://v3.cn.vuejs.org) + [Element Plus](https://element-plus.org/zh-CN) + [Vite](https://cn.vitejs.dev))。 +* 前端采用Vue、Element UI。 +* 后端采用Spring Boot、Spring Security、Redis & Jwt。 +* 权限认证使用Jwt,支持多终端认证系统。 +* 支持加载动态权限菜单,多方式轻松权限控制。 +* 高效率开发,使用代码生成器可以一键生成前后端代码。 +* 提供了一个Oracle版本[RuoYi-Vue-Oracle](https://github.com/yangzongzhuan/RuoYi-Vue-Oracle),保持同步更新。 +* 不分离版本,请移步[RuoYi](https://gitee.com/y_project/RuoYi),微服务版本,请移步[RuoYi-Cloud](https://gitee.com/y_project/RuoYi-Cloud) +* 阿里云折扣场:[点我进入](http://aly.ruoyi.vip),腾讯云秒杀场:[点我进入](http://txy.ruoyi.vip)   +* 阿里云优惠券:[点我领取](https://www.aliyun.com/minisite/goods?userCode=brki8iof&share_source=copy_link),腾讯云优惠券:[点我领取](https://cloud.tencent.com/redirect.php?redirect=1025&cps_key=198c8df2ed259157187173bc7f4f32fd&from=console)   + +## 内置功能 + +1. 用户管理:用户是系统操作者,该功能主要完成系统用户配置。 +2. 部门管理:配置系统组织机构(公司、部门、小组),树结构展现支持数据权限。 +3. 岗位管理:配置系统用户所属担任职务。 +4. 菜单管理:配置系统菜单,操作权限,按钮权限标识等。 +5. 角色管理:角色菜单权限分配、设置角色按机构进行数据范围权限划分。 +6. 字典管理:对系统中经常使用的一些较为固定的数据进行维护。 +7. 参数管理:对系统动态配置常用参数。 +8. 通知公告:系统通知公告信息发布维护。 +9. 操作日志:系统正常操作日志记录和查询;系统异常信息日志记录和查询。 +10. 登录日志:系统登录日志记录查询包含登录异常。 +11. 在线用户:当前系统中活跃用户状态监控。 +12. 定时任务:在线(添加、修改、删除)任务调度包含执行结果日志。 +13. 代码生成:前后端代码的生成(java、html、xml、sql)支持CRUD下载 。 +14. 系统接口:根据业务代码自动生成相关的api接口文档。 +15. 服务监控:监视当前系统CPU、内存、磁盘、堆栈等相关信息。 +16. 缓存监控:对系统的缓存信息查询,命令统计等。 +17. 在线构建器:拖动表单元素生成相应的HTML代码。 +18. 连接池监视:监视当前系统数据库连接池状态,可进行分析SQL找出系统性能瓶颈。 + +## 在线体验 + +- admin/admin123 +- 陆陆续续收到一些打赏,为了更好的体验已用于演示服务器升级。谢谢各位小伙伴。 + +演示地址:http://vue.ruoyi.vip +文档地址:http://doc.ruoyi.vip + +## 演示图 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +## 若依前后端分离交流群 + +QQ群: [![加入QQ群](https://img.shields.io/badge/已满-937441-blue.svg)](https://jq.qq.com/?_wv=1027&k=5bVB1og) [![加入QQ群](https://img.shields.io/badge/已满-887144332-blue.svg)](https://jq.qq.com/?_wv=1027&k=5eiA4DH) [![加入QQ群](https://img.shields.io/badge/已满-180251782-blue.svg)](https://jq.qq.com/?_wv=1027&k=5AxMKlC) [![加入QQ群](https://img.shields.io/badge/已满-104180207-blue.svg)](https://jq.qq.com/?_wv=1027&k=51G72yr) [![加入QQ群](https://img.shields.io/badge/已满-186866453-blue.svg)](https://jq.qq.com/?_wv=1027&k=VvjN2nvu) [![加入QQ群](https://img.shields.io/badge/已满-201396349-blue.svg)](https://jq.qq.com/?_wv=1027&k=5vYAqA05) [![加入QQ群](https://img.shields.io/badge/已满-101456076-blue.svg)](https://jq.qq.com/?_wv=1027&k=kOIINEb5) [![加入QQ群](https://img.shields.io/badge/已满-101539465-blue.svg)](https://jq.qq.com/?_wv=1027&k=UKtX5jhs) [![加入QQ群](https://img.shields.io/badge/已满-264312783-blue.svg)](https://jq.qq.com/?_wv=1027&k=EI9an8lJ) [![加入QQ群](https://img.shields.io/badge/已满-167385320-blue.svg)](https://jq.qq.com/?_wv=1027&k=SWCtLnMz) [![加入QQ群](https://img.shields.io/badge/已满-104748341-blue.svg)](https://jq.qq.com/?_wv=1027&k=96Dkdq0k) [![加入QQ群](https://img.shields.io/badge/已满-160110482-blue.svg)](https://jq.qq.com/?_wv=1027&k=0fsNiYZt) [![加入QQ群](https://img.shields.io/badge/已满-170801498-blue.svg)](https://jq.qq.com/?_wv=1027&k=7xw4xUG1) [![加入QQ群](https://img.shields.io/badge/已满-108482800-blue.svg)](https://jq.qq.com/?_wv=1027&k=eCx8eyoJ) [![加入QQ群](https://img.shields.io/badge/已满-101046199-blue.svg)](https://jq.qq.com/?_wv=1027&k=SpyH2875) [![加入QQ群](https://img.shields.io/badge/已满-136919097-blue.svg)](https://jq.qq.com/?_wv=1027&k=tKEt51dz) [![加入QQ群](https://img.shields.io/badge/已满-143961921-blue.svg)](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=0vBbSb0ztbBgVtn3kJS-Q4HUNYwip89G&authKey=8irq5PhutrZmWIvsUsklBxhj57l%2F1nOZqjzigkXZVoZE451GG4JHPOqW7AW6cf0T&noverify=0&group_code=143961921) [![加入QQ群](https://img.shields.io/badge/已满-174951577-blue.svg)](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=ZFAPAbp09S2ltvwrJzp7wGlbopsc0rwi&authKey=HB2cxpxP2yspk%2Bo3WKTBfktRCccVkU26cgi5B16u0KcAYrVu7sBaE7XSEqmMdFQp&noverify=0&group_code=174951577) [![加入QQ群](https://img.shields.io/badge/已满-161281055-blue.svg)](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=Fn2aF5IHpwsy8j6VlalNJK6qbwFLFHat&authKey=uyIT%2B97x2AXj3odyXpsSpVaPMC%2Bidw0LxG5MAtEqlrcBcWJUA%2FeS43rsF1Tg7IRJ&noverify=0&group_code=161281055) [![加入QQ群](https://img.shields.io/badge/138988063-blue.svg)](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=XIzkm_mV2xTsUtFxo63bmicYoDBA6Ifm&authKey=dDW%2F4qsmw3x9govoZY9w%2FoWAoC4wbHqGal%2BbqLzoS6VBarU8EBptIgPKN%2FviyC8j&noverify=0&group_code=138988063) 点击按钮入群。 \ No newline at end of file diff --git a/bin/clean.bat b/bin/clean.bat new file mode 100644 index 0000000..24c0974 --- /dev/null +++ b/bin/clean.bat @@ -0,0 +1,12 @@ +@echo off +echo. +echo [Ϣ] target· +echo. + +%~d0 +cd %~dp0 + +cd .. +call mvn clean + +pause \ No newline at end of file diff --git a/bin/package.bat b/bin/package.bat new file mode 100644 index 0000000..c693ec0 --- /dev/null +++ b/bin/package.bat @@ -0,0 +1,12 @@ +@echo off +echo. +echo [Ϣ] Weḅwar/jarļ +echo. + +%~d0 +cd %~dp0 + +cd .. +call mvn clean package -Dmaven.test.skip=true + +pause \ No newline at end of file diff --git a/bin/run-tomcat.bat b/bin/run-tomcat.bat new file mode 100644 index 0000000..53f936d --- /dev/null +++ b/bin/run-tomcat.bat @@ -0,0 +1,14 @@ +@echo off +echo. +echo [Ϣ] ʹǶTomcatWeb̡ +echo. + +%~d0 +cd %~dp0 + +cd .. +title %cd% +set MAVEN_OPTS=%MAVEN_OPTS% -Xms256m -Xmx1024m -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=512m +call mvn clean spring-boot:run -Dmaven.test.skip=true -U + +pause \ No newline at end of file diff --git a/doc/若依环境使用手册.docx b/doc/若依环境使用手册.docx new file mode 100644 index 0000000000000000000000000000000000000000..9e4daef4d9be2e445419109a02eaf321cd4d537e GIT binary patch literal 428430 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 +HtCx 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 Rli)_iWGz7fMupr%QDEgRC> z`GMjf4QA8>dNe?Fon+;^v5|)<{`$*+r4CjI&L~yC{C+=ED$DHFe-xzC2jv zHJ54IsMFeeI8;)niO+*v3z?@I;VX0}`AQboc*+Cd!+FQj`t!*zR!B!2_aE z9mr{pOJoKKCc8uale@S_qRo)7W#n5_RBg0BCq*>W{*^A=xot*0qvbn)bybr5co05R zOdqP@-e|f6~N@%O6UY@@R{rF$? zkdHkb&Ob#~@;?A^Wbm(#NDv-?2!bjiblbl+jYF1!vjjsrz0%-_N+JAT$rJA%CoGKY z4_trhTL@ru>JDE%uPsQ%gYaO)`tzsV{rjK$v(Jz?i0fi6j>?mY4GzZnhT`x*>$yX= z%*E-u2W{Z+nP9P!%6u2O)f;WyneV&u8z*1%x-Y@jHC=TL8Mk2F`z;7Jg<3rBc zxdpb|l05%F#xIb=k8@ZoovqT|Gb2jwZ|WjY{1BLwKZQDuSb9C)X&myS_w|hQ9#*N7 z6|e?=FHvVah}c=JZ%5Zci{p9tsqbE2K&M&=B0edRYW(vmn|@m^R|NJ<;w!iDq+hZl zag(9Iry#Gj7?wvo#CBIECkd^`a%QF#8S572p;wlh6^ajcPGYhM+3@&w7Z4oMczA*M z^fa}rjj1G}Hl5<$SqTSk5}KgB(8v;)?n)}tA!|>t?f)n7oC+zEJ+FjYdyj&;@ea#< zukAeeWM%yInp_pc&M*_Q#t@qX7{xXk>t9b;YCl1of8J}*s6B5tcJUCSCjuXeTpI+IX@Ci#h}Eieu=l>*L}W2To58g`)=Wym|?_mdn6*czq4e=K9vidP9;|EkY7Py0GEzTF6H8jdv9C==3!%l) zW?XU%%fqG=QKd+eZN{o|YlR_4#rv4&a#sZyQ2Q=F`8)`zeRSsXsaLADR}yzi^4ou} zWRjLbdax{8Ol9(x)4puEMpCIjorixI^{9Zz!@!T-mL=rdF;69nb9*4!K(Hn6@3t62 zBGv&W{~fD;cPr^8wmF)Bvt#=IAnYx};%b^LPzDI@1Pks#f@^?4LV~*v?h@Qx28ZCT zL4vylcMtCF?iSqd<~{bEbMLw52M;{#-aUJESFc)CwYnScVLU7+VJi4!0<3}6V#J9|B+!+EgIh%$4IFYzy6m_~$xT!wuI-Jg3}!lT zrbgeH8bj$~+fm^+S$9l@%&dB2J`Ouhj^^&%i-bxaF7QsmI5HgkHs8aGvUc3)RI`)O zC@GkwH|EWp`ylhXp5Wor!?LRK+P^2Xio^ajokifoH?Z@qylqo4<0O=ai2Huh+QxruPVRDoEgKaRcD3x#kGUWFfbEC{a zCqlo5(i9Y#X3FB2ja?~Ji-PFilX-SRR_Ui5$q05yt$EVMt}J)9U&m*n_M$_;o^M*nGcy^u+} z9o`;H6s)_q=U#SE948kIc>Tb3LQp_q%zI0zN70wv$heIcg{eGm6BCourqfoPmxEbg zhv)Mh!I6I)z^~Wz*yP#*s}uHSv1fGAf5wH;;P(=nLlT$ zgu>c}kwre0nxdy_SLOz2(p?(rgn{+e5-6>rDKI}TVm0j>skzSUc>QHA4BOoGPyd_$ zeL<dimmKa_9PS=7giuE7LRm^}NaU2iwqv%;-Eex1b zn5i3_6Gx9W{=8USERLF4F7*ksvBh?%OmhUM!@(2UD)zVUX1aT6qOAjEvMuO+^u@W? zJN`m5Do;u?T%xfe1~jvU$i~O)HdNj5x-FMG_JI#(s5$M~AqD+@CBO7_#W?&M2u7Va z3A_1CBaa@D#oYfkJZt4hirCmn)6E(e^gn3G52yTXFO6*YP?;Lpylkb3OdgJ^-|=0j zi%#N|4|cN{zF|CDNMt%533X%061@GE6m0Bf@0 zu^_M+gklO@P9Tt^kNr)dd7v8mTb>L^7~u$D17SM8`v+daCW*=@S{a6*O~v94sdTLO zDwVq9nhHt+G~(+T!Z9ks0VIXuT<^h6wVI^LsS_s-oLL&0*qM9$HgocK3J3ym%#IF2 zw5o zf7I-aDc%E0Fw%I5@4C4_H7BT4BN$&MdV*F|4QYO;GA6MK+tVRPQwSIf6G0;3-N$JUN6)RlBI*`~{OC@VT9Y=fdEo?6GT`}s?4A^o=(TqKTV*e+9 z-?C(*VH4-R&hCI4Hu4T4PSyu0+otcUlhg3pf_)=D1?V=ozN4Pb_iphQl2=fsnI6~t zIJzB2sE~(+{h`GOw$^u4?{Lf63BmlNzvK2@GcIY+>44HS)ZfHnD}mQaq5p^vEB2}O zTBcmT+w009z$aBCHvzV;p|d-!AdHPZHiu@Q$Jcc$&z3845ncI%PCr_+*WMM3LXEpbx1FDkhs{f^}CmqM9gG1Tfz{!E1}byW3qR)hqEye;^IISuWVmJ*yJ zj@H1(ge)?kEY+6OCa>3ysF<0xSFjpMs%)G2DmrK1+l1V0`WB zTe^Xins6M%iB(n-_qUYSO5VU%^O|D91JQ^l$t7={{6jape+V7N|MAEun+a+^yzzJY z-ji8-;tq2tZQDW`IrH>XO4!g@;Zj=MJ*f((rAo6fJm{()9vd1BMXmhZD20pJgb^!` z&q{rfcIV)_3im@U{cM^ov39ccc4Y*f;r+Z)+#q}?B9}cuCi`>}5a95@km_5bw1G3HH+3sdDSkYCe&+5&@g;MVRhJVbzC95HB&H$ z>t=p^Yi7IfdpqK1l?*e_h^<@c0MRakhe|)X_ib0X)qH1$a%YAj)`ehYcFxx7_2a{A z+p}YjqrWT0`}ffQmSTDT-i#reZkF$0(!4JxshJPQ^>l=u$X2JxX}Icov=Mhq>X#hB zsUzeK#(Ns*1jJhZX(z?Jq$JnU;@XJR)st?^1y)zW&(``#<8JTzCYu{_Y`UM0sz)2o zkMDLfoE1@wUV$76mI@T$Lm2!D#Za(H7;;JU^6N|%uRIzGC@W;61HT|b>6k#ewHnUv zACe4d5C8O!R?<}j%7<_C9m>f+t)aQ^|G8<40Pqw1<}CI9$SDw(_F!I_b(9J0GwFfE zyIA;qhQt3|uv%>EKx!im1@-!a12l8Cwlie5G&20I$Nb$=&%}uLf6=Qt1B+!V5ukxP zaIy7XyZ=9z&PwC_hh7y~Q_1N0C4&GXG!?_#SUmyNFgV#sgPb{89`ZD$^4aMIuko%+ zC-2LZ{jt7t5oc9qXj4T>v=u_l4{jUr3*7oA%wYv5`-0!n>^O)xaf3sTZKJ&GUF~Vf zyzKTT7XszgM+l>?OsA*p8R@&cyB;Jzd-Ta$Nf=&KqFFh4`5C9uJNkE!UsQiv4#z5b z4r7%ILyHN0hFw@(R4rA%8WTqMkxP;?#(o?{5gcy&{oBFOi8ZJ&I~ykE>JU%9>E?o9 z(&X}VmGqrlO^cU{2md0Lzzf{+fifcI&IGzz9!G7>rtON zyk^sWe7;$aWmFoT-rrG~H}BNZZhg3Y-X2|Ud)#dCe+0jfTqFyuGMHbt>Tz(V#9x`N z2#nTwG&3ik;>=v%Pkn4P()~!aHXVj&^W?OFaClS9@bh6aou&b=0vnc01E_`u)B! zk6=fKua@v+B>2QLzU7_GUGqKMpEQ}VJ>4AgjGra($Hrd=+_D_uOh`+|)RJm63vdkC zc0)77H`O2BmN%)?ddzj8UAU*ccb!INsU(Fhp@+e$wps7Qq}=oWGi`g47C_vfY1VSv zS(NF0C=z~uN`Q^`j5~7LStjw$Xzsk%`(?9cRphJAd8F2iwSHvDp5gj;g%K}hi&f>a z&L4_u%YP`SPiT3`SPO%Y!fNku#A_HZhf>6>yw(%!6e`~Li1J?^N)+GIi?61zT2&ls zu1P-VU*zB%U7D>(RZXun-VX8g7d0+(h8tcV&!oDQRo$J05>q8>d!c{0In&}OepB6F zs{pqhdBvC9rjE^WDU+yd-Sq5royh#*okTBH+wYAL(9qXYc{z@P>4T&?wbt?34{ zdA&6mglmZ$s3c-SNYF|~M_4ec7TiYZ)|1g?GohB2vZY9oQuUtg;epq+OxG6X#|zJ% zyL~Sw;B(hWjXS;se%AhPpZu&nn)Wz z_@Zo`QF4p9qR5_^Uoj*EQ4?=+WK^PsqOgCCITM1Oz#O}X!!H=`8CeT0O(c0ZY)|qZhbberDXfVVh;cKLZ%Ah&?6iD!DSN9PbWG(iT>u`2)mV^rm= z_g>g8s!rPgtE${>dY#KDVGqG;JaH`E~Ny<#SLq zdnqL`eWF#6K{`FeTNK(0V{CSM+7S>xt+utL^BNDQY}0egCBNJajwPjvpy4mQD45UY zBF|K34|?n`P1`VOHU~b9>2cW`ercn3xYi?FcQ0`qP5rhthG{HNXDjLY!JTeT%+f01 zi2&b?yMHx6yX<3lgXhFpFY$Hi{Lz{($BTL5RU!FUbcU+(BA(nki8it?%$MoUS^Zp^ z2TV5(Y68Tp9cLIc604k7Zg4?`%0DfC-!Au^tv1-R|XemvERC8O1`5!u;Z5%iD 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

K#u)l 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

)q;tSdIrAwYxlSM|Hl};Y` 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 literal 0 HcmV?d00001 diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..8d2ae8c --- /dev/null +++ b/pom.xml @@ -0,0 +1,286 @@ + + + 4.0.0 + + com.ruoyi + ruoyi + 3.8.7 + jar + + ruoyi + http://www.ruoyi.vip + 若依管理系统 + + + org.springframework.boot + spring-boot-starter-parent + 2.5.15 + + + + + UTF-8 + UTF-8 + 1.8 + 3.1.1 + 1.4.7 + 2.0.43 + 1.2.20 + 2.13.0 + 1.21 + 0.9.1 + 2.3.3 + 3.0.0 + 4.1.2 + 6.5.0 + 2.3 + 3.4.2 + + + + + + + org.springframework.boot + spring-boot-starter + + + + + org.springframework.boot + spring-boot-starter-test + test + + + + + org.springframework.boot + spring-boot-starter-aop + + + + + org.springframework.boot + spring-boot-starter-web + + + + + org.springframework.boot + spring-boot-devtools + true + + + org.projectlombok + lombok + true + + + + org.springframework.boot + spring-boot-starter-security + + + + + org.springframework.boot + spring-boot-starter-data-redis + + + + com.github.xiaoymin + knife4j-spring-boot-starter + + 2.0.9 + + + + org.apache.commons + commons-pool2 + + + + + mysql + mysql-connector-java + runtime + + + + + com.github.pagehelper + pagehelper-spring-boot-starter + ${pagehelper.spring.boot.starter.version} + + + + com.alibaba + druid-spring-boot-starter + ${druid.version} + + + + com.baomidou + mybatis-plus-boot-starter + ${mybatis-plus.version} + + + + org.springframework.boot + spring-boot-starter-validation + + + + + org.apache.commons + commons-lang3 + + + + + commons-io + commons-io + ${commons.io.version} + + + + + eu.bitwalker + UserAgentUtils + ${bitwalker.version} + + + + + com.alibaba.fastjson2 + fastjson2 + ${fastjson.version} + + + + + org.springframework + spring-context-support + + + + + io.jsonwebtoken + jjwt + ${jwt.version} + + + + + javax.xml.bind + jaxb-api + + + + + io.springfox + springfox-boot-starter + ${swagger.version} + + + io.swagger + swagger-models + + + + + + + io.swagger + swagger-models + 1.6.2 + + + + + com.github.oshi + oshi-core + ${oshi.version} + + + + + org.apache.poi + poi-ooxml + ${poi.version} + + + + + org.apache.velocity + velocity-engine-core + ${velocity.version} + + + + + org.quartz-scheduler + quartz + + + com.mchange + c3p0 + + + + + + + pro.fessional + kaptcha + ${kaptcha.version} + + + servlet-api + javax.servlet + + + + + + + + ${project.artifactId} + + + org.springframework.boot + spring-boot-maven-plugin + + true + + + + + + + + public + aliyun nexus + https://maven.aliyun.com/repository/public + + true + + + + + + + public + aliyun nexus + https://maven.aliyun.com/repository/public + + true + + + false + + + + + \ No newline at end of file diff --git a/ry.bat b/ry.bat new file mode 100644 index 0000000..d523e8c --- /dev/null +++ b/ry.bat @@ -0,0 +1,67 @@ +@echo off + +rem jarƽĿ¼ +set AppName=ruoyi.jar + +rem JVM +set JVM_OPTS="-Dname=%AppName% -Duser.timezone=Asia/Shanghai -Xms512m -Xmx1024m -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=512m -XX:+HeapDumpOnOutOfMemoryError -XX:+PrintGCDateStamps -XX:+PrintGCDetails -XX:NewRatio=1 -XX:SurvivorRatio=30 -XX:+UseParallelGC -XX:+UseParallelOldGC" + + +ECHO. + ECHO. [1] %AppName% + ECHO. [2] ر%AppName% + ECHO. [3] %AppName% + ECHO. [4] ״̬ %AppName% + ECHO. [5] +ECHO. + +ECHO.ѡĿ: +set /p ID= + IF "%id%"=="1" GOTO start + IF "%id%"=="2" GOTO stop + IF "%id%"=="3" GOTO restart + IF "%id%"=="4" GOTO status + IF "%id%"=="5" EXIT +PAUSE +:start + for /f "usebackq tokens=1-2" %%a in (`jps -l ^| findstr %AppName%`) do ( + set pid=%%a + set image_name=%%b + ) + if defined pid ( + echo %%is running + PAUSE + ) + +start javaw %JVM_OPTS% -jar %AppName% + +echo starting +echo Start %AppName% success... +goto:eof + +rem stopͨjpspid +:stop + for /f "usebackq tokens=1-2" %%a in (`jps -l ^| findstr %AppName%`) do ( + set pid=%%a + set image_name=%%b + ) + if not defined pid (echo process %AppName% does not exists) else ( + echo prepare to kill %image_name% + echo start kill %pid% ... + rem ݽIDkill + taskkill /f /pid %pid% + ) +goto:eof +:restart + call :stop + call :start +goto:eof +:status + for /f "usebackq tokens=1-2" %%a in (`jps -l ^| findstr %AppName%`) do ( + set pid=%%a + set image_name=%%b + ) + if not defined pid (echo process %AppName% is dead ) else ( + echo %image_name% is running + ) +goto:eof diff --git a/ry.sh b/ry.sh new file mode 100644 index 0000000..393bf9d --- /dev/null +++ b/ry.sh @@ -0,0 +1,86 @@ +#!/bin/sh +# ./ry.sh start 启动 stop 停止 restart 重启 status 状态 +AppName=target/ruoyi.jar + +# JVM参数 +JVM_OPTS="-Dname=$AppName -Duser.timezone=Asia/Shanghai -Xms512m -Xmx1024m -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=512m -XX:+HeapDumpOnOutOfMemoryError -XX:+PrintGCDateStamps -XX:+PrintGCDetails -XX:NewRatio=1 -XX:SurvivorRatio=30 -XX:+UseParallelGC -XX:+UseParallelOldGC" +APP_HOME=`pwd` +LOG_PATH=$APP_HOME/logs/$AppName.log + +if [ "$1" = "" ]; +then + echo -e "\033[0;31m 未输入操作名 \033[0m \033[0;34m {start|stop|restart|status} \033[0m" + exit 1 +fi + +if [ "$AppName" = "" ]; +then + echo -e "\033[0;31m 未输入应用名 \033[0m" + exit 1 +fi + +function start() +{ + PID=`ps -ef |grep java|grep $AppName|grep -v grep|awk '{print $2}'` + + if [ x"$PID" != x"" ]; then + echo "$AppName is running..." + else + nohup java $JVM_OPTS -jar $AppName > /dev/null 2>&1 & + echo "Start $AppName success..." + fi +} + +function stop() +{ + echo "Stop $AppName" + + PID="" + query(){ + PID=`ps -ef |grep java|grep $AppName|grep -v grep|awk '{print $2}'` + } + + query + if [ x"$PID" != x"" ]; then + kill -TERM $PID + echo "$AppName (pid:$PID) exiting..." + while [ x"$PID" != x"" ] + do + sleep 1 + query + done + echo "$AppName exited." + else + echo "$AppName already stopped." + fi +} + +function restart() +{ + stop + sleep 2 + start +} + +function status() +{ + PID=`ps -ef |grep java|grep $AppName|grep -v grep|wc -l` + if [ $PID != 0 ];then + echo "$AppName is running..." + else + echo "$AppName is not running..." + fi +} + +case $1 in + start) + start;; + stop) + stop;; + restart) + restart;; + status) + status;; + *) + +esac diff --git a/sql/quartz.sql b/sql/quartz.sql new file mode 100644 index 0000000..cee613b --- /dev/null +++ b/sql/quartz.sql @@ -0,0 +1,174 @@ +DROP TABLE IF EXISTS QRTZ_FIRED_TRIGGERS; +DROP TABLE IF EXISTS QRTZ_PAUSED_TRIGGER_GRPS; +DROP TABLE IF EXISTS QRTZ_SCHEDULER_STATE; +DROP TABLE IF EXISTS QRTZ_LOCKS; +DROP TABLE IF EXISTS QRTZ_SIMPLE_TRIGGERS; +DROP TABLE IF EXISTS QRTZ_SIMPROP_TRIGGERS; +DROP TABLE IF EXISTS QRTZ_CRON_TRIGGERS; +DROP TABLE IF EXISTS QRTZ_BLOB_TRIGGERS; +DROP TABLE IF EXISTS QRTZ_TRIGGERS; +DROP TABLE IF EXISTS QRTZ_JOB_DETAILS; +DROP TABLE IF EXISTS QRTZ_CALENDARS; + +-- ---------------------------- +-- 1、存储每一个已配置的 jobDetail 的详细信息 +-- ---------------------------- +create table QRTZ_JOB_DETAILS ( + sched_name varchar(120) not null comment '调度名称', + job_name varchar(200) not null comment '任务名称', + job_group varchar(200) not null comment '任务组名', + description varchar(250) null comment '相关介绍', + job_class_name varchar(250) not null comment '执行任务类名称', + is_durable varchar(1) not null comment '是否持久化', + is_nonconcurrent varchar(1) not null comment '是否并发', + is_update_data varchar(1) not null comment '是否更新数据', + requests_recovery varchar(1) not null comment '是否接受恢复执行', + job_data blob null comment '存放持久化job对象', + primary key (sched_name, job_name, job_group) +) engine=innodb comment = '任务详细信息表'; + +-- ---------------------------- +-- 2、 存储已配置的 Trigger 的信息 +-- ---------------------------- +create table QRTZ_TRIGGERS ( + sched_name varchar(120) not null comment '调度名称', + trigger_name varchar(200) not null comment '触发器的名字', + trigger_group varchar(200) not null comment '触发器所属组的名字', + job_name varchar(200) not null comment 'qrtz_job_details表job_name的外键', + job_group varchar(200) not null comment 'qrtz_job_details表job_group的外键', + description varchar(250) null comment '相关介绍', + next_fire_time bigint(13) null comment '上一次触发时间(毫秒)', + prev_fire_time bigint(13) null comment '下一次触发时间(默认为-1表示不触发)', + priority integer null comment '优先级', + trigger_state varchar(16) not null comment '触发器状态', + trigger_type varchar(8) not null comment '触发器的类型', + start_time bigint(13) not null comment '开始时间', + end_time bigint(13) null comment '结束时间', + calendar_name varchar(200) null comment '日程表名称', + misfire_instr smallint(2) null comment '补偿执行的策略', + job_data blob null comment '存放持久化job对象', + primary key (sched_name, trigger_name, trigger_group), + foreign key (sched_name, job_name, job_group) references QRTZ_JOB_DETAILS(sched_name, job_name, job_group) +) engine=innodb comment = '触发器详细信息表'; + +-- ---------------------------- +-- 3、 存储简单的 Trigger,包括重复次数,间隔,以及已触发的次数 +-- ---------------------------- +create table QRTZ_SIMPLE_TRIGGERS ( + sched_name varchar(120) not null comment '调度名称', + trigger_name varchar(200) not null comment 'qrtz_triggers表trigger_name的外键', + trigger_group varchar(200) not null comment 'qrtz_triggers表trigger_group的外键', + repeat_count bigint(7) not null comment '重复的次数统计', + repeat_interval bigint(12) not null comment '重复的间隔时间', + times_triggered bigint(10) not null comment '已经触发的次数', + primary key (sched_name, trigger_name, trigger_group), + foreign key (sched_name, trigger_name, trigger_group) references QRTZ_TRIGGERS(sched_name, trigger_name, trigger_group) +) engine=innodb comment = '简单触发器的信息表'; + +-- ---------------------------- +-- 4、 存储 Cron Trigger,包括 Cron 表达式和时区信息 +-- ---------------------------- +create table QRTZ_CRON_TRIGGERS ( + sched_name varchar(120) not null comment '调度名称', + trigger_name varchar(200) not null comment 'qrtz_triggers表trigger_name的外键', + trigger_group varchar(200) not null comment 'qrtz_triggers表trigger_group的外键', + cron_expression varchar(200) not null comment 'cron表达式', + time_zone_id varchar(80) comment '时区', + primary key (sched_name, trigger_name, trigger_group), + foreign key (sched_name, trigger_name, trigger_group) references QRTZ_TRIGGERS(sched_name, trigger_name, trigger_group) +) engine=innodb comment = 'Cron类型的触发器表'; + +-- ---------------------------- +-- 5、 Trigger 作为 Blob 类型存储(用于 Quartz 用户用 JDBC 创建他们自己定制的 Trigger 类型,JobStore 并不知道如何存储实例的时候) +-- ---------------------------- +create table QRTZ_BLOB_TRIGGERS ( + sched_name varchar(120) not null comment '调度名称', + trigger_name varchar(200) not null comment 'qrtz_triggers表trigger_name的外键', + trigger_group varchar(200) not null comment 'qrtz_triggers表trigger_group的外键', + blob_data blob null comment '存放持久化Trigger对象', + primary key (sched_name, trigger_name, trigger_group), + foreign key (sched_name, trigger_name, trigger_group) references QRTZ_TRIGGERS(sched_name, trigger_name, trigger_group) +) engine=innodb comment = 'Blob类型的触发器表'; + +-- ---------------------------- +-- 6、 以 Blob 类型存储存放日历信息, quartz可配置一个日历来指定一个时间范围 +-- ---------------------------- +create table QRTZ_CALENDARS ( + sched_name varchar(120) not null comment '调度名称', + calendar_name varchar(200) not null comment '日历名称', + calendar blob not null comment '存放持久化calendar对象', + primary key (sched_name, calendar_name) +) engine=innodb comment = '日历信息表'; + +-- ---------------------------- +-- 7、 存储已暂停的 Trigger 组的信息 +-- ---------------------------- +create table QRTZ_PAUSED_TRIGGER_GRPS ( + sched_name varchar(120) not null comment '调度名称', + trigger_group varchar(200) not null comment 'qrtz_triggers表trigger_group的外键', + primary key (sched_name, trigger_group) +) engine=innodb comment = '暂停的触发器表'; + +-- ---------------------------- +-- 8、 存储与已触发的 Trigger 相关的状态信息,以及相联 Job 的执行信息 +-- ---------------------------- +create table QRTZ_FIRED_TRIGGERS ( + sched_name varchar(120) not null comment '调度名称', + entry_id varchar(95) not null comment '调度器实例id', + trigger_name varchar(200) not null comment 'qrtz_triggers表trigger_name的外键', + trigger_group varchar(200) not null comment 'qrtz_triggers表trigger_group的外键', + instance_name varchar(200) not null comment '调度器实例名', + fired_time bigint(13) not null comment '触发的时间', + sched_time bigint(13) not null comment '定时器制定的时间', + priority integer not null comment '优先级', + state varchar(16) not null comment '状态', + job_name varchar(200) null comment '任务名称', + job_group varchar(200) null comment '任务组名', + is_nonconcurrent varchar(1) null comment '是否并发', + requests_recovery varchar(1) null comment '是否接受恢复执行', + primary key (sched_name, entry_id) +) engine=innodb comment = '已触发的触发器表'; + +-- ---------------------------- +-- 9、 存储少量的有关 Scheduler 的状态信息,假如是用于集群中,可以看到其他的 Scheduler 实例 +-- ---------------------------- +create table QRTZ_SCHEDULER_STATE ( + sched_name varchar(120) not null comment '调度名称', + instance_name varchar(200) not null comment '实例名称', + last_checkin_time bigint(13) not null comment '上次检查时间', + checkin_interval bigint(13) not null comment '检查间隔时间', + primary key (sched_name, instance_name) +) engine=innodb comment = '调度器状态表'; + +-- ---------------------------- +-- 10、 存储程序的悲观锁的信息(假如使用了悲观锁) +-- ---------------------------- +create table QRTZ_LOCKS ( + sched_name varchar(120) not null comment '调度名称', + lock_name varchar(40) not null comment '悲观锁名称', + primary key (sched_name, lock_name) +) engine=innodb comment = '存储的悲观锁信息表'; + +-- ---------------------------- +-- 11、 Quartz集群实现同步机制的行锁表 +-- ---------------------------- +create table QRTZ_SIMPROP_TRIGGERS ( + sched_name varchar(120) not null comment '调度名称', + trigger_name varchar(200) not null comment 'qrtz_triggers表trigger_name的外键', + trigger_group varchar(200) not null comment 'qrtz_triggers表trigger_group的外键', + str_prop_1 varchar(512) null comment 'String类型的trigger的第一个参数', + str_prop_2 varchar(512) null comment 'String类型的trigger的第二个参数', + str_prop_3 varchar(512) null comment 'String类型的trigger的第三个参数', + int_prop_1 int null comment 'int类型的trigger的第一个参数', + int_prop_2 int null comment 'int类型的trigger的第二个参数', + long_prop_1 bigint null comment 'long类型的trigger的第一个参数', + long_prop_2 bigint null comment 'long类型的trigger的第二个参数', + dec_prop_1 numeric(13,4) null comment 'decimal类型的trigger的第一个参数', + dec_prop_2 numeric(13,4) null comment 'decimal类型的trigger的第二个参数', + bool_prop_1 varchar(1) null comment 'Boolean类型的trigger的第一个参数', + bool_prop_2 varchar(1) null comment 'Boolean类型的trigger的第二个参数', + primary key (sched_name, trigger_name, trigger_group), + foreign key (sched_name, trigger_name, trigger_group) references QRTZ_TRIGGERS(sched_name, trigger_name, trigger_group) +) engine=innodb comment = '同步机制的行锁表'; + +commit; \ No newline at end of file diff --git a/sql/ry_20231130.sql b/sql/ry_20231130.sql new file mode 100644 index 0000000..452c2fc --- /dev/null +++ b/sql/ry_20231130.sql @@ -0,0 +1,700 @@ +-- ---------------------------- +-- 1、部门表 +-- ---------------------------- +drop table if exists sys_dept; +create table sys_dept ( + dept_id bigint(20) not null auto_increment comment '部门id', + parent_id bigint(20) default 0 comment '父部门id', + ancestors varchar(50) default '' comment '祖级列表', + dept_name varchar(30) default '' comment '部门名称', + order_num int(4) default 0 comment '显示顺序', + leader varchar(20) default null comment '负责人', + phone varchar(11) default null comment '联系电话', + email varchar(50) default null comment '邮箱', + status char(1) default '0' comment '部门状态(0正常 1停用)', + del_flag char(1) default '0' comment '删除标志(0代表存在 2代表删除)', + create_by varchar(64) default '' comment '创建者', + create_time datetime comment '创建时间', + update_by varchar(64) default '' comment '更新者', + update_time datetime comment '更新时间', + primary key (dept_id) +) engine=innodb auto_increment=200 comment = '部门表'; + +-- ---------------------------- +-- 初始化-部门表数据 +-- ---------------------------- +insert into sys_dept values(100, 0, '0', '若依科技', 0, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', sysdate(), '', null); +insert into sys_dept values(101, 100, '0,100', '深圳总公司', 1, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', sysdate(), '', null); +insert into sys_dept values(102, 100, '0,100', '长沙分公司', 2, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', sysdate(), '', null); +insert into sys_dept values(103, 101, '0,100,101', '研发部门', 1, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', sysdate(), '', null); +insert into sys_dept values(104, 101, '0,100,101', '市场部门', 2, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', sysdate(), '', null); +insert into sys_dept values(105, 101, '0,100,101', '测试部门', 3, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', sysdate(), '', null); +insert into sys_dept values(106, 101, '0,100,101', '财务部门', 4, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', sysdate(), '', null); +insert into sys_dept values(107, 101, '0,100,101', '运维部门', 5, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', sysdate(), '', null); +insert into sys_dept values(108, 102, '0,100,102', '市场部门', 1, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', sysdate(), '', null); +insert into sys_dept values(109, 102, '0,100,102', '财务部门', 2, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', sysdate(), '', null); + + +-- ---------------------------- +-- 2、用户信息表 +-- ---------------------------- +drop table if exists sys_user; +create table sys_user ( + user_id bigint(20) not null auto_increment comment '用户ID', + dept_id bigint(20) default null comment '部门ID', + user_name varchar(30) not null comment '用户账号', + nick_name varchar(30) not null comment '用户昵称', + user_type varchar(2) default '00' comment '用户类型(00系统用户)', + email varchar(50) default '' comment '用户邮箱', + phonenumber varchar(11) default '' comment '手机号码', + sex char(1) default '0' comment '用户性别(0男 1女 2未知)', + avatar varchar(100) default '' comment '头像地址', + password varchar(100) default '' comment '密码', + status char(1) default '0' comment '帐号状态(0正常 1停用)', + del_flag char(1) default '0' comment '删除标志(0代表存在 2代表删除)', + login_ip varchar(128) default '' comment '最后登录IP', + login_date datetime comment '最后登录时间', + create_by varchar(64) default '' comment '创建者', + create_time datetime comment '创建时间', + update_by varchar(64) default '' comment '更新者', + update_time datetime comment '更新时间', + remark varchar(500) default null comment '备注', + primary key (user_id) +) engine=innodb auto_increment=100 comment = '用户信息表'; + +-- ---------------------------- +-- 初始化-用户信息表数据 +-- ---------------------------- +insert into sys_user values(1, 103, 'admin', '若依', '00', 'ry@163.com', '15888888888', '1', '', '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '127.0.0.1', sysdate(), 'admin', sysdate(), '', null, '管理员'); +insert into sys_user values(2, 105, 'ry', '若依', '00', 'ry@qq.com', '15666666666', '1', '', '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '127.0.0.1', sysdate(), 'admin', sysdate(), '', null, '测试员'); + + +-- ---------------------------- +-- 3、岗位信息表 +-- ---------------------------- +drop table if exists sys_post; +create table sys_post +( + post_id bigint(20) not null auto_increment comment '岗位ID', + post_code varchar(64) not null comment '岗位编码', + post_name varchar(50) not null comment '岗位名称', + post_sort int(4) not null comment '显示顺序', + status char(1) not null comment '状态(0正常 1停用)', + create_by varchar(64) default '' comment '创建者', + create_time datetime comment '创建时间', + update_by varchar(64) default '' comment '更新者', + update_time datetime comment '更新时间', + remark varchar(500) default null comment '备注', + primary key (post_id) +) engine=innodb comment = '岗位信息表'; + +-- ---------------------------- +-- 初始化-岗位信息表数据 +-- ---------------------------- +insert into sys_post values(1, 'ceo', '董事长', 1, '0', 'admin', sysdate(), '', null, ''); +insert into sys_post values(2, 'se', '项目经理', 2, '0', 'admin', sysdate(), '', null, ''); +insert into sys_post values(3, 'hr', '人力资源', 3, '0', 'admin', sysdate(), '', null, ''); +insert into sys_post values(4, 'user', '普通员工', 4, '0', 'admin', sysdate(), '', null, ''); + + +-- ---------------------------- +-- 4、角色信息表 +-- ---------------------------- +drop table if exists sys_role; +create table sys_role ( + role_id bigint(20) not null auto_increment comment '角色ID', + role_name varchar(30) not null comment '角色名称', + role_key varchar(100) not null comment '角色权限字符串', + role_sort int(4) not null comment '显示顺序', + data_scope char(1) default '1' comment '数据范围(1:全部数据权限 2:自定数据权限 3:本部门数据权限 4:本部门及以下数据权限)', + menu_check_strictly tinyint(1) default 1 comment '菜单树选择项是否关联显示', + dept_check_strictly tinyint(1) default 1 comment '部门树选择项是否关联显示', + status char(1) not null comment '角色状态(0正常 1停用)', + del_flag char(1) default '0' comment '删除标志(0代表存在 2代表删除)', + create_by varchar(64) default '' comment '创建者', + create_time datetime comment '创建时间', + update_by varchar(64) default '' comment '更新者', + update_time datetime comment '更新时间', + remark varchar(500) default null comment '备注', + primary key (role_id) +) engine=innodb auto_increment=100 comment = '角色信息表'; + +-- ---------------------------- +-- 初始化-角色信息表数据 +-- ---------------------------- +insert into sys_role values('1', '超级管理员', 'admin', 1, 1, 1, 1, '0', '0', 'admin', sysdate(), '', null, '超级管理员'); +insert into sys_role values('2', '普通角色', 'common', 2, 2, 1, 1, '0', '0', 'admin', sysdate(), '', null, '普通角色'); + + +-- ---------------------------- +-- 5、菜单权限表 +-- ---------------------------- +drop table if exists sys_menu; +create table sys_menu ( + menu_id bigint(20) not null auto_increment comment '菜单ID', + menu_name varchar(50) not null comment '菜单名称', + parent_id bigint(20) default 0 comment '父菜单ID', + order_num int(4) default 0 comment '显示顺序', + path varchar(200) default '' comment '路由地址', + component varchar(255) default null comment '组件路径', + query varchar(255) default null comment '路由参数', + is_frame int(1) default 1 comment '是否为外链(0是 1否)', + is_cache int(1) default 0 comment '是否缓存(0缓存 1不缓存)', + menu_type char(1) default '' comment '菜单类型(M目录 C菜单 F按钮)', + visible char(1) default 0 comment '菜单状态(0显示 1隐藏)', + status char(1) default 0 comment '菜单状态(0正常 1停用)', + perms varchar(100) default null comment '权限标识', + icon varchar(100) default '#' comment '菜单图标', + create_by varchar(64) default '' comment '创建者', + create_time datetime comment '创建时间', + update_by varchar(64) default '' comment '更新者', + update_time datetime comment '更新时间', + remark varchar(500) default '' comment '备注', + primary key (menu_id) +) engine=innodb auto_increment=2000 comment = '菜单权限表'; + +-- ---------------------------- +-- 初始化-菜单信息表数据 +-- ---------------------------- +-- 一级菜单 +insert into sys_menu values('1', '系统管理', '0', '1', 'system', null, '', 1, 0, 'M', '0', '0', '', 'system', 'admin', sysdate(), '', null, '系统管理目录'); +insert into sys_menu values('2', '系统监控', '0', '2', 'monitor', null, '', 1, 0, 'M', '0', '0', '', 'monitor', 'admin', sysdate(), '', null, '系统监控目录'); +insert into sys_menu values('3', '系统工具', '0', '3', 'tool', null, '', 1, 0, 'M', '0', '0', '', 'tool', 'admin', sysdate(), '', null, '系统工具目录'); +insert into sys_menu values('4', '若依官网', '0', '4', 'http://ruoyi.vip', null, '', 0, 0, 'M', '0', '0', '', 'guide', 'admin', sysdate(), '', null, '若依官网地址'); +-- 二级菜单 +insert into sys_menu values('100', '用户管理', '1', '1', 'user', 'system/user/index', '', 1, 0, 'C', '0', '0', 'system:user:list', 'user', 'admin', sysdate(), '', null, '用户管理菜单'); +insert into sys_menu values('101', '角色管理', '1', '2', 'role', 'system/role/index', '', 1, 0, 'C', '0', '0', 'system:role:list', 'peoples', 'admin', sysdate(), '', null, '角色管理菜单'); +insert into sys_menu values('102', '菜单管理', '1', '3', 'menu', 'system/menu/index', '', 1, 0, 'C', '0', '0', 'system:menu:list', 'tree-table', 'admin', sysdate(), '', null, '菜单管理菜单'); +insert into sys_menu values('103', '部门管理', '1', '4', 'dept', 'system/dept/index', '', 1, 0, 'C', '0', '0', 'system:dept:list', 'tree', 'admin', sysdate(), '', null, '部门管理菜单'); +insert into sys_menu values('104', '岗位管理', '1', '5', 'post', 'system/post/index', '', 1, 0, 'C', '0', '0', 'system:post:list', 'post', 'admin', sysdate(), '', null, '岗位管理菜单'); +insert into sys_menu values('105', '字典管理', '1', '6', 'dict', 'system/dict/index', '', 1, 0, 'C', '0', '0', 'system:dict:list', 'dict', 'admin', sysdate(), '', null, '字典管理菜单'); +insert into sys_menu values('106', '参数设置', '1', '7', 'config', 'system/config/index', '', 1, 0, 'C', '0', '0', 'system:config:list', 'edit', 'admin', sysdate(), '', null, '参数设置菜单'); +insert into sys_menu values('107', '通知公告', '1', '8', 'notice', 'system/notice/index', '', 1, 0, 'C', '0', '0', 'system:notice:list', 'message', 'admin', sysdate(), '', null, '通知公告菜单'); +insert into sys_menu values('108', '日志管理', '1', '9', 'log', '', '', 1, 0, 'M', '0', '0', '', 'log', 'admin', sysdate(), '', null, '日志管理菜单'); +insert into sys_menu values('109', '在线用户', '2', '1', 'online', 'monitor/online/index', '', 1, 0, 'C', '0', '0', 'monitor:online:list', 'online', 'admin', sysdate(), '', null, '在线用户菜单'); +insert into sys_menu values('110', '定时任务', '2', '2', 'job', 'monitor/job/index', '', 1, 0, 'C', '0', '0', 'monitor:job:list', 'job', 'admin', sysdate(), '', null, '定时任务菜单'); +insert into sys_menu values('111', '数据监控', '2', '3', 'druid', 'monitor/druid/index', '', 1, 0, 'C', '0', '0', 'monitor:druid:list', 'druid', 'admin', sysdate(), '', null, '数据监控菜单'); +insert into sys_menu values('112', '服务监控', '2', '4', 'server', 'monitor/server/index', '', 1, 0, 'C', '0', '0', 'monitor:server:list', 'server', 'admin', sysdate(), '', null, '服务监控菜单'); +insert into sys_menu values('113', '缓存监控', '2', '5', 'cache', 'monitor/cache/index', '', 1, 0, 'C', '0', '0', 'monitor:cache:list', 'redis', 'admin', sysdate(), '', null, '缓存监控菜单'); +insert into sys_menu values('114', '缓存列表', '2', '6', 'cacheList', 'monitor/cache/list', '', 1, 0, 'C', '0', '0', 'monitor:cache:list', 'redis-list', 'admin', sysdate(), '', null, '缓存列表菜单'); +insert into sys_menu values('115', '表单构建', '3', '1', 'build', 'tool/build/index', '', 1, 0, 'C', '0', '0', 'tool:build:list', 'build', 'admin', sysdate(), '', null, '表单构建菜单'); +insert into sys_menu values('116', '代码生成', '3', '2', 'gen', 'tool/gen/index', '', 1, 0, 'C', '0', '0', 'tool:gen:list', 'code', 'admin', sysdate(), '', null, '代码生成菜单'); +insert into sys_menu values('117', '系统接口', '3', '3', 'swagger', 'tool/swagger/index', '', 1, 0, 'C', '0', '0', 'tool:swagger:list', 'swagger', 'admin', sysdate(), '', null, '系统接口菜单'); +-- 三级菜单 +insert into sys_menu values('500', '操作日志', '108', '1', 'operlog', 'monitor/operlog/index', '', 1, 0, 'C', '0', '0', 'monitor:operlog:list', 'form', 'admin', sysdate(), '', null, '操作日志菜单'); +insert into sys_menu values('501', '登录日志', '108', '2', 'logininfor', 'monitor/logininfor/index', '', 1, 0, 'C', '0', '0', 'monitor:logininfor:list', 'logininfor', 'admin', sysdate(), '', null, '登录日志菜单'); +-- 用户管理按钮 +insert into sys_menu values('1000', '用户查询', '100', '1', '', '', '', 1, 0, 'F', '0', '0', 'system:user:query', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1001', '用户新增', '100', '2', '', '', '', 1, 0, 'F', '0', '0', 'system:user:add', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1002', '用户修改', '100', '3', '', '', '', 1, 0, 'F', '0', '0', 'system:user:edit', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1003', '用户删除', '100', '4', '', '', '', 1, 0, 'F', '0', '0', 'system:user:remove', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1004', '用户导出', '100', '5', '', '', '', 1, 0, 'F', '0', '0', 'system:user:export', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1005', '用户导入', '100', '6', '', '', '', 1, 0, 'F', '0', '0', 'system:user:import', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1006', '重置密码', '100', '7', '', '', '', 1, 0, 'F', '0', '0', 'system:user:resetPwd', '#', 'admin', sysdate(), '', null, ''); +-- 角色管理按钮 +insert into sys_menu values('1007', '角色查询', '101', '1', '', '', '', 1, 0, 'F', '0', '0', 'system:role:query', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1008', '角色新增', '101', '2', '', '', '', 1, 0, 'F', '0', '0', 'system:role:add', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1009', '角色修改', '101', '3', '', '', '', 1, 0, 'F', '0', '0', 'system:role:edit', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1010', '角色删除', '101', '4', '', '', '', 1, 0, 'F', '0', '0', 'system:role:remove', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1011', '角色导出', '101', '5', '', '', '', 1, 0, 'F', '0', '0', 'system:role:export', '#', 'admin', sysdate(), '', null, ''); +-- 菜单管理按钮 +insert into sys_menu values('1012', '菜单查询', '102', '1', '', '', '', 1, 0, 'F', '0', '0', 'system:menu:query', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1013', '菜单新增', '102', '2', '', '', '', 1, 0, 'F', '0', '0', 'system:menu:add', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1014', '菜单修改', '102', '3', '', '', '', 1, 0, 'F', '0', '0', 'system:menu:edit', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1015', '菜单删除', '102', '4', '', '', '', 1, 0, 'F', '0', '0', 'system:menu:remove', '#', 'admin', sysdate(), '', null, ''); +-- 部门管理按钮 +insert into sys_menu values('1016', '部门查询', '103', '1', '', '', '', 1, 0, 'F', '0', '0', 'system:dept:query', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1017', '部门新增', '103', '2', '', '', '', 1, 0, 'F', '0', '0', 'system:dept:add', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1018', '部门修改', '103', '3', '', '', '', 1, 0, 'F', '0', '0', 'system:dept:edit', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1019', '部门删除', '103', '4', '', '', '', 1, 0, 'F', '0', '0', 'system:dept:remove', '#', 'admin', sysdate(), '', null, ''); +-- 岗位管理按钮 +insert into sys_menu values('1020', '岗位查询', '104', '1', '', '', '', 1, 0, 'F', '0', '0', 'system:post:query', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1021', '岗位新增', '104', '2', '', '', '', 1, 0, 'F', '0', '0', 'system:post:add', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1022', '岗位修改', '104', '3', '', '', '', 1, 0, 'F', '0', '0', 'system:post:edit', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1023', '岗位删除', '104', '4', '', '', '', 1, 0, 'F', '0', '0', 'system:post:remove', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1024', '岗位导出', '104', '5', '', '', '', 1, 0, 'F', '0', '0', 'system:post:export', '#', 'admin', sysdate(), '', null, ''); +-- 字典管理按钮 +insert into sys_menu values('1025', '字典查询', '105', '1', '#', '', '', 1, 0, 'F', '0', '0', 'system:dict:query', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1026', '字典新增', '105', '2', '#', '', '', 1, 0, 'F', '0', '0', 'system:dict:add', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1027', '字典修改', '105', '3', '#', '', '', 1, 0, 'F', '0', '0', 'system:dict:edit', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1028', '字典删除', '105', '4', '#', '', '', 1, 0, 'F', '0', '0', 'system:dict:remove', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1029', '字典导出', '105', '5', '#', '', '', 1, 0, 'F', '0', '0', 'system:dict:export', '#', 'admin', sysdate(), '', null, ''); +-- 参数设置按钮 +insert into sys_menu values('1030', '参数查询', '106', '1', '#', '', '', 1, 0, 'F', '0', '0', 'system:config:query', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1031', '参数新增', '106', '2', '#', '', '', 1, 0, 'F', '0', '0', 'system:config:add', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1032', '参数修改', '106', '3', '#', '', '', 1, 0, 'F', '0', '0', 'system:config:edit', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1033', '参数删除', '106', '4', '#', '', '', 1, 0, 'F', '0', '0', 'system:config:remove', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1034', '参数导出', '106', '5', '#', '', '', 1, 0, 'F', '0', '0', 'system:config:export', '#', 'admin', sysdate(), '', null, ''); +-- 通知公告按钮 +insert into sys_menu values('1035', '公告查询', '107', '1', '#', '', '', 1, 0, 'F', '0', '0', 'system:notice:query', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1036', '公告新增', '107', '2', '#', '', '', 1, 0, 'F', '0', '0', 'system:notice:add', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1037', '公告修改', '107', '3', '#', '', '', 1, 0, 'F', '0', '0', 'system:notice:edit', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1038', '公告删除', '107', '4', '#', '', '', 1, 0, 'F', '0', '0', 'system:notice:remove', '#', 'admin', sysdate(), '', null, ''); +-- 操作日志按钮 +insert into sys_menu values('1039', '操作查询', '500', '1', '#', '', '', 1, 0, 'F', '0', '0', 'monitor:operlog:query', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1040', '操作删除', '500', '2', '#', '', '', 1, 0, 'F', '0', '0', 'monitor:operlog:remove', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1041', '日志导出', '500', '3', '#', '', '', 1, 0, 'F', '0', '0', 'monitor:operlog:export', '#', 'admin', sysdate(), '', null, ''); +-- 登录日志按钮 +insert into sys_menu values('1042', '登录查询', '501', '1', '#', '', '', 1, 0, 'F', '0', '0', 'monitor:logininfor:query', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1043', '登录删除', '501', '2', '#', '', '', 1, 0, 'F', '0', '0', 'monitor:logininfor:remove', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1044', '日志导出', '501', '3', '#', '', '', 1, 0, 'F', '0', '0', 'monitor:logininfor:export', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1045', '账户解锁', '501', '4', '#', '', '', 1, 0, 'F', '0', '0', 'monitor:logininfor:unlock', '#', 'admin', sysdate(), '', null, ''); +-- 在线用户按钮 +insert into sys_menu values('1046', '在线查询', '109', '1', '#', '', '', 1, 0, 'F', '0', '0', 'monitor:online:query', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1047', '批量强退', '109', '2', '#', '', '', 1, 0, 'F', '0', '0', 'monitor:online:batchLogout', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1048', '单条强退', '109', '3', '#', '', '', 1, 0, 'F', '0', '0', 'monitor:online:forceLogout', '#', 'admin', sysdate(), '', null, ''); +-- 定时任务按钮 +insert into sys_menu values('1049', '任务查询', '110', '1', '#', '', '', 1, 0, 'F', '0', '0', 'monitor:job:query', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1050', '任务新增', '110', '2', '#', '', '', 1, 0, 'F', '0', '0', 'monitor:job:add', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1051', '任务修改', '110', '3', '#', '', '', 1, 0, 'F', '0', '0', 'monitor:job:edit', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1052', '任务删除', '110', '4', '#', '', '', 1, 0, 'F', '0', '0', 'monitor:job:remove', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1053', '状态修改', '110', '5', '#', '', '', 1, 0, 'F', '0', '0', 'monitor:job:changeStatus', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1054', '任务导出', '110', '6', '#', '', '', 1, 0, 'F', '0', '0', 'monitor:job:export', '#', 'admin', sysdate(), '', null, ''); +-- 代码生成按钮 +insert into sys_menu values('1055', '生成查询', '116', '1', '#', '', '', 1, 0, 'F', '0', '0', 'tool:gen:query', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1056', '生成修改', '116', '2', '#', '', '', 1, 0, 'F', '0', '0', 'tool:gen:edit', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1057', '生成删除', '116', '3', '#', '', '', 1, 0, 'F', '0', '0', 'tool:gen:remove', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1058', '导入代码', '116', '4', '#', '', '', 1, 0, 'F', '0', '0', 'tool:gen:import', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1059', '预览代码', '116', '5', '#', '', '', 1, 0, 'F', '0', '0', 'tool:gen:preview', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1060', '生成代码', '116', '6', '#', '', '', 1, 0, 'F', '0', '0', 'tool:gen:code', '#', 'admin', sysdate(), '', null, ''); + + +-- ---------------------------- +-- 6、用户和角色关联表 用户N-1角色 +-- ---------------------------- +drop table if exists sys_user_role; +create table sys_user_role ( + user_id bigint(20) not null comment '用户ID', + role_id bigint(20) not null comment '角色ID', + primary key(user_id, role_id) +) engine=innodb comment = '用户和角色关联表'; + +-- ---------------------------- +-- 初始化-用户和角色关联表数据 +-- ---------------------------- +insert into sys_user_role values ('1', '1'); +insert into sys_user_role values ('2', '2'); + + +-- ---------------------------- +-- 7、角色和菜单关联表 角色1-N菜单 +-- ---------------------------- +drop table if exists sys_role_menu; +create table sys_role_menu ( + role_id bigint(20) not null comment '角色ID', + menu_id bigint(20) not null comment '菜单ID', + primary key(role_id, menu_id) +) engine=innodb comment = '角色和菜单关联表'; + +-- ---------------------------- +-- 初始化-角色和菜单关联表数据 +-- ---------------------------- +insert into sys_role_menu values ('2', '1'); +insert into sys_role_menu values ('2', '2'); +insert into sys_role_menu values ('2', '3'); +insert into sys_role_menu values ('2', '4'); +insert into sys_role_menu values ('2', '100'); +insert into sys_role_menu values ('2', '101'); +insert into sys_role_menu values ('2', '102'); +insert into sys_role_menu values ('2', '103'); +insert into sys_role_menu values ('2', '104'); +insert into sys_role_menu values ('2', '105'); +insert into sys_role_menu values ('2', '106'); +insert into sys_role_menu values ('2', '107'); +insert into sys_role_menu values ('2', '108'); +insert into sys_role_menu values ('2', '109'); +insert into sys_role_menu values ('2', '110'); +insert into sys_role_menu values ('2', '111'); +insert into sys_role_menu values ('2', '112'); +insert into sys_role_menu values ('2', '113'); +insert into sys_role_menu values ('2', '114'); +insert into sys_role_menu values ('2', '115'); +insert into sys_role_menu values ('2', '116'); +insert into sys_role_menu values ('2', '117'); +insert into sys_role_menu values ('2', '500'); +insert into sys_role_menu values ('2', '501'); +insert into sys_role_menu values ('2', '1000'); +insert into sys_role_menu values ('2', '1001'); +insert into sys_role_menu values ('2', '1002'); +insert into sys_role_menu values ('2', '1003'); +insert into sys_role_menu values ('2', '1004'); +insert into sys_role_menu values ('2', '1005'); +insert into sys_role_menu values ('2', '1006'); +insert into sys_role_menu values ('2', '1007'); +insert into sys_role_menu values ('2', '1008'); +insert into sys_role_menu values ('2', '1009'); +insert into sys_role_menu values ('2', '1010'); +insert into sys_role_menu values ('2', '1011'); +insert into sys_role_menu values ('2', '1012'); +insert into sys_role_menu values ('2', '1013'); +insert into sys_role_menu values ('2', '1014'); +insert into sys_role_menu values ('2', '1015'); +insert into sys_role_menu values ('2', '1016'); +insert into sys_role_menu values ('2', '1017'); +insert into sys_role_menu values ('2', '1018'); +insert into sys_role_menu values ('2', '1019'); +insert into sys_role_menu values ('2', '1020'); +insert into sys_role_menu values ('2', '1021'); +insert into sys_role_menu values ('2', '1022'); +insert into sys_role_menu values ('2', '1023'); +insert into sys_role_menu values ('2', '1024'); +insert into sys_role_menu values ('2', '1025'); +insert into sys_role_menu values ('2', '1026'); +insert into sys_role_menu values ('2', '1027'); +insert into sys_role_menu values ('2', '1028'); +insert into sys_role_menu values ('2', '1029'); +insert into sys_role_menu values ('2', '1030'); +insert into sys_role_menu values ('2', '1031'); +insert into sys_role_menu values ('2', '1032'); +insert into sys_role_menu values ('2', '1033'); +insert into sys_role_menu values ('2', '1034'); +insert into sys_role_menu values ('2', '1035'); +insert into sys_role_menu values ('2', '1036'); +insert into sys_role_menu values ('2', '1037'); +insert into sys_role_menu values ('2', '1038'); +insert into sys_role_menu values ('2', '1039'); +insert into sys_role_menu values ('2', '1040'); +insert into sys_role_menu values ('2', '1041'); +insert into sys_role_menu values ('2', '1042'); +insert into sys_role_menu values ('2', '1043'); +insert into sys_role_menu values ('2', '1044'); +insert into sys_role_menu values ('2', '1045'); +insert into sys_role_menu values ('2', '1046'); +insert into sys_role_menu values ('2', '1047'); +insert into sys_role_menu values ('2', '1048'); +insert into sys_role_menu values ('2', '1049'); +insert into sys_role_menu values ('2', '1050'); +insert into sys_role_menu values ('2', '1051'); +insert into sys_role_menu values ('2', '1052'); +insert into sys_role_menu values ('2', '1053'); +insert into sys_role_menu values ('2', '1054'); +insert into sys_role_menu values ('2', '1055'); +insert into sys_role_menu values ('2', '1056'); +insert into sys_role_menu values ('2', '1057'); +insert into sys_role_menu values ('2', '1058'); +insert into sys_role_menu values ('2', '1059'); +insert into sys_role_menu values ('2', '1060'); + +-- ---------------------------- +-- 8、角色和部门关联表 角色1-N部门 +-- ---------------------------- +drop table if exists sys_role_dept; +create table sys_role_dept ( + role_id bigint(20) not null comment '角色ID', + dept_id bigint(20) not null comment '部门ID', + primary key(role_id, dept_id) +) engine=innodb comment = '角色和部门关联表'; + +-- ---------------------------- +-- 初始化-角色和部门关联表数据 +-- ---------------------------- +insert into sys_role_dept values ('2', '100'); +insert into sys_role_dept values ('2', '101'); +insert into sys_role_dept values ('2', '105'); + + +-- ---------------------------- +-- 9、用户与岗位关联表 用户1-N岗位 +-- ---------------------------- +drop table if exists sys_user_post; +create table sys_user_post +( + user_id bigint(20) not null comment '用户ID', + post_id bigint(20) not null comment '岗位ID', + primary key (user_id, post_id) +) engine=innodb comment = '用户与岗位关联表'; + +-- ---------------------------- +-- 初始化-用户与岗位关联表数据 +-- ---------------------------- +insert into sys_user_post values ('1', '1'); +insert into sys_user_post values ('2', '2'); + + +-- ---------------------------- +-- 10、操作日志记录 +-- ---------------------------- +drop table if exists sys_oper_log; +create table sys_oper_log ( + oper_id bigint(20) not null auto_increment comment '日志主键', + title varchar(50) default '' comment '模块标题', + business_type int(2) default 0 comment '业务类型(0其它 1新增 2修改 3删除)', + method varchar(100) default '' comment '方法名称', + request_method varchar(10) default '' comment '请求方式', + operator_type int(1) default 0 comment '操作类别(0其它 1后台用户 2手机端用户)', + oper_name varchar(50) default '' comment '操作人员', + dept_name varchar(50) default '' comment '部门名称', + oper_url varchar(255) default '' comment '请求URL', + oper_ip varchar(128) default '' comment '主机地址', + oper_location varchar(255) default '' comment '操作地点', + oper_param varchar(2000) default '' comment '请求参数', + json_result varchar(2000) default '' comment '返回参数', + status int(1) default 0 comment '操作状态(0正常 1异常)', + error_msg varchar(2000) default '' comment '错误消息', + oper_time datetime comment '操作时间', + cost_time bigint(20) default 0 comment '消耗时间', + primary key (oper_id), + key idx_sys_oper_log_bt (business_type), + key idx_sys_oper_log_s (status), + key idx_sys_oper_log_ot (oper_time) +) engine=innodb auto_increment=100 comment = '操作日志记录'; + + +-- ---------------------------- +-- 11、字典类型表 +-- ---------------------------- +drop table if exists sys_dict_type; +create table sys_dict_type +( + dict_id bigint(20) not null auto_increment comment '字典主键', + dict_name varchar(100) default '' comment '字典名称', + dict_type varchar(100) default '' comment '字典类型', + status char(1) default '0' comment '状态(0正常 1停用)', + create_by varchar(64) default '' comment '创建者', + create_time datetime comment '创建时间', + update_by varchar(64) default '' comment '更新者', + update_time datetime comment '更新时间', + remark varchar(500) default null comment '备注', + primary key (dict_id), + unique (dict_type) +) engine=innodb auto_increment=100 comment = '字典类型表'; + +insert into sys_dict_type values(1, '用户性别', 'sys_user_sex', '0', 'admin', sysdate(), '', null, '用户性别列表'); +insert into sys_dict_type values(2, '菜单状态', 'sys_show_hide', '0', 'admin', sysdate(), '', null, '菜单状态列表'); +insert into sys_dict_type values(3, '系统开关', 'sys_normal_disable', '0', 'admin', sysdate(), '', null, '系统开关列表'); +insert into sys_dict_type values(4, '任务状态', 'sys_job_status', '0', 'admin', sysdate(), '', null, '任务状态列表'); +insert into sys_dict_type values(5, '任务分组', 'sys_job_group', '0', 'admin', sysdate(), '', null, '任务分组列表'); +insert into sys_dict_type values(6, '系统是否', 'sys_yes_no', '0', 'admin', sysdate(), '', null, '系统是否列表'); +insert into sys_dict_type values(7, '通知类型', 'sys_notice_type', '0', 'admin', sysdate(), '', null, '通知类型列表'); +insert into sys_dict_type values(8, '通知状态', 'sys_notice_status', '0', 'admin', sysdate(), '', null, '通知状态列表'); +insert into sys_dict_type values(9, '操作类型', 'sys_oper_type', '0', 'admin', sysdate(), '', null, '操作类型列表'); +insert into sys_dict_type values(10, '系统状态', 'sys_common_status', '0', 'admin', sysdate(), '', null, '登录状态列表'); + + +-- ---------------------------- +-- 12、字典数据表 +-- ---------------------------- +drop table if exists sys_dict_data; +create table sys_dict_data +( + dict_code bigint(20) not null auto_increment comment '字典编码', + dict_sort int(4) default 0 comment '字典排序', + dict_label varchar(100) default '' comment '字典标签', + dict_value varchar(100) default '' comment '字典键值', + dict_type varchar(100) default '' comment '字典类型', + css_class varchar(100) default null comment '样式属性(其他样式扩展)', + list_class varchar(100) default null comment '表格回显样式', + is_default char(1) default 'N' comment '是否默认(Y是 N否)', + status char(1) default '0' comment '状态(0正常 1停用)', + create_by varchar(64) default '' comment '创建者', + create_time datetime comment '创建时间', + update_by varchar(64) default '' comment '更新者', + update_time datetime comment '更新时间', + remark varchar(500) default null comment '备注', + primary key (dict_code) +) engine=innodb auto_increment=100 comment = '字典数据表'; + +insert into sys_dict_data values(1, 1, '男', '0', 'sys_user_sex', '', '', 'Y', '0', 'admin', sysdate(), '', null, '性别男'); +insert into sys_dict_data values(2, 2, '女', '1', 'sys_user_sex', '', '', 'N', '0', 'admin', sysdate(), '', null, '性别女'); +insert into sys_dict_data values(3, 3, '未知', '2', 'sys_user_sex', '', '', 'N', '0', 'admin', sysdate(), '', null, '性别未知'); +insert into sys_dict_data values(4, 1, '显示', '0', 'sys_show_hide', '', 'primary', 'Y', '0', 'admin', sysdate(), '', null, '显示菜单'); +insert into sys_dict_data values(5, 2, '隐藏', '1', 'sys_show_hide', '', 'danger', 'N', '0', 'admin', sysdate(), '', null, '隐藏菜单'); +insert into sys_dict_data values(6, 1, '正常', '0', 'sys_normal_disable', '', 'primary', 'Y', '0', 'admin', sysdate(), '', null, '正常状态'); +insert into sys_dict_data values(7, 2, '停用', '1', 'sys_normal_disable', '', 'danger', 'N', '0', 'admin', sysdate(), '', null, '停用状态'); +insert into sys_dict_data values(8, 1, '正常', '0', 'sys_job_status', '', 'primary', 'Y', '0', 'admin', sysdate(), '', null, '正常状态'); +insert into sys_dict_data values(9, 2, '暂停', '1', 'sys_job_status', '', 'danger', 'N', '0', 'admin', sysdate(), '', null, '停用状态'); +insert into sys_dict_data values(10, 1, '默认', 'DEFAULT', 'sys_job_group', '', '', 'Y', '0', 'admin', sysdate(), '', null, '默认分组'); +insert into sys_dict_data values(11, 2, '系统', 'SYSTEM', 'sys_job_group', '', '', 'N', '0', 'admin', sysdate(), '', null, '系统分组'); +insert into sys_dict_data values(12, 1, '是', 'Y', 'sys_yes_no', '', 'primary', 'Y', '0', 'admin', sysdate(), '', null, '系统默认是'); +insert into sys_dict_data values(13, 2, '否', 'N', 'sys_yes_no', '', 'danger', 'N', '0', 'admin', sysdate(), '', null, '系统默认否'); +insert into sys_dict_data values(14, 1, '通知', '1', 'sys_notice_type', '', 'warning', 'Y', '0', 'admin', sysdate(), '', null, '通知'); +insert into sys_dict_data values(15, 2, '公告', '2', 'sys_notice_type', '', 'success', 'N', '0', 'admin', sysdate(), '', null, '公告'); +insert into sys_dict_data values(16, 1, '正常', '0', 'sys_notice_status', '', 'primary', 'Y', '0', 'admin', sysdate(), '', null, '正常状态'); +insert into sys_dict_data values(17, 2, '关闭', '1', 'sys_notice_status', '', 'danger', 'N', '0', 'admin', sysdate(), '', null, '关闭状态'); +insert into sys_dict_data values(18, 99, '其他', '0', 'sys_oper_type', '', 'info', 'N', '0', 'admin', sysdate(), '', null, '其他操作'); +insert into sys_dict_data values(19, 1, '新增', '1', 'sys_oper_type', '', 'info', 'N', '0', 'admin', sysdate(), '', null, '新增操作'); +insert into sys_dict_data values(20, 2, '修改', '2', 'sys_oper_type', '', 'info', 'N', '0', 'admin', sysdate(), '', null, '修改操作'); +insert into sys_dict_data values(21, 3, '删除', '3', 'sys_oper_type', '', 'danger', 'N', '0', 'admin', sysdate(), '', null, '删除操作'); +insert into sys_dict_data values(22, 4, '授权', '4', 'sys_oper_type', '', 'primary', 'N', '0', 'admin', sysdate(), '', null, '授权操作'); +insert into sys_dict_data values(23, 5, '导出', '5', 'sys_oper_type', '', 'warning', 'N', '0', 'admin', sysdate(), '', null, '导出操作'); +insert into sys_dict_data values(24, 6, '导入', '6', 'sys_oper_type', '', 'warning', 'N', '0', 'admin', sysdate(), '', null, '导入操作'); +insert into sys_dict_data values(25, 7, '强退', '7', 'sys_oper_type', '', 'danger', 'N', '0', 'admin', sysdate(), '', null, '强退操作'); +insert into sys_dict_data values(26, 8, '生成代码', '8', 'sys_oper_type', '', 'warning', 'N', '0', 'admin', sysdate(), '', null, '生成操作'); +insert into sys_dict_data values(27, 9, '清空数据', '9', 'sys_oper_type', '', 'danger', 'N', '0', 'admin', sysdate(), '', null, '清空操作'); +insert into sys_dict_data values(28, 1, '成功', '0', 'sys_common_status', '', 'primary', 'N', '0', 'admin', sysdate(), '', null, '正常状态'); +insert into sys_dict_data values(29, 2, '失败', '1', 'sys_common_status', '', 'danger', 'N', '0', 'admin', sysdate(), '', null, '停用状态'); + + +-- ---------------------------- +-- 13、参数配置表 +-- ---------------------------- +drop table if exists sys_config; +create table sys_config ( + config_id int(5) not null auto_increment comment '参数主键', + config_name varchar(100) default '' comment '参数名称', + config_key varchar(100) default '' comment '参数键名', + config_value varchar(500) default '' comment '参数键值', + config_type char(1) default 'N' comment '系统内置(Y是 N否)', + create_by varchar(64) default '' comment '创建者', + create_time datetime comment '创建时间', + update_by varchar(64) default '' comment '更新者', + update_time datetime comment '更新时间', + remark varchar(500) default null comment '备注', + 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.captchaEnabled', 'true', 'Y', 'admin', sysdate(), '', null, '是否开启验证码功能(true开启,false关闭)'); +insert into sys_config values(5, '账号自助-是否开启用户注册功能', 'sys.account.registerUser', 'false', 'Y', 'admin', sysdate(), '', null, '是否开启注册用户功能(true开启,false关闭)'); +insert into sys_config values(6, '用户登录-黑名单列表', 'sys.login.blackIPList', '', 'Y', 'admin', sysdate(), '', null, '设置登录IP黑名单限制,多个匹配项以;分隔,支持匹配(*通配、网段)'); + + +-- ---------------------------- +-- 14、系统访问记录 +-- ---------------------------- +drop table if exists sys_logininfor; +create table sys_logininfor ( + info_id bigint(20) not null auto_increment comment '访问ID', + user_name varchar(50) default '' comment '用户账号', + ipaddr varchar(128) default '' comment '登录IP地址', + login_location varchar(255) default '' comment '登录地点', + browser varchar(50) default '' comment '浏览器类型', + os varchar(50) default '' comment '操作系统', + status char(1) default '0' comment '登录状态(0成功 1失败)', + msg varchar(255) default '' comment '提示消息', + login_time datetime comment '访问时间', + primary key (info_id), + key idx_sys_logininfor_s (status), + key idx_sys_logininfor_lt (login_time) +) engine=innodb auto_increment=100 comment = '系统访问记录'; + + +-- ---------------------------- +-- 15、定时任务调度表 +-- ---------------------------- +drop table if exists sys_job; +create table sys_job ( + job_id bigint(20) not null auto_increment comment '任务ID', + job_name varchar(64) default '' comment '任务名称', + job_group varchar(64) default 'DEFAULT' comment '任务组名', + invoke_target varchar(500) not null comment '调用目标字符串', + cron_expression varchar(255) default '' comment 'cron执行表达式', + misfire_policy varchar(20) default '3' comment '计划执行错误策略(1立即执行 2执行一次 3放弃执行)', + concurrent char(1) default '1' comment '是否并发执行(0允许 1禁止)', + status char(1) default '0' comment '状态(0正常 1暂停)', + create_by varchar(64) default '' comment '创建者', + create_time datetime comment '创建时间', + update_by varchar(64) default '' comment '更新者', + update_time datetime comment '更新时间', + remark varchar(500) default '' comment '备注信息', + primary key (job_id, job_name, job_group) +) engine=innodb auto_increment=100 comment = '定时任务调度表'; + +insert into sys_job values(1, '系统默认(无参)', 'DEFAULT', 'ryTask.ryNoParams', '0/10 * * * * ?', '3', '1', '1', 'admin', sysdate(), '', null, ''); +insert into sys_job values(2, '系统默认(有参)', 'DEFAULT', 'ryTask.ryParams(\'ry\')', '0/15 * * * * ?', '3', '1', '1', 'admin', sysdate(), '', null, ''); +insert into sys_job values(3, '系统默认(多参)', 'DEFAULT', 'ryTask.ryMultipleParams(\'ry\', true, 2000L, 316.50D, 100)', '0/20 * * * * ?', '3', '1', '1', 'admin', sysdate(), '', null, ''); + + +-- ---------------------------- +-- 16、定时任务调度日志表 +-- ---------------------------- +drop table if exists sys_job_log; +create table sys_job_log ( + job_log_id bigint(20) not null auto_increment comment '任务日志ID', + job_name varchar(64) not null comment '任务名称', + job_group varchar(64) not null comment '任务组名', + invoke_target varchar(500) not null comment '调用目标字符串', + job_message varchar(500) comment '日志信息', + status char(1) default '0' comment '执行状态(0正常 1失败)', + exception_info varchar(2000) default '' comment '异常信息', + create_time datetime comment '创建时间', + primary key (job_log_id) +) engine=innodb comment = '定时任务调度日志表'; + + +-- ---------------------------- +-- 17、通知公告表 +-- ---------------------------- +drop table if exists sys_notice; +create table sys_notice ( + notice_id int(4) not null auto_increment comment '公告ID', + notice_title varchar(50) not null comment '公告标题', + notice_type char(1) not null comment '公告类型(1通知 2公告)', + notice_content longblob default null comment '公告内容', + status char(1) default '0' comment '公告状态(0正常 1关闭)', + create_by varchar(64) default '' comment '创建者', + create_time datetime comment '创建时间', + update_by varchar(64) default '' comment '更新者', + update_time datetime comment '更新时间', + remark varchar(255) default null comment '备注', + primary key (notice_id) +) engine=innodb auto_increment=10 comment = '通知公告表'; + +-- ---------------------------- +-- 初始化-公告信息表数据 +-- ---------------------------- +insert into sys_notice values('1', '温馨提醒:2018-07-01 若依新版本发布啦', '2', '新版本内容', '0', 'admin', sysdate(), '', null, '管理员'); +insert into sys_notice values('2', '维护通知:2018-07-01 若依系统凌晨维护', '1', '维护内容', '0', 'admin', sysdate(), '', null, '管理员'); + + +-- ---------------------------- +-- 18、代码生成业务表 +-- ---------------------------- +drop table if exists gen_table; +create table gen_table ( + table_id bigint(20) not null auto_increment comment '编号', + table_name varchar(200) default '' comment '表名称', + table_comment varchar(500) default '' comment '表描述', + sub_table_name varchar(64) default null comment '关联子表的表名', + sub_table_fk_name varchar(64) default null comment '子表关联的外键名', + class_name varchar(100) default '' comment '实体类名称', + tpl_category varchar(200) default 'crud' comment '使用的模板(crud单表操作 tree树表操作)', + tpl_web_type varchar(30) default '' comment '前端模板类型(element-ui模版 element-plus模版)', + package_name varchar(100) comment '生成包路径', + module_name varchar(30) comment '生成模块名', + business_name varchar(30) comment '生成业务名', + function_name varchar(50) comment '生成功能名', + function_author varchar(50) comment '生成功能作者', + gen_type char(1) default '0' comment '生成代码方式(0zip压缩包 1自定义路径)', + gen_path varchar(200) default '/' comment '生成路径(不填默认项目路径)', + options varchar(1000) comment '其它生成选项', + create_by varchar(64) default '' comment '创建者', + create_time datetime comment '创建时间', + update_by varchar(64) default '' comment '更新者', + update_time datetime comment '更新时间', + remark varchar(500) default null comment '备注', + primary key (table_id) +) engine=innodb auto_increment=1 comment = '代码生成业务表'; + + +-- ---------------------------- +-- 19、代码生成业务表字段 +-- ---------------------------- +drop table if exists gen_table_column; +create table gen_table_column ( + column_id bigint(20) not null auto_increment comment '编号', + table_id bigint(20) comment '归属表编号', + column_name varchar(200) comment '列名称', + column_comment varchar(500) comment '列描述', + column_type varchar(100) comment '列类型', + java_type varchar(500) comment 'JAVA类型', + java_field varchar(200) comment 'JAVA字段名', + is_pk char(1) comment '是否主键(1是)', + is_increment char(1) comment '是否自增(1是)', + is_required char(1) comment '是否必填(1是)', + is_insert char(1) comment '是否为插入字段(1是)', + is_edit char(1) comment '是否编辑字段(1是)', + is_list char(1) comment '是否列表字段(1是)', + is_query char(1) comment '是否查询字段(1是)', + query_type varchar(200) default 'EQ' comment '查询方式(等于、不等于、大于、小于、范围)', + html_type varchar(200) comment '显示类型(文本框、文本域、下拉框、复选框、单选框、日期控件)', + dict_type varchar(200) default '' comment '字典类型', + sort int comment '排序', + create_by varchar(64) default '' comment '创建者', + create_time datetime comment '创建时间', + update_by varchar(64) default '' comment '更新者', + update_time datetime comment '更新时间', + primary key (column_id) +) engine=innodb auto_increment=1 comment = '代码生成业务表字段'; \ No newline at end of file diff --git a/src/main/java/com/ruoyi/RuoYiApplication.java b/src/main/java/com/ruoyi/RuoYiApplication.java new file mode 100644 index 0000000..cc2efb9 --- /dev/null +++ b/src/main/java/com/ruoyi/RuoYiApplication.java @@ -0,0 +1,49 @@ +package com.ruoyi; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.core.env.Environment; +import org.springframework.scheduling.annotation.EnableScheduling; + +import java.net.InetAddress; +import java.net.UnknownHostException; + +/** + * 启动程序 + * + * @author ruoyi + */ +@Slf4j +@EnableScheduling +@SpringBootApplication(exclude = { DataSourceAutoConfiguration.class }) +public class RuoYiApplication +{ + public static void main(String[] args) throws UnknownHostException { + ConfigurableApplicationContext application = SpringApplication.run(RuoYiApplication.class, args); + Environment env = application.getEnvironment(); + String ip = InetAddress.getLocalHost().getHostAddress(); + String port = env.getProperty("server.port"); + // System.setProperty("spring.devtools.restart.enabled", "false"); +// SpringApplication.run(RuoYiApplication.class, args); + System.out.println(" ████████ ██ ██████ ██ ████████ ██ ██ \n" + + " ██░░░░░░ ██████ ░░ █████ ░█░░░░██ ░██ ██░░░░░░ ░██ ░██ \n" + + "░██ ░██░░░██ ██████ ██ ███████ ██░░░██░█ ░██ ██████ ██████ ██████░██ ██████ ██████ ██████ ██████\n" + + "░█████████░██ ░██░░██░░█░██░░██░░░██░██ ░██░██████ ██░░░░██ ██░░░░██░░░██░ ░█████████░░░██░ ░░░░░░██ ░░██░░█░░░██░ \n" + + "░░░░░░░░██░██████ ░██ ░ ░██ ░██ ░██░░██████░█░░░░ ██░██ ░██░██ ░██ ░██ ░░░░░░░░██ ░██ ███████ ░██ ░ ░██ \n" + + " ░██░██░░░ ░██ ░██ ░██ ░██ ░░░░░██░█ ░██░██ ░██░██ ░██ ░██ ░██ ░██ ██░░░░██ ░██ ░██ \n" + + " ████████ ░██ ░███ ░██ ███ ░██ █████ ░███████ ░░██████ ░░██████ ░░██ ████████ ░░██ ░░████████░███ ░░██ "); + System.out.println(""); +// System.out.println("http://localhost:9999/swagger-ui/index.html"); + log.info("\n----------------------------------------------------------\n\t" + + "Application is running! Access URLs:\n\t" + + "localhost: \t\thttp://" + "localhost" + ":" + port + "/\n\t" + + "localhost: \t\thttp://" + "localhost" + ":" + port + "/doc.html\n\t" + + "Doc: \t\thttp://" + ip + ":" + port + "/doc.html\n\t" + + "swagger-ui: \t\thttp://" + ip + ":" + port + "/swagger-ui/index.html\n\t" + + "----------------------------------------------------------"); + } + +} diff --git a/src/main/java/com/ruoyi/RuoYiServletInitializer.java b/src/main/java/com/ruoyi/RuoYiServletInitializer.java new file mode 100644 index 0000000..6de67dc --- /dev/null +++ b/src/main/java/com/ruoyi/RuoYiServletInitializer.java @@ -0,0 +1,18 @@ +package com.ruoyi; + +import org.springframework.boot.builder.SpringApplicationBuilder; +import org.springframework.boot.web.servlet.support.SpringBootServletInitializer; + +/** + * web容器中进行部署 + * + * @author ruoyi + */ +public class RuoYiServletInitializer extends SpringBootServletInitializer +{ + @Override + protected SpringApplicationBuilder configure(SpringApplicationBuilder application) + { + return application.sources(RuoYiApplication.class); + } +} diff --git a/src/main/java/com/ruoyi/common/constant/CacheConstants.java b/src/main/java/com/ruoyi/common/constant/CacheConstants.java new file mode 100644 index 0000000..0080343 --- /dev/null +++ b/src/main/java/com/ruoyi/common/constant/CacheConstants.java @@ -0,0 +1,44 @@ +package com.ruoyi.common.constant; + +/** + * 缓存的key 常量 + * + * @author ruoyi + */ +public class CacheConstants +{ + /** + * 登录用户 redis key + */ + public static final String LOGIN_TOKEN_KEY = "login_tokens:"; + + /** + * 验证码 redis key + */ + public static final String CAPTCHA_CODE_KEY = "captcha_codes:"; + + /** + * 参数管理 cache key + */ + public static final String SYS_CONFIG_KEY = "sys_config:"; + + /** + * 字典管理 cache key + */ + public static final String SYS_DICT_KEY = "sys_dict:"; + + /** + * 防重提交 redis key + */ + public static final String REPEAT_SUBMIT_KEY = "repeat_submit:"; + + /** + * 限流 redis key + */ + public static final String RATE_LIMIT_KEY = "rate_limit:"; + + /** + * 登录账户密码错误次数 redis key + */ + public static final String PWD_ERR_CNT_KEY = "pwd_err_cnt:"; +} diff --git a/src/main/java/com/ruoyi/common/constant/Constants.java b/src/main/java/com/ruoyi/common/constant/Constants.java new file mode 100644 index 0000000..fb736c5 --- /dev/null +++ b/src/main/java/com/ruoyi/common/constant/Constants.java @@ -0,0 +1,173 @@ +package com.ruoyi.common.constant; + +import java.util.Locale; +import io.jsonwebtoken.Claims; + +/** + * 通用常量信息 + * + * @author ruoyi + */ +public class Constants +{ + /** + * UTF-8 字符集 + */ + public static final String UTF8 = "UTF-8"; + + /** + * GBK 字符集 + */ + public static final String GBK = "GBK"; + + /** + * 系统语言 + */ + public static final Locale DEFAULT_LOCALE = Locale.SIMPLIFIED_CHINESE; + + /** + * www主域 + */ + public static final String WWW = "www."; + + /** + * http请求 + */ + public static final String HTTP = "http://"; + + /** + * https请求 + */ + public static final String HTTPS = "https://"; + + /** + * 通用成功标识 + */ + public static final String SUCCESS = "0"; + + /** + * 通用失败标识 + */ + public static final String FAIL = "1"; + + /** + * 登录成功 + */ + public static final String LOGIN_SUCCESS = "Success"; + + /** + * 注销 + */ + public static final String LOGOUT = "Logout"; + + /** + * 注册 + */ + public static final String REGISTER = "Register"; + + /** + * 登录失败 + */ + public static final String LOGIN_FAIL = "Error"; + + /** + * 所有权限标识 + */ + public static final String ALL_PERMISSION = "*:*:*"; + + /** + * 管理员角色权限标识 + */ + public static final String SUPER_ADMIN = "admin"; + + /** + * 角色权限分隔符 + */ + public static final String ROLE_DELIMETER = ","; + + /** + * 权限标识分隔符 + */ + public static final String PERMISSION_DELIMETER = ","; + + /** + * 验证码有效期(分钟) + */ + public static final Integer CAPTCHA_EXPIRATION = 2; + + /** + * 令牌 + */ + public static final String TOKEN = "token"; + + /** + * 令牌前缀 + */ + public static final String TOKEN_PREFIX = "Bearer "; + + /** + * 令牌前缀 + */ + public static final String LOGIN_USER_KEY = "login_user_key"; + + /** + * 用户ID + */ + public static final String JWT_USERID = "userid"; + + /** + * 用户名称 + */ + public static final String JWT_USERNAME = Claims.SUBJECT; + + /** + * 用户头像 + */ + public static final String JWT_AVATAR = "avatar"; + + /** + * 创建时间 + */ + public static final String JWT_CREATED = "created"; + + /** + * 用户权限 + */ + public static final String JWT_AUTHORITIES = "authorities"; + + /** + * 资源映射路径 前缀 + */ + public static final String RESOURCE_PREFIX = "/profile"; + + /** + * RMI 远程方法调用 + */ + public static final String LOOKUP_RMI = "rmi:"; + + /** + * LDAP 远程方法调用 + */ + public static final String LOOKUP_LDAP = "ldap:"; + + /** + * LDAPS 远程方法调用 + */ + public static final String LOOKUP_LDAPS = "ldaps:"; + + /** + * 自动识别json对象白名单配置(仅允许解析的包名,范围越小越安全) + */ + public static final String[] JSON_WHITELIST_STR = { "org.springframework", "com.ruoyi" }; + + /** + * 定时任务白名单配置(仅允许访问的包名,如其他需要可以自行添加) + */ + public static final String[] JOB_WHITELIST_STR = { "com.ruoyi.framework.task" }; + + /** + * 定时任务违规的字符 + */ + public static final String[] JOB_ERROR_STR = { "java.net.URL", "javax.naming.InitialContext", "org.yaml.snakeyaml", + "org.springframework", "org.apache", "com.ruoyi.common.utils.file", "com.ruoyi.framework.config", "com.ruoyi.project.tool" }; +} diff --git a/src/main/java/com/ruoyi/common/constant/GenConstants.java b/src/main/java/com/ruoyi/common/constant/GenConstants.java new file mode 100644 index 0000000..7d899d4 --- /dev/null +++ b/src/main/java/com/ruoyi/common/constant/GenConstants.java @@ -0,0 +1,117 @@ +package com.ruoyi.common.constant; + +/** + * 代码生成通用常量 + * + * @author ruoyi + */ +public class GenConstants +{ + /** 单表(增删改查) */ + public static final String TPL_CRUD = "crud"; + + /** 树表(增删改查) */ + public static final String TPL_TREE = "tree"; + + /** 主子表(增删改查) */ + public static final String TPL_SUB = "sub"; + + /** 树编码字段 */ + public static final String TREE_CODE = "treeCode"; + + /** 树父编码字段 */ + public static final String TREE_PARENT_CODE = "treeParentCode"; + + /** 树名称字段 */ + public static final String TREE_NAME = "treeName"; + + /** 上级菜单ID字段 */ + public static final String PARENT_MENU_ID = "parentMenuId"; + + /** 上级菜单名称字段 */ + public static final String PARENT_MENU_NAME = "parentMenuName"; + + /** 数据库字符串类型 */ + public static final String[] COLUMNTYPE_STR = { "char", "varchar", "nvarchar", "varchar2" }; + + /** 数据库文本类型 */ + public static final String[] COLUMNTYPE_TEXT = { "tinytext", "text", "mediumtext", "longtext" }; + + /** 数据库时间类型 */ + public static final String[] COLUMNTYPE_TIME = { "datetime", "time", "date", "timestamp" }; + + /** 数据库数字类型 */ + public static final String[] COLUMNTYPE_NUMBER = { "tinyint", "smallint", "mediumint", "int", "number", "integer", + "bit", "bigint", "float", "double", "decimal" }; + + /** 页面不需要编辑字段 */ + public static final String[] COLUMNNAME_NOT_EDIT = { "id", "create_by", "create_time", "del_flag" }; + + /** 页面不需要显示的列表字段 */ + public static final String[] COLUMNNAME_NOT_LIST = { "id", "create_by", "create_time", "del_flag", "update_by", + "update_time" }; + + /** 页面不需要查询字段 */ + public static final String[] COLUMNNAME_NOT_QUERY = { "id", "create_by", "create_time", "del_flag", "update_by", + "update_time", "remark" }; + + /** Entity基类字段 */ + public static final String[] BASE_ENTITY = { "createBy", "createTime", "updateBy", "updateTime", "remark" }; + + /** Tree基类字段 */ + public static final String[] TREE_ENTITY = { "parentName", "parentId", "orderNum", "ancestors", "children" }; + + /** 文本框 */ + public static final String HTML_INPUT = "input"; + + /** 文本域 */ + public static final String HTML_TEXTAREA = "textarea"; + + /** 下拉框 */ + public static final String HTML_SELECT = "select"; + + /** 单选框 */ + public static final String HTML_RADIO = "radio"; + + /** 复选框 */ + public static final String HTML_CHECKBOX = "checkbox"; + + /** 日期控件 */ + public static final String HTML_DATETIME = "datetime"; + + /** 图片上传控件 */ + public static final String HTML_IMAGE_UPLOAD = "imageUpload"; + + /** 文件上传控件 */ + public static final String HTML_FILE_UPLOAD = "fileUpload"; + + /** 富文本控件 */ + public static final String HTML_EDITOR = "editor"; + + /** 字符串类型 */ + public static final String TYPE_STRING = "String"; + + /** 整型 */ + public static final String TYPE_INTEGER = "Integer"; + + /** 长整型 */ + public static final String TYPE_LONG = "Long"; + + /** 浮点型 */ + public static final String TYPE_DOUBLE = "Double"; + + /** 高精度计算类型 */ + public static final String TYPE_BIGDECIMAL = "BigDecimal"; + + /** 时间类型 */ + public static final String TYPE_DATE = "Date"; + + /** 模糊查询 */ + public static final String QUERY_LIKE = "LIKE"; + + /** 相等查询 */ + public static final String QUERY_EQ = "EQ"; + + /** 需要 */ + public static final String REQUIRE = "1"; +} diff --git a/src/main/java/com/ruoyi/common/constant/HttpStatus.java b/src/main/java/com/ruoyi/common/constant/HttpStatus.java new file mode 100644 index 0000000..a983c77 --- /dev/null +++ b/src/main/java/com/ruoyi/common/constant/HttpStatus.java @@ -0,0 +1,94 @@ +package com.ruoyi.common.constant; + +/** + * 返回状态码 + * + * @author ruoyi + */ +public class HttpStatus +{ + /** + * 操作成功 + */ + public static final int SUCCESS = 200; + + /** + * 对象创建成功 + */ + public static final int CREATED = 201; + + /** + * 请求已经被接受 + */ + public static final int ACCEPTED = 202; + + /** + * 操作已经执行成功,但是没有返回数据 + */ + public static final int NO_CONTENT = 204; + + /** + * 资源已被移除 + */ + public static final int MOVED_PERM = 301; + + /** + * 重定向 + */ + public static final int SEE_OTHER = 303; + + /** + * 资源没有被修改 + */ + public static final int NOT_MODIFIED = 304; + + /** + * 参数列表错误(缺少,格式不匹配) + */ + public static final int BAD_REQUEST = 400; + + /** + * 未授权 + */ + public static final int UNAUTHORIZED = 401; + + /** + * 访问受限,授权过期 + */ + public static final int FORBIDDEN = 403; + + /** + * 资源,服务未找到 + */ + public static final int NOT_FOUND = 404; + + /** + * 不允许的http方法 + */ + public static final int BAD_METHOD = 405; + + /** + * 资源冲突,或者资源被锁 + */ + public static final int CONFLICT = 409; + + /** + * 不支持的数据,媒体类型 + */ + public static final int UNSUPPORTED_TYPE = 415; + + /** + * 系统内部错误 + */ + public static final int ERROR = 500; + + /** + * 接口未实现 + */ + public static final int NOT_IMPLEMENTED = 501; + + /** + * 系统警告消息 + */ + public static final int WARN = 601; +} diff --git a/src/main/java/com/ruoyi/common/constant/ScheduleConstants.java b/src/main/java/com/ruoyi/common/constant/ScheduleConstants.java new file mode 100644 index 0000000..62ad815 --- /dev/null +++ b/src/main/java/com/ruoyi/common/constant/ScheduleConstants.java @@ -0,0 +1,50 @@ +package com.ruoyi.common.constant; + +/** + * 任务调度通用常量 + * + * @author ruoyi + */ +public class ScheduleConstants +{ + public static final String TASK_CLASS_NAME = "TASK_CLASS_NAME"; + + /** 执行目标key */ + public static final String TASK_PROPERTIES = "TASK_PROPERTIES"; + + /** 默认 */ + public static final String MISFIRE_DEFAULT = "0"; + + /** 立即触发执行 */ + public static final String MISFIRE_IGNORE_MISFIRES = "1"; + + /** 触发一次执行 */ + public static final String MISFIRE_FIRE_AND_PROCEED = "2"; + + /** 不触发立即执行 */ + public static final String MISFIRE_DO_NOTHING = "3"; + + public enum Status + { + /** + * 正常 + */ + NORMAL("0"), + /** + * 暂停 + */ + PAUSE("1"); + + private String value; + + private Status(String value) + { + this.value = value; + } + + public String getValue() + { + return value; + } + } +} diff --git a/src/main/java/com/ruoyi/common/constant/UserConstants.java b/src/main/java/com/ruoyi/common/constant/UserConstants.java new file mode 100644 index 0000000..96b149c --- /dev/null +++ b/src/main/java/com/ruoyi/common/constant/UserConstants.java @@ -0,0 +1,78 @@ +package com.ruoyi.common.constant; + +/** + * 用户常量信息 + * + * @author ruoyi + */ +public class UserConstants +{ + /** + * 平台内系统用户的唯一标志 + */ + public static final String SYS_USER = "SYS_USER"; + + /** 正常状态 */ + public static final String NORMAL = "0"; + + /** 异常状态 */ + public static final String EXCEPTION = "1"; + + /** 用户封禁状态 */ + public static final String USER_DISABLE = "1"; + + /** 角色封禁状态 */ + public static final String ROLE_DISABLE = "1"; + + /** 部门正常状态 */ + public static final String DEPT_NORMAL = "0"; + + /** 部门停用状态 */ + public static final String DEPT_DISABLE = "1"; + + /** 字典正常状态 */ + public static final String DICT_NORMAL = "0"; + + /** 是否为系统默认(是) */ + public static final String YES = "Y"; + + /** 是否菜单外链(是) */ + public static final String YES_FRAME = "0"; + + /** 是否菜单外链(否) */ + public static final String NO_FRAME = "1"; + + /** 菜单类型(目录) */ + public static final String TYPE_DIR = "M"; + + /** 菜单类型(菜单) */ + public static final String TYPE_MENU = "C"; + + /** 菜单类型(按钮) */ + public static final String TYPE_BUTTON = "F"; + + /** Layout组件标识 */ + public final static String LAYOUT = "Layout"; + + /** ParentView组件标识 */ + public final static String PARENT_VIEW = "ParentView"; + + /** InnerLink组件标识 */ + public final static String INNER_LINK = "InnerLink"; + + /** 校验是否唯一的返回标识 */ + public final static boolean UNIQUE = true; + public final static boolean NOT_UNIQUE = false; + + /** + * 用户名长度限制 + */ + 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; +} diff --git a/src/main/java/com/ruoyi/common/core/text/CharsetKit.java b/src/main/java/com/ruoyi/common/core/text/CharsetKit.java new file mode 100644 index 0000000..84124aa --- /dev/null +++ b/src/main/java/com/ruoyi/common/core/text/CharsetKit.java @@ -0,0 +1,86 @@ +package com.ruoyi.common.core.text; + +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; +import com.ruoyi.common.utils.StringUtils; + +/** + * 字符集工具类 + * + * @author ruoyi + */ +public class CharsetKit +{ + /** ISO-8859-1 */ + public static final String ISO_8859_1 = "ISO-8859-1"; + /** UTF-8 */ + public static final String UTF_8 = "UTF-8"; + /** GBK */ + public static final String GBK = "GBK"; + + /** ISO-8859-1 */ + public static final Charset CHARSET_ISO_8859_1 = Charset.forName(ISO_8859_1); + /** UTF-8 */ + public static final Charset CHARSET_UTF_8 = Charset.forName(UTF_8); + /** GBK */ + public static final Charset CHARSET_GBK = Charset.forName(GBK); + + /** + * 转换为Charset对象 + * + * @param charset 字符集,为空则返回默认字符集 + * @return Charset + */ + public static Charset charset(String charset) + { + return StringUtils.isEmpty(charset) ? Charset.defaultCharset() : Charset.forName(charset); + } + + /** + * 转换字符串的字符集编码 + * + * @param source 字符串 + * @param srcCharset 源字符集,默认ISO-8859-1 + * @param destCharset 目标字符集,默认UTF-8 + * @return 转换后的字符集 + */ + public static String convert(String source, String srcCharset, String destCharset) + { + return convert(source, Charset.forName(srcCharset), Charset.forName(destCharset)); + } + + /** + * 转换字符串的字符集编码 + * + * @param source 字符串 + * @param srcCharset 源字符集,默认ISO-8859-1 + * @param destCharset 目标字符集,默认UTF-8 + * @return 转换后的字符集 + */ + public static String convert(String source, Charset srcCharset, Charset destCharset) + { + if (null == srcCharset) + { + srcCharset = StandardCharsets.ISO_8859_1; + } + + if (null == destCharset) + { + destCharset = StandardCharsets.UTF_8; + } + + if (StringUtils.isEmpty(source) || srcCharset.equals(destCharset)) + { + return source; + } + return new String(source.getBytes(srcCharset), destCharset); + } + + /** + * @return 系统字符集编码 + */ + public static String systemCharset() + { + return Charset.defaultCharset().name(); + } +} diff --git a/src/main/java/com/ruoyi/common/core/text/Convert.java b/src/main/java/com/ruoyi/common/core/text/Convert.java new file mode 100644 index 0000000..0066112 --- /dev/null +++ b/src/main/java/com/ruoyi/common/core/text/Convert.java @@ -0,0 +1,1006 @@ +package com.ruoyi.common.core.text; + +import java.math.BigDecimal; +import java.math.BigInteger; +import java.math.RoundingMode; +import java.nio.ByteBuffer; +import java.nio.charset.Charset; +import java.text.NumberFormat; +import java.util.Set; +import com.ruoyi.common.utils.StringUtils; +import org.apache.commons.lang3.ArrayUtils; + +/** + * 类型转换器 + * + * @author ruoyi + */ +public class Convert +{ + /** + * 转换为字符串
+ * 如果给定的值为null,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static String toStr(Object value, String defaultValue) + { + if (null == value) + { + return defaultValue; + } + if (value instanceof String) + { + return (String) value; + } + return value.toString(); + } + + /** + * 转换为字符串
+ * 如果给定的值为null,或者转换失败,返回默认值null
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @return 结果 + */ + public static String toStr(Object value) + { + return toStr(value, null); + } + + /** + * 转换为字符
+ * 如果给定的值为null,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static Character toChar(Object value, Character defaultValue) + { + if (null == value) + { + return defaultValue; + } + if (value instanceof Character) + { + return (Character) value; + } + + final String valueStr = toStr(value, null); + return StringUtils.isEmpty(valueStr) ? defaultValue : valueStr.charAt(0); + } + + /** + * 转换为字符
+ * 如果给定的值为null,或者转换失败,返回默认值null
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @return 结果 + */ + public static Character toChar(Object value) + { + return toChar(value, null); + } + + /** + * 转换为byte
+ * 如果给定的值为null,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static Byte toByte(Object value, Byte defaultValue) + { + if (value == null) + { + return defaultValue; + } + if (value instanceof Byte) + { + return (Byte) value; + } + if (value instanceof Number) + { + return ((Number) value).byteValue(); + } + final String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) + { + return defaultValue; + } + try + { + return Byte.parseByte(valueStr); + } + catch (Exception e) + { + return defaultValue; + } + } + + /** + * 转换为byte
+ * 如果给定的值为null,或者转换失败,返回默认值null
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @return 结果 + */ + public static Byte toByte(Object value) + { + return toByte(value, null); + } + + /** + * 转换为Short
+ * 如果给定的值为null,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static Short toShort(Object value, Short defaultValue) + { + if (value == null) + { + return defaultValue; + } + if (value instanceof Short) + { + return (Short) value; + } + if (value instanceof Number) + { + return ((Number) value).shortValue(); + } + final String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) + { + return defaultValue; + } + try + { + return Short.parseShort(valueStr.trim()); + } + catch (Exception e) + { + return defaultValue; + } + } + + /** + * 转换为Short
+ * 如果给定的值为null,或者转换失败,返回默认值null
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @return 结果 + */ + public static Short toShort(Object value) + { + return toShort(value, null); + } + + /** + * 转换为Number
+ * 如果给定的值为空,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static Number toNumber(Object value, Number defaultValue) + { + if (value == null) + { + return defaultValue; + } + if (value instanceof Number) + { + return (Number) value; + } + final String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) + { + return defaultValue; + } + try + { + return NumberFormat.getInstance().parse(valueStr); + } + catch (Exception e) + { + return defaultValue; + } + } + + /** + * 转换为Number
+ * 如果给定的值为空,或者转换失败,返回默认值null
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @return 结果 + */ + public static Number toNumber(Object value) + { + return toNumber(value, null); + } + + /** + * 转换为int
+ * 如果给定的值为空,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static Integer toInt(Object value, Integer defaultValue) + { + if (value == null) + { + return defaultValue; + } + if (value instanceof Integer) + { + return (Integer) value; + } + if (value instanceof Number) + { + return ((Number) value).intValue(); + } + final String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) + { + return defaultValue; + } + try + { + return Integer.parseInt(valueStr.trim()); + } + catch (Exception e) + { + return defaultValue; + } + } + + /** + * 转换为int
+ * 如果给定的值为null,或者转换失败,返回默认值null
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @return 结果 + */ + public static Integer toInt(Object value) + { + return toInt(value, null); + } + + /** + * 转换为Integer数组
+ * + * @param str 被转换的值 + * @return 结果 + */ + public static Integer[] toIntArray(String str) + { + return toIntArray(",", str); + } + + /** + * 转换为Long数组
+ * + * @param str 被转换的值 + * @return 结果 + */ + public static Long[] toLongArray(String str) + { + return toLongArray(",", str); + } + + /** + * 转换为Integer数组
+ * + * @param split 分隔符 + * @param split 被转换的值 + * @return 结果 + */ + public static Integer[] toIntArray(String split, String str) + { + if (StringUtils.isEmpty(str)) + { + return new Integer[] {}; + } + String[] arr = str.split(split); + final Integer[] ints = new Integer[arr.length]; + for (int i = 0; i < arr.length; i++) + { + final Integer v = toInt(arr[i], 0); + ints[i] = v; + } + return ints; + } + + /** + * 转换为Long数组
+ * + * @param split 分隔符 + * @param str 被转换的值 + * @return 结果 + */ + public static Long[] toLongArray(String split, String str) + { + if (StringUtils.isEmpty(str)) + { + return new Long[] {}; + } + String[] arr = str.split(split); + final Long[] longs = new Long[arr.length]; + for (int i = 0; i < arr.length; i++) + { + final Long v = toLong(arr[i], null); + longs[i] = v; + } + return longs; + } + + /** + * 转换为String数组
+ * + * @param str 被转换的值 + * @return 结果 + */ + public static String[] toStrArray(String str) + { + return toStrArray(",", str); + } + + /** + * 转换为String数组
+ * + * @param split 分隔符 + * @param split 被转换的值 + * @return 结果 + */ + public static String[] toStrArray(String split, String str) + { + return str.split(split); + } + + /** + * 转换为long
+ * 如果给定的值为空,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static Long toLong(Object value, Long defaultValue) + { + if (value == null) + { + return defaultValue; + } + if (value instanceof Long) + { + return (Long) value; + } + if (value instanceof Number) + { + return ((Number) value).longValue(); + } + final String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) + { + return defaultValue; + } + try + { + // 支持科学计数法 + return new BigDecimal(valueStr.trim()).longValue(); + } + catch (Exception e) + { + return defaultValue; + } + } + + /** + * 转换为long
+ * 如果给定的值为null,或者转换失败,返回默认值null
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @return 结果 + */ + public static Long toLong(Object value) + { + return toLong(value, null); + } + + /** + * 转换为double
+ * 如果给定的值为空,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static Double toDouble(Object value, Double defaultValue) + { + if (value == null) + { + return defaultValue; + } + if (value instanceof Double) + { + return (Double) value; + } + if (value instanceof Number) + { + return ((Number) value).doubleValue(); + } + final String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) + { + return defaultValue; + } + try + { + // 支持科学计数法 + return new BigDecimal(valueStr.trim()).doubleValue(); + } + catch (Exception e) + { + return defaultValue; + } + } + + /** + * 转换为double
+ * 如果给定的值为空,或者转换失败,返回默认值null
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @return 结果 + */ + public static Double toDouble(Object value) + { + return toDouble(value, null); + } + + /** + * 转换为Float
+ * 如果给定的值为空,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static Float toFloat(Object value, Float defaultValue) + { + if (value == null) + { + return defaultValue; + } + if (value instanceof Float) + { + return (Float) value; + } + if (value instanceof Number) + { + return ((Number) value).floatValue(); + } + final String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) + { + return defaultValue; + } + try + { + return Float.parseFloat(valueStr.trim()); + } + catch (Exception e) + { + return defaultValue; + } + } + + /** + * 转换为Float
+ * 如果给定的值为空,或者转换失败,返回默认值null
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @return 结果 + */ + public static Float toFloat(Object value) + { + return toFloat(value, null); + } + + /** + * 转换为boolean
+ * String支持的值为:true、false、yes、ok、no,1,0 如果给定的值为空,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static Boolean toBool(Object value, Boolean defaultValue) + { + if (value == null) + { + return defaultValue; + } + if (value instanceof Boolean) + { + return (Boolean) value; + } + String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) + { + return defaultValue; + } + valueStr = valueStr.trim().toLowerCase(); + switch (valueStr) + { + case "true": + case "yes": + case "ok": + case "1": + return true; + case "false": + case "no": + case "0": + return false; + default: + return defaultValue; + } + } + + /** + * 转换为boolean
+ * 如果给定的值为空,或者转换失败,返回默认值null
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @return 结果 + */ + public static Boolean toBool(Object value) + { + return toBool(value, null); + } + + /** + * 转换为Enum对象
+ * 如果给定的值为空,或者转换失败,返回默认值
+ * + * @param clazz Enum的Class + * @param value 值 + * @param defaultValue 默认值 + * @return Enum + */ + public static > E toEnum(Class clazz, Object value, E defaultValue) + { + if (value == null) + { + return defaultValue; + } + if (clazz.isAssignableFrom(value.getClass())) + { + @SuppressWarnings("unchecked") + E myE = (E) value; + return myE; + } + final String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) + { + return defaultValue; + } + try + { + return Enum.valueOf(clazz, valueStr); + } + catch (Exception e) + { + return defaultValue; + } + } + + /** + * 转换为Enum对象
+ * 如果给定的值为空,或者转换失败,返回默认值null
+ * + * @param clazz Enum的Class + * @param value 值 + * @return Enum + */ + public static > E toEnum(Class clazz, Object value) + { + return toEnum(clazz, value, null); + } + + /** + * 转换为BigInteger
+ * 如果给定的值为空,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static BigInteger toBigInteger(Object value, BigInteger defaultValue) + { + if (value == null) + { + return defaultValue; + } + if (value instanceof BigInteger) + { + return (BigInteger) value; + } + if (value instanceof Long) + { + return BigInteger.valueOf((Long) value); + } + final String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) + { + return defaultValue; + } + try + { + return new BigInteger(valueStr); + } + catch (Exception e) + { + return defaultValue; + } + } + + /** + * 转换为BigInteger
+ * 如果给定的值为空,或者转换失败,返回默认值null
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @return 结果 + */ + public static BigInteger toBigInteger(Object value) + { + return toBigInteger(value, null); + } + + /** + * 转换为BigDecimal
+ * 如果给定的值为空,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static BigDecimal toBigDecimal(Object value, BigDecimal defaultValue) + { + if (value == null) + { + return defaultValue; + } + if (value instanceof BigDecimal) + { + return (BigDecimal) value; + } + if (value instanceof Long) + { + return new BigDecimal((Long) value); + } + if (value instanceof Double) + { + return BigDecimal.valueOf((Double) value); + } + if (value instanceof Integer) + { + return new BigDecimal((Integer) value); + } + final String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) + { + return defaultValue; + } + try + { + return new BigDecimal(valueStr); + } + catch (Exception e) + { + return defaultValue; + } + } + + /** + * 转换为BigDecimal
+ * 如果给定的值为空,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @return 结果 + */ + public static BigDecimal toBigDecimal(Object value) + { + return toBigDecimal(value, null); + } + + /** + * 将对象转为字符串
+ * 1、Byte数组和ByteBuffer会被转换为对应字符串的数组 2、对象数组会调用Arrays.toString方法 + * + * @param obj 对象 + * @return 字符串 + */ + public static String utf8Str(Object obj) + { + return str(obj, CharsetKit.CHARSET_UTF_8); + } + + /** + * 将对象转为字符串
+ * 1、Byte数组和ByteBuffer会被转换为对应字符串的数组 2、对象数组会调用Arrays.toString方法 + * + * @param obj 对象 + * @param charsetName 字符集 + * @return 字符串 + */ + public static String str(Object obj, String charsetName) + { + return str(obj, Charset.forName(charsetName)); + } + + /** + * 将对象转为字符串
+ * 1、Byte数组和ByteBuffer会被转换为对应字符串的数组 2、对象数组会调用Arrays.toString方法 + * + * @param obj 对象 + * @param charset 字符集 + * @return 字符串 + */ + public static String str(Object obj, Charset charset) + { + if (null == obj) + { + return null; + } + + if (obj instanceof String) + { + return (String) obj; + } + else if (obj instanceof byte[]) + { + return str((byte[]) obj, charset); + } + else if (obj instanceof Byte[]) + { + byte[] bytes = ArrayUtils.toPrimitive((Byte[]) obj); + return str(bytes, charset); + } + else if (obj instanceof ByteBuffer) + { + return str((ByteBuffer) obj, charset); + } + return obj.toString(); + } + + /** + * 将byte数组转为字符串 + * + * @param bytes byte数组 + * @param charset 字符集 + * @return 字符串 + */ + public static String str(byte[] bytes, String charset) + { + return str(bytes, StringUtils.isEmpty(charset) ? Charset.defaultCharset() : Charset.forName(charset)); + } + + /** + * 解码字节码 + * + * @param data 字符串 + * @param charset 字符集,如果此字段为空,则解码的结果取决于平台 + * @return 解码后的字符串 + */ + public static String str(byte[] data, Charset charset) + { + if (data == null) + { + return null; + } + + if (null == charset) + { + return new String(data); + } + return new String(data, charset); + } + + /** + * 将编码的byteBuffer数据转换为字符串 + * + * @param data 数据 + * @param charset 字符集,如果为空使用当前系统字符集 + * @return 字符串 + */ + public static String str(ByteBuffer data, String charset) + { + if (data == null) + { + return null; + } + + return str(data, Charset.forName(charset)); + } + + /** + * 将编码的byteBuffer数据转换为字符串 + * + * @param data 数据 + * @param charset 字符集,如果为空使用当前系统字符集 + * @return 字符串 + */ + public static String str(ByteBuffer data, Charset charset) + { + if (null == charset) + { + charset = Charset.defaultCharset(); + } + return charset.decode(data).toString(); + } + + // ----------------------------------------------------------------------- 全角半角转换 + /** + * 半角转全角 + * + * @param input String. + * @return 全角字符串. + */ + public static String toSBC(String input) + { + return toSBC(input, null); + } + + /** + * 半角转全角 + * + * @param input String + * @param notConvertSet 不替换的字符集合 + * @return 全角字符串. + */ + public static String toSBC(String input, Set notConvertSet) + { + char[] c = input.toCharArray(); + for (int i = 0; i < c.length; i++) + { + if (null != notConvertSet && notConvertSet.contains(c[i])) + { + // 跳过不替换的字符 + continue; + } + + if (c[i] == ' ') + { + c[i] = '\u3000'; + } + else if (c[i] < '\177') + { + c[i] = (char) (c[i] + 65248); + + } + } + return new String(c); + } + + /** + * 全角转半角 + * + * @param input String. + * @return 半角字符串 + */ + public static String toDBC(String input) + { + return toDBC(input, null); + } + + /** + * 替换全角为半角 + * + * @param text 文本 + * @param notConvertSet 不替换的字符集合 + * @return 替换后的字符 + */ + public static String toDBC(String text, Set notConvertSet) + { + char[] c = text.toCharArray(); + for (int i = 0; i < c.length; i++) + { + if (null != notConvertSet && notConvertSet.contains(c[i])) + { + // 跳过不替换的字符 + continue; + } + + if (c[i] == '\u3000') + { + c[i] = ' '; + } + else if (c[i] > '\uFF00' && c[i] < '\uFF5F') + { + c[i] = (char) (c[i] - 65248); + } + } + String returnString = new String(c); + + return returnString; + } + + /** + * 数字金额大写转换 先写个完整的然后将如零拾替换成零 + * + * @param n 数字 + * @return 中文大写数字 + */ + public static String digitUppercase(double n) + { + String[] fraction = { "角", "分" }; + String[] digit = { "零", "壹", "贰", "叁", "肆", "伍", "陆", "柒", "捌", "玖" }; + String[][] unit = { { "元", "万", "亿" }, { "", "拾", "佰", "仟" } }; + + String head = n < 0 ? "负" : ""; + n = Math.abs(n); + + String s = ""; + for (int i = 0; i < fraction.length; i++) + { + // 优化double计算精度丢失问题 + BigDecimal nNum = new BigDecimal(n); + BigDecimal decimal = new BigDecimal(10); + BigDecimal scale = nNum.multiply(decimal).setScale(2, RoundingMode.HALF_EVEN); + double d = scale.doubleValue(); + s += (digit[(int) (Math.floor(d * Math.pow(10, i)) % 10)] + fraction[i]).replaceAll("(零.)+", ""); + } + if (s.length() < 1) + { + s = "整"; + } + int integerPart = (int) Math.floor(n); + + for (int i = 0; i < unit[0].length && integerPart > 0; i++) + { + String p = ""; + for (int j = 0; j < unit[1].length && n > 0; j++) + { + p = digit[integerPart % 10] + unit[1][j] + p; + integerPart = integerPart / 10; + } + s = p.replaceAll("(零.)*零$", "").replaceAll("^$", "零") + unit[0][i] + s; + } + return head + s.replaceAll("(零.)*零元", "元").replaceFirst("(零.)+", "").replaceAll("(零.)+", "零").replaceAll("^整$", "零元整"); + } +} diff --git a/src/main/java/com/ruoyi/common/core/text/StrFormatter.java b/src/main/java/com/ruoyi/common/core/text/StrFormatter.java new file mode 100644 index 0000000..c78ac77 --- /dev/null +++ b/src/main/java/com/ruoyi/common/core/text/StrFormatter.java @@ -0,0 +1,92 @@ +package com.ruoyi.common.core.text; + +import com.ruoyi.common.utils.StringUtils; + +/** + * 字符串格式化 + * + * @author ruoyi + */ +public class StrFormatter +{ + public static final String EMPTY_JSON = "{}"; + public static final char C_BACKSLASH = '\\'; + public static final char C_DELIM_START = '{'; + public static final char C_DELIM_END = '}'; + + /** + * 格式化字符串
+ * 此方法只是简单将占位符 {} 按照顺序替换为参数
+ * 如果想输出 {} 使用 \\转义 { 即可,如果想输出 {} 之前的 \ 使用双转义符 \\\\ 即可
+ * 例:
+ * 通常使用:format("this is {} for {}", "a", "b") -> this is a for b
+ * 转义{}: format("this is \\{} for {}", "a", "b") -> this is \{} for a
+ * 转义\: format("this is \\\\{} for {}", "a", "b") -> this is \a for b
+ * + * @param strPattern 字符串模板 + * @param argArray 参数列表 + * @return 结果 + */ + public static String format(final String strPattern, final Object... argArray) + { + if (StringUtils.isEmpty(strPattern) || StringUtils.isEmpty(argArray)) + { + return strPattern; + } + final int strPatternLength = strPattern.length(); + + // 初始化定义好的长度以获得更好的性能 + StringBuilder sbuf = new StringBuilder(strPatternLength + 50); + + int handledPosition = 0; + int delimIndex;// 占位符所在位置 + for (int argIndex = 0; argIndex < argArray.length; argIndex++) + { + delimIndex = strPattern.indexOf(EMPTY_JSON, handledPosition); + if (delimIndex == -1) + { + if (handledPosition == 0) + { + return strPattern; + } + else + { // 字符串模板剩余部分不再包含占位符,加入剩余部分后返回结果 + sbuf.append(strPattern, handledPosition, strPatternLength); + return sbuf.toString(); + } + } + else + { + if (delimIndex > 0 && strPattern.charAt(delimIndex - 1) == C_BACKSLASH) + { + if (delimIndex > 1 && strPattern.charAt(delimIndex - 2) == C_BACKSLASH) + { + // 转义符之前还有一个转义符,占位符依旧有效 + sbuf.append(strPattern, handledPosition, delimIndex - 1); + sbuf.append(Convert.utf8Str(argArray[argIndex])); + handledPosition = delimIndex + 2; + } + else + { + // 占位符被转义 + argIndex--; + sbuf.append(strPattern, handledPosition, delimIndex - 1); + sbuf.append(C_DELIM_START); + handledPosition = delimIndex + 1; + } + } + else + { + // 正常占位符 + sbuf.append(strPattern, handledPosition, delimIndex); + sbuf.append(Convert.utf8Str(argArray[argIndex])); + handledPosition = delimIndex + 2; + } + } + } + // 加入最后一个占位符后所有的字符 + sbuf.append(strPattern, handledPosition, strPattern.length()); + + return sbuf.toString(); + } +} diff --git a/src/main/java/com/ruoyi/common/enums/HttpMethod.java b/src/main/java/com/ruoyi/common/enums/HttpMethod.java new file mode 100644 index 0000000..be6f739 --- /dev/null +++ b/src/main/java/com/ruoyi/common/enums/HttpMethod.java @@ -0,0 +1,36 @@ +package com.ruoyi.common.enums; + +import java.util.HashMap; +import java.util.Map; +import org.springframework.lang.Nullable; + +/** + * 请求方式 + * + * @author ruoyi + */ +public enum HttpMethod +{ + GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS, TRACE; + + private static final Map mappings = new HashMap<>(16); + + static + { + for (HttpMethod httpMethod : values()) + { + mappings.put(httpMethod.name(), httpMethod); + } + } + + @Nullable + public static HttpMethod resolve(@Nullable String method) + { + return (method != null ? mappings.get(method) : null); + } + + public boolean matches(String method) + { + return (this == resolve(method)); + } +} diff --git a/src/main/java/com/ruoyi/common/enums/UserStatus.java b/src/main/java/com/ruoyi/common/enums/UserStatus.java new file mode 100644 index 0000000..d7ff44a --- /dev/null +++ b/src/main/java/com/ruoyi/common/enums/UserStatus.java @@ -0,0 +1,30 @@ +package com.ruoyi.common.enums; + +/** + * 用户状态 + * + * @author ruoyi + */ +public enum UserStatus +{ + OK("0", "正常"), DISABLE("1", "停用"), DELETED("2", "删除"); + + private final String code; + private final String info; + + UserStatus(String code, String info) + { + this.code = code; + this.info = info; + } + + public String getCode() + { + return code; + } + + public String getInfo() + { + return info; + } +} diff --git a/src/main/java/com/ruoyi/common/exception/DemoModeException.java b/src/main/java/com/ruoyi/common/exception/DemoModeException.java new file mode 100644 index 0000000..f6ad2ab --- /dev/null +++ b/src/main/java/com/ruoyi/common/exception/DemoModeException.java @@ -0,0 +1,15 @@ +package com.ruoyi.common.exception; + +/** + * 演示模式异常 + * + * @author ruoyi + */ +public class DemoModeException extends RuntimeException +{ + private static final long serialVersionUID = 1L; + + public DemoModeException() + { + } +} diff --git a/src/main/java/com/ruoyi/common/exception/GlobalException.java b/src/main/java/com/ruoyi/common/exception/GlobalException.java new file mode 100644 index 0000000..81a71b5 --- /dev/null +++ b/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; + } + + @Override + public String getMessage() + { + return message; + } + + public GlobalException setMessage(String message) + { + this.message = message; + return this; + } +} \ No newline at end of file diff --git a/src/main/java/com/ruoyi/common/exception/ServiceException.java b/src/main/java/com/ruoyi/common/exception/ServiceException.java new file mode 100644 index 0000000..fcc7ab6 --- /dev/null +++ b/src/main/java/com/ruoyi/common/exception/ServiceException.java @@ -0,0 +1,74 @@ +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; + } + + @Override + public String getMessage() + { + return message; + } + + public Integer getCode() + { + return code; + } + + public ServiceException setMessage(String message) + { + this.message = message; + return this; + } + + public ServiceException setDetailMessage(String detailMessage) + { + this.detailMessage = detailMessage; + return this; + } +} \ No newline at end of file diff --git a/src/main/java/com/ruoyi/common/exception/UtilException.java b/src/main/java/com/ruoyi/common/exception/UtilException.java new file mode 100644 index 0000000..980fa46 --- /dev/null +++ b/src/main/java/com/ruoyi/common/exception/UtilException.java @@ -0,0 +1,26 @@ +package com.ruoyi.common.exception; + +/** + * 工具类异常 + * + * @author ruoyi + */ +public class UtilException extends RuntimeException +{ + private static final long serialVersionUID = 8247610319171014183L; + + public UtilException(Throwable e) + { + super(e.getMessage(), e); + } + + public UtilException(String message) + { + super(message); + } + + public UtilException(String message, Throwable throwable) + { + super(message, throwable); + } +} diff --git a/src/main/java/com/ruoyi/common/exception/base/BaseException.java b/src/main/java/com/ruoyi/common/exception/base/BaseException.java new file mode 100644 index 0000000..b55d72e --- /dev/null +++ b/src/main/java/com/ruoyi/common/exception/base/BaseException.java @@ -0,0 +1,97 @@ +package com.ruoyi.common.exception.base; + +import com.ruoyi.common.utils.MessageUtils; +import com.ruoyi.common.utils.StringUtils; + +/** + * 基础异常 + * + * @author ruoyi + */ +public class BaseException extends RuntimeException +{ + private static final long serialVersionUID = 1L; + + /** + * 所属模块 + */ + private String module; + + /** + * 错误码 + */ + private String code; + + /** + * 错误码对应的参数 + */ + private Object[] args; + + /** + * 错误消息 + */ + private String defaultMessage; + + public BaseException(String module, String code, Object[] args, String defaultMessage) + { + this.module = module; + this.code = code; + this.args = args; + this.defaultMessage = defaultMessage; + } + + public BaseException(String module, String code, Object[] args) + { + this(module, code, args, null); + } + + public BaseException(String module, String defaultMessage) + { + this(module, null, null, defaultMessage); + } + + public BaseException(String code, Object[] args) + { + this(null, code, args, null); + } + + public BaseException(String defaultMessage) + { + this(null, null, null, defaultMessage); + } + + @Override + public String getMessage() + { + String message = null; + if (!StringUtils.isEmpty(code)) + { + message = MessageUtils.message(code, args); + } + if (message == null) + { + message = defaultMessage; + } + return message; + } + + public String getModule() + { + return module; + } + + public String getCode() + { + return code; + } + + public Object[] getArgs() + { + return args; + } + + public String getDefaultMessage() + { + return defaultMessage; + } +} diff --git a/src/main/java/com/ruoyi/common/exception/file/FileException.java b/src/main/java/com/ruoyi/common/exception/file/FileException.java new file mode 100644 index 0000000..871f09b --- /dev/null +++ b/src/main/java/com/ruoyi/common/exception/file/FileException.java @@ -0,0 +1,19 @@ +package com.ruoyi.common.exception.file; + +import com.ruoyi.common.exception.base.BaseException; + +/** + * 文件信息异常类 + * + * @author ruoyi + */ +public class FileException extends BaseException +{ + private static final long serialVersionUID = 1L; + + public FileException(String code, Object[] args) + { + super("file", code, args, null); + } + +} diff --git a/src/main/java/com/ruoyi/common/exception/file/FileNameLengthLimitExceededException.java b/src/main/java/com/ruoyi/common/exception/file/FileNameLengthLimitExceededException.java new file mode 100644 index 0000000..70e0ec9 --- /dev/null +++ b/src/main/java/com/ruoyi/common/exception/file/FileNameLengthLimitExceededException.java @@ -0,0 +1,16 @@ +package com.ruoyi.common.exception.file; + +/** + * 文件名称超长限制异常类 + * + * @author ruoyi + */ +public class FileNameLengthLimitExceededException extends FileException +{ + private static final long serialVersionUID = 1L; + + public FileNameLengthLimitExceededException(int defaultFileNameLength) + { + super("upload.filename.exceed.length", new Object[] { defaultFileNameLength }); + } +} diff --git a/src/main/java/com/ruoyi/common/exception/file/FileSizeLimitExceededException.java b/src/main/java/com/ruoyi/common/exception/file/FileSizeLimitExceededException.java new file mode 100644 index 0000000..ec6ab05 --- /dev/null +++ b/src/main/java/com/ruoyi/common/exception/file/FileSizeLimitExceededException.java @@ -0,0 +1,16 @@ +package com.ruoyi.common.exception.file; + +/** + * 文件名大小限制异常类 + * + * @author ruoyi + */ +public class FileSizeLimitExceededException extends FileException +{ + private static final long serialVersionUID = 1L; + + public FileSizeLimitExceededException(long defaultMaxSize) + { + super("upload.exceed.maxSize", new Object[] { defaultMaxSize }); + } +} diff --git a/src/main/java/com/ruoyi/common/exception/file/FileUploadException.java b/src/main/java/com/ruoyi/common/exception/file/FileUploadException.java new file mode 100644 index 0000000..f45e7ef --- /dev/null +++ b/src/main/java/com/ruoyi/common/exception/file/FileUploadException.java @@ -0,0 +1,61 @@ +package com.ruoyi.common.exception.file; + +import java.io.PrintStream; +import java.io.PrintWriter; + +/** + * 文件上传异常类 + * + * @author ruoyi + */ +public class FileUploadException extends Exception +{ + + private static final long serialVersionUID = 1L; + + private final Throwable cause; + + public FileUploadException() + { + this(null, null); + } + + public FileUploadException(final String msg) + { + this(msg, null); + } + + public FileUploadException(String msg, Throwable cause) + { + super(msg); + this.cause = cause; + } + + @Override + public void printStackTrace(PrintStream stream) + { + super.printStackTrace(stream); + if (cause != null) + { + stream.println("Caused by:"); + cause.printStackTrace(stream); + } + } + + @Override + public void printStackTrace(PrintWriter writer) + { + super.printStackTrace(writer); + if (cause != null) + { + writer.println("Caused by:"); + cause.printStackTrace(writer); + } + } + + @Override + public Throwable getCause() + { + return cause; + } +} diff --git a/src/main/java/com/ruoyi/common/exception/file/InvalidExtensionException.java b/src/main/java/com/ruoyi/common/exception/file/InvalidExtensionException.java new file mode 100644 index 0000000..011f308 --- /dev/null +++ b/src/main/java/com/ruoyi/common/exception/file/InvalidExtensionException.java @@ -0,0 +1,80 @@ +package com.ruoyi.common.exception.file; + +import java.util.Arrays; + +/** + * 文件上传 误异常类 + * + * @author ruoyi + */ +public class InvalidExtensionException extends FileUploadException +{ + private static final long serialVersionUID = 1L; + + private String[] allowedExtension; + private String extension; + private String filename; + + public InvalidExtensionException(String[] allowedExtension, String extension, String filename) + { + super("文件[" + filename + "]后缀[" + extension + "]不正确,请上传" + Arrays.toString(allowedExtension) + "格式"); + this.allowedExtension = allowedExtension; + this.extension = extension; + this.filename = filename; + } + + public String[] getAllowedExtension() + { + return allowedExtension; + } + + public String getExtension() + { + return extension; + } + + public String getFilename() + { + return filename; + } + + public static class InvalidImageExtensionException extends InvalidExtensionException + { + private static final long serialVersionUID = 1L; + + public InvalidImageExtensionException(String[] allowedExtension, String extension, String filename) + { + super(allowedExtension, extension, filename); + } + } + + public static class InvalidFlashExtensionException extends InvalidExtensionException + { + private static final long serialVersionUID = 1L; + + public InvalidFlashExtensionException(String[] allowedExtension, String extension, String filename) + { + super(allowedExtension, extension, filename); + } + } + + public static class InvalidMediaExtensionException extends InvalidExtensionException + { + private static final long serialVersionUID = 1L; + + public InvalidMediaExtensionException(String[] allowedExtension, String extension, String filename) + { + super(allowedExtension, extension, filename); + } + } + + public static class InvalidVideoExtensionException extends InvalidExtensionException + { + private static final long serialVersionUID = 1L; + + public InvalidVideoExtensionException(String[] allowedExtension, String extension, String filename) + { + super(allowedExtension, extension, filename); + } + } +} diff --git a/src/main/java/com/ruoyi/common/exception/job/TaskException.java b/src/main/java/com/ruoyi/common/exception/job/TaskException.java new file mode 100644 index 0000000..a567b40 --- /dev/null +++ b/src/main/java/com/ruoyi/common/exception/job/TaskException.java @@ -0,0 +1,34 @@ +package com.ruoyi.common.exception.job; + +/** + * 计划策略异常 + * + * @author ruoyi + */ +public class TaskException extends Exception +{ + private static final long serialVersionUID = 1L; + + private Code code; + + public TaskException(String msg, Code code) + { + this(msg, code, null); + } + + public TaskException(String msg, Code code, Exception nestedEx) + { + super(msg, nestedEx); + this.code = code; + } + + public Code getCode() + { + return code; + } + + public enum Code + { + TASK_EXISTS, NO_TASK_EXISTS, TASK_ALREADY_STARTED, UNKNOWN, CONFIG_ERROR, TASK_NODE_NOT_AVAILABLE + } +} \ No newline at end of file diff --git a/src/main/java/com/ruoyi/common/exception/user/BlackListException.java b/src/main/java/com/ruoyi/common/exception/user/BlackListException.java new file mode 100644 index 0000000..2bf5038 --- /dev/null +++ b/src/main/java/com/ruoyi/common/exception/user/BlackListException.java @@ -0,0 +1,16 @@ +package com.ruoyi.common.exception.user; + +/** + * 黑名单IP异常类 + * + * @author ruoyi + */ +public class BlackListException extends UserException +{ + private static final long serialVersionUID = 1L; + + public BlackListException() + { + super("login.blocked", null); + } +} diff --git a/src/main/java/com/ruoyi/common/exception/user/CaptchaException.java b/src/main/java/com/ruoyi/common/exception/user/CaptchaException.java new file mode 100644 index 0000000..389dbc7 --- /dev/null +++ b/src/main/java/com/ruoyi/common/exception/user/CaptchaException.java @@ -0,0 +1,16 @@ +package com.ruoyi.common.exception.user; + +/** + * 验证码错误异常类 + * + * @author ruoyi + */ +public class CaptchaException extends UserException +{ + private static final long serialVersionUID = 1L; + + public CaptchaException() + { + super("user.jcaptcha.error", null); + } +} diff --git a/src/main/java/com/ruoyi/common/exception/user/CaptchaExpireException.java b/src/main/java/com/ruoyi/common/exception/user/CaptchaExpireException.java new file mode 100644 index 0000000..85f9486 --- /dev/null +++ b/src/main/java/com/ruoyi/common/exception/user/CaptchaExpireException.java @@ -0,0 +1,16 @@ +package com.ruoyi.common.exception.user; + +/** + * 验证码失效异常类 + * + * @author ruoyi + */ +public class CaptchaExpireException extends UserException +{ + private static final long serialVersionUID = 1L; + + public CaptchaExpireException() + { + super("user.jcaptcha.expire", null); + } +} diff --git a/src/main/java/com/ruoyi/common/exception/user/UserException.java b/src/main/java/com/ruoyi/common/exception/user/UserException.java new file mode 100644 index 0000000..c292d70 --- /dev/null +++ b/src/main/java/com/ruoyi/common/exception/user/UserException.java @@ -0,0 +1,18 @@ +package com.ruoyi.common.exception.user; + +import com.ruoyi.common.exception.base.BaseException; + +/** + * 用户信息异常类 + * + * @author ruoyi + */ +public class UserException extends BaseException +{ + private static final long serialVersionUID = 1L; + + public UserException(String code, Object[] args) + { + super("user", code, args, null); + } +} diff --git a/src/main/java/com/ruoyi/common/exception/user/UserNotExistsException.java b/src/main/java/com/ruoyi/common/exception/user/UserNotExistsException.java new file mode 100644 index 0000000..eff8181 --- /dev/null +++ b/src/main/java/com/ruoyi/common/exception/user/UserNotExistsException.java @@ -0,0 +1,16 @@ +package com.ruoyi.common.exception.user; + +/** + * 用户不存在异常类 + * + * @author ruoyi + */ +public class UserNotExistsException extends UserException +{ + private static final long serialVersionUID = 1L; + + public UserNotExistsException() + { + super("user.not.exists", null); + } +} diff --git a/src/main/java/com/ruoyi/common/exception/user/UserPasswordNotMatchException.java b/src/main/java/com/ruoyi/common/exception/user/UserPasswordNotMatchException.java new file mode 100644 index 0000000..a7f3e5f --- /dev/null +++ b/src/main/java/com/ruoyi/common/exception/user/UserPasswordNotMatchException.java @@ -0,0 +1,16 @@ +package com.ruoyi.common.exception.user; + +/** + * 用户密码不正确或不符合规范异常类 + * + * @author ruoyi + */ +public class UserPasswordNotMatchException extends UserException +{ + private static final long serialVersionUID = 1L; + + public UserPasswordNotMatchException() + { + super("user.password.not.match", null); + } +} diff --git a/src/main/java/com/ruoyi/common/exception/user/UserPasswordRetryLimitExceedException.java b/src/main/java/com/ruoyi/common/exception/user/UserPasswordRetryLimitExceedException.java new file mode 100644 index 0000000..c887cf1 --- /dev/null +++ b/src/main/java/com/ruoyi/common/exception/user/UserPasswordRetryLimitExceedException.java @@ -0,0 +1,16 @@ +package com.ruoyi.common.exception.user; + +/** + * 用户错误最大次数异常类 + * + * @author ruoyi + */ +public class UserPasswordRetryLimitExceedException extends UserException +{ + private static final long serialVersionUID = 1L; + + public UserPasswordRetryLimitExceedException(int retryLimitCount, int lockTime) + { + super("user.password.retry.limit.exceed", new Object[] { retryLimitCount, lockTime }); + } +} diff --git a/src/main/java/com/ruoyi/common/filter/PropertyPreExcludeFilter.java b/src/main/java/com/ruoyi/common/filter/PropertyPreExcludeFilter.java new file mode 100644 index 0000000..e1e431b --- /dev/null +++ b/src/main/java/com/ruoyi/common/filter/PropertyPreExcludeFilter.java @@ -0,0 +1,24 @@ +package com.ruoyi.common.filter; + +import com.alibaba.fastjson2.filter.SimplePropertyPreFilter; + +/** + * 排除JSON敏感属性 + * + * @author ruoyi + */ +public class PropertyPreExcludeFilter extends SimplePropertyPreFilter +{ + public PropertyPreExcludeFilter() + { + } + + public PropertyPreExcludeFilter addExcludes(String... filters) + { + for (int i = 0; i < filters.length; i++) + { + this.getExcludes().add(filters[i]); + } + return this; + } +} diff --git a/src/main/java/com/ruoyi/common/filter/RepeatableFilter.java b/src/main/java/com/ruoyi/common/filter/RepeatableFilter.java new file mode 100644 index 0000000..a1bcfe2 --- /dev/null +++ b/src/main/java/com/ruoyi/common/filter/RepeatableFilter.java @@ -0,0 +1,52 @@ +package com.ruoyi.common.filter; + +import java.io.IOException; +import javax.servlet.Filter; +import javax.servlet.FilterChain; +import javax.servlet.FilterConfig; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletRequest; +import org.springframework.http.MediaType; +import com.ruoyi.common.utils.StringUtils; + +/** + * 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() + { + + } +} diff --git a/src/main/java/com/ruoyi/common/filter/RepeatedlyRequestWrapper.java b/src/main/java/com/ruoyi/common/filter/RepeatedlyRequestWrapper.java new file mode 100644 index 0000000..407d1ba --- /dev/null +++ b/src/main/java/com/ruoyi/common/filter/RepeatedlyRequestWrapper.java @@ -0,0 +1,76 @@ +package com.ruoyi.common.filter; + +import java.io.BufferedReader; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStreamReader; +import javax.servlet.ReadListener; +import javax.servlet.ServletInputStream; +import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletRequestWrapper; +import com.ruoyi.common.utils.http.HttpHelper; +import com.ruoyi.common.constant.Constants; + +/** + * 构建可重复读取inputStream的request + * + * @author ruoyi + */ +public class RepeatedlyRequestWrapper extends HttpServletRequestWrapper +{ + private final byte[] body; + + public RepeatedlyRequestWrapper(HttpServletRequest request, ServletResponse response) throws IOException + { + super(request); + request.setCharacterEncoding(Constants.UTF8); + response.setCharacterEncoding(Constants.UTF8); + + body = HttpHelper.getBodyString(request).getBytes(Constants.UTF8); + } + + @Override + public BufferedReader getReader() throws IOException + { + return new BufferedReader(new InputStreamReader(getInputStream())); + } + + @Override + public ServletInputStream getInputStream() throws IOException + { + final ByteArrayInputStream bais = new ByteArrayInputStream(body); + return new ServletInputStream() + { + @Override + public int read() throws IOException + { + return bais.read(); + } + + @Override + public int available() throws IOException + { + return body.length; + } + + @Override + public boolean isFinished() + { + return false; + } + + @Override + public boolean isReady() + { + return false; + } + + @Override + public void setReadListener(ReadListener readListener) + { + + } + }; + } +} diff --git a/src/main/java/com/ruoyi/common/filter/XssFilter.java b/src/main/java/com/ruoyi/common/filter/XssFilter.java new file mode 100644 index 0000000..9052f0d --- /dev/null +++ b/src/main/java/com/ruoyi/common/filter/XssFilter.java @@ -0,0 +1,75 @@ +package com.ruoyi.common.filter; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import javax.servlet.Filter; +import javax.servlet.FilterChain; +import javax.servlet.FilterConfig; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.enums.HttpMethod; + +/** + * 防止XSS攻击的过滤器 + * + * @author ruoyi + */ +public class XssFilter implements Filter +{ + /** + * 排除链接 + */ + public List excludes = new ArrayList<>(); + + @Override + public void init(FilterConfig filterConfig) throws ServletException + { + String tempExcludes = filterConfig.getInitParameter("excludes"); + if (StringUtils.isNotEmpty(tempExcludes)) + { + String[] url = tempExcludes.split(","); + for (int i = 0; url != null && i < url.length; i++) + { + excludes.add(url[i]); + } + } + } + + @Override + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) + throws IOException, ServletException + { + HttpServletRequest req = (HttpServletRequest) request; + HttpServletResponse resp = (HttpServletResponse) response; + if (handleExcludeURL(req, resp)) + { + chain.doFilter(request, response); + return; + } + XssHttpServletRequestWrapper xssRequest = new XssHttpServletRequestWrapper((HttpServletRequest) request); + chain.doFilter(xssRequest, response); + } + + private boolean handleExcludeURL(HttpServletRequest request, HttpServletResponse response) + { + String url = request.getServletPath(); + String method = request.getMethod(); + // GET DELETE 不过滤 + if (method == null || HttpMethod.GET.matches(method) || HttpMethod.DELETE.matches(method)) + { + return true; + } + return StringUtils.matches(url, excludes); + } + + @Override + public void destroy() + { + + } +} \ No newline at end of file diff --git a/src/main/java/com/ruoyi/common/filter/XssHttpServletRequestWrapper.java b/src/main/java/com/ruoyi/common/filter/XssHttpServletRequestWrapper.java new file mode 100644 index 0000000..05149f0 --- /dev/null +++ b/src/main/java/com/ruoyi/common/filter/XssHttpServletRequestWrapper.java @@ -0,0 +1,111 @@ +package com.ruoyi.common.filter; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import javax.servlet.ReadListener; +import javax.servlet.ServletInputStream; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletRequestWrapper; +import org.apache.commons.io.IOUtils; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.html.EscapeUtil; + +/** + * XSS过滤处理 + * + * @author ruoyi + */ +public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper +{ + /** + * @param request + */ + public XssHttpServletRequestWrapper(HttpServletRequest request) + { + super(request); + } + + @Override + public String[] getParameterValues(String name) + { + String[] values = super.getParameterValues(name); + if (values != null) + { + int length = values.length; + String[] escapesValues = new String[length]; + for (int i = 0; i < length; i++) + { + // 防xss攻击和过滤前后空格 + escapesValues[i] = EscapeUtil.clean(values[i]).trim(); + } + return escapesValues; + } + return super.getParameterValues(name); + } + + @Override + public ServletInputStream getInputStream() throws IOException + { + // 非json类型,直接返回 + if (!isJsonRequest()) + { + return super.getInputStream(); + } + + // 为空,直接返回 + String json = IOUtils.toString(super.getInputStream(), "utf-8"); + if (StringUtils.isEmpty(json)) + { + return super.getInputStream(); + } + + // xss过滤 + json = EscapeUtil.clean(json).trim(); + byte[] jsonBytes = json.getBytes("utf-8"); + final ByteArrayInputStream bis = new ByteArrayInputStream(jsonBytes); + return new ServletInputStream() + { + @Override + public boolean isFinished() + { + return true; + } + + @Override + public boolean isReady() + { + return true; + } + + @Override + public int available() throws IOException + { + return jsonBytes.length; + } + + @Override + public void setReadListener(ReadListener readListener) + { + } + + @Override + public int read() throws IOException + { + return bis.read(); + } + }; + } + + /** + * 是否是Json请求 + * + * @param request + */ + public boolean isJsonRequest() + { + String header = super.getHeader(HttpHeaders.CONTENT_TYPE); + return StringUtils.startsWithIgnoreCase(header, MediaType.APPLICATION_JSON_VALUE); + } +} \ No newline at end of file diff --git a/src/main/java/com/ruoyi/common/utils/Arith.java b/src/main/java/com/ruoyi/common/utils/Arith.java new file mode 100644 index 0000000..48a650e --- /dev/null +++ b/src/main/java/com/ruoyi/common/utils/Arith.java @@ -0,0 +1,114 @@ +package com.ruoyi.common.utils; + +import java.math.BigDecimal; +import java.math.RoundingMode; + +/** + * 精确的浮点数运算 + * + * @author ruoyi + */ +public class Arith +{ + + /** 默认除法运算精度 */ + private static final int DEF_DIV_SCALE = 10; + + /** 这个类不能实例化 */ + private Arith() + { + } + + /** + * 提供精确的加法运算。 + * @param v1 被加数 + * @param v2 加数 + * @return 两个参数的和 + */ + public static double add(double v1, double v2) + { + BigDecimal b1 = new BigDecimal(Double.toString(v1)); + BigDecimal b2 = new BigDecimal(Double.toString(v2)); + return b1.add(b2).doubleValue(); + } + + /** + * 提供精确的减法运算。 + * @param v1 被减数 + * @param v2 减数 + * @return 两个参数的差 + */ + public static double sub(double v1, double v2) + { + BigDecimal b1 = new BigDecimal(Double.toString(v1)); + BigDecimal b2 = new BigDecimal(Double.toString(v2)); + return b1.subtract(b2).doubleValue(); + } + + /** + * 提供精确的乘法运算。 + * @param v1 被乘数 + * @param v2 乘数 + * @return 两个参数的积 + */ + public static double mul(double v1, double v2) + { + BigDecimal b1 = new BigDecimal(Double.toString(v1)); + BigDecimal b2 = new BigDecimal(Double.toString(v2)); + return b1.multiply(b2).doubleValue(); + } + + /** + * 提供(相对)精确的除法运算,当发生除不尽的情况时,精确到 + * 小数点以后10位,以后的数字四舍五入。 + * @param v1 被除数 + * @param v2 除数 + * @return 两个参数的商 + */ + public static double div(double v1, double v2) + { + return div(v1, v2, DEF_DIV_SCALE); + } + + /** + * 提供(相对)精确的除法运算。当发生除不尽的情况时,由scale参数指 + * 定精度,以后的数字四舍五入。 + * @param v1 被除数 + * @param v2 除数 + * @param scale 表示表示需要精确到小数点以后几位。 + * @return 两个参数的商 + */ + public static double div(double v1, double v2, int scale) + { + if (scale < 0) + { + throw new IllegalArgumentException( + "The scale must be a positive integer or zero"); + } + BigDecimal b1 = new BigDecimal(Double.toString(v1)); + BigDecimal b2 = new BigDecimal(Double.toString(v2)); + if (b1.compareTo(BigDecimal.ZERO) == 0) + { + return BigDecimal.ZERO.doubleValue(); + } + return b1.divide(b2, scale, RoundingMode.HALF_UP).doubleValue(); + } + + /** + * 提供精确的小数位四舍五入处理。 + * @param v 需要四舍五入的数字 + * @param scale 小数点后保留几位 + * @return 四舍五入后的结果 + */ + public static double round(double v, int scale) + { + if (scale < 0) + { + throw new IllegalArgumentException( + "The scale must be a positive integer or zero"); + } + BigDecimal b = new BigDecimal(Double.toString(v)); + BigDecimal one = new BigDecimal("1"); + return b.divide(one, scale, RoundingMode.HALF_UP).doubleValue(); + } +} diff --git a/src/main/java/com/ruoyi/common/utils/DateUtils.java b/src/main/java/com/ruoyi/common/utils/DateUtils.java new file mode 100644 index 0000000..fb2ae21 --- /dev/null +++ b/src/main/java/com/ruoyi/common/utils/DateUtils.java @@ -0,0 +1,191 @@ +package com.ruoyi.common.utils; + +import java.lang.management.ManagementFactory; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.util.Date; +import org.apache.commons.lang3.time.DateFormatUtils; + +/** + * 时间工具类 + * + * @author ruoyi + */ +public class DateUtils extends org.apache.commons.lang3.time.DateUtils +{ + public static String YYYY = "yyyy"; + + public static String YYYY_MM = "yyyy-MM"; + + public static String YYYY_MM_DD = "yyyy-MM-dd"; + + public static String YYYYMMDDHHMMSS = "yyyyMMddHHmmss"; + + public static String YYYY_MM_DD_HH_MM_SS = "yyyy-MM-dd HH:mm:ss"; + + private static String[] parsePatterns = { + "yyyy-MM-dd", "yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd HH:mm", "yyyy-MM", + "yyyy/MM/dd", "yyyy/MM/dd HH:mm:ss", "yyyy/MM/dd HH:mm", "yyyy/MM", + "yyyy.MM.dd", "yyyy.MM.dd HH:mm:ss", "yyyy.MM.dd HH:mm", "yyyy.MM"}; + + /** + * 获取当前Date型日期 + * + * @return Date() 当前日期 + */ + public static Date getNowDate() + { + return new Date(); + } + + /** + * 获取当前日期, 默认格式为yyyy-MM-dd + * + * @return String + */ + public static String getDate() + { + return dateTimeNow(YYYY_MM_DD); + } + + public static final String getTime() + { + return dateTimeNow(YYYY_MM_DD_HH_MM_SS); + } + + public static final String dateTimeNow() + { + return dateTimeNow(YYYYMMDDHHMMSS); + } + + public static final String dateTimeNow(final String format) + { + return parseDateToStr(format, new Date()); + } + + public static final String dateTime(final Date date) + { + return parseDateToStr(YYYY_MM_DD, date); + } + + public static final String parseDateToStr(final String format, final Date date) + { + return new SimpleDateFormat(format).format(date); + } + + public static final Date dateTime(final String format, final String ts) + { + try + { + return new SimpleDateFormat(format).parse(ts); + } + catch (ParseException e) + { + throw new RuntimeException(e); + } + } + + /** + * 日期路径 即年/月/日 如2018/08/08 + */ + public static final String datePath() + { + Date now = new Date(); + return DateFormatUtils.format(now, "yyyy/MM/dd"); + } + + /** + * 日期路径 即年/月/日 如20180808 + */ + public static final String dateTime() + { + Date now = new Date(); + return DateFormatUtils.format(now, "yyyyMMdd"); + } + + /** + * 日期型字符串转化为日期 格式 + */ + public static Date parseDate(Object str) + { + if (str == null) + { + return null; + } + try + { + return parseDate(str.toString(), parsePatterns); + } + catch (ParseException e) + { + return null; + } + } + + /** + * 获取服务器启动时间 + */ + public static Date getServerStartDate() + { + long time = ManagementFactory.getRuntimeMXBean().getStartTime(); + return new Date(time); + } + + /** + * 计算相差天数 + */ + public static int differentDaysByMillisecond(Date date1, Date date2) + { + return Math.abs((int) ((date2.getTime() - date1.getTime()) / (1000 * 3600 * 24))); + } + + /** + * 计算时间差 + * + * @param endDate 最后时间 + * @param startTime 开始时间 + * @return 时间差(天/小时/分钟) + */ + public static String timeDistance(Date endDate, Date startTime) + { + long nd = 1000 * 24 * 60 * 60; + long nh = 1000 * 60 * 60; + long nm = 1000 * 60; + // long ns = 1000; + // 获得两个时间的毫秒时间差异 + long diff = endDate.getTime() - startTime.getTime(); + // 计算差多少天 + long day = diff / nd; + // 计算差多少小时 + long hour = diff % nd / nh; + // 计算差多少分钟 + long min = diff % nd % nh / nm; + // 计算差多少秒//输出结果 + // long sec = diff % nd % nh % nm / ns; + return day + "天" + hour + "小时" + min + "分钟"; + } + + /** + * 增加 LocalDateTime ==> Date + */ + public static Date toDate(LocalDateTime temporalAccessor) + { + ZonedDateTime zdt = temporalAccessor.atZone(ZoneId.systemDefault()); + return Date.from(zdt.toInstant()); + } + + /** + * 增加 LocalDate ==> Date + */ + public static Date toDate(LocalDate temporalAccessor) + { + LocalDateTime localDateTime = LocalDateTime.of(temporalAccessor, LocalTime.of(0, 0, 0)); + ZonedDateTime zdt = localDateTime.atZone(ZoneId.systemDefault()); + return Date.from(zdt.toInstant()); + } +} diff --git a/src/main/java/com/ruoyi/common/utils/DictUtils.java b/src/main/java/com/ruoyi/common/utils/DictUtils.java new file mode 100644 index 0000000..d9f811e --- /dev/null +++ b/src/main/java/com/ruoyi/common/utils/DictUtils.java @@ -0,0 +1,186 @@ +package com.ruoyi.common.utils; + +import java.util.Collection; +import java.util.List; +import com.alibaba.fastjson2.JSONArray; +import com.ruoyi.common.constant.CacheConstants; +import com.ruoyi.common.utils.spring.SpringUtils; +import com.ruoyi.framework.redis.RedisCache; +import com.ruoyi.project.system.domain.SysDictData; + +/** + * 字典工具类 + * + * @author ruoyi + */ +public class DictUtils +{ + /** + * 分隔符 + */ + public static final String SEPARATOR = ","; + + /** + * 设置字典缓存 + * + * @param key 参数键 + * @param dictDatas 字典数据列表 + */ + public static void setDictCache(String key, List dictDatas) + { + SpringUtils.getBean(RedisCache.class).setCacheObject(getCacheKey(key), dictDatas); + } + + /** + * 获取字典缓存 + * + * @param key 参数键 + * @return dictDatas 字典数据列表 + */ + public static List getDictCache(String key) + { + JSONArray arrayCache = SpringUtils.getBean(RedisCache.class).getCacheObject(getCacheKey(key)); + if (StringUtils.isNotNull(arrayCache)) + { + return arrayCache.toList(SysDictData.class); + } + 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 datas = getDictCache(dictType); + + if (StringUtils.isNotEmpty(datas)) + { + if (StringUtils.containsAny(separator, dictValue)) + { + for (SysDictData dict : datas) + { + for (String value : dictValue.split(separator)) + { + if (value.equals(dict.getDictValue())) + { + propertyString.append(dict.getDictLabel()).append(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 datas = getDictCache(dictType); + + if (StringUtils.containsAny(separator, dictLabel) && StringUtils.isNotEmpty(datas)) + { + for (SysDictData dict : datas) + { + for (String label : dictLabel.split(separator)) + { + if (label.equals(dict.getDictLabel())) + { + propertyString.append(dict.getDictValue()).append(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 keys = SpringUtils.getBean(RedisCache.class).keys(CacheConstants.SYS_DICT_KEY + "*"); + SpringUtils.getBean(RedisCache.class).deleteObject(keys); + } + + /** + * 设置cache key + * + * @param configKey 参数键 + * @return 缓存键key + */ + public static String getCacheKey(String configKey) + { + return CacheConstants.SYS_DICT_KEY + configKey; + } +} diff --git a/src/main/java/com/ruoyi/common/utils/ExceptionUtil.java b/src/main/java/com/ruoyi/common/utils/ExceptionUtil.java new file mode 100644 index 0000000..214e4a0 --- /dev/null +++ b/src/main/java/com/ruoyi/common/utils/ExceptionUtil.java @@ -0,0 +1,39 @@ +package com.ruoyi.common.utils; + +import java.io.PrintWriter; +import java.io.StringWriter; +import org.apache.commons.lang3.exception.ExceptionUtils; + +/** + * 错误信息处理类。 + * + * @author ruoyi + */ +public class ExceptionUtil +{ + /** + * 获取exception的详细错误信息。 + */ + public static String getExceptionMessage(Throwable e) + { + StringWriter sw = new StringWriter(); + e.printStackTrace(new PrintWriter(sw, true)); + return sw.toString(); + } + + public static String getRootErrorMessage(Exception e) + { + Throwable root = ExceptionUtils.getRootCause(e); + root = (root == null ? e : root); + if (root == null) + { + return ""; + } + String msg = root.getMessage(); + if (msg == null) + { + return "null"; + } + return StringUtils.defaultString(msg); + } +} diff --git a/src/main/java/com/ruoyi/common/utils/LogUtils.java b/src/main/java/com/ruoyi/common/utils/LogUtils.java new file mode 100644 index 0000000..0de30c6 --- /dev/null +++ b/src/main/java/com/ruoyi/common/utils/LogUtils.java @@ -0,0 +1,18 @@ +package com.ruoyi.common.utils; + +/** + * 处理并记录日志文件 + * + * @author ruoyi + */ +public class LogUtils +{ + public static String getBlock(Object msg) + { + if (msg == null) + { + msg = ""; + } + return "[" + msg.toString() + "]"; + } +} diff --git a/src/main/java/com/ruoyi/common/utils/MessageUtils.java b/src/main/java/com/ruoyi/common/utils/MessageUtils.java new file mode 100644 index 0000000..7dac75a --- /dev/null +++ b/src/main/java/com/ruoyi/common/utils/MessageUtils.java @@ -0,0 +1,26 @@ +package com.ruoyi.common.utils; + +import org.springframework.context.MessageSource; +import org.springframework.context.i18n.LocaleContextHolder; +import com.ruoyi.common.utils.spring.SpringUtils; + +/** + * 获取i18n资源文件 + * + * @author ruoyi + */ +public class MessageUtils +{ + /** + * 根据消息键和参数 获取消息 委托给spring messageSource + * + * @param code 消息键 + * @param args 参数 + * @return 获取国际化翻译值 + */ + public static String message(String code, Object... args) + { + MessageSource messageSource = SpringUtils.getBean(MessageSource.class); + return messageSource.getMessage(code, args, LocaleContextHolder.getLocale()); + } +} diff --git a/src/main/java/com/ruoyi/common/utils/PageUtils.java b/src/main/java/com/ruoyi/common/utils/PageUtils.java new file mode 100644 index 0000000..b5349aa --- /dev/null +++ b/src/main/java/com/ruoyi/common/utils/PageUtils.java @@ -0,0 +1,35 @@ +package com.ruoyi.common.utils; + +import com.github.pagehelper.PageHelper; +import com.ruoyi.common.utils.sql.SqlUtil; +import com.ruoyi.framework.web.page.PageDomain; +import com.ruoyi.framework.web.page.TableSupport; + +/** + * 分页工具类 + * + * @author ruoyi + */ +public class PageUtils extends PageHelper +{ + /** + * 设置请求分页数据 + */ + public static void startPage() + { + PageDomain pageDomain = TableSupport.buildPageRequest(); + Integer pageNum = pageDomain.getPageNum(); + Integer pageSize = pageDomain.getPageSize(); + String orderBy = SqlUtil.escapeOrderBySql(pageDomain.getOrderBy()); + Boolean reasonable = pageDomain.getReasonable(); + PageHelper.startPage(pageNum, pageSize, orderBy).setReasonable(reasonable); + } + + /** + * 清理分页的线程变量 + */ + public static void clearPage() + { + PageHelper.clearPage(); + } +} diff --git a/src/main/java/com/ruoyi/common/utils/SecurityUtils.java b/src/main/java/com/ruoyi/common/utils/SecurityUtils.java new file mode 100644 index 0000000..467c9a1 --- /dev/null +++ b/src/main/java/com/ruoyi/common/utils/SecurityUtils.java @@ -0,0 +1,176 @@ +package com.ruoyi.common.utils; + +import java.util.Collection; +import java.util.List; +import java.util.stream.Collectors; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.util.PatternMatchUtils; +import com.ruoyi.common.constant.Constants; +import com.ruoyi.common.constant.HttpStatus; +import com.ruoyi.common.exception.ServiceException; +import com.ruoyi.framework.security.LoginUser; +import com.ruoyi.project.system.domain.SysRole; + +/** + * 安全服务工具类 + * + * @author ruoyi + */ +public class SecurityUtils +{ + /** + * 用户ID + **/ + public static Long getUserId() + { + try + { + return getLoginUser().getUserId(); + } + catch (Exception e) + { + throw new ServiceException("获取用户ID异常", HttpStatus.UNAUTHORIZED); + } + } + + /** + * 获取部门ID + **/ + public static Long getDeptId() + { + try + { + return getLoginUser().getDeptId(); + } + catch (Exception e) + { + throw new ServiceException("获取部门ID异常", HttpStatus.UNAUTHORIZED); + } + } + + /** + * 获取用户账户 + **/ + public static String getUsername() + { + try + { + return getLoginUser().getUsername(); + } + catch (Exception e) + { + throw new ServiceException("获取用户账户异常", HttpStatus.UNAUTHORIZED); + } + } + + /** + * 获取用户 + **/ + public static LoginUser getLoginUser() + { + try + { + return (LoginUser) getAuthentication().getPrincipal(); + } + catch (Exception e) + { + throw new ServiceException("获取用户信息异常", HttpStatus.UNAUTHORIZED); + } + } + + /** + * 获取Authentication + */ + public static Authentication getAuthentication() + { + return SecurityContextHolder.getContext().getAuthentication(); + } + + /** + * 生成BCryptPasswordEncoder密码 + * + * @param password 密码 + * @return 加密字符串 + */ + public static String encryptPassword(String password) + { + BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder(); + return passwordEncoder.encode(password); + } + + /** + * 判断密码是否相同 + * + * @param rawPassword 真实密码 + * @param encodedPassword 加密后字符 + * @return 结果 + */ + public static boolean matchesPassword(String rawPassword, String encodedPassword) + { + BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder(); + return passwordEncoder.matches(rawPassword, encodedPassword); + } + + /** + * 是否为管理员 + * + * @param userId 用户ID + * @return 结果 + */ + public static boolean isAdmin(Long userId) + { + return userId != null && 1L == userId; + } + + /** + * 验证用户是否具备某权限 + * + * @param permission 权限字符串 + * @return 用户是否具备某权限 + */ + public static boolean hasPermi(String permission) + { + return hasPermi(getLoginUser().getPermissions(), permission); + } + + /** + * 判断是否包含权限 + * + * @param authorities 权限列表 + * @param permission 权限字符串 + * @return 用户是否具备某权限 + */ + public static boolean hasPermi(Collection authorities, String permission) + { + return authorities.stream().filter(StringUtils::hasText) + .anyMatch(x -> Constants.ALL_PERMISSION.equals(x) || PatternMatchUtils.simpleMatch(x, permission)); + } + + /** + * 验证用户是否拥有某个角色 + * + * @param role 角色标识 + * @return 用户是否具备某角色 + */ + public static boolean hasRole(String role) + { + List roleList = getLoginUser().getUser().getRoles(); + Collection roles = roleList.stream().map(SysRole::getRoleKey).collect(Collectors.toSet()); + return hasRole(roles, role); + } + + /** + * 判断是否包含角色 + * + * @param roles 角色列表 + * @param role 角色 + * @return 用户是否具备某角色权限 + */ + public static boolean hasRole(Collection roles, String role) + { + return roles.stream().filter(StringUtils::hasText) + .anyMatch(x -> Constants.SUPER_ADMIN.equals(x) || PatternMatchUtils.simpleMatch(x, role)); + } +} diff --git a/src/main/java/com/ruoyi/common/utils/ServletUtils.java b/src/main/java/com/ruoyi/common/utils/ServletUtils.java new file mode 100644 index 0000000..febb603 --- /dev/null +++ b/src/main/java/com/ruoyi/common/utils/ServletUtils.java @@ -0,0 +1,218 @@ +package com.ruoyi.common.utils; + +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.net.URLDecoder; +import java.net.URLEncoder; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import javax.servlet.ServletRequest; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; +import org.springframework.web.context.request.RequestAttributes; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; +import com.ruoyi.common.constant.Constants; +import com.ruoyi.common.core.text.Convert; + +/** + * 客户端工具类 + * + * @author ruoyi + */ +public class ServletUtils +{ + /** + * 获取String参数 + */ + public static String getParameter(String name) + { + return getRequest().getParameter(name); + } + + /** + * 获取String参数 + */ + public static String getParameter(String name, String defaultValue) + { + return Convert.toStr(getRequest().getParameter(name), defaultValue); + } + + /** + * 获取Integer参数 + */ + public static Integer getParameterToInt(String name) + { + return Convert.toInt(getRequest().getParameter(name)); + } + + /** + * 获取Integer参数 + */ + public static Integer getParameterToInt(String name, Integer defaultValue) + { + return Convert.toInt(getRequest().getParameter(name), defaultValue); + } + + /** + * 获取Boolean参数 + */ + public static Boolean getParameterToBool(String name) + { + return Convert.toBool(getRequest().getParameter(name)); + } + + /** + * 获取Boolean参数 + */ + public static Boolean getParameterToBool(String name, Boolean defaultValue) + { + return Convert.toBool(getRequest().getParameter(name), defaultValue); + } + + /** + * 获得所有请求参数 + * + * @param request 请求对象{@link ServletRequest} + * @return Map + */ + public static Map getParams(ServletRequest request) + { + final Map map = request.getParameterMap(); + return Collections.unmodifiableMap(map); + } + + /** + * 获得所有请求参数 + * + * @param request 请求对象{@link ServletRequest} + * @return Map + */ + public static Map getParamMap(ServletRequest request) + { + Map params = new HashMap<>(); + for (Map.Entry entry : getParams(request).entrySet()) + { + params.put(entry.getKey(), StringUtils.join(entry.getValue(), ",")); + } + return params; + } + + /** + * 获取request + */ + public static HttpServletRequest getRequest() + { + return getRequestAttributes().getRequest(); + } + + /** + * 获取response + */ + public static HttpServletResponse getResponse() + { + return getRequestAttributes().getResponse(); + } + + /** + * 获取session + */ + public static HttpSession getSession() + { + return getRequest().getSession(); + } + + public static ServletRequestAttributes getRequestAttributes() + { + RequestAttributes attributes = RequestContextHolder.getRequestAttributes(); + return (ServletRequestAttributes) attributes; + } + + /** + * 将字符串渲染到客户端 + * + * @param response 渲染对象 + * @param string 待渲染的字符串 + */ + public static void renderString(HttpServletResponse response, String string) + { + try + { + response.setStatus(200); + response.setContentType("application/json"); + response.setCharacterEncoding("utf-8"); + response.getWriter().print(string); + } + catch (IOException e) + { + e.printStackTrace(); + } + } + + /** + * 是否是Ajax异步请求 + * + * @param request + */ + public static boolean isAjaxRequest(HttpServletRequest request) + { + String accept = request.getHeader("accept"); + if (accept != null && accept.contains("application/json")) + { + return true; + } + + String xRequestedWith = request.getHeader("X-Requested-With"); + if (xRequestedWith != null && xRequestedWith.contains("XMLHttpRequest")) + { + return true; + } + + String uri = request.getRequestURI(); + if (StringUtils.inStringIgnoreCase(uri, ".json", ".xml")) + { + return true; + } + + String ajax = request.getParameter("__ajax"); + return StringUtils.inStringIgnoreCase(ajax, "json", "xml"); + } + + /** + * 内容编码 + * + * @param str 内容 + * @return 编码后的内容 + */ + public static String urlEncode(String str) + { + try + { + return URLEncoder.encode(str, Constants.UTF8); + } + catch (UnsupportedEncodingException e) + { + return StringUtils.EMPTY; + } + } + + /** + * 内容解码 + * + * @param str 内容 + * @return 解码后的内容 + */ + public static String urlDecode(String str) + { + try + { + return URLDecoder.decode(str, Constants.UTF8); + } + catch (UnsupportedEncodingException e) + { + return StringUtils.EMPTY; + } + } +} diff --git a/src/main/java/com/ruoyi/common/utils/StringUtils.java b/src/main/java/com/ruoyi/common/utils/StringUtils.java new file mode 100644 index 0000000..ae435d2 --- /dev/null +++ b/src/main/java/com/ruoyi/common/utils/StringUtils.java @@ -0,0 +1,638 @@ +package com.ruoyi.common.utils; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import org.springframework.util.AntPathMatcher; +import com.ruoyi.common.constant.Constants; +import com.ruoyi.common.core.text.StrFormatter; + +/** + * 字符串工具类 + * + * @author ruoyi + */ +public class StringUtils extends org.apache.commons.lang3.StringUtils +{ + /** 空字符串 */ + private static final String NULLSTR = ""; + + /** 下划线 */ + private static final char SEPARATOR = '_'; + + /** + * 获取参数不为空值 + * + * @param value defaultValue 要判断的value + * @return value 返回值 + */ + public static T nvl(T value, T defaultValue) + { + return value != null ? value : defaultValue; + } + + /** + * * 判断一个Collection是否为空, 包含List,Set,Queue + * + * @param coll 要判断的Collection + * @return true:为空 false:非空 + */ + public static boolean isEmpty(Collection coll) + { + return isNull(coll) || coll.isEmpty(); + } + + /** + * * 判断一个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 isNull(objects) || (objects.length == 0); + } + + /** + * * 判断一个对象数组是否非空 + * + * @param objects 要判断的对象数组 + * @return true:非空 false:空 + */ + public static boolean isNotEmpty(Object[] objects) + { + return !isEmpty(objects); + } + + /** + * * 判断一个Map是否为空 + * + * @param map 要判断的Map + * @return true:为空 false:非空 + */ + public static boolean isEmpty(Map map) + { + return isNull(map) || map.isEmpty(); + } + + /** + * * 判断一个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 isNull(str) || NULLSTR.equals(str.trim()); + } + + /** + * * 判断一个字符串是否为非空串 + * + * @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 object == null; + } + + /** + * * 判断一个对象是否非空 + * + * @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 isNotNull(object) && object.getClass().isArray(); + } + + /** + * 去空格 + */ + public static String trim(String str) + { + return (str == null ? "" : str.trim()); + } + + /** + * 截取字符串 + * + * @param str 字符串 + * @param start 开始 + * @return 结果 + */ + public static String substring(final String str, int start) + { + if (str == null) + { + return NULLSTR; + } + + if (start < 0) + { + start = str.length() + start; + } + + if (start < 0) + { + start = 0; + } + if (start > str.length()) + { + return NULLSTR; + } + + return str.substring(start); + } + + /** + * 截取字符串 + * + * @param str 字符串 + * @param start 开始 + * @param end 结束 + * @return 结果 + */ + public static String substring(final String str, int start, int end) + { + if (str == null) + { + return NULLSTR; + } + + if (end < 0) + { + end = str.length() + end; + } + if (start < 0) + { + start = str.length() + start; + } + + if (end > str.length()) + { + end = str.length(); + } + + if (start > end) + { + return NULLSTR; + } + + if (start < 0) + { + start = 0; + } + if (end < 0) + { + end = 0; + } + + return str.substring(start, end); + } + + /** + * 判断是否为空,并且不是空白字符 + * + * @param str 要判断的value + * @return 结果 + */ + public static boolean hasText(String str) + { + return (str != null && !str.isEmpty() && containsText(str)); + } + + private static boolean containsText(CharSequence str) + { + int strLen = str.length(); + for (int i = 0; i < strLen; i++) + { + if (!Character.isWhitespace(str.charAt(i))) + { + return true; + } + } + return false; + } + + /** + * 格式化文本, {} 表示占位符
+ * 此方法只是简单将占位符 {} 按照顺序替换为参数
+ * 如果想输出 {} 使用 \\转义 { 即可,如果想输出 {} 之前的 \ 使用双转义符 \\\\ 即可
+ * 例:
+ * 通常使用:format("this is {} for {}", "a", "b") -> this is a for b
+ * 转义{}: format("this is \\{} for {}", "a", "b") -> this is \{} for a
+ * 转义\: format("this is \\\\{} for {}", "a", "b") -> this is \a for b
+ * + * @param template 文本模板,被替换的部分用 {} 表示 + * @param params 参数值 + * @return 格式化后的文本 + */ + public static String format(String template, Object... params) + { + if (isEmpty(params) || isEmpty(template)) + { + return template; + } + return StrFormatter.format(template, params); + } + + /** + * 是否为http(s)://开头 + * + * @param link 链接 + * @return 结果 + */ + public static boolean ishttp(String link) + { + return StringUtils.startsWithAny(link, Constants.HTTP, Constants.HTTPS); + } + + /** + * 字符串转set + * + * @param str 字符串 + * @param sep 分隔符 + * @return set集合 + */ + public static final Set 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 final List str2List(String str, String sep, boolean filterBlank, boolean trim) + { + List list = new ArrayList(); + if (StringUtils.isEmpty(str)) + { + return list; + } + + // 过滤空白字符串 + if (filterBlank && StringUtils.isBlank(str)) + { + return list; + } + String[] split = str.split(sep); + for (String string : split) + { + if (filterBlank && StringUtils.isBlank(string)) + { + continue; + } + if (trim) + { + string = string.trim(); + } + list.add(string); + } + + return list; + } + + /** + * 判断给定的collection列表中是否包含数组array 判断给定的数组array中是否包含给定的元素value + * + * @param collection 给定的集合 + * @param array 给定的数组 + * @return boolean 结果 + */ + public static boolean containsAny(Collection collection, String... array) + { + if (isEmpty(collection) || isEmpty(array)) + { + return false; + } + else + { + for (String str : array) + { + if (collection.contains(str)) + { + return true; + } + } + return false; + } + } + + /** + * 查找指定字符串是否包含指定字符串列表中的任意一个字符串同时串忽略大小写 + * + * @param cs 指定字符串 + * @param searchCharSequences 需要检查的字符串数组 + * @return 是否包含任意一个字符串 + */ + public static boolean containsAnyIgnoreCase(CharSequence cs, CharSequence... searchCharSequences) + { + if (isEmpty(cs) || isEmpty(searchCharSequences)) + { + return false; + } + for (CharSequence testStr : searchCharSequences) + { + if (containsIgnoreCase(cs, testStr)) + { + return true; + } + } + return false; + } + + /** + * 驼峰转下划线命名 + */ + public static String toUnderScoreCase(String str) + { + if (str == null) + { + return null; + } + StringBuilder sb = new StringBuilder(); + // 前置字符是否大写 + boolean preCharIsUpperCase = true; + // 当前字符是否大写 + boolean curreCharIsUpperCase = true; + // 下一字符是否大写 + boolean nexteCharIsUpperCase = true; + for (int i = 0; i < str.length(); i++) + { + char c = str.charAt(i); + if (i > 0) + { + preCharIsUpperCase = Character.isUpperCase(str.charAt(i - 1)); + } + else + { + preCharIsUpperCase = false; + } + + curreCharIsUpperCase = Character.isUpperCase(c); + + if (i < (str.length() - 1)) + { + nexteCharIsUpperCase = Character.isUpperCase(str.charAt(i + 1)); + } + + if (preCharIsUpperCase && curreCharIsUpperCase && !nexteCharIsUpperCase) + { + sb.append(SEPARATOR); + } + else if ((i != 0 && !preCharIsUpperCase) && curreCharIsUpperCase) + { + sb.append(SEPARATOR); + } + sb.append(Character.toLowerCase(c)); + } + + return sb.toString(); + } + + /** + * 是否包含字符串 + * + * @param str 验证字符串 + * @param strs 字符串组 + * @return 包含返回true + */ + public static boolean inStringIgnoreCase(String str, String... strs) + { + if (str != null && strs != null) + { + for (String s : strs) + { + if (str.equalsIgnoreCase(trim(s))) + { + return true; + } + } + } + return false; + } + + /** + * 将下划线大写方式命名的字符串转换为驼峰式。如果转换前的下划线大写方式命名的字符串为空,则返回空字符串。 例如:HELLO_WORLD->HelloWorld + * + * @param name 转换前的下划线大写方式命名的字符串 + * @return 转换后的驼峰式命名的字符串 + */ + public static String convertToCamelCase(String name) + { + StringBuilder result = new StringBuilder(); + // 快速检查 + if (name == null || name.isEmpty()) + { + // 没必要转换 + return ""; + } + else if (!name.contains("_")) + { + // 不含下划线,仅将首字母大写 + return name.substring(0, 1).toUpperCase() + name.substring(1); + } + // 用下划线将原始字符串分割 + String[] camels = name.split("_"); + for (String camel : camels) + { + // 跳过原始字符串中开头、结尾的下换线或双重下划线 + if (camel.isEmpty()) + { + continue; + } + // 首字母大写 + result.append(camel.substring(0, 1).toUpperCase()); + result.append(camel.substring(1).toLowerCase()); + } + return result.toString(); + } + + /** + * 驼峰式命名法 + * 例如:user_name->userName + */ + public static String toCamelCase(String s) + { + if (s == null) + { + return null; + } + if (s.indexOf(SEPARATOR) == -1) + { + return s; + } + s = s.toLowerCase(); + StringBuilder sb = new StringBuilder(s.length()); + boolean upperCase = false; + for (int i = 0; i < s.length(); i++) + { + char c = s.charAt(i); + + if (c == SEPARATOR) + { + upperCase = true; + } + else if (upperCase) + { + sb.append(Character.toUpperCase(c)); + upperCase = false; + } + else + { + sb.append(c); + } + } + return sb.toString(); + } + + /** + * 查找指定字符串是否匹配指定字符串列表中的任意一个字符串 + * + * @param str 指定字符串 + * @param strs 需要检查的字符串数组 + * @return 是否匹配 + */ + public static boolean matches(String str, List 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) + { + AntPathMatcher matcher = new AntPathMatcher(); + return matcher.match(pattern, url); + } + + @SuppressWarnings("unchecked") + public static T cast(Object obj) + { + return (T) obj; + } + + /** + * 数字左边补齐0,使之达到指定长度。注意,如果数字转换为字符串后,长度大于size,则只保留 最后size个字符。 + * + * @param num 数字对象 + * @param size 字符串指定长度 + * @return 返回数字的字符串格式,该字符串为指定长度。 + */ + public static final String padl(final Number num, final int size) + { + return padl(num.toString(), size, '0'); + } + + /** + * 字符串左补齐。如果原始字符串s长度大于size,则只保留最后size个字符。 + * + * @param s 原始字符串 + * @param size 字符串指定长度 + * @param c 用于补齐的字符 + * @return 返回指定长度的字符串,由原字符串左补齐或截取得到。 + */ + public static final String padl(final String s, final int size, final char c) + { + final StringBuilder sb = new StringBuilder(size); + if (s != null) + { + final int len = s.length(); + if (s.length() <= size) + { + for (int i = size - len; i > 0; i--) + { + sb.append(c); + } + sb.append(s); + } + else + { + return s.substring(len - size, len); + } + } + else + { + for (int i = size; i > 0; i--) + { + sb.append(c); + } + } + return sb.toString(); + } +} \ No newline at end of file diff --git a/src/main/java/com/ruoyi/common/utils/Threads.java b/src/main/java/com/ruoyi/common/utils/Threads.java new file mode 100644 index 0000000..71fe6d5 --- /dev/null +++ b/src/main/java/com/ruoyi/common/utils/Threads.java @@ -0,0 +1,99 @@ +package com.ruoyi.common.utils; + +import java.util.concurrent.CancellationException; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * 线程相关工具类. + * + * @author ruoyi + */ +public class Threads +{ + private static final Logger logger = LoggerFactory.getLogger(Threads.class); + + /** + * sleep等待,单位为毫秒 + */ + public static void sleep(long milliseconds) + { + try + { + Thread.sleep(milliseconds); + } + catch (InterruptedException e) + { + return; + } + } + + /** + * 停止线程池 + * 先使用shutdown, 停止接收新任务并尝试完成所有已存在任务. + * 如果超时, 则调用shutdownNow, 取消在workQueue中Pending的任务,并中断所有阻塞函数. + * 如果仍然超時,則強制退出. + * 另对在shutdown时线程本身被调用中断做了处理. + */ + public static void shutdownAndAwaitTermination(ExecutorService pool) + { + if (pool != null && !pool.isShutdown()) + { + pool.shutdown(); + try + { + if (!pool.awaitTermination(120, TimeUnit.SECONDS)) + { + pool.shutdownNow(); + if (!pool.awaitTermination(120, TimeUnit.SECONDS)) + { + logger.info("Pool did not terminate"); + } + } + } + catch (InterruptedException ie) + { + pool.shutdownNow(); + Thread.currentThread().interrupt(); + } + } + } + + /** + * 打印线程异常信息 + */ + public static void printException(Runnable r, Throwable t) + { + if (t == null && r instanceof Future) + { + try + { + Future future = (Future) r; + if (future.isDone()) + { + future.get(); + } + } + catch (CancellationException ce) + { + t = ce; + } + catch (ExecutionException ee) + { + t = ee.getCause(); + } + catch (InterruptedException ie) + { + Thread.currentThread().interrupt(); + } + } + if (t != null) + { + logger.error(t.getMessage(), t); + } + } +} diff --git a/src/main/java/com/ruoyi/common/utils/bean/BeanUtils.java b/src/main/java/com/ruoyi/common/utils/bean/BeanUtils.java new file mode 100644 index 0000000..4463662 --- /dev/null +++ b/src/main/java/com/ruoyi/common/utils/bean/BeanUtils.java @@ -0,0 +1,110 @@ +package com.ruoyi.common.utils.bean; + +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * Bean 工具类 + * + * @author ruoyi + */ +public class BeanUtils extends org.springframework.beans.BeanUtils +{ + /** Bean方法名中属性名开始的下标 */ + private static final int BEAN_METHOD_PROP_INDEX = 3; + + /** * 匹配getter方法的正则表达式 */ + private static final Pattern GET_PATTERN = Pattern.compile("get(\\p{javaUpperCase}\\w*)"); + + /** * 匹配setter方法的正则表达式 */ + private static final Pattern SET_PATTERN = Pattern.compile("set(\\p{javaUpperCase}\\w*)"); + + /** + * Bean属性复制工具方法。 + * + * @param dest 目标对象 + * @param src 源对象 + */ + public static void copyBeanProp(Object dest, Object src) + { + try + { + copyProperties(src, dest); + } + catch (Exception e) + { + e.printStackTrace(); + } + } + + /** + * 获取对象的setter方法。 + * + * @param obj 对象 + * @return 对象的setter方法列表 + */ + public static List getSetterMethods(Object obj) + { + // setter方法列表 + List setterMethods = new ArrayList(); + + // 获取所有方法 + Method[] methods = obj.getClass().getMethods(); + + // 查找setter方法 + + for (Method method : methods) + { + Matcher m = SET_PATTERN.matcher(method.getName()); + if (m.matches() && (method.getParameterTypes().length == 1)) + { + setterMethods.add(method); + } + } + // 返回setter方法列表 + return setterMethods; + } + + /** + * 获取对象的getter方法。 + * + * @param obj 对象 + * @return 对象的getter方法列表 + */ + + public static List getGetterMethods(Object obj) + { + // getter方法列表 + List getterMethods = new ArrayList(); + // 获取所有方法 + Method[] methods = obj.getClass().getMethods(); + // 查找getter方法 + for (Method method : methods) + { + Matcher m = GET_PATTERN.matcher(method.getName()); + if (m.matches() && (method.getParameterTypes().length == 0)) + { + getterMethods.add(method); + } + } + // 返回getter方法列表 + return getterMethods; + } + + /** + * 检查Bean方法名中的属性名是否相等。
+ * 如getName()和setName()属性名一样,getName()和setAge()属性名不一样。 + * + * @param m1 方法名1 + * @param m2 方法名2 + * @return 属性名一样返回true,否则返回false + */ + + public static boolean isMethodPropEquals(String m1, String m2) + { + return m1.substring(BEAN_METHOD_PROP_INDEX).equals(m2.substring(BEAN_METHOD_PROP_INDEX)); + } +} diff --git a/src/main/java/com/ruoyi/common/utils/bean/BeanValidators.java b/src/main/java/com/ruoyi/common/utils/bean/BeanValidators.java new file mode 100644 index 0000000..80bfed7 --- /dev/null +++ b/src/main/java/com/ruoyi/common/utils/bean/BeanValidators.java @@ -0,0 +1,24 @@ +package com.ruoyi.common.utils.bean; + +import java.util.Set; +import javax.validation.ConstraintViolation; +import javax.validation.ConstraintViolationException; +import javax.validation.Validator; + +/** + * bean对象属性验证 + * + * @author ruoyi + */ +public class BeanValidators +{ + public static void validateWithException(Validator validator, Object object, Class... groups) + throws ConstraintViolationException + { + Set> constraintViolations = validator.validate(object, groups); + if (!constraintViolations.isEmpty()) + { + throw new ConstraintViolationException(constraintViolations); + } + } +} diff --git a/src/main/java/com/ruoyi/common/utils/file/FileTypeUtils.java b/src/main/java/com/ruoyi/common/utils/file/FileTypeUtils.java new file mode 100644 index 0000000..68130b9 --- /dev/null +++ b/src/main/java/com/ruoyi/common/utils/file/FileTypeUtils.java @@ -0,0 +1,76 @@ +package com.ruoyi.common.utils.file; + +import java.io.File; +import org.apache.commons.lang3.StringUtils; + +/** + * 文件类型工具类 + * + * @author ruoyi + */ +public class FileTypeUtils +{ + /** + * 获取文件类型 + *

+ * 例如: ruoyi.txt, 返回: txt + * + * @param file 文件名 + * @return 后缀(不含".") + */ + public static String getFileType(File file) + { + if (null == file) + { + return StringUtils.EMPTY; + } + return getFileType(file.getName()); + } + + /** + * 获取文件类型 + *

+ * 例如: ruoyi.txt, 返回: txt + * + * @param fileName 文件名 + * @return 后缀(不含".") + */ + public static String getFileType(String fileName) + { + int separatorIndex = fileName.lastIndexOf("."); + if (separatorIndex < 0) + { + return ""; + } + return fileName.substring(separatorIndex + 1).toLowerCase(); + } + + /** + * 获取文件类型 + * + * @param photoByte 文件字节码 + * @return 后缀(不含".") + */ + public static String getFileExtendName(byte[] photoByte) + { + String strFileExtendName = "JPG"; + if ((photoByte[0] == 71) && (photoByte[1] == 73) && (photoByte[2] == 70) && (photoByte[3] == 56) + && ((photoByte[4] == 55) || (photoByte[4] == 57)) && (photoByte[5] == 97)) + { + strFileExtendName = "GIF"; + } + else if ((photoByte[6] == 74) && (photoByte[7] == 70) && (photoByte[8] == 73) && (photoByte[9] == 70)) + { + strFileExtendName = "JPG"; + } + else if ((photoByte[0] == 66) && (photoByte[1] == 77)) + { + strFileExtendName = "BMP"; + } + else if ((photoByte[1] == 80) && (photoByte[2] == 78) && (photoByte[3] == 71)) + { + strFileExtendName = "PNG"; + } + return strFileExtendName; + } +} \ No newline at end of file diff --git a/src/main/java/com/ruoyi/common/utils/file/FileUploadUtils.java b/src/main/java/com/ruoyi/common/utils/file/FileUploadUtils.java new file mode 100644 index 0000000..e13dcb3 --- /dev/null +++ b/src/main/java/com/ruoyi/common/utils/file/FileUploadUtils.java @@ -0,0 +1,232 @@ +package com.ruoyi.common.utils.file; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Paths; +import java.util.Objects; +import org.apache.commons.io.FilenameUtils; +import org.springframework.web.multipart.MultipartFile; +import com.ruoyi.common.constant.Constants; +import com.ruoyi.common.exception.file.FileNameLengthLimitExceededException; +import com.ruoyi.common.exception.file.FileSizeLimitExceededException; +import com.ruoyi.common.exception.file.InvalidExtensionException; +import com.ruoyi.common.utils.DateUtils; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.uuid.Seq; +import com.ruoyi.framework.config.RuoYiConfig; + +/** + * 文件上传工具类 + * + * @author ruoyi + */ +public class FileUploadUtils +{ + /** + * 默认大小 50M + */ + public static final long DEFAULT_MAX_SIZE = 50 * 1024 * 1024; + + /** + * 默认的文件名最大长度 100 + */ + public static final int DEFAULT_FILE_NAME_LENGTH = 100; + + /** + * 默认上传的地址 + */ + private static String defaultBaseDir = RuoYiConfig.getProfile(); + + public static void setDefaultBaseDir(String defaultBaseDir) + { + FileUploadUtils.defaultBaseDir = defaultBaseDir; + } + + public static String getDefaultBaseDir() + { + return defaultBaseDir; + } + + /** + * 以默认配置进行文件上传 + * + * @param file 上传的文件 + * @return 文件名称 + * @throws Exception + */ + public static final String upload(MultipartFile file) throws IOException + { + try + { + return upload(getDefaultBaseDir(), file, MimeTypeUtils.DEFAULT_ALLOWED_EXTENSION); + } + catch (Exception e) + { + throw new IOException(e.getMessage(), e); + } + } + + /** + * 根据文件路径上传 + * + * @param baseDir 相对应用的基目录 + * @param file 上传的文件 + * @return 文件名称 + * @throws IOException + */ + public static final String upload(String baseDir, MultipartFile file) throws IOException + { + try + { + return upload(baseDir, file, MimeTypeUtils.DEFAULT_ALLOWED_EXTENSION); + } + catch (Exception e) + { + throw new IOException(e.getMessage(), e); + } + } + + /** + * 文件上传 + * + * @param baseDir 相对应用的基目录 + * @param file 上传的文件 + * @param allowedExtension 上传文件类型 + * @return 返回上传成功的文件名 + * @throws FileSizeLimitExceededException 如果超出最大大小 + * @throws FileNameLengthLimitExceededException 文件名太长 + * @throws IOException 比如读写文件出错时 + * @throws InvalidExtensionException 文件校验异常 + */ + public static final String upload(String baseDir, MultipartFile file, String[] allowedExtension) + throws FileSizeLimitExceededException, IOException, FileNameLengthLimitExceededException, + InvalidExtensionException + { + int fileNameLength = Objects.requireNonNull(file.getOriginalFilename()).length(); + if (fileNameLength > FileUploadUtils.DEFAULT_FILE_NAME_LENGTH) + { + throw new FileNameLengthLimitExceededException(FileUploadUtils.DEFAULT_FILE_NAME_LENGTH); + } + + assertAllowed(file, allowedExtension); + + String fileName = extractFilename(file); + + String absPath = getAbsoluteFile(baseDir, fileName).getAbsolutePath(); + file.transferTo(Paths.get(absPath)); + return getPathFileName(baseDir, fileName); + } + + /** + * 编码文件名 + */ + public static final String extractFilename(MultipartFile file) + { + return StringUtils.format("{}/{}_{}.{}", DateUtils.datePath(), + FilenameUtils.getBaseName(file.getOriginalFilename()), Seq.getId(Seq.uploadSeqType), getExtension(file)); + } + + public static final File getAbsoluteFile(String uploadDir, String fileName) throws IOException + { + File desc = new File(uploadDir + File.separator + fileName); + + if (!desc.exists()) + { + if (!desc.getParentFile().exists()) + { + desc.getParentFile().mkdirs(); + } + } + return desc; + } + + public static final String getPathFileName(String uploadDir, String fileName) throws IOException + { + int dirLastIndex = RuoYiConfig.getProfile().length() + 1; + String currentDir = StringUtils.substring(uploadDir, dirLastIndex); + return Constants.RESOURCE_PREFIX + "/" + currentDir + "/" + fileName; + } + + /** + * 文件大小校验 + * + * @param file 上传的文件 + * @return + * @throws FileSizeLimitExceededException 如果超出最大大小 + * @throws InvalidExtensionException + */ + public static final void assertAllowed(MultipartFile file, String[] allowedExtension) + throws FileSizeLimitExceededException, InvalidExtensionException + { + long size = file.getSize(); + if (size > DEFAULT_MAX_SIZE) + { + throw new FileSizeLimitExceededException(DEFAULT_MAX_SIZE / 1024 / 1024); + } + + String fileName = file.getOriginalFilename(); + String extension = getExtension(file); + if (allowedExtension != null && !isAllowedExtension(extension, allowedExtension)) + { + if (allowedExtension == MimeTypeUtils.IMAGE_EXTENSION) + { + throw new InvalidExtensionException.InvalidImageExtensionException(allowedExtension, extension, + fileName); + } + else if (allowedExtension == MimeTypeUtils.FLASH_EXTENSION) + { + throw new InvalidExtensionException.InvalidFlashExtensionException(allowedExtension, extension, + fileName); + } + else if (allowedExtension == MimeTypeUtils.MEDIA_EXTENSION) + { + throw new InvalidExtensionException.InvalidMediaExtensionException(allowedExtension, extension, + fileName); + } + else if (allowedExtension == MimeTypeUtils.VIDEO_EXTENSION) + { + throw new InvalidExtensionException.InvalidVideoExtensionException(allowedExtension, extension, + fileName); + } + else + { + throw new InvalidExtensionException(allowedExtension, extension, fileName); + } + } + } + + /** + * 判断MIME类型是否是允许的MIME类型 + * + * @param extension + * @param allowedExtension + * @return + */ + public static final boolean isAllowedExtension(String extension, String[] allowedExtension) + { + for (String str : allowedExtension) + { + if (str.equalsIgnoreCase(extension)) + { + return true; + } + } + return false; + } + + /** + * 获取文件名的后缀 + * + * @param file 表单文件 + * @return 后缀名 + */ + public static final String getExtension(MultipartFile file) + { + String extension = FilenameUtils.getExtension(file.getOriginalFilename()); + if (StringUtils.isEmpty(extension)) + { + extension = MimeTypeUtils.getExtension(Objects.requireNonNull(file.getContentType())); + } + return extension; + } +} diff --git a/src/main/java/com/ruoyi/common/utils/file/FileUtils.java b/src/main/java/com/ruoyi/common/utils/file/FileUtils.java new file mode 100644 index 0000000..f6ef349 --- /dev/null +++ b/src/main/java/com/ruoyi/common/utils/file/FileUtils.java @@ -0,0 +1,291 @@ +package com.ruoyi.common.utils.file; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import org.apache.commons.io.FilenameUtils; +import org.apache.commons.io.IOUtils; +import org.apache.commons.lang3.ArrayUtils; +import com.ruoyi.common.utils.DateUtils; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.uuid.IdUtils; +import com.ruoyi.framework.config.RuoYiConfig; + +/** + * 文件处理工具类 + * + * @author ruoyi + */ +public class FileUtils +{ + public static String FILENAME_PATTERN = "[a-zA-Z0-9_\\-\\|\\.\\u4e00-\\u9fa5]+"; + + /** + * 输出指定文件的byte数组 + * + * @param filePath 文件路径 + * @param os 输出流 + * @return + */ + public static void writeBytes(String filePath, OutputStream os) throws IOException + { + FileInputStream fis = null; + try + { + File file = new File(filePath); + if (!file.exists()) + { + throw new FileNotFoundException(filePath); + } + fis = new FileInputStream(file); + byte[] b = new byte[1024]; + int length; + while ((length = fis.read(b)) > 0) + { + os.write(b, 0, length); + } + } + catch (IOException e) + { + throw e; + } + finally + { + IOUtils.close(os); + IOUtils.close(fis); + } + } + + /** + * 写数据到文件中 + * + * @param data 数据 + * @return 目标文件 + * @throws IOException IO异常 + */ + public static String writeImportBytes(byte[] data) throws IOException + { + return writeBytes(data, RuoYiConfig.getImportPath()); + } + + /** + * 写数据到文件中 + * + * @param data 数据 + * @param uploadDir 目标文件 + * @return 目标文件 + * @throws IOException IO异常 + */ + public static String writeBytes(byte[] data, String uploadDir) throws IOException + { + FileOutputStream fos = null; + String pathName = ""; + try + { + String extension = getFileExtendName(data); + pathName = DateUtils.datePath() + "/" + IdUtils.fastUUID() + "." + extension; + File file = FileUploadUtils.getAbsoluteFile(uploadDir, pathName); + fos = new FileOutputStream(file); + fos.write(data); + } + finally + { + IOUtils.close(fos); + } + return FileUploadUtils.getPathFileName(uploadDir, pathName); + } + + /** + * 删除文件 + * + * @param filePath 文件 + * @return + */ + public static boolean deleteFile(String filePath) + { + boolean flag = false; + File file = new File(filePath); + // 路径为文件且不为空则进行删除 + if (file.isFile() && file.exists()) + { + flag = file.delete(); + } + return flag; + } + + /** + * 文件名称验证 + * + * @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 (StringUtils.contains(resource, "..")) + { + return false; + } + + // 检查允许下载的文件规则 + if (ArrayUtils.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, "utf-8"); + 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, "utf-8"); + } + else + { + // 其它浏览器 + filename = URLEncoder.encode(filename, "utf-8"); + } + return filename; + } + + /** + * 下载文件名重新编码 + * + * @param response 响应对象 + * @param realFileName 真实文件名 + */ + public static void setAttachmentResponseHeader(HttpServletResponse response, String realFileName) throws UnsupportedEncodingException + { + String percentEncodedFileName = percentEncode(realFileName); + + StringBuilder contentDispositionValue = new StringBuilder(); + contentDispositionValue.append("attachment; filename=") + .append(percentEncodedFileName) + .append(";") + .append("filename*=") + .append("utf-8''") + .append(percentEncodedFileName); + + response.addHeader("Access-Control-Expose-Headers", "Content-Disposition,download-filename"); + response.setHeader("Content-disposition", contentDispositionValue.toString()); + response.setHeader("download-filename", percentEncodedFileName); + } + + /** + * 百分号编码工具方法 + * + * @param s 需要百分号编码的字符串 + * @return 百分号编码后的字符串 + */ + public static String percentEncode(String s) throws UnsupportedEncodingException + { + String encode = URLEncoder.encode(s, StandardCharsets.UTF_8.toString()); + return encode.replaceAll("\\+", "%20"); + } + + /** + * 获取图像后缀 + * + * @param photoByte 图像数据 + * @return 后缀名 + */ + public static String getFileExtendName(byte[] photoByte) + { + String strFileExtendName = "jpg"; + if ((photoByte[0] == 71) && (photoByte[1] == 73) && (photoByte[2] == 70) && (photoByte[3] == 56) + && ((photoByte[4] == 55) || (photoByte[4] == 57)) && (photoByte[5] == 97)) + { + strFileExtendName = "gif"; + } + else if ((photoByte[6] == 74) && (photoByte[7] == 70) && (photoByte[8] == 73) && (photoByte[9] == 70)) + { + strFileExtendName = "jpg"; + } + else if ((photoByte[0] == 66) && (photoByte[1] == 77)) + { + strFileExtendName = "bmp"; + } + else if ((photoByte[1] == 80) && (photoByte[2] == 78) && (photoByte[3] == 71)) + { + strFileExtendName = "png"; + } + return strFileExtendName; + } + + /** + * 获取文件名称 /profile/upload/2022/04/16/ruoyi.png -- ruoyi.png + * + * @param fileName 路径名称 + * @return 没有文件路径的名称 + */ + public static String getName(String fileName) + { + if (fileName == null) + { + return null; + } + int lastUnixPos = fileName.lastIndexOf('/'); + int lastWindowsPos = fileName.lastIndexOf('\\'); + int index = Math.max(lastUnixPos, lastWindowsPos); + return fileName.substring(index + 1); + } + + /** + * 获取不带后缀文件名称 /profile/upload/2022/04/16/ruoyi.png -- ruoyi + * + * @param fileName 路径名称 + * @return 没有文件路径和后缀的名称 + */ + public static String getNameNotSuffix(String fileName) + { + if (fileName == null) + { + return null; + } + String baseName = FilenameUtils.getBaseName(fileName); + return baseName; + } +} diff --git a/src/main/java/com/ruoyi/common/utils/file/ImageUtils.java b/src/main/java/com/ruoyi/common/utils/file/ImageUtils.java new file mode 100644 index 0000000..857736d --- /dev/null +++ b/src/main/java/com/ruoyi/common/utils/file/ImageUtils.java @@ -0,0 +1,98 @@ +package com.ruoyi.common.utils.file; + +import java.io.ByteArrayInputStream; +import java.io.FileInputStream; +import java.io.InputStream; +import java.net.URL; +import java.net.URLConnection; +import java.util.Arrays; +import org.apache.poi.util.IOUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import com.ruoyi.common.constant.Constants; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.framework.config.RuoYiConfig; + +/** + * 图片处理工具类 + * + * @author ruoyi + */ +public class ImageUtils +{ + private static final Logger log = LoggerFactory.getLogger(ImageUtils.class); + + public static byte[] getImage(String imagePath) + { + InputStream is = getFile(imagePath); + try + { + return IOUtils.toByteArray(is); + } + catch (Exception e) + { + log.error("图片加载异常 {}", e); + return null; + } + finally + { + IOUtils.closeQuietly(is); + } + } + + public static InputStream getFile(String imagePath) + { + try + { + byte[] result = readFile(imagePath); + result = Arrays.copyOf(result, result.length); + return new ByteArrayInputStream(result); + } + catch (Exception e) + { + log.error("获取图片异常 {}", e); + } + return null; + } + + /** + * 读取文件为字节数据 + * + * @param url 地址 + * @return 字节数据 + */ + public static byte[] readFile(String url) + { + InputStream in = null; + try + { + if (url.startsWith("http")) + { + // 网络地址 + URL urlObj = new URL(url); + URLConnection urlConnection = urlObj.openConnection(); + urlConnection.setConnectTimeout(30 * 1000); + urlConnection.setReadTimeout(60 * 1000); + urlConnection.setDoInput(true); + in = urlConnection.getInputStream(); + } + else + { + // 本机地址 + String localPath = RuoYiConfig.getProfile(); + String downloadPath = localPath + StringUtils.substringAfter(url, Constants.RESOURCE_PREFIX); + in = new FileInputStream(downloadPath); + } + return IOUtils.toByteArray(in); + } + catch (Exception e) + { + log.error("获取文件路径异常 {}", e); + return null; + } + finally + { + IOUtils.closeQuietly(in); + } + } +} diff --git a/src/main/java/com/ruoyi/common/utils/file/MimeTypeUtils.java b/src/main/java/com/ruoyi/common/utils/file/MimeTypeUtils.java new file mode 100644 index 0000000..f968f1a --- /dev/null +++ b/src/main/java/com/ruoyi/common/utils/file/MimeTypeUtils.java @@ -0,0 +1,59 @@ +package com.ruoyi.common.utils.file; + +/** + * 媒体类型工具类 + * + * @author ruoyi + */ +public class MimeTypeUtils +{ + public static final String IMAGE_PNG = "image/png"; + + public static final String IMAGE_JPG = "image/jpg"; + + public static final String IMAGE_JPEG = "image/jpeg"; + + public static final String IMAGE_BMP = "image/bmp"; + + public static final String IMAGE_GIF = "image/gif"; + + public static final String[] IMAGE_EXTENSION = { "bmp", "gif", "jpg", "jpeg", "png" }; + + public static final String[] FLASH_EXTENSION = { "swf", "flv" }; + + public static final String[] MEDIA_EXTENSION = { "swf", "flv", "mp3", "wav", "wma", "wmv", "mid", "avi", "mpg", + "asf", "rm", "rmvb" }; + + public static final String[] VIDEO_EXTENSION = { "mp4", "avi", "rmvb" }; + + public static final String[] DEFAULT_ALLOWED_EXTENSION = { + // 图片 + "bmp", "gif", "jpg", "jpeg", "png", + // word excel powerpoint + "doc", "docx", "xls", "xlsx", "ppt", "pptx", "html", "htm", "txt", + // 压缩文件 + "rar", "zip", "gz", "bz2", + // 视频格式 + "mp4", "avi", "rmvb", + // pdf + "pdf" }; + + public static String getExtension(String prefix) + { + switch (prefix) + { + case IMAGE_PNG: + return "png"; + case IMAGE_JPG: + return "jpg"; + case IMAGE_JPEG: + return "jpeg"; + case IMAGE_BMP: + return "bmp"; + case IMAGE_GIF: + return "gif"; + default: + return ""; + } + } +} diff --git a/src/main/java/com/ruoyi/common/utils/html/EscapeUtil.java b/src/main/java/com/ruoyi/common/utils/html/EscapeUtil.java new file mode 100644 index 0000000..f52e83e --- /dev/null +++ b/src/main/java/com/ruoyi/common/utils/html/EscapeUtil.java @@ -0,0 +1,167 @@ +package com.ruoyi.common.utils.html; + +import com.ruoyi.common.utils.StringUtils; + +/** + * 转义和反转义工具类 + * + * @author ruoyi + */ +public class EscapeUtil +{ + public static final String RE_HTML_MARK = "(<[^<]*?>)|(<[\\s]*?/[^<]*?>)|(<[^<]*?/[\\s]*?>)"; + + private static final char[][] TEXT = new char[64][]; + + static + { + for (int i = 0; i < 64; i++) + { + TEXT[i] = new char[] { (char) i }; + } + + // special HTML characters + TEXT['\''] = "'".toCharArray(); // 单引号 + TEXT['"'] = """.toCharArray(); // 双引号 + TEXT['&'] = "&".toCharArray(); // &符 + TEXT['<'] = "<".toCharArray(); // 小于号 + TEXT['>'] = ">".toCharArray(); // 大于号 + } + + /** + * 转义文本中的HTML字符为安全的字符 + * + * @param text 被转义的文本 + * @return 转义后的文本 + */ + public static String escape(String text) + { + return encode(text); + } + + /** + * 还原被转义的HTML特殊字符 + * + * @param content 包含转义符的HTML内容 + * @return 转换后的字符串 + */ + public static String unescape(String content) + { + return decode(content); + } + + /** + * 清除所有HTML标签,但是不删除标签内的内容 + * + * @param content 文本 + * @return 清除标签后的文本 + */ + public static String clean(String content) + { + return new HTMLFilter().filter(content); + } + + /** + * Escape编码 + * + * @param text 被编码的文本 + * @return 编码后的字符 + */ + private static String encode(String text) + { + if (StringUtils.isEmpty(text)) + { + return StringUtils.EMPTY; + } + + final StringBuilder tmp = new StringBuilder(text.length() * 6); + char c; + for (int i = 0; i < text.length(); i++) + { + c = text.charAt(i); + if (c < 256) + { + tmp.append("%"); + if (c < 16) + { + tmp.append("0"); + } + tmp.append(Integer.toString(c, 16)); + } + else + { + tmp.append("%u"); + if (c <= 0xfff) + { + // issue#I49JU8@Gitee + tmp.append("0"); + } + tmp.append(Integer.toString(c, 16)); + } + } + return tmp.toString(); + } + + /** + * Escape解码 + * + * @param content 被转义的内容 + * @return 解码后的字符串 + */ + public static String decode(String content) + { + if (StringUtils.isEmpty(content)) + { + return content; + } + + StringBuilder tmp = new StringBuilder(content.length()); + int lastPos = 0, pos = 0; + char ch; + while (lastPos < content.length()) + { + pos = content.indexOf("%", lastPos); + if (pos == lastPos) + { + if (content.charAt(pos + 1) == 'u') + { + ch = (char) Integer.parseInt(content.substring(pos + 2, pos + 6), 16); + tmp.append(ch); + lastPos = pos + 6; + } + else + { + ch = (char) Integer.parseInt(content.substring(pos + 1, pos + 3), 16); + tmp.append(ch); + lastPos = pos + 3; + } + } + else + { + if (pos == -1) + { + tmp.append(content.substring(lastPos)); + lastPos = content.length(); + } + else + { + tmp.append(content.substring(lastPos, pos)); + lastPos = pos; + } + } + } + return tmp.toString(); + } + + public static void main(String[] args) + { + String html = ""; + String escape = EscapeUtil.escape(html); + // String html = "ipt>alert(\"XSS\")ipt>"; + // String html = "<123"; + // String html = "123>"; + System.out.println("clean: " + EscapeUtil.clean(html)); + System.out.println("escape: " + escape); + System.out.println("unescape: " + EscapeUtil.unescape(escape)); + } +} diff --git a/src/main/java/com/ruoyi/common/utils/html/HTMLFilter.java b/src/main/java/com/ruoyi/common/utils/html/HTMLFilter.java new file mode 100644 index 0000000..63f1c9d --- /dev/null +++ b/src/main/java/com/ruoyi/common/utils/html/HTMLFilter.java @@ -0,0 +1,570 @@ +package com.ruoyi.common.utils.html; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * HTML过滤器,用于去除XSS漏洞隐患。 + * + * @author ruoyi + */ +public final class HTMLFilter +{ + /** + * regex flag union representing /si modifiers in php + **/ + private static final int REGEX_FLAGS_SI = Pattern.CASE_INSENSITIVE | Pattern.DOTALL; + private static final Pattern P_COMMENTS = Pattern.compile("", Pattern.DOTALL); + private static final Pattern P_COMMENT = Pattern.compile("^!--(.*)--$", REGEX_FLAGS_SI); + private static final Pattern P_TAGS = Pattern.compile("<(.*?)>", Pattern.DOTALL); + private static final Pattern P_END_TAG = Pattern.compile("^/([a-z0-9]+)", REGEX_FLAGS_SI); + private static final Pattern P_START_TAG = Pattern.compile("^([a-z0-9]+)(.*?)(/?)$", REGEX_FLAGS_SI); + private static final Pattern P_QUOTED_ATTRIBUTES = Pattern.compile("([a-z0-9]+)=([\"'])(.*?)\\2", REGEX_FLAGS_SI); + private static final Pattern P_UNQUOTED_ATTRIBUTES = Pattern.compile("([a-z0-9]+)(=)([^\"\\s']+)", REGEX_FLAGS_SI); + private static final Pattern P_PROTOCOL = Pattern.compile("^([^:]+):", REGEX_FLAGS_SI); + private static final Pattern P_ENTITY = Pattern.compile("&#(\\d+);?"); + private static final Pattern P_ENTITY_UNICODE = Pattern.compile("&#x([0-9a-f]+);?"); + private static final Pattern P_ENCODE = Pattern.compile("%([0-9a-f]{2});?"); + private static final Pattern P_VALID_ENTITIES = Pattern.compile("&([^&;]*)(?=(;|&|$))"); + private static final Pattern P_VALID_QUOTES = Pattern.compile("(>|^)([^<]+?)(<|$)", Pattern.DOTALL); + private static final Pattern P_END_ARROW = Pattern.compile("^>"); + private static final Pattern P_BODY_TO_END = Pattern.compile("<([^>]*?)(?=<|$)"); + private static final Pattern P_XML_CONTENT = Pattern.compile("(^|>)([^<]*?)(?=>)"); + private static final Pattern P_STRAY_LEFT_ARROW = Pattern.compile("<([^>]*?)(?=<|$)"); + private static final Pattern P_STRAY_RIGHT_ARROW = Pattern.compile("(^|>)([^<]*?)(?=>)"); + private static final Pattern P_AMP = Pattern.compile("&"); + private static final Pattern P_QUOTE = Pattern.compile("\""); + private static final Pattern P_LEFT_ARROW = Pattern.compile("<"); + private static final Pattern P_RIGHT_ARROW = Pattern.compile(">"); + private static final Pattern P_BOTH_ARROWS = Pattern.compile("<>"); + + // @xxx could grow large... maybe use sesat's ReferenceMap + private static final ConcurrentMap P_REMOVE_PAIR_BLANKS = new ConcurrentHashMap<>(); + private static final ConcurrentMap P_REMOVE_SELF_BLANKS = new ConcurrentHashMap<>(); + + /** + * set of allowed html elements, along with allowed attributes for each element + **/ + private final Map> vAllowed; + /** + * counts of open tags for each (allowable) html element + **/ + private final Map vTagCounts = new HashMap<>(); + + /** + * html elements which must always be self-closing (e.g. "") + **/ + private final String[] vSelfClosingTags; + /** + * html elements which must always have separate opening and closing tags (e.g. "") + **/ + private final String[] vNeedClosingTags; + /** + * set of disallowed html elements + **/ + private final String[] vDisallowed; + /** + * attributes which should be checked for valid protocols + **/ + private final String[] vProtocolAtts; + /** + * allowed protocols + **/ + private final String[] vAllowedProtocols; + /** + * tags which should be removed if they contain no content (e.g. "" or "") + **/ + private final String[] vRemoveBlanks; + /** + * entities allowed within html markup + **/ + private final String[] vAllowedEntities; + /** + * flag determining whether comments are allowed in input String. + */ + private final boolean stripComment; + private final boolean encodeQuotes; + /** + * flag determining whether to try to make tags when presented with "unbalanced" angle brackets (e.g. "" + * becomes " text "). If set to false, unbalanced angle brackets will be html escaped. + */ + private final boolean alwaysMakeTags; + + /** + * Default constructor. + */ + public HTMLFilter() + { + vAllowed = new HashMap<>(); + + final ArrayList a_atts = new ArrayList<>(); + a_atts.add("href"); + a_atts.add("target"); + vAllowed.put("a", a_atts); + + final ArrayList img_atts = new ArrayList<>(); + img_atts.add("src"); + img_atts.add("width"); + img_atts.add("height"); + img_atts.add("alt"); + vAllowed.put("img", img_atts); + + final ArrayList no_atts = new ArrayList<>(); + vAllowed.put("b", no_atts); + vAllowed.put("strong", no_atts); + vAllowed.put("i", no_atts); + vAllowed.put("em", no_atts); + + vSelfClosingTags = new String[] { "img" }; + vNeedClosingTags = new String[] { "a", "b", "strong", "i", "em" }; + vDisallowed = new String[] {}; + vAllowedProtocols = new String[] { "http", "mailto", "https" }; // no ftp. + vProtocolAtts = new String[] { "src", "href" }; + vRemoveBlanks = new String[] { "a", "b", "strong", "i", "em" }; + vAllowedEntities = new String[] { "amp", "gt", "lt", "quot" }; + stripComment = true; + encodeQuotes = true; + alwaysMakeTags = false; + } + + /** + * Map-parameter configurable constructor. + * + * @param conf map containing configuration. keys match field names. + */ + @SuppressWarnings("unchecked") + public HTMLFilter(final Map conf) + { + + assert conf.containsKey("vAllowed") : "configuration requires vAllowed"; + assert conf.containsKey("vSelfClosingTags") : "configuration requires vSelfClosingTags"; + assert conf.containsKey("vNeedClosingTags") : "configuration requires vNeedClosingTags"; + assert conf.containsKey("vDisallowed") : "configuration requires vDisallowed"; + assert conf.containsKey("vAllowedProtocols") : "configuration requires vAllowedProtocols"; + assert conf.containsKey("vProtocolAtts") : "configuration requires vProtocolAtts"; + assert conf.containsKey("vRemoveBlanks") : "configuration requires vRemoveBlanks"; + assert conf.containsKey("vAllowedEntities") : "configuration requires vAllowedEntities"; + + vAllowed = Collections.unmodifiableMap((HashMap>) conf.get("vAllowed")); + vSelfClosingTags = (String[]) conf.get("vSelfClosingTags"); + vNeedClosingTags = (String[]) conf.get("vNeedClosingTags"); + vDisallowed = (String[]) conf.get("vDisallowed"); + vAllowedProtocols = (String[]) conf.get("vAllowedProtocols"); + vProtocolAtts = (String[]) conf.get("vProtocolAtts"); + vRemoveBlanks = (String[]) conf.get("vRemoveBlanks"); + vAllowedEntities = (String[]) conf.get("vAllowedEntities"); + stripComment = conf.containsKey("stripComment") ? (Boolean) conf.get("stripComment") : true; + encodeQuotes = conf.containsKey("encodeQuotes") ? (Boolean) conf.get("encodeQuotes") : true; + alwaysMakeTags = conf.containsKey("alwaysMakeTags") ? (Boolean) conf.get("alwaysMakeTags") : true; + } + + private void reset() + { + vTagCounts.clear(); + } + + // --------------------------------------------------------------- + // my versions of some PHP library functions + public static String chr(final int decimal) + { + return String.valueOf((char) decimal); + } + + public static String htmlSpecialChars(final String s) + { + String result = s; + result = regexReplace(P_AMP, "&", result); + result = regexReplace(P_QUOTE, """, result); + result = regexReplace(P_LEFT_ARROW, "<", result); + result = regexReplace(P_RIGHT_ARROW, ">", result); + return result; + } + + // --------------------------------------------------------------- + + /** + * given a user submitted input String, filter out any invalid or restricted html. + * + * @param input text (i.e. submitted by a user) than may contain html + * @return "clean" version of input, with only valid, whitelisted html elements allowed + */ + public String filter(final String input) + { + reset(); + String s = input; + + s = escapeComments(s); + + s = balanceHTML(s); + + s = checkTags(s); + + s = processRemoveBlanks(s); + + // s = validateEntities(s); + + return s; + } + + public boolean isAlwaysMakeTags() + { + return alwaysMakeTags; + } + + public boolean isStripComments() + { + return stripComment; + } + + private String escapeComments(final String s) + { + final Matcher m = P_COMMENTS.matcher(s); + final StringBuffer buf = new StringBuffer(); + if (m.find()) + { + final String match = m.group(1); // (.*?) + m.appendReplacement(buf, Matcher.quoteReplacement("")); + } + m.appendTail(buf); + + return buf.toString(); + } + + private String balanceHTML(String s) + { + if (alwaysMakeTags) + { + // + // try and form html + // + s = regexReplace(P_END_ARROW, "", s); + // 不追加结束标签 + s = regexReplace(P_BODY_TO_END, "<$1>", s); + s = regexReplace(P_XML_CONTENT, "$1<$2", s); + + } + else + { + // + // escape stray brackets + // + s = regexReplace(P_STRAY_LEFT_ARROW, "<$1", s); + s = regexReplace(P_STRAY_RIGHT_ARROW, "$1$2><", s); + + // + // the last regexp causes '<>' entities to appear + // (we need to do a lookahead assertion so that the last bracket can + // be used in the next pass of the regexp) + // + s = regexReplace(P_BOTH_ARROWS, "", s); + } + + return s; + } + + private String checkTags(String s) + { + Matcher m = P_TAGS.matcher(s); + + final StringBuffer buf = new StringBuffer(); + while (m.find()) + { + String replaceStr = m.group(1); + replaceStr = processTag(replaceStr); + m.appendReplacement(buf, Matcher.quoteReplacement(replaceStr)); + } + m.appendTail(buf); + + // these get tallied in processTag + // (remember to reset before subsequent calls to filter method) + final StringBuilder sBuilder = new StringBuilder(buf.toString()); + for (String key : vTagCounts.keySet()) + { + for (int ii = 0; ii < vTagCounts.get(key); ii++) + { + sBuilder.append(""); + } + } + s = sBuilder.toString(); + + return s; + } + + private String processRemoveBlanks(final String s) + { + String result = s; + for (String tag : vRemoveBlanks) + { + if (!P_REMOVE_PAIR_BLANKS.containsKey(tag)) + { + P_REMOVE_PAIR_BLANKS.putIfAbsent(tag, Pattern.compile("<" + tag + "(\\s[^>]*)?>")); + } + result = regexReplace(P_REMOVE_PAIR_BLANKS.get(tag), "", result); + if (!P_REMOVE_SELF_BLANKS.containsKey(tag)) + { + P_REMOVE_SELF_BLANKS.putIfAbsent(tag, Pattern.compile("<" + tag + "(\\s[^>]*)?/>")); + } + result = regexReplace(P_REMOVE_SELF_BLANKS.get(tag), "", result); + } + + return result; + } + + private static String regexReplace(final Pattern regex_pattern, final String replacement, final String s) + { + Matcher m = regex_pattern.matcher(s); + return m.replaceAll(replacement); + } + + private String processTag(final String s) + { + // ending tags + Matcher m = P_END_TAG.matcher(s); + if (m.find()) + { + final String name = m.group(1).toLowerCase(); + if (allowed(name)) + { + if (false == inArray(name, vSelfClosingTags)) + { + if (vTagCounts.containsKey(name)) + { + vTagCounts.put(name, vTagCounts.get(name) - 1); + return ""; + } + } + } + } + + // starting tags + m = P_START_TAG.matcher(s); + if (m.find()) + { + final String name = m.group(1).toLowerCase(); + final String body = m.group(2); + String ending = m.group(3); + + // debug( "in a starting tag, name='" + name + "'; body='" + body + "'; ending='" + ending + "'" ); + if (allowed(name)) + { + final StringBuilder params = new StringBuilder(); + + final Matcher m2 = P_QUOTED_ATTRIBUTES.matcher(body); + final Matcher m3 = P_UNQUOTED_ATTRIBUTES.matcher(body); + final List paramNames = new ArrayList<>(); + final List paramValues = new ArrayList<>(); + while (m2.find()) + { + paramNames.add(m2.group(1)); // ([a-z0-9]+) + paramValues.add(m2.group(3)); // (.*?) + } + while (m3.find()) + { + paramNames.add(m3.group(1)); // ([a-z0-9]+) + paramValues.add(m3.group(3)); // ([^\"\\s']+) + } + + String paramName, paramValue; + for (int ii = 0; ii < paramNames.size(); ii++) + { + paramName = paramNames.get(ii).toLowerCase(); + paramValue = paramValues.get(ii); + + // debug( "paramName='" + paramName + "'" ); + // debug( "paramValue='" + paramValue + "'" ); + // debug( "allowed? " + vAllowed.get( name ).contains( paramName ) ); + + if (allowedAttribute(name, paramName)) + { + if (inArray(paramName, vProtocolAtts)) + { + paramValue = processParamProtocol(paramValue); + } + params.append(' ').append(paramName).append("=\\\"").append(paramValue).append("\\\""); + } + } + + if (inArray(name, vSelfClosingTags)) + { + ending = " /"; + } + + if (inArray(name, vNeedClosingTags)) + { + ending = ""; + } + + if (ending == null || ending.length() < 1) + { + if (vTagCounts.containsKey(name)) + { + vTagCounts.put(name, vTagCounts.get(name) + 1); + } + else + { + vTagCounts.put(name, 1); + } + } + else + { + ending = " /"; + } + return "<" + name + params + ending + ">"; + } + else + { + return ""; + } + } + + // comments + m = P_COMMENT.matcher(s); + if (!stripComment && m.find()) + { + return "<" + m.group() + ">"; + } + + return ""; + } + + private String processParamProtocol(String s) + { + s = decodeEntities(s); + final Matcher m = P_PROTOCOL.matcher(s); + if (m.find()) + { + final String protocol = m.group(1); + if (!inArray(protocol, vAllowedProtocols)) + { + // bad protocol, turn into local anchor link instead + s = "#" + s.substring(protocol.length() + 1); + if (s.startsWith("#//")) + { + s = "#" + s.substring(3); + } + } + } + + return s; + } + + private String decodeEntities(String s) + { + StringBuffer buf = new StringBuffer(); + + Matcher m = P_ENTITY.matcher(s); + while (m.find()) + { + final String match = m.group(1); + final int decimal = Integer.decode(match).intValue(); + m.appendReplacement(buf, Matcher.quoteReplacement(chr(decimal))); + } + m.appendTail(buf); + s = buf.toString(); + + buf = new StringBuffer(); + m = P_ENTITY_UNICODE.matcher(s); + while (m.find()) + { + final String match = m.group(1); + final int decimal = Integer.valueOf(match, 16).intValue(); + m.appendReplacement(buf, Matcher.quoteReplacement(chr(decimal))); + } + m.appendTail(buf); + s = buf.toString(); + + buf = new StringBuffer(); + m = P_ENCODE.matcher(s); + while (m.find()) + { + final String match = m.group(1); + final int decimal = Integer.valueOf(match, 16).intValue(); + m.appendReplacement(buf, Matcher.quoteReplacement(chr(decimal))); + } + m.appendTail(buf); + s = buf.toString(); + + s = validateEntities(s); + return s; + } + + private String validateEntities(final String s) + { + StringBuffer buf = new StringBuffer(); + + // validate entities throughout the string + Matcher m = P_VALID_ENTITIES.matcher(s); + while (m.find()) + { + final String one = m.group(1); // ([^&;]*) + final String two = m.group(2); // (?=(;|&|$)) + m.appendReplacement(buf, Matcher.quoteReplacement(checkEntity(one, two))); + } + m.appendTail(buf); + + return encodeQuotes(buf.toString()); + } + + private String encodeQuotes(final String s) + { + if (encodeQuotes) + { + StringBuffer buf = new StringBuffer(); + Matcher m = P_VALID_QUOTES.matcher(s); + while (m.find()) + { + final String one = m.group(1); // (>|^) + final String two = m.group(2); // ([^<]+?) + final String three = m.group(3); // (<|$) + // 不替换双引号为",防止json格式无效 regexReplace(P_QUOTE, """, two) + m.appendReplacement(buf, Matcher.quoteReplacement(one + two + three)); + } + m.appendTail(buf); + return buf.toString(); + } + else + { + return s; + } + } + + private String checkEntity(final String preamble, final String term) + { + + return ";".equals(term) && isValidEntity(preamble) ? '&' + preamble : "&" + preamble; + } + + private boolean isValidEntity(final String entity) + { + return inArray(entity, vAllowedEntities); + } + + private static boolean inArray(final String s, final String[] array) + { + for (String item : array) + { + if (item != null && item.equals(s)) + { + return true; + } + } + return false; + } + + private boolean allowed(final String name) + { + return (vAllowed.isEmpty() || vAllowed.containsKey(name)) && !inArray(name, vDisallowed); + } + + private boolean allowedAttribute(final String name, final String paramName) + { + return allowed(name) && (vAllowed.isEmpty() || vAllowed.get(name).contains(paramName)); + } +} \ No newline at end of file diff --git a/src/main/java/com/ruoyi/common/utils/http/HttpHelper.java b/src/main/java/com/ruoyi/common/utils/http/HttpHelper.java new file mode 100644 index 0000000..589d123 --- /dev/null +++ b/src/main/java/com/ruoyi/common/utils/http/HttpHelper.java @@ -0,0 +1,55 @@ +package com.ruoyi.common.utils.http; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; +import javax.servlet.ServletRequest; +import org.apache.commons.lang3.exception.ExceptionUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * 通用http工具封装 + * + * @author ruoyi + */ +public class HttpHelper +{ + private static final Logger LOGGER = LoggerFactory.getLogger(HttpHelper.class); + + public static String getBodyString(ServletRequest request) + { + StringBuilder sb = new StringBuilder(); + BufferedReader reader = null; + try (InputStream inputStream = request.getInputStream()) + { + reader = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8)); + String line = ""; + while ((line = reader.readLine()) != null) + { + sb.append(line); + } + } + catch (IOException e) + { + LOGGER.warn("getBodyString出现问题!"); + } + finally + { + if (reader != null) + { + try + { + reader.close(); + } + catch (IOException e) + { + LOGGER.error(ExceptionUtils.getMessage(e)); + } + } + } + return sb.toString(); + } +} diff --git a/src/main/java/com/ruoyi/common/utils/http/HttpUtils.java b/src/main/java/com/ruoyi/common/utils/http/HttpUtils.java new file mode 100644 index 0000000..ef5eb28 --- /dev/null +++ b/src/main/java/com/ruoyi/common/utils/http/HttpUtils.java @@ -0,0 +1,274 @@ +package com.ruoyi.common.utils.http; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.PrintWriter; +import java.net.ConnectException; +import java.net.SocketTimeoutException; +import java.net.URL; +import java.net.URLConnection; +import java.nio.charset.StandardCharsets; +import java.security.cert.X509Certificate; +import javax.net.ssl.HostnameVerifier; +import javax.net.ssl.HttpsURLConnection; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLSession; +import javax.net.ssl.TrustManager; +import javax.net.ssl.X509TrustManager; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import com.ruoyi.common.constant.Constants; +import com.ruoyi.common.utils.StringUtils; + +/** + * 通用http发送方法 + * + * @author ruoyi + */ +public class HttpUtils +{ + private static final Logger log = LoggerFactory.getLogger(HttpUtils.class); + + /** + * 向指定 URL 发送GET方法的请求 + * + * @param url 发送请求的 URL + * @return 所代表远程资源的响应结果 + */ + public static String sendGet(String url) + { + return sendGet(url, StringUtils.EMPTY); + } + + /** + * 向指定 URL 发送GET方法的请求 + * + * @param url 发送请求的 URL + * @param param 请求参数,请求参数应该是 name1=value1&name2=value2 的形式。 + * @return 所代表远程资源的响应结果 + */ + public static String sendGet(String url, String param) + { + return sendGet(url, param, Constants.UTF8); + } + + /** + * 向指定 URL 发送GET方法的请求 + * + * @param url 发送请求的 URL + * @param param 请求参数,请求参数应该是 name1=value1&name2=value2 的形式。 + * @param contentType 编码类型 + * @return 所代表远程资源的响应结果 + */ + public static String sendGet(String url, String param, String contentType) + { + StringBuilder result = new StringBuilder(); + BufferedReader in = null; + try + { + String urlNameString = StringUtils.isNotBlank(param) ? url + "?" + param : url; + log.info("sendGet - {}", urlNameString); + URL realUrl = new URL(urlNameString); + URLConnection connection = realUrl.openConnection(); + connection.setRequestProperty("accept", "*/*"); + connection.setRequestProperty("connection", "Keep-Alive"); + connection.setRequestProperty("user-agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64)"); + connection.connect(); + in = new BufferedReader(new InputStreamReader(connection.getInputStream(), contentType)); + String line; + while ((line = in.readLine()) != null) + { + result.append(line); + } + log.info("recv - {}", result); + } + catch (ConnectException e) + { + log.error("调用HttpUtils.sendGet ConnectException, url=" + url + ",param=" + param, e); + } + catch (SocketTimeoutException e) + { + log.error("调用HttpUtils.sendGet SocketTimeoutException, url=" + url + ",param=" + param, e); + } + catch (IOException e) + { + log.error("调用HttpUtils.sendGet IOException, url=" + url + ",param=" + param, e); + } + catch (Exception e) + { + log.error("调用HttpsUtil.sendGet Exception, url=" + url + ",param=" + param, e); + } + finally + { + try + { + if (in != null) + { + in.close(); + } + } + catch (Exception ex) + { + log.error("调用in.close Exception, url=" + url + ",param=" + param, ex); + } + } + return result.toString(); + } + + /** + * 向指定 URL 发送POST方法的请求 + * + * @param url 发送请求的 URL + * @param param 请求参数,请求参数应该是 name1=value1&name2=value2 的形式。 + * @return 所代表远程资源的响应结果 + */ + public static String sendPost(String url, String param) + { + PrintWriter out = null; + BufferedReader in = null; + StringBuilder result = new StringBuilder(); + try + { + log.info("sendPost - {}", url); + URL realUrl = new URL(url); + URLConnection conn = realUrl.openConnection(); + conn.setRequestProperty("accept", "*/*"); + conn.setRequestProperty("connection", "Keep-Alive"); + conn.setRequestProperty("user-agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64)"); + conn.setRequestProperty("Accept-Charset", "utf-8"); + conn.setRequestProperty("contentType", "utf-8"); + conn.setDoOutput(true); + conn.setDoInput(true); + out = new PrintWriter(conn.getOutputStream()); + out.print(param); + out.flush(); + in = new BufferedReader(new InputStreamReader(conn.getInputStream(), StandardCharsets.UTF_8)); + String line; + while ((line = in.readLine()) != null) + { + result.append(line); + } + log.info("recv - {}", result); + } + catch (ConnectException e) + { + log.error("调用HttpUtils.sendPost ConnectException, url=" + url + ",param=" + param, e); + } + catch (SocketTimeoutException e) + { + log.error("调用HttpUtils.sendPost SocketTimeoutException, url=" + url + ",param=" + param, e); + } + catch (IOException e) + { + log.error("调用HttpUtils.sendPost IOException, url=" + url + ",param=" + param, e); + } + catch (Exception e) + { + log.error("调用HttpsUtil.sendPost Exception, url=" + url + ",param=" + param, e); + } + finally + { + try + { + if (out != null) + { + out.close(); + } + if (in != null) + { + in.close(); + } + } + catch (IOException ex) + { + log.error("调用in.close Exception, url=" + url + ",param=" + param, ex); + } + } + return result.toString(); + } + + public static String sendSSLPost(String url, String param) + { + StringBuilder result = new StringBuilder(); + String urlNameString = url + "?" + param; + try + { + log.info("sendSSLPost - {}", urlNameString); + SSLContext sc = SSLContext.getInstance("SSL"); + sc.init(null, new TrustManager[] { new TrustAnyTrustManager() }, new java.security.SecureRandom()); + URL console = new URL(urlNameString); + HttpsURLConnection conn = (HttpsURLConnection) console.openConnection(); + conn.setRequestProperty("accept", "*/*"); + conn.setRequestProperty("connection", "Keep-Alive"); + conn.setRequestProperty("user-agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64)"); + conn.setRequestProperty("Accept-Charset", "utf-8"); + conn.setRequestProperty("contentType", "utf-8"); + conn.setDoOutput(true); + conn.setDoInput(true); + + conn.setSSLSocketFactory(sc.getSocketFactory()); + conn.setHostnameVerifier(new TrustAnyHostnameVerifier()); + conn.connect(); + InputStream is = conn.getInputStream(); + BufferedReader br = new BufferedReader(new InputStreamReader(is)); + String ret = ""; + while ((ret = br.readLine()) != null) + { + if (ret != null && !"".equals(ret.trim())) + { + result.append(new String(ret.getBytes(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8)); + } + } + log.info("recv - {}", result); + conn.disconnect(); + br.close(); + } + catch (ConnectException e) + { + log.error("调用HttpUtils.sendSSLPost ConnectException, url=" + url + ",param=" + param, e); + } + catch (SocketTimeoutException e) + { + log.error("调用HttpUtils.sendSSLPost SocketTimeoutException, url=" + url + ",param=" + param, e); + } + catch (IOException e) + { + log.error("调用HttpUtils.sendSSLPost IOException, url=" + url + ",param=" + param, e); + } + catch (Exception e) + { + log.error("调用HttpsUtil.sendSSLPost Exception, url=" + url + ",param=" + param, e); + } + return result.toString(); + } + + private static class TrustAnyTrustManager implements X509TrustManager + { + @Override + public void checkClientTrusted(X509Certificate[] chain, String authType) + { + } + + @Override + public void checkServerTrusted(X509Certificate[] chain, String authType) + { + } + + @Override + public X509Certificate[] getAcceptedIssuers() + { + return new X509Certificate[] {}; + } + } + + private static class TrustAnyHostnameVerifier implements HostnameVerifier + { + @Override + public boolean verify(String hostname, SSLSession session) + { + return true; + } + } +} \ No newline at end of file diff --git a/src/main/java/com/ruoyi/common/utils/ip/AddressUtils.java b/src/main/java/com/ruoyi/common/utils/ip/AddressUtils.java new file mode 100644 index 0000000..31c3388 --- /dev/null +++ b/src/main/java/com/ruoyi/common/utils/ip/AddressUtils.java @@ -0,0 +1,56 @@ +package com.ruoyi.common.utils.ip; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONObject; +import com.ruoyi.common.constant.Constants; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.http.HttpUtils; +import com.ruoyi.framework.config.RuoYiConfig; + +/** + * 获取地址类 + * + * @author ruoyi + */ +public class AddressUtils +{ + private static final Logger log = LoggerFactory.getLogger(AddressUtils.class); + + // IP地址查询 + public static final String IP_URL = "http://whois.pconline.com.cn/ipJson.jsp"; + + // 未知地址 + public static final String UNKNOWN = "XX XX"; + + public static String getRealAddressByIP(String ip) + { + // 内网不查询 + if (IpUtils.internalIp(ip)) + { + return "内网IP"; + } + if (RuoYiConfig.isAddressEnabled()) + { + try + { + String rspStr = HttpUtils.sendGet(IP_URL, "ip=" + ip + "&json=true", Constants.GBK); + if (StringUtils.isEmpty(rspStr)) + { + log.error("获取地理位置异常 {}", ip); + return UNKNOWN; + } + JSONObject obj = JSON.parseObject(rspStr); + String region = obj.getString("pro"); + String city = obj.getString("city"); + return String.format("%s %s", region, city); + } + catch (Exception e) + { + log.error("获取地理位置异常 {}", ip); + } + } + return UNKNOWN; + } +} diff --git a/src/main/java/com/ruoyi/common/utils/ip/IpUtils.java b/src/main/java/com/ruoyi/common/utils/ip/IpUtils.java new file mode 100644 index 0000000..8e89e30 --- /dev/null +++ b/src/main/java/com/ruoyi/common/utils/ip/IpUtils.java @@ -0,0 +1,382 @@ +package com.ruoyi.common.utils.ip; + +import java.net.InetAddress; +import java.net.UnknownHostException; +import javax.servlet.http.HttpServletRequest; +import com.ruoyi.common.utils.ServletUtils; +import com.ruoyi.common.utils.StringUtils; + +/** + * 获取IP方法 + * + * @author ruoyi + */ +public class IpUtils +{ + public final static String REGX_0_255 = "(25[0-5]|2[0-4]\\d|1\\d{2}|[1-9]\\d|\\d)"; + // 匹配 ip + public final static String REGX_IP = "((" + REGX_0_255 + "\\.){3}" + REGX_0_255 + ")"; + public final static String REGX_IP_WILDCARD = "(((\\*\\.){3}\\*)|(" + REGX_0_255 + "(\\.\\*){3})|(" + REGX_0_255 + "\\." + REGX_0_255 + ")(\\.\\*){2}" + "|((" + REGX_0_255 + "\\.){3}\\*))"; + // 匹配网段 + public final static String REGX_IP_SEG = "(" + REGX_IP + "\\-" + REGX_IP + ")"; + + /** + * 获取客户端IP + * + * @return IP地址 + */ + public static String getIpAddr() + { + return getIpAddr(ServletUtils.getRequest()); + } + + /** + * 获取客户端IP + * + * @param request 请求对象 + * @return IP地址 + */ + public static String getIpAddr(HttpServletRequest request) + { + if (request == null) + { + return "unknown"; + } + String ip = request.getHeader("x-forwarded-for"); + if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) + { + ip = request.getHeader("Proxy-Client-IP"); + } + if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) + { + ip = request.getHeader("X-Forwarded-For"); + } + if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) + { + ip = request.getHeader("WL-Proxy-Client-IP"); + } + if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) + { + ip = request.getHeader("X-Real-IP"); + } + + if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) + { + ip = request.getRemoteAddr(); + } + + return "0:0:0:0:0:0:0:1".equals(ip) ? "127.0.0.1" : getMultistageReverseProxyIp(ip); + } + + /** + * 检查是否为内部IP地址 + * + * @param ip IP地址 + * @return 结果 + */ + public static boolean internalIp(String ip) + { + byte[] addr = textToNumericFormatV4(ip); + return internalIp(addr) || "127.0.0.1".equals(ip); + } + + /** + * 检查是否为内部IP地址 + * + * @param addr byte地址 + * @return 结果 + */ + private static boolean internalIp(byte[] addr) + { + if (StringUtils.isNull(addr) || addr.length < 2) + { + return true; + } + final byte b0 = addr[0]; + final byte b1 = addr[1]; + // 10.x.x.x/8 + final byte SECTION_1 = 0x0A; + // 172.16.x.x/12 + final byte SECTION_2 = (byte) 0xAC; + final byte SECTION_3 = (byte) 0x10; + final byte SECTION_4 = (byte) 0x1F; + // 192.168.x.x/16 + final byte SECTION_5 = (byte) 0xC0; + final byte SECTION_6 = (byte) 0xA8; + switch (b0) + { + case SECTION_1: + return true; + case SECTION_2: + if (b1 >= SECTION_3 && b1 <= SECTION_4) + { + return true; + } + case SECTION_5: + switch (b1) + { + case SECTION_6: + return true; + } + default: + return false; + } + } + + /** + * 将IPv4地址转换成字节 + * + * @param text IPv4地址 + * @return byte 字节 + */ + public static byte[] textToNumericFormatV4(String text) + { + if (text.length() == 0) + { + return null; + } + + byte[] bytes = new byte[4]; + String[] elements = text.split("\\.", -1); + try + { + long l; + int i; + switch (elements.length) + { + case 1: + l = Long.parseLong(elements[0]); + if ((l < 0L) || (l > 4294967295L)) + { + return null; + } + bytes[0] = (byte) (int) (l >> 24 & 0xFF); + bytes[1] = (byte) (int) ((l & 0xFFFFFF) >> 16 & 0xFF); + bytes[2] = (byte) (int) ((l & 0xFFFF) >> 8 & 0xFF); + bytes[3] = (byte) (int) (l & 0xFF); + break; + case 2: + l = Integer.parseInt(elements[0]); + if ((l < 0L) || (l > 255L)) + { + return null; + } + bytes[0] = (byte) (int) (l & 0xFF); + l = Integer.parseInt(elements[1]); + if ((l < 0L) || (l > 16777215L)) + { + return null; + } + bytes[1] = (byte) (int) (l >> 16 & 0xFF); + bytes[2] = (byte) (int) ((l & 0xFFFF) >> 8 & 0xFF); + bytes[3] = (byte) (int) (l & 0xFF); + break; + case 3: + for (i = 0; i < 2; ++i) + { + l = Integer.parseInt(elements[i]); + if ((l < 0L) || (l > 255L)) + { + return null; + } + bytes[i] = (byte) (int) (l & 0xFF); + } + l = Integer.parseInt(elements[2]); + if ((l < 0L) || (l > 65535L)) + { + return null; + } + bytes[2] = (byte) (int) (l >> 8 & 0xFF); + bytes[3] = (byte) (int) (l & 0xFF); + break; + case 4: + for (i = 0; i < 4; ++i) + { + l = Integer.parseInt(elements[i]); + if ((l < 0L) || (l > 255L)) + { + return null; + } + bytes[i] = (byte) (int) (l & 0xFF); + } + break; + default: + return null; + } + } + catch (NumberFormatException e) + { + return null; + } + return bytes; + } + + /** + * 获取IP地址 + * + * @return 本地IP地址 + */ + public static String getHostIp() + { + try + { + return InetAddress.getLocalHost().getHostAddress(); + } + catch (UnknownHostException e) + { + } + return "127.0.0.1"; + } + + /** + * 获取主机名 + * + * @return 本地主机名 + */ + public static String getHostName() + { + try + { + return InetAddress.getLocalHost().getHostName(); + } + catch (UnknownHostException e) + { + } + return "未知"; + } + + /** + * 从多级反向代理中获得第一个非unknown IP地址 + * + * @param ip 获得的IP地址 + * @return 第一个非unknown IP地址 + */ + public static String getMultistageReverseProxyIp(String ip) + { + // 多级反向代理检测 + if (ip != null && ip.indexOf(",") > 0) + { + final String[] ips = ip.trim().split(","); + for (String subIp : ips) + { + if (false == isUnknown(subIp)) + { + ip = subIp; + break; + } + } + } + return StringUtils.substring(ip, 0, 255); + } + + /** + * 检测给定字符串是否为未知,多用于检测HTTP请求相关 + * + * @param checkString 被检测的字符串 + * @return 是否未知 + */ + public static boolean isUnknown(String checkString) + { + return StringUtils.isBlank(checkString) || "unknown".equalsIgnoreCase(checkString); + } + + /** + * 是否为IP + */ + public static boolean isIP(String ip) + { + return StringUtils.isNotBlank(ip) && ip.matches(REGX_IP); + } + + /** + * 是否为IP,或 *为间隔的通配符地址 + */ + public static boolean isIpWildCard(String ip) + { + return StringUtils.isNotBlank(ip) && ip.matches(REGX_IP_WILDCARD); + } + + /** + * 检测参数是否在ip通配符里 + */ + public static boolean ipIsInWildCardNoCheck(String ipWildCard, String ip) + { + String[] s1 = ipWildCard.split("\\."); + String[] s2 = ip.split("\\."); + boolean isMatchedSeg = true; + for (int i = 0; i < s1.length && !s1[i].equals("*"); i++) + { + if (!s1[i].equals(s2[i])) + { + isMatchedSeg = false; + break; + } + } + return isMatchedSeg; + } + + /** + * 是否为特定格式如:“10.10.10.1-10.10.10.99”的ip段字符串 + */ + public static boolean isIPSegment(String ipSeg) + { + return StringUtils.isNotBlank(ipSeg) && ipSeg.matches(REGX_IP_SEG); + } + + /** + * 判断ip是否在指定网段中 + */ + public static boolean ipIsInNetNoCheck(String iparea, String ip) + { + int idx = iparea.indexOf('-'); + String[] sips = iparea.substring(0, idx).split("\\."); + String[] sipe = iparea.substring(idx + 1).split("\\."); + String[] sipt = ip.split("\\."); + long ips = 0L, ipe = 0L, ipt = 0L; + for (int i = 0; i < 4; ++i) + { + ips = ips << 8 | Integer.parseInt(sips[i]); + ipe = ipe << 8 | Integer.parseInt(sipe[i]); + ipt = ipt << 8 | Integer.parseInt(sipt[i]); + } + if (ips > ipe) + { + long t = ips; + ips = ipe; + ipe = t; + } + return ips <= ipt && ipt <= ipe; + } + + /** + * 校验ip是否符合过滤串规则 + * + * @param filter 过滤IP列表,支持后缀'*'通配,支持网段如:`10.10.10.1-10.10.10.99` + * @param ip 校验IP地址 + * @return boolean 结果 + */ + public static boolean isMatchedIp(String filter, String ip) + { + if (StringUtils.isEmpty(filter) || StringUtils.isEmpty(ip)) + { + return false; + } + String[] ips = filter.split(";"); + for (String iStr : ips) + { + if (isIP(iStr) && iStr.equals(ip)) + { + return true; + } + else if (isIpWildCard(iStr) && ipIsInWildCardNoCheck(iStr, ip)) + { + return true; + } + else if (isIPSegment(iStr) && ipIsInNetNoCheck(iStr, ip)) + { + return true; + } + } + return false; + } +} \ No newline at end of file diff --git a/src/main/java/com/ruoyi/common/utils/job/AbstractQuartzJob.java b/src/main/java/com/ruoyi/common/utils/job/AbstractQuartzJob.java new file mode 100644 index 0000000..ec7e622 --- /dev/null +++ b/src/main/java/com/ruoyi/common/utils/job/AbstractQuartzJob.java @@ -0,0 +1,107 @@ +package com.ruoyi.common.utils.job; + +import java.util.Date; +import org.quartz.Job; +import org.quartz.JobExecutionContext; +import org.quartz.JobExecutionException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import com.ruoyi.common.constant.Constants; +import com.ruoyi.common.constant.ScheduleConstants; +import com.ruoyi.common.utils.ExceptionUtil; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.bean.BeanUtils; +import com.ruoyi.common.utils.spring.SpringUtils; +import com.ruoyi.project.monitor.domain.SysJob; +import com.ruoyi.project.monitor.domain.SysJobLog; +import com.ruoyi.project.monitor.service.ISysJobLogService; + +/** + * 抽象quartz调用 + * + * @author ruoyi + */ +public abstract class AbstractQuartzJob implements Job +{ + private static final Logger log = LoggerFactory.getLogger(AbstractQuartzJob.class); + + /** + * 线程本地变量 + */ + private static ThreadLocal threadLocal = new ThreadLocal<>(); + + @Override + public void execute(JobExecutionContext context) throws JobExecutionException + { + SysJob sysJob = new SysJob(); + BeanUtils.copyBeanProp(sysJob, context.getMergedJobDataMap().get(ScheduleConstants.TASK_PROPERTIES)); + try + { + before(context, sysJob); + if (sysJob != null) + { + 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 sysScheduleJob 系统计划任务 + */ + 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.getExceptionMessage(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; +} diff --git a/src/main/java/com/ruoyi/common/utils/job/CronUtils.java b/src/main/java/com/ruoyi/common/utils/job/CronUtils.java new file mode 100644 index 0000000..0763a8d --- /dev/null +++ b/src/main/java/com/ruoyi/common/utils/job/CronUtils.java @@ -0,0 +1,63 @@ +package com.ruoyi.common.utils.job; + +import java.text.ParseException; +import java.util.Date; +import org.quartz.CronExpression; + +/** + * cron表达式工具类 + * + * @author ruoyi + * + */ +public class CronUtils +{ + /** + * 返回一个布尔值代表一个给定的Cron表达式的有效性 + * + * @param cronExpression Cron表达式 + * @return boolean 表达式是否有效 + */ + public static boolean isValid(String cronExpression) + { + return CronExpression.isValidExpression(cronExpression); + } + + /** + * 返回一个字符串值,表示该消息无效Cron表达式给出有效性 + * + * @param cronExpression Cron表达式 + * @return String 无效时返回表达式错误描述,如果有效返回null + */ + public static String getInvalidMessage(String cronExpression) + { + try + { + new CronExpression(cronExpression); + return null; + } + catch (ParseException pe) + { + return pe.getMessage(); + } + } + + /** + * 返回下一个执行时间根据给定的Cron表达式 + * + * @param cronExpression Cron表达式 + * @return Date 下次Cron表达式执行时间 + */ + public static Date getNextExecution(String cronExpression) + { + try + { + CronExpression cron = new CronExpression(cronExpression); + return cron.getNextValidTimeAfter(new Date(System.currentTimeMillis())); + } + catch (ParseException e) + { + throw new IllegalArgumentException(e.getMessage()); + } + } +} diff --git a/src/main/java/com/ruoyi/common/utils/job/JobInvokeUtil.java b/src/main/java/com/ruoyi/common/utils/job/JobInvokeUtil.java new file mode 100644 index 0000000..0d8f74b --- /dev/null +++ b/src/main/java/com/ruoyi/common/utils/job/JobInvokeUtil.java @@ -0,0 +1,182 @@ +package com.ruoyi.common.utils.job; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.LinkedList; +import java.util.List; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.spring.SpringUtils; +import com.ruoyi.project.monitor.domain.SysJob; + +/** + * 任务执行工具 + * + * @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 methodParams = getMethodParams(invokeTarget); + + if (!isValidClassName(beanName)) + { + Object bean = SpringUtils.getBean(beanName); + invokeMethod(bean, methodName, methodParams); + } + else + { + Object bean = Class.forName(beanName).getDeclaredConstructor().newInstance(); + invokeMethod(bean, methodName, methodParams); + } + } + + /** + * 调用任务方法 + * + * @param bean 目标对象 + * @param methodName 方法名称 + * @param methodParams 方法参数 + */ + private static void invokeMethod(Object bean, String methodName, List methodParams) + throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, + InvocationTargetException + { + if (StringUtils.isNotNull(methodParams) && methodParams.size() > 0) + { + Method method = bean.getClass().getMethod(methodName, getMethodParamsType(methodParams)); + method.invoke(bean, getMethodParamsValue(methodParams)); + } + else + { + Method method = bean.getClass().getMethod(methodName); + method.invoke(bean); + } + } + + /** + * 校验是否为为class包名 + * + * @param invokeTarget 名称 + * @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.substringBeforeLast(beanName, "."); + } + + /** + * 获取bean方法 + * + * @param invokeTarget 目标字符串 + * @return method方法 + */ + public static String getMethodName(String invokeTarget) + { + String methodName = StringUtils.substringBefore(invokeTarget, "("); + return StringUtils.substringAfterLast(methodName, "."); + } + + /** + * 获取method方法参数相关列表 + * + * @param invokeTarget 目标字符串 + * @return method方法相关参数列表 + */ + public static List getMethodParams(String invokeTarget) + { + String methodStr = StringUtils.substringBetween(invokeTarget, "(", ")"); + if (StringUtils.isEmpty(methodStr)) + { + return null; + } + String[] methodParams = methodStr.split(",(?=([^\"']*[\"'][^\"']*[\"'])*[^\"']*$)"); + List classs = new LinkedList<>(); + for (int i = 0; i < methodParams.length; i++) + { + String str = StringUtils.trimToEmpty(methodParams[i]); + // String字符串类型,以'或"开头 + if (StringUtils.startsWithAny(str, "'", "\"")) + { + classs.add(new Object[] { StringUtils.substring(str, 1, str.length() - 1), String.class }); + } + // boolean布尔类型,等于true或者false + else if ("true".equalsIgnoreCase(str) || "false".equalsIgnoreCase(str)) + { + classs.add(new Object[] { Boolean.valueOf(str), Boolean.class }); + } + // long长整形,以L结尾 + else if (StringUtils.endsWith(str, "L")) + { + classs.add(new Object[] { Long.valueOf(StringUtils.substring(str, 0, str.length() - 1)), Long.class }); + } + // double浮点类型,以D结尾 + else if (StringUtils.endsWith(str, "D")) + { + classs.add(new Object[] { Double.valueOf(StringUtils.substring(str, 0, str.length() - 1)), Double.class }); + } + // 其他类型归类为整形 + else + { + classs.add(new Object[] { Integer.valueOf(str), Integer.class }); + } + } + return classs; + } + + /** + * 获取参数类型 + * + * @param methodParams 参数相关列表 + * @return 参数类型列表 + */ + public static Class[] getMethodParamsType(List 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 methodParams) + { + Object[] classs = new Object[methodParams.size()]; + int index = 0; + for (Object[] os : methodParams) + { + classs[index] = (Object) os[0]; + index++; + } + return classs; + } +} diff --git a/src/main/java/com/ruoyi/common/utils/job/QuartzDisallowConcurrentExecution.java b/src/main/java/com/ruoyi/common/utils/job/QuartzDisallowConcurrentExecution.java new file mode 100644 index 0000000..7097088 --- /dev/null +++ b/src/main/java/com/ruoyi/common/utils/job/QuartzDisallowConcurrentExecution.java @@ -0,0 +1,21 @@ +package com.ruoyi.common.utils.job; + +import org.quartz.DisallowConcurrentExecution; +import org.quartz.JobExecutionContext; +import com.ruoyi.project.monitor.domain.SysJob; + +/** + * 定时任务处理(禁止并发执行) + * + * @author ruoyi + * + */ +@DisallowConcurrentExecution +public class QuartzDisallowConcurrentExecution extends AbstractQuartzJob +{ + @Override + protected void doExecute(JobExecutionContext context, SysJob sysJob) throws Exception + { + JobInvokeUtil.invokeMethod(sysJob); + } +} diff --git a/src/main/java/com/ruoyi/common/utils/job/QuartzJobExecution.java b/src/main/java/com/ruoyi/common/utils/job/QuartzJobExecution.java new file mode 100644 index 0000000..55aa7e0 --- /dev/null +++ b/src/main/java/com/ruoyi/common/utils/job/QuartzJobExecution.java @@ -0,0 +1,19 @@ +package com.ruoyi.common.utils.job; + +import org.quartz.JobExecutionContext; +import com.ruoyi.project.monitor.domain.SysJob; + +/** + * 定时任务处理(允许并发执行) + * + * @author ruoyi + * + */ +public class QuartzJobExecution extends AbstractQuartzJob +{ + @Override + protected void doExecute(JobExecutionContext context, SysJob sysJob) throws Exception + { + JobInvokeUtil.invokeMethod(sysJob); + } +} diff --git a/src/main/java/com/ruoyi/common/utils/job/ScheduleUtils.java b/src/main/java/com/ruoyi/common/utils/job/ScheduleUtils.java new file mode 100644 index 0000000..0353ea2 --- /dev/null +++ b/src/main/java/com/ruoyi/common/utils/job/ScheduleUtils.java @@ -0,0 +1,141 @@ +package com.ruoyi.common.utils.job; + +import org.quartz.CronScheduleBuilder; +import org.quartz.CronTrigger; +import org.quartz.Job; +import org.quartz.JobBuilder; +import org.quartz.JobDetail; +import org.quartz.JobKey; +import org.quartz.Scheduler; +import org.quartz.SchedulerException; +import org.quartz.TriggerBuilder; +import org.quartz.TriggerKey; +import com.ruoyi.common.constant.Constants; +import com.ruoyi.common.constant.ScheduleConstants; +import com.ruoyi.common.exception.job.TaskException; +import com.ruoyi.common.exception.job.TaskException.Code; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.spring.SpringUtils; +import com.ruoyi.project.monitor.domain.SysJob; + +/** + * 定时任务工具类 + * + * @author ruoyi + * + */ +public class ScheduleUtils +{ + /** + * 得到quartz任务类 + * + * @param sysJob 执行计划 + * @return 具体执行任务类 + */ + private static Class getQuartzJobClass(SysJob sysJob) + { + boolean isConcurrent = "0".equals(sysJob.getConcurrent()); + return isConcurrent ? QuartzJobExecution.class : QuartzDisallowConcurrentExecution.class; + } + + /** + * 构建任务触发对象 + */ + public static TriggerKey getTriggerKey(Long jobId, String jobGroup) + { + return TriggerKey.triggerKey(ScheduleConstants.TASK_CLASS_NAME + jobId, jobGroup); + } + + /** + * 构建任务键对象 + */ + public static JobKey getJobKey(Long jobId, String jobGroup) + { + return JobKey.jobKey(ScheduleConstants.TASK_CLASS_NAME + jobId, jobGroup); + } + + /** + * 创建定时任务 + */ + public static void createScheduleJob(Scheduler scheduler, SysJob job) throws SchedulerException, TaskException + { + Class jobClass = getQuartzJobClass(job); + // 构建job信息 + Long jobId = job.getJobId(); + String jobGroup = job.getJobGroup(); + JobDetail jobDetail = JobBuilder.newJob(jobClass).withIdentity(getJobKey(jobId, jobGroup)).build(); + + // 表达式调度构建器 + CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(job.getCronExpression()); + cronScheduleBuilder = handleCronScheduleMisfirePolicy(job, cronScheduleBuilder); + + // 按新的cronExpression表达式构建一个新的trigger + CronTrigger trigger = TriggerBuilder.newTrigger().withIdentity(getTriggerKey(jobId, jobGroup)) + .withSchedule(cronScheduleBuilder).build(); + + // 放入参数,运行时的方法可以获取 + jobDetail.getJobDataMap().put(ScheduleConstants.TASK_PROPERTIES, job); + + // 判断是否存在 + if (scheduler.checkExists(getJobKey(jobId, jobGroup))) + { + // 防止创建时存在数据问题 先移除,然后在执行创建操作 + scheduler.deleteJob(getJobKey(jobId, jobGroup)); + } + + // 判断任务是否过期 + if (StringUtils.isNotNull(CronUtils.getNextExecution(job.getCronExpression()))) + { + // 执行调度任务 + scheduler.scheduleJob(jobDetail, trigger); + } + + // 暂停任务 + if (job.getStatus().equals(ScheduleConstants.Status.PAUSE.getValue())) + { + scheduler.pauseJob(ScheduleUtils.getJobKey(jobId, jobGroup)); + } + } + + /** + * 设置定时任务策略 + */ + public static CronScheduleBuilder handleCronScheduleMisfirePolicy(SysJob job, CronScheduleBuilder cb) + throws TaskException + { + switch (job.getMisfirePolicy()) + { + case ScheduleConstants.MISFIRE_DEFAULT: + return cb; + case ScheduleConstants.MISFIRE_IGNORE_MISFIRES: + return cb.withMisfireHandlingInstructionIgnoreMisfires(); + case ScheduleConstants.MISFIRE_FIRE_AND_PROCEED: + return cb.withMisfireHandlingInstructionFireAndProceed(); + case ScheduleConstants.MISFIRE_DO_NOTHING: + return cb.withMisfireHandlingInstructionDoNothing(); + default: + throw new TaskException("The task misfire policy '" + job.getMisfirePolicy() + + "' cannot be used in cron schedule tasks", Code.CONFIG_ERROR); + } + } + + /** + * 检查包名是否为白名单配置 + * + * @param invokeTarget 目标字符串 + * @return 结果 + */ + public static boolean whiteList(String invokeTarget) + { + String packageName = StringUtils.substringBefore(invokeTarget, "("); + int count = StringUtils.countMatches(packageName, "."); + if (count > 1) + { + return StringUtils.containsAnyIgnoreCase(invokeTarget, Constants.JOB_WHITELIST_STR); + } + Object obj = SpringUtils.getBean(StringUtils.split(invokeTarget, ".")[0]); + String beanPackageName = obj.getClass().getPackage().getName(); + return StringUtils.containsAnyIgnoreCase(beanPackageName, Constants.JOB_WHITELIST_STR) + && !StringUtils.containsAnyIgnoreCase(beanPackageName, Constants.JOB_ERROR_STR); + } +} diff --git a/src/main/java/com/ruoyi/common/utils/poi/ExcelHandlerAdapter.java b/src/main/java/com/ruoyi/common/utils/poi/ExcelHandlerAdapter.java new file mode 100644 index 0000000..ccab288 --- /dev/null +++ b/src/main/java/com/ruoyi/common/utils/poi/ExcelHandlerAdapter.java @@ -0,0 +1,24 @@ +package com.ruoyi.common.utils.poi; + +import org.apache.poi.ss.usermodel.Cell; +import org.apache.poi.ss.usermodel.Workbook; + +/** + * Excel数据格式处理适配器 + * + * @author ruoyi + */ +public interface ExcelHandlerAdapter +{ + /** + * 格式化 + * + * @param value 单元格数据值 + * @param args excel注解args参数组 + * @param cell 单元格对象 + * @param wb 工作簿对象 + * + * @return 处理后的值 + */ + Object format(Object value, String[] args, Cell cell, Workbook wb); +} diff --git a/src/main/java/com/ruoyi/common/utils/poi/ExcelUtil.java b/src/main/java/com/ruoyi/common/utils/poi/ExcelUtil.java new file mode 100644 index 0000000..99517d6 --- /dev/null +++ b/src/main/java/com/ruoyi/common/utils/poi/ExcelUtil.java @@ -0,0 +1,1796 @@ +package com.ruoyi.common.utils.poi; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.lang.reflect.ParameterizedType; +import java.math.BigDecimal; +import java.text.DecimalFormat; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Comparator; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.UUID; +import java.util.stream.Collectors; +import javax.servlet.http.HttpServletResponse; +import org.apache.commons.lang3.ArrayUtils; +import org.apache.commons.lang3.RegExUtils; +import org.apache.commons.lang3.reflect.FieldUtils; +import org.apache.poi.hssf.usermodel.HSSFClientAnchor; +import org.apache.poi.hssf.usermodel.HSSFPicture; +import org.apache.poi.hssf.usermodel.HSSFPictureData; +import org.apache.poi.hssf.usermodel.HSSFShape; +import org.apache.poi.hssf.usermodel.HSSFSheet; +import org.apache.poi.hssf.usermodel.HSSFWorkbook; +import org.apache.poi.ooxml.POIXMLDocumentPart; +import org.apache.poi.ss.usermodel.BorderStyle; +import org.apache.poi.ss.usermodel.Cell; +import org.apache.poi.ss.usermodel.CellStyle; +import org.apache.poi.ss.usermodel.CellType; +import org.apache.poi.ss.usermodel.ClientAnchor; +import org.apache.poi.ss.usermodel.DataFormat; +import org.apache.poi.ss.usermodel.DataValidation; +import org.apache.poi.ss.usermodel.DataValidationConstraint; +import org.apache.poi.ss.usermodel.DataValidationHelper; +import org.apache.poi.ss.usermodel.DateUtil; +import org.apache.poi.ss.usermodel.Drawing; +import org.apache.poi.ss.usermodel.FillPatternType; +import org.apache.poi.ss.usermodel.Font; +import org.apache.poi.ss.usermodel.HorizontalAlignment; +import org.apache.poi.ss.usermodel.IndexedColors; +import org.apache.poi.ss.usermodel.Name; +import org.apache.poi.ss.usermodel.PictureData; +import org.apache.poi.ss.usermodel.Row; +import org.apache.poi.ss.usermodel.Sheet; +import org.apache.poi.ss.usermodel.VerticalAlignment; +import org.apache.poi.ss.usermodel.Workbook; +import org.apache.poi.ss.usermodel.WorkbookFactory; +import org.apache.poi.ss.util.CellRangeAddress; +import org.apache.poi.ss.util.CellRangeAddressList; +import org.apache.poi.util.IOUtils; +import org.apache.poi.xssf.streaming.SXSSFWorkbook; +import org.apache.poi.xssf.usermodel.XSSFClientAnchor; +import org.apache.poi.xssf.usermodel.XSSFDataValidation; +import org.apache.poi.xssf.usermodel.XSSFDrawing; +import org.apache.poi.xssf.usermodel.XSSFPicture; +import org.apache.poi.xssf.usermodel.XSSFShape; +import org.apache.poi.xssf.usermodel.XSSFSheet; +import org.apache.poi.xssf.usermodel.XSSFWorkbook; +import org.openxmlformats.schemas.drawingml.x2006.spreadsheetDrawing.CTMarker; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import com.ruoyi.common.core.text.Convert; +import com.ruoyi.common.exception.UtilException; +import com.ruoyi.common.utils.DateUtils; +import com.ruoyi.common.utils.DictUtils; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.file.FileTypeUtils; +import com.ruoyi.common.utils.file.FileUtils; +import com.ruoyi.common.utils.file.ImageUtils; +import com.ruoyi.common.utils.reflect.ReflectUtils; +import com.ruoyi.framework.aspectj.lang.annotation.Excel; +import com.ruoyi.framework.aspectj.lang.annotation.Excel.ColumnType; +import com.ruoyi.framework.aspectj.lang.annotation.Excel.Type; +import com.ruoyi.framework.aspectj.lang.annotation.Excels; +import com.ruoyi.framework.config.RuoYiConfig; +import com.ruoyi.framework.web.domain.AjaxResult; + +/** + * Excel相关处理 + * + * @author ruoyi + */ +public class ExcelUtil +{ + private static final Logger log = LoggerFactory.getLogger(ExcelUtil.class); + + public static final String FORMULA_REGEX_STR = "=|-|\\+|@"; + + public static final String[] FORMULA_STR = { "=", "-", "+", "@" }; + + /** + * 用于dictType属性数据存储,避免重复查缓存 + */ + public Map sysDictMap = new HashMap(); + + /** + * Excel sheet最大行数,默认65536 + */ + public static final int sheetSize = 65536; + + /** + * 工作表名称 + */ + private String sheetName; + + /** + * 导出类型(EXPORT:导出数据;IMPORT:导入模板) + */ + private Type type; + + /** + * 工作薄对象 + */ + private Workbook wb; + + /** + * 工作表对象 + */ + private Sheet sheet; + + /** + * 样式列表 + */ + private Map styles; + + /** + * 导入导出数据列表 + */ + private List list; + + /** + * 注解列表 + */ + private List fields; + + /** + * 当前行号 + */ + private int rownum; + + /** + * 标题 + */ + private String title; + + /** + * 最大高度 + */ + private short maxHeight; + + /** + * 合并后最后行数 + */ + private int subMergedLastRowNum = 0; + + /** + * 合并后开始行数 + */ + private int subMergedFirstRowNum = 1; + + /** + * 对象的子列表方法 + */ + private Method subMethod; + + /** + * 对象的子列表属性 + */ + private List subFields; + + /** + * 统计列表 + */ + private Map statistics = new HashMap(); + + /** + * 数字格式 + */ + private static final DecimalFormat DOUBLE_FORMAT = new DecimalFormat("######0.00"); + + /** + * 实体对象 + */ + public Class clazz; + + /** + * 需要排除列属性 + */ + public String[] excludeFields; + + public ExcelUtil(Class clazz) + { + this.clazz = clazz; + } + + /** + * 隐藏Excel中列属性 + * + * @param fields 列属性名 示例[单个"name"/多个"id","name"] + * @throws Exception + */ + public void hideColumn(String... fields) + { + this.excludeFields = fields; + } + + public void init(List list, String sheetName, String title, Type type) + { + if (list == null) + { + list = new ArrayList(); + } + this.list = list; + this.sheetName = sheetName; + this.type = type; + this.title = title; + createExcelField(); + createWorkbook(); + createTitle(); + createSubHead(); + } + + /** + * 创建excel第一行标题 + */ + public void createTitle() + { + if (StringUtils.isNotEmpty(title)) + { + subMergedFirstRowNum++; + subMergedLastRowNum++; + int titleLastCol = this.fields.size() - 1; + if (isSubList()) + { + titleLastCol = titleLastCol + subFields.size() - 1; + } + Row titleRow = sheet.createRow(rownum == 0 ? rownum++ : 0); + titleRow.setHeightInPoints(30); + Cell titleCell = titleRow.createCell(0); + titleCell.setCellStyle(styles.get("title")); + titleCell.setCellValue(title); + sheet.addMergedRegion(new CellRangeAddress(titleRow.getRowNum(), titleRow.getRowNum(), titleRow.getRowNum(), titleLastCol)); + } + } + + /** + * 创建对象的子列表名称 + */ + public void createSubHead() + { + if (isSubList()) + { + subMergedFirstRowNum++; + subMergedLastRowNum++; + Row subRow = sheet.createRow(rownum); + int excelNum = 0; + for (Object[] objects : fields) + { + Excel attr = (Excel) objects[1]; + Cell headCell1 = subRow.createCell(excelNum); + headCell1.setCellValue(attr.name()); + headCell1.setCellStyle(styles.get(StringUtils.format("header_{}_{}", attr.headerColor(), attr.headerBackgroundColor()))); + excelNum++; + } + int headFirstRow = excelNum - 1; + int headLastRow = headFirstRow + subFields.size() - 1; + if (headLastRow > headFirstRow) + { + sheet.addMergedRegion(new CellRangeAddress(rownum, rownum, headFirstRow, headLastRow)); + } + rownum++; + } + } + + /** + * 对excel表单默认第一个索引名转换成list + * + * @param is 输入流 + * @return 转换后集合 + */ + public List importExcel(InputStream is) + { + List list = null; + try + { + list = importExcel(is, 0); + } + catch (Exception e) + { + log.error("导入Excel异常{}", e.getMessage()); + throw new UtilException(e.getMessage()); + } + finally + { + IOUtils.closeQuietly(is); + } + return list; + } + + /** + * 对excel表单默认第一个索引名转换成list + * + * @param is 输入流 + * @param titleNum 标题占用行数 + * @return 转换后集合 + */ + public List importExcel(InputStream is, int titleNum) throws Exception + { + return importExcel(StringUtils.EMPTY, is, titleNum); + } + + /** + * 对excel表单指定表格索引名转换成list + * + * @param sheetName 表格索引名 + * @param titleNum 标题占用行数 + * @param is 输入流 + * @return 转换后集合 + */ + public List importExcel(String sheetName, InputStream is, int titleNum) throws Exception + { + this.type = Type.IMPORT; + this.wb = WorkbookFactory.create(is); + List list = new ArrayList(); + // 如果指定sheet名,则取指定sheet中的内容 否则默认指向第1个sheet + Sheet sheet = StringUtils.isNotEmpty(sheetName) ? wb.getSheet(sheetName) : wb.getSheetAt(0); + if (sheet == null) + { + throw new IOException("文件sheet不存在"); + } + boolean isXSSFWorkbook = !(wb instanceof HSSFWorkbook); + Map pictures; + if (isXSSFWorkbook) + { + pictures = getSheetPictures07((XSSFSheet) sheet, (XSSFWorkbook) wb); + } + else + { + pictures = getSheetPictures03((HSSFSheet) sheet, (HSSFWorkbook) wb); + } + // 获取最后一个非空行的行下标,比如总行数为n,则返回的为n-1 + int rows = sheet.getLastRowNum(); + if (rows > 0) + { + // 定义一个map用于存放excel列的序号和field. + Map cellMap = new HashMap(); + // 获取表头 + Row heard = sheet.getRow(titleNum); + for (int i = 0; i < heard.getPhysicalNumberOfCells(); i++) + { + Cell cell = heard.getCell(i); + if (StringUtils.isNotNull(cell)) + { + String value = this.getCellValue(heard, i).toString(); + cellMap.put(value, i); + } + else + { + cellMap.put(null, i); + } + } + // 有数据时才处理 得到类的所有field. + List fields = this.getFields(); + Map fieldsMap = new HashMap(); + for (Object[] objects : fields) + { + Excel attr = (Excel) objects[1]; + Integer column = cellMap.get(attr.name()); + if (column != null) + { + fieldsMap.put(column, objects); + } + } + for (int i = titleNum + 1; i <= rows; i++) + { + // 从第2行开始取数据,默认第一行是表头. + Row row = sheet.getRow(i); + // 判断当前行是否是空行 + if (isRowEmpty(row)) + { + continue; + } + T entity = null; + for (Map.Entry entry : fieldsMap.entrySet()) + { + Object val = this.getCellValue(row, entry.getKey()); + + // 如果不存在实例则新建. + entity = (entity == null ? clazz.newInstance() : entity); + // 从map中得到对应列的field. + Field field = (Field) entry.getValue()[0]; + Excel attr = (Excel) entry.getValue()[1]; + // 取得类型,并根据对象类型设置值. + Class fieldType = field.getType(); + if (String.class == fieldType) + { + String s = Convert.toStr(val); + if (StringUtils.endsWith(s, ".0")) + { + val = StringUtils.substringBefore(s, ".0"); + } + else + { + String dateFormat = field.getAnnotation(Excel.class).dateFormat(); + if (StringUtils.isNotEmpty(dateFormat)) + { + val = parseDateToStr(dateFormat, val); + } + else + { + val = Convert.toStr(val); + } + } + } + else if ((Integer.TYPE == fieldType || Integer.class == fieldType) && StringUtils.isNumeric(Convert.toStr(val))) + { + val = Convert.toInt(val); + } + else if ((Long.TYPE == fieldType || Long.class == fieldType) && StringUtils.isNumeric(Convert.toStr(val))) + { + 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 (StringUtils.isNotNull(fieldType)) + { + String propertyName = field.getName(); + if (StringUtils.isNotEmpty(attr.targetAttr())) + { + propertyName = field.getName() + "." + attr.targetAttr(); + } + if (StringUtils.isNotEmpty(attr.readConverterExp())) + { + val = reverseByExp(Convert.toStr(val), attr.readConverterExp(), attr.separator()); + } + else if (StringUtils.isNotEmpty(attr.dictType())) + { + val = reverseDictByExp(Convert.toStr(val), attr.dictType(), attr.separator()); + } + else if (!attr.handler().equals(ExcelHandlerAdapter.class)) + { + val = dataFormatHandlerAdapter(val, attr, null); + } + else if (ColumnType.IMAGE == attr.cellType() && StringUtils.isNotEmpty(pictures)) + { + PictureData image = pictures.get(row.getRowNum() + "_" + entry.getKey()); + if (image == null) + { + val = ""; + } + else + { + byte[] data = image.getData(); + val = FileUtils.writeImportBytes(data); + } + } + ReflectUtils.invokeSetter(entity, propertyName, val); + } + } + list.add(entity); + } + } + return list; + } + + /** + * 对list数据源将其里面的数据导入到excel表单 + * + * @param list 导出数据集合 + * @param sheetName 工作表的名称 + * @return 结果 + */ + public AjaxResult exportExcel(List list, String sheetName) + { + return exportExcel(list, sheetName, StringUtils.EMPTY); + } + + /** + * 对list数据源将其里面的数据导入到excel表单 + * + * @param list 导出数据集合 + * @param sheetName 工作表的名称 + * @param title 标题 + * @return 结果 + */ + public AjaxResult exportExcel(List list, String sheetName, String title) + { + this.init(list, sheetName, title, Type.EXPORT); + return exportExcel(); + } + + /** + * 对list数据源将其里面的数据导入到excel表单 + * + * @param response 返回数据 + * @param list 导出数据集合 + * @param sheetName 工作表的名称 + * @return 结果 + */ + public void exportExcel(HttpServletResponse response, List list, String sheetName) + { + exportExcel(response, list, sheetName, StringUtils.EMPTY); + } + + /** + * 对list数据源将其里面的数据导入到excel表单 + * + * @param response 返回数据 + * @param list 导出数据集合 + * @param sheetName 工作表的名称 + * @param title 标题 + * @return 结果 + */ + public void exportExcel(HttpServletResponse response, List list, String sheetName, String title) + { + response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); + response.setCharacterEncoding("utf-8"); + this.init(list, sheetName, title, Type.EXPORT); + exportExcel(response); + } + + /** + * 对list数据源将其里面的数据导入到excel表单 + * + * @param sheetName 工作表的名称 + * @return 结果 + */ + public AjaxResult importTemplateExcel(String sheetName) + { + return importTemplateExcel(sheetName, StringUtils.EMPTY); + } + + /** + * 对list数据源将其里面的数据导入到excel表单 + * + * @param sheetName 工作表的名称 + * @param title 标题 + * @return 结果 + */ + public AjaxResult importTemplateExcel(String sheetName, String title) + { + this.init(null, sheetName, title, Type.IMPORT); + return exportExcel(); + } + + /** + * 对list数据源将其里面的数据导入到excel表单 + * + * @param sheetName 工作表的名称 + * @return 结果 + */ + public void importTemplateExcel(HttpServletResponse response, String sheetName) + { + importTemplateExcel(response, sheetName, StringUtils.EMPTY); + } + + /** + * 对list数据源将其里面的数据导入到excel表单 + * + * @param sheetName 工作表的名称 + * @param title 标题 + * @return 结果 + */ + public void importTemplateExcel(HttpServletResponse response, String sheetName, String title) + { + response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); + response.setCharacterEncoding("utf-8"); + this.init(null, sheetName, title, Type.IMPORT); + exportExcel(response); + } + + /** + * 对list数据源将其里面的数据导入到excel表单 + * + * @return 结果 + */ + public void exportExcel(HttpServletResponse response) + { + try + { + writeSheet(); + wb.write(response.getOutputStream()); + } + catch (Exception e) + { + log.error("导出Excel异常{}", e.getMessage()); + } + finally + { + IOUtils.closeQuietly(wb); + } + } + + /** + * 对list数据源将其里面的数据导入到excel表单 + * + * @return 结果 + */ + public AjaxResult exportExcel() + { + OutputStream out = null; + try + { + writeSheet(); + 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 UtilException("导出Excel失败,请联系网站管理员!"); + } + finally + { + IOUtils.closeQuietly(wb); + IOUtils.closeQuietly(out); + } + } + + /** + * 创建写入数据到Sheet + */ + public void writeSheet() + { + // 取出一共有多少个sheet. + int sheetNo = Math.max(1, (int) Math.ceil(list.size() * 1.0 / sheetSize)); + for (int index = 0; index < sheetNo; index++) + { + createSheet(sheetNo, index); + + // 产生一行 + Row row = sheet.createRow(rownum); + int column = 0; + // 写入各个字段的列头名称 + for (Object[] os : fields) + { + Field field = (Field) os[0]; + Excel excel = (Excel) os[1]; + if (Collection.class.isAssignableFrom(field.getType())) + { + for (Field subField : subFields) + { + Excel subExcel = subField.getAnnotation(Excel.class); + this.createHeadCell(subExcel, row, column++); + } + } + else + { + this.createHeadCell(excel, row, column++); + } + } + if (Type.EXPORT.equals(type)) + { + fillExcelData(index, row); + addStatisticsRow(); + } + } + } + + /** + * 填充excel数据 + * + * @param index 序号 + * @param row 单元格行 + */ + @SuppressWarnings("unchecked") + public void fillExcelData(int index, Row row) + { + int startNo = index * sheetSize; + int endNo = Math.min(startNo + sheetSize, list.size()); + int rowNo = (1 + rownum) - startNo; + for (int i = startNo; i < endNo; i++) + { + rowNo = isSubList() ? (i > 1 ? rowNo + 1 : rowNo + i) : i + 1 + rownum - startNo; + row = sheet.createRow(rowNo); + // 得到导出对象. + T vo = (T) list.get(i); + Collection subList = null; + if (isSubList()) + { + if (isSubListValue(vo)) + { + subList = getListCellValue(vo); + subMergedLastRowNum = subMergedLastRowNum + subList.size(); + } + else + { + subMergedFirstRowNum++; + subMergedLastRowNum++; + } + } + int column = 0; + for (Object[] os : fields) + { + Field field = (Field) os[0]; + Excel excel = (Excel) os[1]; + if (Collection.class.isAssignableFrom(field.getType()) && StringUtils.isNotNull(subList)) + { + boolean subFirst = false; + for (Object obj : subList) + { + if (subFirst) + { + rowNo++; + row = sheet.createRow(rowNo); + } + List subFields = FieldUtils.getFieldsListWithAnnotation(obj.getClass(), Excel.class); + int subIndex = 0; + for (Field subField : subFields) + { + if (subField.isAnnotationPresent(Excel.class)) + { + subField.setAccessible(true); + Excel attr = subField.getAnnotation(Excel.class); + this.addCell(attr, row, (T) obj, subField, column + subIndex); + } + subIndex++; + } + subFirst = true; + } + this.subMergedFirstRowNum = this.subMergedFirstRowNum + subList.size(); + } + else + { + this.addCell(excel, row, vo, field, column++); + } + } + } + } + + /** + * 创建表格样式 + * + * @param wb 工作薄对象 + * @return 样式列表 + */ + private Map createStyles(Workbook wb) + { + // 写入各条记录,每条记录对应excel表中的一行 + Map styles = new HashMap(); + CellStyle style = wb.createCellStyle(); + style.setAlignment(HorizontalAlignment.CENTER); + style.setVerticalAlignment(VerticalAlignment.CENTER); + Font titleFont = wb.createFont(); + titleFont.setFontName("Arial"); + titleFont.setFontHeightInPoints((short) 16); + titleFont.setBold(true); + style.setFont(titleFont); + DataFormat dataFormat = wb.createDataFormat(); + style.setDataFormat(dataFormat.getFormat("@")); + styles.put("title", style); + + 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.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); + + styles.putAll(annotationHeaderStyles(wb, styles)); + + styles.putAll(annotationDataStyles(wb)); + + return styles; + } + + /** + * 根据Excel注解创建表格头样式 + * + * @param wb 工作薄对象 + * @return 自定义样式列表 + */ + private Map annotationHeaderStyles(Workbook wb, Map styles) + { + Map headerStyles = new HashMap(); + for (Object[] os : fields) + { + Excel excel = (Excel) os[1]; + String key = StringUtils.format("header_{}_{}", excel.headerColor(), excel.headerBackgroundColor()); + if (!headerStyles.containsKey(key)) + { + CellStyle style = wb.createCellStyle(); + style.cloneStyleFrom(styles.get("data")); + style.setAlignment(HorizontalAlignment.CENTER); + style.setVerticalAlignment(VerticalAlignment.CENTER); + style.setFillForegroundColor(excel.headerBackgroundColor().index); + style.setFillPattern(FillPatternType.SOLID_FOREGROUND); + Font headerFont = wb.createFont(); + headerFont.setFontName("Arial"); + headerFont.setFontHeightInPoints((short) 10); + headerFont.setBold(true); + headerFont.setColor(excel.headerColor().index); + style.setFont(headerFont); + // 设置表格头单元格文本形式 + DataFormat dataFormat = wb.createDataFormat(); + style.setDataFormat(dataFormat.getFormat("@")); + headerStyles.put(key, style); + } + } + return headerStyles; + } + + /** + * 根据Excel注解创建表格列样式 + * + * @param wb 工作薄对象 + * @return 自定义样式列表 + */ + private Map annotationDataStyles(Workbook wb) + { + Map styles = new HashMap(); + for (Object[] os : fields) + { + Field field = (Field) os[0]; + Excel excel = (Excel) os[1]; + if (Collection.class.isAssignableFrom(field.getType())) + { + ParameterizedType pt = (ParameterizedType) field.getGenericType(); + Class subClass = (Class) pt.getActualTypeArguments()[0]; + List subFields = FieldUtils.getFieldsListWithAnnotation(subClass, Excel.class); + for (Field subField : subFields) + { + Excel subExcel = subField.getAnnotation(Excel.class); + annotationDataStyles(styles, subField, subExcel); + } + } + else + { + annotationDataStyles(styles, field, excel); + } + } + return styles; + } + + /** + * 根据Excel注解创建表格列样式 + * + * @param styles 自定义样式列表 + * @param field 属性列信息 + * @param excel 注解信息 + */ + public void annotationDataStyles(Map styles, Field field, Excel excel) + { + String key = StringUtils.format("data_{}_{}_{}_{}", excel.align(), excel.color(), excel.backgroundColor(), excel.cellType()); + if (!styles.containsKey(key)) + { + CellStyle style = wb.createCellStyle(); + style.setAlignment(excel.align()); + 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()); + style.setFillPattern(FillPatternType.SOLID_FOREGROUND); + style.setFillForegroundColor(excel.backgroundColor().getIndex()); + Font dataFont = wb.createFont(); + dataFont.setFontName("Arial"); + dataFont.setFontHeightInPoints((short) 10); + dataFont.setColor(excel.color().index); + style.setFont(dataFont); + if (ColumnType.TEXT == excel.cellType()) + { + DataFormat dataFormat = wb.createDataFormat(); + style.setDataFormat(dataFormat.getFormat("@")); + } + styles.put(key, style); + } + } + + /** + * 创建单元格 + */ + public Cell createHeadCell(Excel attr, Row row, int column) + { + // 创建列 + Cell cell = row.createCell(column); + // 写入列信息 + cell.setCellValue(attr.name()); + setDataValidation(attr, row, column); + cell.setCellStyle(styles.get(StringUtils.format("header_{}_{}", attr.headerColor(), attr.headerBackgroundColor()))); + if (isSubList()) + { + // 填充默认样式,防止合并单元格样式失效 + sheet.setDefaultColumnStyle(column, styles.get(StringUtils.format("data_{}_{}_{}_{}", attr.align(), attr.color(), attr.backgroundColor(), attr.cellType()))); + if (attr.needMerge()) + { + sheet.addMergedRegion(new CellRangeAddress(rownum - 1, rownum, column, column)); + } + } + return cell; + } + + /** + * 设置单元格信息 + * + * @param value 单元格值 + * @param attr 注解相关 + * @param cell 单元格信息 + */ + public void setCellVo(Object value, Excel attr, Cell cell) + { + if (ColumnType.STRING == attr.cellType() || ColumnType.TEXT == attr.cellType()) + { + String cellValue = Convert.toStr(value); + // 对于任何以表达式触发字符 =-+@开头的单元格,直接使用tab字符作为前缀,防止CSV注入。 + if (StringUtils.startsWithAny(cellValue, FORMULA_STR)) + { + cellValue = RegExUtils.replaceFirst(cellValue, FORMULA_REGEX_STR, "\t$0"); + } + if (value instanceof Collection && StringUtils.equals("[]", cellValue)) + { + cellValue = StringUtils.EMPTY; + } + cell.setCellValue(StringUtils.isNull(cellValue) ? attr.defaultValue() : cellValue + attr.suffix()); + } + else if (ColumnType.NUMERIC == attr.cellType()) + { + if (StringUtils.isNotNull(value)) + { + cell.setCellValue(StringUtils.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 (StringUtils.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 (StringUtils.isNotEmpty(attr.prompt()) || attr.combo().length > 0) + { + if (attr.combo().length > 15 || StringUtils.join(attr.combo()).length() > 255) + { + // 如果下拉数大于15或字符串长度大于255,则使用一个新sheet存储,避免生成的模板下拉值获取不到 + setXSSFValidationWithHidden(sheet, attr.combo(), attr.prompt(), 1, 100, column, column); + } + else + { + // 提示信息或只能选择不能输入的列内容. + setPromptOrValidation(sheet, attr.combo(), attr.prompt(), 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); + if (isSubListValue(vo) && getListCellValue(vo).size() > 1 && attr.needMerge()) + { + CellRangeAddress cellAddress = new CellRangeAddress(subMergedFirstRowNum, subMergedLastRowNum, column, column); + sheet.addMergedRegion(cellAddress); + } + cell.setCellStyle(styles.get(StringUtils.format("data_{}_{}_{}_{}", attr.align(), attr.color(), attr.backgroundColor(), attr.cellType()))); + + // 用于读取对象中的属性 + Object value = getTargetValue(vo, field, attr); + String dateFormat = attr.dateFormat(); + String readConverterExp = attr.readConverterExp(); + String separator = attr.separator(); + String dictType = attr.dictType(); + if (StringUtils.isNotEmpty(dateFormat) && StringUtils.isNotNull(value)) + { + cell.setCellValue(parseDateToStr(dateFormat, value)); + } + else if (StringUtils.isNotEmpty(readConverterExp) && StringUtils.isNotNull(value)) + { + cell.setCellValue(convertByExp(Convert.toStr(value), readConverterExp, separator)); + } + else if (StringUtils.isNotEmpty(dictType) && StringUtils.isNotNull(value)) + { + if (!sysDictMap.containsKey(dictType + value)) + { + String lable = convertDictByExp(Convert.toStr(value), dictType, separator); + sysDictMap.put(dictType + value, lable); + } + cell.setCellValue(sysDictMap.get(dictType + value)); + } + else if (value instanceof BigDecimal && -1 != attr.scale()) + { + cell.setCellValue((((BigDecimal) value).setScale(attr.scale(), attr.roundingMode())).doubleValue()); + } + else if (!attr.handler().equals(ExcelHandlerAdapter.class)) + { + cell.setCellValue(dataFormatHandlerAdapter(value, attr, cell)); + } + 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 textlist 下拉框显示的内容 + * @param promptContent 提示内容 + * @param firstRow 开始行 + * @param endRow 结束行 + * @param firstCol 开始列 + * @param endCol 结束列 + */ + public void setPromptOrValidation(Sheet sheet, String[] textlist, String promptContent, int firstRow, int endRow, + int firstCol, int endCol) + { + DataValidationHelper helper = sheet.getDataValidationHelper(); + DataValidationConstraint constraint = textlist.length > 0 ? helper.createExplicitListConstraint(textlist) : helper.createCustomConstraint("DD1"); + CellRangeAddressList regions = new CellRangeAddressList(firstRow, endRow, firstCol, endCol); + DataValidation dataValidation = helper.createValidation(constraint, regions); + if (StringUtils.isNotEmpty(promptContent)) + { + // 如果设置了提示信息则鼠标放上去提示 + dataValidation.createPromptBox("", promptContent); + dataValidation.setShowPromptBox(true); + } + // 处理Excel兼容性问题 + if (dataValidation instanceof XSSFDataValidation) + { + dataValidation.setSuppressDropDownArrow(true); + dataValidation.setShowErrorBox(true); + } + else + { + dataValidation.setSuppressDropDownArrow(false); + } + sheet.addValidationData(dataValidation); + } + + /** + * 设置某些列的值只能输入预制的数据,显示下拉框(兼容超出一定数量的下拉框). + * + * @param sheet 要设置的sheet. + * @param textlist 下拉框显示的内容 + * @param promptContent 提示内容 + * @param firstRow 开始行 + * @param endRow 结束行 + * @param firstCol 开始列 + * @param endCol 结束列 + */ + public void setXSSFValidationWithHidden(Sheet sheet, String[] textlist, String promptContent, int firstRow, int endRow, int firstCol, int endCol) + { + String hideSheetName = "combo_" + firstCol + "_" + endCol; + Sheet hideSheet = wb.createSheet(hideSheetName); // 用于存储 下拉菜单数据 + for (int i = 0; i < textlist.length; i++) + { + hideSheet.createRow(i).createCell(0).setCellValue(textlist[i]); + } + // 创建名称,可被其他单元格引用 + Name name = wb.createName(); + name.setNameName(hideSheetName + "_data"); + name.setRefersToFormula(hideSheetName + "!$A$1:$A$" + textlist.length); + DataValidationHelper helper = sheet.getDataValidationHelper(); + // 加载下拉列表内容 + DataValidationConstraint constraint = helper.createFormulaListConstraint(hideSheetName + "_data"); + // 设置数据有效性加载在哪个单元格上,四个参数分别是:起始行、终止行、起始列、终止列 + CellRangeAddressList regions = new CellRangeAddressList(firstRow, endRow, firstCol, endCol); + // 数据有效性对象 + DataValidation dataValidation = helper.createValidation(constraint, regions); + if (StringUtils.isNotEmpty(promptContent)) + { + // 如果设置了提示信息则鼠标放上去提示 + dataValidation.createPromptBox("", promptContent); + dataValidation.setShowPromptBox(true); + } + // 处理Excel兼容性问题 + if (dataValidation instanceof XSSFDataValidation) + { + dataValidation.setSuppressDropDownArrow(true); + dataValidation.setShowErrorBox(true); + } + else + { + dataValidation.setSuppressDropDownArrow(false); + } + + sheet.addValidationData(dataValidation); + // 设置hiddenSheet隐藏 + wb.setSheetHidden(wb.getSheetIndex(hideSheet), true); + } + + /** + * 解析导出值 0=男,1=女,2=未知 + * + * @param propertyValue 参数值 + * @param converterExp 翻译注解 + * @param separator 分隔符 + * @return 解析后值 + */ + public static String convertByExp(String propertyValue, String converterExp, String separator) + { + StringBuilder propertyString = new StringBuilder(); + String[] convertSource = converterExp.split(","); + for (String item : convertSource) + { + String[] itemArray = item.split("="); + if (StringUtils.containsAny(propertyValue, separator)) + { + for (String value : propertyValue.split(separator)) + { + if (itemArray[0].equals(value)) + { + propertyString.append(itemArray[1] + separator); + break; + } + } + } + else + { + if (itemArray[0].equals(propertyValue)) + { + return itemArray[1]; + } + } + } + return StringUtils.stripEnd(propertyString.toString(), separator); + } + + /** + * 反向解析值 男=0,女=1,未知=2 + * + * @param propertyValue 参数值 + * @param converterExp 翻译注解 + * @param separator 分隔符 + * @return 解析后值 + */ + public static String reverseByExp(String propertyValue, String converterExp, String separator) + { + StringBuilder propertyString = new StringBuilder(); + String[] convertSource = converterExp.split(","); + for (String item : convertSource) + { + String[] itemArray = item.split("="); + if (StringUtils.containsAny(propertyValue, separator)) + { + for (String value : propertyValue.split(separator)) + { + if (itemArray[1].equals(value)) + { + propertyString.append(itemArray[0] + separator); + break; + } + } + } + else + { + if (itemArray[1].equals(propertyValue)) + { + return itemArray[0]; + } + } + } + return StringUtils.stripEnd(propertyString.toString(), separator); + } + + /** + * 解析字典值 + * + * @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); + } + + /** + * 数据处理器 + * + * @param value 数据值 + * @param excel 数据注解 + * @return + */ + public String dataFormatHandlerAdapter(Object value, Excel excel, Cell cell) + { + try + { + Object instance = excel.handler().newInstance(); + Method formatMethod = excel.handler().getMethod("format", new Class[] { Object.class, String[].class, Cell.class, Workbook.class }); + value = formatMethod.invoke(instance, value, excel.args(), cell, this.wb); + } + catch (Exception e) + { + log.error("不能格式化数据 " + excel.handler(), e.getMessage()); + } + return Convert.toStr(value); + } + + /** + * 合计统计信息 + */ + 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) + { + Row row = sheet.createRow(sheet.getLastRowNum() + 1); + Set keys = statistics.keySet(); + Cell 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() + "_" + 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 (StringUtils.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 (StringUtils.isNotNull(o) && StringUtils.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 = getFields(); + this.fields = this.fields.stream().sorted(Comparator.comparing(objects -> ((Excel) objects[1]).sort())).collect(Collectors.toList()); + this.maxHeight = getRowHeight(); + } + + /** + * 获取字段注解信息 + */ + public List getFields() + { + List fields = new ArrayList(); + List tempFields = new ArrayList<>(); + tempFields.addAll(Arrays.asList(clazz.getSuperclass().getDeclaredFields())); + tempFields.addAll(Arrays.asList(clazz.getDeclaredFields())); + for (Field field : tempFields) + { + if (!ArrayUtils.contains(this.excludeFields, field.getName())) + { + // 单注解 + if (field.isAnnotationPresent(Excel.class)) + { + Excel attr = field.getAnnotation(Excel.class); + if (attr != null && (attr.type() == Type.ALL || attr.type() == type)) + { + field.setAccessible(true); + fields.add(new Object[] { field, attr }); + } + if (Collection.class.isAssignableFrom(field.getType())) + { + subMethod = getSubMethod(field.getName(), clazz); + ParameterizedType pt = (ParameterizedType) field.getGenericType(); + Class subClass = (Class) pt.getActualTypeArguments()[0]; + this.subFields = FieldUtils.getFieldsListWithAnnotation(subClass, Excel.class); + } + } + + // 多注解 + if (field.isAnnotationPresent(Excels.class)) + { + Excels attrs = field.getAnnotation(Excels.class); + Excel[] excels = attrs.value(); + for (Excel attr : excels) + { + if (!ArrayUtils.contains(this.excludeFields, field.getName() + "." + attr.targetAttr()) + && (attr != null && (attr.type() == Type.ALL || attr.type() == type))) + { + field.setAccessible(true); + fields.add(new Object[] { field, attr }); + } + } + } + } + } + return fields; + } + + /** + * 根据注解获取最大行高 + */ + public short getRowHeight() + { + double maxHeight = 0; + for (Object[] os : this.fields) + { + Excel excel = (Excel) os[1]; + maxHeight = Math.max(maxHeight, excel.height()); + } + return (short) (maxHeight * 20); + } + + /** + * 创建一个工作簿 + */ + public void createWorkbook() + { + this.wb = new SXSSFWorkbook(500); + this.sheet = wb.createSheet(); + wb.setSheetName(0, sheetName); + this.styles = createStyles(wb); + } + + /** + * 创建工作表 + * + * @param sheetNo sheet数量 + * @param index 序号 + */ + public void createSheet(int sheetNo, int index) + { + // 设置工作表的名称. + if (sheetNo > 1 && index > 0) + { + this.sheet = wb.createSheet(); + this.createTitle(); + 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 (StringUtils.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; + } + + /** + * 判断是否是空行 + * + * @param row 判断的行 + * @return + */ + private boolean isRowEmpty(Row row) + { + if (row == null) + { + return true; + } + for (int i = row.getFirstCellNum(); i < row.getLastCellNum(); i++) + { + Cell cell = row.getCell(i); + if (cell != null && cell.getCellType() != CellType.BLANK) + { + return false; + } + } + return true; + } + + /** + * 获取Excel2003图片 + * + * @param sheet 当前sheet对象 + * @param workbook 工作簿对象 + * @return Map key:图片单元格索引(1_1)String,value:图片流PictureData + */ + public static Map getSheetPictures03(HSSFSheet sheet, HSSFWorkbook workbook) + { + Map sheetIndexPicMap = new HashMap(); + List pictures = workbook.getAllPictures(); + if (!pictures.isEmpty()) + { + for (HSSFShape shape : sheet.getDrawingPatriarch().getChildren()) + { + HSSFClientAnchor anchor = (HSSFClientAnchor) shape.getAnchor(); + if (shape instanceof HSSFPicture) + { + HSSFPicture pic = (HSSFPicture) shape; + int pictureIndex = pic.getPictureIndex() - 1; + HSSFPictureData picData = pictures.get(pictureIndex); + String picIndex = anchor.getRow1() + "_" + anchor.getCol1(); + sheetIndexPicMap.put(picIndex, picData); + } + } + return sheetIndexPicMap; + } + else + { + return sheetIndexPicMap; + } + } + + /** + * 获取Excel2007图片 + * + * @param sheet 当前sheet对象 + * @param workbook 工作簿对象 + * @return Map key:图片单元格索引(1_1)String,value:图片流PictureData + */ + public static Map getSheetPictures07(XSSFSheet sheet, XSSFWorkbook workbook) + { + Map sheetIndexPicMap = new HashMap(); + for (POIXMLDocumentPart dr : sheet.getRelations()) + { + if (dr instanceof XSSFDrawing) + { + XSSFDrawing drawing = (XSSFDrawing) dr; + List shapes = drawing.getShapes(); + for (XSSFShape shape : shapes) + { + if (shape instanceof XSSFPicture) + { + XSSFPicture pic = (XSSFPicture) shape; + XSSFClientAnchor anchor = pic.getPreferredSize(); + CTMarker ctMarker = anchor.getFrom(); + String picIndex = ctMarker.getRow() + "_" + ctMarker.getCol(); + sheetIndexPicMap.put(picIndex, pic.getPictureData()); + } + } + } + } + return sheetIndexPicMap; + } + + /** + * 格式化不同类型的日期对象 + * + * @param dateFormat 日期格式 + * @param val 被格式化的日期对象 + * @return 格式化后的日期字符 + */ + public String parseDateToStr(String dateFormat, Object val) + { + if (val == null) + { + return ""; + } + String str; + if (val instanceof Date) + { + str = DateUtils.parseDateToStr(dateFormat, (Date) val); + } + else if (val instanceof LocalDateTime) + { + str = DateUtils.parseDateToStr(dateFormat, DateUtils.toDate((LocalDateTime) val)); + } + else if (val instanceof LocalDate) + { + str = DateUtils.parseDateToStr(dateFormat, DateUtils.toDate((LocalDate) val)); + } + else + { + str = val.toString(); + } + return str; + } + + /** + * 是否有对象的子列表 + */ + public boolean isSubList() + { + return StringUtils.isNotNull(subFields) && subFields.size() > 0; + } + + /** + * 是否有对象的子列表,集合不为空 + */ + public boolean isSubListValue(T vo) + { + return StringUtils.isNotNull(subFields) && subFields.size() > 0 && StringUtils.isNotNull(getListCellValue(vo)) && getListCellValue(vo).size() > 0; + } + + /** + * 获取集合的值 + */ + public Collection getListCellValue(Object obj) + { + Object value; + try + { + value = subMethod.invoke(obj, new Object[] {}); + } + catch (Exception e) + { + return new ArrayList(); + } + return (Collection) value; + } + + /** + * 获取对象的子列表方法 + * + * @param name 名称 + * @param pojoClass 类对象 + * @return 子列表方法 + */ + public Method getSubMethod(String name, Class pojoClass) + { + StringBuffer getMethodName = new StringBuffer("get"); + getMethodName.append(name.substring(0, 1).toUpperCase()); + getMethodName.append(name.substring(1)); + Method method = null; + try + { + method = pojoClass.getMethod(getMethodName.toString(), new Class[] {}); + } + catch (Exception e) + { + log.error("获取对象异常{}", e.getMessage()); + } + return method; + } +} diff --git a/src/main/java/com/ruoyi/common/utils/reflect/ReflectUtils.java b/src/main/java/com/ruoyi/common/utils/reflect/ReflectUtils.java new file mode 100644 index 0000000..b19953e --- /dev/null +++ b/src/main/java/com/ruoyi/common/utils/reflect/ReflectUtils.java @@ -0,0 +1,410 @@ +package com.ruoyi.common.utils.reflect; + +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.util.Date; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.Validate; +import org.apache.poi.ss.usermodel.DateUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import com.ruoyi.common.core.text.Convert; +import com.ruoyi.common.utils.DateUtils; + +/** + * 反射工具类. 提供调用getter/setter方法, 访问私有变量, 调用私有方法, 获取泛型类型Class, 被AOP过的真实类等工具函数. + * + * @author ruoyi + */ +@SuppressWarnings("rawtypes") +public class ReflectUtils +{ + private static final String SETTER_PREFIX = "set"; + + private static final String GETTER_PREFIX = "get"; + + private static final String CGLIB_CLASS_SEPARATOR = "$$"; + + private static Logger logger = LoggerFactory.getLogger(ReflectUtils.class); + + /** + * 调用Getter方法. + * 支持多级,如:对象名.对象名.方法 + */ + @SuppressWarnings("unchecked") + public static E invokeGetter(Object obj, String propertyName) + { + Object object = obj; + for (String name : StringUtils.split(propertyName, ".")) + { + String getterMethodName = GETTER_PREFIX + StringUtils.capitalize(name); + object = invokeMethod(object, getterMethodName, new Class[] {}, new Object[] {}); + } + return (E) object; + } + + /** + * 调用Setter方法, 仅匹配方法名。 + * 支持多级,如:对象名.对象名.方法 + */ + public static void invokeSetter(Object obj, String propertyName, E value) + { + Object object = obj; + String[] names = StringUtils.split(propertyName, "."); + for (int i = 0; i < names.length; i++) + { + if (i < names.length - 1) + { + String getterMethodName = GETTER_PREFIX + StringUtils.capitalize(names[i]); + object = invokeMethod(object, getterMethodName, new Class[] {}, new Object[] {}); + } + else + { + String setterMethodName = SETTER_PREFIX + StringUtils.capitalize(names[i]); + invokeMethodByName(object, setterMethodName, new Object[] { value }); + } + } + } + + /** + * 直接读取对象属性值, 无视private/protected修饰符, 不经过getter函数. + */ + @SuppressWarnings("unchecked") + public static E getFieldValue(final Object obj, final String fieldName) + { + Field field = getAccessibleField(obj, fieldName); + if (field == null) + { + logger.debug("在 [" + obj.getClass() + "] 中,没有找到 [" + fieldName + "] 字段 "); + return null; + } + E result = null; + try + { + result = (E) field.get(obj); + } + catch (IllegalAccessException e) + { + logger.error("不可能抛出的异常{}", e.getMessage()); + } + return result; + } + + /** + * 直接设置对象属性值, 无视private/protected修饰符, 不经过setter函数. + */ + public static void setFieldValue(final Object obj, final String fieldName, final E value) + { + Field field = getAccessibleField(obj, fieldName); + if (field == null) + { + // throw new IllegalArgumentException("在 [" + obj.getClass() + "] 中,没有找到 [" + fieldName + "] 字段 "); + logger.debug("在 [" + obj.getClass() + "] 中,没有找到 [" + fieldName + "] 字段 "); + return; + } + try + { + field.set(obj, value); + } + catch (IllegalAccessException e) + { + logger.error("不可能抛出的异常: {}", e.getMessage()); + } + } + + /** + * 直接调用对象方法, 无视private/protected修饰符. + * 用于一次性调用的情况,否则应使用getAccessibleMethod()函数获得Method后反复调用. + * 同时匹配方法名+参数类型, + */ + @SuppressWarnings("unchecked") + public static E invokeMethod(final Object obj, final String methodName, final Class[] parameterTypes, + final Object[] args) + { + if (obj == null || methodName == null) + { + return null; + } + Method method = getAccessibleMethod(obj, methodName, parameterTypes); + if (method == null) + { + logger.debug("在 [" + obj.getClass() + "] 中,没有找到 [" + methodName + "] 方法 "); + return null; + } + try + { + return (E) method.invoke(obj, args); + } + catch (Exception e) + { + String msg = "method: " + method + ", obj: " + obj + ", args: " + args + ""; + throw convertReflectionExceptionToUnchecked(msg, e); + } + } + + /** + * 直接调用对象方法, 无视private/protected修饰符, + * 用于一次性调用的情况,否则应使用getAccessibleMethodByName()函数获得Method后反复调用. + * 只匹配函数名,如果有多个同名函数调用第一个。 + */ + @SuppressWarnings("unchecked") + public static E invokeMethodByName(final Object obj, final String methodName, final Object[] args) + { + Method method = getAccessibleMethodByName(obj, methodName, args.length); + if (method == null) + { + // 如果为空不报错,直接返回空。 + logger.debug("在 [" + obj.getClass() + "] 中,没有找到 [" + methodName + "] 方法 "); + return null; + } + try + { + // 类型转换(将参数数据类型转换为目标方法参数类型) + Class[] cs = method.getParameterTypes(); + for (int i = 0; i < cs.length; i++) + { + if (args[i] != null && !args[i].getClass().equals(cs[i])) + { + if (cs[i] == String.class) + { + args[i] = Convert.toStr(args[i]); + if (StringUtils.endsWith((String) args[i], ".0")) + { + args[i] = StringUtils.substringBefore((String) args[i], ".0"); + } + } + else if (cs[i] == Integer.class) + { + args[i] = Convert.toInt(args[i]); + } + else if (cs[i] == Long.class) + { + args[i] = Convert.toLong(args[i]); + } + else if (cs[i] == Double.class) + { + args[i] = Convert.toDouble(args[i]); + } + else if (cs[i] == Float.class) + { + args[i] = Convert.toFloat(args[i]); + } + else if (cs[i] == Date.class) + { + if (args[i] instanceof String) + { + args[i] = DateUtils.parseDate(args[i]); + } + else + { + args[i] = DateUtil.getJavaDate((Double) args[i]); + } + } + else if (cs[i] == boolean.class || cs[i] == Boolean.class) + { + args[i] = Convert.toBool(args[i]); + } + } + } + return (E) method.invoke(obj, args); + } + catch (Exception e) + { + String msg = "method: " + method + ", obj: " + obj + ", args: " + args + ""; + throw convertReflectionExceptionToUnchecked(msg, e); + } + } + + /** + * 循环向上转型, 获取对象的DeclaredField, 并强制设置为可访问. + * 如向上转型到Object仍无法找到, 返回null. + */ + public static Field getAccessibleField(final Object obj, final String fieldName) + { + // 为空不报错。直接返回 null + if (obj == null) + { + return null; + } + Validate.notBlank(fieldName, "fieldName can't be blank"); + for (Class superClass = obj.getClass(); superClass != Object.class; superClass = superClass.getSuperclass()) + { + try + { + Field field = superClass.getDeclaredField(fieldName); + makeAccessible(field); + return field; + } + catch (NoSuchFieldException e) + { + continue; + } + } + return null; + } + + /** + * 循环向上转型, 获取对象的DeclaredMethod,并强制设置为可访问. + * 如向上转型到Object仍无法找到, 返回null. + * 匹配函数名+参数类型。 + * 用于方法需要被多次调用的情况. 先使用本函数先取得Method,然后调用Method.invoke(Object obj, Object... args) + */ + public static Method getAccessibleMethod(final Object obj, final String methodName, + final Class... parameterTypes) + { + // 为空不报错。直接返回 null + if (obj == null) + { + return null; + } + Validate.notBlank(methodName, "methodName can't be blank"); + for (Class searchType = obj.getClass(); searchType != Object.class; searchType = searchType.getSuperclass()) + { + try + { + Method method = searchType.getDeclaredMethod(methodName, parameterTypes); + makeAccessible(method); + return method; + } + catch (NoSuchMethodException e) + { + continue; + } + } + return null; + } + + /** + * 循环向上转型, 获取对象的DeclaredMethod,并强制设置为可访问. + * 如向上转型到Object仍无法找到, 返回null. + * 只匹配函数名。 + * 用于方法需要被多次调用的情况. 先使用本函数先取得Method,然后调用Method.invoke(Object obj, Object... args) + */ + public static Method getAccessibleMethodByName(final Object obj, final String methodName, int argsNum) + { + // 为空不报错。直接返回 null + if (obj == null) + { + return null; + } + Validate.notBlank(methodName, "methodName can't be blank"); + for (Class searchType = obj.getClass(); searchType != Object.class; searchType = searchType.getSuperclass()) + { + Method[] methods = searchType.getDeclaredMethods(); + for (Method method : methods) + { + if (method.getName().equals(methodName) && method.getParameterTypes().length == argsNum) + { + makeAccessible(method); + return method; + } + } + } + return null; + } + + /** + * 改变private/protected的方法为public,尽量不调用实际改动的语句,避免JDK的SecurityManager抱怨。 + */ + public static void makeAccessible(Method method) + { + if ((!Modifier.isPublic(method.getModifiers()) || !Modifier.isPublic(method.getDeclaringClass().getModifiers())) + && !method.isAccessible()) + { + method.setAccessible(true); + } + } + + /** + * 改变private/protected的成员变量为public,尽量不调用实际改动的语句,避免JDK的SecurityManager抱怨。 + */ + public static void makeAccessible(Field field) + { + if ((!Modifier.isPublic(field.getModifiers()) || !Modifier.isPublic(field.getDeclaringClass().getModifiers()) + || Modifier.isFinal(field.getModifiers())) && !field.isAccessible()) + { + field.setAccessible(true); + } + } + + /** + * 通过反射, 获得Class定义中声明的泛型参数的类型, 注意泛型必须定义在父类处 + * 如无法找到, 返回Object.class. + */ + @SuppressWarnings("unchecked") + public static Class getClassGenricType(final Class clazz) + { + return getClassGenricType(clazz, 0); + } + + /** + * 通过反射, 获得Class定义中声明的父类的泛型参数的类型. + * 如无法找到, 返回Object.class. + */ + public static Class getClassGenricType(final Class clazz, final int index) + { + Type genType = clazz.getGenericSuperclass(); + + if (!(genType instanceof ParameterizedType)) + { + logger.debug(clazz.getSimpleName() + "'s superclass not ParameterizedType"); + return Object.class; + } + + Type[] params = ((ParameterizedType) genType).getActualTypeArguments(); + + if (index >= params.length || index < 0) + { + logger.debug("Index: " + index + ", Size of " + clazz.getSimpleName() + "'s Parameterized Type: " + + params.length); + return Object.class; + } + if (!(params[index] instanceof Class)) + { + logger.debug(clazz.getSimpleName() + " not set the actual class on superclass generic parameter"); + return Object.class; + } + + return (Class) params[index]; + } + + public static Class getUserClass(Object instance) + { + if (instance == null) + { + throw new RuntimeException("Instance must not be null"); + } + Class clazz = instance.getClass(); + if (clazz != null && clazz.getName().contains(CGLIB_CLASS_SEPARATOR)) + { + Class superClass = clazz.getSuperclass(); + if (superClass != null && !Object.class.equals(superClass)) + { + return superClass; + } + } + return clazz; + + } + + /** + * 将反射时的checked exception转换为unchecked exception. + */ + public static RuntimeException convertReflectionExceptionToUnchecked(String msg, Exception e) + { + if (e instanceof IllegalAccessException || e instanceof IllegalArgumentException + || e instanceof NoSuchMethodException) + { + return new IllegalArgumentException(msg, e); + } + else if (e instanceof InvocationTargetException) + { + return new RuntimeException(msg, ((InvocationTargetException) e).getTargetException()); + } + return new RuntimeException(msg, e); + } +} diff --git a/src/main/java/com/ruoyi/common/utils/sign/Base64.java b/src/main/java/com/ruoyi/common/utils/sign/Base64.java new file mode 100644 index 0000000..ca1cd92 --- /dev/null +++ b/src/main/java/com/ruoyi/common/utils/sign/Base64.java @@ -0,0 +1,291 @@ +package com.ruoyi.common.utils.sign; + +/** + * Base64工具类 + * + * @author ruoyi + */ +public final class Base64 +{ + static private final int BASELENGTH = 128; + static private final int LOOKUPLENGTH = 64; + static private final int TWENTYFOURBITGROUP = 24; + static private final int EIGHTBIT = 8; + static private final int SIXTEENBIT = 16; + static private final int FOURBYTE = 4; + static private final int SIGN = -128; + static private final char PAD = '='; + static final private byte[] base64Alphabet = new byte[BASELENGTH]; + static final private char[] lookUpBase64Alphabet = new char[LOOKUPLENGTH]; + + static + { + for (int i = 0; i < BASELENGTH; ++i) + { + base64Alphabet[i] = -1; + } + for (int i = 'Z'; i >= 'A'; i--) + { + base64Alphabet[i] = (byte) (i - 'A'); + } + for (int i = 'z'; i >= 'a'; i--) + { + base64Alphabet[i] = (byte) (i - 'a' + 26); + } + + for (int i = '9'; i >= '0'; i--) + { + base64Alphabet[i] = (byte) (i - '0' + 52); + } + + base64Alphabet['+'] = 62; + base64Alphabet['/'] = 63; + + for (int i = 0; i <= 25; i++) + { + lookUpBase64Alphabet[i] = (char) ('A' + i); + } + + for (int i = 26, j = 0; i <= 51; i++, j++) + { + lookUpBase64Alphabet[i] = (char) ('a' + j); + } + + for (int i = 52, j = 0; i <= 61; i++, j++) + { + lookUpBase64Alphabet[i] = (char) ('0' + j); + } + lookUpBase64Alphabet[62] = (char) '+'; + lookUpBase64Alphabet[63] = (char) '/'; + } + + private static boolean isWhiteSpace(char octect) + { + return (octect == 0x20 || octect == 0xd || octect == 0xa || octect == 0x9); + } + + private static boolean isPad(char octect) + { + return (octect == PAD); + } + + private static boolean isData(char octect) + { + return (octect < BASELENGTH && base64Alphabet[octect] != -1); + } + + /** + * Encodes hex octects into Base64 + * + * @param binaryData Array containing binaryData + * @return Encoded Base64 array + */ + public static String encode(byte[] binaryData) + { + if (binaryData == null) + { + return null; + } + + int lengthDataBits = binaryData.length * EIGHTBIT; + if (lengthDataBits == 0) + { + return ""; + } + + int fewerThan24bits = lengthDataBits % TWENTYFOURBITGROUP; + int numberTriplets = lengthDataBits / TWENTYFOURBITGROUP; + int numberQuartet = fewerThan24bits != 0 ? numberTriplets + 1 : numberTriplets; + char encodedData[] = null; + + encodedData = new char[numberQuartet * 4]; + + byte k = 0, l = 0, b1 = 0, b2 = 0, b3 = 0; + + int encodedIndex = 0; + int dataIndex = 0; + + for (int i = 0; i < numberTriplets; i++) + { + b1 = binaryData[dataIndex++]; + b2 = binaryData[dataIndex++]; + b3 = binaryData[dataIndex++]; + + l = (byte) (b2 & 0x0f); + k = (byte) (b1 & 0x03); + + byte val1 = ((b1 & SIGN) == 0) ? (byte) (b1 >> 2) : (byte) ((b1) >> 2 ^ 0xc0); + byte val2 = ((b2 & SIGN) == 0) ? (byte) (b2 >> 4) : (byte) ((b2) >> 4 ^ 0xf0); + byte val3 = ((b3 & SIGN) == 0) ? (byte) (b3 >> 6) : (byte) ((b3) >> 6 ^ 0xfc); + + encodedData[encodedIndex++] = lookUpBase64Alphabet[val1]; + encodedData[encodedIndex++] = lookUpBase64Alphabet[val2 | (k << 4)]; + encodedData[encodedIndex++] = lookUpBase64Alphabet[(l << 2) | val3]; + encodedData[encodedIndex++] = lookUpBase64Alphabet[b3 & 0x3f]; + } + + // form integral number of 6-bit groups + if (fewerThan24bits == EIGHTBIT) + { + b1 = binaryData[dataIndex]; + k = (byte) (b1 & 0x03); + byte val1 = ((b1 & SIGN) == 0) ? (byte) (b1 >> 2) : (byte) ((b1) >> 2 ^ 0xc0); + encodedData[encodedIndex++] = lookUpBase64Alphabet[val1]; + encodedData[encodedIndex++] = lookUpBase64Alphabet[k << 4]; + encodedData[encodedIndex++] = PAD; + encodedData[encodedIndex++] = PAD; + } + else if (fewerThan24bits == SIXTEENBIT) + { + b1 = binaryData[dataIndex]; + b2 = binaryData[dataIndex + 1]; + l = (byte) (b2 & 0x0f); + k = (byte) (b1 & 0x03); + + byte val1 = ((b1 & SIGN) == 0) ? (byte) (b1 >> 2) : (byte) ((b1) >> 2 ^ 0xc0); + byte val2 = ((b2 & SIGN) == 0) ? (byte) (b2 >> 4) : (byte) ((b2) >> 4 ^ 0xf0); + + encodedData[encodedIndex++] = lookUpBase64Alphabet[val1]; + encodedData[encodedIndex++] = lookUpBase64Alphabet[val2 | (k << 4)]; + encodedData[encodedIndex++] = lookUpBase64Alphabet[l << 2]; + encodedData[encodedIndex++] = PAD; + } + return new String(encodedData); + } + + /** + * Decodes Base64 data into octects + * + * @param encoded string containing Base64 data + * @return Array containind decoded data. + */ + public static byte[] decode(String encoded) + { + if (encoded == null) + { + return null; + } + + char[] base64Data = encoded.toCharArray(); + // remove white spaces + int len = removeWhiteSpace(base64Data); + + if (len % FOURBYTE != 0) + { + return null;// should be divisible by four + } + + int numberQuadruple = (len / FOURBYTE); + + if (numberQuadruple == 0) + { + return new byte[0]; + } + + byte decodedData[] = null; + byte b1 = 0, b2 = 0, b3 = 0, b4 = 0; + char d1 = 0, d2 = 0, d3 = 0, d4 = 0; + + int i = 0; + int encodedIndex = 0; + int dataIndex = 0; + decodedData = new byte[(numberQuadruple) * 3]; + + for (; i < numberQuadruple - 1; i++) + { + + if (!isData((d1 = base64Data[dataIndex++])) || !isData((d2 = base64Data[dataIndex++])) + || !isData((d3 = base64Data[dataIndex++])) || !isData((d4 = base64Data[dataIndex++]))) + { + return null; + } // if found "no data" just return null + + b1 = base64Alphabet[d1]; + b2 = base64Alphabet[d2]; + b3 = base64Alphabet[d3]; + b4 = base64Alphabet[d4]; + + decodedData[encodedIndex++] = (byte) (b1 << 2 | b2 >> 4); + decodedData[encodedIndex++] = (byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf)); + decodedData[encodedIndex++] = (byte) (b3 << 6 | b4); + } + + if (!isData((d1 = base64Data[dataIndex++])) || !isData((d2 = base64Data[dataIndex++]))) + { + return null;// if found "no data" just return null + } + + b1 = base64Alphabet[d1]; + b2 = base64Alphabet[d2]; + + d3 = base64Data[dataIndex++]; + d4 = base64Data[dataIndex++]; + if (!isData((d3)) || !isData((d4))) + {// Check if they are PAD characters + if (isPad(d3) && isPad(d4)) + { + if ((b2 & 0xf) != 0)// last 4 bits should be zero + { + return null; + } + byte[] tmp = new byte[i * 3 + 1]; + System.arraycopy(decodedData, 0, tmp, 0, i * 3); + tmp[encodedIndex] = (byte) (b1 << 2 | b2 >> 4); + return tmp; + } + else if (!isPad(d3) && isPad(d4)) + { + b3 = base64Alphabet[d3]; + if ((b3 & 0x3) != 0)// last 2 bits should be zero + { + return null; + } + byte[] tmp = new byte[i * 3 + 2]; + System.arraycopy(decodedData, 0, tmp, 0, i * 3); + tmp[encodedIndex++] = (byte) (b1 << 2 | b2 >> 4); + tmp[encodedIndex] = (byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf)); + return tmp; + } + else + { + return null; + } + } + else + { // No PAD e.g 3cQl + b3 = base64Alphabet[d3]; + b4 = base64Alphabet[d4]; + decodedData[encodedIndex++] = (byte) (b1 << 2 | b2 >> 4); + decodedData[encodedIndex++] = (byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf)); + decodedData[encodedIndex++] = (byte) (b3 << 6 | b4); + + } + return decodedData; + } + + /** + * remove WhiteSpace from MIME containing encoded Base64 data. + * + * @param data the byte array of base64 data (with WS) + * @return the new length + */ + private static int removeWhiteSpace(char[] data) + { + if (data == null) + { + return 0; + } + + // count characters that's not whitespace + int newSize = 0; + int len = data.length; + for (int i = 0; i < len; i++) + { + if (!isWhiteSpace(data[i])) + { + data[newSize++] = data[i]; + } + } + return newSize; + } +} diff --git a/src/main/java/com/ruoyi/common/utils/sign/Md5Utils.java b/src/main/java/com/ruoyi/common/utils/sign/Md5Utils.java new file mode 100644 index 0000000..c1c58db --- /dev/null +++ b/src/main/java/com/ruoyi/common/utils/sign/Md5Utils.java @@ -0,0 +1,67 @@ +package com.ruoyi.common.utils.sign; + +import java.nio.charset.StandardCharsets; +import java.security.MessageDigest; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Md5加密方法 + * + * @author ruoyi + */ +public class Md5Utils +{ + private static final Logger log = LoggerFactory.getLogger(Md5Utils.class); + + private static byte[] md5(String s) + { + MessageDigest algorithm; + try + { + algorithm = MessageDigest.getInstance("MD5"); + algorithm.reset(); + algorithm.update(s.getBytes("UTF-8")); + byte[] messageDigest = algorithm.digest(); + return messageDigest; + } + catch (Exception e) + { + log.error("MD5 Error...", e); + } + return null; + } + + private static final String toHex(byte hash[]) + { + if (hash == null) + { + return null; + } + StringBuffer buf = new StringBuffer(hash.length * 2); + int i; + + for (i = 0; i < hash.length; i++) + { + if ((hash[i] & 0xff) < 0x10) + { + buf.append("0"); + } + buf.append(Long.toString(hash[i] & 0xff, 16)); + } + return buf.toString(); + } + + public static String hash(String s) + { + try + { + return new String(toHex(md5(s)).getBytes(StandardCharsets.UTF_8), StandardCharsets.UTF_8); + } + catch (Exception e) + { + log.error("not supported charset...{}", e); + return s; + } + } +} diff --git a/src/main/java/com/ruoyi/common/utils/spring/SpringUtils.java b/src/main/java/com/ruoyi/common/utils/spring/SpringUtils.java new file mode 100644 index 0000000..f290ec3 --- /dev/null +++ b/src/main/java/com/ruoyi/common/utils/spring/SpringUtils.java @@ -0,0 +1,158 @@ +package com.ruoyi.common.utils.spring; + +import org.springframework.aop.framework.AopContext; +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.NoSuchBeanDefinitionException; +import org.springframework.beans.factory.config.BeanFactoryPostProcessor; +import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; +import org.springframework.stereotype.Component; +import com.ruoyi.common.utils.StringUtils; + +/** + * spring工具类 方便在非spring管理环境中获取bean + * + * @author ruoyi + */ +@Component +public final class SpringUtils implements BeanFactoryPostProcessor, ApplicationContextAware +{ + /** Spring应用上下文环境 */ + private static ConfigurableListableBeanFactory beanFactory; + + private static ApplicationContext applicationContext; + + @Override + public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException + { + SpringUtils.beanFactory = beanFactory; + } + + @Override + public void setApplicationContext(ApplicationContext applicationContext) throws BeansException + { + SpringUtils.applicationContext = applicationContext; + } + + /** + * 获取对象 + * + * @param name + * @return Object 一个以所给名字注册的bean的实例 + * @throws org.springframework.beans.BeansException + * + */ + @SuppressWarnings("unchecked") + public static T getBean(String name) throws BeansException + { + return (T) beanFactory.getBean(name); + } + + /** + * 获取类型为requiredType的对象 + * + * @param clz + * @return + * @throws org.springframework.beans.BeansException + * + */ + public static T getBean(Class clz) throws BeansException + { + T result = (T) beanFactory.getBean(clz); + return result; + } + + /** + * 如果BeanFactory包含一个与所给名称匹配的bean定义,则返回true + * + * @param name + * @return boolean + */ + public static boolean containsBean(String name) + { + return beanFactory.containsBean(name); + } + + /** + * 判断以给定名字注册的bean定义是一个singleton还是一个prototype。 如果与给定名字相应的bean定义没有被找到,将会抛出一个异常(NoSuchBeanDefinitionException) + * + * @param name + * @return boolean + * @throws org.springframework.beans.factory.NoSuchBeanDefinitionException + * + */ + public static boolean isSingleton(String name) throws NoSuchBeanDefinitionException + { + return beanFactory.isSingleton(name); + } + + /** + * @param name + * @return Class 注册对象的类型 + * @throws org.springframework.beans.factory.NoSuchBeanDefinitionException + * + */ + public static Class getType(String name) throws NoSuchBeanDefinitionException + { + return beanFactory.getType(name); + } + + /** + * 如果给定的bean名字在bean定义中有别名,则返回这些别名 + * + * @param name + * @return + * @throws org.springframework.beans.factory.NoSuchBeanDefinitionException + * + */ + public static String[] getAliases(String name) throws NoSuchBeanDefinitionException + { + return beanFactory.getAliases(name); + } + + /** + * 获取aop代理对象 + * + * @param invoker + * @return + */ + @SuppressWarnings("unchecked") + public static T getAopProxy(T invoker) + { + return (T) AopContext.currentProxy(); + } + + /** + * 获取当前的环境配置,无配置返回null + * + * @return 当前的环境配置 + */ + public static String[] getActiveProfiles() + { + return applicationContext.getEnvironment().getActiveProfiles(); + } + + /** + * 获取当前的环境配置,当有多个环境配置时,只获取第一个 + * + * @return 当前的环境配置 + */ + public static String getActiveProfile() + { + final String[] activeProfiles = getActiveProfiles(); + return StringUtils.isNotEmpty(activeProfiles) ? activeProfiles[0] : null; + } + + /** + * 获取配置文件中的值 + * + * @param key 配置文件的key + * @return 当前的配置文件的值 + * + */ + public static String getRequiredProperty(String key) + { + return applicationContext.getEnvironment().getRequiredProperty(key); + } +} diff --git a/src/main/java/com/ruoyi/common/utils/sql/SqlUtil.java b/src/main/java/com/ruoyi/common/utils/sql/SqlUtil.java new file mode 100644 index 0000000..93b0347 --- /dev/null +++ b/src/main/java/com/ruoyi/common/utils/sql/SqlUtil.java @@ -0,0 +1,70 @@ +package com.ruoyi.common.utils.sql; + +import com.ruoyi.common.exception.UtilException; +import com.ruoyi.common.utils.StringUtils; + +/** + * sql操作工具类 + * + * @author ruoyi + */ +public class SqlUtil +{ + /** + * 定义常用的 sql关键字 + */ + public static String SQL_REGEX = "and |extractvalue|updatexml|exec |insert |select |delete |update |drop |count |chr |mid |master |truncate |char |declare |or |+|user()"; + + /** + * 仅支持字母、数字、下划线、空格、逗号、小数点(支持多个字段排序) + */ + public static String SQL_PATTERN = "[a-zA-Z0-9_\\ \\,\\.]+"; + + /** + * 限制orderBy最大长度 + */ + private static final int ORDER_BY_MAX_LENGTH = 500; + + /** + * 检查字符,防止注入绕过 + */ + public static String escapeOrderBySql(String value) + { + if (StringUtils.isNotEmpty(value) && !isValidOrderBySql(value)) + { + throw new UtilException("参数不符合规范,不能进行查询"); + } + if (StringUtils.length(value) > ORDER_BY_MAX_LENGTH) + { + throw new UtilException("参数已超过最大限制,不能进行查询"); + } + return value; + } + + /** + * 验证 order by 语法是否符合规范 + */ + public static boolean isValidOrderBySql(String value) + { + return value.matches(SQL_PATTERN); + } + + /** + * SQL关键字检查 + */ + public static void filterKeyword(String value) + { + if (StringUtils.isEmpty(value)) + { + return; + } + String[] sqlKeywords = StringUtils.split(SQL_REGEX, "\\|"); + for (String sqlKeyword : sqlKeywords) + { + if (StringUtils.indexOfIgnoreCase(value, sqlKeyword) > -1) + { + throw new UtilException("参数存在SQL注入风险"); + } + } + } +} diff --git a/src/main/java/com/ruoyi/common/utils/uuid/IdUtils.java b/src/main/java/com/ruoyi/common/utils/uuid/IdUtils.java new file mode 100644 index 0000000..2c84427 --- /dev/null +++ b/src/main/java/com/ruoyi/common/utils/uuid/IdUtils.java @@ -0,0 +1,49 @@ +package com.ruoyi.common.utils.uuid; + +/** + * ID生成器工具类 + * + * @author ruoyi + */ +public class IdUtils +{ + /** + * 获取随机UUID + * + * @return 随机UUID + */ + public static String randomUUID() + { + return UUID.randomUUID().toString(); + } + + /** + * 简化的UUID,去掉了横线 + * + * @return 简化的UUID,去掉了横线 + */ + public static String simpleUUID() + { + return UUID.randomUUID().toString(true); + } + + /** + * 获取随机UUID,使用性能更好的ThreadLocalRandom生成UUID + * + * @return 随机UUID + */ + public static String fastUUID() + { + return UUID.fastUUID().toString(); + } + + /** + * 简化的UUID,去掉了横线,使用性能更好的ThreadLocalRandom生成UUID + * + * @return 简化的UUID,去掉了横线 + */ + public static String fastSimpleUUID() + { + return UUID.fastUUID().toString(true); + } +} diff --git a/src/main/java/com/ruoyi/common/utils/uuid/Seq.java b/src/main/java/com/ruoyi/common/utils/uuid/Seq.java new file mode 100644 index 0000000..bf99611 --- /dev/null +++ b/src/main/java/com/ruoyi/common/utils/uuid/Seq.java @@ -0,0 +1,86 @@ +package com.ruoyi.common.utils.uuid; + +import java.util.concurrent.atomic.AtomicInteger; +import com.ruoyi.common.utils.DateUtils; +import com.ruoyi.common.utils.StringUtils; + +/** + * @author ruoyi 序列生成类 + */ +public class Seq +{ + // 通用序列类型 + public static final String commSeqType = "COMMON"; + + // 上传序列类型 + public static final String uploadSeqType = "UPLOAD"; + + // 通用接口序列数 + private static AtomicInteger commSeq = new AtomicInteger(1); + + // 上传接口序列数 + private static AtomicInteger uploadSeq = new AtomicInteger(1); + + // 机器标识 + private static final String machineCode = "A"; + + /** + * 获取通用序列号 + * + * @return 序列值 + */ + public static String getId() + { + return getId(commSeqType); + } + + /** + * 默认16位序列号 yyMMddHHmmss + 一位机器标识 + 3长度循环递增字符串 + * + * @return 序列值 + */ + public static String getId(String type) + { + AtomicInteger atomicInt = commSeq; + if (uploadSeqType.equals(type)) + { + atomicInt = uploadSeq; + } + return getId(atomicInt, 3); + } + + /** + * 通用接口序列号 yyMMddHHmmss + 一位机器标识 + length长度循环递增字符串 + * + * @param atomicInt 序列数 + * @param length 数值长度 + * @return 序列值 + */ + public static String getId(AtomicInteger atomicInt, int length) + { + String result = DateUtils.dateTimeNow(); + result += machineCode; + result += getSeq(atomicInt, length); + return result; + } + + /** + * 序列循环递增字符串[1, 10 的 (length)幂次方), 用0左补齐length位数 + * + * @return 序列值 + */ + private synchronized static String getSeq(AtomicInteger atomicInt, int length) + { + // 先取值再+1 + int value = atomicInt.getAndIncrement(); + + // 如果更新后值>=10 的 (length)幂次方则重置为1 + int maxSeq = (int) Math.pow(10, length); + if (atomicInt.get() >= maxSeq) + { + atomicInt.set(1); + } + // 转字符串,用0左补齐 + return StringUtils.padl(value, length); + } +} diff --git a/src/main/java/com/ruoyi/common/utils/uuid/UUID.java b/src/main/java/com/ruoyi/common/utils/uuid/UUID.java new file mode 100644 index 0000000..acc3a6f --- /dev/null +++ b/src/main/java/com/ruoyi/common/utils/uuid/UUID.java @@ -0,0 +1,484 @@ +package com.ruoyi.common.utils.uuid; + +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; +import java.util.Random; +import java.util.concurrent.ThreadLocalRandom; +import com.ruoyi.common.exception.UtilException; + +/** + * 提供通用唯一识别码(universally unique identifier)(UUID)实现 + * + * @author ruoyi + */ +public final class UUID implements java.io.Serializable, Comparable +{ + private static final long serialVersionUID = -1185015143654744140L; + + /** + * SecureRandom 的单例 + * + */ + private static class Holder + { + static final SecureRandom numberGenerator = getSecureRandom(); + } + + /** 此UUID的最高64有效位 */ + private final long mostSigBits; + + /** 此UUID的最低64有效位 */ + private final long leastSigBits; + + /** + * 私有构造 + * + * @param data 数据 + */ + private UUID(byte[] data) + { + long msb = 0; + long lsb = 0; + assert data.length == 16 : "data must be 16 bytes in length"; + for (int i = 0; i < 8; i++) + { + msb = (msb << 8) | (data[i] & 0xff); + } + for (int i = 8; i < 16; i++) + { + lsb = (lsb << 8) | (data[i] & 0xff); + } + this.mostSigBits = msb; + this.leastSigBits = lsb; + } + + /** + * 使用指定的数据构造新的 UUID。 + * + * @param mostSigBits 用于 {@code UUID} 的最高有效 64 位 + * @param leastSigBits 用于 {@code UUID} 的最低有效 64 位 + */ + public UUID(long mostSigBits, long leastSigBits) + { + this.mostSigBits = mostSigBits; + this.leastSigBits = leastSigBits; + } + + /** + * 获取类型 4(伪随机生成的)UUID 的静态工厂。 + * + * @return 随机生成的 {@code UUID} + */ + public static UUID fastUUID() + { + return randomUUID(false); + } + + /** + * 获取类型 4(伪随机生成的)UUID 的静态工厂。 使用加密的强伪随机数生成器生成该 UUID。 + * + * @return 随机生成的 {@code UUID} + */ + public static UUID randomUUID() + { + return randomUUID(true); + } + + /** + * 获取类型 4(伪随机生成的)UUID 的静态工厂。 使用加密的强伪随机数生成器生成该 UUID。 + * + * @param isSecure 是否使用{@link SecureRandom}如果是可以获得更安全的随机码,否则可以得到更好的性能 + * @return 随机生成的 {@code UUID} + */ + public static UUID randomUUID(boolean isSecure) + { + final Random ng = isSecure ? Holder.numberGenerator : getRandom(); + + byte[] randomBytes = new byte[16]; + ng.nextBytes(randomBytes); + randomBytes[6] &= 0x0f; /* clear version */ + randomBytes[6] |= 0x40; /* set to version 4 */ + randomBytes[8] &= 0x3f; /* clear variant */ + randomBytes[8] |= 0x80; /* set to IETF variant */ + return new UUID(randomBytes); + } + + /** + * 根据指定的字节数组获取类型 3(基于名称的)UUID 的静态工厂。 + * + * @param name 用于构造 UUID 的字节数组。 + * + * @return 根据指定数组生成的 {@code UUID} + */ + public static UUID nameUUIDFromBytes(byte[] name) + { + MessageDigest md; + try + { + md = MessageDigest.getInstance("MD5"); + } + catch (NoSuchAlgorithmException nsae) + { + throw new InternalError("MD5 not supported"); + } + byte[] md5Bytes = md.digest(name); + md5Bytes[6] &= 0x0f; /* clear version */ + md5Bytes[6] |= 0x30; /* set to version 3 */ + md5Bytes[8] &= 0x3f; /* clear variant */ + md5Bytes[8] |= 0x80; /* set to IETF variant */ + return new UUID(md5Bytes); + } + + /** + * 根据 {@link #toString()} 方法中描述的字符串标准表示形式创建{@code UUID}。 + * + * @param name 指定 {@code UUID} 字符串 + * @return 具有指定值的 {@code UUID} + * @throws IllegalArgumentException 如果 name 与 {@link #toString} 中描述的字符串表示形式不符抛出此异常 + * + */ + public static UUID fromString(String name) + { + String[] components = name.split("-"); + if (components.length != 5) + { + throw new IllegalArgumentException("Invalid UUID string: " + name); + } + for (int i = 0; i < 5; i++) + { + components[i] = "0x" + components[i]; + } + + long mostSigBits = Long.decode(components[0]).longValue(); + mostSigBits <<= 16; + mostSigBits |= Long.decode(components[1]).longValue(); + mostSigBits <<= 16; + mostSigBits |= Long.decode(components[2]).longValue(); + + long leastSigBits = Long.decode(components[3]).longValue(); + leastSigBits <<= 48; + leastSigBits |= Long.decode(components[4]).longValue(); + + return new UUID(mostSigBits, leastSigBits); + } + + /** + * 返回此 UUID 的 128 位值中的最低有效 64 位。 + * + * @return 此 UUID 的 128 位值中的最低有效 64 位。 + */ + public long getLeastSignificantBits() + { + return leastSigBits; + } + + /** + * 返回此 UUID 的 128 位值中的最高有效 64 位。 + * + * @return 此 UUID 的 128 位值中最高有效 64 位。 + */ + public long getMostSignificantBits() + { + return mostSigBits; + } + + /** + * 与此 {@code UUID} 相关联的版本号. 版本号描述此 {@code UUID} 是如何生成的。 + *

+ * 版本号具有以下含意: + *

    + *
  • 1 基于时间的 UUID + *
  • 2 DCE 安全 UUID + *
  • 3 基于名称的 UUID + *
  • 4 随机生成的 UUID + *
+ * + * @return 此 {@code UUID} 的版本号 + */ + public int version() + { + // Version is bits masked by 0x000000000000F000 in MS long + return (int) ((mostSigBits >> 12) & 0x0f); + } + + /** + * 与此 {@code UUID} 相关联的变体号。变体号描述 {@code UUID} 的布局。 + *

+ * 变体号具有以下含意: + *

    + *
  • 0 为 NCS 向后兼容保留 + *
  • 2 IETF RFC 4122(Leach-Salz), 用于此类 + *
  • 6 保留,微软向后兼容 + *
  • 7 保留供以后定义使用 + *
+ * + * @return 此 {@code UUID} 相关联的变体号 + */ + public int variant() + { + // This field is composed of a varying number of bits. + // 0 - - Reserved for NCS backward compatibility + // 1 0 - The IETF aka Leach-Salz variant (used by this class) + // 1 1 0 Reserved, Microsoft backward compatibility + // 1 1 1 Reserved for future definition. + return (int) ((leastSigBits >>> (64 - (leastSigBits >>> 62))) & (leastSigBits >> 63)); + } + + /** + * 与此 UUID 相关联的时间戳值。 + * + *

+ * 60 位的时间戳值根据此 {@code UUID} 的 time_low、time_mid 和 time_hi 字段构造。
+ * 所得到的时间戳以 100 毫微秒为单位,从 UTC(通用协调时间) 1582 年 10 月 15 日零时开始。 + * + *

+ * 时间戳值仅在在基于时间的 UUID(其 version 类型为 1)中才有意义。
+ * 如果此 {@code UUID} 不是基于时间的 UUID,则此方法抛出 UnsupportedOperationException。 + * + * @throws UnsupportedOperationException 如果此 {@code UUID} 不是 version 为 1 的 UUID。 + */ + public long timestamp() throws UnsupportedOperationException + { + checkTimeBase(); + return (mostSigBits & 0x0FFFL) << 48// + | ((mostSigBits >> 16) & 0x0FFFFL) << 32// + | mostSigBits >>> 32; + } + + /** + * 与此 UUID 相关联的时钟序列值。 + * + *

+ * 14 位的时钟序列值根据此 UUID 的 clock_seq 字段构造。clock_seq 字段用于保证在基于时间的 UUID 中的时间唯一性。 + *

+ * {@code clockSequence} 值仅在基于时间的 UUID(其 version 类型为 1)中才有意义。 如果此 UUID 不是基于时间的 UUID,则此方法抛出 + * UnsupportedOperationException。 + * + * @return 此 {@code UUID} 的时钟序列 + * + * @throws UnsupportedOperationException 如果此 UUID 的 version 不为 1 + */ + public int clockSequence() throws UnsupportedOperationException + { + checkTimeBase(); + return (int) ((leastSigBits & 0x3FFF000000000000L) >>> 48); + } + + /** + * 与此 UUID 相关的节点值。 + * + *

+ * 48 位的节点值根据此 UUID 的 node 字段构造。此字段旨在用于保存机器的 IEEE 802 地址,该地址用于生成此 UUID 以保证空间唯一性。 + *

+ * 节点值仅在基于时间的 UUID(其 version 类型为 1)中才有意义。
+ * 如果此 UUID 不是基于时间的 UUID,则此方法抛出 UnsupportedOperationException。 + * + * @return 此 {@code UUID} 的节点值 + * + * @throws UnsupportedOperationException 如果此 UUID 的 version 不为 1 + */ + public long node() throws UnsupportedOperationException + { + checkTimeBase(); + return leastSigBits & 0x0000FFFFFFFFFFFFL; + } + + /** + * 返回此{@code UUID} 的字符串表现形式。 + * + *

+ * UUID 的字符串表示形式由此 BNF 描述: + * + *

+     * {@code
+     * UUID                   = ----
+     * time_low               = 4*
+     * time_mid               = 2*
+     * time_high_and_version  = 2*
+     * variant_and_sequence   = 2*
+     * node                   = 6*
+     * hexOctet               = 
+     * hexDigit               = [0-9a-fA-F]
+     * }
+     * 
+ * + * + * + * @return 此{@code UUID} 的字符串表现形式 + * @see #toString(boolean) + */ + @Override + public String toString() + { + return toString(false); + } + + /** + * 返回此{@code UUID} 的字符串表现形式。 + * + *

+ * UUID 的字符串表示形式由此 BNF 描述: + * + *

+     * {@code
+     * UUID                   = ----
+     * time_low               = 4*
+     * time_mid               = 2*
+     * time_high_and_version  = 2*
+     * variant_and_sequence   = 2*
+     * node                   = 6*
+     * hexOctet               = 
+     * hexDigit               = [0-9a-fA-F]
+     * }
+     * 
+ * + * + * + * @param isSimple 是否简单模式,简单模式为不带'-'的UUID字符串 + * @return 此{@code UUID} 的字符串表现形式 + */ + public String toString(boolean isSimple) + { + final StringBuilder builder = new StringBuilder(isSimple ? 32 : 36); + // time_low + builder.append(digits(mostSigBits >> 32, 8)); + if (false == isSimple) + { + builder.append('-'); + } + // time_mid + builder.append(digits(mostSigBits >> 16, 4)); + if (false == isSimple) + { + builder.append('-'); + } + // time_high_and_version + builder.append(digits(mostSigBits, 4)); + if (false == isSimple) + { + builder.append('-'); + } + // variant_and_sequence + builder.append(digits(leastSigBits >> 48, 4)); + if (false == isSimple) + { + builder.append('-'); + } + // node + builder.append(digits(leastSigBits, 12)); + + return builder.toString(); + } + + /** + * 返回此 UUID 的哈希码。 + * + * @return UUID 的哈希码值。 + */ + @Override + public int hashCode() + { + long hilo = mostSigBits ^ leastSigBits; + return ((int) (hilo >> 32)) ^ (int) hilo; + } + + /** + * 将此对象与指定对象比较。 + *

+ * 当且仅当参数不为 {@code null}、而是一个 UUID 对象、具有与此 UUID 相同的 varriant、包含相同的值(每一位均相同)时,结果才为 {@code true}。 + * + * @param obj 要与之比较的对象 + * + * @return 如果对象相同,则返回 {@code true};否则返回 {@code false} + */ + @Override + public boolean equals(Object obj) + { + if ((null == obj) || (obj.getClass() != UUID.class)) + { + return false; + } + UUID id = (UUID) obj; + return (mostSigBits == id.mostSigBits && leastSigBits == id.leastSigBits); + } + + // Comparison Operations + + /** + * 将此 UUID 与指定的 UUID 比较。 + * + *

+ * 如果两个 UUID 不同,且第一个 UUID 的最高有效字段大于第二个 UUID 的对应字段,则第一个 UUID 大于第二个 UUID。 + * + * @param val 与此 UUID 比较的 UUID + * + * @return 在此 UUID 小于、等于或大于 val 时,分别返回 -1、0 或 1。 + * + */ + @Override + public int compareTo(UUID val) + { + // The ordering is intentionally set up so that the UUIDs + // can simply be numerically compared as two numbers + return (this.mostSigBits < val.mostSigBits ? -1 : // + (this.mostSigBits > val.mostSigBits ? 1 : // + (this.leastSigBits < val.leastSigBits ? -1 : // + (this.leastSigBits > val.leastSigBits ? 1 : // + 0)))); + } + + // ------------------------------------------------------------------------------------------------------------------- + // Private method start + /** + * 返回指定数字对应的hex值 + * + * @param val 值 + * @param digits 位 + * @return 值 + */ + private static String digits(long val, int digits) + { + long hi = 1L << (digits * 4); + return Long.toHexString(hi | (val & (hi - 1))).substring(1); + } + + /** + * 检查是否为time-based版本UUID + */ + private void checkTimeBase() + { + if (version() != 1) + { + throw new UnsupportedOperationException("Not a time-based UUID"); + } + } + + /** + * 获取{@link SecureRandom},类提供加密的强随机数生成器 (RNG) + * + * @return {@link SecureRandom} + */ + public static SecureRandom getSecureRandom() + { + try + { + return SecureRandom.getInstance("SHA1PRNG"); + } + catch (NoSuchAlgorithmException e) + { + throw new UtilException(e); + } + } + + /** + * 获取随机数生成器对象
+ * ThreadLocalRandom是JDK 7之后提供并发产生随机数,能够解决多个线程发生的竞争争夺。 + * + * @return {@link ThreadLocalRandom} + */ + public static ThreadLocalRandom getRandom() + { + return ThreadLocalRandom.current(); + } +} diff --git a/src/main/java/com/ruoyi/common/xss/Xss.java b/src/main/java/com/ruoyi/common/xss/Xss.java new file mode 100644 index 0000000..7bfdf04 --- /dev/null +++ b/src/main/java/com/ruoyi/common/xss/Xss.java @@ -0,0 +1,27 @@ +package com.ruoyi.common.xss; + +import javax.validation.Constraint; +import javax.validation.Payload; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * 自定义xss校验注解 + * + * @author ruoyi + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(value = { ElementType.METHOD, ElementType.FIELD, ElementType.CONSTRUCTOR, ElementType.PARAMETER }) +@Constraint(validatedBy = { XssValidator.class }) +public @interface Xss +{ + String message() + + default "不允许任何脚本运行"; + + Class[] groups() default {}; + + Class[] payload() default {}; +} diff --git a/src/main/java/com/ruoyi/common/xss/XssValidator.java b/src/main/java/com/ruoyi/common/xss/XssValidator.java new file mode 100644 index 0000000..42f425c --- /dev/null +++ b/src/main/java/com/ruoyi/common/xss/XssValidator.java @@ -0,0 +1,39 @@ +package com.ruoyi.common.xss; + +import com.ruoyi.common.utils.StringUtils; +import javax.validation.ConstraintValidator; +import javax.validation.ConstraintValidatorContext; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * 自定义xss校验注解实现 + * + * @author ruoyi + */ +public class XssValidator implements ConstraintValidator +{ + private static final String HTML_PATTERN = "<(\\S*?)[^>]*>.*?|<.*? />"; + + @Override + public boolean isValid(String value, ConstraintValidatorContext constraintValidatorContext) + { + if (StringUtils.isBlank(value)) + { + return true; + } + return !containsHtml(value); + } + + public static boolean containsHtml(String value) + { + StringBuilder sHtml = new StringBuilder(); + Pattern pattern = Pattern.compile(HTML_PATTERN); + Matcher matcher = pattern.matcher(value); + while (matcher.find()) + { + sHtml.append(matcher.group()); + } + return pattern.matcher(sHtml).matches(); + } +} \ No newline at end of file diff --git a/src/main/java/com/ruoyi/framework/aspectj/DataScopeAspect.java b/src/main/java/com/ruoyi/framework/aspectj/DataScopeAspect.java new file mode 100644 index 0000000..134f7d2 --- /dev/null +++ b/src/main/java/com/ruoyi/framework/aspectj/DataScopeAspect.java @@ -0,0 +1,174 @@ +package com.ruoyi.framework.aspectj; + +import java.util.ArrayList; +import java.util.List; +import org.aspectj.lang.JoinPoint; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Before; +import org.springframework.stereotype.Component; +import com.ruoyi.common.core.text.Convert; +import com.ruoyi.common.utils.SecurityUtils; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.framework.aspectj.lang.annotation.DataScope; +import com.ruoyi.framework.security.LoginUser; +import com.ruoyi.framework.security.context.PermissionContextHolder; +import com.ruoyi.framework.web.domain.BaseEntity; +import com.ruoyi.project.system.domain.SysRole; +import com.ruoyi.project.system.domain.SysUser; + +/** + * 数据过滤处理 + * + * @author ruoyi + */ +@Aspect +@Component +public class DataScopeAspect +{ + /** + * 全部数据权限 + */ + public static final String DATA_SCOPE_ALL = "1"; + + /** + * 自定数据权限 + */ + public static final String DATA_SCOPE_CUSTOM = "2"; + + /** + * 部门数据权限 + */ + public static final String DATA_SCOPE_DEPT = "3"; + + /** + * 部门及以下数据权限 + */ + public static final String DATA_SCOPE_DEPT_AND_CHILD = "4"; + + /** + * 仅本人数据权限 + */ + public static final String DATA_SCOPE_SELF = "5"; + + /** + * 数据权限过滤关键字 + */ + public static final String DATA_SCOPE = "dataScope"; + + @Before("@annotation(controllerDataScope)") + public void doBefore(JoinPoint point, DataScope controllerDataScope) throws Throwable + { + clearDataScope(point); + handleDataScope(point, controllerDataScope); + } + + protected void handleDataScope(final JoinPoint joinPoint, DataScope controllerDataScope) + { + // 获取当前的用户 + LoginUser loginUser = SecurityUtils.getLoginUser(); + if (StringUtils.isNotNull(loginUser)) + { + SysUser currentUser = loginUser.getUser(); + // 如果是超级管理员,则不过滤数据 + if (StringUtils.isNotNull(currentUser) && !currentUser.isAdmin()) + { + String permission = StringUtils.defaultIfEmpty(controllerDataScope.permission(), PermissionContextHolder.getContext()); + dataScopeFilter(joinPoint, currentUser, controllerDataScope.deptAlias(), + controllerDataScope.userAlias(), permission); + } + } + } + + /** + * 数据范围过滤 + * + * @param joinPoint 切点 + * @param user 用户 + * @param deptAlias 部门别名 + * @param userAlias 用户别名 + * @param permission 权限字符 + */ + public static void dataScopeFilter(JoinPoint joinPoint, SysUser user, String deptAlias, String userAlias, String permission) + { + StringBuilder sqlString = new StringBuilder(); + List conditions = new ArrayList(); + + for (SysRole role : user.getRoles()) + { + String dataScope = role.getDataScope(); + if (!DATA_SCOPE_CUSTOM.equals(dataScope) && conditions.contains(dataScope)) + { + continue; + } + if (StringUtils.isNotEmpty(permission) && StringUtils.isNotEmpty(role.getPermissions()) + && !StringUtils.containsAny(role.getPermissions(), Convert.toStrArray(permission))) + { + continue; + } + if (DATA_SCOPE_ALL.equals(dataScope)) + { + sqlString = new StringBuilder(); + conditions.add(dataScope); + break; + } + else if (DATA_SCOPE_CUSTOM.equals(dataScope)) + { + 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(StringUtils.format(" OR {}.dept_id = {} ", deptAlias, user.getDeptId())); + } + else if (DATA_SCOPE_DEPT_AND_CHILD.equals(dataScope)) + { + 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 (StringUtils.isNotBlank(userAlias)) + { + sqlString.append(StringUtils.format(" OR {}.user_id = {} ", userAlias, user.getUserId())); + } + else + { + // 数据权限为仅本人且没有userAlias别名不查询任何数据 + sqlString.append(StringUtils.format(" OR {}.dept_id = 0 ", deptAlias)); + } + } + conditions.add(dataScope); + } + + // 多角色情况下,所有角色都不包含传递过来的权限字符,这个时候sqlString也会为空,所以要限制一下,不查询任何数据 + if (StringUtils.isEmpty(conditions)) + { + sqlString.append(StringUtils.format(" OR {}.dept_id = 0 ", deptAlias)); + } + + if (StringUtils.isNotBlank(sqlString.toString())) + { + Object params = joinPoint.getArgs()[0]; + if (StringUtils.isNotNull(params) && params instanceof BaseEntity) + { + BaseEntity baseEntity = (BaseEntity) params; + baseEntity.getParams().put(DATA_SCOPE, " AND (" + sqlString.substring(4) + ")"); + } + } + } + + /** + * 拼接权限sql前先清空params.dataScope参数防止注入 + */ + private void clearDataScope(final JoinPoint joinPoint) + { + Object params = joinPoint.getArgs()[0]; + if (StringUtils.isNotNull(params) && params instanceof BaseEntity) + { + BaseEntity baseEntity = (BaseEntity) params; + baseEntity.getParams().put(DATA_SCOPE, ""); + } + } +} diff --git a/src/main/java/com/ruoyi/framework/aspectj/DataSourceAspect.java b/src/main/java/com/ruoyi/framework/aspectj/DataSourceAspect.java new file mode 100644 index 0000000..c2bb892 --- /dev/null +++ b/src/main/java/com/ruoyi/framework/aspectj/DataSourceAspect.java @@ -0,0 +1,72 @@ +package com.ruoyi.framework.aspectj; + +import java.util.Objects; +import org.aspectj.lang.ProceedingJoinPoint; +import org.aspectj.lang.annotation.Around; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Pointcut; +import org.aspectj.lang.reflect.MethodSignature; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.core.annotation.AnnotationUtils; +import org.springframework.core.annotation.Order; +import org.springframework.stereotype.Component; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.framework.aspectj.lang.annotation.DataSource; +import com.ruoyi.framework.datasource.DynamicDataSourceContextHolder; + +/** + * 多数据源处理 + * + * @author ruoyi + */ +@Aspect +@Order(1) +@Component +public class DataSourceAspect +{ + protected Logger logger = LoggerFactory.getLogger(getClass()); + + @Pointcut("@annotation(com.ruoyi.framework.aspectj.lang.annotation.DataSource)" + + "|| @within(com.ruoyi.framework.aspectj.lang.annotation.DataSource)") + public void dsPointCut() + { + + } + + @Around("dsPointCut()") + public Object around(ProceedingJoinPoint point) throws Throwable + { + DataSource dataSource = getDataSource(point); + + if (StringUtils.isNotNull(dataSource)) + { + DynamicDataSourceContextHolder.setDataSourceType(dataSource.value().name()); + } + + try + { + return point.proceed(); + } + finally + { + // 销毁数据源 在执行方法之后 + DynamicDataSourceContextHolder.clearDataSourceType(); + } + } + + /** + * 获取需要切换的数据源 + */ + public DataSource getDataSource(ProceedingJoinPoint point) + { + MethodSignature signature = (MethodSignature) point.getSignature(); + DataSource dataSource = AnnotationUtils.findAnnotation(signature.getMethod(), DataSource.class); + if (Objects.nonNull(dataSource)) + { + return dataSource; + } + + return AnnotationUtils.findAnnotation(signature.getDeclaringType(), DataSource.class); + } +} diff --git a/src/main/java/com/ruoyi/framework/aspectj/LogAspect.java b/src/main/java/com/ruoyi/framework/aspectj/LogAspect.java new file mode 100644 index 0000000..e62f421 --- /dev/null +++ b/src/main/java/com/ruoyi/framework/aspectj/LogAspect.java @@ -0,0 +1,255 @@ +package com.ruoyi.framework.aspectj; + +import java.util.Collection; +import java.util.Map; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import org.apache.commons.lang3.ArrayUtils; +import org.aspectj.lang.JoinPoint; +import org.aspectj.lang.annotation.AfterReturning; +import org.aspectj.lang.annotation.AfterThrowing; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Before; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.core.NamedThreadLocal; +import org.springframework.stereotype.Component; +import org.springframework.validation.BindingResult; +import org.springframework.web.multipart.MultipartFile; +import com.alibaba.fastjson2.JSON; +import com.ruoyi.common.enums.HttpMethod; +import com.ruoyi.common.filter.PropertyPreExcludeFilter; +import com.ruoyi.common.utils.SecurityUtils; +import com.ruoyi.common.utils.ServletUtils; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.ip.IpUtils; +import com.ruoyi.framework.aspectj.lang.annotation.Log; +import com.ruoyi.framework.aspectj.lang.enums.BusinessStatus; +import com.ruoyi.framework.manager.AsyncManager; +import com.ruoyi.framework.manager.factory.AsyncFactory; +import com.ruoyi.framework.security.LoginUser; +import com.ruoyi.project.monitor.domain.SysOperLog; +import com.ruoyi.project.system.domain.SysUser; + +/** + * 操作日志记录处理 + * + * @author ruoyi + */ +@Aspect +@Component +public class LogAspect +{ + private static final Logger log = LoggerFactory.getLogger(LogAspect.class); + + /** 排除敏感属性字段 */ + public static final String[] EXCLUDE_PROPERTIES = { "password", "oldPassword", "newPassword", "confirmPassword" }; + + /** 计算操作消耗时间 */ + private static final ThreadLocal TIME_THREADLOCAL = new NamedThreadLocal("Cost Time"); + + /** + * 处理请求前执行 + */ + @Before(value = "@annotation(controllerLog)") + public void boBefore(JoinPoint joinPoint, Log controllerLog) + { + TIME_THREADLOCAL.set(System.currentTimeMillis()); + } + + /** + * 处理完请求后执行 + * + * @param joinPoint 切点 + */ + @AfterReturning(pointcut = "@annotation(controllerLog)", returning = "jsonResult") + public void doAfterReturning(JoinPoint joinPoint, Log controllerLog, Object jsonResult) + { + handleLog(joinPoint, controllerLog, null, jsonResult); + } + + /** + * 拦截异常操作 + * + * @param joinPoint 切点 + * @param e 异常 + */ + @AfterThrowing(value = "@annotation(controllerLog)", throwing = "e") + public void doAfterThrowing(JoinPoint joinPoint, Log controllerLog, Exception e) + { + handleLog(joinPoint, controllerLog, e, null); + } + + protected void handleLog(final JoinPoint joinPoint, Log controllerLog, final Exception e, Object jsonResult) + { + try + { + // 获取当前的用户 + LoginUser loginUser = SecurityUtils.getLoginUser(); + + // *========数据库日志=========*// + SysOperLog operLog = new SysOperLog(); + operLog.setStatus(BusinessStatus.SUCCESS.ordinal()); + // 请求的地址 + String ip = IpUtils.getIpAddr(); + operLog.setOperIp(ip); + operLog.setOperUrl(StringUtils.substring(ServletUtils.getRequest().getRequestURI(), 0, 255)); + if (loginUser != null) + { + operLog.setOperName(loginUser.getUsername()); + SysUser currentUser = loginUser.getUser(); + if (StringUtils.isNotNull(currentUser) && StringUtils.isNotNull(currentUser.getDept())) + { + operLog.setDeptName(currentUser.getDept().getDeptName()); + } + } + + if (e != null) + { + operLog.setStatus(BusinessStatus.FAIL.ordinal()); + operLog.setErrorMsg(StringUtils.substring(e.getMessage(), 0, 2000)); + } + // 设置方法名称 + String className = joinPoint.getTarget().getClass().getName(); + String methodName = joinPoint.getSignature().getName(); + operLog.setMethod(className + "." + methodName + "()"); + // 设置请求方式 + operLog.setRequestMethod(ServletUtils.getRequest().getMethod()); + // 处理设置注解上的参数 + getControllerMethodDescription(joinPoint, controllerLog, operLog, jsonResult); + // 设置消耗时间 + operLog.setCostTime(System.currentTimeMillis() - TIME_THREADLOCAL.get()); + // 保存数据库 + AsyncManager.me().execute(AsyncFactory.recordOper(operLog)); + } + catch (Exception exp) + { + // 记录本地异常日志 + log.error("异常信息:{}", exp.getMessage()); + exp.printStackTrace(); + } + finally + { + TIME_THREADLOCAL.remove(); + } + } + + /** + * 获取注解中对方法的描述信息 用于Controller层注解 + * + * @param log 日志 + * @param operLog 操作日志 + * @throws Exception + */ + public void getControllerMethodDescription(JoinPoint joinPoint, Log log, SysOperLog operLog, Object jsonResult) throws Exception + { + // 设置action动作 + operLog.setBusinessType(log.businessType().ordinal()); + // 设置标题 + operLog.setTitle(log.title()); + // 设置操作人类别 + operLog.setOperatorType(log.operatorType().ordinal()); + // 是否需要保存request,参数和值 + if (log.isSaveRequestData()) + { + // 获取参数的信息,传入到数据库中。 + setRequestValue(joinPoint, operLog, log.excludeParamNames()); + } + // 是否需要保存response,参数和值 + if (log.isSaveResponseData() && StringUtils.isNotNull(jsonResult)) + { + operLog.setJsonResult(StringUtils.substring(JSON.toJSONString(jsonResult), 0, 2000)); + } + } + + /** + * 获取请求的参数,放到log中 + * + * @param operLog 操作日志 + * @throws Exception 异常 + */ + private void setRequestValue(JoinPoint joinPoint, SysOperLog operLog, String[] excludeParamNames) throws Exception + { + String requestMethod = operLog.getRequestMethod(); + Map paramsMap = ServletUtils.getParamMap(ServletUtils.getRequest()); + if (StringUtils.isEmpty(paramsMap) + && (HttpMethod.PUT.name().equals(requestMethod) || HttpMethod.POST.name().equals(requestMethod))) + { + String params = argsArrayToString(joinPoint.getArgs(), excludeParamNames); + operLog.setOperParam(StringUtils.substring(params, 0, 2000)); + } + else + { + operLog.setOperParam(StringUtils.substring(JSON.toJSONString(paramsMap, excludePropertyPreFilter(excludeParamNames)), 0, 2000)); + } + } + + /** + * 参数拼装 + */ + private String argsArrayToString(Object[] paramsArray, String[] excludeParamNames) + { + String params = ""; + if (paramsArray != null && paramsArray.length > 0) + { + for (Object o : paramsArray) + { + if (StringUtils.isNotNull(o) && !isFilterObject(o)) + { + try + { + String jsonObj = JSON.toJSONString(o, excludePropertyPreFilter(excludeParamNames)); + params += jsonObj.toString() + " "; + } + catch (Exception e) + { + } + } + } + } + return params.trim(); + } + + /** + * 忽略敏感属性 + */ + public PropertyPreExcludeFilter excludePropertyPreFilter(String[] excludeParamNames) + { + return new PropertyPreExcludeFilter().addExcludes(ArrayUtils.addAll(EXCLUDE_PROPERTIES, excludeParamNames)); + } + + /** + * 判断是否需要过滤的对象。 + * + * @param o 对象信息。 + * @return 如果是需要过滤的对象,则返回true;否则返回false。 + */ + @SuppressWarnings("rawtypes") + public boolean isFilterObject(final Object o) + { + Class clazz = o.getClass(); + if (clazz.isArray()) + { + return clazz.getComponentType().isAssignableFrom(MultipartFile.class); + } + else if (Collection.class.isAssignableFrom(clazz)) + { + Collection collection = (Collection) o; + for (Object value : collection) + { + return value instanceof MultipartFile; + } + } + else if (Map.class.isAssignableFrom(clazz)) + { + Map map = (Map) o; + for (Object value : map.entrySet()) + { + Map.Entry entry = (Map.Entry) value; + return entry.getValue() instanceof MultipartFile; + } + } + return o instanceof MultipartFile || o instanceof HttpServletRequest || o instanceof HttpServletResponse + || o instanceof BindingResult; + } +} diff --git a/src/main/java/com/ruoyi/framework/aspectj/RateLimiterAspect.java b/src/main/java/com/ruoyi/framework/aspectj/RateLimiterAspect.java new file mode 100644 index 0000000..820c34b --- /dev/null +++ b/src/main/java/com/ruoyi/framework/aspectj/RateLimiterAspect.java @@ -0,0 +1,89 @@ +package com.ruoyi.framework.aspectj; + +import java.lang.reflect.Method; +import java.util.Collections; +import java.util.List; +import org.aspectj.lang.JoinPoint; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Before; +import org.aspectj.lang.reflect.MethodSignature; +import org.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 com.ruoyi.common.exception.ServiceException; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.ip.IpUtils; +import com.ruoyi.framework.aspectj.lang.annotation.RateLimiter; +import com.ruoyi.framework.aspectj.lang.enums.LimitType; + +/** + * 限流处理 + * + * @author ruoyi + */ +@Aspect +@Component +public class RateLimiterAspect +{ + private static final Logger log = LoggerFactory.getLogger(RateLimiterAspect.class); + + private RedisTemplate redisTemplate; + + private RedisScript limitScript; + + @Autowired + public void setRedisTemplate1(RedisTemplate redisTemplate) + { + this.redisTemplate = redisTemplate; + } + + @Autowired + public void setLimitScript(RedisScript limitScript) + { + this.limitScript = limitScript; + } + + @Before("@annotation(rateLimiter)") + public void doBefore(JoinPoint point, RateLimiter rateLimiter) throws Throwable + { + int time = rateLimiter.time(); + int count = rateLimiter.count(); + + String combineKey = getCombineKey(rateLimiter, point); + List 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(), combineKey); + } + catch (ServiceException e) + { + throw e; + } + catch (Exception e) + { + throw new RuntimeException("服务器限流异常,请稍候再试"); + } + } + + public String getCombineKey(RateLimiter rateLimiter, JoinPoint point) + { + StringBuffer stringBuffer = new StringBuffer(rateLimiter.key()); + if (rateLimiter.limitType() == LimitType.IP) + { + stringBuffer.append(IpUtils.getIpAddr()).append("-"); + } + MethodSignature signature = (MethodSignature) point.getSignature(); + Method method = signature.getMethod(); + Class targetClass = method.getDeclaringClass(); + stringBuffer.append(targetClass.getName()).append("-").append(method.getName()); + return stringBuffer.toString(); + } +} diff --git a/src/main/java/com/ruoyi/framework/aspectj/lang/annotation/Anonymous.java b/src/main/java/com/ruoyi/framework/aspectj/lang/annotation/Anonymous.java new file mode 100644 index 0000000..2fb432c --- /dev/null +++ b/src/main/java/com/ruoyi/framework/aspectj/lang/annotation/Anonymous.java @@ -0,0 +1,19 @@ +package com.ruoyi.framework.aspectj.lang.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; + +/** + * 匿名访问不鉴权注解 + * + * @author ruoyi + */ +@Target({ ElementType.METHOD, ElementType.TYPE }) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface Anonymous +{ +} diff --git a/src/main/java/com/ruoyi/framework/aspectj/lang/annotation/DataScope.java b/src/main/java/com/ruoyi/framework/aspectj/lang/annotation/DataScope.java new file mode 100644 index 0000000..eff418f --- /dev/null +++ b/src/main/java/com/ruoyi/framework/aspectj/lang/annotation/DataScope.java @@ -0,0 +1,33 @@ +package com.ruoyi.framework.aspectj.lang.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; + +/** + * 数据权限过滤注解 + * + * @author ruoyi + */ +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface DataScope +{ + /** + * 部门表的别名 + */ + public String deptAlias() default ""; + + /** + * 用户表的别名 + */ + public String userAlias() default ""; + + /** + * 权限字符(用于多个角色匹配符合要求的权限)默认根据权限注解@ss获取,多个权限用逗号分隔开来 + */ + public String permission() default ""; +} diff --git a/src/main/java/com/ruoyi/framework/aspectj/lang/annotation/DataSource.java b/src/main/java/com/ruoyi/framework/aspectj/lang/annotation/DataSource.java new file mode 100644 index 0000000..e5a780e --- /dev/null +++ b/src/main/java/com/ruoyi/framework/aspectj/lang/annotation/DataSource.java @@ -0,0 +1,28 @@ +package com.ruoyi.framework.aspectj.lang.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 com.ruoyi.framework.aspectj.lang.enums.DataSourceType; + +/** + * 自定义多数据源切换注解 + * + * 优先级:先方法,后类,如果方法覆盖了类上的数据源类型,以方法的为准,否则以类上的为准 + * + * @author ruoyi + */ +@Target({ ElementType.METHOD, ElementType.TYPE }) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@Inherited +public @interface DataSource +{ + /** + * 切换数据源名称 + */ + public DataSourceType value() default DataSourceType.MASTER; +} diff --git a/src/main/java/com/ruoyi/framework/aspectj/lang/annotation/Excel.java b/src/main/java/com/ruoyi/framework/aspectj/lang/annotation/Excel.java new file mode 100644 index 0000000..b11514f --- /dev/null +++ b/src/main/java/com/ruoyi/framework/aspectj/lang/annotation/Excel.java @@ -0,0 +1,187 @@ +package com.ruoyi.framework.aspectj.lang.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.math.BigDecimal; +import org.apache.poi.ss.usermodel.HorizontalAlignment; +import org.apache.poi.ss.usermodel.IndexedColors; +import com.ruoyi.common.utils.poi.ExcelHandlerAdapter; + +/** + * 自定义导出Excel数据注解 + * + * @author ruoyi + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.FIELD) +public @interface Excel +{ + /** + * 导出时在excel中排序 + */ + public int sort() default Integer.MAX_VALUE; + + /** + * 导出到Excel中的名字. + */ + public String name() default ""; + + /** + * 日期格式, 如: yyyy-MM-dd + */ + public String dateFormat() default ""; + + /** + * 如果是字典类型,请设置字典的type值 (如: sys_user_sex) + */ + public String dictType() default ""; + + /** + * 读取内容转表达式 (如: 0=男,1=女,2=未知) + */ + public String readConverterExp() default ""; + + /** + * 分隔符,读取字符串组内容 + */ + public String separator() default ","; + + /** + * BigDecimal 精度 默认:-1(默认不开启BigDecimal格式化) + */ + public int scale() default -1; + + /** + * BigDecimal 舍入规则 默认:BigDecimal.ROUND_HALF_EVEN + */ + public int roundingMode() default BigDecimal.ROUND_HALF_EVEN; + + /** + * 导出时在excel中每个列的高度 + */ + public double height() default 14; + + /** + * 导出时在excel中每个列的宽度 + */ + public double width() default 16; + + /** + * 文字后缀,如% 90 变成90% + */ + public String suffix() default ""; + + /** + * 当值为空时,字段的默认值 + */ + public String defaultValue() default ""; + + /** + * 提示信息 + */ + public String prompt() default ""; + + /** + * 设置只能选择不能输入的列内容. + */ + public String[] combo() default {}; + + /** + * 是否需要纵向合并单元格,应对需求:含有list集合单元格) + */ + public boolean needMerge() default false; + + /** + * 是否导出数据,应对需求:有时我们需要导出一份模板,这是标题需要但内容需要用户手工填写. + */ + public boolean isExport() default true; + + /** + * 另一个类中的属性名称,支持多级获取,以小数点隔开 + */ + public String targetAttr() default ""; + + /** + * 是否自动统计数据,在最后追加一行统计数据总和 + */ + public boolean isStatistics() default false; + + /** + * 导出类型(0数字 1字符串 2图片) + */ + public ColumnType cellType() default ColumnType.STRING; + + /** + * 导出列头背景颜色 + */ + public IndexedColors headerBackgroundColor() default IndexedColors.GREY_50_PERCENT; + + /** + * 导出列头字体颜色 + */ + public IndexedColors headerColor() default IndexedColors.WHITE; + + /** + * 导出单元格背景颜色 + */ + public IndexedColors backgroundColor() default IndexedColors.WHITE; + + /** + * 导出单元格字体颜色 + */ + public IndexedColors color() default IndexedColors.BLACK; + + /** + * 导出字段对齐方式 + */ + public HorizontalAlignment align() default HorizontalAlignment.CENTER; + + /** + * 自定义数据处理器 + */ + public Class handler() default ExcelHandlerAdapter.class; + + /** + * 自定义数据处理器参数 + */ + public String[] args() default {}; + + /** + * 字段类型(0:导出导入;1:仅导出;2:仅导入) + */ + Type type() default Type.ALL; + + public enum Type + { + ALL(0), EXPORT(1), IMPORT(2); + private final int value; + + Type(int value) + { + this.value = value; + } + + public int value() + { + return this.value; + } + } + + public enum ColumnType + { + NUMERIC(0), STRING(1), IMAGE(2), TEXT(3); + private final int value; + + ColumnType(int value) + { + this.value = value; + } + + public int value() + { + return this.value; + } + } +} \ No newline at end of file diff --git a/src/main/java/com/ruoyi/framework/aspectj/lang/annotation/Excels.java b/src/main/java/com/ruoyi/framework/aspectj/lang/annotation/Excels.java new file mode 100644 index 0000000..fdcd9da --- /dev/null +++ b/src/main/java/com/ruoyi/framework/aspectj/lang/annotation/Excels.java @@ -0,0 +1,18 @@ +package com.ruoyi.framework.aspectj.lang.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Excel注解集 + * + * @author ruoyi + */ +@Target(ElementType.FIELD) +@Retention(RetentionPolicy.RUNTIME) +public @interface Excels +{ + public Excel[] value(); +} diff --git a/src/main/java/com/ruoyi/framework/aspectj/lang/annotation/Log.java b/src/main/java/com/ruoyi/framework/aspectj/lang/annotation/Log.java new file mode 100644 index 0000000..984d369 --- /dev/null +++ b/src/main/java/com/ruoyi/framework/aspectj/lang/annotation/Log.java @@ -0,0 +1,51 @@ +package com.ruoyi.framework.aspectj.lang.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.framework.aspectj.lang.enums.BusinessType; +import com.ruoyi.framework.aspectj.lang.enums.OperatorType; + +/** + * 自定义操作日志记录注解 + * + * @author ruoyi + * + */ +@Target({ ElementType.PARAMETER, ElementType.METHOD }) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface Log +{ + /** + * 模块 + */ + public String title() default ""; + + /** + * 功能 + */ + public BusinessType businessType() default BusinessType.OTHER; + + /** + * 操作人类别 + */ + public OperatorType operatorType() default OperatorType.MANAGE; + + /** + * 是否保存请求的参数 + */ + public boolean isSaveRequestData() default true; + + /** + * 是否保存响应的参数 + */ + public boolean isSaveResponseData() default true; + + /** + * 排除指定的请求参数 + */ + public String[] excludeParamNames() default {}; +} diff --git a/src/main/java/com/ruoyi/framework/aspectj/lang/annotation/RateLimiter.java b/src/main/java/com/ruoyi/framework/aspectj/lang/annotation/RateLimiter.java new file mode 100644 index 0000000..5aa6af6 --- /dev/null +++ b/src/main/java/com/ruoyi/framework/aspectj/lang/annotation/RateLimiter.java @@ -0,0 +1,40 @@ +package com.ruoyi.framework.aspectj.lang.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.CacheConstants; +import com.ruoyi.framework.aspectj.lang.enums.LimitType; + +/** + * 限流注解 + * + * @author ruoyi + */ +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface RateLimiter +{ + /** + * 限流key + */ + public String key() default CacheConstants.RATE_LIMIT_KEY; + + /** + * 限流时间,单位秒 + */ + public int time() default 60; + + /** + * 限流次数 + */ + public int count() default 100; + + /** + * 限流类型 + */ + public LimitType limitType() default LimitType.DEFAULT; +} diff --git a/src/main/java/com/ruoyi/framework/aspectj/lang/enums/BusinessStatus.java b/src/main/java/com/ruoyi/framework/aspectj/lang/enums/BusinessStatus.java new file mode 100644 index 0000000..2e1c7af --- /dev/null +++ b/src/main/java/com/ruoyi/framework/aspectj/lang/enums/BusinessStatus.java @@ -0,0 +1,20 @@ +package com.ruoyi.framework.aspectj.lang.enums; + +/** + * 操作状态 + * + * @author ruoyi + * + */ +public enum BusinessStatus +{ + /** + * 成功 + */ + SUCCESS, + + /** + * 失败 + */ + FAIL, +} diff --git a/src/main/java/com/ruoyi/framework/aspectj/lang/enums/BusinessType.java b/src/main/java/com/ruoyi/framework/aspectj/lang/enums/BusinessType.java new file mode 100644 index 0000000..2810215 --- /dev/null +++ b/src/main/java/com/ruoyi/framework/aspectj/lang/enums/BusinessType.java @@ -0,0 +1,59 @@ +package com.ruoyi.framework.aspectj.lang.enums; + +/** + * 业务操作类型 + * + * @author ruoyi + */ +public enum BusinessType +{ + /** + * 其它 + */ + OTHER, + + /** + * 新增 + */ + INSERT, + + /** + * 修改 + */ + UPDATE, + + /** + * 删除 + */ + DELETE, + + /** + * 授权 + */ + GRANT, + + /** + * 导出 + */ + EXPORT, + + /** + * 导入 + */ + IMPORT, + + /** + * 强退 + */ + FORCE, + + /** + * 生成代码 + */ + GENCODE, + + /** + * 清空数据 + */ + CLEAN, +} diff --git a/src/main/java/com/ruoyi/framework/aspectj/lang/enums/DataSourceType.java b/src/main/java/com/ruoyi/framework/aspectj/lang/enums/DataSourceType.java new file mode 100644 index 0000000..103cd80 --- /dev/null +++ b/src/main/java/com/ruoyi/framework/aspectj/lang/enums/DataSourceType.java @@ -0,0 +1,19 @@ +package com.ruoyi.framework.aspectj.lang.enums; + +/** + * 数据源 + * + * @author ruoyi + */ +public enum DataSourceType +{ + /** + * 主库 + */ + MASTER, + + /** + * 从库 + */ + SLAVE +} diff --git a/src/main/java/com/ruoyi/framework/aspectj/lang/enums/LimitType.java b/src/main/java/com/ruoyi/framework/aspectj/lang/enums/LimitType.java new file mode 100644 index 0000000..599312d --- /dev/null +++ b/src/main/java/com/ruoyi/framework/aspectj/lang/enums/LimitType.java @@ -0,0 +1,20 @@ +package com.ruoyi.framework.aspectj.lang.enums; + +/** + * 限流类型 + * + * @author ruoyi + */ + +public enum LimitType +{ + /** + * 默认策略全局限流 + */ + DEFAULT, + + /** + * 根据请求者IP进行限流 + */ + IP +} diff --git a/src/main/java/com/ruoyi/framework/aspectj/lang/enums/OperatorType.java b/src/main/java/com/ruoyi/framework/aspectj/lang/enums/OperatorType.java new file mode 100644 index 0000000..e3d87db --- /dev/null +++ b/src/main/java/com/ruoyi/framework/aspectj/lang/enums/OperatorType.java @@ -0,0 +1,24 @@ +package com.ruoyi.framework.aspectj.lang.enums; + +/** + * 操作人类别 + * + * @author ruoyi + */ +public enum OperatorType +{ + /** + * 其它 + */ + OTHER, + + /** + * 后台用户 + */ + MANAGE, + + /** + * 手机端用户 + */ + MOBILE +} diff --git a/src/main/java/com/ruoyi/framework/config/ApplicationConfig.java b/src/main/java/com/ruoyi/framework/config/ApplicationConfig.java new file mode 100644 index 0000000..55de6f7 --- /dev/null +++ b/src/main/java/com/ruoyi/framework/config/ApplicationConfig.java @@ -0,0 +1,30 @@ +package com.ruoyi.framework.config; + +import java.util.TimeZone; +import org.mybatis.spring.annotation.MapperScan; +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; + +/** + * 程序注解配置 + * + * @author ruoyi + */ +@Configuration +// 表示通过aop框架暴露该代理对象,AopContext能够访问 +@EnableAspectJAutoProxy(exposeProxy = true) +// 指定要扫描的Mapper类的包的路径 +@MapperScan("com.ruoyi.project.**.mapper") +public class ApplicationConfig +{ + /** + * 时区配置 + */ + @Bean + public Jackson2ObjectMapperBuilderCustomizer jacksonObjectMapperCustomization() + { + return jacksonObjectMapperBuilder -> jacksonObjectMapperBuilder.timeZone(TimeZone.getDefault()); + } +} diff --git a/src/main/java/com/ruoyi/framework/config/CaptchaConfig.java b/src/main/java/com/ruoyi/framework/config/CaptchaConfig.java new file mode 100644 index 0000000..43e78ae --- /dev/null +++ b/src/main/java/com/ruoyi/framework/config/CaptchaConfig.java @@ -0,0 +1,83 @@ +package com.ruoyi.framework.config; + +import java.util.Properties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import com.google.code.kaptcha.impl.DefaultKaptcha; +import com.google.code.kaptcha.util.Config; +import static com.google.code.kaptcha.Constants.*; + +/** + * 验证码配置 + * + * @author ruoyi + */ +@Configuration +public class CaptchaConfig +{ + @Bean(name = "captchaProducer") + public DefaultKaptcha getKaptchaBean() + { + DefaultKaptcha defaultKaptcha = new DefaultKaptcha(); + Properties properties = new Properties(); + // 是否有边框 默认为true 我们可以自己设置yes,no + properties.setProperty(KAPTCHA_BORDER, "yes"); + // 验证码文本字符颜色 默认为Color.BLACK + properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_COLOR, "black"); + // 验证码图片宽度 默认为200 + properties.setProperty(KAPTCHA_IMAGE_WIDTH, "160"); + // 验证码图片高度 默认为50 + properties.setProperty(KAPTCHA_IMAGE_HEIGHT, "60"); + // 验证码文本字符大小 默认为40 + properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_SIZE, "38"); + // KAPTCHA_SESSION_KEY + properties.setProperty(KAPTCHA_SESSION_CONFIG_KEY, "kaptchaCode"); + // 验证码文本字符长度 默认为5 + properties.setProperty(KAPTCHA_TEXTPRODUCER_CHAR_LENGTH, "4"); + // 验证码文本字体样式 默认为new Font("Arial", 1, fontSize), new Font("Courier", 1, fontSize) + properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_NAMES, "Arial,Courier"); + // 图片样式 水纹com.google.code.kaptcha.impl.WaterRipple 鱼眼com.google.code.kaptcha.impl.FishEyeGimpy 阴影com.google.code.kaptcha.impl.ShadowGimpy + properties.setProperty(KAPTCHA_OBSCURIFICATOR_IMPL, "com.google.code.kaptcha.impl.ShadowGimpy"); + Config config = new Config(properties); + defaultKaptcha.setConfig(config); + return defaultKaptcha; + } + + @Bean(name = "captchaProducerMath") + public DefaultKaptcha getKaptchaBeanMath() + { + DefaultKaptcha defaultKaptcha = new DefaultKaptcha(); + Properties properties = new Properties(); + // 是否有边框 默认为true 我们可以自己设置yes,no + properties.setProperty(KAPTCHA_BORDER, "yes"); + // 边框颜色 默认为Color.BLACK + properties.setProperty(KAPTCHA_BORDER_COLOR, "105,179,90"); + // 验证码文本字符颜色 默认为Color.BLACK + properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_COLOR, "blue"); + // 验证码图片宽度 默认为200 + properties.setProperty(KAPTCHA_IMAGE_WIDTH, "160"); + // 验证码图片高度 默认为50 + properties.setProperty(KAPTCHA_IMAGE_HEIGHT, "60"); + // 验证码文本字符大小 默认为40 + properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_SIZE, "35"); + // KAPTCHA_SESSION_KEY + properties.setProperty(KAPTCHA_SESSION_CONFIG_KEY, "kaptchaCodeMath"); + // 验证码文本生成器 + properties.setProperty(KAPTCHA_TEXTPRODUCER_IMPL, "com.ruoyi.framework.config.KaptchaTextCreator"); + // 验证码文本字符间距 默认为2 + properties.setProperty(KAPTCHA_TEXTPRODUCER_CHAR_SPACE, "3"); + // 验证码文本字符长度 默认为5 + properties.setProperty(KAPTCHA_TEXTPRODUCER_CHAR_LENGTH, "6"); + // 验证码文本字体样式 默认为new Font("Arial", 1, fontSize), new Font("Courier", 1, fontSize) + properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_NAMES, "Arial,Courier"); + // 验证码噪点颜色 默认为Color.BLACK + properties.setProperty(KAPTCHA_NOISE_COLOR, "white"); + // 干扰实现类 + properties.setProperty(KAPTCHA_NOISE_IMPL, "com.google.code.kaptcha.impl.NoNoise"); + // 图片样式 水纹com.google.code.kaptcha.impl.WaterRipple 鱼眼com.google.code.kaptcha.impl.FishEyeGimpy 阴影com.google.code.kaptcha.impl.ShadowGimpy + properties.setProperty(KAPTCHA_OBSCURIFICATOR_IMPL, "com.google.code.kaptcha.impl.ShadowGimpy"); + Config config = new Config(properties); + defaultKaptcha.setConfig(config); + return defaultKaptcha; + } +} diff --git a/src/main/java/com/ruoyi/framework/config/DruidConfig.java b/src/main/java/com/ruoyi/framework/config/DruidConfig.java new file mode 100644 index 0000000..7a8fd82 --- /dev/null +++ b/src/main/java/com/ruoyi/framework/config/DruidConfig.java @@ -0,0 +1,126 @@ +package com.ruoyi.framework.config; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; +import javax.servlet.Filter; +import javax.servlet.FilterChain; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.sql.DataSource; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.boot.web.servlet.FilterRegistrationBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Primary; +import com.alibaba.druid.pool.DruidDataSource; +import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder; +import com.alibaba.druid.spring.boot.autoconfigure.properties.DruidStatProperties; +import com.alibaba.druid.util.Utils; +import com.ruoyi.common.utils.spring.SpringUtils; +import com.ruoyi.framework.aspectj.lang.enums.DataSourceType; +import com.ruoyi.framework.config.properties.DruidProperties; +import com.ruoyi.framework.datasource.DynamicDataSource; + +/** + * druid 配置多数据源 + * + * @author ruoyi + */ +@Configuration +public class DruidConfig +{ + @Bean + @ConfigurationProperties("spring.datasource.druid.master") + public DataSource masterDataSource(DruidProperties druidProperties) + { + DruidDataSource dataSource = DruidDataSourceBuilder.create().build(); + return druidProperties.dataSource(dataSource); + } + + @Bean + @ConfigurationProperties("spring.datasource.druid.slave") + @ConditionalOnProperty(prefix = "spring.datasource.druid.slave", name = "enabled", havingValue = "true") + public DataSource slaveDataSource(DruidProperties druidProperties) + { + DruidDataSource dataSource = DruidDataSourceBuilder.create().build(); + return druidProperties.dataSource(dataSource); + } + + @Bean(name = "dynamicDataSource") + @Primary + public DynamicDataSource dataSource(DataSource masterDataSource) + { + Map targetDataSources = new HashMap<>(); + targetDataSources.put(DataSourceType.MASTER.name(), masterDataSource); + setDataSource(targetDataSources, DataSourceType.SLAVE.name(), "slaveDataSource"); + return new DynamicDataSource(masterDataSource, targetDataSources); + } + + /** + * 设置数据源 + * + * @param targetDataSources 备选数据源集合 + * @param sourceName 数据源名称 + * @param beanName bean名称 + */ + public void setDataSource(Map targetDataSources, String sourceName, String beanName) + { + try + { + DataSource dataSource = SpringUtils.getBean(beanName); + targetDataSources.put(sourceName, dataSource); + } + catch (Exception e) + { + } + } + + /** + * 去除监控页面底部的广告 + */ + @SuppressWarnings({ "rawtypes", "unchecked" }) + @Bean + @ConditionalOnProperty(name = "spring.datasource.druid.statViewServlet.enabled", havingValue = "true") + public FilterRegistrationBean removeDruidFilterRegistrationBean(DruidStatProperties properties) + { + // 获取web监控页面的参数 + DruidStatProperties.StatViewServlet config = properties.getStatViewServlet(); + // 提取common.js的配置路径 + String pattern = config.getUrlPattern() != null ? config.getUrlPattern() : "/druid/*"; + String commonJsPattern = pattern.replaceAll("\\*", "js/common.js"); + final String filePath = "support/http/resources/js/common.js"; + // 创建filter进行过滤 + Filter filter = new Filter() + { + @Override + public void init(javax.servlet.FilterConfig filterConfig) throws ServletException + { + } + @Override + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) + throws IOException, ServletException + { + chain.doFilter(request, response); + // 重置缓冲区,响应头不会被重置 + response.resetBuffer(); + // 获取common.js + String text = Utils.readFromResource(filePath); + // 正则替换banner, 除去底部的广告信息 + text = text.replaceAll("
", ""); + text = text.replaceAll("powered.*?shrek.wang", ""); + response.getWriter().write(text); + } + @Override + public void destroy() + { + } + }; + FilterRegistrationBean registrationBean = new FilterRegistrationBean(); + registrationBean.setFilter(filter); + registrationBean.addUrlPatterns(commonJsPattern); + return registrationBean; + } +} diff --git a/src/main/java/com/ruoyi/framework/config/FastJson2JsonRedisSerializer.java b/src/main/java/com/ruoyi/framework/config/FastJson2JsonRedisSerializer.java new file mode 100644 index 0000000..4adbb7f --- /dev/null +++ b/src/main/java/com/ruoyi/framework/config/FastJson2JsonRedisSerializer.java @@ -0,0 +1,52 @@ +package com.ruoyi.framework.config; + +import java.nio.charset.Charset; +import org.springframework.data.redis.serializer.RedisSerializer; +import org.springframework.data.redis.serializer.SerializationException; +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONReader; +import com.alibaba.fastjson2.JSONWriter; +import com.alibaba.fastjson2.filter.Filter; +import com.ruoyi.common.constant.Constants; + +/** + * Redis使用FastJson序列化 + * + * @author ruoyi + */ +public class FastJson2JsonRedisSerializer implements RedisSerializer +{ + public static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8"); + + static final Filter AUTO_TYPE_FILTER = JSONReader.autoTypeFilter(Constants.JSON_WHITELIST_STR); + + private Class clazz; + + public FastJson2JsonRedisSerializer(Class clazz) + { + super(); + this.clazz = clazz; + } + + @Override + public byte[] serialize(T t) throws SerializationException + { + if (t == null) + { + return new byte[0]; + } + return JSON.toJSONString(t, JSONWriter.Feature.WriteClassName).getBytes(DEFAULT_CHARSET); + } + + @Override + public T deserialize(byte[] bytes) throws SerializationException + { + if (bytes == null || bytes.length <= 0) + { + return null; + } + String str = new String(bytes, DEFAULT_CHARSET); + + return JSON.parseObject(str, clazz, AUTO_TYPE_FILTER); + } +} diff --git a/src/main/java/com/ruoyi/framework/config/FilterConfig.java b/src/main/java/com/ruoyi/framework/config/FilterConfig.java new file mode 100644 index 0000000..bb14c04 --- /dev/null +++ b/src/main/java/com/ruoyi/framework/config/FilterConfig.java @@ -0,0 +1,58 @@ +package com.ruoyi.framework.config; + +import java.util.HashMap; +import java.util.Map; +import javax.servlet.DispatcherType; +import org.springframework.beans.factory.annotation.Value; +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; +import com.ruoyi.common.filter.RepeatableFilter; +import com.ruoyi.common.filter.XssFilter; +import com.ruoyi.common.utils.StringUtils; + +/** + * Filter配置 + * + * @author ruoyi + */ +@Configuration +public class FilterConfig +{ + @Value("${xss.excludes}") + private String excludes; + + @Value("${xss.urlPatterns}") + private String urlPatterns; + + @SuppressWarnings({ "rawtypes", "unchecked" }) + @Bean + @ConditionalOnProperty(value = "xss.enabled", havingValue = "true") + public FilterRegistrationBean xssFilterRegistration() + { + FilterRegistrationBean registration = new FilterRegistrationBean(); + registration.setDispatcherTypes(DispatcherType.REQUEST); + registration.setFilter(new XssFilter()); + registration.addUrlPatterns(StringUtils.split(urlPatterns, ",")); + registration.setName("xssFilter"); + registration.setOrder(FilterRegistrationBean.HIGHEST_PRECEDENCE); + Map initParameters = new HashMap(); + initParameters.put("excludes", excludes); + registration.setInitParameters(initParameters); + return registration; + } + + @SuppressWarnings({ "rawtypes", "unchecked" }) + @Bean + public FilterRegistrationBean someFilterRegistration() + { + FilterRegistrationBean registration = new FilterRegistrationBean(); + registration.setFilter(new RepeatableFilter()); + registration.addUrlPatterns("/*"); + registration.setName("repeatableFilter"); + registration.setOrder(FilterRegistrationBean.LOWEST_PRECEDENCE); + return registration; + } + +} diff --git a/src/main/java/com/ruoyi/framework/config/GenConfig.java b/src/main/java/com/ruoyi/framework/config/GenConfig.java new file mode 100644 index 0000000..ef4456b --- /dev/null +++ b/src/main/java/com/ruoyi/framework/config/GenConfig.java @@ -0,0 +1,66 @@ +package com.ruoyi.framework.config; + +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; + +/** + * 读取代码生成相关配置 + * + * @author ruoyi + */ +@Component +@ConfigurationProperties(prefix = "gen") +public class GenConfig +{ + /** 作者 */ + public static String author; + + /** 生成包路径 */ + public static String packageName; + + /** 自动去除表前缀,默认是true */ + public static boolean autoRemovePre; + + /** 表前缀(类名不会包含表前缀) */ + public static String tablePrefix; + + public static String getAuthor() + { + return author; + } + + public void setAuthor(String author) + { + GenConfig.author = author; + } + + public static String getPackageName() + { + return packageName; + } + + public void setPackageName(String packageName) + { + GenConfig.packageName = packageName; + } + + public static boolean getAutoRemovePre() + { + return autoRemovePre; + } + + public void setAutoRemovePre(boolean autoRemovePre) + { + GenConfig.autoRemovePre = autoRemovePre; + } + + public static String getTablePrefix() + { + return tablePrefix; + } + + public void setTablePrefix(String tablePrefix) + { + GenConfig.tablePrefix = tablePrefix; + } +} diff --git a/src/main/java/com/ruoyi/framework/config/I18nConfig.java b/src/main/java/com/ruoyi/framework/config/I18nConfig.java new file mode 100644 index 0000000..163fd01 --- /dev/null +++ b/src/main/java/com/ruoyi/framework/config/I18nConfig.java @@ -0,0 +1,43 @@ +package com.ruoyi.framework.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.servlet.LocaleResolver; +import org.springframework.web.servlet.config.annotation.InterceptorRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; +import org.springframework.web.servlet.i18n.LocaleChangeInterceptor; +import org.springframework.web.servlet.i18n.SessionLocaleResolver; +import com.ruoyi.common.constant.Constants; + +/** + * 资源文件配置加载 + * + * @author ruoyi + */ +@Configuration +public class I18nConfig implements WebMvcConfigurer +{ + @Bean + public LocaleResolver localeResolver() + { + SessionLocaleResolver slr = new SessionLocaleResolver(); + // 默认语言 + slr.setDefaultLocale(Constants.DEFAULT_LOCALE); + return slr; + } + + @Bean + public LocaleChangeInterceptor localeChangeInterceptor() + { + LocaleChangeInterceptor lci = new LocaleChangeInterceptor(); + // 参数名 + lci.setParamName("lang"); + return lci; + } + + @Override + public void addInterceptors(InterceptorRegistry registry) + { + registry.addInterceptor(localeChangeInterceptor()); + } +} diff --git a/src/main/java/com/ruoyi/framework/config/KaptchaTextCreator.java b/src/main/java/com/ruoyi/framework/config/KaptchaTextCreator.java new file mode 100644 index 0000000..d2902f4 --- /dev/null +++ b/src/main/java/com/ruoyi/framework/config/KaptchaTextCreator.java @@ -0,0 +1,68 @@ +package com.ruoyi.framework.config; + +import java.util.Random; +import com.google.code.kaptcha.text.impl.DefaultTextCreator; + +/** + * 验证码文本生成器 + * + * @author ruoyi + */ +public class KaptchaTextCreator extends DefaultTextCreator +{ + private static final String[] CNUMBERS = "0,1,2,3,4,5,6,7,8,9,10".split(","); + + @Override + public String getText() + { + Integer result = 0; + Random random = new Random(); + int x = random.nextInt(10); + int y = random.nextInt(10); + StringBuilder suChinese = new StringBuilder(); + int randomoperands = random.nextInt(3); + if (randomoperands == 0) + { + result = x * y; + suChinese.append(CNUMBERS[x]); + suChinese.append("*"); + suChinese.append(CNUMBERS[y]); + } + else if (randomoperands == 1) + { + if ((x != 0) && y % x == 0) + { + result = y / x; + suChinese.append(CNUMBERS[y]); + suChinese.append("/"); + suChinese.append(CNUMBERS[x]); + } + else + { + result = x + y; + suChinese.append(CNUMBERS[x]); + suChinese.append("+"); + suChinese.append(CNUMBERS[y]); + } + } + else if (randomoperands == 2) + { + if (x >= y) + { + result = x - y; + suChinese.append(CNUMBERS[x]); + suChinese.append("-"); + suChinese.append(CNUMBERS[y]); + } + else + { + result = y - x; + suChinese.append(CNUMBERS[y]); + suChinese.append("-"); + suChinese.append(CNUMBERS[x]); + } + } + suChinese.append("=?@" + result); + return suChinese.toString(); + } +} \ No newline at end of file diff --git a/src/main/java/com/ruoyi/framework/config/MybatisPlusConfig.java b/src/main/java/com/ruoyi/framework/config/MybatisPlusConfig.java new file mode 100644 index 0000000..62d5c20 --- /dev/null +++ b/src/main/java/com/ruoyi/framework/config/MybatisPlusConfig.java @@ -0,0 +1,62 @@ +package com.ruoyi.framework.config; + +import com.baomidou.mybatisplus.annotation.DbType; +import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; +import com.baomidou.mybatisplus.extension.plugins.inner.BlockAttackInnerInterceptor; +import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor; +import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.transaction.annotation.EnableTransactionManagement; + +/** + * Mybatis Plus 配置 + * + * @author ruoyi + */ +@EnableTransactionManagement(proxyTargetClass = true) +@Configuration +public class MybatisPlusConfig +{ + @Bean + public MybatisPlusInterceptor mybatisPlusInterceptor() + { + MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); + // 分页插件 + interceptor.addInnerInterceptor(paginationInnerInterceptor()); + // 乐观锁插件 + interceptor.addInnerInterceptor(optimisticLockerInnerInterceptor()); + // 阻断插件 + interceptor.addInnerInterceptor(blockAttackInnerInterceptor()); + return interceptor; + } + + /** + * 分页插件,自动识别数据库类型 https://baomidou.com/guide/interceptor-pagination.html + */ + public PaginationInnerInterceptor paginationInnerInterceptor() + { + PaginationInnerInterceptor paginationInnerInterceptor = new PaginationInnerInterceptor(); + // 设置数据库类型为mysql + paginationInnerInterceptor.setDbType(DbType.MYSQL); + // 设置最大单页限制数量,默认 500 条,-1 不受限制 + paginationInnerInterceptor.setMaxLimit(-1L); + return paginationInnerInterceptor; + } + + /** + * 乐观锁插件 https://baomidou.com/guide/interceptor-optimistic-locker.html + */ + public OptimisticLockerInnerInterceptor optimisticLockerInnerInterceptor() + { + return new OptimisticLockerInnerInterceptor(); + } + + /** + * 如果是对全表的删除或更新操作,就会终止该操作 https://baomidou.com/guide/interceptor-block-attack.html + */ + public BlockAttackInnerInterceptor blockAttackInnerInterceptor() + { + return new BlockAttackInnerInterceptor(); + } +} diff --git a/src/main/java/com/ruoyi/framework/config/RedisConfig.java b/src/main/java/com/ruoyi/framework/config/RedisConfig.java new file mode 100644 index 0000000..3f4f485 --- /dev/null +++ b/src/main/java/com/ruoyi/framework/config/RedisConfig.java @@ -0,0 +1,69 @@ +package com.ruoyi.framework.config; + +import org.springframework.cache.annotation.CachingConfigurerSupport; +import org.springframework.cache.annotation.EnableCaching; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.redis.connection.RedisConnectionFactory; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.core.script.DefaultRedisScript; +import org.springframework.data.redis.serializer.StringRedisSerializer; + +/** + * redis配置 + * + * @author ruoyi + */ +@Configuration +@EnableCaching +public class RedisConfig extends CachingConfigurerSupport +{ + @Bean + @SuppressWarnings(value = { "unchecked", "rawtypes" }) + public RedisTemplate redisTemplate(RedisConnectionFactory connectionFactory) + { + RedisTemplate template = new RedisTemplate<>(); + template.setConnectionFactory(connectionFactory); + + FastJson2JsonRedisSerializer serializer = new FastJson2JsonRedisSerializer(Object.class); + + // 使用StringRedisSerializer来序列化和反序列化redis的key值 + template.setKeySerializer(new StringRedisSerializer()); + template.setValueSerializer(serializer); + + // Hash的key也采用StringRedisSerializer的序列化方式 + template.setHashKeySerializer(new StringRedisSerializer()); + template.setHashValueSerializer(serializer); + + template.afterPropertiesSet(); + return template; + } + + @Bean + public DefaultRedisScript limitScript() + { + DefaultRedisScript redisScript = new DefaultRedisScript<>(); + redisScript.setScriptText(limitScriptText()); + redisScript.setResultType(Long.class); + return redisScript; + } + + /** + * 限流脚本 + */ + private String limitScriptText() + { + return "local key = KEYS[1]\n" + + "local count = tonumber(ARGV[1])\n" + + "local time = tonumber(ARGV[2])\n" + + "local current = redis.call('get', key);\n" + + "if current and tonumber(current) > count then\n" + + " return tonumber(current);\n" + + "end\n" + + "current = redis.call('incr', key)\n" + + "if tonumber(current) == 1 then\n" + + " redis.call('expire', key, time)\n" + + "end\n" + + "return tonumber(current);"; + } +} diff --git a/src/main/java/com/ruoyi/framework/config/ResourcesConfig.java b/src/main/java/com/ruoyi/framework/config/ResourcesConfig.java new file mode 100644 index 0000000..41c2f8b --- /dev/null +++ b/src/main/java/com/ruoyi/framework/config/ResourcesConfig.java @@ -0,0 +1,72 @@ +package com.ruoyi.framework.config; + +import java.util.concurrent.TimeUnit; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.http.CacheControl; +import org.springframework.web.cors.CorsConfiguration; +import org.springframework.web.cors.UrlBasedCorsConfigurationSource; +import org.springframework.web.filter.CorsFilter; +import org.springframework.web.servlet.config.annotation.InterceptorRegistry; +import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; +import com.ruoyi.common.constant.Constants; +import com.ruoyi.framework.interceptor.RepeatSubmitInterceptor; + +/** + * 通用配置 + * + * @author ruoyi + */ +@Configuration +public class ResourcesConfig implements WebMvcConfigurer +{ + @Autowired + private RepeatSubmitInterceptor repeatSubmitInterceptor; + + @Override + public void addResourceHandlers(ResourceHandlerRegistry registry) + { + /** 本地文件上传路径 */ + registry.addResourceHandler(Constants.RESOURCE_PREFIX + "/**") + .addResourceLocations("file:" + RuoYiConfig.getProfile() + "/"); + + /** swagger配置 */ + registry.addResourceHandler("/swagger-ui/**") + .addResourceLocations("classpath:/META-INF/resources/webjars/springfox-swagger-ui/") + .setCacheControl(CacheControl.maxAge(5, TimeUnit.HOURS).cachePublic());; + } + + /** + * 自定义拦截规则 + */ + @Override + public void addInterceptors(InterceptorRegistry registry) + { + registry.addInterceptor(repeatSubmitInterceptor).addPathPatterns("/**"); + } + + /** + * 跨域配置 + */ + @Bean + public CorsFilter corsFilter() + { + CorsConfiguration config = new CorsConfiguration(); + config.setAllowCredentials(true); + // 设置访问源地址 + config.addAllowedOriginPattern("*"); + // 设置访问源请求头 + config.addAllowedHeader("*"); + // 设置访问源请求方法 + config.addAllowedMethod("*"); + // 有效期 1800秒 + config.setMaxAge(1800L); + // 添加映射路径,拦截一切请求 + UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); + source.registerCorsConfiguration("/**", config); + // 返回新的CorsFilter + return new CorsFilter(source); + } +} diff --git a/src/main/java/com/ruoyi/framework/config/RuoYiConfig.java b/src/main/java/com/ruoyi/framework/config/RuoYiConfig.java new file mode 100644 index 0000000..fe1407e --- /dev/null +++ b/src/main/java/com/ruoyi/framework/config/RuoYiConfig.java @@ -0,0 +1,111 @@ +package com.ruoyi.framework.config; + +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; + +/** + * 读取项目相关配置 + * + * @author ruoyi + */ +@Component +@ConfigurationProperties(prefix = "ruoyi") +public class RuoYiConfig +{ + /** 项目名称 */ + private String name; + + /** 版本 */ + private String version; + + /** 版权年份 */ + private String copyrightYear; + + /** 上传路径 */ + private static String profile; + + /** 获取地址开关 */ + private static boolean addressEnabled; + + public String getName() + { + return name; + } + + public void setName(String name) + { + this.name = name; + } + + public String getVersion() + { + return version; + } + + public void setVersion(String version) + { + this.version = version; + } + + public String getCopyrightYear() + { + return copyrightYear; + } + + public void setCopyrightYear(String copyrightYear) + { + this.copyrightYear = copyrightYear; + } + + public static String getProfile() + { + return profile; + } + + public void setProfile(String profile) + { + RuoYiConfig.profile = profile; + } + + public static boolean isAddressEnabled() + { + return addressEnabled; + } + + public void setAddressEnabled(boolean addressEnabled) + { + RuoYiConfig.addressEnabled = addressEnabled; + } + + /** + * 获取导入上传路径 + */ + public static String getImportPath() + { + return getProfile() + "/import"; + } + + /** + * 获取头像上传路径 + */ + public static String getAvatarPath() + { + return getProfile() + "/avatar"; + } + + /** + * 获取下载路径 + */ + public static String getDownloadPath() + { + return getProfile() + "/download/"; + } + + /** + * 获取上传路径 + */ + public static String getUploadPath() + { + return getProfile() + "/upload"; + } +} \ No newline at end of file diff --git a/src/main/java/com/ruoyi/framework/config/ScheduleConfig.java b/src/main/java/com/ruoyi/framework/config/ScheduleConfig.java new file mode 100644 index 0000000..347247a --- /dev/null +++ b/src/main/java/com/ruoyi/framework/config/ScheduleConfig.java @@ -0,0 +1,57 @@ +//package com.ruoyi.framework.config; +// +//import org.springframework.context.annotation.Bean; +//import org.springframework.context.annotation.Configuration; +//import org.springframework.scheduling.quartz.SchedulerFactoryBean; +//import javax.sql.DataSource; +//import java.util.Properties; +// +///** +// * 定时任务配置(单机部署建议默认走内存,如需集群需要创建qrtz数据库表/打开类注释) +// * +// * @author ruoyi +// */ +//@Configuration +//public class ScheduleConfig +//{ +// @Bean +// public SchedulerFactoryBean schedulerFactoryBean(DataSource dataSource) +// { +// SchedulerFactoryBean factory = new SchedulerFactoryBean(); +// factory.setDataSource(dataSource); +// +// // quartz参数 +// Properties prop = new Properties(); +// prop.put("org.quartz.scheduler.instanceName", "RuoyiScheduler"); +// prop.put("org.quartz.scheduler.instanceId", "AUTO"); +// // 线程池配置 +// prop.put("org.quartz.threadPool.class", "org.quartz.simpl.SimpleThreadPool"); +// prop.put("org.quartz.threadPool.threadCount", "20"); +// prop.put("org.quartz.threadPool.threadPriority", "5"); +// // JobStore配置 +// prop.put("org.quartz.jobStore.class", "org.springframework.scheduling.quartz.LocalDataSourceJobStore"); +// // 集群配置 +// prop.put("org.quartz.jobStore.isClustered", "true"); +// prop.put("org.quartz.jobStore.clusterCheckinInterval", "15000"); +// prop.put("org.quartz.jobStore.maxMisfiresToHandleAtATime", "10"); +// prop.put("org.quartz.jobStore.txIsolationLevelSerializable", "true"); +// +// // sqlserver 启用 +// // prop.put("org.quartz.jobStore.selectWithLockSQL", "SELECT * FROM {0}LOCKS UPDLOCK WHERE LOCK_NAME = ?"); +// prop.put("org.quartz.jobStore.misfireThreshold", "12000"); +// prop.put("org.quartz.jobStore.tablePrefix", "QRTZ_"); +// factory.setQuartzProperties(prop); +// +// factory.setSchedulerName("RuoyiScheduler"); +// // 延时启动 +// factory.setStartupDelay(1); +// factory.setApplicationContextSchedulerContextKey("applicationContextKey"); +// // 可选,QuartzScheduler +// // 启动时更新己存在的Job,这样就不用每次修改targetObject后删除qrtz_job_details表对应记录了 +// factory.setOverwriteExistingJobs(true); +// // 设置自动启动,默认为true +// factory.setAutoStartup(true); +// +// return factory; +// } +//} diff --git a/src/main/java/com/ruoyi/framework/config/SecurityConfig.java b/src/main/java/com/ruoyi/framework/config/SecurityConfig.java new file mode 100644 index 0000000..2125853 --- /dev/null +++ b/src/main/java/com/ruoyi/framework/config/SecurityConfig.java @@ -0,0 +1,148 @@ +package com.ruoyi.framework.config; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.http.HttpMethod; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; +import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; +import org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer; +import org.springframework.security.config.http.SessionCreationPolicy; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; +import org.springframework.security.web.authentication.logout.LogoutFilter; +import org.springframework.web.filter.CorsFilter; +import com.ruoyi.framework.config.properties.PermitAllUrlProperties; +import com.ruoyi.framework.security.filter.JwtAuthenticationTokenFilter; +import com.ruoyi.framework.security.handle.AuthenticationEntryPointImpl; +import com.ruoyi.framework.security.handle.LogoutSuccessHandlerImpl; + +/** + * spring security配置 + * + * @author ruoyi + */ +@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true) +public class SecurityConfig extends WebSecurityConfigurerAdapter +{ + /** + * 自定义用户认证逻辑 + */ + @Autowired + private UserDetailsService userDetailsService; + + /** + * 认证失败处理类 + */ + @Autowired + private AuthenticationEntryPointImpl unauthorizedHandler; + + /** + * 退出处理类 + */ + @Autowired + private LogoutSuccessHandlerImpl logoutSuccessHandler; + + /** + * token认证过滤器 + */ + @Autowired + private JwtAuthenticationTokenFilter authenticationTokenFilter; + + /** + * 跨域过滤器 + */ + @Autowired + private CorsFilter corsFilter; + + /** + * 允许匿名访问的地址 + */ + @Autowired + private PermitAllUrlProperties permitAllUrl; + + /** + * 解决 无法直接注入 AuthenticationManager + * + * @return + * @throws Exception + */ + @Bean + @Override + public AuthenticationManager authenticationManagerBean() throws Exception + { + return super.authenticationManagerBean(); + } + + /** + * anyRequest | 匹配所有请求路径 + * access | SpringEl表达式结果为true时可以访问 + * anonymous | 匿名可以访问 + * denyAll | 用户不能访问 + * fullyAuthenticated | 用户完全认证可以访问(非remember-me下自动登录) + * hasAnyAuthority | 如果有参数,参数表示权限,则其中任何一个权限可以访问 + * hasAnyRole | 如果有参数,参数表示角色,则其中任何一个角色可以访问 + * hasAuthority | 如果有参数,参数表示权限,则其权限可以访问 + * hasIpAddress | 如果有参数,参数表示IP地址,如果用户IP和参数匹配,则可以访问 + * hasRole | 如果有参数,参数表示角色,则其角色可以访问 + * permitAll | 用户可以任意访问 + * rememberMe | 允许通过remember-me登录的用户访问 + * authenticated | 用户登录后可访问 + */ + @Override + protected void configure(HttpSecurity httpSecurity) throws Exception + { + // 注解标记允许匿名访问的url + ExpressionUrlAuthorizationConfigurer.ExpressionInterceptUrlRegistry registry = httpSecurity.authorizeRequests(); + permitAllUrl.getUrls().forEach(url -> registry.antMatchers(url).permitAll()); + + httpSecurity + // CSRF禁用,因为不使用session + .csrf().disable() + // 禁用HTTP响应标头 + .headers().cacheControl().disable().and() + // 认证失败处理类 + .exceptionHandling().authenticationEntryPoint(unauthorizedHandler).and() + // 基于token,所以不需要session + .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and() + // 过滤请求 + .authorizeRequests() + // 对于登录login 注册register 验证码captchaImage 允许匿名访问 + .antMatchers("/login", "/register", "/captchaImage").permitAll() + // 静态资源,可匿名访问 + .antMatchers(HttpMethod.GET, "/", "/*.html", "/**/*.html", "/**/*.css", "/**/*.js", "/profile/**").permitAll() + .antMatchers("/swagger-ui.html", "/swagger-resources/**", "/webjars/**", "/*/api-docs", "/druid/**").permitAll() + // 除上面外的所有请求全部需要鉴权认证 + .anyRequest().authenticated() + .and() + .headers().frameOptions().disable(); + // 添加Logout filter + httpSecurity.logout().logoutUrl("/logout").logoutSuccessHandler(logoutSuccessHandler); + // 添加JWT filter + httpSecurity.addFilterBefore(authenticationTokenFilter, UsernamePasswordAuthenticationFilter.class); + // 添加CORS filter + httpSecurity.addFilterBefore(corsFilter, JwtAuthenticationTokenFilter.class); + httpSecurity.addFilterBefore(corsFilter, LogoutFilter.class); + } + + /** + * 强散列哈希加密实现 + */ + @Bean + public BCryptPasswordEncoder bCryptPasswordEncoder() + { + return new BCryptPasswordEncoder(); + } + + /** + * 身份认证接口 + */ + @Override + protected void configure(AuthenticationManagerBuilder auth) throws Exception + { + auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder()); + } +} diff --git a/src/main/java/com/ruoyi/framework/config/ServerConfig.java b/src/main/java/com/ruoyi/framework/config/ServerConfig.java new file mode 100644 index 0000000..b5b7de3 --- /dev/null +++ b/src/main/java/com/ruoyi/framework/config/ServerConfig.java @@ -0,0 +1,32 @@ +package com.ruoyi.framework.config; + +import javax.servlet.http.HttpServletRequest; +import org.springframework.stereotype.Component; +import com.ruoyi.common.utils.ServletUtils; + +/** + * 服务相关配置 + * + * @author ruoyi + */ +@Component +public class ServerConfig +{ + /** + * 获取完整的请求路径,包括:域名,端口,上下文访问路径 + * + * @return 服务地址 + */ + public String getUrl() + { + HttpServletRequest request = ServletUtils.getRequest(); + return getDomain(request); + } + + public static String getDomain(HttpServletRequest request) + { + StringBuffer url = request.getRequestURL(); + String contextPath = request.getServletContext().getContextPath(); + return url.delete(url.length() - request.getRequestURI().length(), url.length()).append(contextPath).toString(); + } +} diff --git a/src/main/java/com/ruoyi/framework/config/SwaggerConfig.java b/src/main/java/com/ruoyi/framework/config/SwaggerConfig.java new file mode 100644 index 0000000..01073ad --- /dev/null +++ b/src/main/java/com/ruoyi/framework/config/SwaggerConfig.java @@ -0,0 +1,126 @@ +package com.ruoyi.framework.config; + +import java.util.ArrayList; +import java.util.List; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import io.swagger.annotations.ApiOperation; +import io.swagger.models.auth.In; +import springfox.documentation.builders.ApiInfoBuilder; +import springfox.documentation.builders.PathSelectors; +import springfox.documentation.builders.RequestHandlerSelectors; +import springfox.documentation.service.ApiInfo; +import springfox.documentation.service.ApiKey; +import springfox.documentation.service.AuthorizationScope; +import springfox.documentation.service.Contact; +import springfox.documentation.service.SecurityReference; +import springfox.documentation.service.SecurityScheme; +import springfox.documentation.spi.DocumentationType; +import springfox.documentation.spi.service.contexts.SecurityContext; +import springfox.documentation.spring.web.plugins.Docket; + +/** + * Swagger2的接口配置 + * + * @author ruoyi + */ +@Configuration +public class SwaggerConfig { + /** + * 系统基础配置 + */ + @Autowired + private RuoYiConfig ruoyiConfig; + + /** + * 是否开启swagger + */ + @Value("${swagger.enabled}") + private boolean enabled; + + /** + * 设置请求的统一前缀 + */ + @Value("${swagger.pathMapping}") + private String pathMapping; + + /** + * 创建API + */ + @Bean + public Docket createRestApi() { + return new Docket(DocumentationType.OAS_30) + // 是否启用Swagger + .enable(enabled) + // 用来创建该API的基本信息,展示在文档的页面中(自定义展示的信息) + .apiInfo(apiInfo()) + // 设置哪些接口暴露给Swagger展示 + .select() + // 扫描所有有注解的api,用这种方式更灵活 +// .apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class)) + // 扫描指定包中的swagger注解 + .apis(RequestHandlerSelectors.basePackage("com.ruoyi.project.tool.swagger") + .or(RequestHandlerSelectors.basePackage("com.ruoyi.project.about.controller"))) + // 扫描所有 .apis(RequestHandlerSelectors.any()) + .paths(PathSelectors.any()) + .build() + /* 设置安全模式,swagger可以设置访问token */ + .securitySchemes(securitySchemes()) + .securityContexts(securityContexts()) + .pathMapping(pathMapping); + } + + /** + * 安全模式,这里指定token通过Authorization头请求头传递 + */ + private List securitySchemes() { + List apiKeyList = new ArrayList(); + apiKeyList.add(new ApiKey("Authorization" , "Authorization" , In.HEADER.toValue())); + return apiKeyList; + } + + /** + * 安全上下文 + */ + private List securityContexts() { + List securityContexts = new ArrayList<>(); + securityContexts.add( + SecurityContext.builder() + .securityReferences(defaultAuth()) + .operationSelector(o -> o.requestMappingPattern().matches("/.*")) + .build()); + return securityContexts; + } + + /** + * 默认的安全上引用 + */ + private List defaultAuth() { + AuthorizationScope authorizationScope = new AuthorizationScope("global" , "accessEverything"); + AuthorizationScope[] authorizationScopes = new AuthorizationScope[1]; + authorizationScopes[0] = authorizationScope; + List securityReferences = new ArrayList<>(); + securityReferences.add(new SecurityReference("Authorization" , authorizationScopes)); + return securityReferences; + } + + /** + * 添加摘要信息 + */ + private ApiInfo apiInfo() { + // 用ApiInfoBuilder进行定制 + return new ApiInfoBuilder() + // 设置标题 + .title("标题:管理后台接口文档") + // 描述 + .description("描述:用于管理后台信息...") + // 作者信息 + .contact(new Contact(ruoyiConfig.getName(), null, null)) + // 版本 + .version("版本号:" + ruoyiConfig.getVersion()) + .build(); + } +} diff --git a/src/main/java/com/ruoyi/framework/config/ThreadPoolConfig.java b/src/main/java/com/ruoyi/framework/config/ThreadPoolConfig.java new file mode 100644 index 0000000..7840141 --- /dev/null +++ b/src/main/java/com/ruoyi/framework/config/ThreadPoolConfig.java @@ -0,0 +1,63 @@ +package com.ruoyi.framework.config; + +import com.ruoyi.common.utils.Threads; +import org.apache.commons.lang3.concurrent.BasicThreadFactory; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledThreadPoolExecutor; +import java.util.concurrent.ThreadPoolExecutor; + +/** + * 线程池配置 + * + * @author ruoyi + **/ +@Configuration +public class ThreadPoolConfig +{ + // 核心线程池大小 + private int corePoolSize = 50; + + // 最大可创建的线程数 + private int maxPoolSize = 200; + + // 队列最大长度 + private int queueCapacity = 1000; + + // 线程池维护线程所允许的空闲时间 + private int keepAliveSeconds = 300; + + @Bean(name = "threadPoolTaskExecutor") + public ThreadPoolTaskExecutor threadPoolTaskExecutor() + { + ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); + executor.setMaxPoolSize(maxPoolSize); + executor.setCorePoolSize(corePoolSize); + executor.setQueueCapacity(queueCapacity); + executor.setKeepAliveSeconds(keepAliveSeconds); + // 线程池对拒绝任务(无线程可用)的处理策略 + executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); + return executor; + } + + /** + * 执行周期性或定时任务 + */ + @Bean(name = "scheduledExecutorService") + protected ScheduledExecutorService scheduledExecutorService() + { + return new ScheduledThreadPoolExecutor(corePoolSize, + new BasicThreadFactory.Builder().namingPattern("schedule-pool-%d").daemon(true).build(), + new ThreadPoolExecutor.CallerRunsPolicy()) + { + @Override + protected void afterExecute(Runnable r, Throwable t) + { + super.afterExecute(r, t); + Threads.printException(r, t); + } + }; + } +} diff --git a/src/main/java/com/ruoyi/framework/config/properties/DruidProperties.java b/src/main/java/com/ruoyi/framework/config/properties/DruidProperties.java new file mode 100644 index 0000000..c8a5c8a --- /dev/null +++ b/src/main/java/com/ruoyi/framework/config/properties/DruidProperties.java @@ -0,0 +1,89 @@ +package com.ruoyi.framework.config.properties; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Configuration; +import com.alibaba.druid.pool.DruidDataSource; + +/** + * druid 配置属性 + * + * @author ruoyi + */ +@Configuration +public class DruidProperties +{ + @Value("${spring.datasource.druid.initialSize}") + private int initialSize; + + @Value("${spring.datasource.druid.minIdle}") + private int minIdle; + + @Value("${spring.datasource.druid.maxActive}") + private int maxActive; + + @Value("${spring.datasource.druid.maxWait}") + private int maxWait; + + @Value("${spring.datasource.druid.connectTimeout}") + private int connectTimeout; + + @Value("${spring.datasource.druid.socketTimeout}") + private int socketTimeout; + + @Value("${spring.datasource.druid.timeBetweenEvictionRunsMillis}") + private int timeBetweenEvictionRunsMillis; + + @Value("${spring.datasource.druid.minEvictableIdleTimeMillis}") + private int minEvictableIdleTimeMillis; + + @Value("${spring.datasource.druid.maxEvictableIdleTimeMillis}") + private int maxEvictableIdleTimeMillis; + + @Value("${spring.datasource.druid.validationQuery}") + private String validationQuery; + + @Value("${spring.datasource.druid.testWhileIdle}") + private boolean testWhileIdle; + + @Value("${spring.datasource.druid.testOnBorrow}") + private boolean testOnBorrow; + + @Value("${spring.datasource.druid.testOnReturn}") + private boolean testOnReturn; + + public DruidDataSource dataSource(DruidDataSource datasource) + { + /** 配置初始化大小、最小、最大 */ + datasource.setInitialSize(initialSize); + datasource.setMaxActive(maxActive); + datasource.setMinIdle(minIdle); + + /** 配置获取连接等待超时的时间 */ + datasource.setMaxWait(maxWait); + + /** 配置驱动连接超时时间,检测数据库建立连接的超时时间,单位是毫秒 */ + datasource.setConnectTimeout(connectTimeout); + + /** 配置网络超时时间,等待数据库操作完成的网络超时时间,单位是毫秒 */ + datasource.setSocketTimeout(socketTimeout); + + /** 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 */ + datasource.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis); + + /** 配置一个连接在池中最小、最大生存的时间,单位是毫秒 */ + datasource.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis); + datasource.setMaxEvictableIdleTimeMillis(maxEvictableIdleTimeMillis); + + /** + * 用来检测连接是否有效的sql,要求是一个查询语句,常用select 'x'。如果validationQuery为null,testOnBorrow、testOnReturn、testWhileIdle都不会起作用。 + */ + datasource.setValidationQuery(validationQuery); + /** 建议配置为true,不影响性能,并且保证安全性。申请连接的时候检测,如果空闲时间大于timeBetweenEvictionRunsMillis,执行validationQuery检测连接是否有效。 */ + datasource.setTestWhileIdle(testWhileIdle); + /** 申请连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。 */ + datasource.setTestOnBorrow(testOnBorrow); + /** 归还连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。 */ + datasource.setTestOnReturn(testOnReturn); + return datasource; + } +} diff --git a/src/main/java/com/ruoyi/framework/config/properties/PermitAllUrlProperties.java b/src/main/java/com/ruoyi/framework/config/properties/PermitAllUrlProperties.java new file mode 100644 index 0000000..a913610 --- /dev/null +++ b/src/main/java/com/ruoyi/framework/config/properties/PermitAllUrlProperties.java @@ -0,0 +1,73 @@ +package com.ruoyi.framework.config.properties; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.regex.Pattern; +import org.apache.commons.lang3.RegExUtils; +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.annotation.AnnotationUtils; +import org.springframework.web.method.HandlerMethod; +import org.springframework.web.servlet.mvc.method.RequestMappingInfo; +import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping; +import com.ruoyi.framework.aspectj.lang.annotation.Anonymous; + +/** + * 设置Anonymous注解允许匿名访问的url + * + * @author ruoyi + */ +@Configuration +public class PermitAllUrlProperties implements InitializingBean, ApplicationContextAware +{ + private static final Pattern PATTERN = Pattern.compile("\\{(.*?)\\}"); + + private ApplicationContext applicationContext; + + private List urls = new ArrayList<>(); + + public String ASTERISK = "*"; + + @Override + public void afterPropertiesSet() + { + RequestMappingHandlerMapping mapping = applicationContext.getBean(RequestMappingHandlerMapping.class); + Map map = mapping.getHandlerMethods(); + + map.keySet().forEach(info -> { + HandlerMethod handlerMethod = map.get(info); + + // 获取方法上边的注解 替代path variable 为 * + Anonymous method = AnnotationUtils.findAnnotation(handlerMethod.getMethod(), Anonymous.class); + Optional.ofNullable(method).ifPresent(anonymous -> Objects.requireNonNull(info.getPatternsCondition().getPatterns()) + .forEach(url -> urls.add(RegExUtils.replaceAll(url, PATTERN, ASTERISK)))); + + // 获取类上边的注解, 替代path variable 为 * + Anonymous controller = AnnotationUtils.findAnnotation(handlerMethod.getBeanType(), Anonymous.class); + Optional.ofNullable(controller).ifPresent(anonymous -> Objects.requireNonNull(info.getPatternsCondition().getPatterns()) + .forEach(url -> urls.add(RegExUtils.replaceAll(url, PATTERN, ASTERISK)))); + }); + } + + @Override + public void setApplicationContext(ApplicationContext context) throws BeansException + { + this.applicationContext = context; + } + + public List getUrls() + { + return urls; + } + + public void setUrls(List urls) + { + this.urls = urls; + } +} diff --git a/src/main/java/com/ruoyi/framework/datasource/DynamicDataSource.java b/src/main/java/com/ruoyi/framework/datasource/DynamicDataSource.java new file mode 100644 index 0000000..e70b8cf --- /dev/null +++ b/src/main/java/com/ruoyi/framework/datasource/DynamicDataSource.java @@ -0,0 +1,26 @@ +package com.ruoyi.framework.datasource; + +import java.util.Map; +import javax.sql.DataSource; +import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource; + +/** + * 动态数据源 + * + * @author ruoyi + */ +public class DynamicDataSource extends AbstractRoutingDataSource +{ + public DynamicDataSource(DataSource defaultTargetDataSource, Map targetDataSources) + { + super.setDefaultTargetDataSource(defaultTargetDataSource); + super.setTargetDataSources(targetDataSources); + super.afterPropertiesSet(); + } + + @Override + protected Object determineCurrentLookupKey() + { + return DynamicDataSourceContextHolder.getDataSourceType(); + } +} \ No newline at end of file diff --git a/src/main/java/com/ruoyi/framework/datasource/DynamicDataSourceContextHolder.java b/src/main/java/com/ruoyi/framework/datasource/DynamicDataSourceContextHolder.java new file mode 100644 index 0000000..9770af6 --- /dev/null +++ b/src/main/java/com/ruoyi/framework/datasource/DynamicDataSourceContextHolder.java @@ -0,0 +1,45 @@ +package com.ruoyi.framework.datasource; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * 数据源切换处理 + * + * @author ruoyi + */ +public class DynamicDataSourceContextHolder +{ + public static final Logger log = LoggerFactory.getLogger(DynamicDataSourceContextHolder.class); + + /** + * 使用ThreadLocal维护变量,ThreadLocal为每个使用该变量的线程提供独立的变量副本, + * 所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。 + */ + private static final ThreadLocal CONTEXT_HOLDER = new ThreadLocal<>(); + + /** + * 设置数据源的变量 + */ + public static void setDataSourceType(String dsType) + { + log.info("切换到{}数据源", dsType); + CONTEXT_HOLDER.set(dsType); + } + + /** + * 获得数据源的变量 + */ + public static String getDataSourceType() + { + return CONTEXT_HOLDER.get(); + } + + /** + * 清空数据源变量 + */ + public static void clearDataSourceType() + { + CONTEXT_HOLDER.remove(); + } +} diff --git a/src/main/java/com/ruoyi/framework/interceptor/RepeatSubmitInterceptor.java b/src/main/java/com/ruoyi/framework/interceptor/RepeatSubmitInterceptor.java new file mode 100644 index 0000000..d8ac4c9 --- /dev/null +++ b/src/main/java/com/ruoyi/framework/interceptor/RepeatSubmitInterceptor.java @@ -0,0 +1,56 @@ +package com.ruoyi.framework.interceptor; + +import java.lang.reflect.Method; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import org.springframework.stereotype.Component; +import org.springframework.web.method.HandlerMethod; +import org.springframework.web.servlet.HandlerInterceptor; +import com.alibaba.fastjson2.JSON; +import com.ruoyi.common.utils.ServletUtils; +import com.ruoyi.framework.interceptor.annotation.RepeatSubmit; +import com.ruoyi.framework.web.domain.AjaxResult; + +/** + * 防止重复提交拦截器 + * + * @author ruoyi + */ +@Component +public abstract class RepeatSubmitInterceptor implements HandlerInterceptor +{ + @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, annotation)) + { + AjaxResult ajaxResult = AjaxResult.error(annotation.message()); + ServletUtils.renderString(response, JSON.toJSONString(ajaxResult)); + return false; + } + } + return true; + } + else + { + return true; + } + } + + /** + * 验证是否重复提交由子类实现具体的防重复提交的规则 + * + * @param request 请求信息 + * @param annotation 防重复注解参数 + * @return 结果 + * @throws Exception + */ + public abstract boolean isRepeatSubmit(HttpServletRequest request, RepeatSubmit annotation); +} diff --git a/src/main/java/com/ruoyi/framework/interceptor/annotation/RepeatSubmit.java b/src/main/java/com/ruoyi/framework/interceptor/annotation/RepeatSubmit.java new file mode 100644 index 0000000..1191d21 --- /dev/null +++ b/src/main/java/com/ruoyi/framework/interceptor/annotation/RepeatSubmit.java @@ -0,0 +1,31 @@ +package com.ruoyi.framework.interceptor.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 +{ + /** + * 间隔时间(ms),小于此时间视为重复提交 + */ + public int interval() default 5000; + + /** + * 提示消息 + */ + public String message() default "不允许重复提交,请稍后再试"; +} \ No newline at end of file diff --git a/src/main/java/com/ruoyi/framework/interceptor/impl/SameUrlDataInterceptor.java b/src/main/java/com/ruoyi/framework/interceptor/impl/SameUrlDataInterceptor.java new file mode 100644 index 0000000..299eff7 --- /dev/null +++ b/src/main/java/com/ruoyi/framework/interceptor/impl/SameUrlDataInterceptor.java @@ -0,0 +1,110 @@ +package com.ruoyi.framework.interceptor.impl; + +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.TimeUnit; +import javax.servlet.http.HttpServletRequest; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; +import com.alibaba.fastjson2.JSON; +import com.ruoyi.common.constant.CacheConstants; +import com.ruoyi.common.filter.RepeatedlyRequestWrapper; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.http.HttpHelper; +import com.ruoyi.framework.interceptor.RepeatSubmitInterceptor; +import com.ruoyi.framework.interceptor.annotation.RepeatSubmit; +import com.ruoyi.framework.redis.RedisCache; + +/** + * 判断请求url和数据是否和上一次相同, + * 如果和上次相同,则是重复提交表单。 有效时间为10秒内。 + * + * @author ruoyi + */ +@Component +public class SameUrlDataInterceptor extends RepeatSubmitInterceptor +{ + public final String REPEAT_PARAMS = "repeatParams"; + + public final String REPEAT_TIME = "repeatTime"; + + // 令牌自定义标识 + @Value("${token.header}") + private String header; + + @Autowired + private RedisCache redisCache; + + @SuppressWarnings("unchecked") + @Override + public boolean isRepeatSubmit(HttpServletRequest request, RepeatSubmit annotation) + { + String nowParams = ""; + if (request instanceof RepeatedlyRequestWrapper) + { + RepeatedlyRequestWrapper repeatedlyRequest = (RepeatedlyRequestWrapper) request; + nowParams = HttpHelper.getBodyString(repeatedlyRequest); + } + + // body参数为空,获取Parameter的数据 + if (StringUtils.isEmpty(nowParams)) + { + nowParams = JSON.toJSONString(request.getParameterMap()); + } + Map nowDataMap = new HashMap(); + nowDataMap.put(REPEAT_PARAMS, nowParams); + nowDataMap.put(REPEAT_TIME, System.currentTimeMillis()); + + // 请求地址(作为存放cache的key值) + String url = request.getRequestURI(); + + // 唯一值(没有消息头则使用请求地址) + String submitKey = StringUtils.trimToEmpty(request.getHeader(header)); + + // 唯一标识(指定key + url + 消息头) + String cacheRepeatKey = CacheConstants.REPEAT_SUBMIT_KEY + url + submitKey; + + Object sessionObj = redisCache.getCacheObject(cacheRepeatKey); + if (sessionObj != null) + { + Map sessionMap = (Map) sessionObj; + if (sessionMap.containsKey(url)) + { + Map preDataMap = (Map) sessionMap.get(url); + if (compareParams(nowDataMap, preDataMap) && compareTime(nowDataMap, preDataMap, annotation.interval())) + { + return true; + } + } + } + Map cacheMap = new HashMap(); + cacheMap.put(url, nowDataMap); + redisCache.setCacheObject(cacheRepeatKey, cacheMap, annotation.interval(), TimeUnit.MILLISECONDS); + return false; + } + + /** + * 判断参数是否相同 + */ + private boolean compareParams(Map nowMap, Map preMap) + { + String nowParams = (String) nowMap.get(REPEAT_PARAMS); + String preParams = (String) preMap.get(REPEAT_PARAMS); + return nowParams.equals(preParams); + } + + /** + * 判断两次间隔时间 + */ + private boolean compareTime(Map nowMap, Map preMap, int interval) + { + long time1 = (Long) nowMap.get(REPEAT_TIME); + long time2 = (Long) preMap.get(REPEAT_TIME); + if ((time1 - time2) < interval) + { + return true; + } + return false; + } +} diff --git a/src/main/java/com/ruoyi/framework/manager/AsyncManager.java b/src/main/java/com/ruoyi/framework/manager/AsyncManager.java new file mode 100644 index 0000000..7387a02 --- /dev/null +++ b/src/main/java/com/ruoyi/framework/manager/AsyncManager.java @@ -0,0 +1,55 @@ +package com.ruoyi.framework.manager; + +import java.util.TimerTask; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; +import com.ruoyi.common.utils.Threads; +import com.ruoyi.common.utils.spring.SpringUtils; + +/** + * 异步任务管理器 + * + * @author ruoyi + */ +public class AsyncManager +{ + /** + * 操作延迟10毫秒 + */ + private final int OPERATE_DELAY_TIME = 10; + + /** + * 异步操作任务调度线程池 + */ + private ScheduledExecutorService executor = SpringUtils.getBean("scheduledExecutorService"); + + /** + * 单例模式 + */ + private AsyncManager(){} + + private static AsyncManager me = new AsyncManager(); + + public static AsyncManager me() + { + return me; + } + + /** + * 执行任务 + * + * @param task 任务 + */ + public void execute(TimerTask task) + { + executor.schedule(task, OPERATE_DELAY_TIME, TimeUnit.MILLISECONDS); + } + + /** + * 停止任务线程池 + */ + public void shutdown() + { + Threads.shutdownAndAwaitTermination(executor); + } +} diff --git a/src/main/java/com/ruoyi/framework/manager/ShutdownManager.java b/src/main/java/com/ruoyi/framework/manager/ShutdownManager.java new file mode 100644 index 0000000..e36ca3c --- /dev/null +++ b/src/main/java/com/ruoyi/framework/manager/ShutdownManager.java @@ -0,0 +1,39 @@ +package com.ruoyi.framework.manager; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Component; +import javax.annotation.PreDestroy; + +/** + * 确保应用退出时能关闭后台线程 + * + * @author ruoyi + */ +@Component +public class ShutdownManager +{ + private static final Logger logger = LoggerFactory.getLogger("sys-user"); + + @PreDestroy + public void destroy() + { + shutdownAsyncManager(); + } + + /** + * 停止异步执行任务 + */ + private void shutdownAsyncManager() + { + try + { + logger.info("====关闭后台任务任务线程池===="); + AsyncManager.me().shutdown(); + } + catch (Exception e) + { + logger.error(e.getMessage(), e); + } + } +} diff --git a/src/main/java/com/ruoyi/framework/manager/factory/AsyncFactory.java b/src/main/java/com/ruoyi/framework/manager/factory/AsyncFactory.java new file mode 100644 index 0000000..1360b40 --- /dev/null +++ b/src/main/java/com/ruoyi/framework/manager/factory/AsyncFactory.java @@ -0,0 +1,102 @@ +package com.ruoyi.framework.manager.factory; + +import java.util.TimerTask; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import com.ruoyi.common.constant.Constants; +import com.ruoyi.common.utils.LogUtils; +import com.ruoyi.common.utils.ServletUtils; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.ip.AddressUtils; +import com.ruoyi.common.utils.ip.IpUtils; +import com.ruoyi.common.utils.spring.SpringUtils; +import com.ruoyi.project.monitor.domain.SysLogininfor; +import com.ruoyi.project.monitor.domain.SysOperLog; +import com.ruoyi.project.monitor.service.ISysLogininforService; +import com.ruoyi.project.monitor.service.ISysOperLogService; +import eu.bitwalker.useragentutils.UserAgent; + +/** + * 异步工厂(产生任务用) + * + * @author ruoyi + */ +public class AsyncFactory +{ + private static final Logger sys_user_logger = LoggerFactory.getLogger("sys-user"); + + /** + * 记录登录信息 + * + * @param username 用户名 + * @param status 状态 + * @param message 消息 + * @param args 列表 + * @return 任务task + */ + public static TimerTask recordLogininfor(final String username, final String status, final String message, + final Object... args) + { + final UserAgent userAgent = UserAgent.parseUserAgentString(ServletUtils.getRequest().getHeader("User-Agent")); + final String ip = IpUtils.getIpAddr(); + return new TimerTask() + { + @Override + public void run() + { + String address = AddressUtils.getRealAddressByIP(ip); + StringBuilder s = new StringBuilder(); + s.append(LogUtils.getBlock(ip)); + s.append(address); + s.append(LogUtils.getBlock(username)); + s.append(LogUtils.getBlock(status)); + s.append(LogUtils.getBlock(message)); + // 打印信息到日志 + sys_user_logger.info(s.toString(), args); + // 获取客户端操作系统 + String os = userAgent.getOperatingSystem().getName(); + // 获取客户端浏览器 + String browser = userAgent.getBrowser().getName(); + // 封装对象 + SysLogininfor logininfor = new SysLogininfor(); + logininfor.setUserName(username); + logininfor.setIpaddr(ip); + logininfor.setLoginLocation(address); + logininfor.setBrowser(browser); + logininfor.setOs(os); + logininfor.setMsg(message); + // 日志状态 + 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); + } + // 插入数据 + SpringUtils.getBean(ISysLogininforService.class).insertLogininfor(logininfor); + } + }; + } + + /** + * 操作日志记录 + * + * @param operLog 操作日志信息 + * @return 任务task + */ + public static TimerTask recordOper(final SysOperLog operLog) + { + return new TimerTask() + { + @Override + public void run() + { + // 远程查询操作地点 + operLog.setOperLocation(AddressUtils.getRealAddressByIP(operLog.getOperIp())); + SpringUtils.getBean(ISysOperLogService.class).insertOperlog(operLog); + } + }; + } +} diff --git a/src/main/java/com/ruoyi/framework/redis/RedisCache.java b/src/main/java/com/ruoyi/framework/redis/RedisCache.java new file mode 100644 index 0000000..0aebd37 --- /dev/null +++ b/src/main/java/com/ruoyi/framework/redis/RedisCache.java @@ -0,0 +1,268 @@ +package com.ruoyi.framework.redis; + +import java.util.Collection; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.TimeUnit; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.BoundSetOperations; +import org.springframework.data.redis.core.HashOperations; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.core.ValueOperations; +import org.springframework.stereotype.Component; + +/** + * spring redis 工具类 + * + * @author ruoyi + **/ +@SuppressWarnings(value = { "unchecked", "rawtypes" }) +@Component +public class RedisCache +{ + @Autowired + public RedisTemplate redisTemplate; + + /** + * 缓存基本的对象,Integer、String、实体类等 + * + * @param key 缓存的键值 + * @param value 缓存的值 + */ + public void setCacheObject(final String key, final T value) + { + redisTemplate.opsForValue().set(key, value); + } + + /** + * 缓存基本的对象,Integer、String、实体类等 + * + * @param key 缓存的键值 + * @param value 缓存的值 + * @param timeout 时间 + * @param timeUnit 时间颗粒度 + */ + public void setCacheObject(final String key, final T value, final Integer timeout, final TimeUnit timeUnit) + { + redisTemplate.opsForValue().set(key, value, timeout, timeUnit); + } + + /** + * 设置有效时间 + * + * @param key Redis键 + * @param timeout 超时时间 + * @return true=设置成功;false=设置失败 + */ + public boolean expire(final String key, final long timeout) + { + return expire(key, timeout, TimeUnit.SECONDS); + } + + /** + * 设置有效时间 + * + * @param key Redis键 + * @param timeout 超时时间 + * @param unit 时间单位 + * @return true=设置成功;false=设置失败 + */ + public boolean expire(final String key, final long timeout, final TimeUnit unit) + { + return redisTemplate.expire(key, timeout, unit); + } + + /** + * 获取有效时间 + * + * @param key Redis键 + * @return 有效时间 + */ + public long getExpire(final String key) + { + return redisTemplate.getExpire(key); + } + + /** + * 判断 key是否存在 + * + * @param key 键 + * @return true 存在 false不存在 + */ + public Boolean hasKey(String key) + { + return redisTemplate.hasKey(key); + } + + /** + * 获得缓存的基本对象。 + * + * @param key 缓存键值 + * @return 缓存键值对应的数据 + */ + public T getCacheObject(final String key) + { + ValueOperations operation = redisTemplate.opsForValue(); + return operation.get(key); + } + + /** + * 删除单个对象 + * + * @param key + */ + public boolean deleteObject(final String key) + { + return redisTemplate.delete(key); + } + + /** + * 删除集合对象 + * + * @param collection 多个对象 + * @return + */ + public boolean deleteObject(final Collection collection) + { + return redisTemplate.delete(collection) > 0; + } + + /** + * 缓存List数据 + * + * @param key 缓存的键值 + * @param dataList 待缓存的List数据 + * @return 缓存的对象 + */ + public long setCacheList(final String key, final List dataList) + { + Long count = redisTemplate.opsForList().rightPushAll(key, dataList); + return count == null ? 0 : count; + } + + /** + * 获得缓存的list对象 + * + * @param key 缓存的键值 + * @return 缓存键值对应的数据 + */ + public List getCacheList(final String key) + { + return redisTemplate.opsForList().range(key, 0, -1); + } + + /** + * 缓存Set + * + * @param key 缓存键值 + * @param dataSet 缓存的数据 + * @return 缓存数据的对象 + */ + public BoundSetOperations setCacheSet(final String key, final Set dataSet) + { + BoundSetOperations setOperation = redisTemplate.boundSetOps(key); + Iterator it = dataSet.iterator(); + while (it.hasNext()) + { + setOperation.add(it.next()); + } + return setOperation; + } + + /** + * 获得缓存的set + * + * @param key + * @return + */ + public Set getCacheSet(final String key) + { + return redisTemplate.opsForSet().members(key); + } + + /** + * 缓存Map + * + * @param key + * @param dataMap + */ + public void setCacheMap(final String key, final Map dataMap) + { + if (dataMap != null) { + redisTemplate.opsForHash().putAll(key, dataMap); + } + } + + /** + * 获得缓存的Map + * + * @param key + * @return + */ + public Map getCacheMap(final String key) + { + return redisTemplate.opsForHash().entries(key); + } + + /** + * 往Hash中存入数据 + * + * @param key Redis键 + * @param hKey Hash键 + * @param value 值 + */ + public void setCacheMapValue(final String key, final String hKey, final T value) + { + redisTemplate.opsForHash().put(key, hKey, value); + } + + /** + * 获取Hash中的数据 + * + * @param key Redis键 + * @param hKey Hash键 + * @return Hash中的对象 + */ + public T getCacheMapValue(final String key, final String hKey) + { + HashOperations opsForHash = redisTemplate.opsForHash(); + return opsForHash.get(key, hKey); + } + + /** + * 获取多个Hash中的数据 + * + * @param key Redis键 + * @param hKeys Hash键集合 + * @return Hash对象集合 + */ + public List getMultiCacheMapValue(final String key, final Collection hKeys) + { + return redisTemplate.opsForHash().multiGet(key, hKeys); + } + + /** + * 删除Hash中的某条数据 + * + * @param key Redis键 + * @param hKey Hash键 + * @return 是否成功 + */ + public boolean deleteCacheMapValue(final String key, final String hKey) + { + return redisTemplate.opsForHash().delete(key, hKey) > 0; + } + + /** + * 获得缓存的基本对象列表 + * + * @param pattern 字符串前缀 + * @return 对象列表 + */ + public Collection keys(final String pattern) + { + return redisTemplate.keys(pattern); + } +} diff --git a/src/main/java/com/ruoyi/framework/security/LoginBody.java b/src/main/java/com/ruoyi/framework/security/LoginBody.java new file mode 100644 index 0000000..ddfac39 --- /dev/null +++ b/src/main/java/com/ruoyi/framework/security/LoginBody.java @@ -0,0 +1,69 @@ +package com.ruoyi.framework.security; + +/** + * 用户登录对象 + * + * @author ruoyi + */ +public class LoginBody +{ + /** + * 用户名 + */ + private String username; + + /** + * 用户密码 + */ + private String password; + + /** + * 验证码 + */ + private String code; + + /** + * 唯一标识 + */ + private String uuid; + + public String getUsername() + { + return username; + } + + public void setUsername(String username) + { + this.username = username; + } + + public String getPassword() + { + return password; + } + + public void setPassword(String password) + { + this.password = password; + } + + public String getCode() + { + return code; + } + + public void setCode(String code) + { + this.code = code; + } + + public String getUuid() + { + return uuid; + } + + public void setUuid(String uuid) + { + this.uuid = uuid; + } +} diff --git a/src/main/java/com/ruoyi/framework/security/LoginUser.java b/src/main/java/com/ruoyi/framework/security/LoginUser.java new file mode 100644 index 0000000..6445e09 --- /dev/null +++ b/src/main/java/com/ruoyi/framework/security/LoginUser.java @@ -0,0 +1,266 @@ +package com.ruoyi.framework.security; + +import java.util.Collection; +import java.util.Set; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.userdetails.UserDetails; +import com.alibaba.fastjson2.annotation.JSONField; +import com.ruoyi.project.system.domain.SysUser; + +/** + * 登录用户身份权限 + * + * @author ruoyi + */ +public class LoginUser implements UserDetails +{ + private static final long serialVersionUID = 1L; + + /** + * 用户ID + */ + private Long userId; + + /** + * 部门ID + */ + private Long deptId; + + /** + * 用户唯一标识 + */ + private String token; + + /** + * 登录时间 + */ + private Long loginTime; + + /** + * 过期时间 + */ + private Long expireTime; + + /** + * 登录IP地址 + */ + private String ipaddr; + + /** + * 登录地点 + */ + private String loginLocation; + + /** + * 浏览器类型 + */ + private String browser; + + /** + * 操作系统 + */ + private String os; + + /** + * 权限列表 + */ + private Set permissions; + + /** + * 用户信息 + */ + private SysUser user; + + public LoginUser() + { + } + + public LoginUser(SysUser user, Set permissions) + { + this.user = user; + this.permissions = permissions; + } + + public LoginUser(Long userId, Long deptId, SysUser user, Set permissions) + { + this.userId = userId; + this.deptId = deptId; + this.user = user; + this.permissions = permissions; + } + + public Long getUserId() + { + return userId; + } + + public void setUserId(Long userId) + { + this.userId = userId; + } + + public Long getDeptId() + { + return deptId; + } + + public void setDeptId(Long deptId) + { + this.deptId = deptId; + } + + public String getToken() + { + return token; + } + + public void setToken(String token) + { + this.token = token; + } + + @JSONField(serialize = false) + @Override + public String getPassword() + { + return user.getPassword(); + } + + @Override + public String getUsername() + { + return user.getUserName(); + } + + /** + * 账户是否未过期,过期无法验证 + */ + @JSONField(serialize = false) + @Override + public boolean isAccountNonExpired() + { + return true; + } + + /** + * 指定用户是否解锁,锁定的用户无法进行身份验证 + * + * @return + */ + @JSONField(serialize = false) + @Override + public boolean isAccountNonLocked() + { + return true; + } + + /** + * 指示是否已过期的用户的凭据(密码),过期的凭据防止认证 + * + * @return + */ + @JSONField(serialize = false) + @Override + public boolean isCredentialsNonExpired() + { + return true; + } + + /** + * 是否可用 ,禁用的用户不能身份验证 + * + * @return + */ + @JSONField(serialize = false) + @Override + public boolean isEnabled() + { + return true; + } + + public Long getLoginTime() + { + return loginTime; + } + + public void setLoginTime(Long loginTime) + { + this.loginTime = loginTime; + } + + public String getIpaddr() + { + return ipaddr; + } + + public void setIpaddr(String ipaddr) + { + this.ipaddr = ipaddr; + } + + public String getLoginLocation() + { + return loginLocation; + } + + public void setLoginLocation(String loginLocation) + { + this.loginLocation = loginLocation; + } + + public String getBrowser() + { + return browser; + } + + public void setBrowser(String browser) + { + this.browser = browser; + } + + public String getOs() + { + return os; + } + + public void setOs(String os) + { + this.os = os; + } + + public Long getExpireTime() + { + return expireTime; + } + + public void setExpireTime(Long expireTime) + { + this.expireTime = expireTime; + } + + public Set getPermissions() + { + return permissions; + } + + public void setPermissions(Set permissions) + { + this.permissions = permissions; + } + + public SysUser getUser() + { + return user; + } + + public void setUser(SysUser user) + { + this.user = user; + } + + @Override + public Collection getAuthorities() + { + return null; + } +} diff --git a/src/main/java/com/ruoyi/framework/security/RegisterBody.java b/src/main/java/com/ruoyi/framework/security/RegisterBody.java new file mode 100644 index 0000000..42be381 --- /dev/null +++ b/src/main/java/com/ruoyi/framework/security/RegisterBody.java @@ -0,0 +1,11 @@ +package com.ruoyi.framework.security; + +/** + * 用户注册对象 + * + * @author ruoyi + */ +public class RegisterBody extends LoginBody +{ + +} diff --git a/src/main/java/com/ruoyi/framework/security/context/AuthenticationContextHolder.java b/src/main/java/com/ruoyi/framework/security/context/AuthenticationContextHolder.java new file mode 100644 index 0000000..6c776ce --- /dev/null +++ b/src/main/java/com/ruoyi/framework/security/context/AuthenticationContextHolder.java @@ -0,0 +1,28 @@ +package com.ruoyi.framework.security.context; + +import org.springframework.security.core.Authentication; + +/** + * 身份验证信息 + * + * @author ruoyi + */ +public class AuthenticationContextHolder +{ + private static final ThreadLocal contextHolder = new ThreadLocal<>(); + + public static Authentication getContext() + { + return contextHolder.get(); + } + + public static void setContext(Authentication context) + { + contextHolder.set(context); + } + + public static void clearContext() + { + contextHolder.remove(); + } +} diff --git a/src/main/java/com/ruoyi/framework/security/context/PermissionContextHolder.java b/src/main/java/com/ruoyi/framework/security/context/PermissionContextHolder.java new file mode 100644 index 0000000..5472f3d --- /dev/null +++ b/src/main/java/com/ruoyi/framework/security/context/PermissionContextHolder.java @@ -0,0 +1,27 @@ +package com.ruoyi.framework.security.context; + +import org.springframework.web.context.request.RequestAttributes; +import org.springframework.web.context.request.RequestContextHolder; +import com.ruoyi.common.core.text.Convert; + +/** + * 权限信息 + * + * @author ruoyi + */ +public class PermissionContextHolder +{ + private static final String PERMISSION_CONTEXT_ATTRIBUTES = "PERMISSION_CONTEXT"; + + public static void setContext(String permission) + { + RequestContextHolder.currentRequestAttributes().setAttribute(PERMISSION_CONTEXT_ATTRIBUTES, permission, + RequestAttributes.SCOPE_REQUEST); + } + + public static String getContext() + { + return Convert.toStr(RequestContextHolder.currentRequestAttributes().getAttribute(PERMISSION_CONTEXT_ATTRIBUTES, + RequestAttributes.SCOPE_REQUEST)); + } +} diff --git a/src/main/java/com/ruoyi/framework/security/filter/JwtAuthenticationTokenFilter.java b/src/main/java/com/ruoyi/framework/security/filter/JwtAuthenticationTokenFilter.java new file mode 100644 index 0000000..fa8a654 --- /dev/null +++ b/src/main/java/com/ruoyi/framework/security/filter/JwtAuthenticationTokenFilter.java @@ -0,0 +1,44 @@ +package com.ruoyi.framework.security.filter; + +import java.io.IOException; +import javax.servlet.FilterChain; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.web.authentication.WebAuthenticationDetailsSource; +import org.springframework.stereotype.Component; +import org.springframework.web.filter.OncePerRequestFilter; +import com.ruoyi.common.utils.SecurityUtils; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.framework.security.LoginUser; +import com.ruoyi.framework.security.service.TokenService; + +/** + * token过滤器 验证token有效性 + * + * @author ruoyi + */ +@Component +public class JwtAuthenticationTokenFilter extends OncePerRequestFilter +{ + @Autowired + private TokenService tokenService; + + @Override + protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) + throws ServletException, IOException + { + LoginUser loginUser = tokenService.getLoginUser(request); + if (StringUtils.isNotNull(loginUser) && StringUtils.isNull(SecurityUtils.getAuthentication())) + { + tokenService.verifyToken(loginUser); + UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(loginUser, null, loginUser.getAuthorities()); + authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); + SecurityContextHolder.getContext().setAuthentication(authenticationToken); + } + chain.doFilter(request, response); + } +} diff --git a/src/main/java/com/ruoyi/framework/security/handle/AuthenticationEntryPointImpl.java b/src/main/java/com/ruoyi/framework/security/handle/AuthenticationEntryPointImpl.java new file mode 100644 index 0000000..f605021 --- /dev/null +++ b/src/main/java/com/ruoyi/framework/security/handle/AuthenticationEntryPointImpl.java @@ -0,0 +1,34 @@ +package com.ruoyi.framework.security.handle; + +import java.io.IOException; +import java.io.Serializable; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.web.AuthenticationEntryPoint; +import org.springframework.stereotype.Component; +import com.alibaba.fastjson2.JSON; +import com.ruoyi.common.constant.HttpStatus; +import com.ruoyi.common.utils.ServletUtils; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.framework.web.domain.AjaxResult; + +/** + * 认证失败处理类 返回未授权 + * + * @author ruoyi + */ +@Component +public class AuthenticationEntryPointImpl implements AuthenticationEntryPoint, Serializable +{ + private static final long serialVersionUID = -8970718410437077606L; + + @Override + public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException e) + throws IOException + { + int code = HttpStatus.UNAUTHORIZED; + String msg = StringUtils.format("请求访问:{},认证失败,无法访问系统资源", request.getRequestURI()); + ServletUtils.renderString(response, JSON.toJSONString(AjaxResult.error(code, msg))); + } +} diff --git a/src/main/java/com/ruoyi/framework/security/handle/LogoutSuccessHandlerImpl.java b/src/main/java/com/ruoyi/framework/security/handle/LogoutSuccessHandlerImpl.java new file mode 100644 index 0000000..16328d4 --- /dev/null +++ b/src/main/java/com/ruoyi/framework/security/handle/LogoutSuccessHandlerImpl.java @@ -0,0 +1,52 @@ +package com.ruoyi.framework.security.handle; + +import java.io.IOException; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.core.Authentication; +import org.springframework.security.web.authentication.logout.LogoutSuccessHandler; +import com.alibaba.fastjson2.JSON; +import com.ruoyi.common.constant.Constants; +import com.ruoyi.common.utils.ServletUtils; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.framework.manager.AsyncManager; +import com.ruoyi.framework.manager.factory.AsyncFactory; +import com.ruoyi.framework.security.LoginUser; +import com.ruoyi.framework.security.service.TokenService; +import com.ruoyi.framework.web.domain.AjaxResult; + +/** + * 自定义退出处理类 返回成功 + * + * @author ruoyi + */ +@Configuration +public class LogoutSuccessHandlerImpl implements LogoutSuccessHandler +{ + @Autowired + private TokenService tokenService; + + /** + * 退出处理 + * + * @return + */ + @Override + public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) + throws IOException, ServletException + { + LoginUser loginUser = tokenService.getLoginUser(request); + if (StringUtils.isNotNull(loginUser)) + { + String userName = loginUser.getUsername(); + // 删除用户缓存记录 + tokenService.delLoginUser(loginUser.getToken()); + // 记录用户退出日志 + AsyncManager.me().execute(AsyncFactory.recordLogininfor(userName, Constants.LOGOUT, "退出成功")); + } + ServletUtils.renderString(response, JSON.toJSONString(AjaxResult.success("退出成功"))); + } +} diff --git a/src/main/java/com/ruoyi/framework/security/service/PermissionService.java b/src/main/java/com/ruoyi/framework/security/service/PermissionService.java new file mode 100644 index 0000000..426d0a7 --- /dev/null +++ b/src/main/java/com/ruoyi/framework/security/service/PermissionService.java @@ -0,0 +1,159 @@ +package com.ruoyi.framework.security.service; + +import java.util.Set; +import org.springframework.stereotype.Service; +import org.springframework.util.CollectionUtils; +import com.ruoyi.common.constant.Constants; +import com.ruoyi.common.utils.SecurityUtils; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.framework.security.LoginUser; +import com.ruoyi.framework.security.context.PermissionContextHolder; +import com.ruoyi.project.system.domain.SysRole; + +/** + * RuoYi首创 自定义权限实现,ss取自SpringSecurity首字母 + * + * @author ruoyi + */ +@Service("ss") +public class PermissionService +{ + /** + * 验证用户是否具备某权限 + * + * @param permission 权限字符串 + * @return 用户是否具备某权限 + */ + public boolean hasPermi(String permission) + { + if (StringUtils.isEmpty(permission)) + { + return false; + } + LoginUser loginUser = SecurityUtils.getLoginUser(); + if (StringUtils.isNull(loginUser) || CollectionUtils.isEmpty(loginUser.getPermissions())) + { + return false; + } + PermissionContextHolder.setContext(permission); + return hasPermissions(loginUser.getPermissions(), permission); + } + + /** + * 验证用户是否不具备某权限,与 hasPermi逻辑相反 + * + * @param permission 权限字符串 + * @return 用户是否不具备某权限 + */ + public boolean lacksPermi(String permission) + { + return hasPermi(permission) != true; + } + + /** + * 验证用户是否具有以下任意一个权限 + * + * @param permissions 以 PERMISSION_DELIMETER 为分隔符的权限列表 + * @return 用户是否具有以下任意一个权限 + */ + public boolean hasAnyPermi(String permissions) + { + if (StringUtils.isEmpty(permissions)) + { + return false; + } + LoginUser loginUser = SecurityUtils.getLoginUser(); + if (StringUtils.isNull(loginUser) || CollectionUtils.isEmpty(loginUser.getPermissions())) + { + return false; + } + PermissionContextHolder.setContext(permissions); + Set authorities = loginUser.getPermissions(); + for (String permission : permissions.split(Constants.PERMISSION_DELIMETER)) + { + if (permission != null && hasPermissions(authorities, permission)) + { + return true; + } + } + return false; + } + + /** + * 判断用户是否拥有某个角色 + * + * @param role 角色字符串 + * @return 用户是否具备某角色 + */ + public boolean hasRole(String role) + { + if (StringUtils.isEmpty(role)) + { + return false; + } + LoginUser loginUser = SecurityUtils.getLoginUser(); + if (StringUtils.isNull(loginUser) || CollectionUtils.isEmpty(loginUser.getUser().getRoles())) + { + return false; + } + for (SysRole sysRole : loginUser.getUser().getRoles()) + { + String roleKey = sysRole.getRoleKey(); + if (Constants.SUPER_ADMIN.equals(roleKey) || roleKey.equals(StringUtils.trim(role))) + { + return true; + } + } + return false; + } + + /** + * 验证用户是否不具备某角色,与 isRole逻辑相反。 + * + * @param role 角色名称 + * @return 用户是否不具备某角色 + */ + public boolean lacksRole(String role) + { + return hasRole(role) != true; + } + + /** + * 验证用户是否具有以下任意一个角色 + * + * @param roles 以 ROLE_NAMES_DELIMETER 为分隔符的角色列表 + * @return 用户是否具有以下任意一个角色 + */ + public boolean hasAnyRoles(String roles) + { + if (StringUtils.isEmpty(roles)) + { + return false; + } + LoginUser loginUser = SecurityUtils.getLoginUser(); + if (StringUtils.isNull(loginUser) || CollectionUtils.isEmpty(loginUser.getUser().getRoles())) + { + return false; + } + for (String role : roles.split(Constants.ROLE_DELIMETER)) + { + if (hasRole(role)) + { + return true; + } + } + return false; + } + + /** + * 判断是否包含权限 + * + * @param permissions 权限列表 + * @param permission 权限字符串 + * @return 用户是否具备某权限 + */ + private boolean hasPermissions(Set permissions, String permission) + { + return permissions.contains(Constants.ALL_PERMISSION) || permissions.contains(StringUtils.trim(permission)); + } +} diff --git a/src/main/java/com/ruoyi/framework/security/service/SysLoginService.java b/src/main/java/com/ruoyi/framework/security/service/SysLoginService.java new file mode 100644 index 0000000..1b3464d --- /dev/null +++ b/src/main/java/com/ruoyi/framework/security/service/SysLoginService.java @@ -0,0 +1,181 @@ +package com.ruoyi.framework.security.service; + +import javax.annotation.Resource; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.authentication.BadCredentialsException; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.stereotype.Component; +import com.ruoyi.common.constant.CacheConstants; +import com.ruoyi.common.constant.Constants; +import com.ruoyi.common.constant.UserConstants; +import com.ruoyi.common.exception.ServiceException; +import com.ruoyi.common.exception.user.BlackListException; +import com.ruoyi.common.exception.user.CaptchaException; +import com.ruoyi.common.exception.user.CaptchaExpireException; +import com.ruoyi.common.exception.user.UserNotExistsException; +import com.ruoyi.common.exception.user.UserPasswordNotMatchException; +import com.ruoyi.common.utils.DateUtils; +import com.ruoyi.common.utils.MessageUtils; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.ip.IpUtils; +import com.ruoyi.framework.manager.AsyncManager; +import com.ruoyi.framework.manager.factory.AsyncFactory; +import com.ruoyi.framework.redis.RedisCache; +import com.ruoyi.framework.security.LoginUser; +import com.ruoyi.framework.security.context.AuthenticationContextHolder; +import com.ruoyi.project.system.domain.SysUser; +import com.ruoyi.project.system.service.ISysConfigService; +import com.ruoyi.project.system.service.ISysUserService; + +/** + * 登录校验方法 + * + * @author ruoyi + */ +@Component +public class SysLoginService +{ + @Autowired + private TokenService tokenService; + + @Resource + private AuthenticationManager authenticationManager; + + @Autowired + private RedisCache redisCache; + + @Autowired + private ISysUserService userService; + + @Autowired + private ISysConfigService configService; + + /** + * 登录验证 + * + * @param username 用户名 + * @param password 密码 + * @param code 验证码 + * @param uuid 唯一标识 + * @return 结果 + */ + public String login(String username, String password, String code, String uuid) + { + // 验证码校验 + validateCaptcha(username, code, uuid); + // 登录前置校验 + loginPreCheck(username, password); + // 用户验证 + Authentication authentication = null; + try + { + UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(username, password); + AuthenticationContextHolder.setContext(authenticationToken); + // 该方法会去调用UserDetailsServiceImpl.loadUserByUsername + authentication = authenticationManager.authenticate(authenticationToken); + } + catch (Exception e) + { + if (e instanceof BadCredentialsException) + { + AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.password.not.match"))); + throw new UserPasswordNotMatchException(); + } + else + { + AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, e.getMessage())); + throw new ServiceException(e.getMessage()); + } + } + finally + { + AuthenticationContextHolder.clearContext(); + } + AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success"))); + LoginUser loginUser = (LoginUser) authentication.getPrincipal(); + recordLoginInfo(loginUser.getUserId()); + // 生成token + return tokenService.createToken(loginUser); + } + + /** + * 校验验证码 + * + * @param username 用户名 + * @param code 验证码 + * @param uuid 唯一标识 + * @return 结果 + */ + public void validateCaptcha(String username, String code, String uuid) + { + boolean captchaEnabled = configService.selectCaptchaEnabled(); + if (captchaEnabled) + { + String verifyKey = CacheConstants.CAPTCHA_CODE_KEY + StringUtils.nvl(uuid, ""); + String captcha = redisCache.getCacheObject(verifyKey); + redisCache.deleteObject(verifyKey); + if (captcha == null) + { + AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.expire"))); + throw new CaptchaExpireException(); + } + if (!code.equalsIgnoreCase(captcha)) + { + AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.error"))); + throw new CaptchaException(); + } + } + } + + /** + * 登录前置校验 + * @param username 用户名 + * @param password 用户密码 + */ + public void loginPreCheck(String username, String password) + { + // 用户名或密码为空 错误 + if (StringUtils.isEmpty(username) || StringUtils.isEmpty(password)) + { + AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("not.null"))); + throw new UserNotExistsException(); + } + // 密码如果不在指定范围内 错误 + if (password.length() < UserConstants.PASSWORD_MIN_LENGTH + || password.length() > UserConstants.PASSWORD_MAX_LENGTH) + { + AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.password.not.match"))); + throw new UserPasswordNotMatchException(); + } + // 用户名不在指定范围内 错误 + if (username.length() < UserConstants.USERNAME_MIN_LENGTH + || username.length() > UserConstants.USERNAME_MAX_LENGTH) + { + AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.password.not.match"))); + throw new UserPasswordNotMatchException(); + } + // IP黑名单校验 + String blackStr = configService.selectConfigByKey("sys.login.blackIPList"); + if (IpUtils.isMatchedIp(blackStr, IpUtils.getIpAddr())) + { + AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("login.blocked"))); + throw new BlackListException(); + } + } + + /** + * 记录登录信息 + * + * @param userId 用户ID + */ + public void recordLoginInfo(Long userId) + { + SysUser sysUser = new SysUser(); + sysUser.setUserId(userId); + sysUser.setLoginIp(IpUtils.getIpAddr()); + sysUser.setLoginDate(DateUtils.getNowDate()); + userService.updateUserProfile(sysUser); + } +} diff --git a/src/main/java/com/ruoyi/framework/security/service/SysPasswordService.java b/src/main/java/com/ruoyi/framework/security/service/SysPasswordService.java new file mode 100644 index 0000000..9a61731 --- /dev/null +++ b/src/main/java/com/ruoyi/framework/security/service/SysPasswordService.java @@ -0,0 +1,86 @@ +package com.ruoyi.framework.security.service; + +import java.util.concurrent.TimeUnit; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.security.core.Authentication; +import org.springframework.stereotype.Component; +import com.ruoyi.common.constant.CacheConstants; +import com.ruoyi.common.exception.user.UserPasswordNotMatchException; +import com.ruoyi.common.exception.user.UserPasswordRetryLimitExceedException; +import com.ruoyi.common.utils.SecurityUtils; +import com.ruoyi.framework.redis.RedisCache; +import com.ruoyi.framework.security.context.AuthenticationContextHolder; +import com.ruoyi.project.system.domain.SysUser; + +/** + * 登录密码方法 + * + * @author ruoyi + */ +@Component +public class SysPasswordService +{ + @Autowired + private RedisCache redisCache; + + @Value(value = "${user.password.maxRetryCount}") + private int maxRetryCount; + + @Value(value = "${user.password.lockTime}") + private int lockTime; + + /** + * 登录账户密码错误次数缓存键名 + * + * @param username 用户名 + * @return 缓存键key + */ + private String getCacheKey(String username) + { + return CacheConstants.PWD_ERR_CNT_KEY + username; + } + + public void validate(SysUser user) + { + Authentication usernamePasswordAuthenticationToken = AuthenticationContextHolder.getContext(); + String username = usernamePasswordAuthenticationToken.getName(); + String password = usernamePasswordAuthenticationToken.getCredentials().toString(); + + Integer retryCount = redisCache.getCacheObject(getCacheKey(username)); + + if (retryCount == null) + { + retryCount = 0; + } + + if (retryCount >= Integer.valueOf(maxRetryCount).intValue()) + { + throw new UserPasswordRetryLimitExceedException(maxRetryCount, lockTime); + } + + if (!matches(user, password)) + { + retryCount = retryCount + 1; + redisCache.setCacheObject(getCacheKey(username), retryCount, lockTime, TimeUnit.MINUTES); + throw new UserPasswordNotMatchException(); + } + else + { + clearLoginRecordCache(username); + } + } + + public boolean matches(SysUser user, String rawPassword) + { + return SecurityUtils.matchesPassword(rawPassword, user.getPassword()); + } + + public void clearLoginRecordCache(String loginName) + { + if (redisCache.hasKey(getCacheKey(loginName))) + { + redisCache.deleteObject(getCacheKey(loginName)); + } + } +} diff --git a/src/main/java/com/ruoyi/framework/security/service/SysPermissionService.java b/src/main/java/com/ruoyi/framework/security/service/SysPermissionService.java new file mode 100644 index 0000000..4b80431 --- /dev/null +++ b/src/main/java/com/ruoyi/framework/security/service/SysPermissionService.java @@ -0,0 +1,83 @@ +package com.ruoyi.framework.security.service; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.springframework.util.CollectionUtils; +import com.ruoyi.project.system.domain.SysRole; +import com.ruoyi.project.system.domain.SysUser; +import com.ruoyi.project.system.service.ISysMenuService; +import com.ruoyi.project.system.service.ISysRoleService; + +/** + * 用户权限处理 + * + * @author ruoyi + */ +@Component +public class SysPermissionService +{ + @Autowired + private ISysRoleService roleService; + + @Autowired + private ISysMenuService menuService; + + /** + * 获取角色数据权限 + * + * @param user 用户信息 + * @return 角色权限信息 + */ + public Set getRolePermission(SysUser user) + { + Set roles = new HashSet(); + // 管理员拥有所有权限 + if (user.isAdmin()) + { + roles.add("admin"); + } + else + { + roles.addAll(roleService.selectRolePermissionByUserId(user.getUserId())); + } + return roles; + } + + /** + * 获取菜单数据权限 + * + * @param user 用户信息 + * @return 菜单权限信息 + */ + public Set getMenuPermission(SysUser user) + { + Set perms = new HashSet(); + // 管理员拥有所有权限 + if (user.isAdmin()) + { + perms.add("*:*:*"); + } + else + { + List roles = user.getRoles(); + if (!CollectionUtils.isEmpty(roles)) + { + // 多角色设置permissions属性,以便数据权限匹配权限 + for (SysRole role : roles) + { + Set rolePerms = menuService.selectMenuPermsByRoleId(role.getRoleId()); + role.setPermissions(rolePerms); + perms.addAll(rolePerms); + } + } + else + { + perms.addAll(menuService.selectMenuPermsByUserId(user.getUserId())); + } + } + return perms; + } +} diff --git a/src/main/java/com/ruoyi/framework/security/service/SysRegisterService.java b/src/main/java/com/ruoyi/framework/security/service/SysRegisterService.java new file mode 100644 index 0000000..c8c7509 --- /dev/null +++ b/src/main/java/com/ruoyi/framework/security/service/SysRegisterService.java @@ -0,0 +1,115 @@ +package com.ruoyi.framework.security.service; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import com.ruoyi.common.constant.CacheConstants; +import com.ruoyi.common.constant.Constants; +import com.ruoyi.common.constant.UserConstants; +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.StringUtils; +import com.ruoyi.framework.manager.AsyncManager; +import com.ruoyi.framework.manager.factory.AsyncFactory; +import com.ruoyi.framework.redis.RedisCache; +import com.ruoyi.framework.security.RegisterBody; +import com.ruoyi.project.system.domain.SysUser; +import com.ruoyi.project.system.service.ISysConfigService; +import com.ruoyi.project.system.service.ISysUserService; + +/** + * 注册校验方法 + * + * @author ruoyi + */ +@Component +public class SysRegisterService +{ + @Autowired + private ISysUserService userService; + + @Autowired + private ISysConfigService configService; + + @Autowired + private RedisCache redisCache; + + /** + * 注册 + */ + public String register(RegisterBody registerBody) + { + String msg = "", username = registerBody.getUsername(), password = registerBody.getPassword(); + SysUser sysUser = new SysUser(); + sysUser.setUserName(username); + + // 验证码开关 + boolean captchaEnabled = configService.selectCaptchaEnabled(); + if (captchaEnabled) + { + 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 (!userService.checkUserNameUnique(sysUser)) + { + msg = "保存用户'" + username + "'失败,注册账号已存在"; + } + else + { + sysUser.setNickName(username); + sysUser.setPassword(SecurityUtils.encryptPassword(password)); + boolean regFlag = userService.registerUser(sysUser); + if (!regFlag) + { + msg = "注册失败,请联系系统管理人员"; + } + else + { + AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.REGISTER, MessageUtils.message("user.register.success"))); + } + } + return msg; + } + + /** + * 校验验证码 + * + * @param username 用户名 + * @param code 验证码 + * @param uuid 唯一标识 + * @return 结果 + */ + public void validateCaptcha(String username, String code, String uuid) + { + String verifyKey = CacheConstants.CAPTCHA_CODE_KEY + StringUtils.nvl(uuid, ""); + String captcha = redisCache.getCacheObject(verifyKey); + redisCache.deleteObject(verifyKey); + if (captcha == null) + { + throw new CaptchaExpireException(); + } + if (!code.equalsIgnoreCase(captcha)) + { + throw new CaptchaException(); + } + } +} diff --git a/src/main/java/com/ruoyi/framework/security/service/TokenService.java b/src/main/java/com/ruoyi/framework/security/service/TokenService.java new file mode 100644 index 0000000..81d0159 --- /dev/null +++ b/src/main/java/com/ruoyi/framework/security/service/TokenService.java @@ -0,0 +1,231 @@ +package com.ruoyi.framework.security.service; + +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.TimeUnit; +import javax.servlet.http.HttpServletRequest; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; +import com.ruoyi.common.constant.CacheConstants; +import com.ruoyi.common.constant.Constants; +import com.ruoyi.common.utils.ServletUtils; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.ip.AddressUtils; +import com.ruoyi.common.utils.ip.IpUtils; +import com.ruoyi.common.utils.uuid.IdUtils; +import com.ruoyi.framework.redis.RedisCache; +import com.ruoyi.framework.security.LoginUser; +import eu.bitwalker.useragentutils.UserAgent; +import io.jsonwebtoken.Claims; +import io.jsonwebtoken.Jwts; +import io.jsonwebtoken.SignatureAlgorithm; + +/** + * token验证处理 + * + * @author ruoyi + */ +@Component +public class TokenService +{ + private static final Logger log = LoggerFactory.getLogger(TokenService.class); + + // 令牌自定义标识 + @Value("${token.header}") + private String header; + + // 令牌秘钥 + @Value("${token.secret}") + private String secret; + + // 令牌有效期(默认30分钟) + @Value("${token.expireTime}") + private int expireTime; + + protected static final long MILLIS_SECOND = 1000; + + protected static final long MILLIS_MINUTE = 60 * MILLIS_SECOND; + + private static final Long MILLIS_MINUTE_TEN = 20 * 60 * 1000L; + + @Autowired + private RedisCache redisCache; + + /** + * 获取用户身份信息 + * + * @return 用户信息 + */ + public LoginUser getLoginUser(HttpServletRequest request) + { + // 获取请求携带的令牌 + String token = getToken(request); + 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) + { + log.error("获取用户信息异常'{}'", e.getMessage()); + } + } + return null; + } + + /** + * 设置用户身份信息 + */ + public void setLoginUser(LoginUser loginUser) + { + if (StringUtils.isNotNull(loginUser) && StringUtils.isNotEmpty(loginUser.getToken())) + { + refreshToken(loginUser); + } + } + + /** + * 删除用户身份信息 + */ + public void delLoginUser(String token) + { + if (StringUtils.isNotEmpty(token)) + { + String userKey = getTokenKey(token); + redisCache.deleteObject(userKey); + } + } + + /** + * 创建令牌 + * + * @param loginUser 用户信息 + * @return 令牌 + */ + public String createToken(LoginUser loginUser) + { + String token = IdUtils.fastUUID(); + loginUser.setToken(token); + setUserAgent(loginUser); + refreshToken(loginUser); + + Map claims = new HashMap<>(); + claims.put(Constants.LOGIN_USER_KEY, token); + return createToken(claims); + } + + /** + * 验证令牌有效期,相差不足20分钟,自动刷新缓存 + * + * @param token 令牌 + * @return 令牌 + */ + public void verifyToken(LoginUser loginUser) + { + long expireTime = loginUser.getExpireTime(); + long currentTime = System.currentTimeMillis(); + if (expireTime - currentTime <= MILLIS_MINUTE_TEN) + { + refreshToken(loginUser); + } + } + + /** + * 刷新令牌有效期 + * + * @param loginUser 登录信息 + */ + public void refreshToken(LoginUser loginUser) + { + loginUser.setLoginTime(System.currentTimeMillis()); + loginUser.setExpireTime(loginUser.getLoginTime() + expireTime * MILLIS_MINUTE); + // 根据uuid将loginUser缓存 + String userKey = getTokenKey(loginUser.getToken()); + redisCache.setCacheObject(userKey, loginUser, expireTime, TimeUnit.MINUTES); + } + + /** + * 设置用户代理信息 + * + * @param loginUser 登录信息 + */ + public void setUserAgent(LoginUser loginUser) + { + UserAgent userAgent = UserAgent.parseUserAgentString(ServletUtils.getRequest().getHeader("User-Agent")); + String ip = IpUtils.getIpAddr(); + loginUser.setIpaddr(ip); + loginUser.setLoginLocation(AddressUtils.getRealAddressByIP(ip)); + loginUser.setBrowser(userAgent.getBrowser().getName()); + loginUser.setOs(userAgent.getOperatingSystem().getName()); + } + + /** + * 从数据声明生成令牌 + * + * @param claims 数据声明 + * @return 令牌 + */ + private String createToken(Map claims) + { + String token = Jwts.builder() + .setClaims(claims) + .signWith(SignatureAlgorithm.HS512, secret).compact(); + return token; + } + + /** + * 从令牌中获取数据声明 + * + * @param token 令牌 + * @return 数据声明 + */ + private Claims parseToken(String token) + { + return Jwts.parser() + .setSigningKey(secret) + .parseClaimsJws(token) + .getBody(); + } + + /** + * 从令牌中获取用户名 + * + * @param token 令牌 + * @return 用户名 + */ + public String getUsernameFromToken(String token) + { + Claims claims = parseToken(token); + return claims.getSubject(); + } + + /** + * 获取请求token + * + * @param request + * @return token + */ + private String getToken(HttpServletRequest request) + { + String token = request.getHeader(header); + if (StringUtils.isNotEmpty(token) && token.startsWith(Constants.TOKEN_PREFIX)) + { + token = token.replace(Constants.TOKEN_PREFIX, ""); + } + return token; + } + + private String getTokenKey(String uuid) + { + return CacheConstants.LOGIN_TOKEN_KEY + uuid; + } +} diff --git a/src/main/java/com/ruoyi/framework/security/service/UserDetailsServiceImpl.java b/src/main/java/com/ruoyi/framework/security/service/UserDetailsServiceImpl.java new file mode 100644 index 0000000..82960c6 --- /dev/null +++ b/src/main/java/com/ruoyi/framework/security/service/UserDetailsServiceImpl.java @@ -0,0 +1,65 @@ +package com.ruoyi.framework.security.service; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.stereotype.Service; +import com.ruoyi.common.enums.UserStatus; +import com.ruoyi.common.exception.ServiceException; +import com.ruoyi.common.utils.MessageUtils; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.framework.security.LoginUser; +import com.ruoyi.project.system.domain.SysUser; +import com.ruoyi.project.system.service.ISysUserService; + +/** + * 用户验证处理 + * + * @author ruoyi + */ +@Service +public class UserDetailsServiceImpl implements UserDetailsService +{ + private static final Logger log = LoggerFactory.getLogger(UserDetailsServiceImpl.class); + + @Autowired + private ISysUserService userService; + + @Autowired + private SysPasswordService passwordService; + + @Autowired + private SysPermissionService permissionService; + + @Override + public UserDetails loadUserByUsername(String username) + { + SysUser user = userService.selectUserByUserName(username); + if (StringUtils.isNull(user)) + { + log.info("登录用户:{} 不存在.", username); + throw new ServiceException(MessageUtils.message("user.not.exists")); + } + else if (UserStatus.DELETED.getCode().equals(user.getDelFlag())) + { + log.info("登录用户:{} 已被删除.", username); + throw new ServiceException(MessageUtils.message("user.password.delete")); + } + else if (UserStatus.DISABLE.getCode().equals(user.getStatus())) + { + log.info("登录用户:{} 已被停用.", username); + throw new ServiceException(MessageUtils.message("user.blocked")); + } + + passwordService.validate(user); + + return createLoginUser(user); + } + + public UserDetails createLoginUser(SysUser user) + { + return new LoginUser(user.getUserId(), user.getDeptId(), user, permissionService.getMenuPermission(user)); + } +} diff --git a/src/main/java/com/ruoyi/framework/task/RyTask.java b/src/main/java/com/ruoyi/framework/task/RyTask.java new file mode 100644 index 0000000..eb7f5b9 --- /dev/null +++ b/src/main/java/com/ruoyi/framework/task/RyTask.java @@ -0,0 +1,28 @@ +package com.ruoyi.framework.task; + +import org.springframework.stereotype.Component; +import com.ruoyi.common.utils.StringUtils; + +/** + * 定时任务调度测试 + * + * @author ruoyi + */ +@Component("ryTask") +public class RyTask +{ + public void ryMultipleParams(String s, Boolean b, Long l, Double d, Integer i) + { + System.out.println(StringUtils.format("执行多参方法: 字符串类型{},布尔类型{},长整型{},浮点型{},整形{}", s, b, l, d, i)); + } + + public void ryParams(String params) + { + System.out.println("执行有参方法:" + params); + } + + public void ryNoParams() + { + System.out.println("执行无参方法"); + } +} diff --git a/src/main/java/com/ruoyi/framework/web/controller/BaseController.java b/src/main/java/com/ruoyi/framework/web/controller/BaseController.java new file mode 100644 index 0000000..79ed4df --- /dev/null +++ b/src/main/java/com/ruoyi/framework/web/controller/BaseController.java @@ -0,0 +1,194 @@ +package com.ruoyi.framework.web.controller; + +import java.beans.PropertyEditorSupport; +import java.util.Date; +import java.util.List; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.web.bind.WebDataBinder; +import org.springframework.web.bind.annotation.InitBinder; +import com.github.pagehelper.PageHelper; +import com.github.pagehelper.PageInfo; +import com.ruoyi.common.constant.HttpStatus; +import com.ruoyi.common.utils.DateUtils; +import com.ruoyi.common.utils.PageUtils; +import com.ruoyi.common.utils.SecurityUtils; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.sql.SqlUtil; +import com.ruoyi.framework.security.LoginUser; +import com.ruoyi.framework.web.domain.AjaxResult; +import com.ruoyi.framework.web.page.PageDomain; +import com.ruoyi.framework.web.page.TableDataInfo; +import com.ruoyi.framework.web.page.TableSupport; + +/** + * web层通用数据处理 + * + * @author ruoyi + */ +public class BaseController +{ + protected final Logger logger = LoggerFactory.getLogger(this.getClass()); + + /** + * 将前台传递过来的日期格式的字符串,自动转化为Date类型 + */ + @InitBinder + public void initBinder(WebDataBinder binder) + { + // Date 类型转换 + binder.registerCustomEditor(Date.class, new PropertyEditorSupport() + { + @Override + public void setAsText(String text) + { + setValue(DateUtils.parseDate(text)); + } + }); + } + + /** + * 设置请求分页数据 + */ + protected void startPage() + { + PageUtils.startPage(); + } + + /** + * 设置请求排序数据 + */ + protected void startOrderBy() + { + PageDomain pageDomain = TableSupport.buildPageRequest(); + if (StringUtils.isNotEmpty(pageDomain.getOrderBy())) + { + String orderBy = SqlUtil.escapeOrderBySql(pageDomain.getOrderBy()); + PageHelper.orderBy(orderBy); + } + } + + /** + * 清理分页的线程变量 + */ + protected void clearPage() + { + PageUtils.clearPage(); + } + + /** + * 响应请求分页数据 + */ + @SuppressWarnings({ "rawtypes", "unchecked" }) + protected TableDataInfo getDataTable(List list) + { + TableDataInfo rspData = new TableDataInfo(); + rspData.setCode(HttpStatus.SUCCESS); + rspData.setMsg("查询成功"); + rspData.setRows(list); + rspData.setTotal(new PageInfo(list).getTotal()); + return rspData; + } + + /** + * 返回成功 + */ + public AjaxResult success() + { + return AjaxResult.success(); + } + + /** + * 返回成功消息 + */ + public AjaxResult success(String message) + { + return AjaxResult.success(message); + } + + /** + * 返回成功消息 + */ + public AjaxResult success(Object data) + { + return AjaxResult.success(data); + } + + /** + * 返回失败消息 + */ + public AjaxResult error() + { + return AjaxResult.error(); + } + + /** + * 返回失败消息 + */ + public AjaxResult error(String message) + { + return AjaxResult.error(message); + } + + /** + * 返回警告消息 + */ + public AjaxResult warn(String message) + { + return AjaxResult.warn(message); + } + + /** + * 响应返回结果 + * + * @param rows 影响行数 + * @return 操作结果 + */ + protected AjaxResult toAjax(int rows) + { + return rows > 0 ? AjaxResult.success() : AjaxResult.error(); + } + + /** + * 响应返回结果 + * + * @param result 结果 + * @return 操作结果 + */ + protected AjaxResult toAjax(boolean result) + { + return result ? success() : error(); + } + + /** + * 获取用户缓存信息 + */ + 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(); + } +} diff --git a/src/main/java/com/ruoyi/framework/web/domain/AjaxResult.java b/src/main/java/com/ruoyi/framework/web/domain/AjaxResult.java new file mode 100644 index 0000000..579dd59 --- /dev/null +++ b/src/main/java/com/ruoyi/framework/web/domain/AjaxResult.java @@ -0,0 +1,216 @@ +package com.ruoyi.framework.web.domain; + +import java.util.HashMap; +import java.util.Objects; +import com.ruoyi.common.constant.HttpStatus; +import com.ruoyi.common.utils.StringUtils; + +/** + * 操作消息提醒 + * + * @author ruoyi + */ +public class AjaxResult extends HashMap +{ + private static final long serialVersionUID = 1L; + + /** 状态码 */ + public static final String CODE_TAG = "code"; + + /** 返回内容 */ + public static final String MSG_TAG = "msg"; + + /** 数据对象 */ + public static final String DATA_TAG = "data"; + + /** + * 初始化一个新创建的 AjaxResult 对象,使其表示一个空消息。 + */ + public AjaxResult() + { + } + + /** + * 初始化一个新创建的 AjaxResult 对象 + * + * @param code 状态码 + * @param msg 返回内容 + */ + public AjaxResult(int code, String msg) + { + super.put(CODE_TAG, code); + super.put(MSG_TAG, msg); + } + + /** + * 初始化一个新创建的 AjaxResult 对象 + * + * @param code 状态码 + * @param msg 返回内容 + * @param data 数据对象 + */ + public AjaxResult(int code, String msg, Object data) + { + super.put(CODE_TAG, code); + super.put(MSG_TAG, msg); + if (StringUtils.isNotNull(data)) + { + super.put(DATA_TAG, data); + } + } + + /** + * 返回成功消息 + * + * @return 成功消息 + */ + public static AjaxResult success() + { + return AjaxResult.success("操作成功"); + } + + /** + * 返回成功数据 + * + * @return 成功消息 + */ + public static AjaxResult success(Object data) + { + return AjaxResult.success("操作成功", data); + } + + /** + * 返回成功消息 + * + * @param msg 返回内容 + * @return 成功消息 + */ + public static AjaxResult success(String msg) + { + return AjaxResult.success(msg, null); + } + + /** + * 返回成功消息 + * + * @param msg 返回内容 + * @param data 数据对象 + * @return 成功消息 + */ + public static AjaxResult success(String msg, Object data) + { + return new AjaxResult(HttpStatus.SUCCESS, msg, data); + } + + /** + * 返回警告消息 + * + * @param msg 返回内容 + * @return 警告消息 + */ + public static AjaxResult warn(String msg) + { + return AjaxResult.warn(msg, null); + } + + /** + * 返回警告消息 + * + * @param msg 返回内容 + * @param data 数据对象 + * @return 警告消息 + */ + public static AjaxResult warn(String msg, Object data) + { + return new AjaxResult(HttpStatus.WARN, msg, data); + } + + /** + * 返回错误消息 + * + * @return 错误消息 + */ + public static AjaxResult error() + { + return AjaxResult.error("操作失败"); + } + + /** + * 返回错误消息 + * + * @param msg 返回内容 + * @return 错误消息 + */ + public static AjaxResult error(String msg) + { + return AjaxResult.error(msg, null); + } + + /** + * 返回错误消息 + * + * @param msg 返回内容 + * @param data 数据对象 + * @return 错误消息 + */ + public static AjaxResult error(String msg, Object data) + { + return new AjaxResult(HttpStatus.ERROR, msg, data); + } + + /** + * 返回错误消息 + * + * @param code 状态码 + * @param msg 返回内容 + * @return 错误消息 + */ + public static AjaxResult error(int code, String msg) + { + return new AjaxResult(code, msg, null); + } + + /** + * 是否为成功消息 + * + * @return 结果 + */ + public boolean isSuccess() + { + return Objects.equals(HttpStatus.SUCCESS, this.get(CODE_TAG)); + } + + /** + * 是否为警告消息 + * + * @return 结果 + */ + public boolean isWarn() + { + return Objects.equals(HttpStatus.WARN, this.get(CODE_TAG)); + } + + /** + * 是否为错误消息 + * + * @return 结果 + */ + public boolean isError() + { + return Objects.equals(HttpStatus.ERROR, this.get(CODE_TAG)); + } + + /** + * 方便链式调用 + * + * @param key 键 + * @param value 值 + * @return 数据对象 + */ + @Override + public AjaxResult put(String key, Object value) + { + super.put(key, value); + return this; + } +} diff --git a/src/main/java/com/ruoyi/framework/web/domain/BaseEntity.java b/src/main/java/com/ruoyi/framework/web/domain/BaseEntity.java new file mode 100644 index 0000000..0cd6233 --- /dev/null +++ b/src/main/java/com/ruoyi/framework/web/domain/BaseEntity.java @@ -0,0 +1,118 @@ +package com.ruoyi.framework.web.domain; + +import java.io.Serializable; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonInclude; + +/** + * Entity基类 + * + * @author ruoyi + */ +public class BaseEntity implements Serializable +{ + private static final long serialVersionUID = 1L; + + /** 搜索值 */ + @JsonIgnore + private String searchValue; + + /** 创建者 */ + private String createBy; + + /** 创建时间 */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date createTime; + + /** 更新者 */ + private String updateBy; + + /** 更新时间 */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date updateTime; + + /** 备注 */ + private String remark; + + /** 请求参数 */ + @JsonInclude(JsonInclude.Include.NON_EMPTY) + private Map params; + + public String getSearchValue() + { + return searchValue; + } + + public void setSearchValue(String searchValue) + { + this.searchValue = searchValue; + } + + public String getCreateBy() + { + return createBy; + } + + public void setCreateBy(String createBy) + { + this.createBy = createBy; + } + + public Date getCreateTime() + { + return createTime; + } + + public void setCreateTime(Date createTime) + { + this.createTime = createTime; + } + + public String getUpdateBy() + { + return updateBy; + } + + public void setUpdateBy(String updateBy) + { + this.updateBy = updateBy; + } + + public Date getUpdateTime() + { + return updateTime; + } + + public void setUpdateTime(Date updateTime) + { + this.updateTime = updateTime; + } + + public String getRemark() + { + return remark; + } + + public void setRemark(String remark) + { + this.remark = remark; + } + + public Map getParams() + { + if (params == null) + { + params = new HashMap<>(); + } + return params; + } + + public void setParams(Map params) + { + this.params = params; + } +} diff --git a/src/main/java/com/ruoyi/framework/web/domain/R.java b/src/main/java/com/ruoyi/framework/web/domain/R.java new file mode 100644 index 0000000..bd4f69f --- /dev/null +++ b/src/main/java/com/ruoyi/framework/web/domain/R.java @@ -0,0 +1,115 @@ +package com.ruoyi.framework.web.domain; + +import java.io.Serializable; +import com.ruoyi.common.constant.HttpStatus; + +/** + * 响应信息主体 + * + * @author ruoyi + */ +public class R implements Serializable +{ + private static final long serialVersionUID = 1L; + + /** 成功 */ + public static final int SUCCESS = HttpStatus.SUCCESS; + + /** 失败 */ + public static final int FAIL = HttpStatus.ERROR; + + private int code; + + private String msg; + + private T data; + + public static R ok() + { + return restResult(null, SUCCESS, "操作成功"); + } + + public static R ok(T data) + { + return restResult(data, SUCCESS, "操作成功"); + } + + public static R ok(T data, String msg) + { + return restResult(data, SUCCESS, msg); + } + + public static R fail() + { + return restResult(null, FAIL, "操作失败"); + } + + public static R fail(String msg) + { + return restResult(null, FAIL, msg); + } + + public static R fail(T data) + { + return restResult(data, FAIL, "操作失败"); + } + + public static R fail(T data, String msg) + { + return restResult(data, FAIL, msg); + } + + public static R fail(int code, String msg) + { + return restResult(null, code, msg); + } + + private static R restResult(T data, int code, String msg) + { + R apiResult = new R<>(); + apiResult.setCode(code); + apiResult.setData(data); + apiResult.setMsg(msg); + return apiResult; + } + + public int getCode() + { + return code; + } + + public void setCode(int code) + { + this.code = code; + } + + public String getMsg() + { + return msg; + } + + public void setMsg(String msg) + { + this.msg = msg; + } + + public T getData() + { + return data; + } + + public void setData(T data) + { + this.data = data; + } + + public static Boolean isError(R ret) + { + return !isSuccess(ret); + } + + public static Boolean isSuccess(R ret) + { + return R.SUCCESS == ret.getCode(); + } +} diff --git a/src/main/java/com/ruoyi/framework/web/domain/Server.java b/src/main/java/com/ruoyi/framework/web/domain/Server.java new file mode 100644 index 0000000..63b03da --- /dev/null +++ b/src/main/java/com/ruoyi/framework/web/domain/Server.java @@ -0,0 +1,240 @@ +package com.ruoyi.framework.web.domain; + +import java.net.UnknownHostException; +import java.util.LinkedList; +import java.util.List; +import java.util.Properties; +import com.ruoyi.common.utils.Arith; +import com.ruoyi.common.utils.ip.IpUtils; +import com.ruoyi.framework.web.domain.server.Cpu; +import com.ruoyi.framework.web.domain.server.Jvm; +import com.ruoyi.framework.web.domain.server.Mem; +import com.ruoyi.framework.web.domain.server.Sys; +import com.ruoyi.framework.web.domain.server.SysFile; +import oshi.SystemInfo; +import oshi.hardware.CentralProcessor; +import oshi.hardware.CentralProcessor.TickType; +import oshi.hardware.GlobalMemory; +import oshi.hardware.HardwareAbstractionLayer; +import oshi.software.os.FileSystem; +import oshi.software.os.OSFileStore; +import oshi.software.os.OperatingSystem; +import oshi.util.Util; + +/** + * 服务器相关信息 + * + * @author ruoyi + */ +public class Server +{ + private static final int OSHI_WAIT_SECOND = 1000; + + /** + * CPU相关信息 + */ + private Cpu cpu = new Cpu(); + + /** + * 內存相关信息 + */ + private Mem mem = new Mem(); + + /** + * JVM相关信息 + */ + private Jvm jvm = new Jvm(); + + /** + * 服务器相关信息 + */ + private Sys sys = new Sys(); + + /** + * 磁盘相关信息 + */ + private List sysFiles = new LinkedList(); + + public Cpu getCpu() + { + return cpu; + } + + public void setCpu(Cpu cpu) + { + this.cpu = cpu; + } + + public Mem getMem() + { + return mem; + } + + public void setMem(Mem mem) + { + this.mem = mem; + } + + public Jvm getJvm() + { + return jvm; + } + + public void setJvm(Jvm jvm) + { + this.jvm = jvm; + } + + public Sys getSys() + { + return sys; + } + + public void setSys(Sys sys) + { + this.sys = sys; + } + + public List getSysFiles() + { + return sysFiles; + } + + public void setSysFiles(List sysFiles) + { + this.sysFiles = sysFiles; + } + + public void copyTo() throws Exception + { + SystemInfo si = new SystemInfo(); + HardwareAbstractionLayer hal = si.getHardware(); + + setCpuInfo(hal.getProcessor()); + + setMemInfo(hal.getMemory()); + + setSysInfo(); + + setJvmInfo(); + + setSysFiles(si.getOperatingSystem()); + } + + /** + * 设置CPU信息 + */ + private void setCpuInfo(CentralProcessor processor) + { + // CPU信息 + long[] prevTicks = processor.getSystemCpuLoadTicks(); + Util.sleep(OSHI_WAIT_SECOND); + long[] ticks = processor.getSystemCpuLoadTicks(); + long nice = ticks[TickType.NICE.getIndex()] - prevTicks[TickType.NICE.getIndex()]; + long irq = ticks[TickType.IRQ.getIndex()] - prevTicks[TickType.IRQ.getIndex()]; + long softirq = ticks[TickType.SOFTIRQ.getIndex()] - prevTicks[TickType.SOFTIRQ.getIndex()]; + long steal = ticks[TickType.STEAL.getIndex()] - prevTicks[TickType.STEAL.getIndex()]; + long cSys = ticks[TickType.SYSTEM.getIndex()] - prevTicks[TickType.SYSTEM.getIndex()]; + long user = ticks[TickType.USER.getIndex()] - prevTicks[TickType.USER.getIndex()]; + long iowait = ticks[TickType.IOWAIT.getIndex()] - prevTicks[TickType.IOWAIT.getIndex()]; + long idle = ticks[TickType.IDLE.getIndex()] - prevTicks[TickType.IDLE.getIndex()]; + long totalCpu = user + nice + cSys + idle + iowait + irq + softirq + steal; + cpu.setCpuNum(processor.getLogicalProcessorCount()); + cpu.setTotal(totalCpu); + cpu.setSys(cSys); + cpu.setUsed(user); + cpu.setWait(iowait); + cpu.setFree(idle); + } + + /** + * 设置内存信息 + */ + private void setMemInfo(GlobalMemory memory) + { + mem.setTotal(memory.getTotal()); + mem.setUsed(memory.getTotal() - memory.getAvailable()); + mem.setFree(memory.getAvailable()); + } + + /** + * 设置服务器信息 + */ + private void setSysInfo() + { + Properties props = System.getProperties(); + sys.setComputerName(IpUtils.getHostName()); + sys.setComputerIp(IpUtils.getHostIp()); + sys.setOsName(props.getProperty("os.name")); + sys.setOsArch(props.getProperty("os.arch")); + sys.setUserDir(props.getProperty("user.dir")); + } + + /** + * 设置Java虚拟机 + */ + private void setJvmInfo() throws UnknownHostException + { + Properties props = System.getProperties(); + jvm.setTotal(Runtime.getRuntime().totalMemory()); + jvm.setMax(Runtime.getRuntime().maxMemory()); + jvm.setFree(Runtime.getRuntime().freeMemory()); + jvm.setVersion(props.getProperty("java.version")); + jvm.setHome(props.getProperty("java.home")); + } + + /** + * 设置磁盘信息 + */ + private void setSysFiles(OperatingSystem os) + { + FileSystem fileSystem = os.getFileSystem(); + List fsArray = fileSystem.getFileStores(); + for (OSFileStore fs : fsArray) + { + long free = fs.getUsableSpace(); + long total = fs.getTotalSpace(); + long used = total - free; + SysFile sysFile = new SysFile(); + sysFile.setDirName(fs.getMount()); + sysFile.setSysTypeName(fs.getType()); + sysFile.setTypeName(fs.getName()); + sysFile.setTotal(convertFileSize(total)); + sysFile.setFree(convertFileSize(free)); + sysFile.setUsed(convertFileSize(used)); + sysFile.setUsage(Arith.mul(Arith.div(used, total, 4), 100)); + sysFiles.add(sysFile); + } + } + + /** + * 字节转换 + * + * @param size 字节大小 + * @return 转换后值 + */ + public String convertFileSize(long size) + { + long kb = 1024; + long mb = kb * 1024; + long gb = mb * 1024; + if (size >= gb) + { + return String.format("%.1f GB", (float) size / gb); + } + else if (size >= mb) + { + float f = (float) size / mb; + return String.format(f > 100 ? "%.0f MB" : "%.1f MB", f); + } + else if (size >= kb) + { + float f = (float) size / kb; + return String.format(f > 100 ? "%.0f KB" : "%.1f KB", f); + } + else + { + return String.format("%d B", size); + } + } +} diff --git a/src/main/java/com/ruoyi/framework/web/domain/TreeEntity.java b/src/main/java/com/ruoyi/framework/web/domain/TreeEntity.java new file mode 100644 index 0000000..b071425 --- /dev/null +++ b/src/main/java/com/ruoyi/framework/web/domain/TreeEntity.java @@ -0,0 +1,79 @@ +package com.ruoyi.framework.web.domain; + +import java.util.ArrayList; +import java.util.List; + +/** + * Tree基类 + * + * @author ruoyi + */ +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<>(); + + public String getParentName() + { + return parentName; + } + + public void setParentName(String parentName) + { + this.parentName = parentName; + } + + public Long getParentId() + { + return parentId; + } + + public void setParentId(Long parentId) + { + this.parentId = parentId; + } + + public Integer getOrderNum() + { + return orderNum; + } + + public void setOrderNum(Integer orderNum) + { + this.orderNum = orderNum; + } + + public String getAncestors() + { + return ancestors; + } + + public void setAncestors(String ancestors) + { + this.ancestors = ancestors; + } + + public List getChildren() + { + return children; + } + + public void setChildren(List children) + { + this.children = children; + } +} diff --git a/src/main/java/com/ruoyi/framework/web/domain/TreeSelect.java b/src/main/java/com/ruoyi/framework/web/domain/TreeSelect.java new file mode 100644 index 0000000..680d65b --- /dev/null +++ b/src/main/java/com/ruoyi/framework/web/domain/TreeSelect.java @@ -0,0 +1,77 @@ +package com.ruoyi.framework.web.domain; + +import java.io.Serializable; +import java.util.List; +import java.util.stream.Collectors; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.ruoyi.project.system.domain.SysDept; +import com.ruoyi.project.system.domain.SysMenu; + +/** + * Treeselect树结构实体类 + * + * @author ruoyi + */ +public class TreeSelect implements Serializable +{ + private static final long serialVersionUID = 1L; + + /** 节点ID */ + private Long id; + + /** 节点名称 */ + private String label; + + /** 子节点 */ + @JsonInclude(JsonInclude.Include.NON_EMPTY) + private List children; + + public TreeSelect() + { + + } + + public TreeSelect(SysDept dept) + { + this.id = dept.getDeptId(); + this.label = dept.getDeptName(); + this.children = dept.getChildren().stream().map(TreeSelect::new).collect(Collectors.toList()); + } + + public TreeSelect(SysMenu menu) + { + this.id = menu.getMenuId(); + this.label = menu.getMenuName(); + this.children = menu.getChildren().stream().map(TreeSelect::new).collect(Collectors.toList()); + } + + public Long getId() + { + return id; + } + + public void setId(Long id) + { + this.id = id; + } + + public String getLabel() + { + return label; + } + + public void setLabel(String label) + { + this.label = label; + } + + public List getChildren() + { + return children; + } + + public void setChildren(List children) + { + this.children = children; + } +} diff --git a/src/main/java/com/ruoyi/framework/web/domain/server/Cpu.java b/src/main/java/com/ruoyi/framework/web/domain/server/Cpu.java new file mode 100644 index 0000000..a13a66c --- /dev/null +++ b/src/main/java/com/ruoyi/framework/web/domain/server/Cpu.java @@ -0,0 +1,101 @@ +package com.ruoyi.framework.web.domain.server; + +import com.ruoyi.common.utils.Arith; + +/** + * CPU相关信息 + * + * @author ruoyi + */ +public class Cpu +{ + /** + * 核心数 + */ + private int cpuNum; + + /** + * CPU总的使用率 + */ + private double total; + + /** + * CPU系统使用率 + */ + private double sys; + + /** + * CPU用户使用率 + */ + private double used; + + /** + * CPU当前等待率 + */ + private double wait; + + /** + * CPU当前空闲率 + */ + private double free; + + public int getCpuNum() + { + return cpuNum; + } + + public void setCpuNum(int cpuNum) + { + this.cpuNum = cpuNum; + } + + public double getTotal() + { + return Arith.round(Arith.mul(total, 100), 2); + } + + public void setTotal(double total) + { + this.total = total; + } + + public double getSys() + { + return Arith.round(Arith.mul(sys / total, 100), 2); + } + + public void setSys(double sys) + { + this.sys = sys; + } + + public double getUsed() + { + return Arith.round(Arith.mul(used / total, 100), 2); + } + + public void setUsed(double used) + { + this.used = used; + } + + public double getWait() + { + return Arith.round(Arith.mul(wait / total, 100), 2); + } + + public void setWait(double wait) + { + this.wait = wait; + } + + public double getFree() + { + return Arith.round(Arith.mul(free / total, 100), 2); + } + + public void setFree(double free) + { + this.free = free; + } +} diff --git a/src/main/java/com/ruoyi/framework/web/domain/server/Jvm.java b/src/main/java/com/ruoyi/framework/web/domain/server/Jvm.java new file mode 100644 index 0000000..1fdc6ac --- /dev/null +++ b/src/main/java/com/ruoyi/framework/web/domain/server/Jvm.java @@ -0,0 +1,130 @@ +package com.ruoyi.framework.web.domain.server; + +import java.lang.management.ManagementFactory; +import com.ruoyi.common.utils.Arith; +import com.ruoyi.common.utils.DateUtils; + +/** + * JVM相关信息 + * + * @author ruoyi + */ +public class Jvm +{ + /** + * 当前JVM占用的内存总数(M) + */ + private double total; + + /** + * JVM最大可用内存总数(M) + */ + private double max; + + /** + * JVM空闲内存(M) + */ + private double free; + + /** + * JDK版本 + */ + private String version; + + /** + * JDK路径 + */ + private String home; + + public double getTotal() + { + return Arith.div(total, (1024 * 1024), 2); + } + + public void setTotal(double total) + { + this.total = total; + } + + public double getMax() + { + return Arith.div(max, (1024 * 1024), 2); + } + + public void setMax(double max) + { + this.max = max; + } + + public double getFree() + { + return Arith.div(free, (1024 * 1024), 2); + } + + public void setFree(double free) + { + this.free = free; + } + + public double getUsed() + { + return Arith.div(total - free, (1024 * 1024), 2); + } + + public double getUsage() + { + return Arith.mul(Arith.div(total - free, total, 4), 100); + } + + /** + * 获取JDK名称 + */ + public String getName() + { + return ManagementFactory.getRuntimeMXBean().getVmName(); + } + + public String getVersion() + { + return version; + } + + public void setVersion(String version) + { + this.version = version; + } + + public String getHome() + { + return home; + } + + public void setHome(String home) + { + this.home = home; + } + + /** + * JDK启动时间 + */ + public String getStartTime() + { + return DateUtils.parseDateToStr(DateUtils.YYYY_MM_DD_HH_MM_SS, DateUtils.getServerStartDate()); + } + + /** + * JDK运行时间 + */ + public String getRunTime() + { + return DateUtils.timeDistance(DateUtils.getNowDate(), DateUtils.getServerStartDate()); + } + + /** + * 运行参数 + */ + public String getInputArgs() + { + return ManagementFactory.getRuntimeMXBean().getInputArguments().toString(); + } +} diff --git a/src/main/java/com/ruoyi/framework/web/domain/server/Mem.java b/src/main/java/com/ruoyi/framework/web/domain/server/Mem.java new file mode 100644 index 0000000..13eec52 --- /dev/null +++ b/src/main/java/com/ruoyi/framework/web/domain/server/Mem.java @@ -0,0 +1,61 @@ +package com.ruoyi.framework.web.domain.server; + +import com.ruoyi.common.utils.Arith; + +/** + * 內存相关信息 + * + * @author ruoyi + */ +public class Mem +{ + /** + * 内存总量 + */ + private double total; + + /** + * 已用内存 + */ + private double used; + + /** + * 剩余内存 + */ + private double free; + + public double getTotal() + { + return Arith.div(total, (1024 * 1024 * 1024), 2); + } + + public void setTotal(long total) + { + this.total = total; + } + + public double getUsed() + { + return Arith.div(used, (1024 * 1024 * 1024), 2); + } + + public void setUsed(long used) + { + this.used = used; + } + + public double getFree() + { + return Arith.div(free, (1024 * 1024 * 1024), 2); + } + + public void setFree(long free) + { + this.free = free; + } + + public double getUsage() + { + return Arith.mul(Arith.div(used, total, 4), 100); + } +} diff --git a/src/main/java/com/ruoyi/framework/web/domain/server/Sys.java b/src/main/java/com/ruoyi/framework/web/domain/server/Sys.java new file mode 100644 index 0000000..45d64d9 --- /dev/null +++ b/src/main/java/com/ruoyi/framework/web/domain/server/Sys.java @@ -0,0 +1,84 @@ +package com.ruoyi.framework.web.domain.server; + +/** + * 系统相关信息 + * + * @author ruoyi + */ +public class Sys +{ + /** + * 服务器名称 + */ + private String computerName; + + /** + * 服务器Ip + */ + private String computerIp; + + /** + * 项目路径 + */ + private String userDir; + + /** + * 操作系统 + */ + private String osName; + + /** + * 系统架构 + */ + private String osArch; + + public String getComputerName() + { + return computerName; + } + + public void setComputerName(String computerName) + { + this.computerName = computerName; + } + + public String getComputerIp() + { + return computerIp; + } + + public void setComputerIp(String computerIp) + { + this.computerIp = computerIp; + } + + public String getUserDir() + { + return userDir; + } + + public void setUserDir(String userDir) + { + this.userDir = userDir; + } + + public String getOsName() + { + return osName; + } + + public void setOsName(String osName) + { + this.osName = osName; + } + + public String getOsArch() + { + return osArch; + } + + public void setOsArch(String osArch) + { + this.osArch = osArch; + } +} diff --git a/src/main/java/com/ruoyi/framework/web/domain/server/SysFile.java b/src/main/java/com/ruoyi/framework/web/domain/server/SysFile.java new file mode 100644 index 0000000..1320cde --- /dev/null +++ b/src/main/java/com/ruoyi/framework/web/domain/server/SysFile.java @@ -0,0 +1,114 @@ +package com.ruoyi.framework.web.domain.server; + +/** + * 系统文件相关信息 + * + * @author ruoyi + */ +public class SysFile +{ + /** + * 盘符路径 + */ + private String dirName; + + /** + * 盘符类型 + */ + private String sysTypeName; + + /** + * 文件类型 + */ + private String typeName; + + /** + * 总大小 + */ + private String total; + + /** + * 剩余大小 + */ + private String free; + + /** + * 已经使用量 + */ + private String used; + + /** + * 资源的使用率 + */ + private double usage; + + public String getDirName() + { + return dirName; + } + + public void setDirName(String dirName) + { + this.dirName = dirName; + } + + public String getSysTypeName() + { + return sysTypeName; + } + + public void setSysTypeName(String sysTypeName) + { + this.sysTypeName = sysTypeName; + } + + public String getTypeName() + { + return typeName; + } + + public void setTypeName(String typeName) + { + this.typeName = typeName; + } + + public String getTotal() + { + return total; + } + + public void setTotal(String total) + { + this.total = total; + } + + public String getFree() + { + return free; + } + + public void setFree(String free) + { + this.free = free; + } + + public String getUsed() + { + return used; + } + + public void setUsed(String used) + { + this.used = used; + } + + public double getUsage() + { + return usage; + } + + public void setUsage(double usage) + { + this.usage = usage; + } +} diff --git a/src/main/java/com/ruoyi/framework/web/exception/GlobalExceptionHandler.java b/src/main/java/com/ruoyi/framework/web/exception/GlobalExceptionHandler.java new file mode 100644 index 0000000..06343a3 --- /dev/null +++ b/src/main/java/com/ruoyi/framework/web/exception/GlobalExceptionHandler.java @@ -0,0 +1,138 @@ +package com.ruoyi.framework.web.exception; + +import javax.servlet.http.HttpServletRequest; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.security.access.AccessDeniedException; +import org.springframework.validation.BindException; +import org.springframework.web.HttpRequestMethodNotSupportedException; +import org.springframework.web.bind.MethodArgumentNotValidException; +import org.springframework.web.bind.MissingPathVariableException; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RestControllerAdvice; +import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException; +import com.ruoyi.common.constant.HttpStatus; +import com.ruoyi.common.exception.DemoModeException; +import com.ruoyi.common.exception.ServiceException; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.framework.web.domain.AjaxResult; + +/** + * 全局异常处理器 + * + * @author ruoyi + */ +@RestControllerAdvice +public class GlobalExceptionHandler +{ + private static final Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class); + + /** + * 权限校验异常 + */ + @ExceptionHandler(AccessDeniedException.class) + public AjaxResult handleAccessDeniedException(AccessDeniedException e, HttpServletRequest request) + { + String requestURI = request.getRequestURI(); + log.error("请求地址'{}',权限校验失败'{}'", requestURI, e.getMessage()); + return AjaxResult.error(HttpStatus.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(ServiceException.class) + public AjaxResult handleServiceException(ServiceException e, HttpServletRequest request) + { + log.error(e.getMessage(), e); + Integer code = e.getCode(); + return StringUtils.isNotNull(code) ? AjaxResult.error(code, e.getMessage()) : AjaxResult.error(e.getMessage()); + } + + /** + * 请求路径中缺少必需的路径变量 + */ + @ExceptionHandler(MissingPathVariableException.class) + public AjaxResult handleMissingPathVariableException(MissingPathVariableException e, HttpServletRequest request) + { + String requestURI = request.getRequestURI(); + log.error("请求路径中缺少必需的路径变量'{}',发生系统异常.", requestURI, e); + return AjaxResult.error(String.format("请求路径中缺少必需的路径变量[%s]", e.getVariableName())); + } + + /** + * 请求参数类型不匹配 + */ + @ExceptionHandler(MethodArgumentTypeMismatchException.class) + public AjaxResult handleMethodArgumentTypeMismatchException(MethodArgumentTypeMismatchException e, HttpServletRequest request) + { + String requestURI = request.getRequestURI(); + log.error("请求参数类型不匹配'{}',发生系统异常.", requestURI, e); + return AjaxResult.error(String.format("请求参数类型不匹配,参数[%s]要求类型为:'%s',但输入值为:'%s'", e.getName(), e.getRequiredType().getName(), e.getValue())); + } + + /** + * 拦截未知的运行时异常 + */ + @ExceptionHandler(RuntimeException.class) + public AjaxResult handleRuntimeException(RuntimeException e, HttpServletRequest request) + { + String requestURI = request.getRequestURI(); + log.error("请求地址'{}',发生未知异常.", requestURI, e); + return AjaxResult.error(e.getMessage()); + } + + /** + * 系统异常 + */ + @ExceptionHandler(Exception.class) + public AjaxResult handleException(Exception e, HttpServletRequest request) + { + String requestURI = request.getRequestURI(); + log.error("请求地址'{}',发生系统异常.", requestURI, e); + return AjaxResult.error(e.getMessage()); + } + + /** + * 自定义验证异常 + */ + @ExceptionHandler(BindException.class) + public AjaxResult handleBindException(BindException e) + { + log.error(e.getMessage(), e); + String message = e.getAllErrors().get(0).getDefaultMessage(); + return AjaxResult.error(message); + } + + /** + * 自定义验证异常 + */ + @ExceptionHandler(MethodArgumentNotValidException.class) + public Object handleMethodArgumentNotValidException(MethodArgumentNotValidException e) + { + log.error(e.getMessage(), e); + String message = e.getBindingResult().getFieldError().getDefaultMessage(); + return AjaxResult.error(message); + } + + /** + * 演示模式异常 + */ + @ExceptionHandler(DemoModeException.class) + public AjaxResult handleDemoModeException(DemoModeException e) + { + return AjaxResult.error("演示模式,不允许操作"); + } +} diff --git a/src/main/java/com/ruoyi/framework/web/page/PageDomain.java b/src/main/java/com/ruoyi/framework/web/page/PageDomain.java new file mode 100644 index 0000000..27df44c --- /dev/null +++ b/src/main/java/com/ruoyi/framework/web/page/PageDomain.java @@ -0,0 +1,101 @@ +package com.ruoyi.framework.web.page; + +import com.ruoyi.common.utils.StringUtils; + +/** + * 分页数据 + * + * @author ruoyi + */ +public class PageDomain +{ + /** 当前记录起始索引 */ + private Integer pageNum; + + /** 每页显示记录数 */ + private Integer pageSize; + + /** 排序列 */ + private String orderByColumn; + + /** 排序的方向desc或者asc */ + private String isAsc = "asc"; + + /** 分页参数合理化 */ + private Boolean reasonable = true; + + public String getOrderBy() + { + if (StringUtils.isEmpty(orderByColumn)) + { + return ""; + } + return StringUtils.toUnderScoreCase(orderByColumn) + " " + isAsc; + } + + public Integer getPageNum() + { + return pageNum; + } + + public void setPageNum(Integer pageNum) + { + this.pageNum = pageNum; + } + + public Integer getPageSize() + { + return pageSize; + } + + public void setPageSize(Integer pageSize) + { + this.pageSize = pageSize; + } + + public String getOrderByColumn() + { + return orderByColumn; + } + + public void setOrderByColumn(String orderByColumn) + { + this.orderByColumn = orderByColumn; + } + + public String getIsAsc() + { + return isAsc; + } + + public void setIsAsc(String isAsc) + { + if (StringUtils.isNotEmpty(isAsc)) + { + // 兼容前端排序类型 + if ("ascending".equals(isAsc)) + { + isAsc = "asc"; + } + else if ("descending".equals(isAsc)) + { + isAsc = "desc"; + } + this.isAsc = isAsc; + } + } + + public Boolean getReasonable() + { + if (StringUtils.isNull(reasonable)) + { + return Boolean.TRUE; + } + return reasonable; + } + + public void setReasonable(Boolean reasonable) + { + this.reasonable = reasonable; + } +} diff --git a/src/main/java/com/ruoyi/framework/web/page/TableDataInfo.java b/src/main/java/com/ruoyi/framework/web/page/TableDataInfo.java new file mode 100644 index 0000000..c44e527 --- /dev/null +++ b/src/main/java/com/ruoyi/framework/web/page/TableDataInfo.java @@ -0,0 +1,85 @@ +package com.ruoyi.framework.web.page; + +import java.io.Serializable; +import java.util.List; + +/** + * 表格分页数据对象 + * + * @author ruoyi + */ +public class TableDataInfo implements Serializable +{ + private static final long serialVersionUID = 1L; + + /** 总记录数 */ + private long total; + + /** 列表数据 */ + private List rows; + + /** 消息状态码 */ + private int code; + + /** 消息内容 */ + private String msg; + + /** + * 表格数据对象 + */ + public TableDataInfo() + { + } + + /** + * 分页 + * + * @param list 列表数据 + * @param total 总记录数 + */ + public TableDataInfo(List list, int total) + { + this.rows = list; + this.total = total; + } + + public long getTotal() + { + return total; + } + + public void setTotal(long total) + { + this.total = total; + } + + public List getRows() + { + return rows; + } + + public void setRows(List rows) + { + this.rows = rows; + } + + public int getCode() + { + return code; + } + + public void setCode(int code) + { + this.code = code; + } + + public String getMsg() + { + return msg; + } + + public void setMsg(String msg) + { + this.msg = msg; + } +} \ No newline at end of file diff --git a/src/main/java/com/ruoyi/framework/web/page/TableSupport.java b/src/main/java/com/ruoyi/framework/web/page/TableSupport.java new file mode 100644 index 0000000..8433d10 --- /dev/null +++ b/src/main/java/com/ruoyi/framework/web/page/TableSupport.java @@ -0,0 +1,56 @@ +package com.ruoyi.framework.web.page; + +import com.ruoyi.common.core.text.Convert; +import com.ruoyi.common.utils.ServletUtils; + +/** + * 表格数据处理 + * + * @author ruoyi + */ +public class TableSupport +{ + /** + * 当前记录起始索引 + */ + public static final String PAGE_NUM = "pageNum"; + + /** + * 每页显示记录数 + */ + public static final String PAGE_SIZE = "pageSize"; + + /** + * 排序列 + */ + public static final String ORDER_BY_COLUMN = "orderByColumn"; + + /** + * 排序的方向 "desc" 或者 "asc". + */ + public static final String IS_ASC = "isAsc"; + + /** + * 分页参数合理化 + */ + public static final String REASONABLE = "reasonable"; + + /** + * 封装分页对象 + */ + public static PageDomain getPageDomain() + { + PageDomain pageDomain = new PageDomain(); + pageDomain.setPageNum(Convert.toInt(ServletUtils.getParameter(PAGE_NUM), 1)); + pageDomain.setPageSize(Convert.toInt(ServletUtils.getParameter(PAGE_SIZE), 10)); + pageDomain.setOrderByColumn(ServletUtils.getParameter(ORDER_BY_COLUMN)); + pageDomain.setIsAsc(ServletUtils.getParameter(IS_ASC)); + pageDomain.setReasonable(ServletUtils.getParameterToBool(REASONABLE)); + return pageDomain; + } + + public static PageDomain buildPageRequest() + { + return getPageDomain(); + } +} diff --git a/src/main/java/com/ruoyi/project/about/controller/AboutPartThreeController.java b/src/main/java/com/ruoyi/project/about/controller/AboutPartThreeController.java new file mode 100644 index 0000000..423581e --- /dev/null +++ b/src/main/java/com/ruoyi/project/about/controller/AboutPartThreeController.java @@ -0,0 +1,81 @@ +package com.ruoyi.project.about.controller; +import java.util.List; +import javax.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.springframework.security.access.prepost.PreAuthorize; + +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import com.ruoyi.project.about.domain.AboutPartThree; +import com.ruoyi.project.about.service.IAboutPartThreeService; +import com.ruoyi.framework.web.domain.R; + +/** + * 关于第三部分Controller + * + * @author wyd + * @date 2024-04-19 + */ +@Slf4j +@Api(tags = "关于第三部分") +@RestController +@RequestMapping("/PartThree") +public class AboutPartThreeController { + @Resource + private IAboutPartThreeService aboutPartThreeService; + + /** + * 关于第三部分列表 + */ + @PreAuthorize("@ss.hasPermi('PartThree:partthree:list')") + @ApiOperation(value = "关于第三部分列表", notes = "关于第三部分列表信息") + @GetMapping("/list") + public R> list() { + List list = aboutPartThreeService.list(); + return R.ok(list); + } + /** + * 关于第三部分id详细信息 + */ + @PreAuthorize("@ss.hasPermi('PartThree:partthree:query')") + @ApiOperation(value = "关于第三部分id详细信息", notes = "关于第三部分id详细信息") + @GetMapping("/query") + public R query(Long id) { + AboutPartThree queryinfo = aboutPartThreeService.getById(id); + return R.ok(queryinfo); + } + /** + * 关于第三部分新增信息 + */ + @PreAuthorize("@ss.hasPermi('PartThree:partthree:add')") + @ApiOperation(value = "关于第三部分新增信息", notes = "关于第三部分新增信息") + @PostMapping("/add") + public R add(AboutPartThree aboutPartThree) { + boolean res = aboutPartThreeService.save(aboutPartThree); + return R.ok(res); + } + /** + * 关于第三部分修改信息 + */ + @PreAuthorize("@ss.hasPermi('PartThree:partthree:edit')") + @ApiOperation(value = "关于第三部分修改信息", notes = "关于第三部分修改信息") + @PostMapping("/edit") + public R edit(AboutPartThree aboutPartThree) { + boolean res = aboutPartThreeService.updateById(aboutPartThree); + return R.ok(res); + } + /** + * 关于第三部分删除信息 + */ + @PreAuthorize("@ss.hasPermi('PartThree:partthree:remove')") + @ApiOperation(value = "关于第三部分删除信息", notes = "关于第三部分删除信息") + @PostMapping("/remove") + public R remove(Long id) { + boolean res = aboutPartThreeService.removeById(id); + return R.ok(res); + } +} diff --git a/src/main/java/com/ruoyi/project/about/domain/AboutPartThree.java b/src/main/java/com/ruoyi/project/about/domain/AboutPartThree.java new file mode 100644 index 0000000..aa40f89 --- /dev/null +++ b/src/main/java/com/ruoyi/project/about/domain/AboutPartThree.java @@ -0,0 +1,69 @@ +package com.ruoyi.project.about.domain; + +import com.alibaba.fastjson2.annotation.JSONField; +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.databind.ser.std.ToStringSerializer; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + + + + +/** + * 关于第三部分对象 about_part_three + * + * @author wyd + * @date 2024-04-19 + */ +@Data +@NoArgsConstructor +@AllArgsConstructor +@Builder +@ApiModel("关于第三部分") +@TableName(" about_part_three") +public class AboutPartThree{ +private static final long serialVersionUID=1L; + + /** 主键 */ + @JSONField(serializeUsing = ToStringSerializer.class) + @ApiModelProperty("主键") + @TableId(value = "id" , type = IdType.ASSIGN_ID) + private Long id; + /** 第一标题 */ + @ApiModelProperty("第一标题") + @TableField("title_one") + private String titleOne; + /** 第二标题 */ + @ApiModelProperty("第二标题") + @TableField("title_two") + private String titleTwo; + /** 备注 */ + @ApiModelProperty("备注") + @TableField("remarks") + private String remarks; + /** 图标 */ + @ApiModelProperty("图标") + @TableField("icon") + private String icon; + /** 排序 */ + @ApiModelProperty("排序") + @TableField("sorts") + private Long sorts; + /** 创建时间 */ + @ApiModelProperty("创建时间") + @TableField("create_time") + @JSONField(serializeUsing = ToStringSerializer.class) + private String createTime; + /** 更新时间 */ + @ApiModelProperty("更新时间") + @TableField("update_time") + @JSONField(serializeUsing = ToStringSerializer.class) + private String updateTime; +} \ No newline at end of file diff --git a/src/main/java/com/ruoyi/project/about/mapper/AboutPartThreeMapper.java b/src/main/java/com/ruoyi/project/about/mapper/AboutPartThreeMapper.java new file mode 100644 index 0000000..24f7d63 --- /dev/null +++ b/src/main/java/com/ruoyi/project/about/mapper/AboutPartThreeMapper.java @@ -0,0 +1,14 @@ +package com.ruoyi.project.about.mapper; + + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.ruoyi.project.about.domain.AboutPartThree; +/** + * 关于第三部分Mapper接口 + * + * @author wyd + * @date 2024-04-19 + */ +public interface AboutPartThreeMapper extends BaseMapper { + +} diff --git a/src/main/java/com/ruoyi/project/about/service/IAboutPartThreeService.java b/src/main/java/com/ruoyi/project/about/service/IAboutPartThreeService.java new file mode 100644 index 0000000..bed5663 --- /dev/null +++ b/src/main/java/com/ruoyi/project/about/service/IAboutPartThreeService.java @@ -0,0 +1,15 @@ +package com.ruoyi.project.about.service; + + +import com.ruoyi.project.about.domain.AboutPartThree; +import com.baomidou.mybatisplus.extension.service.IService; + +/** + * 关于第三部分Service接口 + * + * @author wyd + * @date 2024-04-19 + */ +public interface IAboutPartThreeService extends IService { + +} diff --git a/src/main/java/com/ruoyi/project/about/service/impl/AboutPartThreeServiceImpl.java b/src/main/java/com/ruoyi/project/about/service/impl/AboutPartThreeServiceImpl.java new file mode 100644 index 0000000..85bb0f0 --- /dev/null +++ b/src/main/java/com/ruoyi/project/about/service/impl/AboutPartThreeServiceImpl.java @@ -0,0 +1,17 @@ +package com.ruoyi.project.about.service.impl; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.ruoyi.project.about.mapper.AboutPartThreeMapper; +import com.ruoyi.project.about.domain.AboutPartThree; +import com.ruoyi.project.about.service.IAboutPartThreeService; +import org.springframework.stereotype.Service; +/** + * 关于第三部分Service业务层处理 + * + * @author wyd + * @date 2024-04-19 + */ +@Service +public class AboutPartThreeServiceImpl extends ServiceImpl implements IAboutPartThreeService { + +} diff --git a/src/main/java/com/ruoyi/project/common/CaptchaController.java b/src/main/java/com/ruoyi/project/common/CaptchaController.java new file mode 100644 index 0000000..0948ce3 --- /dev/null +++ b/src/main/java/com/ruoyi/project/common/CaptchaController.java @@ -0,0 +1,98 @@ +package com.ruoyi.project.common; + +import java.awt.image.BufferedImage; +import java.io.IOException; +import java.util.concurrent.TimeUnit; +import javax.annotation.Resource; +import javax.imageio.ImageIO; +import javax.servlet.http.HttpServletResponse; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.util.FastByteArrayOutputStream; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; +import com.google.code.kaptcha.Producer; +import com.ruoyi.common.constant.CacheConstants; +import com.ruoyi.common.constant.Constants; +import com.ruoyi.common.utils.sign.Base64; +import com.ruoyi.common.utils.uuid.IdUtils; +import com.ruoyi.framework.redis.RedisCache; +import com.ruoyi.framework.web.domain.AjaxResult; +import com.ruoyi.project.system.service.ISysConfigService; + +/** + * 验证码操作处理 + * + * @author ruoyi + */ +@RestController +public class CaptchaController +{ + @Resource(name = "captchaProducer") + private Producer captchaProducer; + + @Resource(name = "captchaProducerMath") + private Producer captchaProducerMath; + + @Autowired + private RedisCache redisCache; + + // 验证码类型 + @Value("${ruoyi.captchaType}") + private String captchaType; + + @Autowired + private ISysConfigService configService; + + /** + * 生成验证码 + */ + @GetMapping("/captchaImage") + public AjaxResult getCode(HttpServletResponse response) throws IOException + { + AjaxResult ajax = AjaxResult.success(); + boolean captchaEnabled = configService.selectCaptchaEnabled(); + ajax.put("captchaEnabled", captchaEnabled); + if (!captchaEnabled) + { + return ajax; + } + + // 保存验证码信息 + String uuid = IdUtils.simpleUUID(); + String verifyKey = CacheConstants.CAPTCHA_CODE_KEY + uuid; + + String capStr = null, code = null; + BufferedImage image = null; + + // 生成验证码 + if ("math".equals(captchaType)) + { + String capText = captchaProducerMath.createText(); + capStr = capText.substring(0, capText.lastIndexOf("@")); + code = capText.substring(capText.lastIndexOf("@") + 1); + image = captchaProducerMath.createImage(capStr); + } + else if ("char".equals(captchaType)) + { + capStr = code = captchaProducer.createText(); + image = captchaProducer.createImage(capStr); + } + + redisCache.setCacheObject(verifyKey, code, Constants.CAPTCHA_EXPIRATION, TimeUnit.MINUTES); + // 转换流信息写出 + FastByteArrayOutputStream os = new FastByteArrayOutputStream(); + try + { + ImageIO.write(image, "jpg", os); + } + catch (IOException e) + { + return AjaxResult.error(e.getMessage()); + } + + ajax.put("uuid", uuid); + ajax.put("img", Base64.encode(os.toByteArray())); + return ajax; + } +} diff --git a/src/main/java/com/ruoyi/project/common/CommonController.java b/src/main/java/com/ruoyi/project/common/CommonController.java new file mode 100644 index 0000000..b48e02e --- /dev/null +++ b/src/main/java/com/ruoyi/project/common/CommonController.java @@ -0,0 +1,163 @@ +package com.ruoyi.project.common; + +import java.util.ArrayList; +import java.util.List; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.MediaType; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.multipart.MultipartFile; +import com.ruoyi.common.constant.Constants; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.file.FileUploadUtils; +import com.ruoyi.common.utils.file.FileUtils; +import com.ruoyi.framework.config.RuoYiConfig; +import com.ruoyi.framework.config.ServerConfig; +import com.ruoyi.framework.web.domain.AjaxResult; + +/** + * 通用请求处理 + * + * @author ruoyi + */ +@RestController +@RequestMapping("/common") +public class CommonController +{ + private static final Logger log = LoggerFactory.getLogger(CommonController.class); + + @Autowired + private ServerConfig serverConfig; + + private static final String FILE_DELIMETER = ","; + + /** + * 通用下载请求 + * + * @param fileName 文件名称 + * @param delete 是否删除 + */ + @GetMapping("/download") + public void fileDownload(String fileName, Boolean delete, HttpServletResponse response, HttpServletRequest request) + { + try + { + if (!FileUtils.checkAllowDownload(fileName)) + { + throw new Exception(StringUtils.format("文件名称({})非法,不允许下载。 ", fileName)); + } + String realFileName = System.currentTimeMillis() + fileName.substring(fileName.indexOf("_") + 1); + String filePath = RuoYiConfig.getDownloadPath() + fileName; + + response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE); + FileUtils.setAttachmentResponseHeader(response, realFileName); + FileUtils.writeBytes(filePath, response.getOutputStream()); + if (delete) + { + FileUtils.deleteFile(filePath); + } + } + catch (Exception e) + { + log.error("下载文件失败", e); + } + } + + /** + * 通用上传请求(单个) + */ + @PostMapping("/upload") + public AjaxResult uploadFile(MultipartFile file) throws Exception + { + try + { + // 上传文件路径 + String filePath = RuoYiConfig.getUploadPath(); + // 上传并返回新文件名称 + String fileName = FileUploadUtils.upload(filePath, file); + String url = serverConfig.getUrl() + fileName; + AjaxResult ajax = AjaxResult.success(); + ajax.put("url", url); + ajax.put("fileName", fileName); + ajax.put("newFileName", FileUtils.getName(fileName)); + ajax.put("originalFilename", file.getOriginalFilename()); + return ajax; + } + catch (Exception e) + { + return AjaxResult.error(e.getMessage()); + } + } + + /** + * 通用上传请求(多个) + */ + @PostMapping("/uploads") + public AjaxResult uploadFiles(List files) throws Exception + { + try + { + // 上传文件路径 + String filePath = RuoYiConfig.getUploadPath(); + List urls = new ArrayList(); + List fileNames = new ArrayList(); + List newFileNames = new ArrayList(); + List originalFilenames = new ArrayList(); + for (MultipartFile file : files) + { + // 上传并返回新文件名称 + String fileName = FileUploadUtils.upload(filePath, file); + String url = serverConfig.getUrl() + fileName; + urls.add(url); + fileNames.add(fileName); + newFileNames.add(FileUtils.getName(fileName)); + originalFilenames.add(file.getOriginalFilename()); + } + AjaxResult ajax = AjaxResult.success(); + ajax.put("urls", StringUtils.join(urls, FILE_DELIMETER)); + ajax.put("fileNames", StringUtils.join(fileNames, FILE_DELIMETER)); + ajax.put("newFileNames", StringUtils.join(newFileNames, FILE_DELIMETER)); + ajax.put("originalFilenames", StringUtils.join(originalFilenames, FILE_DELIMETER)); + return ajax; + } + catch (Exception e) + { + return AjaxResult.error(e.getMessage()); + } + } + + /** + * 本地资源通用下载 + */ + @GetMapping("/download/resource") + public void resourceDownload(String resource, HttpServletRequest request, HttpServletResponse response) + throws Exception + { + try + { + if (!FileUtils.checkAllowDownload(resource)) + { + throw new Exception(StringUtils.format("资源文件({})非法,不允许下载。 ", resource)); + } + // 本地资源路径 + String localPath = RuoYiConfig.getProfile(); + // 数据库资源地址 + String downloadPath = localPath + StringUtils.substringAfter(resource, Constants.RESOURCE_PREFIX); + // 下载名称 + String downloadName = StringUtils.substringAfterLast(downloadPath, "/"); + response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE); + FileUtils.setAttachmentResponseHeader(response, downloadName); + FileUtils.writeBytes(downloadPath, response.getOutputStream()); + } + catch (Exception e) + { + log.error("下载文件失败", e); + } + } +} diff --git a/src/main/java/com/ruoyi/project/monitor/controller/CacheController.java b/src/main/java/com/ruoyi/project/monitor/controller/CacheController.java new file mode 100644 index 0000000..fdb04ef --- /dev/null +++ b/src/main/java/com/ruoyi/project/monitor/controller/CacheController.java @@ -0,0 +1,120 @@ +package com.ruoyi.project.monitor.controller; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.Set; +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.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import com.ruoyi.common.constant.CacheConstants; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.framework.web.domain.AjaxResult; +import com.ruoyi.project.monitor.domain.SysCache; + +/** + * 缓存监控 + * + * @author ruoyi + */ +@RestController +@RequestMapping("/monitor/cache") +public class CacheController +{ + @Autowired + private RedisTemplate redisTemplate; + + private final static List caches = new ArrayList(); + { + caches.add(new SysCache(CacheConstants.LOGIN_TOKEN_KEY, "用户信息")); + caches.add(new SysCache(CacheConstants.SYS_CONFIG_KEY, "配置信息")); + caches.add(new SysCache(CacheConstants.SYS_DICT_KEY, "数据字典")); + caches.add(new SysCache(CacheConstants.CAPTCHA_CODE_KEY, "验证码")); + caches.add(new SysCache(CacheConstants.REPEAT_SUBMIT_KEY, "防重提交")); + caches.add(new SysCache(CacheConstants.RATE_LIMIT_KEY, "限流处理")); + caches.add(new SysCache(CacheConstants.PWD_ERR_CNT_KEY, "密码错误次数")); + } + + @PreAuthorize("@ss.hasPermi('monitor:cache:list')") + @GetMapping() + public AjaxResult getInfo() throws Exception + { + Properties info = (Properties) redisTemplate.execute((RedisCallback) connection -> connection.info()); + Properties commandStats = (Properties) redisTemplate.execute((RedisCallback) connection -> connection.info("commandstats")); + Object dbSize = redisTemplate.execute((RedisCallback) connection -> connection.dbSize()); + + Map result = new HashMap<>(3); + result.put("info", info); + result.put("dbSize", dbSize); + + List> pieList = new ArrayList<>(); + commandStats.stringPropertyNames().forEach(key -> { + Map 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); + } + + @PreAuthorize("@ss.hasPermi('monitor:cache:list')") + @GetMapping("/getNames") + public AjaxResult cache() + { + return AjaxResult.success(caches); + } + + @PreAuthorize("@ss.hasPermi('monitor:cache:list')") + @GetMapping("/getKeys/{cacheName}") + public AjaxResult getCacheKeys(@PathVariable String cacheName) + { + Set cacheKeys = redisTemplate.keys(cacheName + "*"); + return AjaxResult.success(cacheKeys); + } + + @PreAuthorize("@ss.hasPermi('monitor:cache:list')") + @GetMapping("/getValue/{cacheName}/{cacheKey}") + public AjaxResult getCacheValue(@PathVariable String cacheName, @PathVariable String cacheKey) + { + String cacheValue = redisTemplate.opsForValue().get(cacheKey); + SysCache sysCache = new SysCache(cacheName, cacheKey, cacheValue); + return AjaxResult.success(sysCache); + } + + @PreAuthorize("@ss.hasPermi('monitor:cache:list')") + @DeleteMapping("/clearCacheName/{cacheName}") + public AjaxResult clearCacheName(@PathVariable String cacheName) + { + Collection cacheKeys = redisTemplate.keys(cacheName + "*"); + redisTemplate.delete(cacheKeys); + return AjaxResult.success(); + } + + @PreAuthorize("@ss.hasPermi('monitor:cache:list')") + @DeleteMapping("/clearCacheKey/{cacheKey}") + public AjaxResult clearCacheKey(@PathVariable String cacheKey) + { + redisTemplate.delete(cacheKey); + return AjaxResult.success(); + } + + @PreAuthorize("@ss.hasPermi('monitor:cache:list')") + @DeleteMapping("/clearCacheAll") + public AjaxResult clearCacheAll() + { + Collection cacheKeys = redisTemplate.keys("*"); + redisTemplate.delete(cacheKeys); + return AjaxResult.success(); + } +} diff --git a/src/main/java/com/ruoyi/project/monitor/controller/ServerController.java b/src/main/java/com/ruoyi/project/monitor/controller/ServerController.java new file mode 100644 index 0000000..e1f55f1 --- /dev/null +++ b/src/main/java/com/ruoyi/project/monitor/controller/ServerController.java @@ -0,0 +1,27 @@ +package com.ruoyi.project.monitor.controller; + +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 com.ruoyi.framework.web.domain.AjaxResult; +import com.ruoyi.framework.web.domain.Server; + +/** + * 服务器监控 + * + * @author ruoyi + */ +@RestController +@RequestMapping("/monitor/server") +public class ServerController +{ + @PreAuthorize("@ss.hasPermi('monitor:server:list')") + @GetMapping() + public AjaxResult getInfo() throws Exception + { + Server server = new Server(); + server.copyTo(); + return AjaxResult.success(server); + } +} diff --git a/src/main/java/com/ruoyi/project/monitor/controller/SysJobController.java b/src/main/java/com/ruoyi/project/monitor/controller/SysJobController.java new file mode 100644 index 0000000..5fb2bbd --- /dev/null +++ b/src/main/java/com/ruoyi/project/monitor/controller/SysJobController.java @@ -0,0 +1,185 @@ +package com.ruoyi.project.monitor.controller; + +import java.util.List; +import javax.servlet.http.HttpServletResponse; +import org.quartz.SchedulerException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.prepost.PreAuthorize; +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.constant.Constants; +import com.ruoyi.common.exception.job.TaskException; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.job.CronUtils; +import com.ruoyi.common.utils.job.ScheduleUtils; +import com.ruoyi.common.utils.poi.ExcelUtil; +import com.ruoyi.framework.aspectj.lang.annotation.Log; +import com.ruoyi.framework.aspectj.lang.enums.BusinessType; +import com.ruoyi.framework.web.controller.BaseController; +import com.ruoyi.framework.web.domain.AjaxResult; +import com.ruoyi.framework.web.page.TableDataInfo; +import com.ruoyi.project.monitor.domain.SysJob; +import com.ruoyi.project.monitor.service.ISysJobService; + +/** + * 调度任务信息操作处理 + * + * @author ruoyi + */ +@RestController +@RequestMapping("/monitor/job") +public class SysJobController extends BaseController +{ + @Autowired + private ISysJobService jobService; + + /** + * 查询定时任务列表 + */ + @PreAuthorize("@ss.hasPermi('monitor:job:list')") + @GetMapping("/list") + public TableDataInfo list(SysJob sysJob) + { + startPage(); + List list = jobService.selectJobList(sysJob); + return getDataTable(list); + } + + /** + * 导出定时任务列表 + */ + @PreAuthorize("@ss.hasPermi('monitor:job:export')") + @Log(title = "定时任务", businessType = BusinessType.EXPORT) + @PostMapping("/export") + public void export(HttpServletResponse response, SysJob sysJob) + { + List list = jobService.selectJobList(sysJob); + ExcelUtil util = new ExcelUtil(SysJob.class); + util.exportExcel(response, list, "定时任务"); + } + + /** + * 获取定时任务详细信息 + */ + @PreAuthorize("@ss.hasPermi('monitor:job:query')") + @GetMapping(value = "/{jobId}") + public AjaxResult getInfo(@PathVariable("jobId") Long jobId) + { + return success(jobService.selectJobById(jobId)); + } + + /** + * 新增定时任务 + */ + @PreAuthorize("@ss.hasPermi('monitor:job:add')") + @Log(title = "定时任务", businessType = BusinessType.INSERT) + @PostMapping + public AjaxResult add(@RequestBody SysJob job) throws SchedulerException, TaskException + { + if (!CronUtils.isValid(job.getCronExpression())) + { + return error("新增任务'" + job.getJobName() + "'失败,Cron表达式不正确"); + } + else if (StringUtils.containsIgnoreCase(job.getInvokeTarget(), Constants.LOOKUP_RMI)) + { + return error("新增任务'" + job.getJobName() + "'失败,目标字符串不允许'rmi'调用"); + } + else if (StringUtils.containsAnyIgnoreCase(job.getInvokeTarget(), new String[] { Constants.LOOKUP_LDAP, Constants.LOOKUP_LDAPS })) + { + return error("新增任务'" + job.getJobName() + "'失败,目标字符串不允许'ldap(s)'调用"); + } + else if (StringUtils.containsAnyIgnoreCase(job.getInvokeTarget(), new String[] { Constants.HTTP, Constants.HTTPS })) + { + return error("新增任务'" + job.getJobName() + "'失败,目标字符串不允许'http(s)'调用"); + } + else if (StringUtils.containsAnyIgnoreCase(job.getInvokeTarget(), Constants.JOB_ERROR_STR)) + { + return error("新增任务'" + job.getJobName() + "'失败,目标字符串存在违规"); + } + else if (!ScheduleUtils.whiteList(job.getInvokeTarget())) + { + return error("新增任务'" + job.getJobName() + "'失败,目标字符串不在白名单内"); + } + job.setCreateBy(getUsername()); + return toAjax(jobService.insertJob(job)); + } + + /** + * 修改定时任务 + */ + @PreAuthorize("@ss.hasPermi('monitor:job:edit')") + @Log(title = "定时任务", businessType = BusinessType.UPDATE) + @PutMapping + public AjaxResult edit(@RequestBody SysJob job) throws SchedulerException, TaskException + { + if (!CronUtils.isValid(job.getCronExpression())) + { + return error("修改任务'" + job.getJobName() + "'失败,Cron表达式不正确"); + } + else if (StringUtils.containsIgnoreCase(job.getInvokeTarget(), Constants.LOOKUP_RMI)) + { + return error("修改任务'" + job.getJobName() + "'失败,目标字符串不允许'rmi'调用"); + } + else if (StringUtils.containsAnyIgnoreCase(job.getInvokeTarget(), new String[] { Constants.LOOKUP_LDAP, Constants.LOOKUP_LDAPS })) + { + return error("修改任务'" + job.getJobName() + "'失败,目标字符串不允许'ldap(s)'调用"); + } + else if (StringUtils.containsAnyIgnoreCase(job.getInvokeTarget(), new String[] { Constants.HTTP, Constants.HTTPS })) + { + return error("修改任务'" + job.getJobName() + "'失败,目标字符串不允许'http(s)'调用"); + } + else if (StringUtils.containsAnyIgnoreCase(job.getInvokeTarget(), Constants.JOB_ERROR_STR)) + { + return error("修改任务'" + job.getJobName() + "'失败,目标字符串存在违规"); + } + else if (!ScheduleUtils.whiteList(job.getInvokeTarget())) + { + return error("修改任务'" + job.getJobName() + "'失败,目标字符串不在白名单内"); + } + job.setUpdateBy(getUsername()); + return toAjax(jobService.updateJob(job)); + } + + /** + * 定时任务状态修改 + */ + @PreAuthorize("@ss.hasPermi('monitor:job:changeStatus')") + @Log(title = "定时任务", businessType = BusinessType.UPDATE) + @PutMapping("/changeStatus") + public AjaxResult changeStatus(@RequestBody SysJob job) throws SchedulerException + { + SysJob newJob = jobService.selectJobById(job.getJobId()); + newJob.setStatus(job.getStatus()); + return toAjax(jobService.changeStatus(newJob)); + } + + /** + * 定时任务立即执行一次 + */ + @PreAuthorize("@ss.hasPermi('monitor:job:changeStatus')") + @Log(title = "定时任务", businessType = BusinessType.UPDATE) + @PutMapping("/run") + public AjaxResult run(@RequestBody SysJob job) throws SchedulerException + { + boolean result = jobService.run(job); + return result ? success() : error("任务不存在或已过期!"); + } + + /** + * 删除定时任务 + */ + @PreAuthorize("@ss.hasPermi('monitor:job:remove')") + @Log(title = "定时任务", businessType = BusinessType.DELETE) + @DeleteMapping("/{jobIds}") + public AjaxResult remove(@PathVariable Long[] jobIds) throws SchedulerException, TaskException + { + jobService.deleteJobByIds(jobIds); + return success(); + } +} diff --git a/src/main/java/com/ruoyi/project/monitor/controller/SysJobLogController.java b/src/main/java/com/ruoyi/project/monitor/controller/SysJobLogController.java new file mode 100644 index 0000000..2061c55 --- /dev/null +++ b/src/main/java/com/ruoyi/project/monitor/controller/SysJobLogController.java @@ -0,0 +1,92 @@ +package com.ruoyi.project.monitor.controller; + +import java.util.List; +import javax.servlet.http.HttpServletResponse; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.prepost.PreAuthorize; +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.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import com.ruoyi.common.utils.poi.ExcelUtil; +import com.ruoyi.framework.aspectj.lang.annotation.Log; +import com.ruoyi.framework.aspectj.lang.enums.BusinessType; +import com.ruoyi.framework.web.controller.BaseController; +import com.ruoyi.framework.web.domain.AjaxResult; +import com.ruoyi.framework.web.page.TableDataInfo; +import com.ruoyi.project.monitor.domain.SysJobLog; +import com.ruoyi.project.monitor.service.ISysJobLogService; + +/** + * 调度日志操作处理 + * + * @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) + { + startPage(); + List list = jobLogService.selectJobLogList(sysJobLog); + return getDataTable(list); + } + + /** + * 导出定时任务调度日志列表 + */ + @PreAuthorize("@ss.hasPermi('monitor:job:export')") + @Log(title = "任务调度日志", businessType = BusinessType.EXPORT) + @PostMapping("/export") + public void export(HttpServletResponse response, SysJobLog sysJobLog) + { + List list = jobLogService.selectJobLogList(sysJobLog); + ExcelUtil util = new ExcelUtil(SysJobLog.class); + util.exportExcel(response, list, "调度日志"); + } + + /** + * 根据调度编号获取详细信息 + */ + @PreAuthorize("@ss.hasPermi('monitor:job:query')") + @GetMapping(value = "/{jobLogId}") + public AjaxResult getInfo(@PathVariable Long jobLogId) + { + return 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 success(); + } +} diff --git a/src/main/java/com/ruoyi/project/monitor/controller/SysLogininforController.java b/src/main/java/com/ruoyi/project/monitor/controller/SysLogininforController.java new file mode 100644 index 0000000..4371d9a --- /dev/null +++ b/src/main/java/com/ruoyi/project/monitor/controller/SysLogininforController.java @@ -0,0 +1,82 @@ +package com.ruoyi.project.monitor.controller; + +import java.util.List; +import javax.servlet.http.HttpServletResponse; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.prepost.PreAuthorize; +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.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import com.ruoyi.common.utils.poi.ExcelUtil; +import com.ruoyi.framework.aspectj.lang.annotation.Log; +import com.ruoyi.framework.aspectj.lang.enums.BusinessType; +import com.ruoyi.framework.security.service.SysPasswordService; +import com.ruoyi.framework.web.controller.BaseController; +import com.ruoyi.framework.web.domain.AjaxResult; +import com.ruoyi.framework.web.page.TableDataInfo; +import com.ruoyi.project.monitor.domain.SysLogininfor; +import com.ruoyi.project.monitor.service.ISysLogininforService; + +/** + * 系统访问记录 + * + * @author ruoyi + */ +@RestController +@RequestMapping("/monitor/logininfor") +public class SysLogininforController extends BaseController +{ + @Autowired + private ISysLogininforService logininforService; + + @Autowired + private SysPasswordService passwordService; + + @PreAuthorize("@ss.hasPermi('monitor:logininfor:list')") + @GetMapping("/list") + public TableDataInfo list(SysLogininfor logininfor) + { + startPage(); + List list = logininforService.selectLogininforList(logininfor); + return getDataTable(list); + } + + @Log(title = "登录日志", businessType = BusinessType.EXPORT) + @PreAuthorize("@ss.hasPermi('monitor:logininfor:export')") + @PostMapping("/export") + public void export(HttpServletResponse response, SysLogininfor logininfor) + { + List list = logininforService.selectLogininforList(logininfor); + ExcelUtil util = new ExcelUtil(SysLogininfor.class); + util.exportExcel(response, list, "登录日志"); + } + + @PreAuthorize("@ss.hasPermi('monitor:logininfor:remove')") + @Log(title = "登录日志", businessType = BusinessType.DELETE) + @DeleteMapping("/{infoIds}") + public AjaxResult remove(@PathVariable Long[] infoIds) + { + return toAjax(logininforService.deleteLogininforByIds(infoIds)); + } + + @PreAuthorize("@ss.hasPermi('monitor:logininfor:remove')") + @Log(title = "登录日志", businessType = BusinessType.CLEAN) + @DeleteMapping("/clean") + public AjaxResult clean() + { + logininforService.cleanLogininfor(); + return success(); + } + + @PreAuthorize("@ss.hasPermi('monitor:logininfor:unlock')") + @Log(title = "账户解锁", businessType = BusinessType.OTHER) + @GetMapping("/unlock/{userName}") + public AjaxResult unlock(@PathVariable("userName") String userName) + { + passwordService.clearLoginRecordCache(userName); + return success(); + } +} diff --git a/src/main/java/com/ruoyi/project/monitor/controller/SysOperlogController.java b/src/main/java/com/ruoyi/project/monitor/controller/SysOperlogController.java new file mode 100644 index 0000000..703077e --- /dev/null +++ b/src/main/java/com/ruoyi/project/monitor/controller/SysOperlogController.java @@ -0,0 +1,69 @@ +package com.ruoyi.project.monitor.controller; + +import java.util.List; +import javax.servlet.http.HttpServletResponse; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.prepost.PreAuthorize; +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.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import com.ruoyi.common.utils.poi.ExcelUtil; +import com.ruoyi.framework.aspectj.lang.annotation.Log; +import com.ruoyi.framework.aspectj.lang.enums.BusinessType; +import com.ruoyi.framework.web.controller.BaseController; +import com.ruoyi.framework.web.domain.AjaxResult; +import com.ruoyi.framework.web.page.TableDataInfo; +import com.ruoyi.project.monitor.domain.SysOperLog; +import com.ruoyi.project.monitor.service.ISysOperLogService; + +/** + * 操作日志记录 + * + * @author ruoyi + */ +@RestController +@RequestMapping("/monitor/operlog") +public class SysOperlogController extends BaseController +{ + @Autowired + private ISysOperLogService operLogService; + + @PreAuthorize("@ss.hasPermi('monitor:operlog:list')") + @GetMapping("/list") + public TableDataInfo list(SysOperLog operLog) + { + startPage(); + List list = operLogService.selectOperLogList(operLog); + return getDataTable(list); + } + + @Log(title = "操作日志", businessType = BusinessType.EXPORT) + @PreAuthorize("@ss.hasPermi('monitor:operlog:export')") + @PostMapping("/export") + public void export(HttpServletResponse response, SysOperLog operLog) + { + List list = operLogService.selectOperLogList(operLog); + ExcelUtil util = new ExcelUtil(SysOperLog.class); + util.exportExcel(response, list, "操作日志"); + } + + @Log(title = "操作日志", businessType = BusinessType.DELETE) + @PreAuthorize("@ss.hasPermi('monitor:operlog:remove')") + @DeleteMapping("/{operIds}") + public AjaxResult remove(@PathVariable Long[] operIds) + { + return toAjax(operLogService.deleteOperLogByIds(operIds)); + } + + @Log(title = "操作日志", businessType = BusinessType.CLEAN) + @PreAuthorize("@ss.hasPermi('monitor:operlog:remove')") + @DeleteMapping("/clean") + public AjaxResult clean() + { + operLogService.cleanOperLog(); + return success(); + } +} diff --git a/src/main/java/com/ruoyi/project/monitor/controller/SysUserOnlineController.java b/src/main/java/com/ruoyi/project/monitor/controller/SysUserOnlineController.java new file mode 100644 index 0000000..913d93f --- /dev/null +++ b/src/main/java/com/ruoyi/project/monitor/controller/SysUserOnlineController.java @@ -0,0 +1,83 @@ +package com.ruoyi.project.monitor.controller; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.prepost.PreAuthorize; +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.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import com.ruoyi.common.constant.CacheConstants; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.framework.aspectj.lang.annotation.Log; +import com.ruoyi.framework.aspectj.lang.enums.BusinessType; +import com.ruoyi.framework.redis.RedisCache; +import com.ruoyi.framework.security.LoginUser; +import com.ruoyi.framework.web.controller.BaseController; +import com.ruoyi.framework.web.domain.AjaxResult; +import com.ruoyi.framework.web.page.TableDataInfo; +import com.ruoyi.project.monitor.domain.SysUserOnline; +import com.ruoyi.project.system.service.ISysUserOnlineService; + +/** + * 在线用户监控 + * + * @author ruoyi + */ +@RestController +@RequestMapping("/monitor/online") +public class SysUserOnlineController extends BaseController +{ + @Autowired + private ISysUserOnlineService userOnlineService; + + @Autowired + private RedisCache redisCache; + + @PreAuthorize("@ss.hasPermi('monitor:online:list')") + @GetMapping("/list") + public TableDataInfo list(String ipaddr, String userName) + { + Collection keys = redisCache.keys(CacheConstants.LOGIN_TOKEN_KEY + "*"); + List userOnlineList = new ArrayList(); + for (String key : keys) + { + LoginUser user = redisCache.getCacheObject(key); + if (StringUtils.isNotEmpty(ipaddr) && StringUtils.isNotEmpty(userName)) + { + userOnlineList.add(userOnlineService.selectOnlineByInfo(ipaddr, userName, user)); + } + else if (StringUtils.isNotEmpty(ipaddr)) + { + userOnlineList.add(userOnlineService.selectOnlineByIpaddr(ipaddr, user)); + } + else if (StringUtils.isNotEmpty(userName) && StringUtils.isNotNull(user.getUser())) + { + userOnlineList.add(userOnlineService.selectOnlineByUserName(userName, user)); + } + else + { + userOnlineList.add(userOnlineService.loginUserToUserOnline(user)); + } + } + Collections.reverse(userOnlineList); + userOnlineList.removeAll(Collections.singleton(null)); + return getDataTable(userOnlineList); + } + + /** + * 强退用户 + */ + @PreAuthorize("@ss.hasPermi('monitor:online:forceLogout')") + @Log(title = "在线用户", businessType = BusinessType.FORCE) + @DeleteMapping("/{tokenId}") + public AjaxResult forceLogout(@PathVariable String tokenId) + { + redisCache.deleteObject(CacheConstants.LOGIN_TOKEN_KEY + tokenId); + return success(); + } +} diff --git a/src/main/java/com/ruoyi/project/monitor/domain/SysCache.java b/src/main/java/com/ruoyi/project/monitor/domain/SysCache.java new file mode 100644 index 0000000..ac32647 --- /dev/null +++ b/src/main/java/com/ruoyi/project/monitor/domain/SysCache.java @@ -0,0 +1,81 @@ +package com.ruoyi.project.monitor.domain; + +import com.ruoyi.common.utils.StringUtils; + +/** + * 缓存信息 + * + * @author ruoyi + */ +public class SysCache +{ + /** 缓存名称 */ + private String cacheName = ""; + + /** 缓存键名 */ + private String cacheKey = ""; + + /** 缓存内容 */ + private String cacheValue = ""; + + /** 备注 */ + private String remark = ""; + + public SysCache() + { + + } + + public SysCache(String cacheName, String remark) + { + this.cacheName = cacheName; + this.remark = remark; + } + + public SysCache(String cacheName, String cacheKey, String cacheValue) + { + this.cacheName = StringUtils.replace(cacheName, ":", ""); + this.cacheKey = StringUtils.replace(cacheKey, cacheName, ""); + this.cacheValue = cacheValue; + } + + public String getCacheName() + { + return cacheName; + } + + public void setCacheName(String cacheName) + { + this.cacheName = cacheName; + } + + public String getCacheKey() + { + return cacheKey; + } + + public void setCacheKey(String cacheKey) + { + this.cacheKey = cacheKey; + } + + public String getCacheValue() + { + return cacheValue; + } + + public void setCacheValue(String cacheValue) + { + this.cacheValue = cacheValue; + } + + public String getRemark() + { + return remark; + } + + public void setRemark(String remark) + { + this.remark = remark; + } +} diff --git a/src/main/java/com/ruoyi/project/monitor/domain/SysJob.java b/src/main/java/com/ruoyi/project/monitor/domain/SysJob.java new file mode 100644 index 0000000..25e77f2 --- /dev/null +++ b/src/main/java/com/ruoyi/project/monitor/domain/SysJob.java @@ -0,0 +1,171 @@ +package com.ruoyi.project.monitor.domain; + +import java.util.Date; +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.Size; +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.ruoyi.common.constant.ScheduleConstants; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.job.CronUtils; +import com.ruoyi.framework.aspectj.lang.annotation.Excel; +import com.ruoyi.framework.aspectj.lang.annotation.Excel.ColumnType; +import com.ruoyi.framework.web.domain.BaseEntity; + +/** + * 定时任务调度表 sys_job + * + * @author ruoyi + */ +public class SysJob extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** 任务ID */ + @Excel(name = "任务序号", cellType = ColumnType.NUMERIC) + private Long jobId; + + /** 任务名称 */ + @Excel(name = "任务名称") + private String jobName; + + /** 任务组名 */ + @Excel(name = "任务组名") + private String jobGroup; + + /** 调用目标字符串 */ + @Excel(name = "调用目标字符串") + private String invokeTarget; + + /** cron执行表达式 */ + @Excel(name = "执行表达式 ") + private String cronExpression; + + /** cron计划策略 */ + @Excel(name = "计划策略 ", readConverterExp = "0=默认,1=立即触发执行,2=触发一次执行,3=不触发立即执行") + private String misfirePolicy = ScheduleConstants.MISFIRE_DEFAULT; + + /** 是否并发执行(0允许 1禁止) */ + @Excel(name = "并发执行", readConverterExp = "0=允许,1=禁止") + private String concurrent; + + /** 任务状态(0正常 1暂停) */ + @Excel(name = "任务状态", readConverterExp = "0=正常,1=暂停") + private String status; + + public Long getJobId() + { + return jobId; + } + + public void setJobId(Long jobId) + { + this.jobId = jobId; + } + + @NotBlank(message = "任务名称不能为空") + @Size(min = 0, max = 64, message = "任务名称不能超过64个字符") + public String getJobName() + { + return jobName; + } + + public void setJobName(String jobName) + { + this.jobName = jobName; + } + + public String getJobGroup() + { + return jobGroup; + } + + public void setJobGroup(String jobGroup) + { + this.jobGroup = jobGroup; + } + + @NotBlank(message = "调用目标字符串不能为空") + @Size(min = 0, max = 500, message = "调用目标字符串长度不能超过500个字符") + public String getInvokeTarget() + { + return invokeTarget; + } + + public void setInvokeTarget(String invokeTarget) + { + this.invokeTarget = invokeTarget; + } + + @NotBlank(message = "Cron执行表达式不能为空") + @Size(min = 0, max = 255, message = "Cron执行表达式不能超过255个字符") + public String getCronExpression() + { + return cronExpression; + } + + public void setCronExpression(String cronExpression) + { + this.cronExpression = cronExpression; + } + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + public Date getNextValidTime() + { + if (StringUtils.isNotEmpty(cronExpression)) + { + return CronUtils.getNextExecution(cronExpression); + } + return null; + } + + public String getMisfirePolicy() + { + return misfirePolicy; + } + + public void setMisfirePolicy(String misfirePolicy) + { + this.misfirePolicy = misfirePolicy; + } + + public String getConcurrent() + { + return concurrent; + } + + public void setConcurrent(String concurrent) + { + this.concurrent = concurrent; + } + + public String getStatus() + { + return status; + } + + public void setStatus(String status) + { + this.status = status; + } + + @Override + public String toString() { + return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) + .append("jobId", getJobId()) + .append("jobName", getJobName()) + .append("jobGroup", getJobGroup()) + .append("cronExpression", getCronExpression()) + .append("nextValidTime", getNextValidTime()) + .append("misfirePolicy", getMisfirePolicy()) + .append("concurrent", getConcurrent()) + .append("status", getStatus()) + .append("createBy", getCreateBy()) + .append("createTime", getCreateTime()) + .append("updateBy", getUpdateBy()) + .append("updateTime", getUpdateTime()) + .append("remark", getRemark()) + .toString(); + } +} \ No newline at end of file diff --git a/src/main/java/com/ruoyi/project/monitor/domain/SysJobLog.java b/src/main/java/com/ruoyi/project/monitor/domain/SysJobLog.java new file mode 100644 index 0000000..c7a3cd2 --- /dev/null +++ b/src/main/java/com/ruoyi/project/monitor/domain/SysJobLog.java @@ -0,0 +1,155 @@ +package com.ruoyi.project.monitor.domain; + +import java.util.Date; +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; +import com.ruoyi.framework.aspectj.lang.annotation.Excel; +import com.ruoyi.framework.web.domain.BaseEntity; + +/** + * 定时任务调度日志表 sys_job_log + * + * @author ruoyi + */ +public class SysJobLog extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** ID */ + @Excel(name = "日志序号") + 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; + + /** 开始时间 */ + private Date startTime; + + /** 停止时间 */ + private Date stopTime; + + public Long getJobLogId() + { + return jobLogId; + } + + public void setJobLogId(Long jobLogId) + { + this.jobLogId = jobLogId; + } + + public String getJobName() + { + return jobName; + } + + public void setJobName(String jobName) + { + this.jobName = jobName; + } + + public String getJobGroup() + { + return jobGroup; + } + + public void setJobGroup(String jobGroup) + { + this.jobGroup = jobGroup; + } + + public String getInvokeTarget() + { + return invokeTarget; + } + + public void setInvokeTarget(String invokeTarget) + { + this.invokeTarget = invokeTarget; + } + + public String getJobMessage() + { + return jobMessage; + } + + public void setJobMessage(String jobMessage) + { + this.jobMessage = jobMessage; + } + + public String getStatus() + { + return status; + } + + public void setStatus(String status) + { + this.status = status; + } + + public String getExceptionInfo() + { + return exceptionInfo; + } + + public void setExceptionInfo(String exceptionInfo) + { + this.exceptionInfo = exceptionInfo; + } + + public Date getStartTime() + { + return startTime; + } + + public void setStartTime(Date startTime) + { + this.startTime = startTime; + } + + public Date getStopTime() + { + return stopTime; + } + + public void setStopTime(Date stopTime) + { + this.stopTime = stopTime; + } + + @Override + public String toString() { + return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) + .append("jobLogId", getJobLogId()) + .append("jobName", getJobName()) + .append("jobGroup", getJobGroup()) + .append("jobMessage", getJobMessage()) + .append("status", getStatus()) + .append("exceptionInfo", getExceptionInfo()) + .append("startTime", getStartTime()) + .append("stopTime", getStopTime()) + .toString(); + } +} diff --git a/src/main/java/com/ruoyi/project/monitor/domain/SysLogininfor.java b/src/main/java/com/ruoyi/project/monitor/domain/SysLogininfor.java new file mode 100644 index 0000000..147a0a5 --- /dev/null +++ b/src/main/java/com/ruoyi/project/monitor/domain/SysLogininfor.java @@ -0,0 +1,144 @@ +package com.ruoyi.project.monitor.domain; + +import java.util.Date; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.ruoyi.framework.aspectj.lang.annotation.Excel; +import com.ruoyi.framework.aspectj.lang.annotation.Excel.ColumnType; +import com.ruoyi.framework.web.domain.BaseEntity; + +/** + * 系统访问记录表 sys_logininfor + * + * @author ruoyi + */ +public class SysLogininfor extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** ID */ + @Excel(name = "序号", cellType = ColumnType.NUMERIC) + private Long infoId; + + /** 用户账号 */ + @Excel(name = "用户账号") + private String userName; + + /** 登录状态 0成功 1失败 */ + @Excel(name = "登录状态", readConverterExp = "0=成功,1=失败") + private String status; + + /** 登录IP地址 */ + @Excel(name = "登录地址") + private String ipaddr; + + /** 登录地点 */ + @Excel(name = "登录地点") + private String loginLocation; + + /** 浏览器类型 */ + @Excel(name = "浏览器") + private String browser; + + /** 操作系统 */ + @Excel(name = "操作系统") + private String os; + + /** 提示消息 */ + @Excel(name = "提示消息") + private String msg; + + /** 访问时间 */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + @Excel(name = "访问时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss") + private Date loginTime; + + public Long getInfoId() + { + return infoId; + } + + public void setInfoId(Long infoId) + { + this.infoId = infoId; + } + + public String getUserName() + { + return userName; + } + + public void setUserName(String userName) + { + this.userName = userName; + } + + public String getStatus() + { + return status; + } + + public void setStatus(String status) + { + this.status = status; + } + + public String getIpaddr() + { + return ipaddr; + } + + public void setIpaddr(String ipaddr) + { + this.ipaddr = ipaddr; + } + + public String getLoginLocation() + { + return loginLocation; + } + + public void setLoginLocation(String loginLocation) + { + this.loginLocation = loginLocation; + } + + public String getBrowser() + { + return browser; + } + + public void setBrowser(String browser) + { + this.browser = browser; + } + + public String getOs() + { + return os; + } + + public void setOs(String os) + { + this.os = os; + } + + public String getMsg() + { + return msg; + } + + public void setMsg(String msg) + { + this.msg = msg; + } + + public Date getLoginTime() + { + return loginTime; + } + + public void setLoginTime(Date loginTime) + { + this.loginTime = loginTime; + } +} \ No newline at end of file diff --git a/src/main/java/com/ruoyi/project/monitor/domain/SysOperLog.java b/src/main/java/com/ruoyi/project/monitor/domain/SysOperLog.java new file mode 100644 index 0000000..5dad339 --- /dev/null +++ b/src/main/java/com/ruoyi/project/monitor/domain/SysOperLog.java @@ -0,0 +1,269 @@ +package com.ruoyi.project.monitor.domain; + +import java.util.Date; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.ruoyi.framework.aspectj.lang.annotation.Excel; +import com.ruoyi.framework.aspectj.lang.annotation.Excel.ColumnType; +import com.ruoyi.framework.web.domain.BaseEntity; + +/** + * 操作日志记录表 oper_log + * + * @author ruoyi + */ +public class SysOperLog extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** 日志主键 */ + @Excel(name = "操作序号", cellType = ColumnType.NUMERIC) + private Long operId; + + /** 操作模块 */ + @Excel(name = "操作模块") + private String title; + + /** 业务类型(0其它 1新增 2修改 3删除) */ + @Excel(name = "业务类型", readConverterExp = "0=其它,1=新增,2=修改,3=删除,4=授权,5=导出,6=导入,7=强退,8=生成代码,9=清空数据") + private Integer businessType; + + /** 业务类型数组 */ + private Integer[] businessTypes; + + /** 请求方法 */ + @Excel(name = "请求方法") + private String method; + + /** 请求方式 */ + @Excel(name = "请求方式") + private String requestMethod; + + /** 操作类别(0其它 1后台用户 2手机端用户) */ + @Excel(name = "操作类别", readConverterExp = "0=其它,1=后台用户,2=手机端用户") + private Integer operatorType; + + /** 操作人员 */ + @Excel(name = "操作人员") + private String operName; + + /** 部门名称 */ + @Excel(name = "部门名称") + private String deptName; + + /** 请求url */ + @Excel(name = "请求地址") + private String operUrl; + + /** 操作地址 */ + @Excel(name = "操作地址") + private String operIp; + + /** 操作地点 */ + @Excel(name = "操作地点") + private String operLocation; + + /** 请求参数 */ + @Excel(name = "请求参数") + private String operParam; + + /** 返回参数 */ + @Excel(name = "返回参数") + private String jsonResult; + + /** 操作状态(0正常 1异常) */ + @Excel(name = "状态", readConverterExp = "0=正常,1=异常") + private Integer status; + + /** 错误消息 */ + @Excel(name = "错误消息") + private String errorMsg; + + /** 操作时间 */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + @Excel(name = "操作时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss") + private Date operTime; + + /** 消耗时间 */ + @Excel(name = "消耗时间", suffix = "毫秒") + private Long costTime; + + public Long getOperId() + { + return operId; + } + + public void setOperId(Long operId) + { + this.operId = operId; + } + + public String getTitle() + { + return title; + } + + public void setTitle(String title) + { + this.title = title; + } + + public Integer getBusinessType() + { + return businessType; + } + + public void setBusinessType(Integer businessType) + { + this.businessType = businessType; + } + + public Integer[] getBusinessTypes() + { + return businessTypes; + } + + public void setBusinessTypes(Integer[] businessTypes) + { + this.businessTypes = businessTypes; + } + + public String getMethod() + { + return method; + } + + public void setMethod(String method) + { + this.method = method; + } + + public String getRequestMethod() + { + return requestMethod; + } + + public void setRequestMethod(String requestMethod) + { + this.requestMethod = requestMethod; + } + + public Integer getOperatorType() + { + return operatorType; + } + + public void setOperatorType(Integer operatorType) + { + this.operatorType = operatorType; + } + + public String getOperName() + { + return operName; + } + + public void setOperName(String operName) + { + this.operName = operName; + } + + public String getDeptName() + { + return deptName; + } + + public void setDeptName(String deptName) + { + this.deptName = deptName; + } + + public String getOperUrl() + { + return operUrl; + } + + public void setOperUrl(String operUrl) + { + this.operUrl = operUrl; + } + + public String getOperIp() + { + return operIp; + } + + public void setOperIp(String operIp) + { + this.operIp = operIp; + } + + public String getOperLocation() + { + return operLocation; + } + + public void setOperLocation(String operLocation) + { + this.operLocation = operLocation; + } + + public String getOperParam() + { + return operParam; + } + + public void setOperParam(String operParam) + { + this.operParam = operParam; + } + + public String getJsonResult() + { + return jsonResult; + } + + public void setJsonResult(String jsonResult) + { + this.jsonResult = jsonResult; + } + + public Integer getStatus() + { + return status; + } + + public void setStatus(Integer status) + { + this.status = status; + } + + public String getErrorMsg() + { + return errorMsg; + } + + public void setErrorMsg(String errorMsg) + { + this.errorMsg = errorMsg; + } + + public Date getOperTime() + { + return operTime; + } + + public void setOperTime(Date operTime) + { + this.operTime = operTime; + } + + public Long getCostTime() + { + return costTime; + } + + public void setCostTime(Long costTime) + { + this.costTime = costTime; + } +} diff --git a/src/main/java/com/ruoyi/project/monitor/domain/SysUserOnline.java b/src/main/java/com/ruoyi/project/monitor/domain/SysUserOnline.java new file mode 100644 index 0000000..387d49c --- /dev/null +++ b/src/main/java/com/ruoyi/project/monitor/domain/SysUserOnline.java @@ -0,0 +1,113 @@ +package com.ruoyi.project.monitor.domain; + +/** + * 当前在线会话 + * + * @author ruoyi + */ +public class SysUserOnline +{ + /** 会话编号 */ + private String tokenId; + + /** 部门名称 */ + private String deptName; + + /** 用户名称 */ + private String userName; + + /** 登录IP地址 */ + private String ipaddr; + + /** 登录地址 */ + private String loginLocation; + + /** 浏览器类型 */ + private String browser; + + /** 操作系统 */ + private String os; + + /** 登录时间 */ + private Long loginTime; + + public String getTokenId() + { + return tokenId; + } + + public void setTokenId(String tokenId) + { + this.tokenId = tokenId; + } + + public String getDeptName() + { + return deptName; + } + + public void setDeptName(String deptName) + { + this.deptName = deptName; + } + + public String getUserName() + { + return userName; + } + + public void setUserName(String userName) + { + this.userName = userName; + } + + public String getIpaddr() + { + return ipaddr; + } + + public void setIpaddr(String ipaddr) + { + this.ipaddr = ipaddr; + } + + public String getLoginLocation() + { + return loginLocation; + } + + public void setLoginLocation(String loginLocation) + { + this.loginLocation = loginLocation; + } + + public String getBrowser() + { + return browser; + } + + public void setBrowser(String browser) + { + this.browser = browser; + } + + public String getOs() + { + return os; + } + + public void setOs(String os) + { + this.os = os; + } + + public Long getLoginTime() + { + return loginTime; + } + + public void setLoginTime(Long loginTime) + { + this.loginTime = loginTime; + } +} diff --git a/src/main/java/com/ruoyi/project/monitor/mapper/SysJobLogMapper.java b/src/main/java/com/ruoyi/project/monitor/mapper/SysJobLogMapper.java new file mode 100644 index 0000000..39137dd --- /dev/null +++ b/src/main/java/com/ruoyi/project/monitor/mapper/SysJobLogMapper.java @@ -0,0 +1,64 @@ +package com.ruoyi.project.monitor.mapper; + +import java.util.List; +import com.ruoyi.project.monitor.domain.SysJobLog; + +/** + * 调度任务日志信息 数据层 + * + * @author ruoyi + */ +public interface SysJobLogMapper +{ + /** + * 获取quartz调度器日志的计划任务 + * + * @param jobLog 调度日志信息 + * @return 调度任务日志集合 + */ + public List selectJobLogList(SysJobLog jobLog); + + /** + * 查询所有调度任务日志 + * + * @return 调度任务日志列表 + */ + public List selectJobLogAll(); + + /** + * 通过调度任务日志ID查询调度信息 + * + * @param jobLogId 调度任务日志ID + * @return 调度任务日志对象信息 + */ + public SysJobLog selectJobLogById(Long jobLogId); + + /** + * 新增任务日志 + * + * @param jobLog 调度日志信息 + * @return 结果 + */ + public int insertJobLog(SysJobLog jobLog); + + /** + * 批量删除调度日志信息 + * + * @param logIds 需要删除的数据ID + * @return 结果 + */ + public int deleteJobLogByIds(Long[] logIds); + + /** + * 删除任务日志 + * + * @param jobId 调度日志ID + * @return 结果 + */ + public int deleteJobLogById(Long jobId); + + /** + * 清空任务日志 + */ + public void cleanJobLog(); +} diff --git a/src/main/java/com/ruoyi/project/monitor/mapper/SysJobMapper.java b/src/main/java/com/ruoyi/project/monitor/mapper/SysJobMapper.java new file mode 100644 index 0000000..ab11461 --- /dev/null +++ b/src/main/java/com/ruoyi/project/monitor/mapper/SysJobMapper.java @@ -0,0 +1,67 @@ +package com.ruoyi.project.monitor.mapper; + +import java.util.List; +import com.ruoyi.project.monitor.domain.SysJob; + +/** + * 调度任务信息 数据层 + * + * @author ruoyi + */ +public interface SysJobMapper +{ + /** + * 查询调度任务日志集合 + * + * @param job 调度信息 + * @return 操作日志集合 + */ + public List selectJobList(SysJob job); + + /** + * 查询所有调度任务 + * + * @return 调度任务列表 + */ + public List selectJobAll(); + + /** + * 通过调度ID查询调度任务信息 + * + * @param jobId 调度ID + * @return 角色对象信息 + */ + public SysJob selectJobById(Long jobId); + + /** + * 通过调度ID删除调度任务信息 + * + * @param jobId 调度ID + * @return 结果 + */ + public int deleteJobById(Long jobId); + + /** + * 批量删除调度任务信息 + * + * @param ids 需要删除的数据ID + * @return 结果 + */ + public int deleteJobByIds(Long[] ids); + + /** + * 修改调度任务信息 + * + * @param job 调度任务信息 + * @return 结果 + */ + public int updateJob(SysJob job); + + /** + * 新增调度任务信息 + * + * @param job 调度任务信息 + * @return 结果 + */ + public int insertJob(SysJob job); +} diff --git a/src/main/java/com/ruoyi/project/monitor/mapper/SysLogininforMapper.java b/src/main/java/com/ruoyi/project/monitor/mapper/SysLogininforMapper.java new file mode 100644 index 0000000..05b8c9b --- /dev/null +++ b/src/main/java/com/ruoyi/project/monitor/mapper/SysLogininforMapper.java @@ -0,0 +1,42 @@ +package com.ruoyi.project.monitor.mapper; + +import java.util.List; +import com.ruoyi.project.monitor.domain.SysLogininfor; + +/** + * 系统访问日志情况信息 数据层 + * + * @author ruoyi + */ +public interface SysLogininforMapper +{ + /** + * 新增系统登录日志 + * + * @param logininfor 访问日志对象 + */ + public void insertLogininfor(SysLogininfor logininfor); + + /** + * 查询系统登录日志集合 + * + * @param logininfor 访问日志对象 + * @return 登录记录集合 + */ + public List selectLogininforList(SysLogininfor logininfor); + + /** + * 批量删除系统登录日志 + * + * @param infoIds 需要删除的登录日志ID + * @return 结果 + */ + public int deleteLogininforByIds(Long[] infoIds); + + /** + * 清空系统登录日志 + * + * @return 结果 + */ + public int cleanLogininfor(); +} diff --git a/src/main/java/com/ruoyi/project/monitor/mapper/SysOperLogMapper.java b/src/main/java/com/ruoyi/project/monitor/mapper/SysOperLogMapper.java new file mode 100644 index 0000000..ed5d573 --- /dev/null +++ b/src/main/java/com/ruoyi/project/monitor/mapper/SysOperLogMapper.java @@ -0,0 +1,48 @@ +package com.ruoyi.project.monitor.mapper; + +import java.util.List; +import com.ruoyi.project.monitor.domain.SysOperLog; + +/** + * 操作日志 数据层 + * + * @author ruoyi + */ +public interface SysOperLogMapper +{ + /** + * 新增操作日志 + * + * @param operLog 操作日志对象 + */ + public void insertOperlog(SysOperLog operLog); + + /** + * 查询系统操作日志集合 + * + * @param operLog 操作日志对象 + * @return 操作日志集合 + */ + public List selectOperLogList(SysOperLog operLog); + + /** + * 批量删除系统操作日志 + * + * @param operIds 需要删除的操作日志ID + * @return 结果 + */ + public int deleteOperLogByIds(Long[] operIds); + + /** + * 查询操作日志详细 + * + * @param operId 操作ID + * @return 操作日志对象 + */ + public SysOperLog selectOperLogById(Long operId); + + /** + * 清空操作日志 + */ + public void cleanOperLog(); +} diff --git a/src/main/java/com/ruoyi/project/monitor/service/ISysJobLogService.java b/src/main/java/com/ruoyi/project/monitor/service/ISysJobLogService.java new file mode 100644 index 0000000..85561e7 --- /dev/null +++ b/src/main/java/com/ruoyi/project/monitor/service/ISysJobLogService.java @@ -0,0 +1,56 @@ +package com.ruoyi.project.monitor.service; + +import java.util.List; +import com.ruoyi.project.monitor.domain.SysJobLog; + +/** + * 定时任务调度日志信息信息 服务层 + * + * @author ruoyi + */ +public interface ISysJobLogService +{ + /** + * 获取quartz调度器日志的计划任务 + * + * @param jobLog 调度日志信息 + * @return 调度任务日志集合 + */ + public List selectJobLogList(SysJobLog jobLog); + + /** + * 通过调度任务日志ID查询调度信息 + * + * @param jobLogId 调度任务日志ID + * @return 调度任务日志对象信息 + */ + public SysJobLog selectJobLogById(Long jobLogId); + + /** + * 新增任务日志 + * + * @param jobLog 调度日志信息 + */ + public void addJobLog(SysJobLog jobLog); + + /** + * 批量删除调度日志信息 + * + * @param logIds 需要删除的日志ID + * @return 结果 + */ + public int deleteJobLogByIds(Long[] logIds); + + /** + * 删除任务日志 + * + * @param jobId 调度日志ID + * @return 结果 + */ + public int deleteJobLogById(Long jobId); + + /** + * 清空任务日志 + */ + public void cleanJobLog(); +} diff --git a/src/main/java/com/ruoyi/project/monitor/service/ISysJobService.java b/src/main/java/com/ruoyi/project/monitor/service/ISysJobService.java new file mode 100644 index 0000000..d834f67 --- /dev/null +++ b/src/main/java/com/ruoyi/project/monitor/service/ISysJobService.java @@ -0,0 +1,102 @@ +package com.ruoyi.project.monitor.service; + +import java.util.List; +import org.quartz.SchedulerException; +import com.ruoyi.common.exception.job.TaskException; +import com.ruoyi.project.monitor.domain.SysJob; + +/** + * 定时任务调度信息信息 服务层 + * + * @author ruoyi + */ +public interface ISysJobService +{ + /** + * 获取quartz调度器的计划任务 + * + * @param job 调度信息 + * @return 调度任务集合 + */ + public List selectJobList(SysJob job); + + /** + * 通过调度任务ID查询调度信息 + * + * @param jobId 调度任务ID + * @return 调度任务对象信息 + */ + public SysJob selectJobById(Long jobId); + + /** + * 暂停任务 + * + * @param job 调度信息 + * @return 结果 + */ + public int pauseJob(SysJob job) throws SchedulerException; + + /** + * 恢复任务 + * + * @param job 调度信息 + * @return 结果 + */ + public int resumeJob(SysJob job) throws SchedulerException; + + /** + * 删除任务后,所对应的trigger也将被删除 + * + * @param job 调度信息 + * @return 结果 + */ + public int deleteJob(SysJob job) throws SchedulerException; + + /** + * 批量删除调度信息 + * + * @param jobIds 需要删除的任务ID + * @return 结果 + */ + public void deleteJobByIds(Long[] jobIds) throws SchedulerException; + + /** + * 任务调度状态修改 + * + * @param job 调度信息 + * @return 结果 + */ + public int changeStatus(SysJob job) throws SchedulerException; + + /** + * 立即运行任务 + * + * @param job 调度信息 + * @return 结果 + */ + public boolean run(SysJob job) throws SchedulerException; + + /** + * 新增任务 + * + * @param job 调度信息 + * @return 结果 + */ + public int insertJob(SysJob job) throws SchedulerException, TaskException; + + /** + * 更新任务 + * + * @param job 调度信息 + * @return 结果 + */ + public int updateJob(SysJob job) throws SchedulerException, TaskException; + + /** + * 校验cron表达式是否有效 + * + * @param cronExpression 表达式 + * @return 结果 + */ + public boolean checkCronExpressionIsValid(String cronExpression); +} \ No newline at end of file diff --git a/src/main/java/com/ruoyi/project/monitor/service/ISysLogininforService.java b/src/main/java/com/ruoyi/project/monitor/service/ISysLogininforService.java new file mode 100644 index 0000000..ac51166 --- /dev/null +++ b/src/main/java/com/ruoyi/project/monitor/service/ISysLogininforService.java @@ -0,0 +1,40 @@ +package com.ruoyi.project.monitor.service; + +import java.util.List; +import com.ruoyi.project.monitor.domain.SysLogininfor; + +/** + * 系统访问日志情况信息 服务层 + * + * @author ruoyi + */ +public interface ISysLogininforService +{ + /** + * 新增系统登录日志 + * + * @param logininfor 访问日志对象 + */ + public void insertLogininfor(SysLogininfor logininfor); + + /** + * 查询系统登录日志集合 + * + * @param logininfor 访问日志对象 + * @return 登录记录集合 + */ + public List selectLogininforList(SysLogininfor logininfor); + + /** + * 批量删除系统登录日志 + * + * @param infoIds 需要删除的登录日志ID + * @return 结果 + */ + public int deleteLogininforByIds(Long[] infoIds); + + /** + * 清空系统登录日志 + */ + public void cleanLogininfor(); +} diff --git a/src/main/java/com/ruoyi/project/monitor/service/ISysOperLogService.java b/src/main/java/com/ruoyi/project/monitor/service/ISysOperLogService.java new file mode 100644 index 0000000..0e63d8d --- /dev/null +++ b/src/main/java/com/ruoyi/project/monitor/service/ISysOperLogService.java @@ -0,0 +1,48 @@ +package com.ruoyi.project.monitor.service; + +import java.util.List; +import com.ruoyi.project.monitor.domain.SysOperLog; + +/** + * 操作日志 服务层 + * + * @author ruoyi + */ +public interface ISysOperLogService +{ + /** + * 新增操作日志 + * + * @param operLog 操作日志对象 + */ + public void insertOperlog(SysOperLog operLog); + + /** + * 查询系统操作日志集合 + * + * @param operLog 操作日志对象 + * @return 操作日志集合 + */ + public List selectOperLogList(SysOperLog operLog); + + /** + * 批量删除系统操作日志 + * + * @param operIds 需要删除的操作日志ID + * @return 结果 + */ + public int deleteOperLogByIds(Long[] operIds); + + /** + * 查询操作日志详细 + * + * @param operId 操作ID + * @return 操作日志对象 + */ + public SysOperLog selectOperLogById(Long operId); + + /** + * 清空操作日志 + */ + public void cleanOperLog(); +} diff --git a/src/main/java/com/ruoyi/project/monitor/service/impl/SysJobLogServiceImpl.java b/src/main/java/com/ruoyi/project/monitor/service/impl/SysJobLogServiceImpl.java new file mode 100644 index 0000000..991cf81 --- /dev/null +++ b/src/main/java/com/ruoyi/project/monitor/service/impl/SysJobLogServiceImpl.java @@ -0,0 +1,87 @@ +package com.ruoyi.project.monitor.service.impl; + +import java.util.List; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import com.ruoyi.project.monitor.domain.SysJobLog; +import com.ruoyi.project.monitor.mapper.SysJobLogMapper; +import com.ruoyi.project.monitor.service.ISysJobLogService; + +/** + * 定时任务调度日志信息 服务层 + * + * @author ruoyi + */ +@Service +public class SysJobLogServiceImpl implements ISysJobLogService +{ + @Autowired + private SysJobLogMapper jobLogMapper; + + /** + * 获取quartz调度器日志的计划任务 + * + * @param jobLog 调度日志信息 + * @return 调度任务日志集合 + */ + @Override + public List selectJobLogList(SysJobLog jobLog) + { + return jobLogMapper.selectJobLogList(jobLog); + } + + /** + * 通过调度任务日志ID查询调度信息 + * + * @param jobLogId 调度任务日志ID + * @return 调度任务日志对象信息 + */ + @Override + public SysJobLog selectJobLogById(Long jobLogId) + { + return jobLogMapper.selectJobLogById(jobLogId); + } + + /** + * 新增任务日志 + * + * @param jobLog 调度日志信息 + */ + @Override + public void addJobLog(SysJobLog jobLog) + { + jobLogMapper.insertJobLog(jobLog); + } + + /** + * 批量删除调度日志信息 + * + * @param logIds 需要删除的数据ID + * @return 结果 + */ + @Override + public int deleteJobLogByIds(Long[] logIds) + { + return jobLogMapper.deleteJobLogByIds(logIds); + } + + /** + * 删除任务日志 + * + * @param jobId 调度日志ID + */ + @Override + public int deleteJobLogById(Long jobId) + { + return jobLogMapper.deleteJobLogById(jobId); + } + + /** + * 清空任务日志 + */ + @Override + public void cleanJobLog() + { + jobLogMapper.cleanJobLog(); + } +} diff --git a/src/main/java/com/ruoyi/project/monitor/service/impl/SysJobServiceImpl.java b/src/main/java/com/ruoyi/project/monitor/service/impl/SysJobServiceImpl.java new file mode 100644 index 0000000..4f2bec5 --- /dev/null +++ b/src/main/java/com/ruoyi/project/monitor/service/impl/SysJobServiceImpl.java @@ -0,0 +1,261 @@ +package com.ruoyi.project.monitor.service.impl; + +import java.util.List; +import javax.annotation.PostConstruct; +import org.quartz.JobDataMap; +import org.quartz.JobKey; +import org.quartz.Scheduler; +import org.quartz.SchedulerException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import com.ruoyi.common.constant.ScheduleConstants; +import com.ruoyi.common.exception.job.TaskException; +import com.ruoyi.common.utils.job.CronUtils; +import com.ruoyi.common.utils.job.ScheduleUtils; +import com.ruoyi.project.monitor.domain.SysJob; +import com.ruoyi.project.monitor.mapper.SysJobMapper; +import com.ruoyi.project.monitor.service.ISysJobService; + +/** + * 定时任务调度信息 服务层 + * + * @author ruoyi + */ +@Service +public class SysJobServiceImpl implements ISysJobService +{ + @Autowired + private Scheduler scheduler; + + @Autowired + private SysJobMapper jobMapper; + + /** + * 项目启动时,初始化定时器 主要是防止手动修改数据库导致未同步到定时任务处理(注:不能手动修改数据库ID和任务组名,否则会导致脏数据) + */ + @PostConstruct + public void init() throws SchedulerException, TaskException + { + scheduler.clear(); + List jobList = jobMapper.selectJobAll(); + for (SysJob job : jobList) + { + ScheduleUtils.createScheduleJob(scheduler, job); + } + } + + /** + * 获取quartz调度器的计划任务列表 + * + * @param job 调度信息 + * @return + */ + @Override + public List selectJobList(SysJob job) + { + return jobMapper.selectJobList(job); + } + + /** + * 通过调度任务ID查询调度信息 + * + * @param jobId 调度任务ID + * @return 调度任务对象信息 + */ + @Override + public SysJob selectJobById(Long jobId) + { + return jobMapper.selectJobById(jobId); + } + + /** + * 暂停任务 + * + * @param job 调度信息 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public int pauseJob(SysJob job) throws SchedulerException + { + Long jobId = job.getJobId(); + String jobGroup = job.getJobGroup(); + job.setStatus(ScheduleConstants.Status.PAUSE.getValue()); + int rows = jobMapper.updateJob(job); + if (rows > 0) + { + scheduler.pauseJob(ScheduleUtils.getJobKey(jobId, jobGroup)); + } + return rows; + } + + /** + * 恢复任务 + * + * @param job 调度信息 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public int resumeJob(SysJob job) throws SchedulerException + { + Long jobId = job.getJobId(); + String jobGroup = job.getJobGroup(); + job.setStatus(ScheduleConstants.Status.NORMAL.getValue()); + int rows = jobMapper.updateJob(job); + if (rows > 0) + { + scheduler.resumeJob(ScheduleUtils.getJobKey(jobId, jobGroup)); + } + return rows; + } + + /** + * 删除任务后,所对应的trigger也将被删除 + * + * @param job 调度信息 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public int deleteJob(SysJob job) throws SchedulerException + { + Long jobId = job.getJobId(); + String jobGroup = job.getJobGroup(); + int rows = jobMapper.deleteJobById(jobId); + if (rows > 0) + { + scheduler.deleteJob(ScheduleUtils.getJobKey(jobId, jobGroup)); + } + return rows; + } + + /** + * 批量删除调度信息 + * + * @param jobIds 需要删除的任务ID + * @return 结果 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public void deleteJobByIds(Long[] jobIds) throws SchedulerException + { + for (Long jobId : jobIds) + { + SysJob job = jobMapper.selectJobById(jobId); + deleteJob(job); + } + } + + /** + * 任务调度状态修改 + * + * @param job 调度信息 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public int changeStatus(SysJob job) throws SchedulerException + { + int rows = 0; + String status = job.getStatus(); + if (ScheduleConstants.Status.NORMAL.getValue().equals(status)) + { + rows = resumeJob(job); + } + else if (ScheduleConstants.Status.PAUSE.getValue().equals(status)) + { + rows = pauseJob(job); + } + return rows; + } + + /** + * 立即运行任务 + * + * @param job 调度信息 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public boolean run(SysJob job) throws SchedulerException + { + boolean result = false; + Long jobId = job.getJobId(); + String jobGroup = job.getJobGroup(); + SysJob properties = selectJobById(job.getJobId()); + // 参数 + JobDataMap dataMap = new JobDataMap(); + dataMap.put(ScheduleConstants.TASK_PROPERTIES, properties); + JobKey jobKey = ScheduleUtils.getJobKey(jobId, jobGroup); + if (scheduler.checkExists(jobKey)) + { + result = true; + scheduler.triggerJob(jobKey, dataMap); + } + return result; + } + + /** + * 新增任务 + * + * @param job 调度信息 调度信息 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public int insertJob(SysJob job) throws SchedulerException, TaskException + { + job.setStatus(ScheduleConstants.Status.PAUSE.getValue()); + int rows = jobMapper.insertJob(job); + if (rows > 0) + { + ScheduleUtils.createScheduleJob(scheduler, job); + } + return rows; + } + + /** + * 更新任务的时间表达式 + * + * @param job 调度信息 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public int updateJob(SysJob job) throws SchedulerException, TaskException + { + SysJob properties = selectJobById(job.getJobId()); + int rows = jobMapper.updateJob(job); + if (rows > 0) + { + updateSchedulerJob(job, properties.getJobGroup()); + } + return rows; + } + + /** + * 更新任务 + * + * @param job 任务对象 + * @param jobGroup 任务组名 + */ + public void updateSchedulerJob(SysJob job, String jobGroup) throws SchedulerException, TaskException + { + Long jobId = job.getJobId(); + // 判断是否存在 + JobKey jobKey = ScheduleUtils.getJobKey(jobId, jobGroup); + if (scheduler.checkExists(jobKey)) + { + // 防止创建时存在数据问题 先移除,然后在执行创建操作 + scheduler.deleteJob(jobKey); + } + ScheduleUtils.createScheduleJob(scheduler, job); + } + + /** + * 校验cron表达式是否有效 + * + * @param cronExpression 表达式 + * @return 结果 + */ + @Override + public boolean checkCronExpressionIsValid(String cronExpression) + { + return CronUtils.isValid(cronExpression); + } +} \ No newline at end of file diff --git a/src/main/java/com/ruoyi/project/monitor/service/impl/SysLogininforServiceImpl.java b/src/main/java/com/ruoyi/project/monitor/service/impl/SysLogininforServiceImpl.java new file mode 100644 index 0000000..e9b099f --- /dev/null +++ b/src/main/java/com/ruoyi/project/monitor/service/impl/SysLogininforServiceImpl.java @@ -0,0 +1,65 @@ +package com.ruoyi.project.monitor.service.impl; + +import java.util.List; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import com.ruoyi.project.monitor.domain.SysLogininfor; +import com.ruoyi.project.monitor.mapper.SysLogininforMapper; +import com.ruoyi.project.monitor.service.ISysLogininforService; + +/** + * 系统访问日志情况信息 服务层处理 + * + * @author ruoyi + */ +@Service +public class SysLogininforServiceImpl implements ISysLogininforService +{ + + @Autowired + private SysLogininforMapper logininforMapper; + + /** + * 新增系统登录日志 + * + * @param logininfor 访问日志对象 + */ + @Override + public void insertLogininfor(SysLogininfor logininfor) + { + logininforMapper.insertLogininfor(logininfor); + } + + /** + * 查询系统登录日志集合 + * + * @param logininfor 访问日志对象 + * @return 登录记录集合 + */ + @Override + public List selectLogininforList(SysLogininfor logininfor) + { + return logininforMapper.selectLogininforList(logininfor); + } + + /** + * 批量删除系统登录日志 + * + * @param infoIds 需要删除的登录日志ID + * @return 结果 + */ + @Override + public int deleteLogininforByIds(Long[] infoIds) + { + return logininforMapper.deleteLogininforByIds(infoIds); + } + + /** + * 清空系统登录日志 + */ + @Override + public void cleanLogininfor() + { + logininforMapper.cleanLogininfor(); + } +} diff --git a/src/main/java/com/ruoyi/project/monitor/service/impl/SysOperLogServiceImpl.java b/src/main/java/com/ruoyi/project/monitor/service/impl/SysOperLogServiceImpl.java new file mode 100644 index 0000000..bed9d8d --- /dev/null +++ b/src/main/java/com/ruoyi/project/monitor/service/impl/SysOperLogServiceImpl.java @@ -0,0 +1,76 @@ +package com.ruoyi.project.monitor.service.impl; + +import java.util.List; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import com.ruoyi.project.monitor.domain.SysOperLog; +import com.ruoyi.project.monitor.mapper.SysOperLogMapper; +import com.ruoyi.project.monitor.service.ISysOperLogService; + +/** + * 操作日志 服务层处理 + * + * @author ruoyi + */ +@Service +public class SysOperLogServiceImpl implements ISysOperLogService +{ + @Autowired + private SysOperLogMapper operLogMapper; + + /** + * 新增操作日志 + * + * @param operLog 操作日志对象 + */ + @Override + public void insertOperlog(SysOperLog operLog) + { + operLogMapper.insertOperlog(operLog); + } + + /** + * 查询系统操作日志集合 + * + * @param operLog 操作日志对象 + * @return 操作日志集合 + */ + @Override + public List selectOperLogList(SysOperLog operLog) + { + return operLogMapper.selectOperLogList(operLog); + } + + /** + * 批量删除系统操作日志 + * + * @param operIds 需要删除的操作日志ID + * @return 结果 + */ + @Override + public int deleteOperLogByIds(Long[] operIds) + { + return operLogMapper.deleteOperLogByIds(operIds); + } + + /** + * 查询操作日志详细 + * + * @param operId 操作ID + * @return 操作日志对象 + */ + @Override + public SysOperLog selectOperLogById(Long operId) + { + return operLogMapper.selectOperLogById(operId); + } + + /** + * 清空操作日志 + */ + @Override + public void cleanOperLog() + { + operLogMapper.cleanOperLog(); + } +} diff --git a/src/main/java/com/ruoyi/project/system/controller/SysConfigController.java b/src/main/java/com/ruoyi/project/system/controller/SysConfigController.java new file mode 100644 index 0000000..a4df558 --- /dev/null +++ b/src/main/java/com/ruoyi/project/system/controller/SysConfigController.java @@ -0,0 +1,133 @@ +package com.ruoyi.project.system.controller; + +import java.util.List; +import javax.servlet.http.HttpServletResponse; +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.utils.poi.ExcelUtil; +import com.ruoyi.framework.aspectj.lang.annotation.Log; +import com.ruoyi.framework.aspectj.lang.enums.BusinessType; +import com.ruoyi.framework.web.controller.BaseController; +import com.ruoyi.framework.web.domain.AjaxResult; +import com.ruoyi.framework.web.page.TableDataInfo; +import com.ruoyi.project.system.domain.SysConfig; +import com.ruoyi.project.system.service.ISysConfigService; + +/** + * 参数配置 信息操作处理 + * + * @author ruoyi + */ +@RestController +@RequestMapping("/system/config") +public class SysConfigController extends BaseController +{ + @Autowired + private ISysConfigService configService; + + /** + * 获取参数配置列表 + */ + @PreAuthorize("@ss.hasPermi('system:config:list')") + @GetMapping("/list") + public TableDataInfo list(SysConfig config) + { + startPage(); + List list = configService.selectConfigList(config); + return getDataTable(list); + } + + @Log(title = "参数管理", businessType = BusinessType.EXPORT) + @PreAuthorize("@ss.hasPermi('system:config:export')") + @PostMapping("/export") + public void export(HttpServletResponse response, SysConfig config) + { + List list = configService.selectConfigList(config); + ExcelUtil util = new ExcelUtil(SysConfig.class); + util.exportExcel(response, list, "参数数据"); + } + + /** + * 根据参数编号获取详细信息 + */ + @PreAuthorize("@ss.hasPermi('system:config:query')") + @GetMapping(value = "/{configId}") + public AjaxResult getInfo(@PathVariable Long configId) + { + return success(configService.selectConfigById(configId)); + } + + /** + * 根据参数键名查询参数值 + */ + @GetMapping(value = "/configKey/{configKey}") + public AjaxResult getConfigKey(@PathVariable String configKey) + { + return success(configService.selectConfigByKey(configKey)); + } + + /** + * 新增参数配置 + */ + @PreAuthorize("@ss.hasPermi('system:config:add')") + @Log(title = "参数管理", businessType = BusinessType.INSERT) + @PostMapping + public AjaxResult add(@Validated @RequestBody SysConfig config) + { + if (!configService.checkConfigKeyUnique(config)) + { + return error("新增参数'" + config.getConfigName() + "'失败,参数键名已存在"); + } + config.setCreateBy(getUsername()); + return toAjax(configService.insertConfig(config)); + } + + /** + * 修改参数配置 + */ + @PreAuthorize("@ss.hasPermi('system:config:edit')") + @Log(title = "参数管理", businessType = BusinessType.UPDATE) + @PutMapping + public AjaxResult edit(@Validated @RequestBody SysConfig config) + { + if (!configService.checkConfigKeyUnique(config)) + { + return error("修改参数'" + config.getConfigName() + "'失败,参数键名已存在"); + } + config.setUpdateBy(getUsername()); + return toAjax(configService.updateConfig(config)); + } + + /** + * 删除参数配置 + */ + @PreAuthorize("@ss.hasPermi('system:config:remove')") + @Log(title = "参数管理", businessType = BusinessType.DELETE) + @DeleteMapping("/{configIds}") + public AjaxResult remove(@PathVariable Long[] configIds) + { + configService.deleteConfigByIds(configIds); + return success(); + } + + /** + * 刷新参数缓存 + */ + @PreAuthorize("@ss.hasPermi('system:config:remove')") + @Log(title = "参数管理", businessType = BusinessType.CLEAN) + @DeleteMapping("/refreshCache") + public AjaxResult refreshCache() + { + configService.resetConfigCache(); + return success(); + } +} diff --git a/src/main/java/com/ruoyi/project/system/controller/SysDeptController.java b/src/main/java/com/ruoyi/project/system/controller/SysDeptController.java new file mode 100644 index 0000000..30eee7d --- /dev/null +++ b/src/main/java/com/ruoyi/project/system/controller/SysDeptController.java @@ -0,0 +1,132 @@ +package com.ruoyi.project.system.controller; + +import java.util.List; +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; +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.constant.UserConstants; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.framework.aspectj.lang.annotation.Log; +import com.ruoyi.framework.aspectj.lang.enums.BusinessType; +import com.ruoyi.framework.web.controller.BaseController; +import com.ruoyi.framework.web.domain.AjaxResult; +import com.ruoyi.project.system.domain.SysDept; +import com.ruoyi.project.system.service.ISysDeptService; + +/** + * 部门信息 + * + * @author ruoyi + */ +@RestController +@RequestMapping("/system/dept") +public class SysDeptController extends BaseController +{ + @Autowired + private ISysDeptService deptService; + + /** + * 获取部门列表 + */ + @PreAuthorize("@ss.hasPermi('system:dept:list')") + @GetMapping("/list") + public AjaxResult list(SysDept dept) + { + List depts = deptService.selectDeptList(dept); + return success(depts); + } + + /** + * 查询部门列表(排除节点) + */ + @PreAuthorize("@ss.hasPermi('system:dept:list')") + @GetMapping("/list/exclude/{deptId}") + public AjaxResult excludeChild(@PathVariable(value = "deptId", required = false) Long deptId) + { + List depts = deptService.selectDeptList(new SysDept()); + depts.removeIf(d -> d.getDeptId().intValue() == deptId || ArrayUtils.contains(StringUtils.split(d.getAncestors(), ","), deptId + "")); + return success(depts); + } + + /** + * 根据部门编号获取详细信息 + */ + @PreAuthorize("@ss.hasPermi('system:dept:query')") + @GetMapping(value = "/{deptId}") + public AjaxResult getInfo(@PathVariable Long deptId) + { + deptService.checkDeptDataScope(deptId); + return success(deptService.selectDeptById(deptId)); + } + + /** + * 新增部门 + */ + @PreAuthorize("@ss.hasPermi('system:dept:add')") + @Log(title = "部门管理", businessType = BusinessType.INSERT) + @PostMapping + public AjaxResult add(@Validated @RequestBody SysDept dept) + { + if (!deptService.checkDeptNameUnique(dept)) + { + return error("新增部门'" + dept.getDeptName() + "'失败,部门名称已存在"); + } + dept.setCreateBy(getUsername()); + return toAjax(deptService.insertDept(dept)); + } + + /** + * 修改部门 + */ + @PreAuthorize("@ss.hasPermi('system:dept:edit')") + @Log(title = "部门管理", businessType = BusinessType.UPDATE) + @PutMapping + public AjaxResult edit(@Validated @RequestBody SysDept dept) + { + Long deptId = dept.getDeptId(); + deptService.checkDeptDataScope(deptId); + if (!deptService.checkDeptNameUnique(dept)) + { + return error("修改部门'" + dept.getDeptName() + "'失败,部门名称已存在"); + } + else if (dept.getParentId().equals(deptId)) + { + return error("修改部门'" + dept.getDeptName() + "'失败,上级部门不能是自己"); + } + else if (StringUtils.equals(UserConstants.DEPT_DISABLE, dept.getStatus()) && deptService.selectNormalChildrenDeptById(deptId) > 0) + { + return error("该部门包含未停用的子部门!"); + } + dept.setUpdateBy(getUsername()); + return toAjax(deptService.updateDept(dept)); + } + + /** + * 删除部门 + */ + @PreAuthorize("@ss.hasPermi('system:dept:remove')") + @Log(title = "部门管理", businessType = BusinessType.DELETE) + @DeleteMapping("/{deptId}") + public AjaxResult remove(@PathVariable Long deptId) + { + if (deptService.hasChildByDeptId(deptId)) + { + return warn("存在下级部门,不允许删除"); + } + if (deptService.checkDeptExistUser(deptId)) + { + return warn("部门存在用户,不允许删除"); + } + deptService.checkDeptDataScope(deptId); + return toAjax(deptService.deleteDeptById(deptId)); + } +} diff --git a/src/main/java/com/ruoyi/project/system/controller/SysDictDataController.java b/src/main/java/com/ruoyi/project/system/controller/SysDictDataController.java new file mode 100644 index 0000000..c76a214 --- /dev/null +++ b/src/main/java/com/ruoyi/project/system/controller/SysDictDataController.java @@ -0,0 +1,121 @@ +package com.ruoyi.project.system.controller; + +import java.util.ArrayList; +import java.util.List; +import javax.servlet.http.HttpServletResponse; +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.utils.StringUtils; +import com.ruoyi.common.utils.poi.ExcelUtil; +import com.ruoyi.framework.aspectj.lang.annotation.Log; +import com.ruoyi.framework.aspectj.lang.enums.BusinessType; +import com.ruoyi.framework.web.controller.BaseController; +import com.ruoyi.framework.web.domain.AjaxResult; +import com.ruoyi.framework.web.page.TableDataInfo; +import com.ruoyi.project.system.domain.SysDictData; +import com.ruoyi.project.system.service.ISysDictDataService; +import com.ruoyi.project.system.service.ISysDictTypeService; + +/** + * 数据字典信息 + * + * @author ruoyi + */ +@RestController +@RequestMapping("/system/dict/data") +public class SysDictDataController extends BaseController +{ + @Autowired + private ISysDictDataService dictDataService; + + @Autowired + private ISysDictTypeService dictTypeService; + + @PreAuthorize("@ss.hasPermi('system:dict:list')") + @GetMapping("/list") + public TableDataInfo list(SysDictData dictData) + { + startPage(); + List list = dictDataService.selectDictDataList(dictData); + return getDataTable(list); + } + + @Log(title = "字典数据", businessType = BusinessType.EXPORT) + @PreAuthorize("@ss.hasPermi('system:dict:export')") + @PostMapping("/export") + public void export(HttpServletResponse response, SysDictData dictData) + { + List list = dictDataService.selectDictDataList(dictData); + ExcelUtil util = new ExcelUtil(SysDictData.class); + util.exportExcel(response, list, "字典数据"); + } + + /** + * 查询字典数据详细 + */ + @PreAuthorize("@ss.hasPermi('system:dict:query')") + @GetMapping(value = "/{dictCode}") + public AjaxResult getInfo(@PathVariable Long dictCode) + { + return success(dictDataService.selectDictDataById(dictCode)); + } + + /** + * 根据字典类型查询字典数据信息 + */ + @GetMapping(value = "/type/{dictType}") + public AjaxResult dictType(@PathVariable String dictType) + { + List data = dictTypeService.selectDictDataByType(dictType); + if (StringUtils.isNull(data)) + { + data = new ArrayList(); + } + return success(data); + } + + /** + * 新增字典类型 + */ + @PreAuthorize("@ss.hasPermi('system:dict:add')") + @Log(title = "字典数据", businessType = BusinessType.INSERT) + @PostMapping + public AjaxResult add(@Validated @RequestBody SysDictData dict) + { + dict.setCreateBy(getUsername()); + return toAjax(dictDataService.insertDictData(dict)); + } + + /** + * 修改保存字典类型 + */ + @PreAuthorize("@ss.hasPermi('system:dict:edit')") + @Log(title = "字典数据", businessType = BusinessType.UPDATE) + @PutMapping + public AjaxResult edit(@Validated @RequestBody SysDictData dict) + { + dict.setUpdateBy(getUsername()); + return toAjax(dictDataService.updateDictData(dict)); + } + + /** + * 删除字典类型 + */ + @PreAuthorize("@ss.hasPermi('system:dict:remove')") + @Log(title = "字典类型", businessType = BusinessType.DELETE) + @DeleteMapping("/{dictCodes}") + public AjaxResult remove(@PathVariable Long[] dictCodes) + { + dictDataService.deleteDictDataByIds(dictCodes); + return success(); + } +} diff --git a/src/main/java/com/ruoyi/project/system/controller/SysDictTypeController.java b/src/main/java/com/ruoyi/project/system/controller/SysDictTypeController.java new file mode 100644 index 0000000..c3a4d0c --- /dev/null +++ b/src/main/java/com/ruoyi/project/system/controller/SysDictTypeController.java @@ -0,0 +1,131 @@ +package com.ruoyi.project.system.controller; + +import java.util.List; +import javax.servlet.http.HttpServletResponse; +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.utils.poi.ExcelUtil; +import com.ruoyi.framework.aspectj.lang.annotation.Log; +import com.ruoyi.framework.aspectj.lang.enums.BusinessType; +import com.ruoyi.framework.web.controller.BaseController; +import com.ruoyi.framework.web.domain.AjaxResult; +import com.ruoyi.framework.web.page.TableDataInfo; +import com.ruoyi.project.system.domain.SysDictType; +import com.ruoyi.project.system.service.ISysDictTypeService; + +/** + * 数据字典信息 + * + * @author ruoyi + */ +@RestController +@RequestMapping("/system/dict/type") +public class SysDictTypeController extends BaseController +{ + @Autowired + private ISysDictTypeService dictTypeService; + + @PreAuthorize("@ss.hasPermi('system:dict:list')") + @GetMapping("/list") + public TableDataInfo list(SysDictType dictType) + { + startPage(); + List list = dictTypeService.selectDictTypeList(dictType); + return getDataTable(list); + } + + @Log(title = "字典类型", businessType = BusinessType.EXPORT) + @PreAuthorize("@ss.hasPermi('system:dict:export')") + @PostMapping("/export") + public void export(HttpServletResponse response, SysDictType dictType) + { + List list = dictTypeService.selectDictTypeList(dictType); + ExcelUtil util = new ExcelUtil(SysDictType.class); + util.exportExcel(response, list, "字典类型"); + } + + /** + * 查询字典类型详细 + */ + @PreAuthorize("@ss.hasPermi('system:dict:query')") + @GetMapping(value = "/{dictId}") + public AjaxResult getInfo(@PathVariable Long dictId) + { + return success(dictTypeService.selectDictTypeById(dictId)); + } + + /** + * 新增字典类型 + */ + @PreAuthorize("@ss.hasPermi('system:dict:add')") + @Log(title = "字典类型", businessType = BusinessType.INSERT) + @PostMapping + public AjaxResult add(@Validated @RequestBody SysDictType dict) + { + if (!dictTypeService.checkDictTypeUnique(dict)) + { + return error("新增字典'" + dict.getDictName() + "'失败,字典类型已存在"); + } + dict.setCreateBy(getUsername()); + return toAjax(dictTypeService.insertDictType(dict)); + } + + /** + * 修改字典类型 + */ + @PreAuthorize("@ss.hasPermi('system:dict:edit')") + @Log(title = "字典类型", businessType = BusinessType.UPDATE) + @PutMapping + public AjaxResult edit(@Validated @RequestBody SysDictType dict) + { + if (!dictTypeService.checkDictTypeUnique(dict)) + { + return error("修改字典'" + dict.getDictName() + "'失败,字典类型已存在"); + } + dict.setUpdateBy(getUsername()); + return toAjax(dictTypeService.updateDictType(dict)); + } + + /** + * 删除字典类型 + */ + @PreAuthorize("@ss.hasPermi('system:dict:remove')") + @Log(title = "字典类型", businessType = BusinessType.DELETE) + @DeleteMapping("/{dictIds}") + public AjaxResult remove(@PathVariable Long[] dictIds) + { + dictTypeService.deleteDictTypeByIds(dictIds); + return success(); + } + + /** + * 刷新字典缓存 + */ + @PreAuthorize("@ss.hasPermi('system:dict:remove')") + @Log(title = "字典类型", businessType = BusinessType.CLEAN) + @DeleteMapping("/refreshCache") + public AjaxResult refreshCache() + { + dictTypeService.resetDictCache(); + return success(); + } + + /** + * 获取字典选择框列表 + */ + @GetMapping("/optionselect") + public AjaxResult optionselect() + { + List dictTypes = dictTypeService.selectDictTypeAll(); + return success(dictTypes); + } +} diff --git a/src/main/java/com/ruoyi/project/system/controller/SysIndexController.java b/src/main/java/com/ruoyi/project/system/controller/SysIndexController.java new file mode 100644 index 0000000..5b587e2 --- /dev/null +++ b/src/main/java/com/ruoyi/project/system/controller/SysIndexController.java @@ -0,0 +1,29 @@ +package com.ruoyi.project.system.controller; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.framework.config.RuoYiConfig; + +/** + * 首页 + * + * @author ruoyi + */ +@RestController +public class SysIndexController +{ + /** 系统基础配置 */ + @Autowired + private RuoYiConfig ruoyiConfig; + + /** + * 访问首页,提示语 + */ + @RequestMapping("/") + public String index() + { + return StringUtils.format("欢迎使用{}后台管理框架,当前版本:v{},请通过前端地址访问。", ruoyiConfig.getName(), ruoyiConfig.getVersion()); + } +} diff --git a/src/main/java/com/ruoyi/project/system/controller/SysLoginController.java b/src/main/java/com/ruoyi/project/system/controller/SysLoginController.java new file mode 100644 index 0000000..2e707af --- /dev/null +++ b/src/main/java/com/ruoyi/project/system/controller/SysLoginController.java @@ -0,0 +1,86 @@ +package com.ruoyi.project.system.controller; + +import java.util.List; +import java.util.Set; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RestController; +import com.ruoyi.common.constant.Constants; +import com.ruoyi.common.utils.SecurityUtils; +import com.ruoyi.framework.security.LoginBody; +import com.ruoyi.framework.security.service.SysLoginService; +import com.ruoyi.framework.security.service.SysPermissionService; +import com.ruoyi.framework.web.domain.AjaxResult; +import com.ruoyi.project.system.domain.SysMenu; +import com.ruoyi.project.system.domain.SysUser; +import com.ruoyi.project.system.service.ISysMenuService; + +/** + * 登录验证 + * + * @author ruoyi + */ +@RestController +public class SysLoginController +{ + @Autowired + private SysLoginService loginService; + + @Autowired + private ISysMenuService menuService; + + @Autowired + private SysPermissionService permissionService; + + /** + * 登录方法 + * + * @param loginBody 登录信息 + * @return 结果 + */ + @PostMapping("/login") + public AjaxResult login(@RequestBody LoginBody loginBody) + { + AjaxResult ajax = AjaxResult.success(); + // 生成令牌 + String token = loginService.login(loginBody.getUsername(), loginBody.getPassword(), loginBody.getCode(), + loginBody.getUuid()); + ajax.put(Constants.TOKEN, token); + return ajax; + } + + /** + * 获取用户信息 + * + * @return 用户信息 + */ + @GetMapping("getInfo") + public AjaxResult getInfo() + { + SysUser user = SecurityUtils.getLoginUser().getUser(); + // 角色集合 + Set roles = permissionService.getRolePermission(user); + // 权限集合 + Set permissions = permissionService.getMenuPermission(user); + AjaxResult ajax = AjaxResult.success(); + ajax.put("user", user); + ajax.put("roles", roles); + ajax.put("permissions", permissions); + return ajax; + } + + /** + * 获取路由信息 + * + * @return 路由信息 + */ + @GetMapping("getRouters") + public AjaxResult getRouters() + { + Long userId = SecurityUtils.getUserId(); + List menus = menuService.selectMenuTreeByUserId(userId); + return AjaxResult.success(menuService.buildMenus(menus)); + } +} diff --git a/src/main/java/com/ruoyi/project/system/controller/SysMenuController.java b/src/main/java/com/ruoyi/project/system/controller/SysMenuController.java new file mode 100644 index 0000000..1e2ccf0 --- /dev/null +++ b/src/main/java/com/ruoyi/project/system/controller/SysMenuController.java @@ -0,0 +1,142 @@ +package com.ruoyi.project.system.controller; + +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.constant.UserConstants; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.framework.aspectj.lang.annotation.Log; +import com.ruoyi.framework.aspectj.lang.enums.BusinessType; +import com.ruoyi.framework.web.controller.BaseController; +import com.ruoyi.framework.web.domain.AjaxResult; +import com.ruoyi.project.system.domain.SysMenu; +import com.ruoyi.project.system.service.ISysMenuService; + +/** + * 菜单信息 + * + * @author ruoyi + */ +@RestController +@RequestMapping("/system/menu") +public class SysMenuController extends BaseController +{ + @Autowired + private ISysMenuService menuService; + + /** + * 获取菜单列表 + */ + @PreAuthorize("@ss.hasPermi('system:menu:list')") + @GetMapping("/list") + public AjaxResult list(SysMenu menu) + { + List menus = menuService.selectMenuList(menu, getUserId()); + return success(menus); + } + + /** + * 根据菜单编号获取详细信息 + */ + @PreAuthorize("@ss.hasPermi('system:menu:query')") + @GetMapping(value = "/{menuId}") + public AjaxResult getInfo(@PathVariable Long menuId) + { + return success(menuService.selectMenuById(menuId)); + } + + /** + * 获取菜单下拉树列表 + */ + @GetMapping("/treeselect") + public AjaxResult treeselect(SysMenu menu) + { + List menus = menuService.selectMenuList(menu, getUserId()); + return success(menuService.buildMenuTreeSelect(menus)); + } + + /** + * 加载对应角色菜单列表树 + */ + @GetMapping(value = "/roleMenuTreeselect/{roleId}") + public AjaxResult roleMenuTreeselect(@PathVariable("roleId") Long roleId) + { + List menus = menuService.selectMenuList(getUserId()); + AjaxResult ajax = AjaxResult.success(); + ajax.put("checkedKeys", menuService.selectMenuListByRoleId(roleId)); + ajax.put("menus", menuService.buildMenuTreeSelect(menus)); + return ajax; + } + + /** + * 新增菜单 + */ + @PreAuthorize("@ss.hasPermi('system:menu:add')") + @Log(title = "菜单管理", businessType = BusinessType.INSERT) + @PostMapping + public AjaxResult add(@Validated @RequestBody SysMenu menu) + { + if (!menuService.checkMenuNameUnique(menu)) + { + return error("新增菜单'" + menu.getMenuName() + "'失败,菜单名称已存在"); + } + else if (UserConstants.YES_FRAME.equals(menu.getIsFrame()) && !StringUtils.ishttp(menu.getPath())) + { + return error("新增菜单'" + menu.getMenuName() + "'失败,地址必须以http(s)://开头"); + } + menu.setCreateBy(getUsername()); + return toAjax(menuService.insertMenu(menu)); + } + + /** + * 修改菜单 + */ + @PreAuthorize("@ss.hasPermi('system:menu:edit')") + @Log(title = "菜单管理", businessType = BusinessType.UPDATE) + @PutMapping + public AjaxResult edit(@Validated @RequestBody SysMenu menu) + { + if (!menuService.checkMenuNameUnique(menu)) + { + return error("修改菜单'" + menu.getMenuName() + "'失败,菜单名称已存在"); + } + else if (UserConstants.YES_FRAME.equals(menu.getIsFrame()) && !StringUtils.ishttp(menu.getPath())) + { + return error("修改菜单'" + menu.getMenuName() + "'失败,地址必须以http(s)://开头"); + } + else if (menu.getMenuId().equals(menu.getParentId())) + { + return error("修改菜单'" + menu.getMenuName() + "'失败,上级菜单不能选择自己"); + } + menu.setUpdateBy(getUsername()); + return toAjax(menuService.updateMenu(menu)); + } + + /** + * 删除菜单 + */ + @PreAuthorize("@ss.hasPermi('system:menu:remove')") + @Log(title = "菜单管理", businessType = BusinessType.DELETE) + @DeleteMapping("/{menuId}") + public AjaxResult remove(@PathVariable("menuId") Long menuId) + { + if (menuService.hasChildByMenuId(menuId)) + { + return warn("存在子菜单,不允许删除"); + } + if (menuService.checkMenuExistRole(menuId)) + { + return warn("菜单已分配,不允许删除"); + } + return toAjax(menuService.deleteMenuById(menuId)); + } +} \ No newline at end of file diff --git a/src/main/java/com/ruoyi/project/system/controller/SysNoticeController.java b/src/main/java/com/ruoyi/project/system/controller/SysNoticeController.java new file mode 100644 index 0000000..6519ae3 --- /dev/null +++ b/src/main/java/com/ruoyi/project/system/controller/SysNoticeController.java @@ -0,0 +1,91 @@ +package com.ruoyi.project.system.controller; + +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.framework.aspectj.lang.annotation.Log; +import com.ruoyi.framework.aspectj.lang.enums.BusinessType; +import com.ruoyi.framework.web.controller.BaseController; +import com.ruoyi.framework.web.domain.AjaxResult; +import com.ruoyi.framework.web.page.TableDataInfo; +import com.ruoyi.project.system.domain.SysNotice; +import com.ruoyi.project.system.service.ISysNoticeService; + +/** + * 公告 信息操作处理 + * + * @author ruoyi + */ +@RestController +@RequestMapping("/system/notice") +public class SysNoticeController extends BaseController +{ + @Autowired + private ISysNoticeService noticeService; + + /** + * 获取通知公告列表 + */ + @PreAuthorize("@ss.hasPermi('system:notice:list')") + @GetMapping("/list") + public TableDataInfo list(SysNotice notice) + { + startPage(); + List list = noticeService.selectNoticeList(notice); + return getDataTable(list); + } + + /** + * 根据通知公告编号获取详细信息 + */ + @PreAuthorize("@ss.hasPermi('system:notice:query')") + @GetMapping(value = "/{noticeId}") + public AjaxResult getInfo(@PathVariable Long noticeId) + { + return success(noticeService.selectNoticeById(noticeId)); + } + + /** + * 新增通知公告 + */ + @PreAuthorize("@ss.hasPermi('system:notice:add')") + @Log(title = "通知公告", businessType = BusinessType.INSERT) + @PostMapping + public AjaxResult add(@Validated @RequestBody SysNotice notice) + { + notice.setCreateBy(getUsername()); + return toAjax(noticeService.insertNotice(notice)); + } + + /** + * 修改通知公告 + */ + @PreAuthorize("@ss.hasPermi('system:notice:edit')") + @Log(title = "通知公告", businessType = BusinessType.UPDATE) + @PutMapping + public AjaxResult edit(@Validated @RequestBody SysNotice notice) + { + notice.setUpdateBy(getUsername()); + return toAjax(noticeService.updateNotice(notice)); + } + + /** + * 删除通知公告 + */ + @PreAuthorize("@ss.hasPermi('system:notice:remove')") + @Log(title = "通知公告", businessType = BusinessType.DELETE) + @DeleteMapping("/{noticeIds}") + public AjaxResult remove(@PathVariable Long[] noticeIds) + { + return toAjax(noticeService.deleteNoticeByIds(noticeIds)); + } +} diff --git a/src/main/java/com/ruoyi/project/system/controller/SysPostController.java b/src/main/java/com/ruoyi/project/system/controller/SysPostController.java new file mode 100644 index 0000000..6bf46a1 --- /dev/null +++ b/src/main/java/com/ruoyi/project/system/controller/SysPostController.java @@ -0,0 +1,129 @@ +package com.ruoyi.project.system.controller; + +import java.util.List; +import javax.servlet.http.HttpServletResponse; +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.utils.poi.ExcelUtil; +import com.ruoyi.framework.aspectj.lang.annotation.Log; +import com.ruoyi.framework.aspectj.lang.enums.BusinessType; +import com.ruoyi.framework.web.controller.BaseController; +import com.ruoyi.framework.web.domain.AjaxResult; +import com.ruoyi.framework.web.page.TableDataInfo; +import com.ruoyi.project.system.domain.SysPost; +import com.ruoyi.project.system.service.ISysPostService; + +/** + * 岗位信息操作处理 + * + * @author ruoyi + */ +@RestController +@RequestMapping("/system/post") +public class SysPostController extends BaseController +{ + @Autowired + private ISysPostService postService; + + /** + * 获取岗位列表 + */ + @PreAuthorize("@ss.hasPermi('system:post:list')") + @GetMapping("/list") + public TableDataInfo list(SysPost post) + { + startPage(); + List list = postService.selectPostList(post); + return getDataTable(list); + } + + @Log(title = "岗位管理", businessType = BusinessType.EXPORT) + @PreAuthorize("@ss.hasPermi('system:post:export')") + @PostMapping("/export") + public void export(HttpServletResponse response, SysPost post) + { + List list = postService.selectPostList(post); + ExcelUtil util = new ExcelUtil(SysPost.class); + util.exportExcel(response, list, "岗位数据"); + } + + /** + * 根据岗位编号获取详细信息 + */ + @PreAuthorize("@ss.hasPermi('system:post:query')") + @GetMapping(value = "/{postId}") + public AjaxResult getInfo(@PathVariable Long postId) + { + return success(postService.selectPostById(postId)); + } + + /** + * 新增岗位 + */ + @PreAuthorize("@ss.hasPermi('system:post:add')") + @Log(title = "岗位管理", businessType = BusinessType.INSERT) + @PostMapping + public AjaxResult add(@Validated @RequestBody SysPost post) + { + if (!postService.checkPostNameUnique(post)) + { + return error("新增岗位'" + post.getPostName() + "'失败,岗位名称已存在"); + } + else if (!postService.checkPostCodeUnique(post)) + { + return error("新增岗位'" + post.getPostName() + "'失败,岗位编码已存在"); + } + post.setCreateBy(getUsername()); + return toAjax(postService.insertPost(post)); + } + + /** + * 修改岗位 + */ + @PreAuthorize("@ss.hasPermi('system:post:edit')") + @Log(title = "岗位管理", businessType = BusinessType.UPDATE) + @PutMapping + public AjaxResult edit(@Validated @RequestBody SysPost post) + { + if (!postService.checkPostNameUnique(post)) + { + return error("修改岗位'" + post.getPostName() + "'失败,岗位名称已存在"); + } + else if (!postService.checkPostCodeUnique(post)) + { + return error("修改岗位'" + post.getPostName() + "'失败,岗位编码已存在"); + } + post.setUpdateBy(getUsername()); + return toAjax(postService.updatePost(post)); + } + + /** + * 删除岗位 + */ + @PreAuthorize("@ss.hasPermi('system:post:remove')") + @Log(title = "岗位管理", businessType = BusinessType.DELETE) + @DeleteMapping("/{postIds}") + public AjaxResult remove(@PathVariable Long[] postIds) + { + return toAjax(postService.deletePostByIds(postIds)); + } + + /** + * 获取岗位选择框列表 + */ + @GetMapping("/optionselect") + public AjaxResult optionselect() + { + List posts = postService.selectPostAll(); + return success(posts); + } +} diff --git a/src/main/java/com/ruoyi/project/system/controller/SysProfileController.java b/src/main/java/com/ruoyi/project/system/controller/SysProfileController.java new file mode 100644 index 0000000..c56c92e --- /dev/null +++ b/src/main/java/com/ruoyi/project/system/controller/SysProfileController.java @@ -0,0 +1,137 @@ +package com.ruoyi.project.system.controller; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +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.RequestParam; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.multipart.MultipartFile; +import com.ruoyi.common.utils.SecurityUtils; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.file.FileUploadUtils; +import com.ruoyi.common.utils.file.MimeTypeUtils; +import com.ruoyi.framework.aspectj.lang.annotation.Log; +import com.ruoyi.framework.aspectj.lang.enums.BusinessType; +import com.ruoyi.framework.config.RuoYiConfig; +import com.ruoyi.framework.security.LoginUser; +import com.ruoyi.framework.security.service.TokenService; +import com.ruoyi.framework.web.controller.BaseController; +import com.ruoyi.framework.web.domain.AjaxResult; +import com.ruoyi.project.system.domain.SysUser; +import com.ruoyi.project.system.service.ISysUserService; + +/** + * 个人信息 业务处理 + * + * @author ruoyi + */ +@RestController +@RequestMapping("/system/user/profile") +public class SysProfileController extends BaseController +{ + @Autowired + private ISysUserService userService; + + @Autowired + private TokenService tokenService; + + /** + * 个人信息 + */ + @GetMapping + public AjaxResult profile() + { + LoginUser loginUser = getLoginUser(); + SysUser user = loginUser.getUser(); + AjaxResult ajax = AjaxResult.success(user); + ajax.put("roleGroup", userService.selectUserRoleGroup(loginUser.getUsername())); + ajax.put("postGroup", userService.selectUserPostGroup(loginUser.getUsername())); + return ajax; + } + + /** + * 修改用户 + */ + @Log(title = "个人信息", businessType = BusinessType.UPDATE) + @PutMapping + public AjaxResult updateProfile(@RequestBody SysUser user) + { + LoginUser loginUser = getLoginUser(); + SysUser currentUser = loginUser.getUser(); + currentUser.setNickName(user.getNickName()); + currentUser.setEmail(user.getEmail()); + currentUser.setPhonenumber(user.getPhonenumber()); + currentUser.setSex(user.getSex()); + if (StringUtils.isNotEmpty(user.getPhonenumber()) && !userService.checkPhoneUnique(currentUser)) + { + return error("修改用户'" + loginUser.getUsername() + "'失败,手机号码已存在"); + } + if (StringUtils.isNotEmpty(user.getEmail()) && !userService.checkEmailUnique(currentUser)) + { + return error("修改用户'" + loginUser.getUsername() + "'失败,邮箱账号已存在"); + } + if (userService.updateUserProfile(currentUser) > 0) + { + // 更新缓存用户信息 + tokenService.setLoginUser(loginUser); + return success(); + } + return error("修改个人信息异常,请联系管理员"); + } + + /** + * 重置密码 + */ + @Log(title = "个人信息", businessType = BusinessType.UPDATE) + @PutMapping("/updatePwd") + public AjaxResult updatePwd(String oldPassword, String newPassword) + { + LoginUser loginUser = getLoginUser(); + String userName = loginUser.getUsername(); + String password = loginUser.getPassword(); + if (!SecurityUtils.matchesPassword(oldPassword, password)) + { + return error("修改密码失败,旧密码错误"); + } + if (SecurityUtils.matchesPassword(newPassword, password)) + { + return error("新密码不能与旧密码相同"); + } + newPassword = SecurityUtils.encryptPassword(newPassword); + if (userService.resetUserPwd(userName, newPassword) > 0) + { + // 更新缓存用户密码 + loginUser.getUser().setPassword(newPassword); + tokenService.setLoginUser(loginUser); + return success(); + } + return error("修改密码异常,请联系管理员"); + } + + /** + * 头像上传 + */ + @Log(title = "用户头像", businessType = BusinessType.UPDATE) + @PostMapping("/avatar") + public AjaxResult avatar(@RequestParam("avatarfile") MultipartFile file) throws Exception + { + if (!file.isEmpty()) + { + LoginUser loginUser = getLoginUser(); + String avatar = FileUploadUtils.upload(RuoYiConfig.getAvatarPath(), file, MimeTypeUtils.IMAGE_EXTENSION); + if (userService.updateUserAvatar(loginUser.getUsername(), avatar)) + { + AjaxResult ajax = AjaxResult.success(); + ajax.put("imgUrl", avatar); + // 更新缓存用户头像 + loginUser.getUser().setAvatar(avatar); + tokenService.setLoginUser(loginUser); + return ajax; + } + } + return error("上传图片异常,请联系管理员"); + } +} diff --git a/src/main/java/com/ruoyi/project/system/controller/SysRegisterController.java b/src/main/java/com/ruoyi/project/system/controller/SysRegisterController.java new file mode 100644 index 0000000..d8f570a --- /dev/null +++ b/src/main/java/com/ruoyi/project/system/controller/SysRegisterController.java @@ -0,0 +1,38 @@ +package com.ruoyi.project.system.controller; + +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; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.framework.security.RegisterBody; +import com.ruoyi.framework.security.service.SysRegisterService; +import com.ruoyi.framework.web.controller.BaseController; +import com.ruoyi.framework.web.domain.AjaxResult; +import com.ruoyi.project.system.service.ISysConfigService; + +/** + * 注册验证 + * + * @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); + } +} diff --git a/src/main/java/com/ruoyi/project/system/controller/SysRoleController.java b/src/main/java/com/ruoyi/project/system/controller/SysRoleController.java new file mode 100644 index 0000000..046bea7 --- /dev/null +++ b/src/main/java/com/ruoyi/project/system/controller/SysRoleController.java @@ -0,0 +1,262 @@ +package com.ruoyi.project.system.controller; + +import java.util.List; +import javax.servlet.http.HttpServletResponse; +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.utils.StringUtils; +import com.ruoyi.common.utils.poi.ExcelUtil; +import com.ruoyi.framework.aspectj.lang.annotation.Log; +import com.ruoyi.framework.aspectj.lang.enums.BusinessType; +import com.ruoyi.framework.security.LoginUser; +import com.ruoyi.framework.security.service.SysPermissionService; +import com.ruoyi.framework.security.service.TokenService; +import com.ruoyi.framework.web.controller.BaseController; +import com.ruoyi.framework.web.domain.AjaxResult; +import com.ruoyi.framework.web.page.TableDataInfo; +import com.ruoyi.project.system.domain.SysDept; +import com.ruoyi.project.system.domain.SysRole; +import com.ruoyi.project.system.domain.SysUser; +import com.ruoyi.project.system.domain.SysUserRole; +import com.ruoyi.project.system.service.ISysDeptService; +import com.ruoyi.project.system.service.ISysRoleService; +import com.ruoyi.project.system.service.ISysUserService; + +/** + * 角色信息 + * + * @author ruoyi + */ +@RestController +@RequestMapping("/system/role") +public class SysRoleController extends BaseController +{ + @Autowired + private ISysRoleService roleService; + + @Autowired + private TokenService tokenService; + + @Autowired + private SysPermissionService permissionService; + + @Autowired + private ISysUserService userService; + + @Autowired + private ISysDeptService deptService; + + @PreAuthorize("@ss.hasPermi('system:role:list')") + @GetMapping("/list") + public TableDataInfo list(SysRole role) + { + startPage(); + List list = roleService.selectRoleList(role); + return getDataTable(list); + } + + @Log(title = "角色管理", businessType = BusinessType.EXPORT) + @PreAuthorize("@ss.hasPermi('system:role:export')") + @PostMapping("/export") + public void export(HttpServletResponse response, SysRole role) + { + List list = roleService.selectRoleList(role); + ExcelUtil util = new ExcelUtil(SysRole.class); + util.exportExcel(response, list, "角色数据"); + } + + /** + * 根据角色编号获取详细信息 + */ + @PreAuthorize("@ss.hasPermi('system:role:query')") + @GetMapping(value = "/{roleId}") + public AjaxResult getInfo(@PathVariable Long roleId) + { + roleService.checkRoleDataScope(roleId); + return success(roleService.selectRoleById(roleId)); + } + + /** + * 新增角色 + */ + @PreAuthorize("@ss.hasPermi('system:role:add')") + @Log(title = "角色管理", businessType = BusinessType.INSERT) + @PostMapping + public AjaxResult add(@Validated @RequestBody SysRole role) + { + if (!roleService.checkRoleNameUnique(role)) + { + return error("新增角色'" + role.getRoleName() + "'失败,角色名称已存在"); + } + else if (!roleService.checkRoleKeyUnique(role)) + { + return error("新增角色'" + role.getRoleName() + "'失败,角色权限已存在"); + } + role.setCreateBy(getUsername()); + return toAjax(roleService.insertRole(role)); + + } + + /** + * 修改保存角色 + */ + @PreAuthorize("@ss.hasPermi('system:role:edit')") + @Log(title = "角色管理", businessType = BusinessType.UPDATE) + @PutMapping + public AjaxResult edit(@Validated @RequestBody SysRole role) + { + roleService.checkRoleAllowed(role); + roleService.checkRoleDataScope(role.getRoleId()); + if (!roleService.checkRoleNameUnique(role)) + { + return error("修改角色'" + role.getRoleName() + "'失败,角色名称已存在"); + } + else if (!roleService.checkRoleKeyUnique(role)) + { + return error("修改角色'" + role.getRoleName() + "'失败,角色权限已存在"); + } + role.setUpdateBy(getUsername()); + + if (roleService.updateRole(role) > 0) + { + // 更新缓存用户权限 + LoginUser loginUser = getLoginUser(); + if (StringUtils.isNotNull(loginUser.getUser()) && !loginUser.getUser().isAdmin()) + { + loginUser.setPermissions(permissionService.getMenuPermission(loginUser.getUser())); + loginUser.setUser(userService.selectUserByUserName(loginUser.getUser().getUserName())); + tokenService.setLoginUser(loginUser); + } + return success(); + } + return error("修改角色'" + role.getRoleName() + "'失败,请联系管理员"); + } + + /** + * 修改保存数据权限 + */ + @PreAuthorize("@ss.hasPermi('system:role:edit')") + @Log(title = "角色管理", businessType = BusinessType.UPDATE) + @PutMapping("/dataScope") + public AjaxResult dataScope(@RequestBody SysRole role) + { + roleService.checkRoleAllowed(role); + roleService.checkRoleDataScope(role.getRoleId()); + return toAjax(roleService.authDataScope(role)); + } + + /** + * 状态修改 + */ + @PreAuthorize("@ss.hasPermi('system:role:edit')") + @Log(title = "角色管理", businessType = BusinessType.UPDATE) + @PutMapping("/changeStatus") + public AjaxResult changeStatus(@RequestBody SysRole role) + { + roleService.checkRoleAllowed(role); + roleService.checkRoleDataScope(role.getRoleId()); + role.setUpdateBy(getUsername()); + return toAjax(roleService.updateRoleStatus(role)); + } + + /** + * 删除角色 + */ + @PreAuthorize("@ss.hasPermi('system:role:remove')") + @Log(title = "角色管理", businessType = BusinessType.DELETE) + @DeleteMapping("/{roleIds}") + public AjaxResult remove(@PathVariable Long[] roleIds) + { + return toAjax(roleService.deleteRoleByIds(roleIds)); + } + + /** + * 获取角色选择框列表 + */ + @PreAuthorize("@ss.hasPermi('system:role:query')") + @GetMapping("/optionselect") + public AjaxResult optionselect() + { + return success(roleService.selectRoleAll()); + } + + /** + * 查询已分配用户角色列表 + */ + @PreAuthorize("@ss.hasPermi('system:role:list')") + @GetMapping("/authUser/allocatedList") + public TableDataInfo allocatedList(SysUser user) + { + startPage(); + List list = userService.selectAllocatedList(user); + return getDataTable(list); + } + + /** + * 查询未分配用户角色列表 + */ + @PreAuthorize("@ss.hasPermi('system:role:list')") + @GetMapping("/authUser/unallocatedList") + public TableDataInfo unallocatedList(SysUser user) + { + startPage(); + List list = userService.selectUnallocatedList(user); + return getDataTable(list); + } + + /** + * 取消授权用户 + */ + @PreAuthorize("@ss.hasPermi('system:role:edit')") + @Log(title = "角色管理", businessType = BusinessType.GRANT) + @PutMapping("/authUser/cancel") + public AjaxResult cancelAuthUser(@RequestBody SysUserRole userRole) + { + return toAjax(roleService.deleteAuthUser(userRole)); + } + + /** + * 批量取消授权用户 + */ + @PreAuthorize("@ss.hasPermi('system:role:edit')") + @Log(title = "角色管理", businessType = BusinessType.GRANT) + @PutMapping("/authUser/cancelAll") + public AjaxResult cancelAuthUserAll(Long roleId, Long[] userIds) + { + return toAjax(roleService.deleteAuthUsers(roleId, userIds)); + } + + /** + * 批量选择用户授权 + */ + @PreAuthorize("@ss.hasPermi('system:role:edit')") + @Log(title = "角色管理", businessType = BusinessType.GRANT) + @PutMapping("/authUser/selectAll") + public AjaxResult selectAuthUserAll(Long roleId, Long[] userIds) + { + roleService.checkRoleDataScope(roleId); + return toAjax(roleService.insertAuthUsers(roleId, userIds)); + } + + /** + * 获取对应角色部门树列表 + */ + @PreAuthorize("@ss.hasPermi('system:role:query')") + @GetMapping(value = "/deptTree/{roleId}") + public AjaxResult deptTree(@PathVariable("roleId") Long roleId) + { + AjaxResult ajax = AjaxResult.success(); + ajax.put("checkedKeys", deptService.selectDeptListByRoleId(roleId)); + ajax.put("depts", deptService.selectDeptTreeList(new SysDept())); + return ajax; + } +} diff --git a/src/main/java/com/ruoyi/project/system/controller/SysUserController.java b/src/main/java/com/ruoyi/project/system/controller/SysUserController.java new file mode 100644 index 0000000..88586ce --- /dev/null +++ b/src/main/java/com/ruoyi/project/system/controller/SysUserController.java @@ -0,0 +1,251 @@ +package com.ruoyi.project.system.controller; + +import java.util.List; +import java.util.stream.Collectors; +import javax.servlet.http.HttpServletResponse; +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; +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 org.springframework.web.multipart.MultipartFile; +import com.ruoyi.common.utils.SecurityUtils; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.poi.ExcelUtil; +import com.ruoyi.framework.aspectj.lang.annotation.Log; +import com.ruoyi.framework.aspectj.lang.enums.BusinessType; +import com.ruoyi.framework.web.controller.BaseController; +import com.ruoyi.framework.web.domain.AjaxResult; +import com.ruoyi.framework.web.page.TableDataInfo; +import com.ruoyi.project.system.domain.SysDept; +import com.ruoyi.project.system.domain.SysRole; +import com.ruoyi.project.system.domain.SysUser; +import com.ruoyi.project.system.service.ISysDeptService; +import com.ruoyi.project.system.service.ISysPostService; +import com.ruoyi.project.system.service.ISysRoleService; +import com.ruoyi.project.system.service.ISysUserService; + +/** + * 用户信息 + * + * @author ruoyi + */ +@RestController +@RequestMapping("/system/user") +public class SysUserController extends BaseController +{ + @Autowired + private ISysUserService userService; + + @Autowired + private ISysRoleService roleService; + + @Autowired + private ISysDeptService deptService; + + @Autowired + private ISysPostService postService; + + /** + * 获取用户列表 + */ + @PreAuthorize("@ss.hasPermi('system:user:list')") + @GetMapping("/list") + public TableDataInfo list(SysUser user) + { + startPage(); + List list = userService.selectUserList(user); + return getDataTable(list); + } + + @Log(title = "用户管理", businessType = BusinessType.EXPORT) + @PreAuthorize("@ss.hasPermi('system:user:export')") + @PostMapping("/export") + public void export(HttpServletResponse response, SysUser user) + { + List list = userService.selectUserList(user); + ExcelUtil util = new ExcelUtil(SysUser.class); + util.exportExcel(response, list, "用户数据"); + } + + @Log(title = "用户管理", businessType = BusinessType.IMPORT) + @PreAuthorize("@ss.hasPermi('system:user:import')") + @PostMapping("/importData") + public AjaxResult importData(MultipartFile file, boolean updateSupport) throws Exception + { + ExcelUtil util = new ExcelUtil(SysUser.class); + List userList = util.importExcel(file.getInputStream()); + String operName = getUsername(); + String message = userService.importUser(userList, updateSupport, operName); + return success(message); + } + + @PostMapping("/importTemplate") + public void importTemplate(HttpServletResponse response) + { + ExcelUtil util = new ExcelUtil(SysUser.class); + util.importTemplateExcel(response, "用户数据"); + } + + /** + * 根据用户编号获取详细信息 + */ + @PreAuthorize("@ss.hasPermi('system:user:query')") + @GetMapping(value = { "/", "/{userId}" }) + public AjaxResult getInfo(@PathVariable(value = "userId", required = false) Long userId) + { + userService.checkUserDataScope(userId); + AjaxResult ajax = AjaxResult.success(); + List 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 (StringUtils.isNotNull(userId)) + { + SysUser sysUser = userService.selectUserById(userId); + ajax.put(AjaxResult.DATA_TAG, sysUser); + ajax.put("postIds", postService.selectPostListByUserId(userId)); + ajax.put("roleIds", sysUser.getRoles().stream().map(SysRole::getRoleId).collect(Collectors.toList())); + } + return ajax; + } + + /** + * 新增用户 + */ + @PreAuthorize("@ss.hasPermi('system:user:add')") + @Log(title = "用户管理", businessType = BusinessType.INSERT) + @PostMapping + public AjaxResult add(@Validated @RequestBody SysUser user) + { + if (!userService.checkUserNameUnique(user)) + { + return error("新增用户'" + user.getUserName() + "'失败,登录账号已存在"); + } + else if (StringUtils.isNotEmpty(user.getPhonenumber()) && !userService.checkPhoneUnique(user)) + { + return error("新增用户'" + user.getUserName() + "'失败,手机号码已存在"); + } + else if (StringUtils.isNotEmpty(user.getEmail()) && !userService.checkEmailUnique(user)) + { + return error("新增用户'" + user.getUserName() + "'失败,邮箱账号已存在"); + } + user.setCreateBy(getUsername()); + user.setPassword(SecurityUtils.encryptPassword(user.getPassword())); + return toAjax(userService.insertUser(user)); + } + + /** + * 修改用户 + */ + @PreAuthorize("@ss.hasPermi('system:user:edit')") + @Log(title = "用户管理", businessType = BusinessType.UPDATE) + @PutMapping + public AjaxResult edit(@Validated @RequestBody SysUser user) + { + userService.checkUserAllowed(user); + userService.checkUserDataScope(user.getUserId()); + if (!userService.checkUserNameUnique(user)) + { + return error("修改用户'" + user.getUserName() + "'失败,登录账号已存在"); + } + else if (StringUtils.isNotEmpty(user.getPhonenumber()) && !userService.checkPhoneUnique(user)) + { + return error("修改用户'" + user.getUserName() + "'失败,手机号码已存在"); + } + else if (StringUtils.isNotEmpty(user.getEmail()) && !userService.checkEmailUnique(user)) + { + return error("修改用户'" + user.getUserName() + "'失败,邮箱账号已存在"); + } + user.setUpdateBy(getUsername()); + return toAjax(userService.updateUser(user)); + } + + /** + * 删除用户 + */ + @PreAuthorize("@ss.hasPermi('system:user:remove')") + @Log(title = "用户管理", businessType = BusinessType.DELETE) + @DeleteMapping("/{userIds}") + public AjaxResult remove(@PathVariable Long[] userIds) + { + if (ArrayUtils.contains(userIds, getUserId())) + { + return error("当前用户不能删除"); + } + return toAjax(userService.deleteUserByIds(userIds)); + } + + /** + * 重置密码 + */ + @PreAuthorize("@ss.hasPermi('system:user:resetPwd')") + @Log(title = "用户管理", businessType = BusinessType.UPDATE) + @PutMapping("/resetPwd") + public AjaxResult resetPwd(@RequestBody SysUser user) + { + userService.checkUserAllowed(user); + userService.checkUserDataScope(user.getUserId()); + user.setPassword(SecurityUtils.encryptPassword(user.getPassword())); + user.setUpdateBy(getUsername()); + return toAjax(userService.resetPwd(user)); + } + + /** + * 状态修改 + */ + @PreAuthorize("@ss.hasPermi('system:user:edit')") + @Log(title = "用户管理", businessType = BusinessType.UPDATE) + @PutMapping("/changeStatus") + public AjaxResult changeStatus(@RequestBody SysUser user) + { + userService.checkUserAllowed(user); + userService.checkUserDataScope(user.getUserId()); + user.setUpdateBy(getUsername()); + return toAjax(userService.updateUserStatus(user)); + } + + /** + * 根据用户编号获取授权角色 + */ + @PreAuthorize("@ss.hasPermi('system:user:query')") + @GetMapping("/authRole/{userId}") + public AjaxResult authRole(@PathVariable("userId") Long userId) + { + AjaxResult ajax = AjaxResult.success(); + SysUser user = userService.selectUserById(userId); + List roles = roleService.selectRolesByUserId(userId); + ajax.put("user", user); + ajax.put("roles", SysUser.isAdmin(userId) ? roles : roles.stream().filter(r -> !r.isAdmin()).collect(Collectors.toList())); + return ajax; + } + + /** + * 用户授权角色 + */ + @PreAuthorize("@ss.hasPermi('system:user:edit')") + @Log(title = "用户管理", businessType = BusinessType.GRANT) + @PutMapping("/authRole") + public AjaxResult insertAuthRole(Long userId, Long[] roleIds) + { + userService.checkUserDataScope(userId); + userService.insertUserAuth(userId, roleIds); + return success(); + } + + /** + * 获取部门树列表 + */ + @PreAuthorize("@ss.hasPermi('system:user:list')") + @GetMapping("/deptTree") + public AjaxResult deptTree(SysDept dept) + { + return success(deptService.selectDeptTreeList(dept)); + } +} diff --git a/src/main/java/com/ruoyi/project/system/domain/SysConfig.java b/src/main/java/com/ruoyi/project/system/domain/SysConfig.java new file mode 100644 index 0000000..f9b290b --- /dev/null +++ b/src/main/java/com/ruoyi/project/system/domain/SysConfig.java @@ -0,0 +1,111 @@ +package com.ruoyi.project.system.domain; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.Size; +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; +import com.ruoyi.framework.aspectj.lang.annotation.Excel; +import com.ruoyi.framework.aspectj.lang.annotation.Excel.ColumnType; +import com.ruoyi.framework.web.domain.BaseEntity; + +/** + * 参数配置表 sys_config + * + * @author ruoyi + */ +public class SysConfig extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** 参数主键 */ + @Excel(name = "参数主键", cellType = ColumnType.NUMERIC) + private Long configId; + + /** 参数名称 */ + @Excel(name = "参数名称") + private String configName; + + /** 参数键名 */ + @Excel(name = "参数键名") + private String configKey; + + /** 参数键值 */ + @Excel(name = "参数键值") + private String configValue; + + /** 系统内置(Y是 N否) */ + @Excel(name = "系统内置", readConverterExp = "Y=是,N=否") + private String configType; + + public Long getConfigId() + { + return configId; + } + + public void setConfigId(Long configId) + { + this.configId = configId; + } + + @NotBlank(message = "参数名称不能为空") + @Size(min = 0, max = 100, message = "参数名称不能超过100个字符") + public String getConfigName() + { + return configName; + } + + public void setConfigName(String configName) + { + this.configName = configName; + } + + @NotBlank(message = "参数键名长度不能为空") + @Size(min = 0, max = 100, message = "参数键名长度不能超过100个字符") + public String getConfigKey() + { + return configKey; + } + + public void setConfigKey(String configKey) + { + this.configKey = configKey; + } + + @NotBlank(message = "参数键值不能为空") + @Size(min = 0, max = 500, message = "参数键值长度不能超过500个字符") + public String getConfigValue() + { + return configValue; + } + + public void setConfigValue(String configValue) + { + this.configValue = configValue; + } + + public String getConfigType() + { + return configType; + } + + public void setConfigType(String configType) + { + this.configType = configType; + } + + @Override + public String toString() { + return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) + .append("configId", getConfigId()) + .append("configName", getConfigName()) + .append("configKey", getConfigKey()) + .append("configValue", getConfigValue()) + .append("configType", getConfigType()) + .append("createBy", getCreateBy()) + .append("createTime", getCreateTime()) + .append("updateBy", getUpdateBy()) + .append("updateTime", getUpdateTime()) + .append("remark", getRemark()) + .toString(); + } +} diff --git a/src/main/java/com/ruoyi/project/system/domain/SysDept.java b/src/main/java/com/ruoyi/project/system/domain/SysDept.java new file mode 100644 index 0000000..e97e9e8 --- /dev/null +++ b/src/main/java/com/ruoyi/project/system/domain/SysDept.java @@ -0,0 +1,203 @@ +package com.ruoyi.project.system.domain; + +import java.util.ArrayList; +import java.util.List; +import javax.validation.constraints.Email; +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Size; +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; +import com.ruoyi.framework.web.domain.BaseEntity; + +/** + * 部门表 sys_dept + * + * @author ruoyi + */ +public class SysDept extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** 部门ID */ + private Long deptId; + + /** 父部门ID */ + private Long parentId; + + /** 祖级列表 */ + private String ancestors; + + /** 部门名称 */ + private String deptName; + + /** 显示顺序 */ + private Integer orderNum; + + /** 负责人 */ + private String leader; + + /** 联系电话 */ + private String phone; + + /** 邮箱 */ + private String email; + + /** 部门状态:0正常,1停用 */ + private String status; + + /** 删除标志(0代表存在 2代表删除) */ + private String delFlag; + + /** 父部门名称 */ + private String parentName; + + /** 子部门 */ + private List children = new ArrayList(); + + public Long getDeptId() + { + return deptId; + } + + public void setDeptId(Long deptId) + { + this.deptId = deptId; + } + + public Long getParentId() + { + return parentId; + } + + public void setParentId(Long parentId) + { + this.parentId = parentId; + } + + public String getAncestors() + { + return ancestors; + } + + public void setAncestors(String ancestors) + { + this.ancestors = ancestors; + } + + @NotBlank(message = "部门名称不能为空") + @Size(min = 0, max = 30, message = "部门名称长度不能超过30个字符") + public String getDeptName() + { + return deptName; + } + + public void setDeptName(String deptName) + { + this.deptName = deptName; + } + + @NotNull(message = "显示顺序不能为空") + public Integer getOrderNum() + { + return orderNum; + } + + public void setOrderNum(Integer orderNum) + { + this.orderNum = orderNum; + } + + public String getLeader() + { + return leader; + } + + public void setLeader(String leader) + { + this.leader = leader; + } + + @Size(min = 0, max = 11, message = "联系电话长度不能超过11个字符") + public String getPhone() + { + return phone; + } + + public void setPhone(String phone) + { + this.phone = phone; + } + + @Email(message = "邮箱格式不正确") + @Size(min = 0, max = 50, message = "邮箱长度不能超过50个字符") + public String getEmail() + { + return email; + } + + public void setEmail(String email) + { + this.email = email; + } + + public String getStatus() + { + return status; + } + + public void setStatus(String status) + { + this.status = status; + } + + public String getDelFlag() + { + return delFlag; + } + + public void setDelFlag(String delFlag) + { + this.delFlag = delFlag; + } + + public String getParentName() + { + return parentName; + } + + public void setParentName(String parentName) + { + this.parentName = parentName; + } + + public List getChildren() + { + return children; + } + + public void setChildren(List children) + { + this.children = children; + } + + @Override + public String toString() { + return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) + .append("deptId", getDeptId()) + .append("parentId", getParentId()) + .append("ancestors", getAncestors()) + .append("deptName", getDeptName()) + .append("orderNum", getOrderNum()) + .append("leader", getLeader()) + .append("phone", getPhone()) + .append("email", getEmail()) + .append("status", getStatus()) + .append("delFlag", getDelFlag()) + .append("createBy", getCreateBy()) + .append("createTime", getCreateTime()) + .append("updateBy", getUpdateBy()) + .append("updateTime", getUpdateTime()) + .toString(); + } +} diff --git a/src/main/java/com/ruoyi/project/system/domain/SysDictData.java b/src/main/java/com/ruoyi/project/system/domain/SysDictData.java new file mode 100644 index 0000000..b0a9a44 --- /dev/null +++ b/src/main/java/com/ruoyi/project/system/domain/SysDictData.java @@ -0,0 +1,176 @@ +package com.ruoyi.project.system.domain; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.Size; +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; +import com.ruoyi.common.constant.UserConstants; +import com.ruoyi.framework.aspectj.lang.annotation.Excel; +import com.ruoyi.framework.aspectj.lang.annotation.Excel.ColumnType; +import com.ruoyi.framework.web.domain.BaseEntity; + +/** + * 字典数据表 sys_dict_data + * + * @author ruoyi + */ +public class SysDictData extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** 字典编码 */ + @Excel(name = "字典编码", cellType = ColumnType.NUMERIC) + private Long dictCode; + + /** 字典排序 */ + @Excel(name = "字典排序", cellType = ColumnType.NUMERIC) + private Long dictSort; + + /** 字典标签 */ + @Excel(name = "字典标签") + private String dictLabel; + + /** 字典键值 */ + @Excel(name = "字典键值") + private String dictValue; + + /** 字典类型 */ + @Excel(name = "字典类型") + private String dictType; + + /** 样式属性(其他样式扩展) */ + private String cssClass; + + /** 表格字典样式 */ + private String listClass; + + /** 是否默认(Y是 N否) */ + @Excel(name = "是否默认", readConverterExp = "Y=是,N=否") + private String isDefault; + + /** 状态(0正常 1停用) */ + @Excel(name = "状态", readConverterExp = "0=正常,1=停用") + private String status; + + public Long getDictCode() + { + return dictCode; + } + + public void setDictCode(Long dictCode) + { + this.dictCode = dictCode; + } + + public Long getDictSort() + { + return dictSort; + } + + public void setDictSort(Long dictSort) + { + this.dictSort = dictSort; + } + + @NotBlank(message = "字典标签不能为空") + @Size(min = 0, max = 100, message = "字典标签长度不能超过100个字符") + public String getDictLabel() + { + return dictLabel; + } + + public void setDictLabel(String dictLabel) + { + this.dictLabel = dictLabel; + } + + @NotBlank(message = "字典键值不能为空") + @Size(min = 0, max = 100, message = "字典键值长度不能超过100个字符") + public String getDictValue() + { + return dictValue; + } + + public void setDictValue(String dictValue) + { + this.dictValue = dictValue; + } + + @NotBlank(message = "字典类型不能为空") + @Size(min = 0, max = 100, message = "字典类型长度不能超过100个字符") + public String getDictType() + { + return dictType; + } + + public void setDictType(String dictType) + { + this.dictType = dictType; + } + + @Size(min = 0, max = 100, message = "样式属性长度不能超过100个字符") + public String getCssClass() + { + return cssClass; + } + + public void setCssClass(String cssClass) + { + this.cssClass = cssClass; + } + + public String getListClass() + { + return listClass; + } + + public void setListClass(String listClass) + { + this.listClass = listClass; + } + + public boolean getDefault() + { + return UserConstants.YES.equals(this.isDefault); + } + + public String getIsDefault() + { + return isDefault; + } + + public void setIsDefault(String isDefault) + { + this.isDefault = isDefault; + } + + public String getStatus() + { + return status; + } + + public void setStatus(String status) + { + this.status = status; + } + + @Override + public String toString() { + return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) + .append("dictCode", getDictCode()) + .append("dictSort", getDictSort()) + .append("dictLabel", getDictLabel()) + .append("dictValue", getDictValue()) + .append("dictType", getDictType()) + .append("cssClass", getCssClass()) + .append("listClass", getListClass()) + .append("isDefault", getIsDefault()) + .append("status", getStatus()) + .append("createBy", getCreateBy()) + .append("createTime", getCreateTime()) + .append("updateBy", getUpdateBy()) + .append("updateTime", getUpdateTime()) + .append("remark", getRemark()) + .toString(); + } +} diff --git a/src/main/java/com/ruoyi/project/system/domain/SysDictType.java b/src/main/java/com/ruoyi/project/system/domain/SysDictType.java new file mode 100644 index 0000000..745296f --- /dev/null +++ b/src/main/java/com/ruoyi/project/system/domain/SysDictType.java @@ -0,0 +1,96 @@ +package com.ruoyi.project.system.domain; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.Pattern; +import javax.validation.constraints.Size; +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; +import com.ruoyi.framework.aspectj.lang.annotation.Excel; +import com.ruoyi.framework.aspectj.lang.annotation.Excel.ColumnType; +import com.ruoyi.framework.web.domain.BaseEntity; + +/** + * 字典类型表 sys_dict_type + * + * @author ruoyi + */ +public class SysDictType extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** 字典主键 */ + @Excel(name = "字典主键", cellType = ColumnType.NUMERIC) + private Long dictId; + + /** 字典名称 */ + @Excel(name = "字典名称") + private String dictName; + + /** 字典类型 */ + @Excel(name = "字典类型") + private String dictType; + + /** 状态(0正常 1停用) */ + @Excel(name = "状态", readConverterExp = "0=正常,1=停用") + private String status; + + public Long getDictId() + { + return dictId; + } + + public void setDictId(Long dictId) + { + this.dictId = dictId; + } + + @NotBlank(message = "字典名称不能为空") + @Size(min = 0, max = 100, message = "字典类型名称长度不能超过100个字符") + public String getDictName() + { + return dictName; + } + + public void setDictName(String dictName) + { + this.dictName = dictName; + } + + @NotBlank(message = "字典类型不能为空") + @Size(min = 0, max = 100, message = "字典类型类型长度不能超过100个字符") + @Pattern(regexp = "^[a-z][a-z0-9_]*$", message = "字典类型必须以字母开头,且只能为(小写字母,数字,下滑线)") + public String getDictType() + { + return dictType; + } + + public void setDictType(String dictType) + { + this.dictType = dictType; + } + + public String getStatus() + { + return status; + } + + public void setStatus(String status) + { + this.status = status; + } + + @Override + public String toString() { + return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) + .append("dictId", getDictId()) + .append("dictName", getDictName()) + .append("dictType", getDictType()) + .append("status", getStatus()) + .append("createBy", getCreateBy()) + .append("createTime", getCreateTime()) + .append("updateBy", getUpdateBy()) + .append("updateTime", getUpdateTime()) + .append("remark", getRemark()) + .toString(); + } +} diff --git a/src/main/java/com/ruoyi/project/system/domain/SysMenu.java b/src/main/java/com/ruoyi/project/system/domain/SysMenu.java new file mode 100644 index 0000000..7aa7bbc --- /dev/null +++ b/src/main/java/com/ruoyi/project/system/domain/SysMenu.java @@ -0,0 +1,259 @@ +package com.ruoyi.project.system.domain; + +import java.util.ArrayList; +import java.util.List; +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Size; +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; +import com.ruoyi.framework.web.domain.BaseEntity; + +/** + * 菜单权限表 sys_menu + * + * @author ruoyi + */ +public class SysMenu extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** 菜单ID */ + private Long menuId; + + /** 菜单名称 */ + private String menuName; + + /** 父菜单名称 */ + private String parentName; + + /** 父菜单ID */ + private Long parentId; + + /** 显示顺序 */ + private Integer orderNum; + + /** 路由地址 */ + private String path; + + /** 组件路径 */ + private String component; + + /** 路由参数 */ + private String query; + + /** 是否为外链(0是 1否) */ + private String isFrame; + + /** 是否缓存(0缓存 1不缓存) */ + private String isCache; + + /** 类型(M目录 C菜单 F按钮) */ + private String menuType; + + /** 显示状态(0显示 1隐藏) */ + private String visible; + + /** 菜单状态(0正常 1停用) */ + private String status; + + /** 权限字符串 */ + private String perms; + + /** 菜单图标 */ + private String icon; + + /** 子菜单 */ + private List children = new ArrayList(); + + public Long getMenuId() + { + return menuId; + } + + public void setMenuId(Long menuId) + { + this.menuId = menuId; + } + + @NotBlank(message = "菜单名称不能为空") + @Size(min = 0, max = 50, message = "菜单名称长度不能超过50个字符") + public String getMenuName() + { + return menuName; + } + + public void setMenuName(String menuName) + { + this.menuName = menuName; + } + + public String getParentName() + { + return parentName; + } + + public void setParentName(String parentName) + { + this.parentName = parentName; + } + + public Long getParentId() + { + return parentId; + } + + public void setParentId(Long parentId) + { + this.parentId = parentId; + } + + @NotNull(message = "显示顺序不能为空") + public Integer getOrderNum() + { + return orderNum; + } + + public void setOrderNum(Integer orderNum) + { + this.orderNum = orderNum; + } + + @Size(min = 0, max = 200, message = "路由地址不能超过200个字符") + public String getPath() + { + return path; + } + + public void setPath(String path) + { + this.path = path; + } + + @Size(min = 0, max = 200, message = "组件路径不能超过255个字符") + public String getComponent() + { + return component; + } + + public void setComponent(String component) + { + this.component = component; + } + + public String getQuery() + { + return query; + } + + public void setQuery(String query) + { + this.query = query; + } + + public String getIsFrame() + { + return isFrame; + } + + public void setIsFrame(String isFrame) + { + this.isFrame = isFrame; + } + + public String getIsCache() + { + return isCache; + } + + public void setIsCache(String isCache) + { + this.isCache = isCache; + } + + @NotBlank(message = "菜单类型不能为空") + public String getMenuType() + { + return menuType; + } + + public void setMenuType(String menuType) + { + this.menuType = menuType; + } + + public String getVisible() + { + return visible; + } + + public void setVisible(String visible) + { + this.visible = visible; + } + + public String getStatus() + { + return status; + } + + public void setStatus(String status) + { + this.status = status; + } + + @Size(min = 0, max = 100, message = "权限标识长度不能超过100个字符") + public String getPerms() + { + return perms; + } + + public void setPerms(String perms) + { + this.perms = perms; + } + + public String getIcon() + { + return icon; + } + + public void setIcon(String icon) + { + this.icon = icon; + } + + public List getChildren() + { + return children; + } + + public void setChildren(List children) + { + this.children = children; + } + + @Override + public String toString() { + return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) + .append("menuId", getMenuId()) + .append("menuName", getMenuName()) + .append("parentId", getParentId()) + .append("orderNum", getOrderNum()) + .append("path", getPath()) + .append("component", getComponent()) + .append("isFrame", getIsFrame()) + .append("IsCache", getIsCache()) + .append("menuType", getMenuType()) + .append("visible", getVisible()) + .append("status ", getStatus()) + .append("perms", getPerms()) + .append("icon", getIcon()) + .append("createBy", getCreateBy()) + .append("createTime", getCreateTime()) + .append("updateBy", getUpdateBy()) + .append("updateTime", getUpdateTime()) + .append("remark", getRemark()) + .toString(); + } +} diff --git a/src/main/java/com/ruoyi/project/system/domain/SysNotice.java b/src/main/java/com/ruoyi/project/system/domain/SysNotice.java new file mode 100644 index 0000000..8b315a0 --- /dev/null +++ b/src/main/java/com/ruoyi/project/system/domain/SysNotice.java @@ -0,0 +1,102 @@ +package com.ruoyi.project.system.domain; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.Size; +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; +import com.ruoyi.common.xss.Xss; +import com.ruoyi.framework.web.domain.BaseEntity; + +/** + * 通知公告表 sys_notice + * + * @author ruoyi + */ +public class SysNotice extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** 公告ID */ + private Long noticeId; + + /** 公告标题 */ + private String noticeTitle; + + /** 公告类型(1通知 2公告) */ + private String noticeType; + + /** 公告内容 */ + private String noticeContent; + + /** 公告状态(0正常 1关闭) */ + private String status; + + public Long getNoticeId() + { + return noticeId; + } + + public void setNoticeId(Long noticeId) + { + this.noticeId = noticeId; + } + + public void setNoticeTitle(String noticeTitle) + { + this.noticeTitle = noticeTitle; + } + + @Xss(message = "公告标题不能包含脚本字符") + @NotBlank(message = "公告标题不能为空") + @Size(min = 0, max = 50, message = "公告标题不能超过50个字符") + public String getNoticeTitle() + { + return noticeTitle; + } + + public void setNoticeType(String noticeType) + { + this.noticeType = noticeType; + } + + public String getNoticeType() + { + return noticeType; + } + + public void setNoticeContent(String noticeContent) + { + this.noticeContent = noticeContent; + } + + public String getNoticeContent() + { + return noticeContent; + } + + public void setStatus(String status) + { + this.status = status; + } + + public String getStatus() + { + return status; + } + + @Override + public String toString() { + return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) + .append("noticeId", getNoticeId()) + .append("noticeTitle", getNoticeTitle()) + .append("noticeType", getNoticeType()) + .append("noticeContent", getNoticeContent()) + .append("status", getStatus()) + .append("createBy", getCreateBy()) + .append("createTime", getCreateTime()) + .append("updateBy", getUpdateBy()) + .append("updateTime", getUpdateTime()) + .append("remark", getRemark()) + .toString(); + } +} diff --git a/src/main/java/com/ruoyi/project/system/domain/SysPost.java b/src/main/java/com/ruoyi/project/system/domain/SysPost.java new file mode 100644 index 0000000..eb403ff --- /dev/null +++ b/src/main/java/com/ruoyi/project/system/domain/SysPost.java @@ -0,0 +1,124 @@ +package com.ruoyi.project.system.domain; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Size; +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; +import com.ruoyi.framework.aspectj.lang.annotation.Excel; +import com.ruoyi.framework.aspectj.lang.annotation.Excel.ColumnType; +import com.ruoyi.framework.web.domain.BaseEntity; + +/** + * 岗位表 sys_post + * + * @author ruoyi + */ +public class SysPost extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** 岗位序号 */ + @Excel(name = "岗位序号", cellType = ColumnType.NUMERIC) + private Long postId; + + /** 岗位编码 */ + @Excel(name = "岗位编码") + private String postCode; + + /** 岗位名称 */ + @Excel(name = "岗位名称") + private String postName; + + /** 岗位排序 */ + @Excel(name = "岗位排序") + private Integer postSort; + + /** 状态(0正常 1停用) */ + @Excel(name = "状态", readConverterExp = "0=正常,1=停用") + private String status; + + /** 用户是否存在此岗位标识 默认不存在 */ + private boolean flag = false; + + public Long getPostId() + { + return postId; + } + + public void setPostId(Long postId) + { + this.postId = postId; + } + + @NotBlank(message = "岗位编码不能为空") + @Size(min = 0, max = 64, message = "岗位编码长度不能超过64个字符") + public String getPostCode() + { + return postCode; + } + + public void setPostCode(String postCode) + { + this.postCode = postCode; + } + + @NotBlank(message = "岗位名称不能为空") + @Size(min = 0, max = 50, message = "岗位名称长度不能超过50个字符") + public String getPostName() + { + return postName; + } + + public void setPostName(String postName) + { + this.postName = postName; + } + + @NotNull(message = "显示顺序不能为空") + public Integer getPostSort() + { + return postSort; + } + + public void setPostSort(Integer postSort) + { + this.postSort = postSort; + } + + public String getStatus() + { + return status; + } + + public void setStatus(String status) + { + this.status = status; + } + + public boolean isFlag() + { + return flag; + } + + public void setFlag(boolean flag) + { + this.flag = flag; + } + + @Override + public String toString() { + return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) + .append("postId", getPostId()) + .append("postCode", getPostCode()) + .append("postName", getPostName()) + .append("postSort", getPostSort()) + .append("status", getStatus()) + .append("createBy", getCreateBy()) + .append("createTime", getCreateTime()) + .append("updateBy", getUpdateBy()) + .append("updateTime", getUpdateTime()) + .append("remark", getRemark()) + .toString(); + } +} diff --git a/src/main/java/com/ruoyi/project/system/domain/SysRole.java b/src/main/java/com/ruoyi/project/system/domain/SysRole.java new file mode 100644 index 0000000..71e817c --- /dev/null +++ b/src/main/java/com/ruoyi/project/system/domain/SysRole.java @@ -0,0 +1,241 @@ +package com.ruoyi.project.system.domain; + +import java.util.Set; +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Size; +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; +import com.ruoyi.framework.aspectj.lang.annotation.Excel; +import com.ruoyi.framework.aspectj.lang.annotation.Excel.ColumnType; +import com.ruoyi.framework.web.domain.BaseEntity; + +/** + * 角色表 sys_role + * + * @author ruoyi + */ +public class SysRole extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** 角色ID */ + @Excel(name = "角色序号", cellType = ColumnType.NUMERIC) + private Long roleId; + + /** 角色名称 */ + @Excel(name = "角色名称") + private String roleName; + + /** 角色权限 */ + @Excel(name = "角色权限") + private String roleKey; + + /** 角色排序 */ + @Excel(name = "角色排序") + private Integer roleSort; + + /** 数据范围(1:所有数据权限;2:自定义数据权限;3:本部门数据权限;4:本部门及以下数据权限;5:仅本人数据权限) */ + @Excel(name = "数据范围", readConverterExp = "1=所有数据权限,2=自定义数据权限,3=本部门数据权限,4=本部门及以下数据权限,5=仅本人数据权限") + private String dataScope; + + /** 菜单树选择项是否关联显示( 0:父子不互相关联显示 1:父子互相关联显示) */ + private boolean menuCheckStrictly; + + /** 部门树选择项是否关联显示(0:父子不互相关联显示 1:父子互相关联显示 ) */ + private boolean deptCheckStrictly; + + /** 角色状态(0正常 1停用) */ + @Excel(name = "角色状态", readConverterExp = "0=正常,1=停用") + private String status; + + /** 删除标志(0代表存在 2代表删除) */ + private String delFlag; + + /** 用户是否存在此角色标识 默认不存在 */ + private boolean flag = false; + + /** 菜单组 */ + private Long[] menuIds; + + /** 部门组(数据权限) */ + private Long[] deptIds; + + /** 角色菜单权限 */ + private Set permissions; + + public SysRole() + { + + } + + public SysRole(Long roleId) + { + this.roleId = roleId; + } + + public Long getRoleId() + { + return roleId; + } + + public void setRoleId(Long roleId) + { + this.roleId = roleId; + } + + public boolean isAdmin() + { + return isAdmin(this.roleId); + } + + public static boolean isAdmin(Long roleId) + { + return roleId != null && 1L == roleId; + } + + @NotBlank(message = "角色名称不能为空") + @Size(min = 0, max = 30, message = "角色名称长度不能超过30个字符") + public String getRoleName() + { + return roleName; + } + + public void setRoleName(String roleName) + { + this.roleName = roleName; + } + + @NotBlank(message = "权限字符不能为空") + @Size(min = 0, max = 100, message = "权限字符长度不能超过100个字符") + public String getRoleKey() + { + return roleKey; + } + + public void setRoleKey(String roleKey) + { + this.roleKey = roleKey; + } + + @NotNull(message = "显示顺序不能为空") + public Integer getRoleSort() + { + return roleSort; + } + + public void setRoleSort(Integer roleSort) + { + this.roleSort = roleSort; + } + + public String getDataScope() + { + return dataScope; + } + + public void setDataScope(String dataScope) + { + this.dataScope = dataScope; + } + + public boolean isMenuCheckStrictly() + { + return menuCheckStrictly; + } + + public void setMenuCheckStrictly(boolean menuCheckStrictly) + { + this.menuCheckStrictly = menuCheckStrictly; + } + + public boolean isDeptCheckStrictly() + { + return deptCheckStrictly; + } + + public void setDeptCheckStrictly(boolean deptCheckStrictly) + { + this.deptCheckStrictly = deptCheckStrictly; + } + + public String getStatus() + { + return status; + } + + public void setStatus(String status) + { + this.status = status; + } + + public String getDelFlag() + { + return delFlag; + } + + public void setDelFlag(String delFlag) + { + this.delFlag = delFlag; + } + + public boolean isFlag() + { + return flag; + } + + public void setFlag(boolean flag) + { + this.flag = flag; + } + + public Long[] getMenuIds() + { + return menuIds; + } + + public void setMenuIds(Long[] menuIds) + { + this.menuIds = menuIds; + } + + public Long[] getDeptIds() + { + return deptIds; + } + + public void setDeptIds(Long[] deptIds) + { + this.deptIds = deptIds; + } + + public Set getPermissions() + { + return permissions; + } + + public void setPermissions(Set permissions) + { + this.permissions = permissions; + } + + @Override + public String toString() { + return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) + .append("roleId", getRoleId()) + .append("roleName", getRoleName()) + .append("roleKey", getRoleKey()) + .append("roleSort", getRoleSort()) + .append("dataScope", getDataScope()) + .append("menuCheckStrictly", isMenuCheckStrictly()) + .append("deptCheckStrictly", isDeptCheckStrictly()) + .append("status", getStatus()) + .append("delFlag", getDelFlag()) + .append("createBy", getCreateBy()) + .append("createTime", getCreateTime()) + .append("updateBy", getUpdateBy()) + .append("updateTime", getUpdateTime()) + .append("remark", getRemark()) + .toString(); + } +} diff --git a/src/main/java/com/ruoyi/project/system/domain/SysRoleDept.java b/src/main/java/com/ruoyi/project/system/domain/SysRoleDept.java new file mode 100644 index 0000000..5cbeed4 --- /dev/null +++ b/src/main/java/com/ruoyi/project/system/domain/SysRoleDept.java @@ -0,0 +1,46 @@ +package com.ruoyi.project.system.domain; + +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; + +/** + * 角色和部门关联 sys_role_dept + * + * @author ruoyi + */ +public class SysRoleDept +{ + /** 角色ID */ + private Long roleId; + + /** 部门ID */ + private Long deptId; + + public Long getRoleId() + { + return roleId; + } + + public void setRoleId(Long roleId) + { + this.roleId = roleId; + } + + public Long getDeptId() + { + return deptId; + } + + public void setDeptId(Long deptId) + { + this.deptId = deptId; + } + + @Override + public String toString() { + return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) + .append("roleId", getRoleId()) + .append("deptId", getDeptId()) + .toString(); + } +} diff --git a/src/main/java/com/ruoyi/project/system/domain/SysRoleMenu.java b/src/main/java/com/ruoyi/project/system/domain/SysRoleMenu.java new file mode 100644 index 0000000..2772cbb --- /dev/null +++ b/src/main/java/com/ruoyi/project/system/domain/SysRoleMenu.java @@ -0,0 +1,46 @@ +package com.ruoyi.project.system.domain; + +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; + +/** + * 角色和菜单关联 sys_role_menu + * + * @author ruoyi + */ +public class SysRoleMenu +{ + /** 角色ID */ + private Long roleId; + + /** 菜单ID */ + private Long menuId; + + public Long getRoleId() + { + return roleId; + } + + public void setRoleId(Long roleId) + { + this.roleId = roleId; + } + + public Long getMenuId() + { + return menuId; + } + + public void setMenuId(Long menuId) + { + this.menuId = menuId; + } + + @Override + public String toString() { + return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) + .append("roleId", getRoleId()) + .append("menuId", getMenuId()) + .toString(); + } +} diff --git a/src/main/java/com/ruoyi/project/system/domain/SysUser.java b/src/main/java/com/ruoyi/project/system/domain/SysUser.java new file mode 100644 index 0000000..83db00b --- /dev/null +++ b/src/main/java/com/ruoyi/project/system/domain/SysUser.java @@ -0,0 +1,324 @@ +package com.ruoyi.project.system.domain; + +import java.util.Date; +import java.util.List; +import javax.validation.constraints.*; +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; +import com.ruoyi.common.xss.Xss; +import com.ruoyi.framework.aspectj.lang.annotation.Excel; +import com.ruoyi.framework.aspectj.lang.annotation.Excel.ColumnType; +import com.ruoyi.framework.aspectj.lang.annotation.Excel.Type; +import com.ruoyi.framework.aspectj.lang.annotation.Excels; +import com.ruoyi.framework.web.domain.BaseEntity; + +/** + * 用户对象 sys_user + * + * @author ruoyi + */ +public class SysUser extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** 用户ID */ + @Excel(name = "用户序号", cellType = ColumnType.NUMERIC, prompt = "用户编号") + private Long userId; + + /** 部门ID */ + @Excel(name = "部门编号", type = Type.IMPORT) + private Long deptId; + + /** 用户账号 */ + @Excel(name = "登录名称") + private String userName; + + /** 用户昵称 */ + @Excel(name = "用户名称") + private String nickName; + + /** 用户邮箱 */ + @Excel(name = "用户邮箱") + private String email; + + /** 手机号码 */ + @Excel(name = "手机号码", cellType = ColumnType.TEXT) + private String phonenumber; + + /** 用户性别 */ + @Excel(name = "用户性别", readConverterExp = "0=男,1=女,2=未知") + private String sex; + + /** 用户头像 */ + private String avatar; + + /** 密码 */ + private String password; + + /** 帐号状态(0正常 1停用) */ + @Excel(name = "帐号状态", readConverterExp = "0=正常,1=停用") + private String status; + + /** 删除标志(0代表存在 2代表删除) */ + private String delFlag; + + /** 最后登录IP */ + @Excel(name = "最后登录IP", type = Type.EXPORT) + private String loginIp; + + /** 最后登录时间 */ + @Excel(name = "最后登录时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss", type = Type.EXPORT) + private Date loginDate; + + /** 部门对象 */ + @Excels({ + @Excel(name = "部门名称", targetAttr = "deptName", type = Type.EXPORT), + @Excel(name = "部门负责人", targetAttr = "leader", type = Type.EXPORT) + }) + private SysDept dept; + + /** 角色对象 */ + private List roles; + + /** 角色组 */ + private Long[] roleIds; + + /** 岗位组 */ + private Long[] postIds; + + /** 角色ID */ + private Long roleId; + + public SysUser() + { + + } + + public SysUser(Long userId) + { + this.userId = userId; + } + + public Long getUserId() + { + return userId; + } + + public void setUserId(Long userId) + { + this.userId = userId; + } + + public boolean isAdmin() + { + return isAdmin(this.userId); + } + + public static boolean isAdmin(Long userId) + { + return userId != null && 1L == userId; + } + + public Long getDeptId() + { + return deptId; + } + + public void setDeptId(Long deptId) + { + this.deptId = deptId; + } + + @Xss(message = "用户昵称不能包含脚本字符") + @Size(min = 0, max = 30, message = "用户昵称长度不能超过30个字符") + public String getNickName() + { + return nickName; + } + + public void setNickName(String nickName) + { + this.nickName = nickName; + } + + @Xss(message = "用户账号不能包含脚本字符") + @NotBlank(message = "用户账号不能为空") + @Size(min = 0, max = 30, message = "用户账号长度不能超过30个字符") + public String getUserName() + { + return userName; + } + + public void setUserName(String userName) + { + this.userName = userName; + } + + @Email(message = "邮箱格式不正确") + @Size(min = 0, max = 50, message = "邮箱长度不能超过50个字符") + public String getEmail() + { + return email; + } + + public void setEmail(String email) + { + this.email = email; + } + + @Size(min = 0, max = 11, message = "手机号码长度不能超过11个字符") + public String getPhonenumber() + { + return phonenumber; + } + + public void setPhonenumber(String phonenumber) + { + this.phonenumber = phonenumber; + } + + public String getSex() + { + return sex; + } + + public void setSex(String sex) + { + this.sex = sex; + } + + public String getAvatar() + { + return avatar; + } + + public void setAvatar(String avatar) + { + this.avatar = avatar; + } + + public String getPassword() + { + return password; + } + + public void setPassword(String password) + { + this.password = password; + } + + public String getStatus() + { + return status; + } + + public void setStatus(String status) + { + this.status = status; + } + + public String getDelFlag() + { + return delFlag; + } + + public void setDelFlag(String delFlag) + { + this.delFlag = delFlag; + } + + public String getLoginIp() + { + return loginIp; + } + + public void setLoginIp(String loginIp) + { + this.loginIp = loginIp; + } + + public Date getLoginDate() + { + return loginDate; + } + + public void setLoginDate(Date loginDate) + { + this.loginDate = loginDate; + } + + public SysDept getDept() + { + return dept; + } + + public void setDept(SysDept dept) + { + this.dept = dept; + } + + public List getRoles() + { + return roles; + } + + public void setRoles(List roles) + { + this.roles = roles; + } + + public Long[] getRoleIds() + { + return roleIds; + } + + public void setRoleIds(Long[] roleIds) + { + this.roleIds = roleIds; + } + + public Long[] getPostIds() + { + return postIds; + } + + public void setPostIds(Long[] postIds) + { + this.postIds = postIds; + } + + public Long getRoleId() + { + return roleId; + } + + public void setRoleId(Long roleId) + { + this.roleId = roleId; + } + + @Override + public String toString() { + return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) + .append("userId", getUserId()) + .append("deptId", getDeptId()) + .append("userName", getUserName()) + .append("nickName", getNickName()) + .append("email", getEmail()) + .append("phonenumber", getPhonenumber()) + .append("sex", getSex()) + .append("avatar", getAvatar()) + .append("password", getPassword()) + .append("status", getStatus()) + .append("delFlag", getDelFlag()) + .append("loginIp", getLoginIp()) + .append("loginDate", getLoginDate()) + .append("createBy", getCreateBy()) + .append("createTime", getCreateTime()) + .append("updateBy", getUpdateBy()) + .append("updateTime", getUpdateTime()) + .append("remark", getRemark()) + .append("dept", getDept()) + .toString(); + } +} diff --git a/src/main/java/com/ruoyi/project/system/domain/SysUserPost.java b/src/main/java/com/ruoyi/project/system/domain/SysUserPost.java new file mode 100644 index 0000000..2633a2f --- /dev/null +++ b/src/main/java/com/ruoyi/project/system/domain/SysUserPost.java @@ -0,0 +1,46 @@ +package com.ruoyi.project.system.domain; + +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; + +/** + * 用户和岗位关联 sys_user_post + * + * @author ruoyi + */ +public class SysUserPost +{ + /** 用户ID */ + private Long userId; + + /** 岗位ID */ + private Long postId; + + public Long getUserId() + { + return userId; + } + + public void setUserId(Long userId) + { + this.userId = userId; + } + + public Long getPostId() + { + return postId; + } + + public void setPostId(Long postId) + { + this.postId = postId; + } + + @Override + public String toString() { + return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) + .append("userId", getUserId()) + .append("postId", getPostId()) + .toString(); + } +} diff --git a/src/main/java/com/ruoyi/project/system/domain/SysUserRole.java b/src/main/java/com/ruoyi/project/system/domain/SysUserRole.java new file mode 100644 index 0000000..3be4829 --- /dev/null +++ b/src/main/java/com/ruoyi/project/system/domain/SysUserRole.java @@ -0,0 +1,46 @@ +package com.ruoyi.project.system.domain; + +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; + +/** + * 用户和角色关联 sys_user_role + * + * @author ruoyi + */ +public class SysUserRole +{ + /** 用户ID */ + private Long userId; + + /** 角色ID */ + private Long roleId; + + public Long getUserId() + { + return userId; + } + + public void setUserId(Long userId) + { + this.userId = userId; + } + + public Long getRoleId() + { + return roleId; + } + + public void setRoleId(Long roleId) + { + this.roleId = roleId; + } + + @Override + public String toString() { + return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) + .append("userId", getUserId()) + .append("roleId", getRoleId()) + .toString(); + } +} diff --git a/src/main/java/com/ruoyi/project/system/domain/vo/MetaVo.java b/src/main/java/com/ruoyi/project/system/domain/vo/MetaVo.java new file mode 100644 index 0000000..68a51d9 --- /dev/null +++ b/src/main/java/com/ruoyi/project/system/domain/vo/MetaVo.java @@ -0,0 +1,106 @@ +package com.ruoyi.project.system.domain.vo; + +import com.ruoyi.common.utils.StringUtils; + +/** + * 路由显示信息 + * + * @author ruoyi + */ +public class MetaVo +{ + /** + * 设置该路由在侧边栏和面包屑中展示的名字 + */ + private String title; + + /** + * 设置该路由的图标,对应路径src/assets/icons/svg + */ + private String icon; + + /** + * 设置为true,则不会被 缓存 + */ + private boolean noCache; + + /** + * 内链地址(http(s)://开头) + */ + private String link; + + public MetaVo() + { + } + + public MetaVo(String title, String icon) + { + this.title = title; + this.icon = icon; + } + + public MetaVo(String title, String icon, boolean noCache) + { + this.title = title; + this.icon = icon; + this.noCache = noCache; + } + + public MetaVo(String title, String icon, String link) + { + this.title = title; + this.icon = icon; + this.link = link; + } + + public MetaVo(String title, String icon, boolean noCache, String link) + { + this.title = title; + this.icon = icon; + this.noCache = noCache; + if (StringUtils.ishttp(link)) + { + this.link = link; + } + } + + public boolean isNoCache() + { + return noCache; + } + + public void setNoCache(boolean noCache) + { + this.noCache = noCache; + } + + public String getTitle() + { + return title; + } + + public void setTitle(String title) + { + this.title = title; + } + + public String getIcon() + { + return icon; + } + + public void setIcon(String icon) + { + this.icon = icon; + } + + public String getLink() + { + return link; + } + + public void setLink(String link) + { + this.link = link; + } +} diff --git a/src/main/java/com/ruoyi/project/system/domain/vo/RouterVo.java b/src/main/java/com/ruoyi/project/system/domain/vo/RouterVo.java new file mode 100644 index 0000000..fc98c71 --- /dev/null +++ b/src/main/java/com/ruoyi/project/system/domain/vo/RouterVo.java @@ -0,0 +1,148 @@ +package com.ruoyi.project.system.domain.vo; + +import com.fasterxml.jackson.annotation.JsonInclude; +import java.util.List; + +/** + * 路由配置信息 + * + * @author ruoyi + */ +@JsonInclude(JsonInclude.Include.NON_EMPTY) +public class RouterVo +{ + /** + * 路由名字 + */ + private String name; + + /** + * 路由地址 + */ + private String path; + + /** + * 是否隐藏路由,当设置 true 的时候该路由不会再侧边栏出现 + */ + private boolean hidden; + + /** + * 重定向地址,当设置 noRedirect 的时候该路由在面包屑导航中不可被点击 + */ + private String redirect; + + /** + * 组件地址 + */ + private String component; + + /** + * 路由参数:如 {"id": 1, "name": "ry"} + */ + private String query; + + /** + * 当你一个路由下面的 children 声明的路由大于1个时,自动会变成嵌套的模式--如组件页面 + */ + private Boolean alwaysShow; + + /** + * 其他元素 + */ + private MetaVo meta; + + /** + * 子路由 + */ + private List children; + + public String getName() + { + return name; + } + + public void setName(String name) + { + this.name = name; + } + + public String getPath() + { + return path; + } + + public void setPath(String path) + { + this.path = path; + } + + public boolean getHidden() + { + return hidden; + } + + public void setHidden(boolean hidden) + { + this.hidden = hidden; + } + + public String getRedirect() + { + return redirect; + } + + public void setRedirect(String redirect) + { + this.redirect = redirect; + } + + public String getComponent() + { + return component; + } + + public void setComponent(String component) + { + this.component = component; + } + + public String getQuery() + { + return query; + } + + public void setQuery(String query) + { + this.query = query; + } + + public Boolean getAlwaysShow() + { + return alwaysShow; + } + + public void setAlwaysShow(Boolean alwaysShow) + { + this.alwaysShow = alwaysShow; + } + + public MetaVo getMeta() + { + return meta; + } + + public void setMeta(MetaVo meta) + { + this.meta = meta; + } + + public List getChildren() + { + return children; + } + + public void setChildren(List children) + { + this.children = children; + } +} diff --git a/src/main/java/com/ruoyi/project/system/mapper/SysConfigMapper.java b/src/main/java/com/ruoyi/project/system/mapper/SysConfigMapper.java new file mode 100644 index 0000000..1569594 --- /dev/null +++ b/src/main/java/com/ruoyi/project/system/mapper/SysConfigMapper.java @@ -0,0 +1,76 @@ +package com.ruoyi.project.system.mapper; + +import java.util.List; +import com.ruoyi.project.system.domain.SysConfig; + +/** + * 参数配置 数据层 + * + * @author ruoyi + */ +public interface SysConfigMapper +{ + /** + * 查询参数配置信息 + * + * @param config 参数配置信息 + * @return 参数配置信息 + */ + public SysConfig selectConfig(SysConfig config); + + /** + * 通过ID查询配置 + * + * @param configId 参数ID + * @return 参数配置信息 + */ + public SysConfig selectConfigById(Long configId); + + /** + * 查询参数配置列表 + * + * @param config 参数配置信息 + * @return 参数配置集合 + */ + public List selectConfigList(SysConfig config); + + /** + * 根据键名查询参数配置信息 + * + * @param configKey 参数键名 + * @return 参数配置信息 + */ + public SysConfig checkConfigKeyUnique(String configKey); + + /** + * 新增参数配置 + * + * @param config 参数配置信息 + * @return 结果 + */ + public int insertConfig(SysConfig config); + + /** + * 修改参数配置 + * + * @param config 参数配置信息 + * @return 结果 + */ + public int updateConfig(SysConfig config); + + /** + * 删除参数配置 + * + * @param configId 参数ID + * @return 结果 + */ + public int deleteConfigById(Long configId); + + /** + * 批量删除参数信息 + * + * @param configIds 需要删除的参数ID + * @return 结果 + */ + public int deleteConfigByIds(Long[] configIds); +} \ No newline at end of file diff --git a/src/main/java/com/ruoyi/project/system/mapper/SysDeptMapper.java b/src/main/java/com/ruoyi/project/system/mapper/SysDeptMapper.java new file mode 100644 index 0000000..e5a5265 --- /dev/null +++ b/src/main/java/com/ruoyi/project/system/mapper/SysDeptMapper.java @@ -0,0 +1,118 @@ +package com.ruoyi.project.system.mapper; + +import java.util.List; +import org.apache.ibatis.annotations.Param; +import com.ruoyi.project.system.domain.SysDept; + +/** + * 部门管理 数据层 + * + * @author ruoyi + */ +public interface SysDeptMapper +{ + /** + * 查询部门管理数据 + * + * @param dept 部门信息 + * @return 部门信息集合 + */ + public List selectDeptList(SysDept dept); + + /** + * 根据角色ID查询部门树信息 + * + * @param roleId 角色ID + * @param deptCheckStrictly 部门树选择项是否关联显示 + * @return 选中部门列表 + */ + public List selectDeptListByRoleId(@Param("roleId") Long roleId, @Param("deptCheckStrictly") boolean deptCheckStrictly); + + /** + * 根据部门ID查询信息 + * + * @param deptId 部门ID + * @return 部门信息 + */ + public SysDept selectDeptById(Long deptId); + + /** + * 根据ID查询所有子部门 + * + * @param deptId 部门ID + * @return 部门列表 + */ + public List selectChildrenDeptById(Long deptId); + + /** + * 根据ID查询所有子部门(正常状态) + * + * @param deptId 部门ID + * @return 子部门数 + */ + public int selectNormalChildrenDeptById(Long deptId); + + /** + * 是否存在子节点 + * + * @param deptId 部门ID + * @return 结果 + */ + public int hasChildByDeptId(Long deptId); + + /** + * 查询部门是否存在用户 + * + * @param deptId 部门ID + * @return 结果 + */ + public int checkDeptExistUser(Long deptId); + + /** + * 校验部门名称是否唯一 + * + * @param deptName 部门名称 + * @param parentId 父部门ID + * @return 结果 + */ + public SysDept checkDeptNameUnique(@Param("deptName") String deptName, @Param("parentId") Long parentId); + + /** + * 新增部门信息 + * + * @param dept 部门信息 + * @return 结果 + */ + public int insertDept(SysDept dept); + + /** + * 修改部门信息 + * + * @param dept 部门信息 + * @return 结果 + */ + public int updateDept(SysDept dept); + + /** + * 修改所在部门正常状态 + * + * @param deptIds 部门ID组 + */ + public void updateDeptStatusNormal(Long[] deptIds); + + /** + * 修改子元素关系 + * + * @param depts 子元素 + * @return 结果 + */ + public int updateDeptChildren(@Param("depts") List depts); + + /** + * 删除部门管理信息 + * + * @param deptId 部门ID + * @return 结果 + */ + public int deleteDeptById(Long deptId); +} diff --git a/src/main/java/com/ruoyi/project/system/mapper/SysDictDataMapper.java b/src/main/java/com/ruoyi/project/system/mapper/SysDictDataMapper.java new file mode 100644 index 0000000..4977bb4 --- /dev/null +++ b/src/main/java/com/ruoyi/project/system/mapper/SysDictDataMapper.java @@ -0,0 +1,95 @@ +package com.ruoyi.project.system.mapper; + +import java.util.List; +import org.apache.ibatis.annotations.Param; +import com.ruoyi.project.system.domain.SysDictData; + +/** + * 字典表 数据层 + * + * @author ruoyi + */ +public interface SysDictDataMapper +{ + /** + * 根据条件分页查询字典数据 + * + * @param dictData 字典数据信息 + * @return 字典数据集合信息 + */ + public List selectDictDataList(SysDictData dictData); + + /** + * 根据字典类型查询字典数据 + * + * @param dictType 字典类型 + * @return 字典数据集合信息 + */ + public List selectDictDataByType(String dictType); + + /** + * 根据字典类型和字典键值查询字典数据信息 + * + * @param dictType 字典类型 + * @param dictValue 字典键值 + * @return 字典标签 + */ + public String selectDictLabel(@Param("dictType") String dictType, @Param("dictValue") String dictValue); + + /** + * 根据字典数据ID查询信息 + * + * @param dictCode 字典数据ID + * @return 字典数据 + */ + public SysDictData selectDictDataById(Long dictCode); + + /** + * 查询字典数据 + * + * @param dictType 字典类型 + * @return 字典数据 + */ + public int countDictDataByType(String dictType); + + /** + * 通过字典ID删除字典数据信息 + * + * @param dictCode 字典数据ID + * @return 结果 + */ + public int deleteDictDataById(Long dictCode); + + /** + * 批量删除字典数据信息 + * + * @param dictCodes 需要删除的字典数据ID + * @return 结果 + */ + public int deleteDictDataByIds(Long[] dictCodes); + + /** + * 新增字典数据信息 + * + * @param dictData 字典数据信息 + * @return 结果 + */ + public int insertDictData(SysDictData dictData); + + /** + * 修改字典数据信息 + * + * @param dictData 字典数据信息 + * @return 结果 + */ + public int updateDictData(SysDictData dictData); + + /** + * 同步修改字典类型 + * + * @param oldDictType 旧字典类型 + * @param newDictType 新旧字典类型 + * @return 结果 + */ + public int updateDictDataType(@Param("oldDictType") String oldDictType, @Param("newDictType") String newDictType); +} diff --git a/src/main/java/com/ruoyi/project/system/mapper/SysDictTypeMapper.java b/src/main/java/com/ruoyi/project/system/mapper/SysDictTypeMapper.java new file mode 100644 index 0000000..9eeb4d3 --- /dev/null +++ b/src/main/java/com/ruoyi/project/system/mapper/SysDictTypeMapper.java @@ -0,0 +1,83 @@ +package com.ruoyi.project.system.mapper; + +import java.util.List; +import com.ruoyi.project.system.domain.SysDictType; + +/** + * 字典表 数据层 + * + * @author ruoyi + */ +public interface SysDictTypeMapper +{ + /** + * 根据条件分页查询字典类型 + * + * @param dictType 字典类型信息 + * @return 字典类型集合信息 + */ + public List selectDictTypeList(SysDictType dictType); + + /** + * 根据所有字典类型 + * + * @return 字典类型集合信息 + */ + public List selectDictTypeAll(); + + /** + * 根据字典类型ID查询信息 + * + * @param dictId 字典类型ID + * @return 字典类型 + */ + public SysDictType selectDictTypeById(Long dictId); + + /** + * 根据字典类型查询信息 + * + * @param dictType 字典类型 + * @return 字典类型 + */ + public SysDictType selectDictTypeByType(String dictType); + + /** + * 通过字典ID删除字典信息 + * + * @param dictId 字典ID + * @return 结果 + */ + public int deleteDictTypeById(Long dictId); + + /** + * 批量删除字典类型信息 + * + * @param dictIds 需要删除的字典ID + * @return 结果 + */ + public int deleteDictTypeByIds(Long[] dictIds); + + /** + * 新增字典类型信息 + * + * @param dictType 字典类型信息 + * @return 结果 + */ + public int insertDictType(SysDictType dictType); + + /** + * 修改字典类型信息 + * + * @param dictType 字典类型信息 + * @return 结果 + */ + public int updateDictType(SysDictType dictType); + + /** + * 校验字典类型称是否唯一 + * + * @param dictType 字典类型 + * @return 结果 + */ + public SysDictType checkDictTypeUnique(String dictType); +} diff --git a/src/main/java/com/ruoyi/project/system/mapper/SysMenuMapper.java b/src/main/java/com/ruoyi/project/system/mapper/SysMenuMapper.java new file mode 100644 index 0000000..8b6dcf5 --- /dev/null +++ b/src/main/java/com/ruoyi/project/system/mapper/SysMenuMapper.java @@ -0,0 +1,125 @@ +package com.ruoyi.project.system.mapper; + +import java.util.List; +import org.apache.ibatis.annotations.Param; +import com.ruoyi.project.system.domain.SysMenu; + +/** + * 菜单表 数据层 + * + * @author ruoyi + */ +public interface SysMenuMapper +{ + /** + * 查询系统菜单列表 + * + * @param menu 菜单信息 + * @return 菜单列表 + */ + public List selectMenuList(SysMenu menu); + + /** + * 根据用户所有权限 + * + * @return 权限列表 + */ + public List selectMenuPerms(); + + /** + * 根据用户查询系统菜单列表 + * + * @param menu 菜单信息 + * @return 菜单列表 + */ + public List selectMenuListByUserId(SysMenu menu); + + /** + * 根据用户ID查询权限 + * + * @param userId 用户ID + * @return 权限列表 + */ + public List selectMenuPermsByUserId(Long userId); + + /** + * 根据角色ID查询权限 + * + * @param roleId 角色ID + * @return 权限列表 + */ + public List selectMenuPermsByRoleId(Long roleId); + + /** + * 根据用户ID查询菜单 + * + * @return 菜单列表 + */ + public List selectMenuTreeAll(); + + /** + * 根据用户ID查询菜单 + * + * @param username 用户ID + * @return 菜单列表 + */ + public List selectMenuTreeByUserId(Long userId); + + /** + * 根据角色ID查询菜单树信息 + * + * @param roleId 角色ID + * @param menuCheckStrictly 菜单树选择项是否关联显示 + * @return 选中菜单列表 + */ + public List selectMenuListByRoleId(@Param("roleId") Long roleId, @Param("menuCheckStrictly") boolean menuCheckStrictly); + + /** + * 根据菜单ID查询信息 + * + * @param menuId 菜单ID + * @return 菜单信息 + */ + public SysMenu selectMenuById(Long menuId); + + /** + * 是否存在菜单子节点 + * + * @param menuId 菜单ID + * @return 结果 + */ + public int hasChildByMenuId(Long menuId); + + /** + * 新增菜单信息 + * + * @param menu 菜单信息 + * @return 结果 + */ + public int insertMenu(SysMenu menu); + + /** + * 修改菜单信息 + * + * @param menu 菜单信息 + * @return 结果 + */ + public int updateMenu(SysMenu menu); + + /** + * 删除菜单管理信息 + * + * @param menuId 菜单ID + * @return 结果 + */ + public int deleteMenuById(Long menuId); + + /** + * 校验菜单名称是否唯一 + * + * @param menuName 菜单名称 + * @param parentId 父菜单ID + * @return 结果 + */ + public SysMenu checkMenuNameUnique(@Param("menuName") String menuName, @Param("parentId") Long parentId); +} diff --git a/src/main/java/com/ruoyi/project/system/mapper/SysNoticeMapper.java b/src/main/java/com/ruoyi/project/system/mapper/SysNoticeMapper.java new file mode 100644 index 0000000..b80c50b --- /dev/null +++ b/src/main/java/com/ruoyi/project/system/mapper/SysNoticeMapper.java @@ -0,0 +1,60 @@ +package com.ruoyi.project.system.mapper; + +import java.util.List; +import com.ruoyi.project.system.domain.SysNotice; + +/** + * 通知公告表 数据层 + * + * @author ruoyi + */ +public interface SysNoticeMapper +{ + /** + * 查询公告信息 + * + * @param noticeId 公告ID + * @return 公告信息 + */ + public SysNotice selectNoticeById(Long noticeId); + + /** + * 查询公告列表 + * + * @param notice 公告信息 + * @return 公告集合 + */ + public List selectNoticeList(SysNotice notice); + + /** + * 新增公告 + * + * @param notice 公告信息 + * @return 结果 + */ + public int insertNotice(SysNotice notice); + + /** + * 修改公告 + * + * @param notice 公告信息 + * @return 结果 + */ + public int updateNotice(SysNotice notice); + + /** + * 批量删除公告 + * + * @param noticeId 公告ID + * @return 结果 + */ + public int deleteNoticeById(Long noticeId); + + /** + * 批量删除公告信息 + * + * @param noticeIds 需要删除的公告ID + * @return 结果 + */ + public int deleteNoticeByIds(Long[] noticeIds); +} \ No newline at end of file diff --git a/src/main/java/com/ruoyi/project/system/mapper/SysPostMapper.java b/src/main/java/com/ruoyi/project/system/mapper/SysPostMapper.java new file mode 100644 index 0000000..ba468bb --- /dev/null +++ b/src/main/java/com/ruoyi/project/system/mapper/SysPostMapper.java @@ -0,0 +1,99 @@ +package com.ruoyi.project.system.mapper; + +import java.util.List; +import com.ruoyi.project.system.domain.SysPost; + +/** + * 岗位信息 数据层 + * + * @author ruoyi + */ +public interface SysPostMapper +{ + /** + * 查询岗位数据集合 + * + * @param post 岗位信息 + * @return 岗位数据集合 + */ + public List selectPostList(SysPost post); + + /** + * 查询所有岗位 + * + * @return 岗位列表 + */ + public List selectPostAll(); + + /** + * 通过岗位ID查询岗位信息 + * + * @param postId 岗位ID + * @return 角色对象信息 + */ + public SysPost selectPostById(Long postId); + + /** + * 根据用户ID获取岗位选择框列表 + * + * @param userId 用户ID + * @return 选中岗位ID列表 + */ + public List selectPostListByUserId(Long userId); + + /** + * 查询用户所属岗位组 + * + * @param userName 用户名 + * @return 结果 + */ + public List selectPostsByUserName(String userName); + + /** + * 删除岗位信息 + * + * @param postId 岗位ID + * @return 结果 + */ + public int deletePostById(Long postId); + + /** + * 批量删除岗位信息 + * + * @param postIds 需要删除的岗位ID + * @return 结果 + */ + public int deletePostByIds(Long[] postIds); + + /** + * 修改岗位信息 + * + * @param post 岗位信息 + * @return 结果 + */ + public int updatePost(SysPost post); + + /** + * 新增岗位信息 + * + * @param post 岗位信息 + * @return 结果 + */ + public int insertPost(SysPost post); + + /** + * 校验岗位名称 + * + * @param postName 岗位名称 + * @return 结果 + */ + public SysPost checkPostNameUnique(String postName); + + /** + * 校验岗位编码 + * + * @param postCode 岗位编码 + * @return 结果 + */ + public SysPost checkPostCodeUnique(String postCode); +} diff --git a/src/main/java/com/ruoyi/project/system/mapper/SysRoleDeptMapper.java b/src/main/java/com/ruoyi/project/system/mapper/SysRoleDeptMapper.java new file mode 100644 index 0000000..77ead08 --- /dev/null +++ b/src/main/java/com/ruoyi/project/system/mapper/SysRoleDeptMapper.java @@ -0,0 +1,44 @@ +package com.ruoyi.project.system.mapper; + +import java.util.List; +import com.ruoyi.project.system.domain.SysRoleDept; + +/** + * 角色与部门关联表 数据层 + * + * @author ruoyi + */ +public interface SysRoleDeptMapper +{ + /** + * 通过角色ID删除角色和部门关联 + * + * @param roleId 角色ID + * @return 结果 + */ + public int deleteRoleDeptByRoleId(Long roleId); + + /** + * 批量删除角色部门关联信息 + * + * @param ids 需要删除的数据ID + * @return 结果 + */ + public int deleteRoleDept(Long[] ids); + + /** + * 查询部门使用数量 + * + * @param deptId 部门ID + * @return 结果 + */ + public int selectCountRoleDeptByDeptId(Long deptId); + + /** + * 批量新增角色部门信息 + * + * @param roleDeptList 角色部门列表 + * @return 结果 + */ + public int batchRoleDept(List roleDeptList); +} diff --git a/src/main/java/com/ruoyi/project/system/mapper/SysRoleMapper.java b/src/main/java/com/ruoyi/project/system/mapper/SysRoleMapper.java new file mode 100644 index 0000000..0f65731 --- /dev/null +++ b/src/main/java/com/ruoyi/project/system/mapper/SysRoleMapper.java @@ -0,0 +1,107 @@ +package com.ruoyi.project.system.mapper; + +import java.util.List; +import com.ruoyi.project.system.domain.SysRole; + +/** + * 角色表 数据层 + * + * @author ruoyi + */ +public interface SysRoleMapper +{ + /** + * 根据条件分页查询角色数据 + * + * @param role 角色信息 + * @return 角色数据集合信息 + */ + public List selectRoleList(SysRole role); + + /** + * 根据用户ID查询角色 + * + * @param userId 用户ID + * @return 角色列表 + */ + public List selectRolePermissionByUserId(Long userId); + + /** + * 查询所有角色 + * + * @return 角色列表 + */ + public List selectRoleAll(); + + /** + * 根据用户ID获取角色选择框列表 + * + * @param userId 用户ID + * @return 选中角色ID列表 + */ + public List selectRoleListByUserId(Long userId); + + /** + * 通过角色ID查询角色 + * + * @param roleId 角色ID + * @return 角色对象信息 + */ + public SysRole selectRoleById(Long roleId); + + /** + * 根据用户ID查询角色 + * + * @param userName 用户名 + * @return 角色列表 + */ + public List selectRolesByUserName(String userName); + + /** + * 校验角色名称是否唯一 + * + * @param roleName 角色名称 + * @return 角色信息 + */ + public SysRole checkRoleNameUnique(String roleName); + + /** + * 校验角色权限是否唯一 + * + * @param roleKey 角色权限 + * @return 角色信息 + */ + public SysRole checkRoleKeyUnique(String roleKey); + + /** + * 修改角色信息 + * + * @param role 角色信息 + * @return 结果 + */ + public int updateRole(SysRole role); + + /** + * 新增角色信息 + * + * @param role 角色信息 + * @return 结果 + */ + public int insertRole(SysRole role); + + /** + * 通过角色ID删除角色 + * + * @param roleId 角色ID + * @return 结果 + */ + public int deleteRoleById(Long roleId); + + /** + * 批量删除角色信息 + * + * @param roleIds 需要删除的角色ID + * @return 结果 + */ + public int deleteRoleByIds(Long[] roleIds); +} diff --git a/src/main/java/com/ruoyi/project/system/mapper/SysRoleMenuMapper.java b/src/main/java/com/ruoyi/project/system/mapper/SysRoleMenuMapper.java new file mode 100644 index 0000000..92247c3 --- /dev/null +++ b/src/main/java/com/ruoyi/project/system/mapper/SysRoleMenuMapper.java @@ -0,0 +1,44 @@ +package com.ruoyi.project.system.mapper; + +import java.util.List; +import com.ruoyi.project.system.domain.SysRoleMenu; + +/** + * 角色与菜单关联表 数据层 + * + * @author ruoyi + */ +public interface SysRoleMenuMapper +{ + /** + * 查询菜单使用数量 + * + * @param menuId 菜单ID + * @return 结果 + */ + public int checkMenuExistRole(Long menuId); + + /** + * 通过角色ID删除角色和菜单关联 + * + * @param roleId 角色ID + * @return 结果 + */ + public int deleteRoleMenuByRoleId(Long roleId); + + /** + * 批量删除角色菜单关联信息 + * + * @param ids 需要删除的数据ID + * @return 结果 + */ + public int deleteRoleMenu(Long[] ids); + + /** + * 批量新增角色菜单信息 + * + * @param roleMenuList 角色菜单列表 + * @return 结果 + */ + public int batchRoleMenu(List roleMenuList); +} diff --git a/src/main/java/com/ruoyi/project/system/mapper/SysUserMapper.java b/src/main/java/com/ruoyi/project/system/mapper/SysUserMapper.java new file mode 100644 index 0000000..f3a4cd2 --- /dev/null +++ b/src/main/java/com/ruoyi/project/system/mapper/SysUserMapper.java @@ -0,0 +1,129 @@ +package com.ruoyi.project.system.mapper; + +import java.util.List; + +import org.apache.ibatis.annotations.Param; + +import com.ruoyi.project.system.domain.SysUser; + +/** + * 用户表 数据层 + * + * @author ruoyi + */ +public interface SysUserMapper +{ + /** + * 根据条件分页查询用户列表 + * + * @param sysUser 用户信息 + * @return 用户信息集合信息 + */ + public List selectUserList(SysUser sysUser); + + /** + * 根据条件分页查询已配用户角色列表 + * + * @param user 用户信息 + * @return 用户信息集合信息 + */ + public List selectAllocatedList(SysUser user); + + /** + * 根据条件分页查询未分配用户角色列表 + * + * @param user 用户信息 + * @return 用户信息集合信息 + */ + public List selectUnallocatedList(SysUser user); + + /** + * 通过用户名查询用户 + * + * @param userName 用户名 + * @return 用户对象信息 + */ + public SysUser selectUserByUserName(String userName); + + /** + * 通过用户ID查询用户 + * + * @param userId 用户ID + * @return 用户对象信息 + */ + public SysUser selectUserById(Long userId); + + /** + * 新增用户信息 + * + * @param user 用户信息 + * @return 结果 + */ + public int insertUser(SysUser user); + + /** + * 修改用户信息 + * + * @param user 用户信息 + * @return 结果 + */ + public int updateUser(SysUser user); + + /** + * 修改用户头像 + * + * @param userName 用户名 + * @param avatar 头像地址 + * @return 结果 + */ + public int updateUserAvatar(@Param("userName") String userName, @Param("avatar") String avatar); + + /** + * 重置用户密码 + * + * @param userName 用户名 + * @param password 密码 + * @return 结果 + */ + public int resetUserPwd(@Param("userName") String userName, @Param("password") String password); + + /** + * 通过用户ID删除用户 + * + * @param userId 用户ID + * @return 结果 + */ + public int deleteUserById(Long userId); + + /** + * 批量删除用户信息 + * + * @param userIds 需要删除的用户ID + * @return 结果 + */ + public int deleteUserByIds(Long[] userIds); + + /** + * 校验用户名称是否唯一 + * + * @param userName 用户名称 + * @return 结果 + */ + public SysUser checkUserNameUnique(String userName); + + /** + * 校验手机号码是否唯一 + * + * @param phonenumber 手机号码 + * @return 结果 + */ + public SysUser checkPhoneUnique(String phonenumber); + + /** + * 校验email是否唯一 + * + * @param email 用户邮箱 + * @return 结果 + */ + public SysUser checkEmailUnique(String email); +} diff --git a/src/main/java/com/ruoyi/project/system/mapper/SysUserPostMapper.java b/src/main/java/com/ruoyi/project/system/mapper/SysUserPostMapper.java new file mode 100644 index 0000000..5653e1d --- /dev/null +++ b/src/main/java/com/ruoyi/project/system/mapper/SysUserPostMapper.java @@ -0,0 +1,44 @@ +package com.ruoyi.project.system.mapper; + +import java.util.List; +import com.ruoyi.project.system.domain.SysUserPost; + +/** + * 用户与岗位关联表 数据层 + * + * @author ruoyi + */ +public interface SysUserPostMapper +{ + /** + * 通过用户ID删除用户和岗位关联 + * + * @param userId 用户ID + * @return 结果 + */ + public int deleteUserPostByUserId(Long userId); + + /** + * 通过岗位ID查询岗位使用数量 + * + * @param postId 岗位ID + * @return 结果 + */ + public int countUserPostById(Long postId); + + /** + * 批量删除用户和岗位关联 + * + * @param ids 需要删除的数据ID + * @return 结果 + */ + public int deleteUserPost(Long[] ids); + + /** + * 批量新增用户岗位信息 + * + * @param userPostList 用户岗位列表 + * @return 结果 + */ + public int batchUserPost(List userPostList); +} diff --git a/src/main/java/com/ruoyi/project/system/mapper/SysUserRoleMapper.java b/src/main/java/com/ruoyi/project/system/mapper/SysUserRoleMapper.java new file mode 100644 index 0000000..e535905 --- /dev/null +++ b/src/main/java/com/ruoyi/project/system/mapper/SysUserRoleMapper.java @@ -0,0 +1,62 @@ +package com.ruoyi.project.system.mapper; + +import java.util.List; +import org.apache.ibatis.annotations.Param; +import com.ruoyi.project.system.domain.SysUserRole; + +/** + * 用户与角色关联表 数据层 + * + * @author ruoyi + */ +public interface SysUserRoleMapper +{ + /** + * 通过用户ID删除用户和角色关联 + * + * @param userId 用户ID + * @return 结果 + */ + public int deleteUserRoleByUserId(Long userId); + + /** + * 批量删除用户和角色关联 + * + * @param ids 需要删除的数据ID + * @return 结果 + */ + public int deleteUserRole(Long[] ids); + + /** + * 通过角色ID查询角色使用数量 + * + * @param roleId 角色ID + * @return 结果 + */ + public int countUserRoleByRoleId(Long roleId); + + /** + * 批量新增用户角色信息 + * + * @param userRoleList 用户角色列表 + * @return 结果 + */ + public int batchUserRole(List userRoleList); + + /** + * 删除用户和角色关联信息 + * + * @param userRole 用户和角色关联信息 + * @return 结果 + */ + public int deleteUserRoleInfo(SysUserRole userRole); + + /** + * 批量取消授权用户角色 + * + * @param roleId 角色ID + * @param userIds 需要删除的用户数据ID + * @return 结果 + */ + public int deleteUserRoleInfos(@Param("roleId") Long roleId, @Param("userIds") Long[] userIds); +} diff --git a/src/main/java/com/ruoyi/project/system/service/ISysConfigService.java b/src/main/java/com/ruoyi/project/system/service/ISysConfigService.java new file mode 100644 index 0000000..70e5fa5 --- /dev/null +++ b/src/main/java/com/ruoyi/project/system/service/ISysConfigService.java @@ -0,0 +1,89 @@ +package com.ruoyi.project.system.service; + +import java.util.List; +import com.ruoyi.project.system.domain.SysConfig; + +/** + * 参数配置 服务层 + * + * @author ruoyi + */ +public interface ISysConfigService +{ + /** + * 查询参数配置信息 + * + * @param configId 参数配置ID + * @return 参数配置信息 + */ + public SysConfig selectConfigById(Long configId); + + /** + * 根据键名查询参数配置信息 + * + * @param configKey 参数键名 + * @return 参数键值 + */ + public String selectConfigByKey(String configKey); + + /** + * 获取验证码开关 + * + * @return true开启,false关闭 + */ + public boolean selectCaptchaEnabled(); + + /** + * 查询参数配置列表 + * + * @param config 参数配置信息 + * @return 参数配置集合 + */ + public List selectConfigList(SysConfig config); + + /** + * 新增参数配置 + * + * @param config 参数配置信息 + * @return 结果 + */ + public int insertConfig(SysConfig config); + + /** + * 修改参数配置 + * + * @param config 参数配置信息 + * @return 结果 + */ + public int updateConfig(SysConfig config); + + /** + * 批量删除参数信息 + * + * @param configIds 需要删除的参数ID + */ + public void deleteConfigByIds(Long[] configIds); + + /** + * 加载参数缓存数据 + */ + public void loadingConfigCache(); + + /** + * 清空参数缓存数据 + */ + public void clearConfigCache(); + + /** + * 重置参数缓存数据 + */ + public void resetConfigCache(); + + /** + * 校验参数键名是否唯一 + * + * @param config 参数信息 + * @return 结果 + */ + public boolean checkConfigKeyUnique(SysConfig config); +} diff --git a/src/main/java/com/ruoyi/project/system/service/ISysDeptService.java b/src/main/java/com/ruoyi/project/system/service/ISysDeptService.java new file mode 100644 index 0000000..ca27753 --- /dev/null +++ b/src/main/java/com/ruoyi/project/system/service/ISysDeptService.java @@ -0,0 +1,124 @@ +package com.ruoyi.project.system.service; + +import java.util.List; +import com.ruoyi.framework.web.domain.TreeSelect; +import com.ruoyi.project.system.domain.SysDept; + +/** + * 部门管理 服务层 + * + * @author ruoyi + */ +public interface ISysDeptService +{ + /** + * 查询部门管理数据 + * + * @param dept 部门信息 + * @return 部门信息集合 + */ + public List selectDeptList(SysDept dept); + + /** + * 查询部门树结构信息 + * + * @param dept 部门信息 + * @return 部门树信息集合 + */ + public List selectDeptTreeList(SysDept dept); + + /** + * 构建前端所需要树结构 + * + * @param depts 部门列表 + * @return 树结构列表 + */ + public List buildDeptTree(List depts); + + /** + * 构建前端所需要下拉树结构 + * + * @param depts 部门列表 + * @return 下拉树结构列表 + */ + public List buildDeptTreeSelect(List depts); + + /** + * 根据角色ID查询部门树信息 + * + * @param roleId 角色ID + * @return 选中部门列表 + */ + public List selectDeptListByRoleId(Long roleId); + + /** + * 根据部门ID查询信息 + * + * @param deptId 部门ID + * @return 部门信息 + */ + public SysDept selectDeptById(Long deptId); + + /** + * 根据ID查询所有子部门(正常状态) + * + * @param deptId 部门ID + * @return 子部门数 + */ + public int selectNormalChildrenDeptById(Long deptId); + + /** + * 是否存在部门子节点 + * + * @param deptId 部门ID + * @return 结果 + */ + public boolean hasChildByDeptId(Long deptId); + + /** + * 查询部门是否存在用户 + * + * @param deptId 部门ID + * @return 结果 true 存在 false 不存在 + */ + public boolean checkDeptExistUser(Long deptId); + + /** + * 校验部门名称是否唯一 + * + * @param dept 部门信息 + * @return 结果 + */ + public boolean checkDeptNameUnique(SysDept dept); + + /** + * 校验部门是否有数据权限 + * + * @param deptId 部门id + */ + public void checkDeptDataScope(Long deptId); + + /** + * 新增保存部门信息 + * + * @param dept 部门信息 + * @return 结果 + */ + public int insertDept(SysDept dept); + + /** + * 修改保存部门信息 + * + * @param dept 部门信息 + * @return 结果 + */ + public int updateDept(SysDept dept); + + /** + * 删除部门管理信息 + * + * @param deptId 部门ID + * @return 结果 + */ + public int deleteDeptById(Long deptId); +} diff --git a/src/main/java/com/ruoyi/project/system/service/ISysDictDataService.java b/src/main/java/com/ruoyi/project/system/service/ISysDictDataService.java new file mode 100644 index 0000000..e3be8bb --- /dev/null +++ b/src/main/java/com/ruoyi/project/system/service/ISysDictDataService.java @@ -0,0 +1,60 @@ +package com.ruoyi.project.system.service; + +import java.util.List; +import com.ruoyi.project.system.domain.SysDictData; + +/** + * 字典 业务层 + * + * @author ruoyi + */ +public interface ISysDictDataService +{ + /** + * 根据条件分页查询字典数据 + * + * @param dictData 字典数据信息 + * @return 字典数据集合信息 + */ + public List selectDictDataList(SysDictData dictData); + + /** + * 根据字典类型和字典键值查询字典数据信息 + * + * @param dictType 字典类型 + * @param dictValue 字典键值 + * @return 字典标签 + */ + public String selectDictLabel(String dictType, String dictValue); + + /** + * 根据字典数据ID查询信息 + * + * @param dictCode 字典数据ID + * @return 字典数据 + */ + public SysDictData selectDictDataById(Long dictCode); + + /** + * 批量删除字典数据信息 + * + * @param dictCodes 需要删除的字典数据ID + */ + public void deleteDictDataByIds(Long[] dictCodes); + + /** + * 新增保存字典数据信息 + * + * @param dictData 字典数据信息 + * @return 结果 + */ + public int insertDictData(SysDictData dictData); + + /** + * 修改保存字典数据信息 + * + * @param dictData 字典数据信息 + * @return 结果 + */ + public int updateDictData(SysDictData dictData); +} diff --git a/src/main/java/com/ruoyi/project/system/service/ISysDictTypeService.java b/src/main/java/com/ruoyi/project/system/service/ISysDictTypeService.java new file mode 100644 index 0000000..0a834d5 --- /dev/null +++ b/src/main/java/com/ruoyi/project/system/service/ISysDictTypeService.java @@ -0,0 +1,98 @@ +package com.ruoyi.project.system.service; + +import java.util.List; +import com.ruoyi.project.system.domain.SysDictData; +import com.ruoyi.project.system.domain.SysDictType; + +/** + * 字典 业务层 + * + * @author ruoyi + */ +public interface ISysDictTypeService +{ + /** + * 根据条件分页查询字典类型 + * + * @param dictType 字典类型信息 + * @return 字典类型集合信息 + */ + public List selectDictTypeList(SysDictType dictType); + + /** + * 根据所有字典类型 + * + * @return 字典类型集合信息 + */ + public List selectDictTypeAll(); + + /** + * 根据字典类型查询字典数据 + * + * @param dictType 字典类型 + * @return 字典数据集合信息 + */ + public List selectDictDataByType(String dictType); + + /** + * 根据字典类型ID查询信息 + * + * @param dictId 字典类型ID + * @return 字典类型 + */ + public SysDictType selectDictTypeById(Long dictId); + + /** + * 根据字典类型查询信息 + * + * @param dictType 字典类型 + * @return 字典类型 + */ + public SysDictType selectDictTypeByType(String dictType); + + /** + * 批量删除字典信息 + * + * @param dictIds 需要删除的字典ID + */ + public void deleteDictTypeByIds(Long[] dictIds); + + /** + * 加载字典缓存数据 + */ + public void loadingDictCache(); + + /** + * 清空字典缓存数据 + */ + public void clearDictCache(); + + /** + * 重置字典缓存数据 + */ + public void resetDictCache(); + + /** + * 新增保存字典类型信息 + * + * @param dictType 字典类型信息 + * @return 结果 + */ + public int insertDictType(SysDictType dictType); + + /** + * 修改保存字典类型信息 + * + * @param dictType 字典类型信息 + * @return 结果 + */ + public int updateDictType(SysDictType dictType); + + /** + * 校验字典类型称是否唯一 + * + * @param dictType 字典类型 + * @return 结果 + */ + public boolean checkDictTypeUnique(SysDictType dictType); +} diff --git a/src/main/java/com/ruoyi/project/system/service/ISysMenuService.java b/src/main/java/com/ruoyi/project/system/service/ISysMenuService.java new file mode 100644 index 0000000..405ef70 --- /dev/null +++ b/src/main/java/com/ruoyi/project/system/service/ISysMenuService.java @@ -0,0 +1,144 @@ +package com.ruoyi.project.system.service; + +import java.util.List; +import java.util.Set; +import com.ruoyi.framework.web.domain.TreeSelect; +import com.ruoyi.project.system.domain.SysMenu; +import com.ruoyi.project.system.domain.vo.RouterVo; + +/** + * 菜单 业务层 + * + * @author ruoyi + */ +public interface ISysMenuService +{ + /** + * 根据用户查询系统菜单列表 + * + * @param userId 用户ID + * @return 菜单列表 + */ + public List selectMenuList(Long userId); + + /** + * 根据用户查询系统菜单列表 + * + * @param menu 菜单信息 + * @param userId 用户ID + * @return 菜单列表 + */ + public List selectMenuList(SysMenu menu, Long userId); + + /** + * 根据用户ID查询权限 + * + * @param userId 用户ID + * @return 权限列表 + */ + public Set selectMenuPermsByUserId(Long userId); + + /** + * 根据角色ID查询权限 + * + * @param roleId 角色ID + * @return 权限列表 + */ + public Set selectMenuPermsByRoleId(Long roleId); + + /** + * 根据用户ID查询菜单树信息 + * + * @param userId 用户ID + * @return 菜单列表 + */ + public List selectMenuTreeByUserId(Long userId); + + /** + * 根据角色ID查询菜单树信息 + * + * @param roleId 角色ID + * @return 选中菜单列表 + */ + public List selectMenuListByRoleId(Long roleId); + + /** + * 构建前端路由所需要的菜单 + * + * @param menus 菜单列表 + * @return 路由列表 + */ + public List buildMenus(List menus); + + /** + * 构建前端所需要树结构 + * + * @param menus 菜单列表 + * @return 树结构列表 + */ + public List buildMenuTree(List menus); + + /** + * 构建前端所需要下拉树结构 + * + * @param menus 菜单列表 + * @return 下拉树结构列表 + */ + public List buildMenuTreeSelect(List menus); + + /** + * 根据菜单ID查询信息 + * + * @param menuId 菜单ID + * @return 菜单信息 + */ + public SysMenu selectMenuById(Long menuId); + + /** + * 是否存在菜单子节点 + * + * @param menuId 菜单ID + * @return 结果 true 存在 false 不存在 + */ + public boolean hasChildByMenuId(Long menuId); + + /** + * 查询菜单是否存在角色 + * + * @param menuId 菜单ID + * @return 结果 true 存在 false 不存在 + */ + public boolean checkMenuExistRole(Long menuId); + + /** + * 新增保存菜单信息 + * + * @param menu 菜单信息 + * @return 结果 + */ + public int insertMenu(SysMenu menu); + + /** + * 修改保存菜单信息 + * + * @param menu 菜单信息 + * @return 结果 + */ + public int updateMenu(SysMenu menu); + + /** + * 删除菜单管理信息 + * + * @param menuId 菜单ID + * @return 结果 + */ + public int deleteMenuById(Long menuId); + + /** + * 校验菜单名称是否唯一 + * + * @param menu 菜单信息 + * @return 结果 + */ + public boolean checkMenuNameUnique(SysMenu menu); +} diff --git a/src/main/java/com/ruoyi/project/system/service/ISysNoticeService.java b/src/main/java/com/ruoyi/project/system/service/ISysNoticeService.java new file mode 100644 index 0000000..3f74551 --- /dev/null +++ b/src/main/java/com/ruoyi/project/system/service/ISysNoticeService.java @@ -0,0 +1,60 @@ +package com.ruoyi.project.system.service; + +import java.util.List; +import com.ruoyi.project.system.domain.SysNotice; + +/** + * 公告 服务层 + * + * @author ruoyi + */ +public interface ISysNoticeService +{ + /** + * 查询公告信息 + * + * @param noticeId 公告ID + * @return 公告信息 + */ + public SysNotice selectNoticeById(Long noticeId); + + /** + * 查询公告列表 + * + * @param notice 公告信息 + * @return 公告集合 + */ + public List selectNoticeList(SysNotice notice); + + /** + * 新增公告 + * + * @param notice 公告信息 + * @return 结果 + */ + public int insertNotice(SysNotice notice); + + /** + * 修改公告 + * + * @param notice 公告信息 + * @return 结果 + */ + public int updateNotice(SysNotice notice); + + /** + * 删除公告信息 + * + * @param noticeId 公告ID + * @return 结果 + */ + public int deleteNoticeById(Long noticeId); + + /** + * 批量删除公告信息 + * + * @param noticeIds 需要删除的公告ID + * @return 结果 + */ + public int deleteNoticeByIds(Long[] noticeIds); +} diff --git a/src/main/java/com/ruoyi/project/system/service/ISysPostService.java b/src/main/java/com/ruoyi/project/system/service/ISysPostService.java new file mode 100644 index 0000000..95b9373 --- /dev/null +++ b/src/main/java/com/ruoyi/project/system/service/ISysPostService.java @@ -0,0 +1,99 @@ +package com.ruoyi.project.system.service; + +import java.util.List; +import com.ruoyi.project.system.domain.SysPost; + +/** + * 岗位信息 服务层 + * + * @author ruoyi + */ +public interface ISysPostService +{ + /** + * 查询岗位信息集合 + * + * @param post 岗位信息 + * @return 岗位列表 + */ + public List selectPostList(SysPost post); + + /** + * 查询所有岗位 + * + * @return 岗位列表 + */ + public List selectPostAll(); + + /** + * 通过岗位ID查询岗位信息 + * + * @param postId 岗位ID + * @return 角色对象信息 + */ + public SysPost selectPostById(Long postId); + + /** + * 根据用户ID获取岗位选择框列表 + * + * @param userId 用户ID + * @return 选中岗位ID列表 + */ + public List selectPostListByUserId(Long userId); + + /** + * 校验岗位名称 + * + * @param post 岗位信息 + * @return 结果 + */ + public boolean checkPostNameUnique(SysPost post); + + /** + * 校验岗位编码 + * + * @param post 岗位信息 + * @return 结果 + */ + public boolean checkPostCodeUnique(SysPost post); + + /** + * 通过岗位ID查询岗位使用数量 + * + * @param postId 岗位ID + * @return 结果 + */ + public int countUserPostById(Long postId); + + /** + * 删除岗位信息 + * + * @param postId 岗位ID + * @return 结果 + */ + public int deletePostById(Long postId); + + /** + * 批量删除岗位信息 + * + * @param postIds 需要删除的岗位ID + * @return 结果 + */ + public int deletePostByIds(Long[] postIds); + + /** + * 新增保存岗位信息 + * + * @param post 岗位信息 + * @return 结果 + */ + public int insertPost(SysPost post); + + /** + * 修改保存岗位信息 + * + * @param post 岗位信息 + * @return 结果 + */ + public int updatePost(SysPost post); +} diff --git a/src/main/java/com/ruoyi/project/system/service/ISysRoleService.java b/src/main/java/com/ruoyi/project/system/service/ISysRoleService.java new file mode 100644 index 0000000..7be3094 --- /dev/null +++ b/src/main/java/com/ruoyi/project/system/service/ISysRoleService.java @@ -0,0 +1,173 @@ +package com.ruoyi.project.system.service; + +import java.util.List; +import java.util.Set; +import com.ruoyi.project.system.domain.SysRole; +import com.ruoyi.project.system.domain.SysUserRole; + +/** + * 角色业务层 + * + * @author ruoyi + */ +public interface ISysRoleService +{ + /** + * 根据条件分页查询角色数据 + * + * @param role 角色信息 + * @return 角色数据集合信息 + */ + public List selectRoleList(SysRole role); + + /** + * 根据用户ID查询角色列表 + * + * @param userId 用户ID + * @return 角色列表 + */ + public List selectRolesByUserId(Long userId); + + /** + * 根据用户ID查询角色权限 + * + * @param userId 用户ID + * @return 权限列表 + */ + public Set selectRolePermissionByUserId(Long userId); + + /** + * 查询所有角色 + * + * @return 角色列表 + */ + public List selectRoleAll(); + + /** + * 根据用户ID获取角色选择框列表 + * + * @param userId 用户ID + * @return 选中角色ID列表 + */ + public List selectRoleListByUserId(Long userId); + + /** + * 通过角色ID查询角色 + * + * @param roleId 角色ID + * @return 角色对象信息 + */ + public SysRole selectRoleById(Long roleId); + + /** + * 校验角色名称是否唯一 + * + * @param role 角色信息 + * @return 结果 + */ + public boolean checkRoleNameUnique(SysRole role); + + /** + * 校验角色权限是否唯一 + * + * @param role 角色信息 + * @return 结果 + */ + public boolean checkRoleKeyUnique(SysRole role); + + /** + * 校验角色是否允许操作 + * + * @param role 角色信息 + */ + public void checkRoleAllowed(SysRole role); + + /** + * 校验角色是否有数据权限 + * + * @param roleId 角色id + */ + public void checkRoleDataScope(Long roleId); + + /** + * 通过角色ID查询角色使用数量 + * + * @param roleId 角色ID + * @return 结果 + */ + public int countUserRoleByRoleId(Long roleId); + + /** + * 新增保存角色信息 + * + * @param role 角色信息 + * @return 结果 + */ + public int insertRole(SysRole role); + + /** + * 修改保存角色信息 + * + * @param role 角色信息 + * @return 结果 + */ + public int updateRole(SysRole role); + + /** + * 修改角色状态 + * + * @param role 角色信息 + * @return 结果 + */ + public int updateRoleStatus(SysRole role); + + /** + * 修改数据权限信息 + * + * @param role 角色信息 + * @return 结果 + */ + public int authDataScope(SysRole role); + + /** + * 通过角色ID删除角色 + * + * @param roleId 角色ID + * @return 结果 + */ + public int deleteRoleById(Long roleId); + + /** + * 批量删除角色信息 + * + * @param roleIds 需要删除的角色ID + * @return 结果 + */ + public int deleteRoleByIds(Long[] roleIds); + + /** + * 取消授权用户角色 + * + * @param userRole 用户和角色关联信息 + * @return 结果 + */ + public int deleteAuthUser(SysUserRole userRole); + + /** + * 批量取消授权用户角色 + * + * @param roleId 角色ID + * @param userIds 需要取消授权的用户数据ID + * @return 结果 + */ + public int deleteAuthUsers(Long roleId, Long[] userIds); + + /** + * 批量选择授权用户角色 + * + * @param roleId 角色ID + * @param userIds 需要删除的用户数据ID + * @return 结果 + */ + public int insertAuthUsers(Long roleId, Long[] userIds); +} diff --git a/src/main/java/com/ruoyi/project/system/service/ISysUserOnlineService.java b/src/main/java/com/ruoyi/project/system/service/ISysUserOnlineService.java new file mode 100644 index 0000000..7974ae7 --- /dev/null +++ b/src/main/java/com/ruoyi/project/system/service/ISysUserOnlineService.java @@ -0,0 +1,48 @@ +package com.ruoyi.project.system.service; + +import com.ruoyi.framework.security.LoginUser; +import com.ruoyi.project.monitor.domain.SysUserOnline; + +/** + * 在线用户 服务层 + * + * @author ruoyi + */ +public interface ISysUserOnlineService +{ + /** + * 通过登录地址查询信息 + * + * @param ipaddr 登录地址 + * @param user 用户信息 + * @return 在线用户信息 + */ + public SysUserOnline selectOnlineByIpaddr(String ipaddr, LoginUser user); + + /** + * 通过用户名称查询信息 + * + * @param userName 用户名称 + * @param user 用户信息 + * @return 在线用户信息 + */ + public SysUserOnline selectOnlineByUserName(String userName, LoginUser user); + + /** + * 通过登录地址/用户名称查询信息 + * + * @param ipaddr 登录地址 + * @param userName 用户名称 + * @param user 用户信息 + * @return 在线用户信息 + */ + public SysUserOnline selectOnlineByInfo(String ipaddr, String userName, LoginUser user); + + /** + * 设置在线用户信息 + * + * @param user 用户信息 + * @return 在线用户 + */ + public SysUserOnline loginUserToUserOnline(LoginUser user); +} diff --git a/src/main/java/com/ruoyi/project/system/service/ISysUserService.java b/src/main/java/com/ruoyi/project/system/service/ISysUserService.java new file mode 100644 index 0000000..0369fa8 --- /dev/null +++ b/src/main/java/com/ruoyi/project/system/service/ISysUserService.java @@ -0,0 +1,206 @@ +package com.ruoyi.project.system.service; + +import java.util.List; +import com.ruoyi.project.system.domain.SysUser; + +/** + * 用户 业务层 + * + * @author ruoyi + */ +public interface ISysUserService +{ + /** + * 根据条件分页查询用户列表 + * + * @param user 用户信息 + * @return 用户信息集合信息 + */ + public List selectUserList(SysUser user); + + /** + * 根据条件分页查询已分配用户角色列表 + * + * @param user 用户信息 + * @return 用户信息集合信息 + */ + public List selectAllocatedList(SysUser user); + + /** + * 根据条件分页查询未分配用户角色列表 + * + * @param user 用户信息 + * @return 用户信息集合信息 + */ + public List selectUnallocatedList(SysUser user); + + /** + * 通过用户名查询用户 + * + * @param userName 用户名 + * @return 用户对象信息 + */ + public SysUser selectUserByUserName(String userName); + + /** + * 通过用户ID查询用户 + * + * @param userId 用户ID + * @return 用户对象信息 + */ + public SysUser selectUserById(Long userId); + + /** + * 根据用户ID查询用户所属角色组 + * + * @param userName 用户名 + * @return 结果 + */ + public String selectUserRoleGroup(String userName); + + /** + * 根据用户ID查询用户所属岗位组 + * + * @param userName 用户名 + * @return 结果 + */ + public String selectUserPostGroup(String userName); + + /** + * 校验用户名称是否唯一 + * + * @param user 用户信息 + * @return 结果 + */ + public boolean checkUserNameUnique(SysUser user); + + /** + * 校验手机号码是否唯一 + * + * @param user 用户信息 + * @return 结果 + */ + public boolean checkPhoneUnique(SysUser user); + + /** + * 校验email是否唯一 + * + * @param user 用户信息 + * @return 结果 + */ + public boolean checkEmailUnique(SysUser user); + + /** + * 校验用户是否允许操作 + * + * @param user 用户信息 + */ + public void checkUserAllowed(SysUser user); + + /** + * 校验用户是否有数据权限 + * + * @param userId 用户id + */ + public void checkUserDataScope(Long userId); + + /** + * 新增用户信息 + * + * @param user 用户信息 + * @return 结果 + */ + public int insertUser(SysUser user); + + /** + * 注册用户信息 + * + * @param user 用户信息 + * @return 结果 + */ + public boolean registerUser(SysUser user); + + /** + * 修改用户信息 + * + * @param user 用户信息 + * @return 结果 + */ + public int updateUser(SysUser user); + + /** + * 用户授权角色 + * + * @param userId 用户ID + * @param roleIds 角色组 + */ + public void insertUserAuth(Long userId, Long[] roleIds); + + /** + * 修改用户状态 + * + * @param user 用户信息 + * @return 结果 + */ + public int updateUserStatus(SysUser user); + + /** + * 修改用户基本信息 + * + * @param user 用户信息 + * @return 结果 + */ + public int updateUserProfile(SysUser user); + + /** + * 修改用户头像 + * + * @param userName 用户名 + * @param avatar 头像地址 + * @return 结果 + */ + public boolean updateUserAvatar(String userName, String avatar); + + /** + * 重置用户密码 + * + * @param user 用户信息 + * @return 结果 + */ + public int resetPwd(SysUser user); + + /** + * 重置用户密码 + * + * @param userName 用户名 + * @param password 密码 + * @return 结果 + */ + public int resetUserPwd(String userName, String password); + + /** + * 通过用户ID删除用户 + * + * @param userId 用户ID + * @return 结果 + */ + public int deleteUserById(Long userId); + + /** + * 批量删除用户信息 + * + * @param userIds 需要删除的用户ID + * @return 结果 + */ + public int deleteUserByIds(Long[] userIds); + + /** + * 导入用户数据 + * + * @param userList 用户数据列表 + * @param isUpdateSupport 是否更新支持,如果已存在,则进行更新数据 + * @param operName 操作用户 + * @return 结果 + */ + public String importUser(List userList, Boolean isUpdateSupport, String operName); +} diff --git a/src/main/java/com/ruoyi/project/system/service/impl/SysConfigServiceImpl.java b/src/main/java/com/ruoyi/project/system/service/impl/SysConfigServiceImpl.java new file mode 100644 index 0000000..a8fa2bc --- /dev/null +++ b/src/main/java/com/ruoyi/project/system/service/impl/SysConfigServiceImpl.java @@ -0,0 +1,229 @@ +package com.ruoyi.project.system.service.impl; + +import java.util.Collection; +import java.util.List; +import javax.annotation.PostConstruct; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import com.ruoyi.common.constant.CacheConstants; +import com.ruoyi.common.constant.UserConstants; +import com.ruoyi.common.core.text.Convert; +import com.ruoyi.common.exception.ServiceException; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.framework.redis.RedisCache; +import com.ruoyi.project.system.domain.SysConfig; +import com.ruoyi.project.system.mapper.SysConfigMapper; +import com.ruoyi.project.system.service.ISysConfigService; + +/** + * 参数配置 服务层实现 + * + * @author ruoyi + */ +@Service +public class SysConfigServiceImpl implements ISysConfigService +{ + @Autowired + private SysConfigMapper configMapper; + + @Autowired + private RedisCache redisCache; + + /** + * 项目启动时,初始化参数到缓存 + */ + @PostConstruct + public void init() + { + loadingConfigCache(); + } + + /** + * 查询参数配置信息 + * + * @param configId 参数配置ID + * @return 参数配置信息 + */ + @Override + public SysConfig selectConfigById(Long configId) + { + SysConfig config = new SysConfig(); + config.setConfigId(configId); + return configMapper.selectConfig(config); + } + + /** + * 根据键名查询参数配置信息 + * + * @param configKey 参数key + * @return 参数键值 + */ + @Override + public String selectConfigByKey(String configKey) + { + String configValue = Convert.toStr(redisCache.getCacheObject(getCacheKey(configKey))); + if (StringUtils.isNotEmpty(configValue)) + { + return configValue; + } + SysConfig config = new SysConfig(); + config.setConfigKey(configKey); + SysConfig retConfig = configMapper.selectConfig(config); + if (StringUtils.isNotNull(retConfig)) + { + redisCache.setCacheObject(getCacheKey(configKey), retConfig.getConfigValue()); + return retConfig.getConfigValue(); + } + return StringUtils.EMPTY; + } + + /** + * 获取验证码开关 + * + * @return true开启,false关闭 + */ + @Override + public boolean selectCaptchaEnabled() + { + String captchaEnabled = selectConfigByKey("sys.account.captchaEnabled"); + if (StringUtils.isEmpty(captchaEnabled)) + { + return true; + } + return Convert.toBool(captchaEnabled); + } + + /** + * 查询参数配置列表 + * + * @param config 参数配置信息 + * @return 参数配置集合 + */ + @Override + public List selectConfigList(SysConfig config) + { + return configMapper.selectConfigList(config); + } + + /** + * 新增参数配置 + * + * @param config 参数配置信息 + * @return 结果 + */ + @Override + public int insertConfig(SysConfig config) + { + int row = configMapper.insertConfig(config); + if (row > 0) + { + redisCache.setCacheObject(getCacheKey(config.getConfigKey()), config.getConfigValue()); + } + return row; + } + + /** + * 修改参数配置 + * + * @param config 参数配置信息 + * @return 结果 + */ + @Override + public int updateConfig(SysConfig config) + { + SysConfig temp = configMapper.selectConfigById(config.getConfigId()); + if (!StringUtils.equals(temp.getConfigKey(), config.getConfigKey())) + { + redisCache.deleteObject(getCacheKey(temp.getConfigKey())); + } + + int row = configMapper.updateConfig(config); + if (row > 0) + { + redisCache.setCacheObject(getCacheKey(config.getConfigKey()), config.getConfigValue()); + } + return row; + } + + /** + * 批量删除参数信息 + * + * @param configIds 需要删除的参数ID + */ + @Override + public void deleteConfigByIds(Long[] configIds) + { + for (Long configId : configIds) + { + SysConfig config = selectConfigById(configId); + if (StringUtils.equals(UserConstants.YES, config.getConfigType())) + { + throw new ServiceException(String.format("内置参数【%1$s】不能删除 ", config.getConfigKey())); + } + configMapper.deleteConfigById(configId); + redisCache.deleteObject(getCacheKey(config.getConfigKey())); + } + } + + /** + * 加载参数缓存数据 + */ + @Override + public void loadingConfigCache() + { + List configsList = configMapper.selectConfigList(new SysConfig()); + for (SysConfig config : configsList) + { + redisCache.setCacheObject(getCacheKey(config.getConfigKey()), config.getConfigValue()); + } + } + + /** + * 清空参数缓存数据 + */ + @Override + public void clearConfigCache() + { + Collection keys = redisCache.keys(CacheConstants.SYS_CONFIG_KEY + "*"); + redisCache.deleteObject(keys); + } + + /** + * 重置参数缓存数据 + */ + @Override + public void resetConfigCache() + { + clearConfigCache(); + loadingConfigCache(); + } + + /** + * 校验参数键名是否唯一 + * + * @param config 参数配置信息 + * @return 结果 + */ + @Override + public boolean checkConfigKeyUnique(SysConfig config) + { + Long configId = StringUtils.isNull(config.getConfigId()) ? -1L : config.getConfigId(); + SysConfig info = configMapper.checkConfigKeyUnique(config.getConfigKey()); + if (StringUtils.isNotNull(info) && info.getConfigId().longValue() != configId.longValue()) + { + return UserConstants.NOT_UNIQUE; + } + return UserConstants.UNIQUE; + } + + /** + * 设置cache key + * + * @param configKey 参数键 + * @return 缓存键key + */ + private String getCacheKey(String configKey) + { + return CacheConstants.SYS_CONFIG_KEY + configKey; + } +} diff --git a/src/main/java/com/ruoyi/project/system/service/impl/SysDeptServiceImpl.java b/src/main/java/com/ruoyi/project/system/service/impl/SysDeptServiceImpl.java new file mode 100644 index 0000000..147831c --- /dev/null +++ b/src/main/java/com/ruoyi/project/system/service/impl/SysDeptServiceImpl.java @@ -0,0 +1,338 @@ +package com.ruoyi.project.system.service.impl; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.stream.Collectors; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import com.ruoyi.common.constant.UserConstants; +import com.ruoyi.common.core.text.Convert; +import com.ruoyi.common.exception.ServiceException; +import com.ruoyi.common.utils.SecurityUtils; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.spring.SpringUtils; +import com.ruoyi.framework.aspectj.lang.annotation.DataScope; +import com.ruoyi.framework.web.domain.TreeSelect; +import com.ruoyi.project.system.domain.SysDept; +import com.ruoyi.project.system.domain.SysRole; +import com.ruoyi.project.system.domain.SysUser; +import com.ruoyi.project.system.mapper.SysDeptMapper; +import com.ruoyi.project.system.mapper.SysRoleMapper; +import com.ruoyi.project.system.service.ISysDeptService; + +/** + * 部门管理 服务实现 + * + * @author ruoyi + */ +@Service +public class SysDeptServiceImpl implements ISysDeptService +{ + @Autowired + private SysDeptMapper deptMapper; + + @Autowired + private SysRoleMapper roleMapper; + + /** + * 查询部门管理数据 + * + * @param dept 部门信息 + * @return 部门信息集合 + */ + @Override + @DataScope(deptAlias = "d") + public List selectDeptList(SysDept dept) + { + return deptMapper.selectDeptList(dept); + } + + /** + * 查询部门树结构信息 + * + * @param dept 部门信息 + * @return 部门树信息集合 + */ + @Override + public List selectDeptTreeList(SysDept dept) + { + List depts = SpringUtils.getAopProxy(this).selectDeptList(dept); + return buildDeptTreeSelect(depts); + } + + /** + * 构建前端所需要树结构 + * + * @param depts 部门列表 + * @return 树结构列表 + */ + @Override + public List buildDeptTree(List depts) + { + List returnList = new ArrayList(); + List tempList = depts.stream().map(SysDept::getDeptId).collect(Collectors.toList()); + for (SysDept dept : depts) + { + // 如果是顶级节点, 遍历该父节点的所有子节点 + if (!tempList.contains(dept.getParentId())) + { + recursionFn(depts, dept); + returnList.add(dept); + } + } + if (returnList.isEmpty()) + { + returnList = depts; + } + return returnList; + } + + /** + * 构建前端所需要下拉树结构 + * + * @param depts 部门列表 + * @return 下拉树结构列表 + */ + @Override + public List buildDeptTreeSelect(List depts) + { + List deptTrees = buildDeptTree(depts); + return deptTrees.stream().map(TreeSelect::new).collect(Collectors.toList()); + } + + /** + * 根据角色ID查询部门树信息 + * + * @param roleId 角色ID + * @return 选中部门列表 + */ + @Override + public List selectDeptListByRoleId(Long roleId) + { + SysRole role = roleMapper.selectRoleById(roleId); + return deptMapper.selectDeptListByRoleId(roleId, role.isDeptCheckStrictly()); + } + + /** + * 根据部门ID查询信息 + * + * @param deptId 部门ID + * @return 部门信息 + */ + @Override + public SysDept selectDeptById(Long deptId) + { + return deptMapper.selectDeptById(deptId); + } + + /** + * 根据ID查询所有子部门(正常状态) + * + * @param deptId 部门ID + * @return 子部门数 + */ + @Override + public int selectNormalChildrenDeptById(Long deptId) + { + return deptMapper.selectNormalChildrenDeptById(deptId); + } + + /** + * 是否存在子节点 + * + * @param deptId 部门ID + * @return 结果 + */ + @Override + public boolean hasChildByDeptId(Long deptId) + { + int result = deptMapper.hasChildByDeptId(deptId); + return result > 0; + } + + /** + * 查询部门是否存在用户 + * + * @param deptId 部门ID + * @return 结果 true 存在 false 不存在 + */ + @Override + public boolean checkDeptExistUser(Long deptId) + { + int result = deptMapper.checkDeptExistUser(deptId); + return result > 0; + } + + /** + * 校验部门名称是否唯一 + * + * @param dept 部门信息 + * @return 结果 + */ + @Override + public boolean checkDeptNameUnique(SysDept dept) + { + Long deptId = StringUtils.isNull(dept.getDeptId()) ? -1L : dept.getDeptId(); + SysDept info = deptMapper.checkDeptNameUnique(dept.getDeptName(), dept.getParentId()); + if (StringUtils.isNotNull(info) && info.getDeptId().longValue() != deptId.longValue()) + { + return UserConstants.NOT_UNIQUE; + } + return UserConstants.UNIQUE; + } + + /** + * 校验部门是否有数据权限 + * + * @param deptId 部门id + */ + @Override + public void checkDeptDataScope(Long deptId) + { + if (!SysUser.isAdmin(SecurityUtils.getUserId())) + { + SysDept dept = new SysDept(); + dept.setDeptId(deptId); + List depts = SpringUtils.getAopProxy(this).selectDeptList(dept); + if (StringUtils.isEmpty(depts)) + { + throw new ServiceException("没有权限访问部门数据!"); + } + } + } + + /** + * 新增保存部门信息 + * + * @param dept 部门信息 + * @return 结果 + */ + @Override + public int insertDept(SysDept dept) + { + SysDept info = deptMapper.selectDeptById(dept.getParentId()); + // 如果父节点不为正常状态,则不允许新增子节点 + if (!UserConstants.DEPT_NORMAL.equals(info.getStatus())) + { + throw new ServiceException("部门停用,不允许新增"); + } + dept.setAncestors(info.getAncestors() + "," + dept.getParentId()); + return deptMapper.insertDept(dept); + } + + /** + * 修改保存部门信息 + * + * @param dept 部门信息 + * @return 结果 + */ + @Override + public int updateDept(SysDept dept) + { + SysDept newParentDept = deptMapper.selectDeptById(dept.getParentId()); + SysDept oldDept = deptMapper.selectDeptById(dept.getDeptId()); + 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 = deptMapper.updateDept(dept); + if (UserConstants.DEPT_NORMAL.equals(dept.getStatus()) && StringUtils.isNotEmpty(dept.getAncestors()) + && !StringUtils.equals("0", dept.getAncestors())) + { + // 如果该部门是启用状态,则启用该部门的所有上级部门 + updateParentDeptStatusNormal(dept); + } + return result; + } + + /** + * 修改该部门的父级部门状态 + * + * @param dept 当前部门 + */ + private void updateParentDeptStatusNormal(SysDept dept) + { + String ancestors = dept.getAncestors(); + Long[] deptIds = Convert.toLongArray(ancestors); + deptMapper.updateDeptStatusNormal(deptIds); + } + + /** + * 修改子元素关系 + * + * @param deptId 被修改的部门ID + * @param newAncestors 新的父ID集合 + * @param oldAncestors 旧的父ID集合 + */ + public void updateDeptChildren(Long deptId, String newAncestors, String oldAncestors) + { + List children = deptMapper.selectChildrenDeptById(deptId); + for (SysDept child : children) + { + child.setAncestors(child.getAncestors().replaceFirst(oldAncestors, newAncestors)); + } + if (children.size() > 0) + { + deptMapper.updateDeptChildren(children); + } + } + + /** + * 删除部门管理信息 + * + * @param deptId 部门ID + * @return 结果 + */ + @Override + public int deleteDeptById(Long deptId) + { + return deptMapper.deleteDeptById(deptId); + } + + /** + * 递归列表 + */ + private void recursionFn(List list, SysDept t) + { + // 得到子节点列表 + List childList = getChildList(list, t); + t.setChildren(childList); + for (SysDept tChild : childList) + { + if (hasChild(list, tChild)) + { + recursionFn(list, tChild); + } + } + } + + /** + * 得到子节点列表 + */ + private List getChildList(List list, SysDept t) + { + List tlist = new ArrayList(); + Iterator it = list.iterator(); + while (it.hasNext()) + { + SysDept n = (SysDept) it.next(); + if (StringUtils.isNotNull(n.getParentId()) && n.getParentId().longValue() == t.getDeptId().longValue()) + { + tlist.add(n); + } + } + return tlist; + } + + /** + * 判断是否有子节点 + */ + private boolean hasChild(List list, SysDept t) + { + return getChildList(list, t).size() > 0 ? true : false; + } +} diff --git a/src/main/java/com/ruoyi/project/system/service/impl/SysDictDataServiceImpl.java b/src/main/java/com/ruoyi/project/system/service/impl/SysDictDataServiceImpl.java new file mode 100644 index 0000000..111df9e --- /dev/null +++ b/src/main/java/com/ruoyi/project/system/service/impl/SysDictDataServiceImpl.java @@ -0,0 +1,111 @@ +package com.ruoyi.project.system.service.impl; + +import java.util.List; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import com.ruoyi.common.utils.DictUtils; +import com.ruoyi.project.system.domain.SysDictData; +import com.ruoyi.project.system.mapper.SysDictDataMapper; +import com.ruoyi.project.system.service.ISysDictDataService; + +/** + * 字典 业务层处理 + * + * @author ruoyi + */ +@Service +public class SysDictDataServiceImpl implements ISysDictDataService +{ + @Autowired + private SysDictDataMapper dictDataMapper; + + /** + * 根据条件分页查询字典数据 + * + * @param dictData 字典数据信息 + * @return 字典数据集合信息 + */ + @Override + public List selectDictDataList(SysDictData dictData) + { + return dictDataMapper.selectDictDataList(dictData); + } + + /** + * 根据字典类型和字典键值查询字典数据信息 + * + * @param dictType 字典类型 + * @param dictValue 字典键值 + * @return 字典标签 + */ + @Override + public String selectDictLabel(String dictType, String dictValue) + { + return dictDataMapper.selectDictLabel(dictType, dictValue); + } + + /** + * 根据字典数据ID查询信息 + * + * @param dictCode 字典数据ID + * @return 字典数据 + */ + @Override + public SysDictData selectDictDataById(Long dictCode) + { + return dictDataMapper.selectDictDataById(dictCode); + } + + /** + * 批量删除字典数据信息 + * + * @param dictCodes 需要删除的字典数据ID + */ + @Override + public void deleteDictDataByIds(Long[] dictCodes) + { + for (Long dictCode : dictCodes) + { + SysDictData data = selectDictDataById(dictCode); + dictDataMapper.deleteDictDataById(dictCode); + List dictDatas = dictDataMapper.selectDictDataByType(data.getDictType()); + DictUtils.setDictCache(data.getDictType(), dictDatas); + } + } + + /** + * 新增保存字典数据信息 + * + * @param data 字典数据信息 + * @return 结果 + */ + @Override + public int insertDictData(SysDictData data) + { + int row = dictDataMapper.insertDictData(data); + if (row > 0) + { + List dictDatas = dictDataMapper.selectDictDataByType(data.getDictType()); + DictUtils.setDictCache(data.getDictType(), dictDatas); + } + return row; + } + + /** + * 修改保存字典数据信息 + * + * @param data 字典数据信息 + * @return 结果 + */ + @Override + public int updateDictData(SysDictData data) + { + int row = dictDataMapper.updateDictData(data); + if (row > 0) + { + List dictDatas = dictDataMapper.selectDictDataByType(data.getDictType()); + DictUtils.setDictCache(data.getDictType(), dictDatas); + } + return row; + } +} diff --git a/src/main/java/com/ruoyi/project/system/service/impl/SysDictTypeServiceImpl.java b/src/main/java/com/ruoyi/project/system/service/impl/SysDictTypeServiceImpl.java new file mode 100644 index 0000000..fe6380f --- /dev/null +++ b/src/main/java/com/ruoyi/project/system/service/impl/SysDictTypeServiceImpl.java @@ -0,0 +1,223 @@ +package com.ruoyi.project.system.service.impl; + +import java.util.Comparator; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; +import javax.annotation.PostConstruct; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import com.ruoyi.common.constant.UserConstants; +import com.ruoyi.common.exception.ServiceException; +import com.ruoyi.common.utils.DictUtils; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.project.system.domain.SysDictData; +import com.ruoyi.project.system.domain.SysDictType; +import com.ruoyi.project.system.mapper.SysDictDataMapper; +import com.ruoyi.project.system.mapper.SysDictTypeMapper; +import com.ruoyi.project.system.service.ISysDictTypeService; + +/** + * 字典 业务层处理 + * + * @author ruoyi + */ +@Service +public class SysDictTypeServiceImpl implements ISysDictTypeService +{ + @Autowired + private SysDictTypeMapper dictTypeMapper; + + @Autowired + private SysDictDataMapper dictDataMapper; + + /** + * 项目启动时,初始化字典到缓存 + */ + @PostConstruct + public void init() + { + loadingDictCache(); + } + + /** + * 根据条件分页查询字典类型 + * + * @param dictType 字典类型信息 + * @return 字典类型集合信息 + */ + @Override + public List selectDictTypeList(SysDictType dictType) + { + return dictTypeMapper.selectDictTypeList(dictType); + } + + /** + * 根据所有字典类型 + * + * @return 字典类型集合信息 + */ + @Override + public List selectDictTypeAll() + { + return dictTypeMapper.selectDictTypeAll(); + } + + /** + * 根据字典类型查询字典数据 + * + * @param dictType 字典类型 + * @return 字典数据集合信息 + */ + @Override + public List selectDictDataByType(String dictType) + { + List dictDatas = DictUtils.getDictCache(dictType); + if (StringUtils.isNotEmpty(dictDatas)) + { + return dictDatas; + } + dictDatas = dictDataMapper.selectDictDataByType(dictType); + if (StringUtils.isNotEmpty(dictDatas)) + { + DictUtils.setDictCache(dictType, dictDatas); + return dictDatas; + } + return null; + } + + /** + * 根据字典类型ID查询信息 + * + * @param dictId 字典类型ID + * @return 字典类型 + */ + @Override + public SysDictType selectDictTypeById(Long dictId) + { + return dictTypeMapper.selectDictTypeById(dictId); + } + + /** + * 根据字典类型查询信息 + * + * @param dictType 字典类型 + * @return 字典类型 + */ + @Override + public SysDictType selectDictTypeByType(String dictType) + { + return dictTypeMapper.selectDictTypeByType(dictType); + } + + /** + * 批量删除字典类型信息 + * + * @param dictIds 需要删除的字典ID + */ + @Override + public void deleteDictTypeByIds(Long[] dictIds) + { + for (Long dictId : dictIds) + { + SysDictType dictType = selectDictTypeById(dictId); + if (dictDataMapper.countDictDataByType(dictType.getDictType()) > 0) + { + throw new ServiceException(String.format("%1$s已分配,不能删除", dictType.getDictName())); + } + dictTypeMapper.deleteDictTypeById(dictId); + DictUtils.removeDictCache(dictType.getDictType()); + } + } + + /** + * 加载字典缓存数据 + */ + @Override + public void loadingDictCache() + { + SysDictData dictData = new SysDictData(); + dictData.setStatus("0"); + Map> dictDataMap = dictDataMapper.selectDictDataList(dictData).stream().collect(Collectors.groupingBy(SysDictData::getDictType)); + for (Map.Entry> entry : dictDataMap.entrySet()) + { + DictUtils.setDictCache(entry.getKey(), entry.getValue().stream().sorted(Comparator.comparing(SysDictData::getDictSort)).collect(Collectors.toList())); + } + } + + /** + * 清空字典缓存数据 + */ + @Override + public void clearDictCache() + { + DictUtils.clearDictCache(); + } + + /** + * 重置字典缓存数据 + */ + @Override + public void resetDictCache() + { + clearDictCache(); + loadingDictCache(); + } + + /** + * 新增保存字典类型信息 + * + * @param dict 字典类型信息 + * @return 结果 + */ + @Override + public int insertDictType(SysDictType dict) + { + int row = dictTypeMapper.insertDictType(dict); + if (row > 0) + { + DictUtils.setDictCache(dict.getDictType(), null); + } + return row; + } + + /** + * 修改保存字典类型信息 + * + * @param dict 字典类型信息 + * @return 结果 + */ + @Override + @Transactional + public int updateDictType(SysDictType dict) + { + SysDictType oldDict = dictTypeMapper.selectDictTypeById(dict.getDictId()); + dictDataMapper.updateDictDataType(oldDict.getDictType(), dict.getDictType()); + int row = dictTypeMapper.updateDictType(dict); + if (row > 0) + { + List dictDatas = dictDataMapper.selectDictDataByType(dict.getDictType()); + DictUtils.setDictCache(dict.getDictType(), dictDatas); + } + return row; + } + + /** + * 校验字典类型称是否唯一 + * + * @param dict 字典类型 + * @return 结果 + */ + @Override + public boolean checkDictTypeUnique(SysDictType dict) + { + Long dictId = StringUtils.isNull(dict.getDictId()) ? -1L : dict.getDictId(); + SysDictType dictType = dictTypeMapper.checkDictTypeUnique(dict.getDictType()); + if (StringUtils.isNotNull(dictType) && dictType.getDictId().longValue() != dictId.longValue()) + { + return UserConstants.NOT_UNIQUE; + } + return UserConstants.UNIQUE; + } +} diff --git a/src/main/java/com/ruoyi/project/system/service/impl/SysMenuServiceImpl.java b/src/main/java/com/ruoyi/project/system/service/impl/SysMenuServiceImpl.java new file mode 100644 index 0000000..48d4e00 --- /dev/null +++ b/src/main/java/com/ruoyi/project/system/service/impl/SysMenuServiceImpl.java @@ -0,0 +1,531 @@ +package com.ruoyi.project.system.service.impl; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import com.ruoyi.common.constant.Constants; +import com.ruoyi.common.constant.UserConstants; +import com.ruoyi.common.utils.SecurityUtils; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.framework.web.domain.TreeSelect; +import com.ruoyi.project.system.domain.SysMenu; +import com.ruoyi.project.system.domain.SysRole; +import com.ruoyi.project.system.domain.SysUser; +import com.ruoyi.project.system.domain.vo.MetaVo; +import com.ruoyi.project.system.domain.vo.RouterVo; +import com.ruoyi.project.system.mapper.SysMenuMapper; +import com.ruoyi.project.system.mapper.SysRoleMapper; +import com.ruoyi.project.system.mapper.SysRoleMenuMapper; +import com.ruoyi.project.system.service.ISysMenuService; + +/** + * 菜单 业务层处理 + * + * @author ruoyi + */ +@Service +public class SysMenuServiceImpl implements ISysMenuService +{ + public static final String PREMISSION_STRING = "perms[\"{0}\"]"; + + @Autowired + private SysMenuMapper menuMapper; + + @Autowired + private SysRoleMapper roleMapper; + + @Autowired + private SysRoleMenuMapper roleMenuMapper; + + /** + * 根据用户查询系统菜单列表 + * + * @param userId 用户ID + * @return 菜单列表 + */ + @Override + public List selectMenuList(Long userId) + { + return selectMenuList(new SysMenu(), userId); + } + + /** + * 查询系统菜单列表 + * + * @param menu 菜单信息 + * @return 菜单列表 + */ + @Override + public List selectMenuList(SysMenu menu, Long userId) + { + List menuList = null; + // 管理员显示所有菜单信息 + if (SysUser.isAdmin(userId)) + { + menuList = menuMapper.selectMenuList(menu); + } + else + { + menu.getParams().put("userId", userId); + menuList = menuMapper.selectMenuListByUserId(menu); + } + return menuList; + } + + /** + * 根据用户ID查询权限 + * + * @param userId 用户ID + * @return 权限列表 + */ + @Override + public Set selectMenuPermsByUserId(Long userId) + { + List perms = menuMapper.selectMenuPermsByUserId(userId); + Set permsSet = new HashSet<>(); + for (String perm : perms) + { + if (StringUtils.isNotEmpty(perm)) + { + permsSet.addAll(Arrays.asList(perm.trim().split(","))); + } + } + return permsSet; + } + + /** + * 根据角色ID查询权限 + * + * @param roleId 角色ID + * @return 权限列表 + */ + @Override + public Set selectMenuPermsByRoleId(Long roleId) + { + List perms = menuMapper.selectMenuPermsByRoleId(roleId); + Set permsSet = new HashSet<>(); + for (String perm : perms) + { + if (StringUtils.isNotEmpty(perm)) + { + permsSet.addAll(Arrays.asList(perm.trim().split(","))); + } + } + return permsSet; + } + + /** + * 根据用户ID查询菜单 + * + * @param userId 用户名称 + * @return 菜单列表 + */ + @Override + public List selectMenuTreeByUserId(Long userId) + { + List menus = null; + if (SecurityUtils.isAdmin(userId)) + { + menus = menuMapper.selectMenuTreeAll(); + } + else + { + menus = menuMapper.selectMenuTreeByUserId(userId); + } + return getChildPerms(menus, 0); + } + + /** + * 根据角色ID查询菜单树信息 + * + * @param roleId 角色ID + * @return 选中菜单列表 + */ + @Override + public List selectMenuListByRoleId(Long roleId) + { + SysRole role = roleMapper.selectRoleById(roleId); + return menuMapper.selectMenuListByRoleId(roleId, role.isMenuCheckStrictly()); + } + + /** + * 构建前端路由所需要的菜单 + * + * @param menus 菜单列表 + * @return 路由列表 + */ + @Override + public List buildMenus(List menus) + { + List routers = new LinkedList(); + for (SysMenu menu : menus) + { + RouterVo router = new RouterVo(); + router.setHidden("1".equals(menu.getVisible())); + router.setName(getRouteName(menu)); + router.setPath(getRouterPath(menu)); + router.setComponent(getComponent(menu)); + router.setQuery(menu.getQuery()); + router.setMeta(new MetaVo(menu.getMenuName(), menu.getIcon(), StringUtils.equals("1", menu.getIsCache()), menu.getPath())); + List cMenus = menu.getChildren(); + if (StringUtils.isNotEmpty(cMenus) && UserConstants.TYPE_DIR.equals(menu.getMenuType())) + { + router.setAlwaysShow(true); + router.setRedirect("noRedirect"); + router.setChildren(buildMenus(cMenus)); + } + else if (isMenuFrame(menu)) + { + router.setMeta(null); + List childrenList = new ArrayList(); + RouterVo children = new RouterVo(); + children.setPath(menu.getPath()); + children.setComponent(menu.getComponent()); + children.setName(StringUtils.capitalize(menu.getPath())); + children.setMeta(new MetaVo(menu.getMenuName(), menu.getIcon(), StringUtils.equals("1", menu.getIsCache()), menu.getPath())); + children.setQuery(menu.getQuery()); + childrenList.add(children); + router.setChildren(childrenList); + } + else if (menu.getParentId().intValue() == 0 && isInnerLink(menu)) + { + router.setMeta(new MetaVo(menu.getMenuName(), menu.getIcon())); + router.setPath("/"); + List childrenList = new ArrayList(); + RouterVo children = new RouterVo(); + String routerPath = innerLinkReplaceEach(menu.getPath()); + children.setPath(routerPath); + children.setComponent(UserConstants.INNER_LINK); + children.setName(StringUtils.capitalize(routerPath)); + children.setMeta(new MetaVo(menu.getMenuName(), menu.getIcon(), menu.getPath())); + childrenList.add(children); + router.setChildren(childrenList); + } + routers.add(router); + } + return routers; + } + + /** + * 构建前端所需要树结构 + * + * @param menus 菜单列表 + * @return 树结构列表 + */ + @Override + public List buildMenuTree(List menus) + { + List returnList = new ArrayList(); + List tempList = menus.stream().map(SysMenu::getMenuId).collect(Collectors.toList()); + for (Iterator iterator = menus.iterator(); iterator.hasNext();) + { + SysMenu menu = (SysMenu) iterator.next(); + // 如果是顶级节点, 遍历该父节点的所有子节点 + if (!tempList.contains(menu.getParentId())) + { + recursionFn(menus, menu); + returnList.add(menu); + } + } + if (returnList.isEmpty()) + { + returnList = menus; + } + return returnList; + } + + /** + * 构建前端所需要下拉树结构 + * + * @param menus 菜单列表 + * @return 下拉树结构列表 + */ + @Override + public List buildMenuTreeSelect(List menus) + { + List menuTrees = buildMenuTree(menus); + return menuTrees.stream().map(TreeSelect::new).collect(Collectors.toList()); + } + + /** + * 根据菜单ID查询信息 + * + * @param menuId 菜单ID + * @return 菜单信息 + */ + @Override + public SysMenu selectMenuById(Long menuId) + { + return menuMapper.selectMenuById(menuId); + } + + /** + * 是否存在菜单子节点 + * + * @param menuId 菜单ID + * @return 结果 + */ + @Override + public boolean hasChildByMenuId(Long menuId) + { + int result = menuMapper.hasChildByMenuId(menuId); + return result > 0; + } + + /** + * 查询菜单使用数量 + * + * @param menuId 菜单ID + * @return 结果 + */ + @Override + public boolean checkMenuExistRole(Long menuId) + { + int result = roleMenuMapper.checkMenuExistRole(menuId); + return result > 0; + } + + /** + * 新增保存菜单信息 + * + * @param menu 菜单信息 + * @return 结果 + */ + @Override + public int insertMenu(SysMenu menu) + { + return menuMapper.insertMenu(menu); + } + + /** + * 修改保存菜单信息 + * + * @param menu 菜单信息 + * @return 结果 + */ + @Override + public int updateMenu(SysMenu menu) + { + return menuMapper.updateMenu(menu); + } + + /** + * 删除菜单管理信息 + * + * @param menuId 菜单ID + * @return 结果 + */ + @Override + public int deleteMenuById(Long menuId) + { + return menuMapper.deleteMenuById(menuId); + } + + /** + * 校验菜单名称是否唯一 + * + * @param menu 菜单信息 + * @return 结果 + */ + @Override + public boolean checkMenuNameUnique(SysMenu menu) + { + Long menuId = StringUtils.isNull(menu.getMenuId()) ? -1L : menu.getMenuId(); + SysMenu info = menuMapper.checkMenuNameUnique(menu.getMenuName(), menu.getParentId()); + if (StringUtils.isNotNull(info) && info.getMenuId().longValue() != menuId.longValue()) + { + return UserConstants.NOT_UNIQUE; + } + return UserConstants.UNIQUE; + } + + /** + * 获取路由名称 + * + * @param menu 菜单信息 + * @return 路由名称 + */ + public String getRouteName(SysMenu menu) + { + String routerName = StringUtils.capitalize(menu.getPath()); + // 非外链并且是一级目录(类型为目录) + if (isMenuFrame(menu)) + { + routerName = StringUtils.EMPTY; + } + return routerName; + } + + /** + * 获取路由地址 + * + * @param menu 菜单信息 + * @return 路由地址 + */ + public String getRouterPath(SysMenu menu) + { + String routerPath = menu.getPath(); + // 内链打开外网方式 + if (menu.getParentId().intValue() != 0 && isInnerLink(menu)) + { + routerPath = innerLinkReplaceEach(routerPath); + } + // 非外链并且是一级目录(类型为目录) + if (0 == menu.getParentId().intValue() && UserConstants.TYPE_DIR.equals(menu.getMenuType()) + && UserConstants.NO_FRAME.equals(menu.getIsFrame())) + { + routerPath = "/" + menu.getPath(); + } + // 非外链并且是一级目录(类型为菜单) + else if (isMenuFrame(menu)) + { + routerPath = "/"; + } + return routerPath; + } + + /** + * 获取组件信息 + * + * @param menu 菜单信息 + * @return 组件信息 + */ + public String getComponent(SysMenu menu) + { + String component = UserConstants.LAYOUT; + if (StringUtils.isNotEmpty(menu.getComponent()) && !isMenuFrame(menu)) + { + component = menu.getComponent(); + } + else if (StringUtils.isEmpty(menu.getComponent()) && menu.getParentId().intValue() != 0 && isInnerLink(menu)) + { + component = UserConstants.INNER_LINK; + } + else if (StringUtils.isEmpty(menu.getComponent()) && isParentView(menu)) + { + component = UserConstants.PARENT_VIEW; + } + return component; + } + + /** + * 是否为菜单内部跳转 + * + * @param menu 菜单信息 + * @return 结果 + */ + public boolean isMenuFrame(SysMenu menu) + { + return menu.getParentId().intValue() == 0 && UserConstants.TYPE_MENU.equals(menu.getMenuType()) + && menu.getIsFrame().equals(UserConstants.NO_FRAME); + } + + /** + * 是否为parent_view组件 + * + * @param menu 菜单信息 + * @return 结果 + */ + public boolean isParentView(SysMenu menu) + { + return menu.getParentId().intValue() != 0 && UserConstants.TYPE_DIR.equals(menu.getMenuType()); + } + + /** + * 是否为内链组件 + * + * @param menu 菜单信息 + * @return 结果 + */ + public boolean isInnerLink(SysMenu menu) + { + return menu.getIsFrame().equals(UserConstants.NO_FRAME) && StringUtils.ishttp(menu.getPath()); + } + + /** + * 根据父节点的ID获取所有子节点 + * + * @param list 分类表 + * @param parentId 传入的父节点ID + * @return String + */ + public List getChildPerms(List list, int parentId) + { + List returnList = new ArrayList(); + for (Iterator iterator = list.iterator(); iterator.hasNext();) + { + SysMenu t = (SysMenu) iterator.next(); + // 一、根据传入的某个父节点ID,遍历该父节点的所有子节点 + if (t.getParentId() == parentId) + { + recursionFn(list, t); + returnList.add(t); + } + } + return returnList; + } + + /** + * 递归列表 + * + * @param list 分类表 + * @param t 子节点 + */ + private void recursionFn(List list, SysMenu t) + { + // 得到子节点列表 + List childList = getChildList(list, t); + t.setChildren(childList); + for (SysMenu tChild : childList) + { + if (hasChild(list, tChild)) + { + recursionFn(list, tChild); + } + } + } + + /** + * 得到子节点列表 + */ + private List getChildList(List list, SysMenu t) + { + List tlist = new ArrayList(); + Iterator it = list.iterator(); + while (it.hasNext()) + { + SysMenu n = (SysMenu) it.next(); + if (n.getParentId().longValue() == t.getMenuId().longValue()) + { + tlist.add(n); + } + } + return tlist; + } + + /** + * 判断是否有子节点 + */ + private boolean hasChild(List list, SysMenu t) + { + return getChildList(list, t).size() > 0; + } + + /** + * 内链域名特殊字符替换 + * + * @return 替换后的内链域名 + */ + public String innerLinkReplaceEach(String path) + { + return StringUtils.replaceEach(path, new String[] { Constants.HTTP, Constants.HTTPS, Constants.WWW, ".", ":" }, + new String[] { "", "", "", "/", "/" }); + } +} diff --git a/src/main/java/com/ruoyi/project/system/service/impl/SysNoticeServiceImpl.java b/src/main/java/com/ruoyi/project/system/service/impl/SysNoticeServiceImpl.java new file mode 100644 index 0000000..2e9bf00 --- /dev/null +++ b/src/main/java/com/ruoyi/project/system/service/impl/SysNoticeServiceImpl.java @@ -0,0 +1,92 @@ +package com.ruoyi.project.system.service.impl; + +import java.util.List; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import com.ruoyi.project.system.domain.SysNotice; +import com.ruoyi.project.system.mapper.SysNoticeMapper; +import com.ruoyi.project.system.service.ISysNoticeService; + +/** + * 公告 服务层实现 + * + * @author ruoyi + */ +@Service +public class SysNoticeServiceImpl implements ISysNoticeService +{ + @Autowired + private SysNoticeMapper noticeMapper; + + /** + * 查询公告信息 + * + * @param noticeId 公告ID + * @return 公告信息 + */ + @Override + public SysNotice selectNoticeById(Long noticeId) + { + return noticeMapper.selectNoticeById(noticeId); + } + + /** + * 查询公告列表 + * + * @param notice 公告信息 + * @return 公告集合 + */ + @Override + public List selectNoticeList(SysNotice notice) + { + return noticeMapper.selectNoticeList(notice); + } + + /** + * 新增公告 + * + * @param notice 公告信息 + * @return 结果 + */ + @Override + public int insertNotice(SysNotice notice) + { + return noticeMapper.insertNotice(notice); + } + + /** + * 修改公告 + * + * @param notice 公告信息 + * @return 结果 + */ + @Override + public int updateNotice(SysNotice notice) + { + return noticeMapper.updateNotice(notice); + } + + /** + * 删除公告对象 + * + * @param noticeId 公告ID + * @return 结果 + */ + @Override + public int deleteNoticeById(Long noticeId) + { + return noticeMapper.deleteNoticeById(noticeId); + } + + /** + * 批量删除公告信息 + * + * @param noticeIds 需要删除的公告ID + * @return 结果 + */ + @Override + public int deleteNoticeByIds(Long[] noticeIds) + { + return noticeMapper.deleteNoticeByIds(noticeIds); + } +} diff --git a/src/main/java/com/ruoyi/project/system/service/impl/SysPostServiceImpl.java b/src/main/java/com/ruoyi/project/system/service/impl/SysPostServiceImpl.java new file mode 100644 index 0000000..49640c8 --- /dev/null +++ b/src/main/java/com/ruoyi/project/system/service/impl/SysPostServiceImpl.java @@ -0,0 +1,178 @@ +package com.ruoyi.project.system.service.impl; + +import java.util.List; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import com.ruoyi.common.constant.UserConstants; +import com.ruoyi.common.exception.ServiceException; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.project.system.domain.SysPost; +import com.ruoyi.project.system.mapper.SysPostMapper; +import com.ruoyi.project.system.mapper.SysUserPostMapper; +import com.ruoyi.project.system.service.ISysPostService; + +/** + * 岗位信息 服务层处理 + * + * @author ruoyi + */ +@Service +public class SysPostServiceImpl implements ISysPostService +{ + @Autowired + private SysPostMapper postMapper; + + @Autowired + private SysUserPostMapper userPostMapper; + + /** + * 查询岗位信息集合 + * + * @param post 岗位信息 + * @return 岗位信息集合 + */ + @Override + public List selectPostList(SysPost post) + { + return postMapper.selectPostList(post); + } + + /** + * 查询所有岗位 + * + * @return 岗位列表 + */ + @Override + public List selectPostAll() + { + return postMapper.selectPostAll(); + } + + /** + * 通过岗位ID查询岗位信息 + * + * @param postId 岗位ID + * @return 角色对象信息 + */ + @Override + public SysPost selectPostById(Long postId) + { + return postMapper.selectPostById(postId); + } + + /** + * 根据用户ID获取岗位选择框列表 + * + * @param userId 用户ID + * @return 选中岗位ID列表 + */ + @Override + public List selectPostListByUserId(Long userId) + { + return postMapper.selectPostListByUserId(userId); + } + + /** + * 校验岗位名称是否唯一 + * + * @param post 岗位信息 + * @return 结果 + */ + @Override + public boolean checkPostNameUnique(SysPost post) + { + Long postId = StringUtils.isNull(post.getPostId()) ? -1L : post.getPostId(); + SysPost info = postMapper.checkPostNameUnique(post.getPostName()); + if (StringUtils.isNotNull(info) && info.getPostId().longValue() != postId.longValue()) + { + return UserConstants.NOT_UNIQUE; + } + return UserConstants.UNIQUE; + } + + /** + * 校验岗位编码是否唯一 + * + * @param post 岗位信息 + * @return 结果 + */ + @Override + public boolean checkPostCodeUnique(SysPost post) + { + Long postId = StringUtils.isNull(post.getPostId()) ? -1L : post.getPostId(); + SysPost info = postMapper.checkPostCodeUnique(post.getPostCode()); + if (StringUtils.isNotNull(info) && info.getPostId().longValue() != postId.longValue()) + { + return UserConstants.NOT_UNIQUE; + } + return UserConstants.UNIQUE; + } + + /** + * 通过岗位ID查询岗位使用数量 + * + * @param postId 岗位ID + * @return 结果 + */ + @Override + public int countUserPostById(Long postId) + { + return userPostMapper.countUserPostById(postId); + } + + /** + * 删除岗位信息 + * + * @param postId 岗位ID + * @return 结果 + */ + @Override + public int deletePostById(Long postId) + { + return postMapper.deletePostById(postId); + } + + /** + * 批量删除岗位信息 + * + * @param postIds 需要删除的岗位ID + * @return 结果 + */ + @Override + public int deletePostByIds(Long[] postIds) + { + for (Long postId : postIds) + { + SysPost post = selectPostById(postId); + if (countUserPostById(postId) > 0) + { + throw new ServiceException(String.format("%1$s已分配,不能删除", post.getPostName())); + } + } + return postMapper.deletePostByIds(postIds); + } + + /** + * 新增保存岗位信息 + * + * @param post 岗位信息 + * @return 结果 + */ + @Override + public int insertPost(SysPost post) + { + return postMapper.insertPost(post); + } + + /** + * 修改保存岗位信息 + * + * @param post 岗位信息 + * @return 结果 + */ + @Override + public int updatePost(SysPost post) + { + return postMapper.updatePost(post); + } +} diff --git a/src/main/java/com/ruoyi/project/system/service/impl/SysRoleServiceImpl.java b/src/main/java/com/ruoyi/project/system/service/impl/SysRoleServiceImpl.java new file mode 100644 index 0000000..b45eee8 --- /dev/null +++ b/src/main/java/com/ruoyi/project/system/service/impl/SysRoleServiceImpl.java @@ -0,0 +1,424 @@ +package com.ruoyi.project.system.service.impl; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import com.ruoyi.common.constant.UserConstants; +import com.ruoyi.common.exception.ServiceException; +import com.ruoyi.common.utils.SecurityUtils; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.spring.SpringUtils; +import com.ruoyi.framework.aspectj.lang.annotation.DataScope; +import com.ruoyi.project.system.domain.SysRole; +import com.ruoyi.project.system.domain.SysRoleDept; +import com.ruoyi.project.system.domain.SysRoleMenu; +import com.ruoyi.project.system.domain.SysUser; +import com.ruoyi.project.system.domain.SysUserRole; +import com.ruoyi.project.system.mapper.SysRoleDeptMapper; +import com.ruoyi.project.system.mapper.SysRoleMapper; +import com.ruoyi.project.system.mapper.SysRoleMenuMapper; +import com.ruoyi.project.system.mapper.SysUserRoleMapper; +import com.ruoyi.project.system.service.ISysRoleService; + +/** + * 角色 业务层处理 + * + * @author ruoyi + */ +@Service +public class SysRoleServiceImpl implements ISysRoleService +{ + @Autowired + private SysRoleMapper roleMapper; + + @Autowired + private SysRoleMenuMapper roleMenuMapper; + + @Autowired + private SysUserRoleMapper userRoleMapper; + + @Autowired + private SysRoleDeptMapper roleDeptMapper; + + /** + * 根据条件分页查询角色数据 + * + * @param role 角色信息 + * @return 角色数据集合信息 + */ + @Override + @DataScope(deptAlias = "d") + public List selectRoleList(SysRole role) + { + return roleMapper.selectRoleList(role); + } + + /** + * 根据用户ID查询角色 + * + * @param userId 用户ID + * @return 角色列表 + */ + @Override + public List selectRolesByUserId(Long userId) + { + List userRoles = roleMapper.selectRolePermissionByUserId(userId); + List roles = selectRoleAll(); + for (SysRole role : roles) + { + for (SysRole userRole : userRoles) + { + if (role.getRoleId().longValue() == userRole.getRoleId().longValue()) + { + role.setFlag(true); + break; + } + } + } + return roles; + } + + /** + * 根据用户ID查询权限 + * + * @param userId 用户ID + * @return 权限列表 + */ + @Override + public Set selectRolePermissionByUserId(Long userId) + { + List perms = roleMapper.selectRolePermissionByUserId(userId); + Set permsSet = new HashSet<>(); + for (SysRole perm : perms) + { + if (StringUtils.isNotNull(perm)) + { + permsSet.addAll(Arrays.asList(perm.getRoleKey().trim().split(","))); + } + } + return permsSet; + } + + /** + * 查询所有角色 + * + * @return 角色列表 + */ + @Override + public List selectRoleAll() + { + return SpringUtils.getAopProxy(this).selectRoleList(new SysRole()); + } + + /** + * 根据用户ID获取角色选择框列表 + * + * @param userId 用户ID + * @return 选中角色ID列表 + */ + @Override + public List selectRoleListByUserId(Long userId) + { + return roleMapper.selectRoleListByUserId(userId); + } + + /** + * 通过角色ID查询角色 + * + * @param roleId 角色ID + * @return 角色对象信息 + */ + @Override + public SysRole selectRoleById(Long roleId) + { + return roleMapper.selectRoleById(roleId); + } + + /** + * 校验角色名称是否唯一 + * + * @param role 角色信息 + * @return 结果 + */ + @Override + public boolean checkRoleNameUnique(SysRole role) + { + Long roleId = StringUtils.isNull(role.getRoleId()) ? -1L : role.getRoleId(); + SysRole info = roleMapper.checkRoleNameUnique(role.getRoleName()); + if (StringUtils.isNotNull(info) && info.getRoleId().longValue() != roleId.longValue()) + { + return UserConstants.NOT_UNIQUE; + } + return UserConstants.UNIQUE; + } + + /** + * 校验角色权限是否唯一 + * + * @param role 角色信息 + * @return 结果 + */ + @Override + public boolean checkRoleKeyUnique(SysRole role) + { + Long roleId = StringUtils.isNull(role.getRoleId()) ? -1L : role.getRoleId(); + SysRole info = roleMapper.checkRoleKeyUnique(role.getRoleKey()); + if (StringUtils.isNotNull(info) && info.getRoleId().longValue() != roleId.longValue()) + { + return UserConstants.NOT_UNIQUE; + } + return UserConstants.UNIQUE; + } + + /** + * 校验角色是否允许操作 + * + * @param role 角色信息 + */ + @Override + public void checkRoleAllowed(SysRole role) + { + if (StringUtils.isNotNull(role.getRoleId()) && role.isAdmin()) + { + throw new ServiceException("不允许操作超级管理员角色"); + } + } + + /** + * 校验角色是否有数据权限 + * + * @param roleId 角色id + */ + @Override + public void checkRoleDataScope(Long roleId) + { + if (!SysUser.isAdmin(SecurityUtils.getUserId())) + { + SysRole role = new SysRole(); + role.setRoleId(roleId); + List roles = SpringUtils.getAopProxy(this).selectRoleList(role); + if (StringUtils.isEmpty(roles)) + { + throw new ServiceException("没有权限访问角色数据!"); + } + } + } + + /** + * 通过角色ID查询角色使用数量 + * + * @param roleId 角色ID + * @return 结果 + */ + @Override + public int countUserRoleByRoleId(Long roleId) + { + return userRoleMapper.countUserRoleByRoleId(roleId); + } + + /** + * 新增保存角色信息 + * + * @param role 角色信息 + * @return 结果 + */ + @Override + @Transactional + public int insertRole(SysRole role) + { + // 新增角色信息 + roleMapper.insertRole(role); + return insertRoleMenu(role); + } + + /** + * 修改保存角色信息 + * + * @param role 角色信息 + * @return 结果 + */ + @Override + @Transactional + public int updateRole(SysRole role) + { + // 修改角色信息 + roleMapper.updateRole(role); + // 删除角色与菜单关联 + roleMenuMapper.deleteRoleMenuByRoleId(role.getRoleId()); + return insertRoleMenu(role); + } + + /** + * 修改角色状态 + * + * @param role 角色信息 + * @return 结果 + */ + @Override + public int updateRoleStatus(SysRole role) + { + return roleMapper.updateRole(role); + } + + /** + * 修改数据权限信息 + * + * @param role 角色信息 + * @return 结果 + */ + @Override + @Transactional + public int authDataScope(SysRole role) + { + // 修改角色信息 + roleMapper.updateRole(role); + // 删除角色与部门关联 + roleDeptMapper.deleteRoleDeptByRoleId(role.getRoleId()); + // 新增角色和部门信息(数据权限) + return insertRoleDept(role); + } + + /** + * 新增角色菜单信息 + * + * @param role 角色对象 + */ + public int insertRoleMenu(SysRole role) + { + int rows = 1; + // 新增用户与角色管理 + List list = new ArrayList(); + for (Long menuId : role.getMenuIds()) + { + SysRoleMenu rm = new SysRoleMenu(); + rm.setRoleId(role.getRoleId()); + rm.setMenuId(menuId); + list.add(rm); + } + if (list.size() > 0) + { + rows = roleMenuMapper.batchRoleMenu(list); + } + return rows; + } + + /** + * 新增角色部门信息(数据权限) + * + * @param role 角色对象 + */ + public int insertRoleDept(SysRole role) + { + int rows = 1; + // 新增角色与部门(数据权限)管理 + List list = new ArrayList(); + for (Long deptId : role.getDeptIds()) + { + SysRoleDept rd = new SysRoleDept(); + rd.setRoleId(role.getRoleId()); + rd.setDeptId(deptId); + list.add(rd); + } + if (list.size() > 0) + { + rows = roleDeptMapper.batchRoleDept(list); + } + return rows; + } + + /** + * 通过角色ID删除角色 + * + * @param roleId 角色ID + * @return 结果 + */ + @Override + @Transactional + public int deleteRoleById(Long roleId) + { + // 删除角色与菜单关联 + roleMenuMapper.deleteRoleMenuByRoleId(roleId); + // 删除角色与部门关联 + roleDeptMapper.deleteRoleDeptByRoleId(roleId); + return roleMapper.deleteRoleById(roleId); + } + + /** + * 批量删除角色信息 + * + * @param roleIds 需要删除的角色ID + * @return 结果 + */ + @Override + @Transactional + public int deleteRoleByIds(Long[] roleIds) + { + for (Long roleId : roleIds) + { + checkRoleAllowed(new SysRole(roleId)); + checkRoleDataScope(roleId); + SysRole role = selectRoleById(roleId); + if (countUserRoleByRoleId(roleId) > 0) + { + throw new ServiceException(String.format("%1$s已分配,不能删除", role.getRoleName())); + } + } + // 删除角色与菜单关联 + roleMenuMapper.deleteRoleMenu(roleIds); + // 删除角色与部门关联 + roleDeptMapper.deleteRoleDept(roleIds); + return roleMapper.deleteRoleByIds(roleIds); + } + + /** + * 取消授权用户角色 + * + * @param userRole 用户和角色关联信息 + * @return 结果 + */ + @Override + public int deleteAuthUser(SysUserRole userRole) + { + return userRoleMapper.deleteUserRoleInfo(userRole); + } + + /** + * 批量取消授权用户角色 + * + * @param roleId 角色ID + * @param userIds 需要取消授权的用户数据ID + * @return 结果 + */ + @Override + public int deleteAuthUsers(Long roleId, Long[] userIds) + { + return userRoleMapper.deleteUserRoleInfos(roleId, userIds); + } + + /** + * 批量选择授权用户角色 + * + * @param roleId 角色ID + * @param userIds 需要授权的用户数据ID + * @return 结果 + */ + @Override + public int insertAuthUsers(Long roleId, Long[] userIds) + { + // 新增用户与角色管理 + List list = new ArrayList(); + for (Long userId : userIds) + { + SysUserRole ur = new SysUserRole(); + ur.setUserId(userId); + ur.setRoleId(roleId); + list.add(ur); + } + return userRoleMapper.batchUserRole(list); + } +} diff --git a/src/main/java/com/ruoyi/project/system/service/impl/SysUserOnlineServiceImpl.java b/src/main/java/com/ruoyi/project/system/service/impl/SysUserOnlineServiceImpl.java new file mode 100644 index 0000000..de38b5f --- /dev/null +++ b/src/main/java/com/ruoyi/project/system/service/impl/SysUserOnlineServiceImpl.java @@ -0,0 +1,96 @@ +package com.ruoyi.project.system.service.impl; + +import org.springframework.stereotype.Service; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.framework.security.LoginUser; +import com.ruoyi.project.monitor.domain.SysUserOnline; +import com.ruoyi.project.system.service.ISysUserOnlineService; + +/** + * 在线用户 服务层处理 + * + * @author ruoyi + */ +@Service +public class SysUserOnlineServiceImpl implements ISysUserOnlineService +{ + /** + * 通过登录地址查询信息 + * + * @param ipaddr 登录地址 + * @param user 用户信息 + * @return 在线用户信息 + */ + @Override + public SysUserOnline selectOnlineByIpaddr(String ipaddr, LoginUser user) + { + if (StringUtils.equals(ipaddr, user.getIpaddr())) + { + return loginUserToUserOnline(user); + } + return null; + } + + /** + * 通过用户名称查询信息 + * + * @param userName 用户名称 + * @param user 用户信息 + * @return 在线用户信息 + */ + @Override + public SysUserOnline selectOnlineByUserName(String userName, LoginUser user) + { + if (StringUtils.equals(userName, user.getUsername())) + { + return loginUserToUserOnline(user); + } + return null; + } + + /** + * 通过登录地址/用户名称查询信息 + * + * @param ipaddr 登录地址 + * @param userName 用户名称 + * @param user 用户信息 + * @return 在线用户信息 + */ + @Override + public SysUserOnline selectOnlineByInfo(String ipaddr, String userName, LoginUser user) + { + if (StringUtils.equals(ipaddr, user.getIpaddr()) && StringUtils.equals(userName, user.getUsername())) + { + return loginUserToUserOnline(user); + } + return null; + } + + /** + * 设置在线用户信息 + * + * @param user 用户信息 + * @return 在线用户 + */ + @Override + public SysUserOnline loginUserToUserOnline(LoginUser user) + { + if (StringUtils.isNull(user) || StringUtils.isNull(user.getUser())) + { + return null; + } + SysUserOnline sysUserOnline = new SysUserOnline(); + sysUserOnline.setTokenId(user.getToken()); + sysUserOnline.setUserName(user.getUsername()); + sysUserOnline.setIpaddr(user.getIpaddr()); + sysUserOnline.setLoginLocation(user.getLoginLocation()); + sysUserOnline.setBrowser(user.getBrowser()); + sysUserOnline.setOs(user.getOs()); + sysUserOnline.setLoginTime(user.getLoginTime()); + if (StringUtils.isNotNull(user.getUser().getDept())) + { + sysUserOnline.setDeptName(user.getUser().getDept().getDeptName()); + } + return sysUserOnline; + } +} diff --git a/src/main/java/com/ruoyi/project/system/service/impl/SysUserServiceImpl.java b/src/main/java/com/ruoyi/project/system/service/impl/SysUserServiceImpl.java new file mode 100644 index 0000000..40d652d --- /dev/null +++ b/src/main/java/com/ruoyi/project/system/service/impl/SysUserServiceImpl.java @@ -0,0 +1,544 @@ +package com.ruoyi.project.system.service.impl; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; +import javax.validation.Validator; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.util.CollectionUtils; +import com.ruoyi.common.constant.UserConstants; +import com.ruoyi.common.exception.ServiceException; +import com.ruoyi.common.utils.SecurityUtils; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.bean.BeanValidators; +import com.ruoyi.common.utils.spring.SpringUtils; +import com.ruoyi.framework.aspectj.lang.annotation.DataScope; +import com.ruoyi.project.system.domain.SysPost; +import com.ruoyi.project.system.domain.SysRole; +import com.ruoyi.project.system.domain.SysUser; +import com.ruoyi.project.system.domain.SysUserPost; +import com.ruoyi.project.system.domain.SysUserRole; +import com.ruoyi.project.system.mapper.SysPostMapper; +import com.ruoyi.project.system.mapper.SysRoleMapper; +import com.ruoyi.project.system.mapper.SysUserMapper; +import com.ruoyi.project.system.mapper.SysUserPostMapper; +import com.ruoyi.project.system.mapper.SysUserRoleMapper; +import com.ruoyi.project.system.service.ISysConfigService; +import com.ruoyi.project.system.service.ISysUserService; + +/** + * 用户 业务层处理 + * + * @author ruoyi + */ +@Service +public class SysUserServiceImpl implements ISysUserService +{ + private static final Logger log = LoggerFactory.getLogger(SysUserServiceImpl.class); + + @Autowired + private SysUserMapper userMapper; + + @Autowired + private SysRoleMapper roleMapper; + + @Autowired + private SysPostMapper postMapper; + + @Autowired + private SysUserRoleMapper userRoleMapper; + + @Autowired + private SysUserPostMapper userPostMapper; + + @Autowired + private ISysConfigService configService; + + @Autowired + protected Validator validator; + + /** + * 根据条件分页查询用户列表 + * + * @param user 用户信息 + * @return 用户信息集合信息 + */ + @Override + @DataScope(deptAlias = "d", userAlias = "u") + public List selectUserList(SysUser user) + { + return userMapper.selectUserList(user); + } + + /** + * 根据条件分页查询已分配用户角色列表 + * + * @param user 用户信息 + * @return 用户信息集合信息 + */ + @Override + @DataScope(deptAlias = "d", userAlias = "u") + public List selectAllocatedList(SysUser user) + { + return userMapper.selectAllocatedList(user); + } + + /** + * 根据条件分页查询未分配用户角色列表 + * + * @param user 用户信息 + * @return 用户信息集合信息 + */ + @Override + @DataScope(deptAlias = "d", userAlias = "u") + public List selectUnallocatedList(SysUser user) + { + return userMapper.selectUnallocatedList(user); + } + + /** + * 通过用户名查询用户 + * + * @param userName 用户名 + * @return 用户对象信息 + */ + @Override + public SysUser selectUserByUserName(String userName) + { + return userMapper.selectUserByUserName(userName); + } + + /** + * 通过用户ID查询用户 + * + * @param userId 用户ID + * @return 用户对象信息 + */ + @Override + public SysUser selectUserById(Long userId) + { + return userMapper.selectUserById(userId); + } + + /** + * 查询用户所属角色组 + * + * @param userName 用户名 + * @return 结果 + */ + @Override + public String selectUserRoleGroup(String userName) + { + List list = roleMapper.selectRolesByUserName(userName); + if (CollectionUtils.isEmpty(list)) + { + return StringUtils.EMPTY; + } + return list.stream().map(SysRole::getRoleName).collect(Collectors.joining(",")); + } + + /** + * 查询用户所属岗位组 + * + * @param userName 用户名 + * @return 结果 + */ + @Override + public String selectUserPostGroup(String userName) + { + List list = postMapper.selectPostsByUserName(userName); + if (CollectionUtils.isEmpty(list)) + { + return StringUtils.EMPTY; + } + return list.stream().map(SysPost::getPostName).collect(Collectors.joining(",")); + } + + /** + * 校验用户名称是否唯一 + * + * @param user 用户信息 + * @return 结果 + */ + @Override + public boolean checkUserNameUnique(SysUser user) + { + Long userId = StringUtils.isNull(user.getUserId()) ? -1L : user.getUserId(); + SysUser info = userMapper.checkUserNameUnique(user.getUserName()); + if (StringUtils.isNotNull(info) && info.getUserId().longValue() != userId.longValue()) + { + return UserConstants.NOT_UNIQUE; + } + return UserConstants.UNIQUE; + } + + /** + * 校验用户名称是否唯一 + * + * @param user 用户信息 + * @return + */ + @Override + public boolean checkPhoneUnique(SysUser user) + { + Long userId = StringUtils.isNull(user.getUserId()) ? -1L : user.getUserId(); + SysUser info = userMapper.checkPhoneUnique(user.getPhonenumber()); + if (StringUtils.isNotNull(info) && info.getUserId().longValue() != userId.longValue()) + { + return UserConstants.NOT_UNIQUE; + } + return UserConstants.UNIQUE; + } + + /** + * 校验email是否唯一 + * + * @param user 用户信息 + * @return + */ + @Override + public boolean checkEmailUnique(SysUser user) + { + Long userId = StringUtils.isNull(user.getUserId()) ? -1L : user.getUserId(); + SysUser info = userMapper.checkEmailUnique(user.getEmail()); + if (StringUtils.isNotNull(info) && info.getUserId().longValue() != userId.longValue()) + { + return UserConstants.NOT_UNIQUE; + } + return UserConstants.UNIQUE; + } + + /** + * 校验用户是否允许操作 + * + * @param user 用户信息 + */ + @Override + public void checkUserAllowed(SysUser user) + { + if (StringUtils.isNotNull(user.getUserId()) && user.isAdmin()) + { + throw new ServiceException("不允许操作超级管理员用户"); + } + } + + /** + * 校验用户是否有数据权限 + * + * @param userId 用户id + */ + @Override + public void checkUserDataScope(Long userId) + { + if (!SysUser.isAdmin(SecurityUtils.getUserId())) + { + SysUser user = new SysUser(); + user.setUserId(userId); + List users = SpringUtils.getAopProxy(this).selectUserList(user); + if (StringUtils.isEmpty(users)) + { + throw new ServiceException("没有权限访问用户数据!"); + } + } + } + + /** + * 新增保存用户信息 + * + * @param user 用户信息 + * @return 结果 + */ + @Override + @Transactional + public int insertUser(SysUser user) + { + // 新增用户信息 + int rows = userMapper.insertUser(user); + // 新增用户岗位关联 + insertUserPost(user); + // 新增用户与角色管理 + insertUserRole(user); + return rows; + } + + /** + * 注册用户信息 + * + * @param user 用户信息 + * @return 结果 + */ + @Override + public boolean registerUser(SysUser user) + { + return userMapper.insertUser(user) > 0; + } + + /** + * 修改保存用户信息 + * + * @param user 用户信息 + * @return 结果 + */ + @Override + @Transactional + public int updateUser(SysUser user) + { + Long userId = user.getUserId(); + // 删除用户与角色关联 + userRoleMapper.deleteUserRoleByUserId(userId); + // 新增用户与角色管理 + insertUserRole(user); + // 删除用户与岗位关联 + userPostMapper.deleteUserPostByUserId(userId); + // 新增用户与岗位管理 + insertUserPost(user); + return userMapper.updateUser(user); + } + + /** + * 用户授权角色 + * + * @param userId 用户ID + * @param roleIds 角色组 + */ + @Override + @Transactional + public void insertUserAuth(Long userId, Long[] roleIds) + { + userRoleMapper.deleteUserRoleByUserId(userId); + insertUserRole(userId, roleIds); + } + + /** + * 修改用户状态 + * + * @param user 用户信息 + * @return 结果 + */ + @Override + public int updateUserStatus(SysUser user) + { + return userMapper.updateUser(user); + } + + /** + * 修改用户基本信息 + * + * @param user 用户信息 + * @return 结果 + */ + @Override + public int updateUserProfile(SysUser user) + { + return userMapper.updateUser(user); + } + + /** + * 修改用户头像 + * + * @param userName 用户名 + * @param avatar 头像地址 + * @return 结果 + */ + @Override + public boolean updateUserAvatar(String userName, String avatar) + { + return userMapper.updateUserAvatar(userName, avatar) > 0; + } + + /** + * 重置用户密码 + * + * @param user 用户信息 + * @return 结果 + */ + @Override + public int resetPwd(SysUser user) + { + return userMapper.updateUser(user); + } + + /** + * 重置用户密码 + * + * @param userName 用户名 + * @param password 密码 + * @return 结果 + */ + @Override + public int resetUserPwd(String userName, String password) + { + return userMapper.resetUserPwd(userName, password); + } + + /** + * 新增用户角色信息 + * + * @param user 用户对象 + */ + public void insertUserRole(SysUser user) + { + this.insertUserRole(user.getUserId(), user.getRoleIds()); + } + + /** + * 新增用户岗位信息 + * + * @param user 用户对象 + */ + public void insertUserPost(SysUser user) + { + Long[] posts = user.getPostIds(); + if (StringUtils.isNotEmpty(posts)) + { + // 新增用户与岗位管理 + List list = new ArrayList(posts.length); + for (Long postId : posts) + { + SysUserPost up = new SysUserPost(); + up.setUserId(user.getUserId()); + up.setPostId(postId); + list.add(up); + } + userPostMapper.batchUserPost(list); + } + } + + /** + * 新增用户角色信息 + * + * @param userId 用户ID + * @param roleIds 角色组 + */ + public void insertUserRole(Long userId, Long[] roleIds) + { + if (StringUtils.isNotEmpty(roleIds)) + { + // 新增用户与角色管理 + List list = new ArrayList(roleIds.length); + for (Long roleId : roleIds) + { + SysUserRole ur = new SysUserRole(); + ur.setUserId(userId); + ur.setRoleId(roleId); + list.add(ur); + } + userRoleMapper.batchUserRole(list); + } + } + + /** + * 通过用户ID删除用户 + * + * @param userId 用户ID + * @return 结果 + */ + @Override + @Transactional + public int deleteUserById(Long userId) + { + // 删除用户与角色关联 + userRoleMapper.deleteUserRoleByUserId(userId); + // 删除用户与岗位表 + userPostMapper.deleteUserPostByUserId(userId); + return userMapper.deleteUserById(userId); + } + + /** + * 批量删除用户信息 + * + * @param userIds 需要删除的用户ID + * @return 结果 + */ + @Override + @Transactional + public int deleteUserByIds(Long[] userIds) + { + for (Long userId : userIds) + { + checkUserAllowed(new SysUser(userId)); + checkUserDataScope(userId); + } + // 删除用户与角色关联 + userRoleMapper.deleteUserRole(userIds); + // 删除用户与岗位关联 + userPostMapper.deleteUserPost(userIds); + return userMapper.deleteUserByIds(userIds); + } + + /** + * 导入用户数据 + * + * @param userList 用户数据列表 + * @param isUpdateSupport 是否更新支持,如果已存在,则进行更新数据 + * @param operName 操作用户 + * @return 结果 + */ + @Override + public String importUser(List userList, Boolean isUpdateSupport, String operName) + { + if (StringUtils.isNull(userList) || userList.size() == 0) + { + throw new ServiceException("导入用户数据不能为空!"); + } + int successNum = 0; + int failureNum = 0; + StringBuilder successMsg = new StringBuilder(); + StringBuilder failureMsg = new StringBuilder(); + String password = configService.selectConfigByKey("sys.user.initPassword"); + for (SysUser user : userList) + { + try + { + // 验证是否存在这个用户 + SysUser u = userMapper.selectUserByUserName(user.getUserName()); + if (StringUtils.isNull(u)) + { + BeanValidators.validateWithException(validator, user); + user.setPassword(SecurityUtils.encryptPassword(password)); + user.setCreateBy(operName); + userMapper.insertUser(user); + successNum++; + successMsg.append("
" + successNum + "、账号 " + user.getUserName() + " 导入成功"); + } + else if (isUpdateSupport) + { + BeanValidators.validateWithException(validator, user); + checkUserAllowed(u); + checkUserDataScope(u.getUserId()); + user.setUserId(u.getUserId()); + user.setUpdateBy(operName); + userMapper.updateUser(user); + successNum++; + successMsg.append("
" + successNum + "、账号 " + user.getUserName() + " 更新成功"); + } + else + { + failureNum++; + failureMsg.append("
" + failureNum + "、账号 " + user.getUserName() + " 已存在"); + } + } + catch (Exception e) + { + failureNum++; + String msg = "
" + failureNum + "、账号 " + user.getUserName() + " 导入失败:"; + failureMsg.append(msg + e.getMessage()); + log.error(msg, e); + } + } + if (failureNum > 0) + { + failureMsg.insert(0, "很抱歉,导入失败!共 " + failureNum + " 条数据格式不正确,错误如下:"); + throw new ServiceException(failureMsg.toString()); + } + else + { + successMsg.insert(0, "恭喜您,数据已全部导入成功!共 " + successNum + " 条,数据如下:"); + } + return successMsg.toString(); + } +} diff --git a/src/main/java/com/ruoyi/project/tool/gen/controller/GenController.java b/src/main/java/com/ruoyi/project/tool/gen/controller/GenController.java new file mode 100644 index 0000000..8689b37 --- /dev/null +++ b/src/main/java/com/ruoyi/project/tool/gen/controller/GenController.java @@ -0,0 +1,259 @@ +package com.ruoyi.project.tool.gen.controller; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import javax.annotation.Resource; +import javax.servlet.http.HttpServletResponse; +import org.apache.commons.io.IOUtils; +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.alibaba.druid.DbType; +import com.alibaba.druid.sql.SQLUtils; +import com.alibaba.druid.sql.ast.SQLStatement; +import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlCreateTableStatement; +import com.ruoyi.common.core.text.Convert; +import com.ruoyi.common.utils.SecurityUtils; +import com.ruoyi.common.utils.sql.SqlUtil; +import com.ruoyi.framework.aspectj.lang.annotation.Log; +import com.ruoyi.framework.aspectj.lang.enums.BusinessType; +import com.ruoyi.framework.web.controller.BaseController; +import com.ruoyi.framework.web.domain.AjaxResult; +import com.ruoyi.framework.web.page.TableDataInfo; +import com.ruoyi.project.tool.gen.domain.GenTable; +import com.ruoyi.project.tool.gen.domain.GenTableColumn; +import com.ruoyi.project.tool.gen.service.IGenTableColumnService; +import com.ruoyi.project.tool.gen.service.IGenTableService; + +/** + * 代码生成 操作处理 + * + * @author ruoyi + */ +@RestController +@RequestMapping("/tool/gen") +public class GenController extends BaseController +{ + @Autowired + private IGenTableService genTableService; + + @Autowired + private IGenTableColumnService genTableColumnService; + + /** + * 查询代码生成列表 + */ + @PreAuthorize("@ss.hasPermi('tool:gen:list')") + @GetMapping("/list") + public TableDataInfo genList(GenTable genTable) + { + startPage(); + List list = genTableService.selectGenTableList(genTable); + return getDataTable(list); + } + + /** + * 修改代码生成业务 + */ + @PreAuthorize("@ss.hasPermi('tool:gen:query')") + @GetMapping(value = "/{talbleId}") + public AjaxResult getInfo(@PathVariable Long talbleId) + { + GenTable table = genTableService.selectGenTableById(talbleId); + List tables = genTableService.selectGenTableAll(); + List list = genTableColumnService.selectGenTableColumnListByTableId(talbleId); + Map map = new HashMap(); + map.put("info", table); + map.put("rows", list); + map.put("tables", tables); + return success(map); + } + + /** + * 查询数据库列表 + */ + @PreAuthorize("@ss.hasPermi('tool:gen:list')") + @GetMapping("/db/list") + public TableDataInfo dataList(GenTable genTable) + { + startPage(); + List list = genTableService.selectDbTableList(genTable); + return getDataTable(list); + } + + /** + * 查询数据表字段列表 + */ + @PreAuthorize("@ss.hasPermi('tool:gen:list')") + @GetMapping(value = "/column/{tableId}") + public TableDataInfo columnList(Long tableId) + { + TableDataInfo dataInfo = new TableDataInfo(); + List list = genTableColumnService.selectGenTableColumnListByTableId(tableId); + dataInfo.setRows(list); + dataInfo.setTotal(list.size()); + return dataInfo; + } + + /** + * 导入表结构(保存) + */ + @PreAuthorize("@ss.hasPermi('tool:gen:import')") + @Log(title = "代码生成", businessType = BusinessType.IMPORT) + @PostMapping("/importTable") + public AjaxResult importTableSave(String tables) + { + String[] tableNames = Convert.toStrArray(tables); + // 查询表信息 + List tableList = genTableService.selectDbTableListByNames(tableNames); + genTableService.importGenTable(tableList, SecurityUtils.getUsername()); + return success(); + } + + /** + * 创建表结构(保存) + */ + @PreAuthorize("@ss.hasRole('admin')") + @Log(title = "创建表", businessType = BusinessType.OTHER) + @PostMapping("/createTable") + public AjaxResult createTableSave(String sql) + { + try + { + SqlUtil.filterKeyword(sql); + List sqlStatements = SQLUtils.parseStatements(sql, DbType.mysql); + List tableNames = new ArrayList<>(); + for (SQLStatement sqlStatement : sqlStatements) + { + if (sqlStatement instanceof MySqlCreateTableStatement) + { + MySqlCreateTableStatement createTableStatement = (MySqlCreateTableStatement) sqlStatement; + if (genTableService.createTable(createTableStatement.toString())) + { + String tableName = createTableStatement.getTableName().replaceAll("`", ""); + tableNames.add(tableName); + } + } + } + List tableList = genTableService.selectDbTableListByNames(tableNames.toArray(new String[tableNames.size()])); + String operName = SecurityUtils.getUsername(); + genTableService.importGenTable(tableList, operName); + return AjaxResult.success(); + } + catch (Exception e) + { + logger.error(e.getMessage(), e); + return AjaxResult.error("创建表结构异常"); + } + } + + /** + * 修改保存代码生成业务 + */ + @PreAuthorize("@ss.hasPermi('tool:gen:edit')") + @Log(title = "代码生成", businessType = BusinessType.UPDATE) + @PutMapping + public AjaxResult editSave(@Validated @RequestBody GenTable genTable) + { + genTableService.validateEdit(genTable); + genTableService.updateGenTable(genTable); + return success(); + } + + /** + * 删除代码生成 + */ + @PreAuthorize("@ss.hasPermi('tool:gen:remove')") + @Log(title = "代码生成", businessType = BusinessType.DELETE) + @DeleteMapping("/{tableIds}") + public AjaxResult remove(@PathVariable Long[] tableIds) + { + genTableService.deleteGenTableByIds(tableIds); + return success(); + } + + /** + * 预览代码 + */ + @PreAuthorize("@ss.hasPermi('tool:gen:preview')") + @GetMapping("/preview/{tableId}") + public AjaxResult preview(@PathVariable("tableId") Long tableId) throws IOException + { + Map dataMap = genTableService.previewCode(tableId); + return success(dataMap); + } + + /** + * 生成代码(下载方式) + */ + @PreAuthorize("@ss.hasPermi('tool:gen:code')") + @Log(title = "代码生成", businessType = BusinessType.GENCODE) + @GetMapping("/download/{tableName}") + public void download(HttpServletResponse response, @PathVariable("tableName") String tableName) throws IOException + { + byte[] data = genTableService.downloadCode(tableName); + genCode(response, data); + } + + /** + * 生成代码(自定义路径) + */ + @PreAuthorize("@ss.hasPermi('tool:gen:code')") + @Log(title = "代码生成", businessType = BusinessType.GENCODE) + @GetMapping("/genCode/{tableName}") + public AjaxResult genCode(@PathVariable("tableName") String tableName) + { + genTableService.generatorCode(tableName); + return success(); + } + + /** + * 同步数据库 + */ + @PreAuthorize("@ss.hasPermi('tool:gen:edit')") + @Log(title = "代码生成", businessType = BusinessType.UPDATE) + @GetMapping("/synchDb/{tableName}") + public AjaxResult synchDb(@PathVariable("tableName") String tableName) + { + genTableService.synchDb(tableName); + return success(); + } + + /** + * 批量生成代码 + */ + @PreAuthorize("@ss.hasPermi('tool:gen:code')") + @Log(title = "代码生成", businessType = BusinessType.GENCODE) + @GetMapping("/batchGenCode") + public void batchGenCode(HttpServletResponse response, String tables) throws IOException + { + String[] tableNames = Convert.toStrArray(tables); + byte[] data = genTableService.downloadCode(tableNames); + genCode(response, data); + } + + /** + * 生成zip文件 + */ + private void genCode(HttpServletResponse response, byte[] data) throws IOException + { + response.reset(); + response.addHeader("Access-Control-Allow-Origin", "*"); + response.addHeader("Access-Control-Expose-Headers", "Content-Disposition"); + response.setHeader("Content-Disposition", "attachment; filename=\"ruoyi.zip\""); + response.addHeader("Content-Length", "" + data.length); + response.setContentType("application/octet-stream; charset=UTF-8"); + IOUtils.write(data, response.getOutputStream()); + } +} \ No newline at end of file diff --git a/src/main/java/com/ruoyi/project/tool/gen/domain/GenTable.java b/src/main/java/com/ruoyi/project/tool/gen/domain/GenTable.java new file mode 100644 index 0000000..321d134 --- /dev/null +++ b/src/main/java/com/ruoyi/project/tool/gen/domain/GenTable.java @@ -0,0 +1,385 @@ +package com.ruoyi.project.tool.gen.domain; + +import java.util.List; +import javax.validation.Valid; +import javax.validation.constraints.NotBlank; +import org.apache.commons.lang3.ArrayUtils; +import com.ruoyi.common.constant.GenConstants; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.framework.web.domain.BaseEntity; + +/** + * 业务表 gen_table + * + * @author ruoyi + */ +public class GenTable extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** 编号 */ + private Long tableId; + + /** 表名称 */ + @NotBlank(message = "表名称不能为空") + private String tableName; + + /** 表描述 */ + @NotBlank(message = "表描述不能为空") + private String tableComment; + + /** 关联父表的表名 */ + private String subTableName; + + /** 本表关联父表的外键名 */ + private String subTableFkName; + + /** 实体类名称(首字母大写) */ + @NotBlank(message = "实体类名称不能为空") + private String className; + + /** 使用的模板(crud单表操作 tree树表操作 sub主子表操作) */ + private String tplCategory; + + /** 前端类型(element-ui模版 element-plus模版) */ + private String tplWebType; + + /** 生成包路径 */ + @NotBlank(message = "生成包路径不能为空") + private String packageName; + + /** 生成模块名 */ + @NotBlank(message = "生成模块名不能为空") + private String moduleName; + + /** 生成业务名 */ + @NotBlank(message = "生成业务名不能为空") + private String businessName; + + /** 生成功能名 */ + @NotBlank(message = "生成功能名不能为空") + private String functionName; + + /** 生成作者 */ + @NotBlank(message = "作者不能为空") + private String functionAuthor; + + /** 生成代码方式(0zip压缩包 1自定义路径) */ + private String genType; + + /** 生成路径(不填默认项目路径) */ + private String genPath; + + /** 主键信息 */ + private GenTableColumn pkColumn; + + /** 子表信息 */ + private GenTable subTable; + + /** 表列信息 */ + @Valid + private List columns; + + /** 其它生成选项 */ + private String options; + + /** 树编码字段 */ + private String treeCode; + + /** 树父编码字段 */ + private String treeParentCode; + + /** 树名称字段 */ + private String treeName; + + /** 上级菜单ID字段 */ + private String parentMenuId; + + /** 上级菜单名称字段 */ + private String parentMenuName; + + public Long getTableId() + { + return tableId; + } + + public void setTableId(Long tableId) + { + this.tableId = tableId; + } + + public String getTableName() + { + return tableName; + } + + public void setTableName(String tableName) + { + this.tableName = tableName; + } + + public String getTableComment() + { + return tableComment; + } + + public void setTableComment(String tableComment) + { + this.tableComment = tableComment; + } + + public String getSubTableName() + { + return subTableName; + } + + public void setSubTableName(String subTableName) + { + this.subTableName = subTableName; + } + + public String getSubTableFkName() + { + return subTableFkName; + } + + public void setSubTableFkName(String subTableFkName) + { + this.subTableFkName = subTableFkName; + } + + public String getClassName() + { + return className; + } + + public void setClassName(String className) + { + this.className = className; + } + + public String getTplCategory() + { + return tplCategory; + } + + public void setTplCategory(String tplCategory) + { + this.tplCategory = tplCategory; + } + + public String getTplWebType() + { + return tplWebType; + } + + public void setTplWebType(String tplWebType) + { + this.tplWebType = tplWebType; + } + + public String getPackageName() + { + return packageName; + } + + public void setPackageName(String packageName) + { + this.packageName = packageName; + } + + public String getModuleName() + { + return moduleName; + } + + public void setModuleName(String moduleName) + { + this.moduleName = moduleName; + } + + public String getBusinessName() + { + return businessName; + } + + public void setBusinessName(String businessName) + { + this.businessName = businessName; + } + + public String getFunctionName() + { + return functionName; + } + + public void setFunctionName(String functionName) + { + this.functionName = functionName; + } + + public String getFunctionAuthor() + { + return functionAuthor; + } + + public void setFunctionAuthor(String functionAuthor) + { + this.functionAuthor = functionAuthor; + } + + public String getGenType() + { + return genType; + } + + public void setGenType(String genType) + { + this.genType = genType; + } + + public String getGenPath() + { + return genPath; + } + + public void setGenPath(String genPath) + { + this.genPath = genPath; + } + + public GenTableColumn getPkColumn() + { + return pkColumn; + } + + public void setPkColumn(GenTableColumn pkColumn) + { + this.pkColumn = pkColumn; + } + + public GenTable getSubTable() + { + return subTable; + } + + public void setSubTable(GenTable subTable) + { + this.subTable = subTable; + } + + public List getColumns() + { + return columns; + } + + public void setColumns(List columns) + { + this.columns = columns; + } + + public String getOptions() + { + return options; + } + + public void setOptions(String options) + { + this.options = options; + } + + public String getTreeCode() + { + return treeCode; + } + + public void setTreeCode(String treeCode) + { + this.treeCode = treeCode; + } + + public String getTreeParentCode() + { + return treeParentCode; + } + + public void setTreeParentCode(String treeParentCode) + { + this.treeParentCode = treeParentCode; + } + + public String getTreeName() + { + return treeName; + } + + public void setTreeName(String treeName) + { + this.treeName = treeName; + } + + public String getParentMenuId() + { + return parentMenuId; + } + + public void setParentMenuId(String parentMenuId) + { + this.parentMenuId = parentMenuId; + } + + public String getParentMenuName() + { + return parentMenuName; + } + + public void setParentMenuName(String parentMenuName) + { + this.parentMenuName = parentMenuName; + } + + public boolean isSub() + { + return isSub(this.tplCategory); + } + + public static boolean isSub(String tplCategory) + { + return tplCategory != null && StringUtils.equals(GenConstants.TPL_SUB, tplCategory); + } + + public boolean isTree() + { + return isTree(this.tplCategory); + } + + public static boolean isTree(String tplCategory) + { + return tplCategory != null && StringUtils.equals(GenConstants.TPL_TREE, tplCategory); + } + + public boolean isCrud() + { + return isCrud(this.tplCategory); + } + + public static boolean isCrud(String tplCategory) + { + return tplCategory != null && StringUtils.equals(GenConstants.TPL_CRUD, tplCategory); + } + + public boolean isSuperColumn(String javaField) + { + return isSuperColumn(this.tplCategory, javaField); + } + + public static boolean isSuperColumn(String tplCategory, String javaField) + { + if (isTree(tplCategory)) + { + return StringUtils.equalsAnyIgnoreCase(javaField, + ArrayUtils.addAll(GenConstants.TREE_ENTITY, GenConstants.BASE_ENTITY)); + } + return StringUtils.equalsAnyIgnoreCase(javaField, GenConstants.BASE_ENTITY); + } +} \ No newline at end of file diff --git a/src/main/java/com/ruoyi/project/tool/gen/domain/GenTableColumn.java b/src/main/java/com/ruoyi/project/tool/gen/domain/GenTableColumn.java new file mode 100644 index 0000000..cf275fe --- /dev/null +++ b/src/main/java/com/ruoyi/project/tool/gen/domain/GenTableColumn.java @@ -0,0 +1,373 @@ +package com.ruoyi.project.tool.gen.domain; + +import javax.validation.constraints.NotBlank; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.framework.web.domain.BaseEntity; + +/** + * 代码生成业务字段表 gen_table_column + * + * @author ruoyi + */ +public class GenTableColumn extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** 编号 */ + private Long columnId; + + /** 归属表编号 */ + private Long tableId; + + /** 列名称 */ + private String columnName; + + /** 列描述 */ + private String columnComment; + + /** 列类型 */ + private String columnType; + + /** JAVA类型 */ + private String javaType; + + /** JAVA字段名 */ + @NotBlank(message = "Java属性不能为空") + private String javaField; + + /** 是否主键(1是) */ + private String isPk; + + /** 是否自增(1是) */ + private String isIncrement; + + /** 是否必填(1是) */ + private String isRequired; + + /** 是否为插入字段(1是) */ + private String isInsert; + + /** 是否编辑字段(1是) */ + private String isEdit; + + /** 是否列表字段(1是) */ + private String isList; + + /** 是否查询字段(1是) */ + private String isQuery; + + /** 查询方式(EQ等于、NE不等于、GT大于、LT小于、LIKE模糊、BETWEEN范围) */ + private String queryType; + + /** 显示类型(input文本框、textarea文本域、select下拉框、checkbox复选框、radio单选框、datetime日期控件、image图片上传控件、upload文件上传控件、editor富文本控件) */ + private String htmlType; + + /** 字典类型 */ + private String dictType; + + /** 排序 */ + private Integer sort; + + public void setColumnId(Long columnId) + { + this.columnId = columnId; + } + + public Long getColumnId() + { + return columnId; + } + + public void setTableId(Long tableId) + { + this.tableId = tableId; + } + + public Long getTableId() + { + return tableId; + } + + public void setColumnName(String columnName) + { + this.columnName = columnName; + } + + public String getColumnName() + { + return columnName; + } + + public void setColumnComment(String columnComment) + { + this.columnComment = columnComment; + } + + public String getColumnComment() + { + return columnComment; + } + + public void setColumnType(String columnType) + { + this.columnType = columnType; + } + + public String getColumnType() + { + return columnType; + } + + public void setJavaType(String javaType) + { + this.javaType = javaType; + } + + public String getJavaType() + { + return javaType; + } + + public void setJavaField(String javaField) + { + this.javaField = javaField; + } + + public String getJavaField() + { + return javaField; + } + + public String getCapJavaField() + { + return StringUtils.capitalize(javaField); + } + + public void setIsPk(String isPk) + { + this.isPk = isPk; + } + + public String getIsPk() + { + return isPk; + } + + public boolean isPk() + { + return isPk(this.isPk); + } + + public boolean isPk(String isPk) + { + return isPk != null && StringUtils.equals("1", isPk); + } + + public String getIsIncrement() + { + return isIncrement; + } + + public void setIsIncrement(String isIncrement) + { + this.isIncrement = isIncrement; + } + + public boolean isIncrement() + { + return isIncrement(this.isIncrement); + } + + public boolean isIncrement(String isIncrement) + { + return isIncrement != null && StringUtils.equals("1", isIncrement); + } + + public void setIsRequired(String isRequired) + { + this.isRequired = isRequired; + } + + public String getIsRequired() + { + return isRequired; + } + + public boolean isRequired() + { + return isRequired(this.isRequired); + } + + public boolean isRequired(String isRequired) + { + return isRequired != null && StringUtils.equals("1", isRequired); + } + + public void setIsInsert(String isInsert) + { + this.isInsert = isInsert; + } + + public String getIsInsert() + { + return isInsert; + } + + public boolean isInsert() + { + return isInsert(this.isInsert); + } + + public boolean isInsert(String isInsert) + { + return isInsert != null && StringUtils.equals("1", isInsert); + } + + public void setIsEdit(String isEdit) + { + this.isEdit = isEdit; + } + + public String getIsEdit() + { + return isEdit; + } + + public boolean isEdit() + { + return isInsert(this.isEdit); + } + + public boolean isEdit(String isEdit) + { + return isEdit != null && StringUtils.equals("1", isEdit); + } + + public void setIsList(String isList) + { + this.isList = isList; + } + + public String getIsList() + { + return isList; + } + + public boolean isList() + { + return isList(this.isList); + } + + public boolean isList(String isList) + { + return isList != null && StringUtils.equals("1", isList); + } + + public void setIsQuery(String isQuery) + { + this.isQuery = isQuery; + } + + public String getIsQuery() + { + return isQuery; + } + + public boolean isQuery() + { + return isQuery(this.isQuery); + } + + public boolean isQuery(String isQuery) + { + return isQuery != null && StringUtils.equals("1", isQuery); + } + + public void setQueryType(String queryType) + { + this.queryType = queryType; + } + + public String getQueryType() + { + return queryType; + } + + public String getHtmlType() + { + return htmlType; + } + + public void setHtmlType(String htmlType) + { + this.htmlType = htmlType; + } + + public void setDictType(String dictType) + { + this.dictType = dictType; + } + + public String getDictType() + { + return dictType; + } + + public void setSort(Integer sort) + { + this.sort = sort; + } + + public Integer getSort() + { + return sort; + } + + public boolean isSuperColumn() + { + return isSuperColumn(this.javaField); + } + + public static boolean isSuperColumn(String javaField) + { + return StringUtils.equalsAnyIgnoreCase(javaField, + // BaseEntity + "createBy", "createTime", "updateBy", "updateTime", "remark", + // TreeEntity + "parentName", "parentId", "orderNum", "ancestors"); + } + + public boolean isUsableColumn() + { + return isUsableColumn(javaField); + } + + public static boolean isUsableColumn(String javaField) + { + // isSuperColumn()中的名单用于避免生成多余Domain属性,若某些属性在生成页面时需要用到不能忽略,则放在此处白名单 + return StringUtils.equalsAnyIgnoreCase(javaField, "parentId", "orderNum", "remark"); + } + + public String readConverterExp() + { + String remarks = StringUtils.substringBetween(this.columnComment, "(", ")"); + StringBuffer sb = new StringBuffer(); + if (StringUtils.isNotEmpty(remarks)) + { + for (String value : remarks.split(" ")) + { + if (StringUtils.isNotEmpty(value)) + { + Object startStr = value.subSequence(0, 1); + String endStr = value.substring(1); + sb.append("").append(startStr).append("=").append(endStr).append(","); + } + } + return sb.deleteCharAt(sb.length() - 1).toString(); + } + else + { + return this.columnComment; + } + } +} \ No newline at end of file diff --git a/src/main/java/com/ruoyi/project/tool/gen/mapper/GenTableColumnMapper.java b/src/main/java/com/ruoyi/project/tool/gen/mapper/GenTableColumnMapper.java new file mode 100644 index 0000000..0124c7b --- /dev/null +++ b/src/main/java/com/ruoyi/project/tool/gen/mapper/GenTableColumnMapper.java @@ -0,0 +1,60 @@ +package com.ruoyi.project.tool.gen.mapper; + +import java.util.List; +import com.ruoyi.project.tool.gen.domain.GenTableColumn; + +/** + * 业务字段 数据层 + * + * @author ruoyi + */ +public interface GenTableColumnMapper +{ + /** + * 根据表名称查询列信息 + * + * @param tableName 表名称 + * @return 列信息 + */ + public List selectDbTableColumnsByName(String tableName); + + /** + * 查询业务字段列表 + * + * @param tableId 业务字段编号 + * @return 业务字段集合 + */ + public List selectGenTableColumnListByTableId(Long tableId); + + /** + * 新增业务字段 + * + * @param genTableColumn 业务字段信息 + * @return 结果 + */ + public int insertGenTableColumn(GenTableColumn genTableColumn); + + /** + * 修改业务字段 + * + * @param genTableColumn 业务字段信息 + * @return 结果 + */ + public int updateGenTableColumn(GenTableColumn genTableColumn); + + /** + * 删除业务字段 + * + * @param genTableColumns 列数据 + * @return 结果 + */ + public int deleteGenTableColumns(List genTableColumns); + + /** + * 批量删除业务字段 + * + * @param ids 需要删除的数据ID + * @return 结果 + */ + public int deleteGenTableColumnByIds(Long[] ids); +} \ No newline at end of file diff --git a/src/main/java/com/ruoyi/project/tool/gen/mapper/GenTableMapper.java b/src/main/java/com/ruoyi/project/tool/gen/mapper/GenTableMapper.java new file mode 100644 index 0000000..b574de9 --- /dev/null +++ b/src/main/java/com/ruoyi/project/tool/gen/mapper/GenTableMapper.java @@ -0,0 +1,91 @@ +package com.ruoyi.project.tool.gen.mapper; + +import java.util.List; +import com.ruoyi.project.tool.gen.domain.GenTable; + +/** + * 业务 数据层 + * + * @author ruoyi + */ +public interface GenTableMapper +{ + /** + * 查询业务列表 + * + * @param genTable 业务信息 + * @return 业务集合 + */ + public List selectGenTableList(GenTable genTable); + + /** + * 查询据库列表 + * + * @param genTable 业务信息 + * @return 数据库表集合 + */ + public List selectDbTableList(GenTable genTable); + + /** + * 查询据库列表 + * + * @param tableNames 表名称组 + * @return 数据库表集合 + */ + public List selectDbTableListByNames(String[] tableNames); + + /** + * 查询所有表信息 + * + * @return 表信息集合 + */ + public List selectGenTableAll(); + + /** + * 查询表ID业务信息 + * + * @param id 业务ID + * @return 业务信息 + */ + public GenTable selectGenTableById(Long id); + + /** + * 查询表名称业务信息 + * + * @param tableName 表名称 + * @return 业务信息 + */ + public GenTable selectGenTableByName(String tableName); + + /** + * 新增业务 + * + * @param genTable 业务信息 + * @return 结果 + */ + public int insertGenTable(GenTable genTable); + + /** + * 修改业务 + * + * @param genTable 业务信息 + * @return 结果 + */ + public int updateGenTable(GenTable genTable); + + /** + * 批量删除业务 + * + * @param ids 需要删除的数据ID + * @return 结果 + */ + public int deleteGenTableByIds(Long[] ids); + + /** + * 创建表 + * + * @param sql 表结构 + * @return 结果 + */ + public int createTable(String sql); +} \ No newline at end of file diff --git a/src/main/java/com/ruoyi/project/tool/gen/service/GenTableColumnServiceImpl.java b/src/main/java/com/ruoyi/project/tool/gen/service/GenTableColumnServiceImpl.java new file mode 100644 index 0000000..446a0df --- /dev/null +++ b/src/main/java/com/ruoyi/project/tool/gen/service/GenTableColumnServiceImpl.java @@ -0,0 +1,68 @@ +package com.ruoyi.project.tool.gen.service; + +import java.util.List; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import com.ruoyi.common.core.text.Convert; +import com.ruoyi.project.tool.gen.domain.GenTableColumn; +import com.ruoyi.project.tool.gen.mapper.GenTableColumnMapper; + +/** + * 业务字段 服务层实现 + * + * @author ruoyi + */ +@Service +public class GenTableColumnServiceImpl implements IGenTableColumnService +{ + @Autowired + private GenTableColumnMapper genTableColumnMapper; + + /** + * 查询业务字段列表 + * + * @param tableId 业务字段编号 + * @return 业务字段集合 + */ + @Override + public List selectGenTableColumnListByTableId(Long tableId) + { + return genTableColumnMapper.selectGenTableColumnListByTableId(tableId); + } + + /** + * 新增业务字段 + * + * @param genTableColumn 业务字段信息 + * @return 结果 + */ + @Override + public int insertGenTableColumn(GenTableColumn genTableColumn) + { + return genTableColumnMapper.insertGenTableColumn(genTableColumn); + } + + /** + * 修改业务字段 + * + * @param genTableColumn 业务字段信息 + * @return 结果 + */ + @Override + public int updateGenTableColumn(GenTableColumn genTableColumn) + { + return genTableColumnMapper.updateGenTableColumn(genTableColumn); + } + + /** + * 删除业务字段对象 + * + * @param ids 需要删除的数据ID + * @return 结果 + */ + @Override + public int deleteGenTableColumnByIds(String ids) + { + return genTableColumnMapper.deleteGenTableColumnByIds(Convert.toLongArray(ids)); + } +} \ No newline at end of file diff --git a/src/main/java/com/ruoyi/project/tool/gen/service/GenTableServiceImpl.java b/src/main/java/com/ruoyi/project/tool/gen/service/GenTableServiceImpl.java new file mode 100644 index 0000000..e07d6d9 --- /dev/null +++ b/src/main/java/com/ruoyi/project/tool/gen/service/GenTableServiceImpl.java @@ -0,0 +1,531 @@ +package com.ruoyi.project.tool.gen.service; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.IOException; +import java.io.StringWriter; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.function.Function; +import java.util.stream.Collectors; +import java.util.zip.ZipEntry; +import java.util.zip.ZipOutputStream; +import org.apache.commons.io.FileUtils; +import org.apache.commons.io.IOUtils; +import org.apache.velocity.Template; +import org.apache.velocity.VelocityContext; +import org.apache.velocity.app.Velocity; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONObject; +import com.ruoyi.common.constant.Constants; +import com.ruoyi.common.constant.GenConstants; +import com.ruoyi.common.core.text.CharsetKit; +import com.ruoyi.common.exception.ServiceException; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.project.tool.gen.domain.GenTable; +import com.ruoyi.project.tool.gen.domain.GenTableColumn; +import com.ruoyi.project.tool.gen.mapper.GenTableColumnMapper; +import com.ruoyi.project.tool.gen.mapper.GenTableMapper; +import com.ruoyi.project.tool.gen.util.GenUtils; +import com.ruoyi.project.tool.gen.util.VelocityInitializer; +import com.ruoyi.project.tool.gen.util.VelocityUtils; + +/** + * 业务 服务层实现 + * + * @author ruoyi + */ +@Service +public class GenTableServiceImpl implements IGenTableService +{ + private static final Logger log = LoggerFactory.getLogger(GenTableServiceImpl.class); + + @Autowired + private GenTableMapper genTableMapper; + + @Autowired + private GenTableColumnMapper genTableColumnMapper; + + /** + * 查询业务信息 + * + * @param id 业务ID + * @return 业务信息 + */ + @Override + public GenTable selectGenTableById(Long id) + { + GenTable genTable = genTableMapper.selectGenTableById(id); + setTableFromOptions(genTable); + return genTable; + } + + /** + * 查询业务列表 + * + * @param genTable 业务信息 + * @return 业务集合 + */ + @Override + public List selectGenTableList(GenTable genTable) + { + return genTableMapper.selectGenTableList(genTable); + } + + /** + * 查询据库列表 + * + * @param genTable 业务信息 + * @return 数据库表集合 + */ + @Override + public List selectDbTableList(GenTable genTable) + { + return genTableMapper.selectDbTableList(genTable); + } + + /** + * 查询据库列表 + * + * @param tableNames 表名称组 + * @return 数据库表集合 + */ + @Override + public List selectDbTableListByNames(String[] tableNames) + { + return genTableMapper.selectDbTableListByNames(tableNames); + } + + /** + * 查询所有表信息 + * + * @return 表信息集合 + */ + @Override + public List selectGenTableAll() + { + return genTableMapper.selectGenTableAll(); + } + + /** + * 修改业务 + * + * @param genTable 业务信息 + * @return 结果 + */ + @Override + @Transactional + public void updateGenTable(GenTable genTable) + { + String options = JSON.toJSONString(genTable.getParams()); + genTable.setOptions(options); + int row = genTableMapper.updateGenTable(genTable); + if (row > 0) + { + for (GenTableColumn genTableColumn : genTable.getColumns()) + { + genTableColumnMapper.updateGenTableColumn(genTableColumn); + } + } + } + + /** + * 删除业务对象 + * + * @param tableIds 需要删除的数据ID + * @return 结果 + */ + @Override + @Transactional + public void deleteGenTableByIds(Long[] tableIds) + { + genTableMapper.deleteGenTableByIds(tableIds); + genTableColumnMapper.deleteGenTableColumnByIds(tableIds); + } + + /** + * 创建表 + * + * @param sql 创建表语句 + * @return 结果 + */ + @Override + public boolean createTable(String sql) + { + return genTableMapper.createTable(sql) == 0; + } + + /** + * 导入表结构 + * + * @param tableList 导入表列表 + */ + @Override + @Transactional + public void importGenTable(List tableList, String operName) + { + try + { + for (GenTable table : tableList) + { + String tableName = table.getTableName(); + GenUtils.initTable(table, operName); + int row = genTableMapper.insertGenTable(table); + if (row > 0) + { + // 保存列信息 + List genTableColumns = genTableColumnMapper.selectDbTableColumnsByName(tableName); + for (GenTableColumn column : genTableColumns) + { + GenUtils.initColumnField(column, table); + genTableColumnMapper.insertGenTableColumn(column); + } + } + } + } + catch (Exception e) + { + throw new ServiceException("导入失败:" + e.getMessage()); + } + } + + /** + * 预览代码 + * + * @param tableId 表编号 + * @return 预览数据列表 + */ + @Override + public Map previewCode(Long tableId) + { + Map dataMap = new LinkedHashMap<>(); + // 查询表信息 + GenTable table = genTableMapper.selectGenTableById(tableId); + // 设置主子表信息 + setSubTable(table); + // 设置主键列信息 + setPkColumn(table); + VelocityInitializer.initVelocity(); + + VelocityContext context = VelocityUtils.prepareContext(table); + + // 获取模板列表 + List templates = VelocityUtils.getTemplateList(table.getTplCategory(), table.getTplWebType()); + for (String template : templates) + { + // 渲染模板 + StringWriter sw = new StringWriter(); + Template tpl = Velocity.getTemplate(template, Constants.UTF8); + tpl.merge(context, sw); + dataMap.put(template, sw.toString()); + } + return dataMap; + } + + /** + * 生成代码(下载方式) + * + * @param tableName 表名称 + * @return 数据 + */ + @Override + public byte[] downloadCode(String tableName) + { + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + ZipOutputStream zip = new ZipOutputStream(outputStream); + generatorCode(tableName, zip); + IOUtils.closeQuietly(zip); + return outputStream.toByteArray(); + } + + /** + * 生成代码(自定义路径) + * + * @param tableName 表名称 + */ + @Override + public void generatorCode(String tableName) + { + // 查询表信息 + GenTable table = genTableMapper.selectGenTableByName(tableName); + // 设置主子表信息 + setSubTable(table); + // 设置主键列信息 + setPkColumn(table); + + VelocityInitializer.initVelocity(); + + VelocityContext context = VelocityUtils.prepareContext(table); + + // 获取模板列表 + List templates = VelocityUtils.getTemplateList(table.getTplCategory(), table.getTplWebType()); + for (String template : templates) + { + if (!StringUtils.containsAny(template, "sql.vm", "api.js.vm", "index.vue.vm", "index-tree.vue.vm")) + { + // 渲染模板 + StringWriter sw = new StringWriter(); + Template tpl = Velocity.getTemplate(template, Constants.UTF8); + tpl.merge(context, sw); + try + { + String path = getGenPath(table, template); + FileUtils.writeStringToFile(new File(path), sw.toString(), CharsetKit.UTF_8); + } + catch (IOException e) + { + throw new ServiceException("渲染模板失败,表名:" + table.getTableName()); + } + } + } + } + + /** + * 同步数据库 + * + * @param tableName 表名称 + */ + @Override + @Transactional + public void synchDb(String tableName) + { + GenTable table = genTableMapper.selectGenTableByName(tableName); + List tableColumns = table.getColumns(); + Map tableColumnMap = tableColumns.stream().collect(Collectors.toMap(GenTableColumn::getColumnName, Function.identity())); + + List dbTableColumns = genTableColumnMapper.selectDbTableColumnsByName(tableName); + if (StringUtils.isEmpty(dbTableColumns)) + { + throw new ServiceException("同步数据失败,原表结构不存在"); + } + List dbTableColumnNames = dbTableColumns.stream().map(GenTableColumn::getColumnName).collect(Collectors.toList()); + + dbTableColumns.forEach(column -> { + GenUtils.initColumnField(column, table); + if (tableColumnMap.containsKey(column.getColumnName())) + { + GenTableColumn prevColumn = tableColumnMap.get(column.getColumnName()); + column.setColumnId(prevColumn.getColumnId()); + if (column.isList()) + { + // 如果是列表,继续保留查询方式/字典类型选项 + column.setDictType(prevColumn.getDictType()); + column.setQueryType(prevColumn.getQueryType()); + } + if (StringUtils.isNotEmpty(prevColumn.getIsRequired()) && !column.isPk() + && (column.isInsert() || column.isEdit()) + && ((column.isUsableColumn()) || (!column.isSuperColumn()))) + { + // 如果是(新增/修改&非主键/非忽略及父属性),继续保留必填/显示类型选项 + column.setIsRequired(prevColumn.getIsRequired()); + column.setHtmlType(prevColumn.getHtmlType()); + } + genTableColumnMapper.updateGenTableColumn(column); + } + else + { + genTableColumnMapper.insertGenTableColumn(column); + } + }); + + List delColumns = tableColumns.stream().filter(column -> !dbTableColumnNames.contains(column.getColumnName())).collect(Collectors.toList()); + if (StringUtils.isNotEmpty(delColumns)) + { + genTableColumnMapper.deleteGenTableColumns(delColumns); + } + } + + /** + * 批量生成代码(下载方式) + * + * @param tableNames 表数组 + * @return 数据 + */ + @Override + public byte[] downloadCode(String[] tableNames) + { + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + ZipOutputStream zip = new ZipOutputStream(outputStream); + for (String tableName : tableNames) + { + generatorCode(tableName, zip); + } + IOUtils.closeQuietly(zip); + return outputStream.toByteArray(); + } + + /** + * 查询表信息并生成代码 + */ + private void generatorCode(String tableName, ZipOutputStream zip) + { + // 查询表信息 + GenTable table = genTableMapper.selectGenTableByName(tableName); + // 设置主子表信息 + setSubTable(table); + // 设置主键列信息 + setPkColumn(table); + + VelocityInitializer.initVelocity(); + + VelocityContext context = VelocityUtils.prepareContext(table); + + // 获取模板列表 + List templates = VelocityUtils.getTemplateList(table.getTplCategory(), table.getTplWebType()); + for (String template : templates) + { + // 渲染模板 + StringWriter sw = new StringWriter(); + Template tpl = Velocity.getTemplate(template, Constants.UTF8); + tpl.merge(context, sw); + try + { + // 添加到zip + zip.putNextEntry(new ZipEntry(VelocityUtils.getFileName(template, table))); + IOUtils.write(sw.toString(), zip, Constants.UTF8); + IOUtils.closeQuietly(sw); + zip.flush(); + zip.closeEntry(); + } + catch (IOException e) + { + log.error("渲染模板失败,表名:" + table.getTableName(), e); + } + } + } + + /** + * 修改保存参数校验 + * + * @param genTable 业务信息 + */ + @Override + public void validateEdit(GenTable genTable) + { + if (GenConstants.TPL_TREE.equals(genTable.getTplCategory())) + { + String options = JSON.toJSONString(genTable.getParams()); + JSONObject paramsObj = JSON.parseObject(options); + if (StringUtils.isEmpty(paramsObj.getString(GenConstants.TREE_CODE))) + { + throw new ServiceException("树编码字段不能为空"); + } + else if (StringUtils.isEmpty(paramsObj.getString(GenConstants.TREE_PARENT_CODE))) + { + throw new ServiceException("树父编码字段不能为空"); + } + else if (StringUtils.isEmpty(paramsObj.getString(GenConstants.TREE_NAME))) + { + throw new ServiceException("树名称字段不能为空"); + } + } + else if (GenConstants.TPL_SUB.equals(genTable.getTplCategory())) + { + if (StringUtils.isEmpty(genTable.getSubTableName())) + { + throw new ServiceException("关联子表的表名不能为空"); + } + else if (StringUtils.isEmpty(genTable.getSubTableFkName())) + { + throw new ServiceException("子表关联的外键名不能为空"); + } + } + } + + /** + * 设置主键列信息 + * + * @param table 业务表信息 + */ + public void setPkColumn(GenTable table) + { + for (GenTableColumn column : table.getColumns()) + { + if (column.isPk()) + { + table.setPkColumn(column); + break; + } + } + if (StringUtils.isNull(table.getPkColumn())) + { + table.setPkColumn(table.getColumns().get(0)); + } + if (GenConstants.TPL_SUB.equals(table.getTplCategory())) + { + for (GenTableColumn column : table.getSubTable().getColumns()) + { + if (column.isPk()) + { + table.getSubTable().setPkColumn(column); + break; + } + } + if (StringUtils.isNull(table.getSubTable().getPkColumn())) + { + table.getSubTable().setPkColumn(table.getSubTable().getColumns().get(0)); + } + } + } + + /** + * 设置主子表信息 + * + * @param table 业务表信息 + */ + public void setSubTable(GenTable table) + { + String subTableName = table.getSubTableName(); + if (StringUtils.isNotEmpty(subTableName)) + { + table.setSubTable(genTableMapper.selectGenTableByName(subTableName)); + } + } + + /** + * 设置代码生成其他选项值 + * + * @param genTable 设置后的生成对象 + */ + public void setTableFromOptions(GenTable genTable) + { + JSONObject paramsObj = JSON.parseObject(genTable.getOptions()); + if (StringUtils.isNotNull(paramsObj)) + { + String treeCode = paramsObj.getString(GenConstants.TREE_CODE); + String treeParentCode = paramsObj.getString(GenConstants.TREE_PARENT_CODE); + String treeName = paramsObj.getString(GenConstants.TREE_NAME); + String parentMenuId = paramsObj.getString(GenConstants.PARENT_MENU_ID); + String parentMenuName = paramsObj.getString(GenConstants.PARENT_MENU_NAME); + + genTable.setTreeCode(treeCode); + genTable.setTreeParentCode(treeParentCode); + genTable.setTreeName(treeName); + genTable.setParentMenuId(parentMenuId); + genTable.setParentMenuName(parentMenuName); + } + } + + /** + * 获取代码生成地址 + * + * @param table 业务表信息 + * @param template 模板文件路径 + * @return 生成地址 + */ + public static String getGenPath(GenTable table, String template) + { + String genPath = table.getGenPath(); + if (StringUtils.equals(genPath, "/")) + { + return System.getProperty("user.dir") + File.separator + "src" + File.separator + VelocityUtils.getFileName(template, table); + } + return genPath + File.separator + VelocityUtils.getFileName(template, table); + } +} \ No newline at end of file diff --git a/src/main/java/com/ruoyi/project/tool/gen/service/IGenTableColumnService.java b/src/main/java/com/ruoyi/project/tool/gen/service/IGenTableColumnService.java new file mode 100644 index 0000000..d87b023 --- /dev/null +++ b/src/main/java/com/ruoyi/project/tool/gen/service/IGenTableColumnService.java @@ -0,0 +1,44 @@ +package com.ruoyi.project.tool.gen.service; + +import java.util.List; +import com.ruoyi.project.tool.gen.domain.GenTableColumn; + +/** + * 业务字段 服务层 + * + * @author ruoyi + */ +public interface IGenTableColumnService +{ + /** + * 查询业务字段列表 + * + * @param tableId 业务字段编号 + * @return 业务字段集合 + */ + public List selectGenTableColumnListByTableId(Long tableId); + + /** + * 新增业务字段 + * + * @param genTableColumn 业务字段信息 + * @return 结果 + */ + public int insertGenTableColumn(GenTableColumn genTableColumn); + + /** + * 修改业务字段 + * + * @param genTableColumn 业务字段信息 + * @return 结果 + */ + public int updateGenTableColumn(GenTableColumn genTableColumn); + + /** + * 删除业务字段信息 + * + * @param ids 需要删除的数据ID + * @return 结果 + */ + public int deleteGenTableColumnByIds(String ids); +} diff --git a/src/main/java/com/ruoyi/project/tool/gen/service/IGenTableService.java b/src/main/java/com/ruoyi/project/tool/gen/service/IGenTableService.java new file mode 100644 index 0000000..845e49e --- /dev/null +++ b/src/main/java/com/ruoyi/project/tool/gen/service/IGenTableService.java @@ -0,0 +1,130 @@ +package com.ruoyi.project.tool.gen.service; + +import java.util.List; +import java.util.Map; +import com.ruoyi.project.tool.gen.domain.GenTable; + +/** + * 业务 服务层 + * + * @author ruoyi + */ +public interface IGenTableService +{ + /** + * 查询业务列表 + * + * @param genTable 业务信息 + * @return 业务集合 + */ + public List selectGenTableList(GenTable genTable); + + /** + * 查询据库列表 + * + * @param genTable 业务信息 + * @return 数据库表集合 + */ + public List selectDbTableList(GenTable genTable); + + /** + * 查询据库列表 + * + * @param tableNames 表名称组 + * @return 数据库表集合 + */ + public List selectDbTableListByNames(String[] tableNames); + + /** + * 查询所有表信息 + * + * @return 表信息集合 + */ + public List selectGenTableAll(); + + /** + * 查询业务信息 + * + * @param id 业务ID + * @return 业务信息 + */ + public GenTable selectGenTableById(Long id); + + /** + * 修改业务 + * + * @param genTable 业务信息 + * @return 结果 + */ + public void updateGenTable(GenTable genTable); + + /** + * 删除业务信息 + * + * @param tableIds 需要删除的表数据ID + * @return 结果 + */ + public void deleteGenTableByIds(Long[] tableIds); + + /** + * 创建表 + * + * @param sql 创建表语句 + * @return 结果 + */ + public boolean createTable(String sql); + + /** + * 导入表结构 + * + * @param tableList 导入表列表 + * @param operName 操作人员 + */ + public void importGenTable(List tableList, String operName); + + /** + * 预览代码 + * + * @param tableId 表编号 + * @return 预览数据列表 + */ + public Map previewCode(Long tableId); + + /** + * 生成代码(下载方式) + * + * @param tableName 表名称 + * @return 数据 + */ + public byte[] downloadCode(String tableName); + + /** + * 生成代码(自定义路径) + * + * @param tableName 表名称 + * @return 数据 + */ + public void generatorCode(String tableName); + + /** + * 同步数据库 + * + * @param tableName 表名称 + */ + public void synchDb(String tableName); + + /** + * 批量生成代码(下载方式) + * + * @param tableNames 表数组 + * @return 数据 + */ + public byte[] downloadCode(String[] tableNames); + + /** + * 修改保存参数校验 + * + * @param genTable 业务信息 + */ + public void validateEdit(GenTable genTable); +} diff --git a/src/main/java/com/ruoyi/project/tool/gen/util/GenUtils.java b/src/main/java/com/ruoyi/project/tool/gen/util/GenUtils.java new file mode 100644 index 0000000..e107a9b --- /dev/null +++ b/src/main/java/com/ruoyi/project/tool/gen/util/GenUtils.java @@ -0,0 +1,257 @@ +package com.ruoyi.project.tool.gen.util; + +import java.util.Arrays; +import org.apache.commons.lang3.RegExUtils; +import com.ruoyi.common.constant.GenConstants; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.framework.config.GenConfig; +import com.ruoyi.project.tool.gen.domain.GenTable; +import com.ruoyi.project.tool.gen.domain.GenTableColumn; + +/** + * 代码生成器 工具类 + * + * @author ruoyi + */ +public class GenUtils +{ + /** + * 初始化表信息 + */ + public static void initTable(GenTable genTable, String operName) + { + genTable.setClassName(convertClassName(genTable.getTableName())); + genTable.setPackageName(GenConfig.getPackageName()); + genTable.setModuleName(getModuleName(GenConfig.getPackageName())); + genTable.setBusinessName(getBusinessName(genTable.getTableName())); + genTable.setFunctionName(replaceText(genTable.getTableComment())); + genTable.setFunctionAuthor(GenConfig.getAuthor()); + genTable.setCreateBy(operName); + } + + /** + * 初始化列属性字段 + */ + public static void initColumnField(GenTableColumn column, GenTable table) + { + String dataType = getDbType(column.getColumnType()); + String columnName = column.getColumnName(); + column.setTableId(table.getTableId()); + column.setCreateBy(table.getCreateBy()); + // 设置java字段名 + column.setJavaField(StringUtils.toCamelCase(columnName)); + // 设置默认类型 + column.setJavaType(GenConstants.TYPE_STRING); + column.setQueryType(GenConstants.QUERY_EQ); + + if (arraysContains(GenConstants.COLUMNTYPE_STR, dataType) || arraysContains(GenConstants.COLUMNTYPE_TEXT, dataType)) + { + // 字符串长度超过500设置为文本域 + Integer columnLength = getColumnLength(column.getColumnType()); + String htmlType = columnLength >= 500 || arraysContains(GenConstants.COLUMNTYPE_TEXT, dataType) ? GenConstants.HTML_TEXTAREA : GenConstants.HTML_INPUT; + column.setHtmlType(htmlType); + } + else if (arraysContains(GenConstants.COLUMNTYPE_TIME, dataType)) + { + column.setJavaType(GenConstants.TYPE_DATE); + column.setHtmlType(GenConstants.HTML_DATETIME); + } + else if (arraysContains(GenConstants.COLUMNTYPE_NUMBER, dataType)) + { + column.setHtmlType(GenConstants.HTML_INPUT); + + // 如果是浮点型 统一用BigDecimal + String[] str = StringUtils.split(StringUtils.substringBetween(column.getColumnType(), "(", ")"), ","); + if (str != null && str.length == 2 && Integer.parseInt(str[1]) > 0) + { + column.setJavaType(GenConstants.TYPE_BIGDECIMAL); + } + // 如果是整形 + else if (str != null && str.length == 1 && Integer.parseInt(str[0]) <= 10) + { + column.setJavaType(GenConstants.TYPE_INTEGER); + } + // 长整形 + else + { + column.setJavaType(GenConstants.TYPE_LONG); + } + } + + // 插入字段(默认所有字段都需要插入) + column.setIsInsert(GenConstants.REQUIRE); + + // 编辑字段 + if (!arraysContains(GenConstants.COLUMNNAME_NOT_EDIT, columnName) && !column.isPk()) + { + column.setIsEdit(GenConstants.REQUIRE); + } + // 列表字段 + if (!arraysContains(GenConstants.COLUMNNAME_NOT_LIST, columnName) && !column.isPk()) + { + column.setIsList(GenConstants.REQUIRE); + } + // 查询字段 + if (!arraysContains(GenConstants.COLUMNNAME_NOT_QUERY, columnName) && !column.isPk()) + { + column.setIsQuery(GenConstants.REQUIRE); + } + + // 查询字段类型 + if (StringUtils.endsWithIgnoreCase(columnName, "name")) + { + column.setQueryType(GenConstants.QUERY_LIKE); + } + // 状态字段设置单选框 + if (StringUtils.endsWithIgnoreCase(columnName, "status")) + { + column.setHtmlType(GenConstants.HTML_RADIO); + } + // 类型&性别字段设置下拉框 + else if (StringUtils.endsWithIgnoreCase(columnName, "type") + || StringUtils.endsWithIgnoreCase(columnName, "sex")) + { + column.setHtmlType(GenConstants.HTML_SELECT); + } + // 图片字段设置图片上传控件 + else if (StringUtils.endsWithIgnoreCase(columnName, "image")) + { + column.setHtmlType(GenConstants.HTML_IMAGE_UPLOAD); + } + // 文件字段设置文件上传控件 + else if (StringUtils.endsWithIgnoreCase(columnName, "file")) + { + column.setHtmlType(GenConstants.HTML_FILE_UPLOAD); + } + // 内容字段设置富文本控件 + else if (StringUtils.endsWithIgnoreCase(columnName, "content")) + { + column.setHtmlType(GenConstants.HTML_EDITOR); + } + } + + /** + * 校验数组是否包含指定值 + * + * @param arr 数组 + * @param targetValue 值 + * @return 是否包含 + */ + public static boolean arraysContains(String[] arr, String targetValue) + { + return Arrays.asList(arr).contains(targetValue); + } + + /** + * 获取模块名 + * + * @param packageName 包名 + * @return 模块名 + */ + public static String getModuleName(String packageName) + { + int lastIndex = packageName.lastIndexOf("."); + int nameLength = packageName.length(); + return StringUtils.substring(packageName, lastIndex + 1, nameLength); + } + + /** + * 获取业务名 + * + * @param tableName 表名 + * @return 业务名 + */ + public static String getBusinessName(String tableName) + { + int lastIndex = tableName.lastIndexOf("_"); + int nameLength = tableName.length(); + return StringUtils.substring(tableName, lastIndex + 1, nameLength); + } + + /** + * 表名转换成Java类名 + * + * @param tableName 表名称 + * @return 类名 + */ + public static String convertClassName(String tableName) + { + boolean autoRemovePre = GenConfig.getAutoRemovePre(); + String tablePrefix = GenConfig.getTablePrefix(); + if (autoRemovePre && StringUtils.isNotEmpty(tablePrefix)) + { + String[] searchList = StringUtils.split(tablePrefix, ","); + tableName = replaceFirst(tableName, searchList); + } + return StringUtils.convertToCamelCase(tableName); + } + + /** + * 批量替换前缀 + * + * @param replacementm 替换值 + * @param searchList 替换列表 + * @return + */ + public static String replaceFirst(String replacementm, String[] searchList) + { + String text = replacementm; + for (String searchString : searchList) + { + if (replacementm.startsWith(searchString)) + { + text = replacementm.replaceFirst(searchString, ""); + break; + } + } + return text; + } + + /** + * 关键字替换 + * + * @param text 需要被替换的名字 + * @return 替换后的名字 + */ + public static String replaceText(String text) + { + return RegExUtils.replaceAll(text, "(?:表|若依)", ""); + } + + /** + * 获取数据库类型字段 + * + * @param columnType 列类型 + * @return 截取后的列类型 + */ + public static String getDbType(String columnType) + { + if (StringUtils.indexOf(columnType, "(") > 0) + { + return StringUtils.substringBefore(columnType, "("); + } + else + { + return columnType; + } + } + + /** + * 获取字段长度 + * + * @param columnType 列类型 + * @return 截取后的列类型 + */ + public static Integer getColumnLength(String columnType) + { + if (StringUtils.indexOf(columnType, "(") > 0) + { + String length = StringUtils.substringBetween(columnType, "(", ")"); + return Integer.valueOf(length); + } + else + { + return 0; + } + } +} diff --git a/src/main/java/com/ruoyi/project/tool/gen/util/VelocityInitializer.java b/src/main/java/com/ruoyi/project/tool/gen/util/VelocityInitializer.java new file mode 100644 index 0000000..578b4fa --- /dev/null +++ b/src/main/java/com/ruoyi/project/tool/gen/util/VelocityInitializer.java @@ -0,0 +1,34 @@ +package com.ruoyi.project.tool.gen.util; + +import java.util.Properties; +import org.apache.velocity.app.Velocity; +import com.ruoyi.common.constant.Constants; + +/** + * VelocityEngine工厂 + * + * @author ruoyi + */ +public class VelocityInitializer +{ + /** + * 初始化vm方法 + */ + public static void initVelocity() + { + Properties p = new Properties(); + try + { + // 加载classpath目录下的vm文件 + p.setProperty("resource.loader.file.class", "org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader"); + // 定义字符集 + p.setProperty(Velocity.INPUT_ENCODING, Constants.UTF8); + // 初始化Velocity引擎,指定配置Properties + Velocity.init(p); + } + catch (Exception e) + { + throw new RuntimeException(e); + } + } +} diff --git a/src/main/java/com/ruoyi/project/tool/gen/util/VelocityUtils.java b/src/main/java/com/ruoyi/project/tool/gen/util/VelocityUtils.java new file mode 100644 index 0000000..9a632d7 --- /dev/null +++ b/src/main/java/com/ruoyi/project/tool/gen/util/VelocityUtils.java @@ -0,0 +1,408 @@ +package com.ruoyi.project.tool.gen.util; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import org.apache.velocity.VelocityContext; +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONObject; +import com.ruoyi.common.constant.GenConstants; +import com.ruoyi.common.utils.DateUtils; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.project.tool.gen.domain.GenTable; +import com.ruoyi.project.tool.gen.domain.GenTableColumn; + +/** + * 模板处理工具类 + * + * @author ruoyi + */ +public class VelocityUtils +{ + /** 项目空间路径 */ + private static final String PROJECT_PATH = "main/java"; + + /** mybatis空间路径 */ + private static final String MYBATIS_PATH = "main/resources/mybatis"; + + /** 默认上级菜单,系统工具 */ + private static final String DEFAULT_PARENT_MENU_ID = "3"; + + /** + * 设置模板变量信息 + * + * @return 模板列表 + */ + public static VelocityContext prepareContext(GenTable genTable) + { + String moduleName = genTable.getModuleName(); + String businessName = genTable.getBusinessName(); + String packageName = genTable.getPackageName(); + String tplCategory = genTable.getTplCategory(); + String functionName = genTable.getFunctionName(); + + VelocityContext velocityContext = new VelocityContext(); + velocityContext.put("tplCategory", genTable.getTplCategory()); + velocityContext.put("tableName", genTable.getTableName()); + velocityContext.put("functionName", StringUtils.isNotEmpty(functionName) ? functionName : "【请填写功能名称】"); + velocityContext.put("ClassName", genTable.getClassName()); + velocityContext.put("className", StringUtils.uncapitalize(genTable.getClassName())); + velocityContext.put("moduleName", genTable.getModuleName()); + velocityContext.put("BusinessName", StringUtils.capitalize(genTable.getBusinessName())); + velocityContext.put("businessName", genTable.getBusinessName()); + velocityContext.put("basePackage", getPackagePrefix(packageName)); + velocityContext.put("packageName", packageName); + velocityContext.put("author", genTable.getFunctionAuthor()); + velocityContext.put("datetime", DateUtils.getDate()); + velocityContext.put("pkColumn", genTable.getPkColumn()); + velocityContext.put("importList", getImportList(genTable)); + velocityContext.put("permissionPrefix", getPermissionPrefix(moduleName, businessName)); + velocityContext.put("columns", genTable.getColumns()); + velocityContext.put("table", genTable); + velocityContext.put("dicts", getDicts(genTable)); + setMenuVelocityContext(velocityContext, genTable); + if (GenConstants.TPL_TREE.equals(tplCategory)) + { + setTreeVelocityContext(velocityContext, genTable); + } + if (GenConstants.TPL_SUB.equals(tplCategory)) + { + setSubVelocityContext(velocityContext, genTable); + } + return velocityContext; + } + + public static void setMenuVelocityContext(VelocityContext context, GenTable genTable) + { + String options = genTable.getOptions(); + JSONObject paramsObj = JSON.parseObject(options); + String parentMenuId = getParentMenuId(paramsObj); + context.put("parentMenuId", parentMenuId); + } + + public static void setTreeVelocityContext(VelocityContext context, GenTable genTable) + { + String options = genTable.getOptions(); + JSONObject paramsObj = JSON.parseObject(options); + String treeCode = getTreecode(paramsObj); + String treeParentCode = getTreeParentCode(paramsObj); + String treeName = getTreeName(paramsObj); + + context.put("treeCode", treeCode); + context.put("treeParentCode", treeParentCode); + context.put("treeName", treeName); + context.put("expandColumn", getExpandColumn(genTable)); + if (paramsObj.containsKey(GenConstants.TREE_PARENT_CODE)) + { + context.put("tree_parent_code", paramsObj.getString(GenConstants.TREE_PARENT_CODE)); + } + if (paramsObj.containsKey(GenConstants.TREE_NAME)) + { + context.put("tree_name", paramsObj.getString(GenConstants.TREE_NAME)); + } + } + + public static void setSubVelocityContext(VelocityContext context, GenTable genTable) + { + GenTable subTable = genTable.getSubTable(); + String subTableName = genTable.getSubTableName(); + String subTableFkName = genTable.getSubTableFkName(); + String subClassName = genTable.getSubTable().getClassName(); + String subTableFkClassName = StringUtils.convertToCamelCase(subTableFkName); + + context.put("subTable", subTable); + context.put("subTableName", subTableName); + context.put("subTableFkName", subTableFkName); + context.put("subTableFkClassName", subTableFkClassName); + context.put("subTableFkclassName", StringUtils.uncapitalize(subTableFkClassName)); + context.put("subClassName", subClassName); + context.put("subclassName", StringUtils.uncapitalize(subClassName)); + context.put("subImportList", getImportList(genTable.getSubTable())); + } + + /** + * 获取模板信息 + * @param tplCategory 生成的模板 + * @param tplWebType 前端类型 + * @return 模板列表 + */ + public static List getTemplateList(String tplCategory, String tplWebType) + { + String useWebType = "vm/vue"; + if ("element-plus".equals(tplWebType)) + { + useWebType = "vm/vue/v3"; + } + List templates = new ArrayList(); + templates.add("vm/java/domain.java.vm"); + templates.add("vm/java/mapper.java.vm"); + templates.add("vm/java/service.java.vm"); + templates.add("vm/java/serviceImpl.java.vm"); + templates.add("vm/java/controller.java.vm"); + templates.add("vm/xml/mapper.xml.vm"); + templates.add("vm/sql/sql.vm"); + templates.add("vm/js/api.js.vm"); + if (GenConstants.TPL_CRUD.equals(tplCategory)) + { + templates.add(useWebType + "/index.vue.vm"); + } + else if (GenConstants.TPL_TREE.equals(tplCategory)) + { + templates.add(useWebType + "/index-tree.vue.vm"); + } + else if (GenConstants.TPL_SUB.equals(tplCategory)) + { + templates.add(useWebType + "/index.vue.vm"); + templates.add("vm/java/sub-domain.java.vm"); + } + return templates; + } + + /** + * 获取文件名 + */ + public static String getFileName(String template, GenTable genTable) + { + // 文件名称 + String fileName = ""; + // 包路径 + String packageName = genTable.getPackageName(); + // 模块名 + String moduleName = genTable.getModuleName(); + // 大写类名 + String className = genTable.getClassName(); + // 业务名称 + String businessName = genTable.getBusinessName(); + + String javaPath = PROJECT_PATH + "/" + StringUtils.replace(packageName, ".", "/"); + String mybatisPath = MYBATIS_PATH + "/" + moduleName; + String vuePath = "vue"; + + if (template.contains("domain.java.vm")) + { + fileName = StringUtils.format("{}/domain/{}.java", javaPath, className); + } + if (template.contains("sub-domain.java.vm") && StringUtils.equals(GenConstants.TPL_SUB, genTable.getTplCategory())) + { + fileName = StringUtils.format("{}/domain/{}.java", javaPath, genTable.getSubTable().getClassName()); + } + else if (template.contains("mapper.java.vm")) + { + fileName = StringUtils.format("{}/mapper/{}Mapper.java", javaPath, className); + } + else if (template.contains("service.java.vm")) + { + fileName = StringUtils.format("{}/service/I{}Service.java", javaPath, className); + } + else if (template.contains("serviceImpl.java.vm")) + { + fileName = StringUtils.format("{}/service/impl/{}ServiceImpl.java", javaPath, className); + } + else if (template.contains("controller.java.vm")) + { + fileName = StringUtils.format("{}/controller/{}Controller.java", javaPath, className); + } + else if (template.contains("mapper.xml.vm")) + { + fileName = StringUtils.format("{}/{}Mapper.xml", mybatisPath, className); + } + else if (template.contains("sql.vm")) + { + fileName = businessName + "Menu.sql"; + } + else if (template.contains("api.js.vm")) + { + fileName = StringUtils.format("{}/api/{}/{}.js", vuePath, moduleName, businessName); + } + else if (template.contains("index.vue.vm")) + { + fileName = StringUtils.format("{}/views/{}/{}/index.vue", vuePath, moduleName, businessName); + } + else if (template.contains("index-tree.vue.vm")) + { + fileName = StringUtils.format("{}/views/{}/{}/index.vue", vuePath, moduleName, businessName); + } + return fileName; + } + + /** + * 获取包前缀 + * + * @param packageName 包名称 + * @return 包前缀名称 + */ + public static String getPackagePrefix(String packageName) + { + int lastIndex = packageName.lastIndexOf("."); + return StringUtils.substring(packageName, 0, lastIndex); + } + + /** + * 根据列类型获取导入包 + * + * @param genTable 业务表对象 + * @return 返回需要导入的包列表 + */ + public static HashSet getImportList(GenTable genTable) + { + List columns = genTable.getColumns(); + GenTable subGenTable = genTable.getSubTable(); + HashSet importList = new HashSet(); + if (StringUtils.isNotNull(subGenTable)) + { + importList.add("java.util.List"); + } + for (GenTableColumn column : columns) + { + if (!column.isSuperColumn() && GenConstants.TYPE_DATE.equals(column.getJavaType())) + { + importList.add("java.util.Date"); + importList.add("com.fasterxml.jackson.annotation.JsonFormat"); + } + else if (!column.isSuperColumn() && GenConstants.TYPE_BIGDECIMAL.equals(column.getJavaType())) + { + importList.add("java.math.BigDecimal"); + } + } + return importList; + } + + /** + * 根据列类型获取字典组 + * + * @param genTable 业务表对象 + * @return 返回字典组 + */ + public static String getDicts(GenTable genTable) + { + List columns = genTable.getColumns(); + Set dicts = new HashSet(); + addDicts(dicts, columns); + if (StringUtils.isNotNull(genTable.getSubTable())) + { + List subColumns = genTable.getSubTable().getColumns(); + addDicts(dicts, subColumns); + } + return StringUtils.join(dicts, ", "); + } + + /** + * 添加字典列表 + * + * @param dicts 字典列表 + * @param columns 列集合 + */ + public static void addDicts(Set dicts, List columns) + { + for (GenTableColumn column : columns) + { + if (!column.isSuperColumn() && StringUtils.isNotEmpty(column.getDictType()) && StringUtils.equalsAny( + column.getHtmlType(), + new String[] { GenConstants.HTML_SELECT, GenConstants.HTML_RADIO, GenConstants.HTML_CHECKBOX })) + { + dicts.add("'" + column.getDictType() + "'"); + } + } + } + + /** + * 获取权限前缀 + * + * @param moduleName 模块名称 + * @param businessName 业务名称 + * @return 返回权限前缀 + */ + public static String getPermissionPrefix(String moduleName, String businessName) + { + return StringUtils.format("{}:{}", moduleName, businessName); + } + + /** + * 获取上级菜单ID字段 + * + * @param paramsObj 生成其他选项 + * @return 上级菜单ID字段 + */ + public static String getParentMenuId(JSONObject paramsObj) + { + if (StringUtils.isNotEmpty(paramsObj) && paramsObj.containsKey(GenConstants.PARENT_MENU_ID) + && StringUtils.isNotEmpty(paramsObj.getString(GenConstants.PARENT_MENU_ID))) + { + return paramsObj.getString(GenConstants.PARENT_MENU_ID); + } + return DEFAULT_PARENT_MENU_ID; + } + + /** + * 获取树编码 + * + * @param paramsObj 生成其他选项 + * @return 树编码 + */ + public static String getTreecode(JSONObject paramsObj) + { + if (paramsObj.containsKey(GenConstants.TREE_CODE)) + { + return StringUtils.toCamelCase(paramsObj.getString(GenConstants.TREE_CODE)); + } + return StringUtils.EMPTY; + } + + /** + * 获取树父编码 + * + * @param paramsObj 生成其他选项 + * @return 树父编码 + */ + public static String getTreeParentCode(JSONObject paramsObj) + { + if (paramsObj.containsKey(GenConstants.TREE_PARENT_CODE)) + { + return StringUtils.toCamelCase(paramsObj.getString(GenConstants.TREE_PARENT_CODE)); + } + return StringUtils.EMPTY; + } + + /** + * 获取树名称 + * + * @param paramsObj 生成其他选项 + * @return 树名称 + */ + public static String getTreeName(JSONObject paramsObj) + { + if (paramsObj.containsKey(GenConstants.TREE_NAME)) + { + return StringUtils.toCamelCase(paramsObj.getString(GenConstants.TREE_NAME)); + } + return StringUtils.EMPTY; + } + + /** + * 获取需要在哪一列上面显示展开按钮 + * + * @param genTable 业务表对象 + * @return 展开按钮列序号 + */ + public static int getExpandColumn(GenTable genTable) + { + String options = genTable.getOptions(); + JSONObject paramsObj = JSON.parseObject(options); + String treeName = paramsObj.getString(GenConstants.TREE_NAME); + int num = 0; + for (GenTableColumn column : genTable.getColumns()) + { + if (column.isList()) + { + num++; + String columnName = column.getColumnName(); + if (columnName.equals(treeName)) + { + break; + } + } + } + return num; + } +} diff --git a/src/main/java/com/ruoyi/project/tool/swagger/TestController.java b/src/main/java/com/ruoyi/project/tool/swagger/TestController.java new file mode 100644 index 0000000..3bd01cf --- /dev/null +++ b/src/main/java/com/ruoyi/project/tool/swagger/TestController.java @@ -0,0 +1,183 @@ +package com.ruoyi.project.tool.swagger; + +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +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.utils.StringUtils; +import com.ruoyi.framework.web.controller.BaseController; +import com.ruoyi.framework.web.domain.R; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiImplicitParams; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import io.swagger.annotations.ApiOperation; + +/** + * swagger 用户测试方法 + * + * @author ruoyi + */ +@Api("用户信息管理") +@RestController +@RequestMapping("/test/user") +public class TestController extends BaseController +{ + private final static Map users = new LinkedHashMap(); + { + users.put(1, new UserEntity(1, "admin", "admin123", "15888888888")); + users.put(2, new UserEntity(2, "ry", "admin123", "15666666666")); + } + + @ApiOperation("获取用户列表") + @GetMapping("/list") + public R> userList() + { + List userList = new ArrayList(users.values()); + return R.ok(userList); + } + + @ApiOperation("获取用户详细") + @ApiImplicitParam(name = "userId", value = "用户ID", required = true, dataType = "int", paramType = "path", dataTypeClass = Integer.class) + @GetMapping("/{userId}") + public R getUser(@PathVariable Integer userId) + { + if (!users.isEmpty() && users.containsKey(userId)) + { + return R.ok(users.get(userId)); + } + else + { + return R.fail("用户不存在"); + } + } + + @ApiOperation("新增用户") + @ApiImplicitParams({ + @ApiImplicitParam(name = "userId", value = "用户id", dataType = "Integer", dataTypeClass = Integer.class), + @ApiImplicitParam(name = "username", value = "用户名称", dataType = "String", dataTypeClass = String.class), + @ApiImplicitParam(name = "password", value = "用户密码", dataType = "String", dataTypeClass = String.class), + @ApiImplicitParam(name = "mobile", value = "用户手机", dataType = "String", dataTypeClass = String.class) + }) + @PostMapping("/save") + public R save(UserEntity user) + { + if (StringUtils.isNull(user) || StringUtils.isNull(user.getUserId())) + { + return R.fail("用户ID不能为空"); + } + users.put(user.getUserId(), user); + return R.ok(); + } + + @ApiOperation("更新用户") + @PutMapping("/update") + public R update(@RequestBody UserEntity user) + { + if (StringUtils.isNull(user) || StringUtils.isNull(user.getUserId())) + { + return R.fail("用户ID不能为空"); + } + if (users.isEmpty() || !users.containsKey(user.getUserId())) + { + return R.fail("用户不存在"); + } + users.remove(user.getUserId()); + users.put(user.getUserId(), user); + return R.ok(); + } + + @ApiOperation("删除用户信息") + @ApiImplicitParam(name = "userId", value = "用户ID", required = true, dataType = "int", paramType = "path", dataTypeClass = Integer.class) + @DeleteMapping("/{userId}") + public R delete(@PathVariable Integer userId) + { + if (!users.isEmpty() && users.containsKey(userId)) + { + users.remove(userId); + return R.ok(); + } + else + { + return R.fail("用户不存在"); + } + } +} + +@ApiModel(value = "UserEntity", description = "用户实体") +class UserEntity +{ + @ApiModelProperty("用户ID") + private Integer userId; + + @ApiModelProperty("用户名称") + private String username; + + @ApiModelProperty("用户密码") + private String password; + + @ApiModelProperty("用户手机") + private String mobile; + + public UserEntity() + { + + } + + public UserEntity(Integer userId, String username, String password, String mobile) + { + this.userId = userId; + this.username = username; + this.password = password; + this.mobile = mobile; + } + + public Integer getUserId() + { + return userId; + } + + public void setUserId(Integer userId) + { + this.userId = userId; + } + + public String getUsername() + { + return username; + } + + public void setUsername(String username) + { + this.username = username; + } + + public String getPassword() + { + return password; + } + + public void setPassword(String password) + { + this.password = password; + } + + public String getMobile() + { + return mobile; + } + + public void setMobile(String mobile) + { + this.mobile = mobile; + } +} diff --git a/src/main/resources/META-INF/spring-devtools.properties b/src/main/resources/META-INF/spring-devtools.properties new file mode 100644 index 0000000..37e7b58 --- /dev/null +++ b/src/main/resources/META-INF/spring-devtools.properties @@ -0,0 +1 @@ +restart.include.json=/com.alibaba.fastjson2.*.jar \ No newline at end of file diff --git a/src/main/resources/application-druid.yml b/src/main/resources/application-druid.yml new file mode 100644 index 0000000..99ff33b --- /dev/null +++ b/src/main/resources/application-druid.yml @@ -0,0 +1,61 @@ +# 数据源配置 +spring: + datasource: + type: com.alibaba.druid.pool.DruidDataSource + driverClassName: com.mysql.cj.jdbc.Driver + druid: + # 主库数据源 + master: + url: jdbc:mysql://192.168.50.18:3307/jgwebsite?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8 + username: root + password: jingang@mysql + # 从库数据源 + slave: + # 从数据源开关/默认关闭 + enabled: false + url: + username: + password: + # 初始连接数 + initialSize: 5 + # 最小连接池数量 + minIdle: 10 + # 最大连接池数量 + maxActive: 20 + # 配置获取连接等待超时的时间 + maxWait: 60000 + # 配置连接超时时间 + connectTimeout: 30000 + # 配置网络超时时间 + socketTimeout: 60000 + # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 + timeBetweenEvictionRunsMillis: 60000 + # 配置一个连接在池中最小生存的时间,单位是毫秒 + minEvictableIdleTimeMillis: 300000 + # 配置一个连接在池中最大生存的时间,单位是毫秒 + maxEvictableIdleTimeMillis: 900000 + # 配置检测连接是否有效 + validationQuery: SELECT 1 FROM DUAL + testWhileIdle: true + testOnBorrow: false + testOnReturn: false + webStatFilter: + enabled: true + statViewServlet: + enabled: true + # 设置白名单,不填则允许所有访问 + allow: + url-pattern: /druid/* + # 控制台管理用户名和密码 + login-username: jg + login-password: 123456 + filter: + stat: + enabled: true + # 慢SQL记录 + log-slow-sql: true + slow-sql-millis: 1000 + merge-sql: true + wall: + config: + multi-statement-allow: true \ No newline at end of file diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml new file mode 100644 index 0000000..e8d3f91 --- /dev/null +++ b/src/main/resources/application.yml @@ -0,0 +1,151 @@ +# 项目相关配置 +ruoyi: + # 名称 + name: RuoYi + # 版本 + version: 3.8.7 + # 版权年份 + copyrightYear: 2024 + # 文件路径 示例( Windows配置D:/ruoyi/uploadPath,Linux配置 /home/ruoyi/uploadPath) + profile: D:/JG/uploadPath + # 获取ip地址开关 + addressEnabled: false + # 验证码类型 math 数字计算 char 字符验证 + captchaType: math + +# 开发环境配置 +server: + # 服务器的HTTP端口,默认为8080 + port: 9999 + servlet: + # 应用的访问路径 + context-path: / + tomcat: + # tomcat的URI编码 + uri-encoding: UTF-8 + # 连接数满后的排队数,默认为100 + accept-count: 1000 + threads: + # tomcat最大线程数,默认为200 + max: 800 + # Tomcat启动初始化的线程数,默认值10 + min-spare: 100 + +# 日志配置 +logging: + level: + com.ruoyi: debug + org.springframework: warn + +# 用户配置 +user: + password: + # 密码最大错误次数 + maxRetryCount: 5 + # 密码锁定时间(默认10分钟) + lockTime: 10 + +# Spring配置 +spring: + # 资源信息 + messages: + # 国际化资源文件路径 + basename: i18n/messages + profiles: + active: druid + # 文件上传 + servlet: + multipart: + # 单个文件大小 + max-file-size: 10MB + # 设置总上传的文件大小 + max-request-size: 20MB + # 服务模块 + devtools: + restart: + # 热部署开关 + enabled: true + # redis 配置 + redis: + # 地址 + host: 192.168.50.18 + # 端口,默认为6379 + port: 6379 + # 数据库索引 + database: 0 + # 密码 + password: jingang@redis + # 连接超时时间 + timeout: 10s + lettuce: + pool: + # 连接池中的最小空闲连接 + min-idle: 0 + # 连接池中的最大空闲连接 + max-idle: 8 + # 连接池的最大数据库连接数 + max-active: 8 + # #连接池最大阻塞等待时间(使用负值表示没有限制) + max-wait: -1ms + +# token配置 +token: + # 令牌自定义标识 + header: Authorization + # 令牌密钥 + secret: abcdefghijklmnopqrstuvwxyz + # 令牌有效期(默认30分钟) + expireTime: 31536000 + +## MyBatis配置 +#mybatis: +# # 搜索指定包别名 +# typeAliasesPackage: com.ruoyi.project.**.domain +# # 配置mapper的扫描,找到所有的mapper.xml映射文件 +# mapperLocations: classpath*:mybatis/**/*Mapper.xml +# # 加载全局的配置文件 +# configLocation: classpath:mybatis/mybatis-config.xml +mybatis-plus: + mapper-locations: classpath*:mybatis/**/*Mapper.xml + type-aliases-package: com.ruoyi.**.domain + global-config: + db-config: + id-type: auto + configuration: + map-underscore-to-camel-case: true + cache-enabled: false + log-impl: org.apache.ibatis.logging.stdout.StdOutImpl + +# PageHelper分页插件 +pagehelper: + helperDialect: mysql + supportMethodsArguments: true + params: count=countSql + +# Swagger配置 +swagger: + # 是否开启swagger + enabled: true + # 请求前缀 +# pathMapping: /dev-api + pathMapping: + +# 防止XSS攻击 +xss: + # 过滤开关 + enabled: true + # 排除链接(多个用逗号分隔) + excludes: /system/notice + # 匹配链接 + urlPatterns: /system/*,/monitor/*,/tool/* + +# 代码生成 +gen: + # 作者 + author: ruoyi + # 默认生成包路径 system 需改成自己的模块名称 如 system monitor tool + packageName: com.ruoyi.project.system + # 自动去除表前缀,默认是true + autoRemovePre: false + # 表前缀(生成类名不会包含表前缀,多个用逗号分隔) + tablePrefix: sys_ \ No newline at end of file diff --git a/src/main/resources/banner.txt b/src/main/resources/banner.txt new file mode 100644 index 0000000..d6ca197 --- /dev/null +++ b/src/main/resources/banner.txt @@ -0,0 +1,18 @@ +Application Version: ${ruoyi.version} +Spring Boot Version: ${spring-boot.version} + # # # # # ########## + #### ## ## ## ## ## ## + #### # ## ## ## # ## ########## + ## ## ## ## ########## ## # ## ## + ## #### ###### ## ############# ########## # + ###### # ## ## ## # ## ## ############### + ## # ## ## ######## ## ## # # + #### ## ## ## # # ## ## ## ########### + ##### ## ## # ### ## ## ## ## ## ## ## + ### # # ##### ### # ## ## ## ########## + # ## #### #### #### ## ## ## ## ## + # ## ### ## # ## ## ## ## ########## + ## ## ## #### ## ## # ## # + ## ## ## ## ### ## ## ######## + ## ## ### ## ### ## #### ## # + # # # # # # # ############## \ No newline at end of file diff --git a/src/main/resources/i18n/messages.properties b/src/main/resources/i18n/messages.properties new file mode 100644 index 0000000..93de005 --- /dev/null +++ b/src/main/resources/i18n/messages.properties @@ -0,0 +1,38 @@ +#错误消息 +not.null=* 必须填写 +user.jcaptcha.error=验证码错误 +user.jcaptcha.expire=验证码已失效 +user.not.exists=用户不存在/密码错误 +user.password.not.match=用户不存在/密码错误 +user.password.retry.limit.count=密码输入错误{0}次 +user.password.retry.limit.exceed=密码输入错误{0}次,帐户锁定{1}分钟 +user.password.delete=对不起,您的账号已被删除 +user.blocked=用户已封禁,请联系管理员 +role.blocked=角色已封禁,请联系管理员 +login.blocked=很遗憾,访问IP已被列入系统黑名单 +user.logout.success=退出成功 + +length.not.valid=长度必须在{min}到{max}个字符之间 + +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=未知错误,请重新登录 + +##文件上传消息 +upload.exceed.maxSize=上传的文件大小超出限制的文件大小!
允许的文件最大大小是:{0}MB! +upload.filename.exceed.length=上传的文件名最长{0}个字符 + +##权限 +no.permission=您没有数据的权限,请联系管理员添加权限 [{0}] +no.create.permission=您没有创建数据的权限,请联系管理员添加权限 [{0}] +no.update.permission=您没有修改数据的权限,请联系管理员添加权限 [{0}] +no.delete.permission=您没有删除数据的权限,请联系管理员添加权限 [{0}] +no.export.permission=您没有导出数据的权限,请联系管理员添加权限 [{0}] +no.view.permission=您没有查看数据的权限,请联系管理员添加权限 [{0}] diff --git a/src/main/resources/logback.xml b/src/main/resources/logback.xml new file mode 100644 index 0000000..a360583 --- /dev/null +++ b/src/main/resources/logback.xml @@ -0,0 +1,93 @@ + + + + + + + + + + + ${log.pattern} + + + + + + ${log.path}/sys-info.log + + + + ${log.path}/sys-info.%d{yyyy-MM-dd}.log + + 60 + + + ${log.pattern} + + + + INFO + + ACCEPT + + DENY + + + + + ${log.path}/sys-error.log + + + + ${log.path}/sys-error.%d{yyyy-MM-dd}.log + + 60 + + + ${log.pattern} + + + + ERROR + + ACCEPT + + DENY + + + + + + ${log.path}/sys-user.log + + + ${log.path}/sys-user.%d{yyyy-MM-dd}.log + + 60 + + + ${log.pattern} + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/main/resources/mybatis/PartThree/AboutPartThreeMapper.xml b/src/main/resources/mybatis/PartThree/AboutPartThreeMapper.xml new file mode 100644 index 0000000..9756a1c --- /dev/null +++ b/src/main/resources/mybatis/PartThree/AboutPartThreeMapper.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/src/main/resources/mybatis/monitor/SysLogininforMapper.xml b/src/main/resources/mybatis/monitor/SysLogininforMapper.xml new file mode 100644 index 0000000..9e92f59 --- /dev/null +++ b/src/main/resources/mybatis/monitor/SysLogininforMapper.xml @@ -0,0 +1,57 @@ + + + + + + + + + + + + + + + + + + insert into sys_logininfor (user_name, status, ipaddr, login_location, browser, os, msg, login_time) + values (#{userName}, #{status}, #{ipaddr}, #{loginLocation}, #{browser}, #{os}, #{msg}, sysdate()) + + + + + + delete from sys_logininfor where info_id in + + #{infoId} + + + + + truncate table sys_logininfor + + + \ No newline at end of file diff --git a/src/main/resources/mybatis/monitor/SysOperLogMapper.xml b/src/main/resources/mybatis/monitor/SysOperLogMapper.xml new file mode 100644 index 0000000..dd5a44a --- /dev/null +++ b/src/main/resources/mybatis/monitor/SysOperLogMapper.xml @@ -0,0 +1,87 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + select oper_id, title, business_type, method, request_method, operator_type, oper_name, dept_name, oper_url, oper_ip, oper_location, oper_param, json_result, status, error_msg, oper_time, cost_time + from sys_oper_log + + + + insert into sys_oper_log(title, business_type, method, request_method, operator_type, oper_name, dept_name, oper_url, oper_ip, oper_location, oper_param, json_result, status, error_msg, cost_time, oper_time) + values (#{title}, #{businessType}, #{method}, #{requestMethod}, #{operatorType}, #{operName}, #{deptName}, #{operUrl}, #{operIp}, #{operLocation}, #{operParam}, #{jsonResult}, #{status}, #{errorMsg}, #{costTime}, sysdate()) + + + + + + delete from sys_oper_log where oper_id in + + #{operId} + + + + + + + truncate table sys_oper_log + + + \ No newline at end of file diff --git a/src/main/resources/mybatis/mybatis-config.xml b/src/main/resources/mybatis/mybatis-config.xml new file mode 100644 index 0000000..ac47c03 --- /dev/null +++ b/src/main/resources/mybatis/mybatis-config.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/mybatis/system/SysConfigMapper.xml b/src/main/resources/mybatis/system/SysConfigMapper.xml new file mode 100644 index 0000000..c586e2e --- /dev/null +++ b/src/main/resources/mybatis/system/SysConfigMapper.xml @@ -0,0 +1,117 @@ + + + + + + + + + + + + + + + + + + select config_id, config_name, config_key, config_value, config_type, create_by, create_time, update_by, update_time, remark + from sys_config + + + + + + + and config_id = #{configId} + + + and config_key = #{configKey} + + + + + + + + + + + + + + insert into sys_config ( + config_name, + config_key, + config_value, + config_type, + create_by, + remark, + create_time + )values( + #{configName}, + #{configKey}, + #{configValue}, + #{configType}, + #{createBy}, + #{remark}, + sysdate() + ) + + + + update sys_config + + config_name = #{configName}, + config_key = #{configKey}, + config_value = #{configValue}, + config_type = #{configType}, + update_by = #{updateBy}, + remark = #{remark}, + update_time = sysdate() + + where config_id = #{configId} + + + + delete from sys_config where config_id = #{configId} + + + + delete from sys_config where config_id in + + #{configId} + + + + \ No newline at end of file diff --git a/src/main/resources/mybatis/system/SysDeptMapper.xml b/src/main/resources/mybatis/system/SysDeptMapper.xml new file mode 100644 index 0000000..d361cdc --- /dev/null +++ b/src/main/resources/mybatis/system/SysDeptMapper.xml @@ -0,0 +1,159 @@ + + + + + + + + + + + + + + + + + + + + + + + + select d.dept_id, d.parent_id, d.ancestors, d.dept_name, d.order_num, d.leader, d.phone, d.email, d.status, d.del_flag, d.create_by, d.create_time + from sys_dept d + + + + + + + + + + + + + + + + + + + + insert into sys_dept( + dept_id, + parent_id, + dept_name, + ancestors, + order_num, + leader, + phone, + email, + status, + create_by, + create_time + )values( + #{deptId}, + #{parentId}, + #{deptName}, + #{ancestors}, + #{orderNum}, + #{leader}, + #{phone}, + #{email}, + #{status}, + #{createBy}, + sysdate() + ) + + + + update sys_dept + + parent_id = #{parentId}, + dept_name = #{deptName}, + ancestors = #{ancestors}, + order_num = #{orderNum}, + leader = #{leader}, + phone = #{phone}, + email = #{email}, + status = #{status}, + update_by = #{updateBy}, + update_time = sysdate() + + where dept_id = #{deptId} + + + + update sys_dept set ancestors = + + when #{item.deptId} then #{item.ancestors} + + where dept_id in + + #{item.deptId} + + + + + update sys_dept set status = '0' where dept_id in + + #{deptId} + + + + + update sys_dept set del_flag = '2' where dept_id = #{deptId} + + + \ No newline at end of file diff --git a/src/main/resources/mybatis/system/SysDictDataMapper.xml b/src/main/resources/mybatis/system/SysDictDataMapper.xml new file mode 100644 index 0000000..8221d6e --- /dev/null +++ b/src/main/resources/mybatis/system/SysDictDataMapper.xml @@ -0,0 +1,124 @@ + + + + + + + + + + + + + + + + + + + + + + select dict_code, dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, status, create_by, create_time, remark + from sys_dict_data + + + + + + + + + + + + + + delete from sys_dict_data where dict_code = #{dictCode} + + + + delete from sys_dict_data where dict_code in + + #{dictCode} + + + + + update sys_dict_data + + dict_sort = #{dictSort}, + dict_label = #{dictLabel}, + dict_value = #{dictValue}, + dict_type = #{dictType}, + css_class = #{cssClass}, + list_class = #{listClass}, + is_default = #{isDefault}, + status = #{status}, + remark = #{remark}, + update_by = #{updateBy}, + update_time = sysdate() + + where dict_code = #{dictCode} + + + + update sys_dict_data set dict_type = #{newDictType} where dict_type = #{oldDictType} + + + + insert into sys_dict_data( + dict_sort, + dict_label, + dict_value, + dict_type, + css_class, + list_class, + is_default, + status, + remark, + create_by, + create_time + )values( + #{dictSort}, + #{dictLabel}, + #{dictValue}, + #{dictType}, + #{cssClass}, + #{listClass}, + #{isDefault}, + #{status}, + #{remark}, + #{createBy}, + sysdate() + ) + + + \ No newline at end of file diff --git a/src/main/resources/mybatis/system/SysDictTypeMapper.xml b/src/main/resources/mybatis/system/SysDictTypeMapper.xml new file mode 100644 index 0000000..06498a9 --- /dev/null +++ b/src/main/resources/mybatis/system/SysDictTypeMapper.xml @@ -0,0 +1,105 @@ + + + + + + + + + + + + + + + + + select dict_id, dict_name, dict_type, status, create_by, create_time, remark + from sys_dict_type + + + + + + + + + + + + + + delete from sys_dict_type where dict_id = #{dictId} + + + + delete from sys_dict_type where dict_id in + + #{dictId} + + + + + update sys_dict_type + + dict_name = #{dictName}, + dict_type = #{dictType}, + status = #{status}, + remark = #{remark}, + update_by = #{updateBy}, + update_time = sysdate() + + where dict_id = #{dictId} + + + + insert into sys_dict_type( + dict_name, + dict_type, + status, + remark, + create_by, + create_time + )values( + #{dictName}, + #{dictType}, + #{status}, + #{remark}, + #{createBy}, + sysdate() + ) + + + \ No newline at end of file diff --git a/src/main/resources/mybatis/system/SysJobLogMapper.xml b/src/main/resources/mybatis/system/SysJobLogMapper.xml new file mode 100644 index 0000000..db17d91 --- /dev/null +++ b/src/main/resources/mybatis/system/SysJobLogMapper.xml @@ -0,0 +1,94 @@ + + + + + + + + + + + + + + + + + select job_log_id, job_name, job_group, invoke_target, job_message, status, exception_info, create_time + from sys_job_log + + + + + + + + + + delete from sys_job_log where job_log_id = #{jobLogId} + + + + delete from sys_job_log where job_log_id in + + #{jobLogId} + + + + + truncate table sys_job_log + + + + insert into sys_job_log( + job_log_id, + job_name, + job_group, + invoke_target, + job_message, + status, + exception_info, + create_time + )values( + #{jobLogId}, + #{jobName}, + #{jobGroup}, + #{invokeTarget}, + #{jobMessage}, + #{status}, + #{exceptionInfo}, + sysdate() + ) + + + \ No newline at end of file diff --git a/src/main/resources/mybatis/system/SysJobMapper.xml b/src/main/resources/mybatis/system/SysJobMapper.xml new file mode 100644 index 0000000..47f61c9 --- /dev/null +++ b/src/main/resources/mybatis/system/SysJobMapper.xml @@ -0,0 +1,111 @@ + + + + + + + + + + + + + + + + + + + + + + select job_id, job_name, job_group, invoke_target, cron_expression, misfire_policy, concurrent, status, create_by, create_time, remark + from sys_job + + + + + + + + + + delete from sys_job where job_id = #{jobId} + + + + delete from sys_job where job_id in + + #{jobId} + + + + + update sys_job + + job_name = #{jobName}, + job_group = #{jobGroup}, + invoke_target = #{invokeTarget}, + cron_expression = #{cronExpression}, + misfire_policy = #{misfirePolicy}, + concurrent = #{concurrent}, + status = #{status}, + remark = #{remark}, + update_by = #{updateBy}, + update_time = sysdate() + + where job_id = #{jobId} + + + + insert into sys_job( + job_id, + job_name, + job_group, + invoke_target, + cron_expression, + misfire_policy, + concurrent, + status, + remark, + create_by, + create_time + )values( + #{jobId}, + #{jobName}, + #{jobGroup}, + #{invokeTarget}, + #{cronExpression}, + #{misfirePolicy}, + #{concurrent}, + #{status}, + #{remark}, + #{createBy}, + sysdate() + ) + + + \ No newline at end of file diff --git a/src/main/resources/mybatis/system/SysMenuMapper.xml b/src/main/resources/mybatis/system/SysMenuMapper.xml new file mode 100644 index 0000000..a07f65f --- /dev/null +++ b/src/main/resources/mybatis/system/SysMenuMapper.xml @@ -0,0 +1,202 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + select menu_id, menu_name, parent_id, order_num, path, component, `query`, is_frame, is_cache, menu_type, visible, status, ifnull(perms,'') as perms, icon, create_time + from sys_menu + + + + + + + + + + + + + + + + + + + + + + + + + + update sys_menu + + menu_name = #{menuName}, + parent_id = #{parentId}, + order_num = #{orderNum}, + path = #{path}, + component = #{component}, + `query` = #{query}, + is_frame = #{isFrame}, + is_cache = #{isCache}, + menu_type = #{menuType}, + visible = #{visible}, + status = #{status}, + perms = #{perms}, + icon = #{icon}, + remark = #{remark}, + update_by = #{updateBy}, + update_time = sysdate() + + where menu_id = #{menuId} + + + + insert into sys_menu( + menu_id, + parent_id, + menu_name, + order_num, + path, + component, + `query`, + is_frame, + is_cache, + menu_type, + visible, + status, + perms, + icon, + remark, + create_by, + create_time + )values( + #{menuId}, + #{parentId}, + #{menuName}, + #{orderNum}, + #{path}, + #{component}, + #{query}, + #{isFrame}, + #{isCache}, + #{menuType}, + #{visible}, + #{status}, + #{perms}, + #{icon}, + #{remark}, + #{createBy}, + sysdate() + ) + + + + delete from sys_menu where menu_id = #{menuId} + + + \ No newline at end of file diff --git a/src/main/resources/mybatis/system/SysNoticeMapper.xml b/src/main/resources/mybatis/system/SysNoticeMapper.xml new file mode 100644 index 0000000..33c37a7 --- /dev/null +++ b/src/main/resources/mybatis/system/SysNoticeMapper.xml @@ -0,0 +1,89 @@ + + + + + + + + + + + + + + + + + + + select notice_id, notice_title, notice_type, cast(notice_content as char) as notice_content, status, create_by, create_time, update_by, update_time, remark + from sys_notice + + + + + + + + insert into sys_notice ( + notice_title, + notice_type, + notice_content, + status, + remark, + create_by, + create_time + )values( + #{noticeTitle}, + #{noticeType}, + #{noticeContent}, + #{status}, + #{remark}, + #{createBy}, + sysdate() + ) + + + + update sys_notice + + notice_title = #{noticeTitle}, + notice_type = #{noticeType}, + notice_content = #{noticeContent}, + status = #{status}, + update_by = #{updateBy}, + update_time = sysdate() + + where notice_id = #{noticeId} + + + + delete from sys_notice where notice_id = #{noticeId} + + + + delete from sys_notice where notice_id in + + #{noticeId} + + + + \ No newline at end of file diff --git a/src/main/resources/mybatis/system/SysPostMapper.xml b/src/main/resources/mybatis/system/SysPostMapper.xml new file mode 100644 index 0000000..23e7c14 --- /dev/null +++ b/src/main/resources/mybatis/system/SysPostMapper.xml @@ -0,0 +1,122 @@ + + + + + + + + + + + + + + + + + + + select post_id, post_code, post_name, post_sort, status, create_by, create_time, remark + from sys_post + + + + + + + + + + + + + + + + + + update sys_post + + post_code = #{postCode}, + post_name = #{postName}, + post_sort = #{postSort}, + status = #{status}, + remark = #{remark}, + update_by = #{updateBy}, + update_time = sysdate() + + where post_id = #{postId} + + + + insert into sys_post( + post_id, + post_code, + post_name, + post_sort, + status, + remark, + create_by, + create_time + )values( + #{postId}, + #{postCode}, + #{postName}, + #{postSort}, + #{status}, + #{remark}, + #{createBy}, + sysdate() + ) + + + + delete from sys_post where post_id = #{postId} + + + + delete from sys_post where post_id in + + #{postId} + + + + \ No newline at end of file diff --git a/src/main/resources/mybatis/system/SysRoleDeptMapper.xml b/src/main/resources/mybatis/system/SysRoleDeptMapper.xml new file mode 100644 index 0000000..23d2cb5 --- /dev/null +++ b/src/main/resources/mybatis/system/SysRoleDeptMapper.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + delete from sys_role_dept where role_id=#{roleId} + + + + + + delete from sys_role_dept where role_id in + + #{roleId} + + + + + insert into sys_role_dept(role_id, dept_id) values + + (#{item.roleId},#{item.deptId}) + + + + \ No newline at end of file diff --git a/src/main/resources/mybatis/system/SysRoleMapper.xml b/src/main/resources/mybatis/system/SysRoleMapper.xml new file mode 100644 index 0000000..adfa374 --- /dev/null +++ b/src/main/resources/mybatis/system/SysRoleMapper.xml @@ -0,0 +1,152 @@ + + + + + + + + + + + + + + + + + + + + + + + select distinct r.role_id, r.role_name, r.role_key, r.role_sort, r.data_scope, r.menu_check_strictly, r.dept_check_strictly, + r.status, r.del_flag, r.create_time, r.remark + from sys_role r + left join sys_user_role ur on ur.role_id = r.role_id + left join sys_user u on u.user_id = ur.user_id + left join sys_dept d on u.dept_id = d.dept_id + + + + + + + + + + + + + + + + + + + + insert into sys_role( + role_id, + role_name, + role_key, + role_sort, + data_scope, + menu_check_strictly, + dept_check_strictly, + status, + remark, + create_by, + create_time + )values( + #{roleId}, + #{roleName}, + #{roleKey}, + #{roleSort}, + #{dataScope}, + #{menuCheckStrictly}, + #{deptCheckStrictly}, + #{status}, + #{remark}, + #{createBy}, + sysdate() + ) + + + + update sys_role + + role_name = #{roleName}, + role_key = #{roleKey}, + role_sort = #{roleSort}, + data_scope = #{dataScope}, + menu_check_strictly = #{menuCheckStrictly}, + dept_check_strictly = #{deptCheckStrictly}, + status = #{status}, + remark = #{remark}, + update_by = #{updateBy}, + update_time = sysdate() + + where role_id = #{roleId} + + + + update sys_role set del_flag = '2' where role_id = #{roleId} + + + + update sys_role set del_flag = '2' where role_id in + + #{roleId} + + + + \ No newline at end of file diff --git a/src/main/resources/mybatis/system/SysRoleMenuMapper.xml b/src/main/resources/mybatis/system/SysRoleMenuMapper.xml new file mode 100644 index 0000000..bf32088 --- /dev/null +++ b/src/main/resources/mybatis/system/SysRoleMenuMapper.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + + + delete from sys_role_menu where role_id=#{roleId} + + + + delete from sys_role_menu where role_id in + + #{roleId} + + + + + insert into sys_role_menu(role_id, menu_id) values + + (#{item.roleId},#{item.menuId}) + + + + \ No newline at end of file diff --git a/src/main/resources/mybatis/system/SysUserMapper.xml b/src/main/resources/mybatis/system/SysUserMapper.xml new file mode 100644 index 0000000..f02de1b --- /dev/null +++ b/src/main/resources/mybatis/system/SysUserMapper.xml @@ -0,0 +1,221 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + select u.user_id, u.dept_id, u.user_name, u.nick_name, u.email, u.avatar, u.phonenumber, u.password, u.sex, u.status, u.del_flag, u.login_ip, u.login_date, u.create_by, u.create_time, u.remark, + d.dept_id, d.parent_id, d.ancestors, d.dept_name, d.order_num, d.leader, d.status as dept_status, + r.role_id, r.role_name, r.role_key, r.role_sort, r.data_scope, r.status as role_status + from sys_user u + left join sys_dept d on u.dept_id = d.dept_id + left join sys_user_role ur on u.user_id = ur.user_id + left join sys_role r on r.role_id = ur.role_id + + + + + + + + + + + + + + + + + + + + insert into sys_user( + user_id, + dept_id, + user_name, + nick_name, + email, + avatar, + phonenumber, + sex, + password, + status, + create_by, + remark, + create_time + )values( + #{userId}, + #{deptId}, + #{userName}, + #{nickName}, + #{email}, + #{avatar}, + #{phonenumber}, + #{sex}, + #{password}, + #{status}, + #{createBy}, + #{remark}, + sysdate() + ) + + + + update sys_user + + dept_id = #{deptId}, + user_name = #{userName}, + nick_name = #{nickName}, + email = #{email}, + phonenumber = #{phonenumber}, + sex = #{sex}, + avatar = #{avatar}, + password = #{password}, + status = #{status}, + login_ip = #{loginIp}, + login_date = #{loginDate}, + update_by = #{updateBy}, + remark = #{remark}, + update_time = sysdate() + + where user_id = #{userId} + + + + update sys_user set status = #{status} where user_id = #{userId} + + + + update sys_user set avatar = #{avatar} where user_name = #{userName} + + + + update sys_user set password = #{password} where user_name = #{userName} + + + + update sys_user set del_flag = '2' where user_id = #{userId} + + + + update sys_user set del_flag = '2' where user_id in + + #{userId} + + + + \ No newline at end of file diff --git a/src/main/resources/mybatis/system/SysUserPostMapper.xml b/src/main/resources/mybatis/system/SysUserPostMapper.xml new file mode 100644 index 0000000..d048078 --- /dev/null +++ b/src/main/resources/mybatis/system/SysUserPostMapper.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + delete from sys_user_post where user_id=#{userId} + + + + + + delete from sys_user_post where user_id in + + #{userId} + + + + + insert into sys_user_post(user_id, post_id) values + + (#{item.userId},#{item.postId}) + + + + \ No newline at end of file diff --git a/src/main/resources/mybatis/system/SysUserRoleMapper.xml b/src/main/resources/mybatis/system/SysUserRoleMapper.xml new file mode 100644 index 0000000..d7324eb --- /dev/null +++ b/src/main/resources/mybatis/system/SysUserRoleMapper.xml @@ -0,0 +1,44 @@ + + + + + + + + + + + delete from sys_user_role where user_id=#{userId} + + + + + + delete from sys_user_role where user_id in + + #{userId} + + + + + insert into sys_user_role(user_id, role_id) values + + (#{item.userId},#{item.roleId}) + + + + + delete from sys_user_role where user_id=#{userId} and role_id=#{roleId} + + + + delete from sys_user_role where role_id=#{roleId} and user_id in + + #{userId} + + + \ No newline at end of file diff --git a/src/main/resources/mybatis/tool/GenTableColumnMapper.xml b/src/main/resources/mybatis/tool/GenTableColumnMapper.xml new file mode 100644 index 0000000..450f89c --- /dev/null +++ b/src/main/resources/mybatis/tool/GenTableColumnMapper.xml @@ -0,0 +1,127 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + select column_id, table_id, column_name, column_comment, column_type, java_type, java_field, is_pk, is_increment, is_required, is_insert, is_edit, is_list, is_query, query_type, html_type, dict_type, sort, create_by, create_time, update_by, update_time from gen_table_column + + + + + + + + insert into gen_table_column ( + table_id, + column_name, + column_comment, + column_type, + java_type, + java_field, + is_pk, + is_increment, + is_required, + is_insert, + is_edit, + is_list, + is_query, + query_type, + html_type, + dict_type, + sort, + create_by, + create_time + )values( + #{tableId}, + #{columnName}, + #{columnComment}, + #{columnType}, + #{javaType}, + #{javaField}, + #{isPk}, + #{isIncrement}, + #{isRequired}, + #{isInsert}, + #{isEdit}, + #{isList}, + #{isQuery}, + #{queryType}, + #{htmlType}, + #{dictType}, + #{sort}, + #{createBy}, + sysdate() + ) + + + + update gen_table_column + + column_comment = #{columnComment}, + java_type = #{javaType}, + java_field = #{javaField}, + is_insert = #{isInsert}, + is_edit = #{isEdit}, + is_list = #{isList}, + is_query = #{isQuery}, + is_required = #{isRequired}, + query_type = #{queryType}, + html_type = #{htmlType}, + dict_type = #{dictType}, + sort = #{sort}, + update_by = #{updateBy}, + update_time = sysdate() + + where column_id = #{columnId} + + + + delete from gen_table_column where table_id in + + #{tableId} + + + + + delete from gen_table_column where column_id in + + #{item.columnId} + + + + \ No newline at end of file diff --git a/src/main/resources/mybatis/tool/GenTableMapper.xml b/src/main/resources/mybatis/tool/GenTableMapper.xml new file mode 100644 index 0000000..f26cc99 --- /dev/null +++ b/src/main/resources/mybatis/tool/GenTableMapper.xml @@ -0,0 +1,210 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + select table_id, table_name, table_comment, sub_table_name, sub_table_fk_name, class_name, tpl_category, tpl_web_type, package_name, module_name, business_name, function_name, function_author, gen_type, gen_path, options, create_by, create_time, update_by, update_time, remark from gen_table + + + + + + + + + + + + + + + + + + insert into gen_table ( + table_name, + table_comment, + class_name, + tpl_category, + tpl_web_type, + package_name, + module_name, + business_name, + function_name, + function_author, + gen_type, + gen_path, + remark, + create_by, + create_time + )values( + #{tableName}, + #{tableComment}, + #{className}, + #{tplCategory}, + #{tplWebType}, + #{packageName}, + #{moduleName}, + #{businessName}, + #{functionName}, + #{functionAuthor}, + #{genType}, + #{genPath}, + #{remark}, + #{createBy}, + sysdate() + ) + + + + ${sql} + + + + update gen_table + + table_name = #{tableName}, + table_comment = #{tableComment}, + sub_table_name = #{subTableName}, + sub_table_fk_name = #{subTableFkName}, + class_name = #{className}, + function_author = #{functionAuthor}, + gen_type = #{genType}, + gen_path = #{genPath}, + tpl_category = #{tplCategory}, + tpl_web_type = #{tplWebType}, + package_name = #{packageName}, + module_name = #{moduleName}, + business_name = #{businessName}, + function_name = #{functionName}, + options = #{options}, + update_by = #{updateBy}, + remark = #{remark}, + update_time = sysdate() + + where table_id = #{tableId} + + + + delete from gen_table where table_id in + + #{tableId} + + + + \ No newline at end of file diff --git a/src/main/resources/vm/java/controller.java.vm b/src/main/resources/vm/java/controller.java.vm new file mode 100644 index 0000000..6c9d936 --- /dev/null +++ b/src/main/resources/vm/java/controller.java.vm @@ -0,0 +1,84 @@ +package ${packageName}.controller; +import java.util.List; +import javax.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.springframework.security.access.prepost.PreAuthorize; + +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import ${packageName}.domain.${ClassName}; +import ${packageName}.service.I${ClassName}Service; +import com.ruoyi.framework.web.domain.R; +#if($table.crud || $table.sub) +#elseif($table.tree) +#end + +/** + * ${functionName}Controller + * + * @author ${author} + * @date ${datetime} + */ +@Slf4j +@Api(tags = "${functionName}") +@RestController +@RequestMapping("/${moduleName}") +public class ${ClassName}Controller { + @Resource + private I${ClassName}Service ${className}Service; + + /** + * ${functionName}列表 + */ + @PreAuthorize("@ss.hasPermi('${permissionPrefix}:list')") + @ApiOperation(value = "${functionName}列表", notes = "${functionName}列表信息") + @GetMapping("/list") + public R> list() { + List<${ClassName}> list = ${className}Service.list(); + return R.ok(list); + } + /** + * ${functionName}id详细信息 + */ + @PreAuthorize("@ss.hasPermi('${permissionPrefix}:query')") + @ApiOperation(value = "${functionName}id详细信息", notes = "${functionName}id详细信息") + @GetMapping("/query") + public R<${ClassName}> query(Long id) { + ${ClassName} queryinfo = ${className}Service.getById(id); + return R.ok(queryinfo); + } + /** + * ${functionName}新增信息 + */ + @PreAuthorize("@ss.hasPermi('${permissionPrefix}:add')") + @ApiOperation(value = "${functionName}新增信息", notes = "${functionName}新增信息") + @PostMapping("/add") + public R add(${ClassName} ${className}) { + boolean res = ${className}Service.save(${className}); + return R.ok(res); + } + /** + * ${functionName}修改信息 + */ + @PreAuthorize("@ss.hasPermi('${permissionPrefix}:edit')") + @ApiOperation(value = "${functionName}修改信息", notes = "${functionName}修改信息") + @PostMapping("/edit") + public R edit(${ClassName} ${className}) { + boolean res = ${className}Service.updateById(${className}); + return R.ok(res); + } + /** + * ${functionName}删除信息 + */ + @PreAuthorize("@ss.hasPermi('${permissionPrefix}:remove')") + @ApiOperation(value = "${functionName}删除信息", notes = "${functionName}删除信息") + @PostMapping("/remove") + public R remove(Long id) { + boolean res = ${className}Service.removeById(id); + return R.ok(res); + } +} diff --git a/src/main/resources/vm/java/domain.java.vm b/src/main/resources/vm/java/domain.java.vm new file mode 100644 index 0000000..f1df6c1 --- /dev/null +++ b/src/main/resources/vm/java/domain.java.vm @@ -0,0 +1,65 @@ +package ${packageName}.domain; + +import com.alibaba.fastjson2.annotation.JSONField; +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.databind.ser.std.ToStringSerializer; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + + + + +/** + * ${functionName}对象 ${tableName} + * + * @author ${author} + * @date ${datetime} + */ +@Data +@NoArgsConstructor +@AllArgsConstructor +@Builder +@ApiModel("${functionName}") +@TableName("${tableName}") +public class ${ClassName}{ +private static final long serialVersionUID=1L; + +#foreach ($column in $columns) + #if(!$table.isSuperColumn($column.javaField)) + /** $column.columnComment */ + #if($column.list) + #set($parentheseIndex=$column.columnComment.indexOf("(")) + #if($parentheseIndex != -1) + #set($comment=$column.columnComment.substring(0, $parentheseIndex)) + #else + #set($comment=$column.columnComment) + #end + #end + #if($column.isPk == 1) + @JSONField(serializeUsing = ToStringSerializer.class) + @ApiModelProperty("$column.columnComment") + @TableId(value = "$column.columnName" , type = IdType.ASSIGN_ID) + private $column.javaType $column.javaField; + #else + @ApiModelProperty("$column.columnComment") + @TableField("$column.columnName") + private $column.javaType $column.javaField; + #end + #else + #if($column.javaType == 'Date') + /** $column.columnComment */ + @ApiModelProperty("$column.columnComment") + @TableField("$column.columnName") + @JSONField(serializeUsing = ToStringSerializer.class) + private String $column.javaField; + #end + #end +#end +} \ No newline at end of file diff --git a/src/main/resources/vm/java/mapper.java.vm b/src/main/resources/vm/java/mapper.java.vm new file mode 100644 index 0000000..e861409 --- /dev/null +++ b/src/main/resources/vm/java/mapper.java.vm @@ -0,0 +1,14 @@ +package ${packageName}.mapper; + + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import ${packageName}.domain.${ClassName}; +/** + * ${functionName}Mapper接口 + * + * @author ${author} + * @date ${datetime} + */ +public interface ${ClassName}Mapper extends BaseMapper<${ClassName}> { + +} diff --git a/src/main/resources/vm/java/service.java.vm b/src/main/resources/vm/java/service.java.vm new file mode 100644 index 0000000..e06f8e4 --- /dev/null +++ b/src/main/resources/vm/java/service.java.vm @@ -0,0 +1,15 @@ +package ${packageName}.service; + + +import ${packageName}.domain.${ClassName}; +import com.baomidou.mybatisplus.extension.service.IService; + +/** + * ${functionName}Service接口 + * + * @author ${author} + * @date ${datetime} + */ +public interface I${ClassName}Service extends IService<${ClassName}> { + +} diff --git a/src/main/resources/vm/java/serviceImpl.java.vm b/src/main/resources/vm/java/serviceImpl.java.vm new file mode 100644 index 0000000..e13a05b --- /dev/null +++ b/src/main/resources/vm/java/serviceImpl.java.vm @@ -0,0 +1,17 @@ +package ${packageName}.service.impl; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import ${packageName}.mapper.${ClassName}Mapper; +import ${packageName}.domain.${ClassName}; +import ${packageName}.service.I${ClassName}Service; +import org.springframework.stereotype.Service; +/** + * ${functionName}Service业务层处理 + * + * @author ${author} + * @date ${datetime} + */ +@Service +public class ${ClassName}ServiceImpl extends ServiceImpl<${ClassName}Mapper, ${ClassName}> implements I${ClassName}Service { + +} diff --git a/src/main/resources/vm/java/sub-domain.java.vm b/src/main/resources/vm/java/sub-domain.java.vm new file mode 100644 index 0000000..bd6247d --- /dev/null +++ b/src/main/resources/vm/java/sub-domain.java.vm @@ -0,0 +1,73 @@ +package ${packageName}.domain; + + #foreach ($import in $subImportList) + import ${import}; + #end +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; +import com.rchuing.common.annotation.Excel; +import com.rchuing.common.core.domain.BaseEntity; + +/** + * ${subTable.functionName}对象 ${subTableName} + * + * @author ${author} + * @date ${datetime} + */ +public class ${subClassName} extends BaseEntity { +private static final long serialVersionUID = 1L; + +#foreach ($column in $subTable.columns) + #if(!$table.isSuperColumn($column.javaField)) + /** $column.columnComment */ + #if($column.list) + #set($parentheseIndex=$column.columnComment.indexOf("(")) + #if($parentheseIndex != -1) + #set($comment=$column.columnComment.substring(0, $parentheseIndex)) + #else + #set($comment=$column.columnComment) + #end + #if($parentheseIndex != -1) + @Excel(name = "${comment}", readConverterExp = "$column.readConverterExp()") + #elseif($column.javaType == 'Date') + @JsonFormat(pattern = "yyyy-MM-dd") + @Excel(name = "${comment}", width = 30, dateFormat = "yyyy-MM-dd") + #else + @Excel(name = "${comment}") + #end + #end + private $column.javaType $column.javaField; + + #end +#end +#foreach ($column in $subTable.columns) + #if(!$table.isSuperColumn($column.javaField)) + #if($column.javaField.length() > 2 && $column.javaField.substring(1,2).matches("[A-Z]")) + #set($AttrName=$column.javaField) + #else + #set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)}) + #end + public void set${AttrName}($column.javaType $column.javaField) { + this.$column.javaField = $column.javaField; + } + + public $column.javaType get${AttrName}() { + return $column.javaField; + } + #end +#end + +@Override +public String toString() { + return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) + #foreach ($column in $subTable.columns) + #if($column.javaField.length() > 2 && $column.javaField.substring(1,2).matches("[A-Z]")) + #set($AttrName=$column.javaField) + #else + #set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)}) + #end + .append("${column.javaField}", get${AttrName}()) + #end + .toString(); + } + } diff --git a/src/main/resources/vm/js/api.js.vm b/src/main/resources/vm/js/api.js.vm new file mode 100644 index 0000000..a1c6e32 --- /dev/null +++ b/src/main/resources/vm/js/api.js.vm @@ -0,0 +1,44 @@ +import request from '@/utils/request' + +// 查询${functionName}列表 +export function list${BusinessName}() { + return request({ + url: '/${moduleName}/list', + method: 'get', + }) +} + +// 查询${functionName}详细 +export function get${BusinessName}(${pkColumn.javaField}) { + return request({ + url: '/${moduleName}/id=' + ${pkColumn.javaField}, + method: 'get' + }) +} + +// 新增${functionName} +export function add${BusinessName}(data) { + return request({ + url: '/${moduleName}/add', + method: 'post', + data: data + }) +} + +// 修改${functionName} +export function update${BusinessName}(data) { + return request({ + url: '/${moduleName}/edit', + method: 'post', + data: data + }) +} + +// 删除${functionName} +export function del${BusinessName}(${pkColumn.javaField}) { + return request({ + url: '/${moduleName}/remove/', + method: 'post', + params: {id:${pkColumn.javaField}} + }) +} diff --git a/src/main/resources/vm/sql/sql.vm b/src/main/resources/vm/sql/sql.vm new file mode 100644 index 0000000..0575583 --- /dev/null +++ b/src/main/resources/vm/sql/sql.vm @@ -0,0 +1,22 @@ +-- 菜单 SQL +insert into sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) +values('${functionName}', '${parentMenuId}', '1', '${businessName}', '${moduleName}/${businessName}/index', 1, 0, 'C', '0', '0', '${permissionPrefix}:list', '#', 'admin', sysdate(), '', null, '${functionName}菜单'); + +-- 按钮父菜单ID +SELECT @parentId := LAST_INSERT_ID(); + +-- 按钮 SQL +insert into sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) +values('${functionName}查询', @parentId, '1', '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:query', '#', 'admin', sysdate(), '', null, ''); + +insert into sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) +values('${functionName}新增', @parentId, '2', '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:add', '#', 'admin', sysdate(), '', null, ''); + +insert into sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) +values('${functionName}修改', @parentId, '3', '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:edit', '#', 'admin', sysdate(), '', null, ''); + +insert into sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) +values('${functionName}删除', @parentId, '4', '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:remove', '#', 'admin', sysdate(), '', null, ''); + +insert into sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) +values('${functionName}导出', @parentId, '5', '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:export', '#', 'admin', sysdate(), '', null, ''); \ No newline at end of file diff --git a/src/main/resources/vm/vue/index-tree.vue.vm b/src/main/resources/vm/vue/index-tree.vue.vm new file mode 100644 index 0000000..4819c2a --- /dev/null +++ b/src/main/resources/vm/vue/index-tree.vue.vm @@ -0,0 +1,505 @@ + + + diff --git a/src/main/resources/vm/vue/index.vue.vm b/src/main/resources/vm/vue/index.vue.vm new file mode 100644 index 0000000..6296014 --- /dev/null +++ b/src/main/resources/vm/vue/index.vue.vm @@ -0,0 +1,602 @@ + + + diff --git a/src/main/resources/vm/vue/v3/index-tree.vue.vm b/src/main/resources/vm/vue/v3/index-tree.vue.vm new file mode 100644 index 0000000..c54d62b --- /dev/null +++ b/src/main/resources/vm/vue/v3/index-tree.vue.vm @@ -0,0 +1,474 @@ + + + diff --git a/src/main/resources/vm/vue/v3/index.vue.vm b/src/main/resources/vm/vue/v3/index.vue.vm new file mode 100644 index 0000000..8b25665 --- /dev/null +++ b/src/main/resources/vm/vue/v3/index.vue.vm @@ -0,0 +1,590 @@ + + + diff --git a/src/main/resources/vm/xml/mapper.xml.vm b/src/main/resources/vm/xml/mapper.xml.vm new file mode 100644 index 0000000..4916767 --- /dev/null +++ b/src/main/resources/vm/xml/mapper.xml.vm @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/target/classes/META-INF/spring-devtools.properties b/target/classes/META-INF/spring-devtools.properties new file mode 100644 index 0000000..37e7b58 --- /dev/null +++ b/target/classes/META-INF/spring-devtools.properties @@ -0,0 +1 @@ +restart.include.json=/com.alibaba.fastjson2.*.jar \ No newline at end of file diff --git a/target/classes/application-druid.yml b/target/classes/application-druid.yml new file mode 100644 index 0000000..99ff33b --- /dev/null +++ b/target/classes/application-druid.yml @@ -0,0 +1,61 @@ +# 数据源配置 +spring: + datasource: + type: com.alibaba.druid.pool.DruidDataSource + driverClassName: com.mysql.cj.jdbc.Driver + druid: + # 主库数据源 + master: + url: jdbc:mysql://192.168.50.18:3307/jgwebsite?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8 + username: root + password: jingang@mysql + # 从库数据源 + slave: + # 从数据源开关/默认关闭 + enabled: false + url: + username: + password: + # 初始连接数 + initialSize: 5 + # 最小连接池数量 + minIdle: 10 + # 最大连接池数量 + maxActive: 20 + # 配置获取连接等待超时的时间 + maxWait: 60000 + # 配置连接超时时间 + connectTimeout: 30000 + # 配置网络超时时间 + socketTimeout: 60000 + # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 + timeBetweenEvictionRunsMillis: 60000 + # 配置一个连接在池中最小生存的时间,单位是毫秒 + minEvictableIdleTimeMillis: 300000 + # 配置一个连接在池中最大生存的时间,单位是毫秒 + maxEvictableIdleTimeMillis: 900000 + # 配置检测连接是否有效 + validationQuery: SELECT 1 FROM DUAL + testWhileIdle: true + testOnBorrow: false + testOnReturn: false + webStatFilter: + enabled: true + statViewServlet: + enabled: true + # 设置白名单,不填则允许所有访问 + allow: + url-pattern: /druid/* + # 控制台管理用户名和密码 + login-username: jg + login-password: 123456 + filter: + stat: + enabled: true + # 慢SQL记录 + log-slow-sql: true + slow-sql-millis: 1000 + merge-sql: true + wall: + config: + multi-statement-allow: true \ No newline at end of file diff --git a/target/classes/application.yml b/target/classes/application.yml new file mode 100644 index 0000000..e8d3f91 --- /dev/null +++ b/target/classes/application.yml @@ -0,0 +1,151 @@ +# 项目相关配置 +ruoyi: + # 名称 + name: RuoYi + # 版本 + version: 3.8.7 + # 版权年份 + copyrightYear: 2024 + # 文件路径 示例( Windows配置D:/ruoyi/uploadPath,Linux配置 /home/ruoyi/uploadPath) + profile: D:/JG/uploadPath + # 获取ip地址开关 + addressEnabled: false + # 验证码类型 math 数字计算 char 字符验证 + captchaType: math + +# 开发环境配置 +server: + # 服务器的HTTP端口,默认为8080 + port: 9999 + servlet: + # 应用的访问路径 + context-path: / + tomcat: + # tomcat的URI编码 + uri-encoding: UTF-8 + # 连接数满后的排队数,默认为100 + accept-count: 1000 + threads: + # tomcat最大线程数,默认为200 + max: 800 + # Tomcat启动初始化的线程数,默认值10 + min-spare: 100 + +# 日志配置 +logging: + level: + com.ruoyi: debug + org.springframework: warn + +# 用户配置 +user: + password: + # 密码最大错误次数 + maxRetryCount: 5 + # 密码锁定时间(默认10分钟) + lockTime: 10 + +# Spring配置 +spring: + # 资源信息 + messages: + # 国际化资源文件路径 + basename: i18n/messages + profiles: + active: druid + # 文件上传 + servlet: + multipart: + # 单个文件大小 + max-file-size: 10MB + # 设置总上传的文件大小 + max-request-size: 20MB + # 服务模块 + devtools: + restart: + # 热部署开关 + enabled: true + # redis 配置 + redis: + # 地址 + host: 192.168.50.18 + # 端口,默认为6379 + port: 6379 + # 数据库索引 + database: 0 + # 密码 + password: jingang@redis + # 连接超时时间 + timeout: 10s + lettuce: + pool: + # 连接池中的最小空闲连接 + min-idle: 0 + # 连接池中的最大空闲连接 + max-idle: 8 + # 连接池的最大数据库连接数 + max-active: 8 + # #连接池最大阻塞等待时间(使用负值表示没有限制) + max-wait: -1ms + +# token配置 +token: + # 令牌自定义标识 + header: Authorization + # 令牌密钥 + secret: abcdefghijklmnopqrstuvwxyz + # 令牌有效期(默认30分钟) + expireTime: 31536000 + +## MyBatis配置 +#mybatis: +# # 搜索指定包别名 +# typeAliasesPackage: com.ruoyi.project.**.domain +# # 配置mapper的扫描,找到所有的mapper.xml映射文件 +# mapperLocations: classpath*:mybatis/**/*Mapper.xml +# # 加载全局的配置文件 +# configLocation: classpath:mybatis/mybatis-config.xml +mybatis-plus: + mapper-locations: classpath*:mybatis/**/*Mapper.xml + type-aliases-package: com.ruoyi.**.domain + global-config: + db-config: + id-type: auto + configuration: + map-underscore-to-camel-case: true + cache-enabled: false + log-impl: org.apache.ibatis.logging.stdout.StdOutImpl + +# PageHelper分页插件 +pagehelper: + helperDialect: mysql + supportMethodsArguments: true + params: count=countSql + +# Swagger配置 +swagger: + # 是否开启swagger + enabled: true + # 请求前缀 +# pathMapping: /dev-api + pathMapping: + +# 防止XSS攻击 +xss: + # 过滤开关 + enabled: true + # 排除链接(多个用逗号分隔) + excludes: /system/notice + # 匹配链接 + urlPatterns: /system/*,/monitor/*,/tool/* + +# 代码生成 +gen: + # 作者 + author: ruoyi + # 默认生成包路径 system 需改成自己的模块名称 如 system monitor tool + packageName: com.ruoyi.project.system + # 自动去除表前缀,默认是true + autoRemovePre: false + # 表前缀(生成类名不会包含表前缀,多个用逗号分隔) + tablePrefix: sys_ \ No newline at end of file diff --git a/target/classes/banner.txt b/target/classes/banner.txt new file mode 100644 index 0000000..d6ca197 --- /dev/null +++ b/target/classes/banner.txt @@ -0,0 +1,18 @@ +Application Version: ${ruoyi.version} +Spring Boot Version: ${spring-boot.version} + # # # # # ########## + #### ## ## ## ## ## ## + #### # ## ## ## # ## ########## + ## ## ## ## ########## ## # ## ## + ## #### ###### ## ############# ########## # + ###### # ## ## ## # ## ## ############### + ## # ## ## ######## ## ## # # + #### ## ## ## # # ## ## ## ########### + ##### ## ## # ### ## ## ## ## ## ## ## + ### # # ##### ### # ## ## ## ########## + # ## #### #### #### ## ## ## ## ## + # ## ### ## # ## ## ## ## ########## + ## ## ## #### ## ## # ## # + ## ## ## ## ### ## ## ######## + ## ## ### ## ### ## #### ## # + # # # # # # # ############## \ No newline at end of file diff --git a/target/classes/com/ruoyi/RuoYiApplication.class b/target/classes/com/ruoyi/RuoYiApplication.class new file mode 100644 index 0000000000000000000000000000000000000000..a5f75526db045a8227e58f256356e7b9e8c7fce3 GIT binary patch literal 4393 zcmbVP-FDPe6#mY1+RUV(;FJPIkSPk3LK}Zjaaz$*K+pkd3suCwOeUSCWOBwN(-yDf z&RcJI4|S~qx|R>%WAFmLfy;f8NpdC=gvHR6v$KD`{q21YS^NC`?;n2wIENh-hfz~7 zuVMioj^YA7Qt>es#p7ZLOQX1i%Vk`_Cna1}aSflA@L3rJ)D>J;F@{$A-R)7V;R_XC;*Nr^7=|3)VyM))*V27w{oJNr=dG6I%`=QF*sdL1AfM^e zYYfFD-n1C@)@|3i(QP*@@0QVUNJGJ<;j9^+E#7;|Vz6QR3`goFZ|h!{-?#OZF27?h zb~=u28iCDSN>OYZw##r}`fh#G*fMm-a9jFn;Ms0#KFn!&EkYU3F*A%BDHg**R6<9@ zu6st?+UDLp-Q;dy?F9M~ch~J!*AomS3ZxchVlrg8TMRSL0P!rH?D}PQ%l5e2wpM&(?{t-Zl{ zpc~zQQzyd?TKa0(8V&X#Ulk3hmBwS?^BfP`LT7sTW|q1+c5 zI}9pgNZFDpbkHSjR2r6_bTBq;&!8>TB-sc07A1>{C%NYscU88^CZW%6Lgbr1CGrt` zlH})PFS3R>Z<(r)#Ul6?qGPG7C6^eTQy#(qI$lC6Dm z;wptlKr-31_WV3Dq=c9U7BsCs*9#+`F{2GO5LxiWk-yp8{7|S)pB_w2s$-g zPjwnP_<>1`q2Bd<_A(g06-wY}0_5_WWjJ(=urWJsPT9E6{Jd_gud1UZtiab16-{;ozT{ zn!}+l8uO%f(~wNvW@t=HGw+gP`2Kv(e11B22BazS#pdx^9tlYNC|rE-LI)hj>-1K}8#qBABKRL#hiQFKDa<^^Q2opk6dA5R z#_%piZk(+sPf#kLR{Ra+in@zYv1;ME=TD5?d4#={eY<#NvPh3gC6UHSniZ=3iE3g{ zb%0d6IQRsI3P_0`M~MhcLZN!csNxB#`565>g_Bh4G=8F|DC%59oV2ItO~H?3J57a- z!N&~FP#Y%T;4EgtQX7~fsYtP)C&Qa~i=<&Xo3xbhcGAwNuvgTjO5rgkf5#rdjJ~4u ej9NNQ$nVfwff&3?YlWouXnmi&&ff(y@xgzByxgMz literal 0 HcmV?d00001 diff --git a/target/classes/com/ruoyi/RuoYiServletInitializer.class b/target/classes/com/ruoyi/RuoYiServletInitializer.class new file mode 100644 index 0000000000000000000000000000000000000000..efdae1b836729693883e191406edfdfad574206e GIT binary patch literal 910 zcmbu7T}vB56o%hPHd#$ljj>v_wj$m{1?R>ai3mmzgw;w@1nJf6W{jhoon>~{uU}Pf z6a@VN{ZYlUn}8)Y^dfsPXU^9%ocHYSKTj_JHnCJf5i>SsZG0+W4)Zn^YIs9jr`wWY(oumtO1iEzKZNU3q~>T(_$R`s)b9ta_*_K{D~_kT+$8!|@$ZQ~ zRV`_*d>J1QSqWd=Nz-5$_jGWkI*B3LP2JI^!=uospmSzKS6*v#$z82u?k37_OT$}P zayJZp<%wA7pxH|_R!8V%D2q2DLt`XX^-#v=x_vCt31VqTi&9G`#*_Oh6>V{d8XFhl zN>pHDjHFKQ+!sNI@A)Dk#KM4@e@S3tv4Su7%CJ08^j>m?gP|H&}iT4Kb?VjM_MPAQJ^0?{c@m(Fh=J%d7LaGTUdJpyU)@k zI*VDbj56ITy#lfy$g?nkNrI%!O@ijgPOMwc$ereE`PKuhUlhsVjZ)1hv%Shbg?doM R6p?FGU0~#ZoawCf<170G1pWX3 literal 0 HcmV?d00001 diff --git a/target/classes/com/ruoyi/common/constant/CacheConstants.class b/target/classes/com/ruoyi/common/constant/CacheConstants.class new file mode 100644 index 0000000000000000000000000000000000000000..c1e8c940b3e717869ebc60c2152eaec121c132d7 GIT binary patch literal 718 zcma))%Wl&^6o&svlO|2lTgv@juB8jSVO0=PaNH8rBvEXFx*AQKA(`TgBYOhsW3fVF z!2|G6h%-q5vtx5S-#6Fu%Rhd8`wrkao>Z}h+hyD-<1WLt-#uw}c-TGlI{ef-V`%v2 z;*)S{l*}Uqwgv$&E|9d9o_c!M5@LWcQQjU|c z(%d8;rOu8hk4Vf+G!Z*>xd{Tn$GAA zOsgAnTE*!*-;^i=lTou7y(JMd)j7?wRg=-%5{>qPdxPIWU5c(dRn=V5EK4&gjNX-K z%&NiSD5WJ0hW8L;8oKI~R9#zAyNTYH=)hpmz=LHKm8xBwR~+?nRWlVEIVIXJbV~~o zujxftY}z&!RZMV8qFh&Xs-{}jE;IT7Q}*htStw*ttuy)%wJ)sL4!39}Mjv6+9j+)# zDx;5gBa~&uRv1}7-zalKYe^mA@7_)ar zj&qx8nuggMnYrW#e)23%#mQ64*zjVw=Al^K54=jJz3N4_yV`;!-#tFdZsc>`HKvjc zKbGi(-U!=;s1x4w3((qO0MR?7g*&G;#$K>Xjn?jLqgL378eZAwx%c#gWPac!n)JiM zW*i1P-f9wVc|oDra{YFkqh(sj($_iqhK}Uuemat+Z*%k=eV?NX^t411ci$IuCXAYL zyv;AqdgQjfoiN&xXar@--GXyuk|oi>zCrGX$%z|p<;dp#9M3EVULw~_U7{(DH{`%= zBd@HXgTC9sDiZHTf?n}CZ@TT490%@}$2Dv;y-;dR(T5C6FfO*@aCj;c6MaE0vk!P zJyR%P)3_FQRq3om(mDP_bQ%pW#?EK-uo-qRoS9zJj}tFKCN+veE~jVkgLK!>C5$`` zy}O;rFzdOI7ahgkx5B0$IGxyw9JC54x$3XsK*pU_AR3*xNSAFyo||}ZjJcg;BaF~< z4<)by+R*D;Dr);Pn54DlZbN3cjG^?7h42Vsi!5!as%vFUEjh(`t)g10MEeHEXZHcY zwAcAFYpkR3C$&?;ExYr?&@sH+llU(E(GVSmoD%pxFpeyIhv)&p&c_`S z_(AY-fgb{&5cpy6BLbfUpAvW){HVaE!Dj@P!3BZOg3k&382E94&x4;3_(||ncQI~X zcU?1r{UYKn3H%KBS%IGeKQFKXo)x$VE(xrH%L31V=LOcl3j!~Kb%B?_6@d+KRp1xE zcniAgz@6_(+$qQ*>?vLzd@q9vkP>8i`ZpT-rR%Qao9PD!b^|4G^UaGqK9RDQ&|O{1xvVGMe1g7sjWd-quNbbhZ@&+y=E ze!{A={64Ebk;&)r&F1%G!~tHEM7*d;O5=MN#a*Lu`Vpt^Cz__8;e8qQA#(l&mk`uG literal 0 HcmV?d00001 diff --git a/target/classes/com/ruoyi/common/constant/GenConstants.class b/target/classes/com/ruoyi/common/constant/GenConstants.class new file mode 100644 index 0000000000000000000000000000000000000000..82a4183eb01728ebdfe493508146db305f006458 GIT binary patch literal 2753 zcma)-_m>k@6vyxEW|O-iz!I<^Hb4;&SwIvOL{o-kWRmPA6C|R>Niv%qNG9uSf*|(Z zJNDkYAS#N2z2WcvIUc`vCOHhK=lILK`?>Aici(-}QM|(RD${EszRvW9tZy>C#q>7QJ52A2;ytGKnLc3pkm)0) zk45)Mg!V>gpF(Vm>g80vm{usRZ!>q8!wu7^4;R|5?bO#QG(FXFrrM^{-fT8H7SpHQ z5F@XuO#2mzNPFsSoAzk+8frx!C>qcp0P3r1d{*<1guS_u;-zenA4c#g{>l7pGKmqj zTpZPN33T*ZbktLC(Db9)?|K``L34Sg@1^8!bG7UU(+>*8JcX7u)DaafKg#uqi=GpQ zpL!ALTrsKRK7U5DJ$q6cNvkO>lhB!d=>Ucbt^wh8Y%a5KN{3noiJS-V~6Sb5<5r5uCyvfBeW6wwQXlt+uGSin7>4uR?Y4-ec(pRsf!pR z&2z7rZL4iJQBkP7hz1Q7!b5FN#^?(1j7VbJL0+C}nhhKZ;-H_~ll#4I*zuYOf1p*j zP-SUoNRkv4KJ8=oPp}JL^#N8oVbR0w6gD~a`M@PK>E04R^dUqevF>kP> zyLTN;+V!-5-8fd-8?}%;Tim-krjW9d=Hu^VHU9n<(p%f-=cs=;C0ACJH1ULy!1vm{>1H1u@CxyM|%3JYPyaaE>pn5AN&|5KT-ijgfR?LmJ zVgS5#ERZ*ke1!lT;9`I`gRczm7H}!RSAn+%csls%0AB;XHo#@@bpbZPl>k@4wE$b- z@c`Gs69Klt+XB2D+z4;$+4o(%ByU^l>1xLZYj`|#a?ZVh8qunsIeK*~M{eRKo9 zVNc1l6LQRVpj~t$h-r5(SlLY>Xh=y#4pCpJKi*#`h2o(?X&^pOD23zU!hVW$O)REC zF-65R4W?MvG+j(H#57Y(Lom%6n&xMWi*2^p=7{ZtLMakIu~6dpNrlo-IDRs|k@#GE zIerShgYkLzM&qX<$7$X4r%Q@6B*mGMVm?d@hQfaOf7pesd+}2Lq%}$(tyLD#dU)RizdpM8Zwcm5NdN!< literal 0 HcmV?d00001 diff --git a/target/classes/com/ruoyi/common/constant/HttpStatus.class b/target/classes/com/ruoyi/common/constant/HttpStatus.class new file mode 100644 index 0000000000000000000000000000000000000000..3e48329ecfa405ed68b54496fedc1c0a9fedbca9 GIT binary patch literal 897 zcma*lOK;Oa5C`ztJeoF5Nh#0rE|k(1S{LXIgp}GF)l%#=egu_^F}Fy3Re1 z5i=Pr=vg|>qcrbEgGpRQ{&%G4E^qnVko>`DwuJ)c*hh@&wg`1$``q?r#!o~n(d9YX2b>uPYk93&e z=lM%7kMha*0j}j`lTklzC$eMn->2=v=vCC9b=qLG`aivJe_5gpqV%~YdWUg8rxhyU zZ$WgDmZ?Hj!rh<})!)oA8?ATbKlsv&FiJiD-q1s+(VkeJ)0H8|w_hghAcG R+9=+uv_;zxLx~*Z&R>4YkFWp$ literal 0 HcmV?d00001 diff --git a/target/classes/com/ruoyi/common/constant/ScheduleConstants$Status.class b/target/classes/com/ruoyi/common/constant/ScheduleConstants$Status.class new file mode 100644 index 0000000000000000000000000000000000000000..fdb1cc2921ff548b39f2137d466c775789fb4f2c GIT binary patch literal 1527 zcmbVMZBNrs6n^g3ty?K$PMEwZ;?!*j1stL>VRL~%GG0Pu3Gq|u>L}^jr0Yoh=1)>k zF&Yh@{ZYnq+a(U+3v1Gzd+vSibDneVx&8g;`!4_mRN@dYCt)57B8Ej2Bn)7Yf1dG` zr5KViJeO$rg8MIH7>r?=4~rsRF+{g__O{AZ8mOM;wOb7GrfnN;#nM_WgWN(@cbbaZ zcHWx`jhc>4#%_6uH8PmCH{w;Y?qT`tYe;jy86l)&^2q)_{JAAx`M zmb)t1xi8&Ez%@{mboagvY%^CY&C~rJp#OtV8sT{WWGM}r3Gpm}AJx zkvBz_Khi1KSA;GQ{zx9fIDMo3+8E&{aF=*EzCf1mkWxba8zL7FI6iaTWt^=vHt~svGpE0~&z%1H{o^NqH#q2G4i7av(y*;zhp;G0Sylz1=cK91twa`u zVq6JK@hP-iQ;5(rPi#Wpy_8?1k;wGi5M>^x=WhwiR+bi}Ov|xMDy2hfB2P#DsXYoD zTj*F~<%uJP&d3f2_R#$720C^L8;#OA_I$QoAxAtWY&OO^%=88!r?hN)pkt4lX{-Z3 z^!(tX;~neR=W*V}X-r0Lsao9RL6T literal 0 HcmV?d00001 diff --git a/target/classes/com/ruoyi/common/constant/UserConstants.class b/target/classes/com/ruoyi/common/constant/UserConstants.class new file mode 100644 index 0000000000000000000000000000000000000000..0eaa572b08b44bc26389924db0b64461c48a46b4 GIT binary patch literal 1159 zcma)+TT|0O6vxlf0!v#20Y$vyjf+ab`vt|&2F;jmYA%KPk_k0~!II3Rtqvc{Cue-{ z1NfmF|C?f2c=F*S`#b-0PR{Px_V=Hkzli7&RSJ}&r=#?2l%6xKnqwz`-ce+8`Z3OLJ$SKcgL|ArBGdaCrG^X!V^tx+Uf)k=kvXWml z9BtFofwpDp$5^Z))bD3Q%xDl>tQwVLG1t@`PV4vp7XFs4)liNLYp`^iby?Zw^a?>4 z)pX%;dd;Yq;il)hvaJntY-&x*b2+_%Sl4V_xPHUXw>fEy3WgA9+BC#_PMZk!gz>?{ z=h(*_4vZl}SgzmqN)IubmirWF%C8y1H+AvWeTM`?%0nk)ZRbdq(hQ`!<~ZAyT~#ur zG<-rDWTBn)D2}?=Rc>*q!6;uzK81`X%qR{;?_f7f-C(y3&y<<8f_5WFBgse3eD`zI zVYFnnl7n*EOAezlRtHIpSAVQ!uM?*KX57ID3r^BYTj5re6+NM@$oo;E6)H1Y_|J-r zCXa8GwYwj-x;Us+*cz+}tHCO;Ejg#)FzF$nP!G}&cPh3f}|YS9lqj sOb#B;Rh+ndGkI&5e-QhgjbIJWk<7{I0oJ8{06oNCj{M0y&Z3_D1-W9qQ~&?~ literal 0 HcmV?d00001 diff --git a/target/classes/com/ruoyi/common/core/text/CharsetKit.class b/target/classes/com/ruoyi/common/core/text/CharsetKit.class new file mode 100644 index 0000000000000000000000000000000000000000..fa3c231cc8e74624f5f2e108c89b070c3d8bb9fd GIT binary patch literal 1891 zcma)6ZBrXn6n<{rHVGTRNRgBZwDu)$5TmUILE0ulc?nRPfVTa>EXfwOCcAZa(~;ld zr}_i*Yt+QkId5f^VW&jWKsEiaXM-N0E|tL&2s%e51B| zP%7P6JSYkztBY&Gv{w$|2N-KG?!VVO^L+ZEUxLNG`< z81Y5C-IIu8b?gSq5O?f`Us{2YrtY{s@4-6>hxO&%R~;%K;hZ?G-tLi2q=%+Omm4j= zpYfU}dCidB8}kpc3k!1ORLo#jK~=>zc34XlySS_3TU=F`S2URD2Ik^OgTK1>IL*UcLhJMHS~U-5v4yl8OtG?PD3~W7|D^tT))uDNn$V zS_)hmVD7j^%kkf-J-O#-7|v?@iF+b&Zb+vx4+KV=`XjB=dehPiud6(SRZjCG%iiJW z30xe|#%VR1n%(pZPyUGhV@H!h-lFf`Zb{Ndk94hqKI*c#@hWLG@P#&}4Shgl-c(JRmj z+Hm#-#P8e$NGa)LP{k#0^%C$2F4K$Q3a&D1j{6P&a58&_z%#_o5c~}aozQbW4fY{j z?n6ovVUo|U@vk6|;3qeY44p7XkS4|n&iM>mL+hIyMV`$2r>bD1O~fY1)u9@ggl6%$o&fC4@9aW?`@QAdB0yUlFOeW`i!-D z{x@Rxv$<2m^Di*+=CU)q5U_$UZt)rwc4-w!tg{fCEMK)xXR)WVNHNo1xr>xEO);`C z5rksVQ88TerzrBk9MfbQWF2G$Y9XT4r8`pK!}C=2-~#**Wn{UDk2 b?1!@d-Q%NPB_?@%3ga9kfzSCYfa|XSY)6y2 literal 0 HcmV?d00001 diff --git a/target/classes/com/ruoyi/common/core/text/Convert.class b/target/classes/com/ruoyi/common/core/text/Convert.class new file mode 100644 index 0000000000000000000000000000000000000000..8745b18631ea9f5ccc5139fecf2667a15e8565c1 GIT binary patch literal 14722 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 DD+Xr| literal 0 HcmV?d00001 diff --git a/target/classes/com/ruoyi/common/core/text/StrFormatter.class b/target/classes/com/ruoyi/common/core/text/StrFormatter.class new file mode 100644 index 0000000000000000000000000000000000000000..cc730c7d7437b742734b60a721fa36e7cf0445d1 GIT binary patch literal 1890 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 D>#a(J literal 0 HcmV?d00001 diff --git a/target/classes/com/ruoyi/common/exception/DemoModeException.class b/target/classes/com/ruoyi/common/exception/DemoModeException.class new file mode 100644 index 0000000000000000000000000000000000000000..c2c7f3e62ec1202e4cd4d22bb092be33bb1fcc9a GIT binary patch literal 408 zcmb7AyH3ME5S(@5L>PxacogaA0HL@b9f&3Y2^1)Jq`#OG9J23_eMazER7ezj03U@I zM<5C+u9)2!&CcH4$LHHSfFaf+G|+2fwT(5wa;XcWvZ*dg3I>zZ z(L}r_BOewtT*}ZUTh-ZPx000qlu8UMHZmVPU>yMMJju94um_eke9h7CPuUZ9gZ#$ QftWU$$hxfjd18Q-FWHP>M*si- literal 0 HcmV?d00001 diff --git a/target/classes/com/ruoyi/common/exception/GlobalException.class b/target/classes/com/ruoyi/common/exception/GlobalException.class new file mode 100644 index 0000000000000000000000000000000000000000..dcf0f6bafffaf0caccb58f008c22ebefef7308ad GIT binary patch literal 1075 zcmb7?eQOgz5XPUqyqrBRZLDe4+G<-x(zJM>f`Xz#Yeh*#tmgksvZO2bB6l0nZ>69J z{^1AkLy6Dk(xf&g=)vvm?9Dv$o7wyM>-!G?cW~W91y|=##=2cLTx`0yCQ#2*s%6+w zX{M9-&9glL@m!#`o5Y!svFXTgs4A4Oj}UMpm1T0E1Qy$G<$D=~G9CnnCe`uaKJ)s@ zNF5#=2UQ>FSeu6erRG*gpuC&(nYz%{v3fC#x+;AwyWvPI>5)Q`h5q=kY>sp$u+#1( zQIHOkk2>HkO5#9$=&5&xz`@fn>B{iQu}_4h<}8@4T|ZFfDCxhHsf?6izdRZ!v-c-( zqq#MkJ{r^M$j3!>{twk-{mH1j?4)y;4AY+4*LFCKQ%&BqgZD9y1s`YO`>4SeX#M+? z1XiYZ_i7j$9jVC(vBCet-gpTssKSN78F~)p__Ew+&x*8Fej^3fI=$Ppg0|B72Jv|m zc8;sZG+>pYYgpvY&x3Ft4SH5!i4j&{pVr2DR-m>070y&;o;bj|{dEblIZ|5AqsL0N zlEo8PW@JUsvGNyik*k#tXmh?VD1G9A1IuTd0B#hrUE&CGwnwy<&9z*m)-*kDZ<`a_ xIRWKPpj>-9EHQ?vQA6NzA(S&!&7p1;Lai1mul<~}L->e^K(z$zQ&OPVOx%21muipS3qftN(D|uwGs+KjiR1MS&)CEeR3|zZ& zEQ8SXy$}1l0^&emX2$T)sW3d}V>*=M%ZvAwpn??tjLgQo&U zSB9b8=6QMS{3r@sug&9`mW*t-b2tFywp`DRo(ZI@wPRB6XzF6q_2ipg_e2Ko?URls z^BuNmvr*>#%}jLah5{Q+$M0G}&;ROL{ObCiCBHcGbHv+NM>05b9r<#w!5%8ri=8R) z(xHq_{nlGMu)8v%8v@k?=veXK+PbI_QZ9L*&{~5n63YW_^x5|Tva)6 zkF?9E@XL%rSim)Air_jCieQgXVGD|&-Z)2U#4?LCPgcmX%C^=t(?X2yGbtt2p1=(v zvw}%Xzu2e$$f!t)L;CvqInpDIGTDJT*9JA#NwA@Hmg2IZPO+~;Ds4=Qnndr#v#NyO+@sq^jJGh&moSrV;N-SRDoZ?wLXH>H= z^c?D=@_3xvd@C literal 0 HcmV?d00001 diff --git a/target/classes/com/ruoyi/common/exception/UtilException.class b/target/classes/com/ruoyi/common/exception/UtilException.class new file mode 100644 index 0000000000000000000000000000000000000000..e40f9dcf0450a659516594e0569e468f24621e18 GIT binary patch literal 872 zcma))%We}f6o!B2HcgsdXp$1{Ef0fGqZtpE$6^B# z3m$+4FTr~teorPBP!?z;+voWBJOA(TkKf+c(j~U| zFSbE>%>`^_hOMyCcBUDaKJ>LS{iaT*Yac7IiIo|OXMD7EwDw+WU(Wga-#`8Ya0$ycY%E%^;L7ra zEMLlU!RB(&Cgdv{U*ocgD<-ZgOvHXS@Y7|m##-wdzYiWQDO8N%G(df_({VvcP4 z&%9ncS@YxAYY|;s3c?_{sn947;nSF-I~hSoL6uFaSkYpRHiFQ<*XwNg-ACS5UwYJJ z${rKvd%9^P&jV_98_lTWc6-t5z@^oRLf8MX>F*@0j=SZ>zI%(y>fRRfnOO6a=h5~9 zuj_UEgmqN7_MwT0?1lGo7GyaCnNb~qPGvx4-){MXMw}>DMk}dccJ?|ozMgo^muudR zu9=Bz3YG*&N5Z7tuen&udIqM-l?+J=lSJG;FbMW~)ay3=?*iSVcj~+-!&bsHu9q-@ z$r6sqavUd0m=bFmADdVz;RbG+_@;z;e5+9ZU#(P_9ZdAYUYG=(L7&;?!N>k+D&)&j z!m}eRy(DPI>7m#Vn>7sL)y_`x`jAdMR+u{^OnOYxI9D_Jm{H%1KH>~70Go%nfLVNm z5_rh@mctSLSyFAYs`|;o>L+WpTpcBl~%BEwE{2%%H+=a$)I6_ynKwP4J>bX-l-^v?Xft+7dkmKBGNp z5DJa~7!Vu+-cA(xc`9;)fer3iPPJB7Z%`a)vUSut#;~Y0IDO#025Vn~B~zx+mW&!T z2=jRU9cE!Ym9pL?s|Y2lQnd`$3uL{hSx@(YnXHm!PIFn(nn8A_M|f1VmX+OQ@?1HD zM{<%1_jB6k*{h`Fb0+0kWIGxAv?&kfBp-3JckBUvb&MS)Y#C_E~d<0?b+ z6X6nhPBon0k$(&G7aSFff8#C28r8orM!V3c{z_P$6SFr>x9GUS`CVn8?>RSjDEID= zhPhNjj{Eak!#Vm|$o+$pCeDvh!NR@{mv2&x0=aU+l+#Q_z4<^#m6IZ;raz;+bu*pw k2c0)Eohxt{PAV|WY#elcoatQ6WG)94H`x!Xg2^aB@>qT%|EeTm+xR35f$Az(*k_ zP!Na;2_K%vvTUi|X<^bBAV3;me(ki|?6vpLKO6cQa96;^ee7#m$2>fidBO0?Wz_-akS?=tJ(U3Tg1w`jSFCG_wG@NZ%kz3Ahf<0*rFB6f~J z;vq6VZ+p0VD@C!oCLWP_c-IZ595p}z0~o~!(c*^^d2;FU$QPL{MPnGJdV+W$LmDK~ K;**RSOuYd-ysFj! literal 0 HcmV?d00001 diff --git a/target/classes/com/ruoyi/common/exception/file/FileNameLengthLimitExceededException.class b/target/classes/com/ruoyi/common/exception/file/FileNameLengthLimitExceededException.class new file mode 100644 index 0000000000000000000000000000000000000000..facf41c591ae8822e9361307dc7c83f60980758e GIT binary patch literal 744 zcmb_aO>fgc5Pg#-b&1lZO=`k(jy<0i!s{AZY zASCz!{3yh8il7x7IIxGAd80RPW_I`6_s?GeJjJ6G+zxQ3j=R{dV;T1X>;$+kSfA_M zsC1ME)e|4df>%*+LcQS7oXFWh?*GRm9{^+l}DJVyv8Ce`5~&9TaKr0vwt zB9j^af()JLr1T4BU$B+vi7HZmoz4{o-0Y?HGnc$lIdweu6V!*U$YWiy1>NgKfA7&d zRp&~uQ=FwvCA|qNE#-RUxH{<(aXj`aX<@4DG&~uf>evT((7-PC8ff65;NXAkD|qrR zGQaLeuWqY|uQFa^=7_8ced}>cs$WofyR`iYOT&+-epv9EwDkpJ4I5};ow$8j YLEIu@EjDqJs#}a}LQuAC(^hcn2k_9wlK=n! literal 0 HcmV?d00001 diff --git a/target/classes/com/ruoyi/common/exception/file/FileSizeLimitExceededException.class b/target/classes/com/ruoyi/common/exception/file/FileSizeLimitExceededException.class new file mode 100644 index 0000000000000000000000000000000000000000..0efee015f53819ae28cd3b1b0488cff896dff86a GIT binary patch literal 701 zcmbtS%Wl&^6g@YOx>#vbTqsZ;ZDErl5pUQ9RY(OSB9|fsA|X~2dy-CN&s5I1D*P2I z5E6UA)cCC$Ioa+=Dki#Vf<6} zbf&Y~FE9haYNn^E%Kd>lt8o-^p#71{UaC?R+Eb1o9J#7YbToJp=Vp>W+kCKI??P?lOJiq)w--u3Wes-6*g>0r2)B(*1X!lO#H@jkkx=dm<5gCM z>^AA6`;oN1pz)@;^$E?~U92(MSmY7&h#9}d`r?KwjN2TYj!U3z GVB-e^P`3yG literal 0 HcmV?d00001 diff --git a/target/classes/com/ruoyi/common/exception/file/FileUploadException.class b/target/classes/com/ruoyi/common/exception/file/FileUploadException.class new file mode 100644 index 0000000000000000000000000000000000000000..02b8e97f1e426d9e4cc5695e2a4d4d64dc353c1f GIT binary patch literal 1448 zcmbVLT~iWK7=8{2tB7fSE2U^@2{l{$4{HB*C)jUQbfVS_E$WtJuLU)4(K?3VAl_j6R zMxf>mY#MkdFx0R-TUL)PuVK5+(aUE7;+4R_uIn^>>G;R8+O%S!rlWBX48qM4;*fu3Er#OT3-?O3mywG+!bRDlA+1-Hnu zlEtZq#vZ?7Hv~2cMYm>pP4~<;Db-xZv_2KBx=)7YJG*L`FZdkQtFA0PZ#h_dEYm^i z!1rvYysbz?YmKt1c*!_)vi&{Fuehas>B*Yqa|i<2E+`$Tm(42Z!8)t>2fi$RIP_$Z zjZKE>wrlPOKF^Xh%_H9MmE0cQwGd8S1MSIrIFIXCZ#|nF1FTam|AiHX2=taMe>d3G zL}saL0|JQyx9JrvHBK_`GSw9|vm{0_tDaO6!x%{-jXCCO^iuldY|Fr-BpxGWU`sg{ z3~(Fk|FJ}Yk;^5uSKG2)otJ>2hhKGC{;S8>vbM5<$zYCuk0ft7f@uuk2Dn454Y4&g zM>$U0D8~4XQ`)zfrpSv$+dIvdiBPy`*EUBh7 zemV$mm-mTfz5vqSj4?91Qs=c456qyZd2h6Y)X8^xYzUz5eqGr8b-)14}7y1Q28y`A%*U2J*hyrWM7fuP!S zPG=3eb4~rHR>$|#Y~U|UO4Ro*)i>hwi?KSrPIx!h%lk`haBs?5f2O;mSi#I679yzV zn@;0&cQz&{$5x!`bNc!_9eTdIuQHl}zeJE6_(9guyVTrnU{rK8{7Ot*MFolAC-8d7?&Y<0K2%p6X zi31YD23&bRMhz5=+9s~KuoYv6JNR|MTkOKaRv%gP4-s{ceF z&jg)b@KrQE4vm}YA_LPWf~7~++xS@U@aEgWzBw?tGk%h9$I|-A=HD>uLqWY4#wu7I zSg$Xp#jch+X4jEf8H5qLG3<%{5m}FWwh~ATN0?uGG7Q__w6C4y`_Ov{lzrRLWL=0)ej-%W|zXdx54{$FDY z9{h7^T*m_69p4bvP{$(e61@d9h`1$W#HYyY@U~9d+_-i7&S%J{nX1h*DS(_2_s}8Q y_^HEboT0}EtYDS75;}`>Gj$W^iE6k&wo{`3yaLHo`r_=ogf4Byz|`O}T0a0Ww%y7A literal 0 HcmV?d00001 diff --git a/target/classes/com/ruoyi/common/exception/file/InvalidExtensionException$InvalidMediaExtensionException.class b/target/classes/com/ruoyi/common/exception/file/InvalidExtensionException$InvalidMediaExtensionException.class new file mode 100644 index 0000000000000000000000000000000000000000..d215ea8c830b66a7c4790ada68c488f975dce026 GIT binary patch literal 810 zcmcIiO;1xn6g`&~+CE#U;3s}0CN2~|-o}k!+!Rb|iZKBjBdekB3S*`-=DjK6&vHfL z!XMy|GM=eW7Z_tqe2cl~o;mmP&A0Dgz5;lRn;B|YY2bPTHw4{EOKaRv%gP4-y1yZi z7lKYN_$nG7hsI5Go`LBT!NODPZG0wJySqDhZw^iFjNi|xN~wQ47=>|OPQoXf6ODt<^;Dd{vrhAqglBWz`E#jt?#(zCK58+G6)gW|A%d2k zcABO;voS$4wc^c~(|5Nt9)#kRDd|T35<#{dCgn(ARa64?^+ElyoD1i6Gk!lX9dlsQI;lS<%_>k5b|`TF3-%{@2)o zC;yxpw=u_e$2Wu()G<%HKyMBWB5ny8@i{U(ysgtVSMNV~cntYGRke901+dA8Ep&)h xf9h}^7w9npOIT*Egf8OJRNcg7q8hG{?bIj$uRt=DzB)axp-Y=FFg3V{)(^5q-a-HX literal 0 HcmV?d00001 diff --git a/target/classes/com/ruoyi/common/exception/file/InvalidExtensionException.class b/target/classes/com/ruoyi/common/exception/file/InvalidExtensionException.class new file mode 100644 index 0000000000000000000000000000000000000000..a34d3682bda91f972b01dc7c8bc86a39e57d004d GIT binary patch literal 1943 zcmb_cSx*yD6#i}tOj`!QvbhBXQA%0H1#tnDMbKhV6Qt1)A1>_`#!P3(bc*=qn~8CI zGVu-Lvs)rdr11y%I}m;I9~jRau#HR!iP&V$IrsGJ+t**8zX0gMsDcwAFq{maOX5=j zbO&%cgfloBz&Xk43850bLF~eL2`)%*Q9+-Aegy*z4KZ_;8+t0Cr-U^$=Lj>STjtPQ zQp`9+FsL!p6!xIOGZ~RlFj!C-6)By2RTNw*$PAO-bTOHsf=n#Ur-WCAp(-P6of`>Z z%b3PvWS5OFR18{X#^I)u;6_%IQYGY%kvumH>%K_kZ820&#P9K0t{L2%(#9NHH>Y|S zg5po24B=t|fxEgPOr91DWxcwoI~N$vMT*$+DtkGt35JqED@DdC-q29%lR$8;O)&pH5Ds7pX_|q6oMyPExFQ0I}ZNodhAgdcGVXHWdVFqRG<)hV=#R(OcafKmp=i7^C-*XQq80zk62>Xg>vDjIN5#UV7RXhI{y*?+Dr?b~*` zGXL=z!{RiH`AxdDoz|A!Y~F53cW!!%WoFp9=`C)+|1iDf>F`J7)|K&@8DXXvIwIcD z^-lA~^vT_E2Q%yFhSqP}`~mGwz_Hfv2SdwWm*D#3J&|;JyynPGZ85(IG|~~Sq?=WO zeb`GM!+v`CaDYAq-H6|mb&@P+-0DCwXtTuPu_ z5@iJ1mZ3;GKwuHUC4{1F3n-7aFF=iUETH0}>*6TEI>gX5@0H@$jLk3Q@{3vr|C z&JaN>y~wQ_By%Ylrq88hpnj4CD*Pk>+BMLs(Pc&(+6l_YHNsM{H2MkUABgZldM|UC z2V8IJy1<4v>K!_Xvsqc)tlU^sSu3hsv03?O5oI5ax$zxGm~a!-po-eI8$nbPt|44Y MxQ=i=;XMfc24O=H+5i9m literal 0 HcmV?d00001 diff --git a/target/classes/com/ruoyi/common/exception/job/TaskException$Code.class b/target/classes/com/ruoyi/common/exception/job/TaskException$Code.class new file mode 100644 index 0000000000000000000000000000000000000000..58793729fdd6a6ecd6917af4b97e5d5174eebb2d GIT binary patch literal 1516 zcmbVMT~pIQ6g}IfO&S7}3ThF=kBTiI6|G+skqE)*SW~B|Xvc?6C=r8g;-sykQ@#V~I0 zVP3(AN{jQ{BCjCHaY4Z-$D0aHa9mU{#_^VdagIwebcVi)Zr!aJj|x`BVvv^o4TnK1 zc%Bn1H|$o+X)#kTTJEPR%pFx*KO zx9k^owqbjlS*snm-ex{?s5}!zziv0y?7-zQ_5w+s-K2Plzk@Ku?>X(K{>FVfAe*)m zki_VM>$&YkhNJ&TtB_fvQ*S?Y$xux>UZ;^;3ayLD@-3cgiIL&t{ z3-!jHXQL+-gM2I;LhK^7jB5Fvpzf@?yq<}DT{X)qs^KJW{0R-Wu`Gj{XYRnAso^XP z4H-;n7{PH3Nj{A7;RGMX_%M#+)WClZk&9x8)EmA>nGL5iJ%UT?TTZ=A+0Ps#7ltdn z$06=ozQ^nDw%MOVerb-b?lAoii-OKv7|-Y>fI;V@xVWNHr$ZKEcDZowSSXuG!rx{wqy1 zBtG~9{87d;)6#0>rN(`jz4zwKIrrR|`SJ7HcK{D@w}1@R3dmwT&*-LuTMjlH+!n}G zqqY)o_rg%c)t*cel?W6Q6$i4{P;nAO;hVi@0%Biat{R0&D#NrPdjpjvxBdiKT88C8 ztrhkCco2OId`A5!^wmjAy-P{rA4g5UE|a&rXO#^tT@lDV4#FUPBJg6fb}ZjZzbC_v ze~`vO*s1&j<5ojpp%#Sd)u7*0aa}fhbhucHT684oi}|RSNxK2!2M}ROsfLkdE=h2*Sm4_ zLEiz+4(ogl`W{DAkrar ziP72+hl?mN;|^THWpe3AGh4XAIB&5G7WjUY%Q#R;s;wEGpAhJRQ0rTh zI%!-W<4hH~cfN>FI@CtHBSOC22?&M09VtTH*G64tvrr}1GK^Vi`ZnURl7}<@6btTF zr-ZH_*_lW(`=|w1Gi!u;h}50q1R~==;y9Mk)Yqvy{nC2`Z(y?|QfGQTzxCU)H<9;} zqsMlv&0ZMWXsSk4cxVy!|9>K3_phcQXZ=n{Xt(`&1QAO!76X@PGw$8|kgmdsid?S~ zU>kY<1b$F#p@Bb5PidkkP`$!)Y#d9iJURU24jPoXf9eH*H|s*29_MVWN**t&$2SH@CW## zjC1%AV`JRn&CJVtGqby|Z_h6P4zO89(?bDkCA3OdC)5*Zm54)W6J_-9{DeRkglgaD z#0l*}5vQ`q-s})z=}2kijtTj8CnOa5=2jAFfzt9aosFcu7NeM%MqnatE4Vq=pJc&J zR6^JfA~W-CY95r&+01BP-bZrocmqG>h5WvlyJ#X#zt|qZ8<^Ba@=RsjTfYU}skjq_ zX6EirqYrymT5D_t9ySPj|D8$L{mU}sY2P~%+U+1)!;gg?`vYf{9`|m3P*1b$F#p@=1pC9ZNPbH-Q2GGm3AL)H<;@=k;33Ax9Gs>kY literal 0 HcmV?d00001 diff --git a/target/classes/com/ruoyi/common/exception/user/CaptchaExpireException.class b/target/classes/com/ruoyi/common/exception/user/CaptchaExpireException.class new file mode 100644 index 0000000000000000000000000000000000000000..d0258147f6185e578e832fbaf472b245ca7f7426 GIT binary patch literal 524 zcmbVJO-lnY5Pj2c*4EZqKhTr6RU$KvZsV>v*i%wvxzc4G@m@Nt6FnjGxMEcp9cBaT+*f4; QvsXOMYMc=^7&EB91CyGF(*OVf literal 0 HcmV?d00001 diff --git a/target/classes/com/ruoyi/common/exception/user/UserException.class b/target/classes/com/ruoyi/common/exception/user/UserException.class new file mode 100644 index 0000000000000000000000000000000000000000..e664a7cf168f9b08d2254ff4219690384aa69d94 GIT binary patch literal 667 zcmb7C$xg#C6dadAAuKIr-z2z|)fa9>94H`x!Xg2^aB@?txN2jRI0!zA6A}kLfR92< zN6M8RI93^*x2yuXkS1a3yfB5 z7&{fZs%j;=pWmiS2n_6)(70WJQps*2Xrs&&%wG|!k+$FQ_#>4{B4{TdTiG47VsBJ^3?*o17Xj3M1Ew303 zKB+Rx1f4R)Zzj6Fc&DNUlTP$-$QhhycVp}4DpGCjI0*so!X{Cm4^7ABk4jr<+sv4H z7=edHfz5we{aasCv6g!bU#$uFJ!bv8%O1V`7OhsXf#<=|mgtP6_!|J0KML_Er(C6NSdh_xmcTN@8(D9YBE!y>$L;y zBhO#L&x>6YQR2PKEQbn5d}XXKRyosU9r0c{Xc9dm_q0^i7(G@4t6W!S1gn=iW({V9 IHO3s8AHRr)fdBvi literal 0 HcmV?d00001 diff --git a/target/classes/com/ruoyi/common/exception/user/UserPasswordNotMatchException.class b/target/classes/com/ruoyi/common/exception/user/UserPasswordNotMatchException.class new file mode 100644 index 0000000000000000000000000000000000000000..cd4e9b135275cc81bf7969a4910d59621bee3600 GIT binary patch literal 548 zcmbtRyG{Z@6g`)BRuIGo#!f3Raknux_$W{l4WbFMjn%MBUWo2UF2l!FO zy9AAuF>#7>A368TnLFXgACU4SkGgFP>E!y#Kf0jq>S!eoD%4gP-+<+ zS)r{jCb7)&Y;uT@J5*ZPV?w&#@Clig8Aw98t+edK)4mLEMSsGbYTE=XD_ETCMKfbZ zDk7Y;12gr)*gPqZv#HUZd<^8wvI8$>Ltc;JMnutr2?rfxuY?UoXJ3>i!ReYf4CJ{= zuG;yP?T*F0Ak-3Fcec=UwXyCrQ7ytjjd1iYM})n<%zwXtP_MTWU*1IMq1Uyc(!=K6 zkJfd6ECbtY_}E68|BH8zEo717nCB{m0%yDkEHM_jbHF^}SlF!+y&&~GXO$QoW&_JS TS7rpW=RB?|ToG0nQ>cCd=$(@6 literal 0 HcmV?d00001 diff --git a/target/classes/com/ruoyi/common/exception/user/UserPasswordRetryLimitExceedException.class b/target/classes/com/ruoyi/common/exception/user/UserPasswordRetryLimitExceedException.class new file mode 100644 index 0000000000000000000000000000000000000000..3ebd819e5fa17df42aad8f49c80898367ff1629d GIT binary patch literal 777 zcmb_aO>fgc5Pj<;b!w#~4N&?8q`)Dmgni)_#3dADWN1a&NL5c8dr7z0yVZJK=+EK= zLV_Q_k3!6*RjI_K2Yi@$^E@AKcK6rs?>_-N!;>b~e5?o9z-E92-1qUo$3uorDU9U# zM3_=)br8Q~U~d?fMp~7YD?8zNB^uPjeT-q@xm41=U}*H?cran8k8~y&+KE)+T~$nl z+2_-om~Ns|YRsuU#g}B=9?O#9b&~2LGL=4;5urk>NPJGksU;0jMUtWeGJCu%KWUS_ z7uK97vXFL%l0@e0JatHO&DGD5u9PMDF+(%g>BoIpkc`e-VUKmT#|BocQpA0(Re*YRhwnnPi@aP(KvEy(s zp%okSu;`Is9X^`m7b)@(5Ms3Z65(6K98p${{MvSxHGaT5tZjTlZG2eYbnYwaU*=_P tvH_KVidNA^hfwdL5XCMQ4vxNY^k7EXLnE26Za>aoGtcJ z@JYP!R&Nvq`v5+Qk07E>HYpaV7s`P-XXea&GxN=S`SIxlzz(iiSh11Es*M?3Dq;;a z3v~;Z8S=MeEcG1*v(emV$lXl>!7$gAv3Qh5o=|(-3(2z3O?)2ib0x?6)R@zUa>UT? z`bp%dG&za(XHm3Z;*GO5A(lhiM@0XCEDgA`gP8jUXO0o^=QOh&v&V z`;M!XjQgGD|KKvoz#cMKlM)Df@f>J|va7lOe3uWWy;OFEK1_lhS3D9_I|bTqk}7{( zG7(V!TQAqgbu(1{EWZQ`S4vpNhK0=%ws4i<#=jE9urm(fq4c=NodZ77gHaNeRL>=pCS2?b{hRmsMsvb@s%7Z7Lepf*Stq9k+6gcY2Oop;7WVkj1)73pz#<6N0-+5bN(H}}41VS=&Rup%hF7qJjnuTc#)Ca3diAsNyN>}ll($O8FKKCYD zHmTc`M<==%hPSt@u2f*(qNP=zC@n(BV-{B!hJ0DG!$7)TZB6&#g)g7lk=L@Qy8QlT zK#1K$v*9{i_s(7qeb2&N9#=8XaC-N-64l0L+_5-W&2z!RwLJPzW*FVw|Ib_!3!R$X z;9kwXv$`%E8qovWv;93g1KZVc?yd2oN&TlmOq8J~LUMh3@y-^fu9{FZ>PmRZl;yx^ zF>15nR8hK!)V86&Qfex<5!%M!ftHDRsJK+&Ls!)oYo0GfnTJ&ADKqsZV!6&`MbD|{ z!~^{geep5FV)Cx+(Dvl5XbnX?zxqxWwwGqh;4;i7aasZX>rNI|a2)#eqnCr8!Zdwp zW@+6^JDa|YzL~LiU~friLpQDMk+KK-NX{Dr>_-pnsD|i8Dsny_*^@5mScn`CQZVgc z9JO>1hiE-aTFs(>BP38hwOBk71FQKHBlP>fITU^L4Kl;HnQfGiV_+^mgGU&9ivJdCk`htf?@g>oWezv zFpr6d^Em_D;=F*P7$9;)hP;nO87+|i5JgI1j-m_@*^_jhL+S@cEDU#NqYT^0b|lWR iGeu!EJjhk&daBicQnV*svW8PzivPQYbaBbN4aHxAVa28Z literal 0 HcmV?d00001 diff --git a/target/classes/com/ruoyi/common/filter/RepeatedlyRequestWrapper$1.class b/target/classes/com/ruoyi/common/filter/RepeatedlyRequestWrapper$1.class new file mode 100644 index 0000000000000000000000000000000000000000..1a548e6185a00f40d3668bbf733bb2db28a9a6e2 GIT binary patch literal 1478 zcmbVMT~8BH5IuKWTDq=KLGcR|wQ5@`TRvYPCPE--Qa?fsCi=9rSGZ=oTX(mFzWIZE zF%dOB@B{o&#&g>OwIZ=L>AiEOGiPSb%=E|4??(U@v7ABz5A^ZSL=ssOIpj@bFq1?f ziAMs%`;MF0aa1U9tNhN{cWmX`YpqB=2?D28@*2&k5(Uz!uLvZfJr!n(0`ujnU$=v% z-%>WEy6@RhKJo&6y-;u#fXUC;xtn61EciRb+es`5|U5bEJ@;n)=yG|HNJ{Jd0EHlT-(obcy z=ht31fm4@cfk<&uRuhP2b0qeQ}70rXCBcD1#QM?9^v z(y6v*90kUYr?vF_gkOQ;pwR?Ic4hP%awePWW9`rQY&)Avz9)~vppK@vgmKw+rU=G zrVD(PHgRgM@q7IQX@M_|lp>P&hM}W=sO280)EQ7WaFYPK=t39Di1lNv_Fx!Y3^wc^ zJlD?joWiXx5lijD0ntxwsqNUIR-91Fy=4zO)DGzf*rq#ejJvqUJI=r4`xv8S(o8p% FpI=EdPNx6> literal 0 HcmV?d00001 diff --git a/target/classes/com/ruoyi/common/filter/RepeatedlyRequestWrapper.class b/target/classes/com/ruoyi/common/filter/RepeatedlyRequestWrapper.class new file mode 100644 index 0000000000000000000000000000000000000000..0ca282d88bf6bbe542939684986227fdfe6b982f GIT binary patch literal 1979 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 Oin;_Wcr$X~KKTdx0&QUc literal 0 HcmV?d00001 diff --git a/target/classes/com/ruoyi/common/utils/DateUtils.class b/target/classes/com/ruoyi/common/utils/DateUtils.class new file mode 100644 index 0000000000000000000000000000000000000000..c61f996aad29ad90617cb1938c9ad150331dafaa GIT binary patch literal 4946 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 literal 0 HcmV?d00001 diff --git a/target/classes/com/ruoyi/common/utils/ExceptionUtil.class b/target/classes/com/ruoyi/common/utils/ExceptionUtil.class new file mode 100644 index 0000000000000000000000000000000000000000..638753923ea0c7b6dfbc5af71ba3872ebff2647e GIT binary patch literal 1394 zcma)5TT|0u5It$rri4_Gn+V9IpwNp%#Cs7@gr`;+K^%MwA-2J`>7=RXU-1w4iZ8&( zjIYl4n;6IQC8aG+9qmk${r1~Ed(Q6W=dW+y0o=j5ic6SNkW>*8@01D|X%!jV6rOn% zI&Q_VAUwBYSX7yFSHV36_a#J^?27FzO9;;;wbL~oj2omE5+b&w+5<;~D2}8l9?dUv;Q)z{=jV@u?({imKTGx`- z6cXR`4D)^7sQRr`@Ib*s3F@X(cTH>E_FLXW6z!WAAR2nmD`CI~{6i!4oVz#JEiX_m zG9D3LLl#RK9%EU-6AdePDghdvfuolz^-@W}s)pxSlQ8o?(_}+is$TByTPF1e1TN0; zLSP`D0vaQygvNm0itX#E3gUiewp_#HtcP2HZKARwIOaJmxQ@%R;J5|ds2XO`YV2jr zU$RBrI$z9-E6SbF8auIS)N8Dt`S)(Nub|58<{~Bj(#L?cm?`VU_pEnDy+qMs!}%?> z7Yv-8Ru5B%^Q#xXFxWWs@_Csjoo`MSk<>AyuQWpF<1^|zHT2Wgf&dI)kf$yT;R>Tf z@-`y0fRR&#c8(FwrA{I5q~&8oju1^BK{@1YfOv_8LA;ocGw34^1=$$JRo=x|c?6@Z zHpWwg!{nngp@Q zl59sQeT=oosbZ3+DNJA*30(L2Oa|~QJi+(~{gY%SF-~J-f;6V-?}vPbxPqA#{*^Oy OD@<>r%%LzUgxTMprBfaN literal 0 HcmV?d00001 diff --git a/target/classes/com/ruoyi/common/utils/LogUtils.class b/target/classes/com/ruoyi/common/utils/LogUtils.class new file mode 100644 index 0000000000000000000000000000000000000000..4f6c708204ca3728f6bd56f22fed5bdd0a9d6ad4 GIT binary patch literal 698 zcmah`O>fgc5PcITu^XH=m_qq%8v-Sb1io+!;!q?mxPTN|sj5KQI4gHs+Z#D+B>oj# zQVArkNcVJGQ^;p>2y@ z7Y|)@2=x~#SLP+b>FysBs;~4&5;h{0%eQ4Vl*I=zOt}+8Iu_}%D3q-i&8j(56GA(R zbru$-{-Q!gna;z~sB{uWIythSPw*0H4pJRo5cay!x%e!?ROCtcZg?(Z)8D_+8kj=m z3Ey$DNn$6junEn95%I;L7%w(y9ZGYiNAE=;GHGPNTQvr{EMoau+4Y{K>B0Rn7dSP+Z^zu7mLod2iCcWO UHRFzhmrY$Y+J|Pc;h13Nohy{@l5(yv$%7WFz8Qdv;pz(yqC-670 zqOd@$`6$F4+Xy42s9wy>z30Bq_0M15z601t*M)-%4lcU5gv&0j;OaaqY&zI-u+31v zD-)^jG1OY^0|vXFP6Wf^KqlhxG#(4}jE^JIGzV$GqXVv_kxQkm4`s%%Hwe<$SJU)^ z^yw9+i9gjc%KV|oG9HTOM(8m(aUnC@X$_9}Tkc0Z3H^~)G6{PxE2<~sBN6CcyP_(s zsKg*Iu{jgtDx`u|X2fDxDASM9sS2p2?jTj6pS>~JUnw4ocd2?!4J2B;*MDj+jH&pg zq0oov^Xi%a8U|4OH44ZW+&1uvwnUvFHcvLOt5* zW^~VWzJPts;dwgixzdA2Z?7=G0?v~35sO%&AjbIxY}DvpYg)&s{eU&R(diz;{)#yp yp9rvEg1K}9>y&dN54c%4VHr)*lRH+hN=X`ouHhV6uamZ6kpTz-K^t^hIR6_G7zIZF literal 0 HcmV?d00001 diff --git a/target/classes/com/ruoyi/common/utils/PageUtils.class b/target/classes/com/ruoyi/common/utils/PageUtils.class new file mode 100644 index 0000000000000000000000000000000000000000..38048f0f4ca90a044ddbead18da4e07c68f01eb9 GIT binary patch literal 1394 zcmb7E>r&G|6#ljigoc!I6^n{c6$%9+h&Mn`K%`?uhARF}%UYsoQZ|VUzKM_FAME(U z2k@aB&!#OYBj}i!J$rKYT)wm4Nq+wN{sX`w@*0xJXt;&j8t!01ib*N58m2HUwVZ|- z%u2sG4F>K?ZC=HKihB&5E0$yVs|@k%)INi<;T~{?RKariPEajz@y0CKWa%%sWz*g_ zg(cscM#Vq0Jcdl6>{bmCxF?oDTGe%oz_)DA*flHst<;to5}t1gUs@Qn8f}|y)wCRj zskZ3%!mRRRS9~;%dC8DoVUC6?B~hh`a4`%NKA4|O!#16Yk#~Gv;eyV)g*-LEP0w{2&9x_6cU_yCjznB0MeI^Waxc1pDDy2#-bIhJg?-J* z%sP6IQn9GxJ|3uesN)eH>sXRv87nHD=vc*?j;DC0VqM1uHgyc)xr!|vFR-oSCQ=Mz z|GLGdOU3dJgU0O~a=XSwct_izO4k`>x!^WK;)+47<_e#ow-i|Rf$V0F9|hbaQ!e{I zd`$kem`vPaS*!4n{E)mMDiU!L(bwH{YA`56UbjdbJQs3CV&i1MWb^rp z>dGcAW|hWp2VF tagAc)v<7ekqvW5EUmJPJ9>-|ON2#M*)@EUg8byL~#E~J(80j&L{{}ayUaSBB literal 0 HcmV?d00001 diff --git a/target/classes/com/ruoyi/common/utils/SecurityUtils.class b/target/classes/com/ruoyi/common/utils/SecurityUtils.class new file mode 100644 index 0000000000000000000000000000000000000000..33855c42baaa666633c3fa886de93799f54011b6 GIT binary patch literal 5768 zcmbVQdwdkt75-*JGRy8jmVkf-6)-|bzy+(V3i1jdNFtH&wuMeMlVo6bXPKP^(%M?J zt+uwPm1?CAr4Oa8Ahs3~u%dm~*4p>`{St+~-|b)h^*b{+yR*qc@@w+T-kE#PJ>R+C zdELzm|2^>xfGhEz80Nu_A&DI^B(PIQT1O^|tPUrJ9CpP}4Of=(QFt;IqS!6+d*axO zUL9|Y;!QE!f;Y$U7VOb+YYY=a;{?1_K5mQQcHAKw-zK}>9>*-aqmJ0`j^jPpR|gIE z%F}%!^4=Kk$No6pCn6C%Bnt;(_y9gAPao3pVbSq$9jb7zj)PHrB!-XTV=;Ujk4EvB zjzc;=p`m7>owdD18mby*Z`3fV+3B=2)VA1J>&8N+!*bV~9cj+gwKyp=z0q`Sc~_I8 zydFERVP;Fp$t2x^(`zUBlyS1jf@i1m$#yGMaBZ)5gS;)!FuL3FHsmdLbte@z%x>9X z?lzNYGuxePakAYM(1>(FXnMuol$G;rC(E@l?Vg$1*=pu|aFQSBTTTJd7gxHoW`;^8 zlxc7GT-(=gP^iV}wzFb|hDJ)2Oz(2Jb&uojOy-r{G)u`6e;oHNEqFav*0WQl7^UG# zDsbHHWIiVV6%?c#*GiV}qN-Xen{s<|UYnWE6Oh)=YzUoNnH%P(c!FrIxTkLD7OEat zUy)7Gl|1CKVJs9woMeY@P;yyw*e<1m7AIOQugB?ZGle71a_RNjjOnF%tbFMZvnm{7 zsExC?(pj##r?gM#psmzCK!%4*dn_mG`MmGLCM6^xn{0N{X)EPNTu5_hYuscAI1FmrA_o%#p}|{ap)6f`a|0>U zSaHX~pIBgs6<^@peoEq30ce?w-;48f~q zm1kTqXlY?6Wpb{wgLEbHy?M{dBs-mqX=jt|y*|zYIYJ_BW;!~}dWBs-pGm%#$yA#5 zeN{h?UTJpCqM4(W|lLkJG&**qc!;pH$+;G6iCj&CRMo!V-AH-YaN_&$DM;D`8;jvpKN34UteXZSfe_WeF6=?rYcCaV60 zhD%HQsR$@KuUUDKab0anh0*a#1HZy^I(}{7H)!R2Nb753mrB_= zldZG7K?au7hQ-4xCYPSI*DE%6QY!DOoV4F=V#HBX8m=2re>{gqnsd$nZ4P@x&{Z_d z3G>?AW4i6ut^yaW1z|rFJFKqynufXO+!%68s5gu)`CWPu>vx*NMsd7z`S* z%^(PhGlQ;n10D(mkeZ6&49-f%jKpd&V1PUS{;cSfs+P;8do^5A+2<>j^LgZ|jG5DL zbwyH(VqyQ3JC)HnBxcm@$&u?}wdXLJHC$X~h|*CMq#b%HxO1_pg>4xk8BW$BXG#37 ztON%J0m}#2PSgC^e-f1&E#&y;Yt_@Pzc`eP`nOxfPr*!<@z#s4z9e*|OH)&yA zXw~s|1OLDh1D(<;W|R`%&hB=0TFDmww!MeCOUj`%$amStJzTxIut%x}u!OHJoY17_2PQ3K?iGeh( z$12K1sdF`Mp#C*{tJXLzA=E;v1NApCAS5JemHO5>jZMce>2Xel_2;N*m#=?XAdVY- z{SwY=`NY;m?gc60BaMzw09+EZHr%ApRw=T_H$IOV%la`h!XTbSPxhfIpP~ChX~eE z#kHF`r&!xglf=xCur~V~tQ{s(N66aYGSRM*7Fo^t^4CK5CEp>xFDk&p$G6fxWk%a3x2z#3GIt5P9Ik8MvRU?mvN(!+;f zUOp1c{biUzNS+sSV}QwM=%zst^x)4NWAx4+@lX5-7vfI7H!|DqqRcz-F5Io2?om$< N;6c1!%{_v<{|De``>p^0 literal 0 HcmV?d00001 diff --git a/target/classes/com/ruoyi/common/utils/ServletUtils.class b/target/classes/com/ruoyi/common/utils/ServletUtils.class new file mode 100644 index 0000000000000000000000000000000000000000..09a4d67dc28bf0232fe99701f8134070031cea29 GIT binary patch literal 6153 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 literal 0 HcmV?d00001 diff --git a/target/classes/com/ruoyi/common/utils/StringUtils.class b/target/classes/com/ruoyi/common/utils/StringUtils.class new file mode 100644 index 0000000000000000000000000000000000000000..91ca084765a32fb4539ee7dc9e1d0b085a0be55d GIT binary patch literal 9696 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 Ti4=FeZs)NbluS?_z=!_>Z=8$t literal 0 HcmV?d00001 diff --git a/target/classes/com/ruoyi/common/utils/bean/BeanValidators.class b/target/classes/com/ruoyi/common/utils/bean/BeanValidators.class new file mode 100644 index 0000000000000000000000000000000000000000..e9043a5f649b67703d8126a71779d5589c350d2a GIT binary patch literal 1265 zcmbtU+iuf95Ivi?adV?3ZCYpo~Di1{M3u!?L3Id)suGMUDypg@G+E3sY z_zRw@f&}k;6GF^7X@oICNLb2xb}nb;%#8Ex$EPm3hi931R#l1Z4qh?|)2Lp8z z>n1iBGTXuvs>P75t@Rj;wmfhc<~qW2_QPS{@n7=3OPW$g+T87NU+DWpX{dn+7&bb# z99n)TPlZL&P~_6H0ng$P+er@X zWa_b3j6tiZ(TZKg?RR^86phS8gCRHVITbv==cs`^c)@)>bd=+hD%%x@9#^4HW8eMX znP^UWEV-@>ecRa;dYKmDJF%&|U%)J~CN>MO@PO(p;2|C{G!iTE2li7!8ssrU{onVI zI`I4KscDhwVu%bYi6W+Z$uK)vIO6qW+^yIc5>sxj#Rp77GDWa69I4ajD||y6H^I56 zfI5hq$k3OppA;x5vUKL?X3?9`d!~K{_AxT&>C8mRA_^oI#ttar65ZZo4)gREpo2dk zO%hP8uWy_qmBxPK9BD@S!e^mwZuHib}3_>xmN>VXR@E{ma7;uAr MDe4Kstw_546P7wi2mk;8 literal 0 HcmV?d00001 diff --git a/target/classes/com/ruoyi/common/utils/file/FileTypeUtils.class b/target/classes/com/ruoyi/common/utils/file/FileTypeUtils.class new file mode 100644 index 0000000000000000000000000000000000000000..587de44169823b50697054810f2064eb818111b0 GIT binary patch literal 1426 zcma)5U31$+6g?Y%NVXM65lx^q4YY(}D>R}g(1e6goRZ++q@GFXOJ^7qXPc;XERQTh zGd%G#7&=8y^tHSMrVso8ep83xtmHKAj30Qpt37+qJ@=kn{qygOzX2@ab`evsH7w}( zu!!qeETRlY$45onz{e`MDlF;vM8~H(Zt7Upu%h8Jf&7}^^`muxOtp4TAiEJ9NP(%Q z-<7+A<9!+4_4YeNtY*;mI`_QLSNkEDjgI`jz(TVf96RA4IPo2R9S2=!5c!?H^T6*& zXOrRXNl$*OpjCm=p^VNjfveT#L+=OA@q;9BwU%x>UiZ-1i$cG9NJ>@}5-7i(DloYh zdF@9#UT@fsxg(>a;GpG&-m#2i$lk6E>yH*3tD!56UGmhdNlT*?iuYxYb)z6`bPwcX z0nrec9?tUB<4AT7Qub{1etoP$p?4HSLH#76#KiqN+5IR~PMotfaN6QtFbLaHov;_R zys~dmHD>wQPH><)cP{1$+QH3Oex-N2i;%(fRPV&DthGVmqp z1~#B>f4jBCxYgKXtnaiKTf0PR|NCYI%yZZ7>_3$4=rt(4OaWu`x&<^knEH01I@YyP z$LmLl<#!$k%#6EEUPPfk*r%I(E-I%nzETu4gCAwM;q@i1@E4il-vea9J~7Q`ap!QQ zX~@}sLj1vx3B19WPrwo`@Y_feFoRj{3bgetW-0PFTou_A_V37BndkN?vVS4>oLDBQ zQ|1aXa<~W`m63WTkxM+QB)LMz+x*B0qN&E^p@w9I31Sy*D}M@Yt@0#Qs${=Eg?@^{ zxtPV-fGS@=4J9ma;_HdvY+3+wc!%d2TYr~+h(wx$zC>A^sm~?qSMgq|&bB5J@>8qW zv@@=`6=TKDx@IHB4Lj$Wn=xv3-ZhtFT(LFRy!jhSYF&vjZR@V-#VA;%<}-X}n@rn| zF>jkZC$D0gOkg3)HVX+-pvW^UDV@6FQ)fr<4We^u*NQQ#RCi;Pl`6Z*Ta(R&a|>(~ zIl(eLTfr*U_~O?gaSM-7$4|J8pRtKw@HL)bd&HWjn8*9%W$-Jm(WKNG1G$%QHGD8x Ro4Ism&^*%wJys8;_8)UfCZYfU literal 0 HcmV?d00001 diff --git a/target/classes/com/ruoyi/common/utils/file/FileUploadUtils.class b/target/classes/com/ruoyi/common/utils/file/FileUploadUtils.class new file mode 100644 index 0000000000000000000000000000000000000000..010382f3866e615067dfddb9069585c7cd21798a GIT binary patch literal 6054 zcmb_g3wRXO75;B_H?tWg5Rw1}MM6*r*~nI*RtQ1^S#Wiepc@|1YMAU!!oX%X?92*? z*oU^&);<(lt8Hzy)<@MAv65)iXlrR}AHG`qerTkmExV6muBQHLulFd0{=ynLVnAH>yi^dSWwR`3xO<@l%y1=mc% zwb-NLI($sQ$5m*!Uco06+@N9#Zj>uNDKDRrmrtvhgPT-*2A`FyZkDoJg7}<*TUC4> zx2d=tcPO|sh%cx(0e8vwcdNJuUsUiV1$$LA;7WP9w}OiH$6{bD_w($oFnxSciA{Vf9J%gnTCFBkANE?6(%UzJHx|CE@SHacO)Xz$vjrW+0r+Wp-3cEjGBNv_qi`hZ~@S#CGAf8z4m#sw4C)4H6q zu|T9TmdW;qtwCvapVYNIlieELZuEu+=p%Db&)VVk{76D>DXCD<71N4#itI=jgSMGT z(=5L~PNtp~@F_8#=sbLo` z)i8)n8nz*;;dzPn1r5K#iyD57mo#h#*)eI??(nYNJ-c=<+_ih06`j3XjD)S=HyVB` zm;O$}@A0z0>gA{0V<%6df;l)EAg> zsMA*F%v91K+P~nh8vZ5&_IKCi7ijngUeWMR{7b{Dctyd#HT*}u`!5a(%$q~X8OpGFi*O$hS%`A!14)8OZYraav$E?Z)kWI-mM8prfz&QebMyAddeIt zUWLG&0~4?1k3?C%)MPEt*an*;Of-x{NI$&L_n0wBzjpB3P&P{iv$)-&{xzkwVMM* z*Umx1>6ZlR^37grEY`Up($*8%*cIuBM>{)sG_a_)8x8N{KCufaqIWuc?WYQhE6fo~rSaB&7Po1h7rF^KlC%IdyWLmS3{*2y+BVQ zeA%tL{lhq)9jTWJa9NZMf5TxQxof>W%KI|e0i9l2@+Mv?5hV`+3`r4@l4A%_nynBsC-~6 zdnB%yH)ix$ThEiD&+HGMm&c(%jNXnX6LNO4v!Z7jjJqD=H0IZ@)MTODhSpH-}GN_(U&+B#0MKH3)PES6L; z5)>?_{3RAo_>xo-OMQKZhstOP-&-yq<#?1lsvj zhEw_6fzHD7TBx!YjY;Pz`qY=d-*Idh9gX4 zda#K`%9$n=%MI#=;cMCl|1bi3Vp`+KCUxx&eoMrHz=AWaW5B(hb`Bs64IzfTNvf0D3jjyHZ8BHM%NxMYSOeD<( zBtaTF9?7|s#2Jxw4w8DRP(38luo*N_MoNw(CXvS!ortNTjFZ7>F0L&euBUh}rz{qF z0IJ~cC6gY7wjY(tXH_L{Mdb*lbXO&ZP&FEA9>&zrFshgOo9gJwn*FF<8VJ<|d=En# z#*hCe!U^-H$M+PUe;|;;W>97$&&M|Dci=kjn%rl3$u$Ak| zs9_lfNK+rz&Ow@H`>A0%^__=6-t2>@7fvWtzFov%OyN&5G9Z<{g7I7VSSLGgDwQO; zkjo{nL)FJA52LQzH;kF_Zhw>Kj^pFqf$CYqI5FN`-ZYHa@nO_U`^t*wvYY!{hWXfC zMB9(`f>5IlcQ{qIf@3`YQf%b#;LplV)8u5I6TBqKSuvO?s#vpLx4RVDdXXE7U3 zvMM}R;A<_*?|U4GRPKP~QjTOLa7!G^^y>&JxLkoXIykVRj3yoz|wL{0}S!`dxRJ_QF^OA#39tBk8Ia>ZSE?wzj za&l-6eOP=11R1CN2|C-UGyKt+S>qjp#b?(%>V6-!mobCC#(M)XEMWv5XQC|Q{hPe6 O;QiaYujKtnRQwOOttr?5 literal 0 HcmV?d00001 diff --git a/target/classes/com/ruoyi/common/utils/file/FileUtils.class b/target/classes/com/ruoyi/common/utils/file/FileUtils.class new file mode 100644 index 0000000000000000000000000000000000000000..223b45ba3ca28f81bb962a65b9c49b7f3f1d38de GIT binary patch literal 6540 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 R-G8BqU%5$e9_2-R^ncm%hwuOZ literal 0 HcmV?d00001 diff --git a/target/classes/com/ruoyi/common/utils/html/HTMLFilter.class b/target/classes/com/ruoyi/common/utils/html/HTMLFilter.class new file mode 100644 index 0000000000000000000000000000000000000000..1bd1f6a849e2d3667db8266d8ac5b31ae4e5c7e5 GIT binary patch literal 13644 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 literal 0 HcmV?d00001 diff --git a/target/classes/com/ruoyi/common/utils/http/HttpHelper.class b/target/classes/com/ruoyi/common/utils/http/HttpHelper.class new file mode 100644 index 0000000000000000000000000000000000000000..40c2e0dc4ccfa9836c1bf9d483826c869b1eef31 GIT binary patch literal 2522 zcma)8O>7ip7=FI}o89e{pKXC|MG*vSDP`nO=@vm-C3y`RF#XdBisR~ZAJDi?4#gZ#{kF^$9(PP-7X6mSJ=@W)+ zSj>8GZ<2X%Y8M_bi&oO;%E(GBFDHDPOpuD_@q)mb>SUFoq(c>_@xH)nM}xU~Iz+zV zf}kRaZuklapFmRjNXZBNL&XTvDn^k}a7x9P^j=r?J~(QbXJr$q7)N$NBui8-gNo;{ zR>6dd983jy6=%RKu6?R|e*bj#kIybY{PO#U-&}nB(bI14Slj6qiz$BtOiXfAw+eDL zY(1CKtyHIbXAk&?2D?u05omPVt5Uh7M4-})X;oA3Y!kT%>fFJUMx{=b+@S1dxf=xP zOK(v%e*rz8H*zU~))GMF=q{b+@~HTeS+hXgT0NB-DCYB)Q79P1X{f|m?45QQ&33uZ z^t_%NHA;J@;6$@iGZyAqTb%46xse3ItVR!^^)!7oKLdNoL^F#ks+T9TZ&;Sewp>*S zQ`%C_GCiwXIVR0E7kXh$HAt79v`y>0K!|a&|HqHqks)~iMJ3oViUD!?D>s)Ve+0_ zhi7;O-ni)5at%I#UJ>(Mhky7w0>iCWpv)jRgHTT-9P@9TM$K@{KZn|usDB#j@u~U8 zt#ddU_03^*>ojVoa3JQ7`lN9A6!y5R?PU35WZsMgsJ*U2yDp|sTXD1%sT~d6+&YKT zbhKg$Cwi|S(s~tj*HG`l6uRX-vg#UEdqAx=S8GR=)`nP+!l)~Z#6q-*h894g!J949 zSaX?)=|&uV{NUDOKR29vjx!;X_y8aBp1=h67w|bQ;w!9VvM%5f&sp5StCyQ#6CrAd%K&hrAA>8+LicF?1S zudyB9lD@>^eaFI0Q$9=i4f1YLew*@NNZ%pmNhZ=vVS z^z3JZAESvDUgE3RLW!5?;SzOwk@o<#4lhK6C)i4S@GTx;o6~n;{E1iS$3vgM7g#w@ zpn!s%3U(>jt>9G!uPN9A{EfOk(luRtbn`dQ2$U*CkPjiXyz_+gE{cI83fgGjHsqc( zR}rd^(|gIdZCSM~VwCy?gO{PVFAmOnXDT=kk%p^?{)mVi9^ur>9iDpL_LAqp>;D42 C6mv)b literal 0 HcmV?d00001 diff --git a/target/classes/com/ruoyi/common/utils/http/HttpUtils$1.class b/target/classes/com/ruoyi/common/utils/http/HttpUtils$1.class new file mode 100644 index 0000000000000000000000000000000000000000..3df88e35d444e2a56034b10f89d0dfd0e98eb122 GIT binary patch literal 231 zcma)$O$x$54256(YptR^f(yl+gWwHB(S^7W4=~oDKTKz&GZnm=3lHF-#B}4@gybcV z_mTJK`2w)QAV3hHPl!&ot`fD)wIYmm-rvqX*KSeByCjS^CehaACOs&h*=vA^Fi&hL zs@gts!Bc6Cs2hvO*erg8>5?$q8>6bVmd+_hh#RQT(xl>eDOBPKv%gsJ1cVkp-b9-d TymioJ?V!g!#iqOY@(6 z@k#K(AK;G??6Y@0p5=-~Jnp*y#PkYr_LhVr%Ye`E1r z0j}a2= K36>e7)%XKKGV37# literal 0 HcmV?d00001 diff --git a/target/classes/com/ruoyi/common/utils/http/HttpUtils$TrustAnyTrustManager.class b/target/classes/com/ruoyi/common/utils/http/HttpUtils$TrustAnyTrustManager.class new file mode 100644 index 0000000000000000000000000000000000000000..0270cad4538c50d47c7579997bf7564ae52aee3d GIT binary patch literal 1260 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 bd>t^^kL1NRu}85%Qv|S?5x$sxGu(dy#?2`L literal 0 HcmV?d00001 diff --git a/target/classes/com/ruoyi/common/utils/ip/AddressUtils.class b/target/classes/com/ruoyi/common/utils/ip/AddressUtils.class new file mode 100644 index 0000000000000000000000000000000000000000..627cee5bb48307d570d8e1fc4b25a148ae361ce4 GIT binary patch literal 2228 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 RyrQXN9Ji7aC%**a{{`6AQQ80i literal 0 HcmV?d00001 diff --git a/target/classes/com/ruoyi/common/utils/job/CronUtils.class b/target/classes/com/ruoyi/common/utils/job/CronUtils.class new file mode 100644 index 0000000000000000000000000000000000000000..ff2bacddca8ae2f3cfbd4c6cea46626a1ed91542 GIT binary patch literal 1346 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 Pu2u0mZcq-px(WOSJthdA literal 0 HcmV?d00001 diff --git a/target/classes/com/ruoyi/common/utils/job/QuartzJobExecution.class b/target/classes/com/ruoyi/common/utils/job/QuartzJobExecution.class new file mode 100644 index 0000000000000000000000000000000000000000..9a54a457d6e17baea590de0c90311e904d297846 GIT binary patch literal 835 zcmb7CO>fgc5Pjn$H8Fvf@=?ACw*(dT!Yv31RRw_z0u^#!JF9j>>?!MYmGZARfjIC3 z_)&;iyDF8CO7UTKXXed&o@f93_5BBc#~3E)Vk5zI+=y{A#x26i6I)vUl+YQCr-ben z&kSKTwWWDe7nyPIb+(Yoddj(8OtrHiKPqltisW7Lbl-;iS)Zo-f;1-wOEdAsJ{l4S5$-NQ{>)ELp7uuF; z=dhB&N!TFH+bP2EU%aw?)eI8GXVxGj+soYShfS&&w+U;njo;(hTkUjVym8`4vct8@ z&2t;9+xXjpM{|9k2k2oX#+?D~;vV7Qf0;KUOAO4w7o2%UyN+{)Evf zB;x4H@_;{@5Arby1KT9+vLZh^vt{Ec?xQ2WUU(u<*jr*OnUa&_>^=Aj`qH$|NepdZ zfPNDj01}*+tSi+ELKcQ!2&yAzZ}S_XKf{S=0G9;1-Vh91&=G;aaS^MnD%J!S2`&(e TN4O%lek1@$3sSl&F~YUqRqEQ$ literal 0 HcmV?d00001 diff --git a/target/classes/com/ruoyi/common/utils/job/ScheduleUtils.class b/target/classes/com/ruoyi/common/utils/job/ScheduleUtils.class new file mode 100644 index 0000000000000000000000000000000000000000..493cbe7ab8527aa29b7ba6578d5efb0b2881eb1a GIT binary patch literal 6554 zcmbtZ2Yee>8UH_9(n<0;PUI{%qt5h9l*H+Xow%_bCvodl%S~tt31|6gD^Yar>C_}G zqfkZ%El_4!C=?2Twm_PI;xy2f4Q209HWW(Pdv)-CcPHs&E2H4w@7}%l?!9mRzwdod ze)O3K9|EvmbcC@LHwJN27&qgVFkXXO72KxawF+JrhJqUvygrEAW%muTd82IJ6vmtJ zmN4FmLt%8|4!QSE*}N@`x8p83_YT>6r)=()AKn$lyK#^F@SZT zH?%eM?$NH%qDd_^7PWViWFT#S6KFVVG6HHM-DRZW8Pn8LmVmfIpfYA@@x6W8gsq-} z9}CRv)2(r1bWk(3eY&Na6gCt~jHNUyW6~-E|A*ikn|cj%EPAzVi>TDCow`)gJi}&U zY)m&V(GLo=)q51~HBw`3o^i}F6P#)A5-MyaH9tMjGfFi)n`|NiL4Mk18kq^{+k#D9 zl2ns|P$*l73!bYG&PM6&xT$NFo&y2r1v=}!^5#C7ZD;K~3Di*9#g!>Aw=_LsnmRg~ zx6(Z244qOMyngPZa!A!Os-@T){6CJkB&E5biz@*C(umkxDE0rNFu4 zT52?@d&TTaq_0Yt`k;|a#EG@NXOHHox=b-mF(kb#UE5LVvN(Mc+CQGK^xj08Avnj2 zvK+J-mTDq(jj@*|F$qb5iU|g(Hm3J8B8l+0k+I{qhwe5;_7tOeU__=8GOHTVwbWo? zqn-Q0F(YHf^{ojz!R1u26V+-N5h}J}J5$3NnpefI@B|I4;@5amVAW|dUOu~ZAJF4W zghq-P?u_fVB+^Q}{r;fhkNA^=# zMimRiB2_HL6RJ2@)T&~M>@5|`DB@Z^>-e;&V!2qMij}gd6DtIor`b}^g^+!e4QuJW z`JfW086MYbEzZ^MbKn+PIcV8CLCxUYkklqqT`tZJf&1=JJ-kGp&ev zRWyi3LQOPLD2|%NDt1<@Vzr1WVvS0H*QugKtXIW(;(SG1po$B{2353*HXc!`*eEtB zqFogiNfkFs)#|6|af;)Z2SWi^POQ-Sg(){{&h|Jg7bQ$+bpb)gI{vIH8vp#4UIh)M!=vFw_ncCe$#Xg)jm5^r|`Qn6JpvBZU z%fp0*w`m4=Cfc11&z-nKgM@=l1tIH?NRiRq3Z%mM?iL|lT}dLJ-~uUqKgX;xy`ngW zcTwR}*hJpy{)9E&Gs@B~Vew>ZE-E=(2~WM-KO#?f9@!MT*gE-wK$fu*kKG3*Og){p zle{VeJcmJn^*aJRC4pVPMyj-m z%p^OsWQO%#a6;p8YAajXW4s$k^G_*lgX3Cy+_~HGqRU#bStbZ~(RNuF^CH#5I?2q$ zWyRJrmNHD;{Z0jz76Y_&Q+mQAy29*Zm(awYSuiw8P)@7&%q z+}+y~8{QoorZKCp^rV=)P;<9jaC*=K%Z4XCLz6Ia@ z#^VqVuv3PM*#_*%1-OKL)meaE^l=oHcN>)3n|E<_8RzCk${MpM&%(EH!65`A{=@J` z0!R3z+@=<`SFPaQl{Uq>&Mpk%Qk$ZRA;idK*dCQVg=*2E;4_U>QyzqNzBe-P8M@p z{hXgSiTSO8rkcPcYFgQxW3O0{#lo6E7K^f2{0Po%RcibvP|I3!3AZfW+31itfn{Yl zgqiu_@-pmnXs(#V%0rmZSmUozPN1&bp07WOhAe1Y-yu}xH#L<(V#a5Cxf3(lZ-*b7 z>FExRIS6ml*D)50ltO$frn0;@F{KkcPzC zGL%0{F@g#v6kJWjDKHiAXCF&JM&TFCTY&vs7~qBj{Mp#WNSsqTpOCz?yRNII6?~jK zMjJKvm9;l#vFcuqhxn}F)52#FpC&#V_-t#Al-bsdmbV{9V{>G+JzC=9Xkl|CYLBY@ z9L;Estg%P_K>H#|l0obImGiZjPc5Gnd>WgM(1mMdvraZGM^0k><+jju(R5h=D#7YK0^Qi literal 0 HcmV?d00001 diff --git a/target/classes/com/ruoyi/common/utils/poi/ExcelHandlerAdapter.class b/target/classes/com/ruoyi/common/utils/poi/ExcelHandlerAdapter.class new file mode 100644 index 0000000000000000000000000000000000000000..4c64947d68bd1f03ab71a1832ecc6c505d63a8a9 GIT binary patch literal 364 zcmaJ-Jx{|x47E#|@GaQ*39$4QwoC|t7>YpZz`*1rCcX9&N6w|d$lqe%2k@g11~F6! zF+AVBvwiQ`?;o#k0B{QX8SG}ThmbyqHWU$Fj;9M+kqgw;?xt998T0$Kr&(0r*7;Pl zcGOWhXV-~$eeZ^zwULU0JLeGouecLk79z|1WbNx8HH3p}j&rGQNvYw;+9Qlt6b42} zNNb}>X%Yyd#{yw{CWDrI>4W8uE}krPMQs&W&noJyeeyLB!labO=^)SE*{zY*4bg}G p+x{P2X5;rFQUueq#Q|V~umOqjv9ZGhQadukreVuqzATPl=MzoEXs`eP literal 0 HcmV?d00001 diff --git a/target/classes/com/ruoyi/common/utils/poi/ExcelUtil.class b/target/classes/com/ruoyi/common/utils/poi/ExcelUtil.class new file mode 100644 index 0000000000000000000000000000000000000000..cf7249c90702db87e209febcacff91e6261c08c0 GIT binary patch literal 49183 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 literal 0 HcmV?d00001 diff --git a/target/classes/com/ruoyi/common/utils/sign/Base64.class b/target/classes/com/ruoyi/common/utils/sign/Base64.class new file mode 100644 index 0000000000000000000000000000000000000000..b2bcd2b7e7602b7b73ea4d67de2b3ba60356743f GIT binary patch literal 4588 zcmb_fdvH|M8ULMo@9y3A-UMz;HVJ}8Nx~yY!lL9s9t4PnfF=O}wYkZzWMQ)#HyZ@3 zl-gKZr`n0rmXetc4?9v@J4NUyDy5DywXL=t>x^Str*<4`TbWw5YHJZpf9Kv!SQN(o z(J z(gWG}07DISXRU#4Ypmg%abrz9JD85Gvoh9DJR8r@kt;E>&WdqOVq}GtwaCO%19reg zm780uI=5Qet(v5j+EUY<%_LG==v3QoC9h(ISO&>1c1SHp60CK9B4uTEI<`W|cxp>_ zPztSyEWL$qjo;4v2d&iNx_)~klyXXvNen0Ba!Sw{xPH{iWM#SiPCPNkqS!_^Np3a#}&*f7N5%M(cty?!+!y^)w75bLUEIm1jCVni+_%OxPZs1F=v>9Ke;BD{$FopPv9O!{q`9 zphA|&_+-A)(r^H`ArrtxYzm+j3jKF098)~uIELb{*BdCzhxT!g3Ejed_ zh|zd4)9rbt19_%hP<)@X%rld-d|l3RQKSHS4#7AIHBzhWzexDk=J_Z(-^a}JeRz2d zEpdEkVW!6upp;eroXH)jK52Lc8%ihOF4T^c8pXqn{3M&o_Q6}O^|5N@QB5p!nVLR$ z!nMq$M%`*uSEC*^>Q$pYHR@NR0W}&lgXM?te6<-E3y9jNXNvJoF+S64gm;feb<^V} zaho1v&+ZBMO^<8W_+bPjEx^-vjYmVA>8QNAqarQ9J5+^DpREdzx}`2?GX*$9rBUKC z{bPP{6fQGV*PADWO}D;d&v`?-Ylmr^Kjbr(@2=||3yQ;&OUew-0rW9;rt+Bq2lHG7 z&+|gJ)3VVx6%x6_*jr{82Qcoq;8*Lbje7s*8U-(2qtHd830<%Z`2ju!_r?;}Em(mBR$(iym#UlqG?uNcGE#J#vrJb?Sf1imf~VqBcS1L9?TL%e}+iVyKE#e=;{A@$1{9gq;!ftl_z2#B+oAw)BsRv@HfwcwwQ z4oT!6uza;Rg90JHg?u`Gh|bGai|FF1(bG&IhjR6_>gB#k3?igzjX9iX%i+Xh@N~{Q z4sT)WnrDW`!lG_t z)O#GheW;z5eYSl0wCuO#8Pjrr@`Mb4=xBOtqb^B(wNbaE{@SQ6sqEYm%?yo&gy~|l zyY*dr#(Snl+{7Y%hpD@Ek;xm^4fhVy!vPxxZk%@Mgm^9l8R7*)$gPO_r`M*Cj3cs;Wcp!UKh9G4KawniVXfH zzJNEy{WvAY@prGA)Qg{v4eNw(&gaeLygDm*E6I1TA9HdAmxvmK=Kh#gg(x~rsLsW|nHmr8yWA^P@zbip zu~U4Yo$wrkYBZm49b+o>&XX<7W{fC3V_@X(7@O)NM$19Wkb~r61Qtvlo_x#LMTK<` zMUKLX8Iiu6?v!y2b&IBTi(*FIw2sq_c}7!@q&3F!9(pxO9+dW7G#}}Epx1W%mJz8} z8zM&SgVF<4R;i<6T@O#5GU{w?HxsOf!_**b554BHd@-QeNv zALeg^V*VbOMOwk%0Sj2^rKF8K>?>K-u3#7m2p1sC9_Dx<+awl)%t{2d!+(~dJigH{t{X1hfO>_55YY#9Q zM|+6cTkIV_IX(9D^qAcHB?!q|@-zz1Kp`<5Qanwh&OFO=C;o%tDas@CIWAVv_g^ZM BRn-6h literal 0 HcmV?d00001 diff --git a/target/classes/com/ruoyi/common/utils/sign/Md5Utils.class b/target/classes/com/ruoyi/common/utils/sign/Md5Utils.class new file mode 100644 index 0000000000000000000000000000000000000000..f0c39e6ac83a676511414e82025674d6be6b4f1d GIT binary patch literal 2168 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 LEbyyh_6hJWF^NY5 literal 0 HcmV?d00001 diff --git a/target/classes/com/ruoyi/common/utils/sql/SqlUtil.class b/target/classes/com/ruoyi/common/utils/sql/SqlUtil.class new file mode 100644 index 0000000000000000000000000000000000000000..a640f8ed7fd70a362f9b0a0aeb13ceee21eab3f1 GIT binary patch literal 1886 zcmZ`)+fy4=82_D+kPt%UE?8?Ly^(MUrL{_FkxNsG4VMzi^n$}In^V}j*$um!3Qisz z+iD+B`{ImfXB=xQb*7Fj%mCiXyZ?i42E&steelWpov>}ggwDX8^ZoWazwh^5&gPH* zet87oFwS~WiNjvJfg@gc@TMDYdC=yC6GsKu?#3|>ju+9Z4lmwDrvSTz+3i7(Fiwa^ z*n^WE^m=g0jXs8==$Xj)aJV;oj-fnqS-K*HR7sl*MJ+?oCfgZ`g~L$S$Vhm2fT3b= z_(XVky!-rkf7iM3NO+)k_yjjbqzRRf@`x@=>ZoKW!nQ%DHKmvgdn2+Q4;e{)RteEAu4|#BrKo1e zoK{28Y4t1_q_dQpvXtP1MvNQXvxGD3ug^~?kPk7qt_aPPL)4Pw_xh!Tt)R4@TT^;$ zNHU~2x41!S$`q3lRARYts@BlPi?5UC^dc1)pVhyscn4BKh`+b{B8ry@$)-4IB}W6 zZ-F+i<%;e5D=lJXs^^nGsVZ+z5IaKu^V z+;tm!8mpYGM_s{M*D6XKPg5)tDPD83g(rWCYHsFPN6i7+;6^*`j-w16B%zav-%V%- z{VIjC^8Dn#kJ{L1SEkbzT1K8tXr=^39ctn9JV7a3ZiL)up+82M@BWvHnUj$R2cI?h iK(;hMae4U?KV?!wL6{WIYXh|XpM{qL$?fPsJDHOJ43;YxqWY$A*0+HV?RvO zB5guWbBMTz~7{f&)Ho`Z0Xqj@SgL&=e<1d<(&QF@85n0a09I@#;~YhDUF+1 z#IT$pSxMto7ON;r<~=FCoyGh3KtUyoJ}gS|VH$VjT9wDOEY4vqwS1XA`DIbZx)j}& zG zIsUe0wW-wKZdtC@I5tuu7XmY(dD}OwlI}Ws+w%;^FKNDOn|f&_SSpThG!2h_Gsk`i z!`AdY(=k@v1|y^vOvm(>1md~;wm>434pgbI*0y&IZ&TZ~0=TZOS=*XtN4}O7Kd4?NOq3b2 zNE^PUKd5P~02NCvaJpvrdv0T0^EBJ=4UZs~qX;imvC4_DQ-`CcCfVGNuJC{ZIKAW3^tZ)z?(a zU{=KhCMnuluhv(}3ht@+6rZV>!lXQ(Q}DTpFHjU1eRjfbwN0yGcq+ccSIlBXg@&Sv zw=kz-7rKIm3Iq35G@+~5gQ;L&#RFIhY!wb%fqdZeWr`@1GuetRi2Clnp|fH=qHyw= zjq$LpS!~mp-a!Zr=JWRiCSFjZvgR9Ga@aW74emEh+gNkIrbn&SG8|6aY_G@Yraj-@ zs5G3^wD10lYJja>aT*mz_Y9lYE4U@>{i$wFHBAmgyd^^>bLDcrOpa!x2a+ao6?v}) zyB5){6i|YBsoxhEp;FKMckk`g3$hLYMtLV%TEh~!+M8#1cjy1(Hp(57e89%|Y9;wH zjpG6YE|QBOPX1C4pW?g5Q48KT4j0$+c(v#J6jHS11e0FjX^E@gnk*ba{K8EPS4pLk zd?ItCYN&ub3gj{z*CHj-_+oHE?V)%FiJvK9Mx>$jRG>Rck5bp*8sj?p1j*`9s&EM9 zM<`E_uFj-7$VlUOU_68b_eIiK^b_A8t_LtiLUUYWHsI+iJey>|*T|(I{=}d{&DY5# z1(iaib5Znf={!!^^$xPNr|8=`f?7O8|IjItLk#rF3S%9dj>JWYD=bNfYmt$ca27Wi zeL2825jqagP?;ER;0@xE_}?VfSb!-Lqk{aGfmP()jc|QWHJR=->oK@EjG?jbIvD<8 ziR_sRKOtR69Aad=gHd8S+pW@k~c}l9<5TL>8mP z2;Si?9@I{O_zUAgJPR|8caNuJF36G1dx~>Ai6fl<70K>4E$1fey!_9Ct?6S3fqCv@ GSojA6tfssG literal 0 HcmV?d00001 diff --git a/target/classes/com/ruoyi/common/utils/uuid/UUID$Holder.class b/target/classes/com/ruoyi/common/utils/uuid/UUID$Holder.class new file mode 100644 index 0000000000000000000000000000000000000000..b9ef6fafa9e9014774d6a2456fc0c0f3ef30ce93 GIT binary patch literal 569 zcma)4OHTqZ5dO+*VO<6B0r=!#B6zSDZxSyUm1Lud2=}tvV9Byg_F=-$G9EM@`~m(b zGf+3sU?}_XbR>gI>B~SwI$+qb zC*ncyP&#p-q8aZej=UAVtEZ<7g^u!7bjFZvwTBG3u68Arv6U~c(^7Y0WW>xnRwgtu)+Pk9jQnd0P4wa?>7c_EKuR<1IQd`#KIBd7%>BQgZA#xq^ivN(QzpY-7hl0S3e2KLr>n=@dNSkNM4LBAtk# z`4^eyFqYBJfidj0+P@7HO?r(yJrP4aX_34PE$W;QlOj%*kR@;K}uD^GFU^ItV!7%eUTNIpYke!lmGw# literal 0 HcmV?d00001 diff --git a/target/classes/com/ruoyi/common/utils/uuid/UUID.class b/target/classes/com/ruoyi/common/utils/uuid/UUID.class new file mode 100644 index 0000000000000000000000000000000000000000..9135b02089d8dc6105596adff2e04717860a4928 GIT binary patch literal 6564 zcma)A31Ae}8UEhxW@mSqkV!}oNrZ(`30W{f5L76T07@`HYd9LT*2!i_Ms{c89!NyM zSZryn)mm(;ZN;OO9@?taB$ZZr+uGY&d*6q>@4K|=_s`63mJo_+cHX@A{{R2}>w9l} z=D$xq1z@QP$Iyy*N1@}0hWAA9-WY;-pS-_cejbYP=iw;U;R7=IU<{AoQ4NpD%ZDO( zJccJQtYJjMXapaQp&C!h)X@k&62nLFl!jv(J{H3aygP!A%kvZR^T`;_z>^VtDuz$v zGxGDaX#8viG08tw#laex{+$0-HRcb-#wu2ic{@2X=KdoapAp@km(dqw^hNRQOQQYD z0_H2S@Kp`ZM)0*5mW%!8V)#0q*YJ%PR^pp6d<)-}P2Y*(yZD}l?`!yhh97G9kwSPw zCe>?Y74&w;v9fDZW-e#t6pWmewawI4E1R=3&X)Fd3TmT5<=Tvs%bQMqtC=cT6~Djr z-|Cf^{kX!EbS9VYvisWXJf-!NWtP28n2 z+7@h82(8WZS_)MiwqtE7qSQ^bgqZLDfzAJ%A3iZo#ue6o5i4k z0F6#7-=FEd%*>i;E6->M)QuYjYGem}6r~FD8W+46)3L1S^k&kY%u5@$zbK)Io$Ioa zg)CzbaQY+=*O?G!-eD(M;9emI8yB$Zr*pm2tM3z+wOw9+u!b5Junx6dQlCyZD#KnV0&o(*x2Y zT2I0Dy7|nLcD8v-E67_+LBE;n_c|@qZ>KVBL;^BX#8+1y|4VzWv6A@}3)l$3OwTo? z;&YMti!4i<H+Od*@JF0!QtqSEx|NS)}o40r3;gi94>j2BL8k&4%6vl$ZqY~jOrx{%Aqd#w24 zW$_*%k+|)|QI|JHFF z|Ix7z`xWMogWTejT)K3?%9_HXWk~Bb(P=X-OtYwmJ>dakEp}~aU3}T)n=aPyUkURV zS^A{pSVdxP-ommkX(dGRh*G)=;INK2Wvnw<3TxtVIYjaWOVPp`9NN0c=E#Fg3?DLe_vWLko&~XYMFma%850bRaWD$;RB%t6ZB9CyOTw7sx`V!Z*leUH?*}BEuYWcoAN{|9- z`#qROKV~rcb73*FJD9~j2ByC(EE_p1B|4Q~t9@J{A@DoaW(EUp zwloA{Z5pm|W5lfN;NB>9vLf7K=0~mPNk`4pQGPid!PL&9sP3+*8Nsw;iKb!Hc24xA z<@CNt%)_)}_&zlu#+mClcLQe!`L+kO*vkgohk4k~%DoZiV~7=blk3}3R!W$%X*l#K z#(y&yAYw4b*U~po`h0(NCTl%g@>fToT|@3TVjA*Ar>~_z6b0<^oK|b8Jj}UwHPzG& zV|qv8smNi>I*K#8k77pm2+pjTIgENbUq6h75u9}___7AJ@tj0MaeWro5)J2iJGu@8 z*v~&q1kWAC>|w-P2pCk5O=3OIyjUWhK{vy=CvZMd6l`gRtX zSJGe6^fYq*D95W2U)Z)n9jm`pf4QFvrhVcz?E%gb5HV!4u*!-l@67IXI> zd}soKXUddRmJr-nQnH?*5GC~jzZUc4VXWF~9o*}VR_{4k<&Vl#)|XTc(J8ORwy@Sj z<$My~*@CSW^Y=67GBgarT(IAW4-UDn@x4RiZpYjd{h4n4rOd>9-^5K+DGo2^->Zq= zGbZ#Y#;Sc|^dUiGd&>ghs#Ot$RJ2TO^(1N!lzdoGimm4Z)h^mU0ja8|%7!wP#rT#Z z-m+#E+oDm^Xr! z&V^nm_ZX2;EM|*t3)bI@*g|8`FwWiPEgE~YQ>~q9K{HxTDWn;?ua4y3Frj7DHcV69 zn5VX5fw~ec>Qz{yuEu)RgAQe(TkXJAsvnlxiGoVudgWk9U5ndP&W(Y0{2lURK#y^d zj4wPtgxBHqjE9`c=O8>rRtWG^=BE@-5dJsDUEV~{n^4CQ*KkYyjQV2t9;B$k8%pJW zi67Q!5<>oIuU<3Ti0?T#``}IEo#YkJKE%|17vXeb0P%P?h#0B%Rvydze8d|`S6*G- z<||&J5)VR83}Z=+{vc1~hY}xy-YM1=^6${y5X&0J8pm4b2tq+I>>Z8n4MDvTQ`PM- z)E%f(Z!Qbbl2VA4kRjhh^L4!7+`(&&7a|>@sa^!GTBC{qxZMo^Kh=Dk2Z&R=X%k2Z zf%Fb&fg@O2dcVhoZ?H zNgy%#X=oVdjdzfjpC=JkN6W%fM@t(hOmV6d z`VaF!c^l_S7bvm!7XAg~tDB&tO*e5_+H@L^!(}1&e!IN8=KK*{aBTceI#qg6eG(D% zY1F8vU8^3;7pp>Ql!|It9B=3Oo#gG9L0)9E;)*o-Q=y89dj+p6`**5@C-al zm!9Jn_VXnJ8uh)CR|B{KWS}qN;mhg_#;9u@zgyNL_CLPUyA}Wd literal 0 HcmV?d00001 diff --git a/target/classes/com/ruoyi/common/xss/Xss.class b/target/classes/com/ruoyi/common/xss/Xss.class new file mode 100644 index 0000000000000000000000000000000000000000..404ec7a498df5127b5db6135be791bfa38c560ce GIT binary patch literal 807 zcmah{&ubGw6n@jD*|zaVtJSvF+Nw}m1qVUARD_srv5+=lvn@hS)6KX{nVktcv!#0! z^rqm&i+J%ao{L4j`*%diUm?ETh=~~KVcxuX?|a{S?_<9G{PYC??!k2jt~s#ez%l_R z;>NI$6PQ_Ds~)gJMkP}rZP-|-aGk((St)Iq6fmabP@-AQ%Gzw{=}F&*=hvly+m0N@jYS0zU2Mg zaNOmAS=pbZsmCqH8m*9a4BI8hxN-`#uB8alYzuF9$KTp^2`mmT@mU;li^vHj#G4YLzlo z{787?DKB~9MM*&w#WP>S7X;4it_#^Rr2Mcmz1=;hPxo}spHF}N9l$l*){(}lPU%e@ z6L?F*+d2|h)$q`13&)_l))T19~~o37LMrQ`3(&cG6wDA|tfmj&Y4+@3&U(``~aU9lZ& zd(hptyt>@)P?M>+4cXa~o~`~PWx{XUeSu4rhTAp0f%}7PQtG;ndC>2hpZEI=VmGDl zdW@{u;jh3(cAQzDIWX^s2W&zFL_KK-R6`W1eCxn3=Drk2(p4p}&`=4T=qIpzDp*#h z=F7&ts_aDxrmL3UcALA>lU>WVJSJ-}o=;Z&Ex+3lm^~qvfL>%jHc2ed7rh?2uoK4l@$yzj{g2{~ojUAVJ~wx4 zry0CGYNA`7JLn0_WTQYwU#siYCf2fU|7#Xv0fcm(|$1)A}7QY2#@B;T4zQQqR zd{a5gnBzK6jmcebPv##({1UjF=L$~{dw^u( z5rz0q&=L=kh(Dqw4uv)qc+E2SJW|9|s29SM5h^k8Qb1ElFVh-h5{gGWrC;aWQ%=R` z0vai*p@>NQ%soNf)!&f#;xUqyjP?YR)usFpsqN(<^aq$OaDQ$HBg=)Qu J*Mt3ye*n&)*g60J literal 0 HcmV?d00001 diff --git a/target/classes/com/ruoyi/framework/aspectj/DataScopeAspect.class b/target/classes/com/ruoyi/framework/aspectj/DataScopeAspect.class new file mode 100644 index 0000000000000000000000000000000000000000..a52298e2a01c06194e265ec9ddfc114b7897fbc6 GIT binary patch literal 5560 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 Ob~6lebUEi&0RIK;)AWk~ literal 0 HcmV?d00001 diff --git a/target/classes/com/ruoyi/framework/aspectj/DataSourceAspect.class b/target/classes/com/ruoyi/framework/aspectj/DataSourceAspect.class new file mode 100644 index 0000000000000000000000000000000000000000..c05f237c5e74168a9d4d8489872a69303e4a3311 GIT binary patch literal 2924 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 literal 0 HcmV?d00001 diff --git a/target/classes/com/ruoyi/framework/aspectj/lang/annotation/Anonymous.class b/target/classes/com/ruoyi/framework/aspectj/lang/annotation/Anonymous.class new file mode 100644 index 0000000000000000000000000000000000000000..b1dda2b66e20b6f86fc936ae1e4a9f2c3aa088b3 GIT binary patch literal 467 zcmaixzfQw25XL{3wt)hrz{G&~Bh-;BFfmnX5J;3(As`_pHyo8iVpoY%lviWm0eC3H zg`q(r5`)jDyWe-VKYxC`e*iecu7jR~Z3jC7oja4KAziAN1iAxlbdi`mJB{Q*3hep0 zwkn|qm8oe=1FemfRvDcM?BA4(8%sTN<+9-hGM$mV5NIuAob%A*dd`a}A#HnI%7c$s(ns$oe)m-=hK9uni z4MjuZVlufW-#K&V-jC0>cK{dIx3FVj*TSAaaX_JIcuK|s+oxxDV>ywwCxemQGm#2L zR|3^W5NIQf(m|7kGVzQ6*qOu>{Wwb@MZSt-Mg&SdokT8OD~|-qO=)D$)ghhFq6H4R zNnn&uPbyY@kJ4;z;Beu2Ie*l4WHchfo#ZFdOE~moIoI;Yr@%PVkQgqsTh2|VDR8u$ z?vi1cJ7%{25AvWrD2|VB*Q1mE6(Nwa>#R4m{l<>7FPZW#a{xO fOAKeQiZ#A#9JI~>-_`38Fn>nY1r1VBajPf>6JUxtqv7`4w>Csyd zzUnd>lC1{1EB(Gse(XiMA(iX~acGk@9@3RSx#s(UlFA8ugL*P{mB7WntL2-uu4Q%v z3X>++j?4UtY1h+%97%z_PV6hkqkAWEo?J>fL<0Mb1T!Hmf63{V4Ev;*@$u}q>5@mj z`WY+VG_9MKA&|8m+NQwa?4%AU&f(d)ZevVLolBjtxd_0^cEk%B27R literal 0 HcmV?d00001 diff --git a/target/classes/com/ruoyi/framework/aspectj/lang/annotation/Excel$ColumnType.class b/target/classes/com/ruoyi/framework/aspectj/lang/annotation/Excel$ColumnType.class new file mode 100644 index 0000000000000000000000000000000000000000..37d54bb9d3209dc9d1785b31e00fda3e08b86522 GIT binary patch literal 1606 zcmbtUT~pIQ6g}G}O$h-@<*NuPTD2`oML|(2AJ&4MDIY@%1LIRD0fK45gcRk?AH+CX zP#hf{p8ZjdcQ+j=qc2V~vwL&0XYak|-rfBC_4NmUQOvbK!l;TdJW|lFU|dB9p77}@ zUzrFas*=rc@GOjOZYRU&;dUyFUT&uq%rGbmEAzSKe3n7YI##t}mk##~1}$&fhMTqY z{e5GfVX{zmDrvXs9GdAZSFadv9QSov-`_LJ-fr5`?d`N~+m5GuremjbZ_9=iKX*65 z5Gt0I^9yqfa(;ehF2^91a%&}m2f9_Icq~t;#8+nuE4d=W)T^fB3BQ#2Od{E|Gb!tA zQjV7jrfn=#D;tJe(l;#nqKGJqTMWyI!mfUx`=uy)u4!*iB%2ixEjVS}TGd^XhpWew zY$`uP^zV2Kk$J=0aWWnLnJDAlFLmz)jXo#XqLpPs#_|%IohW7Jb zbBT0}%a&tPa-E4}gZ#yfUAiA6VC28kVi;@em$--%4X*zjv5T2dtnS8rbsF_+dW5cg z2mL%k(=o0IAWO1@C1t{rIATP(dLYur%S%{B3kGl%4A%(VAj==M2KE_&GXy^nVz^Fo zNUR+Ze~@?q!5<~dcc@V*^#$@7B+d_9;NPS#n$JQUw+P*)Av46^aVqrzLE-ErS&5dk z)KLE^LaBjIP(Bj&-L=yyP@^=C(TWprx81bHohOAf{>H@xMS!*-QW0Q?MvO>zX#{YO z@`z!WX0A3(mS-=gPM{nK!gZQ`pC5m=Yf2@^tJsZ{czR literal 0 HcmV?d00001 diff --git a/target/classes/com/ruoyi/framework/aspectj/lang/annotation/Excel$Type.class b/target/classes/com/ruoyi/framework/aspectj/lang/annotation/Excel$Type.class new file mode 100644 index 0000000000000000000000000000000000000000..285243e3270255629a0404d3b24a58225f6877c0 GIT binary patch literal 1513 zcmbtUK~vLE5dL11v?YX63lxihpjF$VR1^?wMG$eUQ-ra|$apc70Kt$jX^L|5hcF)0 zg9i@^Dvpj0XMdFAzL$=a(TmWTynUPZz1{EI-Sp?LuRj0`VzL1;22@QsZCoR2FF)9or#j>-W zb*s*nnO%1Eb>o@iKF#Wt4WsO>Wi8!a$?CT4c)Djgc6RD{*|3s4P>vxu^=NkH!5nQ0 z)BZ-;)U7JzUH)uq&O zN?emtX=+h8U$$x)=mkZ)S4V7$0_KmZe~iXMEjd-UY}_$tn#vV1C}qpBiBW4RU8i_g`G zz#)7Oz0rIWdMN{NmL7$n_XUSi=^=?=cge~eoayV_Lon0z384?<`{5#V3cMm3uhED% z=pipoXFT|MA&u|2I7uXEl5imkr09tgC{0fQ8R8K~AFW(%mMmwlWIiJFP5=&sz7-jK z;z}(;7#DG=cKHdFj{k0dgS-$6OVS=1cA*NdQI>_L39m^}gck{ZC#bYT^SvPNKr1aM oJBXBa!MIAl$f{8El8A1T4xe;C5#{7BQ}d|Qgp}#e{ls|UH(3E!p#T5? literal 0 HcmV?d00001 diff --git a/target/classes/com/ruoyi/framework/aspectj/lang/annotation/Excel.class b/target/classes/com/ruoyi/framework/aspectj/lang/annotation/Excel.class new file mode 100644 index 0000000000000000000000000000000000000000..0db44532cd2270cdec02b665a7990c3d0fab5254 GIT binary patch literal 2135 zcmbtUZC4vb7=5OB1Byal6?>|+R)m&zseQ4whzZF8v4LWe;<0?_8Fq(c>1HNpW|o3q z{9k_X2l%7JyBpFhDaW2@&dEvk?tSiapO=}x{{G`nB6>*M1^T%_^#U~t)MPYKR$;=kk3yUI{rLD8WXU^F zxjZ*t+vf+|3At=J4XXp$dd7&5%;#3DD&64_8}kFt_T;Cv;C@-j1EHU1HH^HgV7F}gB0zjIb0<5CEJL+BQQ(2-ZM0VEm=44k7+ z@iRlQmV;tT)}n;Tz%=5P85?*8Tu+2yul6Fkg@lfd)KTE>C9 zGD3GSfzYW+UmOaI5NrcScSd4Qt<~LMUq4!Wz2(-+?q-wG`JRuH?73got4){D)b6k^ zj4JV$Td9@G&oMe41}(|x!3X@VD;@l%q~&2L4(JGJ;rjl+x;{H59~*Y-j0$Zo{ZOD; zmrm_}a+)B%m>Lr1-BHqsY!I5HnaNPsdjch&cX0|3GTwkUZX=CFyZXN9((PrCve+_g zRMsXZ_$g?kZ&SR9kb3gpLo=mX4J+SJk@m!D5W?|9Qq;ZpCSWvEkE9JcVmmOwZYTyf z2&~^o9kSG-*M-GcaLK?~?x1%@XFovQQeog7A)ruyx!J64U}>(O9nwq=Czv94&AJNBsz|a(c>0#i)FmQ1gxReGiL*q(1%T)lMq=8QX%+P23 zw3Woz^)&D~7&p?Qv9T|Qft$m?S9Gf{xO0Jn!V?xY!U$O7Fx z(Z~t9M-HGxx<7!vr3Zi>o`xO)di*~0-D&6vSl`prfz~rx0`vndV`j;WJo=G}7}hx| z;l;{uePyZ)f?Zn0nAZ~aI#uy|4RJq#@LW)hHfS^cyF*OS3)-SQ2p2)f0?id?sDR(G G;>>geK literal 0 HcmV?d00001 diff --git a/target/classes/com/ruoyi/framework/aspectj/lang/annotation/Excels.class b/target/classes/com/ruoyi/framework/aspectj/lang/annotation/Excels.class new file mode 100644 index 0000000000000000000000000000000000000000..33a115e8a99196521713e10b63fb21bd0958d9bd GIT binary patch literal 473 zcmb7>O-sW-5Qg8WjnUSR>P>GV|`o=i57gGi>|V^wILMMerudluS53Iv(_5n+ADlD;+%LD&-fO zKL@H9bL^79sB9FdEVE8IZL=VJjoDlf8uzx$WA11pD$QB7I7!q*5q6?7b2{ZmUFf0V z%kRepVXrr3eyR3VK4K@bhl}q+!zpL3UyWHHuhR{CSA>JbaKuhnX;#qoAIP0GI<5rt zqlcS*_c|n;{@V_jt2H0%c9@8Bg#CHxhktW7+5|#P){^`V)d_WYa;jl@7JaP9zAO2v RF!ce5(LfVx(~Pl>jW0L8j2Zv{ literal 0 HcmV?d00001 diff --git a/target/classes/com/ruoyi/framework/aspectj/lang/annotation/Log.class b/target/classes/com/ruoyi/framework/aspectj/lang/annotation/Log.class new file mode 100644 index 0000000000000000000000000000000000000000..f0f606e4a9e5e901ada5b79651633883a6316c2b GIT binary patch literal 991 zcmbVKO>Yx16dWf_(v|{EX@Rzseo(69!d^feI0Yq8q~=4Lz@a%g+t1n z>)J%c!#4uuS{!RDt)6oMeVM)mp}U}tI znC;y-aC0x_xCGX-`ECzJJeUIe88pYvGCUDH>^c6@E*6&rmQdhRkxzMp5(CRv`3n>X ztGL6^8t!J$J=|w#9p#zq92*QgxDIS$YsToadkzo(tdH=R_hbJ5Hec`bkt6KlNxJOe MX$lecaezbMHz_|DrT_o{ literal 0 HcmV?d00001 diff --git a/target/classes/com/ruoyi/framework/aspectj/lang/annotation/RateLimiter.class b/target/classes/com/ruoyi/framework/aspectj/lang/annotation/RateLimiter.class new file mode 100644 index 0000000000000000000000000000000000000000..6328244d12eaa14a38db9670ad00490a88c9085d GIT binary patch literal 753 zcmbVK%Sr=55UeqYtMQfien&k7b8$fgLA?lx7>Ezz>cxXh);3Js-I3kdNPf+OAK*uc zy}q)CfZ$;mx~Q(HuIab;msbFLC|Ovwux4RhAoEB)f!Xa!qa(Z0_GHkuuS}?d_Mt%W zI0&?nM(LnR4>INqe-aW63X$#Z&7%-Q2N zT_>JZQBS(q!|s2YtZMD#_`2Z;r*upJq;$*{FtS9&qaZVl$uJn|{{ znPd2@=A2$s1vUn=n`D^GNwaYI56Y$Xl-pyX)x17;>Sr~9&A}~I?Z$}-x@Y$OpYf-r zOhv`P^_SzK)#2|FDD|OKS3ke9fWQ!+WEHYmUKQlH8s;h!!D3(pqd!0$c?QPPz&HaF onEYns2vaDqUXlBUFpU|$ORPQ1gt=P?!aNqRm{f!%EMo=u08%v3od5s; literal 0 HcmV?d00001 diff --git a/target/classes/com/ruoyi/framework/aspectj/lang/enums/BusinessStatus.class b/target/classes/com/ruoyi/framework/aspectj/lang/enums/BusinessStatus.class new file mode 100644 index 0000000000000000000000000000000000000000..de66c89b6fd34a5f95813dcad729d3fdc01165b3 GIT binary patch literal 1215 zcmbVLZBNrs6n^g7t!wFC*mMfw8&2H-sZ)r?5dtodEKV?~kPtuJZ3T*5Yx;umn?FfM zB1WU(vp>psZbx*B`ejYp)6?_Z=bY!9(;q*-d zflGt&DYH}&r_w7O=bX7YRLTZ%tc)pmm8PzlUgUSIOX5|k_7N>F)w(>~(T2l=pgsiq zJ^fME`dW>C6P^AoX>Cd*2gp)>AU`d%QACD{0*au_?CGfaq?;72week7to)R3%3-H^=&VG>yhAcH9>eVLFknMh`=0d(`W zPy2h6RIOXPwOc;>;y>Ejdz+ME>Ao-&_w?z$ef!+ow?F**=RW{$!-_${Pde6-Q;}A2 zU&jpcYzi9A>$F&44^zWM#tjXZ7&kS{GCt5S$5_;Gnek^0Nydj7t}vD~TxHzSaE-C7 zq9PEf)E}5Nf#p)uKg8cL^_+IQ ze)Q7G3Pg)#%dFLD@VHvY)lC`{%#z82$Yw29<`wfvwIW@LTm3;~qf*P0hq`H&^OXYo z=1XR-Of$*tT!yPB6+dD0qQDyMT zbAe*2bYQ=-`=zluf$QyM(?d#5l>DaM+O`9iPdPbC@#q!=5?>!9U~D;^eSf!V^G$V} zfU=FQx}Muv6FBz`Va4>efYRA_$x%=8MJD^vNBhJNc3sbI3B)XS&$Bz-fKCplc$c=? zY@I~j9FSsCjMbsiU8AniEx#Kyoeh_3IzHG^3tZGVQv3kYaa8e0;MNeW8OH)@ae7tD zah%76I4-ie#AcSw9GlB*l5DQ9xyt4mE(o0Ze6^e?^=Gr?d!+SDDm_qy%FY2j56WkG zu;%%e-EL3mr=9pOY{t9=0EA_*CBiT*sq@!o-XZ#yhW{5y#U0K7=6F9lASwooRI80qwhH>*_08J`=$;3Z_*L&(L-t|J&ah5NDre{Bih55 z)lhpFw;Eaxr>%xTQ)dZiZi0X&CkbLbOk2kgT%G>CEMSDjqx4H)3KP_yGgQ=Bs5GIIGOglA+84swe=_)Cz5oCK literal 0 HcmV?d00001 diff --git a/target/classes/com/ruoyi/framework/aspectj/lang/enums/DataSourceType.class b/target/classes/com/ruoyi/framework/aspectj/lang/enums/DataSourceType.class new file mode 100644 index 0000000000000000000000000000000000000000..6cecd25907f9698ff5441f12a37d255b0422d86a GIT binary patch literal 1215 zcmbVL?@!ZE6g_Y4)~?t5b-^5dwjk5C<4Lmk>X_jWRmBmUJD4Z~jR- z5-}PLpZ%kZ_q9Yv)GvEUdwbtK@7#OtyZ!O=%Xa{ac$|cc`x*);Dwt3(r$NWOcrB<% zX%t)#hzBahg)J#C7!q4+X1)4~;YqFKbq&Ao9oxpf&%4$;&wp$1-l5eBI)=mDwqd#b zZqL}@0XMzA-?Hk*hgO9lZr0WsRR$&BSgXCRnha0hj7*j|;!fY{F+3|2M`w}Oy*-Oz zyk@)BcE8)S{5o$sw4;RdJNpbT3bhVD;*l~l@NKtUDUMurw&u0C)8M`>@|-7=U1}pk z_U}XtnJp_g@b+GE(dxkRNpWJ?cI{w=A@`5cHj51gIXJKhsO7~?jcsw&ZsaM)b(m@b;oKj%v(*rZ3bQc%rOrG-nQTS!8?<8PV8pbGF|Q+nYPz$ zcTCG^J7MGO#4a;P8{5Q@-(9tKs&$5iw}X(xPr2K*I}A&O;y?t-ntxz3q%Fs@H@oe9 zJ81BIm%gYG^VT6ltzfnIDew8Hhk@fAm5YNko3;EVcXxT<2#ISQ#iMyKWdGJ+$h@(` zWB*{AiWDH7IO@ZZ&3EKs(gvW1+*y9l_(jCeW#Lq lPDgD|Imb}_0!)baY^S20QZ&91YF?w3dMbImAdG~UzX3&`5pw_l literal 0 HcmV?d00001 diff --git a/target/classes/com/ruoyi/framework/aspectj/lang/enums/OperatorType.class b/target/classes/com/ruoyi/framework/aspectj/lang/enums/OperatorType.class new file mode 100644 index 0000000000000000000000000000000000000000..49ec28048c105fa18e509578f08d3d11d17f4eae GIT binary patch literal 1254 zcmbVLZBNrs6n^g3tzGG0tB8X04To%?ROCgI5yB)Bmkeeck`O;##sxaNcIi3{-~35B z5-}PLpZ!tBb339V>X$X?J@@oJ_c`Y|=k&+VFW&(y;z=|*R=eOcVb%yK5vS5p8wY3?Utwq4a?!~ zp(Wf-vu#yd!smhKZ=bY8fg!$G+$wIAXHmud{lZw4hj=1xbn8J_0y zgK{YA-o9WMso1XA>NNL+zs>g?`l5#9)dPlduF~Mgyr-@f__ljk$PbvZS@G)J+2Ot| zqg;eiTq-1k`F9wG)TRiIy#1G47CR6=sg1ALt{tp0jQu0Aa(;(F365+6>RH)~Y>z$J zb3A|FcDcilsM&`u4>~?Yj^^YoZKdv9FmLpwQX-C(f#j}I(OkYyVRf%T z<4egd^%r#A;q7*z5B9tDmstyI^Yohx(?6!Hjmr!GvXn$aB7e5;{YnW5hxy8UIGOXqTpOSLtG?wj1jrSvv)j`SdDbhLlMp VsizYZWSRm-E`m=ZN4N;`zW}9?BTN7Q literal 0 HcmV?d00001 diff --git a/target/classes/com/ruoyi/framework/config/ApplicationConfig.class b/target/classes/com/ruoyi/framework/config/ApplicationConfig.class new file mode 100644 index 0000000000000000000000000000000000000000..76ab7ae5b349a66aa7b786585cd6f67605e0c681 GIT binary patch literal 1909 zcmb_cU31e$6g^84n+TVLkWwI&&(Jz9SmCp!=F2fKz!=IjnZUr)THZLD$h(@=D#ZLM z`cfDk_yL{%rcUolqA^615{3t9_pa_eckj99>aTy^y$5g^O%ElUoTB|%4?o~X4^_;S zQ7hvohSGJZq`k>7Qk`377;WjUV3=r2B_1dJjxbBS6Vl04TL(N`=0-X@KOD8IGG?f^ z1Ksycq6gAnF}yEcYP0SKTCK>Q-;APA2HZ-mTIorH;p7?*)?=-H>#T{uKH^a%Oe=}4 z?#oxHC&RPqTw9x-A4f*2-WF;{YwPpG>I^8Dd!85QZ6etDer%-{(|6&u;xPE?{xX>{}nVl&v3t52)=4<1srex2O{nh~%Bip!M?c-7s|L;!6jT~IJcWQp$v{rKjf~-^$rF~H?N>n_|yUf!l zP5_zDlKHUm-wY4?Lw#x45ewY6SPB8JKXrRdiX z@;DZx?Z5Mjr+nvlS!eHa)eS{Fn*}zq`ck(=>;6u|Cm#ha4I=SnAG4G@$L1X!zHf_% z;i9yHn7Ykyb{juM^)MXY*$<~kaU~iiUo;UE?8gLox!`%STyW_f9pE+XhNF)SMVIpEN#{*52Y^Y>DIiC>M5{v@uf4^{ zpBR1m=~afo#xad=$@vgHV+_*-I*RYe*n`7x8D^-+BeWhR%$esnhVN-}`~_Hy6Sz(v XJhFeqO*F{u6xpuC(-d?DvpDlFWVkYl literal 0 HcmV?d00001 diff --git a/target/classes/com/ruoyi/framework/config/CaptchaConfig.class b/target/classes/com/ruoyi/framework/config/CaptchaConfig.class new file mode 100644 index 0000000000000000000000000000000000000000..eb4e93e8624b084203ae574c319c7cc085552fa2 GIT binary patch literal 2435 zcmb7_TT|Oc6vzMK=8YkQ3PMAY5=cmDz(^1-;T9S$C225*1WH2Fn=CK(f-E_bTuP^} zea&O@lF4UiU&2h8=?Ca{=~w9Vtk(A61Ss{4tg~m&?m6doc8~u0=Z`-Dj3JES^Ei@N zj^SY(_455Fjz%mqe8KQoiYp8$hE;|%!y3an!xM(53>yra3|}&AF+5{<&aln!6~l`d zzK-K6zLEQV%kUk;_Y4+8hQVgYGH`|+h8#nlK``tx>@heo6k>2SG|dWEgmW6|6a8Bn z8Wz1Q*KjE%T%Il!GTh&^G7f3Y;xT16m+@c7~M1QnC=Xp7Y)3Pi#_mqJt3Ps0U;yYH! zsVtIPWNS1UhW_UU@uKe)xgQEn(RZeHtsgA26bi>&ujN{*^6RV&PEXn)U4Y2zDS$+!8khzeL!2t($qVdA_|sT@mwln#a0D=U6d#mF!SEjJ8W zFa$%x;Aw^@lvYE>Nu{8Im(PQd@%*fWP=CP5frp}o8~$VzneBNM~qxOOhjMJ^v|sFQQumAMZ+1mY(uQr+m}vE2h61XOcl zj~{4gF0WPBs^`R==yv#q!`++=M;kjcC3!07G+mshd*~z=slGz;xK`QN%LF#*c7$z( zo^MFV8WL0kLlujdWVunXmea;Y-pYFW4@stYZ=F`IV<%l!_GJrvP#{%qsxlSi@sZ*2 zSr+M-=vXAZ;jrK1Ba;lorF3J(|=Q>Y{Ll2Ey@6QF-jwizJ%js zB^Uoqn>En;H^kEHm@Ei$P=*KPwsQU~?$qlFk%E{3;78AHfNw$&YB&NvAE@GI*45d0to?2<{ zK0R|UpauAhqz4rono-8__6!`K=-n!@xPC(kCvoXfQ@Y-(gq!*;CG_dHmC&KzQ9_l} zr+PwZ`t<=N{1>}4+9{(EVt1GF9V2!VL~D}BOw(wV@|+`f4~X3Yv0EY{%S7W50zAgg tNZ}Wx@rq_&V;yhsJbdE&hg)pjwWEsx<6@Gl__feQct literal 0 HcmV?d00001 diff --git a/target/classes/com/ruoyi/framework/config/DruidConfig$1.class b/target/classes/com/ruoyi/framework/config/DruidConfig$1.class new file mode 100644 index 0000000000000000000000000000000000000000..d3bca6635ad5c1443234ce083a2ba53e946cfb3c GIT binary patch literal 2167 zcmbtVTUQfT7~O{iCWOJJQBbPljZz@ONQ+_(MFpX?jY2`OtxuEW5C$hRz*+eP%{XB6hJ>S&KQh@7v%0uKD|)KmP)-fail4#%Fn)!om=4;?@ur zaXW|4^SFa@4of+Fk;gLb=5S9x-OpnMs|FqzSTnFLFc9rI;lvGrsY=anTB_y0a;#lt zH)Y#bFRYsH?K%zXfoeJR)p%c~&XVIf(Xv44#7fgU0@+o+E(JcQIG%jmYF4FsVpm<> z6f1tsc6V&$=yQ6R)x-qMhn^?Zs%wX#*6u`7Q9rAh3tXD2 z?Ar(Sp%qGX;L6BacU%gj9%{1YZpV$lh5wQl$gdsNWDq&N7aI6dAXoPjM1kkMrnVE4 zTk>T~hEcf(_~!{S#B4S+aHUu1K3X|0(He+jXjT)wb%o_pLC{d9|v( zA6a%Q@{_^PQql^PA4nBBGEBzAc4SAJM|b7vimw`}`rEk@ckU6c1+SK#(BK=0xl3q9D7(KF?6zzLk4N<~;gn@nl?z(5~W zR>w65L0c;dj2vTrdIy*J>vIZhU5G+Kw<&*=vivXNH2QFcyJfC@T=O%fUm<3GL*Fkv z^ zAr@-(HO#kc#yL`eedORWLLU7MX!RvZP`HIKX1S1%dZZ8DLx+)rFLxJjBUM%QU-Gr?}LRKT(g+R Or#Q!5o>IeH`S&l&Cp*Ic literal 0 HcmV?d00001 diff --git a/target/classes/com/ruoyi/framework/config/DruidConfig.class b/target/classes/com/ruoyi/framework/config/DruidConfig.class new file mode 100644 index 0000000000000000000000000000000000000000..f31b68cc214de8fbc0e5320810ee8fee7a16c460 GIT binary patch literal 4887 zcmb_gTX!2*8QsT{Jo02*J4$dXLPJ8*;QJ(yOKYcT9mi>6NzDd`UjPtl%pOzAB(y zbsW>1v<=@keSyyNmSY7M1QPuNivr0>w`26zqG2x@o+anC z#bmH*QAU5R=$3V_>TX*4vS*adJFa(IFS^dMwW440s#a+-9FGh1mJL5Jy-P-5k5@dmVtRpP`jMo$`h0vKw`Qyx8@m6ptv3WxNl!`YZ0r;`w->@aR0yc0Ca4io z!?p@W!O%l3dc}3AUMg(C&^u!Wt8Qt|kbKZ$(v+RAI)PO-7cJi+hZBzD21a1H4&ROB zTyI79E1u=7v``qB8-Z@rx9S&7!=Z^&vHJ_I8%W;6LrrEy^jE!5TZ0$MNv#`pl}f00w)HrCCCi2{%Bb|l;>aAkLhMm2Dw@ThvG1YEKt4?1y1Bg39` zYB)EEva{lu%hm?N(;=Gzis{HGED7veHP%VlVq`@!@T4)*{qd185E}&zll4oRly4Oq zR#JCgyextJf#I!~L1Q!B*b{EntAS=sg){0{U33~wp;zJ?#*hZ=q)S1#i@){3}Zy;lvC`hTq8CmMc=p9vg{Yq|ml zLL_1Nn%XFu6yp{zh$VuttAQEiVetz?XT_mD7{3j*FC%5=2XwD*hbNuudT zlEBcOzn_}gQ0S*F-ut5K27i#$-P_t#r9U4rN~H!`I<2q&Mhu`u{;)m5kNoQd>o<<` z`w7l;J_Vnt!MhOea?pVjwN2oGk8-R<8_wXU%TS&|d4R0a& zM=nG{R6aow3Xe)bN21L&K|F?Gj8LFLOru27fs@>o5?lsO@zcYlkMa3&ZaVGb3?FA7 zO!-L)KlO0RpCH;M<>&Au7oPHQ4r4bUe2n9~kEaP~6c_kLlKBRbp@}2+kXpLSQZoD} zC|gJm{TW>eyfDbA%4ufb7PNclUK$#{i$^41iIDURkArH`cnW>I7#yRH25%#E-bUzx zXQ(($rO#r5Bwge=>85C9vccpaUq~i1A(Ns8{Sv0Q#&&_{YRKQ_GsU&+$b0DdJ9@{G zL&Mo*1p6JN#**)HJ8*!w_7kNHk-qT2RdivR>(@BX;W5mHh)?rf7gSR7OlVWKjy)S9 zma?+Af~$ljN7Ec7>Q=~=XE8~QpWs^s9g7Nbyb^KJo!}erBzOjx;irST`R@tL)lB&p zb*H&LJG6z&3?uTuKcLD7?Z_OwHj?>3rf&<6jwLgXjiv4(yTr)<;8^E99HPrUG^S*g zZ08mp&nn5k;_y-;o6K+F!&#-_rV}&`c#&#u(&$eytZy-$UZSy^431YA4X=`l*FqxZ zBPezAXYoAd2{%Rfc`OoqAD#U|X#NqVz$ZCMQrZxfI7(6GQEIxu1UW}?Z&I5QlA@vG z-#DP))`WsjZ>P=kge9H*Xy|MiNgXwZzZAu40tR1qpb&18g`mj!NBEKPk>ykVE%0Ag H*U|M~Hobh2 literal 0 HcmV?d00001 diff --git a/target/classes/com/ruoyi/framework/config/FastJson2JsonRedisSerializer.class b/target/classes/com/ruoyi/framework/config/FastJson2JsonRedisSerializer.class new file mode 100644 index 0000000000000000000000000000000000000000..4be41ee1b1f3217c3d1fa6d56ca01fe3be66c5cf GIT binary patch literal 2811 zcmbVOZC4vb6n-WNY!bFW)ACwqX-i8WEvu+4Er`4{5iq=zEJZ}cNwS26u*=z9RQTXm zKlu~<50t9qsO?8R#~A48biv4UUW1|Nx1`=*5!));DF((hhOAIL76IY+a_HkqZbVB>{%K^ z>uLR{s*a4&qWV$Y(48@c>+#b|M*3ecgr>|q**B$iLz~~*$!eC$vqh4`(q@hqUvNto zb6*&8Hg%ifiOb&FGxv36!{R&Icc!(iy3vK1*6&w=)`n6!inmB0{az zxcj>$?6J#&mO^cw)0|B+zsLn6$|4c%I1{wDSVSJNOSFho<+_FJmX>oy`qw5Im?JXb z*4E9vuVV;Oei^Q09G=^r4&~~h9_Zi31jRHBA;soGQ2#@B9eu;x|5!dFZ`t&UH*jZ89vO2^786xo zUzwg!lj)g^x}H(#O1!pKk%)4=8u^}Ms&q?uSE7U+hL#CmHmMO)ZJHbTqDCj>T)YNE z|2f$d6#P8ZgrmLs=={BUktJ@^Lm~!>h#ngq;#8uCNV_8 z@+L{Lqz}*-9e52kkoXmWmoy9F7JVCO1cXHwB4p7+vsQ04dAvh2vh&gJR_S`nx*O!} z9?j|)Erd_vJ_-8}CtyM02h_Qs-HE>;1^)~mCPHrzTn!!}lsQJ-5$b<~bVv(B6|C;i zte4(U_bXU+msxeA9|Pp%gq6^Ftb{H>x(9?91PkM#3(YWCBHB*^FIx(&dk^pXM1P`D zWTY?g7s83RKT}YLe;~Y?Ad0UMnR_ts3j#-o2H&9J81f0_7#V^bM#x740k&btrQPGf zm1*~p`~z~?hGu$4eneRj0mvjFr+xr}b_jt8JIi~BR37g~l$pmECPR_nK^Fjs#jYHq z@fGTi&~%LESBQ#QGdHVIw*=S7u7yZ>C{OT|^Z|@u!bJ{}W(?!w9Tdc@J8sdqT_5=m Dk8$CS literal 0 HcmV?d00001 diff --git a/target/classes/com/ruoyi/framework/config/FilterConfig.class b/target/classes/com/ruoyi/framework/config/FilterConfig.class new file mode 100644 index 0000000000000000000000000000000000000000..bf96d47281b9fe5779f75f0b17e8eeee526e1645 GIT binary patch literal 2386 zcmbtW{ZkWH6g`^&8^Q`21jSl~79kDMM%!eX+U+1(&ar_=sP zt<#BZ8T)Hz`bU+Xw;Mu2BGZ`;L*9G$-FM$P_nvo?zyJB;PXKpdhH(?KG9HByz-%Yl z@mR*3SUiz2ABK!sv3S~v8~8kgg)kQJg|IveAt}bC;7<%BX3H{G!iZotghAoH+J!Z& zi=C8=jWDi?dp1Kzhwwc3Dc3g4d_uy& zdf9Z1qMkM!BU8{HnWp7xu3?!xa3^WmdDSTi*Bx6c>aQ&OrJB(-(@}S{tZUhas#ZIo zrnN#@XUO(_F_F+S{QZIBOq>AtK|)vAF05*ndkCL#Rw;A^e$ESTR>^tEAiRA+PT8q+^QW=D6C`_9;S7VHAaH z3w`xFR#9(NyKu@X+gY(iuS@j>pAauV!emRFEqD8dYI6szDHe5+stLlUHM8Q&?; zkWr8YQ`T7%wgMeH3i8-xHoO+#X=XulcBvZ$gBX%wDA>bG1qBo(MBjDwx?a*XR}{t1 zs)UPYdy0gBI!3$Qy0u(xv%aJDM_c#_NHU=>vvaWDwYX1sgy^wi=X9ITE4nBw7fz)@nXn<3%EWuG z{=8qDL>=C$CDv@B_ISnOnI;yghO2e4SQ_6E`~UdT8kWd*_-nKU-=e*Oj`$H|+Cmj{#>aj|IMVeSlnO4yBi$AB#3L6gxO9XFU3!oOlHU>Nv}0p1>8X&QWb6p z&*=FHVb9<)KIKo20PfODkn$~)w216o;W)+d9>?KrNDl7fGY1JePS+Uk^A}2hP7^i8 zN4QE31A=DTTU^@;M&3(#G_QL!-zQCvOu!>^jhO}Egb1aVU?_qu0O- WngYB4C~6`A2Jpc9d|0#20RIBuM~Qs^ literal 0 HcmV?d00001 diff --git a/target/classes/com/ruoyi/framework/config/GenConfig.class b/target/classes/com/ruoyi/framework/config/GenConfig.class new file mode 100644 index 0000000000000000000000000000000000000000..f039e314f3927100b1cbe441698d5704b4a199bd GIT binary patch literal 1358 zcma))OH&g;5XXD-bVDA9fDiD&LkQ2+DoYQjB}J*1h$*l*aNC4o86Z1bvlE2xUOw=`eXCE2HZ=AAw zD9=&txeTRw=}@}5wC7M}OC2DuQj?+BZnqjjf8cC}siuFr=WEG+H<&R;GInU8)>JJ| zx7CI84mH3Zl|J#BDm;{d_f~{kc8BKNK$y*L_v6*2Vg0*tKV_o&Zw5Cx`c7a12X&uW zMrgE9M&`ICzk$?lXsrTBU!6#YO)L6n0fxH^m#tBP#c# zuJgkvDxyj)W{@ZyM{tv|CEYJ@Tuk?^)uD^j5d4d(WG zXmUd-!&5fm+<1{zk~nkz1q!FC$beQi)uL z=KtqCF}eQ#d*3(~O+z#IsCoS347pqwJC|!)i}KcjU@Znl6+#tw9;^eKUH(SS7i>~= z9c#`8OEiXkv6DbIXdIqP6EukkU!JxnfPSUaXZX&*-MjE^(k;~E%op%du-TPMO7$@X z8=OVga~3t(N$R4IZnfY&`Kkkl`FDo#(@Fj!h*LxP=_G#@{Ix6iJi{KpVENO;+7kLR zpKvy)|1rFKbU&$o>XI@^{!{RuUBTzK>+we{|G_Z**`)prT$>rHKbz#g0)O)gKL42> YzXX1YX6-sh^RN~84`COOv=~$MH=0}XNdN!< literal 0 HcmV?d00001 diff --git a/target/classes/com/ruoyi/framework/config/I18nConfig.class b/target/classes/com/ruoyi/framework/config/I18nConfig.class new file mode 100644 index 0000000000000000000000000000000000000000..f76ad70d30bdbc038b03d0f773cd43f50c428691 GIT binary patch literal 1911 zcmb_d+foxj5Iw^!VO=0Z0r83=5{R&Zh)58*E7k%QAe2wmCKnRK7i-E(^K^Vjzu046Y}p$&-$hBP!F(TZUt)#_0MBN|$e zh+xF!pd)rpprjxko zW-NQh@`T!#4x8k@@EHcpjGH&Sf_p5CJdG(d3hOrC|yc& z?h^F{X@>b#A*gEYj9{3pnNnq&>cDhZ*=$8>ROOpQUCh!QR#aR8vB`77m)>zo2`B26 zb_}on6D&h)jmv#E`_@vTqOMZX+Ll|O?yd+W%8s(QMh~o_#V}cuyT;9hk}P<^m8W!c zAjU9VPl^{SDuPKJ&oHIqIi^XRmqNPW(rFBBQj(4oW^~-b?Fe4zn8izmflEyqbxyUz zP$fnETRlq(N@Mf-b3jX?bP-ow@zN z+*q79m(}YL!Aa#p^J75-RL1N6}(2afo$g)8s49x(M*0r(+QfB zU+6FdYDXijdk{r0vEB#(yThOoU=*#mPQNnxH6*@CmMQ6ypQ16zDw^0CT8JiMCgaf) zv{te75zBq5vA=>PcFq#T9o(&A>7(9+EDI$TEjfGw?F3hL%k5BqiNFwLI$VLRm9PPg Xc>4+a0eLku+Nkmeg73rgvIc?Q!>AbB literal 0 HcmV?d00001 diff --git a/target/classes/com/ruoyi/framework/config/KaptchaTextCreator.class b/target/classes/com/ruoyi/framework/config/KaptchaTextCreator.class new file mode 100644 index 0000000000000000000000000000000000000000..9ff554a3d988c5a2d0ef6d16eefdcf2b719adfcf GIT binary patch literal 1721 zcmb7EO>Y}j6g@AtXC{u_rcT_D5<{SE>LlY%KAq;XZJd^bIte792)gOSo;cHtJ=SbE@`3Q`>X&qVb9C9B4Va zv}YEyTYA~e?dv%KH&d)g#(v!xLCkblcT) z2kUy-uhsA8Dv2Q-?CL_!YP2o9ZC9O~antmg)OWnt%O1W6UPHf()d<$`u0Z!u%n{r| zmhMOSX~2u%Eczl?#|CQ};nKK9xl*KU@#;12XfuNMFeq?#OdVGz)Jb(pomSK8jCw_# zRmaC@tjc&lf-P_qQvZuDfho_vVA}=DAgy^rJE)VYx#UnYi)Bk&G4^y4)<(0ynN~dw z>d5?k-KrX!dqkRCYp1?Nu3TbNZaIxKA&-(?E*qshaVL*4INu)QD5?Ll-`u@teJq@ZiwpRamY-t?Ut$E`U=&a2^%N<*#29|T1b*Xx{dY{`52W!YX7Cs0@d^tf zj71T{l1SjXNMqUOe6`MNgY%G>VV<|idQ@o4c7apUE0WANMC=1%5SJ(gaZ&UkiD6ct zif*D9;v9{#rU8WhCR|wte*h^NmxYkQ9|-1#r-z^r-{Cl(XJqf`ik*qYYUud^iZ_&^ OZ78GMjZ@cwiGKi}Hg*dD literal 0 HcmV?d00001 diff --git a/target/classes/com/ruoyi/framework/config/MybatisPlusConfig.class b/target/classes/com/ruoyi/framework/config/MybatisPlusConfig.class new file mode 100644 index 0000000000000000000000000000000000000000..8c79958e015b6e65956e808fb6b928fc0c46bf55 GIT binary patch literal 2330 zcmb_dYgZFV5WO893Bw#g(1^0Gh_3{YLB+=k>k0uBod_y&j{2z+n%IzZ=gf2z{+0Rx zJ)OgI)`C&q1HHf5dU0p>tX)Y3?z!rP8Ym z?HRo$7w;u44c&CF+ zFR#_QEO^=N95*stE>^-!v@^w!EbVVDDkFltC>tFx%%{>3R*vhET+Jn#VWzTh8$C zKmnM5tB~idNa{r}uc98>Kg@E)3!)^HA;R8rptrVHcu)`~Q{-XTM(+S)xOE@}by=gv zs%=&YB6&@@Na7U3jBU~X)mAPp;G&Be%(}RYD=tzk#RAHc^3>Q zFIL3TI@KoiDD8iJOmy8um*rbNy+*cvsm2@WRh*~)$dlAUKnJLUaf-$PT6tud-5vP^ z_JJlboTjle+U&;}n!D8t7{o8MVmOOm>6Ud~CmSO>yo=b&PiXT;M|RM@gAQvuYc_7} zd%9IcPEr6M?*w7@65;@%4dGnGl&qrFnCy*nbjB$7YDB|inQcHCE&pq@MIM8@NDzOQ zKl%~Q4!TEA!2OJ4JLst+?n67dTp{8#dN4|9j@6M4?veHr!6hPKw!dpc*NMhfDY1)Q zB09dGPPI^z5mL2KiM>M6)6neSNB;ed{?iBe$1p;SF^bft-$S!8Lc973r&fB6h3Q1Y!nlSLm;F#H%-9~1_g2e=PE1L}bacS~sEZ z(syY8n3*!u2k1j(dX6Mp1{^z`bf%v1uJ-J?eCM)f_wRRq{sjQ{;YSU|;7JT}8bsg; z4GS^I$6!%|eprgZHyXr!`Z8^<(6CCwQySJ{u&%)nF^s}9`g$INZ)tuJhm9B%Vz9~J z(t>H5YLP)SohdQsTXo8U!BD}p#a6w#FXS%Yw~&%7I0m;$T$;2Fh<)nNbQz=zhEvsL z-8nY(1Ier6jU!*{hGQR?6@5pPO?TD%&M_F2wA>Zdn#C2WeUmOYvZA{+Y1)-gw#*gR zJyud<3qzTX-BJ~-*SVn_d7R7iRw0Ei+%$|xu7-*OMGRJYYw87tL0q-GyVqM^2V|Nn zFv^zstB?#HcZ=;hchw8mvFB*;0$ps0qKl(vb9P*7=56oa<60;j@n5)Zal4|w+&{vM z@43>I837e1Z$MjrZNx&=ENkEZ?T^hclp+sKh zd#E;?ZsnN38}C5^zK0(eTnWKbT_s=-p|eek?x56bah%GhHrDovKh6HE`8319V~bSf z*xs#`Ufy|D!tG43Pe=N~_FQY*s7on41>OOhK{Hl*vLe)Emds^xnt70FGI6^c+6jo6 z)MCoVpbmwtrSK;Oc0BtcXHFA#S!*iGBOl&~fko3cWMGmkw!CRs$>&qEsWua#sCZj5 zrSOHbDe^bt8G0`a1`}P1tL@;tC$Yx)$`5M3d2WO()@wCK(ouDVLh{*ro59+DaTnmD zWhC9!2v#lbx;c8w45QTzaU5QW3>JG@Y%BkBh41N1qRtYx>Y`mMx%ZsAhv$rq2ILF; zNq&GA^WN4R)3?BazEU*h9#3}1@?eZZ*M z;CK^PI?fE|{yQhY{=kn2e1hX8k9Z9}#qWgA0H49Ej!(S%i%gf>%nXTu4I2l1yRrF1PtK!I6hWy!XTO+ zLsPfV*d%-fb8s8xVFGfvB2agH!vQFSfF9mLaB+Bm+_%w(&^oy@kU>QP&& WqJgOhJoLEcJ%#kve}wDD0RI5Ct)jvJ literal 0 HcmV?d00001 diff --git a/target/classes/com/ruoyi/framework/config/ResourcesConfig.class b/target/classes/com/ruoyi/framework/config/ResourcesConfig.class new file mode 100644 index 0000000000000000000000000000000000000000..922e751faa5663f74822577e537bf040be8737a8 GIT binary patch literal 4109 zcmb_f`B&6d6#gCp8Dc7Qz(K9nrHZV^T5DD80*JDMtVL~WX67*jLo&@|Si0MN-*>xO z`+M8dGjhsl|A78cJ-si%dGkPWTN$8JY zK*k`3BwUbDj@}4{#XKTo6k`#Li-!prk7F`|ixFHZBLwkRR)NcSLdKJLN(@&7iB^Fo z#APIqjDX9SLQ2B4j9uuJp<_m@u0~*pc~%Uj7%U%BipWUgX|ZKX$jGP@Y;|xXWMwp9 zPQtu|1qq7`wKh+4%^AtYXLYCBbhw@1X~(h|PW2_MS=G*3i@G{xYqNabvahOofkYh+ zXKiKJIh-{eeU^{w89i?BHq*2m&CxA0!*H_CvQug%ZR=*LfE(wUnNg>-1j$}hwG{@n zE$dkGy3LaWTYg42b>|U=E%goK45b}bk~38H=_Vh@&c?Yts*yB?XrGnPjB(A@#k0Rz z>P+h-zB$yagk?_YDY7VIW$6Sm9qu!Utk#mr{BD!jy>ZKQZ*)L1>1me(p?L$stX6ki1LESCUBqPt#7%X3PmdtTN^N zCiD-(VvrM-ojIo)q&7oKeM3>;DVIj=SRog&;yHlfXi+u+?=pGb&f0E>3SKV?7#`V} zcg!~0wG2-R!K*Mv+$bWXmQVmv&EX49FwEO2{)t}pILT-VCowT3ks60YTCBwq`BJIDB&3e z&*C|%A8X;-v$~NK4l5J1rxiSp7bLu>;3ZsRV5)+baZSQ23SJfb+tkdwmP&EEC94xK zLuuj35~Zz^~TZ!35Q z?<#l??<+V0Rl)}fKEy{9yrNnjDj$|r39ojO?8 zLXsFbTyb^!H}xJ@i>#v?q97!)w#}&|jMBY6M%OT9c-P?A@Cd_RPu*gKr2C;Tg{K`S zt#)XMX->!B*p@+scUD_yOHt|TuJ5@GXtrBac5B#g5%!=2R% zE`;Bd>zTvV+f0LykHXHdGu#+3!fHG}kx1?x(gs1QiyWq-_Oh+wW&+bx@%DUMRTJ>tngSt|emV408A?hPE;y^1`oP z*Q8U$<29Vi@5UO&v7bJxnn8t}ekGzMp!q0$gO$^KKh32yQt&Qr-hvd3 z4Tv{JmOLDg@FE}=SynjE%I6m;4}(tv%~OC}z}L3H5| zF3>06B^-A3jCs2FdXiRX#TnA~Vd5&Iri&)(cJZjM`~b>ahFDYM?}+74>)Wt{wAdLC zE4x%Gw$mw!Z^z`LVTP+R*Ncxq&V851d`bE;^8V`kG3Yuser6P0@RE*z1n_ z+;M+X4Gt{fU=D|tQAf0g`$m`NP!qxxtmNo vfHrb|2YKf#?VfWL-sj2p6^_uEbkJMj{wj15P031loOkiN+_8IQw+Hwaiw5w# literal 0 HcmV?d00001 diff --git a/target/classes/com/ruoyi/framework/config/RuoYiConfig.class b/target/classes/com/ruoyi/framework/config/RuoYiConfig.class new file mode 100644 index 0000000000000000000000000000000000000000..5d887ed9bb9aec1dc1f6b4e31dc7a7fbc009a334 GIT binary patch literal 2091 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 literal 0 HcmV?d00001 diff --git a/target/classes/com/ruoyi/framework/config/ServerConfig.class b/target/classes/com/ruoyi/framework/config/ServerConfig.class new file mode 100644 index 0000000000000000000000000000000000000000..b4c7979257c9bdcec1d8f533aed10aae6e426389 GIT binary patch literal 1453 zcma)6Yg-ag5PlCJE3hh>;UzN5OfKeT*%h-CGbt<|O~0>jfHl}d%fZXPsxPE{=m+$p zdYZG#Ba%MlmzkY2Gw;m4Gw1yJ{o^Nq1-uWVAJbvn!%P@`NJNlCN{Ms?v$!9|91LYX z2xA_GG9N1ONQngvk2O4D2rgQ-C6^fdiR3OrAS;TTVIXhW{6n+0$DL2+UX@OU@}gi? zcTLAqcKa|OD^`OcnJlzuVSLSlZsWJ>F zlKFk}z%;6+O;OUZ>@xW^hkt4EhGdv=-A6`)0IFOX6)Ee+IthhH2U}hs<%9@xRn$z& zX2>M2n09G(e^a$KdZDqEW~sy-Dixu>lKe<+nzEuux#SO`8(daIann>xNbV3p;cd}$ z3Vh8{Ob(uBWKIDvjBPb-Y1R0y)v$=(W!o0gl$Nk5Ih7Yq*=W=iK^=A*gv^CJu5%+R zYIR|ATV`|&AgbZ1j%RqTV;na$ywH)sqK=nX((p>hYm8`E*0F-Dj#Xv6(XocNI@XcX z@eU(I{T1iQ5bY4~ac`d&Xl_%TWcsQJ+h|Iw+He}gf; zzkv*WodP>sd4};z_U&w}gB09eE80{wV1~&HO?5}mHpIJo>Gm`96KWgI+m~2}EpOqy z2Iy8)xm{KpMYk~bSMOw>W7*52j3Os8X`SMUT<+p7pZ!_Ttk=0+WSI3XM%Q)wM_#WK zUMbwBx22yR9Ka8H84x8oNR~mGk?u)-2m3}JJ`9lzx`%zZM&G&@fMHxGiy?*)k}7$b zw2yS?1ipOw1pcpNcmQG75kr826-dkrY=Z>QgHeo8dPwaz`4rqC_2wr%pE^Z=Ay2A@ z;S@oJjdZ#NjShMlS_l=D)k1i-g-8o}r)<9q5Qm1FRCdBGJ?VkE1woz^;^bwne*(9t X_#_pjN~5HF#FUbEFhyF?a2NOkH`sE3 literal 0 HcmV?d00001 diff --git a/target/classes/com/ruoyi/framework/config/SwaggerConfig.class b/target/classes/com/ruoyi/framework/config/SwaggerConfig.class new file mode 100644 index 0000000000000000000000000000000000000000..810477bccd88ec31a87c6b3e487fb400d942953a GIT binary patch literal 6675 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 CqA0Zh literal 0 HcmV?d00001 diff --git a/target/classes/com/ruoyi/framework/config/ThreadPoolConfig$1.class b/target/classes/com/ruoyi/framework/config/ThreadPoolConfig$1.class new file mode 100644 index 0000000000000000000000000000000000000000..82e072897349b52efa206d8bc6a38e202bf1e48e GIT binary patch literal 1349 zcmcIkT~8B16g^Yc*4DKYq)HVLwQ4`vVu%lx_(C9(R6`2ESBKq!E^cSYbeE4eAN)f; z7>p180DqM6&TgTG)Q3LUWT$uU+>dk4+}U5hfBXcnidGgk@HB@s%4T_%!*jgIpprqA zAszPoLAk}y?6|7$=t!OW&VlBAajNtu$5rycKXi6`T5xY$sbD>Jv?+1Tmwxz$;Y($r z%&pE5KjBUk`hltLMp_FQ4m)geH&pt(JwB!@j)WTuZ{tk3l&|DlF1*UYu~&NotB}^U~fq&^m@Pt13|COBuvUHq(bV9q+?sg zSYarS$DOEm$<4&RG`+^^@K25mg?A$CDQ}yb(+Pz(`1t`Di5^6?QAvshTpl{zNJ_(a zC}!*ZWtTOmX7udPucLvBY!+7+9=QMY(~G?oo5I)@GE25R8T!vu8( zH5+v_Y-DiLMixyQ^C)GoY{Nl`=5K=WE0fl(vGVP>J>f-x@J6r_brf-}Ptby4esEdq zvcitgC%#LhS1Q%N=p668b4@y9k1TNI%&atKIyGTWh zvhCF@Kk|09^~#qu^}XkBTIArBUbmJud?l;QsTdmy0{!8UZAk{3k9L$Dg`O|)FgIHE z{BDZLsv0?DV^llxedUHHkWZozeup|joO>5|mlh*8)HSuN15Lrtx~><> zP}kGE_UEC66Ap=9F}aThgWc2n;ksQq{xgE6U&2Hf>E%5?;$~X+@!SPR3hl zWZ}#aLQPk^$hXzJ=2G>&netlHMN%B8N>%m*lZ@t5hO^$z1Ec{T(yz;yFcRjH^i1d6%UcL3ME#;qf8uQ>_N&y6_! zlsnfzF10Spsg^FZe&#<)hpY4w9H4h(5(yq2^ee&hBi1xRVog(obQ=WE7?M|MwMx$k z(nI-uhbgStsjAvu>~B!q@>!da$dG z?y;ZgU;^y?3Jj9h&_OOTV6c@~%R!~z47w5}vXi7S30+TE1G1v7%Cp4ZLC=6#jf7K2)edDgK_)bZ0 zd;W%<(~cY1D^5Q2{9W5Qbg;8dv8+cGnzjTKuJt{SeTNE8=mcKb&+DOrUp9rIBg=T9 z&{8t)RG~ZD`(Fa(2e@q1mEC-!TpcR?!U3&XQxo<7bF%Xx^FkTvzAAMIMY- zrdNOcX=+XB+!1*B4PEt3*Y~Zlq-&4d#JCQ(wRRtF8Sc?@c|XC7DHaVcg>E#(7J96+QsxS%oy(dX<7Tb3THCw&X!{r9e34U z2qhbtWdrrs{6+xA>&*ORg=6le#+vSy49vRYp6`3x3fJpTeblh(IIqL9 z@1DRhHl$N5SIkr#L4}-XLPvc73Lf%N1^t)&9-=~7ghy-GM07f3KT5w*3-|G*g%5DK1sfKMC|S6M%NAUrJ|RyiEwqVWEDQ+w zGWM}hAT%Ttiu$QgS?DvNEumqISlGsngM|9&t8ZsE zx6Y!*!YrZw|2|m<107Bs{l!S7rz9OMj?WyFSKfeh4@!8{IjFX&^t9NYrU6g3rlfXr zY@xPNUg40GrDG~!PdruKq7&(Ls|()?{rNjpTVKwllLbbWElXUiEgY%zvM62qO0_0b zN8&e(UjF#@@SlJVG)MwL;R<{0YE1?FW^uqiN2lfn%O%-W>zIRz$hQn*QhIh_>lP+*EDg;5I3_@wX!1tpJ>-v>NK zo2_EoH1H?X?}P@7@!K2?c3_-r#RizbBzp=E@rYIr$gj~z0^GrYKhRJ?qqM0Cnxt*6 zphenKzg5sG&{jdav{nTj(sokl66hwJ7C1wAN8oG)=cGMf!MoC4sNkZsmnwK~@Fi!A zS9uqm31aUuGZFq3;khNqnS7F{LXuC^um9xJP%+e z{++}+j4Q|_k-&Hp+A+}xLq4+H{R2X z_u_qN)Zwv4d?1Mr;zJ5Ptl%RGKC0kj0?J7L*x2BaVS%=xGx|AQD|)7-xyGb%K^xIM z&v5M?fwZ2_TV_W0OvfH@Y|psh32Ys5+)1sFcTIaT>*_hN; zAdrfP8ZrwWSDHr6Nn7`dt|8DqYq>Xgw{&|_8}+2Po<44A>>s7(2S)Fvesd`V8hTCJ z^!fxAbZk6E*#pi5nYRp?wlQ4HjT`QmK5lU)J>+C`>zMAE@;#ia^G=)8v~MWm@@1gk`wl8>KFw0BLF^qFm#A zYqhCU`I5O0SCjVsFKSima=^|yJvFz7mXs9s?ec@UtYCO^AtSJ(BbL`SP$pxogzEyy z!3!B9FL7H?@Nq)#kS}vYmw;tt7-MD9@EA6Z(j6Nkeh87de$HeKJSsFH;IpJ-HXEtb zhS{^Rd?e)xOV5o@=SMH82ztEdOwr44c57n`9-h@N|ep-^~JB zYI2N)0#9u)FK|cf(bEfgBjcUX{A3V05dF4ePvx9qfhbSP=(~=^BSD2iU)n{>YTqF+ z6%VNSyXywkl3s4oOIps-YrDX*FsjikC-*F1%A7^LZPxo%=EB~L6=u8Ss8e(^#y(T# zwiWS-X^Tt@O!-HOwrA#yV`jl*>gzA(7oLUg*ua&}UBvSPj?H9Pbp6ECqhdSgnm(od)4HZ1D;uH9!icjIw3O=La zvv`6hf3M?s1<%#cJ^o>uUTif3_=8cnBgNoq8s;4id1SUe(j zGZ`&rbt&P~&xt%+i>pdXP?{sL;{@~IS!2p?7OIIk)sw`p@tav0Bk*(2#$!pge!msi zd$Vay=AH&UBa5ei)e)QZ=8K4)NI8_&MyqVLXlG=3)&^Lji#fxk&y=j=4(gfH0;{97 zZZ4^xAn9O%_0e#YoDqnvjz<&8g9IS3vJ?lO#5J-Ov8n~DRHFv`TqD|vRg&gWcFqj$ zqO568RAvQ+qpHmk647ujh9dc{!jF2TE64n>-`eP`5J@LH%n}#0v>j2A)!y*?`_eW- zsU5!)*c~6=>t-(EP2lnP&GQFG+##${0;==A-)mkEOigBX#5D?rO_Ve<`11IUvGZk- zstSMLe}TcM?}Eu2h>~>d?*;C>Dcb~=QsM9#R^S1dV1{&<7!OkO`EX_of-R-HTp@H| znHS--@BhOo@NP+2Cb>Tpq^(!F?c=A z<(O#v{&5GdQImrg!gHH+jl8923|`_YJ+_}1!aHXqiB;c^Bk%}Gqm>|>qVW}n$aaXW zojx&9BaNSwuKA5hg}GEY=J*+n~~$f`77D3^~r(A}g1Az_tz7KRE_t z{$fpEHVy?%diUb?w@r{yKalPhZAoALKqrnR~59pRPYI;VGf`19D zo8zB2djwWpn+|Ij<8}OsvmP7xfo8dE(fBPWAsb`=$#$9FeYW^xjU#r=95-{s9-IF= z`L>^*djy}YotM~IKZnGLOIUCbb)D(@i)eVt-@A+di%18opqw@Qg0vRhd{cv+xC1Zc zo50J;TpuB|q~Fl_ER=NPWi%yl6^%oirjb0j!W!)N+Ko%39V{yb@@67q#49L(3r4B_Bw=yL^>GZM&#Y zAn@)~LLS^u>uCyZ;M%@T49X%Iqn6>#(@3Qk&mesji+9(x)y-gujMY+L8q2uYDi?2& zi}h{w;o`0SV#Te?eXd=^nua#wu7f`6rb7m4ymWJ4Ahqn~eSQ$7|Mp=RhpAmN!#jd| zs9zgVd<3I(e>YMXtn9pk+ah2K zf)ikR7a`P5K<)B13xL*E)~wCfM{3rFH8srJO7phSysbBC z9#i@anYV48=J7-Jz2(HQ#y>AWQ~LH9tbGQnq|jvhNFk@a5#FTJbEV{4d>pENB;|_(5t`z literal 0 HcmV?d00001 diff --git a/target/classes/com/ruoyi/framework/datasource/DynamicDataSource.class b/target/classes/com/ruoyi/framework/datasource/DynamicDataSource.class new file mode 100644 index 0000000000000000000000000000000000000000..26cc712872d39cc8e56ced02f9339e1292b5678e GIT binary patch literal 1185 zcmb7DT~8B16g|@xYU?V{mI8i)qP7KDWAv#=V%iuJ3ld9spY9IJ!gi<5&MN&^`c&hC zKfoVlyt6H#EtVIHek%i>WMj~QmS z{m|Dt43$djgn!{@cJw*0n_Tm@jFltm32MjM5A0put5**hG7aephPjp>ioLko5$b?< z0zwKc>G0r?E8oN;Fr$zCh~Z_+kzHHGvhUkRig(3VsZMQ|grY>qZuUdo^_?+kh9y@V z@i@>2TzNuYykaQ-yIO_?J;f#Mxv5K-^odIQz4K~cPg1vsVZcMr{@6JYjvh5+9NDQe z%uBL?|)m+>6Lat*)o!Prmcdw{sJXmp!N|zjH#7c=!x1>Ccdmlufp;W0} zE}J1gbo8BXtSVn>_m;8W!VRojc!H-Eo}pr)iuEjN7B;ZS@Z!1_GBl;~?5L;w(7SN` z#O*la)(1)L_S;UR6?gO}N#hoJ<94Sj=F)c}8iM~##Nx2U_P8@9sYFTB^F!_wM)Zlv zF7PAj>b{aap|mfew$Kc3u6yf-47E7Z?`7Z$MH4wOx(76C(-yV0=}#SNn57TYJSi}8 zB8N2c6h#!LX)o3WV6}|_Qnk$i(iCOB6P(6PTFaQBbq*QKQ{n<{qd>nREJ9a@>9|Fx zd5XlFCkgX!fmkWpLCkZcM93l`xfBsW6mz_(YKe|t#tIQlU0bx9l*|vzd`sS&LS_?e QIT0QnnHdR_g9+>IU;iOM6#xJL literal 0 HcmV?d00001 diff --git a/target/classes/com/ruoyi/framework/datasource/DynamicDataSourceContextHolder.class b/target/classes/com/ruoyi/framework/datasource/DynamicDataSourceContextHolder.class new file mode 100644 index 0000000000000000000000000000000000000000..a73ee5848af63848dcc2915e59df6e16722d1d7d GIT binary patch literal 1320 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 literal 0 HcmV?d00001 diff --git a/target/classes/com/ruoyi/framework/manager/ShutdownManager.class b/target/classes/com/ruoyi/framework/manager/ShutdownManager.class new file mode 100644 index 0000000000000000000000000000000000000000..ae952e57df7a925b257ec97d4a764c6ed12d50c3 GIT binary patch literal 1336 zcmb7DOH&g;5dJ0!Y*?08h%Xd*2$%$MRqzR@h;j;Q8H)R|3}GSJNzHD=oIH8aD)r`7 zPkOORqzZXd>D}KUQvM0co=tcdDXQEE-5z7R+=I8>Zb6 z$94Rh4Bd1uR=WLm0Y{R&cI0QkpODuEH`W3dt&o~q9jN|#<%Rn zNvja(r>-$XN9ns>LgL14WNZ4Yv)9;SNLpf7}d1^In1nGYj1I zDO5c(G$W2XC#cDZeO=D@sw2Z{gc66P{QIs#dFu4JMDo!2*$<&RjP6`JgP!I7B=>@8jnRY5V1N;G_@k2fU literal 0 HcmV?d00001 diff --git a/target/classes/com/ruoyi/framework/manager/factory/AsyncFactory$1.class b/target/classes/com/ruoyi/framework/manager/factory/AsyncFactory$1.class new file mode 100644 index 0000000000000000000000000000000000000000..cf18176a741931c41699ec669ef75bb77c209b75 GIT binary patch literal 3283 zcmbVP>30)V6#q?{WYXy%C9)Xhh_ba!Tc%}kp^CN@u#&b&pjvTZnoPscWG2i^3UxzN z6c^iNQKZzhnm7A$^v_uaSMd*AQgckj&4e|_^k zfc5yi9!cCR$PPi$g6tGzmmot5T5*elTQMx-HU;%!up74v?hXZmxKnU3MO!O z4EM;mSHXR_zX44+B;x@Yhb2VE_54b+C?THOr;qDeUbk{u+H*`R*Cjy}t0mWPEWKbz zP{h#nbg$$}Xb^*f;ktT`OEEFhog5b~++8DTaAcp6@hEEEX;Ixd!}26FrHqm`VtNPk z{FvcrLJ0*~Udc1_uC|LqeNp*EKj?pUT(;%_-RvrZ(yj z-2=8WrWJIHAUN8np7CsFLhE)Xtjv~Ru(Fd_H1-?bUOT&87e_q9p`<#eWRW%t%Va&9 zb?6ca5?kV5;PjNteAaL%C}eYkJmS~~h%`N$w{t|<8nrp-sIjf+*usHY!KM#vN6Xp; z-L$mygqu2ZLU=dr>6x*9z35-s53UCp@r+-}PTM6XV{9=6LA9}t5_q5CP;@Momi`49oDPZfa_#DqT*2;m9S)H_*U*J<1rPF;|Uc{ z;+Tr3@U)6&a9qZ-DxSmhGES&?0WZpUNySMVSMf4V$#_M=nyQ250)mQH@tTU)1$je| zHwAf1khk%ggvC|+diUy1+Sp$*tPBxZQPo3^IBOZ(;v=&>t+&DmSEWmtjPKbffwxP1 zL9rc1&U6_BGTu?~F5aWpJ5;Mj?IM!ReXRCxzH)$%BrH77h(*In;Apvo95Sa zFe~FD6(8dh6`$fW32Uoj<{G%n3I6TH+tzlP1@U0qF-CJ0li)cs_Rd#2r8q{$cCyuY zl(6T2W{O$zLQC+7rkl=)UtKknoN!x^xy+*09X!HaB9k#GFUMw0`Rzmxn?5L&Rw(m?3R`!7Y5D$lf8IuuTwkr?4%kWm|D-|yxmEI&&C1}?6|U_G;$ zo_D*g30AgRD{50+SFIzbEwtPg7|9~ye_BKXaT2E8=yX7HbIVzba*N`_MN9i##WvxJ z9z*9vS531_RN}7M@I3RW(r8-5G=Qm664y1KZ%u3&V!2B4^ACtNHi}9HuxByzd)70* zXG!yWRyDt8VdIGq+Xh_0{z`J&*~{!#CtD{WCEF$uO14j;E|~~VBAiS_CJ{*{qLYXw zzk>XQfBq`cC?wKWTurL_{Az5(H5^^bmWW~<4eDg;VAF(gY;9~3HsLx_Dwlpjgo1!S zlV3ubLM-(y>W9CA(%%+LG?by<($?xz&V;5A%#g3E;lmYv3iH~^xPYsXuQ9(4WTIs> zma$+UVB=+6xH0@K7E;Ngjgdrz)1fjJ`;1h^l0<~cE=oia;W845NP8I<`)eFt!o79T z@33^ZE`DixI2>P=9*)E>OHbkQlbDx?#Fv+`;(aWRuM`{0SalK#XIFDJ-ZX{gAJCMb zSuG(55bGd7yc0`EHN@ynqJ^b&)N+1*tf8Ye(n(v0(hwom(S-tm9Vd2&h~80L&yS66 zoInpwp%?F9Gk(NY{DMCGif#A}1Neh~0Kz$&0)#8hiQ#viZ72SX+||wVAxb|+8SS*G zfWx?fT$q{;Qsx%=XDekaMd)t|kIJ}FMxTtEd`e~;5a+oR_k_?-t>D=~22~e1g<6vA Gf!IGhu9>?4 literal 0 HcmV?d00001 diff --git a/target/classes/com/ruoyi/framework/manager/factory/AsyncFactory$2.class b/target/classes/com/ruoyi/framework/manager/factory/AsyncFactory$2.class new file mode 100644 index 0000000000000000000000000000000000000000..3ddf7e0f8d4884969792c05e2bf6da6d970c82f4 GIT binary patch literal 1266 zcmb7ETTc@~6#k|yUAin#3W)a$H`^i%BHn^A;UY-N z!~77^cxWMSp};V7D*Rd1l)9|y42xw~HMnl6w#S=V9ZNUj4HbBi(!8b`!V7q%9qx3l zuQ8-wdGzr$!*c#go`nO3)TXLQhS9PY$ak$qRq8!a^~sVeD_8gjLVM=iHKw8?FJy2^ zK_K;}FTzmLakVe%p%xAKQRx%j5P_&m%@2i3#M^v5YzOXk=P?KjKJ{G5OT7eFIwlKG9X!Kx2QM(fu+qc$-zPJS8{)hbc|PCs z8Yb1@303W?P?Q@jU8QTL1sJxjSoQJ+ET|j{JsSUPI)+SLMkaiz$uN;GbTEAp)OjV+ zR1yN5?*WoV4IIBb>qfh@OWygvF)s`=8g`hNevLdu z{WE!yD|J-i5<+3qFy@tb>vFP)(}om;HrJ09_xxHvkDaRdKk2!{UO?RwC&gEFEYkq6 zX%?_-*7PH330lJ#p?x%-5sBDh7Z<*O6~7|!DZY~>nFb^I0b?YcShtbG1f3>n4bZ$G zP=;%mB5A<(3Cg(46h9LdturLgkvhXbE`1K`C(_3F4TA~vqKqI(E`p*|F%7pb+DtcE a64&WQ0yl7z_7ra6HpWS_DOiSfVDJxOxmdOU literal 0 HcmV?d00001 diff --git a/target/classes/com/ruoyi/framework/manager/factory/AsyncFactory.class b/target/classes/com/ruoyi/framework/manager/factory/AsyncFactory.class new file mode 100644 index 0000000000000000000000000000000000000000..3016b00ca79cff665f9838a72ca86b07d9262133 GIT binary patch literal 2109 zcmbVN?Q+vb6g}(Mku6l>1So_;OG*GcVAcfs6{l&_&@^ccp*S$Z@PjJy#!(_mt|UYK z0G(;yrXN2@%M1_DhwAjKr4kpM8K?0?+P!-}&OP_u)n9-A`4Ye_Y!+}F(!>O+CT?Ip zkDJFR{=meC_{hY^_{6}1i9Bis7EPG=G>;_{pW$-@%Lcv>u-1KFg)5#NMJf`QhTb8arNT{n z%cF$Gj_p0PLs#!pVK&}z8EU@n1Z^31gPtq5L%Xe>1>v4-+rG`Xa@%&|AneKIsOLMY z$%cW8p>l$-NgxE^ZwCbUU8%lnKeeT2`z_gsL)UNBMv*)oP}olT0M$KzRd_vS2y%CIbM6n%rnUm30QAGTt#12~&oEQ>CprHLNx0D|XT&b(B+;Zb* zwztQ;^$8vLu?7*z2mdM(MaJ!rfIBMQ37Yq8%~-5L#xN63Hafaow@U-GcEUjCCEEe3 z6@;=Gv~Aayjb5acN#day8$qHVP&tx6qOl!^B+OQ;Re_VG^04;FFFIb@KxQN8hK^cw zb-PRsR{Odp+QKBJES$he11lD8<4Z=+q*VI!8CbRO71j)_TlgB^7^qvggS+gmqc+#< z4IO6V|5(mz%X@4S4o^iF(7?AA?xP`4Jt_=V_@Kd(wiLK{;DswzQzqTfP~lTg#Se6= zke8N<@2l^-Dq>re$-H*-gCriUBzNMtBiHyytx{Ds)}@gY*|rR@rtGHfv&%!KE*+o2 zL8SAM+MBJ?vBOBc>i1-j1!_a%jO3G<%^X6U+mY*?ls28!dS|)WWLCOg59m)X6M-Lx zfOu1H6v>e5V8r>?_iXiu!%dUZuixJ3U7A!gxzG=*=If?4%>tQw zu#fSD>^_Wz+zaF%P3PvWUaL%JpQG>#^%CF5F~)U?u*!rn*N?H3_`x}x=NUcXU9L2+ zS-i(proXztbjE0R3h(10va~Mp{5(^-#Pi84&5JZF(xiwB{4d}#uB2dZ(o{Pvyny+L z*&aJUlw|vOAlm}*m8q=#<^(szVxCa1(t}R?T;CIr{S8995&t1Ak87{^ok=wdQ`SD- f`W-@ZJg9z_@G6vLaGij`5@q<+*ME8b`tN@LYlSsG literal 0 HcmV?d00001 diff --git a/target/classes/com/ruoyi/framework/redis/RedisCache.class b/target/classes/com/ruoyi/framework/redis/RedisCache.class new file mode 100644 index 0000000000000000000000000000000000000000..e945aaec95f19c427bb584cace1e3ecd1902167b GIT binary patch literal 8640 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 literal 0 HcmV?d00001 diff --git a/target/classes/com/ruoyi/framework/security/LoginUser.class b/target/classes/com/ruoyi/framework/security/LoginUser.class new file mode 100644 index 0000000000000000000000000000000000000000..bcd951bbbc6f67e0d0823f7d4ffe40ab9f341184 GIT binary patch literal 4824 zcmcJR%X1q=5QlqYNw#G95$El^96}Pmk_}0C+5{Xs2@a0!#J0!-0&8hwuPyD0-BlcO z;KZ2&$NT}DH~^K9DhjCL0#$H<0~a`OfddyfaDf99zn)#KSL;zwQH841vpd_<^L0;8 z&wTaw$Da|=XFEp*{g;ns3)Q`=Q*a-%9QLS)0#KShM=r*r(!yW|B^NwWa;Y(8 z7jzFyf?5|HdkxbHO4zQT_KM+@O_zD`*5%Jp^~{nsXL!KK%$ZA;?o}O3aH#(4=w_fZ z7?EVB1Px3T?6T%m?R8VDIQEKB@HBVb^^CGsw9C3_X>;rD0#w=8QPVQLGlE+B2lB8m zZWl3K*OX}))7A2#;mqrcCG_;L8>PICAmcsgOnS>E(EB6v-E#D@v1U7~nrjrQj_Iu< zs)$oC-AGjT;cWFDk1*YidWiK(4W#>$;Vs+6S)FJ1kOP8d{_9N2*z)W*`Xf=7sj}(4 z*|;lwv;Qya4gJY186GbSXL?WnK-?K5pu3^4brT+kgiTBYn}xM&&u!?R_xH}WI?Lhi zX@hp4u{~-!P+;TjFfyyVF0u)PyRTF&51TD-x+XGk%(84x?klYIuuMR`WG?E9x^_!< zy%pECPHK9nq+OhwnLckCr6O-EIE`9izG1Y;Y9MFgNLa`oL0e6CtWdBq-L!3;lN%fJ zV}D#noTA^5jjIMe?KItS$0!=$(@So2P{wr6S-fGOm4iESEBI9+hX6!gIg*n>-QY;p zp1W(KHp&P}oj-D;D+{f&wc?V+_wXf!FEa>H3Z2|`;NADh$LK(R^t}_`XQOp#OKtrk zmFaN2PnlzaI$^xdSWmFjp)FqLBe71UF;;8^8!wZfApkVH*Dwwk{RQ zybnDH{6fG(r?EW&-i5n^pe@9%_Rv;5Z^JfD(02S02KkIg$QHqnEkYn$cr05)L$(Nr zY!MULA}q2+Okwv?`Ft355okR3p$~C~9K_oxv;u8v@BxVrr0Ni47V1PVQj(@<7*9E0 ziH^~6ys-g|TBLzhXmSPWVgrLi_bD+re4kp_R12}G9vC5-ft;lbU8YXDB2D)B!vd2q zAo-Z3i?ab{PvF^~n0wfR$Iz2uM+hAhxOP(VoCQC67jTeDMaj*OT&o0oTiwGpE#@>fnJH7j9_!mVOVGEElCU} z45JwSY2ZIjPeAf|XlVWQxJSwN(UFirOv}!j%C&fso`Mz+K7*E-(!37I_-Yp5O;K9d z;cJC{r31g1Glio74Yq}nDHUg0Rx&-KW=h4G%3!h@FmZ&1j2)ObZh}T=6fz#Cbezdi zGCeEfvnfvLIMW)K?lfTHtr#-(N+y)3ApD$&ymoDa_bBlZwZ^gDj9|3|!G9k5?HDzN z|Jmy_j%FfsGG?$XZtyLIK|$x}d;l~IYYdb~zQ;0>NpD9+CK{t$Xu{Op=1jfNgsFF% zGd0Rd zCDXhz$2;RpzbKg&)H&W6XZjUPzcpau`%1{PPcr3k+WKc!^uTM0GySe$^6xWOVeuM{ zY5x*33uEEI7rA@**O))R_Gbe&|BiG5Q2tFLEsyMwcH@?D0~5YXuRz9+pG2JcF9o%r zoAhena@{eVh(%K1`XX8y(DGFyWaC?qpw~zb%!buo#DGLw1gKW+{V*=VLIHLXRQv~j CRz(v4 literal 0 HcmV?d00001 diff --git a/target/classes/com/ruoyi/framework/security/RegisterBody.class b/target/classes/com/ruoyi/framework/security/RegisterBody.class new file mode 100644 index 0000000000000000000000000000000000000000..ef4beb04b76cf4d97ee1140f640b1e5880045da5 GIT binary patch literal 341 zcma)1yH3ME5S(@VAjXi8KcI<7khq2pL;+GND@BOXUz{Ush<(!82Klp6AyM!Fd=z3G zQKUlbNTZ$E)sFV-`{NV9HTp3+IEio?p+^XBx!~%K(76~b2>ydz8A31R!c6OYY1~{d zx8e*^o9XRBJ8t!_=~vHO5-!us=E~K!VfEy6Zr-eWQKiXh$5o?dX3b?~+`U~jH-z}n z)-E#>ZqJ?_4zD))RVVNf5Jvw#Nf`dQo7y!OZ8px~$?I*AKw^YLQEIP&VEhi+iQyp< W4#ftJ1VFOy19TCKA{+~QNPYnKEmWQW literal 0 HcmV?d00001 diff --git a/target/classes/com/ruoyi/framework/security/context/AuthenticationContextHolder.class b/target/classes/com/ruoyi/framework/security/context/AuthenticationContextHolder.class new file mode 100644 index 0000000000000000000000000000000000000000..f01d593924705ff438343e81debd7499b7ff2413 GIT binary patch literal 1192 zcmbtTTTc@~6#k~SZI?v~f+%>QfZA4NjX_^%h)QBiTrUZP#K-A&YzMbHW;%=Vue>CX zXkvW!M;Xs-8(Oe0jSo9#_RN{@JLi07e*gLY6Tl{X7cMF#l(6c;!J310Yj4`+mV?{Y zRvc6v)EK5i6&vx%JXcXmXokh$fFE){;&I#G>uAASfeLxF#ZcOn?UPV|W^bs_W}S9Z7#Ibz0>)_MOq%DG|3V z%)rO6S*=Z84q^00FdkMXhp;Vh2C6VTm>hti{8E^XYQ5oB10yu`#|tCD^=Wa%Mi>p6 z?(V8YhvJ#EEnYb*&zS~o*varPgR+MycpmDw!*G96$vxb~Jr5fwGc1f9HipOlD<<_| z2>rTwAVNdqMe;Jtldk8_Nc$}5MhR8_J+p!owCJisfeO7UmtGmelI;Q6Y5L3H0`jz{ z*3ddj9-k~Do3EdMeMv*+Xf32p7Z+)7FH->Ku|T$jOIRe2y}X*90Pa_0j*?mpTQ<*EWp$cIB!o-{D#c%Qa@o^>djlI!tu&~I_sa9TY=5+%`gdQC^VIv;^y zb?o{K`=z$%+cFRruKhteeSR+FN89J^K)UL}ZVONG&&sZH*>`>46&`8O$Xn6ta7m&} zpQ{nZu$rFLt)-c#<9deWL#~cRr|RhK6qlsJ>`sxaq%*TDw5Bcs+2(IteWdk&2-?+c zvk=3Qfs}Y1$FR zH0$U&VGAL8s{q3qOe|8gD4Ro=aC&Q4qA^a0O_5QIWWRxZ9V}UR!#=we*r_RQa~+ZRkEXYn~-`=@8KAOASnEhU8++5muG2SiV)T`IO_h z!Vs40_zZV5u2PDSGpQ;IW+wypk#CG1d97{Z8hz+M! z7~<1zkszMUSPswDss-*X8KjNje8x2md&Tf9d5<3BqGFNy$L$R&S+?Mw{(M`I;tD4# z%m8>`;IrT(0XbK6!}F{|)Q({@REIO`#+I=YxWwi{uL!ZJ&(I=$Ne_$smm2p)`b08+ zll;Vw(uMk_D~9EyrSbj7zwt3Fv~+GgT+KZCGKJ5fn9du%PjM}_Fqw>1ybw9V(23YX z6B)WZ9-6^$vR@*D84{*6kKxi0J&m?AblYy(awK*sylr}B4k^iu-n=l(jX7g8$Oi>; z4C>PzlWzujsUWK$N0FW5qT&|k4Vmi{Tgn(U@76q%69`JJfp(d3Q)U3eNb4YwH13Ms zO|DP7Rf;hsS6ahGBsF}13kv2nEMQSVUc(ZW6|87@fmID_Sk~|o-!bG`RnGqiE;&KN z_xM4g_>m#oR{SlxZGUTQ8CvOhYC>==$5O`<{Dma zwUtk~E8A;R+oaZ8=AuFK!pW5rmRsudMR4UEEIL(PN3RJAn4TB19WTo>6`18`U6HNX z)YdM9AavVs%6hJ_&P|a{y=1s{oStJv48mc$H0gGPo{xew9o}bL zyD0mxN`8%=q~lH#;A12b=6nAc4nrT=+nhQKHY<-VG!dOqJPd|eNf~3yj1ReY_vGB~obR0P+QCry98*Na1VKaCG?*uW73c>_lowgy}}mRsgB zaD+cwILW}K82ToJA1KRLWy=kD>YGc~hQo)mR~fpGI=-XE7!uiBnISnLs+?g<(ee4& z(5rBH-m17{=`RYKU@Zct_t=+B{zrvStPi>G-t{`PoALE4u}oH*}hLT@*t`@fh}Q8ZkFz1g4!M*>C~bbq(cRyjIE{jVsSG|wk>PkdzYdam2FWuV-cXendF4MxhGDRkxg$)P2pV_i zi)cy**35~q$;&UBu zsE$Z99WU#!RMd)9vCKIuQ1gNC51Lb@>9Y(OCGrK8cA9h5-| sBXnw*kcNod2zHTWH~pU^|2>2V^d;yx=pg8bW}j`XIgHY-DeeRQ0n)WajQ{`u literal 0 HcmV?d00001 diff --git a/target/classes/com/ruoyi/framework/security/handle/LogoutSuccessHandlerImpl.class b/target/classes/com/ruoyi/framework/security/handle/LogoutSuccessHandlerImpl.class new file mode 100644 index 0000000000000000000000000000000000000000..e6c6e3413f5564abb6c6d395cce88591d8380501 GIT binary patch literal 2659 zcmb7GTXPge6#hE9$%M%SFoF^g6i~2tnwpFjS2_8Wj}kTx7# zJAeVaW8*sBwK0nKY>XjqmT^P7VPgU}Exg~0$zFV5Vampbm^Rc~Ha@~kA8zB0jk_pV zn6)s+;ApibWKo1qd`~c3EqJQxhOyf6-Q|#1#fA#k+(>wF=<6*yBz*2-%cVTS;6g05 zUlmJ!;qa``E2?~to1E4nq|C63Dh@qy%Qq=>vMt6f8F0yXmf?Cw zi##Q@*wn50JEi2ZUx~vsP7aP?(7_QLwXoozh(!wz94z5u0_xxseCpt#Ssvjt3!gjq z0$(zmZ5mIfs*)}#iZqwnCGp9D@b{#Usf7P}`tNNTxP(VZO8~JZ<5z z1CEjd4`mC&!7?fqRvh?PwXo(OK$T&!<9YdQx2MT*w*IA&lRCtG~eI9LejFz zy?8R8X~LqDiF~a6AgUMj19PTA@uNAV=i(q>IGTb;OFs>FKKGEJkFc6jl1YC!GL-Ao zA$o}^>EvWvSp+S%wTZ2F6w`Btn)sSIQst6YMCeXNThg0NUl_VfW4cI+U-Syq^5wmF zcZNlx=2kj6DBD9p4YHhI_vNyp=JB}ICw1_<&EHLV)I!>EWlAzZa2IL7(Zq??Om7vN zG!ivgc&x7MKP<*sxlY!YLMW>$_oX|z$~PB86bG6FM(ONfxY+@HXD4Tt;oKNSyIizl(I&0O|Zgl%J+1-=snVF8fdTKhMkYy3>NWYOvZx-V2 zNx3run@y{cZa7WFF{}l=fHXU#N%nS84B}o%$=S$LY$Ta z8fM@GUZd3rBLgV`FgKOi@l-A4CVxA|a> zJ=?Hq=pF1Hum}1wzrv}Ze;Wrc4)oPe};sBu7fTbmt9HLk%l9$0{;SoS0)nx literal 0 HcmV?d00001 diff --git a/target/classes/com/ruoyi/framework/security/service/PermissionService.class b/target/classes/com/ruoyi/framework/security/service/PermissionService.class new file mode 100644 index 0000000000000000000000000000000000000000..215b701bf9d4fd97f4c412610d5ed5ec806a4408 GIT binary patch literal 3610 zcma)8OLG+075?r__uSE_g+L&|_#wc^AdQfQC`2X_mciKA7$F{G6dPhk-!J0WPW&pUa=FSIHf+3+!3)Z(EK=nk;7xY<4@t@~`R?tWhtNQ%sp`J>^ttEp zo$s7GfB)tCn*es=%M9+v!3-vGD1#{+R;wc!9L2E=3V1eyh6klR+2GrZKA&=afZPoj#Yw`F500mhdEefz)o-bHfRN zWdFdlfU(b?aRfRiUC)`Smd`kqXYDg3O1dWfqFtJ{E3VqdLL;1W1A$$WMZcV{RQ*La zf3{+motOQ}`F!9Ms}(m~N3v~BSp0gM1e987^ z^M$bDdb8sLCj>I{^>ZqW6*>ZKC4biSj?<4o|J`GmtZCABMY`3dC%Zy!zyUr-kYUq~o1t+Atp5+KG z&eu@fvRrld$`mKYb-6A`R3vCzGK^!sDvPY}bnAXFWTt zR_JBdZAV(>+uBT~;8!a}XTPgb@6lCg=F_m^hCudM)eGISGwlX0xw_Z${Ll_bJI32T z=~rg+!MtKu6IEGi#}8Q&)=HC=(3lH$sY*YIAh7TN9+aqBSU{h|%NAb2qJ>v+k*u-s zd;CG-kLu%V60ci$18-W`i^nZ&L7%{uMt5*k_Pu$I3=+5=ie7vOFKMYx?e_9w!|iux6dGDdb##bC0!v zs;)~OrQBeuS}NT(;3AmxfLlf54K6dhH8kJIEt($vt$3>u+UJ)_M4bGNgUvQKSmUeq zwY!mTi?GSd(0L_nn$#qyl05B~X4G`e1Wp)>Zp)`Ouz81=!F4j_pe@uOp?!{4R4!F!&wP*LWhSz4StI{UDzk(MJ0n^w3M|o3NG7 zZQA>$$V2R%zVI-=au`su4gHK{fHpEnw*8EC5;=iE?oB~873}tUuv1h`P}Z3nTtb`i zHKcJBMzRr2l=F!8+7qePy>`Z4l`j*Cjy<=z8LGJv4=~;|wNBp)CJK_N!LIftSYt-k zxH)PjM^oLYZu1*#&KljRkujN-e`Cl)SxMxGrE%@;l(>59A3APBqd39}0gn>J$B5!y zBJ(&=e1duGXH-uSpQrhJ2Gjhv_Z-)!a9HQKlbJk)H99j@htu_JPZQ+`<3b(dM@W?& z%0lu-Q};y(l-#%EmPGf~5b|Nfh} zyZ(;cUC`mJ_giEb1Vj^0BVBGyg~-5fQNqh`0+ZG40KhPus>u~aUb zTEhB>tR~6&&nCQcY*pY5x_OIuzeBv=C1&pv?@O4#ha}`jIKcH`{zKVB%^Y!^V2xA? ziXlhq2p`3s7~v{ag61nV8_+HeEe1g>E9 zAO~2F-dt{)5mB#2GvX^9S~Qt@>jCw$Yj%*6 eet@1;`}j_fO$j`ySNn+tn7Lrf3LFUV%>Muh7Crd@ literal 0 HcmV?d00001 diff --git a/target/classes/com/ruoyi/framework/security/service/SysLoginService.class b/target/classes/com/ruoyi/framework/security/service/SysLoginService.class new file mode 100644 index 0000000000000000000000000000000000000000..a762385d5d60456926d618118214e68e9cd7f9f7 GIT binary patch literal 6595 zcmbVQ33waD75+z-wDNl6I8ID3&=QW1B)$}aNrM9^jxk`cgNbtxpwP9nwl|iwLRuwu zOG}{_6bdc0Zj(Ye3#A8yqmDxgA@ranz3=-jy$iidOQHYlu2w5qF@`U(c6N^U-u&-> zZ{F-vFFf)XfOc_&iaK1a;#^!Kf8M3wS`}ejr{dkXzM4HZ)ZjgMuYA2x!A%O@r{evX zRPccqJ{ZGJ1-n#y2sf*^1s|3}w<@?z#Ud$F#qA32P_Y3eCVUL1o`My_9eniDSd|LiI z8pC4>_N!PSwKn1L7!D|SLPfiLpTcKi_^g7@saWlfe_p|pF?>NP`=W}ca8UkyDTXgA z_=<{7srjoay5!_8d`P1*XERQ>H5d$Yj7 za{{Y-t?Y0-m&uyx;iAMrLr>@8LwdrtvJ-LLUl8xi+tv;>D%@3pz;ky&dO&K21iwzVCK5?#0XJ2p&}~IjQ9qB z9#_@miI5NG%D}Qr*4kzy?09Y>XB%TakUawvxlP426frMhrH9O6Ur77hgmeX0QB3qy zGi};y1*(=U-z*UBvXb1${9ZF{tj~`P8rh9HJta`zYbEs5W<6`lcds*SkC=4bs(|XE z>wKJPwj@oQTj;fh%>s$2Jd%>{0v9iHFpj76^l*H@mK0d?%6gX1Tqdxv)YA?r18uPJ6OwY2rQf3VUCyNOfH)@VC#v|K0V{OoT-4RNx}D> z6!`;zx<11mv6B6|%mKEMB^%~%*HdOvw~a16V<$#*fm2?gdnF;Ow;Ne=XyROBLO@9r zB-VPZ?zChhF%lQjRA-0%S)*&jNQ??Bos(LrBsQ4hCP_S6pEZbI(t)5SJ!FwRK{34+ zsVSF4r8j%1xh~*KXOwm_cwL~E2Y@b%?4&iOn`s#o9@6T7lT*$#WwqPm z<0l&WalV3|YWNv`uHhG;KNc2A?#BU>^2;$}ou2Gs`joj{Pvv|m3Vx~KSNOHSam8bZ ztuZSdXIjkJdfFxwZiAGJbZ4^`ox4~%t*sDeZ9IM2#&mL&UM$*r-k?mt3ZB;R8~j$m z?=<`#f6(wp{7K-n8TyPuh{vUH2e?9xSZ~>VQoL`shCkyOf#z9+47kZMuw*NqC8Y-P ziG-2kUh17aI8<-Txt)wh4S&I3c>on9S4hNuTx<9n{;uH)yhTGV`V{;_!$0va4O?)5 zf`4mx4*wxqMNHhBye@C1l1~08s$r`Xb$Jp7pf5z~r81@EIyC$j&kI1o|1`XS7fX268y0A>B zOOzFJmg(hpO(;@YOj0VHPo?Hy)+W-04Cx*>b9Ry1nuv*N=B^nA_`H_u0suEK*|fQI zaI#oWe=h}ULKQWN&@?fRV2WBXzmOl>22)mIROZZ;Gjx;zw@zk7Da{A1#IRWtnkMSV zF0la5DxzK!3&kQ$G>F4AY{F(mEY`%~qEVo|XhH?Pa-)?B2Z0r{=8y{KUr93BIFhw? zxK&Nb5#a`Yu<-DDWWGOrR!iv|+ZeaYPF;FQ_MUCIQKRMz+v^fId0FLDUZ|ieY{EHr zI%nN_Sr#uXQvAAD3ru4G4ZRXmUcU9bLnKm$F1agFVHHvd(lNJqk9X!K(h1+h+s<&NVUdE;UkpBp78iZM=x*+lbjPg&U% z$#r$uusxKRb%}cUK|nzb6>ODjHLmSv0iQ54IpXQWs=6b_t1amuR| z%b}>A$rx#FxU~{=GcfiJTS9$o5ev5DY3mZ%Wr)Y)s`Pfky*eoR(D^K&%y&u5wp@V% z#}^eAqpbWE%X@CVM8xWh%knJydWO@y(RArK zvQ{y3-D4S>mw{-?NDtdA-H2|Fzyc3?$06(7`@&1v)_Vml3TyO1=J%N~!Oo+=q z@x9X>yEx2b!8&8^cowH8rU0BUyP_-Sa(^|kf$s~$z&G#0pz z`Y9~jheccWpkco|p)opz!-9j0w>n*WaQOZXg+dz@cV=V#5i>+JM)u-JfyZ%FN3=;U zd({+PO{YO+=3w6?JorbW9kiWFUP9~NQ^X-cs;-ih#wG%czi zqu{c@|15-p-1BJS?~B~XA!hKdm#3HoVOjy|2TcBLaxcyDc68lzA8w205} zkc+gW7RIGEqoLM=QS1fy_)+z zWj~{_BX&D9`K()Xmygt;L#>%JBU~Y$b6X)DUVrLdoEF0R)@iJr#%qhFonFDTPMWqZ z;F5Jt^1F^5i2BxGN`HB7}oKEn!Vv+%~lOXP*!)_*sw{YY(+=|xQid{ z?{-|&|PEc!-(x sVaMhru2|pZrA!uAF|cAdn(60lR4ZdIBAiSV!rS>0VlFA15AOv2528JvL;wH) literal 0 HcmV?d00001 diff --git a/target/classes/com/ruoyi/framework/security/service/SysPasswordService.class b/target/classes/com/ruoyi/framework/security/service/SysPasswordService.class new file mode 100644 index 0000000000000000000000000000000000000000..d4ccb4667c60e2eda13c0073b40fea0672142d70 GIT binary patch literal 3467 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 literal 0 HcmV?d00001 diff --git a/target/classes/com/ruoyi/framework/security/service/UserDetailsServiceImpl.class b/target/classes/com/ruoyi/framework/security/service/UserDetailsServiceImpl.class new file mode 100644 index 0000000000000000000000000000000000000000..70044f84cac323c5d99f2a28d6a5244e14ba1b9c GIT binary patch literal 3323 zcmbtWTXPge6#hCFW-}QKtXvEiNkAYscMwqs%Po*Zqni*}hyvawyPai{*_mZ7ffZ5l zf}r9RtHk2LvV5S*vP#iPqKLlxJB;X)f55UlJ+qf#v!p`#u)Ws6n z(=wjH(Fl%3a9n(OR%|{O!SfNkAT+%wL&K;{vy_Ci_{haDCcb54oPaK46~-k@$S{zV zU^3JiR)!&(u=sDa@a(hxwafYpll$BL&&&um+&bG#R%2l1b zL}MtM;+5ru4U&{t#m-`@m%YaZu2y$ZOUnmL*nTYy6zCVvNDIKJX?43C4>ZU`1Yfu zZE23%S>166R!Luc;rc2Mlg356bW?Zt5W8*dBMfy1tTZv$l+aB+eOMzleA-i* zF{0VJSQj?y+!H#{vwL9(1mC^f)pWxtfFH=_48jmKEG;d*?$3+SB!U?Zwk5{3Nli60 zGovP5L4CZvY8*<`r7WAP9%)DiR@9J1G7}M>0_D-Uy2*F(ouNJCO-1&kt*oY-Y9V(> z<>I8Pr6vZooKJ`Zi($zicTZU9VNK+T%WdMPDP?ob<&>?uDIhV7wJq4l|MYoR$|wX_ zPFkLw;{Cb^=jsqO$>2_iV8jIF3~kkUCU3aq=5t&v9EmG<5HSTCu}MNs!AaN>90e{s z1sm`H!8 z1xdl{;{Oc=Z^C0(Hy7jV^z}c#{Cf7n7k^#(D#ozPH(0#Mu{1X*vI^c3D$nEGd@9ae zCErPf;;R@ntkeWgD`-cDgtrx3z(obSLHBP{nSv@+p_Drc-od*H-V;M7x+Gju@IF2e zetyUhpH~U7<`=)}v6t|Xf{*bD!#=;lR#dwM-LgVnFl;UZnfDGcY`LRk5pG4Trf@hE zfRroT2r9;qUom!9tr&MyKj$*9FN@MlB%$Y0ttu3sBVtbVQg?qy@WVa&kE#Kic`6iGTaRs%(AxjYG7m%=;cPg zp;4y~UFrZAvCmI>nh1l`JCN+zpXg&~pd&>*+sl}%+@WGV&F!qz8zwbFPg9X*=(>yA zOgstuA`=JF)W~l0Dq&H}3IuwomI;CG1p};X)ztgR&>)dIT>8YY^DeLke2bK(lIjw$ zMZn5tr2a9_BPXe5&gIMdsJBG>`|Sp*sG&8buJoJI;GM-qyPGjX|V5nHCt#D zN`aMlh(;w4z*am=D@KpxM+g8NX7>=JTH3qZ@dIi)e?)B!2D@fZcLnRNqdvNbwxn9z zK;&q&q4p-^88l8q`Sm&$PvM@fDKt%CNkMjLNH%(#?7e~Pva;;*knD<(ETLFQTJP(i zq4`_FRSV(ER`LKuRU^?9C5qNk^N0}zd&q|Yk~l^ZQ^jp(O zXxGx@{%NceSZjQ&0q=W#`zwM&CHu|3RaUgJgAPTow-m`%KTJSFv}FdZ-=khclX?t| Sf!1cyw~tme=qV-6e&9bMINQ7c literal 0 HcmV?d00001 diff --git a/target/classes/com/ruoyi/framework/task/RyTask.class b/target/classes/com/ruoyi/framework/task/RyTask.class new file mode 100644 index 0000000000000000000000000000000000000000..fc02082dd98bc91d4293dfa955bf0b48f1c705b0 GIT binary patch literal 1590 zcmah}TW=Fb6#gc$y^gaj;1Foi3xrFWE8CV^scBl0Kvl_H6w^pnRaKj02m`x2)_N9X zAzr9d2^Fa?O_5Mlm8xoAS_HI&P@uf?JFwF}@eit=S+DK2uvAOl*_rdr`M&d=V?X`( z&%Xfd!$%n;Fwuia>`B8i@Ky$Ic_ij95V18L+Y^bgqmXL z9vPiyNX*D3&afdbJbof@7r1}gUU29no0p5WGjIDs@1w(nIxETyL-|GNT7Doa!aC#I zF25-K^Omy9=dHQQXtzRm${$QwJ_qy#u7#p6j)&Biv=1?6mdvtED{pZ@lt!Hh?fGQ zJyV08vK;5?tXv9f<%;{HCGA%lrT2FzT}w`tB5yK&<%DdYGX)v=i~N|-jp(ggx<^~b zFfbQ*O1OMplm%IF#Pg)Gm5?6sjpe1kY?Vve*j8K0B!){>DRFB?x+UpxPf>r87i=e> z^eI2=n2DFM!9dQ$VZ6`qdF|>if8V@T{pm*a>npWyAJl$ddH(3e_Udof*MI%_+1lOp zI}fYhe|72d^GDaJYhP4ve-k_SXXQz4<&Q?VcJJ2ul?Tlo1s?x+=`#7%8ikV!=lG&B zFlFKcd}!c^iD~R)*xG{BTR9yFXNmg^{dIY@@2nV7+>iK941-HQ;{e`q2Yw*Al8 zwy7#q$T^pOCo)jPSRWE0ty5&LB0b!8$;KV6l=S9G&kB@q%F%d#qTk8mGtzf$%5tc~ z73B%@*7lAnp z;U)U7(VqmEo)da*q@6`8qcu5pAM6gjbYT-cQ{kbBEWMj`0Q#_*c4Q;A5K3SEl5iG&HSr+H zeF&p?AL(q*Dl+5rT}5vPiT#vsODIv7OgEbn2eB)ZsHu~THkxX8)7FCo*S7X(XUq2N76g|@{u8pA~ZP}Lq0kVh=5z9+Zr8X1^DTPWY;dz`4sf*)@#$!s~_$3}# z!~-9|M4!dvE;dn&pme=|N8yoCxFLjx+vhPha#?dxQ-hhnKvC&9Nc0kn!PZo zK4f^*9jGzasX7e#J1xfYK&zTRiZ zchpcaEOoGQLTuCY4sQFngQ^b)i#}Z3b#Tweebju^(V*3wdNb7)LnW&{kRnd_ zj#d+?&EeKCG)ni<$wX;tuytx;YS1oxGmM8)Gw@nh=@CyRb~f|3CLjoZD}(34h$%dq zU9M&)SUFBi+E~RrHDQ!+0?Rj6qeV=FPNa1(R8KDAb+9i7rd^*--^OKOw9ZA^U0*~g zMa-@4Z$uvzy81=>DOjmwhfUf7y)HbGXGmKoX+2(}`30=;4GW($j8A6~jLtgF(s^Nu z!a0=4B7DZiaGvx6gY&6G;4+qHv>P*8x6%BL+*jm}S#4(E0oAg0g4sP{cCmt0BAYR< a5I9HaZQ&PjiDVv^aRp1Hc?2tvq~{N87X&i^ literal 0 HcmV?d00001 diff --git a/target/classes/com/ruoyi/framework/web/controller/BaseController.class b/target/classes/com/ruoyi/framework/web/controller/BaseController.class new file mode 100644 index 0000000000000000000000000000000000000000..dae1f09169798e002d9087f48af9418b8121e4ff GIT binary patch literal 4538 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 OOhf5&I-)P0kADE3;4A3> literal 0 HcmV?d00001 diff --git a/target/classes/com/ruoyi/framework/web/domain/R.class b/target/classes/com/ruoyi/framework/web/domain/R.class new file mode 100644 index 0000000000000000000000000000000000000000..72c1ab120b5fb79ee465d6cf470295358c49a582 GIT binary patch literal 3989 zcmb_eTW=dh6h7l?>}=X5X@io|3!zZQO=?O@3Qb5axe%9J+BiwuKnrVU<7{Jl!+PDS z`~rkj35hpU2_8Va@Ia+Btw8Wx@jGb656Cm(JG*0h?Al5iYRfY_XV0AToy&aVzyAI0 zPa--)g)l8fse`VC>3R#@h?1X{TIds=ukr9H4>x(Z6~XWs51;d(vn(B@3@!6u@Ny+g z*)XjN+E+AeQ_n3LcG0v73&}A-;4j_}<=AE+I|Pko-O+2`&8BY~83*qo zDKlHpos!Ka=I0>^4Vwki8No8vw}{Q7uKB%FX2F;#<nSqfVY%Y5njJ>#jN3Qox8_ue=d`0I}IHIm46Nv8PEbweUIy0HCb0ewrn9l+Znh&yb4}3f%3e7| zQQIgwb4Ib0a|9(DRCHI?fi|MAn`OHTO^~DAc4fW{KAbfik08JnNpMxMybi>YOdlaA zOfxg>$3bhrO(AS>-ycvTFSiH`u!k|1ziKU17%Uj;b9swhdx|xtq@If<#;1#In#FP3 zwroLXW7|`Qiu;mf+=?=>2t;fn0pj}QOZ_(`LSbV;Mj^sz?c zG@;R9>e1*39o6U!>eXnDX2N7@beq;R%28gU^K=1MXSIX8Fws}8s4h-H?Nv=#GayKN z)pIXc_kLSKFWlklc6J5#QEaJE{sG93{{VtIDMUTApAO);3-_>(UZZY&dn)ULSiP>S z4?zckxw?qWoe>`N-*lgkxpt9MNRC5t5wpM?j6WvdLp=B?hB4#{Td5Dvnx{ZW?J=^F3@BY?KnQ)e<l71y6gLGD6JuOob5$$3tF1PuVOrBCQ;vuZWU&Zpii={!D z6)b831{I?(ndU+Kef6rTq*-h=O7qaGOY;ZP=MZKtZCLb_J*vsNpm+?+69Q#fSZ*7X z@i3-p>dsM=X++w;594vG#axQ0p%hgx>LkS1@yuL-B);sI^KLQCFHm-1ADFL((SG z33HNqfcPDghdi(xgP>6wYk*s;X4b=feTTTg@-N^%@rbl~+&6*yRukOy2fq$CcZ)!Y zC#9n*oo-NDJ?`|5aC+71!Pc(FqFLvq^iuqj2D73izMUK8`P-5K>Nwuajo$t_zlM#joHOB;TG3 r71y(ncu`-4Yuv*X*FNAQ>I7NioVEYZ-1Qf)z_m|;NTG@Os$KXGP5<$z literal 0 HcmV?d00001 diff --git a/target/classes/com/ruoyi/framework/web/domain/Server.class b/target/classes/com/ruoyi/framework/web/domain/Server.class new file mode 100644 index 0000000000000000000000000000000000000000..6cf946e676e0cc88b1ae8ba6595d45477778e0fd GIT binary patch literal 8318 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 literal 0 HcmV?d00001 diff --git a/target/classes/com/ruoyi/framework/web/domain/TreeSelect.class b/target/classes/com/ruoyi/framework/web/domain/TreeSelect.class new file mode 100644 index 0000000000000000000000000000000000000000..bfa17ad66a2dc3f0de78a03ab470e608804172f0 GIT binary patch literal 3559 zcmb7GX?N2`6usjlR^lim4G_vw)&SXvfI@&e2}>X);4H-s!E%UQtjIhLMg-rTwG&3#Y$=f7uv0XT=BQ`m`#6mH>m3p$aJ z)}*wiq&3|N6*HamoNL9Kcq@f_cw1WUD0o+Ty_drKxGygs$jfvm1m+av6yy~c0v(>= znz}V-xSr|QcPFk3h+6_}V~*|ly6w;DR>^20zWfP+gqi1}%(A|!YnE;=Y8l5~Obaww z`n+KYbjGH#zH0`P&ABDh%DaYLSz7Wf5{|~{%vGRV4x@FUUZ#hCQ$R%n3_U4v)MK8x>5Ag0i!%Ef!?|o@t`LS zf62+u=r(@DcFg_{;=)wyY~h$psa_!AUpXHRcThh>R+;pEPq^>Z@BW-iTy z8s1g2x~gKXk34qAAtDC}(n^rAiq#T8st zu>$sqj%$wNd%mj|L!Ef-NfbI@w>QJ-KvBhqa20qee3TTds(65hD%S8wU`Nn=DgmfC zh(ihj@8 zFN|m?#rMRQd@XFFC&U`Ti`p`)QZE**HGxAlj2B9FPIhK(JTh1ud#XoU6A(LNK`#ni zjH`DeKonabjhgj@-oF8Q%(3Kq!;!5i={xdmByg~XV+2z1B#x2`mkFGXliA?8NuKKy zfNW)<0HjqGfV8Cm3%X1}Wp6OT*&xYR_ zv5}gEv+RCx_xdNpEuX(Oe$El{m~L5F(>KxzeiAsoAt9T^L*+f+4@Y?NJNfmt3yo~6 zyU|5^4?oHhc$t4T+Q4R;4esoT96cP^M{tz)Hu@jqzuQ0$deK6Yt(w;XPQJ>!;JvBu z4~SobRi}7O5d-Y6jYJxxrG`$>aGFmN;0!$^z+*H7z854wU;pn&JVArJH$EXK@5!f7 z1fHQ8c#4(;{zU32S`+vi>Jzk8x9o!a+C0WBySd$QhI;}-I2ml;6>br24_v_6fcZJD zXcBw~Ii25%(~gK!d&H?D;`CBoochSApPUBB=@dB)Zoz4Yy!eU;ISmDzF5(i0WS%tM z!$kjvhF>_4AQq4e2G4Gqp3;8=QO-2r%_zqj8oHkPFKn4+7_IoR4beg1f2MN5W&Xt)V7|}_$HhV6ClC8 z1jcZkVw8c+aHNv2Kw}L-w!?hgCb!HM++_I(+#p6M`6P?rNR{nDP8{YxfpKp2dSKn8 lNWmISP_zp;ab+*Q#Si!nd+`RxrF8GmH;Y-^jVvAW{{ta7gP#BZ literal 0 HcmV?d00001 diff --git a/target/classes/com/ruoyi/framework/web/domain/server/Cpu.class b/target/classes/com/ruoyi/framework/web/domain/server/Cpu.class new file mode 100644 index 0000000000000000000000000000000000000000..3e5c0bb5f17cec73af372836235e14ba4a993fac GIT binary patch literal 1690 zcma)*YflqF6o%jJW!o-`KTSwTb*tPm;*289H~;0Kd!yTD@GCEHyH|H;Id zKqP+f2l%6m?@XI5Hrrr7oXgvDW}Y)M`}@z&UjSyXq@fp2QqVCa%(S|=L!CbjPcb9R zGojB4^IVu0!ptS`GJ$yoYWdKA@7EM4s|w-SN%n0uXJQ+tO0umb#Ii-vrLtsGjH>c486>EDkYgt(wIXUCl52bWytkUqTaIUoMR6u^*YWGT zWag{6BJT2e^z!|vhzMdv5dkedQKcuX^aPfk%=Eob#Gtg-m>Kj6eLVjI%6D0IomQ2i zh8v9aW&uVp%1psc+@k%r`4o|_9nBM=&9`xfq@uY%AE8(CBWH-Tx7MUOg*c|=3d7B+ z*7gET%&YJONUbmtO!siV#WaT)BOskWMKpuhIn?+$65l9A&>n5}0$n;7?ExOLs!10` zls#&G>(Bp+%P=T~ZJ_-4PcrAZ7x@v2es>C*m6-u5>{Z z4aG%3s(NoU;qFB literal 0 HcmV?d00001 diff --git a/target/classes/com/ruoyi/framework/web/domain/server/Jvm.class b/target/classes/com/ruoyi/framework/web/domain/server/Jvm.class new file mode 100644 index 0000000000000000000000000000000000000000..44bc9c15ba9f0f1801cbf03f495e909a886882d0 GIT binary patch literal 2559 zcma)6e^=9H6n;`@12Li?Dk?g4n-l}6CHMj!>l*|DTnXbjkR+boS>v2i|o zGF}J@_{8eo(R{mw-NNxRD{xR^9zZu;5o&j8^m-e%V^KrL%o(%cKlQ z$9uT0V+5B}lyt0PL&s%Y(s4zWZ}42jrjBp%or>>u{D3JPqqwT$8m_8%p<_#oA3>aL zlyy|(WMM}~6*V0;rWB05^Po6{mS~=A?^@+R!D#C`PmbprjlkZk8w|){hqGeLRw~o!N?K&IW zileq}CMG!+rJ$b)Hk?Qi*Sl1dGXrZ~YEy7-lfSJ(VJny0T3L}pvB>vy{WLk8huX*#uEVGHYGys?#;^Fv_J%;W@h=m~NS=XO2Ky5bp#z z*e1+|OoRm=uOiPB^411Io}Uif;0Nj=zh7yjM5{cdM5f_RrG`6o8Sa!O_dzHkZLT+H z8Qc}_>G40I{1%4Y7;g-)b4emCKHIMT@I;zoJdXWLhV5!waH{^YA3ZtQd{amO+*n>yAo0} zVrp|o109rs@?WiX;rFPG-(Wn|J28+j5M_+SKypy2k74vRp>?Vx--b65<9&iDqKHbS zxeH!@6Q}-yuHaW1?J_I10rbZJ(=k9x=dW?P^YUif<#c>`2A{?nO-1S(pLm1Qo!x%H zTDL-klNyy*FYfjzIrD>)$Z4RHQ#p~-XUUufIyu$I$?n2QJ_9kQYayq*F{c?8Dvj2D z$C)P1HZj;qY`=||7B}nONrWbbLWFaj2rm;6K93P*$v`;h;jo7l{DJeG7(cgRv_30y Jyp_WJ{{gN3uG|0s literal 0 HcmV?d00001 diff --git a/target/classes/com/ruoyi/framework/web/domain/server/Mem.class b/target/classes/com/ruoyi/framework/web/domain/server/Mem.class new file mode 100644 index 0000000000000000000000000000000000000000..71a5b9e454b5e5ff79449143c4a8fb7c9e038fc0 GIT binary patch literal 1143 zcma)(+int36o&r|%nXiW!GiVFTHC4xdKjywUer?~Y0`$OiER@v2iS$F43i84jj!O# z7!!?+7e0VjK9TX?0|7#q#JO09f7V+2+k5@~`|}roCwOea!u?CQj0gJeA4>d8tfHW# zqB14@)rXP3W?%AF9ec>;tAE)ew}Kj;K zUUUrdcy+G3Iayc5) zVi`+Zqs1L2kx+z;5(+Nz~`+w{V+PYQ{^VCh3jR;^*&3j1Wu~0Lw&K ziAKytxiJ{Q2HtO;$1ku-6HFA_ovCbk4BP5dw#67WeUfdRY#T9bnQ^vE#CCTon-Rmd zHI;2ChRv8{+a}x77`E&@>%n>T0hBmzqcV0_Nmucg7HN!_}^EU;_94 E0oYodP5=M^ literal 0 HcmV?d00001 diff --git a/target/classes/com/ruoyi/framework/web/domain/server/Sys.class b/target/classes/com/ruoyi/framework/web/domain/server/Sys.class new file mode 100644 index 0000000000000000000000000000000000000000..50838fd1a9a24eb589c54929c252f35629e8b674 GIT binary patch literal 1425 zcma)*+iuf95Qb-+v*R?OwB<}GP{;xBO}RjzAQXhCl(vXK@10~xT#~rjIIa3tAc2s$ z-~o6j#6RoQZOumFW@cx0X8&(z?caZX{vx6+dTdjkHVc%a2W%d)sj{iDspqJXqep_A zUNAnKg;MQ$V<~94ec&B=?#P=A+)k+c$>6yl`?O~JP*83*mFl&x1Z9J1)P%(=)f*!7 z!k_qIQ&6f}+ZU8>1$~@ZZu=9tI~#YU`rvg(S|#XtqkT{L9M6;KaOh73HQG>f)hsyi z-A@Wo$AS9n9?P!V55}H9aUp*smD@R)0;W;cE5YI;O7d$Z#0+ z-+3&K!GNiBPK9SsI(HbyHugWBj>X1i92Bs#Hy=|08}VA_qp3ytXl-|bnd-?mK94Dn zJ3is}aHvcbhYDmnRAl3@DY021TTt!4M~3U2J@8)lK=wk~!1s{HH2VW#io+_}H5V`OIK2!fe;;SxOhRy0^o-SkUL&ELrSl4#yE zHeaP{AmrvIyak`FH@=f~CT;6%9XD#|6V{@txjdusJq2AyZ$=BU3ub^vuX~+su?sGBv^Uasd;+f6U})rgbAzCdt$?GTkvV zWs*#9!L+@AiND2|sic`Uj7-@i(~gnpu9+#DWO@&#&H^U>FJh)8&2$geqWgaVyzIlq literal 0 HcmV?d00001 diff --git a/target/classes/com/ruoyi/framework/web/domain/server/SysFile.class b/target/classes/com/ruoyi/framework/web/domain/server/SysFile.class new file mode 100644 index 0000000000000000000000000000000000000000..a70581fd9dfb70130bbf5492d59035158f29bb1b GIT binary patch literal 1825 zcmb7^+foxj5Qcj;hYd+c00GYjJQ0Gh9zaDcynv##3eYluH+r#2hOm%iad!iir&6n+ ztn$JK@S!aKo{bw>Cl}uQ-P6-O{cUF^KYxAyK|~AmEK6CsKSV?HfaM{}BbEi0MV7}b zPfU7h(vl#v?s%JaO9~oW-?xu!t7*3zRweM9cH_CAoZt1gx`#4M$pn#`3f#bM3QF#J z62-}mFYAI*9p7$XDOLrgUpQ?iSQeBh&Fmm!#jRtfxbC#&W~Wt^-j-c$s=jW`ZtmEg z!|Pr!8SFW}AZxwmwk)sXb{%Wi1Lm>o9azV*YSrDA?X)dldPmZ;DqVlgX-WWR8!}k^ z^GV~S8Qn)^{l9W1O8=%X@5qJ>_T2hgo7)D`15E*Eqf>t#1srrHkV8g)JBRK5D#Lr> z&@#Lcx+bD)YJ?_&JrA*MHBXL(9#?ON!^J!?UOuB2NS0QaI9qW$UQO~K1m$}pndcuh zPvbO^ryS+;G|W<9DYA^PjIxZ;IYD#(HE&F*e`nrQ_hl`hDcr6s?xINsH-u+!Pi4_o zS#(ww&6P!eWq|-&B*DDUNvteb0h=m+Bk@(Gork7XFhdvM=fecLNSCk@beXO|&laDM z@g-JlX^XGYH4t<0GOPicF3+Bl(Qlnq(FID;qB>zR%<8pgH2!Wu*HN3o6M_+RL(7zi zGc9SEru0mSIMXtiUJhX5-ymWdR!ldwOvyOYik9h?o+%k;dIhG}1DN* literal 0 HcmV?d00001 diff --git a/target/classes/com/ruoyi/framework/web/exception/GlobalExceptionHandler.class b/target/classes/com/ruoyi/framework/web/exception/GlobalExceptionHandler.class new file mode 100644 index 0000000000000000000000000000000000000000..5dcc1c9cb50fda664c97631b4406249914536e40 GIT binary patch literal 7033 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 Tnx>Erg?{B=tLa7F+64R$SR--l literal 0 HcmV?d00001 diff --git a/target/classes/com/ruoyi/framework/web/page/PageDomain.class b/target/classes/com/ruoyi/framework/web/page/PageDomain.class new file mode 100644 index 0000000000000000000000000000000000000000..e00c530e59fe1a20e71d40e7e32e85ff7bbd1428 GIT binary patch literal 2373 zcma)6ZC4vr5Poi6Hp#LfX(*{^DbOknNy}DktCmVzUaDvaNB}L?>XIyBX|ju(O?&W* zzk^@kSM(Ih(R2I({wR+#yPNES={fx}bMNfDJTvp${pa65{|0aydoo5ao5X_@-oZl| zk1)rlc@_&Y7V$WVFB14tMhdfhU*gkONqo(^%x=ppD*SCFiB%cjU@eJi66X|Ixy57TWjerE&I9pQr}h?x4KGV z!D?v+*OME%v*NWNaIrAe4}QApXtn3d+P+_gV>pRBq2V(z!Hnc~$SGe4_fPP*ha~-2egj4V%eo~-eTR@B}sG+FfUA(8@GK)Hgr>KT*)`@}% z=n8hwAZ&HLYb0{mrqlf{=|6WgZskk^-t1I3m1PIz@Q3t|pSWn&{(dVh(AA=tA5g`99qi$)6a6q2gXl@2x+NUIz}ewTz0J`8t)A^L*Dc9GmyVKbb(u3 zSkQ$>@nK&~+4Gog^~Lm2e@xl)nC=j!yKlh61n6P9>|*-3x1R8vr1O~W^~H1>cc}0G z%}F|s=>cJS_y$Z&#vZ0o7t`Ion4;$~E%e3oNe|PngpGGsD4pWMA+F_er4tMtB9)7j ze?umB(Ys2y;WBSG%BVf2T|t7*OC-z4qC)ImCC;tU^H`@1Z-zQ4^fq4LzcF>f9E>BH zABc6AKSmaTPw^Q^o>hgk_q1Liev14d5`W;*5z#ZwEr!XyMPz?U&+J*K)$Y^s$v#NA k?qR0i9zrkG<7+=YCfWCWl82RlBs)&Kwi literal 0 HcmV?d00001 diff --git a/target/classes/com/ruoyi/framework/web/page/TableDataInfo.class b/target/classes/com/ruoyi/framework/web/page/TableDataInfo.class new file mode 100644 index 0000000000000000000000000000000000000000..fdc4ca45841e71446c4792f15807213157e76237 GIT binary patch literal 1720 zcma)-YflqF6o%jFWec?e7AOiR;I%Bsq6jJ=m_STQ3KENaU&>Mz+g-BVO7Oe?MiWgW zCVub-_@j*P%(Mm9l1Rer%$aj}&U=RN>-UeJ0G?nqjTD}C!@x7SJeSL&T$WN;PT@rg zD*}CWCvffRt`pQ<-+NnF7Z4i)SJ!;69@<{GYgZdilKJG15J-l8XjcUifqznGc=5=7 zZ(EJfty)F59xe%Vm)weHhmF7y=#N}4n=AB4l>L2Pih@AAR<8(TwOiHpDpn~BT(3ee zW7+lGkcjd8W0bn9-Zmui8?s)CE^m~)+5BXbNMP_Eqn0(rL^`ZL zT75~T%|)#;tW{5|OCkz9JhWd^`z-CGQhS2&D`SYw^#)IK{0e@P=68s% z%57LV#kh_kfpLs*m(flb#Z8`Mf?J5I1XX_HwDrjZ=F}Nt=IF;W#AU8HA%QHj0Fzuz zB2Q7SX_a$a$2D^@1M5j}x$7ZvjI~>p4cmQ3{?C~})?%*z0*^$LSnIvg` zL;N#0F-2i0<7~%n?%_VYWbjI}8^auH_MoHFya(nf`+~}lQ)i>iZYc3JUs5PGfd|Mp z(^@RvVv2opui(M p?!c6cD!J8>X^#I?TGPFtU^2>7BGb+#O!7~s%`~W(=2c4ze*k2@3J3rI literal 0 HcmV?d00001 diff --git a/target/classes/com/ruoyi/framework/web/page/TableSupport.class b/target/classes/com/ruoyi/framework/web/page/TableSupport.class new file mode 100644 index 0000000000000000000000000000000000000000..80516ab1ee83a4828ad7f346231296c08e2a42b2 GIT binary patch literal 1526 zcmb7EYg5xe6g^uW2_ZBClc6K#J!JIxZTx1k1o>TrqGJ z*Z9wM9XEKsspA&Uw{_g%`L2#Brr%?5-@qWIH9Qc|=L(O?i?#V@0-5StXT`BR$8TG9 z7`T3WN?@#{{BG#@;ez8uQeY(J+m%;kf$@4{rrcO8zFaKTtMkul0>c%1v0#@3j7GU& z*K37hm3YJVT;C0+1qQOY1%bhmS|X+Ks_V;I)M?7#h12vXlBuee<1ILW%XmFF7`}14 z0+ZF2>R3UfKDpNG!0E^jDtKpokWFjZX-g|6+tKo}3PN_>mf;-5XH>^=eag<}cFymU zDcfMljnw~1OgAIfTVkz-d>_efNLsdvf|i_hd4s!?HZSpMn>dPN0_S(!T;H5PZbxQP zUqIu1&+S`o}*#nG|mX5`%P4DzLhOHgVTM_^wm+m6@{+XwQL!z zcru)4AYjHdnv$*gq-6?tNdsg??) z70QpHMLoYF19CnTD#{SJn>5qg)>OjFhU)e!8QoqpQYF~Wc6oFwM%P(f2s%YV^<)_Y z_9Zjd?Pm?a&bZw)CmHY@v&*9}^30jKEo*h`Pn z5DsA<_Cw$Rp;U|yCa^*O4<)ck=x_q>B77u)(-oZ z92~_-`Zjw6vNSKk;W literal 0 HcmV?d00001 diff --git a/target/classes/com/ruoyi/project/about/controller/AboutPartThreeController.class b/target/classes/com/ruoyi/project/about/controller/AboutPartThreeController.class new file mode 100644 index 0000000000000000000000000000000000000000..ff5092c81b607126ba8e4cf8464c5e463e7a49ea GIT binary patch literal 3803 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 yl(@3tQiN*~YeP?@}`*e|urHsXzjRdmC zjJ>I=BGikdhPaCmpN}4v!ik<)2Ijz&LRAIQ5 z;Bq}TMP&H6ANX?}HCzhg2Ii6X4dc{$L*zFB<~61O&mr<^#QJqa(Z1GCL#swH)$*JW8^=34w?w z58Yi2T}4%YojyJROmUxJkYJc#WH8L^+Hr_p3=WGe@0m(=Yg*=8DMGp^w4$7~vMY^L z9di!0-j%T?IRtUFRY$e>y)A_1*$;T?NIvS((#~tEL!4_2w9<;LqnCDhV;|l(UL<`^ z4Mv$pU&yO18y}DJ{};cy5Pj{sZrm8xs0ri}$j#(nU&PDE5)lMpBf*uu_smw-GTYrlPmj7k%Tpi+ ze}I3%{}QYj4XErQC=J#9UVU`e>$?8;_X@xkKDN=oY71*Ethew%VPS_I$F4$ST;>^mWRI7c9NRNMLsSIO&5_| z!QMTt{Gis(%(qyR=MnNj$3RJzEP23)Y{zU!QiqK0=O`$o@)kuLJUZuBb{OXoiP)!X123X0GUc|Np=^6vgZ-1aJg_y$|v6UO> dU{UN+eQu(Sw}KjitEhKauJ;aBWUu1i1GmNa`!4_h literal 0 HcmV?d00001 diff --git a/target/classes/com/ruoyi/project/common/CaptchaController.class b/target/classes/com/ruoyi/project/common/CaptchaController.class new file mode 100644 index 0000000000000000000000000000000000000000..452c9abdff3f9a823ac3a4e54165af8c9f7483ca GIT binary patch literal 4088 zcmb7HS#%p$8UDt$G?F}VTeVIRsgo`y*p8E-#!Z9Lrm`j;*jIP?@4x@wf9L9d zUwj!rH?Br-2qJ=cq{HaILL*EphAf2QP%YytWj< zGFIfQ(1^$JLhP%$hC=wXJpW7tpT*}?Jfq_C3T{doIWM_jj5u~GpA?RQy9bhX zR!`e@IwSO?of7(m5{X`+Ij$f!nzuYND<)0XoXdzF%d$PgGi}RNuy0_|STa_0V`Ebv z6|S9kl7crIEF&uv+L70!wMZ) z9viTowC?5{(@Iwf=Y(Oo`n-|!Y^PAGOi$jkmre5F)yA?K6V8&EBw2Tju{ptM{zO8+d&h2Bo7}NpN5<~gyoN&eshDTavCymUc;h1t= z-fZv|OvbBo8_`UGtZnIirN~pN=h&Hya41qu3$G+*qOBZhsbNNV`hw@>^wS)qS=?aQ zb4&?wyuC(5HA2f`PEXlc!?g6CMPr53`HUwe&{gAN#Z#bA_2yDgOj}P3_pc;HPNo^D zLQa{xg8y3&ddQ$81&4N}P*XW}{g(7zih9kEQ$BBU8eS5PIbS#<3XBHnm^VC=b;BGm zvqqY~`%5Mm%bxD<=)L**dBF@!$aC6KXEP4lCtBV!Gx{mR^?D1Q=y4pQFr4>td5?00 zk>%YzW1f+`Fi4k4T54Y_O zg1V{(R2*<4G$z!$B17}bX} z5)Xt$EIho8npe>ZV0E8d#9|}BH~8CzcmO|7^)&r zw6AAmyzg|+x&B8-5~DriiQyrZbxkM|mM7AFcW&|H;{yaaCG(CW2x;Sds!i~rz?2(I z3{8ypk104%y1x@C?QX4RNVSTueSZ-jT|NjTW>%_ydK*vFC|eF{%}z!A8)zh7IL*?& zl~Nm(kR}^wVwkOV*)csfokUr76%$68n$=!gBefi((hhlT>Z`J3BYf!$5~jQ-U0miPkGth#Q9JLYwdeF<2f<8Qq26?$|mKHQ}@84^v z8|-)i_3LOL+sxz-h(xs__QV=E6wwq9DDj{YSCx1u zdJ`GkT*NKU;z7=$>$p`xJRA$x$HLLuifArkZ#)>|hQDe!U&QTkRZ_-+>v$b44P8V> zG`7KIKWDG^xlG5^cu=l3D7n}d3&hk*xZ@I9ir9Yz2dLbgloyKL6$`K7?x~AZFA|Fs z(Yl5=tae5ZN~P7Q&gdIs;Ue1B(C#l+>5;4W6aLI^xOzz6S=h(%KKke=ns7g2I7Uy# z`Av5Mt)z(4lPBrRKJ;T4k6;F;`7N420dM6;;u$=R0X%~tK9Pp8ic!3ZaY~q?jJHwB zH2#WN{0;BG-*FEA!g)VNk26X$Oq+hn43i>^Q{)_xu_?z*&Q4PbQyKrHq%%y8Cj5i4 z2B~)|{z8dIc^2oBW0L_&UInDwKwTE9PP24=s|6{XI TTo|-tjwf|U`lm8;Q^5ZK%aV>7 literal 0 HcmV?d00001 diff --git a/target/classes/com/ruoyi/project/common/CommonController.class b/target/classes/com/ruoyi/project/common/CommonController.class new file mode 100644 index 0000000000000000000000000000000000000000..a750e35cf95681a46fb97c43c33749214d7d733d GIT binary patch literal 6665 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 literal 0 HcmV?d00001 diff --git a/target/classes/com/ruoyi/project/monitor/controller/CacheController.class b/target/classes/com/ruoyi/project/monitor/controller/CacheController.class new file mode 100644 index 0000000000000000000000000000000000000000..d1074ea4b2f32ddd2ec2f17104e8c34c18cea145 GIT binary patch literal 7634 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 R&ucxIYdz(+4&&o^=D&hLHB|rr literal 0 HcmV?d00001 diff --git a/target/classes/com/ruoyi/project/monitor/controller/ServerController.class b/target/classes/com/ruoyi/project/monitor/controller/ServerController.class new file mode 100644 index 0000000000000000000000000000000000000000..63224238fd6b2da5a7fc873036cf504a9d71461d GIT binary patch literal 1166 zcmb7DTTc`*6#lv_JF>1^1;iU7;_gDEFBlCNl@K*13lbnC#;5L3mg3BGY&%^1Rr(@E z6Muj|%6Mjm?4Ti0=AoT)dcN;m`t|3pZ$AJmV6F-elOFC=QNm=pOjU6g(;n`5m|>_q zQIT>_8G0JcZHDrS-jNIgEfvYNBy3BwCE5XbhFjVf!L~3ewNIU8x2G(_qn58jZW4X0 zcx?2(^c@d1(Q3ne9XX?eKpMU&&4Dy4UB?oG*OBgZw5u5&G@55vc8v(-p*HXNp=|RV z9SRlk<$ZCqA#D;kLRM_X#!&wkEKf_2>Wd>^#!l(TdbrOpv5`bhg>qY2MLjJ?k#-_G zW>{=#)8RHYD(V!)Sm`H5xnnMTUs_Ae%2->+*Nt3G+@3b-qohQ=2O>ylX4B8Do!=97 zU7Aoe>Zc=F%qw3Ely&tc!@{|#(!Sd&+To&G7Jnt(s)%DMDeY!cC&rgAReC$4f6sP) zUmOUA$N$YugLQvg%|)Oe5<(ZH4RmimZ?%R?=wp~ER8oxe%>Cv))KEvChei!e%rZPI zKo^h}!(ci99*C&J-?URjGK>_it`bN{zK)N#NP*2F)4Amp@*~~aBvSyxvqr0+%`+`s zM9G*!giJT>*MEw*Bj8n#IL^P2Rj^&_J!nBql3={DPMV4v5ic$I- hpr|pj#w|f7$Wg-eY`u{k50ib9>|1HbJ1~pezX2TcMwS2o literal 0 HcmV?d00001 diff --git a/target/classes/com/ruoyi/project/monitor/controller/SysJobController.class b/target/classes/com/ruoyi/project/monitor/controller/SysJobController.class new file mode 100644 index 0000000000000000000000000000000000000000..889a3f75fe2c443fef4f98c11fa8326c8f61559b GIT binary patch literal 7250 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 literal 0 HcmV?d00001 diff --git a/target/classes/com/ruoyi/project/monitor/controller/SysJobLogController.class b/target/classes/com/ruoyi/project/monitor/controller/SysJobLogController.class new file mode 100644 index 0000000000000000000000000000000000000000..db614c6d75c8b1c967ddb3bc45427fc99d461245 GIT binary patch literal 3954 zcmb7GSyL2O6h3!YhNTrHu36C-84&3haS0&GGAd?30*G6-o|y|Y&PXVT8z_@pgk*2 zI^`_QNq5!GNI~aP87FVLC1=Ani>|XGGuq5M1zS6=sr*CcHTpOWMwvDIkTmL))p?D3<9MsX9Bn8&f0Dyl+bC zWhXmhaSXL|J#K4-R>@4swKbxe$w;M;NV4cCZO*t7!CiJ-`;mmcrd2CfLh!n-DAm7g zsTt|!?L>EwZiC3oL1w?ZN6j;ZXhXW$2O2&963QJBi2xVV89D0m~(71 zxt@`^TQ~=Y=67^(*O5J5KLS2;9D_3v+hJB)s)!!3V&<&EQZx=|DjWc9Yp*{4dHHhd zr=S0Q`s3D%Utc}`W$UlUf{b)Yp^%lDN0M%DJ$3ZULMgAzv4|LKG$-%O+?<_f1+|i~ zR+7vh8^bJ#o8|O~FG_PMBlBHgKVl_)InX2u2}0xpHhyiu*L7Ili5m*dIWiz z1Rbl-_rsEOH<<2r^ZT-n9|RrW-d`&Z@olfk|Q)7EFH;t!~qtl?$4D_|lIVCqEuiBjdM`}!$es10Mrem$FMRMtF!>RUQ ztu2bYispPrWvcrDGw3iKF=&||@6drb*#@o91A}su7jz=L6x44|f_lSaZ<}3guV9cv zMS~uaYmg#sP>EIzTBCJ?HW-MaDezNY>6@oFI#wteHWa2OA~n?lt{;bz2{J=3j^soap8sa3XTsz$E1@$K;>r-EAFtmH^!M8!ja24;w z)m~7Hs}AcyyftGz6s)L9ydA}xiB({2>U~DycWh$xZg7a`19}g8!#_apzYQOjWC(Z*A!J6kuYja~QAc0z*aJhjsW@O2T+e(oV2@by#>chPC; zhb`##u*V>A(8JyUxi&ESDKxPchXB<7cLFy2e`oNI{b`Ls4Xf~d>c)YHy8|#R@~^IOEdy?0lAm( zvx?ly5pu84C=SFqXU4D*usu-^#U4hnw;IKC9TZ8r8lp%BC?a%RE7Nh^r(=?C;JhF5 zFRD?cXbNsJH^Vq2uK?L$tLX!LA7~w+@)5!=K=GReQ!VyGf@k}w2$Ft1j7sz VeTm~Sn)L|m#BL7n^A(i0{s(kqTfhJS literal 0 HcmV?d00001 diff --git a/target/classes/com/ruoyi/project/monitor/controller/SysLogininforController.class b/target/classes/com/ruoyi/project/monitor/controller/SysLogininforController.class new file mode 100644 index 0000000000000000000000000000000000000000..e584df0f2fe5ac929f823c8831f302c5739ad638 GIT binary patch literal 4113 zcmbVPYf~Fl7=BNH1j0gE3a!@1d7Ps3MI&7hcSU?$79Umdlod~0NUg+am2xrCVzkES zux_quhGwjo);zb?HI*~EoL0@eY`Nx^CKoJojjN8FHx11(E!p-r$y4)NcDhn@h|%#m z*Ko8vU({@ES?8mMVLFPVnTE~iQrfguWxHT$#_Ectc`D2%ub{= zgU`76Wo|7f;LfNmZK{gCs92hK4;Jg4oCae?Uf?9vG#ty+b#BQ!+%#TV2Z?%JvmHja zlP>_unt4Stc7hCbmkETHZ}4TgpsaG)qdB2C$`pbMJ@GKYjLwzGR))5!GDe5eYs$JJ zyN;&IX;22_(Vc$TDinP)RZpz20|erTFIIWwD4M2a}vg0ly!W=<*CimGzkMy~RL zX*+Vp;>ewxX=(R3@EXx!{j?rL-!;bTX0L}^O@eE;O>AKyKD@YB=39^iG^wNbscy?|t7G_XH1H{84}kCll5 zJ({|inVnl;)V*)6_%RmQGEj52WRRU?B)Q~QkY{|_L|W}#?Lobc zt|^&P$6C5ecLj&XT{eaF_j85(x z45jMcF(EG0tSh59xng4$X`$*EBaF@_59Y6i@pNi*2G+^}K-4vKQ@z9JieFQH8qPan zW*smVjV^}e40@OtBwi2oq8eb;5)Ge=h=%n1;m^;$dG!33AOHRKYnVH`FqxVIuIz7- z?-zB!)A4!JwN!ph6LoT8j{(#t2< zs}kK2mm}03BVD3A84{UPV00=xk?h^A81;qQYME|A=Us^`vL$lJm1vzdB-#`dwrEnK zWAsXl?n!i?zGO7KH}?MXp3(VGJnVJCTV=vyip@hGGitIO#d0#3>L|ldzS6W8p2--s z+o(&`LH`U6dSZ$*8heR@$F7rrw8|ZE)(FlYewR)R+sGfQ$L1#SBPG2wp1q7X> zw*j7j>P|WZ;CCP?RQoHZvC@T=2FxDZgptRvl*EQ^n|gqZhb3Y*@G&^35BS2qAE31X z_fA3Yj7M+g2}N(U#U4@PVEys>-m`5@+Y}e3`)u1FA^x5<(*S&U0Y+c+829@)CB~ib z-8tw2w#PjJi4Ko@9pvJ4p56tLu;M6)`rl4NhyU$8d=vIGMF2x3o4T-}AKM3PXuinl z5?wCZI7ma#=G!=or}t?D7{bPJ2t{yD^aL?zj?gwqA^;ID0Dd$_JTn9tY%Q6QpeuA0 zaDj8L;G=M^AQCutw1UwX6c&k0Pzsx4B8RTwfzfrEgaW}za1`EY6{H?h3nOGnYe*{Y z5xGW9kO|YQH6XSRhp93SA3)8Aln$Jc0<5sG1!@z$+jOX!)>&$#Oqn}cO73W(X_^6G x;Erh^RZy8Nx+CKU=%cdSoAcZu`R?#Ca~fpk0TQ7F52y`KH}Jj)9eyl7{vRPaiWmR@ literal 0 HcmV?d00001 diff --git a/target/classes/com/ruoyi/project/monitor/controller/SysOperlogController.class b/target/classes/com/ruoyi/project/monitor/controller/SysOperlogController.class new file mode 100644 index 0000000000000000000000000000000000000000..dd65da4cf6e4457f13220fcfe656db386b29edf7 GIT binary patch literal 3471 zcmb7HTUQ%Z6y7Ir^jZrEmEYn#sJzGDAi&0g!;XboZOqOt!Cme@+ zMi`YNN|W_PFo-&~RE(C>`=As>#kAcm1mrU_(6qY|@>52I!s{uF9gAH+2;i z_mJJumc^Cas(H3TT`h}C2`%xekjlt=9OW$w&;FVNuYJvQYAD~iyHXC9O_}Fj#ZLE! ziW}8gK|Mxw3Hk>ZU2W-0XSHCv#byoN=W52RRv|d;#RzH`ad1N`Vlz`{J2RY;fbC!O8Z&q`@yN(kE;qyKZsk zA@(7nhlkpD^!ko8;WlJz$|~l-s^OS!sX577=*X_Jm2Sc0%kRJY=Z_y>KKtq4zn}`0aJTw3-X3jM_HdTc1s>O~0zax{=bJ`pFp1sU)4G zQ%U+%{~V`OoIXp^=k!IAz9f)5*Em`C?rlaxjco_cf9a5|xu=YyYb> z#pu@FSO*+@j7A!^jY**M(quVdO5VsOqYkM|PvvpKVOBTncgC^zhXndWLb#Ti3%)P( ziN@&8QP198M`E(Xl|F964}-B_G&h}^dn>i&d3_=T@aMv$V@jFP!@o0_ApWFgG;`Pt=JjHN5fonUi5I=rTD9sK#D2j|gkHn1-c0ls_2M@f zY@pZa4LmV=6YFihaRs~Euy%5AlOlt^QrplAibiN=@WLjw|BM%HK3JE(={!ZLAI#D| zXkTC$f&zlxrgs3|4cBi{AArw2Z< z5coR2AK zwvR}7zQXYb;@Vb>*Xahl1u@>l-7OjghK_L(MiE@Q2g4e4Mrf0gx&Y199QDJ{WU#d! zMmJ^XHsHeKX7Ez0WjB%cw}_0*-2t)U6rFmRcI1Ig;rBQZMKgMo>vt sgH*yNGC>`%X~?WAB-WZkwiSna0J;xH5qjWLPvLGF&mX{jkggB^2VD)&ZvX%Q literal 0 HcmV?d00001 diff --git a/target/classes/com/ruoyi/project/monitor/controller/SysUserOnlineController.class b/target/classes/com/ruoyi/project/monitor/controller/SysUserOnlineController.class new file mode 100644 index 0000000000000000000000000000000000000000..f36e356058e8cd963ba9601b9168f9793e8f77b3 GIT binary patch literal 4357 zcmb_f>vtSw9eyU+>?E7%g``bg3Q}6IY0`E?pgC{35^SuUq;a+rbC~bM0&ICK>R6KFf{tPa z4qRa?b>b0to$wLJS2=?XSeDasvOF)3N(PVOF?oDe=pN6I?Fk*9%V0asw&O{8e_kG6 z5b00J@fUS`NynEpTvzs$cibu1j+#;4vYk^J?wZKCMbj(0727O%?xM;CreE;`RWwN~ zH0JnB#Xq^0b5KKey6gmYQJu1Vd$yoP9LEi;z;+#9#Jb+R>6bj)nV<8lqB`e#OXjSy z9N(O?a)IkrOsmddj+6uUob9PR#b)@j{V^-IK=VG5FfW!9Qkc_qI&!6lY{w3UHMI8b zIi(>r=H|)1X@b5^mW#8>J7LjA4P6s%&MKU;JX_Y$W-3^)Y4mVHSq+P#OR=tJ=3FQ6 z+(JQlW&<2!^?e$VD%gIYVY+u>(OS05g5}JcGl5_*xPE6(LR;+GIW=pRta)XIrX98d zYuuT0MYaS_LvK?qU**c49aPK-ciwg+SmbD3QWbG{oh?MmE?N0J#kGsnNydi?NKO!X zs`TZ+E|_EDa85!>aT|rSnmrSto0}7qvO}7lMszEs8p3@E+F0t)dfSF6Wi&x7f`<}5 zuZ>IIEn2qI7?VK}vSB8$a!bdoQV67ur*%A|<5`v+D^A1aV=7p1^HY{2XP`WKKF~C0 z>(E-cobr7Zi7L5%U`~08S-aqR_M?jG+Lo-@Cx z%LNIW9=J=&8P99z`=90~)QT6kCE^liu;rJSGmF*iPt-Wg6&C_KkUBK}{_@55-+Jf$ z7cc(fnM+*GmVK70@1J1KXt?9*l9f{~`sQeYO{5<^K0S6s!=2ZR?_pJ7_+yAO=3@~U zk89Z3*x~BQ($MM-T(hnzD_E#?9vYjOamx(jQCsl1;R;=4zf|v)`f0O-VNipHrqsL@ zH)nmMOzs$rO=hkARrA+CANmdSg2C^N=LWw>CoR$sEGIC--c1|A6$@))LAuh}5LbVo zPsdjbd;(w9&>gm>u+@xso>h@zXQdi=4$rez4ZMJ_8TdNB!FpZW#SDBC-{OFX&4^R5 zUbFRfsN+Qg-^NP@Zo#cOzGL9KxWJBJ;CuMKftT?E13$!%47?)CAItJ5_^FPc8TdJV zVc=C+ zWz4YbV)oc&iD!a&)bbT05XsW8B}O`Kzl==0E!cBf#2e%6iWWzohFh8`Tswo=JDT>P zSYWJ}$=`0*#rO2?Ni_VLTGHt3qm$}9&Eil$!wKbti!C+!aK0X&l{*WmH_odd%)IIj zQ{&3^G;;2Y{o|LUvZSORjqa23&1$v%NjErBECn2@Tbqfdg$H6uwa8w}Ws11_>_fSr zI$lRBGV?V&(L~GoiP^MOuCIW>PtMs59(x;nC2~yomMGT#WE<^pNS&5yo?}Tqy9`(&&kZtKNyWhSe(Gf?-`GA}9|7TpoVVds>>9h5t72Pfz}rU8j=h`EoO--n^96S z9iA$>%W6b0$R=ebN>w6l5M#NDM%U%xBxzNB0{A$<(vzCrWz)RTD2J)FFKK3UQ;wQJ z;_@6dR~pxmC^7nItS9ikWH_^ zco!Q6^#04(cmX^2hOh0}6i+wx@6GC~*qp*Du4}>RSMhL_>iXzdPPow2CYh_GRhM^{>;r6a0$<1qe)BO#LaKqt?W_&9lV z^1pyP$e9)dBXs5KPVNc%?@{6bhBbq~@)|Ng%)2RVkn&UbB+q;5XT$ZggFHLLvzw6q z0NZtp=or;8_HT^qIIQEyjlKUw*T2YkltdjZ)CFomm^9nCeK$4V<6|86%EyDye0+*u z6Ld2b=F9z5)k20H62Z5zox#4L>&6v)xS#(kxM>ADPWP?gW&y4x1TGWOPw}VzeIW>9 zHu>ug@4#f}uvpZlaZ$BqHx)(J9LE%S?(;E?8HE!#iBom!PUC(u=#s<_@S_E1!smmb PM_pV$#P!2<@=yO4=q3?> literal 0 HcmV?d00001 diff --git a/target/classes/com/ruoyi/project/monitor/domain/SysCache.class b/target/classes/com/ruoyi/project/monitor/domain/SysCache.class new file mode 100644 index 0000000000000000000000000000000000000000..b195ea99d8c1140a4bbe182a71a99eb769d48419 GIT binary patch literal 1873 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 literal 0 HcmV?d00001 diff --git a/target/classes/com/ruoyi/project/monitor/domain/SysJobLog.class b/target/classes/com/ruoyi/project/monitor/domain/SysJobLog.class new file mode 100644 index 0000000000000000000000000000000000000000..61a51f32c87b02ab06d6b0c8217981c7ed88ef5f GIT binary patch literal 3542 zcmb7``*Raj6vxk&Hrb@x2jwOB!q?CS=oVV5(ucePrlti+C=?akB-?aLvKx0deJCnL zM@Pf~6|IiH$S9&SqQGEbs3X7ocaWC9!tr}|H;-;NBXlO`o_o)|=YIB_bMNgx|K9nV zh`Q)Zke;GMfHqQ+i4+qu6QfL|nHXb2VPc$#lYYtsC`)RP>Ph2iQxJnW)_I$!t*oQ- z)WKSVr!Ll-Jaw}+&(jXpPVm&r+DV@JSv$oRPy6W|KfTLQvmxt>lo^(FL($a3k-Z#o z0~|H%(Nx2fRC8F$SDp`40 zF_c6`-leLVDVcC=aMT%1YFSawYtxE2s!LgUQqxb0l97{>=D6)eD$N(eQ%N}kkF_dh zawIIwUM}37E!;U@IDd;HKPZMQLrVoj%+wVNL%26TUpRZIaN+aCE7xr1{koRV!H;cO zoc{<$bQN_%J1NH{JuRCYt+{{g!^MlgEPns{!VjMmuFfw0_Vc~lH#iDnyFuA7q;whO z-rY+JGkA^veB9=N8Y-Eu5Y~baYutA&L{SZpwOi3Y)L&=v%n@W8wDg zE!+AEKV83n^D+W0<3n>SL=kmVgO|czXE3AaJpk{z8AnJ<@y?q{M%*izGNQv9rk1m< zzFtLD%zlnmw6+Z+ZhN#8)-**GRUXP`6S5wckP(hrqFNI9mXLMyoxxgjOu;8?sYp>y z*T#_pD=DfNn>Gfl3c&952%6nt<~plpt5 zse=+L3d9N^LEE#P;?+wWmy#T`UE6ckRC;!%Fy9WMlYNKA=`~@K$dddw!R6AC1{2p&_3EP&}o_x z=v9gebbyH%6Ni~N!o&z26X;DQPEeGiQ27q|zXIi`tvtDW;Y`YjVlD5M3^|PZ%ADq? z{eeFvW=^y4H4h$+#v>=9k)iO3SbQWJMmVAm=LAP6Tv@jV670nFJggU106r;~lU1C; zqm>SwgHUnkoGscOd=DpcJ$Se_;ztyu4fHfULmWMeUJX5mAF2lZHuUS!hVa{L&=wd_ zNy`l)45+T9L2I;!>cGvmUu(~Dwr6N|4hCSWfB5tU^-TX$u4?_X}iUQd(b0JJ~z{GPo}sxlh4hR1XHRClYQPCrY?&K z*Rltb-_11U$u#WE(iyv|*~%`^k1GgX+_-Nuo_)N3)}4fS9Ox|u%qWIFE66m&Cv z2BvdWnAkmAV(Pb;@T5CTzfvtUq9gNEKN7k@!YyiOX$;L#(;PLotc23SlvPkxGi42w zwMYw3=9RDdBc-eUEqJIQ=|4?$vZMe2 literal 0 HcmV?d00001 diff --git a/target/classes/com/ruoyi/project/monitor/domain/SysLogininfor.class b/target/classes/com/ruoyi/project/monitor/domain/SysLogininfor.class new file mode 100644 index 0000000000000000000000000000000000000000..0a8d9311fb4408948fa9f9e2944fbf5ddfbd6fdb GIT binary patch literal 3238 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 K2|coN?SBC+dTd+( literal 0 HcmV?d00001 diff --git a/target/classes/com/ruoyi/project/monitor/domain/SysUserOnline.class b/target/classes/com/ruoyi/project/monitor/domain/SysUserOnline.class new file mode 100644 index 0000000000000000000000000000000000000000..eb72982a14273be75952ddc900f9d6ce13b706d8 GIT binary patch literal 2107 zcmb8vTT|0O6bJB=(t8V(a`ApgZHqQ2s3;d_&{3JGpn|;UgJ}~>u+4VTREKY+Gdj-r z;0N$SIsVVK4ehdh@a3HB{kOYG{`~#*n~0X_ahfu;oT4<{<8YtD0}d-39&&iZVU@#L zg4PqXVNfCvpB!(?HYi^?v_4yA!}99pZs5CKebu0(?X-eztLfme({}vsW87_7w(T1< z-Vk-ytB9HvxWdD1RbPBTH-jRgZBR-!@3}lYH!yug$jRetuIC0oj1`Le21TC;8yzPr zuIFrbnpMZ&v#Jf$%T3QQ|}(-uw3 z^~~L)_FI^Cyap!5p3-$E*z50lrcfM!h5Jso2l~twPIh5%?xhnP2>X@g|}^q+I6uMD4Y4$Vpx?`X4s@=Om4F|_Q!6+A~I0F1+{juITtR=b0-nXy}N`{2pa#j!tDMOXFFZ;4sM{ z$6<;?p2IYU8Jaby{C~HIvp;@WcdC5rbOo<<246ywBD^e~BD}K9SX^eTFEa=-Lm@LD zGQ)yAMF#5;Porca8_2QJPcpvAZf6kVvNB0$@to~;pmTH{C4(-|Ma1F3-zoA{89c8Y ze2FeY&Vx6QBgpa6!ViiZFP)aPMT${L&M?>Q)mxrvynh2H0yXX&$)KxR)0k>1Yfabm zrZLsD1k>^e6W>K>nvkY>ttqOSRBdNabr-%DtklBSZ@lu%9kTGN8wlu%9Y zV0u5o#NTdc%1hHNttqLRK4?w1^`@k1a$x#6!o>fP&@?SgW%&b9{{<=4bf`5g(j9!; aC;kN~)g)kQjWF>)Ei}zY(_O>}E&T(&4H(z} literal 0 HcmV?d00001 diff --git a/target/classes/com/ruoyi/project/monitor/mapper/SysJobLogMapper.class b/target/classes/com/ruoyi/project/monitor/mapper/SysJobLogMapper.class new file mode 100644 index 0000000000000000000000000000000000000000..b310e7250ed4402eba0d3f843e33ba738d8414b0 GIT binary patch literal 893 zcmbtS%TB{E5FD4jTj-NF+|vu@z^$kP4oF3*K&r%rliOM$l@nJ^iqv1@zz6VAh)q(F z0_9L~@L|Whp7G4Sf4sf{xWH)v#|4}))B~yHIK0y%TRXNJgbY2)9&3-Axn8(@X7rTq zxu<HWX`jgny*e4}?huhkB$hZw$W+)~O zMsHPJKaggb83W(U#y&%1LZOE;Nifn!3AL@KB*_(p)S6e?iSO$_XBb-6x``YKjZ{{~ zN(w*WVA!%cj|}CZo}00}brr?m{#v4en9Za)iwt5o|JxQX?n{?1nWKeEt-T*b>zZd^ z$k3((g)EUHOI41l9Q`N)>10r(s}jo8n$#7ltJtP`*}4M|R6{+5Xqun~nkm$Zp*D6? QXfKBLaX>r_hcVmHC#yL7%K!iX literal 0 HcmV?d00001 diff --git a/target/classes/com/ruoyi/project/monitor/mapper/SysJobMapper.class b/target/classes/com/ruoyi/project/monitor/mapper/SysJobMapper.class new file mode 100644 index 0000000000000000000000000000000000000000..d8b642d8c8224caddfd1e0f8887980135d667664 GIT binary patch literal 837 zcmbVK%TB^T6g?v^i-5d*|3FRDZrq_Jx-h|%Xu{IfKt~+XcA9Bx!mqjT1N9z$~=qG#niaLqjt2^}&NrfS4s4CTbx2+UAmpFMomS^~m+Rnf0Aizf={M`xDDn{)A*&RZxZ!iIr5T zJ9L#ukjh!rj+trwt!kg4@q#!qpi(3Qp-54Qq7p+n z0qJdElTrq%M9tzYifgD-Jf}7Qg0|6Igj#6lP$y+|v6Dm8gFfyqK$PE08}EMtaCqev literal 0 HcmV?d00001 diff --git a/target/classes/com/ruoyi/project/monitor/mapper/SysLogininforMapper.class b/target/classes/com/ruoyi/project/monitor/mapper/SysLogininforMapper.class new file mode 100644 index 0000000000000000000000000000000000000000..3e51de070fffd51b5ad0a93bf2e9e5a9d0de1d08 GIT binary patch literal 634 zcmbtSyH3ME5S$B6Ow1!ZjKn864OfsZh$012Bu5}wqMtd5Dt@N9*47X97nv&bvtQB8bvy`dh zrP0b6%QI67rFpWhx{>MlnTQtBy_@VwSW!wRZN(52zgWXc7DUud#j0`)v;CLFOYtFi z?Nq@Vjw!=1QMnebwvxg9oA7R5`}ZW_DkFPNcJ^lLc}5w8(aTnEA#~1Tqx0!_&fqs$ zw}4SvNTItqhJ&a<1c|9_Dj!rqrmlDLUWnC7+M6Z@hRNTzDqGy{t+umtt~SJ0)Z=S& y;Q<3fk3I+Bc_acJz5Dd)GYqIknsG>gu7n|VdkCm?gyRl<(xOi>qBbpRLFYe2>%7YV literal 0 HcmV?d00001 diff --git a/target/classes/com/ruoyi/project/monitor/mapper/SysOperLogMapper.class b/target/classes/com/ruoyi/project/monitor/mapper/SysOperLogMapper.class new file mode 100644 index 0000000000000000000000000000000000000000..905df9d90402b6f80a0b32208c43165aa936583e GIT binary patch literal 715 zcmbtS!A=4(6r3Wkf}nt)zknC3i8qWU9!yAQ{8RWlHHbciwVEx!4L4GjIYZk ziw6(HW8a&3Z)RS*3aZ2JuTT&6Ul#D^+(4EKBP2$$kr z@U>GZ_X8#jt%XXoaBC|W^#7*a?&bH6mJedGTENyBHE?j&2atq zy|V(hbMGyYPF~5LzxprIWMHV#*8&=KT8}!t8~#Na0V)MJBtTU{i@G(msda=-h3*#U TW1Li_UQy~}Kn#Y{f;apD_#e*Q literal 0 HcmV?d00001 diff --git a/target/classes/com/ruoyi/project/monitor/service/ISysJobLogService.class b/target/classes/com/ruoyi/project/monitor/service/ISysJobLogService.class new file mode 100644 index 0000000000000000000000000000000000000000..d84e7bbe416b2f0f10cd72cdfb9062867e32245f GIT binary patch literal 773 zcmbtS$xg#C6dZS7SXx@vFGw%=0JowZI3N`TffR`gCpWQzt29QAQ>nkkfe+xL5HAUk zisevouq@BpX6E_z?fC`3IZm25YT}rom&i!^_FB(e9l9#93=_xo^@5vJuM~eWdQNe^ z(6O@G@IWtwin+H+ijwijor|U5sZ|lr>c`acM%qUm+zBHV(n^ysH0JpLhPJ1|SlHA^ zhUi~|E;eB~$!M)hypNT z8f4_iw4w7*n7c-AN?t}f%;UZ9H4Fpiw~9Kk%qVvKNQ(HAWYrnb;EtzLfgc5S?w4xamh)XbZGZ%GV)C70ZEJ5C>3^QU!rz)B~J0_L6LBd(D1S4L^+o zKY$;Fc(&W5kxJBZu)O0pGyC3~@$WxBe-Y6udKS>5fF3hivoev9JJS80vW`)w-HUX} zjn`wvbE8K9d8#wzwBfN%h01t1w#e^1?~TN%;NGc(7sVY$8y}<_>iCl|B9%@W%V=?= z`;3~Q8f3zGBN=`B|0`Z!o7@|)nQD7G?#89Il5rxk0q^N-014L>%cu?^MVF;qcq?H5 zqk|hL=`vbAJd0%Rl+J8GPhg6X)~8jgW{f$w>fD-~3*PW>tNpd|`V#YFMvamY@YQGt zNiuYBxUk&#>}KS?fQ-tlG{qtW^c11bV{y)BHyX<5&E>^hI2`4lg*}<1HyANmz&}H@QiHWrLsUmp zhyQp5cot|0P?MICdJe4+;nOOu0c;_q259{PL><75dEl*i;BDHxs_jCNaU4V-Ue>Hvkuy3~@BXF~fl}rF1^FxiZQ;S;ufbjZ<52SJ_(e7iU*8 z^}Mh~dFyy)3!w~8>arV&X7@yNBmJ|@?t~MC^wO0Kq5j4?DWxW&ZYox#XIN~$EMAGV z;FVXJHyjbeC{ei)zH*Ym|C?~Wsr@UGaFUTdFFSjSdYMrMlj*~kUM)<{V{7tgwqzJK zS+{_3s--a99K+VM$umrB}1fNCTh+XU!J7*V%}kXk#~?a+HIdLLtI)1nr1_zAIw BzOw)T literal 0 HcmV?d00001 diff --git a/target/classes/com/ruoyi/project/monitor/service/ISysOperLogService.class b/target/classes/com/ruoyi/project/monitor/service/ISysOperLogService.class new file mode 100644 index 0000000000000000000000000000000000000000..4ad2561af6a9298fe75ad45c6efb3db1b6574c99 GIT binary patch literal 720 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 J`oWrp{{dkShPeO$ literal 0 HcmV?d00001 diff --git a/target/classes/com/ruoyi/project/monitor/service/impl/SysLogininforServiceImpl.class b/target/classes/com/ruoyi/project/monitor/service/impl/SysLogininforServiceImpl.class new file mode 100644 index 0000000000000000000000000000000000000000..5b9dbdc50adc91a04c524b03ec06834e6070dfb2 GIT binary patch literal 1611 zcmbtUTTc@~6#izp6v`?W0Y&BFy%c3o32I6}ATepOsIdkgeA;e@GPt|b%uXTwPd;d3 zV(`Hq;Eyt%SuTZ2LE=N_?ChNL^?cv?W`F(u@e{y2o@CL3`@P8EK_7CMwoBeFGj@5H z!J`ajGvEvZp{mMQ#+yp7`?Z?T3=3YMBChMIA$d)!Z4nqAsaP7Nd6eexQX}zN>Iw{F z@9MFUk=T%lEQewxj+OC^RB^)a+*7*BlbV)sbyNG1*j4%yFAG0T_@*Bac7yvz1$?D$ z)UMQ`La^P7)Sy{n=*UfPFm$e|ieTvXWGr6Sqq5NNefl?sAx{NSI90lxmv98J_0Oz^$mrm+?u&f@P3xYR)isQ3Psa zU6?IZdFxvpjnKqzI1wSuso9$+6T`52@&3GRf5+!_BSUU6h<9&ER%74PHEC@A8*uTW ze3z(&kqXf>qDgPHQLI?nxsUt(hJIY-o{Fo5=^{hM`Z-M){Og>Y6NF^DCKe5(r0O~l zucWon_z80#v%suXh9P&VlM|YQP^M86{Gg=Z;xhVOWYL$woQn~Ry10%TE+#PP;x-;L zEVsXDWVQ2MNnS|DmC}DxwpW$m+4-Qw(>OBBx36q}@zZ-*#4Y;R^nlz#KgW`$=+eii zo8l#kT@*!Hv<|E=C)RO=CevioM)|-Ra;9r5tZB|F5_= z)53AWr->7U?Y$Mh%faUs{Dn67Q>5`x-&0h?$V7*NzM!4eF4bfY-3%XEHCZN2ULKS7 QP!k7t(hlw(&FdcU2k5t$G5`Po literal 0 HcmV?d00001 diff --git a/target/classes/com/ruoyi/project/monitor/service/impl/SysOperLogServiceImpl.class b/target/classes/com/ruoyi/project/monitor/service/impl/SysOperLogServiceImpl.class new file mode 100644 index 0000000000000000000000000000000000000000..0fc1516aada22880ae3e03be8330c560242348dc GIT binary patch literal 1765 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 literal 0 HcmV?d00001 diff --git a/target/classes/com/ruoyi/project/system/controller/SysDeptController.class b/target/classes/com/ruoyi/project/system/controller/SysDeptController.class new file mode 100644 index 0000000000000000000000000000000000000000..652e807de9ceb385bdbb81a35bdc5f1903acefd7 GIT binary patch literal 6295 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 literal 0 HcmV?d00001 diff --git a/target/classes/com/ruoyi/project/system/controller/SysDictDataController.class b/target/classes/com/ruoyi/project/system/controller/SysDictDataController.class new file mode 100644 index 0000000000000000000000000000000000000000..4ced745b3d282b69a38c8dbef25b19fe26b27ff3 GIT binary patch literal 5391 zcmb_g3v(M+75=U)TeiHl6W57J3d9Ma@gvD5kfg1hI`ylGL4MS)<^hGZbYrh0Y1Q48 zZMBqld9;*wDJ@Xm?}2G&7^jdKegOX?{Z<<*k*q9rfuZpre)vKSEP}5^qWT7wd|5^R2Jx?Mc3LiZJD8258+~| zARh!_WF1YlTr~5hJ0j53*S{A#}&BI*VszN${A)pYGJrPGCO2%Q?BR*V^!)N zKo!HO{7s9Sw3cHka4NF0CYW{h5!jbnH#Q8t=$ctQMX0oPu$l}8A|T{a&D@K7+G~WS z_f1Q8&C1LfN)fKKN$*fyp*S*Kv`v>9NvEabP?xe`Ij%luOG;(UvdtGH?KN*0*&^fD z`;_CHUNf9IY3Izo6TW(f6e~lD{}cTJ=j!gGC|xo0nWz>oOLy8R6ewJ^gYE~gMQ#-= zn~}WQ=drNml?0OR_OvO&%(MT|u z&p9ja1(noUrarlqmf0oFqen}-nz;Aao{iWIHETEp){}KTYvfm>B&RHoUDtF~{%Lpr z^2Xhl-+TY{ciw;PuY6r8I!sZ=QQ0hT{{EI_zL<0LvB)xd+A?`{ZgzfA;848rAB;b6$ColWMnb|*Z*G?w_beWid%sr zjRQdpDhw@CGYgYSg7wwK@@3LB9+}bHSoTM~!?6CkdSz=~GWd==W@So@NnB=3BD_ao zR1-XiOLG&Wi<5h)kaNX|{#$IBvo@HfBYig_v3mg3x0MykV`2XeTlY+3AA@oyC?)@X z_s-q7Uu6I$CR3BjyT^8|?g_~%F2e-Z!c=l7ujGPNwA1owQx&embP&6a1J#e6 z!&EId6AWMd_No3-FYF=Q{pIFOv8k)X(QphO(eSFed=MW>;9DAA!?!hj2j3Mq9(I3? z&kcdWaN2$Q3bo<)G<+XF(C|aNuHi>`Lqji)Ch%hoZ{n>4exl*0_?f`NVf!&^+eS(C zAR6Ar&o%r4ztr$6{940r@LLVPQ?I{QuYbTFmB~K|Jkb!;{aQ1Dv*FtCxKa#9mW~;Y z4C5lu5u#N<1=<|fu-!Q}Wz3(@U=2g}XrtMCI@B9maz;zFbiQ5I(T3LQbq_V285N}Q zFjDq{-fbi(%weO~_(ZDPV|GU?`*_xHoMF|owEKsj*I)=t`;&Il991(p9H|`TV}sUf zxEU)=v9ed4F+0jk4fS=%5=b~judS@HHM^pI`|R<&+I`g3@l^Rq(_J0)v66p&9*?qI zCSqrBkA>pkQ`qbID^#P$653)qGsP@h!vj&HWhbvQc9h5xwV7u#F3{cAzcUD&#{inV#W`-;#IXYd+lUSP z4NZf8M?8knsn=Y|5qsTDUmaRpN}7m#~}A8W|{M1r`&W;m#v>EPl}Ad9s=EMYnD zVK!j0XON2S+Q!3WM!OZG2SP@Z9*fU$%g;nu?WsWS!Ly#610c@^K#tIUAla)malXdz z!b8?BX#6@~Zt%arry8O=SVq@V7u`D1-FiTDpRPgo9Q~||?z1og?k*FxuR^~Nd>{sr zc&L`M4W`Cs1XWatD@fDffU^~DsDh_bBH%1jMkW2+xQSKH_ful6I;t_4TpPo>XQPWx LxA>l|Owaufg1gw~ literal 0 HcmV?d00001 diff --git a/target/classes/com/ruoyi/project/system/controller/SysDictTypeController.class b/target/classes/com/ruoyi/project/system/controller/SysDictTypeController.class new file mode 100644 index 0000000000000000000000000000000000000000..2f49e9f8f9903f5cbe1dbab03daf079bc852a2c3 GIT binary patch literal 5595 zcmb_g>wg?&6+Z9gvbju?G`(58Bn2e5rb9I?kd%}p8%l7K#@uXE6eqiHl1X-VmYLZk z3!Ao!RYdn*8)b=AC&j=bY!9=bZOs z|Nfsp|CNaP=)(rOldJ|hMs__Oxkg%}b^hFlQa(ygHc%}+RYx%%zru@8^WsuFOxUWGw``eKEq9MPHl4R( zrRN3&9i7T$Y&|XKbW2}I$)QZfur*sZGM1qGIW)gN*Ed2G*T&PssU6Z#aUQTozg8^(524GQj$hm(=$N{16_gL zKIC<|pk}otsX7M6G+P_XEE-T!R|tfl-oVMS2!!Yn)DmCO)-*L|>nSx3Qz7kesWT8? z&mm{a_MX|3u0e=y8JG65k(|^xjJ7o4-u}?&S~8I{bsK?AB&215OfqX&wmNA_#CO>+ z^(Q6d)vRf$9OBphkY$}%)~rcsruEJamva5=m45brN0*?!P(AF@1wE4t66lEVWz336)J1epXAeTGEU0yUyb(&mg5^q?C*7j@hKlPYJlM~0ylZ3EQFr8os^ znYv@iVtP8>bL0p*RvF9_Iceq@OTGG(E8%H?FDdijj3X3#evZ)iZD|G=RQOEnRDvHgaY{KCE+*x+C|Z=&l%pY6FBq9n`7NEBv^N?vB!r6?&CkQ|NX2iJ-gv_N?+9 zA*jdScH16;6~3X+Pw7pCenyuS`Z@hVpgc$59I$9o8)A#~NI-Y|XSM(ZeC*{qto`-l0`St7A~O)IFiyVYk*svM)$6C`Vm3nyKZyJI?sVXt#hg6NH6ov zaGtMF_{QhqEq8I5>q9y-K~XE`bfIOk&M)x)H+#I<51^DKRtwRa&FIKzLHF)4e)G88 z(4xu=@bw1q6`T=(T#|aYoEkaOV9x|va6t!ARDJh&$I*(~hWm~VSemARoGtNVQIAjJ z(j*AG$XW9&$enRCH(JsjF{Nh9;k=+jVJJYDCLOJ`=@{%bgL`$=7+hr=++7Je(K%L` z#-SF&#@Q^I1jojvoby@;o}dFs=lUv4FyC;A91OXN840d3*Yn8~RGf_MB;HkJ9 z=s5M_3r@kID(7$-yQ?sFsC$zl-PftA=LS_rXuSLQCe>WULX{(|$=TFJ)%a?LNEX`e z9`uAl1l>y~A-ox?Pta!|__Nr>s@<7B%$&kZ4Y-ei0Ug2AX>7R9qWkFqez`=#qJF#$ z&>6^Qd~ZN&4aRmh@3>>$#7(Nb*c#oUy8h~;)jhqf^_$ed&|Pa=ae|<|5x=DzhR!x9 zVT=1*IYo=x=|LKVF35IlkKn{1$M!0mYoN3A5F{~*!!Xoc>w^w=Z3t_Or#=EPRK(PQ z4a3+zVo{7nB|S{%3K$=uF=%r!K8m-`Q5-TD;|LB#FgAC4F=&k7wo*6%!Dya!V6ZXR zT7=O|<1_(ro^!{sSmxaG0q0KA=dmHmoSDLlplO=%ta#b8BIX&fF9PS^)qRtiE^bkC zYs)6>@7bhQ46O(7*rbD-bm(disB?IejucG)7@L07=Z+hVM`<6>_QKFpbUpwhR)i6w zS%*J!u&RngZvoG&ho=p*9$CIn=HUx)EIGIuIb$y3V;+C_4Q994?${CA#|zj#5sGad z*d7D6FTv!;|5t2}m0|lL#D-#foWAVE?Hn+>>~RzLliO0NdY3o=Nf z1)o706f%RD%AUs)WmY9gV!un}#d1b1(J~Y=ug_o;=T{Rm>ISumP{YM=m8s6pk}qGcHVPHi>tO>P`n$=b@ItA5w1s`fk&PVc%;Qs%s;HAP{Ks9gt;?) fm|>%uF#6?+YiI_u5i*?BR=j2L{RDQo9y0$6mqir2 literal 0 HcmV?d00001 diff --git a/target/classes/com/ruoyi/project/system/controller/SysIndexController.class b/target/classes/com/ruoyi/project/system/controller/SysIndexController.class new file mode 100644 index 0000000000000000000000000000000000000000..3fb92776361bf6e84417986701aa4495cc07e9e2 GIT binary patch literal 1102 zcmbVL-Ahwp82`Qda5`6-WoDM8l}(G@n~1^)M2LcA;A$XVoy~Xa*mmA^&U>^Wf^=aC zWkNw97T8sgs6l9iw)_5#+J{&E2hnr3;RbqB7w7r@`aSP+e*XFX1He^W_QQ=+^*HTA zX9MbS#*ee;a!9uy=WyPK3k;2>Ei?L{G~#-KAsC9u6fc>0+|*KHQksu>tmNgxwj8NM z_A|5(+lJCpBBEP*G${rQLn=+_(y$n=hoqU{R@&5!M72y*Xokh(T1-hZ!?nExK42?3 zsheVq%GTY~4PD)0a0h!M44y$bCK#GRx*>+_R8*J`H0s9C5|S}38PQDL(JMxedZd@y zS5c%*IWA&~TNz7!?9p*Xk44ncv3SGJggif z*x22AncIGon|oE5dy!vS z+1*;4n3^VIacy&FX0e!k?zld$<(AfS?`Mju*`0T*zh<5>G*?%1KkBSEfCl(|=n3E= zS_258)rZ~y`f!Qi+W$QULr*oadPMtgk-?V`YKV@N7E4k@m=>KOLw7Zwq?9zct@NZ- zdCiB8O!pR-W=c~GH-h^ge6+??2HJnXS)hx4E;s#h3@&F6#2ZPgBOV~`B2Ldiw23rM zoDui-ZGwF%xtobP!JvR6L<8jjwBRUdbc<-EB2MxhcuT&{7MFJ$?h$w2S9mty4Uc%r rQeF5PeCrhHLbYl~P1VjyRcAnFq~r9pje=P?K{@TE%#(ZLcL09@L;Ok3 literal 0 HcmV?d00001 diff --git a/target/classes/com/ruoyi/project/system/controller/SysLoginController.class b/target/classes/com/ruoyi/project/system/controller/SysLoginController.class new file mode 100644 index 0000000000000000000000000000000000000000..f4b2f7c443eb72d234309c7aff24fd813e966db2 GIT binary patch literal 3455 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 literal 0 HcmV?d00001 diff --git a/target/classes/com/ruoyi/project/system/controller/SysNoticeController.class b/target/classes/com/ruoyi/project/system/controller/SysNoticeController.class new file mode 100644 index 0000000000000000000000000000000000000000..862182828d572379458bd8e61053d771316c84ea GIT binary patch literal 3814 zcmb_fNpl-T6#iOX6x)-?2}ztlAS+n5g9k!ZCvoB}K_ENA8wp`+No{)^X-1juu@Mwi zT)1%K%7qg*4p2pzf&)K*|B<5j6BMs!Mw+oaGB$A0bhrDx_r33J?ce|U^)~>QvDu4$ z%=IFJ2R-yt=)*gh*V_fXTGXp0{j=PQckxi4J<{9v^!BmdzOR2iNaMpaK4Lg%JIX4F z1>tVchv9O*?@`IImFrY0 z!Zn*dO*PNRerLs!is4bVy^gX|<(3`E-yDkFjojT7MYG0Nh3SKv=8EU+6-T=m1b|^A zCfCCJ4jT;p`E|a*O;1@BGfyB%)A8*j)N2hQZnxaMwGT_oTZYGk`hSN9I4ELD=4aKj%$4?NMF|muXq%-b9bd2UE{JK+^Urw4AN{Y z;A2cPKRBclGAVmBokhzo#}ntiP_w*Nqb$=l%pi2>RiQM~47alZ4Ha&$nt6wAwU1Ot z_{2K@WL`+GqUk*l0Lzt|!wd&tn~AaA39|zUDU>(Uwre& zx8FVc^806Be?{A(C#mkGT%^oqxbn}Mh3!?PIT;fpTDo&{3p4YJTESiut3Wv%Wmd8r zrLbrzl(0gLW+zeMQsVy;PWQ|pyh9hmh24@TK;b(yJ(Hi&4qe-Gw5COcnp6}oBdR$` zMc2FFcy3ADvvi7`NND?`y0NyKTFK#(YTMLE9(c*3exxI?hZ@5+pwGZD95?W>{yB^z zX>bEYlnj(146I<)z#1$A>v)nz#XuFd0S7e$PvIJndaLx-!v=BKcC_z42&s%C$*798 zLovZgE=5EGLyuJ4RRx-P43{GLmeJPsQ9)f+QY1@i&VOp?8Rz%#wwiVf$)T39N3~?o zG|;m*d?Ad7BO#ii?MFi5@ZJV-vaV;t?qjCK`yH4W($e#XC&QiizwGz=WW=)Oi! zj=Gp~^yJl&x%L4`8c7=8QjPkskI(S@WspGBhRS|t2(~&{)S8Ya^tl|)(u295UfT>Q zU3T<+j&|MjJsN(gENM4DJCnYQzFosVgZ)U06i$SffR}KR)<$pv&*KH!F`UAS=O#XKI}N`YGdxOU0%^_XYqX%YgG(Wb2O)}q5n}4V z7J3?JdNrE92u-h#U;+m)8ACMCL^Kf6)I!B+(jQXs2K|$j!WD_DxF&ENH!wzEG_pRO z4>xIX%SU&+Ejput?qDLiSwc7WFVT&+pu2;+1SSF91QF0Cf`?jA>BI&e#Smqhd}SyB urlSZ>(M4SggArddEr{;nKHU#e>CNpCO(93JDZJ(DI6|wpX`gRipZy!x<0Etc literal 0 HcmV?d00001 diff --git a/target/classes/com/ruoyi/project/system/controller/SysPostController.class b/target/classes/com/ruoyi/project/system/controller/SysPostController.class new file mode 100644 index 0000000000000000000000000000000000000000..a4f943fb2126a3e3c57b977a939907319a339854 GIT binary patch literal 5476 zcmb_g>wgsG6@F%O*{~T1MlQu(2x?6T2_rU$C5R>=YT6Bi+!W}=$?i)sVRvVlnFXTt z){EL!TG}dFYpvI6FVaA1_)%^9q2Ky<7}9V35BlqKW_EWbn_Zgx+7Fp`=6%mO?>W!= zob#T|KmPsazXIsNpAxtob^<$)U&qJkW;k%=(~F}J$C(5gakdHPWSo=5CuH$SdHYrz zPbIJcb`zeK@tFkTc(w`8$;Y=v>^ma%-6nibKAxB57i8u|`TTwpejqbHl$n?0ab6xj zisQ#|T+q;*w_R^cIcKbt(r`~QWoHeiV9!}b-mzy@$}`+K*Hc-;4P=a=u{n1n)ali* zZM2Z{tgM=}Tx%+$`f@qjGd;`Bxfa6XYGNzQ7bB&W`%Cnt0 z!z?QpeFe`xYdI=SvyBI=oaG(VP}i|@QbYZKou-E^Nh_xg7qU~z88^9`hSsE=GBcB= zW65`Mvfi7qxc$D`@ucjW=h&Hya*Sd`1LYa+UZ1gCPs5`fwJoIWtZC&++Pym?l3}Q4 z)s&Gpr6Teu1nG=iOU#q6wh+TcC059 z?KPY+GXvVd&YD}l#{hO+5>9s7mfXbcl%BaJyrJ+7^kdUI!cy@l{akW^JJFn zU<3!&sLthWhro_^_z0ZyiJMWLG2?l8;}JfD?VK1@Zk`-fy{kq>XxQWw)ikVn10J@V znTER}TQSkHcFsRc!e(2>;JK8_Oi+%|99@;*+Rk)O#Ae7()6Fx0iz-U3z=z!t?x`D!5ET*BvL}H0=HFmQ}8hb&dXr8l!9+d~D>%=(vX4 zYtte)P9etLRTD$XQ^<_CQKCeAT6vOv8txCyE{Hm5lR?ou4ABwV71OY9);veb6f%-_ zjY8R>bU7t`^n5!ScGM2$bU`_D63aT{$-u%>41Rg}Ea%*^I(qNwgEzgIQZb~bb!M7w zPX0g2ST!Klk9lV56u~am-R-r5ry~-2s2cd zql$pLUca55BNFv0ZLu!gqy&k8@8ZPBp}z6KwbY`KLWFURj>_6+m>35;P6TB*lzPaLa^|N=qWfyw&PNcf3Pn$4)z0?DV?>gGi zq2nj=xC3{_@lzc?!^=8e!9@*shW%6R068{B;<+< zwgT!Ejc(j6`0b>I2cmY4Ovw+DHD1-Xre;*?l$_RK_OcT>i^;8_YYk_1oR=2+OqF9? z-BEvnM58}psLOJXmrgTJx3ss0O)Qp`vJ`6a>_Crgta2QiNxLlW@;)J1my^BP%^b!p zKb%T&i$4>jc8*xkJ6CxFjxs&fKc``Hwb)3VgvBnI2}20i2HX&V^K5CHH0K|86beA7&SSBJbc&b3j8( z>N&?R@~e^KT^yS@vflH#lV1i$jblUSHE5Ul5<@4SA`G-)7oU0{fG%|Ni*Zw_&R5P- zx{h<3I~Ne^yo$PA*HItCaA(&78s@oB=NsGL3w5EM23n{tM%x0#qEVW_-Pl9pE!=uH z?xEo?QA)NBW_mcYmop74a2zC{t(>}-0{dLthcC;^31}|%^Q{*LXkX%cox3)0Y!mmo zeD@B2jK*WF@oQ+>U%#z>S9j~W1tcW&z}r@8fQFl~4_jz?J8g@{J%OFF$8ESD2e}t* z`@YAhvDx>%j(Q0^fCp(wqS!)5gS8&+5v=ubP2yP>qZyQAYNtRy#Rps*!l1%Kc(@ef zBN*atL5yGF+gFjK4T*7pN->Ttoy9X~j$r}196ayEuj{%R}VGW^a`DsVOTA2{6&ivR!s literal 0 HcmV?d00001 diff --git a/target/classes/com/ruoyi/project/system/controller/SysProfileController.class b/target/classes/com/ruoyi/project/system/controller/SysProfileController.class new file mode 100644 index 0000000000000000000000000000000000000000..ed0a20b3f775e0d99e76d3694722326cf9b5074d GIT binary patch literal 5902 zcmbVQdwdkt75*l>$!<0S!T5}|T98Uehy#jLiK0Ox(k3w`1YfN>*$iRGW@nw5C8X6V zV11%!iwbJ3w!UgBAkt)msI9GiXy5PmGn-&v_T4}FtG|9{_BBg3K>EusckaD&&$-|E z&bjBF+1KCR_cDOR_;&${r;u8@(hR4JBWEh{4?h~^1N!jG7FrKc$Gx&4_&*C$c_$;1_ zU?+CTg583eDxQ~xd%}1jguNkrPC-@DG3+kGUT;PX1!uHItpUwWT0^Fmu&s4Q)YY6J z$2A5tE|WFd%B~@&yCBn~ptds^cg+D~wdt5WF{3#iw_M#dt+=D$>{iR})0~8D#{1UT z`hYQL+5K9Np~oFC9@?2cXa!A<5lz~rJCujF z$|po673Z39(_N+@P&a?Ig5ZT#FU_oOHRDEma-hesSLuYTpr+M|>ao?jZOS-18Fbg0 zv|*`l=YvRzT5;F5Vll(kNK1!ptubT9h51PW2vI^N6*Sh(_bDovFlh8>z1Dzk#rJ8GcHsss4FR%H@-Eul43z|r~?092Z7FRmi42E=-gL9L&;61g15M& zXJvJd8SnKwb%o)!>4^j*fP2kICVF+($Wo1as7`732GX&1)a`k9G zVR)_$VK{^l1+&@=cdgaip-W`ChD~QLDoKLwBzP3oX)!(C=QoYS)24FQbj8E!SnAoa zef!2EeHsZ+vM_cX_qxOpKj*FUCwJ11p#ygTe#^)3t z&%Cik=k%;2_GYJXebR8;QkN@7zG?EzG`(oW=pM&m z+Tac~lhrYU#f`zjlCXq-JKd=uoX84>kfM{;>#-zfBbpC#1@XE%kIOZ6QxY)1<7g&y z+tu1~y^DFEo2F+-Fp~rpO?w+6ZrKnu5?(?LVU%$-vF(}h+egw*>`iapDsfdR#g;mV z9hT#Zi;$Kxe#sGQ$U5JPA$mWO(U37M(q$#>s3F|&PAHWQPnYnR>P{#Hn7Pm`%BH2S zlhUGIm=+SxF}aIO0z@u1L9OCEEK{)$FES;l*pHV&cv;0O_&h_KiQQOXTgilq_uy<5 zU%;y&d{M=h@S2J*<11W}AfwqOU&U+8DJs5(S5>@@H&lFGdf$-VH>LM2>3v&z-@zLp zd{@Qy@O>3Ozz-E1fAGOso-|{#AK3fkTl=?+ z-*MOYBm2@@M$`aer|jEk!Mu=ME3b9ex~B*qTeU*=BAw!J1P7WWqbm^z%Nxa z;oJ~@rQ+B4jgY{c)0o-bpV-p-H>Fc4Zhc_a$bpU9CSG`PV&~%%_uiVmZ@Y@$3P-RP5A4p&jHT{Au=%C2)IH<5H4_h~()Vsr zF)o}>;DCxZ@rHtVMVhAu6vFRS`~iOq;ZG|5EZE*s@fZ9xgukiyJN}{Kp8~YP9O&z| zV=DfYQJ%VUZ2x2FhhLt!yMF8n3 zEIj0u${XMB1GBDHQZ1nc1UcLn_n=an4?MM z68CVD(J1d)1?T&(p-X$DhMi`vuFB$#d)LS@#9W{xd#@>2SO9mB9n3GKDrGY37%`#| zDmrtygFM9)4TuZuU%+WFobxWTY zkm_ zD}$Hyhhf_mO)m=1yt@m=9wySMG-HoPZO)#fGBu4vR!8o8V>&jjoFXCRk8>`j{hwD% z3Urn)DKz+5E!dG)l3&^+-(nJ8g}QBSfq`OoXFu?=4zC{2UAogL2+k|vl1UzEnoloQ z8}X<;l<;k;+6FD~Sd#+w2vw0RISd5|+EfMD6s04xmFZ-Imfxj>Rx-2mCRk(kX`M;y zN|RL(`z!}YTC-QPcIfWfq7+#~n-n)%8$ZhB+at%_%C_beE!VbOv8tuLYvrZwOh{Kx zW{Tf|Q@Fy@Ddk;vB4+8mS*;aGZ=_&dDMw6`nWhqeO6I~@g@Dh{!D5U)$@DRszn3iG zZ#rfCW9bG3=WtZP=TdK$bhPg@|Ags&NKR#hJ8eu?M0)vr!gAIcG1%3WAAH<`S%=mTJ`Fy?7r2 zc?MdkQ9&EpDNDG@C447vM+I)%0>W)?Bt~@@GvqmQ7}fHeHH@R=Su>2IWthS-JFrl| zH;&@iGCYJ+>tDoiSB~KLniGamOGP0*YEEP`jCTy88iP=C(wxbnK z;!-?^4(vgvkIvC7outGgk*OPdJz~qKbr)q;a=#AlGy{SELJ#4x5IUFCqKi}I3T5bf z{vSvBSMk5w!D?J?;0j!ct2lo(|3eaA?`KEB2XGD5i6;VliobR7M0+C%TpDCMcWHS; zZFvgseh88Jn$yJM1>SgvjOTgd6zbZ9r`q6xQOpmJsS_v}AXD|i82dTK=mZ~QOK1ZK zq?|CS2;?YSOU87@c$AD847olYq_vFgeuhj8-58(^mdDnz4A?wdI%Th;K{SAW*U|EF zYLl7Lr_Tcmsw05CMf|FspFn}GHo5G@{!P;QW8Dy#Q za=r?%b7n5kYJ&{3#YLpmGYr6AVk`NR>P*~#THHvoZsM7{nYm^Y18*~f;1-^`+dQHc z6Oo3KymJ_&#Ch3Zs^w|!BNYL`o2Ri8Cf3m=p@&)NKUCAtZje*4T!@*RkP!1RGms0h jI2UG^vAj@ICDal!>EL=YX;ZEYjwfHkp38AEFSGuCGEzAr literal 0 HcmV?d00001 diff --git a/target/classes/com/ruoyi/project/system/controller/SysRegisterController.class b/target/classes/com/ruoyi/project/system/controller/SysRegisterController.class new file mode 100644 index 0000000000000000000000000000000000000000..8e4bbb75abf37997a314b38f403482c5d1f503c3 GIT binary patch literal 2052 zcmb7FU2oe|7=BJZ>NIoP5w^0GudN$f(snf)8{Np*Xv+p;=~gwYLU83cr`hQopI{%i z?SENS+^B({H_piSJ zIElqHhB2LjF^C~NpThB7IDr?^v~wzj7t97Ytb0q_P4f)R6@TcZaSW^Vp>YNb;Il!7_+{T>%!$_ayw*6XGK&x zDV~g^Saannx0K=4sfubRy-SqeeAJ5E44f5?Q0Exp+1xTiVqTW1-jRZE_!Yle;_jO! zCC!j2NXxXBO;-fpEnz}k7gR-l=g^d-a;0r^Hwq3V(UMXIsW#dSZ)GE%LOeP+x51s0 z<$6BXS9hJ4jIykn!ZBu7&G(kL=i7>+Bz$TT!(?Bh{s9P+s#gh0Up3cE!#16YQB*;z z0{Dx{v~DbzwI&oAUePeiuzP{4>$1FP1{@&*5jdy%wm3+$B%E@zyNl9Oyg!qhar&1@(HctKjfJ^*cpL?o_CPTU?eb?d_MbPfC2$jQbO$R{i#M*u(5&SAN z&+FKWQR;|xkP9Gx~TOVW@`w=2Cyz0xqJV;-*(v^p-}BE!+fgU=f`KYe)j z-otyhHt&41`Ss1l{r4N6eZP74yT(VKG(Nud$A=I8y#IlY*YLWI5$x7*Nyk%oL&Ieq z1uW>ef~z#m;V^ei%74+7;dp2p5;Q11VmfDfJgk~n(aj%*+)i$KxXW-N)a^>QeeU!O zHp62cx9JF=H0Y3+t6%1Ih9lWtCg*xZbSMxpOq3np* zH5-pMmos5;A)cLiY!?_|W3hcEQ|y1W?K&*Fdq6Wh9@^8YDjmaD!uDE2el1utXu`Wt ztto~_;+A3XyHAk+Qy)VCBch9~KZ(J(xXMAP^S#OVTNGO>vt5zi!kK{AF7 z419(3L`K^{>Iae!PHrH*F!eKbeM#aC=-(nWF%@i*X&gbg3^@dzrExw;VGhxCxEcDu zmity{3WR4!i_!ib93b5o#>s97@v*-!s^Jh#`e8grq9h|tG_e>a{UdZAeFw}#7CDk@ SLD3Vmh+(q%oa(@K4EP7+b!3VF literal 0 HcmV?d00001 diff --git a/target/classes/com/ruoyi/project/system/controller/SysRoleController.class b/target/classes/com/ruoyi/project/system/controller/SysRoleController.class new file mode 100644 index 0000000000000000000000000000000000000000..16ce3853d4e84574fd5aad8f5542937bb02acda3 GIT binary patch literal 10193 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 LzrwGrTr>C|##Spo literal 0 HcmV?d00001 diff --git a/target/classes/com/ruoyi/project/system/controller/SysUserController.class b/target/classes/com/ruoyi/project/system/controller/SysUserController.class new file mode 100644 index 0000000000000000000000000000000000000000..cda28a02fb379550c8ef5d2250abb826da1221a8 GIT binary patch literal 12115 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 W{SSRYzmsoV$K#PutvNo8e)k8PNoY|3 literal 0 HcmV?d00001 diff --git a/target/classes/com/ruoyi/project/system/domain/SysConfig.class b/target/classes/com/ruoyi/project/system/domain/SysConfig.class new file mode 100644 index 0000000000000000000000000000000000000000..8a89bc04f566d1ec4331c4b895ecd38823cf9325 GIT binary patch literal 3629 zcmb7GTUQfT6yC!n31L8rcU!Ghy9Bfj7Nu5@A{aFqq9uWft=dUAgn?uxotYr9H>y>8 zv)Zn@+PA*h)vjLb4OyV|p>O>iAo||tu6}1`OeP_yuvX65`<#9D-rv3r|NJ-o7ZL5E zFa7i!4OP)bO7LZvFA85q_%iCHWEG`I^^=!0A8qyHFwL2_eAL1j-AAo_H+-~*@1~FT z@;&3DF}|Pm(Kz3;K03$uw|(>u-{1AodwhSNE1U4r2VOcaP@TbaMNalJ-B2{OFL*#8 z;fO#r-I{8cvTF9r$qcK6U#b9QRx!B;k0G*yJ5zL9=05+cY%+Xx?;h1yqvpsW&XRH0#(@$jbkn@kUkr=e?L-@R{s*&g0EckPBio_0l5 z%npId8yfo&ziw?1QCSvJR2I&pVoZ<9$c#YsAuW!)%g8=>=di*YRuHspuArrLZ3G#T zjI3d@lr*TNWJQ%CSp!a>5D=m21T$@hS4Dk8qboE{T~r%xk>w4=65P|e9%c?}gU4lF z8^8uXYI2(Gpsy&Y8p~lGrLfvqdb`@W*Bk;@YmP{ zWv6O(>dO5KcV?%i=f9s8X!E1hMU-<4s>)M}T5dH3NO_!-K)+?7+WV6i@87+=v$=V8 z>W8_XzMlUPNm^gnCp^;yy;75lmkoTSJ7NxQ9y2i)AgjnW*w*bLdvN{k-1Ln?XibX= zlyXwcIbkWU&DnCQ)(IiEHgVf+2eA*)8d{Et#wWMoRL{e4 z>6C>t;w+A*+%-NkrjnEnpfsE(eNLU&Pi)S9QiaCswQ5A5ol8z1{u$YbACZUF(I(nV z7-Hbc=z09mHsBk<*JE$O$u@(w(+iAt(u?5GQOsIydArrN08pnW9b1Z|A96Rt?u1?-hgw@COEL>cYJ>ah;%s0%cI00HWz1K3d? zbP#JN==|e9sO%;V+>e_|K0ZW;A;lkeU@yb&Y1;NDmE}$Qty2jGn=K8iY$?Y)cg+rD z5N|65Fx)513o2jWQ+csZttEWo3E2P}A*kswC``wo>3EP{rQRS#_)(PlAhU_eR!~j~ zWC=Ne`sp?3>V$j&zVc#xZNS&Q2tKXGEcE;2LVN?I@STJs zub0MGQH<{p@Eu+RpIy%mzE%q#rhJL~RTkq5KOWzy0(_@={?07Kcd7`VBPYB(7u^z+*5R>$59(OC%z;KLe@iAUagS3$tM&u9%VuIRe7$Zla9vVSk jjUtap+@^3EbHzzUS>121@4D8OVv$Mcj z+lthx)%YqF+G-UahzbZQfo^#8{rWoyN&bTV)=$6Z&SZ8n&hmkLJ~{W?JNKURJ&$|N z-T(Zre2Iv@MVF$qnO=<0OHrz!7aM64O)+_y$tz4=W%3%6*O~m9$ss0hF!>FW!%U7u z=(mmZJNkW;TIfxg?u%maEuQ>ArmZ}ATc%DP-;rq>k4I(d=J8#bw)6O&Og%ilFH{j=xv8-xjVhPj8bV?-WRZBC( z*GAPyX$9M~3KF&Yb|!60clNTJAvLGrm}#XoYp9r$XoYWU+_1HbWxm!!wu|la}Yq?n3gmNR6EEW1;LT5Jv?9vV0?v|)_efyY1 zb$w0#AE3~WSSq9#bi+>KxpL{w%9zV%c>x^jt9FQ^L6_;$7V zHM^TwH!EWJPLOVXxM$@Yl}r#_OO8KQ` zv2QC5V0Y~Fu0Y!o*H$jU-PgtrR}2-iS-@X&^~j~U_sa`M%d_QImfFvHE1FjM!?UP} z$5*c#DS=@Js8E`J} zUDq~B?1D*c3(uUIJ9XyjI~Qgzut`@>zBhOD6r_tG9S#C5A&+QL%WZJzD*rw*rZvsQre)-1x<0Q;0Epm3Ozs%qK1pUAXN2O zmRjO!tI#0buh4z;9fh_s>7*`&wlV3Z?<%yNxgI9HO!}DgGZ~;Ri8h*6CZ^`q)TD;% zpUa^Fh`zHWHc>=<*R0rx3$NFiN9c2fzM!*=Xj-9%=wXG<(Rqc&n0u7Q6?%+`PP-K{ znR|jsk;zY({FKIFbjs3HTk9<;^hcgYo%slAl|p5j!H!}cRY~l)Aa*!&XU9dFQRsPI zUU!|n1UIU(^KmZB`%G(3Y@ar<*mQc;f;NC^X_qA0aNS->+9eKF%Z|~+NPJf!J~Xf^ zIWnFYfM*joCIpE*;IA4xC%7|q-l!B#sRlKl*9DL6*CTyuXBoA?MEQpBcpCqog>bSkI?OSK5a(TX{K9f3toj1-HTZbeH;JC zZNVeG#fS_SBa&Q<$a66w)y0Tx7b6m0jL3O0BJIV9%oigr4)lCa@Nwvs_?Cetu+mMn z;D}l}&XLqHLp9^)sP-(?oe{ftfbw&dsFmt~ZwQ>??5CYHjG4q&!<7}o$g=7?zM%Tk zU}}g3G>FA6-{E@iaFRx_%K=g&6g70*bC&9Z>U*dj_nOtOa(1~ASUS5+qES{XVZxYj zNkLhxA5%jR(_kp3N5U~R1Tj4bOo=L(ID{UitpXEjMhK=z5Yuodrti}a!u%8oVj2ae zu_~B2ojgpP0uxFY;ve*r9K`fk2&NxGz*=DX5oGQv(2q%lq=`7CC>TA_d{q)xxVGA0@KGZD|VHasakGQ^gi8lb$U-!WOjY z5L=X>Ezei6h4X$dWWs`dON`pjOC?|4FYXHDy}M7MC&TSig7!_pzL%@m$K~I%uUFWI z7YLSu`O_S<@AVM-Bzh_wQ*#j0VPHB^1rxU-4^yAOga=Uwre#4)Z-!#}SvaO;K}<)1 z>D?-rxGj2^`UR$+hhkbD#B?ka)6?OYmIpC?0!$~WVB!|-VHyya4j_@8mgw(YbwNz0 zLohil{vag&f}V8~d>F#Gfs4K>+Ip?%%1&EHtEK5G*qmp>FLBms`3-o1$17+D@xf8} z92W6P1?7zUFJ21#3YeOqmhq0y=%#bj+Pb1+npRFzTk9$?tC?8?=4NJY0rM4Rz6$16 zX4ZoF8Z)%-vwtF|!^_J2M?%?qTK|V7|%B1~4%&)3mYTy+OJO z;L5a&W@t5CpxfvowbLa$$}Zz4#h>t-;m`2TU(iMWO1tQ9=uCgd)BYd0ssE&d^e=jW W{!K68eB~)L<-KNJfaDtb)&Bu{QbjQU literal 0 HcmV?d00001 diff --git a/target/classes/com/ruoyi/project/system/domain/SysDictData.class b/target/classes/com/ruoyi/project/system/domain/SysDictData.class new file mode 100644 index 0000000000000000000000000000000000000000..1b96d58ccf3291d7493222e1f3ab8700eb4dfd5a GIT binary patch literal 4929 zcmb7I?SB;26+V-5fxn>N`s;IM?q)Z$69f5h@40i&J@EBUmrZ;OzrTyIem7BM?d7GO9+`Plh!3e!8(;*fQN9jF!pQXP==twOc zr4OQX7kwzxQ&D_8#)FS!YU9DjGPQI6iA)>0|5T;~_kWjZ6ZgkuN^<|1OdZ^h$+U(0 z&t>|8`x7#q%xSA#eINfOCvBjv`dw2r089on#_W;Am^O+Ir`^K2|gb z0Flm~j`-F@Oi=H#YAiQk8pF z1~nLKeTVK2TIm-w&Nuh~ND7wyKo*RMu!>zpD3{FHb~czRyq!kWVJ%~%z3f&~d|GVK za2>VNa@%kwc5?1yb;r>L;G6sWSo#TYWAfn5H@?3yIW=>3N}@-9*j$(Kst!|?S;MUK z&LBi>gvGQ(O{HdU9lf5PI^`R`IkE5N_5Ev`n{P~>gU-x(MEilchm(_R&H+_~Lk0V# zr5+XJ*{6zdlE`1d%+KGs%mg}rY9Vxm-G$jgnEaz4Hkt8`Lg*&`)IysS!tGn-Jz}$m zs%d)ca{lU}{Kezbdrseuq2f|@NwRn(nM%np$_^_WQJkTdd>cUD5xS_*xAYH%w($C9%N|g*AuTniV(3QPTzQfP=9mzxo8{=m(jx!Fr8 zE`z9QuHH7P&=ilO{`rV;COYHWy1M(49Ngrc89|wfmlh2H=T5C~k*U#! zhIA7Z?!{7rq6(E%`U2zI-$Ue~xMN}>negJ_V&IK9QE;0Tihs^EGAgbmuEJa~dPH{l zOZ<_)gwO3(FS8_Cd&eQChw(UTMqQ86{j`>TMtCw|RDl;~1s`RMe~EDe`ls>bR}QVC z^*TL6&timAUOpcy&Z7W``8sdC#OpU;A&Nrz#B-QHp(vkt9up`Y9xT)kuSZc3iuImp z4_YT}cIX9Yqe_&|Zspl7T8bGIDzv?@q@tJ5S2tZI>7r-)H)w0TkBjL=$cnE(ee@DW z5?2qja~EHtif;mopAKF8JNiBF@#0qW73gc4R((em#jT^>*H)^gUwanr^|i$I49ySA zHmt3dFknomV zcN(O0hqluIEDa{epdAU?$)7Tmh0Y^Xu>?;Np#?ESPa=~n*edgRRghmd`1Q=g&rfB+ zuifK^8zY2Yb&%i73-jAmj@vHhw=0aF4em}jzv>{rSHMr3ho65U1;33RKU{_({Az;y z1{UU*E9W;{&Mz0nZy5Y`hx4lm^2>l9yVG;Zi9zWdkA^ajie)c^4xQ>*i+o(rt z8A)MWMzP}6a4wM`m)+npG7lH7g%Sxan>;RfuHaL!CdolAuZ3`t=(TW6auCyAV0wKX zOk9^|F(o}rf22PNOnjGck&QM)n=Vn^1S#h+s-R+}eX|5ubhgss{T0HhF39RY$>QqS z#e2e8)dg7{2CMhxVddX4!qXidD?A57Ff9sVIvR@U^>9p!f|x!ArcdU<1`yu>@g&#wB zfQ1Jk{Dg&vAUw>%BM=^C;V}r0v+z?0D_B?wp^=3q2&-6F4dDq06SSu2n+>!WfIq{( zg2reWeNGS27t}~6@a#B=cg2_Z#c&GG{nJR~Gsy3=G)U)Y7k!0Oa~==oae9j`&^uUP dyp|7&cvsM0yp-C9TazC%61C9R)cVZw{{xX!l92!a literal 0 HcmV?d00001 diff --git a/target/classes/com/ruoyi/project/system/domain/SysDictType.class b/target/classes/com/ruoyi/project/system/domain/SysDictType.class new file mode 100644 index 0000000000000000000000000000000000000000..a26b60a78527ce58f3e0d5c824941d33c839318a GIT binary patch literal 3385 zcmb7G`%@EF6h6x%gb)?+ZL75{RX}iqXscDJ0-=o!NJ&8Pg-ddUg=9C~-5}T()}mJH z*v{y5I@9S)r>$+B+G59I259-ce+L2m$^W3!@9wVTK~Q0ad(XZ1+;hM0yf**-H}MY< z?V_tL+DvDPX%k6&=;1?%58)zW#dMZ>UF4uXCzZRfDf30YlRSJe;G{Y}N1U{q&rv5S zd{&(l4L`NrQYo$BhgX(Qpx+7pPQcnk+>+nWoFC(&1|oNH{FevPMD?#LhW5B($WhiQ>vB(o9a0q4 zkPMjB1*-Fh)TpS%)nQpYt4UFIPSplPNsqCR(QjFi=JiGIP>4lfu|R>QK#9<^j9AicFp+K%iH4=|1d<{*XqA;y7BI!EAtMyBNwLE1G0E${WTD26Qg;f>V=NxBg3p4uEaGS)V4t_vUVq3M`xjMt{yhFE{ z#)iZ}DI$l>G!jERDK(%pbg@-68jySgJV;Td>ryYMueV%n1z{#}{o%#GXA%>0zfK7B z>eI~yiS0Mb1#8khXM2GA?unzUM6%ggiImAM1EHnxEQDL1?vOUgK(G|}#G$cX zW+vt76H?9jQ+)8$?Cm~PRW8uc4A)GLKKg!gX7VR&&i-*dIW>~}@&4nfFJ=-qlQ-^I z!FW7%B{_B@dFdNV2dA^Q#vq$&*JdZbnwyw@JoP!a+a9vG^F$2Tg~N01iR?5~%U@^& z0X43LShLKFpfoLyckoW-roHs0o66}mH+iVmO?7P;`8BR#yY?OUo9UT_X67GnxZ{#d#}yCT!NoBc%P^S@!I4zTi~ZnP5vU)uQ< z4|-acjT>@AY(jF_S32xWv94Gv>k6-pb~8%^s$FvU=w>WaaBDa;hqU;KSqbSxsnb*c3TQmPj6u*@Jhs(dHFu&jb&Zlmh*B0 zH3A>MT#q>qv!iPJUzC@6+GXxm!&5b;g|(KJ{XB!A1C0WYv?6X}5_9 z9|;@NI2Ax5Ix|U*uBtz&=m9y)imS%SHBKdEZV1b`upGh)E|fx8$%QfqtGKWl!Wu5D zg|Lnb>mh95!bS)$Kp3YNQ*?KtCW(TyoH}S7g0O{-qF)?CPv}DG9j7LI8CyWS6Q8n^ gbeg)Mo2uk9b}8lIgtwq>6dIXrVLN?5)dx5H51k@-mH+?% literal 0 HcmV?d00001 diff --git a/target/classes/com/ruoyi/project/system/domain/SysMenu.class b/target/classes/com/ruoyi/project/system/domain/SysMenu.class new file mode 100644 index 0000000000000000000000000000000000000000..76af12b62ba716ea8327bbc946cd433bf12709e9 GIT binary patch literal 6125 zcmb7Idw3LA6+e^LPQnC2c(&R~DHalxrL_18jgpYi#m$3k5{X)Il1w)P`|9j0kZNr$ zv{Y*ZYmrs~OIyLVAW1C=CKg(?TB(ovXw~{?^-D^+}W+&5GKEn6qoOAEo zd+zU?d(OFMIs5AHX(GCoep5o#^iY5vE}=YnsF<#!FEBa6l<&)q}$Yj@I53#K1N^t#`&C6~P=clg7@JX2ih@^sZzAi@SRCST}GvCS^=L77XiY z6DJo(^k@S63}~pbusNUCgn=h$WRkslFl8jSXkBJ7-J3SGc(6MeSM@|N(wlC8ORB1& zsc4O!(9LRz@|RV#j@2(cWOqfDsYyElU=wE6}N)1Jy~G(=zv^t(I*0V zR5ST-c5wN!3YTB)+bT|H7tC4a{mvN68Z@&f+1;$NgH1$;L^Euy)*a5BqSv|SOIk7s zQ;%!ymhaal5=rJAoH!JBTi z=l7i%8$MzwpBTJj;>_;VD@RW~I(B^j_^~4rT{hJu4z4L?&9I$m`DLElowi%pp{4L1 zyy@gXDR#NX>|JZjx5heMBYbOe@?_o=&u25`stfxA?rqG(VsOdAbN8Jde|Y%(;o-62 zJyU(k=Vniu@3v*~AT}l-z?wbm&1WTAGBI>w{N91F1N~zMZ*%)`)k?P`1HvfIW(E6e zvkARqd}PNPFdLez3niG(4#XF`PFP>; z!DM7xb;Az1@qv-CLwCQ{J)Bt9LO#llbBS1?GvEY_*foc9ZBk~)&0K=mak%Ena|d^x zzklz!2X;@KxbwAPc=Z(bv5{i4<^PZ?GHGJc%w!{z7A6rU ztxVczy+kXMMl`6V_~k(*i^oyS#G`mkurq^)MKgk}Hsl&>9H18!`U(A%QT|LJl{yuA ziGHrojTBXAI}dy5W`%C$;Z7!ZQ54r5vYt1EKF)KWV6sPiupYKW3Vn*_KFwr5e~`Nj zO*OTeUWIlUkx8Kjixn@671r2V@d~dfGPJmAY*pyjto8w3wDclh zJrvmN${YC!*`74E2DfXSlh0#~n%3&@QkZzhS6s9kBW5q(cv(YRxHYsn9BQoF9BJ(c z*WoI{(^G4hrcQpttEQ`k`Mhu}0Pwu23!Fe(4G{hSjJ?v*}WLC%ua#T8mL0)!+}w zB4p%?;7A$4ku`!NaRf*12#)j-92q1yl1Ommk>E%r!I4dZBcTLGP6>{*5*(Qd9GisP zL5m?HtwG<9nQF?1L{wHeL{jAl<#h~E{z)nr5UXzh<>sF4DWF>V03^j)Pn+q37)hLW zwyq#J>nf~#f(rW~nznGjyoglBTT`gQj?h5|j*4il=TO18Wvj z-du84LG)QCQJPFY`-;5wZGwFrIqc(baqO!X_GMt7b$@RA0$%%W@Uc&#ZT^@7UQC_9 z)RhAhhpvO^dVvWA*9VjA#T50$w8K9RWG|)|FvW9V;xuwFg#;$lf1fxg_F^)8FmW8r zfW8mm510O7ap|Qb;ijD_iEg2f_}y=@7x#AH-jM@0r@e!FgTRd^%LjLf7xyhWaLAhWoHgsxO6$TgoQ15d3|h|;kD(i>DaRCLbmJz zf;;?enc=mi54L%w9a|cNEqD6no#M6S9v@pI+U<`?@nYH!O!wu$#7}{PsZn4; zQ-djQtt<6nI^c`xWB!;*y_gOI(}OuM@w4P$Y7&^xS@~d^>BV%!7t`JTm}Yu0JqAq2 za$w>o(!ta$Frm@&iNjf5OanfcCToHHCiJ;#fioo`j(ltVEHCas;2z3>o1b(C_eOym zy{V5c%e=Tpd~i#&*B?`v7t>R~^mGnP++H}CS_CFE$Uc~6doexZi|HPJOtZb1&H~eO zIWTdn<6w#iOz78r?zh~F>3JVae81kBc`w(@eL3ok{ZXRN_}fzMwdDoa@?s8KxLtE> zX%)8MXNQk}=Xh;-$;TE6wZk_K=Xf!_3{0=&z{IVigQ-nmx}QF4KXvY&qri)4)CZH* zdVUTX570sTlA5868%xpeiFPwzbc0E;3n~X`;UHa7ekp|4vG95bm$7g;gg3CT2*MRC zEQW9;3rirpk%g-uEM;LCgbEfaAuMO%O%UG9!U_mM7FI&InuS#mu3_O?2&-9m3xv0_ z@HPluR#|vjvoKXi0gQH$}=kK6U|AgancDG5IrVex_?>T4pd_3plJ=?$kz4r%z z7w~=*&mz--?HH3|T#mQoI48$MJI;6DZCr?=9Ty|$jq>3o5hf#ui*PxDexX_fyM<;W z*dsI-L0+gH!4;uX5ljm;BDgAaCW3;{qBt|#VYQ>Apxe=HLo1Bywqsc4Sn{|6<)nhn zVas$}&2-1LLRoJkaQRadL`=&ya(XgP)6`Y%nx+;sb4pEF=G35qHU8U-;TCiSJ#`9c z*ET$rXrNLlQKQor7|hj8S3#ID-LgaGkYO6`5d|%M@o@#M!&aW^Yg2})kCuyB-OgxP zKZKPddW|_y4j#6;(*}L?r*c+NwaZq;P)oLTRnNJqQ*m6qsOGJrW|(Ta;*5If2=po4 z^##eTr!QUyeknVrUS(ZhqlO^uKBc?UR{o4;Yen5t2!Z1I zWhkm8+;X;Tx<*kSHyndmA2V4+&1L%-Y-?T8OuuNE>a63a=NyMQwik8B(WY3*T?=!! z7C!sv!Phtc`R0rJcRyMD;P-`(=N3NwVe!K+AAEId;m6M(-uhNSub1MidQB@Bc`t;T zlfkfAJ=alLuOYU7hORfSba?;ny@%i3Q?T>@ourK$IvunW4YS1?GP2sNu={Ac((U5> z+~S`%5A3~v_xmN7+m;t3!`WI^l+IcQ{IRPQ+LL(+g5FmrvAe2Lebhm%bTFSCe7(d+ zT+45bf@s<*+d2J&A+y<49j^hoC@~B`jiDEPF~pIGp73);t^I1YY!vdkt!9EaL%!UOcVe*Nh+{W~12`B%8P{Ssgu^kU0XiB zaow_K)a!b7={62&jy}S{b{T5mk&BUbD^l$4Q)8)2ax#@19hppLCQ>6TXzKBta0o~0 zZ|3Q&tc=0O^&&Sptd&Z-$vtwe-eGWo>()jPBL3(xg5DFWEUe+6sobPhNg7tSz{;*s zP>-{*fyr3VW#7b?d=r`JvtEb!f!fX=5XM&Qz)o;sPzvEW{y4xM&l&b8$JnEsWRG%~ zNn^C(yz|^kNvJ~Fmbe4uXHV^Uo?-9dTI?e)22{X)ej@p$k#Xj^-1s&^Kk;PMbrJP#XvtsuNWlROo?OxrdtLJ|e9V32An+_p5j z9iUrv6}M8hs@u5dmiHAcD^hJ~O!Z)sRFBX>FO6TN&!Y~GVaR8Oahy*_k~o3al6YO- zC2^9!1MC(LfZ|8?P5hW6TSR145Z3l5N1dDVQ$=>w5_7;j#)X+ z9ouJM%dv_ZIwdvi1kOlNJ>t4vXon0N7}$|3cqPlPcvZ(b<=Z87#`7mEJ1DC{I37o_ z7yYgNse&ppVv|cx1LoYP7cYN)es1={+=WX9GBg$kt7VnO1p}>pUa?YgMW<)hb(P;& zw1Yq~>w)XfdiPQ|0!R-G9qvDt?PF3Q-sg=nNqd5NKJ=Z4yv8{H-*@K!a!$JsEJOds z`S}k%(U!dDhX&Rs0%hG$jVYfwb)+k=ze&Qpan>QMkW6a+)#)oQFm{`->`@kIQu(3s z`=`nV)@|E=<>SB2&CJ}tegE9K_vYXFke7F=Xpga+C-`Z%<2vC11C1S>!?eUaZp=BZ z8mN>;ls{;*V8YBRuo-N&1a%f2_x!dXEz2vx}%^-8wmTKQ^ll$S&b zEn_MaQ5vPaqccHMw@OQ}mv`^HJoC@Fk1x)D^uB>x zm(PxL^RoF_qc1GxzA<@anz~%t!VSfZr)kYajhCb)_#Rxu7u#3j+qEr$Zy|pic_B~S z#v7MaVyThux%S<$6a$r_jQQp(?|w1=_t|g$I?Ikv0wp8OJI=Hz$$KG@5SNC_>p79$ zT98vta44DrHa_PdO;fBlb)1mzGNBh=ANNS!tM~*>BhKb+d zO^N<{6IncJ;w`*wVgN@>JS`6!BPPahgg6VF`B3#%O}ry|Ypf4)+nTtDcj-|nv)v<) zKSUm}9((*zJW{?Y+5UuyKS}J8INiInl&?9`?3E?YrS*MAjV#Q5uN|m$zWZ+b8y}fvw;9GV@_o>eY65C^wirnad9JpUe*)&-Jq)x$6tUKI~7n*O3ufar>?} zigZ=VE|-6OE9_`KSRU6s5PKp-th$1p^4xRQ8Hss{0Kj$X2{U$C>BqN(%kiLG=4%M z4v>moZcTjLy!i2B$k9t69N;O5QeF3bhQ`|TJJ8GjPOjwlObtKnXW+T|{F-a| zJw|@HRrtjXuKDeW_;Kmi;g_oA_xLLOWP%ED{YE1)YhjSV;wu9Rr|Q)uRf~I=xSv=B zcRYU@_udFMe@-#2lsnmX=~~=R)xmAxbp5+b*J65(nCw+B#iOq=bw`*SjMu@`@DYv4 z`BZf=O`y2qo^GkL9>$4jViioXa13ZnJrO4U;MJJE;$D%R;rs`g<6WPkNr72!4t-={E*+m~vw z8*7=^<0>rUCOnVZ;d4g^+{YpIp~5mw67?Aj^H0%}sIq6yvPNFwo*cb}8sIWg18v)R Owvv#A*Ric<%l`ndnUmuH literal 0 HcmV?d00001 diff --git a/target/classes/com/ruoyi/project/system/domain/SysRole.class b/target/classes/com/ruoyi/project/system/domain/SysRole.class new file mode 100644 index 0000000000000000000000000000000000000000..4e299cd2a9afcd35ae72afe1cc614deb063b19c0 GIT binary patch literal 6374 zcmbVQd3aRS6+bV_%w!ls7C_L7J0y`ZY_|P`P#}vAB!Olt;DRqRk1%95-+KeZ+7_fp zSt68zC}P!$Yz2x4A%+62ZPi-4Xj{8zTdT<=>H25?>(}48?`87l<&BDbANQVn-?``f z&OP`3?wP#tzuwD4G?xavXpR=p96H9$aW9$^Uiu!LM5X?sn<)zbkRe1vcVtl;F5=Gd2rc70q(DOXeswU^iTu$Kk`r`_ph+> zk3H1PgP(Y)h5Mg+Xa)DLdgwLoU-!_@xPQY#Kj;2U5B-AsUwY_Q-2a-5yyd31-SiuY ziZn$H%aK+^)xz;uOQ2dJX^BK*tKulIJoVs8<>Q|HT&ai)tDo2$KakaxQ zYY8Q&uQRO3X?=gq#-I{`#k?3aCGrg(-8;DbY=7_W{@oWO^5A2nxig`->30%&tKyMl zG{$3S24XQqt%}H+roiihtM_{Ah&cdoH?-8(GzO~Rlml-Na7rwpH|c6v1V4;%PuIZF zGg!##OO?(v_|(%|hMwGG+G&cbxKsy_Dsl*kTCb?OqSkCoNc7cNm8tEUQirx1nugQqvnvO;o#;P(uRiK-^Hz1+t=G`otLOMs>G62Yn5OJ zZ%j~+U<0I8m=7rl-8S#S0n?Mnk{R*9j_%Z^&Cs1$nL71+e^1Z!S(W{p4-M=)hV2X~ zk=lsdjw7)~bZ&MO2w`>cJ(g`WPa@P4y7)RKp{UWY#)lia;|$fL9*+2%6dk)=&=hWu z0fvghb4%ve`4+oonipV&YkoKu))z>WTUOo*JXP@!){d(S$CQR-v`tZ)WgLEqO6uZ4 zTr3&a3uALKPhT4bj&jSnOsMg7xCDN!Q`42GKNOG3;h4XvQ)|Qtg1^!-3l0$YTMZJ7DZ zXHwJ*t^<4|Q=5-gQj?GFqG>H>D(+@1!n&RAptUaU?3ttmE)ZL{QF&x=%a~s0L?z;zTs*oqRTm z>RalX1FP!-4K=Hqnpf7tDXc>?hQr$>CcEDyW5<)B!8{CG)=c_fV`#>iMA>H={b zC7N~BXN7LUZ-%*euL|i}x`RGP5`7+{9Qp$O_+@d0Eo`oj`@06~oYP>&BF4f8{hFeO|qqvaZX317h`0u%^E^x%}y+O4ep zWoUzJgcj&Z^xz%QgL*^{_QC4aLVkd5fDCp+8^p|fte4PxD=v^!k$awUUKDyEw1vbE zdo|@@a|#f=f|1YArZv=#5nEad3v6i_`W*D*(v~W2KTo+M=1O3QuvLb+B{08Kn4D-V zGfl#PnTIg6^Cb+}56BNK`|+b^#Q7EHDEAD69AdC8F}chFGCvJ6k_NfJ1nIJ2^9l^j zln?smFbzzuG$xl5rYOZsOtVc)9>&x|c_(eKR$5?rOrr(-sK719bT>AIV=Aq9m5RI- z-ITxYAi497QeG~b%C$szzr|FkKt+^*PCn%nFO(=_)9_AUt$ov6_DxePCWkkTFToU` z(|}9YC`s#KX+wZEQfGi3;;FCD!_c{za>i3y3!;M_e#g<*%t$j5;2Jp`MgrC$jE*b` zFfN}NUoBiq`~*70=eEc9;K#?eDFgRrj_;At@olojXX;6`#W_B=J-+pbZ$p;&3=T2l z3yApex;w<@vB&ptmiU;Y9yRi}O$0NRBt(#&lIWYX-6<~~JMPWE{YVzv2F01UmkQh{ zNZ{6f?h5RREU;tR2~4}PU^3X%#MB@#p+0iJm6`*lF?LJ`9Wm{3#x%x`=^0>pHVYW3)6?`F zGgNCaQDn#Tjsqs6ih2eXpQYzaa$XK&T=|HiqKL5>54MrRcY*D_EZB_7?7LWD)KxD0 zQhZezh2D$pSVX_2#S16@ABfn^@Bjb+ literal 0 HcmV?d00001 diff --git a/target/classes/com/ruoyi/project/system/domain/SysRoleDept.class b/target/classes/com/ruoyi/project/system/domain/SysRoleDept.class new file mode 100644 index 0000000000000000000000000000000000000000..f09663695b33542193daccc8d6380df233b819b4 GIT binary patch literal 1354 zcmb7^ZBG+H5Xb*p`pgNX1$;ooDrg^oML~Um;f0C`$$`dJBJl;ewwv}yd)r*ECjC}w z6cSDR0DdUr%=M(D)&$aIcV=&Q{=b>o?XTb8e*)OV(3eXFUq$KRsQY{&Mt z1txO&1A*`hT~h+nrtPTxZmXi)vQ=r)O4`&_t9f9#Hv7S1*lXAwfmO4rThi_7o-Nz1 zK2}vvc6uF8wPa1VEZdQ#Ugs^ryd+T)Pt=viF6*ar`G4sWoxz4oZuC2IruLNA(6u*~ zYqgZ8T%zEpEWlAYz+XG=>5}K#PF-O3oLGOAc#48kqy9&{dn(15APnx5kdx#8;v*byd z1?)?GVix=?# literal 0 HcmV?d00001 diff --git a/target/classes/com/ruoyi/project/system/domain/SysRoleMenu.class b/target/classes/com/ruoyi/project/system/domain/SysRoleMenu.class new file mode 100644 index 0000000000000000000000000000000000000000..ed5f6d0a099b4c9baa742fcc8b2782122f1d27da GIT binary patch literal 1354 zcmb7^ZBG+H5Xb*p`pgNXJotbLRnR^Fi-P(B!wU^2BnKK>iNqJ=+HT5`-feTen)F+# zQAjlL1NfngGuM-rS`$c<-I=}J`Tu5Sx4(XW{|R6d&(fH{QVQ3w%)^5O9;T2-A&n@C zNld0`vBb_w5>xD~va^E!P1{xb9jB_iidAjWO4ight9f8~Hv8dX)IYY{0;^_CJJRdut}R=h zK2bGaw!3X#IkK)D%XVeC+kQ(hdn8KYsfO~|W&Lcv@Go7e-QO^sAO6mqnLXtn>-rnZ zvmE6sk0>}Q3vg5p@YhcIy6k(l+Yp$!Al6$Yo^)AvyqbDtbE266)-|r1fh?vCOd?}o z3K@ZQ?KPy;vTDalQsRzw+d*wNWVK^A>&larQ1M02PvDV($JpRVn+9&;mVqaDYTzzg zi^vI-u24Z@?Dz9^mCG%#c4?@x-wpEIduLX5kIdcuouhK)(A=TK%&P^V((Md(r^m8& z)hOK0xYcSYmpb=;FkslBfeR2y6fUn3_FxM+dLR?@t|IiKW(F4Zljb<+9%7ot8S*5} zg63H94B~UJZ8qEljAM>$qqhOqaf3X;9i{Q&;jf5%8ag~X^6)%v6A>TY4%mpNSo(%Y zUph_eAc+{}g9vkh9}nf3pYsv8L%U-1cxe)Rw?;6H4Kpo_WLg-_G&an%L`=*7VdC1J zGff3dbk#$q5A;XHMb7;|^sx8^u`|T8iQ*}er$}YfWQ>z>3Zu_EM_NFEdRU~)OO)*j Sy`91Uf9|{?_h@GX_x}K%Xc!>? literal 0 HcmV?d00001 diff --git a/target/classes/com/ruoyi/project/system/domain/SysUser.class b/target/classes/com/ruoyi/project/system/domain/SysUser.class new file mode 100644 index 0000000000000000000000000000000000000000..cd53e58b60bdd19621f982082acb35b3d385b0de GIT binary patch literal 8257 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 NcQjp#{<^uN{|72foErcD literal 0 HcmV?d00001 diff --git a/target/classes/com/ruoyi/project/system/domain/SysUserPost.class b/target/classes/com/ruoyi/project/system/domain/SysUserPost.class new file mode 100644 index 0000000000000000000000000000000000000000..978b3192284fe15aec51cc91b1ed50fbe846e748 GIT binary patch literal 1354 zcmb7^ZBG+H5Xb*p`pgNXJotc$RnR^Fi-P(B!wU^2BnOhT5Q#6ywcV5>?QL^+HR-of zqmXFg2k=7~XRaqLwI+}zyEA*c^Z(7vZh!s${u96kUZydD#T0H}iHFAtJV_ysLK;yN zlbB4?Vu_vQB&OI|VP`b~nZTMryyGc%r!J5&PpyxZY+6o3n%Ze>(zmwueEt@HWjnUN zB`}sR9129==(-Y^G;K%iby`*BR;+51R6BqyCBQ39Ohk-I8uccWv2r z^{J})((8J@YRS58S+*m~UGIQk_DPh)Qw`;_%lg@T;a|Fx*WWOmAO6mqnO)_d==#3p zS}o-(mnb+Y3vg5p@YhcIy6n5Q(-4@sB-UFco^)Av+?sl8bE266)>W>Xfh?vCOd?}o z3K@Yl?KY&&lgtQ1NxoPvEJ6XISS*8wPIUj)CWRVcMadtg>}j?JCD?c;Lg$lRvH%$qqhM!af>{`9i{Q|;jf5%8ag~X^6)(F5)mKX3fPFJSo(%Y zUph_eAc+{}g9vkh9}nf3pYsv8N4sM5cxe)Rw?;6H4Kpo_WLg-_G&an%NK8xrVdC0e zFiizabk#$q5A;XHMb7;|^r-j+v2(<;iQ*ZOXGmq!WQ>z>2BXhAM_NFEdRU~)OO)+0 Sy`91Uf9|3o4{2uvkNyCGq8QZx literal 0 HcmV?d00001 diff --git a/target/classes/com/ruoyi/project/system/domain/SysUserRole.class b/target/classes/com/ruoyi/project/system/domain/SysUserRole.class new file mode 100644 index 0000000000000000000000000000000000000000..38882fec45d71521ef8bc0468bee8db6385eb788 GIT binary patch literal 1354 zcmb7^ZBG+H5Xb*p`pgNXJotc$RnR^Fi-P(B!wU^2BnKK>h{PA<+HT5`_O`jZn)F+# zQAjlL1NfngGuM-rS`$c<-I=}J`Tu5Sx4(XW{|R6N&(oN|VhY!=#KXe`9;J{+A&n@C zNld0`vBb`D5>xD~u(O(gOkhnQ-tm;XQy0jXC)P(xHZ7+iP3<%`>6@#YioeBQ*pBUQ z35?|n2LjPox~>E!P1{j>omN%36|35$m8_|2R`bAeZT7>(sDEsG0xM=sx1`(AU0b$Y zeWGf<^tzs}TC%QNmhH%L*V`wUcO**UsfO~|W&Lcv@Go7;>u;FO4}WLQ%&zi}b^Wd7 zS}o-(mnb+Y3vg5p@YhcIy6n5Q(-4@sAl6$Yo^)Av+?slAbE266)>W>Xfh?vCOd?}o z3K@Yl?KY&&lgtQ1NBYPvEhECs^l58wPITmVu{uX5cPc z_mC4PU7>=+*zf0?DwkVe_0mvfzZ>MayKh!@j?A6C?W1z#(A=iP%&P^V(rpiRr^m8+ z)hOK0xYceehdTFuFkslBfeR2y6fUn3_Fxk^dLR?@t|IiKW(F4Zljb<+9%7ot8S*5} zg63H94B~UJZ8qEljAM>$qqhOqaf3X;9i{Q&;jf5%8ag~X^6)%v6A>TY3fPFJSo(%Y zUph_eAc+{}g9vkh9}nf3pYsv8L%U-1cxe)Rw?;6H4Kpo_WLg-_G&an%NK8xrVdC1J zGff3dbk#$q5A;XHMb7;|^sx8^u`|T8iQ*}er$}YfWQ>z>3Zu_EM_NFEdRU~)OO)+0 Sy`91Uf9|{?_i1MY5B>m~@)%tJ literal 0 HcmV?d00001 diff --git a/target/classes/com/ruoyi/project/system/domain/vo/MetaVo.class b/target/classes/com/ruoyi/project/system/domain/vo/MetaVo.class new file mode 100644 index 0000000000000000000000000000000000000000..2137a6971d2620eb8ea2158ed113afb27e29067a GIT binary patch literal 2139 zcmb7^YflqF6o%hvw_RF`AXXIdS}&AxSrxqCHO6S7;w1q|03`^UR!g`s3%f?*L{oS41Bs3$QUI!?X;J4CjhC zj|(dV{OfGLua|c&(7X z?gxH!Qz2U&Ut;!7xXJ|s^M2qx-e|3O?L~LR2#0mIx#YHe`HquT)bKkB6Z7@3<+L}# zx4yI94qtio$mzW8L|)5T4O^}sIB!Ddkr%m3A!){{+ZnfK1ZI$X8sX|wx9zsP$ZNAy zbyu60W%t~!mZn#}x~sj*>6`l(BDenfk-M&MNq6X?Lc#Am{&WAlYj&X6@uDPURW)?q zninmaR+g*d+w~RtxGP?9Acay+Zd2sGX*n0iGv?S-cHCyp%!&))M!W9a^QFOiOoOJy zKP3!cu!I7NCG3%*g#HrtqG;n%372t2VSLvuXCJ$se6k|$*pf0fRj70oIctT1vl02t zPHf|6GN)brP9uue6;jW>jFWsBeSA0;GJ?-pMoyq351}K^!PpPl9D^M9@oR=rG3IJt zpg!qkLmZ3T4vJc+U_WOivj}Az;FkytbA<>zXUsAVh(K*(6PeoNCbC_rA&LNJIG!ch z7;m$xB~O@bu_OgFx3COo@(d1gUXbH}2~+h@T(!w4vgNH+YezR>bxRK;$2AjNIZ2f% zmY!y5N0+ae{Ym+9QobCQr@Hi?m;%zDO+ZR9%*a1rrZ$o5g}K18GgO)-%teA+`VScC z-!_=NG0fo@<~^gFx{`7;Bi2{s-=R<&v0}6=q}#`69$=1WSBZ9wXxE8#1EaV_58T#> zvqm+!uS#-XmE^uE@qMv6HHy?5rS34)FUsU>9IY%rP9N#l&0= zqjBeFSRa_kXx_X|-ql>a&OE_V#+f%6#l`vB=oVKPZ$IF99%=<~GFK%h%j;7p&6iXB z5bITNieoQR=3@`jlT@bDw0|#?yp*mv4AUZ+p6$RSk2+zJC!zSB(wMTnOwUu9#?qPo zdboz^1({y%z$8B~36puE)l{ZjFH=30X*`|juSaB<8f5Z!U=rUYOvX<&-K2@%t_w8% literal 0 HcmV?d00001 diff --git a/target/classes/com/ruoyi/project/system/domain/vo/RouterVo.class b/target/classes/com/ruoyi/project/system/domain/vo/RouterVo.class new file mode 100644 index 0000000000000000000000000000000000000000..db4406f5ef69344c5e33998c96b5a6e3489475d3 GIT binary patch literal 3106 zcmb7_OLH4V5XXCD>*+N%eg%^N0RqT&;^kq&BaRbWflK%r97t7CRB_+~@S!OFJu9u`^{UD`sAp%UyXV(EJ*|KL`}-dvTB5IWq|s)U zMreygiN!XH9TsI4yDYw7QDJdEO<$(zfuLbqHVi=%#Y1^4bxYa@df9VL``{Y(j-*!? zl&YJxnqdnPdxA2qQ8Qhm>IuqLoyL)48#ca^-}{E!5|oqHW7%q!>&|0AlRZw?9LF-G z&0ZUZCj~7OVXnKr(=zoV*Exhsz1eDdMnkVT4Qbl?v7>K5wc@}iQ>~j;&1H)5z^m_> zmR>ZQ9(J>3^T3v#?;?^Lk?rNPoU`Njp5fx86?jW6o3`n#2pY=IRlr+tYVbQ&G;L$k zZ|ocHuH3g&&YY^WD$+H1-d-H`>Sj~Wm4Qwbls+)LO?4Af`MI97g?Eu|b`+-br}vq2 z9LcIXwUTt<2ho6ny_VD+vU%{8#oFEWg(5&)X%>5(mErsz`x=4LPI%EHQ0UGy>=n8< zuVJ%in!)aNPzxFCv_D6&y4q72({W9_IhAVeSy_RDIC%*?X?+_-@s> zZE~59b{@wfw+KPAJHG9i4WnWFAIPx;%&(y^ema;eQ}Y362n72@X_DjF**MiiKj~CB4kP}YQP|1CnQ!<*Thp|lW#^)&+W%?FOvJVs2Y{)dBnBI$JN=2EBSf=;mnNm@vLohw+ z!^H0=WSUe=AH*`HqfAaL(^5QBI?CjM$?wC&Z#iU|QcNGxRV+o{T_(!Zie>tUK0Z}X zcz2m7(~n^KsSgwPm5^y#F?~Xx#xP~0Oh3mmU8ChVrfihy7cl+WhlzV!$aGFIU01Co znx|Zp>9-iBphw<-$D6cDLn_%NFou09$81BY%LRSv3D|z`!xr@2HCO~KG==}53tie- nYy<}u;PNxf5_F4hV;qIj9sKSZR7A)597{P&*J+RL-JJa&DjYFA literal 0 HcmV?d00001 diff --git a/target/classes/com/ruoyi/project/system/mapper/SysConfigMapper.class b/target/classes/com/ruoyi/project/system/mapper/SysConfigMapper.class new file mode 100644 index 0000000000000000000000000000000000000000..ca4ca31569eba4c80ab904accc3912667ff83998 GIT binary patch literal 1011 zcmb7D%TB{E5L`pw6liI9!@XSU3%Bx+KtifW1yY2B#K|P4b;%3ILCUXj-~;$5#3n9H zOT{5auV-h+v*WMtk52$Nh2tt5R^f<1GvY3{)pg*%h!KIuu4xAzm2r>=8p>dTcQi^O z#Xaf-9uq#bl1QWbCk3zsT0^eJfpgC!^SI(NB2clXV_Wkou9AU6;Jj;2*gK;x^GDPS z{87PiHO-w;EwGy+QW<-ZcV(Wf%Z2#^|;HXZCV++wq}ii(QM+ax=wlbGK)7Jh#{WbVW6zL1sR! zYuM=v^Q@}^X(~Hr^yc~yo5M*orlrHkRnig1GL$E_7*O@rbv?zE^jwg(((!_x!F_J_ zg&|KM4h^0g7>O5xmCv~yfzdzOxE<>P(Z-}Xd4a7PGskFD*Q>*11n%^uZ3V*BZBOGb zT@k-94SUxb-L9SUX*>75z~OcgQ=^z}7bDr{VPd--YxugVI#@N&^zHpO%0+3oFy)HTO!IRA_A#T1hO=v7;_A8Q1+(BJV>Ex_Eo3ZfC%9}4Yj4v{9ca?R@=2km zQ&sDFQDBM>(gr{EY|rd*-xtA#o-Rh~e_c*aVs^2dQYUmje+368dY6qdVpPHsB$^mu zbcaT8E8|wi*yB^dDNz-wF{(kWRbwT(i_iE#JwcQBpDQ8MJ)Ef!`oZrwe5a`nK^C?_ z1knuL&oe=kWtxR*jvfqKKP-wK6-AF}K8p#63y8UhGYoAZhqj1hPv~i$3>8E4tSDNd n<=pCdVzojqGCn}n>Y#vTl5kwpnsKp;kgXxG(7uFAiC+B%HaEXw literal 0 HcmV?d00001 diff --git a/target/classes/com/ruoyi/project/system/mapper/SysDictDataMapper.class b/target/classes/com/ruoyi/project/system/mapper/SysDictDataMapper.class new file mode 100644 index 0000000000000000000000000000000000000000..9e63a7264e57799c27f08fe5f1e7cf7f6d683bb6 GIT binary patch literal 1414 zcmbtU+iuf95Sc6@e%Ssgf%YsrtgxCSIjm*fIlI4p|M*EnuV~PqXARn8v>r=Oj?G7fC5&)XY#6;8 zIAiVeDAB3nVWiK2;Bguo>2p{6LIr%7#?zRCedk*PUbEqbPFi9jC1^;)}n}wLZ1-0JgiVxG1Gz5>GK~aS#f(aj*NClI5h2>Qg z5yO9oSe#zR(sRT}dW>GrIp1x&}b9J!Ru(oQ*ej95&hJE(iK}?r=Fq9{45W%!bTh@d$Fx{cM u)>NSG(S2)rkfR>bBWnU{1U|lMdO}Yv%Rbm`&tcg|qLT17es=(=(e58cvxMsa literal 0 HcmV?d00001 diff --git a/target/classes/com/ruoyi/project/system/mapper/SysDictTypeMapper.class b/target/classes/com/ruoyi/project/system/mapper/SysDictTypeMapper.class new file mode 100644 index 0000000000000000000000000000000000000000..1be7270677442f9f3689942e0e86663b9a55e550 GIT binary patch literal 1066 zcmbtT%TB^T6upB$0Tp?PZ`XBWHF1a0K#U0qL=$x7YHTMk*q76(Nq^0SAK*tBZ(E=R zbb&6K=ANEA_sluBpI`4E0B`}v3LGkMM4;v|lj-t?>+&IRS&w^?z_r@b9ZM6w6L8HH z&KMb*7kH9cn&DWK+u9)Trai5b-k82o&6nKN!UI|aDt#tLj&V-~wU}hWBTz6VGYAw1 zd}veY3r4{H->l1(lwP5<>imt{rim`7Kir|c1g<`8!QM{LH6nW>!%S)qwVq=S(ZN;x zCXf%k43BEeU#1g;Arfe-aU{`%ffW1?GinBIhGf%}W9jwu!Ljr%f%-BP18=BLDHSua z5^G07ncE%{v)v&=yn@2)hxgLM?$}Rvj7%v>maUu#-S>__qrz5(+q BE>8de literal 0 HcmV?d00001 diff --git a/target/classes/com/ruoyi/project/system/mapper/SysMenuMapper.class b/target/classes/com/ruoyi/project/system/mapper/SysMenuMapper.class new file mode 100644 index 0000000000000000000000000000000000000000..87947645898c31f30b4f50cbec83725b1ab80397 GIT binary patch literal 1760 zcmbtUOH&g;5blYAJXFX-L3|qo69nd>UV>H;s;r_aQHqEMZ||hJ)D!iARJR z;fXKxVVA*bqKFbDno4ynRMW#iQvfUmah!M8TF*c>3EG$o0hRd25+zT94~g%JK|h9 z4qT%g`BnL*Q*!s<(|Iw5{5Emf#+m-XAwjqO7256&@f| zf|*MJMOhm~dI*p3yqZF+kMVorqkeQ;L*J+H3M1lmb#lCX-mjRT4+GgOtCqjK(ZoQD3I3 z`P2_(LRBscK{F-SNT9mVh9@+V2@@#|3T-9kD7ob3RYp%tv4k70G=W}P>6hxi8u~(; zs^S0jr*^p9h?tgiY}mEx1l&NZQf6|+39P+6hTavu-h+FK6AVPEx!P=zz-+JH3*_fn z#0J^Kb(5!Pt{ literal 0 HcmV?d00001 diff --git a/target/classes/com/ruoyi/project/system/mapper/SysPostMapper.class b/target/classes/com/ruoyi/project/system/mapper/SysPostMapper.class new file mode 100644 index 0000000000000000000000000000000000000000..b3847fd890fd91f1114fb32325509ee0de07f9ca GIT binary patch literal 1355 zcmbVM$!-%t5PcmS+c9LpPQt#0Z6X14$;}B8K|+e;kVsZeIW;qlGhsG*dXVPVIPd{{ z6r#G@xCa}A&B3y&>Q%j}*H!cV$JcKF_TaVftcB+cYlVnKpiZ+w4WdFZ^u0llCB7`P zD)MugT~gF9szQmx53__vsXwd=jqmRcF8LSkmnw>Vo!n>GI2P(63s1S^iBLio42_(y z3@gKElyX%{!SM0_Iriu7?lrZu>ZW@W#|&NX4?Q2uI2R+SNqATtg+@XnOw7}fKgiM% zDK)RA3{9<-A?|qFIj7d;di5PnXluZ8`SBQopAsWq49A zH&ik*#ZB*gde&V6wyfj?=#+WL zZDocR3#=(s=|m~bY)*-x7hH(olkQN9aF#}&OA_y(-seQT`FEqO8?~xBg11>XAus^# zpmv5?DFg8?ipl7XZ}ynyxsb2)b1}U9D{*3AzYg6d(Q&)2KKY=(3rz-wCHkNMR~rmh z&>(A>tYunb`fJf|32h3kphHk=Yv?tsl0Am50pwc8hReP_$?j69hfNpSnuN9~bOSf1 o3*EwP=elER-^D%WqRuGs{;cZ(9y-(`gW9RP9uqSifEnuPPo)riod5s; literal 0 HcmV?d00001 diff --git a/target/classes/com/ruoyi/project/system/mapper/SysRoleDeptMapper.class b/target/classes/com/ruoyi/project/system/mapper/SysRoleDeptMapper.class new file mode 100644 index 0000000000000000000000000000000000000000..4831040a8f321722f1bb63497e03606508196341 GIT binary patch literal 525 zcmZ{h!A`t7yi5h8Yxp4AG_nN64SxX~7uLQ(K*V0i64a AlK=n! literal 0 HcmV?d00001 diff --git a/target/classes/com/ruoyi/project/system/mapper/SysRoleMapper.class b/target/classes/com/ruoyi/project/system/mapper/SysRoleMapper.class new file mode 100644 index 0000000000000000000000000000000000000000..39926420da940d50d3d4f71c5668ce8a206c7d38 GIT binary patch literal 1491 zcmbVMOK;Oa5dOBLNgC*bq%E&Pp)KGbz?a@ak>~*_BB2#gPn?XsO}F?Fc5Nj8H4gj$ zeiUNXn`Q$sBH<7@JM+!VH_snGzkLUAh-Wo8HMAMl6c4$den>*z6DnosI6Xg!T$v@g za7Qw^B(1A*mGa09lE@RWJIIyBciO#6@2lr#sR&(d++o=0^YkJKPCe;GJmpd`R3u>; zRt91idub-=|M~yNcr`b>*FW_QYEB$N_Ke4L*tF1rE4o?|iyy?E&Ml6Y8_QZ*Z+ z`~O5QMsC%TdlQBXO{b)wM>E33f^6F$x^kV$GNT{&C4IK_S`LE zsOsU+Zni8>Wk|lK31T>yc0Wj^C~s0-H_2F>%we%u4O{*N_dn~A*AdQQ@g*Z>N2PJ* z-5rLyPTf#$P6E8+`IY~g*3+Azk&OZ`<(dG)^96RNOSU4W$`t!e%?oJRjU&^%)^rYL z0mx?kcNp#Prt@*8%LFVab^`)521zD;{#Iy;&GCWgd!rGTFZFmZy!dNnq5^hxRg4i` z5<68VpY#!`GB7OB^#X2G7_Om0uVs2I(-_gGM!zN0NwtE8Rb8K`wACus$curl(|e;F zdXrR5v8?q`HH<7JaWk?@WvC7orE)wJ0J@h7V^&kFaM^Ch|TG OtmrXCW_VKM`}7x!XQEXA literal 0 HcmV?d00001 diff --git a/target/classes/com/ruoyi/project/system/mapper/SysRoleMenuMapper.class b/target/classes/com/ruoyi/project/system/mapper/SysRoleMenuMapper.class new file mode 100644 index 0000000000000000000000000000000000000000..4bb06cb97102f7b1db99739c1e3f26b21e380db2 GIT binary patch literal 516 zcmZ`$!A`rQNiPZMWHOjr}zbet;ikoUO&y zL=T(HyqWiA=IzJl+dBYU!I=$n8x{m+e#HG}m#4+;tH|}Ej5&e%%Gt9QMq`$S)RAep zKHn0Ux?D#xcwmYpTyvEZ=qE_r1_YJ?j}bF;qqQkbWD0#%2b13&1Qx5Ozov9WfWqS) z(|&Z2Yc?vhh^d2%RvV3sxS3}V0s~KkDbt0*6E^h;KZI+?mkCvcECtP!+;d;kyv#LE zXdn|NQtFlYVc`0v(jR)VP(HsC#*}5N?y@Z7>cW^r;PPMLq-tvme-qKm#^3v$*`-f_ yKnMQ@z_JK*!NRCSHR)*a&JS9*wJmP^pG+cLuu>{(!ZlGeS zQ#BDZ(dr0qshJqdV;ZWM2}%1CI>`ff`510;t zH?1t=h{+-CTP@@eMYyP31S;;;3~EmIy~%#)b@ys|?5?^b@{&V#Fv~*X&exN`^Gk@l zkFjYIqC_rVtYm)ZjG1LZzlbK^VlNB8r$SwHhq;d5-l;XQ%Quo&0NXeOLC2=R2#sjaScX)?m z6P?7AxI3ZReU<8fzZVe#txsp_fF%jnFI^`PcyVrL#op~?5VIg;lQ+=Eft%YU0t5;; z@Bpp^US& z*qZ2Jli7dg|K`u^$LHHS0NlZq2Qv@m1QrobxZ%&5tDV$lUD|3B5tuFgBYR~uVOdOl znZ>K?4S~s)n}dvYOtBPgN)vF4+A$4fk(q{MDQ=EXTa16tAuwOQ{57B}B4m#C%!G#% zstHpVkx*Y~Q-vAY$u_lB2=oIHXUr6e6Ij>je+>72C{wBmSqhpfdE}v?y3~fJG?FP3 z84XH(8n}9>_=7+eD&&tMLE@tIzGZpN)s5W`f!lw0Y6QHtbXG}y7&Kkyl{ADAVrt_U{TFyTFu}fQRb74o^p%JI literal 0 HcmV?d00001 diff --git a/target/classes/com/ruoyi/project/system/mapper/SysUserRoleMapper.class b/target/classes/com/ruoyi/project/system/mapper/SysUserRoleMapper.class new file mode 100644 index 0000000000000000000000000000000000000000..af5e112602253d34dbafb0b63fe2f52c2a213a56 GIT binary patch literal 829 zcmb7CO-lnY5S>)}<+s(=Zx5nSFGj&zMX(2)r@4cCMnUBx6cL2DA!y@b#;ebFb;32pC(Qy5sLf#y?tQ8R0sCxtU%4o=B zk9tb>8mBD+Yi(}(D!6BwMM%?zKp{>UD?gWUnpbo>Eob1-1NtW- z{v?d(hJ5lRH&QCgtWXj`C!W$h%7)DEb1KkLVJOQ0G+{Le4Bqh|{p6>Fd0iVz>Q zDLdM1IOO`wodki4KYff6*tCz|M7*5rn(n&J`T_w0IlM-I!aRXFn8$Y>UwJ$N04x=u zl$FX!X<-_*n3R@a8D&!93i4MmarlxAHWKtER8w?02PgnFj4c;Agl=Q(7_3jy K8m7C6;NBPUT-~Vv literal 0 HcmV?d00001 diff --git a/target/classes/com/ruoyi/project/system/service/ISysConfigService.class b/target/classes/com/ruoyi/project/system/service/ISysConfigService.class new file mode 100644 index 0000000000000000000000000000000000000000..b6d4169624d3ed920f6fde748b74f787476ba1ca GIT binary patch literal 1020 zcmb7D%Wl&^6und0rY|5ZX(?~mRLQ0zA$A28t%?*i2qX&@u$qo9$ppufj>nb!Yb^Kx zJ_>O>c5B+GQnRo|bLX6MALshd-(SCp=ncJU(Tf(n6x7Z*VeOA>`ojzli+(KVU3V~6 z=Sn6j9m;`ChX=iZwiD@cTNpWYb_{~dip=vw#&)7iDx)H+Bn}|?k^RWVpOsS+_Utl2 zD>~?x>YG;+o^T;(@BfUEcP8X)il{?^Rx6!A>eNDN>h(w+r|L9e=-=t~zFli;U@{-t zx_uYDY6Q7Ai7e@8Yq`peA|f+PmCqdu8h7Uy&2`bYb8z3If(?^q>}szB?a!6F4}Lz4 zmFLPRI1$78i)(2;jOuj1&zS%9a_#}Ll~@(Sp-SdR=@I6DsRP#52`hKyq3t;1y5t($ z0Ym?+P;l}~YR+;<%-0S)UW;t*G=DG&*gO5p6^z)O8_oOW{s?+~qv@DsiIX!-G}7$e zr_*xXmjwx0z^_BJzKC{d5%(qBmarH^2(@UX39W|E8m)(OgYJS`qwj%qAKwDC!P=y) nhJ`1D9?XV1wB4X~0_q{{hVxNTONsoFo)h!aR>z|QYZ*h4%{~OHs03uhPB<+{4@^y z0Dcr=#tvEPxKe`PU|Bo!&Fh=TkDuSZ1HcZn>+rM=Ee6Yx@P!laiBa5_QOuyz>N_g1 zbfQvejkG#KtrewFECS0_0hgg=rxD>h?fwxz;Z_n$-y-74I+Y_*D%m2g-k!jQPLB6yzKi+<;^WzamAcU};Kum3BdS*Tn$t)w<~(+|oQ zJ=bvR^KfYORX8l0%bFgecg8hbjNZ*#WB}C2Kh~t1QdC@%Z1@eVJ@aHTs8Nf_%|>3; zzS05rUwJqzT7)z&d6edKlpZdb$D<6}O$kbVN`Rkn{2fm?$Ao!(hMXNlLL+n{+{Xbv z2<4XqC-d1HOL5Ng9*?>0s1f=oN#YZOh72)oc46iVgOxUtk;`L26<`lsT&(<&v(vCu zq8;%@`WSqzH$F?Y&?nLnAOUR-$0Fe$=W1v{kIH1^$q&R>lIg9W^s0Lv8ym!N{L zD!!`NN&MCETY$@`S}a#JP;~{a8mpy=6*;_Cu3ACWbyzi4&56|-s&2qKrZrCECIE_V v!R?~x4%{t@Hl{`Q;J!g&N$BeVJT#)*{U&S~QI6W47CkB?fhLbLf}Z>W4DiwB literal 0 HcmV?d00001 diff --git a/target/classes/com/ruoyi/project/system/service/ISysDictDataService.class b/target/classes/com/ruoyi/project/system/service/ISysDictDataService.class new file mode 100644 index 0000000000000000000000000000000000000000..db251d1f0bdc6c325f4b6ac6303e07d872b11a6c GIT binary patch literal 860 zcmbtSO;5r=5S>N7iV6s*=r4ea>w$v_MiVtABsH4UaPhRX8(h+M$#zT9zvjUo;EyuS zmSPF&fp}=P`*vpDdo%O#`SuO~130Zfw*n^wS`qiTtM3GUOfy?Vn!t6>c4a_SEE7TJ zO3pBXMoFZ3Ks^~S5mG0KRyo7IJ!1<-V=a7Y3JeJ}AGm&%-V;+S;F_z5K-J4Q2w0Ao zhD^tb6A1sub-9(&E0osA9ouZeeF7J~r2(IXQ|f3X!s+lQlDS+D%1k{cb2MTZe`Dsy z_`lKBn`GprT}}IxRKTj~jTCCa YK@PQ2s12PQ!n&AvxB=n&QA&3F1lp2T$b24~_@@WiNq+XP*P)@Nx>Bmb+=VkC{! zF~g!?)L>Y0)i4w$(UKwjzgma2n7$`!t9Lu@1p%qxRP@lh=d53>Qx-cGMjWM9WQJWQ zmn;zBkh?~!a9E|H`vBO3IobMME5KhPKT7+4Hu7wU8KXgz@u{-|3=4L?Hq}(mw%bY1 zy~<|l)oA;)twv5Z&r+w&>NvlqSq4QuQcH=Un)=T+o3WX{8MIX&f<7QEa_IR#FrEM2rMu#%+L!0tjv*!m?LYRta+M0 z9S!j_uuRb0enj>P9#=t|^y>*)1TC_x0_0jlyL3@va&@p?x}IjPXV}Qj=f(M= YIA7vb3C*Kl-*>&i<{cCnTN(Ygx8yE+rvLx| literal 0 HcmV?d00001 diff --git a/target/classes/com/ruoyi/project/system/service/ISysMenuService.class b/target/classes/com/ruoyi/project/system/service/ISysMenuService.class new file mode 100644 index 0000000000000000000000000000000000000000..0d0e121b83ab962fa6a9487e28a33da0d7d046b5 GIT binary patch literal 1964 zcmcgtTXWJt6h4bw1Z~AqTkqO>1Fgn~`b5VE9GzhV?KIX0-$Js&wj^6O32hvInGgN| zf0W}nA&>}Zff@CIOU`$$-+pKF^Vjzu0I&^D3-F`>TMU+6-sh&*=8oSIuE$`r+Bz@> zhSoQnuGW%Hw_Y0>d{6W>@~JZ@x4GAo)~=xpn|oZj4D!CqRnuZn)J4}ZJYR7J?U~7T zTBfu$<;y^5M@k-GFPa;;9=A11+J8g9O&*|jyD*Ce)H}*ks9`}_1NC}N# z6uNz8Gvu4>CX^NRQtJ4kZ_)9hf=0@Vg_fec9Gw|De~Su^ofn~gGVkozfzXOc!(v*9K`>KbEX#}JhGF7HCe`2s=_PBXSKCe$ud!$knn{aTw!mEzK*8EFf zYhtW_WEmc(jCf~SNb>MuI9^H<7*iQH@eV25cf=PTyI7l>;*iPsj(^{wOE=GeK?dIq0Oc%$bCAWS96sfcfBY%nCj;k0 zs2D-T43y9u!xs>~2uo>^%LrXcLgjH%f-aw4ZzT!EQ5B3@g)4EbwQ<6Ags!GT*ATj% z4&6ZLCfq_AqOorSpy>|WoiuGsn>OKI?1iLY;QeXS19%vF4bwk@YHT7l8PxDQ1CM_L DPz)o@ literal 0 HcmV?d00001 diff --git a/target/classes/com/ruoyi/project/system/service/ISysNoticeService.class b/target/classes/com/ruoyi/project/system/service/ISysNoticeService.class new file mode 100644 index 0000000000000000000000000000000000000000..087dcf3e50f480ddf1ca2fa2a4b21c58dd9b2697 GIT binary patch literal 771 zcmb7C%TB^T6upDIMFmmx6JWz6#vSUyg$YT0Bwe~1+X)V7J7hW~>94Wy1Nm`ow-;F9*Ahh9UaXk zBgfMrH;GOaUl=_HfyZfVWypOU3KelTjdO_!Bp;=H*ZzwzB9vB|m{BuY>u2h}20ALX z>w*8*ALYv>Rs}qxV_-d3XVh}lED|;`lF@v)$I`o^H+yjJut7&fu{0YO8IAXvJq15m z_`=HELU`mOl}Yzj{{xF-qJDh*TgsOyV>)4nUZ z7^Pm|N!Fv5=o#Fht-w?GTD8$No(<|tZc~+9Bapjf@$LGS?%J=p&ZEbc=T! zL;5ZwaR2`rM}O|_1l!5#*PYrnfkNe*o{wYB@rn#dcox*HP{L3c+i*IxA)F2>^;b;^ zq?BHYW>Gh<)IAH@9&=ToT#1Aq=8sJx6COu^MRRBoX*aFW*0D2 rgpx)hF}fDwu0>eVsEBhp?plFWjam!2)?q`tBKpk{7sj{3$83K9Jd{C% literal 0 HcmV?d00001 diff --git a/target/classes/com/ruoyi/project/system/service/ISysRoleService.class b/target/classes/com/ruoyi/project/system/service/ISysRoleService.class new file mode 100644 index 0000000000000000000000000000000000000000..91c517093ca8c93a900b7f881ae1b679d68a8f2b GIT binary patch literal 1908 zcmbVMTXWJt82uJ06qJjF)?2-_Rt2q1Rr*AAri|kZQ>q;^^+BIvvcgu#VzQ}|zvhEK zz#rxKZ4%f>VhcWS*|TTQcfQN+uirm@0zeD)^6(-LEdphi4yh^L^C9gqR}g47dL|!f zj>mnbjU9f9yPE5}f{rwcj|^sOy6;MUyV*N6&J4{HY^ce@Z35LJDhAv-HXLI_1$A5k zSqHfU3OeiChVUHx|Kw;5!h^m#)pR8*LvLUr)84!BKFaR z?8Q8-D^JJN88O#o+*al`&JCwiS>?TpIQy{#b>T2fQI?~mLLUr=1Xda;Eqs{(7t3!= z1(8FcGHaddT5{vIgT6@9u?g+?vVAzA!Q92$H}A+2$jNb(GBtBR&98Eh-{P#E*zB8! zqq)7nXnw>BPDKu>Khda8L98uA1Dlg&b{}O2?Pq zL3&Acmq3x($PBLgVy5I-Z)_QY$^dxL)j}6|t?{Cl3{MPVe#;g7kJKBV z61~Joc_&B$36aJDD$5iJ?9SDnNjch0R9lueIdmO4n8;y(twfp99Z8L@g$F8O1!EFL zoqLW+-!NI1dN+C7S#-^;yn_}zg*ISQL81KpB(#-;9wecMN%=?6Q1Z>d{bP8d bpzQ#93Of;0$N*XeEr54ZVB|du`tke^xqJJa literal 0 HcmV?d00001 diff --git a/target/classes/com/ruoyi/project/system/service/ISysUserOnlineService.class b/target/classes/com/ruoyi/project/system/service/ISysUserOnlineService.class new file mode 100644 index 0000000000000000000000000000000000000000..e3ed3a09df5fd31e56753afff2a8079c837412c6 GIT binary patch literal 720 zcmchVzfQw25XSG?w55eYsepkeC<|T?OE(6jN`WE;7B-W(#U-&T+i8`B2V>v?cqqiV ze@H8JL@aUc?&q`b`~3C&@d*G|&}~4c0mld@R-__!LzPM;Zi>N_Cy7Bwd*Ou7I7_)2 zv&b2##{DqX88f*qBpVr?iMcj!%!)WS(iJS!W2v63F#U@SaBB2~+Ote6>9k>q&bU-8 zDy&DBr}hy#cf!5uWo9X6nx&`|yx?Nq+W$6Igt5OYp L6ABLLTY;k=UGUl! literal 0 HcmV?d00001 diff --git a/target/classes/com/ruoyi/project/system/service/ISysUserService.class b/target/classes/com/ruoyi/project/system/service/ISysUserService.class new file mode 100644 index 0000000000000000000000000000000000000000..5789e94100fb3d29fde6c3542d8bc67594c3b501 GIT binary patch literal 2087 zcmbVN+foxj5bX(Y)N2rmd!Vj56a?8cP0{8$iZ)LSm= zSv+BV--IP6_P&e~y=DuI@FKQ`PLYgMhh?pq{3=NPX0I(QSy8tuy9oD8%H>{%Ob3`niW8+i>_rq#6)W3M@!QrFwJQPC!KOxD?%lahjXP5 z6@$B-h%SNUT>5XBNjj>h$gyQj51-1MfwKj^6*SEoQ=79rKR{X;x9tODi-oLaQ>A^) zDIL=Kk!|;A)hgU%~RJ9swe%dV{te=W&wQq zF1Pz60Rjd5SpZlXCol%%c$&b|1WpV7DdJav5?)Q6epNpG>KsgCf)sED0D&qnYfN*> zG!F~Lq`j(nQw TTlhW3PdE4p`ai`}0iOK>t)d?7 literal 0 HcmV?d00001 diff --git a/target/classes/com/ruoyi/project/system/service/impl/SysConfigServiceImpl.class b/target/classes/com/ruoyi/project/system/service/impl/SysConfigServiceImpl.class new file mode 100644 index 0000000000000000000000000000000000000000..1c8821ad38bb8837df5f1ce15e90a9ce2a9865e9 GIT binary patch literal 6034 zcmbtY33wFc8GdK8$!s=53?W2xIBy^c`3pbrChnP3?IYC<>@LRxH^na;F>V5#dTp!#3zOB zQ}T4ZJl!C>pVsl25I!5ipfKDho1YVgo5J`!ZWeL3$mLdHxJ@p%%j+FNcc<`wL0<0? z-aB;c4B?AH^CcZ$4&f_0c7^d(+#N&=8M@BU)Y-DDH=qC91zoRG8Iq}|>`Xi(1cKS>^p2IRL@!?O%<-2r zlzK1}Q)MB7GLK-r8A~x%r3uUTk%kGKt{L6JbX6hK@dWExLzR!l?02J^%;j;3DLud( zI7dTybky<)x3}3z28(V#V&aeSvTsw$wPO)sr`t_&k#=gl_Y;Dmk zo=Vojy10FTr2F_{$OLD@>2Wla^-{jHOI26lPA8R!S}Sbnb~V|qo!6@7N@Z%OE7+ph z@)MTh(#sK_rNzKF=6INyMYE&~>N47?=z4&Y@29XQLtD|pqwYeH|M zN5kwf7^2e5&A1y`$0B4P)eC`W8x8yxFR;&!VX?MHt$s@4ywezBYL03}#ZUqn_0z6PjJn!0+&T2@pN^ z2mFyqZQxIMotbUm&tl_W@K=U<44SL~Dsl2GFzCngxy!RbbAy2$_?vj*?*;}W2mXQQ zS z=luxpc^Ek$ak$7>7|( zeGvr@4`Dmm;ke8D`duD5Cg*$Ii)V|>e0tHJiK-bEB$JV)WObxsu~DKj0Oa@}_Nzkh zk0Vue1#y*&N@>3tKg}+HEpIktR1k)4Cw+TLTxXf%5X}hY3~3&>Q1KK^FHg*6ufIt1 zyS!~5v5_@Fc|#2fWsxLqz~O%G#TTU<{(TX}6E3V-E355Il1fp5|apa0umtn40CR z38ryW8WrlkyRuRs7b(aM2=n=Lf?0~A8I&4^;5hnm7X3J%6wM0wDnf2o=A7tZ%9^tl z8*nypS1_C#`4k}c0%cec`DfxBni#~n)G-l(gQ(H*-a}ZZ<9+;}v3vt}HJpd@d6m%j zP>KXD3;;k5Z@Z_bZfw zBLwXrDu{#Hc(kJ=(RrG$vrPQhIE*RzarKhU&J$ghbwHUiR?FD3hO(mk4WOIyd1~a7 z$lpOdl0>z>{F(q3PrDh_UWn3|%J?#j5~OLP?MtByTX7V&)1N!?lBzZfqSgmd#`yJO z6LpHEgnbY*M?ll$sOm#{jDW;_s}-a)jv2?{O;ZM)OKbC<3E@Nme$244tCR~MuxEd|Eh9~Ijlqn4!hO8;8eN)QWCpN44Sm+y7 zq>M5ojhdJ}2XWje)3zYyoAx>ti|U@n_NIrI6rCKL9GreT&S{z)oZnpBP*a@7jOLQc znKdP898Wt3F>NnuH&&hy+=p2ki)49f=MZM^LER%_a82H`qtLk()z_r@hbZRg>; zq``RkIFs20+=MFJOoMMhJ#M2JchJ~7ncX)rAMVN;J&n~S4WNY7t5j2vYVXN9q6dA5 zGlwLSY$9(Wq{Gp)LpW7OKkYxvYzXMMfUBhNn@ArYHTeePRnji)$9S2T;r9!b+-=P9 z2941kMx%y79M6X)4UeLzLs>b;3psC+k~TK840>164kTF!y%g1C*M<;H`X+D;hqO)z_ll zfzpOF=J7XgfOtwCrz4(Vq@PquyeU-bE7AC=^ASG_?;)KOSSz6(GlcnrD6PVRoe0Z& z8Yd0mPvaPV5!d4xHm+ys{pYf2 zMRnN}@)ArVZJkQ606C`OLMEM8OEjlqMeq>ny^P8vk@T>De1Q9QoWZ*az+Rlf0eEU< pa~drK-IB(l-0<_7 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 AE&u=k literal 0 HcmV?d00001 diff --git a/target/classes/com/ruoyi/project/system/service/impl/SysDictDataServiceImpl.class b/target/classes/com/ruoyi/project/system/service/impl/SysDictDataServiceImpl.class new file mode 100644 index 0000000000000000000000000000000000000000..cb26e62f7e80a48ac2aa214fbbaa4953bc914458 GIT binary patch literal 2844 zcmcIm%TgOx5It8&42S_^3?z!d#)&cFIbeuQ07G~fTaLhv1+pEl25Bq?q#4y{WJ>$| zMzY8zn=F#5loPV>CcAt=J|&BklXGWALLgKwDwPHI(bKp4boc2Nf4uwcEr269%67%nijI9?WL&LODY-^LD|jkes#4lG^=MHb_OxudX2D3CC37!ttXh`s z>aJ;9C4u>*?c}sl(J`&uD@QLFN49gQ?HRgN(q8Eq*LEtJUVEUemR>19EPHzG|NjszG)4F5Idms$E`zGBlHxH;}IIqo& zp_zqZzCNs!FHcZR5IrR$&n#49PMRfGU?tw%Y}PL5rd4lnaWr|Lzt**~Yvwg+fM(mO znHQM6kS&4kEyLZnvrlxHP1kT3WqZoZS-M+xm<{W{VOzQonq`J^=_Er*eb30V=5asZ zyl&;Rlq*ZW*eD6IQm+)`Wv5c~%Ian9^!Ij|x3r!wONA51lEZ72MAj?K$+M(Q&!ZQL zD1hK|t-B7jXG`EpmV9uHDj%d%7N9r&@~o;y*(`CfJk?@TGcZ}t1@E(T$j_G>>=65IjE&e zAeyqvPR7_YW#?Y4np_Y6U>w0FNoEbjH@R>eX98Kc}e+aw4>BPT~qz3mM z{&==>zrtPNPS$Yk=S|}-xJSl*f%u7s5I*24mBFVUa#ei=#PAVs3A)qtn@MaW?i z1pE#6Fl8eFA38#~H}EZD-nAOm_BcD6qP_IO7^BW0 z^C-bfGK|z9k9eW6TN&m&KBMz0cwc{(`Vu_J{5OVcWtyZ&$m!PvwR$nsIr`rx>I0%Y VB@d*3kEWe*u$drOW^T literal 0 HcmV?d00001 diff --git a/target/classes/com/ruoyi/project/system/service/impl/SysDictTypeServiceImpl.class b/target/classes/com/ruoyi/project/system/service/impl/SysDictTypeServiceImpl.class new file mode 100644 index 0000000000000000000000000000000000000000..874501f7b0b8f3ac11109751bc1bb11eaae17207 GIT binary patch literal 7302 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 literal 0 HcmV?d00001 diff --git a/target/classes/com/ruoyi/project/system/service/impl/SysMenuServiceImpl.class b/target/classes/com/ruoyi/project/system/service/impl/SysMenuServiceImpl.class new file mode 100644 index 0000000000000000000000000000000000000000..db76cb950d1bbaa409a691d8b4130d5622d45237 GIT binary patch literal 14432 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 literal 0 HcmV?d00001 diff --git a/target/classes/com/ruoyi/project/system/service/impl/SysNoticeServiceImpl.class b/target/classes/com/ruoyi/project/system/service/impl/SysNoticeServiceImpl.class new file mode 100644 index 0000000000000000000000000000000000000000..48df4740ebf1622086709071eb0e09e67f2864ab GIT binary patch literal 1893 zcmb7ETTc@~6#k}MO3Nx&QM}+?+oFshA_8i_#H2|@Ip23?e*gLT3&0{)(@0}3g%tQO9%0^^1#2E#vuMo| zYnGCDn#6Jv&lnt6`>HHAMWZ1N!+fEv>)Zsosd&TaJz4g-*Ytc@=k-W-cAKI9m99vJk%DsNR!}cV^Ip(t4C4h|7PW0*lwG$s`~6)- z$FIe=<4LnmjdN9R)H+QS!`(b>QVFA;tPulYzZ=bbh2eRouqXBfuL-xx3)-!&X5-JO z=(;xP{ve?pSjd_Z-|uhG8XkUWnL}uL1GZ z8TdCP`ywzb{1-3Z-6I2!P3rx$;3mCfX)p}am$03ZKTj{+Ao&sUN%BMj*>PHN@{IgI z?mO65`bl7dtPKW*O_FuO09?Qnt(YZ^qHOX@@(J?8xo=3!{y<*>Uubz8Vx|Q#6^6AT zC;`*BOi))SdVr)Q$E*(M>xtVR#m&Xw&ft0ncZP6p(3c=mn3b6RdeuFUz~@$2r}d@N z;xjunF}QO)Y(jdm*CZo7pF}OW=rrD0c9g5<9h{4Sw*Wo$aB5$^39~=$aL*Ore CNVa+a literal 0 HcmV?d00001 diff --git a/target/classes/com/ruoyi/project/system/service/impl/SysPostServiceImpl.class b/target/classes/com/ruoyi/project/system/service/impl/SysPostServiceImpl.class new file mode 100644 index 0000000000000000000000000000000000000000..a7a5e45542b8e8a446929cac94fba7c465a001e6 GIT binary patch literal 3909 zcmb_e=~EMD82@b&SPAP5D2P3*)^NFaRt2F*wPFLP5n8p^l5Aih*^Rp!G(BxSY7bkd z$4vRs>2#d&i!-&I4njNX$IkTcs5A9H=$C4rXLmzFjtWj;=G}eYJkRgBfA9YB;MY3< zcHoO38Zcair|@z$Ucm`oPV#b!msf*$4X+3B2HvcA==}wuV1$<_{}^L2l_laLM)_on zPc%jxuf_xtyiEpady1EY2z?0*5vd?f!wh0AES9i&N%Jx(;*5y10)i>Svip@(N;L(@ z;i!?6&9srx5utIfV`Z^~ch>ttHiA&C*5^YLBiPwqk3B zZVA{OHq24kN|~BI8aI`sdd4s(5&^#rAsqEBr&F&K54>b)72=p2;RlxGFrmF+#SIB7rtHMT9NenBd#(VED zV2^1O#oqFk$=swCRb?%iN@Oz=anJhbh~u!-1SOFp32T-upd(ZsE@mVZP0s>#G=;~N zNkvZET0&-Jf(vl51T-xeL%@oD)gCiqM-(oAt(p{3RYV)r6+3NGz zbD8K#Bq-&f67lUTI$WHQymar+^u?TXJ4s9^`luW>^ij%c*%mLzKeZ31n_*VCrO2TO zsLRE}#o3?X=406np+eyjfmU*Vv-FNfN_Q5*aW+>+$JFQqx77fZbx7Avr>U5&izwzv z>LyK(8&sKytwbk?0-U}VkrDA$z6Bndtc8_PgXTRuXKs#H7l+VCogbslw^er06}-od*f z-jnb?&QY=@e1H!noW}(T7ja2K4?dD`8RrCS&UGriB@JDU60h2dZp&POUT^MJVdosG4Gq&N_Y|dmx};)7FjFy<0!uyL{>X#Vf6U&RqHH z{Ox;}zrR0qjm981OM@ePIf(dF!e_X`fqyRHC4?m$!BGjvaFqBzXSv`qA@Bc-I0dvn zQnhb>xv1fC&hennOJ9Nc`GYQ?t*o_Dw;mDbsOoed5AYn1<&(pP93Z`M<(S1SIb!o& zIK=P7E}AuvW-6@BA0Rie9ZfW!RSAk?SV{0`(*<^zMq`q9gK-+q&|!~O^6gf8jxj0l zeC3O{wuw9T<(uax=AxXJ)NGW)c$&VB8X(ZqNB=ab^uCe4(JJY=iJm^5pn@}r_B7bE zHj{)*Pl28l&9@-@^cU4n!TDk(dX;Z(FgAptduxKpXz7WW}9yNE}0N?A#98vF#w^&mtfc!MEpm81&#> zq}`-RMCBnBdRkHJX=fQvJJ3l`o~Iq;4fPzl%4DFj823;a-0pnbZYNsK!1ILPgZ-Z0 zKO#W?KyS-5#2GA^LRAf_t|Q3%X;jZZ>UWYqjis|#=EFeS43iO{kp)7-c z#lgToXcBQi#KCNyxS|4a_+!Meau%x=6Gxol7$vX9D2{Q8Bk}*oLDx@-K3Yv3U*iMk zHNWG}mZ^7%KB60~1TIffZ87eJh2zrA<>B6iNdLfKOMmN~F8{7TZD9Qm*wb1Y*tWN# zxxQi=b$ctF=bG=(_#4)qSXCdmjfNBc`bui4$PCuqLgU>F0pa??-@Pec@A~HUQUg&1 zaFwXyOElsuVx4bjIIdD}d^=Z%Mq(zO_ezp#cDjiBZ*Z>12GNfJY8S7c9$<-xK|$F0 yH}$DEcm1Rw@MT;kUr!Y0>spz76)}&%_j?)KCyQ}!E)>^?2zl+JLCaF|5b!SpvZu=c literal 0 HcmV?d00001 diff --git a/target/classes/com/ruoyi/project/system/service/impl/SysRoleServiceImpl.class b/target/classes/com/ruoyi/project/system/service/impl/SysRoleServiceImpl.class new file mode 100644 index 0000000000000000000000000000000000000000..bf1c589257021fbfdcbfbfac9b882597710c55b0 GIT binary patch literal 9557 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 literal 0 HcmV?d00001 diff --git a/target/classes/com/ruoyi/project/system/service/impl/SysUserOnlineServiceImpl.class b/target/classes/com/ruoyi/project/system/service/impl/SysUserOnlineServiceImpl.class new file mode 100644 index 0000000000000000000000000000000000000000..67d103d5e4fee130efa7703fd67ac6a9092cc08f GIT binary patch literal 2784 zcmcgtZC4vb6n-WlgoI6bM=LGXs%;>FMO*PD6)DteY*sx9~QqN_S}$JHyUquU2YQCE#h)l~C!XPp*SER(F~ zgPkmGWCVsC-K1l|)z)a@$v;$2wHLI-VUE8qAnKoxTwpP&(rM*vniZ$DC z>uOGx4U4^GCfXF6QB@>eR@!PwRy4!v42YuWHX42h&$=aUYGhU**=F~`00a_wS1ax> zY1IZ_{Y$#LD@&^y$L;DiaW~p|agKbMHU)avTL1?;b+Xou6!_^pYW`2MM$9JF1<7Ft zxf9sDG(rC%LkX&@c4sqf$+%RK=}8I1^Ri|a^@j#m_Vt&Q9ddNqOw&`PWv-k+|}$n?dVof&$QX@SfAJw=%p~Y zOfCN@cN@-f%`^o@+KmOIWLQ1gP!~v1%&qvE72P1dvujbm>Y;N5xvyJP(oO?rOE36o zv`y~ry`;op1#KlpvnE&lWXW%x+~&EsDSx#6!cv^H7HoM)CIXMAlL+ms5M2>E#8PS6 zE6~fK-=iZHIZ17tbeawVouPJed=(D)_9fW}NPM7Ismv}(A}doEG0Ki~g9Kg1Ti}B; z%qXCeiEYmC7X9R75V%JFe1s&ud>Y@RnMzXd>g02XUkHS7gT`)uv5yV{N}zz7c$;Pd z@8Dh1;=|h{!z71NX%C?jgpUxLB-nKpj4$v_+c_TR1Fi8e*3%7kY zLr7wR%uf2}#Q;(SVjvdF68BTDa6fhGewru?ZhuI!i`K_e zQ_b5CrwKMAA$SfEa~Q=uC1SykWHPXDO2qgn5#!*PLTulsB;2Irnomh0>{HT;i~4pW zu0Kg~R;sBJbpMP*%0raU5hCLFH}pJxj@VL~7ULdzSw=i4EJGd=EU$RzW7+RvfaRcv zA(q1)Mo2_GjGo}NqhOO~9$xowHQh@2EE42@nfP9%)UHwK<*9ZG6!<#sU;`OE#v-2J z5q&**Y+{SP9*aJmR%ujE8L9fW7i41{@6+j^G~x&kh`;#Xy6e*sp^uMThVX$u&f-Io JT*My%{{XT|&-wrW literal 0 HcmV?d00001 diff --git a/target/classes/com/ruoyi/project/system/service/impl/SysUserServiceImpl.class b/target/classes/com/ruoyi/project/system/service/impl/SysUserServiceImpl.class new file mode 100644 index 0000000000000000000000000000000000000000..e0d8cfb4b526cea37a6bd5e910c14706c70ab793 GIT binary patch literal 13212 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 literal 0 HcmV?d00001 diff --git a/target/classes/com/ruoyi/project/tool/gen/domain/GenTable.class b/target/classes/com/ruoyi/project/tool/gen/domain/GenTable.class new file mode 100644 index 0000000000000000000000000000000000000000..0b65bd0987e1b93f9b66ca406f564af4dde7fe20 GIT binary patch literal 8307 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 literal 0 HcmV?d00001 diff --git a/target/classes/com/ruoyi/project/tool/gen/mapper/GenTableColumnMapper.class b/target/classes/com/ruoyi/project/tool/gen/mapper/GenTableColumnMapper.class new file mode 100644 index 0000000000000000000000000000000000000000..940fa66ee21daced2c4d3cd625d469e43dfb9d73 GIT binary patch literal 981 zcmbVLO;5r=5S>*%1Qk$<3VP9afQx%kFBlJKBqX3F7-QmTp_{s-?UL=*gumv&AK;HN zPFq3&i60!Art{vseY5lW@%i=+0H<(}hDsXt2~<38^S-|BJ+Pk5ucYleuGbjdGKUj5 zuC@p4l~J3y7VT;!T&rG-O1>60Z3|D=2^2b9zew|*DeTf*c?2>#kc(6@U17OQ`wA7G z{-Ai#?n{R%UycMFDmg%VR7+`7i@Vg64iheIa(Bvb{SsXqMw84Ufy2fqq_#`~N7Ki* z<>GPDq4$9P_$z%$uZD0vuJo)|0%z6#k6vpL$l=XQT?YZfADT?_dHs@!%bqXewu<99 zomgNOAp(2h!R0UPWg1oh literal 0 HcmV?d00001 diff --git a/target/classes/com/ruoyi/project/tool/gen/mapper/GenTableMapper.class b/target/classes/com/ruoyi/project/tool/gen/mapper/GenTableMapper.class new file mode 100644 index 0000000000000000000000000000000000000000..2214838e8dc93278bae609efce23a80dccbd2a28 GIT binary patch literal 1259 zcmbtU$xa(V5Pi*NY!Wsb0we^oAejrzfg1=Bgg{6piDYg$wLLAEF5nBpVkEU}x-%JGiiVao6*-2(YOQJ7 z+{UJ>c*mM+^5D)G&08|$foTgB@;+sMyLK&Z1&^K5JmK#%%wI@%WrCl=inespMhw$L z^(h#Nb=3-mi>+kP|CQ<>Po0NU*h1RwN#j|!D?`|E_dC^1MQ>GqrOjy(ws_rH6}Cq8 zTXKnbJyQK|NdH=SU-fTV)9|Vzv_BdGbLV=s6vy53z%PZLvzMY~ybw9Igkelkl*-F} zOO<9gs*ch*SNP&!1`M+*jHDfGH3QGLX6FaSFDldke%0ldM zl|0Tgt)v*ewO<<5W8xd#&@4-J6WgXdRhq7%+)sNUIvr_u65$zk|DZ?PI}VOA<G zdGH7Lql~kMhF+*u9?s0}oSAQDzM0)$zkmD$@BmLoFpS(VhA=;h1>ExDwik;d;JD*g zcT>2R!u=E;FicdXek-;Dxo(0e)ElB!la}Fev1~$aBhyg4X3efFJMN4LIJxsMcH%F3 zMCKXJzlpR{q1;q;MYyY48z-DHy6$0(t@3)!DqY>SB9!~azUNy~=sMpPWoK-Ii^BlE z8acDCtgI01;Ih)ntuQ3BxlM+FbyJZHV@0Lq%P8EEHm-tUylBcI*c4Xzs+z_Dx1;Fz zm3~##rQK6y*;Q8Qz*-1v0VQ#*E(4;Xv+|;{$ zL{0_VI}=dK`Ru&=scvsFZAiCcDzAlS$Vp3?50_L`3l~|cdGntKEcdIkVnU&GSDE=0 zN@_xBBB84~hKJeX!{rJLnawI zKB1)Yf5+PhZ~kN$DVfNY<#QDf!PA|GwT-}{?<_;M=NQ+iRnoXdP4Z?)K8-P)N@Eo1 z6dtBAg|lf~#&jCjFrC6u8jtWejTt;)SnhM-s4M+PlV+whbFa4ib!B++ABcsXr7|q_ ziS2X!Y5H4%vW~a!U@{YCCO}+q->r%p zC@3n8t^3jtL~FG!t<}~pT3eSc8r1rAwToI?zW=%Ry_q-3fX4Fsap%7K?mhS1v;5Dw z_rARJ-zUCHL{n9#pE`MZ0qx{t3b~qV{HPr(wd16iA;s}h%=FVRo>j=Rd5+ZPO4~fC z%`c==ctIhbz_n7GXz|yC+QLGv<9g|}NNS6vc9O+QWJrUb8~J2uSt{?A$>S+fTW;|R zi%+%qG(U~y=|29t{GRTo(R_x~&h*n54qCj@Plxh!VYNw$Wu|^)( zS{$*s)lY}>bc>@tj`_Kb*ZDcl328jDkQz8?al4<6ksj;)RKpvDnpY-u0 zA3x>e?~1&h_VM@R_Zc5Q>!%X_zC1i9#q%=i1>x;QdHjJq{!nU={V;w>9)INH$v!TZ z=a;2;MT#Fw@e?UtmExy9{+aasxsQM0=hyi40*LLGK7K=H`<0OYwP@o_Dc+K{x21ST zir+}_TPc1g#qXtfSBm$f=#gTt6z@y%ffOG~@dqhBviSe}{73$i#ecT=FHAX+SPN6> z!dSc|kch0Bx+btN*3uG+*Dw{egrbeXm61?gu&pf=XF6hGQ>--*Z;y3^18woxnov_R zkc`D50dx$s>P~?L8Dl`DY)N}G8Ey?N4JX2&H7gp8C4 za5S8p0g+6ayp$<-POLe^G;m=!8mez^T^WjNS!h``1tUv?@vz9oY0OQo4(kOSKthR7 ze0{hnloC;cd9H13i(vZFL?{A*y7HUdQQOQkW75Jk!S%sFBp7XhVxlcIlMl4O=2&Ym z98FCCswxaB#l49z`CRcaRrZf!aa}06I@Y{6DEdl<;#hSr))$5oNzM8JWYL?`$%e)D zWH=I#@nF56A>0xTCfnoCLS#~ZnCwmL*!~QjfvHQ3F6OS(+BNGL-luJLM?FM<^-r91 zsz<^NNik369?_6Loas<~7dUy@F zAZ5r*dAbfs!cB*Gh|k;D6w(S1SJr)uANgX4CE^22Ll?%pNZQfnE^=-!ru$=rj{e1s zXZ#OjYG%|RR3a5Yl7=))JoA5}<`IpnHpsoOEgo7Q4s9^OW}H{K4&0|v1Qv=#sRYb+ zsG$~)1q`ia@i3qmZ*!7uFoV(Bc7tufrq!Xq`cNd+6i#*o8bgRQuq1TzC7n9lMC(R^ zjRn6AViE~@y=S$WV$o!1BXE6UR@zIQN#>Ytn4-X)0w#1+CcR_(%A#+JC~$&joLEnm zfug3rA&s>7W2OaPB)=>Kvd?Tc$2LSGv0$@N%up|3SAD0>X0l`L$+q?+nnJ-=rZI*Z z^fJ368JZQ32RjydI*5YK3PZq7KkZX!2&@RV_3AeOei(`ekpdYGvidy?D7P(`T%Gbf zOkBGt?RiM{E&e-la40v?5p7yMcO_WX3QxIrk>^n>zo^-Sy;P6Vj0Pus`GIh$9w&SB z1RB|-{N-~2j{^_O0L--9EqW2V6l_qKre@KMhnsA|6}+?82P0vLQ}dd`FzSLfNoNv^ zR<2ih%u1UnAM+8o6SX7S=$kAYO|A6ZzLdnHz4Y%g0q1{Lh-1c@U=JT z!!eTTVf|Or@3)qEU@YryLojZW?*rtfHE$}zQ+T%^3`QZpc*KkN&Yt)m(PZ7{$ znv^whNC2Nhu_R(#z!_3w(_=y{udNH5v^ zFaE^hPi_8;|84W;xeL29tAhA0ckkq$1}#8ObjRFR$o zwN~U;Avc3?CFbcpEKS3h>FoA!q**qp1A88NvgeYWJ@-8F!Q(ss{Nz?!4OPQzHC)-Y zDnmh!u+3z~J2o3#8)`lwm{<+7vH4(VfXCA-mKtHJk@Tpg4zkrKHQH8Vgv3}W#;Nfz z*xoE4!P8B>qB7ILeU^r3nn(tt$pGS|t{_O}o!mITYPzKkw$%hR5x8xuL)4+RnxrN( zjdCexjVtc^{lyb3T9w;e$fGP(VXI0!s46wZRsjV!8=79ooYld2Lug%lDB1)g&qq!< zWj(@LL9i`6WlciY?Qpa?v~kM%_KPVX_xDrt4a&8=FVisA% zLjlPx0x~<0@@wIwrKZ~IC^gMBmdv<@1ZQveqkHezy!YN6d$&F9s`}~=cHG-@-8EY0 zN2}@3(Ck<&nLx_kX7;R!f%)o~VpTmbk8UVdHMTld9cQT-wmM#ZXWD9(nr*2$wwkNv zVOyXXck#ql^VI@)LeF(q?|oqB-kToYd(DHXiF>ZS19QK>XY=0e7r)niP0s_j_S}B2 ztxiz2wmMM(MvBt3B#$Xp3kO=Nu2|LEYLQxOtCQ4{V$}fl8jID*wpyx|+3FOv+*T{p z0$ZJ`PII;7)$qJ%GTvdUudCB-b%r|ARzbB=l-p#hW&n!{3A%iXzHO;hwrWwx5)R2C ze8&)hXC7P36@A(&tQu^!M&aD>AGV69RtSOKW*WKo&g{NarcVvfwdZNNt!FG63Z;XNG6R_oOUh??HE)kbme z4ta4FFjk!{#W_-(E5&&zxI$*+fJor815$1SzVRu_njE~KBzek=|>TqNW! z7IK%E$*OI2sk+Qom#Zsmb*13-Rnp~Zp?;0ju9ez1q;{RWy&;d3&>< z*tE1h`<%ytt^J)gS?U&B-Kw@^V3QiS|075xs z?7c1NwRK=RC|fUg^TIS%7(7sVcHr>Wn(dqt5Qi)Z2^f+<9RFmTf=DvKG-Cg=0^^b~ z$qr2e=<%gA@u@D4mCxlS^ZgGO_tmn`Dal$ z3di*v;Ec#Pb~KAjn3G4~p4ZSE_OqPo4HPEL(D~s|1e=Ipe^_-?(T{|>Mz&akGnZnh zAgK@|iGqi!{WI^`emHp@j909^E|f?FTacRgk}=oY4g^?QTQ0!VLJDC3$61mlB8>?r z-8W$7i&XA*b@X0@eS&PnobwKSJahmOz0N3cIGhdR+&LVgSTjySlZ|bWIoPPS;4%bB ziD_6CTG@!L1hQRucFuCl`P#HBV$EV}rb)|3#$m3p*;s1N~9J3KU*6D7%%B}sJr1__~a<|U?eo<3X`!^jt+#KG+B;`2KT<)G59kx ziv;_5!81N>b%wcs=rj>9-r=xVC_==|P-0dT9A+Gucr*SQ@9A^K3keouvdclzdS{xF z4RQ==W^Rm`d>^2%l>&)LLl)TcsK|vdNIgG+6jYAXM!LaC))g?VvO2u1@vE)swl9vi zDK*dk3b269m|N8zH8%tEoeHdbWI8eG^IC#-Gmds}IGfSYStJlsZUkd; z#g>ZUafXzUHZ&PdkXU)A??b~Ys+7#FaAIC- zo2<}vGR>Z|INYPuh;ij0p-6l1mtR1GF$CC4GG_2^bcRa$@Yw4H#RY|>_AtyDZ_vv z0Dm}9A4@uRI3!!*2p=2doxm`Mlu=@yAeTXGoUf*142(~1di#|a^&O5}1mR24a=oD! z#50|=7dQvl`jt+r@-)aY9hZ$07iu$tSl=l{&F*S5BWd2MNP7Zy;@3gVVW?P;-}`Y0 z@F*0I%NDPEzg*}j+_)OIta9+JSWxlIgp6}uKYy)h%MB+MuQkd~pYcxxw4}prpB;CvB+n$gU^5@a z*DhD?IhaWNtJcd{3Mj?UTsD}d^lL6LaSTuGB@48@n_1xefL`yMNHCFbb|7A-uCdg1Tiv1V zw)rqV+~z49z-3rkVd3cd*xFEFp}qmafojnCLJRW8hd}O=rFLSi3uCdh?Fi_0Z4|ed zdPezB=4G$ZIP}cX>?E%#D@%x}|!#z!}w&8fjO%lq- z&?A6f#&2HvF5*Y@kjGI90ir7U4ocf}pf1{lCzhKhOeTX*!EX+JOUoZ8Rk53L6xEei zc2RES!)R3`l!|nZnN&!#sF-GJiet?vha$$)6SN1E3(2A<=_!nq&ujA-%`zb_xG+E_ zJRYwUehZxm^Awin&SZG5*E0+?-BL3YYP#&weG-G7(F30aecYYxhkkxv`YZZRAJ=mG zp>Or2ziL19U!WJksw0;dQvCk_jkxi{az#E<@g$1#i+pyIkNb#cohENo>UYFrB=IAR ze3@QxIDHiq^FgVuqKgW;$zN9iy}Dwz70np6osPu20lTRPUq9uRMA=yJ?W3Wu5!p5&O!~RxzG@sGo=Od>TNTXgFO!2hl}z99=@SbSW*Q%Qd4@ zDGzNn{g{4&8rVr`w9e6}kLJ;<^i!}qoQ|TO(a$l%FVJ%!ssGZU7QMz6z5W?Za78Do zcU|O{s58Amzj7Ac04ic$!yNM(47OznFzq3zrrpCB^^Gvtn`kKA>`~dU6obR)*YqaH zJ1YBipLyS+w=tWjZHY7QAZOm8infxK+ex`O_v5t@nKADjp1B64<{CurKm@E8o=2)= zW@_4ZFrMi*^jpf6srVWE`q6e5)&#}XS2=2nqoTDoOmx{r!>jYkax1&3Ojr_?j!-l$ ze@OmznkNzwrH)ioZIxNZ0DNUWhtfo;IR;QrR#2APO$X^A1-ogKwu4`G?z^g!#@Fwm zG0XSR*yX!uT}e zS{qb>0#(ASE{^@Rmr?(XJ~l?p>+mZY-?WDgg{UXhIYtJst}lnkCreS@O%+w$R9T&i zhbsJ^(oF%F0Msn*onMykXtzdcTDyl;TYKnmP&}gAS7wz??WUuuwZ6-IT{Nw-%#z=u zchmG7@%?9XNHBV=Mo=Kg-v^lug2N7{*Ju=0Jr=Hb2z=&9dJ8V`8#)zM+YC*$LPuvq z(&s@#t`1K{@4wSOK%<$4(Ld>5(B=}FOrL~A3VV!^1(kM5yQtKV7?HI`<4F`8V%vl0tE?_@qbr^dOlQZu3ZNm_M z4Dw{TuNv2Ai9W$(p5Z+w(~)>gUje{9!byeXnKyvXo;_au&$y(JOH*WS#hsx)$6d+4pd1YDd6SSO=R<2NFrYXyR0`8BN(=xjw-gMK~x6nYDQew~PPFyyc zJ_4x%aXBy<|WW>BYm5fYK<;2OHS$MzLb9M zbo5h9x4|;rg^(&}6AuQCmn?HGVn32-}Y|4yp>x549IkOMgR7j-% z?zFrdiR(l1?xJC3xkK`gsJ6<>EPxb2}cmP1J0bmlG!cF{^5>M8}Kn#z3L z)C`9S;s2^mnpo|Blv>;@M*s`K)d8$B|882HL!J9B5X=hEwRC+7vzE|c2zebWIRQ&< zhgEF=W_93i`Obk=pGzn5Cj6<_1#}u;NFlzI*7Id_0bfp+@)dL?UrE>UH|To4nl|&b z^cY`9U3`5CyEfBUjaYtapfjB)dKxWrqUebfPDRmODYfl#JOU!t%2LRE?{}i;Xr%on zidN9oo+v8VrCXpo6FY&(`>;vTu~RVVpx*Fx8u=Yu%Y4@x!N$1=hC~R_j0>Q921LK& z*h2vBCLwln=^AWOAo8_a=wx>*gJ>41uPSZrqG&h8T&!vntg0$qm)ROOt%=Omq_lP- zEVZYoSD?1uLvDj~LEA=8TZgozH$k(34aB#R&D&@&Z>KW8gC_D03h>=$0d#&AI3@s=v=U_(#ZtJpkmVeddT;zEmT;6 z-?KX@A3HsCKc}0{-9m#!E(3CQ(|HmrO3=Y0v(xbI{62)o_aEGi3L6mFIONcJ2+`CCTWoVOA~gx-Ce{5M%0u5S<%@@EN6WkY;@BWDya}WgFI#`Oahp2H-n{O-TWTTNP1vC@6!nWP;+V2 zJ~GA8NQa|58p;!Z#YzVkAMxij0tS%D3%(|H)Gpg#2^x*yey1b$+zl*M@kW}u<^dhEV>rebQ z1d@-Dq5cCb{uB82FFJ%jp(*?+P2+#V4?d?^{2y9?Z6AUJO99qZo=`3D=twSwB?}-S#b#yx~(n6D5XdJcj z5kL-!N!wB;xXtlSwCR*m&Yre8xFSG3P>u`cSbXG|0VSW&0Kc}$0XfuDT+D-wfZ;q- zQ-`_ZUayTh82O-eRf=wI{T5Aa8c zlPdH>Q9Oi@nK#eOyvf`9%PRn!K{tTC0QMOim#8pxx2PY*P~o*vRiR^9y1{f7h%yEz zy?7)hf-9kO9vhv9{m($?L?+{>YZnB@rxrg zv{&+<0rWES$GabTGn)(;)aV}o)a%p?)alfqQ-eX1wt!X*)+yD3Hc7uy8zg72`8~7+ Oow;h8E>q1JV)p~}%CQXq literal 0 HcmV?d00001 diff --git a/target/classes/com/ruoyi/project/tool/gen/service/IGenTableService.class b/target/classes/com/ruoyi/project/tool/gen/service/IGenTableService.class new file mode 100644 index 0000000000000000000000000000000000000000..0d6c62c9be7af541929a5a9a8d050be1b0a38de6 GIT binary patch literal 1708 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 L@L5U};FkXZ12oW{ literal 0 HcmV?d00001 diff --git a/target/classes/com/ruoyi/project/tool/gen/util/VelocityInitializer.class b/target/classes/com/ruoyi/project/tool/gen/util/VelocityInitializer.class new file mode 100644 index 0000000000000000000000000000000000000000..a710871fa3ccec8d41d58742d8a930bccebe29ca GIT binary patch literal 1087 zcma)5+fEZf82+Xdb}3tdau86Y98@T7F&iu#kKhDoz-@XG_$HNSUFlS*tgB0d$EZDeh$U7G9+E}!( zWTTLQg=GsX7FILJ!nNR8xW~}NpdCS z^I)IH((o-|T32PAVWZ@$nin_Jsq~^)9SdK3TB*RRh|p_j8F>35P`=csPbrqcv$hCeDFo2r3%TEMGWIjT>|(jR*{Ic6m?Q&cQyClJ?Bx9 z7BzzEWXSBQM(m4iY0{s#4AYf?YeG2~Mvh@L33~;aVJa5&1jP*$FN@ecA~e?zc%8~F zs<`6vi2GIHp0o;d<3^}uO}JMbi^eD7y1LUsEG2*r?&ASNo}j!Yly?b5eAI3n!(X36`b_n9q&v+* z+Cwmn+F)4gg!=DMGYoZbUmlwMU>K+i-J0Sl!{%bw;CFRQWo`W}S?UrsacKH!vqjXO z7Q&SoZker~=tkeG#_GLUVp3rigYXp`854QNc|== RW|1cyGyPNdH7dkB8_sK0xt- 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 C^V4bo literal 0 HcmV?d00001 diff --git a/target/classes/com/ruoyi/project/tool/swagger/TestController.class b/target/classes/com/ruoyi/project/tool/swagger/TestController.class new file mode 100644 index 0000000000000000000000000000000000000000..9ec2e3a0cd3a35328b3a766a091a17c6d97cab31 GIT binary patch literal 4706 zcmb_f{dW}A8Gi2WW@oc3k}LwEU?YOfM{ycI5SvH|32I~^G=V78s*~Lz8L~UG&derF zt!=56Vil#WML}x8TBSc!=*MXSoVLIB?;r{NCwltanc1D$7L`_Hp6VjCRk~d@v;It1t$eJg-RL;Os&E+zMsNO3O`WrihTQF8X3Hr z#*gr0`T7(2b3*?7G=R7i&XdE69IwZ9%rFaTs8)SScgeLf*GZjf(MyanuDg;f z0*|#X!?T96mXvuOEC{S!MuWiW5m(Dk(saP5mi@XrVHFN)k_E1J9tvO0_R7++1i)KY zK3TkRQlR4tvM^XG7md8(dcG31gh`i4Y(E8q1034=%N4`c3vm_~!S{mZYKO>Do;AZH z^P9^uPJbp**4zmKQeD$u)Ex4lS$)Mv@Ku2=^?a#;R7ad04d?tI4OWk{^xx5@=-E*F z3eHMH6iCPb1iDtBS0MlYbt76QM_hRxyxNypID2*eqYwR*33N7^ZijdUi8jb>B`Cd| zl(G6m$OLEc%<08Xud>YgW@R1q>bqC616R1O%xtsjQCW50m6MAXPjFw!8Z%`54dxf# zcyr;xb%944&$H#!9d19*$x4}2lX@3E_#0uUV$gWaI9Q3ORn)4!#vL2x@!-9%$12Pa z&tyR_^2iJ9`>F^2bGXVPo1H&(@jvH2_QSjRuS*LjuCSgp4Bmj}R9G8Rz4>BrU#3T_ zik;V=^s=+I{)T%{`XaFD3kW5Bi+Z*A*`>uZtT{Uy_HB6SrMi->9`s>cY1O4RRnj+u zEx12}d+{{|Z)flh&ae0L{76^M87O z{=(%9F5x2uzt7-fx#kb}MBq+;&EUYzxzo2^`^U|>>x+N9F4c8r@TpX?A+*g7Xi_gc ztvM5fNnrbzKu}Krf$ng9{oR|d^+D<$&C$cQNT4;OaIK^$-VHx=jO|YQU`M2QRQo-F z+;S%-vN9+$jm*dvaCL}*7kRu~;bpZmYGi0k_LV&mjloRR!C;C<+Y5oW*gzbn?*$^9G@JvK zBF!`h&717K)>UDz`%d6uTQ6BtI_~2K&t}=9@Oy;Ep3gSE#W=5U%xb{jt(=)giX)(} za~9$+h$ZF_zk$RN`OG$DlUZdJscUGC<5^0Z0;zmv)0D2NN&oDr-^Sl8zl%Ua32dXG z2e2Do=P&q8vr%E9+rdeLOA?V3P#4a2}gD?7D{57)wiwKhDM=9n7ws!=0g(+2FMuHJ~Noq?;= zi^nJxc$_Qa5L@vr4w`t2@zKWr7;IM|CP z^Hdmy^Aw(@8b#2?AO`}^U>`FgV{@oe#@0b}*PX}euD@eFN9WL--5>*zv2lvA%B;`f zt}rw|wa=gxIfi$DSs99i*HH_vgYx}BcnQQ>o2v2UXt$03F>aR0cs-172nV@?$w=&G zq={Oj-;qHMd-2r-BNBo`QgB!bMr(p+rQo?`1V^OcXea=a9%TM`j=o29{LFtApfh@V zbgsc8L^c4*QaVOp2sM7b6HF+$@6$DBtRjWmJ+9OE~Wn zV}g+!=gFTWF|0@gU9||h>UqDd%8QWq`K7!sG{Aek7T^-zr*N!R>hAZ#16JL@-A86| o&tDOfMT;Ni{7v!+E{$QF&ln~=H|`-B45`~;T$2ncFt`&^ZTEf)1SXLe-P0CU5ZkK&el_y z&Ixl~n0{dfB6J}_7bOZ8EoP2POVpZ~Rp(SiSB;!9V%RLl%*zsW+$tKjmS>ZirA_H< z$S}CA+8Q@3iBcJjE7rW4gR7!ePL!d7Hp-`&K4$U)GwlV$3C*c`kx5jy^X&ch{f&{U zn2OAXn#XQSRqk=y)QlW{5_4U*9_#y^#mos6l0$BwU(l3-$!A&CR&37ovhLduaSd1GsLUj*gp@9^YRl91KRbfBX_VPBe0oeZaZ5Hc z!Bd}Own!-|)}BmNMJlC(k5%!B`w@xQ4PGF(0BwJTDwm+ai{v`BSI6>vN}X4|P$%9}1W!$-fQU{& z!m0tI_+l3A!vQkxK4`)LD0UyT-~eR1584nLRi`-Y0c7dNC}9kxH%R(|Nq`Q*MlllI z1JWXOU>0+oC_#s?5&;gwLj<^kF@!N80;IdwDUj}2r(n8wo#b?fyiTD?)C7bQEg|Xy z$Xte|hoD?RhDT180XLGD1u?zr5Hq1)FeMzpL1!iu%5((tW*M#oCrt6R;*;no9fK8+ z3PxAuR|>3Q!&Mn}HlO$^?4&MF;UGqlIqaybsB!P;IhbEK5#p{_nLg~%3%(HkdoW0J zoKC>jG6idyR(+XH(kVZtU@g;YFumD>$?YSLN!*u2r+t~^TBf(YOlSO=9!&1*@t8zE1t{&L^t$ literal 0 HcmV?d00001 diff --git a/target/classes/i18n/messages.properties b/target/classes/i18n/messages.properties new file mode 100644 index 0000000..93de005 --- /dev/null +++ b/target/classes/i18n/messages.properties @@ -0,0 +1,38 @@ +#错误消息 +not.null=* 必须填写 +user.jcaptcha.error=验证码错误 +user.jcaptcha.expire=验证码已失效 +user.not.exists=用户不存在/密码错误 +user.password.not.match=用户不存在/密码错误 +user.password.retry.limit.count=密码输入错误{0}次 +user.password.retry.limit.exceed=密码输入错误{0}次,帐户锁定{1}分钟 +user.password.delete=对不起,您的账号已被删除 +user.blocked=用户已封禁,请联系管理员 +role.blocked=角色已封禁,请联系管理员 +login.blocked=很遗憾,访问IP已被列入系统黑名单 +user.logout.success=退出成功 + +length.not.valid=长度必须在{min}到{max}个字符之间 + +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=未知错误,请重新登录 + +##文件上传消息 +upload.exceed.maxSize=上传的文件大小超出限制的文件大小!
允许的文件最大大小是:{0}MB! +upload.filename.exceed.length=上传的文件名最长{0}个字符 + +##权限 +no.permission=您没有数据的权限,请联系管理员添加权限 [{0}] +no.create.permission=您没有创建数据的权限,请联系管理员添加权限 [{0}] +no.update.permission=您没有修改数据的权限,请联系管理员添加权限 [{0}] +no.delete.permission=您没有删除数据的权限,请联系管理员添加权限 [{0}] +no.export.permission=您没有导出数据的权限,请联系管理员添加权限 [{0}] +no.view.permission=您没有查看数据的权限,请联系管理员添加权限 [{0}] diff --git a/target/classes/logback.xml b/target/classes/logback.xml new file mode 100644 index 0000000..a360583 --- /dev/null +++ b/target/classes/logback.xml @@ -0,0 +1,93 @@ + + + + + + + + + + + ${log.pattern} + + + + + + ${log.path}/sys-info.log + + + + ${log.path}/sys-info.%d{yyyy-MM-dd}.log + + 60 + + + ${log.pattern} + + + + INFO + + ACCEPT + + DENY + + + + + ${log.path}/sys-error.log + + + + ${log.path}/sys-error.%d{yyyy-MM-dd}.log + + 60 + + + ${log.pattern} + + + + ERROR + + ACCEPT + + DENY + + + + + + ${log.path}/sys-user.log + + + ${log.path}/sys-user.%d{yyyy-MM-dd}.log + + 60 + + + ${log.pattern} + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/target/classes/mybatis/PartThree/AboutPartThreeMapper.xml b/target/classes/mybatis/PartThree/AboutPartThreeMapper.xml new file mode 100644 index 0000000..9756a1c --- /dev/null +++ b/target/classes/mybatis/PartThree/AboutPartThreeMapper.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/target/classes/mybatis/monitor/SysLogininforMapper.xml b/target/classes/mybatis/monitor/SysLogininforMapper.xml new file mode 100644 index 0000000..9e92f59 --- /dev/null +++ b/target/classes/mybatis/monitor/SysLogininforMapper.xml @@ -0,0 +1,57 @@ + + + + + + + + + + + + + + + + + + insert into sys_logininfor (user_name, status, ipaddr, login_location, browser, os, msg, login_time) + values (#{userName}, #{status}, #{ipaddr}, #{loginLocation}, #{browser}, #{os}, #{msg}, sysdate()) + + + + + + delete from sys_logininfor where info_id in + + #{infoId} + + + + + truncate table sys_logininfor + + + \ No newline at end of file diff --git a/target/classes/mybatis/monitor/SysOperLogMapper.xml b/target/classes/mybatis/monitor/SysOperLogMapper.xml new file mode 100644 index 0000000..dd5a44a --- /dev/null +++ b/target/classes/mybatis/monitor/SysOperLogMapper.xml @@ -0,0 +1,87 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + select oper_id, title, business_type, method, request_method, operator_type, oper_name, dept_name, oper_url, oper_ip, oper_location, oper_param, json_result, status, error_msg, oper_time, cost_time + from sys_oper_log + + + + insert into sys_oper_log(title, business_type, method, request_method, operator_type, oper_name, dept_name, oper_url, oper_ip, oper_location, oper_param, json_result, status, error_msg, cost_time, oper_time) + values (#{title}, #{businessType}, #{method}, #{requestMethod}, #{operatorType}, #{operName}, #{deptName}, #{operUrl}, #{operIp}, #{operLocation}, #{operParam}, #{jsonResult}, #{status}, #{errorMsg}, #{costTime}, sysdate()) + + + + + + delete from sys_oper_log where oper_id in + + #{operId} + + + + + + + truncate table sys_oper_log + + + \ No newline at end of file diff --git a/target/classes/mybatis/mybatis-config.xml b/target/classes/mybatis/mybatis-config.xml new file mode 100644 index 0000000..ac47c03 --- /dev/null +++ b/target/classes/mybatis/mybatis-config.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + diff --git a/target/classes/mybatis/system/SysConfigMapper.xml b/target/classes/mybatis/system/SysConfigMapper.xml new file mode 100644 index 0000000..c586e2e --- /dev/null +++ b/target/classes/mybatis/system/SysConfigMapper.xml @@ -0,0 +1,117 @@ + + + + + + + + + + + + + + + + + + select config_id, config_name, config_key, config_value, config_type, create_by, create_time, update_by, update_time, remark + from sys_config + + + + + + + and config_id = #{configId} + + + and config_key = #{configKey} + + + + + + + + + + + + + + insert into sys_config ( + config_name, + config_key, + config_value, + config_type, + create_by, + remark, + create_time + )values( + #{configName}, + #{configKey}, + #{configValue}, + #{configType}, + #{createBy}, + #{remark}, + sysdate() + ) + + + + update sys_config + + config_name = #{configName}, + config_key = #{configKey}, + config_value = #{configValue}, + config_type = #{configType}, + update_by = #{updateBy}, + remark = #{remark}, + update_time = sysdate() + + where config_id = #{configId} + + + + delete from sys_config where config_id = #{configId} + + + + delete from sys_config where config_id in + + #{configId} + + + + \ No newline at end of file diff --git a/target/classes/mybatis/system/SysDeptMapper.xml b/target/classes/mybatis/system/SysDeptMapper.xml new file mode 100644 index 0000000..d361cdc --- /dev/null +++ b/target/classes/mybatis/system/SysDeptMapper.xml @@ -0,0 +1,159 @@ + + + + + + + + + + + + + + + + + + + + + + + + select d.dept_id, d.parent_id, d.ancestors, d.dept_name, d.order_num, d.leader, d.phone, d.email, d.status, d.del_flag, d.create_by, d.create_time + from sys_dept d + + + + + + + + + + + + + + + + + + + + insert into sys_dept( + dept_id, + parent_id, + dept_name, + ancestors, + order_num, + leader, + phone, + email, + status, + create_by, + create_time + )values( + #{deptId}, + #{parentId}, + #{deptName}, + #{ancestors}, + #{orderNum}, + #{leader}, + #{phone}, + #{email}, + #{status}, + #{createBy}, + sysdate() + ) + + + + update sys_dept + + parent_id = #{parentId}, + dept_name = #{deptName}, + ancestors = #{ancestors}, + order_num = #{orderNum}, + leader = #{leader}, + phone = #{phone}, + email = #{email}, + status = #{status}, + update_by = #{updateBy}, + update_time = sysdate() + + where dept_id = #{deptId} + + + + update sys_dept set ancestors = + + when #{item.deptId} then #{item.ancestors} + + where dept_id in + + #{item.deptId} + + + + + update sys_dept set status = '0' where dept_id in + + #{deptId} + + + + + update sys_dept set del_flag = '2' where dept_id = #{deptId} + + + \ No newline at end of file diff --git a/target/classes/mybatis/system/SysDictDataMapper.xml b/target/classes/mybatis/system/SysDictDataMapper.xml new file mode 100644 index 0000000..8221d6e --- /dev/null +++ b/target/classes/mybatis/system/SysDictDataMapper.xml @@ -0,0 +1,124 @@ + + + + + + + + + + + + + + + + + + + + + + select dict_code, dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, status, create_by, create_time, remark + from sys_dict_data + + + + + + + + + + + + + + delete from sys_dict_data where dict_code = #{dictCode} + + + + delete from sys_dict_data where dict_code in + + #{dictCode} + + + + + update sys_dict_data + + dict_sort = #{dictSort}, + dict_label = #{dictLabel}, + dict_value = #{dictValue}, + dict_type = #{dictType}, + css_class = #{cssClass}, + list_class = #{listClass}, + is_default = #{isDefault}, + status = #{status}, + remark = #{remark}, + update_by = #{updateBy}, + update_time = sysdate() + + where dict_code = #{dictCode} + + + + update sys_dict_data set dict_type = #{newDictType} where dict_type = #{oldDictType} + + + + insert into sys_dict_data( + dict_sort, + dict_label, + dict_value, + dict_type, + css_class, + list_class, + is_default, + status, + remark, + create_by, + create_time + )values( + #{dictSort}, + #{dictLabel}, + #{dictValue}, + #{dictType}, + #{cssClass}, + #{listClass}, + #{isDefault}, + #{status}, + #{remark}, + #{createBy}, + sysdate() + ) + + + \ No newline at end of file diff --git a/target/classes/mybatis/system/SysDictTypeMapper.xml b/target/classes/mybatis/system/SysDictTypeMapper.xml new file mode 100644 index 0000000..06498a9 --- /dev/null +++ b/target/classes/mybatis/system/SysDictTypeMapper.xml @@ -0,0 +1,105 @@ + + + + + + + + + + + + + + + + + select dict_id, dict_name, dict_type, status, create_by, create_time, remark + from sys_dict_type + + + + + + + + + + + + + + delete from sys_dict_type where dict_id = #{dictId} + + + + delete from sys_dict_type where dict_id in + + #{dictId} + + + + + update sys_dict_type + + dict_name = #{dictName}, + dict_type = #{dictType}, + status = #{status}, + remark = #{remark}, + update_by = #{updateBy}, + update_time = sysdate() + + where dict_id = #{dictId} + + + + insert into sys_dict_type( + dict_name, + dict_type, + status, + remark, + create_by, + create_time + )values( + #{dictName}, + #{dictType}, + #{status}, + #{remark}, + #{createBy}, + sysdate() + ) + + + \ No newline at end of file diff --git a/target/classes/mybatis/system/SysJobLogMapper.xml b/target/classes/mybatis/system/SysJobLogMapper.xml new file mode 100644 index 0000000..db17d91 --- /dev/null +++ b/target/classes/mybatis/system/SysJobLogMapper.xml @@ -0,0 +1,94 @@ + + + + + + + + + + + + + + + + + select job_log_id, job_name, job_group, invoke_target, job_message, status, exception_info, create_time + from sys_job_log + + + + + + + + + + delete from sys_job_log where job_log_id = #{jobLogId} + + + + delete from sys_job_log where job_log_id in + + #{jobLogId} + + + + + truncate table sys_job_log + + + + insert into sys_job_log( + job_log_id, + job_name, + job_group, + invoke_target, + job_message, + status, + exception_info, + create_time + )values( + #{jobLogId}, + #{jobName}, + #{jobGroup}, + #{invokeTarget}, + #{jobMessage}, + #{status}, + #{exceptionInfo}, + sysdate() + ) + + + \ No newline at end of file diff --git a/target/classes/mybatis/system/SysJobMapper.xml b/target/classes/mybatis/system/SysJobMapper.xml new file mode 100644 index 0000000..47f61c9 --- /dev/null +++ b/target/classes/mybatis/system/SysJobMapper.xml @@ -0,0 +1,111 @@ + + + + + + + + + + + + + + + + + + + + + + select job_id, job_name, job_group, invoke_target, cron_expression, misfire_policy, concurrent, status, create_by, create_time, remark + from sys_job + + + + + + + + + + delete from sys_job where job_id = #{jobId} + + + + delete from sys_job where job_id in + + #{jobId} + + + + + update sys_job + + job_name = #{jobName}, + job_group = #{jobGroup}, + invoke_target = #{invokeTarget}, + cron_expression = #{cronExpression}, + misfire_policy = #{misfirePolicy}, + concurrent = #{concurrent}, + status = #{status}, + remark = #{remark}, + update_by = #{updateBy}, + update_time = sysdate() + + where job_id = #{jobId} + + + + insert into sys_job( + job_id, + job_name, + job_group, + invoke_target, + cron_expression, + misfire_policy, + concurrent, + status, + remark, + create_by, + create_time + )values( + #{jobId}, + #{jobName}, + #{jobGroup}, + #{invokeTarget}, + #{cronExpression}, + #{misfirePolicy}, + #{concurrent}, + #{status}, + #{remark}, + #{createBy}, + sysdate() + ) + + + \ No newline at end of file diff --git a/target/classes/mybatis/system/SysMenuMapper.xml b/target/classes/mybatis/system/SysMenuMapper.xml new file mode 100644 index 0000000..a07f65f --- /dev/null +++ b/target/classes/mybatis/system/SysMenuMapper.xml @@ -0,0 +1,202 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + select menu_id, menu_name, parent_id, order_num, path, component, `query`, is_frame, is_cache, menu_type, visible, status, ifnull(perms,'') as perms, icon, create_time + from sys_menu + + + + + + + + + + + + + + + + + + + + + + + + + + update sys_menu + + menu_name = #{menuName}, + parent_id = #{parentId}, + order_num = #{orderNum}, + path = #{path}, + component = #{component}, + `query` = #{query}, + is_frame = #{isFrame}, + is_cache = #{isCache}, + menu_type = #{menuType}, + visible = #{visible}, + status = #{status}, + perms = #{perms}, + icon = #{icon}, + remark = #{remark}, + update_by = #{updateBy}, + update_time = sysdate() + + where menu_id = #{menuId} + + + + insert into sys_menu( + menu_id, + parent_id, + menu_name, + order_num, + path, + component, + `query`, + is_frame, + is_cache, + menu_type, + visible, + status, + perms, + icon, + remark, + create_by, + create_time + )values( + #{menuId}, + #{parentId}, + #{menuName}, + #{orderNum}, + #{path}, + #{component}, + #{query}, + #{isFrame}, + #{isCache}, + #{menuType}, + #{visible}, + #{status}, + #{perms}, + #{icon}, + #{remark}, + #{createBy}, + sysdate() + ) + + + + delete from sys_menu where menu_id = #{menuId} + + + \ No newline at end of file diff --git a/target/classes/mybatis/system/SysNoticeMapper.xml b/target/classes/mybatis/system/SysNoticeMapper.xml new file mode 100644 index 0000000..33c37a7 --- /dev/null +++ b/target/classes/mybatis/system/SysNoticeMapper.xml @@ -0,0 +1,89 @@ + + + + + + + + + + + + + + + + + + + select notice_id, notice_title, notice_type, cast(notice_content as char) as notice_content, status, create_by, create_time, update_by, update_time, remark + from sys_notice + + + + + + + + insert into sys_notice ( + notice_title, + notice_type, + notice_content, + status, + remark, + create_by, + create_time + )values( + #{noticeTitle}, + #{noticeType}, + #{noticeContent}, + #{status}, + #{remark}, + #{createBy}, + sysdate() + ) + + + + update sys_notice + + notice_title = #{noticeTitle}, + notice_type = #{noticeType}, + notice_content = #{noticeContent}, + status = #{status}, + update_by = #{updateBy}, + update_time = sysdate() + + where notice_id = #{noticeId} + + + + delete from sys_notice where notice_id = #{noticeId} + + + + delete from sys_notice where notice_id in + + #{noticeId} + + + + \ No newline at end of file diff --git a/target/classes/mybatis/system/SysPostMapper.xml b/target/classes/mybatis/system/SysPostMapper.xml new file mode 100644 index 0000000..23e7c14 --- /dev/null +++ b/target/classes/mybatis/system/SysPostMapper.xml @@ -0,0 +1,122 @@ + + + + + + + + + + + + + + + + + + + select post_id, post_code, post_name, post_sort, status, create_by, create_time, remark + from sys_post + + + + + + + + + + + + + + + + + + update sys_post + + post_code = #{postCode}, + post_name = #{postName}, + post_sort = #{postSort}, + status = #{status}, + remark = #{remark}, + update_by = #{updateBy}, + update_time = sysdate() + + where post_id = #{postId} + + + + insert into sys_post( + post_id, + post_code, + post_name, + post_sort, + status, + remark, + create_by, + create_time + )values( + #{postId}, + #{postCode}, + #{postName}, + #{postSort}, + #{status}, + #{remark}, + #{createBy}, + sysdate() + ) + + + + delete from sys_post where post_id = #{postId} + + + + delete from sys_post where post_id in + + #{postId} + + + + \ No newline at end of file diff --git a/target/classes/mybatis/system/SysRoleDeptMapper.xml b/target/classes/mybatis/system/SysRoleDeptMapper.xml new file mode 100644 index 0000000..23d2cb5 --- /dev/null +++ b/target/classes/mybatis/system/SysRoleDeptMapper.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + delete from sys_role_dept where role_id=#{roleId} + + + + + + delete from sys_role_dept where role_id in + + #{roleId} + + + + + insert into sys_role_dept(role_id, dept_id) values + + (#{item.roleId},#{item.deptId}) + + + + \ No newline at end of file diff --git a/target/classes/mybatis/system/SysRoleMapper.xml b/target/classes/mybatis/system/SysRoleMapper.xml new file mode 100644 index 0000000..adfa374 --- /dev/null +++ b/target/classes/mybatis/system/SysRoleMapper.xml @@ -0,0 +1,152 @@ + + + + + + + + + + + + + + + + + + + + + + + select distinct r.role_id, r.role_name, r.role_key, r.role_sort, r.data_scope, r.menu_check_strictly, r.dept_check_strictly, + r.status, r.del_flag, r.create_time, r.remark + from sys_role r + left join sys_user_role ur on ur.role_id = r.role_id + left join sys_user u on u.user_id = ur.user_id + left join sys_dept d on u.dept_id = d.dept_id + + + + + + + + + + + + + + + + + + + + insert into sys_role( + role_id, + role_name, + role_key, + role_sort, + data_scope, + menu_check_strictly, + dept_check_strictly, + status, + remark, + create_by, + create_time + )values( + #{roleId}, + #{roleName}, + #{roleKey}, + #{roleSort}, + #{dataScope}, + #{menuCheckStrictly}, + #{deptCheckStrictly}, + #{status}, + #{remark}, + #{createBy}, + sysdate() + ) + + + + update sys_role + + role_name = #{roleName}, + role_key = #{roleKey}, + role_sort = #{roleSort}, + data_scope = #{dataScope}, + menu_check_strictly = #{menuCheckStrictly}, + dept_check_strictly = #{deptCheckStrictly}, + status = #{status}, + remark = #{remark}, + update_by = #{updateBy}, + update_time = sysdate() + + where role_id = #{roleId} + + + + update sys_role set del_flag = '2' where role_id = #{roleId} + + + + update sys_role set del_flag = '2' where role_id in + + #{roleId} + + + + \ No newline at end of file diff --git a/target/classes/mybatis/system/SysRoleMenuMapper.xml b/target/classes/mybatis/system/SysRoleMenuMapper.xml new file mode 100644 index 0000000..bf32088 --- /dev/null +++ b/target/classes/mybatis/system/SysRoleMenuMapper.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + + + delete from sys_role_menu where role_id=#{roleId} + + + + delete from sys_role_menu where role_id in + + #{roleId} + + + + + insert into sys_role_menu(role_id, menu_id) values + + (#{item.roleId},#{item.menuId}) + + + + \ No newline at end of file diff --git a/target/classes/mybatis/system/SysUserMapper.xml b/target/classes/mybatis/system/SysUserMapper.xml new file mode 100644 index 0000000..f02de1b --- /dev/null +++ b/target/classes/mybatis/system/SysUserMapper.xml @@ -0,0 +1,221 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + select u.user_id, u.dept_id, u.user_name, u.nick_name, u.email, u.avatar, u.phonenumber, u.password, u.sex, u.status, u.del_flag, u.login_ip, u.login_date, u.create_by, u.create_time, u.remark, + d.dept_id, d.parent_id, d.ancestors, d.dept_name, d.order_num, d.leader, d.status as dept_status, + r.role_id, r.role_name, r.role_key, r.role_sort, r.data_scope, r.status as role_status + from sys_user u + left join sys_dept d on u.dept_id = d.dept_id + left join sys_user_role ur on u.user_id = ur.user_id + left join sys_role r on r.role_id = ur.role_id + + + + + + + + + + + + + + + + + + + + insert into sys_user( + user_id, + dept_id, + user_name, + nick_name, + email, + avatar, + phonenumber, + sex, + password, + status, + create_by, + remark, + create_time + )values( + #{userId}, + #{deptId}, + #{userName}, + #{nickName}, + #{email}, + #{avatar}, + #{phonenumber}, + #{sex}, + #{password}, + #{status}, + #{createBy}, + #{remark}, + sysdate() + ) + + + + update sys_user + + dept_id = #{deptId}, + user_name = #{userName}, + nick_name = #{nickName}, + email = #{email}, + phonenumber = #{phonenumber}, + sex = #{sex}, + avatar = #{avatar}, + password = #{password}, + status = #{status}, + login_ip = #{loginIp}, + login_date = #{loginDate}, + update_by = #{updateBy}, + remark = #{remark}, + update_time = sysdate() + + where user_id = #{userId} + + + + update sys_user set status = #{status} where user_id = #{userId} + + + + update sys_user set avatar = #{avatar} where user_name = #{userName} + + + + update sys_user set password = #{password} where user_name = #{userName} + + + + update sys_user set del_flag = '2' where user_id = #{userId} + + + + update sys_user set del_flag = '2' where user_id in + + #{userId} + + + + \ No newline at end of file diff --git a/target/classes/mybatis/system/SysUserPostMapper.xml b/target/classes/mybatis/system/SysUserPostMapper.xml new file mode 100644 index 0000000..d048078 --- /dev/null +++ b/target/classes/mybatis/system/SysUserPostMapper.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + delete from sys_user_post where user_id=#{userId} + + + + + + delete from sys_user_post where user_id in + + #{userId} + + + + + insert into sys_user_post(user_id, post_id) values + + (#{item.userId},#{item.postId}) + + + + \ No newline at end of file diff --git a/target/classes/mybatis/system/SysUserRoleMapper.xml b/target/classes/mybatis/system/SysUserRoleMapper.xml new file mode 100644 index 0000000..d7324eb --- /dev/null +++ b/target/classes/mybatis/system/SysUserRoleMapper.xml @@ -0,0 +1,44 @@ + + + + + + + + + + + delete from sys_user_role where user_id=#{userId} + + + + + + delete from sys_user_role where user_id in + + #{userId} + + + + + insert into sys_user_role(user_id, role_id) values + + (#{item.userId},#{item.roleId}) + + + + + delete from sys_user_role where user_id=#{userId} and role_id=#{roleId} + + + + delete from sys_user_role where role_id=#{roleId} and user_id in + + #{userId} + + + \ No newline at end of file diff --git a/target/classes/mybatis/tool/GenTableColumnMapper.xml b/target/classes/mybatis/tool/GenTableColumnMapper.xml new file mode 100644 index 0000000..450f89c --- /dev/null +++ b/target/classes/mybatis/tool/GenTableColumnMapper.xml @@ -0,0 +1,127 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + select column_id, table_id, column_name, column_comment, column_type, java_type, java_field, is_pk, is_increment, is_required, is_insert, is_edit, is_list, is_query, query_type, html_type, dict_type, sort, create_by, create_time, update_by, update_time from gen_table_column + + + + + + + + insert into gen_table_column ( + table_id, + column_name, + column_comment, + column_type, + java_type, + java_field, + is_pk, + is_increment, + is_required, + is_insert, + is_edit, + is_list, + is_query, + query_type, + html_type, + dict_type, + sort, + create_by, + create_time + )values( + #{tableId}, + #{columnName}, + #{columnComment}, + #{columnType}, + #{javaType}, + #{javaField}, + #{isPk}, + #{isIncrement}, + #{isRequired}, + #{isInsert}, + #{isEdit}, + #{isList}, + #{isQuery}, + #{queryType}, + #{htmlType}, + #{dictType}, + #{sort}, + #{createBy}, + sysdate() + ) + + + + update gen_table_column + + column_comment = #{columnComment}, + java_type = #{javaType}, + java_field = #{javaField}, + is_insert = #{isInsert}, + is_edit = #{isEdit}, + is_list = #{isList}, + is_query = #{isQuery}, + is_required = #{isRequired}, + query_type = #{queryType}, + html_type = #{htmlType}, + dict_type = #{dictType}, + sort = #{sort}, + update_by = #{updateBy}, + update_time = sysdate() + + where column_id = #{columnId} + + + + delete from gen_table_column where table_id in + + #{tableId} + + + + + delete from gen_table_column where column_id in + + #{item.columnId} + + + + \ No newline at end of file diff --git a/target/classes/mybatis/tool/GenTableMapper.xml b/target/classes/mybatis/tool/GenTableMapper.xml new file mode 100644 index 0000000..f26cc99 --- /dev/null +++ b/target/classes/mybatis/tool/GenTableMapper.xml @@ -0,0 +1,210 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + select table_id, table_name, table_comment, sub_table_name, sub_table_fk_name, class_name, tpl_category, tpl_web_type, package_name, module_name, business_name, function_name, function_author, gen_type, gen_path, options, create_by, create_time, update_by, update_time, remark from gen_table + + + + + + + + + + + + + + + + + + insert into gen_table ( + table_name, + table_comment, + class_name, + tpl_category, + tpl_web_type, + package_name, + module_name, + business_name, + function_name, + function_author, + gen_type, + gen_path, + remark, + create_by, + create_time + )values( + #{tableName}, + #{tableComment}, + #{className}, + #{tplCategory}, + #{tplWebType}, + #{packageName}, + #{moduleName}, + #{businessName}, + #{functionName}, + #{functionAuthor}, + #{genType}, + #{genPath}, + #{remark}, + #{createBy}, + sysdate() + ) + + + + ${sql} + + + + update gen_table + + table_name = #{tableName}, + table_comment = #{tableComment}, + sub_table_name = #{subTableName}, + sub_table_fk_name = #{subTableFkName}, + class_name = #{className}, + function_author = #{functionAuthor}, + gen_type = #{genType}, + gen_path = #{genPath}, + tpl_category = #{tplCategory}, + tpl_web_type = #{tplWebType}, + package_name = #{packageName}, + module_name = #{moduleName}, + business_name = #{businessName}, + function_name = #{functionName}, + options = #{options}, + update_by = #{updateBy}, + remark = #{remark}, + update_time = sysdate() + + where table_id = #{tableId} + + + + delete from gen_table where table_id in + + #{tableId} + + + + \ No newline at end of file diff --git a/target/classes/vm/java/controller.java.vm b/target/classes/vm/java/controller.java.vm new file mode 100644 index 0000000..6c9d936 --- /dev/null +++ b/target/classes/vm/java/controller.java.vm @@ -0,0 +1,84 @@ +package ${packageName}.controller; +import java.util.List; +import javax.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.springframework.security.access.prepost.PreAuthorize; + +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import ${packageName}.domain.${ClassName}; +import ${packageName}.service.I${ClassName}Service; +import com.ruoyi.framework.web.domain.R; +#if($table.crud || $table.sub) +#elseif($table.tree) +#end + +/** + * ${functionName}Controller + * + * @author ${author} + * @date ${datetime} + */ +@Slf4j +@Api(tags = "${functionName}") +@RestController +@RequestMapping("/${moduleName}") +public class ${ClassName}Controller { + @Resource + private I${ClassName}Service ${className}Service; + + /** + * ${functionName}列表 + */ + @PreAuthorize("@ss.hasPermi('${permissionPrefix}:list')") + @ApiOperation(value = "${functionName}列表", notes = "${functionName}列表信息") + @GetMapping("/list") + public R> list() { + List<${ClassName}> list = ${className}Service.list(); + return R.ok(list); + } + /** + * ${functionName}id详细信息 + */ + @PreAuthorize("@ss.hasPermi('${permissionPrefix}:query')") + @ApiOperation(value = "${functionName}id详细信息", notes = "${functionName}id详细信息") + @GetMapping("/query") + public R<${ClassName}> query(Long id) { + ${ClassName} queryinfo = ${className}Service.getById(id); + return R.ok(queryinfo); + } + /** + * ${functionName}新增信息 + */ + @PreAuthorize("@ss.hasPermi('${permissionPrefix}:add')") + @ApiOperation(value = "${functionName}新增信息", notes = "${functionName}新增信息") + @PostMapping("/add") + public R add(${ClassName} ${className}) { + boolean res = ${className}Service.save(${className}); + return R.ok(res); + } + /** + * ${functionName}修改信息 + */ + @PreAuthorize("@ss.hasPermi('${permissionPrefix}:edit')") + @ApiOperation(value = "${functionName}修改信息", notes = "${functionName}修改信息") + @PostMapping("/edit") + public R edit(${ClassName} ${className}) { + boolean res = ${className}Service.updateById(${className}); + return R.ok(res); + } + /** + * ${functionName}删除信息 + */ + @PreAuthorize("@ss.hasPermi('${permissionPrefix}:remove')") + @ApiOperation(value = "${functionName}删除信息", notes = "${functionName}删除信息") + @PostMapping("/remove") + public R remove(Long id) { + boolean res = ${className}Service.removeById(id); + return R.ok(res); + } +} diff --git a/target/classes/vm/java/domain.java.vm b/target/classes/vm/java/domain.java.vm new file mode 100644 index 0000000..16ed42e --- /dev/null +++ b/target/classes/vm/java/domain.java.vm @@ -0,0 +1,65 @@ +package ${packageName}.domain; + +import com.alibaba.fastjson2.annotation.JSONField; +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.databind.ser.std.ToStringSerializer; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + + + + +/** + * ${functionName}对象 ${tableName} + * + * @author ${author} + * @date ${datetime} + */ +@Data +@NoArgsConstructor +@AllArgsConstructor +@Builder +@ApiModel("${functionName}") +@TableName(" ${tableName}") +public class ${ClassName}{ +private static final long serialVersionUID=1L; + +#foreach ($column in $columns) + #if(!$table.isSuperColumn($column.javaField)) + /** $column.columnComment */ + #if($column.list) + #set($parentheseIndex=$column.columnComment.indexOf("(")) + #if($parentheseIndex != -1) + #set($comment=$column.columnComment.substring(0, $parentheseIndex)) + #else + #set($comment=$column.columnComment) + #end + #end + #if($column.isPk == 1) + @JSONField(serializeUsing = ToStringSerializer.class) + @ApiModelProperty("$column.columnComment") + @TableId(value = "$column.columnName" , type = IdType.ASSIGN_ID) + private $column.javaType $column.javaField; + #else + @ApiModelProperty("$column.columnComment") + @TableField("$column.columnName") + private $column.javaType $column.javaField; + #end + #else + #if($column.javaType == 'Date') + /** $column.columnComment */ + @ApiModelProperty("$column.columnComment") + @TableField("$column.columnName") + @JSONField(serializeUsing = ToStringSerializer.class) + private String $column.javaField; + #end + #end +#end +} \ No newline at end of file diff --git a/target/classes/vm/java/mapper.java.vm b/target/classes/vm/java/mapper.java.vm new file mode 100644 index 0000000..e861409 --- /dev/null +++ b/target/classes/vm/java/mapper.java.vm @@ -0,0 +1,14 @@ +package ${packageName}.mapper; + + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import ${packageName}.domain.${ClassName}; +/** + * ${functionName}Mapper接口 + * + * @author ${author} + * @date ${datetime} + */ +public interface ${ClassName}Mapper extends BaseMapper<${ClassName}> { + +} diff --git a/target/classes/vm/java/service.java.vm b/target/classes/vm/java/service.java.vm new file mode 100644 index 0000000..e06f8e4 --- /dev/null +++ b/target/classes/vm/java/service.java.vm @@ -0,0 +1,15 @@ +package ${packageName}.service; + + +import ${packageName}.domain.${ClassName}; +import com.baomidou.mybatisplus.extension.service.IService; + +/** + * ${functionName}Service接口 + * + * @author ${author} + * @date ${datetime} + */ +public interface I${ClassName}Service extends IService<${ClassName}> { + +} diff --git a/target/classes/vm/java/serviceImpl.java.vm b/target/classes/vm/java/serviceImpl.java.vm new file mode 100644 index 0000000..e13a05b --- /dev/null +++ b/target/classes/vm/java/serviceImpl.java.vm @@ -0,0 +1,17 @@ +package ${packageName}.service.impl; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import ${packageName}.mapper.${ClassName}Mapper; +import ${packageName}.domain.${ClassName}; +import ${packageName}.service.I${ClassName}Service; +import org.springframework.stereotype.Service; +/** + * ${functionName}Service业务层处理 + * + * @author ${author} + * @date ${datetime} + */ +@Service +public class ${ClassName}ServiceImpl extends ServiceImpl<${ClassName}Mapper, ${ClassName}> implements I${ClassName}Service { + +} diff --git a/target/classes/vm/java/sub-domain.java.vm b/target/classes/vm/java/sub-domain.java.vm new file mode 100644 index 0000000..bd6247d --- /dev/null +++ b/target/classes/vm/java/sub-domain.java.vm @@ -0,0 +1,73 @@ +package ${packageName}.domain; + + #foreach ($import in $subImportList) + import ${import}; + #end +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; +import com.rchuing.common.annotation.Excel; +import com.rchuing.common.core.domain.BaseEntity; + +/** + * ${subTable.functionName}对象 ${subTableName} + * + * @author ${author} + * @date ${datetime} + */ +public class ${subClassName} extends BaseEntity { +private static final long serialVersionUID = 1L; + +#foreach ($column in $subTable.columns) + #if(!$table.isSuperColumn($column.javaField)) + /** $column.columnComment */ + #if($column.list) + #set($parentheseIndex=$column.columnComment.indexOf("(")) + #if($parentheseIndex != -1) + #set($comment=$column.columnComment.substring(0, $parentheseIndex)) + #else + #set($comment=$column.columnComment) + #end + #if($parentheseIndex != -1) + @Excel(name = "${comment}", readConverterExp = "$column.readConverterExp()") + #elseif($column.javaType == 'Date') + @JsonFormat(pattern = "yyyy-MM-dd") + @Excel(name = "${comment}", width = 30, dateFormat = "yyyy-MM-dd") + #else + @Excel(name = "${comment}") + #end + #end + private $column.javaType $column.javaField; + + #end +#end +#foreach ($column in $subTable.columns) + #if(!$table.isSuperColumn($column.javaField)) + #if($column.javaField.length() > 2 && $column.javaField.substring(1,2).matches("[A-Z]")) + #set($AttrName=$column.javaField) + #else + #set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)}) + #end + public void set${AttrName}($column.javaType $column.javaField) { + this.$column.javaField = $column.javaField; + } + + public $column.javaType get${AttrName}() { + return $column.javaField; + } + #end +#end + +@Override +public String toString() { + return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) + #foreach ($column in $subTable.columns) + #if($column.javaField.length() > 2 && $column.javaField.substring(1,2).matches("[A-Z]")) + #set($AttrName=$column.javaField) + #else + #set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)}) + #end + .append("${column.javaField}", get${AttrName}()) + #end + .toString(); + } + } diff --git a/target/classes/vm/js/api.js.vm b/target/classes/vm/js/api.js.vm new file mode 100644 index 0000000..a1c6e32 --- /dev/null +++ b/target/classes/vm/js/api.js.vm @@ -0,0 +1,44 @@ +import request from '@/utils/request' + +// 查询${functionName}列表 +export function list${BusinessName}() { + return request({ + url: '/${moduleName}/list', + method: 'get', + }) +} + +// 查询${functionName}详细 +export function get${BusinessName}(${pkColumn.javaField}) { + return request({ + url: '/${moduleName}/id=' + ${pkColumn.javaField}, + method: 'get' + }) +} + +// 新增${functionName} +export function add${BusinessName}(data) { + return request({ + url: '/${moduleName}/add', + method: 'post', + data: data + }) +} + +// 修改${functionName} +export function update${BusinessName}(data) { + return request({ + url: '/${moduleName}/edit', + method: 'post', + data: data + }) +} + +// 删除${functionName} +export function del${BusinessName}(${pkColumn.javaField}) { + return request({ + url: '/${moduleName}/remove/', + method: 'post', + params: {id:${pkColumn.javaField}} + }) +} diff --git a/target/classes/vm/sql/sql.vm b/target/classes/vm/sql/sql.vm new file mode 100644 index 0000000..0575583 --- /dev/null +++ b/target/classes/vm/sql/sql.vm @@ -0,0 +1,22 @@ +-- 菜单 SQL +insert into sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) +values('${functionName}', '${parentMenuId}', '1', '${businessName}', '${moduleName}/${businessName}/index', 1, 0, 'C', '0', '0', '${permissionPrefix}:list', '#', 'admin', sysdate(), '', null, '${functionName}菜单'); + +-- 按钮父菜单ID +SELECT @parentId := LAST_INSERT_ID(); + +-- 按钮 SQL +insert into sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) +values('${functionName}查询', @parentId, '1', '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:query', '#', 'admin', sysdate(), '', null, ''); + +insert into sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) +values('${functionName}新增', @parentId, '2', '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:add', '#', 'admin', sysdate(), '', null, ''); + +insert into sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) +values('${functionName}修改', @parentId, '3', '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:edit', '#', 'admin', sysdate(), '', null, ''); + +insert into sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) +values('${functionName}删除', @parentId, '4', '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:remove', '#', 'admin', sysdate(), '', null, ''); + +insert into sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) +values('${functionName}导出', @parentId, '5', '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:export', '#', 'admin', sysdate(), '', null, ''); \ No newline at end of file diff --git a/target/classes/vm/vue/index-tree.vue.vm b/target/classes/vm/vue/index-tree.vue.vm new file mode 100644 index 0000000..4819c2a --- /dev/null +++ b/target/classes/vm/vue/index-tree.vue.vm @@ -0,0 +1,505 @@ + + + diff --git a/target/classes/vm/vue/index.vue.vm b/target/classes/vm/vue/index.vue.vm new file mode 100644 index 0000000..6296014 --- /dev/null +++ b/target/classes/vm/vue/index.vue.vm @@ -0,0 +1,602 @@ + + + diff --git a/target/classes/vm/vue/v3/index-tree.vue.vm b/target/classes/vm/vue/v3/index-tree.vue.vm new file mode 100644 index 0000000..c54d62b --- /dev/null +++ b/target/classes/vm/vue/v3/index-tree.vue.vm @@ -0,0 +1,474 @@ + + + diff --git a/target/classes/vm/vue/v3/index.vue.vm b/target/classes/vm/vue/v3/index.vue.vm new file mode 100644 index 0000000..8b25665 --- /dev/null +++ b/target/classes/vm/vue/v3/index.vue.vm @@ -0,0 +1,590 @@ + + + diff --git a/target/classes/vm/xml/mapper.xml.vm b/target/classes/vm/xml/mapper.xml.vm new file mode 100644 index 0000000..4916767 --- /dev/null +++ b/target/classes/vm/xml/mapper.xml.vm @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file