From 54e88191b3cb0d250ee3a3cfce65523b6f29f28c Mon Sep 17 00:00:00 2001 From: 谢茂盛 <A807732> Date: Tue, 2 Apr 2024 14:17:24 +0800 Subject: [PATCH] feat: init项目 --- pom.xml | 213 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/main/java/com/canrd/webmagic/Application.java | 56 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/main/java/com/canrd/webmagic/common/annotation/AnonymousAccess.java | 15 +++++++++++++++ src/main/java/com/canrd/webmagic/common/constant/Constant.java | 332 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/main/java/com/canrd/webmagic/common/constant/ElAdminConstant.java | 25 +++++++++++++++++++++++++ src/main/java/com/canrd/webmagic/common/constant/ServerResult.java | 232 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/main/java/com/canrd/webmagic/common/constant/ServerResultCode.java | 114 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/main/java/com/canrd/webmagic/common/directory/Path.java | 39 +++++++++++++++++++++++++++++++++++++++ src/main/java/com/canrd/webmagic/common/exception/BadRequestException.java | 26 ++++++++++++++++++++++++++ src/main/java/com/canrd/webmagic/common/exception/BusinessException.java | 78 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/main/java/com/canrd/webmagic/common/exception/BusinessExceptionHandlerAdvice.java | 36 ++++++++++++++++++++++++++++++++++++ src/main/java/com/canrd/webmagic/common/exception/EntityExistException.java | 19 +++++++++++++++++++ src/main/java/com/canrd/webmagic/common/exception/EntityNotFoundException.java | 19 +++++++++++++++++++ src/main/java/com/canrd/webmagic/common/exception/ErrorInfo.java | 11 +++++++++++ src/main/java/com/canrd/webmagic/common/exception/handler/ApiError.java | 39 +++++++++++++++++++++++++++++++++++++++ src/main/java/com/canrd/webmagic/common/exception/handler/GlobalExceptionHandler.java | 134 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/main/java/com/canrd/webmagic/common/jsr303/ListValueConstraintValidator.java | 45 +++++++++++++++++++++++++++++++++++++++++++++ src/main/java/com/canrd/webmagic/common/jsr303/OperateGroup.java | 77 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/main/java/com/canrd/webmagic/common/jsr303/annotation/ListIntValue.java | 32 ++++++++++++++++++++++++++++++++ src/main/java/com/canrd/webmagic/common/utils/DateUtil.java | 591 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/main/java/com/canrd/webmagic/common/utils/DateUtils.java | 569 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/main/java/com/canrd/webmagic/common/utils/EncryptUtils.java | 84 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/main/java/com/canrd/webmagic/common/utils/FileUtil.java | 360 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/main/java/com/canrd/webmagic/common/utils/JsonUtil.java | 183 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/main/java/com/canrd/webmagic/common/utils/LocalDateTimeUtil.java | 35 +++++++++++++++++++++++++++++++++++ src/main/java/com/canrd/webmagic/common/utils/PageUtils.java | 136 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/main/java/com/canrd/webmagic/common/utils/RedisUtil.java | 1443 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/main/java/com/canrd/webmagic/common/utils/RedisUtils.java | 646 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/main/java/com/canrd/webmagic/common/utils/RequestContextUtil.java | 35 +++++++++++++++++++++++++++++++++++ src/main/java/com/canrd/webmagic/common/utils/RequestHolder.java | 19 +++++++++++++++++++ src/main/java/com/canrd/webmagic/common/utils/RequestStringUtils.java | 199 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/main/java/com/canrd/webmagic/common/utils/ServletUtils.java | 40 ++++++++++++++++++++++++++++++++++++++++ src/main/java/com/canrd/webmagic/common/utils/SpringContextHolder.java | 66 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/main/java/com/canrd/webmagic/common/utils/StringUtils.java | 1005 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/main/java/com/canrd/webmagic/common/utils/TemplateFormatUtils.java | 57 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/main/java/com/canrd/webmagic/common/utils/ThrowableUtil.java | 36 ++++++++++++++++++++++++++++++++++++ src/main/java/com/canrd/webmagic/common/utils/TransactionHelper.java | 27 +++++++++++++++++++++++++++ src/main/java/com/canrd/webmagic/config/AdminMetaObjectHandler.java | 66 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/main/java/com/canrd/webmagic/config/ConfigurerAdapter.java | 32 ++++++++++++++++++++++++++++++++ src/main/java/com/canrd/webmagic/config/MybatisPlusConfig.java | 9 +++++++++ src/main/java/com/canrd/webmagic/config/RedisConfig.java | 55 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/main/java/com/canrd/webmagic/config/RestTemplateConfig.java | 26 ++++++++++++++++++++++++++ src/main/java/com/canrd/webmagic/config/WebConfig.java | 96 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/main/java/com/canrd/webmagic/controller/TestController.java | 88 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/main/java/com/canrd/webmagic/domain/dto/BaseDO.java | 75 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/main/java/com/canrd/webmagic/domain/dto/TestDO.java | 32 ++++++++++++++++++++++++++++++++ src/main/java/com/canrd/webmagic/domain/vo/BasePageVO.java | 43 +++++++++++++++++++++++++++++++++++++++++++ src/main/java/com/canrd/webmagic/domain/vo/TestQueryVO.java | 33 +++++++++++++++++++++++++++++++++ src/main/java/com/canrd/webmagic/domain/vo/TestVO.java | 31 +++++++++++++++++++++++++++++++ src/main/java/com/canrd/webmagic/mapper/TestMapper.java | 16 ++++++++++++++++ src/main/java/com/canrd/webmagic/processor/BaiduHotSearchPageProcessor.java | 66 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/main/java/com/canrd/webmagic/processor/GithubRepoPageProcessor.java | 38 ++++++++++++++++++++++++++++++++++++++ src/main/java/com/canrd/webmagic/processor/NatureSearchPageProcessor.java | 69 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/main/java/com/canrd/webmagic/service/TestService.java | 57 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/main/java/com/canrd/webmagic/service/impl/TestServiceImpl.java | 133 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/main/resources/application-local.yml | 186 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/main/resources/application-prod.yml | 186 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/main/resources/application-test.yml | 186 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/main/resources/application.yml | 6 ++++++ src/main/resources/ip2region/ip2region.db | Bin 0 -> 6502265 bytes src/main/resources/log4j2-dev.xml | 78 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/main/resources/log4j2-prod.xml | 73 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 62 files changed, 8753 insertions(+), 0 deletions(-) create mode 100644 pom.xml create mode 100644 src/main/java/com/canrd/webmagic/Application.java create mode 100644 src/main/java/com/canrd/webmagic/common/annotation/AnonymousAccess.java create mode 100644 src/main/java/com/canrd/webmagic/common/constant/Constant.java create mode 100644 src/main/java/com/canrd/webmagic/common/constant/ElAdminConstant.java create mode 100644 src/main/java/com/canrd/webmagic/common/constant/ServerResult.java create mode 100644 src/main/java/com/canrd/webmagic/common/constant/ServerResultCode.java create mode 100644 src/main/java/com/canrd/webmagic/common/directory/Path.java create mode 100644 src/main/java/com/canrd/webmagic/common/exception/BadRequestException.java create mode 100644 src/main/java/com/canrd/webmagic/common/exception/BusinessException.java create mode 100644 src/main/java/com/canrd/webmagic/common/exception/BusinessExceptionHandlerAdvice.java create mode 100644 src/main/java/com/canrd/webmagic/common/exception/EntityExistException.java create mode 100644 src/main/java/com/canrd/webmagic/common/exception/EntityNotFoundException.java create mode 100644 src/main/java/com/canrd/webmagic/common/exception/ErrorInfo.java create mode 100644 src/main/java/com/canrd/webmagic/common/exception/handler/ApiError.java create mode 100644 src/main/java/com/canrd/webmagic/common/exception/handler/GlobalExceptionHandler.java create mode 100644 src/main/java/com/canrd/webmagic/common/jsr303/ListValueConstraintValidator.java create mode 100644 src/main/java/com/canrd/webmagic/common/jsr303/OperateGroup.java create mode 100644 src/main/java/com/canrd/webmagic/common/jsr303/annotation/ListIntValue.java create mode 100644 src/main/java/com/canrd/webmagic/common/utils/DateUtil.java create mode 100644 src/main/java/com/canrd/webmagic/common/utils/DateUtils.java create mode 100644 src/main/java/com/canrd/webmagic/common/utils/EncryptUtils.java create mode 100644 src/main/java/com/canrd/webmagic/common/utils/FileUtil.java create mode 100644 src/main/java/com/canrd/webmagic/common/utils/JsonUtil.java create mode 100644 src/main/java/com/canrd/webmagic/common/utils/LocalDateTimeUtil.java create mode 100644 src/main/java/com/canrd/webmagic/common/utils/PageUtils.java create mode 100644 src/main/java/com/canrd/webmagic/common/utils/RedisUtil.java create mode 100644 src/main/java/com/canrd/webmagic/common/utils/RedisUtils.java create mode 100644 src/main/java/com/canrd/webmagic/common/utils/RequestContextUtil.java create mode 100644 src/main/java/com/canrd/webmagic/common/utils/RequestHolder.java create mode 100644 src/main/java/com/canrd/webmagic/common/utils/RequestStringUtils.java create mode 100644 src/main/java/com/canrd/webmagic/common/utils/ServletUtils.java create mode 100644 src/main/java/com/canrd/webmagic/common/utils/SpringContextHolder.java create mode 100644 src/main/java/com/canrd/webmagic/common/utils/StringUtils.java create mode 100644 src/main/java/com/canrd/webmagic/common/utils/TemplateFormatUtils.java create mode 100644 src/main/java/com/canrd/webmagic/common/utils/ThrowableUtil.java create mode 100644 src/main/java/com/canrd/webmagic/common/utils/TransactionHelper.java create mode 100644 src/main/java/com/canrd/webmagic/config/AdminMetaObjectHandler.java create mode 100644 src/main/java/com/canrd/webmagic/config/ConfigurerAdapter.java create mode 100644 src/main/java/com/canrd/webmagic/config/MybatisPlusConfig.java create mode 100644 src/main/java/com/canrd/webmagic/config/RedisConfig.java create mode 100644 src/main/java/com/canrd/webmagic/config/RestTemplateConfig.java create mode 100644 src/main/java/com/canrd/webmagic/config/WebConfig.java create mode 100644 src/main/java/com/canrd/webmagic/controller/TestController.java create mode 100644 src/main/java/com/canrd/webmagic/domain/dto/BaseDO.java create mode 100644 src/main/java/com/canrd/webmagic/domain/dto/TestDO.java create mode 100644 src/main/java/com/canrd/webmagic/domain/vo/BasePageVO.java create mode 100644 src/main/java/com/canrd/webmagic/domain/vo/TestQueryVO.java create mode 100644 src/main/java/com/canrd/webmagic/domain/vo/TestVO.java create mode 100644 src/main/java/com/canrd/webmagic/mapper/TestMapper.java create mode 100644 src/main/java/com/canrd/webmagic/processor/BaiduHotSearchPageProcessor.java create mode 100644 src/main/java/com/canrd/webmagic/processor/GithubRepoPageProcessor.java create mode 100644 src/main/java/com/canrd/webmagic/processor/NatureSearchPageProcessor.java create mode 100644 src/main/java/com/canrd/webmagic/service/TestService.java create mode 100644 src/main/java/com/canrd/webmagic/service/impl/TestServiceImpl.java create mode 100644 src/main/resources/application-local.yml create mode 100644 src/main/resources/application-prod.yml create mode 100644 src/main/resources/application-test.yml create mode 100644 src/main/resources/application.yml create mode 100644 src/main/resources/ip2region/ip2region.db create mode 100644 src/main/resources/log4j2-dev.xml create mode 100644 src/main/resources/log4j2-prod.xml diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..0b645cf --- /dev/null +++ b/pom.xml @@ -0,0 +1,213 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project xmlns="http://maven.apache.org/POM/4.0.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <parent> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-starter-parent</artifactId> + <version>2.2.5.RELEASE</version> + <relativePath/> <!-- lookup parent from repository --> + </parent> + <modelVersion>4.0.0</modelVersion> + + <groupId>org.canrd</groupId> + <artifactId>webmagic-canrd-service</artifactId> + <version>1.0-SNAPSHOT</version> + + <properties> + <maven-compiler-plugin>3.7.0</maven-compiler-plugin> + <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> + <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> + <java.version>1.8</java.version> + <joda-time.version>2.9.9</joda-time.version> + <springboot.version>2.2.5.RELEASE</springboot.version> + <lombok.version>1.18.12</lombok.version> + <baomidou.version>3.4.0</baomidou.version> + <hutool-crypto.version>5.6.1</hutool-crypto.version> + <hutool-all.version>5.0.6</hutool-all.version> + <mysql-connector.version>8.0.11</mysql-connector.version> + <druid.version>1.1.23</druid.version> + <fastjson.version>1.2.28</fastjson.version> + <poi.version>3.17</poi.version> + <poi-ooxml.version>3.17</poi-ooxml.version> + <poi-excel.version>poi-317.8</poi-excel.version> + <commons-csv.version>1.6</commons-csv.version> + <commons-lang3.version>3.8.1</commons-lang3.version> + <commons-pool2.version>2.11.1</commons-pool2.version> + <ip2region.version>1.7.2</ip2region.version> + <userAgentUtils.version>1.20</userAgentUtils.version> + <swagger.version>2.9.2</swagger.version> + <swagger-annotations.version>1.5.21</swagger-annotations.version> + <swagger-models.version>1.5.21</swagger-models.version> + <guava.version>20.0</guava.version> + <easy-captcha.version>1.6.2</easy-captcha.version> + <aliyun-java-sdk-core.version>4.0.3</aliyun-java-sdk-core.version> + <aliyun-sdk-oss.version>3.15.0</aliyun-sdk-oss.version> + <thumbnailator.version>0.4.8</thumbnailator.version> + <jjwt.version>0.10.6</jjwt.version> + <easyexcel.version>2.2.3</easyexcel.version> + <webmagic.version>0.10.0</webmagic.version> + </properties> + + <dependencies> + + <dependency> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-starter-web</artifactId> + <version>2.2.5.RELEASE</version> + <exclusions> + <exclusion> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-starter-logging</artifactId> + </exclusion> + </exclusions> + </dependency> + + <!-- webmagic核心库 --> + <dependency> + <groupId>us.codecraft</groupId> + <artifactId>webmagic-core</artifactId> + <version>${webmagic.version}</version> + </dependency> + + + <!-- webmagic扩展库 --> + <dependency> + <groupId>us.codecraft</groupId> + <artifactId>webmagic-extension</artifactId> + <version>${webmagic.version}</version> + </dependency> + + + <!-- Lombok 依赖--> + <dependency> + <groupId>org.projectlombok</groupId> + <artifactId>lombok</artifactId> + <version>${lombok.version}</version> + <optional>true</optional> + </dependency> + <dependency> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-starter-aop</artifactId> + <version>${springboot.version}</version> + </dependency> + <dependency> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-starter-test</artifactId> + <version>${springboot.version}</version> + <scope>test</scope> + </dependency> + + <dependency> + <groupId>com.baomidou</groupId> + <artifactId>mybatis-plus-boot-starter</artifactId> + <version>${baomidou.version}</version> + </dependency> + <!-- <dependency>--> + <!-- <groupId>com.baomidou</groupId>--> + <!-- <artifactId>dynamic-datasource-spring-boot-starter</artifactId>--> + <!-- </dependency>--> + <dependency> + <groupId>mysql</groupId> + <artifactId>mysql-connector-java</artifactId> + <version>${mysql-connector.version}</version> + </dependency> + <dependency> + <groupId>com.alibaba</groupId> + <artifactId>druid-spring-boot-starter</artifactId> + <version>${druid.version}</version> + </dependency> + + <dependency> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-starter-log4j2</artifactId> + <version>${springboot.version}</version> + </dependency> + + + <dependency> + <groupId>com.alibaba</groupId> + <artifactId>fastjson</artifactId> + <version>${fastjson.version}</version> + </dependency> + + <dependency> + <groupId>cn.hutool</groupId> + <artifactId>hutool-crypto</artifactId> + <version>${hutool-crypto.version}</version> + </dependency> + + <dependency> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-starter-data-redis</artifactId> + <version>${springboot.version}</version> + </dependency> + + <dependency> + <groupId>org.apache.commons</groupId> + <artifactId>commons-pool2</artifactId> + <version>${commons-pool2.version}</version> + </dependency> + + + <dependency> + <groupId>org.apache.commons</groupId> + <artifactId>commons-lang3</artifactId> + <version>${commons-lang3.version}</version> + </dependency> + <dependency> + <groupId>org.lionsoul</groupId> + <artifactId>ip2region</artifactId> + <version>${ip2region.version}</version> + </dependency> + <dependency> + <groupId>eu.bitwalker</groupId> + <artifactId>UserAgentUtils</artifactId> + <version>${userAgentUtils.version}</version> + </dependency> + + <!--工具包--> + <dependency> + <groupId>cn.hutool</groupId> + <artifactId>hutool-all</artifactId> + <version>${hutool-all.version}</version> + </dependency> + <dependency> + <groupId>org.apache.poi</groupId> + <artifactId>poi</artifactId> + <version>${poi.version}</version> + </dependency> + <dependency> + <groupId>org.apache.poi</groupId> + <artifactId>poi-ooxml</artifactId> + <version>${poi-ooxml.version}</version> + </dependency> + + <dependency> + <groupId>joda-time</groupId> + <artifactId>joda-time</artifactId> + <version>${joda-time.version}</version> + </dependency> + + + + </dependencies> + <build> + <finalName>webmagic-canrd.service-1.0-SNAPSHOT</finalName> + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-source-plugin</artifactId> + </plugin> + + <plugin> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-maven-plugin</artifactId> + <configuration> + <executable>true</executable> + </configuration> + </plugin> + + </plugins> + </build> +</project> \ No newline at end of file diff --git a/src/main/java/com/canrd/webmagic/Application.java b/src/main/java/com/canrd/webmagic/Application.java new file mode 100644 index 0000000..25cb130 --- /dev/null +++ b/src/main/java/com/canrd/webmagic/Application.java @@ -0,0 +1,56 @@ +package com.canrd.webmagic; + +import com.baomidou.mybatisplus.annotation.DbType; +import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; +import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor; +import com.canrd.webmagic.common.directory.Path; +import com.canrd.webmagic.common.utils.SpringContextHolder; +import org.mybatis.spring.annotation.MapperScan; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; +import org.springframework.context.annotation.Bean; +import org.springframework.scheduling.annotation.EnableAsync; +import org.springframework.scheduling.annotation.EnableScheduling; +import org.springframework.transaction.annotation.EnableTransactionManagement; + +/** + * @author: xms + * @description: + * @date: 2024/1/12 14:00 + * @version: 1.0 + */ +@EnableAsync +@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class}, scanBasePackages = {"com.canrd.webmagic"}) +@MapperScan("com.canrd.webmagic.**.mapper") +@EnableScheduling +@EnableTransactionManagement +public class Application { + + private static void setLogPath() { + String appPath = Path.getAppPath(Application.class); + System.setProperty("logging.path", appPath); + } + + @Bean + public SpringContextHolder springContextHolder() { + return new SpringContextHolder(); + } + + public static void main(String[] args) { + setLogPath(); + SpringApplication.run(Application.class, args); + } + + /** + * 分页插件 + * + * @return + */ + @Bean + public MybatisPlusInterceptor mybatisPlusInterceptor() { + MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); + interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.POSTGRE_SQL)); + return interceptor; + } +} diff --git a/src/main/java/com/canrd/webmagic/common/annotation/AnonymousAccess.java b/src/main/java/com/canrd/webmagic/common/annotation/AnonymousAccess.java new file mode 100644 index 0000000..bafd8c5 --- /dev/null +++ b/src/main/java/com/canrd/webmagic/common/annotation/AnonymousAccess.java @@ -0,0 +1,15 @@ +package com.canrd.webmagic.common.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * 用于标记匿名访问方法 + */ +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +public @interface AnonymousAccess { + +} diff --git a/src/main/java/com/canrd/webmagic/common/constant/Constant.java b/src/main/java/com/canrd/webmagic/common/constant/Constant.java new file mode 100644 index 0000000..302791c --- /dev/null +++ b/src/main/java/com/canrd/webmagic/common/constant/Constant.java @@ -0,0 +1,332 @@ +package com.canrd.webmagic.common.constant; + +import java.util.Arrays; +import java.util.List; + +public class Constant { + /** + * 分隔符英文的横杠 + */ + public static final String CROSS_BAR_CHARACTER = "-"; + /** + * 英文的 . + */ + public static final String POINT_BAR_CHARACTER = "."; + /** + * 英文的 ! + */ + public static final String EXCLAMATION_MARK_CHARACTER = "!"; + /** + * 英文的 / + */ + public static final String SLASH_MARK_CHARACTER = "/"; + /** + * 英文的 : + */ + public static final String COLON_CHARACTER = ":"; + + /** + * 英文的 ; + */ + public static final String SEMICOLON_CHARACTER = ";"; + + /** + * 英文的逗号 + */ + public static final String COMMA_CHARACTER = ","; + /** + * 英文的*号 + */ + public static final String START_CHARACTER = "*"; + /** + * 分隔符 + */ + public static final String SPLIT_SYMBOL = ",|,"; + + /** + * 特殊分隔符 _ + */ + public static final String SPECIAL_KEY = "_"; + + + /** + * 括号 + */ + public static final String BRACKETS_RIGHT = "]"; + /** + * 括号 + */ + public static final String BRACKETS_LEFT = "["; + + /** + * 手机号码正则校验 + */ + public static final String PHONE_REGEXP = "^[1][3,4,5,6,7,8,9][0-9]{9}$"; + + /** + * 脱敏手机号 + */ + public static final String PHONE_DESENSITIZATION_REGEXP = "^[1][3,4,5,6,7,8,9][0-9][*]{4}[0-9]{4}$"; + + /** + * 邮箱 + */ + public static final String EMAIL_REGEXP = "^\\s*\\w+(?:\\.{0,1}[\\w-]+)*@[a-zA-Z0-9]+(?:[-.][a-zA-Z0-9]+)*\\.[a-zA-Z]+\\s*$"; + + /** + * 三位数字 + */ + public static final String THREE_DIGITS_REGEXP = "\\d{3}"; + + /** + * 中文 英文 数字 + */ + public static final String CHI_EN_NUM_REGEXP = "^[A-z0-9\\u4e00-\\u9fa5]*$"; + + + /** + * 仅包含英文和数字 + */ + public static final String EN_REGEXP = "^[a-zA-Z]+$"; + + public static final String EN_NUM_REGEXP = "^[a-z0-9A-Z]+$"; + + public static final String LINE_EN_NUM_REGEXP = "^[a-z0-9A-Z\\-]+$"; + + /** + * 仅包含英文和中文 + */ + public static final String CHI_EN_REGEXP = "^[A-z\\u4e00-\\u9fa5]*$"; + + /** + * 纯数字 + */ + public static final String NUMERIC_REGEXP = "^\\d+$"; + /** + * 纯中文 + */ + public static final String CHI_REGEXP = "^[\u4e00-\u9fa5]+$"; + /** + * 不超过两位小数 + */ + public static final String DICMAL_REGEXP = "^(([1-9]{1}\\d*)|([0]{1}))(\\.(\\d){0,3})?$"; + + /** + * 默认空字符串 + */ + public static final String EMPTY_STRING = ""; + + /** + * 统一返回data的key + */ + public static final String RESULT_CHARACTER = "result"; + + /** + * 统一返回data的key + */ + public static final String SUCCESS_RESULT_CHARACTER = "success"; + + /** + * 返回的默认结果集 + */ + public static final String RESULT_FAIL = null; + + /** + * ENABLE_FLAG + */ + public static final String ENABLE_FLAG = "enable_flag"; + + /** + * 是否可用 10-可用 20-删除 + */ + public static final int ENABLE_TEN = 10; + /** + * 是否可用 10-可用 20-删除 + */ + public static final int UNABLE_TWENTY = 20; + + public static final int ONE = 1; + + public static final Integer INTEGER_ONE = 1; + + public static final int TWO = 2; + + public static final int THREE = 3; + + public static final int FOUR = 4; + + public static final int FIVE = 5; + + public static final int SIX = 6; + + public static final int SEVEN = 7; + + public static final int THIRTY = 30; + + public static final int NINETEEN = 19; + + public static final int THOUSAND = 1000; + + public static final String DATE_TIME = "yyyy-MM-dd HH:mm:ss"; + + public static final int ZERO = 0; + + public static final String STRING_ZERO = "0"; + + public static final String ZERO_STRING = "0"; + + public static final String THOUSAND_STRING = "1000"; + + public static final String SYSTEM_USER = "System"; + + public static final String percent50 = "0.5"; + + public static final String DELETE_SUCCESS_RESULT_CHARACTER = "删除成功"; + + + /** + * token rediskey + */ + public static final String TOKEN_FLAG = "token:"; + /** + * token 限制api请求 + */ + public static final String PERMISSION_API_LIMIT = "token_api_limit:"; + + /** + * token过期时间 + */ + public static final Integer TOKEN_EXPIRE_HOURS = 1; + /** + * 密码3次错误锁定 + */ + public static final int LOCK_ERROR_TIMES = 3; + + /** + * session字段 + */ + public static final String HEAD_TOKEN = "Authorization"; + public static final String ACCESS_TOKEN = "token"; + public static final String REQUEST_ORIGIN = "source"; + public static final String REQUEST_IP = "ip"; + public static final String USER_INFO = "userInfo"; + public static final String FILED_ID = "id"; + public static final String FILED_USER_NAME = "username"; + public static final String FILED_DEVICE_NUM = "deviceNum"; + + /** + * 请求来源 + */ + public static final String ORIGIN_WEB = "WEB"; + public static final String ORIGIN_PDA = "PDA"; + + /** + * 1001 参数校验不通过 + * 1002 签名验证不通过 + */ + public static final List<String> ERROR_CODE_LIST = Arrays.asList("1001", "1002"); + + /** + * 分布式锁超时时间 + */ + public static final long REDIS_LOCK_DEFAULT_TIME_OUT_MILLIS = 3000; + public static final long REDIS_LOCK_FIVE_SECONDS_TIME_OUT_MILLIS = 5000; + + /** + * 系统来源WMS + */ + public static final String SYS_ORIGIN_WMS = "WMS"; + + public static final String STRING_ONE = "1"; + + + public static final String STRING_TWO = "2"; + + public static final String STRING_THREE = "3"; + + public static final String STRING_FOUR = "4"; + + public static final String STRING_FIVE = "5"; + + public static final String STRING_SIX = "6"; + + public static final String STRING_SEVEN = "7"; + + public static final String STRING_EIGHT = "8"; + + public static final String STRING_NINE = "9"; + + public static final String STRING_TEN = "10"; + + public static final String STRING_ELEVEN = "11"; + + public static final String STRING_TWELVE = "12"; + + /** + * 异常延时队列后缀 + */ + public static final String ERROR_DELAY_QUEUE_SUFFIX = "error"; + + /** + * 推送出库数据延时队列 redis key prefix + */ + public static final String OUTBOUND_NOTIFY_DELAY_QUEUE_PREFIX = "outbound:notify:"; + + /** + * 同步MES库存队列 redis key prefix + */ + public static final String MES_STOCK_SYNC_QUEUE_PREFIX = "mes:stock_sync:"; + + /** + * 分拣详情 错误码: 157205 返回值段 + */ + public static final String SEPARATE_PICK_QUERY_RETURN_FIELD = "separatePickQuery"; + /** + * 限制上限值 + */ + public static final Integer LIMIT_999 = 999; + + /** + * 东八区 + */ + public static final String GMT_8 = "GMT+8"; + + /** + * 英文的 " + */ + public static final String QUOTATION_MARK_CHARACTER = "\""; + + + /** + * 英文的 \" + */ + public static final String QUOTATION_SLASH_MARK_CHARACTER = "\\\""; + + public static final String SUCCESS_VALUE = "success"; + + + public static final String TRUE = "true"; + + public static final String ROOT_PARENT_ID = "0"; + + public static final Long ROOT_PARENT_ID_LONG = 0L; + + + /** + * 短信验证码 redis key + */ + public static final String SMS_AUTH_CODE_PREFIX = "sms:auth:code:"; + + public static final String DEFAULT_PASSWORD = "JBXT123456"; + + /** + * 账号在线队列 redis key + */ + public static final String ACCOUNT_ONLINE_LIST = "account:online:"; + + /** + * 缓存导入excel错误信息 redis key + */ + public static final String EXCEL_IMPORT_ERROR_PREFIX = "excel:import:error:"; + +} diff --git a/src/main/java/com/canrd/webmagic/common/constant/ElAdminConstant.java b/src/main/java/com/canrd/webmagic/common/constant/ElAdminConstant.java new file mode 100644 index 0000000..f2dd076 --- /dev/null +++ b/src/main/java/com/canrd/webmagic/common/constant/ElAdminConstant.java @@ -0,0 +1,25 @@ +package com.canrd.webmagic.common.constant; + +/** + * 常用静态常量 + * + * @date 2018-12-26 + */ +public class ElAdminConstant { + + public static final String RESET_PASS = "重置密码"; + + public static final String RESET_MAIL = "重置邮箱"; + + /** + * 用于IP定位转换 + */ + public static final String REGION = "内网IP|内网IP"; + + /** + * 常用接口 + */ + public static class Url { + public static final String SM_MS_URL = "https://sm.ms/api"; + } +} diff --git a/src/main/java/com/canrd/webmagic/common/constant/ServerResult.java b/src/main/java/com/canrd/webmagic/common/constant/ServerResult.java new file mode 100644 index 0000000..72a6df6 --- /dev/null +++ b/src/main/java/com/canrd/webmagic/common/constant/ServerResult.java @@ -0,0 +1,232 @@ +package com.canrd.webmagic.common.constant; + + +import java.io.Serializable; +import java.util.HashMap; +import java.util.Map; + +/** + * @Date: 2020/9/5 + * 统一的返回结果实体 + */ +public class ServerResult<T> implements Serializable { + /** + * 序列化ID + */ + private static final long serialVersionUID = -5809782578272943999L; + /** + * 返回的状态码 + */ + private int result = ServerResultCode.SUCCESS.getErrorCode(); + /** + * 返回的消息 + */ + private String message = ServerResultCode.SUCCESS.getErrorDesc(); + /** + * 返回的数据实体 + */ + private T data = null; + /** + * + */ + final int INIT_SIZE = 128; + + /** + * + */ + public ServerResult() { + } + + @Override + public String toString() { + StringBuilder buffer = new StringBuilder(INIT_SIZE); + buffer.append("{\"result\":").append(this.result); + buffer.append(",\"message\":\"").append(this.message != null ? message : "").append("\""); + buffer.append(",\"data\":").append(data).append("}"); + return buffer.toString(); + } + + public int getResult() { + return result; + } + + public ServerResult setResult(int result) { + this.result = result; + return this; + } + + public ServerResult setResult(ServerResultCode serverResultCode) { + this.result = serverResultCode.getErrorCode(); + this.message = serverResultCode.getErrorDesc(); + return this; + } + + public ServerResult setResult(int errorCode, String errorDesc) { + this.result = errorCode; + this.message = errorDesc; + return this; + } + + public String getMessage() { + return message; + } + + public ServerResult<T> setMessage(String message) { + this.message = message; + return this; + } + + public T getData() { + return data; + } + + public ServerResult setData(T data) { + this.data = data; + return this; + } + + public static <T> ServerResult<T> success() { + return new ServerResult() + .setResult(ServerResultCode.SUCCESS) + .setMessage(ServerResultCode.SUCCESS.getErrorDesc()) + .setData(defaultResult(ServerResultCode.SUCCESS)); + } + + public static <T> ServerResult<T> success(T data) { + return new ServerResult() + .setResult(ServerResultCode.SUCCESS) + .setMessage(ServerResultCode.SUCCESS.getErrorDesc()) + .setData(data); + } + + public static <T> ServerResult<T> success(T data, String message) { + return new ServerResult() + .setResult(ServerResultCode.SUCCESS) + .setMessage(message) + .setData(data); + } + + /** + * @description 错误码使用系统错误 1000 错误类型自定义 + * @author dengbin + * @date 2020/9/12 + */ + public static <T> ServerResult<T> fail(String message) { + return new ServerResult() + .setResult(ServerResultCode.FAIL) + .setMessage(message) + .setData(defaultResult(Constant.RESULT_FAIL)); + } + + /** + * 错误时需要返回相关数据时使用 + * + * @param data + * @param <T> + * @return + */ + public static <T> ServerResult<T> fail(T data, ServerResultCode serverResultCode) { + return new ServerResult() + .setData(data) + .setResult(serverResultCode) + .setMessage(serverResultCode.getErrorDesc()); + } + + /** + * 错误时需要返回相关数据时使用(不设值 data) + * + * @param sr + * @param + * @return + */ + public static <T> ServerResult<T> fail(ServerResult<?> sr) { + return new ServerResult<>() + .setMessage(sr.getMessage()) + .setResult(sr.getResult()); + + } + + /** + * @description 错误码使用上面的枚举定义的,错误信息重新定义 + * @author dengbin + * @date 2020/9/12 + */ + public static <T> ServerResult<T> fail(ServerResultCode serverResultCode, String msg) { + return new ServerResult() + .setResult(serverResultCode) + .setMessage(msg) + .setData(defaultResult(Constant.RESULT_FAIL)); + } + + /** + * @return + * @description 直接选用上面的枚举错误码,输出定义的错误信息 + * @author dengbin + * @date 2020/9/12 + */ + public static <T> ServerResult<T> fail(ServerResultCode serverResultCode) { + return new ServerResult() + .setResult(serverResultCode) + .setMessage(serverResultCode.getErrorDesc()) + .setData(defaultResult(Constant.RESULT_FAIL)); + } + + /** + * 错误时需要返回相关数据时使用 + * + * @param data + * @param <T> + * @return + */ + public static <T> ServerResult<T> fail(T data, int errorCode, String errorDesc) { + return new ServerResult() + .setData(data) + .setResult(errorCode, errorDesc); + } + + + public static <T> ServerResult<T> fail() { + return new ServerResult() + .setResult(ServerResultCode.FAIL.getErrorCode()) + .setMessage(ServerResultCode.FAIL.getErrorDesc()) + .setData(defaultResult(Constant.RESULT_FAIL)); + } + + + /** + * @description 直接选用上面的枚举错误码,输出定义的错误信息 + * @author dengbin + * @date 2020/9/12 + */ + public ServerResult failEnum(ServerResultCode serverResultCode) { + setResult(serverResultCode); + setMessage(serverResultCode.getErrorDesc()); + return this; + } + + public static <T> ServerResult<T> fail(int errorCode, String errorDesc) { + return new ServerResult() + .setResult(errorCode, errorDesc) + .setData(defaultResult(Constant.RESULT_FAIL)); + + } + + /** + * 判断当前的状态码是否是SUCCESS + * + * @return 结果 ture or false + */ + public boolean checkSuccess() { + return result == ServerResultCode.SUCCESS.getErrorCode(); + } + + public boolean checkNotSuccess() { + return !checkSuccess(); + } + + private static Map<String, Object> defaultResult(Object object) { + Map<String, Object> map = new HashMap<>(Constant.ONE); + map.put(Constant.RESULT_CHARACTER, null); + return map; + } +} diff --git a/src/main/java/com/canrd/webmagic/common/constant/ServerResultCode.java b/src/main/java/com/canrd/webmagic/common/constant/ServerResultCode.java new file mode 100644 index 0000000..af50baa --- /dev/null +++ b/src/main/java/com/canrd/webmagic/common/constant/ServerResultCode.java @@ -0,0 +1,114 @@ +package com.canrd.webmagic.common.constant; + + +import com.canrd.webmagic.common.exception.ErrorInfo; +import lombok.Setter; + + +/** + * 请求成功 返回 0 + */ +public enum ServerResultCode implements ErrorInfo { + //成功 "0" + SUCCESS(0, "成功"), + FAIL(1000, "系统内部错误,请联系业务系统运维管理员"), + FIlE_UPLOAD_TOO_LARGE(1051, "上传文件太大,图片文件一般小于2兆"), + + UNAUTHORIZED(401, "登录状态过期"), + + //空指针异常 + NULL_POINT(1001, "空指针异常"), + + //校验异常 + RUN_ERROR(1002, "程序运行报错"), + ILLEGAL_ARGUMENT(1003, "参数非法"), + FILED_ERROR(1004, "传入参数错误"), + PARAM_ERROR(1005, "入参为空"), + EMPTY_RESULT(1006, "数据不存在"), + EMPTY_LIST(1007, "查询结果为空"), + IMG_CAPTCHA_ERROR(1008, "图片验证码错误"), + IMG_CAPTCHA_EXPIRE_ERROR(1009, "图片验证码不存在或已过期"), + SMS_CAPTCHA_ERROR(1010, "短信验证码错误"), + SMS_CAPTCHA_EXPIRE_ERROR(1011, "短信验证码不存在或已过期"), + + //认证授权异常 + UNAUTHENTICATION(401, "未登录"), + + + //用户 + USER_NOT_EXIT(20001, "用户不存在"), + USER_UN_ENABLE(20002, "用户未激活"), + + + // 公司 + COMPANY_NOT_EXIT(30001, "公司不存在"), + + // 短信 + SMS_SEND_OVER_LIMIT_ERROR(40001, "发送频次过高,请一分钟后再发送"), + EMAIL_SEND_OVER_LIMIT_ERROR(40002, "发送频次过高(一天最多修改三次),请第二天后再发送"), + + // 会员 + MEMBER_PHONE_REGISTER_ERROR(50001, "手机已经被注册使用!"), + MEMBER_AGREE_AGREEMENT_CHOOSE_ERROR(50002, "必须同意注册协议才可进行注册操作!"), + MEMBER_CONFIRM_PASSWORD_ERROR(50003, "两次密码输入不一致!"), + MEMBER_LOGIN_PHONE_ERROR(50004, "手机号没有注册,请注册后登录!"), + MEMBER_LOGIN_LOCKED_TIME_ERROR(50005, "账号被锁定,请稍后再试试!"), + MEMBER_RECOVER_PASSWORD_ERROR(50006, "对不起,此密码找回链接已失效!"), + MEMBER_RECOVER_PASSWORD_EXPIRE_ERROR(50007, "对不起,此密码找回链接已过期!"), + + //产品 + PRODUCT_NOT_EXIST_ERROR(60001, "此商品已下架!"), + PRODUCT_NOT_MORE_STORE_ERROR(60002, "添加购物车失败,商品库存不足!"), + + //订单 + ORDER_BASE_INFO_EMPTY(70001, "订单基础信息不能为空!"), + + //申请 + APPLY_UNLOCK_FIELD_EXIST(80001, "还有未审批完结的申请,请等上一个申请单完结!"), + APPLY_NOT_EXIST(80002, "申请单不存在!"), + + //上传图片 + UPLOAD_IMAGES_ERROR(900021, "上传图片失败!"), + ; + + + ServerResultCode(Integer errorCode, String errorDesc) { + this.errorCode = errorCode; + this.errorDesc = errorDesc; + } + + @Setter + private Integer errorCode; + @Setter + private String errorDesc; + + @Override + public Integer getErrorCode() { + return this.errorCode; + } + + @Override + public String getErrorDesc() { + return this.errorDesc; + } + + /** + * 根据errorCode获得枚举 + * + * @param errorCode + * @return + */ + public static String getDescByCode(Integer errorCode) { + if (errorCode == null) { + return null; + } + ServerResultCode[] serverResults = values(); + for (ServerResultCode serverResult : serverResults) { + if (serverResult.getErrorCode().equals(errorCode)) { + return serverResult.getErrorDesc(); + } + } + return null; + } + +} \ No newline at end of file diff --git a/src/main/java/com/canrd/webmagic/common/directory/Path.java b/src/main/java/com/canrd/webmagic/common/directory/Path.java new file mode 100644 index 0000000..ed2131f --- /dev/null +++ b/src/main/java/com/canrd/webmagic/common/directory/Path.java @@ -0,0 +1,39 @@ +package com.canrd.webmagic.common.directory; + +import com.canrd.webmagic.common.constant.Constant; + +/** + * @author xms + */ +public class Path { + /** + * 获取程序启动路径 + * + * @param cls,建议直接传 ServiceApplication.class + * @return 应用启动路径 + */ + public static String getAppPath(Class cls) { + String path = cls.getResource(Constant.SLASH_MARK_CHARACTER).getPath(); + String os = System.getProperty("os.name").toLowerCase(); + final String OSWINDOW = "windows"; + if (os.indexOf(OSWINDOW) != -1 && path.length() > 1) { + //windows路径样例: /D:/work/code/shop-services/online-shop-oss/target/classes/ + return path.substring(1); + } + + //linux 路径样例 file:/opt/target/online-shop-oss-SNAPSHOT.jar!/BOOT-INF/classes!/"; + final String FILE = "file:"; + if (path.indexOf(FILE) != -1) { + path = path.substring(FILE.length()); + } + + int pos = -1; + if ((pos = path.indexOf(Constant.EXCLAMATION_MARK_CHARACTER)) != -1) { + if (-1 != (pos = path.lastIndexOf(Constant.SLASH_MARK_CHARACTER, pos))) { + path = path.substring(0, pos + 1); + } + } + + return path; + } +} diff --git a/src/main/java/com/canrd/webmagic/common/exception/BadRequestException.java b/src/main/java/com/canrd/webmagic/common/exception/BadRequestException.java new file mode 100644 index 0000000..21a78cf --- /dev/null +++ b/src/main/java/com/canrd/webmagic/common/exception/BadRequestException.java @@ -0,0 +1,26 @@ +package com.canrd.webmagic.common.exception; + +import lombok.Getter; +import org.springframework.http.HttpStatus; + +import static org.springframework.http.HttpStatus.BAD_REQUEST; + +/** + * + * @date 2018-11-23 + * 统一异常处理 + */ +@Getter +public class BadRequestException extends RuntimeException { + + private Integer status = BAD_REQUEST.value(); + + public BadRequestException(String msg) { + super(msg); + } + + public BadRequestException(HttpStatus status, String msg) { + super(msg); + this.status = status.value(); + } +} diff --git a/src/main/java/com/canrd/webmagic/common/exception/BusinessException.java b/src/main/java/com/canrd/webmagic/common/exception/BusinessException.java new file mode 100644 index 0000000..ed77ab2 --- /dev/null +++ b/src/main/java/com/canrd/webmagic/common/exception/BusinessException.java @@ -0,0 +1,78 @@ +package com.canrd.webmagic.common.exception; + +import com.canrd.webmagic.common.constant.ServerResult; +import com.canrd.webmagic.common.constant.ServerResultCode; +import lombok.Getter; +import lombok.Setter; +import lombok.extern.slf4j.Slf4j; + +/** + * <p>基础异常类,所有自定义异常类都需要继承本类</p> + */ +@Slf4j +public class BusinessException extends RuntimeException { + + private static final long serialVersionUID = 3152549963899218489L; + @Setter + @Getter + private String errorDesc; + @Setter + @Getter + private Integer errorCode; + @Setter + @Getter + private Object data; + + public BusinessException() { + super(ServerResultCode.FAIL.getErrorDesc()); + this.errorCode = ServerResultCode.FAIL.getErrorCode(); + this.errorDesc = ServerResultCode.FAIL.getErrorDesc(); + } + + public BusinessException(int errorCode, String errorDesc) { + super(errorDesc); + this.errorCode = errorCode; + this.errorDesc = errorDesc; + } + + public BusinessException(ServerResultCode serverResultCode) { + super(serverResultCode.getErrorDesc()); + this.errorCode = serverResultCode.getErrorCode(); + this.errorDesc = serverResultCode.getErrorDesc(); + log.error("业务异常: ", this); + } + + public BusinessException(ErrorInfo errorInfo) { + super(errorInfo.getErrorDesc()); + this.errorCode = errorInfo.getErrorCode(); + this.errorDesc = errorInfo.getErrorDesc(); + log.error("业务异常: ", this); + } + + public BusinessException(ServerResult<?> serverResult) { + super(serverResult == null ? ServerResultCode.FAIL.getErrorDesc() : serverResult.getMessage()); + if (serverResult == null) { + this.errorCode = ServerResultCode.FAIL.getErrorCode(); + this.errorDesc = ServerResultCode.FAIL.getErrorDesc(); + } else { + this.errorCode = serverResult.getResult(); + this.errorDesc = serverResult.getMessage(); + this.data = serverResult.getData(); + } + log.error("业务异常: ", this); + } + + public BusinessException(String message) { + super(message); + } + + public BusinessException(Throwable cause) { + super(cause); + log.error("业务异常: ", this); + } + + public BusinessException(String message, Throwable cause) { + super(message, cause); + log.error("业务异常: ", this); + } +} diff --git a/src/main/java/com/canrd/webmagic/common/exception/BusinessExceptionHandlerAdvice.java b/src/main/java/com/canrd/webmagic/common/exception/BusinessExceptionHandlerAdvice.java new file mode 100644 index 0000000..2bd7acb --- /dev/null +++ b/src/main/java/com/canrd/webmagic/common/exception/BusinessExceptionHandlerAdvice.java @@ -0,0 +1,36 @@ +package com.canrd.webmagic.common.exception; + +import com.canrd.webmagic.common.constant.ServerResult; +import com.canrd.webmagic.common.constant.ServerResultCode; +import org.springframework.core.annotation.Order; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.ControllerAdvice; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.ResponseStatus; + +import java.util.Optional; + +/** + * 业务异常统一处理类 + * + * @date 2021/1/11 + */ +@ControllerAdvice +@Order(5) +public class BusinessExceptionHandlerAdvice { + + + @ExceptionHandler(value = {BusinessException.class}) + @ResponseStatus(value = HttpStatus.OK) + public ResponseEntity<Object> handleException(BusinessException exception) throws Exception { + return new ResponseEntity<Object>(handleBusinessException(exception), HttpStatus.OK); + } + + private ServerResult handleBusinessException(BusinessException exception) { + Integer code = Optional.ofNullable(exception.getErrorCode()).orElse(ServerResultCode.FAIL.getErrorCode()); + String msg = Optional.ofNullable(exception.getMessage()).orElse(ServerResultCode.FAIL.getErrorDesc()); + Object data = Optional.ofNullable(exception.getData()).orElse(null); + return ServerResult.fail(data, code, msg); + } +} diff --git a/src/main/java/com/canrd/webmagic/common/exception/EntityExistException.java b/src/main/java/com/canrd/webmagic/common/exception/EntityExistException.java new file mode 100644 index 0000000..58fc391 --- /dev/null +++ b/src/main/java/com/canrd/webmagic/common/exception/EntityExistException.java @@ -0,0 +1,19 @@ +package com.canrd.webmagic.common.exception; + +import org.springframework.util.StringUtils; + +/** + * + * @date 2018-11-23 + */ +public class EntityExistException extends RuntimeException { + + public EntityExistException(Class clazz, String field, String val) { + super(EntityExistException.generateMessage(clazz.getSimpleName(), field, val)); + } + + private static String generateMessage(String entity, String field, String val) { + return StringUtils.capitalize(entity) + + " with " + field + " " + val + " existed"; + } +} \ No newline at end of file diff --git a/src/main/java/com/canrd/webmagic/common/exception/EntityNotFoundException.java b/src/main/java/com/canrd/webmagic/common/exception/EntityNotFoundException.java new file mode 100644 index 0000000..a02574d --- /dev/null +++ b/src/main/java/com/canrd/webmagic/common/exception/EntityNotFoundException.java @@ -0,0 +1,19 @@ +package com.canrd.webmagic.common.exception; + +import org.springframework.util.StringUtils; + +/** + * + * @date 2018-11-23 + */ +public class EntityNotFoundException extends RuntimeException { + + public EntityNotFoundException(Class clazz, String field, String val) { + super(EntityNotFoundException.generateMessage(clazz.getSimpleName(), field, val)); + } + + private static String generateMessage(String entity, String field, String val) { + return StringUtils.capitalize(entity) + + " with " + field + " " + val + " does not exist"; + } +} \ No newline at end of file diff --git a/src/main/java/com/canrd/webmagic/common/exception/ErrorInfo.java b/src/main/java/com/canrd/webmagic/common/exception/ErrorInfo.java new file mode 100644 index 0000000..c9d2ccf --- /dev/null +++ b/src/main/java/com/canrd/webmagic/common/exception/ErrorInfo.java @@ -0,0 +1,11 @@ +package com.canrd.webmagic.common.exception; + +/** + * @date 2023-01-12 + */ +public interface ErrorInfo { + + Integer getErrorCode(); + + String getErrorDesc(); +} diff --git a/src/main/java/com/canrd/webmagic/common/exception/handler/ApiError.java b/src/main/java/com/canrd/webmagic/common/exception/handler/ApiError.java new file mode 100644 index 0000000..5af0dd8 --- /dev/null +++ b/src/main/java/com/canrd/webmagic/common/exception/handler/ApiError.java @@ -0,0 +1,39 @@ +package com.canrd.webmagic.common.exception.handler; + +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.Data; + +import java.time.LocalDateTime; + +/** + * + * @date 2018-11-23 + */ +@Data +class ApiError { + + private Integer status = 200; + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private LocalDateTime timestamp; + private String message; + private String errorCode; + + private ApiError() { + timestamp = LocalDateTime.now(); + } + + public static ApiError error(String message){ + ApiError apiError = new ApiError(); + apiError.setMessage(message); + return apiError; + } + + public static ApiError error(Integer status, String message){ + ApiError apiError = new ApiError(); + apiError.setStatus(status); + apiError.setMessage(message); + return apiError; + } +} + + diff --git a/src/main/java/com/canrd/webmagic/common/exception/handler/GlobalExceptionHandler.java b/src/main/java/com/canrd/webmagic/common/exception/handler/GlobalExceptionHandler.java new file mode 100644 index 0000000..4d54389 --- /dev/null +++ b/src/main/java/com/canrd/webmagic/common/exception/handler/GlobalExceptionHandler.java @@ -0,0 +1,134 @@ +package com.canrd.webmagic.common.exception.handler; + +import com.canrd.webmagic.common.constant.ServerResult; +import com.canrd.webmagic.common.constant.ServerResultCode; +import com.canrd.webmagic.common.exception.BusinessException; +import com.canrd.webmagic.common.exception.EntityExistException; +import com.canrd.webmagic.common.exception.EntityNotFoundException; +import com.canrd.webmagic.common.utils.ThrowableUtil; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.MethodArgumentNotValidException; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.ResponseStatus; +import org.springframework.web.bind.annotation.RestControllerAdvice; +import org.springframework.web.multipart.MaxUploadSizeExceededException; + +import java.util.Objects; + +/** + * @date 2018-11-23 + */ +@Slf4j +@RestControllerAdvice +public class GlobalExceptionHandler { + /** + * @param exception + * @return + * @throws Exception + */ + @ExceptionHandler(value = RuntimeException.class) + public ResponseEntity<Object> handleServiceException(Exception exception) throws Exception { + return new ResponseEntity(translateException(exception), HttpStatus.OK); + } + + /** + * @param exception + * @return + * @throws Exception + */ + @ExceptionHandler(value = Exception.class) + @ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR) + public ResponseEntity<Object> handleException(Exception exception) throws Exception { + return new ResponseEntity<Object>(translateException(exception), HttpStatus.OK); + } + + + /** + * 处理所有接口数据验证异常 + * + * @param e + * @return + */ + @ExceptionHandler(MethodArgumentNotValidException.class) + public ResponseEntity<ServerResult> handleMethodArgumentNotValidException(MethodArgumentNotValidException e) { + // 打印堆栈信息 + log.error(ThrowableUtil.getStackTrace(e)); + String[] str = Objects.requireNonNull(e.getBindingResult().getAllErrors().get(0).getCodes())[1].split("\\."); + String message = e.getBindingResult().getAllErrors().get(0).getDefaultMessage(); + String msg = "不能为空"; + if (msg.equals(message)) { + message = str[1] + ":" + message; + } + return buildResponseEntity(ServerResult.fail(message)); + } + + /** + * 处理自定义异常 + * + * @param e + * @return + */ + @ExceptionHandler(value = BusinessException.class) + public ResponseEntity<ServerResult> badRequestException(BusinessException e) { + // 打印堆栈信息 + log.error(ThrowableUtil.getStackTrace(e)); + return buildResponseEntity(ServerResult.fail(e.getErrorCode(), e.getMessage())); + } + + /** + * 处理 EntityExist + */ + @ExceptionHandler(value = EntityExistException.class) + public ResponseEntity<ServerResult> entityExistException(EntityExistException e) { + // 打印堆栈信息 + log.error(ThrowableUtil.getStackTrace(e)); + return buildResponseEntity(ServerResult.fail(e.getMessage())); + } + + /** + * 处理 EntityNotFound + * + * @param e + * @return + */ + @ExceptionHandler(value = EntityNotFoundException.class) + public ResponseEntity<ServerResult> entityNotFoundException(EntityNotFoundException e) { + // 打印堆栈信息 + log.error(ThrowableUtil.getStackTrace(e)); + return buildResponseEntity(ServerResult.fail(HttpStatus.NOT_FOUND.value(), e.getMessage())); + } + + /** + * @param serverResult + * @return + */ + private ResponseEntity<ServerResult> buildResponseEntity(ServerResult serverResult) { + return new ResponseEntity<>(serverResult, HttpStatus.valueOf(200)); + } + + /** + * @param e + * @return + * @throws Exception + */ + private Object translateException(Exception e) throws Exception { + ServerResult serverResult = null; + if (e instanceof NullPointerException) { + serverResult = ServerResult.fail(ServerResultCode.NULL_POINT); + } else if (e instanceof IllegalArgumentException) { + serverResult = ServerResult.fail(ServerResultCode.FAIL); + } else if (e instanceof MaxUploadSizeExceededException) { + serverResult = ServerResult.fail(ServerResultCode.FIlE_UPLOAD_TOO_LARGE); + } else if (e instanceof BusinessException) { + serverResult = ServerResult.fail(((BusinessException) e).getErrorCode(), ((BusinessException) e).getErrorDesc()); + } else { + serverResult = ServerResult.fail(ServerResultCode.FAIL); + serverResult.setMessage(serverResult.getMessage() + "|,业务异常:" + e.getMessage()); + } + + log.error("业务异常", e); + return serverResult; + } +} diff --git a/src/main/java/com/canrd/webmagic/common/jsr303/ListValueConstraintValidator.java b/src/main/java/com/canrd/webmagic/common/jsr303/ListValueConstraintValidator.java new file mode 100644 index 0000000..032ec30 --- /dev/null +++ b/src/main/java/com/canrd/webmagic/common/jsr303/ListValueConstraintValidator.java @@ -0,0 +1,45 @@ +package com.canrd.webmagic.common.jsr303; + + +import com.canrd.webmagic.common.jsr303.annotation.ListIntValue; + +import javax.validation.ConstraintValidator; +import javax.validation.ConstraintValidatorContext; +import java.util.HashSet; +import java.util.Set; + +/** + * 自定义校验注解 -枚举值校验 + * + * @author fanzhenyu + * @date 2023-01-15 + */ +public class ListValueConstraintValidator implements ConstraintValidator<ListIntValue, Integer> { + private Set<Integer> set = new HashSet<>(); + // 是否必填 + private boolean require; + + @Override + public void initialize(ListIntValue constraintAnnotation) { + for (int i : constraintAnnotation.enumValue()) { + set.add(i); + } + require = constraintAnnotation.require(); + } + + /** + * 判断是否通过校验 + * + * @param value 传入的值 + * @param context + * @return + */ + @Override + public boolean isValid(Integer value, ConstraintValidatorContext context) { + if (!require && value == null) { + return true; + } + return set.contains(value); + } + +} diff --git a/src/main/java/com/canrd/webmagic/common/jsr303/OperateGroup.java b/src/main/java/com/canrd/webmagic/common/jsr303/OperateGroup.java new file mode 100644 index 0000000..7801802 --- /dev/null +++ b/src/main/java/com/canrd/webmagic/common/jsr303/OperateGroup.java @@ -0,0 +1,77 @@ +package com.canrd.webmagic.common.jsr303; + + +public class OperateGroup { + /** + * 新增校验 + */ + public interface Save { + } + + /** + * 删除校验 + */ + public interface Delete { + } + + /** + * 修改校验 + */ + public interface Update { + } + + /** + * 查询列表 + */ + public interface List { + } + + /** + * 分页查询 + */ + public interface Page { + } + + /** + * 长度校验 + */ + public interface Length { + } + + /** + * 格式校验 + */ + public interface Format { + } + + /** + * 详情 + */ + public interface Detail { + } + + /** + * 单条 + */ + public interface One { + } + + + public interface Trace { + + } + + /** + * 批量查询 + */ + public interface BatchQuery { + } + + + /** + * 导出 + */ + public interface Export { + } + +} diff --git a/src/main/java/com/canrd/webmagic/common/jsr303/annotation/ListIntValue.java b/src/main/java/com/canrd/webmagic/common/jsr303/annotation/ListIntValue.java new file mode 100644 index 0000000..2648f06 --- /dev/null +++ b/src/main/java/com/canrd/webmagic/common/jsr303/annotation/ListIntValue.java @@ -0,0 +1,32 @@ +package com.canrd.webmagic.common.jsr303.annotation; + + +import com.canrd.webmagic.common.jsr303.ListValueConstraintValidator; + +import javax.validation.Constraint; +import javax.validation.Payload; +import java.lang.annotation.*; + +/** + * @date 2023-01-15 + */ +@Constraint(validatedBy = {ListValueConstraintValidator.class}) +@Documented +@Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.PARAMETER, ElementType.TYPE_USE}) +@Retention(RetentionPolicy.RUNTIME) +public @interface ListIntValue { + + + // 配置文件中错误提示信息的名称 + String message() default "非法的类型"; + + Class<?>[] groups() default {}; + + Class<? extends Payload>[] payload() default {}; + + // 自定义值的类型 + int[] enumValue() default {}; + + // 是否必填,默认允许为空 + boolean require() default false; +} diff --git a/src/main/java/com/canrd/webmagic/common/utils/DateUtil.java b/src/main/java/com/canrd/webmagic/common/utils/DateUtil.java new file mode 100644 index 0000000..c00c04f --- /dev/null +++ b/src/main/java/com/canrd/webmagic/common/utils/DateUtil.java @@ -0,0 +1,591 @@ +package com.canrd.webmagic.common.utils; + + +import java.text.DateFormat; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.time.*; +import java.time.format.DateTimeFormatter; +import java.time.temporal.ChronoField; +import java.time.temporal.TemporalAdjusters; +import java.util.Date; +import java.util.Locale; +import java.util.TimeZone; + +/** + * @Date: 2020/9/1 + * 基于Java8的时间工具类 + */ +public class DateUtil { + /** + * 例如:2018-12-28 + */ + public static final String DATE = "yyyy-MM-dd"; + /** + * 例如:2018.12.28 + */ + public static final String DATE_WITH_POINT = "yyyy.MM.dd"; + /** + * 例如:2018-12-28 10:00:00 + */ + public static final String DATE_TIME = "yyyy-MM-dd HH:mm:ss"; + /** + * 例如:2018-12-28 10:00:00:215 + */ + public static final String DATE_TIME_MS = "yyyy-MM-dd HH:mm:ss.SSS"; + /** + * 例如:10:00:00 + */ + public static final String TIME = "HHmmss"; + /** + * 例如:10:00:00 + */ + public static final String TIME_HAVE_SECOND = "HH:mm:ss"; + /** + * 例如:10:00 + */ + public static final String TIME_WITHOUT_SECOND = "HH:mm"; + + /** + * 例如:2018-12-28 10:00 + */ + public static final String DATE_TIME_WITHOUT_SECONDS = "yyyy-MM-dd HH:mm"; + + /** + * 例如 2020/04/16 12:23:0 + */ + public static final String DATE_TIME_WITHOUT_SECONDS_OTHER = "yyyy/MM/dd HH:mm:ss"; + + /** + * 例如:2020年04月23日 10:00 + */ + public static final String DATE_CHINESE_TIME = "yyyy年MM月dd日 HH:mm"; + + /** + * 例如:20181228100000215 + */ + public static final String MAX_DATE = "9999-12-31 59:59:59"; + + /** + * 例如:20210822082134 + */ + public static final String DATE_TIME_HMS = "yyyyMMddHHmmssSSS"; + + public static final String YYMMDD = "yyyyMMdd"; + + public static final String YMMDD = "yyMMdd"; + + public static final String YYMMDDHHMMSS = "yyMMddHHmmss"; + + public static final String MONTH_TIME = "MM-dd HH:mm:ss"; + + /** + * 例如:2018-12-28 10:00 + */ + public static final String DDMMYYYYHHMMSS = "dd/MM/yyyy HH:mm:ss"; + + public static LocalDateTime getCurrentTime() { + LocalDateTime now = LocalDateTime.now(); + return now; + } + + public static Date getNextDay() { + LocalDateTime now = LocalDateTime.now(); + LocalDateTime tomorrow = now.minusDays(-1); + return Date.from(tomorrow.atZone(ZoneId.systemDefault()).toInstant()); + } + + public static Date getAppointDay(Integer count) { + LocalDateTime now = LocalDateTime.now(); + LocalDateTime tomorrow = now.minusDays(count); + return Date.from(tomorrow.atZone(ZoneId.systemDefault()).toInstant()); + } + + /** + * 获取年 + * + * @return 年 + */ + public static int getYear() { + LocalDateTime localDateTime = LocalDateTime.now(); + return localDateTime.get(ChronoField.YEAR); + } + + /** + * 获取月份 + * + * @return 月份 + */ + public static int getMonth() { + LocalDateTime localDateTime = LocalDateTime.now(); + return localDateTime.get(ChronoField.MONTH_OF_YEAR); + } + + /** + * 获取某月的第几天 + * + * @return 几号 + */ + public static int getMonthOfDay() { + LocalDateTime localDateTime = LocalDateTime.now(); + return localDateTime.get(ChronoField.DAY_OF_MONTH); + } + + /** + * 格式化日期为字符串 + * + * @param date date + * @param pattern 格式 + * @return 日期字符串 + */ + public static String formatOrElseBlank(Date date, String pattern) { + if (date == null || pattern == null) { + return ""; + } + Instant instant = date.toInstant(); + + LocalDateTime localDateTime = LocalDateTime.ofInstant(instant, ZoneId.systemDefault()); + + return localDateTime.format(DateTimeFormatter.ofPattern(pattern)); + } + + /** + * 格式化日期为字符串 + * + * @param date date + * @param pattern 格式 + * @return 日期字符串 + */ + public static String format(Date date, String pattern) { + + Instant instant = date.toInstant(); + + LocalDateTime localDateTime = LocalDateTime.ofInstant(instant, ZoneId.systemDefault()); + + return localDateTime.format(DateTimeFormatter.ofPattern(pattern)); + } + + public static String formatNow(String pattern) { + LocalDateTime localDateTime = LocalDateTime.now(); + + return localDateTime.format(DateTimeFormatter.ofPattern(pattern)); + } + + + public static String format(LocalDateTime date, String pattern) { + return date.format(DateTimeFormatter.ofPattern(pattern)); + } + + public static String format(LocalDate date, String pattern) { + return date.format(DateTimeFormatter.ofPattern(pattern)); + } + + /** + * 解析字符串日期为LocalDateTime + * + * @param dateStr 日期字符串 + * @param pattern 格式 + * @return Date + */ + public static LocalDateTime parse(String dateStr, String pattern) { + return LocalDateTime.parse(dateStr, DateTimeFormatter.ofPattern(pattern)); + } + + /** + * 解析字符串日期为LocalDateTime + * + * @param dateStr 日期字符串 + * @param pattern 格式 + * @return Date + */ + public static LocalDateTime parseStrByPattern(String dateStr, String pattern) { + try { + return parse(dateStr, pattern); + } catch (Exception e) { + return getCurrentTime(); + } + } + + /** + * 解析字符串日期为LocalDate + * + * @param dateStr + * @param pattern + * @return + */ + public static LocalDate parseDate(String dateStr, String pattern) { + return LocalDate.parse(dateStr, DateTimeFormatter.ofPattern(pattern)); + } + + /** + * @param dateTimeStr + * @param prePattern + * @param expectPattern + * @return + */ + public static String format(String dateTimeStr, String prePattern, String expectPattern) { + LocalDateTime localDateTime = parseDate(dateTimeStr, prePattern).atStartOfDay(); + return localDateTime.format(DateTimeFormatter.ofPattern(expectPattern)); + } + + /** + * String -> Date + * + * @param date + * @return + */ + public static Date parseStringToDate(String date, String pattern) { + SimpleDateFormat sdf = new SimpleDateFormat(pattern); + try { + return sdf.parse(date); + } catch (ParseException e) { + return null; + } + } + + /** + * 为Date增加分钟,减传负数 + * + * @param date 日期 + * @param plusMinutes 要增加的分钟数 + * @return 新的日期 + */ + public static Date addMinutes(Date date, Long plusMinutes) { + LocalDateTime dateTime = LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault()); + LocalDateTime newDateTime = dateTime.plusMinutes(plusMinutes); + return Date.from(newDateTime.atZone(ZoneId.systemDefault()).toInstant()); + } + + /** + * 增加时间 + * + * @param date date + * @param hour 要增加的小时数 + * @return new date + */ + public static Date addHour(Date date, Long hour) { + LocalDateTime dateTime = LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault()); + LocalDateTime localDateTime = dateTime.plusHours(hour); + return Date.from(localDateTime.atZone(ZoneId.systemDefault()).toInstant()); + } + + /** + * @return 返回当天的起始时间 + */ + public static Date getStartTime() { + + LocalDateTime now = LocalDateTime.now().withHour(0).withMinute(0).withSecond(0); + return localDateTime2Date(now); + } + + + /** + * @return 返回当天的结束时间 + */ + public static Date getEndTime() { + LocalDateTime now = LocalDateTime.now().withHour(23).withMinute(59).withSecond(59).withNano(999999999); + return localDateTime2Date(now); + } + + /** + * @return 获取入参当天结束时间 + */ + public static LocalDateTime getEndTime(LocalDate localDate) { + return localDate.atTime(23, 59, 59, 999999999); + } + + /** + * 减月份 + * + * @param monthsToSubtract 月份 + * @return Date + */ + public static Date minusMonths(long monthsToSubtract) { + LocalDate localDate = LocalDate.now().minusMonths(monthsToSubtract); + + return localDate2Date(localDate); + } + + /** + * LocalDate类型转为Date + * + * @param localDate LocalDate object + * @return Date object + */ + public static Date localDate2Date(LocalDate localDate) { + + ZonedDateTime zonedDateTime = localDate.atStartOfDay(ZoneId.systemDefault()); + + return Date.from(zonedDateTime.toInstant()); + } + + /** + * Date类型转为LocalDateTime + * + * @param date Date object + * @return localDate LocalDateTime + */ + public static LocalDateTime date2LocalDateTime(Date date) { + + Instant instant = date.toInstant(); + ZoneId zoneId = ZoneId.systemDefault(); + LocalDateTime localDateTime = instant.atZone(zoneId).toLocalDateTime(); + return localDateTime; + } + + + /** + * LocalDateTime类型转为Date + * + * @param localDateTime LocalDateTime object + * @return Date object + */ + public static Date localDateTime2Date(LocalDateTime localDateTime) { + return Date.from(localDateTime.atZone(ZoneId.systemDefault()).toInstant()); + } + + /** + * 查询当前年的第一天 + * + * @param pattern 格式,默认格式yyyyMMdd + * @return 20190101 + */ + public static String getFirstDayOfCurrentYear(String pattern) { + LocalDateTime localDateTime = LocalDateTime.now().withMonth(1).withDayOfMonth(1); + + if (StringUtils.isEmpty(pattern)) { + pattern = "yyyyMMdd"; + } + + return format(localDateTime2Date(localDateTime), pattern); + } + + /** + * 查询前一年最后一个月第一天 + * + * @param pattern 格式,默认格式yyyyMMdd + * @return 20190101 + */ + public static String getLastMonthFirstDayOfPreviousYear(String pattern) { + LocalDateTime localDateTime = LocalDateTime.now().minusYears(1L).withMonth(12).withDayOfMonth(1); + + if (StringUtils.isEmpty(pattern)) { + pattern = "yyyyMMdd"; + } + + return format(localDateTime2Date(localDateTime), pattern); + } + + /** + * 查询前一年最后一个月第一天 + * + * @param pattern 格式,默认格式yyyyMMdd + * @return 20190101 + */ + public static String getLastMonthLastDayOfPreviousYear(String pattern) { + LocalDateTime localDateTime = LocalDateTime.now().minusYears(1L).with(TemporalAdjusters.lastDayOfYear()); + + if (StringUtils.isEmpty(pattern)) { + pattern = "yyyyMMdd"; + } + + return format(localDateTime2Date(localDateTime), pattern); + } + + /** + * 获取当前日期 + * + * @param pattern 格式,默认格式yyyyMMdd + * @return 20190101 + */ + public static String getCurrentDay(String pattern) { + LocalDateTime localDateTime = LocalDateTime.now(); + + if (StringUtils.isEmpty(pattern)) { + pattern = "yyyyMMdd"; + } + + return format(localDateTime2Date(localDateTime), pattern); + } + + public static String getTransitionTime(LocalDateTime time, String pattern) { + if (StringUtils.isEmpty(pattern)) { + pattern = "yyyyMMdd"; + } + return format(localDateTime2Date(time), pattern); + } + + + /** + * 用于判断两个时间段有没有重合, 重合: true,未重合: false + * + * @param aStartTime + * @param aEndTime + * @param bStartTime + * @param bEndTime + * @return + * @author A80068 + */ + public static boolean judgeTimeSlotCoincidence(LocalDateTime aStartTime, LocalDateTime aEndTime, LocalDateTime bStartTime, LocalDateTime bEndTime) { + /** + * 旧版 + */ + //boolean flag; + //if(aStartTime.isAfter(bStartTime)){ + // if (aStartTime.isBefore(bEndTime) || aStartTime.isEqual(bEndTime)){ + // flag = true; + // }else{ + // flag = false; + // } + //}else if(aStartTime.isBefore(bStartTime)){ + // if(aEndTime.isAfter(bStartTime) || aEndTime.isEqual(bStartTime)){ + // flag = true; + // }else{ + // flag = false; + // } + //}else{ + // flag = true; + //} + //return flag; + /** + * 优化版 + */ + boolean flag = false; + if ((aStartTime.isBefore(bEndTime) || aStartTime.isEqual(bEndTime))) { + if ((aEndTime.isAfter(bStartTime)) || aEndTime.isEqual(bStartTime)) { + flag = true; + } + } + return flag; + } + + /** + * @param aStartTimeStr + * @param aEndTimeStr + * @param bStartTimeStr + * @param bEndTimeStr + * @return + * @author A80068 + */ + public static boolean judgeTimeSlotCoincidenceString(String aStartTimeStr, String aEndTimeStr, String bStartTimeStr, String bEndTimeStr) { + + LocalDateTime aStartTime = parse(aStartTimeStr, DATE_TIME); + LocalDateTime aEndTime = parse(aEndTimeStr, DATE_TIME); + LocalDateTime bStartTime = parse(bStartTimeStr, DATE_TIME); + LocalDateTime bEndTime = parse(bEndTimeStr, DATE_TIME); + + return judgeTimeSlotCoincidence(aStartTime, aEndTime, bStartTime, bEndTime); + } + + /** + * @param strGMT + * @return + * @throws ParseException + * @throws ParseException + */ + public static Date getCST(String strGMT) throws ParseException, ParseException { + DateFormat df = new SimpleDateFormat("EEE, d-MMM-yyyy HH:mm:ss z", Locale.ENGLISH); + return df.parse(strGMT); + } + + /** + * @param dateCST + * @return + */ + public static Date getGMT(Date dateCST) throws ParseException { + DateFormat df = new SimpleDateFormat("EEE, d-MMM-yyyy HH:mm:ss z", Locale.ENGLISH); + df.setTimeZone(TimeZone.getTimeZone("GMT")); // modify Time Zone. + return df.parse(df.format(dateCST)); + } + + /** + * LocalDateTime转化为cron表达式 + * + * @param dateTime + * @return + */ + public static String dateToCron(LocalDateTime dateTime) { + if (dateTime == null) { + return ""; + } + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("ss mm HH dd MM ? yyyy"); + return formatter.format(dateTime); + } + + /** + * cron表达式转为LocalDateTime + * + * @param cron + * @return + */ + public static LocalDateTime cronToDate(String cron) { + if (StringUtils.isNotBlank(cron)) { + return null; + } + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("ss mm HH dd MM ? yyyy"); + return LocalDateTime.parse(cron, formatter); + } + + public static String timeToString(long time) { + DateTimeFormatter ftf = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); + return ftf.format(LocalDateTime.ofInstant(Instant.ofEpochMilli(time), ZoneId.systemDefault())); + } + + public static String timeToString(String time) { + try { + return timeToString(Long.parseLong(time)); + } catch (Exception e) { + } + return time; + } + + /** + * 比较时间大小 + * + * @param date1 + * @param date2 + * @return + */ + public static int compareDate(Date date1, Date date2) { + if (date1 == null || date2 == null) { + return -2; + } + if (date1.getTime() < date2.getTime()) { + return -1; + } else if (date1.getTime() > date2.getTime()) { + return 1; + } + return 0; + } + + /** + * 判斷两个时间是否在同一分钟 + */ + public static boolean isMinuteIdentical(LocalDateTime date1, LocalDateTime date2) { + return date1.getYear() == date2.getYear() && date1.getMonth() == date2.getMonth() && date1.getMinute() == date2.getMinute(); + } + + /** + * 时区 + */ + public static class Zone { + public static final String GMT8 = "GMT+8"; + } + + public static LocalDateTime parseProductDate(String dateStr) { + if (StringUtils.isBlank(dateStr)) { + return null; + } + if (dateStr.length() == 8) { + dateStr = dateStr + " 00:00:00"; + } + if (dateStr.contains("/")) { + dateStr = dateStr.replaceAll("/", "-"); + } + try { + return LocalDateTime.parse(dateStr, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")); + } catch (Exception e) { + return null; + } + } +} diff --git a/src/main/java/com/canrd/webmagic/common/utils/DateUtils.java b/src/main/java/com/canrd/webmagic/common/utils/DateUtils.java new file mode 100644 index 0000000..8d34fb2 --- /dev/null +++ b/src/main/java/com/canrd/webmagic/common/utils/DateUtils.java @@ -0,0 +1,569 @@ +package com.canrd.webmagic.common.utils; + + +import java.text.DateFormat; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.time.*; +import java.time.format.DateTimeFormatter; +import java.time.temporal.ChronoField; +import java.time.temporal.TemporalAdjusters; +import java.util.Date; +import java.util.Locale; +import java.util.TimeZone; + +/** + * @Date: 2020/9/1 + * 基于Java8的时间工具类 + */ +public class DateUtils { + /** + * 例如:2018-12-28 + */ + public static final String DATE = "yyyy-MM-dd"; + /** + * 例如:2018.12.28 + */ + public static final String DATE_WITH_POINT = "yyyy.MM.dd"; + /** + * 例如:2018-12-28 10:00:00 + */ + public static final String DATE_TIME = "yyyy-MM-dd HH:mm:ss"; + /** + * 例如:2018-12-28 10:00:00:215 + */ + public static final String DATE_TIME_MS = "yyyy-MM-dd HH:mm:ss.SSS"; + /** + * 例如:10:00:00 + */ + public static final String TIME = "HHmmss"; + /** + * 例如:10:00:00 + */ + public static final String TIME_HAVE_SECOND = "HH:mm:ss"; + /** + * 例如:10:00 + */ + public static final String TIME_WITHOUT_SECOND = "HH:mm"; + + /** + * 例如:2018-12-28 10:00 + */ + public static final String DATE_TIME_WITHOUT_SECONDS = "yyyy-MM-dd HH:mm"; + + /** + * 例如 2020/04/16 12:23:0 + */ + public static final String DATE_TIME_WITHOUT_SECONDS_OTHER = "yyyy/MM/dd HH:mm:ss"; + + /** + * 例如:2020年04月23日 10:00 + */ + public static final String DATE_CHINESE_TIME = "yyyy年MM月dd日 HH:mm"; + + /** + * 例如:20181228100000215 + */ + public static final String MAX_DATE = "9999-12-31 59:59:59"; + + /** + * 例如:20210822082134 + */ + public static final String DATE_TIME_HMS = "yyyyMMddHHmmssSSS"; + + public static final String YYMMDD = "yyyyMMdd"; + + public static final String YMMDD = "yyMMdd"; + + public static final String YYMMDDHHMMSS = "yyMMddHHmmss"; + + public static final String MONTH_TIME = "MM-dd HH:mm:ss"; + + public static LocalDateTime getCurrentTime() { + LocalDateTime now = LocalDateTime.now(); + return now; + } + + public static Date getNextDay() { + LocalDateTime now = LocalDateTime.now(); + LocalDateTime tomorrow = now.minusDays(-1); + return Date.from(tomorrow.atZone(ZoneId.systemDefault()).toInstant()); + } + + public static Date getAppointDay(Integer count) { + LocalDateTime now = LocalDateTime.now(); + LocalDateTime tomorrow = now.minusDays(count); + return Date.from(tomorrow.atZone(ZoneId.systemDefault()).toInstant()); + } + + /** + * 获取年 + * + * @return 年 + */ + public static int getYear() { + LocalDateTime localDateTime = LocalDateTime.now(); + return localDateTime.get(ChronoField.YEAR); + } + + /** + * 获取月份 + * + * @return 月份 + */ + public static int getMonth() { + LocalDateTime localDateTime = LocalDateTime.now(); + return localDateTime.get(ChronoField.MONTH_OF_YEAR); + } + + /** + * 获取某月的第几天 + * + * @return 几号 + */ + public static int getMonthOfDay() { + LocalDateTime localDateTime = LocalDateTime.now(); + return localDateTime.get(ChronoField.DAY_OF_MONTH); + } + + /** + * 格式化日期为字符串 + * + * @param date date + * @param pattern 格式 + * @return 日期字符串 + */ + public static String formatOrElseBlank(Date date, String pattern) { + if (date == null || pattern == null) { + return ""; + } + Instant instant = date.toInstant(); + + LocalDateTime localDateTime = LocalDateTime.ofInstant(instant, ZoneId.systemDefault()); + + return localDateTime.format(DateTimeFormatter.ofPattern(pattern)); + } + + /** + * 格式化日期为字符串 + * + * @param date date + * @param pattern 格式 + * @return 日期字符串 + */ + public static String format(Date date, String pattern) { + + Instant instant = date.toInstant(); + + LocalDateTime localDateTime = LocalDateTime.ofInstant(instant, ZoneId.systemDefault()); + + return localDateTime.format(DateTimeFormatter.ofPattern(pattern)); + } + + public static String formatNow(String pattern) { + LocalDateTime localDateTime = LocalDateTime.now(); + + return localDateTime.format(DateTimeFormatter.ofPattern(pattern)); + } + + + public static String format(LocalDateTime date, String pattern) { + return date.format(DateTimeFormatter.ofPattern(pattern)); + } + + public static String format(LocalDate date, String pattern) { + return date.format(DateTimeFormatter.ofPattern(pattern)); + } + + /** + * 解析字符串日期为LocalDateTime + * + * @param dateStr 日期字符串 + * @param pattern 格式 + * @return Date + */ + public static LocalDateTime parse(String dateStr, String pattern) { + return LocalDateTime.parse(dateStr, DateTimeFormatter.ofPattern(pattern)); + } + + /** + * 解析字符串日期为LocalDateTime + * + * @param dateStr 日期字符串 + * @param pattern 格式 + * @return Date + */ + public static LocalDateTime parseStrByPattern(String dateStr, String pattern) { + try { + return parse(dateStr, pattern); + } catch (Exception e) { + return getCurrentTime(); + } + } + + /** + * 解析字符串日期为LocalDate + * + * @param dateStr + * @param pattern + * @return + */ + public static LocalDate parseDate(String dateStr, String pattern) { + return LocalDate.parse(dateStr, DateTimeFormatter.ofPattern(pattern)); + } + + /** + * @param dateTimeStr + * @param prePattern + * @param expectPattern + * @return + */ + public static String format(String dateTimeStr, String prePattern, String expectPattern) { + LocalDateTime localDateTime = parseDate(dateTimeStr, prePattern).atStartOfDay(); + return localDateTime.format(DateTimeFormatter.ofPattern(expectPattern)); + } + + /** + * String -> Date + * + * @param date + * @return + */ + public static Date parseStringToDate(String date, String pattern) { + SimpleDateFormat sdf = new SimpleDateFormat(pattern); + try { + return sdf.parse(date); + } catch (ParseException e) { + return null; + } + } + + /** + * 为Date增加分钟,减传负数 + * + * @param date 日期 + * @param plusMinutes 要增加的分钟数 + * @return 新的日期 + */ + public static Date addMinutes(Date date, Long plusMinutes) { + LocalDateTime dateTime = LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault()); + LocalDateTime newDateTime = dateTime.plusMinutes(plusMinutes); + return Date.from(newDateTime.atZone(ZoneId.systemDefault()).toInstant()); + } + + /** + * 增加时间 + * + * @param date date + * @param hour 要增加的小时数 + * @return new date + */ + public static Date addHour(Date date, Long hour) { + LocalDateTime dateTime = LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault()); + LocalDateTime localDateTime = dateTime.plusHours(hour); + return Date.from(localDateTime.atZone(ZoneId.systemDefault()).toInstant()); + } + + /** + * @return 返回当天的起始时间 + */ + public static Date getStartTime() { + + LocalDateTime now = LocalDateTime.now().withHour(0).withMinute(0).withSecond(0); + return localDateTime2Date(now); + } + + + /** + * @return 返回当天的结束时间 + */ + public static Date getEndTime() { + LocalDateTime now = LocalDateTime.now().withHour(23).withMinute(59).withSecond(59).withNano(999999999); + return localDateTime2Date(now); + } + + /** + * @return 获取入参当天结束时间 + */ + public static LocalDateTime getEndTime(LocalDate localDate) { + return localDate.atTime(23, 59, 59, 999999999); + } + + /** + * 减月份 + * + * @param monthsToSubtract 月份 + * @return Date + */ + public static Date minusMonths(long monthsToSubtract) { + LocalDate localDate = LocalDate.now().minusMonths(monthsToSubtract); + + return localDate2Date(localDate); + } + + /** + * LocalDate类型转为Date + * + * @param localDate LocalDate object + * @return Date object + */ + public static Date localDate2Date(LocalDate localDate) { + + ZonedDateTime zonedDateTime = localDate.atStartOfDay(ZoneId.systemDefault()); + + return Date.from(zonedDateTime.toInstant()); + } + + /** + * Date类型转为LocalDateTime + * + * @param date Date object + * @return localDate LocalDateTime + */ + public static LocalDateTime date2LocalDateTime(Date date) { + + Instant instant = date.toInstant(); + ZoneId zoneId = ZoneId.systemDefault(); + LocalDateTime localDateTime = instant.atZone(zoneId).toLocalDateTime(); + return localDateTime; + } + + + /** + * LocalDateTime类型转为Date + * + * @param localDateTime LocalDateTime object + * @return Date object + */ + public static Date localDateTime2Date(LocalDateTime localDateTime) { + return Date.from(localDateTime.atZone(ZoneId.systemDefault()).toInstant()); + } + + /** + * 查询当前年的第一天 + * + * @param pattern 格式,默认格式yyyyMMdd + * @return 20190101 + */ + public static String getFirstDayOfCurrentYear(String pattern) { + LocalDateTime localDateTime = LocalDateTime.now().withMonth(1).withDayOfMonth(1); + + if (StringUtils.isEmpty(pattern)) { + pattern = "yyyyMMdd"; + } + + return format(localDateTime2Date(localDateTime), pattern); + } + + /** + * 查询前一年最后一个月第一天 + * + * @param pattern 格式,默认格式yyyyMMdd + * @return 20190101 + */ + public static String getLastMonthFirstDayOfPreviousYear(String pattern) { + LocalDateTime localDateTime = LocalDateTime.now().minusYears(1L).withMonth(12).withDayOfMonth(1); + + if (StringUtils.isEmpty(pattern)) { + pattern = "yyyyMMdd"; + } + + return format(localDateTime2Date(localDateTime), pattern); + } + + /** + * 查询前一年最后一个月第一天 + * + * @param pattern 格式,默认格式yyyyMMdd + * @return 20190101 + */ + public static String getLastMonthLastDayOfPreviousYear(String pattern) { + LocalDateTime localDateTime = LocalDateTime.now().minusYears(1L).with(TemporalAdjusters.lastDayOfYear()); + + if (StringUtils.isEmpty(pattern)) { + pattern = "yyyyMMdd"; + } + + return format(localDateTime2Date(localDateTime), pattern); + } + + /** + * 获取当前日期 + * + * @param pattern 格式,默认格式yyyyMMdd + * @return 20190101 + */ + public static String getCurrentDay(String pattern) { + LocalDateTime localDateTime = LocalDateTime.now(); + + if (StringUtils.isEmpty(pattern)) { + pattern = "yyyyMMdd"; + } + + return format(localDateTime2Date(localDateTime), pattern); + } + + public static String getTransitionTime(LocalDateTime time, String pattern) { + if (StringUtils.isEmpty(pattern)) { + pattern = "yyyyMMdd"; + } + return format(localDateTime2Date(time), pattern); + } + + + /** + * 用于判断两个时间段有没有重合, 重合: true,未重合: false + * + * @param aStartTime + * @param aEndTime + * @param bStartTime + * @param bEndTime + * @return + * @author A80068 + */ + public static boolean judgeTimeSlotCoincidence(LocalDateTime aStartTime, LocalDateTime aEndTime, LocalDateTime bStartTime, LocalDateTime bEndTime) { + /** + * 旧版 + */ + //boolean flag; + //if(aStartTime.isAfter(bStartTime)){ + // if (aStartTime.isBefore(bEndTime) || aStartTime.isEqual(bEndTime)){ + // flag = true; + // }else{ + // flag = false; + // } + //}else if(aStartTime.isBefore(bStartTime)){ + // if(aEndTime.isAfter(bStartTime) || aEndTime.isEqual(bStartTime)){ + // flag = true; + // }else{ + // flag = false; + // } + //}else{ + // flag = true; + //} + //return flag; + /** + * 优化版 + */ + boolean flag = false; + if ((aStartTime.isBefore(bEndTime) || aStartTime.isEqual(bEndTime))) { + if ((aEndTime.isAfter(bStartTime)) || aEndTime.isEqual(bStartTime)) { + flag = true; + } + } + return flag; + } + + /** + * @param aStartTimeStr + * @param aEndTimeStr + * @param bStartTimeStr + * @param bEndTimeStr + * @return + * @author A80068 + */ + public static boolean judgeTimeSlotCoincidenceString(String aStartTimeStr, String aEndTimeStr, String bStartTimeStr, String bEndTimeStr) { + + LocalDateTime aStartTime = parse(aStartTimeStr, DATE_TIME); + LocalDateTime aEndTime = parse(aEndTimeStr, DATE_TIME); + LocalDateTime bStartTime = parse(bStartTimeStr, DATE_TIME); + LocalDateTime bEndTime = parse(bEndTimeStr, DATE_TIME); + + return judgeTimeSlotCoincidence(aStartTime, aEndTime, bStartTime, bEndTime); + } + + /** + * @param strGMT + * @return + * @throws ParseException + * @throws ParseException + */ + public static Date getCST(String strGMT) throws ParseException, ParseException { + DateFormat df = new SimpleDateFormat("EEE, d-MMM-yyyy HH:mm:ss z", Locale.ENGLISH); + return df.parse(strGMT); + } + + /** + * @param dateCST + * @return + */ + public static Date getGMT(Date dateCST) throws ParseException { + DateFormat df = new SimpleDateFormat("EEE, d-MMM-yyyy HH:mm:ss z", Locale.ENGLISH); + df.setTimeZone(TimeZone.getTimeZone("GMT")); // modify Time Zone. + return df.parse(df.format(dateCST)); + } + + /** + * LocalDateTime转化为cron表达式 + * + * @param dateTime + * @return + */ + public static String dateToCron(LocalDateTime dateTime) { + if (dateTime == null) { + return ""; + } + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("ss mm HH dd MM ? yyyy"); + return formatter.format(dateTime); + } + + /** + * cron表达式转为LocalDateTime + * + * @param cron + * @return + */ + public static LocalDateTime cronToDate(String cron) { + if (StringUtils.isNotBlank(cron)) { + return null; + } + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("ss mm HH dd MM ? yyyy"); + return LocalDateTime.parse(cron, formatter); + } + + public static String timeToString(long time) { + DateTimeFormatter ftf = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); + return ftf.format(LocalDateTime.ofInstant(Instant.ofEpochMilli(time), ZoneId.systemDefault())); + } + + public static String timeToString(String time) { + try { + return timeToString(Long.parseLong(time)); + } catch (Exception e) { + } + return time; + } + + /** + * 比较时间大小 + * + * @param date1 + * @param date2 + * @return + */ + public static int compareDate(Date date1, Date date2) { + if (date1 == null || date2 == null) { + return -2; + } + if (date1.getTime() < date2.getTime()) { + return -1; + } else if (date1.getTime() > date2.getTime()) { + return 1; + } + return 0; + } + + /** + * 判斷两个时间是否在同一分钟 + */ + public static boolean isMinuteIdentical(LocalDateTime date1, LocalDateTime date2) { + return date1.getYear() == date2.getYear() && date1.getMonth() == date2.getMonth() && date1.getMinute() == date2.getMinute(); + } + + /** + * 时区 + */ + public static class Zone { + public static final String GMT8 = "GMT+8"; + } +} diff --git a/src/main/java/com/canrd/webmagic/common/utils/EncryptUtils.java b/src/main/java/com/canrd/webmagic/common/utils/EncryptUtils.java new file mode 100644 index 0000000..73b8f52 --- /dev/null +++ b/src/main/java/com/canrd/webmagic/common/utils/EncryptUtils.java @@ -0,0 +1,84 @@ +package com.canrd.webmagic.common.utils; + +import javax.crypto.Cipher; +import javax.crypto.SecretKey; +import javax.crypto.SecretKeyFactory; +import javax.crypto.spec.DESKeySpec; +import javax.crypto.spec.IvParameterSpec; +import java.nio.charset.StandardCharsets; + +/** + * 加密 + * + * @date 2018-11-23 + */ +public class EncryptUtils { + + private static String strParam = "Passw0rd"; + + private static Cipher cipher; + + private static IvParameterSpec iv = new IvParameterSpec(strParam.getBytes(StandardCharsets.UTF_8)); + + private static DESKeySpec getDesKeySpec(String source) throws Exception { + if (source == null || source.length() == 0) { + return null; + } + cipher = Cipher.getInstance("DES/CBC/PKCS5Padding"); + String strKey = "Passw0rd"; + return new DESKeySpec(strKey.getBytes(StandardCharsets.UTF_8)); + } + + /** + * 对称加密 + */ + public static String desEncrypt(String source) throws Exception { + DESKeySpec desKeySpec = getDesKeySpec(source); + SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES"); + SecretKey secretKey = keyFactory.generateSecret(desKeySpec); + cipher.init(Cipher.ENCRYPT_MODE, secretKey, iv); + return byte2hex( + cipher.doFinal(source.getBytes(StandardCharsets.UTF_8))).toUpperCase(); + } + + /** + * 对称解密 + */ + public static String desDecrypt(String source) throws Exception { + byte[] src = hex2byte(source.getBytes()); + DESKeySpec desKeySpec = getDesKeySpec(source); + SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES"); + SecretKey secretKey = keyFactory.generateSecret(desKeySpec); + cipher.init(Cipher.DECRYPT_MODE, secretKey, iv); + byte[] retByte = cipher.doFinal(src); + return new String(retByte); + } + + private static String byte2hex(byte[] inStr) { + String stmp; + StringBuilder out = new StringBuilder(inStr.length * 2); + for (byte b : inStr) { + stmp = Integer.toHexString(b & 0xFF); + if (stmp.length() == 1) { + // 如果是0至F的单位字符串,则添加0 + out.append("0").append(stmp); + } else { + out.append(stmp); + } + } + return out.toString(); + } + + private static byte[] hex2byte(byte[] b) { + int size = 2; + if ((b.length % size) != 0) { + throw new IllegalArgumentException("长度不是偶数"); + } + byte[] b2 = new byte[b.length / 2]; + for (int n = 0; n < b.length; n += size) { + String item = new String(b, n, 2); + b2[n / 2] = (byte) Integer.parseInt(item, 16); + } + return b2; + } +} diff --git a/src/main/java/com/canrd/webmagic/common/utils/FileUtil.java b/src/main/java/com/canrd/webmagic/common/utils/FileUtil.java new file mode 100644 index 0000000..f00a2b2 --- /dev/null +++ b/src/main/java/com/canrd/webmagic/common/utils/FileUtil.java @@ -0,0 +1,360 @@ +package com.canrd.webmagic.common.utils; + +import cn.hutool.core.codec.Base64; +import cn.hutool.core.io.IoUtil; +import cn.hutool.core.util.IdUtil; +import cn.hutool.poi.excel.BigExcelWriter; +import cn.hutool.poi.excel.ExcelUtil; +import org.apache.poi.util.IOUtils; +import org.springframework.web.multipart.MultipartFile; + +import javax.activation.MimetypesFileTypeMap; +import javax.servlet.ServletOutputStream; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.*; +import java.security.MessageDigest; +import java.text.DecimalFormat; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.List; +import java.util.Map; + +/** + * File工具类,扩展 hutool 工具包 + * + * @date 2018-12-27 + */ +public class FileUtil extends cn.hutool.core.io.FileUtil { + + /** + * 定义GB的计算常量 + */ + private static final int GB = 1024 * 1024 * 1024; + /** + * 定义MB的计算常量 + */ + private static final int MB = 1024 * 1024; + /** + * 定义KB的计算常量 + */ + private static final int KB = 1024; + + /** + * 格式化小数 + */ + private static final DecimalFormat DF = new DecimalFormat("0.00"); + + /** + * MultipartFile转File + */ + public static File toFile(MultipartFile multipartFile) { + // 获取文件名 + String fileName = multipartFile.getOriginalFilename(); + // 获取文件后缀 + String prefix = "." + getExtensionName(fileName); + File file = null; + try { + // 用uuid作为文件名,防止生成的临时文件重复 + file = File.createTempFile(IdUtil.simpleUUID(), prefix); + // MultipartFile to File + multipartFile.transferTo(file); + } catch (IOException e) { + e.printStackTrace(); + } + return file; + } + + /** + * 获取文件扩展名,不带 . + */ + public static String getExtensionName(String filename) { + if ((filename != null) && (filename.length() > 0)) { + int dot = filename.lastIndexOf('.'); + if ((dot > -1) && (dot < (filename.length() - 1))) { + return filename.substring(dot + 1); + } + } + return filename; + } + + /** + * Java文件操作 获取不带扩展名的文件名 + */ + public static String getFileNameNoEx(String filename) { + if ((filename != null) && (filename.length() > 0)) { + int dot = filename.lastIndexOf('.'); + if ((dot > -1) && (dot < (filename.length()))) { + return filename.substring(0, dot); + } + } + return filename; + } + + /** + * 文件大小转换 + */ + public static String getSize(long size) { + 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; + } + + /** + * inputStream 转 File + */ + public static File inputStreamToFile(InputStream ins, String name) throws Exception { + File file = new File(System.getProperty("java.io.tmpdir") + File.separator + name); + if (file.exists()) { + return file; + } + OutputStream os = new FileOutputStream(file); + int bytesRead; + int len = 8192; + byte[] buffer = new byte[len]; + while ((bytesRead = ins.read(buffer, 0, len)) != -1) { + os.write(buffer, 0, bytesRead); + } + os.close(); + ins.close(); + return file; + } + + /** + * 将文件转换为byte数组,作为图片数据导入 + * + * @param file + * @return byte[] + */ + public static byte[] imageParseBytes(File file) { + FileInputStream fileInputStream = null; + try { + fileInputStream = new FileInputStream(file); + } catch (FileNotFoundException e) { + e.printStackTrace(); + } + return imageParseBytes(fileInputStream); + } + + /** + * 将流转换为byte数组,作为图片数据导入 + * + * @param fis + * @return byte[] + */ + public static byte[] imageParseBytes(InputStream fis) { + byte[] buffer = null; + ByteArrayOutputStream bos = null; + try { + bos = new ByteArrayOutputStream(1024); + byte[] b = new byte[1024]; + int n; + while ((n = fis.read(b)) != -1) { + bos.write(b, 0, n); + } + buffer = bos.toByteArray(); + } catch (Exception e) { + e.printStackTrace(); + } finally { + try { + fis.close(); + bos.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + return buffer; + } + + /** + * 将文件名解析成文件的上传路径 + */ + public static File upload(MultipartFile file, String filePath) { + Date date = new Date(); + SimpleDateFormat format = new SimpleDateFormat("yyyyMMddhhmmssS"); + String name = getFileNameNoEx(file.getOriginalFilename()); + String suffix = getExtensionName(file.getOriginalFilename()); + String nowStr = "-" + format.format(date); + try { + String fileName = name + nowStr + "." + suffix; + String path = filePath + fileName; + // getCanonicalFile 可解析正确各种路径 + File dest = new File(path).getCanonicalFile(); + // 检测是否存在目录 + if (!dest.getParentFile().exists()) { + dest.getParentFile().mkdirs(); + } + // 文件写入 + file.transferTo(dest); + return dest; + } catch (Exception e) { + e.printStackTrace(); + } + return null; + } + + public static String fileToBase64(File file) throws Exception { + FileInputStream inputFile = new FileInputStream(file); + String base64; + byte[] buffer = new byte[(int) file.length()]; + inputFile.read(buffer); + inputFile.close(); + base64 = Base64.encode(buffer); + return base64.replaceAll("[\\s*\t\n\r]", ""); + } + + /** + * 导出excel + */ + public static void downloadExcel(List<Map<String, Object>> list, HttpServletResponse response) throws IOException { + String tempPath = System.getProperty("java.io.tmpdir") + IdUtil.fastSimpleUUID() + ".xlsx"; + File file = new File(tempPath); + BigExcelWriter writer = ExcelUtil.getBigWriter(file); + // 一次性写出内容,使用默认样式,强制输出标题 + writer.write(list, true); + //response为HttpServletResponse对象 + response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8"); + //test.xls是弹出下载对话框的文件名,不能为中文,中文请自行编码 + response.setHeader("Content-Disposition", "attachment;filename=file.xlsx"); + ServletOutputStream out = response.getOutputStream(); + // 终止后删除临时文件 + file.deleteOnExit(); + writer.flush(out, true); + //此处记得关闭输出Servlet流 + IoUtil.close(out); + } + + public static String getFileType(String type) { + String documents = "txt doc pdf ppt pps xlsx xls docx"; + String music = "mp3 wav wma mpa ram ra aac aif m4a"; + String video = "avi mpg mpe mpeg asf wmv mov qt rm mp4 flv m4v webm ogv ogg"; + String image = "bmp dib pcp dif wmf gif jpg tif eps psd cdr iff tga pcd mpt png jpeg"; + if (image.contains(type)) { + return "images"; + } else if (documents.contains(type)) { + return "docs"; + } else if (music.contains(type)) { + return "musics"; + } else if (video.contains(type)) { + return "videos"; + } else { + return "others"; + } + } + + public static String getFileTypeByMimeType(String type) { + String mimeType = new MimetypesFileTypeMap().getContentType("." + type); + return mimeType.split("/")[0]; + } + + public static void checkSize(long maxSize, long size) { + // 1M + int len = 1024 * 1024; + if (size > (maxSize * len)) { + throw new RuntimeException("文件超出规定大小"); + } + } + + /** + * 判断两个文件是否相同 + */ + public static boolean check(File file1, File file2) { + String img1Md5 = getMd5(file1); + String img2Md5 = getMd5(file2); + return img1Md5.equals(img2Md5); + } + + /** + * 判断两个文件是否相同 + */ + public static boolean check(String file1Md5, String file2Md5) { + return file1Md5.equals(file2Md5); + } + + private static byte[] getByte(File file) { + // 得到文件长度 + byte[] b = new byte[(int) file.length()]; + try { + InputStream in = new FileInputStream(file); + try { + in.read(b); + } catch (IOException e) { + e.printStackTrace(); + } + } catch (FileNotFoundException e) { + e.printStackTrace(); + return null; + } + return b; + } + + private static String getMd5(byte[] bytes) { + // 16进制字符 + char[] hexDigits = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; + try { + MessageDigest mdTemp = MessageDigest.getInstance("MD5"); + mdTemp.update(bytes); + byte[] md = mdTemp.digest(); + int j = md.length; + char[] str = new char[j * 2]; + int k = 0; + // 移位 输出字符串 + for (byte byte0 : md) { + str[k++] = hexDigits[byte0 >>> 4 & 0xf]; + str[k++] = hexDigits[byte0 & 0xf]; + } + return new String(str); + } catch (Exception e) { + e.printStackTrace(); + } + return null; + } + + /** + * 下载文件 + * + * @param request / + * @param response / + * @param file / + */ + public static void downloadFile(HttpServletRequest request, HttpServletResponse response, File file, boolean deleteOnExit) { + response.setCharacterEncoding(request.getCharacterEncoding()); + response.setContentType("application/octet-stream"); + FileInputStream fis = null; + try { + fis = new FileInputStream(file); + response.setHeader("Content-Disposition", "attachment; filename=" + file.getName()); + IOUtils.copy(fis, response.getOutputStream()); + response.flushBuffer(); + } catch (Exception e) { + e.printStackTrace(); + } finally { + if (fis != null) { + try { + fis.close(); + if (deleteOnExit) { + file.deleteOnExit(); + } + } catch (IOException e) { + e.printStackTrace(); + } + } + } + } + + public static String getMd5(File file) { + return getMd5(getByte(file)); + } + +} diff --git a/src/main/java/com/canrd/webmagic/common/utils/JsonUtil.java b/src/main/java/com/canrd/webmagic/common/utils/JsonUtil.java new file mode 100644 index 0000000..9487fc4 --- /dev/null +++ b/src/main/java/com/canrd/webmagic/common/utils/JsonUtil.java @@ -0,0 +1,183 @@ +package com.canrd.webmagic.common.utils; + + +import com.alibaba.fastjson.JSONException; +import com.alibaba.fastjson.JSONObject; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.node.ArrayNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import lombok.extern.slf4j.Slf4j; + +import java.io.IOException; + +/** + * + * @author achims + * @date 2019/11/4 + * 用于转换成json字符串以及解析成对象 + */ +@Slf4j +public class JsonUtil { + + private static ObjectMapper mapper = new ObjectMapper(); + + static { + log.info("注册jackson-datatype-jsr310的java8时间处理模块"); + mapper.findAndRegisterModules(); + mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + mapper.getSerializerProvider().setNullValueSerializer(new JsonSerializer<Object>() { + @Override + public void serialize(Object paramT, JsonGenerator paramJsonGenerator, + SerializerProvider paramSerializerProvider) throws IOException { + //设置返回null转为 空字符串"" + paramJsonGenerator.writeString(""); + } + }); + } + + + + + /** + * Create empty json node + * + * @return + */ + public static ObjectNode createJsonNode() { + return mapper.createObjectNode(); + } + + /** + * Create empty json Array + * + * @return + */ + public static ArrayNode createJsonArray() { + return mapper.createArrayNode(); + + } + + /** + * Convert the object to corresponding json string + * + * @param obj + * @return + */ + public static String toJsonString(Object obj) { + try { + mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); + return mapper.writeValueAsString(obj); + } catch (JsonProcessingException e) { + return null; + } + } + + /** + * 转成格式化后的json,便于调试排错 + * @param obj + * @return + */ + public static String toJsonStringWithDefaultPretty(Object obj) { + try { + mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); + return mapper.writerWithDefaultPrettyPrinter().writeValueAsString(obj); + } catch (JsonProcessingException e) { + + return null; + } + } + + public static <T> T jsonToEntity(String json, Class<T> clazz) { + if (StringUtils.isEmpty(json)) return null; + try { + return mapper.readValue(json, clazz); + } catch (IOException e) { + e.printStackTrace(); + } + return null; + } + + /** + * 借助json做中转,实体类转实体类 + * @param obj 实体 + * @param clazz 目标类型 + * @param <T> 目标泛型 + * @return 结果 + */ + public static <T> T entityConvert(Object obj, Class<T> clazz) { + return jsonToEntity(toJsonString(obj), clazz); + } + + /** + * 带泛型返回不告警 + * @param json json字符 + * @param typeReference TypeReference + * @param <R> 返回类型 + * @param <T> 转换的类型 + * @return 转换结果 + * @author 刘迪榕 + * @version Date 2022-08-26 + */ + public static <R, T extends R> R jsonToEntity(String json, TypeReference<T> typeReference) { + if (StringUtils.isEmpty(json)) return null; + try { + return mapper.readValue(json, typeReference); + } catch (IOException e) { + e.printStackTrace(); + } + return null; + } + + public static JsonNode readStringAsJsonNode(String json) { + try { + return mapper.readValue(json, JsonNode.class); + } catch (IOException e) { + e.printStackTrace(); + } + return null; + } + + public static JsonNode valueToTree(Object value) { + return mapper.valueToTree(value); + } + + public static <T> T treeToValue(JsonNode jsonNode, Class<T> clazz) { + try { + return mapper.treeToValue(jsonNode, clazz); + } catch (IOException e) { + e.printStackTrace(); + } + return null; + } + + /** + * 是否爲json格式的數據 + * + * @param test + * @return + */ + public final static boolean isJSONValid(String test) { + try { + JSONObject.parseObject(test); + } catch (JSONException ex) { + try { + JSONObject.parseArray(test); + } catch (JSONException ex1) { + return false; + } + } + return true; + } + + public <T> T paraseJsonTOClass(String json, Class<T> cla) { + try { + return JSONObject.parseObject(json, cla); + } catch (JSONException ex) { + return null; + } + } +} diff --git a/src/main/java/com/canrd/webmagic/common/utils/LocalDateTimeUtil.java b/src/main/java/com/canrd/webmagic/common/utils/LocalDateTimeUtil.java new file mode 100644 index 0000000..e8c523c --- /dev/null +++ b/src/main/java/com/canrd/webmagic/common/utils/LocalDateTimeUtil.java @@ -0,0 +1,35 @@ +package com.canrd.webmagic.common.utils; + +import org.joda.time.DateTime; + +import java.time.LocalDateTime; + +/** + * @author: xms + * @description: TODO + * @date: 2023/1/16 16:35 + * @version: 1.0 + */ +public class LocalDateTimeUtil { + + /** + * @param dateTime + * @return + */ + public static LocalDateTime toLocalDateTime(DateTime dateTime) { + return LocalDateTime.of(dateTime.getYear(), dateTime.getMonthOfYear(), + dateTime.getDayOfMonth(), dateTime.getHourOfDay(), + dateTime.getMinuteOfHour(), dateTime.getSecondOfMinute()); + } + + /** + * + * @param localDateTime + * @return + */ + public static DateTime toDateTime(LocalDateTime localDateTime) { + return new DateTime().withYear(localDateTime.getYear()).withMonthOfYear(localDateTime.getMonthValue()) + .withDayOfMonth(localDateTime.getDayOfMonth()).withHourOfDay(localDateTime.getHour()) + .withMinuteOfHour(localDateTime.getMinute()).withSecondOfMinute(localDateTime.getSecond()); + } +} diff --git a/src/main/java/com/canrd/webmagic/common/utils/PageUtils.java b/src/main/java/com/canrd/webmagic/common/utils/PageUtils.java new file mode 100644 index 0000000..057cfe7 --- /dev/null +++ b/src/main/java/com/canrd/webmagic/common/utils/PageUtils.java @@ -0,0 +1,136 @@ +package com.canrd.webmagic.common.utils; + +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.canrd.webmagic.domain.vo.BasePageVO; +import org.springframework.util.CollectionUtils; + +import java.io.Serializable; +import java.util.*; + +/** + * @Author: dengbin + * @Date: 2020/9/11 + * 分页工具类 + */ + +public class PageUtils implements Serializable { + private static final long serialVersionUID = 4359709211352400087L; + private static final String PAGE_INDEX = "pageNo"; + private static final String PAGE_SIZE = "pageSize"; + private static final String TOTAL_ROWS = "total"; + private static final String TOTAL_PAGES = "pages"; + private static final String RECORDS = "records"; + + private static final int DEFAULT_PAGE_INDEX = 1; + private static final int DEFAULT_PAGE_SIZE = 10; + private static final int DEFAULT_MAP_CAPACITY = 5; + + /** + * 统一分页返回对象 + * + * @param list + * @return + */ + public static Map<String, Object> getUnifiedPageReturn(IPage list) { + if (list == null) { + return getUnifiedEmptyPage(DEFAULT_PAGE_INDEX, DEFAULT_PAGE_SIZE); + } + + Map<String, Object> resultMap = new HashMap<>(DEFAULT_MAP_CAPACITY); + resultMap.put(PAGE_INDEX, list.getCurrent()); + resultMap.put(PAGE_SIZE, list.getSize()); + resultMap.put(RECORDS, list.getRecords()); + resultMap.put(TOTAL_PAGES, list.getPages()); + resultMap.put(TOTAL_ROWS, list.getTotal()); + + return resultMap; + } + + /** + * 返回空分页信息给前端 + * 返回空或其他字段不满足前端要求,会报错 + * + * @return + */ + public static Map<String, Object> getUnifiedEmptyPage(int pageIndex, int pageSize) { + Map<String, Object> resultMap = new HashMap<>(DEFAULT_MAP_CAPACITY); + resultMap.put(PAGE_INDEX, pageIndex); + resultMap.put(PAGE_SIZE, pageSize); + resultMap.put(RECORDS, new ArrayList<>()); + resultMap.put(TOTAL_PAGES, 0); + resultMap.put(TOTAL_ROWS, 0); + return resultMap; + } + + public static Map<String, Object> getUnifiedEmptyPage(BasePageVO pageVO) { + return getUnifiedEmptyPage(pageVO.getCurrent(), pageVO.getSize()); + } + + + /** + * 将旧的分页转换成新的分页对象 + * + * @param oldPage 旧的分页对象,例如:xxDo的分页对象 + * @param records 新的分页对象,例如:xxVo的分页对象 + * @param <T> 新的分页对象的类型 + * @return 统一的返回的分页对象 + */ + public static <T> Map<String, Object> transferPage(IPage<?> oldPage, List<T> records) { + Page<T> newPage = new Page<>(); + newPage.setCurrent(oldPage.getCurrent()); + newPage.setSize(oldPage.getSize()); + newPage.setRecords(records); + newPage.setPages(oldPage.getPages()); + newPage.setTotal(oldPage.getTotal()); + return getUnifiedPageReturn(newPage); + } + + /** + * 统一分页返回对象 + * + * @param records 分页数据 + * @param pageVO 分页参数 + * @return + */ + public static Map<String, Object> getPageReturn(List records, BasePageVO pageVO) { + if (records == null) { + return getUnifiedEmptyPage(DEFAULT_PAGE_INDEX, DEFAULT_PAGE_SIZE); + } + if (CollectionUtils.isEmpty(records)) { + return getUnifiedEmptyPage(Math.toIntExact(pageVO.getCurrent()), Math.toIntExact(pageVO.getSize())); + } + Map<String, Object> resultMap = new HashMap<>(DEFAULT_MAP_CAPACITY); + resultMap.put(PAGE_INDEX, pageVO.getCurrent()); + resultMap.put(PAGE_SIZE, pageVO.getSize()); + resultMap.put(RECORDS, records); + resultMap.put(TOTAL_PAGES, (pageVO.getTotal() + pageVO.getSize() - 1) / pageVO.getSize()); + resultMap.put(TOTAL_ROWS, pageVO.getTotal()); + + return resultMap; + } + + public static <T> Page<T> getRAMPageReturn(Integer current, Integer size, List<T> records) { + if (Objects.isNull(current)) { + current = 1; + } + if (Objects.isNull(size)) { + size = 10; + } + int startIndex = (current - 1) * size; + int endIndex = Math.min(current * size, records.size()); + if (startIndex > endIndex) { + startIndex = endIndex; + } + Page<T> page = new Page<>(); + page.setCurrent(current); + page.setSize(size); + page.setRecords(records.subList(startIndex, endIndex)); + int totalPages = size == 0 ? 0 : records.size() % size == 0 ? (records.size() / size) : (records.size() / size + 1); + page.setPages(totalPages); + page.setTotal(records.size()); + return page; + } + + +} diff --git a/src/main/java/com/canrd/webmagic/common/utils/RedisUtil.java b/src/main/java/com/canrd/webmagic/common/utils/RedisUtil.java new file mode 100644 index 0000000..0a5a669 --- /dev/null +++ b/src/main/java/com/canrd/webmagic/common/utils/RedisUtil.java @@ -0,0 +1,1443 @@ +package com.canrd.webmagic.common.utils; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.data.redis.connection.DataType; +import org.springframework.data.redis.core.*; +import org.springframework.stereotype.Repository; + +import javax.annotation.Resource; +import java.util.*; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; + +/** + * @Date: 2020/9/1 + */ +@Slf4j +@Repository("redisUtil") +public class RedisUtil { + + @Resource(name = "stringRedisTemplate") + private StringRedisTemplate redisTemplate; + + + /* -------------------key相关操作--------------------- */ + + /** + * 删除key + * + * @param key key key + */ + public void delete(String key) { + redisTemplate.delete(key); + } + + /** + * 批量删除key + * + * @param keys keys + */ + public void delete(Collection<String> keys) { + redisTemplate.delete(keys); + } + + /** + * 序列化key + * + * @param key key + * @return + */ + public byte[] dump(String key) { + return redisTemplate.dump(key); + } + + /** + * 是否存在key + * + * @param key key + * @return + */ + public Boolean hasKey(String key) { + return redisTemplate.hasKey(key); + } + + /** + * 设置过期时间 + * + * @param key key + * @param timeout + * @param unit + * @return + */ + public Boolean expire(String key, long timeout, TimeUnit unit) { + return redisTemplate.expire(key, timeout, unit); + } + + /** + * 设置过期时间 + * + * @param key key + * @param date + * @return + */ + public Boolean expireAt(String key, Date date) { + return redisTemplate.expireAt(key, date); + } + + /** + * 查找匹配的key + * + * @param pattern + * @return + */ + public Set<String> keys(String pattern) { + return redisTemplate.keys(pattern); + } + + /** + * 将当前数据库的 key 移动到给定的数据库 db 当中 + * + * @param key key + * @param dbIndex + * @return + */ + public Boolean move(String key, int dbIndex) { + return redisTemplate.move(key, dbIndex); + } + + /** + * 移除 key 的过期时间,key 将持久保持 + * + * @param key key + * @return + */ + public Boolean persist(String key) { + return redisTemplate.persist(key); + } + + /** + * 返回 key 的剩余的过期时间 + * + * @param key key + * @param unit + * @return + */ + public Long getExpire(String key, TimeUnit unit) { + return redisTemplate.getExpire(key, unit); + } + + /** + * 返回 key 的剩余的过期时间 + * + * @param key key + * @return + */ + public Long getExpire(String key) { + return redisTemplate.getExpire(key); + } + + /** + * 从当前数据库中随机返回一个 key + * + * @return + */ + public String randomKey() { + return redisTemplate.randomKey(); + } + + /** + * 修改 key 的名称 + * + * @param oldKey + * @param newKey + */ + public void rename(String oldKey, String newKey) { + redisTemplate.rename(oldKey, newKey); + } + + /** + * 仅当 newkey 不存在时,将 oldKey 改名为 newkey + * + * @param oldKey + * @param newKey + * @return + */ + public Boolean renameIfAbsent(String oldKey, String newKey) { + return redisTemplate.renameIfAbsent(oldKey, newKey); + } + + /** + * 返回 key 所储存的值的类型 + * + * @param key key + * @return + */ + public DataType type(String key) { + return redisTemplate.type(key); + } + + /** + * -------------------string相关操作--------------------- + *//* + + /** + * 设置指定 key 的值 + * @param key key + * @param value val + */ + public void set(String key, String value) { + redisTemplate.opsForValue().set(key, value); + } + + /** + * 设置缓存,指定有效时间 + * 时间单位为 秒 + * + * @param key + * @param value + * @param expireTime + */ + public void set(String key, String value, Long expireTime) { + redisTemplate.opsForValue().set(key, value, expireTime, TimeUnit.SECONDS); + } + + /** + * 设置缓存,指定有效时间 + * 时间单位 自定义 + * + * @param key + * @param value + * @param expireTime + * @param timeUnit + */ + public void set(String key, String value, Long expireTime, TimeUnit timeUnit) { + redisTemplate.opsForValue().set(key, value, expireTime, timeUnit); + } + + /** + * 获取指定 key 的值 + * + * @param key key + * @return + */ + public String get(String key) { + return redisTemplate.opsForValue().get(key); + } + + /** + * 返回 key 中字符串值的子字符 + * + * @param key key + * @param start + * @param end + * @return + */ + public String getRange(String key, long start, long end) { + return redisTemplate.opsForValue().get(key, start, end); + } + + /** + * 将给定 key 的值设为 value ,并返回 key 的旧值(old value) + * + * @param key key + * @param value val + * @return + */ + public String getAndSet(String key, String value) { + return redisTemplate.opsForValue().getAndSet(key, value); + } + + /** + * 对 key 所储存的字符串值,获取指定偏移量上的位(bit) + * + * @param key key + * @param offset + * @return + */ + public Boolean getBit(String key, long offset) { + return redisTemplate.opsForValue().getBit(key, offset); + } + + /** + * 批量获取 + * + * @param keys keys + * @return 多个key的value + */ + public List<String> multiGet(Collection<String> keys) { + return redisTemplate.opsForValue().multiGet(keys); + } + + public List<String> multiGetPip(Collection<String> keys) { + return redisTemplate.executePipelined((RedisCallback<String>) connection -> { + keys.forEach(a -> connection.get(a.getBytes())); + return null; + }).stream() + .filter(Objects::nonNull) + .map(Object::toString) + .collect(Collectors.toList()); + } + + /** + * 设置ASCII码, 字符串'a'的ASCII码是97, 转为二进制是'01100001', 此方法是将二进制第offset位值变为value + * + * @param key key + * @param offset 偏移 + * @param value val + */ + public Boolean setBit(String key, long offset, boolean value) { + return redisTemplate.opsForValue().setBit(key, offset, value); + } + + /** + * 将值 value 关联到 key ,并将 key 的过期时间设为 timeout + * + * @param key key + * @param value val + * @param timeout 过期时间 + * @param unit 时间单位, 天:TimeUnit.DAYS 小时:TimeUnit.HOURS 分钟:TimeUnit.MINUTES + * 秒:TimeUnit.SECONDS 毫秒:TimeUnit.MILLISECONDS + */ + public void setEx(String key, String value, long timeout, TimeUnit unit) { + redisTemplate.opsForValue().set(key, value, timeout, unit); + } + + /** + * 只有在 key 不存在时设置 key 的值 + * + * @param key key + * @param value val + * @return 之前已经存在返回false, 不存在返回true + */ + public Boolean setIfAbsent(String key, String value) { + return redisTemplate.opsForValue().setIfAbsent(key, value); + } + + + /** + * 用 value 参数覆写给定 key 所储存的字符串值,从偏移量 offset 开始 + * + * @param key key + * @param value val + * @param offset 从指定位置开始覆写 + */ + public void setRange(String key, String value, long offset) { + redisTemplate.opsForValue().set(key, value, offset); + } + + /** + * 获取字符串的长度 + * + * @param key key + */ + public Long size(String key) { + return redisTemplate.opsForValue().size(key); + } + + /** + * 批量添加 + */ + public void multiSet(Map<String, String> maps) { + redisTemplate.opsForValue().multiSet(maps); + } + + /** + * 同时设置一个或多个 key-value 对,当且仅当所有给定 key 都不存在 + * + * @return 之前已经存在返回false, 不存在返回true + */ + public Boolean multiSetIfAbsent(Map<String, String> maps) { + return redisTemplate.opsForValue().multiSetIfAbsent(maps); + } + + /** + * 增加(自增长), 负数则为自减 + * + * @param key key + * @param increment 步长 + */ + public Long incrBy(String key, long increment) { + return redisTemplate.opsForValue().increment(key, increment); + } + + /** + * @param key key + * @param increment 步长 + */ + public Double incrByFloat(String key, double increment) { + return redisTemplate.opsForValue().increment(key, increment); + } + + /** + * 增加(自增长), 负数则为自减 + * + * @param key key redisKey + * @param decrement 步长 + */ + public Long decreByLong(String key, long decrement) { + return redisTemplate.opsForValue().decrement(key, decrement); + } + + /** + * @param key key redisKey + */ + public Long decreBy(String key) { + return redisTemplate.opsForValue().decrement(key); + } + + /** + * 追加到末尾 + * + * @param key key + * @param value val + * @return + */ + public Integer append(String key, String value) { + return redisTemplate.opsForValue().append(key, value); + } + + /** -------------------hash相关操作------------------------- */ + + /** + * 获取存储在哈希表中指定字段的值 + * + * @param key key + * @param field + * @return + */ + public Object hGet(String key, String field) { + return redisTemplate.opsForHash().get(key, field); + } + + /** + * 获取所有给定字段的值 + * + * @param key key + * @return + */ + public Map<Object, Object> hGetAll(String key) { + return redisTemplate.opsForHash().entries(key); + } + + /** + * 获取所有给定字段的值 + * + * @param key key + * @param fields + * @return + */ + public List<Object> hMultiGet(String key, Collection<Object> fields) { + return redisTemplate.opsForHash().multiGet(key, fields); + } + + public void hPut(String key, String hashKey, String value) { + redisTemplate.opsForHash().put(key, hashKey, value); + } + + public void hPutAll(String key, Map<String, String> maps) { + redisTemplate.opsForHash().putAll(key, maps); + } + + /** + * 仅当hashKey不存在时才设置 + * + * @param key key + * @param hashKey + * @param value val + * @return + */ + public Boolean hPutIfAbsent(String key, String hashKey, String value) { + return redisTemplate.opsForHash().putIfAbsent(key, hashKey, value); + } + + /** + * 删除一个或多个哈希表字段 + * + * @param key key + * @param fields + * @return + */ + public Long hDelete(String key, Object... fields) { + return redisTemplate.opsForHash().delete(key, fields); + } + + /** + * 查看哈希表 key 中,指定的字段是否存在 + * + * @param key key + * @param field + * @return + */ + public Boolean hExists(String key, String field) { + return redisTemplate.opsForHash().hasKey(key, field); + } + + /** + * 为哈希表 key 中的指定字段的整数值加上增量 increment + * + * @param key key + * @param field + * @param increment + * @return + */ + public Long hIncrBy(String key, Object field, long increment) { + return redisTemplate.opsForHash().increment(key, field, increment); + } + + /** + * 为哈希表 key 中的指定字段的整数值加上增量 increment + * + * @param key key + * @param field + * @param delta + * @return + */ + public Double hIncrByFloat(String key, Object field, double delta) { + return redisTemplate.opsForHash().increment(key, field, delta); + } + + /** + * 获取所有哈希表中的字段 + * + * @param key key + * @return + */ + public Set<Object> hKeys(String key) { + return redisTemplate.opsForHash().keys(key); + } + + /** + * 获取哈希表中字段的数量 + * + * @param key key + * @return + */ + public Long hSize(String key) { + return redisTemplate.opsForHash().size(key); + } + + /** + * 获取哈希表中所有值 + * + * @param key key + * @return + */ + public List<Object> hValues(String key) { + return redisTemplate.opsForHash().values(key); + } + + /** + * 迭代哈希表中的键值对 + * + * @param key key + * @param options + * @return + */ + public Cursor<Map.Entry<Object, Object>> hScan(String key, ScanOptions options) { + return redisTemplate.opsForHash().scan(key, options); + } + + /** ------------------------list相关操作---------------------------- */ + + /** + * 通过索引获取列表中的元素 + * + * @param key key + * @param index + * @return + */ + public String lIndex(String key, long index) { + return redisTemplate.opsForList().index(key, index); + } + + /** + * 获取列表指定范围内的元素 + * + * @param key key + * @param start 开始位置, 0是开始位置 + * @param end 结束位置, -1返回所有 + * @return + */ + public List<String> lRange(String key, long start, long end) { + return redisTemplate.opsForList().range(key, start, end); + } + + /** + * 存储在list头部 + * + * @param key key + * @param value val + * @return + */ + public Long lLeftPush(String key, String value) { + return redisTemplate.opsForList().leftPush(key, value); + } + + /** + * @param key key + * @param value val + * @return + */ + public Long lLeftPushAll(String key, String... value) { + return redisTemplate.opsForList().leftPushAll(key, value); + } + + /** + * @param key key + * @param value val + * @return + */ + public Long lLeftPushAll(String key, Collection<String> value) { + return redisTemplate.opsForList().leftPushAll(key, value); + } + + /** + * 当list存在的时候才加入 + * + * @param key key + * @param value val + * @return + */ + public Long lLeftPushIfPresent(String key, String value) { + return redisTemplate.opsForList().leftPushIfPresent(key, value); + } + + /** + * 如果pivot存在,再pivot前面添加 + * + * @param key key + * @param pivot + * @param value val + * @return + */ + public Long lLeftPush(String key, String pivot, String value) { + return redisTemplate.opsForList().leftPush(key, pivot, value); + } + + /** + * @param key key + * @param value val + * @return + */ + public Long lRightPush(String key, String value) { + return redisTemplate.opsForList().rightPush(key, value); + } + + /** + * @param key key + * @param value val + * @return + */ + public Long lRightPushAll(String key, String... value) { + return redisTemplate.opsForList().rightPushAll(key, value); + } + + /** + * @param key key + * @param value val + * @return + */ + public Long lRightPushAll(String key, Collection<String> value) { + return redisTemplate.opsForList().rightPushAll(key, value); + } + + /** + * 为已存在的列表添加值 + * + * @param key key + * @param value val + * @return + */ + public Long lRightPushIfPresent(String key, String value) { + return redisTemplate.opsForList().rightPushIfPresent(key, value); + } + + /** + * 在pivot元素的右边添加值 + * + * @param key key + * @param pivot + * @param value val + * @return + */ + public Long lRightPush(String key, String pivot, String value) { + return redisTemplate.opsForList().rightPush(key, pivot, value); + } + + /** + * 通过索引设置列表元素的值 + * + * @param key key + * @param index 位置 + * @param value val + */ + public void lSet(String key, long index, String value) { + redisTemplate.opsForList().set(key, index, value); + } + + /** + * 移出并获取列表的第一个元素 + * + * @param key key + * @return 删除的元素 + */ + public String lLeftPop(String key) { + return redisTemplate.opsForList().leftPop(key); + } + + /** + * 移出并获取列表的第一个元素, 如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止 + * + * @param key key + * @param timeout 等待时间 + * @param unit 时间单位 + * @return + */ + public String lbLeftPop(String key, long timeout, TimeUnit unit) { + return redisTemplate.opsForList().leftPop(key, timeout, unit); + } + + /** + * 移除并获取列表最后一个元素 + * + * @param key key + * @return 删除的元素 + */ + public String lRightPop(String key) { + return redisTemplate.opsForList().rightPop(key); + } + + /** + * 移出并获取列表的最后一个元素, 如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止 + * + * @param key key + * @param timeout 等待时间 + * @param unit 时间单位 + * @return + */ + public String lbRightPop(String key, long timeout, TimeUnit unit) { + return redisTemplate.opsForList().rightPop(key, timeout, unit); + } + + /** + * 移除列表的最后一个元素,并将该元素添加到另一个列表并返回 + * + * @param sourceKey + * @param destinationKey + * @return + */ + public String lRightPopAndLeftPush(String sourceKey, String destinationKey) { + return redisTemplate.opsForList().rightPopAndLeftPush(sourceKey, destinationKey); + } + + /** + * 从列表中弹出一个值,将弹出的元素插入到另外一个列表中并返回它; 如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止 + * + * @param sourceKey + * @param destinationKey + * @param timeout + * @param unit + * @return + */ + public String lbRightPopAndLeftPush(String sourceKey, String destinationKey, + long timeout, TimeUnit unit) { + return redisTemplate.opsForList().rightPopAndLeftPush(sourceKey, destinationKey, timeout, unit); + } + + /** + * 删除集合中值等于value得元素 + * + * @param key key + * @param index index=0, 删除所有值等于value的元素; index>0, 从头部开始删除第一个值等于value的元素; + * index<0, 从尾部开始删除第一个值等于value的元素; + * @param value val + * @return + */ + public Long lRemove(String key, long index, String value) { + return redisTemplate.opsForList().remove(key, index, value); + } + + /** + * 裁剪list + * + * @param key key + * @param start + * @param end + */ + public void lTrim(String key, long start, long end) { + redisTemplate.opsForList().trim(key, start, end); + } + + /** + * 获取列表长度 + * + * @param key key + * @return + */ + public Long lLen(String key) { + return redisTemplate.opsForList().size(key); + } + + /** --------------------set相关操作-------------------------- */ + + /** + * set添加元素 + * + * @param key key + * @param values vals + * @return + */ + public Long sAdd(String key, String... values) { + return redisTemplate.opsForSet().add(key, values); + } + + /** + * set移除元素 + * + * @param key key + * @param values vals + * @return + */ + public Long sRemove(String key, Object... values) { + return redisTemplate.opsForSet().remove(key, values); + } + + /** + * 移除并返回集合的一个随机元素 + * + * @param key key + * @return + */ + public String sPop(String key) { + return redisTemplate.opsForSet().pop(key); + } + + /** + * 移除并返回集合的多个随机元素 + * + * @param key key + * @return + */ + public List<String> sPop(String key, int count) { + return redisTemplate.opsForSet().pop(key, count); + } + + /** + * 将元素value从一个集合移到另一个集合 + * + * @param key key + * @param value val + * @param destKey + * @return + */ + public Boolean sMove(String key, String value, String destKey) { + return redisTemplate.opsForSet().move(key, value, destKey); + } + + /** + * 获取集合的大小 + * + * @param key key + * @return + */ + public Long sSize(String key) { + return redisTemplate.opsForSet().size(key); + } + + /** + * 判断集合是否包含value + * + * @param key key + * @param value val + * @return + */ + public Boolean sIsMember(String key, Object value) { + return redisTemplate.opsForSet().isMember(key, value); + } + + /** + * 获取两个集合的交集 + * + * @param key key + * @param otherKey + * @return + */ + public Set<String> sIntersect(String key, String otherKey) { + return redisTemplate.opsForSet().intersect(key, otherKey); + } + + /** + * 获取key集合与多个集合的交集 + * + * @param key key + * @param otherKeys + * @return + */ + public Set<String> sIntersect(String key, Collection<String> otherKeys) { + return redisTemplate.opsForSet().intersect(key, otherKeys); + } + + /** + * key集合与otherKey集合的交集存储到destKey集合中 + * + * @param key key + * @param otherKey + * @param destKey + * @return + */ + public Long sIntersectAndStore(String key, String otherKey, String destKey) { + return redisTemplate.opsForSet().intersectAndStore(key, otherKey, destKey); + } + + /** + * key集合与多个集合的交集存储到destKey集合中 + * + * @param key key + * @param otherKeys + * @param destKey + * @return + */ + public Long sIntersectAndStore(String key, Collection<String> otherKeys, String destKey) { + return redisTemplate.opsForSet().intersectAndStore(key, otherKeys, destKey); + } + + /** + * 获取两个集合的并集 + * + * @param key key + * @param otherKeys + * @return + */ + public Set<String> sUnion(String key, String otherKeys) { + return redisTemplate.opsForSet().union(key, otherKeys); + } + + /** + * 获取key集合与多个集合的并集 + * + * @param key key + * @param otherKeys + * @return + */ + public Set<String> sUnion(String key, Collection<String> otherKeys) { + return redisTemplate.opsForSet().union(key, otherKeys); + } + + /** + * key集合与otherKey集合的并集存储到destKey中 + * + * @param key key + * @param otherKey + * @param destKey + * @return + */ + public Long sUnionAndStore(String key, String otherKey, String destKey) { + return redisTemplate.opsForSet().unionAndStore(key, otherKey, destKey); + } + + /** + * key集合与多个集合的并集存储到destKey中 + * + * @param key key + * @param otherKeys + * @param destKey + * @return + */ + public Long sUnionAndStore(String key, Collection<String> otherKeys, String destKey) { + return redisTemplate.opsForSet().unionAndStore(key, otherKeys, destKey); + } + + /** + * 获取两个集合的差集 + * + * @param key key + * @param otherKey + * @return + */ + public Set<String> sDifference(String key, String otherKey) { + return redisTemplate.opsForSet().difference(key, otherKey); + } + + /** + * 获取key集合与多个集合的差集 + * + * @param key key + * @param otherKeys + * @return + */ + public Set<String> sDifference(String key, Collection<String> otherKeys) { + return redisTemplate.opsForSet().difference(key, otherKeys); + } + + /** + * key集合与otherKey集合的差集存储到destKey中 + * + * @param key key + * @param otherKey + * @param destKey + * @return + */ + public Long sDifference(String key, String otherKey, String destKey) { + return redisTemplate.opsForSet().differenceAndStore(key, otherKey, destKey); + } + + /** + * key集合与多个集合的差集存储到destKey中 + * + * @param key key + * @param otherKeys + * @param destKey + * @return + */ + public Long sDifference(String key, Collection<String> otherKeys, String destKey) { + return redisTemplate.opsForSet().differenceAndStore(key, otherKeys, destKey); + } + + /** + * 获取集合所有元素 + * + * @param key key + * @param + * @param + * @return + */ + public Set<String> setMembers(String key) { + return redisTemplate.opsForSet().members(key); + } + + /** + * 随机获取集合中的一个元素 + * + * @param key key + * @return + */ + public String sRandomMember(String key) { + return redisTemplate.opsForSet().randomMember(key); + } + + /** + * 随机获取集合中count个元素 + * + * @param key key + * @param count + * @return + */ + public List<String> sRandomMembers(String key, long count) { + return redisTemplate.opsForSet().randomMembers(key, count); + } + + /** + * 随机获取集合中count个元素并且去除重复的 + * + * @param key key + * @param count + * @return + */ + public Set<String> sDistinctRandomMembers(String key, long count) { + return redisTemplate.opsForSet().distinctRandomMembers(key, count); + } + + /** + * @param key key + * @param options + * @return + */ + public Cursor<String> sScan(String key, ScanOptions options) { + return redisTemplate.opsForSet().scan(key, options); + } + + /**------------------zSet相关操作--------------------------------*/ + + /** + * 添加元素,有序集合是按照元素的score值由小到大排列 + * + * @param key key + * @param value val + * @param score + * @return + */ + public Boolean zAdd(String key, String value, double score) { + return redisTemplate.opsForZSet().add(key, value, score); + } + + /** + * @param key key + * @param values val + * @return + */ + public Long zAdd(String key, Set<ZSetOperations.TypedTuple<String>> values) { + return redisTemplate.opsForZSet().add(key, values); + } + + /** + * @param key key + * @param values val + * @return 已删除元素的数量 + */ + public Long zRemove(String key, Object... values) { + return redisTemplate.opsForZSet().remove(key, values); + } + + /** + * 增加元素的score值,并返回增加后的值 + * + * @param key key + * @param value val + * @param delta 分数值 + * @return 增加后的值 + */ + public Double zIncrementScore(String key, String value, double delta) { + return redisTemplate.opsForZSet().incrementScore(key, value, delta); + } + + /** + * 返回元素在集合的排名,有序集合是按照元素的score值由小到大排列 + * + * @param key key + * @param value val + * @return 0表示第一位 + */ + public Long zRank(String key, Object value) { + return redisTemplate.opsForZSet().rank(key, value); + + } + + /** + * 返回元素在集合的排名,按元素的score值由大到小排列 + * + * @param key key + * @param value val + */ + public Long zReverseRank(String key, Object value) { + return redisTemplate.opsForZSet().reverseRank(key, value); + } + + /** + * 获取集合的元素, 从小到大排序 + * + * @param key key + * @param start 开始位置 + * @param end 结束位置, -1查询所有 + */ + public Set<String> zRange(String key, long start, long end) { + return redisTemplate.opsForZSet().range(key, start, end); + } + + /** + * 获取集合元素, 并且把score值也获取 + * + * @param key key + * @param start + * @param end + * @return + */ + public Set<ZSetOperations.TypedTuple<String>> zRangeWithScores(String key, long start, long end) { + return redisTemplate.opsForZSet().rangeWithScores(key, start, end); + } + + /** + * 根据Score值查询集合元素 + * + * @param key key + * @param min 最小值 + * @param max 最大值 + * @return + */ + public Set<String> zRangeByScore(String key, double min, double max) { + return redisTemplate.opsForZSet().rangeByScore(key, min, max); + } + + /** + * 根据Score值查询集合元素, 从小到大排序 + * + * @param key key + * @param min 最小值 + * @param max 最大值 + * @return + */ + public Set<ZSetOperations.TypedTuple<String>> zRangeByScoreWithScores(String key, + double min, double max) { + return redisTemplate.opsForZSet().rangeByScoreWithScores(key, min, max); + } + + /** + * @param key key + * @param min + * @param max + * @param start + * @param end + * @return + */ + public Set<ZSetOperations.TypedTuple<String>> zRangeByScoreWithScores(String key, double min, double max, long start, long end) { + return redisTemplate.opsForZSet().rangeByScoreWithScores(key, min, max, start, end); + } + + /** + * 获取集合的元素, 从大到小排序 + * + * @param key key + * @param start + * @param end + * @return + */ + public Set<String> zReverseRange(String key, long start, long end) { + return redisTemplate.opsForZSet().reverseRange(key, start, end); + } + + /** + * 获取集合的元素, 从大到小排序, 并返回score值 + * + * @param key key + * @param start + * @param end + * @return + */ + public Set<ZSetOperations.TypedTuple<String>> zReverseRangeWithScores(String key, + long start, long end) { + return redisTemplate.opsForZSet().reverseRangeWithScores(key, start, end); + } + + /** + * 根据Score值查询集合元素, 从大到小排序 + * + * @param key key + * @param min + * @param max + * @return + */ + public Set<String> zReverseRangeByScore(String key, double min, double max) { + return redisTemplate.opsForZSet().reverseRangeByScore(key, min, max); + } + + /** + * 根据Score值查询集合元素, 从大到小排序 + * + * @param key key + * @param min + * @param max + * @return + */ + public Set<ZSetOperations.TypedTuple<String>> zReverseRangeByScoreWithScores(String key, double min, double max) { + return redisTemplate.opsForZSet().reverseRangeByScoreWithScores(key, min, max); + } + + /** + * @param key key + * @param min + * @param max + * @param start + * @param end + * @return + */ + public Set<String> zReverseRangeByScore(String key, double min, + double max, long start, long end) { + return redisTemplate.opsForZSet().reverseRangeByScore(key, min, max, start, end); + } + + /** + * 根据score值获取集合元素数量 + * + * @param key key + * @param min + * @param max + * @return + */ + public Long zCount(String key, double min, double max) { + return redisTemplate.opsForZSet().count(key, min, max); + } + + /** + * 获取集合大小 + * + * @param key key + * @return + */ + public Long zSize(String key) { + return redisTemplate.opsForZSet().size(key); + } + + /** + * 获取集合大小 + * + * @param key key + * @return + */ + public Long zzCard(String key) { + return redisTemplate.opsForZSet().zCard(key); + } + + /** + * 获取集合中value元素的score值 + * + * @param key key + * @param value val + * @return + */ + public Double zScore(String key, Object value) { + return redisTemplate.opsForZSet().score(key, value); + } + + /** + * 移除指定索引位置的成员 + * + * @param key key + * @param start + * @param end + * @return + */ + public Long zRemoveRange(String key, long start, long end) { + return redisTemplate.opsForZSet().removeRange(key, start, end); + } + + /** + * 根据指定的score值的范围来移除成员 + * + * @param key key + * @param min + * @param max + * @return + */ + public Long zRemoveRangeByScore(String key, double min, double max) { + return redisTemplate.opsForZSet().removeRangeByScore(key, min, max); + } + + /** + * 获取key和otherKey的并集并存储在destKey中 + * + * @param key key + * @param otherKey + * @param destKey + * @return + */ + public Long zUnionAndStore(String key, String otherKey, String destKey) { + return redisTemplate.opsForZSet().unionAndStore(key, otherKey, destKey); + } + + /** + * @param key key + * @param otherKeys + * @param destKey + * @return + */ + public Long zUnionAndStore(String key, Collection<String> otherKeys, String destKey) { + return redisTemplate.opsForZSet().unionAndStore(key, otherKeys, destKey); + } + + /** + * 交集 + * + * @param key key + * @param otherKey + * @param destKey + * @return + */ + public Long zIntersectAndStore(String key, String otherKey, String destKey) { + return redisTemplate.opsForZSet().intersectAndStore(key, otherKey, destKey); + } + + /** + * 交集 + * + * @param key key + * @param otherKeys + * @param destKey + * @return + */ + public Long zIntersectAndStore(String key, Collection<String> otherKeys, String destKey) { + return redisTemplate.opsForZSet().intersectAndStore(key, otherKeys, destKey); + } + + /** + * @param key key + * @param options + * @return + */ + public Cursor<ZSetOperations.TypedTuple<String>> zScan(String key, ScanOptions options) { + return redisTemplate.opsForZSet().scan(key, options); + } + + /* -------------------锁相关操作--------------------- */ + + + /** + * 获得锁 + */ + public boolean getLock(String lockId, String value, long millisecond) { + Boolean success = redisTemplate.opsForValue().setIfAbsent(lockId, value, millisecond, TimeUnit.MILLISECONDS); + return success != null && success; + } + + /** + * 获得锁,并自旋,返回 + */ + public boolean getLock(String lockId, String value, long millisecond, long timeout) { + long start = System.currentTimeMillis(); + do { + boolean success = getLock(lockId, value, millisecond); + if (success) { + return true; + } + try { + Thread.sleep(100); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } while (System.currentTimeMillis() - start < timeout); + return false; + } + + /** + * 释放锁 + */ + public void releaseLock(String id, String lockValue) { + if (StringUtils.isNotEmpty(lockValue) && lockValue.equals(this.get(id))) { + log.debug("RedisUtil releaseLock, key: {}, value: {}", id, lockValue); + redisTemplate.delete(id); + } + } + + /* -------------------HyperLogLog相关操作--------------------- */ + + /** + * HyperLogLog 添加元素 + * <p> key不存在则新增 + * + * @param key key + * @param value value集合 + * @return true表示加入成功 + * @author A80080 + * @createDate 2021/3/26 + */ + public boolean pfAdd(String key, String... value) { + return redisTemplate.opsForHyperLogLog().add(key, value) == 1; + } + + /** + * HyperLogLog 统计key中元素个数 + * <p> key 不存在则返回0 + * + * @param key key + * @return key中数据大小(基数统计存在误差) + * @author A80080 + * @createDate 2021/3/26 + */ + public Long pfCount(String key) { + return redisTemplate.opsForHyperLogLog().size(key); + } + + /** + * 合并HyperLogLog key,并统计新key元素个数 + * <p>不删除参与合并的key; 新key不存在则新增, + * + * @param destination 合并后的key名称 + * @param sourceKeys 参与合并的key + * @return 合并后的key元素个数 + * @author A80080 + * @createDate 2021/3/26 + */ + public Long pfMerge(String destination, String... sourceKeys) { + return redisTemplate.opsForHyperLogLog().union(destination, sourceKeys); + } + + +} \ No newline at end of file diff --git a/src/main/java/com/canrd/webmagic/common/utils/RedisUtils.java b/src/main/java/com/canrd/webmagic/common/utils/RedisUtils.java new file mode 100644 index 0000000..56975f7 --- /dev/null +++ b/src/main/java/com/canrd/webmagic/common/utils/RedisUtils.java @@ -0,0 +1,646 @@ +package com.canrd.webmagic.common.utils; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.data.redis.connection.RedisConnection; +import org.springframework.data.redis.connection.RedisConnectionFactory; +import org.springframework.data.redis.core.Cursor; +import org.springframework.data.redis.core.RedisConnectionUtils; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.core.ScanOptions; +import org.springframework.stereotype.Component; +import org.springframework.util.CollectionUtils; + +import java.util.*; +import java.util.concurrent.TimeUnit; + +/** + * @author / + */ +@Component +@SuppressWarnings({"unchecked", "all"}) +public class RedisUtils { + + private RedisTemplate<Object, Object> redisTemplate; + @Value("${jwt.online-key}") + private String onlineKey; + + public RedisUtils(RedisTemplate<Object, Object> redisTemplate) { + this.redisTemplate = redisTemplate; + } + + // =============================common============================ + + /** + * 指定缓存失效时间 + * + * @param key 键 + * @param time 时间(秒) + */ + public boolean expire(String key, long time) { + try { + if (time > 0) { + redisTemplate.expire(key, time, TimeUnit.SECONDS); + } + } catch (Exception e) { + e.printStackTrace(); + return false; + } + return true; + } + + /** + * 根据 key 获取过期时间 + * + * @param key 键 不能为null + * @return 时间(秒) 返回0代表为永久有效 + */ + public long getExpire(Object key) { + return redisTemplate.getExpire(key, TimeUnit.SECONDS); + } + + /** + * 查找匹配key + * + * @param pattern key + * @return / + */ + public List<String> scan(String pattern) { + ScanOptions options = ScanOptions.scanOptions().match(pattern).build(); + RedisConnectionFactory factory = redisTemplate.getConnectionFactory(); + RedisConnection rc = Objects.requireNonNull(factory).getConnection(); + Cursor<byte[]> cursor = rc.scan(options); + List<String> result = new ArrayList<>(); + while (cursor.hasNext()) { + result.add(new String(cursor.next())); + } + try { + RedisConnectionUtils.releaseConnection(rc, factory); + } catch (Exception e) { + e.printStackTrace(); + } + return result; + } + + /** + * 分页查询 key + * + * @param patternKey key + * @param page 页码 + * @param size 每页数目 + * @return / + */ + public List<String> findKeysForPage(String patternKey, int page, int size) { + ScanOptions options = ScanOptions.scanOptions().match(patternKey).build(); + RedisConnectionFactory factory = redisTemplate.getConnectionFactory(); + RedisConnection rc = Objects.requireNonNull(factory).getConnection(); + Cursor<byte[]> cursor = rc.scan(options); + List<String> result = new ArrayList<>(size); + int tmpIndex = 0; + int fromIndex = page * size; + int toIndex = page * size + size; + while (cursor.hasNext()) { + if (tmpIndex >= fromIndex && tmpIndex < toIndex) { + result.add(new String(cursor.next())); + tmpIndex++; + continue; + } + // 获取到满足条件的数据后,就可以退出了 + if (tmpIndex >= toIndex) { + break; + } + tmpIndex++; + cursor.next(); + } + try { + RedisConnectionUtils.releaseConnection(rc, factory); + } catch (Exception e) { + e.printStackTrace(); + } + return result; + } + + /** + * 判断key是否存在 + * + * @param key 键 + * @return true 存在 false不存在 + */ + public boolean hasKey(String key) { + try { + return redisTemplate.hasKey(key); + } catch (Exception e) { + e.printStackTrace(); + return false; + } + } + + /** + * 删除缓存 + * + * @param key 可以传一个值 或多个 + */ + public void del(String... key) { + if (key != null && key.length > 0) { + if (key.length == 1) { + redisTemplate.delete(key[0]); + } else { + redisTemplate.delete(CollectionUtils.arrayToList(key)); + } + } + } + + // ============================String============================= + + /** + * 普通缓存获取 + * + * @param key 键 + * @return 值 + */ + public Object get(String key) { + return key == null ? null : redisTemplate.opsForValue().get(key); + } + + /** + * 批量获取 + * + * @param keys + * @return + */ + public List<Object> multiGet(List<String> keys) { + Object obj = redisTemplate.opsForValue().multiGet(Collections.singleton(keys)); + return null; + } + + /** + * 普通缓存放入 + * + * @param key 键 + * @param value 值 + * @return true成功 false失败 + */ + public boolean set(String key, Object value) { + try { + redisTemplate.opsForValue().set(key, value); + return true; + } catch (Exception e) { + e.printStackTrace(); + return false; + } + } + + /** + * 普通缓存放入并设置时间 + * + * @param key 键 + * @param value 值 + * @param time 时间(秒) time要大于0 如果time小于等于0 将设置无限期 + * @return true成功 false 失败 + */ + public boolean set(String key, Object value, long time) { + try { + if (time > 0) { + redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS); + } else { + set(key, value); + } + return true; + } catch (Exception e) { + e.printStackTrace(); + return false; + } + } + + /** + * 普通缓存放入并设置时间 + * + * @param key 键 + * @param value 值 + * @param time 时间 + * @param timeUnit 类型 + * @return true成功 false 失败 + */ + public boolean set(String key, Object value, long time, TimeUnit timeUnit) { + try { + if (time > 0) { + redisTemplate.opsForValue().set(key, value, time, timeUnit); + } else { + set(key, value); + } + return true; + } catch (Exception e) { + e.printStackTrace(); + return false; + } + } + + // ================================Map================================= + + /** + * HashGet + * + * @param key 键 不能为null + * @param item 项 不能为null + * @return 值 + */ + public Object hget(String key, String item) { + return redisTemplate.opsForHash().get(key, item); + } + + /** + * 获取hashKey对应的所有键值 + * + * @param key 键 + * @return 对应的多个键值 + */ + public Map<Object, Object> hmget(String key) { + return redisTemplate.opsForHash().entries(key); + + } + + /** + * HashSet + * + * @param key 键 + * @param map 对应多个键值 + * @return true 成功 false 失败 + */ + public boolean hmset(String key, Map<String, Object> map) { + try { + redisTemplate.opsForHash().putAll(key, map); + return true; + } catch (Exception e) { + e.printStackTrace(); + return false; + } + } + + /** + * HashSet 并设置时间 + * + * @param key 键 + * @param map 对应多个键值 + * @param time 时间(秒) + * @return true成功 false失败 + */ + public boolean hmset(String key, Map<String, Object> map, long time) { + try { + redisTemplate.opsForHash().putAll(key, map); + if (time > 0) { + expire(key, time); + } + return true; + } catch (Exception e) { + e.printStackTrace(); + return false; + } + } + + /** + * 向一张hash表中放入数据,如果不存在将创建 + * + * @param key 键 + * @param item 项 + * @param value 值 + * @return true 成功 false失败 + */ + public boolean hset(String key, String item, Object value) { + try { + redisTemplate.opsForHash().put(key, item, value); + return true; + } catch (Exception e) { + e.printStackTrace(); + return false; + } + } + + /** + * 向一张hash表中放入数据,如果不存在将创建 + * + * @param key 键 + * @param item 项 + * @param value 值 + * @param time 时间(秒) 注意:如果已存在的hash表有时间,这里将会替换原有的时间 + * @return true 成功 false失败 + */ + public boolean hset(String key, String item, Object value, long time) { + try { + redisTemplate.opsForHash().put(key, item, value); + if (time > 0) { + expire(key, time); + } + return true; + } catch (Exception e) { + e.printStackTrace(); + return false; + } + } + + /** + * 删除hash表中的值 + * + * @param key 键 不能为null + * @param item 项 可以使多个 不能为null + */ + public void hdel(String key, Object... item) { + redisTemplate.opsForHash().delete(key, item); + } + + /** + * 判断hash表中是否有该项的值 + * + * @param key 键 不能为null + * @param item 项 不能为null + * @return true 存在 false不存在 + */ + public boolean hHasKey(String key, String item) { + return redisTemplate.opsForHash().hasKey(key, item); + } + + /** + * hash递增 如果不存在,就会创建一个 并把新增后的值返回 + * + * @param key 键 + * @param item 项 + * @param by 要增加几(大于0) + * @return + */ + public double hincr(String key, String item, double by) { + return redisTemplate.opsForHash().increment(key, item, by); + } + + /** + * hash递减 + * + * @param key 键 + * @param item 项 + * @param by 要减少记(小于0) + * @return + */ + public double hdecr(String key, String item, double by) { + return redisTemplate.opsForHash().increment(key, item, -by); + } + + // ============================set============================= + + /** + * 根据key获取Set中的所有值 + * + * @param key 键 + * @return + */ + public Set<Object> sGet(String key) { + try { + return redisTemplate.opsForSet().members(key); + } catch (Exception e) { + e.printStackTrace(); + return null; + } + } + + /** + * 根据value从一个set中查询,是否存在 + * + * @param key 键 + * @param value 值 + * @return true 存在 false不存在 + */ + public boolean sHasKey(String key, Object value) { + try { + return redisTemplate.opsForSet().isMember(key, value); + } catch (Exception e) { + e.printStackTrace(); + return false; + } + } + + /** + * 将数据放入set缓存 + * + * @param key 键 + * @param values 值 可以是多个 + * @return 成功个数 + */ + public long sSet(String key, Object... values) { + try { + return redisTemplate.opsForSet().add(key, values); + } catch (Exception e) { + e.printStackTrace(); + return 0; + } + } + + /** + * 将set数据放入缓存 + * + * @param key 键 + * @param time 时间(秒) + * @param values 值 可以是多个 + * @return 成功个数 + */ + public long sSetAndTime(String key, long time, Object... values) { + try { + Long count = redisTemplate.opsForSet().add(key, values); + if (time > 0) { + expire(key, time); + } + return count; + } catch (Exception e) { + e.printStackTrace(); + return 0; + } + } + + /** + * 获取set缓存的长度 + * + * @param key 键 + * @return + */ + public long sGetSetSize(String key) { + try { + return redisTemplate.opsForSet().size(key); + } catch (Exception e) { + e.printStackTrace(); + return 0; + } + } + + /** + * 移除值为value的 + * + * @param key 键 + * @param values 值 可以是多个 + * @return 移除的个数 + */ + public long setRemove(String key, Object... values) { + try { + Long count = redisTemplate.opsForSet().remove(key, values); + return count; + } catch (Exception e) { + e.printStackTrace(); + return 0; + } + } + + // ===============================list================================= + + /** + * 获取list缓存的内容 + * + * @param key 键 + * @param start 开始 + * @param end 结束 0 到 -1代表所有值 + * @return + */ + public List<Object> lGet(String key, long start, long end) { + try { + return redisTemplate.opsForList().range(key, start, end); + } catch (Exception e) { + e.printStackTrace(); + return null; + } + } + + /** + * 获取list缓存的长度 + * + * @param key 键 + * @return + */ + public long lGetListSize(String key) { + try { + return redisTemplate.opsForList().size(key); + } catch (Exception e) { + e.printStackTrace(); + return 0; + } + } + + /** + * 通过索引 获取list中的值 + * + * @param key 键 + * @param index 索引 index>=0时, 0 表头,1 第二个元素,依次类推;index<0时,-1,表尾,-2倒数第二个元素,依次类推 + * @return + */ + public Object lGetIndex(String key, long index) { + try { + return redisTemplate.opsForList().index(key, index); + } catch (Exception e) { + e.printStackTrace(); + return null; + } + } + + /** + * 将list放入缓存 + * + * @param key 键 + * @param value 值 + * @return + */ + public boolean lSet(String key, Object value) { + try { + redisTemplate.opsForList().rightPush(key, value); + return true; + } catch (Exception e) { + e.printStackTrace(); + return false; + } + } + + /** + * 将list放入缓存 + * + * @param key 键 + * @param value 值 + * @param time 时间(秒) + * @return + */ + public boolean lSet(String key, Object value, long time) { + try { + redisTemplate.opsForList().rightPush(key, value); + if (time > 0) { + expire(key, time); + } + return true; + } catch (Exception e) { + e.printStackTrace(); + return false; + } + } + + /** + * 将list放入缓存 + * + * @param key 键 + * @param value 值 + * @return + */ + public boolean lSet(String key, List<Object> value) { + try { + redisTemplate.opsForList().rightPushAll(key, value); + return true; + } catch (Exception e) { + e.printStackTrace(); + return false; + } + } + + /** + * 将list放入缓存 + * + * @param key 键 + * @param value 值 + * @param time 时间(秒) + * @return + */ + public boolean lSet(String key, List<Object> value, long time) { + try { + redisTemplate.opsForList().rightPushAll(key, value); + if (time > 0) { + expire(key, time); + } + return true; + } catch (Exception e) { + e.printStackTrace(); + return false; + } + } + + /** + * 根据索引修改list中的某条数据 + * + * @param key 键 + * @param index 索引 + * @param value 值 + * @return / + */ + public boolean lUpdateIndex(String key, long index, Object value) { + try { + redisTemplate.opsForList().set(key, index, value); + return true; + } catch (Exception e) { + e.printStackTrace(); + return false; + } + } + + /** + * 移除N个值为value + * + * @param key 键 + * @param count 移除多少个 + * @param value 值 + * @return 移除的个数 + */ + public long lRemove(String key, long count, Object value) { + try { + return redisTemplate.opsForList().remove(key, count, value); + } catch (Exception e) { + e.printStackTrace(); + return 0; + } + } +} diff --git a/src/main/java/com/canrd/webmagic/common/utils/RequestContextUtil.java b/src/main/java/com/canrd/webmagic/common/utils/RequestContextUtil.java new file mode 100644 index 0000000..c1ce21d --- /dev/null +++ b/src/main/java/com/canrd/webmagic/common/utils/RequestContextUtil.java @@ -0,0 +1,35 @@ +package com.canrd.webmagic.common.utils; + +import org.springframework.web.context.ContextLoader; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; + +import javax.servlet.ServletContext; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; + + +public class RequestContextUtil { + + public static HttpServletRequest getRequest() { + return getRequestAttributes().getRequest(); + } + + public static HttpServletResponse getResponse() { + return getRequestAttributes().getResponse(); + } + + public static HttpSession getSession() { + return getRequest().getSession(); + } + + public static ServletRequestAttributes getRequestAttributes() { + return ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()); + } + + public static ServletContext getServletContext() { + return ContextLoader.getCurrentWebApplicationContext().getServletContext(); + } + +} diff --git a/src/main/java/com/canrd/webmagic/common/utils/RequestHolder.java b/src/main/java/com/canrd/webmagic/common/utils/RequestHolder.java new file mode 100644 index 0000000..e35b2b2 --- /dev/null +++ b/src/main/java/com/canrd/webmagic/common/utils/RequestHolder.java @@ -0,0 +1,19 @@ +package com.canrd.webmagic.common.utils; + +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; + +import javax.servlet.http.HttpServletRequest; +import java.util.Objects; + +/** + * 获取 HttpServletRequest + * + * @date 2018-11-24 + */ +public class RequestHolder { + + public static HttpServletRequest getHttpServletRequest() { + return ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest(); + } +} diff --git a/src/main/java/com/canrd/webmagic/common/utils/RequestStringUtils.java b/src/main/java/com/canrd/webmagic/common/utils/RequestStringUtils.java new file mode 100644 index 0000000..8df31c1 --- /dev/null +++ b/src/main/java/com/canrd/webmagic/common/utils/RequestStringUtils.java @@ -0,0 +1,199 @@ +package com.canrd.webmagic.common.utils; + +import cn.hutool.core.io.resource.ClassPathResource; +import com.canrd.webmagic.common.constant.ElAdminConstant; +import eu.bitwalker.useragentutils.Browser; +import eu.bitwalker.useragentutils.UserAgent; +import org.lionsoul.ip2region.DataBlock; +import org.lionsoul.ip2region.DbConfig; +import org.lionsoul.ip2region.DbSearcher; + +import javax.servlet.http.HttpServletRequest; +import java.io.File; +import java.io.IOException; +import java.lang.reflect.Method; +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.util.Calendar; +import java.util.Date; + +/** + * 字符串工具类, 继承org.apache.commons.lang3.StringUtils类 + */ +public class RequestStringUtils extends org.apache.commons.lang3.StringUtils { + + private static final char SEPARATOR = '_'; + + private static final String UNKNOWN = "unknown"; + + /** + * 驼峰命名法工具 + * + * @return toCamelCase(" hello_world ") == "helloWorld" + * toCapitalizeCamelCase("hello_world") == "HelloWorld" + * toUnderScoreCase("helloWorld") = "hello_world" + */ + 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(); + } + + /** + * 驼峰命名法工具 + * + * @return toCamelCase(" hello_world ") == "helloWorld" + * toCapitalizeCamelCase("hello_world") == "HelloWorld" + * toUnderScoreCase("helloWorld") = "hello_world" + */ + public static String toCapitalizeCamelCase(String s) { + if (s == null) { + return null; + } + s = toCamelCase(s); + return s.substring(0, 1).toUpperCase() + s.substring(1); + } + + /** + * 驼峰命名法工具 + * + * @return toCamelCase(" hello_world ") == "helloWorld" + * toCapitalizeCamelCase("hello_world") == "HelloWorld" + * toUnderScoreCase("helloWorld") = "hello_world" + */ + static String toUnderScoreCase(String s) { + if (s == null) { + return null; + } + + StringBuilder sb = new StringBuilder(); + boolean upperCase = false; + for (int i = 0; i < s.length(); i++) { + char c = s.charAt(i); + + boolean nextUpperCase = true; + + if (i < (s.length() - 1)) { + nextUpperCase = Character.isUpperCase(s.charAt(i + 1)); + } + + if ((i > 0) && Character.isUpperCase(c)) { + if (!upperCase || !nextUpperCase) { + sb.append(SEPARATOR); + } + upperCase = true; + } else { + upperCase = false; + } + + sb.append(Character.toLowerCase(c)); + } + + return sb.toString(); + } + + /** + * 获取ip地址 + */ + public static String getIp(HttpServletRequest request) { + 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("WL-Proxy-Client-IP"); + } + if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) { + ip = request.getRemoteAddr(); + } + String comma = ","; + String localhost = "127.0.0.1"; + if (ip.contains(comma)) { + ip = ip.split(",")[0]; + } + if (localhost.equals(ip)) { + // 获取本机真正的ip地址 + try { + ip = InetAddress.getLocalHost().getHostAddress(); + } catch (UnknownHostException e) { + e.printStackTrace(); + } + } + return ip; + } + + /** + * 根据ip获取详细地址 + */ + public static String getCityInfo(String ip) { + DbSearcher searcher = null; + try { + String path = "ip2region/ip2region.db"; + String name = "ip2region.db"; + DbConfig config = new DbConfig(); + File file = FileUtil.inputStreamToFile(new ClassPathResource(path).getStream(), name); + searcher = new DbSearcher(config, file.getPath()); + Method method; + method = searcher.getClass().getMethod("btreeSearch", String.class); + DataBlock dataBlock; + dataBlock = (DataBlock) method.invoke(searcher, ip); + String address = dataBlock.getRegion().replace("0|", ""); + char symbol = '|'; + if (address.charAt(address.length() - 1) == symbol) { + address = address.substring(0, address.length() - 1); + } + return address.equals(ElAdminConstant.REGION) ? "内网IP" : address; + } catch (Exception e) { + e.printStackTrace(); + } finally { + if (searcher != null) { + try { + searcher.close(); + } catch (IOException ignored) { + } + } + + } + return ""; + } + + public static String getBrowser(HttpServletRequest request) { + UserAgent userAgent = UserAgent.parseUserAgentString(request.getHeader("User-Agent")); + Browser browser = userAgent.getBrowser(); + return browser.getName(); + } + + /** + * 获得当天是周几 + */ + public static String getWeekDay() { + String[] weekDays = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}; + Calendar cal = Calendar.getInstance(); + cal.setTime(new Date()); + + int w = cal.get(Calendar.DAY_OF_WEEK) - 1; + if (w < 0) { + w = 0; + } + return weekDays[w]; + } +} diff --git a/src/main/java/com/canrd/webmagic/common/utils/ServletUtils.java b/src/main/java/com/canrd/webmagic/common/utils/ServletUtils.java new file mode 100644 index 0000000..72a0bc4 --- /dev/null +++ b/src/main/java/com/canrd/webmagic/common/utils/ServletUtils.java @@ -0,0 +1,40 @@ +package com.canrd.webmagic.common.utils; + +import com.canrd.webmagic.common.constant.ServerResult; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.HttpStatus; + +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; + +/** + * @date 2023-02-02 + */ +@Slf4j +public class ServletUtils { + + + public static void renderServerResult(HttpServletResponse response, ServerResult serverResult, HttpStatus httpStatus) { + try { + response.setStatus(httpStatus.value()); + response.setContentType("application/json"); + response.setCharacterEncoding("utf-8"); + response.getWriter().print(JsonUtil.toJsonString(serverResult)); + } catch (IOException e) { + log.error("ServletUtils#renderServerResult:", e); + } + } + + + public static void renderExcelFileNotFound(HttpServletResponse response) { + try { + response.setStatus(404); + response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8"); + //test.xls是弹出下载对话框的文件名,不能为中文,中文请自行编码 + response.setHeader("Content-Disposition", "attachment;filename=file.xlsx"); + response.sendError(HttpStatus.NOT_FOUND.value(), "下载失败,资源不存在"); + } catch (IOException e) { + log.error("ServletUtils#renderWithResourceNotFound:", e); + } + } +} diff --git a/src/main/java/com/canrd/webmagic/common/utils/SpringContextHolder.java b/src/main/java/com/canrd/webmagic/common/utils/SpringContextHolder.java new file mode 100644 index 0000000..0f8d41a --- /dev/null +++ b/src/main/java/com/canrd/webmagic/common/utils/SpringContextHolder.java @@ -0,0 +1,66 @@ +package com.canrd.webmagic.common.utils; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.DisposableBean; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; + +/** + * @author Jie + * @date 2019-01-07 + */ +@Slf4j +public class SpringContextHolder implements ApplicationContextAware, DisposableBean { + + private static ApplicationContext applicationContext = null; + + /** + * 从静态变量applicationContext中取得Bean, 自动转型为所赋值对象的类型. + */ + @SuppressWarnings("unchecked") + public static <T> T getBean(String name) { + assertContextInjected(); + return (T) applicationContext.getBean(name); + } + + /** + * 从静态变量applicationContext中取得Bean, 自动转型为所赋值对象的类型. + */ + public static <T> T getBean(Class<T> requiredType) { + assertContextInjected(); + return applicationContext.getBean(requiredType); + } + + /** + * 检查ApplicationContext不为空. + */ + private static void assertContextInjected() { + if (applicationContext == null) { + throw new IllegalStateException("applicaitonContext属性未注入, 请在applicationContext" + + ".xml中定义SpringContextHolder或在SpringBoot启动类中注册SpringContextHolder."); + } + } + + /** + * 清除SpringContextHolder中的ApplicationContext为Null. + */ + private static void clearHolder() { + log.debug("清除SpringContextHolder中的ApplicationContext:" + + applicationContext); + applicationContext = null; + } + + @Override + public void destroy() { + SpringContextHolder.clearHolder(); + } + + @Override + public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { + if (SpringContextHolder.applicationContext != null) { + log.warn("SpringContextHolder中的ApplicationContext被覆盖, 原有ApplicationContext为:" + SpringContextHolder.applicationContext); + } + SpringContextHolder.applicationContext = applicationContext; + } +} diff --git a/src/main/java/com/canrd/webmagic/common/utils/StringUtils.java b/src/main/java/com/canrd/webmagic/common/utils/StringUtils.java new file mode 100644 index 0000000..77f998a --- /dev/null +++ b/src/main/java/com/canrd/webmagic/common/utils/StringUtils.java @@ -0,0 +1,1005 @@ +package com.canrd.webmagic.common.utils; + +import com.canrd.webmagic.common.constant.Constant; +import lombok.extern.slf4j.Slf4j; +import org.springframework.util.CollectionUtils; + +import java.math.BigDecimal; +import java.nio.charset.StandardCharsets; +import java.util.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.regex.PatternSyntaxException; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +/** + * @param + * @version 0.1.0 + * @Description + * @return + * @date 2021/4/16 17:44 + * @since 0.1.0 + */ +@Slf4j +public class StringUtils { + /** + * 首字母变小写 + * + * @param str + * @return + */ + public static String firstCharToLowerCase(String str) { + char firstChar = str.charAt(0); + if (firstChar >= 'A' && firstChar <= 'Z') { + char[] arr = str.toCharArray(); + arr[0] += ('a' - 'A'); + return new String(arr); + } + return str; + } + + /** + * 首字母变大写 + * + * @param str + * @return + */ + public static String firstCharToUpperCase(String str) { + char firstChar = str.charAt(0); + if (firstChar >= 'a' && firstChar <= 'z') { + char[] arr = str.toCharArray(); + arr[0] -= ('a' - 'A'); + return new String(arr); + } + return str; + } + + /** + * 判断是否为空 + * + * @param str + * @return + */ + public static boolean isEmpty(final String str) { + return (str == null) || (str.length() == 0); + } + + /** + * 判断是否不为空 + * + * @param str + * @return + */ + public static boolean isNotEmpty(final String str) { + return !isEmpty(str); + } + + /** + * 判断是否空白 + * + * @param str + * @return + */ + public static boolean isBlank(final String str) { + int strLen; + if ((str == null) || ((strLen = str.length()) == 0)) { + return true; + } + for (int i = 0; i < strLen; i++) { + if (!Character.isWhitespace(str.charAt(i))) { + return false; + } + } + return true; + } + + /** + * 判断是否不是空白 + * + * @param str + * @return + */ + public static boolean isNotBlank(final String str) { + return !isBlank(str); + } + + /** + * 判断多个字符串全部是否为空 + * + * @param strings + * @return + */ + public static boolean isAllEmpty(String... strings) { + if (strings == null) { + return true; + } + for (String str : strings) { + if (isNotEmpty(str)) { + return false; + } + } + return true; + } + + /** + * 判断多个字符串其中任意一个是否为空 + * + * @param strings + * @return + */ + public static boolean isHasEmpty(String... strings) { + if (strings == null) { + return true; + } + for (String str : strings) { + if (isEmpty(str)) { + return true; + } + } + return false; + } + + /** + * 判断多个字符串是否都为blank + * + * @param strings + * @return + */ + public static boolean isAllBlank(String... strings) { + if (strings == null) { + return true; + } + for (String str : strings) { + if (isNotBlank(str)) { + return false; + } + } + return true; + } + + /** + * checkValue为 null 或者为 "" 时返回 defaultValue + * + * @param checkValue + * @param defaultValue + * @return + */ + public static String isEmpty(String checkValue, String defaultValue) { + return isEmpty(checkValue) ? defaultValue : checkValue; + } + + /** + * 字符串不为 null 而且不为 "" 并且等于other + * + * @param str + * @param other + * @return + */ + public static boolean isNotEmptyAndEqualsOther(String str, String other) { + return !isEmpty(str) && str.equals(other); + } + + /** + * 字符串不为 null 而且不为 "" 并且不等于other + * + * @param str + * @param other + * @return + */ + public static boolean isNotEmptyAndNotEqualsOther(String str, String... other) { + if (isEmpty(str)) { + return false; + } + for (String s : other) { + if (str.equals(s)) { + return false; + } + } + return true; + } + + /** + * 字符串不等于other + * + * @param str + * @param other + * @return + */ + public static boolean isNotEqualsOther(String str, String... other) { + for (String s : other) { + if (s.equals(str)) { + return false; + } + } + return true; + } + + /** + * 判断字符串不为空 + * + * @param strings + * @return + */ + public static boolean isNotEmpty(String... strings) { + if (strings == null || strings.length == 0) { + return false; + } + for (String str : strings) { + if (str == null || "".equals(str.trim())) { + return false; + } + } + return true; + } + + /** + * 比较字符相等 + * + * @param value + * @param equals + * @return + */ + public static boolean equals(String value, String equals) { + if (isAllEmpty(value, equals)) { + return true; + } + //进一步判断value是不是空,如果是空,要直接equals会报NPE异常 + if (value == null) { + return false; + } + return value.equals(equals); + } + + /** + * @description 将字符串转换为数字数组 + * @author 黄楷涵 + * @date 2020/9/15 + */ + public static int[] stringParseToInt(String str, String regex) { + return stringParseToInt(str.split(regex)); + } + + /** + * @description 将字符串数组转换为数字数组 + * @author dengbin + * @date 2020/9/15 + */ + public static int[] stringParseToInt(String[] str) { + int[] num = new int[str.length]; + for (int i = 0; i < str.length; i++) { + num[i] = Integer.parseInt(str[i]); + } + return num; + } + + /** + * 比较字符串不相等 + * + * @param value + * @param equals + * @return + */ + public static boolean isNotEquals(String value, String equals) { + return !equals(value, equals); + } + + public static String[] split(String content, String separatorChars) { + return splitWorker(content, separatorChars, -1, false); + } + + public static String[] split(String str, String separatorChars, int max) { + return splitWorker(str, separatorChars, max, false); + } + + public static final String[] EMPTY_STRING_ARRAY = new String[0]; + + private static String[] splitWorker(String str, String separatorChars, int max, boolean preserveAllTokens) { + if (str == null) { + return null; + } + int len = str.length(); + if (len == 0) { + return EMPTY_STRING_ARRAY; + } + List<String> list = new ArrayList<String>(); + int sizePlus1 = 1; + int i = 0, start = 0; + boolean match = false; + boolean lastMatch = false; + if (separatorChars == null) { + while (i < len) { + if (Character.isWhitespace(str.charAt(i))) { + if (match || preserveAllTokens) { + lastMatch = true; + if (sizePlus1++ == max) { + i = len; + lastMatch = false; + } + list.add(str.substring(start, i)); + match = false; + } + start = ++i; + continue; + } + lastMatch = false; + match = true; + i++; + } + } else if (separatorChars.length() == 1) { + char sep = separatorChars.charAt(0); + while (i < len) { + if (str.charAt(i) == sep) { + if (match || preserveAllTokens) { + lastMatch = true; + if (sizePlus1++ == max) { + i = len; + lastMatch = false; + } + list.add(str.substring(start, i)); + match = false; + } + start = ++i; + continue; + } + lastMatch = false; + match = true; + i++; + } + } else { + while (i < len) { + if (separatorChars.indexOf(str.charAt(i)) >= 0) { + if (match || preserveAllTokens) { + lastMatch = true; + if (sizePlus1++ == max) { + i = len; + lastMatch = false; + } + list.add(str.substring(start, i)); + match = false; + } + start = ++i; + continue; + } + lastMatch = false; + match = true; + i++; + } + } + if (match || (preserveAllTokens && lastMatch)) { + list.add(str.substring(start, i)); + } + return list.toArray(EMPTY_STRING_ARRAY); + } + + /** + * 消除转义字符 + * + * @param str + * @return + */ + public static String escapeXml(String str) { + if (str == null) { + return ""; + } + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < str.length(); ++i) { + char c = str.charAt(i); + switch (c) { + case '\u00FF': + case '\u0024': + break; + case '&': + sb.append("&"); + break; + case '<': + sb.append("<"); + break; + case '>': + sb.append(">"); + break; + case '\"': + sb.append("""); + break; + case '\'': + sb.append("'"); + break; + default: + if (c <= '\u001F') { + break; + } + if (c >= '\uE000' && c <= '\uF8FF') { + break; + } + if (c >= '\uFFF0') { + break; + } + sb.append(c); + break; + } + } + return sb.toString(); + } + + /** + * 将字符串中特定模式的字符转换成map中对应的值 + * + * @param s 需要转换的字符串 + * @param map 转换所需的键值对集合 + * @return 转换后的字符串 + */ + public static String replace(String s, Map<String, Object> map) { + StringBuilder ret = new StringBuilder((int) (s.length() * 1.5)); + int cursor = 0; + for (int start, end; (start = s.indexOf("${", cursor)) != -1 && (end = s.indexOf("}", start)) != -1; ) { + ret.append(s.substring(cursor, start)).append(map.get(s.substring(start + 2, end))); + cursor = end + 1; + } + ret.append(s.substring(cursor, s.length())); + return ret.toString(); + } + + public static String replace(String s, Object... objs) { + if (objs == null || objs.length == 0) { + return s; + } + if (!s.contains("{}")) { + return s; + } + StringBuilder ret = new StringBuilder((int) (s.length() * 1.5)); + int cursor = 0; + int index = 0; + for (int start; (start = s.indexOf("{}", cursor)) != -1; ) { + ret.append(s.substring(cursor, start)); + if (index < objs.length) { + ret.append(objs[index]); + } else { + ret.append("{}"); + } + cursor = start + 2; + index++; + } + ret.append(s.substring(cursor, s.length())); + return ret.toString(); + } + + + /** + * 转换为字节数组 + * + * @param bytes + * @return + */ + public static String toString(byte[] bytes) { + return new String(bytes, StandardCharsets.UTF_8); + } + + /** + * 转换为字节数组 + * + * @param str + * @return + */ + public static byte[] getBytes(String str) { + return str != null ? str.getBytes(StandardCharsets.UTF_8) : null; + } + + public static boolean isNumeric(String cs) { + if (isEmpty(cs)) { + return false; + } + for (int i = 0, sz = cs.length(); i < sz; ++i) { + if (!Character.isDigit(cs.charAt(i))) { + return false; + } + } + return true; + + } + + /** + * 手机号脱敏 + * + * @param phoneNumber + * @return + */ + public static String desensitizedPhoneNumber(String phoneNumber) { + if (StringUtils.isNotEmpty(phoneNumber)) { + phoneNumber = phoneNumber.replaceAll("(\\w{3})\\w*(\\w{4})", "$1****$2"); + } + return phoneNumber; + } + + /** + * 校验3位小数 + * + * @param str + * @return + */ + public static boolean checkDecimal(String str) { + return Pattern.compile(Constant.DICMAL_REGEXP).matcher(str).find(); + } + + /** + * 校验11位国内手机号 + * <p>规则: 11位数,首位必须为1,第二位可以是3-9;其他位数不限制 + * + * @param phone 手机号 + * @return 格式正确则返回false; 反之为true + * @author A80080 + * @createDate 2020/12/19 + */ + public static boolean checkPhoneNum(String phone) { + return StringUtils.isEmpty(phone) || !Pattern.compile("^1([3-9])[0-9]{9}$").matcher(phone).find(); + } + + /** + * @param str + * @return boolean + * @Description 判断字符串是否含有空格 + * @version 0.1.0 + * @author 邓彬 + * @date 2021/1/13 18:10 + * @since 0.1.0 + */ + public static boolean checkStringContainEmpty(String str) { + return !StringUtils.isEmpty(str) && str.contains(" "); + } + + /** + * 字符串切分 + * + * @param splitStr + * @param splitFlag + * @return + */ + public static List<String> splitTag(String splitStr, String splitFlag) { + return StringUtils.isBlank(splitStr) || StringUtils.isBlank(splitFlag) ? + new ArrayList<>() : + Arrays.stream(splitStr.split(splitFlag)).collect(Collectors.toList()); + } + + /** + * @param name + * @return true代表全是汉字 + * @Description 校验String是否全是中文 + * @version 0.1.0 + * @author 邓彬 + * @date 2021/1/18 19:54 + * @since 0.1.0 + */ + public static boolean checkNameChina(String name) { + boolean res = true; + char[] cTemp = name.toCharArray(); + for (int i = 0; i < name.length(); i++) { + if (!isChinese(cTemp[i])) { + res = false; + break; + } + } + return res; + } + + /** + * @param c + * @return true代表是汉字 + * @Description 判定输入的是否是汉字 + * @version 0.1.0 + * @author 邓彬 + * @date 2021/1/18 19:53 + * @since 0.1.0 + */ + public static boolean isChinese(char c) { + Character.UnicodeBlock ub = Character.UnicodeBlock.of(c); + if (ub == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS + || ub == Character.UnicodeBlock.CJK_COMPATIBILITY_IDEOGRAPHS + || ub == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS_EXTENSION_A + || ub == Character.UnicodeBlock.GENERAL_PUNCTUATION + || ub == Character.UnicodeBlock.CJK_SYMBOLS_AND_PUNCTUATION + || ub == Character.UnicodeBlock.HALFWIDTH_AND_FULLWIDTH_FORMS) { + return true; + } + return false; + } + + /** + * @param c + * @return true代表符合条件 + * @Description 校验某个字符是否是a-z、A-Z、_、0-9 + * @version 0.1.0 + * @author 邓彬 + * @date 2021/1/18 19:56 + * @since 0.1.0 + */ + public static boolean isWord(char c) { + return Pattern.compile("[\\w]").matcher("" + c).matches(); + } + + /** + * @param str + * @return boolean + * @Description 字符串是否仅包含数字和字母 + * @version 0.1.0 + * @author 邓彬 + * @date 2021/1/18 20:00 + * @since 0.1.0 + */ + public static boolean isLetterDigit(String str) { + return str.matches("^[a-z0-9A-Z]+$"); + } + + /** + * @param str + * @return boolean + * @Description 判断是否是纯数字 + * @version 0.1.0 + * @author 邓彬 + * @date 2021/1/23 16:33 + * @since 0.1.0 + */ + public static boolean isDigit(String str) { + return str.matches("^[0-9]+$"); + } + + /** + * 是否合法手机号 + * + * @param phone + * @return + */ + public static boolean isMobilePhone(String phone) { + return Pattern.compile(Constant.PHONE_REGEXP).matcher(phone).matches(); + } + + /** + * 是否脱敏手机号 + * + * @param phone + * @return + */ + public static boolean isDesensitizationMobilePhone(String phone) { + return Pattern.compile(Constant.PHONE_DESENSITIZATION_REGEXP).matcher(phone).matches(); + } + + /** + * 判断String是否是整数<br> + * 支持10进制 + * + * @param s String + * @return 是否为整数 + */ + public static boolean isInteger(String s) { + try { + Integer.parseInt(s); + } catch (NumberFormatException e) { + return false; + } + return true; + } + + /** + * 判断对象能否转 + * + * @param o Object + * @return 是否为Integer + */ + public static boolean isInteger(Object o) { + try { + Integer.parseInt(String.valueOf(o)); + return true; + } catch (NumberFormatException e) { + return false; + } + } + + public static String escapeCharacter(String str) { + if (!isEmpty(str)) { + if (str.contains("\\")) { + str = str.replaceAll("\\\\", "\\\\\\\\"); + } + } + return str; + } + + /** + * @param str + * @return boolean + * @Description 匹配字符串是,数字、26个英文字母、下划线组成的8-16位的字符串组合 + * @version 0.1.0 + * @author A80077-刘始达 + * @date 2021/04/06 + * @since 0.1.0 + */ + public static boolean checkPWD(String str) { + return str.matches("^(?=.*([a-zA-Z].*))(?=.*[0-9].*)[a-zA-Z0-9-_]{8,16}+$"); + } + + /** + * 判断是否包含特殊字符 + * + * @param str + * @return + */ + public static boolean checkSpecificSymbol(String str) { + String regEx = "[ _`~!@#$%^&*()+=|{}':;',\\[\\].<>/?~!@#¥%……&*()——+|{}【】‘;:”“’。,、?]|\n|\r|\t"; + Pattern p = Pattern.compile(regEx); + Matcher m = p.matcher(str); + return m.find(); + } + + /** + * 拼接图片标签url + * + * @param str + * @param ossDomain + * @return + */ + public static String replaceHtmlTag(String str, String ossDomain) { + if (StringUtils.isEmpty(str)) { + return ""; + } + if (StringUtils.isEmpty(ossDomain)) { + return str; + } + return replaceHtmlTag(str, "img", "src", "src=\"" + ossDomain, "\""); + } + + /** + * html格式处理:替换指定标签的属性和值 + * + * @param str 需要处理的字符串 + * @param tag 标签名称 + * @param tagAttrib 要替换的标签属性值 + * @param startTag 新标签开始标记 + * @param endTag 新标签结束标记 + * @return + */ + public static String replaceHtmlTag(String str, String tag, String tagAttrib, String startTag, String endTag) { + String regxpForTag = "<\\s*" + tag + "\\s+([^>]*)\\s*"; + String regxpForTagAttrib = tagAttrib + "=\\s*\"([^\"]+)\""; + Pattern patternForTag = Pattern.compile(regxpForTag, Pattern.CASE_INSENSITIVE); + Pattern patternForAttrib = Pattern.compile(regxpForTagAttrib, Pattern.CASE_INSENSITIVE); + Matcher matcherForTag = patternForTag.matcher(str); + StringBuffer sb = new StringBuffer(); + boolean result = matcherForTag.find(); + while (result) { + StringBuffer stringBuffer = new StringBuffer("<" + tag + " "); + Matcher matcherForAttrib = patternForAttrib.matcher(matcherForTag.group(1)); + if (matcherForAttrib.find()) { + String attributeStr = matcherForAttrib.group(1); + if (!attributeStr.contains("https") && !attributeStr.contains("http")) { + matcherForAttrib.appendReplacement(stringBuffer, startTag + attributeStr + endTag); + } + } + matcherForAttrib.appendTail(stringBuffer); + matcherForTag.appendReplacement(sb, stringBuffer.toString()); + result = matcherForTag.find(); + } + matcherForTag.appendTail(sb); + return sb.toString(); + } + + /** + * @param str + * @return java.util.Map<String, List < String>> + * @Description 字符串的连续切割 返回中文词组 数字和字母的集合 + * @version 0.1.0 + * @author 邓彬 + * @date 2021/4/15 13:53 + * @since 0.1.0 + */ + public static Map<String, List<String>> convertStrToChineseList(String str) { + if (StringUtils.isEmpty(str)) { + return null; + } + Map<String, List<String>> resultMap = new HashMap<>(); + List<String> chineseList; + List<String> charList; + char[] list = str.toCharArray(); + StringBuilder chineseStr = new StringBuilder(); + StringBuilder charStr = new StringBuilder(); + for (char c : list) { + if (StringUtils.isChinese(c)) { + chineseStr.append(c); + charStr.append(Constant.SLASH_MARK_CHARACTER); + } else if (Character.isLetterOrDigit(c)) { + charStr.append(c); + chineseStr.append(Constant.SLASH_MARK_CHARACTER); + } else { + charStr.append(Constant.SLASH_MARK_CHARACTER); + chineseStr.append(Constant.SLASH_MARK_CHARACTER); + } + + } + chineseList = Arrays.stream(chineseStr.toString() + .split(Constant.SLASH_MARK_CHARACTER)) + .filter(StringUtils::isNotEmpty).collect(Collectors.toList()); + charList = Arrays.stream(charStr.toString() + .split(Constant.SLASH_MARK_CHARACTER)) + .filter(StringUtils::isNotEmpty).map(String::toLowerCase).collect(Collectors.toList()); + + resultMap.put("chineseKey", chineseList); + resultMap.put("charKey", charList); + return resultMap; + } + + /** + * 单个字符转换为小写 + * + * @param c + * @return + */ + public static char singleCharToLowerCase(char c) { + if (c >= 'A' && c <= 'Z') { + c += ('a' - 'A'); + return c; + } + return c; + } + + public static boolean isLong(String shopId) { + try { + Long.parseLong(shopId); + } catch (NumberFormatException e) { + return false; + } + return true; + } + + public static String priceFormatting(String price) { + if (StringUtils.isEmpty(price)) { + return Constant.EMPTY_STRING; + } + + String newPrice = ""; + // 价格不存在小数点,则直接返回,无需处理 + if (!price.contains(Constant.POINT_BAR_CHARACTER)) { + newPrice = price; + return newPrice; + } + + // 有小数点,则分割字符串 + String[] s = price.split("\\."); + + // 小数位数大于0 且小于2 且存在非0字符 + if (s[1].length() > 0 && s[1].length() <= 2 && !judgeIsNumeric(s[1])) { + BigDecimal bigDecimal = new BigDecimal(price).setScale(2, BigDecimal.ROUND_HALF_UP); + newPrice = bigDecimal.toPlainString(); + } + // 【保留这个else if】小数位数大于0 且大于2 且存在非0字符,理论上不会存在,因为新增商品时已经限定最多输入2个小数点 + else if (s[1].length() > 0 && s[1].length() > 2 && !judgeIsNumeric(s[1])) { + BigDecimal bigDecimal = new BigDecimal(price).setScale(2, BigDecimal.ROUND_HALF_UP); + String[] split = bigDecimal.toPlainString().split("\\."); + if (split[1].length() > 0 && judgeIsNumeric(split[1])) { + newPrice = new BigDecimal(bigDecimal.toPlainString()).setScale(0, BigDecimal.ROUND_HALF_UP).toPlainString(); + } else { + newPrice = bigDecimal.toPlainString(); + } + } else { + // 小数位数大于0 且小于2 且小数点都是0 + newPrice = new BigDecimal(price).setScale(0, BigDecimal.ROUND_HALF_UP).toPlainString(); + } + return newPrice; + } + + /** + * 判断字符串是否只包含0 + * + * @param str + * @return + */ + private static boolean judgeIsNumeric(String str) { + return Pattern.compile("[0]*").matcher(str).matches(); + } + + /** + * 字符串以","分隔后转为String[] + * + * @param s + * @return + */ + public static String[] string2ArraySplitByComma(String s) { + if (s == null || s.length() == 0) { + return new String[]{}; + } + return s.split(Constant.COMMA_CHARACTER); + } + + public static boolean isStringArrayContainsSpecifiedValue(String[] strings, String value) { + if (null == strings || strings.length <= 0 || value == null || value.length() == 0) { + return false; + } + boolean isContainsSpecifiedValue = false; + for (String s : strings) { + if (s.equals(value)) { + isContainsSpecifiedValue = true; + break; + } + } + return isContainsSpecifiedValue; + } + + /** + * 字符串以","分隔后转为list + * + * @param s + * @return + */ + public static List<String> string2ListSplitByComma(String s) { + List<String> stringList = new ArrayList<>(); + if (s == null || s.length() == 0) { + return stringList; + } + String[] strArray = string2ArraySplitByComma(s); + + for (String st : strArray) { + if (null != st && st.trim() != null || st.trim().length() != 0) { + stringList.add(st.trim()); + } + } + + return stringList; + } + + /** + * 字符串按照特殊符号 + * ".*[`~!@#$%^&*()+=|{}':;',\\[\\].<>/?~!@#¥%……&*()——+|{}【】‘;:”“’。,、?\\\\]+.*" + * 截取转成字符串集合 + * + * @param str + * @return + */ + public static List<String> splitStringBySpecificSymbol(String str) throws PatternSyntaxException { + String regEx = "[`~!@#$%^&*()\\-+=|{}':;,\\[\\].<>/?!¥…()—【】‘;:”“’。,、?\\\\]"; + Pattern p = Pattern.compile(regEx); + Matcher m = p.matcher(str); + return Stream.of(m.replaceAll("_").split("_")).map(String::trim).filter(StringUtils::isNotBlank).collect(Collectors.toList()); + } + + /** + * 将list转为字符串,用逗号隔开 + * + * @param list + * @return + */ + public static String listTransToStr(List<String> list) { + if (CollectionUtils.isEmpty(list)) return null; + StringBuilder sb = new StringBuilder(); + list.forEach(x -> { + sb.append(x); + sb.append(","); + }); + String str = sb.toString(); + return str.substring(0, str.length() - 1); + } + + /** + * 是否包含中英文 + * + * @param str + * @return + */ + public static boolean checkCnAndEn(String str) { + String regEx = Constant.CHI_EN_REGEXP; + Pattern p = Pattern.compile(regEx); + Matcher m = p.matcher(str); + return m.find(); + } + + + /** + * @param code 要隐藏显示的字符串 + * @param head 前面保留的位数 + * @param tail 后面保留的位数 + * @return 处理后的字符串 + */ + public static String getEncryptCode(String code, int head, int tail) { + int body = code.length() - head - tail; + String regexVar = "(\\w{%d})(\\w{%d})(\\w{%d})"; + String regex = String.format(regexVar, head, body, tail); + String bodyPart = code.replaceAll(regex, "$2"); + String bodyEncrypt = bodyPart.replaceAll("\\w", "*"); + String replacement = String.format("$1%s$3", bodyEncrypt); + return code.replaceAll(regex, replacement); + } +} diff --git a/src/main/java/com/canrd/webmagic/common/utils/TemplateFormatUtils.java b/src/main/java/com/canrd/webmagic/common/utils/TemplateFormatUtils.java new file mode 100644 index 0000000..d88c052 --- /dev/null +++ b/src/main/java/com/canrd/webmagic/common/utils/TemplateFormatUtils.java @@ -0,0 +1,57 @@ +package com.canrd.webmagic.common.utils; + +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * + * 消息模板占位符替换工具 + * + * @author fanzhenyu + * @date 2022-04-20 + */ +public class TemplateFormatUtils { + + + /** + * 正则 匹配 { + "多个任意字符" + } + */ + private static final String DEFAULT_REG = "\\{[\\w]+\\}"; + + /** + * + * @param text 原模板 占位符格式必须为:{fieldName} + * @param map 模板参数 + * @return + */ + public static String replaceTemplateContent(String text, Map<String, String> map){ + return replaceTemplateContent(text,map,DEFAULT_REG); + } + + /** + * + * @param text 原模板 占位符格式必须为:{fieldName} + * @param map 模板参数 + * @param reg 自定义占位符样式 - 正则匹配 + * @return + */ + public static String replaceTemplateContent(String text,Map<String,String> map,String reg){ + + if(StringUtils.isBlank(reg)){ + reg = DEFAULT_REG; + } + + Pattern pattern = Pattern.compile(reg); + Matcher m = pattern.matcher(text); + while(m.find()){ + String currentGroup = m.group(); + String currentPattern = currentGroup.replaceAll("^\\{", "").replaceAll("\\}$", "").trim(); + String mapValue = map.get(currentPattern); + if (mapValue != null){ + text = text.replace(currentGroup, mapValue); + } + } + return text; + } +} diff --git a/src/main/java/com/canrd/webmagic/common/utils/ThrowableUtil.java b/src/main/java/com/canrd/webmagic/common/utils/ThrowableUtil.java new file mode 100644 index 0000000..da67ff3 --- /dev/null +++ b/src/main/java/com/canrd/webmagic/common/utils/ThrowableUtil.java @@ -0,0 +1,36 @@ +package com.canrd.webmagic.common.utils; + +import com.canrd.webmagic.common.exception.BusinessException; + +import javax.validation.ConstraintViolationException; +import java.io.PrintWriter; +import java.io.StringWriter; + +/** + * 异常工具 2019-01-06 + */ +public class ThrowableUtil { + + /** + * 获取堆栈信息 + */ + public static String getStackTrace(Throwable throwable) { + StringWriter sw = new StringWriter(); + try (PrintWriter pw = new PrintWriter(sw)) { + throwable.printStackTrace(pw); + return sw.toString(); + } + } + + public static void throwForeignKeyException(Throwable e, String msg) { + Throwable t = e.getCause(); + while ((t != null) && !(t instanceof ConstraintViolationException)) { + t = t.getCause(); + } + if (t != null) { + throw new BusinessException(msg); + } + assert false; + throw new BusinessException("删除失败:" + t.getMessage()); + } +} diff --git a/src/main/java/com/canrd/webmagic/common/utils/TransactionHelper.java b/src/main/java/com/canrd/webmagic/common/utils/TransactionHelper.java new file mode 100644 index 0000000..0ef7d58 --- /dev/null +++ b/src/main/java/com/canrd/webmagic/common/utils/TransactionHelper.java @@ -0,0 +1,27 @@ +package com.canrd.webmagic.common.utils; + +import org.springframework.stereotype.Component; +import org.springframework.transaction.annotation.Propagation; +import org.springframework.transaction.annotation.Transactional; + +import java.util.function.Supplier; + +/** + * @author: + * @date: 2021/12/21 + */ +@Component +public class TransactionHelper { + + @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class) + public <T> T run(Supplier<T> command) { + return command.get(); + } + + @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class) + public void run(Runnable command) { + command.run(); + } + + +} diff --git a/src/main/java/com/canrd/webmagic/config/AdminMetaObjectHandler.java b/src/main/java/com/canrd/webmagic/config/AdminMetaObjectHandler.java new file mode 100644 index 0000000..ddee7b5 --- /dev/null +++ b/src/main/java/com/canrd/webmagic/config/AdminMetaObjectHandler.java @@ -0,0 +1,66 @@ +package com.canrd.webmagic.config; + +import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler; +import com.canrd.webmagic.common.constant.Constant; +import lombok.extern.slf4j.Slf4j; +import org.apache.ibatis.reflection.MetaObject; +import org.springframework.stereotype.Component; + +import java.time.LocalDateTime; + + +@Slf4j +@Component +public class AdminMetaObjectHandler implements MetaObjectHandler { + + + private static final String CREATE_TIME = "createTime"; + private static final String MODIFY_TIME = "modifyTime"; + private static final String CREATE_BY = "createBy"; + private static final String MODIFY_BY = "modifyBy"; + private static final String ENABLE_FLAG = "enableFlag"; + private static final String STRING_CLASS_NAME = "java.lang.String"; + private static final String LONG_CLASS_NAME = "java.lang.Long"; + + @Override + public void insertFill(MetaObject metaObject) { + LocalDateTime now = LocalDateTime.now(); + this.strictInsertFill(metaObject, CREATE_TIME, LocalDateTime.class, now); + this.strictInsertFill(metaObject, MODIFY_TIME, LocalDateTime.class, now); + this.strictInsertFill(metaObject, ENABLE_FLAG, Integer.class, Constant.ENABLE_TEN); + try { +// UserDetails loginUser = SecurityUtils.getUserDetails(); + this.strictInsertFill(metaObject, CREATE_BY, String.class, "system"); + this.strictInsertFill(metaObject, MODIFY_BY, String.class, "system"); + +// this.strictInsertFill(metaObject, CREATE_BY, String.class, loginUser.getUsername()); +// this.strictInsertFill(metaObject, MODIFY_BY, String.class, loginUser.getUsername()); + } catch (Exception e) { + this.strictInsertFill(metaObject, CREATE_BY, String.class, "system"); + + this.strictInsertFill(metaObject, MODIFY_BY, String.class, "system"); + } + + + } + + @Override + public void updateFill(MetaObject metaObject) { + + this.strictUpdateFill(metaObject, MODIFY_TIME, LocalDateTime.class, LocalDateTime.now()); + + + try { +// UserDetails loginUser = SecurityUtils.getUserDetails(); +// if (null == loginUser) { +// return; +// } +// this.strictUpdateFill(metaObject, MODIFY_BY, String.class, loginUser.getUsername()); + this.strictUpdateFill(metaObject, MODIFY_BY, String.class, "system"); + + } catch (Exception e) { + this.strictUpdateFill(metaObject, MODIFY_BY, String.class, "system"); + } + + } +} diff --git a/src/main/java/com/canrd/webmagic/config/ConfigurerAdapter.java b/src/main/java/com/canrd/webmagic/config/ConfigurerAdapter.java new file mode 100644 index 0000000..15e7898 --- /dev/null +++ b/src/main/java/com/canrd/webmagic/config/ConfigurerAdapter.java @@ -0,0 +1,32 @@ +package com.canrd.webmagic.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.cors.CorsConfiguration; +import org.springframework.web.cors.UrlBasedCorsConfigurationSource; +import org.springframework.web.filter.CorsFilter; +import org.springframework.web.servlet.config.annotation.EnableWebMvc; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +/** + * WebMvcConfigurer + * + * @date 2018-11-30 + */ +@Configuration +@EnableWebMvc +public class ConfigurerAdapter implements WebMvcConfigurer { + + + @Bean + public CorsFilter corsFilter() { + UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); + CorsConfiguration config = new CorsConfiguration(); + config.setAllowCredentials(true); + config.addAllowedOrigin("*"); + config.addAllowedHeader("*"); + config.addAllowedMethod("*"); + source.registerCorsConfiguration("/**", config); + return new CorsFilter(source); + } +} diff --git a/src/main/java/com/canrd/webmagic/config/MybatisPlusConfig.java b/src/main/java/com/canrd/webmagic/config/MybatisPlusConfig.java new file mode 100644 index 0000000..306a654 --- /dev/null +++ b/src/main/java/com/canrd/webmagic/config/MybatisPlusConfig.java @@ -0,0 +1,9 @@ +package com.canrd.webmagic.config; + +import org.springframework.context.annotation.Configuration; + + +@Configuration +public class MybatisPlusConfig { + +} diff --git a/src/main/java/com/canrd/webmagic/config/RedisConfig.java b/src/main/java/com/canrd/webmagic/config/RedisConfig.java new file mode 100644 index 0000000..832b184 --- /dev/null +++ b/src/main/java/com/canrd/webmagic/config/RedisConfig.java @@ -0,0 +1,55 @@ +package com.canrd.webmagic.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.data.redis.connection.RedisConnectionFactory; +import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.core.StringRedisTemplate; +import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer; +import org.springframework.data.redis.serializer.StringRedisSerializer; + +import javax.annotation.Resource; + + +public class RedisConfig { + + + @Resource + private LettuceConnectionFactory lqlcfactory; + + /** + * @param connectionFactory + * @return + */ + @Bean + public RedisTemplate redisTemplate(RedisConnectionFactory connectionFactory) { + RedisTemplate template = new RedisTemplate(); + template.setConnectionFactory(connectionFactory); + return template; + } + + /** + * 配置该bean是为了获取logistics 1.0存入redis的数据 + * 序列化方式保持跟logistics 1.0统一 + * + * @param factory + * @return + */ + @Bean(name = "logisticsRedisTemplate") + public RedisTemplate<String, Object> logisticsRedisTemplate(RedisConnectionFactory factory) { + RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>(); + redisTemplate.setConnectionFactory(lqlcfactory); + redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer()); + redisTemplate.setKeySerializer(new StringRedisSerializer()); + return redisTemplate; + } + + @Bean + public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory factory) { + StringRedisTemplate stringRedisTemplate = new StringRedisTemplate(); + // lqlcfactory.setShareNativeConnection(true); + stringRedisTemplate.setConnectionFactory(lqlcfactory); + return stringRedisTemplate; + + } +} diff --git a/src/main/java/com/canrd/webmagic/config/RestTemplateConfig.java b/src/main/java/com/canrd/webmagic/config/RestTemplateConfig.java new file mode 100644 index 0000000..52f7ab0 --- /dev/null +++ b/src/main/java/com/canrd/webmagic/config/RestTemplateConfig.java @@ -0,0 +1,26 @@ +package com.canrd.webmagic.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.http.client.ClientHttpRequestFactory; +import org.springframework.http.client.SimpleClientHttpRequestFactory; +import org.springframework.web.client.RestTemplate; + + +@Configuration +public class RestTemplateConfig { + + @Bean + public RestTemplate restTemplate(ClientHttpRequestFactory factory) { + return new RestTemplate(factory); + } + + @Bean + public ClientHttpRequestFactory simpleClientHttpRequestFactory() { + SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory(); + factory.setReadTimeout(3000); + factory.setConnectTimeout(5000); + return factory; + } + +} diff --git a/src/main/java/com/canrd/webmagic/config/WebConfig.java b/src/main/java/com/canrd/webmagic/config/WebConfig.java new file mode 100644 index 0000000..663b95a --- /dev/null +++ b/src/main/java/com/canrd/webmagic/config/WebConfig.java @@ -0,0 +1,96 @@ +package com.canrd.webmagic.config; + +import com.alibaba.fastjson.support.config.FastJsonConfig; +import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter; +import lombok.extern.slf4j.Slf4j; +import org.springframework.context.annotation.Configuration; +import org.springframework.http.MediaType; +import org.springframework.http.converter.HttpMessageConverter; +import org.springframework.http.converter.StringHttpMessageConverter; +import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; + + +@Slf4j +@Configuration +public class WebConfig implements WebMvcConfigurer { + + + @Override + public void configureMessageConverters(List<HttpMessageConverter<?>> converters) { + FastJsonHttpMessageConverter fastConverter = getFastJsonHttpMessageConverter(); + + StringHttpMessageConverter stringHttpMessageConverter = getStringHttpMessageConverter(); + + /** + * 添加StringHttpMessageConverter ,让其转化String。注意顺序,StringHttpMessageConverter 要在FastJsonHttpMessageConverter 之前。 + * + * https://www.cnblogs.com/slankka/p/11437034.html + */ + converters.add(0, fastConverter); + converters.add(0, stringHttpMessageConverter); + log.info("HttpMessageConverter顺序\n{}", converters.stream().map(c -> c.getClass().getName()).collect(Collectors.joining("\n"))); + } + + private static StringHttpMessageConverter getStringHttpMessageConverter() { + StringHttpMessageConverter stringHttpMessageConverter = new StringHttpMessageConverter(); + stringHttpMessageConverter.setSupportedMediaTypes(Collections.singletonList(MediaType.TEXT_PLAIN)); + return stringHttpMessageConverter; + } + + private static FastJsonHttpMessageConverter getFastJsonHttpMessageConverter() { + FastJsonConfig fastJsonConfig = new FastJsonConfig(); + //解决空值序列化的问题,改为不序列化 + /*fastJsonConfig.setSerializeFilters(new PropertyFilter() { + @Override + public boolean apply(Object source,String name, Object value) { + //if(value.getClass().isPrimitive() == null){ + if(value == null){ + return false; + } + return true; + } + });*/ + fastJsonConfig.setCharset(StandardCharsets.UTF_8); + FastJsonHttpMessageConverter fastConverter = new FastJsonHttpMessageConverter(); + List<MediaType> supportedMediaTypes = new ArrayList<>(); + supportedMediaTypes.add(MediaType.APPLICATION_JSON); + supportedMediaTypes.add(MediaType.APPLICATION_JSON_UTF8); + supportedMediaTypes.add(MediaType.APPLICATION_ATOM_XML); +// supportedMediaTypes.add(MediaType.APPLICATION_FORM_URLENCODED); + supportedMediaTypes.add(MediaType.APPLICATION_OCTET_STREAM); + supportedMediaTypes.add(MediaType.APPLICATION_PDF); + supportedMediaTypes.add(MediaType.APPLICATION_RSS_XML); + supportedMediaTypes.add(MediaType.APPLICATION_XHTML_XML); +// supportedMediaTypes.add(MediaType.APPLICATION_XML); + supportedMediaTypes.add(MediaType.IMAGE_GIF); + supportedMediaTypes.add(MediaType.IMAGE_JPEG); + supportedMediaTypes.add(MediaType.IMAGE_PNG); + supportedMediaTypes.add(MediaType.TEXT_EVENT_STREAM); + supportedMediaTypes.add(MediaType.TEXT_HTML); + supportedMediaTypes.add(MediaType.TEXT_MARKDOWN); + supportedMediaTypes.add(MediaType.TEXT_PLAIN); +// supportedMediaTypes.add(MediaType.TEXT_XML); + fastConverter.setSupportedMediaTypes(supportedMediaTypes); + fastConverter.setFastJsonConfig(fastJsonConfig); + fastConverter.setDefaultCharset(StandardCharsets.UTF_8); + return fastConverter; + } + + @Override + public void addResourceHandlers(ResourceHandlerRegistry registry) { + registry.addResourceHandler("/**").addResourceLocations( + "classpath:/static/"); + registry.addResourceHandler("swagger-ui.html").addResourceLocations( + "classpath:/META-INF/resources/"); + registry.addResourceHandler("/webjars/**").addResourceLocations( + "classpath:/META-INF/resources/webjars/"); + WebMvcConfigurer.super.addResourceHandlers(registry); + } +} diff --git a/src/main/java/com/canrd/webmagic/controller/TestController.java b/src/main/java/com/canrd/webmagic/controller/TestController.java new file mode 100644 index 0000000..2bf7dc8 --- /dev/null +++ b/src/main/java/com/canrd/webmagic/controller/TestController.java @@ -0,0 +1,88 @@ +package com.canrd.webmagic.controller; + + +import com.canrd.webmagic.common.constant.ServerResult; +import com.canrd.webmagic.common.jsr303.OperateGroup; +import com.canrd.webmagic.domain.vo.TestQueryVO; +import com.canrd.webmagic.domain.vo.TestVO; +import com.canrd.webmagic.service.TestService; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import javax.annotation.Resource; + +/** + * (Test)表控制层 + * + * @author makejava + * @since 2024-01-12 14:36:56 + */ +@RestController +@RequestMapping("/lift-hub/test") +public class TestController { + /** + * 服务对象 + */ + @Resource + private TestService testService; + + /** + * 分页查询 + * + * @param testQueryVO 查询条件 + * @return 查询结果 + */ + @PostMapping("/list") + public ServerResult list(@RequestBody @Validated({OperateGroup.List.class}) TestQueryVO testQueryVO) { + return testService.list(testQueryVO); + } + + /** + * 通过主键查询单条数据 + * + * @param testQueryVO 查询条件 + * @return 单条数据 + */ + @PostMapping("/query_by_id") + public ServerResult queryById(@RequestBody TestQueryVO testQueryVO) { + return testService.queryById(testQueryVO); + } + + /** + * 新增数据 + * + * @param testVO 数据VO + * @return 新增结果 + */ + @PostMapping("/add") + public ServerResult add(@RequestBody TestVO testVO) { + return testService.add(testVO); + } + + /** + * 编辑数据 + * + * @param testVO 数据VO + * @return 编辑结果 + */ + @PostMapping("/edit") + public ServerResult edit(@RequestBody TestVO testVO) { + return testService.edit(testVO); + } + + /** + * 删除数据 + * + * @param testQueryVO 查询条件 + * @return 删除是否成功 + */ + @PostMapping("/delete_by_id") + public ServerResult deleteById(@RequestBody TestQueryVO testQueryVO) { + return testService.deleteById(testQueryVO); + } + +} + diff --git a/src/main/java/com/canrd/webmagic/domain/dto/BaseDO.java b/src/main/java/com/canrd/webmagic/domain/dto/BaseDO.java new file mode 100644 index 0000000..3340708 --- /dev/null +++ b/src/main/java/com/canrd/webmagic/domain/dto/BaseDO.java @@ -0,0 +1,75 @@ +package com.canrd.webmagic.domain.dto; + +import com.alibaba.fastjson.annotation.JSONField; +import com.baomidou.mybatisplus.annotation.FieldFill; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableLogic; +import com.canrd.webmagic.common.utils.DateUtil; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer; +import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.SuperBuilder; +import org.apache.ibatis.type.LocalDateTimeTypeHandler; +import org.springframework.format.annotation.DateTimeFormat; + +import java.io.Serializable; +import java.time.LocalDateTime; + + +@Data +@SuperBuilder(toBuilder = true) +@NoArgsConstructor +@AllArgsConstructor +public class BaseDO implements Serializable { + + /** + * 创建时间 + */ + @TableField(fill = FieldFill.INSERT, typeHandler = LocalDateTimeTypeHandler.class) + @JsonFormat(pattern = DateUtil.DATE_TIME, timezone = DateUtil.Zone.GMT8) + @DateTimeFormat(pattern = DateUtil.DATE_TIME) + @JSONField(format = DateUtil.DATE_TIME) + @JsonDeserialize(using = LocalDateTimeDeserializer.class) + @JsonSerialize(using = LocalDateTimeSerializer.class) + private LocalDateTime createTime; + + /** + * 创建人 + */ + @TableField(fill = FieldFill.INSERT) + private String createBy; + + /** + * 修改时间 + */ + @TableField(fill = FieldFill.INSERT_UPDATE, typeHandler = LocalDateTimeTypeHandler.class) + @JsonFormat(pattern = DateUtil.DATE_TIME, timezone = DateUtil.Zone.GMT8) + @DateTimeFormat(pattern = DateUtil.DATE_TIME) + @JSONField(format = DateUtil.DATE_TIME) + @JsonDeserialize(using = LocalDateTimeDeserializer.class) + @JsonSerialize(using = LocalDateTimeSerializer.class) + private LocalDateTime modifyTime; + + /** + * 修改人 + */ + @TableField(fill = FieldFill.INSERT_UPDATE) + private String modifyBy; + + /** + * 是否可用 10-可用 20-删除 30-禁用 + */ + @TableLogic + @TableField(fill = FieldFill.INSERT) + private Integer enableFlag; + + /** + * 版本号--乐观锁预留字段 + */ + private Integer version; +} diff --git a/src/main/java/com/canrd/webmagic/domain/dto/TestDO.java b/src/main/java/com/canrd/webmagic/domain/dto/TestDO.java new file mode 100644 index 0000000..d9412b0 --- /dev/null +++ b/src/main/java/com/canrd/webmagic/domain/dto/TestDO.java @@ -0,0 +1,32 @@ +package com.canrd.webmagic.domain.dto; + + +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.*; +import lombok.experimental.SuperBuilder; + +import java.io.Serializable; + +/** + * (Test)实体类 + * + * @author makejava + * @since 2024-01-12 14:36:56 + */ +@TableName("test") +@Data +@AllArgsConstructor +@ToString +@NoArgsConstructor +@EqualsAndHashCode(callSuper = false) +@SuperBuilder +public class TestDO extends BaseDO implements Serializable { + private static final long serialVersionUID = 856841241099412904L; + + private Integer id; + + private String username; + + private String email; + +} diff --git a/src/main/java/com/canrd/webmagic/domain/vo/BasePageVO.java b/src/main/java/com/canrd/webmagic/domain/vo/BasePageVO.java new file mode 100644 index 0000000..1995e9d --- /dev/null +++ b/src/main/java/com/canrd/webmagic/domain/vo/BasePageVO.java @@ -0,0 +1,43 @@ +package com.canrd.webmagic.domain.vo; + +import com.canrd.webmagic.common.constant.Constant; +import com.canrd.webmagic.common.jsr303.OperateGroup; +import com.fasterxml.jackson.annotation.JsonInclude; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.SuperBuilder; + +import javax.validation.constraints.Max; +import javax.validation.constraints.Min; +import javax.validation.constraints.NotNull; + + +@SuperBuilder(toBuilder = true) +@Data +@NoArgsConstructor +@AllArgsConstructor +public class BasePageVO { + + /** + * 页码 + */ + @JsonInclude(JsonInclude.Include.NON_NULL) + @NotNull(message = "页码不能为空", groups = {OperateGroup.List.class}) + @Min(value = Constant.ONE, message = "页码不能小于1", groups = {OperateGroup.List.class}) + private Integer current = 1; + /** + * 页 + */ + @JsonInclude(JsonInclude.Include.NON_NULL) + @NotNull(message = "每页大小不能为空", groups = {OperateGroup.List.class}) + @Min(value = 1, message = "每页大小不能小于1", groups = {OperateGroup.List.class}) + @Max(value = 5000, message = "每页大小不能大于5000", groups = {OperateGroup.List.class}) + private Integer size = 10; + + /** + * 总数 + */ + @JsonInclude(JsonInclude.Include.NON_NULL) + private Integer total; +} diff --git a/src/main/java/com/canrd/webmagic/domain/vo/TestQueryVO.java b/src/main/java/com/canrd/webmagic/domain/vo/TestQueryVO.java new file mode 100644 index 0000000..645d29b --- /dev/null +++ b/src/main/java/com/canrd/webmagic/domain/vo/TestQueryVO.java @@ -0,0 +1,33 @@ +package com.canrd.webmagic.domain.vo; + +import lombok.*; +import lombok.experimental.SuperBuilder; + +import java.io.Serializable; +import java.util.List; + +/** + * @author makejava + * @since 2024-01-12 14:36:56 + */ +@Data +@AllArgsConstructor +@ToString +@NoArgsConstructor +@EqualsAndHashCode(callSuper = false) +@SuperBuilder +public class TestQueryVO extends BasePageVO implements Serializable { + private static final long serialVersionUID = 542072710112757366L; + + private List<Long> ids; + + + private Integer id; + + private String username; + + private String email; + + +} + diff --git a/src/main/java/com/canrd/webmagic/domain/vo/TestVO.java b/src/main/java/com/canrd/webmagic/domain/vo/TestVO.java new file mode 100644 index 0000000..1ab4616 --- /dev/null +++ b/src/main/java/com/canrd/webmagic/domain/vo/TestVO.java @@ -0,0 +1,31 @@ +package com.canrd.webmagic.domain.vo; + + +import lombok.*; +import lombok.experimental.SuperBuilder; + +import java.io.Serializable; + +/** + * (Test)实体类 + * + * @author makejava + * @since 2024-01-12 14:36:56 + */ +@Data +@AllArgsConstructor +@ToString +@NoArgsConstructor +@EqualsAndHashCode(callSuper = false) +@SuperBuilder +public class TestVO implements Serializable { + private static final long serialVersionUID = 257625796831027116L; + + private Integer id; + + private String username; + + private String email; + + +} diff --git a/src/main/java/com/canrd/webmagic/mapper/TestMapper.java b/src/main/java/com/canrd/webmagic/mapper/TestMapper.java new file mode 100644 index 0000000..5d47008 --- /dev/null +++ b/src/main/java/com/canrd/webmagic/mapper/TestMapper.java @@ -0,0 +1,16 @@ +package com.canrd.webmagic.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.canrd.webmagic.domain.dto.TestDO; + +/** + * (Test)表数据库访问层 + * + * @author makejava + * @since 2024-01-12 14:36:56 + */ +public interface TestMapper extends BaseMapper<TestDO> { + + +} + diff --git a/src/main/java/com/canrd/webmagic/processor/BaiduHotSearchPageProcessor.java b/src/main/java/com/canrd/webmagic/processor/BaiduHotSearchPageProcessor.java new file mode 100644 index 0000000..d43ba90 --- /dev/null +++ b/src/main/java/com/canrd/webmagic/processor/BaiduHotSearchPageProcessor.java @@ -0,0 +1,66 @@ +package com.canrd.webmagic.processor; + +import us.codecraft.webmagic.Page; +import us.codecraft.webmagic.Site; +import us.codecraft.webmagic.Spider; +import us.codecraft.webmagic.processor.PageProcessor; +import us.codecraft.webmagic.selector.Selectable; +import us.codecraft.webmagic.selector.XpathSelector; + +import java.util.List; + +/** + * @author: xms + * @description: TODO + * @date: 2024/4/1 14:19 + * @version: 1.0 + */ +public class BaiduHotSearchPageProcessor implements PageProcessor { + + // 抓取网站的相关配置,包括编码、抓取间隔、重试次数等 + private Site site = Site.me().setRetryTimes(3).setSleepTime(100); + + /** + * 定制爬虫逻辑的核心接口,在这里编写抽取逻辑 + * + * @param page + */ + @Override + public void process(Page page) { + + System.out.println(page.getHtml()); + /** + * 通过page.getHtml()可以获取到main函数中Spider.create(new BaiduHotSearchPageProcessor()).addUrl中的地址的网页内容 + * 1、通过$或css()方法获取到该page html下某元素dom + */ + Selectable selectable = page.getHtml().$(".theme-hot").select( + new XpathSelector("a[@class='item-wrap_2oCLZ']") + ); + List<Selectable> nodes = selectable.nodes(); + + /** + * 获取到指定的dom后,从这些dom中提取元素内容。 + */ + System.out.println("今日百度热搜:"); + for (int i = 1; i <= nodes.size() - 1; i++) { + Selectable node = nodes.get(i); + String link = node.$(".item-wrap_2oCLZ", "href").get(); + String title = node.$(".c-single-text-ellipsis", "text").get(); + System.out.printf("%d、%s,访问地址:%s%n", i, title, link); + } + } + + @Override + public Site getSite() { + return site; + } + + public static void main(String[] args) { + // 创建一个Spider,并把我们的处理器放进去 + Spider.create(new BaiduHotSearchPageProcessor()) + // 添加这个Spider要爬取的网页地址 + .addUrl("https://top.baidu.com/board?platform=pc&sa=pcindex_entry") + // 开启5个线程执行,并开始爬取 + .thread(5).run(); + } +} \ No newline at end of file diff --git a/src/main/java/com/canrd/webmagic/processor/GithubRepoPageProcessor.java b/src/main/java/com/canrd/webmagic/processor/GithubRepoPageProcessor.java new file mode 100644 index 0000000..28153b6 --- /dev/null +++ b/src/main/java/com/canrd/webmagic/processor/GithubRepoPageProcessor.java @@ -0,0 +1,38 @@ +package com.canrd.webmagic.processor; + +import us.codecraft.webmagic.Page; +import us.codecraft.webmagic.Site; +import us.codecraft.webmagic.Spider; +import us.codecraft.webmagic.processor.PageProcessor; + +/** + * @author: xms + * @description: TODO + * @date: 2024/4/1 12:11 + * @version: 1.0 + */ +public class GithubRepoPageProcessor implements PageProcessor { + + private Site site = Site.me().setRetryTimes(3).setSleepTime(1000); + + @Override + public void process(Page page) { + page.addTargetRequests(page.getHtml().links().regex("(https://github\\.com/\\w+/\\w+)").all()); + page.putField("author", page.getUrl().regex("https://github\\.com/(\\w+)/.*").toString()); + page.putField("name", page.getHtml().xpath("//h1[@class='public']/strong/a/text()").toString()); + if (page.getResultItems().get("name")==null){ + //skip this page + page.setSkip(true); + } + page.putField("readme", page.getHtml().xpath("//div[@id='readme']/tidyText()")); + } + + @Override + public Site getSite() { + return site; + } + + public static void main(String[] args) { + Spider.create(new GithubRepoPageProcessor()).addUrl("https://github.com/code4craft").thread(5).run(); + } +} \ No newline at end of file diff --git a/src/main/java/com/canrd/webmagic/processor/NatureSearchPageProcessor.java b/src/main/java/com/canrd/webmagic/processor/NatureSearchPageProcessor.java new file mode 100644 index 0000000..4439fbe --- /dev/null +++ b/src/main/java/com/canrd/webmagic/processor/NatureSearchPageProcessor.java @@ -0,0 +1,69 @@ +package com.canrd.webmagic.processor; + +import org.jsoup.nodes.Element; +import org.jsoup.nodes.TextNode; +import us.codecraft.webmagic.Page; +import us.codecraft.webmagic.Site; +import us.codecraft.webmagic.Spider; +import us.codecraft.webmagic.processor.PageProcessor; +import us.codecraft.webmagic.selector.HtmlNode; +import us.codecraft.webmagic.selector.Selectable; +import us.codecraft.webmagic.selector.XpathSelector; + +import java.util.List; + +/** + * @author: xms + * @description: TODO + * @date: 2024/4/1 14:19 + * @version: 1.0 + */ +public class NatureSearchPageProcessor implements PageProcessor { + + // 抓取网站的相关配置,包括编码、抓取间隔、重试次数等 + private Site site = Site.me().setRetryTimes(3).setSleepTime(100); + + /** + * 定制爬虫逻辑的核心接口,在这里编写抽取逻辑 + * + * @param page + */ + @Override + public void process(Page page) { + + System.out.println(page.getHtml()); + /** + * 通过page.getHtml()可以获取到main函数中Spider.create(new BaiduHotSearchPageProcessor()).addUrl中的地址的网页内容 + * 1、通过$或css()方法获取到该page html下某元素dom + */ + Selectable selectable = page.getHtml().$(".app-article-list-row").select( + new XpathSelector("li[@class='app-article-list-row__item']") + ); + List<Selectable> nodes = selectable.nodes(); + + /** + * 获取到指定的dom后,从这些dom中提取元素内容。 + */ + System.out.println("今日百度热搜:"); + for (int i = 1; i <= nodes.size() - 1; i++) { + Selectable node = nodes.get(i).$(".u-full-height").nodes().get(2).nodes().get(0).$(".u-full-height").select(new XpathSelector("a[@class='c-card__link u-link-inherit']")).nodes().get(0); + String link = node.$("a","href").get(); + String title = node.$("a","text").get(); + System.out.printf("%d、%s,访问地址:%s%n", i, title, link); + } + } + + @Override + public Site getSite() { + return site; + } + + public static void main(String[] args) { + // 创建一个Spider,并把我们的处理器放进去 + Spider.create(new NatureSearchPageProcessor()) + // 添加这个Spider要爬取的网页地址 + .addUrl("https://www.nature.com/search?q=battery&page=1") + // 开启5个线程执行,并开始爬取 + .thread(5).run(); + } +} \ No newline at end of file diff --git a/src/main/java/com/canrd/webmagic/service/TestService.java b/src/main/java/com/canrd/webmagic/service/TestService.java new file mode 100644 index 0000000..47a39b2 --- /dev/null +++ b/src/main/java/com/canrd/webmagic/service/TestService.java @@ -0,0 +1,57 @@ +package com.canrd.webmagic.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.canrd.webmagic.common.constant.ServerResult; +import com.canrd.webmagic.domain.dto.TestDO; +import com.canrd.webmagic.domain.vo.TestQueryVO; +import com.canrd.webmagic.domain.vo.TestVO; + +/** + * (Test)表服务接口 + * + * @author makejava + * @since 2024-01-12 14:36:57 + */ +public interface TestService extends IService<TestDO> { + + /** + * 通过ID查询单条数据 + * + * @param testQueryVO 主键 + * @return 实例对象 + */ + ServerResult queryById(TestQueryVO testQueryVO); + + /** + * 分页查询 + * + * @param testQueryVO 筛选条件 + * @return 查询结果 + */ + ServerResult list(TestQueryVO testQueryVO); + + /** + * 新增数据 + * + * @param testVO 数据VO + * @return 新增结果 + */ + ServerResult add(TestVO testVO); + + /** + * 修改数据 + * + * @param testVO 数据VO + * @return 编辑结果 + */ + ServerResult edit(TestVO testVO); + + /** + * 通过主键删除数据 + * + * @param testQueryVO 筛选条件 + * @return 是否成功 + */ + ServerResult deleteById(TestQueryVO testQueryVO); + +} diff --git a/src/main/java/com/canrd/webmagic/service/impl/TestServiceImpl.java b/src/main/java/com/canrd/webmagic/service/impl/TestServiceImpl.java new file mode 100644 index 0000000..befd08c --- /dev/null +++ b/src/main/java/com/canrd/webmagic/service/impl/TestServiceImpl.java @@ -0,0 +1,133 @@ +package com.canrd.webmagic.service.impl; + +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.collection.CollUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.canrd.webmagic.common.constant.Constant; +import com.canrd.webmagic.common.constant.ServerResult; +import com.canrd.webmagic.common.utils.PageUtils; +import com.canrd.webmagic.domain.dto.TestDO; +import com.canrd.webmagic.domain.vo.TestQueryVO; +import com.canrd.webmagic.domain.vo.TestVO; +import com.canrd.webmagic.mapper.TestMapper; +import com.canrd.webmagic.service.TestService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.Objects; + +/** + * (Test)表服务实现类 + * + * @author makejava + * @since 2024-01-12 14:36:57 + */ +@Slf4j +@Service +public class TestServiceImpl extends ServiceImpl<TestMapper, TestDO> implements TestService { + + + /** + * 通过ID查询单条数据 + * <p> + * testQueryVO 主键 + * + * @return 实例对象 + */ + @Override + public ServerResult queryById(TestQueryVO testQueryVO) { + if (Objects.isNull(testQueryVO.getId())) { + return ServerResult.fail("id 不能为空"); + } + TestDO TestDo = getById(testQueryVO.getId()); + if (Objects.isNull(TestDo)) { + return ServerResult.success(null); + } + return ServerResult.success(BeanUtil.copyProperties(TestDo, TestVO.class)); + } + + /** + * 分页查询 + * + * @param testQueryVO 筛选条件 + * @return 查询结果 + */ + @Override + public ServerResult list(TestQueryVO testQueryVO) { + + LambdaQueryWrapper<TestDO> queryWapper = new LambdaQueryWrapper<TestDO>() + .eq(TestDO::getEnableFlag, Constant.ENABLE_TEN) + .orderByDesc(TestDO::getId); + Page page = new Page<>(testQueryVO.getCurrent(), testQueryVO.getSize()); + IPage<TestDO> iPage = page(page, queryWapper); + testQueryVO.setTotal(Long.valueOf(iPage.getTotal()).intValue()); + return ServerResult.success(PageUtils.getPageReturn(iPage.getRecords(), testQueryVO)); + } + + /** + * 新增数据 + * + * @param testVO 实例对象 + * @return 实例对象 + */ + @Override + public ServerResult add(TestVO testVO) { + //todo 校验 + if (Objects.nonNull(testVO.getId())) { + testVO.setId(null); + } + TestDO testDo = BeanUtil.copyProperties(testVO, TestDO.class); + + save(testDo); + + return ServerResult.success(); + } + + /** + * 修改数据 + * + * @param testVO 实例对象 + * @return 实例对象 + */ + @Override + public ServerResult edit(TestVO testVO) { + //todo 校验 + if (Objects.isNull(testVO.getId())) { + return ServerResult.fail("id 不能为空"); + } + TestDO testDo = BeanUtil.copyProperties(testVO, TestDO.class); + + updateById(testDo); + + return ServerResult.success(); + } + + /** + * 通过主键删除数据 + * + * @param testQueryVO 筛选条件 + * @return 是否成功 + */ + @Override + public ServerResult deleteById(TestQueryVO testQueryVO) { + List<Long> ids = testQueryVO.getIds(); + if (CollUtil.isEmpty(ids)) { + return ServerResult.fail("ids 参数不能为空"); + } + List<TestDO> testList = listByIds(ids); + if (CollUtil.isEmpty(testList)) { + return ServerResult.success(); + } + //todo 校验是否可以逻辑删除 + LambdaUpdateWrapper<TestDO> updateWrapper = new LambdaUpdateWrapper<TestDO>() + .in(TestDO::getId, ids) + .set(TestDO::getEnableFlag, Constant.UNABLE_TWENTY); + update(updateWrapper); + return ServerResult.success(); + } +} diff --git a/src/main/resources/application-local.yml b/src/main/resources/application-local.yml new file mode 100644 index 0000000..bd542a3 --- /dev/null +++ b/src/main/resources/application-local.yml @@ -0,0 +1,186 @@ +mybatis-plus: + configuration: + cache-enabled: false + call-setters-on-nulls: true + jdbc-type-for-null: 'null' + map-underscore-to-camel-case: true + log-impl: org.apache.ibatis.logging.stdout.StdOutImpl + global-config: + db-config: + capital-mode: false + field-strategy: NOT_NULL + id-type: AUTO + logic-delete-field: enable_flag + logic-delete-value: 20 + logic-not-delete-value: 10 + mapper-locations: classpath:/mapper/**.xml + type-aliases-package: com.order.erp.**.dto +#spring: +# datasource: +# dynamic: +# primary: overtime #设置默认的数据源或者数据源组,默认值即为master +# strict: false #设置严格模式,默认false不启动. 启动后在未匹配到指定数据源时候会抛出异常,不启动则使用默认数据源. +# datasource: +# wms_warehouse: +# url: jdbc:mysql://127.0.0.1:3306/overtime?useUnicode=true&characterEncoding=UTF-8&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=Asia/Shanghai&useSSL=false&autoReconnect=true&failOverReadOnly=false&maxReconnects=10&allowMultiQueries=true&useAffectedRows=true&autoReconnectForPools=true&rewriteBatchedStatements=true +# username: root +# password: root +# driver-class-name: com.mysql.cj.jdbc.Driver # 3.2.0开始支持SPI可省略此配置 +# druid: +# initial-size: 5 +# max-active: 20 +# max-evictable-idle-time-millis: 300000 +# max-wait: 60000 +# min-evictable-idle-time-millis: 300000 +# min-idle: 5 +# time-between-eviction-runs-millis: 60000 +# type: com.alibaba.druid.pool.DruidDataSource +spring: + servlet: + multipart: + enabled: true + max-file-size: 100MB + max-request-size: 20MB + file-size-threshold: 20MB + datasource: + db-type: com.alibaba.druid.pool.DruidDataSource + driverClassName: com.mysql.cj.jdbc.Driver + initial-size: 5 + max-active: 30 + max-wait: 30000 + min-idle: 5 + #配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 + timeBetweenEvictionRunsMillis: 30000 + #配置一个连接在池中最小生存的时间,单位是毫秒,30000=30s + minEvictableIdleTimeMillis: 30000 + validationQuery: SELECT 'x' + testWhileIdle: true + testOnBorrow: true + testOnReturn: true + password: canrd@2024 + time-between-eviction-runs-millis: 1000 + url: jdbc:mysql://39.108.227.113:3307/order-erp1?useUnicode=true&characterEncoding=UTF-8&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=Asia/Shanghai&useSSL=false&autoReconnect=true&failOverReadOnly=false&maxReconnects=10&allowMultiQueries=true&useAffectedRows=true&autoReconnectForPools=true + username: root + redis: + database: 0 + host: 39.108.227.113 + lettuce: + pool: + max-active: 2000 + max-idle: 10 + max-wait: -1 + min-idle: 3 + time-between-eviction-runs: 100 + password: '' + port: 6379 + timeout: 2000 + mail: + # 配置 SMTP 服务器地址 + host: smtp.mxhichina.com + # 发送者邮箱,已开通POP3/SMTP服务的邮箱,也就是你自己的 + username: system@canrd.com + # 配置密码,注意不是真正的密码,而是刚刚申请到的授权码 + password: Kelude2015 + # 邮件接收者 + mailRecipient: #邮件接收者邮箱 + # 端口号465或587(QQ邮箱发送邮件仅支持587端口协议) + port: 587 + # 默认的邮件编码为UTF-8 + default-encoding: UTF-8 + # 配置SSL 加密工厂 + properties: + mail: + smtp: + socketFactoryClass: javax.net.ssl.SSLSocketFactory + #表示开启 DEBUG 模式,这样,邮件发送过程的日志会在控制台打印出来,方便排查错误 + debug: true + + freemarker: + template-loader-path: classpath:/template/ + suffix: .flt + enabled: true + cache: false + charset: UTF-8 + content-type: text/html + allow-request-override: false + check-template-location: true + expose-request-attributes: false + expose-session-attributes: false + expose-spring-macro-helpers: false + + +logging: + config: classpath:log4j2-dev.xml + +#登录图形验证码有效时间/分钟 +loginCode: + expiration: 2 + +#密码加密传输,前端公钥加密,后端私钥解密 +rsa: + private_key: MIIBUwIBADANBgkqhkiG9w0BAQEFAASCAT0wggE5AgEAAkEA0vfvyTdGJkdbHkB8mp0f3FE0GYP3AYPaJF7jUd1M0XxFSE2ceK3k2kw20YvQ09NJKk+OMjWQl9WitG9pB6tSCQIDAQABAkA2SimBrWC2/wvauBuYqjCFwLvYiRYqZKThUS3MZlebXJiLB+Ue/gUifAAKIg1avttUZsHBHrop4qfJCwAI0+YRAiEA+W3NK/RaXtnRqmoUUkb59zsZUBLpvZgQPfj1MhyHDz0CIQDYhsAhPJ3mgS64NbUZmGWuuNKp5coY2GIj/zYDMJp6vQIgUueLFXv/eZ1ekgz2Oi67MNCk5jeTF2BurZqNLR3MSmUCIFT3Q6uHMtsB9Eha4u7hS31tj1UWE+D+ADzp59MGnoftAiBeHT7gDMuqeJHPL4b+kC+gzV4FGTfhR9q3tTbklZkD2A== + + +#jwt +jwt: + header: Authorization + # 令牌前缀 + token-start-with: Bearer + # 必须使用最少88位的Base64对该令牌进行编码 + base64-secret: ZmQ0ZGI5NjQ0MDQwY2I4MjMxY2Y3ZmI3MjdhN2ZmMjNhODViOTg1ZGE0NTBjMGM4NDA5NzYxMjdjOWMwYWRmZTBlZjlhNGY3ZTg4Y2U3YTE1ODVkZDU5Y2Y3OGYwZWE1NzUzNWQ2YjFjZDc0NGMxZWU2MmQ3MjY1NzJmNTE0MzI= + # 令牌过期时间 此处单位/毫秒 ,默认2小时,可在此网站生成 https://www.convertworld.com/zh-hans/time/milliseconds.html + token-validity-in-seconds: 720000000 + # 在线用户key + online-key: online-token + # 验证码 + code-key: code-key + +outsys: + sms: + regionId: cn-hangzhou + accessKeyId: LTAIZCPI7OaWud0m + secret: nvtGeScBwRztGeoj8WSp5OWalalgpK + domain: dysmsapi.aliyuncs.com + version: 2017-05-25 + action: SendSms + signName: canrd + templateCode: SMS_173005236 + email: + host: http://core.canrd.com + passwordRecoverKey: http://www.canrd.com/canrd/shop/member/passwordModify + + + +system: + isLoginFailureLock: true + loginFailureLockTime: 5 + loginFailureLockCount: 3 + +openai: + token: Bearer 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 + +# 文件存储路径 +file: + path: /home/canrd/order-erp/files/ + host: http://39.108.227.113 + avatar: /home/order-erp/avatar/ + # 文件大小 /M + maxSize: 100 + avatarMaxSize: 5 + +# 阿里pss图片服务 +oss: + endpoint: https://oss-cn-qingdao.aliyuncs.com + accessKeyId: LTAIZCPI7OaWud0m + accessKeySecret: nvtGeScBwRztGeoj8WSp5OWalalgpK + bucket: order-erp + + +db: + mysql: + ip: 39.108.227.113 + port: 3307 + user: root + password: 123456 + databaseName: order-erp + savePath: /home/canrd/order-erp/files/backup/ diff --git a/src/main/resources/application-prod.yml b/src/main/resources/application-prod.yml new file mode 100644 index 0000000..e1c30ea --- /dev/null +++ b/src/main/resources/application-prod.yml @@ -0,0 +1,186 @@ +mybatis-plus: + configuration: + cache-enabled: false + call-setters-on-nulls: true + jdbc-type-for-null: 'null' + map-underscore-to-camel-case: true + log-impl: org.apache.ibatis.logging.stdout.StdOutImpl + global-config: + db-config: + capital-mode: false + field-strategy: NOT_NULL + id-type: AUTO + logic-delete-field: enable_flag + logic-delete-value: 20 + logic-not-delete-value: 10 + mapper-locations: classpath:/mapper/**.xml + type-aliases-package: com.order.erp.**.dto +#spring: +# datasource: +# dynamic: +# primary: overtime #设置默认的数据源或者数据源组,默认值即为master +# strict: false #设置严格模式,默认false不启动. 启动后在未匹配到指定数据源时候会抛出异常,不启动则使用默认数据源. +# datasource: +# wms_warehouse: +# url: jdbc:mysql://127.0.0.1:3306/overtime?useUnicode=true&characterEncoding=UTF-8&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=Asia/Shanghai&useSSL=false&autoReconnect=true&failOverReadOnly=false&maxReconnects=10&allowMultiQueries=true&useAffectedRows=true&autoReconnectForPools=true&rewriteBatchedStatements=true +# username: root +# password: root +# driver-class-name: com.mysql.cj.jdbc.Driver # 3.2.0开始支持SPI可省略此配置 +# druid: +# initial-size: 5 +# max-active: 20 +# max-evictable-idle-time-millis: 300000 +# max-wait: 60000 +# min-evictable-idle-time-millis: 300000 +# min-idle: 5 +# time-between-eviction-runs-millis: 60000 +# type: com.alibaba.druid.pool.DruidDataSource +spring: + servlet: + multipart: + enabled: true + max-file-size: 100MB + max-request-size: 20MB + file-size-threshold: 20MB + datasource: + db-type: com.alibaba.druid.pool.DruidDataSource + driverClassName: com.mysql.cj.jdbc.Driver + initial-size: 5 + max-active: 30 + max-wait: 30000 + min-idle: 5 + #配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 + timeBetweenEvictionRunsMillis: 30000 + #配置一个连接在池中最小生存的时间,单位是毫秒,30000=30s + minEvictableIdleTimeMillis: 30000 + validationQuery: SELECT 'x' + testWhileIdle: true + testOnBorrow: true + testOnReturn: true + password: 123456 + time-between-eviction-runs-millis: 1000 + url: jdbc:mysql://172.17.0.1:3306/order-erp?useUnicode=true&characterEncoding=UTF-8&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=Asia/Shanghai&useSSL=false&autoReconnect=true&failOverReadOnly=false&maxReconnects=10&allowMultiQueries=true&useAffectedRows=true&autoReconnectForPools=true + username: root + redis: + database: 0 + host: 172.17.0.1 + lettuce: + pool: + max-active: 2000 + max-idle: 10 + max-wait: -1 + min-idle: 3 + time-between-eviction-runs: 100 + password: '' + port: 6379 + timeout: 2000 + mail: + # 配置 SMTP 服务器地址 + host: xxx + # 发送者邮箱,已开通POP3/SMTP服务的邮箱,也就是你自己的 + username: xxxx + # 配置密码,注意不是真正的密码,而是刚刚申请到的授权码 + password: xxx + # 邮件接收者 + mailRecipient: #邮件接收者邮箱 + # 端口号465或587(QQ邮箱发送邮件仅支持587端口协议) + port: 587 + # 默认的邮件编码为UTF-8 + default-encoding: UTF-8 + # 配置SSL 加密工厂 + properties: + mail: + smtp: + socketFactoryClass: javax.net.ssl.SSLSocketFactory + #表示开启 DEBUG 模式,这样,邮件发送过程的日志会在控制台打印出来,方便排查错误 + debug: true + + freemarker: + template-loader-path: classpath:/template/ + suffix: .flt + enabled: true + cache: false + charset: UTF-8 + content-type: text/html + allow-request-override: false + check-template-location: true + expose-request-attributes: false + expose-session-attributes: false + expose-spring-macro-helpers: false + + +logging: + config: classpath:log4j2-prod.xml + +#登录图形验证码有效时间/分钟 +loginCode: + expiration: 2 + +#密码加密传输,前端公钥加密,后端私钥解密 +rsa: + private_key: MIIBUwIBADANBgkqhkiG9w0BAQEFAASCAT0wggE5AgEAAkEA0vfvyTdGJkdbHkB8mp0f3FE0GYP3AYPaJF7jUd1M0XxFSE2ceK3k2kw20YvQ09NJKk+OMjWQl9WitG9pB6tSCQIDAQABAkA2SimBrWC2/wvauBuYqjCFwLvYiRYqZKThUS3MZlebXJiLB+Ue/gUifAAKIg1avttUZsHBHrop4qfJCwAI0+YRAiEA+W3NK/RaXtnRqmoUUkb59zsZUBLpvZgQPfj1MhyHDz0CIQDYhsAhPJ3mgS64NbUZmGWuuNKp5coY2GIj/zYDMJp6vQIgUueLFXv/eZ1ekgz2Oi67MNCk5jeTF2BurZqNLR3MSmUCIFT3Q6uHMtsB9Eha4u7hS31tj1UWE+D+ADzp59MGnoftAiBeHT7gDMuqeJHPL4b+kC+gzV4FGTfhR9q3tTbklZkD2A== + + +#jwt +jwt: + header: Authorization + # 令牌前缀 + token-start-with: Bearer + # 必须使用最少88位的Base64对该令牌进行编码 + base64-secret: ZmQ0ZGI5NjQ0MDQwY2I4MjMxY2Y3ZmI3MjdhN2ZmMjNhODViOTg1ZGE0NTBjMGM4NDA5NzYxMjdjOWMwYWRmZTBlZjlhNGY3ZTg4Y2U3YTE1ODVkZDU5Y2Y3OGYwZWE1NzUzNWQ2YjFjZDc0NGMxZWU2MmQ3MjY1NzJmNTE0MzI= + # 令牌过期时间 此处单位/毫秒 ,默认2小时,可在此网站生成 https://www.convertworld.com/zh-hans/time/milliseconds.html + token-validity-in-seconds: 720000000 + # 在线用户key + online-key: online-token + # 验证码 + code-key: code-key + +outsys: + sms: + regionId: cn-hangzhou + accessKeyId: LTAIZCPI7OaWud0m + secret: nvtGeScBwRztGeoj8WSp5OWalalgpK + domain: dysmsapi.aliyuncs.com + version: 2017-05-25 + action: SendSms + signName: canrd + templateCode: SMS_173005236 + email: + host: xxxx + passwordRecoverKey: xxxxx + + + +system: + isLoginFailureLock: true + loginFailureLockTime: 5 + loginFailureLockCount: 3 + +openai: + token: Bearer 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 + +# 文件存储路径 +file: + path: /home/canrd/order-erp/files/ + host: http://47.104.8.35 + avatar: /home/order-erp/avatar/ + # 文件大小 /M + maxSize: 100 + avatarMaxSize: 5 + +# 阿里pss图片服务 +oss: + endpoint: https://oss-cn-qingdao.aliyuncs.com + accessKeyId: LTAI5t7u1gXR2vm82sd6CkVz + accessKeySecret: m4NzHZZsZiauKmRO8y7DihmcGNdQk4 + bucket: alterego + + +db: + mysql: + ip: 172.17.0.1 + port: 3306 + user: root + password: 123456 + databaseName: order-erp + savePath: /home/canrd/order-erp/files/backup/ diff --git a/src/main/resources/application-test.yml b/src/main/resources/application-test.yml new file mode 100644 index 0000000..2dbd6a1 --- /dev/null +++ b/src/main/resources/application-test.yml @@ -0,0 +1,186 @@ +mybatis-plus: + configuration: + cache-enabled: false + call-setters-on-nulls: true + jdbc-type-for-null: 'null' + map-underscore-to-camel-case: true + log-impl: org.apache.ibatis.logging.stdout.StdOutImpl + global-config: + db-config: + capital-mode: false + field-strategy: NOT_NULL + id-type: AUTO + logic-delete-field: enable_flag + logic-delete-value: 20 + logic-not-delete-value: 10 + mapper-locations: classpath:/mapper/**.xml + type-aliases-package: com.order.erp.**.dto +#spring: +# datasource: +# dynamic: +# primary: overtime #设置默认的数据源或者数据源组,默认值即为master +# strict: false #设置严格模式,默认false不启动. 启动后在未匹配到指定数据源时候会抛出异常,不启动则使用默认数据源. +# datasource: +# wms_warehouse: +# url: jdbc:mysql://127.0.0.1:3306/overtime?useUnicode=true&characterEncoding=UTF-8&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=Asia/Shanghai&useSSL=false&autoReconnect=true&failOverReadOnly=false&maxReconnects=10&allowMultiQueries=true&useAffectedRows=true&autoReconnectForPools=true&rewriteBatchedStatements=true +# username: root +# password: root +# driver-class-name: com.mysql.cj.jdbc.Driver # 3.2.0开始支持SPI可省略此配置 +# druid: +# initial-size: 5 +# max-active: 20 +# max-evictable-idle-time-millis: 300000 +# max-wait: 60000 +# min-evictable-idle-time-millis: 300000 +# min-idle: 5 +# time-between-eviction-runs-millis: 60000 +# type: com.alibaba.druid.pool.DruidDataSource +spring: + servlet: + multipart: + enabled: true + max-file-size: 100MB + max-request-size: 20MB + file-size-threshold: 20MB + datasource: + db-type: com.alibaba.druid.pool.DruidDataSource + driverClassName: com.mysql.cj.jdbc.Driver + initial-size: 5 + max-active: 30 + max-wait: 30000 + min-idle: 5 + #配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 + timeBetweenEvictionRunsMillis: 30000 + #配置一个连接在池中最小生存的时间,单位是毫秒,30000=30s + minEvictableIdleTimeMillis: 30000 + validationQuery: SELECT 'x' + testWhileIdle: true + testOnBorrow: true + testOnReturn: true + password: 123456 + time-between-eviction-runs-millis: 1000 + url: jdbc:mysql://39.108.227.113:3307/order-erp?useUnicode=true&characterEncoding=UTF-8&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=Asia/Shanghai&useSSL=false&autoReconnect=true&failOverReadOnly=false&maxReconnects=10&allowMultiQueries=true&useAffectedRows=true&autoReconnectForPools=true + username: root + redis: + database: 0 + host: 39.108.227.113 + lettuce: + pool: + max-active: 2000 + max-idle: 10 + max-wait: -1 + min-idle: 3 + time-between-eviction-runs: 100 + password: '' + port: 6379 + timeout: 2000 + mail: + # 配置 SMTP 服务器地址 + host: smtp.mxhichina.com + # 发送者邮箱,已开通POP3/SMTP服务的邮箱,也就是你自己的 + username: system@canrd.com + # 配置密码,注意不是真正的密码,而是刚刚申请到的授权码 + password: Kelude2015 + # 邮件接收者 + mailRecipient: #邮件接收者邮箱 + # 端口号465或587(QQ邮箱发送邮件仅支持587端口协议) + port: 587 + # 默认的邮件编码为UTF-8 + default-encoding: UTF-8 + # 配置SSL 加密工厂 + properties: + mail: + smtp: + socketFactoryClass: javax.net.ssl.SSLSocketFactory + #表示开启 DEBUG 模式,这样,邮件发送过程的日志会在控制台打印出来,方便排查错误 + debug: true + + freemarker: + template-loader-path: classpath:/template/ + suffix: .flt + enabled: true + cache: false + charset: UTF-8 + content-type: text/html + allow-request-override: false + check-template-location: true + expose-request-attributes: false + expose-session-attributes: false + expose-spring-macro-helpers: false + + +logging: + config: classpath:log4j2-dev.xml + +#登录图形验证码有效时间/分钟 +loginCode: + expiration: 2 + +#密码加密传输,前端公钥加密,后端私钥解密 +rsa: + private_key: MIIBUwIBADANBgkqhkiG9w0BAQEFAASCAT0wggE5AgEAAkEA0vfvyTdGJkdbHkB8mp0f3FE0GYP3AYPaJF7jUd1M0XxFSE2ceK3k2kw20YvQ09NJKk+OMjWQl9WitG9pB6tSCQIDAQABAkA2SimBrWC2/wvauBuYqjCFwLvYiRYqZKThUS3MZlebXJiLB+Ue/gUifAAKIg1avttUZsHBHrop4qfJCwAI0+YRAiEA+W3NK/RaXtnRqmoUUkb59zsZUBLpvZgQPfj1MhyHDz0CIQDYhsAhPJ3mgS64NbUZmGWuuNKp5coY2GIj/zYDMJp6vQIgUueLFXv/eZ1ekgz2Oi67MNCk5jeTF2BurZqNLR3MSmUCIFT3Q6uHMtsB9Eha4u7hS31tj1UWE+D+ADzp59MGnoftAiBeHT7gDMuqeJHPL4b+kC+gzV4FGTfhR9q3tTbklZkD2A== + + +#jwt +jwt: + header: Authorization + # 令牌前缀 + token-start-with: Bearer + # 必须使用最少88位的Base64对该令牌进行编码 + base64-secret: ZmQ0ZGI5NjQ0MDQwY2I4MjMxY2Y3ZmI3MjdhN2ZmMjNhODViOTg1ZGE0NTBjMGM4NDA5NzYxMjdjOWMwYWRmZTBlZjlhNGY3ZTg4Y2U3YTE1ODVkZDU5Y2Y3OGYwZWE1NzUzNWQ2YjFjZDc0NGMxZWU2MmQ3MjY1NzJmNTE0MzI= + # 令牌过期时间 此处单位/毫秒 ,默认2小时,可在此网站生成 https://www.convertworld.com/zh-hans/time/milliseconds.html + token-validity-in-seconds: 720000000 + # 在线用户key + online-key: online-token + # 验证码 + code-key: code-key + +outsys: + sms: + regionId: cn-hangzhou + accessKeyId: LTAIZCPI7OaWud0m + secret: nvtGeScBwRztGeoj8WSp5OWalalgpK + domain: dysmsapi.aliyuncs.com + version: 2017-05-25 + action: SendSms + signName: canrd + templateCode: SMS_173005236 + email: + host: http://core.canrd.com + passwordRecoverKey: http://www.canrd.com/canrd/shop/member/passwordModify + + + +system: + isLoginFailureLock: true + loginFailureLockTime: 5 + loginFailureLockCount: 3 + +openai: + token: Bearer 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 + +# 文件存储路径 +file: + path: /home/canrd/order-erp/files/ + host: http://39.108.227.113 + avatar: /home/order-erp/avatar/ + # 文件大小 /M + maxSize: 100 + avatarMaxSize: 5 + +# 阿里pss图片服务 +oss: + endpoint: https://oss-cn-qingdao.aliyuncs.com + accessKeyId: LTAIZCPI7OaWud0m + accessKeySecret: nvtGeScBwRztGeoj8WSp5OWalalgpK + bucket: order-erp + + +db: + mysql: + ip: 39.108.227.113 + port: 3307 + user: root + password: 123456 + databaseName: order-erp + savePath: /home/canrd/order-erp/files/backup/ diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml new file mode 100644 index 0000000..88e0b99 --- /dev/null +++ b/src/main/resources/application.yml @@ -0,0 +1,6 @@ +server: + port: 9000 + +spring: + profiles: + active: local \ No newline at end of file diff --git a/src/main/resources/ip2region/ip2region.db b/src/main/resources/ip2region/ip2region.db new file mode 100644 index 0000000..43e1daf Binary files /dev/null and b/src/main/resources/ip2region/ip2region.db differ diff --git a/src/main/resources/log4j2-dev.xml b/src/main/resources/log4j2-dev.xml new file mode 100644 index 0000000..e70ff2c --- /dev/null +++ b/src/main/resources/log4j2-dev.xml @@ -0,0 +1,78 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!--日志级别以及优先级排序: OFF > FATAL > ERROR > WARN > INFO > DEBUG > TRACE > ALL --> +<!--Configuration后面的status,这个用于设置log4j2自身内部的信息输出,可以不设置,当设置成trace时,你会看到log4j2内部各种详细输出 --> +<!--monitorInterval:Log4j能够自动检测修改配置 文件和重新配置本身,设置间隔秒数 --> +<configuration status="debug" monitorInterval="30"> + <!--<contextName>log4j2</contextName>--> + <properties> + <!--${sys:catalina.home}表示linux中环境变量中的tomcat根目录 用户主目录--> + <!--原来用logback时候在统一配置中心也配置一个logging.path=/opt/tomcat-log/${spring.application.name} LOG_PATH是内置变量--> + <!--${sys:user.home} 用户主目录--> + <!-- <Property name="log_path">${sys:user.home}/logs</Property>--> +<!-- <Property name="log_path" value="./logs/" />--> + <property name="console_log_pattern">%d|%t|%traceId|%-5level|%F:%L|%M|%m%n</property> + <!-- 保留日志天数 D H M S 分别对应天 小时 分钟 秒 --> + <property name="KEEP_LOG_DAY">60D</property> + <!-- 日志切割的最小单位 --> + <property name="EVERY_FILE_SIZE">100M</property> + </properties> + <!--先定义所有的appender --> + <appenders> + <console name="Console" target="SYSTEM_OUT"> + <!--输出日志的格式 --> + <PatternLayout charset="UTF-8" pattern="${console_log_pattern}"/> + <ThresholdFilter level="info" onMatch="ACCEPT" onMismatch="DENY" /> + </console> + <!--这个输出控制台的配置 --> + <!--<console name="Console" target="SYSTEM_OUT" follow="false">--> + <!--<!–输出日志的格式 –>--> + <!--<PatternLayout pattern="%date{yyyy-MM-dd HH:mm:ss.SSS} %level [%thread][%file:%line] - %msg%n" />--> + <!--</console>--> + + <!-- 这个会打印出所有的info及以下级别的信息,每次大小超过size,则这size大小的日志会自动存入按年份-月份建立的文件夹下面并进行压缩,作为存档 --> + <RollingFile name="RollingFileInfo" fileName="${sys:logging.path}/logs/overtime.log" filePattern="${sys:logging.path}/logs/$${date:yyyy-MM-dd}/info-%d{yyyy-MM-dd}-%i.log"> + <!--控制台只输出level及以上级别的信息(onMatch),其他的直接拒绝(onMismatch) --> + <ThresholdFilter level="debug" onMatch="ACCEPT" onMismatch="DENY" /> + <!--<Filters>--> + <!--<ThresholdFilter level="INFO"/>--> + <!--<ThresholdFilter level="WARN" onMatch="DENY"--> + <!--onMismatch="NEUTRAL"/>--> + <!--</Filters>--> + <PatternLayout charset="UTF-8" pattern="${console_log_pattern}"/> + <Policies> + <!-- 归档每天的文件 --> + <!--<TimeBasedTriggeringPolicy interval="1" modulate="true"/>--> + <TimeBasedTriggeringPolicy /> + <!-- 限制单个文件大小 --> + <SizeBasedTriggeringPolicy size="${EVERY_FILE_SIZE}"/> + </Policies> + <!-- 限制每天文件个数 --> <!-- DefaultRolloverStrategy属性如不设置,则默认为最多同一文件夹下7个文件,这里设置了100 --> + <DefaultRolloverStrategy max="256"> + <Delete basePath="${sys:logging.path}/logs/" maxDepth="3"> + <IfFileName glob="*/*info*.log"/> + <IfLastModified age="${KEEP_LOG_DAY}"/> + </Delete> + </DefaultRolloverStrategy> + </RollingFile> + </appenders> + <!--然后定义logger,只有定义了logger并引入的appender,appender才会生效 --> + <loggers> + <!--过滤掉spring和mybatis的一些无用的DEBUG信息 --> + <logger name="org.springframework" level="debug" > + <ThresholdFilter level="debug"/> + <appender-ref ref="RollingFileInfo" /> + </logger> + <logger name="org.mybatis" level="DEBUG" > + <ThresholdFilter level="debug"/> + <appender-ref ref="RollingFileInfo" /> + </logger> + <logger name="com.canrd.shop" level="DEBUG" > + <ThresholdFilter level="debug"/> + <appender-ref ref="RollingFileInfo" /> + </logger> + <!--<root level="all">--> + <root level="debug"> + <appender-ref ref="Console" /> + </root> + </loggers> +</configuration> diff --git a/src/main/resources/log4j2-prod.xml b/src/main/resources/log4j2-prod.xml new file mode 100644 index 0000000..1515486 --- /dev/null +++ b/src/main/resources/log4j2-prod.xml @@ -0,0 +1,73 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!--日志级别以及优先级排序: OFF > FATAL > ERROR > WARN > INFO > DEBUG > TRACE > ALL --> +<!--Configuration后面的status,这个用于设置log4j2自身内部的信息输出,可以不设置,当设置成trace时,你会看到log4j2内部各种详细输出 --> +<!--monitorInterval:Log4j能够自动检测修改配置 文件和重新配置本身,设置间隔秒数 --> +<configuration status="INFO" monitorInterval="30"> + <!--<contextName>log4j2</contextName>--> + <properties> + <!--${sys:catalina.home}表示linux中环境变量中的tomcat根目录 用户主目录--> + <!--原来用logback时候在统一配置中心也配置一个logging.path=/opt/tomcat-log/${spring.application.name} LOG_PATH是内置变量--> + <!--${sys:user.home} 用户主目录--> + <!-- <Property name="log_path">${sys:user.home}/logs</Property>--> +<!-- <Property name="log_path" value="./logs/" />--> + <property name="console_log_pattern">%d|%t|%traceId|%-5level|%F:%L|%M|%m%n</property> + <!-- 保留日志天数 D H M S 分别对应天 小时 分钟 秒 --> + <property name="KEEP_LOG_DAY">60D</property> + <!-- 日志切割的最小单位 --> + <property name="EVERY_FILE_SIZE">100M</property> + </properties> + <!--先定义所有的appender --> + <appenders> + <console name="Console" target="SYSTEM_OUT"> + <!--输出日志的格式 --> + <PatternLayout charset="UTF-8" pattern="${console_log_pattern}"/> + <ThresholdFilter level="info" onMatch="ACCEPT" onMismatch="DENY" /> + </console> + <!--这个输出控制台的配置 --> + <!--<console name="Console" target="SYSTEM_OUT" follow="false">--> + <!--<!–输出日志的格式 –>--> + <!--<PatternLayout pattern="%date{yyyy-MM-dd HH:mm:ss.SSS} %level [%thread][%file:%line] - %msg%n" />--> + <!--</console>--> + + <!-- 这个会打印出所有的info及以下级别的信息,每次大小超过size,则这size大小的日志会自动存入按年份-月份建立的文件夹下面并进行压缩,作为存档 --> + <RollingFile name="RollingFileInfo" fileName="${sys:logging.path}/logs/overtime.log" filePattern="${sys:logging.path}/logs/$${date:yyyy-MM-dd}/info-%d{yyyy-MM-dd}-%i.log"> + <!--控制台只输出level及以上级别的信息(onMatch),其他的直接拒绝(onMismatch) --> + <ThresholdFilter level="info" onMatch="ACCEPT" onMismatch="DENY" /> + <!--<Filters>--> + <!--<ThresholdFilter level="INFO"/>--> + <!--<ThresholdFilter level="WARN" onMatch="DENY"--> + <!--onMismatch="NEUTRAL"/>--> + <!--</Filters>--> + <PatternLayout charset="UTF-8" pattern="${console_log_pattern}"/> + <Policies> + <!-- 归档每天的文件 --> + <!--<TimeBasedTriggeringPolicy interval="1" modulate="true"/>--> + <TimeBasedTriggeringPolicy /> + <!-- 限制单个文件大小 --> + <SizeBasedTriggeringPolicy size="${EVERY_FILE_SIZE}"/> + </Policies> + <!-- 限制每天文件个数 --> <!-- DefaultRolloverStrategy属性如不设置,则默认为最多同一文件夹下7个文件,这里设置了100 --> + <DefaultRolloverStrategy max="256"> + <Delete basePath="${sys:logging.path}/logs/" maxDepth="3"> + <IfFileName glob="*/*info*.log"/> + <IfLastModified age="${KEEP_LOG_DAY}"/> + </Delete> + </DefaultRolloverStrategy> + </RollingFile> + </appenders> + <!--然后定义logger,只有定义了logger并引入的appender,appender才会生效 --> + <loggers> + <logger name="org.springframework" level="info" > + <ThresholdFilter level="info"/> + <appender-ref ref="RollingFileInfo" /> + </logger> + <logger name="org.mybatis" level="info" > + <ThresholdFilter level="info"/> + <appender-ref ref="RollingFileInfo" /> + </logger> + <logger name="com.canrd.shop" level="info" > + <ThresholdFilter level="info"/> + <appender-ref ref="RollingFileInfo" /> + </logger> + </loggers> +</configuration> -- libgit2 0.23.3