diff --git a/.idea/compiler.xml b/.idea/compiler.xml index 41670ba..2554f66 100644 --- a/.idea/compiler.xml +++ b/.idea/compiler.xml @@ -2,6 +2,7 @@ + diff --git a/sky-common/src/main/java/com/sky/common/config/Knife4jConfiguration.java b/sky-common/src/main/java/com/sky/common/config/Knife4jConfiguration.java new file mode 100644 index 0000000..73701c1 --- /dev/null +++ b/sky-common/src/main/java/com/sky/common/config/Knife4jConfiguration.java @@ -0,0 +1,55 @@ +package com.sky.common.config; + +import com.github.xiaoymin.knife4j.spring.annotations.EnableKnife4j; +import lombok.extern.slf4j.Slf4j; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import springfox.documentation.builders.ApiInfoBuilder; +import springfox.documentation.builders.PathSelectors; +import springfox.documentation.builders.RequestHandlerSelectors; +import springfox.documentation.service.ApiInfo; +import springfox.documentation.service.Contact; +import springfox.documentation.spi.DocumentationType; +import springfox.documentation.spring.web.plugins.Docket; + +@Configuration +@Slf4j +@EnableKnife4j +public class Knife4jConfiguration { + /** + * 通过knife4j生成接口文档 + * 管理端接口 + * + * @return Docket + */ + @Bean + public Docket docketAdmin() { + log.info("A管理端接口"); + // 添加作者 + springfox.documentation.service.Contact contact = new Contact("Bunny", "1319900154@qq.com", "1319900154@qq.com"); + ApiInfo apiInfo = new ApiInfoBuilder().title("苍穹外卖项目接口文档").version("2.0").description("苍穹外卖项目接口文档").contact(contact).build(); + return new Docket(DocumentationType.SWAGGER_2).groupName("A管理端接口") + .apiInfo(apiInfo).select().apis(RequestHandlerSelectors.basePackage("com.sky.controller.admin")) + .paths(PathSelectors.any()).build(); + } + + @Bean + public Docket docketUser() { + log.info("B用户端接口"); + + ApiInfo apiInfo = new ApiInfoBuilder().title("苍穹外卖项目接口文档").version("2.0").description("苍穹外卖项目接口文档").build(); + return new Docket(DocumentationType.SWAGGER_2).groupName("B用户端接口") + .apiInfo(apiInfo).select().apis(RequestHandlerSelectors.basePackage("com.sky.controller.user")) + .paths(PathSelectors.any()).build(); + } + + @Bean + public Docket docketNotify() { + log.info("Pay支付接口"); + + ApiInfo apiInfo = new ApiInfoBuilder().title("苍穹外卖项目接口文档").version("2.0").description("苍穹外卖项目接口文档").build(); + return new Docket(DocumentationType.SWAGGER_2).groupName("Pay支付接口") + .apiInfo(apiInfo).select().apis(RequestHandlerSelectors.basePackage("com.sky.controller.notify")) + .paths(PathSelectors.any()).build(); + } +} diff --git a/sky-common/src/main/java/com/sky/common/config/RedisConfiguration.java b/sky-common/src/main/java/com/sky/common/config/RedisConfiguration.java new file mode 100644 index 0000000..fe0a4bd --- /dev/null +++ b/sky-common/src/main/java/com/sky/common/config/RedisConfiguration.java @@ -0,0 +1,95 @@ +package com.sky.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.SerializationFeature; +import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; +import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer; +import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer; +import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer; +import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer; +import lombok.extern.slf4j.Slf4j; +import org.springframework.cache.CacheManager; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.redis.cache.RedisCacheConfiguration; +import org.springframework.data.redis.cache.RedisCacheManager; +import org.springframework.data.redis.connection.RedisConnectionFactory; +import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer; +import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer; +import org.springframework.data.redis.serializer.RedisSerializationContext; +import org.springframework.data.redis.serializer.StringRedisSerializer; + +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; + +@Configuration +@Slf4j +public class RedisConfiguration { + /** + * 使用StringRedisSerializer序列化为字符串 + */ + @Bean + public RedisTemplate redisTemplate(LettuceConnectionFactory connectionFactory) { + RedisTemplate redisTemplate = new RedisTemplate(); + redisTemplate.setConnectionFactory(connectionFactory); + // 设置key序列化为string + redisTemplate.setKeySerializer(new StringRedisSerializer()); + // 设置value序列化为JSON,使用GenericJackson2JsonRedisSerializer替换默认序列化 + redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer()); + redisTemplate.setHashKeySerializer(new StringRedisSerializer()); + redisTemplate.setHashValueSerializer(new GenericJackson2JsonRedisSerializer()); + + return redisTemplate; + } + + /** + * 解决cache(@Cacheable)把数据缓存到redis中的value是乱码问题 + */ + @Bean + @SuppressWarnings("all") + public CacheManager cacheManager(RedisConnectionFactory factory) { + StringRedisSerializer redisSerializer = new StringRedisSerializer(); + // json序列化 + Jackson2JsonRedisSerializer serializer = jsonRedisSerializer(); + // 配置序列化 + RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig(); + RedisCacheConfiguration redisCacheConfiguration = config + .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(redisSerializer)) + .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(serializer)); + + RedisCacheManager cacheManager = RedisCacheManager.builder(factory) + .cacheDefaults(redisCacheConfiguration).build(); + return cacheManager; + } + + /** + * 指定的日期模式 + */ + public Jackson2JsonRedisSerializer jsonRedisSerializer() { + Jackson2JsonRedisSerializer serializer = new Jackson2JsonRedisSerializer<>(Object.class); + ObjectMapper mapper = new ObjectMapper(); + // 设置ObjectMapper访问权限 + mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); + // 记录序列化之后的数据类型,方便反序列化 + mapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL); + // LocalDatetime序列化,默认不兼容jdk8日期序列化 + JavaTimeModule timeModule = new JavaTimeModule(); + timeModule.addDeserializer(LocalDate.class, new LocalDateDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd"))); + timeModule.addSerializer(LocalDate.class, new LocalDateSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd"))); + + timeModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))); + timeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))); + // 关闭默认的日期格式化方式,默认UTC日期格式 yyyy-MM-dd’T’HH:mm:ss.SSS + mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); + mapper.registerModule(timeModule); + + serializer.setObjectMapper(mapper); + return serializer; + } +} \ No newline at end of file diff --git a/sky-common/src/main/java/com/sky/common/config/RestTemplateConfiguration.java b/sky-common/src/main/java/com/sky/common/config/RestTemplateConfiguration.java new file mode 100644 index 0000000..3d839fe --- /dev/null +++ b/sky-common/src/main/java/com/sky/common/config/RestTemplateConfiguration.java @@ -0,0 +1,16 @@ +package com.sky.common.config; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.client.RestTemplate; + +@Configuration +@Slf4j +public class RestTemplateConfiguration { + @Bean + public RestTemplate restTemplate() { + log.info("注入restTemplate"); + return new RestTemplate(); + } +} \ No newline at end of file diff --git a/sky-common/src/main/java/com/sky/common/config/WebMvcConfiguration.java b/sky-common/src/main/java/com/sky/common/config/WebMvcConfiguration.java new file mode 100644 index 0000000..372e0c5 --- /dev/null +++ b/sky-common/src/main/java/com/sky/common/config/WebMvcConfiguration.java @@ -0,0 +1,59 @@ +package com.sky.common.config; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.sky.common.interceptor.JwtTokenUserInterceptor; +import com.sky.common.interceptor.RedisTokenAdminInterceptor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Configuration; +import org.springframework.http.converter.HttpMessageConverter; +import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; +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.util.List; + +@Configuration +@Slf4j +public class WebMvcConfiguration extends WebMvcConfigurationSupport { + @Autowired + RedisTokenAdminInterceptor adminInterceptor; + @Autowired + JwtTokenUserInterceptor userInterceptor; + + /** + * 注册自定义拦截器 + * + * @param registry InterceptorRegistry + */ + protected void addInterceptors(InterceptorRegistry registry) { + log.info("开始注册自定义拦截器..."); + registry.addInterceptor(adminInterceptor).addPathPatterns("/admin/**") + .excludePathPatterns("/admin/employee/login"); + registry.addInterceptor(userInterceptor).addPathPatterns("/user/**") + .excludePathPatterns("/user/user/login") + .excludePathPatterns("/user/shop/status"); + } + + /** + * 扩展Spring MVC框架的消息转化器 + * + * @param converters 转换器 + */ + protected void extendMessageConverters(List> converters) { + log.info("扩展消息转换器..."); + // 创建一个消息转换器对象 + MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter(); + // 需要为消息转换器设置一个对象转换器,对象转换器可以将Java对象序列化为json数据 + converter.setObjectMapper(new ObjectMapper()); + // 将自己的消息转化器加入容器中 + converters.add(0, converter); + } + + protected void addResourceHandlers(ResourceHandlerRegistry registry) { + log.info("设置静态资源映射"); + registry.addResourceHandler("/doc.html").addResourceLocations("classpath:/META-INF/resources/"); + registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/"); + } +} diff --git a/sky-common/src/main/java/com/sky/common/config/WebSocketConfiguration.java b/sky-common/src/main/java/com/sky/common/config/WebSocketConfiguration.java new file mode 100644 index 0000000..e30d3c6 --- /dev/null +++ b/sky-common/src/main/java/com/sky/common/config/WebSocketConfiguration.java @@ -0,0 +1,19 @@ +package com.sky.common.config; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.socket.server.standard.ServerEndpointExporter; + +/** + * WebSocket配置类,用于注册WebSocket的Bean + */ +@Configuration +@Slf4j +public class WebSocketConfiguration { + @Bean + public ServerEndpointExporter serverEndpointExporter() { + log.info("WebSocket配置类,用于注册WebSocket的Bean"); + return new ServerEndpointExporter(); + } +} \ No newline at end of file diff --git a/sky-common/src/main/java/com/sky/common/context/BaseContext.java b/sky-common/src/main/java/com/sky/common/context/BaseContext.java new file mode 100644 index 0000000..311b0f6 --- /dev/null +++ b/sky-common/src/main/java/com/sky/common/context/BaseContext.java @@ -0,0 +1,30 @@ +package com.sky.common.context; + +public class BaseContext { + public static ThreadLocal threadLocal = new ThreadLocal<>(); + + /** + * 获取当前用户id + * + * @return 用户id + */ + public static Long getUserId() { + return threadLocal.get(); + } + + /** + * 设置用户id + * + * @param userId 用户id + */ + public static void setUserId(Long userId) { + threadLocal.set(userId); + } + + /** + * 移出当前id + */ + public static void removeCurrentId() { + threadLocal.remove(); + } +} diff --git a/sky-common/src/main/java/com/sky/common/handler/GlobalExceptionHandler.java b/sky-common/src/main/java/com/sky/common/handler/GlobalExceptionHandler.java new file mode 100644 index 0000000..b430184 --- /dev/null +++ b/sky-common/src/main/java/com/sky/common/handler/GlobalExceptionHandler.java @@ -0,0 +1,36 @@ +package com.sky.common.handler; + +import com.sky.common.constant.MessageConstant; +import com.sky.common.exception.BaseException; +import com.sky.common.result.Result; +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RestControllerAdvice; + +import java.sql.SQLIntegrityConstraintViolationException; + +@RestControllerAdvice +@Slf4j +public class GlobalExceptionHandler { + // 捕获业务异常 + @ExceptionHandler + public Result exceptionHandler(BaseException exception) { + log.error("异常信息:{}", exception.getMessage()); + return Result.error(exception.getMessage()); + } + + // 处理SQL异常 + public Result exceptionHandler(SQLIntegrityConstraintViolationException exception) { + log.error("处理SQL异常:{}", exception.getMessage()); + String message = exception.getMessage(); + if (message.contains("Duplicate entry")) { + // 截取用户名 + String username = message.split(" ")[2]; + // 错误信息 + String errorMessage = username + MessageConstant.ALREADY_EXISTS; + return Result.error(errorMessage); + } else { + return Result.error(MessageConstant.UNKNOWN_ERROR); + } + } +} diff --git a/sky-common/src/main/java/com/sky/common/interceptor/JwtTokenUserInterceptor.java b/sky-common/src/main/java/com/sky/common/interceptor/JwtTokenUserInterceptor.java new file mode 100644 index 0000000..ccbfb56 --- /dev/null +++ b/sky-common/src/main/java/com/sky/common/interceptor/JwtTokenUserInterceptor.java @@ -0,0 +1,45 @@ +package com.sky.common.interceptor; + +import com.sky.common.constant.JwtClaimsConstant; +import com.sky.common.context.BaseContext; +import com.sky.common.properties.RedisTokenProperties; +import com.sky.common.utils.JwtUtil; +import io.jsonwebtoken.Claims; +import lombok.extern.slf4j.Slf4j; +import org.apache.http.HttpStatus; +import org.checkerframework.checker.nullness.qual.NonNull; +import org.springframework.beans.factory.annotation.Autowired; +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; + +@Component +@Slf4j +public class JwtTokenUserInterceptor implements HandlerInterceptor { + @Autowired + RedisTokenProperties redisTokenProperties; + + public boolean preHandle(@NonNull HttpServletRequest request, @NonNull HttpServletResponse response, @NonNull Object handler) throws Exception { + // 判断当前拦截到的是Controller方法还是其它资源 + if (!(handler instanceof HandlerMethod)) { + // 当前拦截到的不是动态方法,直接放行 + return true; + } + // 1. 从请求头中获取令牌 + String token = request.getHeader(redisTokenProperties.getUserTokenName()); + // 2. 校验令牌 + log.info("jwt校验:{}", token); + try { + Claims claims = JwtUtil.parseJWT(redisTokenProperties.getUserSecretKey(), token); + Long userId = Long.valueOf(claims.get(JwtClaimsConstant.USER_ID).toString()); + BaseContext.setUserId(userId); + return true; + } catch (Exception exception) { + response.setStatus(HttpStatus.SC_UNAUTHORIZED); + return false; + } + } +} diff --git a/sky-common/src/main/java/com/sky/common/interceptor/RedisTokenAdminInterceptor.java b/sky-common/src/main/java/com/sky/common/interceptor/RedisTokenAdminInterceptor.java new file mode 100644 index 0000000..69090f4 --- /dev/null +++ b/sky-common/src/main/java/com/sky/common/interceptor/RedisTokenAdminInterceptor.java @@ -0,0 +1,49 @@ +package com.sky.common.interceptor; + +import com.sky.common.context.BaseContext; +import com.sky.common.properties.RedisTokenProperties; +import com.sky.common.utils.InterceptorUtil; +import lombok.extern.slf4j.Slf4j; +import org.jetbrains.annotations.NotNull; +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.security.auth.login.LoginException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; + +@Component +@Slf4j +public class RedisTokenAdminInterceptor implements HandlerInterceptor { + @Autowired + private RedisTokenProperties redisTokenProperties; + @Autowired + private RedisTemplate redisTemplate; + + public boolean preHandle(@NotNull HttpServletRequest request, @NotNull HttpServletResponse response, @NotNull Object handler) throws IOException, LoginException { + // 判断当前兰街道的是Controller的方法还是其它资源 拦截到的不是动态方法,直接放行 + if (!(handler instanceof HandlerMethod)) return true; + + // 1. 从请求头中获取令牌 + String token = request.getHeader(redisTokenProperties.getAdminTokenName()); + log.info("jwt校验:{}", token); + if (token == null) { + InterceptorUtil.unLoginInterceptor(response); + return false; + } + + try { + Long empId = (Long) redisTemplate.opsForValue().get(token); + log.info("当前员工ID:{}", empId); + // 3. 通过放行 + BaseContext.setUserId(empId); + return true; + } catch (Exception exception) { + return false; + } + } +} diff --git a/sky-common/src/main/java/com/sky/common/json/JacksonObjectMapper.java b/sky-common/src/main/java/com/sky/common/json/JacksonObjectMapper.java new file mode 100644 index 0000000..fa1ea24 --- /dev/null +++ b/sky-common/src/main/java/com/sky/common/json/JacksonObjectMapper.java @@ -0,0 +1,47 @@ +package com.sky.common.json; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.module.SimpleModule; +import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer; +import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer; +import com.fasterxml.jackson.datatype.jsr310.deser.LocalTimeDeserializer; +import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer; +import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer; +import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer; + +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.format.DateTimeFormatter; + +import static com.fasterxml.jackson.databind.DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES; + +/** + * 对象映射器,基于Jackson将Java对象转为JSON,或者将JSON转为Java对象 + * 将JSON转为Java对象过程--JSON反序列化Java对象 + * 将Java对象转为JSON--序列化Java对象到JSON + */ +public class JacksonObjectMapper extends ObjectMapper { + public static final String DEFAULT_DATE_FORMAT = "yyyy-MM-dd"; + // public static final String DEFAULT_DATE_TIME_FORMAT = "yyyy-MM-dd HH:mm:ss"; + public static final String DEFAULT_DATE_TIME_FORMAT = "yyyy-MM-dd HH:mm"; + public static final String DEFAULT_TIME_FORMAT = "HH:mm:ss"; + + public JacksonObjectMapper() { + super(); + // 收到未知属性时不报异常 + this.configure(FAIL_ON_UNKNOWN_PROPERTIES, false); + // 反序列化时,属性不存在的兼容处理 + this.getDeserializationConfig().withoutFeatures(FAIL_ON_UNKNOWN_PROPERTIES); + + SimpleModule simpleModule = new SimpleModule(); + simpleModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_TIME_FORMAT))) + .addDeserializer(LocalDate.class, new LocalDateDeserializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_FORMAT))) + .addDeserializer(LocalTime.class, new LocalTimeDeserializer(DateTimeFormatter.ofPattern(DEFAULT_TIME_FORMAT))) + .addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_TIME_FORMAT))) + .addSerializer(LocalDate.class, new LocalDateSerializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_FORMAT))) + .addSerializer(LocalTime.class, new LocalTimeSerializer(DateTimeFormatter.ofPattern(DEFAULT_TIME_FORMAT))); + // 注册功能模块 例如,可以添加自定义序列化器和反序列化器 + this.registerModule(simpleModule); + } +} diff --git a/sky-common/src/main/java/com/sky/common/properties/JwtProperties.java b/sky-common/src/main/java/com/sky/common/properties/RedisTokenProperties.java similarity index 85% rename from sky-common/src/main/java/com/sky/common/properties/JwtProperties.java rename to sky-common/src/main/java/com/sky/common/properties/RedisTokenProperties.java index 00936eb..d737851 100644 --- a/sky-common/src/main/java/com/sky/common/properties/JwtProperties.java +++ b/sky-common/src/main/java/com/sky/common/properties/RedisTokenProperties.java @@ -5,9 +5,9 @@ import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.stereotype.Component; @Component -@ConfigurationProperties(prefix = "sky.jwt") +@ConfigurationProperties(prefix = "sky.redis-token") @Data -public class JwtProperties { +public class RedisTokenProperties { /** * 管理端员工生成jwt令牌相关配置 */ diff --git a/sky-common/src/main/java/com/sky/common/result/PageResult.java b/sky-common/src/main/java/com/sky/common/result/PageResult.java new file mode 100644 index 0000000..c127ece --- /dev/null +++ b/sky-common/src/main/java/com/sky/common/result/PageResult.java @@ -0,0 +1,19 @@ +package com.sky.common.result; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; +import java.util.List; + +/** + * 封装分页查询结果 + */ +@Data +@AllArgsConstructor +@NoArgsConstructor +public class PageResult implements Serializable { + private long total; // 总记录数 + private List records; // 当前页数据集合 +} diff --git a/sky-common/src/main/java/com/sky/common/result/Result.java b/sky-common/src/main/java/com/sky/common/result/Result.java new file mode 100644 index 0000000..401546f --- /dev/null +++ b/sky-common/src/main/java/com/sky/common/result/Result.java @@ -0,0 +1,40 @@ +package com.sky.common.result; + +import lombok.Data; + +import java.io.Serializable; + +/** + * 后端统一返回结果 + * + * @param + */ +@Data +public class Result implements Serializable { + private Integer code; // 编码:1成功,0和其它数字为失败 + private String message; // 错误信息 + private T data; // 数据 + + // 成功 + public static Result success() { + Result result = new Result<>(); + result.code = 1; + return result; + } + + // 有数据返回 + public static Result success(T object) { + Result result = new Result<>(); + result.code = 1; + result.data = object; + return result; + } + + // 错误返回 + public static Result error(String message) { + Result result = new Result<>(); + result.code = 0; + result.message = message; + return result; + } +} \ No newline at end of file diff --git a/sky-common/src/main/java/com/sky/common/utils/HttpClientUtil.java b/sky-common/src/main/java/com/sky/common/utils/HttpClientUtil.java new file mode 100644 index 0000000..b98c434 --- /dev/null +++ b/sky-common/src/main/java/com/sky/common/utils/HttpClientUtil.java @@ -0,0 +1,168 @@ +package com.sky.common.utils; + +import com.alibaba.fastjson.JSONObject; +import org.apache.http.NameValuePair; +import org.apache.http.client.config.RequestConfig; +import org.apache.http.client.entity.UrlEncodedFormEntity; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.client.utils.URIBuilder; +import org.apache.http.entity.StringEntity; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClients; +import org.apache.http.message.BasicNameValuePair; +import org.apache.http.util.EntityUtils; + +import java.io.IOException; +import java.net.URI; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +/** + * Http工具类 + */ +public class HttpClientUtil { + + static final int TIMEOUT_MSEC = 5 * 1000; + + /** + * 发送GET方式请求 + */ + public static String doGet(String url, Map paramMap) { + // 创建Httpclient对象 + CloseableHttpClient httpClient = HttpClients.createDefault(); + + String result = ""; + CloseableHttpResponse response = null; + + try { + URIBuilder builder = new URIBuilder(url); + if (paramMap != null) { + for (String key : paramMap.keySet()) { + builder.addParameter(key, paramMap.get(key)); + } + } + URI uri = builder.build(); + + // 创建GET请求 + HttpGet httpGet = new HttpGet(uri); + + // 发送请求 + response = httpClient.execute(httpGet); + + // 判断响应状态 + if (response.getStatusLine().getStatusCode() == 200) { + result = EntityUtils.toString(response.getEntity(), "UTF-8"); + } + } catch (Exception e) { + e.printStackTrace(); + } finally { + try { + response.close(); + httpClient.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + return result; + } + + /** + * 发送POST方式请求 + */ + public static String doPost(String url, Map paramMap) throws IOException { + // 创建Httpclient对象 + CloseableHttpClient httpClient = HttpClients.createDefault(); + CloseableHttpResponse response = null; + String resultString = ""; + + try { + // 创建Http Post请求 + HttpPost httpPost = new HttpPost(url); + + // 创建参数列表 + if (paramMap != null) { + List paramList = new ArrayList(); + for (Map.Entry param : paramMap.entrySet()) { + paramList.add(new BasicNameValuePair(param.getKey(), param.getValue())); + } + // 模拟表单 + UrlEncodedFormEntity entity = new UrlEncodedFormEntity(paramList); + httpPost.setEntity(entity); + } + + httpPost.setConfig(builderRequestConfig()); + + // 执行http请求 + response = httpClient.execute(httpPost); + + resultString = EntityUtils.toString(response.getEntity(), "UTF-8"); + } catch (Exception e) { + throw e; + } finally { + try { + response.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + return resultString; + } + + /** + * 发送POST方式请求 + */ + public static String doPost4Json(String url, Map paramMap) throws IOException { + // 创建Httpclient对象 + CloseableHttpClient httpClient = HttpClients.createDefault(); + CloseableHttpResponse response = null; + String resultString = ""; + + try { + // 创建Http Post请求 + HttpPost httpPost = new HttpPost(url); + + if (paramMap != null) { + // 构造json格式数据 + JSONObject jsonObject = new JSONObject(); + for (Map.Entry param : paramMap.entrySet()) { + jsonObject.put(param.getKey(), param.getValue()); + } + StringEntity entity = new StringEntity(jsonObject.toString(), "utf-8"); + // 设置请求编码 + entity.setContentEncoding("utf-8"); + // 设置数据类型 + entity.setContentType("application/json"); + httpPost.setEntity(entity); + } + + httpPost.setConfig(builderRequestConfig()); + + // 执行http请求 + response = httpClient.execute(httpPost); + + resultString = EntityUtils.toString(response.getEntity(), "UTF-8"); + } catch (Exception e) { + throw e; + } finally { + try { + response.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + return resultString; + } + + private static RequestConfig builderRequestConfig() { + return RequestConfig.custom() + .setConnectTimeout(TIMEOUT_MSEC) + .setConnectionRequestTimeout(TIMEOUT_MSEC) + .setSocketTimeout(TIMEOUT_MSEC).build(); + } +} diff --git a/sky-common/src/main/java/com/sky/common/utils/InterceptorUtil.java b/sky-common/src/main/java/com/sky/common/utils/InterceptorUtil.java new file mode 100644 index 0000000..eb93468 --- /dev/null +++ b/sky-common/src/main/java/com/sky/common/utils/InterceptorUtil.java @@ -0,0 +1,42 @@ +package com.sky.common.utils; + +import com.alibaba.fastjson.JSON; +import com.sky.common.result.Result; +import lombok.extern.slf4j.Slf4j; + +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; + +@Slf4j +public class InterceptorUtil { + /** + * 用户未登录返回响应 + * + * @param response 返回体 + */ + public static void unLoginInterceptor(HttpServletResponse response) throws IOException { + log.info("用户未登录"); + response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); + response.setContentType("application/json"); + response.setCharacterEncoding("UTF-8"); + Result result = new Result<>(); + result.setCode(HttpServletResponse.SC_UNAUTHORIZED); + result.setData("未登录"); + result.setMessage("请先登录"); + // 将消息写入响应体 + response.getWriter().write(JSON.toJSONString(result)); + } + + public static void otherLoginInterceptor(HttpServletResponse response) throws IOException { + log.info("其它异常"); + response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); + response.setContentType("application/json"); + response.setCharacterEncoding("UTF-8"); + Result result = new Result<>(); + result.setCode(HttpServletResponse.SC_UNAUTHORIZED); + result.setData("未登录"); + result.setMessage("请先登录"); + // 将消息写入响应体 + response.getWriter().write(JSON.toJSONString(result)); + } +} diff --git a/sky-common/src/main/java/com/sky/common/utils/JwtUtil.java b/sky-common/src/main/java/com/sky/common/utils/JwtUtil.java new file mode 100644 index 0000000..509f6c4 --- /dev/null +++ b/sky-common/src/main/java/com/sky/common/utils/JwtUtil.java @@ -0,0 +1,53 @@ +package com.sky.common.utils; + +import io.jsonwebtoken.Claims; +import io.jsonwebtoken.JwtBuilder; +import io.jsonwebtoken.Jwts; +import io.jsonwebtoken.SignatureAlgorithm; + +import java.nio.charset.StandardCharsets; +import java.util.Date; +import java.util.Map; + +public class JwtUtil { + /** + * 生成JWT + * 使用Hs256算法, 私匙使用固定秘钥 + * + * @param secretKey jwt秘钥 + * @param ttlMillis jwt过期时间(毫秒) + * @param claims 设置的信息 + * @return 加密后JWT + */ + public static String createJWT(String secretKey, long ttlMillis, Map claims) { + // 指定签名的时候使用的签名算法,也就是header那部分 + SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256; + // 生成JWT的时间 + long expMillis = System.currentTimeMillis() + ttlMillis; + Date date = new Date(expMillis); + JwtBuilder builder = Jwts.builder() + // 如果有私有声明,一定要先设置这个自己创建的私有的声明,这个是给builder的claim赋值,一旦写在标准的声明赋值之后,就是覆盖了那些标准的声明的 + .setClaims(claims) + // 设置签名使用的签名算法和签名使用的秘钥 + .signWith(signatureAlgorithm, secretKey.getBytes(StandardCharsets.UTF_8)) + // 设置过期时间 + .setExpiration(date); + return builder.compact(); + } + + /** + * Token解密 + * + * @param secretKey jwt秘钥 + * @param token token + * @return 解密后数据 + */ + public static Claims parseJWT(String secretKey, String token) { + // 得到DefaultJwtParser + return Jwts.parser() + // 设置签名的秘钥 + .setSigningKey(secretKey.getBytes(StandardCharsets.UTF_8)) + // 设置需要解析的jwt + .parseClaimsJwt(token).getBody(); + } +} diff --git a/sky-common/src/main/java/com/sky/common/utils/MD5.java b/sky-common/src/main/java/com/sky/common/utils/MD5.java new file mode 100644 index 0000000..64c5bb0 --- /dev/null +++ b/sky-common/src/main/java/com/sky/common/utils/MD5.java @@ -0,0 +1,34 @@ +package com.sky.common.utils; + +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; + + +public final class MD5 { + + public static String encrypt(String strSrc) { + try { + char[] hexChars = {'0', '1', '2', '3', '4', '5', '6', '7', '8', + '9', 'a', 'b', 'c', 'd', 'e', 'f'}; + byte[] bytes = strSrc.getBytes(); + MessageDigest md = MessageDigest.getInstance("MD5"); + md.update(bytes); + bytes = md.digest(); + int j = bytes.length; + char[] chars = new char[j * 2]; + int k = 0; + for (byte b : bytes) { + chars[k++] = hexChars[b >>> 4 & 0xf]; + chars[k++] = hexChars[b & 0xf]; + } + return new String(chars); + } catch (NoSuchAlgorithmException e) { + e.printStackTrace(); + throw new RuntimeException("MD5加密出错!!+" + e); + } + } + + public static void main(String[] args) { + System.out.println(MD5.encrypt("111111")); + } +} diff --git a/sky-common/src/main/java/com/sky/common/utils/MinioUtils.java b/sky-common/src/main/java/com/sky/common/utils/MinioUtils.java new file mode 100644 index 0000000..dc451ff --- /dev/null +++ b/sky-common/src/main/java/com/sky/common/utils/MinioUtils.java @@ -0,0 +1,112 @@ +package com.sky.common.utils; + + +import com.sky.common.properties.MinioProperties; +import io.minio.*; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +import java.io.InputStream; + +@Component +@RequiredArgsConstructor +@Slf4j +public class MinioUtils { + private final MinioClient minioClient; + private final MinioProperties minioProperties; + + /** + * 判断桶是否存在 + * + * @param bucketName String + * @return found + */ + public boolean bucketExists(String bucketName) { + boolean found = false; + try { + found = minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build()); + } catch (Exception e) { + e.printStackTrace(); + } + return found; + } + + /** + * 如果桶不存在就新建 + * + * @param bucketName String + */ + public void bucketCreate(String bucketName) { + boolean exists = bucketExists(bucketName); + if (!exists) { + try { + minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build()); + } catch (Exception e) { + e.printStackTrace(); + } + } + } + + /** + * 上传文件 + * + * @param fileName 文件名 + * @param inputStream 输入流 + * @param size 文件大小 + */ + public String uploadFile(String bucketName, String fileName, InputStream inputStream, Long size) { + try { + minioClient.putObject(PutObjectArgs.builder() + .bucket(bucketName) + .object(fileName) + .stream(inputStream, size, -1) + .build()); + return minioProperties.getEndpointUrl() + "/" + bucketName + "/" + fileName; + } catch (Exception e) { + e.printStackTrace(); + return null; + } + } + + /** + * 上传文件 + * + * @param fileName 文件名 + * @param inputStream 输入流 + * @param size 文件大小 + * @param contentType 文件类型 + */ + public String uploadFile(String bucketName, String fileName, InputStream inputStream, Long size, String contentType) { + try { + minioClient.putObject(PutObjectArgs.builder() + .bucket(bucketName) + .object(fileName) + .stream(inputStream, size, -1) + .contentType(contentType) + .build()); + return minioProperties.getEndpointUrl() + "/" + bucketName + "/" + fileName; + } catch (Exception e) { + e.printStackTrace(); + return null; + } + } + + /** + * 获取文件 + * + * @param bucketName 桶名称 + * @param fileName 对象名称 + */ + public InputStream getFile(String bucketName, String fileName) { + try { + return minioClient.getObject(GetObjectArgs.builder() + .bucket(bucketName) + .object(fileName) + .build()); + } catch (Exception e) { + e.printStackTrace(); + return null; + } + } +} \ No newline at end of file diff --git a/sky-common/src/main/java/com/sky/common/utils/RedisUtil.java b/sky-common/src/main/java/com/sky/common/utils/RedisUtil.java new file mode 100644 index 0000000..f7ec95a --- /dev/null +++ b/sky-common/src/main/java/com/sky/common/utils/RedisUtil.java @@ -0,0 +1,20 @@ +package com.sky.common.utils; + +import lombok.RequiredArgsConstructor; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.stereotype.Component; + +import java.util.Set; + +@Component +@RequiredArgsConstructor +public class RedisUtil { + private static final RedisTemplate redisTemplate = new RedisTemplate<>(); + + public static void cleanCache(String key) { + Set keys = redisTemplate.keys(key); + if (keys != null) { + redisTemplate.delete(keys); + } + } +} diff --git a/sky-common/src/main/java/com/sky/common/utils/RestTemplateUtil.java b/sky-common/src/main/java/com/sky/common/utils/RestTemplateUtil.java new file mode 100644 index 0000000..33b95e2 --- /dev/null +++ b/sky-common/src/main/java/com/sky/common/utils/RestTemplateUtil.java @@ -0,0 +1,27 @@ +package com.sky.common.utils; + +import lombok.RequiredArgsConstructor; +import org.springframework.core.ParameterizedTypeReference; +import org.springframework.http.*; +import org.springframework.stereotype.Component; +import org.springframework.web.client.RestTemplate; + +import java.io.Serializable; + +@Component +@RequiredArgsConstructor +public class RestTemplateUtil implements Serializable { + private final RestTemplate restTemplate; + + public ResponseEntity requestGet(String url, T t) { + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_JSON); + + HttpEntity entity = new HttpEntity(t, headers); + + ResponseEntity response = restTemplate.exchange(url, HttpMethod.GET, entity, new ParameterizedTypeReference() { + }); + + return response.getStatusCode().is2xxSuccessful() ? response : null; + } +} diff --git a/sky-common/src/main/java/com/sky/common/utils/WeChatPayUtil.java b/sky-common/src/main/java/com/sky/common/utils/WeChatPayUtil.java new file mode 100644 index 0000000..d73fb35 --- /dev/null +++ b/sky-common/src/main/java/com/sky/common/utils/WeChatPayUtil.java @@ -0,0 +1,225 @@ +package com.sky.common.utils; + +import com.alibaba.fastjson.JSONObject; +import com.sky.common.context.BaseContext; +import com.sky.common.properties.WeChatProperties; +import com.wechat.pay.contrib.apache.httpclient.WechatPayHttpClientBuilder; +import com.wechat.pay.contrib.apache.httpclient.util.PemUtil; +import lombok.RequiredArgsConstructor; +import org.apache.commons.lang.RandomStringUtils; +import org.apache.http.HttpHeaders; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.entity.ContentType; +import org.apache.http.entity.StringEntity; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.util.EntityUtils; +import org.springframework.stereotype.Component; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.security.PrivateKey; +import java.security.cert.X509Certificate; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.UUID; + +/** + * 微信支付工具类 + */ +@Component +@RequiredArgsConstructor +public class WeChatPayUtil { + // 微信支付下单接口地址 + public static final String JSAPI = "https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi"; + // 申请退款接口地址 + public static final String REFUNDS = "https://api.mch.weixin.qq.com/v3/refund/domestic/refunds"; + private final WeChatProperties weChatProperties; + + /** + * 获取调用微信接口的客户端工具对象 + */ + private CloseableHttpClient getClient() { + PrivateKey merchantPrivateKey = null; + try { + // merchantPrivateKey商户API私钥,如何加载商户API私钥请看常见问题 + merchantPrivateKey = PemUtil.loadPrivateKey(new FileInputStream(new File(weChatProperties.getPrivateKeyFilePath()))); + // 加载平台证书文件 + X509Certificate x509Certificate = PemUtil.loadCertificate(new FileInputStream(new File(weChatProperties.getWeChatPayCertFilePath()))); + // wechatPayCertificates微信支付平台证书列表。你也可以使用后面章节提到的“定时更新平台证书功能”,而不需要关心平台证书的来龙去脉 + List wechatPayCertificates = Collections.singletonList(x509Certificate); + + WechatPayHttpClientBuilder builder = WechatPayHttpClientBuilder.create() + .withMerchant(weChatProperties.getMchid(), weChatProperties.getMchSerialNo(), merchantPrivateKey) + .withWechatPay(wechatPayCertificates); + + // 通过WechatPayHttpClientBuilder构造的HttpClient,会自动的处理签名和验签 + CloseableHttpClient httpClient = builder.build(); + return httpClient; + } catch (FileNotFoundException e) { + e.printStackTrace(); + return null; + } + } + + /** + * 发送post方式请求 + * + * @param url + * @param body + * @return + */ + private String post(String url, String body) throws Exception { + CloseableHttpClient httpClient = getClient(); + + HttpPost httpPost = new HttpPost(url); + httpPost.addHeader(HttpHeaders.ACCEPT, ContentType.APPLICATION_JSON.toString()); + httpPost.addHeader(HttpHeaders.CONTENT_TYPE, ContentType.APPLICATION_JSON.toString()); + httpPost.addHeader("Wechatpay-Serial", weChatProperties.getMchSerialNo()); + httpPost.setEntity(new StringEntity(body, "UTF-8")); + + CloseableHttpResponse response = httpClient.execute(httpPost); + try { + return EntityUtils.toString(response.getEntity()); + } finally { + httpClient.close(); + response.close(); + } + } + + /** + * 发送get方式请求 + * + * @param url + * @return + */ + private String get(String url) throws Exception { + CloseableHttpClient httpClient = getClient(); + + HttpGet httpGet = new HttpGet(url); + httpGet.addHeader(HttpHeaders.ACCEPT, ContentType.APPLICATION_JSON.toString()); + httpGet.addHeader(HttpHeaders.CONTENT_TYPE, ContentType.APPLICATION_JSON.toString()); + httpGet.addHeader("Wechatpay-Serial", weChatProperties.getMchSerialNo()); + + CloseableHttpResponse response = httpClient.execute(httpGet); + try { + return EntityUtils.toString(response.getEntity()); + } finally { + httpClient.close(); + response.close(); + } + } + + /** + * jsapi下单 + * + * @param orderNum 商户订单号 + * @param total 总金额 + * @param description 商品描述 + * @param openid 微信用户的openid + */ + private String jsapi(String orderNum, BigDecimal total, String description, String openid) throws Exception { + JSONObject jsonObject = new JSONObject(); + jsonObject.put("appid", weChatProperties.getAppid()); + jsonObject.put("mchid", weChatProperties.getMchid()); + jsonObject.put("description", description); + jsonObject.put("out_trade_no", orderNum); + jsonObject.put("notify_url", weChatProperties.getNotifyUrl()); + + JSONObject amount = new JSONObject(); + amount.put("total", total.multiply(new BigDecimal(100)).setScale(2, RoundingMode.HALF_UP).intValue()); + amount.put("currency", "CNY"); + + jsonObject.put("amount", amount); + + JSONObject payer = new JSONObject(); + payer.put("openid", openid); + + jsonObject.put("payer", payer); + + String body = jsonObject.toJSONString(); + return post(JSAPI, body); + } + + /** + * 小程序支付 + * + * @param orderNum 商户订单号 + * @param total 金额,单位 元 + * @param description 商品描述 + * @param openid 微信用户的openid + * @return + */ + public JSONObject pay(String orderNum, BigDecimal total, String description, String openid) throws Exception { + // 统一下单,生成预支付交易单 + // String bodyAsString = jsapi(orderNum, total, description, openid); + // 解析返回结果 + // JSONObject jsonObject = JSON.parseObject(bodyAsString); + + // String prepayId = jsonObject.getString("prepay_id"); + String prepayId = "bunny-" + UUID.randomUUID(); + String timeStamp = String.valueOf(System.currentTimeMillis() / 1000); + String nonceStr = RandomStringUtils.randomNumeric(32); + ArrayList list = new ArrayList<>(); + list.add(weChatProperties.getAppid()); + list.add(timeStamp); + list.add(nonceStr); + list.add("prepay_id=" + prepayId); + // 二次签名,调起支付需要重新签名 + // StringBuilder stringBuilder = new StringBuilder(); + // for (Object o : list) { + // stringBuilder.append(o).append("\n"); + // } + // String signMessage = stringBuilder.toString(); + // byte[] message = signMessage.getBytes(); + + // Signature signature = Signature.getInstance("SHA256withRSA"); + // signature.initSign(PemUtil.loadPrivateKey(Files.newInputStream(new File(weChatProperties.getPrivateKeyFilePath()).toPath()))); + // signature.update(message); + // String packageSign = Base64.getEncoder().encodeToString(signature.sign()); + + // 构造数据给微信小程序,用于调起微信支付 + JSONObject jo = new JSONObject(); + jo.put("timeStamp", timeStamp); + jo.put("nonceStr", nonceStr); + jo.put("package", "prepay_id=" + prepayId); + jo.put("signType", "RSA"); + jo.put("paySign", BaseContext.getUserId() + "-" + prepayId); + + return jo; + // return jsonObject; + } + + /** + * 申请退款 + * + * @param outTradeNo 商户订单号 + * @param outRefundNo 商户退款单号 + * @param refund 退款金额 + * @param total 原订单金额 + * @return + */ + public String refund(String outTradeNo, String outRefundNo, BigDecimal refund, BigDecimal total) throws Exception { + JSONObject jsonObject = new JSONObject(); + jsonObject.put("out_trade_no", outTradeNo); + jsonObject.put("out_refund_no", outRefundNo); + + JSONObject amount = new JSONObject(); + amount.put("refund", refund.multiply(new BigDecimal(100)).setScale(2, RoundingMode.HALF_UP).intValue()); + amount.put("total", total.multiply(new BigDecimal(100)).setScale(2, RoundingMode.HALF_UP).intValue()); + amount.put("currency", "CNY"); + + jsonObject.put("amount", amount); + jsonObject.put("notify_url", weChatProperties.getRefundNotifyUrl()); + + String body = jsonObject.toJSONString(); + + // 调用申请退款接口 + return post(REFUNDS, body); + } +} diff --git a/sky-pojo/pom.xml b/sky-pojo/pom.xml index 2df6dcc..28f3c38 100644 --- a/sky-pojo/pom.xml +++ b/sky-pojo/pom.xml @@ -1,4 +1,4 @@ - 4.0.0 @@ -22,5 +22,9 @@ org.projectlombok lombok + + com.github.xiaoymin + knife4j-spring-boot-starter + diff --git a/sky-server/pom.xml b/sky-server/pom.xml index e8eda8a..c443207 100644 --- a/sky-server/pom.xml +++ b/sky-server/pom.xml @@ -1,6 +1,9 @@ - + 4.0.0 + com.sky dev-sky-serve-v1 @@ -9,26 +12,12 @@ sky-server jar - - sky-server - http://maven.apache.org - + 8 + 8 UTF-8 - - - com.sky - sky-common - 1.0-SNAPSHOT - - - com.sky - sky-pojo - 1.0-SNAPSHOT - - org.springframework.boot spring-boot-starter @@ -131,6 +120,17 @@ junit 4.13.1 + + + com.sky + sky-common + 1.0-SNAPSHOT + + + com.sky + sky-pojo + 1.0-SNAPSHOT + @@ -149,4 +149,4 @@ - + \ No newline at end of file diff --git a/sky-server/src/main/java/com/sky/SkyApplication.java b/sky-server/src/main/java/com/sky/SkyApplication.java index bd4c639..b2dbf88 100644 --- a/sky-server/src/main/java/com/sky/SkyApplication.java +++ b/sky-server/src/main/java/com/sky/SkyApplication.java @@ -14,6 +14,6 @@ import org.springframework.transaction.annotation.EnableTransactionManagement; @Slf4j public class SkyApplication { public static void main(String[] args) { - SpringApplication.run(SkyApplication.class); + SpringApplication.run(SkyApplication.class, args); } } diff --git a/sky-server/src/main/java/com/sky/annotation/AutoFill.java b/sky-server/src/main/java/com/sky/annotation/AutoFill.java new file mode 100644 index 0000000..ae60627 --- /dev/null +++ b/sky-server/src/main/java/com/sky/annotation/AutoFill.java @@ -0,0 +1,14 @@ +package com.sky.annotation; + +import com.sky.common.enumeration.OperationType; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +public @interface AutoFill { + OperationType value(); +} diff --git a/sky-server/src/main/java/com/sky/aspect/AutoFillAspect.java b/sky-server/src/main/java/com/sky/aspect/AutoFillAspect.java new file mode 100644 index 0000000..1fd3c9f --- /dev/null +++ b/sky-server/src/main/java/com/sky/aspect/AutoFillAspect.java @@ -0,0 +1,13 @@ +package com.sky.aspect; + +import lombok.extern.slf4j.Slf4j; +import org.aspectj.lang.annotation.Aspect; +import org.springframework.stereotype.Component; +/** + * 自定义切面,实现公共字段自动填充处理逻辑 + */ +@Aspect +@Component +@Slf4j +public class AutoFillAspect { +} diff --git a/sky-server/src/main/java/com/sky/controller/admin/EmployeeController.java b/sky-server/src/main/java/com/sky/controller/admin/EmployeeController.java index 1cdecb3..1d32b6e 100644 --- a/sky-server/src/main/java/com/sky/controller/admin/EmployeeController.java +++ b/sky-server/src/main/java/com/sky/controller/admin/EmployeeController.java @@ -1,10 +1,7 @@ package com.sky.controller.admin; -import com.sky.common.constant.JwtClaimsConstant; -import com.sky.common.properties.JwtProperties; import com.sky.common.result.PageResult; import com.sky.common.result.Result; -import com.sky.common.utils.JwtUtil; import com.sky.pojo.dto.EmployeeDTO; import com.sky.pojo.dto.EmployeeLoginDTO; import com.sky.pojo.dto.EmployeePageQueryDTO; @@ -18,9 +15,6 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; -import java.util.HashMap; -import java.util.Map; - /** * 员工管理 */ @@ -29,31 +23,14 @@ import java.util.Map; @Slf4j @Api(tags = "员工相关接口") public class EmployeeController { - @Autowired private EmployeeService employeeService; - @Autowired - private JwtProperties jwtProperties; @PostMapping("/login") @ApiOperation(value = "登录接口") public Result login(@RequestBody EmployeeLoginDTO employeeLoginDTO) { log.info("员工登录:{}", employeeLoginDTO); - - Employee employee = employeeService.login(employeeLoginDTO); - - // 登录成功后,生成jwt令牌 - Map claims = new HashMap<>(); - claims.put(JwtClaimsConstant.EMP_ID, employee.getId()); - String token = JwtUtil.createJWT(jwtProperties.getAdminSecretKey(), jwtProperties.getAdminTtl(), claims); - - EmployeeLoginVO employeeLoginVO = EmployeeLoginVO.builder() - .id(employee.getId()) - .userName(employee.getUsername()) - .name(employee.getName()) - .token(token).build(); - - return Result.success(employeeLoginVO); + return Result.success(employeeService.login(employeeLoginDTO)); } @PostMapping("/logout") diff --git a/sky-server/src/main/java/com/sky/controller/user/UserController.java b/sky-server/src/main/java/com/sky/controller/user/UserController.java index 18a46b3..924f0a5 100644 --- a/sky-server/src/main/java/com/sky/controller/user/UserController.java +++ b/sky-server/src/main/java/com/sky/controller/user/UserController.java @@ -1,7 +1,7 @@ package com.sky.controller.user; import com.sky.common.constant.JwtClaimsConstant; -import com.sky.common.properties.JwtProperties; +import com.sky.common.properties.RedisTokenProperties; import com.sky.common.result.Result; import com.sky.common.utils.JwtUtil; import com.sky.pojo.dto.UserLoginDTO; @@ -28,7 +28,7 @@ public class UserController { @Autowired private UserService userService; @Autowired - private JwtProperties jwtProperties; + private RedisTokenProperties redisTokenProperties; @PostMapping("/login") @ApiOperation("微信登录") @@ -41,7 +41,7 @@ public class UserController { // 为微信用户生成jwt令牌 Map claims = new HashMap<>(); claims.put(JwtClaimsConstant.USER_ID, user.getId()); - String token = JwtUtil.createJWT(jwtProperties.getUserSecretKey(), jwtProperties.getUserTtl(), claims); + String token = JwtUtil.createJWT(redisTokenProperties.getUserSecretKey(), redisTokenProperties.getUserTtl(), claims); UserLoginVO userLoginVO = UserLoginVO.builder() .id(user.getId()) diff --git a/sky-server/src/main/java/com/sky/service/EmployeeService.java b/sky-server/src/main/java/com/sky/service/EmployeeService.java index 22df531..743e12b 100644 --- a/sky-server/src/main/java/com/sky/service/EmployeeService.java +++ b/sky-server/src/main/java/com/sky/service/EmployeeService.java @@ -7,6 +7,7 @@ import com.sky.pojo.dto.EmployeeLoginDTO; import com.sky.pojo.dto.EmployeePageQueryDTO; import com.sky.pojo.dto.PasswordEditDTO; import com.sky.pojo.entity.Employee; +import com.sky.pojo.vo.EmployeeLoginVO; public interface EmployeeService { @@ -16,7 +17,7 @@ public interface EmployeeService { * @param employeeLoginDTO 员工等了信息 * @return 员工信息 */ - Employee login(EmployeeLoginDTO employeeLoginDTO); + EmployeeLoginVO login(EmployeeLoginDTO employeeLoginDTO); /** * 新增员工 diff --git a/sky-server/src/main/java/com/sky/service/impl/EmployeeServiceImpl.java b/sky-server/src/main/java/com/sky/service/impl/EmployeeServiceImpl.java index 3772769..31f33e0 100644 --- a/sky-server/src/main/java/com/sky/service/impl/EmployeeServiceImpl.java +++ b/sky-server/src/main/java/com/sky/service/impl/EmployeeServiceImpl.java @@ -16,21 +16,25 @@ import com.sky.pojo.dto.EmployeeLoginDTO; import com.sky.pojo.dto.EmployeePageQueryDTO; import com.sky.pojo.dto.PasswordEditDTO; import com.sky.pojo.entity.Employee; +import com.sky.pojo.vo.EmployeeLoginVO; import com.sky.service.EmployeeService; import org.springframework.beans.BeanUtils; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Service; import org.springframework.util.DigestUtils; import java.time.LocalDateTime; import java.util.List; import java.util.Objects; +import java.util.UUID; @Service public class EmployeeServiceImpl implements EmployeeService { - @Autowired private EmployeeMapper employeeMapper; + @Autowired + private RedisTemplate redisTemplate; /** * 员工登录 @@ -38,7 +42,7 @@ public class EmployeeServiceImpl implements EmployeeService { * @param employeeLoginDTO 员工登录时传递的数据模型 * @return 员工信息 */ - public Employee login(EmployeeLoginDTO employeeLoginDTO) { + public EmployeeLoginVO login(EmployeeLoginDTO employeeLoginDTO) { String username = employeeLoginDTO.getUsername(); String password = employeeLoginDTO.getPassword(); @@ -64,8 +68,16 @@ public class EmployeeServiceImpl implements EmployeeService { throw new AccountLockedException(MessageConstant.ACCOUNT_LOCKED); } + // TODO 生成token,使用userId存入登录信息 + String token = UUID.randomUUID().toString(); + String userId = employee.getId().toString(); + redisTemplate.opsForValue().set(token, Long.valueOf(userId)); // 3、返回实体对象 - return employee; + return EmployeeLoginVO.builder() + .id(employee.getId()) + .userName(employee.getUsername()) + .name(employee.getName()) + .token(token).build(); } /** diff --git a/sky-server/src/main/java/com/sky/task/OrderTask.java b/sky-server/src/main/java/com/sky/task/OrderTask.java new file mode 100644 index 0000000..0cdaf71 --- /dev/null +++ b/sky-server/src/main/java/com/sky/task/OrderTask.java @@ -0,0 +1,54 @@ +package com.sky.task; + +import com.sky.mapper.OrderMapper; +import com.sky.pojo.entity.Orders; +import lombok.extern.slf4j.Slf4j; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; + +import javax.annotation.Resource; +import java.time.LocalDateTime; +import java.util.List; + +@Component +@Slf4j +public class OrderTask { + @Resource + private OrderMapper orderMapper; + + /** + * 处理超时订单 + */ + @Scheduled(cron = "0 * * * * ?") + // @Scheduled(cron = "0/5 * * * * ?") + public void processTImeOrder() { + log.info("定时处理超时订单:{}", LocalDateTime.now()); + LocalDateTime time = LocalDateTime.now().plusMinutes(-15); + List list = orderMapper.getByStatusAndOrderTime(Orders.PENDING_PAYMENT, time); + if (list != null && !list.isEmpty()) { + for (Orders orders : list) { + orders.setStatus(Orders.CANCELLED); + orders.setCancelReason("订单超时,自动取消"); + orders.setCancelTime(LocalDateTime.now()); + orderMapper.update(orders); + } + } + } + + /** + * 处理一直派送中的订单 + */ + @Scheduled(cron = "0 0 1 * * ?") + // @Scheduled(cron = "0/5 * * * * ?") + public void processDeliveryOrder() { + log.info("定时处理处于派送中的订单:{}", LocalDateTime.now()); + LocalDateTime time = LocalDateTime.now().plusMinutes(-60); + List list = orderMapper.getByStatusAndOrderTime(Orders.DELIVERY_IN_PROGRESS, time); + if (list != null && !list.isEmpty()) { + for (Orders orders : list) { + orders.setStatus(Orders.COMPLETED); + orderMapper.update(orders); + } + } + } +} diff --git a/sky-server/src/main/java/com/sky/task/WebSocketTask.java b/sky-server/src/main/java/com/sky/task/WebSocketTask.java new file mode 100644 index 0000000..fba8dc3 --- /dev/null +++ b/sky-server/src/main/java/com/sky/task/WebSocketTask.java @@ -0,0 +1,20 @@ +package com.sky.task; + +import com.sky.websocket.WebSocketServer; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; + +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; + +@Component +public class WebSocketTask { + @Autowired + WebSocketServer webSocketServer; + + @Scheduled(cron = "0/2 * * * * ?") + public void sendMessageToClient() { + webSocketServer.sendToAllClient("这是来自服务端消息" + DateTimeFormatter.ofPattern("HH:mm:ss").format(LocalDateTime.now())); + } +} diff --git a/sky-server/src/main/java/com/sky/websocket/WebSocketServer.java b/sky-server/src/main/java/com/sky/websocket/WebSocketServer.java new file mode 100644 index 0000000..4816b8e --- /dev/null +++ b/sky-server/src/main/java/com/sky/websocket/WebSocketServer.java @@ -0,0 +1,47 @@ +package com.sky.websocket; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +import javax.websocket.OnMessage; +import javax.websocket.OnOpen; +import javax.websocket.Session; +import javax.websocket.server.PathParam; +import javax.websocket.server.ServerEndpoint; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; + +@Component +@ServerEndpoint("/ws/{sid}") +@Slf4j +public class WebSocketServer { + // 存放会话对象 + private static final Map sessionMap = new HashMap<>(); + + // 连接建立成功调用的方法 + @OnOpen + public void onOpen(Session session, @PathParam("sid") String sid) { + log.info("客户端:{}--->建立连接", sid); + sessionMap.put(sid, session); + } + + // 收到客户端消息后调用的方法 + @OnMessage + public void onMessage(String message, @PathParam("sid") String sid) { + log.info("收到来自客户端:{};信息为:{}", sid, message); + } + + // 群发 + public void sendToAllClient(String message) { + Collection sessions = sessionMap.values(); + for (Session session : sessions) { + try { + // 服务器向客户端发送消息 + session.getBasicRemote().sendText(message); + } catch (Exception exception) { + exception.printStackTrace(); + } + } + } +} diff --git a/sky-server/src/main/resources/application-dev.yml b/sky-server/src/main/resources/application-dev.yml index f340038..54e6164 100644 --- a/sky-server/src/main/resources/application-dev.yml +++ b/sky-server/src/main/resources/application-dev.yml @@ -15,7 +15,7 @@ sky: redis: host: 106.15.251.123 - port: 6378 + port: 6379 password: "02120212" database: 10 diff --git a/sky-server/src/main/resources/application.yml b/sky-server/src/main/resources/application.yml index 671e8a6..62cc312 100644 --- a/sky-server/src/main/resources/application.yml +++ b/sky-server/src/main/resources/application.yml @@ -37,9 +37,9 @@ logging: controller: info sky: - jwt: + redisToken: # 设置jwt签名加密时使用的秘钥 - admin-secret-key: itcast + admin-secret-key: bunny # 设置jwt过期时间 admin-ttl: 7200000 # 设置前端传递过来的令牌名称