Преглед изворни кода

修复BUG单WA04-59,新增天气功能,获取小孩接口增加返回天气,定位上报接口返回天气。

tea.yu пре 1 месец
родитељ
комит
66a5caf719

+ 20 - 2
business-application/src/main/java/com/sikey/wa04/business/application/service/location/LocationService.java

@@ -15,6 +15,7 @@ import com.sikey.wa04.business.infrastructure.adapter.RedisMessageIdIncrementing
 import com.sikey.wa04.business.infrastructure.adapter.amap.AmapRequestService;
 import com.sikey.wa04.business.infrastructure.adapter.amap.AmapResponse;
 import com.sikey.wa04.business.infrastructure.adapter.baidumap.BaiduMapRequestService;
+import com.sikey.wa04.business.infrastructure.adapter.weather.BaiduWeatherService;
 import com.sikey.wa04.business.infrastructure.configuration.NatsConfig;
 import com.sikey.wa04.business.infrastructure.dto.model.DeviceModel;
 import com.sikey.wa04.business.infrastructure.dto.request.ask.LocationAskRequest;
@@ -127,6 +128,9 @@ public class LocationService {
     @Resource
     private DeviceConfigAdapter deviceConfigAdapter;
 
+    @Resource
+    private BaiduWeatherService baiduWeatherService;
+
     // trainSpeed the train has moved at a speed of 83 meters per second
     private float trainSpeed = 0.0833333333F; //  300 / (1 * 60 * 60) = 0.0833333333 km/s
 
@@ -245,11 +249,24 @@ public class LocationService {
 
         location.setDeviceId(new DeviceId(deviceModel.getId()));
         log.info("[定位]半径数据为:{}", location.getAddress().getRadius());
+
+        IotResponse iotResponse = new IotResponse();
         if (location.getAddress().getRadius() != -1) {
             locationAdapter.save(location, false);
+            //当存在城市id时,查询天气
+            if (location.getAddress().getAdCode() !=null) {
+                Weather weather = baiduWeatherService.getWeather(location.getAddress().getAdCode());
+                iotResponse.setWeather(new IotResponse.Weather()
+                        .setCode(weather.getTextCode())
+                        .setMyTextCode(weather.getMyTextCode())
+                        .setHigh(weather.getHigh())
+                        .setLow(weather.getLow())
+                        .setNow(weather.getNow())
+                );
+            }
         }
 
-        IotResponse iotResponse = new IotResponse().setAddress(location.getAddress().getDesc()).setCity(location.getAddress().getCity()).setCityId(location.getAddress().getCityCode());
+        iotResponse.setAddress(location.getAddress().getDesc()).setCity(location.getAddress().getCity()).setCityId(location.getAddress().getCityCode());
         log.info("[定位]保存定位数据:{},返回数据:{}", JSONUtil.toJsonPrettyStr(location), JSONUtil.toJsonPrettyStr(iotResponse));
         return iotResponse;
     }
@@ -575,11 +592,12 @@ public class LocationService {
         addressComponent.setDistrict(baiduLocationResponse.getDistrict());
         addressComponent.setProvince(baiduLocationResponse.getProvince());
         addressComponent.setStreet(baiduLocationResponse.getRoad());
-
+        addressComponent.setAdCode(baiduLocationResponse.getAdcode());
         String[] points = LocationUtil.splitPoint(position.getLocation());
         // 上海市 浦东新区 规划一支路 靠近陆家嘴软件园
         // city district  road  poi
 
+        //百度定位不会AdCode,只有高德定位才会返回,所以当时百度定位时需要通过District(县区)
         String desc = "%s %s %s %s %s".formatted(addressComponent.getCountry(), addressComponent.getCity(), addressComponent.getDistrict(), addressComponent.getRoad(), addressComponent.getStreet());
         Address address = new Address().setLongitude(points[0]).setLatitude(points[1]).setRadius(position.getRadius()).setDesc(desc).setCountry(addressComponent.getCountry()).setProvince(addressComponent.getProvince()).setCity(addressComponent.getCity()).setCityCode(addressComponent.getCityCode()).setAdCode(addressComponent.getAdCode()).setRoad(addressComponent.getRoad()).setPoi(addressComponent.getPoi());
         Location location = new Location().setChannel(TrajectoryChannel.BAIDUMAPS).setType(locationType).setChildId(childId).setTraceId(request.traceId()).setAddress(address);

+ 30 - 8
business-application/src/main/java/com/sikey/wa04/business/application/service/user/ChildService.java

@@ -12,10 +12,8 @@ import com.sikey.wa04.business.domain.adapter.*;
 import com.sikey.wa04.business.domain.entity.*;
 import com.sikey.wa04.business.domain.usecase.ChildIdFactory;
 import com.sikey.wa04.business.domain.usecase.GroupIdFactory;
-import com.sikey.wa04.business.infrastructure.dto.model.ChildModel;
-import com.sikey.wa04.business.infrastructure.dto.model.ChildRefModel;
-import com.sikey.wa04.business.infrastructure.dto.model.ContactModel;
-import com.sikey.wa04.business.infrastructure.dto.model.UserModel;
+import com.sikey.wa04.business.infrastructure.adapter.weather.BaiduWeatherService;
+import com.sikey.wa04.business.infrastructure.dto.model.*;
 import com.sikey.wa04.business.infrastructure.dto.request.child.CreateChildRequest;
 import com.sikey.wa04.business.infrastructure.dto.request.child.GetPhoneNumberRequest;
 import com.sikey.wa04.business.infrastructure.dto.request.child.UpdateChildRequest;
@@ -25,10 +23,7 @@ import com.sikey.wa04.business.infrastructure.dto.response.blockunknowncall.GetB
 import com.sikey.wa04.business.infrastructure.dto.response.child.CreateChildResponse;
 import com.sikey.wa04.business.infrastructure.dto.response.child.GetChildResponse;
 import com.sikey.wa04.business.infrastructure.dto.response.child.GetPhoneNumberResponse;
-import com.sikey.wa04.business.infrastructure.repository.ChildRefRepository;
-import com.sikey.wa04.business.infrastructure.repository.ChildRepository;
-import com.sikey.wa04.business.infrastructure.repository.ContactRepository;
-import com.sikey.wa04.business.infrastructure.repository.UserRepository;
+import com.sikey.wa04.business.infrastructure.repository.*;
 import com.sikey.wa04.common.dto.header.GenericManufacturerHeader;
 import com.sikey.wa04.common.enums.ContactRole;
 import com.sikey.wa04.common.enums.Gender;
@@ -111,6 +106,12 @@ public class ChildService extends BaseService {
     @Resource
     private ContactMessageService contactMessageService;
 
+    @Resource
+    private BaiduWeatherService baiduWeatherService;
+
+    @Resource
+    private MapTrajectoryPoiRepository mapTrajectoryPoiRepository;
+
     // trainSpeed the train has moved at a speed of 83 meters per second
     private float trainSpeed = 0.0833333333F; //  300 / (1 * 60 * 60) = 0.0833333333 km/s
 
@@ -416,6 +417,27 @@ public class ChildService extends BaseService {
                             .setPositionTime(String.valueOf(location.getPositionTime().toEpochMilli()))
                             .setRadius(location.getAddress().getRadius())
                             .setPointType(Integer.parseInt(location.getType().getTargetValue())));
+
+                    //查询天气返回数据
+                    //查询定位的城市信息
+                    Optional<MapTrajectoryPoiModel> optionalMapTrajectoryPoiModel = mapTrajectoryPoiRepository.findByTrajectoryId(location.getId());
+                    if (optionalMapTrajectoryPoiModel.isPresent()) {
+                        MapTrajectoryPoiModel trajectoryPoiModel = optionalMapTrajectoryPoiModel.get();
+                        if (trajectoryPoiModel.getAdCode() != null) {
+                            //根据城市id查询天气
+                            Weather weather = baiduWeatherService.getWeather(trajectoryPoiModel.getAdCode());
+                            //组装设置天气返回数据
+                            result.setWeather(new GetChildResponse.GetWeather()
+                                    .setCityId(weather.getCityId())
+                                    .setMyTextCode(weather.getMyTextCode())
+                                    .setTextCode(weather.getTextCode())
+                                    .setHigh(weather.getHigh())
+                                    .setLow(weather.getLow())
+                                    .setNow(weather.getNow())
+                            );
+                        }
+
+                    }
                     // GlobalCoordinates coordinates = location.getCoordinates();
 
                     // Convert the coordinate system

+ 3 - 2
business-common/src/main/java/com/sikey/wa04/common/enums/WeatherPhenomenon.java

@@ -239,7 +239,8 @@ public enum WeatherPhenomenon {
     TORNADO("龙卷风", "107", "33", "Tornado", "Tornado");
 
     private final String name;
-    private final String code;
+    //MyCode 自定义Code, 百度的Code转换为我们自己的Code,可以做到归类天气返回
+    private final String myCode;
     private final String baiduCode;
     private final String day;
     private final String night;
@@ -255,7 +256,7 @@ public enum WeatherPhenomenon {
 
     public static WeatherPhenomenon valueOfCode(String code) {
         for (WeatherPhenomenon phenomenon : values()) {
-            if (phenomenon.getCode().equals(code)) {
+            if (phenomenon.getBaiduCode().equals(code)) {
                 return phenomenon;
             }
         }

+ 33 - 0
business-domain/src/main/java/com/sikey/wa04/business/domain/entity/Weather.java

@@ -1,4 +1,37 @@
 package com.sikey.wa04.business.domain.entity;
 
+import lombok.Data;
+import lombok.experimental.Accessors;
+
+
+/**
+ * 天气
+ */
+@Data
+@Accessors(chain = true)
 public class Weather {
+    /**
+     * 城市ID
+     */
+    private String cityId;
+    /**
+     * 自定义气象Code
+     */
+    private String myTextCode;
+    /**
+     * 原始气象Code
+     */
+    private String textCode;
+    /**
+     * 最高温
+     */
+    private Integer high;
+    /**
+     *  最低温
+     */
+    private Integer low;
+    /**
+     * 当前温度
+     */
+    private Integer now;
 }

+ 224 - 0
business-infrastructure/src/main/java/com/sikey/wa04/business/infrastructure/adapter/weather/BaiduWeatherService.java

@@ -0,0 +1,224 @@
+package com.sikey.wa04.business.infrastructure.adapter.weather;
+
+import cn.hutool.json.JSONUtil;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.PropertyNamingStrategies;
+import com.sikey.wa04.business.domain.entity.Weather;
+import com.sikey.wa04.business.infrastructure.dto.model.MapWeatherModel;
+import com.sikey.wa04.business.infrastructure.repository.WeatherRepository;
+import com.sikey.wa04.common.enums.WeatherPhenomenon;
+import jakarta.annotation.Resource;
+import lombok.Setter;
+import lombok.extern.slf4j.Slf4j;
+import okhttp3.OkHttpClient;
+import okhttp3.Request;
+import okhttp3.Response;
+import okhttp3.ResponseBody;
+import org.apache.http.client.utils.URIBuilder;
+import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
+import org.springframework.stereotype.Component;
+
+import java.io.IOException;
+import java.net.URISyntaxException;
+import java.time.Instant;
+import java.time.temporal.ChronoUnit;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * 百度天气
+ */
+@Setter
+@Slf4j
+@Component
+public class BaiduWeatherService {
+    /**
+     * 百度获取填写的接口地址
+     */
+    private final String weatherUrl = "https://api.map.baidu.com/weather/v1/?";
+
+    // ak API控制台申请得到的ak
+    private final String ak = "suBnrGHXb5K7iPUtipyLPDBzPHlnSMqc";
+
+    private final String output   = "json";
+    private final String dataType = "all";
+
+    private volatile OkHttpClient httpClient;
+
+    private OkHttpClient getHttpClient() {
+        if (httpClient == null) {
+            synchronized (this) {
+                if (httpClient == null) {
+                    httpClient = new OkHttpClient.Builder()  // 使用 Builder 模式创建 OkHttpClient
+                            .connectTimeout(60, TimeUnit.SECONDS)  // 设置连接超时时间
+                            .readTimeout(60, TimeUnit.SECONDS)     // 设置读取超时时间
+                            .writeTimeout(60, TimeUnit.SECONDS)    // 设置写入超时时间
+                            .build();  // 构建 OkHttpClient 实例
+                }
+            }
+        }
+        return httpClient;
+    }
+
+    private static final ObjectMapper objectMapper;
+
+    static {
+        objectMapper = new Jackson2ObjectMapperBuilder().build();
+        objectMapper.setPropertyNamingStrategy(PropertyNamingStrategies.SNAKE_CASE);
+    }
+
+    @Resource
+    private WeatherRepository weatherRepository;
+
+    /**
+     * 百度天气
+     *
+     * @param cityId 城市id
+     * @return
+     */
+    public Weather getWeather(String cityId)  {
+        //查询当前数据库是否有对应城市Id的天气
+        MapWeatherModel weatherModel = weatherRepository.findByCityId(cityId);
+        Instant curTime = Instant.now();
+        if (weatherModel != null) {
+            //该数据创建时间加4小时后早于当前时间(已超过4小时)
+            if (isOlderThan4Hours(weatherModel.getUpdatedAt())) {
+                log.info("[百度天气]该数据创建时间加4小时后早于当前时间(已超过4小时),需重新获取;cityId:{}", cityId);
+                //获取新天气
+                WeatherData weatherData = requestBaiDuWeather(cityId);
+
+                //组装天气数据存入数据库
+                WeatherDataResultNow now = weatherData.result().now();
+                WeatherDataResultForecasts forecast = weatherData.result().forecasts().getFirst();
+                //更新新天气
+                weatherModel.setTextCode(WeatherPhenomenon.valueOfName(forecast.text_day).getBaiduCode());
+                weatherModel.setNow(now.temp());
+                weatherModel.setDate(forecast.date);
+                weatherModel.setHigh(forecast.high);
+                weatherModel.setLow(forecast.low);
+                weatherModel.setUpdatedAt(curTime);
+                weatherRepository.save(weatherModel);
+            }
+        } else {
+            //不存在则查询百度天气获取
+            WeatherData weatherData = requestBaiDuWeather(cityId);
+            weatherModel = new MapWeatherModel();
+
+            WeatherDataResultNow now = weatherData.result().now();
+            WeatherDataResultForecasts forecast = weatherData.result().forecasts().getFirst();
+
+            //更新新天气
+            weatherModel.setCityId(cityId);
+            weatherModel.setTextCode(WeatherPhenomenon.valueOfName(forecast.text_day).getBaiduCode());
+            weatherModel.setNow(now.temp());
+            weatherModel.setDate(forecast.date);
+            weatherModel.setHigh(forecast.high);
+            weatherModel.setLow(forecast.low);
+            weatherModel.setCreatedAt(curTime);
+            weatherModel.setUpdatedAt(curTime);
+            //保存新天气
+            weatherRepository.save(weatherModel);
+        }
+        log.info("[百度天气]天气查询结果;cityId:{}; weatherModel:{}", cityId, JSONUtil.toJsonPrettyStr(weatherModel));
+        //获取自定义的天气code
+        String myTextCode = WeatherPhenomenon.valueOfBaiduCode(weatherModel.getTextCode()).getMyCode();
+        //返回天气
+        return new Weather()
+                .setCityId(weatherModel.getCityId())
+                .setTextCode(weatherModel.getTextCode())
+                .setMyTextCode(myTextCode)
+                .setHigh(weatherModel.getHigh())
+                .setLow(weatherModel.getLow())
+                .setNow(weatherModel.getNow());
+    }
+
+    /**
+     * 发起request请求百度天气获取最新天气信息
+     *
+     * @param cityId
+     * @return
+     */
+    public WeatherData requestBaiDuWeather(String cityId)  {
+        //组装请求地址和参数
+        String reqUrl = buildWeatherUrl(cityId);
+
+        OkHttpClient client = getHttpClient();
+        Request request = new Request.Builder()
+                .url(reqUrl) // OkHttp直接接受完整URL
+                .build();
+
+        // 使用newCall替换send
+        try (Response response = client.newCall(request).execute()) { // 同步请求
+            if (!response.isSuccessful()) {
+                throw new RuntimeException("[获取百度天气] Request failed with code: " + response.code());
+            }
+
+            ResponseBody body = response.body();
+            if (body == null) {
+                throw new RuntimeException("[获取百度天气] Response body is null");
+            }
+
+            String bodyString = body.string();
+            WeatherData  weatherData  = objectMapper.readValue(bodyString, WeatherData.class);
+            if (weatherData.status != 0){
+                log.info("[获取百度天气]status 不等于 0 ;message:{}",weatherData.message);
+            }
+            return weatherData;
+        } catch (IOException e) {
+            throw new RuntimeException("[获取百度天气]失败eror city: " + cityId, e);
+        }
+    }
+
+    /**
+     * 使用Instant检查数据是否超过4小时
+     * @param createTime 数据对象
+     * @return true如果创建时间+4小时早于当前时间
+     */
+    public static boolean isOlderThan4Hours(Instant createTime) {
+        // 当前时间
+        Instant now = Instant.now();
+
+        // 创建时间加4小时
+        Instant createPlus4Hours = createTime.plus(4, ChronoUnit.HOURS);
+
+        // 比较
+        return createPlus4Hours.isBefore(now);
+    }
+
+    /**
+     * 组装请求地址和参数
+     * @param cityId
+     * @return
+     */
+    public String buildWeatherUrl(String cityId) {
+        try {
+            return new URIBuilder(weatherUrl)
+                    .addParameter("district_id", cityId)
+                    .addParameter("data_type", dataType)
+                    .addParameter("output", output)
+                    .addParameter("ak", ak)
+                    .build()
+                    .toString();
+        } catch (URISyntaxException e) {
+            throw new IllegalArgumentException("[获取百度天气]buildWeatherUrl URL Invalid", e);
+        }
+    }
+
+    record WeatherData(Integer status, WeatherDataResult result, String message) {
+    }
+
+    private record WeatherDataResult(WeatherDataResultLocation location, WeatherDataResultNow now, List<WeatherDataResultForecasts> forecasts) {
+    }
+
+
+    private record WeatherDataResultLocation(String country, String province, String city, String name, String id) {
+    }
+
+    private record WeatherDataResultNow(String text, Integer temp, Integer feels_like, Integer rh, String wind_class, String wind_dir, String uptime) {
+    }
+
+
+    private record WeatherDataResultForecasts(String text_day, String text_night, Integer high, Integer low, String wc_day, String wd_day, String wc_night, String wd_night, String date, String week) {
+    }
+
+}

+ 8 - 5
business-infrastructure/src/main/java/com/sikey/wa04/business/infrastructure/dto/model/MapWeatherModel.java

@@ -1,9 +1,6 @@
 package com.sikey.wa04.business.infrastructure.dto.model;
 
-import jakarta.persistence.Column;
-import jakarta.persistence.Entity;
-import jakarta.persistence.Id;
-import jakarta.persistence.Table;
+import jakarta.persistence.*;
 import jakarta.validation.constraints.NotNull;
 import jakarta.validation.constraints.Size;
 import lombok.Getter;
@@ -19,12 +16,18 @@ import java.time.LocalDate;
 @Table(name = "tb_map_weather")
 public class MapWeatherModel extends BaseModel {
     @Id
+    @GeneratedValue(strategy = GenerationType.IDENTITY)
     @Size(max = 36)
     @Column(name = "id", length = 36)
     private String id;
 
+    @Size(max = 25)
+    @NotNull
+    @Column(name = "city_id", nullable = false, length = 25)
+    private String cityId;
+
     @Column(name = "date")
-    private LocalDate date;
+    private String date;
 
     @Size(max = 25)
     @NotNull

+ 1 - 0
business-infrastructure/src/main/java/com/sikey/wa04/business/infrastructure/dto/response/baidu/BaiduLocationResponse.java

@@ -32,4 +32,5 @@ public class BaiduLocationResponse {
     private List<String> paname;// 附近POI名称列表
     private String sub_station; // 地铁站点名称
     private String sub_line;    // 地铁线路名称
+    private String adcode;      // 地址编码
 }

+ 30 - 0
business-infrastructure/src/main/java/com/sikey/wa04/business/infrastructure/dto/response/child/GetChildResponse.java

@@ -52,6 +52,35 @@ public class GetChildResponse implements Serializable {
         private Integer pointType;
     }
 
+    @Data
+    @Accessors(chain = true)
+    public static class GetWeather {
+        /**
+         * 城市ID
+         */
+        private String cityId;
+        /**
+         * 自定义气象Code
+         */
+        private String myTextCode;
+        /**
+         * 原始气象Code
+         */
+        private String textCode;
+        /**
+         * 最高温
+         */
+        private Integer high;
+        /**
+         *  最低温
+         */
+        private Integer low;
+        /**
+         * 当前温度
+         */
+        private Integer now;
+    }
+
     @Data
     @Accessors(chain = true)
     public static class GetChild {
@@ -69,5 +98,6 @@ public class GetChildResponse implements Serializable {
         private Integer role;
         private GetChildDevice device;
         private GetChildLocation lastLocation;
+        private GetWeather weather;
     }
 }

+ 5 - 0
business-infrastructure/src/main/java/com/sikey/wa04/business/infrastructure/dto/response/iot/IotResponse.java

@@ -47,6 +47,11 @@ public class IotResponse implements Serializable {
          */
         private Integer now;
 
+        /**
+         * 自定义气象Code
+         */
+        private String myTextCode;
+
         /**
          * 天气code
          */

+ 20 - 0
business-infrastructure/src/main/java/com/sikey/wa04/business/infrastructure/repository/WeatherRepository.java

@@ -0,0 +1,20 @@
+package com.sikey.wa04.business.infrastructure.repository;
+
+import com.sikey.wa04.business.infrastructure.dto.model.MapWeatherModel;
+import com.sikey.wa04.business.infrastructure.dto.model.UserModel;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.Query;
+import org.springframework.stereotype.Repository;
+
+@Repository
+public interface WeatherRepository extends JpaRepository<MapWeatherModel, String> {
+
+    /**
+     * 根据城市Id查询
+     *
+     * @param cityId 城市Id
+     * @return
+     */
+    MapWeatherModel findByCityId(String cityId);
+
+}