├── .sonarcloud.properties ├── jframework-excel ├── .gitignore ├── test.xls ├── src │ ├── test │ │ ├── resources │ │ │ ├── test.xls │ │ │ └── test.xlsx │ │ └── java │ │ │ └── com │ │ │ └── github │ │ │ └── neatlife │ │ │ └── jframework │ │ │ ├── .DS_Store │ │ │ └── excel │ │ │ ├── .DS_Store │ │ │ ├── Model.java │ │ │ ├── TestExportBean.java │ │ │ ├── TestImportExcel.java │ │ │ └── TestExportMap.java │ └── main │ │ └── java │ │ └── com │ │ └── github │ │ └── neatlife │ │ └── jframework │ │ └── excel │ │ ├── FieldForSortting.java │ │ ├── ExcelLogs.java │ │ ├── ExcelSheet.java │ │ ├── ExcelLog.java │ │ └── ExcelCell.java └── pom.xml ├── settings.gradle ├── out └── production │ └── jframework │ └── META-INF │ └── jframework.kotlin_module ├── jframework-test ├── .DS_Store ├── src │ ├── .DS_Store │ ├── test │ │ ├── .DS_Store │ │ └── java │ │ │ ├── .DS_Store │ │ │ └── com │ │ │ ├── .DS_Store │ │ │ └── github │ │ │ ├── .DS_Store │ │ │ └── neatlife │ │ │ ├── .DS_Store │ │ │ └── jframework │ │ │ ├── .DS_Store │ │ │ └── test │ │ │ ├── .DS_Store │ │ │ ├── excel │ │ │ └── .DS_Store │ │ │ ├── util │ │ │ ├── DateUtilTest.java │ │ │ ├── HttpUtilTest.java │ │ │ ├── LockUtilTest.java │ │ │ ├── DingTalkUtilTest.java │ │ │ ├── QRCodeUtilTest.java │ │ │ ├── SnowFlakeUtilTest.java │ │ │ ├── HexUtilTest.java │ │ │ ├── ZipUtilTest.java │ │ │ ├── RedisUtilTest.java │ │ │ ├── IpUtilTest.java │ │ │ └── PrecisionUtilTest.java │ │ │ ├── JframeworkApplicationTests.java │ │ │ ├── Person.java │ │ │ ├── SendEmailTest.java │ │ │ ├── RedisTest.java │ │ │ └── json │ │ │ ├── DoubleSpecifyTest.java │ │ │ └── BigDecimalSpecifyTest.java │ └── main │ │ ├── resources │ │ ├── 1.png │ │ ├── banner.txt │ │ └── logback-spring.xml │ │ └── java │ │ └── com │ │ └── github │ │ └── neatlife │ │ └── jframework │ │ └── test │ │ ├── JframeworkApplication.java │ │ ├── dto │ │ └── ExcelUserDto.java │ │ └── controller │ │ ├── TestController.java │ │ └── ExcelController.java ├── .gitignore └── pom.xml ├── .mvn └── wrapper │ ├── maven-wrapper.jar │ └── maven-wrapper.properties ├── Dockerfile ├── publish.sh ├── ci ├── auto_test.sh ├── sonar_analyze.sh └── sonar_preview.sh ├── jframework-fundation ├── src │ └── main │ │ └── java │ │ └── com │ │ └── github │ │ └── neatlife │ │ └── jframework │ │ └── fundation │ │ ├── request │ │ ├── RequestToPo.java │ │ ├── RequestJsonToPo.java │ │ ├── RequestToPoHandlerMethodArgumentResolver.java │ │ └── RequestJsonToPoHandlerMethodArgumentResolver.java │ │ ├── exception │ │ ├── IBusinessExceptionEnum.java │ │ └── BusinessRuntimeException.java │ │ ├── util │ │ ├── MapperUtil.java │ │ ├── DateUtil.java │ │ ├── CaseUtil.java │ │ ├── DingTalkUtil.java │ │ ├── HexUtil.java │ │ ├── CsvUtil.java │ │ ├── Md5Util.java │ │ ├── QRCodeUtil.java │ │ ├── MapUtil.java │ │ ├── SnowFlakeUtil.java │ │ ├── IpUtil.java │ │ ├── JsonUtil.java │ │ └── ZipUtil.java │ │ ├── apiversion │ │ ├── ApiVersion.java │ │ ├── ApiVersionConfig.java │ │ ├── ApiVersionRequestMappingHandlerMapping.java │ │ └── ApiVersionCondition.java │ │ ├── controller │ │ ├── Controller.java │ │ └── K8sController.java │ │ ├── json │ │ ├── DoubleSpecify.java │ │ ├── BigDecimalSpecify.java │ │ ├── DoubleSpecifySerialize.java │ │ └── BigDecimalSpecifySerialize.java │ │ ├── http │ │ └── HttpCode.java │ │ ├── config │ │ └── JFrameworkConfig.java │ │ ├── validator │ │ ├── StringLengthValidator.java │ │ └── StringLength.java │ │ ├── listener │ │ └── ApplicationStartedEventListener.java │ │ └── handler │ │ └── ControllerExceptionHandler.java ├── .gitignore └── pom.xml ├── jframework-jpa ├── src │ └── main │ │ └── java │ │ └── com │ │ └── github │ │ └── neatlife │ │ └── jframework │ │ └── jpa │ │ ├── model │ │ └── BaseEntity.java │ │ └── listener │ │ └── AuditingEntityListener.java ├── .gitignore └── pom.xml ├── .gitlab-ci.yml ├── jframework-springfox ├── src │ └── main │ │ └── java │ │ └── com │ │ └── github │ │ └── neatlife │ │ └── jframework │ │ └── springfox │ │ ├── annotation │ │ └── ApiReferer.java │ │ ├── config │ │ └── Swagger2Config.java │ │ └── plugin │ │ ├── ApiRefererParameterReader.java │ │ └── RequestToPoOperationBuilder.java ├── .gitignore └── pom.xml ├── .gitignore ├── jframework-hystrix ├── .gitignore ├── pom.xml └── src │ └── main │ └── java │ └── com │ └── github │ └── neatlife │ └── jframework │ └── hystrix │ └── RegisterCommandExcutionHook.java ├── jframework-logback ├── .gitignore ├── src │ └── main │ │ └── java │ │ └── com │ │ └── github │ │ └── neatlife │ │ └── jframework │ │ └── logback │ │ ├── AdditionalField.java │ │ ├── CustomCssBuilder.java │ │ ├── CustomHTMLLayout.java │ │ ├── DingTalkAppender.java │ │ └── RedisAppender.java └── pom.xml ├── jframework-redis ├── .gitignore ├── pom.xml └── src │ └── main │ └── java │ └── com │ └── github │ └── neatlife │ └── jframework │ └── redis │ ├── listener │ └── ApplicationStartedEventListener.java │ ├── config │ └── RedisConfig.java │ └── util │ ├── LockUtil.java │ └── RedisUtil.java ├── Jenkinsfile ├── k8s.yml ├── k8s-gateway.yml ├── README.md ├── pom.xml └── mvnw.cmd /.sonarcloud.properties: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /jframework-excel/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | .idea 3 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'jframework' 2 | -------------------------------------------------------------------------------- /out/production/jframework/META-INF/jframework.kotlin_module: -------------------------------------------------------------------------------- 1 |  -------------------------------------------------------------------------------- /jframework-excel/test.xls: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/micro-jframework/jframework/HEAD/jframework-excel/test.xls -------------------------------------------------------------------------------- /jframework-test/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/micro-jframework/jframework/HEAD/jframework-test/.DS_Store -------------------------------------------------------------------------------- /.mvn/wrapper/maven-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/micro-jframework/jframework/HEAD/.mvn/wrapper/maven-wrapper.jar -------------------------------------------------------------------------------- /jframework-test/src/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/micro-jframework/jframework/HEAD/jframework-test/src/.DS_Store -------------------------------------------------------------------------------- /jframework-test/src/test/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/micro-jframework/jframework/HEAD/jframework-test/src/test/.DS_Store -------------------------------------------------------------------------------- /jframework-test/src/main/resources/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/micro-jframework/jframework/HEAD/jframework-test/src/main/resources/1.png -------------------------------------------------------------------------------- /jframework-test/src/test/java/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/micro-jframework/jframework/HEAD/jframework-test/src/test/java/.DS_Store -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM openjdk:8-jdk-alpine 2 | COPY target/jframework.jar /app.jar 3 | ENTRYPOINT ["java", "-jar", "/app.jar", "-Dfile.encoding=utf-8"] -------------------------------------------------------------------------------- /jframework-excel/src/test/resources/test.xls: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/micro-jframework/jframework/HEAD/jframework-excel/src/test/resources/test.xls -------------------------------------------------------------------------------- /jframework-excel/src/test/resources/test.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/micro-jframework/jframework/HEAD/jframework-excel/src/test/resources/test.xlsx -------------------------------------------------------------------------------- /jframework-test/src/test/java/com/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/micro-jframework/jframework/HEAD/jframework-test/src/test/java/com/.DS_Store -------------------------------------------------------------------------------- /.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.0/apache-maven-3.6.0-bin.zip 2 | -------------------------------------------------------------------------------- /jframework-test/src/test/java/com/github/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/micro-jframework/jframework/HEAD/jframework-test/src/test/java/com/github/.DS_Store -------------------------------------------------------------------------------- /jframework-test/src/test/java/com/github/neatlife/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/micro-jframework/jframework/HEAD/jframework-test/src/test/java/com/github/neatlife/.DS_Store -------------------------------------------------------------------------------- /jframework-test/src/test/java/com/github/neatlife/jframework/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/micro-jframework/jframework/HEAD/jframework-test/src/test/java/com/github/neatlife/jframework/.DS_Store -------------------------------------------------------------------------------- /jframework-excel/src/test/java/com/github/neatlife/jframework/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/micro-jframework/jframework/HEAD/jframework-excel/src/test/java/com/github/neatlife/jframework/.DS_Store -------------------------------------------------------------------------------- /jframework-test/src/test/java/com/github/neatlife/jframework/test/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/micro-jframework/jframework/HEAD/jframework-test/src/test/java/com/github/neatlife/jframework/test/.DS_Store -------------------------------------------------------------------------------- /publish.sh: -------------------------------------------------------------------------------- 1 | docker build -t jframework . 2 | docker tag jframework:latest registry.cn-hangzhou.aliyuncs.com/suxiaolin/jframework:latest 3 | docker push registry.cn-hangzhou.aliyuncs.com/suxiaolin/jframework:latest -------------------------------------------------------------------------------- /jframework-excel/src/test/java/com/github/neatlife/jframework/excel/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/micro-jframework/jframework/HEAD/jframework-excel/src/test/java/com/github/neatlife/jframework/excel/.DS_Store -------------------------------------------------------------------------------- /ci/auto_test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | COMMITTER=$(git log -1 --format=%cE) 4 | echo ${COMMITTER} 5 | 6 | if [ $? -eq 0 ]; then 7 | echo "do something for auto_test here." 8 | echo "auto_test over." 9 | fi 10 | -------------------------------------------------------------------------------- /jframework-test/src/test/java/com/github/neatlife/jframework/test/excel/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/micro-jframework/jframework/HEAD/jframework-test/src/test/java/com/github/neatlife/jframework/test/excel/.DS_Store -------------------------------------------------------------------------------- /jframework-fundation/src/main/java/com/github/neatlife/jframework/fundation/request/RequestToPo.java: -------------------------------------------------------------------------------- 1 | package com.github.neatlife.jframework.fundation.request; 2 | 3 | import java.lang.annotation.*; 4 | 5 | @Target(ElementType.PARAMETER) 6 | @Retention(RetentionPolicy.RUNTIME) 7 | @Documented 8 | public @interface RequestToPo { 9 | boolean validator() default true; 10 | } 11 | -------------------------------------------------------------------------------- /jframework-jpa/src/main/java/com/github/neatlife/jframework/jpa/model/BaseEntity.java: -------------------------------------------------------------------------------- 1 | package com.github.neatlife.jframework.jpa.model; 2 | 3 | import lombok.Data; 4 | 5 | /** 6 | * @author suxiaolin 7 | */ 8 | @Data 9 | public abstract class BaseEntity { 10 | 11 | private Long createdAt; 12 | 13 | private Long updatedAt; 14 | 15 | private Long deletedAt; 16 | 17 | } 18 | 19 | -------------------------------------------------------------------------------- /jframework-fundation/src/main/java/com/github/neatlife/jframework/fundation/request/RequestJsonToPo.java: -------------------------------------------------------------------------------- 1 | package com.github.neatlife.jframework.fundation.request; 2 | 3 | import java.lang.annotation.*; 4 | 5 | @Target(ElementType.PARAMETER) 6 | @Retention(RetentionPolicy.RUNTIME) 7 | @Documented 8 | public @interface RequestJsonToPo { 9 | String value(); 10 | 11 | boolean validator() default true; 12 | } 13 | -------------------------------------------------------------------------------- /ci/sonar_analyze.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | mvn clean install -Dmaven.test.skip=true 4 | 5 | mvn --batch-mode sonar:sonar \ 6 | -Dsonar.host.url=http://172.16.67.160:9000 \ 7 | -Dsonar.login=e6ece14822a5d124966dff427f5f13b1ae614101 \ 8 | -Dsonar.issuesReport.html.enable=true \ 9 | -Dsonar.java.binaries=./target/classes/ \ 10 | 11 | 12 | if [ $? -eq 0 ]; then 13 | echo "sonarqube code-analyze over." 14 | fi 15 | -------------------------------------------------------------------------------- /.gitlab-ci.yml: -------------------------------------------------------------------------------- 1 | sonar_preview: 2 | stage: test 3 | script: 4 | - ci/sonar_preview.sh 5 | except: 6 | - master 7 | tags: 8 | - framework 9 | 10 | auto_test: 11 | stage: test 12 | script: ci/auto_test.sh 13 | except: 14 | - master 15 | tags: 16 | - framework 17 | 18 | sonar_analyze: 19 | stage: test 20 | script: 21 | - ci/sonar_analyze.sh 22 | only: 23 | - master 24 | tags: 25 | - framework 26 | -------------------------------------------------------------------------------- /jframework-test/src/main/resources/banner.txt: -------------------------------------------------------------------------------- 1 | 2 | __ _____ __ 3 | |__|/ ____\___________ _____ ______ _ _____________| | __ 4 | | \ __\\_ __ \__ \ / \_/ __ \ \/ \/ / _ \_ __ \ |/ / 5 | | || | | | \// __ \| Y Y \ ___/\ ( <_> ) | \/ < 6 | /\__| ||__| |__| (____ /__|_| /\___ >\/\_/ \____/|__| |__|_ \ 7 | \______| \/ \/ \/ \/ 8 | -------------------------------------------------------------------------------- /jframework-fundation/src/main/java/com/github/neatlife/jframework/fundation/exception/IBusinessExceptionEnum.java: -------------------------------------------------------------------------------- 1 | package com.github.neatlife.jframework.fundation.exception; 2 | 3 | /** 4 | * @author suxiaolin 5 | * @date 2019-03-25 23:55 6 | */ 7 | public interface IBusinessExceptionEnum { 8 | /** 9 | * 获取错误编码 10 | * 11 | * @return 12 | */ 13 | Integer getCode(); 14 | 15 | /** 16 | * 获取错误描述 17 | * 18 | * @return 19 | */ 20 | String getDescription(); 21 | } -------------------------------------------------------------------------------- /jframework-springfox/src/main/java/com/github/neatlife/jframework/springfox/annotation/ApiReferer.java: -------------------------------------------------------------------------------- 1 | package com.github.neatlife.jframework.springfox.annotation; 2 | 3 | import javax.validation.constraints.Null; 4 | import java.lang.annotation.*; 5 | 6 | /** 7 | * @author suxiaolin 8 | * @date 2019-03-07 12:39 9 | */ 10 | @Target(ElementType.METHOD) 11 | @Retention(RetentionPolicy.RUNTIME) 12 | @Documented 13 | public @interface ApiReferer { 14 | 15 | Class value() default Null.class; 16 | 17 | } 18 | 19 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .gradle 2 | /build/ 3 | !gradle/wrapper/gradle-wrapper.jar 4 | 5 | /target/ 6 | !.mvn/wrapper/maven-wrapper.jar 7 | 8 | ### STS ### 9 | .apt_generated 10 | .classpath 11 | .factorypath 12 | .project 13 | .settings 14 | .springBeans 15 | .sts4-cache 16 | 17 | ### IntelliJ IDEA ### 18 | .idea 19 | *.iws 20 | *.iml 21 | *.ipr 22 | 23 | ### NetBeans ### 24 | /nbproject/private/ 25 | /nbbuild/ 26 | /dist/ 27 | /nbdist/ 28 | /.nb-gradle/ 29 | /build/ 30 | 31 | logs/* 32 | 33 | src/main/resources/application.properties -------------------------------------------------------------------------------- /jframework-test/src/test/java/com/github/neatlife/jframework/test/util/DateUtilTest.java: -------------------------------------------------------------------------------- 1 | package com.github.neatlife.jframework.test.util; 2 | 3 | import com.github.neatlife.jframework.fundation.util.DateUtil; 4 | import org.junit.Assert; 5 | import org.junit.Test; 6 | 7 | public class DateUtilTest { 8 | 9 | @Test 10 | public void currentSecond() { 11 | Long timestamp = DateUtil.currentSecond(); 12 | Assert.assertTrue(timestamp > 0); 13 | 14 | System.out.println(timestamp); 15 | } 16 | } -------------------------------------------------------------------------------- /jframework-hystrix/.gitignore: -------------------------------------------------------------------------------- 1 | .gradle 2 | build/ 3 | !gradle/wrapper/gradle-wrapper.jar 4 | 5 | target/ 6 | !.mvn/wrapper/maven-wrapper.jar 7 | 8 | ### STS ### 9 | .apt_generated 10 | .classpath 11 | .factorypath 12 | .project 13 | .settings 14 | .springBeans 15 | .sts4-cache 16 | 17 | ### IntelliJ IDEA ### 18 | .idea 19 | *.iws 20 | *.iml 21 | *.ipr 22 | 23 | ### NetBeans ### 24 | nbproject/private/ 25 | nbbuild/ 26 | dist/ 27 | nbdist/ 28 | .nb-gradle/ 29 | build/ 30 | 31 | logs/* 32 | 33 | src/main/resources/application.properties -------------------------------------------------------------------------------- /jframework-jpa/.gitignore: -------------------------------------------------------------------------------- 1 | .gradle 2 | build/ 3 | !gradle/wrapper/gradle-wrapper.jar 4 | 5 | target/ 6 | !.mvn/wrapper/maven-wrapper.jar 7 | 8 | ### STS ### 9 | .apt_generated 10 | .classpath 11 | .factorypath 12 | .project 13 | .settings 14 | .springBeans 15 | .sts4-cache 16 | 17 | ### IntelliJ IDEA ### 18 | .idea 19 | *.iws 20 | *.iml 21 | *.ipr 22 | 23 | ### NetBeans ### 24 | nbproject/private/ 25 | nbbuild/ 26 | dist/ 27 | nbdist/ 28 | .nb-gradle/ 29 | build/ 30 | 31 | logs/* 32 | 33 | src/main/resources/application.properties -------------------------------------------------------------------------------- /jframework-logback/.gitignore: -------------------------------------------------------------------------------- 1 | .gradle 2 | build/ 3 | !gradle/wrapper/gradle-wrapper.jar 4 | 5 | target/ 6 | !.mvn/wrapper/maven-wrapper.jar 7 | 8 | ### STS ### 9 | .apt_generated 10 | .classpath 11 | .factorypath 12 | .project 13 | .settings 14 | .springBeans 15 | .sts4-cache 16 | 17 | ### IntelliJ IDEA ### 18 | .idea 19 | *.iws 20 | *.iml 21 | *.ipr 22 | 23 | ### NetBeans ### 24 | nbproject/private/ 25 | nbbuild/ 26 | dist/ 27 | nbdist/ 28 | .nb-gradle/ 29 | build/ 30 | 31 | logs/* 32 | 33 | src/main/resources/application.properties -------------------------------------------------------------------------------- /jframework-redis/.gitignore: -------------------------------------------------------------------------------- 1 | .gradle 2 | build/ 3 | !gradle/wrapper/gradle-wrapper.jar 4 | 5 | target/ 6 | !.mvn/wrapper/maven-wrapper.jar 7 | 8 | ### STS ### 9 | .apt_generated 10 | .classpath 11 | .factorypath 12 | .project 13 | .settings 14 | .springBeans 15 | .sts4-cache 16 | 17 | ### IntelliJ IDEA ### 18 | .idea 19 | *.iws 20 | *.iml 21 | *.ipr 22 | 23 | ### NetBeans ### 24 | nbproject/private/ 25 | nbbuild/ 26 | dist/ 27 | nbdist/ 28 | .nb-gradle/ 29 | build/ 30 | 31 | logs/* 32 | 33 | src/main/resources/application.properties -------------------------------------------------------------------------------- /jframework-test/.gitignore: -------------------------------------------------------------------------------- 1 | .gradle 2 | build/ 3 | !gradle/wrapper/gradle-wrapper.jar 4 | 5 | target/ 6 | !.mvn/wrapper/maven-wrapper.jar 7 | 8 | ### STS ### 9 | .apt_generated 10 | .classpath 11 | .factorypath 12 | .project 13 | .settings 14 | .springBeans 15 | .sts4-cache 16 | 17 | ### IntelliJ IDEA ### 18 | .idea 19 | *.iws 20 | *.iml 21 | *.ipr 22 | 23 | ### NetBeans ### 24 | nbproject/private/ 25 | nbbuild/ 26 | dist/ 27 | nbdist/ 28 | .nb-gradle/ 29 | build/ 30 | 31 | logs/* 32 | 33 | src/main/resources/application.properties -------------------------------------------------------------------------------- /jframework-test/src/test/java/com/github/neatlife/jframework/test/JframeworkApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.github.neatlife.jframework.test; 2 | 3 | import org.junit.Test; 4 | import org.junit.runner.RunWith; 5 | import org.springframework.boot.test.context.SpringBootTest; 6 | import org.springframework.test.context.junit4.SpringRunner; 7 | 8 | @RunWith(SpringRunner.class) 9 | @SpringBootTest 10 | public class JframeworkApplicationTests { 11 | 12 | @Test 13 | public void contextLoads() { 14 | } 15 | 16 | } 17 | 18 | -------------------------------------------------------------------------------- /jframework-fundation/.gitignore: -------------------------------------------------------------------------------- 1 | .gradle 2 | build/ 3 | !gradle/wrapper/gradle-wrapper.jar 4 | 5 | target/ 6 | !.mvn/wrapper/maven-wrapper.jar 7 | 8 | ### STS ### 9 | .apt_generated 10 | .classpath 11 | .factorypath 12 | .project 13 | .settings 14 | .springBeans 15 | .sts4-cache 16 | 17 | ### IntelliJ IDEA ### 18 | .idea 19 | *.iws 20 | *.iml 21 | *.ipr 22 | 23 | ### NetBeans ### 24 | nbproject/private/ 25 | nbbuild/ 26 | dist/ 27 | nbdist/ 28 | .nb-gradle/ 29 | build/ 30 | 31 | logs/* 32 | 33 | src/main/resources/application.properties -------------------------------------------------------------------------------- /jframework-springfox/.gitignore: -------------------------------------------------------------------------------- 1 | .gradle 2 | build/ 3 | !gradle/wrapper/gradle-wrapper.jar 4 | 5 | target/ 6 | !.mvn/wrapper/maven-wrapper.jar 7 | 8 | ### STS ### 9 | .apt_generated 10 | .classpath 11 | .factorypath 12 | .project 13 | .settings 14 | .springBeans 15 | .sts4-cache 16 | 17 | ### IntelliJ IDEA ### 18 | .idea 19 | *.iws 20 | *.iml 21 | *.ipr 22 | 23 | ### NetBeans ### 24 | nbproject/private/ 25 | nbbuild/ 26 | dist/ 27 | nbdist/ 28 | .nb-gradle/ 29 | build/ 30 | 31 | logs/* 32 | 33 | src/main/resources/application.properties -------------------------------------------------------------------------------- /jframework-test/src/main/java/com/github/neatlife/jframework/test/JframeworkApplication.java: -------------------------------------------------------------------------------- 1 | package com.github.neatlife.jframework.test; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | /** 7 | * @author suxiaolin 8 | */ 9 | @SpringBootApplication 10 | public class JframeworkApplication { 11 | 12 | public static void main(String[] args) { 13 | SpringApplication.run(JframeworkApplication.class, args); 14 | } 15 | 16 | } 17 | 18 | -------------------------------------------------------------------------------- /jframework-fundation/src/main/java/com/github/neatlife/jframework/fundation/util/MapperUtil.java: -------------------------------------------------------------------------------- 1 | package com.github.neatlife.jframework.fundation.util; 2 | 3 | import org.springframework.beans.BeanUtils; 4 | 5 | /** 6 | * @author suxiaolin 7 | */ 8 | public class MapperUtil { 9 | 10 | public static TARGET to(SOURCE source, Class targetClass) { 11 | TARGET target = BeanUtils.instantiateClass(targetClass); 12 | BeanUtils.copyProperties(source, target); 13 | return target; 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /ci/sonar_preview.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | mvn clean install -Dmaven.test.skip=true 4 | 5 | mvn --batch-mode verify sonar:sonar \ 6 | -Dsonar.host.url=http://172.16.67.160:9000 \ 7 | -Dsonar.login=e6ece14822a5d124966dff427f5f13b1ae614101 \ 8 | -Dsonar.analysis.mode=preview \ 9 | -Dsonar.gitlab.project_id=$CI_PROJECT_ID \ 10 | -Dsonar.gitlab.commit_sha=$CI_COMMIT_SHA \ 11 | -Dsonar.gitlab.ref_name=$CI_COMMIT_REF_NAME 12 | -Dsonar.java.binaries=./target/classes/ \ 13 | 14 | 15 | if [ $? -eq 0 ]; then 16 | echo "sonarqube code-analyze-preview over." 17 | fi 18 | -------------------------------------------------------------------------------- /jframework-logback/src/main/java/com/github/neatlife/jframework/logback/AdditionalField.java: -------------------------------------------------------------------------------- 1 | package com.github.neatlife.jframework.logback; 2 | 3 | public class AdditionalField { 4 | private String key; 5 | private String value; 6 | 7 | public String getKey() { 8 | return this.key; 9 | } 10 | 11 | public void setKey(String key) { 12 | this.key = key; 13 | } 14 | 15 | public String getValue() { 16 | return this.value; 17 | } 18 | 19 | public void setValue(String value) { 20 | this.value = value; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /jframework-fundation/src/main/java/com/github/neatlife/jframework/fundation/apiversion/ApiVersion.java: -------------------------------------------------------------------------------- 1 | package com.github.neatlife.jframework.fundation.apiversion; 2 | 3 | import org.springframework.web.bind.annotation.Mapping; 4 | 5 | import java.lang.annotation.*; 6 | 7 | /** 8 | * @author suxiaolin 9 | * @date 2019-03-07 12:39 10 | */ 11 | @Target({ElementType.METHOD, ElementType.TYPE}) 12 | @Retention(RetentionPolicy.RUNTIME) 13 | @Documented 14 | @Mapping 15 | public @interface ApiVersion { 16 | /** 17 | * 版本号 18 | * 19 | * @return 20 | */ 21 | int value(); 22 | } -------------------------------------------------------------------------------- /jframework-fundation/src/main/java/com/github/neatlife/jframework/fundation/controller/Controller.java: -------------------------------------------------------------------------------- 1 | package com.github.neatlife.jframework.fundation.controller; 2 | 3 | import com.github.neatlife.jframework.fundation.http.Response; 4 | import lombok.extern.slf4j.Slf4j; 5 | import org.springframework.web.bind.annotation.CrossOrigin; 6 | 7 | /** 8 | * @author suxiaolin 9 | * @date 2019-03-07 12:39 10 | */ 11 | @CrossOrigin(origins = "*") 12 | @Slf4j 13 | public class Controller { 14 | 15 | public Response success(E body) { 16 | return Response.success(body); 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /jframework-test/src/test/java/com/github/neatlife/jframework/test/util/HttpUtilTest.java: -------------------------------------------------------------------------------- 1 | package com.github.neatlife.jframework.test.util; 2 | 3 | import com.github.neatlife.jframework.fundation.util.HttpUtil; 4 | import com.github.neatlife.jframework.test.JframeworkApplicationTests; 5 | import org.junit.Assert; 6 | import org.junit.Test; 7 | 8 | import java.io.IOException; 9 | 10 | public class HttpUtilTest extends JframeworkApplicationTests { 11 | 12 | @Test 13 | public void testGet() throws IOException { 14 | Assert.assertNotNull(HttpUtil.get("https://bing.com")); 15 | } 16 | 17 | } -------------------------------------------------------------------------------- /Jenkinsfile: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env groovy 2 | 3 | node('master') { 4 | try { 5 | stage('build') { 6 | checkout scm 7 | 8 | sh "mvn clean package" 9 | sh "docker build -t jframework ." 10 | sh "docker tag jframework:latest registry.cn-hangzhou.aliyuncs.com/suxiaolin/jframework:latest" 11 | sh "docker push registry.cn-hangzhou.aliyuncs.com/suxiaolin/jframework:latest" 12 | } 13 | 14 | stage('deploy') { 15 | sh "rancher kubectl apply -f k8s.yml" 16 | } 17 | } catch(error) { 18 | throw error 19 | } finally { 20 | 21 | } 22 | } -------------------------------------------------------------------------------- /jframework-excel/src/test/java/com/github/neatlife/jframework/excel/Model.java: -------------------------------------------------------------------------------- 1 | package com.github.neatlife.jframework.excel; 2 | 3 | import lombok.Data; 4 | 5 | import java.util.Date; 6 | 7 | /** 8 | * The Model 9 | */ 10 | @Data 11 | public class Model { 12 | @ExcelCell(index = 0) 13 | private String a; 14 | @ExcelCell(index = 1) 15 | private String b; 16 | @ExcelCell(index = 2) 17 | private String c; 18 | @ExcelCell(index = 3) 19 | private Date d; 20 | 21 | public Model(String a, String b, String c, Date d) { 22 | this.a = a; 23 | this.b = b; 24 | this.c = c; 25 | this.d = d; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /jframework-fundation/src/main/java/com/github/neatlife/jframework/fundation/controller/K8sController.java: -------------------------------------------------------------------------------- 1 | package com.github.neatlife.jframework.fundation.controller; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import org.springframework.web.bind.annotation.GetMapping; 5 | import org.springframework.web.bind.annotation.RequestMapping; 6 | import org.springframework.web.bind.annotation.RestController; 7 | 8 | /** 9 | * @author suxiaolin 10 | * @date 2019-03-07 12:39 11 | */ 12 | @RestController 13 | @RequestMapping("/") 14 | @Slf4j 15 | public class K8sController extends Controller { 16 | 17 | @GetMapping("/heartbeat") 18 | public Long heartbeat() { 19 | return System.currentTimeMillis(); 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /jframework-fundation/src/main/java/com/github/neatlife/jframework/fundation/json/DoubleSpecify.java: -------------------------------------------------------------------------------- 1 | package com.github.neatlife.jframework.fundation.json; 2 | 3 | import com.fasterxml.jackson.annotation.JacksonAnnotationsInside; 4 | import com.fasterxml.jackson.databind.annotation.JsonSerialize; 5 | 6 | import java.lang.annotation.Retention; 7 | import java.lang.annotation.RetentionPolicy; 8 | 9 | /** 10 | * @author suxiaolin 11 | * @date 2019-03-20 08:52 12 | */ 13 | @Retention(RetentionPolicy.RUNTIME) 14 | @JacksonAnnotationsInside 15 | @JsonSerialize(using = DoubleSpecifySerialize.class) 16 | public @interface DoubleSpecify { 17 | /** 18 | * 小数位数,默认2位小数 19 | * 20 | * @return 21 | */ 22 | int value() default 2; 23 | } 24 | -------------------------------------------------------------------------------- /jframework-fundation/src/main/java/com/github/neatlife/jframework/fundation/json/BigDecimalSpecify.java: -------------------------------------------------------------------------------- 1 | package com.github.neatlife.jframework.fundation.json; 2 | 3 | import com.fasterxml.jackson.annotation.JacksonAnnotationsInside; 4 | import com.fasterxml.jackson.databind.annotation.JsonSerialize; 5 | 6 | import java.lang.annotation.Retention; 7 | import java.lang.annotation.RetentionPolicy; 8 | 9 | /** 10 | * @author suxiaolin 11 | * @date 2019-03-20 08:52 12 | */ 13 | @Retention(RetentionPolicy.RUNTIME) 14 | @JacksonAnnotationsInside 15 | @JsonSerialize(using = BigDecimalSpecifySerialize.class) 16 | public @interface BigDecimalSpecify { 17 | /** 18 | * 小数位数,默认2位小数 19 | * 20 | * @return 21 | */ 22 | int value() default 2; 23 | } 24 | -------------------------------------------------------------------------------- /jframework-test/src/test/java/com/github/neatlife/jframework/test/util/LockUtilTest.java: -------------------------------------------------------------------------------- 1 | package com.github.neatlife.jframework.redis.util; 2 | 3 | import com.github.neatlife.jframework.test.JframeworkApplicationTests; 4 | import org.junit.Assert; 5 | import org.junit.Test; 6 | 7 | import static org.junit.Assert.*; 8 | 9 | /** 10 | * @author suxiaolin 11 | * @date 2019-04-25 13:35 12 | */ 13 | public class LockUtilTest extends JframeworkApplicationTests { 14 | 15 | @Test 16 | public void tryGetDistributedLock() { 17 | boolean lock = LockUtil.tryGetDistributedLock("lock_key_1", "1", 60); 18 | boolean release = LockUtil.releaseDistributedLock("lock_key_1", "1"); 19 | Assert.assertTrue(lock); 20 | Assert.assertTrue(release); 21 | } 22 | } -------------------------------------------------------------------------------- /jframework-test/src/test/java/com/github/neatlife/jframework/test/Person.java: -------------------------------------------------------------------------------- 1 | package com.github.neatlife.jframework.test; 2 | 3 | /** 4 | * @author suxiaolin 5 | * @date 2019-03-18 16:43 6 | */ 7 | public class Person { 8 | private String name; 9 | private Integer age; 10 | 11 | public Person() { 12 | } 13 | 14 | public Person(String name, Integer age) { 15 | this.name = name; 16 | this.age = age; 17 | } 18 | 19 | public String getName() { 20 | return name; 21 | } 22 | 23 | public void setName(String name) { 24 | this.name = name; 25 | } 26 | 27 | public Integer getAge() { 28 | return age; 29 | } 30 | 31 | public void setAge(Integer age) { 32 | this.age = age; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /jframework-logback/src/main/java/com/github/neatlife/jframework/logback/CustomCssBuilder.java: -------------------------------------------------------------------------------- 1 | package com.github.neatlife.jframework.logback; 2 | 3 | import ch.qos.logback.classic.html.DefaultCssBuilder; 4 | 5 | import static ch.qos.logback.core.CoreConstants.LINE_SEPARATOR; 6 | 7 | /** 8 | * @author suxiaolin 9 | */ 10 | public class CustomCssBuilder extends DefaultCssBuilder { 11 | @Override 12 | public void addCss(StringBuilder sbuf) { 13 | super.addCss(sbuf); 14 | sbuf.append(""); 21 | sbuf.append(LINE_SEPARATOR); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /jframework-fundation/src/main/java/com/github/neatlife/jframework/fundation/util/DateUtil.java: -------------------------------------------------------------------------------- 1 | package com.github.neatlife.jframework.fundation.util; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | 5 | import java.text.SimpleDateFormat; 6 | 7 | /** 8 | * @author suxiaolin 9 | */ 10 | @Slf4j 11 | public final class DateUtil { 12 | 13 | public static Long currentSecond() { 14 | return (System.currentTimeMillis() / 1000); 15 | } 16 | 17 | public static String format(Long ms, String pattern) { 18 | String result = ""; 19 | SimpleDateFormat sdf = new SimpleDateFormat(pattern); 20 | try { 21 | result = sdf.format(ms * 1000); 22 | } catch (Exception e) { 23 | log.warn("", e); 24 | } 25 | return result; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /jframework-test/src/test/java/com/github/neatlife/jframework/test/util/DingTalkUtilTest.java: -------------------------------------------------------------------------------- 1 | package com.github.neatlife.jframework.test.util; 2 | 3 | import com.github.neatlife.jframework.fundation.util.DingTalkUtil; 4 | import com.github.neatlife.jframework.test.JframeworkApplicationTests; 5 | import org.junit.Assert; 6 | import org.junit.Test; 7 | 8 | import java.util.stream.Collectors; 9 | import java.util.stream.Stream; 10 | 11 | /** 12 | * @author suxiaolin 13 | * @date 2019-04-13 8:03 14 | */ 15 | public class DingTalkUtilTest extends JframeworkApplicationTests { 16 | 17 | @Test 18 | public void send() { 19 | boolean success = DingTalkUtil.send("test content", "test title", Stream.of("13288888888").collect(Collectors.toSet())); 20 | Assert.assertTrue(success); 21 | } 22 | } -------------------------------------------------------------------------------- /jframework-test/src/test/java/com/github/neatlife/jframework/test/SendEmailTest.java: -------------------------------------------------------------------------------- 1 | package com.github.neatlife.jframework.test; 2 | 3 | import org.junit.Test; 4 | import org.springframework.beans.factory.annotation.Autowired; 5 | import org.springframework.mail.SimpleMailMessage; 6 | import org.springframework.mail.javamail.JavaMailSender; 7 | 8 | public class SendEmailTest extends JframeworkApplicationTests { 9 | 10 | @Autowired 11 | private JavaMailSender mailSender; 12 | 13 | @Test 14 | public void send() { 15 | SimpleMailMessage message = new SimpleMailMessage(); 16 | message.setFrom("jframework"); 17 | message.setTo("dear.lin@live.com"); 18 | message.setSubject("主题:简单邮件"); 19 | message.setText("测试邮件内容"); 20 | 21 | mailSender.send(message); 22 | } 23 | } -------------------------------------------------------------------------------- /jframework-logback/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | jframework 7 | com.github.neatlife 8 | 0.0.2 9 | 10 | 4.0.0 11 | 12 | jframework-logback 13 | 14 | 15 | 16 | com.github.neatlife 17 | jframework-fundation 18 | 0.0.2 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /jframework-fundation/src/main/java/com/github/neatlife/jframework/fundation/http/HttpCode.java: -------------------------------------------------------------------------------- 1 | package com.github.neatlife.jframework.fundation.http; 2 | 3 | /** 4 | * @author suxiaolin 5 | * @date 2019-03-21 13:07 6 | */ 7 | public enum HttpCode { 8 | /** 9 | * 常用的http状态码 10 | */ 11 | SUCCESS("200"), 12 | BAD_REQUEST("400"), 13 | UNAUTHORIZED("401"), 14 | FORBIDDEN("403"), 15 | NOT_FOUND("404"), 16 | CONFLICT("409"), 17 | LOCKED("423"), 18 | UNSUPPORTED_MEDIA_TYPE("415"), 19 | INTERNAL_SERVER_ERROR("500"), 20 | NOT_IMPLEMENTED("501"), 21 | SERVICE_UNAVAILABLE("503"), 22 | UNKNOWN("-1"); 23 | 24 | private String code; 25 | 26 | HttpCode(String code) { 27 | this.code = code; 28 | } 29 | 30 | @Override 31 | public String toString() { 32 | return code; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /jframework-test/src/test/java/com/github/neatlife/jframework/test/util/QRCodeUtilTest.java: -------------------------------------------------------------------------------- 1 | package com.github.neatlife.jframework.test.util; 2 | 3 | import com.github.neatlife.jframework.fundation.util.QRCodeUtil; 4 | import org.junit.Test; 5 | 6 | import java.io.File; 7 | 8 | /** 9 | * @author suxiaolin 10 | * @date 2019-03-25 23:24 11 | */ 12 | public class QRCodeUtilTest { 13 | 14 | @Test 15 | public void createQRCode() throws Exception { 16 | QRCodeUtil.createQRCode("https://bing.com", "src/main/resources/1.png", 300, 300); 17 | } 18 | 19 | @Test 20 | public void testPath() { 21 | System.out.println(new File("src/main/resources/1.png").toPath()); 22 | } 23 | 24 | @Test 25 | public void read() throws Exception { 26 | String content = QRCodeUtil.read("src/main/resources/1.png"); 27 | System.out.println(content); 28 | } 29 | } -------------------------------------------------------------------------------- /jframework-fundation/src/main/java/com/github/neatlife/jframework/fundation/util/CaseUtil.java: -------------------------------------------------------------------------------- 1 | package com.github.neatlife.jframework.fundation.util; 2 | 3 | import java.util.regex.Matcher; 4 | import java.util.regex.Pattern; 5 | 6 | /** 7 | * @author suxiaolin 8 | */ 9 | public class CaseUtil { 10 | 11 | private static Pattern p = Pattern.compile("_(.)"); 12 | 13 | public static String camelCase(String origin) { 14 | Matcher matcher = p.matcher(origin); 15 | StringBuffer sb = new StringBuffer(); 16 | 17 | while (matcher.find()) { 18 | matcher.appendReplacement(sb, matcher.group(1).toUpperCase()); 19 | } 20 | 21 | matcher.appendTail(sb); 22 | return sb.toString(); 23 | } 24 | 25 | public static String snakeCase(String origin) { 26 | return origin.replaceAll("(.)(\\p{Upper})", "$1_$2").toLowerCase(); 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /jframework-test/src/test/java/com/github/neatlife/jframework/test/util/SnowFlakeUtilTest.java: -------------------------------------------------------------------------------- 1 | package com.github.neatlife.jframework.test.util; 2 | 3 | import com.github.neatlife.jframework.fundation.util.SnowFlakeUtil; 4 | import com.google.common.collect.Lists; 5 | import org.junit.Assert; 6 | import org.junit.Test; 7 | 8 | import java.util.List; 9 | import java.util.stream.Collectors; 10 | 11 | /** 12 | * @author suxiaolin 13 | * @date 2019-04-11 17:11 14 | */ 15 | public class SnowFlakeUtilTest { 16 | 17 | @Test 18 | public void next() { 19 | long count = 1000; 20 | List idList = Lists.newArrayList(); 21 | for (int i = 0; i < count; i++) { 22 | long id = SnowFlakeUtil.next(); 23 | idList.add(id); 24 | System.out.println("id: " + id); 25 | } 26 | Assert.assertEquals(count, idList.stream().distinct().collect(Collectors.toList()).size()); 27 | } 28 | } -------------------------------------------------------------------------------- /jframework-fundation/src/main/java/com/github/neatlife/jframework/fundation/config/JFrameworkConfig.java: -------------------------------------------------------------------------------- 1 | package com.github.neatlife.jframework.fundation.config; 2 | 3 | import lombok.Data; 4 | import org.springframework.boot.context.properties.ConfigurationProperties; 5 | import org.springframework.stereotype.Component; 6 | 7 | /** 8 | * @author suxiaolin 9 | * @date 2019-03-29 08:50 10 | */ 11 | @Data 12 | @ConfigurationProperties(prefix = "jframework") 13 | @Component 14 | public class JFrameworkConfig { 15 | 16 | private Mail mail = new Mail(); 17 | 18 | private Notification notification = new Notification(); 19 | 20 | @Data 21 | public static class Notification { 22 | private String dingTalkUrl; 23 | private String dingTalkTo; 24 | } 25 | 26 | @Data 27 | public static class Mail { 28 | private Boolean enableNoRepeat = false; 29 | private Integer repeatInterval = 600; 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /jframework-fundation/src/main/java/com/github/neatlife/jframework/fundation/apiversion/ApiVersionConfig.java: -------------------------------------------------------------------------------- 1 | package com.github.neatlife.jframework.fundation.apiversion; 2 | 3 | import org.springframework.context.annotation.Bean; 4 | import org.springframework.context.annotation.Configuration; 5 | import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport; 6 | import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping; 7 | 8 | /** 9 | * @author suxiaolin 10 | * @date 2019-03-07 12:39 11 | */ 12 | @Configuration 13 | public class ApiVersionConfig extends WebMvcConfigurationSupport { 14 | 15 | @Override 16 | @Bean 17 | public RequestMappingHandlerMapping requestMappingHandlerMapping() { 18 | RequestMappingHandlerMapping handlerMapping = new ApiVersionRequestMappingHandlerMapping(); 19 | handlerMapping.setOrder(0); 20 | handlerMapping.setInterceptors(getInterceptors()); 21 | return handlerMapping; 22 | } 23 | } -------------------------------------------------------------------------------- /jframework-test/src/test/java/com/github/neatlife/jframework/test/RedisTest.java: -------------------------------------------------------------------------------- 1 | package com.github.neatlife.jframework.test; 2 | 3 | import org.junit.Test; 4 | import org.springframework.beans.factory.annotation.Autowired; 5 | import org.springframework.data.redis.core.RedisTemplate; 6 | 7 | public class RedisTest extends JframeworkApplicationTests { 8 | 9 | @Autowired 10 | private RedisTemplate redisTemplate; 11 | 12 | @Test 13 | public void test1() { 14 | redisTemplate.opsForValue().set("jframework", "hello"); 15 | System.out.println("end..."); 16 | } 17 | 18 | @Test 19 | public void testObjectSet() { 20 | Person person = new Person("小明", 22); 21 | redisTemplate.opsForValue().set("jframework-person", person); 22 | System.out.println("end..."); 23 | } 24 | 25 | @Test 26 | public void testObjectGet() { 27 | Person person = (Person) redisTemplate.opsForValue().get("jframework-person"); 28 | System.out.println("end..."); 29 | } 30 | } -------------------------------------------------------------------------------- /jframework-redis/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | jframework 7 | com.github.neatlife 8 | 0.0.2 9 | 10 | 4.0.0 11 | 12 | jframework-redis 13 | 14 | 15 | 16 | com.github.neatlife 17 | jframework-fundation 18 | 0.0.2 19 | 20 | 21 | 22 | org.springframework.boot 23 | spring-boot-starter-data-redis 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /jframework-fundation/src/main/java/com/github/neatlife/jframework/fundation/validator/StringLengthValidator.java: -------------------------------------------------------------------------------- 1 | package com.github.neatlife.jframework.fundation.validator; 2 | 3 | import org.springframework.util.ObjectUtils; 4 | 5 | import javax.validation.ConstraintValidator; 6 | import javax.validation.ConstraintValidatorContext; 7 | 8 | /** 9 | * @author suxiaolin 10 | */ 11 | public class StringLengthValidator implements ConstraintValidator { 12 | 13 | private int min; 14 | private int max; 15 | 16 | @Override 17 | public void initialize(StringLength parameters) { 18 | min = parameters.min(); 19 | max = parameters.max(); 20 | } 21 | 22 | @Override 23 | public boolean isValid(CharSequence charSequence, ConstraintValidatorContext constraintValidatorContext) { 24 | if (ObjectUtils.isEmpty(charSequence)) { 25 | return true; 26 | } 27 | int length = charSequence.length(); 28 | return length >= min && length <= max; 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /jframework-test/src/test/java/com/github/neatlife/jframework/test/util/HexUtilTest.java: -------------------------------------------------------------------------------- 1 | package com.github.neatlife.jframework.test.util; 2 | 3 | import com.github.neatlife.jframework.fundation.util.HexUtil; 4 | import org.junit.Assert; 5 | import org.junit.Rule; 6 | import org.junit.Test; 7 | import org.junit.rules.ExpectedException; 8 | 9 | public class HexUtilTest { 10 | 11 | @Rule 12 | public final ExpectedException thrown = ExpectedException.none(); 13 | 14 | @Test 15 | public void testHex2Bytes() throws Exception { 16 | Assert.assertArrayEquals(new byte[]{-81, 27}, HexUtil.hex2Bytes("AF1B")); 17 | 18 | thrown.expect(Exception.class); 19 | HexUtil.hex2Bytes("AF1"); 20 | } 21 | 22 | @Test 23 | public void testBytes2Hex() { 24 | Assert.assertEquals("af1b", HexUtil.bytes2Hex(new byte[]{-81, 27})); 25 | Assert.assertEquals("af1b", 26 | HexUtil.bytes2Hex(new byte[]{-81, 27}, true)); 27 | Assert.assertEquals("AF1B", 28 | HexUtil.bytes2Hex(new byte[]{-81, 27}, false)); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /jframework-hystrix/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | jframework 7 | com.github.neatlife 8 | 0.0.2 9 | 10 | 4.0.0 11 | 12 | jframework-hystrix 13 | 14 | 15 | 16 | 17 | org.springframework.cloud 18 | spring-cloud-starter-netflix-hystrix 19 | 2.1.0.RELEASE 20 | 21 | 22 | 23 | com.github.neatlife 24 | jframework-logback 25 | 0.0.2 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /jframework-test/src/main/java/com/github/neatlife/jframework/test/dto/ExcelUserDto.java: -------------------------------------------------------------------------------- 1 | package com.github.neatlife.jframework.test.dto; 2 | 3 | public class ExcelUserDto { 4 | /** 5 | * 用户账号 6 | */ 7 | private String username; 8 | /** 9 | * 用户昵称 10 | */ 11 | private String nickname; 12 | /** 13 | * 用户等级 14 | */ 15 | private String level; 16 | 17 | public ExcelUserDto(String username, String nickname, String level) { 18 | this.username = username; 19 | this.nickname = nickname; 20 | this.level = level; 21 | } 22 | 23 | public String getUsername() { 24 | return username; 25 | } 26 | 27 | public void setUsername(String username) { 28 | this.username = username; 29 | } 30 | 31 | public String getNickname() { 32 | return nickname; 33 | } 34 | 35 | public void setNickname(String nickname) { 36 | this.nickname = nickname; 37 | } 38 | 39 | public String getLevel() { 40 | return level; 41 | } 42 | 43 | public void setLevel(String level) { 44 | this.level = level; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /jframework-jpa/src/main/java/com/github/neatlife/jframework/jpa/listener/AuditingEntityListener.java: -------------------------------------------------------------------------------- 1 | package com.github.neatlife.jframework.jpa.listener; 2 | 3 | import com.github.neatlife.jframework.fundation.util.DateUtil; 4 | import com.github.neatlife.jframework.jpa.model.BaseEntity; 5 | import lombok.extern.slf4j.Slf4j; 6 | import org.springframework.context.annotation.Configuration; 7 | 8 | import javax.persistence.PrePersist; 9 | import javax.persistence.PreRemove; 10 | import javax.persistence.PreUpdate; 11 | 12 | /** 13 | * @author suxiaolin 14 | */ 15 | @Configuration 16 | @Slf4j 17 | public class AuditingEntityListener { 18 | 19 | @PrePersist 20 | public void touchCreated(BaseEntity target) { 21 | target.setCreatedAt(DateUtil.currentSecond()); 22 | target.setUpdatedAt(DateUtil.currentSecond()); 23 | } 24 | 25 | @PreUpdate 26 | public void touchUpdate(BaseEntity target) { 27 | target.setUpdatedAt(DateUtil.currentSecond()); 28 | } 29 | 30 | @PreRemove 31 | public void touchDeleted(BaseEntity target) { 32 | target.setDeletedAt(DateUtil.currentSecond()); 33 | } 34 | 35 | } 36 | 37 | -------------------------------------------------------------------------------- /jframework-excel/src/test/java/com/github/neatlife/jframework/excel/TestExportBean.java: -------------------------------------------------------------------------------- 1 | package com.github.neatlife.jframework.excel; 2 | 3 | import org.junit.Test; 4 | 5 | import java.io.File; 6 | import java.io.FileOutputStream; 7 | import java.io.IOException; 8 | import java.io.OutputStream; 9 | import java.util.*; 10 | 11 | public class TestExportBean { 12 | @Test 13 | public void exportXls() throws IOException { 14 | //用排序的Map且Map的键应与ExcelCell注解的index对应 15 | Map map = new LinkedHashMap<>(); 16 | map.put("a", "姓名"); 17 | map.put("b", "年龄"); 18 | map.put("c", "性别"); 19 | map.put("d", "出生日期"); 20 | Collection dataset = new ArrayList(); 21 | dataset.add(new Model("", "", "", null)); 22 | dataset.add(new Model(null, null, null, null)); 23 | dataset.add(new Model("小明", "20", "男", new Date())); 24 | dataset.add(new Model("王五", "34", "男", new Date())); 25 | File f = new File("test.xls"); 26 | OutputStream out = new FileOutputStream(f); 27 | 28 | ExcelUtil.exportExcel(map, dataset, out); 29 | out.close(); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /jframework-logback/src/main/java/com/github/neatlife/jframework/logback/CustomHTMLLayout.java: -------------------------------------------------------------------------------- 1 | package com.github.neatlife.jframework.logback; 2 | 3 | import ch.qos.logback.classic.html.DefaultThrowableRenderer; 4 | import ch.qos.logback.classic.html.HTMLLayout; 5 | import ch.qos.logback.classic.spi.ILoggingEvent; 6 | import ch.qos.logback.core.html.IThrowableRenderer; 7 | 8 | /** 9 | * @author suxiaolin 10 | */ 11 | public class CustomHTMLLayout extends HTMLLayout { 12 | 13 | static final String DEFAULT_CONVERSION_PATTERN = "%date%thread%level%logger%mdc%msg"; 14 | 15 | IThrowableRenderer throwableRenderer; 16 | 17 | public CustomHTMLLayout() { 18 | pattern = DEFAULT_CONVERSION_PATTERN; 19 | throwableRenderer = new DefaultThrowableRenderer(); 20 | cssBuilder = new CustomCssBuilder(); 21 | } 22 | 23 | @Override 24 | public String doLayout(ILoggingEvent event) { 25 | String content = super.doLayout(event); 26 | if (content.contains("com.neatlife")) { 27 | content = content.replace(" { 16 | 17 | private final JFrameworkConfig jFrameworkConfig; 18 | 19 | @Autowired 20 | public ApplicationStartedEventListener(JFrameworkConfig jFrameworkConfig) { 21 | this.jFrameworkConfig = jFrameworkConfig; 22 | } 23 | 24 | @Override 25 | public void onApplicationEvent(ApplicationStartedEvent event) { 26 | DingTalkUtil.setDdUrl(jFrameworkConfig.getNotification().getDingTalkUrl()); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /jframework-jpa/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | jframework 7 | com.github.neatlife 8 | 0.0.2 9 | 10 | 4.0.0 11 | 12 | jframework-jpa 13 | 14 | 15 | 16 | com.github.neatlife 17 | jframework-fundation 18 | 0.0.2 19 | 20 | 21 | 22 | mysql 23 | mysql-connector-java 24 | runtime 25 | 26 | 27 | 28 | org.springframework.boot 29 | spring-boot-starter-data-jpa 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /jframework-redis/src/main/java/com/github/neatlife/jframework/redis/listener/ApplicationStartedEventListener.java: -------------------------------------------------------------------------------- 1 | package com.github.neatlife.jframework.redis.listener; 2 | 3 | import com.github.neatlife.jframework.redis.util.LockUtil; 4 | import com.github.neatlife.jframework.redis.util.RedisUtil; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.boot.context.event.ApplicationStartedEvent; 7 | import org.springframework.context.ApplicationListener; 8 | import org.springframework.data.redis.core.RedisTemplate; 9 | import org.springframework.stereotype.Component; 10 | 11 | /** 12 | * @author suxiaolin 13 | * @date 2019-03-21 08:37 14 | */ 15 | @Component 16 | public class ApplicationStartedEventListener implements ApplicationListener { 17 | 18 | private final RedisTemplate redisTemplate; 19 | 20 | @Autowired 21 | public ApplicationStartedEventListener(RedisTemplate redisTemplate) { 22 | this.redisTemplate = redisTemplate; 23 | } 24 | 25 | @Override 26 | public void onApplicationEvent(ApplicationStartedEvent event) { 27 | RedisUtil.setRedisTemplate(redisTemplate); 28 | LockUtil.setRedisTemplate(redisTemplate); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /jframework-springfox/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | jframework 7 | com.github.neatlife 8 | 0.0.2 9 | 10 | 4.0.0 11 | 12 | jframework-springfox 13 | 14 | 15 | 16 | com.github.neatlife 17 | jframework-fundation 18 | 0.0.2 19 | 20 | 21 | 22 | io.springfox 23 | springfox-swagger2 24 | [2.9.2] 25 | 26 | 27 | 28 | com.github.xiaoymin 29 | swagger-bootstrap-ui 30 | [1.8.9] 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /jframework-test/src/test/java/com/github/neatlife/jframework/test/util/ZipUtilTest.java: -------------------------------------------------------------------------------- 1 | package com.github.neatlife.jframework.test.util; 2 | 3 | import com.github.neatlife.jframework.fundation.util.ZipUtil; 4 | import org.junit.Assert; 5 | import org.junit.Test; 6 | 7 | import java.io.File; 8 | 9 | /** 10 | * @author suxiaolin 11 | * @date 2019-03-30 17:31 12 | */ 13 | public class ZipUtilTest { 14 | 15 | @Test 16 | public void zip() { 17 | String root = "src/main"; 18 | // 压缩后文件存储路径 19 | String zipPath = root + "/zip"; 20 | // 压缩源文件(如果是文件, 则为全路径 /groovy.png) 21 | String sourcePath = root + "/resources"; 22 | // 压缩后文件名 23 | String zipFileName = "zip.zip"; 24 | try { 25 | ZipUtil.zip(sourcePath, zipPath, zipFileName); 26 | } catch (Exception e) { 27 | e.printStackTrace(); 28 | } 29 | 30 | String zipFilePath = zipPath + File.separator + zipFileName; 31 | Assert.assertTrue(new File(zipFilePath).exists()); 32 | 33 | // 解压源文件路径 34 | // 解压后文件存储路径 35 | String unzipPath = root + "/unzip"; 36 | try { 37 | ZipUtil.unzip(zipFilePath, unzipPath, false); 38 | } catch (Exception e) { 39 | e.printStackTrace(); 40 | } 41 | } 42 | 43 | } -------------------------------------------------------------------------------- /jframework-excel/src/main/java/com/github/neatlife/jframework/excel/FieldForSortting.java: -------------------------------------------------------------------------------- 1 | package com.github.neatlife.jframework.excel; 2 | 3 | import java.lang.reflect.Field; 4 | 5 | /** 6 | * The FieldForSortting 7 | */ 8 | public class FieldForSortting { 9 | private Field field; 10 | private int index; 11 | 12 | /** 13 | * @param field 14 | */ 15 | public FieldForSortting(Field field) { 16 | super(); 17 | this.field = field; 18 | } 19 | 20 | /** 21 | * @param field 22 | * @param index 23 | */ 24 | public FieldForSortting(Field field, int index) { 25 | super(); 26 | this.field = field; 27 | this.index = index; 28 | } 29 | 30 | /** 31 | * @return the field 32 | */ 33 | public Field getField() { 34 | return field; 35 | } 36 | 37 | /** 38 | * @param field the field to set 39 | */ 40 | public void setField(Field field) { 41 | this.field = field; 42 | } 43 | 44 | /** 45 | * @return the index 46 | */ 47 | public int getIndex() { 48 | return index; 49 | } 50 | 51 | /** 52 | * @param index the index to set 53 | */ 54 | public void setIndex(int index) { 55 | this.index = index; 56 | } 57 | 58 | } 59 | -------------------------------------------------------------------------------- /jframework-fundation/src/main/java/com/github/neatlife/jframework/fundation/apiversion/ApiVersionRequestMappingHandlerMapping.java: -------------------------------------------------------------------------------- 1 | package com.github.neatlife.jframework.fundation.apiversion; 2 | 3 | import org.springframework.core.annotation.AnnotationUtils; 4 | import org.springframework.web.servlet.mvc.condition.RequestCondition; 5 | import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping; 6 | 7 | import java.lang.reflect.Method; 8 | 9 | /** 10 | * @author suxiaolin 11 | * @date 2019-03-07 12:39 12 | */ 13 | public class ApiVersionRequestMappingHandlerMapping extends RequestMappingHandlerMapping { 14 | 15 | @Override 16 | protected RequestCondition getCustomTypeCondition(Class handlerType) { 17 | ApiVersion apiVersion = AnnotationUtils.findAnnotation(handlerType, ApiVersion.class); 18 | return createCondition(apiVersion); 19 | } 20 | 21 | @Override 22 | protected RequestCondition getCustomMethodCondition(Method method) { 23 | ApiVersion apiVersion = AnnotationUtils.findAnnotation(method, ApiVersion.class); 24 | return createCondition(apiVersion); 25 | } 26 | 27 | private RequestCondition createCondition(ApiVersion apiVersion) { 28 | return apiVersion == null ? null : new ApiVersionCondition(apiVersion.value()); 29 | } 30 | } -------------------------------------------------------------------------------- /jframework-excel/src/test/java/com/github/neatlife/jframework/excel/TestImportExcel.java: -------------------------------------------------------------------------------- 1 | package com.github.neatlife.jframework.excel; 2 | 3 | import org.junit.Test; 4 | 5 | import java.io.File; 6 | import java.io.FileInputStream; 7 | import java.io.FileNotFoundException; 8 | import java.io.InputStream; 9 | import java.util.Collection; 10 | import java.util.Map; 11 | 12 | /** 13 | * 测试导入Excel 97/2003 14 | */ 15 | public class TestImportExcel { 16 | 17 | @Test 18 | public void importXls() throws FileNotFoundException { 19 | File f = new File("src/test/resources/test.xls"); 20 | InputStream inputStream = new FileInputStream(f); 21 | 22 | ExcelLogs logs = new ExcelLogs(); 23 | Collection importExcel = ExcelUtil.importExcel(Map.class, inputStream, "yyyy/MM/dd HH:mm:ss", logs, 0); 24 | 25 | for (Map m : importExcel) { 26 | System.out.println(m); 27 | } 28 | } 29 | 30 | @Test 31 | public void importXlsx() throws FileNotFoundException { 32 | File f = new File("src/test/resources/test.xlsx"); 33 | InputStream inputStream = new FileInputStream(f); 34 | 35 | ExcelLogs logs = new ExcelLogs(); 36 | Collection importExcel = ExcelUtil.importExcel(Map.class, inputStream, "yyyy/MM/dd HH:mm:ss", logs, 0); 37 | 38 | for (Map m : importExcel) { 39 | System.out.println(m); 40 | } 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /k8s.yml: -------------------------------------------------------------------------------- 1 | --- 2 | kind: Deployment 3 | apiVersion: apps/v1 4 | metadata: 5 | name: jframework 6 | namespace: default 7 | labels: 8 | k8s-app: jframework 9 | annotations: 10 | deployment.kubernetes.io/revision: '2' 11 | spec: 12 | replicas: 1 13 | selector: 14 | matchLabels: 15 | k8s-app: jframework 16 | template: 17 | metadata: 18 | name: jframework 19 | labels: 20 | k8s-app: jframework 21 | spec: 22 | containers: 23 | - name: jframework 24 | image: registry.cn-hangzhou.aliyuncs.com/suxiaolin/jframework:latest 25 | livenessProbe: 26 | httpGet: 27 | scheme: HTTP 28 | path: "/heartbeat" 29 | port: 8080 30 | initialDelaySeconds: 10 31 | periodSeconds: 5 32 | resources: {} 33 | terminationMessagePath: "/dev/termination-log" 34 | terminationMessagePolicy: File 35 | imagePullPolicy: Always 36 | securityContext: 37 | privileged: false 38 | procMount: Default 39 | restartPolicy: Always 40 | terminationGracePeriodSeconds: 30 41 | dnsPolicy: ClusterFirst 42 | securityContext: {} 43 | schedulerName: default-scheduler 44 | strategy: 45 | type: RollingUpdate 46 | rollingUpdate: 47 | maxUnavailable: 25% 48 | maxSurge: 25% 49 | revisionHistoryLimit: 10 50 | progressDeadlineSeconds: 600 -------------------------------------------------------------------------------- /jframework-excel/src/main/java/com/github/neatlife/jframework/excel/ExcelLogs.java: -------------------------------------------------------------------------------- 1 | package com.github.neatlife.jframework.excel; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | /** 7 | * The ExcelLogs 8 | */ 9 | public class ExcelLogs { 10 | private Boolean hasError; 11 | private List logList; 12 | 13 | /** 14 | * 15 | */ 16 | public ExcelLogs() { 17 | super(); 18 | hasError = false; 19 | } 20 | 21 | /** 22 | * @return the hasError 23 | */ 24 | public Boolean getHasError() { 25 | return hasError; 26 | } 27 | 28 | /** 29 | * @param hasError the hasError to set 30 | */ 31 | public void setHasError(Boolean hasError) { 32 | this.hasError = hasError; 33 | } 34 | 35 | /** 36 | * @return the logList 37 | */ 38 | public List getLogList() { 39 | return logList; 40 | } 41 | 42 | /** 43 | * @param logList the logList to set 44 | */ 45 | public void setLogList(List logList) { 46 | this.logList = logList; 47 | } 48 | 49 | public List getErrorLogList() { 50 | List errList = new ArrayList<>(); 51 | for (ExcelLog log : this.logList) { 52 | if (log != null && ExcelUtil.isNotBlank(log.getLog())) { 53 | errList.add(log); 54 | } 55 | } 56 | return errList; 57 | } 58 | 59 | } 60 | -------------------------------------------------------------------------------- /jframework-fundation/src/main/java/com/github/neatlife/jframework/fundation/util/DingTalkUtil.java: -------------------------------------------------------------------------------- 1 | package com.github.neatlife.jframework.fundation.util; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | 5 | import java.util.Set; 6 | import java.util.stream.Collectors; 7 | 8 | /** 9 | * @author suxiaolin 10 | * @date 2019-04-13 08:53 11 | */ 12 | @Slf4j 13 | public class DingTalkUtil { 14 | private static String ddUrl = ""; 15 | 16 | public static void setDdUrl(String ddUrl) { 17 | DingTalkUtil.ddUrl = ddUrl; 18 | } 19 | 20 | public static boolean send(String content, String title, Set receivers) { 21 | try { 22 | HttpUtil.ResponseWrap result = HttpUtil.postWrap(ddUrl, 23 | "{\n" 24 | + " \"msgtype\": \"text\",\n" 25 | + " \"text\": {\"content\":\"" + title + "\r\n" + content + "\n|" 26 | + receivers.stream().map(r -> "@" + r).collect(Collectors.joining(" ")) + "\"},\n" 27 | + " \"at\": {\n" 28 | + " \"atMobiles\": [" + receivers.stream().map(r -> "\"" + r + "\"").collect(Collectors.joining(",")) + "], \n" 29 | + " \"isAtAll\": false\n" 30 | + " }\n" 31 | + " }"); 32 | return result.getStatusCode() == 200; 33 | } catch (Exception e) { 34 | return false; 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /jframework-springfox/src/main/java/com/github/neatlife/jframework/springfox/config/Swagger2Config.java: -------------------------------------------------------------------------------- 1 | package com.github.neatlife.jframework.springfox.config; 2 | 3 | import org.springframework.context.annotation.Bean; 4 | import org.springframework.context.annotation.Configuration; 5 | import springfox.documentation.builders.ApiInfoBuilder; 6 | import springfox.documentation.builders.PathSelectors; 7 | import springfox.documentation.builders.RequestHandlerSelectors; 8 | import springfox.documentation.service.ApiInfo; 9 | import springfox.documentation.spi.DocumentationType; 10 | import springfox.documentation.spring.web.plugins.Docket; 11 | import springfox.documentation.swagger2.annotations.EnableSwagger2; 12 | 13 | /** 14 | * @author suxiaolin 15 | * @date 2019-03-07 12:39 16 | */ 17 | @Configuration 18 | @EnableSwagger2 19 | public class Swagger2Config { 20 | 21 | @Bean 22 | public Docket createRestApi() { 23 | return new Docket(DocumentationType.SWAGGER_2) 24 | .apiInfo(apiInfo()) 25 | .select() 26 | .apis(RequestHandlerSelectors.basePackage("com.neatlife.jframework")) 27 | .paths(PathSelectors.any()) 28 | .build(); 29 | } 30 | 31 | private ApiInfo apiInfo() { 32 | return new ApiInfoBuilder() 33 | .title("com/github/neatlife/jframework/fundation") 34 | .description("") 35 | .termsOfServiceUrl("") 36 | .version("1.0") 37 | .build(); 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /jframework-excel/src/main/java/com/github/neatlife/jframework/excel/ExcelSheet.java: -------------------------------------------------------------------------------- 1 | package com.github.neatlife.jframework.excel; 2 | 3 | import java.util.Collection; 4 | import java.util.Map; 5 | 6 | /** 7 | * 用于汇出多个sheet的Vo The ExcelSheet 8 | */ 9 | public class ExcelSheet { 10 | private String sheetName; 11 | private Map headers; 12 | private Collection dataset; 13 | 14 | /** 15 | * @return the sheetName 16 | */ 17 | public String getSheetName() { 18 | return sheetName; 19 | } 20 | 21 | /** 22 | * Excel页签名称 23 | * 24 | * @param sheetName the sheetName to set 25 | */ 26 | public void setSheetName(String sheetName) { 27 | this.sheetName = sheetName; 28 | } 29 | 30 | /** 31 | * Excel表头 32 | * 33 | * @return the headers 34 | */ 35 | public Map getHeaders() { 36 | return headers; 37 | } 38 | 39 | /** 40 | * @param headers the headers to set 41 | */ 42 | public void setHeaders(Map headers) { 43 | this.headers = headers; 44 | } 45 | 46 | /** 47 | * Excel数据集合 48 | * 49 | * @return the dataset 50 | */ 51 | public Collection getDataset() { 52 | return dataset; 53 | } 54 | 55 | /** 56 | * @param dataset the dataset to set 57 | */ 58 | public void setDataset(Collection dataset) { 59 | this.dataset = dataset; 60 | } 61 | 62 | } 63 | -------------------------------------------------------------------------------- /jframework-fundation/src/main/java/com/github/neatlife/jframework/fundation/validator/StringLength.java: -------------------------------------------------------------------------------- 1 | package com.github.neatlife.jframework.fundation.validator; 2 | 3 | import javax.validation.Constraint; 4 | import javax.validation.Payload; 5 | import javax.validation.constraints.Size; 6 | import java.lang.annotation.Documented; 7 | import java.lang.annotation.Retention; 8 | import java.lang.annotation.Target; 9 | 10 | import static java.lang.annotation.ElementType.*; 11 | import static java.lang.annotation.RetentionPolicy.RUNTIME; 12 | 13 | /** 14 | * @author suxiaolin 15 | */ 16 | @Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER}) 17 | @Retention(RUNTIME) 18 | @Documented 19 | @Constraint(validatedBy = {StringLengthValidator.class}) 20 | public @interface StringLength { 21 | 22 | String message() default "{javax.validation.constraints.Size.message}"; 23 | 24 | Class[] groups() default {}; 25 | 26 | Class[] payload() default {}; 27 | 28 | /** 29 | * @return size the element must be higher or equal to 30 | */ 31 | int min() default 0; 32 | 33 | /** 34 | * @return size the element must be lower or equal to 35 | */ 36 | int max() default Integer.MAX_VALUE; 37 | 38 | /** 39 | * Defines several {@link Size} annotations on the same element. 40 | * 41 | * @see Size 42 | */ 43 | @Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER}) 44 | @Retention(RUNTIME) 45 | @Documented 46 | @interface List { 47 | Size[] value(); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /jframework-test/src/test/java/com/github/neatlife/jframework/test/json/DoubleSpecifyTest.java: -------------------------------------------------------------------------------- 1 | package com.github.neatlife.jframework.test.json; 2 | 3 | import com.fasterxml.jackson.databind.ObjectMapper; 4 | import com.github.neatlife.jframework.fundation.json.DoubleSpecify; 5 | import com.github.neatlife.jframework.test.JframeworkApplicationTests; 6 | import org.junit.Assert; 7 | import org.junit.Test; 8 | 9 | public class DoubleSpecifyTest extends JframeworkApplicationTests { 10 | 11 | @Test 12 | public void test1() throws Exception { 13 | Product product = new Product(111231.5585, "小米手机"); 14 | 15 | ObjectMapper objectMapper = new ObjectMapper(); 16 | 17 | Product product1 = objectMapper.readValue(objectMapper.writeValueAsString(product), Product.class); 18 | 19 | Assert.assertEquals(111231.56D, product1.getPrice(), 0.0); 20 | } 21 | 22 | public static class Product { 23 | 24 | @DoubleSpecify 25 | private Double price; 26 | private String name; 27 | 28 | public Product() { 29 | } 30 | 31 | public Product(Double price, String name) { 32 | this.price = price; 33 | this.name = name; 34 | } 35 | 36 | public String getName() { 37 | return name; 38 | } 39 | 40 | public void setName(String name) { 41 | this.name = name; 42 | } 43 | 44 | public Double getPrice() { 45 | return price; 46 | } 47 | 48 | public void setPrice(Double price) { 49 | this.price = price; 50 | } 51 | } 52 | 53 | } -------------------------------------------------------------------------------- /jframework-redis/src/main/java/com/github/neatlife/jframework/redis/config/RedisConfig.java: -------------------------------------------------------------------------------- 1 | package com.github.neatlife.jframework.redis.config; 2 | 3 | /** 4 | * @author suxiaolin 5 | * @date 2019-03-18 16:18 6 | */ 7 | 8 | import org.springframework.beans.factory.annotation.Autowired; 9 | import org.springframework.cache.annotation.CachingConfigurerSupport; 10 | import org.springframework.cache.annotation.EnableCaching; 11 | import org.springframework.cache.interceptor.KeyGenerator; 12 | import org.springframework.context.annotation.Bean; 13 | import org.springframework.context.annotation.Configuration; 14 | import org.springframework.data.redis.core.RedisTemplate; 15 | import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer; 16 | import org.springframework.data.redis.serializer.StringRedisSerializer; 17 | 18 | @Configuration 19 | @EnableCaching 20 | public class RedisConfig extends CachingConfigurerSupport { 21 | 22 | @Autowired 23 | public RedisConfig(RedisTemplate redisTemplate) { 24 | redisTemplate.setKeySerializer(new StringRedisSerializer()); 25 | redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer()); 26 | } 27 | 28 | @Bean 29 | @Override 30 | public KeyGenerator keyGenerator() { 31 | return (target, method, params) -> { 32 | StringBuilder sb = new StringBuilder(); 33 | sb.append(target.getClass().getName()); 34 | sb.append(method.getName()); 35 | for (Object obj : params) { 36 | sb.append(obj.toString()); 37 | } 38 | return sb.toString(); 39 | }; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /k8s-gateway.yml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: Service 4 | metadata: 5 | name: gateway 6 | spec: 7 | selector: 8 | k8s-app: gateway 9 | ports: 10 | - protocol: TCP 11 | name: http 12 | port: 8080 13 | targetPort: 8080 14 | type: NodePort 15 | --- 16 | kind: DaemonSet 17 | apiVersion: apps/v1 18 | metadata: 19 | name: gateway 20 | namespace: default 21 | labels: 22 | k8s-app: gateway 23 | annotations: 24 | deployment.kubernetes.io/revision: '2' 25 | spec: 26 | selector: 27 | matchLabels: 28 | k8s-app: gateway 29 | template: 30 | metadata: 31 | name: gateway 32 | labels: 33 | k8s-app: gateway 34 | spec: 35 | containers: 36 | - name: gateway 37 | ports: 38 | - containerPort: 8080 39 | hostPort: 8080 40 | name: 8080tcp80800 41 | protocol: TCP 42 | image: registry.cn-hangzhou.aliyuncs.com/suxiaolin/gateway:latest 43 | livenessProbe: 44 | httpGet: 45 | scheme: HTTP 46 | path: "/actuator/info" 47 | port: 8080 48 | initialDelaySeconds: 10 49 | periodSeconds: 5 50 | resources: {} 51 | terminationMessagePath: "/dev/termination-log" 52 | terminationMessagePolicy: File 53 | imagePullPolicy: Always 54 | securityContext: 55 | privileged: false 56 | procMount: Default 57 | restartPolicy: Always 58 | terminationGracePeriodSeconds: 30 59 | dnsPolicy: ClusterFirst 60 | securityContext: {} 61 | schedulerName: default-scheduler 62 | revisionHistoryLimit: 10 -------------------------------------------------------------------------------- /jframework-excel/src/test/java/com/github/neatlife/jframework/excel/TestExportMap.java: -------------------------------------------------------------------------------- 1 | package com.github.neatlife.jframework.excel; 2 | 3 | import org.junit.Test; 4 | 5 | import java.io.File; 6 | import java.io.FileOutputStream; 7 | import java.io.IOException; 8 | import java.io.OutputStream; 9 | import java.util.*; 10 | 11 | /** 12 | * The TestExportMap 13 | */ 14 | public class TestExportMap { 15 | @Test 16 | public void exportXls() throws IOException { 17 | List> list = new ArrayList<>(); 18 | Map map = new LinkedHashMap<>(); 19 | map.put("name", ""); 20 | map.put("age", ""); 21 | map.put("birthday", ""); 22 | map.put("sex", ""); 23 | Map map2 = new LinkedHashMap(); 24 | map2.put("name", "测试是否是中文长度不能自动宽度.测试是否是中文长度不能自动宽度."); 25 | map2.put("age", null); 26 | map2.put("sex", null); 27 | map.put("birthday", null); 28 | Map map3 = new LinkedHashMap(); 29 | map3.put("name", "张三"); 30 | map3.put("age", 12); 31 | map3.put("sex", "男"); 32 | map3.put("birthday", new Date()); 33 | list.add(map); 34 | list.add(map2); 35 | list.add(map3); 36 | Map map1 = new LinkedHashMap<>(); 37 | map1.put("name", "姓名"); 38 | map1.put("age", "年龄"); 39 | map1.put("birthday", "出生日期"); 40 | map1.put("sex", "性别"); 41 | File f = new File("test.xls"); 42 | OutputStream out = new FileOutputStream(f); 43 | ExcelUtil.exportExcel(map1, list, out); 44 | out.close(); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /jframework-test/src/test/java/com/github/neatlife/jframework/test/json/BigDecimalSpecifyTest.java: -------------------------------------------------------------------------------- 1 | package com.github.neatlife.jframework.test.json; 2 | 3 | import com.fasterxml.jackson.databind.ObjectMapper; 4 | import com.github.neatlife.jframework.fundation.json.BigDecimalSpecify; 5 | import com.github.neatlife.jframework.test.JframeworkApplicationTests; 6 | import org.junit.Assert; 7 | import org.junit.Test; 8 | 9 | import java.math.BigDecimal; 10 | 11 | public class BigDecimalSpecifyTest extends JframeworkApplicationTests { 12 | 13 | @Test 14 | public void test1() throws Exception { 15 | Product product = new Product(new BigDecimal("111231.5585"), "小米手机"); 16 | 17 | ObjectMapper objectMapper = new ObjectMapper(); 18 | 19 | Product product1 = objectMapper.readValue(objectMapper.writeValueAsString(product), Product.class); 20 | 21 | Assert.assertEquals(new BigDecimal("111231.559"), product1.getPrice()); 22 | } 23 | 24 | public static class Product { 25 | 26 | @BigDecimalSpecify(3) 27 | private BigDecimal price; 28 | private String name; 29 | 30 | public Product() { 31 | } 32 | 33 | public Product(BigDecimal price, String name) { 34 | this.price = price; 35 | this.name = name; 36 | } 37 | 38 | public String getName() { 39 | return name; 40 | } 41 | 42 | public void setName(String name) { 43 | this.name = name; 44 | } 45 | 46 | public BigDecimal getPrice() { 47 | return price; 48 | } 49 | 50 | public void setPrice(BigDecimal price) { 51 | this.price = price; 52 | } 53 | } 54 | 55 | } -------------------------------------------------------------------------------- /jframework-fundation/src/main/java/com/github/neatlife/jframework/fundation/handler/ControllerExceptionHandler.java: -------------------------------------------------------------------------------- 1 | package com.github.neatlife.jframework.fundation.handler; 2 | 3 | import com.github.neatlife.jframework.fundation.exception.BusinessRuntimeException; 4 | import com.github.neatlife.jframework.fundation.http.HttpCode; 5 | import com.github.neatlife.jframework.fundation.http.Response; 6 | import com.google.common.collect.Maps; 7 | import lombok.extern.slf4j.Slf4j; 8 | import org.springframework.core.annotation.AnnotationUtils; 9 | import org.springframework.web.bind.annotation.ControllerAdvice; 10 | import org.springframework.web.bind.annotation.ExceptionHandler; 11 | import org.springframework.web.bind.annotation.ResponseBody; 12 | import org.springframework.web.bind.annotation.ResponseStatus; 13 | 14 | import javax.servlet.http.HttpServletRequest; 15 | import java.util.Map; 16 | 17 | /** 18 | * @author suxiaolin 19 | */ 20 | @ControllerAdvice 21 | @Slf4j 22 | public class ControllerExceptionHandler { 23 | @ExceptionHandler({Exception.class}) 24 | public String handleException(HttpServletRequest request, Exception e) throws Exception { 25 | log.error("Request URL : {} , Exception : {}", request.getRequestURL(), e.getMessage()); 26 | 27 | if (AnnotationUtils.findAnnotation(e.getClass(), ResponseStatus.class) != null) { 28 | throw e; 29 | } 30 | 31 | return e.getMessage(); 32 | } 33 | 34 | @ExceptionHandler({BusinessRuntimeException.class}) 35 | @ResponseBody 36 | public Response handlerBusinessException(BusinessRuntimeException ex) { 37 | return new Response(HttpCode.INTERNAL_SERVER_ERROR.toString(), ex.getMessage(), Maps.newHashMap()); 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /jframework-test/src/test/java/com/github/neatlife/jframework/test/util/RedisUtilTest.java: -------------------------------------------------------------------------------- 1 | package com.github.neatlife.jframework.test.util; 2 | 3 | import com.github.neatlife.jframework.redis.util.RedisUtil; 4 | import com.github.neatlife.jframework.test.Person; 5 | import org.junit.Assert; 6 | import org.junit.Test; 7 | import org.junit.runner.RunWith; 8 | import org.springframework.boot.test.context.SpringBootTest; 9 | import org.springframework.test.context.junit4.SpringRunner; 10 | import org.springframework.util.ObjectUtils; 11 | 12 | /** 13 | * @author suxiaolin 14 | * @date 2019-03-21 08:52 15 | */ 16 | @RunWith(SpringRunner.class) 17 | @SpringBootTest 18 | public class RedisUtilTest { 19 | private final static String QUEUE = "myqueue"; 20 | 21 | @Test 22 | public void setGetObject() { 23 | Person person = new Person("小明", 22); 24 | RedisUtil.setCacheObject("person-xiaoming", person); 25 | Person personFromRedis = RedisUtil.getCacheObject("person-xiaoming"); 26 | 27 | Assert.assertEquals(personFromRedis.getName(), person.getName()); 28 | Assert.assertEquals(personFromRedis.getAge(), person.getAge()); 29 | } 30 | 31 | @Test 32 | public void blpop() throws Exception { 33 | lpush(); 34 | String message = RedisUtil.blpop(QUEUE, 30); 35 | if (ObjectUtils.isEmpty(message)) { 36 | System.out.println("没有收到消息"); 37 | } else { 38 | System.out.println("收到消息,消息是: " + message); 39 | } 40 | } 41 | 42 | public void lpush() throws Exception { 43 | String message = "message: " + System.currentTimeMillis(); 44 | RedisUtil.lpush(QUEUE, message); 45 | System.out.println("push message: " + message); 46 | } 47 | } -------------------------------------------------------------------------------- /jframework-excel/pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | jframework 5 | com.github.neatlife 6 | 0.0.2 7 | 8 | 4.0.0 9 | 10 | jframework-excel 11 | 12 | 13 | 14 | com.github.neatlife 15 | jframework-fundation 16 | 0.0.2 17 | 18 | 19 | org.springframework.boot 20 | spring-boot-starter-test 21 | test 22 | 23 | 24 | 25 | org.apache.poi 26 | poi 27 | 3.17 28 | 29 | 30 | org.apache.poi 31 | poi-ooxml 32 | 3.17 33 | 34 | 35 | commons-collections 36 | commons-collections 37 | 3.2.1 38 | 39 | 40 | commons-beanutils 41 | commons-beanutils 42 | 1.8.3 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /jframework-excel/src/main/java/com/github/neatlife/jframework/excel/ExcelLog.java: -------------------------------------------------------------------------------- 1 | package com.github.neatlife.jframework.excel; 2 | 3 | /** 4 | * The ExcelLog 5 | */ 6 | public class ExcelLog { 7 | private Integer rowNum; 8 | private Object object; 9 | private String log; 10 | 11 | /** 12 | * @param object 13 | * @param log 14 | */ 15 | public ExcelLog(Object object, String log) { 16 | super(); 17 | this.object = object; 18 | this.log = log; 19 | } 20 | 21 | /** 22 | * @param rowNum 23 | * @param object 24 | * @param log 25 | */ 26 | public ExcelLog(Object object, String log, Integer rowNum) { 27 | super(); 28 | this.rowNum = rowNum; 29 | this.object = object; 30 | this.log = log; 31 | } 32 | 33 | /** 34 | * @return the rowNum 35 | */ 36 | public Integer getRowNum() { 37 | return rowNum; 38 | } 39 | 40 | /** 41 | * @param rowNum the rowNum to set 42 | */ 43 | public void setRowNum(Integer rowNum) { 44 | this.rowNum = rowNum; 45 | } 46 | 47 | /** 48 | * @return the object 49 | */ 50 | public Object getObject() { 51 | return object; 52 | } 53 | 54 | /** 55 | * @param object the object to set 56 | */ 57 | public void setObject(Object object) { 58 | this.object = object; 59 | } 60 | 61 | /** 62 | * @return the log 63 | */ 64 | public String getLog() { 65 | return log; 66 | } 67 | 68 | /** 69 | * @param log the log to set 70 | */ 71 | public void setLog(String log) { 72 | this.log = log; 73 | } 74 | 75 | } 76 | -------------------------------------------------------------------------------- /jframework-springfox/src/main/java/com/github/neatlife/jframework/springfox/plugin/ApiRefererParameterReader.java: -------------------------------------------------------------------------------- 1 | package com.github.neatlife.jframework.springfox.plugin; 2 | 3 | import com.github.neatlife.jframework.springfox.annotation.ApiReferer; 4 | import io.swagger.annotations.ApiModelProperty; 5 | import lombok.extern.slf4j.Slf4j; 6 | import org.springframework.core.Ordered; 7 | import org.springframework.core.annotation.Order; 8 | import org.springframework.stereotype.Component; 9 | import org.springframework.util.ObjectUtils; 10 | import springfox.documentation.spi.DocumentationType; 11 | import springfox.documentation.spi.service.ParameterBuilderPlugin; 12 | import springfox.documentation.spi.service.contexts.ParameterContext; 13 | 14 | /** 15 | * @author suxiaolin 16 | * @date 2019-03-07 12:39 17 | */ 18 | @Component 19 | @Order(Ordered.HIGHEST_PRECEDENCE + 1) 20 | @Slf4j 21 | class ApiRefererParameterReader implements ParameterBuilderPlugin { 22 | 23 | @Override 24 | public void apply(ParameterContext context) { 25 | ApiReferer apiReferer = context.getOperationContext().findAnnotation(ApiReferer.class).orNull(); 26 | if (ObjectUtils.isEmpty(apiReferer)) { 27 | return; 28 | } 29 | String filedName = context.resolvedMethodParameter().defaultName().orNull(); 30 | if (ObjectUtils.isEmpty(filedName)) { 31 | return; 32 | } 33 | try { 34 | String apimodelPropertyValue = apiReferer.value().getDeclaredField(filedName).getAnnotation(ApiModelProperty.class).value(); 35 | context.parameterBuilder().description(apimodelPropertyValue); 36 | } catch (Exception e) { 37 | log.debug("apiReferer get filed error ", e); 38 | } 39 | } 40 | 41 | @Override 42 | public boolean supports(DocumentationType delimiter) { 43 | return true; 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /jframework-fundation/src/main/java/com/github/neatlife/jframework/fundation/apiversion/ApiVersionCondition.java: -------------------------------------------------------------------------------- 1 | package com.github.neatlife.jframework.fundation.apiversion; 2 | 3 | import org.springframework.web.servlet.mvc.condition.RequestCondition; 4 | 5 | import javax.servlet.http.HttpServletRequest; 6 | import java.util.regex.Matcher; 7 | import java.util.regex.Pattern; 8 | 9 | /** 10 | * @author suxiaolin 11 | * @date 2019-03-07 12:39 12 | */ 13 | public class ApiVersionCondition implements RequestCondition { 14 | 15 | // 路径中版本的前缀, 这里用 /v[1-9]/的形式 16 | private final static Pattern VERSION_PREFIX_PATTERN = Pattern.compile("v(\\d+)/"); 17 | 18 | private int apiVersion; 19 | 20 | public ApiVersionCondition(int apiVersion) { 21 | this.apiVersion = apiVersion; 22 | } 23 | 24 | @Override 25 | public ApiVersionCondition combine(ApiVersionCondition other) { 26 | // 采用最后定义优先原则,则方法上的定义覆盖类上面的定义 27 | return new ApiVersionCondition(other.getApiVersion()); 28 | } 29 | 30 | @Override 31 | public ApiVersionCondition getMatchingCondition(HttpServletRequest request) { 32 | String pathInfo = request.getPathInfo(); 33 | StringBuffer requestURL = request.getRequestURL(); 34 | String requestURI = request.getRequestURI(); 35 | // Matcher m = VERSION_PREFIX_PATTERN.matcher(request.getPathInfo()); 36 | Matcher m = VERSION_PREFIX_PATTERN.matcher(requestURI); 37 | if (m.find()) { 38 | Integer version = Integer.valueOf(m.group(1)); 39 | // 如果请求的版本号大于配置版本号, 则满足 40 | if (version >= this.apiVersion) { 41 | return this; 42 | } 43 | } 44 | return null; 45 | } 46 | 47 | @Override 48 | public int compareTo(ApiVersionCondition other, HttpServletRequest request) { 49 | // 优先匹配最新的版本号 50 | return other.getApiVersion() - this.apiVersion; 51 | } 52 | 53 | public int getApiVersion() { 54 | return apiVersion; 55 | } 56 | 57 | } -------------------------------------------------------------------------------- /jframework-excel/src/main/java/com/github/neatlife/jframework/excel/ExcelCell.java: -------------------------------------------------------------------------------- 1 | package com.github.neatlife.jframework.excel; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | /** 9 | * The ExcelCell
10 | * 数值型的栏位只能使用Double 11 | * 12 | * @see {@link ExcelUtil#exportExcel} 13 | */ 14 | @Retention(RetentionPolicy.RUNTIME) 15 | @Target(ElementType.FIELD) 16 | public @interface ExcelCell { 17 | /** 18 | * 顺序 default 100 19 | * 20 | * @return index 21 | */ 22 | int index(); 23 | 24 | /** 25 | * 当值为null时要显示的值 default StringUtils.EMPTY 26 | * 27 | * @return defaultValue 28 | */ 29 | String defaultValue() default ""; 30 | 31 | /** 32 | * 用于验证 33 | * 34 | * @return valid 35 | */ 36 | Valid valid() default @Valid(); 37 | 38 | @Retention(RetentionPolicy.RUNTIME) 39 | @Target(ElementType.FIELD) 40 | @interface Valid { 41 | /** 42 | * 必须与in中String相符,目前仅支持String类型 43 | * 44 | * @return e.g. {"key","value"} 45 | */ 46 | String[] in() default {}; 47 | 48 | /** 49 | * 是否允许为空,用于验证数据 default true 50 | * 51 | * @return allowNull 52 | */ 53 | boolean allowNull() default true; 54 | 55 | /** 56 | * Apply a "greater than" constraint to the named property 57 | * 58 | * @return gt 59 | */ 60 | double gt() default Double.NaN; 61 | 62 | /** 63 | * Apply a "less than" constraint to the named property 64 | * 65 | * @return lt 66 | */ 67 | double lt() default Double.NaN; 68 | 69 | /** 70 | * Apply a "greater than or equal" constraint to the named property 71 | * 72 | * @return ge 73 | */ 74 | double ge() default Double.NaN; 75 | 76 | /** 77 | * Apply a "less than or equal" constraint to the named property 78 | * 79 | * @return le 80 | */ 81 | double le() default Double.NaN; 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /jframework-fundation/src/main/java/com/github/neatlife/jframework/fundation/request/RequestToPoHandlerMethodArgumentResolver.java: -------------------------------------------------------------------------------- 1 | package com.github.neatlife.jframework.fundation.request; 2 | 3 | import com.github.neatlife.jframework.fundation.util.JsonUtil; 4 | import org.springframework.core.MethodParameter; 5 | import org.springframework.util.LinkedMultiValueMap; 6 | import org.springframework.util.MultiValueMap; 7 | import org.springframework.web.bind.support.WebDataBinderFactory; 8 | import org.springframework.web.context.request.NativeWebRequest; 9 | import org.springframework.web.method.support.HandlerMethodArgumentResolver; 10 | import org.springframework.web.method.support.ModelAndViewContainer; 11 | 12 | import java.util.LinkedHashMap; 13 | import java.util.Map; 14 | 15 | public class RequestToPoHandlerMethodArgumentResolver implements HandlerMethodArgumentResolver { 16 | 17 | @Override 18 | public boolean supportsParameter(MethodParameter parameter) { 19 | return parameter.hasParameterAnnotation(RequestToPo.class); 20 | } 21 | 22 | @Override 23 | public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception { 24 | Class parameterType = parameter.getParameterType(); 25 | Map requestParameterMap = webRequest.getParameterMap(); 26 | if (MultiValueMap.class.isAssignableFrom(parameterType)) { 27 | MultiValueMap linkedMultiValueMap = new LinkedMultiValueMap<>(requestParameterMap.size()); 28 | requestParameterMap.forEach((key, values) -> { 29 | for (String value : values) { 30 | linkedMultiValueMap.add(key, value); 31 | } 32 | }); 33 | return linkedMultiValueMap; 34 | } else { 35 | Map stringStringLinkedHashMap = new LinkedHashMap<>(requestParameterMap.size()); 36 | requestParameterMap.forEach((key, values) -> { 37 | if (values.length > 0) { 38 | stringStringLinkedHashMap.put(key, values[0]); 39 | } 40 | }); 41 | return JsonUtil.toObject(stringStringLinkedHashMap, parameterType); 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /jframework-hystrix/src/main/java/com/github/neatlife/jframework/hystrix/RegisterCommandExcutionHook.java: -------------------------------------------------------------------------------- 1 | package com.github.neatlife.jframework.hystrix; 2 | 3 | import com.netflix.hystrix.HystrixCommand; 4 | import com.netflix.hystrix.HystrixInvokable; 5 | import com.netflix.hystrix.strategy.HystrixPlugins; 6 | import com.netflix.hystrix.strategy.concurrency.HystrixConcurrencyStrategy; 7 | import com.netflix.hystrix.strategy.eventnotifier.HystrixEventNotifier; 8 | import com.netflix.hystrix.strategy.executionhook.HystrixCommandExecutionHook; 9 | import com.netflix.hystrix.strategy.metrics.HystrixMetricsPublisher; 10 | import com.netflix.hystrix.strategy.properties.HystrixPropertiesStrategy; 11 | import lombok.extern.slf4j.Slf4j; 12 | import org.springframework.stereotype.Component; 13 | 14 | /** 15 | * @author suxiaolin 16 | */ 17 | @Component 18 | @Slf4j 19 | public class RegisterCommandExcutionHook { 20 | public RegisterCommandExcutionHook() { 21 | HystrixEventNotifier eventNotifier = HystrixPlugins.getInstance().getEventNotifier(); 22 | HystrixPropertiesStrategy propertiesStrategy = HystrixPlugins.getInstance().getPropertiesStrategy(); 23 | HystrixConcurrencyStrategy concurrencyStrategy = HystrixPlugins.getInstance().getConcurrencyStrategy(); 24 | HystrixMetricsPublisher hystrixMetricsPublisher = HystrixPlugins.getInstance().getMetricsPublisher(); 25 | 26 | HystrixPlugins.reset(); 27 | HystrixPlugins.getInstance().registerMetricsPublisher(hystrixMetricsPublisher); 28 | HystrixPlugins.getInstance().registerConcurrencyStrategy(concurrencyStrategy); 29 | HystrixPlugins.getInstance().registerEventNotifier(eventNotifier); 30 | HystrixPlugins.getInstance().registerPropertiesStrategy(propertiesStrategy); 31 | HystrixPlugins.getInstance().registerCommandExecutionHook(new HystrixCommandExecutionHook() { 32 | @Override 33 | public void onFallbackStart(HystrixInvokable commandInstance) { 34 | HystrixCommand hystrixCommand = (HystrixCommand) commandInstance; 35 | String commandKey = hystrixCommand.getCommandKey().toString(); 36 | log.error("Hystrix: {} 接口开始降级", commandKey); 37 | super.onFallbackStart(commandInstance); 38 | } 39 | }); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /jframework-fundation/src/main/java/com/github/neatlife/jframework/fundation/json/DoubleSpecifySerialize.java: -------------------------------------------------------------------------------- 1 | package com.github.neatlife.jframework.fundation.json; 2 | 3 | import com.fasterxml.jackson.core.JsonGenerator; 4 | import com.fasterxml.jackson.databind.BeanProperty; 5 | import com.fasterxml.jackson.databind.JsonMappingException; 6 | import com.fasterxml.jackson.databind.JsonSerializer; 7 | import com.fasterxml.jackson.databind.SerializerProvider; 8 | import com.fasterxml.jackson.databind.ser.ContextualSerializer; 9 | 10 | import java.io.IOException; 11 | import java.math.BigDecimal; 12 | import java.util.Objects; 13 | 14 | /** 15 | * @author suxiaolin 16 | * @date 2019-03-20 08:52 17 | */ 18 | public class DoubleSpecifySerialize extends JsonSerializer implements ContextualSerializer { 19 | 20 | private int decimalNumber; 21 | 22 | public DoubleSpecifySerialize() { 23 | this(2); 24 | } 25 | 26 | public DoubleSpecifySerialize(int decimalNumber) { 27 | this.decimalNumber = decimalNumber; 28 | } 29 | 30 | @Override 31 | public void serialize(Double value, JsonGenerator gen, SerializerProvider serializers) throws IOException { 32 | if (value == null) { 33 | return; 34 | } 35 | gen.writeNumber(BigDecimal.valueOf(value).setScale(decimalNumber, BigDecimal.ROUND_HALF_UP).stripTrailingZeros().toString()); 36 | } 37 | 38 | @Override 39 | public JsonSerializer createContextual(SerializerProvider prov, BeanProperty property) throws JsonMappingException { 40 | // 为空直接跳过 41 | if (property == null) { 42 | return prov.findNullValueSerializer(property); 43 | } 44 | // 非 Double 类直接跳过 45 | if (Objects.equals(property.getType().getRawClass(), Double.class)) { 46 | DoubleSpecify doubleSpecify = property.getAnnotation(DoubleSpecify.class); 47 | if (doubleSpecify == null) { 48 | doubleSpecify = property.getContextAnnotation(DoubleSpecify.class); 49 | } 50 | // 如果能得到注解,就将注解的 value 传入 DoubleSpecifySerialize 51 | if (doubleSpecify != null) { 52 | return new DoubleSpecifySerialize(doubleSpecify.value()); 53 | } 54 | } 55 | return prov.findValueSerializer(property.getType(), property); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /jframework-fundation/src/main/java/com/github/neatlife/jframework/fundation/json/BigDecimalSpecifySerialize.java: -------------------------------------------------------------------------------- 1 | package com.github.neatlife.jframework.fundation.json; 2 | 3 | import com.fasterxml.jackson.core.JsonGenerator; 4 | import com.fasterxml.jackson.databind.BeanProperty; 5 | import com.fasterxml.jackson.databind.JsonMappingException; 6 | import com.fasterxml.jackson.databind.JsonSerializer; 7 | import com.fasterxml.jackson.databind.SerializerProvider; 8 | import com.fasterxml.jackson.databind.ser.ContextualSerializer; 9 | 10 | import java.io.IOException; 11 | import java.math.BigDecimal; 12 | import java.util.Objects; 13 | 14 | /** 15 | * @author suxiaolin 16 | * @date 2019-03-20 08:52 17 | */ 18 | public class BigDecimalSpecifySerialize extends JsonSerializer implements ContextualSerializer { 19 | 20 | private int decimalNumber; 21 | 22 | public BigDecimalSpecifySerialize() { 23 | this(2); 24 | } 25 | 26 | public BigDecimalSpecifySerialize(int decimalNumber) { 27 | this.decimalNumber = decimalNumber; 28 | } 29 | 30 | @Override 31 | public void serialize(BigDecimal value, JsonGenerator gen, SerializerProvider serializers) throws IOException { 32 | if (value == null) { 33 | return; 34 | } 35 | gen.writeNumber(value.setScale(decimalNumber, BigDecimal.ROUND_HALF_UP).stripTrailingZeros().toString()); 36 | } 37 | 38 | @Override 39 | public JsonSerializer createContextual(SerializerProvider prov, BeanProperty property) throws JsonMappingException { 40 | // 为空直接跳过 41 | if (property == null) { 42 | return prov.findNullValueSerializer(property); 43 | } 44 | // 非 BigDecimal 类直接跳过 45 | if (Objects.equals(property.getType().getRawClass(), BigDecimal.class)) { 46 | BigDecimalSpecify doubleSpecify = property.getAnnotation(BigDecimalSpecify.class); 47 | if (doubleSpecify == null) { 48 | doubleSpecify = property.getContextAnnotation(BigDecimalSpecify.class); 49 | } 50 | // 如果能得到注解,就将注解的 value 传入 BigDecimalSpecifySerialize 51 | if (doubleSpecify != null) { 52 | return new BigDecimalSpecifySerialize(doubleSpecify.value()); 53 | } 54 | } 55 | return prov.findValueSerializer(property.getType(), property); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /jframework-test/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | jframework 7 | com.github.neatlife 8 | 0.0.2 9 | 10 | 4.0.0 11 | 12 | jframework-test 13 | 14 | 15 | 16 | com.github.neatlife 17 | jframework-fundation 18 | 0.0.2 19 | 20 | 21 | 22 | com.github.neatlife 23 | jframework-redis 24 | 0.0.2 25 | 26 | 27 | 28 | com.github.neatlife 29 | jframework-springfox 30 | 0.0.2 31 | 32 | 33 | 34 | com.github.neatlife 35 | jframework-logback 36 | 0.0.2 37 | 38 | 39 | 40 | com.github.neatlife 41 | jframework-jpa 42 | 0.0.2 43 | 44 | 45 | 46 | org.springframework.boot 47 | spring-boot-starter-test 48 | test 49 | 50 | 51 | 52 | org.powermock 53 | powermock-module-junit4 54 | 2.0.2 55 | test 56 | 57 | 58 | 59 | org.powermock 60 | powermock-api-mockito2 61 | 2.0.2 62 | test 63 | 64 | 65 | 66 | -------------------------------------------------------------------------------- /jframework-fundation/src/main/java/com/github/neatlife/jframework/fundation/request/RequestJsonToPoHandlerMethodArgumentResolver.java: -------------------------------------------------------------------------------- 1 | package com.github.neatlife.jframework.fundation.request; 2 | 3 | import com.github.neatlife.jframework.fundation.util.JsonUtil; 4 | import com.google.common.collect.Lists; 5 | import org.springframework.core.MethodParameter; 6 | import org.springframework.web.bind.support.WebDataBinderFactory; 7 | import org.springframework.web.context.request.NativeWebRequest; 8 | import org.springframework.web.method.support.HandlerMethodArgumentResolver; 9 | import org.springframework.web.method.support.ModelAndViewContainer; 10 | 11 | import java.net.URLDecoder; 12 | import java.nio.charset.StandardCharsets; 13 | import java.util.ArrayList; 14 | import java.util.List; 15 | import java.util.Objects; 16 | 17 | public class RequestJsonToPoHandlerMethodArgumentResolver implements HandlerMethodArgumentResolver { 18 | @Override 19 | public boolean supportsParameter(MethodParameter parameter) { 20 | return parameter.hasParameterAnnotation(RequestJsonToPo.class); 21 | } 22 | 23 | @Override 24 | public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest nativeWebRequest, WebDataBinderFactory binderFactory) throws Exception { 25 | RequestJsonToPo parameterAnnotation = parameter.getParameterAnnotation(RequestJsonToPo.class); 26 | Objects.requireNonNull(parameterAnnotation); 27 | String value = parameterAnnotation.value(); 28 | Class clazz = parameter.getNestedParameterType(); 29 | String jsonParameter = nativeWebRequest.getParameter(value); 30 | if (jsonParameter == null) { 31 | if (clazz.isAssignableFrom(List.class)) { 32 | return Lists.newArrayList(); 33 | } 34 | return clazz.newInstance(); 35 | } 36 | jsonParameter = URLDecoder.decode(jsonParameter, StandardCharsets.UTF_8.name()); 37 | 38 | if (clazz.isAssignableFrom(List.class) || clazz.isAssignableFrom(ArrayList.class)) { 39 | String typeName = ((sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl) parameter.getGenericParameterType()).getActualTypeArguments()[0].getTypeName(); 40 | Class aClass = Class.forName(typeName); 41 | return JsonUtil.toList(jsonParameter, aClass); 42 | } else { 43 | return JsonUtil.toObject(jsonParameter, clazz); 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /jframework-test/src/main/java/com/github/neatlife/jframework/test/controller/TestController.java: -------------------------------------------------------------------------------- 1 | package com.github.neatlife.jframework.test.controller; 2 | 3 | import com.github.neatlife.jframework.fundation.controller.Controller; 4 | import lombok.extern.slf4j.Slf4j; 5 | import org.springframework.core.io.InputStreamResource; 6 | import org.springframework.core.io.Resource; 7 | import org.springframework.http.HttpHeaders; 8 | import org.springframework.http.MediaType; 9 | import org.springframework.http.ResponseEntity; 10 | import org.springframework.util.StreamUtils; 11 | import org.springframework.web.bind.annotation.GetMapping; 12 | import org.springframework.web.bind.annotation.RequestMapping; 13 | import org.springframework.web.bind.annotation.RestController; 14 | 15 | import javax.servlet.http.HttpServletResponse; 16 | import java.io.File; 17 | import java.io.FileInputStream; 18 | import java.io.IOException; 19 | 20 | /** 21 | * @author suxiaolin 22 | * @date 2019-03-07 12:39 23 | */ 24 | @RestController 25 | @RequestMapping("test") 26 | @Slf4j 27 | public class TestController extends Controller { 28 | 29 | @GetMapping("/send-error-email") 30 | public String sendErrorEmail() { 31 | log.error("这个是一个错误"); 32 | return "hello world."; 33 | } 34 | 35 | @GetMapping("/send-dingtalk-error") 36 | public String sendDingTalkError() throws Exception { 37 | if (true) { 38 | throw new Exception("这个是一个错误"); 39 | } 40 | return "Hello"; 41 | } 42 | 43 | @GetMapping(value = "/image", produces = MediaType.IMAGE_PNG_VALUE) 44 | public void getImage(HttpServletResponse response) throws IOException { 45 | try (FileInputStream fileInputStream = new FileInputStream(new File("src/main/resources/1.png"))) { 46 | response.setContentType(MediaType.IMAGE_PNG_VALUE); 47 | StreamUtils.copy(fileInputStream, response.getOutputStream()); 48 | } 49 | } 50 | 51 | @GetMapping(path = "/download") 52 | public ResponseEntity download() throws IOException { 53 | File file = new File("src/main/resources/1.png"); 54 | InputStreamResource resource = new InputStreamResource(new FileInputStream(file)); 55 | 56 | HttpHeaders headers = new HttpHeaders(); 57 | headers.add(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=1.png"); 58 | 59 | return ResponseEntity.ok() 60 | .headers(headers) 61 | .contentLength(file.length()) 62 | .contentType(MediaType.parseMediaType("application/octet-stream")) 63 | .body(resource); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /jframework-logback/src/main/java/com/github/neatlife/jframework/logback/DingTalkAppender.java: -------------------------------------------------------------------------------- 1 | package com.github.neatlife.jframework.logback; 2 | 3 | import ch.qos.logback.classic.Level; 4 | import ch.qos.logback.classic.spi.ILoggingEvent; 5 | import ch.qos.logback.classic.spi.IThrowableProxy; 6 | import ch.qos.logback.classic.spi.ThrowableProxyUtil; 7 | import ch.qos.logback.core.UnsynchronizedAppenderBase; 8 | import com.github.neatlife.jframework.fundation.util.DingTalkUtil; 9 | 10 | import java.text.SimpleDateFormat; 11 | import java.util.Date; 12 | import java.util.stream.Collectors; 13 | import java.util.stream.Stream; 14 | 15 | /** 16 | * @author suxiaolin 17 | * @date 2019-04-13 11:37 18 | */ 19 | public class DingTalkAppender extends UnsynchronizedAppenderBase { 20 | private static final SimpleDateFormat DEFAULT_DATE_FORMAT = new SimpleDateFormat("yyyyMMdd:HHmmssSSS"); 21 | private static final String FORMAT_MESSAGE = "time: %s\nthreadName: %s\nlevel: %s\nlogger: %s\nclassName: %s.%s\nlineNumber: %d\nexception: %s:%s"; 22 | 23 | private String to; 24 | 25 | @Override 26 | public void append(ILoggingEvent event) { 27 | if (event.getLevel() != Level.ERROR) { 28 | return; 29 | } 30 | 31 | String msg = transformStackTrace(event); 32 | DingTalkUtil.send(msg, event.getLoggerName(), Stream.of(to).collect(Collectors.toSet())); 33 | } 34 | 35 | private String transformStackTrace(ILoggingEvent event) { 36 | String exception = ""; 37 | IThrowableProxy throwableProxy = event.getThrowableProxy(); 38 | if (throwableProxy != null) { 39 | exception = ThrowableProxyUtil.asString(throwableProxy); 40 | } 41 | 42 | StackTraceElement[] callerData = event.getCallerData(); 43 | StackTraceElement stackTraceElement = callerData[0]; 44 | 45 | String time = DEFAULT_DATE_FORMAT.format(new Date(event.getTimeStamp())); 46 | String threadName = event.getThreadName(); 47 | String level = event.getLevel().toString(); 48 | String logger = event.getLoggerName(); 49 | String msg = event.getFormattedMessage(); 50 | String className = stackTraceElement.getClassName(); 51 | String method = stackTraceElement.getMethodName(); 52 | int lineNumber = stackTraceElement.getLineNumber(); 53 | 54 | return String.format(FORMAT_MESSAGE, time, threadName, level, logger, className, method, lineNumber, exception, msg); 55 | } 56 | 57 | public String getTo() { 58 | return to; 59 | } 60 | 61 | public void setTo(String to) { 62 | this.to = to; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /jframework-test/src/main/java/com/github/neatlife/jframework/test/controller/ExcelController.java: -------------------------------------------------------------------------------- 1 | package com.github.neatlife.jframework.test.controller; 2 | 3 | import com.github.neatlife.jframework.fundation.controller.Controller; 4 | import com.github.neatlife.jframework.fundation.util.CsvUtil; 5 | import com.github.neatlife.jframework.test.dto.ExcelUserDto; 6 | import lombok.extern.slf4j.Slf4j; 7 | import org.springframework.http.MediaType; 8 | import org.springframework.util.FileCopyUtils; 9 | import org.springframework.web.bind.annotation.GetMapping; 10 | import org.springframework.web.bind.annotation.RequestMapping; 11 | import org.springframework.web.bind.annotation.RestController; 12 | 13 | import javax.servlet.http.HttpServletRequest; 14 | import javax.servlet.http.HttpServletResponse; 15 | import java.io.IOException; 16 | import java.net.URLEncoder; 17 | import java.nio.charset.StandardCharsets; 18 | import java.util.ArrayList; 19 | import java.util.LinkedHashMap; 20 | import java.util.List; 21 | 22 | @RestController 23 | @RequestMapping("excel") 24 | @Slf4j 25 | public class ExcelController extends Controller { 26 | 27 | @GetMapping("/export1") 28 | public void exportCsv(HttpServletResponse response, HttpServletRequest request) throws IOException { 29 | // csv文件名字,为了方便默认给个名字,当然名字可以自定义,看实际需求了 30 | String fileName = "csv文件.csv"; 31 | // 解决不同浏览器出现的乱码 32 | fileName = URLEncoder.encode(fileName, StandardCharsets.UTF_8.toString()); 33 | response.setContentType(MediaType.APPLICATION_OCTET_STREAM.toString()); 34 | response.setHeader("Content-Disposition", "attachment; filename=\"" + fileName + "\"; filename*=utf-8''" + fileName); 35 | FileCopyUtils.copy(genCsvUserDto(), response.getOutputStream()); 36 | } 37 | 38 | private byte[] genCsvUserDto() { 39 | // 模拟导出数据,这里数据可以是从数据库获取回来的,也可以是前端传过来再解析的 40 | // 这里的数据应该放在dao层获取的,就先简单放在这里,大家不必介意,只是demo演示 41 | List userDtoList = new ArrayList<>(); 42 | userDtoList.add(new ExcelUserDto("13800138001", "圣诞老人1", "VIP1")); 43 | userDtoList.add(new ExcelUserDto("13800138002", "圣诞老人2", "VIP7")); 44 | userDtoList.add(new ExcelUserDto("13800138003", "圣诞老人3", "VIP8")); 45 | // 为了方便,也不写dao层 46 | List> exportData = new ArrayList<>(userDtoList.size()); 47 | // 行数据 48 | for (ExcelUserDto excelUserDto : userDtoList) { 49 | LinkedHashMap rowData = new LinkedHashMap<>(); 50 | rowData.put("1", excelUserDto.getUsername()); 51 | rowData.put("2", excelUserDto.getNickname()); 52 | rowData.put("3", excelUserDto.getLevel()); 53 | exportData.add(rowData); 54 | } 55 | LinkedHashMap header = new LinkedHashMap<>(); 56 | header.put("1", "用户账号"); 57 | header.put("2", "用户昵称"); 58 | header.put("3", "用户等级"); 59 | return CsvUtil.export(header, exportData); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /jframework-fundation/src/main/java/com/github/neatlife/jframework/fundation/util/HexUtil.java: -------------------------------------------------------------------------------- 1 | package com.github.neatlife.jframework.fundation.util; 2 | 3 | /** 4 | * @author suxiaolin 5 | * @date 2019-03-29 09:03 6 | */ 7 | public class HexUtil { 8 | 9 | private static final char[] DIGITS_LOWER = {'0', '1', '2', '3', '4', '5', 10 | '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; 11 | private static final char[] DIGITS_UPPER = {'0', '1', '2', '3', '4', '5', 12 | '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'}; 13 | 14 | /** 15 | * 16进制转byte数组 16 | * 17 | * @param data 16进制字符串 18 | * @return byte数组 19 | * @throws Exception 转化失败的异常 20 | */ 21 | public static byte[] hex2Bytes(final String data) throws Exception { 22 | final int len = data.length(); 23 | 24 | if ((len & 0x01) != 0) { 25 | throw new Exception("Odd number of characters."); 26 | } 27 | 28 | final byte[] out = new byte[len >> 1]; 29 | 30 | // two characters form the hex value. 31 | for (int i = 0, j = 0; j < len; i++) { 32 | int f = toDigit(data.charAt(j), j) << 4; 33 | j++; 34 | f = f | toDigit(data.charAt(j), j); 35 | j++; 36 | out[i] = (byte) (f & 0xFF); 37 | } 38 | return out; 39 | } 40 | 41 | /** 42 | * bytes数组转16进制String 43 | * 44 | * @param data bytes数组 45 | * @return 转化结果 46 | */ 47 | public static String bytes2Hex(final byte[] data) { 48 | return bytes2Hex(data, true); 49 | } 50 | 51 | /** 52 | * bytes数组转16进制String 53 | * 54 | * @param data bytes数组 55 | * @param toLowerCase 是否小写 56 | * @return 转化结果 57 | */ 58 | public static String bytes2Hex(final byte[] data, final boolean toLowerCase) { 59 | return bytes2Hex(data, toLowerCase ? DIGITS_LOWER : DIGITS_UPPER); 60 | } 61 | 62 | 63 | /** 64 | * bytes数组转16进制String 65 | * 66 | * @param data bytes数组 67 | * @param toDigits DIGITS_LOWER或DIGITS_UPPER 68 | * @return 转化结果 69 | */ 70 | private static String bytes2Hex(final byte[] data, final char[] toDigits) { 71 | final int l = data.length; 72 | final char[] out = new char[l << 1]; 73 | // two characters form the hex value. 74 | for (int i = 0, j = 0; i < l; i++) { 75 | out[j++] = toDigits[(0xF0 & data[i]) >>> 4]; 76 | out[j++] = toDigits[0x0F & data[i]]; 77 | } 78 | return new String(out); 79 | } 80 | 81 | /** 82 | * 16转化为数字 83 | * 84 | * @param ch 16进制 85 | * @param index 索引 86 | * @return 转化结果 87 | * @throws Exception 转化失败异常 88 | */ 89 | private static int toDigit(final char ch, final int index) 90 | throws Exception { 91 | final int digit = Character.digit(ch, 16); 92 | if (digit == -1) { 93 | throw new Exception("Illegal hexadecimal character " + ch 94 | + " at index " + index); 95 | } 96 | return digit; 97 | } 98 | 99 | } -------------------------------------------------------------------------------- /jframework-fundation/src/main/java/com/github/neatlife/jframework/fundation/util/CsvUtil.java: -------------------------------------------------------------------------------- 1 | package com.github.neatlife.jframework.fundation.util; 2 | 3 | import java.io.BufferedWriter; 4 | import java.io.ByteArrayOutputStream; 5 | import java.io.IOException; 6 | import java.io.OutputStreamWriter; 7 | import java.util.Iterator; 8 | import java.util.LinkedHashMap; 9 | import java.util.List; 10 | import java.util.Map; 11 | 12 | /** 13 | * CSV导出 14 | * 15 | * @author suxiaolin 16 | */ 17 | public class CsvUtil { 18 | 19 | /** 20 | * 导出csv文件 21 | * 22 | * @param headers 内容标题 23 | * 注意:headers类型是LinkedHashMap,保证遍历输出顺序和添加顺序一致。 24 | * 而HashMap的话不保证添加数据的顺序和遍历出来的数据顺序一致,这样就出现 25 | * 数据的标题不搭的情况的 26 | * @param exportData 要导出的数据集合 27 | * @return 28 | */ 29 | public static byte[] export(LinkedHashMap headers, List> exportData) { 30 | 31 | ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); 32 | BufferedWriter buffCvsWriter = null; 33 | 34 | try { 35 | // 编码gb2312,处理excel打开csv的时候会出现的标题中文乱码 36 | buffCvsWriter = new BufferedWriter(new OutputStreamWriter(byteArrayOutputStream, "gb2312")); 37 | // 写入cvs文件的头部 38 | Map.Entry propertyEntry = null; 39 | for (Iterator> propertyIterator = headers.entrySet().iterator(); propertyIterator.hasNext(); ) { 40 | propertyEntry = propertyIterator.next(); 41 | buffCvsWriter.write("\"" + propertyEntry.getValue().toString() + "\""); 42 | if (propertyIterator.hasNext()) { 43 | buffCvsWriter.write(","); 44 | } 45 | } 46 | buffCvsWriter.newLine(); 47 | // 写入文件内容 48 | LinkedHashMap row = null; 49 | for (Iterator> iterator = exportData.iterator(); iterator.hasNext(); ) { 50 | row = iterator.next(); 51 | for (Iterator propertyIterator = row.entrySet().iterator(); propertyIterator.hasNext(); ) { 52 | propertyEntry = propertyIterator.next(); 53 | buffCvsWriter.write("\"" + propertyEntry.getValue().toString() + "\""); 54 | if (propertyIterator.hasNext()) { 55 | buffCvsWriter.write(","); 56 | } 57 | } 58 | if (iterator.hasNext()) { 59 | buffCvsWriter.newLine(); 60 | } 61 | } 62 | // 记得刷新缓冲区,不然数可能会不全的,当然close的话也会flush的,不加也没问题 63 | buffCvsWriter.flush(); 64 | } catch (IOException ignored) { 65 | 66 | } finally { 67 | // 释放资源 68 | if (buffCvsWriter != null) { 69 | try { 70 | buffCvsWriter.close(); 71 | } catch (IOException e) { 72 | e.printStackTrace(); 73 | } 74 | } 75 | } 76 | return byteArrayOutputStream.toByteArray(); 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /jframework-fundation/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | jframework 7 | com.github.neatlife 8 | 0.0.2 9 | 10 | 4.0.0 11 | 12 | jframework-fundation 13 | 14 | 15 | 16 | org.springframework.boot 17 | spring-boot-starter-web 18 | 19 | 20 | org.projectlombok 21 | lombok 22 | 23 | 24 | 25 | redis.clients 26 | jedis 27 | 2.5.2 28 | 29 | 30 | 31 | ch.qos.logback 32 | logback-classic 33 | 1.2.2 34 | 35 | 36 | 37 | 38 | org.springframework.boot 39 | spring-boot-starter-mail 40 | 41 | 42 | 43 | 44 | org.codehaus.janino 45 | janino 46 | 2.7.8 47 | 48 | 49 | 50 | javax.mail 51 | mail 52 | 1.4.7 53 | 54 | 55 | 56 | org.apache.commons 57 | commons-pool2 58 | 59 | 60 | 61 | 62 | org.apache.httpcomponents 63 | httpclient 64 | ${httpclient.version} 65 | 66 | 67 | org.apache.httpcomponents 68 | httpmime 69 | ${httpclient.version} 70 | 71 | 72 | org.jooq 73 | joox 74 | ${joox.version} 75 | 76 | 77 | 78 | 79 | com.google.zxing 80 | javase 81 | 3.3.0 82 | 83 | 84 | 85 | com.google.guava 86 | guava 87 | 27.0-jre 88 | 89 | 90 | 91 | org.apache.commons 92 | commons-lang3 93 | 3.9 94 | 95 | 96 | 97 | -------------------------------------------------------------------------------- /jframework-fundation/src/main/java/com/github/neatlife/jframework/fundation/util/Md5Util.java: -------------------------------------------------------------------------------- 1 | package com.github.neatlife.jframework.fundation.util; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | 5 | import java.io.File; 6 | import java.io.FileInputStream; 7 | import java.io.IOException; 8 | import java.nio.ByteBuffer; 9 | import java.nio.channels.FileChannel; 10 | import java.nio.charset.StandardCharsets; 11 | import java.security.MessageDigest; 12 | import java.security.NoSuchAlgorithmException; 13 | 14 | /** 15 | * @author suxiaolin 16 | * @date 2019-03-29 09:02 17 | */ 18 | @Slf4j 19 | public class Md5Util { 20 | /** 21 | * 获得字符串的md5值 22 | * 23 | * @param str 待加密的字符串 24 | * @return md5加密后的字符串 25 | */ 26 | public static String getMD5String(String str) { 27 | byte[] bytes = null; 28 | try { 29 | MessageDigest md5 = MessageDigest.getInstance("MD5"); 30 | bytes = md5.digest(str.getBytes(StandardCharsets.UTF_8)); 31 | } catch (NoSuchAlgorithmException e) { 32 | log.error("e message: {}", e.getMessage()); 33 | } 34 | return HexUtil.bytes2Hex(bytes); 35 | 36 | } 37 | 38 | 39 | /** 40 | * 获得字符串的md5大写值 41 | * 42 | * @param str 待加密的字符串 43 | * @return md5加密后的大写字符串 44 | */ 45 | public static String getMD5UpperString(String str) { 46 | return getMD5String(str).toUpperCase(); 47 | } 48 | 49 | /** 50 | * 获得文件的md5值 51 | * 52 | * @param file 文件对象 53 | * @return 文件的md5 54 | */ 55 | public static String getFileMD5String(File file) { 56 | String ret = ""; 57 | FileInputStream in = null; 58 | FileChannel ch = null; 59 | try { 60 | in = new FileInputStream(file); 61 | ch = in.getChannel(); 62 | ByteBuffer byteBuffer = ch.map(FileChannel.MapMode.READ_ONLY, 0, 63 | file.length()); 64 | MessageDigest md5 = MessageDigest.getInstance("MD5"); 65 | md5.update(byteBuffer); 66 | ret = HexUtil.bytes2Hex(md5.digest()); 67 | } catch (IOException | NoSuchAlgorithmException e) { 68 | log.error("e message: {}", e.getMessage()); 69 | } finally { 70 | if (in != null) { 71 | try { 72 | in.close(); 73 | } catch (IOException e) { 74 | log.error("e message: {}", e.getMessage()); 75 | } 76 | } 77 | if (ch != null) { 78 | try { 79 | ch.close(); 80 | } catch (IOException e) { 81 | log.error("e message: {}", e.getMessage()); 82 | } 83 | } 84 | } 85 | return ret; 86 | } 87 | 88 | /** 89 | * 获得文件md5值大写字符串 90 | * 91 | * @param file 文件对象 92 | * @return 文件md5大写字符串 93 | */ 94 | public static String getFileMD5UpperString(File file) { 95 | return getFileMD5String(file).toUpperCase(); 96 | } 97 | 98 | /** 99 | * 校验文件的md5值 100 | * 101 | * @param file 目标文件 102 | * @param md5 基准md5 103 | * @return 校验结果 104 | */ 105 | public static boolean checkFileMD5(File file, String md5) { 106 | return getFileMD5String(file).equalsIgnoreCase(md5); 107 | } 108 | 109 | /** 110 | * 校验字符串的md5值 111 | * 112 | * @param str 目标字符串 113 | * @param md5 基准md5 114 | * @return 校验结果 115 | */ 116 | public static boolean checkMD5(String str, String md5) { 117 | return getMD5String(str).equalsIgnoreCase(md5); 118 | } 119 | 120 | /** 121 | * 获得加盐md5,算法过程是原字符串md5后连接加盐字符串后再进行md5 122 | * 123 | * @param str 待加密的字符串 124 | * @param salt 盐 125 | * @return 加盐md5 126 | */ 127 | public static String getMD5AndSalt(String str, String salt) { 128 | return getMD5String(getMD5String(str).concat(salt)); 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /jframework-fundation/src/main/java/com/github/neatlife/jframework/fundation/util/QRCodeUtil.java: -------------------------------------------------------------------------------- 1 | package com.github.neatlife.jframework.fundation.util; 2 | 3 | import com.google.common.collect.Maps; 4 | import com.google.zxing.*; 5 | import com.google.zxing.client.j2se.BufferedImageLuminanceSource; 6 | import com.google.zxing.client.j2se.MatrixToImageWriter; 7 | import com.google.zxing.common.BitMatrix; 8 | import com.google.zxing.common.HybridBinarizer; 9 | import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel; 10 | 11 | import javax.imageio.ImageIO; 12 | import java.awt.image.BufferedImage; 13 | import java.io.File; 14 | import java.io.IOException; 15 | import java.io.OutputStream; 16 | import java.util.EnumMap; 17 | import java.util.HashMap; 18 | import java.util.Map; 19 | import java.util.Objects; 20 | 21 | /** 22 | * 二维码工具类 23 | * 24 | * @author suxiaolin 25 | * @date 2019-03-25 23:22 26 | */ 27 | public class QRCodeUtil { 28 | /** 29 | * 默认二维码宽度 30 | */ 31 | private static final int WIDTH = 300; 32 | 33 | /** 34 | * 默认二维码高度 35 | */ 36 | private static final int HEIGHT = 300; 37 | 38 | /** 39 | * 默认二维码文件格式 40 | */ 41 | private static final String FORMAT = "png"; 42 | 43 | /** 44 | * 二维码参数 45 | */ 46 | private static final Map hints = new HashMap(); 47 | 48 | static { 49 | // 字符编码 50 | hints.put(EncodeHintType.CHARACTER_SET, "utf-8"); 51 | // 容错等级 L、M、Q、H 其中 L 为最低, H 为最高 52 | hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H); 53 | // 二维码与图片边距 54 | hints.put(EncodeHintType.MARGIN, 2); 55 | } 56 | 57 | /** 58 | * 返回一个 BufferedImage 对象 59 | * 60 | * @param content 二维码内容 61 | * @param width 宽 62 | * @param height 高 63 | */ 64 | public static BufferedImage toBufferedImage(String content, int width, int height) throws WriterException, IOException { 65 | BitMatrix bitMatrix = new MultiFormatWriter().encode(content, BarcodeFormat.QR_CODE, width, height, hints); 66 | return MatrixToImageWriter.toBufferedImage(bitMatrix); 67 | } 68 | 69 | /** 70 | * 将二维码图片输出到一个流中 71 | * 72 | * @param content 二维码内容 73 | * @param stream 输出流 74 | * @param width 宽 75 | * @param height 高 76 | */ 77 | public static void writeToStream(String content, OutputStream stream, int width, int height) throws WriterException, IOException { 78 | BitMatrix bitMatrix = new MultiFormatWriter().encode(content, BarcodeFormat.QR_CODE, width, height, hints); 79 | MatrixToImageWriter.writeToStream(bitMatrix, FORMAT, stream); 80 | } 81 | 82 | /** 83 | * 生成二维码图片文件 84 | * 85 | * @param content 二维码内容 86 | * @param path 文件保存路径 87 | * @param width 宽 88 | * @param height 高 89 | */ 90 | public static void createQRCode(String content, String path, int width, int height) throws WriterException, IOException { 91 | BitMatrix bitMatrix = new MultiFormatWriter().encode(content, BarcodeFormat.QR_CODE, width, height, hints); 92 | //toPath() 方法由 jdk1.7 及以上提供 93 | MatrixToImageWriter.writeToPath(bitMatrix, FORMAT, new File(path).toPath()); 94 | } 95 | 96 | 97 | /** 98 | * 读取二维码图片 99 | * 100 | * @param path 101 | * @return 102 | * @throws Exception 103 | */ 104 | public static String read(String path) throws Exception { 105 | File file = new File(path); 106 | BufferedImage image; 107 | image = ImageIO.read(file); 108 | if (Objects.isNull(image)) { 109 | throw new RuntimeException("无法读取源文件"); 110 | } 111 | LuminanceSource source = new BufferedImageLuminanceSource(image); 112 | BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source)); 113 | Result result; 114 | @SuppressWarnings("rawtypes") 115 | EnumMap hints = Maps.newEnumMap(DecodeHintType.class); 116 | //解码设置编码方式为:utf-8 117 | hints.put(DecodeHintType.CHARACTER_SET, "utf-8"); 118 | result = new MultiFormatReader().decode(bitmap, hints); 119 | return result.getText(); 120 | } 121 | } -------------------------------------------------------------------------------- /jframework-test/src/test/java/com/github/neatlife/jframework/test/util/IpUtilTest.java: -------------------------------------------------------------------------------- 1 | package com.github.neatlife.jframework.test.util; 2 | 3 | import com.github.neatlife.jframework.fundation.util.IpUtil; 4 | import org.junit.Assert; 5 | import org.junit.Test; 6 | import org.powermock.api.mockito.PowerMockito; 7 | 8 | import javax.servlet.http.HttpServletRequest; 9 | 10 | public class IpUtilTest { 11 | 12 | @Test 13 | public void testGetIpAddr1() { 14 | HttpServletRequest httpServletRequest = 15 | PowerMockito.mock(HttpServletRequest.class); 16 | PowerMockito.when(httpServletRequest.getHeader("x-forwarded-for")) 17 | .thenReturn("0:0:0:0:0:0:0:1"); 18 | 19 | Assert.assertEquals("127.0.0.1", IpUtil.getIpAddr(httpServletRequest)); 20 | } 21 | 22 | @Test 23 | public void testGetIpAddr2() { 24 | HttpServletRequest httpServletRequest = 25 | PowerMockito.mock(HttpServletRequest.class); 26 | PowerMockito.when(httpServletRequest.getHeader("Proxy-Client-IP")) 27 | .thenReturn("0:0:0:0:0:0:0:1"); 28 | 29 | Assert.assertEquals("127.0.0.1", IpUtil.getIpAddr(httpServletRequest)); 30 | } 31 | 32 | @Test 33 | public void testGetIpAddr3() { 34 | HttpServletRequest httpServletRequest = 35 | PowerMockito.mock(HttpServletRequest.class); 36 | PowerMockito.when(httpServletRequest.getHeader("X-Forwarded-For")) 37 | .thenReturn("0:0:0:0:0:0:0:1"); 38 | 39 | Assert.assertEquals("127.0.0.1", IpUtil.getIpAddr(httpServletRequest)); 40 | } 41 | 42 | @Test 43 | public void testGetIpAddr4() { 44 | HttpServletRequest httpServletRequest = 45 | PowerMockito.mock(HttpServletRequest.class); 46 | PowerMockito.when(httpServletRequest.getHeader("WL-Proxy-Client-IP")) 47 | .thenReturn("0:0:0:0:0:0:0:1"); 48 | 49 | Assert.assertEquals("127.0.0.1", IpUtil.getIpAddr(httpServletRequest)); 50 | } 51 | 52 | @Test 53 | public void testGetIpAddr5() { 54 | HttpServletRequest httpServletRequest = 55 | PowerMockito.mock(HttpServletRequest.class); 56 | PowerMockito.when(httpServletRequest.getHeader("X-Real-IP")) 57 | .thenReturn("0:0:0:0:0:0:0:1"); 58 | 59 | Assert.assertEquals("127.0.0.1", IpUtil.getIpAddr(httpServletRequest)); 60 | } 61 | 62 | @Test 63 | public void testGetIpAddr6() { 64 | HttpServletRequest httpServletRequest = 65 | PowerMockito.mock(HttpServletRequest.class); 66 | PowerMockito.when(httpServletRequest.getRemoteAddr()) 67 | .thenReturn("10.0.0.1"); 68 | 69 | Assert.assertEquals("10.0.0.1", IpUtil.getIpAddr(httpServletRequest)); 70 | } 71 | 72 | @Test 73 | public void testInternalIp() { 74 | Assert.assertTrue(IpUtil.internalIp("10.0.0.1")); 75 | Assert.assertTrue(IpUtil.internalIp("127.0.0.1")); 76 | Assert.assertTrue(IpUtil.internalIp("172.17.0.1")); 77 | Assert.assertTrue(IpUtil.internalIp("192.168.1.1")); 78 | 79 | Assert.assertFalse(IpUtil.internalIp("172.0.0.1")); 80 | Assert.assertFalse(IpUtil.internalIp("224.0.0.1")); 81 | Assert.assertFalse(IpUtil.internalIp("240.0.0.1")); 82 | } 83 | 84 | @Test 85 | public void testIpv4ToByte() { 86 | Assert.assertArrayEquals(new byte[0], IpUtil.ipv4ToByte("foo")); 87 | Assert.assertArrayEquals(new byte[0], IpUtil.ipv4ToByte("")); 88 | Assert.assertArrayEquals(new byte[0], IpUtil.ipv4ToByte("-10")); 89 | Assert.assertArrayEquals(new byte[0], IpUtil.ipv4ToByte("-10.1")); 90 | Assert.assertArrayEquals(new byte[0], IpUtil.ipv4ToByte("10.-1")); 91 | Assert.assertArrayEquals(new byte[0], IpUtil.ipv4ToByte("-10.1.1")); 92 | Assert.assertArrayEquals(new byte[0], IpUtil.ipv4ToByte("10.1.-1")); 93 | Assert.assertArrayEquals(new byte[0], IpUtil.ipv4ToByte("-10.1.1.1")); 94 | Assert.assertArrayEquals(new byte[0], IpUtil.ipv4ToByte("10.1.1.1.1")); 95 | Assert.assertArrayEquals(new byte[]{0, 0, 0, 10}, 96 | IpUtil.ipv4ToByte("10")); 97 | Assert.assertArrayEquals(new byte[]{10, 0, 0, 1}, 98 | IpUtil.ipv4ToByte("10.1")); 99 | Assert.assertArrayEquals(new byte[]{10, 1, 0, 1}, 100 | IpUtil.ipv4ToByte("10.1.1")); 101 | Assert.assertArrayEquals(new byte[]{10, 1, 1, 1}, 102 | IpUtil.ipv4ToByte("10.1.1.1")); 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /jframework-fundation/src/main/java/com/github/neatlife/jframework/fundation/util/MapUtil.java: -------------------------------------------------------------------------------- 1 | package com.github.neatlife.jframework.fundation.util; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | 6 | /** 7 | * 更少的代码封装参数 8 | * 9 | * @author suxiaolin 10 | * @date 2019-03-28 10:57 11 | */ 12 | public class MapUtil { 13 | public static Map of() { 14 | HashMap map = new HashMap<>(); 15 | return map; 16 | } 17 | 18 | public static Map of(K k1, V v1) { 19 | HashMap map = new HashMap<>(); 20 | map.put(k1, v1); 21 | return map; 22 | } 23 | 24 | public static Map of(K k1, V v1, K k2, V v2) { 25 | HashMap map = new HashMap<>(); 26 | map.put(k1, v1); 27 | map.put(k2, v2); 28 | return map; 29 | } 30 | 31 | public static Map of(K k1, V v1, K k2, V v2, K k3, V v3) { 32 | HashMap map = new HashMap<>(); 33 | map.put(k1, v1); 34 | map.put(k2, v2); 35 | map.put(k3, v3); 36 | return map; 37 | } 38 | 39 | public static Map of(K k1, V v1, K k2, V v2, K k3, V v3, 40 | K k4, V v4) { 41 | HashMap map = new HashMap<>(); 42 | map.put(k1, v1); 43 | map.put(k2, v2); 44 | map.put(k3, v3); 45 | map.put(k4, v4); 46 | return map; 47 | } 48 | 49 | public static Map of(K k1, V v1, K k2, V v2, K k3, V v3, 50 | K k4, V v4, K k5, V v5) { 51 | HashMap map = new HashMap<>(); 52 | map.put(k1, v1); 53 | map.put(k2, v2); 54 | map.put(k3, v3); 55 | map.put(k4, v4); 56 | map.put(k5, v5); 57 | return map; 58 | } 59 | 60 | public static Map of(K k1, V v1, K k2, V v2, K k3, V v3, 61 | K k4, V v4, K k5, V v5, K k6, V v6) { 62 | HashMap map = new HashMap<>(); 63 | map.put(k1, v1); 64 | map.put(k2, v2); 65 | map.put(k3, v3); 66 | map.put(k4, v4); 67 | map.put(k5, v5); 68 | map.put(k6, v6); 69 | return map; 70 | } 71 | 72 | public static Map of(K k1, V v1, K k2, V v2, K k3, V v3, 73 | K k4, V v4, K k5, V v5, K k6, V v6, 74 | K k7, V v7) { 75 | HashMap map = new HashMap<>(); 76 | map.put(k1, v1); 77 | map.put(k2, v2); 78 | map.put(k3, v3); 79 | map.put(k4, v4); 80 | map.put(k5, v5); 81 | map.put(k6, v6); 82 | map.put(k7, v7); 83 | return map; 84 | } 85 | 86 | public static Map of(K k1, V v1, K k2, V v2, K k3, V v3, 87 | K k4, V v4, K k5, V v5, K k6, V v6, 88 | K k7, V v7, K k8, V v8) { 89 | HashMap map = new HashMap<>(); 90 | map.put(k1, v1); 91 | map.put(k2, v2); 92 | map.put(k3, v3); 93 | map.put(k4, v4); 94 | map.put(k5, v5); 95 | map.put(k6, v6); 96 | map.put(k7, v7); 97 | map.put(k8, v8); 98 | return map; 99 | } 100 | 101 | public static Map of(K k1, V v1, K k2, V v2, K k3, V v3, 102 | K k4, V v4, K k5, V v5, K k6, V v6, 103 | K k7, V v7, K k8, V v8, K k9, V v9) { 104 | HashMap map = new HashMap<>(); 105 | map.put(k1, v1); 106 | map.put(k2, v2); 107 | map.put(k3, v3); 108 | map.put(k4, v4); 109 | map.put(k5, v5); 110 | map.put(k6, v6); 111 | map.put(k7, v7); 112 | map.put(k8, v8); 113 | map.put(k9, v9); 114 | return map; 115 | } 116 | 117 | public static Map of(K k1, V v1, K k2, V v2, K k3, V v3, 118 | K k4, V v4, K k5, V v5, K k6, V v6, 119 | K k7, V v7, K k8, V v8, K k9, V v9, 120 | K k10, V v10) { 121 | HashMap map = new HashMap<>(); 122 | map.put(k1, v1); 123 | map.put(k2, v2); 124 | map.put(k3, v3); 125 | map.put(k4, v4); 126 | map.put(k5, v5); 127 | map.put(k6, v6); 128 | map.put(k7, v7); 129 | map.put(k8, v8); 130 | map.put(k9, v9); 131 | map.put(k10, v10); 132 | return map; 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /jframework-fundation/src/main/java/com/github/neatlife/jframework/fundation/util/SnowFlakeUtil.java: -------------------------------------------------------------------------------- 1 | package com.github.neatlife.jframework.fundation.util; 2 | 3 | import com.google.common.base.Preconditions; 4 | import lombok.SneakyThrows; 5 | 6 | import java.util.Calendar; 7 | 8 | /** 9 | * @author suxiaolin 10 | * @date 2019-04-11 16:44 11 | */ 12 | public class SnowFlakeUtil { 13 | 14 | private static long workerId = 0; 15 | 16 | private static SnowflakeShardingKeyGenerator snowflakeShardingKeyGenerator; 17 | 18 | private SnowFlakeUtil() { 19 | } 20 | 21 | public static void setWorkerId(long workerId) { 22 | SnowFlakeUtil.workerId = workerId; 23 | } 24 | 25 | public static long next() { 26 | return getInstance().generateKey(); 27 | } 28 | 29 | private static synchronized SnowflakeShardingKeyGenerator getInstance() { 30 | if (snowflakeShardingKeyGenerator == null) { 31 | snowflakeShardingKeyGenerator = new SnowflakeShardingKeyGenerator(workerId); 32 | } 33 | return snowflakeShardingKeyGenerator; 34 | } 35 | 36 | public static class SnowflakeShardingKeyGenerator { 37 | 38 | static final long EPOCH; 39 | 40 | private static final long SEQUENCE_BITS = 12L; 41 | 42 | private static final long WORKER_ID_BITS = 10L; 43 | 44 | private static final long SEQUENCE_MASK = (1 << SEQUENCE_BITS) - 1; 45 | 46 | private static final long WORKER_ID_LEFT_SHIFT_BITS = SEQUENCE_BITS; 47 | 48 | private static final long TIMESTAMP_LEFT_SHIFT_BITS = WORKER_ID_LEFT_SHIFT_BITS + WORKER_ID_BITS; 49 | 50 | private static final long WORKER_ID_MAX_VALUE = 1L << WORKER_ID_BITS; 51 | private static final int MAX_TOLERATE_TIME_DIFFERENCE_MILLISECONDS = 10; 52 | 53 | static { 54 | Calendar calendar = Calendar.getInstance(); 55 | calendar.set(2016, Calendar.NOVEMBER, 1); 56 | calendar.set(Calendar.HOUR_OF_DAY, 0); 57 | calendar.set(Calendar.MINUTE, 0); 58 | calendar.set(Calendar.SECOND, 0); 59 | calendar.set(Calendar.MILLISECOND, 0); 60 | EPOCH = calendar.getTimeInMillis(); 61 | } 62 | 63 | private final long workerId; 64 | private byte sequenceOffset; 65 | private long sequence; 66 | private long lastMilliseconds; 67 | 68 | SnowflakeShardingKeyGenerator(long workerId) { 69 | this.workerId = workerId; 70 | } 71 | 72 | synchronized long generateKey() { 73 | long currentMilliseconds = System.currentTimeMillis(); 74 | if (waitTolerateTimeDifferenceIfNeed(currentMilliseconds)) { 75 | currentMilliseconds = System.currentTimeMillis(); 76 | } 77 | if (lastMilliseconds == currentMilliseconds) { 78 | if (0L == (sequence = (sequence + 1) & SEQUENCE_MASK)) { 79 | currentMilliseconds = waitUntilNextTime(currentMilliseconds); 80 | } 81 | } else { 82 | vibrateSequenceOffset(); 83 | sequence = sequenceOffset; 84 | } 85 | lastMilliseconds = currentMilliseconds; 86 | return ((currentMilliseconds - EPOCH) << TIMESTAMP_LEFT_SHIFT_BITS) | (getWorkerId() << WORKER_ID_LEFT_SHIFT_BITS) | sequence; 87 | } 88 | 89 | @SneakyThrows 90 | private boolean waitTolerateTimeDifferenceIfNeed(final long currentMilliseconds) { 91 | if (lastMilliseconds <= currentMilliseconds) { 92 | return false; 93 | } 94 | long timeDifferenceMilliseconds = lastMilliseconds - currentMilliseconds; 95 | Preconditions.checkState(timeDifferenceMilliseconds < getMaxTolerateTimeDifferenceMilliseconds(), 96 | "Clock is moving backwards, last time is %d milliseconds, current time is %d milliseconds", lastMilliseconds, currentMilliseconds); 97 | Thread.sleep(timeDifferenceMilliseconds); 98 | return true; 99 | } 100 | 101 | private long getWorkerId() { 102 | long result = workerId; 103 | Preconditions.checkArgument(result >= 0L && result < WORKER_ID_MAX_VALUE); 104 | return result; 105 | } 106 | 107 | private int getMaxTolerateTimeDifferenceMilliseconds() { 108 | return Integer.valueOf(String.valueOf(MAX_TOLERATE_TIME_DIFFERENCE_MILLISECONDS)); 109 | } 110 | 111 | private long waitUntilNextTime(final long lastTime) { 112 | long result = System.currentTimeMillis(); 113 | while (result <= lastTime) { 114 | result = System.currentTimeMillis(); 115 | } 116 | return result; 117 | } 118 | 119 | private void vibrateSequenceOffset() { 120 | sequenceOffset = (byte) (~sequenceOffset & 1); 121 | } 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /jframework-fundation/src/main/java/com/github/neatlife/jframework/fundation/util/IpUtil.java: -------------------------------------------------------------------------------- 1 | package com.github.neatlife.jframework.fundation.util; 2 | 3 | import javax.servlet.http.HttpServletRequest; 4 | import java.util.Objects; 5 | 6 | /** 7 | * @author suxiaolin 8 | * @date 2019-04-13 08:58 9 | */ 10 | public class IpUtil { 11 | public static String getIpAddr(HttpServletRequest request) { 12 | Objects.requireNonNull(request); 13 | String ip = request.getHeader("x-forwarded-for"); 14 | if (ip == null || ip.length() == 0) { 15 | ip = request.getHeader("Proxy-Client-IP"); 16 | } 17 | if (ip == null || ip.length() == 0) { 18 | ip = request.getHeader("X-Forwarded-For"); 19 | } 20 | if (ip == null || ip.length() == 0) { 21 | ip = request.getHeader("WL-Proxy-Client-IP"); 22 | } 23 | if (ip == null || ip.length() == 0) { 24 | ip = request.getHeader("X-Real-IP"); 25 | } 26 | 27 | if (ip == null || ip.length() == 0) { 28 | ip = request.getRemoteAddr(); 29 | } 30 | 31 | return "0:0:0:0:0:0:0:1".equals(ip) ? "127.0.0.1" : ip; 32 | } 33 | 34 | public static boolean internalIp(String ip) { 35 | byte[] addr = ipv4ToByte(ip); 36 | return internalIp(addr) || "127.0.0.1".equals(ip); 37 | } 38 | 39 | private static boolean internalIp(byte[] addr) { 40 | final byte b0 = addr[0]; 41 | final byte b1 = addr[1]; 42 | // 10.x.x.x/8 43 | final byte section1 = 0x0A; 44 | // 172.16.x.x/12 45 | final byte section2 = (byte) 0xAC; 46 | final byte section3 = (byte) 0x10; 47 | final byte section4 = (byte) 0x1F; 48 | // 192.168.x.x/16 49 | final byte section5 = (byte) 0xC0; 50 | final byte section6 = (byte) 0xA8; 51 | if (b0 == section1) { 52 | return true; 53 | } else if (b0 == section2) { 54 | if (b1 >= section3 && b1 <= section4) { 55 | return true; 56 | } 57 | return b1 == section6; 58 | } else if (b0 == section5) { 59 | return b1 == section6; 60 | 61 | } 62 | return false; 63 | } 64 | 65 | /** 66 | * 将IPv4地址转换成字节 67 | * 68 | * @param text IPv4地址 69 | * @return byte 字节 70 | */ 71 | public static byte[] ipv4ToByte(String text) { 72 | if (text.length() == 0) { 73 | return new byte[0]; 74 | } 75 | 76 | byte[] bytes = new byte[4]; 77 | String[] elements = text.split("\\.", -1); 78 | try { 79 | long l; 80 | int i; 81 | switch (elements.length) { 82 | case 1: 83 | l = Long.parseLong(elements[0]); 84 | if ((l < 0L) || (l > 4294967295L)) { 85 | return new byte[0]; 86 | } 87 | bytes[0] = (byte) (int) (l >> 24 & 0xFF); 88 | bytes[1] = (byte) (int) ((l & 0xFFFFFF) >> 16 & 0xFF); 89 | bytes[2] = (byte) (int) ((l & 0xFFFF) >> 8 & 0xFF); 90 | bytes[3] = (byte) (int) (l & 0xFF); 91 | break; 92 | case 2: 93 | l = Integer.parseInt(elements[0]); 94 | if ((l < 0L) || (l > 255L)) { 95 | return new byte[0]; 96 | } 97 | bytes[0] = (byte) (int) (l & 0xFF); 98 | l = Integer.parseInt(elements[1]); 99 | if ((l < 0L) || (l > 16777215L)) { 100 | return new byte[0]; 101 | } 102 | bytes[1] = (byte) (int) (l >> 16 & 0xFF); 103 | bytes[2] = (byte) (int) ((l & 0xFFFF) >> 8 & 0xFF); 104 | bytes[3] = (byte) (int) (l & 0xFF); 105 | break; 106 | case 3: 107 | for (i = 0; i < 2; ++i) { 108 | l = Integer.parseInt(elements[i]); 109 | if ((l < 0L) || (l > 255L)) 110 | return new byte[0]; 111 | bytes[i] = (byte) (int) (l & 0xFF); 112 | } 113 | l = Integer.parseInt(elements[2]); 114 | if ((l < 0L) || (l > 65535L)) { 115 | return new byte[0]; 116 | } 117 | bytes[2] = (byte) (int) (l >> 8 & 0xFF); 118 | bytes[3] = (byte) (int) (l & 0xFF); 119 | break; 120 | case 4: 121 | for (i = 0; i < 4; ++i) { 122 | l = Integer.parseInt(elements[i]); 123 | if ((l < 0L) || (l > 255L)) 124 | return new byte[0]; 125 | bytes[i] = (byte) (int) (l & 0xFF); 126 | } 127 | break; 128 | default: 129 | return new byte[0]; 130 | } 131 | } catch (NumberFormatException e) { 132 | return new byte[0]; 133 | } 134 | return bytes; 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | [![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=neatlife_jframework&metric=alert_status)](https://sonarcloud.io/dashboard?id=neatlife_jframework) 3 | [![Maven Central](https://img.shields.io/maven-central/v/com.github.neatlife/jframework.svg?label=Maven%20Central)](https://search.maven.org/search?q=g:%22com.github.neatlife%22%20AND%20a:%22jframework%22) 4 | [![License](https://img.shields.io/badge/license-Apache%202-4EB1BA.svg)](https://www.apache.org/licenses/LICENSE-2.0.html) 5 | 6 | ##### 基于对spring boot的二次封装,目的是减少重复代码,提高开发效率, 收集可复用的技术 7 | 8 | 1. 实现jpa对Long类型的时间戳的created_at, updated_at和deleted_at字段的自动维护 9 | 2. 日志集成elk 10 | 3. 日期和json工具类 11 | 4. 集成redis 12 | 5. 集成数据库操作jpa 13 | 6. 集成swagger文档 14 | 7. 控制器发生错误,全局拦截 15 | 8. 支持docker部署 16 | 9. 支持k8s部署 17 | 10. 支持json序列化时自定义小数位数 18 | 11. 接口版本控制 19 | 12. 保留精度的数学计算工具类 20 | 13. http请求工具类 21 | 14. redis和redis分布式锁工具类 22 | 15. json响应工具类 23 | 16. 支持Jenkinsfile构建 24 | 17. 二维码生成工具类 25 | 18. redis阻塞消息队列 26 | 17. id生成工具类 27 | 28 | ##### Maven中引入Jar包 29 | 30 | ```xml 31 | 32 | com.github.neatlife 33 | jframework 34 | 0.0.11 35 | 36 | ``` 37 | 38 | ##### 配置文件示例: src/main/resources/application.properties 39 | 40 | ```properties 41 | spring.datasource.driver-class-name=com.mysql.jdbc.Driver 42 | spring.datasource.url=jdbc:mysql://127.0.0.1/jframework?useUnicode=yes&characterEncoding=utf-8&useSSL=false&allowPublicKeyRetrieval=true 43 | spring.datasource.username=root 44 | spring.datasource.password=root 45 | spring.jpa.open-in-view=false 46 | logging.level.root=INFO 47 | logging.config=classpath:logback-spring.xml 48 | 49 | spring.mail.host=smtp.qq.com 50 | spring.mail.port=587 51 | spring.mail.username=username 52 | spring.mail.password=password 53 | spring.mail.properties.mail.smtp.auth=true 54 | spring.mail.properties.mail.smtp.starttls.enable=true 55 | spring.mail.properties.mail.smtp.starttls.required=true 56 | 57 | 58 | logging.level.org.hibernate.SQL=DEBUG 59 | logging.level.org.hibernate.type.descriptor.sql.BasicBinder=TRACE 60 | spring.jpa.properties.hibernate.format_sql=true 61 | 62 | # Redis数据库索引(默认为0) 63 | spring.redis.database=0 64 | # Redis服务器地址 65 | spring.redis.host=localhost 66 | # Redis服务器连接端口 67 | spring.redis.port=6379 68 | # Redis服务器连接密码(默认为空) 69 | spring.redis.password= 70 | # 连接池最大连接数(使用负值表示没有限制) 默认 8 71 | spring.redis.lettuce.pool.max-active=8 72 | # 连接池最大阻塞等待时间(使用负值表示没有限制) 默认 -1 73 | spring.redis.lettuce.pool.max-wait=-1 74 | # 连接池中的最大空闲连接 默认 8 75 | spring.redis.lettuce.pool.max-idle=8 76 | # 连接池中的最小空闲连接 默认 0 77 | spring.redis.lettuce.pool.min-idle=0 78 | 79 | # 防止邮件重发 80 | jframework.mail.enable-no-repeat=false 81 | jframework.mail.repeat-interval=600 82 | # 钉钉机器人地址 83 | jframework.notification.ding-talk-url=https://oapi.dingtalk.com/robot/send?access_token=钉钉机器人token 84 | jframework.notification.ding-talk-to=钉钉收件人手机号 85 | ``` 86 | 87 | ##### docker 构建步骤 88 | ```shell 89 | # 本地调试 90 | mvn clean package 91 | docker build -t jframework . 92 | docker run -it --rm -p 8780:8080 jframework 93 | 94 | # 推送阿里云 95 | docker build -t jframework . 96 | docker tag jframework:latest registry.cn-hangzhou.aliyuncs.com/suxiaolin/jframework:latest 97 | docker push registry.cn-hangzhou.aliyuncs.com/suxiaolin/jframework:latest 98 | ``` 99 | 100 | 访问127.0.0.1:8780即可看到输出效果 101 | 102 | ##### 工具类 103 | 104 | $.date.currentSecond() 获取当前unix时间戳,单位为秒 105 | 106 | MapperUtil::to(对象1, SomeClass.class) 创建SomeClass类的对象,并把对象1的属性复制到SomeClass的对象上, 在stream流式类型转换时非常有用 107 | ```java 108 | orderList 109 | .stream() 110 | .map(order -> $.mapper.to(order, OrderDto.class)) 111 | .collect(Collectors.toList()); 112 | ``` 113 | 114 | ##### json序列化时小数指定小数位数 115 | ``` 116 | 目前支持对Double和BigDecimal两种类型进行自定义小数位数 117 | Double: 在字段上加上DoubleSpecify(2)注解即可,2是需要保留的小数位数 118 | BigDecimal: 在字段上加上BigDecimalSpecify(2) 119 | ``` 120 | 121 | ##### 验证器 122 | 123 | StringLength 验证器,验证参数字符串的长度范围 124 | 125 | ##### 控制台日志显示全包名 126 | 127 | logging.pattern.console=%clr(%d{yyyy-MM-dd HH:mm:ss}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.400logger{390}):%line{cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx} 128 | 129 | ##### logstash 配置 130 | ```conf 131 | input { 132 | redis { 133 | data_type => "list" 134 | key => "logstash" 135 | host => "127.0.0.1" 136 | port => 6379 137 | threads => 5 138 | codec => "json" 139 | } 140 | } 141 | filter { 142 | 143 | } 144 | output { 145 | 146 | elasticsearch { 147 | hosts => ["elasticsearch:9200"] 148 | index => "logstash-%{type}-%{+YYYY.MM.dd}" 149 | document_type => "%{type}" 150 | workers => 1 151 | template_overwrite => true 152 | } 153 | stdout{} 154 | } 155 | ``` 156 | 157 | ##### 参考资料 158 | 159 | 1. https://github.com/kmtong/logback-redis-appender 160 | 2. https://github.com/looly/hutool 161 | 3. https://github.com/gudaoxuri/dew-common 162 | 4. https://github.com/gudaoxuri/dew 163 | 5. https://github.com/xdd-organ/xdd-nasa 164 | 6. https://github.com/indrabasak/swagger-deepdive 165 | 7. https://github.com/ityouknow/spring-boot-examples 166 | 8. https://github.com/WellJay/spring-data-redis-tools 167 | 9. https://github.com/WuNanliang/demo-csv 168 | 10. https://github.com/SargerasWang/ExcelUtil.git 169 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | pom 6 | 7 | jframework-fundation 8 | jframework-redis 9 | jframework-test 10 | jframework-springfox 11 | jframework-logback 12 | jframework-jpa 13 | jframework-hystrix 14 | jframework-excel 15 | 16 | 17 | org.springframework.boot 18 | spring-boot-starter-parent 19 | 2.1.2.RELEASE 20 | 21 | 22 | com.github.neatlife 23 | jframework 24 | 0.0.2 25 | jframework 26 | https://github.com/neatlife/jframework 27 | 基于对spring boot的二次封装,目的是减少重复代码,提高开发效率 28 | 29 | 30 | Apache 2 31 | http://www.apache.org/licenses/LICENSE-2.0.txt 32 | repo 33 | A business-friendly OSS license 34 | 35 | 36 | 37 | https://github.com/neatlife/jframework 38 | https://github.com/neatlife/jframework.git 39 | 40 | 41 | 42 | 1.8 43 | 4.5.6 44 | 1.4.0 45 | 46 | 47 | 48 | 49 | 50 | org.springframework.boot 51 | spring-boot-starter 52 | 53 | 54 | 55 | 56 | 57 | jframework 58 | 59 | 60 | org.springframework.boot 61 | spring-boot-maven-plugin 62 | 63 | 64 | 65 | org.apache.maven.plugins 66 | maven-source-plugin 67 | 2.2.1 68 | 69 | 70 | attach-sources 71 | 72 | jar-no-fork 73 | 74 | 75 | 76 | 77 | 78 | 79 | org.apache.maven.plugins 80 | maven-javadoc-plugin 81 | 2.9.1 82 | 83 | 84 | attach-javadocs 85 | 86 | jar 87 | 88 | 89 | -Xdoclint:none 90 | 91 | 92 | 93 | 94 | 95 | 96 | org.apache.maven.plugins 97 | maven-gpg-plugin 98 | 1.6 99 | 100 | 101 | sign-artifacts 102 | verify 103 | 104 | sign 105 | 106 | 107 | 108 | 109 | 110 | 111 | org.sonatype.plugins 112 | nexus-staging-maven-plugin 113 | 1.6.7 114 | true 115 | 116 | oss 117 | https://oss.sonatype.org/ 118 | true 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | suxiaolin 127 | suxiaolin 128 | dear.lin@live.com 129 | 130 | Developer 131 | 132 | +8 133 | 134 | 135 | 136 | -------------------------------------------------------------------------------- /jframework-fundation/src/main/java/com/github/neatlife/jframework/fundation/util/JsonUtil.java: -------------------------------------------------------------------------------- 1 | package com.github.neatlife.jframework.fundation.util; 2 | 3 | import com.fasterxml.jackson.core.JsonParser; 4 | import com.fasterxml.jackson.databind.*; 5 | import com.fasterxml.jackson.databind.node.ArrayNode; 6 | import com.fasterxml.jackson.databind.node.MissingNode; 7 | import com.fasterxml.jackson.databind.node.ObjectNode; 8 | import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; 9 | import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer; 10 | 11 | import java.io.IOException; 12 | import java.time.LocalDateTime; 13 | import java.time.format.DateTimeFormatter; 14 | import java.util.*; 15 | 16 | /** 17 | * @author suxiaolin 18 | */ 19 | public class JsonUtil { 20 | 21 | private static final Map INSTANCES = new HashMap<>(); 22 | 23 | private static ObjectMapper mapper; 24 | 25 | static { 26 | mapper = new ObjectMapper(); 27 | mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); 28 | mapper.configure(JsonParser.Feature.ALLOW_COMMENTS, true); 29 | mapper.configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true); 30 | mapper.configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true); 31 | JavaTimeModule javaTimeModule = new JavaTimeModule(); 32 | javaTimeModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.ISO_DATE_TIME)); 33 | mapper.registerModule(javaTimeModule); 34 | mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false); 35 | setTimeZone(Calendar.getInstance().getTimeZone()); 36 | } 37 | 38 | static JsonUtil pick(String instanceId) { 39 | return INSTANCES.computeIfAbsent(instanceId, k -> new JsonUtil()); 40 | } 41 | 42 | public static void setTimeZone(TimeZone tz) { 43 | mapper.setTimeZone(tz); 44 | } 45 | 46 | public static String toJsonString(Object obj) { 47 | if (obj instanceof String) { 48 | return (String) obj; 49 | } else { 50 | try { 51 | return mapper.writeValueAsString(obj); 52 | } catch (Exception e) { 53 | throw new RuntimeException(e); 54 | } 55 | } 56 | } 57 | 58 | public static JsonNode toJson(Object obj) { 59 | if (obj instanceof String) { 60 | try { 61 | return mapper.readTree((String) obj); 62 | } catch (IOException e) { 63 | throw new RuntimeException(e); 64 | } 65 | } else { 66 | return mapper.valueToTree(obj); 67 | } 68 | } 69 | 70 | public static List toList(Object obj, Class clazz) { 71 | return (List) toGeneric(obj, List.class, clazz); 72 | } 73 | 74 | public static Set toSet(Object obj, Class clazz) { 75 | return (Set) toGeneric(obj, Set.class, clazz); 76 | } 77 | 78 | public static Map toMap(Object obj, Class keyClazz, Class valueClazz) { 79 | return (Map) toGeneric(obj, Map.class, keyClazz, valueClazz); 80 | } 81 | 82 | public static Object toGeneric(Object obj, Class parametrized, Class... parameterClasses) { 83 | JavaType type = mapper.getTypeFactory().constructParametricType(parametrized, parameterClasses); 84 | return toGeneric(obj, type); 85 | } 86 | 87 | private static Object toGeneric(Object obj, JavaType type) { 88 | try { 89 | if (obj instanceof String) { 90 | return mapper.readValue((String) obj, type); 91 | } else if (obj instanceof JsonNode) { 92 | return mapper.readValue(obj.toString(), type); 93 | } else { 94 | return mapper.readValue(mapper.writeValueAsString(obj), type); 95 | } 96 | } catch (IOException e) { 97 | throw new RuntimeException(e); 98 | } 99 | } 100 | 101 | public static E toObject(Object obj, Class clazz) { 102 | try { 103 | if (obj instanceof String) { 104 | if (clazz == String.class) { 105 | return (E) obj; 106 | } else { 107 | return mapper.readValue((String) obj, clazz); 108 | } 109 | } else if (obj instanceof JsonNode) { 110 | return mapper.readValue(obj.toString(), clazz); 111 | } else { 112 | return mapper.readValue(mapper.writeValueAsString(obj), clazz); 113 | } 114 | } catch (IOException e) { 115 | throw new RuntimeException(e); 116 | } 117 | } 118 | 119 | public static JsonNode path(JsonNode jsonNode, String pathStr) { 120 | String[] splitPaths = pathStr.split("\\."); 121 | jsonNode = jsonNode.path(splitPaths[0]); 122 | if (jsonNode instanceof MissingNode) { 123 | return null; 124 | } else if (splitPaths.length == 1) { 125 | return jsonNode; 126 | } else { 127 | return path(jsonNode, pathStr.substring(pathStr.indexOf(".") + 1)); 128 | } 129 | } 130 | 131 | public static ObjectNode createObjectNode() { 132 | return mapper.createObjectNode(); 133 | } 134 | 135 | public static ArrayNode createArrayNode() { 136 | return mapper.createArrayNode(); 137 | } 138 | 139 | public static ObjectMapper getMapper() { 140 | return mapper; 141 | } 142 | 143 | } 144 | 145 | -------------------------------------------------------------------------------- /jframework-test/src/main/resources/logback-spring.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 16 | 17 | 18 | ERROR 19 | 20 | DENY 21 | 22 | ACCEPT 23 | 24 | ${logback.logDir}/info.log 25 | 26 | 27 | 28 | ${logback.logDir}/info.%d{yyyy-MM-dd}.%i.log.gz 29 | 30 | 100MB 31 | 32 | 30 33 | 34 | 1GB 35 | 36 | 37 | 38 | UTF-8 39 | %d [%t] %-5level %logger{36}.%M\(%file:%line\) - %msg%n 40 | 41 | 42 | 43 | 44 | 45 | ERROR 46 | 47 | 51 | ${logback.logDir}/error.log 52 | 53 | 54 | 55 | ${logback.logDir}/error.%d{yyyy-MM-dd}.%i.log.gz 56 | 57 | 100MB 58 | 59 | 30 60 | 61 | 1GB 62 | 63 | 64 | 65 | UTF-8 66 | %d [%t] %-5level %logger{36}.%M\(%file:%line\) - %msg%n 67 | 68 | 69 | 70 | jframework 71 | 127.0.0.1 72 | 6379 73 | 74 | logstash 75 | 76 | true 77 | 78 | env 79 | local 80 | 81 | true 82 | 83 | 84 | ${DING_TALK_TO} 85 | 86 | 87 | 88 | 89 | 90 | ${MAIL_HOST} 91 | ${MAIL_PORT} 92 | ${MAIL_USERNAME} 93 | ${MAIL_PASSWORD} 94 | ${MAIL_ENABLE_TLS} 95 | true 96 | dear.lin@live.com 97 | jframework 98 | Error: %logger{20} - %m 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | z 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | -------------------------------------------------------------------------------- /jframework-redis/src/main/java/com/github/neatlife/jframework/redis/util/LockUtil.java: -------------------------------------------------------------------------------- 1 | package com.github.neatlife.jframework.redis.util; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import org.springframework.data.redis.connection.RedisConnection; 5 | import org.springframework.data.redis.connection.ReturnType; 6 | import org.springframework.data.redis.core.RedisTemplate; 7 | import org.springframework.data.redis.core.script.DefaultRedisScript; 8 | 9 | import java.util.concurrent.TimeUnit; 10 | 11 | /** 12 | * @author suxiaolin 13 | * @date 2019-03-21 8:12 14 | */ 15 | @Slf4j 16 | public class LockUtil { 17 | public static final String LOCK_SCRIPT_STR = "if redis.call('set',KEYS[1],ARGV[1],'EX',ARGV[2],'NX') then return 1 else return 0 end"; 18 | public static final String UNLOCK_SCRIPT_STR = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end"; 19 | public static final Integer DEFAULT_EXPIRE_SECOND = 60; 20 | public static final Long DEFAULT_LOOP_TIMES = 10L; 21 | public static final Long DEFAULT_SLEEP_INTERVAL = 500L; 22 | public static final String PACKAGE_NAME_SPLIT_STR = "\\."; 23 | public static final String CLASS_AND_METHOD_CONCAT_STR = "->"; 24 | private static final Long SUCCESS = 1L; 25 | private static RedisTemplate redisTemplate; 26 | 27 | private LockUtil() { 28 | } 29 | 30 | public static void setRedisTemplate(RedisTemplate redisTemplate) { 31 | LockUtil.redisTemplate = redisTemplate; 32 | } 33 | 34 | /** 35 | * 得到分布式锁 36 | * 默认key:调用者类名 37 | * 38 | * @return 39 | * @throws InterruptedException 40 | */ 41 | public static boolean tryGetDistributedLock() { 42 | String callerKey = getCurrentThreadCaller(); 43 | String requestId = String.valueOf(Thread.currentThread().getId()); 44 | return tryGetDistributedLock(callerKey, requestId); 45 | } 46 | 47 | /** 48 | * @param lockKey 锁名称 49 | * @param requestId 随机请求id 50 | * @return 51 | * @throws InterruptedException 52 | */ 53 | public static boolean tryGetDistributedLock(String lockKey, String requestId) { 54 | return tryGetDistributedLock(lockKey, requestId, DEFAULT_EXPIRE_SECOND); 55 | } 56 | 57 | /** 58 | * @param lockKey key 59 | * @param requestId 随机请求id 60 | * @param expireSecond 超时秒 61 | * @return 62 | * @throws InterruptedException 63 | */ 64 | public static boolean tryGetDistributedLock(String lockKey, String requestId, Integer expireSecond) { 65 | return tryGetDistributedLock(lockKey, requestId, expireSecond, DEFAULT_LOOP_TIMES, DEFAULT_SLEEP_INTERVAL); 66 | } 67 | 68 | 69 | /** 70 | * 加锁 71 | * 72 | * @param lockKey key 73 | * @param requestId 随机请求id 74 | * @param expireSecond 超时秒 75 | * @param loopTimes 循环次数 76 | * @param sleepInterval 等待间隔(毫秒) 77 | * @return 78 | */ 79 | public static boolean tryGetDistributedLock(String lockKey, String requestId, Integer expireSecond, Long loopTimes, Long sleepInterval) { 80 | DefaultRedisScript redisScript = new DefaultRedisScript<>(LOCK_SCRIPT_STR, Long.class); 81 | while (loopTimes-- >= 0) { 82 | Object result = redisTemplate.execute( 83 | (RedisConnection connection) -> connection.eval( 84 | redisScript.getScriptAsString().getBytes(), 85 | ReturnType.INTEGER, 86 | 1, 87 | lockKey.getBytes(), 88 | requestId.getBytes(), 89 | String.valueOf(expireSecond).getBytes() 90 | ) 91 | ); 92 | if (SUCCESS.equals(result)) { 93 | return true; 94 | } 95 | try { 96 | TimeUnit.MILLISECONDS.sleep(sleepInterval); 97 | } catch (InterruptedException e) { 98 | log.error("e message: {}", e.getMessage()); 99 | Thread.currentThread().interrupt(); 100 | } 101 | } 102 | return false; 103 | } 104 | 105 | /** 106 | * 释放锁 107 | * 108 | * @return 109 | */ 110 | public static boolean releaseDistributedLock() { 111 | String callerKey = getCurrentThreadCaller(); 112 | String requestId = String.valueOf(Thread.currentThread().getId()); 113 | return releaseDistributedLock(callerKey, requestId); 114 | } 115 | 116 | /** 117 | * 释放锁 118 | * 119 | * @param lockKey key 120 | * @param requestId 加锁的请求id 121 | * @return 122 | */ 123 | public static boolean releaseDistributedLock(String lockKey, String requestId) { 124 | DefaultRedisScript redisScript = new DefaultRedisScript<>(UNLOCK_SCRIPT_STR, Long.class); 125 | Object result = redisTemplate.execute( 126 | (RedisConnection connection) -> connection.eval( 127 | redisScript.getScriptAsString().getBytes(), 128 | ReturnType.INTEGER, 129 | 1, 130 | lockKey.getBytes(), 131 | requestId.getBytes() 132 | ) 133 | ); 134 | return SUCCESS.equals(result); 135 | } 136 | 137 | private static String getSimpleClassName(String className) { 138 | String[] splits = className.split(PACKAGE_NAME_SPLIT_STR); 139 | return splits[splits.length - 1]; 140 | } 141 | 142 | /** 143 | * Get caller 144 | * 145 | * @return 146 | */ 147 | private static String getCurrentThreadCaller() { 148 | StackTraceElement stackTraceElement = Thread.currentThread().getStackTrace()[3]; 149 | return getSimpleClassName(stackTraceElement.getClassName()) + CLASS_AND_METHOD_CONCAT_STR + stackTraceElement.getMethodName(); 150 | } 151 | } 152 | 153 | -------------------------------------------------------------------------------- /jframework-logback/src/main/java/com/github/neatlife/jframework/logback/RedisAppender.java: -------------------------------------------------------------------------------- 1 | package com.github.neatlife.jframework.logback; 2 | 3 | import ch.qos.logback.classic.spi.ILoggingEvent; 4 | import ch.qos.logback.core.Layout; 5 | import ch.qos.logback.core.UnsynchronizedAppenderBase; 6 | import lombok.extern.slf4j.Slf4j; 7 | import org.apache.commons.pool2.impl.GenericObjectPoolConfig; 8 | import redis.clients.jedis.Jedis; 9 | import redis.clients.jedis.JedisPool; 10 | import redis.clients.jedis.Protocol; 11 | 12 | import java.util.Arrays; 13 | import java.util.Iterator; 14 | 15 | @Slf4j 16 | public class RedisAppender extends UnsynchronizedAppenderBase { 17 | 18 | JedisPool pool; 19 | /** 20 | * keep this for redis compatibility for now 21 | */ 22 | JSONEventLayout jsonlayout; 23 | Layout layout; 24 | /** 25 | * logger configurable options 26 | */ 27 | String host = "localhost"; 28 | int port = Protocol.DEFAULT_PORT; 29 | String key = null; 30 | int timeout = Protocol.DEFAULT_TIMEOUT; 31 | String password = null; 32 | int database = Protocol.DEFAULT_DATABASE; 33 | 34 | public RedisAppender() { 35 | jsonlayout = new JSONEventLayout(); 36 | } 37 | 38 | @Override 39 | protected void append(ILoggingEvent event) { 40 | Jedis client = pool.getResource(); 41 | try { 42 | 43 | String json = layout == null ? jsonlayout.doLayout(event) : layout.doLayout(event); 44 | client.rpush(key, json); 45 | } catch (Exception e) { 46 | log.error("e message: {}", e.getMessage()); 47 | pool.returnBrokenResource(client); 48 | client = null; 49 | } finally { 50 | if (client != null) { 51 | pool.returnResource(client); 52 | } 53 | } 54 | } 55 | 56 | @Deprecated 57 | public String getSource() { 58 | return jsonlayout.getSource(); 59 | } 60 | 61 | @Deprecated 62 | public void setSource(String source) { 63 | jsonlayout.setSource(source); 64 | } 65 | 66 | @Deprecated 67 | public String getSourceHost() { 68 | return jsonlayout.getSourceHost(); 69 | } 70 | 71 | @Deprecated 72 | public void setSourceHost(String sourceHost) { 73 | jsonlayout.setSourceHost(sourceHost); 74 | } 75 | 76 | @Deprecated 77 | public String getSourcePath() { 78 | return jsonlayout.getSourcePath(); 79 | } 80 | 81 | @Deprecated 82 | public void setSourcePath(String sourcePath) { 83 | jsonlayout.setSourcePath(sourcePath); 84 | } 85 | 86 | @Deprecated 87 | public String getTags() { 88 | if (jsonlayout.getTags() != null) { 89 | Iterator i = jsonlayout.getTags().iterator(); 90 | StringBuilder sb = new StringBuilder(); 91 | while (i.hasNext()) { 92 | sb.append(i.next()); 93 | if (i.hasNext()) { 94 | sb.append(','); 95 | } 96 | } 97 | return sb.toString(); 98 | } 99 | return null; 100 | } 101 | 102 | @Deprecated 103 | public void setTags(String tags) { 104 | if (tags != null) { 105 | String[] atags = tags.split(","); 106 | jsonlayout.setTags(Arrays.asList(atags)); 107 | } 108 | } 109 | 110 | @Deprecated 111 | public String getType() { 112 | return jsonlayout.getType(); 113 | } 114 | 115 | @Deprecated 116 | public void setType(String type) { 117 | jsonlayout.setType(type); 118 | } 119 | 120 | public String getHost() { 121 | return host; 122 | } 123 | 124 | public void setHost(String host) { 125 | this.host = host; 126 | } 127 | 128 | public int getPort() { 129 | return port; 130 | } 131 | 132 | public void setPort(int port) { 133 | this.port = port; 134 | } 135 | 136 | public String getKey() { 137 | return key; 138 | } 139 | 140 | public void setKey(String key) { 141 | this.key = key; 142 | } 143 | 144 | public int getTimeout() { 145 | return timeout; 146 | } 147 | 148 | public void setTimeout(int timeout) { 149 | this.timeout = timeout; 150 | } 151 | 152 | public String getPassword() { 153 | return password; 154 | } 155 | 156 | public void setPassword(String password) { 157 | this.password = password; 158 | } 159 | 160 | public int getDatabase() { 161 | return database; 162 | } 163 | 164 | public void setDatabase(int database) { 165 | this.database = database; 166 | } 167 | 168 | @Deprecated 169 | public boolean getMdc() { 170 | return jsonlayout.getProperties(); 171 | } 172 | 173 | @Deprecated 174 | public void setMdc(boolean flag) { 175 | jsonlayout.setProperties(flag); 176 | } 177 | 178 | @Deprecated 179 | public boolean getLocation() { 180 | return jsonlayout.getLocationInfo(); 181 | } 182 | 183 | @Deprecated 184 | public void setLocation(boolean flag) { 185 | jsonlayout.setLocationInfo(flag); 186 | } 187 | 188 | @Deprecated 189 | public int getCallerStackIndex() { 190 | return jsonlayout.getCallerStackIdx(); 191 | } 192 | 193 | @Deprecated 194 | public void setCallerStackIndex(int index) { 195 | jsonlayout.setCallerStackIdx(index); 196 | } 197 | 198 | @Deprecated 199 | public void addAdditionalField(AdditionalField p) { 200 | jsonlayout.addAdditionalField(p); 201 | } 202 | 203 | public Layout getLayout() { 204 | return layout; 205 | } 206 | 207 | public void setLayout(Layout layout) { 208 | this.layout = layout; 209 | } 210 | 211 | @Override 212 | public void start() { 213 | super.start(); 214 | GenericObjectPoolConfig config = new GenericObjectPoolConfig(); 215 | config.setTestOnBorrow(true); 216 | pool = new JedisPool(config, host, port, timeout, password, database); 217 | } 218 | 219 | @Override 220 | public void stop() { 221 | super.stop(); 222 | pool.destroy(); 223 | } 224 | 225 | } 226 | -------------------------------------------------------------------------------- /mvnw.cmd: -------------------------------------------------------------------------------- 1 | @REM ---------------------------------------------------------------------------- 2 | @REM Licensed to the Apache Software Foundation (ASF) under one 3 | @REM or more contributor license agreements. See the NOTICE file 4 | @REM distributed with this work for additional information 5 | @REM regarding copyright ownership. The ASF licenses this file 6 | @REM to you under the Apache License, Version 2.0 (the 7 | @REM "License"); you may not use this file except in compliance 8 | @REM with the License. You may obtain a copy of the License at 9 | @REM 10 | @REM http://www.apache.org/licenses/LICENSE-2.0 11 | @REM 12 | @REM Unless required by applicable law or agreed to in writing, 13 | @REM software distributed under the License is distributed on an 14 | @REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | @REM KIND, either express or implied. See the License for the 16 | @REM specific language governing permissions and limitations 17 | @REM under the License. 18 | @REM ---------------------------------------------------------------------------- 19 | 20 | @REM ---------------------------------------------------------------------------- 21 | @REM Maven2 Start Up Batch script 22 | @REM 23 | @REM Required ENV vars: 24 | @REM JAVA_HOME - location of a JDK home dir 25 | @REM 26 | @REM Optional ENV vars 27 | @REM M2_HOME - location of maven2's installed home dir 28 | @REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands 29 | @REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a key stroke before ending 30 | @REM MAVEN_OPTS - parameters passed to the Java VM when running Maven 31 | @REM e.g. to debug Maven itself, use 32 | @REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 33 | @REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files 34 | @REM ---------------------------------------------------------------------------- 35 | 36 | @REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' 37 | @echo off 38 | @REM set title of command window 39 | title %0 40 | @REM enable echoing my setting MAVEN_BATCH_ECHO to 'on' 41 | @if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% 42 | 43 | @REM set %HOME% to equivalent of $HOME 44 | if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") 45 | 46 | @REM Execute a user defined script before this one 47 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre 48 | @REM check for pre script, once with legacy .bat ending and once with .cmd ending 49 | if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" 50 | if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" 51 | :skipRcPre 52 | 53 | @setlocal 54 | 55 | set ERROR_CODE=0 56 | 57 | @REM To isolate internal variables from possible post scripts, we use another setlocal 58 | @setlocal 59 | 60 | @REM ==== START VALIDATION ==== 61 | if not "%JAVA_HOME%" == "" goto OkJHome 62 | 63 | echo. 64 | echo Error: JAVA_HOME not found in your environment. >&2 65 | echo Please set the JAVA_HOME variable in your environment to match the >&2 66 | echo location of your Java installation. >&2 67 | echo. 68 | goto error 69 | 70 | :OkJHome 71 | if exist "%JAVA_HOME%\bin\java.exe" goto init 72 | 73 | echo. 74 | echo Error: JAVA_HOME is set to an invalid directory. >&2 75 | echo JAVA_HOME = "%JAVA_HOME%" >&2 76 | echo Please set the JAVA_HOME variable in your environment to match the >&2 77 | echo location of your Java installation. >&2 78 | echo. 79 | goto error 80 | 81 | @REM ==== END VALIDATION ==== 82 | 83 | :init 84 | 85 | @REM Find the project base dir, i.e. the directory that contains the folder ".mvn". 86 | @REM Fallback to current working directory if not found. 87 | 88 | set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% 89 | IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir 90 | 91 | set EXEC_DIR=%CD% 92 | set WDIR=%EXEC_DIR% 93 | :findBaseDir 94 | IF EXIST "%WDIR%"\.mvn goto baseDirFound 95 | cd .. 96 | IF "%WDIR%"=="%CD%" goto baseDirNotFound 97 | set WDIR=%CD% 98 | goto findBaseDir 99 | 100 | :baseDirFound 101 | set MAVEN_PROJECTBASEDIR=%WDIR% 102 | cd "%EXEC_DIR%" 103 | goto endDetectBaseDir 104 | 105 | :baseDirNotFound 106 | set MAVEN_PROJECTBASEDIR=%EXEC_DIR% 107 | cd "%EXEC_DIR%" 108 | 109 | :endDetectBaseDir 110 | 111 | IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig 112 | 113 | @setlocal EnableExtensions EnableDelayedExpansion 114 | for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a 115 | @endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% 116 | 117 | :endReadAdditionalConfig 118 | 119 | SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" 120 | set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" 121 | set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 122 | 123 | set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.4.2/maven-wrapper-0.4.2.jar" 124 | FOR /F "tokens=1,2 delims==" %%A IN (%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties) DO ( 125 | IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B 126 | ) 127 | 128 | @REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central 129 | @REM This allows using the maven wrapper in projects that prohibit checking in binary data. 130 | if exist %WRAPPER_JAR% ( 131 | echo Found %WRAPPER_JAR% 132 | ) else ( 133 | echo Couldn't find %WRAPPER_JAR%, downloading it ... 134 | echo Downloading from: %DOWNLOAD_URL% 135 | powershell -Command "(New-Object Net.WebClient).DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')" 136 | echo Finished downloading %WRAPPER_JAR% 137 | ) 138 | @REM End of extension 139 | 140 | %MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* 141 | if ERRORLEVEL 1 goto error 142 | goto end 143 | 144 | :error 145 | set ERROR_CODE=1 146 | 147 | :end 148 | @endlocal & set ERROR_CODE=%ERROR_CODE% 149 | 150 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost 151 | @REM check for post script, once with legacy .bat ending and once with .cmd ending 152 | if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" 153 | if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" 154 | :skipRcPost 155 | 156 | @REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' 157 | if "%MAVEN_BATCH_PAUSE%" == "on" pause 158 | 159 | if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% 160 | 161 | exit /B %ERROR_CODE% 162 | -------------------------------------------------------------------------------- /jframework-redis/src/main/java/com/github/neatlife/jframework/redis/util/RedisUtil.java: -------------------------------------------------------------------------------- 1 | package com.github.neatlife.jframework.redis.util; 2 | 3 | import org.springframework.data.redis.core.*; 4 | import org.springframework.util.ObjectUtils; 5 | 6 | import java.util.*; 7 | import java.util.concurrent.TimeUnit; 8 | 9 | /** 10 | * @author suxiaolin 11 | * @date 2019-03-21 08:20 12 | */ 13 | public class RedisUtil { 14 | 15 | private static RedisTemplate redisTemplate; 16 | 17 | private RedisUtil() { 18 | } 19 | 20 | public static void setRedisTemplate(RedisTemplate redisTemplate) { 21 | RedisUtil.redisTemplate = redisTemplate; 22 | } 23 | 24 | /** 25 | * 缓存基本的对象,Integer、String、实体类等 26 | * 27 | * @param key 缓存的键值 28 | * @param value 缓存的值 29 | * @return 缓存的对象 30 | */ 31 | public static ValueOperations setCacheObject(String key, T value) { 32 | ValueOperations operation = redisTemplate.opsForValue(); 33 | operation.set(key, value); 34 | return operation; 35 | } 36 | 37 | public static ValueOperations setCacheObject(String key, T value, Integer timeout, TimeUnit timeUnit) { 38 | ValueOperations operation = redisTemplate.opsForValue(); 39 | operation.set(key, value, timeout, timeUnit); 40 | return operation; 41 | } 42 | 43 | /** 44 | * 获得缓存的基本对象。 45 | * 46 | * @param key 缓存键值 47 | * @return 缓存键值对应的数据 48 | */ 49 | public static T getCacheObject(String key) { 50 | ValueOperations operation = redisTemplate.opsForValue(); 51 | return operation.get(key); 52 | } 53 | 54 | /** 55 | * 删除单个对象 56 | * 57 | * @param key 58 | */ 59 | public static void deleteObject(String key) { 60 | redisTemplate.delete(key); 61 | } 62 | 63 | /** 64 | * 删除集合对象 65 | * 66 | * @param collection 67 | */ 68 | public static void deleteObject(Collection collection) { 69 | redisTemplate.delete(collection); 70 | } 71 | 72 | /** 73 | * 缓存List数据 74 | * 75 | * @param key 缓存的键值 76 | * @param dataList 待缓存的List数据 77 | * @return 缓存的对象 78 | */ 79 | public static ListOperations setCacheList(String key, List dataList) { 80 | ListOperations listOperation = redisTemplate.opsForList(); 81 | if (null != dataList) { 82 | int size = dataList.size(); 83 | for (int i = 0; i < size; i++) { 84 | listOperation.leftPush(key, dataList.get(i)); 85 | } 86 | } 87 | return listOperation; 88 | } 89 | 90 | /** 91 | * 获得缓存的list对象 92 | * 93 | * @param key 缓存的键值 94 | * @return 缓存键值对应的数据 95 | */ 96 | public static List getCacheList(String key) { 97 | List dataList = new ArrayList(); 98 | ListOperations listOperation = redisTemplate.opsForList(); 99 | Long size = listOperation.size(key); 100 | 101 | for (int i = 0; i < size; i++) { 102 | dataList.add(listOperation.index(key, i)); 103 | } 104 | return dataList; 105 | } 106 | 107 | /** 108 | * 缓存Set 109 | * 110 | * @param key 缓存键值 111 | * @param dataSet 缓存的数据 112 | * @return 缓存数据的对象 113 | */ 114 | public static BoundSetOperations setCacheSet(String key, Set dataSet) { 115 | BoundSetOperations setOperation = redisTemplate.boundSetOps(key); 116 | Iterator it = dataSet.iterator(); 117 | while (it.hasNext()) { 118 | setOperation.add(it.next()); 119 | } 120 | return setOperation; 121 | } 122 | 123 | /** 124 | * 获得缓存的set 125 | * 126 | * @param key 127 | * @return 128 | */ 129 | public static Set getCacheSet(String key) { 130 | Set dataSet = new HashSet(); 131 | BoundSetOperations operation = redisTemplate.boundSetOps(key); 132 | Long size = operation.size(); 133 | for (int i = 0; i < size; i++) { 134 | dataSet.add(operation.pop()); 135 | } 136 | return dataSet; 137 | } 138 | 139 | /** 140 | * 缓存Map 141 | * 142 | * @param key 143 | * @param dataMap 144 | * @return 145 | */ 146 | public static HashOperations setCacheMap(String key, Map dataMap) { 147 | 148 | HashOperations hashOperations = redisTemplate.opsForHash(); 149 | if (null != dataMap) { 150 | for (Map.Entry entry : dataMap.entrySet()) { 151 | hashOperations.put(key, entry.getKey(), entry.getValue()); 152 | } 153 | } 154 | return hashOperations; 155 | } 156 | 157 | /** 158 | * 获得缓存的Map 159 | * 160 | * @param key 161 | * @return 162 | */ 163 | public static Map getCacheMap(String key) { 164 | Map map = redisTemplate.opsForHash().entries(key); 165 | return map; 166 | } 167 | 168 | /** 169 | * 缓存Map 170 | * 171 | * @param key 172 | * @param dataMap 173 | * @return 174 | */ 175 | public static HashOperations setCacheIntegerMap(String key, Map dataMap) { 176 | HashOperations hashOperations = redisTemplate.opsForHash(); 177 | if (null != dataMap) { 178 | for (Map.Entry entry : dataMap.entrySet()) { 179 | hashOperations.put(key, entry.getKey(), entry.getValue()); 180 | } 181 | } 182 | return hashOperations; 183 | } 184 | 185 | /** 186 | * 获得缓存的Map 187 | * 188 | * @param key 189 | * @return 190 | */ 191 | public static Map getCacheIntegerMap(String key) { 192 | Map map = redisTemplate.opsForHash().entries(key); 193 | return map; 194 | } 195 | 196 | /** 197 | * push 198 | * 199 | * @param key 缓存的键值 200 | * @param message 待缓存的数据 201 | * @return 缓存的对象 202 | */ 203 | public static Boolean lpush(String key, String message) { 204 | return !ObjectUtils.isEmpty(redisTemplate.opsForList().leftPush(key, message)); 205 | } 206 | 207 | /** 208 | * 阻塞队列, 出队列 209 | * 210 | * @param queue 需要阻塞的队列名称 211 | * @param blockSeconds 阻塞的秒数 212 | * @return 213 | */ 214 | public static String blpop(String queue, Integer blockSeconds) { 215 | return (String) redisTemplate.opsForList().leftPop(queue, blockSeconds, TimeUnit.SECONDS); 216 | } 217 | } 218 | -------------------------------------------------------------------------------- /jframework-test/src/test/java/com/github/neatlife/jframework/test/util/PrecisionUtilTest.java: -------------------------------------------------------------------------------- 1 | package com.github.neatlife.jframework.test.util; 2 | 3 | import com.github.neatlife.jframework.fundation.util.PrecisionUtil; 4 | import org.junit.Assert; 5 | import org.junit.Test; 6 | 7 | import java.math.BigDecimal; 8 | 9 | public class PrecisionUtilTest { 10 | 11 | @Test 12 | public void testBigDecimalAdd() { 13 | Assert.assertEquals(BigDecimal.TEN, 14 | PrecisionUtil.add(null, BigDecimal.TEN)); 15 | Assert.assertEquals(BigDecimal.TEN, 16 | PrecisionUtil.add(BigDecimal.TEN, null)); 17 | Assert.assertEquals(new BigDecimal("7"), 18 | PrecisionUtil.add(new BigDecimal("3"), new BigDecimal("4"))); 19 | } 20 | 21 | @Test 22 | public void testBigDecimalSubtract() { 23 | Assert.assertEquals(new BigDecimal("-10"), 24 | PrecisionUtil.subtract(null, BigDecimal.TEN)); 25 | Assert.assertEquals(BigDecimal.ONE, 26 | PrecisionUtil.subtract(BigDecimal.ONE, null)); 27 | Assert.assertEquals(new BigDecimal("3"), 28 | PrecisionUtil.subtract(new BigDecimal("7"), new BigDecimal("4"))); 29 | } 30 | 31 | @Test 32 | public void testBigDecimalMultiply() { 33 | Assert.assertEquals(BigDecimal.TEN, 34 | PrecisionUtil.multiply(null, BigDecimal.TEN)); 35 | Assert.assertEquals(BigDecimal.TEN, 36 | PrecisionUtil.multiply(BigDecimal.TEN, null)); 37 | Assert.assertEquals(new BigDecimal("8"), 38 | PrecisionUtil.multiply(new BigDecimal("2"), new BigDecimal("4"))); 39 | } 40 | 41 | @Test 42 | public void testBigDecimalDivide() { 43 | Assert.assertEquals(new BigDecimal("0.10"), 44 | PrecisionUtil.divide(BigDecimal.ONE, BigDecimal.TEN)); 45 | Assert.assertEquals(new BigDecimal("0.10"), 46 | PrecisionUtil.divide(BigDecimal.ONE, BigDecimal.TEN)); 47 | Assert.assertEquals(new BigDecimal("0"), 48 | PrecisionUtil.divide(null, BigDecimal.TEN, 1)); 49 | Assert.assertEquals(BigDecimal.TEN, 50 | PrecisionUtil.divide(BigDecimal.TEN, null, 0)); 51 | Assert.assertEquals(new BigDecimal("5"), 52 | PrecisionUtil.divide(BigDecimal.TEN, new BigDecimal("2"), 0)); 53 | } 54 | 55 | @Test 56 | public void testStringAdd() { 57 | Assert.assertEquals("1", PrecisionUtil.add("1", null)); 58 | Assert.assertEquals("2", PrecisionUtil.add(null, "2")); 59 | Assert.assertEquals("5", PrecisionUtil.add("3", "2")); 60 | } 61 | 62 | @Test 63 | public void testStringSubtract() { 64 | Assert.assertEquals("1", PrecisionUtil.subtract("1", null)); 65 | Assert.assertEquals("-2", PrecisionUtil.subtract(null, "2")); 66 | Assert.assertEquals("3", PrecisionUtil.subtract("5", "2")); 67 | } 68 | 69 | @Test 70 | public void testStringMultiply() { 71 | Assert.assertEquals("1", PrecisionUtil.multiply("1", null)); 72 | Assert.assertEquals("2", PrecisionUtil.multiply(null, "2")); 73 | Assert.assertEquals("6", PrecisionUtil.multiply("3", "2")); 74 | } 75 | 76 | @Test 77 | public void testStringDivide() { 78 | Assert.assertEquals("1.00", PrecisionUtil.divide("1", null)); 79 | Assert.assertEquals("0", PrecisionUtil.divide(null, "2")); 80 | Assert.assertEquals("2.00", PrecisionUtil.divide("6", "3")); 81 | } 82 | 83 | @Test 84 | public void testBigDecimalSum() { 85 | Assert.assertEquals(new BigDecimal("11"), PrecisionUtil.sum(null, 86 | BigDecimal.ZERO, BigDecimal.ONE, BigDecimal.TEN)); 87 | Assert.assertEquals(BigDecimal.TEN, 88 | PrecisionUtil.sum(BigDecimal.TEN, null)); 89 | Assert.assertEquals(new BigDecimal("14"), 90 | PrecisionUtil.sum(BigDecimal.TEN, new BigDecimal("4"))); 91 | } 92 | 93 | @Test 94 | public void testStringSum() { 95 | Assert.assertEquals("1", PrecisionUtil.sum("1", null)); 96 | Assert.assertEquals("6", PrecisionUtil.sum(null, "1", "2", "3")); 97 | Assert.assertEquals("8", PrecisionUtil.sum("2", "1", "2", "3")); 98 | } 99 | 100 | @Test 101 | public void testBigDecimalAvg() { 102 | Assert.assertEquals(new BigDecimal("3.67"), PrecisionUtil.avg( 103 | BigDecimal.ZERO, BigDecimal.ONE, BigDecimal.TEN)); 104 | } 105 | 106 | @Test 107 | public void testStringAvg() { 108 | Assert.assertEquals("2.00", PrecisionUtil.avg("1", "2", "3")); 109 | } 110 | 111 | @Test 112 | public void testBigDecimalMaxArr() { 113 | Assert.assertEquals(BigDecimal.TEN, PrecisionUtil.maxArr( 114 | BigDecimal.ZERO, BigDecimal.TEN, BigDecimal.ONE)); 115 | } 116 | 117 | @Test 118 | public void testBigDecimalMinArr() { 119 | Assert.assertEquals(BigDecimal.ZERO, PrecisionUtil.minArr( 120 | BigDecimal.ONE, BigDecimal.ZERO, BigDecimal.TEN)); 121 | } 122 | 123 | // 124 | @Test 125 | public void testStringMaxArr() { 126 | Assert.assertNull(PrecisionUtil.max(null, "1", "2", "3")); 127 | Assert.assertEquals("4", PrecisionUtil.maxArr("2", "4", "1", "3")); 128 | Assert.assertEquals("2", PrecisionUtil.maxArr("2", "", "", "")); 129 | Assert.assertEquals("2", PrecisionUtil.maxArr("2", null, null, null)); 130 | } 131 | 132 | @Test 133 | public void testStringMinArr() { 134 | Assert.assertNull(PrecisionUtil.minArr(null, "1", "2", "3")); 135 | Assert.assertEquals("1", PrecisionUtil.minArr("2", "4", "1", "3")); 136 | Assert.assertEquals("2", PrecisionUtil.minArr("2", "", "", "")); 137 | Assert.assertEquals("2", PrecisionUtil.minArr("2", null, null, null)); 138 | } 139 | 140 | @Test 141 | public void testDoubleRound() { 142 | Assert.assertEquals(3.0, PrecisionUtil.round(2.567, 0), 0); 143 | Assert.assertEquals(2.6, PrecisionUtil.round(2.567, 1), 0); 144 | Assert.assertEquals(2.57, PrecisionUtil.round(2.567, 2), 0); 145 | Assert.assertEquals(2.567, PrecisionUtil.round(2.567, 3), 0); 146 | Assert.assertEquals(2.567, PrecisionUtil.round(2.567, 4), 0); 147 | } 148 | 149 | @Test 150 | public void testBigDecimalRound() { 151 | Assert.assertEquals(new BigDecimal("3"), 152 | PrecisionUtil.round(new BigDecimal("2.567"), 0)); 153 | Assert.assertEquals(new BigDecimal("2.6"), 154 | PrecisionUtil.round(new BigDecimal("2.567"), 1)); 155 | Assert.assertEquals(new BigDecimal("2.57"), 156 | PrecisionUtil.round(new BigDecimal("2.567"), 2)); 157 | Assert.assertEquals(new BigDecimal("2.567"), 158 | PrecisionUtil.round(new BigDecimal("2.567"), 3)); 159 | Assert.assertEquals(new BigDecimal("2.5670"), 160 | PrecisionUtil.round(new BigDecimal("2.567"), 4)); 161 | } 162 | } 163 | -------------------------------------------------------------------------------- /jframework-fundation/src/main/java/com/github/neatlife/jframework/fundation/util/ZipUtil.java: -------------------------------------------------------------------------------- 1 | package com.github.neatlife.jframework.fundation.util; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import org.apache.commons.lang3.StringUtils; 5 | import org.springframework.util.ObjectUtils; 6 | 7 | import java.io.*; 8 | import java.util.Enumeration; 9 | import java.util.zip.*; 10 | 11 | /** 12 | * @author suxiaolin 13 | * @date 2019-03-30 17:27 14 | */ 15 | @Slf4j 16 | public class ZipUtil { 17 | 18 | private ZipUtil() { 19 | } 20 | 21 | /** 22 | * 递归压缩文件夹 23 | * 24 | * @param srcRootDir 压缩文件夹根目录的子路径 25 | * @param file 当前递归压缩的文件或目录对象 26 | * @param zos 压缩文件存储对象 27 | * @throws \Exception 28 | */ 29 | private static void zip(String srcRootDir, File file, ZipOutputStream zos) throws Exception { 30 | if (file == null) { 31 | return; 32 | } 33 | 34 | // 如果是文件,则直接压缩该文件 35 | if (file.isFile()) { 36 | int count; 37 | int bufferLen = 1024; 38 | byte[] data = new byte[bufferLen]; 39 | 40 | // 获取文件相对于压缩文件夹根目录的子路径 41 | String subPath = file.getAbsolutePath(); 42 | int index = subPath.indexOf(srcRootDir); 43 | if (index != -1) { 44 | subPath = subPath.substring(srcRootDir.length() + File.separator.length()); 45 | } 46 | ZipEntry entry = new ZipEntry(subPath); 47 | zos.putNextEntry(entry); 48 | try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file))) { 49 | while ((count = bis.read(data, 0, bufferLen)) != -1) { 50 | zos.write(data, 0, count); 51 | } 52 | } 53 | zos.closeEntry(); 54 | } 55 | // 如果是目录,则压缩整个目录 56 | else { 57 | // 压缩目录中的文件或子目录 58 | File[] childFileList = file.listFiles(); 59 | if (!ObjectUtils.isEmpty(childFileList)) { 60 | for (File childFile : childFileList) { 61 | zip(srcRootDir, childFile, zos); 62 | } 63 | } 64 | } 65 | } 66 | 67 | /** 68 | * 对文件或文件目录进行压缩 69 | * 70 | * @param srcPath 要压缩的源文件路径。如果压缩一个文件,则为该文件的全路径;如果压缩一个目录,则为该目录的顶层目录路径 71 | * @param zipPath 压缩文件保存的路径。注意:zipPath不能是srcPath路径下的子文件夹 72 | * @param zipFileName 压缩文件名 73 | * @throws \Exception 74 | */ 75 | public static void zip(String srcPath, String zipPath, String zipFileName) throws Exception { 76 | srcPath = new File(srcPath).getAbsolutePath(); 77 | zipPath = new File(zipPath).getAbsolutePath(); 78 | 79 | ZipOutputStream zos = null; 80 | CheckedOutputStream cos = null; 81 | FileOutputStream fos = null; 82 | try { 83 | File srcFile = new File(srcPath); 84 | 85 | // 判断压缩文件保存的路径是否为源文件路径的子文件夹,如果是,则抛出异常(防止无限递归压缩的发生) 86 | if (srcFile.isDirectory() && zipPath.contains(srcPath)) { 87 | throw new Exception("压缩文件保存的路径是否为源文件路径的子文件夹"); 88 | } 89 | 90 | // 判断压缩文件保存的路径是否存在,如果不存在,则创建目录 91 | File zipDir = new File(zipPath); 92 | if (!zipDir.exists() || !zipDir.isDirectory()) { 93 | zipDir.mkdirs(); 94 | } 95 | 96 | // 创建压缩文件保存的文件对象 97 | String zipFilePath = zipPath + File.separator + zipFileName; 98 | File zipFile = new File(zipFilePath); 99 | 100 | fos = new FileOutputStream(zipFile); 101 | cos = new CheckedOutputStream(fos, new CRC32()); 102 | zos = new ZipOutputStream(cos); 103 | 104 | // 如果只是压缩一个文件,则需要截取该文件的父目录 105 | String srcRootDir = srcPath; 106 | if (srcFile.isFile()) { 107 | int index = srcPath.lastIndexOf(File.separator); 108 | if (index != -1) { 109 | srcRootDir = srcPath.substring(0, index); 110 | } 111 | } 112 | // 调用递归压缩方法进行目录或文件压缩 113 | zip(srcRootDir, srcFile, zos); 114 | zos.flush(); 115 | } finally { 116 | try { 117 | if (zos != null) { 118 | zos.close(); 119 | } 120 | if (cos != null) { 121 | cos.close(); 122 | } 123 | if (fos != null) { 124 | fos.close(); 125 | } 126 | } catch (Exception e) { 127 | log.error("e message: {}", e.getMessage()); 128 | } 129 | } 130 | } 131 | 132 | /** 133 | * 解压缩zip包 134 | * 135 | * @param zipFilePath zip文件的全路径 136 | * @param unzipFilePath 解压后的文件保存的路径 137 | * @param includeZipFileName 解压后的文件保存的路径是否包含压缩文件的文件名。true-包含;false-不包含 138 | */ 139 | @SuppressWarnings({"unchecked", "resource"}) 140 | public static void unzip(String zipFilePath, String unzipFilePath, boolean includeZipFileName) throws Exception { 141 | File zipFile = new File(zipFilePath); 142 | // 如果解压后的文件保存路径包含压缩文件的文件名,则追加该文件名到解压路径 143 | if (includeZipFileName) { 144 | String fileName = zipFile.getName(); 145 | if (StringUtils.isNotEmpty(fileName)) { 146 | fileName = fileName.substring(0, fileName.lastIndexOf('.')); 147 | } 148 | unzipFilePath = unzipFilePath + File.separator + fileName; 149 | } 150 | // 创建解压缩文件保存的路径 151 | File unzipFileDir = new File(unzipFilePath); 152 | if (!unzipFileDir.exists() || !unzipFileDir.isDirectory()) { 153 | unzipFileDir.mkdirs(); 154 | } 155 | 156 | // 开始解压 157 | ZipEntry entry; 158 | String entryFilePath; 159 | String entryDirPath; 160 | File entryFile; 161 | File entryDir; 162 | int index = 0; 163 | int count = 0; 164 | int bufferSize = 1024; 165 | byte[] buffer = new byte[bufferSize]; 166 | BufferedInputStream bis; 167 | try (ZipFile zip = new ZipFile(zipFile)) { 168 | Enumeration entries = (Enumeration) zip.entries(); 169 | // 循环对压缩包里的每一个文件进行解压 170 | while (entries.hasMoreElements()) { 171 | entry = entries.nextElement(); 172 | // 构建压缩包中一个文件解压后保存的文件全路径 173 | entryFilePath = unzipFilePath + File.separator + entry.getName(); 174 | // 构建解压后保存的文件夹路径 175 | index = entryFilePath.lastIndexOf(File.separator); 176 | if (index != -1) { 177 | entryDirPath = entryFilePath.substring(0, index); 178 | } else { 179 | entryDirPath = ""; 180 | } 181 | entryDir = new File(entryDirPath); 182 | // 如果文件夹路径不存在,则创建文件夹 183 | if (!entryDir.exists() || !entryDir.isDirectory()) { 184 | entryDir.mkdirs(); 185 | } 186 | 187 | // 创建解压文件 188 | entryFile = new File(entryFilePath); 189 | 190 | // 写入文件 191 | try (BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(entryFile))) { 192 | bis = new BufferedInputStream(zip.getInputStream(entry)); 193 | while ((count = bis.read(buffer, 0, bufferSize)) != -1) { 194 | bos.write(buffer, 0, count); 195 | } 196 | bos.flush(); 197 | } 198 | } 199 | } 200 | } 201 | 202 | } 203 | -------------------------------------------------------------------------------- /jframework-springfox/src/main/java/com/github/neatlife/jframework/springfox/plugin/RequestToPoOperationBuilder.java: -------------------------------------------------------------------------------- 1 | package com.github.neatlife.jframework.springfox.plugin; 2 | 3 | import com.fasterxml.classmate.ResolvedType; 4 | import com.fasterxml.classmate.TypeResolver; 5 | import com.fasterxml.classmate.members.RawField; 6 | import com.github.neatlife.jframework.fundation.request.RequestToPo; 7 | import com.github.neatlife.jframework.fundation.util.CaseUtil; 8 | import com.google.common.collect.Lists; 9 | import io.swagger.annotations.ApiModelProperty; 10 | import lombok.extern.slf4j.Slf4j; 11 | import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; 12 | import org.springframework.core.Ordered; 13 | import org.springframework.core.annotation.Order; 14 | import org.springframework.stereotype.Component; 15 | import org.springframework.util.CollectionUtils; 16 | import org.springframework.util.ObjectUtils; 17 | import springfox.documentation.builders.OperationBuilder; 18 | import springfox.documentation.builders.ParameterBuilder; 19 | import springfox.documentation.schema.ModelRef; 20 | import springfox.documentation.service.Parameter; 21 | import springfox.documentation.service.ResolvedMethodParameter; 22 | import springfox.documentation.spi.DocumentationType; 23 | import springfox.documentation.spi.service.OperationBuilderPlugin; 24 | import springfox.documentation.spi.service.contexts.OperationContext; 25 | import springfox.documentation.spring.web.plugins.Docket; 26 | 27 | import javax.validation.constraints.NotNull; 28 | import java.lang.annotation.Annotation; 29 | import java.lang.reflect.Field; 30 | import java.lang.reflect.ParameterizedType; 31 | import java.util.ArrayList; 32 | import java.util.List; 33 | import java.util.Set; 34 | import java.util.function.Predicate; 35 | import java.util.stream.Collectors; 36 | 37 | import static springfox.documentation.schema.Types.typeNameFor; 38 | 39 | /** 40 | * @author suxiaolin 41 | * @date 2019-04-01 19:55 42 | */ 43 | @Component 44 | @Order(Ordered.HIGHEST_PRECEDENCE + 100) 45 | @ConditionalOnBean(Docket.class) 46 | @Slf4j 47 | class RequestToPoOperationBuilder implements OperationBuilderPlugin { 48 | private static List addFieldsToDoc(Field classField, String prefix, Class parentClass) { 49 | Class itemClass = classField.getType(); 50 | if (List.class.isAssignableFrom(classField.getType())) { 51 | ParameterizedType listType = (ParameterizedType) classField.getGenericType(); 52 | itemClass = (Class) listType.getActualTypeArguments()[0]; 53 | } 54 | List requestMapParameters = Lists.newArrayList(); 55 | for (Field field : itemClass.getDeclaredFields()) { 56 | String itemTypeName = typeNameFor(field.getType()); 57 | if (!ObjectUtils.isEmpty(itemTypeName)) { 58 | requestMapParameters.add(genPrimaryTypeDoc(field, prefix, itemTypeName)); 59 | } else { 60 | if (field.getClass().equals(parentClass)) { 61 | requestMapParameters.add(genPrimaryTypeDoc(field, prefix, "String")); 62 | } else { 63 | // 解析出这个类型的所有成员加到文档里 64 | String subPrefix = ObjectUtils.isEmpty(prefix) ? prefix : prefix + "."; 65 | requestMapParameters.addAll(addFieldsToDoc(field, subPrefix + CaseUtil.snakeCase(field.getName()), classField.getClass())); 66 | } 67 | } 68 | } 69 | return requestMapParameters; 70 | } 71 | 72 | private static Parameter genPrimaryTypeDoc(Field field, String prefix, String itemTypeName) { 73 | ApiModelProperty apiModelProperty = field.getAnnotation(ApiModelProperty.class); 74 | String description = ObjectUtils.isEmpty(apiModelProperty) ? field.getName() : apiModelProperty.value(); 75 | String name = CaseUtil.snakeCase(field.getName()); 76 | boolean required = false; 77 | NotNull notNull = field.getAnnotation(NotNull.class); 78 | if (notNull != null) { 79 | required = true; 80 | } 81 | return new ParameterBuilder() 82 | .description(description) 83 | .type(new TypeResolver().resolve(field.getClass())) 84 | .name(ObjectUtils.isEmpty(prefix) ? name : prefix + "." + name) 85 | .parameterType("query") 86 | .parameterAccess("access") 87 | .required(required) 88 | .modelRef(new ModelRef(itemTypeName)) 89 | .build(); 90 | } 91 | 92 | @Override 93 | public void apply(OperationContext context) { 94 | context.operationBuilder().parameters(readParameters(context)); 95 | } 96 | 97 | @Override 98 | public boolean supports(DocumentationType delimiter) { 99 | return true; 100 | } 101 | 102 | private List readParameters(final OperationContext context) { 103 | 104 | List methodParameters = context.getParameters(); 105 | final List parameterList = new ArrayList<>(); 106 | 107 | for (ResolvedMethodParameter methodParameter : methodParameters) { 108 | ResolvedType alternate = context.alternateFor(methodParameter.getParameterType()); 109 | 110 | if (!shouldIgnore(methodParameter, alternate, context.getIgnorableParameterTypes()) && isRequestFormToPojo(methodParameter)) { 111 | readRequestFormToPojo(context, methodParameter); 112 | } 113 | } 114 | return parameterList.stream().filter(((Predicate) Parameter::isHidden).negate()).collect(Collectors.toList()); 115 | } 116 | 117 | private void readRequestFormToPojo(OperationContext context, ResolvedMethodParameter methodParameter) { 118 | 119 | List fields = methodParameter.getParameterType().getMemberFields(); 120 | List parentRawFields = methodParameter.getParameterType().getParentClass().getMemberFields(); 121 | 122 | List fieldList = Lists.newArrayList(); 123 | if (!CollectionUtils.isEmpty(fields)) { 124 | fieldList.addAll(fields); 125 | } 126 | if (!CollectionUtils.isEmpty(parentRawFields)) { 127 | fieldList.addAll(parentRawFields); 128 | } 129 | List requestMapParameters = Lists.newArrayList(); 130 | for (RawField rawField : fieldList) { 131 | String name = CaseUtil.snakeCase(rawField.getName()); 132 | Class itemClass = rawField.getRawMember().getType(); 133 | if (itemClass != null) { 134 | String itemTypeName = typeNameFor(itemClass); 135 | if (ObjectUtils.isEmpty(itemTypeName)) { 136 | // 解析出这个类型的所有成员加到文档里 137 | requestMapParameters.addAll(addFieldsToDoc(rawField.getRawMember(), name, null)); 138 | } else { 139 | requestMapParameters.add(genPrimaryTypeDoc(rawField.getRawMember(), "", itemTypeName)); 140 | } 141 | } 142 | } 143 | 144 | try { 145 | OperationBuilder operationBuilder = context.operationBuilder(); 146 | Field parametersField = context.operationBuilder().getClass().getDeclaredField("parameters"); 147 | parametersField.setAccessible(true); 148 | parametersField.set(operationBuilder, requestMapParameters); 149 | } catch (Exception e) { 150 | log.debug("swagger RequestToPo error", e); 151 | } 152 | } 153 | 154 | private boolean shouldIgnore( 155 | final ResolvedMethodParameter parameter, 156 | ResolvedType resolvedParameterType, 157 | final Set ignorableParamTypes) { 158 | 159 | if (ignorableParamTypes.contains(resolvedParameterType.getErasedType())) { 160 | return true; 161 | } 162 | return ignorableParamTypes.stream() 163 | .filter(Annotation.class::isAssignableFrom) 164 | .anyMatch(parameter::hasParameterAnnotation); 165 | } 166 | 167 | private boolean isRequestFormToPojo(final ResolvedMethodParameter parameter) { 168 | return parameter.hasParameterAnnotation(RequestToPo.class); 169 | } 170 | } 171 | 172 | 173 | 174 | --------------------------------------------------------------------------------