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; } }