From 1b4628e52cb1e618471dac3c3131d8c466923a7d Mon Sep 17 00:00:00 2001 From: Number7 <1845377266@qq.com> Date: Tue, 23 Jul 2024 01:11:35 +0800 Subject: [PATCH] =?UTF-8?q?=E5=88=9D=E5=A7=8B=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .idea/.gitignore | 8 + .idea/discord.xml | 7 + .idea/encodings.xml | 8 + .idea/misc.xml | 26 + pom.xml | 169 +++ .../muyu/common/core/annotation/Excel.java | 176 +++ .../muyu/common/core/annotation/Excels.java | 17 + .../common/core/constant/CacheConstants.java | 58 + .../muyu/common/core/constant/Constants.java | 134 ++ .../common/core/constant/GenConstants.java | 186 +++ .../muyu/common/core/constant/HttpStatus.java | 93 ++ .../core/constant/ScheduleConstants.java | 56 + .../core/constant/SecurityConstants.java | 48 + .../core/constant/ServiceNameConstants.java | 23 + .../common/core/constant/TokenConstants.java | 24 + .../common/core/constant/UserConstants.java | 113 ++ .../core/context/SecurityContextHolder.java | 83 ++ .../com/muyu/common/core/domain/Result.java | 112 ++ .../muyu/common/core/enums/UserStatus.java | 26 + .../core/exception/CaptchaException.java | 14 + .../core/exception/CheckedException.java | 26 + .../core/exception/DemoModeException.java | 13 + .../core/exception/GlobalException.java | 51 + .../core/exception/InnerAuthException.java | 14 + .../core/exception/PreAuthorizeException.java | 13 + .../core/exception/ServiceException.java | 65 + .../common/core/exception/UtilException.java | 22 + .../exception/auth/NotLoginException.java | 14 + .../auth/NotPermissionException.java | 20 + .../core/exception/auth/NotRoleException.java | 20 + .../core/exception/base/BaseException.java | 69 + .../core/exception/file/FileException.java | 17 + .../FileNameLengthLimitExceededException.java | 14 + .../file/FileSizeLimitExceededException.java | 14 + .../exception/file/FileUploadException.java | 52 + .../file/InvalidExtensionException.java | 67 + .../core/exception/job/TaskException.java | 29 + .../user/CaptchaExpireException.java | 14 + .../core/exception/user/UserException.java | 16 + .../user/UserPasswordNotMatchException.java | 14 + .../muyu/common/core/feign/FeginConfig.java | 19 + .../com/muyu/common/core/text/CharsetKit.java | 94 ++ .../com/muyu/common/core/text/Convert.java | 903 ++++++++++++ .../muyu/common/core/text/StrFormatter.java | 77 ++ .../com/muyu/common/core/utils/DateUtils.java | 158 +++ .../muyu/common/core/utils/ExceptionUtil.java | 35 + .../com/muyu/common/core/utils/JwtUtils.java | 165 +++ .../com/muyu/common/core/utils/PageUtils.java | 32 + .../muyu/common/core/utils/ServletUtils.java | 289 ++++ .../muyu/common/core/utils/SpringUtils.java | 114 ++ .../muyu/common/core/utils/StringUtils.java | 504 +++++++ .../common/core/utils/bean/BeanUtils.java | 107 ++ .../core/utils/bean/BeanValidators.java | 22 + .../common/core/utils/file/FileTypeUtils.java | 85 ++ .../common/core/utils/file/FileUtils.java | 223 +++ .../common/core/utils/file/ImageUtils.java | 69 + .../common/core/utils/file/MimeTypeUtils.java | 56 + .../common/core/utils/html/EscapeUtil.java | 145 ++ .../common/core/utils/html/HTMLFilter.java | 498 +++++++ .../muyu/common/core/utils/ip/IpUtils.java | 331 +++++ .../core/utils/poi/ExcelHandlerAdapter.java | 23 + .../muyu/common/core/utils/poi/ExcelUtil.java | 1230 +++++++++++++++++ .../core/utils/reflect/ReflectUtils.java | 324 +++++ .../muyu/common/core/utils/sign/Base64.java | 256 ++++ .../muyu/common/core/utils/sql/SqlUtil.java | 59 + .../muyu/common/core/utils/uuid/IdUtils.java | 44 + .../com/muyu/common/core/utils/uuid/Seq.java | 78 ++ .../com/muyu/common/core/utils/uuid/UUID.java | 450 ++++++ .../core/validation/ValidationConfig.java | 14 + .../core/web/controller/BaseController.java | 129 ++ .../common/core/web/domain/BaseEntity.java | 80 ++ .../common/core/web/domain/TreeEntity.java | 47 + .../muyu/common/core/web/page/PageDomain.java | 93 ++ .../common/core/web/page/TableDataInfo.java | 45 + .../common/core/web/page/TableSupport.java | 53 + .../java/com/muyu/common/core/xss/Xss.java | 27 + .../muyu/common/core/xss/XssValidator.java | 31 + ...ot.autoconfigure.AutoConfiguration.imports | 2 + ...ot.autoconfigure.AutoConfiguration.imports | 2 + .../core/annotation/Excel$ColumnType.class | Bin 0 -> 1552 bytes .../common/core/annotation/Excel$Type.class | Bin 0 -> 1507 bytes .../muyu/common/core/annotation/Excel.class | Bin 0 -> 2053 bytes .../muyu/common/core/annotation/Excels.class | Bin 0 -> 449 bytes .../common/core/constant/CacheConstants.class | Bin 0 -> 878 bytes .../muyu/common/core/constant/Constants.class | Bin 0 -> 1774 bytes .../common/core/constant/GenConstants.class | Bin 0 -> 2727 bytes .../common/core/constant/HttpStatus.class | Bin 0 -> 905 bytes .../constant/ScheduleConstants$Status.class | Bin 0 -> 1633 bytes .../core/constant/ScheduleConstants.class | Bin 0 -> 755 bytes .../core/constant/SecurityConstants.class | Bin 0 -> 750 bytes .../core/constant/ServiceNameConstants.class | Bin 0 -> 527 bytes .../common/core/constant/TokenConstants.class | Bin 0 -> 526 bytes .../common/core/constant/UserConstants.class | Bin 0 -> 1167 bytes .../core/context/SecurityContextHolder.class | Bin 0 -> 3555 bytes .../core/domain/Result$ResultBuilder.class | Bin 0 -> 2355 bytes .../com/muyu/common/core/domain/Result.class | Bin 0 -> 6911 bytes .../muyu/common/core/enums/UserStatus.class | Bin 0 -> 1661 bytes .../core/exception/CaptchaException.class | Bin 0 -> 499 bytes .../core/exception/CheckedException.class | Bin 0 -> 1051 bytes .../core/exception/DemoModeException.class | Bin 0 -> 416 bytes .../core/exception/GlobalException.class | Bin 0 -> 1087 bytes .../core/exception/InnerAuthException.class | Bin 0 -> 509 bytes .../exception/PreAuthorizeException.class | Bin 0 -> 428 bytes .../core/exception/ServiceException.class | Bin 0 -> 1394 bytes .../common/core/exception/UtilException.class | Bin 0 -> 880 bytes .../exception/auth/NotLoginException.class | Bin 0 -> 516 bytes .../auth/NotPermissionException.class | Bin 0 -> 813 bytes .../exception/auth/NotRoleException.class | Bin 0 -> 783 bytes .../core/exception/base/BaseException.class | Bin 0 -> 1706 bytes .../core/exception/file/FileException.class | Bin 0 -> 717 bytes ...FileNameLengthLimitExceededException.class | Bin 0 -> 806 bytes .../file/FileSizeLimitExceededException.class | Bin 0 -> 764 bytes .../exception/file/FileUploadException.class | Bin 0 -> 1456 bytes ...ption$InvalidFlashExtensionException.class | Bin 0 -> 841 bytes ...ption$InvalidImageExtensionException.class | Bin 0 -> 841 bytes ...ption$InvalidMediaExtensionException.class | Bin 0 -> 841 bytes ...ption$InvalidVideoExtensionException.class | Bin 0 -> 841 bytes .../file/InvalidExtensionException.class | Bin 0 -> 2293 bytes .../exception/job/TaskException$Code.class | Bin 0 -> 1622 bytes .../core/exception/job/TaskException.class | Bin 0 -> 1205 bytes .../user/CaptchaExpireException.class | Bin 0 -> 536 bytes .../core/exception/user/UserException.class | Bin 0 -> 679 bytes .../user/UserPasswordNotMatchException.class | Bin 0 -> 560 bytes .../muyu/common/core/feign/FeginConfig.class | Bin 0 -> 647 bytes .../muyu/common/core/text/CharsetKit.class | Bin 0 -> 1890 bytes .../com/muyu/common/core/text/Convert.class | Bin 0 -> 15101 bytes .../muyu/common/core/text/StrFormatter.class | Bin 0 -> 1885 bytes .../muyu/common/core/utils/DateUtils.class | Bin 0 -> 4966 bytes .../common/core/utils/ExceptionUtil.class | Bin 0 -> 1403 bytes .../com/muyu/common/core/utils/JwtUtils.class | Bin 0 -> 4145 bytes .../muyu/common/core/utils/PageUtils.class | Bin 0 -> 1410 bytes .../muyu/common/core/utils/ServletUtils.class | Bin 0 -> 10794 bytes .../muyu/common/core/utils/SpringUtils.class | Bin 0 -> 2482 bytes .../muyu/common/core/utils/StringUtils.class | Bin 0 -> 8818 bytes .../common/core/utils/bean/BeanUtils.class | Bin 0 -> 2817 bytes .../core/utils/bean/BeanValidators.class | Bin 0 -> 1282 bytes .../core/utils/file/FileTypeUtils.class | Bin 0 -> 2080 bytes .../common/core/utils/file/FileUtils.class | Bin 0 -> 5346 bytes .../common/core/utils/file/ImageUtils.class | Bin 0 -> 2348 bytes .../core/utils/file/MimeTypeUtils.class | Bin 0 -> 1928 bytes .../common/core/utils/html/EscapeUtil.class | Bin 0 -> 3456 bytes .../common/core/utils/html/HTMLFilter.class | Bin 0 -> 14133 bytes .../muyu/common/core/utils/ip/IpUtils.class | Bin 0 -> 6889 bytes .../core/utils/poi/ExcelHandlerAdapter.class | Bin 0 -> 368 bytes .../common/core/utils/poi/ExcelUtil.class | Bin 0 -> 40349 bytes .../core/utils/reflect/ReflectUtils.class | Bin 0 -> 11322 bytes .../muyu/common/core/utils/sign/Base64.class | Bin 0 -> 4589 bytes .../muyu/common/core/utils/sql/SqlUtil.class | Bin 0 -> 1899 bytes .../muyu/common/core/utils/uuid/IdUtils.class | Bin 0 -> 729 bytes .../com/muyu/common/core/utils/uuid/Seq.class | Bin 0 -> 2349 bytes .../common/core/utils/uuid/UUID$Holder.class | Bin 0 -> 600 bytes .../muyu/common/core/utils/uuid/UUID.class | Bin 0 -> 7032 bytes .../core/validation/ValidationConfig.class | Bin 0 -> 1025 bytes .../web/controller/BaseController$1.class | Bin 0 -> 1048 bytes .../core/web/controller/BaseController.class | Bin 0 -> 3578 bytes .../domain/BaseEntity$BaseEntityBuilder.class | Bin 0 -> 3431 bytes .../BaseEntity$BaseEntityBuilderImpl.class | Bin 0 -> 1228 bytes .../common/core/web/domain/BaseEntity.class | Bin 0 -> 7075 bytes .../domain/TreeEntity$TreeEntityBuilder.class | Bin 0 -> 3447 bytes .../TreeEntity$TreeEntityBuilderImpl.class | Bin 0 -> 1618 bytes .../common/core/web/domain/TreeEntity.class | Bin 0 -> 5596 bytes .../common/core/web/page/PageDomain.class | Bin 0 -> 2701 bytes .../TableDataInfo$TableDataInfoBuilder.class | Bin 0 -> 2890 bytes .../common/core/web/page/TableDataInfo.class | Bin 0 -> 4074 bytes .../common/core/web/page/TableSupport.class | Bin 0 -> 1534 bytes .../com/muyu/common/core/xss/Xss.class | Bin 0 -> 819 bytes .../muyu/common/core/xss/XssValidator.class | Bin 0 -> 1653 bytes target/cloud-common-core-3.6.3.jar | Bin 0 -> 142681 bytes target/maven-archiver/pom.properties | 5 + .../compile/default-compile/createdFiles.lst | 88 ++ .../compile/default-compile/inputFiles.lst | 72 + 171 files changed, 9023 insertions(+) create mode 100644 .idea/.gitignore create mode 100644 .idea/discord.xml create mode 100644 .idea/encodings.xml create mode 100644 .idea/misc.xml create mode 100644 pom.xml create mode 100644 src/main/java/com/muyu/common/core/annotation/Excel.java create mode 100644 src/main/java/com/muyu/common/core/annotation/Excels.java create mode 100644 src/main/java/com/muyu/common/core/constant/CacheConstants.java create mode 100644 src/main/java/com/muyu/common/core/constant/Constants.java create mode 100644 src/main/java/com/muyu/common/core/constant/GenConstants.java create mode 100644 src/main/java/com/muyu/common/core/constant/HttpStatus.java create mode 100644 src/main/java/com/muyu/common/core/constant/ScheduleConstants.java create mode 100644 src/main/java/com/muyu/common/core/constant/SecurityConstants.java create mode 100644 src/main/java/com/muyu/common/core/constant/ServiceNameConstants.java create mode 100644 src/main/java/com/muyu/common/core/constant/TokenConstants.java create mode 100644 src/main/java/com/muyu/common/core/constant/UserConstants.java create mode 100644 src/main/java/com/muyu/common/core/context/SecurityContextHolder.java create mode 100644 src/main/java/com/muyu/common/core/domain/Result.java create mode 100644 src/main/java/com/muyu/common/core/enums/UserStatus.java create mode 100644 src/main/java/com/muyu/common/core/exception/CaptchaException.java create mode 100644 src/main/java/com/muyu/common/core/exception/CheckedException.java create mode 100644 src/main/java/com/muyu/common/core/exception/DemoModeException.java create mode 100644 src/main/java/com/muyu/common/core/exception/GlobalException.java create mode 100644 src/main/java/com/muyu/common/core/exception/InnerAuthException.java create mode 100644 src/main/java/com/muyu/common/core/exception/PreAuthorizeException.java create mode 100644 src/main/java/com/muyu/common/core/exception/ServiceException.java create mode 100644 src/main/java/com/muyu/common/core/exception/UtilException.java create mode 100644 src/main/java/com/muyu/common/core/exception/auth/NotLoginException.java create mode 100644 src/main/java/com/muyu/common/core/exception/auth/NotPermissionException.java create mode 100644 src/main/java/com/muyu/common/core/exception/auth/NotRoleException.java create mode 100644 src/main/java/com/muyu/common/core/exception/base/BaseException.java create mode 100644 src/main/java/com/muyu/common/core/exception/file/FileException.java create mode 100644 src/main/java/com/muyu/common/core/exception/file/FileNameLengthLimitExceededException.java create mode 100644 src/main/java/com/muyu/common/core/exception/file/FileSizeLimitExceededException.java create mode 100644 src/main/java/com/muyu/common/core/exception/file/FileUploadException.java create mode 100644 src/main/java/com/muyu/common/core/exception/file/InvalidExtensionException.java create mode 100644 src/main/java/com/muyu/common/core/exception/job/TaskException.java create mode 100644 src/main/java/com/muyu/common/core/exception/user/CaptchaExpireException.java create mode 100644 src/main/java/com/muyu/common/core/exception/user/UserException.java create mode 100644 src/main/java/com/muyu/common/core/exception/user/UserPasswordNotMatchException.java create mode 100644 src/main/java/com/muyu/common/core/feign/FeginConfig.java create mode 100644 src/main/java/com/muyu/common/core/text/CharsetKit.java create mode 100644 src/main/java/com/muyu/common/core/text/Convert.java create mode 100644 src/main/java/com/muyu/common/core/text/StrFormatter.java create mode 100644 src/main/java/com/muyu/common/core/utils/DateUtils.java create mode 100644 src/main/java/com/muyu/common/core/utils/ExceptionUtil.java create mode 100644 src/main/java/com/muyu/common/core/utils/JwtUtils.java create mode 100644 src/main/java/com/muyu/common/core/utils/PageUtils.java create mode 100644 src/main/java/com/muyu/common/core/utils/ServletUtils.java create mode 100644 src/main/java/com/muyu/common/core/utils/SpringUtils.java create mode 100644 src/main/java/com/muyu/common/core/utils/StringUtils.java create mode 100644 src/main/java/com/muyu/common/core/utils/bean/BeanUtils.java create mode 100644 src/main/java/com/muyu/common/core/utils/bean/BeanValidators.java create mode 100644 src/main/java/com/muyu/common/core/utils/file/FileTypeUtils.java create mode 100644 src/main/java/com/muyu/common/core/utils/file/FileUtils.java create mode 100644 src/main/java/com/muyu/common/core/utils/file/ImageUtils.java create mode 100644 src/main/java/com/muyu/common/core/utils/file/MimeTypeUtils.java create mode 100644 src/main/java/com/muyu/common/core/utils/html/EscapeUtil.java create mode 100644 src/main/java/com/muyu/common/core/utils/html/HTMLFilter.java create mode 100644 src/main/java/com/muyu/common/core/utils/ip/IpUtils.java create mode 100644 src/main/java/com/muyu/common/core/utils/poi/ExcelHandlerAdapter.java create mode 100644 src/main/java/com/muyu/common/core/utils/poi/ExcelUtil.java create mode 100644 src/main/java/com/muyu/common/core/utils/reflect/ReflectUtils.java create mode 100644 src/main/java/com/muyu/common/core/utils/sign/Base64.java create mode 100644 src/main/java/com/muyu/common/core/utils/sql/SqlUtil.java create mode 100644 src/main/java/com/muyu/common/core/utils/uuid/IdUtils.java create mode 100644 src/main/java/com/muyu/common/core/utils/uuid/Seq.java create mode 100644 src/main/java/com/muyu/common/core/utils/uuid/UUID.java create mode 100644 src/main/java/com/muyu/common/core/validation/ValidationConfig.java create mode 100644 src/main/java/com/muyu/common/core/web/controller/BaseController.java create mode 100644 src/main/java/com/muyu/common/core/web/domain/BaseEntity.java create mode 100644 src/main/java/com/muyu/common/core/web/domain/TreeEntity.java create mode 100644 src/main/java/com/muyu/common/core/web/page/PageDomain.java create mode 100644 src/main/java/com/muyu/common/core/web/page/TableDataInfo.java create mode 100644 src/main/java/com/muyu/common/core/web/page/TableSupport.java create mode 100644 src/main/java/com/muyu/common/core/xss/Xss.java create mode 100644 src/main/java/com/muyu/common/core/xss/XssValidator.java create mode 100644 src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports create mode 100644 target/classes/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports create mode 100644 target/classes/com/muyu/common/core/annotation/Excel$ColumnType.class create mode 100644 target/classes/com/muyu/common/core/annotation/Excel$Type.class create mode 100644 target/classes/com/muyu/common/core/annotation/Excel.class create mode 100644 target/classes/com/muyu/common/core/annotation/Excels.class create mode 100644 target/classes/com/muyu/common/core/constant/CacheConstants.class create mode 100644 target/classes/com/muyu/common/core/constant/Constants.class create mode 100644 target/classes/com/muyu/common/core/constant/GenConstants.class create mode 100644 target/classes/com/muyu/common/core/constant/HttpStatus.class create mode 100644 target/classes/com/muyu/common/core/constant/ScheduleConstants$Status.class create mode 100644 target/classes/com/muyu/common/core/constant/ScheduleConstants.class create mode 100644 target/classes/com/muyu/common/core/constant/SecurityConstants.class create mode 100644 target/classes/com/muyu/common/core/constant/ServiceNameConstants.class create mode 100644 target/classes/com/muyu/common/core/constant/TokenConstants.class create mode 100644 target/classes/com/muyu/common/core/constant/UserConstants.class create mode 100644 target/classes/com/muyu/common/core/context/SecurityContextHolder.class create mode 100644 target/classes/com/muyu/common/core/domain/Result$ResultBuilder.class create mode 100644 target/classes/com/muyu/common/core/domain/Result.class create mode 100644 target/classes/com/muyu/common/core/enums/UserStatus.class create mode 100644 target/classes/com/muyu/common/core/exception/CaptchaException.class create mode 100644 target/classes/com/muyu/common/core/exception/CheckedException.class create mode 100644 target/classes/com/muyu/common/core/exception/DemoModeException.class create mode 100644 target/classes/com/muyu/common/core/exception/GlobalException.class create mode 100644 target/classes/com/muyu/common/core/exception/InnerAuthException.class create mode 100644 target/classes/com/muyu/common/core/exception/PreAuthorizeException.class create mode 100644 target/classes/com/muyu/common/core/exception/ServiceException.class create mode 100644 target/classes/com/muyu/common/core/exception/UtilException.class create mode 100644 target/classes/com/muyu/common/core/exception/auth/NotLoginException.class create mode 100644 target/classes/com/muyu/common/core/exception/auth/NotPermissionException.class create mode 100644 target/classes/com/muyu/common/core/exception/auth/NotRoleException.class create mode 100644 target/classes/com/muyu/common/core/exception/base/BaseException.class create mode 100644 target/classes/com/muyu/common/core/exception/file/FileException.class create mode 100644 target/classes/com/muyu/common/core/exception/file/FileNameLengthLimitExceededException.class create mode 100644 target/classes/com/muyu/common/core/exception/file/FileSizeLimitExceededException.class create mode 100644 target/classes/com/muyu/common/core/exception/file/FileUploadException.class create mode 100644 target/classes/com/muyu/common/core/exception/file/InvalidExtensionException$InvalidFlashExtensionException.class create mode 100644 target/classes/com/muyu/common/core/exception/file/InvalidExtensionException$InvalidImageExtensionException.class create mode 100644 target/classes/com/muyu/common/core/exception/file/InvalidExtensionException$InvalidMediaExtensionException.class create mode 100644 target/classes/com/muyu/common/core/exception/file/InvalidExtensionException$InvalidVideoExtensionException.class create mode 100644 target/classes/com/muyu/common/core/exception/file/InvalidExtensionException.class create mode 100644 target/classes/com/muyu/common/core/exception/job/TaskException$Code.class create mode 100644 target/classes/com/muyu/common/core/exception/job/TaskException.class create mode 100644 target/classes/com/muyu/common/core/exception/user/CaptchaExpireException.class create mode 100644 target/classes/com/muyu/common/core/exception/user/UserException.class create mode 100644 target/classes/com/muyu/common/core/exception/user/UserPasswordNotMatchException.class create mode 100644 target/classes/com/muyu/common/core/feign/FeginConfig.class create mode 100644 target/classes/com/muyu/common/core/text/CharsetKit.class create mode 100644 target/classes/com/muyu/common/core/text/Convert.class create mode 100644 target/classes/com/muyu/common/core/text/StrFormatter.class create mode 100644 target/classes/com/muyu/common/core/utils/DateUtils.class create mode 100644 target/classes/com/muyu/common/core/utils/ExceptionUtil.class create mode 100644 target/classes/com/muyu/common/core/utils/JwtUtils.class create mode 100644 target/classes/com/muyu/common/core/utils/PageUtils.class create mode 100644 target/classes/com/muyu/common/core/utils/ServletUtils.class create mode 100644 target/classes/com/muyu/common/core/utils/SpringUtils.class create mode 100644 target/classes/com/muyu/common/core/utils/StringUtils.class create mode 100644 target/classes/com/muyu/common/core/utils/bean/BeanUtils.class create mode 100644 target/classes/com/muyu/common/core/utils/bean/BeanValidators.class create mode 100644 target/classes/com/muyu/common/core/utils/file/FileTypeUtils.class create mode 100644 target/classes/com/muyu/common/core/utils/file/FileUtils.class create mode 100644 target/classes/com/muyu/common/core/utils/file/ImageUtils.class create mode 100644 target/classes/com/muyu/common/core/utils/file/MimeTypeUtils.class create mode 100644 target/classes/com/muyu/common/core/utils/html/EscapeUtil.class create mode 100644 target/classes/com/muyu/common/core/utils/html/HTMLFilter.class create mode 100644 target/classes/com/muyu/common/core/utils/ip/IpUtils.class create mode 100644 target/classes/com/muyu/common/core/utils/poi/ExcelHandlerAdapter.class create mode 100644 target/classes/com/muyu/common/core/utils/poi/ExcelUtil.class create mode 100644 target/classes/com/muyu/common/core/utils/reflect/ReflectUtils.class create mode 100644 target/classes/com/muyu/common/core/utils/sign/Base64.class create mode 100644 target/classes/com/muyu/common/core/utils/sql/SqlUtil.class create mode 100644 target/classes/com/muyu/common/core/utils/uuid/IdUtils.class create mode 100644 target/classes/com/muyu/common/core/utils/uuid/Seq.class create mode 100644 target/classes/com/muyu/common/core/utils/uuid/UUID$Holder.class create mode 100644 target/classes/com/muyu/common/core/utils/uuid/UUID.class create mode 100644 target/classes/com/muyu/common/core/validation/ValidationConfig.class create mode 100644 target/classes/com/muyu/common/core/web/controller/BaseController$1.class create mode 100644 target/classes/com/muyu/common/core/web/controller/BaseController.class create mode 100644 target/classes/com/muyu/common/core/web/domain/BaseEntity$BaseEntityBuilder.class create mode 100644 target/classes/com/muyu/common/core/web/domain/BaseEntity$BaseEntityBuilderImpl.class create mode 100644 target/classes/com/muyu/common/core/web/domain/BaseEntity.class create mode 100644 target/classes/com/muyu/common/core/web/domain/TreeEntity$TreeEntityBuilder.class create mode 100644 target/classes/com/muyu/common/core/web/domain/TreeEntity$TreeEntityBuilderImpl.class create mode 100644 target/classes/com/muyu/common/core/web/domain/TreeEntity.class create mode 100644 target/classes/com/muyu/common/core/web/page/PageDomain.class create mode 100644 target/classes/com/muyu/common/core/web/page/TableDataInfo$TableDataInfoBuilder.class create mode 100644 target/classes/com/muyu/common/core/web/page/TableDataInfo.class create mode 100644 target/classes/com/muyu/common/core/web/page/TableSupport.class create mode 100644 target/classes/com/muyu/common/core/xss/Xss.class create mode 100644 target/classes/com/muyu/common/core/xss/XssValidator.class create mode 100644 target/cloud-common-core-3.6.3.jar create mode 100644 target/maven-archiver/pom.properties create mode 100644 target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst create mode 100644 target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..13566b8 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,8 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/discord.xml b/.idea/discord.xml new file mode 100644 index 0000000..30bab2a --- /dev/null +++ b/.idea/discord.xml @@ -0,0 +1,7 @@ + + + + + \ No newline at end of file diff --git a/.idea/encodings.xml b/.idea/encodings.xml new file mode 100644 index 0000000..63574ec --- /dev/null +++ b/.idea/encodings.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..681b54b --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + Ali-Check + + + + + + + \ No newline at end of file diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..57cf128 --- /dev/null +++ b/pom.xml @@ -0,0 +1,169 @@ + + + + com.muyu + cloud-common + 3.6.3 + + 4.0.0 + + cloud-common-core + + + cloud-common-core核心模块 + + + + + + + org.springframework.cloud + spring-cloud-starter-openfeign + + + + + org.springframework.cloud + spring-cloud-starter-loadbalancer + + + + + org.springframework + spring-context-support + + + + + org.springframework + spring-web + + + + + com.alibaba + transmittable-thread-local + + + + + com.github.pagehelper + pagehelper-spring-boot-starter + + + + + com.baomidou + mybatis-plus-spring-boot3-starter + 3.5.6 + + + com.github.jsqlparser + jsqlparser + + + org.mybatis + mybatis + + + + + + org.mybatis + mybatis + 3.5.16 + + + + + org.springframework.boot + spring-boot-starter-validation + + + + + com.fasterxml.jackson.core + jackson-databind + + + + + com.alibaba.fastjson2 + fastjson2 + + + + + io.jsonwebtoken + jjwt + + + + + org.apache.commons + commons-lang3 + + + + + commons-io + commons-io + + + + + org.apache.poi + poi-ooxml + + + + + + jakarta.servlet + jakarta.servlet-api + + + + + org.springframework.boot + spring-boot-starter-aop + + + + + + + + + + javax.annotation + javax.annotation-api + + + + + org.projectlombok + lombok + + + + + org.apache.skywalking + apm-toolkit-trace + + + + + org.apache.skywalking + apm-toolkit-logback-1.x + + + + cn.hutool + hutool-all + + + + diff --git a/src/main/java/com/muyu/common/core/annotation/Excel.java b/src/main/java/com/muyu/common/core/annotation/Excel.java new file mode 100644 index 0000000..36180e5 --- /dev/null +++ b/src/main/java/com/muyu/common/core/annotation/Excel.java @@ -0,0 +1,176 @@ +package com.muyu.common.core.annotation; + +import com.muyu.common.core.utils.poi.ExcelHandlerAdapter; +import org.apache.poi.ss.usermodel.HorizontalAlignment; +import org.apache.poi.ss.usermodel.IndexedColors; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.math.BigDecimal; + +/** + * 自定义导出Excel数据注解 + * + * @author muyu + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.FIELD) +public @interface Excel { + /** + * 导出时在excel中排序 + */ + public int sort () default Integer.MAX_VALUE; + + /** + * 导出到Excel中的名字. + */ + public String name () default ""; + + /** + * 日期格式, 如: yyyy-MM-dd + */ + public String dateFormat () default ""; + + /** + * 读取内容转表达式 (如: 0=男,1=女,2=未知) + */ + public String readConverterExp () default ""; + + /** + * 分隔符,读取字符串组内容 + */ + public String separator () default ","; + + /** + * BigDecimal 精度 默认:-1(默认不开启BigDecimal格式化) + */ + public int scale () default -1; + + /** + * BigDecimal 舍入规则 默认:BigDecimal.ROUND_HALF_EVEN + */ + public int roundingMode () default BigDecimal.ROUND_HALF_EVEN; + + /** + * 导出时在excel中每个列的高度 + */ + public double height () default 14; + + /** + * 导出时在excel中每个列的宽度 + */ + public double width () default 16; + + /** + * 文字后缀,如% 90 变成90% + */ + public String suffix () default ""; + + /** + * 当值为空时,字段的默认值 + */ + public String defaultValue () default ""; + + /** + * 提示信息 + */ + public String prompt () default ""; + + /** + * 设置只能选择不能输入的列内容. + */ + public String[] combo () default {}; + + /** + * 是否需要纵向合并单元格,应对需求:含有list集合单元格) + */ + public boolean needMerge () default false; + + /** + * 是否导出数据,应对需求:有时我们需要导出一份模板,这是标题需要但内容需要用户手工填写. + */ + public boolean isExport () default true; + + /** + * 另一个类中的属性名称,支持多级获取,以小数点隔开 + */ + public String targetAttr () default ""; + + /** + * 是否自动统计数据,在最后追加一行统计数据总和 + */ + public boolean isStatistics () default false; + + /** + * 导出类型(0数字 1字符串) + */ + public ColumnType cellType () default ColumnType.STRING; + + /** + * 导出列头背景颜色 + */ + public IndexedColors headerBackgroundColor () default IndexedColors.GREY_50_PERCENT; + + /** + * 导出列头字体颜色 + */ + public IndexedColors headerColor () default IndexedColors.WHITE; + + /** + * 导出单元格背景颜色 + */ + public IndexedColors backgroundColor () default IndexedColors.WHITE; + + /** + * 导出单元格字体颜色 + */ + public IndexedColors color () default IndexedColors.BLACK; + + /** + * 导出字段对齐方式 + */ + public HorizontalAlignment align () default HorizontalAlignment.CENTER; + + /** + * 自定义数据处理器 + */ + public Class handler () default ExcelHandlerAdapter.class; + + /** + * 自定义数据处理器参数 + */ + public String[] args () default {}; + + /** + * 字段类型(0:导出导入;1:仅导出;2:仅导入) + */ + Type type () default Type.ALL; + + public enum Type { + ALL(0), EXPORT(1), IMPORT(2); + private final int value; + + Type (int value) { + this.value = value; + } + + public int value () { + return this.value; + } + } + + public enum ColumnType { + NUMERIC(0), STRING(1), IMAGE(2); + private final int value; + + ColumnType (int value) { + this.value = value; + } + + public int value () { + return this.value; + } + } +} diff --git a/src/main/java/com/muyu/common/core/annotation/Excels.java b/src/main/java/com/muyu/common/core/annotation/Excels.java new file mode 100644 index 0000000..f8fc165 --- /dev/null +++ b/src/main/java/com/muyu/common/core/annotation/Excels.java @@ -0,0 +1,17 @@ +package com.muyu.common.core.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Excel注解集 + * + * @author muyu + */ +@Target(ElementType.FIELD) +@Retention(RetentionPolicy.RUNTIME) +public @interface Excels { + Excel[] value (); +} diff --git a/src/main/java/com/muyu/common/core/constant/CacheConstants.java b/src/main/java/com/muyu/common/core/constant/CacheConstants.java new file mode 100644 index 0000000..532c9f2 --- /dev/null +++ b/src/main/java/com/muyu/common/core/constant/CacheConstants.java @@ -0,0 +1,58 @@ +package com.muyu.common.core.constant; + +/** + * 缓存常量信息 + * + * @author muyu + */ +public class CacheConstants { + /** + * 缓存有效期,默认720(分钟) + */ + public final static long EXPIRATION = 720; + + /** + * 缓存刷新时间,默认120(分钟) + */ + public final static long REFRESH_TIME = 120; + + /** + * 密码最大错误次数 + */ + public final static int PASSWORD_MAX_RETRY_COUNT = 5; + + /** + * 密码锁定时间,默认10(分钟) + */ + public final static long PASSWORD_LOCK_TIME = 10; + + /** + * 权限缓存前缀 + */ + public final static String LOGIN_TOKEN_KEY = "login_tokens:"; + + /** + * 验证码 redis key + */ + public static final String CAPTCHA_CODE_KEY = "captcha_codes:"; + + /** + * 参数管理 cache key + */ + public static final String SYS_CONFIG_KEY = "sys_config:"; + + /** + * 字典管理 cache key + */ + public static final String SYS_DICT_KEY = "sys_dict:"; + + /** + * 登录账户密码错误次数 redis key + */ + public static final String PWD_ERR_CNT_KEY = "pwd_err_cnt:"; + + /** + * 登录IP黑名单 cache key + */ + public static final String SYS_LOGIN_BLACKIPLIST = SYS_CONFIG_KEY + "sys.login.blackIPList"; +} diff --git a/src/main/java/com/muyu/common/core/constant/Constants.java b/src/main/java/com/muyu/common/core/constant/Constants.java new file mode 100644 index 0000000..a3540fc --- /dev/null +++ b/src/main/java/com/muyu/common/core/constant/Constants.java @@ -0,0 +1,134 @@ +package com.muyu.common.core.constant; + +/** + * 通用常量信息 + * + * @author muyu + */ +public class Constants { + /** + * UTF-8 字符集 + */ + public static final String UTF8 = "UTF-8"; + + /** + * GBK 字符集 + */ + public static final String GBK = "GBK"; + + /** + * www主域 + */ + public static final String WWW = "www."; + + /** + * RMI 远程方法调用 + */ + public static final String LOOKUP_RMI = "rmi:"; + + /** + * LDAP 远程方法调用 + */ + public static final String LOOKUP_LDAP = "ldap:"; + + /** + * LDAPS 远程方法调用 + */ + public static final String LOOKUP_LDAPS = "ldaps:"; + + /** + * http请求 + */ + public static final String HTTP = "http://"; + + /** + * https请求 + */ + public static final String HTTPS = "https://"; + + /** + * 成功标记 + */ + public static final Integer SUCCESS = 200; + + /** + * 失败标记 + */ + public static final Integer FAIL = 500; + + /** + * 登录成功状态 + */ + public static final String LOGIN_SUCCESS_STATUS = "0"; + + /** + * 登录失败状态 + */ + public static final String LOGIN_FAIL_STATUS = "1"; + + /** + * 登录成功 + */ + public static final String LOGIN_SUCCESS = "Success"; + + /** + * 注销 + */ + public static final String LOGOUT = "Logout"; + + /** + * 注册 + */ + public static final String REGISTER = "Register"; + + /** + * 登录失败 + */ + public static final String LOGIN_FAIL = "Error"; + + /** + * 当前记录起始索引 + */ + public static final String PAGE_NUM = "pageNum"; + + /** + * 每页显示记录数 + */ + public static final String PAGE_SIZE = "pageSize"; + + /** + * 排序列 + */ + public static final String ORDER_BY_COLUMN = "orderByColumn"; + + /** + * 排序的方向 "desc" 或者 "asc". + */ + public static final String IS_ASC = "isAsc"; + + /** + * 验证码有效期(分钟) + */ + public static final long CAPTCHA_EXPIRATION = 2; + + /** + * 资源映射路径 前缀 + */ + public static final String RESOURCE_PREFIX = "/profile"; + + /** + * 自动识别json对象白名单配置(仅允许解析的包名,范围越小越安全) + */ + public static final String[] JSON_WHITELIST_STR = {"org.springframework", "com.muyu"}; + + /** + * 定时任务白名单配置(仅允许访问的包名,如其他需要可以自行添加) + */ + public static final String[] JOB_WHITELIST_STR = {"com.muyu"}; + + /** + * 定时任务违规的字符 + */ + public static final String[] JOB_ERROR_STR = {"java.net.URL", "javax.naming.InitialContext", "org.yaml.snakeyaml", + "org.springframework", "org.apache", "com.muyu.common.core.utils.file"}; +} diff --git a/src/main/java/com/muyu/common/core/constant/GenConstants.java b/src/main/java/com/muyu/common/core/constant/GenConstants.java new file mode 100644 index 0000000..fde9967 --- /dev/null +++ b/src/main/java/com/muyu/common/core/constant/GenConstants.java @@ -0,0 +1,186 @@ +package com.muyu.common.core.constant; + +/** + * 代码生成通用常量 + * + * @author muyu + */ +public class GenConstants { + /** + * 单表(增删改查) + */ + public static final String TPL_CRUD = "crud"; + + /** + * 树表(增删改查) + */ + public static final String TPL_TREE = "tree"; + + /** + * 主子表(增删改查) + */ + public static final String TPL_SUB = "sub"; + + /** + * 树编码字段 + */ + public static final String TREE_CODE = "treeCode"; + + /** + * 树父编码字段 + */ + public static final String TREE_PARENT_CODE = "treeParentCode"; + + /** + * 树名称字段 + */ + public static final String TREE_NAME = "treeName"; + + /** + * 上级菜单ID字段 + */ + public static final String PARENT_MENU_ID = "parentMenuId"; + + /** + * 上级菜单名称字段 + */ + public static final String PARENT_MENU_NAME = "parentMenuName"; + + /** + * 数据库字符串类型 + */ + public static final String[] COLUMNTYPE_STR = {"char", "varchar", "nvarchar", "varchar2"}; + + /** + * 数据库文本类型 + */ + public static final String[] COLUMNTYPE_TEXT = {"tinytext", "text", "mediumtext", "longtext"}; + + /** + * 数据库时间类型 + */ + public static final String[] COLUMNTYPE_TIME = {"datetime", "time", "date", "timestamp"}; + + /** + * 数据库数字类型 + */ + public static final String[] COLUMNTYPE_NUMBER = {"tinyint", "smallint", "mediumint", "int", "number", "integer", + "bigint", "float", "double", "decimal"}; + + /** + * 页面不需要编辑字段 + */ + public static final String[] COLUMNNAME_NOT_EDIT = {"id", "create_by", "create_time", "del_flag"}; + + /** + * 页面不需要显示的列表字段 + */ + public static final String[] COLUMNNAME_NOT_LIST = {"id", "create_by", "create_time", "del_flag", "update_by", + "update_time"}; + + /** + * 页面不需要查询字段 + */ + public static final String[] COLUMNNAME_NOT_QUERY = {"id", "create_by", "create_time", "del_flag", "update_by", + "update_time", "remark"}; + + /** + * Entity基类字段 + */ + public static final String[] BASE_ENTITY = {"createBy", "createTime", "updateBy", "updateTime", "remark"}; + + /** + * Tree基类字段 + */ + public static final String[] TREE_ENTITY = {"parentName", "parentId", "orderNum", "ancestors"}; + + /** + * 文本框 + */ + public static final String HTML_INPUT = "input"; + + /** + * 文本域 + */ + public static final String HTML_TEXTAREA = "textarea"; + + /** + * 下拉框 + */ + public static final String HTML_SELECT = "select"; + + /** + * 单选框 + */ + public static final String HTML_RADIO = "radio"; + + /** + * 复选框 + */ + public static final String HTML_CHECKBOX = "checkbox"; + + /** + * 日期控件 + */ + public static final String HTML_DATETIME = "datetime"; + + /** + * 图片上传控件 + */ + public static final String HTML_IMAGE_UPLOAD = "imageUpload"; + + /** + * 文件上传控件 + */ + public static final String HTML_FILE_UPLOAD = "fileUpload"; + + /** + * 富文本控件 + */ + public static final String HTML_EDITOR = "editor"; + + /** + * 字符串类型 + */ + public static final String TYPE_STRING = "String"; + + /** + * 整型 + */ + public static final String TYPE_INTEGER = "Integer"; + + /** + * 长整型 + */ + public static final String TYPE_LONG = "Long"; + + /** + * 浮点型 + */ + public static final String TYPE_DOUBLE = "Double"; + + /** + * 高精度计算类型 + */ + public static final String TYPE_BIGDECIMAL = "BigDecimal"; + + /** + * 时间类型 + */ + public static final String TYPE_DATE = "Date"; + + /** + * 模糊查询 + */ + public static final String QUERY_LIKE = "LIKE"; + + /** + * 相等查询 + */ + public static final String QUERY_EQ = "EQ"; + + /** + * 需要 + */ + public static final String REQUIRE = "1"; +} diff --git a/src/main/java/com/muyu/common/core/constant/HttpStatus.java b/src/main/java/com/muyu/common/core/constant/HttpStatus.java new file mode 100644 index 0000000..36e0783 --- /dev/null +++ b/src/main/java/com/muyu/common/core/constant/HttpStatus.java @@ -0,0 +1,93 @@ +package com.muyu.common.core.constant; + +/** + * 返回状态码 + * + * @author muyu + */ +public class HttpStatus { + /** + * 操作成功 + */ + public static final int SUCCESS = 200; + + /** + * 对象创建成功 + */ + public static final int CREATED = 201; + + /** + * 请求已经被接受 + */ + public static final int ACCEPTED = 202; + + /** + * 操作已经执行成功,但是没有返回数据 + */ + public static final int NO_CONTENT = 204; + + /** + * 资源已被移除 + */ + public static final int MOVED_PERM = 301; + + /** + * 重定向 + */ + public static final int SEE_OTHER = 303; + + /** + * 资源没有被修改 + */ + public static final int NOT_MODIFIED = 304; + + /** + * 参数列表错误(缺少,格式不匹配) + */ + public static final int BAD_REQUEST = 400; + + /** + * 未授权 + */ + public static final int UNAUTHORIZED = 401; + + /** + * 访问受限,授权过期 + */ + public static final int FORBIDDEN = 403; + + /** + * 资源,服务未找到 + */ + public static final int NOT_FOUND = 404; + + /** + * 不允许的http方法 + */ + public static final int BAD_METHOD = 405; + + /** + * 资源冲突,或者资源被锁 + */ + public static final int CONFLICT = 409; + + /** + * 不支持的数据,媒体类型 + */ + public static final int UNSUPPORTED_TYPE = 415; + + /** + * 系统内部错误 + */ + public static final int ERROR = 500; + + /** + * 接口未实现 + */ + public static final int NOT_IMPLEMENTED = 501; + + /** + * 系统警告消息 + */ + public static final int WARN = 601; +} diff --git a/src/main/java/com/muyu/common/core/constant/ScheduleConstants.java b/src/main/java/com/muyu/common/core/constant/ScheduleConstants.java new file mode 100644 index 0000000..b5bceb0 --- /dev/null +++ b/src/main/java/com/muyu/common/core/constant/ScheduleConstants.java @@ -0,0 +1,56 @@ +package com.muyu.common.core.constant; + +/** + * 任务调度通用常量 + * + * @author muyu + */ +public class ScheduleConstants { + public static final String TASK_CLASS_NAME = "TASK_CLASS_NAME"; + + /** + * 执行目标key + */ + public static final String TASK_PROPERTIES = "TASK_PROPERTIES"; + + /** + * 默认 + */ + public static final String MISFIRE_DEFAULT = "0"; + + /** + * 立即触发执行 + */ + public static final String MISFIRE_IGNORE_MISFIRES = "1"; + + /** + * 触发一次执行 + */ + public static final String MISFIRE_FIRE_AND_PROCEED = "2"; + + /** + * 不触发立即执行 + */ + public static final String MISFIRE_DO_NOTHING = "3"; + + public enum Status { + /** + * 正常 + */ + NORMAL("0"), + /** + * 暂停 + */ + PAUSE("1"); + + private String value; + + private Status (String value) { + this.value = value; + } + + public String getValue () { + return value; + } + } +} diff --git a/src/main/java/com/muyu/common/core/constant/SecurityConstants.java b/src/main/java/com/muyu/common/core/constant/SecurityConstants.java new file mode 100644 index 0000000..c862929 --- /dev/null +++ b/src/main/java/com/muyu/common/core/constant/SecurityConstants.java @@ -0,0 +1,48 @@ +package com.muyu.common.core.constant; + +/** + * 权限相关通用常量 + * + * @author muyu + */ +public class SecurityConstants { + /** + * 用户ID字段 + */ + public static final String DETAILS_USER_ID = "user_id"; + + /** + * 用户名字段 + */ + public static final String DETAILS_USERNAME = "username"; + + /** + * 授权信息字段 + */ + public static final String AUTHORIZATION_HEADER = "authorization"; + + /** + * 请求来源 + */ + public static final String FROM_SOURCE = "from-source"; + + /** + * 内部请求 + */ + public static final String INNER = "inner"; + + /** + * 用户标识 + */ + public static final String USER_KEY = "user_key"; + + /** + * 登录用户 + */ + public static final String LOGIN_USER = "login_user"; + + /** + * 角色权限 + */ + public static final String ROLE_PERMISSION = "role_permission"; +} diff --git a/src/main/java/com/muyu/common/core/constant/ServiceNameConstants.java b/src/main/java/com/muyu/common/core/constant/ServiceNameConstants.java new file mode 100644 index 0000000..bb59b70 --- /dev/null +++ b/src/main/java/com/muyu/common/core/constant/ServiceNameConstants.java @@ -0,0 +1,23 @@ +package com.muyu.common.core.constant; + +/** + * 服务名称 + * + * @author muyu + */ +public class ServiceNameConstants { + /** + * 认证服务的serviceid + */ + public static final String AUTH_SERVICE = "cloud-auth"; + + /** + * 系统模块的serviceid + */ + public static final String SYSTEM_SERVICE = "cloud-system"; + + /** + * 文件服务的serviceid + */ + public static final String FILE_SERVICE = "cloud-file"; +} diff --git a/src/main/java/com/muyu/common/core/constant/TokenConstants.java b/src/main/java/com/muyu/common/core/constant/TokenConstants.java new file mode 100644 index 0000000..38abd57 --- /dev/null +++ b/src/main/java/com/muyu/common/core/constant/TokenConstants.java @@ -0,0 +1,24 @@ +package com.muyu.common.core.constant; + +/** + * Token的Key常量 + * + * @author muyu + */ +public class TokenConstants { + /** + * 令牌自定义标识 + */ + public static final String AUTHENTICATION = "Authorization"; + + /** + * 令牌前缀 + */ + public static final String PREFIX = "Bearer "; + + /** + * 令牌秘钥 + */ + public final static String SECRET = "abcdefghijklmnsalieopadfaqawefwerstuvwxyz"; + +} diff --git a/src/main/java/com/muyu/common/core/constant/UserConstants.java b/src/main/java/com/muyu/common/core/constant/UserConstants.java new file mode 100644 index 0000000..0df401c --- /dev/null +++ b/src/main/java/com/muyu/common/core/constant/UserConstants.java @@ -0,0 +1,113 @@ +package com.muyu.common.core.constant; + +/** + * 用户常量信息 + * + * @author muyu + */ +public class UserConstants { + /** + * 平台内系统用户的唯一标志 + */ + public static final String SYS_USER = "SYS_USER"; + + /** + * 正常状态 + */ + public static final String NORMAL = "0"; + + /** + * 异常状态 + */ + public static final String EXCEPTION = "1"; + + /** + * 用户封禁状态 + */ + public static final String USER_DISABLE = "1"; + + /** + * 角色封禁状态 + */ + public static final String ROLE_DISABLE = "1"; + + /** + * 部门正常状态 + */ + public static final String DEPT_NORMAL = "0"; + + /** + * 部门停用状态 + */ + public static final String DEPT_DISABLE = "1"; + + /** + * 字典正常状态 + */ + public static final String DICT_NORMAL = "0"; + + /** + * 是否为系统默认(是) + */ + public static final String YES = "Y"; + + /** + * 是否菜单外链(是) + */ + public static final String YES_FRAME = "0"; + + /** + * 是否菜单外链(否) + */ + public static final String NO_FRAME = "1"; + + /** + * 菜单类型(目录) + */ + public static final String TYPE_DIR = "M"; + + /** + * 菜单类型(菜单) + */ + public static final String TYPE_MENU = "C"; + + /** + * 菜单类型(按钮) + */ + public static final String TYPE_BUTTON = "F"; + + /** + * Layout组件标识 + */ + public final static String LAYOUT = "Layout"; + + /** + * ParentView组件标识 + */ + public final static String PARENT_VIEW = "ParentView"; + + /** + * InnerLink组件标识 + */ + public final static String INNER_LINK = "InnerLink"; + + /** + * 校验是否唯一的返回标识 + */ + public final static boolean UNIQUE = true; + public final static boolean NOT_UNIQUE = false; + + /** + * 用户名长度限制 + */ + public static final int USERNAME_MIN_LENGTH = 2; + + public static final int USERNAME_MAX_LENGTH = 20; + + /** + * 密码长度限制 + */ + public static final int PASSWORD_MIN_LENGTH = 5; + + public static final int PASSWORD_MAX_LENGTH = 20; +} diff --git a/src/main/java/com/muyu/common/core/context/SecurityContextHolder.java b/src/main/java/com/muyu/common/core/context/SecurityContextHolder.java new file mode 100644 index 0000000..80ea42b --- /dev/null +++ b/src/main/java/com/muyu/common/core/context/SecurityContextHolder.java @@ -0,0 +1,83 @@ +package com.muyu.common.core.context; + +import com.alibaba.ttl.TransmittableThreadLocal; +import com.muyu.common.core.constant.SecurityConstants; +import com.muyu.common.core.text.Convert; +import com.muyu.common.core.utils.StringUtils; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * 获取当前线程变量中的 用户id、用户名称、Token等信息 + * 注意: 必须在网关通过请求头的方法传入,同时在HeaderInterceptor拦截器设置值。 否则这里无法获取 + * + * @author muyu + */ +public class SecurityContextHolder { + private static final TransmittableThreadLocal> THREAD_LOCAL = new TransmittableThreadLocal<>(); + + public static void set (String key, Object value) { + Map map = getLocalMap(); + map.put(key, value == null ? StringUtils.EMPTY : value); + } + + public static String get (String key) { + Map map = getLocalMap(); + return Convert.toStr(map.getOrDefault(key, StringUtils.EMPTY)); + } + + public static T get (String key, Class clazz) { + Map map = getLocalMap(); + return StringUtils.cast(map.getOrDefault(key, null)); + } + + public static Map getLocalMap () { + Map map = THREAD_LOCAL.get(); + if (map == null) { + map = new ConcurrentHashMap(); + THREAD_LOCAL.set(map); + } + return map; + } + + public static void setLocalMap (Map threadLocalMap) { + THREAD_LOCAL.set(threadLocalMap); + } + + public static Long getUserId () { + return Convert.toLong(get(SecurityConstants.DETAILS_USER_ID), 0L); + } + + public static void setUserId (String account) { + set(SecurityConstants.DETAILS_USER_ID, account); + } + + public static String getUserName () { + return get(SecurityConstants.DETAILS_USERNAME); + } + + public static void setUserName (String username) { + set(SecurityConstants.DETAILS_USERNAME, username); + } + + public static String getUserKey () { + return get(SecurityConstants.USER_KEY); + } + + public static void setUserKey (String userKey) { + set(SecurityConstants.USER_KEY, userKey); + } + + public static String getPermission () { + return get(SecurityConstants.ROLE_PERMISSION); + } + + public static void setPermission (String permissions) { + set(SecurityConstants.ROLE_PERMISSION, permissions); + } + + public static void remove () { + THREAD_LOCAL.remove(); + } +} diff --git a/src/main/java/com/muyu/common/core/domain/Result.java b/src/main/java/com/muyu/common/core/domain/Result.java new file mode 100644 index 0000000..70faa40 --- /dev/null +++ b/src/main/java/com/muyu/common/core/domain/Result.java @@ -0,0 +1,112 @@ +package com.muyu.common.core.domain; + +import com.muyu.common.core.constant.Constants; +import com.muyu.common.core.constant.HttpStatus; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; + +/** + * 响应信息主体 + * + * @author muyu + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class Result implements Serializable { + /** + * 成功 + */ + public static final int SUCCESS = Constants.SUCCESS; + /** + * 失败 + */ + public static final int FAIL = Constants.FAIL; + /** + * 警告 + */ + public static final int WARN = HttpStatus.WARN; + + private static final long serialVersionUID = 1L; + private int code; + + private String msg; + + private T data; + + public static Result success () { + return restResult(null, SUCCESS, null); + } + + public static Result success (T data) { + return restResult(data, SUCCESS, null); + } + + public static Result success (T data, String msg) { + return restResult(data, SUCCESS, msg); + } + + public static Result error () { + return restResult(null, FAIL, null); + } + + public static Result error (String msg) { + return restResult(null, FAIL, msg); + } + + public static Result error (T data) { + return restResult(data, FAIL, null); + } + + public static Result error (T data, String msg) { + return restResult(data, FAIL, msg); + } + + public static Result error (int code, String msg) { + return restResult(null, code, msg); + } + + + + public static Result warn () { + return restResult(null, WARN, null); + } + + public static Result warn (String msg) { + return restResult(null, WARN, msg); + } + + public static Result warn (T data) { + return restResult(data, WARN, null); + } + + public static Result warn (T data, String msg) { + return restResult(data, WARN, msg); + } + + public static Result warn (int code, String msg) { + return restResult(null, code, msg); + } + + private static Result restResult (T data, int code, String msg) { + return Result.builder() + .code(code) + .data(data) + .msg(msg) + .build(); + } + + public static Boolean isError (Result ret) { + return !isSuccess(ret); + } + + public static Boolean isSuccess (Result ret) { + return Result.SUCCESS == ret.getCode(); + } + +} diff --git a/src/main/java/com/muyu/common/core/enums/UserStatus.java b/src/main/java/com/muyu/common/core/enums/UserStatus.java new file mode 100644 index 0000000..32ff39a --- /dev/null +++ b/src/main/java/com/muyu/common/core/enums/UserStatus.java @@ -0,0 +1,26 @@ +package com.muyu.common.core.enums; + +/** + * 用户状态 + * + * @author muyu + */ +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/muyu/common/core/exception/CaptchaException.java b/src/main/java/com/muyu/common/core/exception/CaptchaException.java new file mode 100644 index 0000000..eb32d0b --- /dev/null +++ b/src/main/java/com/muyu/common/core/exception/CaptchaException.java @@ -0,0 +1,14 @@ +package com.muyu.common.core.exception; + +/** + * 验证码错误异常类 + * + * @author muyu + */ +public class CaptchaException extends RuntimeException { + private static final long serialVersionUID = 1L; + + public CaptchaException (String msg) { + super(msg); + } +} diff --git a/src/main/java/com/muyu/common/core/exception/CheckedException.java b/src/main/java/com/muyu/common/core/exception/CheckedException.java new file mode 100644 index 0000000..4f12893 --- /dev/null +++ b/src/main/java/com/muyu/common/core/exception/CheckedException.java @@ -0,0 +1,26 @@ +package com.muyu.common.core.exception; + +/** + * 检查异常 + * + * @author muyu + */ +public class CheckedException extends RuntimeException { + private static final long serialVersionUID = 1L; + + public CheckedException (String message) { + super(message); + } + + public CheckedException (Throwable cause) { + super(cause); + } + + public CheckedException (String message, Throwable cause) { + super(message, cause); + } + + public CheckedException (String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { + super(message, cause, enableSuppression, writableStackTrace); + } +} diff --git a/src/main/java/com/muyu/common/core/exception/DemoModeException.java b/src/main/java/com/muyu/common/core/exception/DemoModeException.java new file mode 100644 index 0000000..82249cf --- /dev/null +++ b/src/main/java/com/muyu/common/core/exception/DemoModeException.java @@ -0,0 +1,13 @@ +package com.muyu.common.core.exception; + +/** + * 演示模式异常 + * + * @author muyu + */ +public class DemoModeException extends RuntimeException { + private static final long serialVersionUID = 1L; + + public DemoModeException () { + } +} diff --git a/src/main/java/com/muyu/common/core/exception/GlobalException.java b/src/main/java/com/muyu/common/core/exception/GlobalException.java new file mode 100644 index 0000000..b14e03c --- /dev/null +++ b/src/main/java/com/muyu/common/core/exception/GlobalException.java @@ -0,0 +1,51 @@ +package com.muyu.common.core.exception; + +/** + * 全局异常 + * + * @author muyu + */ +public class GlobalException extends RuntimeException { + private static final long serialVersionUID = 1L; + + /** + * 错误提示 + */ + private String message; + + /** + * 错误明细,内部调试错误 + *

+ * 和 {@link CommonResult#getDetailMessage()} 一致的设计 + */ + private String detailMessage; + + /** + * 空构造方法,避免反序列化问题 + */ + public GlobalException () { + } + + public GlobalException (String message) { + this.message = message; + } + + public String getDetailMessage () { + return detailMessage; + } + + public GlobalException setDetailMessage (String detailMessage) { + this.detailMessage = detailMessage; + return this; + } + + @Override + public String getMessage () { + return message; + } + + public GlobalException setMessage (String message) { + this.message = message; + return this; + } +} diff --git a/src/main/java/com/muyu/common/core/exception/InnerAuthException.java b/src/main/java/com/muyu/common/core/exception/InnerAuthException.java new file mode 100644 index 0000000..f211c7f --- /dev/null +++ b/src/main/java/com/muyu/common/core/exception/InnerAuthException.java @@ -0,0 +1,14 @@ +package com.muyu.common.core.exception; + +/** + * 内部认证异常 + * + * @author muyu + */ +public class InnerAuthException extends RuntimeException { + private static final long serialVersionUID = 1L; + + public InnerAuthException (String message) { + super(message); + } +} diff --git a/src/main/java/com/muyu/common/core/exception/PreAuthorizeException.java b/src/main/java/com/muyu/common/core/exception/PreAuthorizeException.java new file mode 100644 index 0000000..6cb8636 --- /dev/null +++ b/src/main/java/com/muyu/common/core/exception/PreAuthorizeException.java @@ -0,0 +1,13 @@ +package com.muyu.common.core.exception; + +/** + * 权限异常 + * + * @author muyu + */ +public class PreAuthorizeException extends RuntimeException { + private static final long serialVersionUID = 1L; + + public PreAuthorizeException () { + } +} diff --git a/src/main/java/com/muyu/common/core/exception/ServiceException.java b/src/main/java/com/muyu/common/core/exception/ServiceException.java new file mode 100644 index 0000000..5039bc0 --- /dev/null +++ b/src/main/java/com/muyu/common/core/exception/ServiceException.java @@ -0,0 +1,65 @@ +package com.muyu.common.core.exception; + +/** + * 业务异常 + * + * @author muyu + */ +public final class ServiceException extends RuntimeException { + private static final long serialVersionUID = 1L; + + /** + * 错误码 + */ + private Integer code; + + /** + * 错误提示 + */ + private String message; + + /** + * 错误明细,内部调试错误 + *

+ * 和 {@link CommonResult#getDetailMessage()} 一致的设计 + */ + private String detailMessage; + + /** + * 空构造方法,避免反序列化问题 + */ + public ServiceException () { + } + + public ServiceException (String message) { + this.message = message; + } + + public ServiceException (String message, Integer code) { + this.message = message; + this.code = code; + } + + public String getDetailMessage () { + return detailMessage; + } + + public ServiceException setDetailMessage (String detailMessage) { + this.detailMessage = detailMessage; + return this; + } + + @Override + public String getMessage () { + return message; + } + + public ServiceException setMessage (String message) { + this.message = message; + return this; + } + + public Integer getCode () { + return code; + } +} diff --git a/src/main/java/com/muyu/common/core/exception/UtilException.java b/src/main/java/com/muyu/common/core/exception/UtilException.java new file mode 100644 index 0000000..8de4bbf --- /dev/null +++ b/src/main/java/com/muyu/common/core/exception/UtilException.java @@ -0,0 +1,22 @@ +package com.muyu.common.core.exception; + +/** + * 工具类异常 + * + * @author muyu + */ +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/muyu/common/core/exception/auth/NotLoginException.java b/src/main/java/com/muyu/common/core/exception/auth/NotLoginException.java new file mode 100644 index 0000000..40293bf --- /dev/null +++ b/src/main/java/com/muyu/common/core/exception/auth/NotLoginException.java @@ -0,0 +1,14 @@ +package com.muyu.common.core.exception.auth; + +/** + * 未能通过的登录认证异常 + * + * @author muyu + */ +public class NotLoginException extends RuntimeException { + private static final long serialVersionUID = 1L; + + public NotLoginException (String message) { + super(message); + } +} diff --git a/src/main/java/com/muyu/common/core/exception/auth/NotPermissionException.java b/src/main/java/com/muyu/common/core/exception/auth/NotPermissionException.java new file mode 100644 index 0000000..e464840 --- /dev/null +++ b/src/main/java/com/muyu/common/core/exception/auth/NotPermissionException.java @@ -0,0 +1,20 @@ +package com.muyu.common.core.exception.auth; + +import org.apache.commons.lang3.StringUtils; + +/** + * 未能通过的权限认证异常 + * + * @author muyu + */ +public class NotPermissionException extends RuntimeException { + private static final long serialVersionUID = 1L; + + public NotPermissionException (String permission) { + super(permission); + } + + public NotPermissionException (String[] permissions) { + super(StringUtils.join(permissions, ",")); + } +} diff --git a/src/main/java/com/muyu/common/core/exception/auth/NotRoleException.java b/src/main/java/com/muyu/common/core/exception/auth/NotRoleException.java new file mode 100644 index 0000000..53a1522 --- /dev/null +++ b/src/main/java/com/muyu/common/core/exception/auth/NotRoleException.java @@ -0,0 +1,20 @@ +package com.muyu.common.core.exception.auth; + +import org.apache.commons.lang3.StringUtils; + +/** + * 未能通过的角色认证异常 + * + * @author muyu + */ +public class NotRoleException extends RuntimeException { + private static final long serialVersionUID = 1L; + + public NotRoleException (String role) { + super(role); + } + + public NotRoleException (String[] roles) { + super(StringUtils.join(roles, ",")); + } +} diff --git a/src/main/java/com/muyu/common/core/exception/base/BaseException.java b/src/main/java/com/muyu/common/core/exception/base/BaseException.java new file mode 100644 index 0000000..9bb1356 --- /dev/null +++ b/src/main/java/com/muyu/common/core/exception/base/BaseException.java @@ -0,0 +1,69 @@ +package com.muyu.common.core.exception.base; + +/** + * 基础异常 + * + * @author muyu + */ +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); + } + + 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/muyu/common/core/exception/file/FileException.java b/src/main/java/com/muyu/common/core/exception/file/FileException.java new file mode 100644 index 0000000..ae2e184 --- /dev/null +++ b/src/main/java/com/muyu/common/core/exception/file/FileException.java @@ -0,0 +1,17 @@ +package com.muyu.common.core.exception.file; + +import com.muyu.common.core.exception.base.BaseException; + +/** + * 文件信息异常类 + * + * @author muyu + */ +public class FileException extends BaseException { + private static final long serialVersionUID = 1L; + + public FileException (String code, Object[] args, String msg) { + super("file", code, args, msg); + } + +} diff --git a/src/main/java/com/muyu/common/core/exception/file/FileNameLengthLimitExceededException.java b/src/main/java/com/muyu/common/core/exception/file/FileNameLengthLimitExceededException.java new file mode 100644 index 0000000..3a85df3 --- /dev/null +++ b/src/main/java/com/muyu/common/core/exception/file/FileNameLengthLimitExceededException.java @@ -0,0 +1,14 @@ +package com.muyu.common.core.exception.file; + +/** + * 文件名称超长限制异常类 + * + * @author muyu + */ +public class FileNameLengthLimitExceededException extends FileException { + private static final long serialVersionUID = 1L; + + public FileNameLengthLimitExceededException (int defaultFileNameLength) { + super("upload.filename.exceed.length", new Object[]{defaultFileNameLength}, "the filename is too long"); + } +} diff --git a/src/main/java/com/muyu/common/core/exception/file/FileSizeLimitExceededException.java b/src/main/java/com/muyu/common/core/exception/file/FileSizeLimitExceededException.java new file mode 100644 index 0000000..7570be5 --- /dev/null +++ b/src/main/java/com/muyu/common/core/exception/file/FileSizeLimitExceededException.java @@ -0,0 +1,14 @@ +package com.muyu.common.core.exception.file; + +/** + * 文件名大小限制异常类 + * + * @author muyu + */ +public class FileSizeLimitExceededException extends FileException { + private static final long serialVersionUID = 1L; + + public FileSizeLimitExceededException (long defaultMaxSize) { + super("upload.exceed.maxSize", new Object[]{defaultMaxSize}, "the filesize is too large"); + } +} diff --git a/src/main/java/com/muyu/common/core/exception/file/FileUploadException.java b/src/main/java/com/muyu/common/core/exception/file/FileUploadException.java new file mode 100644 index 0000000..94341ab --- /dev/null +++ b/src/main/java/com/muyu/common/core/exception/file/FileUploadException.java @@ -0,0 +1,52 @@ +package com.muyu.common.core.exception.file; + +import java.io.PrintStream; +import java.io.PrintWriter; + +/** + * 文件上传异常类 + * + * @author muyu + */ +public class FileUploadException extends Exception { + + private static final long serialVersionUID = 1L; + + private final Throwable cause; + + public FileUploadException () { + this(null, null); + } + + public FileUploadException (final String msg) { + this(msg, null); + } + + public FileUploadException (String msg, Throwable cause) { + super(msg); + this.cause = cause; + } + + @Override + public void printStackTrace (PrintStream stream) { + super.printStackTrace(stream); + if (cause != null) { + stream.println("Caused by:"); + cause.printStackTrace(stream); + } + } + + @Override + public void printStackTrace (PrintWriter writer) { + super.printStackTrace(writer); + if (cause != null) { + writer.println("Caused by:"); + cause.printStackTrace(writer); + } + } + + @Override + public Throwable getCause () { + return cause; + } +} diff --git a/src/main/java/com/muyu/common/core/exception/file/InvalidExtensionException.java b/src/main/java/com/muyu/common/core/exception/file/InvalidExtensionException.java new file mode 100644 index 0000000..3a993c2 --- /dev/null +++ b/src/main/java/com/muyu/common/core/exception/file/InvalidExtensionException.java @@ -0,0 +1,67 @@ +package com.muyu.common.core.exception.file; + +import java.util.Arrays; + +/** + * 文件上传 误异常类 + * + * @author muyu + */ +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); + } + } + + public static class InvalidVideoExtensionException extends InvalidExtensionException { + private static final long serialVersionUID = 1L; + + public InvalidVideoExtensionException (String[] allowedExtension, String extension, String filename) { + super(allowedExtension, extension, filename); + } + } +} diff --git a/src/main/java/com/muyu/common/core/exception/job/TaskException.java b/src/main/java/com/muyu/common/core/exception/job/TaskException.java new file mode 100644 index 0000000..aee364a --- /dev/null +++ b/src/main/java/com/muyu/common/core/exception/job/TaskException.java @@ -0,0 +1,29 @@ +package com.muyu.common.core.exception.job; + +/** + * 计划策略异常 + * + * @author muyu + */ +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 + } +} diff --git a/src/main/java/com/muyu/common/core/exception/user/CaptchaExpireException.java b/src/main/java/com/muyu/common/core/exception/user/CaptchaExpireException.java new file mode 100644 index 0000000..a95a57b --- /dev/null +++ b/src/main/java/com/muyu/common/core/exception/user/CaptchaExpireException.java @@ -0,0 +1,14 @@ +package com.muyu.common.core.exception.user; + +/** + * 验证码失效异常类 + * + * @author muyu + */ +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/muyu/common/core/exception/user/UserException.java b/src/main/java/com/muyu/common/core/exception/user/UserException.java new file mode 100644 index 0000000..f113749 --- /dev/null +++ b/src/main/java/com/muyu/common/core/exception/user/UserException.java @@ -0,0 +1,16 @@ +package com.muyu.common.core.exception.user; + +import com.muyu.common.core.exception.base.BaseException; + +/** + * 用户信息异常类 + * + * @author muyu + */ +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/muyu/common/core/exception/user/UserPasswordNotMatchException.java b/src/main/java/com/muyu/common/core/exception/user/UserPasswordNotMatchException.java new file mode 100644 index 0000000..7615cda --- /dev/null +++ b/src/main/java/com/muyu/common/core/exception/user/UserPasswordNotMatchException.java @@ -0,0 +1,14 @@ +package com.muyu.common.core.exception.user; + +/** + * 用户密码不正确或不符合规范异常类 + * + * @author muyu + */ +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/muyu/common/core/feign/FeginConfig.java b/src/main/java/com/muyu/common/core/feign/FeginConfig.java new file mode 100644 index 0000000..77a12fe --- /dev/null +++ b/src/main/java/com/muyu/common/core/feign/FeginConfig.java @@ -0,0 +1,19 @@ +package com.muyu.common.core.feign; + +import feign.Contract; +import org.springframework.cloud.openfeign.support.SpringMvcContract; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class FeginConfig { +// @Bean +// public Contract feignConfiguration() { +// return new feign.Contract.Default(); +// } + + @Bean + public Contract feignConfiguration() { + return new SpringMvcContract(); + } +} diff --git a/src/main/java/com/muyu/common/core/text/CharsetKit.java b/src/main/java/com/muyu/common/core/text/CharsetKit.java new file mode 100644 index 0000000..fe5e9fa --- /dev/null +++ b/src/main/java/com/muyu/common/core/text/CharsetKit.java @@ -0,0 +1,94 @@ +package com.muyu.common.core.text; + +import com.muyu.common.core.utils.StringUtils; + +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; + +/** + * 字符集工具类 + * + * @author muyu + */ +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/muyu/common/core/text/Convert.java b/src/main/java/com/muyu/common/core/text/Convert.java new file mode 100644 index 0000000..fb57cb9 --- /dev/null +++ b/src/main/java/com/muyu/common/core/text/Convert.java @@ -0,0 +1,903 @@ +package com.muyu.common.core.text; + +import com.muyu.common.core.utils.StringUtils; + +import java.math.BigDecimal; +import java.math.BigInteger; +import java.math.RoundingMode; +import java.nio.ByteBuffer; +import java.nio.charset.Charset; +import java.text.NumberFormat; +import java.util.Set; + +/** + * 类型转换器 + * + * @author muyu + */ +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 str 被转换的值 + * + * @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 str 被转换的值 + * + * @return 结果 + */ + public static String[] toStrArray (String split, String str) { + return str.split(split); + } + + /** + * 转换为long
+ * 如果给定的值为空,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * + * @return 结果 + */ + public static Long toLong (Object value, Long defaultValue) { + if (value == null) { + return defaultValue; + } + if (value instanceof Long) { + return (Long) value; + } + if (value instanceof Number) { + return ((Number) value).longValue(); + } + final String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) { + return defaultValue; + } + try { + // 支持科学计数法 + return new BigDecimal(valueStr.trim()).longValue(); + } catch (Exception e) { + return defaultValue; + } + } + + /** + * 转换为long
+ * 如果给定的值为null,或者转换失败,返回默认值null
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * + * @return 结果 + */ + public static Long toLong (Object value) { + return toLong(value, null); + } + + /** + * 转换为double
+ * 如果给定的值为空,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * + * @return 结果 + */ + public static Double toDouble (Object value, Double defaultValue) { + if (value == null) { + return defaultValue; + } + if (value instanceof Double) { + return (Double) value; + } + if (value instanceof Number) { + return ((Number) value).doubleValue(); + } + final String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) { + return defaultValue; + } + try { + // 支持科学计数法 + return new BigDecimal(valueStr.trim()).doubleValue(); + } catch (Exception e) { + return defaultValue; + } + } + + /** + * 转换为double
+ * 如果给定的值为空,或者转换失败,返回默认值null
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * + * @return 结果 + */ + public static Double toDouble (Object value) { + return toDouble(value, null); + } + + /** + * 转换为Float
+ * 如果给定的值为空,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * + * @return 结果 + */ + public static Float toFloat (Object value, Float defaultValue) { + if (value == null) { + return defaultValue; + } + if (value instanceof Float) { + return (Float) value; + } + if (value instanceof Number) { + return ((Number) value).floatValue(); + } + final String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) { + return defaultValue; + } + try { + return Float.parseFloat(valueStr.trim()); + } catch (Exception e) { + return defaultValue; + } + } + + /** + * 转换为Float
+ * 如果给定的值为空,或者转换失败,返回默认值null
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * + * @return 结果 + */ + public static Float toFloat (Object value) { + return toFloat(value, null); + } + + /** + * 转换为boolean
+ * String支持的值为:true、false、yes、ok、no,1,0 如果给定的值为空,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * + * @return 结果 + */ + public static Boolean toBool (Object value, Boolean defaultValue) { + if (value == null) { + return defaultValue; + } + if (value instanceof Boolean) { + return (Boolean) value; + } + String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) { + return defaultValue; + } + valueStr = valueStr.trim().toLowerCase(); + switch (valueStr) { + case "true": + case "yes": + case "ok": + case "1": + return true; + case "false": + case "no": + case "0": + return false; + default: + return defaultValue; + } + } + + /** + * 转换为boolean
+ * 如果给定的值为空,或者转换失败,返回默认值null
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * + * @return 结果 + */ + public static Boolean toBool (Object value) { + return toBool(value, null); + } + + /** + * 转换为Enum对象
+ * 如果给定的值为空,或者转换失败,返回默认值
+ * + * @param clazz Enum的Class + * @param value 值 + * @param defaultValue 默认值 + * + * @return Enum + */ + public static > E toEnum (Class clazz, Object value, E defaultValue) { + if (value == null) { + return defaultValue; + } + if (clazz.isAssignableFrom(value.getClass())) { + @SuppressWarnings("unchecked") + E myE = (E) value; + return myE; + } + final String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) { + return defaultValue; + } + try { + return Enum.valueOf(clazz, valueStr); + } catch (Exception e) { + return defaultValue; + } + } + + /** + * 转换为Enum对象
+ * 如果给定的值为空,或者转换失败,返回默认值null
+ * + * @param clazz Enum的Class + * @param value 值 + * + * @return Enum + */ + public static > E toEnum (Class clazz, Object value) { + return toEnum(clazz, value, null); + } + + /** + * 转换为BigInteger
+ * 如果给定的值为空,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * + * @return 结果 + */ + public static BigInteger toBigInteger (Object value, BigInteger defaultValue) { + if (value == null) { + return defaultValue; + } + if (value instanceof BigInteger) { + return (BigInteger) value; + } + if (value instanceof Long) { + return BigInteger.valueOf((Long) value); + } + final String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) { + return defaultValue; + } + try { + return new BigInteger(valueStr); + } catch (Exception e) { + return defaultValue; + } + } + + /** + * 转换为BigInteger
+ * 如果给定的值为空,或者转换失败,返回默认值null
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * + * @return 结果 + */ + public static BigInteger toBigInteger (Object value) { + return toBigInteger(value, null); + } + + /** + * 转换为BigDecimal
+ * 如果给定的值为空,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * + * @return 结果 + */ + public static BigDecimal toBigDecimal (Object value, BigDecimal defaultValue) { + if (value == null) { + return defaultValue; + } + if (value instanceof BigDecimal) { + return (BigDecimal) value; + } + if (value instanceof Long) { + return new BigDecimal((Long) value); + } + if (value instanceof Double) { + return BigDecimal.valueOf((Double) value); + } + if (value instanceof Integer) { + return new BigDecimal((Integer) value); + } + final String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) { + return defaultValue; + } + try { + return new BigDecimal(valueStr); + } catch (Exception e) { + return defaultValue; + } + } + + /** + * 转换为BigDecimal
+ * 如果给定的值为空,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * + * @return 结果 + */ + public static BigDecimal toBigDecimal (Object value) { + return toBigDecimal(value, null); + } + + /** + * 将对象转为字符串
+ * 1、Byte数组和ByteBuffer会被转换为对应字符串的数组 2、对象数组会调用Arrays.toString方法 + * + * @param obj 对象 + * + * @return 字符串 + */ + public static String utf8Str (Object obj) { + return str(obj, CharsetKit.CHARSET_UTF_8); + } + + /** + * 将对象转为字符串
+ * 1、Byte数组和ByteBuffer会被转换为对应字符串的数组 2、对象数组会调用Arrays.toString方法 + * + * @param obj 对象 + * @param charsetName 字符集 + * + * @return 字符串 + */ + public static String str (Object obj, String charsetName) { + return str(obj, Charset.forName(charsetName)); + } + + /** + * 将对象转为字符串
+ * 1、Byte数组和ByteBuffer会被转换为对应字符串的数组 2、对象数组会调用Arrays.toString方法 + * + * @param obj 对象 + * @param charset 字符集 + * + * @return 字符串 + */ + public static String str (Object obj, Charset charset) { + if (null == obj) { + return null; + } + + if (obj instanceof String) { + return (String) obj; + } else if (obj instanceof byte[] || obj instanceof Byte[]) { + if (obj instanceof byte[]) { + return str((byte[]) obj, charset); + } else { + Byte[] bytes = (Byte[]) obj; + int length = bytes.length; + byte[] dest = new byte[length]; + for (int i = 0 ; i < length ; i++) { + dest[i] = bytes[i]; + } + return str(dest, 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); + } + } + return new String(c); + } + + /** + * 数字金额大写转换 先写个完整的然后将如零拾替换成零 + * + * @param n 数字 + * + * @return 中文大写数字 + */ + public static String digitUppercase (double n) { + String[] fraction = {"角", "分"}; + String[] digit = {"零", "壹", "贰", "叁", "肆", "伍", "陆", "柒", "捌", "玖"}; + String[][] unit = {{"元", "万", "亿"}, {"", "拾", "佰", "仟"}}; + + String head = n < 0 ? "负" : ""; + n = Math.abs(n); + + String s = ""; + for (int i = 0 ; i < fraction.length ; i++) { + // 优化double计算精度丢失问题 + BigDecimal nNum = new BigDecimal(n); + BigDecimal decimal = new BigDecimal(10); + BigDecimal scale = nNum.multiply(decimal).setScale(2, RoundingMode.HALF_EVEN); + double d = scale.doubleValue(); + s += (digit[(int) (Math.floor(d * Math.pow(10, i)) % 10)] + fraction[i]).replaceAll("(零.)+", ""); + } + if (s.length() < 1) { + s = "整"; + } + int integerPart = (int) Math.floor(n); + + for (int i = 0 ; i < unit[0].length && integerPart > 0 ; i++) { + String p = ""; + for (int j = 0 ; j < unit[1].length && n > 0 ; j++) { + p = digit[integerPart % 10] + unit[1][j] + p; + integerPart = integerPart / 10; + } + s = p.replaceAll("(零.)*零$", "").replaceAll("^$", "零") + unit[0][i] + s; + } + return head + s.replaceAll("(零.)*零元", "元").replaceFirst("(零.)+", "").replaceAll("(零.)+", "零").replaceAll("^整$", "零元整"); + } +} diff --git a/src/main/java/com/muyu/common/core/text/StrFormatter.java b/src/main/java/com/muyu/common/core/text/StrFormatter.java new file mode 100644 index 0000000..0c07cf5 --- /dev/null +++ b/src/main/java/com/muyu/common/core/text/StrFormatter.java @@ -0,0 +1,77 @@ +package com.muyu.common.core.text; + +import com.muyu.common.core.utils.StringUtils; + +/** + * 字符串格式化 + * + * @author muyu + */ +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/muyu/common/core/utils/DateUtils.java b/src/main/java/com/muyu/common/core/utils/DateUtils.java new file mode 100644 index 0000000..fb15c59 --- /dev/null +++ b/src/main/java/com/muyu/common/core/utils/DateUtils.java @@ -0,0 +1,158 @@ +package com.muyu.common.core.utils; + +import org.apache.commons.lang3.time.DateFormatUtils; + +import java.lang.management.ManagementFactory; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.time.*; +import java.util.Date; + +/** + * 时间工具类 + * + * @author muyu + */ +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); + } + + /** + * 计算时间差 + * + * @param endDate 最后时间 + * @param startTime 开始时间 + * + * @return 时间差(天/小时/分钟) + */ + public static String timeDistance (Date endDate, Date startTime) { + long nd = 1000 * 24 * 60 * 60; + long nh = 1000 * 60 * 60; + long nm = 1000 * 60; + // long ns = 1000; + // 获得两个时间的毫秒时间差异 + long diff = endDate.getTime() - startTime.getTime(); + // 计算差多少天 + long day = diff / nd; + // 计算差多少小时 + long hour = diff % nd / nh; + // 计算差多少分钟 + long min = diff % nd % nh / nm; + // 计算差多少秒//输出结果 + // long sec = diff % nd % nh % nm / ns; + return day + "天" + hour + "小时" + min + "分钟"; + } + + /** + * 增加 LocalDateTime ==> Date + */ + public static Date toDate (LocalDateTime temporalAccessor) { + ZonedDateTime zdt = temporalAccessor.atZone(ZoneId.systemDefault()); + return Date.from(zdt.toInstant()); + } + + /** + * 增加 LocalDate ==> Date + */ + public static Date toDate (LocalDate temporalAccessor) { + LocalDateTime localDateTime = LocalDateTime.of(temporalAccessor, LocalTime.of(0, 0, 0)); + ZonedDateTime zdt = localDateTime.atZone(ZoneId.systemDefault()); + return Date.from(zdt.toInstant()); + } +} diff --git a/src/main/java/com/muyu/common/core/utils/ExceptionUtil.java b/src/main/java/com/muyu/common/core/utils/ExceptionUtil.java new file mode 100644 index 0000000..e6abdf9 --- /dev/null +++ b/src/main/java/com/muyu/common/core/utils/ExceptionUtil.java @@ -0,0 +1,35 @@ +package com.muyu.common.core.utils; + +import org.apache.commons.lang3.exception.ExceptionUtils; + +import java.io.PrintWriter; +import java.io.StringWriter; + +/** + * 错误信息处理类。 + * + * @author muyu + */ +public class ExceptionUtil { + /** + * 获取exception的详细错误信息。 + */ + public static String getExceptionMessage (Throwable e) { + StringWriter sw = new StringWriter(); + e.printStackTrace(new PrintWriter(sw, true)); + return sw.toString(); + } + + public static String getRootErrorMessage (Exception e) { + Throwable root = ExceptionUtils.getRootCause(e); + root = (root == null ? e : root); + if (root == null) { + return ""; + } + String msg = root.getMessage(); + if (msg == null) { + return "null"; + } + return StringUtils.defaultString(msg); + } +} diff --git a/src/main/java/com/muyu/common/core/utils/JwtUtils.java b/src/main/java/com/muyu/common/core/utils/JwtUtils.java new file mode 100644 index 0000000..62816cb --- /dev/null +++ b/src/main/java/com/muyu/common/core/utils/JwtUtils.java @@ -0,0 +1,165 @@ +package com.muyu.common.core.utils; + +import com.muyu.common.core.constant.SecurityConstants; +import com.muyu.common.core.constant.TokenConstants; +import com.muyu.common.core.text.Convert; +import io.jsonwebtoken.Claims; +import io.jsonwebtoken.Jwts; +import io.jsonwebtoken.security.Keys; +import io.jsonwebtoken.security.SecureDigestAlgorithm; + +import javax.crypto.SecretKey; +import java.util.Date; +import java.util.Map; + +/** + * Jwt工具类 + * + * @author muyu + */ +public class JwtUtils { + + /** + * 加密算法 + */ + private final static SecureDigestAlgorithm ALGORITHM = Jwts.SIG.HS256; + /** + * 私钥 / 生成签名的时候使用的秘钥secret,一般可以从本地配置文件中读取,切记这个秘钥不能外露,只在服务端使用,在任何场景都不应该流露出去。 + * 一旦客户端得知这个secret, 那就意味着客户端是可以自我签发jwt了。 + * 应该大于等于 256位(长度32及以上的字符串),并且是随机的字符串 + */ + private final static String secret = TokenConstants.SECRET; + /** + * 秘钥实例 + */ + public static final SecretKey KEY = Keys.hmacShaKeyFor(secret.getBytes()); + /** + * jwt签发者 + */ + private final static String JWT_ISS = "MUYU"; + /** + * jwt主题 + */ + private final static String SUBJECT = "Peripherals"; + + + /** + * 从数据声明生成令牌 + * + * @param claims 数据声明 + * + * @return 令牌 + */ + public static String createToken (Map claims) { + return Jwts.builder() + // 设置头部信息header + .header().add("typ", "JWT").add("alg", "HS256").and() + // 设置自定义负载信息payload + .claims(claims) + // 签发时间 + .issuedAt(new Date()) + // 主题 + .subject(SUBJECT) + // 签发者 + .issuer(JWT_ISS) + // 签名 + .signWith(KEY, ALGORITHM) + .compact(); + } + + /** + * 从令牌中获取数据声明 + * + * @param token 令牌 + * + * @return 数据声明 + */ + public static Claims parseToken (String token) { + return Jwts.parser() + .verifyWith(KEY) + .build() + .parseSignedClaims(token) + .getPayload(); + } + + /** + * 根据令牌获取用户标识 + * + * @param token 令牌 + * + * @return 用户ID + */ + public static String getUserKey (String token) { + Claims claims = parseToken(token); + return getValue(claims, SecurityConstants.USER_KEY); + } + + /** + * 根据令牌获取用户标识 + * + * @param claims 身份信息 + * + * @return 用户ID + */ + public static String getUserKey (Claims claims) { + return getValue(claims, SecurityConstants.USER_KEY); + } + + /** + * 根据令牌获取用户ID + * + * @param token 令牌 + * + * @return 用户ID + */ + public static String getUserId (String token) { + Claims claims = parseToken(token); + return getValue(claims, SecurityConstants.DETAILS_USER_ID); + } + + /** + * 根据身份信息获取用户ID + * + * @param claims 身份信息 + * + * @return 用户ID + */ + public static String getUserId (Claims claims) { + return getValue(claims, SecurityConstants.DETAILS_USER_ID); + } + + /** + * 根据令牌获取用户名 + * + * @param token 令牌 + * + * @return 用户名 + */ + public static String getUserName (String token) { + Claims claims = parseToken(token); + return getValue(claims, SecurityConstants.DETAILS_USERNAME); + } + + /** + * 根据身份信息获取用户名 + * + * @param claims 身份信息 + * + * @return 用户名 + */ + public static String getUserName (Claims claims) { + return getValue(claims, SecurityConstants.DETAILS_USERNAME); + } + + /** + * 根据身份信息获取键值 + * + * @param claims 身份信息 + * @param key 键 + * + * @return 值 + */ + public static String getValue (Claims claims, String key) { + return Convert.toStr(claims.get(key), ""); + } +} diff --git a/src/main/java/com/muyu/common/core/utils/PageUtils.java b/src/main/java/com/muyu/common/core/utils/PageUtils.java new file mode 100644 index 0000000..d4b0554 --- /dev/null +++ b/src/main/java/com/muyu/common/core/utils/PageUtils.java @@ -0,0 +1,32 @@ +package com.muyu.common.core.utils; + +import com.github.pagehelper.PageHelper; +import com.muyu.common.core.utils.sql.SqlUtil; +import com.muyu.common.core.web.page.PageDomain; +import com.muyu.common.core.web.page.TableSupport; + +/** + * 分页工具类 + * + * @author muyu + */ +public class PageUtils extends PageHelper { + /** + * 设置请求分页数据 + */ + public static void startPage () { + PageDomain pageDomain = TableSupport.buildPageRequest(); + Integer pageNum = pageDomain.getPageNum(); + Integer pageSize = pageDomain.getPageSize(); + String orderBy = SqlUtil.escapeOrderBySql(pageDomain.getOrderBy()); + Boolean reasonable = pageDomain.getReasonable(); + PageHelper.startPage(pageNum, pageSize, orderBy).setReasonable(reasonable); + } + + /** + * 清理分页的线程变量 + */ + public static void clearPage () { + PageHelper.clearPage(); + } +} diff --git a/src/main/java/com/muyu/common/core/utils/ServletUtils.java b/src/main/java/com/muyu/common/core/utils/ServletUtils.java new file mode 100644 index 0000000..9a50531 --- /dev/null +++ b/src/main/java/com/muyu/common/core/utils/ServletUtils.java @@ -0,0 +1,289 @@ +package com.muyu.common.core.utils; + +import com.alibaba.fastjson2.JSON; +import com.muyu.common.core.constant.Constants; +import com.muyu.common.core.domain.Result; +import com.muyu.common.core.text.Convert; +import jakarta.servlet.ServletRequest; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.servlet.http.HttpSession; +import org.springframework.core.io.buffer.DataBuffer; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.server.reactive.ServerHttpResponse; +import org.springframework.util.LinkedCaseInsensitiveMap; +import org.springframework.web.context.request.RequestAttributes; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; +import reactor.core.publisher.Mono; + + +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.net.URLDecoder; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import java.util.*; + +/** + * 客户端工具类 + * + * @author muyu + */ +public class ServletUtils { + /** + * 获取String参数 + */ + public static String getParameter (String name) { + return getRequest().getParameter(name); + } + + /** + * 获取String参数 + */ + public static String getParameter (String name, String defaultValue) { + return Convert.toStr(getRequest().getParameter(name), defaultValue); + } + + /** + * 获取Integer参数 + */ + public static Integer getParameterToInt (String name) { + return Convert.toInt(getRequest().getParameter(name)); + } + + /** + * 获取Integer参数 + */ + public static Integer getParameterToInt (String name, Integer defaultValue) { + return Convert.toInt(getRequest().getParameter(name), defaultValue); + } + + /** + * 获取Boolean参数 + */ + public static Boolean getParameterToBool (String name) { + return Convert.toBool(getRequest().getParameter(name)); + } + + /** + * 获取Boolean参数 + */ + public static Boolean getParameterToBool (String name, Boolean defaultValue) { + return Convert.toBool(getRequest().getParameter(name), defaultValue); + } + + /** + * 获得所有请求参数 + * + * @param request 请求对象{@link ServletRequest} + * + * @return Map + */ + public static Map getParams (ServletRequest request) { + final Map map = request.getParameterMap(); + return Collections.unmodifiableMap(map); + } + + /** + * 获得所有请求参数 + * + * @param request 请求对象{@link ServletRequest} + * + * @return Map + */ + public static Map getParamMap (ServletRequest request) { + Map params = new HashMap<>(); + for (Map.Entry entry : getParams(request).entrySet()) { + params.put(entry.getKey(), StringUtils.join(entry.getValue(), ",")); + } + return params; + } + + /** + * 获取request + */ + public static HttpServletRequest getRequest () { + try { + return Objects.requireNonNull(getRequestAttributes()).getRequest(); + } catch (Exception e) { + return null; + } + } + + /** + * 获取response + */ + public static HttpServletResponse getResponse () { + try { + return Objects.requireNonNull(getRequestAttributes()).getResponse(); + } catch (Exception e) { + return null; + } + } + + /** + * 获取session + */ + public static HttpSession getSession () { + return Objects.requireNonNull(getRequest()).getSession(); + } + + public static ServletRequestAttributes getRequestAttributes () { + try { + RequestAttributes attributes = RequestContextHolder.getRequestAttributes(); + return (ServletRequestAttributes) attributes; + } catch (Exception e) { + return null; + } + } + + public static String getHeader (HttpServletRequest request, String name) { + String value = request.getHeader(name); + if (StringUtils.isEmpty(value)) { + return StringUtils.EMPTY; + } + return urlDecode(value); + } + + public static Map getHeaders (HttpServletRequest request) { + Map map = new LinkedCaseInsensitiveMap<>(); + Enumeration enumeration = request.getHeaderNames(); + if (enumeration != null) { + while (enumeration.hasMoreElements()) { + String key = enumeration.nextElement(); + String value = request.getHeader(key); + map.put(key, value); + } + } + return map; + } + + /** + * 将字符串渲染到客户端 + * + * @param response 渲染对象 + * @param string 待渲染的字符串 + */ + public static void renderString (HttpServletResponse response, String string) { + try { + response.setStatus(200); + response.setContentType("application/json"); + response.setCharacterEncoding("utf-8"); + response.getWriter().print(string); + } catch (IOException e) { + e.printStackTrace(); + } + } + + /** + * 是否是Ajax异步请求 + * + * @param request + */ + public static boolean isAjaxRequest (HttpServletRequest request) { + String accept = request.getHeader("accept"); + if (accept != null && accept.contains("application/json")) { + return true; + } + + String xRequestedWith = request.getHeader("X-Requested-With"); + if (xRequestedWith != null && xRequestedWith.contains("XMLHttpRequest")) { + return true; + } + + String uri = request.getRequestURI(); + if (StringUtils.inStringIgnoreCase(uri, ".json", ".xml")) { + return true; + } + + String ajax = request.getParameter("__ajax"); + return StringUtils.inStringIgnoreCase(ajax, "json", "xml"); + } + + /** + * 内容编码 + * + * @param str 内容 + * + * @return 编码后的内容 + */ + public static String urlEncode (String str) { + try { + return URLEncoder.encode(str, Constants.UTF8); + } catch (UnsupportedEncodingException e) { + return StringUtils.EMPTY; + } + } + + /** + * 内容解码 + * + * @param str 内容 + * + * @return 解码后的内容 + */ + public static String urlDecode (String str) { + return URLDecoder.decode(str, StandardCharsets.UTF_8); + } + + /** + * 设置webflux模型响应 + * + * @param response ServerHttpResponse + * @param value 响应内容 + * + * @return Mono + */ + public static Mono webFluxResponseWriter (ServerHttpResponse response, Object value) { + return webFluxResponseWriter(response, HttpStatus.OK, value, Result.FAIL); + } + + /** + * 设置webflux模型响应 + * + * @param response ServerHttpResponse + * @param code 响应状态码 + * @param value 响应内容 + * + * @return Mono + */ + public static Mono webFluxResponseWriter (ServerHttpResponse response, Object value, int code) { + return webFluxResponseWriter(response, HttpStatus.OK, value, code); + } + + /** + * 设置webflux模型响应 + * + * @param response ServerHttpResponse + * @param status http状态码 + * @param code 响应状态码 + * @param value 响应内容 + * + * @return Mono + */ + public static Mono webFluxResponseWriter (ServerHttpResponse response, HttpStatus status, Object value, int code) { + return webFluxResponseWriter(response, MediaType.APPLICATION_JSON_VALUE, status, value, code); + } + + /** + * 设置webflux模型响应 + * + * @param response ServerHttpResponse + * @param contentType content-type + * @param status http状态码 + * @param code 响应状态码 + * @param value 响应内容 + * + * @return Mono + */ + public static Mono webFluxResponseWriter (ServerHttpResponse response, String contentType, HttpStatus status, Object value, int code) { + response.setStatusCode(status); + response.getHeaders().add(HttpHeaders.CONTENT_TYPE, contentType); + Result result = Result.error(code, value.toString()); + DataBuffer dataBuffer = response.bufferFactory().wrap(JSON.toJSONString(result).getBytes()); + return response.writeWith(Mono.just(dataBuffer)); + } +} diff --git a/src/main/java/com/muyu/common/core/utils/SpringUtils.java b/src/main/java/com/muyu/common/core/utils/SpringUtils.java new file mode 100644 index 0000000..c37a65c --- /dev/null +++ b/src/main/java/com/muyu/common/core/utils/SpringUtils.java @@ -0,0 +1,114 @@ +package com.muyu.common.core.utils; + +import org.springframework.aop.framework.AopContext; +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.NoSuchBeanDefinitionException; +import org.springframework.beans.factory.config.BeanFactoryPostProcessor; +import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; +import org.springframework.stereotype.Component; + +/** + * spring工具类 方便在非spring管理环境中获取bean + * + * @author muyu + */ +@Component +public final class SpringUtils implements BeanFactoryPostProcessor { + /** + * Spring应用上下文环境 + */ + private static ConfigurableListableBeanFactory beanFactory; + + /** + * 获取对象 + * + * @param name + * + * @return Object 一个以所给名字注册的bean的实例 + * + * @throws org.springframework.beans.BeansException + */ + @SuppressWarnings("unchecked") + public static T getBean (String name) throws BeansException { + return (T) beanFactory.getBean(name); + } + + /** + * 获取类型为requiredType的对象 + * + * @param clz + * + * @return + * + * @throws org.springframework.beans.BeansException + */ + public static T getBean (Class clz) throws BeansException { + T result = (T) beanFactory.getBean(clz); + return result; + } + + /** + * 如果BeanFactory包含一个与所给名称匹配的bean定义,则返回true + * + * @param name + * + * @return boolean + */ + public static boolean containsBean (String name) { + return beanFactory.containsBean(name); + } + + /** + * 判断以给定名字注册的bean定义是一个singleton还是一个prototype。 如果与给定名字相应的bean定义没有被找到,将会抛出一个异常(NoSuchBeanDefinitionException) + * + * @param name + * + * @return boolean + * + * @throws org.springframework.beans.factory.NoSuchBeanDefinitionException + */ + public static boolean isSingleton (String name) throws NoSuchBeanDefinitionException { + return beanFactory.isSingleton(name); + } + + /** + * @param name + * + * @return Class 注册对象的类型 + * + * @throws org.springframework.beans.factory.NoSuchBeanDefinitionException + */ + public static Class getType (String name) throws NoSuchBeanDefinitionException { + return beanFactory.getType(name); + } + + /** + * 如果给定的bean名字在bean定义中有别名,则返回这些别名 + * + * @param name + * + * @return + * + * @throws org.springframework.beans.factory.NoSuchBeanDefinitionException + */ + public static String[] getAliases (String name) throws NoSuchBeanDefinitionException { + return beanFactory.getAliases(name); + } + + /** + * 获取aop代理对象 + * + * @param invoker + * + * @return + */ + @SuppressWarnings("unchecked") + public static T getAopProxy (T invoker) { + return (T) AopContext.currentProxy(); + } + + @Override + public void postProcessBeanFactory (ConfigurableListableBeanFactory beanFactory) throws BeansException { + SpringUtils.beanFactory = beanFactory; + } +} diff --git a/src/main/java/com/muyu/common/core/utils/StringUtils.java b/src/main/java/com/muyu/common/core/utils/StringUtils.java new file mode 100644 index 0000000..ec8f557 --- /dev/null +++ b/src/main/java/com/muyu/common/core/utils/StringUtils.java @@ -0,0 +1,504 @@ +package com.muyu.common.core.utils; + +import com.muyu.common.core.constant.Constants; +import com.muyu.common.core.text.StrFormatter; +import org.springframework.util.AntPathMatcher; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + +/** + * 字符串工具类 + * + * @author muyu + */ +public class StringUtils extends org.apache.commons.lang3.StringUtils { + /** + * 空字符串 + */ + private static final String NULLSTR = ""; + + /** + * 下划线 + */ + private static final char SEPARATOR = '_'; + + /** + * 获取参数不为空值 + * + * @param value defaultValue 要判断的value + * + * @return value 返回值 + */ + public static T nvl (T value, T defaultValue) { + return value != null ? value : defaultValue; + } + + /** + * * 判断一个Collection是否为空, 包含List,Set,Queue + * + * @param coll 要判断的Collection + * + * @return true:为空 false:非空 + */ + public static boolean isEmpty (Collection coll) { + return isNull(coll) || coll.isEmpty(); + } + + /** + * * 判断一个Collection是否非空,包含List,Set,Queue + * + * @param coll 要判断的Collection + * + * @return true:非空 false:空 + */ + public static boolean isNotEmpty (Collection coll) { + return !isEmpty(coll); + } + + /** + * * 判断一个对象数组是否为空 + * + * @param objects 要判断的对象数组 + * * @return true:为空 false:非空 + */ + public static boolean isEmpty (Object[] objects) { + return isNull(objects) || (objects.length == 0); + } + + /** + * * 判断一个对象数组是否非空 + * + * @param objects 要判断的对象数组 + * + * @return true:非空 false:空 + */ + public static boolean isNotEmpty (Object[] objects) { + return !isEmpty(objects); + } + + /** + * * 判断一个Map是否为空 + * + * @param map 要判断的Map + * + * @return true:为空 false:非空 + */ + public static boolean isEmpty (Map map) { + return isNull(map) || map.isEmpty(); + } + + /** + * * 判断一个Map是否为空 + * + * @param map 要判断的Map + * + * @return true:非空 false:空 + */ + public static boolean isNotEmpty (Map map) { + return !isEmpty(map); + } + + /** + * * 判断一个字符串是否为空串 + * + * @param str String + * + * @return true:为空 false:非空 + */ + public static boolean isEmpty (String str) { + return isNull(str) || NULLSTR.equals(str.trim()); + } + + /** + * * 判断一个字符串是否为非空串 + * + * @param str String + * + * @return true:非空串 false:空串 + */ + public static boolean isNotEmpty (String str) { + return !isEmpty(str); + } + + /** + * * 判断一个对象是否为空 + * + * @param object Object + * + * @return true:为空 false:非空 + */ + public static boolean isNull (Object object) { + return object == null; + } + + /** + * * 判断一个对象是否非空 + * + * @param object Object + * + * @return true:非空 false:空 + */ + public static boolean isNotNull (Object object) { + return !isNull(object); + } + + /** + * * 判断一个对象是否是数组类型(Java基本型别的数组) + * + * @param object 对象 + * + * @return true:是数组 false:不是数组 + */ + public static boolean isArray (Object object) { + return isNotNull(object) && object.getClass().isArray(); + } + + /** + * 去空格 + */ + public static String trim (String str) { + return (str == null ? "" : str.trim()); + } + + /** + * 截取字符串 + * + * @param str 字符串 + * @param start 开始 + * + * @return 结果 + */ + public static String substring (final String str, int start) { + if (str == null) { + return NULLSTR; + } + + if (start < 0) { + start = str.length() + start; + } + + if (start < 0) { + start = 0; + } + if (start > str.length()) { + return NULLSTR; + } + + return str.substring(start); + } + + /** + * 截取字符串 + * + * @param str 字符串 + * @param start 开始 + * @param end 结束 + * + * @return 结果 + */ + public static String substring (final String str, int start, int end) { + if (str == null) { + return NULLSTR; + } + + if (end < 0) { + end = str.length() + end; + } + if (start < 0) { + start = str.length() + start; + } + + if (end > str.length()) { + end = str.length(); + } + + if (start > end) { + return NULLSTR; + } + + if (start < 0) { + start = 0; + } + if (end < 0) { + end = 0; + } + + return str.substring(start, end); + } + + /** + * 判断是否为空,并且不是空白字符 + * + * @param str 要判断的value + * + * @return 结果 + */ + public static boolean hasText (String str) { + return (str != null && !str.isEmpty() && containsText(str)); + } + + private static boolean containsText (CharSequence str) { + int strLen = str.length(); + for (int i = 0 ; i < strLen ; i++) { + if (!Character.isWhitespace(str.charAt(i))) { + return true; + } + } + return false; + } + + /** + * 格式化文本, {} 表示占位符
+ * 此方法只是简单将占位符 {} 按照顺序替换为参数
+ * 如果想输出 {} 使用 \\转义 { 即可,如果想输出 {} 之前的 \ 使用双转义符 \\\\ 即可
+ * 例:
+ * 通常使用:format("this is {} for {}", "a", "b") -> this is a for b
+ * 转义{}: format("this is \\{} for {}", "a", "b") -> this is \{} for a
+ * 转义\: format("this is \\\\{} for {}", "a", "b") -> this is \a for b
+ * + * @param template 文本模板,被替换的部分用 {} 表示 + * @param params 参数值 + * + * @return 格式化后的文本 + */ + public static String format (String template, Object... params) { + if (isEmpty(params) || isEmpty(template)) { + return template; + } + return StrFormatter.format(template, params); + } + + /** + * 是否为http(s)://开头 + * + * @param link 链接 + * + * @return 结果 + */ + public static boolean ishttp (String link) { + return StringUtils.startsWithAny(link, Constants.HTTP, Constants.HTTPS); + } + + /** + * 判断给定的collection列表中是否包含数组array 判断给定的数组array中是否包含给定的元素value + * + * @param collection 给定的集合 + * @param array 给定的数组 + * + * @return boolean 结果 + */ + public static boolean containsAny (Collection collection, String... array) { + if (isEmpty(collection) || isEmpty(array)) { + return false; + } else { + for (String str : array) { + if (collection.contains(str)) { + return true; + } + } + return false; + } + } + + /** + * 驼峰转下划线命名 + */ + public static String toUnderScoreCase (String str) { + if (str == null) { + return null; + } + StringBuilder sb = new StringBuilder(); + // 前置字符是否大写 + boolean preCharIsUpperCase = true; + // 当前字符是否大写 + boolean curreCharIsUpperCase = true; + // 下一字符是否大写 + boolean nexteCharIsUpperCase = true; + for (int i = 0 ; i < str.length() ; i++) { + char c = str.charAt(i); + if (i > 0) { + preCharIsUpperCase = Character.isUpperCase(str.charAt(i - 1)); + } else { + preCharIsUpperCase = false; + } + + curreCharIsUpperCase = Character.isUpperCase(c); + + if (i < (str.length() - 1)) { + nexteCharIsUpperCase = Character.isUpperCase(str.charAt(i + 1)); + } + + if (preCharIsUpperCase && curreCharIsUpperCase && !nexteCharIsUpperCase) { + sb.append(SEPARATOR); + } else if ((i != 0 && !preCharIsUpperCase) && curreCharIsUpperCase) { + sb.append(SEPARATOR); + } + sb.append(Character.toLowerCase(c)); + } + + return sb.toString(); + } + + /** + * 是否包含字符串 + * + * @param str 验证字符串 + * @param strs 字符串组 + * + * @return 包含返回true + */ + public static boolean inStringIgnoreCase (String str, String... strs) { + if (str != null && strs != null) { + for (String s : strs) { + if (str.equalsIgnoreCase(trim(s))) { + return true; + } + } + } + return false; + } + + /** + * 将下划线大写方式命名的字符串转换为驼峰式。如果转换前的下划线大写方式命名的字符串为空,则返回空字符串。 例如:HELLO_WORLD->HelloWorld + * + * @param name 转换前的下划线大写方式命名的字符串 + * + * @return 转换后的驼峰式命名的字符串 + */ + public static String convertToCamelCase (String name) { + StringBuilder result = new StringBuilder(); + // 快速检查 + if (name == null || name.isEmpty()) { + // 没必要转换 + return ""; + } else if (!name.contains("_")) { + // 不含下划线,仅将首字母大写 + return name.substring(0, 1).toUpperCase() + name.substring(1); + } + // 用下划线将原始字符串分割 + String[] camels = name.split("_"); + for (String camel : camels) { + // 跳过原始字符串中开头、结尾的下换线或双重下划线 + if (camel.isEmpty()) { + continue; + } + // 首字母大写 + result.append(camel.substring(0, 1).toUpperCase()); + result.append(camel.substring(1).toLowerCase()); + } + return result.toString(); + } + + /** + * 驼峰式命名法 + * 例如:user_name->userName + */ + public static String toCamelCase (String s) { + if (s == null) { + return null; + } + if (s.indexOf(SEPARATOR) == -1) { + return s; + } + s = s.toLowerCase(); + StringBuilder sb = new StringBuilder(s.length()); + boolean upperCase = false; + for (int i = 0 ; i < s.length() ; i++) { + char c = s.charAt(i); + + if (c == SEPARATOR) { + upperCase = true; + } else if (upperCase) { + sb.append(Character.toUpperCase(c)); + upperCase = false; + } else { + sb.append(c); + } + } + return sb.toString(); + } + + /** + * 查找指定字符串是否匹配指定字符串列表中的任意一个字符串 + * + * @param str 指定字符串 + * @param strs 需要检查的字符串数组 + * + * @return 是否匹配 + */ + public static boolean matches (String str, List strs) { + if (isEmpty(str) || isEmpty(strs)) { + return false; + } + for (String pattern : strs) { + if (isMatch(pattern, str)) { + return true; + } + } + return false; + } + + /** + * 判断url是否与规则配置: + * ? 表示单个字符; + * * 表示一层路径内的任意字符串,不可跨层级; + * ** 表示任意层路径; + * + * @param pattern 匹配规则 + * @param url 需要匹配的url + * + * @return + */ + public static boolean isMatch (String pattern, String url) { + AntPathMatcher matcher = new AntPathMatcher(); + return matcher.match(pattern, url); + } + + @SuppressWarnings("unchecked") + public static T cast (Object obj) { + return (T) obj; + } + + /** + * 数字左边补齐0,使之达到指定长度。注意,如果数字转换为字符串后,长度大于size,则只保留 最后size个字符。 + * + * @param num 数字对象 + * @param size 字符串指定长度 + * + * @return 返回数字的字符串格式,该字符串为指定长度。 + */ + public static final String padl (final Number num, final int size) { + return padl(num.toString(), size, '0'); + } + + /** + * 字符串左补齐。如果原始字符串s长度大于size,则只保留最后size个字符。 + * + * @param s 原始字符串 + * @param size 字符串指定长度 + * @param c 用于补齐的字符 + * + * @return 返回指定长度的字符串,由原字符串左补齐或截取得到。 + */ + public static final String padl (final String s, final int size, final char c) { + final StringBuilder sb = new StringBuilder(size); + if (s != null) { + final int len = s.length(); + if (s.length() <= size) { + for (int i = size - len ; i > 0 ; i--) { + sb.append(c); + } + sb.append(s); + } else { + return s.substring(len - size, len); + } + } else { + for (int i = size ; i > 0 ; i--) { + sb.append(c); + } + } + return sb.toString(); + } +} diff --git a/src/main/java/com/muyu/common/core/utils/bean/BeanUtils.java b/src/main/java/com/muyu/common/core/utils/bean/BeanUtils.java new file mode 100644 index 0000000..d44b351 --- /dev/null +++ b/src/main/java/com/muyu/common/core/utils/bean/BeanUtils.java @@ -0,0 +1,107 @@ +package com.muyu.common.core.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 muyu + */ +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/muyu/common/core/utils/bean/BeanValidators.java b/src/main/java/com/muyu/common/core/utils/bean/BeanValidators.java new file mode 100644 index 0000000..b904747 --- /dev/null +++ b/src/main/java/com/muyu/common/core/utils/bean/BeanValidators.java @@ -0,0 +1,22 @@ +package com.muyu.common.core.utils.bean; + +import jakarta.validation.ConstraintViolation; +import jakarta.validation.ConstraintViolationException; +import jakarta.validation.Validator; + +import java.util.Set; + +/** + * bean对象属性验证 + * + * @author muyu + */ +public class BeanValidators { + public static void validateWithException (Validator validator, Object object, Class... groups) + throws ConstraintViolationException { + Set> constraintViolations = validator.validate(object, groups); + if (!constraintViolations.isEmpty()) { + throw new ConstraintViolationException(constraintViolations); + } + } +} diff --git a/src/main/java/com/muyu/common/core/utils/file/FileTypeUtils.java b/src/main/java/com/muyu/common/core/utils/file/FileTypeUtils.java new file mode 100644 index 0000000..dde7e85 --- /dev/null +++ b/src/main/java/com/muyu/common/core/utils/file/FileTypeUtils.java @@ -0,0 +1,85 @@ +package com.muyu.common.core.utils.file; + +import org.apache.commons.io.FilenameUtils; +import org.apache.commons.lang3.StringUtils; +import org.springframework.web.multipart.MultipartFile; + +import java.io.File; +import java.util.Objects; + +/** + * 文件类型工具类 + * + * @author muyu + */ +public class FileTypeUtils { + /** + * 获取文件类型 + *

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

+ * 例如: muyu.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 file 表单文件 + * + * @return 后缀名 + */ + public static final String getExtension (MultipartFile file) { + String extension = FilenameUtils.getExtension(file.getOriginalFilename()); + if (StringUtils.isEmpty(extension)) { + extension = MimeTypeUtils.getExtension(Objects.requireNonNull(file.getContentType())); + } + return extension; + } + + /** + * 获取文件类型 + * + * @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; + } +} diff --git a/src/main/java/com/muyu/common/core/utils/file/FileUtils.java b/src/main/java/com/muyu/common/core/utils/file/FileUtils.java new file mode 100644 index 0000000..5766365 --- /dev/null +++ b/src/main/java/com/muyu/common/core/utils/file/FileUtils.java @@ -0,0 +1,223 @@ +package com.muyu.common.core.utils.file; + +import com.muyu.common.core.utils.StringUtils; +import org.apache.commons.lang3.ArrayUtils; + +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import java.io.*; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; + +/** + * 文件处理工具类 + * + * @author muyu + */ +public class FileUtils { + /** + * 字符常量:斜杠 {@code '/'} + */ + public static final char SLASH = '/'; + + /** + * 字符常量:反斜杠 {@code '\\'} + */ + public static final char BACKSLASH = '\\'; + + 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()) { + flag = file.delete(); + } + return flag; + } + + /** + * 文件名称验证 + * + * @param filename 文件名称 + * + * @return true 正常 false 非法 + */ + public static boolean isValidFilename (String filename) { + return filename.matches(FILENAME_PATTERN); + } + + /** + * 检查文件是否可下载 + * + * @param resource 需要下载的文件 + * + * @return true 正常 false 非法 + */ + public static boolean checkAllowDownload (String resource) { + // 禁止目录上跳级别 + if (StringUtils.contains(resource, "..")) { + return false; + } + // 判断是否在允许下载的文件规则内 + return ArrayUtils.contains(MimeTypeUtils.DEFAULT_ALLOWED_EXTENSION, FileTypeUtils.getFileType(resource)); + } + + /** + * 下载文件名重新编码 + * + * @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 filePath 文件 + * + * @return 文件名 + */ + public static String getName (String filePath) { + if (null == filePath) { + return null; + } + int len = filePath.length(); + if (0 == len) { + return filePath; + } + if (isFileSeparator(filePath.charAt(len - 1))) { + // 以分隔符结尾的去掉结尾分隔符 + len--; + } + + int begin = 0; + char c; + for (int i = len - 1 ; i > -1 ; i--) { + c = filePath.charAt(i); + if (isFileSeparator(c)) { + // 查找最后一个路径分隔符(/或者\) + begin = i + 1; + break; + } + } + + return filePath.substring(begin, len); + } + + /** + * 是否为Windows或者Linux(Unix)文件分隔符
+ * Windows平台下分隔符为\,Linux(Unix)为/ + * + * @param c 字符 + * + * @return 是否为Windows或者Linux(Unix)文件分隔符 + */ + public static boolean isFileSeparator (char c) { + return SLASH == c || BACKSLASH == c; + } + + /** + * 下载文件名重新编码 + * + * @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()); + response.setHeader("download-filename", percentEncodedFileName); + } + + /** + * 百分号编码工具方法 + * + * @param s 需要百分号编码的字符串 + * + * @return 百分号编码后的字符串 + */ + public static String percentEncode (String s) throws UnsupportedEncodingException { + String encode = URLEncoder.encode(s, StandardCharsets.UTF_8.toString()); + return encode.replaceAll("\\+", "%20"); + } +} diff --git a/src/main/java/com/muyu/common/core/utils/file/ImageUtils.java b/src/main/java/com/muyu/common/core/utils/file/ImageUtils.java new file mode 100644 index 0000000..7e23345 --- /dev/null +++ b/src/main/java/com/muyu/common/core/utils/file/ImageUtils.java @@ -0,0 +1,69 @@ +package com.muyu.common.core.utils.file; + +import org.apache.poi.util.IOUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.ByteArrayInputStream; +import java.io.InputStream; +import java.net.URL; +import java.net.URLConnection; +import java.util.Arrays; + +/** + * 图片处理工具类 + * + * @author muyu + */ +public class ImageUtils { + private static final Logger log = LoggerFactory.getLogger(ImageUtils.class); + + public static byte[] getImage (String imagePath) { + InputStream is = getFile(imagePath); + try { + return IOUtils.toByteArray(is); + } catch (Exception e) { + log.error("图片加载异常 {}", e); + return null; + } finally { + IOUtils.closeQuietly(is); + } + } + + public static InputStream getFile (String imagePath) { + try { + byte[] result = readFile(imagePath); + result = Arrays.copyOf(result, result.length); + return new ByteArrayInputStream(result); + } catch (Exception e) { + log.error("获取图片异常 {}", e); + } + return null; + } + + /** + * 读取文件为字节数据 + * + * @param url 地址 + * + * @return 字节数据 + */ + public static byte[] readFile (String url) { + InputStream in = null; + try { + // 网络地址 + URL urlObj = new URL(url); + URLConnection urlConnection = urlObj.openConnection(); + urlConnection.setConnectTimeout(30 * 1000); + urlConnection.setReadTimeout(60 * 1000); + urlConnection.setDoInput(true); + in = urlConnection.getInputStream(); + return IOUtils.toByteArray(in); + } catch (Exception e) { + log.error("访问文件异常 {}", e); + return null; + } finally { + IOUtils.closeQuietly(in); + } + } +} diff --git a/src/main/java/com/muyu/common/core/utils/file/MimeTypeUtils.java b/src/main/java/com/muyu/common/core/utils/file/MimeTypeUtils.java new file mode 100644 index 0000000..9eb1d84 --- /dev/null +++ b/src/main/java/com/muyu/common/core/utils/file/MimeTypeUtils.java @@ -0,0 +1,56 @@ +package com.muyu.common.core.utils.file; + +/** + * 媒体类型工具类 + * + * @author muyu + */ +public class MimeTypeUtils { + public static final String IMAGE_PNG = "image/png"; + + public static final String IMAGE_JPG = "image/jpg"; + + public static final String IMAGE_JPEG = "image/jpeg"; + + public static final String IMAGE_BMP = "image/bmp"; + + public static final String IMAGE_GIF = "image/gif"; + + public static final String[] IMAGE_EXTENSION = {"bmp", "gif", "jpg", "jpeg", "png"}; + + public static final String[] FLASH_EXTENSION = {"swf", "flv"}; + + public static final String[] MEDIA_EXTENSION = {"swf", "flv", "mp3", "wav", "wma", "wmv", "mid", "avi", "mpg", + "asf", "rm", "rmvb"}; + + public static final String[] VIDEO_EXTENSION = {"mp4", "avi", "rmvb"}; + + public static final String[] DEFAULT_ALLOWED_EXTENSION = { + // 图片 + "bmp", "gif", "jpg", "jpeg", "png", + // word excel powerpoint + "doc", "docx", "xls", "xlsx", "ppt", "pptx", "html", "htm", "txt", + // 压缩文件 + "rar", "zip", "gz", "bz2", + // 视频格式 + "mp4", "avi", "rmvb", + // pdf + "pdf"}; + + public static String getExtension (String prefix) { + switch (prefix) { + case IMAGE_PNG: + return "png"; + case IMAGE_JPG: + return "jpg"; + case IMAGE_JPEG: + return "jpeg"; + case IMAGE_BMP: + return "bmp"; + case IMAGE_GIF: + return "gif"; + default: + return ""; + } + } +} diff --git a/src/main/java/com/muyu/common/core/utils/html/EscapeUtil.java b/src/main/java/com/muyu/common/core/utils/html/EscapeUtil.java new file mode 100644 index 0000000..7dba9be --- /dev/null +++ b/src/main/java/com/muyu/common/core/utils/html/EscapeUtil.java @@ -0,0 +1,145 @@ +package com.muyu.common.core.utils.html; + +import com.muyu.common.core.utils.StringUtils; + +/** + * 转义和反转义工具类 + * + * @author muyu + */ +public class EscapeUtil { + public static final String RE_HTML_MARK = "(<[^<]*?>)|(<[\\s]*?/[^<]*?>)|(<[^<]*?/[\\s]*?>)"; + + private static final char[][] TEXT = new char[64][]; + + static { + for (int i = 0 ; i < 64 ; i++) { + TEXT[i] = new char[]{(char) i}; + } + + // special HTML characters + TEXT['\''] = "'".toCharArray(); // 单引号 + TEXT['"'] = """.toCharArray(); // 双引号 + TEXT['&'] = "&".toCharArray(); // &符 + TEXT['<'] = "<".toCharArray(); // 小于号 + TEXT['>'] = ">".toCharArray(); // 大于号 + } + + /** + * 转义文本中的HTML字符为安全的字符 + * + * @param text 被转义的文本 + * + * @return 转义后的文本 + */ + public static String escape (String text) { + return encode(text); + } + + /** + * 还原被转义的HTML特殊字符 + * + * @param content 包含转义符的HTML内容 + * + * @return 转换后的字符串 + */ + public static String unescape (String content) { + return decode(content); + } + + /** + * 清除所有HTML标签,但是不删除标签内的内容 + * + * @param content 文本 + * + * @return 清除标签后的文本 + */ + public static String clean (String content) { + return new HTMLFilter().filter(content); + } + + /** + * Escape编码 + * + * @param text 被编码的文本 + * + * @return 编码后的字符 + */ + private static String encode (String text) { + if (StringUtils.isEmpty(text)) { + return StringUtils.EMPTY; + } + + final StringBuilder tmp = new StringBuilder(text.length() * 6); + char c; + for (int i = 0 ; i < text.length() ; i++) { + c = text.charAt(i); + if (c < 256) { + tmp.append("%"); + if (c < 16) { + tmp.append("0"); + } + tmp.append(Integer.toString(c, 16)); + } else { + tmp.append("%u"); + if (c <= 0xfff) { + // issue#I49JU8@Gitee + tmp.append("0"); + } + tmp.append(Integer.toString(c, 16)); + } + } + return tmp.toString(); + } + + /** + * Escape解码 + * + * @param content 被转义的内容 + * + * @return 解码后的字符串 + */ + public static String decode (String content) { + if (StringUtils.isEmpty(content)) { + return content; + } + + StringBuilder tmp = new StringBuilder(content.length()); + int lastPos = 0, pos = 0; + char ch; + while (lastPos < content.length()) { + pos = content.indexOf("%", lastPos); + if (pos == lastPos) { + if (content.charAt(pos + 1) == 'u') { + ch = (char) Integer.parseInt(content.substring(pos + 2, pos + 6), 16); + tmp.append(ch); + lastPos = pos + 6; + } else { + ch = (char) Integer.parseInt(content.substring(pos + 1, pos + 3), 16); + tmp.append(ch); + lastPos = pos + 3; + } + } else { + if (pos == -1) { + tmp.append(content.substring(lastPos)); + lastPos = content.length(); + } else { + tmp.append(content.substring(lastPos, pos)); + lastPos = pos; + } + } + } + return tmp.toString(); + } + + public static void main (String[] args) { + String html = ""; + String escape = EscapeUtil.escape(html); + // String html = "ipt>alert(\"XSS\")ipt>"; + // String html = "<123"; + // String html = "123>"; + System.out.println("clean: " + EscapeUtil.clean(html)); + System.out.println("escape: " + escape); + System.out.println("unescape: " + EscapeUtil.unescape(escape)); + } +} diff --git a/src/main/java/com/muyu/common/core/utils/html/HTMLFilter.java b/src/main/java/com/muyu/common/core/utils/html/HTMLFilter.java new file mode 100644 index 0000000..68221f3 --- /dev/null +++ b/src/main/java/com/muyu/common/core/utils/html/HTMLFilter.java @@ -0,0 +1,498 @@ +package com.muyu.common.core.utils.html; + +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * HTML过滤器,用于去除XSS漏洞隐患。 + * + * @author muyu + */ +public final class HTMLFilter { + /** + * regex flag union representing /si modifiers in php + **/ + private static final int REGEX_FLAGS_SI = Pattern.CASE_INSENSITIVE | Pattern.DOTALL; + private static final Pattern P_COMMENTS = Pattern.compile("", Pattern.DOTALL); + private static final Pattern P_COMMENT = Pattern.compile("^!--(.*)--$", REGEX_FLAGS_SI); + private static final Pattern P_TAGS = Pattern.compile("<(.*?)>", Pattern.DOTALL); + private static final Pattern P_END_TAG = Pattern.compile("^/([a-z0-9]+)", REGEX_FLAGS_SI); + private static final Pattern P_START_TAG = Pattern.compile("^([a-z0-9]+)(.*?)(/?)$", REGEX_FLAGS_SI); + private static final Pattern P_QUOTED_ATTRIBUTES = Pattern.compile("([a-z0-9]+)=([\"'])(.*?)\\2", REGEX_FLAGS_SI); + private static final Pattern P_UNQUOTED_ATTRIBUTES = Pattern.compile("([a-z0-9]+)(=)([^\"\\s']+)", REGEX_FLAGS_SI); + private static final Pattern P_PROTOCOL = Pattern.compile("^([^:]+):", REGEX_FLAGS_SI); + private static final Pattern P_ENTITY = Pattern.compile("&#(\\d+);?"); + private static final Pattern P_ENTITY_UNICODE = Pattern.compile("&#x([0-9a-f]+);?"); + private static final Pattern P_ENCODE = Pattern.compile("%([0-9a-f]{2});?"); + private static final Pattern P_VALID_ENTITIES = Pattern.compile("&([^&;]*)(?=(;|&|$))"); + private static final Pattern P_VALID_QUOTES = Pattern.compile("(>|^)([^<]+?)(<|$)", Pattern.DOTALL); + private static final Pattern P_END_ARROW = Pattern.compile("^>"); + private static final Pattern P_BODY_TO_END = Pattern.compile("<([^>]*?)(?=<|$)"); + private static final Pattern P_XML_CONTENT = Pattern.compile("(^|>)([^<]*?)(?=>)"); + private static final Pattern P_STRAY_LEFT_ARROW = Pattern.compile("<([^>]*?)(?=<|$)"); + private static final Pattern P_STRAY_RIGHT_ARROW = Pattern.compile("(^|>)([^<]*?)(?=>)"); + private static final Pattern P_AMP = Pattern.compile("&"); + private static final Pattern P_QUOTE = Pattern.compile("\""); + private static final Pattern P_LEFT_ARROW = Pattern.compile("<"); + private static final Pattern P_RIGHT_ARROW = Pattern.compile(">"); + private static final Pattern P_BOTH_ARROWS = Pattern.compile("<>"); + + // @xxx could grow large... maybe use sesat's ReferenceMap + private static final ConcurrentMap P_REMOVE_PAIR_BLANKS = new ConcurrentHashMap<>(); + private static final ConcurrentMap P_REMOVE_SELF_BLANKS = new ConcurrentHashMap<>(); + + /** + * set of allowed html elements, along with allowed attributes for each element + **/ + private final Map> vAllowed; + /** + * counts of open tags for each (allowable) html element + **/ + private final Map vTagCounts = new HashMap<>(); + + /** + * html elements which must always be self-closing (e.g. "") + **/ + private final String[] vSelfClosingTags; + /** + * html elements which must always have separate opening and closing tags (e.g. "") + **/ + private final String[] vNeedClosingTags; + /** + * set of disallowed html elements + **/ + private final String[] vDisallowed; + /** + * attributes which should be checked for valid protocols + **/ + private final String[] vProtocolAtts; + /** + * allowed protocols + **/ + private final String[] vAllowedProtocols; + /** + * tags which should be removed if they contain no content (e.g. "" or "") + **/ + private final String[] vRemoveBlanks; + /** + * entities allowed within html markup + **/ + private final String[] vAllowedEntities; + /** + * flag determining whether comments are allowed in input String. + */ + private final boolean stripComment; + private final boolean encodeQuotes; + /** + * flag determining whether to try to make tags when presented with "unbalanced" angle brackets (e.g. "" + * becomes " text "). If set to false, unbalanced angle brackets will be html escaped. + */ + private final boolean alwaysMakeTags; + + /** + * Default constructor. + */ + public HTMLFilter () { + vAllowed = new HashMap<>(); + + final ArrayList a_atts = new ArrayList<>(); + a_atts.add("href"); + a_atts.add("target"); + vAllowed.put("a", a_atts); + + final ArrayList img_atts = new ArrayList<>(); + img_atts.add("src"); + img_atts.add("width"); + img_atts.add("height"); + img_atts.add("alt"); + vAllowed.put("img", img_atts); + + final ArrayList no_atts = new ArrayList<>(); + vAllowed.put("b", no_atts); + vAllowed.put("strong", no_atts); + vAllowed.put("i", no_atts); + vAllowed.put("em", no_atts); + + vSelfClosingTags = new String[]{"img"}; + vNeedClosingTags = new String[]{"a", "b", "strong", "i", "em"}; + vDisallowed = new String[]{}; + vAllowedProtocols = new String[]{"http", "mailto", "https"}; // no ftp. + vProtocolAtts = new String[]{"src", "href"}; + vRemoveBlanks = new String[]{"a", "b", "strong", "i", "em"}; + vAllowedEntities = new String[]{"amp", "gt", "lt", "quot"}; + stripComment = true; + encodeQuotes = true; + alwaysMakeTags = false; + } + + /** + * Map-parameter configurable constructor. + * + * @param conf map containing configuration. keys match field names. + */ + @SuppressWarnings("unchecked") + public HTMLFilter (final Map conf) { + + assert conf.containsKey("vAllowed") : "configuration requires vAllowed"; + assert conf.containsKey("vSelfClosingTags") : "configuration requires vSelfClosingTags"; + assert conf.containsKey("vNeedClosingTags") : "configuration requires vNeedClosingTags"; + assert conf.containsKey("vDisallowed") : "configuration requires vDisallowed"; + assert conf.containsKey("vAllowedProtocols") : "configuration requires vAllowedProtocols"; + assert conf.containsKey("vProtocolAtts") : "configuration requires vProtocolAtts"; + assert conf.containsKey("vRemoveBlanks") : "configuration requires vRemoveBlanks"; + assert conf.containsKey("vAllowedEntities") : "configuration requires vAllowedEntities"; + + vAllowed = Collections.unmodifiableMap((HashMap>) conf.get("vAllowed")); + vSelfClosingTags = (String[]) conf.get("vSelfClosingTags"); + vNeedClosingTags = (String[]) conf.get("vNeedClosingTags"); + vDisallowed = (String[]) conf.get("vDisallowed"); + vAllowedProtocols = (String[]) conf.get("vAllowedProtocols"); + vProtocolAtts = (String[]) conf.get("vProtocolAtts"); + vRemoveBlanks = (String[]) conf.get("vRemoveBlanks"); + vAllowedEntities = (String[]) conf.get("vAllowedEntities"); + stripComment = conf.containsKey("stripComment") ? (Boolean) conf.get("stripComment") : true; + encodeQuotes = conf.containsKey("encodeQuotes") ? (Boolean) conf.get("encodeQuotes") : true; + alwaysMakeTags = conf.containsKey("alwaysMakeTags") ? (Boolean) conf.get("alwaysMakeTags") : true; + } + + // --------------------------------------------------------------- + // 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; + } + + 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 static boolean inArray (final String s, final String[] array) { + for (String item : array) { + if (item != null && item.equals(s)) { + return true; + } + } + return false; + } + + private void reset () { + vTagCounts.clear(); + } + + /** + * given a user submitted input String, filter out any invalid or restricted html. + * + * @param input text (i.e. submitted by a user) than may contain html + * + * @return "clean" version of input, with only valid, whitelisted html elements allowed + */ + public String filter (final String input) { + reset(); + String s = input; + + s = escapeComments(s); + + s = balanceHTML(s); + + s = checkTags(s); + + s = processRemoveBlanks(s); + + // s = validateEntities(s); + + return s; + } + + public boolean isAlwaysMakeTags () { + return alwaysMakeTags; + } + + public boolean isStripComments () { + return stripComment; + } + + private String escapeComments (final String s) { + final Matcher m = P_COMMENTS.matcher(s); + final StringBuffer buf = new StringBuffer(); + if (m.find()) { + final String match = m.group(1); // (.*?) + m.appendReplacement(buf, Matcher.quoteReplacement("")); + } + m.appendTail(buf); + + return buf.toString(); + } + + private String balanceHTML (String s) { + if (alwaysMakeTags) { + // + // try and form html + // + s = regexReplace(P_END_ARROW, "", s); + // 不追加结束标签 + s = regexReplace(P_BODY_TO_END, "<$1>", s); + s = regexReplace(P_XML_CONTENT, "$1<$2", s); + + } else { + // + // escape stray brackets + // + s = regexReplace(P_STRAY_LEFT_ARROW, "<$1", s); + s = regexReplace(P_STRAY_RIGHT_ARROW, "$1$2><", s); + + // + // the last regexp causes '<>' entities to appear + // (we need to do a lookahead assertion so that the last bracket can + // be used in the next pass of the regexp) + // + s = regexReplace(P_BOTH_ARROWS, "", s); + } + + return s; + } + + private String checkTags (String s) { + Matcher m = P_TAGS.matcher(s); + + final StringBuffer buf = new StringBuffer(); + while (m.find()) { + String replaceStr = m.group(1); + replaceStr = processTag(replaceStr); + m.appendReplacement(buf, Matcher.quoteReplacement(replaceStr)); + } + m.appendTail(buf); + + // these get tallied in processTag + // (remember to reset before subsequent calls to filter method) + final StringBuilder sBuilder = new StringBuilder(buf.toString()); + for (String key : vTagCounts.keySet()) { + for (int ii = 0 ; ii < vTagCounts.get(key) ; ii++) { + sBuilder.append(""); + } + } + s = sBuilder.toString(); + + return s; + } + + private String processRemoveBlanks (final String s) { + String result = s; + for (String tag : vRemoveBlanks) { + if (!P_REMOVE_PAIR_BLANKS.containsKey(tag)) { + P_REMOVE_PAIR_BLANKS.putIfAbsent(tag, Pattern.compile("<" + tag + "(\\s[^>]*)?>")); + } + result = regexReplace(P_REMOVE_PAIR_BLANKS.get(tag), "", result); + if (!P_REMOVE_SELF_BLANKS.containsKey(tag)) { + P_REMOVE_SELF_BLANKS.putIfAbsent(tag, Pattern.compile("<" + tag + "(\\s[^>]*)?/>")); + } + result = regexReplace(P_REMOVE_SELF_BLANKS.get(tag), "", result); + } + + return result; + } + + private 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 (!inArray(name, vSelfClosingTags)) { + if (vTagCounts.containsKey(name)) { + vTagCounts.put(name, vTagCounts.get(name) - 1); + return ""; + } + } + } + } + + // starting tags + m = P_START_TAG.matcher(s); + if (m.find()) { + final String name = m.group(1).toLowerCase(); + final String body = m.group(2); + String ending = m.group(3); + + // debug( "in a starting tag, name='" + name + "'; body='" + body + "'; ending='" + ending + "'" ); + if (allowed(name)) { + final StringBuilder params = new StringBuilder(); + + final Matcher m2 = P_QUOTED_ATTRIBUTES.matcher(body); + final Matcher m3 = P_UNQUOTED_ATTRIBUTES.matcher(body); + final List paramNames = new ArrayList<>(); + final List paramValues = new ArrayList<>(); + while (m2.find()) { + paramNames.add(m2.group(1)); // ([a-z0-9]+) + paramValues.add(m2.group(3)); // (.*?) + } + while (m3.find()) { + paramNames.add(m3.group(1)); // ([a-z0-9]+) + paramValues.add(m3.group(3)); // ([^\"\\s']+) + } + + String paramName, paramValue; + for (int ii = 0 ; ii < paramNames.size() ; ii++) { + paramName = paramNames.get(ii).toLowerCase(); + paramValue = paramValues.get(ii); + + // debug( "paramName='" + paramName + "'" ); + // debug( "paramValue='" + paramValue + "'" ); + // debug( "allowed? " + vAllowed.get( name ).contains( paramName ) ); + + if (allowedAttribute(name, paramName)) { + if (inArray(paramName, vProtocolAtts)) { + paramValue = processParamProtocol(paramValue); + } + params.append(' ').append(paramName).append("=\\\"").append(paramValue).append("\\\""); + } + } + + if (inArray(name, vSelfClosingTags)) { + ending = " /"; + } + + if (inArray(name, vNeedClosingTags)) { + ending = ""; + } + + if (ending == null || ending.length() < 1) { + if (vTagCounts.containsKey(name)) { + vTagCounts.put(name, vTagCounts.get(name) + 1); + } else { + vTagCounts.put(name, 1); + } + } else { + ending = " /"; + } + return "<" + name + params + ending + ">"; + } else { + return ""; + } + } + + // comments + m = P_COMMENT.matcher(s); + if (!stripComment && m.find()) { + return "<" + m.group() + ">"; + } + + return ""; + } + + private String processParamProtocol (String s) { + s = decodeEntities(s); + final Matcher m = P_PROTOCOL.matcher(s); + if (m.find()) { + final String protocol = m.group(1); + if (!inArray(protocol, vAllowedProtocols)) { + // bad protocol, turn into local anchor link instead + s = "#" + s.substring(protocol.length() + 1); + if (s.startsWith("#//")) { + s = "#" + s.substring(3); + } + } + } + + return s; + } + + private String decodeEntities (String s) { + StringBuffer buf = new StringBuffer(); + + Matcher m = P_ENTITY.matcher(s); + while (m.find()) { + final String match = m.group(1); + final int decimal = Integer.decode(match).intValue(); + m.appendReplacement(buf, Matcher.quoteReplacement(chr(decimal))); + } + m.appendTail(buf); + s = buf.toString(); + + buf = new StringBuffer(); + m = P_ENTITY_UNICODE.matcher(s); + while (m.find()) { + final String match = m.group(1); + final int decimal = Integer.valueOf(match, 16).intValue(); + m.appendReplacement(buf, Matcher.quoteReplacement(chr(decimal))); + } + m.appendTail(buf); + s = buf.toString(); + + buf = new StringBuffer(); + m = P_ENCODE.matcher(s); + while (m.find()) { + final String match = m.group(1); + final int decimal = Integer.valueOf(match, 16).intValue(); + m.appendReplacement(buf, Matcher.quoteReplacement(chr(decimal))); + } + m.appendTail(buf); + s = buf.toString(); + + s = validateEntities(s); + return s; + } + + private String validateEntities (final String s) { + StringBuffer buf = new StringBuffer(); + + // validate entities throughout the string + Matcher m = P_VALID_ENTITIES.matcher(s); + while (m.find()) { + final String one = m.group(1); // ([^&;]*) + final String two = m.group(2); // (?=(;|&|$)) + m.appendReplacement(buf, Matcher.quoteReplacement(checkEntity(one, two))); + } + m.appendTail(buf); + + return encodeQuotes(buf.toString()); + } + + private String encodeQuotes (final String s) { + if (encodeQuotes) { + StringBuffer buf = new StringBuffer(); + Matcher m = P_VALID_QUOTES.matcher(s); + while (m.find()) { + final String one = m.group(1); // (>|^) + final String two = m.group(2); // ([^<]+?) + final String three = m.group(3); // (<|$) + // 不替换双引号为",防止json格式无效 regexReplace(P_QUOTE, """, two) + m.appendReplacement(buf, Matcher.quoteReplacement(one + two + three)); + } + m.appendTail(buf); + return buf.toString(); + } else { + return s; + } + } + + private String checkEntity (final String preamble, final String term) { + + return ";".equals(term) && isValidEntity(preamble) ? '&' + preamble : "&" + preamble; + } + + private boolean isValidEntity (final String entity) { + return inArray(entity, vAllowedEntities); + } + + private 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)); + } +} diff --git a/src/main/java/com/muyu/common/core/utils/ip/IpUtils.java b/src/main/java/com/muyu/common/core/utils/ip/IpUtils.java new file mode 100644 index 0000000..e750583 --- /dev/null +++ b/src/main/java/com/muyu/common/core/utils/ip/IpUtils.java @@ -0,0 +1,331 @@ +package com.muyu.common.core.utils.ip; + +import com.muyu.common.core.utils.ServletUtils; +import com.muyu.common.core.utils.StringUtils; + +import jakarta.servlet.http.HttpServletRequest; +import java.net.InetAddress; +import java.net.UnknownHostException; + +/** + * 获取IP方法 + * + * @author muyu + */ +public class IpUtils { + public final static String REGX_0_255 = "(25[0-5]|2[0-4]\\d|1\\d{2}|[1-9]\\d|\\d)"; + // 匹配 ip + public final static String REGX_IP = "((" + REGX_0_255 + "\\.){3}" + REGX_0_255 + ")"; + // 匹配网段 + public final static String REGX_IP_SEG = "(" + REGX_IP + "\\-" + REGX_IP + ")"; + public final static String REGX_IP_WILDCARD = "(((\\*\\.){3}\\*)|(" + REGX_0_255 + "(\\.\\*){3})|(" + REGX_0_255 + "\\." + REGX_0_255 + ")(\\.\\*){2}" + "|((" + REGX_0_255 + "\\.){3}\\*))"; + + /** + * 获取客户端IP + * + * @return IP地址 + */ + public static String getIpAddr () { + return getIpAddr(ServletUtils.getRequest()); + } + + /** + * 获取客户端IP + * + * @param request 请求对象 + * + * @return IP地址 + */ + public static String getIpAddr (HttpServletRequest request) { + if (request == null) { + return "unknown"; + } + String ip = request.getHeader("x-forwarded-for"); + if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { + ip = request.getHeader("Proxy-Client-IP"); + } + if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { + ip = request.getHeader("X-Forwarded-For"); + } + if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { + ip = request.getHeader("WL-Proxy-Client-IP"); + } + if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { + ip = request.getHeader("X-Real-IP"); + } + + if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { + ip = request.getRemoteAddr(); + } + + return "0:0:0:0:0:0:0:1".equals(ip) ? "127.0.0.1" : getMultistageReverseProxyIp(ip); + } + + /** + * 检查是否为内部IP地址 + * + * @param ip IP地址 + * + * @return 结果 + */ + public static boolean internalIp (String ip) { + byte[] addr = textToNumericFormatV4(ip); + return internalIp(addr) || "127.0.0.1".equals(ip); + } + + /** + * 检查是否为内部IP地址 + * + * @param addr byte地址 + * + * @return 结果 + */ + private static boolean internalIp (byte[] addr) { + if (StringUtils.isNull(addr) || addr.length < 2) { + return true; + } + final byte b0 = addr[0]; + final byte b1 = addr[1]; + // 10.x.x.x/8 + final byte SECTION_1 = 0x0A; + // 172.16.x.x/12 + final byte SECTION_2 = (byte) 0xAC; + final byte SECTION_3 = (byte) 0x10; + final byte SECTION_4 = (byte) 0x1F; + // 192.168.x.x/16 + final byte SECTION_5 = (byte) 0xC0; + final byte SECTION_6 = (byte) 0xA8; + switch (b0) { + case SECTION_1: + return true; + case SECTION_2: + if (b1 >= SECTION_3 && b1 <= SECTION_4) { + return true; + } + case SECTION_5: + switch (b1) { + case SECTION_6: + return true; + } + default: + return false; + } + } + + /** + * 将IPv4地址转换成字节 + * + * @param text IPv4地址 + * + * @return byte 字节 + */ + public static byte[] textToNumericFormatV4 (String text) { + if (text.length() == 0) { + return null; + } + + byte[] bytes = new byte[4]; + String[] elements = text.split("\\.", -1); + try { + long l; + int i; + switch (elements.length) { + case 1: + l = Long.parseLong(elements[0]); + if ((l < 0L) || (l > 4294967295L)) { + return null; + } + bytes[0] = (byte) (int) (l >> 24 & 0xFF); + bytes[1] = (byte) (int) ((l & 0xFFFFFF) >> 16 & 0xFF); + bytes[2] = (byte) (int) ((l & 0xFFFF) >> 8 & 0xFF); + bytes[3] = (byte) (int) (l & 0xFF); + break; + case 2: + l = Integer.parseInt(elements[0]); + if ((l < 0L) || (l > 255L)) { + return null; + } + bytes[0] = (byte) (int) (l & 0xFF); + l = Integer.parseInt(elements[1]); + if ((l < 0L) || (l > 16777215L)) { + return null; + } + bytes[1] = (byte) (int) (l >> 16 & 0xFF); + bytes[2] = (byte) (int) ((l & 0xFFFF) >> 8 & 0xFF); + bytes[3] = (byte) (int) (l & 0xFF); + break; + case 3: + for (i = 0; i < 2 ; ++i) { + l = Integer.parseInt(elements[i]); + if ((l < 0L) || (l > 255L)) { + return null; + } + bytes[i] = (byte) (int) (l & 0xFF); + } + l = Integer.parseInt(elements[2]); + if ((l < 0L) || (l > 65535L)) { + return null; + } + bytes[2] = (byte) (int) (l >> 8 & 0xFF); + bytes[3] = (byte) (int) (l & 0xFF); + break; + case 4: + for (i = 0; i < 4 ; ++i) { + l = Integer.parseInt(elements[i]); + if ((l < 0L) || (l > 255L)) { + return null; + } + bytes[i] = (byte) (int) (l & 0xFF); + } + break; + default: + return null; + } + } catch (NumberFormatException e) { + return null; + } + return bytes; + } + + /** + * 获取IP地址 + * + * @return 本地IP地址 + */ + public static String getHostIp () { + try { + return InetAddress.getLocalHost().getHostAddress(); + } catch (UnknownHostException e) { + } + return "127.0.0.1"; + } + + /** + * 获取主机名 + * + * @return 本地主机名 + */ + public static String getHostName () { + try { + return InetAddress.getLocalHost().getHostName(); + } catch (UnknownHostException e) { + } + return "未知"; + } + + /** + * 从多级反向代理中获得第一个非unknown IP地址 + * + * @param ip 获得的IP地址 + * + * @return 第一个非unknown IP地址 + */ + public static String getMultistageReverseProxyIp (String ip) { + // 多级反向代理检测 + if (ip != null && ip.indexOf(",") > 0) { + final String[] ips = ip.trim().split(","); + for (String subIp : ips) { + if (false == isUnknown(subIp)) { + ip = subIp; + break; + } + } + } + return StringUtils.substring(ip, 0, 255); + } + + /** + * 检测给定字符串是否为未知,多用于检测HTTP请求相关 + * + * @param checkString 被检测的字符串 + * + * @return 是否未知 + */ + public static boolean isUnknown (String checkString) { + return StringUtils.isBlank(checkString) || "unknown".equalsIgnoreCase(checkString); + } + + /** + * 是否为IP + */ + public static boolean isIP (String ip) { + return StringUtils.isNotBlank(ip) && ip.matches(REGX_IP); + } + + /** + * 是否为IP,或 *为间隔的通配符地址 + */ + public static boolean isIpWildCard (String ip) { + return StringUtils.isNotBlank(ip) && ip.matches(REGX_IP_WILDCARD); + } + + /** + * 检测参数是否在ip通配符里 + */ + public static boolean ipIsInWildCardNoCheck (String ipWildCard, String ip) { + String[] s1 = ipWildCard.split("\\."); + String[] s2 = ip.split("\\."); + boolean isMatchedSeg = true; + for (int i = 0 ; i < s1.length && !s1[i].equals("*") ; i++) { + if (!s1[i].equals(s2[i])) { + isMatchedSeg = false; + break; + } + } + return isMatchedSeg; + } + + /** + * 是否为特定格式如:“10.10.10.1-10.10.10.99”的ip段字符串 + */ + public static boolean isIPSegment (String ipSeg) { + return StringUtils.isNotBlank(ipSeg) && ipSeg.matches(REGX_IP_SEG); + } + + /** + * 判断ip是否在指定网段中 + */ + public static boolean ipIsInNetNoCheck (String iparea, String ip) { + int idx = iparea.indexOf('-'); + String[] sips = iparea.substring(0, idx).split("\\."); + String[] sipe = iparea.substring(idx + 1).split("\\."); + String[] sipt = ip.split("\\."); + long ips = 0L, ipe = 0L, ipt = 0L; + for (int i = 0 ; i < 4 ; ++i) { + ips = ips << 8 | Integer.parseInt(sips[i]); + ipe = ipe << 8 | Integer.parseInt(sipe[i]); + ipt = ipt << 8 | Integer.parseInt(sipt[i]); + } + if (ips > ipe) { + long t = ips; + ips = ipe; + ipe = t; + } + return ips <= ipt && ipt <= ipe; + } + + /** + * 校验ip是否符合过滤串规则 + * + * @param filter 过滤IP列表,支持后缀'*'通配,支持网段如:`10.10.10.1-10.10.10.99` + * @param ip 校验IP地址 + * + * @return boolean 结果 + */ + public static boolean isMatchedIp (String filter, String ip) { + if (StringUtils.isEmpty(filter) || StringUtils.isEmpty(ip)) { + return false; + } + String[] ips = filter.split(";"); + for (String iStr : ips) { + if (isIP(iStr) && iStr.equals(ip)) { + return true; + } else if (isIpWildCard(iStr) && ipIsInWildCardNoCheck(iStr, ip)) { + return true; + } else if (isIPSegment(iStr) && ipIsInNetNoCheck(iStr, ip)) { + return true; + } + } + return false; + } +} diff --git a/src/main/java/com/muyu/common/core/utils/poi/ExcelHandlerAdapter.java b/src/main/java/com/muyu/common/core/utils/poi/ExcelHandlerAdapter.java new file mode 100644 index 0000000..e2041fc --- /dev/null +++ b/src/main/java/com/muyu/common/core/utils/poi/ExcelHandlerAdapter.java @@ -0,0 +1,23 @@ +package com.muyu.common.core.utils.poi; + +import org.apache.poi.ss.usermodel.Cell; +import org.apache.poi.ss.usermodel.Workbook; + +/** + * Excel数据格式处理适配器 + * + * @author muyu + */ +public interface ExcelHandlerAdapter { + /** + * 格式化 + * + * @param value 单元格数据值 + * @param args excel注解args参数组 + * @param cell 单元格对象 + * @param wb 工作簿对象 + * + * @return 处理后的值 + */ + Object format (Object value, String[] args, Cell cell, Workbook wb); +} diff --git a/src/main/java/com/muyu/common/core/utils/poi/ExcelUtil.java b/src/main/java/com/muyu/common/core/utils/poi/ExcelUtil.java new file mode 100644 index 0000000..16f2c39 --- /dev/null +++ b/src/main/java/com/muyu/common/core/utils/poi/ExcelUtil.java @@ -0,0 +1,1230 @@ +package com.muyu.common.core.utils.poi; + +import com.muyu.common.core.annotation.Excel; +import com.muyu.common.core.annotation.Excel.ColumnType; +import com.muyu.common.core.annotation.Excel.Type; +import com.muyu.common.core.annotation.Excels; +import com.muyu.common.core.exception.UtilException; +import com.muyu.common.core.text.Convert; +import com.muyu.common.core.utils.DateUtils; +import com.muyu.common.core.utils.StringUtils; +import com.muyu.common.core.utils.file.FileTypeUtils; +import com.muyu.common.core.utils.file.ImageUtils; +import com.muyu.common.core.utils.reflect.ReflectUtils; +import org.apache.commons.lang3.ArrayUtils; +import org.apache.commons.lang3.RegExUtils; +import org.apache.commons.lang3.reflect.FieldUtils; +import org.apache.poi.ss.usermodel.*; +import org.apache.poi.ss.util.CellRangeAddress; +import org.apache.poi.ss.util.CellRangeAddressList; +import org.apache.poi.util.IOUtils; +import org.apache.poi.xssf.streaming.SXSSFWorkbook; +import org.apache.poi.xssf.usermodel.XSSFClientAnchor; +import org.apache.poi.xssf.usermodel.XSSFDataValidation; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import jakarta.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.io.InputStream; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.lang.reflect.ParameterizedType; +import java.math.BigDecimal; +import java.text.DecimalFormat; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.util.*; +import java.util.stream.Collectors; + +/** + * Excel相关处理 + * + * @author muyu + */ +public class ExcelUtil { + public static final String FORMULA_REGEX_STR = "=|-|\\+|@"; + public static final String[] FORMULA_STR = {"=", "-", "+", "@"}; + /** + * Excel sheet最大行数,默认65536 + */ + public static final int sheetSize = 65536; + private static final Logger log = LoggerFactory.getLogger(ExcelUtil.class); + /** + * 数字格式 + */ + private static final DecimalFormat DOUBLE_FORMAT = new DecimalFormat("######0.00"); + /** + * 实体对象 + */ + public Class clazz; + /** + * 需要排除列属性 + */ + public String[] excludeFields; + /** + * 工作表名称 + */ + private String sheetName; + /** + * 导出类型(EXPORT:导出数据;IMPORT:导入模板) + */ + private Type type; + /** + * 工作薄对象 + */ + private Workbook wb; + /** + * 工作表对象 + */ + private Sheet sheet; + /** + * 样式列表 + */ + private Map styles; + /** + * 导入导出数据列表 + */ + private List list; + /** + * 注解列表 + */ + private List fields; + /** + * 当前行号 + */ + private int rownum; + /** + * 标题 + */ + private String title; + /** + * 最大高度 + */ + private short maxHeight; + /** + * 合并后最后行数 + */ + private int subMergedLastRowNum = 0; + /** + * 合并后开始行数 + */ + private int subMergedFirstRowNum = 1; + /** + * 对象的子列表方法 + */ + private Method subMethod; + /** + * 对象的子列表属性 + */ + private List subFields; + /** + * 统计列表 + */ + private Map statistics = new HashMap(); + + public ExcelUtil (Class clazz) { + this.clazz = clazz; + } + + /** + * 获取画布 + */ + public static Drawing getDrawingPatriarch (Sheet sheet) { + if (sheet.getDrawingPatriarch() == null) { + sheet.createDrawingPatriarch(); + } + return sheet.getDrawingPatriarch(); + } + + /** + * 解析导出值 0=男,1=女,2=未知 + * + * @param propertyValue 参数值 + * @param converterExp 翻译注解 + * @param separator 分隔符 + * + * @return 解析后值 + */ + public static String convertByExp (String propertyValue, String converterExp, String separator) { + StringBuilder propertyString = new StringBuilder(); + String[] convertSource = converterExp.split(","); + for (String item : convertSource) { + String[] itemArray = item.split("="); + if (StringUtils.containsAny(propertyValue, separator)) { + for (String value : propertyValue.split(separator)) { + if (itemArray[0].equals(value)) { + propertyString.append(itemArray[1] + separator); + break; + } + } + } else { + if (itemArray[0].equals(propertyValue)) { + return itemArray[1]; + } + } + } + return StringUtils.stripEnd(propertyString.toString(), separator); + } + + /** + * 反向解析值 男=0,女=1,未知=2 + * + * @param propertyValue 参数值 + * @param converterExp 翻译注解 + * @param separator 分隔符 + * + * @return 解析后值 + */ + public static String reverseByExp (String propertyValue, String converterExp, String separator) { + StringBuilder propertyString = new StringBuilder(); + String[] convertSource = converterExp.split(","); + for (String item : convertSource) { + String[] itemArray = item.split("="); + if (StringUtils.containsAny(propertyValue, separator)) { + for (String value : propertyValue.split(separator)) { + if (itemArray[1].equals(value)) { + propertyString.append(itemArray[0] + separator); + break; + } + } + } else { + if (itemArray[1].equals(propertyValue)) { + return itemArray[0]; + } + } + } + return StringUtils.stripEnd(propertyString.toString(), separator); + } + + /** + * 隐藏Excel中列属性 + * + * @param fields 列属性名 示例[单个"name"/多个"id","name"] + * + * @throws Exception + */ + public void hideColumn (String... fields) { + this.excludeFields = fields; + } + + public void init (List list, String sheetName, String title, Type type) { + if (list == null) { + list = new ArrayList(); + } + this.list = list; + this.sheetName = sheetName; + this.type = type; + this.title = title; + createExcelField(); + createWorkbook(); + createTitle(); + createSubHead(); + } + + /** + * 创建excel第一行标题 + */ + public void createTitle () { + if (StringUtils.isNotEmpty(title)) { + subMergedFirstRowNum++; + subMergedLastRowNum++; + int titleLastCol = this.fields.size() - 1; + if (isSubList()) { + titleLastCol = titleLastCol + subFields.size() - 1; + } + Row titleRow = sheet.createRow(rownum == 0 ? rownum++ : 0); + titleRow.setHeightInPoints(30); + Cell titleCell = titleRow.createCell(0); + titleCell.setCellStyle(styles.get("title")); + titleCell.setCellValue(title); + sheet.addMergedRegion(new CellRangeAddress(titleRow.getRowNum(), titleRow.getRowNum(), titleRow.getRowNum(), titleLastCol)); + } + } + + /** + * 创建对象的子列表名称 + */ + public void createSubHead () { + if (isSubList()) { + subMergedFirstRowNum++; + subMergedLastRowNum++; + Row subRow = sheet.createRow(rownum); + int excelNum = 0; + for (Object[] objects : fields) { + Excel attr = (Excel) objects[1]; + Cell headCell1 = subRow.createCell(excelNum); + headCell1.setCellValue(attr.name()); + headCell1.setCellStyle(styles.get(StringUtils.format("header_{}_{}", attr.headerColor(), attr.headerBackgroundColor()))); + excelNum++; + } + int headFirstRow = excelNum - 1; + int headLastRow = headFirstRow + subFields.size() - 1; + if (headLastRow > headFirstRow) { + sheet.addMergedRegion(new CellRangeAddress(rownum, rownum, headFirstRow, headLastRow)); + } + rownum++; + } + } + + /** + * 对excel表单默认第一个索引名转换成list + * + * @param is 输入流 + * + * @return 转换后集合 + */ + public List importExcel (InputStream is) { + List list = null; + try { + list = importExcel(is, 0); + } catch (Exception e) { + log.error("导入Excel异常{}", e.getMessage()); + throw new UtilException(e.getMessage()); + } finally { + IOUtils.closeQuietly(is); + } + return list; + } + + /** + * 对excel表单默认第一个索引名转换成list + * + * @param is 输入流 + * @param titleNum 标题占用行数 + * + * @return 转换后集合 + */ + public List importExcel (InputStream is, int titleNum) throws Exception { + return importExcel(StringUtils.EMPTY, is, titleNum); + } + + /** + * 对excel表单指定表格索引名转换成list + * + * @param sheetName 表格索引名 + * @param titleNum 标题占用行数 + * @param is 输入流 + * + * @return 转换后集合 + */ + public List importExcel (String sheetName, InputStream is, int titleNum) throws Exception { + this.type = Type.IMPORT; + this.wb = WorkbookFactory.create(is); + List list = new ArrayList(); + // 如果指定sheet名,则取指定sheet中的内容 否则默认指向第1个sheet + Sheet sheet = StringUtils.isNotEmpty(sheetName) ? wb.getSheet(sheetName) : wb.getSheetAt(0); + if (sheet == null) { + throw new IOException("文件sheet不存在"); + } + + // 获取最后一个非空行的行下标,比如总行数为n,则返回的为n-1 + int rows = sheet.getLastRowNum(); + if (rows > 0) { + // 定义一个map用于存放excel列的序号和field. + Map cellMap = new HashMap(); + // 获取表头 + Row heard = sheet.getRow(titleNum); + for (int i = 0 ; i < heard.getPhysicalNumberOfCells() ; i++) { + Cell cell = heard.getCell(i); + if (StringUtils.isNotNull(cell)) { + String value = this.getCellValue(heard, i).toString(); + cellMap.put(value, i); + } else { + cellMap.put(null, i); + } + } + // 有数据时才处理 得到类的所有field. + List fields = this.getFields(); + Map fieldsMap = new HashMap(); + for (Object[] objects : fields) { + Excel attr = (Excel) objects[1]; + Integer column = cellMap.get(attr.name()); + if (column != null) { + fieldsMap.put(column, objects); + } + } + for (int i = titleNum + 1 ; i <= rows ; i++) { + // 从第2行开始取数据,默认第一行是表头. + Row row = sheet.getRow(i); + // 判断当前行是否是空行 + if (isRowEmpty(row)) { + continue; + } + T entity = null; + for (Map.Entry entry : fieldsMap.entrySet()) { + Object val = this.getCellValue(row, entry.getKey()); + + // 如果不存在实例则新建. + entity = (entity == null ? clazz.newInstance() : entity); + // 从map中得到对应列的field. + Field field = (Field) entry.getValue()[0]; + Excel attr = (Excel) entry.getValue()[1]; + // 取得类型,并根据对象类型设置值. + Class fieldType = field.getType(); + if (String.class == fieldType) { + String s = Convert.toStr(val); + if (StringUtils.endsWith(s, ".0")) { + val = StringUtils.substringBefore(s, ".0"); + } else { + String dateFormat = field.getAnnotation(Excel.class).dateFormat(); + if (StringUtils.isNotEmpty(dateFormat)) { + val = parseDateToStr(dateFormat, val); + } else { + val = Convert.toStr(val); + } + } + } else if ((Integer.TYPE == fieldType || Integer.class == fieldType) && StringUtils.isNumeric(Convert.toStr(val))) { + val = Convert.toInt(val); + } else if ((Long.TYPE == fieldType || Long.class == fieldType) && StringUtils.isNumeric(Convert.toStr(val))) { + val = Convert.toLong(val); + } else if (Double.TYPE == fieldType || Double.class == fieldType) { + val = Convert.toDouble(val); + } else if (Float.TYPE == fieldType || Float.class == fieldType) { + val = Convert.toFloat(val); + } else if (BigDecimal.class == fieldType) { + val = Convert.toBigDecimal(val); + } else if (Date.class == fieldType) { + if (val instanceof String) { + val = DateUtils.parseDate(val); + } else if (val instanceof Double) { + val = DateUtil.getJavaDate((Double) val); + } + } else if (Boolean.TYPE == fieldType || Boolean.class == fieldType) { + val = Convert.toBool(val, false); + } + if (StringUtils.isNotNull(fieldType)) { + String propertyName = field.getName(); + if (StringUtils.isNotEmpty(attr.targetAttr())) { + propertyName = field.getName() + "." + attr.targetAttr(); + } + if (StringUtils.isNotEmpty(attr.readConverterExp())) { + val = reverseByExp(Convert.toStr(val), attr.readConverterExp(), attr.separator()); + } else if (!attr.handler().equals(ExcelHandlerAdapter.class)) { + val = dataFormatHandlerAdapter(val, attr, null); + } + ReflectUtils.invokeSetter(entity, propertyName, val); + } + } + list.add(entity); + } + } + return list; + } + + /** + * 对list数据源将其里面的数据导入到excel表单 + * + * @param response 返回数据 + * @param list 导出数据集合 + * @param sheetName 工作表的名称 + * + * @return 结果 + */ + public void exportExcel (HttpServletResponse response, List list, String sheetName) { + exportExcel(response, list, sheetName, StringUtils.EMPTY); + } + + /** + * 对list数据源将其里面的数据导入到excel表单 + * + * @param response 返回数据 + * @param list 导出数据集合 + * @param sheetName 工作表的名称 + * @param title 标题 + * + * @return 结果 + */ + public void exportExcel (HttpServletResponse response, List list, String sheetName, String title) { + response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); + response.setCharacterEncoding("utf-8"); + this.init(list, sheetName, title, Type.EXPORT); + exportExcel(response); + } + + /** + * 对list数据源将其里面的数据导入到excel表单 + * + * @param sheetName 工作表的名称 + * + * @return 结果 + */ + public void importTemplateExcel (HttpServletResponse response, String sheetName) { + importTemplateExcel(response, sheetName, StringUtils.EMPTY); + } + + /** + * 对list数据源将其里面的数据导入到excel表单 + * + * @param sheetName 工作表的名称 + * @param title 标题 + * + * @return 结果 + */ + public void importTemplateExcel (HttpServletResponse response, String sheetName, String title) { + response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); + response.setCharacterEncoding("utf-8"); + this.init(null, sheetName, title, Type.IMPORT); + exportExcel(response); + } + + /** + * 对list数据源将其里面的数据导入到excel表单 + * + * @return 结果 + */ + public void exportExcel (HttpServletResponse response) { + try { + writeSheet(); + wb.write(response.getOutputStream()); + } catch (Exception e) { + log.error("导出Excel异常{}", e.getMessage()); + } finally { + IOUtils.closeQuietly(wb); + } + } + + /** + * 创建写入数据到Sheet + */ + public void writeSheet () { + // 取出一共有多少个sheet. + int sheetNo = Math.max(1, (int) Math.ceil(list.size() * 1.0 / sheetSize)); + for (int index = 0 ; index < sheetNo ; index++) { + createSheet(sheetNo, index); + + // 产生一行 + Row row = sheet.createRow(rownum); + int column = 0; + // 写入各个字段的列头名称 + for (Object[] os : fields) { + Field field = (Field) os[0]; + Excel excel = (Excel) os[1]; + if (Collection.class.isAssignableFrom(field.getType())) { + for (Field subField : subFields) { + Excel subExcel = subField.getAnnotation(Excel.class); + this.createHeadCell(subExcel, row, column++); + } + } else { + this.createHeadCell(excel, row, column++); + } + } + if (Type.EXPORT.equals(type)) { + fillExcelData(index, row); + addStatisticsRow(); + } + } + } + + /** + * 填充excel数据 + * + * @param index 序号 + * @param row 单元格行 + */ + @SuppressWarnings("unchecked") + public void fillExcelData (int index, Row row) { + int startNo = index * sheetSize; + int endNo = Math.min(startNo + sheetSize, list.size()); + int rowNo = (1 + rownum) - startNo; + for (int i = startNo ; i < endNo ; i++) { + rowNo = isSubList() ? (i > 1 ? rowNo + 1 : rowNo + i) : i + 1 + rownum - startNo; + row = sheet.createRow(rowNo); + // 得到导出对象. + T vo = (T) list.get(i); + Collection subList = null; + if (isSubList()) { + if (isSubListValue(vo)) { + subList = getListCellValue(vo); + subMergedLastRowNum = subMergedLastRowNum + subList.size(); + } else { + subMergedFirstRowNum++; + subMergedLastRowNum++; + } + } + int column = 0; + for (Object[] os : fields) { + Field field = (Field) os[0]; + Excel excel = (Excel) os[1]; + if (Collection.class.isAssignableFrom(field.getType()) && StringUtils.isNotNull(subList)) { + boolean subFirst = false; + for (Object obj : subList) { + if (subFirst) { + rowNo++; + row = sheet.createRow(rowNo); + } + List subFields = FieldUtils.getFieldsListWithAnnotation(obj.getClass(), Excel.class); + int subIndex = 0; + for (Field subField : subFields) { + if (subField.isAnnotationPresent(Excel.class)) { + subField.setAccessible(true); + Excel attr = subField.getAnnotation(Excel.class); + this.addCell(attr, row, (T) obj, subField, column + subIndex); + } + subIndex++; + } + subFirst = true; + } + this.subMergedFirstRowNum = this.subMergedFirstRowNum + subList.size(); + } else { + this.addCell(excel, row, vo, field, column++); + } + } + } + } + + /** + * 创建表格样式 + * + * @param wb 工作薄对象 + * + * @return 样式列表 + */ + private Map createStyles (Workbook wb) { + // 写入各条记录,每条记录对应excel表中的一行 + Map styles = new HashMap(); + CellStyle style = wb.createCellStyle(); + style.setAlignment(HorizontalAlignment.CENTER); + style.setVerticalAlignment(VerticalAlignment.CENTER); + Font titleFont = wb.createFont(); + titleFont.setFontName("Arial"); + titleFont.setFontHeightInPoints((short) 16); + titleFont.setBold(true); + style.setFont(titleFont); + styles.put("title", style); + + style = wb.createCellStyle(); + style.setAlignment(HorizontalAlignment.CENTER); + style.setVerticalAlignment(VerticalAlignment.CENTER); + style.setBorderRight(BorderStyle.THIN); + style.setRightBorderColor(IndexedColors.GREY_50_PERCENT.getIndex()); + style.setBorderLeft(BorderStyle.THIN); + style.setLeftBorderColor(IndexedColors.GREY_50_PERCENT.getIndex()); + style.setBorderTop(BorderStyle.THIN); + style.setTopBorderColor(IndexedColors.GREY_50_PERCENT.getIndex()); + style.setBorderBottom(BorderStyle.THIN); + style.setBottomBorderColor(IndexedColors.GREY_50_PERCENT.getIndex()); + Font dataFont = wb.createFont(); + dataFont.setFontName("Arial"); + dataFont.setFontHeightInPoints((short) 10); + style.setFont(dataFont); + styles.put("data", style); + + style = wb.createCellStyle(); + style.setAlignment(HorizontalAlignment.CENTER); + style.setVerticalAlignment(VerticalAlignment.CENTER); + Font totalFont = wb.createFont(); + totalFont.setFontName("Arial"); + totalFont.setFontHeightInPoints((short) 10); + style.setFont(totalFont); + styles.put("total", style); + + styles.putAll(annotationHeaderStyles(wb, styles)); + + styles.putAll(annotationDataStyles(wb)); + + return styles; + } + + /** + * 根据Excel注解创建表格头样式 + * + * @param wb 工作薄对象 + * + * @return 自定义样式列表 + */ + private Map annotationHeaderStyles (Workbook wb, Map styles) { + Map headerStyles = new HashMap(); + for (Object[] os : fields) { + Excel excel = (Excel) os[1]; + String key = StringUtils.format("header_{}_{}", excel.headerColor(), excel.headerBackgroundColor()); + if (!headerStyles.containsKey(key)) { + CellStyle style = wb.createCellStyle(); + style.cloneStyleFrom(styles.get("data")); + style.setAlignment(HorizontalAlignment.CENTER); + style.setVerticalAlignment(VerticalAlignment.CENTER); + style.setFillForegroundColor(excel.headerBackgroundColor().index); + style.setFillPattern(FillPatternType.SOLID_FOREGROUND); + Font headerFont = wb.createFont(); + headerFont.setFontName("Arial"); + headerFont.setFontHeightInPoints((short) 10); + headerFont.setBold(true); + headerFont.setColor(excel.headerColor().index); + style.setFont(headerFont); + headerStyles.put(key, style); + } + } + return headerStyles; + } + + /** + * 根据Excel注解创建表格列样式 + * + * @param wb 工作薄对象 + * + * @return 自定义样式列表 + */ + private Map annotationDataStyles (Workbook wb) { + Map styles = new HashMap(); + for (Object[] os : fields) { + Excel excel = (Excel) os[1]; + String key = StringUtils.format("data_{}_{}_{}", excel.align(), excel.color(), excel.backgroundColor()); + if (!styles.containsKey(key)) { + CellStyle style = wb.createCellStyle(); + style.setAlignment(excel.align()); + style.setVerticalAlignment(VerticalAlignment.CENTER); + style.setBorderRight(BorderStyle.THIN); + style.setRightBorderColor(IndexedColors.GREY_50_PERCENT.getIndex()); + style.setBorderLeft(BorderStyle.THIN); + style.setLeftBorderColor(IndexedColors.GREY_50_PERCENT.getIndex()); + style.setBorderTop(BorderStyle.THIN); + style.setTopBorderColor(IndexedColors.GREY_50_PERCENT.getIndex()); + style.setBorderBottom(BorderStyle.THIN); + style.setBottomBorderColor(IndexedColors.GREY_50_PERCENT.getIndex()); + style.setFillPattern(FillPatternType.SOLID_FOREGROUND); + style.setFillForegroundColor(excel.backgroundColor().getIndex()); + Font dataFont = wb.createFont(); + dataFont.setFontName("Arial"); + dataFont.setFontHeightInPoints((short) 10); + dataFont.setColor(excel.color().index); + style.setFont(dataFont); + styles.put(key, style); + } + } + return styles; + } + + /** + * 创建单元格 + */ + public Cell createHeadCell (Excel attr, Row row, int column) { + // 创建列 + Cell cell = row.createCell(column); + // 写入列信息 + cell.setCellValue(attr.name()); + setDataValidation(attr, row, column); + cell.setCellStyle(styles.get(StringUtils.format("header_{}_{}", attr.headerColor(), attr.headerBackgroundColor()))); + if (isSubList()) { + // 填充默认样式,防止合并单元格样式失效 + sheet.setDefaultColumnStyle(column, styles.get(StringUtils.format("data_{}_{}_{}", attr.align(), attr.color(), attr.backgroundColor()))); + if (attr.needMerge()) { + sheet.addMergedRegion(new CellRangeAddress(rownum - 1, rownum, column, column)); + } + } + return cell; + } + + /** + * 设置单元格信息 + * + * @param value 单元格值 + * @param attr 注解相关 + * @param cell 单元格信息 + */ + public void setCellVo (Object value, Excel attr, Cell cell) { + if (ColumnType.STRING == attr.cellType()) { + String cellValue = Convert.toStr(value); + // 对于任何以表达式触发字符 =-+@开头的单元格,直接使用tab字符作为前缀,防止CSV注入。 + if (StringUtils.startsWithAny(cellValue, FORMULA_STR)) { + cellValue = RegExUtils.replaceFirst(cellValue, FORMULA_REGEX_STR, "\t$0"); + } + if (value instanceof Collection && StringUtils.equals("[]", cellValue)) { + cellValue = StringUtils.EMPTY; + } + cell.setCellValue(StringUtils.isNull(cellValue) ? attr.defaultValue() : cellValue + attr.suffix()); + } else if (ColumnType.NUMERIC == attr.cellType()) { + if (StringUtils.isNotNull(value)) { + cell.setCellValue(StringUtils.contains(Convert.toStr(value), ".") ? Convert.toDouble(value) : Convert.toInt(value)); + } + } else if (ColumnType.IMAGE == attr.cellType()) { + ClientAnchor anchor = new XSSFClientAnchor(0, 0, 0, 0, (short) cell.getColumnIndex(), cell.getRow().getRowNum(), (short) (cell.getColumnIndex() + 1), cell.getRow().getRowNum() + 1); + String imagePath = Convert.toStr(value); + if (StringUtils.isNotEmpty(imagePath)) { + byte[] data = ImageUtils.getImage(imagePath); + getDrawingPatriarch(cell.getSheet()).createPicture(anchor, + cell.getSheet().getWorkbook().addPicture(data, getImageType(data))); + } + } + } + + /** + * 获取图片类型,设置图片插入类型 + */ + public int getImageType (byte[] value) { + String type = FileTypeUtils.getFileExtendName(value); + if ("JPG".equalsIgnoreCase(type)) { + return Workbook.PICTURE_TYPE_JPEG; + } else if ("PNG".equalsIgnoreCase(type)) { + return Workbook.PICTURE_TYPE_PNG; + } + return Workbook.PICTURE_TYPE_JPEG; + } + + /** + * 创建表格样式 + */ + public void setDataValidation (Excel attr, Row row, int column) { + if (attr.name().indexOf("注:") >= 0) { + sheet.setColumnWidth(column, 6000); + } else { + // 设置列宽 + sheet.setColumnWidth(column, (int) ((attr.width() + 0.72) * 256)); + } + if (StringUtils.isNotEmpty(attr.prompt()) || attr.combo().length > 0) { + if (attr.combo().length > 15 || StringUtils.join(attr.combo()).length() > 255) { + // 如果下拉数大于15或字符串长度大于255,则使用一个新sheet存储,避免生成的模板下拉值获取不到 + setXSSFValidationWithHidden(sheet, attr.combo(), attr.prompt(), 1, 100, column, column); + } else { + // 提示信息或只能选择不能输入的列内容. + setPromptOrValidation(sheet, attr.combo(), attr.prompt(), 1, 100, column, column); + } + } + } + + /** + * 添加单元格 + */ + public Cell addCell (Excel attr, Row row, T vo, Field field, int column) { + Cell cell = null; + try { + // 设置行高 + row.setHeight(maxHeight); + // 根据Excel中设置情况决定是否导出,有些情况需要保持为空,希望用户填写这一列. + if (attr.isExport()) { + // 创建cell + cell = row.createCell(column); + if (isSubListValue(vo) && getListCellValue(vo).size() > 1 && attr.needMerge()) { + CellRangeAddress cellAddress = new CellRangeAddress(subMergedFirstRowNum, subMergedLastRowNum, column, column); + sheet.addMergedRegion(cellAddress); + } + cell.setCellStyle(styles.get(StringUtils.format("data_{}_{}_{}", attr.align(), attr.color(), attr.backgroundColor()))); + + // 用于读取对象中的属性 + Object value = getTargetValue(vo, field, attr); + String dateFormat = attr.dateFormat(); + String readConverterExp = attr.readConverterExp(); + String separator = attr.separator(); + if (StringUtils.isNotEmpty(dateFormat) && StringUtils.isNotNull(value)) { + cell.setCellValue(parseDateToStr(dateFormat, value)); + } else if (StringUtils.isNotEmpty(readConverterExp) && StringUtils.isNotNull(value)) { + cell.setCellValue(convertByExp(Convert.toStr(value), readConverterExp, separator)); + } else if (value instanceof BigDecimal && -1 != attr.scale()) { + cell.setCellValue((((BigDecimal) value).setScale(attr.scale(), attr.roundingMode())).doubleValue()); + } else if (!attr.handler().equals(ExcelHandlerAdapter.class)) { + cell.setCellValue(dataFormatHandlerAdapter(value, attr, cell)); + } else { + // 设置列类型 + setCellVo(value, attr, cell); + } + addStatisticsData(column, Convert.toStr(value), attr); + } + } catch (Exception e) { + log.error("导出Excel失败{}", e); + } + return cell; + } + + /** + * 设置 POI XSSFSheet 单元格提示或选择框 + * + * @param sheet 表单 + * @param textlist 下拉框显示的内容 + * @param promptContent 提示内容 + * @param firstRow 开始行 + * @param endRow 结束行 + * @param firstCol 开始列 + * @param endCol 结束列 + */ + public void setPromptOrValidation (Sheet sheet, String[] textlist, String promptContent, int firstRow, int endRow, + int firstCol, int endCol) { + DataValidationHelper helper = sheet.getDataValidationHelper(); + DataValidationConstraint constraint = textlist.length > 0 ? helper.createExplicitListConstraint(textlist) : helper.createCustomConstraint("DD1"); + CellRangeAddressList regions = new CellRangeAddressList(firstRow, endRow, firstCol, endCol); + DataValidation dataValidation = helper.createValidation(constraint, regions); + if (StringUtils.isNotEmpty(promptContent)) { + // 如果设置了提示信息则鼠标放上去提示 + dataValidation.createPromptBox("", promptContent); + dataValidation.setShowPromptBox(true); + } + // 处理Excel兼容性问题 + if (dataValidation instanceof XSSFDataValidation) { + dataValidation.setSuppressDropDownArrow(true); + dataValidation.setShowErrorBox(true); + } else { + dataValidation.setSuppressDropDownArrow(false); + } + sheet.addValidationData(dataValidation); + } + + /** + * 设置某些列的值只能输入预制的数据,显示下拉框(兼容超出一定数量的下拉框). + * + * @param sheet 要设置的sheet. + * @param textlist 下拉框显示的内容 + * @param promptContent 提示内容 + * @param firstRow 开始行 + * @param endRow 结束行 + * @param firstCol 开始列 + * @param endCol 结束列 + */ + public void setXSSFValidationWithHidden (Sheet sheet, String[] textlist, String promptContent, int firstRow, int endRow, int firstCol, int endCol) { + String hideSheetName = "combo_" + firstCol + "_" + endCol; + Sheet hideSheet = wb.createSheet(hideSheetName); // 用于存储 下拉菜单数据 + for (int i = 0 ; i < textlist.length ; i++) { + hideSheet.createRow(i).createCell(0).setCellValue(textlist[i]); + } + // 创建名称,可被其他单元格引用 + Name name = wb.createName(); + name.setNameName(hideSheetName + "_data"); + name.setRefersToFormula(hideSheetName + "!$A$1:$A$" + textlist.length); + DataValidationHelper helper = sheet.getDataValidationHelper(); + // 加载下拉列表内容 + DataValidationConstraint constraint = helper.createFormulaListConstraint(hideSheetName + "_data"); + // 设置数据有效性加载在哪个单元格上,四个参数分别是:起始行、终止行、起始列、终止列 + CellRangeAddressList regions = new CellRangeAddressList(firstRow, endRow, firstCol, endCol); + // 数据有效性对象 + DataValidation dataValidation = helper.createValidation(constraint, regions); + if (StringUtils.isNotEmpty(promptContent)) { + // 如果设置了提示信息则鼠标放上去提示 + dataValidation.createPromptBox("", promptContent); + dataValidation.setShowPromptBox(true); + } + // 处理Excel兼容性问题 + if (dataValidation instanceof XSSFDataValidation) { + dataValidation.setSuppressDropDownArrow(true); + dataValidation.setShowErrorBox(true); + } else { + dataValidation.setSuppressDropDownArrow(false); + } + + sheet.addValidationData(dataValidation); + // 设置hiddenSheet隐藏 + wb.setSheetHidden(wb.getSheetIndex(hideSheet), true); + } + + /** + * 数据处理器 + * + * @param value 数据值 + * @param excel 数据注解 + * + * @return + */ + public String dataFormatHandlerAdapter (Object value, Excel excel, Cell cell) { + try { + Object instance = excel.handler().newInstance(); + Method formatMethod = excel.handler().getMethod("format", new Class[]{Object.class, String[].class, Cell.class, Workbook.class}); + value = formatMethod.invoke(instance, value, excel.args(), cell, this.wb); + } catch (Exception e) { + log.error("不能格式化数据 " + excel.handler(), e.getMessage()); + } + return Convert.toStr(value); + } + + /** + * 合计统计信息 + */ + private void addStatisticsData (Integer index, String text, Excel entity) { + if (entity != null && entity.isStatistics()) { + Double temp = 0D; + if (!statistics.containsKey(index)) { + statistics.put(index, temp); + } + try { + temp = Double.valueOf(text); + } catch (NumberFormatException e) { + } + statistics.put(index, statistics.get(index) + temp); + } + } + + /** + * 创建统计行 + */ + public void addStatisticsRow () { + if (statistics.size() > 0) { + Row row = sheet.createRow(sheet.getLastRowNum() + 1); + Set keys = statistics.keySet(); + Cell cell = row.createCell(0); + cell.setCellStyle(styles.get("total")); + cell.setCellValue("合计"); + + for (Integer key : keys) { + cell = row.createCell(key); + cell.setCellStyle(styles.get("total")); + cell.setCellValue(DOUBLE_FORMAT.format(statistics.get(key))); + } + statistics.clear(); + } + } + + /** + * 获取bean中的属性值 + * + * @param vo 实体对象 + * @param field 字段 + * @param excel 注解 + * + * @return 最终的属性值 + * + * @throws Exception + */ + private Object getTargetValue (T vo, Field field, Excel excel) throws Exception { + Object o = field.get(vo); + if (StringUtils.isNotEmpty(excel.targetAttr())) { + String target = excel.targetAttr(); + if (target.contains(".")) { + String[] targets = target.split("[.]"); + for (String name : targets) { + o = getValue(o, name); + } + } else { + o = getValue(o, target); + } + } + return o; + } + + /** + * 以类的属性的get方法方法形式获取值 + * + * @param o + * @param name + * + * @return value + * + * @throws Exception + */ + private Object getValue (Object o, String name) throws Exception { + if (StringUtils.isNotNull(o) && StringUtils.isNotEmpty(name)) { + Class clazz = o.getClass(); + Field field = clazz.getDeclaredField(name); + field.setAccessible(true); + o = field.get(o); + } + return o; + } + + /** + * 得到所有定义字段 + */ + private void createExcelField () { + this.fields = getFields(); + this.fields = this.fields.stream().sorted(Comparator.comparing(objects -> ((Excel) objects[1]).sort())).collect(Collectors.toList()); + this.maxHeight = getRowHeight(); + } + + /** + * 获取字段注解信息 + */ + public List getFields () { + List fields = new ArrayList(); + List tempFields = new ArrayList<>(); + tempFields.addAll(Arrays.asList(clazz.getSuperclass().getDeclaredFields())); + tempFields.addAll(Arrays.asList(clazz.getDeclaredFields())); + for (Field field : tempFields) { + if (!ArrayUtils.contains(this.excludeFields, field.getName())) { + // 单注解 + if (field.isAnnotationPresent(Excel.class)) { + Excel attr = field.getAnnotation(Excel.class); + if (attr != null && (attr.type() == Type.ALL || attr.type() == type)) { + field.setAccessible(true); + fields.add(new Object[]{field, attr}); + } + if (Collection.class.isAssignableFrom(field.getType())) { + subMethod = getSubMethod(field.getName(), clazz); + ParameterizedType pt = (ParameterizedType) field.getGenericType(); + Class subClass = (Class) pt.getActualTypeArguments()[0]; + this.subFields = FieldUtils.getFieldsListWithAnnotation(subClass, Excel.class); + } + } + + // 多注解 + if (field.isAnnotationPresent(Excels.class)) { + Excels attrs = field.getAnnotation(Excels.class); + Excel[] excels = attrs.value(); + for (Excel attr : excels) { + if (!ArrayUtils.contains(this.excludeFields, field.getName() + "." + attr.targetAttr()) + && (attr != null && (attr.type() == Type.ALL || attr.type() == type))) { + field.setAccessible(true); + fields.add(new Object[]{field, attr}); + } + } + } + } + } + return fields; + } + + /** + * 根据注解获取最大行高 + */ + public short getRowHeight () { + double maxHeight = 0; + for (Object[] os : this.fields) { + Excel excel = (Excel) os[1]; + maxHeight = Math.max(maxHeight, excel.height()); + } + return (short) (maxHeight * 20); + } + + /** + * 创建一个工作簿 + */ + public void createWorkbook () { + this.wb = new SXSSFWorkbook(500); + this.sheet = wb.createSheet(); + wb.setSheetName(0, sheetName); + this.styles = createStyles(wb); + } + + /** + * 创建工作表 + * + * @param sheetNo sheet数量 + * @param index 序号 + */ + public void createSheet (int sheetNo, int index) { + // 设置工作表的名称. + if (sheetNo > 1 && index > 0) { + this.sheet = wb.createSheet(); + this.createTitle(); + wb.setSheetName(index, sheetName + index); + } + } + + /** + * 获取单元格值 + * + * @param row 获取的行 + * @param column 获取单元格列号 + * + * @return 单元格值 + */ + public Object getCellValue (Row row, int column) { + if (row == null) { + return row; + } + Object val = ""; + try { + Cell cell = row.getCell(column); + if (StringUtils.isNotNull(cell)) { + if (cell.getCellType() == CellType.NUMERIC || cell.getCellType() == CellType.FORMULA) { + val = cell.getNumericCellValue(); + if (DateUtil.isCellDateFormatted(cell)) { + val = DateUtil.getJavaDate((Double) val); // POI Excel 日期格式转换 + } else { + if ((Double) val % 1 != 0) { + val = new BigDecimal(val.toString()); + } else { + val = new DecimalFormat("0").format(val); + } + } + } else if (cell.getCellType() == CellType.STRING) { + val = cell.getStringCellValue(); + } else if (cell.getCellType() == CellType.BOOLEAN) { + val = cell.getBooleanCellValue(); + } else if (cell.getCellType() == CellType.ERROR) { + val = cell.getErrorCellValue(); + } + + } + } catch (Exception e) { + return val; + } + return val; + } + + /** + * 判断是否是空行 + * + * @param row 判断的行 + * + * @return + */ + private boolean isRowEmpty (Row row) { + if (row == null) { + return true; + } + for (int i = row.getFirstCellNum() ; i < row.getLastCellNum() ; i++) { + Cell cell = row.getCell(i); + if (cell != null && cell.getCellType() != CellType.BLANK) { + return false; + } + } + return true; + } + + /** + * 格式化不同类型的日期对象 + * + * @param dateFormat 日期格式 + * @param val 被格式化的日期对象 + * + * @return 格式化后的日期字符 + */ + public String parseDateToStr (String dateFormat, Object val) { + if (val == null) { + return ""; + } + String str; + if (val instanceof Date) { + str = DateUtils.parseDateToStr(dateFormat, (Date) val); + } else if (val instanceof LocalDateTime) { + str = DateUtils.parseDateToStr(dateFormat, DateUtils.toDate((LocalDateTime) val)); + } else if (val instanceof LocalDate) { + str = DateUtils.parseDateToStr(dateFormat, DateUtils.toDate((LocalDate) val)); + } else { + str = val.toString(); + } + return str; + } + + /** + * 是否有对象的子列表 + */ + public boolean isSubList () { + return StringUtils.isNotNull(subFields) && subFields.size() > 0; + } + + /** + * 是否有对象的子列表,集合不为空 + */ + public boolean isSubListValue (T vo) { + return StringUtils.isNotNull(subFields) && subFields.size() > 0 && StringUtils.isNotNull(getListCellValue(vo)) && getListCellValue(vo).size() > 0; + } + + /** + * 获取集合的值 + */ + public Collection getListCellValue (Object obj) { + Object value; + try { + value = subMethod.invoke(obj, new Object[]{}); + } catch (Exception e) { + return new ArrayList(); + } + return (Collection) value; + } + + /** + * 获取对象的子列表方法 + * + * @param name 名称 + * @param pojoClass 类对象 + * + * @return 子列表方法 + */ + public Method getSubMethod (String name, Class pojoClass) { + StringBuffer getMethodName = new StringBuffer("get"); + getMethodName.append(name.substring(0, 1).toUpperCase()); + getMethodName.append(name.substring(1)); + Method method = null; + try { + method = pojoClass.getMethod(getMethodName.toString(), new Class[]{}); + } catch (Exception e) { + log.error("获取对象异常{}", e.getMessage()); + } + return method; + } +} diff --git a/src/main/java/com/muyu/common/core/utils/reflect/ReflectUtils.java b/src/main/java/com/muyu/common/core/utils/reflect/ReflectUtils.java new file mode 100644 index 0000000..2ec7e4f --- /dev/null +++ b/src/main/java/com/muyu/common/core/utils/reflect/ReflectUtils.java @@ -0,0 +1,324 @@ +package com.muyu.common.core.utils.reflect; + +import com.muyu.common.core.text.Convert; +import com.muyu.common.core.utils.DateUtils; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.Validate; +import org.apache.poi.ss.usermodel.DateUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.lang.reflect.*; +import java.util.Date; + +/** + * 反射工具类. 提供调用getter/setter方法, 访问私有变量, 调用私有方法, 获取泛型类型Class, 被AOP过的真实类等工具函数. + * + * @author muyu + */ +@SuppressWarnings("rawtypes") +public class ReflectUtils { + private static final String SETTER_PREFIX = "set"; + + private static final String GETTER_PREFIX = "get"; + + private static final String CGLIB_CLASS_SEPARATOR = "$$"; + + private static Logger logger = LoggerFactory.getLogger(ReflectUtils.class); + + /** + * 调用Getter方法. + * 支持多级,如:对象名.对象名.方法 + */ + @SuppressWarnings("unchecked") + public static E invokeGetter (Object obj, String propertyName) { + Object object = obj; + for (String name : StringUtils.split(propertyName, ".")) { + String getterMethodName = GETTER_PREFIX + StringUtils.capitalize(name); + object = invokeMethod(object, getterMethodName, new Class[]{}, new Object[]{}); + } + return (E) object; + } + + /** + * 调用Setter方法, 仅匹配方法名。 + * 支持多级,如:对象名.对象名.方法 + */ + public static void invokeSetter (Object obj, String propertyName, E value) { + Object object = obj; + String[] names = StringUtils.split(propertyName, "."); + for (int i = 0 ; i < names.length ; i++) { + if (i < names.length - 1) { + String getterMethodName = GETTER_PREFIX + StringUtils.capitalize(names[i]); + object = invokeMethod(object, getterMethodName, new Class[]{}, new Object[]{}); + } else { + String setterMethodName = SETTER_PREFIX + StringUtils.capitalize(names[i]); + invokeMethodByName(object, setterMethodName, new Object[]{value}); + } + } + } + + /** + * 直接读取对象属性值, 无视private/protected修饰符, 不经过getter函数. + */ + @SuppressWarnings("unchecked") + public static E getFieldValue (final Object obj, final String fieldName) { + Field field = getAccessibleField(obj, fieldName); + if (field == null) { + logger.debug("在 [" + obj.getClass() + "] 中,没有找到 [" + fieldName + "] 字段 "); + return null; + } + E result = null; + try { + result = (E) field.get(obj); + } catch (IllegalAccessException e) { + logger.error("不可能抛出的异常{}", e.getMessage()); + } + return result; + } + + /** + * 直接设置对象属性值, 无视private/protected修饰符, 不经过setter函数. + */ + public static void setFieldValue (final Object obj, final String fieldName, final E value) { + Field field = getAccessibleField(obj, fieldName); + if (field == null) { + // throw new IllegalArgumentException("在 [" + obj.getClass() + "] 中,没有找到 [" + fieldName + "] 字段 "); + logger.debug("在 [" + obj.getClass() + "] 中,没有找到 [" + fieldName + "] 字段 "); + return; + } + try { + field.set(obj, value); + } catch (IllegalAccessException e) { + logger.error("不可能抛出的异常: {}", e.getMessage()); + } + } + + /** + * 直接调用对象方法, 无视private/protected修饰符. + * 用于一次性调用的情况,否则应使用getAccessibleMethod()函数获得Method后反复调用. + * 同时匹配方法名+参数类型, + */ + @SuppressWarnings("unchecked") + public static E invokeMethod (final Object obj, final String methodName, final Class[] parameterTypes, + final Object[] args) { + if (obj == null || methodName == null) { + return null; + } + Method method = getAccessibleMethod(obj, methodName, parameterTypes); + if (method == null) { + logger.debug("在 [" + obj.getClass() + "] 中,没有找到 [" + methodName + "] 方法 "); + return null; + } + try { + return (E) method.invoke(obj, args); + } catch (Exception e) { + String msg = "method: " + method + ", obj: " + obj + ", args: " + args + ""; + throw convertReflectionExceptionToUnchecked(msg, e); + } + } + + /** + * 直接调用对象方法, 无视private/protected修饰符, + * 用于一次性调用的情况,否则应使用getAccessibleMethodByName()函数获得Method后反复调用. + * 只匹配函数名,如果有多个同名函数调用第一个。 + */ + @SuppressWarnings("unchecked") + public static E invokeMethodByName (final Object obj, final String methodName, final Object[] args) { + Method method = getAccessibleMethodByName(obj, methodName, args.length); + if (method == null) { + // 如果为空不报错,直接返回空。 + logger.debug("在 [" + obj.getClass() + "] 中,没有找到 [" + methodName + "] 方法 "); + return null; + } + try { + // 类型转换(将参数数据类型转换为目标方法参数类型) + Class[] cs = method.getParameterTypes(); + for (int i = 0 ; i < cs.length ; i++) { + if (args[i] != null && !args[i].getClass().equals(cs[i])) { + if (cs[i] == String.class) { + args[i] = Convert.toStr(args[i]); + if (StringUtils.endsWith((String) args[i], ".0")) { + args[i] = StringUtils.substringBefore((String) args[i], ".0"); + } + } else if (cs[i] == Integer.class) { + args[i] = Convert.toInt(args[i]); + } else if (cs[i] == Long.class) { + args[i] = Convert.toLong(args[i]); + } else if (cs[i] == Double.class) { + args[i] = Convert.toDouble(args[i]); + } else if (cs[i] == Float.class) { + args[i] = Convert.toFloat(args[i]); + } else if (cs[i] == Date.class) { + if (args[i] instanceof String) { + args[i] = DateUtils.parseDate(args[i]); + } else { + args[i] = DateUtil.getJavaDate((Double) args[i]); + } + } else if (cs[i] == boolean.class || cs[i] == Boolean.class) { + args[i] = Convert.toBool(args[i]); + } + } + } + return (E) method.invoke(obj, args); + } catch (Exception e) { + String msg = "method: " + method + ", obj: " + obj + ", args: " + args + ""; + throw convertReflectionExceptionToUnchecked(msg, e); + } + } + + /** + * 循环向上转型, 获取对象的DeclaredField, 并强制设置为可访问. + * 如向上转型到Object仍无法找到, 返回null. + */ + public static Field getAccessibleField (final Object obj, final String fieldName) { + // 为空不报错。直接返回 null + if (obj == null) { + return null; + } + Validate.notBlank(fieldName, "fieldName can't be blank"); + for (Class superClass = obj.getClass() ; superClass != Object.class ; superClass = superClass.getSuperclass()) { + try { + Field field = superClass.getDeclaredField(fieldName); + makeAccessible(field); + return field; + } catch (NoSuchFieldException e) { + continue; + } + } + return null; + } + + /** + * 循环向上转型, 获取对象的DeclaredMethod,并强制设置为可访问. + * 如向上转型到Object仍无法找到, 返回null. + * 匹配函数名+参数类型。 + * 用于方法需要被多次调用的情况. 先使用本函数先取得Method,然后调用Method.invoke(Object obj, Object... args) + */ + public static Method getAccessibleMethod (final Object obj, final String methodName, + final Class... parameterTypes) { + // 为空不报错。直接返回 null + if (obj == null) { + return null; + } + Validate.notBlank(methodName, "methodName can't be blank"); + for (Class searchType = obj.getClass() ; searchType != Object.class ; searchType = searchType.getSuperclass()) { + try { + Method method = searchType.getDeclaredMethod(methodName, parameterTypes); + makeAccessible(method); + return method; + } catch (NoSuchMethodException e) { + continue; + } + } + return null; + } + + /** + * 循环向上转型, 获取对象的DeclaredMethod,并强制设置为可访问. + * 如向上转型到Object仍无法找到, 返回null. + * 只匹配函数名。 + * 用于方法需要被多次调用的情况. 先使用本函数先取得Method,然后调用Method.invoke(Object obj, Object... args) + */ + public static Method getAccessibleMethodByName (final Object obj, final String methodName, int argsNum) { + // 为空不报错。直接返回 null + if (obj == null) { + return null; + } + Validate.notBlank(methodName, "methodName can't be blank"); + for (Class searchType = obj.getClass() ; searchType != Object.class ; searchType = searchType.getSuperclass()) { + Method[] methods = searchType.getDeclaredMethods(); + for (Method method : methods) { + if (method.getName().equals(methodName) && method.getParameterTypes().length == argsNum) { + makeAccessible(method); + return method; + } + } + } + return null; + } + + /** + * 改变private/protected的方法为public,尽量不调用实际改动的语句,避免JDK的SecurityManager抱怨。 + */ + public static void makeAccessible (Method method) { + if ((!Modifier.isPublic(method.getModifiers()) || !Modifier.isPublic(method.getDeclaringClass().getModifiers())) + && !method.isAccessible()) { + method.setAccessible(true); + } + } + + /** + * 改变private/protected的成员变量为public,尽量不调用实际改动的语句,避免JDK的SecurityManager抱怨。 + */ + public static void makeAccessible (Field field) { + if ((!Modifier.isPublic(field.getModifiers()) || !Modifier.isPublic(field.getDeclaringClass().getModifiers()) + || Modifier.isFinal(field.getModifiers())) && !field.isAccessible()) { + field.setAccessible(true); + } + } + + /** + * 通过反射, 获得Class定义中声明的泛型参数的类型, 注意泛型必须定义在父类处 + * 如无法找到, 返回Object.class. + */ + @SuppressWarnings("unchecked") + public static Class getClassGenricType (final Class clazz) { + return getClassGenricType(clazz, 0); + } + + /** + * 通过反射, 获得Class定义中声明的父类的泛型参数的类型. + * 如无法找到, 返回Object.class. + */ + public static Class getClassGenricType (final Class clazz, final int index) { + Type genType = clazz.getGenericSuperclass(); + + if (!(genType instanceof ParameterizedType)) { + logger.debug(clazz.getSimpleName() + "'s superclass not ParameterizedType"); + return Object.class; + } + + Type[] params = ((ParameterizedType) genType).getActualTypeArguments(); + + if (index >= params.length || index < 0) { + logger.debug("Index: " + index + ", Size of " + clazz.getSimpleName() + "'s Parameterized Type: " + + params.length); + return Object.class; + } + if (!(params[index] instanceof Class)) { + logger.debug(clazz.getSimpleName() + " not set the actual class on superclass generic parameter"); + return Object.class; + } + + return (Class) params[index]; + } + + public static Class getUserClass (Object instance) { + if (instance == null) { + throw new RuntimeException("Instance must not be null"); + } + Class clazz = instance.getClass(); + if (clazz != null && clazz.getName().contains(CGLIB_CLASS_SEPARATOR)) { + Class superClass = clazz.getSuperclass(); + if (superClass != null && !Object.class.equals(superClass)) { + return superClass; + } + } + return clazz; + + } + + /** + * 将反射时的checked exception转换为unchecked exception. + */ + public static RuntimeException convertReflectionExceptionToUnchecked (String msg, Exception e) { + if (e instanceof IllegalAccessException || e instanceof IllegalArgumentException + || e instanceof NoSuchMethodException) { + return new IllegalArgumentException(msg, e); + } else if (e instanceof InvocationTargetException) { + return new RuntimeException(msg, ((InvocationTargetException) e).getTargetException()); + } + return new RuntimeException(msg, e); + } +} diff --git a/src/main/java/com/muyu/common/core/utils/sign/Base64.java b/src/main/java/com/muyu/common/core/utils/sign/Base64.java new file mode 100644 index 0000000..038933f --- /dev/null +++ b/src/main/java/com/muyu/common/core/utils/sign/Base64.java @@ -0,0 +1,256 @@ +package com.muyu.common.core.utils.sign; + +/** + * Base64工具类 + * + * @author muyu + */ +public final class Base64 { + static private final int BASELENGTH = 128; + static private final int LOOKUPLENGTH = 64; + static private final int TWENTYFOURBITGROUP = 24; + static private final int EIGHTBIT = 8; + static private final int SIXTEENBIT = 16; + static private final int FOURBYTE = 4; + static private final int SIGN = -128; + static private final char PAD = '='; + static final private byte[] base64Alphabet = new byte[BASELENGTH]; + static final private char[] lookUpBase64Alphabet = new char[LOOKUPLENGTH]; + + static { + for (int i = 0 ; i < BASELENGTH ; ++i) { + base64Alphabet[i] = -1; + } + for (int i = 'Z' ; i >= 'A' ; i--) { + base64Alphabet[i] = (byte) (i - 'A'); + } + for (int i = 'z' ; i >= 'a' ; i--) { + base64Alphabet[i] = (byte) (i - 'a' + 26); + } + + for (int i = '9' ; i >= '0' ; i--) { + base64Alphabet[i] = (byte) (i - '0' + 52); + } + + base64Alphabet['+'] = 62; + base64Alphabet['/'] = 63; + + for (int i = 0 ; i <= 25 ; i++) { + lookUpBase64Alphabet[i] = (char) ('A' + i); + } + + for (int i = 26, j = 0 ; i <= 51 ; i++, j++) { + lookUpBase64Alphabet[i] = (char) ('a' + j); + } + + for (int i = 52, j = 0 ; i <= 61 ; i++, j++) { + lookUpBase64Alphabet[i] = (char) ('0' + j); + } + lookUpBase64Alphabet[62] = (char) '+'; + lookUpBase64Alphabet[63] = (char) '/'; + } + + private static boolean isWhiteSpace (char octect) { + return (octect == 0x20 || octect == 0xd || octect == 0xa || octect == 0x9); + } + + private static boolean isPad (char octect) { + return (octect == PAD); + } + + private static boolean isData (char octect) { + return (octect < BASELENGTH && base64Alphabet[octect] != -1); + } + + /** + * Encodes hex octects into Base64 + * + * @param binaryData Array containing binaryData + * + * @return Encoded Base64 array + */ + public static String encode (byte[] binaryData) { + if (binaryData == null) { + return null; + } + + int lengthDataBits = binaryData.length * EIGHTBIT; + if (lengthDataBits == 0) { + return ""; + } + + int fewerThan24bits = lengthDataBits % TWENTYFOURBITGROUP; + int numberTriplets = lengthDataBits / TWENTYFOURBITGROUP; + int numberQuartet = fewerThan24bits != 0 ? numberTriplets + 1 : numberTriplets; + char encodedData[] = null; + + encodedData = new char[numberQuartet * 4]; + + byte k = 0, l = 0, b1 = 0, b2 = 0, b3 = 0; + + int encodedIndex = 0; + int dataIndex = 0; + + for (int i = 0 ; i < numberTriplets ; i++) { + b1 = binaryData[dataIndex++]; + b2 = binaryData[dataIndex++]; + b3 = binaryData[dataIndex++]; + + l = (byte) (b2 & 0x0f); + k = (byte) (b1 & 0x03); + + byte val1 = ((b1 & SIGN) == 0) ? (byte) (b1 >> 2) : (byte) ((b1) >> 2 ^ 0xc0); + byte val2 = ((b2 & SIGN) == 0) ? (byte) (b2 >> 4) : (byte) ((b2) >> 4 ^ 0xf0); + byte val3 = ((b3 & SIGN) == 0) ? (byte) (b3 >> 6) : (byte) ((b3) >> 6 ^ 0xfc); + + encodedData[encodedIndex++] = lookUpBase64Alphabet[val1]; + encodedData[encodedIndex++] = lookUpBase64Alphabet[val2 | (k << 4)]; + encodedData[encodedIndex++] = lookUpBase64Alphabet[(l << 2) | val3]; + encodedData[encodedIndex++] = lookUpBase64Alphabet[b3 & 0x3f]; + } + + // form integral number of 6-bit groups + if (fewerThan24bits == EIGHTBIT) { + b1 = binaryData[dataIndex]; + k = (byte) (b1 & 0x03); + byte val1 = ((b1 & SIGN) == 0) ? (byte) (b1 >> 2) : (byte) ((b1) >> 2 ^ 0xc0); + encodedData[encodedIndex++] = lookUpBase64Alphabet[val1]; + encodedData[encodedIndex++] = lookUpBase64Alphabet[k << 4]; + encodedData[encodedIndex++] = PAD; + encodedData[encodedIndex++] = PAD; + } else if (fewerThan24bits == SIXTEENBIT) { + b1 = binaryData[dataIndex]; + b2 = binaryData[dataIndex + 1]; + l = (byte) (b2 & 0x0f); + k = (byte) (b1 & 0x03); + + byte val1 = ((b1 & SIGN) == 0) ? (byte) (b1 >> 2) : (byte) ((b1) >> 2 ^ 0xc0); + byte val2 = ((b2 & SIGN) == 0) ? (byte) (b2 >> 4) : (byte) ((b2) >> 4 ^ 0xf0); + + encodedData[encodedIndex++] = lookUpBase64Alphabet[val1]; + encodedData[encodedIndex++] = lookUpBase64Alphabet[val2 | (k << 4)]; + encodedData[encodedIndex++] = lookUpBase64Alphabet[l << 2]; + encodedData[encodedIndex++] = PAD; + } + return new String(encodedData); + } + + /** + * Decodes Base64 data into octects + * + * @param encoded string containing Base64 data + * + * @return Array containind decoded data. + */ + public static byte[] decode (String encoded) { + if (encoded == null) { + return null; + } + + char[] base64Data = encoded.toCharArray(); + // remove white spaces + int len = removeWhiteSpace(base64Data); + + if (len % FOURBYTE != 0) { + return null;// should be divisible by four + } + + int numberQuadruple = (len / FOURBYTE); + + if (numberQuadruple == 0) { + return new byte[0]; + } + + byte decodedData[] = null; + byte b1 = 0, b2 = 0, b3 = 0, b4 = 0; + char d1 = 0, d2 = 0, d3 = 0, d4 = 0; + + int i = 0; + int encodedIndex = 0; + int dataIndex = 0; + decodedData = new byte[(numberQuadruple) * 3]; + + for ( ; i < numberQuadruple - 1 ; i++) { + + if (!isData((d1 = base64Data[dataIndex++])) || !isData((d2 = base64Data[dataIndex++])) + || !isData((d3 = base64Data[dataIndex++])) || !isData((d4 = base64Data[dataIndex++]))) { + return null; + } // if found "no data" just return null + + b1 = base64Alphabet[d1]; + b2 = base64Alphabet[d2]; + b3 = base64Alphabet[d3]; + b4 = base64Alphabet[d4]; + + decodedData[encodedIndex++] = (byte) (b1 << 2 | b2 >> 4); + decodedData[encodedIndex++] = (byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf)); + decodedData[encodedIndex++] = (byte) (b3 << 6 | b4); + } + + if (!isData((d1 = base64Data[dataIndex++])) || !isData((d2 = base64Data[dataIndex++]))) { + return null;// if found "no data" just return null + } + + b1 = base64Alphabet[d1]; + b2 = base64Alphabet[d2]; + + d3 = base64Data[dataIndex++]; + d4 = base64Data[dataIndex++]; + if (!isData((d3)) || !isData((d4))) {// Check if they are PAD characters + if (isPad(d3) && isPad(d4)) { + if ((b2 & 0xf) != 0)// last 4 bits should be zero + { + return null; + } + byte[] tmp = new byte[i * 3 + 1]; + System.arraycopy(decodedData, 0, tmp, 0, i * 3); + tmp[encodedIndex] = (byte) (b1 << 2 | b2 >> 4); + return tmp; + } else if (!isPad(d3) && isPad(d4)) { + b3 = base64Alphabet[d3]; + if ((b3 & 0x3) != 0)// last 2 bits should be zero + { + return null; + } + byte[] tmp = new byte[i * 3 + 2]; + System.arraycopy(decodedData, 0, tmp, 0, i * 3); + tmp[encodedIndex++] = (byte) (b1 << 2 | b2 >> 4); + tmp[encodedIndex] = (byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf)); + return tmp; + } else { + return null; + } + } else { // No PAD e.g 3cQl + b3 = base64Alphabet[d3]; + b4 = base64Alphabet[d4]; + decodedData[encodedIndex++] = (byte) (b1 << 2 | b2 >> 4); + decodedData[encodedIndex++] = (byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf)); + decodedData[encodedIndex++] = (byte) (b3 << 6 | b4); + + } + return decodedData; + } + + /** + * remove WhiteSpace from MIME containing encoded Base64 data. + * + * @param data the byte array of base64 data (with WS) + * + * @return the new length + */ + private static int removeWhiteSpace (char[] data) { + if (data == null) { + return 0; + } + + // count characters that's not whitespace + int newSize = 0; + int len = data.length; + for (int i = 0 ; i < len ; i++) { + if (!isWhiteSpace(data[i])) { + data[newSize++] = data[i]; + } + } + return newSize; + } +} diff --git a/src/main/java/com/muyu/common/core/utils/sql/SqlUtil.java b/src/main/java/com/muyu/common/core/utils/sql/SqlUtil.java new file mode 100644 index 0000000..3f418e7 --- /dev/null +++ b/src/main/java/com/muyu/common/core/utils/sql/SqlUtil.java @@ -0,0 +1,59 @@ +package com.muyu.common.core.utils.sql; + +import com.muyu.common.core.exception.UtilException; +import com.muyu.common.core.utils.StringUtils; + +/** + * sql操作工具类 + * + * @author muyu + */ +public class SqlUtil { + /** + * 限制orderBy最大长度 + */ + private static final int ORDER_BY_MAX_LENGTH = 500; + /** + * 定义常用的 sql关键字 + */ + public static String SQL_REGEX = "and |extractvalue|updatexml|exec |insert |select |delete |update |drop |count |chr |mid |master |truncate |char |declare |or |+|user()"; + /** + * 仅支持字母、数字、下划线、空格、逗号、小数点(支持多个字段排序) + */ + public static String SQL_PATTERN = "[a-zA-Z0-9_\\ \\,\\.]+"; + + /** + * 检查字符,防止注入绕过 + */ + public static String escapeOrderBySql (String value) { + if (StringUtils.isNotEmpty(value) && !isValidOrderBySql(value)) { + throw new UtilException("参数不符合规范,不能进行查询"); + } + if (StringUtils.length(value) > ORDER_BY_MAX_LENGTH) { + throw new UtilException("参数已超过最大限制,不能进行查询"); + } + return value; + } + + /** + * 验证 order by 语法是否符合规范 + */ + public static boolean isValidOrderBySql (String value) { + return value.matches(SQL_PATTERN); + } + + /** + * SQL关键字检查 + */ + public static void filterKeyword (String value) { + if (StringUtils.isEmpty(value)) { + return; + } + String[] sqlKeywords = StringUtils.split(SQL_REGEX, "\\|"); + for (String sqlKeyword : sqlKeywords) { + if (StringUtils.indexOfIgnoreCase(value, sqlKeyword) > -1) { + throw new UtilException("参数存在SQL注入风险"); + } + } + } +} diff --git a/src/main/java/com/muyu/common/core/utils/uuid/IdUtils.java b/src/main/java/com/muyu/common/core/utils/uuid/IdUtils.java new file mode 100644 index 0000000..375b034 --- /dev/null +++ b/src/main/java/com/muyu/common/core/utils/uuid/IdUtils.java @@ -0,0 +1,44 @@ +package com.muyu.common.core.utils.uuid; + +/** + * ID生成器工具类 + * + * @author muyu + */ +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/muyu/common/core/utils/uuid/Seq.java b/src/main/java/com/muyu/common/core/utils/uuid/Seq.java new file mode 100644 index 0000000..b72e3dd --- /dev/null +++ b/src/main/java/com/muyu/common/core/utils/uuid/Seq.java @@ -0,0 +1,78 @@ +package com.muyu.common.core.utils.uuid; + +import com.muyu.common.core.utils.DateUtils; +import com.muyu.common.core.utils.StringUtils; + +import java.util.concurrent.atomic.AtomicInteger; + +/** + * @author muyu 序列生成类 + */ +public class Seq { + // 通用序列类型 + public static final String commSeqType = "COMMON"; + + // 上传序列类型 + public static final String uploadSeqType = "UPLOAD"; + // 机器标识 + private static final String machineCode = "A"; + // 通用接口序列数 + private static AtomicInteger commSeq = new AtomicInteger(1); + // 上传接口序列数 + private static AtomicInteger uploadSeq = new AtomicInteger(1); + + /** + * 获取通用序列号 + * + * @return 序列值 + */ + public static String getId () { + return getId(commSeqType); + } + + /** + * 默认16位序列号 yyMMddHHmmss + 一位机器标识 + 3长度循环递增字符串 + * + * @return 序列值 + */ + public static String getId (String type) { + AtomicInteger atomicInt = commSeq; + if (uploadSeqType.equals(type)) { + atomicInt = uploadSeq; + } + return getId(atomicInt, 3); + } + + /** + * 通用接口序列号 yyMMddHHmmss + 一位机器标识 + length长度循环递增字符串 + * + * @param atomicInt 序列数 + * @param length 数值长度 + * + * @return 序列值 + */ + public static String getId (AtomicInteger atomicInt, int length) { + String result = DateUtils.dateTimeNow(); + result += machineCode; + result += getSeq(atomicInt, length); + return result; + } + + /** + * 序列循环递增字符串[1, 10 的 (length)幂次方), 用0左补齐length位数 + * + * @return 序列值 + */ + private synchronized static String getSeq (AtomicInteger atomicInt, int length) { + // 先取值再+1 + int value = atomicInt.getAndIncrement(); + + // 如果更新后值>=10 的 (length)幂次方则重置为1 + int maxSeq = (int) Math.pow(10, length); + if (atomicInt.get() >= maxSeq) { + atomicInt.set(1); + } + // 转字符串,用0左补齐 + return StringUtils.padl(value, length); + } +} diff --git a/src/main/java/com/muyu/common/core/utils/uuid/UUID.java b/src/main/java/com/muyu/common/core/utils/uuid/UUID.java new file mode 100644 index 0000000..37c3c9e --- /dev/null +++ b/src/main/java/com/muyu/common/core/utils/uuid/UUID.java @@ -0,0 +1,450 @@ +package com.muyu.common.core.utils.uuid; + +import com.muyu.common.core.exception.UtilException; + +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; +import java.util.Random; +import java.util.concurrent.ThreadLocalRandom; + +/** + * 提供通用唯一识别码(universally unique identifier)(UUID)实现 + * + * @author muyu + */ +public final class UUID implements java.io.Serializable, Comparable { + private static final long serialVersionUID = -1185015143654744140L; + /** + * 此UUID的最高64有效位 + */ + private final long mostSigBits; + /** + * 此UUID的最低64有效位 + */ + private final long leastSigBits; + + /** + * 私有构造 + * + * @param data 数据 + */ + private UUID (byte[] data) { + long msb = 0; + long lsb = 0; + assert data.length == 16 : "data must be 16 bytes in length"; + for (int i = 0 ; i < 8 ; i++) { + msb = (msb << 8) | (data[i] & 0xff); + } + for (int i = 8 ; i < 16 ; i++) { + lsb = (lsb << 8) | (data[i] & 0xff); + } + this.mostSigBits = msb; + this.leastSigBits = lsb; + } + + /** + * 使用指定的数据构造新的 UUID。 + * + * @param mostSigBits 用于 {@code UUID} 的最高有效 64 位 + * @param leastSigBits 用于 {@code UUID} 的最低有效 64 位 + */ + public UUID (long mostSigBits, long leastSigBits) { + this.mostSigBits = mostSigBits; + this.leastSigBits = leastSigBits; + } + + /** + * 获取类型 4(伪随机生成的)UUID 的静态工厂。 + * + * @return 随机生成的 {@code UUID} + */ + public static UUID fastUUID () { + return randomUUID(false); + } + + /** + * 获取类型 4(伪随机生成的)UUID 的静态工厂。 使用加密的强伪随机数生成器生成该 UUID。 + * + * @return 随机生成的 {@code UUID} + */ + public static UUID randomUUID () { + return randomUUID(true); + } + + /** + * 获取类型 4(伪随机生成的)UUID 的静态工厂。 使用加密的强伪随机数生成器生成该 UUID。 + * + * @param isSecure 是否使用{@link SecureRandom}如果是可以获得更安全的随机码,否则可以得到更好的性能 + * + * @return 随机生成的 {@code UUID} + */ + public static UUID randomUUID (boolean isSecure) { + final Random ng = isSecure ? Holder.numberGenerator : getRandom(); + + byte[] randomBytes = new byte[16]; + ng.nextBytes(randomBytes); + randomBytes[6] &= 0x0f; /* clear version */ + randomBytes[6] |= 0x40; /* set to version 4 */ + randomBytes[8] &= 0x3f; /* clear variant */ + randomBytes[8] |= 0x80; /* set to IETF variant */ + return new UUID(randomBytes); + } + + /** + * 根据指定的字节数组获取类型 3(基于名称的)UUID 的静态工厂。 + * + * @param name 用于构造 UUID 的字节数组。 + * + * @return 根据指定数组生成的 {@code UUID} + */ + public static UUID nameUUIDFromBytes (byte[] name) { + MessageDigest md; + try { + md = MessageDigest.getInstance("MD5"); + } catch (NoSuchAlgorithmException nsae) { + throw new InternalError("MD5 not supported"); + } + byte[] md5Bytes = md.digest(name); + md5Bytes[6] &= 0x0f; /* clear version */ + md5Bytes[6] |= 0x30; /* set to version 3 */ + md5Bytes[8] &= 0x3f; /* clear variant */ + md5Bytes[8] |= 0x80; /* set to IETF variant */ + return new UUID(md5Bytes); + } + + /** + * 根据 {@link #toString()} 方法中描述的字符串标准表示形式创建{@code UUID}。 + * + * @param name 指定 {@code UUID} 字符串 + * + * @return 具有指定值的 {@code UUID} + * + * @throws IllegalArgumentException 如果 name 与 {@link #toString} 中描述的字符串表示形式不符抛出此异常 + */ + public static UUID fromString (String name) { + String[] components = name.split("-"); + if (components.length != 5) { + throw new IllegalArgumentException("Invalid UUID string: " + name); + } + for (int i = 0 ; i < 5 ; i++) { + components[i] = "0x" + components[i]; + } + + long mostSigBits = Long.decode(components[0]).longValue(); + mostSigBits <<= 16; + mostSigBits |= Long.decode(components[1]).longValue(); + mostSigBits <<= 16; + mostSigBits |= Long.decode(components[2]).longValue(); + + long leastSigBits = Long.decode(components[3]).longValue(); + leastSigBits <<= 48; + leastSigBits |= Long.decode(components[4]).longValue(); + + return new UUID(mostSigBits, leastSigBits); + } + + /** + * 返回指定数字对应的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); + } + + /** + * 获取{@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(); + } + + /** + * 返回此 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(); + } + + // Comparison Operations + + /** + * 返回此 UUID 的哈希码。 + * + * @return UUID 的哈希码值。 + */ + @Override + public int hashCode () { + long hilo = mostSigBits ^ leastSigBits; + return ((int) (hilo >> 32)) ^ (int) hilo; + } + + // ------------------------------------------------------------------------------------------------------------------- + // Private method start + + /** + * 将此对象与指定对象比较。 + *

+ * 当且仅当参数不为 {@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); + } + + /** + * 将此 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)))); + } + + /** + * 检查是否为time-based版本UUID + */ + private void checkTimeBase () { + if (version() != 1) { + throw new UnsupportedOperationException("Not a time-based UUID"); + } + } + + /** + * SecureRandom 的单例 + */ + private static class Holder { + static final SecureRandom numberGenerator = getSecureRandom(); + } +} diff --git a/src/main/java/com/muyu/common/core/validation/ValidationConfig.java b/src/main/java/com/muyu/common/core/validation/ValidationConfig.java new file mode 100644 index 0000000..0540064 --- /dev/null +++ b/src/main/java/com/muyu/common/core/validation/ValidationConfig.java @@ -0,0 +1,14 @@ +package com.muyu.common.core.validation; + +import jakarta.validation.Validation; +import jakarta.validation.Validator; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class ValidationConfig { + @Bean + public Validator validator() { + return Validation.byDefaultProvider().configure().buildValidatorFactory().getValidator(); + } +} diff --git a/src/main/java/com/muyu/common/core/web/controller/BaseController.java b/src/main/java/com/muyu/common/core/web/controller/BaseController.java new file mode 100644 index 0000000..3789a82 --- /dev/null +++ b/src/main/java/com/muyu/common/core/web/controller/BaseController.java @@ -0,0 +1,129 @@ +package com.muyu.common.core.web.controller; + +import com.github.pagehelper.PageInfo; +import com.muyu.common.core.utils.DateUtils; +import com.muyu.common.core.utils.PageUtils; +import com.muyu.common.core.domain.Result; +import com.muyu.common.core.web.page.TableDataInfo; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.web.bind.WebDataBinder; +import org.springframework.web.bind.annotation.InitBinder; + +import java.beans.PropertyEditorSupport; +import java.util.Date; +import java.util.List; + +/** + * web层通用数据处理 + * + * @author muyu + */ +public class BaseController { + protected final Logger logger = LoggerFactory.getLogger(this.getClass()); + + /** + * 将前台传递过来的日期格式的字符串,自动转化为Date类型 + */ + @InitBinder + public void initBinder (WebDataBinder binder) { + // Date 类型转换 + binder.registerCustomEditor(Date.class, new PropertyEditorSupport() { + @Override + public void setAsText (String text) { + setValue(DateUtils.parseDate(text)); + } + }); + } + + /** + * 设置请求分页数据 + */ + protected void startPage () { + PageUtils.startPage(); + } + + /** + * 清理分页的线程变量 + */ + protected void clearPage () { + PageUtils.clearPage(); + } + + /** + * 响应请求分页数据 + */ + @SuppressWarnings({"rawtypes", "unchecked"}) + protected Result> getDataTable (List list) { + return Result.success( + TableDataInfo.builder() + .total(new PageInfo(list).getTotal()) + .rows(list) + .build() + ); + } + + /** + * 返回成功 + */ + public Result success () { + return Result.success(); + } + + /** + * 返回成功消息 + */ + public Result success (String message) { + return Result.success(message); + } + + /** + * 返回成功消息 + */ + public Result success (Object data) { + return Result.success(data); + } + + /** + * 返回失败消息 + */ + public Result error () { + return Result.error(); + } + + /** + * 返回失败消息 + */ + public Result error (String message) { + return Result.error(message); + } + + /** + * 返回警告消息 + */ + public Result warn (String message) { + return Result.warn(message); + } + + /** + * 响应返回结果 + * + * @param rows 影响行数 + * + * @return 操作结果 + */ + protected Result toAjax (int rows) { + return rows > 0 ? Result.success() : Result.error(); + } + + /** + * 响应返回结果 + * + * @param result 结果 + * + * @return 操作结果 + */ + protected Result toAjax (boolean result) { + return result ? success() : error(); + } +} diff --git a/src/main/java/com/muyu/common/core/web/domain/BaseEntity.java b/src/main/java/com/muyu/common/core/web/domain/BaseEntity.java new file mode 100644 index 0000000..add8e3e --- /dev/null +++ b/src/main/java/com/muyu/common/core/web/domain/BaseEntity.java @@ -0,0 +1,80 @@ +package com.muyu.common.core.web.domain; + +import com.baomidou.mybatisplus.annotation.TableField; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonInclude; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.SuperBuilder; + +import java.io.Serializable; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; + +/** + * Entity基类 + * + * @author muyu + */ +@Data +@SuperBuilder +@NoArgsConstructor +@AllArgsConstructor +public class BaseEntity implements Serializable { + private static final long serialVersionUID = 1L; + + /** + * 搜索值 + */ + @JsonIgnore + @TableField(exist = false) + private String searchValue; + + /** + * 创建者 + */ + private String createBy; + + /** + * 创建时间 + */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date createTime; + + /** + * 更新者 + */ + private String updateBy; + + /** + * 更新时间 + */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date updateTime; + + /** + * 备注 + */ + private String remark; + + /** + * 请求参数 + */ + @JsonInclude(JsonInclude.Include.NON_EMPTY) + @TableField(exist = false) + private Map params; + + public Map getParams () { + if (params == null) { + params = new HashMap<>(); + } + return params; + } + + public void setParams (Map params) { + this.params = params; + } +} diff --git a/src/main/java/com/muyu/common/core/web/domain/TreeEntity.java b/src/main/java/com/muyu/common/core/web/domain/TreeEntity.java new file mode 100644 index 0000000..85ea8df --- /dev/null +++ b/src/main/java/com/muyu/common/core/web/domain/TreeEntity.java @@ -0,0 +1,47 @@ +package com.muyu.common.core.web.domain; + +import lombok.*; +import lombok.experimental.SuperBuilder; + +import java.util.ArrayList; +import java.util.List; + +/** + * Tree基类 + * + * @author muyu + */ +@Data +@SuperBuilder +@NoArgsConstructor +@AllArgsConstructor +@EqualsAndHashCode(callSuper = true) +public class TreeEntity extends BaseEntity { + private static final long serialVersionUID = 1L; + + /** + * 父菜单名称 + */ + private String parentName; + + /** + * 父菜单ID + */ + private Long parentId; + + /** + * 显示顺序 + */ + private Integer orderNum; + + /** + * 祖级列表 + */ + private String ancestors; + + /** + * 子部门 + */ + private List children = new ArrayList<>(); + +} diff --git a/src/main/java/com/muyu/common/core/web/page/PageDomain.java b/src/main/java/com/muyu/common/core/web/page/PageDomain.java new file mode 100644 index 0000000..b9c5e45 --- /dev/null +++ b/src/main/java/com/muyu/common/core/web/page/PageDomain.java @@ -0,0 +1,93 @@ +package com.muyu.common.core.web.page; + +import com.muyu.common.core.utils.StringUtils; + +/** + * 分页数据 + * + * @author muyu + */ +public class PageDomain { + /** + * 当前记录起始索引 + */ + private Integer pageNum; + + /** + * 每页显示记录数 + */ + private Integer pageSize; + + /** + * 排序列 + */ + private String orderByColumn; + + /** + * 排序的方向desc或者asc + */ + private String isAsc = "asc"; + + /** + * 分页参数合理化 + */ + private Boolean reasonable = true; + + public String getOrderBy () { + if (StringUtils.isEmpty(orderByColumn)) { + return ""; + } + return StringUtils.toUnderScoreCase(orderByColumn) + " " + isAsc; + } + + public Integer getPageNum () { + return pageNum; + } + + public void setPageNum (Integer pageNum) { + this.pageNum = pageNum; + } + + public Integer getPageSize () { + return pageSize; + } + + public void setPageSize (Integer pageSize) { + this.pageSize = pageSize; + } + + public String getOrderByColumn () { + return orderByColumn; + } + + public void setOrderByColumn (String orderByColumn) { + this.orderByColumn = orderByColumn; + } + + public String getIsAsc () { + return isAsc; + } + + public void setIsAsc (String isAsc) { + if (StringUtils.isNotEmpty(isAsc)) { + // 兼容前端排序类型 + if ("ascending".equals(isAsc)) { + isAsc = "asc"; + } else if ("descending".equals(isAsc)) { + isAsc = "desc"; + } + this.isAsc = isAsc; + } + } + + public Boolean getReasonable () { + if (StringUtils.isNull(reasonable)) { + return Boolean.TRUE; + } + return reasonable; + } + + public void setReasonable (Boolean reasonable) { + this.reasonable = reasonable; + } +} diff --git a/src/main/java/com/muyu/common/core/web/page/TableDataInfo.java b/src/main/java/com/muyu/common/core/web/page/TableDataInfo.java new file mode 100644 index 0000000..d677cce --- /dev/null +++ b/src/main/java/com/muyu/common/core/web/page/TableDataInfo.java @@ -0,0 +1,45 @@ +package com.muyu.common.core.web.page; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.apache.poi.ss.formula.functions.T; + +import java.io.Serializable; +import java.util.List; + +/** + * 表格分页数据对象 + * + * @author muyu + */ + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class TableDataInfo implements Serializable { + private static final long serialVersionUID = 1L; + + /** + * 总记录数 + */ + private long total; + + /** + * 列表数据 + */ + private List rows; + + /** + * 消息状态码 + */ + private int code; + + /** + * 消息内容 + */ + private String msg; + +} diff --git a/src/main/java/com/muyu/common/core/web/page/TableSupport.java b/src/main/java/com/muyu/common/core/web/page/TableSupport.java new file mode 100644 index 0000000..ce0328d --- /dev/null +++ b/src/main/java/com/muyu/common/core/web/page/TableSupport.java @@ -0,0 +1,53 @@ +package com.muyu.common.core.web.page; + +import com.muyu.common.core.text.Convert; +import com.muyu.common.core.utils.ServletUtils; + +/** + * 表格数据处理 + * + * @author muyu + */ +public class TableSupport { + /** + * 当前记录起始索引 + */ + public static final String PAGE_NUM = "pageNum"; + + /** + * 每页显示记录数 + */ + public static final String PAGE_SIZE = "pageSize"; + + /** + * 排序列 + */ + public static final String ORDER_BY_COLUMN = "orderByColumn"; + + /** + * 排序的方向 "desc" 或者 "asc". + */ + public static final String IS_ASC = "isAsc"; + + /** + * 分页参数合理化 + */ + public static final String REASONABLE = "reasonable"; + + /** + * 封装分页对象 + */ + public static PageDomain getPageDomain () { + PageDomain pageDomain = new PageDomain(); + pageDomain.setPageNum(Convert.toInt(ServletUtils.getParameter(PAGE_NUM), 1)); + pageDomain.setPageSize(Convert.toInt(ServletUtils.getParameter(PAGE_SIZE), 10)); + pageDomain.setOrderByColumn(ServletUtils.getParameter(ORDER_BY_COLUMN)); + pageDomain.setIsAsc(ServletUtils.getParameter(IS_ASC)); + pageDomain.setReasonable(ServletUtils.getParameterToBool(REASONABLE)); + return pageDomain; + } + + public static PageDomain buildPageRequest () { + return getPageDomain(); + } +} diff --git a/src/main/java/com/muyu/common/core/xss/Xss.java b/src/main/java/com/muyu/common/core/xss/Xss.java new file mode 100644 index 0000000..fa31755 --- /dev/null +++ b/src/main/java/com/muyu/common/core/xss/Xss.java @@ -0,0 +1,27 @@ +package com.muyu.common.core.xss; + +import jakarta.validation.Constraint; +import jakarta.validation.Payload; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * 自定义xss校验注解 + * + * @author muyu + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(value = {ElementType.METHOD, ElementType.FIELD, ElementType.CONSTRUCTOR, ElementType.PARAMETER}) +@Constraint(validatedBy = {XssValidator.class}) +public @interface Xss { + String message () + + default "不允许任何脚本运行"; + + Class[] groups () default {}; + + Class[] payload () default {}; +} diff --git a/src/main/java/com/muyu/common/core/xss/XssValidator.java b/src/main/java/com/muyu/common/core/xss/XssValidator.java new file mode 100644 index 0000000..3a03eab --- /dev/null +++ b/src/main/java/com/muyu/common/core/xss/XssValidator.java @@ -0,0 +1,31 @@ +package com.muyu.common.core.xss; + +import com.muyu.common.core.utils.StringUtils; +import jakarta.validation.ConstraintValidator; +import jakarta.validation.ConstraintValidatorContext; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * 自定义xss校验注解实现 + * + * @author muyu + */ +public class XssValidator implements ConstraintValidator { + private static final String HTML_PATTERN = "<(\\S*?)[^>]*>.*?|<.*? />"; + + public static boolean containsHtml (String value) { + Pattern pattern = Pattern.compile(HTML_PATTERN); + Matcher matcher = pattern.matcher(value); + return matcher.matches(); + } + + @Override + public boolean isValid (String value, ConstraintValidatorContext constraintValidatorContext) { + if (StringUtils.isBlank(value)) { + return true; + } + return !containsHtml(value); + } +} diff --git a/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports new file mode 100644 index 0000000..17f73ec --- /dev/null +++ b/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -0,0 +1,2 @@ +com.muyu.common.core.utils.SpringUtils +com.muyu.common.core.feign.FeginConfig diff --git a/target/classes/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/target/classes/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports new file mode 100644 index 0000000..17f73ec --- /dev/null +++ b/target/classes/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -0,0 +1,2 @@ +com.muyu.common.core.utils.SpringUtils +com.muyu.common.core.feign.FeginConfig diff --git a/target/classes/com/muyu/common/core/annotation/Excel$ColumnType.class b/target/classes/com/muyu/common/core/annotation/Excel$ColumnType.class new file mode 100644 index 0000000000000000000000000000000000000000..1ac9ab23acc5382efd5b5f131791b6d162fb93a8 GIT binary patch literal 1552 zcmbVMTW=Fb6#mA$YdgyZjDe&H_h7&_7ZWZm;1Cjm1D2eCaEKJ~v~iZOXuXTpYgFF) zW2n?ttyBUn2t>s*KMM6bvuQDc2f|7-XV2|Bmzgu~KfL}2z!l8rkWo0(iMq9J|4F~b zpKcVA#9qw}!zghRpM2xTju({XqM+Xmm!3TGhM-_Nu#i>AE#6&d+-c4!oNa9tr!GKw z2SWmsZ7(0|9?nJ)yK*Qv*o{4e z?*vikDeNv+S`XdFZY^-b_1dk~hh8VC+Ze@nIgB|N$6hAz(V!9byUgUV8}z+f_Z2Rb z2fXJ3x7Vu=2-->PhwJsh2m+w-_d7U%gDic@5B+3XVPp_$)3aGwmJS?tFo7dXTE|sT z%{(ldiemzDTw(M}zI4uGC=W+0PdGSjP2cD5thw z)^wfOw4;99@#cMLm#s(av|KVqT=aU$&8U~;z_B{|Ra2$lG{ew~_5Jgt*pK5nGs(jr z<-l7rV=}Y=DQ8Yo4jMH@xm-YDT*i!imip5;MZX%k2~zp&4GjN{o$pZmlN=uo?pdvS zO8aq4BBSlklZuC3RMl4)c?Tu-nNRG`QsSgmIEVA(E^t{K?4QJxR~m)}yGd$_arMN$ zm)KX`{}&EDBcH-;rxy5yY5a<9_zeO(LY;truS=6ZDbCOVh7J5s04{P((C89Z2A7$~ z1nS(S+8U{3Z&hF5(C^yd4EMCqKXimNaVw2buw|_Nca~g%(d=8O6=MTNd&S&9uDxPy z@W`bPpY|<}DL%w$3*KnOwUIc|}Xv~cL3nLa&NB{r; literal 0 HcmV?d00001 diff --git a/target/classes/com/muyu/common/core/annotation/Excel$Type.class b/target/classes/com/muyu/common/core/annotation/Excel$Type.class new file mode 100644 index 0000000000000000000000000000000000000000..66efe3aa1ac688886c8f09124181ebbd1b6cbdc9 GIT binary patch literal 1507 zcmb7EYfsZ)6n@^d-A3tPPGo}O1*dL99Ef-u&H>K2WQcJ{Bz`KRVoBGUt~2t@e}Rdb z_yhc);4K=BpZ!tBbKatk$Ol={o}SC|oXh*3{rLI$8-T005ko{E-|(7+W_!I|ppT~K zlJKp9>AGHE1~%!HCk@NVEUZ7Wx*(t$&=3_+rfW5UGqwLm7a1Nm(8cg*<=*Z2I}2>; zHeg6ob?&#R$3QQV0Rfj%4%&>D{CS^|C9Ty4#KY!)2T zT`kNnuUUthYk5>#fqUqaAxy`G(W|Qbs|ISt*IcEhAy_MUhPcebzQ)_DQ&?cd0ESh#E#3n+;^H*%w zbOgHU_Nr?JZJ&76EX|7UZ#Gw@>p0bLwvU7iWG;1N<>Q;$OGBk1)lv7_e#5$9^N4i0 zJ7@VyQLbB7E4b;kf*586PX5>82pCn@wS0NoEbf-~uslwOoTeSt$^*QO07+UaDQTC; zlj2JRBnNqsNRN{LEJg`eAZ?f=|MX2HK49N>q~4GwFh=jF^lec5AjT1q@#jf0LQjeO zC-i@Z;P}Xn_;X}2!YEAOJZY2kXaW;YIh3w06iIfIq{_khk;5NxI6w3r$KH}2()ExR zcur-!KsR23Y5U2?CN31NOJXB{vD3J zmH}h*4jX+XGlYd}VTPp6>+=W9wF2qrSBNEL6H0wa-9)Uuq;1k+^Bi{0EsiPrZw)ig z(dqDS{0m`I8AbVslUq=vmQ_`$#iW+gG^y#Ikl#Xr=MmECviE1mV}qs^wpYS6@<&j{ Rw3IWrPLelYUV6hab06d?2lrFLv35!fcRO}a`RSYv5oi%J@=(I`#& z(*Np1KcFAgbW66eV!TSIU#!{MXV0E<&K&*y&!2x0(GRp+pfil_*wQ!r=qNJq;L8Ai zN*Fu{q~_Yi+B~#{SD-ATxwrg)8y*ij#>@6wVe10r8QmD6si@LB0Y=$SD$OXfxMVRp zKYCLU`#kb==8xm!W3U453r3d~m#QPhrdDpydBTVoO*>qRO{shiu{kBUQmuPoZE*Y7PLfM#$CZfWy~XHU-^XD0+|S!q%Vad$9`}WkB>r+6)l&HdO2j?4 z6EJ%80l(W)xxdRmbFUN!_#)77{op@cPmamQhRp_}LYD`QCy=a*Cz`wL@i0!w`sBhy z+V#RDp~)58?u(Qh-b2SDY^n)=Tt^BmapisCr_0Yk%A%vtJz1S}&MIi9>zqN)tb80j zQ>s?c#!VS1TWq=>oJ}X@SK}+qXuc5z+V#b*8@g>zj79-0-b`Jb)F5dHjS^rG;V`*_ z{=HlH0Ch)tu6=}nLgQ7vW!2Ci*C&Uxk_!jst;z3APsANC$VP6{GOHEzU|oc|Cj7Qg zVS#oSt(`8ZXxhPbED-u)`E*h7<`@T75D1mDj6k-gM_Qpp zR#RAIESd&zj%J2HNS^@sbR3u+2j8s zf(%*%^x!1)5YVIdq4kr{W3axaZ$?_*(szKqrzdo#UlN<1;u}m@KEsP2$In-!(kOU? z%9N!_!Zv9W|JM+=1;VxS5Xb*hjnUSR>PMO=g@qA8Ul8~(ug!9w0as01-148$qsLPCp+DK;ady0!i zE5d$KTc>k=(v_YVUbd(b_#1@eZYD842fx5EiLVP!dj z27e&;h0$3fpr1V6rlXrEAP`pMc=8J*TlKLnQ3o4ewu_$l1JRqp_+t=b3)|RPC`KQ< EpA+GNlK=n! literal 0 HcmV?d00001 diff --git a/target/classes/com/muyu/common/core/constant/CacheConstants.class b/target/classes/com/muyu/common/core/constant/CacheConstants.class new file mode 100644 index 0000000000000000000000000000000000000000..8fbbbccee4ce763b74eb5352e15db83c67f00ad6 GIT binary patch literal 878 zcmb7?OLNmO5XV=|vmrE)(xK(q@@z|q;YxuS<2bE}YiDeeHYbgun8d9kcWejf$Ku2c z2R;BNJ`uwz3Bf($!&*P>e^rn2^3!kB1qp7$ZhX*2~_lxj^U`D zVOs>qdjePg=IXL=;uo+G{FZ(Zs5*Mf(cMGp8AtkjAHEP+@2IYOY&%VQq@GYm_naQp z><^ZQ`HUO}7YNiB8q?O=moiic)J*%pu&8IZb&Iz39_r11g}Z4Sh~a)2Zeej@GzSn}Q*ky4bdA^xX zM?pfMVgAo(?Bip*(In=6E0CF2FHcTG-Z*Rl)`D+Vfelzji}xg-AF9YJ8LlB;hx)?5 zihLvEUqikL>kIz|^6d=YLB5ya`^Z}vet`Ta!;g`lW_TMj6FK8(cdIA-~a#s literal 0 HcmV?d00001 diff --git a/target/classes/com/muyu/common/core/constant/Constants.class b/target/classes/com/muyu/common/core/constant/Constants.class new file mode 100644 index 0000000000000000000000000000000000000000..b6a66d752983ca62f64ac8e6c16dc19397a62ed2 GIT binary patch literal 1774 zcma)-;Zqw$7{;H4ge){|A+2bwT5HupwU<^~ZGl!pIN;#m;#~ryRpSwka3#5mcNZxA zpkMrloPMa|jDB|fd;IM9Y(nvvjx)l{?f%~P-QDNieRuQc-@pD2-~kTCFofY8GI?Y% zA~62MJ#dR{H)s{D?I&I{5g3{C13!5vFg!KAF$Rgz9LDlEgS^1Rzu`uZcr7my$Q`)t zo@ea}Oimfo<|`4iqd152IgIBqfr7yGX4olqdPlt?JDo6K6M6guapDF^v3%T#IkQtM zmvzSxxbPp-Ud-b)HD_kAWSBX;ekvg+iTt1?af#!@sHMeS#k-N)@eadiU*fWWqy|li zQ6k^Y;~iWPD6BZvdSi3hsOqNSR2xp!<`j=p8U|DMGrO9{duq_ciZy?FjKuo_d9@!} z;3ZnkHYGk3xU4KswZQFAjb?DKeYef56YpsvF~#MT*dw>o*5bh3_f${fhQOGzyIr@r z=Sh4laOI_TZLpDSB2VijemmB7{kEr4(())Ob!U}2-L@@zkWHpmUA)JoUd@TIFOOhD z9jwG1fh@ysGJbezepTWwJDZ!E68E|Dhlht6wV9T+TB|hdHA7;K=BVS(2%I@Kn+v6i z#21{Xz2kQ2$fq2R#5~JRd1AUU%hf7_b)NrTl5}T^MGjLo#x3*2lripGFPhepvEDeY z5++uv))=%ZAa3zM23}PeC$4q6ZZpGE10}vvwbpAky_i)uLZ?-$N>o)pY=u2u3Tf+0 zECt<`*i_`T{5Vn7&%~7mwTi?eD%GPXq+M1@OL}9ywkGinXYaZ#Z@t$M8135~mntG^*mZR?Izv&(v;V(W{>mIRf^VmAs^op8&`;f_*Z z`hilp?L}30TR945*mT<)ZsaRIHfEANpZhdzvh4oPMV{qabItbgt1{+q AhyVZp literal 0 HcmV?d00001 diff --git a/target/classes/com/muyu/common/core/constant/GenConstants.class b/target/classes/com/muyu/common/core/constant/GenConstants.class new file mode 100644 index 0000000000000000000000000000000000000000..10ae21ef1b0a2726a1cafcd2428e65c503009f42 GIT binary patch literal 2727 zcma);`F9gl6vywBws~y>gn$cz8z=%I6+{F9HOo*&CP|x_R03+8Cew5vnbb*w)P3I- z_kG`YMN||8cRk0S{DV9mzxTbAj-u!IOWymud*Aonx7<7V?a#wUiD(P`+D~0HD?;5- z>Y-kZV!NHa&S1^)tAn|*-EO(1QSWBY_ga@|G;8^a(FlpZIa#g1^Q(gTHR>);IDw$3 zMv=WvpkR(h!aqhaPzTobQJm&SXhD=tpc6G(S#Hz^>+PxbAUgGik0x;O<2PH5-x?fp z{Zyyl)M!pBXBIM9yEv?uEIY5!{9Wcu+$y9^f=!HwVI^0P*D*|T!*ulo%Og4zg-`5an2hs9fNL_ z;bIuZysC8CtWnQ+t>Hiwm)2Wpw8v_Q>@xUQ+_Hy7IId)rQXJR3&?d8mOj6Ga(lzSx zDuRX(Mmcb?vC`O-AVZ^p4ytW$=i_i|rSY0mWz9^KvaC5@X=W1{y_C(_B|UB6@P?t* zp5$YaYPhz64ze20x1&_}xA~@FVg41^OyJg?U=RBiqqLKK7yM(bk%FEt3c3=ZsaTVU z{A%2|B3s_qHL$9+**XTRBDS)4B}y)f4{Z`iD`}d%t*uY`! z0X2e(8)VycL3M1_@ymDzjiAYrjVMh@NjtAkOJasSY?e~_LRzEv@lRNz+0(Cn)TyD+rl#H~Xdo+rw1If64+d!1Du#3{T9}{Ps zlv4jTJ7boN>~I11{1D!+=TEj#dS=UU&OJn#5cCMIk1R!a4sJ(UE#1^pnD#iHN7Jpz zlBM+WiL{Xu^c3_0r{XoH^-|mP)b?aJ}!EV!T7?wACxdJ@2k!z>(%1&+62|VV`V0UZ6Ls?&C{{N4_ zCd9G1jA|*rO#N1)9hBXzICNZF_+zn}zY8emaeU^8meNAHmIBhK3F@L2cyEZW15btc zdhm@Qz6pFwh;Ieo9^yN|cZK+F@Vz0v4~)+p)?Ub;7}?_MAX`*p*`k8V7L`!8sB*GJ zL6R-1h-@DS={^X(heNy{{Ah?D13wYsC&5pL_!;nXAzlK0KEyA8UkdTd;8#QZ8u*P6 zzX^Uj#P5LL3-SBl4@3MB_>&MH03QtTXW%bFdEZ zqKIE)6=S{ejrfhkH{n;rx8S!geko=rI>Kq5yp<=U8|IURLbC z6lT15`BC`p5K3?tFLo6#R^r7Rj+E`?j?_5YC)mE5?KPNuK5tE)_cH23e;qBNjkKOF fp%MHA*-r+ZMwWgjoBp7k+F~lf`)Bxd(Jy}iAhJ`M literal 0 HcmV?d00001 diff --git a/target/classes/com/muyu/common/core/constant/HttpStatus.class b/target/classes/com/muyu/common/core/constant/HttpStatus.class new file mode 100644 index 0000000000000000000000000000000000000000..ca14d556a094d0993a092b35dd61d4316b2caba2 GIT binary patch literal 905 zcma*lNpI6Y6bJA(S(-GYq%CXN%bEsSm*vJHrOrgL6nl)9pmMRCV1cW|L8*ov;4zCZB>QQYq*^acU%F|S zX8VjPuCMFdbr~^((ULyT8HD33Dq;uUk)k`i?eUJ}4@T8CCfIWyFsfN1(1qo3%aa*D z5i!MpcLJL`CL(N;(W1+FAiN%TWI$t7w}cm%qGNQ8yfE8lbY!R92^{{|=dP^8-XP7l z+P>Elj`8#`?Ja6`g|lOHI^2>;?+}o+y27{Q4%vIGFAJF*^YX|C>=_&E_6ocN~%L@fV|ptZSHdpPM+_;rhQBm7cU6xpZL{@A~vvgv@@N zL{>H#Ml&xQjuBbyPe#!C0WXe*v>MlKKDu literal 0 HcmV?d00001 diff --git a/target/classes/com/muyu/common/core/constant/ScheduleConstants$Status.class b/target/classes/com/muyu/common/core/constant/ScheduleConstants$Status.class new file mode 100644 index 0000000000000000000000000000000000000000..fe39bd0baa700faaaa681a84df4a4f0941980da7 GIT binary patch literal 1633 zcmbVMTTc^F5dKc@UA9mv7eOwHMcZ;I7eTOyKrI1NE^(y^@o8ySShC$swp$Y4{7DKb zM5E!^A7z}`t+8BQ>^9xmIcMhkX6DSCU%$Wo1n?Z|VfX~*%T_g8t-r5l>7#0yBy5dd zX3bGeC##f?v`XF3@{NUBQgKwLUW-B?AR~y7KxljC_10=pV4>I=SdP&mGNOzY++E#M z3cS)LLq<#>oGh&t_X>)@;@egkBy=DiModO05=2rqEK?KcN~Md(>WP{)RP!*qb8xJc zot%U&bcfL+qZfAt+W#38%zBmLIZ=(ew)0M4F4g3nH`H1!*R-HGwr(Egni&|t#qXDK z4+B)YCEe7WWr3b%sD|fey2N!Dk}-_?6tv4LkhsYaK`?@c0%A%chfxN9B#^od^QLpU z6hSnCG`UZ+`eH#h>OS|zIZmgXE;m0i^))zNIqF5n#LB&4K9r<3%|VxD5z72Q+~ zfvBP%n$-9TwV zUDnohE@{t=$(`grrWm%hnzLcmoG|hNJoW!G{sQu*X==9H>>5=qo3EdknwJG5LEn1aNHi?AH0SU7eSn`|w|9ks3;Tk-{o$6|%V zf(PKC5Whn#BC+b`%zWqk=Q2n0>-UeJ0N&wo7Y#H!X!Wpxw!rpz@Fh^=AURc?`MC@W zf%e-diHi3E%_mQ19Vh~7m@d>s`L(Pri!|YpNtz@tf}~L0@JybRvD8W z6kHJyF9de$fO%v)jIWSc*C;p?*sBH0bj=?5EUg>HNWm@@FF9DP7&*S}cppvsNWl%p zUJ11H^hC0~6(!Ox7jv0;!91pCV5MOY&w?zfc;##rXA$3iXkBn!TtwGR%Pf@Rs4Bkm z-$I{PyA!x%%e~!E3K~ZwXo|z;v)8in|C9(b2kNfEGdjM?=v579& zlGH$t++X9%Y$!&qqA^F#H3^^QJ!l literal 0 HcmV?d00001 diff --git a/target/classes/com/muyu/common/core/constant/SecurityConstants.class b/target/classes/com/muyu/common/core/constant/SecurityConstants.class new file mode 100644 index 0000000000000000000000000000000000000000..206f8bd3547593ff3fbe83aee5130357fa9bf349 GIT binary patch literal 750 zcmb7?+iuf95QhIvb1)$#C6sbLlon_!fyFfkDN0;w;n>JdBEgNdahG(9y+&&rDZCX| zNL=s$JQQMV1!0LRZf573)z17gvp;`*{{i47j=E@Iql0D-EwmZ7&iQ9<##}8;Z*eZ7 zoT2?fDw)4x*f=QJR=Zu_{U@NfjMhkWpFARc;0%Dzwa3qjex-=#Q=G&~}1w z7Fd30j~NEe-%3HQrCL5`*!=Tk&f`KD*ktGwnb4s;C292>?r>rm*kUlsd&LvMusxhj zPdwj#Kb+d08=hFhvE>`MLV0t5DWR!MCH#%`&cN+;(eR^KF?1d8we41Y8MsFrr%S2AQWdrBdyW;pwfu=4 z1f*Rtewu$iAp8tF6V zKes^h?FLnc$FfYm^Ut21mMg(7I`k(1>K_--1x<&jfgbUt8uy7W*LXm@UE{07*J^y7 z_-2i75#OotUE=#S-lfX40JO>Okpi<~pmp>O>}w@ypKN(uihW47SNSbG!eb(a1F{Vq F{s!KpqUQhr literal 0 HcmV?d00001 diff --git a/target/classes/com/muyu/common/core/constant/ServiceNameConstants.class b/target/classes/com/muyu/common/core/constant/ServiceNameConstants.class new file mode 100644 index 0000000000000000000000000000000000000000..fde0b1c9bba3efd03574ed477f00f8f0aca57073 GIT binary patch literal 527 zcmb7>OH0E*5Xb*(U$LeS>$7_Drh*2Dq3|1CmXlAIp>A!4Kev z5+|{>J$UglJHMIP|Lo3ue!YJHxWaxJE6A0Q*HJ)`p+4plZg@Nx8u$INa1}%GQU+38 zGvp4BoDwvKGdJ`NKba;5Ier+>5eYIutazXdTSOD-iZ1s>YZi+c^qa@t?Xzt@IGvWs z(6APrY!%61c)?Ja*E`%xgoX-3+4aI?aKaNcVyN0rcF(+%}xR_5Xb+OuN4s${Pv_BZ5qATwdhS(wAbiV^K9Ne=Gls-| zsg;4laOmsM4Wmiqk`ro0hY@7d&~jy6Pu~kw`Jy31zI^LlRvTWeQubyB1z@Q_O+2yZlzZAlk1!6MeWYPGDGHEa3jnvsm*HTrs_E;GL(4R z?}%=%F9-KQsD?a{LO<|Mmp}5c=#GUM+GsR>noJpz72OdGOLeJ4BMRHXc)T5uQmAX6 z2Q6-7%wJ;C_9gXlRR2%!)K<2sBjbw;8D}p39nDcZ2J9h2I{*^c!aTBISRk51jyV5| wmxxz>ae-d4cxz-=DZ-sGkUDq)d!D6OBO6}>4%W%$W_}7A*d$_lr*OnbaT<>A zIl9fLWcJHDX%fW;>x{aiUoo z!4MNo(+rJL9xW({t5Oj>Ch-OEgv1xYljnHq9AA=H7!Jw&hR4TzBjJ3!li0jq|=A^-pY literal 0 HcmV?d00001 diff --git a/target/classes/com/muyu/common/core/context/SecurityContextHolder.class b/target/classes/com/muyu/common/core/context/SecurityContextHolder.class new file mode 100644 index 0000000000000000000000000000000000000000..c92903ab84ecf83edbd5b8f593c81f4c7c5cddad GIT binary patch literal 3555 zcmb7H`%@EF6#gy=ZW0%PsMu2}y#bIW%d+xdC`_4JveelO$zy1c`D!$904G9BD6Ybch z(DlGtw(=FrUC2+(J+RBZ!nOg&b^JkvL~q|r1}UTsWK3*_sc^PjtLCeXl}4T~)tbv6 z&*sDR?MHro+AcRd$6px^zDH`6dD~OizF_;sTG^^hSW61sy?sRu+VGu9o~D9<9SX-| zBa2TxKka*tyKr0HQ|MA4mn?P~*kxijdK5ZZ;*t1LgLz!)4R9jK75-@n$wZwb`fDa$ zm(V7;PkGnu`&OgkXRr@%7}#&(01he~j-{+C!3r$f9!a$OHL_=VF=V39L5hV8I&j#; zn>eD7EL(L(_`jJ!+X`tm>X?b+I6=~-kz3b?uFV#whKGs@xuOuYD$blWXXSmrk}r9d zTdz94Z_QQg(xPWu^SVLA??cW&zloDLrO><8iL5ybrLb$gxzTzW$TN**k0=-F#Is#L z&pL&#BUXKpV#we$62juFiMMf%aqALycU<{q3|vqUCB{m!?ps8NXi^)fpi2ryqi%b% z&b-L`iX>auROqlDu`RlaK?CoYco#zoJDLlqSaTN`dfBQp?5X<-JA22X^JSO?-iKim z*KnN+&94Ph&c$RGQL3I86DfuFrAQ+RC!zsD>X87UMEYh@7*j||?ygm}Q=mBX*X%=k zC51_aPOnz6XP0cR>eTB_&6Q5vH1UCK727FIb543RA#zNtL<0B zO0XOfxY^o}9M`k%ZtGxJUe2q^2G`k|Bjfe7Qe0WGL+ zQb{*$c%XD8G8P6K+yaRpLLcJ@>5$PY%7!NoH;?AYlMQj#!xZ{vxPJZS_Q!%YkBxP& zEjC4BrMU;*=HePXrUUt`JG5+Rp}r)DVU^3ZhRaP)&Q0;2WcxO6ts8=r z6q`<&aLTv{I1>)(5542*w+{N;+*sih6WyZU+ndXuib)NWE8%@0GhJ(VW&4IB%WiLE zW0fN>IP6c05OA2gY8$T`^pXRKztj9`LkV4c=e>i^+qC5Q%hMd~xu;M+X_py3Wf`7n^dw--cp~=g24J>1l=Z4gy zCzuctPx87TbXA0|SS(ruw_Rym!Mx9aQZ|>rQns$3Y$}92v>EasArD1r)wNNpE-YYC zSH_IgDj+6f&c>KjEt38MVFMvNU>SIy~oyH`xI}D z)0%sNGdW=xbhL*W0*)esW9;s67Ucx?^H^FJOorD&DGN5r5?Cx+q>Cx2Ke1oB7tnb~ zIu%sI3W>mkPAUWzB}JQY3I;DJ>sS)aZNUmGf)%#FS$Qt8$Q%is*Bc&LY^Iv;W~vKMn}xY1lQflkhI?OcdZGvI51heUkFPNRY4 zkiY?*Cl_3Hlms>-U#9ODG03BF$XAIx*n&J3MIO`0k7AI=V~~f4yb^;v9*2CL$TwP$ zSEI+!O`ZVKpk0z_=YZR`1Wt)qYv8v literal 0 HcmV?d00001 diff --git a/target/classes/com/muyu/common/core/domain/Result$ResultBuilder.class b/target/classes/com/muyu/common/core/domain/Result$ResultBuilder.class new file mode 100644 index 0000000000000000000000000000000000000000..a883239c39f187b27d09127ef0ab5a4ba803385b GIT binary patch literal 2355 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 literal 0 HcmV?d00001 diff --git a/target/classes/com/muyu/common/core/exception/CaptchaException.class b/target/classes/com/muyu/common/core/exception/CaptchaException.class new file mode 100644 index 0000000000000000000000000000000000000000..a25d692bd7a1358913d97684d585354ecd2d587a GIT binary patch literal 499 zcmb7B%T5A85Ud6k6j2Zb4L5H-5;vYvE~trSHAa;CuuNdg&J5X|HTqeeOg#7jew4At zM>KkJ4&BwAN_F-0%j?rKfCH>oQ9!YRQVjvhgwBn)6=5RGB)rUxQ(7L~#d7A9HH7lM zGRpM`twh_@Ua!WU53mlxfCO zu1^jLbV^w4Ta!6q+)yOB4A`xIL@4#`SQ6@yGV(mvBbi=_QNo>OWMkG5tj6_cv*e~K zBkV>0^wlL4bvE&;e^w7ZO6&KUyIuqvsPK357U*Ce zRn7}s6;NYD{s4@ZxYOh8dFAb$N6^DNr_RwI3(O9%hz6tf2Mfz+a>abCV3n0xj057r Kk7{!)VC@ZaO?U+W literal 0 HcmV?d00001 diff --git a/target/classes/com/muyu/common/core/exception/CheckedException.class b/target/classes/com/muyu/common/core/exception/CheckedException.class new file mode 100644 index 0000000000000000000000000000000000000000..e9e4e971da48f8934bf17a72103b41aaa2c2a9e0 GIT binary patch literal 1051 zcmb7?QE$^Q6orqwv|*v8?O322jI9h1>n0*3-UfRqAPp6SXxE26;iXm^)+CkVF!)(K zRpNmkz>h*)yIEIdAVDj|*U9mB&b9sX*Y_U)Uf_{~0#ol9L;$sigX?)5e5n&zxBG?o>;v5e38=|p6r>snN1 zXf2B}%n~@i?1o-eWjOfPWrM+9-$tFm^W)G9)32#6!Z@Nq3Qt^g=C#MdKNG{HR%?ln z0iX1ROsMsfhnn|inN9L#~7@3JQNJoP7sNAX?QARPdCi4)`@-M zaH7#Xduge0kT5*${AaF120Ii)^L-? zb($66!lIh48P&Jx%@K`SS8DCgz`hwxgP!^x0s62j*r4jxTt&1yG$TFkVv9)ks4g;^ zXe+vPB)fvO^Vk|G>wm#+W9JgPongxv_82Sl2kh4V!3<8GyH2rO!`+$77IEz?E-i(2 zD45`@1rz+jrJA{Xq(;`&NoKpI)d%z};NfqJ9mul) literal 0 HcmV?d00001 diff --git a/target/classes/com/muyu/common/core/exception/DemoModeException.class b/target/classes/com/muyu/common/core/exception/DemoModeException.class new file mode 100644 index 0000000000000000000000000000000000000000..a244c78a3ea949002c25bd485db36b0dbd15195b GIT binary patch literal 416 zcmb7AyH3ME5S&fy7)&4`Jc@L5K*1p>c{B+~AVa~!{l%Q%kbQ^jI|QFag+##z@KK1p z2qYvbw%DCr?TkkA@%i=+-~cNrT4*PTx`@#s4DaQm6s0t`;=&uJD|Pf#s0XL5A$0b& z(e9AYUf;+Q%n-HWxhDWW0`k{!is_dD)`7P3KRIO#1tGZ#)>yt5oP6_iP zYZ@nw%Vp_R%;rK7AsR8C(95(@=f0Y#`bthpz6>&3u#RLk-oKBLyVDI}C;P9lJwkeE zeO;(y9n@F~1X0KkD_0$FcAxvv zNcyM5>_8=noGO8fgWU8$$6h$yG2%-Onnt-XQCfQb{&9ILQL!iep^6hy9lq!bh?gwe zjlx9BP!FY_stUU^KOs=wjV5$y4!lskN`sM#-^!6c^Nq&TkyK+oe_7T?ULvqPI9oqE z0xO-xKDtAJ*1pn5(d4y^WuP>v`D&_k|L?ArJKe?YtV|Xv`$axy$E}m$gBXYhfILbf$JWo%Cw4V!Ja8ohjb*4`bq yarYFIeF9~hi%yFIjf1nCZYJ+GNHva%t`o>cL literal 0 HcmV?d00001 diff --git a/target/classes/com/muyu/common/core/exception/InnerAuthException.class b/target/classes/com/muyu/common/core/exception/InnerAuthException.class new file mode 100644 index 0000000000000000000000000000000000000000..c088e961b511f5ebb486c5908b857985feb17a63 GIT binary patch literal 509 zcmb7B%T5A85UfUE6+sXM3^#8+5;w*pN@7qG%xa7%_hFg9nB5sOJ8Sf_Jehd#1NGzMet83Dm7a9AhaMIQ`=N>bIDJZe2C>nctn}rNh~L**+T+ws|LE2dX_bhE z(k49TwtqsPb3&zObmD||LlLF2z<#qwgnZA8C7~K9Eicn}B<+nDMckab=e?nyB8TidbiPBtUr`}Z@Bw@j zVjY1*LB$ojv#Xuiz4`cjdk3(Gl>`A=F~SZavJ=P_mo+l8sJ%0KyxL1i>2D>L~+$&75o)Q zR7gDV0elo2%sv_S2HbMDwZ$M4#2 z`hM(sa_>_|evI9~7npwT`fmJEAlIvy*l`&L)0(qh-p6t}l=K-iZv~ zIww7CFX)iRAsgp|KlAaK8wqS4{^Pl~1geeHNe1V&+5(j$8J`8Gubt5GWK7!vjSOgN z^DqXzyDQ_}%LyzstQ2^lM4e~?xtF11{m0RYG%>n>B6UU+lPgKU)l061M-V8sf_~VM z`>x8odbM93sTRQ|+rr>==iv`JL<}&V4Wd`oz3?R&YrLe)~V)q zh_4z}<6C_Mz+KYTag(D`0>Ula=1dXXAwm)Cvnp&+5j58?V2)YlkmJgIvaFzh2byU) zK@XUelCmeLMH7OPguXtYf6uB&$|L&b#sy}_8Vh6xR{36|Mgw!Ov`#(QHqt2%bV#Lx zF|=aA(28ZPdXH9{V0SnWik- HHPHM6&6xDZ literal 0 HcmV?d00001 diff --git a/target/classes/com/muyu/common/core/exception/UtilException.class b/target/classes/com/muyu/common/core/exception/UtilException.class new file mode 100644 index 0000000000000000000000000000000000000000..6352b7851b43c302fe2abf6fbbac98ce6d12607e GIT binary patch literal 880 zcmb7?%Wl&^6o&tC61zzqnx+YqYr{=6NmXx=0$or*DoPQeZT8c2lnn9(*<&t`#Reo6 zJOB$`g7-lDPTbT$Hf($@bLKn$oa67mzJ3Gn824+iP;yXqQGqS6@kYLro-f0RcRY=w z_wv+N0=0=Uhbl?rgh6Y&JG`hmGO-RPgBn&)bx?D$3RmFTMZxPdG&)evK8)2{qoYv3 zexyTf_5^y{|AGpcg@8eKl<_ZR@HK(S&^ QylOEGCNlAN`L%HGH(nvSW&i*H literal 0 HcmV?d00001 diff --git a/target/classes/com/muyu/common/core/exception/auth/NotLoginException.class b/target/classes/com/muyu/common/core/exception/auth/NotLoginException.class new file mode 100644 index 0000000000000000000000000000000000000000..7c66f8f8e4274671e2db8e089ae7a01985f14faf GIT binary patch literal 516 zcmbVJO;5r=5PgG`DuPrLG~B%Tk=T>*fO0`iG?f@p?n~K}HSMlxw?==KCle3;0DqKm ziW*J0xrdqeCNFPhcHTc;-vAtAvx*WHT$F2YP$6{g#Dnl-p(Fn~*H$I+^f8q8RvAsG z94W1AkI>oJN+!Or-Q5Z!eRn z67fi;8TYz9KOxWsq24z-vqIaEh;!-i%*7)@xo;+t&4gFG3_^hS(ht~3J^vX5Xl z=HJD#ovMs*82oFxF2PM?mWgQQKesx;yOefnCRZXAiL|@}p*l2q8p<Yx15Pg&8qh!-2BoLqlN(+b5R`|dPQhEs>fkLS$&7~)2YbB0$*OAv-!O!9Z z;=m8!M((kyCESXz%g`~2R#v48*h`3t}+Jg%aEr3#8Qlu#zDjpex%kxYi-QGt#xjBSs;DBUpjN{QRtZmx9SS*-;Yf+l z#IZ@zDgSeEaqz_HC?yofMzh3=?b96f$H7>Iu9M5m#jd6*Xb@hqf{3%P*-VOqRYJ`z z2${K&I5h4^*;uD3yUN*Xq{?a;^_BHMPTsv{H}456T`t+l#PwyAsS>x%58{%$=1dXl zJ)Nk-EFLKPSq>u3G1#cX`#mK;a(q`BBC??laQ^UYEEQ4xaWQ q!#r&lOVb0v&Tg~v12?YWZn8!z#~oa92iU-)1^333dz0q^w*CSVy~J1m literal 0 HcmV?d00001 diff --git a/target/classes/com/muyu/common/core/exception/auth/NotRoleException.class b/target/classes/com/muyu/common/core/exception/auth/NotRoleException.class new file mode 100644 index 0000000000000000000000000000000000000000..c9bb092fe2cd81e6507df2d6496aca29560a9a6e GIT binary patch literal 783 zcmbV~O>Yx15Qbl;*-c5(CL}aK`6vYrrA7DvLPCLF0!W}xP(O0vCMjJhS8Hof-MZ&u`xWJj0zTN?59(Tt|RVV11%KC>g7CB=_^w znMA+%80iydY$^~wHK}pC0&83Sv%CYBnRL{>H(189fVf{p6}1ZLb*x}j;I7R^Qk|%1 ztYu`A#HQ03|Dim+c;rkx6(~=vp~RD|_XYIZ;Y3HSTZk;g&W9>!3Ou5MO!80pT#6>s zQZHJNDtBYKXWhPyi;Xp>Ix{LB=xoYHk6t}zAFl;gdMwtd)D2Xe>wp9EOR(6UJ=Ow^ zzDf07o(y$%sD?2kt-g)OQKZqI2g`13*x}>;{}k7yTE=QU``pY0ns2ll+v9gCQ;Bx0 zU!e2H2p0nz2n3$Pgm!Vn^Zx?X1Dj`&erdec+TSyJ;I*-d2A>N@fOT9%i?&U#giHL$ zM@ZXYWS7?Gh1)w{AU@A>E_3&f4OnGn16O$JoMz!Fd47eshU+BV;5iU_9pidZvO)Lh_^=o literal 0 HcmV?d00001 diff --git a/target/classes/com/muyu/common/core/exception/base/BaseException.class b/target/classes/com/muyu/common/core/exception/base/BaseException.class new file mode 100644 index 0000000000000000000000000000000000000000..7d3b4ee4b317383cde66f64abcc7f55ffef31605 GIT binary patch literal 1706 zcmbVM+iuf95IvhX2@Xl`Ei`~gDNx5r>sGCVM7gN6AOR^LO7p^#lPsyL#72&d;EhiL ziKr3}d;lMXm|44R+87B)6z}Z#%$zy1v-$b!`wsy3v6e>)=^V5IGRO*)4($)N)wSJ@ z^*VF|rzanOY{~b5^j^c?a9Ttq=&t>yKsUic}r$mn@4Exxqm zaj@02`_g(ukKuehuN`)!z*PN|b}#T9x3kIV#|jwd+*-?POLj~aFokJy*nX!kF#UEY z?q%~(wgMG5TfiL0jkV=FJM0F#vfsBmGEW&Uv(9;ex-k?QQ;vEP@{fWl4S|Wi^c}m~ zkba*!wf}TSKs*yDZhLM&u-%|xcSD(>j`K&5fNfGFFkW|D`6BE!rT@lmMxF3ll*guQ zw2z%yaNv;K_4+^ec#}%omBE46er5Z1PX<(=z}^29&r+BEDwvT*0y3ea44qAYvS07W zU^gC;8KW}Ve1RN6qHeOar3Pcxs3Z^xOcG+}%uER6_q@<=$tR9#|CtG0;i<#|jZKP< zErSw$uR!1`?NYc#KN@{y)0nE2rcCdW@K7i)?O|ZVDl)33}FvXG8JKMZh6FtU@Ri84;gP1tz(8 zh%8#Uh>ZCSxi4g-P^M>AIq#^9QI(lEqd+w=xOhHzGcmY^Y3wPyF`&@(N%V3wfjjGPjPa3}leaVW5B<@&X%n;F(_h7@JJIz~>Sw zQ$5(~PCG0cGE(L~!{Ik1FtFqJj@}cnmaLAvm!>QImU*c|$8T-_Ms9kb^F~M6x({i& zo`dnXoa6@P5krrha->2>x^)#sRD6AWARtZzinYLxwDfgdy0OaAhvX9iTYXgjQzJdB z1x+PTvK(KX#a=^&SF+&}saSzc5t33^e}Ng@b|QgI>kpTIUu&DA?Cz*ak9}VrvnRtA z4<7BKTOjR4ErIf>((RymAw%gYO>+c{%ODPImH0=&KWto2ek2w#$mcMPe9AgUeAL-J!*Z=?k literal 0 HcmV?d00001 diff --git a/target/classes/com/muyu/common/core/exception/file/FileNameLengthLimitExceededException.class b/target/classes/com/muyu/common/core/exception/file/FileNameLengthLimitExceededException.class new file mode 100644 index 0000000000000000000000000000000000000000..861ed21f7f246935def64cd955caab1866bfdb00 GIT binary patch literal 806 zcmb_a%Wl(95IvJ7bubA{LmMhe!Mvn~MJ%=i*c1?{Op6qVgjn6!6X#a8Z)IOs@L8-t zNbmuC6k?pVY7i_~;Kj_G<9Xlt@$>6<08engi5g+2JWh;?1~Z*-rcxg8hmbiM2#W38 zL7mV!QYXq!R2KV((<2V8i&Z!dnk`&{OIW}7Kguk}oD-ZAm6UuqBlLDh{ooIeVG}LF zmYuVAUfI)yXN~a^lf^EsV9i0Ng>`fZ4?~msY5B4A#YjyiDrdjy^w?^FRSN%wi09`N zVfC@jw0%l=w7XP(Y;&E(!}m+n=}f~V=FF)-aj-#n`LD$fWPQP6ZGld;t$N{zqotlY z1?O5N6V3~1`|ao%f%XaQJ(CqyWp+|6ts$>deMG3s?k1x4OvHq>KxceVrc=(}sA(ch zH!z{3Q<7Bh8Ckb;T@dzz|JC=9uo>}8m5Hr(d@-hUKa%r!#ddC@*D9C!t&B!+$EM6f zuDl8T--G_Xn?ZFUxQ(mw@G?5CV-r2m*96tDC6?Tour+K86UYJ9L~lIkQvDlN-mPqZ xL1Wy#{2A>}i-;Q{-Gxs}a<;J}xOW>QW9Zhge(*aG(W=%B4twNEN4zJxR8*y_LPL3jc}| z2nl`wKMFCM77-3eNbF%|zHfKu{r$(6ZvdX)eghT4T6vln75Di(VvhT%Iv?r}?4d^J zoTxJuBr2N*N8=NYY!gfHJTzLkf+k`4_kC!xDZx8aNy$eO!s^b!ZusZ*pn(?Qrk$}r z(Fqq4#n**zjqwwePq~R@ta#|Oa1C9;!^osTTD~s>F;bI>$~kaZo?0!?`3PQ!c>aqb zEIrnlwoeI{?IQcIxj< z=(`ob7FK1pQX@97hIP@`1yyiEERtz&hvP=0U2F=H-|*)#w#2TJ02lb4$O^W90zYKT4*&oF literal 0 HcmV?d00001 diff --git a/target/classes/com/muyu/common/core/exception/file/FileUploadException.class b/target/classes/com/muyu/common/core/exception/file/FileUploadException.class new file mode 100644 index 0000000000000000000000000000000000000000..ef7dc4ac6bbdff00c01970c581baf8f7bb198998 GIT binary patch literal 1456 zcmbVLNmCO+7=4|Pg(U0}ke~!`$%2fEfGp|(wMr{kSU@}vNt=u&GpWf$r(LX`)P z9{f?3uRD_k$;ml%_t*V>@4auC-+zAm1n>mAF$55dU?_nQ!UCHmr*70+XDx$=x?^*5 zr6E6;WW%#)zPD=9c**6sQFF}li_U{Uc-OKm@2Nm8Q}|$hGL4#PSByi?wd~4H-_23g zbxzHbn%v12V~Aonf|w$Y3rzLm`hceg;AM-$)IE~GD8>XrC9~C(0@DL&6v;#alb8}1 zYfz4N=$WODN3L07%&d=YImSWz&6Q@I+M<{ii0vula{A`+PY47v*`mPEo>OM(V+G5WZ(8*e=^m+u2uv27646Xz z()KThysFg{*ed+zXzY---mEC}UVr_$U3donUg8p^i&VJ{p@7U7}Xl4 zryIC$HhhKQQ(d$x0e&q7q7~`sP72IqvVAhTEg@>`&}q3PsfL?v88G9kNYK|CXb@{Q#3gCLTGn9&f)wwn*Z0Nlp|LOo<~NYEJ9xM3gnE%zXw;<@|Ue_Lg?HBnrS$|Yw$v4!Qakjt8!Oaq!inD?i&)nFp;0U zc8;Vn^5;k?_8?SolH>)_To+lGCBiK0daFXUvrKhWq_BoZq*~X}Vy=_!8f_&YJxIXK zegTWVfLs3x$WlO#0`i1ep@7xCGWQGEz~%q}8(P3)Z1GL?(crE|A*ATO@DR|ss(ArL P_l6+k=O(bNy>|Wrdn*@` literal 0 HcmV?d00001 diff --git a/target/classes/com/muyu/common/core/exception/file/InvalidExtensionException$InvalidFlashExtensionException.class b/target/classes/com/muyu/common/core/exception/file/InvalidExtensionException$InvalidFlashExtensionException.class new file mode 100644 index 0000000000000000000000000000000000000000..f6f4d02708a176cfde30496299b0e5a53f63d4c2 GIT binary patch literal 841 zcmcIiT~8B16g{^s^ur<*{Qe*@@j*dZeHHMfLaeF81ndjOWN0sR%X^oMqsE1g)K{%!j(g9>LOu_OC`_a&2o7US&lPw0qub z-lYqLl4pAK0$Xq&bv{Q99JkR#i}4bx3fjyhK2YYDiR_}rA80JEJbv;O@@3LB&ow@0 z;3;{Yp~GzXS0AomffY|+5m(6-vun6M>2BZ#vnp;9ovX3|+yX%myv4ObId>+pg1d}S JSQL5>&7Xo|<*)z% literal 0 HcmV?d00001 diff --git a/target/classes/com/muyu/common/core/exception/file/InvalidExtensionException$InvalidImageExtensionException.class b/target/classes/com/muyu/common/core/exception/file/InvalidExtensionException$InvalidImageExtensionException.class new file mode 100644 index 0000000000000000000000000000000000000000..cb0a78ffa7e6fedaa90a187e69857c1960a46814 GIT binary patch literal 841 zcmcIi$!-%t5PfAk@q$B4!oEa8;*f-8?u2kLK^U0`DZv*IQpYZwMm^nVrYD5Y;)KM3 z58$H^uT3ltBoY#0M(V11UH$ZG`TpbUHvrpM>!5~u6OAqwuqfCbhdj%xPgO=I51yvb zOpnLM_boNRutZ^ePQUR|9PXzK@u+$H}jK;^2ag{ExMd=ekqaP+J zSRPuhyH$Rm#lAUkL{^4jOddlv#wTDS9@ep| z)v-sZ17T)+w=zQ8Ijk}G+aaCw&9!WDXTTqU|xX99Qyf*^Q}XN_`h%xn!eX_K%d I^cLDb0Y~EHe*gdg literal 0 HcmV?d00001 diff --git a/target/classes/com/muyu/common/core/exception/file/InvalidExtensionException$InvalidMediaExtensionException.class b/target/classes/com/muyu/common/core/exception/file/InvalidExtensionException$InvalidMediaExtensionException.class new file mode 100644 index 0000000000000000000000000000000000000000..eceffbfc7cb373131827ca8ac8c4de59ea1d52c2 GIT binary patch literal 841 zcmcIi$!-%t5PfAk@q$B4!oEa8;s9ZpD-goP1YzVvND00KA$9B`Y1Gq=W_m*SEKW!q z_y9f%@!G`VKq4U_W~8pF*VRw2mhV5legm+Ml@4mCH__-~4)cQbamcg0`c!3f^5AI- z&GcxjAEIUW&N|Ho{?Ir(**J>oOQIXIYr*^z>ur20czks)=H#sZnZE8FSlUJr-7}+2}XFF8ISf4XTuyX47A0%ROZD|=_gfa@c1Mjuy zJ5!cQp4rn=#)6w@avj-lTtyol+B1x5=+cw4p!ClY*+GLp&|1BF@BSCa=V{g=&$Q0K zL-IUAkKXF9Jed literal 0 HcmV?d00001 diff --git a/target/classes/com/muyu/common/core/exception/file/InvalidExtensionException$InvalidVideoExtensionException.class b/target/classes/com/muyu/common/core/exception/file/InvalidExtensionException$InvalidVideoExtensionException.class new file mode 100644 index 0000000000000000000000000000000000000000..4b9d66cd04ba4875e6e9ef74a0da33f0a8475211 GIT binary patch literal 841 zcmcIi+iDa+5IwcK$zI%;HSvB42tJs2nRjDEStG8jK}g&eMd<8Sl9rxsndyn~vwRYK z@B{oP@${O-2NMv)8R)7yU41xp`TpbUHvk)0>7a&s6OAtBFfZ5`hdj%xPgO=I51yvb zOpnI;AzFre)@e5IhsN2-=228%65X6#3+5kMZ{s7ugWGS0@6DmfobmhFb}X#lU;i6q zb<{*l@b;gC+&Y#xvJ(v_>FkwSSmQ=oqykZ{=T4}J}&S-VD%$AKo?%o&m!6~rrGJ*l4jTM{*4mxB_r5?rPqP+zrga7$ zljjL~^wxgm;T)D2@dcJ~o?OYhfQ!@Y7B11N<1*2OIupPv5Cp+1JZqG5ZE9<{PMd@! Ip*PU}3A%#i>i_@% literal 0 HcmV?d00001 diff --git a/target/classes/com/muyu/common/core/exception/file/InvalidExtensionException.class b/target/classes/com/muyu/common/core/exception/file/InvalidExtensionException.class new file mode 100644 index 0000000000000000000000000000000000000000..a39a7b86f8c710f98c11714c51b00d2f5bf863e3 GIT binary patch literal 2293 zcmb_d?NZxT5IxrzWE@cwK0-+vLZ&3K4Mc64CKTt3IH55$NoPphA8CJF>|0QfC69DX z%A52t+Le299+1tQWg)R9%+}eX7{7G{x8%6XVDV z%r)hKDtm5S>K|;q&sCtMs~LDJKj`@(ngqZsOiUQ~#6%900(a|P(`vSl+7=g0&!zB{ zrGBZambSV5*>;q*$;Ydf74DQXME%snJT3~1t8N^DsljT6rk|O(9GYgrW4f}b z*w#S#wsdOB4>NdGc_JXT1SY!Os7a@-M%lISBLvppYjh`#L9{_lp(cWh!hJ`X*84Vx;bHZoYFXkLx0T-a8sAHwN7g(ki@B%t#z2W@ z^M!;W3z)P%>5{-qK4H)V&e19yow%^s9k1=zRj3eH7~1Na;jN+Xa~0@q6_zA0@Rh)i zA9d)Z4qnY}DDNcmlle06^;scotA;I;5bmB8!lomG{Un5M&I+N@lzVCrfrInd^K_tn z*@`D9$c^D%mgM-rz{8Nm9tkY2~Hs5PKYuIX2;g5l)+k!f_J0hy2>xJNrB?U>Q4HQ$?F*m8JwQMkiPc}hP2H%SCWct;5&xt1RimS)C^m`QG8lM;V&kKU)FpU|0=Lxew?R9F4aVXqCo;wDOQV`K9 zOd=Jn!Zb!u;Cf<&24D#AFMv$W0Ly%ad0CX9<7n{@od1oB6hic|NI4&&nXy3!O<)D9 z)SPynJMDZq(fMYg^QF_yR}-AmOg7B#3w%kLWeslOHtt{z>)dCUQ;EL~l({Ridbe1; ZC%mV(DW6i_q1>cgqx_cg8MaWx{C`b-eeVDO literal 0 HcmV?d00001 diff --git a/target/classes/com/muyu/common/core/exception/job/TaskException$Code.class b/target/classes/com/muyu/common/core/exception/job/TaskException$Code.class new file mode 100644 index 0000000000000000000000000000000000000000..8cb92f689a320878dc4daebf88e9c25f14c9ae8f GIT binary patch literal 1622 zcmbVMTT|0O6#lliq#;PHih_5o+Tx`mAZigCAZUl0I!&o|eCUJ{G1w-Wri{M%leEqV zqod=qKg#j!wv2Vu2b-DX^Zwb&V>>8ItnBz!w- zzi!ws1Bc+Yw~?(_?HBsKCbjHs+Ho*MR74SD7^!IHT3vrqFe@g*d~qnk9A}hO#5v;u zqf{Rt<)#r8DmNV#daYR2wUt`ktZ3zmzQP?2s2Jl8(z>x`lpY&gcTh!=>(u3vv0Aua z*URNnnHvtPIKmA_1)EV?(d$O3QrD_lp{Oktb?$Ri#W73}VXCSX*L5oG*3%(tQ*az7 zBuuC{iBk;GM$>a`T17fj+_rYCY}0bLvZak}yAk9RoWWTM=Tw|Wipu)spu3$GEoaAS zcI?tK+PU;V^m5Z`w{rsuGw>aED>s;d6A1o`DlQ?-5L<9uCs7BW>CQH45j zQAS*bDq~DWQpORkn^tiHGrT)uJ#B_N>CCYGp_S~jMEL1q7%MuiZFE{2wqLO}nxt9# z%Mvp~?gvA%=rycn)$$$A-cJ#6trlfU{@F!_#6vrH?rlD@eDVowp9+oy&m9s<%BH@i zdaQ6>-Y_O)#1HL z{x-1J_z|MShTRSxcR$$u6z#4|_e97+BtU5tOk2F}%KlGdbEz+nxFWkl*LOFV)47cBMSDFEY1=%#oFqx#O%O`em;XVES zi%G%)uR<`zL%5p&7U{hwLh=cPj|HXVB`qOBUtlT``vO)AcM&#gkuD^&7VXlG)nZ+Y znl-75gjtijIAs1=vJ4IRKHsZ8-}+}_OJ>5mnEHUQ0GA^INCI4m3ZMvZH73BQ0M{e| z5&~pp0S@iq`Ui~jntR<=sCLkeWnhvL_6l3VGU-BCfiC1K9+2c00mCZh@)JJ+w3cz- literal 0 HcmV?d00001 diff --git a/target/classes/com/muyu/common/core/exception/job/TaskException.class b/target/classes/com/muyu/common/core/exception/job/TaskException.class new file mode 100644 index 0000000000000000000000000000000000000000..22cf2c7b1556f11c58c9dd8dab2428c60e108e1e GIT binary patch literal 1205 zcmbVLU2hUm5IqA+mye~b#ain}TeT`|t7}Y*F{m-YnzW&fNs%XCE^q@|*OZvQob4hZE&6skmoNm~p@GRLaxj|k0+c+MwRtQm>m0)pp^`KMR+{(v~@x@P<`EYs_EpiS%X8F45Hp!5sN@3tOOw+ zN|ngo-YH@ATuFLi+|cgJWX%3qwKg-O@x~B}$I&S6$~V3l`oAT8sHqZ4M@;2VYHgH_ zdxR%*$xU$f!%)VJp-@UH+~O_D^POgJ4a>L$*2Z`hj__)+o#j{#tL$H4?*JA@fV{i2 zOJ|^OCVP#4^&!oqlqnKLKzICJOyTJ%z z@B4JJZ^I#0`4}u0`&dJjeT}06))^6>r_OehnG+Nl0hOZ$(F=;tc~*n1&Dr2eiz~Ns Ql;@#|ZH@>#Yzx@^1dCjaD*ylh literal 0 HcmV?d00001 diff --git a/target/classes/com/muyu/common/core/exception/user/UserException.class b/target/classes/com/muyu/common/core/exception/user/UserException.class new file mode 100644 index 0000000000000000000000000000000000000000..580c625eadce4ca37475f48b5dbd2820351d9cd9 GIT binary patch literal 679 zcmah{%TB^T6g`8y1wnkHCc48%Y>Z3Pg{X<9J`#kDt7(~F32jX~HTqeuOkDT@ew6VP zF~LA$7x$if=FZ$Rr}O#s{sG_!>scfOl2NFFJd#LdkS-vDtiZnQyM`ORLa zJU2n5K>14P2fuSG1L-PF69n>2KMHJh?hK^2>Vc0~D&wy^AifI&>i>wluArgET zW#U;fCQ_JU&Tm)5&aQaF)a#(H5_9YUcjQpQ9MQ&4C+4xhDqdg25>JcKGFI4KCC(*C MgG4-hl`(;}Z(rK4@&Et; literal 0 HcmV?d00001 diff --git a/target/classes/com/muyu/common/core/exception/user/UserPasswordNotMatchException.class b/target/classes/com/muyu/common/core/exception/user/UserPasswordNotMatchException.class new file mode 100644 index 0000000000000000000000000000000000000000..f5c7b0f7ff3b3552234105b9ed0ba1ed5de7d4f4 GIT binary patch literal 560 zcmbtR%TB^j5Iw`gVg*HfVBF~nT(oiH1~oCLi3ZUG>BiNi+(1ctFKKUme3mN{7k+>r zWtwk1RQ2v+&-I&ABwPa9H_vY5*m%pL_7*N6l&!5OdP0D z>tTVq9ZaNeT20S_O*s4OsJpr7t~NKB&kIJwlu8_53GT`9oGh4ekPzCPf9q`#?4C|z zUtR{;xczLAzy8k2c8NVK^MzPEcCmso`wB-ntTH0LS(WWNGe^iX0!jxpqBrDUXIVA2 YHfMt?Ew0>{p;;a_vBeQ#n{5vDZ&UA=L;wH) literal 0 HcmV?d00001 diff --git a/target/classes/com/muyu/common/core/feign/FeginConfig.class b/target/classes/com/muyu/common/core/feign/FeginConfig.class new file mode 100644 index 0000000000000000000000000000000000000000..3d466820b66cf79863933d47227010a3dbb2ec56 GIT binary patch literal 647 zcma)3%SuBr5IyPJT5YZGr5mY&23)w&3M#m|DyS4(rq}eE+MAR#*T-M!s-WNp_)+5I z*7^V!W-*hQoO5O-^Y;Gy3ZRLlGBU^(k*gq&0z<7UZUyfN9r4q4SB8$Eu&cCkdkope zTC0c>L(|xZ5BgT=sAEMe?~J|SVb3I2-1MdHNEK;5Ncw$a9Y0TT$G733(aws{l~ME+ zCmB{l6Z1HENH|?$qsdvxNAx2ZDNU4)iWqW-=1MYD1Eu9j61Szj5bYji>VXMGuO+PV z@nJ*Cf6Wc@QRfoZap5dc-xC}NmFokTo&D-{DuFDPCPG4V*b48Hj@Klr6j{w!g< P`do^gPwfK6*+t+3BMGCW literal 0 HcmV?d00001 diff --git a/target/classes/com/muyu/common/core/text/CharsetKit.class b/target/classes/com/muyu/common/core/text/CharsetKit.class new file mode 100644 index 0000000000000000000000000000000000000000..7a6abf0d3a72da99010c4ea47c105c3e335f62c2 GIT binary patch literal 1890 zcma)7ZBrXn6n<`A*d%NM#P-u&JgEVT}Aka!8w3Gm~0o2wRW=XcNHQBA3O-Fu* zpXv|LuT^I#qci>hf0W~McbBl8CXOF6_ndp5`9qN z1V*20FSTMzGY^Z~2ha6}BQUgNn1*v#Ae_nWB@jbgK|;kaRDs!s)h@QX$K4{0wqT}o9?}A1F2ppfV&h1& zJGxV>daFRHsXx=YEvL6Jk;!_Dec+3d_kxOxxWpWuS@xFJ)&=JKA-&)u&30xi=O;TT1aYOP(cX!tI%TmmbRAi9lROsJ#HQCv>`svLK z`>7!R)-ZYLCXmCNig^?TVu!j@Id*hXO#1rtYhd6+EhT6>ypNZ;R#I7bFGoUQ$zsa%v}dG?Z$oQ=kJo7xMXX>?O}j;!V* z&8BXSZj<%lz{w4k@NQ9kCY2-8vATAHbtF1=qxXD=n|kN`F(*669Y=5XPGd`rmjC%l z)ZOcAhLmE&NARW1%bDl+kMT0#krV|xBkrA%okso1$EMe{?b9u5B_&dc1l02B^ zvP*u|1L8}UTqgh6)k9i!S|K+^GE4w?1a5kjfV?p1wmNf5$?4 f$zAWJSKPHK`|n*(xk^m(`V2-f%iTSmh49tC?jV~3 literal 0 HcmV?d00001 diff --git a/target/classes/com/muyu/common/core/text/Convert.class b/target/classes/com/muyu/common/core/text/Convert.class new file mode 100644 index 0000000000000000000000000000000000000000..4d7da15c95b7cbdc87e5de8eda52a1101b9c9a0e GIT binary patch literal 15101 zcmbtb33yc1^*`smnR%1RO9%-GL|K(3%fvub3Skivh(H4>VKJcMBuv6cG82;tidq## zt*v59MZvYALaSX;YeNK4s9RlH)Yk5;wkp=OZvR%TOMmCS_h#NqCd99QzHjpGyYJp} z&-tBm&vM`6sn7R5L`1W+i2>3`H^^g>mwZfl=Y%$dD%(QQ*2+_w&IvcinS3)N(MWtY zlU`D~!XSUjpoVy7B-$!O0w!gX$>fj6tWKt3C8hOAdRFg@0OisUgYrxoO8HFVnq%#i z?cE!@E753=MRDm2SH{EV$17`N(GB5FaNt!WnWmMv$XV>oaP`;=WLVRnVN3-{R%+LU zIz!FzaA$yuXt+TmOe&_Mm?maqraK;K>#DS}pvfjp zp(=>jgkeb$YXVeGCx{>?0``&`_fkR-oR`yAOqxd1F}EYs*%g-V0l=&=Xhw?m=AYjj z?ubWXQ8A@iCe5ak6aW^D6OIgkTE8Dd2F+y}k`%jPU92-M64shDkLEK4x}+y5VncxD z(8(g=0D0(nq$E|F%u;4utg}4?^4YE7_`+ycJQQt)I*xMoZ@-a{ zZaJ+s=?pp(+fxaH-Q(uX*$S@xX)UG9c8y7AQ%E@rt9Q`NZBKTKW_KCX%rrEqyM@ts zxYe@Hut_b{ie(~Em+lq@sEHz?yK?{qHlB+|doq}8qBfJ-DT>)jK0FyfRiKwk9VVSi z*njXk+EonU(bW7S4G7vRrcZxf$Ym0@HJN>>%_t3r2M~n0%8LbP@F8ZM``Xh0tb#9t! zGWgs@JtqBx?sL4S>K_0x(95O!P5K%2D(`8J9mIQDEA?O7puQ9jQxl7|g+mBG2JJOz zpLlq+na&G$)`q&E5`*@e^pNsA>q1@YYGZ4|embDyK(~7QbCVvSM`7XNbGt*5P^6?0 zw!0`@<)_DyGi(fZ`RNx-T5P?ao`P$b;-_b@+gdOabbo~>(U_l}#goYa+C$F^j=vVy zw#qGw%{7qBr58;4E&a}lnh++HnC_cnogDe#|t-&1wo~>6C2#(!0eO{98QH zN0N~)Duv_OF5Y1){YRMlRG4#|h%?uAVgRp+VB1^i3lM-Qs+1N4a#=H3XOF|JJ^ssZ z>yS?4*6Ge-9~%byP0r!~)A)?ZyH!abHSP+>zbe5gr*^^Ir493!ty#XTZp}0#IhGYf zBe67D>5sd908g$5d-GojSrU*D|r4X*Ga#tjXhf0yg{Fa7(DWEpF?E zX{6&{G7*Cd=1F3QCGe9FQY_ivGU+L28gpc^R|K$bg~`WpCH%nJa5LODQ!ukYHrv94 zux0@f4g3}2mpU6{HzSvMxo93}>C0nYI^0Tr5=9mc~NHxvm>0GTs zkSt**Ii8Zz)wS6?lV=$`+vJlZ+TJKIhfcV&E4ff7bYU5jMp?c5n4_JsPim{YpPCLF4tCd;NT>^xXM?pO?y*`+NMn zOnR={anwUhC&oXuQ4yJ!hbC_;wIHOB&Z+M2(ov#K<@L~s; z{ogiutDxp>X^X`=^NIL+leh5=OvBql>%%z0X%59#M&ihUR9P7ZlqttJDWK*m0%hVy-ute6y?-=nQwXg_^^2+mK8fe5=W~N%G}u3rAby>->!DS3kV% z{(NHIDeT?Jv?zm9wE%Lj*uN*kCiL^&*nJjTWngZ+pOOA*XW{yNY`WwF7^<*$ugO0U zu1%Zkx=3eN+|NG(if4gKOzS~;0YVs$dzhLekVu#6Bhj!`^e&V9h$+852JNm0bw=dg z?)1dhMS$|L^+$+@*2x-225p~f=jrY-wvr%TLp;>HesQS7UOjJd zIKD2n_Ea3OwTE%S22N7W1#IFOU_=t^VnSS*I-&!Ue#In~WGZ#5Edwq0GzLO$l8JL8 zkj!%}rNScehe1f&!-l$MTE{I+69$>;jB_JsDH5y<22WyOfoo+aB}^-}Ga!;2S>oo> z*=268njD%UP^O`(!bX<20lx@-3&l@%LP4HLRQC9=Omq!?E){v3idRZ$OiD>s)H8!$ z7+f(n6*stcX*xK#iGgmlT6N&yYIVlB72{M&gDWOEQg7clKNT2NGhlZ%w}mda0Km93 zZ!?b0r`~UM`FPrz}+yW*Xp4lC4l4e@Ei1KJ3!*w9eeZPSj1%T(|y zX(G`LvGrm5^_7*6)q&AiC-T(oZhY;57SRQv=-Re$*ZBHaY<+jf440Bp)Zkvhnz#&( zD8q0!0;yZjO`*Tx{QF#eIbFdD`Anj9{Ly9^C4&nEN}G86BH zHlAtpk%uBxv;?JbI9<_H)?0xi47?IW%J>u9SK8Xj^MIg1LSM-XoB++R69fLze zVdfZlC#dKb*))2255@?!K7Ih~?M2%|nvu=>(8|)d7g>0pG-)V5XtOXGlZ91Z86M@x zf^Gq(xCHRAT0jx=vJe9yykoQpT@OMi0BLekmI6zbf*wVCaj=gHcTk|bmyX^|{-EAV zWBTaWq>M)aHKLOs;~dCX0~u=}V;$bWo~+myYtwh+oD4blgZW|_&JRIiO-XE$$H@Ma z3Jfylaev86`Qc2GPfC}3**h@aGgS-(MPwRnv8{V?EV^#-d zol7(Enn~=f3^Qhew*!1oAvW7moNx%levWm@@Y*|x*bwsivWI?3xq$3sgR1+3Hk%*G z1lr7W&@LSeG`zI52|aIvHa7v<91FBLvI~-+)$~zaKhSz0hc-fnZ+ZLd3 z9dP?LaN7#pt_N=0fZL6bemij60o-oEtXrYS+Y-2mP95ClCUBbz+#Xf9xh>6&+oFuP zE#=2D;dW{|Za)|dx3ei^6T08VEttR!Sc%7|NJpuUmIZrhl?~B2D#2!TAS$B(%waDe z+6RdC1EPlj(E%Dk2jO6T4hQoXMn4Xyo=89=8{L5>n1Ch-XdVYNBT`H%mpndf!htR7 zYmnQ>T)XRYD6~J{EaJ>e& zUWY}!0gHMIFue_ndIuKu9xUp8Skwmz*ao(!U;GaibuK@d3Ab1pZitJ?$dl1=pT|$x zgr>lvBmx-uU1fdLolePgQ8I9Z`v8K7#1n*o99**KLVh|UrH$#7h7U#wZ?tVnhv2v* z7|beHF~hI#r>35Agv75c4nBnT5`C(t&=Y)UCyfjida86Nu8(5X-iks`vA1Efhj)MR za)qpKdD4ksllWL1v5dz%xrvC&lMp3K@S#m9RdN}=Mk&FkBtgaBY%E-fFP@&^Umy}4 zO=J0&{3~c4%+Mtwy|fe#IA)=8KEzD`a0E@@XZbm-Cs8+#bn|mqKZgu{ok+v1P4J%DTxV>!zfv#Ud-h^yMjn9_}aTGzdB!g3f@TGa=|K2s#_z@0^5B za%Ryqo|_Ofs097B5_D8T(CUPs)d@jM5`rGXzu^}$2>M$HIwD=&8Q?e)d?u9eyDFq}3wgK|$VVuL&w-BGsF>T4%*F7*RtHVt zbLn{Q#>Yh)s18TZPDrl)fJ6r0M4Dj-FFt2cF zC8Hz3w*dpfx7~todn$Z4_0erdgzpX+Ffe@I0esg3z8e7Fjeu_l;JXR%-2(V-1$?&w zzMX*YPQZ89mw@m0{{wt?@gFk5w=*5S`v!v#A0pU<`p2(b7JR!>;rm`6{U9AaD}Mc0 zo;bMm%ZT^@a61Ux9szET0=LJ3+Y`X;N#OPraC;iK{R%naA$&IXYy!7|On&O;Jw)F^nLsc z{3hdf3w~4ZixK4<#PwQS|BUO|#LvHm>uEH#dkd~L-lvO?#r5Kf{D*t#;0_8QAnm5r z75R^;+oc}4t*^*`LfzJS<#tv@{*&tV1fSd398I}SzV{g| zMIFJp>Yo|(cx;dBsy=_qf5~J7zY%U-)u)y>m=WNE3s zB^XMkH3b;H2!=;ffx=MdzoS=>wrISJmNoMgT&?jhgQlbwl3Cle+I+{c=ji+rHh^8; z^Q#!9@nPgVuk&j*K8rz8BBoI$K)jZ;3w2PT%}`QUA=5~3Y*-vC?yN%$c?O;wl{%|h zEB*s3;_DNvEgE?iD+&N7CAQ_8*0y|8+!+F^RK=Tm>FqvxCw-&tqc_VNDPKE=Y;R}KAMO30)46DC{sTVY)KffcVY-$6b0(uA2vXIf|+dRR()Hcg1EAQhW8E9GgfVI)G zELw`hKT%mEPOCr_9$Ha`L8$G7eBPtHez!VaW6^T*sPG^c-b8`2gFJi()!>3-HnZ#i z7lMi69yErdYMMRjL4UPJpXx32j@n6u#hyZMu+Ur8%OjU<_M%>Tv)E$|Kggr3>7#Hz zChZ)u4yTu-JxpHh02$gNl&d{T1=?dYPJ0}t@dP&BlQdO(il%E%(`@Y-I$8TAHQ?H) z9ip?f=crYCo;tMOC`83+&P4!{`pQdFk%hd=BLLrYn#8xEPe(0%48O-kYDaV@(CX&* zIbfe}9z%X3J7>rjG=zKx&N?~I;Nfg=Ar$%v4gZLILmX!%sJ?Hb`hl%4Sf|og5%eX8 zlUaRy%x*F)dq? zvq#B!#5^v)n+nT=qfWrt_5(aaqaEbiO_R`(H}<<^21gB@Ov`suk#t=!v#*#?4(vm~9K1P);?=P0}fA~{KE}4fF+gI|({F%M2fc9sEPbfdwk!tV6KJGeM zT0VWMQE15SXp!t?;vU{u?UzMOs!cEtfs$ii}wU@s`$ewQm`Ci_k?y7y84eXyC#lBv?#j}$j+U?ao zF!8M|Qw@EpztCUo(W}5jw9vSXBEndfJ~dDnDE4@yyJBMQjknRHib5kV&`~9wg@LN# zti5~(QFWH|woNrQ2e8DUVu*Q{wR8dRD&WQ7L5LUntBQTr1m9+S?DfT#VsA0P_+9}w zV~E4=J*pv-3iyWw{9{Lgq|a-?(K6}lXs8~cmHIifO>d)JdOO{#$EZi|puKu0J*s!n zAw5nn>fLl$-@wE54j!eS&lB~HT&`cpRr=R>o_;Ye)Gy&P^h>!*-^3T7eW`vq--T!Q z=+@cJcErv{5u|lwWPUnC3p5W}S@a@J)4XT}aNs>f^Jxa^qMzb_Mu}H3xSmeX{AdMu zA+6T3VEu57)UBTS#YVLNW)R*qKA~l+Bhr<;2_Jkn!6R+sS}jMDRCgCwX+tpAOFdkI zdhY?$rd6~{8>-fjdZ&uAKBqg>NovT@@@o)MEq*6gFY6~HhXadn76mbZ@W3k&xU1jOBRSn#j!ZA!H8PHX_o8W;&Sio6U})Bd1~jVh`EdY# zfzz2nK9VoqFGl#2FIfE8Mx^MNxwS~zu}y3FhWr;$&9zv5)wwUA1ru*$lrbZro6>Ky zbb3&rEqJR6QclB#w4hbRAwLd7dnePf7iO9>tYv4#%`~p!h`^EmhZ-xIxvXLP(1vzd z@_PbGFdmv!(Xn4OjZ)O71qQ3J(G}fJ8($U;D`SkrbasJa3H!cHX6N}PK>4}1w^!-t8iPw7RF2J)CpV-l7wg{ZiHz ziyZa#n!r2lBRea)mCYI1RAJ3@%mM>7r5!!1t(JFbIHbUNc ze8=b^!mr@h6MhNtz3Xy)k@r zL%MnqE+KH~C$!CX1P1QB#L=nxA*FuEt9g50J;n2I>v2sfp>xQidFr0vsHQxUMtxyT z=_uj-xxNy5UTB`j$b>bIk}aV(aQg{n`Ts4>QsvoEbxV^ngjT(pcVG*V5(ejpxih3Z z!{try_B)usMhU|_R@~#x@|hd6#(L8$o^7WjLRPbz7t5`Oq{w1NfKD{yBmy`Eja%D= z9(41D>PHyE=w)Zlko`WA6~PoQo-{8V1Lv@gA#CD2z9#A+`}qw!`W-v^oEBRc!_Tz( z7327gzWyNXf0A<9ib>KvjHgH-N$Pv>1HC5MEVcmC^dX-&NZ}e=rr~#7=V=|YddoljyL>k*lq!~vV zXDM`{l+aRWOA55KWiLzFYGRtW38l0wg;Mr?U&A@@1AhXC@6L>*v8@x3kLEqSci(sS zd*Aq#|6Kk$fa`HJh7jshgmu&-qF}9E98DPGMrO=RWb8u0wn~Y-VU2E1IJttE=rtVk zpp(m&6hyY?tekU$g1YvOAr+c}#^c5*BQe2kmo5esby3WNuA)IlBjzhuL(D{B;_QSE z&Y@_!M@m8L*f2jwQmG@oy$a$3F0M$CNIS)xHQF5`>H-~cEL1QrOPK~K+&=rXg0}X# z)E#q|M65+RT12LWRc1$$N#UAK%l={=OJu)M_EV|e-efYBN~eYQG99nMYq>RU6icQU zWzeQ*3bxi@?{Tk~rMreWDpn|1>Z<9OXPiVjR~XN`xO?ql!Ej<&j#c8J)e08XV9_xo z6l-)`hclG}n3cX)!vFA+29CvcI zrD9`QB^Twuge4JF*^glZUN0)WfgnMZ2FHr_Y2#Sl?Cu!SuvtOuEI)}s8#KV+VFBXsQP1O~;O# z8Hc2f1Oww#L34WzJPEGdI(oq1Zg4HW-*6nWXfdl6-a1z#iMYKw-h!lpXeHtlbOp`k zXGV?Va~u~#KWoMePbHL~#7T2ZM+R91tp($R*<)K7!?`)98?QZe9Wj%z&LS@@_G4HI5jL05i;eKL=5K`6^4<@;TxcQBvXPn82wS_<~lUV<+1{hc8wqeuA%M`gm)^~6J%SlQ@58Zt`Vx|=Aczfqv2gtL=st2@ZCD@llmT( z`mW(Uj1$?~kV?t!dv&}|c2(JBQ_%2`Uy4YuA)D3ke%60EAVndWEEGy54UbS+AA~e3 zlgVv`!nP9K@xgMy1WKxgN4YwiT*HTb5U=J{Kr}qzlLt36e8d-=s~SG$3%N0s+9*PP z!pHJ{2e3ZnV+A)feA>t2Dh<1j3h;NaPDvj%za zvM@6`gAhGNKA+AxX19hvG9Z|*Cu6ndHP}%%W{Yq3)#Uu1Z;}I8Gagn1TS4a>28C>wuPZGV3 z=&b?twUenU*f6~Q5)v*{)`;`T+BA#cfyUG@lDg%Qq@-8#eoCRcug)xp~gGdv}-}} zKsl~zK)tyJ)FDFMS_3M|mxFdePi|D?9UDMbzIiO5Z-Uzi=P%%)8 zd(}RMFiTnlBitEfP>xj`6epG#O7v{TWnAGX=1~!f^q1|!kh;vZD85!ntDQs?DdN-U zdk!}#*daz*ehCL>n`fNoPF8e^^4If_|7Aq+0=_{EiT7W+QUg1iam%h|9O?=;<8W7m zy4}Y3y3Op;TC|mgt{9&uTf&7d-J6_ji4?jTTVgGGOXFo2V1Si#WHl`b+(9YtB&WL= z5ce=8?!^k6Lp!U{7Chj}beOy3ix*v&$?PT0*f06k_)W?g;(iyt#aWmboA7PUB1Bq? z?{KCOZxvqVEJoDD_%3HUv2}cpv&M38y+{@>@f*b}_&y;;O?oJY<-C1>7Gea@jEXAM82UA&U}FgeR78^3o7 z=i>KI;eq&rQ;5YMoEUqve0sP({-N}6Bz}K-SdDK=4{Pzq z(!))W_~U%4@hADz;veNR8vi(-vG^zXoELv8J%wFTs=RW(l>DW0)KkD17BSIV8PQAl iw~UJXoO>bsf{`YsQ}8Q(|A0U7SB$ei$s0!rBJgrjsV3f5kuGE4~0D zGrl_GZ(`@TJw{QmRfCxBIKst6&hAfh3PK8E1~{)y`~-l*uW z_YQ>NF!ZgM4bxd;2&Xe;1u=#ZFUYj?ZO1knm3Ox32wR0Bfd&|oZTA*=owip+TyhN3 z@pQ2r;X4cOGi4RSxS}ATA&HSThNokvYFmeVuO=9Vni6r_;l{@un;T>qyTIyVD@Z&A zDGgUK#t?HXU#aBI_~ah6Z!M^pz@&m}8m?oCVac*9I&X5LDs;oD*R4j&%gC}WJ{zLx zm{vpIY_HxrX064bRfO}#vYZX>wn$b!kkxyELN|BkT7?*97$6fcr{RVatP)yDv|79( zVz|W+X}GnTiUp(Y z+m>q^;-x8PC(&V$eX%Gf5VOeB_{&ciOuhiuF_?;NE^-gL%?C*l>_MajNO${59I iSi@72u}=0RBIk%J7+$BJa*hE7&$=ZuY*17PFa84KfmfdZ literal 0 HcmV?d00001 diff --git a/target/classes/com/muyu/common/core/utils/JwtUtils.class b/target/classes/com/muyu/common/core/utils/JwtUtils.class new file mode 100644 index 0000000000000000000000000000000000000000..cc4638de67d1ca8a5d614f8ed898e81dc5f79ed6 GIT binary patch literal 4145 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 literal 0 HcmV?d00001 diff --git a/target/classes/com/muyu/common/core/utils/ServletUtils.class b/target/classes/com/muyu/common/core/utils/ServletUtils.class new file mode 100644 index 0000000000000000000000000000000000000000..d1f3562b90a16270d4d705e1826bf6ab62b37f38 GIT binary patch literal 10794 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 literal 0 HcmV?d00001 diff --git a/target/classes/com/muyu/common/core/utils/bean/BeanUtils.class b/target/classes/com/muyu/common/core/utils/bean/BeanUtils.class new file mode 100644 index 0000000000000000000000000000000000000000..c584da9113f6bfc75a48f2b2dc1838779141b8bb GIT binary patch literal 2817 zcma)8TXz#x7~LngNyD^+LJMs{xk;N|h)^gZ+?Y&&auFZ= z6Z+)xg%=-0*9xd>@vQzJ^-0`kGEF7~gsvv%oH^(0@7w#kO#b}$*B1cJ;;Rrm@G9`B z@FO5_sA#T5%O%q&tlc!VyneT6-il^)tx%3m^LxoMa%F+Qgi$c8NdfObBppH!Z3;pv zwm}t$WQ+M|zOq?~a*{6=IGB30A}`uNZkv9y^mizVH(44uIJ19KbN9W9#E3Tx4Y z%!Zz|##^66(h52Rb~cX2?q&6oWfTh`v?Huyhvc$d(y)@2mc5lSwXCjS=hh|ZPISsN zwaq!BY=zJXZwTGktzeIe9`p)?8{joR1cGb2HIviI^mFGx#O0R#a6E*)*r(t<74Ks| ztv3LiOCjEm>(+X4l?eL=BG+9IrhYR=veDZ9cpEi6sNxU~3v5fqQY(q6R4TSOFVIuF zWD_v;HT_;Rp;?x07HIzn`V|bQh+t4)Ps4sQxPU@O<{4apT}?camW(@rhB2bxsEQ~) z6xeM8wHhQ&UbC|6x+yT~j@!(-W+wI96}^zv#~qzq*W{jKs zG9`!c3X&RCC8(1t?=r}MDN0kK~k5sRWI7ZZke|d z#8r?onP^_*UstDFdCy2QU)co&dR-1S7b#J^#mojBWtiM9gj>xiH8{H5=g#~V!0zTy zuv{GS!>?dYyxFbEaRn5=EorP3G^=7V2bGnNqw=IXFJt*^X@dtPKX%&m|ZEa zye^!yclo|m2+IPV{ITYIaQ5WRakln}Y|eS5g_6aJnbj}Yb=6US=^d4&hy&~e9+~mz z#S+pGxWbhOSGi_`;JjBhZH@u%@r}ywYg|P+3XcB4XAn<0@nG3G1a#m!=W6W$Zs0Sn z+VDA_cwdg1#H z>QGPM7mRyw(01(&*=Am35+ zVI2njKSItMxJBXVA#yfHc2wk1AjdE+pokI~+~&SiytZdL{QKa2$=82CLAQbl1!evV z#_hyEG$J+e3t|EmKOR)5%bmI|e>u7=y+xO?EnTKsbm^+;^6jg#{Lvyyf{rhePKtz= zNI#8PW@4P{Bt>b+(p#6Mzb?yCU6wfRIhoHV03Nn8P8~$&>k?vcl>?gywVh&y%)M&J9y2z4teyZZ*ix?1EKaKXIvf%I<(dN zw`)v>{J6hwggl&3V%Swg{w$1nq3=VL=Sk6FNRIH8mg|3qq&e|N1EbHvQ}iJ4!#>f} zXH{odvt}it+?65HL=T3-bOTTN<0j^3U>ntKYEf$%qA`!focU0B(+|Piraz5fTXO5D z#rq@iQj@k*z1fy65B>dDJm9V^?SNd#wm9&(3T%P0jdlio8GRS4XJDVAaFJFO2EDctt(jp0u4A2cA8`XW5swa^ zB1RTas%~taLyKXrc8+C+Q)Ip%_qKM1{5ORehr-mO+*KsW(&?QQk&p1JLkueMHf0eS z?%-~8X9@Rkp9)S=_5+mZwhF}y5fK%uPGTDr+oV_m><5Y(RbHcAoKSM8leHWpcn~n? Pm}0H^utnjAk@e^oKXy;f literal 0 HcmV?d00001 diff --git a/target/classes/com/muyu/common/core/utils/file/FileTypeUtils.class b/target/classes/com/muyu/common/core/utils/file/FileTypeUtils.class new file mode 100644 index 0000000000000000000000000000000000000000..53cd36acce77a7e82acfe9ead90b02d6df816893 GIT binary patch literal 2080 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 literal 0 HcmV?d00001 diff --git a/target/classes/com/muyu/common/core/utils/file/ImageUtils.class b/target/classes/com/muyu/common/core/utils/file/ImageUtils.class new file mode 100644 index 0000000000000000000000000000000000000000..d06a47a3bbb16007a168db6c454287f09c303e53 GIT binary patch literal 2348 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 SA{ECP_+UpR)OK|-75pD`tNRE5 literal 0 HcmV?d00001 diff --git a/target/classes/com/muyu/common/core/utils/ip/IpUtils.class b/target/classes/com/muyu/common/core/utils/ip/IpUtils.class new file mode 100644 index 0000000000000000000000000000000000000000..ffe720a7e5ff44f3311e8879c35933474b6d0cdd GIT binary patch literal 6889 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 literal 0 HcmV?d00001 diff --git a/target/classes/com/muyu/common/core/utils/poi/ExcelUtil.class b/target/classes/com/muyu/common/core/utils/poi/ExcelUtil.class new file mode 100644 index 0000000000000000000000000000000000000000..22c4a28dbef6ecc39049493256595524bd6f0978 GIT binary patch literal 40349 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 E5Aj%NQ2+n{ literal 0 HcmV?d00001 diff --git a/target/classes/com/muyu/common/core/utils/sql/SqlUtil.class b/target/classes/com/muyu/common/core/utils/sql/SqlUtil.class new file mode 100644 index 0000000000000000000000000000000000000000..7a03db649b2e2bbbe3000e3dc30cda806dc937ca GIT binary patch literal 1899 zcmah~TT@e46#jNdOb7>s018?p-XUCqcqW`N%0z5k(a4Gm9y@u5$(YbQa%K&LaAv)5XC?X|!4t-V%$ z`{$<@08V1i4IA>DuuE`Y3q#R$`G)M*WPQxvH+r2b7Q>b{RadQ340+WxLvFZ`?}S@I z0VIZlN;Kk+#HZqZ5+YHZ7K8iama3WlkY%X)*xN#8aI0o-)Cxpm))Yfob#U#Pj9Y8X z6*snGn-fJ6ivKUPIidMO6Pk$4P^_9mvZjXnj4(Gkr^xO^2}9kc(Dc7qc1qZVH;6%v>toh9!>-)KyT)ZB#3$lhS9ohpw+p)&8Z!`*3$v;D+o`2H z%lAJ>K770~b1!*!ep4X%M4DH^9_(c(2we=0^mhyn1_pW=JR3Q7V?Pc!aZth`!EjEk zGeuxp(K{)o+NKSTKVRam6`8cXl3|F*2CTepR^2Fv2Ms(oG0S3ux#0v zcoV8_a>Mc_Os-LbcoSh-E$+?wk#0m|-h>j3>$Iti8{R}jrI-=fv^Xi25!V$8DE4I1 z!(7p1gVZP~wTUq^IN3 zNHEZQZty$<>t-nIit45%>lXDxoaX`TC4;>y8YafJ1y!B*#v`NL7?ejfvUq|~Mb?I7 zLlt_~Xt%~yYS-%Errnci?40f+!iu=5$T7aweKZwvtFzkQIfcoZ=%J(uCg34UR^IKA zV_6hMJ=_|PhWlkhju5p5Au7D4Y9w9YQ}0EMFbxz!@meWZQ^>VslLzHQAA_q+(XzA5 z9g4;cg`ZUg6B5k~F%BBURH9zmN;472!%m#SX)v^tWwl9&PqL{sWn6*`=ZO`aLUiy~p1O%ug=(|x4FTvY`I_%52wE^8}&~K5S9k%ik zTp-E8$Sk1H%*{e+qE$d~`DRfyk9|)`&P~Oa6V#Orx-L|Z5M;9Yk_SKcyn_3*c3dbUj+E!D1Q>1vy&K|$|U9g$Sb1w}B z;4qvtX;Xi3BKlK%meqK9%8%(Q2L7L#W$`Y1ucLj~-vS15wR ziTzIW(~nW+Re#fwoQzz!_`1l4($r8rJuP#TOWPc(1UQy1g7_~5O6c?svJ8-HL+D>u CC+p?_ literal 0 HcmV?d00001 diff --git a/target/classes/com/muyu/common/core/utils/uuid/IdUtils.class b/target/classes/com/muyu/common/core/utils/uuid/IdUtils.class new file mode 100644 index 0000000000000000000000000000000000000000..1ccd3a3670df1416568bae779273d8e825f71052 GIT binary patch literal 729 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 literal 0 HcmV?d00001 diff --git a/target/classes/com/muyu/common/core/web/controller/BaseController$1.class b/target/classes/com/muyu/common/core/web/controller/BaseController$1.class new file mode 100644 index 0000000000000000000000000000000000000000..385b3215ffd0a6f66614b18f9b72b9f741ebf798 GIT binary patch literal 1048 zcmbVL+invv5Is(F+wDR_O3NJrO(7S=QiR|kP^k??AcayXB|Li*lWy6pBil>q8^6Q@ z7xBOc@KK2ICMvBIA<>oAGajEgbMg4s?;k$_JjGoH8O#-s^^ilJ;bC8n{84h4_@s2`tJQlh&SIf}l819BGgN10keD#i{xfdG8>=xm zW3IHY26Cx&(wGsChyLqeU-V6@J~3Ip)Qz&t;Jk+=EHf0fFg+e6x5x0fcA8ey z$urxQ10r7Ngt6F8MuAYfJc!6r?#MomdR&EeKQ(4;>lq%MB3gB=#ZY=B%$^+VaK%T$ z2xVJC)otiq@sT=xqP(tAVLWW5Nt*Pye5`TPQwFyy6V(^bL!0#C$!WP~8%@P+#r;TX zVw;37;5Ng9nK$>(;qy>Kog&(C*bL)=Pz-*pBh}E?W1H+dTh@V8@6#a;2;#pL0bM(v zz|&%i+d`WcQd7O&%QzOQ9dWINMuqOYNDqaTO7?w_mZ5(h7f4BRnf~&M6e9Fdfjr1Ht?o)}t*9knK-I&rkjplcp`HIEAtab{4 sTa?wxF=pq8*`b_o64{iwLf|aHZQ?7arn^<#K_j)SqDj&vxFx0U54Up)N&o-= literal 0 HcmV?d00001 diff --git a/target/classes/com/muyu/common/core/web/controller/BaseController.class b/target/classes/com/muyu/common/core/web/controller/BaseController.class new file mode 100644 index 0000000000000000000000000000000000000000..3908f09e32192a98cac7cf0911281038bef952f6 GIT binary patch literal 3578 zcmb_eTUQfT6#fn*j0q!BRFq1!TGT+)sYtyAsSw3t!=)0jYHyR|5C$eQcrp>O7u$#a zh`#jI*Y=^vs%!NJ^hb4d@0l@#T)M)tSu1nSoH_g3``de;z0cqO{P`Dv8~7=X4s>dW z=!l|Apm)`LY8tlb6pi`Zs?2)=UE`Kxc@qMiiQy&QzE4LCae;kB>7{M6ToyQ(7|!fS z`irSJbnMsAqvHU21&+9?Xq4^cu~j4E7K>6%n|aSwn*uSy36=y-CmL{uGxd_G82WHf z!yz4q(JwHXcS}a8vRN@`QgR&{lr%PEjwX($T-%n)m@>;UU4125wr_<%Un9PTqXIoX zp^9hO#x2v68r~Hc+q@*c#9Hn528c6fTjYJl!i+J#~G$*c`KDb z=Zdt+4?ZPU8pkL;;$U9ptPdRK=SYTJ?0abxS9M&&_27VHUC*@rvvW6&aoo@_q2puR z6u9}e_6kJ30R492?*Bla?VHk(#w~$}ayKY?*+zzM?`GCBt>X?pAqIW~IT`mvfQrjq ze5xU%V-|A)r<(e*;Fe6wG49E7#il~0l`HwY`ii$hIt_DSl^j6%$g30BNY8w$4xL0vJt0b4KvPJAB9fwYbXSt3~?mz7^t9y-^Djg!HQ)}XK z6ME`$yT@U73Q$07st#myeZ3Iq&$zV_*<61UmBV#=$Fteg1ep|Dw2F@DRTKsI=ke_I zU|UVcPEFKX8Hl!%;Nz`xpt1f2uxyi7qGLt%nGiYwJF`D!g3%UQSvUj6_uCMmQA)F+>04CL3s9s#+qyzygu6Y>+3r#Lv)z$x z4Am=0l0D~YY>k8pBHZvB<%jF#M@jxtQ+W%kAy-A#FxSCtoO_MYhsjqM+rpJCq>@{h z+`{c`%=|{*JAEJalV^Ywx~bjt(N*^_`TZ!a*EnIY>V!e8!6qw9BoX+UR~%Xi-Lgg> zT>5uloffJ7hmx;eX~K_sToc64 z>k+MX5hJMJX^1#aJUIqE$9_d@8}l#GwGE$XkVHWwy&CAAYA|}}s3-YGt|TWWIesdenFg0{NtCYjj~@~H7k@}v#sB~S literal 0 HcmV?d00001 diff --git a/target/classes/com/muyu/common/core/web/domain/BaseEntity$BaseEntityBuilder.class b/target/classes/com/muyu/common/core/web/domain/BaseEntity$BaseEntityBuilder.class new file mode 100644 index 0000000000000000000000000000000000000000..5a776660714536fcc8883f44895b862badf1ed07 GIT binary patch literal 3431 zcmbtW>2e!I5dOxNEm>Lc89R2c@fDDi*vpY{Sh7i^#K=KDu!RGmimI_RvDe;1wL1#v z72pMU45$iFl^=Nk9*Ux8cP*`vP+28a&Gt-BPk-G#-P8K}pVw~yY~pzm0~m}WreO%f z3}ff~CD%=EpX&Q3=b|DRhI58($SsD!RQh-X35>*%)NlhDL$2ajx>aw~b(&a?O~e)Y zkK#nHIuvBk3CUX*Nl4Z)+IE z7(>hx<_m_0sdT9y)U0x0Lc^r8KT&Z7mm=R#%$pjfF~g7ym}SEvqs9WG>e4Xv$0X_> zpVctuAFtP{&ExNAnD>tdOm$poeOJSxk{x!1#oY_VTGp_FRbth+%Po&#Gy<~AYuO39 zoA)%lj}*g<#V3)yns_v^63P4$_@l$pCio;lS6l|w=bmqIbq35RT8m_4jh z6rfoZ}#U+^*uW?CU*bI{m`mOEmu2rjx#o~r#ZFuDBeiSE2<-Xn$hK>Jd zyk@SaefNe^Aw5wqBEx!5ZyWim6cmUiq!YZD3{#||?Hy(KIak=|A)T}NUZmUK_Ga>L zMT`k6onF`@r|wq7wxMc!wAC2amEq*gp73PR@njr344LcS5EVt~gKS z>P~8P*}z)!%blQIDSptUHd&fM+p%ro7EJDWg34*U4LOcuhC6Km?i9B;zGYbGE=oNz z2y$$R`sM(&#W;QGuLsQrR15bhb#0$gPxdLbVV_dp^(i%5Fr@u?hE7x6V3@vliSvjk zBRZ7%3+#8A4B{Sr)iy}Y8hy1u0PE0c^}!X+hqR*qG582EU)=$jrOZrb?*4&z=1;u! z8?D-PP6twzKRK>LNUFEdO_Z4Ia zCCu)XCiG@3rvC2JR#2~7F@`Vju(?+Cnn!`Ys(OE*_dpfua^@9P+bfLIn4)ou#%&r4 zG?rfD4$!#_gVJ7ZD{aM5h7N^RJjN4R4PYBz`m~6rL=%Ml6^*ZPM5_tfPoRi$Yy{un I8NS2pzZ1k6-2eap literal 0 HcmV?d00001 diff --git a/target/classes/com/muyu/common/core/web/domain/BaseEntity$BaseEntityBuilderImpl.class b/target/classes/com/muyu/common/core/web/domain/BaseEntity$BaseEntityBuilderImpl.class new file mode 100644 index 0000000000000000000000000000000000000000..c55f058812770ae457312ec902c4d2c81995286a GIT binary patch literal 1228 zcmb_cO;6iE5Pjn~7#o)aDA3XtTGT@lRTzn50-}K;kRmQrk$T)D+p?8EDu00ZQ;1$b z0*N2cA5}4HB60{tMCoPU?quJaHHTWG;4ey9jF|6~dOcMVr6j-o8?$~~pqR2q!`+D~yJ2$P30N%x~9CAbG+DB~SpB#Gp< z%?$27JeB#On9hTB9$SN-CSWr1n*>h;pFBolEY_|-zjf0Ts}Sh@pXibGTY literal 0 HcmV?d00001 diff --git a/target/classes/com/muyu/common/core/web/domain/BaseEntity.class b/target/classes/com/muyu/common/core/web/domain/BaseEntity.class new file mode 100644 index 0000000000000000000000000000000000000000..80edf949a54bccb7fd04bbabfb09064e050fa8d4 GIT binary patch literal 7075 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 literal 0 HcmV?d00001 diff --git a/target/classes/com/muyu/common/core/web/domain/TreeEntity$TreeEntityBuilder.class b/target/classes/com/muyu/common/core/web/domain/TreeEntity$TreeEntityBuilder.class new file mode 100644 index 0000000000000000000000000000000000000000..23bf6fa5d45654f759469a22e69bb5ad75e8063d GIT binary patch literal 3447 zcmb_eT~pge6g_Kf46a(f0)&vdNlRK|65Q{P7-(WAG_($(u}j+IN!VLZuq8%Xr^7@4 zM}I~;lcqCy$Peg`>g22>OJ?jCJoG`@yQ`~v_TGE;-u?H#m#+Y<<4FbyjHHn?kiw|I zTHR?CTJ7U@fkMl%Ik>X$o7^ijoR(?Xg_7yXySB2_@lq_7+E%k6U4hXx%eK^pz({U+ zcN}9FPa|XCEf{AoU2|o}bSy@uGe@Q?ZMAK-q`-9Lsrk$-G);TIP*tvF?-w&fd&j^8 zCIyn7Z2l^6E4N%Z&7T-)DHcP}qV{0gzzi-3j0N~x4S~rn^om1ltv6?29v7+SxD3U1 zyCpEwrMG1(xi4K`^^$=_9g%Uc)!MgB)pkjYFys;FT7H8X$+FYn z$BBw%OJ-(Ix;1mJ>GQ~`o6TL*we&n(OsWHmk=ZyOcLe4tCyXB-bpSTcWqGYuF4js# z?bpLT$vY?INxJ(|9XO30)8$c9(q$;-`{8g80z2Jbd}^EgqKrsDsG7MaxiT=vmYxp*uyvS&F4aRufNXQ_7a553i!CSI+TZw|rDVrdxd-ufCb z`ShbUXycI?flSqDyLGu~>BBt{7xYzK9a%EBS#Iw+o=W38f&AHf15ZWCag?WA^C&nT z-oz-r&)^4C(zvgy?6$yCT!St9nR6(^-V_wxO|#BYe=P7vH|0Xb!00{GZZxI0RB@cc z_EGUoCDm_xCw{E4xb#tf(q-Yn-cwmuq;StB%Vx7#wUjK5VMn0R6}4ClJ7UgjA4zvz zT)z@`<9N2!&~m5$X*p_edeZ4@>tb2J*s^WumYb&MNiU7+8PXARKwv7KL^p(~p6#ab zNZ?wJ!~KGt#xDZd!K%F91er-*W#3A$zoz)+<%Uv1-xB(Ag-=3~B%KmniNQ_em+vGO@*f+oMZXPOoTlcj%TjbMlbA@j#q* x$Wmc^N+>f93EaV5f84}9j$;Jc;`0DMa(0P{OyD7ENqwu|#7`V|@iU{h@EXQkNCyA_ literal 0 HcmV?d00001 diff --git a/target/classes/com/muyu/common/core/web/domain/TreeEntity$TreeEntityBuilderImpl.class b/target/classes/com/muyu/common/core/web/domain/TreeEntity$TreeEntityBuilderImpl.class new file mode 100644 index 0000000000000000000000000000000000000000..1b5c0be1e0dc2e1999bb8d3b52d2a844bf259e0b GIT binary patch literal 1618 zcmbtU%We}f6g{3yGM!-{ZJ|8MqdWp60d5(7r_K=T0grx0vF z0*MdcqY&3q3ojC?iL#k%-|;#3_}JIK|NQs`;29oyuuyYg`>3PA(4Qnx5EUmyK!YfW zDWoF!B*wusiMWh|<5Y+jaW3Zg`r!W(1Qa{={U!* z+a6X2&MN73Mjp=Nf`g`yRje^QUxw}=`VcbMnF!x7?6*5hX;Wl9rS77SOW0tjkClB4 z+Y1r*NYGv~6%5UxjKyIQjYWFQ$023bhslJ8Bc4h%*GW5nCo_h@^5V(Og{00opK7}u z@JwGe+HIAZ2GBA?eLIQ5J(6!@o);;3^m!>C`U{|Y3#^^Bs=O{cB;GqpigY4gN_E0b z(|p_S`A5#MawxL=Rg&cl{y`jzbT8ytCg{Z1%-VmAx-pYebsRjb@B|oVXH9m{snGL= zfz+9aR);6l92PxKK!YN^OJKN6vw$L_SnqxZ`$_`~SIR{y*~C@)`lkzU4VyHxRqt!? zV-2>tU+B;Okd{U=)tVal6t$Ao{R!tam3Ge}a8C(V_o?)CO<;6?gMSvduED!h{l*;d xdKIv$d~*)CN@a5FR_V?bZkO>6?v~NPJ&K#;ghj7(%XaCA9NeeO1GLe#-M{Vqqxk>; literal 0 HcmV?d00001 diff --git a/target/classes/com/muyu/common/core/web/domain/TreeEntity.class b/target/classes/com/muyu/common/core/web/domain/TreeEntity.class new file mode 100644 index 0000000000000000000000000000000000000000..6e2fa7fae4b073a4dc98e3f8a96ede7a48fbaa95 GIT binary patch literal 5596 zcmcIo`*T}Y5&n)X=}PidtT?e7rAeBmPH-fvC(D#B#mHq2Uf+a%YF<>&s51 zHazY$-QzW{;$2oqjaF(EZ%pCHz~sjD`Xh+(R;4=cHb(ZJwUAcWa>2Ri47a>Wb@)i5 z;ar}qG`%b=WW~5Hh0cNfXEVrQlZ9>@d2HT*y=NM3Xm5IMk?S?wnnHduT1%E0$zTh% zTIjXWhr1L;Z|6^h+aHm>KML6uhs$`=&a$h}W7euVwT0msuTiNjaJ3w^%97h{+>IRy zX`?VPADX7cC+oiOb`ieE#%|n8;d+Bnn{F*LWHIcCn&&PsngV{0$oJdWE6!w`+ML_; z>W!vM^~)4PxL@J$op?F1yi(0#0NE@-2W<@DeudnE>z#`EzI9-K%tGILQTVWpkKh4? zu24aI?^z|Ty%*sJZG03TGs2ICzW1&Y4!jrnkK1@itg#7xB=B7(Mr@4YkO4XxCPTQY zPCcCtJ*z#{nwEeN?z{D8KkBH$! z%eM_$hY9sD8^>{iSup3+jz8OSstUbvJ06|A;Ldp?`_D^hpR_R{!YNlIn^`=9lNKhK zWjg5Iz*l`LX5p~5C(kfYWTActn^oDHx-u9SmdrMI_!@Qy-k`Q zV;F(xpz{SHMt2OsoU{2jhGA{eLgDeJ&~0R59J5e#Gw>IR7`YkGnOmB2R{R!{Jau|v z>NuOi1lwxEZMLc&dtJ=*On+o$m(MrmWcyRptHCrI}OH zJiez~X>P3R{CK_YHNA$jVrstGox=CB_&$DM;fHc&{YardYVVcW#rl%#-#pAQcEXwC zZGbnp*W!8L3mVXqPHn#GHv1>*^`+Lz$OomB&c#)VXV}=P!QDaP2pt4l=rFwU5QO)T;#J3s`l_i<5<4D(qdiN$p~9;flhp|1Qc*G#iC`k>f!Qq_7>Q zF$0A&eCojC{P1S0Y2G;X$jhW2c}dhGZ`723LeuBag9NYLPofj&$$5#R;679=-GnN> z4eQC9$XthAEWL@I>)0k^;W~E8xa&Ih$avp%?33|Bl=}mfJMk31H*o~I2|mP2d^bMD zr#=IYr}1e%aT$CD&(Ii$w9V0QW=YMFPAX34eM{$&fs4-)K`c}av8ODkb5=?DdiDD?$(g?_Kxq{D5O+c z2)S27uHad}#3pHN7|U3_n8f{{Sel}Z>Lf+fbI~GsKAMedkl74V=NQXRGJbpU21@)4 z^K*cogZz}=!WghNC5!>447P`5z!!7kNDYnA4MEHoZ(=;wAfnjFD+cG;SK{fFk70R! zD6Vi!QMR-OOJ`YeP}o2f69#5Wo1L&7;nj`X44Bw_Ld$Wt{>C5d(r?-rxMcP~OSI_hs6b5wb^RQxTH ziEE`lBlkLzo!3h5AU8G0s2|mj(q2DlC~nI1Aiv6qp}(N3khq1-S78qo5;yR0AaS6a zq{xACs*n_!)GhQ}MXr!6q+&v)vPGd%IbE+2lU^zpfgXL@?BSKkUC{xJB zgv#YE3YE)tp-Y5pUx-v7l(UEg;UHNqg5_j zdWuQoYgu|~NBb>4Z7lV-xDzkq1-wW<()c?6UZf{EZpce?w-fuYh;NWe;0UJhO;Smw z$phSE+2TtqzL)uJ=?(h_vhVXUli-Fa#XW*j?-L?=?hr;Vl7T=V!|O|&<%^|ayhw5_ z*B7={3rF&8CVZ9jI+aMP{Tvk;h;QqB`3_#;m}0O7_$%FaNv9dYSNZudUgOgqO8%6y ZZzSYbyc~YUae(8`8KGa`SNJsw{{flK-oyX^ literal 0 HcmV?d00001 diff --git a/target/classes/com/muyu/common/core/web/page/PageDomain.class b/target/classes/com/muyu/common/core/web/page/PageDomain.class new file mode 100644 index 0000000000000000000000000000000000000000..0186d6621b2666824348a7a8eb98e5e686b22d70 GIT binary patch literal 2701 zcmaJ@+g95~6x{>1Wg8KQ2~e7bBm{^tG$v`d9?r>H>4;Ye`;I?V-JzX<w(m7ikmWoPSYw2YxX2> z>a~iI#4QXexUJ$2?lO#q##eJR-OhUCH|3i6Y4&`j>YOn2rUMuH0gmm&agTv0-ol8A zQQW7LJLaZAHy35f^4un>qhYR>(Na{xF%=n%GxS&Zk;t1yi96exb3iNG;f6zurb56j zV+B*`la-nLSj7WmDJ)sWm0G1WjZ20j_J!r<{Gp0R(lRbBi`vgZ#`svpr!sv>&0aAb zH!*R1&X6Dl38PFkPvQx_P>@qGji)X8^H`An#G4wIsclK9r_P*A=$C|F7InZlW*8!J zFA3yT%t_oB5vb{Uf&{jp;wveR$j18SJWYACOeyawXod`MPgonCUQz|K#Puz1X>#u$ zMw|nUsALMEdI-^bj>zo`XVudd!;N%Erb&E7hnJ3)dDXv1`pBknQ{LO+Q%^bp+X17# zV43SSV_THcW#Jr{S+ovsI3u{EH2=Ta8PC1}?YQ{7C)lq{Mxt0WdQ+1Opp*X4nHF^DkPI<{l+ zsu#Q66+<}*f!7N5Wy2gW3^yMa%{VrXgx|+r4=wPLV_MY3zXsFl8+xFN+$ih99xj;X zQLUQ0+){y)7RQZ~st7UP4cOP!9XRqK@?6)8nj>;?9MVh7?l1;*$uNYK*ST#ATLCrc z$fe-II4dw22LIobTE`0zcj-B3qZcPiBfSZ$dx znyxP%3A0gyX`fQ~G=z~7`s%y(S>4G(U?O&6n zSyG4+pV2JS7R!vEVWJtSm&{2@&_frY&$IFo$lgsd z!v^_A8Es_fo1vU0FLHVu%4sW{)8s`?Q^e`X6`W+j8=M}woVFX~2NKoMx`zB*oX@bkpUu6Ur%Zk<(l#r`-mpU;Nso$ItP}Dek2{8~+nir%0rf z!f)tGJ@dA))boO@ZbGv5lr{*3){8X0pij)#ue-$TRe*i0!9FH`auJfR*M67Bk2JP{V~^%I{j)@W8jfg}q?HZ> HRSf(Gf<7!1 literal 0 HcmV?d00001 diff --git a/target/classes/com/muyu/common/core/web/page/TableDataInfo$TableDataInfoBuilder.class b/target/classes/com/muyu/common/core/web/page/TableDataInfo$TableDataInfoBuilder.class new file mode 100644 index 0000000000000000000000000000000000000000..4b72c4f8cb53edbb6bd54bcb23b3671118a83bd1 GIT binary patch literal 2890 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 EKO$8bBme*a literal 0 HcmV?d00001 diff --git a/target/classes/com/muyu/common/core/web/page/TableSupport.class b/target/classes/com/muyu/common/core/web/page/TableSupport.class new file mode 100644 index 0000000000000000000000000000000000000000..3a4da38a6ee9f720ae6cf42fa56380c9924836fd GIT binary patch literal 1534 zcmah}?NZZF5IwhmX+sdGilT_32yH~-2YyjN+fuYr+S)*8@DG!gYYmeom?kJbfsdnq zfYBKrz=v|&n^MSVNB`VpH+#<6-MgEgzrOtdun1j71U(XZ74+c@!_bbgXK0pTH?&f1 zhu1xZGYh6|dP@vFiDXqmlp#@fnp)G}_ceNHIyOCAu6^J&ZP#dUtxU(V(=<$5h9t=Q z8PtD6){UCQb$@r)alI%87$m_|^qVpUF(hGFK^*58rn}Pko@uo-ox6J$_cnyVpftEw zHe930J?=70B?_mN>7Hxaja2e)my8P-k#JGLC5$o*pJMX1#~Xr4+A}Plm)bJAXETIK&>#pJbP2{}~rO>0}YD*V0ATQuad?)YwVJfOPJI|f23asHlNEtNb}nR3lD!hLlVraL*&5j|L-rhch>QMmbmoasqbCzO z>LJ+IK;jjh;xo{XRXUY+018+mD<)=wJOcbVAOlvuL)ttyMig!4zZe1Pj} ze3I-Tx#O5p<4JNH;Ixlqfca@<~=>LB0T`u_QQIz}c?o0a>rf7eVDWykU7P literal 0 HcmV?d00001 diff --git a/target/classes/com/muyu/common/core/xss/Xss.class b/target/classes/com/muyu/common/core/xss/Xss.class new file mode 100644 index 0000000000000000000000000000000000000000..a7ef1bf346b7e7affdd5a08c043adf93c967a40a GIT binary patch literal 819 zcmah{%}*0S6n|5o3)Bx#6cMoKfe%gGgNc`_Ni9nxDYV%x#H6QTJ8TALXPTK=;vP-( zrim9X#*263nMl;Te@8X_D~xZcu}x^?Fz>y2@ArQ1Bk#xWFW&&*A>4EzM_?gTu^T68 z;-V2NiApo~xiRijV;mSIFm*r=sVk_A+}hp&3#|ir0>!?UN~tWh9FxocOae~KjG+-D zFtfT=?ZX>Za~YKgO!v|2Y@a5=5}5BCz4`g-MfdYj=lHnu^25Y0fjK{smd9+D8@?x4e>!Yq_u2&F1iTwu8+rkHF&alz{4pSw!9%9`^)`v3!uW8G`a#Uht&0j*V<= zdR5egN^QFl_&b%L=HqVbWxtFN5A7oIomx>vNnKR}LRk6(vsdXf literal 0 HcmV?d00001 diff --git a/target/classes/com/muyu/common/core/xss/XssValidator.class b/target/classes/com/muyu/common/core/xss/XssValidator.class new file mode 100644 index 0000000000000000000000000000000000000000..a6e7f6d02cedfcb218cfa4eea4a089d2555137fb GIT binary patch literal 1653 zcmb7E*-{fh6g{1VA#s#VP{0Kjkc4Gaa3e8@sO3X~ltHv`t0qj5kz@vEIuusioxbPXt?&0AU%mpkj+;qD5H%1p5l5Rq=L@+dt%`Kl zt$V94?2-~_%Q~*3astuO)Uttgf#H%@wW_sOHH(|7=Ti8#wH*Z3qaaw86=zK<&u>Rk z;AnRANijW}di*T+G@YAF&%Vxb8M1Oom^f(Qkckd-3LFV}Ys#ruzP)a5TZ>XD+jj*F za%?&kTVP@||J%Ew@*Q_Qld7wB8)h^?w}~DcCPr1NQrY$erkXM4%hE5}FKf12vRfb( z>PAHB!w~~VP4r`+g{zHwzi5ImRVzsx!*K&AOq|4!z(~uaX~zSr9?S#1CyEo?WC1n= z2KRQB=mt)kID@kSX32Avbll*ss#XMgo6UCev|?c7kMh=kXqZtGDWpTDkeiKQ z|EOU7C1>^tnpzP^6rFWfs+!LiUySfvfaUegI{)n!^_($}J0&h>PEOkzckeW zT*eiW=rP-atF*jEeO&MYsOI}?@RfPee0^h|G57}Y*n4hHy@e5bhgejTMw>_nn1n(4 zd%TIXuMq_?LV6u#A7+ptLzeqCL=(vyB6J`T2_2XLI_K!b^@bDEB+*VJ(_`N-%xKU3 cj&^t2k_|!~kp}DpX81*rqauP?+z92|PYeK^5dZ)H literal 0 HcmV?d00001 diff --git a/target/cloud-common-core-3.6.3.jar b/target/cloud-common-core-3.6.3.jar new file mode 100644 index 0000000000000000000000000000000000000000..bd092c4901a9468501eb8f33456f81130252540f GIT binary patch literal 142681 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

}#}*Of=;w>Kc$|B?pZdh@vM(_Wa*+;FFfr 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