From 4685991a2bb53e7ef93180da0d003143902b9a19 Mon Sep 17 00:00:00 2001 From: weiyachao <13526234727@126.com> Date: Thu, 21 Sep 2023 17:46:39 +0800 Subject: [PATCH 1/3] =?UTF-8?q?gitway=20=E6=B7=BB=E5=8A=A0=E4=BE=9D?= =?UTF-8?q?=E8=B5=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- iot-gateway/pom.xml | 12 ++- .../config/GlobalCorsConfiguration.java | 45 ++++++++++ .../config/properties/CorsProperties.java | 48 ++++++++++ .../config/properties/XssProperties.java | 31 +++++++ .../filter/GlobalCacheRequestFilter.java | 40 +++++++++ .../handler/GatewayExceptionHandler.java | 47 ++++++++++ .../qiuguo/iot/gateway/util/WebFluxUtils.java | 88 +++++++++++++++++++ 7 files changed, 310 insertions(+), 1 deletion(-) create mode 100644 iot-gateway/src/main/java/com/qiuguo/iot/gateway/config/GlobalCorsConfiguration.java create mode 100644 iot-gateway/src/main/java/com/qiuguo/iot/gateway/config/properties/CorsProperties.java create mode 100644 iot-gateway/src/main/java/com/qiuguo/iot/gateway/config/properties/XssProperties.java create mode 100644 iot-gateway/src/main/java/com/qiuguo/iot/gateway/filter/GlobalCacheRequestFilter.java create mode 100644 iot-gateway/src/main/java/com/qiuguo/iot/gateway/handler/GatewayExceptionHandler.java create mode 100644 iot-gateway/src/main/java/com/qiuguo/iot/gateway/util/WebFluxUtils.java diff --git a/iot-gateway/pom.xml b/iot-gateway/pom.xml index 9516122..6e514d6 100644 --- a/iot-gateway/pom.xml +++ b/iot-gateway/pom.xml @@ -49,7 +49,17 @@ org.springframework.boot spring-boot-starter-actuator - + + org.projectlombok + lombok + + + com.alibaba + fastjson + 1.2.83 + compile + + diff --git a/iot-gateway/src/main/java/com/qiuguo/iot/gateway/config/GlobalCorsConfiguration.java b/iot-gateway/src/main/java/com/qiuguo/iot/gateway/config/GlobalCorsConfiguration.java new file mode 100644 index 0000000..7810bbd --- /dev/null +++ b/iot-gateway/src/main/java/com/qiuguo/iot/gateway/config/GlobalCorsConfiguration.java @@ -0,0 +1,45 @@ +package com.qiuguo.iot.gateway.config; + +import com.qiuguo.iot.gateway.config.properties.CorsProperties; +import org.springframework.boot.autoconfigure.AutoConfiguration; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.web.cors.CorsConfiguration; +import org.springframework.web.cors.UrlBasedCorsConfigurationSource; +import org.springframework.web.filter.CorsFilter; + +/** + * 全局跨域配置. + * + */ +@AutoConfiguration +@EnableConfigurationProperties(CorsProperties.class) +public class GlobalCorsConfiguration { + + /** + * 允许跨域调用的过滤器. + * + * @param corsProperties 跨域配置 + */ + @Bean + @ConditionalOnMissingBean(CorsFilter.class) + public CorsFilter corsFilter(CorsProperties corsProperties) { + CorsConfiguration config = new CorsConfiguration(); + // 设置允许跨域访问的域名 + config.setAllowedOriginPatterns(corsProperties.getAllowedOriginPatterns()); + // 设置允许跨域访问的方法 + config.setAllowedMethods(corsProperties.getAllowedMethods()); + // 设置允许跨域访问的请求头 + config.setAllowedHeaders(corsProperties.getAllowedHeaders()); + config.setExposedHeaders(corsProperties.getExposedHeaders()); + // 允许跨越发送cookie + config.setAllowCredentials(corsProperties.getAllowCredentials()); + config.setMaxAge(corsProperties.getMaxAge()); + + // 对接口配置跨域设置 + UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); + source.registerCorsConfiguration("/**", config); + return new CorsFilter(source); + } +} diff --git a/iot-gateway/src/main/java/com/qiuguo/iot/gateway/config/properties/CorsProperties.java b/iot-gateway/src/main/java/com/qiuguo/iot/gateway/config/properties/CorsProperties.java new file mode 100644 index 0000000..a0ef19d --- /dev/null +++ b/iot-gateway/src/main/java/com/qiuguo/iot/gateway/config/properties/CorsProperties.java @@ -0,0 +1,48 @@ +package com.qiuguo.iot.gateway.config.properties; + +import java.util.List; +import lombok.Getter; +import lombok.Setter; +import org.springframework.boot.context.properties.ConfigurationProperties; + +/** + * 跨域配置. + * + * @author yangning + * @since 2022/11/7 14:45 + */ +@Getter +@Setter +@ConfigurationProperties(prefix = "application.cors") +public class CorsProperties { + + /** + * 允许跨域访问的域名. + */ + private List allowedOriginPatterns; + + /** + * 允许跨域访问的方法. + */ + private List allowedMethods; + + /** + * 允许跨域访问的请求头. + */ + private List allowedHeaders; + + /** + * 暴露的响应头. + */ + private List exposedHeaders; + + /** + * 是否允许跨域发送cookie. + */ + private Boolean allowCredentials = true; + + /** + * 跨域访问有效期. + */ + private Long maxAge = 1800L; +} diff --git a/iot-gateway/src/main/java/com/qiuguo/iot/gateway/config/properties/XssProperties.java b/iot-gateway/src/main/java/com/qiuguo/iot/gateway/config/properties/XssProperties.java new file mode 100644 index 0000000..622adf3 --- /dev/null +++ b/iot-gateway/src/main/java/com/qiuguo/iot/gateway/config/properties/XssProperties.java @@ -0,0 +1,31 @@ +package com.qiuguo.iot.gateway.config.properties; + +import java.util.ArrayList; +import java.util.List; +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.cloud.context.config.annotation.RefreshScope; +import org.springframework.stereotype.Component; + +/** + * Xss配置. + * + * @author yangning + * @since 2023/4/7 18:31 + */ +@Data +@Component +@RefreshScope +@ConfigurationProperties(prefix = "security.xss") +public class XssProperties { + + /** + * Xss开关. + */ + private Boolean enabled; + + /** + * 排除路径. + */ + private List excludeUrls = new ArrayList<>(); +} diff --git a/iot-gateway/src/main/java/com/qiuguo/iot/gateway/filter/GlobalCacheRequestFilter.java b/iot-gateway/src/main/java/com/qiuguo/iot/gateway/filter/GlobalCacheRequestFilter.java new file mode 100644 index 0000000..93132e8 --- /dev/null +++ b/iot-gateway/src/main/java/com/qiuguo/iot/gateway/filter/GlobalCacheRequestFilter.java @@ -0,0 +1,40 @@ +package com.qiuguo.iot.gateway.filter; + +import com.qiuguo.iot.gateway.util.WebFluxUtils; +import org.springframework.cloud.gateway.filter.GatewayFilterChain; +import org.springframework.cloud.gateway.filter.GlobalFilter; +import org.springframework.cloud.gateway.support.ServerWebExchangeUtils; +import org.springframework.core.Ordered; +import org.springframework.stereotype.Component; +import org.springframework.web.server.ServerWebExchange; +import reactor.core.publisher.Mono; + +/** + * 全局缓存获取body请求数据(解决流不能重复读取问题). + * + * @author yangning + * @since 2022/12/14 16:07 + */ +@Component +public class GlobalCacheRequestFilter implements GlobalFilter, Ordered { + + @Override + public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) { + // 只缓存json类型请求 + // if (!WebFluxUtils.isJsonRequest(exchange)) { + // return chain.filter(exchange); + // } + return ServerWebExchangeUtils.cacheRequestBody(exchange, (serverHttpRequest) -> { + if (serverHttpRequest == exchange.getRequest()) { + return chain.filter(exchange); + } + return chain.filter(exchange.mutate().request(serverHttpRequest).build()); + }); + } + + @Override + public int getOrder() { + return Ordered.HIGHEST_PRECEDENCE; + } + +} diff --git a/iot-gateway/src/main/java/com/qiuguo/iot/gateway/handler/GatewayExceptionHandler.java b/iot-gateway/src/main/java/com/qiuguo/iot/gateway/handler/GatewayExceptionHandler.java new file mode 100644 index 0000000..9b3840c --- /dev/null +++ b/iot-gateway/src/main/java/com/qiuguo/iot/gateway/handler/GatewayExceptionHandler.java @@ -0,0 +1,47 @@ +package com.qiuguo.iot.gateway.handler; + +import com.qiuguo.iot.gateway.util.WebFluxUtils; +import lombok.extern.slf4j.Slf4j; +import org.springframework.boot.web.reactive.error.ErrorWebExceptionHandler; +import org.springframework.cloud.gateway.support.NotFoundException; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.annotation.Order; +import org.springframework.http.server.reactive.ServerHttpResponse; +import org.springframework.web.server.ResponseStatusException; +import org.springframework.web.server.ServerWebExchange; +import reactor.core.publisher.Mono; + +/** + * 网关统一异常处理. + * + */ +@Slf4j +@Order(-1) +@Configuration +public class GatewayExceptionHandler implements ErrorWebExceptionHandler { + + @Override + public Mono handle(ServerWebExchange exchange, Throwable ex) { + final ServerHttpResponse response = exchange.getResponse(); + + if (exchange.getResponse().isCommitted()) { + return Mono.error(ex); + } + + String msg; + + if (ex instanceof NotFoundException) { + msg = "服务未找到"; + } else if (ex instanceof ResponseStatusException) { + ResponseStatusException responseStatusException = (ResponseStatusException) ex; + msg = responseStatusException.getMessage(); + } else { + msg = "内部服务器错误"; + } + + log.error("[网关异常处理]请求路径:{},异常信息:{}", exchange.getRequest().getPath(), ex.getMessage()); + + return WebFluxUtils.webFluxResponseWriter(response, msg); + } + +} diff --git a/iot-gateway/src/main/java/com/qiuguo/iot/gateway/util/WebFluxUtils.java b/iot-gateway/src/main/java/com/qiuguo/iot/gateway/util/WebFluxUtils.java new file mode 100644 index 0000000..5e5eef9 --- /dev/null +++ b/iot-gateway/src/main/java/com/qiuguo/iot/gateway/util/WebFluxUtils.java @@ -0,0 +1,88 @@ +package com.qiuguo.iot.gateway.util; + +import com.alibaba.fastjson.JSON; +import java.util.HashMap; +import org.springframework.core.io.buffer.DataBuffer; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.server.reactive.ServerHttpResponse; +import org.springframework.util.StringUtils; +import org.springframework.web.server.ServerWebExchange; +import reactor.core.publisher.Mono; + +/** + * WebFlux 工具类. + */ +public class WebFluxUtils { + + /** + * 是否是Json请求. + * + * @param exchange HTTP请求 + */ + public static boolean isJsonRequest(ServerWebExchange exchange) { + String header = exchange.getRequest().getHeaders().getFirst(HttpHeaders.CONTENT_TYPE); + return StringUtils.startsWithIgnoreCase(header, MediaType.APPLICATION_JSON_VALUE); + } + + /** + * 设置webflux模型响应. + * + * @param response ServerHttpResponse + * @param value 响应内容 + * @return Mono + */ + public static Mono webFluxResponseWriter(ServerHttpResponse response, Object value) { + return webFluxResponseWriter(response, HttpStatus.OK, value, HttpStatus.INTERNAL_SERVER_ERROR.value()); + } + + /** + * 设置webflux模型响应. + * + * @param response ServerHttpResponse + * @param code 响应状态码 + * @param value 响应内容 + * @return Mono + */ + public static Mono webFluxResponseWriter(ServerHttpResponse response, Object value, int code) { + return webFluxResponseWriter(response, HttpStatus.OK, value, code); + } + + /** + * 设置webflux模型响应. + * + * @param response ServerHttpResponse + * @param status http状态码 + * @param code 响应状态码 + * @param value 响应内容 + * @return Mono + */ + public static Mono webFluxResponseWriter(ServerHttpResponse response, HttpStatus status, Object value, + int code) { + return webFluxResponseWriter(response, MediaType.APPLICATION_JSON_VALUE, status, value, code); + } + + /** + * 设置webflux模型响应. + * + * @param response ServerHttpResponse + * @param contentType content-type + * @param status http状态码 + * @param code 响应状态码 + * @param value 响应内容 + * @return Mono + */ + public static Mono webFluxResponseWriter(ServerHttpResponse response, String contentType, HttpStatus status, + Object value, int code) { + response.setStatusCode(status); + response.getHeaders().add(HttpHeaders.CONTENT_TYPE, contentType); + HashMap map = new HashMap<>(); + map.put("message", value.toString()); + map.put("status", code); + map.put("code", "error"); + map.put("timestamp", System.currentTimeMillis()); + DataBuffer dataBuffer = response.bufferFactory().wrap(JSON.toJSONString(map).getBytes()); + return response.writeWith(Mono.just(dataBuffer)); + } +} From 3aecd37d4b13881fa0b3cec544ef29e84787c66b Mon Sep 17 00:00:00 2001 From: simon <861719797@qq.com> Date: Thu, 21 Sep 2023 18:24:02 +0800 Subject: [PATCH 2/3] =?UTF-8?q?=E5=A4=A9=E6=B0=94=E3=80=81ip=E4=B8=89?= =?UTF-8?q?=E6=96=B9=E6=9C=8D=E5=8A=A1=E5=AF=B9=E6=8E=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../third/ThirdWeatherInfoRequest.java | 44 +++++++ .../iot/data/resp/third/ThirdIpInfoResp.java | 26 +++++ .../iot/data/resp/third/ThirdRpcResp.java | 22 ++++ .../data/resp/third/weather/AirQuality.java | 11 ++ .../iot/data/resp/third/weather/Daily.java | 30 +++++ .../iot/data/resp/third/weather/Hourly.java | 22 ++++ .../data/resp/third/weather/LifeIndex.java | 20 ++++ .../resp/third/weather/LifeIndexDesc.java | 19 +++ .../resp/third/weather/Precipitation.java | 18 +++ .../iot/data/resp/third/weather/Result.java | 11 ++ .../data/resp/third/weather/Temperature.java | 22 ++++ .../iot/data/resp/third/weather/Value.java | 9 ++ .../data/resp/third/weather/Visibility.java | 14 +++ .../data/resp/third/weather/WeatherResp.java | 19 +++ .../resp/third/weather/WeatherTimeDouble.java | 14 +++ .../resp/third/weather/WeatherTimeInt.java | 18 +++ .../resp/third/weather/WeatherTimeString.java | 14 +++ .../resp/third/weather/WeatherTimeValue.java | 14 +++ .../iot/data/resp/third/weather/Wind.java | 19 +++ .../data/resp/third/weather/WindSpeed.java | 14 +++ iot-common/iot-third/.gitignore | 33 ++++++ iot-common/iot-third/pom.xml | 68 +++++++++++ .../qiuguo/iot/third/enums/WeatherEnum.java | 30 +++++ .../qiuguo/iot/third/service/IpService.java | 32 ++++++ .../iot/third/service/WeatherService.java | 108 ++++++++++++++++++ .../iot/third/IotThirdApplicationTests.java | 16 +++ .../iot/third/service/IpServiceTest.java | 46 ++++++++ .../iot/third/service/WeatherServiceTest.java | 47 ++++++++ 28 files changed, 760 insertions(+) create mode 100644 iot-common/iot-data/src/main/java/com/qiuguo/iot/data/request/third/ThirdWeatherInfoRequest.java create mode 100644 iot-common/iot-data/src/main/java/com/qiuguo/iot/data/resp/third/ThirdIpInfoResp.java create mode 100644 iot-common/iot-data/src/main/java/com/qiuguo/iot/data/resp/third/ThirdRpcResp.java create mode 100644 iot-common/iot-data/src/main/java/com/qiuguo/iot/data/resp/third/weather/AirQuality.java create mode 100644 iot-common/iot-data/src/main/java/com/qiuguo/iot/data/resp/third/weather/Daily.java create mode 100644 iot-common/iot-data/src/main/java/com/qiuguo/iot/data/resp/third/weather/Hourly.java create mode 100644 iot-common/iot-data/src/main/java/com/qiuguo/iot/data/resp/third/weather/LifeIndex.java create mode 100644 iot-common/iot-data/src/main/java/com/qiuguo/iot/data/resp/third/weather/LifeIndexDesc.java create mode 100644 iot-common/iot-data/src/main/java/com/qiuguo/iot/data/resp/third/weather/Precipitation.java create mode 100644 iot-common/iot-data/src/main/java/com/qiuguo/iot/data/resp/third/weather/Result.java create mode 100644 iot-common/iot-data/src/main/java/com/qiuguo/iot/data/resp/third/weather/Temperature.java create mode 100644 iot-common/iot-data/src/main/java/com/qiuguo/iot/data/resp/third/weather/Value.java create mode 100644 iot-common/iot-data/src/main/java/com/qiuguo/iot/data/resp/third/weather/Visibility.java create mode 100644 iot-common/iot-data/src/main/java/com/qiuguo/iot/data/resp/third/weather/WeatherResp.java create mode 100644 iot-common/iot-data/src/main/java/com/qiuguo/iot/data/resp/third/weather/WeatherTimeDouble.java create mode 100644 iot-common/iot-data/src/main/java/com/qiuguo/iot/data/resp/third/weather/WeatherTimeInt.java create mode 100644 iot-common/iot-data/src/main/java/com/qiuguo/iot/data/resp/third/weather/WeatherTimeString.java create mode 100644 iot-common/iot-data/src/main/java/com/qiuguo/iot/data/resp/third/weather/WeatherTimeValue.java create mode 100644 iot-common/iot-data/src/main/java/com/qiuguo/iot/data/resp/third/weather/Wind.java create mode 100644 iot-common/iot-data/src/main/java/com/qiuguo/iot/data/resp/third/weather/WindSpeed.java create mode 100644 iot-common/iot-third/.gitignore create mode 100644 iot-common/iot-third/pom.xml create mode 100644 iot-common/iot-third/src/main/java/com/qiuguo/iot/third/enums/WeatherEnum.java create mode 100644 iot-common/iot-third/src/main/java/com/qiuguo/iot/third/service/IpService.java create mode 100644 iot-common/iot-third/src/main/java/com/qiuguo/iot/third/service/WeatherService.java create mode 100644 iot-common/iot-third/src/test/java/com/qiuguo/iot/third/IotThirdApplicationTests.java create mode 100644 iot-common/iot-third/src/test/java/com/qiuguo/iot/third/service/IpServiceTest.java create mode 100644 iot-common/iot-third/src/test/java/com/qiuguo/iot/third/service/WeatherServiceTest.java diff --git a/iot-common/iot-data/src/main/java/com/qiuguo/iot/data/request/third/ThirdWeatherInfoRequest.java b/iot-common/iot-data/src/main/java/com/qiuguo/iot/data/request/third/ThirdWeatherInfoRequest.java new file mode 100644 index 0000000..6d42bc3 --- /dev/null +++ b/iot-common/iot-data/src/main/java/com/qiuguo/iot/data/request/third/ThirdWeatherInfoRequest.java @@ -0,0 +1,44 @@ +package com.qiuguo.iot.data.request.third; + +import lombok.Data; + +import javax.annotation.Nullable; +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; + +/** + * @author simon + * @date 2023/9/21 + * @description 天气查询参数 + **/ + +@Data +public class ThirdWeatherInfoRequest { + + /* + * 1: 按小时查询 2:按天查询 + */ + @NotNull + private Integer type; + + /* + * 查询条数 + */ + @NotNull + private Integer size; + + /* + * 客户端ip + */ + private String ip; + + /* + * 纬度 + */ + private String lat; + + /* + * 经度 + */ + private String lng; +} diff --git a/iot-common/iot-data/src/main/java/com/qiuguo/iot/data/resp/third/ThirdIpInfoResp.java b/iot-common/iot-data/src/main/java/com/qiuguo/iot/data/resp/third/ThirdIpInfoResp.java new file mode 100644 index 0000000..4af2c98 --- /dev/null +++ b/iot-common/iot-data/src/main/java/com/qiuguo/iot/data/resp/third/ThirdIpInfoResp.java @@ -0,0 +1,26 @@ +package com.qiuguo.iot.data.resp.third; + +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.Data; +import org.springframework.format.annotation.DateTimeFormat; + +import java.util.Date; + +/** + * @author simon + * @date 2023/9/21 + * @description + **/ + +@Data +public class ThirdIpInfoResp { + private Long id; + private String ip; + private Float lat; + private Float lng; + private String nation; + private String province; + private String city; + private String district; + private Integer adcode; +} diff --git a/iot-common/iot-data/src/main/java/com/qiuguo/iot/data/resp/third/ThirdRpcResp.java b/iot-common/iot-data/src/main/java/com/qiuguo/iot/data/resp/third/ThirdRpcResp.java new file mode 100644 index 0000000..3770b3a --- /dev/null +++ b/iot-common/iot-data/src/main/java/com/qiuguo/iot/data/resp/third/ThirdRpcResp.java @@ -0,0 +1,22 @@ +package com.qiuguo.iot.data.resp.third; + +import lombok.Data; +import org.springframework.core.ParameterizedTypeReference; + +/** + * @author simon + * @date 2023/9/21 + * @description + **/ +@Data +public class ThirdRpcResp { + private Integer code; + private String msg; + private Boolean success; + private Boolean error; + private T data; + + public static ParameterizedTypeReference> getResponseIpType() { + return new ParameterizedTypeReference>() {}; + } +} diff --git a/iot-common/iot-data/src/main/java/com/qiuguo/iot/data/resp/third/weather/AirQuality.java b/iot-common/iot-data/src/main/java/com/qiuguo/iot/data/resp/third/weather/AirQuality.java new file mode 100644 index 0000000..a8ed30d --- /dev/null +++ b/iot-common/iot-data/src/main/java/com/qiuguo/iot/data/resp/third/weather/AirQuality.java @@ -0,0 +1,11 @@ +package com.qiuguo.iot.data.resp.third.weather; + +import lombok.Data; + +import java.util.List; + +@Data +public class AirQuality { + private List aqi; + private List pm25; +} \ No newline at end of file diff --git a/iot-common/iot-data/src/main/java/com/qiuguo/iot/data/resp/third/weather/Daily.java b/iot-common/iot-data/src/main/java/com/qiuguo/iot/data/resp/third/weather/Daily.java new file mode 100644 index 0000000..acbeddb --- /dev/null +++ b/iot-common/iot-data/src/main/java/com/qiuguo/iot/data/resp/third/weather/Daily.java @@ -0,0 +1,30 @@ +package com.qiuguo.iot.data.resp.third.weather; + +import lombok.Data; + +import java.util.List; + +/** + * @author simon + * @date 2023/9/21 + * @description + **/ +@Data +public class Daily { + private String status; + private List precipitation_08h_20h; + private List precipitation_20h_32h; + private List precipitation; + private List temperature; + private List temperature_08h_20h; + private List temperature_20h_32h; + private List wind; + private List wind_08h_20h; + private List wind_20h_32h; + private List humidity; + private List cloudrate; + private List pressure; + private List visibility; + private List dswrf; + private LifeIndex life_index; +} diff --git a/iot-common/iot-data/src/main/java/com/qiuguo/iot/data/resp/third/weather/Hourly.java b/iot-common/iot-data/src/main/java/com/qiuguo/iot/data/resp/third/weather/Hourly.java new file mode 100644 index 0000000..df50cf5 --- /dev/null +++ b/iot-common/iot-data/src/main/java/com/qiuguo/iot/data/resp/third/weather/Hourly.java @@ -0,0 +1,22 @@ +package com.qiuguo.iot.data.resp.third.weather; + +import lombok.Data; + +import java.util.List; + +@Data +public class Hourly { + private String status; + private String description; + private List precipitation; + private List temperature; + private List apparent_temperature; + private List wind; + private List humidity; + private List cloudrate; + private List skycon; + private List pressure; + private List visibility; + private List dswrf; + private AirQuality air_quality; +} \ No newline at end of file diff --git a/iot-common/iot-data/src/main/java/com/qiuguo/iot/data/resp/third/weather/LifeIndex.java b/iot-common/iot-data/src/main/java/com/qiuguo/iot/data/resp/third/weather/LifeIndex.java new file mode 100644 index 0000000..61a79b8 --- /dev/null +++ b/iot-common/iot-data/src/main/java/com/qiuguo/iot/data/resp/third/weather/LifeIndex.java @@ -0,0 +1,20 @@ +package com.qiuguo.iot.data.resp.third.weather; + +import lombok.Data; + +import java.util.List; + +/** + * @author simon + * @date 2023/9/21 + * @description + **/ + +@Data +public class LifeIndex { + private List ultraviolet; + private List carWashing; + private List dressing; + private List comfort; + private List coldRisk; +} diff --git a/iot-common/iot-data/src/main/java/com/qiuguo/iot/data/resp/third/weather/LifeIndexDesc.java b/iot-common/iot-data/src/main/java/com/qiuguo/iot/data/resp/third/weather/LifeIndexDesc.java new file mode 100644 index 0000000..853e8d7 --- /dev/null +++ b/iot-common/iot-data/src/main/java/com/qiuguo/iot/data/resp/third/weather/LifeIndexDesc.java @@ -0,0 +1,19 @@ +package com.qiuguo.iot.data.resp.third.weather; + +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.Data; + +import java.util.Date; + +/** + * @author simon + * @date 2023/9/21 + * @description + **/ +@Data +public class LifeIndexDesc { + @JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm", timezone = "GMT+8") + private Date date; + private String index; + private String desc; +} diff --git a/iot-common/iot-data/src/main/java/com/qiuguo/iot/data/resp/third/weather/Precipitation.java b/iot-common/iot-data/src/main/java/com/qiuguo/iot/data/resp/third/weather/Precipitation.java new file mode 100644 index 0000000..0e3d5ed --- /dev/null +++ b/iot-common/iot-data/src/main/java/com/qiuguo/iot/data/resp/third/weather/Precipitation.java @@ -0,0 +1,18 @@ +package com.qiuguo.iot.data.resp.third.weather; + +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.Data; +import org.springframework.format.annotation.DateTimeFormat; + +import java.util.Date; + +@Data +public class Precipitation { + @JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm", timezone = "GMT+8") + private Date datetime; + private Integer value; + private Integer probability; + private Integer max; + private Integer min; + private Double avg; +} \ No newline at end of file diff --git a/iot-common/iot-data/src/main/java/com/qiuguo/iot/data/resp/third/weather/Result.java b/iot-common/iot-data/src/main/java/com/qiuguo/iot/data/resp/third/weather/Result.java new file mode 100644 index 0000000..d1b46ec --- /dev/null +++ b/iot-common/iot-data/src/main/java/com/qiuguo/iot/data/resp/third/weather/Result.java @@ -0,0 +1,11 @@ +package com.qiuguo.iot.data.resp.third.weather; + +import lombok.Data; + +@Data +public class Result { + private Hourly hourly; + private Daily daily; + private Integer primary; + private String forecast_keypoint; +} \ No newline at end of file diff --git a/iot-common/iot-data/src/main/java/com/qiuguo/iot/data/resp/third/weather/Temperature.java b/iot-common/iot-data/src/main/java/com/qiuguo/iot/data/resp/third/weather/Temperature.java new file mode 100644 index 0000000..9017780 --- /dev/null +++ b/iot-common/iot-data/src/main/java/com/qiuguo/iot/data/resp/third/weather/Temperature.java @@ -0,0 +1,22 @@ +package com.qiuguo.iot.data.resp.third.weather; + +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.Data; + +import java.util.Date; + +/** + * @author simon + * @date 2023/9/21 + * @description + **/ +@Data +public class Temperature { + @JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm", timezone = "GMT+8") + private Date datetime; + //天气 + private Double max; + private Double min; + private Double avg; + private Double probability; +} diff --git a/iot-common/iot-data/src/main/java/com/qiuguo/iot/data/resp/third/weather/Value.java b/iot-common/iot-data/src/main/java/com/qiuguo/iot/data/resp/third/weather/Value.java new file mode 100644 index 0000000..d3bd4bc --- /dev/null +++ b/iot-common/iot-data/src/main/java/com/qiuguo/iot/data/resp/third/weather/Value.java @@ -0,0 +1,9 @@ +package com.qiuguo.iot.data.resp.third.weather; + +import lombok.Data; + +@Data +public class Value { + private Integer chn; + private Integer usa; +} \ No newline at end of file diff --git a/iot-common/iot-data/src/main/java/com/qiuguo/iot/data/resp/third/weather/Visibility.java b/iot-common/iot-data/src/main/java/com/qiuguo/iot/data/resp/third/weather/Visibility.java new file mode 100644 index 0000000..6d5888c --- /dev/null +++ b/iot-common/iot-data/src/main/java/com/qiuguo/iot/data/resp/third/weather/Visibility.java @@ -0,0 +1,14 @@ +package com.qiuguo.iot.data.resp.third.weather; + +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.Data; +import org.springframework.format.annotation.DateTimeFormat; + +import java.util.Date; + +@Data +public class Visibility { + @JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm", timezone = "GMT+8") + private Date datetime; + private double value; +} \ No newline at end of file diff --git a/iot-common/iot-data/src/main/java/com/qiuguo/iot/data/resp/third/weather/WeatherResp.java b/iot-common/iot-data/src/main/java/com/qiuguo/iot/data/resp/third/weather/WeatherResp.java new file mode 100644 index 0000000..15a0a45 --- /dev/null +++ b/iot-common/iot-data/src/main/java/com/qiuguo/iot/data/resp/third/weather/WeatherResp.java @@ -0,0 +1,19 @@ +package com.qiuguo.iot.data.resp.third.weather; + +import lombok.Data; + +import java.util.List; + +@Data +public class WeatherResp { + private String status; + private String api_version; + private String api_status; + private String lang; + private String unit; + private Integer tzshift; + private String timezone; + private Long server_time; + private List location; + private Result result; +} \ No newline at end of file diff --git a/iot-common/iot-data/src/main/java/com/qiuguo/iot/data/resp/third/weather/WeatherTimeDouble.java b/iot-common/iot-data/src/main/java/com/qiuguo/iot/data/resp/third/weather/WeatherTimeDouble.java new file mode 100644 index 0000000..508f28e --- /dev/null +++ b/iot-common/iot-data/src/main/java/com/qiuguo/iot/data/resp/third/weather/WeatherTimeDouble.java @@ -0,0 +1,14 @@ +package com.qiuguo.iot.data.resp.third.weather; + +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.Data; +import org.springframework.format.annotation.DateTimeFormat; + +import java.util.Date; + +@Data +public class WeatherTimeDouble { + @JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm", timezone = "GMT+8") + private Date datetime; + private Double value; +} \ No newline at end of file diff --git a/iot-common/iot-data/src/main/java/com/qiuguo/iot/data/resp/third/weather/WeatherTimeInt.java b/iot-common/iot-data/src/main/java/com/qiuguo/iot/data/resp/third/weather/WeatherTimeInt.java new file mode 100644 index 0000000..8c6b0d2 --- /dev/null +++ b/iot-common/iot-data/src/main/java/com/qiuguo/iot/data/resp/third/weather/WeatherTimeInt.java @@ -0,0 +1,18 @@ +package com.qiuguo.iot.data.resp.third.weather; + +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.Data; +import org.springframework.format.annotation.DateTimeFormat; + +import java.util.Date; + +@Data +public class WeatherTimeInt { + @JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm", timezone = "GMT+8") + private Date datetime; + private Integer value; + //天气 + private Double max; + private Double min; + private Double avg; +} \ No newline at end of file diff --git a/iot-common/iot-data/src/main/java/com/qiuguo/iot/data/resp/third/weather/WeatherTimeString.java b/iot-common/iot-data/src/main/java/com/qiuguo/iot/data/resp/third/weather/WeatherTimeString.java new file mode 100644 index 0000000..fbdffbf --- /dev/null +++ b/iot-common/iot-data/src/main/java/com/qiuguo/iot/data/resp/third/weather/WeatherTimeString.java @@ -0,0 +1,14 @@ +package com.qiuguo.iot.data.resp.third.weather; + +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.Data; +import org.springframework.format.annotation.DateTimeFormat; + +import java.util.Date; + +@Data +public class WeatherTimeString { + @JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm", timezone = "GMT+8") + private Date datetime; + private String value; +} \ No newline at end of file diff --git a/iot-common/iot-data/src/main/java/com/qiuguo/iot/data/resp/third/weather/WeatherTimeValue.java b/iot-common/iot-data/src/main/java/com/qiuguo/iot/data/resp/third/weather/WeatherTimeValue.java new file mode 100644 index 0000000..23c4d16 --- /dev/null +++ b/iot-common/iot-data/src/main/java/com/qiuguo/iot/data/resp/third/weather/WeatherTimeValue.java @@ -0,0 +1,14 @@ +package com.qiuguo.iot.data.resp.third.weather; + +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.Data; +import org.springframework.format.annotation.DateTimeFormat; + +import java.util.Date; + +@Data +public class WeatherTimeValue { + @JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm", timezone = "GMT+8") + private Date datetime; + private Value value; +} \ No newline at end of file diff --git a/iot-common/iot-data/src/main/java/com/qiuguo/iot/data/resp/third/weather/Wind.java b/iot-common/iot-data/src/main/java/com/qiuguo/iot/data/resp/third/weather/Wind.java new file mode 100644 index 0000000..68563f1 --- /dev/null +++ b/iot-common/iot-data/src/main/java/com/qiuguo/iot/data/resp/third/weather/Wind.java @@ -0,0 +1,19 @@ +package com.qiuguo.iot.data.resp.third.weather; + +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.Data; +import org.springframework.format.annotation.DateTimeFormat; + +import java.util.Date; + +@Data +public class Wind { + @JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm", timezone = "GMT+8") + private Date datetime; + private Double speed; + private Double direction; + + private WindSpeed max; + private WindSpeed min; + private WindSpeed avg; +} \ No newline at end of file diff --git a/iot-common/iot-data/src/main/java/com/qiuguo/iot/data/resp/third/weather/WindSpeed.java b/iot-common/iot-data/src/main/java/com/qiuguo/iot/data/resp/third/weather/WindSpeed.java new file mode 100644 index 0000000..bbab749 --- /dev/null +++ b/iot-common/iot-data/src/main/java/com/qiuguo/iot/data/resp/third/weather/WindSpeed.java @@ -0,0 +1,14 @@ +package com.qiuguo.iot.data.resp.third.weather; + +import lombok.Data; + +/** + * @author simon + * @date 2023/9/21 + * @description + **/ +@Data +public class WindSpeed { + private Double speed; + private Double direction; +} diff --git a/iot-common/iot-third/.gitignore b/iot-common/iot-third/.gitignore new file mode 100644 index 0000000..549e00a --- /dev/null +++ b/iot-common/iot-third/.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/ diff --git a/iot-common/iot-third/pom.xml b/iot-common/iot-third/pom.xml new file mode 100644 index 0000000..85edf18 --- /dev/null +++ b/iot-common/iot-third/pom.xml @@ -0,0 +1,68 @@ + + + 4.0.0 + + com.qiuguo.iot + iot-common + 0.0.1-SNAPSHOT + + iot-third + 0.0.1-SNAPSHOT + iot-third + iot-third + + UTF-8 + + + + org.springframework.boot + spring-boot-starter-test + test + + + org.springframework.boot + spring-boot-starter-webflux + + + com.qiuguo.iot + iot-base + 0.0.1-SNAPSHOT + compile + + + com.qiuguo.iot + iot-data + 0.0.1-SNAPSHOT + compile + + + io.projectreactor + reactor-test + test + + + + + ${project.artifactId} + + + org.springframework.boot + spring-boot-maven-plugin + ${spring.boot.maven.plugin.version} + + + true + + + + + repackage + + + + + + + + diff --git a/iot-common/iot-third/src/main/java/com/qiuguo/iot/third/enums/WeatherEnum.java b/iot-common/iot-third/src/main/java/com/qiuguo/iot/third/enums/WeatherEnum.java new file mode 100644 index 0000000..6c24a36 --- /dev/null +++ b/iot-common/iot-third/src/main/java/com/qiuguo/iot/third/enums/WeatherEnum.java @@ -0,0 +1,30 @@ +package com.qiuguo.iot.third.enums; + +/** + * @author simon + * @create 2023-09-21 10:57 + */ +public enum WeatherEnum { + //退款申请状态 0:待申请, 1:申请成功, 2:退款成功 + QUERY_TYPE_1(1, "按小时查询"), + QUERY_TYPE_2(2, "按天查询"), + QUERY_MIN_SIZE(1, "1"), + QUERY_HOUR_MAX_SIZE(360, "360小时"), + QUERY_DAY_MAX_SIZE(15, "15天"), + ; + + public final String name; + public final Integer code; + + WeatherEnum(Integer code, String name) { + this.code = code; + this.name = name; + } + + public Integer getCode() { + return this.code; + } + public String getName() { + return name; + } +} \ No newline at end of file diff --git a/iot-common/iot-third/src/main/java/com/qiuguo/iot/third/service/IpService.java b/iot-common/iot-third/src/main/java/com/qiuguo/iot/third/service/IpService.java new file mode 100644 index 0000000..f3e3782 --- /dev/null +++ b/iot-common/iot-third/src/main/java/com/qiuguo/iot/third/service/IpService.java @@ -0,0 +1,32 @@ +package com.qiuguo.iot.third.service; + +import com.qiuguo.iot.data.resp.third.ThirdIpInfoResp; +import com.qiuguo.iot.data.resp.third.ThirdRpcResp; +import org.springframework.beans.factory.annotation.Value; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.web.reactive.function.client.WebClient; +import reactor.core.publisher.Mono; + +/** + * @author simon + * @date 2023/9/21 + * @description ip查询 + **/ + +@Service +@Slf4j +public class IpService { + @Value("https://qiuguo-app.qiuguojihua.com/prod-api/third/ip/info") + public String thirdIpInfoUrl; + + /** + * 查询ip信息 + * @param ip + * @return + */ + public Mono> getIpInfo(String ip) { + WebClient webClient = WebClient.builder().build(); + return webClient.get().uri(thirdIpInfoUrl + "?ip=" + ip).retrieve().bodyToMono(ThirdRpcResp.getResponseIpType()); + } +} diff --git a/iot-common/iot-third/src/main/java/com/qiuguo/iot/third/service/WeatherService.java b/iot-common/iot-third/src/main/java/com/qiuguo/iot/third/service/WeatherService.java new file mode 100644 index 0000000..8697eff --- /dev/null +++ b/iot-common/iot-third/src/main/java/com/qiuguo/iot/third/service/WeatherService.java @@ -0,0 +1,108 @@ +package com.qiuguo.iot.third.service; + +import cn.hutool.json.JSONObject; +import com.qiuguo.iot.data.request.third.ThirdWeatherInfoRequest; +import com.qiuguo.iot.data.resp.third.ThirdIpInfoResp; +import com.qiuguo.iot.data.resp.third.ThirdRpcResp; +import com.qiuguo.iot.data.resp.third.weather.WeatherResp; +import com.qiuguo.iot.third.enums.WeatherEnum; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.HttpStatus; +import org.springframework.stereotype.Service; +import org.springframework.util.ObjectUtils; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.reactive.function.client.WebClient; +import reactor.core.CoreSubscriber; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; +import reactor.util.annotation.Nullable; + +import javax.annotation.Resource; +import java.util.regex.Pattern; + +/** + * @author simon + * @date 2023/9/21 + * @description 天气服务 + **/ + +@Service +@Slf4j +@Validated +public class WeatherService { + //101.6656,39.2072/hourly?hourlysteps=1 + //101.6656,39.2072/daily?dailysteps=1 + @Value("https://api.caiyunapp.com/v2.6/ilUeAnf1vNkphxYS/") + private String queryWeatherUrl; + + @Resource + private IpService ipService; + + /** + * + * @return + */ + public Mono queryWeather(ThirdWeatherInfoRequest req) { + if (req.getType() < WeatherEnum.QUERY_TYPE_1.getCode() || req.getType() > WeatherEnum.QUERY_TYPE_2.getCode()) { + req.setType(WeatherEnum.QUERY_TYPE_1.getCode()); + } + if (req.getSize() < WeatherEnum.QUERY_MIN_SIZE.getCode()) { + req.setSize(WeatherEnum.QUERY_MIN_SIZE.getCode()); + } + //小时查询最大360小时 + if (req.getType().equals(WeatherEnum.QUERY_TYPE_1.getCode()) && req.getSize() > WeatherEnum.QUERY_HOUR_MAX_SIZE.getCode()) { + req.setSize(WeatherEnum.QUERY_HOUR_MAX_SIZE.getCode()); + } + //按天查询最多15天 + if (req.getType().equals(WeatherEnum.QUERY_TYPE_2.getCode()) && req.getSize() > WeatherEnum.QUERY_DAY_MAX_SIZE.getCode()) { + req.setSize(WeatherEnum.QUERY_DAY_MAX_SIZE.getCode()); + } + + + Mono lngLatMono = Mono.empty(); + + //如果经纬度为空,通过ip获取经纬度 + if (ObjectUtils.isEmpty(req.getLng()) || ObjectUtils.isEmpty(req.getLat())) { + if (ObjectUtils.isEmpty(req.getIp())) { + throw new RuntimeException("ip经纬度不能同时为空"); + } + boolean matches = Pattern.matches("^([1-9]|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])(\\.(\\d|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])){3}$", req.getIp()); + if (!matches) { + throw new RuntimeException("ip格式不正确"); + } + + lngLatMono = ipService.getIpInfo(req.getIp()).flatMap(resp -> { + if (!resp.getCode().equals(HttpStatus.OK.value())) { + return Mono.error(new RuntimeException("ip服务查询失败:" + resp.getMsg() + " " + resp.getCode())); + } + if (ObjectUtils.isEmpty(resp.getData().getLng()) || ObjectUtils.isEmpty(resp.getData().getLat())) { + return Mono.error(new RuntimeException("当前ip查询失败:" + req.getIp())); + } + req.setLng(resp.getData().getLng().toString()); + req.setLat(resp.getData().getLat().toString()); + return Mono.just(req); + }); + + } else { + lngLatMono = Mono.just(req); + } + + return thirdQueryWeather(lngLatMono, req.getType(), req.getSize()); + } + + public Mono thirdQueryWeather(Mono lngLatMono, Integer queryType, Integer querySize) { + WebClient webClient = WebClient.builder().build(); + + return lngLatMono.flatMap(r -> { + if (queryType.equals(WeatherEnum.QUERY_TYPE_1.getCode())) { + queryWeatherUrl = queryWeatherUrl + r.getLng().toString() + "," + r.getLat().toString() + "/hourly?hourlysteps=" + querySize; + } else { + queryWeatherUrl = queryWeatherUrl + r.getLng().toString() + "," + r.getLat().toString() + "/daily?dailysteps=" + querySize; + } + + + return webClient.get().uri(queryWeatherUrl).retrieve().bodyToMono(WeatherResp.class); + }); + } +} diff --git a/iot-common/iot-third/src/test/java/com/qiuguo/iot/third/IotThirdApplicationTests.java b/iot-common/iot-third/src/test/java/com/qiuguo/iot/third/IotThirdApplicationTests.java new file mode 100644 index 0000000..93a6c67 --- /dev/null +++ b/iot-common/iot-third/src/test/java/com/qiuguo/iot/third/IotThirdApplicationTests.java @@ -0,0 +1,16 @@ +package com.qiuguo.iot.third; + +import com.qiuguo.iot.data.resp.third.ThirdIpInfoResp; +import com.qiuguo.iot.data.resp.third.ThirdRpcResp; +import com.qiuguo.iot.third.service.IpService; +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; +import reactor.core.publisher.Mono; + +import javax.annotation.Resource; +import java.util.concurrent.atomic.AtomicReference; + +@SpringBootTest() +class IotThirdApplicationTests { + +} diff --git a/iot-common/iot-third/src/test/java/com/qiuguo/iot/third/service/IpServiceTest.java b/iot-common/iot-third/src/test/java/com/qiuguo/iot/third/service/IpServiceTest.java new file mode 100644 index 0000000..ee3284f --- /dev/null +++ b/iot-common/iot-third/src/test/java/com/qiuguo/iot/third/service/IpServiceTest.java @@ -0,0 +1,46 @@ +package com.qiuguo.iot.third.service; + +import com.qiuguo.iot.data.resp.third.ThirdIpInfoResp; +import com.qiuguo.iot.data.resp.third.ThirdRpcResp; +import org.junit.jupiter.api.Test; +import org.springframework.beans.BeanUtils; +import org.springframework.boot.test.context.SpringBootTest; +import reactor.core.publisher.Mono; +import reactor.test.StepVerifier; + +import javax.annotation.Resource; + +import static org.junit.jupiter.api.Assertions.*; + +/** + * @author simon + * @date 2023/9/21 + * @description + **/ + +@SpringBootTest(classes = IpService.class) +class IpServiceTest { + @Resource + private IpService ipService; + + @Test + void contextLoads() throws InterruptedException { + Mono> ipInfo = ipService.getIpInfo("60.186.105.204"); + System.out.println("contextLoads"); + + Mono> thirdRpcRespMono = ipInfo.flatMap(resp -> { + System.out.println(resp.getData().getCity()); + return Mono.just(resp); + }); + + thirdRpcRespMono.subscribe(System.out::println); + Thread.sleep(20000); + + // StepVerifier.create(ipInfo) + // //.expectNext("Hello, World!") // 验证预期的输出值 + // //.expectNext() + // .expectComplete() // 验证是否正常完成 + // .verify(); // 执行验证 + System.out.println("contextLoads"); + } +} \ No newline at end of file diff --git a/iot-common/iot-third/src/test/java/com/qiuguo/iot/third/service/WeatherServiceTest.java b/iot-common/iot-third/src/test/java/com/qiuguo/iot/third/service/WeatherServiceTest.java new file mode 100644 index 0000000..ef2ab9d --- /dev/null +++ b/iot-common/iot-third/src/test/java/com/qiuguo/iot/third/service/WeatherServiceTest.java @@ -0,0 +1,47 @@ +package com.qiuguo.iot.third.service; + +import cn.hutool.json.JSONObject; +import com.qiuguo.iot.data.request.third.ThirdWeatherInfoRequest; +import com.qiuguo.iot.data.resp.third.weather.WeatherResp; +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; +import reactor.core.publisher.Mono; + +import javax.annotation.Resource; + + +/** + * @author simon + * @date 2023/9/21 + * @description + **/ + +@SpringBootTest(classes = {WeatherService.class, IpService.class}) +class WeatherServiceTest { + @Resource + private WeatherService weatherService; + + @Test + void queryWeather() throws InterruptedException { + ThirdWeatherInfoRequest thirdWeatherInfoRequest = new ThirdWeatherInfoRequest(); + thirdWeatherInfoRequest.setType(2); + thirdWeatherInfoRequest.setSize(1); + thirdWeatherInfoRequest.setIp("60.186.105.204"); + + System.out.println(thirdWeatherInfoRequest.getIp()); + Mono jsonObjectMono = weatherService.queryWeather(thirdWeatherInfoRequest); + + jsonObjectMono.flatMap(r -> { + System.out.println("flatMap"); + System.out.println(r.getApi_version()); + return Mono.just(r); + }).subscribe(System.out::println); + + Thread.sleep(20000); + System.out.println(thirdWeatherInfoRequest.getIp()); + } + + @Test + void thirdQueryWeather() { + } +} \ No newline at end of file From 962ea083f0bf2edb86a4b59afe0f71e5f4dae8f9 Mon Sep 17 00:00:00 2001 From: weiyachao <13526234727@126.com> Date: Thu, 21 Sep 2023 18:24:27 +0800 Subject: [PATCH 3/3] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E7=99=BB=E5=BD=95?= =?UTF-8?q?=E6=8B=A6=E6=88=AA=E5=99=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/qiuguo/iot/base/annotation/Auth.java | 18 ----- iot-gateway/pom.xml | 10 +++ .../config/GlobalCorsConfiguration.java | 8 +-- .../config/properties/CorsProperties.java | 2 +- .../qiuguo/iot/gateway/filter/AuthFilter.java | 69 +++++++++++++++++++ iot-gateway/src/main/resources/bootstrap.yml | 31 +++++++++ .../api/controller/user/UserController.java | 52 +++++++++----- .../iot/user/api/filter/AuthFilter.java | 12 ---- .../src/main/resources/bootstrap.yml | 4 +- .../src/test/java/UserTest.java | 4 +- 10 files changed, 155 insertions(+), 55 deletions(-) delete mode 100644 iot-common/iot-base/src/main/java/com/qiuguo/iot/base/annotation/Auth.java create mode 100644 iot-gateway/src/main/java/com/qiuguo/iot/gateway/filter/AuthFilter.java diff --git a/iot-common/iot-base/src/main/java/com/qiuguo/iot/base/annotation/Auth.java b/iot-common/iot-base/src/main/java/com/qiuguo/iot/base/annotation/Auth.java deleted file mode 100644 index f7dbd36..0000000 --- a/iot-common/iot-base/src/main/java/com/qiuguo/iot/base/annotation/Auth.java +++ /dev/null @@ -1,18 +0,0 @@ -package com.qiuguo.iot.base.annotation; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * 强制登陆 - * - * @author weiyachao - * @since 2023/9/20 15:55 - */ -@Target(ElementType.METHOD) -@Retention(RetentionPolicy.RUNTIME) -public @interface Auth { - -} diff --git a/iot-gateway/pom.xml b/iot-gateway/pom.xml index 6e514d6..04ecd3d 100644 --- a/iot-gateway/pom.xml +++ b/iot-gateway/pom.xml @@ -59,6 +59,16 @@ 1.2.83 compile + + org.springframework.boot + spring-boot-starter-data-redis-reactive + + + com.qiuguo.iot + iot-base + 0.0.1-SNAPSHOT + compile + diff --git a/iot-gateway/src/main/java/com/qiuguo/iot/gateway/config/GlobalCorsConfiguration.java b/iot-gateway/src/main/java/com/qiuguo/iot/gateway/config/GlobalCorsConfiguration.java index 7810bbd..dfb0065 100644 --- a/iot-gateway/src/main/java/com/qiuguo/iot/gateway/config/GlobalCorsConfiguration.java +++ b/iot-gateway/src/main/java/com/qiuguo/iot/gateway/config/GlobalCorsConfiguration.java @@ -13,8 +13,8 @@ import org.springframework.web.filter.CorsFilter; * 全局跨域配置. * */ -@AutoConfiguration -@EnableConfigurationProperties(CorsProperties.class) +// @AutoConfiguration +// @EnableConfigurationProperties(CorsProperties.class) public class GlobalCorsConfiguration { /** @@ -22,8 +22,8 @@ public class GlobalCorsConfiguration { * * @param corsProperties 跨域配置 */ - @Bean - @ConditionalOnMissingBean(CorsFilter.class) + // @Bean + // @ConditionalOnMissingBean(CorsFilter.class) public CorsFilter corsFilter(CorsProperties corsProperties) { CorsConfiguration config = new CorsConfiguration(); // 设置允许跨域访问的域名 diff --git a/iot-gateway/src/main/java/com/qiuguo/iot/gateway/config/properties/CorsProperties.java b/iot-gateway/src/main/java/com/qiuguo/iot/gateway/config/properties/CorsProperties.java index a0ef19d..e9368e5 100644 --- a/iot-gateway/src/main/java/com/qiuguo/iot/gateway/config/properties/CorsProperties.java +++ b/iot-gateway/src/main/java/com/qiuguo/iot/gateway/config/properties/CorsProperties.java @@ -13,7 +13,7 @@ import org.springframework.boot.context.properties.ConfigurationProperties; */ @Getter @Setter -@ConfigurationProperties(prefix = "application.cors") +// @ConfigurationProperties(prefix = "application.cors") public class CorsProperties { /** diff --git a/iot-gateway/src/main/java/com/qiuguo/iot/gateway/filter/AuthFilter.java b/iot-gateway/src/main/java/com/qiuguo/iot/gateway/filter/AuthFilter.java new file mode 100644 index 0000000..3d9791d --- /dev/null +++ b/iot-gateway/src/main/java/com/qiuguo/iot/gateway/filter/AuthFilter.java @@ -0,0 +1,69 @@ +package com.qiuguo.iot.gateway.filter; + +import com.qiuguo.iot.base.constans.RedisConstans; +import com.qiuguo.iot.base.constans.UserAuthContains; +import com.qiuguo.iot.gateway.config.properties.XssProperties; +import java.time.Duration; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.cloud.gateway.filter.GatewayFilterChain; +import org.springframework.cloud.gateway.filter.GlobalFilter; +import org.springframework.core.Ordered; +import org.springframework.data.redis.core.ReactiveStringRedisTemplate; +import org.springframework.http.server.RequestPath; +import org.springframework.http.server.reactive.ServerHttpRequest; +import org.springframework.stereotype.Component; +import org.springframework.util.ObjectUtils; +import org.springframework.web.server.ServerWebExchange; +import reactor.core.publisher.Mono; + +/** + * XXX + * + * @author weiyachao + * @since 2023/9/21 17:56 + */ +@Component +@Slf4j +public class AuthFilter implements GlobalFilter, Ordered { + + @Autowired + private ReactiveStringRedisTemplate reactiveRedisTemplate; + + @Autowired + private XssProperties xssProperties; + + @Override + public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) { + ServerHttpRequest request = exchange.getRequest(); + String url = request.getURI().toString(); + if (xssProperties.getExcludeUrls().contains(url)) { + return chain.filter(exchange); + } + + String api_token = exchange.getRequest().getHeaders().getFirst(UserAuthContains.API_TOKEN); + String api_type = exchange.getRequest().getHeaders().getFirst(UserAuthContains.API_TYPE); + if (ObjectUtils.isEmpty(api_token) || ObjectUtils.isEmpty(api_type)) { + return Mono.error(new RuntimeException("未登录")); + } + String key = RedisConstans.IOT_TOKEN.concat(api_token); + return reactiveRedisTemplate.getExpire(key).map(Duration::getSeconds).flatMap(ttl -> { + if (ttl == -1) { + // 用户没登陆 + return Mono.error(new RuntimeException("未登录")); + } else if (ttl <= 3600) { + // token 将要失效 + return reactiveRedisTemplate.expire(key, Duration.ofDays(7)).then(chain.filter(exchange)); + } else { + // 正常登录 + return chain.filter(exchange); + } + }); + } + + + @Override + public int getOrder() { + return -1; + } +} diff --git a/iot-gateway/src/main/resources/bootstrap.yml b/iot-gateway/src/main/resources/bootstrap.yml index 8efac3c..b854733 100644 --- a/iot-gateway/src/main/resources/bootstrap.yml +++ b/iot-gateway/src/main/resources/bootstrap.yml @@ -19,4 +19,35 @@ spring: # 共享配置 shared-configs: - application-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension} + gateway: + discovery: + locator: + # 开启服务发现 + enabled: true + # 忽略注册中心服务的大小写 + lower-case-service-id: true + globalcors: + corsConfigurations: + '[/**]': + # 允许携带认证信息 + allow-credentials: true + # 允许跨域的源(网站域名/ip),设置*为全部 + allowedOriginPatterns: "*" + # 允许跨域的method, 默认为GET和OPTIONS,设置*为全部 + allowedMethods: "*" + # 允许跨域请求里的head字段,设置*为全部 + allowedHeaders: "*" + routes: + +# 安全配置 +security: + # 防止XSS攻击 + xss: + enabled: true + # 排除的路径 + exclude-urls: + - /ehs-audit/web/audit-content +application: + cors: + allowed-crigin-patterns: diff --git a/iot-modules/iot-box-user-api/src/main/java/com/qiuguo/iot/user/api/controller/user/UserController.java b/iot-modules/iot-box-user-api/src/main/java/com/qiuguo/iot/user/api/controller/user/UserController.java index 1d31b5e..a99c9b0 100644 --- a/iot-modules/iot-box-user-api/src/main/java/com/qiuguo/iot/user/api/controller/user/UserController.java +++ b/iot-modules/iot-box-user-api/src/main/java/com/qiuguo/iot/user/api/controller/user/UserController.java @@ -1,9 +1,16 @@ package com.qiuguo.iot.user.api.controller.user; import com.alibaba.fastjson.JSONObject; +import com.qiuguo.iot.base.constans.RedisConstans; +import com.qiuguo.iot.base.constans.UserAuthContains; +import java.time.Duration; import java.util.Objects; +import javax.annotation.Resource; import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; +import org.springframework.data.redis.core.ReactiveRedisTemplate; +import org.springframework.data.redis.core.ReactiveStringRedisTemplate; import org.springframework.http.HttpHeaders; import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; @@ -31,7 +38,7 @@ import static org.springframework.http.MediaType.APPLICATION_FORM_URLENCODED_VAL public class UserController { private final WebClient webClient = WebClient.builder() .defaultHeader(HttpHeaders.CONTENT_TYPE,APPLICATION_FORM_URLENCODED_VALUE) - .defaultHeader("Api-Type","web") + .defaultHeader("Api-Type","iot") .build(); @Value("${userUrl.baseUrl}") @@ -64,12 +71,15 @@ public class UserController { @Value("${userUrl.editUserInfoUrl}") private String editUserInfoUrl; + @Resource + private ReactiveStringRedisTemplate reactiveStringRedisTemplate; + /** * 修改登录密码-auth */ @PostMapping("/change") - public Mono change(@RequestBody JSONObject jsonObject, @RequestHeader("Api-Token") String token, - @RequestHeader("Api-Type") String type) { + public Mono change(@RequestBody JSONObject jsonObject, @RequestHeader(UserAuthContains.API_TOKEN) String token, + @RequestHeader(UserAuthContains.API_TYPE) String type) { WebClient authWebClient = getAuthWebClient(token, type); return authWebClient.post().uri(baseUrl + changeUrl).bodyValue(getMultiValueMap(jsonObject)).retrieve() .bodyToMono(JSONObject.class).doOnNext(res -> { @@ -83,8 +93,8 @@ public class UserController { * 账号注销-auth */ @PostMapping("/userCance") - public Mono userCance(@RequestBody JSONObject jsonObject, @RequestHeader("Api-Token") String token, - @RequestHeader("Api-Type") String type) { + public Mono userCance(@RequestBody JSONObject jsonObject, @RequestHeader(UserAuthContains.API_TOKEN) String token, + @RequestHeader(UserAuthContains.API_TYPE) String type) { return getAuthWebClient(token, type).post().uri(baseUrl + userCancelUrl) .bodyValue(getMultiValueMap(jsonObject)) .retrieve() @@ -99,8 +109,8 @@ public class UserController { * 修改用户信息-auth */ @PostMapping("/edit/userInfo") - public Mono editUserInfo(@RequestBody JSONObject jsonObject, @RequestHeader("Api-Token") String token, - @RequestHeader("Api-Type") String type) { + public Mono editUserInfo(@RequestBody JSONObject jsonObject, @RequestHeader(UserAuthContains.API_TOKEN) String token, + @RequestHeader(UserAuthContains.API_TYPE) String type) { return webClient.mutate() .defaultHeader("Api-Token", token) .defaultHeader("Api-Type", type).build().post().uri(editUserInfoUrl) @@ -117,8 +127,8 @@ public class UserController { * 个人信息管理-auth */ @GetMapping("/userInfo") - public Mono getUserInfo(@RequestHeader("Api-Token") String token, - @RequestHeader("Api-Type") String type) { + public Mono getUserInfo(@RequestHeader(UserAuthContains.API_TOKEN) String token, + @RequestHeader(UserAuthContains.API_TYPE) String type) { return getAuthWebClient(token, type).get().uri(userInfoUrl).retrieve() .bodyToMono(JSONObject.class).doOnNext(res -> { if (!Objects.equals(res.getInteger("code"), 200)) { @@ -131,8 +141,8 @@ public class UserController { * 是否设置登录密码-auth */ @GetMapping("/first/password") - public Mono firstPassword(@RequestHeader("Api-Token") String token, - @RequestHeader("Api-Type") String type) { + public Mono firstPassword(@RequestHeader(UserAuthContains.API_TOKEN) String token, + @RequestHeader(UserAuthContains.API_TYPE) String type) { return getAuthWebClient(token, type).get().uri(firstPasswordUrl).retrieve() .bodyToMono(JSONObject.class).doOnNext(res -> { if (!Objects.equals(res.getInteger("code"), 200)) { @@ -166,7 +176,9 @@ public class UserController { .retrieve().bodyToMono(JSONObject.class).flatMap(res -> { if (Objects.equals(res.getInteger("code"), 1) && !res.getString("info") .contains("该手机号还没有注册哦")) { - return Mono.just(res); + String token = res.getJSONObject("data").getJSONObject("token").getString("token"); + return reactiveStringRedisTemplate.opsForValue() + .set(RedisConstans.IOT_TOKEN.concat(token), token, Duration.ofDays(7)).then(Mono.just(res)); } else if(!res.getString("info").contains("该手机号还没有注册哦")){ return Mono.error(new RuntimeException(res.getString("info"))); }else { @@ -186,10 +198,14 @@ public class UserController { object.add("phone", jsonObject.getString("phone")); object.add("verify", jsonObject.getString("verify")); return webClient.post().uri(baseUrl + smsUrl).bodyValue(object).retrieve() - .bodyToMono(JSONObject.class).doOnNext(twoRes -> { + .bodyToMono(JSONObject.class).flatMap(twoRes -> { if (!Objects.equals(twoRes.getInteger("code"), 1)) { - throw new RuntimeException(twoRes.getString("info")); + return Mono.error(new RuntimeException(twoRes.getString("info"))); } + String token = res.getJSONObject("data").getJSONObject("token").getString("token"); + return reactiveStringRedisTemplate.opsForValue() + .set(RedisConstans.IOT_TOKEN.concat(token), token, Duration.ofDays(7)).then(Mono.just(res)); + }); } }); @@ -205,11 +221,15 @@ public class UserController { log.info("UserController[]loginByPwd[]jsonObject:{}", jsonObject); return webClient.post().uri(baseUrl + pwdUrl).bodyValue(getMultiValueMap(jsonObject)).retrieve() .bodyToMono(JSONObject.class) - .doOnNext(res -> { + .flatMap(res -> { if (!Objects.equals(res.getInteger("code"), 1)) { - throw new RuntimeException(res.getString("info")); + return Mono.error(new RuntimeException(res.getString("info"))); } + String token = res.getJSONObject("data").getJSONObject("token").getString("token"); + return reactiveStringRedisTemplate.opsForValue() + .set(RedisConstans.IOT_TOKEN.concat(token), token, Duration.ofDays(7)).then(Mono.just(res)); }); + } private MultiValueMap getMultiValueMap(JSONObject jsonObject) { diff --git a/iot-modules/iot-box-user-api/src/main/java/com/qiuguo/iot/user/api/filter/AuthFilter.java b/iot-modules/iot-box-user-api/src/main/java/com/qiuguo/iot/user/api/filter/AuthFilter.java index 82e29df..ae8d0bd 100644 --- a/iot-modules/iot-box-user-api/src/main/java/com/qiuguo/iot/user/api/filter/AuthFilter.java +++ b/iot-modules/iot-box-user-api/src/main/java/com/qiuguo/iot/user/api/filter/AuthFilter.java @@ -1,21 +1,9 @@ package com.qiuguo.iot.user.api.filter; -import com.qiuguo.iot.base.annotation.Auth; -import com.qiuguo.iot.base.constans.RedisConstans; -import com.qiuguo.iot.base.constans.UserAuthContains; -import java.lang.annotation.ElementType; -import java.time.Duration; -import java.util.Map; -import javax.annotation.Resource; import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Configuration; -import org.springframework.core.Ordered; import org.springframework.core.annotation.Order; -import org.springframework.data.redis.core.ReactiveRedisTemplate; import org.springframework.data.redis.core.ReactiveStringRedisTemplate; -import org.springframework.util.ObjectUtils; -import org.springframework.web.method.HandlerMethod; import org.springframework.web.server.ServerWebExchange; import org.springframework.web.server.WebFilter; import org.springframework.web.server.WebFilterChain; diff --git a/iot-modules/iot-box-user-api/src/main/resources/bootstrap.yml b/iot-modules/iot-box-user-api/src/main/resources/bootstrap.yml index 5b7a48f..f5cc792 100644 --- a/iot-modules/iot-box-user-api/src/main/resources/bootstrap.yml +++ b/iot-modules/iot-box-user-api/src/main/resources/bootstrap.yml @@ -10,10 +10,10 @@ spring: nacos: discovery: # 服务注册地址 - server-addr: 172.24.218.235:8848/ + server-addr: 192.168.8.146:32470 config: # 配置中心地址 - server-addr: 172.24.218.235:8848/ + server-addr: 192.168.8.146:32470 # 配置文件格式 file-extension: yml # 共享配置 diff --git a/iot-modules/iot-box-user-api/src/test/java/UserTest.java b/iot-modules/iot-box-user-api/src/test/java/UserTest.java index 59628cf..54ce71d 100644 --- a/iot-modules/iot-box-user-api/src/test/java/UserTest.java +++ b/iot-modules/iot-box-user-api/src/test/java/UserTest.java @@ -18,7 +18,7 @@ import org.springframework.boot.test.context.SpringBootTest; @Slf4j public class UserTest { - public String deviceId = "6c4a153095be2b7f8baofp"; + public String deviceId = "6cae26f5512eee7c12aqd9"; public String spaceId = "163257138"; @@ -88,7 +88,7 @@ public class UserTest { JSONObject js3 = new JSONObject(); js3.put("code", "bright_value_v2"); js3.put("value", 10); - commands.put("commands", Arrays.asList( js3)); + commands.put("commands", Arrays.asList(jsonObject)); Object controlDevice = tuyaDeviceConnector.controlDevice(deviceId,commands); System.out.println(controlDevice);