官网:https://www.visualcrossing.com/
//获取天气
@PostMapping
@ApiOperation(value = "Visual Crossing 天气查询接口")
public Result<WeatherResult> getTodayWeatherByVC(@RequestBody CurrentWeatherReq currentWeatherReq) {
return Result.ok(weatherService.getTodayWeatherByVC(currentWeatherReq));
}
@Data
//入参
@ApiModel(value = "CurrentWeatherReq", description = "查询实时天气")
public class CurrentWeatherReq {
@ApiModelProperty(value = "经度")
private BigDecimal longitude;
@ApiModelProperty(value = "纬度")
private BigDecimal latitude;
}
//出参
@Data
@ToString
public class WeatherResult{
@ApiModelProperty(value = "天气情况")
private String text;
@ApiModelProperty(value = "温度")
private BigDecimal temperature;
@ApiModelProperty(value = "温度单位")
private String tempUnit = "℃";
@ApiModelProperty(value = "天气图标")
private String icon;
}
package com.autel.cloud.hems.service.impl;
import com.alibaba.fastjson.JSON;
import com.autel.cloud.hems.api.dto.CurrentWeatherReq;
import com.autel.cloud.hems.api.vo.visualcrossing.CurrentConditions;
import com.autel.cloud.hems.api.vo.visualcrossing.VCWeatherSearchResult;
import com.autel.cloud.hems.api.vo.visualcrossing.WeatherResult;
import com.autel.cloud.hems.constant.Constants;
import com.autel.cloud.hems.constant.ErrorCode;
import com.autel.cloud.hems.constant.VCUnitEnum;
import com.autel.cloud.hems.exception.CustomServiceException;
import com.autel.cloud.hems.service.WeatherService;
import com.autel.cloud.hems.utils.LoginUserUtil;
import com.autel.cloud.hems.utils.MapUtils;
import com.autel.cloud.hems.utils.MathematicsUtil;
import com.autel.cloud.hems.utils.RedisUtil;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.CharArrayBuffer;
import org.apache.http.util.EntityUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.stereotype.Service;
import java.io.IOException;
import java.io.InputStreamReader;
import java.math.BigDecimal;
import java.nio.charset.StandardCharsets;
import java.util.Map;
import java.util.Objects;
/**
* @Description 天气查询
* @Author X22028
* @DateTime 2023/1/8 14:40
**/
@Slf4j
@Service
@RefreshScope
public class WeatherServiceImpl implements WeatherService {
@Value("${weather.visualcrossing.weatherUrl}")
private String vcWeatherUrl;
@Value("${weather.visualcrossing.key}")
private String vcKey;
@Value("${weather.visualcrossing.cacheTime:600}")
private Long vcCacheTime;
@Value("${weather.visualcrossing.fahrenheitCountry}")
private String fahrenheitCountry;
@Autowired
private RedisUtil redisUtil;
@Override
public WeatherResult getTodayWeatherByVC(CurrentWeatherReq currentWeatherReq) {
WeatherResult weatherResult = new WeatherResult();
VCWeatherSearchResult vcWeatherSearchResult;
//BigDecimal 转 String
String longitude = getFormatAddressStr(currentWeatherReq.getLongitude());
String latitude = getFormatAddressStr(currentWeatherReq.getLatitude());
//判断温度返回单位(摄氏/华氏)
boolean changeUnit = needChangeUnit();
String redisKey = String.format("weather:%s:%s", longitude, latitude);
//redis中有直接返回
Map<Object, Object> redisMap = redisUtil.hmget(redisKey);
if (CollectionUtils.isNotEmpty(redisMap)) {
log.info("redis get [{},{}]", longitude, latitude);
weatherResult = JSON.parseObject(JSON.toJSONString(redisMap), WeatherResult.class);
checkAndChangeUnit(weatherResult, changeUnit);
return weatherResult;
}
//查询
vcWeatherSearchResult = getVCWeatherSearchResult(longitude, latitude);
if (Objects.isNull(vcWeatherSearchResult)) {
return weatherResult;
}
//转换对象
convertWeatherResult(vcWeatherSearchResult, weatherResult);
//存入redis 设置过期时间
redisUtil.hmset(redisKey, MapUtils.objectToMap(weatherResult), vcCacheTime);
log.info("redis weather data set [{},{}] ", longitude, latitude);
//转换温度返回单位
checkAndChangeUnit(weatherResult, changeUnit);
return weatherResult;
}
private VCWeatherSearchResult getVCWeatherSearchResult(String longitude, String latitude) {
String weatherUrl = vcWeatherUrl
.replace("{apiKey}", vcKey)
.replace("{lon}", longitude)
.replace("{lat}", latitude);
log.info("visual crossing weather url is ---{}", weatherUrl);
HttpGet weatherGet = new HttpGet(weatherUrl);
CloseableHttpResponse response;
HttpEntity entity;
String result;
try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
response = httpClient.execute(weatherGet);
entity = response.getEntity();
result = entityToString(entity);
log.info("weather api response is --- {}", result);
return JSON.parseObject(result, VCWeatherSearchResult.class);
} catch (IOException e) {
log.error("weather url {} get data error!", weatherUrl);
throw new CustomServiceException(ErrorCode.FAIL);
}
}
private void convertWeatherResult(VCWeatherSearchResult vcWeatherSearchResult, WeatherResult weatherResult) {
CurrentConditions conditions = vcWeatherSearchResult.getCurrentConditions();
if (Objects.nonNull(conditions)) {
weatherResult.setIcon(conditions.getIcon());
weatherResult.setTemperature(new BigDecimal(conditions.getTemp()));
weatherResult.setText(conditions.getConditions());
}
}
private boolean needChangeUnit() {
String userCountryCode = LoginUserUtil.getUserCountryCode();
log.info("userCountryCode------>{}", userCountryCode);
if (StringUtils.isNotBlank(userCountryCode)) {
String[] countryCode = fahrenheitCountry.split(Constants.COMMA);
for (String s : countryCode) {
if (userCountryCode.equals(s)) {
return true;
}
}
}
return false;
}
private String getFormatAddressStr(BigDecimal value) {
return MathematicsUtil.getBigDecimalRoundDownValue(value, Constants.DEFAULT_SCALE).toString();
}
private void checkAndChangeUnit(WeatherResult weatherResult, boolean changeUnit) {
if (changeUnit) {
changeTemperature(weatherResult);
weatherResult.setTempUnit(VCUnitEnum.US.getUnit());
}
}
private void changeTemperature(WeatherResult weatherResult) {
BigDecimal temperature = weatherResult.getTemperature();
temperature = MathematicsUtil.getBigDecimalValue(temperature.multiply(new BigDecimal("1.8")).add(new BigDecimal("32")), 2);
weatherResult.setTemperature(temperature);
}
private String entityToString(HttpEntity entity) throws IOException {
String result = null;
if (entity != null) {
long lenth = entity.getContentLength();
if (lenth != -1 && lenth < 2048) {
result = EntityUtils.toString(entity, StandardCharsets.UTF_8);
} else {
InputStreamReader reader1 = new InputStreamReader(entity.getContent(), StandardCharsets.UTF_8);
CharArrayBuffer buffer = new CharArrayBuffer(2048);
char[] tmp = new char[1024];
int l;
while ((l = reader1.read(tmp)) != -1) {
buffer.append(tmp, 0, l);
}
result = buffer.toString();
}
}
return result;
}
}
//请求官网返回对象
package com.autel.cloud.hems.api.vo.visualcrossing;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.util.List;
@Data
public class VCWeatherSearchResult {
@ApiModelProperty(value = "所在城市名")
private String address;
@ApiModelProperty(value = "所在地区名")
private String resolvedAddress;
@ApiModelProperty(value = "当前温度")
private CurrentConditions currentConditions;
@ApiModelProperty(value = "天气文本")
private List<Day> days;
}
package com.autel.cloud.hems.api.vo.visualcrossing;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
* @Description TODO
* @Author X22028
* @DateTime 2023/1/14 16:20
**/
@Data
public class CurrentConditions {
@ApiModelProperty(value = "时间")
String datetime;
@ApiModelProperty(value = "时间戳")
String datetimeEpoch;
@ApiModelProperty(value = "温度")
String temp;
@ApiModelProperty(value = "体感温度")
String feelslike;
@ApiModelProperty(value = "相对湿度")
String humidity;
@ApiModelProperty(value = "所在城市名")
String dew;
@ApiModelProperty(value = "降雨量")
String precip;
@ApiModelProperty(value = "所在城市名")
String precipprob;
@ApiModelProperty(value = "降雪量")
String snow;
@ApiModelProperty(value = "雪深")
String snowdepth;
@ApiModelProperty(value = "所在城市名")
String preciptype;
@ApiModelProperty(value = "所在城市名")
String windgust;
@ApiModelProperty(value = "风速")
String windspeed;
@ApiModelProperty(value = "风向")
String winddir;
@ApiModelProperty(value = "压强")
String pressure;
@ApiModelProperty(value = "能见度")
String visibility;
@ApiModelProperty(value = "云量")
String cloudcover;
@ApiModelProperty(value = "所在城市名")
String solarradiation;
@ApiModelProperty(value = "太阳能")
String solarenergy;
@ApiModelProperty(value = "紫外线强度")
String uvindex;
@ApiModelProperty(value = "天气情况文本")
String conditions;
@ApiModelProperty(value = "天气图标")
String icon;
}
#nacos配置
weather:
visualcrossing:
key: UCSRD3H7BX7UHPK8DBBKQV6CL
weatherUrl: https://weather.visualcrossing.com/VisualCrossingWebServices/rest/services/timeline/{lat},{lon}/today?key={apiKey}&contentType=json&lang=en&include=current&unitGroup=metric
cacheTime: 600
fahrenheitCountry: US