linxk il y a 1 mois
commit
2f223dcc01
47 fichiers modifiés avec 2510 ajouts et 0 suppressions
  1. 23 0
      .gitignore
  2. 45 0
      README.md
  3. 1 0
      deploy.bat
  4. 1 0
      eclipse.bat
  5. 1 0
      install.bat
  6. 1 0
      package.bat
  7. 339 0
      pom.xml
  8. 19 0
      src/main/java/com/medipath/project/FeignConfiguration.java
  9. 96 0
      src/main/java/com/medipath/project/GatewayApplication.java
  10. 50 0
      src/main/java/com/medipath/project/Index.java
  11. 67 0
      src/main/java/com/medipath/project/configurer/ApiFallbackProvider.java
  12. 26 0
      src/main/java/com/medipath/project/configurer/CorsConfig.java
  13. 150 0
      src/main/java/com/medipath/project/configurer/MyWebMvcConfigurer.java
  14. 36 0
      src/main/java/com/medipath/project/configurer/Parameters.java
  15. 62 0
      src/main/java/com/medipath/project/configurer/SwaggerConfig.java
  16. 81 0
      src/main/java/com/medipath/project/core/AbstractService.java
  17. 36 0
      src/main/java/com/medipath/project/core/BaseBeen.java
  18. 15 0
      src/main/java/com/medipath/project/core/BaseUser.java
  19. 17 0
      src/main/java/com/medipath/project/core/Mapper.java
  20. 18 0
      src/main/java/com/medipath/project/core/ProjectConstant.java
  21. 50 0
      src/main/java/com/medipath/project/core/Result.java
  22. 35 0
      src/main/java/com/medipath/project/core/ResultGenerator.java
  23. 22 0
      src/main/java/com/medipath/project/core/Service.java
  24. 17 0
      src/main/java/com/medipath/project/core/ServiceException.java
  25. 33 0
      src/main/java/com/medipath/project/core/cache/CacheKey.java
  26. 35 0
      src/main/java/com/medipath/project/core/cache/CacheType.java
  27. 13 0
      src/main/java/com/medipath/project/core/interceptor/LoginType.java
  28. 16 0
      src/main/java/com/medipath/project/core/interceptor/NoToken.java
  29. 24 0
      src/main/java/com/medipath/project/core/interceptor/ThreadUser.java
  30. 24 0
      src/main/java/com/medipath/project/dubbo/client/OperatingInterfacesDubboServiceClient.java
  31. 26 0
      src/main/java/com/medipath/project/dubbo/client/UserDubboServiceClient.java
  32. 51 0
      src/main/java/com/medipath/project/enums/ResultCode.java
  33. 399 0
      src/main/java/com/medipath/project/filter/AccessFilter.java
  34. 44 0
      src/main/java/com/medipath/project/filter/ErrorFilter.java
  35. 52 0
      src/main/java/com/medipath/project/filter/HeaderMapRequestWrapper.java
  36. 101 0
      src/main/java/com/medipath/project/filter/PostFilter.java
  37. 47 0
      src/main/java/com/medipath/project/filter/ThrowExceptionFilter.java
  38. 27 0
      src/main/java/com/medipath/project/task/SchedulingConfigTask.java
  39. 79 0
      src/main/java/com/medipath/project/util/MD5Util.java
  40. 39 0
      src/main/java/com/medipath/project/util/NoticeUtil.java
  41. 21 0
      src/main/java/com/medipath/project/util/StringUtil.java
  42. 83 0
      src/main/resources/bootstrap.properties
  43. 81 0
      src/main/resources/logback.xml
  44. 27 0
      src/main/resources/mapper/InterfaceMapper.xml
  45. 11 0
      src/main/resources/templates/index.html
  46. 46 0
      src/test/java/com/conpany/project/TestRedis.java
  47. 23 0
      src/test/java/com/conpany/project/Tester.java

+ 23 - 0
.gitignore

@@ -0,0 +1,23 @@
+target/
+
+### STS ###
+.apt_generated
+.classpath
+.factorypath
+.project
+.settings
+.springBeans
+
+### IntelliJ IDEA ###
+.idea
+*.iws
+*.iml
+*.ipr
+
+### NetBeans ###
+nbproject/private/
+build/
+nbbuild/
+dist/
+nbdist/
+.nb-gradle/

+ 45 - 0
README.md

@@ -0,0 +1,45 @@
+![Licence](https://img.shields.io/badge/licence-none-green.svg)
+[![GitHub Release](https://img.shields.io/github/release/lihengming/spring-boot-api-project-seed.svg)](https://github.com/lihengming/spring-boot-api-project-seed/releases)
+## 简介
+Spring Boot API Project Seed 是一个基于Spring Boot & MyBatis的种子项目,用于快速构建中小型API、RESTful API项目,该种子项目已经有过多个真实项目的实践,稳定、简单、快速,使我们摆脱那些重复劳动,专注于业务代码的编写,减少加班。下面是一个简单的使用演示,看如何基于本项目在短短几十秒钟内实现一套简单的API,并运行提供服务。
+
+[![请选择超清](https://raw.githubusercontent.com/lihengming/java-codes/master/shared-resources/github-images/project-example-youku.png)](http://v.youku.com/v_show/id_XMjg1NjYwNDgxNg==.html?spm=a2h3j.8428770.3416059.1)
+## 特征&提供
+- 最佳实践的项目结构、配置文件、精简的POM([查看项目结构图](https://github.com/lihengming/java-codes/blob/master/shared-resources/github-images/project-struct.png))
+- 统一响应结果封装及生成工具
+- 统一异常处理
+- 简单的接口签名认证
+- 常用基础方法抽象封装
+- 使用Druid Spring Boot Starter 集成Druid数据库连接池与监控
+- 使用FastJsonHttpMessageConverter,提高JSON序列化速度
+- 集成MyBatis、通用Mapper插件、PageHelper分页插件,实现单表业务零SQL
+- 提供代码生成器根据表名生成对应的Model、Mapper、MapperXML、Service、ServiceImpl、Controller等基础代码,其中Controller模板默认提供POST和RESTful两套,根据需求在```CodeGenerator.genController(tableName)```方法中自己选择,默认使用POST模板。代码模板可根据实际项目的需求来扩展,由于每个公司业务都不太一样,所以只提供了一些比较基础、通用的模板,主要是提供一个思路来减少重复代码的编写,我在实际项目的使用中,其实根据公司业务的抽象编写了大量的模板。另外,使用模板也有助于保持团队代码风格的统一
+- 另有彩蛋,待你探索
+## 快速开始
+1. 克隆项目
+2. 对```test```包内的代码生成器```CodeGenerator```进行配置,主要是JDBC,因为要根据表名来生成代码
+3. 如果只是想根据上面的演示来亲自试试的话可以使用```test resources```目录下的```demo-user.sql```,否则忽略该步
+3. 输入表名,运行```CodeGenerator.main()```方法,生成基础代码(可能需要刷新项目目录才会出来)
+4. 根据业务在基础代码上进行扩展
+5. 对开发环境配置文件```application-dev.properties```进行配置,启动项目,Have Fun!
+## 开发建议
+- 表名,建议使用小写,多个单词使用下划线拼接
+- Model内成员变量建议与表字段数量对应,如需扩展成员变量(比如连表查询)建议创建DTO,否则需在扩展的成员变量上加```@Transient```注解,详情见[通用Mapper插件文档说明](https://mapperhelper.github.io/docs/2.use/)
+- 建议业务失败直接使用```ServiceException("message")```抛出,由统一异常处理器来封装业务失败的响应结果,比如```throw new ServiceException("该手机号已被注册")```,会直接被封装为```{"code":400,"message":"该手机号已被注册"}```返回,无需自己处理,尽情抛出
+- 需要工具类的话建议先从```apache-commons-*```和```guava```中找,实在没有再造轮子或引入类库,尽量精简项目
+- 开发规范建议遵循阿里巴巴Java开发手册([最新版下载](https://github.com/lihengming/java-codes/blob/master/shared-resources/%E9%98%BF%E9%87%8C%E5%B7%B4%E5%B7%B4Java%E5%BC%80%E5%8F%91%E6%89%8B%E5%86%8CV1.2.0.pdf))
+- 建议在公司内部使用[ShowDoc](https://github.com/star7th/showdoc)、[SpringFox-Swagger2](https://github.com/springfox/springfox) 、[RAP](https://github.com/thx/RAP)等开源项目来编写、管理API文档
+## 技术选型&文档
+- Spring Boot([查看Spring Boot学习&使用指南](http://www.jianshu.com/p/1a9fd8936bd8))
+- MyBatis([查看官方中文文档](http://www.mybatis.org/mybatis-3/zh/index.html))
+- MyBatisb通用Mapper插件([查看官方中文文档](https://mapperhelper.github.io/docs/))
+- MyBatis PageHelper分页插件([查看官方中文文档](https://pagehelper.github.io/))
+- Druid Spring Boot Starter([查看官方中文文档](https://github.com/alibaba/druid/tree/master/druid-spring-boot-starter/))
+- Fastjson([查看官方中文文档](https://github.com/Alibaba/fastjson/wiki/%E9%A6%96%E9%A1%B5))
+- 其他略
+
+## License
+无,纯粹开源分享,感谢大家 [Star](https://github.com/lihengming/spring-boot-api-project-seed/stargazers) & [Fork](https://github.com/lihengming/spring-boot-api-project-seed/network/members) 的支持。

+ 1 - 0
deploy.bat

@@ -0,0 +1 @@
+cmd /k mvn deploy -DskipTests

+ 1 - 0
eclipse.bat

@@ -0,0 +1 @@
+cmd /k mvn clean eclipse:clean eclipse:eclipse -DdownloadSources=true -DdownloadJavadocs=true

+ 1 - 0
install.bat

@@ -0,0 +1 @@
+cmd /k mvn install -Dmaven.test.skip=true

+ 1 - 0
package.bat

@@ -0,0 +1 @@
+cmd /k mvn clean package

+ 339 - 0
pom.xml

@@ -0,0 +1,339 @@
+<?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.medipath</groupId>
+    <artifactId>gateway</artifactId>
+    <version>1.0-SNAPSHOT</version>
+    <packaging>jar</packaging>
+
+    <properties>
+        <java.version>1.8</java.version>
+    </properties>
+
+    <!-- Inherit defaults from Spring Boot -->
+    <parent>
+        <groupId>org.springframework.boot</groupId>
+        <artifactId>spring-boot-starter-parent</artifactId>
+        <version>2.0.5.RELEASE</version>
+    </parent>
+
+    <dependencies>
+        <!--Spring Boot依赖-->
+
+        <dependency>
+            <groupId>com.medipath</groupId>
+            <artifactId>operating-api</artifactId>
+            <version>1.0.0-SNAPSHOT</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-actuator</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.medipath</groupId>
+            <artifactId>cif-api</artifactId>
+            <version>1.0.0-SNAPSHOT</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-web</artifactId>
+            <!-- 移除tomcat容器-->
+            <exclusions>
+                <exclusion>
+                    <groupId>org.springframework.boot</groupId>
+                    <artifactId>spring-boot-starter-tomcat</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+        <!-- 替换undertow容器 -->
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-undertow</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-webflux</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-thymeleaf</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>cn.hutool</groupId>
+            <artifactId>hutool-all</artifactId>
+            <version>RELEASE</version>
+            <scope>compile</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.retry</groupId>
+            <artifactId>spring-retry</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-aop</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springframework.cloud</groupId>
+            <artifactId>spring-cloud-starter-openfeign</artifactId>
+            <!--            <version>1.4.3.RELEASE</version>-->
+        </dependency>
+
+        <dependency>
+            <groupId>org.springframework.cloud</groupId>
+            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springframework.cloud</groupId>
+            <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
+        </dependency>
+
+        <!--redis依赖-->
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-redis</artifactId>
+            <version>1.3.8.RELEASE</version>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-jdbc</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-test</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springframework.cloud</groupId>
+            <artifactId>spring-cloud-starter-zipkin</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springframework.cloud</groupId>
+            <artifactId>spring-cloud-starter-netflix-zuul</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>com.netflix.zuul</groupId>
+            <artifactId>zuul-core</artifactId>
+        </dependency>
+
+        <!--常用库依赖-->
+        <dependency>
+            <groupId>commons-codec</groupId>
+            <artifactId>commons-codec</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.commons</groupId>
+            <artifactId>commons-lang3</artifactId>
+            <version>3.5</version>
+        </dependency>
+<!--        <dependency>-->
+<!--            <groupId>com.google.guava</groupId>-->
+<!--            <artifactId>guava</artifactId>-->
+<!--            <version>18.0</version>-->
+<!--        </dependency>-->
+        <dependency>
+		    <groupId>javax.servlet</groupId>
+		    <artifactId>javax.servlet-api</artifactId>
+		    <version>3.1.0</version>
+		    <scope>provided</scope>
+		</dependency>
+        <!--MySQL JDBC驱动-->
+        <dependency>
+            <groupId>mysql</groupId>
+            <artifactId>mysql-connector-java</artifactId>
+            <scope>runtime</scope>
+        </dependency>
+        <!--MyBatis 及 插件依赖-->
+        <dependency>
+            <groupId>org.mybatis</groupId>
+            <artifactId>mybatis-spring</artifactId>
+<!--            <version>1.3.1</version>-->
+            <version>2.0.1</version>
+        </dependency>
+        <dependency>
+            <groupId>org.mybatis</groupId>
+            <artifactId>mybatis</artifactId>
+            <version>3.4.4</version>
+        </dependency>
+        <dependency>
+            <groupId>tk.mybatis</groupId>
+            <artifactId>mapper</artifactId>
+            <version>3.4.0</version>
+        </dependency>
+        <dependency>
+            <groupId>com.github.pagehelper</groupId>
+            <artifactId>pagehelper-spring-boot-starter</artifactId>
+            <version>1.2.3</version>
+        </dependency>
+        <!--阿里 FastJson依赖-->
+        <dependency>
+            <groupId>com.alibaba</groupId>
+            <artifactId>fastjson</artifactId>
+<!--            <version>1.2.22</version>-->
+            <version>1.2.58</version>
+        </dependency>
+
+        <!--代码生成器依赖-->
+        <dependency>
+            <groupId>org.freemarker</groupId>
+            <artifactId>freemarker</artifactId>
+            <version>2.3.23</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.mybatis.generator</groupId>
+            <artifactId>mybatis-generator-core</artifactId>
+            <version>1.3.5</version>
+            <scope>test</scope>
+        </dependency>
+
+
+        <dependency>
+            <groupId>org.apache.dubbo</groupId>
+            <artifactId>dubbo-spring-boot-starter</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>com.esotericsoftware</groupId>
+            <artifactId>kryo</artifactId>
+            <version>4.0.2</version>
+        </dependency>
+        <dependency>
+            <groupId>de.javakaffee</groupId>
+            <artifactId>kryo-serializers</artifactId>
+            <version>0.45</version>
+        </dependency>
+
+        <!-- Dubbo -->
+        <dependency>
+            <groupId>org.apache.dubbo</groupId>
+            <artifactId>dubbo</artifactId>
+        </dependency>
+
+        <!-- Dubbo Registry Nacos -->
+        <dependency>
+            <groupId>org.apache.dubbo</groupId>
+            <artifactId>dubbo-registry-nacos</artifactId>
+            <version>2.7.1</version>
+        </dependency>
+
+        <dependency>
+            <groupId>com.alibaba.nacos</groupId>
+            <artifactId>nacos-client</artifactId>
+            <exclusions>
+                <exclusion>
+                    <groupId>com.google.guava</groupId>
+                    <artifactId>guava</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+
+        <!-- swagger2 -->
+        <dependency>
+            <groupId>io.springfox</groupId>
+            <artifactId>springfox-swagger2</artifactId>
+            <version>2.9.2</version>
+        </dependency>
+        <dependency>
+            <groupId>io.springfox</groupId>
+            <artifactId>springfox-swagger-ui</artifactId>
+            <version>2.9.2</version>
+        </dependency>
+        <dependency>
+            <groupId>com.github.xiaoymin</groupId>
+            <artifactId>swagger-bootstrap-ui</artifactId>
+            <version>1.8.5</version>
+        </dependency>
+
+        <!-- swagger2 皮肤 -->
+        <dependency>
+            <groupId>com.github.caspar-chen</groupId>
+            <artifactId>swagger-ui-layer</artifactId>
+            <version>1.1.2</version>
+        </dependency>
+
+
+        <dependency>
+            <groupId>com.spring4all</groupId>
+            <artifactId>swagger-spring-boot-starter</artifactId>
+            <version>1.8.0.RELEASE</version>
+            <scope>compile</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>io.jsonwebtoken</groupId>
+            <artifactId>jjwt</artifactId>
+            <version>0.9.1</version>
+        </dependency>
+        <dependency>
+            <groupId>org.projectlombok</groupId>
+            <artifactId>lombok</artifactId>
+        </dependency>
+
+    </dependencies>
+
+    <dependencyManagement>
+        <dependencies>
+            <dependency>
+                <groupId>org.springframework.cloud</groupId>
+                <artifactId>spring-cloud-dependencies</artifactId>
+                <version>Finchley.SR1</version>
+                <type>pom</type>
+                <scope>import</scope>
+            </dependency>
+            <dependency>
+                <groupId>org.springframework.cloud</groupId>
+                <artifactId>spring-cloud-alibaba-dependencies</artifactId>
+                <version>0.2.2.RELEASE</version>
+                <type>pom</type>
+                <scope>import</scope>
+            </dependency>
+        </dependencies>
+    </dependencyManagement>
+    <build>
+       <plugins>
+            <plugin>
+                <groupId>org.springframework.boot</groupId>
+                <artifactId>spring-boot-maven-plugin</artifactId>
+                <configuration>
+                	<finalName>gateway</finalName>
+                </configuration>
+            </plugin>
+            <plugin>
+                <artifactId>maven-compiler-plugin</artifactId>
+                <configuration>
+                    <source>${java.version}</source>
+                    <target>${java.version}</target>
+                    <encoding>UTF-8</encoding>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+    <distributionManagement>
+        <snapshotRepository>
+            <uniqueVersion>false</uniqueVersion>
+            <id>nexus-snapshots</id>
+            <name>Nexus Snapshot Repository</name>
+            <url>https://maven.medipath.com.cn/repository/maven-snapshots/</url>
+            <layout>default</layout>
+        </snapshotRepository>
+
+        <repository>
+            <id>nexus-releases</id>
+            <name>maven-releases</name>
+            <url>https://maven.medipath.com.cn/repository/maven-releases/</url>
+        </repository>
+    </distributionManagement>
+
+</project>

+ 19 - 0
src/main/java/com/medipath/project/FeignConfiguration.java

@@ -0,0 +1,19 @@
+package com.medipath.project;
+import feign.Request;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+@Configuration
+public class FeignConfiguration {
+    @Value("${feign.connectTimeout:60000}")
+    private int connectTimeout;
+
+    @Value("${feign.readTimeOut:60000}")
+    private int readTimeout;
+
+    @Bean
+    public Request.Options options() {
+        return new Request.Options(connectTimeout, readTimeout);
+    }
+}

+ 96 - 0
src/main/java/com/medipath/project/GatewayApplication.java

@@ -0,0 +1,96 @@
+package com.medipath.project;
+
+import cn.hutool.core.util.StrUtil;
+import com.spring4all.swagger.EnableSwagger2Doc;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.boot.builder.SpringApplicationBuilder;
+import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
+import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
+import org.springframework.cloud.netflix.zuul.EnableZuulProxy;
+import org.springframework.cloud.netflix.zuul.filters.discovery.PatternServiceRouteMapper;
+import org.springframework.cloud.openfeign.EnableFeignClients;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Primary;
+import org.springframework.stereotype.Component;
+import org.springframework.web.cors.CorsConfiguration;
+import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
+import org.springframework.web.filter.CorsFilter;
+import springfox.documentation.swagger.web.SwaggerResource;
+import springfox.documentation.swagger.web.SwaggerResourcesProvider;
+
+import java.util.ArrayList;
+import java.util.List;
+
+
+@EnableZuulProxy
+@EnableDiscoveryClient
+@SpringBootApplication
+@EnableFeignClients
+@EnableSwagger2Doc
+public class GatewayApplication extends SpringBootServletInitializer {
+
+    @Value("${swagger.resource}")
+    private String        swaggerResource;
+
+    @Override
+    protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
+        return application.sources(GatewayApplication.class);
+    }
+
+    public static void main(String[] args) throws Exception {
+        SpringApplication.run(GatewayApplication.class, args);
+    }
+
+	@Bean
+	public PatternServiceRouteMapper serviceRouteMapper() {
+		return new PatternServiceRouteMapper(
+				"(?<name>^.+)-(?<version>v.+$)",
+				"${version}/${name}");
+	}
+
+    @Component
+    @Primary
+    class DocumentationConfig implements SwaggerResourcesProvider {
+        @Override
+        public List<SwaggerResource> get() {
+            List resources = new ArrayList<>();
+            resources.add(swaggerResource("gateway", "/v2/api-docs", "1.0"));
+            if (StrUtil.isNotBlank(swaggerResource)){
+                for (String resource:swaggerResource.split(",")) {
+                    resources.add(swaggerResource(resource, "/"+resource+"/v2/api-docs", "1.0"));
+                }
+            }
+            return resources;
+        }
+
+        private SwaggerResource swaggerResource(String name, String location, String version) {
+            SwaggerResource swaggerResource = new SwaggerResource();
+            swaggerResource.setName(name);
+            swaggerResource.setLocation(location);
+            swaggerResource.setSwaggerVersion(version);
+            return swaggerResource;
+        }
+    }
+
+    @Bean
+    public CorsFilter corsFilter() {
+        final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
+        final CorsConfiguration config = new CorsConfiguration();
+        config.setAllowCredentials(true); // 允许cookies跨域
+        config.addAllowedOrigin("*");// 允许向该服务器提交请求的URI,*表示全部允许。。这里尽量限制来源域,比如http://xxxx:8080 ,以降低安全风险。。
+        config.addAllowedHeader("*");// 允许访问的头信息,*表示全部
+        config.setMaxAge(18000L);// 预检请求的缓存时间(秒),即在这个时间段里,对于相同的跨域请求不会再预检了
+        config.addAllowedMethod("*");// 允许提交请求的方法,*表示全部允许,也可以单独设置GET、PUT等
+        config.addAllowedMethod("HEAD");
+        config.addAllowedMethod("GET");// 允许Get的请求方法
+        config.addAllowedMethod("PUT");
+        config.addAllowedMethod("POST");
+        config.addAllowedMethod("DELETE");
+        config.addAllowedMethod("PATCH");
+        source.registerCorsConfiguration("/**", config);
+        return new CorsFilter(source);
+    }
+}
+

+ 50 - 0
src/main/java/com/medipath/project/Index.java

@@ -0,0 +1,50 @@
+//package com.kameng.project;
+//
+//import java.io.File;
+//
+//import javax.servlet.http.HttpServletRequest;
+//
+//import org.springframework.stereotype.Controller;
+//
+//import com.kameng.project.docs.DocsBaseRpc;
+//import com.kameng.project.docs.generator.DocsGenerator;
+//
+//
+//@Controller
+//public class Index extends DocsBaseRpc {
+//
+//	private static String springXmlPath = "/config/spring-core.xml";
+//
+//	@NoToken
+//	public void execute(String uri, HttpServletRequest request) {
+//		super.execute(uri, request);
+//	}
+//
+//	// 更新 docs.db 文件
+//	public static void main(String[] args) {	
+//		
+//		String currentProject = "product-api";
+//		String propterFile = "/product-api/src/main/resources/config.properties";
+//		String packageName = "com.cgtz.module";
+//
+//		String classPath = DocsBaseRpc.class.getResource(propterFile.substring(propterFile.lastIndexOf("/"))).getPath();
+//		String pcDbFile = classPath.replace("target/classes" + propterFile.substring(propterFile.lastIndexOf("/")), "src/main/resources/docs_pc.db");
+//		String appDbFile = classPath.replace("target/classes" + propterFile.substring(propterFile.lastIndexOf("/")), "src/main/resources/docs_app.db");
+//		String pom = classPath.replace("product-api/target/classes" + propterFile.substring(propterFile.lastIndexOf("/")), "pom.xml");
+//
+//		System.out.println("classPath: " + classPath);
+//		System.out.println("pcDbFile: " + pcDbFile);
+//		System.out.println("appDbFile: " + appDbFile);
+//		System.out.println("pomFile: " + pom);
+//		//pom = pom.substring(1);
+//		//System.out.println(pom);
+//		if (!new File(pom).exists()) {
+//			throw new RuntimeException("没有找到 pom.xml");
+//		}
+//		DocsGenerator pc = new DocsGenerator(pcDbFile, springXmlPath, pom, false);
+//		pc.createDbFile(packageName);
+//
+//		DocsGenerator app = new DocsGenerator(appDbFile, springXmlPath, pom, true);
+//		app.createDbFile(packageName);
+//	}
+//}

+ 67 - 0
src/main/java/com/medipath/project/configurer/ApiFallbackProvider.java

@@ -0,0 +1,67 @@
+package com.medipath.project.configurer;
+
+import org.springframework.cloud.netflix.zuul.filters.route.FallbackProvider;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.MediaType;
+import org.springframework.http.client.ClientHttpResponse;
+import org.springframework.stereotype.Component;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * @author: linjie
+ * @description:错误拦截回显,熔断
+ * @create: 2018/10/11 20:01
+ */
+@Component
+public class ApiFallbackProvider implements FallbackProvider{
+
+    @Override
+    public String getRoute() {
+        //设置熔断的服务名
+        //如果是所有服务则设置为*
+        return "*";
+    }
+
+    @Override
+    public ClientHttpResponse fallbackResponse(String route, Throwable cause) {
+        return new ClientHttpResponse() {
+            @Override
+            public HttpStatus getStatusCode() throws IOException {
+                return HttpStatus.OK;
+            }
+
+            @Override
+            public int getRawStatusCode() throws IOException {
+                return 200;
+            }
+
+            @Override
+            public String getStatusText() throws IOException {
+                return "{code:0,message:service error =_=}";
+            }
+
+            @Override
+            public void close() {
+
+            }
+
+            @Override
+            public InputStream getBody() throws IOException {
+                return new ByteArrayInputStream("{\"code\":\"599\",\"message\":\"zuul Access Filter - The service is unavailable.\"}".getBytes());
+            }
+
+            @Override
+            public HttpHeaders getHeaders() {
+                HttpHeaders headers = new HttpHeaders();
+                headers.setContentType(MediaType.APPLICATION_JSON);
+                return headers;
+            }
+        };
+    }
+
+
+}

+ 26 - 0
src/main/java/com/medipath/project/configurer/CorsConfig.java

@@ -0,0 +1,26 @@
+package com.medipath.project.configurer;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.cors.CorsConfiguration;
+import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
+import org.springframework.web.filter.CorsFilter;
+
+import java.util.Arrays;
+
+@Configuration
+public class CorsConfig {
+
+    @Bean
+    public CorsFilter corsFilter(){
+        final UrlBasedCorsConfigurationSource source=new UrlBasedCorsConfigurationSource();
+        final CorsConfiguration config=new CorsConfiguration();
+        config.setAllowCredentials(true);
+        config.setAllowedHeaders(Arrays.asList("*"));
+        config.setAllowedOrigins(Arrays.asList("*"));  //http:www.a.com
+        config.setAllowedMethods(Arrays.asList("*"));
+        config.setMaxAge(300L);
+        source.registerCorsConfiguration("/",config);
+        return new CorsFilter(source);
+    }
+}

+ 150 - 0
src/main/java/com/medipath/project/configurer/MyWebMvcConfigurer.java

@@ -0,0 +1,150 @@
+package com.medipath.project.configurer;
+
+import java.io.IOException;
+import java.nio.charset.Charset;
+import java.util.List;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter;
+import com.medipath.project.core.ProjectConstant;
+import com.medipath.project.core.Result;
+import com.medipath.project.core.ServiceException;
+import com.medipath.project.enums.ResultCode;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.http.converter.HttpMessageConverter;
+import org.springframework.web.method.HandlerMethod;
+import org.springframework.web.servlet.HandlerExceptionResolver;
+import org.springframework.web.servlet.ModelAndView;
+import org.springframework.web.servlet.NoHandlerFoundException;
+import org.springframework.web.servlet.config.annotation.CorsRegistry;
+import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
+import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.serializer.SerializerFeature;
+import com.alibaba.fastjson.support.config.FastJsonConfig;
+
+/**
+ * Spring MVC 配置
+ */
+@Configuration
+public class MyWebMvcConfigurer implements WebMvcConfigurer {
+
+    private final Logger  logger = LoggerFactory.getLogger(MyWebMvcConfigurer.class);
+    @Value("${spring.profiles.active}")
+    private String        env;                                                      // 当前激活的配置文件
+
+    @Value("${signature}")
+    private String        signature;                                                // 是否启用签名
+
+    @Value("${login.check}")
+    private String        loginCheck;                                              // 是否启用登录验证
+
+//    @Resource
+//    private RedisTemplate redisTemplate;
+
+    @Value("${spring.application.name}")
+    private String        application;
+
+    @Override
+    public void addResourceHandlers(ResourceHandlerRegistry registry) {
+        registry.addResourceHandler("doc.html")
+                .addResourceLocations("classpath:/META-INF/resources/");
+        registry.addResourceHandler("swagger-ui.html")
+                .addResourceLocations("classpath:/META-INF/resources/");
+        registry.addResourceHandler("docs.html")
+                .addResourceLocations("classpath:/META-INF/resources/");
+        registry.addResourceHandler("/webjars/**")
+                .addResourceLocations("classpath:/META-INF/resources/webjars/");
+    }
+
+    // 使用阿里 FastJson 作为JSON MessageConverter
+    @Override
+    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
+        FastJsonHttpMessageConverter converter = new FastJsonHttpMessageConverter();
+        FastJsonConfig config = new FastJsonConfig();
+        config.setSerializerFeatures(SerializerFeature.WriteMapNullValue,// 保留空的字段
+            SerializerFeature.WriteNullStringAsEmpty,// String null -> ""
+            SerializerFeature.WriteNullNumberAsZero);// Number null -> 0
+        converter.setFastJsonConfig(config);
+        converter.setDefaultCharset(Charset.forName("UTF-8"));
+        converters.add(converter);
+        // 设置项目名称
+        ProjectConstant.application = application;
+    }
+
+    // 统一异常处理
+    @Override
+    public void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> exceptionResolvers) {
+        exceptionResolvers.add(new HandlerExceptionResolver() {
+            @Override
+            public ModelAndView resolveException(HttpServletRequest request,
+                                                 HttpServletResponse response, Object handler,
+                                                 Exception e) {
+                Result result = new Result();
+                if (e instanceof ServiceException) {// 业务失败的异常,如“账号或密码错误”
+                    result.setCode(ResultCode.FAIL).setMessage(e.getMessage());
+                    logger.info(e.getMessage());
+                } else if (e instanceof NoHandlerFoundException) {
+                    result.setCode(ResultCode.NOT_FOUND).setMessage(
+                        "接口 [" + request.getRequestURI() + "] 不存在");
+                } else if (e instanceof ServletException) {
+                    result.setCode(ResultCode.FAIL).setMessage(e.getMessage());
+                } else {
+                    result.setCode(ResultCode.INTERNAL_SERVER_ERROR).setMessage(
+                        "接口 [" + request.getRequestURI() + "] 内部错误,请联系管理员");
+                    String message;
+                    if (handler instanceof HandlerMethod) {
+                        HandlerMethod handlerMethod = (HandlerMethod) handler;
+                        message = String.format("接口 [%s] 出现异常,方法:%s.%s,异常摘要:%s",
+                            request.getRequestURI(), handlerMethod.getBean().getClass().getName(),
+                            handlerMethod.getMethod().getName(), e.getMessage());
+                    } else {
+                        message = e.getMessage();
+                    }
+                    logger.error(message, e);
+                }
+                responseResult(response, result);
+                return new ModelAndView();
+            }
+
+        });
+    }
+
+    // 解决跨域问题
+    @Override
+    public void addCorsMappings(CorsRegistry registry) {
+        registry.addMapping("/**")
+                .allowedOrigins("*")
+                .allowedMethods("GET", "HEAD", "POST", "PUT", "DELETE", "OPTIONS")
+                .allowCredentials(true)
+                .maxAge(3600)
+                .allowedHeaders("*");
+    }
+
+    // 添加拦截器
+    @Override
+    public void addInterceptors(InterceptorRegistry registry) {
+        // 接口签名认证拦截器,该签名认证比较简单,实际项目中可以使用Json Web Token或其他更好的方式替代。
+
+    }
+
+    private void responseResult(HttpServletResponse response, Result result) {
+        response.setCharacterEncoding("UTF-8");
+        response.setHeader("Content-type", "application/json;charset=UTF-8");
+        response.setStatus(200);
+        try {
+            response.getWriter().write(JSON.toJSONString(result));
+        } catch (IOException ex) {
+            logger.error(ex.getMessage());
+        }
+    }
+
+}

+ 36 - 0
src/main/java/com/medipath/project/configurer/Parameters.java

@@ -0,0 +1,36 @@
+package com.medipath.project.configurer;
+
+import lombok.Data;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.cloud.context.config.annotation.RefreshScope;
+import org.springframework.stereotype.Component;
+
+@Component
+@Data
+@RefreshScope
+public class Parameters {
+    /**
+     *
+     当前激活的配置文件
+     */
+    @Value("${spring.profiles.active}")
+    private String        env;
+
+    /**
+     *
+     是否启用签名
+     */
+    @Value("${signature}")
+    private String        signature;
+
+    /**
+     * 是否启用登录验证
+     */
+    @Value("${login.check}")
+    private String        loginCheck;
+
+    @Value("${spring.application.name}")
+    private String        application;
+
+
+}

+ 62 - 0
src/main/java/com/medipath/project/configurer/SwaggerConfig.java

@@ -0,0 +1,62 @@
+package com.medipath.project.configurer;
+
+import io.swagger.annotations.ApiOperation;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Profile;
+import springfox.documentation.builders.ApiInfoBuilder;
+import springfox.documentation.builders.ParameterBuilder;
+import springfox.documentation.builders.PathSelectors;
+import springfox.documentation.builders.RequestHandlerSelectors;
+import springfox.documentation.schema.ModelRef;
+import springfox.documentation.service.ApiInfo;
+import springfox.documentation.service.Contact;
+import springfox.documentation.service.Parameter;
+import springfox.documentation.spi.DocumentationType;
+import springfox.documentation.spring.web.plugins.Docket;
+import springfox.documentation.swagger2.annotations.EnableSwagger2;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@Configuration
+@EnableSwagger2
+@Profile({"dev","test"})
+public class SwaggerConfig {
+
+    @Bean
+    public Docket docket(){
+        //添加header参数
+        List<Parameter> pars = new ArrayList<>();
+        ParameterBuilder ticketPar = new ParameterBuilder();
+        ticketPar.name("token").description("user token")
+                .modelRef(new ModelRef("string")).parameterType("header")
+                .required(false).build(); //header中的ticket参数非必填,传空也可以
+        ticketPar = new ParameterBuilder();
+        ticketPar.name("appCode").description("appCode")
+                .modelRef(new ModelRef("string")).parameterType("header")
+                .required(false).build(); //header中的ticket参数非必填,传空也可以
+
+        pars.add(ticketPar.build());
+        return new Docket(DocumentationType.SWAGGER_2)
+                .globalOperationParameters(pars)
+                .groupName("demo")
+                .apiInfo(getApiInfo())
+                .select()
+                //设置basePackage会将包下的所有被@Api标记类的所有方法作为api
+                .apis(RequestHandlerSelectors.basePackage("com.yiweikeji.dream.web"))
+                //只有标记了@ApiOperation的方法才会暴露出给swagger
+                .apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class))
+                .paths(PathSelectors.regex("/api/.*")).build();
+    }
+
+    private ApiInfo getApiInfo(){
+        return new ApiInfoBuilder()
+                .title("API接口文档")
+                .description("swagger2 demo api")
+                .termsOfServiceUrl("http://localhost/swagger-ui.html")
+                .version("1.0").contact(new Contact("admin", "http://localhost/swagger-ui.html", "xxx@qq.com"))
+                .build();
+    }
+
+}

+ 81 - 0
src/main/java/com/medipath/project/core/AbstractService.java

@@ -0,0 +1,81 @@
+package com.medipath.project.core;
+
+
+import org.apache.ibatis.exceptions.TooManyResultsException;
+import org.springframework.beans.factory.annotation.Autowired;
+import tk.mybatis.mapper.entity.Condition;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.ParameterizedType;
+import java.util.List;
+
+/**
+ * 基于通用MyBatis Mapper插件的Service接口的实现
+ */
+public abstract class AbstractService<T> implements Service<T> {
+
+    @Autowired
+    protected Mapper<T> mapper;
+
+    private Class<T> modelClass;    // 当前泛型真实类型的Class
+
+    public AbstractService() {
+        ParameterizedType pt = (ParameterizedType) this.getClass().getGenericSuperclass();
+        modelClass = (Class<T>) pt.getActualTypeArguments()[0];
+    }
+
+    @Override
+    public int  save(T model) {
+       return mapper.insertSelective(model);
+    }
+
+    @Override
+    public int  save(List<T> models) {
+    	return mapper.insertList(models);
+    }
+
+    @Override
+    public void deleteById(Integer id) {
+        mapper.deleteByPrimaryKey(id);
+    }
+
+    @Override
+    public void deleteByIds(String ids) {
+        mapper.deleteByIds(ids);
+    }
+
+    @Override
+    public void update(T model) {
+        mapper.updateByPrimaryKeySelective(model);
+    }
+
+    @Override
+    public T findById(Integer id) {
+        return mapper.selectByPrimaryKey(id);
+    }
+
+    @Override
+    public T findBy(String fieldName, Object value) throws TooManyResultsException {
+        try {
+            T model = modelClass.newInstance();
+            Field field = modelClass.getDeclaredField(fieldName);
+            field.setAccessible(true);
+            field.set(model, value);
+            return mapper.selectOne(model);
+        } catch (ReflectiveOperationException e) {
+            throw new ServiceException(e.getMessage(), e);
+        }
+    }
+
+    public List<T> findByIds(String ids) {
+        return mapper.selectByIds(ids);
+    }
+
+    public List<T> findByCondition(Condition condition) {
+        return mapper.selectByCondition(condition);
+    }
+
+    public List<T> findAll() {
+        return mapper.selectAll();
+    }
+}

+ 36 - 0
src/main/java/com/medipath/project/core/BaseBeen.java

@@ -0,0 +1,36 @@
+package com.medipath.project.core;
+
+import javax.persistence.Transient;
+
+import org.apache.commons.lang.builder.ToStringBuilder;
+import org.apache.commons.lang.builder.ToStringStyle;
+
+public class BaseBeen {
+    
+    @Transient 
+    private String startTime;
+    
+    @Transient 
+    private String endTime;
+
+	public String getStartTime() {
+        return startTime;
+    }
+
+    public void setStartTime(String startTime) {
+        this.startTime = startTime;
+    }
+
+    public String getEndTime() {
+        return endTime;
+    }
+
+    public void setEndTime(String endTime) {
+        this.endTime = endTime;
+    }
+
+    @Override
+    public String toString() {
+        return ToStringBuilder.reflectionToString(this, ToStringStyle.MULTI_LINE_STYLE);  
+    }
+}

+ 15 - 0
src/main/java/com/medipath/project/core/BaseUser.java

@@ -0,0 +1,15 @@
+package com.medipath.project.core;
+
+/**
+ * 管理员与会员的父类
+ * 
+ * @author wang xiaobo
+ * 
+ */
+public abstract class BaseUser {
+
+	private static final long serialVersionUID = 1L;
+
+	public abstract String getUserName();
+
+}

+ 17 - 0
src/main/java/com/medipath/project/core/Mapper.java

@@ -0,0 +1,17 @@
+package com.medipath.project.core;
+
+import tk.mybatis.mapper.common.BaseMapper;
+import tk.mybatis.mapper.common.ConditionMapper;
+import tk.mybatis.mapper.common.IdsMapper;
+import tk.mybatis.mapper.common.special.InsertListMapper;
+
+/**
+ * 定制版MyBatis Mapper插件接口,如需其他接口参考官方文档自行添加。
+ */
+public interface Mapper<T>
+        extends
+        BaseMapper<T>,
+        ConditionMapper<T>,
+        IdsMapper<T>,
+        InsertListMapper<T> {
+}

+ 18 - 0
src/main/java/com/medipath/project/core/ProjectConstant.java

@@ -0,0 +1,18 @@
+package com.medipath.project.core;
+
+/**
+ * 项目常量
+ */
+public final class ProjectConstant {
+    public static final String BASE_PACKAGE = "com.medipath.project";//项目基础包名称,根据自己公司的项目修改
+
+    public static final String MODEL_PACKAGE = BASE_PACKAGE + ".model";//Model所在包
+    public static final String MAPPER_PACKAGE = BASE_PACKAGE + ".dao";//Mapper所在包
+    public static final String SERVICE_PACKAGE = BASE_PACKAGE + ".service";//Service所在包
+    public static final String SERVICE_IMPL_PACKAGE = SERVICE_PACKAGE + ".impl";//ServiceImpl所在包
+    public static final String CONTROLLER_PACKAGE = BASE_PACKAGE + ".web";//Controller所在包
+
+    public static final String MAPPER_INTERFACE_REFERENCE = BASE_PACKAGE + ".core.Mapper";//Mapper插件基础接口的完全限定名
+    //项目名称
+    public static  String application;
+}

+ 50 - 0
src/main/java/com/medipath/project/core/Result.java

@@ -0,0 +1,50 @@
+package com.medipath.project.core;
+
+import com.alibaba.fastjson.JSON;
+import com.medipath.project.enums.ResultCode;
+
+/**
+ * 统一API响应结果封装
+ */
+public class Result {
+    private String code;
+    private String message;
+    private Object data;
+
+    public Result setCode(ResultCode resultCode) {
+        this.code = resultCode.code();
+        return this;
+    }
+
+    public String getCode() {
+        return code;
+    }
+
+    public Result setCode(String code) {
+        this.code = code;
+        return this;
+    }
+
+    public String getMessage() {
+        return message;
+    }
+
+    public Result setMessage(String message) {
+        this.message = message;
+        return this;
+    }
+
+    public Object getData() {
+        return data;
+    }
+
+    public Result setData(Object data) {
+        this.data = data;
+        return this;
+    }
+
+    @Override
+    public String toString() {
+        return JSON.toJSONString(this);
+    }
+}

+ 35 - 0
src/main/java/com/medipath/project/core/ResultGenerator.java

@@ -0,0 +1,35 @@
+package com.medipath.project.core;
+
+import com.medipath.project.enums.ResultCode;
+
+/**
+ * 响应结果生成工具
+ */
+public class ResultGenerator {
+    private static final String DEFAULT_SUCCESS_MESSAGE = "SUCCESS";
+
+    public static Result genSuccessResult() {
+        return new Result()
+                .setCode(ResultCode.SUCCESS)
+                .setMessage(DEFAULT_SUCCESS_MESSAGE);
+    }
+
+    public static Result genSuccessResult(Object data) {
+        return new Result()
+                .setCode(ResultCode.SUCCESS)
+                .setMessage(DEFAULT_SUCCESS_MESSAGE)
+                .setData(data);
+    }
+
+    public static Result genFailResult(String message) {
+        return new Result()
+                .setCode(ResultCode.FAIL)
+                .setMessage(message);
+    }
+    
+    public static Result genFailResult(ResultCode code) {
+        return new Result()
+                .setCode(code.getCode())
+                .setMessage(code.getMessage());
+    }
+}

+ 22 - 0
src/main/java/com/medipath/project/core/Service.java

@@ -0,0 +1,22 @@
+package com.medipath.project.core;
+
+import org.apache.ibatis.exceptions.TooManyResultsException;
+import tk.mybatis.mapper.entity.Condition;
+
+import java.util.List;
+
+/**
+ * Service 层 基础接口,其他Service 接口 请继承该接口
+ */
+public interface Service<T> {
+	int  save(T model);//持久化
+	int  save(List<T> models);//批量持久化
+    void deleteById(Integer id);//通过主鍵刪除
+    void deleteByIds(String ids);//批量刪除 eg:ids -> “1,2,3,4”
+    void update(T model);//更新
+    T findById(Integer id);//通过ID查找
+    T findBy(String fieldName, Object value) throws TooManyResultsException; //通过Model中某个成员变量名称(非数据表中column的名称)查找,value需符合unique约束
+    List<T> findByIds(String ids);//通过多个ID查找//eg:ids -> “1,2,3,4”
+    List<T> findByCondition(Condition condition);//根据条件查找
+    List<T> findAll();//获取所有
+}

+ 17 - 0
src/main/java/com/medipath/project/core/ServiceException.java

@@ -0,0 +1,17 @@
+package com.medipath.project.core;
+
+/**
+ * 服务(业务)异常如“ 账号或密码错误 ”,该异常只做INFO级别的日志记录 @see WebMvcConfigurer
+ */
+public class ServiceException extends RuntimeException {
+    public ServiceException() {
+    }
+
+    public ServiceException(String message) {
+        super(message);
+    }
+
+    public ServiceException(String message, Throwable cause) {
+        super(message, cause);
+    }
+}

+ 33 - 0
src/main/java/com/medipath/project/core/cache/CacheKey.java

@@ -0,0 +1,33 @@
+package com.medipath.project.core.cache;
+
+import java.io.Serializable;
+
+
+public class CacheKey implements Serializable {
+
+	private CacheType cacheType;
+
+	private String subKey;
+
+	private CacheKey() {
+	}
+
+	public static CacheKey generateKey(CacheType cacheType, String subKey) {
+		if (cacheType == null) {
+			throw new NullPointerException("CacheKey不允许存在空参数");
+		}
+		CacheKey key = new CacheKey();
+		key.cacheType = cacheType;
+		key.subKey = subKey;
+		return key;
+	}
+
+	public String toString() {
+		return new StringBuilder().append(cacheType).append("_").append(subKey != null ? subKey : "").toString();
+	}
+
+	public CacheType getType() {
+		return cacheType;
+	}
+
+}

+ 35 - 0
src/main/java/com/medipath/project/core/cache/CacheType.java

@@ -0,0 +1,35 @@
+package com.medipath.project.core.cache;
+
+import com.medipath.project.core.ProjectConstant;
+
+public enum CacheType {
+
+	// ----------------------- token 密钥相关 ------------------------
+	/**
+	 * 卡盟 token
+	 */
+	UserCifToken,
+	
+	   /**
+     * 卡盟 图片code
+     */
+    PicCode,
+
+	/**
+	 * app端密钥
+	 */
+	AppSecretKey,
+
+	// ------------------------- 订单相关 -------------------------
+	/**
+	 * 用户相关的一些配置:黑名单等
+	 */
+	UserConfig;
+
+	private static String keyHead = ProjectConstant.application;
+
+	@Override
+	public String toString() {
+		return keyHead + "_" + name();
+	};
+}

+ 13 - 0
src/main/java/com/medipath/project/core/interceptor/LoginType.java

@@ -0,0 +1,13 @@
+package com.medipath.project.core.interceptor;
+
+public enum LoginType {
+	/**
+	 * 普通用户
+	 */
+	Common,
+
+	/**
+	 * 商家用户
+	 */
+	Provider
+}

+ 16 - 0
src/main/java/com/medipath/project/core/interceptor/NoToken.java

@@ -0,0 +1,16 @@
+package com.medipath.project.core.interceptor;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+
+/**
+ * @author linxk
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ ElementType.METHOD, ElementType.TYPE })
+public @interface NoToken {
+	public LoginType type() default LoginType.Common;
+}

+ 24 - 0
src/main/java/com/medipath/project/core/interceptor/ThreadUser.java

@@ -0,0 +1,24 @@
+package com.medipath.project.core.interceptor;
+
+import com.medipath.project.core.BaseUser;
+
+/**
+ * @author linxk
+ */
+public class ThreadUser {
+
+	private static ThreadLocal<BaseUser> local = new ThreadLocal<BaseUser>();
+
+	public static void putLoginUser(BaseUser user) {
+		local.set(user);
+	}
+
+	public static BaseUser getLoginUser() {
+		return local.get();
+	}
+
+	public static void remove() {
+		local.remove();
+	}
+
+}

+ 24 - 0
src/main/java/com/medipath/project/dubbo/client/OperatingInterfacesDubboServiceClient.java

@@ -0,0 +1,24 @@
+package com.medipath.project.dubbo.client;
+
+import com.medipath.operating.dto.ErrorCodeDTO;
+import com.medipath.operating.dto.InterfacesDTO;
+import com.medipath.operating.result.Result;
+import com.medipath.operating.service.OperatingInterfacesDubboService;
+import org.apache.dubbo.config.annotation.Reference;
+import org.springframework.stereotype.Component;
+
+@Component
+public class OperatingInterfacesDubboServiceClient {
+
+    @Reference
+    private OperatingInterfacesDubboService operatingInterfacesDubboService;
+
+    public Result<InterfacesDTO> detailForGateway(String servicePath) {
+        return operatingInterfacesDubboService.detailForGateway(servicePath);
+    }
+
+    public Result<ErrorCodeDTO> getErrorCode(String servicePath, String retCode){
+        return operatingInterfacesDubboService.getErrorCode(servicePath,retCode);
+    }
+
+}

+ 26 - 0
src/main/java/com/medipath/project/dubbo/client/UserDubboServiceClient.java

@@ -0,0 +1,26 @@
+package com.medipath.project.dubbo.client;
+
+import com.medipath.cif.dto.Result;
+import com.medipath.cif.dto.UserDTO;
+import com.medipath.cif.service.UserDubboService;
+import org.apache.dubbo.config.annotation.Reference;
+import org.springframework.stereotype.Component;
+
+@Component
+public class UserDubboServiceClient {
+
+    @Reference
+    private UserDubboService userDubboService;
+
+    public UserDTO validateLogin(String token) {
+        return userDubboService.validateLogin(token).getData();
+    }
+
+    public void updateLastTime(Long userId){
+        userDubboService.updateLastTime(userId);
+    }
+
+    public UserDTO getUser(Long userId){
+        return userDubboService.getUser(userId);
+    }
+}

+ 51 - 0
src/main/java/com/medipath/project/enums/ResultCode.java

@@ -0,0 +1,51 @@
+package com.medipath.project.enums;
+
+/**
+ * 响应码枚举,参考HTTP状态码的语义
+ */
+public enum ResultCode {
+    SUCCESS("200","成功"),//成功
+    FAIL("400","失败"),//失败
+    UNAUTHORIZED("401","未认证(签名错误)"),//未认证(签名错误)
+    NOT_FOUND("404","接口不存在"),//接口不存在
+    INTERNAL_SERVER_ERROR("500","服务器内部错误");//服务器内部错误
+
+    private final String code;   //状态码
+    private final String message;
+
+    ResultCode(String code, String message) {
+        this.code = code;
+        this.message = message;
+    }
+
+    public String code() {
+        return code;
+    }
+
+    public String message() {
+        return message;
+    }
+
+    public String getCode() {
+        return code;
+    }
+
+    public String getMessage() {
+        return message;
+    }
+
+    /**
+     * 通过状态码获取ENUM的名字
+     * @param statusCode
+     * @return
+     */
+    public static ResultCode getEnumByStatusCode(String code) {
+        for (ResultCode p : ResultCode.values()) {
+            if (p.code().equalsIgnoreCase(code)) {
+                return p;
+            }
+        }
+
+        return null;
+    }
+}

+ 399 - 0
src/main/java/com/medipath/project/filter/AccessFilter.java

@@ -0,0 +1,399 @@
+package com.medipath.project.filter;
+
+import cn.hutool.core.date.DateUtil;
+import cn.hutool.core.util.StrUtil;
+import com.alibaba.fastjson.JSON;
+import com.medipath.cif.dto.UserDTO;
+import com.medipath.operating.dto.InterfacesDTO;
+import com.medipath.operating.result.Result;
+import com.medipath.project.dubbo.client.OperatingInterfacesDubboServiceClient;
+import com.medipath.project.dubbo.client.UserDubboServiceClient;
+import com.netflix.zuul.ZuulFilter;
+import com.netflix.zuul.context.RequestContext;
+import io.jsonwebtoken.Claims;
+import io.jsonwebtoken.ExpiredJwtException;
+import io.jsonwebtoken.Jwts;
+import org.apache.commons.codec.digest.DigestUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.lang3.tuple.ImmutableTriple;
+import org.apache.commons.lang3.tuple.Triple;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Component;
+
+import javax.annotation.Resource;
+import javax.servlet.http.HttpServletRequest;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+@Component
+public class AccessFilter extends ZuulFilter {
+
+    private static Logger log = LoggerFactory.getLogger(AccessFilter.class);
+
+    private static final String X_CLIENT_TOKEN_USERID = "x-client-token-userId";
+    private static final String X_CLIENT_TOKEN_OPERATORID = "x-client-token-operatorId";
+
+    private static final String BEARER = "Bearer ";
+
+    /**
+     * jwt token 密钥,主要用于token解析,签名验证
+     */
+    @Value("${spring.security.oauth2.jwt.signingKey}")
+    private static String signingKey = "123456";
+
+
+    @Value("${signature}")
+    private String signature;                                        // 是否启用签名
+
+    @Value("${pub.secret}")
+    private String pubSecret;
+
+    @Value("${player.switch}")
+    private String playerSwitch;
+
+    @Resource
+    private UserDubboServiceClient userDubboServiceClient;
+
+    @Resource
+    private OperatingInterfacesDubboServiceClient operatingInterfacesDubboServiceClient;
+
+    @Override
+    public String filterType() {
+        return "pre";
+    }
+
+    @Override
+    public int filterOrder() {
+        return 0;
+    }
+
+    @Override
+    public boolean shouldFilter() {
+        return true;
+    }
+
+
+//    @Override    public void doFilter(ServletRequest request, ServletResponse response,            FilterChain chain) throws IOException, ServletException {
+//        HttpServletRequest req = (HttpServletRequest) request;
+//        HeaderMapRequestWrapper requestWrapper = new HeaderMapRequestWrapper(req);        //获得请求参数中的token值
+//         String token = request.getParamter("token");
+//         if(!StringUtils.isEntry(token)){			//如果请求中带有这个参数,则进行过滤加一个header头
+//             requestWrapper.addHeader("tokenr", token);
+//             chain.doFilter(requestWrapper, response); // Goes to default servlet.
+//        }
+//         chain.doFilter(request, response); // Goes to default servlet.
+//           }
+    @Override
+    public Object run() {
+        RequestContext ctx = RequestContext.getCurrentContext();
+        HttpServletRequest request = ctx.getRequest();
+        String token = request.getHeader("token");
+        String servicePath = request.getRequestURI();
+        try {
+            if (StrUtil.endWith(servicePath,"/v2/api-docs")){
+                return null;
+            }
+            Result<InterfacesDTO> interfacesDTOResult = operatingInterfacesDubboServiceClient.detailForGateway(servicePath);
+            if (!StringUtils.equals("200", interfacesDTOResult.getCode())) {
+                ctx.setSendZuulResponse(false);
+                ctx.setResponseStatusCode(200);
+                ctx.setResponseBody("{\"code\":\"512\",\"message\":\"zuul Access Filter -  unknown interfaces :"+servicePath+"\"}");
+                return false;
+            }
+            InterfacesDTO interfacesDTO = interfacesDTOResult.getData();
+            if (interfacesDTO.getStatus() != 1) {
+                ctx.setSendZuulResponse(false);
+                ctx.setResponseStatusCode(200);
+                ctx.setResponseBody("{\"code\":\"513\",\"message\":\"zuul Access Filter -  interfaces status is error\"}");
+                return false;
+            }
+
+            if (interfacesDTO.getAuthorizeType() == 1) {
+                // 登录拦截
+                Triple<Boolean, String, String> tripleUser = getUserIdFromToken(token,request);
+                if(tripleUser.getLeft() == false) {
+                    log.warn("登录认证失败,请求接口:{},请求IP:{},请求token:{},请求参数:{}", request.getRequestURI(),
+                            getIpAddress(request), request.getHeader("token"), JSON.toJSONString(request.getParameterMap()));
+                    // responseResult(response, result);
+                    ctx.setSendZuulResponse(false);
+                    ctx.setResponseStatusCode(200);
+                    ctx.setResponseBody("{\"code\":\"598\",\"message\":\"zuul Access Filter - login validate is false\"}");
+                    return false;
+                }
+                UserDTO user = userDubboServiceClient.getUser(Long.parseLong(tripleUser.getMiddle()));
+                if (user == null){
+                    ctx.setSendZuulResponse(false);
+                    ctx.setResponseStatusCode(200);
+                    ctx.setResponseBody("{\"code\":\"597\",\"message\":\"zuul Access Filter - user is null\"}");
+                    return false;
+                }
+                if (user.getStatus() != 1){
+                    ctx.setSendZuulResponse(false);
+                    ctx.setResponseStatusCode(200);
+                    ctx.setResponseBody("{\"code\":\"596\",\"message\":\"zuul Access Filter - user status is error\"}");
+                    return false;
+                }
+                ctx.addZuulRequestHeader(X_CLIENT_TOKEN_USERID, tripleUser.getMiddle());
+                ctx.addZuulRequestHeader(X_CLIENT_TOKEN_OPERATORID, tripleUser.getRight());
+                try{
+                    userDubboServiceClient.updateLastTime(Long.parseLong(tripleUser.getMiddle()));
+                }catch (Exception e){
+                    log.error("更新最后请求时间异常",e);
+                }
+//                boolean pass = validateLogin(request);
+//                if (!pass) {
+//                    log.warn("登录认证失败,请求接口:{},请求IP:{},请求token:{},请求参数:{}", request.getRequestURI(),
+//                            getIpAddress(request), request.getHeader("token"), JSON.toJSONString(request.getParameterMap()));
+//                    // responseResult(response, result);
+//                    ctx.setSendZuulResponse(false);
+//                    ctx.setResponseStatusCode(200);
+//                    ctx.setResponseBody("{\"code\":\"598\",\"message\":\"zuul Access Filter - login validate is false\"}");
+//                    return false;
+//                }
+            }
+
+//            if (StringUtils.equals("true", signature)) {
+//                // 验证签名
+//                boolean pass = validateSign(request, (String) merchant.get("secret"));
+//                if (!pass) {
+//                    log.warn("签名认证失败,请求接口:{},请求IP:{},请求参数:{}", request.getRequestURI(),
+//                        getIpAddress(request), JSON.toJSONString(request.getParameterMap()));
+//
+//                    Result result = new Result();
+//                    result.setCode(ResultCode.UNAUTHORIZED).setMessage("签名认证失败");
+//                    // responseResult(response, result);
+//                    ctx.setSendZuulResponse(false);
+//                    ctx.setResponseStatusCode(200);
+//                    ctx.setResponseBody("{\"code\":\"597\",\"message\":\"zuul Access Filter - sign validate is false\"}");
+//                    return false;
+//                }
+//            }
+
+//            if (StringUtils.equals("1", i.getMerchantAuthorizeType())) {
+//                // 商户授权
+//                boolean pass = validateMerchantAuthorize(i.getId(),merchantCode);
+//                if (!pass) {
+//                    log.warn("商户认证失败,请求接口:{},请求IP:{},请求参数:{}", request.getRequestURI(),
+//                        getIpAddress(request), JSON.toJSONString(request.getParameterMap()));
+//
+//                    Result result = new Result();
+//                    result.setCode(ResultCode.UNAUTHORIZED).setMessage("商户认证失败");
+//                    // responseResult(response, result);
+//                    ctx.setSendZuulResponse(false);
+//                    ctx.setResponseStatusCode(200);
+//                    ctx.setResponseBody("{\"code\":\"596\",\"message\":\"zuul Access Filter - merchant validate is false\"}");
+//                    return false;
+//                }
+//            }
+            if (interfacesDTO.getReturnCodeTranslate() != null) {
+                if (interfacesDTO.getReturnCodeTranslate() == 1) {
+                    request.setAttribute("returnCodeTranslate", 1);
+                } else {
+                    request.setAttribute("returnCodeTranslate", 0);
+                }
+            } else {
+                request.setAttribute("returnCodeTranslate", 0);
+            }
+        } catch (Exception e) {
+            log.error("异常e:{}", e);
+            ctx.setSendZuulResponse(false);
+            ctx.setResponseStatusCode(200);
+            ctx.setResponseBody("{\"code\":\"599\",\"message\":\"zuul Access Filter - gateway has error\"}");
+            return false;
+        }
+        return null;
+    }
+
+    /**
+     * 提取jwt token中的数据,获取用户id
+     *
+     * @param authentication
+     * @param request
+     * @return
+     */
+    private Triple<Boolean, String, String> getUserIdFromToken(String authentication,
+                                                               HttpServletRequest request) {
+        try {
+            final Claims claims = this.getJwt(authentication);
+            // claims.getExpiration();
+            //  checkt expiration
+            boolean pass = DateUtil.compare(DateUtil.date(),claims.getExpiration()) < 0;
+//            boolean pass = validateLogin(request);
+            if (pass){
+                String userId = claims.get("userId").toString();
+                String operatorId = claims.get("operatorId").toString();
+                return new ImmutableTriple<Boolean, String, String>(true, userId , operatorId);
+            }
+        } catch (Exception e) {
+            log.error("get userId from token error:{}", e.getMessage());
+        }
+        return new ImmutableTriple<Boolean, String, String>(false,null,null);
+    }
+
+
+    private static Claims getJwt(String jwtToken) {
+        if (jwtToken.startsWith(BEARER)) {
+            jwtToken = StringUtils.substring(jwtToken, BEARER.length());
+        }
+        Claims claims;
+        try {
+            claims = Jwts.parser()  //得到DefaultJwtParser
+                    .setSigningKey(signingKey.getBytes()) //设置签名的秘钥
+                    .parseClaimsJws(jwtToken).getBody();
+        } catch(ExpiredJwtException e) {
+            claims = e.getClaims();
+        }
+
+        return claims;
+    }
+
+
+    public static void main(String[] args) {
+        System.out.printf(getJwt("eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJzYXdhIiwiZXhwIjoxNjgxMzY2NTg4LCJ1c2VySWQiOjEyLCJvcGVyYXRvcklkIjo5LCJpYXQiOjE2Nzg3NzQ1ODgsImp0aSI6ImQ1ZjM3MmQ4LWM0YzEtNGZkMy05NmNlLTg3MzEyOWM3MDZjNSJ9.XP4ukA_VuOka_Siwe_DRbaYb1jh5TT4XFdz9TvEUBRs")
+                .get("userId").toString());
+    }
+
+
+    /**
+     * 检验入参(命中否决)
+     *
+     * @param i
+     * @param request
+     * @return 返回false 为命中  返回true 为未命中
+     */
+    private boolean validateParameterAuthorize(InterfacesDTO i, HttpServletRequest request) {
+//        if (i == null) {
+//            return true;
+//        }
+//        List<InterfacePmtDTO> pmts = i.getInPmts();
+//        if (pmts == null || pmts.isEmpty()) {
+//            return true;
+//        }
+//        InterfacePmt jsonPmt = null;
+//        for (InterfacePmt interfacePmt : pmts) {
+//            if (interfacePmt.getIsEnable() != null && interfacePmt.getIsEnable() == 0) {
+//                if (interfacePmt.getDataType() == 5) {
+//                    jsonPmt = interfacePmt;
+//                }
+//            }
+//        }
+//        if (jsonPmt == null) {
+//            for (InterfacePmt interfacePmt : pmts) {
+//                if (interfacePmt.getIsEnable() != null && interfacePmt.getIsEnable() == 0) {
+//                    if (StringUtils.equals(interfacePmt.getIsnull(), "1")) {
+//                        if (StringUtils.isBlank(request.getParameter(interfacePmt.getParam()))) {
+//                            log.info("接口入参验证失败:"+interfacePmt.getParam()+"不能为空");
+//                            request.setAttribute("errorMsg", interfacePmt.getParam());
+//                            return false;
+//                        }
+//                    }
+//                }
+//            }
+//        } else {
+//            JSONObject json = null;
+//            try {
+//                json = JSONObject.parseObject(request.getParameter(jsonPmt.getParam()));
+//            } catch (Exception e) {
+//                log.error("接口入参验证失败,json解析失败e:{}",e);
+//                return false;
+//            }
+//            for (InterfacePmt interfacePmt : pmts) {
+//                if (interfacePmt.getIsEnable() != null && interfacePmt.getIsEnable() == 0) {
+//                    if (StringUtils.equals(interfacePmt.getIsnull(), "1")) {
+//                        if (StringUtils.startsWith(interfacePmt.getParam(),jsonPmt.getParam()+".")) {
+//                            String pmtName = interfacePmt.getParam().substring(jsonPmt.getParam().length()+1, interfacePmt.getParam().length());
+//                            if (json.get(pmtName) == null) {
+//                                log.info("接口入参验证失败:"+interfacePmt.getParam()+"不能为空");
+//                                request.setAttribute("errorMsg", interfacePmt.getParam());
+//                                return false;
+//                            }
+//                        } else {
+//                            if (StringUtils.isBlank(request.getParameter(interfacePmt.getParam()))) {
+//                                log.info("接口入参验证失败:"+interfacePmt.getParam()+"不能为空");
+//                                request.setAttribute("errorMsg", interfacePmt.getParam());
+//                                return false;
+//                            }
+//                        }
+//                    }
+//                }
+//            }
+//        }
+        return true;
+    }
+
+    private boolean validateMerchantAuthorize(Integer id, String merchantCode) {
+//        Result findByCode = commonClient.findByCode(id, merchantCode);
+//        if (StringUtils.equals("200", findByCode.getCode())) {
+//            if (findByCode.getData()!= null) {
+//                return true;
+//            }
+//        }
+        return false;
+    }
+
+    /**
+     * 一个简单的登录认证
+     */
+    private boolean validateLogin(HttpServletRequest request) {
+        String token = request.getHeader("token");
+        if (StringUtils.isBlank(token)) {
+            return false;
+        }
+        UserDTO booleanResult = userDubboServiceClient.validateLogin(token);
+        return  booleanResult != null;
+    }
+
+    /**
+     * 一个简单的签名认证,规则: 1. 将请求参数按ascii码排序 2. 拼接为a=value&b=value...这样的字符串(不包含sign)
+     * 3. 混合密钥(secret)进行md5获得签名,与请求的签名进行比较
+     */
+    private boolean validateSign(HttpServletRequest request, String secret) {
+        String requestSign = request.getParameter("sign");// 获得请求签名,如sign=19e907700db7ad91318424a97c54ed57
+        if (StringUtils.isEmpty(requestSign)) {
+            return false;
+        }
+        List<String> keys = new ArrayList<String>(request.getParameterMap().keySet());
+        keys.remove("sign");// 排除sign参数
+        Collections.sort(keys);// 排序
+
+        StringBuilder sb = new StringBuilder();
+        for (String key : keys) {
+            sb.append(key).append("=").append(request.getParameter(key)).append("&");// 拼接字符串
+        }
+        String linkString = sb.toString();
+        linkString = StringUtils.substring(linkString, 0, linkString.length() - 1);// 去除最后一个'&'
+
+        String sign = DigestUtils.md5Hex(linkString + secret + pubSecret);// 混合密钥md5
+
+        return StringUtils.equals(sign, requestSign);// 比较
+    }
+
+    private String getIpAddress(HttpServletRequest request) {
+        String ip = request.getHeader("x-forwarded-for");
+        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
+            ip = request.getHeader("Proxy-Client-IP");
+        }
+        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
+            ip = request.getHeader("WL-Proxy-Client-IP");
+        }
+        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
+            ip = request.getHeader("HTTP_CLIENT_IP");
+        }
+        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
+            ip = request.getHeader("HTTP_X_FORWARDED_FOR");
+        }
+        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
+            ip = request.getRemoteAddr();
+        }
+        // 如果是多级代理,那么取第一个ip为客户端ip
+        if (ip != null && ip.indexOf(",") != -1) {
+            ip = ip.substring(0, ip.indexOf(",")).trim();
+        }
+
+        return ip;
+    }
+
+}

+ 44 - 0
src/main/java/com/medipath/project/filter/ErrorFilter.java

@@ -0,0 +1,44 @@
+package com.medipath.project.filter;
+
+import com.netflix.zuul.ZuulFilter;
+import com.netflix.zuul.context.RequestContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Component;
+
+import javax.servlet.http.HttpServletResponse;
+
+/**
+ * 捕获为处理的异常统一做一些处理,让`SendErrorFilter`可以正确的返回异常信息
+ */
+@Component
+public class ErrorFilter extends ZuulFilter {
+
+    Logger log = LoggerFactory.getLogger(ErrorFilter.class);
+
+    @Override
+    public String filterType() {
+        return "error";
+    }
+
+    @Override
+    public int filterOrder() {
+        return 10;
+    }
+
+    @Override
+    public boolean shouldFilter() {
+        return true;
+    }
+
+    @Override
+    public Object run() {
+        RequestContext ctx = RequestContext.getCurrentContext();
+        Throwable throwable = RequestContext.getCurrentContext().getThrowable();
+        log.error("this is a ErrorFilter : {}", throwable);
+        ctx.set("error.status_code", HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
+        ctx.set("error.exception", throwable.getCause());
+        return null;
+    }
+
+}

+ 52 - 0
src/main/java/com/medipath/project/filter/HeaderMapRequestWrapper.java

@@ -0,0 +1,52 @@
+package com.medipath.project.filter;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletRequestWrapper;
+import java.util.*;
+ 
+/**
+ * [ Filter请求拦截中添加header属性 ]
+ *
+ * @author Love丶TG
+ * @version 1.0.0
+ * @create 2019 年 11 月 22 日 11:44
+ */
+public class HeaderMapRequestWrapper extends HttpServletRequestWrapper {
+ 
+    public HeaderMapRequestWrapper(HttpServletRequest request) {
+        super(request);
+    }
+ 
+    private Map<String, String> headerMap = new HashMap<String, String>();
+ 
+    public void addHeader(String name, String value) {
+        headerMap.put(name, value);
+    }
+ 
+    @Override
+    public String getHeader(String name) {
+        String headerValue = super.getHeader(name);
+        if (headerMap.containsKey(name)) {
+            headerValue = headerMap.get(name);
+        }
+        return headerValue;
+    }
+ 
+    @Override
+    public Enumeration<String> getHeaderNames() {
+        List<String> names = Collections.list(super.getHeaderNames());
+        for (String name : headerMap.keySet()) {
+            names.add(name);
+        }
+        return Collections.enumeration(names);
+    }
+ 
+    @Override
+    public Enumeration<String> getHeaders(String name) {
+        List<String> values = Collections.list(super.getHeaders(name));
+        if (headerMap.containsKey(name)) {
+            values.add(headerMap.get(name));
+        }
+        return Collections.enumeration(values);
+    }
+}

+ 101 - 0
src/main/java/com/medipath/project/filter/PostFilter.java

@@ -0,0 +1,101 @@
+package com.medipath.project.filter;
+
+import com.alibaba.fastjson.JSONObject;
+import com.medipath.operating.dto.ErrorCodeDTO;
+import com.medipath.operating.result.Result;
+import com.medipath.project.dubbo.client.OperatingInterfacesDubboServiceClient;
+import com.netflix.zuul.ZuulFilter;
+import com.netflix.zuul.context.RequestContext;
+import org.apache.commons.lang.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Component;
+
+import javax.annotation.Resource;
+import javax.servlet.http.HttpServletRequest;
+import java.io.ByteArrayOutputStream;
+import java.io.InputStream;
+
+/**
+ * 捕获为处理的异常统一做一些处理,让`SendErrorFilter`可以正确的返回异常信息
+ */
+@Component
+public class PostFilter extends ZuulFilter {
+
+    Logger log = LoggerFactory.getLogger(PostFilter.class);
+
+    @Resource
+    private OperatingInterfacesDubboServiceClient operatingInterfacesDubboServiceClient;
+
+    @Override
+    public String filterType() {
+        return "post";
+    }
+
+    @Override
+    public int filterOrder() {
+        return 10;
+    }
+
+    @Override
+    public boolean shouldFilter() {
+        return true;
+    }
+
+    @Override
+    public Object run() {
+        RequestContext ctx = RequestContext.getCurrentContext();
+        try {
+            HttpServletRequest request = ctx.getRequest();
+//            log.info(String.format("%s AccessTokenFilter request to %s", request.getRequestURI(),
+//                    request.getRequestURL().toString()));
+            Integer returnCodeTranslate = (Integer) request.getAttribute("returnCodeTranslate");
+            if (returnCodeTranslate == null) {
+                return null;
+            }
+            if (returnCodeTranslate == 0) {
+                return null;
+            }
+            InputStream in = ctx.getResponseDataStream();
+            if (in == null) {
+                return null;
+            }
+            ByteArrayOutputStream out = new ByteArrayOutputStream();
+            byte[] buffer = new byte[1024 * 4];
+            int n = 0;
+            while ((n = in.read(buffer)) != -1) {
+                out.write(buffer, 0, n);
+            }
+            String jsonStr = new String(out.toByteArray());
+            JSONObject json = JSONObject.parseObject(jsonStr);
+            if (StringUtils.equals((String) json.get("code"), "200")) {
+                ctx.setSendZuulResponse(true);
+                ctx.setResponseStatusCode(200);
+                ctx.setResponseBody(json.toJSONString());
+                return null;
+            }
+            String service = request.getRequestURI().split("/")[1];
+            Result<ErrorCodeDTO> codeResult = operatingInterfacesDubboServiceClient.getErrorCode(service, String.valueOf(json.get("code")));
+            if (StringUtils.equals("200", codeResult.getCode())) {
+                json.put("code", codeResult.getData().getReturnCode());
+                json.put("data", json.get("data"));
+                json.put("message", codeResult.getData().getReturnMsg());
+                ctx.setSendZuulResponse(true);
+                ctx.setResponseStatusCode(200);
+                ctx.setResponseBody(json.toJSONString());// 输出最终结果
+            } else {
+                ctx.setSendZuulResponse(true);
+                ctx.setResponseStatusCode(200);
+                ctx.setResponseBody(json.toJSONString());
+                return null;
+            }
+        } catch (Exception e) {
+            log.error("错误码转译失败,e{}", e);
+            ctx.setSendZuulResponse(true);
+            ctx.setResponseStatusCode(200);
+            ctx.setResponseBody("{\"code\":\"999\",\"message\":\"inner error code change out error code has exception\"}");// 输出最终结果
+        }
+        return null;
+    }
+
+}

+ 47 - 0
src/main/java/com/medipath/project/filter/ThrowExceptionFilter.java

@@ -0,0 +1,47 @@
+package com.medipath.project.filter;
+
+import com.netflix.zuul.ZuulFilter;
+import com.netflix.zuul.context.RequestContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.servlet.http.HttpServletResponse;
+
+//@Component
+public class ThrowExceptionFilter extends ZuulFilter  {
+
+    private static Logger log = LoggerFactory.getLogger(ThrowExceptionFilter.class);
+
+    @Override
+    public String filterType() {
+        return "pre";
+    }
+
+    @Override
+    public int filterOrder() {
+        return 0;
+    }
+
+    @Override
+    public boolean shouldFilter() {
+        return true;
+    }
+
+    @Override
+    public Object run() {
+        RequestContext ctx = RequestContext.getCurrentContext();
+        try {
+            doSomething();
+        } catch (Exception e) {
+            ctx.set("error.status_code", HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
+            ctx.set("error.exception", e);
+            ctx.set("error.message", "有一些错误发生");
+        }
+        return null;
+    }
+
+    private void doSomething() {
+//        throw new RuntimeException("Exist some errors...");
+    }
+
+}

+ 27 - 0
src/main/java/com/medipath/project/task/SchedulingConfigTask.java

@@ -0,0 +1,27 @@
+//package com.kameng.project.task;
+//
+//import org.slf4j.Logger;
+//import org.slf4j.LoggerFactory;
+//import org.springframework.context.annotation.Configuration;
+//import org.springframework.scheduling.annotation.EnableScheduling;
+//import org.springframework.scheduling.annotation.Scheduled;
+//
+///**
+// * 定时任务配置类
+// *
+// * @author 单红宇(365384722)
+// * @myblog http://blog.csdn.net/catoop/
+// * @create 2016年3月21日
+// */
+//@Configuration
+//@EnableScheduling // 启用定时任务
+//public class SchedulingConfigTask{
+//
+//    private final Logger logger = LoggerFactory.getLogger(getClass());
+//
+//    @Scheduled(cron = "0/20 * * * * ?") // 每20秒执行一次
+//    public void scheduler() {
+//        logger.info(">>>>>>>>>>>>> scheduled ... ");
+//    }
+//
+//}

+ 79 - 0
src/main/java/com/medipath/project/util/MD5Util.java

@@ -0,0 +1,79 @@
+package com.medipath.project.util;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.security.MessageDigest;
+
+
+/**
+ * message-digest algorithm 5(信息-摘要算法)
+ * 
+ * md5的长度,默认为128bit,也就是128个 0和1的 二进制串 。
+ * 
+ * 128/4 = 32 换成 16进制 表示后,为32位了。
+ */
+public class MD5Util {
+
+	static Logger logger = LoggerFactory.getLogger(MD5Util.class);
+
+	// 测试方法
+//	public static void main(String[] args) {
+////		String pwd = "123456";
+//		String pwd = "E10ADC3949BA59ABBE56E057F20F883E1111";
+//		System.out.println("加密前: " + pwd);
+//		System.err.println("加密后: " + MD5Util.getMD5(pwd));
+//	}
+
+	/**
+	 * 生成md5
+	 * 
+	 * @param message
+	 * @return
+	 */
+	public static String getMD5(String message) {
+		String md5str = "";
+		try {
+			// 1 创建一个提供信息摘要算法的对象,初始化为md5算法对象
+			MessageDigest md = MessageDigest.getInstance("MD5");
+
+			// 2 将消息变成byte数组
+			byte[] input = message.getBytes();
+
+			// 3 计算后获得字节数组,这就是那128位了
+			byte[] buff = md.digest(input);
+
+			// 4 把数组每一字节(一个字节占八位)换成16进制连成md5字符串
+			md5str = bytesToHex(buff);
+
+		} catch (Exception e) {
+			// e.printStackTrace();
+			logger.error("md5加密异常:{}", e);
+		}
+		return md5str;
+	}
+
+	/**
+	 * 二进制转十六进制
+	 * 
+	 * @param bytes
+	 * @return
+	 */
+	public static String bytesToHex(byte[] bytes) {
+		StringBuffer md5str = new StringBuffer();
+		// 把数组每一字节换成16进制连成md5字符串
+		int digital;
+		for (int i = 0; i < bytes.length; i++) {
+			digital = bytes[i];
+
+			if (digital < 0) {
+				digital += 256;
+			}
+			if (digital < 16) {
+				md5str.append("0");
+			}
+			md5str.append(Integer.toHexString(digital));
+		}
+		return md5str.toString().toUpperCase();
+	}
+}

+ 39 - 0
src/main/java/com/medipath/project/util/NoticeUtil.java

@@ -0,0 +1,39 @@
+package com.medipath.project.util;
+
+import java.util.HashMap;
+import java.util.Iterator;
+
+import org.apache.commons.lang.StringUtils;
+
+/**
+ * 消息通知工具类
+ *
+ * @author chenwj
+ * @version $Id: NoticeUtil.java, v 0.1 2017年10月12日 下午1:55:20 Exp $
+ */
+public class NoticeUtil {
+
+	/**
+	 * 根据properties替换模版中${变量}生成内容
+	 *
+	 * @param properties
+	 *            模版中需要的变量
+	 * @param content
+	 *            生成的内容
+	 * @return
+	 */
+	public static String getTempletContent(HashMap<String, Object> properties, String content) {
+		if (StringUtils.isBlank(content)) {
+			return "";
+		}
+		String contents = content;
+		// 替换参数
+		for (Iterator<String> iter = properties.keySet().iterator(); iter.hasNext();) {
+			String key = (String) iter.next();
+			Object value = properties.get(key);
+			contents = StringUtils.replace(contents, "${" + key + "}", String.valueOf(value));
+			contents = StringUtils.replace(contents, ":" + key, String.valueOf(value));
+		}
+		return contents;
+	}
+}

+ 21 - 0
src/main/java/com/medipath/project/util/StringUtil.java

@@ -0,0 +1,21 @@
+package com.medipath.project.util;
+
+public class StringUtil {
+
+	
+	//获取指定位数的随机字符串(包含小写字母、大写字母、数字,0<length)
+	public static String getRandomString(int length) {
+	    //随机字符串的随机字符库
+	    String KeyString = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
+	    StringBuffer sb = new StringBuffer();
+	    int len = KeyString.length();
+	    for (int i = 0; i < length; i++) {
+	       sb.append(KeyString.charAt((int) Math.round(Math.random() * (len - 1))));
+	    }
+	    return sb.toString();
+	}
+	
+//	public static void main(String[] args) {
+//		System.out.println(getRandomString(4));
+//	}
+}

+ 83 - 0
src/main/resources/bootstrap.properties

@@ -0,0 +1,83 @@
+spring.application.name=gateway
+server.port=8110
+
+signature=false
+login.check=false
+pub.secret=idhsoihpdih
+
+spring.profiles.active=dev
+local.nacos=127.0.0.1:8848
+dev.nacos=172.17.0.3:8848
+test.nacos=172.10.100.40:8848
+pro.nacos=172.10.100.10:30308
+spring.cloud.nacos.discovery.server-addr=${${spring.profiles.active}.nacos}
+spring.cloud.nacos.config.server-addr=${${spring.profiles.active}.nacos}
+spring.cloud.nacos.config.prefix=gateway
+spring.cloud.nacos.config.file-extension=properties
+spring.cloud.nacos.config.ext-config[0].data-id=mq-${spring.profiles.active}.properties
+spring.cloud.nacos.config.ext-config[0].refresh=true
+spring.cloud.nacos.config.ext-config[1].data-id=redis-${spring.profiles.active}.properties
+spring.cloud.nacos.config.ext-config[1].refresh=true
+dubbo.protocol.serialization=kryo
+dubbo.protocol.optimizer=
+dubbo.protocol.name=dubbo
+dubbo.protocol.port=-1
+dubbo.registry.file=/var/logs/${spring.application.name}/dubbo_${server.port}.cache
+
+dubbo.registry.address=nacos://${${spring.profiles.active}.nacos}
+
+dubbo.cloud.subscribed-services=cif,operating
+dubbo.application.qos.enable=true
+dubbo.application.qos.port=28110
+dubbo.application.qos.accept.foreign.ip=true
+dubbo.reference.com.foo.BarService.check=false
+dubbo.reference.check=false
+dubbo.consumer.check=false
+dubbo.registry.check=false
+spring.mvc.throw-exception-if-no-handler-found=true
+spring.resources.add-mappings=true
+multipart.maxFileSize=50Mb
+server.undertow.max-http-post-size=83886080
+spring.servlet.multipart.max-file-size=100MB
+spring.servlet.multipart.max-request-size=1000MB
+dubbo.protocol.dubbo.payload=83886080
+
+hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=30000
+#hystrix.command.default.circuitBreaker.sleepWindowInMilliseconds=100000
+#hystrix.command.default.circuitBreaker.forceClosed=true
+#hystrix.shareSecurityContext=true
+
+zuul.ignored-services='*'
+zuul.sensitive-headers=Access-Control-Allow-Origin
+zuul.ignored-headers=Access-Control-Allow-Origin,H-APP-Id,accessToken
+
+
+#zuul.routes.api-a.path=/cif/application/**
+#zuul.routes.api-a.service-id=apia
+#zuul.routes.api-a.serviceId=apia
+# 映射具体的url路径
+#zuul.routes.api-a.url=http://192.168.50.98:9031/application/
+
+#apia.ribbon.listOfServers=http://192.168.50.98:9031/application/,http://192.168.50.98:9030/application/
+
+
+hystrix.command.default.circuitBreaker.requestVolumeThreshold=20
+hystrix.command.default.circuitBreaker.errorThresholdPercentage=50%
+hystrix.command.default.circuitBreaker.sleepWindowInMilliseconds=500
+#zuul.ignore-security-headers=false
+zuul.retryable=true
+ribbon.ConnectTimeout=500
+ribbon.ReadTimeout=5000
+ribbon.MaxAutoRetries=1
+ribbon.MaxAutoRetriesNextServer=1
+#\u8BBE\u7F6E\u5168\u90E8\u8DEF\u7531\u6700\u5927\u4FE1\u53F7\u91CF
+zuul.semaphore.max-semaphores=5000
+player.switch=off
+swagger.resource=cif,operating,backstage,report
+
+cif.ribbon.NFLoadBalancerRuleClassName=com.netflix.loadbalancer.BestAvailableRule
+spring.security.oauth2.jwt.signingKey=123456
+
+
+
+

+ 81 - 0
src/main/resources/logback.xml

@@ -0,0 +1,81 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<configuration debug="false">
+    <!--定义日志文件的存储地址 勿在 LogBack 的配置中使用相对路径-->
+    <property name="LOG_HOME" value="/var/logs/gateway" />
+    <!-- 控制台输出 -->
+    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
+        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
+            <!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符-->
+            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
+        </encoder>
+    </appender>
+    <!-- 按照每天生成日志文件 -->
+    <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
+        <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
+            <!-- rollover daily -->
+            <fileNamePattern>${LOG_HOME}/log.gateway.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
+            <!-- each file should be at most 100MB, keep 60 days worth of history, but at most 20GB -->
+            <maxFileSize>2GB</maxFileSize>
+            <maxHistory>2</maxHistory>
+        </rollingPolicy>
+        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
+            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
+        </encoder>
+        <filter class="ch.qos.logback.classic.filter.LevelFilter"><!-- 只打印错误日志 -->
+            <level>INFO</level>
+            <onMatch>ACCEPT</onMatch>
+            <onMismatch>DENY</onMismatch>
+        </filter>
+    </appender>
+
+
+        <!-- 按照每天生成日志文件 -->
+    <appender name="ERROR_FILE"  class="ch.qos.logback.core.rolling.RollingFileAppender">
+        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+            <FileNamePattern>${LOG_HOME}/error.gateway.log.%d{yyyy-MM-dd}.log</FileNamePattern>
+            <MaxHistory>5</MaxHistory>
+        </rollingPolicy>
+        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
+            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
+        </encoder>
+        <filter class="ch.qos.logback.classic.filter.LevelFilter"><!-- 只打印错误日志 -->
+	        <level>ERROR</level>
+	        <onMatch>ACCEPT</onMatch>
+	        <onMismatch>DENY</onMismatch>
+	    </filter>
+    </appender>
+    <!-- show parameters for hibernate sql 专为 Hibernate 定制 -->
+<!--     <logger name="org.hibernate.type.descriptor.sql.BasicBinder"  level="TRACE" /> -->
+<!--     <logger name="org.hibernate.type.descriptor.sql.BasicExtractor"  level="DEBUG" /> -->
+<!--     <logger name="org.hibernate.SQL" level="DEBUG" /> -->
+<!--     <logger name="org.hibernate.engine.QueryParameters" level="DEBUG" /> -->
+<!--     <logger name="org.hibernate.engine.query.HQLQueryPlan" level="DEBUG" /> -->
+
+    <!--myibatis log configure-->
+    <logger name="com.apache.ibatis" level="TRACE"/>
+    <logger name="java.sql.Connection" level="DEBUG"/>
+    <logger name="java.sql.Statement" level="DEBUG"/>
+    <logger name="java.sql.PreparedStatement" level="DEBUG"/>
+	<logger name="com.kameng.project" />
+
+    <!-- 日志输出级别 -->
+    <root level="INFO">
+        <appender-ref ref="STDOUT" />
+        <appender-ref ref="FILE" />
+        <appender-ref ref="ERROR_FILE" />
+    </root>
+
+    <!--日志异步到数据库 -->
+    <!--<appender name="DB" class="ch.qos.logback.classic.db.DBAppender">-->
+        <!--&lt;!&ndash;日志异步到数据库 &ndash;&gt;-->
+        <!--<connectionSource class="ch.qos.logback.core.db.DriverManagerConnectionSource">-->
+            <!--&lt;!&ndash;连接池 &ndash;&gt;-->
+            <!--<dataSource class="com.mchange.v2.c3p0.ComboPooledDataSource">-->
+                <!--<driverClass>com.mysql.jdbc.Driver</driverClass>-->
+                <!--<url>jdbc:mysql://127.0.0.1:3306/databaseName</url>-->
+                <!--<user>root</user>-->
+                <!--<password>root</password>-->
+            <!--</dataSource>-->
+        <!--</connectionSource>-->
+    <!--</appender>-->
+</configuration>

+ 27 - 0
src/main/resources/mapper/InterfaceMapper.xml

@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.kameng.project.dao.InterfaceMapper">
+  <resultMap id="BaseResultMap" type="com.kameng.project.model.Interface">
+    <!--
+      WARNING - @mbg.generated
+    -->
+    <id column="id" jdbcType="INTEGER" property="id" />
+    <result column="api_name" jdbcType="VARCHAR" property="apiName" />
+    <result column="api_remark" jdbcType="VARCHAR" property="apiRemark" />
+    <result column="api_memo" jdbcType="VARCHAR" property="apiMemo" />
+    <result column="service_name" jdbcType="VARCHAR" property="serviceName" />
+    <result column="service_path" jdbcType="VARCHAR" property="servicePath" />
+    <result column="connection_timeout" jdbcType="INTEGER" property="connectionTimeout" />
+    <result column="so_timeout" jdbcType="INTEGER" property="soTimeout" />
+    <result column="module_id" jdbcType="VARCHAR" property="moduleId" />
+    <result column="module_name" jdbcType="VARCHAR" property="moduleName" />
+    <result column="authorize_type" jdbcType="VARCHAR" property="authorizeType" />
+    <result column="project_name" jdbcType="VARCHAR" property="projectName" />
+    <result column="status" jdbcType="VARCHAR" property="status" />
+    <result column="service_type" jdbcType="VARCHAR" property="serviceType" />
+    <result column="create_user_id" jdbcType="VARCHAR" property="createUserId" />
+    <result column="update_user_id" jdbcType="VARCHAR" property="updateUserId" />
+    <result column="create_time" jdbcType="TIMESTAMP" property="createTime" />
+    <result column="update_time" jdbcType="TIMESTAMP" property="updateTime" />
+  </resultMap>
+</mapper>

+ 11 - 0
src/main/resources/templates/index.html

@@ -0,0 +1,11 @@
+<!DOCTYPE HTML>
+<html>
+<head lang="en">
+  <meta charset="UTF-8" />
+  <title></title>
+</head>
+<body>
+
+1111111111111111
+<h1 th:text="${host}"></h1>
+</body>

+ 46 - 0
src/test/java/com/conpany/project/TestRedis.java

@@ -0,0 +1,46 @@
+//package com.conpany.project;
+//
+//import java.util.concurrent.TimeUnit;
+//
+//import javax.annotation.Resource;
+//
+//import org.junit.Assert;
+//import org.junit.Test;
+//import org.springframework.data.redis.core.RedisTemplate;
+//import org.springframework.data.redis.core.StringRedisTemplate;
+//import org.springframework.data.redis.core.ValueOperations;
+//
+//
+//public class TestRedis extends Tester{
+//
+//	@Resource
+//    private StringRedisTemplate stringRedisTemplate;
+//    
+//    @Resource
+//    private RedisTemplate redisTemplate;
+//
+//    @Test
+//    public void test() throws Exception {
+//        stringRedisTemplate.opsForValue().set("aaa", "111");
+//        Assert.assertEquals("111", stringRedisTemplate.opsForValue().get("aaa"));
+//    }
+//    
+////    @Test
+////    public void testObj() throws Exception {
+////        User user=new User();
+////        user.setName("222");
+////        ValueOperations<String, User> operations= redisTemplate.opsForValue();
+////        operations.set("com.neox", user);
+////        operations.set("com.neo.f", user,30000,TimeUnit.SECONDS);
+////        Thread.sleep(1000);
+////        //redisTemplate.delete("com.neo.f");
+////        boolean exists=redisTemplate.hasKey("com.neo.f");
+////        if(exists){
+////            System.out.println("exists is true");
+////        }else{
+////            System.out.println("exists is false");
+////        }
+////        System.out.println(operations.get("com.neo.f").getName());
+////        Assert.assertEquals("aa", operations.get("com.neo.f").getName());
+////    }
+//}

+ 23 - 0
src/test/java/com/conpany/project/Tester.java

@@ -0,0 +1,23 @@
+package com.conpany.project;
+
+
+
+import org.junit.runner.RunWith;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.annotation.Rollback;
+import org.springframework.test.context.junit4.SpringRunner;
+import org.springframework.transaction.annotation.Transactional;
+
+import com.medipath.project.GatewayApplication;
+
+/**
+ * 单元测试继承该类即可
+ */
+@RunWith(SpringRunner.class)
+@SpringBootTest(classes = GatewayApplication.class)
+@Transactional
+@Rollback
+public abstract class Tester {}
+
+
+