解决冲突
This commit is contained in:
commit
c0d5b22535
@ -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 {
|
||||
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
@ -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 <T>{
|
||||
private Integer code;
|
||||
private String msg;
|
||||
private Boolean success;
|
||||
private Boolean error;
|
||||
private T data;
|
||||
|
||||
public static ParameterizedTypeReference<ThirdRpcResp<ThirdIpInfoResp>> getResponseIpType() {
|
||||
return new ParameterizedTypeReference<ThirdRpcResp<ThirdIpInfoResp>>() {};
|
||||
}
|
||||
}
|
||||
@ -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<WeatherTimeValue> aqi;
|
||||
private List<WeatherTimeInt> pm25;
|
||||
}
|
||||
@ -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<Temperature> precipitation_08h_20h;
|
||||
private List<Temperature> precipitation_20h_32h;
|
||||
private List<Temperature> precipitation;
|
||||
private List<Temperature> temperature;
|
||||
private List<Temperature> temperature_08h_20h;
|
||||
private List<Temperature> temperature_20h_32h;
|
||||
private List<Wind> wind;
|
||||
private List<Wind> wind_08h_20h;
|
||||
private List<Wind> wind_20h_32h;
|
||||
private List<Temperature> humidity;
|
||||
private List<Temperature> cloudrate;
|
||||
private List<Temperature> pressure;
|
||||
private List<Temperature> visibility;
|
||||
private List<Temperature> dswrf;
|
||||
private LifeIndex life_index;
|
||||
}
|
||||
@ -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> precipitation;
|
||||
private List<WeatherTimeInt> temperature;
|
||||
private List<WeatherTimeDouble> apparent_temperature;
|
||||
private List<Wind> wind;
|
||||
private List<WeatherTimeDouble> humidity;
|
||||
private List<WeatherTimeInt> cloudrate;
|
||||
private List<WeatherTimeString> skycon;
|
||||
private List<WeatherTimeDouble> pressure;
|
||||
private List<WeatherTimeDouble> visibility;
|
||||
private List<WeatherTimeDouble> dswrf;
|
||||
private AirQuality air_quality;
|
||||
}
|
||||
@ -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<LifeIndexDesc> ultraviolet;
|
||||
private List<LifeIndexDesc> carWashing;
|
||||
private List<LifeIndexDesc> dressing;
|
||||
private List<LifeIndexDesc> comfort;
|
||||
private List<LifeIndexDesc> coldRisk;
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
@ -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<Double> location;
|
||||
private Result result;
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
33
iot-common/iot-third/.gitignore
vendored
Normal file
33
iot-common/iot-third/.gitignore
vendored
Normal file
@ -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/
|
||||
68
iot-common/iot-third/pom.xml
Normal file
68
iot-common/iot-third/pom.xml
Normal file
@ -0,0 +1,68 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>com.qiuguo.iot</groupId>
|
||||
<artifactId>iot-common</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>iot-third</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
<name>iot-third</name>
|
||||
<description>iot-third</description>
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
</properties>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-webflux</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.qiuguo.iot</groupId>
|
||||
<artifactId>iot-base</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.qiuguo.iot</groupId>
|
||||
<artifactId>iot-data</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.projectreactor</groupId>
|
||||
<artifactId>reactor-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<finalName>${project.artifactId}</finalName>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
<version>${spring.boot.maven.plugin.version}</version>
|
||||
<configuration>
|
||||
<!--跳过对项目中main方法的查找-->
|
||||
<skip>true</skip>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>repackage</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
@ -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<ThirdRpcResp<ThirdIpInfoResp>> getIpInfo(String ip) {
|
||||
WebClient webClient = WebClient.builder().build();
|
||||
return webClient.get().uri(thirdIpInfoUrl + "?ip=" + ip).retrieve().bodyToMono(ThirdRpcResp.getResponseIpType());
|
||||
}
|
||||
}
|
||||
@ -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<WeatherResp> 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<ThirdWeatherInfoRequest> 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<WeatherResp> thirdQueryWeather(Mono<ThirdWeatherInfoRequest> 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);
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -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 {
|
||||
|
||||
}
|
||||
@ -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<ThirdRpcResp<ThirdIpInfoResp>> ipInfo = ipService.getIpInfo("60.186.105.204");
|
||||
System.out.println("contextLoads");
|
||||
|
||||
Mono<ThirdRpcResp<ThirdIpInfoResp>> 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");
|
||||
}
|
||||
}
|
||||
@ -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<WeatherResp> 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() {
|
||||
}
|
||||
}
|
||||
@ -49,7 +49,27 @@
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-actuator</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.alibaba</groupId>
|
||||
<artifactId>fastjson</artifactId>
|
||||
<version>1.2.83</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-data-redis-reactive</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.qiuguo.iot</groupId>
|
||||
<artifactId>iot-base</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
@ -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<String> allowedOriginPatterns;
|
||||
|
||||
/**
|
||||
* 允许跨域访问的方法.
|
||||
*/
|
||||
private List<String> allowedMethods;
|
||||
|
||||
/**
|
||||
* 允许跨域访问的请求头.
|
||||
*/
|
||||
private List<String> allowedHeaders;
|
||||
|
||||
/**
|
||||
* 暴露的响应头.
|
||||
*/
|
||||
private List<String> exposedHeaders;
|
||||
|
||||
/**
|
||||
* 是否允许跨域发送cookie.
|
||||
*/
|
||||
private Boolean allowCredentials = true;
|
||||
|
||||
/**
|
||||
* 跨域访问有效期.
|
||||
*/
|
||||
private Long maxAge = 1800L;
|
||||
}
|
||||
@ -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<String> excludeUrls = new ArrayList<>();
|
||||
}
|
||||
@ -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<Void> 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;
|
||||
}
|
||||
}
|
||||
@ -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<Void> 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;
|
||||
}
|
||||
|
||||
}
|
||||
@ -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<Void> 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);
|
||||
}
|
||||
|
||||
}
|
||||
@ -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<Void> 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<Void> 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<Void> 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<Void> webFluxResponseWriter(ServerHttpResponse response, String contentType, HttpStatus status,
|
||||
Object value, int code) {
|
||||
response.setStatusCode(status);
|
||||
response.getHeaders().add(HttpHeaders.CONTENT_TYPE, contentType);
|
||||
HashMap<String, Object> 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));
|
||||
}
|
||||
}
|
||||
@ -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:
|
||||
|
||||
|
||||
@ -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<JSONObject> change(@RequestBody JSONObject jsonObject, @RequestHeader("Api-Token") String token,
|
||||
@RequestHeader("Api-Type") String type) {
|
||||
public Mono<JSONObject> 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<JSONObject> userCance(@RequestBody JSONObject jsonObject, @RequestHeader("Api-Token") String token,
|
||||
@RequestHeader("Api-Type") String type) {
|
||||
public Mono<JSONObject> 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<JSONObject> editUserInfo(@RequestBody JSONObject jsonObject, @RequestHeader("Api-Token") String token,
|
||||
@RequestHeader("Api-Type") String type) {
|
||||
public Mono<JSONObject> 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<JSONObject> getUserInfo(@RequestHeader("Api-Token") String token,
|
||||
@RequestHeader("Api-Type") String type) {
|
||||
public Mono<JSONObject> 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<JSONObject> firstPassword(@RequestHeader("Api-Token") String token,
|
||||
@RequestHeader("Api-Type") String type) {
|
||||
public Mono<JSONObject> 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<String,String> getMultiValueMap(JSONObject jsonObject) {
|
||||
|
||||
@ -0,0 +1,58 @@
|
||||
package com.qiuguo.iot.user.api.filter;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.data.redis.core.ReactiveStringRedisTemplate;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
import org.springframework.web.server.WebFilter;
|
||||
import org.springframework.web.server.WebFilterChain;
|
||||
// import org.springframework.web.servlet.HandlerMapping;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
/**
|
||||
* XXX
|
||||
*
|
||||
* @author weiyachao
|
||||
* @since 2023/9/20 16:06
|
||||
*/
|
||||
@Configuration
|
||||
@Slf4j
|
||||
@Order(-1)
|
||||
public class AuthFilter implements WebFilter {
|
||||
|
||||
// @Autowired
|
||||
private ReactiveStringRedisTemplate reactiveRedisTemplate;
|
||||
|
||||
@Override
|
||||
public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
|
||||
// HandlerMethod handlerMethod = exchange.getAttribute(HandlerMapping.BEST_MATCHING_HANDLER_ATTRIBUTE);
|
||||
Object attribute = exchange.getAttribute("org.springframework.web.server.ServerWebExchange.LOG_ID");
|
||||
System.out.println("attribute = " + attribute);
|
||||
// if (handlerMethod != null && handlerMethod.getMethod().isAnnotationPresent(Auth.class)) {
|
||||
// // 如果请求方法上有 Auth 注解,执行登录验证逻辑
|
||||
// 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);
|
||||
// }
|
||||
// });
|
||||
// }
|
||||
|
||||
return chain.filter(exchange);
|
||||
}
|
||||
|
||||
}
|
||||
110
iot-modules/iot-box-user-api/src/test/java/UserTest.java
Normal file
110
iot-modules/iot-box-user-api/src/test/java/UserTest.java
Normal file
@ -0,0 +1,110 @@
|
||||
import com.alibaba.fastjson.JSONArray;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.qiuguo.iot.user.api.IotBoxUserApiApplication;
|
||||
import com.qiuguo.iot.user.api.service.TuyaDeviceConnector;
|
||||
import java.util.Arrays;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
|
||||
/**
|
||||
* XXX
|
||||
*
|
||||
* @author weiyachao
|
||||
* @since 2023/9/5 16:03
|
||||
*/
|
||||
@SpringBootTest(classes = IotBoxUserApiApplication.class)
|
||||
@Slf4j
|
||||
public class UserTest {
|
||||
|
||||
public String deviceId = "6cae26f5512eee7c12aqd9";
|
||||
|
||||
public String spaceId = "163257138";
|
||||
|
||||
@Autowired
|
||||
private TuyaDeviceConnector tuyaDeviceConnector;
|
||||
|
||||
@Test
|
||||
public void 查询所有分类() {
|
||||
JSONArray aa = tuyaDeviceConnector.categories();
|
||||
aa.forEach(System.out::println);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void 转移设备() {
|
||||
JSONObject jsonObject = new JSONObject();
|
||||
jsonObject.put("space_id", "163257138");
|
||||
Boolean aBoolean = tuyaDeviceConnector.transferDevice(deviceId, jsonObject);
|
||||
System.out.println(aBoolean);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void 获取设备支持的指令集() {
|
||||
Object functions = tuyaDeviceConnector.getFunctions(deviceId);
|
||||
System.out.println(functions);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void 修改空间() {
|
||||
JSONObject jsonObject = new JSONObject();
|
||||
jsonObject.put("name", "testname");
|
||||
Boolean jsonObject1 = tuyaDeviceConnector.updateSpace(spaceId, jsonObject);
|
||||
System.out.println(jsonObject1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void 查询空间() {
|
||||
//40001900
|
||||
JSONObject spaceInfo = tuyaDeviceConnector.getSpaceInfo("163257138");
|
||||
System.out.println(spaceInfo);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void 删除空间() {
|
||||
//40001900
|
||||
Boolean jsonObject = tuyaDeviceConnector.deleteSpace("163258893");
|
||||
System.out.println(jsonObject);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void 创建空间() {
|
||||
JSONObject jsonObject = new JSONObject();
|
||||
jsonObject.put("name", "space1_2");
|
||||
jsonObject.put("description", "space1_2的空间描述");
|
||||
Long jsonObject1 = tuyaDeviceConnector.creatSpace(jsonObject);
|
||||
System.out.println(jsonObject1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void 控制设备动作() {
|
||||
JSONObject commands = new JSONObject();
|
||||
JSONObject jsonObject = new JSONObject();
|
||||
jsonObject.put("code", "switch_led");
|
||||
jsonObject.put("value", true);
|
||||
JSONObject js2 = new JSONObject();
|
||||
js2.put("code", "work_mode");
|
||||
js2.put("value", "colour");
|
||||
JSONObject js3 = new JSONObject();
|
||||
js3.put("code", "bright_value_v2");
|
||||
js3.put("value", 10);
|
||||
commands.put("commands", Arrays.asList(jsonObject));
|
||||
|
||||
Object controlDevice = tuyaDeviceConnector.controlDevice(deviceId,commands);
|
||||
System.out.println(controlDevice);
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void 查询空间下设备列表() {
|
||||
JSONArray devicesBySpaceIds = tuyaDeviceConnector.getDevicesBySpaceIds("163257138", 20);
|
||||
System.out.println(devicesBySpaceIds);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void 查询设备信息() {
|
||||
JSONArray byid = tuyaDeviceConnector.getByid(deviceId);
|
||||
System.out.println(byid);
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user