Browse Source

"项目构建"

yu_u66 2 months ago
commit
0a63ff3a8c
39 changed files with 5586 additions and 0 deletions
  1. 33 0
      .gitignore
  2. 144 0
      pom.xml
  3. 13 0
      src/main/java/com/futu/course/GooseApplication.java
  4. 11 0
      src/main/java/com/futu/course/common/annotation/CurrentReq.java
  5. 11 0
      src/main/java/com/futu/course/common/annotation/CurrentUser.java
  6. 11 0
      src/main/java/com/futu/course/common/annotation/IgnoreSign.java
  7. 11 0
      src/main/java/com/futu/course/common/annotation/LoginRequired.java
  8. 11 0
      src/main/java/com/futu/course/common/annotation/NonLoginRequired.java
  9. 18 0
      src/main/java/com/futu/course/common/config/CorsConfig.java
  10. 217 0
      src/main/java/com/futu/course/common/config/IntercepterConfig.java
  11. 51 0
      src/main/java/com/futu/course/common/config/RedisConfig.java
  12. 301 0
      src/main/java/com/futu/course/common/constant/BestConstants.java
  13. 152 0
      src/main/java/com/futu/course/common/constant/Constants.java
  14. 52 0
      src/main/java/com/futu/course/common/constant/RedisKeyConst.java
  15. 52 0
      src/main/java/com/futu/course/common/entity/ApiErrorCode.java
  16. 42 0
      src/main/java/com/futu/course/common/entity/HttpResult.java
  17. 20 0
      src/main/java/com/futu/course/common/entity/IErrorCode.java
  18. 92 0
      src/main/java/com/futu/course/common/entity/R.java
  19. 44 0
      src/main/java/com/futu/course/common/exception/ApiException.java
  20. 106 0
      src/main/java/com/futu/course/common/exception/GlobalExceptionHandler.java
  21. 53 0
      src/main/java/com/futu/course/common/exception/RRException.java
  22. 25 0
      src/main/java/com/futu/course/common/exception/UnBindPhoneException.java
  23. 31 0
      src/main/java/com/futu/course/common/handle/CurrentUserHandler.java
  24. 58 0
      src/main/java/com/futu/course/common/handle/StringListTypeHandler.java
  25. 108 0
      src/main/java/com/futu/course/common/intercepter/AuthIntercepter.java
  26. 69 0
      src/main/java/com/futu/course/common/utils/BestUtil.java
  27. 152 0
      src/main/java/com/futu/course/common/utils/CacheKey.java
  28. 120 0
      src/main/java/com/futu/course/common/utils/Constant.java
  29. 164 0
      src/main/java/com/futu/course/common/utils/EscapeUtil.java
  30. 565 0
      src/main/java/com/futu/course/common/utils/HTMLFilter.java
  31. 56 0
      src/main/java/com/futu/course/common/utils/HttpHelper.java
  32. 622 0
      src/main/java/com/futu/course/common/utils/HttpUtil.java
  33. 110 0
      src/main/java/com/futu/course/common/utils/PageUtils.java
  34. 287 0
      src/main/java/com/futu/course/common/utils/RedisCache.java
  35. 1076 0
      src/main/java/com/futu/course/common/utils/RedisUtil.java
  36. 558 0
      src/main/java/com/futu/course/common/utils/StringUtils.java
  37. 113 0
      src/main/java/com/futu/course/common/utils/TokenUtils.java
  38. 14 0
      src/main/resources/application.yml
  39. 13 0
      src/test/java/com/futu/course/GooseApplicationTests.java

+ 33 - 0
.gitignore

@@ -0,0 +1,33 @@
+HELP.md
+target/
+!.mvn/wrapper/maven-wrapper.jar
+!**/src/main/**/target/
+!**/src/test/**/target/
+
+### STS ###
+.apt_generated
+.classpath
+.factorypath
+.project
+.settings
+.springBeans
+.sts4-cache
+
+### IntelliJ IDEA ###
+.idea
+*.iws
+*.iml
+*.ipr
+
+### NetBeans ###
+/nbproject/private/
+/nbbuild/
+/dist/
+/nbdist/
+/.nb-gradle/
+build/
+!**/src/main/**/build/
+!**/src/test/**/build/
+
+### VS Code ###
+.vscode/

+ 144 - 0
pom.xml

@@ -0,0 +1,144 @@
+<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>com.futu.course</groupId>
+    <artifactId>goose</artifactId>
+    <version>0.0.1-SNAPSHOT</version>
+    <name>goose</name>
+    <description>goose</description>
+    <properties>
+        <java.version>1.8</java.version>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
+        <spring-boot.version>2.7.6</spring-boot.version>
+    </properties>
+    <dependencies>
+        <dependency>
+            <groupId>com.alibaba</groupId>
+            <artifactId>fastjson</artifactId>
+            <version>1.2.80</version>
+        </dependency>
+        <dependency>
+            <groupId>io.jsonwebtoken</groupId>
+            <artifactId>jjwt-api</artifactId>
+            <version>0.11.5</version>
+        </dependency>
+        <dependency>
+            <groupId>io.jsonwebtoken</groupId>
+            <artifactId>jjwt-impl</artifactId>
+            <version>0.11.5</version>
+            <scope>runtime</scope>
+        </dependency>
+        <dependency>
+            <groupId>io.jsonwebtoken</groupId>
+            <artifactId>jjwt-jackson</artifactId>
+            <version>0.11.5</version>
+            <scope>runtime</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>commons-io</groupId>
+            <artifactId>commons-io</artifactId>
+            <version>2.11.0</version>
+        </dependency>
+        <dependency>
+            <groupId>javax.validation</groupId>
+            <artifactId>validation-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.commons</groupId>
+            <artifactId>commons-lang3</artifactId>
+            <version>3.3.2</version>
+        </dependency>
+        <dependency>
+            <groupId>commons-httpclient</groupId>
+            <artifactId>commons-httpclient</artifactId>
+            <version>3.1</version>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-web</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-data-redis</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.mybatis.spring.boot</groupId>
+            <artifactId>mybatis-spring-boot-starter</artifactId>
+            <version>2.3.0</version>
+        </dependency>
+        <dependency>
+            <groupId>com.baomidou</groupId>
+            <artifactId>mybatis-plus-boot-starter</artifactId>
+            <version>3.5.4</version>
+        </dependency>
+        <dependency>
+            <groupId>cn.hutool</groupId>
+            <artifactId>hutool-all</artifactId>
+            <version>5.8.16</version>
+        </dependency>
+
+        <dependency>
+            <groupId>com.mysql</groupId>
+            <artifactId>mysql-connector-j</artifactId>
+            <scope>runtime</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.projectlombok</groupId>
+            <artifactId>lombok</artifactId>
+            <optional>true</optional>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-test</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+    <dependencyManagement>
+        <dependencies>
+            <dependency>
+                <groupId>org.springframework.boot</groupId>
+                <artifactId>spring-boot-dependencies</artifactId>
+                <version>${spring-boot.version}</version>
+                <type>pom</type>
+                <scope>import</scope>
+            </dependency>
+        </dependencies>
+    </dependencyManagement>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+                <version>3.8.1</version>
+                <configuration>
+                    <source>1.8</source>
+                    <target>1.8</target>
+                    <encoding>UTF-8</encoding>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.springframework.boot</groupId>
+                <artifactId>spring-boot-maven-plugin</artifactId>
+                <version>${spring-boot.version}</version>
+                <configuration>
+                    <mainClass>com.futu.course.GooseApplication</mainClass>
+                    <skip>true</skip>
+                </configuration>
+                <executions>
+                    <execution>
+                        <id>repackage</id>
+                        <goals>
+                            <goal>repackage</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>

+ 13 - 0
src/main/java/com/futu/course/GooseApplication.java

@@ -0,0 +1,13 @@
+package com.futu.course;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication
+public class GooseApplication {
+
+    public static void main(String[] args) {
+        SpringApplication.run(GooseApplication.class, args);
+    }
+
+}

+ 11 - 0
src/main/java/com/futu/course/common/annotation/CurrentReq.java

@@ -0,0 +1,11 @@
+package com.futu.course.common.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.PARAMETER)
+public @interface CurrentReq {
+}

+ 11 - 0
src/main/java/com/futu/course/common/annotation/CurrentUser.java

@@ -0,0 +1,11 @@
+package com.futu.course.common.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.PARAMETER)
+public @interface CurrentUser {
+}

+ 11 - 0
src/main/java/com/futu/course/common/annotation/IgnoreSign.java

@@ -0,0 +1,11 @@
+package com.futu.course.common.annotation;
+
+
+
+/**
+ * 需要忽略参与计算的签名计算的属性
+ *
+ */
+public @interface IgnoreSign {
+
+}

+ 11 - 0
src/main/java/com/futu/course/common/annotation/LoginRequired.java

@@ -0,0 +1,11 @@
+package com.futu.course.common.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.METHOD)
+public @interface LoginRequired {
+}

+ 11 - 0
src/main/java/com/futu/course/common/annotation/NonLoginRequired.java

@@ -0,0 +1,11 @@
+package com.futu.course.common.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.METHOD)
+public @interface NonLoginRequired {
+}

+ 18 - 0
src/main/java/com/futu/course/common/config/CorsConfig.java

@@ -0,0 +1,18 @@
+package com.futu.course.common.config;
+
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.servlet.config.annotation.CorsRegistry;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
+
+@Configuration
+public class CorsConfig implements WebMvcConfigurer {
+
+    @Override
+    public void addCorsMappings(CorsRegistry registry) {
+        registry.addMapping("/**")
+            .allowedOrigins("*")
+            .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")
+            .maxAge(3600);
+    }
+
+}

+ 217 - 0
src/main/java/com/futu/course/common/config/IntercepterConfig.java

@@ -0,0 +1,217 @@
+package com.futu.course.common.config;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.databind.DeserializationFeature;
+import com.fasterxml.jackson.databind.JsonSerializer;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.SerializerProvider;
+
+import com.futu.course.common.handle.CurrentUserHandler;
+import com.futu.course.common.intercepter.AuthIntercepter;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Primary;
+import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
+import org.springframework.web.filter.HttpPutFormContentFilter;
+import org.springframework.web.method.support.HandlerMethodArgumentResolver;
+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.WebMvcConfigurationSupport;
+
+import java.io.IOException;
+import java.util.List;
+
+@Configuration
+@Slf4j
+//@EnableWebMvc
+public class IntercepterConfig extends WebMvcConfigurationSupport {
+
+    @Override
+    public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
+        log.info("IntercepterConfig--------------------");
+        resolvers.add(new CurrentUserHandler());
+    }
+    @Autowired
+    AuthIntercepter authIntercepter;
+    @Autowired
+    protected RedisTemplate redisTemplate;
+
+    @Override
+    public void addInterceptors(InterceptorRegistry registry) {
+        // 拦截所有请求,通过判断是否有 @LoginRequired 注解 决定是否需要登录
+        registry.addInterceptor(authenticationInterceptor()).excludePathPatterns("**.html").excludePathPatterns("**.txt")
+                .excludePathPatterns("/webjars/**")
+                .excludePathPatterns("/api/goods-detail")
+                .excludePathPatterns("/api/home")
+                .excludePathPatterns("/api/cate")
+                .excludePathPatterns("/api/goods")
+                .excludePathPatterns("/api/boss/home")
+                .excludePathPatterns("/api/boss/team-detail")
+                .excludePathPatterns("/api/boss/home-detail")
+                .excludePathPatterns("/api/boss/supplier")
+                .excludePathPatterns("/api/boss/lottery")
+                .excludePathPatterns("/api/boss/lottery-detail")
+                .excludePathPatterns("/api/team-buy")
+                .excludePathPatterns("/api/cart")
+                .excludePathPatterns("/api/order")
+                .excludePathPatterns("/api/keywords")
+                .excludePathPatterns("/api/share-detail")
+                .excludePathPatterns("/dashboard/list")
+                .excludePathPatterns("/swagger-ui.html")
+                .excludePathPatterns("/swagger-resources/**").excludePathPatterns("/error").addPathPatterns("/**");
+        super.addInterceptors(registry);
+    }
+
+
+    @Override
+    public void addResourceHandlers(ResourceHandlerRegistry registry) {
+        super.addResourceHandlers(registry);
+        registry.addResourceHandler("/**")
+                .addResourceLocations("classpath:/static/")
+                .addResourceLocations("classpath:/templates/")
+                .addResourceLocations("classpath:/resources/");
+//        registry.addResourceHandler("/static/**").addResourceLocations("classpath:/static/");
+//        registry.addResourceHandler("swagger-ui.html")
+//                .addResourceLocations("classpath:/resources/");
+//        registry.addResourceHandler("resources/**.html")
+//                .addResourceLocations("classpath:/META-INF/resources/");
+//
+//        registry.addResourceHandler("doc.html")
+//                .addResourceLocations("classpath:resources/");
+//
+//        registry.addResourceHandler("/webjars/**")
+//                .addResourceLocations("classpath:resources/webjars/");
+        registry.addResourceHandler("swagger-ui.html")
+                .addResourceLocations("classpath:/META-INF/resources/");
+        registry.addResourceHandler("/webjars/**")
+                .addResourceLocations("classpath:/META-INF/resources/webjars/");
+        registry.addResourceHandler("SFLMPwcUTv.txt")
+                .addResourceLocations("classpath:/resources/");
+
+    }
+    @Override
+    public void addCorsMappings(CorsRegistry registry) {
+        super.addCorsMappings(registry);
+        registry
+                .addMapping("/**")
+                .allowedHeaders("*")
+                .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")
+                .allowedOrigins("*");
+    }
+
+    @Bean
+    public AuthIntercepter authenticationInterceptor() {
+        return new AuthIntercepter(redisTemplate);
+    }
+
+//    @Bean
+//    public CurrentUserMethodArgumentResolver currentUserMethodArgumentResolver() {
+//        return new CurrentUserMethodArgumentResolver();
+//    }
+//    @Override
+////    public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
+////        argumentResolvers.add(currentUserMethodArgumentResolver());
+////        super.addArgumentResolvers(argumentResolvers);
+////    }
+
+    /**
+     * @Description 解决使用put请求   服务器接收不到参数
+     * @author DengKaiTao
+     * @date 2018/12/4 16:35
+     * @return org.springframework.web.filter.HttpPutFormContentFilter
+     * @version v1.0
+     **/
+    @Bean
+    public HttpPutFormContentFilter httpPutFormContentFilter() {
+        return new HttpPutFormContentFilter();
+    }
+
+//    @Override
+//    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
+//
+////        FastJsonHttpMessageConverter converter = new FastJsonHttpMessageConverter();
+////        FastJsonConfig config = new FastJsonConfig();
+////        config.setSerializerFeatures(
+////                //保留map空的字段
+////                SerializerFeature.WriteMapNullValue,
+////                // 将String类型的NULL转化为""
+////                SerializerFeature.WriteNullStringAsEmpty,
+////                // 将Number类型的NULL转化为0
+////                SerializerFeature.WriteNullNumberAsZero,
+////                // 将List类型的NULL转成[]
+////                SerializerFeature.WriteNullListAsEmpty,
+////                // 将Boolean类型的NULL转化为false
+////                SerializerFeature.WriteNullBooleanAsFalse,
+////                SerializerFeature.PrettyFormat,
+////                // 避免循环引用
+////                SerializerFeature.DisableCircularReferenceDetect);
+////
+////        converter.setFastJsonConfig(config);
+////        converter.setDefaultCharset(Charset.forName("UTF-8"));
+////
+////        List<MediaType> mediaTypeList = new ArrayList<>();
+////        // 解决中文乱码问题,相当于在Controller上的@RequestMapping中加了个属性produces = "application/json"
+////        mediaTypeList.add(MediaType.APPLICATION_JSON);
+////        converter.setSupportedMediaTypes(mediaTypeList);
+////        converters.add(converter);
+//        //定义Json转换器
+//        MappingJackson2HttpMessageConverter jackson2HttpMessageConverter = new MappingJackson2HttpMessageConverter();
+//        ObjectMapper objectMapper = new ObjectMapper();
+//        //定义对象模型
+//        SimpleModule simpleModule = new SimpleModule();
+//        //添加对长整型的转换关系
+//        simpleModule.addSerializer(BigInteger.class, ToStringSerializer.instance);
+////        simpleModule.addSerializer(Long.class, ToStringSerializer.instance);
+////        simpleModule.addSerializer(Long.TYPE, ToStringSerializer.instance);
+////        simpleModule.addSerializer(Date.class,ToStringSerializer.instance);
+//        //将对象模型添加至对象映射器
+//        objectMapper.registerModule(simpleModule);
+//        jackson2HttpMessageConverter.setObjectMapper(objectMapper);
+//        //在转换器列表中添加自定义的Json转换器
+//        converters.add(jackson2HttpMessageConverter);
+//        //添加utf-8的默认String转换器
+//        converters.add(new StringHttpMessageConverter(Charset.forName("UTF-8")));
+//    }
+
+    @Bean
+    @Primary
+    @ConditionalOnMissingBean(ObjectMapper.class)
+    public ObjectMapper jacksonObjectMapper(Jackson2ObjectMapperBuilder builder)
+    {
+        ObjectMapper objectMapper = builder.createXmlMapper(false).build();
+
+        // 通过该方法对mapper对象进行设置,所有序列化的对象都将按改规则进行系列化
+        // Include.Include.ALWAYS 默认
+        // Include.NON_DEFAULT 属性为默认值不序列化
+        // Include.NON_EMPTY 属性为 空("") 或者为 NULL 都不序列化,则返回的json是没有这个字段的。这样对移动端会更省流量
+        // Include.NON_NULL 属性为NULL 不序列化
+        objectMapper.setSerializationInclusion(JsonInclude.Include.NON_EMPTY);
+        objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
+        // 允许出现特殊字符和转义符
+        objectMapper.configure(JsonParser.Feature.ALLOW_UNQUOTED_CONTROL_CHARS, true);
+        // 允许出现单引号
+        objectMapper.configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true);
+        // 字段保留,将null值转为""
+        objectMapper.getSerializerProvider().setNullValueSerializer(new JsonSerializer<Object>()
+        {
+            @Override
+            public void serialize(Object o, JsonGenerator jsonGenerator,
+                                  SerializerProvider serializerProvider)
+                    throws IOException
+            {
+                jsonGenerator.writeString("");
+            }
+        });
+        return objectMapper;
+    }
+
+
+
+}

+ 51 - 0
src/main/java/com/futu/course/common/config/RedisConfig.java

@@ -0,0 +1,51 @@
+package com.futu.course.common.config;
+
+import com.fasterxml.jackson.annotation.JsonAutoDetect;
+import com.fasterxml.jackson.annotation.PropertyAccessor;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.data.redis.connection.RedisConnectionFactory;
+import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
+import org.springframework.data.redis.serializer.StringRedisSerializer;
+
+
+@Configuration
+public class RedisConfig {
+
+
+    @Bean
+    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
+
+        RedisTemplate<String, Object> template = new RedisTemplate<>();
+
+        template.setConnectionFactory(factory);
+
+
+        Jackson2JsonRedisSerializer<Object> serializer = new Jackson2JsonRedisSerializer<>(Object.class);
+
+        ObjectMapper om = new ObjectMapper();
+
+        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
+
+        om.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL);
+
+        serializer.setObjectMapper(om);
+
+
+        template.setKeySerializer(new StringRedisSerializer());
+
+        template.setHashKeySerializer(new StringRedisSerializer());
+
+        template.setValueSerializer(serializer);
+
+        template.setHashValueSerializer(serializer);
+
+        template.afterPropertiesSet();
+
+
+        return template;
+    }
+}

+ 301 - 0
src/main/java/com/futu/course/common/constant/BestConstants.java

@@ -0,0 +1,301 @@
+package com.futu.course.common.constant;
+
+/**
+ * @className: BestConstants
+ * @Description:
+ * @author: zhangxiong
+ * @date: 2022/4/23/0023 20:58
+ */
+public class BestConstants {
+    public static final Integer BASE_SHOW = 1;
+    public static final Integer BASE_FALSE = 0;
+    public static final int MAX_QUERY_SIZE = 10;
+    public static final int MIN_QUERY_CURRENT = 1;
+    public static final String OVER_QUERY_SIZE = "超出最大查询长度";
+    //销售规格
+    public static final Integer ITEM_TYPE_SPECIFICATIONS = 1;
+    //基础属性
+    public static final Integer ITEM_TYPE_PROPERTIES = 2;
+
+    public static final String BTB_MINI_CAROUSEL = "btb_mini_carousel";
+    public static final String BTB_MINI_ADVERTS = "btb_mini_adverts";
+    public static final String BTB_MINI_NAVIGATION = "btb_mini_navigation";
+    public static final String BTB_TEAMBUY = "btb_teambuy";
+    public static final String BTB_CONFIG_FOOTER = "btb_config_footer";
+    public static final String BTB_MINI_CONFIG = "btb_mini_config";
+
+    public static final String GOODS_NOT_FOUND = "商品已下架!";
+    public static final String TB_GOODS_NOT_FOUND = "团购商品不存在!";
+    public static final String CART_ITEM_ERROR_MSG = "购物车不存在!";
+    public static final String ORDER_NOT_FOUND = "订单不存在!";
+    public static final String NUM_ERROR_MSG = "数量异常!";
+    public static final String TYPE_ERROR_MSG = "类型异常!";
+    public static final String FILE_UPLOAD_ERROR = "文件上传异常!";
+    public static final String DATA_NOT_FOUND_MSG = "数据未找到!";
+    public static final String CURRENT_ADDRESS_CANT = "当前区域无法购买!";
+    //立即购买
+    public static final Integer PACKAGE_TYPE_NOW = 1;
+    //购物车购买
+    public static final Integer PACKAGE_TYPE_CART = 2;
+    public static final String PACKAGE_TYPE_ERROR = "状态异常!";
+    //删除购物车
+    public static final Integer REMOVE_CART = 1;
+    //删除购物车明细
+    public static final Integer REMOVE_CART_ITEM = 2;
+    //编辑购物车数量
+    public static final Integer MODIFY_CART_ITEM = 3;
+    //正常购买
+    public static final Integer TYPE_NORMAL_BUY = 1;
+    //团购类型
+    public static final Integer TYPE_BUY_TEAM = 2;
+    //狐狸金抵用
+    public static final Integer TYPE_FOX_DEDUCT = 1;
+    //自定义返回码
+    public static final Integer OTHER_RESULT_CODE = 2;
+    public static final String OTHER_RESULT_CODE_MSG = "商品已抢光!";
+    public static final String STOCK_OVERFLOW = "超过商品库存!";
+    public static final String TEAMBUY_OVERTIME_MSG = "团购已经过期!";
+    public static final String TEAMBUY_OVER_MSG = "团购已经售罄!";
+    public static final String FOX_MONEY_INSUFFICIENT_MSG = "狐狸金余额不足!";
+    public static final String FOX_MONEY_ERROR_MSG = "狐狸金抵扣异常!";
+    public static final String FOX_MONEY_OVERSTEP_MSG = "狐狸金抵扣超出商品金额!";
+    public static final String COUPON_OVER_MSG = "优惠券已过期!";
+    public static final String INSIDE_ERROR_MSG = "购物金抵扣异常!";
+    public static final String INSIDE_ERROR_SHOPPING_NUM_MSG = "内购会一次只能购买一件商品!";
+    public static final String INSIDE_OVER_DOWN_MSG = "内购会不存在或已过期!";
+    public static final String INSIDE_NUM_DOWN_MSG = "内购会商品下架或者不允许使用!";
+    public static final String INSIDE_NUM_OVER_MSG = "购物金使用超出限制数量!";
+    public static final String INSIDE_USER_USER_MSG = "购物金已被使用!";
+    public static final String INSIDE_SHOPPING_DOWN_MSG = "购物金已暂停使用!";
+    public static final String SHOPPING_QRCODE_NOT_FOUND = "购物金卡不存在!";
+    public static final String SHOPPING_QRCODE_HAS_RECEIVE = "购物金卡已被领取!";
+    public static final String SHOPPING_USER_ERROR_MSG ="购物金抵用异常!";
+    public static final String INSID_SHOPPING_FUCK_ERROR = "购物金限制使用配置异常!";
+    public static final String COUPON_NOT_FOUND_MSG = "优惠券不存在!";
+    public static final String COUPON_USED_ERROR_MSG = "优惠券已被使用!";
+    public static final String COUPON_TEBUY_ERROR_MSG = "优惠券专场不匹配!";
+    public static final String COUPON_GOOD_ERROR_MSG = "优惠券商品不匹配!";
+    public static final String COUPON_LIMIT_ERROR_MSG = "优惠券满减未达标!";
+    public static final String COUPON_USED_MONEY_ERROR_MSG = "优惠券抵用金额异常!";
+    public static final String FOX_RECEIVE_NOT_FOUND = "此转发不存在!";
+    public static final String FOX_RECEIVE_WAS_USED = "此转发已被领用!";
+    public static final String FOX_RECEIVE_WAS_END = "此转发已过期!";
+    public static final String FOX_RECEIVE_ERROR_MSG = "购物金领用异常!";
+    public static final String FOX_RECEIVE_WAS_OWNER = "不能领用自己赠送的福利金!";
+    //狐狸金卡片转账
+    public static final Integer FOX_RECEIVE_TYPE_USER = 6;
+    public static final String  FOX_RECEIVE_TYPE_USER_MSG = "狐狸金赠送";
+    //狐狸金企业扫码
+    public static final Integer FOX_RECEIVE_TYPE_ENTERPRISE = 7;
+    public static final String FOX_RECEIVE_TYPE_ENTERPRISE_MSG = "企业扫码发放";
+    //所有商品
+    public static final Integer COUPON_TYPE_ALL = 0;
+    //指定商品
+    public static final Integer COUPON_TYPE_GOOD = 1;
+    //指定分类
+    public static final Integer COUPON_TYPE_CATE = 2;
+    //指定专场
+    public static final Integer COUPON_TYPE_TEBUY = 3;
+
+    //指定标签
+    public static final Integer COUPON_TYPE_TAG=4;
+    //限制消费金额
+    public static final Integer COUPON_LIMIT_TRUE = 1;
+    public static final Integer ORDER_TYPE_ALL = 0;
+    public static final Integer ORDER_TYPE_PAY = 1;
+    //待发货
+    public static final Integer ORDER_TYPE_DELIVERY = 2;
+    //待收货
+    public static final Integer ORDER_TYPE_RECEIPT = 3;
+    //退款
+    public static final Integer ORDER_TYPE_REFUND = 4;
+    //售后
+    public static final Integer ORDER_TYPE_AFTER_SALES = 5;
+    //取消订单
+    public static final Integer ORDER_TYPE_CANCEL = 6;
+    //订单完成
+    public static final Integer ORDER_TYPE_FINISH = 7;
+
+    public static final Integer ORDER_HAS_PAY = 1;
+    //订单有退款
+    public static final Integer ORDER_NOT_REFUND = 0;
+    public static final Integer ORDER_HAS_REFUND = 1;
+    public static final String ORDER_CANCEL_ERROR_MSG = "取消订单失败!";
+    public static final String ORDER_STOCK_CANCEL_ERROR_MSG = "取消订单失败!!";
+    public static final String CANT_GET_LOCK_MSG = "略略略~神奇的事情发生了";
+    public static final String ORDER_COMMIT_USER_NOT_EXISTS_MSG = "联系人或手机号获取失败!";
+    public static final Integer TYPE_PROVINCE = 1;
+    public static final Integer TYPE_CITY = 2;
+    public static final Integer TYPE_AREA = 3;
+    public static final Integer TYPE_STREET = 4;
+    public static final int DEFAULT_QR_CODE_SIZE = 176;
+    public static final String GOODS_CATE_IMG = "goods_cate_img";
+    public static final String GOODS_DETAIL_LOGO = "goods_detail_logo";
+    public static final String MINI_PATH_KEY = "mini_path";
+    //内购会跳转链接
+    public static final String INSIDE_PATH_KEY = "inside_path";
+
+    public static final String TOKEN_IS_NULL = "token is null";
+    //配置表内剔除提现手续费的比例
+    public static final String SERVICE_CHARGE = "service_charge";
+    //推荐分佣比例
+    public static final String RECOMMEND_C = "recommend_c";
+    public static final String TB_PROCESS_BEGIN = "tb_process_begin";
+    public static final String TB_PROCESS_END = "tb_process_end";
+    public static final Integer TB_PROCESS_BEGIN_VAL = 1;
+    public static final Integer TB_PROCESS_OTHER_VAL = 2;
+    public static final Integer TB_PROCESS_END_VAL = 3;
+    public static final String WEIXIN_ENTERPRISE_DEFAULT_IMG = "default_weixin_img";
+    public static final String WEIXIN_ENTERPRISE_DEFAULT_NAME = "default_weixin_name";
+    public static final String WEIXIN_ENTERPRISE_DEFAULT_URL = "default_weixin_url";
+    public static final String WEIXIN_ENTERPRISE_DEFAULT_PIC = "default_weixin_pic";
+
+    //运费加价按数量加
+    public static final Integer EXPENSES_TYPE_COUNT = 1;
+    //超级推手业务员分享
+    public static final Integer SHARE_BY_PUSHER = 1;
+    //惟客优品超级推手业务员分享
+    public static final Integer SHARE_BY_BEST_PUSHER = 2;
+    //惟客优品普通用户分享
+    public static final Integer SHARE_BY_BEST = 2;
+    //惟客优品短链接分享进入
+    public static final Integer SHARE_BY_SHORT_LINK = 2;
+    //微信返回错误码为0为正常
+    public static final Integer WX_MINI_CODE_NO_ERROR = 0;
+    //分享商品时间
+    public static final String SHARE_CONFIG_KEY = "share";
+    //今日主推
+    public static final String TODAY_FEATURED_KEY = "1";
+    public static final Integer TEAMBUY_TODAY_FEATURED = 1;
+    //品牌专场
+    public static final String BRAND_SPECIAL_KEY = "2";
+    public static final Integer TEAMBUY_BRAND_SPECIAL = 2;
+    //内购会
+    public static final Integer TEAMBUY_INSIDER = 3;
+    //日常购买,带商品分类
+    public static final String DAILY_BUY_KEY = "3";
+    //供应商分组
+    public static final String CONFIG_SUPPLIER_GROUP = "supplier_group";
+    //团购类型内购会
+    public static final Integer TEBUY_TYPE_INSIDE = 3;
+    public static final String TEBUY_TYPE_INSIDE_MSG = "内购会不能加入购物车";
+    //购物金已被使用
+    public static final Integer INSIDE_SHOPPING_USED = 2;
+    //优惠券领用限制
+    public static final String CONFIG_COUPON_LIMIT = "coupon_limit";
+    public static final String COUPON_LIMIT_OVER = "领用超过限制!";
+    public static final String COUPON_RECEIVCE_ERROR_MSG = "领用失败!";
+    public static final String SHOPPING_RECEIVCE_ERROR_MSG = "购物金领用失败!";
+    public static final Integer COUPON_SEND_AUTO_TYPE = 3;
+    //按天领取优惠卷
+    public static final Integer COUPON_VALID_FLAG_DAY_TYPE = 0;
+    //扫企业卡
+    public static final Integer SCAN_CARD_TYPE_ENTER = 1;
+    //短链接邀请
+    public static final Integer SCAN_CARD_TYPE_INVITE = 2;
+    public static final String SCAN_CARD_AGAIN_MSG = "重复扫码!";
+    public static final String BIND_ROLE_AGAIN_MSG = "重复绑定!";
+    public static final String ENTERPRISE_NOT_FOUND = "企业不存在!";
+    public static final String INSIDE_BIND_USER_NOT_FOUND = "内购会绑定用户不存在!";
+    public static final String USER_NOT_FOUND = "用户不存在!";
+    public static final String SCAN_CARD_BIND_ERROR_MSG = "扫码绑定异常!";
+    public static final String INSIDE_CANT_INVITE_MSG = "没有邀请资格!";
+    public static final String PARAMS_ERROR_MSG = "参数异常!";
+    public static final String CHOP_USED_ERROR_MSG = "帮抢抵扣异常!";
+    public static final String CHOP_STATUS_ERROR_MSG = "帮抢不存在或者异常!";
+    public static final String CHOP_CANT_INVITE_MSG ="此活动您未收到邀请,请联系客服";
+    public static final String CHOP_INVITE_ERROR_MSG ="帮抢绑定内购会失败!";
+
+    public static final String LOTTERY_NOT_FOUND = "此抽奖活动不存在!";
+    public static final String LOTTERY_NOT_CHANCE = "没有抽奖次数!";
+    public static final String LOTTERY_DREW_SUCCESS ="抽奖成功!";
+    public static final String LOTTERY_DREW_FAILED="抽奖失败!";
+    public static final String LOTTERY_DREW_ERROR="点击太快,请重新尝试!";
+    public static final String LOTTERY_CHANCE_SUCCESS ="成功获得抽奖次数!";
+    public static final String LOTTERY_CHANCE_FAILED ="获得抽奖次数失败!";
+    public static final String LOTTERY_CHANCE_REPEAT ="无法重复获得次数!";
+    public static final String BASE_NOT_LOGIN = "账号未登录!";
+    //企业发放狐狸金
+    public static final Integer FOX_RECEIVE_ENTERPRISE = 1;
+    //个人转发狐狸金
+    public static final Integer FOX_RECEIVE_USER = 2;
+
+    public static final String CARD_NOT_MATCH_ENTERPRISE_MSG = "您不是该企业员工,无法领取~";
+    public static final String CARD_NOT_FOUND_MSG = "会员卡不存在!";
+    public static final String CARD_NOT_ENTERPRISE_MSG = "会员卡未绑定企业!";
+    public static final String CARD_MASTER_NOT_FOUND_MSG = "会员主卡不存在!";
+    public static final String CARD_TYPE_NOT_FOUND_MSG = "会员卡类型不存在!";
+    public static final String CARD_WAS_USED_MSG = "会员卡已被使用!";
+    public static final String CARD_BIND_SELF_MSG = "副卡不能绑定自己!";
+    public static final String CARD_CHANGE_ERROR_MSG = "不符合会员卡变更规则!";
+    public static final String CARD_SLAVE_OVER_MSG = "绑定副卡已到上限!";
+    public static final String CARD_SLAVE_HAS_MASTER_MSG = "不能领取副卡!";
+    //主卡
+    public static final Integer CARD_TYPE_MASTER = 1;
+    //副卡
+    public static final Integer CARD_TYPE_SLAVE =2;
+    //副卡在会员中心看到的卡片
+    public static final Integer CARD_TYPE_SLAVE_SHOW =3;
+    public static final Integer CARD_VIP_HALF_MONTH =1;
+    public static final Integer CARD_VIP_ONE_YEAR =2;
+    public static final Integer CARD_VIP_TWO_YEARS =3;
+    //扫会员卡
+    public static final Integer CARD_SCAN_VIP = 1;
+    //扫企业卡
+    public static final Integer CARD_SCAN_ENTERPRISE = 2;
+    //已领取
+    public static final Integer CARD_STATUS_HAS = 2;
+    //待领取
+    public static final Integer CARD_STATUS_CAN = 1;
+    //不能领
+    public static final Integer CARD_STATUS_CANT = 0;
+
+    public static final String ENTERPRISE_MONEY_NOT_ENOUGH_MSG = "企业狐狸金余额不足!";
+    public static final String ENTERPRISE_FOX_DAY_ERROR_MSG = "企业狐狸金未设置日期!";
+    public static final String CARD_FOX_MONEY_MSG = "会员卡狐狸金发放";
+    public static final String CARD_FOX_MONEY_ERROR_MSG = "会员卡狐狸金发放失败!";
+    public static final Long SAN_XIANG_BANK_ID = 443611075020201984L;
+    public static final Integer USER_NOT_PHONE_CODE = 110;
+    public static final String USER_NOT_PHONE_MSG = "请先绑定手机号!";
+
+    //企业价
+    public static final Integer priceTypeEnterAdd=1;
+    //普通京东价
+    public static final Integer priceTypeNormalAdd=2;
+    //活动优惠券
+    public static final String activeCoupon="activeCoupon";
+    //删除标识
+    public static final Integer delFlag=1;
+    //过期
+    public static final Integer invalidLosed=1;
+    //结算版本
+    public static final Integer setNewVersion3=3;
+    //第一结算版
+    public static final Integer setVersion1=0;
+
+    //默认过期时间
+    public static final Integer defaultExpDate=7;
+    //默认扣狐豆5000
+    public static final Integer defaultExpDeductFoxBean=5000;
+
+    //会员过期key
+    public static final String memberDownGrade="member_down_grade";
+    //过期扣狐豆数
+    public static final String memberDownGradeDeductFoxBean="member_down_fox_bean";
+
+    public static final String expTimeException="过期时间异常";
+    public static final Integer masterCard=1;//主卡
+    public static final  Integer slaveCard=2;//副卡
+    public static final Integer tempCard=3;//临时卡
+    public static final String slaveCardIsGet="不能领多张副卡";
+    public static final String tempCardIsGet ="不能领多张临时卡";
+    public static final String userGradeException="主卡用户等级异常";
+    public static final String masterSlaveCardNumOut="副卡已领完";
+    public static final String masterTempCardNumOut="临时卡已领完";
+    public static final String masterCardTimeLimit="卡已过期";
+    public static final Integer limitBuyTeType=5;//限购类型活动
+    public static final Integer newUserLimitBuy=9;
+    public static final String payFullMoneyGive="pay_full_money_give";
+    public static final String isNewUser="is_new_user";
+
+}

+ 152 - 0
src/main/java/com/futu/course/common/constant/Constants.java

@@ -0,0 +1,152 @@
+package com.futu.course.common.constant;
+
+
+/**
+ * 通用常量信息
+ *
+ */
+public class Constants
+{
+    /**
+     * UTF-8 字符集
+     */
+    public static final String UTF8 = "UTF-8";
+
+    /**
+     * GBK 字符集
+     */
+    public static final String GBK = "GBK";
+
+    /**
+     * http请求
+     */
+    public static final String HTTP = "http://";
+
+    /**
+     * https请求
+     */
+    public static final String HTTPS = "https://";
+
+    /**
+     * 通用成功标识
+     */
+    public static final String SUCCESS = "0";
+
+    /**
+     * 通用失败标识
+     */
+    public static final String FAIL = "1";
+
+    /**
+     * 登录成功
+     */
+    public static final String LOGIN_SUCCESS = "Success";
+
+    /**
+     * 注销
+     */
+    public static final String LOGOUT = "Logout";
+
+    /**
+     * 注册
+     */
+    public static final String REGISTER = "Register";
+
+    /**
+     * 登录失败
+     */
+    public static final String LOGIN_FAIL = "Error";
+
+    /**
+     * 验证码 redis key
+     */
+    public static final String CAPTCHA_CODE_KEY = "captcha_codes:";
+
+    /**
+     * 登录用户 redis key
+     */
+    public static final String LOGIN_TOKEN_KEY = "login_tokens:";
+
+    /**
+     * 防重提交 redis key
+     */
+    public static final String REPEAT_SUBMIT_KEY = "repeat_submit:";
+
+    /**
+     * 限流 redis key
+     */
+    public static final String RATE_LIMIT_KEY = "rate_limit:";
+
+    /**
+     * 验证码有效期(分钟)
+     */
+    public static final Integer CAPTCHA_EXPIRATION = 2;
+
+    /**
+     * 令牌
+     */
+    public static final String TOKEN = "token";
+
+    /**
+     * 令牌前缀
+     */
+    public static final String TOKEN_PREFIX = "Bearer ";
+
+    /**
+     * 令牌前缀
+     */
+    public static final String LOGIN_USER_KEY = "login_user_key";
+
+    /**
+     * 用户ID
+     */
+    public static final String JWT_USERID = "userid";
+
+
+    /**
+     * 用户头像
+     */
+    public static final String JWT_AVATAR = "avatar";
+
+    /**
+     * 创建时间
+     */
+    public static final String JWT_CREATED = "created";
+
+    /**
+     * 用户权限
+     */
+    public static final String JWT_AUTHORITIES = "authorities";
+
+    /**
+     * 参数管理 cache key
+     */
+    public static final String SYS_CONFIG_KEY = "sys_config:";
+
+    /**
+     * 字典管理 cache key
+     */
+    public static final String SYS_DICT_KEY = "sys_dict:";
+
+    /**
+     * 资源映射路径 前缀
+     */
+    public static final String RESOURCE_PREFIX = "/profile";
+
+    /**
+     * RMI 远程方法调用
+     */
+    public static final String LOOKUP_RMI = "rmi:";
+
+    /**
+     * LDAP 远程方法调用
+     */
+    public static final String LOOKUP_LDAP = "ldap:";
+
+    /**
+     * LDAPS 远程方法调用
+     */
+    public static final String LOOKUP_LDAPS = "ldaps:";
+
+
+}

+ 52 - 0
src/main/java/com/futu/course/common/constant/RedisKeyConst.java

@@ -0,0 +1,52 @@
+package com.futu.course.common.constant;
+
+/**
+ * @className: RedisKeyConst
+ * @Description:
+ * @author: zhangxiong
+ * @date: 2022/4/25/0025 15:35
+ */
+public class RedisKeyConst {
+    public static final int TEN_SECOND = 10;
+    public static final int THIRTY_SECOND = TEN_SECOND*3;
+    public static final int ONE_MINUTE = THIRTY_SECOND*2;
+    public static final int FIVE_MINUTE = ONE_MINUTE*5;
+    public static final int TEN_MINUTE = FIVE_MINUTE*2;
+    public static final int THIRTY_MINUTE = FIVE_MINUTE*3;
+    public static final int ONE_HOUR = THIRTY_MINUTE*2;
+    public static final int TWENTY_NINE = 29;
+    public static final String CACHE_HOME_BASE = "test:boss:api:cache:home:base";
+    public static final String CACHE_TEAM_BUY_BASE = "test:boss:api:cache:teambuy:base";
+    public static final String CACHE_TEAM_BUY_ITEM = "test:boss:api:cache:teambuy:%s";
+    public static final String CACHE_SUPPLIER_BASE = "test:boss:api:cache:supplier:base";
+    public static final String CACHE_CONFIG_BASE = "test:boss:api:cache:home:config";
+    public static final String CACHE_CATE_BASE = "test:boss:api:cache:cate:base";
+    public static final String CACHE_BASE_TYPE_PAGE = "test:boss:api:cache:%s:c%ss%st%s%s";
+    public static final String CACHE_BASE_PAGE = "test:boss:api:cache:%s:c%ss%s";
+    public static final String CACHE_BASE_DELETE = "test:boss:api:cache:%s%s:*";
+    public static final String CACHE_LOTTERY_RESERVE = "test:boss:api:cache:lottery:reserve:";
+    public static final String CACHE_LOTTERY_DETAIL = "test:boss:api:cache:lottery:detail:";
+    public static final String CACHE_LOTTERY_RATE = "test:boss:api:cache:lottery:rate:";
+    public static final String CACHE_LOTTERY_ITEM = "test:boss:api:cache:lottery:item:";
+    public static final String CACHE_BASE_GOODS = "test:boss:api:cache:goods:";
+    public static final String LOCK_PAY_COMMIT = "test:boss:api:lock:sku:";
+    public static final String LOCK_COUNPON_RECEIVCE = "test:boss:api:lock:coupon:";
+    public static final String LOCK_LOTTERY_ITEM = "test:boss:api:lock:lottery:item:";
+    public static final String LOCK_FOX_RECEIVE_INIT ="test:boss:api:lock:fox:init:";
+    public static final String LOCK_FOX_RECEIVE ="test:boss:api:lock:fox:receive:";
+    //微信小程序商品/对应人的短链接
+    public static final String CACHE_MINI_SHORT_LINK = "test:boss:api:wx:mini:shortlink:%s:%s";
+    public static final String GOODS = "test:goods:";
+    public static final String CART = "test:cart:";
+    public static final String ORDER = "test:order:";
+
+    public static String generatePageKey(String key, long current,long page,Integer type,Long supplierId) {
+        return String.format(CACHE_BASE_TYPE_PAGE, key, current,page,type,supplierId);
+    }
+    public static String generatePageKey(String key, long current,long page) {
+        return String.format(CACHE_BASE_PAGE, key, current,page);
+    }
+    public static String generateKey(String key, Object... params) {
+        return String.format(key, params);
+    }
+}

+ 52 - 0
src/main/java/com/futu/course/common/entity/ApiErrorCode.java

@@ -0,0 +1,52 @@
+package com.futu.course.common.entity;
+
+
+/**
+ * @className: ApiErrorCode
+ * @Description:
+ * @author: zhangxiong
+ * @date: 2022/4/24/0024 15:43
+ */
+public enum ApiErrorCode implements IErrorCode {
+    /**
+     * 失败
+     */
+    FAILED(1, "操作失败"),
+    /**
+     * 成功
+     */
+    SUCCESS(0, "执行成功");
+
+    private final long code;
+    private final String msg;
+
+    ApiErrorCode(final long code, final String msg) {
+        this.code = code;
+        this.msg = msg;
+    }
+
+    public static ApiErrorCode fromCode(long code) {
+        ApiErrorCode[] ecs = ApiErrorCode.values();
+        for (ApiErrorCode ec : ecs) {
+            if (ec.getCode() == code) {
+                return ec;
+            }
+        }
+        return SUCCESS;
+    }
+
+    @Override
+    public long getCode() {
+        return code;
+    }
+
+    @Override
+    public String getMsg() {
+        return msg;
+    }
+
+    @Override
+    public String toString() {
+        return String.format(" ErrorCode:{code=%s, msg=%s} ", code, msg);
+    }
+}

+ 42 - 0
src/main/java/com/futu/course/common/entity/HttpResult.java

@@ -0,0 +1,42 @@
+package com.futu.course.common.entity;
+
+
+
+import org.apache.commons.httpclient.HttpStatus;
+
+
+/**
+ * http返回结果
+ *
+ */
+public class HttpResult {
+	private int stateCode;
+
+	private String content;
+
+	public String getContent() {
+		return content;
+	}
+
+	public void setContent(String content) {
+		this.content = content;
+	}
+
+	public int getStateCode() {
+		return stateCode;
+	}
+
+	/**
+	 * 获取请求状态
+	 * @return:
+	 */
+	public boolean getOK() {
+        return this.stateCode == HttpStatus.SC_OK
+                || this.stateCode == HttpStatus.SC_MOVED_PERMANENTLY
+                || this.stateCode == HttpStatus.SC_MOVED_TEMPORARILY;
+	}
+
+	public void setStateCode(int stateCode) {
+		this.stateCode = stateCode;
+	}
+}

+ 20 - 0
src/main/java/com/futu/course/common/entity/IErrorCode.java

@@ -0,0 +1,20 @@
+package com.futu.course.common.entity;
+
+/**
+ * @className: IErrorCode
+ * @Description:
+ * @author: zhangxiong
+ * @date: 2022/4/24/0024 15:43
+ */
+public interface IErrorCode {
+
+    /**
+     * 错误编码 -1、失败 0、成功
+     */
+    long getCode();
+
+    /**
+     * 错误描述
+     */
+    String getMsg();
+}

+ 92 - 0
src/main/java/com/futu/course/common/entity/R.java

@@ -0,0 +1,92 @@
+package com.futu.course.common.entity;
+
+
+import com.futu.course.common.exception.ApiException;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.Optional;
+
+/**
+ * @className: R
+ * @Description:
+ * @author: zhangxiong
+ * @date: 2022/4/23/0023 20:52
+ */
+@Data
+public class R<T> implements Serializable {
+
+    /**
+     * serialVersionUID
+     */
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 业务错误码
+     */
+    private long code;
+    /**
+     * 结果集
+     */
+    private T data;
+    /**
+     * 描述
+     */
+    private String msg;
+
+    public R() {
+        // to do nothing
+    }
+
+    public R(IErrorCode errorCode) {
+        errorCode = Optional.ofNullable(errorCode).orElse(ApiErrorCode.FAILED);
+        this.code = errorCode.getCode();
+        this.msg = errorCode.getMsg();
+    }
+
+    public static <T> R<T> ok(T data) {
+        ApiErrorCode aec = ApiErrorCode.SUCCESS;
+        if (data instanceof Boolean && Boolean.FALSE.equals(data)) {
+            aec = ApiErrorCode.FAILED;
+        }
+        return restResult(data, aec);
+    }
+
+    public static <T> R<T> failed(String msg) {
+        return restResult(null, ApiErrorCode.FAILED.getCode(), msg);
+    }
+
+    public static <T> R<T> failed(int code,String msg) {
+        return restResult(null, code, msg);
+    }
+
+    public static <T> R<T> failed(IErrorCode errorCode) {
+        return restResult(null, errorCode);
+    }
+
+    public static <T> R<T> restResult(T data, IErrorCode errorCode) {
+        return restResult(data, errorCode.getCode(), errorCode.getMsg());
+    }
+
+    public static <T> R<T> restResult(T data, long code, String msg) {
+        R<T> apiResult = new R<>();
+        apiResult.setCode(code);
+        apiResult.setData(data);
+        apiResult.setMsg(msg);
+        return apiResult;
+    }
+
+    public boolean ok() {
+        return ApiErrorCode.SUCCESS.getCode() == code;
+    }
+
+    /**
+     * 服务间调用非业务正常,异常直接释放
+     */
+    public T serviceData() {
+        if (!ok()) {
+            throw new ApiException(this.msg);
+        }
+        return data;
+    }
+}

+ 44 - 0
src/main/java/com/futu/course/common/exception/ApiException.java

@@ -0,0 +1,44 @@
+package com.futu.course.common.exception;
+
+
+import com.futu.course.common.entity.IErrorCode;
+
+/**
+ * @className: ApiException
+ * @Description:
+ * @author: zhangxiong
+ * @date: 2022/4/24/0024 15:44
+ */
+public class ApiException extends RuntimeException {
+
+    /**
+     * serialVersionUID
+     */
+    private static final long serialVersionUID = -5885155226898287919L;
+
+    /**
+     * 错误码
+     */
+    private IErrorCode errorCode;
+
+    public ApiException(IErrorCode errorCode) {
+        super(errorCode.getMsg());
+        this.errorCode = errorCode;
+    }
+
+    public ApiException(String message) {
+        super(message);
+    }
+
+    public ApiException(Throwable cause) {
+        super(cause);
+    }
+
+    public ApiException(String message, Throwable cause) {
+        super(message, cause);
+    }
+
+    public IErrorCode getErrorCode() {
+        return errorCode;
+    }
+}

+ 106 - 0
src/main/java/com/futu/course/common/exception/GlobalExceptionHandler.java

@@ -0,0 +1,106 @@
+package com.futu.course.common.exception;
+
+import com.futu.course.common.constant.BestConstants;
+import com.futu.course.common.entity.R;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.jdbc.BadSqlGrammarException;
+import org.springframework.validation.BindException;
+import org.springframework.web.bind.MethodArgumentNotValidException;
+import org.springframework.web.bind.annotation.ExceptionHandler;
+import org.springframework.web.bind.annotation.RestControllerAdvice;
+
+import javax.servlet.http.HttpServletRequest;
+import java.sql.SQLException;
+
+/**
+ * 全局异常处理器
+ */
+@RestControllerAdvice
+public class GlobalExceptionHandler {
+    private static final Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class);
+
+
+    /**
+     * 拦截未知的运行时异常
+     */
+    @ExceptionHandler(RuntimeException.class)
+    public R handleRuntimeException(RuntimeException e, HttpServletRequest request) {
+        String requestURI = request.getRequestURI();
+        log.error("请求地址'{}',发生未知异常.", requestURI, e);
+        return R.failed(e.getMessage() == null ? "发生未知错误,请联系管理员!" : e.getMessage());
+    }
+
+    /**
+     * 处理自定义异常
+     */
+    @ExceptionHandler(RRException.class)
+    public R handleRRException(RRException e, HttpServletRequest request) {
+        String requestURI = request.getRequestURI();
+//        log.error("请求地址'{}',发生系统异常.", requestURI, e);
+
+        log.info("自定义异常");
+        return R.failed(e.getCode(), e.getMsg());
+    }
+
+    /**
+     * 系统异常
+     */
+    @ExceptionHandler(Exception.class)
+    public R handleException(Exception e, HttpServletRequest request) {
+        String requestURI = request.getRequestURI();
+        log.error("请求地址'{}',发生系统异常.", requestURI, e);
+        return R.failed("系统错误,请联系管理员!");
+    }
+
+    /**
+     * 系统异常
+     */
+    @ExceptionHandler(BadSqlGrammarException.class)
+    public R handleBadSQLException(BadSqlGrammarException e, HttpServletRequest request) {
+        String requestURI = request.getRequestURI();
+        log.error("请求地址'{}',发生系统异常.", requestURI, e);
+        return R.failed("系统错误,请联系管理员!");
+    }
+
+    /**
+     * 系统异常
+     */
+    @ExceptionHandler(SQLException.class)
+    public R handleSQLException(SQLException e, HttpServletRequest request) {
+        String requestURI = request.getRequestURI();
+        log.error("请求地址'{}',发生系统异常.", requestURI, e);
+        return R.failed("系统错误,请联系管理员!");
+    }
+
+    /**
+     * 自定义验证异常
+     */
+    @ExceptionHandler(BindException.class)
+    public R handleBindException(BindException e) {
+//        log.error(e.getMessage(), e);
+        String message = e.getAllErrors().get(0).getDefaultMessage();
+        return R.failed(message);
+    }
+
+    /**
+     * 自定义验证异常
+     */
+    @ExceptionHandler(UnBindPhoneException.class)
+    public R handleUnBindPhoneException(UnBindPhoneException e) {
+//        log.error(e.getMessage(), e);
+        return R.failed(BestConstants.USER_NOT_PHONE_CODE,BestConstants.USER_NOT_PHONE_MSG);
+    }
+    /**
+     * 自定义验证异常
+     */
+    @ExceptionHandler(MethodArgumentNotValidException.class)
+    public Object handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {
+//        log.error(e.getMessage(), e);
+        log.info("自定义验证异常");
+        String message = e.getBindingResult().getFieldError().getDefaultMessage();
+        return R.failed(message);
+    }
+
+}

+ 53 - 0
src/main/java/com/futu/course/common/exception/RRException.java

@@ -0,0 +1,53 @@
+
+package com.futu.course.common.exception;
+
+/**
+ * 自定义异常
+ *
+ */
+public class RRException extends RuntimeException {
+	private static final long serialVersionUID = 1L;
+
+    private String msg;
+    private int code = 500;
+
+    public RRException(String msg) {
+		super(msg);
+		this.msg = msg;
+	}
+
+	public RRException(String msg, Throwable e) {
+		super(msg, e);
+		this.msg = msg;
+	}
+
+	public RRException(String msg, int code) {
+		super(msg);
+		this.msg = msg;
+		this.code = code;
+	}
+
+	public RRException(String msg, int code, Throwable e) {
+		super(msg, e);
+		this.msg = msg;
+		this.code = code;
+	}
+
+	public String getMsg() {
+		return msg;
+	}
+
+	public void setMsg(String msg) {
+		this.msg = msg;
+	}
+
+	public int getCode() {
+		return code;
+	}
+
+	public void setCode(int code) {
+		this.code = code;
+	}
+
+
+}

+ 25 - 0
src/main/java/com/futu/course/common/exception/UnBindPhoneException.java

@@ -0,0 +1,25 @@
+package com.futu.course.common.exception;
+
+/**
+ * @className: UnBindPhoneException
+ * @Description:
+ * @author: zhangxiong
+ * @date: 2023/1/5/0005 19:03
+ */
+public class UnBindPhoneException extends RRException {
+    public UnBindPhoneException(String msg) {
+        super(msg);
+    }
+
+    public UnBindPhoneException(String msg, Throwable e) {
+        super(msg, e);
+    }
+
+    public UnBindPhoneException(String msg, int code) {
+        super(msg, code);
+    }
+
+    public UnBindPhoneException(String msg, int code, Throwable e) {
+        super(msg, code, e);
+    }
+}

+ 31 - 0
src/main/java/com/futu/course/common/handle/CurrentUserHandler.java

@@ -0,0 +1,31 @@
+package com.futu.course.common.handle;
+
+
+import com.futu.course.common.annotation.CurrentUser;
+import com.futu.course.common.utils.TokenUtils;
+import org.springframework.core.MethodParameter;
+import org.springframework.web.bind.support.WebDataBinderFactory;
+import org.springframework.web.context.request.NativeWebRequest;
+import org.springframework.web.method.support.HandlerMethodArgumentResolver;
+import org.springframework.web.method.support.ModelAndViewContainer;
+
+/**
+ * @className: CurrentUserHandle
+ * @Description:
+ * @author: zhangxiong
+ * @date: 2022/5/5/0005 16:49
+ */
+public class CurrentUserHandler implements HandlerMethodArgumentResolver {
+    @Override
+    public boolean supportsParameter(MethodParameter parameter) {
+        return parameter.hasParameterAnnotation(CurrentUser.class);
+    }
+
+    @Override
+    public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
+        String authorization = webRequest.getHeader("token");
+        //从token中拿到userId
+        Long userId = TokenUtils.getUserId(authorization);
+        return userId;
+    }
+}

+ 58 - 0
src/main/java/com/futu/course/common/handle/StringListTypeHandler.java

@@ -0,0 +1,58 @@
+package com.futu.course.common.handle;
+
+import cn.hutool.json.JSONArray;
+import cn.hutool.json.JSONUtil;
+import org.apache.ibatis.type.BaseTypeHandler;
+import org.apache.ibatis.type.JdbcType;
+
+import java.sql.CallableStatement;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.List;
+
+/**
+ * @className: LongArrayTypeHandler
+ * @Description:
+ * @author: zhangxiong
+ * @date: 2022/4/26/0026 19:24
+ */
+public class StringListTypeHandler extends BaseTypeHandler<List<String>> {
+    @Override
+    public void setNonNullParameter(PreparedStatement ps, int i, List<String> strings, JdbcType jdbcType) throws SQLException {
+        String s = JSONUtil.toJsonStr(strings);
+        ps.setString(i, s);
+    }
+
+    @Override
+    public List<String> getNullableResult(ResultSet rs, String s) throws SQLException {
+        if (rs.wasNull()){
+            return null;
+        }
+        String str = rs.getString(s);
+        JSONArray objects = JSONUtil.parseArray(str);
+        return JSONUtil.toList(objects,String.class);
+
+    }
+
+    @Override
+    public List<String> getNullableResult(ResultSet rs, int i) throws SQLException {
+        if (rs.wasNull()){
+            return null;
+        }
+        String str = rs.getString(i);
+        JSONArray objects = JSONUtil.parseArray(str);
+        return JSONUtil.toList(objects,String.class);
+    }
+
+    @Override
+    public List<String> getNullableResult(CallableStatement cs, int i) throws SQLException {
+        if (cs.wasNull()){
+            return null;
+        }
+        String str = cs.getString(i);
+        JSONArray objects = JSONUtil.parseArray(str);
+        return JSONUtil.toList(objects,String.class);
+
+    }
+}

+ 108 - 0
src/main/java/com/futu/course/common/intercepter/AuthIntercepter.java

@@ -0,0 +1,108 @@
+package com.futu.course.common.intercepter;
+
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONObject;
+import com.futu.course.common.annotation.NonLoginRequired;
+import com.futu.course.common.exception.RRException;
+import com.futu.course.common.utils.RedisUtil;
+import com.futu.course.common.utils.StringUtils;
+import com.futu.course.common.utils.TokenUtils;
+
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.stereotype.Component;
+import org.springframework.web.method.HandlerMethod;
+import org.springframework.web.servlet.HandlerInterceptor;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.lang.reflect.Method;
+
+@Component
+@Slf4j
+public class AuthIntercepter implements HandlerInterceptor {
+
+    @Autowired
+    protected RedisTemplate redisTemplate;
+
+    @Autowired
+    private RedisUtil redisUtil;
+
+    public final static String ACCESSTOKEN = "token";
+
+    public AuthIntercepter() {
+
+    }
+
+    public AuthIntercepter(RedisTemplate redisTemplate) {
+        this.redisTemplate = redisTemplate;
+    }
+
+    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
+            throws Exception {
+        //System.out.println("preHandle");
+        String deviceId = request.getHeader("deviceId");
+        String token = request.getHeader(ACCESSTOKEN);
+//        String time = request.getHeader("time");
+        log.info(request.getRequestURI());
+        //log.info("deviceId={},token={},time={}",deviceId,token,time);
+        try {
+
+            if (!(handler instanceof HandlerMethod)) {
+                return true;
+            }
+
+            HandlerMethod handlerMethod = (HandlerMethod) handler;
+
+            Method method = handlerMethod.getMethod();
+
+            NonLoginRequired loginRequired = method.getAnnotation(NonLoginRequired.class);
+            //有@LoginRequired注解,需要认证
+            if (loginRequired != null) {
+
+                return true;
+            }
+
+            log.info("---interceptertoken");
+            log.info(token);
+            if (StringUtils.isEmpty(deviceId)) {
+
+                //response.getWriter().print("");
+                log.info("*********************》deviceId错误");
+                throw new RRException("deviceId错误", 102);
+                //return false;
+            }
+            if (StringUtils.isEmpty(token)) {
+                log.info("*********************》token为空");
+                throw new RRException("token错误,请重新登录", 101);
+            }
+//            if (StringUtils.isEmpty(time)) {
+//                throw new RRException("时间错误", 103);
+//            }
+
+            log.info("调用token**********************》");
+            log.info(token);
+            log.info("************************************》");
+            Long userId = TokenUtils.getUserId(token);
+            log.info("userId={}",userId);
+            Object obj = redisTemplate.opsForValue().get(Long.toString(userId));
+            if (obj == null) {
+                log.info("************************用户token不存在");
+                throw new RRException("token过期", 101);
+            }
+
+
+
+        } catch (RRException e) {
+            log.info("调用了异常通知" + e.getMessage());
+
+            throw e;
+        }
+
+        return true;
+    }
+
+
+}

+ 69 - 0
src/main/java/com/futu/course/common/utils/BestUtil.java

@@ -0,0 +1,69 @@
+package com.futu.course.common.utils;
+
+
+import cn.hutool.core.util.RandomUtil;
+
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+
+/**
+ * @className: CpaUtil
+ * @Description:
+ * @author: zhangxiong
+ * @date: 2021/12/29/0029 16:06
+ */
+public class BestUtil {
+
+    private static final BigDecimal ZERO = new BigDecimal(0);
+    private static final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyMMddHHmmss");
+    private static final int idFormatter = 100;
+    public static final BigDecimal ZERO_BUY = new BigDecimal("0.01");
+    public static final BigDecimal ARRIVAL_PRICE = new BigDecimal("500");
+    public static int handleDivide(Integer dividend, Integer divisor) {
+        int result = 0;
+        if (divisor > 0 && dividend > 0) {
+            BigDecimal divide = BigDecimal.valueOf(dividend).divide(BigDecimal.valueOf(divisor), 2, BigDecimal.ROUND_HALF_UP);
+            return divide.scaleByPowerOfTen(2).intValue();
+        }
+        return result;
+    }
+
+    public static String geneOrderNum(Long userId){
+        StringBuilder format = new StringBuilder(LocalDateTime.now().format(formatter));
+        format.append(RandomUtil.randomNumbers(4));
+        format.append(userId%idFormatter);
+        return format.toString();
+    }
+    /**
+     *
+     * @param val
+     * @return 大于 0 true,小于等于0false
+     */
+    public static boolean GTZero(BigDecimal val){
+        if(val == null){
+            return false;
+        }
+        return ZERO.compareTo(val) < 0;
+    }
+    /**
+     *
+     * @param val
+     * @return 大于等于 0 true,小于0false
+     */
+    public static boolean GEZero(BigDecimal val){
+        if(val == null){
+            return false;
+        }
+        return ZERO.compareTo(val) <= 0;
+    }
+
+    public static String desensitizationMobile(String mobile){
+        if(mobile != null && mobile.length() > 10){
+            String suffix = mobile.substring(6);
+            String prefix = mobile.substring(0, 3);
+            return prefix +"***" +suffix;
+        }
+        return "";
+    }
+}

+ 152 - 0
src/main/java/com/futu/course/common/utils/CacheKey.java

@@ -0,0 +1,152 @@
+package com.futu.course.common.utils;
+
+/**
+ * 缓存key 定义
+ */
+public final class CacheKey {
+
+	/**
+	 * 1秒
+	 */
+	public static final int	SEC1		= 1;
+
+	/**
+	 * 3秒
+	 */
+	public static final int	SEC3		= SEC1 * 3;
+
+	/**
+	 * 5秒
+	 */
+	public static final int	SEC5		= SEC1 * 5;
+
+	/**
+	 * 15秒
+	 */
+	public static final int	SEC15		= SEC1 * 15;
+
+	/**
+	 * 30秒
+	 */
+	public static final int	SEC30		= SEC1 * 30;
+
+	/**
+	 * 1分钟
+	 */
+	public static final int	MINUTES1	= 60 * SEC1;
+
+	/**
+	 * 3分钟
+	 */
+	public static final int	MINUTES3	= 60 * SEC3;
+
+	/**
+	 * 5分钟
+	 */
+	public static final int	MINUTES5	= 5 * MINUTES1;
+	/**
+	 * 10分钟
+	 */
+	public static final int	MINUTES10	= 10 * MINUTES1;
+	/**
+	 * 30分钟
+	 */
+	public static final int	MINUTES30	= 30 * MINUTES1;
+	/**
+	 * 1小时
+	 */
+	public static final int	HOUR1		= 60 * MINUTES1;
+	/**
+	 * 半天
+	 */
+	public static final int	HOUR12		= 12 * HOUR1;
+	/**
+	 * 1天
+	 */
+	public static final int	DAY1		= 24 * HOUR1;
+
+	/**
+	 * 3天
+	 */
+	public static final int	DAY3		= 3 * DAY1;
+	/**
+	 * 7天
+	 */
+	public static final int	DAY7		= 7 * DAY1;
+
+	/**
+	 * 15天
+	 */
+	public static final int	DAY15		= 15 * DAY1;
+
+	/**
+	 * 30天
+	 */
+	public static final int	DAY30		= 30 * DAY1;
+
+	/**
+	 * 最大值
+	 */
+	public static final int	MAX			= Integer.MAX_VALUE;
+
+	/**
+	 * 超时时间
+	 */
+	private Integer			expire;
+
+	/**
+	 * 缓存 key,通常是一个前缀或后缀
+	 */
+	private String			key;
+
+	private CacheKey() {
+	}
+
+	public CacheKey(String key, Integer expire) {
+		this.expire = expire;
+		this.key = key;
+	}
+
+	public CacheKey(String key) {
+		this.key = key;
+	}
+
+	@Override
+	public String toString() {
+		return key;
+	}
+
+	public String getKey() {
+		return key;
+	}
+
+	public Integer getExpire() {
+		return expire;
+	}
+
+	/**
+	 * key+appendKey
+	 *
+	 * @param appendKey
+	 * @return
+	 */
+	public String getKeyPrefix(Object appendKey) {
+		if (appendKey == null) {
+			throw new IllegalArgumentException("appendKey 不能为空......");
+		}
+		return key + appendKey;
+	}
+
+	/**
+	 * appendKey+key
+	 *
+	 * @param appendKey
+	 * @return
+	 */
+	public String getKeySuffix(Object appendKey) {
+		if (appendKey == null) {
+			throw new IllegalArgumentException("appendKey 不能为空......");
+		}
+		return appendKey + key;
+	}
+}

+ 120 - 0
src/main/java/com/futu/course/common/utils/Constant.java

@@ -0,0 +1,120 @@
+
+package com.futu.course.common.utils;
+
+/**
+ * 常量
+ *
+ * @author Mark sunlightcs@gmail.com
+ */
+public class Constant {
+	/** 超级管理员ID */
+	public static final int SUPER_ADMIN = 1;
+    /**
+     * 当前页码
+     */
+    public static final String PAGE = "page";
+    /**
+     * 每页显示记录数
+     */
+    public static final String LIMIT = "limit";
+    /**
+     * 排序字段
+     */
+    public static final String ORDER_FIELD = "sidx";
+    /**
+     * 排序方式
+     */
+    public static final String ORDER = "order";
+    /**
+     *  升序
+     */
+    public static final String ASC = "asc";
+	/**
+	 * 菜单类型
+	 *
+	 * @author chenshun
+	 * @email sunlightcs@gmail.com
+	 * @date 2016年11月15日 下午1:24:29
+	 */
+    public enum MenuType {
+        /**
+         * 目录
+         */
+    	CATALOG(0),
+        /**
+         * 菜单
+         */
+        MENU(1),
+        /**
+         * 按钮
+         */
+        BUTTON(2);
+
+        private int value;
+
+        MenuType(int value) {
+            this.value = value;
+        }
+
+        public int getValue() {
+            return value;
+        }
+    }
+
+    /**
+     * 定时任务状态
+     *
+     * @author chenshun
+     * @email sunlightcs@gmail.com
+     * @date 2016年12月3日 上午12:07:22
+     */
+    public enum ScheduleStatus {
+        /**
+         * 正常
+         */
+    	NORMAL(0),
+        /**
+         * 暂停
+         */
+    	PAUSE(1);
+
+        private int value;
+
+        ScheduleStatus(int value) {
+            this.value = value;
+        }
+
+        public int getValue() {
+            return value;
+        }
+    }
+
+    /**
+     * 云服务商
+     */
+    public enum CloudService {
+        /**
+         * 七牛云
+         */
+        QINIU(1),
+        /**
+         * 阿里云
+         */
+        ALIYUN(2),
+        /**
+         * 腾讯云
+         */
+        QCLOUD(3);
+
+        private int value;
+
+        CloudService(int value) {
+            this.value = value;
+        }
+
+        public int getValue() {
+            return value;
+        }
+    }
+
+}

+ 164 - 0
src/main/java/com/futu/course/common/utils/EscapeUtil.java

@@ -0,0 +1,164 @@
+package com.futu.course.common.utils;
+
+/**
+ * 转义和反转义工具类
+ *
+ */
+public class EscapeUtil
+{
+    public static final String RE_HTML_MARK = "(<[^<]*?>)|(<[\\s]*?/[^<]*?>)|(<[^<]*?/[\\s]*?>)";
+
+    private static final char[][] TEXT = new char[64][];
+
+    static
+    {
+        for (int i = 0; i < 64; i++)
+        {
+            TEXT[i] = new char[] { (char) i };
+        }
+
+        // special HTML characters
+        TEXT['\''] = "&#039;".toCharArray(); // 单引号
+        TEXT['"'] = "&#34;".toCharArray(); // 双引号
+        TEXT['&'] = "&#38;".toCharArray(); // &符
+        TEXT['<'] = "&#60;".toCharArray(); // 小于号
+        TEXT['>'] = "&#62;".toCharArray(); // 大于号
+    }
+
+    /**
+     * 转义文本中的HTML字符为安全的字符
+     *
+     * @param text 被转义的文本
+     * @return 转义后的文本
+     */
+    public static String escape(String text)
+    {
+        return encode(text);
+    }
+
+    /**
+     * 还原被转义的HTML特殊字符
+     *
+     * @param content 包含转义符的HTML内容
+     * @return 转换后的字符串
+     */
+    public static String unescape(String content)
+    {
+        return decode(content);
+    }
+
+    /**
+     * 清除所有HTML标签,但是不删除标签内的内容
+     *
+     * @param content 文本
+     * @return 清除标签后的文本
+     */
+    public static String clean(String content)
+    {
+        return new HTMLFilter().filter(content);
+    }
+
+    /**
+     * Escape编码
+     *
+     * @param text 被编码的文本
+     * @return 编码后的字符
+     */
+    private static String encode(String text)
+    {
+        if (StringUtils.isEmpty(text))
+        {
+            return StringUtils.EMPTY;
+        }
+
+        final StringBuilder tmp = new StringBuilder(text.length() * 6);
+        char c;
+        for (int i = 0; i < text.length(); i++)
+        {
+            c = text.charAt(i);
+            if (c < 256)
+            {
+                tmp.append("%");
+                if (c < 16)
+                {
+                    tmp.append("0");
+                }
+                tmp.append(Integer.toString(c, 16));
+            }
+            else
+            {
+                tmp.append("%u");
+                if (c <= 0xfff)
+                {
+                    // issue#I49JU8@Gitee
+                    tmp.append("0");
+                }
+                tmp.append(Integer.toString(c, 16));
+            }
+        }
+        return tmp.toString();
+    }
+
+    /**
+     * Escape解码
+     *
+     * @param content 被转义的内容
+     * @return 解码后的字符串
+     */
+    public static String decode(String content)
+    {
+        if (StringUtils.isEmpty(content))
+        {
+            return content;
+        }
+
+        StringBuilder tmp = new StringBuilder(content.length());
+        int lastPos = 0, pos = 0;
+        char ch;
+        while (lastPos < content.length())
+        {
+            pos = content.indexOf("%", lastPos);
+            if (pos == lastPos)
+            {
+                if (content.charAt(pos + 1) == 'u')
+                {
+                    ch = (char) Integer.parseInt(content.substring(pos + 2, pos + 6), 16);
+                    tmp.append(ch);
+                    lastPos = pos + 6;
+                }
+                else
+                {
+                    ch = (char) Integer.parseInt(content.substring(pos + 1, pos + 3), 16);
+                    tmp.append(ch);
+                    lastPos = pos + 3;
+                }
+            }
+            else
+            {
+                if (pos == -1)
+                {
+                    tmp.append(content.substring(lastPos));
+                    lastPos = content.length();
+                }
+                else
+                {
+                    tmp.append(content.substring(lastPos, pos));
+                    lastPos = pos;
+                }
+            }
+        }
+        return tmp.toString();
+    }
+
+    public static void main(String[] args)
+    {
+        String html = "<script>alert(1);</script>";
+        String escape = EscapeUtil.escape(html);
+        // String html = "<scr<script>ipt>alert(\"XSS\")</scr<script>ipt>";
+        // String html = "<123";
+        // String html = "123>";
+        System.out.println("clean: " + EscapeUtil.clean(html));
+        System.out.println("escape: " + escape);
+        System.out.println("unescape: " + EscapeUtil.unescape(escape));
+    }
+}

+ 565 - 0
src/main/java/com/futu/course/common/utils/HTMLFilter.java

@@ -0,0 +1,565 @@
+package com.futu.course.common.utils;
+
+import java.util.*;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * HTML过滤器,用于去除XSS漏洞隐患。
+ *
+ */
+public final class HTMLFilter
+{
+    /**
+     * regex flag union representing /si modifiers in php
+     **/
+    private static final int REGEX_FLAGS_SI = Pattern.CASE_INSENSITIVE | Pattern.DOTALL;
+    private static final Pattern P_COMMENTS = Pattern.compile("<!--(.*?)-->", Pattern.DOTALL);
+    private static final Pattern P_COMMENT = Pattern.compile("^!--(.*)--$", REGEX_FLAGS_SI);
+    private static final Pattern P_TAGS = Pattern.compile("<(.*?)>", Pattern.DOTALL);
+    private static final Pattern P_END_TAG = Pattern.compile("^/([a-z0-9]+)", REGEX_FLAGS_SI);
+    private static final Pattern P_START_TAG = Pattern.compile("^([a-z0-9]+)(.*?)(/?)$", REGEX_FLAGS_SI);
+    private static final Pattern P_QUOTED_ATTRIBUTES = Pattern.compile("([a-z0-9]+)=([\"'])(.*?)\\2", REGEX_FLAGS_SI);
+    private static final Pattern P_UNQUOTED_ATTRIBUTES = Pattern.compile("([a-z0-9]+)(=)([^\"\\s']+)", REGEX_FLAGS_SI);
+    private static final Pattern P_PROTOCOL = Pattern.compile("^([^:]+):", REGEX_FLAGS_SI);
+    private static final Pattern P_ENTITY = Pattern.compile("&#(\\d+);?");
+    private static final Pattern P_ENTITY_UNICODE = Pattern.compile("&#x([0-9a-f]+);?");
+    private static final Pattern P_ENCODE = Pattern.compile("%([0-9a-f]{2});?");
+    private static final Pattern P_VALID_ENTITIES = Pattern.compile("&([^&;]*)(?=(;|&|$))");
+    private static final Pattern P_VALID_QUOTES = Pattern.compile("(>|^)([^<]+?)(<|$)", Pattern.DOTALL);
+    private static final Pattern P_END_ARROW = Pattern.compile("^>");
+    private static final Pattern P_BODY_TO_END = Pattern.compile("<([^>]*?)(?=<|$)");
+    private static final Pattern P_XML_CONTENT = Pattern.compile("(^|>)([^<]*?)(?=>)");
+    private static final Pattern P_STRAY_LEFT_ARROW = Pattern.compile("<([^>]*?)(?=<|$)");
+    private static final Pattern P_STRAY_RIGHT_ARROW = Pattern.compile("(^|>)([^<]*?)(?=>)");
+    private static final Pattern P_AMP = Pattern.compile("&");
+    private static final Pattern P_QUOTE = Pattern.compile("\"");
+    private static final Pattern P_LEFT_ARROW = Pattern.compile("<");
+    private static final Pattern P_RIGHT_ARROW = Pattern.compile(">");
+    private static final Pattern P_BOTH_ARROWS = Pattern.compile("<>");
+
+    // @xxx could grow large... maybe use sesat's ReferenceMap
+    private static final ConcurrentMap<String, Pattern> P_REMOVE_PAIR_BLANKS = new ConcurrentHashMap<>();
+    private static final ConcurrentMap<String, Pattern> P_REMOVE_SELF_BLANKS = new ConcurrentHashMap<>();
+
+    /**
+     * set of allowed html elements, along with allowed attributes for each element
+     **/
+    private final Map<String, List<String>> vAllowed;
+    /**
+     * counts of open tags for each (allowable) html element
+     **/
+    private final Map<String, Integer> vTagCounts = new HashMap<>();
+
+    /**
+     * html elements which must always be self-closing (e.g. "<img />")
+     **/
+    private final String[] vSelfClosingTags;
+    /**
+     * html elements which must always have separate opening and closing tags (e.g. "<b></b>")
+     **/
+    private final String[] vNeedClosingTags;
+    /**
+     * set of disallowed html elements
+     **/
+    private final String[] vDisallowed;
+    /**
+     * attributes which should be checked for valid protocols
+     **/
+    private final String[] vProtocolAtts;
+    /**
+     * allowed protocols
+     **/
+    private final String[] vAllowedProtocols;
+    /**
+     * tags which should be removed if they contain no content (e.g. "<b></b>" or "<b />")
+     **/
+    private final String[] vRemoveBlanks;
+    /**
+     * entities allowed within html markup
+     **/
+    private final String[] vAllowedEntities;
+    /**
+     * flag determining whether comments are allowed in input String.
+     */
+    private final boolean stripComment;
+    private final boolean encodeQuotes;
+    /**
+     * flag determining whether to try to make tags when presented with "unbalanced" angle brackets (e.g. "<b text </b>"
+     * becomes "<b> text </b>"). If set to false, unbalanced angle brackets will be html escaped.
+     */
+    private final boolean alwaysMakeTags;
+
+    /**
+     * Default constructor.
+     */
+    public HTMLFilter()
+    {
+        vAllowed = new HashMap<>();
+
+        final ArrayList<String> a_atts = new ArrayList<>();
+        a_atts.add("href");
+        a_atts.add("target");
+        vAllowed.put("a", a_atts);
+
+        final ArrayList<String> img_atts = new ArrayList<>();
+        img_atts.add("src");
+        img_atts.add("width");
+        img_atts.add("height");
+        img_atts.add("alt");
+        vAllowed.put("img", img_atts);
+
+        final ArrayList<String> no_atts = new ArrayList<>();
+        vAllowed.put("b", no_atts);
+        vAllowed.put("strong", no_atts);
+        vAllowed.put("i", no_atts);
+        vAllowed.put("em", no_atts);
+
+        vSelfClosingTags = new String[] { "img" };
+        vNeedClosingTags = new String[] { "a", "b", "strong", "i", "em" };
+        vDisallowed = new String[] {};
+        vAllowedProtocols = new String[] { "http", "mailto", "https" }; // no ftp.
+        vProtocolAtts = new String[] { "src", "href" };
+        vRemoveBlanks = new String[] { "a", "b", "strong", "i", "em" };
+        vAllowedEntities = new String[] { "amp", "gt", "lt", "quot" };
+        stripComment = true;
+        encodeQuotes = true;
+        alwaysMakeTags = false;
+    }
+
+    /**
+     * Map-parameter configurable constructor.
+     *
+     * @param conf map containing configuration. keys match field names.
+     */
+    @SuppressWarnings("unchecked")
+    public HTMLFilter(final Map<String, Object> conf)
+    {
+
+        assert conf.containsKey("vAllowed") : "configuration requires vAllowed";
+        assert conf.containsKey("vSelfClosingTags") : "configuration requires vSelfClosingTags";
+        assert conf.containsKey("vNeedClosingTags") : "configuration requires vNeedClosingTags";
+        assert conf.containsKey("vDisallowed") : "configuration requires vDisallowed";
+        assert conf.containsKey("vAllowedProtocols") : "configuration requires vAllowedProtocols";
+        assert conf.containsKey("vProtocolAtts") : "configuration requires vProtocolAtts";
+        assert conf.containsKey("vRemoveBlanks") : "configuration requires vRemoveBlanks";
+        assert conf.containsKey("vAllowedEntities") : "configuration requires vAllowedEntities";
+
+        vAllowed = Collections.unmodifiableMap((HashMap<String, List<String>>) conf.get("vAllowed"));
+        vSelfClosingTags = (String[]) conf.get("vSelfClosingTags");
+        vNeedClosingTags = (String[]) conf.get("vNeedClosingTags");
+        vDisallowed = (String[]) conf.get("vDisallowed");
+        vAllowedProtocols = (String[]) conf.get("vAllowedProtocols");
+        vProtocolAtts = (String[]) conf.get("vProtocolAtts");
+        vRemoveBlanks = (String[]) conf.get("vRemoveBlanks");
+        vAllowedEntities = (String[]) conf.get("vAllowedEntities");
+        stripComment = conf.containsKey("stripComment") ? (Boolean) conf.get("stripComment") : true;
+        encodeQuotes = conf.containsKey("encodeQuotes") ? (Boolean) conf.get("encodeQuotes") : true;
+        alwaysMakeTags = conf.containsKey("alwaysMakeTags") ? (Boolean) conf.get("alwaysMakeTags") : true;
+    }
+
+    private void reset()
+    {
+        vTagCounts.clear();
+    }
+
+    // ---------------------------------------------------------------
+    // my versions of some PHP library functions
+    public static String chr(final int decimal)
+    {
+        return String.valueOf((char) decimal);
+    }
+
+    public static String htmlSpecialChars(final String s)
+    {
+        String result = s;
+        result = regexReplace(P_AMP, "&amp;", result);
+        result = regexReplace(P_QUOTE, "&quot;", result);
+        result = regexReplace(P_LEFT_ARROW, "&lt;", result);
+        result = regexReplace(P_RIGHT_ARROW, "&gt;", result);
+        return result;
+    }
+
+    // ---------------------------------------------------------------
+
+    /**
+     * given a user submitted input String, filter out any invalid or restricted html.
+     *
+     * @param input text (i.e. submitted by a user) than may contain html
+     * @return "clean" version of input, with only valid, whitelisted html elements allowed
+     */
+    public String filter(final String input)
+    {
+        reset();
+        String s = input;
+
+        s = escapeComments(s);
+
+        s = balanceHTML(s);
+
+        s = checkTags(s);
+
+        s = processRemoveBlanks(s);
+
+        // s = validateEntities(s);
+
+        return s;
+    }
+
+    public boolean isAlwaysMakeTags()
+    {
+        return alwaysMakeTags;
+    }
+
+    public boolean isStripComments()
+    {
+        return stripComment;
+    }
+
+    private String escapeComments(final String s)
+    {
+        final Matcher m = P_COMMENTS.matcher(s);
+        final StringBuffer buf = new StringBuffer();
+        if (m.find())
+        {
+            final String match = m.group(1); // (.*?)
+            m.appendReplacement(buf, Matcher.quoteReplacement("<!--" + htmlSpecialChars(match) + "-->"));
+        }
+        m.appendTail(buf);
+
+        return buf.toString();
+    }
+
+    private String balanceHTML(String s)
+    {
+        if (alwaysMakeTags)
+        {
+            //
+            // try and form html
+            //
+            s = regexReplace(P_END_ARROW, "", s);
+            // 不追加结束标签
+            s = regexReplace(P_BODY_TO_END, "<$1>", s);
+            s = regexReplace(P_XML_CONTENT, "$1<$2", s);
+
+        }
+        else
+        {
+            //
+            // escape stray brackets
+            //
+            s = regexReplace(P_STRAY_LEFT_ARROW, "&lt;$1", s);
+            s = regexReplace(P_STRAY_RIGHT_ARROW, "$1$2&gt;<", s);
+
+            //
+            // the last regexp causes '<>' entities to appear
+            // (we need to do a lookahead assertion so that the last bracket can
+            // be used in the next pass of the regexp)
+            //
+            s = regexReplace(P_BOTH_ARROWS, "", s);
+        }
+
+        return s;
+    }
+
+    private String checkTags(String s)
+    {
+        Matcher m = P_TAGS.matcher(s);
+
+        final StringBuffer buf = new StringBuffer();
+        while (m.find())
+        {
+            String replaceStr = m.group(1);
+            replaceStr = processTag(replaceStr);
+            m.appendReplacement(buf, Matcher.quoteReplacement(replaceStr));
+        }
+        m.appendTail(buf);
+
+        // these get tallied in processTag
+        // (remember to reset before subsequent calls to filter method)
+        final StringBuilder sBuilder = new StringBuilder(buf.toString());
+        for (String key : vTagCounts.keySet())
+        {
+            for (int ii = 0; ii < vTagCounts.get(key); ii++)
+            {
+                sBuilder.append("</").append(key).append(">");
+            }
+        }
+        s = sBuilder.toString();
+
+        return s;
+    }
+
+    private String processRemoveBlanks(final String s)
+    {
+        String result = s;
+        for (String tag : vRemoveBlanks)
+        {
+            if (!P_REMOVE_PAIR_BLANKS.containsKey(tag))
+            {
+                P_REMOVE_PAIR_BLANKS.putIfAbsent(tag, Pattern.compile("<" + tag + "(\\s[^>]*)?></" + tag + ">"));
+            }
+            result = regexReplace(P_REMOVE_PAIR_BLANKS.get(tag), "", result);
+            if (!P_REMOVE_SELF_BLANKS.containsKey(tag))
+            {
+                P_REMOVE_SELF_BLANKS.putIfAbsent(tag, Pattern.compile("<" + tag + "(\\s[^>]*)?/>"));
+            }
+            result = regexReplace(P_REMOVE_SELF_BLANKS.get(tag), "", result);
+        }
+
+        return result;
+    }
+
+    private static String regexReplace(final Pattern regex_pattern, final String replacement, final String s)
+    {
+        Matcher m = regex_pattern.matcher(s);
+        return m.replaceAll(replacement);
+    }
+
+    private String processTag(final String s)
+    {
+        // ending tags
+        Matcher m = P_END_TAG.matcher(s);
+        if (m.find())
+        {
+            final String name = m.group(1).toLowerCase();
+            if (allowed(name))
+            {
+                if (!inArray(name, vSelfClosingTags))
+                {
+                    if (vTagCounts.containsKey(name))
+                    {
+                        vTagCounts.put(name, vTagCounts.get(name) - 1);
+                        return "</" + name + ">";
+                    }
+                }
+            }
+        }
+
+        // starting tags
+        m = P_START_TAG.matcher(s);
+        if (m.find())
+        {
+            final String name = m.group(1).toLowerCase();
+            final String body = m.group(2);
+            String ending = m.group(3);
+
+            // debug( "in a starting tag, name='" + name + "'; body='" + body + "'; ending='" + ending + "'" );
+            if (allowed(name))
+            {
+                final StringBuilder params = new StringBuilder();
+
+                final Matcher m2 = P_QUOTED_ATTRIBUTES.matcher(body);
+                final Matcher m3 = P_UNQUOTED_ATTRIBUTES.matcher(body);
+                final List<String> paramNames = new ArrayList<>();
+                final List<String> paramValues = new ArrayList<>();
+                while (m2.find())
+                {
+                    paramNames.add(m2.group(1)); // ([a-z0-9]+)
+                    paramValues.add(m2.group(3)); // (.*?)
+                }
+                while (m3.find())
+                {
+                    paramNames.add(m3.group(1)); // ([a-z0-9]+)
+                    paramValues.add(m3.group(3)); // ([^\"\\s']+)
+                }
+
+                String paramName, paramValue;
+                for (int ii = 0; ii < paramNames.size(); ii++)
+                {
+                    paramName = paramNames.get(ii).toLowerCase();
+                    paramValue = paramValues.get(ii);
+
+                    // debug( "paramName='" + paramName + "'" );
+                    // debug( "paramValue='" + paramValue + "'" );
+                    // debug( "allowed? " + vAllowed.get( name ).contains( paramName ) );
+
+                    if (allowedAttribute(name, paramName))
+                    {
+                        if (inArray(paramName, vProtocolAtts))
+                        {
+                            paramValue = processParamProtocol(paramValue);
+                        }
+                        params.append(' ').append(paramName).append("=\\\"").append(paramValue).append("\"");
+                    }
+                }
+
+                if (inArray(name, vSelfClosingTags))
+                {
+                    ending = " /";
+                }
+
+                if (inArray(name, vNeedClosingTags))
+                {
+                    ending = "";
+                }
+
+                if (ending == null || ending.length() < 1)
+                {
+                    if (vTagCounts.containsKey(name))
+                    {
+                        vTagCounts.put(name, vTagCounts.get(name) + 1);
+                    }
+                    else
+                    {
+                        vTagCounts.put(name, 1);
+                    }
+                }
+                else
+                {
+                    ending = " /";
+                }
+                return "<" + name + params + ending + ">";
+            }
+            else
+            {
+                return "";
+            }
+        }
+
+        // comments
+        m = P_COMMENT.matcher(s);
+        if (!stripComment && m.find())
+        {
+            return "<" + m.group() + ">";
+        }
+
+        return "";
+    }
+
+    private String processParamProtocol(String s)
+    {
+        s = decodeEntities(s);
+        final Matcher m = P_PROTOCOL.matcher(s);
+        if (m.find())
+        {
+            final String protocol = m.group(1);
+            if (!inArray(protocol, vAllowedProtocols))
+            {
+                // bad protocol, turn into local anchor link instead
+                s = "#" + s.substring(protocol.length() + 1);
+                if (s.startsWith("#//"))
+                {
+                    s = "#" + s.substring(3);
+                }
+            }
+        }
+
+        return s;
+    }
+
+    private String decodeEntities(String s)
+    {
+        StringBuffer buf = new StringBuffer();
+
+        Matcher m = P_ENTITY.matcher(s);
+        while (m.find())
+        {
+            final String match = m.group(1);
+            final int decimal = Integer.decode(match).intValue();
+            m.appendReplacement(buf, Matcher.quoteReplacement(chr(decimal)));
+        }
+        m.appendTail(buf);
+        s = buf.toString();
+
+        buf = new StringBuffer();
+        m = P_ENTITY_UNICODE.matcher(s);
+        while (m.find())
+        {
+            final String match = m.group(1);
+            final int decimal = Integer.valueOf(match, 16).intValue();
+            m.appendReplacement(buf, Matcher.quoteReplacement(chr(decimal)));
+        }
+        m.appendTail(buf);
+        s = buf.toString();
+
+        buf = new StringBuffer();
+        m = P_ENCODE.matcher(s);
+        while (m.find())
+        {
+            final String match = m.group(1);
+            final int decimal = Integer.valueOf(match, 16).intValue();
+            m.appendReplacement(buf, Matcher.quoteReplacement(chr(decimal)));
+        }
+        m.appendTail(buf);
+        s = buf.toString();
+
+        s = validateEntities(s);
+        return s;
+    }
+
+    private String validateEntities(final String s)
+    {
+        StringBuffer buf = new StringBuffer();
+
+        // validate entities throughout the string
+        Matcher m = P_VALID_ENTITIES.matcher(s);
+        while (m.find())
+        {
+            final String one = m.group(1); // ([^&;]*)
+            final String two = m.group(2); // (?=(;|&|$))
+            m.appendReplacement(buf, Matcher.quoteReplacement(checkEntity(one, two)));
+        }
+        m.appendTail(buf);
+
+        return encodeQuotes(buf.toString());
+    }
+
+    private String encodeQuotes(final String s)
+    {
+        if (encodeQuotes)
+        {
+            StringBuffer buf = new StringBuffer();
+            Matcher m = P_VALID_QUOTES.matcher(s);
+            while (m.find())
+            {
+                final String one = m.group(1); // (>|^)
+                final String two = m.group(2); // ([^<]+?)
+                final String three = m.group(3); // (<|$)
+                // 不替换双引号为&quot;,防止json格式无效 regexReplace(P_QUOTE, "&quot;", two)
+                m.appendReplacement(buf, Matcher.quoteReplacement(one + two + three));
+            }
+            m.appendTail(buf);
+            return buf.toString();
+        }
+        else
+        {
+            return s;
+        }
+    }
+
+    private String checkEntity(final String preamble, final String term)
+    {
+
+        return ";".equals(term) && isValidEntity(preamble) ? '&' + preamble : "&amp;" + preamble;
+    }
+
+    private boolean isValidEntity(final String entity)
+    {
+        return inArray(entity, vAllowedEntities);
+    }
+
+    private static boolean inArray(final String s, final String[] array)
+    {
+        for (String item : array)
+        {
+            if (item != null && item.equals(s))
+            {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private boolean allowed(final String name)
+    {
+        return (vAllowed.isEmpty() || vAllowed.containsKey(name)) && !inArray(name, vDisallowed);
+    }
+
+    private boolean allowedAttribute(final String name, final String paramName)
+    {
+        return allowed(name) && (vAllowed.isEmpty() || vAllowed.get(name).contains(paramName));
+    }
+}

+ 56 - 0
src/main/java/com/futu/course/common/utils/HttpHelper.java

@@ -0,0 +1,56 @@
+package com.futu.course.common.utils;
+
+
+import org.apache.commons.lang3.exception.ExceptionUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.servlet.ServletRequest;
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.nio.charset.StandardCharsets;
+
+/**
+ * 通用http工具封装
+ *
+ */
+public class HttpHelper
+{
+    private static final Logger LOGGER = LoggerFactory.getLogger(HttpHelper.class);
+
+    public static String getBodyString(ServletRequest request)
+    {
+        StringBuilder sb = new StringBuilder();
+        BufferedReader reader = null;
+        try (InputStream inputStream = request.getInputStream())
+        {
+            reader = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8));
+            String line = "";
+            while ((line = reader.readLine()) != null)
+            {
+                sb.append(line);
+            }
+        }
+        catch (IOException e)
+        {
+            LOGGER.warn("getBodyString出现问题!");
+        }
+        finally
+        {
+            if (reader != null)
+            {
+                try
+                {
+                    reader.close();
+                }
+                catch (IOException e)
+                {
+                    LOGGER.error(ExceptionUtils.getMessage(e));
+                }
+            }
+        }
+        return sb.toString();
+    }
+}

+ 622 - 0
src/main/java/com/futu/course/common/utils/HttpUtil.java

@@ -0,0 +1,622 @@
+package com.futu.course.common.utils;
+
+
+import com.futu.course.common.entity.HttpResult;
+import org.apache.commons.codec.net.URLCodec;
+import org.apache.commons.httpclient.*;
+import org.apache.commons.httpclient.methods.GetMethod;
+import org.apache.commons.httpclient.methods.HeadMethod;
+import org.apache.commons.httpclient.methods.PostMethod;
+import org.apache.commons.httpclient.methods.StringRequestEntity;
+import org.apache.commons.httpclient.methods.multipart.MultipartRequestEntity;
+import org.apache.commons.httpclient.methods.multipart.Part;
+import org.apache.commons.httpclient.params.HttpConnectionManagerParams;
+import org.apache.commons.httpclient.params.HttpMethodParams;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.X509TrustManager;
+import java.io.*;
+import java.net.HttpURLConnection;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.zip.GZIPInputStream;
+
+/**
+ * http 请求工具类
+ *
+ */
+public class HttpUtil {
+
+	private static final Log logger = LogFactory.getLog(HttpUtil.class);
+
+	private HttpClient httpClient = null;
+
+ 	private static final HttpUtil instance = new HttpUtil();
+
+	public static HttpUtil getInstance() {
+		return instance;
+		//return new HttpUtil();
+	}
+
+	private HttpUtil() {
+		if (httpClient == null) {
+			MultiThreadedHttpConnectionManager conn = new MultiThreadedHttpConnectionManager();
+			HttpConnectionManagerParams params = conn.getParams();
+			params.setConnectionTimeout(120000); // 连接超时 时间 20秒
+			params.setSoTimeout(60000); // 已连接上,但读取超时 时间 30秒
+			params.setDefaultMaxConnectionsPerHost(1024);
+			params.setMaxTotalConnections(2048);
+			params.setReceiveBufferSize(4096);
+			params.setSendBufferSize(4096);
+			httpClient = new HttpClient(conn);
+			httpClient.getHttpConnectionManager().setParams(params);
+
+			httpClient.getParams().setParameter(HttpMethodParams.RETRY_HANDLER,
+					new DefaultHttpMethodRetryHandler());
+			httpClient.getParams().setVersion(HttpVersion.HTTP_1_1);
+		}
+	}
+
+	private NameValuePair[] map2Data(Map<String, String> data) {
+		int count = 0;
+
+		NameValuePair[] pairs = new NameValuePair[data.size()];
+		for (Iterator<String> it = data.keySet().iterator(); it.hasNext();) {
+			String key = it.next();
+			String value = data.get(key);
+
+			pairs[count] = new NameValuePair(key, value);
+
+			count++;
+		}
+
+		return pairs;
+	}
+
+	public HttpResult post(String url, String charset, Map<String, String> data)
+			throws Exception {
+		return post(url, charset, data, null);
+	}
+
+	public HttpResult post(String url, String charset, NameValuePair[] data)
+			throws Exception {
+		return post(url, charset, data, null);
+	}
+
+	public HttpResult post(String url, String charset,
+			Map<String, String> param, Map<String, String> headerMap)
+			throws Exception {
+
+		if (param != null) {
+			NameValuePair[] data = map2Data(param);
+			return post(url, charset, data, headerMap);
+		} else {
+			return post(url, charset, new NameValuePair[0], headerMap);
+		}
+	}
+
+//	public static HttpResult postByte(String url,String charset,byte[]data,Map<String,String> headerMap) throws Exception {
+//
+//	String content= post(url, data, headerMap);
+//
+//
+//	}
+    protected static final int SOCKET_TIMEOUT = 10000; // 10S
+	  public  HttpResult postByte(String host, byte[]data,Map<String,String> headerMap) {
+	        try {
+	            // 设置SSLContext
+	          //  SSLContext sslcontext = SSLContext.getInstance("TLS");
+	           // sslcontext.init(null, new TrustManager[] { myX509TrustManager }, null);
+
+	         //   String sendUrl = getUrlWithQueryString(host, params);
+
+	            // System.out.println("URL:" + sendUrl);
+
+	            URL uri = new URL(host); // 创建URL对象
+
+	            HttpURLConnection conn = (HttpURLConnection) uri.openConnection();
+
+	            if(!(null==headerMap&&headerMap.isEmpty())) {
+
+	            	for(Map.Entry<String, String> entry: headerMap.entrySet())
+	                {
+
+	                 conn.setRequestProperty( entry.getKey(), entry.getValue());
+	                }
+
+	            }
+
+
+
+//	            if (conn instanceof HttpsURLConnection) {
+//	                ((HttpsURLConnection) conn).setSSLSocketFactory(sslcontext.getSocketFactory());
+//	            }
+
+	            conn.setConnectTimeout(SOCKET_TIMEOUT); // 设置相应超时
+	            conn.setRequestMethod("POST");
+	            conn.setDoOutput(true);
+	          OutputStream out=  conn.getOutputStream();
+	          out.write(data);
+	          out.flush();
+	            int statusCode = conn.getResponseCode();
+	            if (statusCode != HttpURLConnection.HTTP_OK) {
+	                System.out.println("Http错误码:" + statusCode);
+	            }
+
+	            // 读取服务器的数据
+	            InputStream is = conn.getInputStream();
+	            BufferedReader br = new BufferedReader(new InputStreamReader(is));
+	            StringBuilder builder = new StringBuilder();
+	            String line = null;
+	            while ((line = br.readLine()) != null) {
+	                builder.append(line);
+	            }
+
+	            String text = builder.toString();
+
+	            close(br); // 关闭数据流
+	            close(is); // 关闭数据流
+	            conn.disconnect(); // 断开连接
+	            HttpResult httpResult = new HttpResult();
+				httpResult.setStateCode(statusCode);
+				httpResult.setContent(text);
+				return httpResult;
+
+	        } catch (MalformedURLException e) {
+	            e.printStackTrace();
+	        } catch (IOException e) {
+	            e.printStackTrace();
+	        }
+
+	        return null;
+
+
+	    }
+
+	  protected static void close(Closeable closeable) {
+	        if (closeable != null) {
+	            try {
+	                closeable.close();
+	            } catch (IOException e) {
+	                e.printStackTrace();
+	            }
+	        }
+	    }
+	  private static final TrustManager myX509TrustManager = new X509TrustManager() {
+
+	        @Override
+	        public X509Certificate[] getAcceptedIssuers() {
+	            return null;
+	        }
+
+	        @Override
+	        public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
+	        }
+
+	        @Override
+	        public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
+	        }
+	    };
+
+	public HttpResult post(String url, String charset, NameValuePair[] data,
+			Map<String, String> headerMap) throws Exception {
+		PostMethod post = new PostMethod(url);
+
+		HttpMethodParams hmp = new HttpMethodParams();
+		hmp.setContentCharset(charset);
+		post.setParams(hmp);
+		setRequestHeader(post, headerMap);
+		if (data != null) {
+			post.setRequestBody(data);
+
+		}
+		return getResponseBodyAsStream(post, charset);
+	}
+
+	public HttpResult post(String url, String charset,
+						   Map<String, String> param,String body, Map<String, String> headerMap)
+			throws Exception {
+
+		if (param != null) {
+			NameValuePair[] data = map2Data(param);
+			return post(url, charset, data, headerMap);
+		} else {
+			return post(url, charset, new NameValuePair[0],body, headerMap);
+		}
+	}
+	public HttpResult post(String url, String charset, NameValuePair[] data,String body,
+						   Map<String, String> headerMap) throws Exception {
+		PostMethod post = new PostMethod(url);
+
+		HttpMethodParams hmp = new HttpMethodParams();
+		hmp.setContentCharset(charset);
+		post.setParams(hmp);
+		setRequestHeader(post, headerMap);
+		if (data != null) {
+			post.setRequestBody(data);
+
+		}
+		return getResponseBodyAsStream(post, charset);
+	}
+
+	public HttpResult requestFront(String url,String charset, Map<String, String> headerMap ,String text) throws Exception {
+		PostMethod post = new PostMethod(url);
+		HttpMethodParams hmp = new HttpMethodParams();
+		hmp.setContentCharset(charset);
+		post.setParams(hmp);
+		setRequestHeader(post, headerMap);
+		if (text != null) {
+			post.setRequestEntity(new StringRequestEntity(text));
+		}
+		return getResponseBodyAsStream(post, charset);
+	}
+
+
+
+	public HttpResult postJson(String url, String json) throws Exception {
+		String charset = "UTF-8";
+		PostMethod post = new PostMethod(url);
+		HttpMethodParams hmp = new HttpMethodParams();
+		hmp.setContentCharset(charset);
+		post.setParams(hmp);
+		Map<String, String> headerMap = new HashMap<String, String>();
+		headerMap.put("Content-Type", "application/json");
+		setRequestHeader(post, headerMap);
+		post.setRequestEntity(new StringRequestEntity(json, "application/json;charset=UTF-8", charset));
+		return getResponseBodyAsStream(post, charset);
+	}
+
+	public HttpResult postJson(String url, String json,Map<String, String> headerMap) throws Exception {
+		String charset = "UTF-8";
+		PostMethod post = new PostMethod(url);
+		HttpMethodParams hmp = new HttpMethodParams();
+		hmp.setContentCharset(charset);
+		post.setParams(hmp);
+		//Map<String, String> headerMap = new HashMap<String, String>();
+		headerMap.put("Content-Type", "application/json");
+		headerMap.put("Accept", "application/json;charset=UTF-8");
+
+		//logger.info("postJson headerMap size="+headerMap.size());
+
+		setRequestHeader(post, headerMap);
+		post.setRequestEntity(new StringRequestEntity(json, "application/json;charset=UTF-8", charset));
+		return getResponseBodyAsStream(post, charset);
+	}
+
+
+	public HttpResult postText(String url, String json,String charset) throws Exception {
+		PostMethod post = new PostMethod(url);
+		HttpMethodParams hmp = new HttpMethodParams();
+		hmp.setContentCharset(charset);
+		post.setParams(hmp);
+		Map<String, String> headerMap = new HashMap<String, String>();
+		headerMap.put("Content-Type", "application/x-www-form-urlencoded");
+		setRequestHeader(post, headerMap);
+		post.setRequestEntity(new StringRequestEntity(json, "application/x-www-form-urlencoded", charset));
+		return getResponseBodyAsStream(post, charset);
+	}
+
+	public HttpResult postJson(String url, String json,String Authorization) throws Exception {
+		String charset = "UTF-8";
+		PostMethod post = new PostMethod(url);
+		HttpMethodParams hmp = new HttpMethodParams();
+		hmp.setContentCharset(charset);
+		post.setParams(hmp);
+		Map<String, String> headerMap = new HashMap<String, String>();
+		headerMap.put("Content-Type", "application/json");
+		headerMap.put("Authorization", Authorization);
+		setRequestHeader(post, headerMap);
+		post.setRequestEntity(new StringRequestEntity(json, "text/json", charset));
+		return getResponseBodyAsStream(post, charset);
+	}
+
+	public HttpResult postXml(String url, String charset, String xml)
+			throws Exception {
+		return postXml(url, charset, xml, null);
+	}
+
+	public HttpResult postXml(String url, String charset, String xml,
+			Map<String, String> headerMap) throws Exception {
+		PostMethod post = new PostMethod(url);
+		HttpMethodParams hmp = new HttpMethodParams();
+		hmp.setContentCharset(charset);
+		post.setParams(hmp);
+		setRequestHeader(post, headerMap);
+		post.setRequestEntity(new StringRequestEntity(xml, "text/xml", charset));
+		return getResponseBodyAsStream(post, charset);
+	}
+
+	public HttpResult postFile(String url, String charset,
+			NameValuePair[] data, Part[] parts) throws Exception {
+		return postFile(url, charset, data, parts, null);
+	}
+
+	public HttpResult postFile(String url, String charset,
+			NameValuePair[] data, Part[] parts, Map<String, String> headerMap)
+			throws Exception {
+		PostMethod post = new PostMethod(url);
+		HttpMethodParams hmp = new HttpMethodParams();
+		hmp.setContentCharset(charset);
+		post.setParams(hmp);
+		setRequestHeader(post, headerMap);
+		if (data != null) {
+			post.setRequestBody(data);
+		}
+		if (parts != null) {
+			post.setRequestEntity(new MultipartRequestEntity(parts, post
+					.getParams()));
+		}
+		return getResponseBodyAsStream(post, charset);
+	}
+
+	/**
+	 * 根据指定的url和字符集发起http
+	 * GET请求,并返回HttpResult,通过HttpResult.getContent()可以获取返回结果的内容 <br>
+	 * author:sl <br>
+	 * date:Dec 28, 2011 7:22:40 PM <br>
+	 * version:V1.0.0
+	 *
+	 * @param url
+	 *            要通过GET方式访问的url
+	 * @param charset
+	 *            指定字符集
+	 * @return
+	 * @throws Exception
+	 *             : <br>
+	 *             ModifyRecord: <br>
+	 *             1、ibm - Dec 28, 2011 7:22:40 PM :
+	 */
+	public HttpResult get(String url, String charset) throws Exception {
+		return get(url, charset, null);
+	}
+
+	public HttpResult get(String url, String charset,
+			Map<String, String> headerMap) throws Exception {
+
+		GetMethod get = new CustomGetMethod(url);
+
+		HttpMethodParams hmp = new HttpMethodParams();
+		hmp.setContentCharset(charset);
+		setRequestHeader(get, headerMap);
+		get.setParams(hmp);
+
+		int result;
+		String sresult;
+		try {
+			result = httpClient.executeMethod(get);
+
+			sresult = get.getResponseBodyAsString();
+
+		} finally {
+			if (get != null) {
+				get.releaseConnection();
+			}
+			get = null;
+		}
+		HttpResult httpResult = new HttpResult();
+		httpResult.setStateCode(result);
+		httpResult.setContent(sresult);
+		return httpResult;
+	}
+
+	public int head(String url) throws Exception {
+		return head(url, null);
+	}
+
+	public int head(String url, Map<String, String> headerMap) throws Exception {
+		HeadMethod head = new HeadMethod(url);
+		int stateCode = 0;
+		try {
+			setRequestHeader(head, headerMap);
+			stateCode = httpClient.executeMethod(head);
+		} finally {
+			if (head != null) {
+				head.releaseConnection();
+			}
+		}
+		return stateCode;
+	}
+
+	private void setRequestHeader(HttpMethod method, Map<String, String> map) {
+		// method.setRequestHeader("User-Agent", "Mozilla/4.0 (compatible; MSIE
+		// 6.0; Windows NT 5.1; SV1)");
+		if (map != null) {
+			for (Map.Entry<String, String> m : map.entrySet()) {
+				method.setRequestHeader(m.getKey(), m.getValue());
+			}
+		}
+	}
+
+	/**
+	 * 下载文件到指定路径
+	 *
+	 * @param url
+	 * @param savePathFileName
+	 * @throws Exception
+	 */
+	public void getAndSaveData(String url, String savePathFileName)
+			throws Exception {
+		GetMethod httpMethod = new GetMethod(url);
+		HttpMethodParams hmp = new HttpMethodParams();
+		httpMethod.setParams(hmp);
+		BufferedInputStream in = null;
+		BufferedOutputStream fos = null;
+		File file = null;
+		try {
+			file = new File(savePathFileName);
+			// 建立文件目录结构
+			file.getParentFile().mkdirs();
+			fos = new BufferedOutputStream(new FileOutputStream(file));
+			int result = httpClient.executeMethod(httpMethod);
+			if (result == 200 || result == 301 || result == 302) {
+				in = new BufferedInputStream(
+						httpMethod.getResponseBodyAsStream());
+				byte[] b = new byte[4096];
+				int count;
+				while ((count = in.read(b)) > 0) {
+					fos.write(b, 0, count);
+				}
+				fos.flush();
+			} else {
+				throw new Exception("出错,错误http状态代码:" + result + "  url=" + url
+						+ "  savePathFileName=" + savePathFileName);
+			}
+		} finally {
+			if (httpMethod != null) {
+				httpMethod.releaseConnection();
+			}
+			if (in != null) {
+				try {
+					in.close();
+				} catch (IOException e) {
+					logger.error(e.getMessage(), e);
+				}
+				in = null;
+			}
+			if (fos != null) {
+				try {
+					fos.close();
+				} catch (Exception e) {
+					logger.error(e.getMessage(), e);
+				}
+				fos = null;
+			}
+			if (file != null) {
+				file = null;
+			}
+		}
+	}
+
+	private HttpResult getResponseBodyAsStream(HttpMethod httpMethod,
+			String charset) throws Exception {
+		StringBuffer sb = new StringBuffer();
+		int result;
+		BufferedReader in = null;
+		try {
+			result = httpClient.executeMethod(httpMethod);
+			in = new BufferedReader(new InputStreamReader(
+					httpMethod.getResponseBodyAsStream(), charset));
+			String line = null;
+			while ((line = in.readLine()) != null) {
+				sb.append(line);
+			}
+		} finally {
+			if (httpMethod != null) {
+				httpMethod.releaseConnection();
+			}
+			if (in != null) {
+				try {
+					in.close();
+				} catch (IOException e) {
+					logger.error(e.getMessage(), e);
+				}
+				in = null;
+			}
+		}
+		HttpResult httpResult = new HttpResult();
+		httpResult.setStateCode(result);
+		httpResult.setContent(sb.toString());
+		return httpResult;
+	}
+
+
+	/**
+	 * 对参数按指定的字符集进行编码,形成http请求的参数格式,例如:
+	 * test=1&UserID=1&CafeID=0&CafeIP=&ServerIP=&ClientIP=127.0.0.1 <br>
+	 * author:sl <br>
+	 * date:Dec 28, 2011 7:12:44 PM <br>
+	 * version:V1.0.0
+	 *
+	 * @param pairs
+	 *            参数对
+	 * @param charset
+	 *            字符集,例如:GBK
+	 * @return
+	 *         返回编码后的http请求参数串,例如:test=1&UserID=1&CafeID=0&CafeIP=&ServerIP=&ClientIP
+	 *         =127.0.0.1
+	 * @throws UnsupportedEncodingException
+	 *             : <br>
+	 *             ModifyRecord: <br>
+	 *             1、ibm - Dec 28, 2011 7:12:44 PM :
+	 */
+	public static String doFormUrlEncode(NameValuePair[] pairs, String charset)
+			throws UnsupportedEncodingException {
+		StringBuffer buf = new StringBuffer();
+		for (int i = 0; i < pairs.length; i++) {
+			URLCodec codec = new URLCodec();
+			NameValuePair pair = pairs[i];
+			if (pair.getName() != null) {
+				if (i > 0) {
+					buf.append("&");
+				}
+				buf.append(codec.encode(pair.getName(), charset));// 对参数名称进行编码
+				buf.append("=");
+				if (pair.getValue() != null) {
+					buf.append(codec.encode(pair.getValue(), charset));// 对参数值进行编码
+				}
+			}
+		}
+		return buf.toString();
+	}
+
+	class CustomGetMethod extends GetMethod {
+
+		public CustomGetMethod(String uri) {
+			super(uri);
+		}
+
+		/**
+		 * Get response as string whether response is GZipped or not
+		 *
+		 * @return
+		 * @throws IOException
+		 */
+		@Override
+		public String getResponseBodyAsString() throws IOException {
+
+			if (getResponseBody() == null && getResponseStream() == null)
+				return null;
+
+			if (!isGziped())
+				return super.getResponseBodyAsString();
+
+			StringBuffer sb = new StringBuffer();
+			GZIPInputStream gzin = null;
+			InputStreamReader isr = null;
+			BufferedReader br = null;
+			// For GZip response
+			try {
+				gzin = new GZIPInputStream(getResponseBodyAsStream());
+
+				isr = new InputStreamReader(gzin, getResponseCharSet());
+				br = new BufferedReader(isr);
+
+				String tempbf;
+				while ((tempbf = br.readLine()) != null) {
+					sb.append(tempbf);
+					sb.append("\r\n");
+				}
+			}catch(Exception ex){
+				System.out.println(ex.getMessage());
+			}
+			return sb.toString();
+		}
+
+		private boolean isGziped() {
+			return getResponseHeader("Content-Encoding") != null
+					&& getResponseHeader("Content-Encoding").getValue()
+					.toLowerCase().indexOf("gzip") > -1;
+		}
+
+	}
+
+
+
+}

+ 110 - 0
src/main/java/com/futu/course/common/utils/PageUtils.java

@@ -0,0 +1,110 @@
+/**
+ * Copyright (c) 2016-2019 人人开源 All rights reserved.
+ *
+ * https://www.renren.io
+ *
+ * 版权所有,侵权必究!
+ */
+
+package com.futu.course.common.utils;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * 分页工具类
+ *
+ * @author Mark sunlightcs@gmail.com
+ */
+public class PageUtils implements Serializable {
+	private static final long serialVersionUID = 1L;
+	/**
+	 * 总记录数
+	 */
+	private int totalCount;
+	/**
+	 * 每页记录数
+	 */
+	private int pageSize;
+	/**
+	 * 总页数
+	 */
+	private int totalPage;
+	/**
+	 * 当前页数
+	 */
+	private int currPage;
+	/**
+	 * 列表数据
+	 */
+	private List<?> list;
+
+	/**
+	 * 分页
+	 * @param list        列表数据
+	 * @param totalCount  总记录数
+	 * @param pageSize    每页记录数
+	 * @param currPage    当前页数
+	 */
+	public PageUtils(List<?> list, int totalCount, int pageSize, int currPage) {
+		this.list = list;
+		this.totalCount = totalCount;
+		this.pageSize = pageSize;
+		this.currPage = currPage;
+		this.totalPage = (int)Math.ceil((double)totalCount/pageSize);
+	}
+
+	/**
+	 * 分页
+	 */
+	public PageUtils(IPage<?> page) {
+		this.list = page.getRecords();
+		this.totalCount = (int)page.getTotal();
+		this.pageSize = (int)page.getSize();
+		this.currPage = (int)page.getCurrent();
+		this.totalPage = (int)page.getPages();
+	}
+
+	public int getTotalCount() {
+		return totalCount;
+	}
+
+	public void setTotalCount(int totalCount) {
+		this.totalCount = totalCount;
+	}
+
+	public int getPageSize() {
+		return pageSize;
+	}
+
+	public void setPageSize(int pageSize) {
+		this.pageSize = pageSize;
+	}
+
+	public int getTotalPage() {
+		return totalPage;
+	}
+
+	public void setTotalPage(int totalPage) {
+		this.totalPage = totalPage;
+	}
+
+	public int getCurrPage() {
+		return currPage;
+	}
+
+	public void setCurrPage(int currPage) {
+		this.currPage = currPage;
+	}
+
+	public List<?> getList() {
+		return list;
+	}
+
+	public void setList(List<?> list) {
+		this.list = list;
+	}
+
+}

+ 287 - 0
src/main/java/com/futu/course/common/utils/RedisCache.java

@@ -0,0 +1,287 @@
+package com.futu.course.common.utils;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.redis.core.BoundSetOperations;
+import org.springframework.data.redis.core.HashOperations;
+import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.data.redis.core.ValueOperations;
+import org.springframework.stereotype.Component;
+
+import java.util.*;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * spring redis 工具类
+ **/
+@SuppressWarnings(value = {"unchecked", "rawtypes"})
+@Component
+public class RedisCache {
+    @Autowired
+    public RedisTemplate redisTemplate;
+
+    /**
+     * 缓存基本的对象,Integer、String、实体类等
+     *
+     * @param key   缓存的键值
+     * @param value 缓存的值
+     */
+    public <T> void setCacheObject(final String key, final T value) {
+        redisTemplate.opsForValue().set(key, value);
+    }
+
+    /**
+     * 缓存基本的对象,Integer、String、实体类等
+     *
+     * @param key      缓存的键值
+     * @param value    缓存的值
+     * @param timeout  时间
+     * @param timeUnit 时间颗粒度
+     */
+    public <T> void setCacheObject(final String key, final T value, final Integer timeout, final TimeUnit timeUnit) {
+        redisTemplate.opsForValue().set(key, value, timeout, timeUnit);
+    }
+
+    public <T> void setCacheObject(final String key, final T value, final Integer timeout) {
+        redisTemplate.opsForValue().set(key, value, timeout, TimeUnit.SECONDS);
+    }
+
+    /**
+     * 判断key是否存在
+     *
+     * @param key 键
+     * @return true 存在 false不存在
+     */
+    public boolean hasKey(String key) {
+        try {
+            return redisTemplate.hasKey(key);
+        } catch (Exception e) {
+            e.printStackTrace();
+            return false;
+        }
+    }
+
+
+
+    /**
+     * 加锁,⽆阻塞
+     * <p>
+     * 锁
+     *
+     * @param key        请求标识
+     * @param userId  超期时间
+     * @param expireTime 上锁时间,单位TimeUnit.SECONDS
+     * @param timeout 等待时间,单位TimeUnit.SECONDS
+     * @return
+     */
+    public Boolean lock(String key, long expireTime, Long userId, long timeout) {
+        Long start = System.currentTimeMillis();
+        //在⼀定时间内获取锁,超时则返回错误
+//
+        for (; ; ) {
+//,则证明获取锁成功
+//Set
+            //     OK
+            Boolean ret = redisTemplate.opsForValue().setIfAbsent(key, userId, expireTime, TimeUnit.SECONDS);
+            if (ret != null && ret) {
+                return true;
+            }
+            //  否则循环等待,在
+            // 时间内仍未获取到锁,则获取失败
+            long end = System.currentTimeMillis() - start;
+            if (end >= timeout * 1000) {
+                return false;
+            }
+        }
+    }
+    public Boolean lock(String key, long expireTime, Long userId) {
+        Boolean ret = redisTemplate.opsForValue().setIfAbsent(key, userId, expireTime, TimeUnit.SECONDS);
+        return ret != null && ret;
+    }
+
+    public Boolean unlock(String key, Long userId) {
+        Object o = redisTemplate.opsForValue().get(key);
+        if(userId.equals(o)){
+            return redisTemplate.delete(key);
+        }else{
+            return false;
+        }
+    }
+
+    /**
+     * 设置有效时间
+     *
+     * @param key     Redis键
+     * @param timeout 超时时间
+     * @return true=设置成功;false=设置失败
+     */
+    public boolean expire(final String key, final long timeout) {
+        return expire(key, timeout, TimeUnit.SECONDS);
+    }
+
+    /**
+     * 设置有效时间
+     *
+     * @param key     Redis键
+     * @param timeout 超时时间
+     * @param unit    时间单位
+     * @return true=设置成功;false=设置失败
+     */
+    public boolean expire(final String key, final long timeout, final TimeUnit unit) {
+        return redisTemplate.expire(key, timeout, unit);
+    }
+
+    /**
+     * 获得缓存的基本对象。
+     *
+     * @param key 缓存键值
+     * @return 缓存键值对应的数据
+     */
+    public <T> T getCacheObject(final String key) {
+        ValueOperations<String, T> operation = redisTemplate.opsForValue();
+        return operation.get(key);
+    }
+
+    /**
+     * 删除单个对象
+     *
+     * @param key
+     */
+    public boolean deleteObject(final String key) {
+        return redisTemplate.delete(key);
+    }
+
+    /**
+     * 删除集合对象
+     *
+     * @param collection 多个对象
+     * @return
+     */
+    public long deleteObject(final Collection collection) {
+        return redisTemplate.delete(collection);
+    }
+
+    /**
+     * 缓存List数据
+     *
+     * @param key      缓存的键值
+     * @param dataList 待缓存的List数据
+     * @return 缓存的对象
+     */
+    public <T> long setCacheList(final String key, final List<T> dataList) {
+        Long count = redisTemplate.opsForList().rightPushAll(key, dataList);
+        return count == null ? 0 : count;
+    }
+
+    /**
+     * 获得缓存的list对象
+     *
+     * @param key 缓存的键值
+     * @return 缓存键值对应的数据
+     */
+    public <T> List<T> getCacheList(final String key) {
+        return redisTemplate.opsForList().range(key, 0, -1);
+    }
+
+    /**
+     * 缓存Set
+     *
+     * @param key     缓存键值
+     * @param dataSet 缓存的数据
+     * @return 缓存数据的对象
+     */
+    public <T> BoundSetOperations<String, T> setCacheSet(final String key, final Set<T> dataSet) {
+        BoundSetOperations<String, T> setOperation = redisTemplate.boundSetOps(key);
+        Iterator<T> it = dataSet.iterator();
+        while (it.hasNext()) {
+            setOperation.add(it.next());
+        }
+        return setOperation;
+    }
+
+    /**
+     * 获得缓存的set
+     *
+     * @param key
+     * @return
+     */
+    public <T> Set<T> getCacheSet(final String key) {
+        return redisTemplate.opsForSet().members(key);
+    }
+
+    /**
+     * 缓存Map
+     *
+     * @param key
+     * @param dataMap
+     */
+    public <T> void setCacheMap(final String key, final Map<String, T> dataMap) {
+        if (dataMap != null) {
+            redisTemplate.opsForHash().putAll(key, dataMap);
+        }
+    }
+
+    /**
+     * 获得缓存的Map
+     *
+     * @param key
+     * @return
+     */
+    public <T> Map<String, T> getCacheMap(final String key) {
+        return redisTemplate.opsForHash().entries(key);
+    }
+
+    /**
+     * 往Hash中存入数据
+     *
+     * @param key   Redis键
+     * @param hKey  Hash键
+     * @param value 值
+     */
+    public <T> void setCacheMapValue(final String key, final String hKey, final T value) {
+        redisTemplate.opsForHash().put(key, hKey, value);
+    }
+
+    /**
+     * 获取Hash中的数据
+     *
+     * @param key  Redis键
+     * @param hKey Hash键
+     * @return Hash中的对象
+     */
+    public <T> T getCacheMapValue(final String key, final String hKey) {
+        HashOperations<String, String, T> opsForHash = redisTemplate.opsForHash();
+        return opsForHash.get(key, hKey);
+    }
+
+    /**
+     * 删除Hash中的数据
+     *
+     * @param key
+     * @param hKey
+     */
+    public void delCacheMapValue(final String key, final String hKey) {
+        HashOperations hashOperations = redisTemplate.opsForHash();
+        hashOperations.delete(key, hKey);
+    }
+
+    /**
+     * 获取多个Hash中的数据
+     *
+     * @param key   Redis键
+     * @param hKeys Hash键集合
+     * @return Hash对象集合
+     */
+    public <T> List<T> getMultiCacheMapValue(final String key, final Collection<Object> hKeys) {
+        return redisTemplate.opsForHash().multiGet(key, hKeys);
+    }
+
+    /**
+     * 获得缓存的基本对象列表
+     *
+     * @param pattern 字符串前缀
+     * @return 对象列表
+     */
+    public Collection<String> keys(final String pattern) {
+        return redisTemplate.keys(pattern);
+    }
+}

+ 1076 - 0
src/main/java/com/futu/course/common/utils/RedisUtil.java

@@ -0,0 +1,1076 @@
+package com.futu.course.common.utils;
+
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.dao.DataAccessException;
+import org.springframework.data.redis.connection.RedisConnection;
+import org.springframework.data.redis.core.BoundListOperations;
+import org.springframework.data.redis.core.HashOperations;
+import org.springframework.data.redis.core.RedisCallback;
+import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.data.redis.support.atomic.RedisAtomicInteger;
+import org.springframework.data.redis.support.atomic.RedisAtomicLong;
+import org.springframework.stereotype.Component;
+import org.springframework.util.CollectionUtils;
+
+import java.util.*;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * redisTemplate封装
+ */
+@Component
+public class RedisUtil {
+
+    @Autowired
+    private final RedisTemplate redisTemplate;
+
+    //锁名称前缀
+    public static final String LOCK_PREFIX = "redis_lock";
+    //加锁失效时间,毫秒
+    public static final int LOCK_EXPIRE = 10; // ms
+
+    public static Integer workId=0;
+    public static Integer dataId=0;
+
+
+    public RedisUtil(RedisTemplate<String, Object> redisTemplate) {
+        this.redisTemplate = redisTemplate;
+    }
+
+
+
+    /**
+     * redis发布消息
+     *
+     * @param channel
+     * @param message
+     */
+    public void sendMessage(String channel, String message) {
+        redisTemplate.convertAndSend(channel, message);
+    }
+
+    /**
+     * 指定缓存失效时间
+     *
+     * @param key  键
+     * @param time 时间(秒)
+     * @return
+     */
+    public boolean expire(String key, long time, TimeUnit timeUnit) {
+        try {
+            if (time > 0) {
+                redisTemplate.expire(key, time, timeUnit);
+            }
+            return true;
+        } catch (Exception e) {
+            e.printStackTrace();
+            return false;
+        }
+    }
+
+    /**
+     * 根据key 获取过期时间
+     *
+     * @param key 键 不能为null
+     * @return 时间(秒) 返回0代表为永久有效
+     */
+    public long getExpire(String key) {
+        return redisTemplate.getExpire(key, TimeUnit.SECONDS);
+    }
+
+    /**
+     * 判断key是否存在
+     *
+     * @param key 键
+     * @return true 存在 false不存在
+     */
+    public boolean hasKey(String key) {
+        try {
+            return redisTemplate.hasKey(key);
+        } catch (Exception e) {
+            e.printStackTrace();
+            return false;
+        }
+    }
+
+    /**
+     * 删除缓存
+     *
+     * @param key 可以传一个值 或多个
+     */
+    @SuppressWarnings("unchecked")
+    public void del(String... key) {
+        if (key != null && key.length > 0) {
+            if (key.length == 1) {
+                redisTemplate.delete(key[0]);
+            } else {
+                redisTemplate.delete(CollectionUtils.arrayToList(key));
+            }
+        }
+    }
+
+    /**
+     * 删除缓存
+     *
+     * @param keys 集合
+     */
+    public void del(Collection<String> keys) {
+        redisTemplate.delete(keys);
+    }
+
+    //============================String=============================
+
+    /**
+     * 普通缓存获取
+     *
+     * @param key 键
+     * @return 值
+     */
+    public Object get(String key) {
+        return key == null ? null : redisTemplate.opsForValue().get(key);
+    }
+
+    /**
+     * 普通缓存放入
+     *
+     * @param key   键
+     * @param value 值
+     * @return true成功 false失败
+     */
+    public boolean set(String key, Object value) {
+        try {
+            redisTemplate.opsForValue().set(key, value);
+            return true;
+        } catch (Exception e) {
+            e.printStackTrace();
+            return false;
+        }
+    }
+
+    /**
+     * 普通缓存放入并设置时间
+     *
+     * @param key   键
+     * @param value 值
+     * @param time  时间(秒) time要大于0 如果time小于等于0 将设置无限期
+     * @return true成功 false 失败
+     */
+    public boolean set(String key, Object value, long time, TimeUnit timeUnit) {
+        try {
+            if (time > 0) {
+                redisTemplate.opsForValue().set(key, value, time, timeUnit);
+            } else {
+                set(key, value);
+            }
+            return true;
+        } catch (Exception e) {
+            e.printStackTrace();
+            return false;
+        }
+    }
+
+    /**
+     * 递增
+     *
+     * @param key   键
+     * @param delta 要增加几(大于0)
+     * @return
+     */
+    public long incr(String key, long delta) {
+        if (delta < 0) {
+            throw new RuntimeException("递增因子必须大于0");
+        }
+        return redisTemplate.opsForValue().increment(key, delta);
+    }
+
+    /**
+     * 递减
+     *
+     * @param key   键
+     * @param delta 要减少几(小于0)
+     * @return
+     */
+    public long decr(String key, long delta) {
+        if (delta < 0) {
+            throw new RuntimeException("递减因子必须大于0");
+        }
+        return redisTemplate.opsForValue().decrement(key, delta);
+    }
+
+    //================================Map=================================
+
+    /**
+     * HashGet
+     *
+     * @param key  键 不能为null
+     * @param item 项 不能为null
+     * @return 值
+     */
+    public Object hget(String key, String item) {
+        return redisTemplate.opsForHash().get(key, item);
+    }
+
+    /**
+     * 获取hashKey对应的所有键值
+     *
+     * @param key 键
+     * @return 对应的多个键值
+     */
+    public Map<Object, Object> hmget(String key) {
+        return redisTemplate.opsForHash().entries(key);
+    }
+
+    /**
+     * HashSet
+     *
+     * @param key 键
+     * @param map 对应多个键值
+     * @return true 成功 false 失败
+     */
+    public boolean hmset(String key, Map<String, Object> map) {
+        try {
+            redisTemplate.opsForHash().putAll(key, map);
+            return true;
+        } catch (Exception e) {
+            e.printStackTrace();
+            return false;
+        }
+    }
+
+    /**
+     * HashSet 并设置时间
+     *
+     * @param key  键
+     * @param map  对应多个键值
+     * @param time 时间(秒)
+     * @return true成功 false失败
+     */
+    public boolean hmset(String key, Map<String, Object> map, long time, TimeUnit timeUnit) {
+        try {
+            redisTemplate.opsForHash().putAll(key, map);
+            if (time > 0) {
+                expire(key, time, timeUnit);
+            }
+            return true;
+        } catch (Exception e) {
+            e.printStackTrace();
+            return false;
+        }
+    }
+
+    /**
+     * 向一张hash表中放入数据,如果不存在将创建
+     *
+     * @param key   键
+     * @param item  项
+     * @param value 值
+     * @return true 成功 false失败
+     */
+    public boolean hset(String key, String item, Object value) {
+        try {
+            redisTemplate.opsForHash().put(key, item, value);
+            return true;
+        } catch (Exception e) {
+            e.printStackTrace();
+            return false;
+        }
+    }
+
+    /**
+     * 向一张hash表中放入数据,如果不存在将创建
+     *
+     * @param key   键
+     * @param item  项
+     * @param value 值
+     * @param time  时间(秒)  注意:如果已存在的hash表有时间,这里将会替换原有的时间
+     * @return true 成功 false失败
+     */
+    public boolean hset(String key, String item, Object value, long time, TimeUnit timeUnit) {
+        try {
+            redisTemplate.opsForHash().put(key, item, value);
+            if (time > 0) {
+                expire(key, time, timeUnit);
+            }
+            return true;
+        } catch (Exception e) {
+            e.printStackTrace();
+            return false;
+        }
+    }
+
+    /**
+     * 删除hash表中的值
+     *
+     * @param key  键 不能为null
+     * @param item 项 可以使多个 不能为null
+     */
+    public void hdel(String key, Object... item) {
+        redisTemplate.opsForHash().delete(key, item);
+    }
+
+    /**
+     * 判断hash表中是否有该项的值
+     *
+     * @param key  键 不能为null
+     * @param item 项 不能为null
+     * @return true 存在 false不存在
+     */
+    public boolean hHasKey(String key, String item) {
+        return redisTemplate.opsForHash().hasKey(key, item);
+    }
+
+    /**
+     * hash递增 如果不存在,就会创建一个 并把新增后的值返回
+     *
+     * @param key  键
+     * @param item 项
+     * @param by   要增加几(大于0)
+     * @return
+     */
+    public double hincr(String key, String item, double by) {
+        return redisTemplate.opsForHash().increment(key, item, by);
+    }
+
+    /**
+     * hash递减
+     *
+     * @param key  键
+     * @param item 项
+     * @param by   要减少记(小于0)
+     * @return
+     */
+    public double hdecr(String key, String item, double by) {
+        return redisTemplate.opsForHash().increment(key, item, -by);
+    }
+
+    //============================set=============================
+
+    /**
+     * 根据key获取Set中的所有值
+     *
+     * @param key 键
+     * @return
+     */
+    public Set<Object> sGet(String key) {
+        try {
+            return redisTemplate.opsForSet().members(key);
+        } catch (Exception e) {
+            e.printStackTrace();
+            return null;
+        }
+    }
+
+    /**
+     * 根据value从一个set中查询,是否存在
+     *
+     * @param key   键
+     * @param value 值
+     * @return true 存在 false不存在
+     */
+    public boolean sHasKey(String key, Object value) {
+        try {
+            return redisTemplate.opsForSet().isMember(key, value);
+        } catch (Exception e) {
+            e.printStackTrace();
+            return false;
+        }
+    }
+
+    /**
+     * 将数据放入set缓存
+     *
+     * @param key    键
+     * @param values 值 可以是多个
+     * @return 成功个数
+     */
+    public long sSet(String key, Object... values) {
+        try {
+            return redisTemplate.opsForSet().add(key, values);
+        } catch (Exception e) {
+            e.printStackTrace();
+            return 0;
+        }
+    }
+
+    /**
+     * 获得锁
+     *
+     * @param lock
+     * @return
+     */
+    public boolean lock(String lock) {
+        return (boolean) redisTemplate.execute((RedisCallback) connection -> {
+            //获取时间毫秒值
+            long expireAt = System.currentTimeMillis() + LOCK_EXPIRE + 1;
+            //获取锁
+            Boolean acquire = connection.setNX(lock.getBytes(), String.valueOf(expireAt).getBytes());
+            if (acquire) {
+                return true;
+            } else {
+                byte[] bytes = connection.get(lock.getBytes());
+                //非空判断
+                if (Objects.nonNull(bytes) && bytes.length > 0) {
+                    long expireTime = Long.parseLong(new String(bytes));
+                    // 如果锁已经过期
+                    if (expireTime < System.currentTimeMillis()) {
+                        // 重新加锁,防止死锁
+                        byte[] set = connection.getSet(lock.getBytes(), String.valueOf(System.currentTimeMillis() + LOCK_EXPIRE + 1).getBytes());
+                        return Long.parseLong(new String(set)) < System.currentTimeMillis();
+                    }
+                }
+            }
+            return false;
+        });
+    }
+
+    /**
+     * 获得锁
+     *
+     * @param lock
+     * @return
+     */
+    public boolean lock(String lock,Long timeOut) {
+        return (boolean) redisTemplate.execute((RedisCallback) connection -> {
+            //获取时间毫秒值
+            long expireAt = System.currentTimeMillis() + timeOut + 1;
+            //获取锁
+            Boolean acquire = connection.setNX(lock.getBytes(), String.valueOf(expireAt).getBytes());
+            if (acquire) {
+                return true;
+            } else {
+                byte[] bytes = connection.get(lock.getBytes());
+                //非空判断
+                if (Objects.nonNull(bytes) && bytes.length > 0) {
+                    long expireTime = Long.parseLong(new String(bytes));
+                    // 如果锁已经过期
+                    if (expireTime < System.currentTimeMillis()) {
+                        // 重新加锁,防止死锁
+                        byte[] set = connection.getSet(lock.getBytes(), String.valueOf(System.currentTimeMillis() + LOCK_EXPIRE + 1).getBytes());
+                        return Long.parseLong(new String(set)) < System.currentTimeMillis();
+                    }
+                }
+            }
+            return false;
+        });
+    }
+
+
+    /**
+     * 删除锁
+     *
+     * @param key
+     */
+    public void deleteLock(String key) {
+
+            redisTemplate.delete(key);
+
+    }
+
+
+    /**
+     * 加锁,⽆阻塞
+     * <p>
+     * 锁
+     *
+     * @param key        请求标识
+     * @param userId  超期时间
+     * @param expireTime 上锁时间,单位TimeUnit.SECONDS
+     * @param timeout 等待时间,单位TimeUnit.SECONDS
+     * @return
+     */
+    public Boolean lock(String key, long expireTime, Long userId, long timeout) {
+        Long start = System.currentTimeMillis();
+        //在⼀定时间内获取锁,超时则返回错误
+//
+        for (; ; ) {
+//,则证明获取锁成功
+//Set
+            //     OK
+            Boolean ret = redisTemplate.opsForValue().setIfAbsent(key, userId, expireTime, TimeUnit.SECONDS);
+            if (ret != null && ret) {
+                return true;
+            }
+            //  否则循环等待,在
+            // 时间内仍未获取到锁,则获取失败
+            long end = System.currentTimeMillis() - start;
+            if (end >= timeout * 1000) {
+                return false;
+            }
+        }
+    }
+    public Boolean lock(String key, long expireTime, Long userId) {
+        Boolean ret = redisTemplate.opsForValue().setIfAbsent(key, userId, expireTime, TimeUnit.SECONDS);
+        return ret != null && ret;
+    }
+
+    public Boolean unlock(String key, Long userId) {
+        Object o = redisTemplate.opsForValue().get(key);
+        if(userId.equals(o)){
+            return redisTemplate.delete(key);
+        }else{
+            return false;
+        }
+    }
+    /**
+     * 将set数据放入缓存
+     *
+     * @param key    键
+     * @param time   时间(秒)
+     * @param values 值 可以是多个
+     * @return 成功个数
+     */
+    public long sSetAndTime(String key, long time, TimeUnit timeUnit, Object... values) {
+        try {
+            Long count = redisTemplate.opsForSet().add(key, values);
+            if (time > 0) {
+                expire(key, time, timeUnit);
+            }
+            return count;
+        } catch (Exception e) {
+            e.printStackTrace();
+            return 0;
+        }
+    }
+
+    /**
+     * 获取set缓存的长度
+     *
+     * @param key 键
+     * @return
+     */
+    public long sGetSetSize(String key) {
+        try {
+            return redisTemplate.opsForSet().size(key);
+        } catch (Exception e) {
+            e.printStackTrace();
+            return 0;
+        }
+    }
+
+    /**
+     * 移除值为value的
+     *
+     * @param key    键
+     * @param values 值 可以是多个
+     * @return 移除的个数
+     */
+    public long setRemove(String key, Object... values) {
+        try {
+            Long count = redisTemplate.opsForSet().remove(key, values);
+            return count;
+        } catch (Exception e) {
+            e.printStackTrace();
+            return 0;
+        }
+    }
+    //===============================list=================================
+
+    /**
+     * 获取list缓存的内容
+     *
+     * @param key   键
+     * @param start 开始
+     * @param end   结束  0 到 -1代表所有值
+     * @return
+     */
+    public List<Object> listGet(String key, long start, long end) {
+        try {
+            return redisTemplate.opsForList().range(key, start, end);
+        } catch (Exception e) {
+            e.printStackTrace();
+            return null;
+        }
+    }
+
+
+    /**
+     * 弹出对象
+     * @param key
+     * @return
+     */
+    public Object popLeftList(String key){
+        return redisTemplate.opsForList().leftPop(key);
+    }
+
+    /**
+     * 获取list缓存的长度
+     *
+     * @param key 键
+     * @return
+     */
+    public long listSize(String key) {
+        try {
+            return redisTemplate.opsForList().size(key);
+        } catch (Exception e) {
+            e.printStackTrace();
+            return 0;
+        }
+    }
+
+    /**
+     * 通过索引 获取list中的值
+     *
+     * @param key   键
+     * @param index 索引  index>=0时, 0 表头,1 第二个元素,依次类推;index<0时,-1,表尾,-2倒数第二个元素,依次类推
+     * @return
+     */
+    public Object listGetIndex(String key, long index) {
+        try {
+            return redisTemplate.opsForList().index(key, index);
+        } catch (Exception e) {
+            e.printStackTrace();
+            return null;
+        }
+    }
+
+    /**
+     * 将list放入缓存
+     *
+     * @param key   键
+     * @param value 值
+     * @return
+     */
+    public boolean listSet(String key, Object value) {
+        try {
+
+            redisTemplate.opsForList().rightPush(key, value);
+            return true;
+        } catch (Exception e) {
+            e.printStackTrace();
+            return false;
+        }
+    }
+
+    public void listTrim(String key, long start,long end) {
+        try {
+
+            redisTemplate.opsForList().trim(key,start,end);
+
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+
+    /**
+     * 将list放入缓存
+     *
+     * @param key   键
+     * @param value 值
+     * @param time  时间(秒)
+     * @return
+     */
+    public boolean listSet(String key, Object value, long time, TimeUnit timeUnit) {
+        try {
+            redisTemplate.opsForList().rightPush(key, value);
+            if (time > 0) {
+                expire(key, time, timeUnit);
+            }
+            return true;
+        } catch (Exception e) {
+            e.printStackTrace();
+            return false;
+        }
+    }
+
+    /**
+     * 将list放入缓存
+     *
+     * @param key   键
+     * @param value 值
+     * @return
+     */
+    public boolean listSet(String key, List<Object> value) {
+        try {
+            redisTemplate.opsForList().rightPushAll(key, value);
+            return true;
+        } catch (Exception e) {
+            e.printStackTrace();
+            return false;
+        }
+    }
+
+    /**
+     * list大小
+     * @param key
+     * @return
+     */
+    public Long getListSize(String key){
+        return redisTemplate.opsForList().size(key);
+    }
+
+    /**
+     * 将list放入缓存
+     *
+     * @param key   键
+     * @param value 值
+     * @param time  时间(秒)
+     * @return
+     */
+    public boolean listSet(String key, List<Object> value, long time, TimeUnit timeUnit) {
+        try {
+            redisTemplate.opsForList().rightPushAll(key, value);
+            if (time > 0) {
+                expire(key, time, timeUnit);
+            }
+            return true;
+        } catch (Exception e) {
+            e.printStackTrace();
+            return false;
+        }
+    }
+
+    /**
+     * 根据索引修改list中的某条数据
+     *
+     * @param key   键
+     * @param index 索引
+     * @param value 值
+     * @return
+     */
+    public boolean listUpdateIndex(String key, long index, Object value) {
+        try {
+            redisTemplate.opsForList().set(key, index, value);
+            return true;
+        } catch (Exception e) {
+            e.printStackTrace();
+            return false;
+        }
+    }
+
+    /**
+     * 移除N个值为value
+     *
+     * @param key   键
+     * @param count 移除多少个
+     * @param value 值
+     * @return 移除的个数
+     */
+    public long listRemove(String key, long count, Object value) {
+        try {
+            Long remove = redisTemplate.opsForList().remove(key, count, value);
+            return remove;
+        } catch (Exception e) {
+            e.printStackTrace();
+            return 0;
+        }
+    }
+
+    /**
+     * 模糊查询获取key值
+     *
+     * @param pattern
+     * @return
+     */
+    public Set keys(String pattern) {
+        return redisTemplate.keys(pattern);
+    }
+
+    /**
+     * 使用Redis的消息队列
+     *
+     * @param channel
+     * @param message 消息内容
+     */
+    public void convertAndSend(String channel, Object message) {
+        redisTemplate.convertAndSend(channel, message);
+    }
+
+
+    //=========BoundListOperations 用法 start============
+
+    /**
+     * 将数据添加到Redis的list中(从右边添加)
+     *
+     * @param listKey
+     * @param time    过期时间
+     * @param values  待添加的数据
+     */
+    public void addToListRight(String listKey, long time, Object... values) {
+        //绑定操作
+        BoundListOperations<String, Object> boundValueOperations = redisTemplate.boundListOps(listKey);
+        //插入数据
+        boundValueOperations.rightPushAll(values);
+        //设置过期时间
+        boundValueOperations.expire(time, TimeUnit.SECONDS);
+    }
+
+    /**
+     * 根据起始结束序号遍历Redis中的list
+     *
+     * @param listKey
+     * @param start   起始序号
+     * @param end     结束序号
+     * @return
+     */
+    public List<Object> rangeList(String listKey, long start, long end) {
+        //绑定操作
+        BoundListOperations<String, Object> boundValueOperations = redisTemplate.boundListOps(listKey);
+        //查询数据
+        return boundValueOperations.range(start, end);
+    }
+
+    /**
+     * 弹出右边的值 --- 并且移除这个值
+     *
+     * @param listKey
+     */
+    public Object rifhtPop(String listKey) {
+        //绑定操作
+        BoundListOperations<String, Object> boundValueOperations = redisTemplate.boundListOps(listKey);
+        return boundValueOperations.rightPop();
+    }
+
+    /**
+     * @param key
+     * @return
+     */
+    public Long getIncr(String key) {
+        RedisAtomicLong entityIdCounter = new RedisAtomicLong(key, redisTemplate.getConnectionFactory());
+        Long increment = entityIdCounter.getAndIncrement();
+        return increment;
+    }
+
+    /**
+     * @return
+     */
+    public synchronized Integer getSequenceId() {
+        String key = "sms_sequence_id";
+        RedisAtomicInteger idCount = new RedisAtomicInteger(key, redisTemplate.getConnectionFactory());
+        int sequenceId = idCount.getAndIncrement();
+        if (sequenceId <= 1000 || sequenceId >= 1000000000) {
+            idCount.set(1000);
+            sequenceId = idCount.getAndIncrement();
+            return sequenceId;
+        }
+        return sequenceId;
+    }
+
+    public void setCount(String key ,int initValue){
+        RedisAtomicInteger idCount = new RedisAtomicInteger(key, redisTemplate.getConnectionFactory());
+        idCount.set(initValue);
+    }
+    /**
+     * @return
+     */
+    public  Integer getCount(String key) {
+        RedisAtomicInteger idCount = new RedisAtomicInteger(key, redisTemplate.getConnectionFactory());
+        int count = idCount.getAndIncrement();
+        if (count ==Integer.MAX_VALUE-1) {
+            idCount.set(0);
+            count = idCount.getAndIncrement();
+            return count;
+        }
+        return count;
+    }
+    public  Integer getStartCount(String key) {
+        RedisAtomicInteger idCount = new RedisAtomicInteger(key, redisTemplate.getConnectionFactory());
+
+        int count = idCount.getAndIncrement();
+        if(count<=1){
+            this.expire(key, CacheKey.DAY1,TimeUnit.DAYS);
+        }
+        if (count ==Integer.MAX_VALUE-1) {
+
+            idCount.set(0);
+            count = idCount.getAndIncrement();
+            return count;
+        }
+
+        return count;
+    }
+    public String getStartNum(Integer num){
+
+        String str=num.toString();
+        int len=str.length();
+        if(str.length()<5){
+            StringBuilder sb = new StringBuilder();
+            for(int i=0;i<5-len;i++){
+
+                sb.append("0");
+
+            }
+            sb.append(str);
+            return sb.toString();
+        }
+        return str;
+    }
+    public String getStartNum(String key){
+        Integer num=getStartCount(key);
+        String str=num.toString();
+        int len=str.length();
+        if(str.length()<5){
+            StringBuilder sb = new StringBuilder();
+            for(int i=0;i<5-len;i++){
+
+                    sb.append("0");
+
+            }
+            sb.append(str);
+            return sb.toString();
+        }
+        return str;
+    }
+
+
+    public synchronized Integer getSequenceNumber3(String key) {
+        RedisAtomicInteger idCount = new RedisAtomicInteger(key, redisTemplate.getConnectionFactory());
+        int sequenceId = idCount.getAndIncrement();
+        if (sequenceId < 0 || sequenceId >= Integer.MAX_VALUE) {
+            idCount.set(0);
+            sequenceId = idCount.getAndIncrement();
+            return sequenceId;
+        }
+        return sequenceId;
+    }
+    public synchronized  Integer getWorkId(String key) {
+        RedisAtomicInteger idCount = new RedisAtomicInteger(key, redisTemplate.getConnectionFactory());
+        int sequenceId = idCount.getAndIncrement();
+        if (sequenceId < 0 || sequenceId >= 31) {
+            idCount.set(0);
+            sequenceId = idCount.getAndIncrement();
+            return sequenceId;
+        }
+        return sequenceId;
+    }
+
+    /**
+     * 14      * @Description: 初始化自增长值
+     * 15      * @param key key
+     * 16      * @param value 当前值
+     * 17
+     */
+    public void setIncr(String key, int value) {
+        RedisAtomicLong counter = new RedisAtomicLong(key, redisTemplate.getConnectionFactory());
+        counter.set(value);
+    }
+
+    /**
+     * 14      * @Description: 初始化自增长值
+     * 15      * @param key key
+     * 16      * @param value 当前值
+     * 17
+     */
+    public void setInteger(String key, int value) {
+        RedisAtomicInteger counter = new RedisAtomicInteger(key, redisTemplate.getConnectionFactory());
+        counter.set(value);
+    }
+
+
+    /**
+     * 保存到hash
+     * @param key
+     * @param hashKey
+     * @param value
+     */
+    public void setHashMap(String key,Object hashKey,Object value){
+        redisTemplate.opsForHash().put(key,hashKey,value);
+    }
+
+    /**
+     * 获取hashvalue
+     * @param key
+     * @param hashKey
+     * @return
+     */
+    public Object getHashValue(String key,Object hashKey){
+       return  redisTemplate.opsForHash().get(key,hashKey);
+    }
+
+    /**
+     * 删除hash中单个对象
+     * @param key
+     * @param hashKey
+     */
+    public void remove(String key,Object hashKey){
+        redisTemplate.opsForHash().delete(key,hashKey);
+    }
+
+    /**
+     * 获取redisTemplate
+     * @return
+     */
+    public RedisTemplate getRedisTemplate(){
+        return redisTemplate;
+    }
+
+    /**
+     * 存储数据或修改数据
+     *
+     * @param modelMap
+     * @param mapName
+     */
+    public void setMap(String mapName, Map<String, Object> modelMap) {
+        HashOperations<String, Object, Object> hps = redisTemplate.opsForHash();
+        hps.putAll(mapName, modelMap);
+    }
+
+    /**
+     * 获取数据Map
+     *
+     * @param mapName
+     * @return
+     */
+    public Map<Object, Object> getMapValue(String mapName) {
+        HashOperations<String, Object, Object> hps = this.redisTemplate.opsForHash();
+        return hps.entries(mapName);
+
+    }
+
+    /**
+     * 获取数据Map size
+     *
+     * @param mapName
+     * @return
+     */
+    public int getMapSize(String mapName) {
+        HashOperations<String, Object, Object> hps = this.redisTemplate.opsForHash();
+        return hps.entries(mapName).size();
+
+    }
+
+    /**
+     * 获取数据value
+     *
+     * @param mapName
+     * @param hashKey
+     * @return
+     */
+    public Object getValue(String mapName, String hashKey) {
+        HashOperations<String, Object, Object> hps = this.redisTemplate.opsForHash();
+        return hps.get(mapName, hashKey);
+
+    }
+
+    /**
+     * 批量删除缓存数据
+     *
+     * @param keys
+     */
+    public long deleteData(String key,Object ...keys) {
+        // 执行批量删除操作时先序列化template
+       long b= redisTemplate.opsForHash().delete(key,keys);
+       return b;
+    }
+
+    private void batchCacheMarketInfo(List<Object > dataList, long expire) {
+        //使用pipeline方式
+        redisTemplate.executePipelined(new RedisCallback<List<Object>>() {
+            @Override
+            public List<Object> doInRedis(RedisConnection connection) throws DataAccessException {
+                for (Object marketInfo : dataList) {
+                    String key = "";
+                    byte[] rawKey = redisTemplate.getKeySerializer().serialize(key);
+                    connection.setEx(rawKey, expire, redisTemplate.getValueSerializer().serialize(marketInfo));
+                }
+                return null;
+            }
+        });
+
+    }
+
+
+
+    //=========BoundListOperations 用法 End============
+
+}

+ 558 - 0
src/main/java/com/futu/course/common/utils/StringUtils.java

@@ -0,0 +1,558 @@
+package com.futu.course.common.utils;
+
+
+import com.futu.course.common.constant.Constants;
+import org.springframework.util.AntPathMatcher;
+
+import java.util.*;
+
+/**
+ * 字符串工具类
+ *
+ */
+public class StringUtils extends org.apache.commons.lang3.StringUtils
+{
+    /** 空字符串 */
+    private static final String NULLSTR = "";
+
+    /** 下划线 */
+    private static final char SEPARATOR = '_';
+
+    /**
+     * 获取参数不为空值
+     *
+     * @param value defaultValue 要判断的value
+     * @return value 返回值
+     */
+    public static <T> T nvl(T value, T defaultValue)
+    {
+        return value != null ? value : defaultValue;
+    }
+
+    /**
+     * * 判断一个Collection是否为空, 包含List,Set,Queue
+     *
+     * @param coll 要判断的Collection
+     * @return true:为空 false:非空
+     */
+    public static boolean isEmpty(Collection<?> coll)
+    {
+        return isNull(coll) || coll.isEmpty();
+    }
+
+    /**
+     * * 判断一个Collection是否非空,包含List,Set,Queue
+     *
+     * @param coll 要判断的Collection
+     * @return true:非空 false:空
+     */
+    public static boolean isNotEmpty(Collection<?> coll)
+    {
+        return !isEmpty(coll);
+    }
+
+    /**
+     * * 判断一个对象数组是否为空
+     *
+     * @param objects 要判断的对象数组
+     ** @return true:为空 false:非空
+     */
+    public static boolean isEmpty(Object[] objects)
+    {
+        return isNull(objects) || (objects.length == 0);
+    }
+
+    /**
+     * * 判断一个对象数组是否非空
+     *
+     * @param objects 要判断的对象数组
+     * @return true:非空 false:空
+     */
+    public static boolean isNotEmpty(Object[] objects)
+    {
+        return !isEmpty(objects);
+    }
+
+    /**
+     * * 判断一个Map是否为空
+     *
+     * @param map 要判断的Map
+     * @return true:为空 false:非空
+     */
+    public static boolean isEmpty(Map<?, ?> map)
+    {
+        return isNull(map) || map.isEmpty();
+    }
+
+    /**
+     * * 判断一个Map是否为空
+     *
+     * @param map 要判断的Map
+     * @return true:非空 false:空
+     */
+    public static boolean isNotEmpty(Map<?, ?> map)
+    {
+        return !isEmpty(map);
+    }
+
+    /**
+     * * 判断一个字符串是否为空串
+     *
+     * @param str String
+     * @return true:为空 false:非空
+     */
+    public static boolean isEmpty(String str)
+    {
+        return isNull(str) || NULLSTR.equals(str.trim());
+    }
+
+    /**
+     * * 判断一个字符串是否为非空串
+     *
+     * @param str String
+     * @return true:非空串 false:空串
+     */
+    public static boolean isNotEmpty(String str)
+    {
+        return !isEmpty(str);
+    }
+
+    /**
+     * * 判断一个对象是否为空
+     *
+     * @param object Object
+     * @return true:为空 false:非空
+     */
+    public static boolean isNull(Object object)
+    {
+        return object == null;
+    }
+
+    /**
+     * * 判断一个对象是否非空
+     *
+     * @param object Object
+     * @return true:非空 false:空
+     */
+    public static boolean isNotNull(Object object)
+    {
+        return !isNull(object);
+    }
+
+    /**
+     * * 判断一个对象是否是数组类型(Java基本型别的数组)
+     *
+     * @param object 对象
+     * @return true:是数组 false:不是数组
+     */
+    public static boolean isArray(Object object)
+    {
+        return isNotNull(object) && object.getClass().isArray();
+    }
+
+    /**
+     * 去空格
+     */
+    public static String trim(String str)
+    {
+        return (str == null ? "" : str.trim());
+    }
+
+    /**
+     * 截取字符串
+     *
+     * @param str 字符串
+     * @param start 开始
+     * @return 结果
+     */
+    public static String substring(final String str, int start)
+    {
+        if (str == null)
+        {
+            return NULLSTR;
+        }
+
+        if (start < 0)
+        {
+            start = str.length() + start;
+        }
+
+        if (start < 0)
+        {
+            start = 0;
+        }
+        if (start > str.length())
+        {
+            return NULLSTR;
+        }
+
+        return str.substring(start);
+    }
+
+    /**
+     * 截取字符串
+     *
+     * @param str 字符串
+     * @param start 开始
+     * @param end 结束
+     * @return 结果
+     */
+    public static String substring(final String str, int start, int end)
+    {
+        if (str == null)
+        {
+            return NULLSTR;
+        }
+
+        if (end < 0)
+        {
+            end = str.length() + end;
+        }
+        if (start < 0)
+        {
+            start = str.length() + start;
+        }
+
+        if (end > str.length())
+        {
+            end = str.length();
+        }
+
+        if (start > end)
+        {
+            return NULLSTR;
+        }
+
+        if (start < 0)
+        {
+            start = 0;
+        }
+        if (end < 0)
+        {
+            end = 0;
+        }
+
+        return str.substring(start, end);
+    }
+
+
+
+    /**
+     * 是否为http(s)://开头
+     *
+     * @param link 链接
+     * @return 结果
+     */
+    public static boolean ishttp(String link)
+    {
+        return StringUtils.startsWithAny(link, Constants.HTTP, Constants.HTTPS);
+    }
+
+    /**
+     * 字符串转set
+     *
+     * @param str 字符串
+     * @param sep 分隔符
+     * @return set集合
+     */
+    public static final Set<String> str2Set(String str, String sep)
+    {
+        return new HashSet<String>(str2List(str, sep, true, false));
+    }
+
+    /**
+     * 字符串转list
+     *
+     * @param str 字符串
+     * @param sep 分隔符
+     * @param filterBlank 过滤纯空白
+     * @param trim 去掉首尾空白
+     * @return list集合
+     */
+    public static final List<String> str2List(String str, String sep, boolean filterBlank, boolean trim)
+    {
+        List<String> list = new ArrayList<String>();
+        if (StringUtils.isEmpty(str))
+        {
+            return list;
+        }
+
+        // 过滤空白字符串
+        if (filterBlank && StringUtils.isBlank(str))
+        {
+            return list;
+        }
+        String[] split = str.split(sep);
+        for (String string : split)
+        {
+            if (filterBlank && StringUtils.isBlank(string))
+            {
+                continue;
+            }
+            if (trim)
+            {
+                string = string.trim();
+            }
+            list.add(string);
+        }
+
+        return list;
+    }
+
+    /**
+     * 查找指定字符串是否包含指定字符串列表中的任意一个字符串同时串忽略大小写
+     *
+     * @param cs 指定字符串
+     * @param searchCharSequences 需要检查的字符串数组
+     * @return 是否包含任意一个字符串
+     */
+    public static boolean containsAnyIgnoreCase(CharSequence cs, CharSequence... searchCharSequences)
+    {
+        if (isEmpty(cs) || isEmpty(searchCharSequences))
+        {
+            return false;
+        }
+        for (CharSequence testStr : searchCharSequences)
+        {
+            if (containsIgnoreCase(cs, testStr))
+            {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * 驼峰转下划线命名
+     */
+    public static String toUnderScoreCase(String str)
+    {
+        if (str == null)
+        {
+            return null;
+        }
+        StringBuilder sb = new StringBuilder();
+        // 前置字符是否大写
+        boolean preCharIsUpperCase = true;
+        // 当前字符是否大写
+        boolean curreCharIsUpperCase = true;
+        // 下一字符是否大写
+        boolean nexteCharIsUpperCase = true;
+        for (int i = 0; i < str.length(); i++)
+        {
+            char c = str.charAt(i);
+            if (i > 0)
+            {
+                preCharIsUpperCase = Character.isUpperCase(str.charAt(i - 1));
+            }
+            else
+            {
+                preCharIsUpperCase = false;
+            }
+
+            curreCharIsUpperCase = Character.isUpperCase(c);
+
+            if (i < (str.length() - 1))
+            {
+                nexteCharIsUpperCase = Character.isUpperCase(str.charAt(i + 1));
+            }
+
+            if (preCharIsUpperCase && curreCharIsUpperCase && !nexteCharIsUpperCase)
+            {
+                sb.append(SEPARATOR);
+            }
+            else if ((i != 0 && !preCharIsUpperCase) && curreCharIsUpperCase)
+            {
+                sb.append(SEPARATOR);
+            }
+            sb.append(Character.toLowerCase(c));
+        }
+
+        return sb.toString();
+    }
+
+    /**
+     * 是否包含字符串
+     *
+     * @param str 验证字符串
+     * @param strs 字符串组
+     * @return 包含返回true
+     */
+    public static boolean inStringIgnoreCase(String str, String... strs)
+    {
+        if (str != null && strs != null)
+        {
+            for (String s : strs)
+            {
+                if (str.equalsIgnoreCase(trim(s)))
+                {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    /**
+     * 将下划线大写方式命名的字符串转换为驼峰式。如果转换前的下划线大写方式命名的字符串为空,则返回空字符串。 例如:HELLO_WORLD->HelloWorld
+     *
+     * @param name 转换前的下划线大写方式命名的字符串
+     * @return 转换后的驼峰式命名的字符串
+     */
+    public static String convertToCamelCase(String name)
+    {
+        StringBuilder result = new StringBuilder();
+        // 快速检查
+        if (name == null || name.isEmpty())
+        {
+            // 没必要转换
+            return "";
+        }
+        else if (!name.contains("_"))
+        {
+            // 不含下划线,仅将首字母大写
+            return name.substring(0, 1).toUpperCase() + name.substring(1);
+        }
+        // 用下划线将原始字符串分割
+        String[] camels = name.split("_");
+        for (String camel : camels)
+        {
+            // 跳过原始字符串中开头、结尾的下换线或双重下划线
+            if (camel.isEmpty())
+            {
+                continue;
+            }
+            // 首字母大写
+            result.append(camel.substring(0, 1).toUpperCase());
+            result.append(camel.substring(1).toLowerCase());
+        }
+        return result.toString();
+    }
+
+    /**
+     * 驼峰式命名法 例如:user_name->userName
+     */
+    public static String toCamelCase(String s)
+    {
+        if (s == null)
+        {
+            return null;
+        }
+        s = s.toLowerCase();
+        StringBuilder sb = new StringBuilder(s.length());
+        boolean upperCase = false;
+        for (int i = 0; i < s.length(); i++)
+        {
+            char c = s.charAt(i);
+
+            if (c == SEPARATOR)
+            {
+                upperCase = true;
+            }
+            else if (upperCase)
+            {
+                sb.append(Character.toUpperCase(c));
+                upperCase = false;
+            }
+            else
+            {
+                sb.append(c);
+            }
+        }
+        return sb.toString();
+    }
+
+    /**
+     * 查找指定字符串是否匹配指定字符串列表中的任意一个字符串
+     *
+     * @param str 指定字符串
+     * @param strs 需要检查的字符串数组
+     * @return 是否匹配
+     */
+    public static boolean matches(String str, List<String> strs)
+    {
+        if (isEmpty(str) || isEmpty(strs))
+        {
+            return false;
+        }
+        for (String pattern : strs)
+        {
+            if (isMatch(pattern, str))
+            {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * 判断url是否与规则配置:
+     * ? 表示单个字符;
+     * * 表示一层路径内的任意字符串,不可跨层级;
+     * ** 表示任意层路径;
+     *
+     * @param pattern 匹配规则
+     * @param url 需要匹配的url
+     * @return
+     */
+    public static boolean isMatch(String pattern, String url)
+    {
+        AntPathMatcher matcher = new AntPathMatcher();
+        return matcher.match(pattern, url);
+    }
+
+    @SuppressWarnings("unchecked")
+    public static <T> T cast(Object obj)
+    {
+        return (T) obj;
+    }
+
+    /**
+     * 数字左边补齐0,使之达到指定长度。注意,如果数字转换为字符串后,长度大于size,则只保留 最后size个字符。
+     *
+     * @param num 数字对象
+     * @param size 字符串指定长度
+     * @return 返回数字的字符串格式,该字符串为指定长度。
+     */
+    public static final String padl(final Number num, final int size)
+    {
+        return padl(num.toString(), size, '0');
+    }
+
+    /**
+     * 字符串左补齐。如果原始字符串s长度大于size,则只保留最后size个字符。
+     *
+     * @param s 原始字符串
+     * @param size 字符串指定长度
+     * @param c 用于补齐的字符
+     * @return 返回指定长度的字符串,由原字符串左补齐或截取得到。
+     */
+    public static final String padl(final String s, final int size, final char c)
+    {
+        final StringBuilder sb = new StringBuilder(size);
+        if (s != null)
+        {
+            final int len = s.length();
+            if (s.length() <= size)
+            {
+                for (int i = size - len; i > 0; i--)
+                {
+                    sb.append(c);
+                }
+                sb.append(s);
+            }
+            else
+            {
+                return s.substring(len - size, len);
+            }
+        }
+        else
+        {
+            for (int i = size; i > 0; i--)
+            {
+                sb.append(c);
+            }
+        }
+        return sb.toString();
+    }
+}

+ 113 - 0
src/main/java/com/futu/course/common/utils/TokenUtils.java

@@ -0,0 +1,113 @@
+package com.futu.course.common.utils;
+
+
+import com.futu.course.common.exception.RRException;
+import io.jsonwebtoken.Claims;
+import io.jsonwebtoken.JwtBuilder;
+import io.jsonwebtoken.Jwts;
+import io.jsonwebtoken.SignatureAlgorithm;
+
+import javax.crypto.spec.SecretKeySpec;
+import javax.xml.bind.DatatypeConverter;
+import java.security.Key;
+import java.util.Date;
+
+/**
+ * 生成Token工具类
+ */
+public class TokenUtils {
+
+    /**
+     * 签名秘钥
+     */
+    public static final String SECRET = "cjyfutu1688";
+
+    /**
+     * 生成token
+     * @param id 一般传入userName
+     * @return
+     */
+    public static String createJwtToken(String id){
+        String issuer = "www.futureading.com";
+        String subject = "65532781@qq.com";
+        long ttlMillis = System.currentTimeMillis();
+        return createJwtToken(id, issuer, subject, ttlMillis);
+    }
+
+    /**
+     * 生成Token
+     *
+     * @param id
+     *            编号
+     * @param issuer
+     *            该JWT的签发者,是否使用是可选的
+     * @param subject
+     *            该JWT所面向的用户,是否使用是可选的;
+     * @param ttlMillis
+     *            签发时间
+     * @return token String
+     */
+    public static String createJwtToken(String id, String issuer, String subject, long ttlMillis) {
+
+        // 签名算法 ,将对token进行签名
+        SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
+
+        // 生成签发时间
+        long nowMillis = System.currentTimeMillis();
+        Date now = new Date(nowMillis);
+
+        // 通过秘钥签名JWT
+        byte[] apiKeySecretBytes = DatatypeConverter.parseBase64Binary(SECRET);
+        Key signingKey = new SecretKeySpec(apiKeySecretBytes, signatureAlgorithm.getJcaName());
+
+        // Let's set the JWT Claims
+        JwtBuilder builder = Jwts.builder().setId(id)
+                .setIssuedAt(now)
+                .setSubject(subject)
+                .setIssuer(issuer)
+                .signWith(signatureAlgorithm, signingKey);
+
+        // if it has been specified, let's add the expiration
+        if (ttlMillis >= 0) {
+            long expMillis = nowMillis + ttlMillis;
+            Date exp = new Date(expMillis);
+            builder.setExpiration(exp);
+        }
+
+        // Builds the JWT and serializes it to a compact, URL-safe string
+        return builder.compact();
+
+    }
+
+    // Sample method to validate and read the JWT
+    public static Claims parseJWT(String jwt) {
+        // This line will throw an exception if it is not a signed JWS (as expected)
+        Claims claims = Jwts.parser()
+                .setSigningKey(DatatypeConverter.parseBase64Binary(SECRET))
+                .parseClaimsJws(jwt).getBody();
+        return claims;
+    }
+
+    public static Long getUserId(String token){
+        Claims claims = null;
+        try {
+            claims = TokenUtils.parseJWT(token);
+        } catch (Exception e) {
+            throw new RRException("token错误,请重新登录",101);
+        }
+
+        if(null==claims) {
+            throw new RRException("token错误,请重新登录",101);
+        }
+        String id = claims.getId();
+        Long userId=Long.valueOf(id);
+
+        return userId;
+
+
+    }
+
+    public static void main(String[] args) {
+        System.out.println(TokenUtils.createJwtToken("admin"));
+    }
+}

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

@@ -0,0 +1,14 @@
+server:
+  port: 8003
+spring:
+  datasource:
+    driver-class-name: com.mysql.cj.jdbc.Driver
+    url: jdbc:mysql://182.92.251.243:3306/dome?characterEncoding=utf8&serverTimezone=GMT%2B8
+    username: root
+    password: root
+
+  redis:
+    host: localhost
+    database: 0
+    port: 6379
+    password:

+ 13 - 0
src/test/java/com/futu/course/GooseApplicationTests.java

@@ -0,0 +1,13 @@
+package com.futu.course;
+
+import org.junit.jupiter.api.Test;
+import org.springframework.boot.test.context.SpringBootTest;
+
+@SpringBootTest
+class GooseApplicationTests {
+
+    @Test
+    void contextLoads() {
+    }
+
+}