From 5b5ade75e091dd057a49f3dc3a364dc33b2028ee Mon Sep 17 00:00:00 2001
From: HMY <3124750372@qq.com>
Date: Sat, 1 Jun 2024 12:27:39 +0800
Subject: [PATCH] ruoyi
---
.gitignore | 43 +
LICENSE | 20 +
README.md | 84 ++
bin/clean(1).bat | 12 +
bin/clean.bat | 12 +
bin/package(1)(1).bat | 12 +
bin/package(1).bat | 12 +
bin/package(2).bat | 12 +
bin/package.bat | 12 +
bin/run-tomcat(1)(1).bat | 14 +
bin/run-tomcat(1).bat | 14 +
bin/run-tomcat.bat | 14 +
pom.xml | 320 +++++
ry.sh | 86 ++
src/main/java/com/ruoyi/RuoYiApplication.java | 30 +
.../com/ruoyi/RuoYiServletInitializer.java | 18 +
.../com/ruoyi/common/constant/Constants.java | 131 ++
.../ruoyi/common/constant/GenConstants.java | 114 ++
.../com/ruoyi/common/constant/HttpStatus.java | 89 ++
.../common/constant/ScheduleConstants.java | 50 +
.../ruoyi/common/constant/UserConstants.java | 63 +
.../java/com/ruoyi/common/core/lang/UUID.java | 484 ++++++++
.../ruoyi/common/core/text/CharsetKit.java | 86 ++
.../com/ruoyi/common/core/text/Convert.java | 999 +++++++++++++++
.../ruoyi/common/core/text/StrFormatter.java | 92 ++
.../com/ruoyi/common/enums/HttpMethod.java | 36 +
.../com/ruoyi/common/enums/UserStatus.java | 30 +
.../ruoyi/common/exception/BaseException.java | 97 ++
.../common/exception/CustomException.java | 43 +
.../common/exception/DemoModeException.java | 15 +
.../ruoyi/common/exception/UtilException.java | 26 +
.../common/exception/file/FileException.java | 19 +
.../FileNameLengthLimitExceededException.java | 16 +
.../file/FileSizeLimitExceededException.java | 16 +
.../file/InvalidExtensionException.java | 71 ++
.../common/exception/job/TaskException.java | 34 +
.../exception/user/CaptchaException.java | 16 +
.../user/CaptchaExpireException.java | 16 +
.../common/exception/user/UserException.java | 18 +
.../user/UserPasswordNotMatchException.java | 16 +
.../ruoyi/common/filter/RepeatableFilter.java | 52 +
.../filter/RepeatedlyRequestWrapper.java | 72 ++
.../com/ruoyi/common/filter/XssFilter.java | 97 ++
.../filter/XssHttpServletRequestWrapper.java | 104 ++
.../java/com/ruoyi/common/utils/Arith.java | 114 ++
.../com/ruoyi/common/utils/DateUtils.java | 155 +++
.../com/ruoyi/common/utils/DictUtils.java | 173 +++
.../com/ruoyi/common/utils/ExceptionUtil.java | 40 +
.../java/com/ruoyi/common/utils/IdUtils.java | 51 +
.../java/com/ruoyi/common/utils/LogUtils.java | 18 +
.../com/ruoyi/common/utils/MessageUtils.java | 26 +
.../common/utils/MyMetaObjectHandler.java | 34 +
.../com/ruoyi/common/utils/SecurityUtils.java | 90 ++
.../com/ruoyi/common/utils/ServletUtils.java | 136 ++
.../com/ruoyi/common/utils/StringUtils.java | 459 +++++++
.../java/com/ruoyi/common/utils/Threads.java | 99 ++
.../ruoyi/common/utils/bean/BeanUtils.java | 110 ++
.../common/utils/file/FileTypeUtils.java | 76 ++
.../common/utils/file/FileUploadUtils.java | 231 ++++
.../ruoyi/common/utils/file/FileUtils.java | 203 +++
.../ruoyi/common/utils/file/ImageUtils.java | 100 ++
.../common/utils/file/MimeTypeUtils.java | 55 +
.../ruoyi/common/utils/html/EscapeUtil.java | 155 +++
.../ruoyi/common/utils/html/HTMLFilter.java | 570 +++++++++
.../ruoyi/common/utils/http/HttpHelper.java | 77 ++
.../ruoyi/common/utils/http/HttpUtils.java | 262 ++++
.../ruoyi/common/utils/ip/AddressUtils.java | 56 +
.../com/ruoyi/common/utils/ip/IpUtils.java | 195 +++
.../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 | 113 ++
.../com/ruoyi/common/utils/poi/ExcelUtil.java | 1093 +++++++++++++++++
.../common/utils/reflect/ReflectUtils.java | 410 +++++++
.../com/ruoyi/common/utils/sign/Base64.java | 291 +++++
.../com/ruoyi/common/utils/sign/Md5Utils.java | 66 +
.../common/utils/spring/SpringUtils.java | 146 +++
.../com/ruoyi/common/utils/sql/SqlUtil.java | 37 +
.../framework/aspectj/DataScopeAspect.java | 169 +++
.../framework/aspectj/DataSourceAspect.java | 72 ++
.../ruoyi/framework/aspectj/LogAspect.java | 242 ++++
.../aspectj/lang/annotation/DataScope.java | 28 +
.../aspectj/lang/annotation/DataSource.java | 28 +
.../aspectj/lang/annotation/Excel.java | 165 +++
.../aspectj/lang/annotation/Excels.java | 18 +
.../aspectj/lang/annotation/Log.java | 41 +
.../aspectj/lang/enums/BusinessStatus.java | 20 +
.../aspectj/lang/enums/BusinessType.java | 59 +
.../aspectj/lang/enums/DataSourceType.java | 19 +
.../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 | 71 ++
.../ruoyi/framework/config/FilterConfig.java | 60 +
.../com/ruoyi/framework/config/GenConfig.java | 66 +
.../framework/config/KaptchaTextCreator.java | 75 ++
.../ruoyi/framework/config/MyBatisConfig.java | 132 ++
.../ruoyi/framework/config/RedisConfig.java | 45 +
.../framework/config/ResourcesConfig.java | 65 +
.../ruoyi/framework/config/RuoYiConfig.java | 116 ++
.../framework/config/ScheduleConfig.java | 57 +
.../framework/config/SecurityConfig.java | 145 +++
.../ruoyi/framework/config/ServerConfig.java | 32 +
.../ruoyi/framework/config/SwaggerConfig.java | 124 ++
.../framework/config/ThreadPoolConfig.java | 62 +
.../config/properties/DruidProperties.java | 77 ++
.../datasource/DynamicDataSource.java | 26 +
.../DynamicDataSourceContextHolder.java | 45 +
.../interceptor/RepeatSubmitInterceptor.java | 55 +
.../interceptor/annotation/RepeatSubmit.java | 23 +
.../impl/SameUrlDataInterceptor.java | 125 ++
.../ruoyi/framework/manager/AsyncManager.java | 55 +
.../framework/manager/ShutdownManager.java | 39 +
.../manager/factory/AsyncFactory.java | 101 ++
.../com/ruoyi/framework/redis/RedisCache.java | 234 ++++
.../ruoyi/framework/security/LoginBody.java | 69 ++
.../ruoyi/framework/security/LoginUser.java | 228 ++++
.../filter/JwtAuthenticationTokenFilter.java | 44 +
.../handle/AuthenticationEntryPointImpl.java | 34 +
.../handle/LogoutSuccessHandlerImpl.java | 53 +
.../security/service/PermissionService.java | 169 +++
.../security/service/SysLoginService.java | 88 ++
.../service/SysPermissionService.java | 66 +
.../security/service/TokenService.java | 219 ++++
.../service/UserDetailsServiceImpl.java | 60 +
.../java/com/ruoyi/framework/task/RyTask.java | 28 +
.../web/controller/BaseController.java | 86 ++
.../framework/web/domain/AjaxResult.java | 148 +++
.../framework/web/domain/BaseEntity.java | 114 ++
.../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 | 122 ++
.../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 | 117 ++
.../ruoyi/framework/web/page/PageDomain.java | 72 ++
.../framework/web/page/TableDataInfo.java | 85 ++
.../framework/web/page/TableSupport.java | 49 +
.../project/common/CaptchaController.java | 86 ++
.../project/common/CommonController.java | 118 ++
.../hit/controller/CardGameController.java | 99 ++
.../controller/CardGameProductController.java | 37 +
.../controller/CardGameRuleController.java | 39 +
.../hit/controller/CardProductController.java | 201 +++
.../hit/controller/CardUserController.java | 47 +
.../hit/controller/InfoController.java | 60 +
.../hit/controller/SystemDictController.java | 113 ++
.../hit/controller/vo/ExcelCardProductVo.java | 36 +
.../hit/controller/vo/ListCardGameVo.java | 25 +
.../hit/controller/vo/ListCardProductVo.java | 16 +
.../hit/controller/vo/ListCardUserVo.java | 31 +
.../project/hit/controller/vo/ListInfoVo.java | 24 +
.../hit/controller/vo/TotalInfoVo.java | 23 +
.../ruoyi/project/hit/domain/CardGame.java | 40 +
.../project/hit/domain/CardGameProduct.java | 26 +
.../project/hit/domain/CardGameRule.java | 28 +
.../ruoyi/project/hit/domain/CardProduct.java | 47 +
.../ruoyi/project/hit/domain/CardUser.java | 43 +
.../ruoyi/project/hit/domain/CardUserHit.java | 28 +
.../ruoyi/project/hit/domain/SystemDict.java | 34 +
.../project/hit/mapper/CardGameMapper.java | 29 +
.../hit/mapper/CardGameProductMapper.java | 24 +
.../hit/mapper/CardGameRuleMapper.java | 24 +
.../project/hit/mapper/CardProductMapper.java | 18 +
.../project/hit/mapper/CardUserHitMapper.java | 31 +
.../project/hit/mapper/CardUserMapper.java | 23 +
.../project/hit/mapper/SystemDictMapper.java | 18 +
.../hit/service/CardGameProductService.java | 52 +
.../hit/service/CardGameRuleService.java | 47 +
.../project/hit/service/CardGameService.java | 47 +
.../hit/service/CardProductService.java | 61 +
.../hit/service/CardUserHitService.java | 39 +
.../project/hit/service/CardUserService.java | 30 +
.../hit/service/SystemDictService.java | 77 ++
.../impl/CardGameProductServiceImpl.java | 90 ++
.../service/impl/CardGameRuleServiceImpl.java | 90 ++
.../hit/service/impl/CardGameServiceImpl.java | 122 ++
.../service/impl/CardProductServiceImpl.java | 102 ++
.../service/impl/CardUserHitServiceImpl.java | 58 +
.../hit/service/impl/CardUserServiceImpl.java | 43 +
.../service/impl/SystemDictServiceImpl.java | 161 +++
.../monitor/controller/CacheController.java | 53 +
.../monitor/controller/ServerController.java | 27 +
.../monitor/controller/SysJobController.java | 142 +++
.../controller/SysJobLogController.java | 90 ++
.../controller/SysLogininforController.java | 67 +
.../controller/SysOperlogController.java | 66 +
.../controller/SysUserOnlineController.java | 92 ++
.../ruoyi/project/monitor/domain/SysJob.java | 172 +++
.../project/monitor/domain/SysJobLog.java | 155 +++
.../project/monitor/domain/SysLogininfor.java | 144 +++
.../project/monitor/domain/SysOperLog.java | 255 ++++
.../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 | 254 ++++
.../impl/SysLogininforServiceImpl.java | 65 +
.../service/impl/SysOperLogServiceImpl.java | 76 ++
.../controller/SysConfigController.java | 133 ++
.../system/controller/SysDeptController.java | 163 +++
.../controller/SysDictDataController.java | 120 ++
.../controller/SysDictTypeController.java | 131 ++
.../system/controller/SysLoginController.java | 94 ++
.../system/controller/SysMenuController.java | 157 +++
.../controller/SysNoticeController.java | 92 ++
.../system/controller/SysPostController.java | 130 ++
.../controller/SysProfileController.java | 127 ++
.../system/controller/SysRoleController.java | 182 +++
.../system/controller/SysUserController.java | 205 ++++
.../project/system/domain/SysConfig.java | 111 ++
.../ruoyi/project/system/domain/SysDept.java | 202 +++
.../project/system/domain/SysDictData.java | 176 +++
.../project/system/domain/SysDictType.java | 94 ++
.../ruoyi/project/system/domain/SysMenu.java | 245 ++++
.../project/system/domain/SysNotice.java | 100 ++
.../ruoyi/project/system/domain/SysPost.java | 123 ++
.../ruoyi/project/system/domain/SysRole.java | 226 ++++
.../project/system/domain/SysRoleDept.java | 46 +
.../project/system/domain/SysRoleMenu.java | 46 +
.../ruoyi/project/system/domain/SysUser.java | 328 +++++
.../project/system/domain/SysUserPost.java | 46 +
.../project/system/domain/SysUserRole.java | 46 +
.../project/system/domain/vo/MetaVo.java | 71 ++
.../project/system/domain/vo/RouterVo.java | 133 ++
.../system/mapper/SysConfigMapper.java | 68 +
.../project/system/mapper/SysDeptMapper.java | 118 ++
.../system/mapper/SysDictDataMapper.java | 95 ++
.../system/mapper/SysDictTypeMapper.java | 85 ++
.../project/system/mapper/SysMenuMapper.java | 117 ++
.../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 | 113 ++
.../system/mapper/SysUserPostMapper.java | 44 +
.../system/mapper/SysUserRoleMapper.java | 62 +
.../system/service/ISysConfigService.java | 73 ++
.../system/service/ISysDeptService.java | 109 ++
.../system/service/ISysDictDataService.java | 61 +
.../system/service/ISysDictTypeService.java | 89 ++
.../system/service/ISysMenuService.java | 136 ++
.../system/service/ISysNoticeService.java | 60 +
.../system/service/ISysPostService.java | 100 ++
.../system/service/ISysRoleService.java | 131 ++
.../system/service/ISysUserOnlineService.java | 48 +
.../system/service/ISysUserService.java | 167 +++
.../service/impl/SysConfigServiceImpl.java | 196 +++
.../service/impl/SysDeptServiceImpl.java | 306 +++++
.../service/impl/SysDictDataServiceImpl.java | 109 ++
.../service/impl/SysDictTypeServiceImpl.java | 204 +++
.../service/impl/SysMenuServiceImpl.java | 465 +++++++
.../service/impl/SysNoticeServiceImpl.java | 92 ++
.../service/impl/SysPostServiceImpl.java | 182 +++
.../service/impl/SysRoleServiceImpl.java | 328 +++++
.../impl/SysUserOnlineServiceImpl.java | 96 ++
.../service/impl/SysUserServiceImpl.java | 463 +++++++
.../tool/gen/controller/GenController.java | 214 ++++
.../project/tool/gen/domain/GenTable.java | 372 ++++++
.../tool/gen/domain/GenTableColumn.java | 373 ++++++
.../tool/gen/mapper/GenTableColumnMapper.java | 60 +
.../tool/gen/mapper/GenTableMapper.java | 83 ++
.../service/GenTableColumnServiceImpl.java | 68 +
.../tool/gen/service/GenTableServiceImpl.java | 500 ++++++++
.../gen/service/IGenTableColumnService.java | 44 +
.../tool/gen/service/IGenTableService.java | 121 ++
.../ruoyi/project/tool/gen/util/GenUtils.java | 258 ++++
.../tool/gen/util/VelocityInitializer.java | 35 +
.../project/tool/gen/util/VelocityUtils.java | 360 ++++++
.../project/tool/swagger/TestController.java | 175 +++
.../META-INF/spring-devtools.properties | 1 +
src/main/resources/application-druid.yml | 61 +
src/main/resources/application.yml | 141 +++
src/main/resources/banner.txt | 24 +
src/main/resources/i18n/messages.properties | 36 +
src/main/resources/logback.xml | 93 ++
.../resources/mybatis/hit/CardGameMapper.xml | 67 +
.../mybatis/hit/CardGameProductMapper.xml | 31 +
.../mybatis/hit/CardGameRuleMapper.xml | 33 +
.../mybatis/hit/CardProductMapper.xml | 20 +
.../mybatis/hit/CardUserHitMapper.xml | 100 ++
.../resources/mybatis/hit/CardUserMapper.xml | 54 +
.../mybatis/hit/SystemDictMapper.xml | 22 +
.../mybatis/monitor/SysLogininforMapper.xml | 57 +
.../mybatis/monitor/SysOperLogMapper.xml | 83 ++
src/main/resources/mybatis/mybatis-config.xml | 15 +
.../mybatis/system/SysConfigMapper.xml | 112 ++
.../mybatis/system/SysDeptMapper.xml | 157 +++
.../mybatis/system/SysDictDataMapper.xml | 124 ++
.../mybatis/system/SysDictTypeMapper.xml | 105 ++
.../mybatis/system/SysJobLogMapper.xml | 93 ++
.../resources/mybatis/system/SysJobMapper.xml | 111 ++
.../mybatis/system/SysMenuMapper.xml | 191 +++
.../mybatis/system/SysNoticeMapper.xml | 89 ++
.../mybatis/system/SysPostMapper.xml | 122 ++
.../mybatis/system/SysRoleDeptMapper.xml | 34 +
.../mybatis/system/SysRoleMapper.xml | 153 +++
.../mybatis/system/SysRoleMenuMapper.xml | 34 +
.../mybatis/system/SysUserMapper.xml | 182 +++
.../mybatis/system/SysUserPostMapper.xml | 34 +
.../mybatis/system/SysUserRoleMapper.xml | 44 +
.../mybatis/tool/GenTableColumnMapper.xml | 127 ++
.../resources/mybatis/tool/GenTableMapper.xml | 201 +++
src/main/resources/vm/java/controller.java.vm | 114 ++
src/main/resources/vm/java/domain.java.vm | 105 ++
src/main/resources/vm/java/mapper.java.vm | 91 ++
src/main/resources/vm/java/service.java.vm | 61 +
.../resources/vm/java/serviceImpl.java.vm | 166 +++
src/main/resources/vm/java/sub-domain.java.vm | 76 ++
src/main/resources/vm/js/api.js.vm | 53 +
src/main/resources/vm/sql/sql.vm | 22 +
src/main/resources/vm/vue/index-tree.vue.vm | 523 ++++++++
src/main/resources/vm/vue/index.vue.vm | 641 ++++++++++
src/main/resources/vm/xml/mapper.xml.vm | 135 ++
327 files changed, 35322 insertions(+)
create mode 100644 .gitignore
create mode 100644 LICENSE
create mode 100644 README.md
create mode 100644 bin/clean(1).bat
create mode 100644 bin/clean.bat
create mode 100644 bin/package(1)(1).bat
create mode 100644 bin/package(1).bat
create mode 100644 bin/package(2).bat
create mode 100644 bin/package.bat
create mode 100644 bin/run-tomcat(1)(1).bat
create mode 100644 bin/run-tomcat(1).bat
create mode 100644 bin/run-tomcat.bat
create mode 100644 pom.xml
create mode 100644 ry.sh
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/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/lang/UUID.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/BaseException.java
create mode 100644 src/main/java/com/ruoyi/common/exception/CustomException.java
create mode 100644 src/main/java/com/ruoyi/common/exception/DemoModeException.java
create mode 100644 src/main/java/com/ruoyi/common/exception/UtilException.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/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/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/UserPasswordNotMatchException.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/IdUtils.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/MyMetaObjectHandler.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/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/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/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/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/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/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/KaptchaTextCreator.java
create mode 100644 src/main/java/com/ruoyi/framework/config/MyBatisConfig.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/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/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/SysPermissionService.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/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/common/CaptchaController.java
create mode 100644 src/main/java/com/ruoyi/project/common/CommonController.java
create mode 100644 src/main/java/com/ruoyi/project/hit/controller/CardGameController.java
create mode 100644 src/main/java/com/ruoyi/project/hit/controller/CardGameProductController.java
create mode 100644 src/main/java/com/ruoyi/project/hit/controller/CardGameRuleController.java
create mode 100644 src/main/java/com/ruoyi/project/hit/controller/CardProductController.java
create mode 100644 src/main/java/com/ruoyi/project/hit/controller/CardUserController.java
create mode 100644 src/main/java/com/ruoyi/project/hit/controller/InfoController.java
create mode 100644 src/main/java/com/ruoyi/project/hit/controller/SystemDictController.java
create mode 100644 src/main/java/com/ruoyi/project/hit/controller/vo/ExcelCardProductVo.java
create mode 100644 src/main/java/com/ruoyi/project/hit/controller/vo/ListCardGameVo.java
create mode 100644 src/main/java/com/ruoyi/project/hit/controller/vo/ListCardProductVo.java
create mode 100644 src/main/java/com/ruoyi/project/hit/controller/vo/ListCardUserVo.java
create mode 100644 src/main/java/com/ruoyi/project/hit/controller/vo/ListInfoVo.java
create mode 100644 src/main/java/com/ruoyi/project/hit/controller/vo/TotalInfoVo.java
create mode 100644 src/main/java/com/ruoyi/project/hit/domain/CardGame.java
create mode 100644 src/main/java/com/ruoyi/project/hit/domain/CardGameProduct.java
create mode 100644 src/main/java/com/ruoyi/project/hit/domain/CardGameRule.java
create mode 100644 src/main/java/com/ruoyi/project/hit/domain/CardProduct.java
create mode 100644 src/main/java/com/ruoyi/project/hit/domain/CardUser.java
create mode 100644 src/main/java/com/ruoyi/project/hit/domain/CardUserHit.java
create mode 100644 src/main/java/com/ruoyi/project/hit/domain/SystemDict.java
create mode 100644 src/main/java/com/ruoyi/project/hit/mapper/CardGameMapper.java
create mode 100644 src/main/java/com/ruoyi/project/hit/mapper/CardGameProductMapper.java
create mode 100644 src/main/java/com/ruoyi/project/hit/mapper/CardGameRuleMapper.java
create mode 100644 src/main/java/com/ruoyi/project/hit/mapper/CardProductMapper.java
create mode 100644 src/main/java/com/ruoyi/project/hit/mapper/CardUserHitMapper.java
create mode 100644 src/main/java/com/ruoyi/project/hit/mapper/CardUserMapper.java
create mode 100644 src/main/java/com/ruoyi/project/hit/mapper/SystemDictMapper.java
create mode 100644 src/main/java/com/ruoyi/project/hit/service/CardGameProductService.java
create mode 100644 src/main/java/com/ruoyi/project/hit/service/CardGameRuleService.java
create mode 100644 src/main/java/com/ruoyi/project/hit/service/CardGameService.java
create mode 100644 src/main/java/com/ruoyi/project/hit/service/CardProductService.java
create mode 100644 src/main/java/com/ruoyi/project/hit/service/CardUserHitService.java
create mode 100644 src/main/java/com/ruoyi/project/hit/service/CardUserService.java
create mode 100644 src/main/java/com/ruoyi/project/hit/service/SystemDictService.java
create mode 100644 src/main/java/com/ruoyi/project/hit/service/impl/CardGameProductServiceImpl.java
create mode 100644 src/main/java/com/ruoyi/project/hit/service/impl/CardGameRuleServiceImpl.java
create mode 100644 src/main/java/com/ruoyi/project/hit/service/impl/CardGameServiceImpl.java
create mode 100644 src/main/java/com/ruoyi/project/hit/service/impl/CardProductServiceImpl.java
create mode 100644 src/main/java/com/ruoyi/project/hit/service/impl/CardUserHitServiceImpl.java
create mode 100644 src/main/java/com/ruoyi/project/hit/service/impl/CardUserServiceImpl.java
create mode 100644 src/main/java/com/ruoyi/project/hit/service/impl/SystemDictServiceImpl.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/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/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/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/hit/CardGameMapper.xml
create mode 100644 src/main/resources/mybatis/hit/CardGameProductMapper.xml
create mode 100644 src/main/resources/mybatis/hit/CardGameRuleMapper.xml
create mode 100644 src/main/resources/mybatis/hit/CardProductMapper.xml
create mode 100644 src/main/resources/mybatis/hit/CardUserHitMapper.xml
create mode 100644 src/main/resources/mybatis/hit/CardUserMapper.xml
create mode 100644 src/main/resources/mybatis/hit/SystemDictMapper.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/xml/mapper.xml.vm
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..fd446f4
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,43 @@
+######################################################################
+# Build Tools
+
+.gradle
+/build/
+!gradle/wrapper/gradle-wrapper.jar
+
+target/
+!.mvn/wrapper/maven-wrapper.jar
+
+######################################################################
+# IDE
+
+### STS ###
+.apt_generated
+.classpath
+.factorypath
+.project
+.settings
+.springBeans
+
+### IntelliJ IDEA ###
+.idea
+*.iws
+*.iml
+*.ipr
+
+### NetBeans ###
+nbproject/private/
+build/*
+nbbuild/
+dist/
+nbdist/
+.nb-gradle/
+
+######################################################################
+# Others
+*.log
+*.xml.versionsBackup
+
+!*/build/*.java
+!*/build/*.html
+!*/build/*.xml
\ No newline at end of file
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..8564f29
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,20 @@
+The MIT License (MIT)
+
+Copyright (c) 2018 RuoYi
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
\ No newline at end of file
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..7467e1f
--- /dev/null
+++ b/README.md
@@ -0,0 +1,84 @@
+## 平台简介
+
+* 本仓库为RuoYi-Vue的单应用版本,保持同步更新。
+* 配套前端代码地址[RuoYi-Vue-UI](https://gitee.com/y_project/RuoYi-Vue/tree/master/ruoyi-ui)。
+* 前端采用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
+
+## 演示图
+
+
+{
+ 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 的静态工厂。 使用加密的本地线程伪随机数生成器生成该 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/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..b17dfdd
--- /dev/null
+++ b/src/main/java/com/ruoyi/common/core/text/Convert.java
@@ -0,0 +1,999 @@
+package com.ruoyi.common.core.text;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.nio.ByteBuffer;
+import java.nio.charset.Charset;
+import java.text.NumberFormat;
+import java.util.Set;
+import com.ruoyi.common.utils.StringUtils;
+
+/**
+ * 类型转换器
+ *
+ * @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":
+ return true;
+ case "false":
+ return false;
+ case "yes":
+ return true;
+ case "ok":
+ return true;
+ case "no":
+ return false;
+ case "1":
+ return true;
+ 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 new BigDecimal((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[] || obj instanceof Byte[])
+ {
+ return str((Byte[]) obj, 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++)
+ {
+ s += (digit[(int) (Math.floor(n * 10 * 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/BaseException.java b/src/main/java/com/ruoyi/common/exception/BaseException.java
new file mode 100644
index 0000000..92d6ae2
--- /dev/null
+++ b/src/main/java/com/ruoyi/common/exception/BaseException.java
@@ -0,0 +1,97 @@
+package com.ruoyi.common.exception;
+
+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/CustomException.java b/src/main/java/com/ruoyi/common/exception/CustomException.java
new file mode 100644
index 0000000..d96c7ba
--- /dev/null
+++ b/src/main/java/com/ruoyi/common/exception/CustomException.java
@@ -0,0 +1,43 @@
+package com.ruoyi.common.exception;
+
+/**
+ * 自定义异常
+ *
+ * @author ruoyi
+ */
+public class CustomException extends RuntimeException
+{
+ private static final long serialVersionUID = 1L;
+
+ private Integer code;
+
+ private String message;
+
+ public CustomException(String message)
+ {
+ this.message = message;
+ }
+
+ public CustomException(String message, Integer code)
+ {
+ this.message = message;
+ this.code = code;
+ }
+
+ public CustomException(String message, Throwable e)
+ {
+ super(message, e);
+ this.message = message;
+ }
+
+ @Override
+ public String getMessage()
+ {
+ return message;
+ }
+
+ public Integer getCode()
+ {
+ return code;
+ }
+}
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/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/file/FileException.java b/src/main/java/com/ruoyi/common/exception/file/FileException.java
new file mode 100644
index 0000000..75d6dbf
--- /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.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/InvalidExtensionException.java b/src/main/java/com/ruoyi/common/exception/file/InvalidExtensionException.java
new file mode 100644
index 0000000..8f63a79
--- /dev/null
+++ b/src/main/java/com/ruoyi/common/exception/file/InvalidExtensionException.java
@@ -0,0 +1,71 @@
+package com.ruoyi.common.exception.file;
+
+import java.util.Arrays;
+import org.apache.commons.fileupload.FileUploadException;
+
+/**
+ * 文件上传 误异常类
+ *
+ * @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 : [" + filename + "], extension : [" + extension + "], allowed 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);
+ }
+ }
+}
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/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..aa015f8
--- /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.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/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/filter/RepeatableFilter.java b/src/main/java/com/ruoyi/common/filter/RepeatableFilter.java
new file mode 100644
index 0000000..ac45bfc
--- /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.equalsAnyIgnoreCase(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..bd7097f
--- /dev/null
+++ b/src/main/java/com/ruoyi/common/filter/RepeatedlyRequestWrapper.java
@@ -0,0 +1,72 @@
+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;
+
+/**
+ * 构建可重复读取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("UTF-8");
+ response.setCharacterEncoding("UTF-8");
+
+ body = HttpHelper.getBodyString(request).getBytes("UTF-8");
+ }
+
+ @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 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..b3df122
--- /dev/null
+++ b/src/main/java/com/ruoyi/common/filter/XssFilter.java
@@ -0,0 +1,97 @@
+package com.ruoyi.common.filter;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+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;
+
+/**
+ * 防止XSS攻击的过滤器
+ *
+ * @author ruoyi
+ */
+public class XssFilter implements Filter
+{
+ /**
+ * 排除链接
+ */
+ public List excludes = new ArrayList<>();
+
+ /**
+ * xss过滤开关
+ */
+ public boolean enabled = false;
+
+ @Override
+ public void init(FilterConfig filterConfig) throws ServletException
+ {
+ String tempExcludes = filterConfig.getInitParameter("excludes");
+ String tempEnabled = filterConfig.getInitParameter("enabled");
+ if (StringUtils.isNotEmpty(tempExcludes))
+ {
+ String[] url = tempExcludes.split(",");
+ for (int i = 0; url != null && i < url.length; i++)
+ {
+ excludes.add(url[i]);
+ }
+ }
+ if (StringUtils.isNotEmpty(tempEnabled))
+ {
+ enabled = Boolean.valueOf(tempEnabled);
+ }
+ }
+
+ @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)
+ {
+ if (!enabled)
+ {
+ return true;
+ }
+ if (excludes == null || excludes.isEmpty())
+ {
+ return false;
+ }
+ String url = request.getServletPath();
+ for (String pattern : excludes)
+ {
+ Pattern p = Pattern.compile("^" + pattern);
+ Matcher m = p.matcher(url);
+ if (m.find())
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @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..3c9efb2
--- /dev/null
+++ b/src/main/java/com/ruoyi/common/filter/XssHttpServletRequestWrapper.java
@@ -0,0 +1,104 @@
+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[] escapseValues = new String[length];
+ for (int i = 0; i < length; i++)
+ {
+ // 防xss攻击和过滤前后空格
+ escapseValues[i] = EscapeUtil.clean(values[i]).trim();
+ }
+ return escapseValues;
+ }
+ 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();
+ final ByteArrayInputStream bis = new ByteArrayInputStream(json.getBytes("utf-8"));
+ return new ServletInputStream()
+ {
+ @Override
+ public boolean isFinished()
+ {
+ return true;
+ }
+
+ @Override
+ public boolean isReady()
+ {
+ return true;
+ }
+
+ @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 MediaType.APPLICATION_JSON_VALUE.equalsIgnoreCase(header);
+ }
+}
\ 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..536cb3c
--- /dev/null
+++ b/src/main/java/com/ruoyi/common/utils/DateUtils.java
@@ -0,0 +1,155 @@
+package com.ruoyi.common.utils;
+
+import java.lang.management.ManagementFactory;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+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 String getDatePoor(Date endDate, Date nowDate)
+ {
+ long nd = 1000 * 24 * 60 * 60;
+ long nh = 1000 * 60 * 60;
+ long nm = 1000 * 60;
+ // long ns = 1000;
+ // 获得两个时间的毫秒时间差异
+ long diff = endDate.getTime() - nowDate.getTime();
+ // 计算差多少天
+ long day = diff / nd;
+ // 计算差多少小时
+ long hour = diff % nd / nh;
+ // 计算差多少分钟
+ long min = diff % nd % nh / nm;
+ // 计算差多少秒//输出结果
+ // long sec = diff % nd % nh % nm / ns;
+ return day + "天" + hour + "小时" + min + "分钟";
+ }
+}
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..cf2c3ff
--- /dev/null
+++ b/src/main/java/com/ruoyi/common/utils/DictUtils.java
@@ -0,0 +1,173 @@
+package com.ruoyi.common.utils;
+
+import java.util.Collection;
+import java.util.List;
+import com.ruoyi.common.constant.Constants;
+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)
+ {
+ Object cacheObj = SpringUtils.getBean(RedisCache.class).getCacheObject(getCacheKey(key));
+ if (StringUtils.isNotNull(cacheObj))
+ {
+ List dictDatas = StringUtils.cast(cacheObj);
+ return dictDatas;
+ }
+ return null;
+ }
+
+ /**
+ * 根据字典类型和字典值获取字典标签
+ *
+ * @param dictType 字典类型
+ * @param dictValue 字典值
+ * @return 字典标签
+ */
+ public static String getDictLabel(String dictType, String dictValue)
+ {
+ return getDictLabel(dictType, dictValue, SEPARATOR);
+ }
+
+ /**
+ * 根据字典类型和字典标签获取字典值
+ *
+ * @param dictType 字典类型
+ * @param dictLabel 字典标签
+ * @return 字典值
+ */
+ public static String getDictValue(String dictType, String dictLabel)
+ {
+ return getDictValue(dictType, dictLabel, SEPARATOR);
+ }
+
+ /**
+ * 根据字典类型和字典值获取字典标签
+ *
+ * @param dictType 字典类型
+ * @param dictValue 字典值
+ * @param separator 分隔符
+ * @return 字典标签
+ */
+ public static String getDictLabel(String dictType, String dictValue, String separator)
+ {
+ StringBuilder propertyString = new StringBuilder();
+ List datas = getDictCache(dictType);
+
+ if (StringUtils.containsAny(separator, dictValue) && StringUtils.isNotEmpty(datas))
+ {
+ for (SysDictData dict : datas)
+ {
+ for (String value : dictValue.split(separator))
+ {
+ if (value.equals(dict.getDictValue()))
+ {
+ propertyString.append(dict.getDictLabel() + separator);
+ break;
+ }
+ }
+ }
+ }
+ else
+ {
+ for (SysDictData dict : datas)
+ {
+ if (dictValue.equals(dict.getDictValue()))
+ {
+ return dict.getDictLabel();
+ }
+ }
+ }
+ return StringUtils.stripEnd(propertyString.toString(), separator);
+ }
+
+ /**
+ * 根据字典类型和字典标签获取字典值
+ *
+ * @param dictType 字典类型
+ * @param dictLabel 字典标签
+ * @param separator 分隔符
+ * @return 字典值
+ */
+ public static String getDictValue(String dictType, String dictLabel, String separator)
+ {
+ StringBuilder propertyString = new StringBuilder();
+ List 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() + separator);
+ break;
+ }
+ }
+ }
+ }
+ else
+ {
+ for (SysDictData dict : datas)
+ {
+ if (dictLabel.equals(dict.getDictLabel()))
+ {
+ return dict.getDictValue();
+ }
+ }
+ }
+ return StringUtils.stripEnd(propertyString.toString(), separator);
+ }
+
+ /**
+ * 清空字典缓存
+ */
+ public static void clearDictCache()
+ {
+ Collection keys = SpringUtils.getBean(RedisCache.class).keys(Constants.SYS_DICT_KEY + "*");
+ SpringUtils.getBean(RedisCache.class).deleteObject(keys);
+ }
+
+ /**
+ * 设置cache key
+ *
+ * @param configKey 参数键
+ * @return 缓存键key
+ */
+ public static String getCacheKey(String configKey)
+ {
+ return Constants.SYS_DICT_KEY + configKey;
+ }
+}
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..ad57439
--- /dev/null
+++ b/src/main/java/com/ruoyi/common/utils/ExceptionUtil.java
@@ -0,0 +1,40 @@
+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));
+ String str = sw.toString();
+ return str;
+ }
+
+ public static String getRootErrorMseeage(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/IdUtils.java b/src/main/java/com/ruoyi/common/utils/IdUtils.java
new file mode 100644
index 0000000..a21313f
--- /dev/null
+++ b/src/main/java/com/ruoyi/common/utils/IdUtils.java
@@ -0,0 +1,51 @@
+package com.ruoyi.common.utils;
+
+import com.ruoyi.common.core.lang.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/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/MyMetaObjectHandler.java b/src/main/java/com/ruoyi/common/utils/MyMetaObjectHandler.java
new file mode 100644
index 0000000..aef1e79
--- /dev/null
+++ b/src/main/java/com/ruoyi/common/utils/MyMetaObjectHandler.java
@@ -0,0 +1,34 @@
+package com.ruoyi.common.utils;
+
+import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.ibatis.reflection.MetaObject;
+import org.springframework.stereotype.Component;
+
+import java.time.LocalDateTime;
+import java.util.Date;
+
+@Slf4j
+@Component
+public class MyMetaObjectHandler implements MetaObjectHandler {
+
+ @Override
+ public void insertFill(MetaObject metaObject) {
+ log.info("start insert fill ....");
+// this.strictInsertFill(metaObject, "createTime", () -> LocalDateTime.now(), LocalDateTime.class); // 起始版本 3.3.3(推荐)
+// this.strictInsertFill(metaObject, "updateTime", () -> LocalDateTime.now(), LocalDateTime.class); // 起始版本 3.3.3(推荐)
+// this.strictInsertFill(metaObject, "delFlag", () -> 0, Integer.class); // 起始版本 3.3.3(推荐)
+ Date d=new Date();
+ this.setFieldValByName("createTime",d,metaObject);
+ this.setFieldValByName("updateTime",d,metaObject);
+ this.setFieldValByName("delFlag",0,metaObject);
+ }
+
+ @Override
+ public void updateFill(MetaObject metaObject) {
+ log.info("start update fill ....");
+ Date d=new Date();
+ this.setFieldValByName("updateTime",d,metaObject);
+ }
+}
+
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..1c86837
--- /dev/null
+++ b/src/main/java/com/ruoyi/common/utils/SecurityUtils.java
@@ -0,0 +1,90 @@
+package com.ruoyi.common.utils;
+
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
+import com.ruoyi.common.constant.HttpStatus;
+import com.ruoyi.common.exception.CustomException;
+import com.ruoyi.framework.security.LoginUser;
+
+/**
+ * 安全服务工具类
+ *
+ * @author ruoyi
+ */
+public class SecurityUtils
+{
+ /**
+ * 获取用户账户
+ **/
+ public static String getUsername()
+ {
+ try
+ {
+ return getLoginUser().getUsername();
+ }
+ catch (Exception e)
+ {
+ throw new CustomException("获取用户账户异常", HttpStatus.UNAUTHORIZED);
+ }
+ }
+
+ /**
+ * 获取用户
+ **/
+ public static LoginUser getLoginUser()
+ {
+ try
+ {
+ return (LoginUser) getAuthentication().getPrincipal();
+ }
+ catch (Exception e)
+ {
+ throw new CustomException("获取用户信息异常", 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;
+ }
+}
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..ab9ebcf
--- /dev/null
+++ b/src/main/java/com/ruoyi/common/utils/ServletUtils.java
@@ -0,0 +1,136 @@
+package com.ruoyi.common.utils;
+
+import java.io.IOException;
+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.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);
+ }
+
+ /**
+ * 获取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 待渲染的字符串
+ * @return null
+ */
+ public static String 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();
+ }
+ return null;
+ }
+
+ /**
+ * 是否是Ajax异步请求
+ *
+ * @param request
+ */
+ public static boolean isAjaxRequest(HttpServletRequest request)
+ {
+ String accept = request.getHeader("accept");
+ if (accept != null && accept.indexOf("application/json") != -1)
+ {
+ return true;
+ }
+
+ String xRequestedWith = request.getHeader("X-Requested-With");
+ if (xRequestedWith != null && xRequestedWith.indexOf("XMLHttpRequest") != -1)
+ {
+ return true;
+ }
+
+ String uri = request.getRequestURI();
+ if (StringUtils.inStringIgnoreCase(uri, ".json", ".xml"))
+ {
+ return true;
+ }
+
+ String ajax = request.getParameter("__ajax");
+ if (StringUtils.inStringIgnoreCase(ajax, "json", "xml"))
+ {
+ return true;
+ }
+ return false;
+ }
+}
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..7e3969f
--- /dev/null
+++ b/src/main/java/com/ruoyi/common/utils/StringUtils.java
@@ -0,0 +1,459 @@
+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 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);
+ }
+
+ /**
+ * 格式化文本, {} 表示占位符
+ * 此方法只是简单将占位符 {} 按照顺序替换为参数
+ * 如果想输出 {} 使用 \\转义 { 即可,如果想输出 {} 之前的 \ 使用双转义符 \\\\ 即可
+ * 例:
+ * 通常使用: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);
+ }
+
+ /**
+ * 字符串转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;
+ }
+
+ /**
+ * 下划线转驼峰命名
+ */
+ 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;
+ }
+ 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();
+ }
+
+ @SuppressWarnings("unchecked")
+ public static T cast(Object obj)
+ {
+ return (T) obj;
+ }
+}
\ 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..1934195
--- /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/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..7c2d16e
--- /dev/null
+++ b/src/main/java/com/ruoyi/common/utils/file/FileUploadUtils.java
@@ -0,0 +1,231 @@
+package com.ruoyi.common.utils.file;
+
+import java.io.File;
+import java.io.IOException;
+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.IdUtils;
+import com.ruoyi.common.utils.StringUtils;
+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 extension 上传文件类型
+ * @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 = 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);
+
+ File desc = getAbsoluteFile(baseDir, fileName);
+ file.transferTo(desc);
+ String pathFileName = getPathFileName(baseDir, fileName);
+ return pathFileName;
+ }
+
+ /**
+ * 编码文件名
+ */
+ public static final String extractFilename(MultipartFile file)
+ {
+ String fileName = file.getOriginalFilename();
+ String extension = getExtension(file);
+ fileName = DateUtils.datePath() + "/" + IdUtils.fastUUID() + "." + extension;
+ return fileName;
+ }
+
+ private static final File getAbsoluteFile(String uploadDir, String fileName) throws IOException
+ {
+ File desc = new File(uploadDir + File.separator + fileName);
+
+ if (!desc.getParentFile().exists())
+ {
+ desc.getParentFile().mkdirs();
+ }
+ if (!desc.exists())
+ {
+ desc.createNewFile();
+ }
+ return desc;
+ }
+
+ private static final String getPathFileName(String uploadDir, String fileName) throws IOException
+ {
+ int dirLastIndex = RuoYiConfig.getProfile().length() + 1;
+ String currentDir = StringUtils.substring(uploadDir, dirLastIndex);
+ String pathFileName = Constants.RESOURCE_PREFIX + "/" + currentDir + "/" + fileName;
+ return pathFileName;
+ }
+
+ /**
+ * 文件大小校验
+ *
+ * @param file 上传的文件
+ * @return
+ * @throws FileSizeLimitExceededException 如果超出最大大小
+ * @throws InvalidExtensionException
+ */
+ public static final void assertAllowed(MultipartFile file, String[] allowedExtension)
+ throws FileSizeLimitExceededException, InvalidExtensionException
+ {
+ long size = file.getSize();
+ if (DEFAULT_MAX_SIZE != -1 && 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
+ {
+ 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(file.getContentType());
+ }
+ return extension;
+ }
+}
\ No newline at end of file
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..f6aa1af
--- /dev/null
+++ b/src/main/java/com/ruoyi/common/utils/file/FileUtils.java
@@ -0,0 +1,203 @@
+package com.ruoyi.common.utils.file;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+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.lang3.ArrayUtils;
+import com.ruoyi.common.utils.StringUtils;
+
+/**
+ * 文件处理工具类
+ *
+ * @author ruoyi
+ */
+public class FileUtils extends org.apache.commons.io.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
+ {
+ if (os != null)
+ {
+ try
+ {
+ os.close();
+ }
+ catch (IOException e1)
+ {
+ e1.printStackTrace();
+ }
+ }
+ if (fis != null)
+ {
+ try
+ {
+ fis.close();
+ }
+ catch (IOException e1)
+ {
+ e1.printStackTrace();
+ }
+ }
+ }
+ }
+
+ /**
+ * 删除文件
+ *
+ * @param filePath 文件
+ * @return
+ */
+ public static boolean deleteFile(String filePath)
+ {
+ boolean flag = false;
+ File file = new File(filePath);
+ // 路径为文件且不为空则进行删除
+ if (file.isFile() && file.exists())
+ {
+ file.delete();
+ flag = true;
+ }
+ 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 真实文件名
+ * @return
+ */
+ 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.setHeader("Content-disposition", contentDispositionValue.toString());
+ }
+
+ /**
+ * 百分号编码工具方法
+ *
+ * @param s 需要百分号编码的字符串
+ * @return 百分号编码后的字符串
+ */
+ public static String percentEncode(String s) throws UnsupportedEncodingException
+ {
+ String encode = URLEncoder.encode(s, StandardCharsets.UTF_8.toString());
+ return encode.replaceAll("\\+", "%20");
+ }
+}
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..a071969
--- /dev/null
+++ b/src/main/java/com/ruoyi/common/utils/file/ImageUtils.java
@@ -0,0 +1,100 @@
+package com.ruoyi.common.utils.file;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+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 key 地址
+ * @return 字节数据
+ */
+ public static byte[] readFile(String url)
+ {
+ InputStream in = null;
+ ByteArrayOutputStream baos = 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(baos);
+ }
+ }
+}
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..6984641
--- /dev/null
+++ b/src/main/java/com/ruoyi/common/utils/file/MimeTypeUtils.java
@@ -0,0 +1,55 @@
+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[] DEFAULT_ALLOWED_EXTENSION = {
+ // 图片
+ "bmp", "gif", "jpg", "jpeg", "png",
+ // word excel powerpoint
+ "doc", "docx", "xls", "xlsx", "ppt", "pptx", "html", "htm", "txt",
+ // 压缩文件
+ "rar", "zip", "gz", "bz2",
+ // 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..bf9980a
--- /dev/null
+++ b/src/main/java/com/ruoyi/common/utils/html/EscapeUtil.java
@@ -0,0 +1,155 @@
+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)
+ {
+ int len;
+ if ((text == null) || ((len = text.length()) == 0))
+ {
+ return StringUtils.EMPTY;
+ }
+ StringBuilder buffer = new StringBuilder(len + (len >> 2));
+ char c;
+ for (int i = 0; i < len; i++)
+ {
+ c = text.charAt(i);
+ if (c < 64)
+ {
+ buffer.append(TEXT[c]);
+ }
+ else
+ {
+ buffer.append(c);
+ }
+ }
+ return buffer.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 html = "ipt>alert(\"XSS\")ipt>";
+ // String html = "<123";
+ // String html = "123>";
+ System.out.println(EscapeUtil.clean(html));
+ System.out.println(EscapeUtil.escape(html));
+ System.out.println(EscapeUtil.unescape(html));
+ }
+}
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..cd8cd4f
--- /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("([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("").append(key).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[^>]*)?>" + tag + ">"));
+ }
+ 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 "" + name + ">";
+ }
+ }
+ }
+ }
+
+ // 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..fb25d12
--- /dev/null
+++ b/src/main/java/com/ruoyi/common/utils/http/HttpHelper.java
@@ -0,0 +1,77 @@
+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.Charset;
+import javax.servlet.ServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.commons.lang.exception.ExceptionUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * 通用http工具封装
+ *
+ * @author ruoyi
+ */
+public class HttpHelper
+{
+
+ /**
+ * 将字符串渲染到客户端
+ *
+ * @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();
+ }
+ }
+
+ 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, Charset.forName("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.getFullStackTrace(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..9aee92e
--- /dev/null
+++ b/src/main/java/com/ruoyi/common/utils/http/HttpUtils.java
@@ -0,0 +1,262 @@
+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.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;
+
+/**
+ * 通用http发送方法
+ *
+ * @author ruoyi
+ */
+public class HttpUtils
+{
+ private static final Logger log = LoggerFactory.getLogger(HttpUtils.class);
+
+ /**
+ * 向指定 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 = url + "?" + param;
+ 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/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
+ 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
+ {
+ String urlNameString = url;
+ log.info("sendPost - {}", urlNameString);
+ URL realUrl = new URL(urlNameString);
+ URLConnection conn = realUrl.openConnection();
+ conn.setRequestProperty("accept", "*/*");
+ conn.setRequestProperty("connection", "Keep-Alive");
+ conn.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
+ 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(), "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/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
+ 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("ISO-8859-1"), "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..4d2041e
--- /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.fastjson.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)
+ {
+ String address = UNKNOWN;
+ // 内网不查询
+ 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 = JSONObject.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 address;
+ }
+}
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..1b3f42b
--- /dev/null
+++ b/src/main/java/com/ruoyi/common/utils/ip/IpUtils.java
@@ -0,0 +1,195 @@
+package com.ruoyi.common.utils.ip;
+
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import javax.servlet.http.HttpServletRequest;
+import com.ruoyi.common.utils.StringUtils;
+import com.ruoyi.common.utils.html.EscapeUtil;
+
+/**
+ * 获取IP方法
+ *
+ * @author ruoyi
+ */
+public class IpUtils
+{
+ 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" : EscapeUtil.clean(ip);
+ }
+
+ public static boolean internalIp(String ip)
+ {
+ byte[] addr = textToNumericFormatV4(ip);
+ return internalIp(addr) || "127.0.0.1".equals(ip);
+ }
+
+ 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;
+ }
+
+ public static String getHostIp()
+ {
+ try
+ {
+ return InetAddress.getLocalHost().getHostAddress();
+ }
+ catch (UnknownHostException e)
+ {
+ }
+ return "127.0.0.1";
+ }
+
+ public static String getHostName()
+ {
+ try
+ {
+ return InetAddress.getLocalHost().getHostName();
+ }
+ catch (UnknownHostException e)
+ {
+ }
+ return "未知";
+ }
+}
\ 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..1670ee5
--- /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