瀏覽代碼

第一次提交

nahida 1 年之前
當前提交
81de62673d
共有 44 個文件被更改,包括 1934 次插入0 次删除
  1. 41 0
      .gitignore
  2. 99 0
      data-service/pom.xml
  3. 12 0
      data-service/src/main/java/com/zksy/data/DataApplication.java
  4. 20 0
      data-service/src/main/resources/application-dev.yaml
  5. 17 0
      data-service/src/main/resources/application-prod.yaml
  6. 24 0
      data-service/src/main/resources/bootstrap.yaml
  7. 125 0
      pom.xml
  8. 105 0
      xh-common/pom.xml
  9. 68 0
      xh-common/src/main/java/com/zksy/common/advice/CommonExceptionAdvice.java
  10. 23 0
      xh-common/src/main/java/com/zksy/common/config/JsonConfig.java
  11. 17 0
      xh-common/src/main/java/com/zksy/common/config/MvcConfig.java
  12. 25 0
      xh-common/src/main/java/com/zksy/common/config/MyBatisConfig.java
  13. 48 0
      xh-common/src/main/java/com/zksy/common/domain/Result.java
  14. 22 0
      xh-common/src/main/java/com/zksy/common/domain/dto/ConditionDTO.java
  15. 61 0
      xh-common/src/main/java/com/zksy/common/domain/dto/PageDTO.java
  16. 69 0
      xh-common/src/main/java/com/zksy/common/domain/query/PageQuery.java
  17. 16 0
      xh-common/src/main/java/com/zksy/common/exception/BadRequestException.java
  18. 16 0
      xh-common/src/main/java/com/zksy/common/exception/BizIllegalException.java
  19. 23 0
      xh-common/src/main/java/com/zksy/common/exception/CommonException.java
  20. 16 0
      xh-common/src/main/java/com/zksy/common/exception/DbException.java
  21. 16 0
      xh-common/src/main/java/com/zksy/common/exception/ForbiddenException.java
  22. 16 0
      xh-common/src/main/java/com/zksy/common/exception/UnauthorizedException.java
  23. 21 0
      xh-common/src/main/java/com/zksy/common/filter/UserInfoInterceptor.java
  24. 59 0
      xh-common/src/main/java/com/zksy/common/utils/BeanUtils.java
  25. 72 0
      xh-common/src/main/java/com/zksy/common/utils/CollUtils.java
  26. 8 0
      xh-common/src/main/java/com/zksy/common/utils/Convert.java
  27. 67 0
      xh-common/src/main/java/com/zksy/common/utils/CookieBuilder.java
  28. 25 0
      xh-common/src/main/java/com/zksy/common/utils/RedisUtils.java
  29. 59 0
      xh-common/src/main/java/com/zksy/common/utils/SearchUtil.java
  30. 28 0
      xh-common/src/main/java/com/zksy/common/utils/UserContext.java
  31. 151 0
      xh-common/src/main/java/com/zksy/common/utils/WebUtils.java
  32. 171 0
      xh-common/src/main/resources/META-INF/spring-configuration-metadata.json
  33. 4 0
      xh-common/src/main/resources/META-INF/spring.factories
  34. 65 0
      xh-gateway/pom.xml
  35. 12 0
      xh-gateway/src/main/java/com/zksy/gateway/GatewayApplication.java
  36. 15 0
      xh-gateway/src/main/java/com/zksy/gateway/config/AuthProperties.java
  37. 16 0
      xh-gateway/src/main/java/com/zksy/gateway/config/JwtProperties.java
  38. 33 0
      xh-gateway/src/main/java/com/zksy/gateway/config/SecurityConfig.java
  39. 66 0
      xh-gateway/src/main/java/com/zksy/gateway/filter/AuthGlobalFilter.java
  40. 74 0
      xh-gateway/src/main/java/com/zksy/gateway/route/DynamicRouteLoader.java
  41. 82 0
      xh-gateway/src/main/java/com/zksy/gateway/utils/JwtTool.java
  42. 13 0
      xh-gateway/src/main/resources/application.yaml
  43. 14 0
      xh-gateway/src/main/resources/bootstrap.yaml
  44. 二進制
      xh-gateway/src/main/resources/hmall.jks

+ 41 - 0
.gitignore

@@ -0,0 +1,41 @@
+target/
+!.mvn/wrapper/maven-wrapper.jar
+!**/src/main/**/target/
+!**/src/test/**/target/
+
+### IntelliJ IDEA ###
+.idea/modules.xml
+.idea/jarRepositories.xml
+.idea/compiler.xml
+.idea/libraries/
+*.iws
+*.iml
+*.ipr
+logs/
+.idea/
+.idea
+
+### Eclipse ###
+.apt_generated
+.classpath
+.factorypath
+.project
+.settings
+.springBeans
+.sts4-cache
+
+### NetBeans ###
+/nbproject/private/
+/nbbuild/
+/dist/
+/nbdist/
+/.nb-gradle/
+build/
+!**/src/main/**/build/
+!**/src/test/**/build/
+
+### VS Code ###
+.vscode/
+
+### Mac OS ###
+.DS_Store

+ 99 - 0
data-service/pom.xml

@@ -0,0 +1,99 @@
+<?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>
+    <version>1.0.0</version>
+    <parent>
+        <groupId>com.zksy</groupId>
+        <artifactId>xh-server-micro</artifactId>
+        <version>1.0.0</version>
+    </parent>
+    <artifactId>data-service</artifactId>
+
+    <groupId>com.zksy.data</groupId>
+    <properties>
+        <maven.compiler.source>11</maven.compiler.source>
+        <maven.compiler.target>11</maven.compiler.target>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+    </properties>
+
+    <dependencies>
+
+        <!--common-->
+        <dependency>
+            <groupId>com.zksy</groupId>
+            <artifactId>xh-common</artifactId>
+            <version>1.0.0</version>
+        </dependency>
+        <!--        api-->
+        <!--        <dependency>-->
+        <!--            <groupId>com.heima</groupId>-->
+        <!--            <artifactId>hm-api</artifactId>-->
+        <!--            <version>1.0.0</version>-->
+        <!--        </dependency>-->
+        <!--web-->
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-web</artifactId>
+        </dependency>
+        <!--数据库-->
+        <dependency>
+            <groupId>mysql</groupId>
+            <artifactId>mysql-connector-java</artifactId>
+        </dependency>
+        <!--mybatis-->
+        <dependency>
+            <groupId>com.baomidou</groupId>
+            <artifactId>mybatis-plus-boot-starter</artifactId>
+        </dependency>
+        <!--nacos 服务注册发现-->
+        <dependency>
+            <groupId>com.alibaba.cloud</groupId>
+            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
+        </dependency>
+        <!--负载均衡-->
+        <dependency>
+            <groupId>org.springframework.cloud</groupId>
+            <artifactId>spring-cloud-starter-loadbalancer</artifactId>
+        </dependency>
+        <!--统一配置管理-->
+        <dependency>
+            <groupId>com.alibaba.cloud</groupId>
+            <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
+        </dependency>
+        <!--加载bootstrap-->
+        <dependency>
+            <groupId>org.springframework.cloud</groupId>
+            <artifactId>spring-cloud-starter-bootstrap</artifactId>
+        </dependency>
+        <!--redis-->
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-data-redis</artifactId>
+        </dependency>
+        <!--sentinel-->
+        <dependency>
+            <groupId>com.alibaba.cloud</groupId>
+            <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.zksy</groupId>
+            <artifactId>minioutil</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.redisson</groupId>
+            <artifactId>redisson</artifactId>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <finalName>${project.artifactId}</finalName>
+        <plugins>
+            <plugin>
+                <groupId>org.springframework.boot</groupId>
+                <artifactId>spring-boot-maven-plugin</artifactId>
+            </plugin>
+        </plugins>
+    </build>
+</project>

+ 12 - 0
data-service/src/main/java/com/zksy/data/DataApplication.java

@@ -0,0 +1,12 @@
+package com.zksy.data;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication
+public class DataApplication {
+    public static void main(String[] args) {
+        SpringApplication.run(DataApplication.class, args);
+        System.out.println("远程调用服务启动成功");
+    }
+}

+ 20 - 0
data-service/src/main/resources/application-dev.yaml

@@ -0,0 +1,20 @@
+zksy:
+  db:
+    host: 192.168.110.30
+    un: root
+    pw: 123
+    port: 3307
+    database: xh_credit_rating
+spring:
+  redis:
+    host: 192.168.110.30
+    port: 6379
+minio:
+  endpoint: http://192.168.110.30:9000
+  accessKey: minio
+  secretKey: minio123
+  bucket: zksy-file
+  readPath: http://192.168.110.30:9000
+
+
+

+ 17 - 0
data-service/src/main/resources/application-prod.yaml

@@ -0,0 +1,17 @@
+zksy:
+  db:
+    host: 192.168.110.30
+    un: root
+    pw: 123
+    port: 3307
+    database: xh_credit_rating
+spring:
+  redis:
+    host: 192.168.110.30
+    port: 6379
+minio:
+  endpoint: http://192.168.110.30:9000
+  accessKey: minio
+  secretKey: minio123
+  bucket: test
+  readPath: http://192.168.110.30:9000

+ 24 - 0
data-service/src/main/resources/bootstrap.yaml

@@ -0,0 +1,24 @@
+spring:
+  application:
+    name: data-service
+  profiles:
+    active: dev
+  main:
+    allow-bean-definition-overriding: true
+  cloud:
+    sentinel:
+      transport:
+        dashboard: 192.168.110.30:8090
+      http-method-specify: true
+    nacos:
+      discovery:
+        server-addr: 192.168.110.30:8848
+        namespace: 5d4109ea-3d63-47d2-ae99-2cc52b06eb1f
+      config:
+        namespace: 5d4109ea-3d63-47d2-ae99-2cc52b06eb1f
+        server-addr: 192.168.110.30:8848
+        file-extension: yaml
+        shared-configs:
+          - dataId: data-service.yaml
+          - dataId: xh-shared-jdbc.yaml
+          - dataId: xh-shared-log.yaml

+ 125 - 0
pom.xml

@@ -0,0 +1,125 @@
+<?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.zksy</groupId>
+   <artifactId>xh-server-micro</artifactId>
+   <packaging>pom</packaging>
+   <version>1.0.0</version>
+   <modules>
+      <module>xh-gateway</module>
+      <module>xh-common</module>
+      <module>data-service</module>
+   </modules>
+
+   <parent>
+      <groupId>org.springframework.boot</groupId>
+      <artifactId>spring-boot-starter-parent</artifactId>
+      <version>2.7.12</version>
+      <relativePath/>
+   </parent>
+
+   <properties>
+      <maven.compiler.source>11</maven.compiler.source>
+      <maven.compiler.target>11</maven.compiler.target>
+      <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+      <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
+      <org.projectlombok.version>1.18.20</org.projectlombok.version>
+      <spring-cloud.version>2021.0.3</spring-cloud.version>
+      <spring-cloud-alibaba.version>2021.0.4.0</spring-cloud-alibaba.version>
+      <mybatis-plus.version>3.4.3</mybatis-plus.version>
+      <hutool.version>5.8.11</hutool.version>
+      <mysql.version>8.0.23</mysql.version>
+      <okhttp.version>4.9.3</okhttp.version>
+      <redis.version>3.0.5</redis.version>
+      <sentinel.version>2021.0.4.0</sentinel.version>
+      <minioutil.version>1.0.0</minioutil.version>
+      <mqtt.version>5.5.9</mqtt.version>
+      <netty.version>4.1.75.Final</netty.version>
+      <redisson.version>3.13.6</redisson.version>
+   </properties>
+   <dependencyManagement>
+      <dependencies>
+         <!--spring cloud-->
+         <dependency>
+            <groupId>org.springframework.cloud</groupId>
+            <artifactId>spring-cloud-dependencies</artifactId>
+            <version>${spring-cloud.version}</version>
+            <type>pom</type>
+            <scope>import</scope>
+         </dependency>
+         <!--spring cloud alibaba-->
+         <dependency>
+            <groupId>com.alibaba.cloud</groupId>
+            <artifactId>spring-cloud-alibaba-dependencies</artifactId>
+            <version>${spring-cloud-alibaba.version}</version>
+            <type>pom</type>
+            <scope>import</scope>
+         </dependency>
+         <!-- 数据库驱动包管理 -->
+         <dependency>
+            <groupId>mysql</groupId>
+            <artifactId>mysql-connector-java</artifactId>
+            <version>${mysql.version}</version>
+         </dependency>
+         <!-- mybatis plus 管理 -->
+         <dependency>
+            <groupId>com.baomidou</groupId>
+            <artifactId>mybatis-plus-boot-starter</artifactId>
+            <version>${mybatis-plus.version}</version>
+         </dependency>
+         <!--hutool工具包-->
+         <dependency>
+            <groupId>cn.hutool</groupId>
+            <artifactId>hutool-all</artifactId>
+            <version>${hutool.version}</version>
+         </dependency>
+         <!--OKHTTP请求包-->
+         <dependency>
+            <groupId>com.squareup.okhttp3</groupId>
+            <artifactId>okhttp</artifactId>
+            <version>${okhttp.version}</version>
+         </dependency>
+         <!--redis-->
+         <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-data-redis</artifactId>
+            <version>${redis.version}</version>
+         </dependency>
+         <!--sentinel-->
+         <dependency>
+            <groupId>com.alibaba.cloud</groupId>
+            <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
+            <version>${sentinel.version}</version>
+         </dependency>
+         <dependency>
+            <groupId>com.zksy</groupId>
+            <artifactId>minioutil</artifactId>
+            <version>${minioutil.version}</version>
+         </dependency>
+         <dependency>
+            <groupId>org.redisson</groupId>
+            <artifactId>redisson</artifactId>
+            <version>${redisson.version}</version>
+         </dependency>
+      </dependencies>
+   </dependencyManagement>
+
+   <build>
+      <pluginManagement>
+         <plugins>
+            <plugin>
+               <groupId>org.apache.maven.plugins</groupId>
+               <artifactId>maven-compiler-plugin</artifactId>
+               <version>3.8.1</version>
+               <configuration>
+                  <source>11</source> <!-- depending on your project -->
+                  <target>11</target> <!-- depending on your project -->
+               </configuration>
+            </plugin>
+         </plugins>
+      </pluginManagement>
+   </build>
+
+</project>

+ 105 - 0
xh-common/pom.xml

@@ -0,0 +1,105 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <groupId>com.zksy</groupId>
+        <artifactId>xh-server-micro</artifactId>
+        <version>1.0.0</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>xh-common</artifactId>
+
+    <properties>
+        <maven.compiler.source>11</maven.compiler.source>
+        <maven.compiler.target>11</maven.compiler.target>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.commons</groupId>
+            <artifactId>commons-pool2</artifactId>
+        </dependency>
+        <!--hutool工具包-->
+        <dependency>
+            <groupId>cn.hutool</groupId>
+            <artifactId>hutool-all</artifactId>
+        </dependency>
+        <!--完成SpringMVC自动装配-->
+        <dependency>
+            <groupId>org.springframework</groupId>
+            <artifactId>spring-webmvc</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <!--日志-->
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-logging</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.tomcat.embed</groupId>
+            <artifactId>tomcat-embed-core</artifactId>
+            <version>9.0.73</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>com.baomidou</groupId>
+            <artifactId>mybatis-plus-core</artifactId>
+            <version>${mybatis-plus.version}</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>com.baomidou</groupId>
+            <artifactId>mybatis-plus-extension</artifactId>
+            <version>${mybatis-plus.version}</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.hibernate.validator</groupId>
+            <artifactId>hibernate-validator</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-autoconfigure</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.github.xiaoymin</groupId>
+            <artifactId>knife4j-openapi2-spring-boot-starter</artifactId>
+            <version>4.1.0</version>
+        </dependency>
+        <!--caffeine-->
+        <dependency>
+            <groupId>com.github.ben-manes.caffeine</groupId>
+            <artifactId>caffeine</artifactId>
+        </dependency>
+        <!--AMQP依赖-->
+        <dependency>
+            <groupId>org.springframework.amqp</groupId>
+            <artifactId>spring-amqp</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <!--Spring整合Rabbit依赖-->
+        <dependency>
+            <groupId>org.springframework.amqp</groupId>
+            <artifactId>spring-rabbit</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <!--json处理-->
+        <dependency>
+            <groupId>com.fasterxml.jackson.dataformat</groupId>
+            <artifactId>jackson-dataformat-xml</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <!--redis依赖-->
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-data-redis</artifactId>
+        </dependency>
+        <!--redisson依赖-->
+        <dependency>
+            <groupId>org.redisson</groupId>
+            <artifactId>redisson</artifactId>
+        </dependency>
+    </dependencies>
+</project>

+ 68 - 0
xh-common/src/main/java/com/zksy/common/advice/CommonExceptionAdvice.java

@@ -0,0 +1,68 @@
+package com.zksy.common.advice;
+
+import com.zksy.common.domain.Result;
+import com.zksy.common.exception.BadRequestException;
+import com.zksy.common.exception.CommonException;
+import com.zksy.common.exception.DbException;
+import com.zksy.common.utils.WebUtils;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.http.ResponseEntity;
+import org.springframework.validation.ObjectError;
+import org.springframework.web.bind.MethodArgumentNotValidException;
+import org.springframework.web.bind.annotation.ExceptionHandler;
+import org.springframework.web.bind.annotation.RestControllerAdvice;
+import org.springframework.web.util.NestedServletException;
+
+import java.net.BindException;
+import java.util.stream.Collectors;
+
+@RestControllerAdvice
+@Slf4j
+public class CommonExceptionAdvice {
+
+    @ExceptionHandler(DbException.class)
+    public Object handleDbException(DbException e) {
+        log.error("mysql数据库操作异常 -> ", e);
+        return processResponse(e);
+    }
+
+    @ExceptionHandler(CommonException.class)
+    public Object handleBadRequestException(CommonException e) {
+        log.error("自定义异常 -> {} , 异常原因:{}  ",e.getClass().getName(), e.getMessage());
+        log.debug("", e);
+        return processResponse(e);
+    }
+
+    @ExceptionHandler(MethodArgumentNotValidException.class)
+    public Object handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {
+        String msg = e.getBindingResult().getAllErrors()
+                .stream().map(ObjectError::getDefaultMessage)
+                .collect(Collectors.joining("|"));
+        log.error("请求参数校验异常 -> {}", msg);
+        log.debug("", e);
+        return processResponse(new BadRequestException(msg));
+    }
+    @ExceptionHandler(BindException.class)
+    public Object handleBindException(BindException e) {
+        log.error("请求参数绑定异常 ->BindException, {}", e.getMessage());
+        log.debug("", e);
+        return processResponse(new BadRequestException("请求参数格式错误"));
+    }
+
+    @ExceptionHandler(NestedServletException.class)
+    public Object handleNestedServletException(NestedServletException e) {
+        log.error("参数异常 -> NestedServletException,{}", e.getMessage());
+        log.debug("", e);
+        return processResponse(new BadRequestException("请求参数处理异常"));
+    }
+
+    @ExceptionHandler(Exception.class)
+    public Object handleRuntimeException(Exception e) {
+        log.error("其他异常 uri : {} -> ", WebUtils.getRequest().getRequestURI(), e);
+        return processResponse(new CommonException("服务器内部异常", 500));
+    }
+
+    private ResponseEntity<Result<Void>> processResponse(CommonException e){
+        return ResponseEntity.status(e.getCode()).body(Result.error(e));
+    }
+}

+ 23 - 0
xh-common/src/main/java/com/zksy/common/config/JsonConfig.java

@@ -0,0 +1,23 @@
+package com.zksy.common.config;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
+import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+import java.math.BigInteger;
+
+@Configuration
+@ConditionalOnClass(ObjectMapper.class)
+public class JsonConfig {
+    @Bean
+    public Jackson2ObjectMapperBuilderCustomizer jackson2ObjectMapperBuilderCustomizer() {
+        return jacksonObjectMapperBuilder -> {
+            // long -> string
+            jacksonObjectMapperBuilder.serializerByType(Long.class, ToStringSerializer.instance);
+            jacksonObjectMapperBuilder.serializerByType(BigInteger.class, ToStringSerializer.instance);
+        };
+    }
+}

+ 17 - 0
xh-common/src/main/java/com/zksy/common/config/MvcConfig.java

@@ -0,0 +1,17 @@
+package com.zksy.common.config;
+
+import com.zksy.common.filter.UserInfoInterceptor;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.servlet.DispatcherServlet;
+import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
+
+@Configuration
+@ConditionalOnClass(DispatcherServlet.class)
+public class MvcConfig implements WebMvcConfigurer {
+    @Override
+    public void addInterceptors(InterceptorRegistry registry) {
+        registry.addInterceptor(new UserInfoInterceptor());
+    }
+}

+ 25 - 0
xh-common/src/main/java/com/zksy/common/config/MyBatisConfig.java

@@ -0,0 +1,25 @@
+package com.zksy.common.config;
+
+import com.baomidou.mybatisplus.annotation.DbType;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
+import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+@Configuration
+@ConditionalOnClass({MybatisPlusInterceptor.class, BaseMapper.class})
+public class MyBatisConfig {
+    @Bean
+    @ConditionalOnMissingBean
+    public MybatisPlusInterceptor mybatisPlusInterceptor() {
+        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
+        // 1.分页拦截器
+        PaginationInnerInterceptor paginationInnerInterceptor = new PaginationInnerInterceptor(DbType.MYSQL);
+        paginationInnerInterceptor.setMaxLimit(1000L);
+        interceptor.addInnerInterceptor(paginationInnerInterceptor);
+        return interceptor;
+    }
+}

+ 48 - 0
xh-common/src/main/java/com/zksy/common/domain/Result.java

@@ -0,0 +1,48 @@
+package com.zksy.common.domain;
+
+import com.zksy.common.exception.CommonException;
+import lombok.Data;
+
+
+/**
+ * @author Administrator
+ */
+@Data
+public class Result<T> {
+    private int code;
+    private String msg;
+    private T data;
+
+    public static Result<Void> ok() {
+        return ok(null);
+    }
+
+    public static <T> Result<T> ok(T data) {
+        return new Result<>(200, "OK", data);
+    }
+
+    public static <T> Result<T> error(String msg) {
+        return new Result<>(500, msg, null);
+    }
+
+    public static <T> Result<T> error(int code, String msg) {
+        return new Result<>(code, msg, null);
+    }
+
+    public static <T> Result<T> error(CommonException e) {
+        return new Result<>(e.getCode(), e.getMessage(), null);
+    }
+
+    public Result() {
+    }
+
+    public Result(int code, String msg, T data) {
+        this.code = code;
+        this.msg = msg;
+        this.data = data;
+    }
+
+    public boolean success(){
+        return code == 200;
+    }
+}

+ 22 - 0
xh-common/src/main/java/com/zksy/common/domain/dto/ConditionDTO.java

@@ -0,0 +1,22 @@
+package com.zksy.common.domain.dto;
+
+import lombok.Data;
+
+import java.io.Serializable;
+
+@Data
+public class ConditionDTO implements Serializable {
+    private static final long serialVersionUID = -5099378457111419832L;
+    /**
+     * 数据库字段名
+     */
+    private String column;
+    /**
+     * 字段值
+     */
+    private String value;
+    /**
+     * 连接类型,如llike,equals,gt,ge,lt,le
+     */
+    private String type;
+}

+ 61 - 0
xh-common/src/main/java/com/zksy/common/domain/dto/PageDTO.java

@@ -0,0 +1,61 @@
+package com.zksy.common.domain.dto;
+
+
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.zksy.common.utils.BeanUtils;
+import com.zksy.common.utils.CollUtils;
+import com.zksy.common.utils.Convert;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.util.List;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+public class PageDTO<T> {
+    protected Long total;
+    protected Long pages;
+    protected List<T> list;
+
+    public static <T> PageDTO<T> empty(Long total, Long pages) {
+        return new PageDTO<>(total, pages, CollUtils.emptyList());
+    }
+    public static <T> PageDTO<T> empty(Page<?> page) {
+        return new PageDTO<>(page.getTotal(), page.getPages(), CollUtils.emptyList());
+    }
+
+    public static <T> PageDTO<T> of(Page<T> page) {
+        if(page == null){
+            return new PageDTO<>();
+        }
+        if (CollUtils.isEmpty(page.getRecords())) {
+            return empty(page);
+        }
+        return new PageDTO<>(page.getTotal(), page.getPages(), page.getRecords());
+    }
+    public static <T,R> PageDTO<T> of(Page<R> page, Function<R, T> mapper) {
+        if(page == null){
+            return new PageDTO<>();
+        }
+        if (CollUtils.isEmpty(page.getRecords())) {
+            return empty(page);
+        }
+        return new PageDTO<>(page.getTotal(), page.getPages(),
+                page.getRecords().stream().map(mapper).collect(Collectors.toList()));
+    }
+    public static <T> PageDTO<T> of(Page<?> page, List<T> list) {
+        return new PageDTO<>(page.getTotal(), page.getPages(), list);
+    }
+
+    public static <T, R> PageDTO<T> of(Page<R> page, Class<T> clazz) {
+        return new PageDTO<>(page.getTotal(), page.getPages(), BeanUtils.copyList(page.getRecords(), clazz));
+    }
+
+    public static <T, R> PageDTO<T> of(Page<R> page, Class<T> clazz, Convert<R, T> convert) {
+        return new PageDTO<>(page.getTotal(), page.getPages(), BeanUtils.copyList(page.getRecords(), clazz, convert));
+    }
+}

+ 69 - 0
xh-common/src/main/java/com/zksy/common/domain/query/PageQuery.java

@@ -0,0 +1,69 @@
+package com.zksy.common.domain.query;
+
+import cn.hutool.core.util.StrUtil;
+import com.baomidou.mybatisplus.core.metadata.OrderItem;
+import com.baomidou.mybatisplus.core.toolkit.StringUtils;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import lombok.experimental.Accessors;
+
+import javax.validation.constraints.Min;
+
+@Data
+@ApiModel(description = "分页查询条件")
+@Accessors(chain = true)
+public class PageQuery {
+    public static final Integer DEFAULT_PAGE_SIZE = 20;
+    public static final Integer DEFAULT_PAGE_NUM = 1;
+    @ApiModelProperty("页码")
+    @Min(value = 1, message = "页码不能小于1")
+    private Integer pageNo = DEFAULT_PAGE_NUM;
+    @ApiModelProperty("页码")
+    @Min(value = 1, message = "每页查询数量不能小于1")
+    private Integer pageSize = DEFAULT_PAGE_SIZE;
+    @ApiModelProperty("是否升序")
+    private Boolean isAsc = true;
+    @ApiModelProperty("排序方式")
+    private String sortBy;
+
+    public int from(){
+        return (pageNo - 1) * pageSize;
+    }
+
+    public <T> Page<T> toMpPage(OrderItem... orderItems) {
+        Page<T> page = new Page<>(pageNo, pageSize);
+        // 是否手动指定排序方式
+        if (orderItems != null && orderItems.length > 0) {
+            for (OrderItem orderItem : orderItems) {
+                page.addOrder(orderItem);
+            }
+            return page;
+        }
+        // 前端是否有排序字段
+        if (StrUtil.isNotEmpty(sortBy)){
+            OrderItem orderItem = new OrderItem();
+            orderItem.setAsc(isAsc);
+            orderItem.setColumn(sortBy);
+            page.addOrder(orderItem);
+        }
+        return page;
+    }
+
+    public <T> Page<T> toMpPage(String defaultSortBy, boolean isAsc) {
+        if (StringUtils.isBlank(sortBy)){
+            sortBy = defaultSortBy;
+            this.isAsc = isAsc;
+        }
+        Page<T> page = new Page<>(pageNo, pageSize);
+        OrderItem orderItem = new OrderItem();
+        orderItem.setAsc(this.isAsc);
+        orderItem.setColumn(sortBy);
+        page.addOrder(orderItem);
+        return page;
+    }
+    public <T> Page<T> toMpPageDefaultSortByCreateTimeDesc() {
+        return toMpPage("create_time", false);
+    }
+}

+ 16 - 0
xh-common/src/main/java/com/zksy/common/exception/BadRequestException.java

@@ -0,0 +1,16 @@
+package com.zksy.common.exception;
+
+public class BadRequestException extends CommonException{
+
+    public BadRequestException(String message) {
+        super(message, 400);
+    }
+
+    public BadRequestException(String message, Throwable cause) {
+        super(message, cause, 400);
+    }
+
+    public BadRequestException(Throwable cause) {
+        super(cause, 400);
+    }
+}

+ 16 - 0
xh-common/src/main/java/com/zksy/common/exception/BizIllegalException.java

@@ -0,0 +1,16 @@
+package com.zksy.common.exception;
+
+public class BizIllegalException extends CommonException{
+
+    public BizIllegalException(String message) {
+        super(message, 500);
+    }
+
+    public BizIllegalException(String message, Throwable cause) {
+        super(message, cause, 500);
+    }
+
+    public BizIllegalException(Throwable cause) {
+        super(cause, 500);
+    }
+}

+ 23 - 0
xh-common/src/main/java/com/zksy/common/exception/CommonException.java

@@ -0,0 +1,23 @@
+package com.zksy.common.exception;
+
+import lombok.Getter;
+
+@Getter
+public class CommonException extends RuntimeException{
+    private int code;
+
+    public CommonException(String message, int code) {
+        super(message);
+        this.code = code;
+    }
+
+    public CommonException(String message, Throwable cause, int code) {
+        super(message, cause);
+        this.code = code;
+    }
+
+    public CommonException(Throwable cause, int code) {
+        super(cause);
+        this.code = code;
+    }
+}

+ 16 - 0
xh-common/src/main/java/com/zksy/common/exception/DbException.java

@@ -0,0 +1,16 @@
+package com.zksy.common.exception;
+
+public class DbException extends CommonException{
+
+    public DbException(String message) {
+        super(message, 500);
+    }
+
+    public DbException(String message, Throwable cause) {
+        super(message, cause, 500);
+    }
+
+    public DbException(Throwable cause) {
+        super(cause, 500);
+    }
+}

+ 16 - 0
xh-common/src/main/java/com/zksy/common/exception/ForbiddenException.java

@@ -0,0 +1,16 @@
+package com.zksy.common.exception;
+
+public class ForbiddenException extends CommonException{
+
+    public ForbiddenException(String message) {
+        super(message, 403);
+    }
+
+    public ForbiddenException(String message, Throwable cause) {
+        super(message, cause, 403);
+    }
+
+    public ForbiddenException(Throwable cause) {
+        super(cause, 403);
+    }
+}

+ 16 - 0
xh-common/src/main/java/com/zksy/common/exception/UnauthorizedException.java

@@ -0,0 +1,16 @@
+package com.zksy.common.exception;
+
+public class UnauthorizedException extends CommonException{
+
+    public UnauthorizedException(String message) {
+        super(message, 401);
+    }
+
+    public UnauthorizedException(String message, Throwable cause) {
+        super(message, cause, 401);
+    }
+
+    public UnauthorizedException(Throwable cause) {
+        super(cause, 401);
+    }
+}

+ 21 - 0
xh-common/src/main/java/com/zksy/common/filter/UserInfoInterceptor.java

@@ -0,0 +1,21 @@
+package com.zksy.common.filter;
+
+import cn.hutool.core.util.StrUtil;
+import com.zksy.common.utils.UserContext;
+import org.springframework.web.servlet.HandlerInterceptor;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+public class UserInfoInterceptor implements HandlerInterceptor {
+    @Override
+    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
+
+        return true;
+    }
+
+    @Override
+    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
+
+    }
+}

+ 59 - 0
xh-common/src/main/java/com/zksy/common/utils/BeanUtils.java

@@ -0,0 +1,59 @@
+package com.zksy.common.utils;
+
+import cn.hutool.core.bean.BeanUtil;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * 继承自 hutool 的BeanUtil,增加了bean转换时自定义转换器的功能
+ */
+public class BeanUtils extends BeanUtil {
+
+    /**
+     * 将原对象转换成目标对象,对于字段不匹配的字段可以使用转换器处理
+     *
+     * @param source  原对象
+     * @param clazz   目标对象的class
+     * @param convert 转换器
+     * @param <R>     原对象类型
+     * @param <T>     目标对象类型
+     * @return 目标对象
+     */
+    public static <R, T> T copyBean(R source, Class<T> clazz, Convert<R, T> convert) {
+        T target = copyBean(source, clazz);
+        if (convert != null) {
+            convert.convert(source, target);
+        }
+        return target;
+    }
+    /**
+     * 将原对象转换成目标对象,对于字段不匹配的字段可以使用转换器处理
+     *
+     * @param source  原对象
+     * @param clazz   目标对象的class
+     * @param <R>     原对象类型
+     * @param <T>     目标对象类型
+     * @return 目标对象
+     */
+    public static <R, T> T copyBean(R source, Class<T> clazz){
+        if (source == null) {
+            return null;
+        }
+        return toBean(source, clazz);
+    }
+
+    public static <R, T> List<T> copyList(List<R> list, Class<T> clazz) {
+        if (list == null || list.size() == 0) {
+            return CollUtils.emptyList();
+        }
+        return copyToList(list, clazz);
+    }
+
+    public static <R, T> List<T> copyList(List<R> list, Class<T> clazz, Convert<R, T> convert) {
+        if (list == null || list.size() == 0) {
+            return CollUtils.emptyList();
+        }
+        return list.stream().map(r -> copyBean(r, clazz, convert)).collect(Collectors.toList());
+    }
+}

+ 72 - 0
xh-common/src/main/java/com/zksy/common/utils/CollUtils.java

@@ -0,0 +1,72 @@
+package com.zksy.common.utils;
+
+import cn.hutool.core.collection.CollectionUtil;
+import cn.hutool.core.collection.IterUtil;
+import cn.hutool.core.util.NumberUtil;
+
+import java.util.*;
+import java.util.stream.Collectors;
+
+/**
+ * 继承自 hutool 的集合工具类
+ */
+public class CollUtils extends CollectionUtil {
+
+    public static <T> List<T> emptyList() {
+        return Collections.emptyList();
+    }
+
+    public static <T> Set<T> emptySet() {
+        return Collections.emptySet();
+    }
+
+    public static <K,V> Map<K, V> emptyMap() {
+        return Collections.emptyMap();
+    }
+
+    public static <T> Set<T> singletonSet(T t) {
+        return Collections.singleton(t);
+    }
+
+    public static <T> List<T> singletonList(T t) {
+        return Collections.singletonList(t);
+    }
+
+    public static List<Integer> convertToInteger(List<String> originList){
+        return CollUtils.isNotEmpty(originList) ? originList.stream().map(NumberUtil::parseInt).collect(Collectors.toList()) : null;
+    }
+
+    public static List<Long> convertToLong(List<String> originLIst){
+        return CollUtils.isNotEmpty(originLIst) ? originLIst.stream().map(NumberUtil::parseLong).collect(Collectors.toList()) : null;
+    }
+
+    /**
+     * 以 conjunction 为分隔符将集合转换为字符串 如果集合元素为数组、Iterable或Iterator,则递归组合其为字符串
+     * @param collection 集合
+     * @param conjunction 分隔符
+     * @param <T> 集合元素类型
+     * @return 连接后的字符串
+     * See Also: IterUtil.join(Iterator, CharSequence)
+     */
+    public static <T> String join(Collection<T> collection, CharSequence conjunction) {
+        if (null == collection || collection.isEmpty()) {
+            return null;
+        }
+        return IterUtil.join(collection.iterator(), conjunction);
+    }
+
+    public static <T> String joinIgnoreNull(Collection<T> collection, CharSequence conjunction) {
+        if (null == collection || collection.isEmpty()) {
+            return null;
+        }
+        StringBuilder sb = new StringBuilder();
+        for (T t : collection) {
+            if(t == null) continue;
+            sb.append(t).append(",");
+        }
+        if(sb.length() <= 0){
+            return null;
+        }
+        return sb.deleteCharAt(sb.length() - 1).toString();
+    }
+}

+ 8 - 0
xh-common/src/main/java/com/zksy/common/utils/Convert.java

@@ -0,0 +1,8 @@
+package com.zksy.common.utils;
+
+/**
+ * 对原对象进行计算,设置到目标对象中
+ **/
+public interface Convert<R,T>{
+    void convert(R origin, T target);
+}

+ 67 - 0
xh-common/src/main/java/com/zksy/common/utils/CookieBuilder.java

@@ -0,0 +1,67 @@
+package com.zksy.common.utils;
+
+import cn.hutool.core.util.StrUtil;
+import lombok.Data;
+import lombok.experimental.Accessors;
+import lombok.extern.slf4j.Slf4j;
+
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.net.URLDecoder;
+import java.net.URLEncoder;
+import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
+
+@Slf4j
+@Data
+@Accessors(chain = true, fluent = true)
+public class CookieBuilder {
+    private Charset charset = StandardCharsets.UTF_8;
+    private int maxAge = -1;
+    private String path = "/";
+    private boolean httpOnly;
+    private String name;
+    private String value;
+    private String domain;
+    private final HttpServletRequest request;
+    private final HttpServletResponse response;
+
+    public CookieBuilder(HttpServletRequest request, HttpServletResponse response) {
+        this.request = request;
+        this.response = response;
+    }
+
+    /**
+     * 构建cookie,会对cookie值用UTF-8做URL编码,避免中文乱码
+     */
+    public void build(){
+        if (response == null) {
+            log.error("response为null,无法写入cookie");
+            return;
+        }
+        Cookie cookie = new Cookie(name, URLEncoder.encode(value, charset));
+        if(StrUtil.isNotBlank(domain)) {
+            cookie.setDomain(domain);
+        }else if (request != null) {
+            String serverName = request.getServerName();
+            serverName = StrUtil.subAfter(serverName, ".", false);
+            cookie.setDomain("." + serverName);
+        }
+        cookie.setHttpOnly(httpOnly);
+        cookie.setMaxAge(maxAge);
+        cookie.setPath(path);
+        log.debug("生成cookie,编码方式:{},【{}={},domain:{};maxAge={};path={};httpOnly={}】",
+                charset.name(), name, value, domain, maxAge, path, httpOnly);
+        response.addCookie(cookie);
+    }
+
+    /**
+     * 利用UTF-8对cookie值解码,避免中文乱码问题
+     * @param cookieValue cookie原始值
+     * @return 解码后的值
+     */
+    public String decode(String cookieValue){
+        return URLDecoder.decode(cookieValue, charset);
+    }
+}

+ 25 - 0
xh-common/src/main/java/com/zksy/common/utils/RedisUtils.java

@@ -0,0 +1,25 @@
+package com.zksy.common.utils;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.stereotype.Component;
+
+@Component
+public class RedisUtils {
+    @Autowired
+    private RedisTemplate<String, Object> redisTemplate;
+    public void set(String key, Object value) {
+        redisTemplate.opsForValue().set(key, value);
+    }
+    public Object get(String key) {
+        return redisTemplate.opsForValue().get(key);
+    }
+    public void delete(String key) {
+        redisTemplate.delete(key);
+    }
+    public void delete(String... keys) {
+        for (String key : keys) {
+            delete(key);
+        }
+    }
+}

+ 59 - 0
xh-common/src/main/java/com/zksy/common/utils/SearchUtil.java

@@ -0,0 +1,59 @@
+package com.zksy.common.utils;
+
+
+import cn.hutool.json.JSONUtil;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.core.toolkit.StringUtils;
+import com.zksy.common.domain.dto.ConditionDTO;
+
+import java.net.URLDecoder;
+import java.util.List;
+
+/**
+ * @author zkh
+ * @since 2020-03-16
+ */
+public class SearchUtil {
+    public static QueryWrapper parseWhereSql(String conditionJson) throws Exception{
+        QueryWrapper<?> queryWrapper = new QueryWrapper();
+        String[] empty = {"none"};
+        if(StringUtils.isNotBlank(conditionJson)){
+            conditionJson = URLDecoder.decode(conditionJson, "UTF-8");
+            List<ConditionDTO> conditionList = JSONUtil.toList(conditionJson, ConditionDTO.class);
+            if(conditionList.size()>0){
+                for(ConditionDTO conditionVo : conditionList){
+                    switch (conditionVo.getType()){
+                        case "eq": queryWrapper.eq(conditionVo.getColumn(),conditionVo.getValue());break;
+                        case "ne": queryWrapper.ne(conditionVo.getColumn(),conditionVo.getValue());break;
+                        case "between": queryWrapper.between(conditionVo.getColumn(), conditionVo.getValue() == null ? empty : conditionVo.getValue().split(",")[0],conditionVo.getValue().split(",")[1]);break;
+                        case "like": queryWrapper.like(conditionVo.getColumn(),conditionVo.getValue());break;
+                        case "leftLike": queryWrapper.likeLeft(conditionVo.getColumn(),conditionVo.getValue());break;
+                        case "rightLike": queryWrapper.likeRight(conditionVo.getColumn(),conditionVo.getValue());break;
+                        case "notLike": queryWrapper.notLike(conditionVo.getColumn(),conditionVo.getValue());break;
+                        case "exists": queryWrapper.exists(conditionVo.getValue());break;
+                        case "notExists": queryWrapper.notExists(conditionVo.getValue());break;
+                        case "gt": queryWrapper.gt(conditionVo.getColumn(),conditionVo.getValue());break;
+                        case "lt": queryWrapper.lt(conditionVo.getColumn(),conditionVo.getValue());break;
+                        case "ge": queryWrapper.ge(conditionVo.getColumn(),conditionVo.getValue());break;
+                        case "le": queryWrapper.le(conditionVo.getColumn(),conditionVo.getValue());break;
+                        case "or": queryWrapper.or();break;
+                        case "orLike2":
+                            String[] columns = conditionVo.getColumn().split(",");
+                            queryWrapper.and(wrapper ->
+                                    wrapper.and(swrapper -> swrapper.like(columns[0], conditionVo.getValue()).or().like(columns[1], conditionVo.getValue())));
+                            break;
+                        case "in": queryWrapper.in(conditionVo.getColumn(), conditionVo.getValue() == null ? empty : conditionVo.getValue().split(","));break;
+                        case "notIn": queryWrapper.notIn(conditionVo.getColumn(), conditionVo.getValue() == null ? empty : conditionVo.getValue().split(","));break;
+                        case "isNull": queryWrapper.isNull(conditionVo.getColumn());break;
+                        case "isNotNull": queryWrapper.isNotNull(conditionVo.getColumn());break;
+                        case "orderByAsc": queryWrapper.orderByAsc(conditionVo.getColumn());break;
+                        case "orderByDesc": queryWrapper.orderByDesc(conditionVo.getColumn());break;
+                        default:
+                    }
+                }
+            }
+        }
+        return queryWrapper;
+    }
+
+}

+ 28 - 0
xh-common/src/main/java/com/zksy/common/utils/UserContext.java

@@ -0,0 +1,28 @@
+package com.zksy.common.utils;
+
+public class UserContext {
+    private static final ThreadLocal<Long> tl = new ThreadLocal<>();
+
+    /**
+     * 保存当前登录用户信息到ThreadLocal
+     * @param userId 用户id
+     */
+    public static void setUser(Long userId) {
+        tl.set(userId);
+    }
+
+    /**
+     * 获取当前登录用户信息
+     * @return 用户id
+     */
+    public static Long getUser() {
+        return tl.get();
+    }
+
+    /**
+     * 移除当前登录用户信息
+     */
+    public static void removeUser(){
+        tl.remove();
+    }
+}

+ 151 - 0
xh-common/src/main/java/com/zksy/common/utils/WebUtils.java

@@ -0,0 +1,151 @@
+package com.zksy.common.utils;
+
+
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.util.StrUtil;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.util.StringUtils;
+import org.springframework.web.context.request.RequestAttributes;
+import org.springframework.web.context.request.RequestContextHolder;
+import org.springframework.web.context.request.ServletRequestAttributes;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.util.Collection;
+import java.util.Map;
+
+@Slf4j
+public class WebUtils {
+
+    /**
+     * 获取ServletRequestAttributes
+     *
+     * @return ServletRequestAttributes
+     */
+    public static ServletRequestAttributes getServletRequestAttributes() {
+        RequestAttributes ra = RequestContextHolder.getRequestAttributes();
+        if (ra == null) {
+            return null;
+        }
+        return (ServletRequestAttributes) ra;
+    }
+
+    /**
+     * 获取request
+     *
+     * @return HttpServletRequest
+     */
+    public static HttpServletRequest getRequest() {
+        ServletRequestAttributes servletRequestAttributes = getServletRequestAttributes();
+        return servletRequestAttributes == null ? null : servletRequestAttributes.getRequest();
+    }
+
+    /**
+     * 获取response
+     *
+     * @return HttpServletResponse
+     */
+    public static HttpServletResponse getResponse() {
+        ServletRequestAttributes servletRequestAttributes = getServletRequestAttributes();
+        return servletRequestAttributes == null ? null : servletRequestAttributes.getResponse();
+    }
+
+    /**
+     * 获取request header中的内容
+     *
+     * @param headerName 请求头名称
+     * @return 请求头的值
+     */
+    public static String getHeader(String headerName) {
+        HttpServletRequest request = getRequest();
+        if (request == null) {
+            return null;
+        }
+        return getRequest().getHeader(headerName);
+    }
+
+    public static void setResponseHeader(String key, String value){
+        HttpServletResponse response = getResponse();
+        if (response == null) {
+            return;
+        }
+        response.setHeader(key, value);
+    }
+
+    public static boolean isSuccess() {
+        HttpServletResponse response = getResponse();
+        return response != null && response.getStatus() < 300;
+    }
+
+    /**
+     * 获取请求地址中的请求参数组装成 key1=value1&key2=value2
+     * 如果key对应多个值,中间使用逗号隔开例如 key1对应value1,key2对应value2,value3, key1=value1&key2=value2,value3
+     *
+     * @param request
+     * @return 返回拼接字符串
+     */
+    public static String getParameters(HttpServletRequest request) {
+        Map<String, String[]> parameterMap = request.getParameterMap();
+        return getParameters(parameterMap);
+    }
+
+    /**
+     * 获取请求地址中的请求参数组装成 key1=value1&key2=value2
+     * 如果key对应多个值,中间使用逗号隔开例如 key1对应value1,key2对应value2,value3, key1=value1&key2=value2,value3
+     *
+     * @param queries
+     * @return
+     */
+    public  static <T> String getParameters(final Map<String, T> queries) {
+        StringBuilder buffer = new StringBuilder();
+        for (Map.Entry<String, T> entry : queries.entrySet()) {
+            if(entry.getValue() instanceof String[]){
+                buffer.append(entry.getKey()).append(String.join(",", ((String[])entry.getValue())))
+                    .append("&");
+            }else if(entry.getValue() instanceof Collection){
+                buffer.append(entry.getKey()).append(
+                        CollUtil.join(((Collection<String>)entry.getValue()),",")
+                ).append("&");
+            }
+        }
+        return buffer.length() > 0 ? buffer.substring(0, buffer.length() - 1) : StrUtil.EMPTY;
+    }
+
+    /**
+     * 获取请求url中的uri
+     *
+     * @param url
+     * @return
+     */
+    public static String getUri(String url){
+        if(StringUtils.isEmpty(url)) {
+            return null;
+        }
+
+        String uri = url;
+        //uri中去掉 http:// 或者https
+        if(uri.contains("http://") ){
+            uri = uri.replace("http://", StrUtil.EMPTY);
+        }else if(uri.contains("https://")){
+            uri = uri.replace("https://", StrUtil.EMPTY);
+        }
+
+        int endIndex = uri.length(); //uri 在url中的最后一个字符的序号+1
+        if(uri.contains("?")){
+            endIndex = uri.indexOf("?");
+        }
+        return uri.substring(uri.indexOf("/"), endIndex);
+    }
+
+    public static String getRemoteAddr() {
+        HttpServletRequest request = getRequest();
+        if (request == null) {
+            return "";
+        }
+        return request.getRemoteAddr();
+    }
+
+    public static CookieBuilder cookieBuilder(){
+        return new CookieBuilder(getRequest(), getResponse());
+    }
+}

+ 171 - 0
xh-common/src/main/resources/META-INF/spring-configuration-metadata.json

@@ -0,0 +1,171 @@
+{
+  "groups": [
+    {
+      "name": "hm.db"
+    },
+    {
+      "name": "hm.mq"
+    },
+    {
+      "name": "hm.swagger"
+    },
+    {
+      "name": "hm.jwt",
+      "type": "com.zksy.config.SecurityConfig",
+      "sourceType": "com.zksy.config.JwtProperties"
+    },
+    {
+      "name": "hm.auth",
+      "type": "com.zksy.config.MvcConfig",
+      "sourceType": "com.zksy.config.AuthProperties"
+    }
+  ],
+  "properties": [
+    {
+      "name": "hm.mq.host",
+      "type": "java.lang.String",
+      "description": "rabbitmq的地址",
+      "defaultValue": "192.168.150.101"
+    },
+    {
+      "name": "hm.mq.port",
+      "type": "java.lang.Integer",
+      "description": "rabbitmq的端口",
+      "defaultValue": "5672"
+    },
+    {
+      "name": "hm.mq.vhost",
+      "type": "java.lang.String",
+      "description": "rabbitmq的virtual-host地址",
+      "defaultValue": "/hmxt"
+    },
+    {
+      "name": "hm.mq.username",
+      "type": "java.lang.String",
+      "description": "rabbitmq的用户名",
+      "defaultValue": "hmxt"
+    },
+    {
+      "name": "hm.mq.password",
+      "type": "java.lang.String",
+      "description": "rabbitmq的密码",
+      "defaultValue": "123321"
+    },
+    {
+      "name": "hm.mq.listener.retry.enable",
+      "type": "java.lang.Boolean",
+      "description": "是否开启rabbitmq的消费者重试机制",
+      "defaultValue": "true"
+    },
+    {
+      "name": "hm.mq.listener.retry.interval",
+      "type": "java.time.Duration",
+      "description": "消费者重试初始失败等待时长",
+      "defaultValue": "1000ms"
+    },
+    {
+      "name": "hm.mq.listener.retry.multiplier",
+      "type": "java.lang.Integer",
+      "description": "失败等待时长的递增倍数",
+      "defaultValue": "1"
+    },
+    {
+      "name": "hm.mq.listener.retry.max-attempts",
+      "type": "java.lang.Integer",
+      "description": "消费者重试最大重试次数",
+      "defaultValue": "3"
+    },
+    {
+      "name": "hm.mq.listener.retry.stateless",
+      "type": "java.lang.Boolean",
+      "description": "是否是无状态,默认true",
+      "defaultValue": "true"
+    },
+    {
+      "name": "hm.db.host",
+      "type": "java.lang.String",
+      "description": "数据库地址",
+      "defaultValue": "192.168.150.101"
+    },
+    {
+      "name": "hm.db.port",
+      "type": "java.lang.Integer",
+      "description": "数据库端口",
+      "defaultValue": "3306"
+    },
+    {
+      "name": "hm.db.database",
+      "type": "java.lang.String",
+      "description": "数据库database名",
+      "defaultValue": ""
+    },
+    {
+      "name": "hm.db.un",
+      "type": "java.lang.String",
+      "description": "数据库用户名",
+      "defaultValue": "root"
+    },
+    {
+      "name": "hm.db.pw",
+      "type": "java.lang.String",
+      "description": "数据库密码",
+      "defaultValue": "123"
+    },
+    {
+      "name": "hm.swagger.title",
+      "type": "java.lang.String",
+      "description": "接口文档标题"
+    },
+    {
+      "name": "hm.swagger.description",
+      "type": "java.lang.String",
+      "description": "接口文档描述"
+    },
+    {
+      "name": "hm.swagger.email",
+      "type": "java.lang.String",
+      "description": "接口文档联系人邮箱"
+    },
+    {
+      "name": "hm.swagger.concat",
+      "type": "java.lang.String",
+      "description": "接口文档联系人"
+    },
+    {
+      "name": "hm.swagger.package",
+      "type": "java.lang.String",
+      "description": "接口controller扫描包"
+    },
+    {
+      "name": "hm.jwt.location",
+      "type": "java.lang.String",
+      "description": "秘钥存储地址"
+    },
+    {
+      "name": "hm.jwt.alias",
+      "type": "java.lang.String",
+      "description": "秘钥别名"
+    },
+    {
+      "name": "hm.jwt.password",
+      "type": "java.lang.String",
+      "description": "秘钥文件密码"
+    },
+    {
+      "name": "hm.jwt.tokenTTL",
+      "type": "java.time.Duration",
+      "description": "登录有效期"
+    },
+    {
+      "name": "hm.auth.excludePaths",
+      "type": "java.util.List",
+      "description": "登录放行的路径"
+    },
+    {
+      "name": "hm.auth.includePaths",
+      "type": "java.util.List",
+      "description": "登录拦截的路径"
+    }
+  ],
+  "hints": []
+}

+ 4 - 0
xh-common/src/main/resources/META-INF/spring.factories

@@ -0,0 +1,4 @@
+org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
+  com.zksy.common.config.MyBatisConfig,\
+  com.zksy.common.config.MvcConfig,\
+  com.zksy.common.config.JsonConfig

+ 65 - 0
xh-gateway/pom.xml

@@ -0,0 +1,65 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <groupId>com.zksy</groupId>
+        <artifactId>xh-server-micro</artifactId>
+        <version>1.0.0</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>xh-gateway</artifactId>
+
+    <properties>
+        <maven.compiler.source>11</maven.compiler.source>
+        <maven.compiler.target>11</maven.compiler.target>
+    </properties>
+
+    <dependencies>
+
+        <!--网关-->
+        <dependency>
+            <groupId>org.springframework.cloud</groupId>
+            <artifactId>spring-cloud-starter-gateway</artifactId>
+        </dependency>
+        <!--nacos discovery-->
+        <dependency>
+            <groupId>com.alibaba.cloud</groupId>
+            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
+        </dependency>
+        <!--负载均衡-->
+        <dependency>
+            <groupId>org.springframework.cloud</groupId>
+            <artifactId>spring-cloud-starter-loadbalancer</artifactId>
+        </dependency>
+        <!--统一配置管理-->
+        <dependency>
+            <groupId>com.alibaba.cloud</groupId>
+            <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
+        </dependency>
+        <!--加载bootstrap-->
+        <dependency>
+            <groupId>org.springframework.cloud</groupId>
+            <artifactId>spring-cloud-starter-bootstrap</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>cn.hutool</groupId>
+            <artifactId>hutool-all</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.zksy</groupId>
+            <artifactId>xh-common</artifactId>
+            <version>1.0.0</version>
+        </dependency>
+    </dependencies>
+    <build>
+        <finalName>${project.artifactId}</finalName>
+        <plugins>
+            <plugin>
+                <groupId>org.springframework.boot</groupId>
+                <artifactId>spring-boot-maven-plugin</artifactId>
+            </plugin>
+        </plugins>
+    </build>
+</project>

+ 12 - 0
xh-gateway/src/main/java/com/zksy/gateway/GatewayApplication.java

@@ -0,0 +1,12 @@
+package com.zksy.gateway;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication
+public class GatewayApplication {
+    public static void main(String[] args) {
+        SpringApplication.run(GatewayApplication.class, args);
+        System.out.println("网关成功启动");
+    }
+}

+ 15 - 0
xh-gateway/src/main/java/com/zksy/gateway/config/AuthProperties.java

@@ -0,0 +1,15 @@
+package com.zksy.gateway.config;
+
+import lombok.Data;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.stereotype.Component;
+
+import java.util.List;
+
+@Data
+@Component
+@ConfigurationProperties(prefix = "hm.auth")
+public class AuthProperties {
+    private List<String> includePaths;
+    private List<String> excludePaths;
+}

+ 16 - 0
xh-gateway/src/main/java/com/zksy/gateway/config/JwtProperties.java

@@ -0,0 +1,16 @@
+package com.zksy.gateway.config;
+
+import lombok.Data;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.core.io.Resource;
+
+import java.time.Duration;
+
+@Data
+@ConfigurationProperties(prefix = "hm.jwt")
+public class JwtProperties {
+    private Resource location;
+    private String password;
+    private String alias;
+    private Duration tokenTTL = Duration.ofMinutes(10);
+}

+ 33 - 0
xh-gateway/src/main/java/com/zksy/gateway/config/SecurityConfig.java

@@ -0,0 +1,33 @@
+package com.zksy.gateway.config;
+
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
+import org.springframework.security.crypto.password.PasswordEncoder;
+import org.springframework.security.rsa.crypto.KeyStoreKeyFactory;
+
+import java.security.KeyPair;
+
+@Configuration
+@EnableConfigurationProperties(JwtProperties.class)
+public class SecurityConfig {
+
+    @Bean
+    public PasswordEncoder passwordEncoder(){
+        return new BCryptPasswordEncoder();
+    }
+
+    @Bean
+    public KeyPair keyPair(JwtProperties properties){
+        // 获取秘钥工厂
+        KeyStoreKeyFactory keyStoreKeyFactory =
+                new KeyStoreKeyFactory(
+                        properties.getLocation(),
+                        properties.getPassword().toCharArray());
+        //读取钥匙对
+        return keyStoreKeyFactory.getKeyPair(
+                properties.getAlias(),
+                properties.getPassword().toCharArray());
+    }
+}

+ 66 - 0
xh-gateway/src/main/java/com/zksy/gateway/filter/AuthGlobalFilter.java

@@ -0,0 +1,66 @@
+package com.zksy.gateway.filter;
+
+import com.zksy.gateway.config.AuthProperties;
+import com.zksy.gateway.utils.JwtTool;
+import lombok.RequiredArgsConstructor;
+import org.springframework.cloud.gateway.filter.GatewayFilterChain;
+import org.springframework.cloud.gateway.filter.GlobalFilter;
+import org.springframework.core.Ordered;
+import org.springframework.stereotype.Component;
+import org.springframework.util.AntPathMatcher;
+import org.springframework.web.server.ServerWebExchange;
+import reactor.core.publisher.Mono;
+
+@Component
+@RequiredArgsConstructor
+public class AuthGlobalFilter implements GlobalFilter, Ordered {
+
+    private final AuthProperties authProperties;
+    private final JwtTool jwtTool;
+    private final AntPathMatcher antPathMatcher = new AntPathMatcher();
+    @Override
+    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
+        System.out.println("AuthGlobalFilter");
+        return chain.filter(exchange);
+//        //获取请求
+//        ServerHttpRequest request = exchange.getRequest();
+//        //判断是否需要拦截
+//        if(isExclude(request.getPath().toString())){
+//            return chain.filter(exchange);
+//        }
+//        //拿到token
+//        String token = null;
+//        String header = request.getHeaders().getFirst("authorization");
+//        if(header != null && !header.isEmpty()){
+//            token = header;
+//        }
+//        Long userId;
+//        //校验token
+//        try {
+//            userId = jwtTool.parseToken(token);
+//        } catch (UnauthorizedException e) {
+//            ServerHttpResponse response = exchange.getResponse();
+//            response.setStatusCode(HttpStatus.UNAUTHORIZED);
+//            return response.setComplete();
+//        }
+//        //保存登录信息到下一个请求
+//        ServerWebExchange swe = exchange.mutate()
+//                .request(r -> r.header("user-info", userId.toString()))
+//                .build();
+//        return chain.filter(swe);
+    }
+
+    private boolean isExclude(String path) {
+        for (String excludePath : authProperties.getExcludePaths()) {
+            if(antPathMatcher.match(excludePath,path)){
+                return true;
+            }
+        }
+        return false;
+    }
+
+    @Override
+    public int getOrder() {
+        return 0;
+    }
+}

+ 74 - 0
xh-gateway/src/main/java/com/zksy/gateway/route/DynamicRouteLoader.java

@@ -0,0 +1,74 @@
+package com.zksy.gateway.route;
+
+import cn.hutool.json.JSONUtil;
+import com.alibaba.cloud.nacos.NacosConfigManager;
+import com.alibaba.nacos.api.config.listener.Listener;
+import com.alibaba.nacos.api.exception.NacosException;
+import com.zksy.common.utils.CollUtils;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.cloud.gateway.route.RouteDefinition;
+import org.springframework.cloud.gateway.route.RouteDefinitionWriter;
+import org.springframework.stereotype.Component;
+import reactor.core.publisher.Mono;
+
+import javax.annotation.PostConstruct;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.Executor;
+
+@Slf4j
+@Component
+@RequiredArgsConstructor
+public class DynamicRouteLoader {
+
+    private final NacosConfigManager nacosConfigManager;
+    private final String DATA_ID = "xh-route.json";
+    private final String GROUP = "DEFAULT_GROUP";
+    // 保存更新过的路由id
+    private final Set<String> routeIds = new HashSet<>();
+    private final RouteDefinitionWriter writer;
+    @PostConstruct//项目启动时直接拉取一次路由表
+    public void initRouteConfigListener() throws NacosException {
+        String configInfo = nacosConfigManager.getConfigService().getConfigAndSignListener(DATA_ID, GROUP, 5000, new Listener() {
+            @Override
+            public Executor getExecutor() {
+                return null;
+            }
+
+            @Override
+            public void receiveConfigInfo(String s) {
+                //监听到配置变更触发的回调函数
+                updateConfigInfo(s);
+            }
+        });
+        //第一次拉取路由信息配置
+        updateConfigInfo(configInfo);
+    }
+
+    private void updateConfigInfo(String configInfo) {
+        log.debug("监听到路由配置变更,{}", configInfo);
+        // 1.反序列化
+        List<RouteDefinition> routeDefinitions = JSONUtil.toList(configInfo, RouteDefinition.class);
+        // 2.更新前先清空旧路由
+        // 2.1.清除旧路由
+        for (String routeId : routeIds) {
+            writer.delete(Mono.just(routeId)).subscribe();
+        }
+        routeIds.clear();
+        // 2.2.判断是否有新的路由要更新
+        if (CollUtils.isEmpty(routeDefinitions)) {
+            // 无新路由配置,直接结束
+            return;
+        }
+        // 3.更新路由
+        routeDefinitions.forEach(routeDefinition -> {
+            // 3.1.更新路由
+            writer.save(Mono.just(routeDefinition)).subscribe();
+            // 3.2.记录路由id,方便将来删除
+            routeIds.add(routeDefinition.getId());
+        });
+    }
+}
+

+ 82 - 0
xh-gateway/src/main/java/com/zksy/gateway/utils/JwtTool.java

@@ -0,0 +1,82 @@
+package com.zksy.gateway.utils;
+
+import cn.hutool.core.exceptions.ValidateException;
+import cn.hutool.jwt.JWT;
+import cn.hutool.jwt.JWTValidator;
+import cn.hutool.jwt.signers.JWTSigner;
+import cn.hutool.jwt.signers.JWTSignerUtil;
+import com.zksy.common.exception.UnauthorizedException;
+import org.springframework.stereotype.Component;
+
+import java.security.KeyPair;
+import java.time.Duration;
+import java.util.Date;
+
+@Component
+public class JwtTool {
+    private final JWTSigner jwtSigner;
+
+    public JwtTool(KeyPair keyPair) {
+        this.jwtSigner = JWTSignerUtil.createSigner("rs256", keyPair);
+    }
+
+    /**
+     * 创建 access-token
+     *
+     * @param userId 用户信息
+     * @return access-token
+     */
+    public String createToken(Long userId, Duration ttl) {
+        // 1.生成jws
+        return JWT.create()
+                .setPayload("user", userId)
+                .setExpiresAt(new Date(System.currentTimeMillis() + ttl.toMillis()))
+                .setSigner(jwtSigner)
+                .sign();
+    }
+
+    /**
+     * 解析token
+     *
+     * @param token token
+     * @return 解析刷新token得到的用户信息
+     */
+    public Long parseToken(String token) {
+        // 1.校验token是否为空
+        if (token == null) {
+            throw new UnauthorizedException("未登录");
+        }
+        // 2.校验并解析jwt
+        JWT jwt;
+        try {
+            jwt = JWT.of(token).setSigner(jwtSigner);
+        } catch (Exception e) {
+            throw new UnauthorizedException("无效的token", e);
+        }
+        // 2.校验jwt是否有效
+        if (!jwt.verify()) {
+            // 验证失败
+            throw new UnauthorizedException("无效的token");
+        }
+        // 3.校验是否过期
+        try {
+            JWTValidator.of(jwt).validateDate();
+        } catch (ValidateException e) {
+            throw new UnauthorizedException("token已经过期");
+        }
+        // 4.数据格式校验
+        Object userPayload = jwt.getPayload("user");
+        if (userPayload == null) {
+            // 数据为空
+            throw new UnauthorizedException("无效的token");
+        }
+
+        // 5.数据解析
+        try {
+           return Long.valueOf(userPayload.toString());
+        } catch (RuntimeException e) {
+            // 数据格式有误
+            throw new UnauthorizedException("无效的token");
+        }
+    }
+}

+ 13 - 0
xh-gateway/src/main/resources/application.yaml

@@ -0,0 +1,13 @@
+server:
+  port: 8900 # 端口
+hm:
+  jwt:
+    location: classpath:hmall.jks # 秘钥地址
+    alias: hmall # 秘钥别名
+    password: hmall123 # 秘钥文件密码
+    tokenTTL: 30m # 登录有效期
+  auth:
+    excludePaths: # 无需登录校验的路径
+      - /search/**
+      - /users/login
+      - /items/**

+ 14 - 0
xh-gateway/src/main/resources/bootstrap.yaml

@@ -0,0 +1,14 @@
+spring:
+  application:
+    name: xh-gateway
+
+  cloud:
+    nacos:
+      discovery:
+        namespace: 5d4109ea-3d63-47d2-ae99-2cc52b06eb1f
+      server-addr: 192.168.110.30
+      config:
+        file-extension: yaml
+        shared-configs:
+          - dataId: xh-log.yaml # 共享日志配置
+        namespace: 5d4109ea-3d63-47d2-ae99-2cc52b06eb1f

二進制
xh-gateway/src/main/resources/hmall.jks