From befbda8534a7d1b332c1b369e378966a6d7e4f83 Mon Sep 17 00:00:00 2001 From: yyb <1416014977@qq.com> Date: Thu, 15 Jan 2026 17:29:12 +0800 Subject: [PATCH] =?UTF-8?q?thewindminer=20=E4=BB=A3=E7=A0=81=E6=8F=90?= =?UTF-8?q?=E4=BA=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 425 ++++ .../jxy/windminer/WindminerApplication.java | 27 + .../com/jxy/windminer/annotation/Excel.java | 163 ++ .../com/jxy/windminer/annotation/Excels.java | 18 + .../jxy/windminer/annotation/ParamName.java | 21 + .../jxy/windminer/annotation/RateLimiter.java | 30 + .../jxy/windminer/common/RedisTransKey.java | 44 + .../com/jxy/windminer/common/Result/R.java | 78 + .../common/constant/CacheConstants.java | 34 + .../windminer/common/constant/Constants.java | 136 ++ .../windminer/common/constant/HttpStatus.java | 99 + .../common/constant/OrderConstants.java | 16 + .../common/constant/SecurityConstants.java | 49 + .../common/constant/TokenConstants.java | 26 + .../common/constant/UserConstants.java | 81 + .../common/context/SecurityContextHolder.java | 98 + .../datascope/annotation/DataScope.java | 25 + .../datascope/aspect/DataScopeAspect.java | 139 ++ .../windminer/common/enums/HttpMethod.java | 37 + .../jxy/windminer/common/enums/LimitType.java | 14 + .../jxy/windminer/common/enums/UserRole.java | 34 + .../windminer/common/enums/UserStatus.java | 31 + .../common/exception/CaptchaException.java | 17 + .../common/exception/GlobalException.java | 57 + .../common/exception/InnerAuthException.java | 17 + .../exception/PreAuthorizeException.java | 16 + .../common/exception/ServiceException.java | 75 + .../common/exception/UtilException.java | 26 + .../exception/auth/LoginExpiresException.java | 17 + .../exception/auth/NotLoginException.java | 17 + .../auth/NotPermissionException.java | 24 + .../exception/auth/NotRoleException.java | 25 + .../exception/auth/TokenExpiresException.java | 17 + .../exception/auth/UnAuthorizedException.java | 17 + .../common/exception/base/BaseException.java | 84 + .../common/exception/file/FileException.java | 20 + .../FileNameLengthLimitExceededException.java | 16 + .../file/FileSizeLimitExceededException.java | 16 + .../file/InvalidExtensionException.java | 82 + .../user/CaptchaExpireException.java | 17 + .../common/exception/user/UserException.java | 18 + .../config/FastJson2JsonRedisSerializer.java | 73 + .../common/redis/config/RedisConfig.java | 85 + .../common/redis/service/RedisService.java | 267 +++ .../annotation/EnableCustomConfig.java | 25 + .../common/security/annotation/Logical.java | 19 + .../security/annotation/RequiresLogin.java | 16 + .../security/annotation/RequiresRoles.java | 27 + .../common/security/aspect/PreAuthAspect.java | 93 + .../security/aspect/RateLimiterAspect.java | 90 + .../common/security/auth/AuthLogic.java | 372 ++++ .../common/security/auth/AuthUtil.java | 147 ++ .../security/config/ApplicationConfig.java | 24 + .../common/security/config/WebMvcConfig.java | 50 + .../handler/GlobalExceptionHandler.java | 213 ++ .../interceptor/HeaderInterceptor.java | 125 ++ .../common/security/service/TokenService.java | 192 ++ .../common/security/utils/SecurityUtils.java | 129 ++ .../jxy/windminer/common/text/CharsetKit.java | 88 + .../jxy/windminer/common/text/Convert.java | 916 +++++++++ .../windminer/common/text/StrFormatter.java | 93 + .../jxy/windminer/common/utils/CodeUtils.java | 43 + .../jxy/windminer/common/utils/DateUtils.java | 310 +++ .../windminer/common/utils/ExceptionUtil.java | 40 + .../common/utils/FileUploadUtils.java | 282 +++ .../jxy/windminer/common/utils/JwtUtils.java | 146 ++ .../common/utils/MysqlBackUpUtils.java | 75 + .../jxy/windminer/common/utils/PageUtils.java | 36 + .../jxy/windminer/common/utils/PwdUtils.java | 110 + .../windminer/common/utils/ServletUtils.java | 306 +++ .../windminer/common/utils/SpringUtils.java | 114 ++ .../windminer/common/utils/StringUtils.java | 548 +++++ .../windminer/common/utils/WalletUils.java | 112 + .../common/utils/WalletVerifyUtils.java | 111 + .../common/utils/bean/BeanUtils.java | 112 + .../common/utils/bean/BeanValidators.java | 25 + .../common/utils/file/FileTypeUtils.java | 77 + .../common/utils/file/FileUtils.java | 257 +++ .../common/utils/file/ImageUtils.java | 85 + .../common/utils/file/MimeTypeUtils.java | 67 + .../common/utils/html/EscapeUtil.java | 171 ++ .../common/utils/html/HTMLFilter.java | 566 +++++ .../windminer/common/utils/ip/IpUtils.java | 267 +++ .../common/utils/poi/ExcelHandlerAdapter.java | 19 + .../windminer/common/utils/poi/ExcelUtil.java | 1168 +++++++++++ .../common/utils/reflect/ReflectUtils.java | 406 ++++ .../windminer/common/utils/sign/Base64.java | 292 +++ .../windminer/common/utils/sign/RsaUtils.java | 173 ++ .../windminer/common/utils/sql/SqlUtil.java | 62 + .../windminer/common/utils/uuid/IdUtils.java | 49 + .../jxy/windminer/common/utils/uuid/Seq.java | 88 + .../jxy/windminer/common/utils/uuid/UUID.java | 486 +++++ .../common/web/Result/AjaxResult.java | 164 ++ .../common/web/Result/PageResult.java | 26 + .../common/web/controller/BaseController.java | 129 ++ .../common/web/entity/BaseEntity.java | 53 + .../jxy/windminer/common/web/page/Page.java | 64 + .../common/web/page/TableDataInfo.java | 52 + .../common/web/page/TableSupport.java | 58 + .../com/jxy/windminer/common/xss/Xss.java | 27 + .../windminer/common/xss/XssValidator.java | 39 + .../jxy/windminer/config/ResourcesConfig.java | 52 + .../windminer/controller/OrderController.java | 137 ++ .../controller/SysFileController.java | 147 ++ .../controller/SysProfileController.java | 151 ++ .../controller/TicketController.java | 418 ++++ .../windminer/controller/TokenController.java | 123 ++ .../jxy/windminer/dto/AccountDetailsDto.java | 53 + .../windminer/dto/AddressOrderDetailDto.java | 53 + .../com/jxy/windminer/dto/AddressUsedDto.java | 40 + .../jxy/windminer/dto/AllTicketListDto.java | 43 + .../jxy/windminer/dto/BaseRepairInfoDto.java | 25 + .../com/jxy/windminer/dto/BaseTicketDto.java | 31 + .../com/jxy/windminer/dto/CallBackDto.java | 31 + .../com/jxy/windminer/dto/ConfirmListDto.java | 32 + .../java/com/jxy/windminer/dto/FaultsDto.java | 16 + .../java/com/jxy/windminer/dto/FileDto.java | 16 + .../com/jxy/windminer/dto/GuaranteeDto.java | 23 + .../java/com/jxy/windminer/dto/IdsDto.java | 15 + .../windminer/dto/OnChainMsgByAddressDto.java | 37 + .../windminer/dto/OnChainQueryResultDto.java | 31 + .../dto/OrderAndOrderLogisticalDto.java | 52 + .../jxy/windminer/dto/OrderConfirmDto.java | 19 + .../com/jxy/windminer/dto/OrderInfoDto.java | 64 + .../jxy/windminer/dto/OrderInfoReturnDto.java | 30 + .../com/jxy/windminer/dto/PaymentDto.java | 21 + .../windminer/dto/PrivateTicketListDto.java | 34 + .../jxy/windminer/dto/RepairRecordDto.java | 42 + .../windminer/dto/RepairTicketInfoDto.java | 31 + .../com/jxy/windminer/dto/ReturnInfoDto.java | 44 + .../com/jxy/windminer/dto/SysFileDto.java | 27 + .../com/jxy/windminer/dto/SysToolsDto.java | 43 + .../jxy/windminer/dto/TicketAcListDto.java | 32 + .../com/jxy/windminer/dto/TicketAuditDto.java | 21 + .../jxy/windminer/dto/TicketContentDto.java | 30 + .../windminer/dto/TicketDailyCountDto.java | 32 + .../jxy/windminer/dto/TicketDetails3Dto.java | 59 + .../jxy/windminer/dto/TicketDetailsDto.java | 42 + .../java/com/jxy/windminer/dto/TicketDto.java | 33 + .../com/jxy/windminer/dto/TicketListDto.java | 29 + .../windminer/dto/TicketOrderBaseInfoDto.java | 43 + .../dto/TicketOrderPhoneAndDetailsDto.java | 30 + .../jxy/windminer/dto/TicketPaymentDto.java | 25 + .../jxy/windminer/dto/TicketPrivateDto.java | 53 + .../jxy/windminer/dto/TicketResponDto.java | 27 + .../jxy/windminer/dto/TotalAddressDto.java | 19 + .../jxy/windminer/dto/WPOrderAddressDto.java | 32 + .../jxy/windminer/dto/WPOrderProductDto.java | 38 + .../com/jxy/windminer/entity/JXYFile.java | 33 + .../jxy/windminer/entity/OrderKeyAddress.java | 34 + .../jxy/windminer/entity/RepairRecord.java | 96 + .../com/jxy/windminer/entity/SysFile.java | 67 + .../java/com/jxy/windminer/entity/Test.java | 30 + .../java/com/jxy/windminer/entity/Ticket.java | 55 + .../windminer/entity/TicketSupplement.java | 24 + .../com/jxy/windminer/entity/ToolsFile.java | 36 + .../entity/auth/EmailCodeEntity.java | 28 + .../windminer/entity/auth/EmailEntity.java | 25 + .../entity/auth/GetEmailCodeEntity.java | 33 + .../jxy/windminer/entity/auth/LoginBody.java | 24 + .../jxy/windminer/entity/auth/LoginUser.java | 46 + .../windminer/entity/auth/RegisterBody.java | 39 + .../windminer/entity/auth/ResetPwdBody.java | 26 + .../windminer/entity/auth/WpLoginUser.java | 26 + .../jxy/windminer/entity/system/SysDept.java | 85 + .../entity/system/SysLogininfor.java | 37 + .../jxy/windminer/entity/system/SysMenu.java | 262 +++ .../windminer/entity/system/SysOperLog.java | 68 + .../jxy/windminer/entity/system/SysRole.java | 89 + .../windminer/entity/system/SysRoleDto.java | 26 + .../windminer/entity/system/SysRoleMenu.java | 46 + .../jxy/windminer/entity/system/SysUser.java | 119 ++ .../windminer/entity/system/SysUserDto.java | 55 + .../windminer/entity/system/SysUserRole.java | 19 + .../jxy/windminer/enums/AccessoryStatus.java | 43 + .../com/jxy/windminer/enums/OrderFrom.java | 42 + .../com/jxy/windminer/enums/PayStatus.java | 43 + .../enums/TheWindMinerLogisticalInfo.java | 50 + .../jxy/windminer/enums/TicketStatusType.java | 53 + .../com/jxy/windminer/enums/TicketType.java | 42 + .../jxy/windminer/enums/WCOrderStatus.java | 42 + .../com/jxy/windminer/filter/XssFilter.java | 70 + .../filter/XssHttpServletRequestWrapper.java | 112 + .../com/jxy/windminer/mapper/OrderMapper.java | 164 ++ .../jxy/windminer/mapper/SysFileMapper.java | 65 + .../windminer/mapper/SysLogininforMapper.java | 44 + .../jxy/windminer/mapper/SysMenuMapper.java | 20 + .../windminer/mapper/SysOperLogMapper.java | 49 + .../jxy/windminer/mapper/SysRoleMapper.java | 55 + .../windminer/mapper/SysRoleMenuMapper.java | 45 + .../jxy/windminer/mapper/SysUserMapper.java | 122 ++ .../windminer/mapper/SysUserRoleMapper.java | 71 + .../jxy/windminer/mapper/TicketMapper.java | 327 +++ .../jxy/windminer/service/FileService.java | 31 + .../windminer/service/ISysFileService.java | 31 + .../service/LocalSysFileServiceImpl.java | 71 + .../jxy/windminer/service/MailService.java | 110 + .../jxy/windminer/service/OrderService.java | 88 + .../jxy/windminer/service/SysFileService.java | 80 + .../windminer/service/SysLoginService.java | 540 +++++ .../service/SysLogininforService.java | 41 + .../jxy/windminer/service/SysMenuService.java | 14 + .../windminer/service/SysOperLogService.java | 50 + .../service/SysPermissionService.java | 22 + .../jxy/windminer/service/SysRoleService.java | 49 + .../jxy/windminer/service/SysUserService.java | 167 ++ .../jxy/windminer/service/TicketService.java | 316 +++ .../service/impl/FileServiceImpl.java | 59 + .../service/impl/MaliServiceImpl.java | 1234 +++++++++++ .../service/impl/OrderServiceImpl.java | 1307 ++++++++++++ .../service/impl/SysFileServiceImpl.java | 319 +++ .../impl/SysLogininforServiceImpl.java | 66 + .../service/impl/SysMenuServiceImpl.java | 39 + .../service/impl/SysOperLogServiceImpl.java | 78 + .../impl/SysPermissionServiceImpl.java | 58 + .../service/impl/SysRoleServiceImpl.java | 113 + .../service/impl/SysUserServiceImpl.java | 398 ++++ .../service/impl/TicketServiceImpl.java | 1820 +++++++++++++++++ .../com/jxy/windminer/task/OrderTask.java | 33 + .../jxy/windminer/vo/AccountConfirmVo.java | 23 + .../jxy/windminer/vo/AddRepairRecordVo.java | 42 + .../com/jxy/windminer/vo/AddressUsedVo.java | 28 + .../java/com/jxy/windminer/vo/AddressVo.java | 21 + .../com/jxy/windminer/vo/AllTicketListVo.java | 42 + .../com/jxy/windminer/vo/BaseRepairVo.java | 17 + .../com/jxy/windminer/vo/ChangeTypeVo.java | 18 + .../com/jxy/windminer/vo/ConfirmListVo.java | 29 + .../com/jxy/windminer/vo/ConfirmTypeVo.java | 19 + .../java/com/jxy/windminer/vo/ConfirmVo.java | 25 + .../com/jxy/windminer/vo/EndTicketVo.java | 17 + src/main/java/com/jxy/windminer/vo/IdVo.java | 17 + .../com/jxy/windminer/vo/OrderApplyVo.java | 61 + .../com/jxy/windminer/vo/OrderHandleVo.java | 41 + .../java/com/jxy/windminer/vo/OrderKeyVo.java | 19 + .../windminer/vo/OrderLogisticsConfirmVo.java | 19 + .../com/jxy/windminer/vo/OrderUpdateVo.java | 27 + .../com/jxy/windminer/vo/PaymentInfoVo.java | 33 + .../java/com/jxy/windminer/vo/PaymentVo.java | 19 + .../jxy/windminer/vo/PrivateTicketListVo.java | 29 + .../java/com/jxy/windminer/vo/QueryVo.java | 24 + .../com/jxy/windminer/vo/QuotedDetailsVo.java | 20 + .../java/com/jxy/windminer/vo/QuotedVo.java | 24 + .../com/jxy/windminer/vo/QuothMachinesVo.java | 20 + .../windminer/vo/RepairChangeStatusVo.java | 19 + .../com/jxy/windminer/vo/RepairConfirmVo.java | 23 + .../com/jxy/windminer/vo/RepairRecordVo.java | 36 + .../com/jxy/windminer/vo/RepairRefuseVo.java | 18 + .../com/jxy/windminer/vo/ResponTicketVo.java | 27 + .../jxy/windminer/vo/ResubmitTicketVo.java | 27 + .../jxy/windminer/vo/ReturnLogisticalVo.java | 32 + .../java/com/jxy/windminer/vo/SerialVo.java | 19 + .../java/com/jxy/windminer/vo/StatusVo.java | 25 + .../java/com/jxy/windminer/vo/SysToolsVo.java | 26 + .../java/com/jxy/windminer/vo/TicketVo.java | 52 + .../com/jxy/windminer/vo/TotalAddressVo.java | 23 + .../jxy/windminer/vo/UserLogisticalVo.java | 56 + .../com/jxy/windminer/vo/responSupportVo.java | 17 + src/main/resources/META-INF/MANIFEST.MF | 3 + src/main/resources/META-INF/spring.factories | 10 + .../resources/application-test.properties | 53 + src/main/resources/application.properties | 54 + .../mapper/windminer/OrderMapper.xml | 332 +++ .../mapper/windminer/SysFileMapper.xml | 137 ++ .../mapper/windminer/SysLogininforMapper.xml | 53 + .../mapper/windminer/SysMenuMapper.xml | 39 + .../mapper/windminer/SysOperLogMapper.xml | 82 + .../mapper/windminer/SysRoleMapper.xml | 85 + .../mapper/windminer/SysRoleMenuMapper.xml | 34 + .../mapper/windminer/SysUserMapper.xml | 192 ++ .../mapper/windminer/SysUserRoleMapper.xml | 49 + .../mapper/windminer/TicketMapper.xml | 702 +++++++ 271 files changed, 27768 insertions(+) create mode 100644 pom.xml create mode 100644 src/main/java/com/jxy/windminer/WindminerApplication.java create mode 100644 src/main/java/com/jxy/windminer/annotation/Excel.java create mode 100644 src/main/java/com/jxy/windminer/annotation/Excels.java create mode 100644 src/main/java/com/jxy/windminer/annotation/ParamName.java create mode 100644 src/main/java/com/jxy/windminer/annotation/RateLimiter.java create mode 100644 src/main/java/com/jxy/windminer/common/RedisTransKey.java create mode 100644 src/main/java/com/jxy/windminer/common/Result/R.java create mode 100644 src/main/java/com/jxy/windminer/common/constant/CacheConstants.java create mode 100644 src/main/java/com/jxy/windminer/common/constant/Constants.java create mode 100644 src/main/java/com/jxy/windminer/common/constant/HttpStatus.java create mode 100644 src/main/java/com/jxy/windminer/common/constant/OrderConstants.java create mode 100644 src/main/java/com/jxy/windminer/common/constant/SecurityConstants.java create mode 100644 src/main/java/com/jxy/windminer/common/constant/TokenConstants.java create mode 100644 src/main/java/com/jxy/windminer/common/constant/UserConstants.java create mode 100644 src/main/java/com/jxy/windminer/common/context/SecurityContextHolder.java create mode 100644 src/main/java/com/jxy/windminer/common/datascope/annotation/DataScope.java create mode 100644 src/main/java/com/jxy/windminer/common/datascope/aspect/DataScopeAspect.java create mode 100644 src/main/java/com/jxy/windminer/common/enums/HttpMethod.java create mode 100644 src/main/java/com/jxy/windminer/common/enums/LimitType.java create mode 100644 src/main/java/com/jxy/windminer/common/enums/UserRole.java create mode 100644 src/main/java/com/jxy/windminer/common/enums/UserStatus.java create mode 100644 src/main/java/com/jxy/windminer/common/exception/CaptchaException.java create mode 100644 src/main/java/com/jxy/windminer/common/exception/GlobalException.java create mode 100644 src/main/java/com/jxy/windminer/common/exception/InnerAuthException.java create mode 100644 src/main/java/com/jxy/windminer/common/exception/PreAuthorizeException.java create mode 100644 src/main/java/com/jxy/windminer/common/exception/ServiceException.java create mode 100644 src/main/java/com/jxy/windminer/common/exception/UtilException.java create mode 100644 src/main/java/com/jxy/windminer/common/exception/auth/LoginExpiresException.java create mode 100644 src/main/java/com/jxy/windminer/common/exception/auth/NotLoginException.java create mode 100644 src/main/java/com/jxy/windminer/common/exception/auth/NotPermissionException.java create mode 100644 src/main/java/com/jxy/windminer/common/exception/auth/NotRoleException.java create mode 100644 src/main/java/com/jxy/windminer/common/exception/auth/TokenExpiresException.java create mode 100644 src/main/java/com/jxy/windminer/common/exception/auth/UnAuthorizedException.java create mode 100644 src/main/java/com/jxy/windminer/common/exception/base/BaseException.java create mode 100644 src/main/java/com/jxy/windminer/common/exception/file/FileException.java create mode 100644 src/main/java/com/jxy/windminer/common/exception/file/FileNameLengthLimitExceededException.java create mode 100644 src/main/java/com/jxy/windminer/common/exception/file/FileSizeLimitExceededException.java create mode 100644 src/main/java/com/jxy/windminer/common/exception/file/InvalidExtensionException.java create mode 100644 src/main/java/com/jxy/windminer/common/exception/user/CaptchaExpireException.java create mode 100644 src/main/java/com/jxy/windminer/common/exception/user/UserException.java create mode 100644 src/main/java/com/jxy/windminer/common/redis/config/FastJson2JsonRedisSerializer.java create mode 100644 src/main/java/com/jxy/windminer/common/redis/config/RedisConfig.java create mode 100644 src/main/java/com/jxy/windminer/common/redis/service/RedisService.java create mode 100644 src/main/java/com/jxy/windminer/common/security/annotation/EnableCustomConfig.java create mode 100644 src/main/java/com/jxy/windminer/common/security/annotation/Logical.java create mode 100644 src/main/java/com/jxy/windminer/common/security/annotation/RequiresLogin.java create mode 100644 src/main/java/com/jxy/windminer/common/security/annotation/RequiresRoles.java create mode 100644 src/main/java/com/jxy/windminer/common/security/aspect/PreAuthAspect.java create mode 100644 src/main/java/com/jxy/windminer/common/security/aspect/RateLimiterAspect.java create mode 100644 src/main/java/com/jxy/windminer/common/security/auth/AuthLogic.java create mode 100644 src/main/java/com/jxy/windminer/common/security/auth/AuthUtil.java create mode 100644 src/main/java/com/jxy/windminer/common/security/config/ApplicationConfig.java create mode 100644 src/main/java/com/jxy/windminer/common/security/config/WebMvcConfig.java create mode 100644 src/main/java/com/jxy/windminer/common/security/handler/GlobalExceptionHandler.java create mode 100644 src/main/java/com/jxy/windminer/common/security/interceptor/HeaderInterceptor.java create mode 100644 src/main/java/com/jxy/windminer/common/security/service/TokenService.java create mode 100644 src/main/java/com/jxy/windminer/common/security/utils/SecurityUtils.java create mode 100644 src/main/java/com/jxy/windminer/common/text/CharsetKit.java create mode 100644 src/main/java/com/jxy/windminer/common/text/Convert.java create mode 100644 src/main/java/com/jxy/windminer/common/text/StrFormatter.java create mode 100644 src/main/java/com/jxy/windminer/common/utils/CodeUtils.java create mode 100644 src/main/java/com/jxy/windminer/common/utils/DateUtils.java create mode 100644 src/main/java/com/jxy/windminer/common/utils/ExceptionUtil.java create mode 100644 src/main/java/com/jxy/windminer/common/utils/FileUploadUtils.java create mode 100644 src/main/java/com/jxy/windminer/common/utils/JwtUtils.java create mode 100644 src/main/java/com/jxy/windminer/common/utils/MysqlBackUpUtils.java create mode 100644 src/main/java/com/jxy/windminer/common/utils/PageUtils.java create mode 100644 src/main/java/com/jxy/windminer/common/utils/PwdUtils.java create mode 100644 src/main/java/com/jxy/windminer/common/utils/ServletUtils.java create mode 100644 src/main/java/com/jxy/windminer/common/utils/SpringUtils.java create mode 100644 src/main/java/com/jxy/windminer/common/utils/StringUtils.java create mode 100644 src/main/java/com/jxy/windminer/common/utils/WalletUils.java create mode 100644 src/main/java/com/jxy/windminer/common/utils/WalletVerifyUtils.java create mode 100644 src/main/java/com/jxy/windminer/common/utils/bean/BeanUtils.java create mode 100644 src/main/java/com/jxy/windminer/common/utils/bean/BeanValidators.java create mode 100644 src/main/java/com/jxy/windminer/common/utils/file/FileTypeUtils.java create mode 100644 src/main/java/com/jxy/windminer/common/utils/file/FileUtils.java create mode 100644 src/main/java/com/jxy/windminer/common/utils/file/ImageUtils.java create mode 100644 src/main/java/com/jxy/windminer/common/utils/file/MimeTypeUtils.java create mode 100644 src/main/java/com/jxy/windminer/common/utils/html/EscapeUtil.java create mode 100644 src/main/java/com/jxy/windminer/common/utils/html/HTMLFilter.java create mode 100644 src/main/java/com/jxy/windminer/common/utils/ip/IpUtils.java create mode 100644 src/main/java/com/jxy/windminer/common/utils/poi/ExcelHandlerAdapter.java create mode 100644 src/main/java/com/jxy/windminer/common/utils/poi/ExcelUtil.java create mode 100644 src/main/java/com/jxy/windminer/common/utils/reflect/ReflectUtils.java create mode 100644 src/main/java/com/jxy/windminer/common/utils/sign/Base64.java create mode 100644 src/main/java/com/jxy/windminer/common/utils/sign/RsaUtils.java create mode 100644 src/main/java/com/jxy/windminer/common/utils/sql/SqlUtil.java create mode 100644 src/main/java/com/jxy/windminer/common/utils/uuid/IdUtils.java create mode 100644 src/main/java/com/jxy/windminer/common/utils/uuid/Seq.java create mode 100644 src/main/java/com/jxy/windminer/common/utils/uuid/UUID.java create mode 100644 src/main/java/com/jxy/windminer/common/web/Result/AjaxResult.java create mode 100644 src/main/java/com/jxy/windminer/common/web/Result/PageResult.java create mode 100644 src/main/java/com/jxy/windminer/common/web/controller/BaseController.java create mode 100644 src/main/java/com/jxy/windminer/common/web/entity/BaseEntity.java create mode 100644 src/main/java/com/jxy/windminer/common/web/page/Page.java create mode 100644 src/main/java/com/jxy/windminer/common/web/page/TableDataInfo.java create mode 100644 src/main/java/com/jxy/windminer/common/web/page/TableSupport.java create mode 100644 src/main/java/com/jxy/windminer/common/xss/Xss.java create mode 100644 src/main/java/com/jxy/windminer/common/xss/XssValidator.java create mode 100644 src/main/java/com/jxy/windminer/config/ResourcesConfig.java create mode 100644 src/main/java/com/jxy/windminer/controller/OrderController.java create mode 100644 src/main/java/com/jxy/windminer/controller/SysFileController.java create mode 100644 src/main/java/com/jxy/windminer/controller/SysProfileController.java create mode 100644 src/main/java/com/jxy/windminer/controller/TicketController.java create mode 100644 src/main/java/com/jxy/windminer/controller/TokenController.java create mode 100644 src/main/java/com/jxy/windminer/dto/AccountDetailsDto.java create mode 100644 src/main/java/com/jxy/windminer/dto/AddressOrderDetailDto.java create mode 100644 src/main/java/com/jxy/windminer/dto/AddressUsedDto.java create mode 100644 src/main/java/com/jxy/windminer/dto/AllTicketListDto.java create mode 100644 src/main/java/com/jxy/windminer/dto/BaseRepairInfoDto.java create mode 100644 src/main/java/com/jxy/windminer/dto/BaseTicketDto.java create mode 100644 src/main/java/com/jxy/windminer/dto/CallBackDto.java create mode 100644 src/main/java/com/jxy/windminer/dto/ConfirmListDto.java create mode 100644 src/main/java/com/jxy/windminer/dto/FaultsDto.java create mode 100644 src/main/java/com/jxy/windminer/dto/FileDto.java create mode 100644 src/main/java/com/jxy/windminer/dto/GuaranteeDto.java create mode 100644 src/main/java/com/jxy/windminer/dto/IdsDto.java create mode 100644 src/main/java/com/jxy/windminer/dto/OnChainMsgByAddressDto.java create mode 100644 src/main/java/com/jxy/windminer/dto/OnChainQueryResultDto.java create mode 100644 src/main/java/com/jxy/windminer/dto/OrderAndOrderLogisticalDto.java create mode 100644 src/main/java/com/jxy/windminer/dto/OrderConfirmDto.java create mode 100644 src/main/java/com/jxy/windminer/dto/OrderInfoDto.java create mode 100644 src/main/java/com/jxy/windminer/dto/OrderInfoReturnDto.java create mode 100644 src/main/java/com/jxy/windminer/dto/PaymentDto.java create mode 100644 src/main/java/com/jxy/windminer/dto/PrivateTicketListDto.java create mode 100644 src/main/java/com/jxy/windminer/dto/RepairRecordDto.java create mode 100644 src/main/java/com/jxy/windminer/dto/RepairTicketInfoDto.java create mode 100644 src/main/java/com/jxy/windminer/dto/ReturnInfoDto.java create mode 100644 src/main/java/com/jxy/windminer/dto/SysFileDto.java create mode 100644 src/main/java/com/jxy/windminer/dto/SysToolsDto.java create mode 100644 src/main/java/com/jxy/windminer/dto/TicketAcListDto.java create mode 100644 src/main/java/com/jxy/windminer/dto/TicketAuditDto.java create mode 100644 src/main/java/com/jxy/windminer/dto/TicketContentDto.java create mode 100644 src/main/java/com/jxy/windminer/dto/TicketDailyCountDto.java create mode 100644 src/main/java/com/jxy/windminer/dto/TicketDetails3Dto.java create mode 100644 src/main/java/com/jxy/windminer/dto/TicketDetailsDto.java create mode 100644 src/main/java/com/jxy/windminer/dto/TicketDto.java create mode 100644 src/main/java/com/jxy/windminer/dto/TicketListDto.java create mode 100644 src/main/java/com/jxy/windminer/dto/TicketOrderBaseInfoDto.java create mode 100644 src/main/java/com/jxy/windminer/dto/TicketOrderPhoneAndDetailsDto.java create mode 100644 src/main/java/com/jxy/windminer/dto/TicketPaymentDto.java create mode 100644 src/main/java/com/jxy/windminer/dto/TicketPrivateDto.java create mode 100644 src/main/java/com/jxy/windminer/dto/TicketResponDto.java create mode 100644 src/main/java/com/jxy/windminer/dto/TotalAddressDto.java create mode 100644 src/main/java/com/jxy/windminer/dto/WPOrderAddressDto.java create mode 100644 src/main/java/com/jxy/windminer/dto/WPOrderProductDto.java create mode 100644 src/main/java/com/jxy/windminer/entity/JXYFile.java create mode 100644 src/main/java/com/jxy/windminer/entity/OrderKeyAddress.java create mode 100644 src/main/java/com/jxy/windminer/entity/RepairRecord.java create mode 100644 src/main/java/com/jxy/windminer/entity/SysFile.java create mode 100644 src/main/java/com/jxy/windminer/entity/Test.java create mode 100644 src/main/java/com/jxy/windminer/entity/Ticket.java create mode 100644 src/main/java/com/jxy/windminer/entity/TicketSupplement.java create mode 100644 src/main/java/com/jxy/windminer/entity/ToolsFile.java create mode 100644 src/main/java/com/jxy/windminer/entity/auth/EmailCodeEntity.java create mode 100644 src/main/java/com/jxy/windminer/entity/auth/EmailEntity.java create mode 100644 src/main/java/com/jxy/windminer/entity/auth/GetEmailCodeEntity.java create mode 100644 src/main/java/com/jxy/windminer/entity/auth/LoginBody.java create mode 100644 src/main/java/com/jxy/windminer/entity/auth/LoginUser.java create mode 100644 src/main/java/com/jxy/windminer/entity/auth/RegisterBody.java create mode 100644 src/main/java/com/jxy/windminer/entity/auth/ResetPwdBody.java create mode 100644 src/main/java/com/jxy/windminer/entity/auth/WpLoginUser.java create mode 100644 src/main/java/com/jxy/windminer/entity/system/SysDept.java create mode 100644 src/main/java/com/jxy/windminer/entity/system/SysLogininfor.java create mode 100644 src/main/java/com/jxy/windminer/entity/system/SysMenu.java create mode 100644 src/main/java/com/jxy/windminer/entity/system/SysOperLog.java create mode 100644 src/main/java/com/jxy/windminer/entity/system/SysRole.java create mode 100644 src/main/java/com/jxy/windminer/entity/system/SysRoleDto.java create mode 100644 src/main/java/com/jxy/windminer/entity/system/SysRoleMenu.java create mode 100644 src/main/java/com/jxy/windminer/entity/system/SysUser.java create mode 100644 src/main/java/com/jxy/windminer/entity/system/SysUserDto.java create mode 100644 src/main/java/com/jxy/windminer/entity/system/SysUserRole.java create mode 100644 src/main/java/com/jxy/windminer/enums/AccessoryStatus.java create mode 100644 src/main/java/com/jxy/windminer/enums/OrderFrom.java create mode 100644 src/main/java/com/jxy/windminer/enums/PayStatus.java create mode 100644 src/main/java/com/jxy/windminer/enums/TheWindMinerLogisticalInfo.java create mode 100644 src/main/java/com/jxy/windminer/enums/TicketStatusType.java create mode 100644 src/main/java/com/jxy/windminer/enums/TicketType.java create mode 100644 src/main/java/com/jxy/windminer/enums/WCOrderStatus.java create mode 100644 src/main/java/com/jxy/windminer/filter/XssFilter.java create mode 100644 src/main/java/com/jxy/windminer/filter/XssHttpServletRequestWrapper.java create mode 100644 src/main/java/com/jxy/windminer/mapper/OrderMapper.java create mode 100644 src/main/java/com/jxy/windminer/mapper/SysFileMapper.java create mode 100644 src/main/java/com/jxy/windminer/mapper/SysLogininforMapper.java create mode 100644 src/main/java/com/jxy/windminer/mapper/SysMenuMapper.java create mode 100644 src/main/java/com/jxy/windminer/mapper/SysOperLogMapper.java create mode 100644 src/main/java/com/jxy/windminer/mapper/SysRoleMapper.java create mode 100644 src/main/java/com/jxy/windminer/mapper/SysRoleMenuMapper.java create mode 100644 src/main/java/com/jxy/windminer/mapper/SysUserMapper.java create mode 100644 src/main/java/com/jxy/windminer/mapper/SysUserRoleMapper.java create mode 100644 src/main/java/com/jxy/windminer/mapper/TicketMapper.java create mode 100644 src/main/java/com/jxy/windminer/service/FileService.java create mode 100644 src/main/java/com/jxy/windminer/service/ISysFileService.java create mode 100644 src/main/java/com/jxy/windminer/service/LocalSysFileServiceImpl.java create mode 100644 src/main/java/com/jxy/windminer/service/MailService.java create mode 100644 src/main/java/com/jxy/windminer/service/OrderService.java create mode 100644 src/main/java/com/jxy/windminer/service/SysFileService.java create mode 100644 src/main/java/com/jxy/windminer/service/SysLoginService.java create mode 100644 src/main/java/com/jxy/windminer/service/SysLogininforService.java create mode 100644 src/main/java/com/jxy/windminer/service/SysMenuService.java create mode 100644 src/main/java/com/jxy/windminer/service/SysOperLogService.java create mode 100644 src/main/java/com/jxy/windminer/service/SysPermissionService.java create mode 100644 src/main/java/com/jxy/windminer/service/SysRoleService.java create mode 100644 src/main/java/com/jxy/windminer/service/SysUserService.java create mode 100644 src/main/java/com/jxy/windminer/service/TicketService.java create mode 100644 src/main/java/com/jxy/windminer/service/impl/FileServiceImpl.java create mode 100644 src/main/java/com/jxy/windminer/service/impl/MaliServiceImpl.java create mode 100644 src/main/java/com/jxy/windminer/service/impl/OrderServiceImpl.java create mode 100644 src/main/java/com/jxy/windminer/service/impl/SysFileServiceImpl.java create mode 100644 src/main/java/com/jxy/windminer/service/impl/SysLogininforServiceImpl.java create mode 100644 src/main/java/com/jxy/windminer/service/impl/SysMenuServiceImpl.java create mode 100644 src/main/java/com/jxy/windminer/service/impl/SysOperLogServiceImpl.java create mode 100644 src/main/java/com/jxy/windminer/service/impl/SysPermissionServiceImpl.java create mode 100644 src/main/java/com/jxy/windminer/service/impl/SysRoleServiceImpl.java create mode 100644 src/main/java/com/jxy/windminer/service/impl/SysUserServiceImpl.java create mode 100644 src/main/java/com/jxy/windminer/service/impl/TicketServiceImpl.java create mode 100644 src/main/java/com/jxy/windminer/task/OrderTask.java create mode 100644 src/main/java/com/jxy/windminer/vo/AccountConfirmVo.java create mode 100644 src/main/java/com/jxy/windminer/vo/AddRepairRecordVo.java create mode 100644 src/main/java/com/jxy/windminer/vo/AddressUsedVo.java create mode 100644 src/main/java/com/jxy/windminer/vo/AddressVo.java create mode 100644 src/main/java/com/jxy/windminer/vo/AllTicketListVo.java create mode 100644 src/main/java/com/jxy/windminer/vo/BaseRepairVo.java create mode 100644 src/main/java/com/jxy/windminer/vo/ChangeTypeVo.java create mode 100644 src/main/java/com/jxy/windminer/vo/ConfirmListVo.java create mode 100644 src/main/java/com/jxy/windminer/vo/ConfirmTypeVo.java create mode 100644 src/main/java/com/jxy/windminer/vo/ConfirmVo.java create mode 100644 src/main/java/com/jxy/windminer/vo/EndTicketVo.java create mode 100644 src/main/java/com/jxy/windminer/vo/IdVo.java create mode 100644 src/main/java/com/jxy/windminer/vo/OrderApplyVo.java create mode 100644 src/main/java/com/jxy/windminer/vo/OrderHandleVo.java create mode 100644 src/main/java/com/jxy/windminer/vo/OrderKeyVo.java create mode 100644 src/main/java/com/jxy/windminer/vo/OrderLogisticsConfirmVo.java create mode 100644 src/main/java/com/jxy/windminer/vo/OrderUpdateVo.java create mode 100644 src/main/java/com/jxy/windminer/vo/PaymentInfoVo.java create mode 100644 src/main/java/com/jxy/windminer/vo/PaymentVo.java create mode 100644 src/main/java/com/jxy/windminer/vo/PrivateTicketListVo.java create mode 100644 src/main/java/com/jxy/windminer/vo/QueryVo.java create mode 100644 src/main/java/com/jxy/windminer/vo/QuotedDetailsVo.java create mode 100644 src/main/java/com/jxy/windminer/vo/QuotedVo.java create mode 100644 src/main/java/com/jxy/windminer/vo/QuothMachinesVo.java create mode 100644 src/main/java/com/jxy/windminer/vo/RepairChangeStatusVo.java create mode 100644 src/main/java/com/jxy/windminer/vo/RepairConfirmVo.java create mode 100644 src/main/java/com/jxy/windminer/vo/RepairRecordVo.java create mode 100644 src/main/java/com/jxy/windminer/vo/RepairRefuseVo.java create mode 100644 src/main/java/com/jxy/windminer/vo/ResponTicketVo.java create mode 100644 src/main/java/com/jxy/windminer/vo/ResubmitTicketVo.java create mode 100644 src/main/java/com/jxy/windminer/vo/ReturnLogisticalVo.java create mode 100644 src/main/java/com/jxy/windminer/vo/SerialVo.java create mode 100644 src/main/java/com/jxy/windminer/vo/StatusVo.java create mode 100644 src/main/java/com/jxy/windminer/vo/SysToolsVo.java create mode 100644 src/main/java/com/jxy/windminer/vo/TicketVo.java create mode 100644 src/main/java/com/jxy/windminer/vo/TotalAddressVo.java create mode 100644 src/main/java/com/jxy/windminer/vo/UserLogisticalVo.java create mode 100644 src/main/java/com/jxy/windminer/vo/responSupportVo.java create mode 100644 src/main/resources/META-INF/MANIFEST.MF create mode 100644 src/main/resources/META-INF/spring.factories create mode 100644 src/main/resources/application-test.properties create mode 100644 src/main/resources/application.properties create mode 100644 src/main/resources/mapper/windminer/OrderMapper.xml create mode 100644 src/main/resources/mapper/windminer/SysFileMapper.xml create mode 100644 src/main/resources/mapper/windminer/SysLogininforMapper.xml create mode 100644 src/main/resources/mapper/windminer/SysMenuMapper.xml create mode 100644 src/main/resources/mapper/windminer/SysOperLogMapper.xml create mode 100644 src/main/resources/mapper/windminer/SysRoleMapper.xml create mode 100644 src/main/resources/mapper/windminer/SysRoleMenuMapper.xml create mode 100644 src/main/resources/mapper/windminer/SysUserMapper.xml create mode 100644 src/main/resources/mapper/windminer/SysUserRoleMapper.xml create mode 100644 src/main/resources/mapper/windminer/TicketMapper.xml diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..857dbcb --- /dev/null +++ b/pom.xml @@ -0,0 +1,425 @@ + + + + 4.0.0 + com.wind + windminer + 3.5.0 + jar + 追风矿机工单系统 + + + + org.springframework.cloud + spring-cloud-starter-bootstrap + + + cn.hutool + hutool-all + 5.6.2 + + + org.springframework.boot + spring-boot-starter-web + + + + + org.springframework.boot + spring-boot-dependencies + ${spring-boot.version} + pom + import + + + + + de.codecentric + spring-boot-admin-starter-client + ${spring-boot-admin.version} + + + + + + + + + + com.github.tobato + fastdfs-client + ${tobato.version} + + + spring-beans + org.springframework + + + + + + + com.baomidou + mybatis-plus-boot-starter + ${mybatis-plus.version} + + + spring-core + org.springframework + + + spring-beans + org.springframework + + + jsqlparser + com.github.jsqlparser + + + mybatis + org.mybatis + + + mybatis-spring + org.mybatis + + + + + + + io.swagger + swagger-models + ${swagger.core.version} + + + io.swagger + swagger-annotations + ${swagger.core.version} + + + + + com.github.penggle + kaptcha + ${kaptcha.version} + + + + + com.github.pagehelper + pagehelper-spring-boot-starter + ${pagehelper.boot.version} + + + spring-core + org.springframework + + + + + + + commons-io + commons-io + ${commons.io.version} + + + + + org.apache.poi + poi-ooxml + ${poi.version} + + + + + commons-fileupload + commons-fileupload + ${commons.fileupload.version} + + + + + + commons-collections + commons-collections + ${commons-collections.version} + + + + + com.alibaba + fastjson + ${fastjson.version} + + + + + io.jsonwebtoken + jjwt + ${jjwt.version} + + + + + com.alibaba + transmittable-thread-local + ${transmittable-thread-local.version} + + + + + org.projectlombok + lombok-maven-plugin + ${lombok-maven-plugin.version} + provided + + + + + org.apache.commons + commons-lang3 + 3.12.0 + + + + mysql + mysql-connector-java + 8.0.26 + + + + + org.springframework.boot + spring-boot-starter-data-redis + + + + + org.springframework.boot + spring-boot-starter-mail + + + + org.springframework + spring-webmvc + 5.3.19 + + + spring-core + org.springframework + + + + + + org.aspectj + aspectjweaver + 1.9.7 + + + + + + windminer + + + 1.8 + 3.5.0 + UTF-8 + UTF-8 + 2.6.7 + 2021.0.1 + 2021.0.1.0 + 2.0.4 + 2.6.6 + 2.2.2 + 3.0.0 + 1.6.2 + + 1.27.2 + 3.3.0 + 2.3.2 + 1.4.1 + 2.11.0 + 1.4 + + 2.3 + 1.2.80 + 0.9.1 + 8.2.2 + 4.1.2 + 3.2.2 + 2.12.2 + 1.18.6.0 + + + + + + + + + org.springframework.cloud + spring-cloud-dependencies + ${spring-cloud.version} + pom + import + + + + + com.alibaba.cloud + spring-cloud-alibaba-dependencies + ${spring-cloud-alibaba.version} + pom + import + + + + + org.springframework.boot + spring-boot-dependencies + ${spring-boot.version} + pom + import + + + + + de.codecentric + spring-boot-admin-starter-client + ${spring-boot-admin.version} + + + + + com.github.tobato + fastdfs-client + ${tobato.version} + + + + + com.baomidou + mybatis-plus-boot-starter + ${mybatis-plus.version} + + + + + io.swagger + swagger-models + ${swagger.core.version} + + + io.swagger + swagger-annotations + ${swagger.core.version} + + + + + com.github.penggle + kaptcha + ${kaptcha.version} + + + + + com.github.pagehelper + pagehelper-spring-boot-starter + ${pagehelper.boot.version} + + + + + commons-io + commons-io + ${commons.io.version} + + + + + org.apache.poi + poi-ooxml + ${poi.version} + + + + + commons-fileupload + commons-fileupload + ${commons.fileupload.version} + + + + + + commons-collections + commons-collections + ${commons-collections.version} + + + + + com.alibaba + fastjson + ${fastjson.version} + + + + + io.jsonwebtoken + jjwt + ${jjwt.version} + + + + + com.alibaba + transmittable-thread-local + ${transmittable-thread-local.version} + + + + + org.projectlombok + lombok-maven-plugin + ${lombok-maven-plugin.version} + provided + + + + + + + windminer + + + org.apache.maven.plugins + maven-compiler-plugin + 3.1 + + ${java.version} + ${java.version} + ${project.build.sourceEncoding} + + + + org.springframework.boot + spring-boot-maven-plugin + + + + repackage + + + + + + + + diff --git a/src/main/java/com/jxy/windminer/WindminerApplication.java b/src/main/java/com/jxy/windminer/WindminerApplication.java new file mode 100644 index 0000000..0c8f173 --- /dev/null +++ b/src/main/java/com/jxy/windminer/WindminerApplication.java @@ -0,0 +1,27 @@ +package com.jxy.windminer; + +import com.jxy.windminer.common.security.annotation.EnableCustomConfig; +import com.jxy.windminer.common.security.config.ApplicationConfig; +import org.mybatis.spring.annotation.MapperScan; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.Import; +import org.springframework.scheduling.annotation.EnableAsync; + +/** + * @Description TODO + * @Date 2023/9/12 15:00 + * @Author 杜懿 + */ +@SpringBootApplication +@EnableAsync +// 自动加载类 +@Import({ ApplicationConfig.class}) +@MapperScan("com.jxy.windminer.mapper") +public class WindminerApplication { + + public static void main(String[] args) { + SpringApplication.run(WindminerApplication.class,args); + System.out.println("windminer微服务启动成功"); + } +} diff --git a/src/main/java/com/jxy/windminer/annotation/Excel.java b/src/main/java/com/jxy/windminer/annotation/Excel.java new file mode 100644 index 0000000..901a4b2 --- /dev/null +++ b/src/main/java/com/jxy/windminer/annotation/Excel.java @@ -0,0 +1,163 @@ +package com.jxy.windminer.annotation; + +import com.jxy.windminer.common.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 + */ +@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 {}; + + /** + * 是否导出数据,应对需求:有时我们需要导出一份模板,这是标题需要但内容需要用户手工填写. + */ + public boolean isExport() default true; + + /** + * 另一个类中的属性名称,支持多级获取,以小数点隔开 + */ + public String targetAttr() default ""; + + /** + * 是否自动统计数据,在最后追加一行统计数据总和 + */ + public boolean isStatistics() default false; + + /** + * 导出类型(0数字 1字符串) + */ + public ColumnType cellType() default ColumnType.STRING; + + /** + * 导出字体颜色 + */ + 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/jxy/windminer/annotation/Excels.java b/src/main/java/com/jxy/windminer/annotation/Excels.java new file mode 100644 index 0000000..ed3c45d --- /dev/null +++ b/src/main/java/com/jxy/windminer/annotation/Excels.java @@ -0,0 +1,18 @@ +package com.jxy.windminer.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Excel注解集 + * + * @author 杜懿 + */ +@Target(ElementType.FIELD) +@Retention(RetentionPolicy.RUNTIME) +public @interface Excels +{ + Excel[] value(); +} diff --git a/src/main/java/com/jxy/windminer/annotation/ParamName.java b/src/main/java/com/jxy/windminer/annotation/ParamName.java new file mode 100644 index 0000000..7bf32d8 --- /dev/null +++ b/src/main/java/com/jxy/windminer/annotation/ParamName.java @@ -0,0 +1,21 @@ +package com.jxy.windminer.annotation; + +import java.lang.annotation.*; + +/** + * @Description TODO + * @Date 2022/7/8 18:24 + * @Author 杜懿 + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(value={ElementType.FIELD}) +@Documented +@Inherited +public @interface ParamName { + /** + * 参数名称 + * + * @return + */ + String value(); +} diff --git a/src/main/java/com/jxy/windminer/annotation/RateLimiter.java b/src/main/java/com/jxy/windminer/annotation/RateLimiter.java new file mode 100644 index 0000000..9540a10 --- /dev/null +++ b/src/main/java/com/jxy/windminer/annotation/RateLimiter.java @@ -0,0 +1,30 @@ +package com.jxy.windminer.annotation; + + +import com.jxy.windminer.common.constant.CacheConstants; +import com.jxy.windminer.common.enums.LimitType; + +import java.lang.annotation.*; + +/** + * @Description TODO + * @Date 2022/11/30 17:28 + * @Author 杜懿 + */ +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface RateLimiter +{ + // 限流key + public String key() default CacheConstants.RATE_LIMIT_KEY; + + // 限流时间,单位秒 + public int time() default 30; + + // 限流次数 + public int count() default 30; + + // 限流类型 + public LimitType limitType() default LimitType.DEFAULT; +} diff --git a/src/main/java/com/jxy/windminer/common/RedisTransKey.java b/src/main/java/com/jxy/windminer/common/RedisTransKey.java new file mode 100644 index 0000000..2d5ad7a --- /dev/null +++ b/src/main/java/com/jxy/windminer/common/RedisTransKey.java @@ -0,0 +1,44 @@ +package com.jxy.windminer.common; + +/** + * @Description TODO + * @Date 2022/11/18 15:35 + * @Author 杜懿 + */ +public class RedisTransKey { + + + public static final String RedisNameSpace="user"; + public static final String RedisTokenName="token"; + public static final String RedisLoginName="login"; + public static final String RedisEmailCodeName="emailCode"; + public static final String RedisAddCreditEmailCodeName="addCreditEmailCode"; + public static final String RedisResetPwdCodeName="restPwdCode"; + + public static String setEmailKey(String key){ + return RedisNameSpace+":"+RedisEmailCodeName+":"+key; + } + public static String setRootKey(String key){ + return RedisNameSpace+":"+key+":"; + } + public static String setTokenKey(String key){ + return RedisNameSpace+':'+RedisTokenName+":"+key; + } + public static String setLoginKey(String key){ + return RedisNameSpace+':'+RedisLoginName+":"+key; + } + public static String setResetPwdKey(String key){ + return RedisNameSpace+":"+RedisResetPwdCodeName+":"+key; + } + public static String setAddCreditEmailKey(String key){ + return RedisNameSpace+":"+RedisAddCreditEmailCodeName+":"+key; + } + + public static String getEmailKey(String key){return setEmailKey(key);} + public static String getRootKey(String key){return setRootKey(key);} + public static String getTokenKey(String key){return setTokenKey(key);} + public static String getLoginKey(String key){return setLoginKey(key);} + public static String getResetPwdKey(String key){return setResetPwdKey(key);} + public static String getAddCreditEmailKey(String key){return setAddCreditEmailKey(key);} + +} diff --git a/src/main/java/com/jxy/windminer/common/Result/R.java b/src/main/java/com/jxy/windminer/common/Result/R.java new file mode 100644 index 0000000..c9ba339 --- /dev/null +++ b/src/main/java/com/jxy/windminer/common/Result/R.java @@ -0,0 +1,78 @@ +package com.jxy.windminer.common.Result; + +import com.jxy.windminer.common.constant.Constants; +import lombok.Data; + +import java.io.Serializable; + +/** + * @Description 响应信息主体 + * @Date 2022/5/10 9:18 + * @Author 杜懿 + */ +@Data +public class R implements Serializable +{ + private static final long serialVersionUID = 3276842119125241681L; + + /** 成功 */ + public static final int SUCCESS = Constants.SUCCESS; + + /** 失败 */ + public static final int FAIL = Constants.FAIL; + + private int code; + + private String msg; + + private T data; + + public static R success() + { + return restResult(null, SUCCESS, null); + } + + public static R success(T data) + { + return restResult(data, SUCCESS, null); + } + + public static R success(T data, String msg) + { + return restResult(data, SUCCESS, msg); + } + + public static R fail() + { + return restResult(null, FAIL, null); + } + + public static R fail(String msg) + { + return restResult(null, FAIL, msg); + } + + public static R fail(T data) + { + return restResult(data, FAIL, null); + } + + public static R fail(T data, String msg) + { + return restResult(data, FAIL, msg); + } + + public static R fail(int code, String msg) + { + return restResult(null, code, msg); + } + + private static R restResult(T data, int code, String msg) + { + R apiResult = new R<>(); + apiResult.setCode(code); + apiResult.setData(data); + apiResult.setMsg(msg); + return apiResult; + } +} diff --git a/src/main/java/com/jxy/windminer/common/constant/CacheConstants.java b/src/main/java/com/jxy/windminer/common/constant/CacheConstants.java new file mode 100644 index 0000000..8ae356d --- /dev/null +++ b/src/main/java/com/jxy/windminer/common/constant/CacheConstants.java @@ -0,0 +1,34 @@ +package com.jxy.windminer.common.constant; + +/** + * @Description 缓存的key + * @Date 2022/5/10 17:22 + * @Author 杜懿 + */ +public class CacheConstants +{ + /** + * 缓存有效期,默认720(分钟) + */ + public final static long EXPIRATION = 720 * 2 *30; + + /** + * 缓存刷新时间,默认120(分钟)* 12 + */ + public final static long REFRESH_TIME = 120 * 12; + + /** + * 权限缓存前缀 + */ + public final static String LOGIN_TOKEN_KEY = "login_tokens:"; + + /** + * 防重提交 redis key + */ + public static final String REPEAT_SUBMIT_KEY = "repeat_submit:"; + + /** + * 限流 redis key + */ + public static final String RATE_LIMIT_KEY = "rate_limit:"; +} diff --git a/src/main/java/com/jxy/windminer/common/constant/Constants.java b/src/main/java/com/jxy/windminer/common/constant/Constants.java new file mode 100644 index 0000000..8c2e8cd --- /dev/null +++ b/src/main/java/com/jxy/windminer/common/constant/Constants.java @@ -0,0 +1,136 @@ +package com.jxy.windminer.common.constant; + +/** + * @Description 常量信息 + * @Date 2022/5/9 18:13 + * @Author 杜懿 + */ +public class Constants { + + /** + * UTF-8 字符集 + */ + public static final String UTF8 = "UTF-8"; + + /** + * GBK 字符集 + */ + public static final String GBK = "GBK"; + + /** + * 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"; + + /** + * 验证码 redis key + */ + public static final String CAPTCHA_CODE_KEY = "captcha_codes:"; + + /** + * 验证码有效期(分钟) + */ + public static final long CAPTCHA_EXPIRATION = 2; + + + /** + * 参数管理 cache key + */ + public static final String SYS_CONFIG_KEY = "sys_config:"; + + /** + * 资源映射路径 前缀 + */ + public static final String RESOURCE_PREFIX = "/profile"; + + /** + * 维修订单key前缀 + */ + public static final String TICKET_ORDER_PREFIX = "tk_order_"; + + /** + * 商城订单key前缀 + */ + public static final String WORDPRESS_ORDER_PREFIX = "wc_order_"; + +} diff --git a/src/main/java/com/jxy/windminer/common/constant/HttpStatus.java b/src/main/java/com/jxy/windminer/common/constant/HttpStatus.java new file mode 100644 index 0000000..052b27b --- /dev/null +++ b/src/main/java/com/jxy/windminer/common/constant/HttpStatus.java @@ -0,0 +1,99 @@ +package com.jxy.windminer.common.constant; + +/** + * @Description 返回状态码 + * @Date 2022/5/10 11:35 + * @Author 杜懿 + */ +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 LOGINEXPIRES = 421; + + /** + * 登陆过期 + */ + public static final int TOKENEXPIRES = 422; + + /** + * 访问受限,授权过期 + */ + 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; +} diff --git a/src/main/java/com/jxy/windminer/common/constant/OrderConstants.java b/src/main/java/com/jxy/windminer/common/constant/OrderConstants.java new file mode 100644 index 0000000..bb603fc --- /dev/null +++ b/src/main/java/com/jxy/windminer/common/constant/OrderConstants.java @@ -0,0 +1,16 @@ +package com.jxy.windminer.common.constant; + +/** + * @Description Order相关常量 + * @Date 2022/5/10 16:12 + * @Author 杜懿 + */ +public class OrderConstants { + + + /** + * REDIS前缀 + */ + public static final String TOKEN_PRE = "userLoginToken::"; + +} diff --git a/src/main/java/com/jxy/windminer/common/constant/SecurityConstants.java b/src/main/java/com/jxy/windminer/common/constant/SecurityConstants.java new file mode 100644 index 0000000..6b356f5 --- /dev/null +++ b/src/main/java/com/jxy/windminer/common/constant/SecurityConstants.java @@ -0,0 +1,49 @@ +package com.jxy.windminer.common.constant; + +/** + * @Description 权限相关通用常量 + * @Date 2022/5/10 16:19 + * @Author 杜懿 + */ +public class SecurityConstants { + + /** + * 用户ID字段 + */ + public static final String DETAILS_USER_ID = "user_id"; + + /** + * 用户名字段 + */ + public static final String DETAILS_USERNAME = "username"; + + /** + * 用户名字段 + */ + public static final String DETAILS_EMAIL = "email"; + /** + * 授权信息字段 + */ + 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"; + +} diff --git a/src/main/java/com/jxy/windminer/common/constant/TokenConstants.java b/src/main/java/com/jxy/windminer/common/constant/TokenConstants.java new file mode 100644 index 0000000..7d7fcef --- /dev/null +++ b/src/main/java/com/jxy/windminer/common/constant/TokenConstants.java @@ -0,0 +1,26 @@ +package com.jxy.windminer.common.constant; + +/** + * @Description Token的Key常量 + * @Date 2022/5/10 16:12 + * @Author 杜懿 + */ +public class TokenConstants { + + /** + * 令牌自定义标识 + */ + public static final String AUTHENTICATION = "Authorization"; + + /** + * 令牌前缀 + */ + public static final String PREFIX = "Bearer "; + + /** + * 令牌秘钥 + */ + //TODO 根据情况更改密钥 + public final static String SECRET = "aatbbhcceddweeiffnggdhhmiiijjnkkellrmm"; + +} diff --git a/src/main/java/com/jxy/windminer/common/constant/UserConstants.java b/src/main/java/com/jxy/windminer/common/constant/UserConstants.java new file mode 100644 index 0000000..73c0b14 --- /dev/null +++ b/src/main/java/com/jxy/windminer/common/constant/UserConstants.java @@ -0,0 +1,81 @@ +package com.jxy.windminer.common.constant; + +/** + * @Description 用户相关常量 + * @Date 2022/5/10 17:39 + * @Author 杜懿 + */ +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 String UNIQUE = "0"; + + public final static String NOT_UNIQUE = "1"; + + /** + * 用户名长度限制 + */ + public static final int USERNAME_MIN_LENGTH = 3; + + public static final int USERNAME_MAX_LENGTH = 16; + + /** + * 密码长度限制 + */ + public static final int PASSWORD_MIN_LENGTH = 6; + + public static final int PASSWORD_MAX_LENGTH = 32; +} diff --git a/src/main/java/com/jxy/windminer/common/context/SecurityContextHolder.java b/src/main/java/com/jxy/windminer/common/context/SecurityContextHolder.java new file mode 100644 index 0000000..62405cf --- /dev/null +++ b/src/main/java/com/jxy/windminer/common/context/SecurityContextHolder.java @@ -0,0 +1,98 @@ +package com.jxy.windminer.common.context; + +import com.alibaba.ttl.TransmittableThreadLocal; +import com.jxy.windminer.common.constant.SecurityConstants; +import com.jxy.windminer.common.text.Convert; +import com.jxy.windminer.common.utils.StringUtils; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** +* @Description 获取当前线程变量中的 用户id、用户名称、Token等信息 +* @Date 2022/5/10 11:32 +* @Author 杜懿 +*/ +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 getEmail() + { + return get(SecurityConstants.DETAILS_EMAIL); + } + + public static void setEmail(String email) + { + set(SecurityConstants.DETAILS_EMAIL, email); + } + + public static String getUserKey() + { + return get(SecurityConstants.USER_KEY); + } + + public static void setUserKey(String userKey) + { + set(SecurityConstants.USER_KEY, userKey); + } + + public static void remove() + { + THREAD_LOCAL.remove(); + } +} diff --git a/src/main/java/com/jxy/windminer/common/datascope/annotation/DataScope.java b/src/main/java/com/jxy/windminer/common/datascope/annotation/DataScope.java new file mode 100644 index 0000000..a2c3e27 --- /dev/null +++ b/src/main/java/com/jxy/windminer/common/datascope/annotation/DataScope.java @@ -0,0 +1,25 @@ +package com.jxy.windminer.common.datascope.annotation; + +import java.lang.annotation.*; + +/** + * @Description 数据权限过滤注解 + * @Date 2022/5/16 15:13 + * @Author 杜懿 + */ +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface DataScope { + + /** + * 部门表的别名 + */ + public String deptAlias() default ""; + + /** + * 用户表的别名 + */ + public String userAlias() default ""; + +} diff --git a/src/main/java/com/jxy/windminer/common/datascope/aspect/DataScopeAspect.java b/src/main/java/com/jxy/windminer/common/datascope/aspect/DataScopeAspect.java new file mode 100644 index 0000000..b8d808a --- /dev/null +++ b/src/main/java/com/jxy/windminer/common/datascope/aspect/DataScopeAspect.java @@ -0,0 +1,139 @@ +package com.jxy.windminer.common.datascope.aspect; + +import com.jxy.windminer.common.datascope.annotation.DataScope; +import com.jxy.windminer.common.security.utils.SecurityUtils; +import com.jxy.windminer.common.utils.StringUtils; +import com.jxy.windminer.common.web.entity.BaseEntity; +import com.jxy.windminer.entity.auth.LoginUser; +import com.jxy.windminer.entity.system.SysRole; +import com.jxy.windminer.entity.system.SysUser; +import org.aspectj.lang.JoinPoint; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Before; +import org.springframework.stereotype.Component; + +/** + * @Description 数据过滤处理 + * @Date 2022/5/16 15:23 + * @Author 杜懿 + */ +@Aspect +@Component +public class DataScopeAspect { + + /** 全部数据权限 */ + public static final String DATA_SCOPE_ALL = "1"; + + /** 自定数据权限 */ + public static final String DATA_SCOPE_CUSTOM = "2"; + + /** 部门数据权限 */ + public static final String DATA_SCOPE_DEPT = "3"; + + /** 部门及以下数据权限 */ + public static final String DATA_SCOPE_DEPT_AND_CHILD = "4"; + + /** 仅本人数据权限 */ + public static final String DATA_SCOPE_SELF = "5"; + + /** 数据权限过滤关键字 */ + public static final String DATA_SCOPE = "dataScope"; + + @Before("@annotation(controllerDataScope)") + public void doBefore(JoinPoint point, DataScope controllerDataScope) throws Throwable + { + clearDataScope(point); + handleDataScope(point, controllerDataScope); + } + + protected void handleDataScope(final JoinPoint joinPoint, DataScope controllerDataScope) + { + // 获取当前的用户 + LoginUser loginUser = SecurityUtils.getLoginUser(); + if (StringUtils.isNotNull(loginUser)) + { + SysUser currentUser = loginUser.getSysUser(); + // 超级管理员,无需过滤 + if (StringUtils.isNotNull(currentUser) && !currentUser.isAdmin()) + { + dataScopeFilter(joinPoint, currentUser, controllerDataScope.deptAlias(), + controllerDataScope.userAlias()); + } + } + } + + /** + * 数据范围过滤 + * + * @param joinPoint 切点 + * @param user 用户 + * @param deptAlias 部门别名 + * @param userAlias 用户别名 + */ + public static void dataScopeFilter(JoinPoint joinPoint, SysUser user, String deptAlias, String userAlias) + { + StringBuilder sqlString = new StringBuilder(); + + for (SysRole role : user.getRoles()) + { + String dataScope = role.getDataScope(); + if (DATA_SCOPE_ALL.equals(dataScope)) + { + sqlString = new StringBuilder(); + break; + } + else if (DATA_SCOPE_CUSTOM.equals(dataScope)) + { + sqlString.append(StringUtils.format( + " OR {}.dept_id IN ( SELECT dept_id FROM sys_role_dept WHERE role_id = {} ) ", deptAlias, + role.getRoleId())); + } + else if (DATA_SCOPE_DEPT.equals(dataScope)) + { + sqlString.append(StringUtils.format(" OR {}.dept_id = {} ", deptAlias, user.getDeptId())); + } + else if (DATA_SCOPE_DEPT_AND_CHILD.equals(dataScope)) + { + sqlString.append(StringUtils.format( + " OR {}.dept_id IN ( SELECT dept_id FROM sys_dept WHERE dept_id = {} or find_in_set( {} , ancestors ) )", + deptAlias, user.getDeptId(), user.getDeptId())); + } + else if (DATA_SCOPE_SELF.equals(dataScope)) + { + if (StringUtils.isNotBlank(userAlias)) + { + sqlString.append(StringUtils.format(" OR {}.user_id = {} ", userAlias, user.getUserId())); + } + else + { + // 数据权限为仅本人且没有userAlias别名不查询任何数据 + sqlString.append(" OR 1=0 "); + } + } + } + + if (StringUtils.isNotBlank(sqlString.toString())) + { + Object params = joinPoint.getArgs()[0]; + if (StringUtils.isNotNull(params) && params instanceof BaseEntity) + { + BaseEntity baseEntity = (BaseEntity) params; + baseEntity.getParams().put(DATA_SCOPE, " AND (" + sqlString.substring(4) + ")"); + } + } + } + + /** + * 先清空params.dataScope参数防止注入 + */ + private void clearDataScope(final JoinPoint joinPoint) + { + Object params = joinPoint.getArgs()[0]; + if (StringUtils.isNotNull(params) && params instanceof BaseEntity) + { + BaseEntity baseEntity = (BaseEntity) params; + baseEntity.getParams().put(DATA_SCOPE, ""); + } + } + +} diff --git a/src/main/java/com/jxy/windminer/common/enums/HttpMethod.java b/src/main/java/com/jxy/windminer/common/enums/HttpMethod.java new file mode 100644 index 0000000..899278d --- /dev/null +++ b/src/main/java/com/jxy/windminer/common/enums/HttpMethod.java @@ -0,0 +1,37 @@ +package com.jxy.windminer.common.enums; + +import org.springframework.lang.Nullable; + +import java.util.HashMap; +import java.util.Map; + +/** + * @Description TODO + * @Date 2024/5/30 11:18 + * @Author 杜懿 + */ +public enum HttpMethod { + + GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS, TRACE; + + private static final Map mappings = new HashMap<>(16); + + static + { + for (HttpMethod httpMethod : values()) + { + mappings.put(httpMethod.name(), httpMethod); + } + } + + @Nullable + public static HttpMethod resolve(@Nullable String method) + { + return (method != null ? mappings.get(method) : null); + } + + public boolean matches(String method) + { + return (this == resolve(method)); + } +} diff --git a/src/main/java/com/jxy/windminer/common/enums/LimitType.java b/src/main/java/com/jxy/windminer/common/enums/LimitType.java new file mode 100644 index 0000000..0ec55a8 --- /dev/null +++ b/src/main/java/com/jxy/windminer/common/enums/LimitType.java @@ -0,0 +1,14 @@ +package com.jxy.windminer.common.enums; + +public enum LimitType { + + /** + * 默认策略全局限流 + */ + DEFAULT, + + /** + * 根据请求者IP进行限流 + */ + IP +} diff --git a/src/main/java/com/jxy/windminer/common/enums/UserRole.java b/src/main/java/com/jxy/windminer/common/enums/UserRole.java new file mode 100644 index 0000000..43416de --- /dev/null +++ b/src/main/java/com/jxy/windminer/common/enums/UserRole.java @@ -0,0 +1,34 @@ +package com.jxy.windminer.common.enums; + +public enum UserRole { + ADMIN(1, "admin", "管理员"), + REGISTERED(2, "registered", "注册用户"), + ADVANCE(3, "advance", "付费用户"), + VIP(4, "vip", "vip"), + SUPPORT(5, "support", "支持人员"), + VERIFIER(6, "verifier", "审核人员"), + ACCOUNTTING(7, "accounting", "财务"); + + private final int roleId; + private final String role; + private final String des; + + UserRole(int roleId, String role, String des) + { + this.roleId = roleId; + this.role = role; + this.des = des; + } + + public int getRoleId() + { + return roleId; + } + + public String getRole() + { + return role; + } + + public String getDes(){return des;} +} diff --git a/src/main/java/com/jxy/windminer/common/enums/UserStatus.java b/src/main/java/com/jxy/windminer/common/enums/UserStatus.java new file mode 100644 index 0000000..fefe2e2 --- /dev/null +++ b/src/main/java/com/jxy/windminer/common/enums/UserStatus.java @@ -0,0 +1,31 @@ +package com.jxy.windminer.common.enums; + +/** + * @Description 用户状态 + * @Date 2022/5/12 14:52 + * @Author 杜懿 + */ +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/jxy/windminer/common/exception/CaptchaException.java b/src/main/java/com/jxy/windminer/common/exception/CaptchaException.java new file mode 100644 index 0000000..c9c2034 --- /dev/null +++ b/src/main/java/com/jxy/windminer/common/exception/CaptchaException.java @@ -0,0 +1,17 @@ +package com.jxy.windminer.common.exception; + +/** + * @Description 验证码错误异常类 + * @Date 2022/5/17 10:51 + * @Author 杜懿 + */ +public class CaptchaException extends RuntimeException{ + + private static final long serialVersionUID = 1L; + + public CaptchaException(String msg) + { + super(msg); + } + +} diff --git a/src/main/java/com/jxy/windminer/common/exception/GlobalException.java b/src/main/java/com/jxy/windminer/common/exception/GlobalException.java new file mode 100644 index 0000000..e9a7d01 --- /dev/null +++ b/src/main/java/com/jxy/windminer/common/exception/GlobalException.java @@ -0,0 +1,57 @@ +package com.jxy.windminer.common.exception; + +/** + * @Description 全局异常 + * @Date 2022/5/16 14:39 + * @Author 杜懿 + */ +public class GlobalException extends RuntimeException { + + private static final long serialVersionUID = 1L; + + /** + * 错误提示 + */ + private String message; + + /** + * 错误明细,内部调试错误 + * + */ + private String detailMessage; + + /** + * 空构造方法,避免反序列化问题 + */ + public GlobalException() + { + } + + public GlobalException(String message) + { + this.message = message; + } + + public String getDetailMessage() + { + return detailMessage; + } + + public GlobalException setDetailMessage(String detailMessage) + { + this.detailMessage = detailMessage; + return this; + } + + public String getMessage() + { + return message; + } + + public GlobalException setMessage(String message) + { + this.message = message; + return this; + } + +} diff --git a/src/main/java/com/jxy/windminer/common/exception/InnerAuthException.java b/src/main/java/com/jxy/windminer/common/exception/InnerAuthException.java new file mode 100644 index 0000000..7f6d6a7 --- /dev/null +++ b/src/main/java/com/jxy/windminer/common/exception/InnerAuthException.java @@ -0,0 +1,17 @@ +package com.jxy.windminer.common.exception; + +/** + * @Description 内部认证异常 + * @Date 2022/5/13 18:23 + * @Author 杜懿 + */ +public class InnerAuthException extends RuntimeException{ + + private static final long serialVersionUID = 1L; + + public InnerAuthException(String message) + { + super(message); + } + +} diff --git a/src/main/java/com/jxy/windminer/common/exception/PreAuthorizeException.java b/src/main/java/com/jxy/windminer/common/exception/PreAuthorizeException.java new file mode 100644 index 0000000..36c9c0e --- /dev/null +++ b/src/main/java/com/jxy/windminer/common/exception/PreAuthorizeException.java @@ -0,0 +1,16 @@ +package com.jxy.windminer.common.exception; + +/** + * @Description 权限异常 + * @Date 2022/5/16 11:31 + * @Author 杜懿 + */ +public class PreAuthorizeException extends RuntimeException{ + + private static final long serialVersionUID = 1L; + + public PreAuthorizeException() + { + } + +} diff --git a/src/main/java/com/jxy/windminer/common/exception/ServiceException.java b/src/main/java/com/jxy/windminer/common/exception/ServiceException.java new file mode 100644 index 0000000..aa4e510 --- /dev/null +++ b/src/main/java/com/jxy/windminer/common/exception/ServiceException.java @@ -0,0 +1,75 @@ +package com.jxy.windminer.common.exception; + +import lombok.Data; + +/** + * @Description 业务异常 + * @Date 2022/5/10 16:32 + * @Author 杜懿 + */ +@Data +public final class ServiceException extends RuntimeException +{ + private static final long serialVersionUID = 1L; + + /** + * 错误码 + */ + private Integer code; + + /** + * 错误提示 + */ + private String message; + + /** + * 错误明细,内部调试错误 + * + */ + private String detailMessage; + + /** + * 空构造方法,避免反序列化问题 + */ + public ServiceException() + { + } + + public ServiceException(String message) + { + this.message = message; + } + + public ServiceException(String message, Integer code) + { + this.message = message; + this.code = code; + } + + public String getDetailMessage() + { + return detailMessage; + } + + public String getMessage() + { + return message; + } + + public Integer getCode() + { + return code; + } + + public ServiceException setMessage(String message) + { + this.message = message; + return this; + } + + public ServiceException setDetailMessage(String detailMessage) + { + this.detailMessage = detailMessage; + return this; + } +} diff --git a/src/main/java/com/jxy/windminer/common/exception/UtilException.java b/src/main/java/com/jxy/windminer/common/exception/UtilException.java new file mode 100644 index 0000000..25b0985 --- /dev/null +++ b/src/main/java/com/jxy/windminer/common/exception/UtilException.java @@ -0,0 +1,26 @@ +package com.jxy.windminer.common.exception; + +/** + * @Description 工具类异常 + * @Date 2022/5/10 11:32 + * @Author 杜懿 + */ +public class UtilException extends RuntimeException +{ + private static final long serialVersionUID = 8247642119125314183L; + + 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/jxy/windminer/common/exception/auth/LoginExpiresException.java b/src/main/java/com/jxy/windminer/common/exception/auth/LoginExpiresException.java new file mode 100644 index 0000000..f81329f --- /dev/null +++ b/src/main/java/com/jxy/windminer/common/exception/auth/LoginExpiresException.java @@ -0,0 +1,17 @@ +package com.jxy.windminer.common.exception.auth; + +/** + * @Description 登录过期异常 + * @Date 2022/5/13 9:39 + * @Author 杜懿 + */ +public class LoginExpiresException extends RuntimeException{ + + private static final long serialVersionUID = 1L; + + public LoginExpiresException(String message) + { + super(message); + } + +} diff --git a/src/main/java/com/jxy/windminer/common/exception/auth/NotLoginException.java b/src/main/java/com/jxy/windminer/common/exception/auth/NotLoginException.java new file mode 100644 index 0000000..e8d9651 --- /dev/null +++ b/src/main/java/com/jxy/windminer/common/exception/auth/NotLoginException.java @@ -0,0 +1,17 @@ +package com.jxy.windminer.common.exception.auth; + +/** + * @Description 没有通过的登陆认证异常 + * @Date 2022/5/13 9:39 + * @Author 杜懿 + */ +public class NotLoginException extends RuntimeException{ + + private static final long serialVersionUID = 1L; + + public NotLoginException(String message) + { + super(message); + } + +} diff --git a/src/main/java/com/jxy/windminer/common/exception/auth/NotPermissionException.java b/src/main/java/com/jxy/windminer/common/exception/auth/NotPermissionException.java new file mode 100644 index 0000000..eeb9186 --- /dev/null +++ b/src/main/java/com/jxy/windminer/common/exception/auth/NotPermissionException.java @@ -0,0 +1,24 @@ +package com.jxy.windminer.common.exception.auth; + +import org.apache.commons.lang3.StringUtils; + +/** + * @Description 未能通过的权限认证异常 + * @Date 2022/5/13 9:45 + * @Author 杜懿 + */ +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/jxy/windminer/common/exception/auth/NotRoleException.java b/src/main/java/com/jxy/windminer/common/exception/auth/NotRoleException.java new file mode 100644 index 0000000..b759be1 --- /dev/null +++ b/src/main/java/com/jxy/windminer/common/exception/auth/NotRoleException.java @@ -0,0 +1,25 @@ +package com.jxy.windminer.common.exception.auth; + +import org.apache.commons.lang3.StringUtils; + +/** + * @Description 未能通过的角色认证异常 + * @Date 2022/5/13 9:48 + * @Author 杜懿 + */ +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/jxy/windminer/common/exception/auth/TokenExpiresException.java b/src/main/java/com/jxy/windminer/common/exception/auth/TokenExpiresException.java new file mode 100644 index 0000000..39dafc3 --- /dev/null +++ b/src/main/java/com/jxy/windminer/common/exception/auth/TokenExpiresException.java @@ -0,0 +1,17 @@ +package com.jxy.windminer.common.exception.auth; + +/** + * @Description Token过期异常 + * @Date 2022/5/13 9:39 + * @Author 杜懿 + */ +public class TokenExpiresException extends RuntimeException{ + + private static final long serialVersionUID = 1L; + + public TokenExpiresException(String message) + { + super(message); + } + +} diff --git a/src/main/java/com/jxy/windminer/common/exception/auth/UnAuthorizedException.java b/src/main/java/com/jxy/windminer/common/exception/auth/UnAuthorizedException.java new file mode 100644 index 0000000..2b02bdd --- /dev/null +++ b/src/main/java/com/jxy/windminer/common/exception/auth/UnAuthorizedException.java @@ -0,0 +1,17 @@ +package com.jxy.windminer.common.exception.auth; + +/** + * @Description 未授权异常 + * @Date 2022/5/13 9:39 + * @Author 杜懿 + */ +public class UnAuthorizedException extends RuntimeException{ + + private static final long serialVersionUID = 1L; + + public UnAuthorizedException(String message) + { + super(message); + } + +} diff --git a/src/main/java/com/jxy/windminer/common/exception/base/BaseException.java b/src/main/java/com/jxy/windminer/common/exception/base/BaseException.java new file mode 100644 index 0000000..1455699 --- /dev/null +++ b/src/main/java/com/jxy/windminer/common/exception/base/BaseException.java @@ -0,0 +1,84 @@ +package com.jxy.windminer.common.exception.base; + +import lombok.Data; + +/** + * @Description 基础异常 + * @Date 2022/5/16 11:42 + * @Author 杜懿 + */ +@Data +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/jxy/windminer/common/exception/file/FileException.java b/src/main/java/com/jxy/windminer/common/exception/file/FileException.java new file mode 100644 index 0000000..6fecf3a --- /dev/null +++ b/src/main/java/com/jxy/windminer/common/exception/file/FileException.java @@ -0,0 +1,20 @@ +package com.jxy.windminer.common.exception.file; + + +import com.jxy.windminer.common.exception.base.BaseException; + +/** + * 文件信息异常类 + * + * @author jxy + */ +public class FileException extends BaseException +{ + private static final long serialVersionUID = 1L; + + public FileException(String code, Object[] args) + { + super("file", code, args); + } + +} diff --git a/src/main/java/com/jxy/windminer/common/exception/file/FileNameLengthLimitExceededException.java b/src/main/java/com/jxy/windminer/common/exception/file/FileNameLengthLimitExceededException.java new file mode 100644 index 0000000..9b153ba --- /dev/null +++ b/src/main/java/com/jxy/windminer/common/exception/file/FileNameLengthLimitExceededException.java @@ -0,0 +1,16 @@ +package com.jxy.windminer.common.exception.file; + +/** + * 文件名称超长限制异常类 + * + * @author jxy + */ +public class FileNameLengthLimitExceededException extends FileException +{ + private static final long serialVersionUID = 1L; + + public FileNameLengthLimitExceededException(int defaultFileNameLength) + { + super("upload.filename.exceed.length", new Object[] { defaultFileNameLength }); + } +} diff --git a/src/main/java/com/jxy/windminer/common/exception/file/FileSizeLimitExceededException.java b/src/main/java/com/jxy/windminer/common/exception/file/FileSizeLimitExceededException.java new file mode 100644 index 0000000..55c0fa4 --- /dev/null +++ b/src/main/java/com/jxy/windminer/common/exception/file/FileSizeLimitExceededException.java @@ -0,0 +1,16 @@ +package com.jxy.windminer.common.exception.file; + +/** + * 文件名大小限制异常类 + * + * @author jxy + */ +public class FileSizeLimitExceededException extends FileException +{ + private static final long serialVersionUID = 1L; + + public FileSizeLimitExceededException(long defaultMaxSize) + { + super("文件名超长", new Object[] { defaultMaxSize }); + } +} diff --git a/src/main/java/com/jxy/windminer/common/exception/file/InvalidExtensionException.java b/src/main/java/com/jxy/windminer/common/exception/file/InvalidExtensionException.java new file mode 100644 index 0000000..874876c --- /dev/null +++ b/src/main/java/com/jxy/windminer/common/exception/file/InvalidExtensionException.java @@ -0,0 +1,82 @@ +package com.jxy.windminer.common.exception.file; + +import org.apache.commons.fileupload.FileUploadException; + +import java.util.Arrays; + +/** + * 文件上传 误异常类 + * + * @author jxy + */ +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/jxy/windminer/common/exception/user/CaptchaExpireException.java b/src/main/java/com/jxy/windminer/common/exception/user/CaptchaExpireException.java new file mode 100644 index 0000000..818d97a --- /dev/null +++ b/src/main/java/com/jxy/windminer/common/exception/user/CaptchaExpireException.java @@ -0,0 +1,17 @@ +package com.jxy.windminer.common.exception.user; + +/** + * @Description 验证码失效异常类 + * @Date 2022/5/18 17:51 + * @Author 杜懿 + */ +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/jxy/windminer/common/exception/user/UserException.java b/src/main/java/com/jxy/windminer/common/exception/user/UserException.java new file mode 100644 index 0000000..37234b1 --- /dev/null +++ b/src/main/java/com/jxy/windminer/common/exception/user/UserException.java @@ -0,0 +1,18 @@ +package com.jxy.windminer.common.exception.user; + +import com.jxy.windminer.common.exception.base.BaseException; + +/** + * @Description 用户信息异常类 + * @Date 2022/5/16 14:05 + * @Author 杜懿 + */ +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/jxy/windminer/common/redis/config/FastJson2JsonRedisSerializer.java b/src/main/java/com/jxy/windminer/common/redis/config/FastJson2JsonRedisSerializer.java new file mode 100644 index 0000000..d4580c0 --- /dev/null +++ b/src/main/java/com/jxy/windminer/common/redis/config/FastJson2JsonRedisSerializer.java @@ -0,0 +1,73 @@ +package com.jxy.windminer.common.redis.config; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.parser.ParserConfig; +import com.alibaba.fastjson.serializer.SerializerFeature; +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.type.TypeFactory; +import org.springframework.data.redis.serializer.RedisSerializer; +import org.springframework.data.redis.serializer.SerializationException; +import org.springframework.util.Assert; + +import java.nio.charset.Charset; + +/** + * @Description redis使用FastJson序列化 + * @Date 2022/5/12 10:01 + * @Author 杜懿 + */ +public class FastJson2JsonRedisSerializer implements RedisSerializer { + + @SuppressWarnings("unused") + private ObjectMapper objectMapper = new ObjectMapper(); + + public static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8"); + + private Class clazz; + + static + { + ParserConfig.getGlobalInstance().setAutoTypeSupport(true); + } + + public FastJson2JsonRedisSerializer(Class clazz) + { + super(); + this.clazz = clazz; + } + + @Override + public byte[] serialize(T t) throws SerializationException + { + if (t == null) + { + return new byte[0]; + } + return JSON.toJSONString(t, SerializerFeature.WriteClassName).getBytes(DEFAULT_CHARSET); + } + + @Override + public T deserialize(byte[] bytes) throws SerializationException + { + if (bytes == null || bytes.length <= 0) + { + return null; + } + String str = new String(bytes, DEFAULT_CHARSET); + + return JSON.parseObject(str, clazz); + } + + public void setObjectMapper(ObjectMapper objectMapper) + { + Assert.notNull(objectMapper, "'objectMapper' must not be null"); + this.objectMapper = objectMapper; + } + + protected JavaType getJavaType(Class clazz) + { + return TypeFactory.defaultInstance().constructType(clazz); + } + +} diff --git a/src/main/java/com/jxy/windminer/common/redis/config/RedisConfig.java b/src/main/java/com/jxy/windminer/common/redis/config/RedisConfig.java new file mode 100644 index 0000000..c6363ad --- /dev/null +++ b/src/main/java/com/jxy/windminer/common/redis/config/RedisConfig.java @@ -0,0 +1,85 @@ +package com.jxy.windminer.common.redis.config; + +import com.fasterxml.jackson.annotation.JsonAutoDetect; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.annotation.PropertyAccessor; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator; +import org.springframework.cache.annotation.CachingConfigurerSupport; +import org.springframework.cache.annotation.EnableCaching; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.redis.connection.RedisConnectionFactory; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.core.script.DefaultRedisScript; +import org.springframework.data.redis.serializer.StringRedisSerializer; + +/** + * @Description redis配置 + * @Date 2022/5/12 09:58 + * @Author 杜懿 + */ +@Configuration +@EnableCaching +public class RedisConfig extends CachingConfigurerSupport { + + @Bean + @SuppressWarnings(value = { "unchecked", "rawtypes" }) + public RedisTemplate redisTemplate(RedisConnectionFactory connectionFactory) + { + RedisTemplate template = new RedisTemplate<>(); + template.setConnectionFactory(connectionFactory); + + FastJson2JsonRedisSerializer serializer = new FastJson2JsonRedisSerializer(Object.class); + + + ObjectMapper mapper = new ObjectMapper(); + mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); + mapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY); + serializer.setObjectMapper(mapper); + + // 使用StringRedisSerializer来序列化和反序列化redis的key值 + template.setKeySerializer(new StringRedisSerializer()); + template.setValueSerializer(serializer); + + // Hash的key也采用StringRedisSerializer的序列化方式 + template.setHashKeySerializer(new StringRedisSerializer()); + template.setHashValueSerializer(serializer); + + template.afterPropertiesSet(); + return template; + } + + @Bean + public DefaultRedisScript limitScript() + { + // 泛型是返回值的类型 + DefaultRedisScript redisScript = new DefaultRedisScript<>(); + // 设置脚本 + redisScript.setScriptText(limitScriptText()); + // 设置返回值类型 + redisScript.setResultType(Long.class); + return redisScript; + } + + /** + * 限流脚本 + */ + private String limitScriptText() + { + return "local key = KEYS[1]\n" + + "local count = tonumber(ARGV[1])\n" + + "local time = tonumber(ARGV[2])\n" + + "local current = redis.call('get', key);\n" + + "if current and tonumber(current) > count then\n" + + " return tonumber(current);\n" + + "end\n" + + "current = redis.call('incr', key)\n" + + "if tonumber(current) == 1 then\n" + + " redis.call('expire', key, time)\n" + + "end\n" + + "return tonumber(current);"; + } + + +} diff --git a/src/main/java/com/jxy/windminer/common/redis/service/RedisService.java b/src/main/java/com/jxy/windminer/common/redis/service/RedisService.java new file mode 100644 index 0000000..6b9a80b --- /dev/null +++ b/src/main/java/com/jxy/windminer/common/redis/service/RedisService.java @@ -0,0 +1,267 @@ +package com.jxy.windminer.common.redis.service; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.BoundSetOperations; +import org.springframework.data.redis.core.HashOperations; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.core.ValueOperations; +import org.springframework.stereotype.Component; + +import java.util.*; +import java.util.concurrent.TimeUnit; + +/** + * @Description redis 相关 + * @Date 2022/5/12 9:22 + * @Author 杜懿 + */ +@SuppressWarnings(value = { "unchecked", "rawtypes" }) +@Component +public class RedisService { + + @Autowired + private RedisTemplate redisTemplate; + + /** + * 缓存基本的对象,Integer、String、实体类等 + * + * @param key 缓存的键值 + * @param value 缓存的值 + */ + public void setCacheObject(final String key, final T value) + { + redisTemplate.opsForValue().set(key, value); + } + + /** + * 缓存基本的对象,Integer、String、实体类等 + * + * @param key 缓存的键值 + * @param value 缓存的值 + * @param timeout 时间 + * @param timeUnit 时间颗粒度 + */ + public void setCacheObject(final String key, final T value, final Long timeout, final TimeUnit timeUnit) + { + redisTemplate.opsForValue().set(key, value, timeout, timeUnit); + } + + /** + * 设置有效时间 + * + * @param key Redis键 + * @param timeout 超时时间 + * @return true=设置成功;false=设置失败 + */ + public boolean expire(final String key, final long timeout) + { + return expire(key, timeout, TimeUnit.SECONDS); + } + + /** + * 设置有效时间 + * + * @param key Redis键 + * @param timeout 超时时间 + * @param unit 时间单位 + * @return true=设置成功;false=设置失败 + */ + public boolean expire(final String key, final long timeout, final TimeUnit unit) + { + return redisTemplate.expire(key, timeout, unit); + } + + /** + * 获取有效时间 + * + * @param key Redis键 + * @return 有效时间 + */ + public long getExpire(final String key) + { + return redisTemplate.getExpire(key); + } + + /** + * 获取有效时间 + * + * @param key Redis键 + * @return 有效时间 + */ + public long getExpire(final String key,TimeUnit timetype) + { + return redisTemplate.getExpire(key,timetype); + } + + + /** + * 判断 key是否存在 + * + * @param key 键 + * @return true 存在 false不存在 + */ + public Boolean hasKey(String key) + { + return redisTemplate.hasKey(key); + } + + /** + * 获得缓存的基本对象。 + * + * @param key 缓存键值 + * @return 缓存键值对应的数据 + */ + public T getCacheObject(final String key) + { + ValueOperations operation = redisTemplate.opsForValue(); + return operation.get(key); + } + + /** + * 删除单个对象 + * + * @param key + */ + public boolean deleteObject(final String key) + { + return redisTemplate.delete(key); + } + + /** + * 删除集合对象 + * + * @param collection 多个对象 + * @return + */ + public long deleteObject(final Collection collection) + { + return redisTemplate.delete(collection); + } + + /** + * 缓存List数据 + * + * @param key 缓存的键值 + * @param dataList 待缓存的List数据 + * @return 缓存的对象 + */ + public long setCacheList(final String key, final List dataList) + { + Long count = redisTemplate.opsForList().rightPushAll(key, dataList); + return count == null ? 0 : count; + } + + /** + * 获得缓存的list对象 + * + * @param key 缓存的键值 + * @return 缓存键值对应的数据 + */ + public List getCacheList(final String key) + { + return redisTemplate.opsForList().range(key, 0, -1); + } + + /** + * 缓存Set + * + * @param key 缓存键值 + * @param dataSet 缓存的数据 + * @return 缓存数据的对象 + */ + public BoundSetOperations setCacheSet(final String key, final Set dataSet) + { + BoundSetOperations setOperation = redisTemplate.boundSetOps(key); + Iterator it = dataSet.iterator(); + while (it.hasNext()) + { + setOperation.add(it.next()); + } + return setOperation; + } + + /** + * 获得缓存的set + * + * @param key + * @return + */ + public Set getCacheSet(final String key) + { + return redisTemplate.opsForSet().members(key); + } + + /** + * 缓存Map + * + * @param key + * @param dataMap + */ + public void setCacheMap(final String key, final Map dataMap) + { + if (dataMap != null) { + redisTemplate.opsForHash().putAll(key, dataMap); + } + } + + /** + * 获得缓存的Map + * + * @param key + * @return + */ + public Map getCacheMap(final String key) + { + return redisTemplate.opsForHash().entries(key); + + } + + /** + * 往Hash中存入数据 + * + * @param key Redis键 + * @param hKey Hash键 + * @param value 值 + */ + public void setCacheMapValue(final String key, final String hKey, final T value) + { + redisTemplate.opsForHash().put(key, hKey, value); + } + + /** + * 获取Hash中的数据 + * + * @param key Redis键 + * @param hKey Hash键 + * @return Hash中的对象 + */ + public T getCacheMapValue(final String key, final String hKey) + { + HashOperations opsForHash = redisTemplate.opsForHash(); + return opsForHash.get(key, hKey); + } + + /** + * 获取多个Hash中的数据 + * + * @param key Redis键 + * @param hKeys Hash键集合 + * @return Hash对象集合 + */ + public List getMultiCacheMapValue(final String key, final Collection hKeys) + { + return redisTemplate.opsForHash().multiGet(key, hKeys); + } + + /** + * 获得缓存的基本对象列表 + * + * @param pattern 字符串前缀 + * @return 对象列表 + */ + public Collection keys(final String pattern) + { + return redisTemplate.keys(pattern); + } + +} diff --git a/src/main/java/com/jxy/windminer/common/security/annotation/EnableCustomConfig.java b/src/main/java/com/jxy/windminer/common/security/annotation/EnableCustomConfig.java new file mode 100644 index 0000000..13729f7 --- /dev/null +++ b/src/main/java/com/jxy/windminer/common/security/annotation/EnableCustomConfig.java @@ -0,0 +1,25 @@ +package com.jxy.windminer.common.security.annotation; + +import com.jxy.windminer.common.security.config.ApplicationConfig; +import org.mybatis.spring.annotation.MapperScan; +import org.springframework.context.annotation.EnableAspectJAutoProxy; +import org.springframework.context.annotation.Import; +import org.springframework.scheduling.annotation.EnableAsync; + +import java.lang.annotation.*; + +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@Inherited +// 表示通过aop框架暴露该代理对象,AopContext能够访问 +@EnableAspectJAutoProxy(exposeProxy = true) +// 指定要扫描的Mapper类的包的路径 +// 开启线程异步执行 +@EnableAsync +// 自动加载类 +@Import({ ApplicationConfig.class}) +public @interface EnableCustomConfig +{ + +} diff --git a/src/main/java/com/jxy/windminer/common/security/annotation/Logical.java b/src/main/java/com/jxy/windminer/common/security/annotation/Logical.java new file mode 100644 index 0000000..38e916d --- /dev/null +++ b/src/main/java/com/jxy/windminer/common/security/annotation/Logical.java @@ -0,0 +1,19 @@ +package com.jxy.windminer.common.security.annotation; + +/** + * @Description 权限注解验证模式设定 + * @Date 2022/5/16 9:33 + * @Author 杜懿 + */ +public enum Logical +{ + /** + * 必须具有所有的元素 + */ + AND, + + /** + * 只需具有其中一个元素 + */ + OR +} diff --git a/src/main/java/com/jxy/windminer/common/security/annotation/RequiresLogin.java b/src/main/java/com/jxy/windminer/common/security/annotation/RequiresLogin.java new file mode 100644 index 0000000..05640c1 --- /dev/null +++ b/src/main/java/com/jxy/windminer/common/security/annotation/RequiresLogin.java @@ -0,0 +1,16 @@ +package com.jxy.windminer.common.security.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * @Description 登录认证:登录成功后才能进入该方法 + * @Date 2022/5/13 19:11 + * @Author 杜懿 + */ +@Target({ ElementType.METHOD, ElementType.TYPE }) +@Retention(RetentionPolicy.RUNTIME) +public @interface RequiresLogin { +} diff --git a/src/main/java/com/jxy/windminer/common/security/annotation/RequiresRoles.java b/src/main/java/com/jxy/windminer/common/security/annotation/RequiresRoles.java new file mode 100644 index 0000000..ab3fd34 --- /dev/null +++ b/src/main/java/com/jxy/windminer/common/security/annotation/RequiresRoles.java @@ -0,0 +1,27 @@ +package com.jxy.windminer.common.security.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * @Description 角色认证:必须拥有该方法所需角色标识才能进入方法 + * @Date 2022/5/16 9:35 + * @Author 杜懿 + */ +@Target({ ElementType.METHOD, ElementType.TYPE }) +@Retention(RetentionPolicy.RUNTIME) +public @interface RequiresRoles { + + /** + * 需要校验的角色标识 + */ + String[] value() default {}; + + /** + * 验证逻辑:AND | OR,默认AND + */ + Logical logical() default Logical.AND; + +} diff --git a/src/main/java/com/jxy/windminer/common/security/aspect/PreAuthAspect.java b/src/main/java/com/jxy/windminer/common/security/aspect/PreAuthAspect.java new file mode 100644 index 0000000..997afe3 --- /dev/null +++ b/src/main/java/com/jxy/windminer/common/security/aspect/PreAuthAspect.java @@ -0,0 +1,93 @@ +package com.jxy.windminer.common.security.aspect; + +import com.jxy.windminer.common.security.annotation.RequiresLogin; +import com.jxy.windminer.common.security.annotation.RequiresRoles; +import com.jxy.windminer.common.security.auth.AuthUtil; +import org.aspectj.lang.ProceedingJoinPoint; +import org.aspectj.lang.annotation.Around; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Pointcut; +import org.aspectj.lang.reflect.MethodSignature; +import org.springframework.stereotype.Component; + +import java.lang.reflect.Method; + +/** + * @Description 基于 Spring Aop 的注解鉴权 + * @Date 2022/5/13 18:47 + * @Author 杜懿 + */ +@Aspect +@Component +public class PreAuthAspect { + + /** + * 构建 + */ + public PreAuthAspect(){ + + } + + /** + * 定义AOP签名 (切入所有使用鉴权注解的方法) + */ + public static final String POINTCUT_SIGN = " @annotation(com.jxy.windminer.common.security.annotation.RequiresLogin) || " + + "@annotation(com.jxy.windminer.common.security.annotation.RequiresRoles)"; + + /** + * 声明AOP签名 + */ + @Pointcut(POINTCUT_SIGN) + public void pointcut() + { + } + + /** + * 环绕切入 + * + * @param joinPoint 切面对象 + * @return 底层方法执行后的返回值 + * @throws Throwable 底层方法抛出的异常 + */ + @Around("pointcut()") + public Object around(ProceedingJoinPoint joinPoint) throws Throwable { + + // 注解鉴权 + MethodSignature signature = (MethodSignature) joinPoint.getSignature(); + checkMethodAnnotation(signature.getMethod()); + try + { + // 执行原有逻辑 + Object obj = joinPoint.proceed(); + return obj; + } + catch (Throwable e) + { + throw e; + } + } + + /** + * 对一个Method对象进行注解检查 + */ + public void checkMethodAnnotation(Method method) + { + //校验 @RequiresLogin 注解 + RequiresLogin requiresLogin = method.getAnnotation(RequiresLogin.class); + if(requiresLogin != null){ + AuthUtil.checkLogin(); + } + + //校验 @RequiresRoles 注解 + RequiresRoles requiresRoles = method.getAnnotation(RequiresRoles.class); + if(requiresRoles != null){ + AuthUtil.checkRole(requiresRoles); + } + + ////校验 @RequiresPermissions 注解 + //RequiresPermissions requiresPermissions = method.getAnnotation(RequiresPermissions.class); + //if(requiresPermissions != null){ + // AuthUtil.checkPermission(requiresPermissions); + //} + } +} diff --git a/src/main/java/com/jxy/windminer/common/security/aspect/RateLimiterAspect.java b/src/main/java/com/jxy/windminer/common/security/aspect/RateLimiterAspect.java new file mode 100644 index 0000000..9a53120 --- /dev/null +++ b/src/main/java/com/jxy/windminer/common/security/aspect/RateLimiterAspect.java @@ -0,0 +1,90 @@ +package com.jxy.windminer.common.security.aspect; + +import com.jxy.windminer.annotation.RateLimiter; +import com.jxy.windminer.common.enums.LimitType; +import com.jxy.windminer.common.exception.ServiceException; +import com.jxy.windminer.common.utils.ServletUtils; +import com.jxy.windminer.common.utils.StringUtils; +import com.jxy.windminer.common.utils.ip.IpUtils; +import org.aspectj.lang.JoinPoint; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Before; +import org.aspectj.lang.reflect.MethodSignature; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.core.script.RedisScript; +import org.springframework.stereotype.Component; + +import java.lang.reflect.Method; +import java.util.Collections; +import java.util.List; + +/** + * @Description 限流处理 + * @Date 2022/11/30 17:38 + * @Author 杜懿 + */ +@Aspect +@Component +public class RateLimiterAspect { + private static final Logger log = LoggerFactory.getLogger(RateLimiterAspect.class); + + private RedisTemplate redisTemplate; + + private RedisScript limitScript; + + @Autowired + public void setRedisTemplate1(RedisTemplate redisTemplate) + { + this.redisTemplate = redisTemplate; + } + + @Autowired + public void setLimitScript(RedisScript limitScript) + { + this.limitScript = limitScript; + } + + @Before("@annotation(rateLimiter)") + public void doBefore(JoinPoint point, RateLimiter rateLimiter) throws Throwable + { + int time = rateLimiter.time(); + int count = rateLimiter.count(); + + String combineKey = getCombineKey(rateLimiter, point); + List keys = Collections.singletonList(combineKey); + try + { + Long number = redisTemplate.execute(limitScript, keys, count, time); + if (StringUtils.isNull(number) || number.intValue() > count) + { + throw new ServiceException("访问过于频繁,请稍候再试"); + } + log.info("限制请求'{}',当前请求'{}',缓存key'{}'", count, number.intValue(), combineKey); + } + catch (ServiceException e) + { + throw e; + } + catch (Exception e) + { + throw new RuntimeException("服务器限流异常,请稍候再试"); + } + } + + public String getCombineKey(RateLimiter rateLimiter, JoinPoint point) + { + StringBuffer stringBuffer = new StringBuffer(rateLimiter.key()); + if (rateLimiter.limitType() == LimitType.IP) + { + stringBuffer.append(IpUtils.getIpAddr(ServletUtils.getRequest())).append("-"); + } + MethodSignature signature = (MethodSignature) point.getSignature(); + Method method = signature.getMethod(); + Class targetClass = method.getDeclaringClass(); + stringBuffer.append(targetClass.getName()).append("-").append(method.getName()); + return stringBuffer.toString(); + } +} diff --git a/src/main/java/com/jxy/windminer/common/security/auth/AuthLogic.java b/src/main/java/com/jxy/windminer/common/security/auth/AuthLogic.java new file mode 100644 index 0000000..b756bef --- /dev/null +++ b/src/main/java/com/jxy/windminer/common/security/auth/AuthLogic.java @@ -0,0 +1,372 @@ +package com.jxy.windminer.common.security.auth; + +//import com.jxy.common.security.annotation.RequiresPermissions; +import com.jxy.windminer.common.exception.auth.NotLoginException; +import com.jxy.windminer.common.exception.auth.NotPermissionException; +import com.jxy.windminer.common.exception.auth.NotRoleException; +import com.jxy.windminer.common.security.annotation.Logical; +import com.jxy.windminer.common.security.annotation.RequiresLogin; +import com.jxy.windminer.common.security.annotation.RequiresRoles; +import com.jxy.windminer.common.security.service.TokenService; +import com.jxy.windminer.common.security.utils.SecurityUtils; +import com.jxy.windminer.common.utils.SpringUtils; +import com.jxy.windminer.common.utils.StringUtils; +import com.jxy.windminer.entity.auth.LoginUser; +import org.springframework.util.PatternMatchUtils; + +import java.util.Collection; +import java.util.HashSet; +import java.util.Set; + +/** + * @Description 权限验证的逻辑实现类 + * @Date 2022/5/13 14:04 + * @Author 杜懿 + */ +public class AuthLogic { + + /** 所有权限标识 */ + private static final String ALL_PERMISSION = "*:*:*"; + + /** 管理员角色权限标识 */ + private static final String SUPER_ADMIN = "admin"; + + private TokenService tokenService = SpringUtils.getBean(TokenService.class); + + /** + * 会话注销 + */ + public void logout() + { + String token = SecurityUtils.getToken(); + if (token == null) + { + return; + } + logoutByToken(token); + } + + /** + * 会话注销,根据指定Token + */ + public void logoutByToken(String token) + { + tokenService.delLoginUser(token); + } + + /** + * 检验用户是否已经登录,如未登录,则抛出异常 + */ + public void checkLogin(){getLoginUser();} + + /** + * 获取当前用户缓存信息, 如果未登录,则抛出异常 + * + * @return 用户缓存信息 + */ + public LoginUser getLoginUser() + { + String token = SecurityUtils.getToken(); + if (token == null) + { + throw new NotLoginException("未提供token"); + } + LoginUser loginUser = SecurityUtils.getLoginUser(); + if (loginUser == null) + { + throw new NotLoginException("无效的token"); + } + return loginUser; + } + + /** + * 获取当前用户缓存信息, 如果未登录,则抛出异常 + * + * @param token 前端传递的认证信息 + * @return 用户缓存信息 + */ + public LoginUser getLoginUser(String token) + { + return tokenService.getLoginUser(token); + } + + /** + * 验证当前用户有效期, 如果相差不足120分钟,自动刷新缓存 + * + * @param loginUser 当前用户信息 + */ + public void verifyLoginUserExpire(LoginUser loginUser) + { + tokenService.verifyToken(loginUser); + } + + /** + * 根据注解(@RequiresLogin)鉴权 + * + * @param at 注解对象 + */ + public void checkByAnnotation(RequiresLogin at) + { + this.checkLogin(); + } + + /** + * 根据注解(@RequiresRoles)鉴权 + * + * @param at 注解对象 + */ + public void checkByAnnotation(RequiresRoles at) + { + String[] roleArray = at.value(); + if (at.logical() == Logical.AND) + { + this.checkRoleAnd(roleArray); + } + else + { + this.checkRoleOr(roleArray); + } + } + + /** + * 根据注解(@RequiresPermissions)鉴权 + * + * @param at 注解对象 + */ + //public void checkByAnnotation(RequiresPermissions at) + //{ + // String[] permissionArray = at.value(); + // if (at.logical() == Logical.AND) + // { + // this.checkPermissionAnd(permissionArray); + // } + // else + // { + // this.checkPermissionOr(permissionArray); + // } + //} + + /** + * 获取当前账号的角色列表 + * + * @return 角色列表 + */ + public Set getRoleList() + { + try + { + LoginUser loginUser = getLoginUser(); + return loginUser.getRoles(); + } + catch (Exception e) + { + return new HashSet<>(); + } + } + + /** + * 获取当前账号的权限列表 + * + * @return 权限列表 + */ + //public Set getPermissionList() + //{ + // try + // { + // LoginUser loginUser = getLoginUser(); + // return loginUser.getPermissions(); + // } + // catch (Exception e) + // { + // return new HashSet<>(); + // } + //} + + /** + * 判断用户是否拥有某个角色 + * + * @param role 角色标识 + * @return 用户是否具备某角色 + */ + public boolean hasRole(String role) + { + return hasRole(getRoleList(), role); + } + + /** + * 判断用户是否拥有某个角色, 如果验证未通过,则抛出异常: NotRoleException + * + * @param role 角色标识 + */ + public void checkRole(String role) + { + if (!hasRole(role)) + { + throw new NotRoleException(role); + } + } + + /** + * 根据注解(@RequiresRoles)鉴权 + * + * @param requiresRoles 注解对象 + */ + public void checkRole(RequiresRoles requiresRoles) + { + if (requiresRoles.logical() == Logical.AND) + { + checkRoleAnd(requiresRoles.value()); + } + else + { + checkRoleOr(requiresRoles.value()); + } + } + + /** + * 验证用户是否含有指定角色,必须全部拥有 + * + * @param roles 角色标识数组 + */ + public void checkRoleAnd(String... roles) + { + Set roleList = getRoleList(); + for (String role : roles) + { + if (!hasRole(roleList, role)) + { + throw new NotRoleException(role); + } + } + } + + /** + * 验证用户是否含有指定角色,只需包含其中一个 + * + * @param roles 角色标识数组 + */ + public void checkRoleOr(String... roles) + { + Set roleList = getRoleList(); + for (String role : roles) + { + if (hasRole(roleList, role)) + { + return; + } + } + if (roles.length > 0) + { + throw new NotRoleException(roles); + } + } + + + /** + * 验证用户是否具备某权限 + * + * @param permission 权限字符串 + * @return 用户是否具备某权限 + */ + //public boolean hasPermission(String permission) + //{ + // return hasPermission(getPermissionList(), permission); + //} + + /** + * 验证用户是否具备某权限, 如果验证未通过,则抛出异常: NotPermissionException + * + * @param permission 权限字符串 + * @return 用户是否具备某权限 + */ + //public void checkPermission(String permission) + //{ + // if (!hasPermission(getPermissionList(), permission)) + // { + // throw new NotPermissionException(permission); + // } + //} + + /** + * 根据注解(@RequiresPermissions)鉴权, 如果验证未通过,则抛出异常: NotPermissionException + * + * @param requiresPermissions 注解对象 + */ + //public void checkPermission(RequiresPermissions requiresPermissions) + //{ + // if (requiresPermissions.logical() == Logical.AND) + // { + // checkPermissionAnd(requiresPermissions.value()); + // } + // else + // { + // checkPermissionOr(requiresPermissions.value()); + // } + //} + + /** + * 验证用户是否含有指定权限,必须全部拥有 + * + * @param permissions 权限列表 + */ + //public void checkPermissionAnd(String... permissions) + //{ + // Set permissionList = getPermissionList(); + // for (String permission : permissions) + // { + // if (!hasPermission(permissionList, permission)) + // { + // throw new NotPermissionException(permission); + // } + // } + //} + + /** + * 验证用户是否含有指定权限,只需包含其中一个 + * + * @param permissions 权限码数组 + */ + //public void checkPermissionOr(String... permissions) + //{ + // Set permissionList = getPermissionList(); + // for (String permission : permissions) + // { + // if (hasPermission(permissionList, permission)) + // { + // return; + // } + // } + // if (permissions.length > 0) + // { + // throw new NotPermissionException(permissions); + // } + //} + + + /** + * 判断是否包含权限 + * + * @param authorities 权限列表 + * @param permission 权限字符串 + * @return 用户是否具备某权限 + */ + public boolean hasPermission(Collection authorities, String permission) + { + return authorities.stream().filter(StringUtils::hasText) + .anyMatch(x -> ALL_PERMISSION.contains(x) || PatternMatchUtils.simpleMatch(x, permission)); + } + + /** + * 判断是否包含角色 + * + * @param roles 角色列表 + * @param role 角色 + * @return 用户是否具备某角色权限 + */ + public boolean hasRole(Collection roles, String role) + { + return roles.stream().filter(StringUtils::hasText) + .anyMatch(x -> SUPER_ADMIN.contains(x) || PatternMatchUtils.simpleMatch(x, role)); + } + +} diff --git a/src/main/java/com/jxy/windminer/common/security/auth/AuthUtil.java b/src/main/java/com/jxy/windminer/common/security/auth/AuthUtil.java new file mode 100644 index 0000000..8462339 --- /dev/null +++ b/src/main/java/com/jxy/windminer/common/security/auth/AuthUtil.java @@ -0,0 +1,147 @@ +package com.jxy.windminer.common.security.auth; + +import com.jxy.windminer.common.security.annotation.RequiresRoles; +import com.jxy.windminer.entity.auth.LoginUser; +/** + * @Description 权限验证工具类 + * @Date 2022/5/13 16:17 + * @Author 杜懿 + */ +public class AuthUtil { + + /** + * 底层的AuthLogic对象 + */ + public static AuthLogic authLogic = new AuthLogic(); + + /** + * 检查当前用户是否登陆 + */ + public static void checkLogin(){ + authLogic.checkLogin(); + } + + /** + * 获取当前登陆用户信息 + * @param token + * @return + */ + public static LoginUser getLoginUser(String token){return authLogic.getLoginUser(token);} + + /** + * 验证当前用户有效期 + * @param loginUser + */ + public static void verifyLoginUserExpire(LoginUser loginUser){ + authLogic.verifyLoginUserExpire(loginUser); + } + + /** + *判断当前用户是否有指定的角色标识 + * @param role 角色标识 + * @return 返回布尔值 + */ + public static boolean hasRole(String role){return authLogic.hasRole(role);} + + /** + *当前账号是否含有指定角色标识,如果验证未通过,则抛出异常: NotRoleException + * @param role + */ + public static void checkRole(String role){authLogic.checkRole(role);} + + /** + * 根据注解传入参数鉴权, 如果验证未通过,则抛出异常: NotRoleException + * + * @param requiresRoles 角色权限注解 + */ + public static void checkRole(RequiresRoles requiresRoles) + { + authLogic.checkRole(requiresRoles); + } + + /** + * 当前账号是否含有指定角色标识 [指定多个,必须全部验证通过] + * + * @param roles 角色标识数组 + */ + public static void checkRoleAnd(String... roles) + { + authLogic.checkRoleAnd(roles); + } + + /** + * 当前账号是否含有指定角色标识 [指定多个,只要其一验证通过即可] + * + * @param roles 角色标识数组 + */ + public static void checkRoleOr(String... roles) + { + authLogic.checkRoleOr(roles); + } + + + ///** + // * 判断当前账号是否含有指定权限 + // * + // * @param permission 权限码 + // * @return 是否含有指定权限 + // */ + //public static boolean hasPermission(String permission) + //{ + // return authLogic.hasPermission(permission); + //} + + /** + * 当前账号是否含有指定权限, 如果验证未通过,则抛出异常: NotPermissionException + * + * @param permission 权限码 + */ + //public static void checkPermission(String permission) + //{ + // authLogic.checkPermission(permission); + //} + + /** + * 根据注解传入参数鉴权, 如果验证未通过,则抛出异常: NotPermissionException + * + * @param requiresPermissions 权限注解 + */ + //public static void checkPermission(RequiresPermissions requiresPermissions) + //{ + // authLogic. checkPermission(requiresPermissions); + //} + + /** + * 当前账号是否含有指定权限 [指定多个,必须全部验证通过] + * + * @param permissions 权限码数组 + */ + //public static void checkPermissionAnd(String... permissions) + //{ + // authLogic.checkPermissionAnd(permissions); + //} + + /** + * 当前账号是否含有指定权限 [指定多个,只要其一验证通过即可] + * + * @param permissions 权限码数组 + */ + //public static void checkPermissionOr(String... permissions) + //{ + // authLogic.checkPermissionOr(permissions); + //} + + + + /** + * 会话注销 + */ + public static void logout(){authLogic.logout();} + + /** + * 根据token注销会话 + * @param token + */ + public static void logoutByToken(String token){authLogic.logoutByToken(token);} + +} diff --git a/src/main/java/com/jxy/windminer/common/security/config/ApplicationConfig.java b/src/main/java/com/jxy/windminer/common/security/config/ApplicationConfig.java new file mode 100644 index 0000000..e0962fa --- /dev/null +++ b/src/main/java/com/jxy/windminer/common/security/config/ApplicationConfig.java @@ -0,0 +1,24 @@ +package com.jxy.windminer.common.security.config; + +import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer; +import org.springframework.context.annotation.Bean; + +import java.util.TimeZone; + +/** + * @Description 系统配置 + * @Date 2022/5/13 10:59 + * @Author 杜懿 + */ +public class ApplicationConfig { + + /** + * 时区配置 + */ + @Bean + public Jackson2ObjectMapperBuilderCustomizer jacksonObjectMapperCustomization() + { + return jacksonObjectMapperBuilder -> jacksonObjectMapperBuilder.timeZone(TimeZone.getDefault()); + } + +} diff --git a/src/main/java/com/jxy/windminer/common/security/config/WebMvcConfig.java b/src/main/java/com/jxy/windminer/common/security/config/WebMvcConfig.java new file mode 100644 index 0000000..1e1ec21 --- /dev/null +++ b/src/main/java/com/jxy/windminer/common/security/config/WebMvcConfig.java @@ -0,0 +1,50 @@ +package com.jxy.windminer.common.security.config; + +import com.jxy.windminer.common.security.interceptor.HeaderInterceptor; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.stereotype.Component; +import org.springframework.web.servlet.config.annotation.CorsRegistry; +import org.springframework.web.servlet.config.annotation.InterceptorRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +/** + * @Description 拦截器配置 + * @Date 2023/5/13 17:56 + * @Author 杜懿 + */ +@Configuration +@Component +public class WebMvcConfig implements WebMvcConfigurer { + + /** 不需要拦截的地址 */ + public static final String[] excludeUrls = {"/**/login","/**/emailcode","/logout", + "/**/refresh","/**/hello", + "/**/resetPwdcode","/**/resetPwd", + "/**/register","/**/getFaults", + "/**/downloadFile","/**/getTools", + "/order/endOrder","/order/test","/order/getPayAddress","/order/payment","/order/getPayment","/order/ifConfirm", + "/**/wpUserLogin","/**/importTemplate"}; + + @Override + public void addInterceptors(InterceptorRegistry registry) { + registry.addInterceptor(getHeaderInterceptor()) + .addPathPatterns("/**") + .excludePathPatterns(excludeUrls) + .order(-10); + } + + @Override + public void addCorsMappings(CorsRegistry registry) { + registry.addMapping("/**")//项目中的所有接口都支持跨域 + .allowedOriginPatterns("*")//所有地址都可以访问,也可以配置具体地址 + .allowCredentials(true) + .allowedMethods("*")//"GET", "HEAD", "POST", "PUT", "DELETE", "OPTIONS" + .maxAge(3600);// 跨域允许时间 + } + /** + * 自定义请求头拦截器 + */ + @Bean + public HeaderInterceptor getHeaderInterceptor(){return new HeaderInterceptor();} +} diff --git a/src/main/java/com/jxy/windminer/common/security/handler/GlobalExceptionHandler.java b/src/main/java/com/jxy/windminer/common/security/handler/GlobalExceptionHandler.java new file mode 100644 index 0000000..183dcf1 --- /dev/null +++ b/src/main/java/com/jxy/windminer/common/security/handler/GlobalExceptionHandler.java @@ -0,0 +1,213 @@ +package com.jxy.windminer.common.security.handler; + +import com.jxy.windminer.common.constant.HttpStatus; +import com.jxy.windminer.common.exception.InnerAuthException; +import com.jxy.windminer.common.exception.ServiceException; +import com.jxy.windminer.common.exception.auth.*; +import com.jxy.windminer.common.exception.file.FileNameLengthLimitExceededException; +import com.jxy.windminer.common.security.utils.SecurityUtils; +import com.jxy.windminer.common.utils.StringUtils; +import com.jxy.windminer.common.web.Result.AjaxResult; +import com.jxy.windminer.entity.auth.LoginUser; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.core.Ordered; +import org.springframework.core.annotation.Order; +import org.springframework.jdbc.BadSqlGrammarException; +import org.springframework.validation.BindException; +import org.springframework.web.HttpRequestMethodNotSupportedException; +import org.springframework.web.bind.MethodArgumentNotValidException; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RestControllerAdvice; +import org.springframework.web.multipart.MultipartException; + +import javax.servlet.http.HttpServletRequest; +import java.sql.SQLException; + +/** + * @Description 全局异常处理器 + * @Date 2022/5/13 11:36 + * @Author 杜懿 + */ +@Order(Ordered.HIGHEST_PRECEDENCE) +@RestControllerAdvice +public class GlobalExceptionHandler { + + private static final Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class); + + /** + * 权限码异常 + */ + @ExceptionHandler(NotPermissionException.class) + public AjaxResult handleNotPermissionException(NotPermissionException e, HttpServletRequest request) + { + String requestURI = request.getRequestURI(); + log.error("请求地址'{}',权限码校验失败'{}'", requestURI, e.getMessage()); + return AjaxResult.error(HttpStatus.FORBIDDEN, "没有访问权限,请联系管理员授权"); + } + + /** + * 令牌为空异常 + */ + @ExceptionHandler(UnAuthorizedException.class) + public AjaxResult handleUnAuthorizedException(UnAuthorizedException e, HttpServletRequest request) + { + String requestURI = request.getRequestURI(); + log.error("请求地址'{}',token令牌异常'{}'", requestURI, e.getMessage()); + return AjaxResult.error(HttpStatus.UNAUTHORIZED, e.getMessage()); + } + + /** + * 令牌过期异常 + */ + @ExceptionHandler(TokenExpiresException.class) + public AjaxResult handleTokenExpiresException(TokenExpiresException e, HttpServletRequest request) + { + String requestURI = request.getRequestURI(); + log.error("请求地址'{}',token令牌异常'{}'", requestURI, e.getMessage()); + return AjaxResult.error(HttpStatus.TOKENEXPIRES, e.getMessage()); + } + + /** + * 登录过期异常 + */ + @ExceptionHandler(LoginExpiresException.class) + public AjaxResult handleLoginExpiresException(LoginExpiresException e, HttpServletRequest request) + { + String requestURI = request.getRequestURI(); + log.error("请求地址'{}',token令牌异常'{}'", requestURI, e.getMessage()); + return AjaxResult.error(HttpStatus.LOGINEXPIRES, e.getMessage()); + } + + /** + * 角色权限异常 + */ + @ExceptionHandler(NotRoleException.class) + public AjaxResult handleNotRoleException(NotRoleException e, HttpServletRequest request) + { + String requestURI = request.getRequestURI(); + LoginUser loginUser = SecurityUtils.getLoginUser(); + System.out.println(loginUser.getRoles()); + log.error("请求地址'{}',角色权限校验失败'{}'", requestURI, e.getMessage()); + return AjaxResult.error(HttpStatus.FORBIDDEN, "权限不足,请自行升级或联系管理员授权"); + } + + /** + * 请求方式不支持 + */ + @ExceptionHandler(HttpRequestMethodNotSupportedException.class) + public AjaxResult handleHttpRequestMethodNotSupported(HttpRequestMethodNotSupportedException e, + HttpServletRequest request) + { + String requestURI = request.getRequestURI(); + log.error("请求地址'{}',不支持'{}'请求", requestURI, e.getMethod()); + return AjaxResult.error(e.getMessage()); + } + + /** + * 业务异常 + */ + @ExceptionHandler(ServiceException.class) + public AjaxResult handleServiceException(ServiceException e, HttpServletRequest request) + { + log.error(e.getMessage(), e); + Integer code = e.getCode(); + return StringUtils.isNotNull(code) ? AjaxResult.error(code, e.getMessage()) : AjaxResult.error(e.getMessage()); + } + + /** + * 拦截未知的运行时异常 + */ + @ExceptionHandler(RuntimeException.class) + public AjaxResult handleRuntimeException(RuntimeException e, HttpServletRequest request) + { + String requestURI = request.getRequestURI(); + log.error("请求地址'{}',发生未知异常.", requestURI, e); + return AjaxResult.error(e.getMessage()); + } + + /** + * 拦截文件异常异常 + */ + @ExceptionHandler(MultipartException.class) + public AjaxResult handleMultException(MultipartException e, HttpServletRequest request) + { + String requestURI = request.getRequestURI(); + log.error("请求地址'{}',发生文件异常.", requestURI, e); + return AjaxResult.error("文件过大,单个文件最大2M"); + } + + /** + * 拦截文件名超长异常 + */ + @ExceptionHandler(FileNameLengthLimitExceededException.class) + public AjaxResult handleFileNameLengthException(FileNameLengthLimitExceededException e, HttpServletRequest request) + { + String requestURI = request.getRequestURI(); + log.error("请求地址'{}',发生文件异常.", requestURI, e); + return AjaxResult.error(e.getMessage()); + } + + /** + * 系统异常 + */ + @ExceptionHandler(Exception.class) + public AjaxResult handleException(Exception e, HttpServletRequest request) + { + String requestURI = request.getRequestURI(); + log.error("请求地址'{}',发生系统异常.", requestURI, e); + return AjaxResult.error(e.getMessage()); + } + + /** + * 自定义验证异常 + */ + @ExceptionHandler(BindException.class) + public AjaxResult handleBindException(BindException e) + { + log.error(e.getMessage(), e); + String message = e.getAllErrors().get(0).getDefaultMessage(); + return AjaxResult.error(message); + } + + /** + * 自定义验证异常 + */ + @ExceptionHandler(MethodArgumentNotValidException.class) + public Object handleMethodArgumentNotValidException(MethodArgumentNotValidException e) + { + log.error(e.getMessage(), e); + String message = e.getBindingResult().getFieldError().getDefaultMessage(); + return AjaxResult.error(message); + } + + /** + * 内部认证异常 + */ + @ExceptionHandler(InnerAuthException.class) + public AjaxResult handleInnerAuthException(InnerAuthException e) + { + return AjaxResult.error(e.getMessage()); + } + + + /** + * sql查询验证异常 + */ + @ExceptionHandler(SQLException.class) + public AjaxResult handleSQLException(SQLException e, HttpServletRequest request) + { + String requestURI = request.getRequestURI(); + log.error("请求地址'{}',发生sql执行异常.", requestURI, e); + return AjaxResult.error("请求地址'{}',发生sql执行异常."); + } + + @ExceptionHandler(BadSqlGrammarException.class) + public AjaxResult handleBadSqlGrammarException(BadSqlGrammarException e, HttpServletRequest request) + { + String requestURI = request.getRequestURI(); + log.error("请求地址'{}',发生SQL异常.", requestURI, e); + return AjaxResult.error(HttpStatus.ERROR, "数据库异常!"); + } + //todo 根据业务需求增加异常 +} diff --git a/src/main/java/com/jxy/windminer/common/security/interceptor/HeaderInterceptor.java b/src/main/java/com/jxy/windminer/common/security/interceptor/HeaderInterceptor.java new file mode 100644 index 0000000..8d15b0e --- /dev/null +++ b/src/main/java/com/jxy/windminer/common/security/interceptor/HeaderInterceptor.java @@ -0,0 +1,125 @@ +package com.jxy.windminer.common.security.interceptor; + +import com.jxy.windminer.common.constant.CacheConstants; +import com.jxy.windminer.common.constant.HttpStatus; +import com.jxy.windminer.common.constant.SecurityConstants; +import com.jxy.windminer.common.constant.TokenConstants; +import com.jxy.windminer.common.context.SecurityContextHolder; +import com.jxy.windminer.common.exception.auth.LoginExpiresException; +import com.jxy.windminer.common.exception.auth.TokenExpiresException; +import com.jxy.windminer.common.exception.auth.UnAuthorizedException; +import com.jxy.windminer.common.redis.service.RedisService; +import com.jxy.windminer.common.security.auth.AuthUtil; +import com.jxy.windminer.common.security.utils.SecurityUtils; +import com.jxy.windminer.common.utils.JwtUtils; +import com.jxy.windminer.common.utils.ServletUtils; +import com.jxy.windminer.common.utils.StringUtils; +import com.jxy.windminer.entity.auth.LoginUser; +import io.jsonwebtoken.Claims; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.method.HandlerMethod; +import org.springframework.web.servlet.AsyncHandlerInterceptor; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +/** + * @Description 自定义请求头拦截器,将请求头数据封装到线程变量中方便后续获取使用 + * 同时获取当前用户有效时间并刷新有效期 + * @Date 2022/5/13 17:29 + * @Author 杜懿 + */ +public class HeaderInterceptor implements AsyncHandlerInterceptor { + + private static final Logger log = LoggerFactory.getLogger(HeaderInterceptor.class); + + @Autowired + private RedisService redisService; + + @Override + public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { + + System.out.println(handler); + System.out.println(handler.toString()); + + if(!(handler instanceof HandlerMethod)){ + + System.out.println("直接放行了!"); + + return true; + + } + //解析token + String token = SecurityUtils.getToken(); + if (StringUtils.isNotEmpty(token) && token.startsWith(TokenConstants.PREFIX)) + { + System.out.println("裁剪了token"); + token = token.replaceFirst(TokenConstants.PREFIX, ""); + } + if (StringUtils.isEmpty(token)) + { + throw new UnAuthorizedException("令牌不能为空"); + } + Claims claims = null; + try { + claims = JwtUtils.parseToken(token); + }catch (Exception e){ + throw new UnAuthorizedException("令牌解析失败!"); + } + + if (claims == null) + { + throw new TokenExpiresException("令牌已过期或验证不正确!"); + } + String userkey = JwtUtils.getUserKey(claims); + boolean islogin = redisService.hasKey(getTokenKey(userkey)); + if (!islogin) + { + throw new LoginExpiresException("登录状态已过期!"); + } + String userid = JwtUtils.getUserId(claims); + String username = JwtUtils.getUserName(claims); + if (StringUtils.isEmpty(userid) || StringUtils.isEmpty(username)) + { + throw new UnAuthorizedException("令牌验证失败!"); + + } + if(StringUtils.isNotEmpty(token)){ + SecurityContextHolder.setUserId(JwtUtils.getUserId(token)); + SecurityContextHolder.setUserName(JwtUtils.getUserName(token)); + SecurityContextHolder.setUserKey(JwtUtils.getUserKey(token)); + SecurityContextHolder.setEmail(JwtUtils.getEmail(token)); + LoginUser loginUser = AuthUtil.getLoginUser(token); + if(StringUtils.isNotNull(loginUser)){ + + //验证当前用户有效时间 + AuthUtil.verifyLoginUserExpire(loginUser); + SecurityContextHolder.set(SecurityConstants.LOGIN_USER,loginUser); + } + + }else { + + } + + return true; + } + + @Override + public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { + + SecurityContextHolder.remove(); + + } + + + /** + * 获取缓存key + */ + private String getTokenKey(String token) + { + return CacheConstants.LOGIN_TOKEN_KEY + token; + } + +} diff --git a/src/main/java/com/jxy/windminer/common/security/service/TokenService.java b/src/main/java/com/jxy/windminer/common/security/service/TokenService.java new file mode 100644 index 0000000..7786df7 --- /dev/null +++ b/src/main/java/com/jxy/windminer/common/security/service/TokenService.java @@ -0,0 +1,192 @@ +package com.jxy.windminer.common.security.service; + + + +import com.jxy.windminer.common.constant.CacheConstants; +import com.jxy.windminer.common.constant.OrderConstants; +import com.jxy.windminer.common.constant.SecurityConstants; +import com.jxy.windminer.common.redis.service.RedisService; +import com.jxy.windminer.common.security.utils.SecurityUtils; +import com.jxy.windminer.common.utils.JwtUtils; +import com.jxy.windminer.common.utils.ServletUtils; +import com.jxy.windminer.common.utils.StringUtils; +import com.jxy.windminer.common.utils.ip.IpUtils; +import com.jxy.windminer.common.utils.uuid.IdUtils; +import com.jxy.windminer.entity.auth.LoginUser; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import javax.servlet.http.HttpServletRequest; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.TimeUnit; + +/** + * @Description token验证处理 + * @Date 2022/5/13 10:06 + * @Author 杜懿 + */ +@Component +public class TokenService { + + @Autowired + private RedisService redisService; + + /** 秒 */ + protected static final long MILLIS_SECOND = 1000; + + /** 分 */ + protected static final long MILLIS_MINUTE = 60 * MILLIS_SECOND; + + /** 过期时间/缓存有效期 一个月*/ + private final static long expireTime = CacheConstants.EXPIRATION ; + + /** 权限缓存前缀 */ + private final static String ACCESS_TOKEN = CacheConstants.LOGIN_TOKEN_KEY; + + /** 缓存刷新时间 */ + private final static Long MILLIS_MINUTE_TEN = CacheConstants.REFRESH_TIME * MILLIS_MINUTE; + + /** + * 创建令牌 + */ + public Map createToken(LoginUser loginUser) + { + String token = IdUtils.fastUUID(); + Long userId = loginUser.getSysUser().getUserId(); + String userName = loginUser.getSysUser().getUserName(); + String email = loginUser.getSysUser().getEmail(); + loginUser.setToken(token); + loginUser.setUserid(userId); + loginUser.setUsername(userName); + loginUser.setIpaddr(IpUtils.getIpAddr(ServletUtils.getRequest())); + refreshToken(loginUser); + + // Jwt存储信息 + Map claimsMap = new HashMap(); + claimsMap.put(SecurityConstants.USER_KEY, token); + claimsMap.put(SecurityConstants.DETAILS_USER_ID, userId); + claimsMap.put(SecurityConstants.DETAILS_USERNAME, userName); + claimsMap.put(SecurityConstants.DETAILS_EMAIL,email); + + + // 接口返回信息 + Map rspMap = new HashMap(); + String accessToken = JwtUtils.createToken(claimsMap); + //将token存入redis 以便于通过用户邮箱拿到token + if(redisService.hasKey(OrderConstants.TOKEN_PRE +email)){ + redisService.deleteObject(OrderConstants.TOKEN_PRE+email); + } + redisService.setCacheObject(OrderConstants.TOKEN_PRE+email,accessToken,expireTime,TimeUnit.MINUTES); + + rspMap.put("access_token", accessToken); + rspMap.put("expires_in", expireTime); + rspMap.put("userName", loginUser.getUsername()); + return rspMap; + } + + /** + * 获取用户身份信息 + * + * @return 用户信息 + */ + public LoginUser getLoginUser() + { + return getLoginUser(ServletUtils.getRequest()); + } + + /** + * 获取用户身份信息 + * + * @return 用户信息 + */ + public LoginUser getLoginUser(HttpServletRequest request) + { + // 获取请求携带的令牌 + String token = SecurityUtils.getToken(request); + return getLoginUser(token); + } + + /** + * 获取用户身份信息 + * + * @return 用户信息 + */ + public LoginUser getLoginUser(String token) + { + LoginUser user = null; + try + { + if (StringUtils.isNotEmpty(token)) + { + String userkey = JwtUtils.getUserKey(token); + user = redisService.getCacheObject(getTokenKey(userkey)); + return user; + } + } + catch (Exception e) + { + } + return user; + } + + /** + * 设置用户身份信息 + */ + public void setLoginUser(LoginUser loginUser) + { + if (StringUtils.isNotNull(loginUser) && StringUtils.isNotEmpty(loginUser.getToken())) + { + refreshToken(loginUser); + } + } + + /** + * 删除用户缓存信息 + */ + public void delLoginUser(String token) + { + if (StringUtils.isNotEmpty(token)) + { + String userkey = JwtUtils.getUserKey(token); + //System.out.println("用户登出,从redis删除key:"+userkey); + redisService.deleteObject(getTokenKey(userkey)); + } + } + + /** + * 验证令牌有效期,相差不足120分钟,自动刷新缓存 + * + * @param loginUser + */ + public void verifyToken(LoginUser loginUser) + { + long expireTime = loginUser.getExpireTime(); + long currentTime = System.currentTimeMillis(); + //可以考虑直接刷新缓存 而不是小于才刷新缓存 + if (expireTime - currentTime <= MILLIS_MINUTE_TEN) + { + refreshToken(loginUser); + } + } + + /** + * 刷新令牌有效期 + * + * @param loginUser 登录信息 + */ + public void refreshToken(LoginUser loginUser) + { + loginUser.setLoginTime(System.currentTimeMillis()); + loginUser.setExpireTime(loginUser.getLoginTime() + expireTime * MILLIS_MINUTE); + // 根据uuid将loginUser缓存 + String userKey = getTokenKey(loginUser.getToken()); + redisService.setCacheObject(userKey, loginUser, expireTime, TimeUnit.MINUTES); + } + + private String getTokenKey(String token) + { + return ACCESS_TOKEN + token; + } + +} diff --git a/src/main/java/com/jxy/windminer/common/security/utils/SecurityUtils.java b/src/main/java/com/jxy/windminer/common/security/utils/SecurityUtils.java new file mode 100644 index 0000000..eeb39fa --- /dev/null +++ b/src/main/java/com/jxy/windminer/common/security/utils/SecurityUtils.java @@ -0,0 +1,129 @@ +package com.jxy.windminer.common.security.utils; + + +import com.jxy.windminer.common.constant.SecurityConstants; +import com.jxy.windminer.common.constant.TokenConstants; +import com.jxy.windminer.common.context.SecurityContextHolder; +import com.jxy.windminer.common.utils.ServletUtils; +import com.jxy.windminer.common.utils.StringUtils; +import com.jxy.windminer.entity.auth.LoginUser; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; + +import javax.servlet.http.HttpServletRequest; + +/** + * @Description 权限获取工具类 + * @Date 2022/5/11 16:58 + * @Author 杜懿 + */ +public class SecurityUtils { + + + /** + * 获取用户ID + */ + public static Long getUserId() + { + return SecurityContextHolder.getUserId(); + } + + /** + * 获取用户名称 + */ + public static String getUsername() + { + return SecurityContextHolder.getUserName(); + } + + /** + * 获取用户key + */ + public static String getUserKey() + { + return SecurityContextHolder.getUserKey(); + } + + /** + * 获取用户邮箱 + */ + public static String getEmail() + { + return SecurityContextHolder.getEmail(); + } + + /** + * 获取登录用户信息 + */ + public static LoginUser getLoginUser() + { + return SecurityContextHolder.get(SecurityConstants.LOGIN_USER, LoginUser.class); + } + + /** + * 获取请求token + */ + public static String getToken() + { + return getToken(ServletUtils.getRequest()); + } + + /** + * 根据request获取请求token + */ + public static String getToken(HttpServletRequest request) + { + // 从header获取token标识 + String token = request.getHeader(TokenConstants.AUTHENTICATION); + return replaceTokenPrefix(token); + } + + /** + * 裁剪token前缀 + */ + public static String replaceTokenPrefix(String token) + { + // 如果前端设置了令牌前缀,则裁剪掉前缀 + if (StringUtils.isNotEmpty(token) && token.startsWith(TokenConstants.PREFIX)) + { + token = token.replaceFirst(TokenConstants.PREFIX, ""); + } + return token; + } + + /** + * 是否为管理员 + * + * @param userId 用户ID + * @return 结果 + */ + public static boolean isAdmin(Long userId) + { + return userId != null && 1L == userId; + } + + /** + * 生成BCryptPasswordEncoder密码 + * + * @param password 密码 + * @return 加密字符串 + */ + public static String encryptPassword(String password) + { + BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder(); + return passwordEncoder.encode(password); + } + + /** + * 判断密码是否相同 + * + * @param rawPassword 真实密码 + * @param encodedPassword 加密后字符 + * @return 结果 + */ + public static boolean matchesPassword(String rawPassword, String encodedPassword) + { + BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder(); + return passwordEncoder.matches(rawPassword, encodedPassword); + } + +} diff --git a/src/main/java/com/jxy/windminer/common/text/CharsetKit.java b/src/main/java/com/jxy/windminer/common/text/CharsetKit.java new file mode 100644 index 0000000..d605707 --- /dev/null +++ b/src/main/java/com/jxy/windminer/common/text/CharsetKit.java @@ -0,0 +1,88 @@ +package com.jxy.windminer.common.text; + + +import com.jxy.windminer.common.utils.StringUtils; + +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; + +/** + * @Description 字符集工具 + * @Date 2022/5/9 9:01 + * @Author 杜懿 + */ +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/jxy/windminer/common/text/Convert.java b/src/main/java/com/jxy/windminer/common/text/Convert.java new file mode 100644 index 0000000..71cbdf3 --- /dev/null +++ b/src/main/java/com/jxy/windminer/common/text/Convert.java @@ -0,0 +1,916 @@ +package com.jxy.windminer.common.text; + +import com.jxy.windminer.common.utils.StringUtils; + +import java.math.BigDecimal; +import java.math.BigInteger; +import java.nio.ByteBuffer; +import java.nio.charset.Charset; +import java.text.NumberFormat; +import java.util.Set; + +/** + * @Description 类型转换器 + * @Date 2022/5/9 9:13 + * @Author 杜懿 + */ +public class Convert +{ + /** + * 转换为字符串
+ * + * @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(); + } + + /** + * 转换为字符串
+ * + * @param value 被转换的值 + * @return 结果 + */ + public static String toStr(Object value) + { + return toStr(value, 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); + } + + /** + * 转换为字符
+ * + * @param value 被转换的值 + * @return 结果 + */ + public static Character toChar(Object value) + { + return toChar(value, null); + } + + /** + * 转换为byte
+ * + * @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
+ * + * @param value 被转换的值 + * @return 结果 + */ + public static Byte toByte(Object value) + { + return toByte(value, null); + } + + /** + * 转换为Short
+ * + * @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
+ * + * @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
+ * + * @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
+ * + * @param value 被转换的值 + * @return 结果 + */ + public static Integer toInt(Object value) + { + return toInt(value, null); + } + + /** + * 转换为Integer数组
+ * + * @param str 被转换的值 + * @return 结果 + */ + public static Integer[] toIntArray(String str) + { + return toIntArray(",", str); + } + + /** + * 转换为Long数组
+ * + * @param str 被转换的值 + * @return 结果 + */ + public static Long[] toLongArray(String str) + { + return toLongArray(",", str); + } + + /** + * 转换为Integer数组
+ * + * @param split 分隔符 + * @param split 被转换的值 + * @return 结果 + */ + public static Integer[] toIntArray(String split, String str) + { + if (StringUtils.isEmpty(str)) + { + return new Integer[] {}; + } + String[] arr = str.split(split); + final Integer[] ints = new Integer[arr.length]; + for (int i = 0; i < arr.length; i++) + { + final Integer v = toInt(arr[i], 0); + ints[i] = v; + } + return ints; + } + + /** + * 转换为Long数组
+ * + * @param split 分隔符 + * @param str 被转换的值 + * @return 结果 + */ + public static Long[] toLongArray(String split, String str) + { + if (StringUtils.isEmpty(str)) + { + return new Long[] {}; + } + String[] arr = str.split(split); + final Long[] longs = new Long[arr.length]; + for (int i = 0; i < arr.length; i++) + { + final Long v = toLong(arr[i], null); + longs[i] = v; + } + return longs; + } + + /** + * 转换为String数组
+ * + * @param str 被转换的值 + * @return 结果 + */ + public static String[] toStrArray(String str) + { + return toStrArray(",", str); + } + + /** + * 转换为String数组
+ * + * @param split 分隔符 + * @param split 被转换的值 + * @return 结果 + */ + public static String[] toStrArray(String split, String str) + { + return str.split(split); + } + + /** + * 转换为long
+ * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static Long toLong(Object value, Long defaultValue) + { + if (value == null) + { + return defaultValue; + } + if (value instanceof Long) + { + return (Long) value; + } + if (value instanceof Number) + { + return ((Number) value).longValue(); + } + final String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) + { + return defaultValue; + } + try + { + // 支持科学计数法 + return new BigDecimal(valueStr.trim()).longValue(); + } + catch (Exception e) + { + return defaultValue; + } + } + + /** + * 转换为long
+ * + * @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
+ * + * @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
+ * + * @param value 被转换的值 + * @return 结果 + */ + public static Float toFloat(Object value) + { + return toFloat(value, null); + } + + /** + * 转换为boolean
+ * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static Boolean toBool(Object value, Boolean defaultValue) + { + if (value == null) + { + return defaultValue; + } + if (value instanceof Boolean) + { + return (Boolean) value; + } + String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) + { + return defaultValue; + } + valueStr = valueStr.trim().toLowerCase(); + switch (valueStr) + { + case "true": + return true; + case "false": + return false; + case "yes": + return true; + case "ok": + return true; + case "no": + return false; + case "1": + return true; + case "0": + return false; + default: + return defaultValue; + } + } + + /** + * 转换为boolean
+ * + * @param value 被转换的值 + * @return 结果 + */ + public static Boolean toBool(Object value) + { + return toBool(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
+ * + * @param value 被转换的值 + * @return 结果 + */ + public static BigInteger toBigInteger(Object value) + { + return toBigInteger(value, null); + } + + /** + * 转换为BigDecimal
+ * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static BigDecimal toBigDecimal(Object value, BigDecimal defaultValue) + { + if (value == null) + { + return defaultValue; + } + if (value instanceof BigDecimal) + { + return (BigDecimal) value; + } + if (value instanceof Long) + { + return new BigDecimal((Long) value); + } + if (value instanceof Double) + { + return new BigDecimal((Double) value); + } + if (value instanceof Integer) + { + return new BigDecimal((Integer) value); + } + final String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) + { + return defaultValue; + } + try + { + return new BigDecimal(valueStr); + } + catch (Exception e) + { + return defaultValue; + } + } + + /** + * 转换为BigDecimal
+ * 如果给定的值为空,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @return 结果 + */ + public static BigDecimal toBigDecimal(Object value) + { + return toBigDecimal(value, null); + } + + /** + * 将对象转为字符串
+ * + * @param obj 对象 + * @return 字符串 + */ + public static String utf8Str(Object obj) + { + return str(obj, CharsetKit.CHARSET_UTF_8); + } + + /** + * 将对象转为字符串
+ * + * @param obj 对象 + * @param charsetName 字符集 + * @return 字符串 + */ + public static String str(Object obj, String charsetName) + { + return str(obj, Charset.forName(charsetName)); + } + + /** + * 将对象转为字符串
+ * + * @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); + } + } + String returnString = new String(c); + + return returnString; + } + + /** + * 数字金额大写转换 先写个完整的然后将如零拾替换成零 + * + * @param n 数字 + * @return 中文大写数字 + */ + public static String digitUppercase(double n) + { + String[] fraction = { "角", "分" }; + String[] digit = { "零", "壹", "贰", "叁", "肆", "伍", "陆", "柒", "捌", "玖" }; + String[][] unit = { { "元", "万", "亿" }, { "", "拾", "佰", "仟" } }; + + String head = n < 0 ? "负" : ""; + n = Math.abs(n); + + String s = ""; + for (int i = 0; i < fraction.length; i++) + { + s += (digit[(int) (Math.floor(n * 10 * Math.pow(10, i)) % 10)] + fraction[i]).replaceAll("(零.)+", ""); + } + if (s.length() < 1) + { + s = "整"; + } + int integerPart = (int) Math.floor(n); + + for (int i = 0; i < unit[0].length && integerPart > 0; i++) + { + String p = ""; + for (int j = 0; j < unit[1].length && n > 0; j++) + { + p = digit[integerPart % 10] + unit[1][j] + p; + integerPart = integerPart / 10; + } + s = p.replaceAll("(零.)*零$", "").replaceAll("^$", "零") + unit[0][i] + s; + } + return head + s.replaceAll("(零.)*零元", "元").replaceFirst("(零.)+", "").replaceAll("(零.)+", "零").replaceAll("^整$", "零元整"); + } +} diff --git a/src/main/java/com/jxy/windminer/common/text/StrFormatter.java b/src/main/java/com/jxy/windminer/common/text/StrFormatter.java new file mode 100644 index 0000000..870c43e --- /dev/null +++ b/src/main/java/com/jxy/windminer/common/text/StrFormatter.java @@ -0,0 +1,93 @@ +package com.jxy.windminer.common.text; + + +import com.jxy.windminer.common.utils.StringUtils; + +/** + * @Description 字符串格式化 + * @Date 2022/5/9 9:52 + * @Author 杜懿 + */ +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/jxy/windminer/common/utils/CodeUtils.java b/src/main/java/com/jxy/windminer/common/utils/CodeUtils.java new file mode 100644 index 0000000..f234fd3 --- /dev/null +++ b/src/main/java/com/jxy/windminer/common/utils/CodeUtils.java @@ -0,0 +1,43 @@ +package com.jxy.windminer.common.utils; + +import java.util.Random; + +/** + * @Description TODO + * @Date 2022/11/18 14:26 + * @Author 杜懿 + */ +public class CodeUtils { + //public static void main(String[] args) { + // String s = creatCode(4); + // //System.out.println("随机验证码为:" + s); + //} + + //定义一个方法返回一个随机验证码 + public static String creatCode(int n) { + + String code = ""; + Random r = new Random(); + //2.在方法内部使用for循环生成指定位数的随机字符,并连接起来 + for (int i = 0; i <= n; i++) { + //生成一个随机字符:大写 ,小写 ,数字(0 1 2) + int type = r.nextInt(3); + switch (type) { + case 0: + char ch = (char) (r.nextInt(26) + 65); + code += ch; + break; + case 1: + char ch1 = (char) (r.nextInt(26) + 97); + code += ch1; + break; + case 2: + code += r.nextInt(10); + break; + } + + } + return code; + } + +} diff --git a/src/main/java/com/jxy/windminer/common/utils/DateUtils.java b/src/main/java/com/jxy/windminer/common/utils/DateUtils.java new file mode 100644 index 0000000..bda2f31 --- /dev/null +++ b/src/main/java/com/jxy/windminer/common/utils/DateUtils.java @@ -0,0 +1,310 @@ +package com.jxy.windminer.common.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.Calendar; +import java.util.Date; +import java.util.TimeZone; + +/** + * @Description 时间工具类 + * @Date 2022/5/9 18:21 + * @Author 杜懿 + */ +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 parseCNDate(Object str) + { + if (str == null) + { + return null; + } + try + { + Date date = parseDate(str.toString(), parsePatterns); + return UTCToCNDate(date); + } + catch (ParseException e) + { + return null; + } + } + + /** + * 获取服务器启动时间 + */ + public static Date getServerStartDate() + { + long time = ManagementFactory.getRuntimeMXBean().getStartTime(); + return new Date(time); + } + + /** + * 计算两个时间差 + */ + public static String getDatePoor(Date endDate, Date nowDate) + { + long nd = 1000 * 24 * 60 * 60; + long nh = 1000 * 60 * 60; + long nm = 1000 * 60; + // long ns = 1000; + // 获得两个时间的毫秒时间差异 + long diff = endDate.getTime() - nowDate.getTime(); + // 计算差多少天 + long day = diff / nd; + // 计算差多少小时 + long hour = diff % nd / nh; + // 计算差多少分钟 + long min = diff % nd % nh / nm; + // 计算差多少秒//输出结果 + // long sec = diff % nd % nh % nm / ns; + return day + "天" + hour + "小时" + min + "分钟"; + } + /** + * 计算两个时间差 天数 + */ + public static Long getDateDiff(Date endDate, Date nowDate) + { + long nd = 1000 * 24 * 60 * 60; + long nh = 1000 * 60 * 60; + long nm = 1000 * 60; + // long ns = 1000; + // 获得两个时间的毫秒时间差异 + long diff = endDate.getTime() - nowDate.getTime(); + // 计算差多少天 + long day = diff / nd; + return day; + } + + public static Long getDateDiffSecond(Date endDate, Date nowDate) + { + long nd = 1000 * 24 * 60 * 60; + long nh = 1000 * 60 * 60; + long nm = 1000 * 60; + long ns = 1000; + // 获得两个时间的毫秒时间差异 + long diff = endDate.getTime() - nowDate.getTime(); + // 计算差多少秒 + long sec = diff % nd % nh % nm / ns; + return sec; + } + + /** + * 计算两个时间差 按月 + */ + public static int getMonthDiff(Date d1, Date d2) { + Calendar c1 = Calendar.getInstance(); + Calendar c2 = Calendar.getInstance(); + c1.setTime(d1); + c2.setTime(d2); + int year1 = c1.get(Calendar.YEAR); + int year2 = c2.get(Calendar.YEAR); + int month1 = c1.get(Calendar.MONTH); + int month2 = c2.get(Calendar.MONTH); + int day1 = c1.get(Calendar.DAY_OF_MONTH); + int day2 = c2.get(Calendar.DAY_OF_MONTH); + // 获取年的差值 + int yearInterval = year1 - year2; + // 如果 d1的 月-日 小于 d2的 月-日 那么 yearInterval-- 这样就得到了相差的年数 + if (month1 < month2 || month1 == month2 && day1 < day2) { + yearInterval--; + } + // 获取月数差值 + int monthInterval = (month1 + 12) - month2; + if (day1 < day2) { + monthInterval--; + } + monthInterval %= 12; + int monthsDiff = Math.abs(yearInterval * 12 + monthInterval); + return monthsDiff; + } + + + /** + * 增加 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()); + } + + public static LocalDate toLocalDate(Date date) + { + LocalDate localDate = date.toInstant().atZone(ZoneId.systemDefault()).toLocalDate(); + return localDate; + } + + public static LocalDateTime toLocalDateTime(Date date) + { + LocalDateTime localDateTime = date.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime(); + return localDateTime; + } + + public static boolean isSameDay(final Date d1,final Date d2){ + String s1 = dateTime(d1); + String s2 = dateTime(d2); + + if (s1 == null || s2 == null){ + return false; + } + + if (s1.equals(s2)){ + return true; + } + return false; + } + + public static boolean isSameDate(final Date d1,final Date d2){ + + String s1 = parseDateToStr(YYYY_MM_DD_HH_MM_SS,d1); + String s2 = parseDateToStr(YYYY_MM_DD_HH_MM_SS,d2); + + if (s1 == null || s2 == null){ + return false; + } + + if (s1.equals(s2)){ + return true; + } + return false; + } + + public static Date UTCToCNDate(Date date) + { + SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + // 设置时区为北京 + simpleDateFormat.setTimeZone(TimeZone.getTimeZone("Asia/Shanghai")); + + String format = simpleDateFormat.format(date); + + return DateUtils.parseDate(format); + } + +} diff --git a/src/main/java/com/jxy/windminer/common/utils/ExceptionUtil.java b/src/main/java/com/jxy/windminer/common/utils/ExceptionUtil.java new file mode 100644 index 0000000..326af4f --- /dev/null +++ b/src/main/java/com/jxy/windminer/common/utils/ExceptionUtil.java @@ -0,0 +1,40 @@ +package com.jxy.windminer.common.utils; + +import org.apache.commons.lang3.exception.ExceptionUtils; + +import java.io.PrintWriter; +import java.io.StringWriter; + +/** + * @Description 错误信息处理类 + * @Date 2022/5/10 14:06 + * @Author 杜懿 + */ +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/jxy/windminer/common/utils/FileUploadUtils.java b/src/main/java/com/jxy/windminer/common/utils/FileUploadUtils.java new file mode 100644 index 0000000..a28bfb4 --- /dev/null +++ b/src/main/java/com/jxy/windminer/common/utils/FileUploadUtils.java @@ -0,0 +1,282 @@ +package com.jxy.windminer.common.utils; + +import com.jxy.windminer.common.exception.file.FileNameLengthLimitExceededException; +import com.jxy.windminer.common.exception.file.FileSizeLimitExceededException; +import com.jxy.windminer.common.exception.file.InvalidExtensionException; +import com.jxy.windminer.common.utils.file.MimeTypeUtils; +import com.jxy.windminer.common.utils.uuid.Seq; +import org.apache.commons.io.FilenameUtils; +import org.springframework.web.multipart.MultipartFile; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Paths; +import java.util.Objects; + +/** + * 文件上传工具类 + * + * @author jxy + */ +public class FileUploadUtils +{ + /** + * 默认大小 200M + */ + public static final long DEFAULT_MAX_SIZE = 10 * 1024 * 1024; + + /** + * 默认的文件名最大长度 100 + */ + public static final int DEFAULT_FILE_NAME_LENGTH = 100; + + /** + * 系统工具大小 200M + */ + public static final long TOOLS_MAX_SIZE = 200 * 1024 * 1024; + + /** + * 系统工具的文件名最大长度 100 + */ + public static final int TOOLS_NAME_LENGTH = 100; + + /** + * 根据文件路径上传 + * + * @param baseDir 相对应用的基目录 + * @param file 上传的文件 + * @return 文件名称 + * @throws IOException + */ + public static final String upload(String baseDir, MultipartFile file) throws IOException + { + try + { + return upload(baseDir, file, MimeTypeUtils.DEFAULT_ALLOWED_EXTENSION); + } + catch (Exception e) + { + throw new IOException(e.getMessage(), e); + } + } + + /** + * 文件上传 + * + * @param baseDir 相对应用的基目录 + * @param file 上传的文件 + * @param allowedExtension 上传文件类型 + * @return 返回上传成功的文件名 + * @throws FileSizeLimitExceededException 如果超出最大大小 + * @throws FileNameLengthLimitExceededException 文件名太长 + * @throws IOException 比如读写文件出错时 + * @throws InvalidExtensionException 文件校验异常 + */ + public static final String upload(String baseDir, MultipartFile file, String[] allowedExtension) + throws FileSizeLimitExceededException, IOException, FileNameLengthLimitExceededException, + InvalidExtensionException + { + int fileNamelength = Objects.requireNonNull(file.getOriginalFilename()).length(); + if (fileNamelength > FileUploadUtils.DEFAULT_FILE_NAME_LENGTH) + { + throw new FileNameLengthLimitExceededException(FileUploadUtils.DEFAULT_FILE_NAME_LENGTH); + } + + assertAllowed(file, allowedExtension); + + String fileName = extractFilename(file); + + String absPath = getAbsoluteFile(baseDir, fileName).getAbsolutePath(); + file.transferTo(Paths.get(absPath)); + return getPathFileName(fileName); + } + + /** + * 系统工具文件上传 + * + * @param baseDir 相对应用的基目录 + * @param file 上传的文件 + * @param allowedExtension 上传文件类型 + * @return 返回上传成功的文件名 + * @throws FileSizeLimitExceededException 如果超出最大大小 + * @throws FileNameLengthLimitExceededException 文件名太长 + * @throws IOException 比如读写文件出错时 + * @throws InvalidExtensionException 文件校验异常 + */ + public static final String toolsUpload(String baseDir, MultipartFile file, String fileName, String[] allowedExtension) + throws FileSizeLimitExceededException, IOException, FileNameLengthLimitExceededException, + InvalidExtensionException + { + int fileNamelength = Objects.requireNonNull(file.getOriginalFilename()).length(); + if (fileNamelength > FileUploadUtils.TOOLS_NAME_LENGTH) + { + throw new FileNameLengthLimitExceededException(FileUploadUtils.TOOLS_NAME_LENGTH); + } + System.out.println("toolsAssertAllowed调用"); + toolsAssertAllowed(file, allowedExtension); + System.out.println("toolsAssertAllowed调用结束"); + String absPath = getAbsoluteFile(baseDir, fileName).getAbsolutePath(); + System.out.println("absPath|"+absPath); + file.transferTo(Paths.get(absPath)); + return getPathFileName(fileName); + } + + /** + * 编码文件名 + */ + public static final String extractFilename(MultipartFile file) + { + return StringUtils.format("{}/{}_{}.{}", DateUtils.datePath(), + FilenameUtils.getBaseName(file.getOriginalFilename()), Seq.getId(Seq.uploadSeqType), getExtension(file)); + } + + private static final File getAbsoluteFile(String uploadDir, String fileName) throws IOException + { + File desc = new File(uploadDir + File.separator + fileName); + + if (!desc.exists()) + { + if (!desc.getParentFile().exists()) + { + desc.getParentFile().mkdirs(); + } + } + return desc.isAbsolute() ? desc : desc.getAbsoluteFile(); + } + + private static final String getPathFileName(String fileName) throws IOException + { + String pathFileName = "/" + fileName; + return pathFileName; + } + + /** + * 文件大小校验 + * + * @param file 上传的文件 + * @throws FileSizeLimitExceededException 如果超出最大大小 + * @throws InvalidExtensionException 文件校验异常 + */ + public static final void assertAllowed(MultipartFile file, String[] allowedExtension) + throws FileSizeLimitExceededException, InvalidExtensionException + { + long size = file.getSize(); + if (size > DEFAULT_MAX_SIZE) + { + throw new FileSizeLimitExceededException(DEFAULT_MAX_SIZE / 1024 / 1024); + } + + String fileName = file.getOriginalFilename(); + String extension = getExtension(file); + if (allowedExtension != null && !isAllowedExtension(extension, allowedExtension)) + { + if (allowedExtension == MimeTypeUtils.IMAGE_EXTENSION) + { + throw new InvalidExtensionException.InvalidImageExtensionException(allowedExtension, extension, + fileName); + } + else if (allowedExtension == MimeTypeUtils.FLASH_EXTENSION) + { + throw new InvalidExtensionException.InvalidFlashExtensionException(allowedExtension, extension, + fileName); + } + else if (allowedExtension == MimeTypeUtils.MEDIA_EXTENSION) + { + throw new InvalidExtensionException.InvalidMediaExtensionException(allowedExtension, extension, + fileName); + } + else if (allowedExtension == MimeTypeUtils.VIDEO_EXTENSION) + { + throw new InvalidExtensionException.InvalidVideoExtensionException(allowedExtension, extension, + fileName); + } + else + { + throw new InvalidExtensionException(allowedExtension, extension, fileName); + } + } + } + + /** + * 文件大小校验 + * + * @param file 上传的文件 + * @throws FileSizeLimitExceededException 如果超出最大大小 + * @throws InvalidExtensionException 文件校验异常 + */ + public static final void toolsAssertAllowed(MultipartFile file, String[] allowedExtension) + throws FileSizeLimitExceededException, InvalidExtensionException + { + long size = file.getSize(); + if (size > TOOLS_MAX_SIZE) + { + throw new FileSizeLimitExceededException(TOOLS_MAX_SIZE / 1024 / 1024); + } + + String fileName = file.getOriginalFilename(); + String extension = getExtension(file); + if (allowedExtension != null && !isAllowedExtension(extension, allowedExtension)) + { + if (allowedExtension == MimeTypeUtils.IMAGE_EXTENSION) + { + throw new InvalidExtensionException.InvalidImageExtensionException(allowedExtension, extension, + fileName); + } + else if (allowedExtension == MimeTypeUtils.FLASH_EXTENSION) + { + throw new InvalidExtensionException.InvalidFlashExtensionException(allowedExtension, extension, + fileName); + } + else if (allowedExtension == MimeTypeUtils.MEDIA_EXTENSION) + { + throw new InvalidExtensionException.InvalidMediaExtensionException(allowedExtension, extension, + fileName); + } + else if (allowedExtension == MimeTypeUtils.VIDEO_EXTENSION) + { + throw new InvalidExtensionException.InvalidVideoExtensionException(allowedExtension, extension, + fileName); + } + else + { + throw new InvalidExtensionException(allowedExtension, extension, fileName); + } + } + } + + /** + * 判断MIME类型是否是允许的MIME类型 + * + * @param extension 上传文件类型 + * @param allowedExtension 允许上传文件类型 + * @return true/false + */ + public static final boolean isAllowedExtension(String extension, String[] allowedExtension) + { + for (String str : allowedExtension) + { + if (str.equalsIgnoreCase(extension)) + { + return true; + } + } + return false; + } + + /** + * 获取文件名的后缀 + * + * @param file 表单文件 + * @return 后缀名 + */ + public static final String getExtension(MultipartFile file) + { + String extension = FilenameUtils.getExtension(file.getOriginalFilename()); + if (StringUtils.isEmpty(extension)) + { + extension = MimeTypeUtils.getExtension(Objects.requireNonNull(file.getContentType())); + } + return extension; + } +} + diff --git a/src/main/java/com/jxy/windminer/common/utils/JwtUtils.java b/src/main/java/com/jxy/windminer/common/utils/JwtUtils.java new file mode 100644 index 0000000..d2d2221 --- /dev/null +++ b/src/main/java/com/jxy/windminer/common/utils/JwtUtils.java @@ -0,0 +1,146 @@ +package com.jxy.windminer.common.utils; + +import com.jxy.windminer.common.constant.SecurityConstants; +import com.jxy.windminer.common.constant.TokenConstants; +import com.jxy.windminer.common.text.Convert; +import io.jsonwebtoken.Claims; +import io.jsonwebtoken.Jwts; +import io.jsonwebtoken.SignatureAlgorithm; + +import java.util.Map; + +/** + * @Description Jwt工具类 + * @Date 2022/5/10 16:38 + * @Author 杜懿 + */ +public class JwtUtils +{ + public static String secret = TokenConstants.SECRET; + + /** + * 从数据声明生成令牌 + * + * @param claims 数据声明 + * @return 令牌 + */ + public static String createToken(Map claims) + { + String token = Jwts.builder().setClaims(claims).signWith(SignatureAlgorithm.HS512, secret).compact(); + return token; + } + + /** + * 从令牌中获取数据声明 + * + * @param token 令牌 + * @return 数据声明 + */ + public static Claims parseToken(String token) + { + return Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody(); + } + + /** + * 根据令牌获取用户标识 + * + * @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); + } + + /** + * 根据令牌获取用户邮箱 + * + * @param token 令牌 + * @return 用户ID + */ + public static String getEmail(String token) + { + Claims claims = parseToken(token); + return getValue(claims, SecurityConstants.DETAILS_EMAIL); + } + + /** + * 根据令牌获取用户标识 + * + * @param claims 身份信息 + * @return 用户ID + */ + public static String getEmail(Claims claims) + { + return getValue(claims, SecurityConstants.DETAILS_EMAIL); + } + /** + * 根据身份信息获取用户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/jxy/windminer/common/utils/MysqlBackUpUtils.java b/src/main/java/com/jxy/windminer/common/utils/MysqlBackUpUtils.java new file mode 100644 index 0000000..b4ff87a --- /dev/null +++ b/src/main/java/com/jxy/windminer/common/utils/MysqlBackUpUtils.java @@ -0,0 +1,75 @@ +package com.jxy.windminer.common.utils; + +import com.jxy.windminer.common.utils.DateUtils; +import org.springframework.beans.factory.annotation.Value; + +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; + +/** + * @Description TODO + * @Date 2022/11/18 14:26 + * @Author 杜懿 + */ +public class MysqlBackUpUtils { + private static Lock lock = new ReentrantLock(); + + @Value("${spring.datasource.username}") + private static String username; + + @Value("${spring.datasource.password}") + private static String pwd; + + @Value("${my.env}") + private static String env; + + + //根据交易链 币种 地址 验证支付信息 + static public void addrBackUp(){ + //验证功能脚本地址 + String command="mysqldump -h127.0.0.1 -u "+username+" -p"+pwd+" -P3306 miner > /home/ubuntu/backup/addr_backup/mysql" + DateUtils.dateTimeNow()+".sql" ; + //if("test".equals(env)){ + // command="mysqldump -h127.0.0.1 -u "+username+" -p"+pwd+" -P3306 test_miner > /home/ubuntu/testBK/addr_backup/mysql" +DateUtils.dateTimeNow()+".sql" ; + //} + try { + lock.lock(); + Process process = Runtime.getRuntime().exec(command); + + process.waitFor(); + System.out.println("Database backup completed."); + + } + catch (Exception e){ + System.out.println("地址数据库数据备份异常"); + e.printStackTrace(); + } + finally { + lock.unlock(); + } + + } + + static public void userBackUp(){ + //验证功能脚本地址 + String command="mysqldump -h127.0.0.1 -u "+username+" -p"+pwd+" -P3306 miner > /home/ubuntu/backup/user_backup/mysql" +DateUtils.dateTimeNow()+".sql" ; + //if("test".equals(env)){ + // command="mysqldump -h127.0.0.1 -u "+username+" -p"+pwd+" -P3306 test_miner > /home/ubuntu/testBK/user_backup/mysql" +DateUtils.dateTimeNow()+".sql" ; + //} + try { + lock.lock(); + System.out.println("用户备份执行指令:|"+command+"|"); + Process process = Runtime.getRuntime().exec(command); + + process.waitFor(); + System.out.println("Database backup completed."); + + } + catch (Exception e){ + System.out.println("用户数据库数据备份异常"); + e.printStackTrace(); + } + finally { + lock.unlock(); + } + } +} diff --git a/src/main/java/com/jxy/windminer/common/utils/PageUtils.java b/src/main/java/com/jxy/windminer/common/utils/PageUtils.java new file mode 100644 index 0000000..ef29f57 --- /dev/null +++ b/src/main/java/com/jxy/windminer/common/utils/PageUtils.java @@ -0,0 +1,36 @@ +package com.jxy.windminer.common.utils; + +import com.github.pagehelper.PageHelper; +import com.jxy.windminer.common.utils.sql.SqlUtil; +import com.jxy.windminer.common.web.page.Page; +import com.jxy.windminer.common.web.page.TableSupport; + +/** + * @Description 分页工具类 + * @Date 2022/5/10 14:37 + * @Author 杜懿 + */ +public class PageUtils extends PageHelper +{ + /** + * 设置请求分页数据 + */ + public static void startPage() + { + Page page = TableSupport.buildPageRequest(); + Integer pageNum = page.getPageNum(); + Integer pageSize = page.getPageSize(); + String orderBy = SqlUtil.escapeOrderBySql(page.getOrderBy()); + Boolean reasonable = page.getReasonable(); + PageHelper.startPage(pageNum, pageSize, orderBy).setReasonable(reasonable); + } + + /** + * 清理分页的线程变量 + */ + public static void clearPage() + { + PageHelper.clearPage(); + } +} + diff --git a/src/main/java/com/jxy/windminer/common/utils/PwdUtils.java b/src/main/java/com/jxy/windminer/common/utils/PwdUtils.java new file mode 100644 index 0000000..1e67800 --- /dev/null +++ b/src/main/java/com/jxy/windminer/common/utils/PwdUtils.java @@ -0,0 +1,110 @@ +package com.jxy.windminer.common.utils; + +import java.util.*; + +/** + * @Description TODO + * @Date 2023/10/25 16:55 + * @Author 杜懿 + */ +public class PwdUtils { + + // 特殊字符 + private static final String SPECIAL_CHARS = "!@#$%&*_="; + // 去掉I、L、O、Q易混淆字符 + private static final String UPPER_WORD_CHARS = "ABCDEFGHJKMNPRSTUVWXYZ"; + // 去掉i、l、o、q易混淆字符 + private static final String LOWER_WORD_CHARS = "abcdefghjkmnprstuvwxyz"; + // 去掉1、0易混淆字符 + private static final String NUMBER_CHARS = "23456789"; + + + /** + * 生成固定长度的复杂密码 + * @param length + * @return + */ + public static String randomPassword(int length){ + return randomPassword(length,true,true,true,true); + } + + /** + * @param length 生成密码的长度 建议至少6位 + * @param isLowerWord 是否包含小写字母 + * @param isUpperWord 是否包含大写字母 + * @param isSpecialChar 是否包含特殊字符 + * @param isNum 是否包含数字 + * @return String 随机密码 + * @description: 生成随机密码的工具方法 + */ + public static String randomPassword(int length, Boolean isLowerWord, Boolean isUpperWord, Boolean isSpecialChar, Boolean isNum) { + Random rnd = new Random(); + // 先取必填项1个 + Map map = new HashMap<>(); + if (isLowerWord) { + map.put(1, LOWER_WORD_CHARS); + } + if (isUpperWord) { + map.put(2, UPPER_WORD_CHARS); + } + if (isSpecialChar) { + map.put(3, SPECIAL_CHARS); + } + if (isNum) { + map.put(4, NUMBER_CHARS); + } + StringBuilder sb = new StringBuilder(); + for (Map.Entry entry : map.entrySet()) { + String value = entry.getValue(); + sb.append(value.charAt(rnd.nextInt(value.length()))); + } + char[] chars = new char[length - sb.length()]; + for (int i = 0; i < length - sb.length(); i++) { + chars[i] = nextChar(rnd, isLowerWord, isUpperWord, isSpecialChar, isNum); + } + StringBuilder resultSb = sb.append(new String(chars)); + // 对结果数据乱序处理(前面固定放到前几个了,故需乱序) + String[] split = resultSb.toString().split(""); + List strings = Arrays.asList(split); + Collections.shuffle(strings); + StringBuilder stringBuilder = new StringBuilder(strings.size()); + strings.forEach(stringBuilder::append); + return stringBuilder.toString(); + } + + + private static char nextChar(Random rnd, Boolean isLowerWord, Boolean isUpperWord, Boolean isSpecialChar, Boolean isNum) { + List list = new ArrayList<>(); + if (isLowerWord) { + list.add(1); + } + if (isUpperWord) { + list.add(2); + } + if (isSpecialChar) { + list.add(3); + } + if (isNum) { + list.add(4); + } + if (list.size() == 0) { + // 默认数字密码 + list.add(4); + } + + // 随机list索引 + int index = rnd.nextInt(list.size()); + Integer integer = list.get(index); + switch (integer) { + case 1: + return LOWER_WORD_CHARS.charAt(rnd.nextInt(LOWER_WORD_CHARS.length())); + case 2: + return UPPER_WORD_CHARS.charAt(rnd.nextInt(UPPER_WORD_CHARS.length())); + case 3: + return SPECIAL_CHARS.charAt(rnd.nextInt(SPECIAL_CHARS.length())); + default: + return NUMBER_CHARS.charAt(rnd.nextInt(NUMBER_CHARS.length())); + } + } + +} diff --git a/src/main/java/com/jxy/windminer/common/utils/ServletUtils.java b/src/main/java/com/jxy/windminer/common/utils/ServletUtils.java new file mode 100644 index 0000000..3631930 --- /dev/null +++ b/src/main/java/com/jxy/windminer/common/utils/ServletUtils.java @@ -0,0 +1,306 @@ +package com.jxy.windminer.common.utils; + + +import com.alibaba.fastjson.JSONObject; +import com.jxy.windminer.common.Result.R; +import com.jxy.windminer.common.constant.Constants; +import com.jxy.windminer.common.text.Convert; +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.web.context.request.RequestAttributes; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; +import reactor.core.publisher.Mono; +//import reactor.core.publisher.Mono; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.net.URLDecoder; +import java.net.URLEncoder; +import java.util.Enumeration; +import java.util.LinkedHashMap; +import java.util.Map; + +/** + * @Description 客户端工具类 + * @Date 2022/5/10 14:45 + * @Author 杜懿 + */ +public class ServletUtils +{ + /** + * 获取String参数 + */ + public static String getParameter(String name) + { + System.out.println(getRequest().toString()); + 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); + } + + /** + * 获取request + */ + public static HttpServletRequest getRequest() + { + try + { + return getRequestAttributes().getRequest(); + } + catch (Exception e) + { + return null; + } + } + + /** + * 获取response + */ + public static HttpServletResponse getResponse() + { + try + { + return getRequestAttributes().getResponse(); + } + catch (Exception e) + { + return null; + } + } + + /** + * 获取session + */ + public static HttpSession getSession() + { + return 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 LinkedHashMap<>(); + 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) + { + try + { + return URLDecoder.decode(str, Constants.UTF8); + } + catch (UnsupportedEncodingException e) + { + return StringUtils.EMPTY; + } + } + + /** + * 设置webflux模型响应 + * + * @param response ServerHttpResponse + * @param value 响应内容 + * @return Mono + */ + public static Mono webFluxResponseWriter(ServerHttpResponse response, Object value) + { + return webFluxResponseWriter(response, HttpStatus.OK, value, R.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); + R result = R.fail(code, value.toString()); + DataBuffer dataBuffer = response.bufferFactory().wrap(JSONObject.toJSONString(result).getBytes()); + return response.writeWith(Mono.just(dataBuffer)); + } +} diff --git a/src/main/java/com/jxy/windminer/common/utils/SpringUtils.java b/src/main/java/com/jxy/windminer/common/utils/SpringUtils.java new file mode 100644 index 0000000..430fedd --- /dev/null +++ b/src/main/java/com/jxy/windminer/common/utils/SpringUtils.java @@ -0,0 +1,114 @@ +package com.jxy.windminer.common.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; + +/** + * @Description Spring工具类 + * 方便在非spring管理环境中获取bean + * @Date 2022/5/9 17:27 + * @Author 杜懿 + */ +@Component +public final class SpringUtils implements BeanFactoryPostProcessor +{ + /** Spring应用Context环境 */ + private static ConfigurableListableBeanFactory beanFactory; + + @Override + public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException + { + SpringUtils.beanFactory = beanFactory; + } + + /** + * 获取对象 + * + * @param name + * @return Object + * @throws BeansException + * + */ + @SuppressWarnings("unchecked") + public static T getBean(String name) throws BeansException + { + return (T) beanFactory.getBean(name); + } + + /** + * 获取类型为requiredType的对象 + * + * @param clz + * @return + * @throws BeansException + * + */ + public static T getBean(Class clz) throws BeansException + { + T result = (T) beanFactory.getBean(clz); + return result; + } + + /** + *判断BeanFactory中是否包含有所给名字的bean + * @param name + * @return boolean + */ + public static boolean containsBean(String name) + { + return beanFactory.containsBean(name); + } + + /** + * 判断以给定名字注册的bean定义是一个singleton还是一个prototype。 + * + * @param name + * @return boolean + * @throws NoSuchBeanDefinitionException + * + */ + public static boolean isSingleton(String name) throws NoSuchBeanDefinitionException + { + return beanFactory.isSingleton(name); + } + + /** + * @param name + * @return Class 注册对象的类型 + * @throws NoSuchBeanDefinitionException + * + */ + public static Class getType(String name) throws NoSuchBeanDefinitionException + { + return beanFactory.getType(name); + } + + /** + * 如果给定的bean名字在bean定义中有别名,则返回这些别名 + * + * @param name + * @return + * @throws 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(); + } +} diff --git a/src/main/java/com/jxy/windminer/common/utils/StringUtils.java b/src/main/java/com/jxy/windminer/common/utils/StringUtils.java new file mode 100644 index 0000000..9d05b06 --- /dev/null +++ b/src/main/java/com/jxy/windminer/common/utils/StringUtils.java @@ -0,0 +1,548 @@ +package com.jxy.windminer.common.utils; + +import com.jxy.windminer.common.constant.Constants; +import com.jxy.windminer.common.text.StrFormatter; +import org.springframework.util.AntPathMatcher; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + +/** + * @Description 字符串工具类 + * @Date 2022/5/9 14:41 + * @Author 杜懿 + */ +public class StringUtils extends org.apache.commons.lang3.StringUtils +{ + /** 空字符串 */ + private static final String NULLSTR = ""; + + public static final int INDEX_NOT_FOUND = -1; + + /** 下划线 */ + 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; + } + + /** + * 格式化文本, {} 表示占位符
+ * + * @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); + } + + /** + * 驼峰转下划线命名 + */ + public static String toUnderScoreCase(String str) + { + if (str == null) + { + return null; + } + StringBuilder sb = new StringBuilder(); + // 前置字符是否大写 + boolean preCharIsUpperCase = true; + // 当前字符是否大写 + boolean curreCharIsUpperCase = true; + // 下一字符是否大写 + boolean nexteCharIsUpperCase = true; + for (int i = 0; i < str.length(); i++) + { + char c = str.charAt(i); + if (i > 0) + { + preCharIsUpperCase = Character.isUpperCase(str.charAt(i - 1)); + } + else + { + preCharIsUpperCase = false; + } + + curreCharIsUpperCase = Character.isUpperCase(c); + + if (i < (str.length() - 1)) + { + nexteCharIsUpperCase = Character.isUpperCase(str.charAt(i + 1)); + } + + if (preCharIsUpperCase && curreCharIsUpperCase && !nexteCharIsUpperCase) + { + sb.append(SEPARATOR); + } + else if ((i != 0 && !preCharIsUpperCase) && curreCharIsUpperCase) + { + sb.append(SEPARATOR); + } + sb.append(Character.toLowerCase(c)); + } + + return sb.toString(); + } + + /** + * 是否包含字符串 + * + * @param str 验证字符串 + * @param strs 字符串组 + * @return 包含返回true + */ + public static boolean inStringIgnoreCase(String str, String... strs) + { + if (str != null && strs != null) + { + for (String s : strs) + { + if (str.equalsIgnoreCase(trim(s))) + { + return true; + } + } + } + return false; + } + + /** + * 将下划线大写方式命名的字符串转换为驼峰式。如果转换前的下划线大写方式命名的字符串为空,则返回空字符串。 例如:HELLO_WORLD->HelloWorld + * + * @param name 转换前的下划线大写方式命名的字符串 + * @return 转换后的驼峰式命名的字符串 + */ + public static String convertToCamelCase(String name) + { + StringBuilder result = new StringBuilder(); + // 快速检查 + if (name == null || name.isEmpty()) + { + // 没必要转换 + return ""; + } + else if (!name.contains("_")) + { + // 不含下划线,仅将首字母大写 + return name.substring(0, 1).toUpperCase() + name.substring(1); + } + // 用下划线将原始字符串分割 + String[] camels = name.split("_"); + for (String camel : camels) + { + // 跳过原始字符串中开头、结尾的下换线或双重下划线 + if (camel.isEmpty()) + { + continue; + } + // 首字母大写 + result.append(camel.substring(0, 1).toUpperCase()); + result.append(camel.substring(1).toLowerCase()); + } + return result.toString(); + } + + /** + * 驼峰式命名法 例如:user_name->userName + */ + public static String toCamelCase(String s) + { + if (s == null) + { + return null; + } + s = s.toLowerCase(); + StringBuilder sb = new StringBuilder(s.length()); + boolean upperCase = false; + for (int i = 0; i < s.length(); i++) + { + char c = s.charAt(i); + + if (c == SEPARATOR) + { + upperCase = true; + } + else if (upperCase) + { + sb.append(Character.toUpperCase(c)); + upperCase = false; + } + else + { + sb.append(c); + } + } + return sb.toString(); + } + + /** + * 查找指定字符串是否匹配指定字符串列表中的任意一个字符串 + * + * @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(); + } + public static String substringAfter(final String str, final String separator) { + if (isEmpty(str)) { + return str; + } + if (separator == null) { + return NULLSTR; + } + final int pos = str.indexOf(separator); + if (pos == INDEX_NOT_FOUND) { + return NULLSTR; + } + return str.substring(pos + separator.length()); + } + + public static String clean(String str){ + if (isEmpty(str)) { + return str; + } + + return str.replaceAll("\\r\\n|\\n|\\\\n| |\\s",""); + } +} diff --git a/src/main/java/com/jxy/windminer/common/utils/WalletUils.java b/src/main/java/com/jxy/windminer/common/utils/WalletUils.java new file mode 100644 index 0000000..9634bae --- /dev/null +++ b/src/main/java/com/jxy/windminer/common/utils/WalletUils.java @@ -0,0 +1,112 @@ +package com.jxy.windminer.common.utils; + +import java.io.BufferedReader; +import java.io.InputStreamReader; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; + +/** + * @Description TODO + * @Date 2022/11/18 14:26 + * @Author 杜懿 + */ +public class WalletUils { + private static Lock lock = new ReentrantLock(); + //根据交易链 币种 地址 验证支付信息 + static public String createAddress(String chain){ + //验证功能脚本地址 + String path="/home/ubuntu/script/wallet/main.js"; + long t1 = System.currentTimeMillis(); + + //String chain = "ETH"; + // + //String num = Convert.toStr(10); + String param =""; + switch (chain.toUpperCase()){ + case "ETH": param = "ETH";break; + case "TRX": param = "TRON";break; + case "TRON": param = "TRON";break; + case "POLYGON": param = "ETH";break; + case "OPTIMISM": param = "ETH";break; + case "ARBITRUM": param = "ETH";break; + } + if(StringUtils.isBlank(param)){ + return ""; + } + + BufferedReader reader = null; + BufferedReader errorReader = null; + try { + lock.lock(); + String num ="200"; + ProcessBuilder processBuilder = new ProcessBuilder("node",path,param,num); + + Process process = processBuilder.start(); + + //获取进程输出流输入参数 + //BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(process.getOutputStream())); + //writer.write("ETH 10"); + // + //writer.newLine(); + //writer.flush(); + + //获取进程输入流读取返回值 + reader = new BufferedReader(new InputStreamReader(process.getInputStream())); + + String line; + + StringBuilder output = new StringBuilder(); + + while ((line = reader.readLine()) != null){ + output.append(line).append("\n"); + } + + System.out.println("输出内容:|"+output.toString()+"|"); + + //获取进程错误流流读取返回值 + errorReader = new BufferedReader(new InputStreamReader(process.getErrorStream())); + + String errorLine; + + StringBuilder errorOutput = new StringBuilder(); + + while ((errorLine = errorReader.readLine()) != null){ + errorOutput.append(errorLine).append("\n"); + } + + reader.close(); + errorReader.close(); + + System.out.println("错误信息:|"+errorOutput.toString()+"|"); + + if(StringUtils.isNotBlank(output) && StringUtils.isBlank(errorOutput)){ + return output.toString(); + } + return ""; + } + catch (Exception e){ + return ""; + } + finally { + try { + if (reader != null) { + reader.close(); + } + if (errorReader != null) { + errorReader.close(); + } + long t2 = System.currentTimeMillis(); + if(t2-t1 < 200){ + // 资源1s最多请求5次 所以每个资源耗时需要200ms + //当走到最后整个程序耗时没到200则强制休眠 休眠时间为200减去执行程序已消耗的时间 即200-(t2-t1) + long time = 200-(t2-t1); + Thread.sleep(time); + } + }catch (Exception e){ + e.printStackTrace(); + } + lock.unlock(); + } + + } +} diff --git a/src/main/java/com/jxy/windminer/common/utils/WalletVerifyUtils.java b/src/main/java/com/jxy/windminer/common/utils/WalletVerifyUtils.java new file mode 100644 index 0000000..414ce95 --- /dev/null +++ b/src/main/java/com/jxy/windminer/common/utils/WalletVerifyUtils.java @@ -0,0 +1,111 @@ +package com.jxy.windminer.common.utils; + +import java.io.BufferedReader; +import java.io.InputStreamReader; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; + +/** + * @Description TODO + * @Date 2022/11/18 14:26 + * @Author 杜懿 + */ +public class WalletVerifyUtils { + private static Lock lock = new ReentrantLock(); + //根据交易链 币种 地址 验证支付信息 + static public String verifyAddress(String chain,String coin,String address){ + //验证功能脚本地址 + String path="/home/ubuntu/script/wallet-verify/main.js"; + long t1 = System.currentTimeMillis(); + + //String chain = "ETH"; + // + //String num = Convert.toStr(10); + String param =""; + switch (chain.toUpperCase()){ + case "ETH": param = "ERC20_analysis_by_receive_address";break; + case "TRX": param = "TRC20_analysis_by_receive_address";break; + case "TRON": param = "TRC20_analysis_by_receive_address";break; + case "POLYGON": param = "Polygon_analysis_by_receive_address";break; + case "OPTIMISM": param = "OP_analysis_by_receive_address";break; + case "ARBITRUM": param = "Arbitrum_analysis_by_receive_address";break; + } + if(StringUtils.isBlank(param)){ + return ""; + } + + BufferedReader reader = null; + BufferedReader errorReader = null; + try { + lock.lock(); + ProcessBuilder processBuilder = new ProcessBuilder("node",path,param,coin,address); + + Process process = processBuilder.start(); + + //获取进程输出流输入参数 + //BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(process.getOutputStream())); + //writer.write("ETH 10"); + // + //writer.newLine(); + //writer.flush(); + + //获取进程输入流读取返回值 + reader = new BufferedReader(new InputStreamReader(process.getInputStream())); + + String line; + + StringBuilder output = new StringBuilder(); + + while ((line = reader.readLine()) != null){ + output.append(line).append("\n"); + } + + System.out.println("输出内容:|"+output.toString()+"|"); + + //获取进程错误流流读取返回值 + errorReader = new BufferedReader(new InputStreamReader(process.getErrorStream())); + + String errorLine; + + StringBuilder errorOutput = new StringBuilder(); + + while ((errorLine = errorReader.readLine()) != null){ + errorOutput.append(errorLine).append("\n"); + } + + reader.close(); + errorReader.close(); + + System.out.println("错误信息:|"+errorOutput.toString()+"|"); + + if(StringUtils.isNotBlank(output) && StringUtils.isBlank(errorOutput)){ + return output.toString(); + } + return ""; + } + catch (Exception e){ + return ""; + } + finally { + try { + if (reader != null) { + reader.close(); + } + if (errorReader != null) { + errorReader.close(); + } + long t2 = System.currentTimeMillis(); + if(t2-t1 < 200){ + // 资源1s最多请求5次 所以每个资源耗时需要200ms + //当走到最后整个程序耗时没到200则强制休眠 休眠时间为200减去执行程序已消耗的时间 即200-(t2-t1) + long time = 200-(t2-t1); + Thread.sleep(time); + } + }catch (Exception e){ + e.printStackTrace(); + } + lock.unlock(); + } + + } +} diff --git a/src/main/java/com/jxy/windminer/common/utils/bean/BeanUtils.java b/src/main/java/com/jxy/windminer/common/utils/bean/BeanUtils.java new file mode 100644 index 0000000..e2b3b77 --- /dev/null +++ b/src/main/java/com/jxy/windminer/common/utils/bean/BeanUtils.java @@ -0,0 +1,112 @@ +package com.jxy.windminer.common.utils.bean; + +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * @Description Bean工具类 + * @Date 2022/5/9 10:16 + * @Author 杜懿 + */ + +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/jxy/windminer/common/utils/bean/BeanValidators.java b/src/main/java/com/jxy/windminer/common/utils/bean/BeanValidators.java new file mode 100644 index 0000000..3fa0d31 --- /dev/null +++ b/src/main/java/com/jxy/windminer/common/utils/bean/BeanValidators.java @@ -0,0 +1,25 @@ +package com.jxy.windminer.common.utils.bean; + +import javax.validation.ConstraintViolation; +import javax.validation.ConstraintViolationException; +import javax.validation.Validator; +import java.util.Set; + +/** + * @Description bean对象属性验证 + * @Date 2022/5/9 10:43 + * @Author 杜懿 + */ +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/jxy/windminer/common/utils/file/FileTypeUtils.java b/src/main/java/com/jxy/windminer/common/utils/file/FileTypeUtils.java new file mode 100644 index 0000000..af916bb --- /dev/null +++ b/src/main/java/com/jxy/windminer/common/utils/file/FileTypeUtils.java @@ -0,0 +1,77 @@ +package com.jxy.windminer.common.utils.file; + +import org.apache.commons.lang3.StringUtils; + +import java.io.File; + +/** + * @Description 文件类型工具类 + * @Date 2022/5/9 10:49 + * @Author 杜懿 + */ +public class FileTypeUtils { + /** + * 根据文件获取文件类型 + *

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

+ * 例如: aa.txt, 返回: txt + * + * @param fileName 文件名 + * @return 后缀(不含".") + */ + public static String getFileType(String fileName) + { + int separatorIndex = fileName.lastIndexOf("."); + if (separatorIndex < 0) + { + return ""; + } + return fileName.substring(separatorIndex + 1).toLowerCase(); + } + + /** + * 获取文件类型 + * + * @param photoByte 文件字节码 + * @return 后缀(不含".") + */ + public static String getFileExtendName(byte[] photoByte) + { + String strFileExtendName = "JPG"; + if ((photoByte[0] == 71) && (photoByte[1] == 73) && (photoByte[2] == 70) && (photoByte[3] == 56) + && ((photoByte[4] == 55) || (photoByte[4] == 57)) && (photoByte[5] == 97)) + { + strFileExtendName = "GIF"; + } + else if ((photoByte[6] == 74) && (photoByte[7] == 70) && (photoByte[8] == 73) && (photoByte[9] == 70)) + { + strFileExtendName = "JPG"; + } + else if ((photoByte[0] == 66) && (photoByte[1] == 77)) + { + strFileExtendName = "BMP"; + } + else if ((photoByte[1] == 80) && (photoByte[2] == 78) && (photoByte[3] == 71)) + { + strFileExtendName = "PNG"; + } + return strFileExtendName; + } + +} diff --git a/src/main/java/com/jxy/windminer/common/utils/file/FileUtils.java b/src/main/java/com/jxy/windminer/common/utils/file/FileUtils.java new file mode 100644 index 0000000..bb6de72 --- /dev/null +++ b/src/main/java/com/jxy/windminer/common/utils/file/FileUtils.java @@ -0,0 +1,257 @@ +package com.jxy.windminer.common.utils.file; + +import org.apache.commons.lang3.ArrayUtils; +import org.apache.commons.lang3.StringUtils; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.*; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; + +/** + * @Description 文件处理工具类 + * @Date 2022/5/9 11:31 + * @Author 杜懿 + */ +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()) + { + file.delete(); + flag = true; + } + return flag; + } + + /** + * 文件名称验证 + * + * @param filename 文件名称 + * @return true 正常 false 非法 + */ + public static boolean isValidFilename(String filename) + { + return filename.matches(FILENAME_PATTERN); + } + + /** + * 检查文件是否可下载 + * + * @param resource 需要下载的文件 + * @return true 正常 false 非法 + */ + public static boolean checkAllowDownload(String resource) + { + // 禁止目录上跳级别 + if (StringUtils.contains(resource, "..")) + { + return false; + } + + // 检查允许下载的文件规则 + if (ArrayUtils.contains(MimeTypeUtils.DEFAULT_ALLOWED_EXTENSION, FileTypeUtils.getFileType(resource))) + { + return true; + } + + // 不在允许下载的文件规则 + return false; + } + + /** + * 下载文件名重新编码 + * + * @param request 请求对象 + * @param fileName 文件名 + * @return 编码后的文件名 + */ + public static String setFileDownloadHeader(HttpServletRequest request, String fileName) throws UnsupportedEncodingException + { + final String agent = request.getHeader("USER-AGENT"); + String filename = fileName; + if (agent.contains("MSIE")) + { + // IE浏览器 + filename = URLEncoder.encode(filename, "utf-8"); + filename = filename.replace("+", " "); + } + else if (agent.contains("Firefox")) + { + // 火狐浏览器 + filename = new String(fileName.getBytes(), "ISO8859-1"); + } + else if (agent.contains("Chrome")) + { + // google浏览器 + filename = URLEncoder.encode(filename, "utf-8"); + } + else + { + // 其它浏览器 + filename = URLEncoder.encode(filename, "utf-8"); + } + return filename; + } + + /** + * 获取文件名 + * + * @param 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/jxy/windminer/common/utils/file/ImageUtils.java b/src/main/java/com/jxy/windminer/common/utils/file/ImageUtils.java new file mode 100644 index 0000000..a67204d --- /dev/null +++ b/src/main/java/com/jxy/windminer/common/utils/file/ImageUtils.java @@ -0,0 +1,85 @@ +package com.jxy.windminer.common.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; + +/** + * @Description 图片处理工具类 + * @Date 2022/5/9 14:03 + * @Author 杜懿 + */ +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/jxy/windminer/common/utils/file/MimeTypeUtils.java b/src/main/java/com/jxy/windminer/common/utils/file/MimeTypeUtils.java new file mode 100644 index 0000000..e06d5e4 --- /dev/null +++ b/src/main/java/com/jxy/windminer/common/utils/file/MimeTypeUtils.java @@ -0,0 +1,67 @@ +package com.jxy.windminer.common.utils.file; + +/** + * @Description 媒体类型工具类 + * @Date 2022/5/9 14:31 + * @Author 杜懿 + */ +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","aif", "aiff", "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", "7z", + // 视频格式 + "mp4", "avi", "rmvb", + // pdf + "pdf" }; + + public static final String[] DEFAULT_WINDMINER_EXTENSION = { + // 图片 + "jpg", "jpeg", "png", + //音频 + "mp3","aif", "aiff", "wav", "wma", + // 视频格式 + "mp4", "avi", "rmvb"}; + + 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/jxy/windminer/common/utils/html/EscapeUtil.java b/src/main/java/com/jxy/windminer/common/utils/html/EscapeUtil.java new file mode 100644 index 0000000..b8b42a6 --- /dev/null +++ b/src/main/java/com/jxy/windminer/common/utils/html/EscapeUtil.java @@ -0,0 +1,171 @@ +package com.jxy.windminer.common.utils.html; + + +import com.jxy.windminer.common.utils.StringUtils; + +/** + * 转义和反转义工具类 + * + * @author 杜懿 + */ +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 html = "ipt>alert(\"XSS\")ipt>"; + // // String html = "<123"; + // // String html = "123>"; + // + // String escape = EscapeUtil.escape(html); + // 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/jxy/windminer/common/utils/html/HTMLFilter.java b/src/main/java/com/jxy/windminer/common/utils/html/HTMLFilter.java new file mode 100644 index 0000000..9af559f --- /dev/null +++ b/src/main/java/com/jxy/windminer/common/utils/html/HTMLFilter.java @@ -0,0 +1,566 @@ +package com.jxy.windminer.common.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 杜懿 + */ +public final class HTMLFilter +{ + /** + * regex flag union representing /si modifiers in php + **/ + private static final int REGEX_FLAGS_SI = Pattern.CASE_INSENSITIVE | Pattern.DOTALL; + private static final Pattern P_COMMENTS = Pattern.compile("", Pattern.DOTALL); + private static final Pattern P_COMMENT = Pattern.compile("^!--(.*)--$", REGEX_FLAGS_SI); + private static final Pattern P_TAGS = Pattern.compile("<(.*?)>", Pattern.DOTALL); + private static final Pattern P_END_TAG = Pattern.compile("^/([a-z0-9]+)", REGEX_FLAGS_SI); + private static final Pattern P_START_TAG = Pattern.compile("^([a-z0-9]+)(.*?)(/?)$", REGEX_FLAGS_SI); + private static final Pattern P_QUOTED_ATTRIBUTES = Pattern.compile("([a-z0-9]+)=([\"'])(.*?)\\2", REGEX_FLAGS_SI); + private static final Pattern P_UNQUOTED_ATTRIBUTES = Pattern.compile("([a-z0-9]+)(=)([^\"\\s']+)", REGEX_FLAGS_SI); + private static final Pattern P_PROTOCOL = Pattern.compile("^([^:]+):", REGEX_FLAGS_SI); + private static final Pattern P_ENTITY = Pattern.compile("&#(\\d+);?"); + private static final Pattern P_ENTITY_UNICODE = Pattern.compile("&#x([0-9a-f]+);?"); + private static final Pattern P_ENCODE = Pattern.compile("%([0-9a-f]{2});?"); + private static final Pattern P_VALID_ENTITIES = Pattern.compile("&([^&;]*)(?=(;|&|$))"); + private static final Pattern P_VALID_QUOTES = Pattern.compile("(>|^)([^<]+?)(<|$)", Pattern.DOTALL); + private static final Pattern P_END_ARROW = Pattern.compile("^>"); + private static final Pattern P_BODY_TO_END = Pattern.compile("<([^>]*?)(?=<|$)"); + private static final Pattern P_XML_CONTENT = Pattern.compile("(^|>)([^<]*?)(?=>)"); + private static final Pattern P_STRAY_LEFT_ARROW = Pattern.compile("<([^>]*?)(?=<|$)"); + private static final Pattern P_STRAY_RIGHT_ARROW = Pattern.compile("(^|>)([^<]*?)(?=>)"); + private static final Pattern P_AMP = Pattern.compile("&"); + private static final Pattern P_QUOTE = Pattern.compile("\""); + private static final Pattern P_LEFT_ARROW = Pattern.compile("<"); + private static final Pattern P_RIGHT_ARROW = Pattern.compile(">"); + private static final Pattern P_BOTH_ARROWS = Pattern.compile("<>"); + + // @xxx could grow large... maybe use sesat's ReferenceMap + private static final ConcurrentMap P_REMOVE_PAIR_BLANKS = new ConcurrentHashMap<>(); + private static final ConcurrentMap P_REMOVE_SELF_BLANKS = new ConcurrentHashMap<>(); + + /** + * set of allowed html elements, along with allowed attributes for each element + **/ + private final Map> vAllowed; + /** + * counts of open tags for each (allowable) html element + **/ + private final Map vTagCounts = new HashMap<>(); + + /** + * html elements which must always be self-closing (e.g. "") + **/ + private final String[] vSelfClosingTags; + /** + * html elements which must always have separate opening and closing tags (e.g. "") + **/ + private final String[] vNeedClosingTags; + /** + * set of disallowed html elements + **/ + private final String[] vDisallowed; + /** + * attributes which should be checked for valid protocols + **/ + private final String[] vProtocolAtts; + /** + * allowed protocols + **/ + private final String[] vAllowedProtocols; + /** + * tags which should be removed if they contain no content (e.g. "" or "") + **/ + private final String[] vRemoveBlanks; + /** + * entities allowed within html markup + **/ + private final String[] vAllowedEntities; + /** + * flag determining whether comments are allowed in input String. + */ + private final boolean stripComment; + private final boolean encodeQuotes; + /** + * flag determining whether to try to make tags when presented with "unbalanced" angle brackets (e.g. "" + * becomes " text "). If set to false, unbalanced angle brackets will be html escaped. + */ + private final boolean alwaysMakeTags; + + /** + * Default constructor. + */ + public HTMLFilter() + { + vAllowed = new HashMap<>(); + + final ArrayList a_atts = new ArrayList<>(); + a_atts.add("href"); + a_atts.add("target"); + vAllowed.put("a", a_atts); + + final ArrayList img_atts = new ArrayList<>(); + img_atts.add("src"); + img_atts.add("width"); + img_atts.add("height"); + img_atts.add("alt"); + vAllowed.put("img", img_atts); + + final ArrayList no_atts = new ArrayList<>(); + vAllowed.put("b", no_atts); + vAllowed.put("strong", no_atts); + vAllowed.put("i", no_atts); + vAllowed.put("em", no_atts); + + vSelfClosingTags = new String[] { "img" }; + vNeedClosingTags = new String[] { "a", "b", "strong", "i", "em" }; + vDisallowed = new String[] {}; + vAllowedProtocols = new String[] { "http", "mailto", "https" }; // no ftp. + vProtocolAtts = new String[] { "src", "href" }; + vRemoveBlanks = new String[] { "a", "b", "strong", "i", "em" }; + vAllowedEntities = new String[] { "amp", "gt", "lt", "quot" }; + stripComment = true; + encodeQuotes = true; + alwaysMakeTags = false; + } + + /** + * Map-parameter configurable constructor. + * + * @param conf map containing configuration. keys match field names. + */ + @SuppressWarnings("unchecked") + public HTMLFilter(final Map conf) + { + + assert conf.containsKey("vAllowed") : "configuration requires vAllowed"; + assert conf.containsKey("vSelfClosingTags") : "configuration requires vSelfClosingTags"; + assert conf.containsKey("vNeedClosingTags") : "configuration requires vNeedClosingTags"; + assert conf.containsKey("vDisallowed") : "configuration requires vDisallowed"; + assert conf.containsKey("vAllowedProtocols") : "configuration requires vAllowedProtocols"; + assert conf.containsKey("vProtocolAtts") : "configuration requires vProtocolAtts"; + assert conf.containsKey("vRemoveBlanks") : "configuration requires vRemoveBlanks"; + assert conf.containsKey("vAllowedEntities") : "configuration requires vAllowedEntities"; + + vAllowed = Collections.unmodifiableMap((HashMap>) conf.get("vAllowed")); + vSelfClosingTags = (String[]) conf.get("vSelfClosingTags"); + vNeedClosingTags = (String[]) conf.get("vNeedClosingTags"); + vDisallowed = (String[]) conf.get("vDisallowed"); + vAllowedProtocols = (String[]) conf.get("vAllowedProtocols"); + vProtocolAtts = (String[]) conf.get("vProtocolAtts"); + vRemoveBlanks = (String[]) conf.get("vRemoveBlanks"); + vAllowedEntities = (String[]) conf.get("vAllowedEntities"); + stripComment = conf.containsKey("stripComment") ? (Boolean) conf.get("stripComment") : true; + encodeQuotes = conf.containsKey("encodeQuotes") ? (Boolean) conf.get("encodeQuotes") : true; + alwaysMakeTags = conf.containsKey("alwaysMakeTags") ? (Boolean) conf.get("alwaysMakeTags") : true; + } + + private void reset() + { + vTagCounts.clear(); + } + + // --------------------------------------------------------------- + // my versions of some PHP library functions + public static String chr(final int decimal) + { + return String.valueOf((char) decimal); + } + + public static String htmlSpecialChars(final String s) + { + String result = s; + result = regexReplace(P_AMP, "&", result); + result = regexReplace(P_QUOTE, """, result); + result = regexReplace(P_LEFT_ARROW, "<", result); + result = regexReplace(P_RIGHT_ARROW, ">", result); + return result; + } + + // --------------------------------------------------------------- + + /** + * given a user submitted input String, filter out any invalid or restricted html. + * + * @param input text (i.e. submitted by a user) than may contain html + * @return "clean" version of input, with only valid, whitelisted html elements allowed + */ + public String filter(final String input) + { + reset(); + String s = input; + + s = escapeComments(s); + + s = balanceHTML(s); + + s = checkTags(s); + + s = processRemoveBlanks(s); + + // s = validateEntities(s); + + return s; + } + + public boolean isAlwaysMakeTags() + { + return alwaysMakeTags; + } + + public boolean isStripComments() + { + return stripComment; + } + + private String escapeComments(final String s) + { + final Matcher m = P_COMMENTS.matcher(s); + final StringBuffer buf = new StringBuffer(); + if (m.find()) + { + final String match = m.group(1); // (.*?) + m.appendReplacement(buf, Matcher.quoteReplacement("")); + } + m.appendTail(buf); + + return buf.toString(); + } + + private String balanceHTML(String s) + { + if (alwaysMakeTags) + { + // + // try and form html + // + s = regexReplace(P_END_ARROW, "", s); + // 不追加结束标签 + s = regexReplace(P_BODY_TO_END, "<$1>", s); + s = regexReplace(P_XML_CONTENT, "$1<$2", s); + + } + else + { + // + // escape stray brackets + // + s = regexReplace(P_STRAY_LEFT_ARROW, "<$1", s); + s = regexReplace(P_STRAY_RIGHT_ARROW, "$1$2><", s); + + // + // the last regexp causes '<>' entities to appear + // (we need to do a lookahead assertion so that the last bracket can + // be used in the next pass of the regexp) + // + s = regexReplace(P_BOTH_ARROWS, "", s); + } + + return s; + } + + private String checkTags(String s) + { + Matcher m = P_TAGS.matcher(s); + + final StringBuffer buf = new StringBuffer(); + while (m.find()) + { + String replaceStr = m.group(1); + replaceStr = processTag(replaceStr); + m.appendReplacement(buf, Matcher.quoteReplacement(replaceStr)); + } + m.appendTail(buf); + + // these get tallied in processTag + // (remember to reset before subsequent calls to filter method) + final StringBuilder sBuilder = new StringBuilder(buf.toString()); + for (String key : vTagCounts.keySet()) + { + for (int ii = 0; ii < vTagCounts.get(key); ii++) + { + sBuilder.append(""); + } + } + s = sBuilder.toString(); + + return s; + } + + private String processRemoveBlanks(final String s) + { + String result = s; + for (String tag : vRemoveBlanks) + { + if (!P_REMOVE_PAIR_BLANKS.containsKey(tag)) + { + P_REMOVE_PAIR_BLANKS.putIfAbsent(tag, Pattern.compile("<" + tag + "(\\s[^>]*)?>")); + } + result = regexReplace(P_REMOVE_PAIR_BLANKS.get(tag), "", result); + if (!P_REMOVE_SELF_BLANKS.containsKey(tag)) + { + P_REMOVE_SELF_BLANKS.putIfAbsent(tag, Pattern.compile("<" + tag + "(\\s[^>]*)?/>")); + } + result = regexReplace(P_REMOVE_SELF_BLANKS.get(tag), "", result); + } + + return result; + } + + private static String regexReplace(final Pattern regex_pattern, final String replacement, final String s) + { + Matcher m = regex_pattern.matcher(s); + return m.replaceAll(replacement); + } + + private String processTag(final String s) + { + // ending tags + Matcher m = P_END_TAG.matcher(s); + if (m.find()) + { + final String name = m.group(1).toLowerCase(); + if (allowed(name)) + { + if (false == inArray(name, vSelfClosingTags)) + { + if (vTagCounts.containsKey(name)) + { + vTagCounts.put(name, vTagCounts.get(name) - 1); + return ""; + } + } + } + } + + // starting tags + m = P_START_TAG.matcher(s); + if (m.find()) + { + final String name = m.group(1).toLowerCase(); + final String body = m.group(2); + String ending = m.group(3); + + // debug( "in a starting tag, name='" + name + "'; body='" + body + "'; ending='" + ending + "'" ); + if (allowed(name)) + { + final StringBuilder params = new StringBuilder(); + + final Matcher m2 = P_QUOTED_ATTRIBUTES.matcher(body); + final Matcher m3 = P_UNQUOTED_ATTRIBUTES.matcher(body); + final List paramNames = new ArrayList<>(); + final List paramValues = new ArrayList<>(); + while (m2.find()) + { + paramNames.add(m2.group(1)); // ([a-z0-9]+) + paramValues.add(m2.group(3)); // (.*?) + } + while (m3.find()) + { + paramNames.add(m3.group(1)); // ([a-z0-9]+) + paramValues.add(m3.group(3)); // ([^\"\\s']+) + } + + String paramName, paramValue; + for (int ii = 0; ii < paramNames.size(); ii++) + { + paramName = paramNames.get(ii).toLowerCase(); + paramValue = paramValues.get(ii); + + // debug( "paramName='" + paramName + "'" ); + // debug( "paramValue='" + paramValue + "'" ); + // debug( "allowed? " + vAllowed.get( name ).contains( paramName ) ); + + if (allowedAttribute(name, paramName)) + { + if (inArray(paramName, vProtocolAtts)) + { + paramValue = processParamProtocol(paramValue); + } + params.append(' ').append(paramName).append("=\\\"").append(paramValue).append("\""); + } + } + + if (inArray(name, vSelfClosingTags)) + { + ending = " /"; + } + + if (inArray(name, vNeedClosingTags)) + { + ending = ""; + } + + if (ending == null || ending.length() < 1) + { + if (vTagCounts.containsKey(name)) + { + vTagCounts.put(name, vTagCounts.get(name) + 1); + } + else + { + vTagCounts.put(name, 1); + } + } + else + { + ending = " /"; + } + return "<" + name + params + ending + ">"; + } + else + { + return ""; + } + } + + // comments + m = P_COMMENT.matcher(s); + if (!stripComment && m.find()) + { + return "<" + m.group() + ">"; + } + + return ""; + } + + private String processParamProtocol(String s) + { + s = decodeEntities(s); + final Matcher m = P_PROTOCOL.matcher(s); + if (m.find()) + { + final String protocol = m.group(1); + if (!inArray(protocol, vAllowedProtocols)) + { + // bad protocol, turn into local anchor link instead + s = "#" + s.substring(protocol.length() + 1); + if (s.startsWith("#//")) + { + s = "#" + s.substring(3); + } + } + } + + return s; + } + + private String decodeEntities(String s) + { + StringBuffer buf = new StringBuffer(); + + Matcher m = P_ENTITY.matcher(s); + while (m.find()) + { + final String match = m.group(1); + final int decimal = Integer.decode(match).intValue(); + m.appendReplacement(buf, Matcher.quoteReplacement(chr(decimal))); + } + m.appendTail(buf); + s = buf.toString(); + + buf = new StringBuffer(); + m = P_ENTITY_UNICODE.matcher(s); + while (m.find()) + { + final String match = m.group(1); + final int decimal = Integer.valueOf(match, 16).intValue(); + m.appendReplacement(buf, Matcher.quoteReplacement(chr(decimal))); + } + m.appendTail(buf); + s = buf.toString(); + + buf = new StringBuffer(); + m = P_ENCODE.matcher(s); + while (m.find()) + { + final String match = m.group(1); + final int decimal = Integer.valueOf(match, 16).intValue(); + m.appendReplacement(buf, Matcher.quoteReplacement(chr(decimal))); + } + m.appendTail(buf); + s = buf.toString(); + + s = validateEntities(s); + return s; + } + + private String validateEntities(final String s) + { + StringBuffer buf = new StringBuffer(); + + // validate entities throughout the string + Matcher m = P_VALID_ENTITIES.matcher(s); + while (m.find()) + { + final String one = m.group(1); // ([^&;]*) + final String two = m.group(2); // (?=(;|&|$)) + m.appendReplacement(buf, Matcher.quoteReplacement(checkEntity(one, two))); + } + m.appendTail(buf); + + return encodeQuotes(buf.toString()); + } + + private String encodeQuotes(final String s) + { + if (encodeQuotes) + { + StringBuffer buf = new StringBuffer(); + Matcher m = P_VALID_QUOTES.matcher(s); + while (m.find()) + { + final String one = m.group(1); // (>|^) + final String two = m.group(2); // ([^<]+?) + final String three = m.group(3); // (<|$) + // 不替换双引号为",防止json格式无效 regexReplace(P_QUOTE, """, two) + m.appendReplacement(buf, Matcher.quoteReplacement(one + two + three)); + } + m.appendTail(buf); + return buf.toString(); + } + else + { + return s; + } + } + + private String checkEntity(final String preamble, final String term) + { + + return ";".equals(term) && isValidEntity(preamble) ? '&' + preamble : "&" + preamble; + } + + private boolean isValidEntity(final String entity) + { + return inArray(entity, vAllowedEntities); + } + + private static boolean inArray(final String s, final String[] array) + { + for (String item : array) + { + if (item != null && item.equals(s)) + { + return true; + } + } + return false; + } + + private boolean allowed(final String name) + { + return (vAllowed.isEmpty() || vAllowed.containsKey(name)) && !inArray(name, vDisallowed); + } + + private boolean allowedAttribute(final String name, final String paramName) + { + return allowed(name) && (vAllowed.isEmpty() || vAllowed.get(name).contains(paramName)); + } +} diff --git a/src/main/java/com/jxy/windminer/common/utils/ip/IpUtils.java b/src/main/java/com/jxy/windminer/common/utils/ip/IpUtils.java new file mode 100644 index 0000000..bc28633 --- /dev/null +++ b/src/main/java/com/jxy/windminer/common/utils/ip/IpUtils.java @@ -0,0 +1,267 @@ +package com.jxy.windminer.common.utils.ip; + + +import com.jxy.windminer.common.utils.StringUtils; + +import javax.servlet.http.HttpServletRequest; +import java.net.InetAddress; +import java.net.UnknownHostException; + +/** + * @Description 获取ip + * @Date 2022/5/9 16:51 + * @Author 杜懿 + */ +public class IpUtils +{ + /** + * 获取客户端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 ip; + } + + /** + * 检测给定字符串是否为未知,多用于检测HTTP请求相关 + * + * @param checkString 被检测的字符串 + * @return 是否未知 + */ + public static boolean isUnknown(String checkString) + { + return StringUtils.isBlank(checkString) || "unknown".equalsIgnoreCase(checkString); + } +} diff --git a/src/main/java/com/jxy/windminer/common/utils/poi/ExcelHandlerAdapter.java b/src/main/java/com/jxy/windminer/common/utils/poi/ExcelHandlerAdapter.java new file mode 100644 index 0000000..cd8ea20 --- /dev/null +++ b/src/main/java/com/jxy/windminer/common/utils/poi/ExcelHandlerAdapter.java @@ -0,0 +1,19 @@ +package com.jxy.windminer.common.utils.poi; + +/** + * Excel数据格式处理适配器 + * + * @author 杜懿 + */ +public interface ExcelHandlerAdapter +{ + /** + * 格式化 + * + * @param value 单元格数据值 + * @param args excel注解args参数组 + * + * @return 处理后的值 + */ + Object format(Object value, String[] args); +} diff --git a/src/main/java/com/jxy/windminer/common/utils/poi/ExcelUtil.java b/src/main/java/com/jxy/windminer/common/utils/poi/ExcelUtil.java new file mode 100644 index 0000000..108420c --- /dev/null +++ b/src/main/java/com/jxy/windminer/common/utils/poi/ExcelUtil.java @@ -0,0 +1,1168 @@ +package com.jxy.windminer.common.utils.poi; + +import com.jxy.windminer.annotation.Excel; +import com.jxy.windminer.annotation.Excel.ColumnType; +import com.jxy.windminer.annotation.Excel.Type; +import com.jxy.windminer.annotation.Excels; +import com.jxy.windminer.common.text.Convert; +import com.jxy.windminer.common.utils.DateUtils; +import com.jxy.windminer.common.utils.StringUtils; +import com.jxy.windminer.common.utils.file.FileTypeUtils; +import com.jxy.windminer.common.utils.file.ImageUtils; +import com.jxy.windminer.common.utils.reflect.ReflectUtils; +import org.apache.commons.lang3.RegExUtils; +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 javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.io.InputStream; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +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 杜懿 + */ +public class ExcelUtil +{ + private static final Logger log = LoggerFactory.getLogger(ExcelUtil.class); + + public static final String FORMULA_REGEX_STR = "=|-|\\+|@"; + + public static final String[] FORMULA_STR = { "=", "-", "+", "@" }; + + /** + * Excel sheet最大行数,默认65536 ->5000 + */ + public static final int sheetSize = 5000; + + /** + * 工作表名称 + */ + 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 Map statistics = new HashMap(); + + /** + * 数字格式 + */ + private static final DecimalFormat DOUBLE_FORMAT = new DecimalFormat("######0.00"); + + /** + * 实体对象 + */ + public Class clazz; + + public ExcelUtil(Class clazz) + { + this.clazz = clazz; + } + + 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(); + } + + /** + * 创建excel第一行标题 + */ + public void createTitle() + { + if (StringUtils.isNotEmpty(title)) + { + 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(), + this.fields.size() - 1)); + } + } + + /** + * 对excel表单默认第一个索引名转换成list + * + * @param is 输入流 + * @return 转换后集合 + */ + public List importExcel(InputStream is) throws Exception + { + return importExcel(is, 0); + } + + /** + * 对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(); + } + else 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); + } + ReflectUtils.invokeSetter(entity, propertyName, val); + } + } + list.add(entity); + } + } + return list; + } + + /** + * 对list数据源将其里面的数据导入到excel表单 + * + * @param response 返回数据 + * @param list 导出数据集合 + * @param sheetName 工作表的名称 + * @return 结果 + * @throws IOException + */ + 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 结果 + * @throws IOException + */ + 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 结果 + */ + /** + * 对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) + { + Excel excel = (Excel) os[1]; + this.createCell(excel, row, column++); + } + if (Type.EXPORT.equals(type)) + { + fillExcelData(index, row); + addStatisticsRow(); + } + } + } + + /** + * 填充excel数据 + * + * @param index 序号 + * @param row 单元格行 + */ + public void fillExcelData(int index, Row row) + { + int startNo = index * sheetSize; + int endNo = Math.min(startNo + sheetSize, list.size()); + for (int i = startNo; i < endNo; i++) + { + row = sheet.createRow(i + 1 + rownum - startNo); + // 得到导出对象. + T vo = (T) list.get(i); + int column = 0; + for (Object[] os : fields) + { + Field field = (Field) os[0]; + Excel excel = (Excel) os[1]; + 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.cloneStyleFrom(styles.get("data")); + style.setAlignment(HorizontalAlignment.CENTER); + style.setVerticalAlignment(VerticalAlignment.CENTER); + style.setFillForegroundColor(IndexedColors.GREY_50_PERCENT.getIndex()); + style.setFillPattern(FillPatternType.SOLID_FOREGROUND); + Font headerFont = wb.createFont(); + headerFont.setFontName("Arial"); + headerFont.setFontHeightInPoints((short) 10); + headerFont.setBold(true); + headerFont.setColor(IndexedColors.WHITE.getIndex()); + style.setFont(headerFont); + styles.put("header", style); + + style = wb.createCellStyle(); + style.setAlignment(HorizontalAlignment.CENTER); + style.setVerticalAlignment(VerticalAlignment.CENTER); + Font totalFont = wb.createFont(); + totalFont.setFontName("Arial"); + totalFont.setFontHeightInPoints((short) 10); + style.setFont(totalFont); + styles.put("total", style); + + styles.putAll(annotationStyles(wb)); + + return styles; + } + + /** + * 根据Excel注解创建表格样式 + * + * @param wb 工作薄对象 + * @return 自定义样式列表 + */ + private Map annotationStyles(Workbook wb) + { + Map styles = new HashMap(); + for (Object[] os : fields) + { + Excel excel = (Excel) os[1]; + String key = "data_" + excel.align() + "_" + excel.color(); + if (!styles.containsKey(key)) + { + CellStyle style = wb.createCellStyle(); + 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()); + 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 createCell(Excel attr, Row row, int column) + { + // 创建列 + Cell cell = row.createCell(column); + // 写入列信息 + cell.setCellValue(attr.name()); + setDataValidation(attr, row, column); + cell.setCellStyle(styles.get("header")); + return cell; + } + + /** + * 设置单元格信息 + * + * @param value 单元格值 + * @param attr 注解相关 + * @param cell 单元格信息 + */ + public void setCellVo(Object value, Excel attr, Cell cell) + { + if (ColumnType.STRING == attr.cellType()) + { + String cellValue = Convert.toStr(value); + // 对于任何以表达式触发字符 =-+@开头的单元格,直接使用tab字符作为前缀,防止CSV注入。 + if (StringUtils.startsWithAny(cellValue, FORMULA_STR)) + { + cellValue = RegExUtils.replaceFirst(cellValue, FORMULA_REGEX_STR, "\t$0"); + } + cell.setCellValue(StringUtils.isNull(cellValue) ? attr.defaultValue() : cellValue + attr.suffix()); + } + else if (ColumnType.NUMERIC == attr.cellType()) + { + if (StringUtils.isNotNull(value)) + { + cell.setCellValue(StringUtils.contains(Convert.toStr(value), ".") ? Convert.toDouble(value) : Convert.toInt(value)); + } + } + else if (ColumnType.IMAGE == attr.cellType()) + { + ClientAnchor anchor = new XSSFClientAnchor(0, 0, 0, 0, (short) cell.getColumnIndex(), cell.getRow().getRowNum(), (short) (cell.getColumnIndex() + 1), cell.getRow().getRowNum() + 1); + String imagePath = Convert.toStr(value); + if (StringUtils.isNotEmpty(imagePath)) + { + byte[] data = ImageUtils.getImage(imagePath); + getDrawingPatriarch(cell.getSheet()).createPicture(anchor, + cell.getSheet().getWorkbook().addPicture(data, getImageType(data))); + } + } + } + + /** + * 获取画布 + */ + public static Drawing getDrawingPatriarch(Sheet sheet) + { + if (sheet.getDrawingPatriarch() == null) + { + sheet.createDrawingPatriarch(); + } + return sheet.getDrawingPatriarch(); + } + + /** + * 获取图片类型,设置图片插入类型 + */ + public int getImageType(byte[] value) + { + String type = FileTypeUtils.getFileExtendName(value); + if ("JPG".equalsIgnoreCase(type)) + { + return Workbook.PICTURE_TYPE_JPEG; + } + else if ("PNG".equalsIgnoreCase(type)) + { + return Workbook.PICTURE_TYPE_PNG; + } + return Workbook.PICTURE_TYPE_JPEG; + } + + /** + * 创建表格样式 + */ + public void setDataValidation(Excel attr, Row row, int column) + { + if (attr.name().indexOf("注:") >= 0) + { + sheet.setColumnWidth(column, 6000); + } + else + { + // 设置列宽 + sheet.setColumnWidth(column, (int) ((attr.width() + 0.72) * 256)); + } + if (StringUtils.isNotEmpty(attr.prompt()) || attr.combo().length > 0) + { + // 提示信息或只能选择不能输入的列内容. + 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); + cell.setCellStyle(styles.get("data_" + attr.align() + "_" + attr.color())); + + // 用于读取对象中的属性 + 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())).toString()); + } + else if (!attr.handler().equals(ExcelHandlerAdapter.class)) + { + cell.setCellValue(dataFormatHandlerAdapter(value, attr)); + } + 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); + } + + /** + * 解析导出值 0=男,1=女,2=未知 + * + * @param propertyValue 参数值 + * @param converterExp 翻译注解 + * @param separator 分隔符 + * @return 解析后值 + */ + public static String convertByExp(String propertyValue, String converterExp, String separator) + { + StringBuilder propertyString = new StringBuilder(); + String[] convertSource = converterExp.split(","); + for (String item : convertSource) + { + String[] itemArray = item.split("="); + if (StringUtils.containsAny(separator, propertyValue)) + { + for (String value : propertyValue.split(separator)) + { + if (itemArray[0].equals(value)) + { + propertyString.append(itemArray[1] + separator); + break; + } + } + } + else + { + if (itemArray[0].equals(propertyValue)) + { + return itemArray[1]; + } + } + } + return StringUtils.stripEnd(propertyString.toString(), separator); + } + + /** + * 反向解析值 男=0,女=1,未知=2 + * + * @param propertyValue 参数值 + * @param converterExp 翻译注解 + * @param separator 分隔符 + * @return 解析后值 + */ + public static String reverseByExp(String propertyValue, String converterExp, String separator) + { + StringBuilder propertyString = new StringBuilder(); + String[] convertSource = converterExp.split(","); + for (String item : convertSource) + { + String[] itemArray = item.split("="); + if (StringUtils.containsAny(separator, propertyValue)) + { + for (String value : propertyValue.split(separator)) + { + if (itemArray[1].equals(value)) + { + propertyString.append(itemArray[0] + separator); + break; + } + } + } + else + { + if (itemArray[1].equals(propertyValue)) + { + return itemArray[0]; + } + } + } + return StringUtils.stripEnd(propertyString.toString(), separator); + } + + /** + * 数据处理器 + * + * @param value 数据值 + * @param excel 数据注解 + * @return + */ + public String dataFormatHandlerAdapter(Object value, Excel excel) + { + try + { + Object instance = excel.handler().newInstance(); + Method formatMethod = excel.handler().getMethod("format", new Class[] { Object.class, String[].class }); + value = formatMethod.invoke(instance, value, excel.args()); + } + 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 (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 (field.isAnnotationPresent(Excels.class)) + { + Excels attrs = field.getAnnotation(Excels.class); + Excel[] excels = attrs.value(); + for (Excel attr : excels) + { + if (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; + } +} diff --git a/src/main/java/com/jxy/windminer/common/utils/reflect/ReflectUtils.java b/src/main/java/com/jxy/windminer/common/utils/reflect/ReflectUtils.java new file mode 100644 index 0000000..712bed6 --- /dev/null +++ b/src/main/java/com/jxy/windminer/common/utils/reflect/ReflectUtils.java @@ -0,0 +1,406 @@ +package com.jxy.windminer.common.utils.reflect; + +import com.jxy.windminer.common.text.Convert; +import com.jxy.windminer.common.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 杜懿 + */ +@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/jxy/windminer/common/utils/sign/Base64.java b/src/main/java/com/jxy/windminer/common/utils/sign/Base64.java new file mode 100644 index 0000000..338c100 --- /dev/null +++ b/src/main/java/com/jxy/windminer/common/utils/sign/Base64.java @@ -0,0 +1,292 @@ +package com.jxy.windminer.common.utils.sign; + +/** + * @Description Base64工具类 + * @Date 2022/5/17 15:19 + * @Author 杜懿 + */ +public final class Base64 { + + static private final int BASELENGTH = 256; + 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/jxy/windminer/common/utils/sign/RsaUtils.java b/src/main/java/com/jxy/windminer/common/utils/sign/RsaUtils.java new file mode 100644 index 0000000..92736e9 --- /dev/null +++ b/src/main/java/com/jxy/windminer/common/utils/sign/RsaUtils.java @@ -0,0 +1,173 @@ +package com.jxy.windminer.common.utils.sign; + + +import org.apache.commons.codec.binary.Base64; + +import javax.crypto.Cipher; +import java.security.*; +import java.security.interfaces.RSAPrivateKey; +import java.security.interfaces.RSAPublicKey; +import java.security.spec.PKCS8EncodedKeySpec; +import java.security.spec.X509EncodedKeySpec; + +/** + * @Description TODO + * @Date 2022/11/25 17:35 + * @Author 杜懿 + */ +public class RsaUtils { + + // Rsa 私钥 + public static String privateKey = "MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDTla3smq/59AVL\n" + + "g0PayphcAyefuJlgJOHF7nN9eOpnbtDdsgLQ0giAoByujHcp7mZv6EngPmlgVudF\n" + + "1pELpfXrhAMwszE13aOBWtJXAlsQgloE14sjNxLpnOjNUQyAiq44F+6vC3yKXTvR\n" + + "4VzOgEuIVed8uWZOnHR9J6bilMEshE+4zR9BAB4PjX0+7+nTrsS5gEw7LX1zql/R\n" + + "BCdRVt09mMO9lvxYtde2kqEBZP/2joKMmSJ5OaSOyU/jv1+T8iZFduYAvCgHJWlj\n" + + "6Rfo1NfqAETLney+LtquULUgr4+n2df9FtSQtGhcfaF7p2B3kX/xAJXh4cwnoJ4u\n" + + "5Q5FXHYVAgMBAAECggEAMEj8Q/6ZIkWZ3725Ankxg+4EYOLTvaktDOp52Kx7cddM\n" + + "OwugsN79qbzgTsUnfJ43KlqsYUxc5+ttI/bvauUY1gJuZ/K8zDokUiTT059qAL5+\n" + + "pJQ74HF1E3MHfbN9UuhTEeIESlYoubrFKARyFX4Zvqc8CK6WtmHmA5nE7/hajTnK\n" + + "aToKPLP8KXa/U8W8paKUhUV1aoEBtqxOLQs1rg8PREGCUmUyTRzzxblm/SVd6YYB\n" + + "vCKcbZgKQwt7lrKrUVcVc+VDrJ6UWU+XNMABWT+NwUvrTayQehot5lxfZxfPk0IG\n" + + "h9OhOtclOQ8FfRh2UeLmkAG4PTadr+r7Lrbcm9NokQKBgQDtKbNcvJIuXKqecVxA\n" + + "+QiINx/PPp44wFhZk9iIHh02MVRNtgxEIMpUleyIHlEGMmTKcchO56GRXiFfqNu5\n" + + "8DduoU4tbKztl1YtYctMRWvYDvcNcveEhm/+J01e/Zyej4ElLjTZeg+rOSREhkMr\n" + + "EKDSA6AoltjpcCThYxG+Rtr0SwKBgQDkY+PRDc5BWw+icKXu6DgajUw+i3pzVRMW\n" + + "lF/Q4sNi7zlTP+YcnuhqgHu0ILFUgTQAgbdqVKZjG3g9bI7Ziaus10H+LXTizNue\n" + + "QNibe/UZ29Q2SsDeATMwDGrUQ2d0FuGaVWJoY4auNWuZr+6XaEnih2x5FniktdQk\n" + + "+YbYinQDHwKBgFBCs4un2YTNIYS7cnAel0+Z8C7vzxX/qiaujTILlvE3IoOmH2KT\n" + + "AkY78q9iKyOAvHFyrkpdw2TxyTOZbrrvW6MZ/d4LkD5b3/M9zFJEkCmvbtZjWPbF\n" + + "lHMbk+iYxX83q4oMqCANWe4lSWvTUDnrx7ErPvFdk4z0wdZw85lEW2cfAoGBAICn\n" + + "3I/JTST33QjOmErKucALVKXvAF2z0PrpPkh3VUWIKSzCVChPQ/GqywSfXgWSeu7G\n" + + "I8JcSRaPRN6lJptYuEK3R8+dX7jbWeP994cu/tVARn0HAzqMRn+MnylPhxmYQiIk\n" + + "czkGx7mfEiwTNT5JW0Wmr+5OQEvYudbSUant5IhVAoGBAM0rAsaQ2ZQEeUCZGuu1\n" + + "IfW21gwl13G4mWt2ng1rV8iv8Kn9yu0o7CRGAf4IlUYqOcJtfh8F5liQBcJzd2Bc\n" + + "odT7Zo+P4Zqtgg9nKehkd5Z7+zmFNog3Z/lZMj/dJuwO+Q8Ef6qKMYdPQuwrsVBD\n" + + "RY91AS8cjE/RsmNeWNJjTkq5"; + + /** + * 私钥解密 + * + * @param text 待解密的文本 + * @return 解密后的文本 + */ + public static String decryptByPrivateKey(String text) throws Exception + { + return decryptByPrivateKey(privateKey, text); + } + + /** + * 公钥解密 + * + * @param publicKeyString 公钥 + * @param text 待解密的信息 + * @return 解密后的文本 + */ + public static String decryptByPublicKey(String publicKeyString, String text) throws Exception + { + X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(Base64.decodeBase64(publicKeyString)); + KeyFactory keyFactory = KeyFactory.getInstance("RSA"); + PublicKey publicKey = keyFactory.generatePublic(x509EncodedKeySpec); + Cipher cipher = Cipher.getInstance("RSA"); + cipher.init(Cipher.DECRYPT_MODE, publicKey); + byte[] result = cipher.doFinal(Base64.decodeBase64(text)); + return new String(result); + } + + /** + * 私钥加密 + * + * @param privateKeyString 私钥 + * @param text 待加密的信息 + * @return 加密后的文本 + */ + public static String encryptByPrivateKey(String privateKeyString, String text) throws Exception + { + PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(Base64.decodeBase64(privateKeyString)); + KeyFactory keyFactory = KeyFactory.getInstance("RSA"); + PrivateKey privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec); + Cipher cipher = Cipher.getInstance("RSA"); + cipher.init(Cipher.ENCRYPT_MODE, privateKey); + byte[] result = cipher.doFinal(text.getBytes()); + return Base64.encodeBase64String(result); + } + + /** + * 私钥解密 + * + * @param privateKeyString 私钥 + * @param text 待解密的文本 + * @return 解密后的文本 + */ + public static String decryptByPrivateKey(String privateKeyString, String text) throws Exception + { + PKCS8EncodedKeySpec pkcs8EncodedKeySpec5 = new PKCS8EncodedKeySpec(Base64.decodeBase64(privateKeyString)); + KeyFactory keyFactory = KeyFactory.getInstance("RSA"); + PrivateKey privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec5); + Cipher cipher = Cipher.getInstance("RSA"); + cipher.init(Cipher.DECRYPT_MODE, privateKey); + byte[] result = cipher.doFinal(Base64.decodeBase64(text)); + return new String(result); + } + + /** + * 公钥加密 + * + * @param publicKeyString 公钥 + * @param text 待加密的文本 + * @return 加密后的文本 + */ + public static String encryptByPublicKey(String publicKeyString, String text) throws Exception + { + X509EncodedKeySpec x509EncodedKeySpec2 = new X509EncodedKeySpec(Base64.decodeBase64(publicKeyString)); + KeyFactory keyFactory = KeyFactory.getInstance("RSA"); + PublicKey publicKey = keyFactory.generatePublic(x509EncodedKeySpec2); + Cipher cipher = Cipher.getInstance("RSA"); + cipher.init(Cipher.ENCRYPT_MODE, publicKey); + byte[] result = cipher.doFinal(text.getBytes()); + return Base64.encodeBase64String(result); + } + + /** + * 构建RSA密钥对 + * + * @return 生成后的公私钥信息 + */ + public static RsaKeyPair generateKeyPair() throws NoSuchAlgorithmException + { + KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA"); + keyPairGenerator.initialize(1024); + KeyPair keyPair = keyPairGenerator.generateKeyPair(); + RSAPublicKey rsaPublicKey = (RSAPublicKey) keyPair.getPublic(); + RSAPrivateKey rsaPrivateKey = (RSAPrivateKey) keyPair.getPrivate(); + String publicKeyString = Base64.encodeBase64String(rsaPublicKey.getEncoded()); + String privateKeyString = Base64.encodeBase64String(rsaPrivateKey.getEncoded()); + return new RsaKeyPair(publicKeyString, privateKeyString); + } + + /** + * RSA密钥对对象 + */ + public static class RsaKeyPair + { + private final String publicKey; + private final String privateKey; + + public RsaKeyPair(String publicKey, String privateKey) + { + this.publicKey = publicKey; + this.privateKey = privateKey; + } + + public String getPublicKey() + { + return publicKey; + } + + public String getPrivateKey() + { + return privateKey; + } + } + +} diff --git a/src/main/java/com/jxy/windminer/common/utils/sql/SqlUtil.java b/src/main/java/com/jxy/windminer/common/utils/sql/SqlUtil.java new file mode 100644 index 0000000..9feb0ff --- /dev/null +++ b/src/main/java/com/jxy/windminer/common/utils/sql/SqlUtil.java @@ -0,0 +1,62 @@ +package com.jxy.windminer.common.utils.sql; + + +import com.jxy.windminer.common.exception.UtilException; +import com.jxy.windminer.common.utils.StringUtils; + +/** + * @Description sql操作工具类 + * @Date 2022/5/10 11:40 + * @Author 杜懿 + */ +public class SqlUtil +{ + /** + * 定义常用的 sql关键字 + */ + public static String SQL_REGEX = "select |insert |delete |update |drop |count |exec |chr |mid |master |truncate |char |and |declare "; + + /** + * 仅支持字母、数字、下划线、空格、逗号、小数点(支持多个字段排序) + */ + public static String SQL_PATTERN = "[a-zA-Z0-9_\\ \\,\\.]+"; + + /** + * 检查字符,防止注入绕过 + */ + public static String escapeOrderBySql(String value) + { + if (StringUtils.isNotEmpty(value) && !isValidOrderBySql(value)) + { + 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/jxy/windminer/common/utils/uuid/IdUtils.java b/src/main/java/com/jxy/windminer/common/utils/uuid/IdUtils.java new file mode 100644 index 0000000..f350dff --- /dev/null +++ b/src/main/java/com/jxy/windminer/common/utils/uuid/IdUtils.java @@ -0,0 +1,49 @@ +package com.jxy.windminer.common.utils.uuid; + +/** + * @Description ID生成器 + * @Date 2022/5/10 11:24 + * @Author 杜懿 + */ +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/jxy/windminer/common/utils/uuid/Seq.java b/src/main/java/com/jxy/windminer/common/utils/uuid/Seq.java new file mode 100644 index 0000000..8be6d24 --- /dev/null +++ b/src/main/java/com/jxy/windminer/common/utils/uuid/Seq.java @@ -0,0 +1,88 @@ +package com.jxy.windminer.common.utils.uuid; + + +import com.jxy.windminer.common.utils.DateUtils; +import com.jxy.windminer.common.utils.StringUtils; + +import java.util.concurrent.atomic.AtomicInteger; + +/** + * @author jxy 序列生成类 + */ +public class Seq +{ + // 通用序列类型 + public static final String commSeqType = "COMMON"; + + // 上传序列类型 + public static final String uploadSeqType = "UPLOAD"; + + // 通用接口序列数 + private static AtomicInteger commSeq = new AtomicInteger(1); + + // 上传接口序列数 + private static AtomicInteger uploadSeq = new AtomicInteger(1); + + // 机器标识 + private static String machineCode = "A"; + + /** + * 获取通用序列号 + * + * @return 序列值 + */ + public static String getId() + { + return getId(commSeqType); + } + + /** + * 默认16位序列号 yyMMddHHmmss + 一位机器标识 + 3长度循环递增字符串 + * + * @return 序列值 + */ + public static String getId(String type) + { + AtomicInteger atomicInt = commSeq; + if (uploadSeqType.equals(type)) + { + atomicInt = uploadSeq; + } + return getId(atomicInt, 3); + } + + /** + * 通用接口序列号 yyMMddHHmmss + 一位机器标识 + length长度循环递增字符串 + * + * @param atomicInt 序列数 + * @param length 数值长度 + * @return 序列值 + */ + public static String getId(AtomicInteger atomicInt, int length) + { + String result = DateUtils.dateTimeNow(); + result += machineCode; + result += getSeq(atomicInt, length); + return result; + } + + /** + * 序列循环递增字符串[1, 10 的 (length)幂次方), 用0左补齐length位数 + * + * @return 序列值 + */ + private synchronized static String getSeq(AtomicInteger atomicInt, int length) + { + // 先取值再+1 + int value = atomicInt.getAndIncrement(); + + // 如果更新后值>=10 的 (length)幂次方则重置为1 + int maxSeq = (int) Math.pow(10, length); + if (atomicInt.get() >= maxSeq) + { + atomicInt.set(1); + } + // 转字符串,用0左补齐 + return StringUtils.padl(value, length); + } +} diff --git a/src/main/java/com/jxy/windminer/common/utils/uuid/UUID.java b/src/main/java/com/jxy/windminer/common/utils/uuid/UUID.java new file mode 100644 index 0000000..81fb5a8 --- /dev/null +++ b/src/main/java/com/jxy/windminer/common/utils/uuid/UUID.java @@ -0,0 +1,486 @@ +package com.jxy.windminer.common.utils.uuid; + +import com.jxy.windminer.common.exception.UtilException; + +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; +import java.util.Random; +import java.util.concurrent.ThreadLocalRandom; + +/** + * @Description 提供通用唯一识别码(universally unique identifier)(UUID)实现 + * @Date 2022/5/10 9:26 + * @Author 杜懿 + */ + +public final class UUID implements java.io.Serializable, Comparable +{ + private static final long serialVersionUID = -1285054133654744140L; + + /** + * SecureRandom 的单例 + * + */ + private static class Holder + { + static final SecureRandom numberGenerator = getSecureRandom(); + } + + /** 此UUID的最高64有效位 */ + private final long mostSigBits; + + /** 此UUID的最低64有效位 */ + private final long leastSigBits; + + /** + * 私有构造 + * + * @param data 数据 + */ + private UUID(byte[] data) + { + long msb = 0; + long lsb = 0; + assert data.length == 16 : "data must be 16 bytes in length"; + for (int i = 0; i < 8; i++) + { + msb = (msb << 8) | (data[i] & 0xff); + } + for (int i = 8; i < 16; i++) + { + lsb = (lsb << 8) | (data[i] & 0xff); + } + this.mostSigBits = msb; + this.leastSigBits = lsb; + } + + /** + * 使用指定的数据构造新的 UUID。 + * + * @param mostSigBits 用于 {@code UUID} 的最高有效 64 位 + * @param leastSigBits 用于 {@code UUID} 的最低有效 64 位 + */ + public UUID(long mostSigBits, long leastSigBits) + { + this.mostSigBits = mostSigBits; + this.leastSigBits = leastSigBits; + } + + /** + * 获取类型 4(伪随机生成的)UUID 的静态工厂。 使用加密的本地线程伪随机数生成器生成该 UUID。 + * + * @return 随机生成的 {@code UUID} + */ + public static UUID fastUUID() + { + return randomUUID(false); + } + + /** + * 获取类型 4(伪随机生成的)UUID 的静态工厂。 使用加密的强伪随机数生成器生成该 UUID。 + * + * @return 随机生成的 {@code UUID} + */ + public static UUID randomUUID() + { + return randomUUID(true); + } + + /** + * 获取类型 4(伪随机生成的)UUID 的静态工厂。 使用加密的强伪随机数生成器生成该 UUID。 + * + * @param isSecure 是否使用{@link SecureRandom}如果是可以获得更安全的随机码,否则可以得到更好的性能 + * @return 随机生成的 {@code UUID} + */ + public static UUID randomUUID(boolean isSecure) + { + final Random ng = isSecure ? Holder.numberGenerator : getRandom(); + + byte[] randomBytes = new byte[16]; + ng.nextBytes(randomBytes); + randomBytes[6] &= 0x0f; /* clear version */ + randomBytes[6] |= 0x40; /* set to version 4 */ + randomBytes[8] &= 0x3f; /* clear variant */ + randomBytes[8] |= 0x80; /* set to IETF variant */ + return new UUID(randomBytes); + } + + /** + * 根据指定的字节数组获取类型 3(基于名称的)UUID 的静态工厂。 + * + * @param name 用于构造 UUID 的字节数组。 + * + * @return 根据指定数组生成的 {@code UUID} + */ + public static UUID nameUUIDFromBytes(byte[] name) + { + MessageDigest md; + try + { + md = MessageDigest.getInstance("MD5"); + } + catch (NoSuchAlgorithmException nsae) + { + throw new InternalError("MD5 not supported"); + } + byte[] md5Bytes = md.digest(name); + md5Bytes[6] &= 0x0f; /* clear version */ + md5Bytes[6] |= 0x30; /* set to version 3 */ + md5Bytes[8] &= 0x3f; /* clear variant */ + md5Bytes[8] |= 0x80; /* set to IETF variant */ + return new UUID(md5Bytes); + } + + /** + * 根据 {@link #toString()} 方法中描述的字符串标准表示形式创建{@code UUID}。 + * + * @param name 指定 {@code UUID} 字符串 + * @return 具有指定值的 {@code UUID} + * @throws IllegalArgumentException 如果 name 与 {@link #toString} 中描述的字符串表示形式不符抛出此异常 + * + */ + public static UUID fromString(String name) + { + String[] components = name.split("-"); + if (components.length != 5) + { + throw new IllegalArgumentException("Invalid UUID string: " + name); + } + for (int i = 0; i < 5; i++) + { + components[i] = "0x" + components[i]; + } + + long mostSigBits = Long.decode(components[0]).longValue(); + mostSigBits <<= 16; + mostSigBits |= Long.decode(components[1]).longValue(); + mostSigBits <<= 16; + mostSigBits |= Long.decode(components[2]).longValue(); + + long leastSigBits = Long.decode(components[3]).longValue(); + leastSigBits <<= 48; + leastSigBits |= Long.decode(components[4]).longValue(); + + return new UUID(mostSigBits, leastSigBits); + } + + /** + * 返回此 UUID 的 128 位值中的最低有效 64 位。 + * + * @return 此 UUID 的 128 位值中的最低有效 64 位。 + */ + public long getLeastSignificantBits() + { + return leastSigBits; + } + + /** + * 返回此 UUID 的 128 位值中的最高有效 64 位。 + * + * @return 此 UUID 的 128 位值中最高有效 64 位。 + */ + public long getMostSignificantBits() + { + return mostSigBits; + } + + /** + * 与此 {@code UUID} 相关联的版本号. 版本号描述此 {@code UUID} 是如何生成的。 + *

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

+ * 如果两个 UUID 不同,且第一个 UUID 的最高有效字段大于第二个 UUID 的对应字段,则第一个 UUID 大于第二个 UUID。 + * + * @param val 与此 UUID 比较的 UUID + * + * @return 在此 UUID 小于、等于或大于 val 时,分别返回 -1、0 或 1。 + * + */ + @Override + public int compareTo(UUID val) + { + // The ordering is intentionally set up so that the UUIDs + // can simply be numerically compared as two numbers + return (this.mostSigBits < val.mostSigBits ? -1 : // + (this.mostSigBits > val.mostSigBits ? 1 : // + (this.leastSigBits < val.leastSigBits ? -1 : // + (this.leastSigBits > val.leastSigBits ? 1 : // + 0)))); + } + + // ------------------------------------------------------------------------------------------------------------------- + // Private method start + /** + * 返回指定数字对应的hex值 + * + * @param val 值 + * @param digits 位 + * @return 值 + */ + private static String digits(long val, int digits) + { + long hi = 1L << (digits * 4); + return Long.toHexString(hi | (val & (hi - 1))).substring(1); + } + + /** + * 检查是否为time-based版本UUID + */ + private void checkTimeBase() + { + if (version() != 1) + { + throw new UnsupportedOperationException("Not a time-based UUID"); + } + } + + /** + * 获取{@link SecureRandom},类提供加密的强随机数生成器 (RNG) + * + * @return {@link SecureRandom} + */ + public static SecureRandom getSecureRandom() + { + try + { + return SecureRandom.getInstance("SHA1PRNG"); + } + catch (NoSuchAlgorithmException e) + { + throw new UtilException(e); + } + } + + /** + * 获取随机数生成器对象
+ * + * @return {@link ThreadLocalRandom} + */ + public static ThreadLocalRandom getRandom() + { + return ThreadLocalRandom.current(); + } +} + diff --git a/src/main/java/com/jxy/windminer/common/web/Result/AjaxResult.java b/src/main/java/com/jxy/windminer/common/web/Result/AjaxResult.java new file mode 100644 index 0000000..7176246 --- /dev/null +++ b/src/main/java/com/jxy/windminer/common/web/Result/AjaxResult.java @@ -0,0 +1,164 @@ +package com.jxy.windminer.common.web.Result; + +import com.jxy.windminer.common.constant.HttpStatus; +import com.jxy.windminer.common.utils.StringUtils; + + +import java.util.HashMap; + +/** + * @Description 操作消息提醒 + * @Date 2022/5/10 15:05 + * @Author 杜懿 + */ +public class AjaxResult extends HashMap +{ + private static final long serialVersionUID = 3247645319123122183L; + + /** 状态码 */ + public static final String CODE_TAG = "code"; + + /** 返回内容 */ + public static final String MSG_TAG = "msg"; + + /** 数据对象 */ + public static final String DATA_TAG = "data"; + + /** + * 初始化一个新创建的 AjaxResult 对象,使其表示一个空消息。 + */ + public AjaxResult() + { + } + + /** + * 初始化一个新创建的 AjaxResult 对象 + * + * @param code 状态码 + * @param msg 返回内容 + */ + public AjaxResult(int code, String msg) + { + super.put(CODE_TAG, code); + super.put(MSG_TAG, msg); + } + + /** + * 初始化一个新创建的 AjaxResult 对象 + * + * @param code 状态码 + * @param msg 返回内容 + * @param data 数据对象 + */ + public AjaxResult(int code, String msg, Object data) + { + super.put(CODE_TAG, code); + super.put(MSG_TAG, msg); + if (StringUtils.isNotNull(data)) + { + super.put(DATA_TAG, data); + } + } + + /** + * 方便链式调用 + * + * @param key + * @param value + * @return + */ + @Override + public AjaxResult put(String key, Object value) + { + super.put(key, value); + return this; + } + + /** + * 返回成功消息 + * + * @return 成功消息 + */ + public static AjaxResult success() + { + return AjaxResult.success("操作成功"); + } + + /** + * 返回成功数据 + * + * @return 成功消息 + */ + public static AjaxResult success(Object data) + { + return AjaxResult.success("操作成功", data); + } + + /** + * 返回成功消息 + * + * @param msg 返回内容 + * @return 成功消息 + */ + public static AjaxResult success(String msg) + { + return AjaxResult.success(msg, null); + } + + /** + * 返回成功消息 + * + * @param msg 返回内容 + * @param data 数据对象 + * @return 成功消息 + */ + public static AjaxResult success(String msg, Object data) + { + return new AjaxResult(HttpStatus.SUCCESS, msg, data); + } + + /** + * 返回错误消息 + * + * @return + */ + public static AjaxResult error() + { + return AjaxResult.error("操作失败"); + } + + /** + * 返回错误消息 + * + * @param msg 返回内容 + * @return 警告消息 + */ + public static AjaxResult error(String msg) + { + return AjaxResult.error(msg, null); + } + + /** + * 返回错误消息 + * + * @param msg 返回内容 + * @param data 数据对象 + * @return 警告消息 + */ + public static AjaxResult error(String msg, Object data) + { + return new AjaxResult(HttpStatus.ERROR, msg, data); + } + + /** + * 返回错误消息 + * + * @param code 状态码 + * @param msg 返回内容 + * @return 警告消息 + */ + public static AjaxResult error(int code, String msg) + { + return new AjaxResult(code, msg, null); + } +} diff --git a/src/main/java/com/jxy/windminer/common/web/Result/PageResult.java b/src/main/java/com/jxy/windminer/common/web/Result/PageResult.java new file mode 100644 index 0000000..a8a775e --- /dev/null +++ b/src/main/java/com/jxy/windminer/common/web/Result/PageResult.java @@ -0,0 +1,26 @@ +package com.jxy.windminer.common.web.Result; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +/** + * @Description 操作消息提醒 + * @Date 2022/5/10 15:05 + * @Author 杜懿 + */ +@ApiModel(description = "响应信息") +@Data +public class PageResult +{ + //总记录条数 + @ApiModelProperty(value = "总记录条数") + private Long count; + + @ApiModelProperty(value = "总页数") + private Long totalPage; + + //返回的数据 + @ApiModelProperty(value = "返回的数据") + private T data; +} diff --git a/src/main/java/com/jxy/windminer/common/web/controller/BaseController.java b/src/main/java/com/jxy/windminer/common/web/controller/BaseController.java new file mode 100644 index 0000000..af163c9 --- /dev/null +++ b/src/main/java/com/jxy/windminer/common/web/controller/BaseController.java @@ -0,0 +1,129 @@ +package com.jxy.windminer.common.web.controller; + +import com.github.pagehelper.PageInfo; +import com.jxy.windminer.common.constant.HttpStatus; +import com.jxy.windminer.common.utils.DateUtils; +import com.jxy.windminer.common.utils.PageUtils; +import com.jxy.windminer.common.web.Result.AjaxResult; +import com.jxy.windminer.common.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; + +/** + * @Description web层数据处理 + * @Date 2022/5/10 15:52 + * @Author 杜懿 + */ +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 TableDataInfo getDataTable(List list) + { + TableDataInfo rspData = new TableDataInfo(); + rspData.setCode(HttpStatus.SUCCESS); + rspData.setRows(list); + rspData.setMsg("查询成功"); + PageInfo pageInfo = new PageInfo(list); + rspData.setTotal(pageInfo.getTotal()); + rspData.setTotalPage(pageInfo.getPages()); + return rspData; + } + + /** + * 响应返回结果 + * + * @param rows 影响行数 + * @return 操作结果 + */ + protected AjaxResult toAjax(int rows) + { + return rows > 0 ? AjaxResult.success() : AjaxResult.error(); + } + + /** + * 响应返回结果 + * + * @param result 结果 + * @return 操作结果 + */ + protected AjaxResult toAjax(boolean result) + { + return result ? success() : error(); + } + + /** + * 返回成功 + */ + public AjaxResult success() + { + return AjaxResult.success(); + } + + /** + * 返回失败消息 + */ + public AjaxResult error() + { + return AjaxResult.error(); + } + + /** + * 返回成功消息 + */ + public AjaxResult success(String message) + { + return AjaxResult.success(message); + } + + /** + * 返回失败消息 + */ + public AjaxResult error(String message) + { + return AjaxResult.error(message); + } +} diff --git a/src/main/java/com/jxy/windminer/common/web/entity/BaseEntity.java b/src/main/java/com/jxy/windminer/common/web/entity/BaseEntity.java new file mode 100644 index 0000000..3d65827 --- /dev/null +++ b/src/main/java/com/jxy/windminer/common/web/entity/BaseEntity.java @@ -0,0 +1,53 @@ +package com.jxy.windminer.common.web.entity; + +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.Data; + +import java.io.Serializable; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; + +/** + * @Description Entity基类 + * @Date 2022/5/10 15:31 + * @Author 杜懿 + */ +@Data +public class BaseEntity implements Serializable +{ + private static final long serialVersionUID = 2417642483861314318L; + + /** 搜索值 */ + 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; + + /** 请求参数 */ + private Map params; + + + public Map getParams() + { + if (params == null) + { + params = new HashMap<>(); + } + return params; + } +} diff --git a/src/main/java/com/jxy/windminer/common/web/page/Page.java b/src/main/java/com/jxy/windminer/common/web/page/Page.java new file mode 100644 index 0000000..ba7b959 --- /dev/null +++ b/src/main/java/com/jxy/windminer/common/web/page/Page.java @@ -0,0 +1,64 @@ +package com.jxy.windminer.common.web.page; + +import com.jxy.windminer.common.utils.StringUtils; +import lombok.Data; + +/** + * @Description 数据分页 + * @Date 2022/5/10 14:14 + * @Author 杜懿 + */ +@Data +public class Page { + + /** 当前记录起始索引 */ + 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 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; + } + +} diff --git a/src/main/java/com/jxy/windminer/common/web/page/TableDataInfo.java b/src/main/java/com/jxy/windminer/common/web/page/TableDataInfo.java new file mode 100644 index 0000000..5c7cea4 --- /dev/null +++ b/src/main/java/com/jxy/windminer/common/web/page/TableDataInfo.java @@ -0,0 +1,52 @@ +package com.jxy.windminer.common.web.page; + +import lombok.Data; + +import java.io.Serializable; +import java.util.List; + +/** + * @Description 表格分页数据对象 + * @Date 2022/5/10 14:59 + * @Author 杜懿 + */ +@Data +public class TableDataInfo implements Serializable +{ + private static final long serialVersionUID = 1L; + + /** 总记录数 */ + private long total; + + /** 总页数 */ + private long totalPage; + + /** 列表数据 */ + private List rows; + + /** 消息状态码 */ + private int code; + + /** 消息内容 */ + private String msg; + + /** + * 表格数据对象 + */ + public TableDataInfo() + { + } + + /** + * 分页 + * + * @param list 列表数据 + * @param total 总记录数 + */ + public TableDataInfo(List list, int total) + { + this.rows = list; + this.total = total; + } + +} diff --git a/src/main/java/com/jxy/windminer/common/web/page/TableSupport.java b/src/main/java/com/jxy/windminer/common/web/page/TableSupport.java new file mode 100644 index 0000000..db56c46 --- /dev/null +++ b/src/main/java/com/jxy/windminer/common/web/page/TableSupport.java @@ -0,0 +1,58 @@ +package com.jxy.windminer.common.web.page; + + +import com.jxy.windminer.common.text.Convert; +import com.jxy.windminer.common.utils.ServletUtils; + +/** + * @Description 表格数据处理 + * @Date 2022/5/10 14:32 + * @Author 杜懿 + */ +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 Page getPage() + { + Page page = new Page(); + page.setPageNum(Convert.toInt(ServletUtils.getParameter(PAGE_NUM), 1)); + page.setPageSize(Convert.toInt(ServletUtils.getParameter(PAGE_SIZE), 20)); + page.setOrderByColumn(ServletUtils.getParameter(ORDER_BY_COLUMN)); + page.setIsAsc(ServletUtils.getParameter(IS_ASC)); + page.setReasonable(ServletUtils.getParameterToBool(REASONABLE)); + System.out.println(page); + return page; + } + + public static Page buildPageRequest() + { + return getPage(); + } +} diff --git a/src/main/java/com/jxy/windminer/common/xss/Xss.java b/src/main/java/com/jxy/windminer/common/xss/Xss.java new file mode 100644 index 0000000..7535c65 --- /dev/null +++ b/src/main/java/com/jxy/windminer/common/xss/Xss.java @@ -0,0 +1,27 @@ +package com.jxy.windminer.common.xss; + +import javax.validation.Constraint; +import javax.validation.Payload; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * 自定义xss校验注解 + * + * @author ruoyi + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(value = { ElementType.METHOD, ElementType.FIELD, ElementType.CONSTRUCTOR, ElementType.PARAMETER }) +@Constraint(validatedBy = { XssValidator.class }) +public @interface Xss { + + String message() + + default "不允许任何脚本运行"; + + Class[] groups() default {}; + + Class[] payload() default {}; +} diff --git a/src/main/java/com/jxy/windminer/common/xss/XssValidator.java b/src/main/java/com/jxy/windminer/common/xss/XssValidator.java new file mode 100644 index 0000000..c24fad9 --- /dev/null +++ b/src/main/java/com/jxy/windminer/common/xss/XssValidator.java @@ -0,0 +1,39 @@ +package com.jxy.windminer.common.xss; + +import com.jxy.windminer.common.utils.StringUtils; + +import javax.validation.ConstraintValidator; +import javax.validation.ConstraintValidatorContext; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * @Description TODO + * @Date 2024/5/30 11:29 + * @Author 杜懿 + */ +public class XssValidator implements ConstraintValidator { + private static final String HTML_PATTERN = "<(\\S*?)[^>]*>.*?|<.*? />"; + + @Override + public boolean isValid(String value, ConstraintValidatorContext constraintValidatorContext) + { + if (StringUtils.isBlank(value)) + { + return true; + } + return !containsHtml(value); + } + + public static boolean containsHtml(String value) + { + StringBuilder sHtml = new StringBuilder(); + Pattern pattern = Pattern.compile(HTML_PATTERN); + Matcher matcher = pattern.matcher(value); + while (matcher.find()) + { + sHtml.append(matcher.group()); + } + return pattern.matcher(sHtml).matches(); + } +} diff --git a/src/main/java/com/jxy/windminer/config/ResourcesConfig.java b/src/main/java/com/jxy/windminer/config/ResourcesConfig.java new file mode 100644 index 0000000..d926eb9 --- /dev/null +++ b/src/main/java/com/jxy/windminer/config/ResourcesConfig.java @@ -0,0 +1,52 @@ +package com.jxy.windminer.config; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.servlet.config.annotation.CorsRegistry; +import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +import java.io.File; + +/** + * 通用映射配置 + * + * @author jxy + */ +@Configuration +public class ResourcesConfig implements WebMvcConfigurer +{ + /** + * 上传文件存储在本地的根路径 + */ + //@Value("D:/marketall/uploadPath") + @Value("/home/windminer/ticket") + private String localFilePath; + + /** + * 资源映射路径 前缀 + */ + @Value("/statics") + public String localFilePrefix; + + @Override + public void addResourceHandlers(ResourceHandlerRegistry registry) + { + /** 本地文件上传路径 */ + registry.addResourceHandler(localFilePrefix + "/**") + .addResourceLocations("file:" + localFilePath + File.separator); + } + + /** + * 开启跨域 + */ + @Override + public void addCorsMappings(CorsRegistry registry) { + // 设置允许跨域的路由 + registry.addMapping(localFilePrefix + "/**") + // 设置允许跨域请求的域名 + .allowedOrigins("*") + // 设置允许的方法 + .allowedMethods("GET"); + } +} diff --git a/src/main/java/com/jxy/windminer/controller/OrderController.java b/src/main/java/com/jxy/windminer/controller/OrderController.java new file mode 100644 index 0000000..b48f02e --- /dev/null +++ b/src/main/java/com/jxy/windminer/controller/OrderController.java @@ -0,0 +1,137 @@ +package com.jxy.windminer.controller; + +import com.github.pagehelper.PageHelper; +import com.jxy.windminer.common.security.annotation.RequiresLogin; +import com.jxy.windminer.common.security.annotation.RequiresRoles; +import com.jxy.windminer.common.web.Result.AjaxResult; +import com.jxy.windminer.common.web.controller.BaseController; +import com.jxy.windminer.common.web.page.TableDataInfo; +import com.jxy.windminer.dto.*; +import com.jxy.windminer.mapper.OrderMapper; +import com.jxy.windminer.service.OrderService; +import com.jxy.windminer.vo.*; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; +import java.util.List; + + +/** + * 个人信息 业务处理 + * + * @author jxy + */ +@RestController +@RequestMapping("/order") +public class OrderController extends BaseController +{ + @Autowired + private OrderService orderService; + + @Autowired + private OrderMapper orderMapper; + + /** 确认支付 */ + @PostMapping("/endOrder") + public AjaxResult handleOrder(@RequestBody OrderHandleVo vo){ + return orderService.handleOrder(vo); + } + + //@PostMapping("/test") + //public AjaxResult test(@RequestBody OrderHandleVo vo){ + // OnChainQueryResultDto dto = new OnChainQueryResultDto(); + // dto.setResult(true); + // Map map = new HashMap<>(); + // + // OrderInfoDto info = orderMapper.selectOrderInfoByOrderKey(vo.getOrderKey()); + // + // OrderUpdateVo updateVo = new OrderUpdateVo(); + // updateVo.setAmount(info.getAmount()+0.1); + // updateVo.setOrderKey(vo.getOrderKey()); + // updateVo.setCurrency(info.getCurrency()); + // updateVo.setChain(vo.getChain()); + // orderMapper.updateOrderInfoPaymentByOrderKey(updateVo); + // //获取更新后的info + // info = orderMapper.selectOrderInfoByOrderKey(vo.getOrderKey()); + // //回调 + // map.put("ext_transaction_id","test"); + // map.put("transaction_id",info.getTransactionId()); + // map.put("status_code",200); + // map.put("note","success"); + // map.put("confirm_rcv_amnt",info.getAmount()); + // map.put("confirm_rcv_amnt_curr",info.getCurrency()); + // map.put("coin_rcv_amnt",info.getAmount()); + // map.put("coin_rcv_amnt_curr",info.getCurrency()); + // map.put("txn_time", DateUtils.dateTime()); + // map.put("order_id",info.getOrderId()); + // HttpRequest post = HttpUtil.createPost(info.getNotifyUrl()); + // String body = post.contentType("application/x-www-form-urlencoded").form(map).timeout(20000).execute().body(); + // AjaxResult success = AjaxResult.success(map); + // success.put("postResult",body); + // return success; + //} + + @PostMapping("/getPayAddress") + public AjaxResult getPayAddress(@RequestBody AddressVo vo){ + return orderService.getPayAddressByUserEmailAndType(vo); + } + + /** 接收订单参数 记录订单相关信息 */ + @PostMapping("/payment") + public AjaxResult payment(@RequestBody OrderApplyVo vo){ + return orderService.getPaymentOrderInfo(vo); + } + + /** 根据订单id获取订单信息 */ + @PostMapping("/getPayment") + public AjaxResult getPayment(@RequestBody OrderKeyVo vo){ + return orderService.getOrderInfoByOrderKey(vo); + } + + /** 获取未使用地址数量 */ + @PostMapping("/address/surplus") + public AjaxResult getSurplusAddressCount() { + return orderService.getAddressSurplus(); + } + + /** 查看已使用地址列表 */ + @PostMapping("/address/list") + @RequiresLogin + @RequiresRoles("accounting") + public TableDataInfo getPaymentAddressList(@RequestBody() AddressUsedVo vo){ + + + PageHelper.startPage(vo.getPage(),vo.getLimit()); + List list =orderService.getUesdAddressList(vo); + return getDataTable(list); + } + + /** 查看所有地址列表 */ + @PostMapping("/address/total") + @RequiresLogin + @RequiresRoles("accounting") + public TableDataInfo getTotalAddressList(@RequestBody() TotalAddressVo vo){ + + PageHelper.startPage(vo.getPage(),vo.getLimit()); + List list =orderService.getTotalAddressList(vo); + return getDataTable(list); + } + + /** 查看已成功收款地址详情 */ + @PostMapping("/address/detail") + @RequiresLogin + @RequiresRoles("accounting") + public AjaxResult getOrderDetails(@RequestBody() OrderKeyVo vo) { + System.out.println(vo); + return orderService.getOrderDetails(vo); + } + + /** 确认发货 */ + @PostMapping("/logistics/confirm") + @RequiresLogin + @RequiresRoles("accounting") + public AjaxResult logisticsConfirm(@RequestBody() OrderLogisticsConfirmVo vo) { + return orderService.logisticsConfirm(vo); + } + + +} diff --git a/src/main/java/com/jxy/windminer/controller/SysFileController.java b/src/main/java/com/jxy/windminer/controller/SysFileController.java new file mode 100644 index 0000000..4cbd501 --- /dev/null +++ b/src/main/java/com/jxy/windminer/controller/SysFileController.java @@ -0,0 +1,147 @@ +package com.jxy.windminer.controller; + +import com.github.pagehelper.PageHelper; +import com.jxy.windminer.common.security.annotation.Logical; +import com.jxy.windminer.common.security.annotation.RequiresLogin; +import com.jxy.windminer.common.security.annotation.RequiresRoles; +import com.jxy.windminer.common.security.utils.SecurityUtils; +import com.jxy.windminer.common.utils.StringUtils; +import com.jxy.windminer.common.utils.poi.ExcelUtil; +import com.jxy.windminer.common.web.Result.AjaxResult; +import com.jxy.windminer.common.web.controller.BaseController; +import com.jxy.windminer.common.web.page.TableDataInfo; +import com.jxy.windminer.dto.IdsDto; +import com.jxy.windminer.dto.RepairRecordDto; +import com.jxy.windminer.dto.SysToolsDto; +import com.jxy.windminer.entity.RepairRecord; +import com.jxy.windminer.service.SysFileService; +import com.jxy.windminer.vo.AddRepairRecordVo; +import com.jxy.windminer.vo.RepairRecordVo; +import com.jxy.windminer.vo.ReturnLogisticalVo; +import com.jxy.windminer.vo.SysToolsVo; +import io.swagger.annotations.ApiOperation; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.validation.Valid; +import java.io.IOException; +import java.sql.SQLException; +import java.util.List; + + +/** + * 个人信息 业务处理 + * + * @author jxy + */ +@RestController +@RequestMapping("/sys") +public class SysFileController extends BaseController +{ + @Autowired + private SysFileService fileService; + + + + @PostMapping("/importData") + @ApiOperation(value = "导入维修记录excel") + @RequiresLogin + @RequiresRoles(value = {"admin","maintainer"}, logical = Logical.OR) + public AjaxResult importData(MultipartFile file, boolean updateSupport) throws Exception + { + ExcelUtil util = new ExcelUtil(RepairRecord.class); + List list = util.importExcel(file.getInputStream()); + String operName = SecurityUtils.getUsername(); + String message = fileService.importRepairRecord(list, updateSupport, operName); + return success(message); + } + + @PostMapping("/importTemplate") + public void importTemplate(HttpServletResponse response) throws IOException + { + ExcelUtil util = new ExcelUtil(RepairRecord.class); + util.importTemplateExcel(response, "维修记录"); + } + + @PostMapping("/getRepairRecord") + @ApiOperation(value = "条件搜索维修记录") + @RequiresLogin + @RequiresRoles(value = {"admin","maintainer","support","accounting"}, logical = Logical.OR) + public TableDataInfo getRepairRecord(@RequestBody(required = false) RepairRecordVo vo) + { + if(StringUtils.isNull(vo)){ + vo = new RepairRecordVo(); + } + PageHelper.startPage(vo.getPage(),vo.getLimit()); + List list = fileService.getRepairRecord(vo); + return getDataTable(list); + } + + //上传使用手册等系统基础文件 + @PostMapping("/uploadFile") + @ApiOperation(value = "文件上传") + @RequiresLogin + @RequiresRoles(value = {"admin","maintainer","support"}, logical = Logical.OR) + public AjaxResult uploadFile(@RequestParam("file") MultipartFile file,String ver,String md5sum){ + if(StringUtils.isBlank(StringUtils.clean(ver))){ + return AjaxResult.error("版本号不能为空"); + } + try { + return fileService.uploadFile(file,ver.toUpperCase(),md5sum); + } catch (SQLException e) { + e.printStackTrace(); + return AjaxResult.error("服务器异常,请联系管理员"); + } + + } + + //下载基础文件 + @GetMapping("/downloadFile") + @ApiOperation(value = "文件下载") + public void download(int id, HttpServletRequest request, HttpServletResponse response) + throws Exception{ + + fileService.downloadById(id,request,response); + + } + //查询基础文件列表 + @PostMapping("/getTools") + @ApiOperation(value = "获取系统使用工具列表") + //@RequiresLogin + //@RequiresRoles(value = {"admin","maintainer","support","accounting"}, logical = Logical.OR) + public TableDataInfo getTools(@RequestBody(required = false) SysToolsVo vo) + { + if(StringUtils.isNull(vo)){ + vo = new SysToolsVo(); + } + PageHelper.startPage(vo.getPage(),vo.getLimit()); + List list = fileService.getToolsList(vo); + return getDataTable(list); + } + + @PostMapping("/addRecord") + @ApiOperation(value = "提交维修记录") + @RequiresLogin + @RequiresRoles(value = {"admin","maintainer"}, logical = Logical.OR) + public AjaxResult addRecord(@Valid() @RequestBody AddRepairRecordVo vo){ + if(StringUtils.isNull(vo)){ + return AjaxResult.error("参数不能为空"); + } + return fileService.addReparRecord(vo); + } + + @DeleteMapping("/{id}") + @ApiOperation(value = "删除指定工具 文件") + @RequiresLogin + @RequiresRoles(value = {"admin"}, logical = Logical.OR) + public AjaxResult delete(@PathVariable int id){ + System.out.println(id); + if(StringUtils.isNull(id) || id < 1){ + return AjaxResult.error("参数不能为空"); + } + return fileService.deleteToolById(id); + } +} diff --git a/src/main/java/com/jxy/windminer/controller/SysProfileController.java b/src/main/java/com/jxy/windminer/controller/SysProfileController.java new file mode 100644 index 0000000..c78a678 --- /dev/null +++ b/src/main/java/com/jxy/windminer/controller/SysProfileController.java @@ -0,0 +1,151 @@ +package com.jxy.windminer.controller; + +import com.jxy.windminer.common.Result.R; +import com.jxy.windminer.common.constant.UserConstants; +import com.jxy.windminer.common.security.annotation.RequiresLogin; +import com.jxy.windminer.common.security.service.TokenService; +import com.jxy.windminer.common.security.utils.SecurityUtils; +import com.jxy.windminer.common.utils.StringUtils; +import com.jxy.windminer.common.web.Result.AjaxResult; +import com.jxy.windminer.common.web.controller.BaseController; +import com.jxy.windminer.entity.auth.LoginUser; +import com.jxy.windminer.entity.system.SysRole; +import com.jxy.windminer.entity.system.SysRoleDto; +import com.jxy.windminer.entity.system.SysUser; +import com.jxy.windminer.entity.system.SysUserDto; +import com.jxy.windminer.mapper.SysUserRoleMapper; +import com.jxy.windminer.service.SysRoleService; +import com.jxy.windminer.service.SysUserService; +import io.swagger.annotations.ApiOperation; +import org.springframework.beans.BeanUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + + +/** + * 个人信息 业务处理 + * + * @author jxy + */ +@RestController +@RequestMapping("/user/profile") +public class SysProfileController extends BaseController +{ + @Autowired + private SysUserService userService; + + + @Autowired + private TokenService tokenService; + + /** + * 个人信息 + */ + @PostMapping + public AjaxResult profile() + { + //String username = SecurityUtils.getUsername(); + //SysUser user = userService.selectUserByUserName(username); + String email = SecurityUtils.getEmail(); + SysUser user = userService.selectUserByEmail(email); + System.out.println(user); + + if(StringUtils.isNull(user)){ + return AjaxResult.error("服务器繁忙,资源请求失败!"); + } + + //隐藏不需要返回前端的信息 + SysUserDto userDto = new SysUserDto(); + BeanUtils.copyProperties(user,userDto); + + SysRole sysRole = user.getRoles().get(0); + + SysRoleDto roleDto = new SysRoleDto(); + BeanUtils.copyProperties(sysRole,roleDto); + + userDto.setRole(roleDto); + + return AjaxResult.success(userDto); + } + + + /** + * 修改用户 + */ + @PutMapping + @ApiOperation(value = "个人信息") + @RequiresLogin + public AjaxResult updateProfile(@RequestBody SysUser user) + { + LoginUser loginUser = SecurityUtils.getLoginUser(); + SysUser sysUser = loginUser.getSysUser(); + user.setUserName(sysUser.getUserName()); + if (StringUtils.isNotEmpty(user.getPhone()) + && UserConstants.NOT_UNIQUE.equals(userService.checkPhoneUnique(user))) + { + return AjaxResult.error("修改用户'" + user.getUserName() + "'失败,手机号码已存在"); + } + else if (StringUtils.isNotEmpty(user.getEmail()) + && UserConstants.NOT_UNIQUE.equals(userService.checkEmailUnique(user))) + { + return AjaxResult.error("修改用户'" + user.getUserName() + "'失败,邮箱账号已存在"); + } + user.setUserId(sysUser.getUserId()); + user.setPassword(null); + if (userService.updateUserProfile(user) > 0) + { + // 更新缓存用户信息 + loginUser.getSysUser().setNickName(user.getNickName()); + loginUser.getSysUser().setPhone(user.getPhone()); + loginUser.getSysUser().setEmail(user.getEmail()); + loginUser.getSysUser().setSex(user.getSex()); + tokenService.setLoginUser(loginUser); + return AjaxResult.success(); + } + return AjaxResult.error("修改个人信息异常,请联系管理员"); + } + + /** + * 重置密码(已知原密码修改为新密码) + */ + @ApiOperation(value = "重置密码") + @PutMapping("/updatePwd") + public AjaxResult updatePwd(String oldPassword, String newPassword) + { + String username = SecurityUtils.getUsername(); + SysUser user = userService.selectUserByUserName(username); + String password = user.getPassword(); + if (!SecurityUtils.matchesPassword(oldPassword, password)) + { + return AjaxResult.error("修改密码失败,旧密码错误"); + } + if (SecurityUtils.matchesPassword(newPassword, password)) + { + return AjaxResult.error("新密码不能与旧密码相同"); + } + if (userService. resetUserPwd(username, SecurityUtils.encryptPassword(newPassword)) > 0) + { + // 更新缓存用户密码 + LoginUser loginUser = SecurityUtils.getLoginUser(); + loginUser.getSysUser().setPassword(SecurityUtils.encryptPassword(newPassword)); + tokenService.setLoginUser(loginUser); + return AjaxResult.success(); + } + return AjaxResult.error("修改密码异常,请联系管理员"); + } + + /** + * 重置密码(忘记原密码通过邮箱验证修改为新密码) + */ + @ApiOperation(value = "重置密码") + @PostMapping("/resetPwd") + public AjaxResult resetPwd(@RequestBody SysUser user){ + if(userService.resetPwdByEmail(user.getEmail(),user.getPassword()) > 0){ + // + return AjaxResult.success(); + } + return AjaxResult.error("修改密码异常,请联系管理员"); + } + + +} diff --git a/src/main/java/com/jxy/windminer/controller/TicketController.java b/src/main/java/com/jxy/windminer/controller/TicketController.java new file mode 100644 index 0000000..6d63d4b --- /dev/null +++ b/src/main/java/com/jxy/windminer/controller/TicketController.java @@ -0,0 +1,418 @@ +package com.jxy.windminer.controller; + +import com.github.pagehelper.PageHelper; +import com.jxy.windminer.common.security.annotation.Logical; +import com.jxy.windminer.common.security.annotation.RequiresLogin; +import com.jxy.windminer.common.security.annotation.RequiresRoles; +import com.jxy.windminer.common.text.Convert; +import com.jxy.windminer.common.utils.StringUtils; +import com.jxy.windminer.common.web.Result.AjaxResult; +import com.jxy.windminer.common.web.controller.BaseController; +import com.jxy.windminer.common.web.page.TableDataInfo; +import com.jxy.windminer.dto.IdsDto; +import com.jxy.windminer.dto.TicketAcListDto; +import com.jxy.windminer.dto.TicketListDto; +import com.jxy.windminer.service.TicketService; +import com.jxy.windminer.vo.*; +import io.swagger.annotations.ApiOperation; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; +import java.util.regex.Pattern; + +/** + * @Description 统一请求接口 + * @Date 2022/5/27 9:54 + * @Author 杜懿 + */ +@RestController +@RequestMapping("/ticket") +public class TicketController extends BaseController { + + @Autowired + private TicketService ticketService; + + + @PostMapping("/uploadFile") + @ApiOperation(value = "文件上传") + @RequiresLogin + public AjaxResult uploadFile(@RequestParam("file") MultipartFile[] file){ + try { + return ticketService.uploadFiles(file); + } catch (SQLException e) { + e.printStackTrace(); + return AjaxResult.error("服务器异常,请联系管理员"); + } + + } + + @GetMapping("/downloadFile") + @ApiOperation(value = "文件批量下载") + public void download(IdsDto dto, HttpServletRequest request, HttpServletResponse response){ + + ticketService.downloadByIds(dto.getIds(),request,response); + + } + + @PostMapping("/getFaults") + @ApiOperation(value = "获取故障现象下拉列表") + public AjaxResult getFaults(){ + return ticketService.getFaults(); + + } + + @PostMapping("/getGuarantee") + @ApiOperation(value = "根据机器序列号获取保修到期日期") + @RequiresLogin + public AjaxResult getGuarantee(@RequestBody SerialVo vo){ + return ticketService.getGuaranteeBySerial(vo); + } + + @PostMapping("/checkSerialNo") + @ApiOperation(value = "根据机器序列号查询是否机器编号存在") + @RequiresLogin + public AjaxResult checkSerialNo(@RequestBody SerialVo vo){ + return ticketService.checkSerialNo(vo); + } + + @PostMapping("/submitTicket") + @ApiOperation(value = "提交工单") + @RequiresLogin + public AjaxResult submitTicket(@Validated @RequestBody(required = false) TicketVo vo){ + if(StringUtils.isNull(vo)){vo = new TicketVo();} + return ticketService.submitTicket(vo); + + } + + @PostMapping("/resubmitTicket") + @ApiOperation(value = "工单内容补充工单") + @RequiresLogin + @RequiresRoles(value = {"admin", "registered"}, logical = Logical.OR) + public AjaxResult resubmitTicket(@RequestBody ResubmitTicketVo vo){ + return ticketService.resubmitTicket(vo); + } + + @PostMapping("/changeTicketType") + @ApiOperation(value = "用户确认之后修改工单类型") + //@RequiresLogin + public AjaxResult changeTicketType(@RequestBody ChangeTypeVo vo){ + return ticketService.changeTicketType(vo); + } + + @PostMapping("/mailRefusePay") + @ApiOperation(value = "通过邮箱点击的链接把工单状态设为已完结") + @RequiresLogin + public AjaxResult mailEndTicket(@RequestBody IdVo vo){ + System.out.println(vo); + return ticketService.mailEndTicket(vo.getId(),vo.getEmail()); + } + + @PostMapping({"/getPrivateTicket","privateConsume"}) + @ApiOperation(value = "个人工单列表") + @RequiresLogin + @RequiresRoles(value = {"admin", "registered"}, logical = Logical.OR) + public AjaxResult getPrivateTicket(@RequestBody(required = false) PrivateTicketListVo vo){ + if(StringUtils.isNull(vo)){ + vo = new PrivateTicketListVo(); + vo.setStatus(0); + } + return ticketService.getPrivateTicketByStatus(vo); + } + + @PostMapping("/privateTicketDetails") + @ApiOperation(value = "个人工单详情") + @RequiresLogin + public AjaxResult getPrivateTicketDetails(@RequestBody IdVo vo){ + return ticketService.getPrivateTicketDetails(vo.getId()); + } + + @PostMapping("/endTicket") + @ApiOperation(value = "用户关闭工单") + @RequiresLogin + @RequiresRoles(value = {"admin", "registered"}, logical = Logical.OR) + public AjaxResult endTicket(@RequestBody(required = false) EndTicketVo vo){ + + return ticketService.endTicket(vo); + } + + @PostMapping("/baseRepairInfo") + @ApiOperation(value = "用户获取维修相关信息") + @RequiresLogin + @RequiresRoles(value = {"registered","admin "}, logical = Logical.OR) + public AjaxResult baseRepairInfo(){ + + return ticketService.getBaseRepairInfo(); + } + + @PostMapping("/repairLogistical") + @ApiOperation(value = "用户填写寄件物流信息") + @RequiresLogin + @RequiresRoles(value = {"registered","admin "}, logical = Logical.OR) + public AjaxResult repairLogistical(@Validated @RequestBody UserLogisticalVo vo){ + return ticketService.addRepairLogisticalInfo(vo); + } + + @PostMapping("/payment") + @ApiOperation(value = "用户 获取支付相关信息") + @RequiresLogin + @RequiresRoles(value = {"admin", "registered"}, logical = Logical.OR) + public AjaxResult ticketPayment(@RequestBody() IdVo vo){ + + return ticketService.getTicketPaymentOrderInfo(vo); + } + + + //@PostMapping("/returnLogistical") + //@ApiOperation(value = "用户填写收件物流信息") + //@RequiresLogin + //@RequiresRoles(value = {"registered","admin "}, logical = Logical.OR) + //public AjaxResult returnLogistical(@RequestBody ReturnLogisticalVo vo){ + // return ticketService.addReturnLogisticalInfo(vo); + //} +/********************* admin相关接口 *************************/ + //@PostMapping("/getTicketList") + //@ApiOperation(value = "按状态搜索后台工单列表") + //@RequiresLogin + //@RequiresRoles(value = {"admin", "accounting","support","maintainer"}, logical = Logical.OR) + //public TableDataInfo getTicketList(@RequestBody StatusVo vo){ + // PageHelper.startPage(vo.getPage(),vo.getLimit()); + // List list = ticketService.getTicketList(vo.getStatus()); + // return getDataTable(list); + //} + + @PostMapping("/getAllTicket") + @ApiOperation(value = "条件搜索后台所有工单列表") + @RequiresLogin + @RequiresRoles("admin") + public TableDataInfo getAllTicket(@RequestBody AllTicketListVo vo){ + System.out.println("vo"+vo); + if(StringUtils.isNotBlank(vo.getCond())){ + System.out.println("不为空"); + Pattern pattern = Pattern.compile("^\\w+([-+.]\\w+)*@\\w+([-.]\\w+)*\\.\\w+([-.]\\w+)*$"); + if(pattern.matcher(vo.getCond()).matches()){ + //验证是否是邮箱 + System.out.println("邮箱"+vo.getCond()); + vo.setEmail(vo.getCond()); + }else { + //判断是否是id + Long id = Convert.toLong(vo.getCond(), 0L); + System.out.println(id); + if(id > 0){ + System.out.println("工单id:"+vo.getCond()); + vo.setId(id); + }else { + return getDataTable(new ArrayList<>()); + } + } + } + + PageHelper.startPage(vo.getPage(),vo.getLimit()); + return getDataTable(ticketService.getAllTicket(vo)) ; + } + + /********************* 线上技术支持人员相关接口 *************************/ + + @PostMapping("/online/list") + @ApiOperation(value = "根据状态获取type为线上(type=0)的工单") + @RequiresLogin + @RequiresRoles(value = {"admin","support","maintainer"}, logical = Logical.OR) + public TableDataInfo onlineTicketList(@RequestBody StatusVo vo){ + + PageHelper.startPage(vo.getPage(),vo.getLimit()); + List list = ticketService.getSupportTicketList(vo); + return getDataTable(list); + } + + @PostMapping("/online/details") + @ApiOperation(value = "工单详情") + @RequiresLogin + @RequiresRoles(value = {"admin","support","maintainer"}, logical = Logical.OR) + public AjaxResult onlineTicketDetails(@RequestBody IdVo vo){ + if(StringUtils.isNull(vo)){ + return AjaxResult.error("工单id缺失"); + } + return ticketService.getTicketDetails(vo.getId()); + } + + @PostMapping("/online/respon") + @ApiOperation(value = "回复工单") + @RequiresLogin + @RequiresRoles(value = {"admin","support","maintainer"}, logical = Logical.OR) + public AjaxResult onlineResponTicket(@RequestBody ResponTicketVo vo){ + return ticketService.onlineResponTicket(vo); + } + + @PostMapping("/online/changeType") + @ApiOperation(value = " 技术支持工单转线下工单") + @RequiresLogin + @RequiresRoles(value = {"admin","support","maintainer"}, logical = Logical.OR) + public AjaxResult onlineChangeType(@RequestBody ConfirmTypeVo vo){ + return ticketService.onlineChangeType(vo); + } + + /********************* 维修人员相关接口 *************************/ + + @PostMapping("/repair/list") + @ApiOperation(value = "根据状态获取type为线上(type=1)的工单") + @RequiresLogin + @RequiresRoles(value = {"admin","maintainer","support"}, logical = Logical.OR) + public TableDataInfo repairTicketList(@RequestBody StatusVo vo){ + + PageHelper.startPage(vo.getPage(),vo.getLimit()); + List list = ticketService.getRepairTicketList(vo); + return getDataTable(list); + } + + @PostMapping("/repair/respon") + @ApiOperation(value = "回复线下维修工单") + @RequiresLogin + @RequiresRoles(value = {"admin","maintainer","support"}, logical = Logical.OR) + public AjaxResult repairResponTicket(@RequestBody ResponTicketVo vo){ + return ticketService.RepairResponTicket(vo); + } + + @PostMapping("/repair/details") + @ApiOperation(value = "工单详情 待处理") + @RequiresLogin + @RequiresRoles(value = {"admin","maintainer","support"}, logical = Logical.OR) + public AjaxResult repairTicketDetails(@RequestBody IdVo vo){ + if(StringUtils.isNull(vo)){ + return AjaxResult.error("工单id缺失"); + } + return ticketService.getTicketDetails3(vo.getId()); + } + + //@PostMapping("/repair/details3") + //@ApiOperation(value = "工单详情 状态:等待收货中和收货中") + //@RequiresLogin + //@RequiresRoles(value = {"admin","maintainer"}, logical = Logical.OR) + //public AjaxResult repairTicketDetails3(@RequestBody IdVo vo){ + // if(StringUtils.isNull(vo)){ + // return AjaxResult.error("工单id缺失"); + // } + // return ticketService.getTicketDetails3(vo.getId()); + //} + + @PostMapping("/repair/confirm") + @ApiOperation(value = "维修工单确认 并填写物流信息") + @RequiresLogin + @RequiresRoles(value = {"admin","maintainer","support"}, logical = Logical.OR) + public AjaxResult repairTicketConfirm(@RequestBody RepairConfirmVo vo){ + if(StringUtils.isNull(vo)){ + return AjaxResult.error("工单id缺失"); + } + return ticketService.repairConfirmTicket(vo); + } + + @PostMapping("/repair/refuse") + @ApiOperation(value = "维修工单 拒绝维修") + @RequiresLogin + @RequiresRoles(value = {"admin","maintainer","support"}, logical = Logical.OR) + public AjaxResult repairTicketRefuse(@RequestBody RepairRefuseVo vo){ + if(StringUtils.isNull(vo)){ + return AjaxResult.error("传参异常"); + } + return ticketService.repairRefuseTicket(vo); + } + + @PostMapping("/repair/changeType") + @ApiOperation(value = " 线下工单转技术支持工单") + @RequiresLogin + @RequiresRoles(value = {"admin","maintainer","support"}, logical = Logical.OR) + public AjaxResult repairChangeType(@RequestBody ConfirmTypeVo vo){ + return ticketService.repairChangeType(vo); + } + + @PostMapping("/repair/receipt") + @ApiOperation(value = " 确认收货") + @RequiresLogin + @RequiresRoles(value = {"admin","maintainer","support"}, logical = Logical.OR) + public AjaxResult receipt(@RequestBody ConfirmTypeVo vo){ + return ticketService.receipt(vo); + } + + + @PostMapping("/repair/quotedPrice") + @ApiOperation(value = "维修工单添加维修价格") + @RequiresLogin + @RequiresRoles(value = {"admin","maintainer","support"}, logical = Logical.OR) + public AjaxResult repairTicketAddPrice(@RequestBody QuotedVo vo){ + return ticketService.repairTicketAddPrice(vo); + } + + @PostMapping("/repair/changeStatus") + @ApiOperation(value = "工单状态为6已付款/7正在维修的工单修改状态") + @RequiresLogin + @RequiresRoles(value = {"admin","maintainer","support"}, logical = Logical.OR) + public AjaxResult repairTicketAddPrice(@RequestBody RepairChangeStatusVo vo){ + return ticketService.changeRepairTicketStatus(vo); + } + + @PostMapping("/repair/returnDetails") + @ApiOperation(value = "回退页面基本信息详情") + @RequiresLogin + @RequiresRoles(value = {"admin","maintainer","support"}, logical = Logical.OR) + public AjaxResult returnDetails(@RequestBody IdVo vo){ + if(StringUtils.isNull(vo)){ + return AjaxResult.error("工单id缺失"); + } + return ticketService.getReturnDetails(vo.getId()); + } + + @PostMapping("/repair/returnLoginInfor") + @ApiOperation(value = "回退物流信息填写") + @RequiresLogin + @RequiresRoles(value = {"admin","maintainer","support"}, logical = Logical.OR) + public AjaxResult returnLoginInfor(@RequestBody ReturnLogisticalVo vo){ + if(StringUtils.isNull(vo)){ + return AjaxResult.error("工单id缺失"); + } + return ticketService.addReturnLogistical(vo); + } + + + /********************* 财务人员相关接口 *************************/ + + @PostMapping("/ac/list") + @ApiOperation(value = "根据状态获取type为线上(type=1)的工单") + @RequiresLogin + @RequiresRoles(value = {"admin","accounting"}, logical = Logical.OR) + public TableDataInfo accountTicketList(@RequestBody StatusVo vo){ + + PageHelper.startPage(vo.getPage(),vo.getLimit()); + List list = ticketService.getAccountTicketList(vo); + return getDataTable(list); + } + + @PostMapping("/ac/details") + @ApiOperation(value = "工单详情 ") + @RequiresLogin + @RequiresRoles(value = {"admin","accounting"}, logical = Logical.OR) + public AjaxResult accountTicketDetails(@RequestBody IdVo vo){ + if(StringUtils.isNull(vo)){ + return AjaxResult.error("工单id缺失"); + } + return ticketService.getAccountTicketDetails(vo.getId()); + } + + @PostMapping("/ac/noticeUser") + @ApiOperation(value = "通知用户未付款 ") + @RequiresLogin + @RequiresRoles(value = {"admin","accounting"}, logical = Logical.OR) + public AjaxResult noticeUser(@RequestBody IdVo vo){ + if(StringUtils.isNull(vo)){ + return AjaxResult.error("工单id缺失"); + } + return ticketService.noticeUserByTicketId(vo); + } + + + +} diff --git a/src/main/java/com/jxy/windminer/controller/TokenController.java b/src/main/java/com/jxy/windminer/controller/TokenController.java new file mode 100644 index 0000000..cb8c80f --- /dev/null +++ b/src/main/java/com/jxy/windminer/controller/TokenController.java @@ -0,0 +1,123 @@ +package com.jxy.windminer.controller; + +import com.jxy.windminer.common.Result.R; +import com.jxy.windminer.common.security.auth.AuthUtil; +import com.jxy.windminer.common.security.service.TokenService; +import com.jxy.windminer.common.security.utils.SecurityUtils; +import com.jxy.windminer.common.utils.JwtUtils; +import com.jxy.windminer.common.utils.StringUtils; +import com.jxy.windminer.entity.auth.*; +import com.jxy.windminer.service.SysLoginService; +import com.jxy.windminer.service.impl.MaliServiceImpl; +import io.swagger.annotations.Api; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import javax.servlet.http.HttpServletRequest; +import javax.validation.Valid; + + +/** + * @Description token 控制 + * @Date 2022/5/12 14:59 + * @Author 杜懿 + */ +@RestController +@Api(tags = "token令牌") +@RequestMapping("/auth") +public class TokenController { + + @Autowired + private SysLoginService sysLoginService; + + @Autowired + private TokenService tokenService; + + @Autowired + private MaliServiceImpl maliService; + + @PostMapping("login") + public R login(@RequestBody LoginBody loginBody) + { + return sysLoginService.login(loginBody); + } + + + @PostMapping("emailcode") + public R emailCode(@RequestBody GetEmailCodeEntity entity) + { + return maliService.emailCode(entity); + } + + @PostMapping("resetPwdcode") + public R resetPwdcode(@RequestBody GetEmailCodeEntity entity) + { + return maliService.resetPwdCode(entity); + } + + @GetMapping("hello") + public R hello() + { + maliService.sendHtmlMailMessage("1328642438@qq.com","回调成功","回调成功了"); + return R.success("浏览器安全认证已确认,请重新返回原页面进行登陆"); + } + + @DeleteMapping("logout") + public R logout(HttpServletRequest request) + { + String token = SecurityUtils.getToken(request); + if (StringUtils.isNotEmpty(token)) + { + String username = JwtUtils.getUserName(token); + //删除用户缓存记录 + AuthUtil.logoutByToken(token); + // 记录用户退出日志 + sysLoginService.logout(username); + } + return R.success("用户登出"); + } + + @PostMapping("refresh") + public R refresh(HttpServletRequest request) + { + LoginUser loginUser = tokenService.getLoginUser(request); + if (StringUtils.isNotNull(loginUser)){ + //刷新令牌有效期 + tokenService.refreshToken(loginUser); + return R.success(); + } + return R.success(); + } + + @PostMapping("register") + public R register(@Valid() @RequestBody RegisterBody registerBody) + { + // 用户注册 + sysLoginService.register(registerBody); + return R.success("用户"+registerBody.getUserName()+"注册成功"); + } + + @PostMapping("wpUserLogin") + public R wpUserLogin(@Valid() @RequestBody WpLoginUser wp) + { + // wordPress注册用户 登录工单系统 + return sysLoginService.wpUserLogin(wp); + } + + @PostMapping("resetPwd") + public R resetPwd(@Valid @RequestBody ResetPwdBody resetPwdBody) + { + // 重置密码 + sysLoginService.resetPwd(resetPwdBody); + return R.success("账号"+ resetPwdBody.getEmail()+"密码重置成功"); + } + + @PostMapping("sendTextMail") + public R sendTextMail(@Valid @RequestBody EmailEntity entity) + { + maliService.sendTextMailMessage(entity.getEmail(), entity.getSubject(),entity.getText()); + return R.success("邮件已发送"); + } + + +} diff --git a/src/main/java/com/jxy/windminer/dto/AccountDetailsDto.java b/src/main/java/com/jxy/windminer/dto/AccountDetailsDto.java new file mode 100644 index 0000000..acd111d --- /dev/null +++ b/src/main/java/com/jxy/windminer/dto/AccountDetailsDto.java @@ -0,0 +1,53 @@ +package com.jxy.windminer.dto; + +import lombok.Data; + +import java.util.Date; +import java.util.List; + +/** + * @Description TODO + * @Date 2022/6/20 17:44 + * @Author 杜懿 + */ +@Data +public class AccountDetailsDto { + + private int id; + + private String email; + + private Date buyDate; + + private String distributor; + + private String fault; + + private String model; + + private String serialNo; + + private Date createTime; + + private String status; + + private String type; + + /** 应收金额 */ + private double receivable; + + /** 用户选择的支付链 */ + private String chain; + + private String txId; + + /** 实际收款地址 */ + private String balance; + + /** 实付金额 */ + private double actual; + + /** 支付状态 */ + private String paymentStatus; + +} diff --git a/src/main/java/com/jxy/windminer/dto/AddressOrderDetailDto.java b/src/main/java/com/jxy/windminer/dto/AddressOrderDetailDto.java new file mode 100644 index 0000000..c4cf35a --- /dev/null +++ b/src/main/java/com/jxy/windminer/dto/AddressOrderDetailDto.java @@ -0,0 +1,53 @@ +package com.jxy.windminer.dto; + +import lombok.Data; + +/** + * @Description TODO + * @Date 2022/6/20 17:44 + * @Author 杜懿 + */ +@Data +public class AddressOrderDetailDto { + + private int orderId; + /** 用户收件地址 **/ + private String userLogistics; + + /** 收件人姓名 **/ + private String username; + + private String email; + + /** 订单编号 **/ + private String phone; + + /** 详情 **/ + private String details; + + /** 寄出物流:公司名 **/ + private String logisticId; + /** 寄出物流:物流单号 **/ + private String logisticNo; + + private String pickupDate; + + private String pickupNumber; + + private String pickupName; + + private String idCard; + + private String pickupPhone; + + private String product; + + private int orderFrom; + + private int proNumber; + + private String ifSend; + + + +} diff --git a/src/main/java/com/jxy/windminer/dto/AddressUsedDto.java b/src/main/java/com/jxy/windminer/dto/AddressUsedDto.java new file mode 100644 index 0000000..4d20c5c --- /dev/null +++ b/src/main/java/com/jxy/windminer/dto/AddressUsedDto.java @@ -0,0 +1,40 @@ +package com.jxy.windminer.dto; + +import lombok.Data; + +/** + * @Description TODO + * @Date 2022/6/20 17:44 + * @Author 杜懿 + */ +@Data +public class AddressUsedDto { + + private String address; + + /** 订单编号 **/ + private String orderId; + + /** 订单key **/ + private String orderKey; + + private String email; + + private String amount; + + private String actual; + + private String date; + + private String orderFrom; + + private String ifSend; + + private String status; + + private String chain; + + private int number; + + +} diff --git a/src/main/java/com/jxy/windminer/dto/AllTicketListDto.java b/src/main/java/com/jxy/windminer/dto/AllTicketListDto.java new file mode 100644 index 0000000..0ceee8f --- /dev/null +++ b/src/main/java/com/jxy/windminer/dto/AllTicketListDto.java @@ -0,0 +1,43 @@ +package com.jxy.windminer.dto; + +import lombok.Data; + +import java.math.BigDecimal; +import java.util.Date; + +/** + * @Description TODO + * @Date 2022/6/20 17:44 + * @Author 杜懿 + */ +@Data +public class AllTicketListDto { + + private int id; + + private String type; + + private Date date; + + private String model; + + private String serialNo; + + private String fault; + + private String desc; + + //实收金额 + private BigDecimal actual; + + //应收金额 + private BigDecimal receivable; + + //支付状态 + private String paymentStatus; + + private String email; + + private String status; + +} diff --git a/src/main/java/com/jxy/windminer/dto/BaseRepairInfoDto.java b/src/main/java/com/jxy/windminer/dto/BaseRepairInfoDto.java new file mode 100644 index 0000000..26a8cb6 --- /dev/null +++ b/src/main/java/com/jxy/windminer/dto/BaseRepairInfoDto.java @@ -0,0 +1,25 @@ +package com.jxy.windminer.dto; + +import lombok.Data; + +import java.util.Date; +import java.util.List; + +/** + * @Description TODO + * @Date 2022/6/20 17:44 + * @Author 杜懿 + */ +@Data +public class BaseRepairInfoDto { + + + private String name; + + private String address; + + private String phone; + + private List list; + +} diff --git a/src/main/java/com/jxy/windminer/dto/BaseTicketDto.java b/src/main/java/com/jxy/windminer/dto/BaseTicketDto.java new file mode 100644 index 0000000..540005a --- /dev/null +++ b/src/main/java/com/jxy/windminer/dto/BaseTicketDto.java @@ -0,0 +1,31 @@ +package com.jxy.windminer.dto; + +import lombok.Data; + +import java.util.Date; + +/** + * @Description TODO + * @Date 2022/6/20 17:44 + * @Author 杜懿 + */ +@Data +public class BaseTicketDto { + + /** 工单id */ + private int ticketId; + + private String email; + + private String model; + + private String serialNo; + + private Date createTime; + + private String name; + + private String address; + + private String phone; +} diff --git a/src/main/java/com/jxy/windminer/dto/CallBackDto.java b/src/main/java/com/jxy/windminer/dto/CallBackDto.java new file mode 100644 index 0000000..d1f1228 --- /dev/null +++ b/src/main/java/com/jxy/windminer/dto/CallBackDto.java @@ -0,0 +1,31 @@ +package com.jxy.windminer.dto; + +import lombok.Data; + +import java.util.List; + + +/** + * @Description 充值订单入库数据 + * @Date 2023/5/18 14:16 + * @Author 杜懿 + */ +@Data +public class CallBackDto { + + + /** 是否查询到 */ + private Boolean result; + + /** 币种 */ + private String coin; + + /** 支付网络 */ + private String chain; + + /** */ + private List list; + + +} + diff --git a/src/main/java/com/jxy/windminer/dto/ConfirmListDto.java b/src/main/java/com/jxy/windminer/dto/ConfirmListDto.java new file mode 100644 index 0000000..2def240 --- /dev/null +++ b/src/main/java/com/jxy/windminer/dto/ConfirmListDto.java @@ -0,0 +1,32 @@ +package com.jxy.windminer.dto; + +import lombok.Data; + +import java.util.Date; +import java.util.List; + + +/** + * @Description 充值订单入库数据 + * @Date 2023/5/18 14:16 + * @Author 杜懿 + */ +@Data +public class ConfirmListDto { + + /** order唯一值 */ + private String orderKey; + + private String email; + + /** 订单提交日期 */ + private Date date; + + /** 订单状态 */ + private String status; + + /** 订单状态 */ + private String ifConfirm; + +} + diff --git a/src/main/java/com/jxy/windminer/dto/FaultsDto.java b/src/main/java/com/jxy/windminer/dto/FaultsDto.java new file mode 100644 index 0000000..cb4232c --- /dev/null +++ b/src/main/java/com/jxy/windminer/dto/FaultsDto.java @@ -0,0 +1,16 @@ +package com.jxy.windminer.dto; + +import lombok.Data; + +/** + * @Description TODO + * @Date 2023/3/20 15:10 + * @Author 杜懿 + */ +@Data +public class FaultsDto { + + private int id; + + private String fault; +} diff --git a/src/main/java/com/jxy/windminer/dto/FileDto.java b/src/main/java/com/jxy/windminer/dto/FileDto.java new file mode 100644 index 0000000..9160aa2 --- /dev/null +++ b/src/main/java/com/jxy/windminer/dto/FileDto.java @@ -0,0 +1,16 @@ +package com.jxy.windminer.dto; + +import lombok.Data; + +/** + * @Description TODO + * @Date 2023/3/20 15:10 + * @Author 杜懿 + */ +@Data +public class FileDto { + + private long id; + + private String url; +} diff --git a/src/main/java/com/jxy/windminer/dto/GuaranteeDto.java b/src/main/java/com/jxy/windminer/dto/GuaranteeDto.java new file mode 100644 index 0000000..4784b97 --- /dev/null +++ b/src/main/java/com/jxy/windminer/dto/GuaranteeDto.java @@ -0,0 +1,23 @@ +package com.jxy.windminer.dto; + +import lombok.Data; + +import java.io.Serializable; +import java.util.Date; + +/** + * @Description 返回数据实体 + * @Date 2022/5/30 11:17 + * @Author 杜懿 + */ +@Data +public class GuaranteeDto implements Serializable { + + private String serial; + + private String name; + + private Date productDate; + + private Date guaranteeDate; +} diff --git a/src/main/java/com/jxy/windminer/dto/IdsDto.java b/src/main/java/com/jxy/windminer/dto/IdsDto.java new file mode 100644 index 0000000..c2fe6d6 --- /dev/null +++ b/src/main/java/com/jxy/windminer/dto/IdsDto.java @@ -0,0 +1,15 @@ +package com.jxy.windminer.dto; + +import lombok.Data; + +import java.io.Serializable; + +/** + * @Description 返回数据实体 + * @Date 2022/5/30 11:17 + * @Author 杜懿 + */ +@Data +public class IdsDto implements Serializable { + private String ids; +} diff --git a/src/main/java/com/jxy/windminer/dto/OnChainMsgByAddressDto.java b/src/main/java/com/jxy/windminer/dto/OnChainMsgByAddressDto.java new file mode 100644 index 0000000..9e0022c --- /dev/null +++ b/src/main/java/com/jxy/windminer/dto/OnChainMsgByAddressDto.java @@ -0,0 +1,37 @@ +package com.jxy.windminer.dto; + +import com.alibaba.fastjson.annotation.JSONField; +import lombok.Data; + +import java.math.BigDecimal; +import java.util.Date; + + +/** + * @Description 充值订单入库数据 + * @Date 2023/5/18 14:16 + * @Author 杜懿 + */ +@Data +public class OnChainMsgByAddressDto { + + + /** 收款地址 */ + @JSONField(name = "to") + private String address; + + @JSONField(name = "from") + private String from; + + /** */ + private String txid; + + /** 金额 */ + private BigDecimal amount; + + + @JSONField(format = "dd/MM/yyyy HH:mm:ss",name = "timestamp") + private Date time; + +} + diff --git a/src/main/java/com/jxy/windminer/dto/OnChainQueryResultDto.java b/src/main/java/com/jxy/windminer/dto/OnChainQueryResultDto.java new file mode 100644 index 0000000..0934415 --- /dev/null +++ b/src/main/java/com/jxy/windminer/dto/OnChainQueryResultDto.java @@ -0,0 +1,31 @@ +package com.jxy.windminer.dto; + +import lombok.Data; + +import java.util.List; + + +/** + * @Description 充值订单入库数据 + * @Date 2023/5/18 14:16 + * @Author 杜懿 + */ +@Data +public class OnChainQueryResultDto { + + + /** 是否查询到 */ + private Boolean result; + + /** 币种 */ + private String coin; + + /** 支付网络 */ + private String chain; + + /** */ + private List list; + + +} + diff --git a/src/main/java/com/jxy/windminer/dto/OrderAndOrderLogisticalDto.java b/src/main/java/com/jxy/windminer/dto/OrderAndOrderLogisticalDto.java new file mode 100644 index 0000000..5c9bb3c --- /dev/null +++ b/src/main/java/com/jxy/windminer/dto/OrderAndOrderLogisticalDto.java @@ -0,0 +1,52 @@ +package com.jxy.windminer.dto; + +import lombok.Data; + +import java.math.BigDecimal; +import java.util.Date; + +/** + * @Description 订单传参 + * @Date 2022/8/1 18:44 + * @Author 杜懿 + */ +@Data +public class OrderAndOrderLogisticalDto { + + private int orderId; + + private String orderKey; + + private double amount; + + private String currency; + + private String billingFname; + + private String billingLname; + + private String email; + + //实际收款金额 + private BigDecimal confirmAmount; + //实际收款币种 + private String confirmCurrency; + + private String chain; + + //支付订单状态 + private int status; + + private String ifSend; + + private int orderFrom; + + /** 配送方式 */ + private String shipping; + + private String company; + + private String trackingNumber; + + private Date date; +} diff --git a/src/main/java/com/jxy/windminer/dto/OrderConfirmDto.java b/src/main/java/com/jxy/windminer/dto/OrderConfirmDto.java new file mode 100644 index 0000000..ab06249 --- /dev/null +++ b/src/main/java/com/jxy/windminer/dto/OrderConfirmDto.java @@ -0,0 +1,19 @@ +package com.jxy.windminer.dto; + +import lombok.Data; + +import java.util.Date; + +/** + * @Description 订单传参 + * @Date 2022/8/1 18:44 + * @Author 杜懿 + */ +@Data +public class OrderConfirmDto { + + private boolean ifConfirm; + + private String url; + +} diff --git a/src/main/java/com/jxy/windminer/dto/OrderInfoDto.java b/src/main/java/com/jxy/windminer/dto/OrderInfoDto.java new file mode 100644 index 0000000..1f8f45f --- /dev/null +++ b/src/main/java/com/jxy/windminer/dto/OrderInfoDto.java @@ -0,0 +1,64 @@ +package com.jxy.windminer.dto; + +import com.alibaba.fastjson.annotation.JSONField; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; + +import java.util.Date; + +/** + * @Description 订单传参 + * @Date 2022/8/1 18:44 + * @Author 杜懿 + */ +@Data +public class OrderInfoDto { + + private String transactionId; + + private int orderId; + + private String orderKey; + + private double amount; + + private String currency; + + private String billingFname; + + private String billingLname; + + private String email; + + private String redirectTo; + + private String cancelUrl; + + private String viewUrl; + + + private String type; + + /** 回调地址 */ + private String notifyUrl; + + //实际收款金额 + private double confirmAmount; + //实际收款币种 + private String confirmCurrency; + + private String chain; + + // + private int status; + + private String wpStatus; + + private int ifSend; + + private int orderFrom; + + private int count; + + private Date date; +} diff --git a/src/main/java/com/jxy/windminer/dto/OrderInfoReturnDto.java b/src/main/java/com/jxy/windminer/dto/OrderInfoReturnDto.java new file mode 100644 index 0000000..0860910 --- /dev/null +++ b/src/main/java/com/jxy/windminer/dto/OrderInfoReturnDto.java @@ -0,0 +1,30 @@ +package com.jxy.windminer.dto; + +import lombok.Data; + +import java.util.Date; + +/** + * @Description 订单传参 + * @Date 2022/8/1 18:44 + * @Author 杜懿 + */ +@Data +public class OrderInfoReturnDto { + + private int orderId; + + private String orderKey; + + private String email; + + private double amount; + + private long expireTime; + + private String currency; + + private String cancelUrl; + + private String viewUrl; +} diff --git a/src/main/java/com/jxy/windminer/dto/PaymentDto.java b/src/main/java/com/jxy/windminer/dto/PaymentDto.java new file mode 100644 index 0000000..811f05e --- /dev/null +++ b/src/main/java/com/jxy/windminer/dto/PaymentDto.java @@ -0,0 +1,21 @@ +package com.jxy.windminer.dto; + +import lombok.Data; + +import java.util.Date; + +/** + * @Description TODO + * @Date 2022/6/20 17:44 + * @Author 杜懿 + */ +@Data +public class PaymentDto { + + /** 应收金额 */ + private String amount; + + /** 实际收款地址 */ + private String balance; + +} diff --git a/src/main/java/com/jxy/windminer/dto/PrivateTicketListDto.java b/src/main/java/com/jxy/windminer/dto/PrivateTicketListDto.java new file mode 100644 index 0000000..fe0b42e --- /dev/null +++ b/src/main/java/com/jxy/windminer/dto/PrivateTicketListDto.java @@ -0,0 +1,34 @@ +package com.jxy.windminer.dto; + +import lombok.Data; + +import java.util.Date; + +/** + * @Description TODO + * @Date 2022/6/20 17:44 + * @Author 杜懿 + */ +@Data +public class PrivateTicketListDto { + + private int id; + + private String type; + + private Date date; + + private String model; + + private String serialNo; + + private String email; + + private String status; + + private String fault; + + /** 维修进度 */ + private String process; + +} diff --git a/src/main/java/com/jxy/windminer/dto/RepairRecordDto.java b/src/main/java/com/jxy/windminer/dto/RepairRecordDto.java new file mode 100644 index 0000000..1c579c5 --- /dev/null +++ b/src/main/java/com/jxy/windminer/dto/RepairRecordDto.java @@ -0,0 +1,42 @@ +package com.jxy.windminer.dto; + +import com.jxy.windminer.annotation.Excel; +import com.jxy.windminer.common.web.entity.BaseEntity; +import lombok.Data; + +import java.util.Date; + +/** + * 维修记录 + * + * @author dy + */ +@Data +public class RepairRecordDto +{ + private long id; + + /** 机器序列号 */ + private String machineSerial; + + /** 算力板序列号 */ + private String hashBoardSerial; + + /** 故障现象 */ + private String fault; + + /** 维修结果 */ + private String repairResult; + + /** 维修方法 */ + private String repairMethod; + + /** 维修时间 */ + private Date repairDate; + + /** 维修记录导入时间 */ + private Date importDate; + + /** 操作人 */ + private String operName; +} diff --git a/src/main/java/com/jxy/windminer/dto/RepairTicketInfoDto.java b/src/main/java/com/jxy/windminer/dto/RepairTicketInfoDto.java new file mode 100644 index 0000000..b44b261 --- /dev/null +++ b/src/main/java/com/jxy/windminer/dto/RepairTicketInfoDto.java @@ -0,0 +1,31 @@ +package com.jxy.windminer.dto; + +import lombok.Data; + +import java.util.Date; + +/** + * @Description TODO + * @Date 2022/6/20 17:44 + * @Author 杜懿 + */ +@Data +public class RepairTicketInfoDto { + + private int id; + + private String email; + + private String model; + + private String serialNo; + + private String desc; + + private Date createTime; + + private int status; + + private int type; + +} diff --git a/src/main/java/com/jxy/windminer/dto/ReturnInfoDto.java b/src/main/java/com/jxy/windminer/dto/ReturnInfoDto.java new file mode 100644 index 0000000..6375091 --- /dev/null +++ b/src/main/java/com/jxy/windminer/dto/ReturnInfoDto.java @@ -0,0 +1,44 @@ +package com.jxy.windminer.dto; + +import lombok.Data; + +import java.util.Date; + +/** + * @Description TODO + * @Date 2022/6/20 17:44 + * @Author 杜懿 + */ +@Data +public class ReturnInfoDto { + + private int id; + + private String email; + + private String model; + + private String serialNo; + + private String desc; + + private Date date; + + private String status; + + private String type; + + private String receiveName; + + private String receiveAddress; + + private String receivePhone; + + private String sendName; + + private String sendAddress; + + private String sendPhone; + + private String notes; +} diff --git a/src/main/java/com/jxy/windminer/dto/SysFileDto.java b/src/main/java/com/jxy/windminer/dto/SysFileDto.java new file mode 100644 index 0000000..20a2f17 --- /dev/null +++ b/src/main/java/com/jxy/windminer/dto/SysFileDto.java @@ -0,0 +1,27 @@ +package com.jxy.windminer.dto; + +import lombok.Data; + +import java.io.Serializable; + +/** + * @Description 返回数据实体 + * @Date 2022/5/30 11:17 + * @Author 杜懿 + */ +@Data +public class SysFileDto implements Serializable { + + /** 多文件拼接 */ + private String id; + + /** + * 文件名称 + */ + private String name; + + /** + * 文件地址 + */ + private String url; +} diff --git a/src/main/java/com/jxy/windminer/dto/SysToolsDto.java b/src/main/java/com/jxy/windminer/dto/SysToolsDto.java new file mode 100644 index 0000000..2488bb1 --- /dev/null +++ b/src/main/java/com/jxy/windminer/dto/SysToolsDto.java @@ -0,0 +1,43 @@ +package com.jxy.windminer.dto; + +import lombok.Data; + +import java.io.Serializable; +import java.util.Date; + +/** + * @Description 返回数据实体 + * @Date 2022/5/30 11:17 + * @Author 杜懿 + */ +@Data +public class SysToolsDto implements Serializable { + + /** 使用工具文件表id */ + private long id; + + /** + * 文件名称 + */ + private String name; + + /** + * 文件版本号 + */ + private String version; + + /** + * 文件大小 + */ + private String size; + + /** + * md5sum 用于文件校验 + */ + private String md5sum; + + /** + * 文件更新时间 + */ + private Date date; +} diff --git a/src/main/java/com/jxy/windminer/dto/TicketAcListDto.java b/src/main/java/com/jxy/windminer/dto/TicketAcListDto.java new file mode 100644 index 0000000..981b484 --- /dev/null +++ b/src/main/java/com/jxy/windminer/dto/TicketAcListDto.java @@ -0,0 +1,32 @@ +package com.jxy.windminer.dto; + +import lombok.Data; + +import java.util.Date; + +/** + * @Description TODO + * @Date 2022/6/20 17:44 + * @Author 杜懿 + */ +@Data +public class TicketAcListDto { + + private int id; + + private String type; + + private Date date; + + private String model; + + private String serialNo; + + private String email; + + private String status; + + private String receivable; + + private String actual; +} diff --git a/src/main/java/com/jxy/windminer/dto/TicketAuditDto.java b/src/main/java/com/jxy/windminer/dto/TicketAuditDto.java new file mode 100644 index 0000000..b626d2f --- /dev/null +++ b/src/main/java/com/jxy/windminer/dto/TicketAuditDto.java @@ -0,0 +1,21 @@ +package com.jxy.windminer.dto; + +import lombok.Data; + +import java.util.Date; + +/** + * @Description TODO + * @Date 2022/6/20 17:44 + * @Author 杜懿 + */ +@Data +public class TicketAuditDto { + + private Date time; + + private String content; + + private String name; + +} diff --git a/src/main/java/com/jxy/windminer/dto/TicketContentDto.java b/src/main/java/com/jxy/windminer/dto/TicketContentDto.java new file mode 100644 index 0000000..49b21d3 --- /dev/null +++ b/src/main/java/com/jxy/windminer/dto/TicketContentDto.java @@ -0,0 +1,30 @@ +package com.jxy.windminer.dto; + +import lombok.Data; + +import java.util.Date; + +/** + * @Description TODO + * @Date 2022/6/20 17:44 + * @Author 杜懿 + */ +@Data +public class TicketContentDto { + + + private Date time; + + private String content; + + private String name; + + private String files; + + private String videoPath; + + private String audioPath; + + private String imagePath; + +} diff --git a/src/main/java/com/jxy/windminer/dto/TicketDailyCountDto.java b/src/main/java/com/jxy/windminer/dto/TicketDailyCountDto.java new file mode 100644 index 0000000..4c4b918 --- /dev/null +++ b/src/main/java/com/jxy/windminer/dto/TicketDailyCountDto.java @@ -0,0 +1,32 @@ +package com.jxy.windminer.dto; + +import lombok.Data; + +import java.util.Date; + +/** + * @Description TODO + * @Date 2023/3/20 15:10 + * @Author 杜懿 + */ +@Data +public class TicketDailyCountDto { + + private Date date; + + /**今日提交工单数*/ + private int submitNum; + + /**已处理工单数*/ + private int solvedNum; + + /**未处理工单数*/ + private int pendingNum; + + /**待审核工单数*/ + private int checkingNum; + + /**已审核工单数*/ + private int checkedNum; + +} diff --git a/src/main/java/com/jxy/windminer/dto/TicketDetails3Dto.java b/src/main/java/com/jxy/windminer/dto/TicketDetails3Dto.java new file mode 100644 index 0000000..a8bf788 --- /dev/null +++ b/src/main/java/com/jxy/windminer/dto/TicketDetails3Dto.java @@ -0,0 +1,59 @@ +package com.jxy.windminer.dto; + +import lombok.Data; + +import java.util.Date; +import java.util.List; +import java.util.Map; + +/** + * @Description TODO + * @Date 2022/6/20 17:44 + * @Author 杜懿 + */ +@Data +public class TicketDetails3Dto { + + private int id; + + private String email; + + private Date buyDate; + + private String distributor; + + private String fault; + + private String model; + + private String serialNo; + + private Map warranty; + + private String desc; + + private Date date; + + private String status; + + private String type; + + private String sendName; + + private String sendAddress; + + private String sendPhone; + + private Date sendTime; + + private String company; + + private String no; + + private List list; + + + + + +} diff --git a/src/main/java/com/jxy/windminer/dto/TicketDetailsDto.java b/src/main/java/com/jxy/windminer/dto/TicketDetailsDto.java new file mode 100644 index 0000000..c28ac05 --- /dev/null +++ b/src/main/java/com/jxy/windminer/dto/TicketDetailsDto.java @@ -0,0 +1,42 @@ +package com.jxy.windminer.dto; + +import lombok.Data; + +import java.util.Date; +import java.util.List; + +/** + * @Description TODO + * @Date 2022/6/20 17:44 + * @Author 杜懿 + */ +@Data +public class TicketDetailsDto { + + private int id; + + private String email; + + private Date buyDate; + + private String distributor; + + private String fault; + + private String model; + + private String serialNo; + + private String desc; + + private Date createTime; + + private String status; + + private String type; + + + private List list; + + +} diff --git a/src/main/java/com/jxy/windminer/dto/TicketDto.java b/src/main/java/com/jxy/windminer/dto/TicketDto.java new file mode 100644 index 0000000..1fe149a --- /dev/null +++ b/src/main/java/com/jxy/windminer/dto/TicketDto.java @@ -0,0 +1,33 @@ +package com.jxy.windminer.dto; + +import lombok.Data; + +import java.util.Date; + +/** + * @Description TODO + * @Date 2022/6/20 17:44 + * @Author 杜懿 + */ +@Data +public class TicketDto { + + private int id; + + private String email; + + private String model; + + private String serialNo; + + private String desc; + + private Date createTime; + + private int status; + + private int type; + + private int ifChange; + +} diff --git a/src/main/java/com/jxy/windminer/dto/TicketListDto.java b/src/main/java/com/jxy/windminer/dto/TicketListDto.java new file mode 100644 index 0000000..b3d664a --- /dev/null +++ b/src/main/java/com/jxy/windminer/dto/TicketListDto.java @@ -0,0 +1,29 @@ +package com.jxy.windminer.dto; + +import lombok.Data; + +import java.util.Date; + +/** + * @Description TODO + * @Date 2022/6/20 17:44 + * @Author 杜懿 + */ +@Data +public class TicketListDto { + + private int id; + + private String type; + + private Date date; + + private String model; + + private String serialNo; + + private String email; + + private String status; + +} diff --git a/src/main/java/com/jxy/windminer/dto/TicketOrderBaseInfoDto.java b/src/main/java/com/jxy/windminer/dto/TicketOrderBaseInfoDto.java new file mode 100644 index 0000000..a90af51 --- /dev/null +++ b/src/main/java/com/jxy/windminer/dto/TicketOrderBaseInfoDto.java @@ -0,0 +1,43 @@ +package com.jxy.windminer.dto; + +import lombok.Data; + +import java.util.Date; + +/** + * @Description 订单传参 + * @Date 2022/8/1 18:44 + * @Author 杜懿 + */ +@Data +public class TicketOrderBaseInfoDto { + + private int orderId; + + private String orderKey; + + private double amount; + + private String currency; + + private String email; + + private String redirectTo; + + private String cancelUrl; + + private String viewUrl; + + //实际收款金额 + private double confirmAmount; + //实际收款币种 + private String confirmCurrency; + + private String chain; + + private int status; + + private int orderFrom; + + private Date date; +} diff --git a/src/main/java/com/jxy/windminer/dto/TicketOrderPhoneAndDetailsDto.java b/src/main/java/com/jxy/windminer/dto/TicketOrderPhoneAndDetailsDto.java new file mode 100644 index 0000000..a2629a1 --- /dev/null +++ b/src/main/java/com/jxy/windminer/dto/TicketOrderPhoneAndDetailsDto.java @@ -0,0 +1,30 @@ +package com.jxy.windminer.dto; + +import lombok.Data; + +import java.math.BigDecimal; + +/** + * @Description TODO + * @Date 2022/6/20 17:44 + * @Author 杜懿 + */ +@Data +public class TicketOrderPhoneAndDetailsDto { + + /** 应收金额 */ + private BigDecimal totalAmount; + + /** 实收金额 */ + private BigDecimal actualAmount; + + /** 订单报价详情 **/ + private String details; + + private String address; + /** 用户手机号 **/ + private String phone; + + + +} diff --git a/src/main/java/com/jxy/windminer/dto/TicketPaymentDto.java b/src/main/java/com/jxy/windminer/dto/TicketPaymentDto.java new file mode 100644 index 0000000..fa45799 --- /dev/null +++ b/src/main/java/com/jxy/windminer/dto/TicketPaymentDto.java @@ -0,0 +1,25 @@ +package com.jxy.windminer.dto; + +import lombok.Data; + +/** + * @Description TODO + * @Date 2022/6/20 17:44 + * @Author 杜懿 + */ +@Data +public class TicketPaymentDto { + + private String orderKey; + + private String orderStatus; + + private String email; + + private String payStatus; + + private int ticketId; + + private String url; + +} diff --git a/src/main/java/com/jxy/windminer/dto/TicketPrivateDto.java b/src/main/java/com/jxy/windminer/dto/TicketPrivateDto.java new file mode 100644 index 0000000..f593652 --- /dev/null +++ b/src/main/java/com/jxy/windminer/dto/TicketPrivateDto.java @@ -0,0 +1,53 @@ +package com.jxy.windminer.dto; + +import lombok.Data; + +import java.util.Date; +import java.util.List; + +/** + * @Description TODO + * @Date 2022/6/20 17:44 + * @Author 杜懿 + */ +@Data +public class TicketPrivateDto { + + private int id; + + private String email; + + private String model; + + private String serialNo; + + private Date buyDate; + + private String distributor; + + private String fault; + + private String desc; + + private Date createTime; + + private String status; + + private String type; + + private String sendName; + + private String sendAddress; + + private String sendPhone; + + private Date sendTime; + + private String company; + + private String no; + + private List list; + + private String details; +} diff --git a/src/main/java/com/jxy/windminer/dto/TicketResponDto.java b/src/main/java/com/jxy/windminer/dto/TicketResponDto.java new file mode 100644 index 0000000..2a801ef --- /dev/null +++ b/src/main/java/com/jxy/windminer/dto/TicketResponDto.java @@ -0,0 +1,27 @@ +package com.jxy.windminer.dto; + +import lombok.Data; + +import java.util.Date; + +/** + * @Description TODO + * @Date 2022/6/20 17:44 + * @Author 杜懿 + */ +@Data +public class TicketResponDto { + + /** 回复时间*/ + private Date responTime; + + /** 回复人*/ + private String responName; + + /** 回复附件*/ + private String responFiles; + + /** 回复内容*/ + private String respon; + +} diff --git a/src/main/java/com/jxy/windminer/dto/TotalAddressDto.java b/src/main/java/com/jxy/windminer/dto/TotalAddressDto.java new file mode 100644 index 0000000..538fa70 --- /dev/null +++ b/src/main/java/com/jxy/windminer/dto/TotalAddressDto.java @@ -0,0 +1,19 @@ +package com.jxy.windminer.dto; + +import lombok.Data; + +/** + * @Description TODO + * @Date 2022/6/20 17:44 + * @Author 杜懿 + */ +@Data +public class TotalAddressDto { + + private int id; + + private String address; + + private int status; + +} diff --git a/src/main/java/com/jxy/windminer/dto/WPOrderAddressDto.java b/src/main/java/com/jxy/windminer/dto/WPOrderAddressDto.java new file mode 100644 index 0000000..a99f596 --- /dev/null +++ b/src/main/java/com/jxy/windminer/dto/WPOrderAddressDto.java @@ -0,0 +1,32 @@ +package com.jxy.windminer.dto; + +import lombok.Data; + +/** + * @Description TODO + * @Date 2022/6/20 17:44 + * @Author 杜懿 + */ +@Data +public class WPOrderAddressDto { + + /** wrodpress订单id **/ + private String orderId; + + private String totalAmount; + + /** **/ + private String city; + + /** 地址1 **/ + private String address1; + + /** 地址2 **/ + private String address2; + + /** 用户手机号 **/ + private String phone; + + + +} diff --git a/src/main/java/com/jxy/windminer/dto/WPOrderProductDto.java b/src/main/java/com/jxy/windminer/dto/WPOrderProductDto.java new file mode 100644 index 0000000..aaa28e2 --- /dev/null +++ b/src/main/java/com/jxy/windminer/dto/WPOrderProductDto.java @@ -0,0 +1,38 @@ +package com.jxy.windminer.dto; + +import lombok.Data; + +import java.math.BigDecimal; + +/** + * @Description wordpress 订单-产品 金额关系表 + * @Date 2022/6/20 17:44 + * @Author 杜懿 + */ +@Data +public class WPOrderProductDto { + + /** 商品名称 **/ + private String product; + + /** 商品单价 **/ + private BigDecimal price; + + /** 商品数量 **/ + private int qty; + + /** 商品总价 单价*数量 = total**/ + private BigDecimal totalPrice; + + /** 优惠券折扣 **/ + private BigDecimal coupon = BigDecimal.ZERO; + + /** 税费 **/ + private BigDecimal tax = BigDecimal.ZERO; + + /** 邮费 **/ + private BigDecimal shippingAmount = BigDecimal.ZERO; + + /** 邮费税费 **/ + private BigDecimal shippingTax = BigDecimal.ZERO; +} diff --git a/src/main/java/com/jxy/windminer/entity/JXYFile.java b/src/main/java/com/jxy/windminer/entity/JXYFile.java new file mode 100644 index 0000000..bd4d085 --- /dev/null +++ b/src/main/java/com/jxy/windminer/entity/JXYFile.java @@ -0,0 +1,33 @@ +package com.jxy.windminer.entity; + + +import lombok.Data; + +import java.util.Date; + +/** + * @Description ETF + * @Date 2022/5/19 15:57 + * @Author 杜懿 + */ +@Data +public class JXYFile { + + /** id */ + private Long id; + + /** 时间 */ + private Date createTime; + + /** value */ + private String fileName; + + private String url; + + // 0 图片 1音频 2视频 + private int fileType; + + + private String userName; + +} diff --git a/src/main/java/com/jxy/windminer/entity/OrderKeyAddress.java b/src/main/java/com/jxy/windminer/entity/OrderKeyAddress.java new file mode 100644 index 0000000..206739c --- /dev/null +++ b/src/main/java/com/jxy/windminer/entity/OrderKeyAddress.java @@ -0,0 +1,34 @@ +package com.jxy.windminer.entity; + +import com.jxy.windminer.common.web.entity.BaseEntity; +import lombok.Data; +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; + +/** + * 文件信息 + * + * @author dy + */ +@Data +public class OrderKeyAddress +{ + + private String orderKey; + + /** + * ETH地址 + */ + private String ethAddress; + + /** + * TRX地址 + */ + private String trxAddress; + + /** 订单对应的email */ + private String email; + + private String image; + +} diff --git a/src/main/java/com/jxy/windminer/entity/RepairRecord.java b/src/main/java/com/jxy/windminer/entity/RepairRecord.java new file mode 100644 index 0000000..f98376a --- /dev/null +++ b/src/main/java/com/jxy/windminer/entity/RepairRecord.java @@ -0,0 +1,96 @@ +package com.jxy.windminer.entity; + +import com.jxy.windminer.annotation.Excel; +import com.jxy.windminer.common.utils.DateUtils; +import com.jxy.windminer.common.web.entity.BaseEntity; +import com.jxy.windminer.annotation.Excel.ColumnType; + +import java.util.Date; + +/** + * 维修记录 + * + * @author dy + */ +public class RepairRecord extends BaseEntity +{ + @Excel(name = "机器序号", prompt = "机器序列号") + private String machineSerial; + + @Excel(name = "算力板序列号") + private String hashBoardSerial; + + @Excel(name = "故障现象",combo = {"整机无算力","某一块板无算力","整机算力过低","某一块板算力过低","其他故障"}) + private String fault; + + @Excel(name = "维修结果") + private String repairResult; + + @Excel(name = "维修方法") + private String repairMethod; + + @Excel(name = "维修时间", prompt = "按格式yyyy-MM-dd输入日期", width = 30, dateFormat = "yyyy-MM-dd HH:mm") + private Date repairDate; + + private String operName; + + public String getOperName() { + return operName; + } + + public void setOperName(String operName) { + this.operName = operName; + } +//导入时间 + //private Date importDate; + + + public String getMachineSerial() { + return machineSerial; + } + + public void setMachineSerial(String machineSerial) { + this.machineSerial = machineSerial; + } + + public String getHashBoardSerial() { + return hashBoardSerial; + } + + public void setHashBoardSerial(String hashBoardSerial) { + this.hashBoardSerial = hashBoardSerial; + } + + public String getFault() { + return fault; + } + + public void setFault(String fault) { + this.fault = fault; + } + + public String getRepairResult() { + return repairResult; + } + + public void setRepairResult(String repairResult) { + this.repairResult = repairResult; + } + + public String getRepairMethod() { + return repairMethod; + } + + public void setRepairMethod(String repairMethod) { + this.repairMethod = repairMethod; + } + + public Date getRepairDate() { + return repairDate; + } + + public void setRepairDate(Date repairDate) { + this.repairDate = repairDate; + } + +} diff --git a/src/main/java/com/jxy/windminer/entity/SysFile.java b/src/main/java/com/jxy/windminer/entity/SysFile.java new file mode 100644 index 0000000..a4f3e48 --- /dev/null +++ b/src/main/java/com/jxy/windminer/entity/SysFile.java @@ -0,0 +1,67 @@ +package com.jxy.windminer.entity; + +import com.jxy.windminer.common.web.entity.BaseEntity; +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; + +/** + * 文件信息 + * + * @author dy + */ +public class SysFile extends BaseEntity +{ + /** + * 文件表主键 + */ + private Long id; + + /** + * 文件名称 + */ + private String name; + + /** + * 文件地址 + */ + private String url; + + public String getName() + { + return name; + } + + public void setName(String name) + { + this.name = name; + } + + public String getUrl() + { + return url; + } + + public void setUrl(String url) + { + this.url = url; + } + + public Long getId() + { + return id; + } + + public void setId(Long id) + { + this.id = id; + } + + @Override + public String toString() { + return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) + .append("name", getName()) + .append("id", getId()) + .append("url", getUrl()) + .toString(); + } +} diff --git a/src/main/java/com/jxy/windminer/entity/Test.java b/src/main/java/com/jxy/windminer/entity/Test.java new file mode 100644 index 0000000..2bdbc7f --- /dev/null +++ b/src/main/java/com/jxy/windminer/entity/Test.java @@ -0,0 +1,30 @@ +package com.jxy.windminer.entity; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.jxy.windminer.common.utils.DateUtils; +import com.jxy.windminer.common.web.Result.AjaxResult; +import lombok.Data; + +import java.util.Date; + +/** + * @Description TODO + * @Date 2022/6/20 17:44 + * @Author 杜懿 + */ +public class Test { + public static void main(String[] args) { + + String a = "2826961034@qq.com"; + //String regex ="/^([a-zA-Z0-9._%-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,6}(\\.[a-zA-Z]{2,6})?)$/"; + String regex ="^[a-zA-Z0-9][\\w\\.-]*[a-zA-Z0-9]@[a-zA-Z0-9][\\w\\.-]*[a-zA-Z0-9]\\.[a-zA-Z][a-zA-Z\\.]*[a-zA-Z]$"; + if(!a.matches(regex)){ + System.out.println(1); + }else { + System.out.println(2); + } + } +} diff --git a/src/main/java/com/jxy/windminer/entity/Ticket.java b/src/main/java/com/jxy/windminer/entity/Ticket.java new file mode 100644 index 0000000..f905868 --- /dev/null +++ b/src/main/java/com/jxy/windminer/entity/Ticket.java @@ -0,0 +1,55 @@ +package com.jxy.windminer.entity; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; + +import java.util.Date; + +/** + * @Description TODO + * @Date 2022/6/20 17:44 + * @Author 杜懿 + */ +@TableName("ticket") +@Data +public class Ticket { + + @TableId(type = IdType.AUTO) + @TableField("order_id") + private int id; + + @TableField("user_name") + private String userName; + + private String email; + + private String model; + + @TableField("serial_no") + private String serialNo; + + private String desc; + + @TableField("create_time") + private Date createTime; + + private int status; + + private int type; + + //购买日期 + private String buyDate; + + //经销商 + private String distributor; + + //故障现象 + private String fault; + + @TableField("order_process") + private String process; + +} diff --git a/src/main/java/com/jxy/windminer/entity/TicketSupplement.java b/src/main/java/com/jxy/windminer/entity/TicketSupplement.java new file mode 100644 index 0000000..7e1d2b8 --- /dev/null +++ b/src/main/java/com/jxy/windminer/entity/TicketSupplement.java @@ -0,0 +1,24 @@ +package com.jxy.windminer.entity; + + +import lombok.Data; + +import java.util.Date; + +/** + * @Description ETF + * @Date 2022/5/19 15:57 + * @Author 杜懿 + */ +@Data +public class TicketSupplement { + + /** 时间 */ + private Date date; + + /** 补充内容 */ + private String content; + + /** 补充内容 */ + private String files; +} diff --git a/src/main/java/com/jxy/windminer/entity/ToolsFile.java b/src/main/java/com/jxy/windminer/entity/ToolsFile.java new file mode 100644 index 0000000..ad29ad7 --- /dev/null +++ b/src/main/java/com/jxy/windminer/entity/ToolsFile.java @@ -0,0 +1,36 @@ +package com.jxy.windminer.entity; + + +import lombok.Data; + +import java.util.Date; + +/** + * @Description ETF + * @Date 2022/5/19 15:57 + * @Author 杜懿 + */ +@Data +public class ToolsFile { + + /** id */ + private Long id; + + /** 时间 */ + private Date createTime; + + /** value */ + private String fileName; + + private String url; + + // 0 图片 1音频 2视频 + private String version; + + private String size; + + private String md5sum; + + private String userName; + +} diff --git a/src/main/java/com/jxy/windminer/entity/auth/EmailCodeEntity.java b/src/main/java/com/jxy/windminer/entity/auth/EmailCodeEntity.java new file mode 100644 index 0000000..fce5123 --- /dev/null +++ b/src/main/java/com/jxy/windminer/entity/auth/EmailCodeEntity.java @@ -0,0 +1,28 @@ +package com.jxy.windminer.entity.auth; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; + +/** + * @Description 用户登录对象 + * @Date 2022/5/12 16:13 + * @Author 杜懿 + */ +@Data +@NoArgsConstructor +@AllArgsConstructor +public class EmailCodeEntity implements Serializable { + + /** 用户名或邮箱 */ + private String userName; + + /** 邮箱 */ + private String email; + + private String emailCode; + + private int times; +} diff --git a/src/main/java/com/jxy/windminer/entity/auth/EmailEntity.java b/src/main/java/com/jxy/windminer/entity/auth/EmailEntity.java new file mode 100644 index 0000000..b68994e --- /dev/null +++ b/src/main/java/com/jxy/windminer/entity/auth/EmailEntity.java @@ -0,0 +1,25 @@ +package com.jxy.windminer.entity.auth; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; + +/** + * @Description 用户登录对象 + * @Date 2022/5/12 16:13 + * @Author 杜懿 + */ +@Data +@NoArgsConstructor +@AllArgsConstructor +public class EmailEntity implements Serializable { + + /** 邮箱 */ + private String email; + + private String subject; + + private String text; +} diff --git a/src/main/java/com/jxy/windminer/entity/auth/GetEmailCodeEntity.java b/src/main/java/com/jxy/windminer/entity/auth/GetEmailCodeEntity.java new file mode 100644 index 0000000..62985bc --- /dev/null +++ b/src/main/java/com/jxy/windminer/entity/auth/GetEmailCodeEntity.java @@ -0,0 +1,33 @@ +package com.jxy.windminer.entity.auth; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.validation.constraints.Email; +import javax.validation.constraints.NotNull; + +/** + * @Description 用户登录对象 + * @Date 2022/5/12 16:13 + * @Author 杜懿 + */ +@Data +@AllArgsConstructor +@NoArgsConstructor +public class GetEmailCodeEntity { + + /** 用户名或邮箱 */ + private String userName; + + /** 邮箱 */ + @NotNull(message = "用户邮箱不能为空") + @Email(message = "邮箱格式错误") + private String email; + + //@NotNull(message = "用户密码不能为空") + //@Size(min=6, max=15,message="密码长度必须在 5 ~ 15 字符之间!") + ////@Pattern(regexp="^[a-zA-Z0-9|_]+$",message="密码必须由字母、数字、下划线组成!") + //private String password; + +} diff --git a/src/main/java/com/jxy/windminer/entity/auth/LoginBody.java b/src/main/java/com/jxy/windminer/entity/auth/LoginBody.java new file mode 100644 index 0000000..31fe551 --- /dev/null +++ b/src/main/java/com/jxy/windminer/entity/auth/LoginBody.java @@ -0,0 +1,24 @@ +package com.jxy.windminer.entity.auth; + +import lombok.Data; + +/** + * @Description 用户登录对象 + * @Date 2022/5/12 16:13 + * @Author 杜懿 + */ +@Data +public class LoginBody { + + /** 用户名或邮箱 */ + private String userName; + + /** 密码 */ + private String password; + + private String code; + + private String uuid; + + //private boolean flag = false; +} diff --git a/src/main/java/com/jxy/windminer/entity/auth/LoginUser.java b/src/main/java/com/jxy/windminer/entity/auth/LoginUser.java new file mode 100644 index 0000000..21c8727 --- /dev/null +++ b/src/main/java/com/jxy/windminer/entity/auth/LoginUser.java @@ -0,0 +1,46 @@ +package com.jxy.windminer.entity.auth; + +import com.jxy.windminer.entity.system.SysUser; +import lombok.Data; + +import java.io.Serializable; +import java.util.Set; + +/** + * @Description 登陆的用户信息 + * @Date 2022/5/12 15:13 + * @Author 杜懿 + */ +@Data +public class LoginUser implements Serializable { + + private static final long serialVersionUID = 1L; + + /** 用户唯一标识 */ + private String token; + + /** 用户名id */ + private Long userid; + + /** 用户名 */ + private String username; + + /** 登录时间 */ + private Long loginTime; + + /** 过期时间 */ + private Long expireTime; + + /** 登录IP地址 */ + private String ipaddr; + + /** 权限列表 */ + private Set permissions; + + /** 角色列表 */ + private Set roles; + + /** 用户信息 */ + private SysUser sysUser; + +} diff --git a/src/main/java/com/jxy/windminer/entity/auth/RegisterBody.java b/src/main/java/com/jxy/windminer/entity/auth/RegisterBody.java new file mode 100644 index 0000000..e24576f --- /dev/null +++ b/src/main/java/com/jxy/windminer/entity/auth/RegisterBody.java @@ -0,0 +1,39 @@ +package com.jxy.windminer.entity.auth; + +import lombok.Data; + +import javax.validation.constraints.Email; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Pattern; +import javax.validation.constraints.Size; + +/** + * @Description 用户注册对象 + * @Date 2022/5/12 16:16 + * @Author 杜懿 + */ +@Data +public class RegisterBody{ + + //todo 添加注册详细信息 + /** 手机号码 */ + private String phone; + + @Email(message = "邮箱格式错误") + private String email; + + private String emailCode; + + /** 用户名 */ + @NotNull(message = "用户名不能为空") + @Size(min=3, max=16,message="密码长度必须在 3 ~ 16 字符之间!") + @Pattern(regexp="^[a-zA-Z][a-zA-Z0-9|_]+$",message="用户名需以字母开头") + @Pattern(regexp = "[a-zA-Z0-9|_]+$",message = "用户名仅允许使用字母、数字、下划线,不能包含其他特殊字符或空格") + private String userName; + + /** 密码 */ + @NotNull(message = "密码不能为空") + //@Size(min=8, max=32,message="密码长度必须在 5 ~ 15 字符之间!") + //@Pattern(regexp="^[a-zA-Z0-9|_]+$",message="密码必须由字母、数字、下划线组成!") + private String password; +} diff --git a/src/main/java/com/jxy/windminer/entity/auth/ResetPwdBody.java b/src/main/java/com/jxy/windminer/entity/auth/ResetPwdBody.java new file mode 100644 index 0000000..eacfc11 --- /dev/null +++ b/src/main/java/com/jxy/windminer/entity/auth/ResetPwdBody.java @@ -0,0 +1,26 @@ +package com.jxy.windminer.entity.auth; + +import lombok.Data; + +import javax.validation.constraints.Email; + +/** + * @Description 用户重置密码对象 + * @Date 2022/5/12 16:16 + * @Author 杜懿 + */ +@Data +public class ResetPwdBody { + + //todo 添加详细信息 + + private String userName; + + @Email + private String email; + + private String emailCode; + + /** 新密码 */ + private String password; +} diff --git a/src/main/java/com/jxy/windminer/entity/auth/WpLoginUser.java b/src/main/java/com/jxy/windminer/entity/auth/WpLoginUser.java new file mode 100644 index 0000000..810f17f --- /dev/null +++ b/src/main/java/com/jxy/windminer/entity/auth/WpLoginUser.java @@ -0,0 +1,26 @@ +package com.jxy.windminer.entity.auth; + +import lombok.Data; + +import javax.validation.constraints.Email; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Pattern; +import javax.validation.constraints.Size; + +/** + * @Description 用户注册对象 + * @Date 2022/5/12 16:16 + * @Author 杜懿 + */ +@Data +public class WpLoginUser { + + @Email(message = "邮箱格式错误") + private String email; + + + /** 用户名 */ + @NotNull(message = "用户名不能为空") + private String userName; + +} diff --git a/src/main/java/com/jxy/windminer/entity/system/SysDept.java b/src/main/java/com/jxy/windminer/entity/system/SysDept.java new file mode 100644 index 0000000..5400435 --- /dev/null +++ b/src/main/java/com/jxy/windminer/entity/system/SysDept.java @@ -0,0 +1,85 @@ +package com.jxy.windminer.entity.system; + +import com.jxy.windminer.common.web.entity.BaseEntity; +import lombok.Data; + +import javax.validation.constraints.Email; +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Size; +import java.util.ArrayList; +import java.util.List; + +/** + * @Description 部门表实体 + * @Date 2022/5/12 11:19 + * @Author 杜懿 + */ +@Data +public class SysDept extends BaseEntity { + + private static final long serialVersionUID = 1L; + + /** 部门ID */ + private Long deptId; + + /** 父部门ID */ + private Long parentId; + + /** 祖级列表 */ + private String ancestors; + + /** 部门名称 */ + private String deptName; + + /** 显示顺序 */ + private Integer orderNum; + + /** 负责人 */ + private String leader; + + /** 联系电话 */ + private String phone; + + /** 邮箱 */ + private String email; + + /** 部门状态:0正常,1停用 */ + private String status; + + /** 删除标志(0代表存在 2代表删除) */ + private String delFlag; + + /** 父部门名称 */ + private String parentName; + + /** 子部门 */ + private List children = new ArrayList(); + + @NotBlank(message = "部门名称不能为空") + @Size(min = 0, max = 30, message = "部门名称长度不能超过30个字符") + public String getDeptName() + { + return deptName; + } + + @NotNull(message = "显示顺序不能为空") + public Integer getOrderNum() + { + return orderNum; + } + + @Size(min = 0, max = 11, message = "联系电话长度不能超过11个字符") + public String getPhone() + { + return phone; + } + + @Email(message = "邮箱格式不正确") + @Size(min = 0, max = 50, message = "邮箱长度不能超过50个字符") + public String getEmail() + { + return email; + } + +} diff --git a/src/main/java/com/jxy/windminer/entity/system/SysLogininfor.java b/src/main/java/com/jxy/windminer/entity/system/SysLogininfor.java new file mode 100644 index 0000000..362dc70 --- /dev/null +++ b/src/main/java/com/jxy/windminer/entity/system/SysLogininfor.java @@ -0,0 +1,37 @@ +package com.jxy.windminer.entity.system; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.jxy.windminer.common.web.entity.BaseEntity; +import lombok.Data; + +import java.util.Date; + +/** + * @Description 系统访问记录表实体类 + * @Date 2022/5/11 10:40 + * @Author 杜懿 + */ +@Data +public class SysLogininfor extends BaseEntity { + + private static final long serialVersionUID = 3217638467115846123L; + + /** ID */ + private Long infoId; + + /** 用户账号 */ + private String userName; + + /** 状态 0成功 1失败 */ + private String status; + + /** 地址 */ + private String ipaddr; + + /** 描述 */ + private String msg; + + /** 访问时间 */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date accessTime; +} diff --git a/src/main/java/com/jxy/windminer/entity/system/SysMenu.java b/src/main/java/com/jxy/windminer/entity/system/SysMenu.java new file mode 100644 index 0000000..2f72440 --- /dev/null +++ b/src/main/java/com/jxy/windminer/entity/system/SysMenu.java @@ -0,0 +1,262 @@ +package com.jxy.windminer.entity.system; + +import com.jxy.windminer.common.web.entity.BaseEntity; +import lombok.Data; +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Size; +import java.util.ArrayList; +import java.util.List; + +/** + * 菜单权限表 sys_menu + * + * @author jxy + */ +@Data +public class SysMenu extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** 菜单ID */ + private Long menuId; + + /** 菜单名称 */ + private String menuName; + + /** 父菜单名称 */ + private String parentName; + + /** 父菜单ID */ + private Long parentId; + + /** 显示顺序 */ + private Integer orderNum; + + /** 路由地址 */ + private String path; + + /** 组件路径 */ + private String component; + + /** 路由参数 */ + private String query; + + /** 是否为外链(0是 1否) */ + private String isFrame; + + /** 是否缓存(0缓存 1不缓存) */ + private String isCache; + + /** 类型(M目录 C菜单 F按钮) */ + private String menuType; + + /** 显示状态(0显示 1隐藏) */ + private String visible; + + /** 菜单状态(0显示 1隐藏) */ + private String status; + + /** 权限字符串 */ + private String perms; + + /** 菜单图标 */ + private String icon; + + /** 子菜单 */ + private List children = new ArrayList(); + + public Long getMenuId() + { + return menuId; + } + + public void setMenuId(Long menuId) + { + this.menuId = menuId; + } + + @NotBlank(message = "菜单名称不能为空") + @Size(min = 0, max = 50, message = "菜单名称长度不能超过50个字符") + public String getMenuName() + { + return menuName; + } + + public void setMenuName(String menuName) + { + this.menuName = menuName; + } + + public String getParentName() + { + return parentName; + } + + public void setParentName(String parentName) + { + this.parentName = parentName; + } + + public Long getParentId() + { + return parentId; + } + + public void setParentId(Long parentId) + { + this.parentId = parentId; + } + + @NotNull(message = "显示顺序不能为空") + public Integer getOrderNum() + { + return orderNum; + } + + public void setOrderNum(Integer orderNum) + { + this.orderNum = orderNum; + } + + @Size(min = 0, max = 200, message = "路由地址不能超过200个字符") + public String getPath() + { + return path; + } + + public void setPath(String path) + { + this.path = path; + } + + @Size(min = 0, max = 200, message = "组件路径不能超过255个字符") + public String getComponent() + { + return component; + } + + public void setComponent(String component) + { + this.component = component; + } + + public String getQuery() + { + return query; + } + + public void setQuery(String query) + { + this.query = query; + } + + public String getIsFrame() + { + return isFrame; + } + + public void setIsFrame(String isFrame) + { + this.isFrame = isFrame; + } + + public String getIsCache() + { + return isCache; + } + + public void setIsCache(String isCache) + { + this.isCache = isCache; + } + + @NotBlank(message = "菜单类型不能为空") + public String getMenuType() + { + return menuType; + } + + public void setMenuType(String menuType) + { + this.menuType = menuType; + } + + public String getVisible() + { + return visible; + } + + public void setVisible(String visible) + { + this.visible = visible; + } + + public String getStatus() + { + return status; + } + + public void setStatus(String status) + { + this.status = status; + } + + @Size(min = 0, max = 100, message = "权限标识长度不能超过100个字符") + public String getPerms() + { + return perms; + } + + public void setPerms(String perms) + { + this.perms = perms; + } + + public String getIcon() + { + return icon; + } + + public void setIcon(String icon) + { + this.icon = icon; + } + + public List getChildren() + { + return children; + } + + public void setChildren(List children) + { + this.children = children; + } + + @Override + public String toString() { + return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) + .append("menuId", getMenuId()) + .append("menuName", getMenuName()) + .append("parentId", getParentId()) + .append("orderNum", getOrderNum()) + .append("path", getPath()) + .append("component", getComponent()) + .append("isFrame", getIsFrame()) + .append("IsCache", getIsCache()) + .append("menuType", getMenuType()) + .append("visible", getVisible()) + .append("status ", getStatus()) + .append("perms", getPerms()) + .append("icon", getIcon()) + .append("createBy", getCreateBy()) + .append("createTime", getCreateTime()) + .append("updateBy", getUpdateBy()) + .append("updateTime", getUpdateTime()) + .append("remark", getRemark()) + .toString(); + } +} diff --git a/src/main/java/com/jxy/windminer/entity/system/SysOperLog.java b/src/main/java/com/jxy/windminer/entity/system/SysOperLog.java new file mode 100644 index 0000000..9cbf9ed --- /dev/null +++ b/src/main/java/com/jxy/windminer/entity/system/SysOperLog.java @@ -0,0 +1,68 @@ +package com.jxy.windminer.entity.system; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.jxy.windminer.common.web.entity.BaseEntity; +import lombok.Data; + +import java.util.Date; + +/** + * @Description 操作日志记录表实体类 + * @Date 2022/5/11 10:43 + * @Author 杜懿 + */ +@Data +public class SysOperLog extends BaseEntity { + + private static final long serialVersionUID = 7117642119125846123L; + + /** 日志主键 */ + private Long operId; + + /** 操作模块 */ + private String title; + + /** 业务类型(0其它 1新增 2修改 3删除) */ + private Integer businessType; + + /** 业务类型数组 */ + private Integer[] businessTypes; + + /** 请求方法 */ + private String method; + + /** 请求方式 */ + private String requestMethod; + + /** 操作类别(0其它 1后台用户 2手机端用户) */ + private Integer operatorType; + + /** 操作人员 */ + private String operName; + + /** 部门名称 */ + private String deptName; + + /** 请求url */ + private String operUrl; + + /** 操作地址 */ + private String operIp; + + /** 请求参数 */ + private String operParam; + + /** 返回参数 */ + private String jsonResult; + + /** 操作状态(0正常 1异常) */ + private Integer status; + + /** 错误消息 */ + private String errorMsg; + + /** 操作时间 */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date operTime; + +} diff --git a/src/main/java/com/jxy/windminer/entity/system/SysRole.java b/src/main/java/com/jxy/windminer/entity/system/SysRole.java new file mode 100644 index 0000000..4f792d7 --- /dev/null +++ b/src/main/java/com/jxy/windminer/entity/system/SysRole.java @@ -0,0 +1,89 @@ +package com.jxy.windminer.entity.system; + +import com.jxy.windminer.common.web.entity.BaseEntity; +import lombok.Data; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.Size; +import java.util.Date; + +/** + * @Description 角色表实体 + * @Date 2022/5/12 11:18 + * @Author 杜懿 + */ +@Data +public class SysRole extends BaseEntity { + + private static final long serialVersionUID = 1L; + + /** 角色ID */ + private Long roleId; + + /** 角色名称 */ + private String roleName; + + /** 角色权限 */ + private String roleKey; + + /** 角色排序 */ + private String roleSort; + + private Date levelExpireDate; + + + /** 数据范围(1:所有数据权限;2:自定义数据权限;3:本部门数据权限;4:本部门及以下数据权限;5:仅本人数据权限) */ + private String dataScope; + + /** 菜单树选择项是否关联显示( 0:父子不互相关联显示 1:父子互相关联显示) */ + private boolean menuCheckStrictly; + + /** 部门树选择项是否关联显示(0:父子不互相关联显示 1:父子互相关联显示 ) */ + private boolean deptCheckStrictly; + + /** 角色状态(0正常 1停用) */ + private String status; + + /** 删除标志(0代表存在 2代表删除) */ + private String delFlag; + + /** 用户是否存在此角色标识 默认不存在 */ + private boolean flag = false; + + /** 菜单组 */ + private Long[] menuIds; + + /** 部门组(数据权限) */ + //private Long[] deptIds; + + public boolean isAdmin() + { + return isAdmin(this.roleId); + } + + public static boolean isAdmin(Long roleId) + { + return roleId != null && 1L == roleId; + } + + @NotBlank(message = "角色名称不能为空") + @Size(min = 0, max = 30, message = "角色名称长度不能超过30个字符") + public String getRoleName() + { + return roleName; + } + + @NotBlank(message = "权限字符不能为空") + @Size(min = 0, max = 100, message = "权限字符长度不能超过100个字符") + public String getRoleKey() + { + return roleKey; + } + + @NotBlank(message = "显示顺序不能为空") + public String getRoleSort() + { + return roleSort; + } + +} diff --git a/src/main/java/com/jxy/windminer/entity/system/SysRoleDto.java b/src/main/java/com/jxy/windminer/entity/system/SysRoleDto.java new file mode 100644 index 0000000..1ac5574 --- /dev/null +++ b/src/main/java/com/jxy/windminer/entity/system/SysRoleDto.java @@ -0,0 +1,26 @@ +package com.jxy.windminer.entity.system; + +import lombok.Data; + +import java.util.Date; + +/** + * @Description TODO + * @Date 2023/1/4 15:41 + * @Author 杜懿 + */ +@Data +public class SysRoleDto { + + private static final long serialVersionUID = 1941856464L; + + /** 角色ID */ + private Long roleId; + + /** 角色名称 */ + private String roleName; + + /** 角色权限 */ + private String roleKey; + +} diff --git a/src/main/java/com/jxy/windminer/entity/system/SysRoleMenu.java b/src/main/java/com/jxy/windminer/entity/system/SysRoleMenu.java new file mode 100644 index 0000000..e7b1899 --- /dev/null +++ b/src/main/java/com/jxy/windminer/entity/system/SysRoleMenu.java @@ -0,0 +1,46 @@ +package com.jxy.windminer.entity.system; + +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; + +/** + * 角色和菜单关联 sys_role_menu + * + * @author 杜懿 + */ +public class SysRoleMenu +{ + /** 角色ID */ + private Long roleId; + + /** 菜单ID */ + private Long menuId; + + public Long getRoleId() + { + return roleId; + } + + public void setRoleId(Long roleId) + { + this.roleId = roleId; + } + + public Long getMenuId() + { + return menuId; + } + + public void setMenuId(Long menuId) + { + this.menuId = menuId; + } + + @Override + public String toString() { + return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) + .append("roleId", getRoleId()) + .append("menuId", getMenuId()) + .toString(); + } +} diff --git a/src/main/java/com/jxy/windminer/entity/system/SysUser.java b/src/main/java/com/jxy/windminer/entity/system/SysUser.java new file mode 100644 index 0000000..09c2492 --- /dev/null +++ b/src/main/java/com/jxy/windminer/entity/system/SysUser.java @@ -0,0 +1,119 @@ +package com.jxy.windminer.entity.system; + +import com.jxy.windminer.common.web.entity.BaseEntity; +import lombok.Data; + +import javax.validation.constraints.Email; +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.Size; +import java.util.Date; +import java.util.List; + +/** + * @Description 用户实体类 + * @Date 2022/5/12 10:59 + * @Author 杜懿 + */ +@Data +public class SysUser extends BaseEntity { + + private static final long serialVersionUID = 2417642483325474318L; + + /** 用户ID */ + private Long userId; + + /** 部门ID */ + private Long deptId; + + /** 用户账号 */ + private String userName; + + /** 用户昵称 */ + private String nickName; + + /** 用户邮箱 */ + private String email; + + /** 手机号码 */ + private String phone; + + /** 用户性别 */ + private String sex; + + /** 用户头像 */ + private String avatar; + + /** 密码 */ + private String password; + + /** 帐号状态(0正常 1停用) */ + private String status; + + /** 删除标志(0代表存在 2代表删除) */ + private String delFlag; + + /** 最后登录IP */ + private String loginIp; + + /** 登录次数 */ + private Long loginCount; + + /** 最后登录时间 */ + private Date loginDate; + + /** 部门对象 */ + private SysDept dept; + + /** 角色对象 */ + private List roles; + + /** 角色组 用户角色更新操作使用*/ + private Long[] roleIds; + + /** 岗位组 */ + private Long[] postIds; + + /** 角色ID */ + private Long roleId; + + private int createFrom; + + public boolean isAdmin() + { + return isAdmin(this.userId); + } + + public static boolean isAdmin(Long userId) + { + return userId != null && 1L == userId; + } + + // TODO: xss + @Size(min = 0, max = 30, message = "用户昵称长度不能超过30个字符") + public String getNickName() + { + return nickName; + } + + // TODO: xss + @NotBlank(message = "用户账号不能为空") + @Size(min = 0, max = 30, message = "用户账号长度不能超过30个字符") + public String getUserName() + { + return userName; + } + + @Email(message = "邮箱格式不正确") + @Size(min = 0, max = 50, message = "邮箱长度不能超过50个字符") + public String getEmail() + { + return email; + } + + @Size(min = 0, max = 11, message = "手机号码长度不能超过11个字符") + public String getPhone() + { + return phone; + } + +} diff --git a/src/main/java/com/jxy/windminer/entity/system/SysUserDto.java b/src/main/java/com/jxy/windminer/entity/system/SysUserDto.java new file mode 100644 index 0000000..861f9e0 --- /dev/null +++ b/src/main/java/com/jxy/windminer/entity/system/SysUserDto.java @@ -0,0 +1,55 @@ +package com.jxy.windminer.entity.system; + +import lombok.Data; + +import java.util.Date; + +/** + * @Description TODO + * @Date 2023/1/4 15:41 + * @Author 杜懿 + */ +@Data +public class SysUserDto { + + + private static final long serialVersionUID = 1L; + + /** 用户ID */ + private Long userId; + + /** 用户账号 */ + private String userName; + + /** 用户昵称 */ + private String nickName; + + /** 用户邮箱 */ + private String email; + + /** 手机号码 */ + private String phone; + + /** 用户性别 */ + private String sex; + + /** 帐号状态(0正常 1停用) */ + //private String status; + + /** 删除标志(0代表存在 2代表删除) */ + //private String delFlag; + + /** 最后登录IP */ + private String loginIp; + + /** 登录次数 */ + private Long loginCount; + + /** 最后登录时间 */ + private Date loginDate; + + /** 角色对象 */ + private SysRoleDto role; + + +} diff --git a/src/main/java/com/jxy/windminer/entity/system/SysUserRole.java b/src/main/java/com/jxy/windminer/entity/system/SysUserRole.java new file mode 100644 index 0000000..3de9422 --- /dev/null +++ b/src/main/java/com/jxy/windminer/entity/system/SysUserRole.java @@ -0,0 +1,19 @@ +package com.jxy.windminer.entity.system; + +import lombok.Data; + +/** + * @Description 用户角色中间表实体 + * @Date 2022/5/12 10:47 + * @Author 杜懿 + */ +@Data +public class SysUserRole { + + /** 用户ID */ + private Long userId; + + /** 角色ID */ + private Long roleId; + +} diff --git a/src/main/java/com/jxy/windminer/enums/AccessoryStatus.java b/src/main/java/com/jxy/windminer/enums/AccessoryStatus.java new file mode 100644 index 0000000..e21e5bc --- /dev/null +++ b/src/main/java/com/jxy/windminer/enums/AccessoryStatus.java @@ -0,0 +1,43 @@ +package com.jxy.windminer.enums; + +/** + * @Description 配件类型 + * @Date 2024/6/04 11:15 + * @Author dy + */ +public enum AccessoryStatus { +// + POWER(0,"电源"), + BOARD(1,"主板"), + DEFAULT(999,""); + + private final int code; + private final String type; + + AccessoryStatus(int code, String type) + { + this.code = code; + this.type = type; + } + + public int getCode() + { + return code; + } + + public String getType() + { + return type; + } + + public static String getTypeByCode(int code){ + for (AccessoryStatus t : AccessoryStatus.values()) { + if(t.getCode()==code){ + return t.getType(); + } + } + return ""; + } + + +} diff --git a/src/main/java/com/jxy/windminer/enums/OrderFrom.java b/src/main/java/com/jxy/windminer/enums/OrderFrom.java new file mode 100644 index 0000000..0e36438 --- /dev/null +++ b/src/main/java/com/jxy/windminer/enums/OrderFrom.java @@ -0,0 +1,42 @@ +package com.jxy.windminer.enums; + +/** + * @Description 支付状态枚举类 + * @Date 2022/5/20 11:15 + * @Author 杜懿 + */ +public enum OrderFrom { +// + WORDPRESS(0,"商城订单"), + TICKET(1,"维修工单"); + + private final int code; + private final String type; + + OrderFrom(int code, String type) + { + this.code = code; + this.type = type; + } + + public int getCode() + { + return code; + } + + public String getType() + { + return type; + } + + public static String getTypeByCode(int code){ + for (OrderFrom t : OrderFrom.values()) { + if(t.getCode()==code){ + return t.getType(); + } + } + return ""; + } + + +} diff --git a/src/main/java/com/jxy/windminer/enums/PayStatus.java b/src/main/java/com/jxy/windminer/enums/PayStatus.java new file mode 100644 index 0000000..6e25a55 --- /dev/null +++ b/src/main/java/com/jxy/windminer/enums/PayStatus.java @@ -0,0 +1,43 @@ +package com.jxy.windminer.enums; + +/** + * @Description 币价查询范围条件 + * @Date 2022/5/20 11:15 + * @Author 杜懿 + */ +public enum PayStatus { +// + FAIL(0,"付款失败"), + LESS(1,"未完全付款"), + SUCCESS(2,"已付款"); + + private final int code; + private final String type; + + PayStatus(int code, String type) + { + this.code = code; + this.type = type; + } + + public int getCode() + { + return code; + } + + public String getType() + { + return type; + } + + public static String getTypeByCode(int code){ + for (PayStatus t : PayStatus.values()) { + if(t.getCode()==code){ + return t.getType(); + } + } + return ""; + } + + +} diff --git a/src/main/java/com/jxy/windminer/enums/TheWindMinerLogisticalInfo.java b/src/main/java/com/jxy/windminer/enums/TheWindMinerLogisticalInfo.java new file mode 100644 index 0000000..bb1d762 --- /dev/null +++ b/src/main/java/com/jxy/windminer/enums/TheWindMinerLogisticalInfo.java @@ -0,0 +1,50 @@ +package com.jxy.windminer.enums; + +/** + * @Description 公司基本物流信息 + * @Date 2022/5/20 11:15 + * @Author 杜懿 + */ +public enum TheWindMinerLogisticalInfo { +//ps:4是后台人员已回复用户但用户还未读 13是用户已读 + INFO_ONE(1,"李工","宝安区西乡街道固戍二路裕兴创谷A栋302","19876587379"), + INFO_TWO(2,"追风收件人2号","中华人民共和国xx省xx市xx区xxxx","18888888889"); + + private final int code; + private final String name; + private final String address; + private final String phone; + + TheWindMinerLogisticalInfo(int code, String name,String address,String phone) + { + this.code = code; + this.name = name; + this.address = address; + this.phone = phone; + } + + public int getCode() + { + return code; + } + + public String getName() + { + return name; + } + + public String getAddress(){return address;} + + public String getPhone(){return phone;} + + public static TheWindMinerLogisticalInfo getInfoByCode(int code){ + for (TheWindMinerLogisticalInfo t : TheWindMinerLogisticalInfo.values()) { + if(t.getCode()==code){ + return INFO_ONE; + } + } + return null; + } + + +} diff --git a/src/main/java/com/jxy/windminer/enums/TicketStatusType.java b/src/main/java/com/jxy/windminer/enums/TicketStatusType.java new file mode 100644 index 0000000..2be9635 --- /dev/null +++ b/src/main/java/com/jxy/windminer/enums/TicketStatusType.java @@ -0,0 +1,53 @@ +package com.jxy.windminer.enums; + +/** + * @Description 币价查询范围条件 + * @Date 2022/5/20 11:15 + * @Author 杜懿 + */ +public enum TicketStatusType { +//ps:4是后台人员已回复用户但用户还未读 13是用户已读 + STATUS_NULL(0,""), + STATUS_ONE(1,"待处理"), + STATUS_TWO(2,"客服处理中"), + STATUS_THREE(3,"等待收货中"), + STATUS_FOUR(4,"已收货"), + STATUS_FIVE(5,"已报价,等待付款"), + STATUS_SIX(6,"已付款"), + STATUS_SEVEN(7,"正在维修"), + STATUS_EIGHT(8,"维修流程结束,等待发回"), + STATUS_NINE(9,"已发回"), + STATUS_TEN(10,"已完结"), + STATUS_CHECKING(20,"确认付款中"), + STATUS_WAITING(21,"待寄件"); + + private final int code; + private final String status; + + TicketStatusType(int code, String status) + { + this.code = code; + this.status = status; + } + + public int getCode() + { + return code; + } + + public String getStatus() + { + return status; + } + + public static String getStatusByCode(int code){ + for (TicketStatusType t : TicketStatusType.values()) { + if(t.getCode()==code){ + return t.getStatus(); + } + } + return ""; + } + + +} diff --git a/src/main/java/com/jxy/windminer/enums/TicketType.java b/src/main/java/com/jxy/windminer/enums/TicketType.java new file mode 100644 index 0000000..8ea915b --- /dev/null +++ b/src/main/java/com/jxy/windminer/enums/TicketType.java @@ -0,0 +1,42 @@ +package com.jxy.windminer.enums; + +/** + * @Description 币价查询范围条件 + * @Date 2022/5/20 11:15 + * @Author 杜懿 + */ +public enum TicketType { +//ps:4是后台人员已回复用户但用户还未读 13是用户已读 + ONLINE(0,"线上技术支持"), + OFFLINE(1,"线下维修"); + + private final int code; + private final String type; + + TicketType(int code, String type) + { + this.code = code; + this.type = type; + } + + public int getCode() + { + return code; + } + + public String getType() + { + return type; + } + + public static String getTypeByCode(int code){ + for (TicketType t : TicketType.values()) { + if(t.getCode()==code){ + return t.getType(); + } + } + return ""; + } + + +} diff --git a/src/main/java/com/jxy/windminer/enums/WCOrderStatus.java b/src/main/java/com/jxy/windminer/enums/WCOrderStatus.java new file mode 100644 index 0000000..d85e5db --- /dev/null +++ b/src/main/java/com/jxy/windminer/enums/WCOrderStatus.java @@ -0,0 +1,42 @@ +package com.jxy.windminer.enums; + +/** + * @Description wordpress订单状态 枚举类 + * @Date 2022/5/20 11:15 + * @Author 杜懿 + */ +public enum WCOrderStatus { +//pending processing on-hold completed cancelled refunded failed trash pending + PENDING("wc-pending","待付款"), + PROCESSING("wc-processing","正在处理"), + ON_HOLD("wc-on-hold","保留"), + COMPLETED("wc-completed","已完成"), + CANCELLED("wc-cancelled","已取消"), + REFUNDED("wc-refunded","已退款"), + FAILED("wc-failed","失败"), + DEFAULT("",""); + + private final String enStatus; + + private final String cnStatus; + + WCOrderStatus(String enStatus, String cnStatus) + { + this.enStatus = enStatus; + this.cnStatus = cnStatus; + } + + public String getEnStatus() + { + return enStatus; + } + + public String getCnStatus() + { + return cnStatus; + } + + + + +} diff --git a/src/main/java/com/jxy/windminer/filter/XssFilter.java b/src/main/java/com/jxy/windminer/filter/XssFilter.java new file mode 100644 index 0000000..c2ea89f --- /dev/null +++ b/src/main/java/com/jxy/windminer/filter/XssFilter.java @@ -0,0 +1,70 @@ +package com.jxy.windminer.filter; + +import com.jxy.windminer.common.enums.HttpMethod; +import com.jxy.windminer.common.utils.StringUtils; + +import javax.servlet.*; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +/** + * @Description TODO + * @Date 2024/5/30 11:20 + * @Author 杜懿 + */ +public class XssFilter implements Filter { + /** + * 排除链接 + */ + public List excludes = new ArrayList<>(); + + @Override + public void init(FilterConfig filterConfig) throws ServletException + { + String tempExcludes = filterConfig.getInitParameter("excludes"); + if (StringUtils.isNotEmpty(tempExcludes)) + { + String[] urls = tempExcludes.split(","); + for (String url : urls) + { + excludes.add(url); + } + } + } + + @Override + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) + throws IOException, ServletException + { + HttpServletRequest req = (HttpServletRequest) request; + HttpServletResponse resp = (HttpServletResponse) response; + if (handleExcludeURL(req, resp)) + { + chain.doFilter(request, response); + return; + } + XssHttpServletRequestWrapper xssRequest = new XssHttpServletRequestWrapper((HttpServletRequest) request); + chain.doFilter(xssRequest, response); + } + + private boolean handleExcludeURL(HttpServletRequest request, HttpServletResponse response) + { + String url = request.getServletPath(); + String method = request.getMethod(); + // GET DELETE 不过滤 + if (method == null || HttpMethod.GET.matches(method) || HttpMethod.DELETE.matches(method)) + { + return true; + } + return StringUtils.matches(url, excludes); + } + + @Override + public void destroy() + { + + } +} diff --git a/src/main/java/com/jxy/windminer/filter/XssHttpServletRequestWrapper.java b/src/main/java/com/jxy/windminer/filter/XssHttpServletRequestWrapper.java new file mode 100644 index 0000000..593e1a0 --- /dev/null +++ b/src/main/java/com/jxy/windminer/filter/XssHttpServletRequestWrapper.java @@ -0,0 +1,112 @@ +package com.jxy.windminer.filter; + +import com.jxy.windminer.common.utils.StringUtils; +import com.jxy.windminer.common.utils.html.EscapeUtil; +import org.apache.commons.io.IOUtils; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; + +import javax.servlet.ReadListener; +import javax.servlet.ServletInputStream; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletRequestWrapper; +import java.io.ByteArrayInputStream; +import java.io.IOException; + +/** + * @Description TODO + * @Date 2024/5/30 11:21 + * @Author 杜懿 + */ +public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper { + + /** + * @param request + */ + public XssHttpServletRequestWrapper(HttpServletRequest request) + { + super(request); + } + + @Override + public String[] getParameterValues(String name) + { + String[] values = super.getParameterValues(name); + if (values != null) + { + int length = values.length; + String[] escapesValues = new String[length]; + for (int i = 0; i < length; i++) + { + // 防xss攻击和过滤前后空格 + escapesValues[i] = EscapeUtil.clean(values[i]).trim(); + } + return escapesValues; + } + return super.getParameterValues(name); + } + + @Override + public ServletInputStream getInputStream() throws IOException + { + // 非json类型,直接返回 + if (!isJsonRequest()) + { + return super.getInputStream(); + } + + // 为空,直接返回 + String json = IOUtils.toString(super.getInputStream(), "utf-8"); + if (StringUtils.isEmpty(json)) + { + return super.getInputStream(); + } + + // xss过滤 + json = EscapeUtil.clean(json).trim(); + byte[] jsonBytes = json.getBytes("utf-8"); + final ByteArrayInputStream bis = new ByteArrayInputStream(jsonBytes); + return new ServletInputStream() + { + @Override + public boolean isFinished() + { + return true; + } + + @Override + public boolean isReady() + { + return true; + } + + @Override + public int available() throws IOException + { + return jsonBytes.length; + } + + @Override + public void setReadListener(ReadListener readListener) + { + } + + @Override + public int read() throws IOException + { + return bis.read(); + } + }; + } + + /** + * 是否是Json请求 + * + */ + public boolean isJsonRequest() + { + String header = super.getHeader(HttpHeaders.CONTENT_TYPE); + return StringUtils.startsWithIgnoreCase(header, MediaType.APPLICATION_JSON_VALUE); + } + +} diff --git a/src/main/java/com/jxy/windminer/mapper/OrderMapper.java b/src/main/java/com/jxy/windminer/mapper/OrderMapper.java new file mode 100644 index 0000000..66b57b3 --- /dev/null +++ b/src/main/java/com/jxy/windminer/mapper/OrderMapper.java @@ -0,0 +1,164 @@ +package com.jxy.windminer.mapper; + +import com.jxy.windminer.dto.*; +import com.jxy.windminer.entity.OrderKeyAddress; +import com.jxy.windminer.vo.*; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +public interface OrderMapper { + + /** + * 获取未使用的地址数 + * @param chain + * @return + */ + public int getAddressNoUsedCount(@Param("chain") String chain); + + /** + * 获取未使用地址 + * @param chain + * @return + */ + public String getAddressNoUse(@Param("chain") String chain); + + /** + * 使用地址 + * @param address + * @param chain + * @return + */ + public boolean updateAddressToUse(@Param("address") String address,@Param("chain") String chain,@Param("key") String key); + + //public boolean updateAddressToUse(@Param("address") String address); + + /** + * 记录订单 支付地址对应关系 + * @param dto + * @return + */ + public int insertAddressAndUserId(@Param("dto") OrderKeyAddress dto); + + /** + * 获取订单对应的地址信息 + * @param key + * @return + */ + public OrderKeyAddress getAddressByOrderKey(@Param("key") String key); + + /** + * 订单信息入库 + * @param vo + * @param id + * @param orderFrom + * @param count + * @return + */ + public boolean insertOrderInfo(@Param("vo") OrderApplyVo vo,@Param("id") String id,@Param("orderFrom") int orderFrom,@Param("count") int count); + + /** + * 修改订单支付状态 + * @param vo + * @return + */ + public boolean updateOrderInfoPaymentByOrderKey(@Param("vo") OrderUpdateVo vo); + + /** + * 获取订单信息 联合WordPress查wordpress订单表的状态 + * @param key 订单key + * @return + */ + public OrderInfoDto selectOrderAndWpStatusInfoByOrderKey(@Param("key") String key); + + /** + * 获取订单基本信息和发货物流信息 从支付网关订单表order和发货物流表获取数据 + * @param vo + * @return + */ + public OrderAndOrderLogisticalDto selectOrderAndLogisticalInfoByOrderKey(@Param("vo") OrderKeyVo vo); + + /** + * 获取来自维修工单的订单基本信息 只从支付网关订单表order获取数据 + * @param key 订单key + * @return + */ + public TicketOrderBaseInfoDto selectTicketOrderInfoByOrderKey(@Param("key") String key); + + ///** + // * 获取订单信息 + // * @param key 订单key + // * @return + // */ + //public OrderInfoDto selectOrderInfoByOrderKey(@Param("key") String key); + + /** + * 获取已使用的地址 根据交易链或者订单来源查询 + * @param vo + * @return + */ + public List getAddressUsedListByChain(@Param("vo") AddressUsedVo vo); + + /** + * 获取已使用的地址 根据交易链或者订单来源查询 + * @param vo + * @return + */ + public List getTotalAddressList(@Param("vo") TotalAddressVo vo); + + /** + * 从wordpress 订单表中通过order_id获取订单基本信息 用户姓名、地址、手机号以及总金额 + * @param orderId + * @return + */ + public WPOrderAddressDto getOrderBaseInfoFromWP(@Param("orderId") int orderId); + + /** + * 从ticket 维修报价表中通过ticket_id获取维修订单基本信息 报价详情、手机号以及总金额 + * @param ticketId + * @return + */ + public TicketOrderPhoneAndDetailsDto getOrderBaseInfoFromTicket(@Param("ticketId") int ticketId); + + /** + * 从wordpress 订单-产品表中通过order_id获取订单产品对应信息 产品单价、数量、产品总金额、优惠券以及邮费等 + * @param orderId + * @return + */ + public List getOrderProductInfoFromWP(@Param("orderId") int orderId); + + /** + * 确认发货/自提(提交物流) 修改支付网关order if_send字段 + * @param key + * @param code + * @return + */ + public boolean confirmSendProductByOrderKey(@Param("key") String key,@Param("code") int code); + + /** + * 记录发货物流信息 + * @param key + * @return + */ + public boolean insertOrderLogistical(@Param("key") String key,@Param("company") String company,@Param("number") String number); + + /** + * 修改wordpress订单状态为完成 + * @param orderId + * @return + */ + public boolean completedWPOrder(@Param("orderId") int orderId); + + /** + * 根据order_id直接从wordpress表获取数据 + * @param id + * @return + */ + public String getShippingByOrderId(@Param("id") int id); + + + + //public OrderConfirmInfoDto selectOrderConfirmInfoByOrderKey(@Param("key") String key); + + //public OrderInfoDto selectOrderInfoByEmail(@Param("email") String email); +} diff --git a/src/main/java/com/jxy/windminer/mapper/SysFileMapper.java b/src/main/java/com/jxy/windminer/mapper/SysFileMapper.java new file mode 100644 index 0000000..600966b --- /dev/null +++ b/src/main/java/com/jxy/windminer/mapper/SysFileMapper.java @@ -0,0 +1,65 @@ +package com.jxy.windminer.mapper; + + +import com.jxy.windminer.dto.RepairRecordDto; +import com.jxy.windminer.dto.SysToolsDto; +import com.jxy.windminer.entity.JXYFile; +import com.jxy.windminer.entity.RepairRecord; +import com.jxy.windminer.entity.ToolsFile; +import com.jxy.windminer.vo.RepairRecordVo; +import com.jxy.windminer.vo.SysToolsVo; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +/** + * 系统相关文件 数据层 + * + * @author 杜懿 + */ +public interface SysFileMapper +{ + + //校验是否已有相同数据 所有参数相同 + public boolean checkRecordIfExist(@Param("vo") RepairRecord vo); + //插入新数据 + public boolean insertRecord(@Param("vo") RepairRecord vo); + //修改原有数据 暂时不需要该接口 + //public boolean updateRecord(@Param("vo") RepairRecord vo); + + //条件搜索维修记录 + public List selectRecordByCond(@Param("vo") RepairRecordVo vo); + + /** + * 添加工具类上传文件的保存信息 + * @param vo + * @param ver + * @return + */ + public boolean insertToolsFileInfo(@Param("vo") ToolsFile vo,@Param("ver") String ver); + + /** + * 根据文件名和版本号判断是否已上传 + * @param name + * @param ver + * @return + */ + public boolean checkToolsIfExist(@Param("name") String name, @Param("ver") String ver); + + /** + * 根据id获取文件相关信息 + * @param id + * @return + */ + public ToolsFile getToolsFileInfoById(int id); + + //分页搜索使用手册/工具列表 + public List selectToolsListByCond(@Param("vo") SysToolsVo vo); + + /** + * 根据id删除指定文件信息 + * @param id + * @return + */ + public boolean deleteToolFileById(@Param("id") int id); +} diff --git a/src/main/java/com/jxy/windminer/mapper/SysLogininforMapper.java b/src/main/java/com/jxy/windminer/mapper/SysLogininforMapper.java new file mode 100644 index 0000000..74171fb --- /dev/null +++ b/src/main/java/com/jxy/windminer/mapper/SysLogininforMapper.java @@ -0,0 +1,44 @@ +package com.jxy.windminer.mapper; + + +import com.jxy.windminer.entity.system.SysLogininfor; + +import java.util.List; + +/** + * 系统访问日志情况信息 数据层 + * + * @author 杜懿 + */ +public interface SysLogininforMapper +{ + /** + * 新增系统登录日志 + * + * @param logininfor 访问日志对象 + */ + public int insertLogininfor(SysLogininfor logininfor); + + /** + * 查询系统登录日志集合 + * + * @param logininfor 访问日志对象 + * @return 登录记录集合 + */ + public List selectLogininforList(SysLogininfor logininfor); + + /** + * 批量删除系统登录日志 + * + * @param infoIds 需要删除的登录日志ID + * @return 结果 + */ + public int deleteLogininforByIds(Long[] infoIds); + + /** + * 清空系统登录日志 + * + * @return 结果 + */ + public int cleanLogininfor(); +} diff --git a/src/main/java/com/jxy/windminer/mapper/SysMenuMapper.java b/src/main/java/com/jxy/windminer/mapper/SysMenuMapper.java new file mode 100644 index 0000000..2f9b7ce --- /dev/null +++ b/src/main/java/com/jxy/windminer/mapper/SysMenuMapper.java @@ -0,0 +1,20 @@ +package com.jxy.windminer.mapper; + +import java.util.List; + +/** + * @Description 角色数据层 + * @Date 2022/6/14 17:37 + * @Author 杜懿 + */ +public interface SysMenuMapper { + + /** + * 根据用户ID查询权限 + * + * @param userId 用户ID + * @return 权限列表 + */ + public List selectMenuPermsByUserId(Long userId); + +} diff --git a/src/main/java/com/jxy/windminer/mapper/SysOperLogMapper.java b/src/main/java/com/jxy/windminer/mapper/SysOperLogMapper.java new file mode 100644 index 0000000..1d0b9c7 --- /dev/null +++ b/src/main/java/com/jxy/windminer/mapper/SysOperLogMapper.java @@ -0,0 +1,49 @@ +package com.jxy.windminer.mapper; + +import com.jxy.windminer.entity.system.SysOperLog; + +import java.util.List; + +/** + * 操作日志 数据层 + * + * @author 杜懿 + */ +public interface SysOperLogMapper +{ + /** + * 新增操作日志 + * + * @param operLog 操作日志对象 + */ + public int insertOperlog(SysOperLog operLog); + + /** + * 查询系统操作日志集合 + * + * @param operLog 操作日志对象 + * @return 操作日志集合 + */ + public List selectOperLogList(SysOperLog operLog); + + /** + * 批量删除系统操作日志 + * + * @param operIds 需要删除的操作日志ID + * @return 结果 + */ + public int deleteOperLogByIds(Long[] operIds); + + /** + * 查询操作日志详细 + * + * @param operId 操作ID + * @return 操作日志对象 + */ + public SysOperLog selectOperLogById(Long operId); + + /** + * 清空操作日志 + */ + public void cleanOperLog(); +} diff --git a/src/main/java/com/jxy/windminer/mapper/SysRoleMapper.java b/src/main/java/com/jxy/windminer/mapper/SysRoleMapper.java new file mode 100644 index 0000000..3949be8 --- /dev/null +++ b/src/main/java/com/jxy/windminer/mapper/SysRoleMapper.java @@ -0,0 +1,55 @@ +package com.jxy.windminer.mapper; + +import com.jxy.windminer.entity.system.SysRole; + +import java.util.List; + +/** + * @Description 角色数据层 + * @Date 2022/6/14 17:37 + * @Author 杜懿 + */ +public interface SysRoleMapper { + + /** + * 根据条件分页查询角色数据 + * + * @param role 角色信息 + * @return 角色数据集合信息 + */ + public List selectRoleList(SysRole role); + + /** + * 根据用户ID查询角色 + * + * @param userId 用户ID + * @return 角色列表 + */ + public List selectRolePermissionByUserId(Long userId); + + /** + * 查询所有角色 + * + * @return 角色列表 + */ + public List selectRoleAll(); + + /** + * 根据用户ID获取角色选择框列表 + * + * @param userId 用户ID + * @return 选中角色ID列表 + */ + public List selectRoleListByUserId(Long userId); + + + /** + * 根据用户名查询角色 + * + * @param userName 用户名 + * @return 角色列表 + */ + public List selectRolesByUserName(String userName); + + public Long[] getResigesterId(); +} diff --git a/src/main/java/com/jxy/windminer/mapper/SysRoleMenuMapper.java b/src/main/java/com/jxy/windminer/mapper/SysRoleMenuMapper.java new file mode 100644 index 0000000..2275fca --- /dev/null +++ b/src/main/java/com/jxy/windminer/mapper/SysRoleMenuMapper.java @@ -0,0 +1,45 @@ +package com.jxy.windminer.mapper; + +import com.jxy.windminer.entity.system.SysRoleMenu; + +import java.util.List; + +/** + * 角色与菜单关联表 数据层 + * + * @author jxy + */ +public interface SysRoleMenuMapper +{ + /** + * 查询菜单使用数量 + * + * @param menuId 菜单ID + * @return 结果 + */ + public int checkMenuExistRole(Long menuId); + + /** + * 通过角色ID删除角色和菜单关联 + * + * @param roleId 角色ID + * @return 结果 + */ + public int deleteRoleMenuByRoleId(Long roleId); + + /** + * 批量删除角色菜单关联信息 + * + * @param ids 需要删除的数据ID + * @return 结果 + */ + public int deleteRoleMenu(Long[] ids); + + /** + * 批量新增角色菜单信息 + * + * @param roleMenuList 角色菜单列表 + * @return 结果 + */ + public int batchRoleMenu(List roleMenuList); +} diff --git a/src/main/java/com/jxy/windminer/mapper/SysUserMapper.java b/src/main/java/com/jxy/windminer/mapper/SysUserMapper.java new file mode 100644 index 0000000..8a883e6 --- /dev/null +++ b/src/main/java/com/jxy/windminer/mapper/SysUserMapper.java @@ -0,0 +1,122 @@ +package com.jxy.windminer.mapper; + +import com.jxy.windminer.entity.system.SysUser; +import org.apache.ibatis.annotations.MapKey; +import org.apache.ibatis.annotations.Param; + +import java.util.List; +import java.util.Map; + +/** + * @Description TODO + * @Date 2022/6/14 17:37 + * @Author 杜懿 + */ +public interface SysUserMapper { + + /** + * 根据条件分页查询用户列表 + * + * @param sysUser 用户信息 + * @return 用户信息集合信息 + */ + public List selectUserList(SysUser sysUser); + + /** + * 通过用户名查询用户 + * + * @param userName 用户名 + * @return 用户对象信息 + */ + public SysUser selectUserByUserName(String userName); + + /** + * 通过邮箱查询用户 + * + * @param email 邮箱 + * @return 用户对象信息 + */ + public SysUser selectUserByEmail(String email); + + /** + * 通过用户ID查询用户 + * + * @param userId 用户ID + * @return 用户对象信息 + */ + public SysUser selectUserById(Long userId); + + /** + * 校验用户名称是否唯一 + * + * @param userName 用户名称 + * @return 结果 + */ + public int checkUserNameUnique(String userName); + + /** + * 校验手机号码是否唯一 + * + * @param phonenumber 手机号码 + * @return 结果 + */ + public SysUser checkPhoneUnique(String phonenumber); + + /** + * 校验email是否唯一 + * + * @param email 用户邮箱 + * @return 结果 + */ + public SysUser checkEmailUnique(String email); + + /** + * 校验email是否存在 + * + * @param email 用户邮箱 + * @return 结果 + */ + public String checkEmailExist(String email); + + /** + * 新增用户信息 + * + * @param user 用户信息 + * @return 结果 + */ + public int insertUser(SysUser user); + + /** + * 修改用户信息 + * + * @param user 用户信息 + * @return 结果 + */ + public int updateUser(SysUser user); + + + /** + * 重置用户密码 + * + * @param userName 用户名 + * @param password 密码 + * @return 结果 + */ + public int resetUserPwd(@Param("userName") String userName, @Param("password") String password); + + + /** + * 重置用户密码 + * + * @param email 邮箱 + * @param password 密码 + * @return 结果 + */ + public int resetPwdByEmail(@Param("email") String email, @Param("password") String password); + + + @MapKey("user_id") + public Map getUserInfoMap(); + + +} diff --git a/src/main/java/com/jxy/windminer/mapper/SysUserRoleMapper.java b/src/main/java/com/jxy/windminer/mapper/SysUserRoleMapper.java new file mode 100644 index 0000000..d715804 --- /dev/null +++ b/src/main/java/com/jxy/windminer/mapper/SysUserRoleMapper.java @@ -0,0 +1,71 @@ +package com.jxy.windminer.mapper; + +import com.jxy.windminer.entity.system.SysUserRole; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +/** + * 用户与角色关联表 数据层 + * + * @author jxy + */ +public interface SysUserRoleMapper +{ + /** + * 通过用户ID删除用户和角色关联 + * + * @param userId 用户ID + * @return 结果 + */ + public int deleteUserRoleByUserId(Long userId); + + /** + * 批量删除用户和角色关联 + * + * @param ids 需要删除的数据ID + * @return 结果 + */ + public int deleteUserRole(Long[] ids); + + /** + * 通过角色ID查询角色使用数量 + * + * @param roleId 角色ID + * @return 结果 + */ + public int countUserRoleByRoleId(Long roleId); + + /** + * 批量新增用户角色信息 + * + * @param userRoleList 用户角色列表 + * @return 结果 + */ + public int batchUserRole(List userRoleList); + + /** + * 批量新增用户角色信息 + * + * @param userRole 用户角色列表 + * @return 结果 + */ + public int createUserRole(SysUserRole userRole); + + /** + * 删除用户和角色关联信息 + * + * @param userRole 用户和角色关联信息 + * @return 结果 + */ + public int deleteUserRoleInfo(SysUserRole userRole); + + /** + * 批量取消授权用户角色 + * + * @param roleId 角色ID + * @param userIds 需要删除的用户数据ID + * @return 结果 + */ + public int deleteUserRoleInfos(@Param("roleId") Long roleId, @Param("userIds") Long[] userIds); +} diff --git a/src/main/java/com/jxy/windminer/mapper/TicketMapper.java b/src/main/java/com/jxy/windminer/mapper/TicketMapper.java new file mode 100644 index 0000000..7cc1360 --- /dev/null +++ b/src/main/java/com/jxy/windminer/mapper/TicketMapper.java @@ -0,0 +1,327 @@ +package com.jxy.windminer.mapper; + +import com.jxy.windminer.dto.*; +import com.jxy.windminer.entity.JXYFile; +import com.jxy.windminer.entity.Ticket; +import com.jxy.windminer.vo.*; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +public interface TicketMapper { + + /** + * 修改工单状态 + * @param id + * @param status + * @return + */ + public boolean changeTicketStatus(@Param("id") long id, @Param("status") int status); + + /** + * 修改工单update_time + * @param id + * @return + */ + public boolean changeTicketUpdateTime(@Param("id") long id); + + /** + * 获取故障现象下拉列表数据 + * @return + */ + public List getFaults(); + + /** + * 根据机器编码查看机器保修期 + * @return + */ + public GuaranteeDto getGuaranteeBySerial(@Param("serial") String serial); + + /** + * 根据配件编码配件机器保修期 + * @return + */ + public GuaranteeDto getAccessoryBySerial(@Param("serial") String serial); + + /** + * 查看机器标号是否存在 + * @return + */ + public boolean checkSerialIfExist(@Param("serial") String serial); + + /** + * 修改工单状态和工单类型 + * @param id + * @param status + * @return + */ + public boolean changeTicketStatusAndType(@Param("id") long id, @Param("status") int status, @Param("type") int type); + + /** + * 修改工单状态、类型并且标记类型转换 + * @param id + * @return + */ + public boolean changeTicketTypeAndFlag(@Param("id") long id, @Param("type") int type); + + /** + * 修改工单和process状态 + * @param vo + * @return + */ + public boolean changeTicketStatusAndProcess(@Param("vo") RepairChangeStatusVo vo); + + public int checkTicketExist(@Param("id") long id); + + /** + * 新增工单 + * @param data + * @return + */ + public boolean insertTicket(@Param("data") Ticket data); + + + /** + * 新增工单支付相关 + * @param vo + * @param details + * @param orderKey + * @return + */ + public boolean insertTicketPayment(@Param("vo") QuotedVo vo,@Param("details") String details,@Param("orderKey") String orderKey); + + /** + * 修改工单支付相关 + * @param vo + * @return + */ + public boolean updateTicketPayment(@Param("vo") PaymentInfoVo vo); + + /** + * 获取工单支付相关 + * @param orderId + * @return + */ + public TicketPaymentDto getTicketPaymentDtoByOrderId(@Param("orderId") long orderId); + + + /** + * 工单补充内容提交工单信息 + * @param vo + * @return + */ + public boolean insertTicketSupplement(@Param("vo") ResubmitTicketVo vo); + + public Integer getTicketSupplementCountByTicketId(@Param("id") long id); + + + /** + * 根据工单id 获取工单补充提交内容 + * @param id + * @return + */ + public List getTicketSupplementListByTicketId(@Param("id") long id); + + + /** + * 根据工单id 获取工单附件 + * @param id + * @return + */ + public String getFileUrlsByTicketId(@Param("id") long id); + + /** + * 根据工单id 获取工单回复内容 + * @param id + * @return + */ + public List getTicketResponListByTicketId(@Param("id") long id); + + /** + * 查询个人工单列表 + * @return + * @param vo + */ + public List getPrivateTicketList(@Param("vo") PrivateTicketListVo vo,@Param("email") String email); + + /** + * 个人处理中的工单列表 + * @return + */ + public List getPrivateProcessingTicketList(@Param("vo") PrivateTicketListVo vo,@Param("email") String email); + + /** + * 个人 已完结的工单列表 + * @return + */ + public List getPrivateEndTicketList(@Param("vo") PrivateTicketListVo vo,@Param("email") String email); + + + /** + * 查看工单详情 + * @param id + * @return + */ + public TicketDto getTicketById(@Param("id") long id); + + /** + * 根据工单id查看工单状态 + * @param id + * @return + */ + public int getTicketStatusById(@Param("id") long id); + /** + * 查看工单详情 + * @param id + * @return + */ + public TicketDetailsDto getBackendTicketById(@Param("id") long id); + + /** + * 查看工单详情 带用户寄件物流信息 + * @param id + * @return + */ + public TicketDetails3Dto geTicketDetails3ById(@Param("id") long id); + + /** + * 查看工单详情 带用户收件物流信息 + * @param id + * @return + */ + public ReturnInfoDto getReturnDetailsById(@Param("id") long id); + + /** + * 查看个人工单详情 + * @param id + * @param email + * @return + */ + public TicketPrivateDto getPrivateTicketById(@Param("id") long id,@Param("email") String email); + + /** + * 查看个人工单详情 + * @param email + * @param email + * @return + */ + public List getPrivateRepairTicket(@Param("email") String email); + + /** + * 后台根据状态查询工单列表 + * @param status + * @return + */ + public List getBackendTicketList(@Param("status") int status); + + /** + * 根据状态和类型查询工单列表 + * @param vo + * @return + */ + public List getTicketListByStatusAndType(@Param("vo") StatusVo vo, @Param("type") int type); + + /** + * 根据状态和类型查询工单列表 + * @param vo + * @return + */ + public List getAccountTicketListByStatus(@Param("vo") StatusVo vo); + + /** + * 财务 获取所有已付款工单 + * @param vo + * @return + */ + public List getPaySuccessAccountTicketList(@Param("vo") StatusVo vo); + + /** + * 后台获取总览工单 条件搜索 工单号/用户邮箱/时间/费用/订单 + * @param vo + * @return + */ + public List getTicketListByCond(@Param("vo") AllTicketListVo vo); + + /** + * 根据时间和状态查询未关闭工单列表 + * @param vo + * @return + */ + public List getTicketListByTime(@Param("vo") QueryVo vo); + + /** + * 添加工单回复 + * @param vo + * @return + */ + public boolean insertRespon(@Param("vo") ResponTicketVo vo, @Param("name") String name); + + + /** + * 添加上传文件的保存信息 + * @param vo + * @return + */ + public boolean insertFileInfo(@Param("vo") JXYFile vo); + + /** + * 根据id获取文件相关信息 + * @param id + * @return + */ + public JXYFile getFileInfoById(Long id); + + public boolean checkRepairLogisticalExistByTicketId(@Param("id") long id); + + public boolean checkReturnLogisticalExistByTicketId(@Param("id") long id); + + /** + * 收件信息表记录收货地址 此时不填入用户寄件信息 + * @param vo + * @return + */ + public boolean insertRepairLogisticalInfo(@Param("vo") RepairConfirmVo vo); + + /** + * 回退信息表记录退货地址 此时不填入用户收货信息 + * @param vo + * @return + */ + public boolean insertReturnLogisticalInfo(@Param("vo") RepairConfirmVo vo); + + /** + * 收件信息表记录用户寄件信息 + * @param vo + * @return + */ + public boolean updateRepairLogisticalInfo(@Param("vo") UserLogisticalVo vo); + + /** + * 回退信息表记录用户收货信息 + * @param vo + * @return + */ + public boolean updateReturnLogisticalInfo(@Param("vo") UserLogisticalVo vo); + + /** + * 回退信息表记录 + * @param vo + * @return + */ + public boolean updateReturnLogisticalCompanyInfo(@Param("vo") ReturnLogisticalVo vo); + + + /** + * 后台查看维修工单详情 ticket表基本信息+物流表基本信息 + * @param id + * @return + */ + public BaseRepairInfoDto getRepairLogisticalInfoById(@Param("id") long id); + + /** + * 查看工单详情 带用户收件物流信息 + * @param id + * @return + */ + public AccountDetailsDto getAccountDetailsById(@Param("id") long id); +} diff --git a/src/main/java/com/jxy/windminer/service/FileService.java b/src/main/java/com/jxy/windminer/service/FileService.java new file mode 100644 index 0000000..4a7a337 --- /dev/null +++ b/src/main/java/com/jxy/windminer/service/FileService.java @@ -0,0 +1,31 @@ +package com.jxy.windminer.service; + + +import com.jxy.windminer.common.Result.R; +import com.jxy.windminer.entity.SysFile; +import org.springframework.web.multipart.MultipartFile; + + +/** + * @Description 追风工单系统服务类 + * @Date 2022/5/19 17:40 + * @Author 杜懿 + */ +public interface FileService { + + + /** + * 文件上传 + * @return + */ + public R uploadFile(MultipartFile file); + + + /** + * 工具类文件上传 + * @return + */ + public R uploadToolsFile(MultipartFile file,String fileName); + + +} diff --git a/src/main/java/com/jxy/windminer/service/ISysFileService.java b/src/main/java/com/jxy/windminer/service/ISysFileService.java new file mode 100644 index 0000000..dd21c71 --- /dev/null +++ b/src/main/java/com/jxy/windminer/service/ISysFileService.java @@ -0,0 +1,31 @@ +package com.jxy.windminer.service; + +import org.springframework.web.multipart.MultipartFile; + +/** + * 文件上传接口 + * + * @author jxy + */ +public interface ISysFileService +{ + /** + * 文件上传接口 + * + * @param file 上传的文件 + * @return 访问地址 + * @throws Exception + */ + public String uploadFile(MultipartFile file) throws Exception; + + /** + * 相关工具类文件上传接口 + * + * @param file 上传的文件 + * @param fileName + * @return 访问地址 + * @throws Exception + */ + public String uploadToolsFile(MultipartFile file,String fileName) throws Exception; + +} diff --git a/src/main/java/com/jxy/windminer/service/LocalSysFileServiceImpl.java b/src/main/java/com/jxy/windminer/service/LocalSysFileServiceImpl.java new file mode 100644 index 0000000..0e5f85a --- /dev/null +++ b/src/main/java/com/jxy/windminer/service/LocalSysFileServiceImpl.java @@ -0,0 +1,71 @@ +package com.jxy.windminer.service; +import com.jxy.windminer.common.utils.FileUploadUtils; +import com.jxy.windminer.common.utils.file.MimeTypeUtils; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Primary; +import org.springframework.stereotype.Service; +import org.springframework.web.multipart.MultipartFile; + +import java.io.File; + +/** + * 本地文件存储 + * + * @author jxy + */ +@Primary +@Service +public class LocalSysFileServiceImpl implements ISysFileService +{ + /** + * 资源映射路径 前缀 + */ + @Value("/statics") + public String localFilePrefix; + + /** + * 域名或本机访问地址 + */ + //@Value("http://10.168.2.125:9808") + @Value("http://54.254.7.187:9808") + public String domain; + + /** + * 上传文件存储在本地的根路径 + */ + //@Value("D:/marketall/uploadPath") + @Value("/home/windminer/ticket") + private String localFilePath; + + /** + * 本地文件上传接口 + * + * @param file 上传的文件 + * @return 访问地址 + * @throws Exception + */ + @Override + public String uploadFile(MultipartFile file) throws Exception + { + String name = FileUploadUtils.upload(localFilePath, file , MimeTypeUtils.DEFAULT_WINDMINER_EXTENSION); + String url = domain + localFilePrefix + name; + return url; + } + + /** + * 本地使用手册等相关工具文件文件上传接口 + * + * @param file 上传的文件 + * @param fileName + * @return 访问地址 + * @throws Exception + */ + @Override + public String uploadToolsFile(MultipartFile file, String fileName) throws Exception + { + String name = FileUploadUtils.toolsUpload(localFilePath + "/tools", file ,fileName, MimeTypeUtils.DEFAULT_ALLOWED_EXTENSION); + String url = domain + localFilePrefix+ "/tools" + name; + System.out.println("url"+url); + return url; + } +} diff --git a/src/main/java/com/jxy/windminer/service/MailService.java b/src/main/java/com/jxy/windminer/service/MailService.java new file mode 100644 index 0000000..1033b5e --- /dev/null +++ b/src/main/java/com/jxy/windminer/service/MailService.java @@ -0,0 +1,110 @@ +package com.jxy.windminer.service; + + +import com.jxy.windminer.common.Result.R; +import com.jxy.windminer.dto.OrderInfoDto; +import com.jxy.windminer.entity.auth.GetEmailCodeEntity; +import com.jxy.windminer.vo.QuotedVo; + +/** + * @Description TODO + * @Date 2022/11/18 14:56 + * @Author 杜懿 + */ +public interface MailService { + /** + * 发送纯文本邮件 + * @param to + * @param subject + * @param text + */ + public void sendTextMailMessage(String to,String subject,String text); + + /** + * 发送html邮件 + * @param to 邮件收信人 + * @param subject 邮件主题 + * @param content 邮件内容 + */ + public void sendHtmlMailMessage(String to,String subject,String content); + + /** + * 发送带附件的邮件 + * @param to 邮件收信人 + * @param subject 邮件主题 + * @param content 邮件内容 + * @param filePath 附件路径 + */ + public void sendAttachmentMailMessage(String to,String subject,String content,String filePath); + + /** + * 发送注册邮箱验证码 + * @param to + * @param code + */ + public void sendCodeMailMessage(String to, String code); + + /** + * 发送重置密码邮箱验证码 + * @param to + * @param code + */ + public void sendResetPwdMailMessage(String to, String code); + + + public R emailCode(GetEmailCodeEntity entity); + + + public R resetPwdCode(GetEmailCodeEntity entity); + + /** + * 发送工单回复后通知用户邮件 + * @param to + * @param ticketId + */ + public void sendRespondNoticeHTMLMailMessage(String to, int ticketId); + + + /** + * 发送需要线下维修通知用户邮件 + * @param to + * @param ticketId + */ + public void sendNeedRepairNoticeHTMLMailMessage(String to, long ticketId, String name, String address, String phone); + + /** + * 发送线上转线下维修通知用户邮件 + * @param to + * @param ticketId + */ + public void sendChangeToRepairNoticeHTMLMailMessage(String to,long ticketId); + + + /** + * 发送已收货通知邮件 + * @param to + * @param serialNo + */ + public void sendReceptMachineNoticeHTMLMailMessage(String to, String serialNo); + + + /** + * 发送提示用户支付金额不达标提示用户邮件 + * @param to + * @param ticketId + */ + public void sendPayLessNoticeHTMLMailMessage(String to, int ticketId); + + /** + * 发送维修报价详情邮件 + * @param to + * @param serialNo + * @param orderKey + * @param details + */ + public void sendRepairQuotationNoticeHTMLMailMessage(String to, String serialNo, QuotedVo vo,String orderKey,String details); + + public void sendWCPaymentSuccessMailMessage(OrderInfoDto order, String company, String no); + + public void sendShippingStartHTMLMailMessage(OrderInfoDto order, String company, String no); +} diff --git a/src/main/java/com/jxy/windminer/service/OrderService.java b/src/main/java/com/jxy/windminer/service/OrderService.java new file mode 100644 index 0000000..878ea14 --- /dev/null +++ b/src/main/java/com/jxy/windminer/service/OrderService.java @@ -0,0 +1,88 @@ +package com.jxy.windminer.service; + +import com.jxy.windminer.common.web.Result.AjaxResult; +import com.jxy.windminer.dto.AddressUsedDto; +import com.jxy.windminer.dto.ConfirmListDto; +import com.jxy.windminer.dto.TotalAddressDto; +import com.jxy.windminer.entity.system.SysOperLog; +import com.jxy.windminer.vo.*; + +import java.util.List; + +/** + * 操作日志 服务层 + * + * @author 杜懿 + */ +public interface OrderService +{ + /** + * 处理订单支付 + * + * @param vo + * @return 结果 + */ + public AjaxResult handleOrder(OrderHandleVo vo); + + /** + * 根据邮箱和支付链获取支付地址 + * + * @param vo 邮箱和chain + * @return 结果 + */ + public AjaxResult getPayAddressByUserEmailAndType(AddressVo vo); + + /** + * 获取订单信息 并存入数据库 + * + * @return 结果 + */ + public AjaxResult getPaymentOrderInfo(OrderApplyVo vo); + + /** + * 根据order_key获取订单详情 + * + * @return 结果 + * @param vo + */ + public AjaxResult getOrderInfoByOrderKey(OrderKeyVo vo); + + /** + * 查看剩余可用地址数 + * + * @return 结果 + */ + public AjaxResult getAddressSurplus(); + + /** + * 获取已支付的地址列表 + * + * @return 结果 + * @param vo + */ + public List getUesdAddressList(AddressUsedVo vo); + + /** + * 获取已上传的所有地址列表 + * + * @return 结果 + * @param vo + */ + public List getTotalAddressList(TotalAddressVo vo); + + /** + * 查看订单详情 根据order_key + * + * @return 结果 + * @param vo + */ + public AjaxResult getOrderDetails(OrderKeyVo vo); + + /** + * 确认发货 填写物流信息 + * + * @return 结果 + * @param vo + */ + public AjaxResult logisticsConfirm(OrderLogisticsConfirmVo vo); +} diff --git a/src/main/java/com/jxy/windminer/service/SysFileService.java b/src/main/java/com/jxy/windminer/service/SysFileService.java new file mode 100644 index 0000000..67d9589 --- /dev/null +++ b/src/main/java/com/jxy/windminer/service/SysFileService.java @@ -0,0 +1,80 @@ +package com.jxy.windminer.service; + +import com.jxy.windminer.common.web.Result.AjaxResult; +import com.jxy.windminer.dto.RepairRecordDto; +import com.jxy.windminer.dto.SysToolsDto; +import com.jxy.windminer.entity.RepairRecord; +import com.jxy.windminer.vo.AddRepairRecordVo; +import com.jxy.windminer.vo.RepairRecordVo; +import com.jxy.windminer.vo.SysToolsVo; +import org.springframework.web.multipart.MultipartFile; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.sql.SQLException; +import java.util.List; + +public interface SysFileService { + + /** + * 导入维修记录数据 + * + * @param list 维修记录列表 + * @param isUpdateSupport 是否更新支持,如果已存在,则进行更新数据 + * @param operName 记录提交人 + * @return 结果 + */ + public String importRepairRecord(List list, Boolean isUpdateSupport, String operName); + + /** + * 查询维修记录数据 + * + * @param vo 维修记录查询条件 + * @return 结果 + */ + public List getRepairRecord(RepairRecordVo vo); + + /** + * 上传使用手册、使用工具等相关文件 + * + * @param file 系统文件 + * @param ver 版本号 + * @param md5sum + * @return 结果 + */ + public AjaxResult uploadFile(MultipartFile file, String ver, String md5sum) throws SQLException; + + /** + * 下载使用手册、使用工具等相关文件 + * + * @param id 系统文件数据库对应id + * @return 结果 + */ + public void downloadById(int id, HttpServletRequest request, HttpServletResponse response) throws Exception; + + /** + * 获取使用工具等相关文件列表 + * + * @param vo 查询条件 + * @return 结果 + */ + public List getToolsList(SysToolsVo vo); + + + /** + * 提交维修记录 + * + * @param vo 查询条件 + * @return 结果 + */ + public AjaxResult addReparRecord(AddRepairRecordVo vo); + + /** + * 删除指定使用工具文件 + * + * @param id 查询条件 + * @return 结果 + */ + public AjaxResult deleteToolById(int id); + +} diff --git a/src/main/java/com/jxy/windminer/service/SysLoginService.java b/src/main/java/com/jxy/windminer/service/SysLoginService.java new file mode 100644 index 0000000..124ef36 --- /dev/null +++ b/src/main/java/com/jxy/windminer/service/SysLoginService.java @@ -0,0 +1,540 @@ +package com.jxy.windminer.service; + +import com.alibaba.fastjson.JSON; +import com.jxy.windminer.common.RedisTransKey; +import com.jxy.windminer.common.Result.R; +import com.jxy.windminer.common.constant.Constants; +import com.jxy.windminer.common.constant.SecurityConstants; +import com.jxy.windminer.common.constant.UserConstants; +import com.jxy.windminer.common.context.SecurityContextHolder; +import com.jxy.windminer.common.enums.UserStatus; +import com.jxy.windminer.common.exception.ServiceException; +import com.jxy.windminer.common.redis.service.RedisService; +import com.jxy.windminer.common.security.service.TokenService; +import com.jxy.windminer.common.security.utils.SecurityUtils; +import com.jxy.windminer.common.text.Convert; +import com.jxy.windminer.common.utils.DateUtils; +import com.jxy.windminer.common.utils.PwdUtils; +import com.jxy.windminer.common.utils.ServletUtils; +import com.jxy.windminer.common.utils.StringUtils; +import com.jxy.windminer.common.utils.ip.IpUtils; +import com.jxy.windminer.common.utils.sign.RsaUtils; +import com.jxy.windminer.entity.auth.*; +import com.jxy.windminer.entity.system.SysLogininfor; +import com.jxy.windminer.entity.system.SysUser; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.TimeUnit; + + +/** + * @Description 登录校验方法 + * @Date 2022/5/12 16:19 + * @Author 杜懿 + */ +@Component +public class SysLoginService { + + @Autowired + private SysLogininforService logininforService; + + @Autowired + private SysUserService sysUserService; + + @Autowired + private MailService mailService; + + @Autowired + private TokenService tokenService; + + @Autowired + private RedisService redisService; + + public static String PWD_REGEX="^(?![A-Za-z0-9]+$)(?![a-z0-9\\W]+$)(?![A-Za-z\\W]+$)(?![A-Z0-9\\W]+$)[a-zA-Z0-9\\W]{8,32}$"; + + + /** + * 登录 + */ + public R login(LoginBody loginBody) + { + String username = loginBody.getUserName(); + //String password= loginBody.getPassword(); + String password=""; + try { + password = RsaUtils.decryptByPrivateKey(loginBody.getPassword()); + password = StringUtils.clean(password); + }catch (Exception e){ + return R.fail(401,"加密密码传参有误"); + } + + System.out.println("密码:"+password); + // 用户名或密码为空 错误 + if (StringUtils.isAnyBlank(username)) + { + recordLogininfor(username, Constants.LOGIN_FAIL, "账号不能为空"); + throw new ServiceException("用户/邮箱不能为空"); + } + + if (StringUtils.isAnyBlank(password)) + { + recordLogininfor(username, Constants.LOGIN_FAIL, "密码不能为空"); + throw new ServiceException("密码不能为空"); + } + // 密码如果不在指定范围内 错误 + if (password.length() < UserConstants.PASSWORD_MIN_LENGTH + || password.length() > UserConstants.PASSWORD_MAX_LENGTH) + { + recordLogininfor(username, Constants.LOGIN_FAIL, "用户密码不在指定范围"); + throw new ServiceException("用户密码不在指定范围"); + } + + if(username.matches("^[a-zA-Z0-9][\\w\\.-]*[a-zA-Z0-9]@[a-zA-Z0-9][\\w\\.-]*[a-zA-Z0-9]\\.[a-zA-Z][a-zA-Z\\.]*[a-zA-Z]$")){ + //todo 验证邮箱 + }else { + // 用户名不在指定范围内 错误 + if (username.length() < UserConstants.USERNAME_MIN_LENGTH + || username.length() > UserConstants.USERNAME_MAX_LENGTH) + { + recordLogininfor(username, Constants.LOGIN_FAIL, "用户名不在指定范围"); + throw new ServiceException("用户名不在指定范围"); + } + } + if(!"admin".equals(username)){ + if(!password.matches(PWD_REGEX)){ + recordLogininfor(username, Constants.LOGIN_FAIL, "密码格式错误"); + throw new ServiceException("密码格式错误,密码应包含大写字母、小写字母、数字以及特殊字符,8到16位"); + } + } + //todo 可以添加校验验证码的功能 + + // 查询用户信息 + R userResult = sysUserService.selectUser(username); + + if (R.FAIL == userResult.getCode()) + { + throw new ServiceException(userResult.getMsg()); + } + + if (StringUtils.isNull(userResult)) + { + recordLogininfor(username, Constants.LOGIN_FAIL, "登录用户不存在"); + throw new ServiceException("登录用户:" + username + " 不存在"); + } + LoginUser userInfo = userResult.getData(); + Long userid = userInfo.getUserid(); + SysUser user = userResult.getData().getSysUser(); + + //TODO 根据createFrom字段判断来源 如果是1则调用wrodpress的验证登陆验证 + + if (UserStatus.DELETED.getCode().equals(user.getDelFlag())) + { + recordLogininfor(username, Constants.LOGIN_FAIL, "对不起,您的账号已被删除"); + throw new ServiceException("对不起,您的账号:" + username + " 已被删除"); + } + if (UserStatus.DISABLE.getCode().equals(user.getStatus())) + { + recordLogininfor(username, Constants.LOGIN_FAIL, "用户已停用,请联系管理员"); + throw new ServiceException("对不起,您的账号:" + username + " 已停用"); + } + //根据user:userId:lock + if(redisService.hasKey("user:"+userid+":lock")){ + //账户输入密码错误 + throw new ServiceException("该账户因密码错误多次被暂时限制登陆24h!"); + } + + if (!SecurityUtils.matchesPassword(password, user.getPassword())) + { + recordLogininfor(username, Constants.LOGIN_FAIL, "用户密码错误"); + //redis 记录用户密码错误次数 大于5时锁定账户24小时 + int count = 0; + if(redisService.hasKey("user:"+userid+":pwdError")){ + count = Convert.toInt(redisService.getCacheObject("user:"+userid+":pwdError")); + } + count++; + if(count == 1){ + redisService.setCacheObject("user:"+userid+":pwdError",count,1L,TimeUnit.HOURS); + }else { + long expire = redisService.getExpire("user:" + userid + ":pwdError", TimeUnit.SECONDS); + System.out.println(expire); + redisService.setCacheObject("user:"+userid+":pwdError",count,redisService.getExpire("user:"+userid+":pwdError",TimeUnit.SECONDS),TimeUnit.SECONDS); + } + if(count >= 5){ + redisService.setCacheObject("user:"+userid+":lock", userid, 24L, TimeUnit.HOURS); + } + throw new ServiceException("密码错误,一小时内错误五次之后锁定账户24h,当前已失败"+count+"次"); + } + + //todo 判断当前ip和用户上一次登陆ip是否一致 + //if(StringUtils.isNotNull(userResult.getData().getSysUser().getLoginIp()) && StringUtils.isNotBlank(userResult.getData().getSysUser().getLoginIp())){ + // //上一次ip不为空 判断 + // if(StringUtils.isBlank(loginBody.getUuid()) || StringUtils.isNull(loginBody.getUuid())){ + // + // //没传uuid说明是刚发起的登录请求 需要判断ip是否发生变化 + // String ipAddr = IpUtils.getIpAddr(ServletUtils.getRequest()); + // if(!ipAddr.equals(userResult.getData().getSysUser().getLoginIp())){ + // return R.fail(201,"登录ip发生变化,请通过验证再登录"); + // } + // }else { + // //传了uuid说明是验证登录 验证 + // if (StringUtils.isBlank(loginBody.getCode())) + // { + // return R.fail("验证码不能为空"); + // } + // + // String verifyKey = Constants.CAPTCHA_CODE_KEY + loginBody.getUuid(); + // String captcha = redisService.getCacheObject(verifyKey); + // + // if(StringUtils.isNull(captcha)){ + // return R.fail("验证码已失效"); + // } + // //redisService.deleteObject(verifyKey); + // + // if (!loginBody.getCode().equalsIgnoreCase(captcha)) + // { + // return R.fail("验证码错误"); + // } + // redisService.deleteObject(verifyKey); + // } + // + //} + + recordLogininfor(username, Constants.LOGIN_SUCCESS, "登录成功"); + + SecurityContextHolder.setEmail(userInfo.getSysUser().getEmail()); + + //修改数据库中的用户登录次数和最后一次登录的ip + user.setLoginCount(user.getLoginCount()+1); + user.setLoginIp(IpUtils.getIpAddr(ServletUtils.getRequest())); + user.setLoginDate(DateUtils.getNowDate()); + + //todo 单独存储用户的登录时间和ip + sysUserService.updateUser(user); + + return R.success(tokenService.createToken(userInfo)); + } + + + + public void logout(String loginName) + { + recordLogininfor(loginName, Constants.LOGOUT, "退出成功"); + } + + /** + * 注册 + */ + public void register(RegisterBody registerBody) + { + String username = registerBody.getUserName(); + String password = registerBody.getPassword(); + try { + password = RsaUtils.decryptByPrivateKey(registerBody.getPassword()); + password = StringUtils.clean(password); + }catch (Exception e){ + //throw new ServiceException("加密密码传参有误",401); + password = StringUtils.clean(password); + } + + String email = registerBody.getEmail(); + String phone = registerBody.getPhone(); + String emailCode = registerBody.getEmailCode(); + //String key = username+"+"+email; + // 用户名或密码为空 错误 + if (StringUtils.isAnyBlank(username, password)) + { + throw new ServiceException("用户/密码必须填写"); + } + if (username.length() < UserConstants.USERNAME_MIN_LENGTH + || username.length() > UserConstants.USERNAME_MAX_LENGTH) + { + throw new ServiceException("账户长度必须在3到16个字符之间"); + } + if (password.length() < UserConstants.PASSWORD_MIN_LENGTH + || password.length() > UserConstants.PASSWORD_MAX_LENGTH) + { + throw new ServiceException("密码长度必须在8到32个字符之间"); + } + if(!password.matches(PWD_REGEX)){ + throw new ServiceException("密码格式错误,密码应包含大写字母、小写字母、数字以及特殊字符,8到16位"); + } + + //todo 手机号验证 + if(!StringUtils.isEmpty(phone)){ + String phoneRex = "^((13[0-9])|(14[5|7])|(15([0-3]|[5-9]))|(18[0,5-9]))\\d{8}$"; + + if (!phone.matches(phoneRex)) + { + throw new ServiceException("手机号格式不正确"); + } + } + if(!StringUtils.isEmpty(email)){ + //todo 邮箱验证 + }else { + throw new ServiceException("邮箱为必填项"); + } + + if(redisService.hasKey(RedisTransKey.getEmailKey(email))){ + + Object o = redisService.getCacheObject(RedisTransKey.getEmailKey(email));//user:emailCode:email + + EmailCodeEntity emailCodeEntity = JSON.parseObject(JSON.toJSONString(o), EmailCodeEntity.class); + + //验证验证码 + if(emailCode.equals(emailCodeEntity.getEmailCode())){ + // 注册用户信息 + SysUser sysUser = new SysUser(); + sysUser.setUserName(username); + sysUser.setNickName(username); + sysUser.setPhone(phone); + sysUser.setEmail(email); + sysUser.setPassword(SecurityUtils.encryptPassword(password)); + + if (UserConstants.NOT_UNIQUE.equals(sysUserService.checkUserNameUnique(username))) + { + throw new ServiceException("用户'" + username + "'注册失败,注册账号已存在"); + } + + if (UserConstants.NOT_UNIQUE.equals(sysUserService.checkEmailUnique(sysUser))) + { + throw new ServiceException("用户'" + username + "'注册失败,邮箱"+email+"已被其他用户注册"); + } + sysUserService.registerUser(sysUser); + + recordLogininfor(username, Constants.REGISTER, "注册成功"); + }else { + throw new ServiceException("验证码错误"); + } + }else { + throw new ServiceException("验证码未获取或已过期,请重新获取验证码"); + } + + } + + /** + * wordpress用户登录 1判断是否注册 未注册先注册再登录 已注册直接登录 登录返回token + * @param wp + */ + public R wpUserLogin(WpLoginUser wp) + { + System.out.println("入参"+wp); + String username = wp.getUserName(); + if("admin".equals(username)){ + return R.fail(); + } + String email = wp.getEmail(); + + //生成随机密码 长度6-18 + int pwdLen = (int) (Math.random()*12)+6; + String pwd = PwdUtils.randomPassword(pwdLen); + // 用户名或邮箱为空 错误 + if (StringUtils.isAnyBlank(email,username)) + { + throw new ServiceException("邮箱/用户名必须填写"); + } + //if (username.length() < UserConstants.USERNAME_MIN_LENGTH + // || username.length() > UserConstants.USERNAME_MAX_LENGTH) + //{ + // throw new ServiceException("账户长度必须在3到16个字符之间"); + //} + //if (password.length() < UserConstants.PASSWORD_MIN_LENGTH + // || password.length() > UserConstants.PASSWORD_MAX_LENGTH) + //{ + // throw new ServiceException("密码长度必须在8到32个字符之间"); + //} + //if(!password.matches(PWD_REGEX)){ + // throw new ServiceException("密码格式错误,密码应包含大写字母、小写字母、数字以及特殊字符,8到16位"); + //} + if(!StringUtils.isEmpty(email)){ + String em = "^[a-zA-Z0-9][\\w\\.-]*[a-zA-Z0-9]@[a-zA-Z0-9][\\w\\.-]*[a-zA-Z0-9]\\.[a-zA-Z][a-zA-Z\\.]*[a-zA-Z]$"; + if(!email.matches(em)){ + throw new ServiceException("邮箱格式错误"); + } + //todo 邮箱验证 + }else { + throw new ServiceException("邮箱为必填项"); + } + if(UserConstants.UNIQUE.equals(sysUserService.checkEmailExist(email))){ + //未注册 先注册 + // 注册用户信息 + SysUser sysUser = new SysUser(); + sysUser.setUserName(username); + sysUser.setNickName(username); + sysUser.setEmail(email); + + sysUser.setPassword(SecurityUtils.encryptPassword(pwd)); + //设置用户来源为wp + sysUser.setCreateFrom(1); + + sysUserService.registerUser(sysUser); + + recordLogininfor(username, Constants.REGISTER, "为WordPress用户注册工单系统账号成功"); + + //String content="\n" + + // "\n" + + // "\n" + + // "\n" + + // "邮件\n" + + // "\n" + + // "\n" + + // "\t

尊敬的用户您好!

\n" + + // "\t

已为您自动注册The Wind Miner售后工单系统,本次注册随机生成密码为"+pwd+",以后您可以通过邮箱+该密码直接访问售后工单系统,您也可以通过售后工单系统修改您的密码。

\n" + + // "\n\t

thewindminer感谢您的支持!

\n" + + // "\n" + + // ""; + //CompletableFuture.runAsync(()->{mailService.sendHtmlMailMessage(wp.getEmail(),"售后工单系统自动注册提醒",content);}); + }else { + System.out.println("用户已注册过,直接走登录流程"); + } + + //登录 (跳过密码校验) + LoginBody loginBody = new LoginBody(); + loginBody.setUserName(email); + //loginBody.setPassword(wp.getPassword()); + + System.out.println("登录用户"+loginBody); + R userResult = sysUserService.selectUser(email); + + if (R.FAIL == userResult.getCode()) + { + throw new ServiceException(userResult.getMsg()); + } + + //if (StringUtils.isNull(userResult)) + //{ + // recordLogininfor(username, Constants.LOGIN_FAIL, "登录用户不存在"); + // throw new ServiceException("登录用户:" + username + " 不存在"); + //} + LoginUser userInfo = userResult.getData(); + SysUser user = userResult.getData().getSysUser(); + + //if (!SecurityUtils.matchesPassword(password, user.getPassword())) + //{ + // throw new ServiceException("密码错误|"+password+"|"+user.getPassword()+"|"); + //} + + recordLogininfor(username, Constants.LOGIN_SUCCESS, "登录成功"); + + SecurityContextHolder.setEmail(userInfo.getSysUser().getEmail()); + + //修改数据库中的用户登录次数和最后一次登录的ip + user.setLoginCount(user.getLoginCount()+1); + user.setLoginIp(IpUtils.getIpAddr(ServletUtils.getRequest())); + user.setLoginDate(DateUtils.getNowDate()); + + //todo 单独存储用户的登录时间和ip + sysUserService.updateUser(user); + + return R.success(tokenService.createToken(userInfo)); + + } + + + /** + * 重置密码 + */ + public void resetPwd(ResetPwdBody resetPwdBody) + { + String password = resetPwdBody.getPassword(); + try { + password = RsaUtils.decryptByPrivateKey(resetPwdBody.getPassword()); + }catch (Exception e){ + throw new ServiceException("加密密码传参有误",401); + } + + String email = resetPwdBody.getEmail(); + String resetPwdCode = resetPwdBody.getEmailCode(); + // 邮箱或密码为空 错误 + if (StringUtils.isAnyBlank(email, password)) + { + throw new ServiceException("邮箱/新密码不能为空"); + } + if (password.length() < UserConstants.PASSWORD_MIN_LENGTH + || password.length() > UserConstants.PASSWORD_MAX_LENGTH) + { + throw new ServiceException("密码长度必须在8到32个字符之间"); + } + //password格式校验 + if(!password.matches(PWD_REGEX)){ + throw new ServiceException("密码格式错误,密码应包含大写字母、小写字母、数字以及特殊字符,8到16位"); + } + R userResult = sysUserService.selectUser(email); + + if (R.FAIL == userResult.getCode()) + { + throw new ServiceException(userResult.getMsg()); + } + + if(SecurityUtils.matchesPassword(password,userResult.getData().getSysUser().getPassword())){ + throw new ServiceException("新密码不能与原密码一致"); + } + + + if(redisService.hasKey(RedisTransKey.getResetPwdKey(email))){ + + Object o = redisService.getCacheObject(RedisTransKey.getResetPwdKey(email));//user:emailCode:username + + EmailCodeEntity emailCodeEntity = JSON.parseObject(JSON.toJSONString(o), EmailCodeEntity.class); + + if (email.equals(emailCodeEntity.getEmail())) { + //邮箱必须和刚刚传的一致 + //验证验证码 + if(resetPwdCode.equals(emailCodeEntity.getEmailCode())){ + // 重置用户密码 + password = SecurityUtils.encryptPassword(password); + + if (sysUserService.resetPwdByEmail(email,password) < 1) + { + throw new ServiceException("密码修改失败"); + } + //recordLogininfor(username, Constants.REGISTER, "注册成功"); + }else { + throw new ServiceException("邮箱验证码错误"); + } + }else { + throw new ServiceException("请勿修改已输入的邮箱"); + } + }else { + ////判断是邮箱不存在还是操作超时 + ////通过邮箱获取用户 + //R userByEmail = remoteUserService.getUserInfo(email, SecurityConstants.INNER); + // + //if(StringUtils.isNull(userByEmail.getData())){ + // throw new ServiceException("邮箱"+email+"未被注册,请检查邮箱是否正确"); + //} + throw new ServiceException("验证码校验失败:操作超时,请重新操作"); + } + + } + + + /** + * 记录登录信息 + * + * @param username 用户名 + * @param status 状态 + * @param message 消息内容 + * @return + */ + public void recordLogininfor(String username, String status, String message) + { + SysLogininfor logininfor = new SysLogininfor(); + logininfor.setUserName(username); + logininfor.setIpaddr(IpUtils.getIpAddr(ServletUtils.getRequest())); + logininfor.setMsg(message); + // 日志状态 + if (StringUtils.equalsAny(status, Constants.LOGIN_SUCCESS, Constants.LOGOUT, Constants.REGISTER)) + { + logininfor.setStatus(Constants.LOGIN_SUCCESS_STATUS); + } + else if (Constants.LOGIN_FAIL.equals(status)) + { + logininfor.setStatus(Constants.LOGIN_FAIL_STATUS); + } + logininforService.insertLogininfor(logininfor); + } + + +} diff --git a/src/main/java/com/jxy/windminer/service/SysLogininforService.java b/src/main/java/com/jxy/windminer/service/SysLogininforService.java new file mode 100644 index 0000000..36a420f --- /dev/null +++ b/src/main/java/com/jxy/windminer/service/SysLogininforService.java @@ -0,0 +1,41 @@ +package com.jxy.windminer.service; + +import com.jxy.windminer.entity.system.SysLogininfor; + +import java.util.List; + +/** + * 系统访问日志情况信息 服务层 + * + * @author 杜懿 + */ +public interface SysLogininforService +{ + /** + * 新增系统登录日志 + * + * @param logininfor 访问日志对象 + */ + public int insertLogininfor(SysLogininfor logininfor); + + /** + * 查询系统登录日志集合 + * + * @param logininfor 访问日志对象 + * @return 登录记录集合 + */ + public List selectLogininforList(SysLogininfor logininfor); + + /** + * 批量删除系统登录日志 + * + * @param infoIds 需要删除的登录日志ID + * @return 结果 + */ + public int deleteLogininforByIds(Long[] infoIds); + + /** + * 清空系统登录日志 + */ + public void cleanLogininfor(); +} diff --git a/src/main/java/com/jxy/windminer/service/SysMenuService.java b/src/main/java/com/jxy/windminer/service/SysMenuService.java new file mode 100644 index 0000000..d95bd0e --- /dev/null +++ b/src/main/java/com/jxy/windminer/service/SysMenuService.java @@ -0,0 +1,14 @@ +package com.jxy.windminer.service; + +import java.util.Set; + +public interface SysMenuService { + + /** + * 根据用户ID查询权限 + * + * @param userId 用户ID + * @return 权限列表 + */ + public Set selectMenuPermsByUserId(Long userId); +} diff --git a/src/main/java/com/jxy/windminer/service/SysOperLogService.java b/src/main/java/com/jxy/windminer/service/SysOperLogService.java new file mode 100644 index 0000000..4aeaf84 --- /dev/null +++ b/src/main/java/com/jxy/windminer/service/SysOperLogService.java @@ -0,0 +1,50 @@ +package com.jxy.windminer.service; + +import com.jxy.windminer.entity.system.SysOperLog; + +import java.util.List; + +/** + * 操作日志 服务层 + * + * @author 杜懿 + */ +public interface SysOperLogService +{ + /** + * 新增操作日志 + * + * @param operLog 操作日志对象 + * @return 结果 + */ + public int insertOperlog(SysOperLog operLog); + + /** + * 查询系统操作日志集合 + * + * @param operLog 操作日志对象 + * @return 操作日志集合 + */ + public List selectOperLogList(SysOperLog operLog); + + /** + * 批量删除系统操作日志 + * + * @param operIds 需要删除的操作日志ID + * @return 结果 + */ + public int deleteOperLogByIds(Long[] operIds); + + /** + * 查询操作日志详细 + * + * @param operId 操作ID + * @return 操作日志对象 + */ + public SysOperLog selectOperLogById(Long operId); + + /** + * 清空操作日志 + */ + public void cleanOperLog(); +} diff --git a/src/main/java/com/jxy/windminer/service/SysPermissionService.java b/src/main/java/com/jxy/windminer/service/SysPermissionService.java new file mode 100644 index 0000000..40cd8e3 --- /dev/null +++ b/src/main/java/com/jxy/windminer/service/SysPermissionService.java @@ -0,0 +1,22 @@ +package com.jxy.windminer.service; + +import java.util.Set; + +public interface SysPermissionService { + + /** + * 获取角色数据权限 + * + * @param userId 用户Id + * @return 角色权限信息 + */ + public Set getRolePermission(Long userId); + + /** + * 获取菜单数据权限 + * + * @param userId 用户Id + * @return 菜单权限信息 + */ + public Set getMenuPermission(Long userId); +} diff --git a/src/main/java/com/jxy/windminer/service/SysRoleService.java b/src/main/java/com/jxy/windminer/service/SysRoleService.java new file mode 100644 index 0000000..3a1c870 --- /dev/null +++ b/src/main/java/com/jxy/windminer/service/SysRoleService.java @@ -0,0 +1,49 @@ +package com.jxy.windminer.service; + +import com.jxy.windminer.entity.system.SysRole; + +import java.util.List; +import java.util.Set; + +public interface SysRoleService { + + /** + * 根据条件分页查询角色数据 + * + * @param role 角色信息 + * @return 角色数据集合信息 + */ + public List selectRoleList(SysRole role); + + /** + * 根据用户ID查询角色权限 + * + * @param userId 用户ID + * @return 权限列表 + */ + public Set selectRolePermissionByUserId(Long userId); + + /** + * 根据用户ID查询角色列表 + * + * @param userId 用户ID + * @return 角色列表 + */ + public List selectRolesByUserId(Long userId); + + /** + * 查询所有角色 + * + * @return 角色列表 + */ + public List selectRoleAll(); + + /** + * 根据用户ID获取角色选择框列表 + * + * @param userId 用户ID + * @return 选中角色ID列表 + */ + public List selectRoleListByUserId(Long userId); + +} diff --git a/src/main/java/com/jxy/windminer/service/SysUserService.java b/src/main/java/com/jxy/windminer/service/SysUserService.java new file mode 100644 index 0000000..7b1b697 --- /dev/null +++ b/src/main/java/com/jxy/windminer/service/SysUserService.java @@ -0,0 +1,167 @@ +package com.jxy.windminer.service; + + +import com.jxy.windminer.common.Result.R; +import com.jxy.windminer.entity.auth.LoginUser; +import com.jxy.windminer.entity.system.SysUser; + +import java.util.List; + +public interface SysUserService { + + /** + * 根据条件分页查询用户列表 + * + * @param user 用户信息 + * @return 用户信息集合信息 + */ + public List selectUserList(SysUser user); + + /** + * 根据条件分页查询用户列表 + * + * @param account 用户名/邮箱 + * @return 用户信息集合信息 + */ + public R selectUser(String account); + + /** + * 通过用户名查询用户 + * + * @param userName 用户名 + * @return 用户对象信息 + */ + public SysUser selectUserByUserName(String userName); + + + /** + * 通过邮箱查询用户 + * + * @param email 邮箱 + * @return 用户对象信息 + */ + public SysUser selectUserByEmail(String email); + + /** + * 通过用户ID查询用户 + * + * @param userId 用户ID + * @return 用户对象信息 + */ + public SysUser selectUserById(Long userId); + + /** + * 根据用户ID查询用户所属角色组 + * + * @param userName 用户名 + * @return 结果 + */ + public String selectUserRoleGroup(String userName); + + /** + * 校验用户名称是否唯一 + * + * @param userName 用户名称 + * @return 结果 + */ + public String checkUserNameUnique(String userName); + + + /** + * 校验手机号码是否唯一 + * + * @param user 用户信息 + * @return 结果 + */ + public String checkPhoneUnique(SysUser user); + + /** + * 校验email是否唯一 + * + * @param user 用户信息 + * @return 结果 + */ + public String checkEmailUnique(SysUser user); + + /** + * 校验email是否唯一 + * + * @param email 用户信息 + * @return 结果 + */ + public String checkEmailExist(String email); + + /** + * 校验用户是否允许操作 + * + * @param user 用户信息 + */ + public void checkUserAllowed(SysUser user); + + /** + * 校验用户是否有数据权限 + * + * @param userId 用户id + */ + public void checkUserDataScope(Long userId); + + + /** + * 注册用户信息 + * + * @param user 用户信息 + * @return 结果 + */ + public boolean registerUser(SysUser user); + + /** + * 修改用户信息 + * + * @param user 用户信息 + * @return 结果 + */ + public int updateUser(SysUser user); + + /** + * 修改用户状态 + * + * @param user 用户信息 + * @return 结果 + */ + public int updateUserStatus(SysUser user); + + /** + * 修改用户基本信息 + * + * @param user 用户信息 + * @return 结果 + */ + public int updateUserProfile(SysUser user); + + /** + * 重置用户密码 + * + * @param user 用户信息 + * @return 结果 + */ + public int resetPwd(SysUser user); + + /** + * 重置用户密码 + * + * @param userName 用户名 + * @param password 密码 + * @return 结果 + */ + public int resetUserPwd(String userName, String password); + + /** + * 重置用户密码 + * + * @param email 邮箱 + * @param password 密码 + * @return 结果 + */ + public int resetPwdByEmail(String email, String password); + +} diff --git a/src/main/java/com/jxy/windminer/service/TicketService.java b/src/main/java/com/jxy/windminer/service/TicketService.java new file mode 100644 index 0000000..6b78a19 --- /dev/null +++ b/src/main/java/com/jxy/windminer/service/TicketService.java @@ -0,0 +1,316 @@ +package com.jxy.windminer.service; + + +import com.jxy.windminer.common.web.Result.AjaxResult; +import com.jxy.windminer.dto.AllTicketListDto; +import com.jxy.windminer.dto.TicketAcListDto; +import com.jxy.windminer.dto.TicketListDto; +import com.jxy.windminer.vo.*; +import org.springframework.web.multipart.MultipartFile; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.sql.SQLException; +import java.util.List; + + +/** + * @Description 追风工单系统服务类 + * @Date 2022/5/19 17:40 + * @Author 杜懿 + */ +public interface TicketService { + + /** + * 根据文件id下载文件 多个 + * @return + */ + public void downloadByIds(String ids, HttpServletRequest request, HttpServletResponse response); + + + /** + * 单文件上传 + * @return + */ + public AjaxResult uploadFile(MultipartFile file) throws SQLException; + + /** + * 多文件上传 + * @return + */ + public AjaxResult uploadFiles(MultipartFile[] file) throws SQLException; + + /** + * 提交工单 + * @param vo + * @return + */ + public AjaxResult submitTicket(TicketVo vo); + + + /** + * 根据机器序列号查看保修期 + * @param vo + * @return + */ + public AjaxResult getGuaranteeBySerial(SerialVo vo); + + /** + * 根据机器序列号查看机器编号是否存在 + * @param vo + * @return + */ + public AjaxResult checkSerialNo(SerialVo vo); + + /** + * 获取故障现象下拉框数据 + * @param + * @return + */ + public AjaxResult getFaults(); + + /** + * 用户 再次补充提交线上技术支持工单内容 + * @param vo + * @return + */ + public AjaxResult resubmitTicket(ResubmitTicketVo vo); + + /** + * 用户 条件搜索获取个人工单列表 + * @param vo + * @return + */ + public AjaxResult getPrivateTicketByStatus(PrivateTicketListVo vo); + + /** + * + * 查看工单详情 + * @param id + * @return + */ + public AjaxResult getPrivateTicketDetails(long id); + + /** + * 用户 获取维修相关物流、工单信息 + * @return + */ + public AjaxResult getBaseRepairInfo(); + + /** + * 用户 提交维修寄件物流信息 + * @param vo + * @return + */ + public AjaxResult addRepairLogisticalInfo(UserLogisticalVo vo); + + /** + * 用户 获取支付页面地址 + * @return + */ + public AjaxResult getTicketPaymentOrderInfo(IdVo vo); + + + + /** + * 用户结束工单 仅能关闭工单状态为2或者9的工单 + * + * @param vo@return + */ + public AjaxResult endTicket(EndTicketVo vo); + +/*************************************** 维修人员相关接口 *************************************************/ + /** + * 确认工单类型 后台 + * @param vo + * @return + */ + public AjaxResult onlineChangeType(ConfirmTypeVo vo); + + /** + * 确认工单类型 后台 + * @param vo + * @return + */ + public AjaxResult repairChangeType(ConfirmTypeVo vo); + + + + + /** + * 修改工单类型 后台 + * @param vo + * @return + */ + public AjaxResult changeTicketType(ChangeTypeVo vo); + + + + + /** + * 邮件内点击结束工单 + * @param id + * @return + */ + public AjaxResult mailEndTicket(long id,String email); + + + + + ///** + // * 查看工单详情 + // * @param id + // * @return + // */ + //public AjaxResult getTicketDetails(long id); + + + + + /** + * 查看所有工单 条件搜索 + * @param + * @param vo + * @return + */ + public List getAllTicket(AllTicketListVo vo); + + + + /** + * 根据status获取工单列表 + * @param vo + * @return + */ + public List getTicketList(StatusVo vo); + + /** + * 根据status获取技术支持工单列表 + * @param vo + * @return + */ + public List getSupportTicketList(StatusVo vo); + + /** + * 根据status获取工单列表 + * @param vo + * @return + */ + public List getRepairTicketList(StatusVo vo); + + + /** + * 技术支持人员回复工单 + * @param vo + * @return + */ + public AjaxResult onlineResponTicket(ResponTicketVo vo); + + /** + * 查看工单详情 + * @param id + * @return + */ + public AjaxResult getTicketDetails(long id); + + /** + * 查看工单详情 + * @param id + * @return + */ + public AjaxResult getTicketDetails3(long id); + + /** + * 查看工单详情 + * @param id + * @return + */ + public AjaxResult getReturnDetails(long id); + + + /** + * 维修人员确认维修工单 并填写物流信息 + * @param vo + * @return + */ + public AjaxResult repairConfirmTicket(RepairConfirmVo vo); + + /** + * 维修人员拒绝维修工单 并拒绝信息 + * @param vo + * @return + */ + public AjaxResult repairRefuseTicket(RepairRefuseVo vo); + + + /** + * 维修人员回复工单 + * @param vo + * @return + */ + public AjaxResult RepairResponTicket(ResponTicketVo vo); + + /** + * 查看维修工单详情 基本信息+用户提交的物流信息 + ** @param id + * @return + */ + public AjaxResult getRepairTicketDetails(long id); + + /** + * 确认收货 + * @param vo + * @return + */ + public AjaxResult receipt(ConfirmTypeVo vo); + + /** + * 维修工单报价 + * @param vo + * @return + */ + public AjaxResult repairTicketAddPrice(QuotedVo vo); + + /** + * 维修工单报价 + * @param vo + * @return + */ + public AjaxResult changeRepairTicketStatus(RepairChangeStatusVo vo); + + /** + * 维修工单报价 + * @param vo + * @return + */ + public AjaxResult addReturnLogistical(ReturnLogisticalVo vo); + + + /** + * 财务 查看工单列表 + * @param vo + * @return + */ + public List getAccountTicketList(StatusVo vo); + + + /** + * 财务 查看工单详情 + * @param id + * @return + */ + public AjaxResult getAccountTicketDetails(long id); + + + public AjaxResult noticeUserByTicketId(IdVo vo); + + + ///** + // * 提交维修寄回物流信息 + // * @param vo + // * @return + // */ + //public AjaxResult addReturnLogisticalInfo(ReturnLogisticalVo vo); + + +} diff --git a/src/main/java/com/jxy/windminer/service/impl/FileServiceImpl.java b/src/main/java/com/jxy/windminer/service/impl/FileServiceImpl.java new file mode 100644 index 0000000..359ebf6 --- /dev/null +++ b/src/main/java/com/jxy/windminer/service/impl/FileServiceImpl.java @@ -0,0 +1,59 @@ +package com.jxy.windminer.service.impl; +import com.jxy.windminer.common.Result.R; +import com.jxy.windminer.common.utils.file.FileUtils; +import com.jxy.windminer.entity.SysFile; +import com.jxy.windminer.service.FileService; +import com.jxy.windminer.service.ISysFileService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.web.multipart.MultipartFile; + +/** + * 本地文件存储 + * + * @author jxy + */ +@Service +public class FileServiceImpl implements FileService +{ + + @Autowired + private ISysFileService sysFileService; + + @Override + public R uploadFile(MultipartFile file){ + try + { + // 上传并返回访问地址 + String url = sysFileService.uploadFile(file); + SysFile sysFile = new SysFile(); + sysFile.setName(FileUtils.getName(url)); + sysFile.setUrl(url); + return R.success(sysFile); + } + catch (Exception e) + { + return R.fail(e.getMessage()); + } + } + + @Override + public R uploadToolsFile(MultipartFile file, String fileName){ + try + { + // 上传并返回访问地址 + String url = sysFileService.uploadToolsFile(file,fileName); + SysFile sysFile = new SysFile(); + sysFile.setName(FileUtils.getName(url)); + sysFile.setUrl(url); + + System.out.println("工具类上传 构造的文件数据:"+sysFile); + return R.success(sysFile); + } + catch (Exception e) + { + return R.fail(e.getMessage()); + } + } + +} diff --git a/src/main/java/com/jxy/windminer/service/impl/MaliServiceImpl.java b/src/main/java/com/jxy/windminer/service/impl/MaliServiceImpl.java new file mode 100644 index 0000000..287c02f --- /dev/null +++ b/src/main/java/com/jxy/windminer/service/impl/MaliServiceImpl.java @@ -0,0 +1,1234 @@ +package com.jxy.windminer.service.impl; + +import com.alibaba.fastjson.JSON; +import com.jxy.windminer.common.RedisTransKey; +import com.jxy.windminer.common.Result.R; +import com.jxy.windminer.common.constant.OrderConstants; +import com.jxy.windminer.common.redis.service.RedisService; +import com.jxy.windminer.common.text.Convert; +import com.jxy.windminer.common.utils.CodeUtils; +import com.jxy.windminer.common.utils.DateUtils; +import com.jxy.windminer.common.utils.StringUtils; +import com.jxy.windminer.dto.OrderInfoDto; +import com.jxy.windminer.entity.auth.EmailCodeEntity; +import com.jxy.windminer.entity.auth.GetEmailCodeEntity; +import com.jxy.windminer.entity.auth.LoginUser; +import com.jxy.windminer.service.MailService; +import com.jxy.windminer.service.SysUserService; +import com.jxy.windminer.vo.QuotedVo; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.core.io.FileSystemResource; +import org.springframework.mail.javamail.JavaMailSenderImpl; +import org.springframework.mail.javamail.MimeMessageHelper; +import org.springframework.stereotype.Service; + +import java.io.File; +import java.util.Date; +import java.util.concurrent.TimeUnit; + +/** + * @Description TODO + * @Date 2022/11/18 15:08 + * @Author 杜懿 + */ +@Service +public class MaliServiceImpl implements MailService { + /** + * 注入邮件工具类 + */ + @Autowired + private JavaMailSenderImpl javaMailSender; + + @Autowired + private SysUserService userService; + + @Autowired + private RedisService redisService; + + @Value("${spring.mail.username}") + private String sendMailer; + + @Value("${my.domain}") + private String MY_DOMAIN; + + @Value("${my.env}") + private String ENV; + + /** + * 检测邮件信息类 + * @param to + * @param subject + * @param text + */ + private void checkMail(String to,String subject,String text){ + if(StringUtils.isEmpty(to)){ + throw new RuntimeException("邮件收信人不能为空"); + } + if(StringUtils.isEmpty(subject)){ + throw new RuntimeException("邮件主题不能为空"); + } + if(StringUtils.isEmpty(text)){ + throw new RuntimeException("邮件内容不能为空"); + } + } + + /** + * 发送纯文本邮件 + * @param to + * @param subject + * @param text + */ + @Override + public void sendTextMailMessage(String to,String subject,String text){ + + try { + //true 代表支持复杂的类型 + MimeMessageHelper mimeMessageHelper = new MimeMessageHelper(javaMailSender.createMimeMessage(),true); + //邮件发信人 + mimeMessageHelper.setFrom(sendMailer); + //邮件收信人 1或多个 + mimeMessageHelper.setTo(to.split(",")); + //邮件主题 + mimeMessageHelper.setSubject(subject); + //邮件内容 + mimeMessageHelper.setText(text); + //邮件发送时间 + mimeMessageHelper.setSentDate(new Date()); + + //发送邮件 + javaMailSender.send(mimeMessageHelper.getMimeMessage()); + //System.out.println("发送邮件成功:"+sendMailer+"->"+to); + + } catch (Exception e) { + e.printStackTrace(); + //System.out.println("发送邮件失败:"+e.getMessage()); + } + } + + + /** + * 发送html邮件 + * @param to + * @param subject + * @param content + */ + @Override + public void sendHtmlMailMessage(String to,String subject,String content){ + + //content="\n" + + // "\n" + + // "\n" + + // "\n" + + // "邮件\n" + + // "\n" + + // "\n" + + // "\t

这是一封HTML邮件!

\n" + + // "\n" + + // ""; + try { + //true 代表支持复杂的类型 + MimeMessageHelper mimeMessageHelper = new MimeMessageHelper(javaMailSender.createMimeMessage(),true); + //邮件发信人 + mimeMessageHelper.setFrom(sendMailer); + //邮件收信人 1或多个 + mimeMessageHelper.setTo(to.split(",")); + //邮件主题 + mimeMessageHelper.setSubject(subject); + //邮件内容 true 代表支持html + mimeMessageHelper.setText(content,true); + //邮件发送时间 + mimeMessageHelper.setSentDate(new Date()); + + //发送邮件 + javaMailSender.send(mimeMessageHelper.getMimeMessage()); + System.out.println("发送邮件成功:"+sendMailer+"->"+to); + + } catch (Exception e) { + e.printStackTrace(); + System.out.println("发送邮件失败:"+e.getMessage()); + } + } + + /** + * 发送带附件的邮件 + * @param to 邮件收信人 + * @param subject 邮件主题 + * @param content 邮件内容 + * @param filePath 附件路径 + */ + @Override + public void sendAttachmentMailMessage(String to,String subject,String content,String filePath){ + try { + //true 代表支持复杂的类型 + MimeMessageHelper mimeMessageHelper = new MimeMessageHelper(javaMailSender.createMimeMessage(),true); + //邮件发信人 + mimeMessageHelper.setFrom(sendMailer); + //邮件收信人 1或多个 + mimeMessageHelper.setTo(to.split(",")); + //邮件主题 + mimeMessageHelper.setSubject(subject); + //邮件内容 true 代表支持html + mimeMessageHelper.setText(content,true); + //邮件发送时间 + mimeMessageHelper.setSentDate(new Date()); + //添加邮件附件 + FileSystemResource file = new FileSystemResource(new File(filePath)); + String fileName = file.getFilename(); + mimeMessageHelper.addAttachment(fileName, file); + + //发送邮件 + javaMailSender.send(mimeMessageHelper.getMimeMessage()); + //System.out.println("发送邮件成功:"+sendMailer+"->"+to); + + } catch (Exception e) { + e.printStackTrace(); + //System.out.println("发送邮件失败:"+e.getMessage()); + } + } + + /** + * 发送账号注册邮箱验证码 + * @param to + * @param code + */ + @Override + public void sendCodeMailMessage(String to, String code) { + String subject = "账号注册,邮箱验证码"; + String text = "注册验证码10分钟内有效:\n\t"+code; + sendTextMailMessage(to,subject,text); + } + + + + @Override + public void sendResetPwdMailMessage(String to, String code) { + String subject = "账号重置密码,邮箱验证码"; + String text = "您正在重置密码,如果不是您本人操作,请忽略。验证码10分钟内有效:\n\t"+code; + sendTextMailMessage(to,subject,text); + } + + @Override + public R emailCode(GetEmailCodeEntity entity) { + String email = entity.getEmail(); + String username = entity.getUserName(); + + System.out.println("邮箱: "+email); + System.out.println("用户名: "+username); + + if(StringUtils.isNull(email)){ + return R.fail(601,"邮箱不能为空"); + } + if(StringUtils.isNull(username)){ + return R.fail(602,"用户名不能为空"); + } + + //判断用户是不是恶意刷邮箱,在规定时间内进行的 + if (redisService.hasKey(RedisTransKey.getEmailKey(email))) { + Object o = redisService.getCacheObject(RedisTransKey.getEmailKey(email));//user:emailCode:email + + EmailCodeEntity emailCodeEntity = JSON.parseObject(JSON.toJSONString(o), EmailCodeEntity.class); + if (emailCodeEntity.getTimes() > 4) { + return R.fail("请求次数过多,请10分钟后再试,(同一用户10分钟内只能请求4次)"); + } else { + + String emailCode = CodeUtils.creatCode(6); + emailCodeEntity.setEmailCode(emailCode); + emailCodeEntity.setUserName(username); + + //if(!email.equals(emailCodeEntity.getEmail())){ + // //email与缓存中所存邮箱不同 则请求次数重置为1 + // emailCodeEntity.setTimes(1); + //}else { + // emailCodeEntity.setTimes(emailCodeEntity.getTimes() + 1); + //} + + emailCodeEntity.setTimes(emailCodeEntity.getTimes() + 1); + long overTime = redisService.getExpire(RedisTransKey.setEmailKey(email)); + redisService.setCacheObject(RedisTransKey.setEmailKey(email), emailCodeEntity, overTime, TimeUnit.SECONDS + ); + sendCodeMailMessage(email, emailCodeEntity.getEmailCode()); + } + } else { + R userByName = userService.selectUser(username); + System.out.println("userByName"+userByName); + + R userByEmail = userService.selectUser(email); + + System.out.println("userByEmail"+userByEmail); + + if (userByName.getData() != null) { + return R.fail(603,"用户名"+username+"已被注册"); + } else if(userByEmail.getData() !=null){ + + return R.fail(604,"邮箱"+email+"已被注册"); + } else { + String emailCode = CodeUtils.creatCode(6); + System.out.println(emailCode); + // 最多允许用户在10分钟内发送2次的邮箱验证 + // 0s倒计时后用户可以再发送验证码,但是间隔在10分钟内只能再发送1次 + EmailCodeEntity emailCodeEntity = new EmailCodeEntity( + username , email, emailCode,1 + ); + System.out.println("emailCodeEntity " + emailCodeEntity); + //设置失效时间10分钟 + redisService.setCacheObject(RedisTransKey.getEmailKey(email), emailCodeEntity, + 10L, TimeUnit.MINUTES + ); + System.out.println("准备发送邮件"); + sendCodeMailMessage(email, emailCodeEntity.getEmailCode()); + } + } + return R.success("请求成功,验证码已经发送至用户邮箱"); + + } + + + @Override + public R resetPwdCode(GetEmailCodeEntity entity) { + + String email = entity.getEmail(); + //通过邮箱获取用户 + R userByEmail = userService.selectUser(email); + + if(StringUtils.isNull(userByEmail.getData())){ + return R.fail("邮箱"+email+"未被注册,请检查邮箱是否正确"); + } + + String username = userByEmail.getData().getUsername(); + + //判断用户是不是恶意刷邮箱,在规定时间内进行的 + if (redisService.hasKey(RedisTransKey.getResetPwdKey(email))) { + + Object o = redisService.getCacheObject(RedisTransKey.getResetPwdKey(email));//user:restPwdCode:email + + EmailCodeEntity emailCodeEntity = JSON.parseObject(JSON.toJSONString(o), EmailCodeEntity.class); + if (emailCodeEntity.getTimes() >= 5) { + return R.fail("请求次数过多,请10分钟后再试"); + } else { + //这里就不去判断两次绑定的邮箱是不是一样的了,不排除第一次输入错了邮箱的情况 + String emailCode = CodeUtils.creatCode(6); + emailCodeEntity.setEmailCode(emailCode); + emailCodeEntity.setTimes(emailCodeEntity.getTimes() + 1); + long overTime = redisService.getExpire(RedisTransKey.getResetPwdKey(email)); + redisService.setCacheObject(RedisTransKey.getResetPwdKey(email), emailCodeEntity, overTime, TimeUnit.SECONDS + ); + sendResetPwdMailMessage(email, emailCodeEntity.getEmailCode()); + } + } else { + String emailCode = CodeUtils.creatCode(6); + // 最多允许用户在10分钟内发送2次的邮箱验证 + // 0s倒计时后用户可以再发送验证码,但是间隔在10分钟内只能再发送1次 + EmailCodeEntity emailCodeEntity = new EmailCodeEntity( + username , email, emailCode,1 + ); + //设置失效时间10分钟 + redisService.setCacheObject(RedisTransKey.getResetPwdKey(email), emailCodeEntity, + 10L, TimeUnit.MINUTES + ); + sendResetPwdMailMessage(email, emailCodeEntity.getEmailCode()); + } + return R.success("请求成功,重置密码验证码已经发送至用户邮箱"); + + } + + @Override + public void sendRespondNoticeHTMLMailMessage(String to, int ticketId) { + String token = Convert.toStr(redisService.getCacheObject(OrderConstants.TOKEN_PRE + to)); + + String content = getContent("

您好,您在thewindminer网站提交的工单号为"+ticketId+ + "的工单有新的回复,请及时前往 " + + "The Wind Miner 网站查看!

"); + sendHtmlMailMessage(to,"The Wind Miner工单回复通知",content); + } + + @Override + public void sendNeedRepairNoticeHTMLMailMessage(String to, long ticketId, String name, String address, String phone) { + String token = Convert.toStr(redisService.getCacheObject(OrderConstants.TOKEN_PRE + to)); + + String msg ="

您好,您在thewindminer网站提交的工单号为"+ticketId+"的工单需要线下维修,收件物流信息如下:

" + + "

\t收件人:"+name+"

" + + "

\t收件地址:"+address+"

" + + "

\t收件人联系方式:"+phone+"

" + + "

请您把需要维修的机器寄到上方地址

" + + "

机器寄出后请您在工单系统提交相关寄件物流信息

" + + "

您可以通过重新打开工单系统页面或者通过点击下方链接的方式,进行物流信息的填写

" + + "The Wind Miner"; + String content = getContent(msg); + sendHtmlMailMessage(to,"The Wind Miner 线下维修通知",content); + } + + @Override + public void sendChangeToRepairNoticeHTMLMailMessage(String to, long ticketId) { + String msg= "

尊敬的用户您好!\n" + + "

根据我们专业技术人员的分析,您在本网站提交的"+ticketId+"号工单并非线上排查问题,现请您查看相关信息:

" + + "

由于我们无法通过您上传的相关资料为您做详细解答,所以请您将机器邮寄给我们,方便我们做更全面的检查

" + + "

或者您也可以上传更清晰的说明以及相关附件,以便我们做出更准确的判断

"; + String content = getContent(msg); + sendHtmlMailMessage(to,"The Wind Miner工单类型转换通知",content); + } + + @Override + public void sendReceptMachineNoticeHTMLMailMessage(String to, String serialNo) { + String msg= "

您的 "+serialNo+" 号机器我方已于 "+ DateUtils.dateTimeNow(DateUtils.YYYY_MM_DD_HH_MM_SS) +" 收到!

"; + String content = getContent(msg); + sendHtmlMailMessage(to,"The Wind Miner维修机器已收到通知",content); + } + // + //@Override + //public void sendNoticeHTMLMailMessage(String to, String serialNo) { + // String content = getContent("您好,您在thewindminer网站提交的工单号为"+serialNo+"的工单有新的回复,请及时前往 thewindminer 网站查看!"); + // sendHtmlMailMessage(to,"The Wind Miner工单回复通知",content); + //} + // + //@Override + //public void sendNoticeHTMLMailMessage(String to, String serialNo) { + // String content = getContent("您好,您在thewindminer网站提交的工单号为"+serialNo+"的工单有新的回复,请及时前往 thewindminer 网站查看!"); + // sendHtmlMailMessage(to,"The Wind Miner工单回复通知",content); + //} + + @Override + public void sendPayLessNoticeHTMLMailMessage(String to, int ticketId) { + String msg ="

您好,您在thewindminer网站提交的工单号为"+ticketId+"的工单实际到账金额与应支付金额不符,请仔细核对,若确实少付请及时补齐。

"; + String content = getContent(msg); + sendHtmlMailMessage(to,"The Wind Miner 支付金额少于应付金额提示",content); + } + + @Override + public void sendRepairQuotationNoticeHTMLMailMessage(String to, String serialNo, QuotedVo vo, String orderKey, String details) { + String token = Convert.toStr( redisService.getCacheObject(OrderConstants.TOKEN_PRE+to),"expire"); + String paymentUrl = "prd".equals(ENV) ? MY_DOMAIN+"/os/paymentSelection?order_key="+orderKey : "http://10.168.2.219:8081/os/paymentSelection?order_key="+orderKey; + String contactUrl = "prd".equals(ENV) ? MY_DOMAIN+"/os/transfer?access_token="+token+"&target=tk-detail&id="+vo.getId() : "http://10.168.2.219:8081/os/transfer?access_token="+token+"&target=tk-detail&id="+vo.getId(); + String refuseUrl = "prd".equals(ENV) ? MY_DOMAIN+"/afterSale/ticket/mailRefusePay" : MY_DOMAIN+":9808/ticket/mailRefusePay"; + String content="\n" + + "\n" + + "\n" + + "\n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + "\n" + + " \n" + + " \n" + + "\n" + + " \n" + + " \n" + + "\n" + + " \n" + + " \n" + + "\n" + + " \n" + + " \n" + + " \n" + + " \n" + + "\n" + + " \n" + + "\n" + + " \n" + + " \n" + + " \n" + + "\n" + + " \n" + + " \n" + + " \n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "
\n" + + " \n" + + "\n" + + " \n" + + "
\n" + + " (Optional) This text will appear in the inbox preview, but not the email body. It can be used to supplement the email subject line or even summarize the email's contents. Extended text preheaders (~490 characters) seems like a better UX for anyone using a screenreader or voice-command apps like Siri to dictate the contents of an email. If this text is not included, email clients will automatically populate it using the text (including image alt text) at the start of the email's body.\n" + + "
\n" + + " \n" + + "\n" + + " \n" + + " \n" + + "
\n" + + " ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ \n" + + "
\n" + + " \n" + + "\n" + + " \n" + + "
\n" + + " \n" + + "\n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + "\n" + + " \n" + + " \n" + + " \n" + + "








\n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + "\n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + "\n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + "\n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + "\n" + + "
\n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + "
\n" + + "\n" + + "
\n" + + "

尊敬的用户您好!

\n" + + "

您机器编号 "+serialNo+" 的机器维修费用为: $"+vo.getTotal()+"

\n" + + //"

费用明细:

\n" + + "
    " +details+ + "
\n" + + "

请您确认以上消息,确认无误后请点击下方 \"去支付\" 按钮,我们在确认收款后即将对您的机器开始维修

\n" + + "

如果您对费用有疑问,请点击下方 \"联系客服\" 按钮,点击后将您的问题重新提交工单,解决问题后再回到本邮件进行后续操作

\n" + + "

如果您点击了 \"无需维修\" 按钮,我们将视为您无意再修复您的机器,点击后请您填写您的收货地址,我们将依照您填写的信息寄回您的机器,期间产生的费用以及损失将由您自己承担

\n" + + "
\n" + + "
\n" + + " \n" + + " \n" + + " \n" + + " \n" + + "
\n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + "
\n" + + "

\n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + "
\n" + + " 去支付\n" + + " \n" + + " 联系客服\n" + + " \n" + + " 无需维修\n" + + "
\n" + + " \n" + + "

\n" + + "
\n" + + "
\n" + + "
\n" + + "  \n" + + "
\n" + + " \n" + + " \n" + + " \n" + + " \n" + + "
\n" + + "

Thank you for your support of thewindminer!

\n" + + "
\n" + + "
\n" + + " \n" + + "\n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + "
\n" + + " \n" + + "

\n" + + " \n" + + "

\n" + + " thewindminer\n" + + "
\n" + + " \n" + + "\n" + + " \n" + + "
\n" + + "\n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + "
\n" + + "
\n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + "
\n" + + "

TheWindMiner is the world-leading cryptocurrency mining manufacturer. We are committed to developing the most efficient mining products and providing the highest quality services.

\n" + + "
\n" + + " \n" + + "
\n" + + "
\n" + + " \n" + + "\n" + + " \n" + + "
\n" + + "\n"+ + "\n" + + "\n" + + ""; + sendHtmlMailMessage(to,"The Wind Miner 维修报价详情",content); + System.out.println(content); + } + + @Override + public void sendWCPaymentSuccessMailMessage(OrderInfoDto order, String company, String no) { + String msg= "

尊敬的用戶您好!

" + + "

您在thewindminer网站上的订单已经支付完成,我们会及时为你安排发货

" + + "

订单号:" + order.getOrderId() + "

" + + "

您可以通过下方的物流详情在对应的平台查询您的物流信息:

" + + "

物流公司: " + company + "

" + + "

物流编号: " + no + "

" + + "
" + + "

如果查询不到您的物流信息,您可以发送邮件至 support@thewindminer.com或前往thewindminer.com,提交工单寻求帮助。

" + + "

感謝您对thewindminer的支持!

"; + String content = getContent(msg); + sendHtmlMailMessage(order.getEmail(),"The Wind Miner 发货通知",content); + } + + @Override + public void sendShippingStartHTMLMailMessage(OrderInfoDto order, String company, String no) { + String msg= "

尊敬的用戶您好!

" + + "

您在thewindminer网站上的订单已经发货

" + + "

订单号:" + order.getOrderId() + "

" + + "

您可以通过下方的物流详情在对应的平台查询您的物流信息:

" + + "

物流公司: " + company + "

" + + "

物流编号: " + no + "

" + + "
" + + "

如果查询不到您的物流信息,您可以发送邮件至 support@thewindminer.com或前往thewindminer.com,提交工单寻求帮助。

" + + "

感謝您对thewindminer的支持!

"; + String content = getContent(msg); + sendHtmlMailMessage(order.getEmail(),"The Wind Miner 发货通知",content); + } + + + public String getContent(String msg) { + String content ="\n" + + "\n" + + "\n" + + "\n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + "\n" + + " \n" + + " \n" + + "\n" + + " \n" + + " \n" + + "\n" + + " \n" + + " \n" + + "\n" + + " \n" + + " \n" + + " \n" + + " \n" + + "\n" + + " \n" + + "\n" + + " \n" + + " \n" + + " \n" + + "\n" + + " \n" + + " \n" + + " \n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "
\n" + + " \n" + + "\n" + + " \n" + + "
\n" + + " (Optional) This text will appear in the inbox preview, but not the email body. It can be used to supplement the email subject line or even summarize the email's contents. Extended text preheaders (~490 characters) seems like a better UX for anyone using a screenreader or voice-command apps like Siri to dictate the contents of an email. If this text is not included, email clients will automatically populate it using the text (including image alt text) at the start of the email's body.\n" + + "
\n" + + " \n" + + "\n" + + " \n" + + " \n" + + "
\n" + + " ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ \n" + + "
\n" + + " \n" + + "\n" + + " \n" + + "
\n" + + " \n" + + "\n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + "\n" + + " \n" + + " \n" + + " \n" + + "








\n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + "\n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + "\n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + "\n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + "\n" + + "
\n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + "
\n" + + "\n" + + "
\n" + + "

尊敬的用户您好!

\n" + + " "+msg+"\n" + + "
\n" + + "
\n" + + " \n" + + " \n" + + " \n" + + " \n" + + "
\n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + "
\n" + + "

\n" + + "\n" + + "

\n" + + "
\n" + + "
\n" + + "
\n" + + "  \n" + + "
\n" + + " \n" + + " \n" + + " \n" + + " \n" + + "
\n" + + "

Thank you for your support of thewindminer!

\n" + + "
\n" + + "
\n" + + " \n" + + "\n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + "
\n" + + " \n" + + "

\n" + + " \n" + + "

\n" + + " thewindminer\n" + + "
\n" + + " \n" + + "\n" + + " \n" + + "
\n" + + "\n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + "
\n" + + "
\n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + "
\n" + + "

TheWindMiner is the world-leading cryptocurrency mining manufacturer. We are committed to developing the most efficient mining products and providing the highest quality services.

\n" + + "
\n" + + " \n" + + "
\n" + + "
\n" + + " \n" + + "\n" + + " \n" + + "
\n" + + "\n" + + "\n" + + ""; + return content; + } +} diff --git a/src/main/java/com/jxy/windminer/service/impl/OrderServiceImpl.java b/src/main/java/com/jxy/windminer/service/impl/OrderServiceImpl.java new file mode 100644 index 0000000..40a827a --- /dev/null +++ b/src/main/java/com/jxy/windminer/service/impl/OrderServiceImpl.java @@ -0,0 +1,1307 @@ +package com.jxy.windminer.service.impl; + +import cn.hutool.http.HttpRequest; +import cn.hutool.http.HttpUtil; +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONArray; +import com.alibaba.fastjson.JSONObject; +import com.alibaba.fastjson.TypeReference; +import com.jxy.windminer.common.constant.Constants; +import com.jxy.windminer.common.redis.service.RedisService; +import com.jxy.windminer.common.text.Convert; +import com.jxy.windminer.common.utils.DateUtils; +import com.jxy.windminer.common.utils.StringUtils; +import com.jxy.windminer.common.utils.WalletVerifyUtils; +import com.jxy.windminer.common.utils.sign.Base64; +import com.jxy.windminer.common.utils.uuid.IdUtils; +import com.jxy.windminer.common.utils.MysqlBackUpUtils; +import com.jxy.windminer.common.web.Result.AjaxResult; +import com.jxy.windminer.dto.*; +import com.jxy.windminer.entity.OrderKeyAddress; +import com.jxy.windminer.enums.OrderFrom; +import com.jxy.windminer.enums.TicketStatusType; +import com.jxy.windminer.enums.WCOrderStatus; +import com.jxy.windminer.mapper.OrderMapper; +import com.jxy.windminer.mapper.TicketMapper; +import com.jxy.windminer.service.MailService; +import com.jxy.windminer.service.OrderService; +import com.jxy.windminer.vo.*; +import org.springframework.beans.BeanUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.transaction.interceptor.TransactionAspectSupport; + +import javax.imageio.ImageIO; +import java.awt.image.BufferedImage; +import java.io.*; +import java.math.BigDecimal; +import java.util.*; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; + +/** + * 系统访问日志情况信息 服务层处理 + * + * @author 杜懿 + */ +@Service +public class OrderServiceImpl implements OrderService +{ + @Autowired + private OrderMapper orderMapper; + + @Autowired + private TicketMapper ticketMapper; + + @Autowired + private MailService mailService; + + @Autowired + private RedisService redisService; + + @Value("${my.domain}") + private String MY_DOMAIN; + + @Value("${my.env}") + private String ENV; + + + + @Override + @Transactional(rollbackFor = Exception.class) + public AjaxResult handleOrder(OrderHandleVo vo) { + + System.out.println("执行支付判断的请求vo"+vo); + vo.setAddress(""); + +/******** 校验参数 开始 *************************************************/ + //order_Key 判空 不为空查询该order_key对应的数据 + if(StringUtils.isNull(vo)){ + return AjaxResult.error("请求体参数丢失!"); + } + if(StringUtils.isNull(vo.getOrderKey())){ + return AjaxResult.error("参数丢失"); + } + + String[] chainArray= {"ETH","POLYGON","OPTIMISM","ARBITRUM","TRON","TRX"}; + String[] coinArray= {"USDT","USDC"}; + + if(!Arrays.asList(chainArray).contains(vo.getChain().toUpperCase())){ + return AjaxResult.error("参数不支持"); + } + + if(!Arrays.asList(coinArray).contains(vo.getCoin().toUpperCase())){ + return AjaxResult.error("不支持该币种"); + } + + String key = vo.getOrderKey(); + String redirectTo = ""; + String orderFrom = ""; + int orderStatus = 0; + double amount = 0; + OrderInfoDto orderInfo = null; + int orderId = 0; + //先判断工单来源 + if(key.startsWith(Constants.WORDPRESS_ORDER_PREFIX)) + { + System.out.println("商城订单"); + orderInfo = orderMapper.selectOrderAndWpStatusInfoByOrderKey(vo.getOrderKey()); + //OrderInfoDto orderInfo = orderMapper.selectOrderInfoByOrderKey(vo.getOrderKey()); + vo.setDate(orderInfo.getDate()); + if(StringUtils.isNull(orderInfo)){ + return AjaxResult.error("参数错误"); + } + if(!WCOrderStatus.PENDING.getEnStatus().equals(orderInfo.getWpStatus())){ + return AjaxResult.error("当前订单状态不可支付"); + } + + key = orderInfo.getOrderKey(); + vo.setDate(orderInfo.getDate()); + redirectTo = orderInfo.getViewUrl(); + orderStatus = orderInfo.getStatus(); + amount = orderInfo.getAmount(); + orderId = orderInfo.getOrderId(); + orderFrom = "wc"; + } + else if(key.startsWith(Constants.TICKET_ORDER_PREFIX)){ + System.out.println("维修订单"); + TicketOrderBaseInfoDto tkOrderInfo = orderMapper.selectTicketOrderInfoByOrderKey(vo.getOrderKey()); + + if(StringUtils.isNull(tkOrderInfo)){ + return AjaxResult.error("order_key错误"); + } + vo.setDate(tkOrderInfo.getDate()); + TicketPaymentDto tpDto = ticketMapper.getTicketPaymentDtoByOrderId(tkOrderInfo.getOrderId()); + if(StringUtils.isNull(tpDto)){ + return AjaxResult.error("数据异常"); + } + TicketDto ticket = ticketMapper.getTicketById(tpDto.getTicketId()); + if (!ticket.getEmail().equals(tkOrderInfo.getEmail())){ + return AjaxResult.error("当前维修订单异常,订单信息与当前用户不匹配"); + } + key = tkOrderInfo.getOrderKey(); + vo.setDate(tkOrderInfo.getDate()); + redirectTo = tkOrderInfo.getViewUrl(); + orderStatus = tkOrderInfo.getStatus(); + amount = tkOrderInfo.getAmount(); + orderId = tkOrderInfo.getOrderId(); + orderFrom = "tk"; + }else { + return AjaxResult.error("违规order_key"); + } + + + //OrderKeyAddress orderKeyAddress = orderMapper.getAddressByOrderKey(key); + + OrderKeyAddress orderKeyAddress = orderMapper.getAddressByOrderKey(key); + if(StringUtils.isNull(orderKeyAddress)){ + return AjaxResult.error("服务器繁忙"); + } + /******** 校验参数 开始 *************************************************/ + String address = orderKeyAddress.getEthAddress(); + if("TRX".equals(vo.getChain().toUpperCase()) || "TRON".equals(vo.getChain().toUpperCase())){ + if(!"USDT".equals(vo.getCoin().toUpperCase())){ + return AjaxResult.error("当前交易链不支持此币种"); + } + address = orderKeyAddress.getTrxAddress(); + } + + vo.setAddress(address); + //vo.setDate(orderInfo.getDate()); + + //判断是否在可支付有效期 + if(!redisService.hasKey("order_key:"+vo.getOrderKey())){ + // + System.out.println("order_key:"+vo.getOrderKey()+"的redis缓存已过期"); + AjaxResult ajaxResult = AjaxResult.success("该order_key对应的有效期已过,点击确定跳转到工单官网"); + ajaxResult.put("redirectTo",redirectTo); + return ajaxResult; + } + + + + //String s = WalletVerifyUtils.verifyAddress(vo.getChain().toUpperCase(),vo.getCoin().toUpperCase(),vo.getAddress()); + // + //String s = "[{\"from\":\"TEYjVpKjWsYUEoP1LACS9FKMaCp9RiFnva\",\"to\":\"TA99nFwLopuUcF3zA7pApTqnWU52yPYSpZ\",\"txid\":\"732982a6a7a41ad78a9cdbe050799fd2cc4c24ca540634d76440561eecf1c2b3\",\"amount\":\"500000000\",\"timestamp\":1716187855000}]"; + String s = "prd".equals(ENV) ? WalletVerifyUtils.verifyAddress(vo.getChain().toUpperCase(),vo.getCoin().toUpperCase(),vo.getAddress()) : "[{\"from\":\"TEYjVpKjWsYUEoP1LACS9FKMaCp9RiFnva\",\"to\":\""+vo.getAddress()+"\",\"txid\":\"732982a6a7a41ad78a9cdbe050799fd2cc4c24ca540634d76440561eecfx0001\",\"amount\":\""+Convert.toInt(amount*1000000)+"\",\"timestamp\":"+DateUtils.getNowDate().getTime()+"}]"; +/******************************* 判断请求是否成功 开始***********************************************/ + if(StringUtils.isBlank(s) || "[]".equals(s)){ + if("ETH".equals(vo.getChain().toUpperCase())){ + return AjaxResult.error("We did not find your transaction information on the blockchain.Please Make Sure Your transaction information can be found on the blockchain.You can try again when your transaction information can be found on the blockchain."); + } + if("TRX".equals(vo.getChain().toUpperCase())){ + //trx查询为空返回的是[]为不是blank + return AjaxResult.error("We did not find your transaction information on the blockchain.Please Make Sure Your transaction information can be found on the blockchain.You can try again when your transaction information can be found on the blockchain."); + } + } +/******************************* 判断请求是否成功 结束***********************************************/ + + + System.out.println("接口返回数据:"+s+"[aaa]"); + + //eth和BSC需要先把时间轴转化为毫秒 + if("ETH".equals(vo.getChain().toUpperCase())){ + List dataList = JSONObject.parseArray(s, String.class); + + for(int i=0; i< dataList.size();i++){ + String e = dataList.get(i); + JSONObject jsonObject = JSON.parseObject(e); + + Long ts = Convert.toLong(jsonObject.get("timestamp")); + jsonObject.put("timestamp",ts*1000); + dataList.set(i,jsonObject.toString()); + } + s = dataList.toString(); + } + + JSONObject.DEFFAULT_DATE_FORMAT = "dd/MM/yyyy HH:mm:ss"; + List list = JSONArray.parseArray(s, OnChainMsgByAddressDto.class); + System.out.println("所有数据:"+list); + + //筛选出 to和address相等 即转入本公司地址的交易信息 + List byAddressList = list.stream().filter(e1 -> vo.getAddress().toLowerCase().equals(e1.getAddress().toLowerCase())).collect(Collectors.toList()); + System.out.println("根据地址筛选后的数据:"+byAddressList); + //筛选出时间符合要求的数据 + List list2 = byAddressList.stream().filter(e2 -> e2.getTime().after(vo.getDate())).collect(Collectors.toList()); + System.out.println("时间筛选后的数据:"+list2); + + if(list2.size() > 0){ + //有订单创建之后的链上交易数据 判断转账金额 + //resultDto.setResult(true); + BigDecimal unit = getAmountUnit(vo); + //list2中的amount进制转换 + BigDecimal total = BigDecimal.ZERO; + String txids=""; + for (OnChainMsgByAddressDto e : list2) { + total = total.add(e.getAmount().divide(unit,2,BigDecimal.ROUND_DOWN)); + txids += e.getTxid(); + } + + //orderInfo = orderMapper.selectOrderInfoByOrderKey(vo.getOrderKey()); + + if(orderStatus >0){ + //订单已处理过 无需再次处理 + AjaxResult ajaxResult = AjaxResult.success(); + ajaxResult.put("redirectTo",redirectTo); + return ajaxResult; + } + if(total.compareTo(BigDecimal.valueOf(amount)) >= 0){ + + OrderUpdateVo updateVo = new OrderUpdateVo(); + updateVo.setAmount(total.doubleValue()); + updateVo.setOrderKey(vo.getOrderKey()); + updateVo.setCurrency(vo.getCoin()); + updateVo.setChain(vo.getChain()); + updateVo.setStatus(1); + orderMapper.updateOrderInfoPaymentByOrderKey(updateVo); + + System.out.println("orderFrom:"+orderFrom); + if("wc".equals(orderFrom)){ + + Map map = new HashMap<>(); + + System.out.println("回调前准备"); + //System.out.println("数据库当前数据:"+orderInfo); + + map.put("transaction_id",orderInfo.getTransactionId()); + map.put("status_code",200); + map.put("note","success"); + map.put("confirm_rcv_amnt",total); + map.put("confirm_rcv_amnt_curr",vo.getCoin()); + map.put("coin_rcv_amnt",orderInfo.getAmount()); + map.put("coin_rcv_amnt_curr",orderInfo.getCurrency()); + map.put("txn_time", DateUtils.dateTime()); + map.put("order_id",orderInfo.getOrderId()); + HttpRequest post = HttpUtil.createPost(orderInfo.getNotifyUrl()); + String body = post.contentType("application/x-www-form-urlencoded").form(map).timeout(20000).execute().body(); + AjaxResult ajaxResult = AjaxResult.success("This order has completed"); + ajaxResult.put("redirectTo",redirectTo); + CompletableFuture.runAsync(()-> { + redisService.deleteObject("order_key:" + vo.getOrderKey()); + //邮件通知? + + }); + + return ajaxResult; + } + else if("tk".equals(orderFrom)){ + //修改paymentInfo表 + //判断工单状态是否是已报价,等待付款 + //if(ticket.getStatus() != TicketStatusType.STATUS_FIVE.getCode()){ + // return AjaxResult.error("该工单不允许此操作"); + //} + //修改payment_info信息表 + System.out.println(111); + PaymentInfoVo piVo = new PaymentInfoVo(); + piVo.setChain(vo.getChain()); + piVo.setCoin(vo.getCoin()); + piVo.setTxId(txids); + piVo.setActual(Convert.toDouble(total)); + piVo.setState(1); + ticketMapper.updateTicketPayment(piVo); + ///修改工单状态 修改为已付款 + boolean b = ticketMapper.changeTicketStatus(orderId, TicketStatusType.STATUS_SIX.getCode()); + + if(b){ + CompletableFuture.runAsync(()-> { + redisService.deleteObject("order_key:" + vo.getOrderKey()); + //邮件通知? + + }); + AjaxResult ajaxResult = AjaxResult.success("工单维修费用支付成功"); + ajaxResult.put("redirectTo",redirectTo); + return ajaxResult; + }else { + return AjaxResult.error("服务器繁忙"); + } + }else { + return AjaxResult.success(); + } + }else { + //支付金额小于应付金额 + AjaxResult ajaxResult = new AjaxResult(501,"We have found that the amount you transferred is not enough to pay for this order. Please pay the remaining amount and click the confirm payment button again"); + //ajaxResult.put("redirectTo",orderInfo.getRedirectTo()); + return ajaxResult; + } + + }else{ + return AjaxResult.error("We did not find your transaction information on the blockchain.Please Make Sure Your transaction information can be found on the blockchain.You can try again when your transaction information can be found on the blockchain."); + } + } + + @Override + public AjaxResult getPayAddressByUserEmailAndType(AddressVo vo) { + System.out.println("vo"+vo); + if(StringUtils.isNull(vo)){ + return AjaxResult.error("参数缺失"); + } + //验证邮箱格式 + //System.out.println(vo); + //if(StringUtils.isAnyBlank(vo.getChain(),vo.getEmail())){ + // return AjaxResult.error("参数不能为空"); + //} + //String regex ="^[a-zA-Z0-9][\\w\\.-]*[a-zA-Z0-9]@[a-zA-Z0-9][\\w\\.-]*[a-zA-Z0-9]\\.[a-zA-Z][a-zA-Z\\.]*[a-zA-Z]$"; + //if(!vo.getEmail().matches(regex)){ + // return AjaxResult.error("邮箱格式错误"); + //} + ////验证邮箱是否已有对应地址 -->验证订单id是否已有对应地址 + String[] ethChain ={"ETH","POLYGON","OPTIMISM","ARBITRUM"}; + boolean isETH = Arrays.asList(ethChain).contains(vo.getChain().toUpperCase()); + + //校验orderkey + if(vo.getKey().startsWith("wc_order") || vo.getKey().startsWith("tk_order")){ + + }else { + return AjaxResult.error("参数错误:订单号"); + } + if(StringUtils.isAnyBlank(vo.getKey())){ + return AjaxResult.error("未获取到订单key"); + } + + if(redisService.hasKey("order_key:"+vo.getKey())){ + + }else { + return AjaxResult.error("订单已过支付有效期,无法获取支付地址"); + } + + + if(isETH || vo.getChain().equals("TRX") || vo.getChain().equals("TRON")){ + OrderKeyAddress dto = orderMapper.getAddressByOrderKey(vo.getKey()); + OrderInfoDto orderInfoDto = orderMapper.selectOrderAndWpStatusInfoByOrderKey(vo.getKey()); + if(StringUtils.isNull(orderInfoDto)){ + return AjaxResult.error("未查询到该订单号的订单"); + } + + if(OrderFrom.WORDPRESS.getCode() == orderInfoDto.getOrderFrom()){ + //商城订单判断 wpstatus + if(WCOrderStatus.CANCELLED.getEnStatus().equals(orderInfoDto.getWpStatus()) || + WCOrderStatus.FAILED.getEnStatus().equals(orderInfoDto.getWpStatus())){ + return AjaxResult.error("该订单已被取消,不能再获取支付地址"); + }else if(WCOrderStatus.ON_HOLD.getEnStatus().equals(orderInfoDto.getWpStatus())){ + return AjaxResult.error("该订单已支付已经支付过全部应付款,不能再获取支付地址"); + }else if(WCOrderStatus.COMPLETED.getEnStatus().equals(orderInfoDto.getWpStatus())){ + return AjaxResult.error("该订单已完成,不能再获取支付地址"); + }else if(WCOrderStatus.DEFAULT.getEnStatus().equals(orderInfoDto.getWpStatus())){ + return AjaxResult.error("服务器获取支付地址失败,请稍后再试"); + }else { + + } + }else if(OrderFrom.TICKET.getCode() == orderInfoDto.getOrderFrom()){ + //维修订单判断是否已取消支付 + TicketOrderBaseInfoDto ticketOrder = orderMapper.selectTicketOrderInfoByOrderKey(vo.getKey()); + if(StringUtils.isNull(ticketOrder)){ + return AjaxResult.error("您的维修工单从服务器获取支付地址失败,请稍后再试"); + } + TicketPaymentDto ticketPaymentDtoBy = ticketMapper.getTicketPaymentDtoByOrderId(ticketOrder.getOrderId()); + if (!"0".equals(ticketPaymentDtoBy.getPayStatus())){ + return AjaxResult.error("您的维修工单从服务器获取支付地址失败,请稍后再试"); + } + } + else{ + return AjaxResult.error("订单来源解析失败,请尝试重新请求支付地址或者直接联系管理员"); + } + + if("TRON".equals(vo.getChain())){ + vo.setChain("TRX"); + } + Map cacheMap = redisService.getCacheMap("order_key:" + vo.getKey()); + //todo 改成动态取值 + String coin= vo.getCoin(); + if(!isETH){ + //TRC20币种只支持USDT + coin= "USDT"; + } + if(StringUtils.isBlank(coin)){ + return AjaxResult.error("币种传参获取失败"); + } + //String email = cacheMap.get("email"); + //if(StringUtils.isBlank(email)){ + // return AjaxResult.error("服务器繁忙,请联系管理员"); + //} + // + //EmailAddress addressByEmail = orderMapper.getAddressByEmail(email); + cacheMap.put("coin",coin); + cacheMap.put("chain",vo.getChain()); + if(isETH){ + vo.setChain("ETH"); + } + if(StringUtils.isNull(dto)){ + //当前邮箱未获取过对应地址 + dto = new OrderKeyAddress(); + dto = getAddress(dto,vo.getKey(), vo.getChain()); + if(StringUtils.isNull(dto)){ + return AjaxResult.error("未能获取到地址,暂不能支付,请稍后再试"); + }else { + String qrCode = getQRCode(vo.getChain(), dto); + if(StringUtils.isBlank(qrCode)) { + return AjaxResult.error("当前地址获取二维码失败,请稍后再试,多次失败请通过官网联系我们"); + } + dto.setImage(qrCode); + redisService.setCacheMap("order_key:"+vo.getKey(),cacheMap); + CompletableFuture.runAsync(()->{ + MysqlBackUpUtils.addrBackUp(); + }); + return AjaxResult.success(dto); + } + }else { + //当前邮箱已获取过地址 1只获取过ETH地址 2只获取过TRX地址 3两个地址都获取过 + if(isETH && StringUtils.isNull(dto.getEthAddress())){ + OrderKeyAddress oldData = getAddress(dto,vo.getKey(), vo.getChain()); + if(StringUtils.isNull(oldData)){ + return AjaxResult.error("未能获取到地址,暂不能支付,请稍后再试"); + }else { + dto.setEthAddress(oldData.getEthAddress()); + //生成二维码 + String qrCode = getQRCode(vo.getChain(), dto); + if(StringUtils.isBlank(qrCode)) { + return AjaxResult.error("当前地址获取二维码失败,请稍后再试,多次失败请通过官网联系我们"); + } + dto.setImage(qrCode); + redisService.setCacheMap("order_key:"+vo.getKey(),cacheMap); + CompletableFuture.runAsync(()->{ + MysqlBackUpUtils.addrBackUp(); + }); + return AjaxResult.success(dto); + } + } + else if("TRX".equals(vo.getChain()) && StringUtils.isNull(dto.getTrxAddress())){ + System.out.println("dto:"+dto); + OrderKeyAddress oldData = getAddress(dto,vo.getKey(), vo.getChain()); + if(StringUtils.isNull(oldData)){ + + return AjaxResult.error("未能获取到地址,暂不能支付,请稍后再试"); + }else { + dto.setTrxAddress(oldData.getTrxAddress()); + //获取二维码 + String qrCode = getQRCode(vo.getChain(), dto); + if(StringUtils.isBlank(qrCode)) { + System.out.println(1); + return AjaxResult.error("当前地址获取二维码失败,请稍后再试,多次失败请通过官网联系我们"); + } + dto.setImage(qrCode); + redisService.setCacheMap("order_key:"+vo.getKey(),cacheMap); + CompletableFuture.runAsync(()->{ + MysqlBackUpUtils.addrBackUp(); + }); + return AjaxResult.success(dto); + } + } + String qrCode = getQRCode(vo.getChain(), dto); + if(StringUtils.isBlank(qrCode)) { + System.out.println("qrCode null"); + return AjaxResult.error("当前地址获取二维码失败,请稍后再试,多次失败请通过官网联系我们"); + } + dto.setImage(qrCode); + return AjaxResult.success(dto); + } + }else { + return AjaxResult.error("参数错误"); + } + } + + @Override + public AjaxResult getPaymentOrderInfo(OrderApplyVo vo) { + System.out.println("收到参数"+vo); + //参数校验 + if(StringUtils.isAnyBlank(vo.getOrderKey(),vo.getBillingFname(),vo.getBillingLname(),vo.getBillingEmail(),vo.getCancelUrl(),vo.getNotifyUrl(),vo.getRedirectTo())){ + return AjaxResult.error("参数缺失"); + } + String regex ="^[a-zA-Z0-9][\\w\\.-]*[a-zA-Z0-9]@[a-zA-Z0-9][\\w\\.-]*[a-zA-Z0-9]\\.[a-zA-Z][a-zA-Z\\.]*[a-zA-Z]$"; + if(!vo.getBillingEmail().matches(regex)){ + return AjaxResult.error("邮箱格式错误"); + } + //判断网关中有没有记录 没有则表示第一次请求 + OrderInfoDto order = orderMapper.selectOrderAndWpStatusInfoByOrderKey(vo.getOrderKey()); + System.out.println("order数据查询结果: "+ order); + + AjaxResult ajaxResult = AjaxResult.success(); + //判断order_key是否存在 + if(StringUtils.isNotNull(order)){ + //判断订单类型 商城订单 or 维修订单 + if(order.getOrderFrom() == OrderFrom.WORDPRESS.getCode()){ + //商城订单 需要根据wordpress状态和支付有效期判断 + if(redisService.hasKey("order_key:"+order.getOrderKey()) + && WCOrderStatus.PENDING.getEnStatus().equals(order.getWpStatus())) { + //存在缓存 且wordpress数据表中的order状态要是待支付状态 说明还在可支付有效期 可跳到支付页面 + Map cacheMap = redisService.getCacheMap("order_key:" + order.getOrderKey()); + System.out.println("redis中的数据: " +cacheMap); + String coin = cacheMap.get("coin"); + System.out.println("redis存储的coin : "+coin); + String chain = cacheMap.get("chain"); + System.out.println("redis存储的chain : "+coin); + + String redirectTO = MY_DOMAIN+"/os/paymentSelection?order_key=" + vo.getOrderKey(); + + if(!StringUtils.isAnyBlank(coin,chain)){ + System.out.println("coin和chain都有值"); + coin = coin.toUpperCase(); + chain = chain.toUpperCase(); + //coin和chain都有值则 跳转到支付页面 + redirectTO= MY_DOMAIN+"/os/paymentPage?coin="+coin+"&chain="+chain+"&order_key=" + vo.getOrderKey() +"&shipping=KD"; + }else{ + redirectTO= MY_DOMAIN+"/os/paymentSelection?order_key="+vo.getOrderKey() + "&shipping=KD"; + } + + System.out.println("redirectTO: "+redirectTO); + ajaxResult.put("redirectTO",redirectTO); + return ajaxResult; + }else { + //不存在 说明不能再支付 直接跳订单详情页 + ajaxResult.put("redirectTO",vo.getViewUrl()); + return ajaxResult; + } + }else if(order.getOrderFrom() == OrderFrom.TICKET.getCode()){ + if(redisService.hasKey("order_key:"+order.getOrderKey())) { + //存在缓存 说明还在可支付有效期 可跳到支付页面 + Map cacheMap = redisService.getCacheMap("order_key:" + order.getOrderKey()); + System.out.println("redis中的数据: " +cacheMap); + String coin = cacheMap.get("coin"); + System.out.println("redis存储的coin : "+coin); + String chain = cacheMap.get("chain"); + System.out.println("redis存储的chain : "+coin); + + String redirectTO = MY_DOMAIN+"/os/paymentSelection?order_key=" + vo.getOrderKey(); + + + if(!StringUtils.isAnyBlank(coin,chain)){ + System.out.println("coin和chain都有值"); + coin = coin.toUpperCase(); + chain = chain.toUpperCase(); + //coin和chain都有值则 跳转到支付页面 + redirectTO= MY_DOMAIN+"/os/paymentPage?coin="+coin+"&chain="+chain+"&order_key=" + vo.getOrderKey() +"&shipping=WX"; + }else{ + redirectTO= MY_DOMAIN+"/os/paymentSelection?order_key="+vo.getOrderKey() + "&shipping=WX"; + } + + System.out.println("redirectTO: "+redirectTO); + ajaxResult.put("redirectTO",redirectTO); + return ajaxResult; + }else { + //不存在 说明不能再支付 直接跳订单详情页 + ajaxResult.put("redirectTO",vo.getViewUrl()); + return ajaxResult; + } + }else { + System.out.println(""); + return AjaxResult.error("异常数据"); + } + + } + //走到此处说明order_key不存在数据库中 (走到此处的订单只能是商城订单 微信订单是在维修报价时生成维修订单信息) 即第一次请求 此时需要通知后台管理员 + //记录vo到数据库并生成txid + String transaction_id = IdUtils.fastSimpleUUID(); + if(orderMapper.insertOrderInfo(vo,transaction_id, OrderFrom.WORDPRESS.getCode(), vo.getCount())){ + //String redirectTO = "https://www.thewindminer.com/?page_id=1817"; + //通过redis设置有效期 + Map selectMap = new HashMap<>(); + selectMap.put("transaction_id", transaction_id); + selectMap.put("email",vo.getBillingEmail()); + System.out.println("将要放入redis的数据 : "+selectMap); + //通过redis设置 支付有效期 + redisService.setCacheMap("order_key:"+vo.getOrderKey(),selectMap); + redisService.expire("order_key:"+vo.getOrderKey(),180L,TimeUnit.MINUTES); + + + + //跳转到提示信息页面 + //String redirectTO = "https://test.thewindminer.org/index.php/payment-notice/?order_key="+vo.getOrderKey(); + String redirectTO = MY_DOMAIN+"/os/paymentSelection?order_key=" + vo.getOrderKey()+"&shipping=KD"; + //try { + // mailService.sendHtmlMailMessage("1328642438@qq.com","用户提交订单提示","订单:| "+vo.getOrderKey()+" |支付请求待确认"); + // System.out.println("order:"+order); + //}catch (Exception e){ + // e.printStackTrace(); + //} + ajaxResult.put("redirectTO",redirectTO); + }else { + ajaxResult.put("redirectTO",vo.getCancelUrl()); + } + return ajaxResult; + + } + + @Override + public AjaxResult getOrderInfoByOrderKey(OrderKeyVo vo) { + if(StringUtils.isNull(vo)){ + return AjaxResult.error(); + } + if(StringUtils.isNotBlank(vo.getOrderKey())){ + OrderInfoDto orderInfoDto = orderMapper.selectOrderAndWpStatusInfoByOrderKey(vo.getOrderKey()); + + if(StringUtils.isNull(orderInfoDto)){ + return AjaxResult.error("order_key错误"); + }else { + OrderInfoReturnDto dto = new OrderInfoReturnDto(); + BeanUtils.copyProperties(orderInfoDto,dto); + if(redisService.hasKey("order_key:"+vo.getOrderKey())){ + dto.setExpireTime(redisService.getExpire("order_key:"+vo.getOrderKey())); + }else { + dto.setExpireTime(0); + } + return AjaxResult.success(dto); + } + }else { + return AjaxResult.error("order_key为空"); + } + + } + + @Override + public AjaxResult getAddressSurplus() { + Map map = new HashMap<>(); + int eth = orderMapper.getAddressNoUsedCount("ETH"); + //因为支付中还是用的TRX 所以数据库中TRX 对应前端显示得TRON + int tron = orderMapper.getAddressNoUsedCount("TRX"); + map.put("eth",eth); + map.put("tron",tron); + return AjaxResult.success(map); + } + + @Override + public List getUesdAddressList(AddressUsedVo vo) { + + List list = new ArrayList<>(); + + //if(SecurityUtils.getLoginUser().getSysUser().isAdmin()){ + // return list; + //} + + if(StringUtils.isNull(vo)){ + return list; + } + + if(StringUtils.isNotBlank(vo.getChain())){ + vo.setChain(vo.getChain().toUpperCase()); + } + + if("ETH".equals(vo.getChain()) || "TRX".equals(vo.getChain())){ + //根据type和idcard筛选 + // 0 全部(已使用) + //1、商城订单 + //2、 维修订单 + + if(vo.getType() == 1){ + //商城订单 + vo.setOrderFrom("0"); + } + + if(vo.getType() == 2){ + //维修订单 + vo.setOrderFrom("1"); + } + + list = orderMapper.getAddressUsedListByChain(vo); + } + + return list; + } + + @Override + public List getTotalAddressList(TotalAddressVo vo) { + + long t1 = System.currentTimeMillis(); + List list = new ArrayList<>(); + + //if(SecurityUtils.getLoginUser().getSysUser().isAdmin()){ + // return list; + //} + + if(StringUtils.isNull(vo)){ + return list; + } + + if("ETH".equals(vo.getChain()) || "TRX".equals(vo.getChain())){ + System.out.println(vo.getChain()); + + list = orderMapper.getTotalAddressList(vo); + } + + return list; + } + + @Override + public AjaxResult + getOrderDetails(OrderKeyVo vo) { + if(StringUtils.isNull(vo)){ + return AjaxResult.error("未接收到参数"); + } + + if(StringUtils.isBlank(vo.getOrderKey())){ + return AjaxResult.error("参数获取异常"); + } + if(vo.getOrderKey().startsWith("tk_order_")){ + vo.setOrderFrom("1"); + }else if(vo.getOrderKey().startsWith("wc_order_")){ + vo.setOrderFrom("0"); + } + + //根据orderkey区分商城订单和维修订单详情 + //先看网关数据库有没有订单 联表查派件物流 + + System.out.println(vo); + OrderAndOrderLogisticalDto order = orderMapper.selectOrderAndLogisticalInfoByOrderKey(vo); + + System.out.println("详情接口获取到数据:"+order); + + if(StringUtils.isNull(order)){ + return AjaxResult.error("订单详情获取失败,请稍后再试"); + } + + //存在order 则根据order来源判断 + //构建返回体 + AddressOrderDetailDto detailDto = new AddressOrderDetailDto(); + + detailDto.setOrderId(order.getOrderId()); + detailDto.setUsername(Convert.toStr(order.getBillingFname(), "").concat(Convert.toStr(order.getBillingLname(), ""))); + detailDto.setEmail(Convert.toStr(order.getEmail(), "")); + detailDto.setLogisticId(Convert.toStr(order.getCompany())); + detailDto.setLogisticNo(Convert.toStr(order.getTrackingNumber())); + + //根据order来源判断 + if(OrderFrom.WORDPRESS.getCode() == order.getOrderFrom()){ + detailDto.setIfSend(order.getIfSend()); + detailDto.setOrderFrom(OrderFrom.WORDPRESS.getCode()); + //从wordpress拿数据 + //通过order_id 拿到wp订单状态、总金额、city、address1、address2以及用户联系方式 + WPOrderAddressDto baseInfo = orderMapper.getOrderBaseInfoFromWP(order.getOrderId()); + System.out.println(baseInfo); + + + if(StringUtils.isNull(baseInfo)){ + return AjaxResult.error("服务器请求失败,请稍后再试"); + } + + detailDto.setPhone(baseInfo.getPhone()); + detailDto.setUserLogistics(Convert.toStr(baseInfo.getCity(),"")+"市区"); + + if(StringUtils.isNotBlank(baseInfo.getAddress1())){ + detailDto.setUserLogistics(detailDto.getUserLogistics()+","+Convert.toStr(baseInfo.getAddress1())); + } + + if(StringUtils.isNotBlank(baseInfo.getAddress2())){ + detailDto.setUserLogistics(detailDto.getUserLogistics()+","+Convert.toStr(baseInfo.getAddress2())); + } + + //获取order_id 对应的商品list 遍历构造金额详情 内容包括 商品 * 数量 =商品总价、邮费总计、优惠券抵扣、总计应收 最后将支付网关order中的实际收款金额写入详情 + List list = orderMapper.getOrderProductInfoFromWP(order.getOrderId()); + + //考虑到wordpress数据表更新有未知延迟 list可以未空 如果list为空则 details设置空 + //构造details + if(list.size() < 1){ + //未查询到 直接把details设置为null + detailDto.setDetails(""); + }else { + // 商品1 单价 * 数量 = 商品1总价 + // 商品2 单价 * 数量 = 商品2总价 + // ... + // 运费 + StringBuilder detailSb = new StringBuilder(); + detailSb.insert(0,"
"); + StringBuilder priceStr = new StringBuilder(); + //priceStr.append("商品详情"); + priceStr.append("
"); + priceStr.append(""); + //报价详情表头 + priceStr.append(""); + priceStr.append(""); + priceStr.append(""); + priceStr.append(""); + priceStr.append(""); + StringBuilder freightStr = new StringBuilder(); + //freightStr.append("总邮费:"); + StringBuilder couponSrt = new StringBuilder(); + couponSrt.append("
优惠券折扣:
"); + //总 + BigDecimal coupon = BigDecimal.ZERO; + BigDecimal total = BigDecimal.ZERO; + BigDecimal freight = BigDecimal.ZERO; + BigDecimal shippingTax = BigDecimal.ZERO; + BigDecimal tax = BigDecimal.ZERO; + String product =""; + int proNum = 0; + + for(int i=0;i"); + priceStr.append("
"); + product = pDto.getProduct(); + priceStr.append(""); + priceStr.append(""); + priceStr.append(""); + proNum += pDto.getQty(); + freight = freight.add(pDto.getShippingAmount()); + coupon = coupon.add(pDto.getCoupon()); + total = total.add(pDto.getTotalPrice()).add(pDto.getShippingAmount()); + shippingTax = shippingTax.add(pDto.getShippingTax()); + tax = tax.add(pDto.getTax()); + } + priceStr.append("
商品名单价数量
"+pDto.getProduct()+""+ pDto.getPrice() +""+ pDto.getQty() +"
"); + freightStr.append("
总邮费:
"+ freight +"
"); + + + detailDto.setProduct(product); + detailDto.setProNumber(proNum); + + total = total.subtract(coupon).add(shippingTax).add(tax); + + couponSrt.append(coupon).append("
"); + + detailSb.append(priceStr).append(couponSrt).append(freightStr); + + if(shippingTax.compareTo(BigDecimal.ZERO) > 0 ){ + detailSb.append("
总邮费税费:"+ shippingTax +"
"); + } + + if(tax.compareTo(BigDecimal.ZERO) > 0 ){ + detailSb.append("
总商品税费:"+ tax +"
"); + } + + detailSb.append("
应收金额:"+order.getAmount()+" USDT
"); + if(StringUtils.isNotBlank(order.getChain())){ + if("TRX".equals(order.getChain().toUpperCase()) || "TRON".equals(order.getChain().toUpperCase())){ + order.setChain("TRC20"); + } + } + + detailSb.append("
实收金额:" +order.getConfirmAmount()+" "+order.getConfirmCurrency()+"("+order.getChain()+")"+"
"); + detailSb.append(""); + detailDto.setDetails(detailSb.toString()); + } + + return AjaxResult.success(detailDto); + } + else if(OrderFrom.TICKET.getCode() == order.getOrderFrom()){ + detailDto.setIfSend(order.getIfSend()); + detailDto.setOrderFrom(OrderFrom.TICKET.getCode()); + //从工单系统拿 payment_info表拿详情details 联repair_logistical_info拿phone + TicketOrderPhoneAndDetailsDto ticketDto = orderMapper.getOrderBaseInfoFromTicket(order.getOrderId()); + if(StringUtils.isNull(ticketDto)){ + return AjaxResult.error("服务器请求失败,请稍后再试"); + } + + detailDto.setUserLogistics(ticketDto.getAddress()); + detailDto.setPhone(ticketDto.getPhone()); + + + if(StringUtils.isBlank(ticketDto.getDetails())){ + return AjaxResult.error("服务器请求失败,未能获取到报价详情,请稍后再试"); + } + + //details map转对应格式 + String deatils = deatilsToTable(ticketDto.getDetails(), order); + detailDto.setDetails(deatils); + + return AjaxResult.success(detailDto); + }else { + return AjaxResult.error("异常数据,请检查此数据"); + } + + + } + + @Override + @Transactional(rollbackFor = Exception.class) + public AjaxResult logisticsConfirm(OrderLogisticsConfirmVo vo) { + //校验参数是否为空 + if(StringUtils.isNull(vo)){ + return AjaxResult.error("参数缺失"); + } + if(StringUtils.isAnyBlank(vo.getKey(),vo.getId(),vo.getNo())){ + return AjaxResult.error("参数缺失"); + } + + //根据orderkey获取订单 为空返回orderkey查不到订单错误 + OrderInfoDto dto = orderMapper.selectOrderAndWpStatusInfoByOrderKey(vo.getKey()); + if(StringUtils.isNull(dto)){ + return AjaxResult.error("参数错误:根据所传参数为查询到订单"); + } + + //正常状态需要调用这个接口的状态应该是process + if(!WCOrderStatus.PROCESSING.getEnStatus().equals(dto.getWpStatus())){ + return AjaxResult.error("违规操作: "+vo.getKey()+" 对应的订单状态不能修改!"); + } + + if(dto.getStatus() != 1){ + return AjaxResult.error("违规操作: "+vo.getKey()+" 对应的订单在未支付状态!"); + } + + //1先把发货物流信息入库 字段key对应表中orderKey 主键、id对应company 物流公司、no对应tracking_Number 物流单号 失败就回滚事务 + if(!orderMapper.insertOrderLogistical(vo.getKey(), vo.getId(), vo.getNo())){ + TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(); + } + + //2、 修改网关order表 if_send改为1 表示修改为已发送 失败就回滚事务 + if(!orderMapper.confirmSendProductByOrderKey(vo.getKey(), 1)){ + TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(); + } + + //2、 修改wordpress order表修改订单状态为完成 失败就回滚事务 + if(!orderMapper.completedWPOrder(dto.getOrderId())){ + TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(); + } + + CompletableFuture.runAsync(()->{ + + mailService.sendShippingStartHTMLMailMessage(dto,vo.getId(),vo.getNo()); + + }); + + return AjaxResult.success("订单 "+dto.getOrderId() +" 已发货"); + } + public BigDecimal getAmountUnit(OrderHandleVo vo){ + + BigDecimal unit = BigDecimal.valueOf(Math.pow(10,6)); + if("ETH".equals(vo.getChain())){ + //只支持usdt + unit = BigDecimal.valueOf(Math.pow(10,6)); + } + + if("TRX".equals(vo.getChain())){ + //只支持USDT + unit = BigDecimal.valueOf(Math.pow(10,6)); + } + + //if("BSC".equals(vo.getChain())){ + // unit = BigDecimal.valueOf(Math.pow(10,18)); + //} + + return unit; + } + + @Transactional(rollbackFor = Exception.class) + public OrderKeyAddress getAddress(OrderKeyAddress dto,String orderKey, String chain){ + if(StringUtils.isNull(dto)){ + return null; + } + dto.setOrderKey(orderKey); + if(StringUtils.isNotBlank(chain)){ + chain = chain.toUpperCase(); + }else{ + return null; + } + + String[] ethChain ={"ETH","POLYGON","OPTIMISM","ARBITRUM"}; + boolean isETH = Arrays.asList(ethChain).contains(chain); + if(isETH){ + chain ="ETH"; + }else if("TRX".equals(chain) || "TRON".equals(chain)){ + chain = "TRX"; + }else { + return null; + } + //获取地址 + String address = orderMapper.getAddressNoUse(chain); + if(StringUtils.isBlank(address)){ + return null; + } + if(orderMapper.updateAddressToUse(address,chain,orderKey)){ + dto.setOrderKey(orderKey); + if(isETH){ + dto.setEthAddress(address); + }else{ + dto.setTrxAddress(address); + } + orderMapper.insertAddressAndUserId(dto); + int count = orderMapper.getAddressNoUsedCount(chain); + if(count < 20){ + //邮箱告警 + //CompletableFuture.runAsync(()->{mailService.sendTextMailMessage("support@thewindminer.com","可用地址不足","可用地址数"+count+",请及时补充");}); + //调用脚本生成地址 + } + return dto; + }else { + return null; + } + } + + public String createQRCode(String chain,OrderKeyAddress dto){ + //获取二维码的字符 Base64字符串 + if(StringUtils.isBlank(chain)){ + System.out.println("chain为空"); + return null; + } + if (StringUtils.isNull(dto)){ + System.out.println("dto为空"); + return null; + } + String address = chain.equals("ETH") ? dto.getEthAddress() : dto.getTrxAddress(); + //获取二维码图片 + System.out.println(address); + + String qrRath = "/home/ubuntu/wallet/"+chain+"-"+address+".png"; + System.out.println("路径|"+qrRath); + File file = new File(qrRath); + if (!file.exists()) { + System.out.println("文件不存在"); + //throw new RuntimeException("文件不存在"); + return null; + } + ByteArrayOutputStream baos = null; + try { + System.out.println(file.getName()); + BufferedImage read = ImageIO.read(file); + baos = new ByteArrayOutputStream(); + ImageIO.write(read,"png",baos); + byte[] bytes = baos.toByteArray(); + return Base64.encode(bytes); + } catch (Exception e) { + System.out.println("异常"); + e.printStackTrace(); + }finally { + try { + if (baos != null) { + baos.close(); + } + } catch (IOException e) { + e.printStackTrace(); + } + } + return null; + } + + /** + * 根据过期时间和当前时间 将statu由into转为中文 + * @param status + * @param expireTime + * @param date + * @return + */ + public String getStatus(int status,Date expireTime,Date date) { + String result = ""; + if(date.before(expireTime)){ + if(status == 0){ + result = "超时未支付"; + }else if(status == 1){ + result = "已支付"; + } + }else { + if(status == 0){ + result = "未支付"; + }else if(status == 1){ + result = "已支付"; + } + } + + + return result; + } + + public String getQRCode(String chain, OrderKeyAddress dto){ + //获取二维码的字符 Base64字符串 + if(StringUtils.isBlank(chain)){ + System.out.println("chain为空"); + return null; + } + if (StringUtils.isNull(dto)){ + System.out.println("dto为空"); + return null; + } + chain =chain.toUpperCase(); + String firstStr = null; + if("TRON".equals(chain) || "TRX".equals(chain)){ + chain = "TRON"; + firstStr ="T"; + } + String[] ethChain ={"ETH","POLYGON","OPTIMISM","ARBITRUM"}; + if(Arrays.asList(ethChain).contains(chain.toUpperCase())){ + firstStr ="0x"; + } + String address = "TRON".equals(chain) ? dto.getTrxAddress() : dto.getEthAddress(); + //获取二维码图片 + System.out.println(address); + + if(StringUtils.isBlank(address)){ + return null; + } + + if(!address.startsWith(firstStr)){ + //格式不准确 + return null; + } + + String qrRath = "prd".equals(ENV)? "/home/ubuntu/script/wallet/public/QRcode/"+chain+"/"+address+".png" : "D:/TheWindMiner/QRcode\\"+chain+"\\"+address+".png"; + System.out.println("路径|"+qrRath); + File file = new File(qrRath); + if (!file.exists()) { + System.out.println("文件不存在"); + //调用脚本生产二维码 之后再次判断文件是否存在 + String newQrCode = createQRCode(chain, address); + if(StringUtils.isBlank(newQrCode)){ + return null; + } + //再次判断文件是否存在 + if (!file.exists()){ + return null; + } + //throw new RuntimeException("文件不存在"); + } + ByteArrayOutputStream baos = null; + try { + System.out.println(file.getName()); + BufferedImage read = ImageIO.read(file); + baos = new ByteArrayOutputStream(); + ImageIO.write(read,"png",baos); + byte[] bytes = baos.toByteArray(); + return Base64.encode(bytes); + } catch (Exception e) { + System.out.println("异常"); + e.printStackTrace(); + }finally { + try { + if (baos != null) { + baos.close(); + } + } catch (IOException e) { + e.printStackTrace(); + } + } + return null; + } + + public String createQRCode(String chain, String address){ + //获取二维码的字符 Base64字符串 + if(StringUtils.isAnyBlank(chain,address)){ + System.out.println("chain为空"); + return null; + } + + chain =chain.toUpperCase(); + if("TRX".equals(chain)){ + chain = "TRON"; + } + + //TRON 以T开头 + if("TRON".equals(chain) && !address.startsWith("T")){ + return null; + } + + //获取二维码图片 + System.out.println(address); + + String path = "/home/ubuntu/script/wallet/dg.js"; + String qrRath = "/home/ubuntu/script/wallet/public/QRcode/"+chain+"/"+address+".png"; + + BufferedReader reader = null; + BufferedReader errorReader = null; + try { + ProcessBuilder processBuilder = new ProcessBuilder("node",path,chain,address); + + Process process = processBuilder.start(); + + //获取进程输出流输入参数 + //BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(process.getOutputStream())); + //writer.write("ETH 10"); + // + //writer.newLine(); + //writer.flush(); + + //获取进程输入流读取返回值 + reader = new BufferedReader(new InputStreamReader(process.getInputStream())); + + String line; + + StringBuilder output = new StringBuilder(); + + while ((line = reader.readLine()) != null){ + output.append(line).append("\n"); + } + + System.out.println("输出内容:|"+output.toString()+"|"); + + //获取进程错误流流读取返回值 + errorReader = new BufferedReader(new InputStreamReader(process.getErrorStream())); + + String errorLine; + + StringBuilder errorOutput = new StringBuilder(); + + while ((errorLine = errorReader.readLine()) != null){ + errorOutput.append(errorLine).append("\n"); + } + + reader.close(); + errorReader.close(); + + System.out.println("错误信息:|"+errorOutput.toString()+"|"); + if(StringUtils.isNotBlank(output) && StringUtils.isBlank(errorOutput)){ + return qrRath; + } + return null; + } catch (Exception e) { + System.out.println("异常"); + e.printStackTrace(); + return null; + }finally { + try { + if (reader != null) { + reader.close(); + } + if (errorReader != null) { + errorReader.close(); + } + } catch (IOException e) { + e.printStackTrace(); + } + } + } + + public String deatilsToTable(String details,OrderAndOrderLogisticalDto order){ + try { + Map> map = JSON.parseObject(details, new TypeReference>>(){}); + Map> ml = new HashMap<>(); + for (String machine: map.keySet()) { + LinkedHashMap dto = map.get(machine); + List list = new ArrayList<>(); + for (String reason : dto.keySet() ){ + QuotedDetailsVo qtv = new QuotedDetailsVo(); + qtv.setReason(reason); + qtv.setPrice(dto.get(reason)); + list.add(qtv); + } + ml.put(machine,list); + } + + if(ml.size() < 1){ + return details; + } + + //根据map 渲染表格 + StringBuilder detailSb = new StringBuilder(); + detailSb.append("
"); + detailSb.append(""); + //报价详情表头 + detailSb.append(""); + detailSb.append(""); + detailSb.append(""); + detailSb.append(""); + detailSb.append(""); + System.out.println("d1:"+detailSb.toString()); + final BigDecimal[] totalPrice = {BigDecimal.ZERO}; + for (String machine :ml.keySet()) { + List list = ml.get(machine); + for (int i=0 ; i "); + if(i == 0){ + detailSb.append(""); + } + QuotedDetailsVo dto = list.get(i); + detailSb.append(""); + detailSb.append(""); + detailSb.append(""); + totalPrice[0] = totalPrice[0].add(Convert.toBigDecimal(dto.getPrice(),BigDecimal.ZERO)); + System.out.println(totalPrice[0]); + + } + } + detailSb.append("
机器SN码维修项价格$
"+machine+""+dto.getReason()+""+dto.getPrice()+"

订单总应收款:"+order.getAmount()+"

"); + detailSb.append("
订单实收金额:"+order.getConfirmAmount()+" "+order.getChain().toUpperCase()+"("+order.getConfirmCurrency().toUpperCase()+")"+"
"); + detailSb.append(""); + return detailSb.toString(); + }catch (Exception e){ + return details; + } + } +} diff --git a/src/main/java/com/jxy/windminer/service/impl/SysFileServiceImpl.java b/src/main/java/com/jxy/windminer/service/impl/SysFileServiceImpl.java new file mode 100644 index 0000000..c89df92 --- /dev/null +++ b/src/main/java/com/jxy/windminer/service/impl/SysFileServiceImpl.java @@ -0,0 +1,319 @@ +package com.jxy.windminer.service.impl; + +import com.jxy.windminer.common.Result.R; +import com.jxy.windminer.common.exception.ServiceException; +import com.jxy.windminer.common.security.utils.SecurityUtils; +import com.jxy.windminer.common.text.Convert; +import com.jxy.windminer.common.utils.DateUtils; +import com.jxy.windminer.common.utils.FileUploadUtils; +import com.jxy.windminer.common.utils.StringUtils; +import com.jxy.windminer.common.utils.bean.BeanValidators; +import com.jxy.windminer.common.utils.file.FileUtils; +import com.jxy.windminer.common.web.Result.AjaxResult; +import com.jxy.windminer.dto.RepairRecordDto; +import com.jxy.windminer.dto.SysFileDto; +import com.jxy.windminer.dto.SysToolsDto; +import com.jxy.windminer.entity.RepairRecord; +import com.jxy.windminer.entity.SysFile; +import com.jxy.windminer.entity.ToolsFile; +import com.jxy.windminer.mapper.SysFileMapper; +import com.jxy.windminer.service.FileService; +import com.jxy.windminer.service.SysFileService; +import com.jxy.windminer.vo.AddRepairRecordVo; +import com.jxy.windminer.vo.RepairRecordVo; +import com.jxy.windminer.vo.SysToolsVo; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.BeanUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.web.multipart.MultipartFile; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.validation.Validator; + +import java.io.*; +import java.sql.SQLException; +import java.text.DecimalFormat; +import java.util.Date; +import java.util.List; + + +/** + * @Description 用户业务层 + * @Date 2022/6/14 17:37 + * @Author 杜懿 + */ +@Service +public class SysFileServiceImpl implements SysFileService { + + private static final Logger log = LoggerFactory.getLogger(SysFileServiceImpl.class); + + @Autowired + private FileService fileService; + + @Autowired + protected Validator validator; + + @Autowired + private SysFileMapper fileMapper; + + public static final String FILE_PATH = "/home/windminer/ticket"; + //public static final String FILE_PATH = "D:/marketall/uploadPath"; + + + @Override + public String importRepairRecord(List list, Boolean isUpdateSupport, String operName) { + if (StringUtils.isNull(list) || list.size() == 0) + { + throw new ServiceException("导入维修记录数据不能为空!"); + } + System.out.println("导入数据总条数"+list.size()); + int successNum = 0; + int repeatNum = 0; + int failureNum = 0; + StringBuilder successMsg = new StringBuilder(); + StringBuilder repeatMsg = new StringBuilder(); + StringBuilder failureMsg = new StringBuilder(); + for (RepairRecord record : list) + { + record.setOperName(SecurityUtils.getUsername()); + try + { + + if(StringUtils.isAnyBlank(record.getFault(),record.getHashBoardSerial(),record.getMachineSerial(),record.getRepairMethod(),record.getRepairResult())){ + throw new ServiceException("导入维修记录数据不能为空!"); + } + // 验证是否存在一模一样的记录 todo 优化判断 + if (!fileMapper.checkRecordIfExist(record)) + { + BeanValidators.validateWithException(validator, record); + fileMapper.insertRecord(record); + successNum++; + //successMsg.append("
" + successNum + "、机器序列号 " + record.getMachineSerial() + ",算力板序列号" + record.getHashBoardSerial() +" 的维修记录导入成功"); + } + //else if (isUpdateSupport) + //{ + // BeanValidators.validateWithException(validator, record); + // user.setUserId(u.getUserId()); + // user.setUpdateBy(operName); + // fileMapper.updateUser(record); + // successNum++; + // successMsg.append("
" + successNum + "、账号 " + record.getUserName() + " 更新成功"); + //} + else + { + repeatNum++; + repeatMsg.append("
" + successNum + "、机器序列号 " + record.getMachineSerial() + ",算力板序列号" + record.getHashBoardSerial() +" 的维修记录存在重复数据"); + } + } + catch (Exception e) + { + failureNum++; + String msg = "
" + failureNum + "、机器序列号 " + record.getMachineSerial() + ",算力板序列号" + record.getHashBoardSerial() + " 的维修记录导入失败:"; + failureMsg.append(msg); + log.error(msg, e); + } + } + if (failureNum > 0) + { + failureMsg.insert(0, "很抱歉,导入失败!共 " + failureNum + " 条数据格式不正确,错误如下:"); + throw new ServiceException(failureMsg.toString()); + } + else + { + + if (repeatNum > 0) + { + successMsg.insert(0, "有重复数据存在,去重导入成功!共导入 " + successNum + " 条不重复数据。"); + repeatMsg.insert(0, "
共 " + repeatNum + " 条数据重复,导入数据库,重复数据如下:"); + successMsg.append(repeatMsg); + }else { + //successMsg.insert(0, "恭喜您,数据已全部导入成功!共 " + successNum + " 条,数据如下:"); + successMsg.insert(0, "恭喜您,数据已全部导入成功!共 " + successNum + " 条数据"); + } + } + return successMsg.toString(); + } + + @Override + public List getRepairRecord(RepairRecordVo vo) { + List list = fileMapper.selectRecordByCond(vo); + return list; + } + + @Override + public AjaxResult uploadFile(MultipartFile file, String ver, String md5sum) throws SQLException { + SysFileDto dto = new SysFileDto(); + //第一步 根据文件名和ver判断数据库有没有该数据 + String fileName = file.getOriginalFilename(); + //文件类型 如txt pdf + String extension = FileUploadUtils.getExtension(file); + if(file.getOriginalFilename().contains("_V")){ + //文件名带版本号 采用通杀原则 包含_V的全Kill + fileName = fileName.substring(0,fileName.indexOf("_V")); + fileName = fileName + "_"+ ver +"."+extension; + }else { + fileName = fileName.replace("."+extension,"_"+ver+"."+extension); + } + System.out.println("fileName |"+fileName); + + if(fileMapper.checkToolsIfExist(fileName,ver)){ + return AjaxResult.error("数据库已有"+ver+"版本的 "+file.getOriginalFilename()+" 文件,请检查版本或放弃上传"); + } + System.out.println("验重通过"); + + // 上传并返回访问地址 + R upload = fileService.uploadToolsFile(file, fileName); + + System.out.println("文件已上传"+upload); + if (StringUtils.isNull(upload) || StringUtils.isNull(upload.getData())) { + System.out.println("gg"); + System.out.println(upload.getMsg()); + return AjaxResult.error("文件服务异常,文件上传失败"); + } + + SysFile sysFile = upload.getData(); + ToolsFile toolsFile = new ToolsFile(); + BeanUtils.copyProperties(sysFile, toolsFile); + toolsFile.setUserName(SecurityUtils.getUsername()); + toolsFile.setFileName(StringUtils.clean(sysFile.getName())); + toolsFile.setSize(getFilseSize(file.getSize())); + toolsFile.setMd5sum(md5sum); + System.out.println("入库数据:"+toolsFile); + //将文件信息存入数据库:(url、name、userName、createTime、version 新增size和md5sum) + if (!fileMapper.insertToolsFileInfo(toolsFile,ver)) { + throw new SQLException("后台异常.."); + } + + dto.setId(Convert.toStr(toolsFile.getId())); + dto.setUrl(toolsFile.getUrl()); + dto.setName(toolsFile.getFileName()); + + return AjaxResult.success(dto); + } + + @Override + public void downloadById(int id, HttpServletRequest request, HttpServletResponse response) throws Exception { + + System.out.println(id); + ToolsFile fileInfo = fileMapper.getToolsFileInfoById(id); + + if(StringUtils.isNull(fileInfo)){ + throw new Exception("对应文件不存在,请检查参数"); + } + + System.out.println("fileInfo" + fileInfo); + + //本地文件路径(绝对路径,包含后缀名) + String path = FILE_PATH + StringUtils.substringAfter(fileInfo.getUrl(), "statics"); + System.out.println("path" + path); + + String downloadName = StringUtils.substringAfterLast(path, "/"); + + File file = new File(path); + if (!file.exists()) { + throw new RuntimeException("文件不存在"); + } + //设置响应头信息 + response.reset(); + response.setCharacterEncoding("utf-8"); + response.setContentType("multipart/form-data"); + + response.setHeader("Content-Disposition", + "attachment;fileName=" + FileUtils.setFileDownloadHeader(request, downloadName)); + + response.addHeader("Access-Contro1-Allow-Origin", "*"); + FileUtils.writeBytes(path, response.getOutputStream()); + + } + + @Override + public List getToolsList(SysToolsVo vo) { + + List list = fileMapper.selectToolsListByCond(vo); + return list; + } + + @Override + public AjaxResult addReparRecord(AddRepairRecordVo vo) { + + Date date = DateUtils.parseDate(vo.getRepairDate()); + if(StringUtils.isNull(date)){ + return AjaxResult.error("日期传参格式错误 应传格式为yyyy-MM-dd 或者yyyy-MM-dd HH:mm:ss"); + } + + RepairRecord record = new RepairRecord(); + BeanUtils.copyProperties(vo,record); + record.setOperName(SecurityUtils.getUsername()); + record.setRepairDate(date); + + //检查是否已有数据 + if(fileMapper.checkRecordIfExist(record)){ + return AjaxResult.error("数据重复提交!"); + } + if(fileMapper.insertRecord(record)){ + return AjaxResult.success("维修记录提交成功"); + }else { + return AjaxResult.error("服务器繁忙"); + } + } + + @Override + public AjaxResult deleteToolById(int id) { + ToolsFile fileInfo = fileMapper.getToolsFileInfoById(id); + if(StringUtils.isNull(fileInfo)){ + return AjaxResult.error("文件id传参有误"); + } + + try { + String path = FILE_PATH + StringUtils.substringAfter(fileInfo.getUrl(), "statics"); + System.out.println("path" + path); + + File file = new File(path); + if (!file.exists()) { + throw new RuntimeException("文件不存在"); + } + + file.delete(); + }catch (RuntimeException e){ + return AjaxResult.error("文件不存在"); + }catch (Exception e){ + return AjaxResult.error("文件删除异常,请稍后再试或联系系统管理员"); + } + + if(fileMapper.deleteToolFileById(id)){ + return AjaxResult.success("文件删除成功"); + }else { + return AjaxResult.error("服务器繁忙"); + } + } + + public String getFilseSize(long size) { + int GB = 1024 * 1024 * 1024;//定义GB的计算常量 + int MB = 1024 * 1024;//定义MB的计算常量 + int KB = 1024;//定义KB的计算常量 + try { + // 格式化小数 + DecimalFormat df = new DecimalFormat("0.00"); + String resultSize = ""; + if (size / GB >= 1) { + //如果当前Byte的值大于等于1GB + resultSize = df.format(size / (float) GB) + " GB"; + } else if (size / MB >= 1) { + //如果当前Byte的值大于等于1MB + resultSize = df.format(size / (float) MB) + " MB"; + } else if (size / KB >= 1) { + //如果当前Byte的值大于等于1KB + resultSize = df.format(size / (float) KB) + " KB"; + } else { + resultSize = size + " B"; + } + return resultSize; + } catch (Exception e) { + return null; + } + } + +} diff --git a/src/main/java/com/jxy/windminer/service/impl/SysLogininforServiceImpl.java b/src/main/java/com/jxy/windminer/service/impl/SysLogininforServiceImpl.java new file mode 100644 index 0000000..c08132f --- /dev/null +++ b/src/main/java/com/jxy/windminer/service/impl/SysLogininforServiceImpl.java @@ -0,0 +1,66 @@ +package com.jxy.windminer.service.impl; + +import com.jxy.windminer.entity.system.SysLogininfor; +import com.jxy.windminer.mapper.SysLogininforMapper; +import com.jxy.windminer.service.SysLogininforService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.List; + +/** + * 系统访问日志情况信息 服务层处理 + * + * @author 杜懿 + */ +@Service +public class SysLogininforServiceImpl implements SysLogininforService +{ + + @Autowired + private SysLogininforMapper logininforMapper; + + /** + * 新增系统登录日志 + * + * @param logininfor 访问日志对象 + */ + @Override + public int insertLogininfor(SysLogininfor logininfor) + { + return logininforMapper.insertLogininfor(logininfor); + } + + /** + * 查询系统登录日志集合 + * + * @param logininfor 访问日志对象 + * @return 登录记录集合 + */ + @Override + public List selectLogininforList(SysLogininfor logininfor) + { + return logininforMapper.selectLogininforList(logininfor); + } + + /** + * 批量删除系统登录日志 + * + * @param infoIds 需要删除的登录日志ID + * @return 结果 + */ + @Override + public int deleteLogininforByIds(Long[] infoIds) + { + return logininforMapper.deleteLogininforByIds(infoIds); + } + + /** + * 清空系统登录日志 + */ + @Override + public void cleanLogininfor() + { + logininforMapper.cleanLogininfor(); + } +} diff --git a/src/main/java/com/jxy/windminer/service/impl/SysMenuServiceImpl.java b/src/main/java/com/jxy/windminer/service/impl/SysMenuServiceImpl.java new file mode 100644 index 0000000..e27ed6a --- /dev/null +++ b/src/main/java/com/jxy/windminer/service/impl/SysMenuServiceImpl.java @@ -0,0 +1,39 @@ +package com.jxy.windminer.service.impl; + +import com.jxy.windminer.common.utils.StringUtils; +import com.jxy.windminer.mapper.SysMenuMapper; +import com.jxy.windminer.service.SysMenuService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +/** + * @Description 用户业务层 + * @Date 2022/6/14 17:37 + * @Author 杜懿 + */ +@Service +public class SysMenuServiceImpl implements SysMenuService { + + @Autowired + private SysMenuMapper menuMapper; + + + @Override + public Set selectMenuPermsByUserId(Long userId) { + List perms = menuMapper.selectMenuPermsByUserId(userId); + Set permsSet = new HashSet<>(); + for (String perm : perms) + { + if (StringUtils.isNotEmpty(perm)) + { + permsSet.addAll(Arrays.asList(perm.trim().split(","))); + } + } + return permsSet; + } +} diff --git a/src/main/java/com/jxy/windminer/service/impl/SysOperLogServiceImpl.java b/src/main/java/com/jxy/windminer/service/impl/SysOperLogServiceImpl.java new file mode 100644 index 0000000..4d252fa --- /dev/null +++ b/src/main/java/com/jxy/windminer/service/impl/SysOperLogServiceImpl.java @@ -0,0 +1,78 @@ +package com.jxy.windminer.service.impl; + +import com.jxy.windminer.entity.system.SysOperLog; +import com.jxy.windminer.mapper.SysOperLogMapper; +import com.jxy.windminer.service.SysOperLogService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.List; + +/** + * 操作日志 服务层处理 + * + * @author jxy + */ +@Service +public class SysOperLogServiceImpl implements SysOperLogService +{ + @Autowired + private SysOperLogMapper operLogMapper; + + /** + * 新增操作日志 + * + * @param operLog 操作日志对象 + * @return 结果 + */ + @Override + public int insertOperlog(SysOperLog operLog) + { + return operLogMapper.insertOperlog(operLog); + } + + /** + * 查询系统操作日志集合 + * + * @param operLog 操作日志对象 + * @return 操作日志集合 + */ + @Override + public List selectOperLogList(SysOperLog operLog) + { + return operLogMapper.selectOperLogList(operLog); + } + + /** + * 批量删除系统操作日志 + * + * @param operIds 需要删除的操作日志ID + * @return 结果 + */ + @Override + public int deleteOperLogByIds(Long[] operIds) + { + return operLogMapper.deleteOperLogByIds(operIds); + } + + /** + * 查询操作日志详细 + * + * @param operId 操作ID + * @return 操作日志对象 + */ + @Override + public SysOperLog selectOperLogById(Long operId) + { + return operLogMapper.selectOperLogById(operId); + } + + /** + * 清空操作日志 + */ + @Override + public void cleanOperLog() + { + operLogMapper.cleanOperLog(); + } +} diff --git a/src/main/java/com/jxy/windminer/service/impl/SysPermissionServiceImpl.java b/src/main/java/com/jxy/windminer/service/impl/SysPermissionServiceImpl.java new file mode 100644 index 0000000..df954a3 --- /dev/null +++ b/src/main/java/com/jxy/windminer/service/impl/SysPermissionServiceImpl.java @@ -0,0 +1,58 @@ +package com.jxy.windminer.service.impl; + +import com.jxy.windminer.entity.system.SysUser; +import com.jxy.windminer.service.SysMenuService; +import com.jxy.windminer.service.SysPermissionService; +import com.jxy.windminer.service.SysRoleService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.HashSet; +import java.util.Set; + +/** + * @Description 权限实现类 + * @Date 2022/6/16 11:37 + * @Author 杜懿 + */ +@Service +public class SysPermissionServiceImpl implements SysPermissionService { + + @Autowired + private SysRoleService roleService; + + @Autowired + private SysMenuService menuService; + + @Override + public Set getRolePermission(Long userId) { + + Set roles = new HashSet(); + // 管理员拥有所有权限 + if (SysUser.isAdmin(userId)) + { + roles.add("admin"); + } + else + { + roles.addAll(roleService.selectRolePermissionByUserId(userId)); + } + return roles; + } + + @Override + public Set getMenuPermission(Long userId) { + + Set perms = new HashSet(); + // 管理员拥有所有权限 + if (SysUser.isAdmin(userId)) + { + perms.add("*:*:*"); + } + else + { + perms.addAll(menuService.selectMenuPermsByUserId(userId)); + } + return perms; + } +} diff --git a/src/main/java/com/jxy/windminer/service/impl/SysRoleServiceImpl.java b/src/main/java/com/jxy/windminer/service/impl/SysRoleServiceImpl.java new file mode 100644 index 0000000..7656628 --- /dev/null +++ b/src/main/java/com/jxy/windminer/service/impl/SysRoleServiceImpl.java @@ -0,0 +1,113 @@ +package com.jxy.windminer.service.impl; + +import com.jxy.windminer.common.datascope.annotation.DataScope; +import com.jxy.windminer.common.utils.SpringUtils; +import com.jxy.windminer.common.utils.StringUtils; +import com.jxy.windminer.entity.system.SysRole; +import com.jxy.windminer.mapper.SysRoleMapper; +import com.jxy.windminer.service.SysRoleService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +/** + * @Description 用户业务层 + * @Date 2022/6/14 17:37 + * @Author 杜懿 + */ +@Service +public class SysRoleServiceImpl implements SysRoleService { + + private static final Logger log = LoggerFactory.getLogger(SysRoleServiceImpl.class); + + @Autowired + private SysRoleMapper roleMapper; + + /** + * 根据条件分页查询角色数据 + * + * @param role 角色信息 + * @return 角色数据集合信息 + */ + @Override + @DataScope(deptAlias = "d") + public List selectRoleList(SysRole role) + { + return roleMapper.selectRoleList(role); + } + + /** + * 根据用户ID查询权限 + * + * @param userId 用户ID + * @return 权限列表 + */ + @Override + public Set selectRolePermissionByUserId(Long userId) + { + List perms = roleMapper.selectRolePermissionByUserId(userId); + Set permsSet = new HashSet<>(); + for (SysRole perm : perms) + { + if (StringUtils.isNotNull(perm)) + { + permsSet.addAll(Arrays.asList(perm.getRoleKey().trim().split(","))); + } + } + return permsSet; + } + + /** + * 根据用户ID查询角色 + * + * @param userId 用户ID + * @return 角色列表 + */ + @Override + public List selectRolesByUserId(Long userId) + { + List userRoles = roleMapper.selectRolePermissionByUserId(userId); + List roles = selectRoleAll(); + for (SysRole role : roles) + { + for (SysRole userRole : userRoles) + { + if (role.getRoleId().longValue() == userRole.getRoleId().longValue()) + { + role.setFlag(true); + break; + } + } + } + return roles; + } + + /** + * 根据用户ID获取用户角色id列表 + * + * @param userId 用户ID + * @return 选中角色ID列表 + */ + @Override + public List selectRoleListByUserId(Long userId) { + return roleMapper.selectRoleListByUserId(userId); + } + + /** + * 查询所有角色 + * + * @return 角色列表 + */ + @Override + public List selectRoleAll() + { + return SpringUtils.getAopProxy(this).selectRoleList(new SysRole()); + } + +} diff --git a/src/main/java/com/jxy/windminer/service/impl/SysUserServiceImpl.java b/src/main/java/com/jxy/windminer/service/impl/SysUserServiceImpl.java new file mode 100644 index 0000000..e4d9421 --- /dev/null +++ b/src/main/java/com/jxy/windminer/service/impl/SysUserServiceImpl.java @@ -0,0 +1,398 @@ +package com.jxy.windminer.service.impl; + +import com.jxy.windminer.common.Result.R; +import com.jxy.windminer.common.constant.UserConstants; +import com.jxy.windminer.common.datascope.annotation.DataScope; +import com.jxy.windminer.common.exception.ServiceException; +import com.jxy.windminer.common.security.utils.SecurityUtils; +import com.jxy.windminer.common.utils.SpringUtils; +import com.jxy.windminer.common.utils.StringUtils; +import com.jxy.windminer.entity.auth.LoginUser; +import com.jxy.windminer.entity.system.SysRole; +import com.jxy.windminer.entity.system.SysUser; +import com.jxy.windminer.entity.system.SysUserRole; +import com.jxy.windminer.mapper.SysRoleMapper; +import com.jxy.windminer.mapper.SysUserMapper; +import com.jxy.windminer.mapper.SysUserRoleMapper; +import com.jxy.windminer.service.SysRoleService; +import com.jxy.windminer.service.SysUserService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.util.CollectionUtils; + +import java.util.ArrayList; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + + +/** + * @Description 用户业务层 + * @Date 2022/6/14 17:37 + * @Author 杜懿 + */ +@Service +public class SysUserServiceImpl implements SysUserService { + + + private static final Logger log = LoggerFactory.getLogger(SysPermissionServiceImpl.class); + + @Autowired + private SysUserMapper userMapper; + + @Autowired + private SysUserRoleMapper userRoleMapper; + + @Autowired + private SysRoleMapper roleMapper; + + @Autowired + private SysRoleService roleService; + + @Autowired + private SysPermissionServiceImpl permissionService; + + + + /** + * 根据条件分页查询用户列表 + * + * @param user 用户信息 + * @return 用户信息集合信息 + */ + @Override + @DataScope(deptAlias = "d", userAlias = "u") + public List selectUserList(SysUser user) + { + return userMapper.selectUserList(user); + } + + @Override + public R selectUser(String account) { + String em = "^[a-zA-Z0-9][\\w\\.-]*[a-zA-Z0-9]@[a-zA-Z0-9][\\w\\.-]*[a-zA-Z0-9]\\.[a-zA-Z][a-zA-Z\\.]*[a-zA-Z]$"; + + SysUser user = new SysUser(); + if(account.matches(em)){ + user = selectUserByEmail(account); + if (StringUtils.isNull(user)) + { + return R.fail("邮箱未被注册"); + } + }else { + user = selectUserByUserName(account); + if (StringUtils.isNull(user)) + { + return R.fail("用户不存在"); + } + } + + List roleIdList = roleService.selectRoleListByUserId(user.getUserId()); + Long[] roleIds = roleIdList.toArray(new Long[roleIdList.size()]); + user.setRoleIds(roleIds); + + + // 角色集合 + Set roles = permissionService.getRolePermission(user.getUserId()); + // 权限集合 + Set permissions = permissionService.getMenuPermission(user.getUserId()); + LoginUser sysUserVo = new LoginUser(); + sysUserVo.setUserid(user.getUserId()); + sysUserVo.setUsername(user.getNickName()); + sysUserVo.setIpaddr(user.getLoginIp()); + sysUserVo.setSysUser(user); + sysUserVo.setRoles(roles); + sysUserVo.setPermissions(permissions); + return R.success(sysUserVo); + } + + /** + * 通过用户名查询用户 + * + * @param userName 用户名 + * @return 用户对象信息 + */ + @Override + public SysUser selectUserByUserName(String userName) { + return userMapper.selectUserByUserName(userName); + } + + /** + * 通过邮箱查询用户 + * + * @param email 邮箱 + * @return 用户对象信息 + */ + @Override + public SysUser selectUserByEmail(String email) { + return userMapper.selectUserByEmail(email); + } + + /** + * 通过用户ID查询用户 + * + * @param userId 用户ID + * @return 用户对象信息 + */ + @Override + public SysUser selectUserById(Long userId) + { + return userMapper.selectUserById(userId); + } + + /** + * 查询用户所属角色组 + * + * @param userName 用户名 + * @return 结果 + */ + @Override + public String selectUserRoleGroup(String userName) + { + List list = roleMapper.selectRolesByUserName(userName); + if (CollectionUtils.isEmpty(list)) + { + return StringUtils.EMPTY; + } + return list.stream().map(SysRole::getRoleName).collect(Collectors.joining(",")); + } + + /** + * 校验用户名称是否唯一 + * + * @param userName 用户名称 + * @return 结果 + */ + @Override + public String checkUserNameUnique(String userName) { + int count = userMapper.checkUserNameUnique(userName); + if (count > 0) + { + return UserConstants.NOT_UNIQUE; + } + return UserConstants.UNIQUE; + } + + /** + * 校验手机号码是否唯一 + * + * @param user 用户信息 + * @return + */ + @Override + public String checkPhoneUnique(SysUser user) + { + Long userId = StringUtils.isNull(user.getUserId()) ? -1L : user.getUserId(); + SysUser info = userMapper.checkPhoneUnique(user.getPhone()); + if (StringUtils.isNotNull(info) && info.getUserId().longValue() != userId.longValue()) + { + return UserConstants.NOT_UNIQUE; + } + return UserConstants.UNIQUE; + } + + /** + * 校验email是否唯一 + * + * @param user 用户信息 + * @return + */ + @Override + public String checkEmailUnique(SysUser user) + { + Long userId = StringUtils.isNull(user.getUserId()) ? -1L : user.getUserId(); + SysUser info = userMapper.checkEmailUnique(user.getEmail()); + if (StringUtils.isNotNull(info) && info.getUserId().longValue() != userId.longValue()) + { + return UserConstants.NOT_UNIQUE; + } + return UserConstants.UNIQUE; + } + + /** + * 校验email是否存在 + * + * @param email 邮箱 + * @return + */ + @Override + public String checkEmailExist(String email) + { + String dbEmail = userMapper.checkEmailExist(email); + if (StringUtils.isNotNull(dbEmail)) + { + return UserConstants.NOT_UNIQUE; + } + return UserConstants.UNIQUE; + } + + /** + * 校验用户是否允许操作 + * + * @param user 用户信息 + */ + @Override + public void checkUserAllowed(SysUser user) + { + if (StringUtils.isNotNull(user.getUserId()) && user.isAdmin()) + { + throw new ServiceException("不允许操作超级管理员用户"); + } + } + + /** + * 校验用户是否有数据权限 + * + * @param userId 用户id + */ + @Override + public void checkUserDataScope(Long userId) + { + if (!SysUser.isAdmin(SecurityUtils.getUserId())) + { + SysUser user = new SysUser(); + user.setUserId(userId); + List users = SpringUtils.getAopProxy(this).selectUserList(user); + if (StringUtils.isEmpty(users)) + { + throw new ServiceException("没有权限访问用户数据!"); + } + } + } + + + @Override + public boolean registerUser(SysUser user) { + + boolean result = userMapper.insertUser(user) > 0; + if(result){ + //用户添加成功后 默认设置用户为注册用户 + SysUser dbUser = userMapper.selectUserByEmail(user.getEmail()); + user.setUserId(dbUser.getUserId()); + + //查询角色:“注册用户”的id并且赋值到user中 + Long[] roleIds = roleMapper.getResigesterId(); + user.setRoleIds(roleIds); + insertUserRole(user); + + } + + return result; + } + + /** + * 修改保存用户信息 + * + * @param user 用户信息 + * @return 结果 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public int updateUser(SysUser user) + { + Long userId = user.getUserId(); + + // 删除用户与角色关联 + userRoleMapper.deleteUserRoleByUserId(userId); + // 新增用户与角色管理 此处要保证传过来的user中要携带roleIds + insertUserRole(user); + //// 删除用户与岗位关联 + //userPostMapper.deleteUserPostByUserId(userId); + //// 新增用户与岗位管理 + //insertUserPost(user); + return userMapper.updateUser(user); + } + + /** + * 修改用户状态 + * + * @param user 用户信息 + * @return 结果 + */ + @Override + public int updateUserStatus(SysUser user) + { + return userMapper.updateUser(user); + } + + /** + * 修改用户基本信息 + * + * @param user 用户信息 + * @return 结果 + */ + @Override + public int updateUserProfile(SysUser user) + { + return userMapper.updateUser(user); + } + + /** + * 重置用户密码 + * + * @param user 用户信息 + * @return 结果 + */ + @Override + public int resetPwd(SysUser user) + { + return userMapper.updateUser(user); + } + + /** + * 重置用户密码 + * + * @param userName 用户名 + * @param password 密码 + * @return 结果 + */ + @Override + public int resetUserPwd(String userName, String password) + { + return userMapper.resetUserPwd(userName, password); + } + + /** + * 重置用户密码 + * + * @param email 邮箱 + * @param password 密码 + * @return 结果 + */ + @Override + public int resetPwdByEmail(String email, String password) + { + return userMapper.resetPwdByEmail(email, password); + } + + /** + * 新增用户角色信息 + * + * @param user 用户对象 + */ + public void insertUserRole(SysUser user) + { + //需要确保user.getRoleIds在整个传递过程中不丢失 + Long[] roles = user.getRoleIds(); + if (StringUtils.isNotNull(roles)) + { + // 新增用户与角色管理 + List list = new ArrayList(); + for (Long roleId : roles) + { + SysUserRole ur = new SysUserRole(); + ur.setUserId(user.getUserId()); + ur.setRoleId(roleId); + list.add(ur); + } + if (list.size() > 0) + { + userRoleMapper.batchUserRole(list); + } + } + } + + +} diff --git a/src/main/java/com/jxy/windminer/service/impl/TicketServiceImpl.java b/src/main/java/com/jxy/windminer/service/impl/TicketServiceImpl.java new file mode 100644 index 0000000..3cd5e2e --- /dev/null +++ b/src/main/java/com/jxy/windminer/service/impl/TicketServiceImpl.java @@ -0,0 +1,1820 @@ +package com.jxy.windminer.service.impl; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.TypeReference; +import com.jxy.windminer.common.Result.R; +import com.jxy.windminer.common.constant.Constants; +import com.jxy.windminer.common.redis.service.RedisService; +import com.jxy.windminer.common.security.annotation.RequiresLogin; +import com.jxy.windminer.common.security.utils.SecurityUtils; +import com.jxy.windminer.common.text.Convert; +import com.jxy.windminer.common.utils.DateUtils; +import com.jxy.windminer.common.utils.StringUtils; +import com.jxy.windminer.common.utils.file.FileTypeUtils; +import com.jxy.windminer.common.utils.uuid.IdUtils; +import com.jxy.windminer.common.web.Result.AjaxResult; +import com.jxy.windminer.dto.*; +import com.jxy.windminer.entity.JXYFile; +import com.jxy.windminer.entity.SysFile; +import com.jxy.windminer.entity.Ticket; +import com.jxy.windminer.enums.*; +import com.jxy.windminer.mapper.OrderMapper; +import com.jxy.windminer.mapper.TicketMapper; +import com.jxy.windminer.service.FileService; +import com.jxy.windminer.service.MailService; +import com.jxy.windminer.service.TicketService; +import com.jxy.windminer.vo.*; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.BeanUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.transaction.interceptor.TransactionAspectSupport; +import org.springframework.web.multipart.MultipartFile; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.*; +import java.math.BigDecimal; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import java.sql.SQLException; +import java.util.*; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; +import java.util.zip.ZipEntry; +import java.util.zip.ZipOutputStream; + + +/** + * @Description ETF查询业务处理 + * @Date 2022/5/19 17:51 + * @Author 杜懿 + */ +@Service +public class TicketServiceImpl implements TicketService { + + private static final Logger log = LoggerFactory.getLogger(TicketServiceImpl.class); + + @Autowired + private FileService fileService; + + @Autowired + private TicketMapper ticketMapper; + + @Autowired + private OrderMapper orderMapper; + + @Autowired + private RedisService redisService; + + @Autowired + private MailService mailService; + + + public static final String PREX = "statics"; + + public static final String FILE_PATH = "/home/windminer/ticket"; + //public static final String FILE_PATH = "D:/marketall/uploadPath"; + + + @Value("${my.domain}") + private String MY_DOMAIN; + + //@Override + //public AjaxResult getTypeList() { + // + // List list = ticketMapper.getTypeSelect(); + // + // return AjaxResult.success(list); + //} + + + @Transactional(rollbackFor = Exception.class) + @Override + public AjaxResult uploadFile(MultipartFile file) throws SQLException { + R upload = fileService.uploadFile(file); + + if (StringUtils.isNull(upload) || StringUtils.isNull(upload.getData())) { + return AjaxResult.error("文件服务异常,文件上传失败,请联系管理员"); + } + + SysFile sysFile = upload.getData(); + JXYFile jxyFile = new JXYFile(); + BeanUtils.copyProperties(sysFile, jxyFile); + jxyFile.setUserName(SecurityUtils.getUsername()); + jxyFile.setFileName(sysFile.getName()); + + //将文件信息存入数据库:(url、name、userName、createTime) + boolean b = ticketMapper.insertFileInfo(jxyFile); + + sysFile.setId(jxyFile.getId()); + if (!b) { + throw new SQLException("发生异常了.."); + } + + return AjaxResult.success(sysFile); + } + + @Transactional(rollbackFor = Exception.class) + @Override + public AjaxResult uploadFiles(MultipartFile[] file) throws SQLException { + + SysFileDto dto = new SysFileDto(); + + for (MultipartFile f : file) { + R upload = fileService.uploadFile(f); + + if (StringUtils.isNull(upload) || StringUtils.isNull(upload.getData())) { + System.out.println("gg"); + System.out.println(upload.getMsg()); + return AjaxResult.error("文件服务异常,文件上传失败"); + } + + SysFile sysFile = upload.getData(); + JXYFile jxyFile = new JXYFile(); + BeanUtils.copyProperties(sysFile, jxyFile); + jxyFile.setUserName(SecurityUtils.getUsername()); + jxyFile.setFileName(StringUtils.clean(sysFile.getName())); + System.out.println(jxyFile); + String type = FileTypeUtils.getFileType(sysFile.getName()).toUpperCase(); + System.out.println("type:"+type); + // 图片 + //"jpg", "jpeg", "png", + // //音频 + // "mp3","aif", "aiff", "wav", "wma", + // // 视频格式 + // "mp4", "avi", "rmvb" + if("JPG".equals(type) || "JPEG".equals(type) ||"PNG".equals(type)){ + jxyFile.setFileType(0); + }else if("MP3".equals(type) || "AIF".equals(type) ||"AIFF".equals(type) || "WAV".equals(type) || "WMA".equals(type)){ + jxyFile.setFileType(1); + }else if("MP4".equals(type) || "AVI".equals(type) ||"RMVB".equals(type)){ + jxyFile.setFileType(2); + }else { + return AjaxResult.error("文件类型不在可允许范围之内"); + } + + //将文件信息存入数据库:(url、name、userName、createTime) + if (!ticketMapper.insertFileInfo(jxyFile)) { + throw new SQLException("后台异常.."); + } + + if (StringUtils.isNull(dto.getId())) { + dto.setId(Convert.toStr(jxyFile.getId())); + dto.setUrl(jxyFile.getUrl()); + dto.setName(jxyFile.getFileName()); + } else { + dto.setId(dto.getId() + "," + jxyFile.getId()); + dto.setUrl(dto.getUrl() + "," + jxyFile.getUrl()); + dto.setName(dto.getName() + "," + jxyFile.getFileName()); + } + + } + + return AjaxResult.success(dto); + } + + @Override + public void downloadByIds(String ids, HttpServletRequest request, HttpServletResponse response) { + List fileInfos = getFileInfos(ids); + System.out.println("fileInfos" + fileInfos); + + //设置响应头信息 + response.reset(); + response.setCharacterEncoding("utf-8"); + response.setContentType("multipart/form-data"); + //设置压缩包的名字,date为时间戳 + String date = DateUtils.dateTimeNow(); + String downloadName = "file" + date + ".zip"; + + String agent = request.getHeader("USER-AGENT"); + try { + //针对IE或者以IE为内核的浏览器: + if (agent.contains("MSIE") || agent.contains("Trident")) { + downloadName = URLEncoder.encode(downloadName, "UTF-8"); + } else { + //非IE浏览器的处理: + downloadName = new String(downloadName.getBytes(StandardCharsets.UTF_8), StandardCharsets.ISO_8859_1); + } + } catch (Exception e) { + log.error("系统异常", e); + } + response.setHeader("Content-Disposition", "attachment;fileName=" + downloadName); + + + //设置压缩流:直接写入response,实现边压缩边下载 + ZipOutputStream zipOs = null; + //循环将文件写入压缩流 + DataOutputStream os = null; + //文件 + File file; + try { + zipOs = new ZipOutputStream(new BufferedOutputStream(response.getOutputStream())); + //设置压缩方法 + zipOs.setMethod(ZipOutputStream.DEFLATED); + //遍历文件信息(主要获取文件名/文件路径等) + for (JXYFile fileInfo : fileInfos) { + //文件名(包含后缀名,如:测试.pdf) + String name = fileInfo.getFileName(); + //本地文件路径(绝对路径,包含后缀名) + String path = FILE_PATH + StringUtils.substringAfter(fileInfo.getUrl(), "statics"); + System.out.println("path" + path); + file = new File(path); + if (!file.exists()) { + throw new RuntimeException("文件不存在"); + } + //添加ZipEntry,并将ZipEntry写入文件流 + zipOs.putNextEntry(new ZipEntry(name)); + os = new DataOutputStream(zipOs); + FileInputStream fs = new FileInputStream(file); + byte[] b = new byte[100]; + int length; + //读入需要下载的文件的内容,打包到zip文件 + while ((length = fs.read(b)) != -1) { + os.write(b, 0, length); + } + //关闭流 + fs.close(); + zipOs.closeEntry(); + } + } catch (Exception e) { + e.printStackTrace(); + } finally { + //关闭流 + try { + if (os != null) { + os.flush(); + os.close(); + } + if (zipOs != null) { + zipOs.close(); + } + } catch (IOException e) { + e.printStackTrace(); + } + } + + } + + @Transactional(rollbackFor = Exception.class) + @Override + @RequiresLogin + public AjaxResult submitTicket(TicketVo vo) { + + vo.setEmail(SecurityUtils.getEmail()); + + if(StringUtils.isBlank(vo.getModel())){ + return AjaxResult.error("机器型号不能为空"); + } + + if(StringUtils.isBlank(vo.getSerialNo())){ + return AjaxResult.error("机器编码不能为空"); + } + + if(StringUtils.isBlank(vo.getDistributor())){ + return AjaxResult.error("经销商不能为空"); + } + + if(StringUtils.isBlank(vo.getDesc())){ + return AjaxResult.error("故障描述不能为空"); + } + + if(StringUtils.isBlank(vo.getFault())){ + return AjaxResult.error("故障现象不能为空"); + } + + Ticket ticket = new Ticket(); + BeanUtils.copyProperties(vo, ticket); + Date buyDate = DateUtils.parseDate(vo.getBuyDate()); + if(StringUtils.isNull(buyDate)){ + return AjaxResult.error("购买日期参数有误"); + } + //List faults = ticketMapper.getFaults(); + //ticket.setFault(faults.get(Convert.toInt(ticket.getFault())).getFault()); + ticket.setStatus(TicketStatusType.STATUS_ONE.getCode()); + ticket.setUserName(SecurityUtils.getUsername()); + + if (ticketMapper.insertTicket(ticket)) { + //工单提交成功 + System.out.println("工单提交成功:"+ticket); + //处理附件 + String video=""; + String audio=""; + String image=""; + if(StringUtils.isNotBlank(vo.getFiles())){ + List files = getFileInfos(vo.getFiles()); + + for (JXYFile f:files) { + if(f.getFileType() == 0){ + //图片 0 + if(StringUtils.isBlank(image)){ + image = image+f.getUrl(); + }else { + image = image+","+f.getUrl(); + } + }else if(f.getFileType() == 1){ + //音频 1 + if(StringUtils.isBlank(audio)){ + audio = audio+f.getUrl(); + }else { + audio = audio+","+f.getUrl(); + } + }else if(f.getFileType() == 2){ + //视频 2 + if(StringUtils.isBlank(video)){ + video = video+f.getUrl(); + }else { + video = video+","+f.getUrl(); + } + } + } + + } + + ResubmitTicketVo rsVo = new ResubmitTicketVo(); + rsVo.setId(ticket.getId()); + rsVo.setDesc(vo.getDesc()); + rsVo.setFiles(vo.getFiles()); + rsVo.setAudioFiles(audio); + rsVo.setImageFiles(image); + rsVo.setVideoFiles(video); + ticketMapper.insertTicketSupplement(rsVo); + return AjaxResult.success("工单提交成功"); + } else { + return AjaxResult.error(509, "服务器网络异常,提交失败"); + } + + } + + @Override + public AjaxResult getGuaranteeBySerial(SerialVo vo) { + + if(StringUtils.isNull(vo)){ + return AjaxResult.error("参数缺失"); + } + if(StringUtils.isBlank(vo.getSerialNo())){ + //格式判断 + return AjaxResult.error("机器编号不能为空"); + } + + if(StringUtils.isBlank(vo.getType())){ + //格式判断 + //return AjaxResult.error("查询类型不能为空"); + vo.setType("ma"); + } + + if("ma".equals(vo.getType())){ + GuaranteeDto dto = ticketMapper.getGuaranteeBySerial(vo.getSerialNo()); + + if(StringUtils.isNotNull(dto)){ + return AjaxResult.success(dto); + }else { + return AjaxResult.error("机器编号不存在,请检查您的机器编号!"); + } + }else if("ac".equals(vo.getType())){ + GuaranteeDto dto = ticketMapper.getAccessoryBySerial(vo.getSerialNo()); + + if(StringUtils.isNotNull(dto)){ + + dto.setName(AccessoryStatus.getTypeByCode(Convert.toInt(dto.getName(),999))); + + return AjaxResult.success(dto); + }else { + return AjaxResult.error("配件编号不存在,请检查您的配件编号!"); + } + }else { + return AjaxResult.error("参数错误!"); + } + } + + @Override + public AjaxResult checkSerialNo(SerialVo vo) { + + //if(StringUtils.isNull(vo)){ + // return AjaxResult.error("参数缺失:机器编号不能为null"); + //} + // + //if(StringUtils.isBlank(vo.getSerialNo())){ + // return AjaxResult.error(801,"参数缺失:机器编号不能为空"); + //} + // + //String[] seriales = vo.getSerialNo().split("/"); + //StringBuilder result = new StringBuilder(); + //int errorCount = 1; + //result.append("机器编号校验失败,以下机器编号错误"); + //if(seriales.length > 20){ + // return AjaxResult.error("一次最多提交20个机器编码,超过20个的请分多个工单提交"); + //} + //for (String serial: seriales) { + // if(!ticketMapper.checkSerialIfExist(serial)){ + // result.append("
" + errorCount + "、不存在机器编号: "+serial+ " 的销售记录,请检查机器编号"); + // errorCount++; + // } + //} + // + //if(errorCount > 1){ + // return AjaxResult.error(801,result.toString()); + //} + + return AjaxResult.success(""); + } + + @Override + public AjaxResult getFaults() { + + List faults = ticketMapper.getFaults(); + return AjaxResult.success(faults); + } + + + @Override + public AjaxResult onlineChangeType(ConfirmTypeVo vo) { + //根据工单id获取工单详细信息 + TicketDto ticket = ticketMapper.getTicketById(vo.getId()); + + if (StringUtils.isNull(ticket)) { + return AjaxResult.error("工单不存在"); + } + + //todo 如果有说明 + String remark = StringUtils.clean(vo.getRespon()); + if(StringUtils.isBlank(remark)){ + return AjaxResult.error("工单类型转换必须说明原因"); + } + + ResponTicketVo rtVo = new ResponTicketVo(); + rtVo.setId(ticket.getId()); + rtVo.setRespon(vo.getRespon()); + ticketMapper.insertRespon(rtVo,SecurityUtils.getUsername()); + + //ticketMapper.changeTicketStatus(vo.getId(), TicketStatusType.STATUS_TWO.getCode()); + //更改类型并标记 + ticketMapper.changeTicketTypeAndFlag(vo.getId(), 1); + //更改工单状态为 待处理 + ticketMapper.changeTicketStatus(vo.getId(),TicketStatusType.STATUS_ONE.getCode()); + + + + //todo + + return AjaxResult.success(); + } + + @Override + public AjaxResult repairChangeType(ConfirmTypeVo vo) { + //根据工单id获取工单详细信息 + TicketDto ticket = ticketMapper.getTicketById(vo.getId()); + + if (StringUtils.isNull(ticket)) { + return AjaxResult.error("工单不存在"); + } + + if(ticket.getIfChange() > 0){ + //从技术支持转过来的工单 + } + + ticketMapper.changeTicketTypeAndFlag(vo.getId(), 0); + + + //todo 如果有说明 + String remark = StringUtils.clean(vo.getRespon()); + if(StringUtils.isNotBlank(remark)){ + ResponTicketVo rtVo = new ResponTicketVo(); + rtVo.setId(ticket.getId()); + rtVo.setRespon(vo.getRespon()); + + ticketMapper.insertRespon(rtVo,"System"); + } + + //todo 邮箱 线下维修 =》 线上排查 + + return AjaxResult.success(); + } + + @Override + public AjaxResult changeTicketType(ChangeTypeVo vo) { + + //根据工单id获取工单详细信息 + TicketDto ticket = ticketMapper.getTicketById(vo.getId()); + + if (StringUtils.isNull(ticket)) { + return AjaxResult.error("工单不存在"); + } + + if (!ticket.getEmail().equals(vo.getEmail())) { + return AjaxResult.error("异常调用!"); + } + + if (vo.getType() == 0) { + ticketMapper.changeTicketStatusAndType(vo.getId(), TicketStatusType.STATUS_TWO.getCode(), vo.getType()); + } + + if (vo.getType() == 1) { + ticketMapper.changeTicketStatusAndType(vo.getId(), TicketStatusType.STATUS_THREE.getCode(), vo.getType()); + } + + return null; + } + + @Override + public AjaxResult resubmitTicket(ResubmitTicketVo vo) { + + if (StringUtils.isNull(vo)) { + return AjaxResult.error(601, "参数错误"); + } + + // 判断是否超过40条补充内容 + Integer num = ticketMapper.getTicketSupplementCountByTicketId(vo.getId()); + TicketDto ticket = ticketMapper.getTicketById(vo.getId()); + if (StringUtils.isNull(ticket)) { + return AjaxResult.error(601, "参数错误"); + } + + if (Convert.toInt(num, 0) >= 41) { + return AjaxResult.error("工单提交内容已达上限40"); + } + //判断工单当前状态 + if(ticket.getStatus() == TicketStatusType.STATUS_TEN.getCode()){ + return AjaxResult.error("工单已完结,不能再继续提交,若还有问题您可以重新提交工单"); + } + + vo.setDesc(StringUtils.clean(vo.getDesc())); + + //处理files 转换为video_files/转换为image_files/转换为audio_files + + //处理附件 + String video=""; + String audio=""; + String image=""; + if(StringUtils.isNotBlank(vo.getFiles())){ + List files = getFileInfos(vo.getFiles()); + + for (JXYFile f:files) { + if(f.getFileType() == 0){ + //图片 0 + if(StringUtils.isBlank(image)){ + image = image+f.getUrl(); + }else { + image = image+","+f.getUrl(); + } + }else if(f.getFileType() == 1){ + //音频 1 + if(StringUtils.isBlank(audio)){ + audio = audio+f.getUrl(); + }else { + audio = audio+","+f.getUrl(); + } + }else if(f.getFileType() == 2){ + //视频 2 + if(StringUtils.isBlank(video)){ + video = video+f.getUrl(); + }else { + video = video+","+f.getUrl(); + } + } + } + + } + vo.setAudioFiles(audio); + vo.setImageFiles(image); + vo.setVideoFiles(video); + + if (ticketMapper.insertTicketSupplement(vo)) { + //提交成功 更 + if(ticketMapper.changeTicketUpdateTime(vo.getId())){ + return AjaxResult.success("提交成功"); + }else { + return AjaxResult.error("服务器网络异常,提交失败"); + } + } else { + return AjaxResult.error("服务器网络异常,提交失败"); + } + } + + @Override + public AjaxResult mailEndTicket(long id, String email) { + if (!checkUserPermissions(email, id)) { + return AjaxResult.error(601, "参数异常"); + } + if(!email.equals(SecurityUtils.getEmail())){ + System.out.println("邮箱不一致"); + System.out.println(SecurityUtils.getEmail()); + System.out.println(SecurityUtils.getLoginUser()); + } + + if (ticketMapper.checkTicketExist(id) < 1) { + return AjaxResult.error(601, "参数错误"); + } + //订单状态修改为支付失败、 + //更改状态为待寄回状态 code:8 + return checkResult(ticketMapper.changeTicketStatus(id, TicketStatusType.STATUS_EIGHT.getCode())); + } + + @Override + public AjaxResult endTicket(EndTicketVo vo) { + + if (StringUtils.isNull(vo)) { + return AjaxResult.error("参数丢失"); + } + + TicketDto ticket = ticketMapper.getTicketById(vo.getId()); + + if (StringUtils.isNull(ticket)) { + return AjaxResult.error(601, "参数错误"); + } + if (!ticket.getEmail().equals(SecurityUtils.getEmail())) { + return AjaxResult.error("不能修改他人的工单"); + } + + if (ticket.getStatus() == 1 || ticket.getStatus() == 2 || ticket.getStatus() == 21 || ticket.getStatus() == 9) { + //用户可关闭工单的状态 待处理1 客服处理中2 待寄回21 已发回9 + } else { + return AjaxResult.error("当前状态下的工单不能关闭"); + } + + return checkResult(ticketMapper.changeTicketStatus(vo.getId(), TicketStatusType.STATUS_TEN.getCode())); + } + + @Override + public AjaxResult getTicketDetails(long id) { + + if (id < 1) { + return AjaxResult.error("工单号参数缺失"); + } + + TicketDetailsDto ticket = ticketMapper.getBackendTicketById(id); + if (StringUtils.isNull(ticket)) { + return AjaxResult.error( "根据工单号未找到工单详情"); + } + + //获取工单内容list + List supplementList = ticketMapper.getTicketSupplementListByTicketId(id); + + //获取工单回复list + List responList = ticketMapper.getTicketResponListByTicketId(id); + + //合并回复和内容list并重新排序 + supplementList.addAll(responList); + List list = supplementList.stream().sorted(Comparator.comparing(TicketContentDto::getTime)).collect(Collectors.toList()); + + ticket.setList(list); + + ticket.setStatus(getStatus(Convert.toInt(ticket.getStatus()))); + ticket.setType(getType(Convert.toInt(ticket.getType()))); + + return AjaxResult.success(ticket); + } + + @Override + public AjaxResult getTicketDetails3(long id) { + if (id < 1) { + return AjaxResult.error("工单号参数缺失"); + } + + TicketDetails3Dto ticket = ticketMapper.geTicketDetails3ById(id); + if (StringUtils.isNull(ticket)) { + return AjaxResult.error(509, "后台异常(后台网络异常/后台传参异常),信息获取失败"); + } + + if(StringUtils.isBlank(ticket.getSerialNo())){ + return AjaxResult.error("异常工单,机器编号不存在"); + } + + String[] seriales = ticket.getSerialNo().split("/"); + + Map warranty = new LinkedHashMap<>(); + for(String serial : seriales){ + GuaranteeDto guarantee = ticketMapper.getGuaranteeBySerial(serial); + if(StringUtils.isNull(guarantee)){ + warranty.put(serial,"未找到"); + }else { + warranty.put(serial,DateUtils.parseDateToStr(DateUtils.YYYY_MM_DD,guarantee.getGuaranteeDate())); + } + } + + ticket.setWarranty(warranty); + ticket.setStatus(getStatus(Convert.toInt(ticket.getStatus()))); + + //获取工单内容list + List supplementList = ticketMapper.getTicketSupplementListByTicketId(id); + + //获取工单回复list + List responList = ticketMapper.getTicketResponListByTicketId(id); + + //合并回复和内容list并重新排序 + supplementList.addAll(responList); + List list = supplementList.stream().sorted(Comparator.comparing(TicketContentDto::getTime)).collect(Collectors.toList()); + + + ticket.setList(list); + + return AjaxResult.success(ticket); + } + + @Override + public AjaxResult getReturnDetails(long id) { + if (id < 1) { + return AjaxResult.error("工单号参数缺失"); + } + + ReturnInfoDto ticket = ticketMapper.getReturnDetailsById(id); + + if(Convert.toInt(ticket.getStatus()) != TicketStatusType.STATUS_EIGHT.getCode()){ + return AjaxResult.error("该工单不允许此操作"); + } + + if (StringUtils.isNull(ticket)) { + return AjaxResult.error(509, "后台异常(后台网络异常/后台传参异常),信息获取失败"); + } + + ticket.setStatus(getStatus(Convert.toInt(ticket.getStatus()))); + ticket.setType(getType(Convert.toInt(ticket.getType()))); + return AjaxResult.success(ticket); + } + + @Override + @Transactional + public AjaxResult repairConfirmTicket(RepairConfirmVo vo) { + if(StringUtils.isNull(vo)){ + return AjaxResult.error("参数缺失"); + } + + if(vo.getId() < 1){ + return AjaxResult.error("工单id参数缺失"); + } + + TicketDto ticket = ticketMapper.getTicketById(vo.getId()); + + if(StringUtils.isNull(ticket)){ + return AjaxResult.error("工单id参数缺失"); + } + + if(StringUtils.isAnyBlank(vo.getName(),vo.getAddress(),vo.getPhone())){ + return AjaxResult.error("物流信息参数缺失"); + } + + if(ticket.getType() != TicketType.OFFLINE.getCode()){ + return AjaxResult.error("该工单为线上支持工单,你无法操作"); + } + if(ticket.getStatus() != TicketStatusType.STATUS_ONE.getCode()){ + return AjaxResult.error("该工单不允许此操作"); + } + + ticketMapper.insertRepairLogisticalInfo(vo); + + ticketMapper.insertReturnLogisticalInfo(vo); + + String remark = StringUtils.clean(vo.getRemark()); + if(StringUtils.isNotBlank(remark)){ + //remark 不为空 添加到respon里面 + ResponTicketVo rtVo = new ResponTicketVo(); + rtVo.setId(vo.getId()); + rtVo.setRespon(remark); + ticketMapper.insertRespon(rtVo,SecurityUtils.getUsername()); + } + + ticketMapper.changeTicketStatus(vo.getId(),TicketStatusType.STATUS_WAITING.getCode()); + + BaseRepairInfoDto info = ticketMapper.getRepairLogisticalInfoById(vo.getId()); + + if(ticket.getIfChange() == 1){ + //用户状态更改过 说明是线上转线下 需要发线上排查 =》 线下维修 + //todo 邮件发送 + CompletableFuture.runAsync(()->{mailService.sendChangeToRepairNoticeHTMLMailMessage(ticket.getEmail(),ticket.getId());}); + }else { + CompletableFuture.runAsync(()->{mailService.sendNeedRepairNoticeHTMLMailMessage(ticket.getEmail(),ticket.getId(),info.getName(),info.getAddress(),info.getPhone());}); + } + + + + return AjaxResult.success(); + } + + @Override + public AjaxResult repairRefuseTicket(RepairRefuseVo vo) { + + if(StringUtils.isBlank(vo.getReason())){ + return AjaxResult.error("拒绝维修缘由不能为空"); + } + + + TicketDto ticket = ticketMapper.getTicketById(vo.getId()); + + if(StringUtils.isNull(ticket)){ + return AjaxResult.error("工单id异常"); + } + + if(ticket.getStatus() != TicketStatusType.STATUS_ONE.getCode()){ + return AjaxResult.error("该工单不允许此操作"); + } + ResponTicketVo reVo = new ResponTicketVo(); + reVo.setId(vo.getId()); + reVo.setRespon(vo.getReason()); + ticketMapper.insertRespon(reVo,SecurityUtils.getUsername()); + + ticketMapper.changeTicketStatus(vo.getId(),TicketStatusType.STATUS_TEN.getCode()); + + + return AjaxResult.success("操作完成"); + } + + @Override + public AjaxResult getPrivateTicketDetails(long id) { + + if (id < 1) { + return AjaxResult.error("工单号参数缺失"); + } + + TicketPrivateDto ticket = ticketMapper.getPrivateTicketById(id, SecurityUtils.getEmail()); + + if (StringUtils.isNull(ticket)) { + return AjaxResult.error("通过工单号:" + id + ",找不到与您对应的工单!"); + } + + //获取工单内容list + List supplementList = ticketMapper.getTicketSupplementListByTicketId(id); + + //获取工单回复list + List responList = ticketMapper.getTicketResponListByTicketId(id); + + //合并回复和内容list并重新排序 + supplementList.addAll(responList); + List list = supplementList.stream().sorted(Comparator.comparing(TicketContentDto::getTime)).collect(Collectors.toList()); + + ticket.setList(list); + + ticket.setStatus(getStatus(Convert.toInt(ticket.getStatus()))); + ticket.setDetails(deatilsToTable(ticket.getDetails())); + ticket.setType(getType(Convert.toInt(ticket.getType()))); + + return AjaxResult.success(ticket); + } + + @Override + public List getAllTicket(AllTicketListVo vo) { + + List list = ticketMapper.getTicketListByCond(vo); + list.stream().forEach(e -> { + e.setStatus(getStatus(Convert.toInt(e.getStatus()))); + e.setType(getType(Convert.toInt(e.getType(),0))); + e.setPaymentStatus(PayStatus.getTypeByCode(Convert.toInt(e.getPaymentStatus(),3))); + }); + + return list; + } + + @Override + public AjaxResult getPrivateTicketByStatus(PrivateTicketListVo vo) { + + System.out.println(vo); + String email = SecurityUtils.getEmail(); + + System.out.println(email); + + //处理搜索条件 + if (StringUtils.isNotBlank(vo.getCond())) { + Long id = Convert.toLong(vo.getCond()); + if (StringUtils.isNull(id)) { + vo.setSerialNo(vo.getCond()); + } else { + if (id <= 0) { + return AjaxResult.error("条件传参异常"); + } + vo.setId(id); + } + } + + List list = new ArrayList<>(); + int status = vo.getStatus(); + if (status == 0) { + //全部工单传 0 + list = ticketMapper.getPrivateTicketList(vo, email); + } else if (status == 1) { + //进行中的工单 + list = ticketMapper.getPrivateProcessingTicketList(vo, email); + } else if (status == 2) { + //已完结工单 + list = ticketMapper.getPrivateEndTicketList(vo, email); + } else { + return AjaxResult.success(list); + } + + list.stream().forEach(e -> { + e.setStatus(getStatus(Convert.toInt(e.getStatus()))); + e.setType(getType(Convert.toInt(e.getType()))); + }); + + return AjaxResult.success(list); + } + + @Override + public List getTicketList(StatusVo vo) { + + if(StringUtils.isNull(vo)){ + + } + + List list = ticketMapper.getBackendTicketList(vo.getStatus()); + + list.stream().forEach(e -> { + e.setStatus(getStatus(Convert.toInt(e.getStatus()))); + e.setType(getType(Convert.toInt(e.getType()))); + }); + return list; + } + + @Override + public List getSupportTicketList(StatusVo vo) { + + if(StringUtils.isNull(vo)){ + return new ArrayList<>(); + } + + if(vo.getStatus() == 1 || vo.getStatus() == 2 || vo.getStatus() == 10 ){ + //status 取值仅有1 待处理 2客服处理中 10已关闭 + List list = ticketMapper.getTicketListByStatusAndType(vo, 0); + + list.stream().forEach(e -> { + e.setStatus(getStatus(Convert.toInt(e.getStatus()))); + e.setType(getType(Convert.toInt(e.getType()))); + }); + return list; + }else { + return new ArrayList<>(); + } + + + } + + @Override + public List getRepairTicketList(StatusVo vo) { + if(StringUtils.isNull(vo)){ + return new ArrayList<>(); + } + + // 1 3 4 5 6 7 8 9 10 + if(vo.getStatus() !=2){ + List list = ticketMapper.getTicketListByStatusAndType(vo, 1); + + list.stream().forEach(e -> { + e.setStatus(getStatus(Convert.toInt(e.getStatus()))); + e.setType(getType(Convert.toInt(e.getType()))); + }); + return list; + }else { + return new ArrayList<>(); + } + } + + + @Override + public AjaxResult onlineResponTicket(ResponTicketVo vo) { + + if (vo.getId() < 1) { + return AjaxResult.error(601, "工单号缺失"); + } + + TicketDto ticket = ticketMapper.getTicketById(vo.getId()); + + if (StringUtils.isNull(ticket)) { + return AjaxResult.error(601, "工单不存在"); + } + + if (ticket.getStatus() == TicketStatusType.STATUS_TEN.getCode()) { + return AjaxResult.error("工单已完结,不能再回复"); + } + + + + vo.setFiles(StringUtils.clean(vo.getFiles())); + vo.setRespon(StringUtils.clean(vo.getRespon())); + + //todo 处理files 转为image/video/audio对应的路径 + String video=""; + String audio=""; + String image=""; + if(StringUtils.isNotBlank(vo.getFiles())){ + List files = getFileInfos(vo.getFiles()); + + for (JXYFile f:files) { + if(f.getFileType() == 0){ + //图片 0 + if(StringUtils.isBlank(image)){ + image = image+f.getUrl(); + }else { + image = image+","+f.getUrl(); + } + }else if(f.getFileType() == 1){ + //音频 1 + if(StringUtils.isBlank(audio)){ + audio = audio+f.getUrl(); + }else { + audio = audio+","+f.getUrl(); + } + }else if(f.getFileType() == 2){ + //视频 2 + if(StringUtils.isBlank(video)){ + video = video+f.getUrl(); + }else { + video = video+","+f.getUrl(); + } + } + } + + } + + vo.setAudioFiles(audio); + vo.setImageFiles(image); + vo.setVideoFiles(video); + + if (ticketMapper.insertRespon(vo, SecurityUtils.getUsername())) { + //return checkResult(ticketMapper.changeTicketStatus(vo.getId(), TicketStatusType.STATUS_FOUR.getCode())); + //if(ticket.getStatus() != TicketStatusType.STATUS_TWO.getCode()){ + // //若果是待处理工单 则修改工单状态为客服处理中 + // + //} + ticketMapper.changeTicketStatus(vo.getId(),TicketStatusType.STATUS_TWO.getCode()); + //回复成功 邮箱通知 异步 + CompletableFuture.runAsync(()->{ + mailService.sendRespondNoticeHTMLMailMessage(ticket.getEmail(), ticket.getId()); + }); + + return AjaxResult.success("回复成功"); + } else { + return AjaxResult.error("服务器网络异常,提交失败"); + } + } + + + @Override + public AjaxResult RepairResponTicket(ResponTicketVo vo) { + if (vo.getId() < 1) { + return AjaxResult.error(601, "工单号缺失"); + } + + TicketDto ticket = ticketMapper.getTicketById(vo.getId()); + + if (StringUtils.isNull(ticket)) { + return AjaxResult.error(601, "工单不存在"); + } + + if (ticket.getStatus() == TicketStatusType.STATUS_TEN.getCode()) { + return AjaxResult.error("工单已完结,不能再回复"); + } + + //if (ticket.getStatus() == TicketStatusType.STATUS_TWO.getCode()) { + // return AjaxResult.error("维修人员不能回复线上处理工单"); + //} + + + vo.setFiles(StringUtils.clean(vo.getFiles())); + vo.setRespon(StringUtils.clean(vo.getRespon())); + + String video=""; + String audio=""; + String image=""; + if(StringUtils.isNotBlank(vo.getFiles())){ + List files = getFileInfos(vo.getFiles()); + + for (JXYFile f:files) { + if(f.getFileType() == 0){ + //图片 0 + if(StringUtils.isBlank(image)){ + image = image+f.getUrl(); + }else { + image = image+","+f.getUrl(); + } + }else if(f.getFileType() == 1){ + //音频 1 + if(StringUtils.isBlank(audio)){ + audio = audio+f.getUrl(); + }else { + audio = audio+","+f.getUrl(); + } + }else if(f.getFileType() == 2){ + //视频 2 + if(StringUtils.isBlank(video)){ + video = video+f.getUrl(); + }else { + video = video+","+f.getUrl(); + } + } + } + + } + + vo.setAudioFiles(audio); + vo.setImageFiles(image); + vo.setVideoFiles(video); + + if (ticketMapper.insertRespon(vo, SecurityUtils.getUsername())) { + //return checkResult(ticketMapper.changeTicketStatus(vo.getId(), TicketStatusType.STATUS_FOUR.getCode())); + ticketMapper.changeTicketUpdateTime(vo.getId()); + //回复成功 邮箱通知 + CompletableFuture.runAsync(()->{ + mailService.sendRespondNoticeHTMLMailMessage(ticket.getEmail(), ticket.getId()); + }); + return AjaxResult.success("回复成功"); + } else { + return AjaxResult.error("服务器网络异常,提交失败"); + } + } + + @Override + public AjaxResult getRepairTicketDetails(long id) { + //ticketMapper.getPrivateRepairTicket() + return null; + } + + @Override + public AjaxResult receipt(ConfirmTypeVo vo) { + if(StringUtils.isNull(vo)){ + return AjaxResult.error("工单id缺失"); + } + + TicketDto ticket = ticketMapper.getTicketById(vo.getId()); + if(StringUtils.isNull(ticket)){ + return AjaxResult.error("工单不存在"); + } + + if(ticket.getStatus() != TicketStatusType.STATUS_THREE.getCode()){ + return AjaxResult.error("该工单不允许此操作"); + } + + if(ticketMapper.changeTicketStatus(vo.getId(), TicketStatusType.STATUS_FOUR.getCode())){ + + CompletableFuture.runAsync(()->{mailService.sendReceptMachineNoticeHTMLMailMessage(ticket.getEmail(),ticket.getSerialNo());}); + + return AjaxResult.success("已确认收货,工单状态已更改"); + }else { + return AjaxResult.error("系统繁忙"); + } + + + } + + @Override + public AjaxResult repairTicketAddPrice(QuotedVo vo) { + + System.out.println("入参"+vo); + //校验 + if(StringUtils.isNull(vo)){ + return AjaxResult.error("工单参数缺失"); + } + + //if(StringUtils.isNull(vo.getDetails())){ + // return AjaxResult.error("收费详情不能为空"); + //} + if(StringUtils.isBlank(vo.getTotal())){ + return AjaxResult.error("维修报价不能为空"); + } + Double total = Convert.toDouble(vo.getTotal(),-1.0); + if(total < 0){ + return AjaxResult.error("维修报价传参错误"); + } + + TicketDto ticket = ticketMapper.getTicketById(vo.getId()); + if(StringUtils.isNull(ticket)){ + if(vo.getId() < 1){ + return AjaxResult.error("提示工单号不能为空"); + }else { + return AjaxResult.error("工单不存在"); + } + } + + System.out.println("查询到工单"+ticket); + + if(ticket.getStatus() != TicketStatusType.STATUS_FOUR.getCode()){ + return AjaxResult.error("该工单不允许此操作"); + } + + if(total == 0){ + //直接进入到维修流程 + ticketMapper.changeTicketStatus(vo.getId(),TicketStatusType.STATUS_SIX.getCode()); + return AjaxResult.success("保修期订单,直接进入维修流程"); + //return AjaxResult.error("总价不能为空"); + } + + //邮件中的报价详情列表 + //StringBuilder htmlDetails = new StringBuilder(); + //数据库存储的报价详情 + StringBuilder detailSb = new StringBuilder(); + detailSb.append("

维修报价详情:

"); + //报价详情详情列 + LinkedHashMap> map = new LinkedHashMap<>(); + if(StringUtils.isNotNull(vo.getDetails())){ + + //System.out.println("details"+vo.getDetails()); + detailSb.append(""); + //报价详情表头 + detailSb.append(""); + detailSb.append(""); + detailSb.append(""); + detailSb.append(""); + detailSb.append(""); + System.out.println("d1:"+detailSb.toString()); + final BigDecimal[] totalPrice = {BigDecimal.ZERO}; + vo.getDetails().stream().forEach(e ->{ + LinkedHashMap detailsMap = new LinkedHashMap<>(); + for (int i=0; i"); + if(i == 0){ + detailSb.append(""); + } + QuotedDetailsVo dto = e.getItems().get(i); + detailsMap.put(dto.getReason(),dto.getPrice()); + System.out.println(i+"e:"+dto); + detailSb.append(""); + detailSb.append(""); + detailSb.append(""); + totalPrice[0] = totalPrice[0].add(Convert.toBigDecimal(dto.getPrice(),BigDecimal.ZERO)); + System.out.println(totalPrice[0]); + } + map.put(e.getMachine(),detailsMap); + }); + System.out.println("map:"+map.toString()); + System.out.println("d2:"+detailSb.toString()); + + if(totalPrice[0].compareTo(BigDecimal.valueOf(total)) != 0){ + System.out.println("totalPrice"+ totalPrice[0]); + System.out.println("total"+total); + return AjaxResult.error("参数错误:维修详情计算结果与上传的总价不一致"); + } + detailSb.append("
机器SN码维修项价格$
"+e.getMachine()+""+dto.getReason()+""+dto.getPrice()+"

订单总应收款:"+vo.getTotal()+"

"); + //htmlDetails.append(thSb).append(thSb); + //htmlDetails.append("

订单总应收款:"+vo.getTotal()+"

"); + } + else { + return AjaxResult.error("接口参数异常"); + } + + //报价信息入库 1、生成order_key 2、支付网关记录支付信息 3、本地记录报价详情 4、redis设置支付有效期为7天 + String tkOrderStr = UUID.randomUUID().toString(); + tkOrderStr = tkOrderStr.substring(tkOrderStr.lastIndexOf("-")+1); + String orderKey = Constants.TICKET_ORDER_PREFIX+tkOrderStr; + //1、、本地记录报价详情 + String details = JSON.toJSONString(map); + if(!ticketMapper.insertTicketPayment(vo,details,orderKey)){ + return AjaxResult.error("数据库异常"); + } + + //if(total == 0){ + // //直接进入到维修流程 + // ticketMapper.changeTicketStatus(vo.getId(),TicketStatusType.STATUS_SIX.getCode()); + // return AjaxResult.success("保修期订单,直接进入维修流程"); + // //return AjaxResult.error("总价不能为空"); + + //} + + //1、支付网关记录支付信息 + String transaction_id = IdUtils.fastSimpleUUID(); + OrderApplyVo oaVo = new OrderApplyVo(); + oaVo.setOrderId(ticket.getId()); + + oaVo.setOrderKey(orderKey); + oaVo.setAmount(total); + oaVo.setCurrency(""); + oaVo.setBillingFname(""); + oaVo.setBillingLname(""); + oaVo.setBillingEmail(ticket.getEmail()); + oaVo.setRedirectTo(MY_DOMAIN +"/os/workManagement/workOrderRecords"); + oaVo.setCancelUrl(MY_DOMAIN +"/os/workManagement/workOrderRecords"); + oaVo.setViewUrl(MY_DOMAIN +"/os/workManagement/workOrderRecords"); + oaVo.setNotifyUrl(""); + //orderFrom 设置为1 代表维修订单 此处设置商品数量为0(商品数量是商城订单的属性) + if(orderMapper.insertOrderInfo(oaVo,transaction_id, OrderFrom.TICKET.getCode(),0)){ + //设置缓存有效期 7天 + Map selectMap = new HashMap<>(); + selectMap.put("transaction_id", transaction_id); + System.out.println("将要放入redis的数据 : "+selectMap); + //通过redis设置 支付有效期 + redisService.setCacheMap("order_key:"+orderKey,selectMap); + redisService.expire("order_key:"+orderKey,7L, TimeUnit.DAYS); + }else { + TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(); + } + + //修改订单状态为 已报价,等待付款 + ticketMapper.changeTicketStatus(vo.getId(),TicketStatusType.STATUS_FIVE.getCode()); + + //发送邮件至用户邮箱 + CompletableFuture.runAsync(()->{ + //mailService.sendRepairQuotationNoticeHTMLMailMessage(ticket.getEmail(), ticket.getSerialNo(),vo,orderKey , htmlDetails.toString()); + mailService.sendRepairQuotationNoticeHTMLMailMessage(ticket.getEmail(), ticket.getSerialNo(),vo,orderKey , detailSb.toString()); + }); + + return AjaxResult.success(); + } + + @Override + public AjaxResult changeRepairTicketStatus(RepairChangeStatusVo vo) { + //判断工单状态码 + if(StringUtils.isNull(vo)){ + return AjaxResult.error("参数缺失"); + } + TicketDto ticket = ticketMapper.getTicketById(vo.getId()); + int status = ticket.getStatus(); + if(status ==0){ + return AjaxResult.error("工单号异常"); + } + if(status == TicketStatusType.STATUS_SIX.getCode()){ + //已付款 + if(status == vo.getStatus()){ + return AjaxResult.error("新状态不能与旧状态一致"); + } + if(vo.getStatus() == TicketStatusType.STATUS_SEVEN.getCode()){ + //修改ticket表状态和process + if(ticketMapper.changeTicketStatusAndProcess(vo)){ + return AjaxResult.success("工单状态已更改为"+TicketStatusType.STATUS_SEVEN.getStatus()); + }else{ + return AjaxResult.error("服务器繁忙"); + } + }else { + return AjaxResult.error("工单状态参数错误"); + } + + } + else if(status == TicketStatusType.STATUS_SEVEN.getCode()){ + //正在维修 + if(status == vo.getStatus()){ + return AjaxResult.error("新状态不能与旧状态一致"); + } + if(vo.getStatus() == TicketStatusType.STATUS_EIGHT.getCode()){ + //修改ticket表状态和process + if(ticketMapper.changeTicketStatusAndProcess(vo)){ + //发送邮箱 + ReturnInfoDto returnDetails= ticketMapper.getReturnDetailsById(vo.getId()); + String subject ="维修完成通知邮件"; + String content ="\n" + + "\n" + + "\n" + + "\n" + + "邮件\n" + + "\n" + + "\n" + + "\t

尊敬的用户您好!

\n" + + "\t

您的"+ticket.getSerialNo()+"号机器现在已完成维修,我们将根据您之前填写的物流信息寄回您的机器,您填写的物流详情如下:\n" + + "

收件人:" +returnDetails.getReceiveName() +"

"+ + "

收件地址:" +returnDetails.getReceiveAddress() +"

"+ + "\n\t

The Wind Miner感谢您的支持!

\n" + + "\n" + + ""; + CompletableFuture.runAsync(()->{mailService.sendHtmlMailMessage(ticket.getEmail(),subject,content);}); + return AjaxResult.success("工单状态已更改为"+TicketStatusType.STATUS_SEVEN.getStatus()); + }else{ + return AjaxResult.error("服务器繁忙"); + } + }else { + return AjaxResult.error("工单状态参数错误"); + } + }else { + return AjaxResult.error("该状态不能调用该接口"); + } + + + } + + @Override + public AjaxResult addReturnLogistical(ReturnLogisticalVo vo) { + if(vo.getId() < 1){ + return AjaxResult.error("工单id参数缺失"); + } + + TicketDto ticket = ticketMapper.getTicketById(vo.getId()); + + if(StringUtils.isNull(ticket)){ + return AjaxResult.error("工单id参数缺失"); + } + + if(StringUtils.isAnyBlank(vo.getCompany(),vo.getNo(),vo.getSendTime())){ + return AjaxResult.error("物流信息参数缺失"); + } + + ReturnInfoDto details = ticketMapper.getReturnDetailsById(ticket.getId()); + + //判断工单状态 + if(ticket.getStatus() != TicketStatusType.STATUS_EIGHT.getCode()){ + return AjaxResult.error("该工单不允许此操作"); + } + + //修改回退物流信息 + if(ticketMapper.updateReturnLogisticalCompanyInfo(vo)){ + if(ticketMapper.changeTicketStatus(vo.getId(),TicketStatusType.STATUS_NINE.getCode())){ + //发送邮件 + orderMapper.confirmSendProductByOrderKey("key",1); + CompletableFuture.runAsync(()->{ + String content="\n" + + "\n" + + "\n" + + "\n" + + "邮件\n" + + "\n" + + "\n" + + "\t

尊敬的用户您好!您的"+ticket.getSerialNo()+"机器现在已维修完成并寄回,请您仔细查阅下方有关物流信息,并前往相关物流公司网站查询最新物流信息

\n" + + "\t

物流公司:\t"+vo.getCompany()+"

\n" + + "\t

物流单号:\t"+vo.getNo()+"

\n" + + "\t

工单号:\t"+ticket.getId()+"

\n" + + "\t

发件人:\t"+details.getSendName()+"

\n" + + "\t

发件地址:\t"+details.getSendAddress()+"

\n" + + "\t

发件人联系方式:\t"+details.getSendPhone()+"

\n" + + "\t

寄件人:\t"+details.getReceiveName()+"

\n" + + "\t

寄件地址:\t"+details.getReceiveAddress()+"

\n" + + "\t

寄件人联系方式:\t"+details.getReceivePhone()+"

\n"; + if(StringUtils.isNotNull(vo.getNotes())){ + content = content + + "\t

备注:\t"+vo.getNotes()+"

\n" + + "\n\t

thewindminer感谢您的支持!

\n" + + "\n" + + ""; + }else { + content = content +"\n\t

thewindminer感谢您的支持!

\n" + + "\n" + + ""; + } + + + mailService.sendHtmlMailMessage(ticket.getEmail(), "机器发回邮件提示内容", content); + }); + return AjaxResult.success("物流信息已提交"); + }else { + return AjaxResult.error("服务器繁忙"); + } + }else { + return AjaxResult.error("服务器繁忙"); + } + } + + @Override + public List getAccountTicketList(StatusVo vo) { + if(StringUtils.isNull(vo)){ + return new ArrayList<>(); + } + + // 1 3 4 5 6 7 8 9 10 + if(vo.getStatus() ==6){ + //已付款 应包含所有已付款的工单 工单状态可以为已付款、正在维修、维修结束、已发回、已完结 此处判断是按付款信息表的付款状态 + List list = ticketMapper.getPaySuccessAccountTicketList(vo); + + list.stream().forEach(e -> { + e.setStatus(getStatus(Convert.toInt(e.getStatus()))); + e.setType(getType(Convert.toInt(e.getType()))); + }); + return list; + }else if(vo.getStatus() ==20){ + //确认付款中 + List list = ticketMapper.getAccountTicketListByStatus(vo); + + list.stream().forEach(e -> { + e.setStatus(getStatus(Convert.toInt(e.getStatus()))); + e.setType(getType(Convert.toInt(e.getType()))); + }); + return list; + }else { + return new ArrayList<>(); + } + } + + @Override + public AjaxResult getBaseRepairInfo() { + + List list = ticketMapper.getPrivateRepairTicket(SecurityUtils.getEmail()); + + BaseRepairInfoDto dto = new BaseRepairInfoDto(); + if(list.size()>0){ + dto.setName(list.get(0).getName()); + dto.setPhone(list.get(0).getPhone()); + dto.setAddress(list.get(0).getAddress()); + }else { + dto.setName(TheWindMinerLogisticalInfo.INFO_ONE.getName()); + dto.setPhone(TheWindMinerLogisticalInfo.INFO_ONE.getPhone()); + dto.setAddress(TheWindMinerLogisticalInfo.INFO_ONE.getAddress()); + } + dto.setList(list); + return AjaxResult.success(dto); + } + + @Override + @Transactional + public AjaxResult addRepairLogisticalInfo(UserLogisticalVo vo) { + + if (StringUtils.isNull(vo.getTicketId())) { + return AjaxResult.error("工单id不能为空"); + } + if (StringUtils.isBlank(vo.getCompany())) { + return AjaxResult.error("物流公司不能为空"); + } + if (StringUtils.isBlank(vo.getNo())) { + return AjaxResult.error("快递单号不能为空"); + } + if (StringUtils.isBlank(vo.getSendTime())) { + return AjaxResult.error("寄出时间不能为空"); + } + if (StringUtils.isBlank(vo.getSendName())) { + return AjaxResult.error("寄件人不能为空"); + } + if (StringUtils.isBlank(vo.getSendAddress())) { + return AjaxResult.error("寄件地址不能为空"); + } + if (StringUtils.isBlank(vo.getSendPhone())) { + return AjaxResult.error("寄件人联系方式不能为空"); + } + if (StringUtils.isBlank(vo.getSendName())) { + return AjaxResult.error("回退收件人不能为空"); + } + if (StringUtils.isBlank(vo.getSendAddress())) { + return AjaxResult.error("回退收件地址不能为空"); + } + if (StringUtils.isBlank(vo.getSendPhone())) { + return AjaxResult.error("回退收件人联系方式不能为空"); + } + + TicketDto ticket = ticketMapper.getTicketById(vo.getTicketId()); + if (StringUtils.isNull(ticket)) { + return AjaxResult.error("工单不存在"); + } + + if(ticket.getStatus() == TicketStatusType.STATUS_ONE.getCode()){ + return AjaxResult.error("待处理工单无须提交物流信息"); + } + + if(ticket.getStatus() != TicketStatusType.STATUS_WAITING.getCode()){ + return AjaxResult.error("只有待寄件工单可以填写物流信息"); + } + + /** 寄回的物流信息入库 */ + boolean b1 = ticketMapper.updateRepairLogisticalInfo(vo); + + /** 用户寄出的物流信息入库 */ + boolean b2 = ticketMapper.updateReturnLogisticalInfo(vo); + + if(b1 && b2){ + ticketMapper.changeTicketStatus(vo.getTicketId(),TicketStatusType.STATUS_THREE.getCode()); + return AjaxResult.success("物流信息已提交,维修人员确认收货后第一时间会以邮件的形式通知您"); + }else { + return AjaxResult.error("服务器繁忙"); + } + + + } + + @Override + public AjaxResult getTicketPaymentOrderInfo(IdVo vo) { + + if(StringUtils.isNull(vo)){ + return AjaxResult.error("参数缺失"); + } + + if(vo.getId() == 0){ + return AjaxResult.error("参数不全"); + } + + TicketPaymentDto dto = ticketMapper.getTicketPaymentDtoByOrderId(vo.getId()); + + Map map =new HashMap<>(); + + if(StringUtils.isNull(dto)){ + return AjaxResult.error("该工单没有对应维修订单"); + } + + //根据redis判断是否在支付有效期 + if(redisService.hasKey("order_key:"+dto.getOrderKey())) { + //存在缓存 且wordpress数据表中的order状态要是待支付状态 说明还在可支付有效期 可跳到支付页面 + Map cacheMap = redisService.getCacheMap("order_key:" + dto.getOrderKey()); + System.out.println("redis中的数据: " +cacheMap); + String coin = cacheMap.get("coin"); + System.out.println("redis存储的coin : "+coin); + String chain = cacheMap.get("chain"); + System.out.println("redis存储的chain : "+coin); + + if("TRX".equals(chain) || "TRON".equals(chain)){ + chain = "TRX"; + } + + String redirectTO = MY_DOMAIN +"/os/paymentSelection?order_key=" + dto.getOrderKey()+"&shipping=WX"; + if(!StringUtils.isAnyBlank(coin,chain)){ + System.out.println("coin和chain都有值"); + coin = coin.toUpperCase(); + chain = chain.toUpperCase(); + //coin和chain都有值则 跳转到支付页面 + redirectTO= MY_DOMAIN +"/os/paymentPage?coin="+coin+"&chain="+chain+"&order_key=" + dto.getOrderKey()+"&shipping=WX"; + } + + System.out.println("redirectTO: "+redirectTO); + map.put("url",redirectTO); + return AjaxResult.success(map); + } + else { + //不存在 说明不能再支付 直接跳订单详情页 + //配合前端提示用户支付订单已取消或者已过支付有效期 即将返回订单详情页面 + return AjaxResult.error("该工单已过支付有效期"); + } + } + + + @Override + public AjaxResult getAccountTicketDetails(long id) { + + //判断工单状态码 + if(StringUtils.isNull(id)){ + return AjaxResult.error("参数缺失"); + } + AccountDetailsDto ticket = ticketMapper.getAccountDetailsById(id); + if(StringUtils.isNull(ticket)){ + return AjaxResult.error("未找到对应工单"); + } + + //可以查看状态为6 + if(Convert.toInt(ticket.getStatus()) == TicketStatusType.STATUS_SIX.getCode() || + Convert.toInt(ticket.getStatus()) == TicketStatusType.STATUS_SEVEN.getCode() || + Convert.toInt(ticket.getStatus()) == TicketStatusType.STATUS_EIGHT.getCode() || + Convert.toInt(ticket.getStatus()) == TicketStatusType.STATUS_NINE.getCode() || + Convert.toInt(ticket.getStatus()) == TicketStatusType.STATUS_TEN.getCode() || + Convert.toInt(ticket.getStatus()) == TicketStatusType.STATUS_CHECKING.getCode()){ + //只能看确认付款中和已付款的工单 + ticket.setStatus(getStatus(Convert.toInt(ticket.getStatus()))); + ticket.setType(getType(Convert.toInt(ticket.getType()))); + ticket.setPaymentStatus(PayStatus.getTypeByCode(Convert.toInt(ticket.getPaymentStatus(),3))); + return AjaxResult.success(ticket); + }else { + return AjaxResult.error("你无权查看其他状态的工单"); + } + } + + + @Override + public AjaxResult noticeUserByTicketId(IdVo vo) { + + TicketDto ticket = ticketMapper.getTicketById(vo.getId()); + + if(StringUtils.isNull(ticket)){ + return AjaxResult.error("工单id错误"); + } + + //只有工单状态为确认付款中的工单才能通知用户 + if(ticket.getStatus() != TicketStatusType.STATUS_CHECKING.getCode()){ + return AjaxResult.error("该工单不允许此操作"); + } + + //邮件通知用户 + //todo 邮件发送 + CompletableFuture.runAsync(()->{mailService.sendPayLessNoticeHTMLMailMessage(ticket.getEmail(),ticket.getId());}); + + return AjaxResult.success("通知用户邮件已发送成功"); + } + + //@Override + //public AjaxResult addReturnLogisticalInfo(ReturnLogisticalVo vo) { + // + // TheWindMinerLogisticalInfo info = TheWindMinerLogisticalInfo.getInfoByCode(1); + // vo.setSendName(info.getName()); + // vo.setSendAddress(info.getAddress()); + // vo.setSendPhone(info.getPhone()); + // + // if(StringUtils.isNull(vo.getTicketId())){ + // return AjaxResult.error("工单id不能为空"); + // } + // + // if(ticketMapper.checkTicketExist(vo.getTicketId()) < 1){ + // return AjaxResult.error("工单不存在"); + // } + // + // if(StringUtils.isBlank(vo.getReceiveName())){ + // return AjaxResult.error("收件人不能为空"); + // } + // if(StringUtils.isBlank(vo.getReceiveAddress())){ + // return AjaxResult.error("收件地址不能为空"); + // } + // if(StringUtils.isBlank(vo.getReceivePhone())){ + // return AjaxResult.error("收件人联系方式不能为空"); + // } + // ticketMapper.insertReturnLogisticalInfo(vo); + // return AjaxResult.success(); + //} + + + public AjaxResult checkResult(boolean b) { + if (!b) { + return AjaxResult.error("服务器网络异常,提交失败"); + } + return AjaxResult.success(); + } + + + public List getFileInfos(String files) { + + List list = new ArrayList<>(); + if (StringUtils.isNull(files)) { + return list; + } + + if (StringUtils.isEmpty(files)) { + return list; + } + Long[] ids = Convert.toLongArray(files); + for (long id : ids) { + JXYFile file = ticketMapper.getFileInfoById(id); + if (StringUtils.isNotNull(file)) { + list.add(file); + } + } + + return list; + + } + + public String getStatus(int status) { + + return TicketStatusType.getStatusByCode(status); + } + + public String getType(int type) { + + return TicketType.getTypeByCode(type); + } + + public boolean checkUserPermissions(String email, long ticketId) { + + if (StringUtils.isNull(email)) { + return false; + } + + if (ticketId < 1) { + return false; + } + + TicketDto ticket = ticketMapper.getTicketById(ticketId); + + System.out.println("根据工单id:" + ticketId + "获取到数据" + ticket); + System.out.println("根据邮箱:" + email); + + if (StringUtils.isNull(ticket)) { + return false; + } + + if (!ticket.getEmail().equals(email)) { + return false; + } + + return true; + } + + public String deatilsToTable(String details){ + try { + LinkedHashMap> map = JSON.parseObject(details, new TypeReference>>(){}); + LinkedHashMap> ml = new LinkedHashMap<>(); + for (String machine: map.keySet()) { + LinkedHashMap dto = map.get(machine); + List list = new ArrayList<>(); + for (String reason : dto.keySet() ){ + QuotedDetailsVo qtv = new QuotedDetailsVo(); + qtv.setReason(reason); + qtv.setPrice(dto.get(reason)); + list.add(qtv); + } + ml.put(machine,list); + } + + if(ml.size() < 1){ + return details; + } + + //根据map 渲染表格 + StringBuilder detailSb = new StringBuilder(); + detailSb.append("
"); + detailSb.append(""); + //报价详情表头 + detailSb.append(""); + detailSb.append(""); + detailSb.append(""); + detailSb.append(""); + detailSb.append(""); + System.out.println("d1:"+detailSb.toString()); + final BigDecimal[] totalPrice = {BigDecimal.ZERO}; + for (String machine :ml.keySet()) { + List list = ml.get(machine); + for (int i=0 ; i "); + if(i == 0){ + detailSb.append(""); + } + QuotedDetailsVo dto = list.get(i); + detailSb.append(""); + detailSb.append(""); + detailSb.append(""); + totalPrice[0] = totalPrice[0].add(Convert.toBigDecimal(dto.getPrice(),BigDecimal.ZERO)); + System.out.println(totalPrice[0]); + + } + } + detailSb.append("
机器SN码维修项价格$
"+machine+""+dto.getReason()+""+dto.getPrice()+"

订单总应收款:"+totalPrice[0]+"

"); + return detailSb.toString(); + }catch (Exception e){ + return details; + } + } +} diff --git a/src/main/java/com/jxy/windminer/task/OrderTask.java b/src/main/java/com/jxy/windminer/task/OrderTask.java new file mode 100644 index 0000000..9d2db43 --- /dev/null +++ b/src/main/java/com/jxy/windminer/task/OrderTask.java @@ -0,0 +1,33 @@ +package com.jxy.windminer.task; + +import com.jxy.windminer.common.redis.service.RedisService; +import com.jxy.windminer.common.utils.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Configuration; +import org.springframework.scheduling.annotation.EnableScheduling; +import org.springframework.scheduling.annotation.Scheduled; + + +/** + * @Description 订单相关定时任务 + * @Date 2022/7/7 18:03 + * @Author 杜懿 + */ +@Configuration +@EnableScheduling +public class OrderTask { + + //@Autowired + //private HolderBalanceMapper holderBalanceMapper; + + @Autowired + private RedisService redisService; + + /** 订单支付 */ + @Scheduled(cron = "0 0/5 * * * ?") + public void HolderDataToRedis(){ + + } + + +} diff --git a/src/main/java/com/jxy/windminer/vo/AccountConfirmVo.java b/src/main/java/com/jxy/windminer/vo/AccountConfirmVo.java new file mode 100644 index 0000000..febbd6d --- /dev/null +++ b/src/main/java/com/jxy/windminer/vo/AccountConfirmVo.java @@ -0,0 +1,23 @@ +package com.jxy.windminer.vo; + +import lombok.Data; + +import java.math.BigDecimal; + +/** + * @Description 财务确认收款 + * @Date 2022/8/1 18:44 + * @Author 杜懿 + */ +@Data +public class AccountConfirmVo { + + private long id; + + /** 实收金额 */ + private double actual; + + /** 实收类型 */ + private String type; + +} diff --git a/src/main/java/com/jxy/windminer/vo/AddRepairRecordVo.java b/src/main/java/com/jxy/windminer/vo/AddRepairRecordVo.java new file mode 100644 index 0000000..f87a50f --- /dev/null +++ b/src/main/java/com/jxy/windminer/vo/AddRepairRecordVo.java @@ -0,0 +1,42 @@ +package com.jxy.windminer.vo; + +import lombok.Data; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.Size; + +/** + * 维修记录 + * + * @author dy + */ +@Data + +public class AddRepairRecordVo +{ + + /** 机器序列号 */ + @NotBlank(message = "机器序列号不能为空") + private String machineSerial; + + /** 算力板序列号 */ + @NotBlank(message = "算力板序列号不能为空") + private String hashBoardSerial; + + /** 故障现象 */ + @NotBlank(message = "故障现象不能为空") + private String fault; + + /** 维修结果 */ + @NotBlank(message = "维修结果不能为空") + private String repairResult; + + @NotBlank(message = "维修方法不能为空") + @Size(min=1, max=1000,message="维修方法最大提交1000个字符") + private String repairMethod; + + @NotBlank(message = "维修时间不能为空") + private String repairDate; + +} diff --git a/src/main/java/com/jxy/windminer/vo/AddressUsedVo.java b/src/main/java/com/jxy/windminer/vo/AddressUsedVo.java new file mode 100644 index 0000000..9ee0f30 --- /dev/null +++ b/src/main/java/com/jxy/windminer/vo/AddressUsedVo.java @@ -0,0 +1,28 @@ +package com.jxy.windminer.vo; + +import lombok.Data; + +/** + * @Description TODO + * @Date 2022/8/1 18:44 + * @Author 杜懿 + */ +@Data +public class AddressUsedVo { + + private String chain; + + /** 订单来源 */ + private String orderFrom; + + private int ifUse; + + private int type = 0; + + private String cond; + + private int limit = 20; + + private int page = 1; + +} diff --git a/src/main/java/com/jxy/windminer/vo/AddressVo.java b/src/main/java/com/jxy/windminer/vo/AddressVo.java new file mode 100644 index 0000000..dd54383 --- /dev/null +++ b/src/main/java/com/jxy/windminer/vo/AddressVo.java @@ -0,0 +1,21 @@ +package com.jxy.windminer.vo; + +import lombok.Data; + +/** + * @Description TODO + * @Date 2022/8/1 18:44 + * @Author 杜懿 + */ +@Data +public class AddressVo { + + private String chain; + + private String coin; + + private String email; + + private String key; + +} diff --git a/src/main/java/com/jxy/windminer/vo/AllTicketListVo.java b/src/main/java/com/jxy/windminer/vo/AllTicketListVo.java new file mode 100644 index 0000000..1b61e83 --- /dev/null +++ b/src/main/java/com/jxy/windminer/vo/AllTicketListVo.java @@ -0,0 +1,42 @@ +package com.jxy.windminer.vo; + +import lombok.Data; + +/** + * @Description 指标查询条件 + * @Date 2022/5/19 17:17 + * @Author 杜懿 + */ +@Data +public class AllTicketListVo { + + //private String id; + + private long id; + + private String email; + + private String cond; + + /** 工单类型 */ + private int type = 0; + + /** 创建时间 */ + private String start; + + private String end; + + /** 维修费用 */ + private double low; + + private double high; + + /** 状态 */ + private int status; + + /** 当前页数 */ + private int page=1; + + /** 每页数据条数 */ + private int limit=10; +} diff --git a/src/main/java/com/jxy/windminer/vo/BaseRepairVo.java b/src/main/java/com/jxy/windminer/vo/BaseRepairVo.java new file mode 100644 index 0000000..35f8bc5 --- /dev/null +++ b/src/main/java/com/jxy/windminer/vo/BaseRepairVo.java @@ -0,0 +1,17 @@ +package com.jxy.windminer.vo; + +import lombok.Data; + +/** + * @Description TODO + * @Date 2022/8/1 18:44 + * @Author 杜懿 + */ +@Data +public class BaseRepairVo { + + private long id; + + private String email; + +} diff --git a/src/main/java/com/jxy/windminer/vo/ChangeTypeVo.java b/src/main/java/com/jxy/windminer/vo/ChangeTypeVo.java new file mode 100644 index 0000000..495203c --- /dev/null +++ b/src/main/java/com/jxy/windminer/vo/ChangeTypeVo.java @@ -0,0 +1,18 @@ +package com.jxy.windminer.vo; + +import lombok.Data; + +/** + * @Description TODO + * @Date 2022/8/1 18:44 + * @Author 杜懿 + */ +@Data +public class ChangeTypeVo { + + private long id; + + private int type; + + private String email; +} diff --git a/src/main/java/com/jxy/windminer/vo/ConfirmListVo.java b/src/main/java/com/jxy/windminer/vo/ConfirmListVo.java new file mode 100644 index 0000000..5915a86 --- /dev/null +++ b/src/main/java/com/jxy/windminer/vo/ConfirmListVo.java @@ -0,0 +1,29 @@ +package com.jxy.windminer.vo; + +import lombok.Data; + +/** + * @Description TODO + * @Date 2022/8/1 18:44 + * @Author 杜懿 + */ +@Data +public class ConfirmListVo { + + /** 订单状态 已支付1 未支付0*/ + private String status; + + /** 辅助支付确认状态 已确认1 未确认2 */ + private String ifConfirm; + + /** 当前页数 */ + private int page =1; + + /** 每页数据条数 */ + private int limit = 20; + + private String start; + + private String end; + +} diff --git a/src/main/java/com/jxy/windminer/vo/ConfirmTypeVo.java b/src/main/java/com/jxy/windminer/vo/ConfirmTypeVo.java new file mode 100644 index 0000000..238917c --- /dev/null +++ b/src/main/java/com/jxy/windminer/vo/ConfirmTypeVo.java @@ -0,0 +1,19 @@ +package com.jxy.windminer.vo; + +import lombok.Data; + +/** + * @Description TODO + * @Date 2022/8/1 18:44 + * @Author 杜懿 + */ +@Data +public class ConfirmTypeVo { + + private long id; + + private int type; + + /** */ + private String respon; +} diff --git a/src/main/java/com/jxy/windminer/vo/ConfirmVo.java b/src/main/java/com/jxy/windminer/vo/ConfirmVo.java new file mode 100644 index 0000000..3b90bdf --- /dev/null +++ b/src/main/java/com/jxy/windminer/vo/ConfirmVo.java @@ -0,0 +1,25 @@ +package com.jxy.windminer.vo; + +import lombok.Data; + +import java.util.Date; + +/** + * @Description TODO + * @Date 2022/8/1 18:44 + * @Author 杜懿 + */ +@Data +public class ConfirmVo { + + /** 订单状态 已支付1 未支付0*/ + private String status; + + /** 辅助支付确认状态 已确认1 未确认2 */ + private String ifConfirm; + + private String start; + + private String end; + +} diff --git a/src/main/java/com/jxy/windminer/vo/EndTicketVo.java b/src/main/java/com/jxy/windminer/vo/EndTicketVo.java new file mode 100644 index 0000000..762386e --- /dev/null +++ b/src/main/java/com/jxy/windminer/vo/EndTicketVo.java @@ -0,0 +1,17 @@ +package com.jxy.windminer.vo; + +import lombok.Data; + +/** + * @Description TODO + * @Date 2022/8/1 18:44 + * @Author 杜懿 + */ +@Data +public class EndTicketVo { + + private long id; + + private int status; + +} diff --git a/src/main/java/com/jxy/windminer/vo/IdVo.java b/src/main/java/com/jxy/windminer/vo/IdVo.java new file mode 100644 index 0000000..4d630f0 --- /dev/null +++ b/src/main/java/com/jxy/windminer/vo/IdVo.java @@ -0,0 +1,17 @@ +package com.jxy.windminer.vo; + +import lombok.Data; + +/** + * @Description TODO + * @Date 2022/8/1 18:44 + * @Author 杜懿 + */ +@Data +public class IdVo { + + private long id; + + private String email; + +} diff --git a/src/main/java/com/jxy/windminer/vo/OrderApplyVo.java b/src/main/java/com/jxy/windminer/vo/OrderApplyVo.java new file mode 100644 index 0000000..01203b9 --- /dev/null +++ b/src/main/java/com/jxy/windminer/vo/OrderApplyVo.java @@ -0,0 +1,61 @@ +package com.jxy.windminer.vo; + +import com.alibaba.fastjson.annotation.JSONField; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; + +/** + * @Description 订单传参 + * @Date 2022/8/1 18:44 + * @Author 杜懿 + */ +@Data +public class OrderApplyVo { + + @JsonProperty("order_id") + @JSONField(name = "order_id") + private int orderId; + + @JsonProperty("order_key") + @JSONField(name = "order_key") + private String orderKey; + + private double amount; + + private String currency; + + private int count; + + @JsonProperty("billing_fname") + @JSONField(name = "billing_fname") + private String billingFname; + + @JsonProperty("billing_lname") + @JSONField(name = "billing_lname") + private String billingLname; + + @JsonProperty("billing_email") + @JSONField(name = "billing_email") + private String billingEmail; + + @JsonProperty("redirect_to") + @JSONField(name = "redirect_to") + private String redirectTo; + + @JsonProperty("cancel_url") + @JSONField(name = "cancel_url") + private String cancelUrl; + + @JsonProperty("view_url") + @JSONField(name = "view_url") + private String viewUrl; + + private String type; + + /** 回调地址 */ + @JsonProperty("notify_url") + @JSONField(name = "notify_url") + private String notifyUrl; + + private Object body; +} diff --git a/src/main/java/com/jxy/windminer/vo/OrderHandleVo.java b/src/main/java/com/jxy/windminer/vo/OrderHandleVo.java new file mode 100644 index 0000000..ee77792 --- /dev/null +++ b/src/main/java/com/jxy/windminer/vo/OrderHandleVo.java @@ -0,0 +1,41 @@ +package com.jxy.windminer.vo; + +import com.alibaba.fastjson.annotation.JSONField; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; + +import javax.validation.constraints.NotNull; +import java.util.Date; + +/** + * @Description TODO + * @Date 2022/8/1 18:44 + * @Author 杜懿 + */ +@Data +public class OrderHandleVo { + + private long id; + + private String address; + + @JsonProperty("order_key") + @JSONField(name = "order_key") + private String orderKey; + + private double amount; + + /** 支付的交易id由用户支付后提供 */ + private String txid; + + /** 币种 */ + private String coin= "USDT"; + + /** 支付网络 应支持ETH TRX */ + @NotNull + private String chain = "TRX"; + + @JSONField(format = "dd/MM/yyyy HH:mm:ss",name = "timestamp") + private Date date; + +} diff --git a/src/main/java/com/jxy/windminer/vo/OrderKeyVo.java b/src/main/java/com/jxy/windminer/vo/OrderKeyVo.java new file mode 100644 index 0000000..bd25044 --- /dev/null +++ b/src/main/java/com/jxy/windminer/vo/OrderKeyVo.java @@ -0,0 +1,19 @@ +package com.jxy.windminer.vo; + +import com.fasterxml.jackson.annotation.JsonAlias; +import lombok.Data; + +/** + * @Description TODO + * @Date 2022/8/1 18:44 + * @Author 杜懿 + */ +@Data +public class OrderKeyVo { + + @JsonAlias("key") + private String orderKey; + + private String orderFrom; + +} diff --git a/src/main/java/com/jxy/windminer/vo/OrderLogisticsConfirmVo.java b/src/main/java/com/jxy/windminer/vo/OrderLogisticsConfirmVo.java new file mode 100644 index 0000000..2689c15 --- /dev/null +++ b/src/main/java/com/jxy/windminer/vo/OrderLogisticsConfirmVo.java @@ -0,0 +1,19 @@ +package com.jxy.windminer.vo; + +import lombok.Data; + +/** + * @Description TODO + * @Date 2022/8/1 18:44 + * @Author 杜懿 + */ +@Data +public class OrderLogisticsConfirmVo { + + private String key; + + private String id; + + private String no; + +} diff --git a/src/main/java/com/jxy/windminer/vo/OrderUpdateVo.java b/src/main/java/com/jxy/windminer/vo/OrderUpdateVo.java new file mode 100644 index 0000000..0f9003e --- /dev/null +++ b/src/main/java/com/jxy/windminer/vo/OrderUpdateVo.java @@ -0,0 +1,27 @@ +package com.jxy.windminer.vo; + +import lombok.Data; + +/** + * @Description TODO + * @Date 2022/8/1 18:44 + * @Author 杜懿 + */ +@Data +public class OrderUpdateVo { + + private String transactionId; + + private String orderKey; + + private double amount; + + private String currency; + + private String chain; + + private String txids; + + private int status; + +} diff --git a/src/main/java/com/jxy/windminer/vo/PaymentInfoVo.java b/src/main/java/com/jxy/windminer/vo/PaymentInfoVo.java new file mode 100644 index 0000000..c2f3293 --- /dev/null +++ b/src/main/java/com/jxy/windminer/vo/PaymentInfoVo.java @@ -0,0 +1,33 @@ +package com.jxy.windminer.vo; + +import lombok.Data; + +/** + * @Description TODO + * @Date 2022/8/1 18:44 + * @Author 杜懿 + */ +@Data +public class PaymentInfoVo { + + private long ticketId; + + private String chain; + + private String coin; + + //地址 + private String balance; + + private double receivable; + + private double actual; + + private String txId; + + private int state; + + private String rejectReason; + + +} diff --git a/src/main/java/com/jxy/windminer/vo/PaymentVo.java b/src/main/java/com/jxy/windminer/vo/PaymentVo.java new file mode 100644 index 0000000..aa9cc76 --- /dev/null +++ b/src/main/java/com/jxy/windminer/vo/PaymentVo.java @@ -0,0 +1,19 @@ +package com.jxy.windminer.vo; + +import lombok.Data; + +/** + * @Description TODO + * @Date 2022/8/1 18:44 + * @Author 杜懿 + */ +@Data +public class PaymentVo { + + private int id; + + private String chain; + + private String txId; + +} diff --git a/src/main/java/com/jxy/windminer/vo/PrivateTicketListVo.java b/src/main/java/com/jxy/windminer/vo/PrivateTicketListVo.java new file mode 100644 index 0000000..d40c32d --- /dev/null +++ b/src/main/java/com/jxy/windminer/vo/PrivateTicketListVo.java @@ -0,0 +1,29 @@ +package com.jxy.windminer.vo; + +import lombok.Data; + +/** + * @Description TODO + * @Date 2022/8/1 18:44 + * @Author 杜懿 + */ +@Data +public class PrivateTicketListVo { + + private int status; + + /** 工单号 */ + private long id; + + //查询条件 可以工单号/机器编号 + private String cond; + + /** 机器编号 */ + private String serialNo; + + private String start; + + private String end; + + +} diff --git a/src/main/java/com/jxy/windminer/vo/QueryVo.java b/src/main/java/com/jxy/windminer/vo/QueryVo.java new file mode 100644 index 0000000..01885fa --- /dev/null +++ b/src/main/java/com/jxy/windminer/vo/QueryVo.java @@ -0,0 +1,24 @@ +package com.jxy.windminer.vo; + +import lombok.Data; + +/** + * @Description 接口请求基础信息 + * @Date 2022/5/27 10:52 + * @Author 杜懿 + */ +@Data +public class QueryVo { + + /** 起始时间 */ + private String start; + + /** 截止时间 */ + private String end; + + /** 当前页数 */ + private int pageNum; + + /** 每页数据条数 */ + private int pageSize; +} diff --git a/src/main/java/com/jxy/windminer/vo/QuotedDetailsVo.java b/src/main/java/com/jxy/windminer/vo/QuotedDetailsVo.java new file mode 100644 index 0000000..f5ae076 --- /dev/null +++ b/src/main/java/com/jxy/windminer/vo/QuotedDetailsVo.java @@ -0,0 +1,20 @@ +package com.jxy.windminer.vo; + +import lombok.Data; + +import java.math.BigDecimal; + +/** + * @Description 报价详情 + * @Date 2022/5/27 10:52 + * @Author 杜懿 + */ +@Data +public class QuotedDetailsVo { + + /** 当前页数 */ + private String reason; + + /** 每页数据条数 */ + private String price; +} diff --git a/src/main/java/com/jxy/windminer/vo/QuotedVo.java b/src/main/java/com/jxy/windminer/vo/QuotedVo.java new file mode 100644 index 0000000..0c14e08 --- /dev/null +++ b/src/main/java/com/jxy/windminer/vo/QuotedVo.java @@ -0,0 +1,24 @@ +package com.jxy.windminer.vo; + +import lombok.Data; + +import java.util.List; +import java.util.Map; + +/** + * @Description 接口请求基础信息 + * @Date 2022/5/27 10:52 + * @Author 杜懿 + */ +@Data +public class QuotedVo { + + /** 工单id */ + private int id; + + /** 工单total */ + private String total; + + /** 报价详情 */ + private List details; +} diff --git a/src/main/java/com/jxy/windminer/vo/QuothMachinesVo.java b/src/main/java/com/jxy/windminer/vo/QuothMachinesVo.java new file mode 100644 index 0000000..58080c9 --- /dev/null +++ b/src/main/java/com/jxy/windminer/vo/QuothMachinesVo.java @@ -0,0 +1,20 @@ +package com.jxy.windminer.vo; + +import lombok.Data; + +import java.util.List; + +/** + * @Description 报价详情 + * @Date 2022/5/27 10:52 + * @Author 杜懿 + */ +@Data +public class QuothMachinesVo { + + /** 当前页数 */ + private String machine; + + /** 每页数据条数 */ + private List items; +} diff --git a/src/main/java/com/jxy/windminer/vo/RepairChangeStatusVo.java b/src/main/java/com/jxy/windminer/vo/RepairChangeStatusVo.java new file mode 100644 index 0000000..7d3c779 --- /dev/null +++ b/src/main/java/com/jxy/windminer/vo/RepairChangeStatusVo.java @@ -0,0 +1,19 @@ +package com.jxy.windminer.vo; + +import lombok.Data; + +/** + * @Description TODO + * @Date 2022/8/1 18:44 + * @Author 杜懿 + */ +@Data +public class RepairChangeStatusVo { + + private long id; + + private int status; + + private String process; + +} diff --git a/src/main/java/com/jxy/windminer/vo/RepairConfirmVo.java b/src/main/java/com/jxy/windminer/vo/RepairConfirmVo.java new file mode 100644 index 0000000..052ceec --- /dev/null +++ b/src/main/java/com/jxy/windminer/vo/RepairConfirmVo.java @@ -0,0 +1,23 @@ +package com.jxy.windminer.vo; + +import lombok.Data; + +/** + * @Description TODO + * @Date 2022/8/1 18:44 + * @Author 杜懿 + */ +@Data +public class RepairConfirmVo { + + private long id; + + private String name; + + private String address; + + private String phone; + + private String remark; + +} diff --git a/src/main/java/com/jxy/windminer/vo/RepairRecordVo.java b/src/main/java/com/jxy/windminer/vo/RepairRecordVo.java new file mode 100644 index 0000000..8c6aae3 --- /dev/null +++ b/src/main/java/com/jxy/windminer/vo/RepairRecordVo.java @@ -0,0 +1,36 @@ +package com.jxy.windminer.vo; + +import lombok.Data; + +import java.util.Date; + +/** + * 维修记录 + * + * @author dy + */ +@Data +public class RepairRecordVo +{ + private long id; + + /** 机器序列号 */ + private String machineSerial; + + /** 算力板序列号 */ + private String hashBoardSerial; + + /** 故障现象 */ + private String fault; + + /** 维修结果 */ + private String repairResult; + + private String start; + + private String end; + + private int page = 1 ; + + private int limit = 30; +} diff --git a/src/main/java/com/jxy/windminer/vo/RepairRefuseVo.java b/src/main/java/com/jxy/windminer/vo/RepairRefuseVo.java new file mode 100644 index 0000000..8632644 --- /dev/null +++ b/src/main/java/com/jxy/windminer/vo/RepairRefuseVo.java @@ -0,0 +1,18 @@ +package com.jxy.windminer.vo; + +import lombok.Data; + +/** + * @Description TODO + * @Date 2022/8/1 18:44 + * @Author 杜懿 + */ +@Data +public class RepairRefuseVo { + + private long id; + + private String reason; + + +} diff --git a/src/main/java/com/jxy/windminer/vo/ResponTicketVo.java b/src/main/java/com/jxy/windminer/vo/ResponTicketVo.java new file mode 100644 index 0000000..608750e --- /dev/null +++ b/src/main/java/com/jxy/windminer/vo/ResponTicketVo.java @@ -0,0 +1,27 @@ +package com.jxy.windminer.vo; + +import lombok.Data; + +/** + * @Description + * @Date 2022/5/19 17:17 + * @Author 杜懿 + */ +@Data +public class ResponTicketVo { + + /** 工单id */ + private long id; + + /** 回复内容 */ + private String respon; + + /** 附件编号 逗号分隔*/ + private String files; + + private String videoFiles; + + private String imageFiles; + + private String audioFiles; +} diff --git a/src/main/java/com/jxy/windminer/vo/ResubmitTicketVo.java b/src/main/java/com/jxy/windminer/vo/ResubmitTicketVo.java new file mode 100644 index 0000000..40dc5b1 --- /dev/null +++ b/src/main/java/com/jxy/windminer/vo/ResubmitTicketVo.java @@ -0,0 +1,27 @@ +package com.jxy.windminer.vo; + +import lombok.Data; + +/** + * @Description 指标查询条件 + * @Date 2022/5/19 17:17 + * @Author 杜懿 + */ +@Data +public class ResubmitTicketVo { + + /** 工单id */ + private long id; + + /** 补充内容 */ + private String desc; + + private String files; + + private String videoFiles; + + private String imageFiles; + + private String audioFiles; + +} diff --git a/src/main/java/com/jxy/windminer/vo/ReturnLogisticalVo.java b/src/main/java/com/jxy/windminer/vo/ReturnLogisticalVo.java new file mode 100644 index 0000000..122e8e7 --- /dev/null +++ b/src/main/java/com/jxy/windminer/vo/ReturnLogisticalVo.java @@ -0,0 +1,32 @@ +package com.jxy.windminer.vo; + +import lombok.Data; + +/** + * @Description 售后维修寄回寄件信息 + * @Date 2022/5/19 17:17 + * @Author 杜懿 + */ +@Data +public class ReturnLogisticalVo { + + /** 工单id */ + private long id; + + /** 物流公司 */ + private String company; + + /** 物流单号 */ + private String no; + + private String sendTime; + + private String name; + + private String address; + + private String phone; + + private String notes; + +} diff --git a/src/main/java/com/jxy/windminer/vo/SerialVo.java b/src/main/java/com/jxy/windminer/vo/SerialVo.java new file mode 100644 index 0000000..cbf8579 --- /dev/null +++ b/src/main/java/com/jxy/windminer/vo/SerialVo.java @@ -0,0 +1,19 @@ +package com.jxy.windminer.vo; + +import lombok.Data; + +/** + * @Description 指标查询条件 + * @Date 2022/5/19 17:17 + * @Author 杜懿 + */ +@Data +public class SerialVo { + + /** 机器编码 */ + private String serialNo; + + /** 编码类型 ma机器编码 ac配件编码 */ + private String type; + +} diff --git a/src/main/java/com/jxy/windminer/vo/StatusVo.java b/src/main/java/com/jxy/windminer/vo/StatusVo.java new file mode 100644 index 0000000..6785868 --- /dev/null +++ b/src/main/java/com/jxy/windminer/vo/StatusVo.java @@ -0,0 +1,25 @@ +package com.jxy.windminer.vo; + +import lombok.Data; + +/** + * @Description TODO + * @Date 2022/8/1 18:44 + * @Author 杜懿 + */ +@Data +public class StatusVo{ + + private int id; + + private int status; + + private String email; + + /** 当前页数 */ + private int page =1; + + /** 每页数据条数 */ + private int limit = 10; + +} diff --git a/src/main/java/com/jxy/windminer/vo/SysToolsVo.java b/src/main/java/com/jxy/windminer/vo/SysToolsVo.java new file mode 100644 index 0000000..162a40e --- /dev/null +++ b/src/main/java/com/jxy/windminer/vo/SysToolsVo.java @@ -0,0 +1,26 @@ +package com.jxy.windminer.vo; + +import lombok.Data; + +/** + * @Description TODO + * @Date 2022/8/1 18:44 + * @Author 杜懿 + */ +@Data +public class SysToolsVo { + + //todo 其他查询条件 + private String operName; + + private String start; + + private String end; + + /** 当前页数 */ + private int page =1; + + /** 每页数据条数 */ + private int limit = 30; + +} diff --git a/src/main/java/com/jxy/windminer/vo/TicketVo.java b/src/main/java/com/jxy/windminer/vo/TicketVo.java new file mode 100644 index 0000000..75b45cf --- /dev/null +++ b/src/main/java/com/jxy/windminer/vo/TicketVo.java @@ -0,0 +1,52 @@ +package com.jxy.windminer.vo; + +import com.jxy.windminer.common.xss.Xss; +import lombok.Data; + +import java.util.Date; + +/** + * @Description 指标查询条件 + * @Date 2022/5/19 17:17 + * @Author 杜懿 + */ +@Data +public class TicketVo { + + @Xss(message = "登录账号不能包含脚本字符") + private String userName; + + @Xss(message = "邮箱不能包含脚本字符") + private String email; + + /** 工单类型 */ + private int type = 0; + + /** 机器型号 */ + @Xss(message = "机器型号不能包含脚本字符") + private String model; + + /** 机器编码 */ + @Xss(message = "机器SN码不能包含脚本字符") + private String serialNo; + + //购买日期 + @Xss(message = "日期不能包含脚本字符") + private String buyDate; + + //经销商 + @Xss(message = "经销商不能包含脚本字符") + private String distributor; + + //故障现象 对应id + @Xss(message = "故障现象不能包含脚本字符") + private String fault; + + /** 故障描述 */ + @Xss(message = "故障描述不能包含脚本字符") + private String desc; + + /** 附件标号 逗号分隔*/ + private String files; + +} diff --git a/src/main/java/com/jxy/windminer/vo/TotalAddressVo.java b/src/main/java/com/jxy/windminer/vo/TotalAddressVo.java new file mode 100644 index 0000000..8ad34bd --- /dev/null +++ b/src/main/java/com/jxy/windminer/vo/TotalAddressVo.java @@ -0,0 +1,23 @@ +package com.jxy.windminer.vo; + +import lombok.Data; + +/** + * @Description TODO + * @Date 2022/8/1 18:44 + * @Author 杜懿 + */ +@Data +public class TotalAddressVo { + + private String chain; + + private String status; + + private String address; + + private int limit = 20; + + private int page = 1; + +} diff --git a/src/main/java/com/jxy/windminer/vo/UserLogisticalVo.java b/src/main/java/com/jxy/windminer/vo/UserLogisticalVo.java new file mode 100644 index 0000000..5dd11a7 --- /dev/null +++ b/src/main/java/com/jxy/windminer/vo/UserLogisticalVo.java @@ -0,0 +1,56 @@ +package com.jxy.windminer.vo; + +import com.jxy.windminer.common.xss.Xss; +import lombok.Data; + +/** + * @Description 用户维修寄件信息 + * @Date 2022/5/19 17:17 + * @Author 杜懿 + */ +@Data +public class UserLogisticalVo { + + /** 工单id */ + private int ticketId; + + /** 物流公司 */ + @Xss(message = "物流公司包含脚本字符") + private String company; + + /** 物流单号 */ + @Xss(message = "物流单号包含脚本字符") + private String no; + + @Xss(message = "物流日期包含脚本字符") + private String sendTime; + + /** 发件人 */ + @Xss(message = "发件人姓名包含脚本字符!") + private String sendName; + + /** 发件地址 */ + @Xss(message = "发件地址包含脚本字符!") + private String sendAddress; + + /** 发件人联系方式 */ + @Xss(message = "发件人联系方式包含脚本字符!") + private String sendPhone; + + /** 维修后寄回收件人 */ + @Xss(message = "手件人姓名包含脚本字符!") + private String receiveName; + + /** 维修后寄回收件地址 */ + @Xss(message = "收件人姓名包含脚本字符!") + private String receiveAddress; + + /** 维修后寄回收件人联系方式 */ + @Xss(message = "收件人姓名包含脚本字符!") + private String receivePhone; + + /** 备注 */ + @Xss(message = "备注包含脚本字符!") + private String notes; + +} diff --git a/src/main/java/com/jxy/windminer/vo/responSupportVo.java b/src/main/java/com/jxy/windminer/vo/responSupportVo.java new file mode 100644 index 0000000..aafa7cb --- /dev/null +++ b/src/main/java/com/jxy/windminer/vo/responSupportVo.java @@ -0,0 +1,17 @@ +package com.jxy.windminer.vo; + +import lombok.Data; + +/** + * @Description TODO + * @Date 2022/8/1 18:44 + * @Author 杜懿 + */ +@Data +public class responSupportVo { + + private long id; + + private String email; + +} diff --git a/src/main/resources/META-INF/MANIFEST.MF b/src/main/resources/META-INF/MANIFEST.MF new file mode 100644 index 0000000..73d8b2e --- /dev/null +++ b/src/main/resources/META-INF/MANIFEST.MF @@ -0,0 +1,3 @@ +Manifest-Version: 1.0 +Main-Class: com.jxy.windminer.WindminerApplication + diff --git a/src/main/resources/META-INF/spring.factories b/src/main/resources/META-INF/spring.factories new file mode 100644 index 0000000..70a002b --- /dev/null +++ b/src/main/resources/META-INF/spring.factories @@ -0,0 +1,10 @@ +org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ + com.jxy.windminer.common.security.config.WebMvcConfig,\ + com.jxy.windminer.common.security.service.TokenService,\ + com.jxy.windminer.common.utils.SpringUtils,\ + com.jxy.windminer.common.datascope.aspect.DataScopeAspect,\ + com.jxy.windminer.common.redis.config.RedisConfig,\ + com.jxy.windminer.common.redis.service.RedisService,\ + com.jxy.windminer.common.security.aspect.PreAuthAspect,\ + com.jxy.windminer.common.security.aspect.RateLimiterAspect,\ + com.jxy.windminer.common.security.handler.GlobalExceptionHandler diff --git a/src/main/resources/application-test.properties b/src/main/resources/application-test.properties new file mode 100644 index 0000000..8e49140 --- /dev/null +++ b/src/main/resources/application-test.properties @@ -0,0 +1,53 @@ +server.port=9808 + +spring.redis.host=localhost +spring.redis.port=6379 +spring.redis.password=jxy123456 + +spring.main.allow-circular-references=true +spring.main.allow-bean-definition-overriding=true + +server.compression.enabled=true +server.compression.mime-types=application/json +spring.cloud.gateway.globalcors.corsConfigurations.[/**].allowedOriginPatterns=* +spring.cloud.gateway.globalcors.corsConfigurations.[/**].allowed-methods=* +spring.cloud.gateway.globalcors.corsConfigurations.[/**].allowed-headers=* +spring.cloud.gateway.globalcors.corsConfigurations.[/**].allow-credentials=true +spring.cloud.gateway.globalcors.corsConfigurations.[/**].exposedHeaders=Content-Disposition,Content-Type,Cache-Control +spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver +spring.datasource.url=jdbc:mysql://127.0.0.1:3306/miner?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&useTimezone=true +spring.datasource.username=root +spring.datasource.password=duyi123456 + +mybatis-plus.typeAliasesPackage=com.jxy.windminer +mybatis-plus.mapperLocations=classpath:mapper/**/*.xml + +spring.mail.host=smtp.qq.com +spring.mail.port=465 +spring.mail.username=1328642438@qq.com +spring.mail.password=eqrzqxeqzlshhedh +spring.mail.protocol=smtps +spring.mail.properties.mail.smtp.ssl.enable=true +spring.mail.properties.mail.smtp.auth=true +# STARTTLS 是对纯文本通信协议的扩展。它提供一种方式将纯文本连接升级为加密连接(TLS或SSL),而不是另外使用一个端口作加密通信。 +spring.mail.properties.mail.smtp.starttls.enable=true +spring.mail.properties.mail.smtp.starttls.required=true +spring.mail.default-encoding=UTF-8 + +logging.level.com.jxy.windminer.mapper:debug + +spring.task.execution.pool.core-size=10 +spring.task.execution.pool.max-size=20 +spring.task.execution.pool.queue-capacity=1000 + +spring.servlet.multipart.max-file-size= 20MB +spring.servlet.multipart.max-request-size= 60MB + +my.domain = http://10.168.2.125 +my.env = test + +swagger.title=接口文档 +swagger.license=Powered By jxy +swagger.licenseUrl=https://localhost:8080/swagger_ui.html +swagger.servlet.multipart.max-file-size=2MB +swagger.servlet.multipart.max-request-size=8MB diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties new file mode 100644 index 0000000..046b389 --- /dev/null +++ b/src/main/resources/application.properties @@ -0,0 +1,54 @@ +server.port=9808 + +spring.redis.host=localhost +spring.redis.port=6379 +spring.redis.password=twm123456 + +spring.main.allow-circular-references=true +spring.main.allow-bean-definition-overriding=true + +server.compression.enabled=true +server.compression.mime-types=application/json +spring.cloud.gateway.globalcors.corsConfigurations.[/**].allowedOriginPatterns=* +spring.cloud.gateway.globalcors.corsConfigurations.[/**].allowed-methods=* +spring.cloud.gateway.globalcors.corsConfigurations.[/**].allowed-headers=* +spring.cloud.gateway.globalcors.corsConfigurations.[/**].allow-credentials=true +spring.cloud.gateway.globalcors.corsConfigurations.[/**].exposedHeaders=Content-Disposition,Content-Type,Cache-Control +spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver +spring.datasource.url=jdbc:mysql://127.0.0.1:3306/miner?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&useTimezone=true +spring.datasource.username=root +spring.datasource.password=FdIW6HAPcbJPVTYE + + +mybatis-plus.typeAliasesPackage=com.jxy.windminer +mybatis-plus.mapperLocations=classpath:mapper/**/*.xml + +spring.mail.host=mail.privateemail.com +spring.mail.port=465 +spring.mail.username=support@thewindminer.com +spring.mail.password=ns202201! +spring.mail.protocol=smtps +spring.mail.properties.mail.smtp.ssl.enable=true +spring.mail.properties.mail.smtp.auth=true +## STARTTLS 是对纯文本通信协议的扩展。它提供一种方式将纯文本连接升级为加密连接(TLS或SSL),而不是另外使用一个端口作加密通信。 +spring.mail.properties.mail.smtp.starttls.enable=true +spring.mail.properties.mail.smtp.starttls.required=true +spring.mail.default-encoding=UTF-8 + +logging.level.com.jxy.windminer.mapper:debug + +spring.task.execution.pool.core-size=10 +spring.task.execution.pool.max-size=20 +spring.task.execution.pool.queue-capacity=1000 + +spring.servlet.multipart.max-file-size= 20MB +spring.servlet.multipart.max-request-size= 60MB + +my.domain = https://www.thewindminer.com +my.env = prd + +swagger.title=接口文档 +swagger.license=Powered By jxy +swagger.licenseUrl=https://localhost:8080/swagger_ui.html +swagger.servlet.multipart.max-file-size=2MB +swagger.servlet.multipart.max-request-size=8MB diff --git a/src/main/resources/mapper/windminer/OrderMapper.xml b/src/main/resources/mapper/windminer/OrderMapper.xml new file mode 100644 index 0000000..49bd553 --- /dev/null +++ b/src/main/resources/mapper/windminer/OrderMapper.xml @@ -0,0 +1,332 @@ + + + + + + replace into order_address( + + eth_address, + + + trx_address, + + order_key + ) values ( + + #{dto.ethAddress}, + + + #{dto.trxAddress}, + + #{dto.orderKey} + ) + + + + insert into `order`( + transaction_id, + order_id, + order_key, + amount, + currency, + billing_fname, + billing_lname, + email, + redirect_to, + cancel_url, + view_url, + notify_url, + order_from, + `count`, + create_time + ) values ( + #{id}, + #{vo.orderId}, + #{vo.orderKey}, + #{vo.amount}, + #{vo.currency}, + #{vo.billingFname}, + #{vo.billingLname}, + #{vo.billingEmail}, + #{vo.redirectTo}, + #{vo.cancelUrl}, + #{vo.viewUrl}, + #{vo.notifyUrl}, + #{orderFrom}, + #{count}, + sysdate() + ) + + + + insert into + order_logistical(order_key,company,tracking_number) values + (#{key},#{company},#{number}) + + + + update + wallet + set + `state` = 1, + order_key =#{key} + where address = #{address} and `chain` = #{chain} and `state` = 0 + + + + update + `order` + set + confirm_rcv_amnt = #{vo.amount}, + confirm_rcv_amnt_curr = #{vo.currency}, + + status = #{vo.status}, + + + txid = #{vo.txids}, + + `chain` = #{vo.chain}, + finish_time = sysdate() + where + order_key = #{vo.orderKey} + + + + update `order` set if_send = #{code} where order_key = #{key} + + + + update wordpress.wp_wc_orders set status='wc-completed' where id=#{orderId} + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/mapper/windminer/SysFileMapper.xml b/src/main/resources/mapper/windminer/SysFileMapper.xml new file mode 100644 index 0000000..513596e --- /dev/null +++ b/src/main/resources/mapper/windminer/SysFileMapper.xml @@ -0,0 +1,137 @@ + + + + + delete from tools_file where id = #{id} + + + + + + insert into repair_record( + machine_serial, + hash_board_serial, + fault, + repair_result, + repair_method, + repair_date, + oper_name, + import_date) value ( + #{vo.machineSerial}, + #{vo.hashBoardSerial}, + #{vo.fault}, + #{vo.repairResult}, + #{vo.repairMethod}, + #{vo.repairDate}, + #{vo.operName}, + sysdate() + ) + + + + insert into `tools_file`( + url, + file_name, + user_name, + version, + `size`, + md5sum, + create_time + ) values( + #{vo.url}, + #{vo.fileName}, + #{vo.userName}, + #{ver}, + #{vo.size}, + #{vo.md5sum}, + sysdate() + ) + + + + + + + + + + + + diff --git a/src/main/resources/mapper/windminer/SysLogininforMapper.xml b/src/main/resources/mapper/windminer/SysLogininforMapper.xml new file mode 100644 index 0000000..7181aef --- /dev/null +++ b/src/main/resources/mapper/windminer/SysLogininforMapper.xml @@ -0,0 +1,53 @@ + + + + + + + + + + + + + + insert into sys_logininfor (user_name, status, ipaddr, msg, access_time) + values (#{userName}, #{status}, #{ipaddr}, #{msg}, sysdate()) + + + + + + delete from sys_logininfor where info_id in + + #{infoId} + + + + + truncate table sys_logininfor + + + diff --git a/src/main/resources/mapper/windminer/SysMenuMapper.xml b/src/main/resources/mapper/windminer/SysMenuMapper.xml new file mode 100644 index 0000000..2451fbd --- /dev/null +++ b/src/main/resources/mapper/windminer/SysMenuMapper.xml @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/mapper/windminer/SysOperLogMapper.xml b/src/main/resources/mapper/windminer/SysOperLogMapper.xml new file mode 100644 index 0000000..33fa204 --- /dev/null +++ b/src/main/resources/mapper/windminer/SysOperLogMapper.xml @@ -0,0 +1,82 @@ + + + + + + + + + + + + + + + + + + + + + + + + select oper_id, title, business_type, method, request_method, operator_type, oper_name, dept_name, oper_url, oper_ip, oper_param, json_result, status, error_msg, oper_time + from sys_oper_log + + + + insert into sys_oper_log(title, business_type, method, request_method, operator_type, oper_name, dept_name, oper_url, oper_ip, oper_param, json_result, status, error_msg, oper_time) + values (#{title}, #{businessType}, #{method}, #{requestMethod}, #{operatorType}, #{operName}, #{deptName}, #{operUrl}, #{operIp}, #{operParam}, #{jsonResult}, #{status}, #{errorMsg}, sysdate()) + + + + + + delete from sys_oper_log where oper_id in + + #{operId} + + + + + + + truncate table sys_oper_log + + + diff --git a/src/main/resources/mapper/windminer/SysRoleMapper.xml b/src/main/resources/mapper/windminer/SysRoleMapper.xml new file mode 100644 index 0000000..baff222 --- /dev/null +++ b/src/main/resources/mapper/windminer/SysRoleMapper.xml @@ -0,0 +1,85 @@ + + + + + + + + + + + + + + + + + + + + + + + select distinct r.role_id, r.role_name, r.role_key, r.role_sort, r.data_scope, r.menu_check_strictly, r.dept_check_strictly, + r.status, r.del_flag, r.create_time, r.remark + from sys_role r + left join sys_user_role ur on ur.role_id = r.role_id + left join sys_user u on u.user_id = ur.user_id + left join sys_dept d on u.dept_id = d.dept_id + + + + + + + + + + + + + + + diff --git a/src/main/resources/mapper/windminer/SysRoleMenuMapper.xml b/src/main/resources/mapper/windminer/SysRoleMenuMapper.xml new file mode 100644 index 0000000..cd74161 --- /dev/null +++ b/src/main/resources/mapper/windminer/SysRoleMenuMapper.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + + + delete from sys_role_menu where role_id=#{roleId} + + + + delete from sys_role_menu where role_id in + + #{roleId} + + + + + insert into sys_role_menu(role_id, menu_id) values + + (#{item.roleId},#{item.menuId}) + + + + diff --git a/src/main/resources/mapper/windminer/SysUserMapper.xml b/src/main/resources/mapper/windminer/SysUserMapper.xml new file mode 100644 index 0000000..5775557 --- /dev/null +++ b/src/main/resources/mapper/windminer/SysUserMapper.xml @@ -0,0 +1,192 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + select u.user_id, u.dept_id, u.user_name, u.nick_name, u.email, u.avatar, u.phonenumber, u.password, u.sex, u.status, u.del_flag, u.login_ip, u.login_date, u.create_by, u.create_time, u.remark,u.login_count,u.create_from, + d.dept_id, d.parent_id, d.ancestors, d.dept_name, d.order_num, d.leader, d.status as dept_status, + r.role_id, r.role_name, r.role_key, r.role_sort, r.data_scope, r.status as role_status + from sys_user u + left join sys_dept d on u.dept_id = d.dept_id + left join sys_user_role ur on u.user_id = ur.user_id + left join sys_role r on r.role_id = ur.role_id + + + + + + + + + + + + + + + + + + + + + + + insert into sys_user( + user_id, + dept_id, + user_name, + nick_name, + email, + avatar, + phonenumber, + sex, + password, + status, + create_by, + remark, + create_from, + create_time + )values( + #{userId}, + #{deptId}, + #{userName}, + #{nickName}, + #{email}, + #{avatar}, + #{phone}, + #{sex}, + #{password}, + #{status}, + #{createBy}, + #{remark}, + #{createFrom}, + sysdate() + ) + + + + update sys_user + + dept_id = #{deptId}, + user_name = #{userName}, + nick_name = #{nickName}, + email = #{email}, + phonenumber = #{phone}, + sex = #{sex}, + avatar = #{avatar}, + password = #{password}, + status = #{status}, + login_ip = #{loginIp}, + login_date = #{loginDate}, + update_by = #{updateBy}, + remark = #{remark}, + login_count = #{loginCount}, + update_time = sysdate() + + where user_id = #{userId} + + + + update sys_user set password = #{password} where user_name = #{userName} + + + + update sys_user set password = #{password} where email = #{email} + + + diff --git a/src/main/resources/mapper/windminer/SysUserRoleMapper.xml b/src/main/resources/mapper/windminer/SysUserRoleMapper.xml new file mode 100644 index 0000000..392bdd8 --- /dev/null +++ b/src/main/resources/mapper/windminer/SysUserRoleMapper.xml @@ -0,0 +1,49 @@ + + + + + + + + + + + delete from sys_user_role where user_id=#{userId} + + + + + + delete from sys_user_role where user_id in + + #{userId} + + + + + insert into sys_user_role(user_id, role_id) values + + (#{item.userId},#{item.roleId}) + + + + + insert into sys_user_role(user_id, role_id) values(#{userId},#{roleId}) + + + + + delete from sys_user_role where user_id=#{userId} and role_id=#{roleId} + + + + delete from sys_user_role where role_id=#{roleId} and user_id in + + #{userId} + + + diff --git a/src/main/resources/mapper/windminer/TicketMapper.xml b/src/main/resources/mapper/windminer/TicketMapper.xml new file mode 100644 index 0000000..facdff4 --- /dev/null +++ b/src/main/resources/mapper/windminer/TicketMapper.xml @@ -0,0 +1,702 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + UPDATE ticket SET status=#{status},update_time =sysdate() WHERE id = #{id} + + + + UPDATE ticket SET status=#{status},`type`=#{type},update_time =sysdate() WHERE id = #{id} + + + + + update repair_logistical_info + + send_name = #{vo.sendName}, + send_address = #{vo.sendAddress}, + send_phone = #{vo.sendPhone}, + send_time = #{vo.sendTime}, + company = #{vo.company}, + `no` = #{vo.no}, + `notes` = #{vo.notes}, + + where ticket_id = #{vo.ticketId} + + + + update return_logistical_info + + receive_name = #{vo.receiveName}, + receive_address = #{vo.receiveAddress}, + receive_phone = #{vo.receivePhone}, + + where ticket_id = #{vo.ticketId} + + + + update return_logistical_info + + send_name = #{vo.name}, + send_address = #{vo.address}, + send_phone = #{vo.phone}, + company = #{vo.company}, + `no` = #{vo.no}, + send_time = #{vo.sendTime}, + `notes` = #{vo.notes}, + + where ticket_id = #{vo.id} + + + + UPDATE ticket SET `type`=#{type},if_change = 1,status=1,update_time=sysdate() WHERE id = #{id} + + + + update ticket + + update_time=sysdate() + ,status = #{vo.status} + ,process = #{vo.process} + + where id = #{vo.id} + + + + update payment_info + + `chain` = #{vo.chain}, + receive_balance = #{vo.balance}, + amount_receivable_USDT = #{vo.receivable}, + amount_actual_USDT = #{vo.actual}, + transaction_hash = #{vo.txId}, + `payment_state` = #{vo.state}, + `reject_reason` = #{vo.rejectReason}, + `coin` = #{vo.coin}, + + where ticket_id = #{vo.ticketId} + + + + UPDATE ticket SET update_time=sysdate() WHERE id = #{id} + + + + + insert into ticket( + email, + user_name, + model, + serial_no, + buy_date, + distributor, + fault, + `desc`, + status, + `type` , + create_time,update_time + )values( + #{data.email}, + #{data.userName}, + #{data.model}, + #{data.serialNo}, + #{data.buyDate}, + #{data.distributor}, + #{data.fault}, + #{data.desc}, + 1, + #{data.type}, + sysdate(),sysdate() + ) + + + + insert into ticket_supplement( + ticket_id, + content, + files, + video_file, + image_file, + audio_file, + create_time + ) values ( + #{vo.id}, + #{vo.desc}, + #{vo.files}, + #{vo.videoFiles}, + #{vo.imageFiles}, + #{vo.audioFiles}, + sysdate() + ) + + + + insert into respon( + ticket_id, + respon, + responer, + files, + video_file, + image_file, + audio_file, + create_time + ) values( + #{vo.id}, + #{vo.respon}, + #{name}, + #{vo.files}, + #{vo.videoFiles}, + #{vo.imageFiles}, + #{vo.audioFiles}, + sysdate() + ) + + + + insert into `file`(url,file_name,user_name,`type`,create_time) values( + #{vo.url},#{vo.fileName},#{vo.userName},#{vo.fileType},sysdate() + ) + + + + insert into repair_logistical_info( + ticket_id, + receive_name, + receive_address, + `notes`, + receive_phone + ) values( + #{vo.id}, + #{vo.name}, + #{vo.address}, + #{vo.remark}, + #{vo.phone} + ) + + + + insert into return_logistical_info( + ticket_id, + send_name, + send_address, + `notes`, + send_phone + ) values( + #{vo.id}, + #{vo.name}, + #{vo.address}, + #{vo.remark}, + #{vo.phone} + ) + + + + insert into payment_info( + amount_receivable_USDT, + ticket_id, + details, + order_key + ) values( + #{vo.total}, + #{vo.id}, + #{details}, + #{orderKey} + ) + ON duplicate KEY UPDATE amount_receivable_USDT = #{vo.total} + + +