Administrator 3 éve
commit
ffb2d4606e
78 módosított fájl, 6049 hozzáadás és 0 törlés
  1. 2 0
      .gitignore
  2. 155 0
      fast-boot.iml
  3. 229 0
      pom.xml
  4. 29 0
      src/main/java/com/xiesx/fastboot/SpringContextAware.java
  5. 84 0
      src/main/java/com/xiesx/fastboot/SpringHelper.java
  6. 49 0
      src/main/java/com/xiesx/fastboot/SpringStartup.java
  7. 17 0
      src/main/java/com/xiesx/fastboot/base/AbstractState.java
  8. 65 0
      src/main/java/com/xiesx/fastboot/base/pagination/PaginationHelper.java
  9. 37 0
      src/main/java/com/xiesx/fastboot/base/pagination/PaginationResult.java
  10. 23 0
      src/main/java/com/xiesx/fastboot/base/pagination/PaginationVo.java
  11. 80 0
      src/main/java/com/xiesx/fastboot/base/result/BaseResult.java
  12. 144 0
      src/main/java/com/xiesx/fastboot/base/result/R.java
  13. 59 0
      src/main/java/com/xiesx/fastboot/core/body/GlobalBodyAdvice.java
  14. 17 0
      src/main/java/com/xiesx/fastboot/core/body/annotation/GoEnableBody.java
  15. 20 0
      src/main/java/com/xiesx/fastboot/core/body/annotation/GoHeader.java
  16. 38 0
      src/main/java/com/xiesx/fastboot/core/body/cfg/HeaderCfg.java
  17. 31 0
      src/main/java/com/xiesx/fastboot/core/body/handle/CurrentHeader.java
  18. 63 0
      src/main/java/com/xiesx/fastboot/core/body/handle/HeaderArgumentResolver.java
  19. 5 0
      src/main/java/com/xiesx/fastboot/core/exception/.html
  20. 133 0
      src/main/java/com/xiesx/fastboot/core/exception/GlobalExceptionAdvice.java
  21. 51 0
      src/main/java/com/xiesx/fastboot/core/exception/RunExc.java
  22. 76 0
      src/main/java/com/xiesx/fastboot/core/exception/RunException.java
  23. 16 0
      src/main/java/com/xiesx/fastboot/core/exception/annotation/GoEnableException.java
  24. 16 0
      src/main/java/com/xiesx/fastboot/core/fastjson/annotation/GoEnableFastJson.java
  25. 82 0
      src/main/java/com/xiesx/fastboot/core/fastjson/cfg/FastJsonCfg.java
  26. 17 0
      src/main/java/com/xiesx/fastboot/core/fastjson/cfg/FastJsonProperties.java
  27. 49 0
      src/main/java/com/xiesx/fastboot/core/fastjson/desensitized/SensitiveTypeEnum.java
  28. 26 0
      src/main/java/com/xiesx/fastboot/core/fastjson/serializer/CustomerBigDecimalCodec.java
  29. 46 0
      src/main/java/com/xiesx/fastboot/core/jpa/JpaPlusRepository.java
  30. 179 0
      src/main/java/com/xiesx/fastboot/core/jpa/JpaPlusRepositoryExecutor.java
  31. 18 0
      src/main/java/com/xiesx/fastboot/core/jpa/LongSupplierImpl.java
  32. 25 0
      src/main/java/com/xiesx/fastboot/core/jpa/annotation/EnableJpaPlusRepositories.java
  33. 24 0
      src/main/java/com/xiesx/fastboot/core/jpa/cfg/JpaPlusCfg.java
  34. 15 0
      src/main/java/com/xiesx/fastboot/core/jpa/entity/JpaPlusEntity.java
  35. 27 0
      src/main/java/com/xiesx/fastboot/core/jpa/factory/JpaPlusRepositoryFactory.java
  36. 19 0
      src/main/java/com/xiesx/fastboot/core/jpa/factory/JpaPlusRepositoryFactoryBean.java
  37. 128 0
      src/main/java/com/xiesx/fastboot/core/jpa/identifier/IdWorker.java
  38. 31 0
      src/main/java/com/xiesx/fastboot/core/jpa/identifier/IdWorkerGenerator.java
  39. 467 0
      src/main/java/com/xiesx/fastboot/core/scheduler/ScheduleHelper.java
  40. 45 0
      src/main/java/com/xiesx/fastboot/core/scheduler/decorator/BaseDecorator.java
  41. 20 0
      src/main/java/com/xiesx/fastboot/core/scheduler/decorator/ISchedule.java
  42. 144 0
      src/main/java/com/xiesx/fastboot/core/sign/SignalAspect.java
  43. 16 0
      src/main/java/com/xiesx/fastboot/core/sign/annotation/GoEnableSign.java
  44. 19 0
      src/main/java/com/xiesx/fastboot/core/sign/annotation/GoSign.java
  45. 14 0
      src/main/java/com/xiesx/fastboot/core/sign/cfg/SignalCfg.java
  46. 16 0
      src/main/java/com/xiesx/fastboot/core/sign/cfg/SignalProperties.java
  47. 4 0
      src/main/java/com/xiesx/fastboot/core/sign/package-info.java
  48. 5 0
      src/main/java/com/xiesx/fastboot/core/sign/package.html
  49. 165 0
      src/main/java/com/xiesx/fastboot/core/token/JwtHelper.java
  50. 16 0
      src/main/java/com/xiesx/fastboot/core/token/annotation/GoEnableToken.java
  51. 18 0
      src/main/java/com/xiesx/fastboot/core/token/annotation/GoToken.java
  52. 56 0
      src/main/java/com/xiesx/fastboot/core/token/cfg/TokenCfg.java
  53. 18 0
      src/main/java/com/xiesx/fastboot/core/token/cfg/TokenProperties.java
  54. 35 0
      src/main/java/com/xiesx/fastboot/core/token/handle/CurrentToken.java
  55. 48 0
      src/main/java/com/xiesx/fastboot/core/token/handle/TokenArgumentResolver.java
  56. 137 0
      src/main/java/com/xiesx/fastboot/core/token/handle/TokenInterceptorHandler.java
  57. 81 0
      src/main/java/com/xiesx/fastboot/tag/TagUtils.java
  58. 84 0
      src/main/java/com/xiesx/fastboot/tag/body/CssTag.java
  59. 74 0
      src/main/java/com/xiesx/fastboot/tag/body/MetaTag.java
  60. 63 0
      src/main/java/com/xiesx/fastboot/tag/body/PropertyTag.java
  61. 91 0
      src/main/java/com/xiesx/fastboot/tag/body/ScriptTag.java
  62. 152 0
      src/main/java/com/xiesx/fastboot/tag/ui/BaseUITag.java
  63. 94 0
      src/main/java/com/xiesx/fastboot/tag/ui/LayoutTag.java
  64. 70 0
      src/main/java/com/xiesx/fastboot/tag/ui/UITag.java
  65. 81 0
      src/main/java/com/xiesx/fastboot/utils/AppUtils.java
  66. 102 0
      src/main/java/com/xiesx/fastboot/utils/ArrayUtils.java
  67. 49 0
      src/main/java/com/xiesx/fastboot/utils/CopyUtils.java
  68. 609 0
      src/main/java/com/xiesx/fastboot/utils/DateUtils.java
  69. 139 0
      src/main/java/com/xiesx/fastboot/utils/DesensitizedUtils.java
  70. 341 0
      src/main/java/com/xiesx/fastboot/utils/EncryptUtil.java
  71. 234 0
      src/main/java/com/xiesx/fastboot/utils/RuntimeUtils.java
  72. 91 0
      src/main/java/com/xiesx/fastboot/utils/ValidatorHelper.java
  73. 156 0
      src/main/resources/META-INF/core.tld
  74. 71 0
      src/main/resources/META-INF/spring-configuration-metadata.json
  75. 2 0
      src/main/resources/META-INF/spring.factories
  76. 25 0
      src/main/resources/application.yml
  77. 6 0
      src/main/resources/banner.txt
  78. 66 0
      src/main/resources/log4j2.xml

+ 2 - 0
.gitignore

@@ -0,0 +1,2 @@
+/.idea
+/target

+ 155 - 0
fast-boot.iml

@@ -0,0 +1,155 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module org.jetbrains.idea.maven.project.MavenProjectsManager.isMavenModule="true" type="JAVA_MODULE" version="4">
+  <component name="FacetManager">
+    <facet type="jpa" name="JPA">
+      <configuration>
+        <setting name="validation-enabled" value="true" />
+        <setting name="provider-name" value="Hibernate" />
+        <datasource-mapping>
+          <factory-entry name="fast-boot" />
+        </datasource-mapping>
+        <naming-strategy-map />
+      </configuration>
+    </facet>
+    <facet type="Spring" name="Spring">
+      <configuration />
+    </facet>
+    <facet type="web" name="Web">
+      <configuration>
+        <webroots />
+      </configuration>
+    </facet>
+  </component>
+  <component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_8">
+    <output url="file://$MODULE_DIR$/target/classes" />
+    <output-test url="file://$MODULE_DIR$/target/test-classes" />
+    <content url="file://$MODULE_DIR$">
+      <sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" />
+      <sourceFolder url="file://$MODULE_DIR$/src/main/resources" type="java-resource" />
+      <excludeFolder url="file://$MODULE_DIR$/target" />
+    </content>
+    <orderEntry type="inheritedJdk" />
+    <orderEntry type="sourceFolder" forTests="false" />
+    <orderEntry type="library" name="Maven: org.projectlombok:lombok:1.18.16" level="project" />
+    <orderEntry type="library" scope="PROVIDED" name="Maven: org.springframework.boot:spring-boot-starter-web:2.4.1" level="project" />
+    <orderEntry type="library" scope="PROVIDED" name="Maven: org.springframework.boot:spring-boot-starter:2.4.1" level="project" />
+    <orderEntry type="library" scope="PROVIDED" name="Maven: org.springframework.boot:spring-boot:2.4.1" level="project" />
+    <orderEntry type="library" scope="PROVIDED" name="Maven: org.springframework.boot:spring-boot-autoconfigure:2.4.1" level="project" />
+    <orderEntry type="library" scope="PROVIDED" name="Maven: jakarta.annotation:jakarta.annotation-api:1.3.5" level="project" />
+    <orderEntry type="library" scope="PROVIDED" name="Maven: org.yaml:snakeyaml:1.27" level="project" />
+    <orderEntry type="library" scope="PROVIDED" name="Maven: org.springframework.boot:spring-boot-starter-json:2.4.1" level="project" />
+    <orderEntry type="library" scope="PROVIDED" name="Maven: com.fasterxml.jackson.datatype:jackson-datatype-jdk8:2.11.3" level="project" />
+    <orderEntry type="library" scope="PROVIDED" name="Maven: com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.11.3" level="project" />
+    <orderEntry type="library" scope="PROVIDED" name="Maven: com.fasterxml.jackson.module:jackson-module-parameter-names:2.11.3" level="project" />
+    <orderEntry type="library" scope="PROVIDED" name="Maven: org.springframework.boot:spring-boot-starter-tomcat:2.4.1" level="project" />
+    <orderEntry type="library" scope="PROVIDED" name="Maven: org.apache.tomcat.embed:tomcat-embed-core:9.0.41" level="project" />
+    <orderEntry type="library" scope="PROVIDED" name="Maven: org.glassfish:jakarta.el:3.0.3" level="project" />
+    <orderEntry type="library" scope="PROVIDED" name="Maven: org.apache.tomcat.embed:tomcat-embed-websocket:9.0.41" level="project" />
+    <orderEntry type="library" scope="PROVIDED" name="Maven: org.springframework:spring-web:5.3.2" level="project" />
+    <orderEntry type="library" scope="PROVIDED" name="Maven: org.springframework:spring-beans:5.3.2" level="project" />
+    <orderEntry type="library" scope="PROVIDED" name="Maven: org.springframework:spring-webmvc:5.3.2" level="project" />
+    <orderEntry type="library" scope="PROVIDED" name="Maven: org.springframework:spring-aop:5.3.2" level="project" />
+    <orderEntry type="library" scope="PROVIDED" name="Maven: org.springframework:spring-context:5.3.2" level="project" />
+    <orderEntry type="library" scope="PROVIDED" name="Maven: org.springframework:spring-expression:5.3.2" level="project" />
+    <orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-configuration-processor:2.4.1" level="project" />
+    <orderEntry type="library" scope="PROVIDED" name="Maven: org.springframework.boot:spring-boot-starter-data-jdbc:2.4.1" level="project" />
+    <orderEntry type="library" scope="PROVIDED" name="Maven: org.springframework.boot:spring-boot-starter-jdbc:2.4.1" level="project" />
+    <orderEntry type="library" scope="PROVIDED" name="Maven: com.zaxxer:HikariCP:3.4.5" level="project" />
+    <orderEntry type="library" scope="PROVIDED" name="Maven: org.springframework:spring-jdbc:5.3.2" level="project" />
+    <orderEntry type="library" scope="PROVIDED" name="Maven: org.springframework.data:spring-data-jdbc:2.1.2" level="project" />
+    <orderEntry type="library" scope="PROVIDED" name="Maven: org.springframework.data:spring-data-relational:2.1.2" level="project" />
+    <orderEntry type="library" scope="PROVIDED" name="Maven: org.springframework.data:spring-data-commons:2.4.2" level="project" />
+    <orderEntry type="library" scope="PROVIDED" name="Maven: org.springframework.boot:spring-boot-starter-data-jpa:2.4.1" level="project" />
+    <orderEntry type="library" scope="PROVIDED" name="Maven: org.springframework.boot:spring-boot-starter-aop:2.4.1" level="project" />
+    <orderEntry type="library" scope="PROVIDED" name="Maven: org.aspectj:aspectjweaver:1.9.6" level="project" />
+    <orderEntry type="library" scope="PROVIDED" name="Maven: jakarta.transaction:jakarta.transaction-api:1.3.3" level="project" />
+    <orderEntry type="library" scope="PROVIDED" name="Maven: jakarta.persistence:jakarta.persistence-api:2.2.3" level="project" />
+    <orderEntry type="library" scope="PROVIDED" name="Maven: org.hibernate:hibernate-core:5.4.25.Final" level="project" />
+    <orderEntry type="library" scope="PROVIDED" name="Maven: org.javassist:javassist:3.27.0-GA" level="project" />
+    <orderEntry type="library" scope="PROVIDED" name="Maven: net.bytebuddy:byte-buddy:1.10.18" level="project" />
+    <orderEntry type="library" scope="PROVIDED" name="Maven: antlr:antlr:2.7.7" level="project" />
+    <orderEntry type="library" scope="PROVIDED" name="Maven: org.jboss:jandex:2.1.3.Final" level="project" />
+    <orderEntry type="library" scope="PROVIDED" name="Maven: org.dom4j:dom4j:2.1.3" level="project" />
+    <orderEntry type="library" scope="PROVIDED" name="Maven: org.hibernate.common:hibernate-commons-annotations:5.1.2.Final" level="project" />
+    <orderEntry type="library" scope="PROVIDED" name="Maven: org.glassfish.jaxb:jaxb-runtime:2.3.3" level="project" />
+    <orderEntry type="library" scope="PROVIDED" name="Maven: org.glassfish.jaxb:txw2:2.3.3" level="project" />
+    <orderEntry type="library" scope="PROVIDED" name="Maven: com.sun.istack:istack-commons-runtime:3.0.11" level="project" />
+    <orderEntry type="library" scope="PROVIDED" name="Maven: com.sun.activation:jakarta.activation:1.2.2" level="project" />
+    <orderEntry type="library" scope="PROVIDED" name="Maven: org.springframework.data:spring-data-jpa:2.4.2" level="project" />
+    <orderEntry type="library" scope="PROVIDED" name="Maven: org.springframework:spring-orm:5.3.2" level="project" />
+    <orderEntry type="library" scope="PROVIDED" name="Maven: org.springframework:spring-aspects:5.3.2" level="project" />
+    <orderEntry type="library" scope="PROVIDED" name="Maven: org.springframework.boot:spring-boot-starter-quartz:2.4.1" level="project" />
+    <orderEntry type="library" scope="PROVIDED" name="Maven: org.springframework:spring-context-support:5.3.2" level="project" />
+    <orderEntry type="library" scope="PROVIDED" name="Maven: org.springframework:spring-tx:5.3.2" level="project" />
+    <orderEntry type="library" scope="PROVIDED" name="Maven: org.quartz-scheduler:quartz:2.3.2" level="project" />
+    <orderEntry type="library" scope="PROVIDED" name="Maven: com.mchange:mchange-commons-java:0.2.15" level="project" />
+    <orderEntry type="library" scope="PROVIDED" name="Maven: org.springframework.boot:spring-boot-starter-log4j2:2.4.1" level="project" />
+    <orderEntry type="library" scope="PROVIDED" name="Maven: org.apache.logging.log4j:log4j-slf4j-impl:2.13.3" level="project" />
+    <orderEntry type="library" scope="PROVIDED" name="Maven: org.apache.logging.log4j:log4j-api:2.13.3" level="project" />
+    <orderEntry type="library" scope="PROVIDED" name="Maven: org.apache.logging.log4j:log4j-core:2.13.3" level="project" />
+    <orderEntry type="library" scope="PROVIDED" name="Maven: org.apache.logging.log4j:log4j-jul:2.13.3" level="project" />
+    <orderEntry type="library" scope="PROVIDED" name="Maven: org.slf4j:jul-to-slf4j:1.7.30" level="project" />
+    <orderEntry type="library" scope="TEST" name="Maven: org.springframework.boot:spring-boot-starter-test:2.4.1" level="project" />
+    <orderEntry type="library" scope="TEST" name="Maven: org.springframework.boot:spring-boot-test:2.4.1" level="project" />
+    <orderEntry type="library" scope="TEST" name="Maven: org.springframework.boot:spring-boot-test-autoconfigure:2.4.1" level="project" />
+    <orderEntry type="library" scope="TEST" name="Maven: com.jayway.jsonpath:json-path:2.4.0" level="project" />
+    <orderEntry type="library" scope="TEST" name="Maven: net.minidev:json-smart:2.3" level="project" />
+    <orderEntry type="library" scope="TEST" name="Maven: net.minidev:accessors-smart:1.2" level="project" />
+    <orderEntry type="library" scope="TEST" name="Maven: org.ow2.asm:asm:5.0.4" level="project" />
+    <orderEntry type="library" scope="PROVIDED" name="Maven: jakarta.xml.bind:jakarta.xml.bind-api:2.3.3" level="project" />
+    <orderEntry type="library" scope="PROVIDED" name="Maven: jakarta.activation:jakarta.activation-api:1.2.2" level="project" />
+    <orderEntry type="library" scope="TEST" name="Maven: org.assertj:assertj-core:3.18.1" level="project" />
+    <orderEntry type="library" scope="TEST" name="Maven: org.hamcrest:hamcrest:2.2" level="project" />
+    <orderEntry type="library" scope="TEST" name="Maven: org.junit.jupiter:junit-jupiter:5.7.0" level="project" />
+    <orderEntry type="library" scope="TEST" name="Maven: org.junit.jupiter:junit-jupiter-api:5.7.0" level="project" />
+    <orderEntry type="library" scope="TEST" name="Maven: org.apiguardian:apiguardian-api:1.1.0" level="project" />
+    <orderEntry type="library" scope="TEST" name="Maven: org.opentest4j:opentest4j:1.2.0" level="project" />
+    <orderEntry type="library" scope="TEST" name="Maven: org.junit.platform:junit-platform-commons:1.7.0" level="project" />
+    <orderEntry type="library" scope="TEST" name="Maven: org.junit.jupiter:junit-jupiter-params:5.7.0" level="project" />
+    <orderEntry type="library" scope="TEST" name="Maven: org.junit.jupiter:junit-jupiter-engine:5.7.0" level="project" />
+    <orderEntry type="library" scope="TEST" name="Maven: org.junit.platform:junit-platform-engine:1.7.0" level="project" />
+    <orderEntry type="library" scope="TEST" name="Maven: org.mockito:mockito-core:3.6.28" level="project" />
+    <orderEntry type="library" scope="TEST" name="Maven: net.bytebuddy:byte-buddy-agent:1.10.18" level="project" />
+    <orderEntry type="library" scope="TEST" name="Maven: org.objenesis:objenesis:3.1" level="project" />
+    <orderEntry type="library" scope="TEST" name="Maven: org.mockito:mockito-junit-jupiter:3.6.28" level="project" />
+    <orderEntry type="library" scope="TEST" name="Maven: org.skyscreamer:jsonassert:1.5.0" level="project" />
+    <orderEntry type="library" scope="TEST" name="Maven: com.vaadin.external.google:android-json:0.0.20131108.vaadin1" level="project" />
+    <orderEntry type="library" scope="PROVIDED" name="Maven: org.springframework:spring-core:5.3.2" level="project" />
+    <orderEntry type="library" scope="PROVIDED" name="Maven: org.springframework:spring-jcl:5.3.2" level="project" />
+    <orderEntry type="library" scope="TEST" name="Maven: org.springframework:spring-test:5.3.2" level="project" />
+    <orderEntry type="library" scope="TEST" name="Maven: org.xmlunit:xmlunit-core:2.7.0" level="project" />
+    <orderEntry type="library" scope="PROVIDED" name="Maven: javax.servlet.jsp:jsp-api:2.2.1-b03" level="project" />
+    <orderEntry type="library" scope="PROVIDED" name="Maven: mysql:mysql-connector-java:8.0.22" level="project" />
+    <orderEntry type="library" scope="PROVIDED" name="Maven: com.querydsl:querydsl-sql:4.4.0" level="project" />
+    <orderEntry type="library" scope="PROVIDED" name="Maven: com.querydsl:querydsl-core:4.4.0" level="project" />
+    <orderEntry type="library" scope="PROVIDED" name="Maven: com.mysema.commons:mysema-commons-lang:0.2.4" level="project" />
+    <orderEntry type="library" scope="PROVIDED" name="Maven: javax.validation:validation-api:2.0.1.Final" level="project" />
+    <orderEntry type="library" scope="PROVIDED" name="Maven: org.slf4j:slf4j-api:1.7.30" level="project" />
+    <orderEntry type="library" scope="PROVIDED" name="Maven: org.apache.servicemix.bundles:org.apache.servicemix.bundles.javax-inject:1_2" level="project" />
+    <orderEntry type="library" scope="PROVIDED" name="Maven: com.infradna.tool:bridge-method-annotation:1.13" level="project" />
+    <orderEntry type="library" scope="PROVIDED" name="Maven: com.querydsl:querydsl-jpa:4.4.0" level="project" />
+    <orderEntry type="library" scope="PROVIDED" name="Maven: javax.inject:javax.inject:1" level="project" />
+    <orderEntry type="library" name="Maven: org.hibernate.validator:hibernate-validator:6.1.6.Final" level="project" />
+    <orderEntry type="library" name="Maven: jakarta.validation:jakarta.validation-api:2.0.2" level="project" />
+    <orderEntry type="library" name="Maven: org.jboss.logging:jboss-logging:3.4.1.Final" level="project" />
+    <orderEntry type="library" name="Maven: com.fasterxml:classmate:1.5.1" level="project" />
+    <orderEntry type="library" name="Maven: com.google.guava:guava:29.0-jre" level="project" />
+    <orderEntry type="library" name="Maven: com.google.guava:failureaccess:1.0.1" level="project" />
+    <orderEntry type="library" name="Maven: com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava" level="project" />
+    <orderEntry type="library" name="Maven: com.google.code.findbugs:jsr305:3.0.2" level="project" />
+    <orderEntry type="library" name="Maven: org.checkerframework:checker-qual:2.11.1" level="project" />
+    <orderEntry type="library" name="Maven: com.google.errorprone:error_prone_annotations:2.3.4" level="project" />
+    <orderEntry type="library" name="Maven: com.google.j2objc:j2objc-annotations:1.3" level="project" />
+    <orderEntry type="library" name="Maven: io.jsonwebtoken:jjwt:0.9.1" level="project" />
+    <orderEntry type="library" name="Maven: com.fasterxml.jackson.core:jackson-databind:2.11.3" level="project" />
+    <orderEntry type="library" name="Maven: com.fasterxml.jackson.core:jackson-annotations:2.11.3" level="project" />
+    <orderEntry type="library" name="Maven: com.fasterxml.jackson.core:jackson-core:2.11.3" level="project" />
+    <orderEntry type="library" name="Maven: com.alibaba:fastjson:1.2.73" level="project" />
+    <orderEntry type="library" name="Maven: commons-codec:commons-codec:1.15" level="project" />
+    <orderEntry type="library" name="Maven: commons-io:commons-io:2.7" level="project" />
+    <orderEntry type="library" name="Maven: org.apache.commons:commons-lang3:3.11" level="project" />
+    <orderEntry type="library" name="Maven: joda-time:joda-time:2.10.6" level="project" />
+    <orderEntry type="library" scope="TEST" name="Maven: junit:junit:4.13.1" level="project" />
+    <orderEntry type="library" scope="TEST" name="Maven: org.hamcrest:hamcrest-core:2.2" level="project" />
+  </component>
+</module>

+ 229 - 0
pom.xml

@@ -0,0 +1,229 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+	<modelVersion>4.0.0</modelVersion>
+	<!-- 不解释 -->
+	<groupId>com.xiesx.fast</groupId>
+	<artifactId>fast-boot</artifactId>
+	<version>2.2.1</version>
+
+	<!-- 打包类型 -->
+	<packaging>jar</packaging>
+
+	<!-- 父类 -->
+	<parent>
+		<groupId>org.springframework.boot</groupId>
+		<artifactId>spring-boot-starter-parent</artifactId>
+		<version>2.4.1</version>
+		<relativePath /> <!-- lookup parent from repository -->
+	</parent>
+
+	<!-- 配置 -->
+	<properties>
+		<!-- 文件拷贝时的编码 -->
+		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+		<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
+		<!-- 编译时的编码 -->
+		<maven.compiler.encoding>UTF-8</maven.compiler.encoding>
+		<!-- JAVA版本 -->
+		<java.version>1.8</java.version>
+	</properties>
+
+	<!-- 版本管理,会引入jar -->
+	<dependencies>
+		<!-- lombok -->
+		<dependency>
+			<groupId>org.projectlombok</groupId>
+			<artifactId>lombok</artifactId>
+		</dependency>
+
+		<!-- ==========================spring-boot========================== -->
+		<!-- web -->
+		<dependency>
+			<groupId>org.springframework.boot</groupId>
+			<artifactId>spring-boot-starter-web</artifactId>
+			<scope>provided</scope>
+			<exclusions><!-- 去掉默认配置 -->
+				<exclusion>
+					<groupId>org.springframework.boot</groupId>
+					<artifactId>spring-boot-starter-logging</artifactId>
+				</exclusion>
+			</exclusions>
+		</dependency>
+		<!-- configuration -->
+		<dependency>
+			<groupId>org.springframework.boot</groupId>
+			<artifactId>spring-boot-configuration-processor</artifactId>
+			<optional>true</optional>
+		</dependency>
+		<!-- data-jdbc -->
+		<dependency>
+			<groupId>org.springframework.boot</groupId>
+			<artifactId>spring-boot-starter-data-jdbc</artifactId>
+			<scope>provided</scope>
+		</dependency>
+		<!-- data-jpa -->
+		<dependency>
+			<groupId>org.springframework.boot</groupId>
+			<artifactId>spring-boot-starter-data-jpa</artifactId>
+			<scope>provided</scope>
+		</dependency>
+		<!-- quartz -->
+		<dependency>
+			<groupId>org.springframework.boot</groupId>
+			<artifactId>spring-boot-starter-quartz</artifactId>
+			<scope>provided</scope>
+		</dependency>
+		<!-- log4j2 -->
+		<dependency>
+			<groupId>org.springframework.boot</groupId>
+			<artifactId>spring-boot-starter-log4j2</artifactId>
+			<scope>provided</scope>
+		</dependency>
+		<!-- test -->
+		<dependency>
+			<groupId>org.springframework.boot</groupId>
+			<artifactId>spring-boot-starter-test</artifactId>
+			<scope>test</scope>
+		</dependency>
+
+		<!-- ==========================jsp========================== -->
+		<!-- https://mvnrepository.com/artifact/javax.servlet.jsp/jsp-api -->
+		<dependency>
+			<groupId>javax.servlet.jsp</groupId>
+			<artifactId>jsp-api</artifactId>
+			<version>2.2.1-b03</version>
+			<scope>provided</scope>
+		</dependency>
+
+		<!-- ==========================数据库========================== -->
+		<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
+		<dependency>
+			<groupId>mysql</groupId>
+			<artifactId>mysql-connector-java</artifactId>
+			<scope>provided</scope>
+		</dependency>
+		<!-- https://mvnrepository.com/artifact/com.querydsl/querydsl-sql -->
+		<dependency>
+			<groupId>com.querydsl</groupId>
+			<artifactId>querydsl-sql</artifactId>
+			<version>${querydsl.version}</version>
+			<scope>provided</scope>
+		</dependency>
+		<!-- https://mvnrepository.com/artifact/com.querydsl/querydsl-jpa -->
+		<dependency>
+			<groupId>com.querydsl</groupId>
+			<artifactId>querydsl-jpa</artifactId>
+			<scope>provided</scope>
+		</dependency>
+		<!-- https://mvnrepository.com/artifact/org.hibernate.validator/hibernate-validator -->
+		<dependency>
+			<groupId>org.hibernate.validator</groupId>
+			<artifactId>hibernate-validator</artifactId>
+		</dependency>
+
+		<!-- ==========================工具========================== -->
+		<!-- https://mvnrepository.com/artifact/com.google.guava/guava -->
+		<dependency>
+			<groupId>com.google.guava</groupId>
+			<artifactId>guava</artifactId>
+			<version>29.0-jre</version>
+		</dependency>
+		<!-- https://mvnrepository.com/artifact/io.jsonwebtoken/jjwt -->
+		<dependency>
+			<groupId>io.jsonwebtoken</groupId>
+			<artifactId>jjwt</artifactId>
+			<version>0.9.1</version>
+		</dependency>
+		<!-- https://mvnrepository.com/artifact/com.alibaba/fastjson -->
+		<dependency>
+			<groupId>com.alibaba</groupId>
+			<artifactId>fastjson</artifactId>
+			<version>1.2.73</version>
+		</dependency>
+		<!-- https://mvnrepository.com/artifact/commons-codec/commons-codec -->
+		<dependency>
+			<groupId>commons-codec</groupId>
+			<artifactId>commons-codec</artifactId>
+		</dependency>
+		<!-- https://mvnrepository.com/artifact/commons-io/commons-io -->
+		<dependency>
+			<groupId>commons-io</groupId>
+			<artifactId>commons-io</artifactId>
+			<version>2.7</version>
+		</dependency>
+		<!-- https://mvnrepository.com/artifact/org.apache.commons/commons-lang3 -->
+		<dependency>
+			<groupId>org.apache.commons</groupId>
+			<artifactId>commons-lang3</artifactId>
+		</dependency>
+		<!-- https://mvnrepository.com/artifact/joda-time/joda-time -->
+		<dependency>
+			<groupId>joda-time</groupId>
+			<artifactId>joda-time</artifactId>
+			<version>2.10.6</version>
+		</dependency>
+		<!-- ==========================测试•========================== -->
+		<!-- https://mvnrepository.com/artifact/junit/junit -->
+		<dependency>
+			<groupId>junit</groupId>
+			<artifactId>junit</artifactId>
+			<scope>test</scope>
+		</dependency>
+	</dependencies>
+
+	<!-- 版本管理,不会引入jar -->
+	<dependencyManagement>
+		<dependencies>
+			<dependency>
+				<groupId>org.springframework.cloud</groupId>
+				<artifactId>spring-cloud-dependencies</artifactId>
+				<version>Greenwich.SR3</version>
+				<type>pom</type>
+				<scope>import</scope>
+			</dependency>
+		</dependencies>
+	</dependencyManagement>
+
+	<!-- 开发者(非必须) -->
+	<developers>
+		<developer>
+			<name>136305973</name>
+			<email>136305973@qq.com</email>
+		</developer>
+	</developers>
+
+	<!-- 编译 -->
+	<build>
+		<finalName>fast-boot</finalName>
+		<resources>
+			<resource>
+				<directory>src/main/resources</directory>
+				<includes>
+					<include>**/*.*</include>
+				</includes>
+				<filtering>false</filtering>
+			</resource>
+		</resources>
+		<plugins>
+			<plugin>
+				<groupId>org.apache.maven.plugins</groupId>
+				<artifactId>maven-jar-plugin</artifactId>
+				<configuration>
+					<archive>
+						<manifest>
+							<addDefaultImplementationEntries>true</addDefaultImplementationEntries>
+						</manifest>
+					</archive>
+				</configuration>
+			</plugin>
+			<plugin>
+				<groupId>org.apache.maven.plugins</groupId>
+				<artifactId>maven-surefire-plugin</artifactId>
+				<configuration>
+					<skipTests>true</skipTests><!-- 忽略测试 -->
+				</configuration>
+			</plugin>
+		</plugins>
+	</build>
+</project>

+ 29 - 0
src/main/java/com/xiesx/fastboot/SpringContextAware.java

@@ -0,0 +1,29 @@
+package com.xiesx.fastboot;
+
+import org.apache.commons.lang3.ObjectUtils;
+import org.springframework.beans.BeansException;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationContextAware;
+import org.springframework.stereotype.Component;
+
+import lombok.extern.slf4j.Slf4j;
+
+@Slf4j
+@Component
+public class SpringContextAware implements ApplicationContextAware {
+
+    private static ApplicationContext applicationContext;
+
+    public static ApplicationContext getApplicationContext() {
+        return applicationContext;
+    }
+
+    @Override
+    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
+        if (ObjectUtils.isEmpty(SpringContextAware.applicationContext)) {
+            SpringContextAware.applicationContext = applicationContext;
+            SpringStartup.init();
+            log.info("Startup ApplicationContext completed.");
+        }
+    }
+}

+ 84 - 0
src/main/java/com/xiesx/fastboot/SpringHelper.java

@@ -0,0 +1,84 @@
+package com.xiesx.fastboot;
+
+import org.springframework.beans.factory.annotation.BeanFactoryAnnotationUtils;
+import org.springframework.context.ApplicationContext;
+
+/**
+ * @title SpringHelper.java
+ * @description Spring工具类,静态工具方法获取bean
+ * @author Sixian.xie
+ * @date 2020-7-21 22:46:54
+ */
+public class SpringHelper {
+
+    /**
+     * applicationContext
+     *
+     * @return
+     */
+    public static ApplicationContext getContext() {
+        ApplicationContext applicationContext = SpringContextAware.getApplicationContext();
+        if (applicationContext != null) {
+            return applicationContext;
+        }
+        return null;
+    }
+
+    /**
+     * 通过name获取 Bean
+     *
+     * @param name
+     * @return
+     */
+    public static Object getBean(String name) {
+        ApplicationContext applicationContext = SpringContextAware.getApplicationContext();
+        if (applicationContext != null) {
+            return applicationContext.getBean(name);
+        }
+        return null;
+    }
+
+    /**
+     * 通过class获取Bean
+     *
+     * @param clazz
+     * @return
+     */
+    public static <T> T getBean(Class<T> clazz) {
+        ApplicationContext applicationContext = SpringContextAware.getApplicationContext();
+        if (applicationContext != null) {
+            return applicationContext.getBean(clazz);
+        }
+        return null;
+    }
+
+    /**
+     * 通过name,以及Clazz返回指定的Bean
+     *
+     * @param name
+     * @param clazz
+     * @return
+     */
+    public static <T> T getBean(String name, Class<T> clazz) {
+        ApplicationContext applicationContext = SpringContextAware.getApplicationContext();
+        if (applicationContext != null) {
+            return applicationContext.getBean(name, clazz);
+        }
+        return null;
+    }
+
+    /**
+     * 通过class,Qulifier值取同类型的某个bean
+     *
+     * @param clazz
+     * @param qualifier
+     * @return
+     */
+    public static <T> T getBean(Class<T> clazz, String qualifier) {
+        ApplicationContext applicationContext = SpringContextAware.getApplicationContext();
+        if (applicationContext != null) {
+            return BeanFactoryAnnotationUtils.qualifiedBeanOfType(applicationContext.getAutowireCapableBeanFactory(), clazz, qualifier);
+        }
+        return null;
+    }
+}

+ 49 - 0
src/main/java/com/xiesx/fastboot/SpringStartup.java

@@ -0,0 +1,49 @@
+package com.xiesx.fastboot;
+
+import com.xiesx.fastboot.utils.RuntimeUtils;
+
+import lombok.extern.slf4j.Slf4j;
+
+@Slf4j
+public class SpringStartup {
+
+    public static String classUrl;
+
+    /** 由于是分布式环境,该名字为每个tomcat的目录名(在部署时必须唯一) */
+    public static String servername;
+
+    public static String serverpath;
+
+    public static void init() {
+        classUrl = RuntimeUtils.getRootPath().toLowerCase();
+        if (classUrl.contains("target")) {// 本地
+            int index = classUrl.indexOf("/target");
+            if (index > 0) {
+                String path = classUrl.substring(0, index);
+                index = path.lastIndexOf("/");
+                servername = path.substring(index + 1);
+                serverpath = classUrl.split(servername)[0] + servername;
+            }
+        } else if (classUrl.contains("web-inf")) {// tomcat启动
+            int index = classUrl.indexOf("/web-inf");
+            if (index > 0) {
+                String path = classUrl.substring(0, index);
+                index = path.lastIndexOf("/");
+                servername = path.substring(index + 1);
+                serverpath = classUrl.split(servername)[0] + servername;
+            }
+        } else if (classUrl.contains("webapps")) {// jar启动
+            int index = classUrl.indexOf("/webapps");
+            if (index > 0) {
+                String path = classUrl.substring(0, index);
+                index = path.lastIndexOf("/");
+                servername = path.substring(index + 1);
+                serverpath = classUrl.split(servername)[0] + servername;
+            }
+        } else {
+            servername = "unknown";
+            serverpath = classUrl;
+        }
+        log.info("Startup name: " + servername + ", path: " + serverpath);
+    }
+}

+ 17 - 0
src/main/java/com/xiesx/fastboot/base/AbstractState.java

@@ -0,0 +1,17 @@
+package com.xiesx.fastboot.base;
+
+/**
+ * @title AbstractState.java
+ * @description 响应状态抽象类
+ * @author Sixian.xie
+ * @date 2020-7-21 22:30:17
+ */
+public abstract class AbstractState {
+
+    /**
+     * 是否成功
+     *
+     * @return
+     */
+    public abstract Boolean isSuccess();
+}

+ 65 - 0
src/main/java/com/xiesx/fastboot/base/pagination/PaginationHelper.java

@@ -0,0 +1,65 @@
+package com.xiesx.fastboot.base.pagination;
+
+import java.util.List;
+import java.util.Map;
+
+import org.springframework.data.domain.Page;
+
+import com.google.common.collect.Lists;
+
+import lombok.NonNull;
+
+/**
+ * @title PaginationHelper.java
+ * @description 分页帮助类
+ * @author Sixian.xie
+ * @date 2020-7-21 22:29:17
+ */
+public class PaginationHelper {
+
+    /**
+     * 构造
+     *
+     * @param page
+     * @return
+     */
+    public static PaginationResult create(Page<?> page) {
+        return create(page.toList(), (int) page.getTotalElements());
+    }
+
+    /**
+     * 构造
+     *
+     * @param page
+     * @return
+     */
+    public static PaginationResult create(Page<?> page, Map<String, Object> extData) {
+        PaginationResult paginationResult = create(page.toList(), (int) page.getTotalElements());
+        paginationResult.extData = extData;
+        return paginationResult;
+    }
+
+    /**
+     * 构造空的返回
+     * @return
+     */
+    public static PaginationResult createEmpty() {
+        return  PaginationResult.builder().code(1).msg("No data").data(Lists.newArrayList()).count(0).build();
+    }
+
+    /**
+     * 构造
+     *
+     * @param data
+     * @param total
+     * @return
+     */
+    public static PaginationResult create(@NonNull List<?> data, Integer total) {
+        List<?> list = Lists.newArrayList(data);
+        if (list.isEmpty()) {
+            return PaginationResult.builder().code(1).msg("No data").data(Lists.newArrayList()).count(0).build();
+        } else {
+            return PaginationResult.builder().code(0).data(list).count(total).build();
+        }
+    }
+}

+ 37 - 0
src/main/java/com/xiesx/fastboot/base/pagination/PaginationResult.java

@@ -0,0 +1,37 @@
+package com.xiesx.fastboot.base.pagination;
+
+import java.util.List;
+import java.util.Map;
+
+import com.alibaba.fastjson.annotation.JSONField;
+
+import lombok.Builder;
+import lombok.Data;
+import lombok.experimental.Accessors;
+
+/**
+ * @title PaginationResult.java
+ * @description 分页结果对象
+ * @author Sixian.xie
+ * @date 2020-7-21 22:29:25
+ */
+@Data
+@Builder
+@Accessors(fluent = true)
+public class PaginationResult {
+
+    @JSONField(ordinal = 1)
+    public Integer code;
+
+    @JSONField(ordinal = 2)
+    public String msg;
+
+    @JSONField(ordinal = 3)
+    public List<?> data;
+
+    @JSONField(ordinal = 4)
+    public Integer count;
+
+    @JSONField(ordinal = 5)
+    public Map<String, Object> extData;
+}

+ 23 - 0
src/main/java/com/xiesx/fastboot/base/pagination/PaginationVo.java

@@ -0,0 +1,23 @@
+package com.xiesx.fastboot.base.pagination;
+
+import lombok.Data;
+
+/**
+ * @title PaginationVo.java
+ * @description 分页请求对象
+ * @author Sixian.xie
+ * @date 2020-7-21 22:29:45
+ */
+@Data
+public class PaginationVo {
+
+    public Integer page = 1;
+
+    public Integer limit = 25;
+
+    public Integer size = 25;
+
+    public Integer getPage() {
+        return page - 1;
+    }
+}

+ 80 - 0
src/main/java/com/xiesx/fastboot/base/result/BaseResult.java

@@ -0,0 +1,80 @@
+package com.xiesx.fastboot.base.result;
+
+import com.alibaba.fastjson.annotation.JSONField;
+
+import lombok.Builder;
+import lombok.Data;
+
+/**
+ * @title BaseResult.java
+ * @description 结果基类
+ * @author Sixian.xie
+ * @date 2020-7-21 22:29:55
+ */
+@Data
+@Builder
+public class BaseResult {
+
+    public static String OP_MSG_SUCC = "success";
+
+    public static String OP_MSG_FAIL = "failed";
+
+    public static String OP_MSG_FAI_RETRY = "retry";
+
+    // 返回值
+    public static Integer SUCCESS = 0; // 成功
+
+    public static Integer FAIL = -1;// 失败
+
+    public static Integer ERROR = -2;// 异常
+
+    public static Integer RETRY = -3;// 重试
+
+    // 状态
+    @JSONField(ordinal = 1)
+    private Integer code;
+
+    // 提示
+    @JSONField(ordinal = 2)
+    private String msg;
+
+    // 数据
+    @JSONField(ordinal = 3)
+    private Object data;
+
+    /**
+     * 判断是否成功并返回
+     */
+    @JSONField(ordinal = 4)
+    public Boolean getSuccess() {
+        return code == SUCCESS;
+    }
+
+    /**
+     * 判断是否成功
+     */
+    @JSONField(serialize = false)
+    public Boolean isOk() {
+        return code == SUCCESS ? true : false;
+    }
+
+    /**
+     * 判断是否失败
+     *
+     * @return
+     */
+    @JSONField(serialize = false)
+    public Boolean isFail() {
+        return code == FAIL ? true : false;
+    }
+
+    /**
+     * 判断是否异常
+     *
+     * @return
+     */
+    @JSONField(serialize = false)
+    public Boolean isError() {
+        return code == ERROR ? true : false;
+    }
+}

+ 144 - 0
src/main/java/com/xiesx/fastboot/base/result/R.java

@@ -0,0 +1,144 @@
+package com.xiesx.fastboot.base.result;
+
+import java.util.List;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.parser.Feature;
+
+import lombok.NonNull;
+
+/**
+ * @title R.java
+ * @description 返回值快速构造类
+ * @author Sixian.xie
+ * @date 2020-7-21 22:30:11
+ */
+public class R {
+
+    /**
+     * 成功
+     */
+    public static BaseResult succ() {
+        return BaseResult.builder().code(BaseResult.SUCCESS).msg(BaseResult.OP_MSG_SUCC).build();
+    }
+
+    public static BaseResult succ(@NonNull String msg) {
+        return BaseResult.builder().code(BaseResult.SUCCESS).msg(msg).build();
+    }
+
+    public static BaseResult succ(@NonNull Object data) {
+        return BaseResult.builder().code(BaseResult.SUCCESS).msg(BaseResult.OP_MSG_SUCC).data(data).build();
+    }
+
+    public static BaseResult succ(@NonNull String msg, @NonNull Object data) {
+        return BaseResult.builder().code(BaseResult.SUCCESS).msg(msg).data(data).build();
+    }
+
+    public static BaseResult succ(@NonNull Integer code, @NonNull String msg) {
+        return BaseResult.builder().code(code).msg(msg).build();
+    }
+
+    public static BaseResult succ(@NonNull Integer code, @NonNull String msg, @NonNull Object data) {
+        return BaseResult.builder().code(code).msg(msg).data(data).build();
+    }
+
+    /**
+     * 失败
+     */
+    public static BaseResult fail() {
+        return BaseResult.builder().code(BaseResult.FAIL).msg(BaseResult.OP_MSG_FAIL).build();
+    }
+
+    public static BaseResult fail(@NonNull String msg) {
+        return BaseResult.builder().code(BaseResult.FAIL).msg(msg).build();
+    }
+
+    public static BaseResult fail(@NonNull Object data) {
+        return BaseResult.builder().code(BaseResult.FAIL).msg(BaseResult.OP_MSG_FAIL).data(data).build();
+    }
+
+    public static BaseResult fail(@NonNull String msg, Object data) {
+        return BaseResult.builder().code(BaseResult.FAIL).msg(msg).data(data).build();
+    }
+
+    public static BaseResult fail(@NonNull Integer code, @NonNull String msg) {
+        return BaseResult.builder().code(code).msg(msg).build();
+    }
+
+    public static BaseResult fail(@NonNull Integer code, @NonNull String msg, Object data) {
+        return BaseResult.builder().code(code).msg(msg).data(data).build();
+    }
+
+    /**
+     * 异常
+     */
+    public static BaseResult error() {
+        return BaseResult.builder().code(BaseResult.ERROR).msg(BaseResult.OP_MSG_FAIL).build();
+    }
+
+    public static BaseResult error(@NonNull String msg) {
+        return BaseResult.builder().code(BaseResult.ERROR).msg(msg).build();
+    }
+
+    public static BaseResult error(@NonNull Object data) {
+        return BaseResult.builder().code(BaseResult.ERROR).msg(BaseResult.OP_MSG_FAIL).data(data).build();
+    }
+
+    public static BaseResult error(@NonNull String msg, Object data) {
+        return BaseResult.builder().code(BaseResult.ERROR).msg(msg).data(data).build();
+    }
+
+    public static BaseResult error(@NonNull Integer code, @NonNull String msg) {
+        return BaseResult.builder().code(code).msg(msg).build();
+    }
+
+    public static BaseResult error(@NonNull Integer code, @NonNull String msg, Object data) {
+        return BaseResult.builder().code(code).msg(msg).data(data).build();
+    }
+
+    /**
+     * 重试
+     */
+    public static BaseResult retry() {
+        return BaseResult.builder().code(BaseResult.RETRY).msg(BaseResult.OP_MSG_FAI_RETRY).build();
+    }
+
+    public static BaseResult retry(@NonNull String msg) {
+        return BaseResult.builder().code(BaseResult.RETRY).msg(msg).build();
+    }
+
+    public static BaseResult retry(@NonNull Object data) {
+        return BaseResult.builder().code(BaseResult.RETRY).msg(BaseResult.OP_MSG_FAI_RETRY).data(data).build();
+    }
+
+    public static BaseResult retry(@NonNull String msg, Object data) {
+        return BaseResult.builder().code(BaseResult.RETRY).msg(msg).data(data).build();
+    }
+
+    public static BaseResult retry(@NonNull Integer code, @NonNull String msg) {
+        return BaseResult.builder().code(code).msg(msg).build();
+    }
+
+    public static BaseResult retry(@NonNull Integer code, @NonNull String msg, Object data) {
+        return BaseResult.builder().code(code).msg(msg).data(data).build();
+    }
+
+
+    /**
+     * str 转 jsonObject
+     */
+    public static <T> T parseObject(String text, Class<T> clazz) {
+        return JSON.parseObject(text, clazz, new Feature[0]);
+    }
+
+    /**
+     * str 转 jsonArray
+     *
+     * @param text
+     * @param clazz
+     * @return
+     */
+    public static <T> List<T> parseArray(String text, Class<T> clazz) {
+        return JSON.parseArray(text, clazz);
+    }
+}

+ 59 - 0
src/main/java/com/xiesx/fastboot/core/body/GlobalBodyAdvice.java

@@ -0,0 +1,59 @@
+package com.xiesx.fastboot.core.body;
+
+import java.util.Arrays;
+import java.util.Map;
+
+import org.springframework.core.MethodParameter;
+import org.springframework.http.MediaType;
+import org.springframework.http.server.ServerHttpRequest;
+import org.springframework.http.server.ServerHttpResponse;
+import org.springframework.web.bind.annotation.RestControllerAdvice;
+import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
+
+import com.xiesx.fastboot.base.pagination.PaginationResult;
+import com.xiesx.fastboot.base.result.BaseResult;
+import com.xiesx.fastboot.base.result.R;
+
+import lombok.extern.slf4j.Slf4j;
+
+/**
+ * @title GlobalBodyAdvice.java
+ * @description 统一返回
+ * @author Sixian.xie
+ * @date 2020-7-21 22:30:32
+ */
+@Slf4j
+@RestControllerAdvice
+@SuppressWarnings("all")
+public class GlobalBodyAdvice implements ResponseBodyAdvice<Object> {
+
+    String[] methodNames = {};
+
+    @Override
+    public Object beforeBodyWrite(Object returnValue, MethodParameter methodParameter, MediaType mediaType, Class clas,
+            ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse) {
+        log.debug("beforeBodyWrite ......");
+        Object res = null;
+        // 按需使用,如果有改动,务必兼容之前代码
+        if (returnValue instanceof BaseResult || returnValue instanceof PaginationResult) {
+            res = returnValue;
+        } else if (returnValue instanceof Map<?, ?> || returnValue instanceof Iterable<?>) {
+            res = returnValue;
+        } else if (returnValue instanceof com.alibaba.fastjson.JSON) {
+            res = returnValue;
+        } else if (returnValue instanceof String) {
+            res = returnValue;
+        } else {
+            res = R.succ(returnValue);
+        }
+        return res;
+    }
+
+    @Override
+    public boolean supports(MethodParameter methodParameter, Class clas) {
+        // 获取当前处理请求的controller的方法
+        String methodName = methodParameter.getMethod().getName();
+        // 不拦截
+        return !Arrays.asList(methodNames).contains(methodName);
+    }
+}

+ 17 - 0
src/main/java/com/xiesx/fastboot/core/body/annotation/GoEnableBody.java

@@ -0,0 +1,17 @@
+package com.xiesx.fastboot.core.body.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import org.springframework.context.annotation.Import;
+
+import com.xiesx.fastboot.core.body.GlobalBodyAdvice;
+import com.xiesx.fastboot.core.body.cfg.HeaderCfg;
+
+@Target({ElementType.TYPE})
+@Retention(RetentionPolicy.RUNTIME)
+@Import({HeaderCfg.class, GlobalBodyAdvice.class})
+public @interface GoEnableBody {
+}

+ 20 - 0
src/main/java/com/xiesx/fastboot/core/body/annotation/GoHeader.java

@@ -0,0 +1,20 @@
+package com.xiesx.fastboot.core.body.annotation;
+
+import static java.lang.annotation.ElementType.PARAMETER;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+
+/**
+ * @title GoHeader.java
+ * @description 请求头信息
+ * @author Sixian.xie
+ * @date 2020-7-21 22:35:48
+ */
+@Target({PARAMETER})
+@Retention(RUNTIME)
+public @interface GoHeader {
+
+}

+ 38 - 0
src/main/java/com/xiesx/fastboot/core/body/cfg/HeaderCfg.java

@@ -0,0 +1,38 @@
+package com.xiesx.fastboot.core.body.cfg;
+
+import java.util.List;
+
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.method.support.HandlerMethodArgumentResolver;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
+
+import com.xiesx.fastboot.core.body.handle.HeaderArgumentResolver;
+
+/**
+ * @title BodyCfg.java
+ * @description 请求参数配置
+ * @author Sixian.xie
+ * @date 2020-7-21 22:44:43
+ */
+@Configuration
+public class HeaderCfg implements WebMvcConfigurer {
+
+    public static final String DEVICE = "device";
+
+    public static final String OSVERSION = "osVersion";
+
+    public static final String APVERSION = "apVersion";
+
+    public static final String APVERSIONCODE = "apVersionCode";
+
+    public static final String ANDROIDID = "androidId";
+
+    public static final String PSUEDOUNIQUEID = "psuedoUniqueId";
+
+    public static final String NETWORKTYPE = "networkType";
+
+    @Override
+    public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
+        resolvers.add(new HeaderArgumentResolver());
+    }
+}

+ 31 - 0
src/main/java/com/xiesx/fastboot/core/body/handle/CurrentHeader.java

@@ -0,0 +1,31 @@
+package com.xiesx.fastboot.core.body.handle;
+
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+
+/**
+ * @title CurrentHeader.java
+ * @description 请求头信息
+ * @author Sixian.xie
+ * @date 2020-7-21 22:37:08
+ */
+@Data
+@Builder
+@AllArgsConstructor
+public class CurrentHeader {
+
+    private String device;
+
+    private String osVersion;
+
+    private String apVersion;
+
+    private String apVersionCode;
+
+    private String androidId;
+
+    private String psuedoUniqueId;
+
+    private String networkType;
+}

+ 63 - 0
src/main/java/com/xiesx/fastboot/core/body/handle/HeaderArgumentResolver.java

@@ -0,0 +1,63 @@
+package com.xiesx.fastboot.core.body.handle;
+
+import org.apache.commons.lang3.ObjectUtils;
+import org.springframework.core.MethodParameter;
+import org.springframework.web.bind.support.WebDataBinderFactory;
+import org.springframework.web.context.request.NativeWebRequest;
+import org.springframework.web.method.support.HandlerMethodArgumentResolver;
+import org.springframework.web.method.support.ModelAndViewContainer;
+
+import com.xiesx.fastboot.core.body.annotation.GoHeader;
+import com.xiesx.fastboot.core.body.cfg.HeaderCfg;
+import com.xiesx.fastboot.core.body.handle.CurrentHeader.CurrentHeaderBuilder;
+
+/**
+ * @title HeaderArgumentResolver.java
+ * @description
+ * @author Sixian.xie
+ * @date 2020-7-21 22:37:35
+ */
+public class HeaderArgumentResolver implements HandlerMethodArgumentResolver {
+
+    @Override
+    public boolean supportsParameter(MethodParameter parameter) {
+        return parameter.getParameterType().isAssignableFrom(CurrentHeader.class) && parameter.hasParameterAnnotation(GoHeader.class);
+    }
+
+    @Override
+    public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer container, NativeWebRequest request,
+            WebDataBinderFactory factory) throws Exception {
+        // 构造设备信息
+        CurrentHeaderBuilder builder = CurrentHeader.builder();
+        // 获取设备信息
+        String device = request.getHeader(HeaderCfg.DEVICE);
+        if (ObjectUtils.isNotEmpty(device)) {
+            builder.device(device);
+        }
+        String osVersion = request.getHeader(HeaderCfg.OSVERSION);
+        if (ObjectUtils.isNotEmpty(osVersion)) {
+            builder.osVersion(osVersion);
+        }
+        String apVersion = request.getHeader(HeaderCfg.APVERSION);
+        if (ObjectUtils.isNotEmpty(apVersion)) {
+            builder.apVersion(apVersion);
+        }
+        String apVersionCode = request.getHeader(HeaderCfg.APVERSIONCODE);
+        if (ObjectUtils.isNotEmpty(apVersionCode)) {
+            builder.apVersionCode(apVersionCode);
+        }
+        String androidId = request.getHeader(HeaderCfg.ANDROIDID);
+        if (ObjectUtils.isNotEmpty(androidId)) {
+            builder.androidId(androidId);
+        }
+        String psuedoUniqueId = request.getHeader(HeaderCfg.PSUEDOUNIQUEID);
+        if (ObjectUtils.isNotEmpty(psuedoUniqueId)) {
+            builder.psuedoUniqueId(psuedoUniqueId);
+        }
+        String networkType = request.getHeader(HeaderCfg.NETWORKTYPE);
+        if (ObjectUtils.isNotEmpty(networkType)) {
+            builder.networkType(networkType);
+        }
+        return builder.build();
+    }
+}

+ 5 - 0
src/main/java/com/xiesx/fastboot/core/exception/.html

@@ -0,0 +1,5 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+<head></head>
+<body>全局异常 @GoEnableRestExcAdvice</body>
+</html>

+ 133 - 0
src/main/java/com/xiesx/fastboot/core/exception/GlobalExceptionAdvice.java

@@ -0,0 +1,133 @@
+package com.xiesx.fastboot.core.exception;
+
+import java.util.List;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.validation.ConstraintViolationException;
+import javax.validation.ValidationException;
+
+import org.springframework.dao.DataAccessException;
+import org.springframework.dao.EmptyResultDataAccessException;
+import org.springframework.dao.InvalidDataAccessApiUsageException;
+import org.springframework.http.converter.HttpMessageNotReadableException;
+import org.springframework.validation.BindException;
+import org.springframework.validation.BindingResult;
+import org.springframework.validation.FieldError;
+import org.springframework.web.HttpMediaTypeNotSupportedException;
+import org.springframework.web.HttpRequestMethodNotSupportedException;
+import org.springframework.web.bind.annotation.ExceptionHandler;
+import org.springframework.web.bind.annotation.RestControllerAdvice;
+
+import com.google.common.collect.Lists;
+import com.xiesx.fastboot.base.result.BaseResult;
+import com.xiesx.fastboot.base.result.R;
+import com.xiesx.fastboot.utils.ValidatorHelper;
+
+import lombok.extern.slf4j.Slf4j;
+
+/**
+ * @title GlobalExceptionAdvice.java
+ * @description 全局配置异常处理,分为5种涵盖日常使用场景
+ * @author Sixian.xie
+ * @date 2020-7-21 22:30:43
+ */
+@Slf4j
+@RestControllerAdvice
+public class GlobalExceptionAdvice {
+
+    /**
+     * 运行
+     *
+     * @param request
+     * @param e
+     * @return
+     */
+    @ExceptionHandler({Exception.class})
+    public BaseResult runtimeException(HttpServletRequest request, Exception e) {
+        log.error("runtimeException ......", e);
+        return R.error(RunExc.RUNTIME.getErrorCode(), e.getMessage());
+    }
+
+    /**
+     * 请求
+     *
+     * @param request
+     * @param e
+     * @return
+     */
+    @ExceptionHandler({HttpMessageNotReadableException.class, HttpRequestMethodNotSupportedException.class,
+            HttpMediaTypeNotSupportedException.class})
+    public BaseResult requestException(HttpServletRequest request, Exception e) {
+        log.error("requestException ......", e);
+        String msg = "";
+        if (e instanceof HttpMessageNotReadableException) {
+            msg = "当前参数解析失败";// 400 - Bad Request
+        } else if (e instanceof HttpRequestMethodNotSupportedException) {
+            msg = "不支持当前请求方法";// 405 - Method Not Allowed
+        } else if (e instanceof HttpMediaTypeNotSupportedException) {
+            msg = "不支持当前媒体类型";// 415 - Unsupported Media Type
+        } else {
+            msg = "未知系统异常";
+        }
+        return R.error(RunExc.REQUEST.getErrorCode(), msg);
+    }
+
+    /**
+     * 效验
+     *
+     * @param request
+     * @param e
+     * @return
+     */
+    @ExceptionHandler({BindException.class, ValidationException.class})
+    public BaseResult validatorException(HttpServletRequest request, Exception e) {
+        log.error("validatorException ......", e);
+        List<String> errorMsg = Lists.newArrayList();
+        // 这里走的是Spring Violation 验证 --> Java Violation,这里有BindException接收
+        if (e instanceof BindException) {
+            BindingResult violations = ((BindException) e).getBindingResult();
+            for (FieldError fieldError : violations.getFieldErrors()) {
+                errorMsg.add(fieldError.getField() + " " + fieldError.getDefaultMessage());
+            }
+        }
+        // 这里走的是Hibernate Violation 验证 --> Java Violation,这里有ConstraintViolationException接收
+        if (e instanceof ConstraintViolationException) {
+            errorMsg.addAll(ValidatorHelper.extractPropertyAndMessageAsList(((ConstraintViolationException) e)));
+        }
+        return R.error(RunExc.VALIDATOR.getErrorCode(), RunExc.VALIDATOR.getErrorMsg(), errorMsg);
+    }
+
+    /**
+     * 数据库
+     *
+     * @param request
+     * @param e
+     * @return
+     */
+    @ExceptionHandler({InvalidDataAccessApiUsageException.class, DataAccessException.class})
+    public BaseResult jdbcException(HttpServletRequest request, Exception e) {
+        log.error("jdbcException ......", e);
+        String msg = "";
+        if (e instanceof EmptyResultDataAccessException) {
+            msg = "无数据";
+        } else if (e instanceof InvalidDataAccessApiUsageException) {
+            msg = "无数据";
+        } else {
+            msg = "未知数据异常";
+        }
+        return R.error(msg);
+    }
+
+    /**
+     * 自定义
+     *
+     * @param request
+     * @param e
+     * @return
+     */
+    @ExceptionHandler({RunException.class})
+    public BaseResult runException(HttpServletRequest request, RunException e) {
+        log.error("runException ......", e);
+        return R.error(e.getErrorCode(), e.getMessage());
+    }
+}

+ 51 - 0
src/main/java/com/xiesx/fastboot/core/exception/RunExc.java

@@ -0,0 +1,51 @@
+package com.xiesx.fastboot.core.exception;
+
+import lombok.AllArgsConstructor;
+
+/**
+ * @title RunExc.java
+ * @description 异常枚举处理类
+ * @author Sixian.xie
+ * @date 2020-7-21 22:30:51
+ */
+@AllArgsConstructor
+public enum RunExc {
+
+    RUNTIME(1000, "运行错误"), // --> BaseRestExceptionAdvice --> runtimeException
+
+    REQUEST(2000, "请求失败"), // --> BaseRestExceptionAdvice --> requestException
+
+    VALIDATOR(3000, "校验错误"), // --> BaseRestExceptionAdvice --> validatorException
+
+    DBASE(4000, "数据错误"), // --> BaseRestExceptionAdvice --> jdbcException
+
+    TOKEN(5000, "登录已过期"), // --> TokenInterceptorHandler
+
+    SIGN(6000, "签名错误"), // --> SignAspect
+
+    RETRY(7000, "重试失败"), // --> HttpRetryer
+
+    LIMITER(8000, "请求限流"), // --> LimiterAspect
+
+    UNKNOWN(9999, "未知");
+
+    private Integer errorCode;
+
+    private String errorMsg;
+
+    public Integer getErrorCode() {
+        return errorCode;
+    }
+
+    public void setErrorCode(Integer errorCode) {
+        this.errorCode = errorCode;
+    }
+
+    public String getErrorMsg() {
+        return errorMsg;
+    }
+
+    public void setErrorMsg(String errorMsg) {
+        this.errorMsg = errorMsg;
+    }
+}

+ 76 - 0
src/main/java/com/xiesx/fastboot/core/exception/RunException.java

@@ -0,0 +1,76 @@
+package com.xiesx.fastboot.core.exception;
+
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * @title RunException.java
+ * @description 自定义异常
+ * @author Sixian.xie
+ * @date 2020-7-21 22:31:17
+ */
+@Data
+@EqualsAndHashCode(callSuper = false)
+public class RunException extends RuntimeException {
+
+    private static final long serialVersionUID = 1L;
+
+    private Integer errorCode;
+
+    public RunException() {
+        super();
+    }
+
+    /**
+     * throw new RunException("出错啦!");
+     *
+     * @param message
+     */
+    public RunException(String message) {
+        super(message);
+        this.errorCode = RunExc.RUNTIME.getErrorCode();
+    }
+
+    /**
+     * throw new RunException(e);
+     *
+     * @param message
+     */
+    public RunException(Throwable e) {
+        super(e);
+        this.errorCode = RunExc.RUNTIME.getErrorCode();
+    }
+
+    /**
+     * throw new RunException(RunExc.RUN);
+     *
+     * @param message
+     */
+    public RunException(RunExc act) {
+        super(act.getErrorMsg());
+        this.errorCode = act.getErrorCode();
+    }
+
+    /**
+     * throw new RunException(RunExc.RUN,"活动业务处理失败----");
+     *
+     * @param message
+     * @param message
+     */
+    public RunException(RunExc act, String message) {
+        super(act.getErrorMsg() + ":" + message);
+        this.errorCode = act.getErrorCode();
+    }
+
+    /**
+     * throw new RunException(RunExc.RUN,"{}活动业务处理失败----","暑期砍价");
+     *
+     * @param message
+     * @param format
+     * @param message
+     */
+    public RunException(RunExc act, String format, Object... message) {
+        super(act.getErrorMsg() + ":" + String.format(format, message));
+        this.errorCode = act.getErrorCode();
+    }
+}

+ 16 - 0
src/main/java/com/xiesx/fastboot/core/exception/annotation/GoEnableException.java

@@ -0,0 +1,16 @@
+package com.xiesx.fastboot.core.exception.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import org.springframework.context.annotation.Import;
+
+import com.xiesx.fastboot.core.exception.GlobalExceptionAdvice;
+
+@Target({ElementType.TYPE})
+@Retention(RetentionPolicy.RUNTIME)
+@Import({GlobalExceptionAdvice.class})
+public @interface GoEnableException {
+}

+ 16 - 0
src/main/java/com/xiesx/fastboot/core/fastjson/annotation/GoEnableFastJson.java

@@ -0,0 +1,16 @@
+package com.xiesx.fastboot.core.fastjson.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import org.springframework.context.annotation.Import;
+
+import com.xiesx.fastboot.core.fastjson.cfg.FastJsonCfg;
+
+@Target({ElementType.TYPE})
+@Retention(RetentionPolicy.RUNTIME)
+@Import({FastJsonCfg.class})
+public @interface GoEnableFastJson {
+}

+ 82 - 0
src/main/java/com/xiesx/fastboot/core/fastjson/cfg/FastJsonCfg.java

@@ -0,0 +1,82 @@
+package com.xiesx.fastboot.core.fastjson.cfg;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.util.List;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.http.MediaType;
+import org.springframework.http.converter.HttpMessageConverter;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
+
+import com.alibaba.fastjson.serializer.SerializeConfig;
+import com.alibaba.fastjson.serializer.SerializerFeature;
+import com.alibaba.fastjson.serializer.ToStringSerializer;
+import com.alibaba.fastjson.support.config.FastJsonConfig;
+import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter;
+import com.xiesx.fastboot.core.fastjson.serializer.CustomerBigDecimalCodec;
+
+@Configuration
+@EnableConfigurationProperties(FastJsonProperties.class)
+public class FastJsonCfg implements WebMvcConfigurer {
+
+    public static SerializerFeature[] serializerFeatures = new SerializerFeature[] {
+            // 字符类型字段如果为null,输出为"",而不是null
+            SerializerFeature.WriteNullStringAsEmpty,
+            // 数值字段如果为null,输出为0,而不是null
+            SerializerFeature.WriteNullNumberAsZero,
+            // Boolean字段如果为null,输出为false,而不是null
+            SerializerFeature.WriteNullBooleanAsFalse,
+            // 枚举类型用ToString输出为
+            SerializerFeature.WriteEnumUsingToString,
+            // 是否输出值为null的字段
+            // SerializerFeature.WriteMapNullValue,
+            // list字段如果为null,输出为[],而不是null
+            // SerializerFeature.WriteNullListAsEmpty,
+            // 输出格式后的日期
+            SerializerFeature.WriteDateUseDateFormat,
+            // 禁用循环引用
+            SerializerFeature.DisableCircularReferenceDetect,
+            // 忽略错误的get
+            SerializerFeature.IgnoreErrorGetter,
+            // 输出格式化
+            // SerializerFeature.PrettyFormat
+    };
+
+    @Autowired
+    private FastJsonProperties fastJsonProperties;
+
+    @Override
+    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
+        converters.add(0, fastJsonHttpMessageConverters());
+    }
+
+    public FastJsonHttpMessageConverter fastJsonHttpMessageConverters() {
+        // 转换器
+        FastJsonHttpMessageConverter fastConverter = new FastJsonHttpMessageConverter();
+        // json配置
+        fastConverter.setFastJsonConfig(fastJsonConfig());
+        // 支持类型
+        fastConverter.setSupportedMediaTypes(MediaType.parseMediaTypes(MediaType.APPLICATION_JSON_VALUE));
+        return fastConverter;
+    }
+
+    public FastJsonConfig fastJsonConfig() {
+        // 自定义配置
+        FastJsonConfig fastJsonConfig = new FastJsonConfig();
+        // 序列化
+        fastJsonConfig.setSerializerFeatures(serializerFeatures);
+        // 日期
+        fastJsonConfig.setDateFormat(fastJsonProperties.getDateFormat());
+        // 精度
+        SerializeConfig serializeConfig = SerializeConfig.globalInstance;
+        serializeConfig.put(Long.class, ToStringSerializer.instance);
+        serializeConfig.put(Long.TYPE, ToStringSerializer.instance);
+        serializeConfig.put(BigInteger.class, ToStringSerializer.instance);
+        serializeConfig.put(BigDecimal.class, CustomerBigDecimalCodec.instance);
+        fastJsonConfig.setSerializeConfig(serializeConfig);
+        return fastJsonConfig;
+    }
+}

+ 17 - 0
src/main/java/com/xiesx/fastboot/core/fastjson/cfg/FastJsonProperties.java

@@ -0,0 +1,17 @@
+package com.xiesx.fastboot.core.fastjson.cfg;
+
+
+import org.springframework.boot.context.properties.ConfigurationProperties;
+
+import lombok.Data;
+
+@Data
+@ConfigurationProperties(prefix = FastJsonProperties.PREFIX)
+public class FastJsonProperties {
+
+    public static final String PREFIX = "fastboot.fastjson";
+
+    private String dateFormat = "yyyy-MM-dd HH:mm:ss";
+
+    private Boolean desensitize = true;
+}

+ 49 - 0
src/main/java/com/xiesx/fastboot/core/fastjson/desensitized/SensitiveTypeEnum.java

@@ -0,0 +1,49 @@
+package com.xiesx.fastboot.core.fastjson.desensitized;
+
+
+public enum SensitiveTypeEnum {
+    /**
+     * 中文名
+     */
+    CHINESE_NAME,
+
+    /**
+     * 身份证号
+     */
+    ID_CARD,
+
+    /**
+     * 银行卡
+     */
+    BANK_CARD,
+
+    /**
+     * 座机号
+     */
+    PHONE,
+
+    /**
+     * 手机号
+     */
+    MOBILE,
+
+    /**
+     * 地址
+     */
+    ADDRESS,
+
+    /**
+     * 电子邮件
+     */
+    EMAIL,
+
+    /**
+     * 密码
+     */
+    PASSWORD,
+
+    /**
+     * 车牌号
+     */
+    CARNUMBER;
+}

+ 26 - 0
src/main/java/com/xiesx/fastboot/core/fastjson/serializer/CustomerBigDecimalCodec.java

@@ -0,0 +1,26 @@
+package com.xiesx.fastboot.core.fastjson.serializer;
+
+import java.text.DecimalFormat;
+
+import com.alibaba.fastjson.serializer.*;
+
+public class CustomerBigDecimalCodec extends BigDecimalCodec implements ContextObjectSerializer {
+
+    public final static CustomerBigDecimalCodec instance = new CustomerBigDecimalCodec();
+
+    /**
+     * 当BigDecimal类型的属性上有@JSONFiled注解,且该注解中的format有值时,使用该方法进行序列化,否则使用fastjson的
+     * BigDecimalCodec中的write方法进行序列化
+     */
+    @Override
+    public void write(JSONSerializer serializer, Object object, BeanContext context) {
+        SerializeWriter out = serializer.out;
+        if (object == null) {
+            out.writeString("");
+            return;
+        }
+        String format = context.getFormat();
+        DecimalFormat decimalFormat = new DecimalFormat(format);
+        out.writeString(decimalFormat.format(object));
+    }
+}

+ 46 - 0
src/main/java/com/xiesx/fastboot/core/jpa/JpaPlusRepository.java

@@ -0,0 +1,46 @@
+package com.xiesx.fastboot.core.jpa;
+
+import java.util.List;
+
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.Pageable;
+import org.springframework.data.domain.Sort;
+import org.springframework.data.jpa.repository.support.JpaRepositoryImplementation;
+import org.springframework.data.querydsl.QuerydslPredicateExecutor;
+import org.springframework.data.repository.NoRepositoryBean;
+
+import com.querydsl.core.types.Predicate;
+import com.querydsl.jpa.impl.JPADeleteClause;
+import com.querydsl.jpa.impl.JPAQuery;
+import com.querydsl.jpa.impl.JPAUpdateClause;
+
+@SuppressWarnings("all")
+@NoRepositoryBean
+public interface JpaPlusRepository<T, ID> extends JpaRepositoryImplementation<T, ID>, QuerydslPredicateExecutor<T> {
+
+    T findOne(ID id);
+
+    @Override
+    List<T> findAll(Predicate predicate);
+
+    @Override
+    List<T> findAll(Predicate predicate, Sort sort);
+
+    <S> Page<S> findAll(JPAQuery<S> query, Pageable pageable);
+
+    <S> Page<S> findAllNoCount(JPAQuery<S> query, Pageable pageable, long count);
+
+    <O extends T> List<O> insert(O... entities);
+
+    int insertOrUpdate(T... entities);
+
+    int delete(ID... ids);
+
+    int delete(JPADeleteClause delete);
+
+    int delete(JPADeleteClause delete, Predicate... predicate);
+
+    int update(JPAUpdateClause update);
+
+    int update(JPAUpdateClause update, Predicate... predicate);
+}

+ 179 - 0
src/main/java/com/xiesx/fastboot/core/jpa/JpaPlusRepositoryExecutor.java

@@ -0,0 +1,179 @@
+package com.xiesx.fastboot.core.jpa;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Optional;
+import java.util.function.LongSupplier;
+
+import javax.persistence.EntityManager;
+
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.Pageable;
+import org.springframework.data.domain.Sort;
+import org.springframework.data.jpa.repository.support.JpaEntityInformationSupport;
+import org.springframework.data.jpa.repository.support.Querydsl;
+import org.springframework.data.jpa.repository.support.QuerydslJpaPredicateExecutor;
+import org.springframework.data.jpa.repository.support.SimpleJpaRepository;
+import org.springframework.data.querydsl.SimpleEntityPathResolver;
+import org.springframework.data.repository.support.PageableExecutionUtils;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.util.Assert;
+
+import com.querydsl.core.types.EntityPath;
+import com.querydsl.core.types.OrderSpecifier;
+import com.querydsl.core.types.Predicate;
+import com.querydsl.core.types.dsl.PathBuilder;
+import com.querydsl.jpa.JPQLQuery;
+import com.querydsl.jpa.impl.JPADeleteClause;
+import com.querydsl.jpa.impl.JPAQuery;
+import com.querydsl.jpa.impl.JPAQueryFactory;
+import com.querydsl.jpa.impl.JPAUpdateClause;
+
+@SuppressWarnings("all")
+@Transactional(readOnly = true)
+public class JpaPlusRepositoryExecutor<T, ID> extends SimpleJpaRepository<T, ID> implements JpaPlusRepository<T, ID> {
+
+    protected final EntityManager entityManager;
+
+    protected final JPAQueryFactory jpaQueryFactory;
+
+    private final EntityPath<T> path;
+
+    protected final Querydsl querydsl;
+
+    protected final QuerydslJpaPredicateExecutor<T> jpaPredicateExecutor;
+
+    public JpaPlusRepositoryExecutor(Class<T> domainClass, EntityManager entityManager) {
+        super(domainClass, entityManager);
+        this.entityManager = entityManager;
+        this.jpaQueryFactory = new JPAQueryFactory(entityManager);
+        this.path = SimpleEntityPathResolver.INSTANCE.createPath(domainClass);
+        this.querydsl = new Querydsl(entityManager, new PathBuilder<T>(path.getType(), path.getMetadata()));
+        this.jpaPredicateExecutor =
+                new QuerydslJpaPredicateExecutor<>(JpaEntityInformationSupport.getEntityInformation(domainClass, entityManager),
+                        entityManager, SimpleEntityPathResolver.INSTANCE, getRepositoryMethodMetadata());
+    }
+
+    // ========================== QuerydslPredicateExecutor
+
+    @Override
+    public Optional<T> findOne(Predicate predicate) {
+        return jpaPredicateExecutor.findOne(predicate);
+    }
+
+    @Override
+    public List<T> findAll(Predicate predicate) {
+        return jpaPredicateExecutor.findAll(predicate);
+    }
+
+    @Override
+    public List<T> findAll(Predicate predicate, Sort sort) {
+        return jpaPredicateExecutor.findAll(predicate, sort);
+    }
+
+    @Override
+    public List<T> findAll(OrderSpecifier<?>... orders) {
+        return jpaPredicateExecutor.findAll(orders);
+    }
+
+    @Override
+    public List<T> findAll(Predicate predicate, OrderSpecifier<?>... orders) {
+        return jpaPredicateExecutor.findAll(predicate, orders);
+    }
+
+    @Override
+    public Page<T> findAll(Predicate predicate, Pageable pageable) {
+        return jpaPredicateExecutor.findAll(predicate, pageable);
+    }
+
+    public <S> Page<S> findAll(JPAQuery<S> query, Pageable pageable, OrderSpecifier<?>... orders) {
+        // 分页查询
+        JPQLQuery<S> jpqlQuery = querydsl.applyPagination(pageable, query).orderBy(orders);
+        // 构造分页
+        return PageableExecutionUtils.getPage(jpqlQuery.fetch(), pageable, query::fetchCount);
+    }
+
+    @Override
+    public long count(Predicate predicate) {
+        return jpaPredicateExecutor.count(predicate);
+    }
+
+    @Override
+    public boolean exists(Predicate predicate) {
+        return jpaPredicateExecutor.exists(predicate);
+    }
+
+    // ========================== JpaPlusRepository
+
+    @Override
+    public T findOne(ID id) {
+        Optional<T> optional = findById(id);
+        return (optional.isPresent()) ? optional.get() : null;
+    }
+
+    @Override
+    public <S> Page<S> findAll(JPAQuery<S> query, Pageable pageable) {
+        // 分页查询
+        JPQLQuery<S> jpqlQuery = querydsl.applyPagination(pageable, query);
+        // 构造分页
+        return PageableExecutionUtils.getPage(jpqlQuery.fetch(), pageable, query::fetchCount);
+    }
+
+    @Override
+    public <S> Page<S> findAllNoCount(JPAQuery<S> query, Pageable pageable, long count) {
+        // 分页查询
+        JPQLQuery<S> jpqlQuery = querydsl.applyPagination(pageable, query);
+        // 构造分页
+        LongSupplierImpl longSupplier =new LongSupplierImpl(count);
+        return PageableExecutionUtils.getPage(jpqlQuery.fetch(), pageable, longSupplier);
+    }
+
+    @Transactional
+    @Override
+    public <S extends T> List<S> insert(S... entities) {
+        List<S> list = saveAll(Arrays.asList(entities));
+        entityManager.flush();
+        return list;
+    }
+
+    @Transactional
+    @Override
+    public int insertOrUpdate(T... entities) {
+        int row = insert(entities).size();
+        return row;
+    }
+
+    @Transactional
+    @Override
+    public int delete(ID... ids) {
+        Assert.notNull(ids, "ids must not be null!");
+        int rows = 0;
+        for (ID id : ids) {
+            deleteById(id);
+            rows++;
+        }
+        return rows;
+    }
+
+    @Override
+    public int delete(JPADeleteClause delete) {
+        return (int) delete.execute();
+    }
+
+    @Override
+    public int delete(JPADeleteClause delete, Predicate... predicate) {
+        return (int) delete.where(predicate).execute();
+    }
+
+    @Transactional
+    @Override
+    public int update(JPAUpdateClause update) {
+        return (int) update.execute();
+    }
+
+    @Transactional
+    @Override
+    public int update(JPAUpdateClause update, Predicate... predicate) {
+        return (int) update.where(predicate).execute();
+    }
+}

+ 18 - 0
src/main/java/com/xiesx/fastboot/core/jpa/LongSupplierImpl.java

@@ -0,0 +1,18 @@
+package com.xiesx.fastboot.core.jpa;
+
+import org.springframework.stereotype.Component;
+
+import java.util.function.LongSupplier;
+
+public class LongSupplierImpl implements LongSupplier {
+
+    private long count;
+    public LongSupplierImpl(long count){
+        this.count= count;
+    }
+
+    @Override
+    public long getAsLong() {
+        return count;
+    }
+}

+ 25 - 0
src/main/java/com/xiesx/fastboot/core/jpa/annotation/EnableJpaPlusRepositories.java

@@ -0,0 +1,25 @@
+package com.xiesx.fastboot.core.jpa.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import org.springframework.context.annotation.Import;
+import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
+
+import com.xiesx.fastboot.core.jpa.cfg.JpaPlusCfg;
+import com.xiesx.fastboot.core.jpa.factory.JpaPlusRepositoryFactoryBean;
+
+/**
+ * @title EnableJpaPlusRepositories.java
+ * @description
+ * @author Sixian.xie
+ * @date 2020-7-21 22:32:26
+ */
+@EnableJpaRepositories(repositoryFactoryBeanClass = JpaPlusRepositoryFactoryBean.class)
+@Target(ElementType.TYPE)
+@Retention(RetentionPolicy.RUNTIME)
+@Import({JpaPlusCfg.class})
+public @interface EnableJpaPlusRepositories {
+}

+ 24 - 0
src/main/java/com/xiesx/fastboot/core/jpa/cfg/JpaPlusCfg.java

@@ -0,0 +1,24 @@
+package com.xiesx.fastboot.core.jpa.cfg;
+
+import javax.persistence.EntityManager;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
+
+import com.querydsl.jpa.impl.JPAQueryFactory;
+
+/**
+ * @title TokenCfg.java
+ * @description 令牌认证
+ * @author Sixian.xie
+ * @date 2020-7-21 22:36:02
+ */
+@Configuration
+public class JpaPlusCfg implements WebMvcConfigurer {
+
+    @Bean
+    public JPAQueryFactory jpaQuery(EntityManager entityManager) {
+        return new JPAQueryFactory(entityManager);
+    }
+}

+ 15 - 0
src/main/java/com/xiesx/fastboot/core/jpa/entity/JpaPlusEntity.java

@@ -0,0 +1,15 @@
+package com.xiesx.fastboot.core.jpa.entity;
+
+import java.io.Serializable;
+
+import com.alibaba.fastjson.JSON;
+
+public abstract class JpaPlusEntity<T> implements Serializable {
+
+    /** 序列化 */
+    private static final long serialVersionUID = 1L;
+
+    public String toJSONString() {
+        return JSON.toJSONString(this);
+    }
+}

+ 27 - 0
src/main/java/com/xiesx/fastboot/core/jpa/factory/JpaPlusRepositoryFactory.java

@@ -0,0 +1,27 @@
+package com.xiesx.fastboot.core.jpa.factory;
+
+import javax.persistence.EntityManager;
+
+import org.springframework.data.jpa.repository.support.JpaRepositoryFactory;
+import org.springframework.data.jpa.repository.support.JpaRepositoryImplementation;
+import org.springframework.data.repository.core.RepositoryInformation;
+import org.springframework.data.repository.core.RepositoryMetadata;
+
+import com.xiesx.fastboot.core.jpa.JpaPlusRepositoryExecutor;
+
+public class JpaPlusRepositoryFactory extends JpaRepositoryFactory {
+
+    public JpaPlusRepositoryFactory(EntityManager entityManager) {
+        super(entityManager);
+    }
+
+    @Override
+    protected Class<?> getRepositoryBaseClass(RepositoryMetadata metadata) {
+        return JpaPlusRepositoryExecutor.class;
+    }
+
+    @Override
+    protected JpaRepositoryImplementation<?, ?> getTargetRepository(RepositoryInformation information, EntityManager entityManager) {
+        return new JpaPlusRepositoryExecutor<>(information.getDomainType(), entityManager);
+    }
+}

+ 19 - 0
src/main/java/com/xiesx/fastboot/core/jpa/factory/JpaPlusRepositoryFactoryBean.java

@@ -0,0 +1,19 @@
+package com.xiesx.fastboot.core.jpa.factory;
+
+import javax.persistence.EntityManager;
+
+import org.springframework.data.jpa.repository.support.JpaRepositoryFactoryBean;
+import org.springframework.data.repository.Repository;
+import org.springframework.data.repository.core.support.RepositoryFactorySupport;
+
+public class JpaPlusRepositoryFactoryBean<T extends Repository<S, ID>, S, ID> extends JpaRepositoryFactoryBean<T, S, ID> {
+
+    public JpaPlusRepositoryFactoryBean(Class<? extends T> repositoryInterface) {
+        super(repositoryInterface);
+    }
+
+    @Override
+    protected RepositoryFactorySupport createRepositoryFactory(EntityManager entityManager) {
+        return new JpaPlusRepositoryFactory(entityManager);
+    }
+}

+ 128 - 0
src/main/java/com/xiesx/fastboot/core/jpa/identifier/IdWorker.java

@@ -0,0 +1,128 @@
+package com.xiesx.fastboot.core.jpa.identifier;
+
+import java.util.UUID;
+import java.util.concurrent.ThreadLocalRandom;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * @title IdWorker.java
+ * @description tweeter的snowflake 移植到Java: (a) id构成: 42位的时间前缀 + 10位的节点标识 +
+ *              12位的sequence避免并发的数字(12位不够用时强制得到新的时间前缀) 注意这里进行了小改动: snowkflake是5位的datacenter加5位的机器id;
+ *              这里变成使用10位的机器id (b) 对系统时间的依赖性非常强,需关闭ntp的时间同步功能。当检测到ntp时间调整后,将会拒绝分配id
+ * @author Sixian.xie
+ * @date 2020-7-21 22:32:56
+ */
+public class IdWorker {
+
+    private final static Logger logger = LoggerFactory.getLogger(IdWorker.class);
+
+    private final long workerId;
+
+    private final long epoch = 1577808000000L; // 时间起始标记点,作为基准,一般取系统的最近时间
+
+    private final long workerIdBits = 10L; // 机器标识位数
+
+    private final long maxWorkerId = -1L ^ -1L << this.workerIdBits;// 机器ID最大值: 1023
+
+    private long sequence = 0L; // 0,并发控制
+
+    private final long sequenceBits = 12L; // 毫秒内自增位
+
+    private final long workerIdShift = this.sequenceBits; // 12
+
+    private final long timestampLeftShift = this.sequenceBits + this.workerIdBits;// 22
+
+    private final long sequenceMask = -1L ^ -1L << this.sequenceBits; // 4095,111111111111,12位
+
+    private long lastTimestamp = -1L;
+
+    private IdWorker(long workerId) {
+        if (workerId > this.maxWorkerId || workerId < 0) {
+            throw new IllegalArgumentException(String.format("worker Id can't be greater than %d or less than 0", this.maxWorkerId));
+        }
+        this.workerId = workerId;
+    }
+
+    /**
+     * 等待下一个毫秒的到来, 保证返回的毫秒数在参数lastTimestamp之后
+     */
+    private long tilNextMillis(long lastTimestamp) {
+        long timestamp = this.timeGen();
+        while (timestamp <= lastTimestamp) {
+            timestamp = this.timeGen();
+        }
+        return timestamp;
+    }
+
+    /**
+     * 获得系统当前毫秒数
+     */
+    private long timeGen() {
+        return System.currentTimeMillis();
+    }
+
+    public synchronized long nextId() throws Exception {
+        long timestamp = this.timeGen();
+        if (this.lastTimestamp == timestamp) { // 如果上一个timestamp与新产生的相等,则sequence加一(0-4095循环);
+                                               // 对新的timestamp,sequence从0开始
+            this.sequence = this.sequence + 1 & this.sequenceMask;
+            if (this.sequence == 0) {
+                timestamp = this.tilNextMillis(this.lastTimestamp);// 重新生成timestamp
+            }
+        } else {
+            this.sequence = 0;
+        }
+
+        if (timestamp < this.lastTimestamp) {
+            logger.error(
+                    String.format("clock moved backwards.Refusing to generate id for %d milliseconds", (this.lastTimestamp - timestamp)));
+            throw new Exception(
+                    String.format("clock moved backwards.Refusing to generate id for %d milliseconds", (this.lastTimestamp - timestamp)));
+        }
+
+        this.lastTimestamp = timestamp;
+        return timestamp - this.epoch << this.timestampLeftShift | this.workerId << this.workerIdShift | this.sequence;
+    }
+
+    private static IdWorker flowIdWorker = new IdWorker(1);
+
+    public static IdWorker getFlowIdWorkerInstance() {
+        return flowIdWorker;
+    }
+
+    public static Long getID() {
+        try {
+            IdWorker idWorker = IdWorker.getFlowIdWorkerInstance();
+            return idWorker.nextId();
+        } catch (Exception e) {
+            return null;
+        }
+    }
+
+    public static String getIDStr() {
+        try {
+            return String.valueOf(getID());
+        } catch (Exception e) {
+            return null;
+        }
+    }
+
+    public static String getUUID() {
+        return getUUIDStr().replace("-", "");
+    }
+
+    public static String getUUIDStr() {
+        ThreadLocalRandom random = ThreadLocalRandom.current();
+        return new UUID(random.nextLong(), random.nextLong()).toString();
+    }
+
+    public static void main(String[] args) {
+        // System.out.println(Long.toBinaryString(idWorker.nextId()));
+        System.out.println(getID());
+        System.out.println(getIDStr());
+        System.out.println(getUUID());
+        System.out.println(getUUIDStr());
+    }
+}

+ 31 - 0
src/main/java/com/xiesx/fastboot/core/jpa/identifier/IdWorkerGenerator.java

@@ -0,0 +1,31 @@
+package com.xiesx.fastboot.core.jpa.identifier;
+
+import java.io.Serializable;
+import java.util.Properties;
+
+import org.apache.commons.lang3.StringUtils;
+import org.hibernate.HibernateException;
+import org.hibernate.MappingException;
+import org.hibernate.engine.spi.SharedSessionContractImplementor;
+import org.hibernate.id.Configurable;
+import org.hibernate.id.IdentifierGenerator;
+import org.hibernate.service.ServiceRegistry;
+import org.hibernate.type.Type;
+
+public class IdWorkerGenerator implements Configurable, IdentifierGenerator {
+
+    public String pre;
+
+    @Override
+    public void configure(Type type, Properties params, ServiceRegistry serviceRegistry) throws MappingException {
+        this.pre = params.getProperty("prefix");
+    }
+
+    @Override
+    public Serializable generate(SharedSessionContractImplementor sharedSessionContractImplementor, Object o) throws HibernateException {
+        if (StringUtils.isNoneEmpty(this.pre)) {
+            return this.pre + IdWorker.getID();
+        }
+        return IdWorker.getID();
+    }
+}

+ 467 - 0
src/main/java/com/xiesx/fastboot/core/scheduler/ScheduleHelper.java

@@ -0,0 +1,467 @@
+package com.xiesx.fastboot.core.scheduler;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.commons.lang3.ObjectUtils;
+import org.quartz.*;
+import org.quartz.impl.matchers.GroupMatcher;
+
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import com.xiesx.fastboot.SpringHelper;
+import com.xiesx.fastboot.core.exception.RunExc;
+import com.xiesx.fastboot.core.exception.RunException;
+
+public class ScheduleHelper {
+
+    private static Scheduler scheduler = SpringHelper.getBean(Scheduler.class);
+
+    private static String JOB_GROUP_NAME = "FAST_JOB_GROUP_NAME";
+
+    // ============================
+
+    /**
+     * 增加SimpleJob
+     *
+     * @param job 任务名称
+     * @param cls 任务实现类
+     * @param interval 时间表达式 (这是每隔多少秒为一次任务)
+     * @param repeat 运行的次数 (<0:表示不限次数)
+     */
+    public static void addJob(String job, Class<? extends Job> cls, int interval, int repeat) {
+        // 创建
+        addJob(job, cls, interval, repeat, null);
+    }
+
+    /**
+     * 增加SimpleJob
+     *
+     * @param job 任务名称
+     * @param cls 任务实现类
+     * @param interval 时间表达式 (这是每隔多少秒为一次任务)
+     * @param repeat 运行的次数 (<0:表示不限次数)
+     * @param data 参数
+     */
+    public static void addJob(String job, Class<? extends Job> cls, int interval, int repeat,
+            Map<? extends String, ? extends Object> data) {
+        // 构建SimpleScheduleBuilder规则
+        SimpleScheduleBuilder simpleScheduleBuilder = SimpleScheduleBuilder.simpleSchedule() // 几秒钟重复执行
+                .withIntervalInSeconds(interval);
+        if (repeat > 0) {
+            // 重复次数
+            simpleScheduleBuilder.withRepeatCount(repeat);
+        }
+        // 一直执行
+        simpleScheduleBuilder.repeatForever();
+        // 创建
+        createJob(job, cls, simpleScheduleBuilder, data);
+    }
+
+    /**
+     * 增加SimpleJob
+     *
+     * @param job 任务名称
+     * @param group 任务组名
+     * @param cls 任务实现类
+     * @param interval 时间表达式 (这是每隔多少秒为一次任务)
+     * @param repeat 运行的次数 (<0:表示不限次数)
+     * @param data 参数
+     */
+    public static void addJob(String job, String group, Class<? extends Job> cls, int interval, int repeat,
+            Map<? extends String, ? extends Object> data) {
+        // 构建SimpleScheduleBuilder规则
+        SimpleScheduleBuilder simpleScheduleBuilder = SimpleScheduleBuilder.simpleSchedule()
+                // 几秒钟重复执行
+                .withIntervalInSeconds(interval);
+        if (repeat > 0) {
+            // 重复次数
+            simpleScheduleBuilder.withRepeatCount(repeat);
+        }
+        // 一直执行
+        simpleScheduleBuilder.repeatForever();
+        // 创建
+        createJob(job, group, cls, simpleScheduleBuilder, data);
+    }
+
+
+    /**
+     * 增加CronJob
+     *
+     * @param job 任务名称
+     * @param cls 任务实现类
+     * @param cron 时间表达式 (如:0/5 * * * * ? )
+     */
+    public static void addJob(String job, Class<? extends Job> cls, String cron) {
+        addJob(job, cls, cron, null);
+    }
+
+    /**
+     * 增加CronJob
+     *
+     * @param job 任务名称
+     * @param cls 任务实现类
+     * @param cron 时间表达式 (如:0/5 * * * * ? )
+     * @param data 参数
+     */
+    public static void addJob(String job, Class<? extends Job> cls, String cron, Map<? extends String, ? extends Object> data) {
+        // 构建CronScheduleBuilder规则
+        CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(cron);
+        // 创建
+        createJob(job, cls, cronScheduleBuilder, data);
+    }
+
+    /**
+     * 增加CronJob
+     *
+     * @param job 任务名称
+     * @param group 任务组名
+     * @param cls 任务实现类
+     * @param cron 时间表达式 (如:0/5 * * * * ? )
+     * @param data 参数
+     */
+    public static void addJob(String job, String group, Class<? extends Job> cls, String cron,
+            Map<? extends String, ? extends Object> data) {
+        // 构建CronScheduleBuilder规则
+        CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(cron);
+        // 创建
+        createJob(job, group, cls, cronScheduleBuilder, data);
+    }
+
+
+    // ============================
+
+    /**
+     * 创建
+     *
+     * @param cls 任务实现类
+     * @param ScheduleBuilder 时间表达式 (这是每隔多少秒为一次任务)
+     * @param data 参数
+     */
+    public static void createJob(String job, Class<? extends Job> cls, ScheduleBuilder<? extends Trigger> ScheduleBuilder,
+            Map<? extends String, ? extends Object> data) {
+        createJob(job, JOB_GROUP_NAME, cls, ScheduleBuilder, data);
+    }
+
+    /**
+     * 创建
+     *
+     * @param job 任务名称
+     * @param group 任务组名
+     * @param cls 任务实现类
+     * @param interval 时间表达式 (这是每隔多少秒为一次任务)
+     * @param repeat 运行的次数 (<0:表示不限次数)
+     * @param data 参数
+     */
+    public static void createJob(String job, String group, Class<? extends Job> cls, ScheduleBuilder<? extends Trigger> ScheduleBuilder,
+            Map<? extends String, ? extends Object> data) {
+        try {
+            // 构建实例
+            JobDetail jobDetail = JobBuilder.newJob(cls).withIdentity(job, group).build();
+            // 设置参数
+            if (data != null && data.size() > 0) {
+                jobDetail.getJobDataMap().putAll(data);
+            }
+            // 构建触发器
+            TriggerBuilder<Trigger> triggerBuilder = TriggerBuilder.newTrigger().withIdentity(job, group);
+            triggerBuilder.withSchedule(ScheduleBuilder);
+            triggerBuilder.startNow();
+            scheduler.scheduleJob(jobDetail, triggerBuilder.build());
+        } catch (SchedulerException e) {
+            e.printStackTrace();
+        }
+    }
+
+    // ============================
+
+    /**
+     * 修改
+     *
+     * @param cron
+     */
+    public static void updateJob(String job, int interval, int repeat) {
+        updateJob(job, JOB_GROUP_NAME, interval, repeat);
+    }
+
+    /**
+     * 修改
+     *
+     * @param cron
+     */
+    public static void updateJob(String job, String cron) {
+        updateJob(job, JOB_GROUP_NAME, cron);
+    }
+
+    /**
+     * 修改
+     *
+     * @param job
+     * @param group
+     * @param cron
+     */
+    public static void updateJob(String job, String group, int interval, int repeat) {
+        if (ObjectUtils.isEmpty(scheduler)) {
+            throw new RunException(RunExc.RUNTIME, "please check pom.xml -> spring-boot-starter-quartz");
+        }
+        try {
+            TriggerKey triggerKey = TriggerKey.triggerKey(job, group);
+            SimpleTrigger trigger = (SimpleTrigger) scheduler.getTrigger(triggerKey);
+            //
+            SimpleScheduleBuilder simpleScheduleBuilder = SimpleScheduleBuilder.simpleSchedule() // 几秒钟重复执行
+                    .withIntervalInSeconds(interval);
+            if (repeat > 0) {
+                // 重复次数
+                simpleScheduleBuilder.withRepeatCount(repeat);
+            }
+            // 一直执行
+            simpleScheduleBuilder.repeatForever();
+            trigger = trigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(simpleScheduleBuilder).build();
+            // 重启触发器
+            scheduler.rescheduleJob(triggerKey, trigger);
+        } catch (SchedulerException e) {
+            e.printStackTrace();
+        }
+    }
+
+    /**
+     * 修改
+     *
+     * @param job
+     * @param group
+     * @param cron
+     */
+    public static void updateJob(String job, String group, String cron) {
+        if (ObjectUtils.isEmpty(scheduler)) {
+            throw new RunException(RunExc.RUNTIME, "please check pom.xml -> spring-boot-starter-quartz");
+        }
+        try {
+            TriggerKey triggerKey = TriggerKey.triggerKey(job, group);
+            CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);
+            //
+            trigger = trigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(CronScheduleBuilder.cronSchedule(cron)).build();
+            // 重启触发器
+            scheduler.rescheduleJob(triggerKey, trigger);
+        } catch (SchedulerException e) {
+            e.printStackTrace();
+        }
+    }
+
+    // ============================
+
+    /**
+     * 删除
+     */
+    public static void deleteJob(String job) {
+        deleteJob(job, JOB_GROUP_NAME);
+    }
+
+    /**
+     * 删除
+     *
+     * @param job 任务名称
+     * @param group 任务组名
+     */
+    public static void deleteJob(String job, String group) {
+        if (ObjectUtils.isEmpty(scheduler)) {
+            throw new RunException(RunExc.RUNTIME, "please check pom.xml -> spring-boot-starter-quartz");
+        }
+        try {
+            scheduler.deleteJob(new JobKey(job, group));
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+
+    // ============================
+
+    /**
+     * 暂停
+     */
+    public static void pauseJob(String job) {
+        pauseJob(job, JOB_GROUP_NAME);
+    }
+
+    /**
+     * 暂停
+     *
+     * @param job
+     * @param group
+     */
+    public static void pauseJob(String job, String group) {
+        if (ObjectUtils.isEmpty(scheduler)) {
+            throw new RunException(RunExc.RUNTIME, "please check pom.xml -> spring-boot-starter-quartz");
+        }
+        try {
+            JobKey jobKey = JobKey.jobKey(job, group);
+            scheduler.pauseJob(jobKey);
+        } catch (SchedulerException e) {
+            e.printStackTrace();
+        }
+    }
+
+    // ============================
+
+    /**
+     * 恢复
+     */
+    public static void resumeJob(String job) {
+        resumeJob(job, JOB_GROUP_NAME);
+    }
+
+    /**
+     * 恢复
+     *
+     * @param job
+     * @param group
+     */
+    public static void resumeJob(String job, String group) {
+        if (ObjectUtils.isEmpty(scheduler)) {
+            throw new RunException(RunExc.RUNTIME, "please check pom.xml -> spring-boot-starter-quartz");
+        }
+        try {
+            JobKey jobKey = JobKey.jobKey(job, group);
+            scheduler.resumeJob(jobKey);
+        } catch (SchedulerException e) {
+            e.printStackTrace();
+        }
+    }
+
+    // ============================
+
+    /**
+     * 立即执行
+     *
+     */
+    public static void runJobNow(String job) {
+        resumeJob(job, JOB_GROUP_NAME);
+    }
+
+    /**
+     * 立即执行
+     *
+     * @param job
+     * @param group
+     */
+    public static void runJobNow(String job, String group) {
+        if (ObjectUtils.isEmpty(scheduler)) {
+            throw new RunException(RunExc.RUNTIME, "please check pom.xml -> spring-boot-starter-quartz");
+        }
+        try {
+            JobKey jobKey = JobKey.jobKey(job, group);
+            scheduler.triggerJob(jobKey);
+        } catch (SchedulerException e) {
+            e.printStackTrace();
+        }
+    }
+
+    // ============================
+
+    /**
+     * 启动任务
+     *
+     * @throws SchedulerException
+     */
+    public static void startJobs() {
+        if (ObjectUtils.isEmpty(scheduler)) {
+            throw new RunException(RunExc.RUNTIME, "please check pom.xml -> spring-boot-starter-quartz");
+        }
+        try {
+            scheduler.start();
+        } catch (SchedulerException e) {
+            e.printStackTrace();
+        }
+    }
+
+    /**
+     * 关闭任务
+     *
+     * @throws SchedulerException
+     */
+    public static void shutdownJobs() {
+        if (ObjectUtils.isEmpty(scheduler)) {
+            throw new RunException(RunExc.RUNTIME, "please check pom.xml -> spring-boot-starter-quartz");
+        }
+        try {
+            if (!scheduler.isShutdown()) {
+                scheduler.shutdown();
+            }
+        } catch (SchedulerException e) {
+            e.printStackTrace();
+        }
+    }
+
+    /**
+     * 获取所有计划中的任务列表
+     *
+     * @return
+     */
+    public static List<Map<String, Object>> queryAllJob() {
+        if (ObjectUtils.isEmpty(scheduler)) {
+            throw new RunException(RunExc.RUNTIME, "please check pom.xml -> spring-boot-starter-quartz");
+        }
+        List<Map<String, Object>> jobList = null;
+        try {
+            GroupMatcher<JobKey> matcher = GroupMatcher.anyJobGroup();
+            Set<JobKey> jobKeys = scheduler.getJobKeys(matcher);
+            jobList = Lists.newArrayList();
+            for (JobKey jobKey : jobKeys) {
+                List<? extends Trigger> triggers = scheduler.getTriggersOfJob(jobKey);
+                for (Trigger trigger : triggers) {
+                    Map<String, Object> map = Maps.newHashMap();
+                    map.put("key", trigger.getKey());
+                    map.put("job", jobKey.getName());
+                    map.put("group", jobKey.getGroup());
+                    Trigger.TriggerState triggerState = scheduler.getTriggerState(trigger.getKey());
+                    map.put("status", triggerState.name());
+                    if (trigger instanceof CronTrigger) {
+                        CronTrigger cronTrigger = (CronTrigger) trigger;
+                        String cronExpression = cronTrigger.getCronExpression();
+                        map.put("cron", cronExpression);
+                    }
+                    jobList.add(map);
+                }
+            }
+        } catch (SchedulerException e) {
+            e.printStackTrace();
+        }
+        return jobList;
+    }
+
+    /**
+     * 获取所有正在运行的job
+     *
+     * @return
+     */
+    public static List<Map<String, Object>> queryRunJob() {
+        if (ObjectUtils.isEmpty(scheduler)) {
+            throw new RunException(RunExc.RUNTIME, "please check pom.xml -> spring-boot-starter-quartz");
+        }
+        List<Map<String, Object>> jobList = null;
+        try {
+            List<JobExecutionContext> executingJobs = scheduler.getCurrentlyExecutingJobs();
+            jobList = new ArrayList<>(executingJobs.size());
+            for (JobExecutionContext executingJob : executingJobs) {
+                Map<String, Object> map = Maps.newHashMap();
+                JobDetail jobDetail = executingJob.getJobDetail();
+                JobKey jobKey = jobDetail.getKey();
+                Trigger trigger = executingJob.getTrigger();
+                map.put("key", trigger.getKey());
+                map.put("job", jobKey.getName());
+                map.put("group", jobKey.getGroup());
+                Trigger.TriggerState triggerState = scheduler.getTriggerState(trigger.getKey());
+                map.put("status", triggerState.name());
+                if (trigger instanceof CronTrigger) {
+                    CronTrigger cronTrigger = (CronTrigger) trigger;
+                    String cronExpression = cronTrigger.getCronExpression();
+                    map.put("cron", cronExpression);
+                }
+                jobList.add(map);
+            }
+        } catch (SchedulerException e) {
+            e.printStackTrace();
+        }
+        return jobList;
+    }
+}

+ 45 - 0
src/main/java/com/xiesx/fastboot/core/scheduler/decorator/BaseDecorator.java

@@ -0,0 +1,45 @@
+package com.xiesx.fastboot.core.scheduler.decorator;
+
+/**
+ * @title BaseDecorator.java
+ * @description 装饰器父类
+ * @author Sixian.xie
+ * @date 2020-7-21 22:42:52
+ */
+public class BaseDecorator implements ISchedule {
+
+    /**
+     * 被装饰对象
+     */
+    protected ISchedule decoratedJob;
+
+    /**
+     * 构造
+     */
+    public BaseDecorator() {}
+
+    /**
+     * 构造
+     *
+     * @param decoratedJob
+     */
+    public BaseDecorator(ISchedule decoratedJob) {
+        this.decoratedJob = decoratedJob;
+    }
+
+    /**
+     * 初始化
+     */
+    @Override
+    public void init() {
+        decoratedJob.init();
+    }
+
+    /**
+     * 是否启动
+     */
+    @Override
+    public boolean isStart() {
+        return false;
+    }
+}

+ 20 - 0
src/main/java/com/xiesx/fastboot/core/scheduler/decorator/ISchedule.java

@@ -0,0 +1,20 @@
+package com.xiesx.fastboot.core.scheduler.decorator;
+
+/**
+ * @title SimpleInterface.java
+ * @description 装饰起接口
+ * @author Sixian.xie
+ * @date 2019年3月4日 下午1:07:35
+ */
+public interface ISchedule {
+
+    /**
+     * 初始化
+     */
+    public void init();
+
+    /**
+     * 是否启动
+     */
+    public boolean isStart();
+}

+ 144 - 0
src/main/java/com/xiesx/fastboot/core/sign/SignalAspect.java

@@ -0,0 +1,144 @@
+package com.xiesx.fastboot.core.sign;
+
+import java.lang.reflect.Method;
+import java.util.*;
+
+import javax.servlet.http.HttpServletRequest;
+
+import org.apache.commons.codec.digest.DigestUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.aspectj.lang.ProceedingJoinPoint;
+import org.aspectj.lang.annotation.Around;
+import org.aspectj.lang.annotation.Aspect;
+import org.aspectj.lang.annotation.Pointcut;
+import org.aspectj.lang.reflect.MethodSignature;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.core.annotation.Order;
+import org.springframework.stereotype.Component;
+import org.springframework.web.context.request.RequestContextHolder;
+import org.springframework.web.context.request.ServletRequestAttributes;
+
+import com.google.common.collect.Maps;
+import com.xiesx.fastboot.core.exception.RunExc;
+import com.xiesx.fastboot.core.exception.RunException;
+import com.xiesx.fastboot.core.sign.annotation.GoSign;
+import com.xiesx.fastboot.core.sign.cfg.SignalProperties;
+
+import lombok.extern.slf4j.Slf4j;
+
+/**
+ * @title SignalAspect.java
+ * @description 数据签名切面
+ * @author Sixian.xie
+ * @date 2020-7-21 22:35:39
+ */
+@Slf4j
+@Component
+@Aspect
+@Order(-98)
+public class SignalAspect {
+
+    public static final String SIGN_KEY = "sign";
+
+    public static final String SIGN_VAL = "123456780";
+
+    @Autowired
+    private SignalProperties properties;
+
+    private String key;
+
+    private String secret;
+
+    @Pointcut("@annotation(com.xiesx.fastboot.core.sign.annotation.GoSign)")
+    public void signPointcut() {
+        log.debug("signPointcut=====");
+    }
+
+    @Around("signPointcut()")
+    public Object signBeforeAspect(ProceedingJoinPoint pjp) throws RunException, Throwable {
+        // 获取配置
+        key = StringUtils.isNotEmpty(properties.getHeader()) ? properties.getHeader() : SIGN_KEY;
+        secret = StringUtils.isNotEmpty(properties.getSecret()) ? properties.getSecret() : SIGN_VAL;
+        // 获取方法信息
+        MethodSignature signature = (MethodSignature) pjp.getSignature();
+        Method method = signature.getMethod();
+        // 获取注解信息
+        GoSign annotation = method.getAnnotation(GoSign.class);
+        Boolean isIgnore = annotation == null ? false : !annotation.ignore();
+        // 获取请求信息
+        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
+        // 获取参数
+        Map<String, String> parms = Maps.newConcurrentMap();
+        Enumeration<String> names = request.getParameterNames();
+        while (names.hasMoreElements()) {
+            String name = names.nextElement();
+            String value = request.getParameter(name);
+            parms.put(name, value);
+        }
+        // 是否进行效验
+        if (isIgnore && !parms.isEmpty()) {
+            // 从header中获取sign
+            String headerSign = request.getHeader(key);
+            // sign为空
+            if (StringUtils.isEmpty(headerSign)) {
+                throw new RunException(RunExc.SIGN, "非法请求");
+            }
+            // sign错误
+            if (!getSignature(parms, secret).equals(headerSign)) {
+                throw new RunException(RunExc.SIGN, "验签失败");
+            }
+        }
+        return pjp.proceed();
+    }
+
+    /**
+     * 获取签名
+     *
+     * @param params
+     * @param key
+     * @return
+     */
+    public static String getSignature(Map<String, String> params, String key) {
+        return DigestUtils.md5Hex(getSortParams(params) + "&key=" + key);
+    }
+
+    /**
+     * 按key进行正序排列,之间以&相连 <功能描述>
+     *
+     * @param params
+     * @return
+     */
+    public static String getSortParams(Map<String, String> params) {
+        SortedMap<String, String> map = Maps.newTreeMap();
+        for (String key : params.keySet()) {
+            map.put(key, params.get(key));
+        }
+        Set<String> keySet = map.keySet();
+        Iterator<String> iter = keySet.iterator();
+        String str = "";
+        while (iter.hasNext()) {
+            String key = iter.next();
+            String value = map.get(key);
+            str += key + "=" + value + "&";
+        }
+        if (str.length() > 0) {
+            str = str.substring(0, str.length() - 1);
+        }
+        return str;
+    }
+
+    public static void main(String[] args) {
+        Map<String, String> params = Maps.newHashMap();
+        params.put("a", "1");
+        params.put("b", "2");
+        System.out.println(getSignature(params, SIGN_VAL));
+
+        params = Maps.newHashMap();
+        params.put("url", "https://www.iqiyi.com/v_19rsmej7tw.html?vfm=2008_aldbd");
+        System.out.println(getSignature(params, SIGN_VAL));
+
+        params = Maps.newHashMap();
+        params.put("url", "https://v.youku.com/v_show/id_XNDE0ODQ5NzczNg==.html");
+        System.out.println(getSignature(params, SIGN_VAL));
+    }
+}

+ 16 - 0
src/main/java/com/xiesx/fastboot/core/sign/annotation/GoEnableSign.java

@@ -0,0 +1,16 @@
+package com.xiesx.fastboot.core.sign.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import org.springframework.context.annotation.Import;
+
+import com.xiesx.fastboot.core.sign.cfg.SignalCfg;
+
+@Target({ElementType.TYPE})
+@Retention(RetentionPolicy.RUNTIME)
+@Import({SignalCfg.class})
+public @interface GoEnableSign {
+}

+ 19 - 0
src/main/java/com/xiesx/fastboot/core/sign/annotation/GoSign.java

@@ -0,0 +1,19 @@
+package com.xiesx.fastboot.core.sign.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * @title GoSign.java
+ * @description 数据签名
+ * @author Sixian.xie
+ * @date 2020-7-21 22:35:16
+ */
+@Target(ElementType.METHOD)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface GoSign {
+
+    boolean ignore() default false;
+}

+ 14 - 0
src/main/java/com/xiesx/fastboot/core/sign/cfg/SignalCfg.java

@@ -0,0 +1,14 @@
+package com.xiesx.fastboot.core.sign.cfg;
+
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Import;
+
+import com.xiesx.fastboot.core.sign.SignalAspect;
+
+@Configuration
+@EnableConfigurationProperties(SignalProperties.class)
+@Import({SignalAspect.class})
+public class SignalCfg {
+
+}

+ 16 - 0
src/main/java/com/xiesx/fastboot/core/sign/cfg/SignalProperties.java

@@ -0,0 +1,16 @@
+package com.xiesx.fastboot.core.sign.cfg;
+
+import org.springframework.boot.context.properties.ConfigurationProperties;
+
+import lombok.Data;
+
+@Data
+@ConfigurationProperties(prefix = SignalProperties.PREFIX)
+public class SignalProperties {
+
+    public static final String PREFIX = "fastboot.sign";
+
+    private String header = "sign";
+
+    private String secret = "123456790";
+}

+ 4 - 0
src/main/java/com/xiesx/fastboot/core/sign/package-info.java

@@ -0,0 +1,4 @@
+/**
+ * 数据签名 @GoEnableSign
+ */
+package com.xiesx.fastboot.core.sign;

+ 5 - 0
src/main/java/com/xiesx/fastboot/core/sign/package.html

@@ -0,0 +1,5 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+<head></head>
+<body>数据签名 @GoEnableSignal</body>
+</html>

+ 165 - 0
src/main/java/com/xiesx/fastboot/core/token/JwtHelper.java

@@ -0,0 +1,165 @@
+package com.xiesx.fastboot.core.token;
+
+import java.util.Date;
+import java.util.Map;
+import java.util.UUID;
+
+import javax.crypto.SecretKey;
+import javax.crypto.spec.SecretKeySpec;
+
+import org.apache.commons.codec.binary.Base64;
+import org.apache.commons.lang3.ObjectUtils;
+
+import com.google.common.collect.Maps;
+import com.xiesx.fastboot.utils.DateUtils;
+
+import io.jsonwebtoken.*;
+
+/**
+ * @title JwtHelper.java
+ * @description
+ * @author Sixian.xie
+ * @date 2020-7-21 22:37:47
+ */
+// https://jwt.io/
+public class JwtHelper {
+
+    /**
+     * jwt密钥
+     */
+    public static final String JWT_SECRET = "fast-jwt";
+
+    /**
+     * jwt有效时间
+     */
+    public static final int JWT_EXPIRE_M_1 = 1 * 60 * 1000; // 1分钟
+
+    public static final int JWT_EXPIRE_M_5 = 5 * JWT_EXPIRE_M_1; // 5分钟
+
+    public static final int JWT_EXPIRE_H_1 = 1 * 60 * 60 * 1000; // 1小时
+
+    public static final int JWT_EXPIRE_H_12 = 12 * JWT_EXPIRE_H_1; // 12小时
+
+    public static final int JWT_EXPIRE_D_1 = 24 * 60 * 60 * 1000; // 1天
+
+    public static final int JWT_EXPIRE_D_7 = 7 * JWT_EXPIRE_D_1; // 7天
+
+    public static final int JWT_EXPIRE_D_30 = 30 * JWT_EXPIRE_D_1; // 30天
+
+    /**
+     * jwt生成方
+     */
+    private final static String JWT_ISSUER = "xiesx";
+
+    /**
+     * 生成jwt
+     *
+     * @return
+     */
+    public static String create(String audience, String subject) {
+        return create(JWT_ISSUER, audience, subject, null, null, JWT_EXPIRE_D_1);
+    }
+
+    public static String create(String audience, String subject, long timeout) {
+        return create(JWT_ISSUER, audience, subject, null, null, timeout);
+    }
+
+    public static String create(String audience, String subject, Map<String, Object> claims, long timeout) {
+        return create(JWT_ISSUER, audience, subject, null, claims, timeout);
+    }
+
+    public static String create(String audience, String subject, Map<String, Object> header, Map<String, Object> claims, long timeout) {
+        return create(JWT_ISSUER, audience, subject, header, claims, timeout);
+    }
+
+    public static String create(String issuer, String audience, String subject, Map<String, Object> header, Map<String, Object> claims,
+            long timeout) {
+        //
+        String jwtid = UUID.randomUUID().toString();
+        //
+        Long timemillis = System.currentTimeMillis();
+        Date staDate = new Date(timemillis);
+        Date endDate = new Date(System.currentTimeMillis() + timeout);
+        //
+        JwtBuilder builder = Jwts.builder()
+                .setId(jwtid) // 唯一身份标识,根据业务需要,可以设置为一个不重复的值,主要用来作为一次性token,从而回避重放攻击
+                .setSubject(subject) // 主题
+                .setIssuer(issuer) // 签发者
+                .setAudience(audience) // 接收者
+                .setIssuedAt(staDate) // 签发时间
+                .setExpiration(endDate) // 过期时间
+                .addClaims(claims)// 私有属性
+                .signWith(SignatureAlgorithm.HS256, key());// 签名算法以及密匙
+
+        // 设置头部信息
+        if (ObjectUtils.isNotEmpty(header)) {
+            builder.setHeader(header);
+        }
+        return builder.compact();
+    }
+
+    /**
+     * 解析
+     *
+     * @param token
+     * @return
+     */
+    public static Claims parser(String token) throws JwtException {
+        Claims claimsJws = Jwts.parser().setSigningKey(key()).parseClaimsJws(token).getBody();
+        return claimsJws;
+    }
+
+    /**
+     * 过期
+     *
+     * @param token
+     * @return
+     */
+    public static boolean isExpired(Date expiration) {
+        return expiration.before(new Date());
+    }
+
+    /**
+     * 生成加密key
+     *
+     * @return
+     */
+    public static SecretKey key() {
+        byte[] encodedKey = Base64.decodeBase64(JWT_SECRET);
+        return new SecretKeySpec(encodedKey, 0, encodedKey.length, "AES");
+    }
+
+    public static void main(String[] args) {
+        //
+//        Map<String, Object> header = Maps.newConcurrentMap();
+//        header.put("jwt", "xxx");
+//        //
+//        Map<String, Object> claims = Maps.newConcurrentMap();
+//        claims.put("userid", "1");
+//        claims.put("username", "136305973");
+//        claims.put("nickname", "管理员");
+//        //
+//        String token = create("zedu", "api");
+//        token = create("zedu", "api", 60 * 1000);
+//        token = create("zedu", "api", claims, 60 * 1000);
+//        token = create("zedu", "api", claims, 60 * 1000);
+//        token = create("zedu", "api", header, claims, 60 * 1000);
+//        System.out.println(token);
+
+        Claims c = parser(
+                "eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiJlNDM4NWZlMi1kMzI2LTQ3OWYtYmQxZS04NGI2ODU5YTdkNzQiLCJzdWIiOiJhcGkiLCJpc3MiOiJ4aWVzeCIsImF1ZCI6InplZHUiLCJpYXQiOjE2MjUxOTE3MDQsImV4cCI6MTYyNTc5NjUwNCwidXNlcmlkIjo1MzU0MDQ2ODUyODUxNzEyMCwidXNlcm5hbWUiOiIxMzY5NzM0ODY5NCJ9.8V7BkwOLRUr5LQ5FC0UCMPdwCzHFlObAfdQxOM3muH4");
+        System.out.println("jti用户id:" + c.getId());
+        System.out.println("sub主题:" + c.getSubject());
+        System.out.println("iss签发者:" + c.getIssuer());
+        System.out.println("aud接收者:" + c.getAudience());
+
+        System.out.println("iat登录时间:" + DateUtils.format(c.getIssuedAt()));
+        System.out.println("exp过期时间:" + DateUtils.format(c.getExpiration()));
+        //
+        System.out.println("exp是否过期时间:" + isExpired(c.getExpiration()));
+        //
+        System.out.println("用户Id:" + c.get("user_id"));
+        System.out.println("用户名:" + c.get("user_name"));
+        System.out.println("用户名:" + c.get("nick_name", String.class));
+    }
+}

+ 16 - 0
src/main/java/com/xiesx/fastboot/core/token/annotation/GoEnableToken.java

@@ -0,0 +1,16 @@
+package com.xiesx.fastboot.core.token.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import org.springframework.context.annotation.Import;
+
+import com.xiesx.fastboot.core.token.cfg.TokenCfg;
+
+@Target({ElementType.TYPE})
+@Retention(RetentionPolicy.RUNTIME)
+@Import({TokenCfg.class})
+public @interface GoEnableToken {
+}

+ 18 - 0
src/main/java/com/xiesx/fastboot/core/token/annotation/GoToken.java

@@ -0,0 +1,18 @@
+package com.xiesx.fastboot.core.token.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * @title GoToken.java
+ * @description 登录用户信息
+ * @author Sixian.xie
+ * @date 2020-7-21 22:35:48
+ */
+@Target(ElementType.PARAMETER)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface GoToken {
+
+}

+ 56 - 0
src/main/java/com/xiesx/fastboot/core/token/cfg/TokenCfg.java

@@ -0,0 +1,56 @@
+package com.xiesx.fastboot.core.token.cfg;
+
+import java.util.List;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.method.support.HandlerMethodArgumentResolver;
+import org.springframework.web.servlet.config.annotation.InterceptorRegistration;
+import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
+
+import com.xiesx.fastboot.core.token.handle.TokenArgumentResolver;
+import com.xiesx.fastboot.core.token.handle.TokenInterceptorHandler;
+import com.xiesx.fastboot.utils.ArrayUtils;
+
+/**
+ * @title TokenCfg.java
+ * @description 令牌认证
+ * @author Sixian.xie
+ * @date 2020-7-21 22:36:02
+ */
+@Configuration
+@EnableConfigurationProperties(TokenProperties.class)
+public class TokenCfg implements WebMvcConfigurer {
+
+    public static final String USERID = "userid";
+
+    public static final String USERNAME = "username";
+
+    public static final String NICKNAME = "nickname";
+
+    @Autowired
+    private TokenProperties mTokenProperties;
+
+    @Override
+    public void addInterceptors(InterceptorRegistry registry) {
+        // 添加处理器
+        InterceptorRegistration in = registry.addInterceptor(new TokenInterceptorHandler());
+        // 处理url
+        List<String> paths = ArrayUtils.arrayToList(mTokenProperties.getIncludePaths());
+        if (!paths.isEmpty()) {
+            in.addPathPatterns(paths);
+        }
+        // 排除处理url
+        List<String> excludePaths = ArrayUtils.arrayToList(mTokenProperties.getExcludePaths());
+        if (!excludePaths.isEmpty()) {
+            in.excludePathPatterns(excludePaths);
+        }
+    }
+
+    @Override
+    public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
+        resolvers.add(new TokenArgumentResolver());
+    }
+}

+ 18 - 0
src/main/java/com/xiesx/fastboot/core/token/cfg/TokenProperties.java

@@ -0,0 +1,18 @@
+package com.xiesx.fastboot.core.token.cfg;
+
+import org.springframework.boot.context.properties.ConfigurationProperties;
+
+import lombok.Data;
+
+@Data
+@ConfigurationProperties(prefix = TokenProperties.PREFIX)
+public class TokenProperties {
+
+    public static final String PREFIX = "fastboot.token";
+
+    private String header = "token";
+
+    private String[] includePaths = new String[0];
+
+    private String[] excludePaths = new String[0];
+}

+ 35 - 0
src/main/java/com/xiesx/fastboot/core/token/handle/CurrentToken.java

@@ -0,0 +1,35 @@
+package com.xiesx.fastboot.core.token.handle;
+
+import com.xiesx.fastboot.core.token.JwtHelper;
+import com.xiesx.fastboot.core.token.cfg.TokenCfg;
+import io.jsonwebtoken.Claims;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+
+/**
+ * @title CurrentToken.java
+ * @description 当前token附带信息
+ * @author Sixian.xie
+ * @date 2020-7-21 22:37:08
+ */
+@Data
+@Builder
+@AllArgsConstructor
+public class CurrentToken {
+
+    private String userId;
+
+    private String userName;
+
+    private String nickName;
+
+    public static CurrentToken getCurrentToken(String token){
+        // 获取token
+        Claims claims = JwtHelper.parser(token);
+        CurrentToken currentToken = new CurrentToken(claims.getOrDefault(TokenCfg.USERID, "").toString(),
+                claims.getOrDefault(TokenCfg.USERNAME, "").toString(),
+                claims.getOrDefault(TokenCfg.NICKNAME, "").toString());
+        return currentToken;
+    }
+}

+ 48 - 0
src/main/java/com/xiesx/fastboot/core/token/handle/TokenArgumentResolver.java

@@ -0,0 +1,48 @@
+package com.xiesx.fastboot.core.token.handle;
+
+import org.apache.commons.lang3.ObjectUtils;
+import org.springframework.core.MethodParameter;
+import org.springframework.web.bind.support.WebDataBinderFactory;
+import org.springframework.web.context.request.NativeWebRequest;
+import org.springframework.web.context.request.RequestAttributes;
+import org.springframework.web.method.support.HandlerMethodArgumentResolver;
+import org.springframework.web.method.support.ModelAndViewContainer;
+
+import com.xiesx.fastboot.core.token.annotation.GoToken;
+import com.xiesx.fastboot.core.token.cfg.TokenCfg;
+import com.xiesx.fastboot.core.token.handle.CurrentToken.CurrentTokenBuilder;
+
+/**
+ * @title TokenArgumentResolver.java
+ * @description
+ * @author Sixian.xie
+ * @date 2020-7-21 22:37:35
+ */
+public class TokenArgumentResolver implements HandlerMethodArgumentResolver {
+
+    @Override
+    public boolean supportsParameter(MethodParameter parameter) {
+        return parameter.getParameterType().isAssignableFrom(CurrentToken.class) && parameter.hasParameterAnnotation(GoToken.class);
+    }
+
+    @Override
+    public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer container, NativeWebRequest request,
+            WebDataBinderFactory factory) throws Exception {
+        // 构造用户信息
+        CurrentTokenBuilder builder = CurrentToken.builder();
+        // 获取用户id
+        Object user_id = request.getAttribute(TokenCfg.USERID, RequestAttributes.SCOPE_REQUEST);
+        if (ObjectUtils.isNotEmpty(user_id)) {
+            builder.userId(user_id.toString());
+        }
+        Object user_name = request.getAttribute(TokenCfg.USERNAME, RequestAttributes.SCOPE_REQUEST);
+        if (ObjectUtils.isNotEmpty(user_name)) {
+            builder.userName(user_name.toString());
+        }
+        Object nick_name = request.getAttribute(TokenCfg.NICKNAME, RequestAttributes.SCOPE_REQUEST);
+        if (ObjectUtils.isNotEmpty(nick_name)) {
+            builder.nickName(nick_name.toString());
+        }
+        return builder.build();
+    }
+}

+ 137 - 0
src/main/java/com/xiesx/fastboot/core/token/handle/TokenInterceptorHandler.java

@@ -0,0 +1,137 @@
+package com.xiesx.fastboot.core.token.handle;
+
+import java.io.BufferedReader;
+import java.io.InputStreamReader;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Method;
+import java.util.Set;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import com.alibaba.fastjson.JSONObject;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.web.method.HandlerMethod;
+import org.springframework.web.servlet.HandlerInterceptor;
+import org.springframework.web.servlet.ModelAndView;
+
+import com.xiesx.fastboot.SpringHelper;
+import com.xiesx.fastboot.core.exception.RunExc;
+import com.xiesx.fastboot.core.exception.RunException;
+import com.xiesx.fastboot.core.token.JwtHelper;
+import com.xiesx.fastboot.core.token.annotation.GoToken;
+import com.xiesx.fastboot.core.token.cfg.TokenCfg;
+import com.xiesx.fastboot.core.token.cfg.TokenProperties;
+
+import io.jsonwebtoken.Claims;
+import io.jsonwebtoken.ExpiredJwtException;
+import lombok.extern.slf4j.Slf4j;
+
+/**
+ * @title TokenInterceptorHandler.java
+ * @description
+ * @author Sixian.xie
+ * @date 2020-7-21 22:37:38
+ */
+@Slf4j
+public class TokenInterceptorHandler implements HandlerInterceptor {
+
+    private static final String TOKEN_KEY = "token";
+
+    private String key;
+
+    /**
+     * Controller执行之前,如果返回false,controller不执行
+     *
+     * @throws Exception
+     */
+    @Override
+    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
+        log.debug("preHandle ......");
+        // 获取配置
+        TokenProperties properties = SpringHelper.getBean(TokenProperties.class);
+        key = StringUtils.isNotEmpty(properties.getHeader()) ? properties.getHeader() : TOKEN_KEY;
+        // 获取方法信息
+        if (handler instanceof HandlerMethod) {
+            // 获取方法
+            Method method = ((HandlerMethod) handler).getMethod();
+            // 获取参数注解信息
+            Annotation[][] parameterAnnotations = method.getParameterAnnotations();
+            for (Annotation[] annotation1 : parameterAnnotations) {
+                for (Annotation annotation2 : annotation1) {
+                    if (annotation2 instanceof GoToken) {
+                        // 获取token
+                        String token = request.getHeader(key);
+                        if (StringUtils.isEmpty(token)) {
+
+                            token = (String) getJsonBodyToken(request, key);
+
+                            if(StringUtils.isEmpty(token)){
+                                log.error("token:" + token);
+                                throw new RunException(RunExc.TOKEN, "请重新登录");
+                            }
+                        }
+                        try {
+                            // 获取token
+                            Claims claims = JwtHelper.parser(token);
+                            // 设置request
+                            request.setAttribute(TokenCfg.USERID, claims.getOrDefault(TokenCfg.USERID, ""));
+                            request.setAttribute(TokenCfg.USERNAME, claims.getOrDefault(TokenCfg.USERNAME, ""));
+                            request.setAttribute(TokenCfg.NICKNAME, claims.getOrDefault(TokenCfg.NICKNAME, ""));
+                        } catch (Exception e) {
+                            log.error("token:" + token);
+                            log.error("jwt token error", e);
+                            if (e instanceof ExpiredJwtException) {
+                                throw new RunException(RunExc.TOKEN, "请重新登录");
+                            } else {
+                                throw new RunException(RunExc.TOKEN);
+                            }
+                        }
+                    }
+                }
+            }
+        }
+        return true;
+    }
+
+    /**
+     * Controller执行之后,且页面渲染之前调用
+     */
+    @Override
+    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)
+            throws Exception {
+        log.debug("postHandle ......");
+    }
+
+    /**
+     * 页面渲染之后调用,一般用于资源清理操作
+     */
+    @Override
+    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
+        log.debug("afterCompletion ......");
+    }
+
+    private Object getJsonBodyToken(HttpServletRequest request,String key){
+        String param = null;
+        try {
+            BufferedReader streamReader = new BufferedReader( new InputStreamReader(request.getInputStream(), "UTF-8"));
+            StringBuilder responseStrBuilder = new StringBuilder();
+            String inputStr;
+            while ((inputStr = streamReader.readLine()) != null)
+                responseStrBuilder.append(inputStr);
+
+            JSONObject jsonObject = JSONObject.parseObject(responseStrBuilder.toString());
+            Set<String> keySet = jsonObject.keySet();
+            for(String k : keySet){
+
+                if(!k.equals(key))
+                request.setAttribute(k, jsonObject.get(k));
+
+            }
+            return jsonObject.get(key);
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        return param;
+    }
+}

+ 81 - 0
src/main/java/com/xiesx/fastboot/tag/TagUtils.java

@@ -0,0 +1,81 @@
+package com.xiesx.fastboot.tag;
+
+import java.io.*;
+
+import javax.servlet.ServletException;
+import javax.servlet.ServletOutputStream;
+import javax.servlet.WriteListener;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpServletResponseWrapper;
+
+import org.apache.commons.lang3.StringUtils;
+
+public class TagUtils {
+
+    /**
+     * @param source 源字符串
+     * @param key 键
+     * @param value 值
+     * @return 占位符替换
+     */
+    public static String replaceRegText(String source, String key, String value) {
+        String _regex = "@\\{" + key + "}";
+        return source.replaceAll(_regex, value);
+    }
+
+    public static String replaceRegClear(String source) {
+        return replaceRegText(source, "(.+?)", "");
+    }
+
+    /**
+     * @param request HttpServletRequest对象
+     * @param response HttpServletResponse对象
+     * @param jspFile JSP文件路径
+     * @param charsetEncoding 字符编码
+     * @return 执行JSP并返回HTML源码
+     * @throws ServletException 可能产生的异常
+     * @throws IOException 可能产生的异常
+     */
+    public static String includeJSP(HttpServletRequest request, HttpServletResponse response, String jspFile, String charsetEncoding)
+            throws ServletException, IOException {
+        final OutputStream _output = new ByteArrayOutputStream();
+        final PrintWriter _writer = new PrintWriter(
+                new OutputStreamWriter(_output, StringUtils.defaultIfEmpty(charsetEncoding, response.getCharacterEncoding())));
+        final ServletOutputStream _servletOutput = new ServletOutputStream() {
+
+            @Override
+            public boolean isReady() {
+                return false;
+            }
+
+            @Override
+            public void setWriteListener(WriteListener arg0) {}
+
+            @Override
+            public void write(int b) throws IOException {
+                _output.write(b);
+            }
+
+            @Override
+            public void write(byte[] b, int off, int len) throws IOException {
+                _output.write(b, off, len);
+            }
+        };
+        HttpServletResponse _response = new HttpServletResponseWrapper(response) {
+
+            @Override
+            public ServletOutputStream getOutputStream() {
+                return _servletOutput;
+            }
+
+            @Override
+            public PrintWriter getWriter() {
+                return _writer;
+            }
+        };
+        request.getRequestDispatcher(jspFile).include(request, _response);
+        _writer.flush();
+        return _output.toString();
+    }
+}

+ 84 - 0
src/main/java/com/xiesx/fastboot/tag/body/CssTag.java

@@ -0,0 +1,84 @@
+package com.xiesx.fastboot.tag.body;
+
+import javax.servlet.jsp.JspException;
+import javax.servlet.jsp.tagext.BodyTagSupport;
+
+import org.apache.commons.lang3.ObjectUtils;
+import org.apache.commons.lang3.StringUtils;
+
+import com.xiesx.fastboot.tag.ui.BaseUITag;
+import com.xiesx.fastboot.utils.RuntimeUtils;
+
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+@Data
+@EqualsAndHashCode(callSuper = false)
+public class CssTag extends BodyTagSupport {
+
+    private static final long serialVersionUID = -1975747968925711584L;
+
+    private BaseUITag __ui;
+
+    private String href;
+
+    private String rel;
+
+    private String media;
+
+    private String type;
+
+    @Override
+    public int doStartTag() throws JspException {
+        __ui = (BaseUITag) this.getParent();
+        if (ObjectUtils.isEmpty(__ui)) {
+            throw new JspException("Parent UITag or LayoutTag not found.");
+        }
+        return super.doStartTag();
+    }
+
+    @Override
+    public int doAfterBody() throws JspException {
+        try {
+            if (this.bodyContent != null) {
+                this.bodyContent.clearBody();
+            }
+        } catch (Exception e) {
+            throw new JspException(RuntimeUtils.unwrapThrow(e));
+        }
+        return super.doAfterBody();
+    }
+
+    @Override
+    public int doEndTag() throws JspException {
+        StringBuilder _metaTmpl = new StringBuilder("<link");
+        boolean _isEmpty = true;
+        if (StringUtils.isNotEmpty(this.getHref())) {
+            _metaTmpl.append(" href=\"").append(this.getHref()).append("\"");
+            _isEmpty = false;
+        }
+        if (!_isEmpty) {
+            if (StringUtils.isEmpty(this.getRel())) {
+                this.setRel("stylesheet");
+            }
+            _metaTmpl.append(" rel=\"").append(this.getRel()).append("\"");
+            //
+            if (StringUtils.isNotEmpty(this.getType())) {
+                _metaTmpl.append(" type=\"").append(this.getType()).append("\"");
+            }
+            //
+            if (StringUtils.isNotEmpty(this.getMedia())) {
+                _metaTmpl.append(" media=\"").append(this.getMedia()).append("\"");
+            }
+            _metaTmpl.append(">\n");
+            __ui.writerToCssPart(_metaTmpl.toString());
+        }
+        //
+        this.__ui = null;
+        this.href = null;
+        this.rel = null;
+        this.media = null;
+        this.type = null;
+        return super.doEndTag();
+    }
+}

+ 74 - 0
src/main/java/com/xiesx/fastboot/tag/body/MetaTag.java

@@ -0,0 +1,74 @@
+package com.xiesx.fastboot.tag.body;
+
+import javax.servlet.jsp.JspException;
+import javax.servlet.jsp.tagext.BodyTagSupport;
+
+import org.apache.commons.lang3.ObjectUtils;
+import org.apache.commons.lang3.StringUtils;
+
+import com.xiesx.fastboot.tag.ui.BaseUITag;
+import com.xiesx.fastboot.utils.RuntimeUtils;
+
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+@Data
+@EqualsAndHashCode(callSuper = false)
+public class MetaTag extends BodyTagSupport {
+
+    private static final long serialVersionUID = 7014634016889539200L;
+
+    private BaseUITag __ui;
+
+    private String attrKey;
+
+    private String attrValue;
+
+    @Override
+    public int doStartTag() throws JspException {
+        __ui = (BaseUITag) this.getParent();
+        if (ObjectUtils.isEmpty(__ui)) {
+            throw new JspException("Parent UITag or LayoutTag not found.");
+        }
+        return super.doStartTag();
+    }
+
+    @Override
+    public int doAfterBody() throws JspException {
+        try {
+            if (this.bodyContent != null) {
+                String _propValue = this.bodyContent.getString();
+                if (StringUtils.isNotEmpty(_propValue)) {
+                    this.setAttrValue(_propValue);
+                }
+                this.bodyContent.clearBody();
+            }
+        } catch (Exception e) {
+            throw new JspException(RuntimeUtils.unwrapThrow(e));
+        }
+        return super.doAfterBody();
+    }
+
+    @Override
+    public int doEndTag() throws JspException {
+        StringBuilder _metaTmpl = new StringBuilder("<meta");
+        boolean _isEmpty = true;
+        if (StringUtils.isNotEmpty(this.getAttrKey())) {
+            _metaTmpl.append(" ").append(this.getAttrKey());
+            _isEmpty = false;
+        }
+        if (StringUtils.isNotEmpty(this.getAttrValue())) {
+            _metaTmpl.append(" ").append(this.getAttrValue());
+            _isEmpty = false;
+        }
+        _metaTmpl.append(">\n");
+        if (!_isEmpty) {
+            __ui.writerToMetaPart(_metaTmpl.toString());
+        }
+        //
+        this.__ui = null;
+        this.attrKey = null;
+        this.attrValue = null;
+        return super.doEndTag();
+    }
+}

+ 63 - 0
src/main/java/com/xiesx/fastboot/tag/body/PropertyTag.java

@@ -0,0 +1,63 @@
+package com.xiesx.fastboot.tag.body;
+
+import javax.servlet.jsp.JspException;
+import javax.servlet.jsp.tagext.BodyTagSupport;
+
+import org.apache.commons.lang3.ObjectUtils;
+import org.apache.commons.lang3.StringUtils;
+
+import com.xiesx.fastboot.tag.ui.BaseUITag;
+import com.xiesx.fastboot.utils.RuntimeUtils;
+
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+@Data
+@EqualsAndHashCode(callSuper = false)
+public class PropertyTag extends BodyTagSupport {
+
+    private static final long serialVersionUID = -3493408190832765819L;
+
+    private BaseUITag __ui;
+
+    private String name;
+
+    private String value;
+
+    @Override
+    public int doStartTag() throws JspException {
+        __ui = (BaseUITag) this.getParent();
+        if (ObjectUtils.isEmpty(__ui)) {
+            throw new JspException("Parent UITag or LayoutTag not found.");
+        }
+        return super.doStartTag();
+    }
+
+    @Override
+    public int doAfterBody() throws JspException {
+        try {
+            if (this.bodyContent != null) {
+                String _propValue = this.bodyContent.getString();
+                if (StringUtils.isNotEmpty(_propValue)) {
+                    this.setValue(_propValue);
+                }
+                this.bodyContent.clearBody();
+            }
+        } catch (Exception e) {
+            throw new JspException(RuntimeUtils.unwrapThrow(e));
+        }
+        return super.doAfterBody();
+    }
+
+    @Override
+    public int doEndTag() throws JspException {
+        if (StringUtils.isNotEmpty(this.getName()) && StringUtils.isNotEmpty(this.getValue())) {
+            __ui.putProperty(this.getName(), this.getValue());
+        }
+        //
+        this.__ui = null;
+        this.name = null;
+        this.value = null;
+        return super.doEndTag();
+    }
+}

+ 91 - 0
src/main/java/com/xiesx/fastboot/tag/body/ScriptTag.java

@@ -0,0 +1,91 @@
+package com.xiesx.fastboot.tag.body;
+
+import javax.servlet.jsp.JspException;
+import javax.servlet.jsp.tagext.BodyTagSupport;
+
+import org.apache.commons.lang3.ObjectUtils;
+import org.apache.commons.lang3.StringUtils;
+
+import com.xiesx.fastboot.tag.ui.BaseUITag;
+import com.xiesx.fastboot.utils.RuntimeUtils;
+
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+@Data
+@EqualsAndHashCode(callSuper = false)
+public class ScriptTag extends BodyTagSupport {
+
+    private static final long serialVersionUID = -2774067114541501535L;
+
+    private BaseUITag __ui;
+
+    private String src;
+
+    private String value;
+
+    private String type;
+
+    private String wrapper;
+
+    @Override
+    public int doStartTag() throws JspException {
+        __ui = (BaseUITag) this.getParent();
+        if (ObjectUtils.isEmpty(__ui)) {
+            throw new JspException("Parent UITag or LayoutTag not found.");
+        }
+        return super.doStartTag();
+    }
+
+    @Override
+    public int doAfterBody() throws JspException {
+        try {
+            if (this.bodyContent != null) {
+                String _propValue = this.bodyContent.getString();
+                if (StringUtils.isNotEmpty(_propValue)) {
+                    this.setValue(_propValue);
+                }
+                this.bodyContent.clearBody();
+            }
+        } catch (Exception e) {
+            throw new JspException(RuntimeUtils.unwrapThrow(e));
+        }
+        return super.doAfterBody();
+    }
+
+    @Override
+    public int doEndTag() throws JspException {
+        StringBuilder _scriptTmpl = new StringBuilder("<script");
+        if (StringUtils.isNotEmpty(this.getId())) {
+            _scriptTmpl.append(" id=\"").append(this.getId()).append("\"");
+        }
+        boolean _isEmpty = true;
+        if (StringUtils.isNotEmpty(this.getSrc())) {
+            _scriptTmpl.append(" src=\"").append(this.getSrc()).append("\"");
+            _isEmpty = false;
+        }
+        _scriptTmpl.append(" type=\"").append(StringUtils.defaultIfBlank(this.getType(), "text/javascript")).append("\">");
+        if (_isEmpty && StringUtils.isNotEmpty(this.getValue())) {
+            String _wrapper = StringUtils.defaultIfBlank(this.getWrapper(), "script");
+            String _content = StringUtils.substringBetween(this.getValue(), "<" + _wrapper + ">", "</" + _wrapper + ">");
+            if (StringUtils.isNotEmpty(_content)) {
+                this.setValue(_content);
+            }
+            _scriptTmpl.append(this.getValue()).append("\n");
+            _isEmpty = false;
+        }
+        _scriptTmpl.append("</script>\n");
+        if (!_isEmpty) {
+            __ui.writerToScriptPart(_scriptTmpl.toString());
+        }
+        //
+        this.__ui = null;
+        this.src = null;
+        this.value = null;
+        this.id = null;
+        this.type = null;
+        this.wrapper = null;
+        return super.doEndTag();
+    }
+
+}

+ 152 - 0
src/main/java/com/xiesx/fastboot/tag/ui/BaseUITag.java

@@ -0,0 +1,152 @@
+package com.xiesx.fastboot.tag.ui;
+
+import java.util.Collections;
+import java.util.Map;
+import java.util.regex.Matcher;
+
+import javax.servlet.jsp.JspException;
+import javax.servlet.jsp.tagext.BodyTagSupport;
+
+import org.apache.commons.lang3.StringUtils;
+
+import com.google.common.collect.Maps;
+import com.xiesx.fastboot.tag.TagUtils;
+
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+@Data
+@EqualsAndHashCode(callSuper = false)
+public abstract class BaseUITag extends BodyTagSupport {
+
+    private static final long serialVersionUID = 8425802569545340622L;
+
+    /**
+     * 模板文件路径
+     */
+    private String src;
+
+    /**
+     * 字符编码
+     */
+    private String charsetEncoding;
+
+    /**
+     * 清理占位符
+     */
+    private boolean cleanup = true;
+
+    private StringBuilder __tmplBodyPart;
+
+    private StringBuilder __tmplScriptPart;
+
+    private StringBuilder __tmplMetaPart;
+
+    private StringBuilder __tmplCssPart;
+
+    private Map<String, String> __tmplPropertyPart;
+
+    @Override
+    public int doStartTag() throws JspException {
+        __tmplBodyPart = new StringBuilder();
+        __tmplScriptPart = new StringBuilder();
+        __tmplMetaPart = new StringBuilder();
+        __tmplCssPart = new StringBuilder();
+        __tmplPropertyPart = Maps.newHashMap();
+        return super.doStartTag();
+    }
+
+    @Override
+    public int doEndTag() throws JspException {
+        this.src = null;
+        this.charsetEncoding = null;
+        this.cleanup = true;
+        return super.doEndTag();
+    }
+
+    @Override
+    public void release() {
+        __tmplBodyPart = null;
+        __tmplScriptPart = null;
+        __tmplMetaPart = null;
+        __tmplCssPart = null;
+        if (__tmplPropertyPart != null) {
+            __tmplPropertyPart.clear();
+            __tmplPropertyPart = null;
+        }
+        super.release();
+    }
+
+    public String mergeContent(String tmplContent) throws JspException {
+        String _tmplContent = tmplContent;
+        if (StringUtils.isNotEmpty(_tmplContent)) {
+            if (__tmplMetaPart.length() > 0) {
+                _tmplContent = TagUtils.replaceRegText(_tmplContent, "meta", __tmplMetaPart.toString());
+            }
+            if (__tmplCssPart.length() > 0) {
+                _tmplContent = TagUtils.replaceRegText(_tmplContent, "css", __tmplCssPart.toString());
+            }
+            if (__tmplScriptPart.length() > 0) {
+                _tmplContent = TagUtils.replaceRegText(_tmplContent, "script", __tmplScriptPart.toString());
+            }
+            if (__tmplBodyPart.length() > 0) {
+                _tmplContent = TagUtils.replaceRegText(_tmplContent, "body", __tmplBodyPart.toString());
+            }
+            for (Map.Entry<String, String> _entry : __tmplPropertyPart.entrySet()) {
+                _tmplContent = TagUtils.replaceRegText(_tmplContent, _entry.getKey(), _entry.getValue());
+            }
+        }
+        return _tmplContent;
+    }
+
+    public String buildSrcUrl() {
+        if (StringUtils.isNotEmpty(this.getSrc())) {
+            StringBuilder _url = new StringBuilder();
+            if (!this.getSrc().startsWith("/")) {
+                _url.append("/WEB-INF/jsp/");
+            }
+            _url.append(this.getSrc());
+            if (!this.getSrc().endsWith(".jsp")) {
+                _url.append(".jsp");
+            }
+            return _url.toString();
+        }
+        return this.getSrc();
+    }
+
+    public void writerToBodyPart(String content) {
+        __tmplBodyPart.append(Matcher.quoteReplacement(content));
+    }
+
+    public String getMetaPartContent() {
+        return __tmplMetaPart.toString();
+    }
+
+    public void writerToMetaPart(String content) {
+        __tmplMetaPart.append(Matcher.quoteReplacement(content));
+    }
+
+    public String getCssPartContent() {
+        return __tmplCssPart.toString();
+    }
+
+    public void writerToCssPart(String content) {
+        __tmplCssPart.append(Matcher.quoteReplacement(content));
+    }
+
+    public String getScriptPartContent() {
+        return __tmplScriptPart.toString();
+    }
+
+    public void writerToScriptPart(String content) {
+        __tmplScriptPart.append(Matcher.quoteReplacement(content));
+    }
+
+    public void putProperty(String key, String value) {
+        __tmplPropertyPart.put(key, Matcher.quoteReplacement(value));
+    }
+
+    protected Map<String, String> getPropertyPart() {
+        return Collections.unmodifiableMap(this.__tmplPropertyPart);
+    }
+}

+ 94 - 0
src/main/java/com/xiesx/fastboot/tag/ui/LayoutTag.java

@@ -0,0 +1,94 @@
+package com.xiesx.fastboot.tag.ui;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.jsp.JspException;
+
+import org.apache.commons.lang3.ObjectUtils;
+import org.apache.commons.lang3.StringUtils;
+
+import com.xiesx.fastboot.tag.TagUtils;
+import com.xiesx.fastboot.utils.RuntimeUtils;
+
+public class LayoutTag extends BaseUITag {
+
+    private static final long serialVersionUID = 7959201563636313024L;
+
+    private BaseUITag __ui;
+
+    /**
+     * Layout模板文件内容
+     */
+    private String __tmplContent;
+
+    /**
+     * 自定义占位符名称, 若未提供则默认为body
+     */
+    private String name;
+
+    @Override
+    public int doStartTag() throws JspException {
+        __ui = (BaseUITag) this.getParent();
+        if (ObjectUtils.isEmpty(__ui)) {
+            throw new JspException("Parent UITag or LayoutTag not found.");
+        }
+        try {
+            if (StringUtils.isNotEmpty(this.getSrc())) {
+                __tmplContent = TagUtils.includeJSP((HttpServletRequest) this.pageContext.getRequest(),
+                        (HttpServletResponse) this.pageContext.getResponse(), this.buildSrcUrl(), __ui.getCharsetEncoding());
+            } else {
+                __tmplContent = "";
+            }
+        } catch (Exception e) {
+            throw new JspException(RuntimeUtils.unwrapThrow(e));
+        }
+        return super.doStartTag();
+    }
+
+    @Override
+    public int doAfterBody() throws JspException {
+        try {
+            if (this.bodyContent != null) {
+                String _layoutBody = StringUtils.defaultIfEmpty(this.bodyContent.getString(), "");
+                if (StringUtils.isNotEmpty(__tmplContent)) {
+                    this.writerToBodyPart(_layoutBody);
+                } else {
+                    __tmplContent = _layoutBody;
+                }
+                this.bodyContent.clearBody();
+            }
+        } catch (Exception e) {
+            throw new JspException(RuntimeUtils.unwrapThrow(e));
+        }
+        return super.doAfterBody();
+    }
+
+    @Override
+    public int doEndTag() throws JspException {
+        try {
+            __ui.writerToMetaPart(this.getMetaPartContent());
+            __ui.writerToCssPart(this.getCssPartContent());
+            __ui.writerToScriptPart(this.getScriptPartContent());
+            __tmplContent = this.mergeContent(StringUtils.defaultIfEmpty(__tmplContent, ""));
+            if (StringUtils.isNotEmpty(name) && !"body".equalsIgnoreCase(name)) {
+                __ui.putProperty(name, !isCleanup() ? __tmplContent : TagUtils.replaceRegClear(__tmplContent));
+            } else {
+                __ui.writerToBodyPart(!isCleanup() ? __tmplContent : TagUtils.replaceRegClear(__tmplContent));
+            }
+        } catch (Exception e) {
+            throw new JspException(RuntimeUtils.unwrapThrow(e));
+        }
+        this.__ui = null;
+        this.__tmplContent = null;
+        this.name = null;
+        return super.doEndTag();
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+}

+ 70 - 0
src/main/java/com/xiesx/fastboot/tag/ui/UITag.java

@@ -0,0 +1,70 @@
+package com.xiesx.fastboot.tag.ui;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.jsp.JspException;
+
+import org.apache.commons.lang3.ObjectUtils;
+import org.apache.commons.lang3.StringUtils;
+
+import com.xiesx.fastboot.tag.TagUtils;
+import com.xiesx.fastboot.utils.RuntimeUtils;
+
+public class UITag extends BaseUITag {
+
+    private static final long serialVersionUID = 584494119933433838L;
+
+    private boolean __isCurrentUI;
+
+    /**
+     * @return 返回当前线程中的UITag对象
+     */
+    public UITag currentUI() {
+        return (UITag) this.pageContext.getAttribute(UITag.class.getName());
+    }
+
+    @Override
+    public int doStartTag() throws JspException {
+        try {
+            if (ObjectUtils.isEmpty(currentUI())) {
+                __isCurrentUI = true;
+                this.pageContext.setAttribute(UITag.class.getName(), this);
+            }
+        } catch (Exception e) {
+            throw new JspException(RuntimeUtils.unwrapThrow(e));
+        }
+        return super.doStartTag();
+    }
+
+    @Override
+    public int doAfterBody() throws JspException {
+        try {
+            if (this.bodyContent != null) {
+                this.bodyContent.clearBody();
+            }
+        } catch (Exception e) {
+            throw new JspException(RuntimeUtils.unwrapThrow(e));
+        }
+        return super.doAfterBody();
+    }
+
+    @Override
+    public int doEndTag() throws JspException {
+        if (__isCurrentUI) {
+            try {
+                /* UI模板文件内容 */
+                String __tmplContent = null;
+                if (StringUtils.isNotBlank(this.getSrc())) {
+                    __tmplContent = TagUtils.includeJSP((HttpServletRequest) this.pageContext.getRequest(),
+                            (HttpServletResponse) this.pageContext.getResponse(), this.buildSrcUrl(), this.getCharsetEncoding());
+                }
+                __tmplContent = this.mergeContent(StringUtils.defaultIfEmpty(__tmplContent, "@{body}"));
+                this.pageContext.getOut().write(!isCleanup() ? __tmplContent : TagUtils.replaceRegClear(__tmplContent));
+            } catch (Exception e) {
+                throw new JspException(RuntimeUtils.unwrapThrow(e));
+            }
+        }
+        this.__isCurrentUI = false;
+        return super.doEndTag();
+    }
+}

+ 81 - 0
src/main/java/com/xiesx/fastboot/utils/AppUtils.java

@@ -0,0 +1,81 @@
+package com.xiesx.fastboot.utils;
+
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.Arrays;
+import java.util.UUID;
+
+public class AppUtils {
+
+    // 生成 app_secret 密钥
+    private final static String SERVER_NAME = "zedu";
+
+    private final static String[] chars = new String[] {"a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q",
+            "r", "s", "t", "u", "v", "w", "x", "y", "z", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F",
+            "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"};
+
+    /**
+     * @Description:
+     *               <p>
+     *               短8位UUID思想其实借鉴微博短域名的生成方式,但是其重复概率过高,而且每次生成4个,需要随即选取一个。
+     *               本算法利用62个可打印字符,通过随机生成32位UUID,由于UUID都为十六进制,所以将UUID分成8组,每4个为一组,然后通过模62操作,结果作为索引取出字符,
+     *               这样重复率大大降低。 经测试,在生成一千万个数据也没有出现重复,完全满足大部分需求。
+     *               </p>
+     * @author mazhq
+     * @date 2019/8/27 16:16
+     */
+    public static String getAppId() {
+        StringBuffer shortBuffer = new StringBuffer();
+        String uuid = UUID.randomUUID().toString().replace("-", "");
+        for (int i = 0; i < 8; i++) {
+            String str = uuid.substring(i * 4, i * 4 + 4);
+            int x = Integer.parseInt(str, 16);
+            shortBuffer.append(chars[x % 0x3E]);
+        }
+        return shortBuffer.toString();
+    }
+
+    /**
+     * <p>
+     * 通过appId和内置关键词生成APP Secret
+     * </P>
+     *
+     * @author mazhq
+     * @date 2019/8/27 16:32
+     */
+    public static String getAppSecret(String appId) {
+        try {
+            String[] array = new String[] {SERVER_NAME, appId, String.valueOf(System.currentTimeMillis())};
+            StringBuffer sb = new StringBuffer();
+            Arrays.sort(array); // 字符串排序
+            for (String element : array) {
+                sb.append(element);
+            }
+            String str = sb.toString();
+            MessageDigest md = MessageDigest.getInstance("SHA-1");
+            md.update(str.getBytes());
+            byte[] digest = md.digest();
+
+            StringBuffer hexstr = new StringBuffer();
+            String shaHex = "";
+            for (byte element : digest) {
+                shaHex = Integer.toHexString(element & 0xFF);
+                if (shaHex.length() < 2) {
+                    hexstr.append(0);
+                }
+                hexstr.append(shaHex);
+            }
+            return hexstr.toString();
+        } catch (NoSuchAlgorithmException e) {
+            e.printStackTrace();
+            throw new RuntimeException();
+        }
+    }
+
+    public static void main(String[] args) {
+        String appId = getAppId();
+        String appSecret = getAppSecret(appId);
+        System.out.println("appId: " + appId);
+        System.out.println("appSecret: " + appSecret);
+    }
+}

+ 102 - 0
src/main/java/com/xiesx/fastboot/utils/ArrayUtils.java

@@ -0,0 +1,102 @@
+package com.xiesx.fastboot.utils;
+
+import java.util.List;
+
+import com.alibaba.fastjson.JSON;
+import com.google.common.base.Joiner;
+import com.google.common.base.Splitter;
+import com.google.common.collect.Lists;
+
+public class ArrayUtils {
+
+    /**
+     * list转str[]
+     *
+     * @param str
+     * @return
+     */
+    public static String[] listToArray(List<String> list) {
+        return list.toArray(new String[list.size()]);
+    }
+
+    /**
+     * str[]转list
+     *
+     * @param str
+     * @return
+     */
+    public static List<String> arrayToList(String[] str) {
+        return Lists.newArrayList(str);
+    }
+
+    /**
+     * str转list
+     *
+     * @param str
+     * @return
+     */
+    public static List<String> splitToList(String str) {
+        return splitToList(str, ",");
+    }
+
+    /**
+     * str转list
+     *
+     * @param str
+     * @param separator
+     * @return
+     */
+    public static List<String> splitToList(String str, String separator) {
+        return Splitter.on(separator).omitEmptyStrings().trimResults().splitToList(str);
+    }
+
+    /**
+     * list转str
+     *
+     * @param str
+     * @return
+     */
+    public static String joinTolist(List<String> list) {
+        return joinTolist(list, ",");
+    }
+
+    /**
+     * list转str
+     *
+     * @param str
+     * @param separator
+     * @return
+     */
+    public static String joinTolist(List<String> list, String separator) {
+        if (list.isEmpty()) {
+            return null;
+        }
+        return Joiner.on(separator).join(list);
+    }
+
+    public static void main(String[] args) {
+        //
+        List<String> list = Lists.newArrayList("1", "2", "3", "4");
+        //
+        String[] arry = listToArray(list);
+        System.out.println(JSON.toJSONString(arry));
+        list = arrayToList(arry);
+        System.out.println(JSON.toJSONString(list));
+
+        //
+        String str1 = "1,2,3,4";
+        //
+        list = splitToList(str1);
+        System.out.println(JSON.toJSONString(list));
+        str1 = joinTolist(list);
+        System.out.println(str1);
+
+        //
+        String str2 = "1-2-3-4";
+        //
+        list = splitToList(str2, "-");
+        System.out.println(JSON.toJSONString(list));
+        str2 = joinTolist(list, "-");
+        System.out.println(str2);
+    }
+}

+ 49 - 0
src/main/java/com/xiesx/fastboot/utils/CopyUtils.java

@@ -0,0 +1,49 @@
+package com.xiesx.fastboot.utils;
+
+import java.beans.PropertyDescriptor;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.commons.lang3.ObjectUtils;
+import org.springframework.beans.BeanWrapper;
+import org.springframework.beans.BeanWrapperImpl;
+
+import com.google.common.collect.Sets;
+
+import lombok.NonNull;
+
+public class CopyUtils {
+
+    public static String[] ignoreNullNames(@NonNull Object source) {
+
+        final BeanWrapper beanWrapper = new BeanWrapperImpl(source);
+
+        PropertyDescriptor[] pds = beanWrapper.getPropertyDescriptors();
+
+        Set<String> noValuePropertySet = Sets.newHashSet();
+        Arrays.stream(pds).forEach(pd -> {
+            Object propertyValue = beanWrapper.getPropertyValue(pd.getName());
+            if (ObjectUtils.isEmpty(propertyValue)) {
+                noValuePropertySet.add(pd.getName());
+            } else {
+                if (Iterable.class.isAssignableFrom(propertyValue.getClass())) {
+                    Iterable<?> iterable = (Iterable<?>) propertyValue;
+                    Iterator<?> iterator = iterable.iterator();
+                    if (!iterator.hasNext()) {
+                        noValuePropertySet.add(pd.getName());
+                    }
+                }
+                if (Map.class.isAssignableFrom(propertyValue.getClass())) {
+                    Map<?, ?> map = (Map<?, ?>) propertyValue;
+                    if (map.isEmpty()) {
+                        noValuePropertySet.add(pd.getName());
+                    }
+                }
+            }
+        });
+        String[] result = new String[noValuePropertySet.size()];
+        return noValuePropertySet.toArray(result);
+    }
+}

+ 609 - 0
src/main/java/com/xiesx/fastboot/utils/DateUtils.java

@@ -0,0 +1,609 @@
+package com.xiesx.fastboot.utils;
+
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.TimeZone;
+
+import org.apache.commons.lang3.StringUtils;
+import org.joda.time.*;
+
+public class DateUtils {
+
+    private static final String FORTER_DATE = "yyyy-MM-dd";
+
+    private static final String FORMAT_TIME = "yyyy-MM-dd HH:mm:ss";
+
+    // time
+
+    /**
+     * 获取当前系统时间
+     *
+     * @return yyyy-MM-dd HH:mm:ss
+     */
+    public static String getCurrentTime() {
+        DateTime dt = new DateTime();
+        String time = dt.toString(FORMAT_TIME);
+        return time;
+    }
+
+    /**
+     * 获取系统当前时间按照指定格式返回
+     *
+     * @param pattern yyyy/MM/dd hh:mm:a
+     * @return
+     */
+    public static String getCurrentTimePattern(String pattern) {
+        DateTime dt = new DateTime();
+        String time = dt.toString(pattern);
+        return time;
+    }
+
+    // date
+
+    /**
+     * 获取当前日期
+     *
+     * @return
+     */
+    public static String getCurrentDate() {
+        DateTime dt = new DateTime();
+        String date = dt.toString(FORTER_DATE);
+        return date;
+    }
+
+    /**
+     * 获取当前日期按照指定格式
+     *
+     * @param pattern
+     * @return
+     */
+    public static String getCurrentDatePattern(String pattern) {
+        DateTime dt = new DateTime();
+        String date = dt.toString(pattern);
+        return date;
+    }
+
+    // point time
+
+    /**
+     * 获取指定时间
+     *
+     * @param year 年
+     * @param month 月
+     * @param day 天
+     * @param hour 小时
+     * @param minute 分钟
+     * @param seconds 秒
+     * @return yyyy-MM-dd HH:mm:ss
+     */
+    public static String getPointTime(Integer year, Integer month, Integer day, Integer hour, Integer minute, Integer seconds) {
+        DateTime dt = new DateTime(year, month, day, hour, minute, seconds);
+        String date = dt.toString(FORMAT_TIME);
+        return date;
+    }
+
+    /**
+     *
+     * @param year 年
+     * @param month 月
+     * @param day 天
+     * @param hour 小时
+     * @param minute 分钟
+     * @param seconds 秒
+     * @param parrten 自定义格式
+     * @return parrten
+     */
+    public static String getPointTimePattern(Integer year, Integer month, Integer day, Integer hour, Integer minute, Integer seconds,
+            String parrten) {
+        DateTime dt = new DateTime(year, month, day, hour, minute, seconds);
+        String date = dt.toString(parrten);
+        return date;
+    }
+
+    // point date
+
+    /**
+     * 获取指定日期
+     *
+     * @param year
+     * @param month
+     * @param day
+     * @return
+     */
+    public static String getPointDate(Integer year, Integer month, Integer day) {
+        LocalDate dt = new LocalDate(year, month, day);
+        String date = dt.toString(FORTER_DATE);
+        return date;
+    }
+
+    /**
+     * 获取指定日期 返回指定格式
+     *
+     * @param year
+     * @param month
+     * @param day
+     * @param parrten
+     * @return
+     */
+    public static String getPointDatParrten(Integer year, Integer month, Integer day, String parrten) {
+        LocalDate dt = new LocalDate(year, month, day);
+        String date = dt.toString(parrten);
+        return date;
+    }
+
+    // point week
+
+    /**
+     * 获取当前是一周星期几
+     *
+     * @return
+     */
+    public static String getWeek() {
+        DateTime dts = new DateTime();
+        String week = null;
+        switch (dts.getDayOfWeek()) {
+            case DateTimeConstants.SUNDAY:
+                week = "星期日";
+                break;
+
+            case DateTimeConstants.MONDAY:
+                week = "星期一";
+                break;
+
+            case DateTimeConstants.TUESDAY:
+                week = "星期二";
+                break;
+            case DateTimeConstants.WEDNESDAY:
+                week = "星期三";
+                break;
+            case DateTimeConstants.THURSDAY:
+                week = "星期四";
+                break;
+            case DateTimeConstants.FRIDAY:
+                week = "星期五";
+                break;
+            case DateTimeConstants.SATURDAY:
+                week = "星期六";
+            default:
+                break;
+        }
+        return week;
+    }
+
+    /**
+     * 获取指定时间是一周的星期几
+     *
+     * @param year
+     * @param month
+     * @param day
+     * @return
+     */
+    public static String getWeekPoint(Integer year, Integer month, Integer day) {
+        LocalDate dts = new LocalDate(year, month, day);
+        String week = null;
+        switch (dts.getDayOfWeek()) {
+            case DateTimeConstants.SUNDAY:
+                week = "星期日";
+                break;
+            case DateTimeConstants.MONDAY:
+                week = "星期一";
+                break;
+            case DateTimeConstants.TUESDAY:
+                week = "星期二";
+                break;
+            case DateTimeConstants.WEDNESDAY:
+                week = "星期三";
+                break;
+            case DateTimeConstants.THURSDAY:
+                week = "星期四";
+                break;
+            case DateTimeConstants.FRIDAY:
+                week = "星期五";
+                break;
+            case DateTimeConstants.SATURDAY:
+                week = "星期六";
+                break;
+
+            default:
+                break;
+        }
+        return week;
+    }
+
+    // format
+
+    /**
+     * 格式化日期
+     *
+     * @param date
+     * @return yyyy-MM-dd HH:mm:ss
+     */
+    public static String format(Date date) {
+        if (date == null) {
+            return null;
+        }
+        SimpleDateFormat format = new SimpleDateFormat(FORMAT_TIME);
+        return format.format(date);
+    }
+
+    /**
+     * 格式化日期字符串
+     *
+     * @param date 日期
+     * @param pattern 日期格式
+     * @return
+     */
+    public static String format(Date date, String pattern) {
+        if (date == null) {
+            return null;
+        }
+        SimpleDateFormat format = new SimpleDateFormat(pattern);
+        return format.format(date);
+    }
+
+    /**
+     * 格式化时间戳
+     *
+     * @param timestamp
+     * @return
+     */
+    public static String format(Long timestamp) {
+        String dateStr = "";
+        if (null == timestamp || timestamp.longValue() < 0) {
+            return dateStr;
+        }
+        try {
+            Date date = new Date(timestamp);
+            SimpleDateFormat format = new SimpleDateFormat(FORMAT_TIME);
+            dateStr = format.format(date);
+        } catch (Exception e) {
+            // ignore
+        }
+
+        return dateStr;
+    }
+
+    /**
+     * 格式化时间戳字符串
+     *
+     * @param timestamp
+     * @return
+     */
+    public static String format(Long timestamp, String pattern) {
+        String dateStr = "";
+        if (null == timestamp || timestamp.longValue() < 0) {
+            return dateStr;
+        }
+        try {
+            Date date = new Date(timestamp);
+            SimpleDateFormat format = new SimpleDateFormat(pattern);
+            dateStr = format.format(date);
+        } catch (Exception e) {
+            // ignore
+        }
+
+        return dateStr;
+    }
+
+    /**
+     * 按照时区转换时间
+     *
+     * @param date
+     * @param timeZone 时区
+     * @param parrten
+     * @return
+     */
+    public static String format(Date date, TimeZone timeZone, String parrten) {
+        if (date == null) {
+            return null;
+        }
+        SimpleDateFormat sdf = new SimpleDateFormat(parrten);
+        sdf.setTimeZone(timeZone);
+        return sdf.format(date);
+    }
+
+    // parse
+
+    /**
+     * 解析日期
+     *
+     * @param date 日期字符串
+     * @return
+     */
+    public static Date parse(String date) {
+        if (date == null) {
+            return null;
+        }
+        try {
+            return new SimpleDateFormat(FORMAT_TIME).parse(date);
+        } catch (ParseException e) {
+            return null;
+        }
+    }
+
+    /**
+     * 解析日期字符串
+     *
+     * @param date 日期字符串
+     * @param pattern 日期格式
+     * @return
+     */
+    public static Date parse(String date, String pattern) {
+        if (date == null) {
+            return null;
+        }
+        Date resultDate = null;
+        try {
+            resultDate = new SimpleDateFormat(pattern).parse(date);
+        } catch (ParseException e) {
+
+        }
+        return resultDate;
+    }
+
+    // forward
+
+    /**
+     * 获取当前时间前几天时间
+     *
+     * @param days
+     * @return
+     */
+    public static Date forwardDay(Integer days) {
+        DateTime dt = new DateTime();
+        DateTime y = dt.minusDays(days);
+        return y.toDate();
+    }
+
+    /**
+     * 获取当前时间前几天时间,按指定格式返回
+     *
+     * @param days
+     * @return
+     */
+    public static String forwardDay(Integer days, String format) {
+        DateTime dt = new DateTime();
+        DateTime y = dt.minusDays(days);
+        return y.toString(format);
+    }
+
+    // add
+
+    /**
+     * xx年之后
+     *
+     * @param date
+     * @param offset
+     * @return
+     */
+    public static Date addYear(Date date, int offset) {
+        DateTime dt1;
+        if (date == null) {
+            dt1 = new DateTime().plusYears(offset);
+            return dt1.toDate();
+        }
+        dt1 = new DateTime(date).plusYears(offset);
+        return dt1.toDate();
+    }
+
+    /**
+     * xx月之后
+     *
+     * @param date
+     * @param offset
+     * @return
+     */
+    public static Date addWeeks(Date date, int offset) {
+        DateTime dt1;
+        if (date == null) {
+            dt1 = new DateTime().plusWeeks(offset);
+            return dt1.toDate();
+        }
+        dt1 = new DateTime(date).plusWeeks(offset);
+        return dt1.toDate();
+    }
+
+    /**
+     * xx日之后
+     *
+     * @param date
+     * @param offset
+     * @return
+     */
+    public static Date addDays(Date date, int offset) {
+        DateTime dt1;
+        if (date == null) {
+            dt1 = new DateTime().plusDays(offset);
+            return dt1.toDate();
+        }
+        dt1 = new DateTime(date).plusDays(offset);
+        return dt1.toDate();
+    }
+
+    /**
+     * xx时之后
+     *
+     * @param date
+     * @param offset
+     * @return
+     */
+    public static Date addHours(Date date, int offset) {
+        DateTime dt1;
+        if (date == null) {
+            dt1 = new DateTime().plusHours(offset);
+            return dt1.toDate();
+        }
+        dt1 = new DateTime(date).plusHours(offset);
+        return dt1.toDate();
+    }
+
+    /**
+     * xx分之后
+     *
+     * @param date
+     * @param offset
+     * @return
+     */
+    public static Date addMinutes(Date date, int offset) {
+        DateTime dt1;
+        if (date == null) {
+            dt1 = new DateTime().plusMinutes(offset);
+            return dt1.toDate();
+        }
+        dt1 = new DateTime(date).plusMinutes(offset);
+        return dt1.toDate();
+    }
+
+    /**
+     * xx秒之后
+     *
+     * @param date
+     * @param offset
+     * @return
+     */
+    public static Date addSeconds(Date date, int offset) {
+        DateTime dt1;
+        if (date == null) {
+            dt1 = new DateTime().plusSeconds(offset);
+            return dt1.toDate();
+        }
+        dt1 = new DateTime(date).plusSeconds(offset);
+        return dt1.toDate();
+    }
+
+    // forward
+
+    /**
+     * 获取某月之前,之后某一个月最后一天,24:59:59
+     *
+     * @return
+     */
+    public static Date lastDay(Date date, Integer month) {
+        DateTime dt1;
+        if (month == null) {
+            month = 0;
+        }
+        if (date == null) {
+            dt1 = new DateTime().minusMonths(month);
+        } else {
+            dt1 = new DateTime(date).minusMonths(month);
+        }
+        DateTime lastDay = dt1.dayOfMonth().withMaximumValue().withHourOfDay(23).withMinuteOfHour(59).withSecondOfMinute(59);
+        return lastDay.toDate();
+    }
+
+    /**
+     * 获取某月月之前,之后某一个月第一天,00:00:00
+     *
+     * @return
+     */
+    public static Date firstDay(Date date, Integer month) {
+        DateTime dt1;
+        if (month == null) {
+            month = 0;
+        }
+        if (date == null) {
+            dt1 = new DateTime().minusMonths(month);
+        } else {
+            dt1 = new DateTime(date).minusMonths(month);
+        }
+        DateTime lastDay = dt1.dayOfMonth().withMinimumValue().withHourOfDay(0).withMinuteOfHour(0).withSecondOfMinute(0);
+        return lastDay.toDate();
+    }
+
+    /**
+     * 获取指定时间之后或者之前的某一天00:00:00 默认返回当天
+     *
+     * @param days
+     * @return
+     */
+    public static Date day00(Integer days, String date, String zimeZone) throws Throwable {
+        DateTime dt;
+        TimeZone timeZone;
+        try {
+            if (StringUtils.isBlank(zimeZone)) {
+                timeZone = TimeZone.getDefault();
+            } else {
+                timeZone = TimeZone.getTimeZone(zimeZone);
+            }
+            if (StringUtils.isBlank(date)) {
+                dt = new DateTime().withZone(DateTimeZone.forTimeZone(timeZone)).toLocalDateTime().toDateTime();
+            } else {
+                dt = new DateTime(date).withZone(DateTimeZone.forTimeZone(timeZone)).toLocalDateTime().toDateTime();
+            }
+        } catch (Exception e) {
+            throw new Throwable(e);
+        }
+
+        DateTime y = dt.minusDays(days).withHourOfDay(0).withMinuteOfHour(0).withSecondOfMinute(0);
+        return y.toDate();
+    }
+
+    /**
+     * 获取指定时间之后或者之前的某一天23:59:59 默认返回当天
+     *
+     * @param days 偏移量
+     * @return
+     */
+    public static Date day59(Integer days, String date, String zimeZone) throws Throwable {
+        DateTime dt;
+        TimeZone timeZone;
+        try {
+            if (StringUtils.isBlank(zimeZone)) {
+                timeZone = TimeZone.getDefault();
+            } else {
+                timeZone = TimeZone.getTimeZone(zimeZone);
+            }
+            if (StringUtils.isBlank(date)) {
+
+                dt = new DateTime().withZone(DateTimeZone.forTimeZone(timeZone)).toLocalDateTime().toDateTime();
+            } else {
+                dt = new DateTime(date).withZone(DateTimeZone.forTimeZone(timeZone)).toLocalDateTime().toDateTime();
+            }
+        } catch (Exception e) {
+            throw new Throwable(e);
+        }
+        DateTime y = dt.minusDays(days).withHourOfDay(23).withMinuteOfHour(59).withSecondOfMinute(59);
+        return y.toDate();
+    }
+
+    /**
+     * 计算两个时间相差多少天
+     *
+     * @param startDate
+     * @param endDate
+     * @return
+     */
+    public static Integer diffDay(Date startDate, Date endDate) {
+        if (startDate == null || endDate == null) {
+            return null;
+        }
+        DateTime dt1 = new DateTime(startDate);
+        DateTime dt2 = new DateTime(endDate);
+        int day = Days.daysBetween(dt1, dt2).getDays();
+        return Math.abs(day);
+    }
+
+    /**
+     * 传入日期时间与当前系统日期时间的比较, 若日期相同,则根据时分秒来返回 , 否则返回具体日期
+     *
+     * @return 日期或者 xx小时前||xx分钟前||xx秒前
+     */
+    public static String getNewUpdateDateString(Date now, Date createDate) {
+        if (now == null || createDate == null) {
+            return null;
+        }
+        Long time = (now.getTime() - createDate.getTime());
+        if (time > (24 * 60 * 60 * 1000)) {
+            return time / (24 * 60 * 60 * 1000) + "天前";
+        } else if (time > (60 * 60 * 1000)) {
+            return time / (60 * 60 * 1000) + "小时前";
+        } else if (time > (60 * 1000)) {
+            return time / (60 * 1000) + "分钟前";
+        } else if (time >= 1000) {
+            return time / 1000 + "秒前";
+        }
+        return "刚刚";
+    }
+}

+ 139 - 0
src/main/java/com/xiesx/fastboot/utils/DesensitizedUtils.java

@@ -0,0 +1,139 @@
+package com.xiesx.fastboot.utils;
+
+import org.apache.commons.lang3.StringUtils;
+
+public class DesensitizedUtils {
+
+    /**
+     * 【中文姓名】只显示第一个汉字,其他隐藏为2个星号,比如:李**
+     *
+     * @param fullName
+     * @return
+     */
+    public static String chineseName(String fullName) {
+        if (StringUtils.isBlank(fullName)) {
+            return "";
+        }
+        String name = StringUtils.left(fullName, 1);
+        return StringUtils.rightPad(name, StringUtils.length(fullName), "*");
+    }
+
+    /**
+     * 【身份证号】显示最后四位,其他隐藏。共计18位或者15位,比如:*************1234
+     *
+     * @param id
+     * @return
+     */
+    public static String idCardNum(String id) {
+        if (StringUtils.isBlank(id)) {
+            return "";
+        }
+        String num = StringUtils.right(id, 4);
+        return StringUtils.leftPad(num, StringUtils.length(id), "*");
+    }
+
+    /**
+     * 【固定电话 后四位,其他隐藏,比如1234
+     *
+     * @param num
+     * @return
+     */
+    public static String fixedPhone(String num) {
+        if (StringUtils.isBlank(num)) {
+            return "";
+        }
+        return StringUtils.leftPad(StringUtils.right(num, 4), StringUtils.length(num), "*");
+    }
+
+    /**
+     * 【手机号码】前三位,后四位,其他隐藏,比如138****0000
+     *
+     * @param num
+     * @return
+     */
+    public static String mobilePhone(String num) {
+        if (StringUtils.isBlank(num)) {
+            return "";
+        }
+        return StringUtils.left(num, 3)
+                .concat(StringUtils.removeStart(StringUtils.leftPad(StringUtils.right(num, 4), StringUtils.length(num), "*"), "***"));
+    }
+
+    /**
+     * 【地址】只显示到地区,不显示详细地址,比如:北京市海淀区****
+     *
+     * @param address
+     * @param sensitiveSize 敏感信息长度
+     * @return
+     */
+    public static String address(String address, int sensitiveSize) {
+        if (StringUtils.isBlank(address)) {
+            return "";
+        }
+        int length = StringUtils.length(address);
+        return StringUtils.rightPad(StringUtils.left(address, length - sensitiveSize), length, "*");
+    }
+
+    /**
+     * 【电子邮箱 邮箱前缀仅显示第一个字母,前缀其他隐藏,用星号代替,@及后面的地址显示,比如:d**@126.com>
+     *
+     * @param email
+     * @return
+     */
+    public static String email(String email) {
+        if (StringUtils.isBlank(email)) {
+            return "";
+        }
+        int index = StringUtils.indexOf(email, "@");
+        if (index <= 1) {
+            return email;
+        } else {
+            return StringUtils.rightPad(StringUtils.left(email, 1), index, "*")
+                    .concat(StringUtils.mid(email, index, StringUtils.length(email)));
+        }
+    }
+
+    /**
+     * 【银行卡号】前六位,后四位,其他用星号隐藏每位1个星号,比如:6222600**********1234>
+     *
+     * @param cardNum
+     * @return
+     */
+    public static String bankCard(String cardNum) {
+        if (StringUtils.isBlank(cardNum)) {
+            return "";
+        }
+        return StringUtils.left(cardNum, 6)
+                .concat(StringUtils.removeStart(StringUtils.leftPad(StringUtils.right(cardNum, 4), StringUtils.length(cardNum), "*"),
+                        "******"));
+    }
+
+    /**
+     * 【密码】密码的全部字符都用*代替,比如:******
+     *
+     * @param password
+     * @return
+     */
+    public static String password(String password) {
+        if (StringUtils.isBlank(password)) {
+            return "";
+        }
+        String pwd = StringUtils.left(password, 0);
+        return StringUtils.rightPad(pwd, StringUtils.length(password), "*");
+    }
+
+    /**
+     * 【车牌号】前两位后一位,比如:苏M****5
+     *
+     * @param carNumber
+     * @return
+     */
+    public static String carNumber(String carNumber) {
+        if (StringUtils.isBlank(carNumber)) {
+            return "";
+        }
+        return StringUtils.left(carNumber, 2)
+                .concat(StringUtils.removeStart(StringUtils.leftPad(StringUtils.right(carNumber, 1), StringUtils.length(carNumber), "*"),
+                        "**"));
+    }
+}

+ 341 - 0
src/main/java/com/xiesx/fastboot/utils/EncryptUtil.java

@@ -0,0 +1,341 @@
+package com.xiesx.fastboot.utils;
+
+import java.security.MessageDigest;
+import java.security.SecureRandom;
+
+import javax.crypto.Cipher;
+import javax.crypto.KeyGenerator;
+import javax.crypto.Mac;
+import javax.crypto.SecretKey;
+import javax.crypto.spec.SecretKeySpec;
+
+import org.apache.commons.codec.binary.Base64;
+
+public class EncryptUtil {
+
+    public static final String MD5 = "MD5";
+
+    public static final String SHA1 = "SHA1";
+
+    public static final String HmacMD5 = "HmacMD5";
+
+    public static final String HmacSHA1 = "HmacSHA1";
+
+    public static final String DES = "DES";
+
+    public static final String AES = "AES";
+
+    /** 编码格式;默认使用uft-8 */
+    public String charset = "utf-8";
+
+    /** DES */
+    public int keysizeDES = 56;
+
+    /** AES */
+    public int keysizeAES = 128;
+
+    public static EncryptUtil me;
+
+    private EncryptUtil() {
+        // 单例
+    }
+
+    // 双重锁
+    public static EncryptUtil getInstance() {
+        if (me == null) {
+            synchronized (EncryptUtil.class) {
+                if (me == null) {
+                    me = new EncryptUtil();
+                }
+            }
+        }
+        return me;
+    }
+
+    /**
+     * 使用MessageDigest进行单向加密(无密码)
+     *
+     * @param res 被加密的文本
+     * @param algorithm 加密算法名称
+     * @return
+     */
+    private String messageDigest(String res, String algorithm) {
+        try {
+            MessageDigest md = MessageDigest.getInstance(algorithm);
+            byte[] resBytes = charset == null ? res.getBytes() : res.getBytes(charset);
+            return Base64.encodeBase64String(md.digest(resBytes));
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        return null;
+    }
+
+    /**
+     * 使用KeyGenerator进行单向/双向加密(可设密码)
+     *
+     * @param res 被加密的原文
+     * @param algorithm 加密使用的算法名称
+     * @param key 加密使用的秘钥
+     * @return
+     */
+    private String keyGeneratorMac(String res, String algorithm, String key) {
+        try {
+            SecretKey sk = null;
+            if (key == null) {
+                KeyGenerator kg = KeyGenerator.getInstance(algorithm);
+                sk = kg.generateKey();
+            } else {
+                byte[] keyBytes = charset == null ? key.getBytes() : key.getBytes(charset);
+                sk = new SecretKeySpec(keyBytes, algorithm);
+            }
+            Mac mac = Mac.getInstance(algorithm);
+            mac.init(sk);
+            byte[] result = mac.doFinal(res.getBytes());
+            return Base64.encodeBase64String(result);
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        return null;
+    }
+
+    /**
+     * 使用KeyGenerator双向加密,DES/AES,注意这里转化为字符串的时候是将2进制转为16进制格式的字符串,不是直接转,因为会出错
+     *
+     * @param res 加密的原文
+     * @param algorithm 加密使用的算法名称
+     * @param key 加密的秘钥
+     * @param keysize
+     * @param isEncode
+     * @return
+     */
+    private String keyGeneratorES(String res, String algorithm, String key, int keysize, boolean isEncode) {
+        try {
+            KeyGenerator kg = KeyGenerator.getInstance(algorithm);
+            if (keysize == 0) {
+                byte[] keyBytes = charset == null ? key.getBytes() : key.getBytes(charset);
+                SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG");
+                secureRandom.setSeed(keyBytes);
+                kg.init(secureRandom);
+                // 该写法linux有问题,https://blog.csdn.net/weixin_30670965/article/details/99920195
+                // kg.init(new SecureRandom(keyBytes));
+            } else if (key == null) {
+                kg.init(keysize);
+            } else {
+                byte[] keyBytes = charset == null ? key.getBytes() : key.getBytes(charset);
+                SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG");
+                secureRandom.setSeed(keyBytes);
+                kg.init(keysize, secureRandom);
+                // 该写法linux有问题,https://blog.csdn.net/weixin_30670965/article/details/99920195
+                // kg.init(keysize, new SecureRandom(keyBytes));
+            }
+            SecretKey sk = kg.generateKey();
+            SecretKeySpec sks = new SecretKeySpec(sk.getEncoded(), algorithm);
+            Cipher cipher = Cipher.getInstance(algorithm);
+            if (isEncode) {
+                cipher.init(Cipher.ENCRYPT_MODE, sks);
+                byte[] resBytes = charset == null ? res.getBytes() : res.getBytes(charset);
+                return parseByte2HexStr(cipher.doFinal(resBytes));
+            } else {
+                cipher.init(Cipher.DECRYPT_MODE, sks);
+                return new String(cipher.doFinal(parseHexStr2Byte(res)));
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        return null;
+    }
+
+    /** 将二进制转换成16进制 */
+    public static String parseByte2HexStr(byte buf[]) {
+        StringBuffer sb = new StringBuffer();
+        for (byte element : buf) {
+            String hex = Integer.toHexString(element & 0xFF);
+            if (hex.length() == 1) {
+                hex = '0' + hex;
+            }
+            sb.append(hex.toUpperCase());
+        }
+        return sb.toString();
+    }
+
+    /** 将16进制转换为二进制 */
+    public static byte[] parseHexStr2Byte(String hexStr) {
+        if (hexStr.length() < 1) {
+            return null;
+        }
+        byte[] result = new byte[hexStr.length() / 2];
+        for (int i = 0; i < hexStr.length() / 2; i++) {
+            int high = Integer.parseInt(hexStr.substring(i * 2, i * 2 + 1), 16);
+            int low = Integer.parseInt(hexStr.substring(i * 2 + 1, i * 2 + 2), 16);
+            result[i] = (byte) (high * 16 + low);
+        }
+        return result;
+    }
+
+    // ==================
+
+    /**
+     * md5加密算法进行加密(不可逆)
+     *
+     * @param res 需要加密的原文
+     * @return
+     */
+    public String MD5(String res) {
+        return messageDigest(res, MD5);
+    }
+
+    /**
+     * md5加密算法进行加密(不可逆)
+     *
+     * @param res 需要加密的原文
+     * @param key 秘钥
+     * @return
+     */
+    public String MD5(String res, String key) {
+        return keyGeneratorMac(res, HmacMD5, key);
+    }
+
+    // ==================
+
+    /**
+     * 使用SHA1加密算法进行加密(不可逆)
+     *
+     * @param res 需要加密的原文
+     * @return
+     */
+    public String SHA1(String res) {
+        return messageDigest(res, SHA1);
+    }
+
+    /**
+     * 使用SHA1加密算法进行加密(不可逆)
+     *
+     * @param res 需要加密的原文
+     * @param key 秘钥
+     * @return
+     */
+    public String SHA1(String res, String key) {
+        return keyGeneratorMac(res, HmacSHA1, key);
+    }
+
+    // ==================
+
+    /**
+     * 使用DES加密算法进行加密(可逆)
+     *
+     * @param res 需要加密的原文
+     * @param key 秘钥
+     * @return
+     */
+    public String DESencode(String res, String key) {
+        return keyGeneratorES(res, DES, key, keysizeDES, true);
+    }
+
+    /**
+     * 对使用DES加密算法的密文进行解密(可逆)
+     *
+     * @param res 需要解密的密文
+     * @param key 秘钥
+     * @return
+     */
+    public String DESdecode(String res, String key) {
+        return keyGeneratorES(res, DES, key, keysizeDES, false);
+    }
+
+    // ==================
+
+    /**
+     * 使用AES加密算法经行加密(可逆)
+     *
+     * @param res 需要加密的密文
+     * @param key 秘钥
+     * @return
+     */
+    public String AESencode(String res, String key) {
+        return keyGeneratorES(res, AES, key, keysizeAES, true);
+    }
+
+    /**
+     * 对使用AES加密算法的密文进行解密(可逆)
+     *
+     * @param res 需要解密的密文
+     * @param key 秘钥
+     * @return
+     */
+    public String AESdecode(String res, String key) {
+        return keyGeneratorES(res, AES, key, keysizeAES, false);
+    }
+
+    // ==================
+
+    /**
+     * 使用异或进行加密
+     *
+     * @param res 需要加密的密文
+     * @param key 秘钥
+     * @return
+     */
+    public String XORencode(String res, String key) {
+        byte[] bs = res.getBytes();
+        for (int i = 0; i < bs.length; i++) {
+            bs[i] = (byte) ((bs[i]) ^ key.hashCode());
+        }
+        return parseByte2HexStr(bs);
+    }
+
+    /**
+     * 使用异或进行解密
+     *
+     * @param res 需要解密的密文
+     * @param key 秘钥
+     * @return
+     */
+    public String XORdecode(String res, String key) {
+        byte[] bs = parseHexStr2Byte(res);
+        for (int i = 0; i < bs.length; i++) {
+            bs[i] = (byte) ((bs[i]) ^ key.hashCode());
+        }
+        return new String(bs);
+    }
+
+    /**
+     * 直接使用异或(第一调用加密,第二次调用解密)
+     *
+     * @param res 密文
+     * @param key 秘钥
+     * @return
+     */
+    public int XOR(int res, String key) {
+        return res ^ key.hashCode();
+    }
+
+    // ==================
+
+    /**
+     * 使用Base64进行加密
+     *
+     * @param res 密文
+     * @return
+     */
+    public String Base64Encode(String res) {
+        return Base64.encodeBase64String(res.getBytes());
+    }
+
+    /**
+     * 使用Base64进行解密
+     *
+     * @param res
+     * @return
+     */
+    public String Base64Decode(String res) {
+        return new String(Base64.decodeBase64(res));
+    }
+
+    public static void main(String[] args) {
+        String key = "123456";
+        System.out.println(EncryptUtil.getInstance().SHA1("136305973"));
+        System.out.println(EncryptUtil.getInstance().Base64Encode("136305973"));
+        System.out.println(EncryptUtil.getInstance().DESencode("1215193073783185410", key));
+        System.out.println(EncryptUtil.getInstance().DESdecode("6EE9BDDA68A885A24476426308E0E2C951F0F6CDECDBAA81", key));
+    }
+}

+ 234 - 0
src/main/java/com/xiesx/fastboot/utils/RuntimeUtils.java

@@ -0,0 +1,234 @@
+package com.xiesx.fastboot.utils;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.lang.management.ManagementFactory;
+import java.lang.reflect.InvocationTargetException;
+import java.net.URL;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.lang3.SystemUtils;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import com.google.common.collect.Maps;
+
+public class RuntimeUtils {
+
+    private static final Log _LOG = LogFactory.getLog(RuntimeUtils.class);
+
+    /**
+     * 系统环境变量映射
+     */
+    private static final Map<String, String> SYSTEM_ENV_MAP = Maps.newHashMap();
+
+    static {
+        initSystemEnvs();
+    }
+
+    /**
+     * 初始化系统环境,获取当前系统环境变量
+     */
+    @SuppressWarnings("deprecation")
+    public static void initSystemEnvs() {
+        Process p = null;
+        BufferedReader br = null;
+        try {
+            if (SystemUtils.IS_OS_WINDOWS) {
+                p = Runtime.getRuntime().exec("cmd /c set");
+            } else if (SystemUtils.IS_OS_UNIX) {
+                p = Runtime.getRuntime().exec("/bin/sh -c set");
+            } else {
+                _LOG.warn("Unknown os.name=" + SystemUtils.OS_NAME);
+                SYSTEM_ENV_MAP.clear();
+            }
+            if (p != null) {
+                br = new BufferedReader(new InputStreamReader(p.getInputStream()));
+                String line;
+                while ((line = br.readLine()) != null) {
+                    int i = line.indexOf('=');
+                    if (i > -1) {
+                        String key = line.substring(0, i);
+                        String value = line.substring(i + 1);
+                        SYSTEM_ENV_MAP.put(key, value);
+                    }
+                }
+            }
+        } catch (IOException ignored) {
+        } finally {
+            IOUtils.closeQuietly(br);
+            if (p != null) {
+                p.destroy();
+            }
+        }
+    }
+
+    /**
+     * 获取系统运行时,可以进行缓存
+     *
+     * @return 环境变量对应表
+     */
+    public static Map<String, String> getSystemEnvs() {
+        if (SYSTEM_ENV_MAP.isEmpty()) {
+            initSystemEnvs();
+        }
+        return SYSTEM_ENV_MAP;
+    }
+
+    /**
+     * 获取指定名称的环境值
+     *
+     * @param envName 环境名,如果为空,返回null
+     * @return 当指定名称为空或者对应名称环境变量不存在时返回空
+     */
+    public static String getSystemEnv(String envName) {
+        if (StringUtils.isNotBlank(envName)) {
+            if (SYSTEM_ENV_MAP.isEmpty()) {
+                initSystemEnvs();
+            }
+            return SYSTEM_ENV_MAP.get(envName);
+        }
+        return null;
+    }
+
+    /**
+     * @return 当前操作系统是否为类Unix系统
+     */
+    public static boolean isUnixOrLinux() {
+        return SystemUtils.IS_OS_UNIX;
+    }
+
+    /**
+     * @return 当前操作系统是否为Windows系统
+     */
+    public static boolean isWindows() {
+        return SystemUtils.IS_OS_WINDOWS;
+    }
+
+    /**
+     * @return 返回当前程序执行进程编号
+     */
+    public static String getProcessId() {
+        return StringUtils.split(ManagementFactory.getRuntimeMXBean().getName(), "@")[0];
+    }
+
+    /**
+     * @return 获取应用根路径(若WEB工程则基于.../WEB-INF/返回,若普通工程则返回类所在路径)
+     */
+    public static String getRootPath() {
+        return getRootPath(true);
+    }
+
+    /**
+     * @param safe 若WEB工程是否保留WEB-INF
+     * @return 返回应用根路径
+     */
+    public static String getRootPath(boolean safe) {
+        //
+        String _rootPath = null;
+        //
+        URL _rootURL = RuntimeUtils.class.getClassLoader().getResource("/");
+        if (_rootURL == null) {
+            _rootURL = RuntimeUtils.class.getClassLoader().getResource("");
+            if (_rootURL != null) {
+                _rootPath = _rootURL.getPath();
+            }
+        } else {
+            _rootPath = StringUtils.removeEnd(StringUtils.substringBefore(_rootURL.getPath(), safe ? "classes/" : "WEB-INF/"), "/");
+        }
+        //
+        if (_rootPath != null) {
+            _rootPath = StringUtils.replace(_rootPath, "%20", " ");
+            if (isWindows()) {
+                _rootPath = StringUtils.removeStart(_rootPath, "/");
+            }
+        }
+        return StringUtils.trimToEmpty(_rootPath);
+    }
+
+    /**
+     * 根据格式化字符串,生成运行时异常
+     *
+     * @param format 格式
+     * @param args 参数
+     * @return 运行时异常
+     */
+    public static RuntimeException makeRuntimeThrow(String format, Object... args) {
+        return new RuntimeException(String.format(format, args));
+    }
+
+    /**
+     * 将抛出对象包裹成运行时异常,并增加描述
+     *
+     * @param e 抛出对象
+     * @param fmt 格式
+     * @param args 参数
+     * @return 运行时异常
+     */
+    public static RuntimeException wrapRuntimeThrow(Throwable e, String fmt, Object... args) {
+        return new RuntimeException(String.format(fmt, args), e);
+    }
+
+    /**
+     * 用运行时异常包裹抛出对象,如果抛出对象本身就是运行时异常,则直接返回
+     * <p>
+     * 若 e 对象是 InvocationTargetException,则将其剥离,仅包裹其 TargetException 对象
+     * </p>
+     *
+     * @param e 抛出对象
+     * @return 运行时异常
+     */
+    public static RuntimeException wrapRuntimeThrow(Throwable e) {
+        if (e instanceof RuntimeException) {
+            return (RuntimeException) e;
+        }
+        if (e instanceof InvocationTargetException) {
+            return wrapRuntimeThrow(((InvocationTargetException) e).getTargetException());
+        }
+        return new RuntimeException(e);
+    }
+
+    public static Throwable unwrapThrow(Throwable e) {
+        if (e == null) {
+            return null;
+        }
+        if (e instanceof InvocationTargetException) {
+            InvocationTargetException itE = (InvocationTargetException) e;
+            if (itE.getTargetException() != null) {
+                return unwrapThrow(itE.getTargetException());
+            }
+        }
+        if (e.getCause() != null) {
+            return unwrapThrow(e.getCause());
+        }
+        return e;
+    }
+
+    /**
+     * 垃圾回收,返回回收的字节数
+     *
+     * @return 回收的字节数,如果为负数则表示当前内存使用情况很差,基本属于没有内存可用了
+     */
+    public static long gc() {
+        Runtime rt = Runtime.getRuntime();
+        long lastUsed = rt.totalMemory() - rt.freeMemory();
+        rt.gc();
+        return lastUsed - rt.totalMemory() + rt.freeMemory();
+    }
+
+    public static Map<String, String> keyStartsWith(Map<String, String> map, String keyPrefix) {
+        Map<String, String> _returnValues = new HashMap<>();
+        for (Map.Entry<String, String> _entry : map.entrySet()) {
+            String _key = _entry.getKey();
+            if (StringUtils.startsWith(_key, keyPrefix)) {
+                String _cfgKey = StringUtils.substring(_key, keyPrefix.length());
+                _returnValues.put(_cfgKey, _entry.getValue());
+            }
+        }
+        return _returnValues;
+    }
+}

+ 91 - 0
src/main/java/com/xiesx/fastboot/utils/ValidatorHelper.java

@@ -0,0 +1,91 @@
+package com.xiesx.fastboot.utils;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import javax.validation.ConstraintViolation;
+import javax.validation.ConstraintViolationException;
+import javax.validation.Validator;
+import javax.validation.groups.Default;
+
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import com.xiesx.fastboot.SpringHelper;
+
+/**
+ * @title ValidatorHelper.java
+ * @description
+ * @author Sixian.xie
+ * @date 2020-8-18 0:40:49
+ */
+@SuppressWarnings("all")
+public class ValidatorHelper {
+
+    private static Validator validator = SpringHelper.getBean(Validator.class);
+
+    public static void validate(Object object) throws ConstraintViolationException {
+        Set<? extends ConstraintViolation<?>> constraintViolations = validator.validate(object, Default.class);
+        if (!constraintViolations.isEmpty()) {
+            throw new ConstraintViolationException(constraintViolations);
+        }
+    }
+
+    public static void validate(Object object, Class<?>... groups) throws ConstraintViolationException {
+        Set<? extends ConstraintViolation<?>> constraintViolations = validator.validate(object, groups);
+        if (!constraintViolations.isEmpty()) {
+            throw new ConstraintViolationException(constraintViolations);
+        }
+    }
+
+    // =============
+
+    public static List<String> extractMessage(ConstraintViolationException e) {
+        return extractMessage(e.getConstraintViolations());
+    }
+
+    public static List<String> extractMessage(Set<? extends ConstraintViolation> constraintViolations) {
+        List<String> errorMessages = Lists.newArrayList();
+        for (ConstraintViolation<String> violation : constraintViolations) {
+            errorMessages.add(violation.getMessage());
+        }
+        return errorMessages;
+    }
+
+    // =============
+
+    public static Map<String, String> extractPropertyAndMessage(ConstraintViolationException e) {
+        return extractPropertyAndMessage(e.getConstraintViolations());
+    }
+
+    @SuppressWarnings("rawtypes")
+    public static Map<String, String> extractPropertyAndMessage(Set<? extends ConstraintViolation> constraintViolations) {
+        Map<String, String> errorMsgs = Maps.newHashMap();
+        for (ConstraintViolation<?> violation : constraintViolations) {
+            errorMsgs.put(violation.getPropertyPath().toString(), violation.getMessage());
+        }
+        return errorMsgs;
+    }
+
+    // =============
+
+    public static List<String> extractPropertyAndMessageAsList(ConstraintViolationException e) {
+        return extractPropertyAndMessageAsList(e.getConstraintViolations(), " ");
+    }
+
+    public static List<String> extractPropertyAndMessageAsList(Set<? extends ConstraintViolation> constraintViolations) {
+        return extractPropertyAndMessageAsList(constraintViolations, " ");
+    }
+
+    public static List<String> extractPropertyAndMessageAsList(ConstraintViolationException e, String separator) {
+        return extractPropertyAndMessageAsList(e.getConstraintViolations(), separator);
+    }
+
+    public static List<String> extractPropertyAndMessageAsList(Set<? extends ConstraintViolation> constraintViolations, String separator) {
+        List<String> errorMessages = Lists.newArrayList();
+        for (ConstraintViolation<?> violation : constraintViolations) {
+            errorMessages.add(violation.getPropertyPath() + separator + violation.getMessage());
+        }
+        return errorMessages;
+    }
+}

+ 156 - 0
src/main/resources/META-INF/core.tld

@@ -0,0 +1,156 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<taglib xmlns="http://java.sun.com/xml/ns/j2ee"
+	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-jsptaglibrary_2_0.xsd"
+	version="2.0">
+
+	<short-name>fastboot</short-name>
+	<tlib-version>1.0</tlib-version>
+	<uri>http://java.sun.com/jsp/jstl/fastboot</uri>
+
+	<display-name>FastBootTagLib</display-name>
+	<description>FastBootTagLib Core Tag Library 1.0</description>
+
+	<!-- ui -->
+	<!-- UITag -->
+	<tag>
+		<display-name>UITag</display-name>
+		<name>ui</name>
+		<tag-class>com.xiesx.fastboot.tag.ui.UITag</tag-class>
+		<body-content>JSP</body-content>
+		<attribute>
+			<name>src</name>
+			<rtexprvalue>true</rtexprvalue>
+			<type>java.lang.String</type>
+		</attribute>
+		<attribute>
+			<name>cleanup</name>
+			<rtexprvalue>true</rtexprvalue>
+			<type>java.lang.Boolean</type>
+		</attribute>
+	</tag>
+
+	<!-- LayoutTag -->
+	<tag>
+		<display-name>LayoutTag</display-name>
+		<name>layout</name>
+		<tag-class>com.xiesx.fastboot.tag.ui.LayoutTag</tag-class>
+		<body-content>JSP</body-content>
+		<attribute>
+			<name>name</name>
+			<rtexprvalue>true</rtexprvalue>
+			<type>java.lang.String</type>
+		</attribute>
+		<attribute>
+			<name>src</name>
+			<rtexprvalue>true</rtexprvalue>
+			<type>java.lang.String</type>
+		</attribute>
+		<attribute>
+			<name>cleanup</name>
+			<rtexprvalue>true</rtexprvalue>
+			<type>java.lang.Boolean</type>
+		</attribute>
+	</tag>
+
+	<!-- body -->
+	<!-- PropertyTag -->
+	<tag>
+		<display-name>PropertyTag</display-name>
+		<name>property</name>
+		<tag-class>com.xiesx.fastboot.tag.body.PropertyTag</tag-class>
+		<body-content>JSP</body-content>
+		<attribute>
+			<name>name</name>
+			<required>true</required>
+			<rtexprvalue>true</rtexprvalue>
+			<type>java.lang.String</type>
+		</attribute>
+		<attribute>
+			<name>value</name>
+			<rtexprvalue>true</rtexprvalue>
+			<type>java.lang.String</type>
+		</attribute>
+	</tag>
+
+	<!-- ScriptTag -->
+	<tag>
+		<display-name>ScriptTag</display-name>
+		<name>script</name>
+		<tag-class>com.xiesx.fastboot.tag.body.ScriptTag</tag-class>
+		<body-content>JSP</body-content>
+		<attribute>
+			<name>id</name>
+			<rtexprvalue>true</rtexprvalue>
+			<type>java.lang.String</type>
+		</attribute>
+		<attribute>
+			<name>src</name>
+			<rtexprvalue>true</rtexprvalue>
+			<type>java.lang.String</type>
+		</attribute>
+		<attribute>
+			<name>value</name>
+			<rtexprvalue>true</rtexprvalue>
+			<type>java.lang.String</type>
+		</attribute>
+		<attribute>
+			<name>type</name>
+			<rtexprvalue>true</rtexprvalue>
+			<type>java.lang.String</type>
+		</attribute>
+		<attribute>
+			<name>wrapper</name>
+			<rtexprvalue>true</rtexprvalue>
+			<type>java.lang.String</type>
+		</attribute>
+	</tag>
+
+	<!-- MetaTag -->
+	<tag>
+		<display-name>MetaTag</display-name>
+		<name>meta</name>
+		<tag-class>com.xiesx.fastboot.tag.body.MetaTag</tag-class>
+		<body-content>JSP</body-content>
+		<attribute>
+			<name>attrKey</name>
+			<rtexprvalue>true</rtexprvalue>
+			<type>java.lang.String</type>
+		</attribute>
+		<attribute>
+			<name>attrValue</name>
+			<rtexprvalue>true</rtexprvalue>
+			<type>java.lang.String</type>
+		</attribute>
+	</tag>
+
+	<!-- CssTag -->
+	<tag>
+		<display-name>CssTag</display-name>
+		<name>css</name>
+		<tag-class>com.xiesx.fastboot.tag.body.CssTag</tag-class>
+		<body-content>empty</body-content>
+		<attribute>
+			<name>href</name>
+			<required>true</required>
+			<rtexprvalue>true</rtexprvalue>
+			<type>java.lang.String</type>
+		</attribute>
+		<attribute>
+			<name>rel</name>
+			<rtexprvalue>true</rtexprvalue>
+			<type>java.lang.String</type>
+		</attribute>
+		<attribute>
+			<name>media</name>
+			<rtexprvalue>true</rtexprvalue>
+			<type>java.lang.String</type>
+		</attribute>
+		<attribute>
+			<name>type</name>
+			<rtexprvalue>true</rtexprvalue>
+			<type>java.lang.String</type>
+		</attribute>
+	</tag>
+
+</taglib>

+ 71 - 0
src/main/resources/META-INF/spring-configuration-metadata.json

@@ -0,0 +1,71 @@
+{
+  "groups": [
+    {
+      "name": "fastboot.fastjson",
+      "type": "com.xiesx.fastboot.core.fastjson.cfg.FastJsonProperties",
+      "sourceType": "com.xiesx.fastboot.core.fastjson.cfg.FastJsonProperties"
+    },
+    {
+      "name": "fastboot.sign",
+      "type": "com.xiesx.fastboot.core.sign.cfg.SignProperties",
+      "sourceType": "com.xiesx.fastboot.core.sign.cfg.SignProperties"
+    },
+    {
+      "name": "fastboot.token",
+      "type": "com.xiesx.fastboot.core.token.cfg.TokenProperties",
+      "sourceType": "com.xiesx.fastboot.core.token.cfg.TokenProperties"
+    }
+  ],
+  "properties": [
+    {
+      "name": "fastboot.fastjson.dateFormat",
+      "type": "java.lang.String",
+      "defaultValue": "yyyy-MM-dd HH:mm:ss",
+      "description": "日期格式"
+    },
+    {
+      "name": "fastboot.sign.header",
+      "type": "java.lang.String",
+      "description": "数据签名header",
+      "defaultValue": "sign"
+    },
+    {
+      "name": "fastboot.sign.secret",
+      "type": "java.lang.String",
+      "defaultValue": "fastboot",
+      "description": "数据签名加密串"
+    },
+    {
+      "name": "fastboot.token.header",
+      "type": "java.lang.String",
+      "defaultValue": "token",
+      "description": "令牌认证header"
+    },
+    {
+      "name": "fastboot.token.includePaths",
+      "type": "java.lang.String",
+      "description": "包含请求"
+    },
+    {
+      "name": "fastboot.token.excludePaths",
+      "type": "java.lang.String",
+      "description": "排除请求"
+    }
+  ],
+  "hints": [
+    {
+      "name": "fastboot.fastjson.dateFormat",
+      "values": [
+        {
+          "value": "yyyy-MM-dd HH:mm:ss"
+        },
+        {
+          "value": "yyyy-MM-dd"
+        },
+        {
+          "value": "HH:mm:ss"
+        }
+      ]
+    }
+  ]
+}

+ 2 - 0
src/main/resources/META-INF/spring.factories

@@ -0,0 +1,2 @@
+org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
+com.xiesx.fastboot.SpringContextAware

+ 25 - 0
src/main/resources/application.yml

@@ -0,0 +1,25 @@
+spring:
+  datasource:
+    driver-class-name: com.mysql.cj.jdbc.Driver
+    url: jdbc:mysql://127.0.0.1:3306/xxxx?characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&autoReconnect=true&useSSL=false&serverTimezone=Asia/Shanghai
+    username: xxx
+    password: xxx
+
+logging:
+  config: classpath:log4j2.xml
+ 
+fastboot:
+# ======= 数据转换 =======
+  fastjson:
+    date-format: yyyy-MM-dd HH:mm:ss
+  # ======= 数据签名 =======
+  sign:
+    header: sign
+    secret: 123456780
+  # ======= 令牌认证 =======
+  token:
+    header: token
+    include-paths:
+    - /api/**
+    exclude-paths:
+    - /static/**

+ 6 - 0
src/main/resources/banner.txt

@@ -0,0 +1,6 @@
+    ______           __  ____              __ 
+   / ____/___ ______/ /_/ __ )____  ____  / /_
+  / /_  / __ `/ ___/ __/ __  / __ \/ __ \/ __/
+ / __/ / /_/ (__  ) /_/ /_/ / /_/ / /_/ / /_  
+/_/    \__,_/____/\__/_____/\____/\____/\__/  Power By SpringBoot${spring-boot.formatted-version} 
+

+ 66 - 0
src/main/resources/log4j2.xml

@@ -0,0 +1,66 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--日志级别 按照从低到高为:1.ALL < 2.TRACE < 3.DEBUG < 4.INFO < 5.WARN < 6.ERROR < 7.FATAL < 8.OFF -->
+<!--1.ALL:最低等级的,用于打开所有日志记录 -->
+<!--2.Trace:是追踪,就是程序推进一下 -->
+<!--3.Debug:指出细粒度信息事件对调试应用程序是非常有帮助的 -->
+<!--4.INFO:消息在粗粒度级别上突出强调应用程序的运行过程 -->
+<!--5.WARN:输出警告及warn以下级别的日志 -->
+<!--6.ERROR:输出错误信息日志 -->
+<!--7.FATAL:输出每个严重的错误事件将会导致应用程序的退出的日志 -->
+<!--8.OFF:最高等级的,用于关闭所有日志记录 -->
+<!-- -->
+<!--Configuration后面的status,这个用于设置log4j2自身内部的信息输出,可以不设置,当设置成trace时,你会看到log4j2内部各种详细输出 -->
+<!--Configuration后面的monitorInterval:Log4j能够自动检测修改配置 文件和重新配置本身,设置间隔秒数 -->
+<Configuration status="WARN" monitorInterval="30">
+	<!-- 配置 -->
+	<Properties>
+		<!-- 存放目录(本地) -->
+		<Property name="path">../logs</Property>
+		<!-- 输出格式 -->
+		<Property name="log_pattern">[FBOOT][%5p][%-d{MM-dd HH:mm:ss}]-->[%t:%5r][%M(%F:%L)] | - %m%n</Property>
+	</Properties>
+
+	<!-- 追加 -->
+	<Appenders>
+		<!-- 控制台日志 -->
+		<Console name="console" target="SYSTEM_OUT">
+			<PatternLayout pattern="${sys:log_pattern}" />
+		</Console>
+
+		<!-- ALL存档日志 -->
+		<RollingFile name="all" fileName="${path}/gotv_error.log" filePattern="${path}/gotv_error_%d{MM-dd-yyyy}.log.%i">
+			<!-- 输出格式 -->
+			<PatternLayout pattern="${sys:log_pattern}" />
+			<!-- 最大文件50M -->
+			<SizeBasedTriggeringPolicy size="50 MB" />
+			<!-- 最多产生20个文件 -->
+			<DefaultRolloverStrategy max="20" fileIndex="min" />
+		</RollingFile>
+
+		<!-- Error存档日志 -->
+		<RollingFile name="error" fileName="${path}/gotv_error.log" filePattern="${path}/gotv_error_%d{MM-dd-yyyy}.log.%i">
+			<!-- 只输出error及以上级别的信息,否则直接拒绝 -->
+			<ThresholdFilter level="error" onMatch="ACCEPT" onMismatch="DENY" />
+			<!-- 输出格式 -->
+			<PatternLayout pattern="${sys:log_pattern}" />
+			<!-- 最大文件50M -->
+			<SizeBasedTriggeringPolicy size="50 MB" />
+			<!-- 最多产生20个文件 -->
+			<DefaultRolloverStrategy max="20" fileIndex="min" />
+		</RollingFile>
+	</Appenders>
+
+	<Loggers>
+		<!-- 默认DEBUG -->
+		<Root level="INFO">
+			<AppenderRef ref="console" />
+			<AppenderRef ref="all" />
+			<AppenderRef ref="error" />
+		</Root>
+		<!-- 第三方ERROR -->
+		<Logger name="org.springframework" level="ERROR" />
+		<Logger name="org.hibernate" level="ERROR" />
+		<Logger name="com.querydsl" level="ERROR" />
+		<Logger name="org.apache" level="ERROR" />
+	</Loggers>
+</Configuration>