From b09e39ad120086b67371e2d9ac03231cd6c5b398 Mon Sep 17 00:00:00 2001
From: jinpengyong <jpy123456>
Date: Tue, 04 Jan 2022 11:10:39 +0800
Subject: [PATCH] 臭氧预测

---
 screen-api/src/main/java/com/moral/api/service/impl/HistoryHourlyServiceImpl.java  |    8 
 screen-job/src/main/java/com/moral/api/mapper/ForecastMapper.java                  |   16 ++
 screen-common/src/main/java/com/moral/util/DateUtils.java                          |   36 +++-
 screen-job/src/main/java/com/moral/api/service/impl/ForecastServiceImpl.java       |  245 ++++++++++++++++++++++++++++++
 screen-job/src/main/java/com/moral/api/entity/Forecast.java                        |   45 +++++
 screen-api/src/main/java/com/moral/api/service/impl/HistoryDailyServiceImpl.java   |    8 
 screen-job/src/main/java/com/moral/api/service/ForecastService.java                |   18 ++
 screen-api/src/main/java/com/moral/api/service/impl/HistoryMonthlyServiceImpl.java |    8 
 screen-job/src/main/java/com/moral/api/task/ForecastTask.java                      |   27 +++
 screen-job/src/main/resources/mapper/ForecastMapper.xml                            |   12 +
 10 files changed, 402 insertions(+), 21 deletions(-)

diff --git a/screen-api/src/main/java/com/moral/api/service/impl/HistoryDailyServiceImpl.java b/screen-api/src/main/java/com/moral/api/service/impl/HistoryDailyServiceImpl.java
index c976a62..58a61b2 100644
--- a/screen-api/src/main/java/com/moral/api/service/impl/HistoryDailyServiceImpl.java
+++ b/screen-api/src/main/java/com/moral/api/service/impl/HistoryDailyServiceImpl.java
@@ -366,13 +366,13 @@
             result = doubleStream.sum();
         } else {
             if ("min".equals(type)) {
-                optionalDouble = doubleStream.average();
-
-            } else if ("max".equals(type)) {
                 optionalDouble = doubleStream.min();
 
-            } else if ("avg".equals(type)) {
+            } else if ("max".equals(type)) {
                 optionalDouble = doubleStream.max();
+
+            } else if ("avg".equals(type)) {
+                optionalDouble = doubleStream.average();
             }
 
             if (optionalDouble.isPresent()) {
diff --git a/screen-api/src/main/java/com/moral/api/service/impl/HistoryHourlyServiceImpl.java b/screen-api/src/main/java/com/moral/api/service/impl/HistoryHourlyServiceImpl.java
index 6715169..734b4dd 100644
--- a/screen-api/src/main/java/com/moral/api/service/impl/HistoryHourlyServiceImpl.java
+++ b/screen-api/src/main/java/com/moral/api/service/impl/HistoryHourlyServiceImpl.java
@@ -651,13 +651,13 @@
             result = doubleStream.sum();
         } else {
             if ("min".equals(type)) {
-                optionalDouble = doubleStream.average();
-
-            } else if ("max".equals(type)) {
                 optionalDouble = doubleStream.min();
 
-            } else if ("avg".equals(type)) {
+            } else if ("max".equals(type)) {
                 optionalDouble = doubleStream.max();
+
+            } else if ("avg".equals(type)) {
+                optionalDouble = doubleStream.average();
             }
 
             if (optionalDouble.isPresent()) {
diff --git a/screen-api/src/main/java/com/moral/api/service/impl/HistoryMonthlyServiceImpl.java b/screen-api/src/main/java/com/moral/api/service/impl/HistoryMonthlyServiceImpl.java
index 613a00c..e227257 100644
--- a/screen-api/src/main/java/com/moral/api/service/impl/HistoryMonthlyServiceImpl.java
+++ b/screen-api/src/main/java/com/moral/api/service/impl/HistoryMonthlyServiceImpl.java
@@ -331,13 +331,13 @@
             result = doubleStream.sum();
         } else {
             if ("min".equals(type)) {
-                optionalDouble = doubleStream.average();
-
-            } else if ("max".equals(type)) {
                 optionalDouble = doubleStream.min();
 
-            } else if ("avg".equals(type)) {
+            } else if ("max".equals(type)) {
                 optionalDouble = doubleStream.max();
+
+            } else if ("avg".equals(type)) {
+                optionalDouble = doubleStream.average();
             }
 
             if (optionalDouble.isPresent()) {
diff --git a/screen-common/src/main/java/com/moral/util/DateUtils.java b/screen-common/src/main/java/com/moral/util/DateUtils.java
index 7776593..2741da9 100644
--- a/screen-common/src/main/java/com/moral/util/DateUtils.java
+++ b/screen-common/src/main/java/com/moral/util/DateUtils.java
@@ -956,9 +956,9 @@
         cal.setTime(date);
         final int last = cal.getActualMinimum(Calendar.DAY_OF_YEAR);
         cal.set(Calendar.DAY_OF_YEAR, last);
-        cal.set(Calendar.HOUR_OF_DAY,0);
-        cal.set(Calendar.MINUTE,0);
-        cal.set(Calendar.SECOND,0);
+        cal.set(Calendar.HOUR_OF_DAY, 0);
+        cal.set(Calendar.MINUTE, 0);
+        cal.set(Calendar.SECOND, 0);
         return cal.getTime();
     }
 
@@ -977,16 +977,16 @@
     }
 
     /*
-    * ���������������������������������
-    * */
-    public static Date getFirstDayOfMonth(Date date){
+     * ���������������������������������
+     * */
+    public static Date getFirstDayOfMonth(Date date) {
         final Calendar cal = Calendar.getInstance();
         cal.setTime(date);
         final int last = cal.getActualMinimum(Calendar.DAY_OF_MONTH);
         cal.set(Calendar.DAY_OF_MONTH, last);
-        cal.set(Calendar.HOUR_OF_DAY,0);
-        cal.set(Calendar.MINUTE,0);
-        cal.set(Calendar.SECOND,0);
+        cal.set(Calendar.HOUR_OF_DAY, 0);
+        cal.set(Calendar.MINUTE, 0);
+        cal.set(Calendar.SECOND, 0);
         return cal.getTime();
     }
 
@@ -1796,4 +1796,22 @@
         String lastYear = getDateAddYear(DateUtils.dateToDateString(getDate(), DateUtils.yyyy), -1);
         return DateUtils.getDate(lastYear, DateUtils.yyyy);
     }
+
+    //���������������������������������������������������
+    public static Map<Date, List<Integer>> getBeforeAndAfterHourDate(Date date) {
+        String s = dateToDateString(date, yyyy_MM_dd_EN);
+        List<String> timeLag = getTimeLag(s);
+        Map<Date, List<Integer>> result = new HashMap<>();
+        for (String s1 : timeLag) {
+            List<Integer> objects = new ArrayList<>();
+            Date current = getDate(s1, yyyy_MM_dd_HH_EN);
+            Date before = addHours(current, -1);
+            Date after = addHours(current, 1);
+            objects.add(getHour(before));
+            objects.add(getHour(current));
+            objects.add(getHour(after));
+            result.put(current, objects);
+        }
+        return result;
+    }
 }
diff --git a/screen-job/src/main/java/com/moral/api/entity/Forecast.java b/screen-job/src/main/java/com/moral/api/entity/Forecast.java
new file mode 100644
index 0000000..3b0e4e8
--- /dev/null
+++ b/screen-job/src/main/java/com/moral/api/entity/Forecast.java
@@ -0,0 +1,45 @@
+package com.moral.api.entity;
+
+import com.baomidou.mybatisplus.extension.activerecord.Model;
+import java.io.Serializable;
+import java.util.Date;
+
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * <p>
+ * ������������������
+ * </p>
+ *
+ * @author moral
+ * @since 2021-12-31
+ */
+@Data
+@EqualsAndHashCode(callSuper = false)
+public class Forecast extends Model<Forecast> {
+
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * ������id
+     */
+    private Integer cityCode;
+
+    /**
+     * ������
+     */
+    private Date time;
+
+    /**
+     * ������
+     */
+    private String value;
+
+
+    @Override
+    protected Serializable pkVal() {
+        return null;
+    }
+
+}
diff --git a/screen-job/src/main/java/com/moral/api/mapper/ForecastMapper.java b/screen-job/src/main/java/com/moral/api/mapper/ForecastMapper.java
new file mode 100644
index 0000000..c8f0e87
--- /dev/null
+++ b/screen-job/src/main/java/com/moral/api/mapper/ForecastMapper.java
@@ -0,0 +1,16 @@
+package com.moral.api.mapper;
+
+import com.moral.api.entity.Forecast;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+
+/**
+ * <p>
+ * ������������������ Mapper ������
+ * </p>
+ *
+ * @author moral
+ * @since 2021-12-31
+ */
+public interface ForecastMapper extends BaseMapper<Forecast> {
+
+}
diff --git a/screen-job/src/main/java/com/moral/api/service/ForecastService.java b/screen-job/src/main/java/com/moral/api/service/ForecastService.java
new file mode 100644
index 0000000..eb3aeb6
--- /dev/null
+++ b/screen-job/src/main/java/com/moral/api/service/ForecastService.java
@@ -0,0 +1,18 @@
+package com.moral.api.service;
+
+import com.moral.api.entity.Forecast;
+import com.baomidou.mybatisplus.extension.service.IService;
+
+/**
+ * <p>
+ * ������������������ ���������
+ * </p>
+ *
+ * @author moral
+ * @since 2021-12-31
+ */
+public interface ForecastService extends IService<Forecast> {
+
+    void forecastO3();
+
+}
diff --git a/screen-job/src/main/java/com/moral/api/service/impl/ForecastServiceImpl.java b/screen-job/src/main/java/com/moral/api/service/impl/ForecastServiceImpl.java
new file mode 100644
index 0000000..944dd08
--- /dev/null
+++ b/screen-job/src/main/java/com/moral/api/service/impl/ForecastServiceImpl.java
@@ -0,0 +1,245 @@
+package com.moral.api.service.impl;
+
+import com.alibaba.fastjson.JSONObject;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.moral.api.entity.CityAqi;
+import com.moral.api.entity.CityConfigWeatherForecast;
+import com.moral.api.entity.CityWeather;
+import com.moral.api.entity.CityWeatherForecast;
+import com.moral.api.entity.Forecast;
+import com.moral.api.mapper.ForecastMapper;
+import com.moral.api.service.CityAqiService;
+import com.moral.api.service.CityConfigWeatherForecastService;
+import com.moral.api.service.CityWeatherForecastService;
+import com.moral.api.service.CityWeatherService;
+import com.moral.api.service.ForecastService;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.moral.constant.Constants;
+import com.moral.util.AmendUtils;
+import com.moral.util.DateUtils;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.util.ObjectUtils;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.OptionalDouble;
+import java.util.function.Supplier;
+import java.util.stream.Collectors;
+import java.util.stream.DoubleStream;
+import java.util.stream.Stream;
+
+/**
+ * <p>
+ * ������������������ ���������������
+ * </p>
+ *
+ * @author moral
+ * @since 2021-12-31
+ */
+@Service
+public class ForecastServiceImpl extends ServiceImpl<ForecastMapper, Forecast> implements ForecastService {
+
+    @Autowired
+    private CityWeatherForecastService cityWeatherForecastService;
+
+    @Autowired
+    private CityConfigWeatherForecastService cityConfigWeatherForecastService;
+
+    @Autowired
+    private CityWeatherService cityWeatherService;
+
+    @Autowired
+    private CityAqiService cityAqiService;
+
+    @Autowired
+    private ForecastService forecastService;
+
+    private static Map<String, Integer> weatherScore = new HashMap<>();
+
+    static {
+        weatherScore.put("���", 100);
+        weatherScore.put("���", 90);
+        weatherScore.put("������", 80);
+        weatherScore.put("���", 70);
+        weatherScore.put("���", 60);
+        weatherScore.put("������", 60);
+        weatherScore.put("������", 60);
+        weatherScore.put("������", 45);
+        weatherScore.put("���������", 40);
+        weatherScore.put("������������������", 30);
+        weatherScore.put("���������������", 30);
+        weatherScore.put("������", 20);
+        weatherScore.put("���", 10);
+        weatherScore.put("������������������", 5);
+        weatherScore.put("������������������", 4);
+        weatherScore.put("������", 0);
+        weatherScore.put("������", 0);
+        weatherScore.put("������", 0);
+        weatherScore.put("������", 0);
+        weatherScore.put("������", 0);
+        weatherScore.put("������", 0);
+        weatherScore.put("������", 0);
+        weatherScore.put("���������", 0);
+    }
+
+    @Override
+    public void forecastO3() {
+        Date nextDay = DateUtils.addDays(new Date(), 1);
+        String nextTime = DateUtils.dateToDateString(nextDay, DateUtils.yyyy_MM_dd_EN);
+        //������������������������������������������
+        Date start = DateUtils.addMonths(DateUtils.getDate(nextTime), -1);
+
+        //������������������������
+        QueryWrapper<CityConfigWeatherForecast> cityConfigWeatherForecastQueryWrapper = new QueryWrapper<>();
+        cityConfigWeatherForecastQueryWrapper.select("city_code")
+                .eq("is_delete", Constants.NOT_DELETE);
+        List<Object> cityCodes = cityConfigWeatherForecastService.listObjs(cityConfigWeatherForecastQueryWrapper);
+
+
+        //������������������������������������
+        QueryWrapper<CityWeatherForecast> cityWeatherForecastQueryWrapper = new QueryWrapper<>();
+        cityWeatherForecastQueryWrapper.likeRight("time", nextTime)
+                .in("city_code", cityCodes);
+        List<CityWeatherForecast> forecasts = cityWeatherForecastService.list(cityWeatherForecastQueryWrapper);
+        Map<Integer, List<CityWeatherForecast>> cityForecast = forecasts.stream()
+                .collect(Collectors.groupingBy(CityWeatherForecast::getCityCode));
+
+
+        //������������������������������������
+        QueryWrapper<CityWeather> cityWeatherQueryWrapper = new QueryWrapper<>();
+        cityWeatherQueryWrapper.ge("time", start)
+                .in("city_code", cityCodes);
+        List<CityWeather> historyWeather = cityWeatherService.list(cityWeatherQueryWrapper);
+        Map<Integer, List<CityWeather>> cityHistoryWeather = historyWeather.stream()
+                .collect(Collectors.groupingBy(CityWeather::getCityCode));
+
+        //������������������������aqi������
+        QueryWrapper<CityAqi> cityAqiQueryWrapper = new QueryWrapper<>();
+        cityAqiQueryWrapper.ge("time", start)
+                .in("city_code", cityCodes);
+        List<CityAqi> historyAqi = cityAqiService.list(cityAqiQueryWrapper);
+        Map<Integer, List<CityAqi>> cityHistoryAqi = historyAqi.stream()
+                .collect(Collectors.groupingBy(CityAqi::getCityCode));
+
+        //���������������������map
+        Map<Date, List<Integer>> hours = DateUtils.getBeforeAndAfterHourDate(nextDay);
+
+        Forecast forecast = new Forecast();
+        Map<String, Object> forecastMap = new HashMap<>();
+        for (Object obj : cityCodes) {
+            Integer cityCode = Integer.parseInt(obj.toString());
+
+            forecast.setCityCode(cityCode);
+
+            //������
+            List<CityWeatherForecast> cityWeatherForecasts = cityForecast.get(cityCode);
+
+            //������������������������������
+            List<CityWeather> cityWeathers = cityHistoryWeather.get(cityCode);
+            //������������������aqi������
+            List<CityAqi> cityAqis = cityHistoryAqi.get(cityCode);
+            Map<Date, List<CityAqi>> cityAqiMap = cityAqis.stream()
+                    .collect(Collectors.groupingBy(CityAqi::getTime));
+
+            Map<Date, List<Map<String, Object>>> cityHistoryMap = new HashMap<>();
+
+            for (Map.Entry<Date, List<Integer>> dateListEntry : hours.entrySet()) {
+                Date date = dateListEntry.getKey();
+                List<Integer> hourList = dateListEntry.getValue();
+                List<Map<String, Object>> hourWeatherData = new ArrayList<>();
+                for (CityWeather cityWeather : cityWeathers) {
+                    Date cityWeatherTime = cityWeather.getTime();
+                    int dataHour = DateUtils.getHour(cityWeatherTime);
+                    if (hourList.contains(dataHour)) {
+                        Map<String, Object> valueMap = JSONObject.parseObject(cityWeather.getValue(), Map.class);
+                        List<CityAqi> o = cityAqiMap.get(cityWeatherTime);
+                        if (!ObjectUtils.isEmpty(o)) {
+                            String value = o.get(0).getValue();
+                            Map<String, Object> aqiMap = JSONObject.parseObject(value, Map.class);
+                            valueMap.put("O3", aqiMap.get("O3"));
+                            hourWeatherData.add(valueMap);
+                        }
+                    }
+                }
+                cityHistoryMap.put(date, hourWeatherData);
+            }
+
+            for (CityWeatherForecast cityWeatherForecast : cityWeatherForecasts) {
+                Date time = cityWeatherForecast.getTime();
+                Map<String, Object> value = JSONObject.parseObject(cityWeatherForecast.getValue(), Map.class);
+                String weather = value.get("text").toString();
+                Integer forecastScore = weatherScore.get(weather);
+                int min;
+                if (forecastScore >= 80) {
+                    min = 80;
+                } else if (forecastScore >= 40) {
+                    min = 40;
+                } else {
+                    min = 0;
+                }
+                //������
+                List<Map<String, Object>> weatherMaps = cityHistoryMap.get(time);
+
+                //������������������������
+                weatherMaps.removeIf(o -> {
+                    Integer historyScore = weatherScore.get(o.get("text").toString());
+                    return historyScore < min;
+                });
+                int size = weatherMaps.size();
+                Double tempAvg = calculateAvg(weatherMaps, "temp");
+                Double o3Avg = calculateAvg(weatherMaps, "O3");
+                Double sum1 = calculateProduct(weatherMaps, "temp", "O3");
+                Double sum2 = calculateProduct(weatherMaps, "temp", "temp");
+                double b = (sum1 - size * tempAvg * o3Avg) / (sum2 - size * tempAvg * tempAvg);
+                double a = o3Avg - b * tempAvg;
+                double tempForecast = Double.parseDouble(value.get("temp").toString());
+                double o3Forecast = b * tempForecast + a;
+
+                forecast.setTime(cityWeatherForecast.getTime());
+
+                if (!Double.isNaN(o3Forecast)) {
+                    forecastMap.put("O3", AmendUtils.sciCal(o3Forecast, 0));
+                    forecast.setValue(JSONObject.toJSONString(forecastMap));
+                    forecastService.save(forecast);
+                }
+            }
+
+        }
+    }
+
+    private Double calculateAvg(List<Map<String, Object>> list, String param) {
+        Supplier<Stream<Map<String, Object>>> supplier = list::stream;
+        DoubleStream doubleStream = supplier.get()
+                .flatMapToDouble(value -> {
+                    String sensorValue = value.get(param).toString();
+                    double paramValue = Double.parseDouble(sensorValue);
+                    return DoubleStream.of(paramValue);
+                });
+        Double result = null;
+        OptionalDouble optionalDouble = doubleStream.average();
+        if (optionalDouble.isPresent()) {
+            result = optionalDouble.getAsDouble();
+        }
+        return result;
+
+    }
+
+    private Double calculateProduct(List<Map<String, Object>> list, String param1, String param2) {
+        Supplier<Stream<Map<String, Object>>> supplier = list::stream;
+        DoubleStream doubleStream = supplier.get()
+                .flatMapToDouble(value -> {
+                    String sensorValue1 = value.get(param1).toString();
+                    String sensorValue2 = value.get(param2).toString();
+                    double paramValue1 = Double.parseDouble(sensorValue1);
+                    double paramValue2 = Double.parseDouble(sensorValue2);
+                    return DoubleStream.of(paramValue1 * paramValue2);
+                });
+        return doubleStream.sum();
+    }
+
+}
diff --git a/screen-job/src/main/java/com/moral/api/task/ForecastTask.java b/screen-job/src/main/java/com/moral/api/task/ForecastTask.java
new file mode 100644
index 0000000..b4a236a
--- /dev/null
+++ b/screen-job/src/main/java/com/moral/api/task/ForecastTask.java
@@ -0,0 +1,27 @@
+package com.moral.api.task;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import com.moral.api.service.ForecastService;
+import com.xxl.job.core.biz.model.ReturnT;
+import com.xxl.job.core.context.XxlJobHelper;
+import com.xxl.job.core.handler.annotation.XxlJob;
+
+@Component
+public class ForecastTask {
+
+    @Autowired
+    private ForecastService forecastService;
+
+    @XxlJob("forecastO3")
+    public ReturnT forecastO3() {
+        try {
+            forecastService.forecastO3();
+        } catch (Exception e) {
+            XxlJobHelper.log(e.getMessage());
+            return new ReturnT(ReturnT.FAIL_CODE, e.getMessage());
+        }
+        return ReturnT.SUCCESS;
+    }
+}
diff --git a/screen-job/src/main/resources/mapper/ForecastMapper.xml b/screen-job/src/main/resources/mapper/ForecastMapper.xml
new file mode 100644
index 0000000..311b519
--- /dev/null
+++ b/screen-job/src/main/resources/mapper/ForecastMapper.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.moral.api.mapper.ForecastMapper">
+
+    <!-- ������������������������ -->
+    <resultMap id="BaseResultMap" type="com.moral.api.entity.Forecast">
+        <result column="city_code" property="cityCode"/>
+        <result column="time" property="time"/>
+        <result column="value" property="value"/>
+    </resultMap>
+
+</mapper>
\ No newline at end of file

--
Gitblit v1.8.0