Newer
Older
huludao / src / main / java / com / newfiber / api / pc / service / impl / GaofenWeatherService.java
package com.newfiber.api.pc.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.newfiber.api.config.GaofenStaticDataConfig;
import com.newfiber.api.config.GaofenWeather;
import com.newfiber.api.core.commons.CustomException;
import com.newfiber.api.pc.model.gaofen.*;
import com.newfiber.api.pc.service.WeatherRecordService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.data.redis.core.BoundListOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.ResponseEntity;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.client.RestTemplate;

import java.nio.charset.Charset;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * 高分天气API接口集成业务层
 * @ClassName GaofenWeatherService
 * @Description TODO
 * @Author 张鸿志
 * @Date 2021年8月18日14:22:59 14:22
 * Version 1.0
 **/
@Service
@Transactional(rollbackFor = Exception.class)
@Slf4j
public class GaofenWeatherService {

    @Autowired
    private GaofenWeather gaofenWeather;

    @Autowired
    @Qualifier("redisTemplateBySmartWeather")
    private RedisTemplate<String, SmartWeather> redisTemplateBySmartWeather;

    @Autowired
    @Qualifier("redisTemplateByDayByDayForecast")
    private RedisTemplate<String, DayByDayForecast> redisTemplateByDayByDayForecast;

    @Autowired
    @Qualifier("redisTemplateByOneByOne")
    private RedisTemplate<String, OneByOneHour> redisTemplateByOneByOne;

    @Autowired
    private RestTemplate restTemplate;

    @Autowired
    private WeatherRecordService weatherRecordService;

    private final static SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");

    /**
     * 获取上一个的历史天气数据
     * @param:
     * @return:
     * @author: 张鸿志
     * @data: 2021/10/21 11:02
     */
    public void  getLastMonthWeather() throws ParseException {
        Calendar instance = Calendar.getInstance();
        instance.add(Calendar.MONTH, -1);
        int maximum = instance.getActualMaximum(Calendar.DAY_OF_MONTH);
        int month = instance.get(Calendar.MONTH) + 1;
        int year = instance.get(Calendar.YEAR);
//        if(month == 1){
//            year--;
//        }
        StringBuilder startTime = new StringBuilder();
        startTime.append(year);
        if(month < 10){
            startTime.append(0 + "" + month);
        }else{
            startTime.append(month);
        }
        startTime.append("01");
        StringBuilder endTime = new StringBuilder();
        endTime.append(year);
        if(month < 10){
            endTime.append(0 + "" + month);
        }else{
            endTime.append(month);
        }
        endTime.append(maximum);
        StringBuilder builder = new StringBuilder();
        builder.append(gaofenWeather.getHistoryWeatherUrl());
        builder.append("?areacode=101071401&inquiry=duration&start=");
        builder.append(startTime.toString());
        builder.append("&end=");
        builder.append(endTime.toString());
        builder.append("&date=0808");
        builder.append("&key=");
        builder.append(gaofenWeather.getKey());
        builder.append("&output_type=json");
        System.out.println(builder.toString());
        restTemplate.getMessageConverters().set(1,new StringHttpMessageConverter(Charset.forName("UTF-8")));
        ResponseEntity<String> forEntity = restTemplate.getForEntity(builder.toString(), String.class);
        if(forEntity.getStatusCode().is2xxSuccessful()){
            List<WeatherRecord> weatherRecords = new ArrayList<>();
            JSONObject object = JSONObject.parseObject(forEntity.getBody());
            JSONObject result = JSONObject.parseObject(JSON.toJSONString(object.get("result")));
            List<JSONObject> hisWeather = JSONArray.parseArray(JSON.toJSONString(result.get("hisWeather")), JSONObject.class);
            for (JSONObject jsonObject : hisWeather) {
                WeatherRecord weatherRecord = new WeatherRecord();
                weatherRecord.setTime(format.parse((String) jsonObject.get("date")));
                String textDay = jsonObject.get("text_day").toString();
                String textNight = jsonObject.get("text_night").toString();
                String weather = "";
                if(textNight.contains("雨")){
                    weather = textNight;
                }
                if(textDay.contains("雨")){
                    weather = textDay;
                }

                weatherRecord.setWeather(StringUtils.isNotBlank(weather) ? weather : textDay);
                weatherRecords.add(weatherRecord);
            }
            //写入历史天气数据表
            if(weatherRecordService.insertBatch(weatherRecords)){
                log.info("历史天气{} - {}时间段的数据写入成功",startTime,endTime);
            }
        }else{
            throw new CustomException(500,"应急指挥高分天气接口调用失败!");
        }
    }






    /**
     * 应急指挥中的天气预报功能。查询信阳市当天的智能天气概况
     * @Param []
     * @return {java.lang.String}
     * @throws
     * @author 张鸿志
     * @date 2021/8/18 14:56
     */
    public EmergencyCommandWeather getSmartAndDayByDayWeather() {
        //获取智能天气概况
        SmartWeather currentSmartWeather = getCurrentSmartWeather();
        //获取逐日天气数据
        List<DayByDayForecast> currentDayByDayForecast = getCurrentDayByDayForecast();
        currentSmartWeather.setLow(currentDayByDayForecast.get(0).getLow());
        currentSmartWeather.setHigh(currentDayByDayForecast.get(0).getHigh());
        currentDayByDayForecast=currentDayByDayForecast.subList(1,currentDayByDayForecast.size()-1);
        EmergencyCommandWeather emergencyCommandWeather = new EmergencyCommandWeather();
        emergencyCommandWeather.setSmartWeather(currentSmartWeather);
        emergencyCommandWeather.setDayByDayForecastList(currentDayByDayForecast);
        return emergencyCommandWeather;
    }

    /**
     * 获取智能实况天气 数据。
     * 通过判断redis中是否已有数据。
     * 如果redis中存在,则返回,没有 则重新发起请求获取,并通过计算得到对应redis的超时时间进行结果设置
     * 这样可以减少api的调用次数
     * @Param []
     * @return {com.newfiber.api.pc.model.gaofen.SmartWeather}
     * @throws
     * @author 张鸿志
     * @date 2021/8/18 15:45
     */
    public SmartWeather getCurrentSmartWeather(){
        //判断redis中是否存在
        if(redisTemplateBySmartWeather.hasKey(GaofenStaticDataConfig.SMARTWEATHERKEY)){
            //如果存在直接返回
            return redisTemplateBySmartWeather.opsForValue().get(GaofenStaticDataConfig.SMARTWEATHERKEY);
        }
        //不存在,调用高分天气api接口,并将结果存入reids
        //?areacode=101071401&key=lGrM39X3U5gFn0CXjxtZohe5REz1RJm7&output_type=json
        String url = gaofenWeather.getSmartUrl() + "?areacode=101071401&key="+gaofenWeather.getKey()+"&output_type=json";
        ResponseEntity<String> forEntity = restTemplate.getForEntity(url, String.class);
        if(forEntity.getStatusCode().is2xxSuccessful()){
            JSONObject object = JSONObject.parseObject(forEntity.getBody());
            JSONObject result = JSONObject.parseObject(JSON.toJSONString(object.get("result")));
            JSONObject smartJSONObject = JSONObject.parseObject(JSON.toJSONString(result.get("realtime")));
            SmartWeather smartWeather = new SmartWeather(smartJSONObject);
            //默认设置为两分钟
            redisTemplateBySmartWeather.opsForValue().set(GaofenStaticDataConfig.SMARTWEATHERKEY,smartWeather,120, TimeUnit.SECONDS);
            return smartWeather;
            //计算得到本次调用后 距离下次数据更新还有多少秒
        }else{
            throw new CustomException(500,"应急指挥高分天气接口调用失败!");
        }

    }

    /**
     * 获取当前时间的未来7天天气预报
     * @Param []
     * @return {com.newfiber.api.pc.model.gaofen.DayByDayForecast}
     * @throws
     * @author 张鸿志
     * @date 2021/8/18 17:02
     */
    private List<DayByDayForecast> getCurrentDayByDayForecast(){
        //判断redis中是否存在
        if(redisTemplateByDayByDayForecast.hasKey(GaofenStaticDataConfig.DAYBYDAYWEATHERKEY)){
            List<DayByDayForecast> range = redisTemplateByDayByDayForecast.opsForList().range(GaofenStaticDataConfig.DAYBYDAYWEATHERKEY, 0, -1);
            Collections.reverse(range);
            //如果存在直接返回
            return  range;
        }
        //未来6天降雨量,去掉今天的(因为今天是实况)
        String url = gaofenWeather.getDayByDayForecastUrl() + "?areacode=101071401&days=7&key="+gaofenWeather.getKey()+"&output_type=json";
        ResponseEntity<String> forEntity = restTemplate.getForEntity(url, String.class);
        if(forEntity.getStatusCode().is2xxSuccessful()){
            JSONObject object = JSONObject.parseObject(forEntity.getBody());
            JSONObject result = JSONObject.parseObject(JSON.toJSONString(object.get("result")));
            List<DayByDayForecast> dayByDayForecastList = new ArrayList<>();
            List<JSONObject> dailyFcsts = JSONArray.parseArray(JSON.toJSONString(result.get("daily_fcsts")), JSONObject.class);
            //JSONObject dayByDayForecastJsonObject = JSONObject.parseObject(JSON.toJSONString(object.get("daily_fcsts")));
            //去掉当天
            for (int i = 0; i < dailyFcsts.size(); i++) {
                DayByDayForecast dayByDayForecast = new DayByDayForecast(dailyFcsts.get(i));
                dayByDayForecastList.add(dayByDayForecast);
            }


            BoundListOperations<String, DayByDayForecast> listOperations = redisTemplateByDayByDayForecast.boundListOps(GaofenStaticDataConfig.DAYBYDAYWEATHERKEY);
            if(null!=listOperations && dayByDayForecastList.size()>0){
                listOperations.leftPushAll(dayByDayForecastList.toArray(new DayByDayForecast[dayByDayForecastList.size()]));
                //根据当前时间来设置超时时间
                //long dayByDayWeatherRedisExpireKeyTime = getDayByDayWeatherRedisExpireKeyTime();
                // log.info("当前得到的超时时间为:{}",dayByDayWeatherRedisExpireKeyTime);
                listOperations.expire(1,TimeUnit.HOURS);
            }
            return dayByDayForecastList;
            //计算得到本次调用后 距离下次数据更新还有多少秒
        }else{
            throw new CustomException(500,"应急指挥高分天气接口dayByDayForecast调用失败!");
        }

    }


    /**
     * 返回当前时间下 距离 下次数据更新还有多少秒
     * 逐日天气接口数据的更新时间为早上 8点  中午  12   下午 8点  这三个时间段
     * @Param []
     * @return {long}
     * @throws
     * @author 张鸿志
     * @date 2021/8/18 16:04
     */
    private long getDayByDayWeatherRedisExpireKeyTime(){
        Calendar instance = Calendar.getInstance();
        Date compareTime = instance.getTime();
        Calendar operationCalendar = Calendar.getInstance();
        operationCalendar.set(instance.get(Calendar.YEAR),instance.get(Calendar.MONTH),instance.get(Calendar.DATE),20,0,0);
        //如果当前时间比当天的晚上20点大,那么就将明天早上8点的时间戳 - 当前时间得到redis的超时时间
        if(compareTime.compareTo(operationCalendar.getTime()) >= 0){
            operationCalendar.set(instance.get(Calendar.YEAR),instance.get(Calendar.MONTH),instance.get(Calendar.DATE) + 1,8,0,0);
            long targetTime = operationCalendar.getTime().getTime();
            long time = compareTime.getTime();
            return targetTime - time / 1000;
        }
        operationCalendar.set(instance.get(Calendar.YEAR),instance.get(Calendar.MONTH),instance.get(Calendar.DATE),12,0,0);
        if(compareTime.compareTo(operationCalendar.getTime()) >= 0){
            operationCalendar.set(instance.get(Calendar.YEAR),instance.get(Calendar.MONTH),instance.get(Calendar.DATE),20,0,0);
            Date operationCalendarTime = operationCalendar.getTime();
            long targetTime = operationCalendarTime.getTime();
            long time = compareTime.getTime();
            return (targetTime - time) /1000;
        }
        operationCalendar.set(instance.get(Calendar.YEAR),instance.get(Calendar.MONTH),instance.get(Calendar.DATE),8,0,0);
        if(compareTime.compareTo(operationCalendar.getTime()) >= 0){
            operationCalendar.set(instance.get(Calendar.YEAR),instance.get(Calendar.MONTH),instance.get(Calendar.DATE),12,0,0);
            long targetTime = operationCalendar.getTime().getTime();
            long time = compareTime.getTime();
            return (targetTime - time) /1000;
        }

        return 0L;
    }

    /**
     * 查询信阳市逐小时预报未来三天的数据。
     * 将查询出来的数据按照API提供的接口刷新频率进行数据保存
     * 通过传的参数来控制返回的数据情况
     * day = 1:表示获取当天的24小时数据
     * day = 2:表示获取明天的24~48小时数据
     * day = 3:表示获取72小时的数据
     * @Param [day]
     * @return {com.newfiber.api.pc.model.gaofen.OneByOneResponse}
     * @throws
     * @author 张鸿志
     * @date 2021/8/18 21:21
     */
    public OneByOneResponse getFutrueWeather(Integer day) {
        //如果存在就从redis中去拿
        if(redisTemplateByOneByOne.hasKey(GaofenStaticDataConfig.ONEBYONEHOURKEY)){
            List<OneByOneHour> range = redisTemplateByOneByOne.opsForList().range(GaofenStaticDataConfig.ONEBYONEHOURKEY, 0, -1);
            Collections.reverse(range);
            return getOneByOneResponse(range, day);
        }
        //不存在则调用接口
        //?areacode=101071401&hours=72&key=lGrM39X3U5gFn0CXjxtZohe5REz1RJm7&output_type=json
        String url = gaofenWeather.getOneByOneUrl() + "?areacode=101071401&hours=72&key="+gaofenWeather.getKey()+"&output_type=json";
        ResponseEntity<String> forEntity = restTemplate.getForEntity(url, String.class);
        if(forEntity.getStatusCode().is2xxSuccessful()){
            JSONObject object = JSONObject.parseObject(forEntity.getBody());
            JSONObject result = JSONObject.parseObject(JSON.toJSONString(object.get("result")));
            List<OneByOneHour> oneByOneHours = new ArrayList<>();
            List<JSONObject> dailyFcsts = JSONArray.parseArray(JSON.toJSONString(result.get("hourly_fcsts")), JSONObject.class);
            for (JSONObject dailyFcst : dailyFcsts) {
                OneByOneHour oneByOneHour = new OneByOneHour(dailyFcst);
                oneByOneHours.add(oneByOneHour);
            }
            OneByOneResponse oneByOneResponse = getOneByOneResponse(oneByOneHours, day);
            redisTemplateByOneByOne.opsForList().leftPushAll(GaofenStaticDataConfig.ONEBYONEHOURKEY,oneByOneHours);
            //每小时更新一次
            redisTemplateByOneByOne.expire(GaofenStaticDataConfig.ONEBYONEHOURKEY,getOneByOneTime(),TimeUnit.SECONDS);
            return oneByOneResponse;
        }else{
            throw  new CustomException(500,"调用高分天气API(逐小时天气)失败");
        }
    }

    private long getOneByOneTime(){
        Calendar instance = Calendar.getInstance();
        Date time = instance.getTime();
        int hour = instance.get(Calendar.HOUR_OF_DAY);
        instance.set(instance.get(Calendar.YEAR),instance.get(Calendar.MONTH),instance.get(Calendar.DATE),hour + 1,0,0);
        Date remoteTime = instance.getTime();
        return (remoteTime.getTime() - time.getTime()) / 1000;
    }


    /**
     * 通过传入的day 参数来分隔 逐小时预报天气数据。
     * 将分割后的数据构建成一个OneByOneResponse对象进行返回。
     * @Param [oneByOneHours, day]
     * @return {com.newfiber.api.pc.model.gaofen.OneByOneResponse}
     * @throws
     * @author 张鸿志
     * @date 2021/8/19 9:34
     */
    private OneByOneResponse getOneByOneResponse(List<OneByOneHour> oneByOneHours,Integer day){
        int index = 0;

        //如果是获取当天24小时的数据
        if(day.compareTo(1) <= 0){
            index = 24;
        }

        //如果是获取明天的数据
        if(day.compareTo(2) == 0){
            index = 48;
        }

        //如果是获取未来三天数据
        if(day.compareTo(3) >= 0){
            index = oneByOneHours.size();
        }
        //拿到需要返回的数据,构建OneByOneResponse
        List<OneByOneHour> oneByOneHourList = oneByOneHours.subList(0, index);
        OneByOneResponse oneByOneResponse = new OneByOneResponse();
        oneByOneResponse.setWindSpeeds(oneByOneHourList.stream().map(OneByOneHour::getWindSpeed).collect(Collectors.toList()));
        oneByOneResponse.setTimes(oneByOneHourList.stream().map(OneByOneHour::getDataTime).collect(Collectors.toList()));
        oneByOneResponse.setTempFcs(oneByOneHourList.stream().map(OneByOneHour::getTempFc).collect(Collectors.toList()));
        oneByOneResponse.setPrec(oneByOneHourList.stream().map(OneByOneHour::getPrec).collect(Collectors.toList()));

        return oneByOneResponse;
    }

}