From fdbef2a3293e536be6b119e4e1e67cc8cc9842f0 Mon Sep 17 00:00:00 2001
From: jinpengyong <jpy123456>
Date: Fri, 08 Apr 2022 08:55:29 +0800
Subject: [PATCH] 空气质量排名报告

---
 screen-api/src/main/resources/application-dev.yml                                     |    2 
 screen-api/src/main/java/com/moral/api/config/properties/SpecialCitiesProperties.java |    8 
 screen-api/src/main/java/com/moral/api/config/properties/AnnouncementProperties.java  |   19 
 screen-job/src/main/java/com/moral/api/service/impl/CityAqiMonthlyServiceImpl.java    |   44 
 screen-api/src/main/java/com/moral/api/service/impl/CityAqiMonthlyServiceImpl.java    | 1601 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 screen-api/src/main/resources/application-announcement.yml                            |    7 
 screen-api/src/main/java/com/moral/api/controller/AirQualityNoticeController.java     |   36 +
 screen-job/src/main/java/com/moral/api/service/impl/CityAqiYearlyServiceImpl.java     |   22 
 screen-common/src/main/java/com/moral/constant/RedisConstants.java                    |    5 
 screen-job/src/main/java/com/moral/api/service/impl/CityAqiServiceImpl.java           |    2 
 screen-api/src/main/java/com/moral/api/service/CityAqiMonthlyService.java             |   30 
 screen-job/src/main/java/com/moral/api/service/impl/CityAqiDailyServiceImpl.java      |    4 
 12 files changed, 1,723 insertions(+), 57 deletions(-)

diff --git a/screen-api/src/main/java/com/moral/api/config/properties/AnnouncementProperties.java b/screen-api/src/main/java/com/moral/api/config/properties/AnnouncementProperties.java
new file mode 100644
index 0000000..5b43efe
--- /dev/null
+++ b/screen-api/src/main/java/com/moral/api/config/properties/AnnouncementProperties.java
@@ -0,0 +1,19 @@
+package com.moral.api.config.properties;
+
+import lombok.Data;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.stereotype.Component;
+
+@Data
+@Component
+@ConfigurationProperties(prefix = "announcement")
+public class AnnouncementProperties {
+
+    private String currentRanking;
+    private String cumulativeRanking;
+    private String contrastCompositeIndex;
+    private String contrastCompositeIndexYearOnYear;
+    private String contrastPm25;
+    private String contrastPm25YearOnYear;
+
+}
diff --git a/screen-api/src/main/java/com/moral/api/config/properties/SpecialCitiesProperties.java b/screen-api/src/main/java/com/moral/api/config/properties/SpecialCitiesProperties.java
index a636026..850f340 100644
--- a/screen-api/src/main/java/com/moral/api/config/properties/SpecialCitiesProperties.java
+++ b/screen-api/src/main/java/com/moral/api/config/properties/SpecialCitiesProperties.java
@@ -42,4 +42,12 @@
         return false;
     }
 
+    public boolean isOneSixEightCities(Integer cityCode){
+        for (SysArea city : oneSixEightCities) {
+            if(city.getAreaCode().equals(cityCode))
+                return true;
+        }
+        return false;
+    }
+
 }
diff --git a/screen-api/src/main/java/com/moral/api/controller/AirQualityNoticeController.java b/screen-api/src/main/java/com/moral/api/controller/AirQualityNoticeController.java
new file mode 100644
index 0000000..20fb97d
--- /dev/null
+++ b/screen-api/src/main/java/com/moral/api/controller/AirQualityNoticeController.java
@@ -0,0 +1,36 @@
+package com.moral.api.controller;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.format.annotation.DateTimeFormat;
+import org.springframework.web.bind.annotation.CrossOrigin;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.Date;
+import java.util.Map;
+
+import com.moral.api.service.CityAqiMonthlyService;
+import com.moral.constant.ResponseCodeEnum;
+import com.moral.constant.ResultMessage;
+
+@RestController
+@RequestMapping("announcement")
+@CrossOrigin(origins = "*", maxAge = 3600)
+public class AirQualityNoticeController {
+
+    @Autowired
+    private CityAqiMonthlyService cityAqiMonthlyService;
+
+    /**
+     * @param regionCode ���������code
+     */
+    @GetMapping("airQualityRanking")
+    public ResultMessage airQualityRankingAnnouncement(Integer regionCode, @DateTimeFormat(pattern = "yyyy-MM") Date time) {
+        if (regionCode == null || time == null) {
+            return ResultMessage.fail(ResponseCodeEnum.PARAMETERS_IS_MISSING.getCode(), ResponseCodeEnum.PARAMETERS_IS_MISSING.getMsg());
+        }
+        Map<String, Object> response = cityAqiMonthlyService.airQualityRankingAnnouncement(regionCode, time);
+        return ResultMessage.ok(response);
+    }
+}
diff --git a/screen-api/src/main/java/com/moral/api/service/CityAqiMonthlyService.java b/screen-api/src/main/java/com/moral/api/service/CityAqiMonthlyService.java
index 2626ffc..c40284c 100644
--- a/screen-api/src/main/java/com/moral/api/service/CityAqiMonthlyService.java
+++ b/screen-api/src/main/java/com/moral/api/service/CityAqiMonthlyService.java
@@ -5,6 +5,7 @@
 
 import java.util.Date;
 import java.util.List;
+import java.util.Map;
 
 /**
  * <p>
@@ -17,20 +18,23 @@
 public interface CityAqiMonthlyService extends IService<CityAqiMonthly> {
 
     /**
-    * @Description: ������������������������������������
-            * @Param: [regionCode, startDate, endDate]
-            * @return: java.util.List<com.moral.api.entity.CityAqiMonthly>
-            * @Author: ���������
-            * @Date: 2022/1/17
-            */
-    List<CityAqiMonthly> getCityAqiMonthByRegionCodeAndTime(Integer regionCode, Date startDate,Date endDate);
+     * @Description: ������������������������������������
+     * @Param: [regionCode, startDate, endDate]
+     * @return: java.util.List<com.moral.api.entity.CityAqiMonthly>
+     * @Author: ���������
+     * @Date: 2022/1/17
+     */
+    List<CityAqiMonthly> getCityAqiMonthByRegionCodeAndTime(Integer regionCode, Date startDate, Date endDate);
 
     /**
-    * @Description: ������������������������������������������
-            * @Param: [regionCode, time]
-            * @return: com.moral.api.entity.CityAqiMonthly
-            * @Author: ���������
-            * @Date: 2022/3/14
-            */
+     * @Description: ������������������������������������������
+     * @Param: [regionCode, time]
+     * @return: com.moral.api.entity.CityAqiMonthly
+     * @Author: ���������
+     * @Date: 2022/3/14
+     */
     CityAqiMonthly getCityAqiMonthByRegionCodeAndTime(Integer regionCode, Date time);
+
+    //���������������������������������������������������
+    Map<String, Object> airQualityRankingAnnouncement(Integer regionCode, Date time);
 }
diff --git a/screen-api/src/main/java/com/moral/api/service/impl/CityAqiMonthlyServiceImpl.java b/screen-api/src/main/java/com/moral/api/service/impl/CityAqiMonthlyServiceImpl.java
index 5806e82..d3822e4 100644
--- a/screen-api/src/main/java/com/moral/api/service/impl/CityAqiMonthlyServiceImpl.java
+++ b/screen-api/src/main/java/com/moral/api/service/impl/CityAqiMonthlyServiceImpl.java
@@ -1,18 +1,40 @@
 package com.moral.api.service.impl;
 
+import com.alibaba.fastjson.JSONObject;
 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.moral.api.config.properties.AnnouncementProperties;
+import com.moral.api.config.properties.SpecialCitiesProperties;
+import com.moral.api.entity.CityAqiDaily;
 import com.moral.api.entity.CityAqiMonthly;
+import com.moral.api.entity.SysArea;
 import com.moral.api.mapper.CityAqiMonthlyMapper;
+import com.moral.api.service.CityAqiDailyService;
 import com.moral.api.service.CityAqiMonthlyService;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.moral.api.service.SysAreaService;
+import com.moral.constant.Constants;
+import com.moral.constant.RedisConstants;
+import com.moral.util.AmendUtils;
+import com.moral.util.ComprehensiveIndexUtils;
 import com.moral.util.DateUtils;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.stereotype.Service;
 
-import java.io.*;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.stereotype.Service;
+import org.springframework.util.ObjectUtils;
+
+import java.text.DecimalFormat;
+import java.time.Duration;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Date;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
 import java.util.List;
+import java.util.Map;
+import java.util.OptionalDouble;
+import java.util.stream.Collectors;
+import java.util.stream.DoubleStream;
 
 /**
  * <p>
@@ -26,23 +48,1584 @@
 public class CityAqiMonthlyServiceImpl extends ServiceImpl<CityAqiMonthlyMapper, CityAqiMonthly> implements CityAqiMonthlyService {
 
     @Autowired
-    CityAqiMonthlyMapper cityAqiMonthlyMapper;
+    private CityAqiMonthlyMapper cityAqiMonthlyMapper;
+
+    @Autowired
+    private SpecialCitiesProperties specialCitiesProperties;
+
+    @Autowired
+    private SysAreaService sysAreaService;
+
+    @Autowired
+    private CityAqiDailyService cityAqiDailyService;
+
+    @Autowired
+    private RedisTemplate redisTemplate;
+
+    @Autowired
+    private AnnouncementProperties announcementProperties;
 
     @Override
     public List<CityAqiMonthly> getCityAqiMonthByRegionCodeAndTime(Integer regionCode, Date startDate, Date endDate) {
         QueryWrapper<CityAqiMonthly> queryWrapper = new QueryWrapper<>();
-        queryWrapper.eq("city_code",regionCode);
-        queryWrapper.between("time",startDate,endDate);
+        queryWrapper.eq("city_code", regionCode);
+        queryWrapper.between("time", startDate, endDate);
         return cityAqiMonthlyMapper.selectList(queryWrapper);
     }
 
     @Override
-    public CityAqiMonthly getCityAqiMonthByRegionCodeAndTime(Integer regionCode, Date time){
+    public CityAqiMonthly getCityAqiMonthByRegionCodeAndTime(Integer regionCode, Date time) {
         time = DateUtils.getFirstDayOfMonth(time);
         QueryWrapper<CityAqiMonthly> queryWrapper = new QueryWrapper<>();
-        queryWrapper.eq("city_code",regionCode);
-        queryWrapper.eq("time",time);
+        queryWrapper.eq("city_code", regionCode);
+        queryWrapper.eq("time", time);
         return cityAqiMonthlyMapper.selectOne(queryWrapper);
     }
 
+    @Override
+    public Map<String, Object> airQualityRankingAnnouncement(Integer regionCode, Date time) {
+        Map<String, Object> result = new LinkedHashMap<>();
+        String month = DateUtils.getMonth(time) + "";
+        int yearMonthOfSelect = Integer.parseInt(DateUtils.dateToDateString(time, DateUtils.yyyyMM_EN));
+        int nowYearMonthOfSelect = Integer.parseInt(DateUtils.getCurDate(DateUtils.yyyyMM_EN));
+
+        if (yearMonthOfSelect > nowYearMonthOfSelect) {
+            return result;
+        }
+
+        //������������������������code���redis���������,������������������������redis
+        result = (Map<String, Object>) redisTemplate.opsForValue().get(RedisConstants.AQI_ANNOUNCEMENT + "_" + regionCode + "_" + DateUtils.dateToDateString(time, DateUtils.yyyyMM_EN));
+        if (result != null) {
+            return result;
+        }
+
+        result = new LinkedHashMap<>();
+        //������xx���������������������������������
+        QueryWrapper<SysArea> sysAreaQueryWrapper = new QueryWrapper<>();
+        sysAreaQueryWrapper.select("area_code", "area_name")
+                .eq("parent_code", regionCode).or()
+                .eq("area_code", regionCode);
+        List<SysArea> sysAreas = sysAreaService.list(sysAreaQueryWrapper);
+        Map<Integer, String> areasMap = new HashMap<>();
+        for (SysArea sysArea : sysAreas) {
+            areasMap.put(sysArea.getAreaCode(), sysArea.getAreaName());
+        }
+        String cityName = areasMap.remove(regionCode);
+        result.put("cityName", cityName);
+        result.put("areaSize", areasMap.size());
+
+        //������������������������
+        //1.������xx���������������������
+        Map<String, Object> currentRankingResult = new HashMap<>();
+
+        //������������xx���������������������
+        QueryWrapper<CityAqiMonthly> cityAqiMonthlyQueryWrapper = new QueryWrapper<>();
+        cityAqiMonthlyQueryWrapper.select("value")
+                .eq("city_code", regionCode)
+                .eq("time", time);
+        CityAqiMonthly cityAqiMonthly = cityAqiMonthlyMapper.selectOne(cityAqiMonthlyQueryWrapper);
+        Double compositeIndex = null;
+        String compositeIndexYearOnYear = null;
+        Integer pm25 = null;
+        String pm25YearOnYear = null;
+        if (cityAqiMonthly != null) {
+            Map<String, Object> aqiMap = JSONObject.parseObject(cityAqiMonthly.getValue(), Map.class);
+
+            //1.������
+            if (aqiMap.get("compositeIndex") != null) {
+                compositeIndex = Double.parseDouble(aqiMap.get("compositeIndex").toString());
+            }
+            currentRankingResult.put("compositeIndex", compositeIndex);
+
+            //2.������������
+            if (aqiMap.get("compositeIndex_yearOnYear") != null) {
+                compositeIndexYearOnYear = aqiMap.get("compositeIndex_yearOnYear").toString();
+            }
+            currentRankingResult.put("compositeIndex_yearOnYear", compositeIndexYearOnYear);
+
+            //3.PM2.5������
+            if (aqiMap.get("PM2_5") != null) {
+                pm25 = Integer.parseInt(aqiMap.get("PM2_5").toString());
+            }
+            currentRankingResult.put("PM2_5", pm25 + "ug/m��");
+
+            //4.PM.5������������
+            if (aqiMap.get("PM2_5_yearOnYear") != null) {
+                pm25YearOnYear = aqiMap.get("PM2_5_yearOnYear").toString();
+            }
+            currentRankingResult.put("PM2_5_yearOnYear", pm25YearOnYear);
+
+            //5.168���������������������������������������������pm2.5,pm2.5������
+            Map<String, Object> ranking168 = getOneSixEightCitiesRanking(regionCode, time);
+            currentRankingResult.putAll(ranking168);
+
+            //6.2+26���������������������������������������������pm2.5,pm2.5������
+            Map<String, Object> ranking28 = getTwentyEightCitiesRanking(regionCode, time);
+            currentRankingResult.putAll(ranking28);
+
+            //7.������������������������������������������������������������pm2.5,pm2.5������
+            Map<String, Object> ranking8Channels = getHeBeiEightCitiesRanking(regionCode, time);
+            currentRankingResult.putAll(ranking8Channels);
+        }
+
+
+        String start = DateUtils.dateToDateString(time, DateUtils.yyyy_MM_dd_CN);
+        //������������������
+        String end = DateUtils.dateToDateString(DateUtils.getLastDayOfMonth(time), DateUtils.yyyy_MM_dd_CN);
+        result.put("currentRanking", aqiQualityRankingResponse(start + "-" + end, cityName, currentRankingResult));
+
+
+        //2.1-x���xx���������������������
+        //���������������codes
+        List<Integer> areaCodes = new ArrayList<>(areasMap.keySet());
+        //���������code
+        areaCodes.add(regionCode);
+        Map<Integer, Map<String, Object>> monthlyCumulativeResult = null;
+        Double cityCompositeIndex = null;
+        String cityCompositeIndexYearOnYear = null;
+        Integer cityPM25 = null;
+        String cityPM25YearOnYear = null;
+        if (!"1".equals(month)) {
+            Map<String, Object> cumulativeRankingResult = new HashMap<>();
+
+            //������xx���������������������������1-xx���������
+            monthlyCumulativeResult = getMonthlyCumulativeResult(time, areaCodes);
+            //xx���������
+
+            if (monthlyCumulativeResult != null) {
+                Map<String, Object> cityMap = monthlyCumulativeResult.remove(regionCode);
+                if (cityMap != null) {
+                    //���������������
+                    cityCompositeIndex = Double.parseDouble(cityMap.get("compositeIndex").toString());
+                    if (cityMap.get("compositeIndex_yearOnYear") != null) {
+                        cityCompositeIndexYearOnYear = cityMap.get("compositeIndex_yearOnYear").toString();
+                    }
+                    //���������PM2.5
+                    cityPM25 = (int) Double.parseDouble(cityMap.get("PM2_5").toString());
+                    if (cityMap.get("PM2_5_yearOnYear") != null) {
+                        cityPM25YearOnYear = cityMap.get("PM2_5_yearOnYear").toString();
+                    }
+                }
+            }
+
+            cumulativeRankingResult.put("compositeIndex", cityCompositeIndex);
+            cumulativeRankingResult.put("compositeIndex_yearOnYear", cityCompositeIndexYearOnYear);
+            cumulativeRankingResult.put("PM2_5", cityPM25);
+            if (cityPM25 != null) {
+                cumulativeRankingResult.put("PM2_5", cityPM25 + "ug/m��");
+            }
+            cumulativeRankingResult.put("PM2_5_yearOnYear", cityPM25YearOnYear);
+
+            //5.168���������������������������������������������pm2.5,pm2.5������
+            Map<String, Object> ranking168 = getOneSixEightCitiesRankingOfCumulative(regionCode, time);
+            cumulativeRankingResult.putAll(ranking168);
+
+            //6.2+26���������������������������������������������pm2.5,pm2.5������
+            Map<String, Object> ranking28 = getTwentyEightCitiesRankingOfCumulative(regionCode, time);
+            cumulativeRankingResult.putAll(ranking28);
+
+            //7.������������������������������������������������������������pm2.5,pm2.5������
+            Map<String, Object> ranking8Channels = getHeBeiEightCitiesRankingOfCumulative(regionCode, time);
+            cumulativeRankingResult.putAll(ranking8Channels);
+            //���������������������1���1���
+            start = DateUtils.dateToDateString(DateUtils.getFirstDayOfYear(time), DateUtils.yyyy_MM_dd_CN);
+            //���������������������������������
+            end = DateUtils.dateToDateString(DateUtils.getLastDayOfMonth(time), DateUtils.yyyy_MM_dd_CN);
+            result.put("cumulativeRanking", aqiQualityRankingResponse(start + "-" + end, cityName, cumulativeRankingResult));
+        }
+
+
+        //���������������������������������������������
+        //������������xx���������������������������
+        cityAqiMonthlyQueryWrapper.clear();
+        areaCodes.remove(regionCode);
+        cityAqiMonthlyQueryWrapper.select("city_code", "value")
+                .eq("time", time)
+                .in("city_code", areaCodes);
+        List<CityAqiMonthly> areaData = cityAqiMonthlyMapper.selectList(cityAqiMonthlyQueryWrapper);
+
+        //���������������������������������������������������������������������������
+        Map<String, Object> areaCurrentMonthResult = getAreaCurrentMonthResult(areasMap, areaData, compositeIndex, compositeIndexYearOnYear, pm25, pm25YearOnYear);
+
+
+        //������������������������������������������
+        result.put("currentCompositeIndexContrast", compositeIndexContrastResponse(month, cityName, areaCodes.size(), areaCurrentMonthResult));
+        //������������������������������������������������
+        result.put("currentCompositeIndexYearOnYearContrast", compositeIndexYearOnYearContrastResponse(month, cityName, areaCodes.size(), areaCurrentMonthResult));
+        //���������������PM2.5���������������������
+        result.put("currentPM25Contrast", pm25tContrastResponse(month, cityName, areaCodes.size(), areaCurrentMonthResult));
+        //���������������Pm2.5���������������������������
+        result.put("currentPM25YearOnYearContrast", pm25YearOnYearContrastResponse(month, cityName, areaCodes.size(), areaCurrentMonthResult));
+
+        //������1-x���������������
+        List<CityAqiMonthly> cumulativeList = null;
+        if (!"1".equals(month)) {
+            cumulativeList = new ArrayList<>();
+            if (monthlyCumulativeResult != null) {
+                for (Map.Entry<Integer, Map<String, Object>> entry : monthlyCumulativeResult.entrySet()) {
+                    CityAqiMonthly cityAqiMonthly1 = new CityAqiMonthly();
+                    cityAqiMonthly1.setCityCode(entry.getKey());
+                    cityAqiMonthly1.setValue(JSONObject.toJSONString(entry.getValue()));
+                    cumulativeList.add(cityAqiMonthly1);
+                }
+            }
+
+            month = "1-" + DateUtils.getMonth(time);
+            //������1-x������������������������������������������������
+            Map<String, Object> monthlyCumulativeListResultMap = getAreaCurrentMonthResult(areasMap, cumulativeList, cityCompositeIndex, cityCompositeIndexYearOnYear, cityPM25, cityPM25YearOnYear);
+            //1-xx������������������������������������������
+            result.put("cumulativeCompositeIndexContrast", compositeIndexContrastResponse(month, cityName, areaCodes.size(), monthlyCumulativeListResultMap));
+            //1-xx������������������������������������������������
+            result.put("cumulativeCompositeIndexYearOnYearContrast", compositeIndexYearOnYearContrastResponse(month, cityName, areaCodes.size(), monthlyCumulativeListResultMap));
+            //1-xx���������������PM2.5���������������������
+            result.put("cumulativePM25Contrast", pm25tContrastResponse(month, cityName, areaCodes.size(), monthlyCumulativeListResultMap));
+            //1-xx���������������Pm2.5���������������������������
+            result.put("cumulativePM25YearOnYearContrast", pm25YearOnYearContrastResponse(month, cityName, areaCodes.size(), monthlyCumulativeListResultMap));
+        }
+
+
+        //������������
+        //1.���������������pm2.5������������������
+        List<Map<String, Object>> airQualityRankingOfCurrentReport = airQualityRankingOfCurrentReport(areaData, areasMap);
+        result.put("currentAirQualityRankingReport", airQualityRankingOfCurrentReport);
+
+        //2.1-x���������������������������������pm2.5������������
+        if (!"1".equals(month)) {
+            List<Map<String, Object>> cumulativeAirQualityRankingReport = cumulativeAirQualityRankingReport(cumulativeList, areasMap);
+            result.put("cumulativeAirQualityRankingReport", cumulativeAirQualityRankingReport);
+        }
+
+
+        //3.������������������PM10,SO2,NO2,CO,O3���������������
+        List<Map<String, Object>> currentFiveSensorsContrastReport = currentFiveSensorsContrastReport(time, areaCodes, areaData, areasMap);
+        result.put("currentFiveSensorsContrastReport", currentFiveSensorsContrastReport);
+
+
+        //4.1-x������������������PM10,SO2,NO2,CO,O3���������������
+        if (!"1".equals(month)) {
+            List<Map<String, Object>> cumulativeFiveSensorsContrastReport = cumulativeFiveSensorsContrastReport(cumulativeList, areasMap);
+            result.put("cumulativeFiveSensorsContrastReport", cumulativeFiveSensorsContrastReport);
+
+        }
+
+        //������redis
+        redisTemplate.opsForValue().set(RedisConstants.AQI_ANNOUNCEMENT + "_" + regionCode + "_" + DateUtils.dateToDateString(time, DateUtils.yyyyMM_EN), result, Duration.ofDays(30));
+        return result;
+    }
+
+    /**
+     * @param startEnd ������
+     * @param cityName ���������������
+     * @param result   ������������map
+     * @description ������������������������
+     */
+    private String aqiQualityRankingResponse(String startEnd, String cityName, Map<String, Object> result) {
+        Object compositeIndex = result.get("compositeIndex");
+        Object compositeIndex_rank_168 = result.get("compositeIndex_rank_168");
+        Object compositeIndex_rank_28 = result.get("compositeIndex_rank_28");
+        Object compositeIndex_rank_8Channels = result.get("compositeIndex_rank_8Channels");
+
+        Object compositeIndexYearOnYear = result.get("compositeIndex_yearOnYear");
+        Object compositeIndexYearOnYear_rank_168 = result.get("compositeIndex_yearOnYear_rank_168");
+        Object compositeIndexYearOnYear_rank_28 = result.get("compositeIndex_yearOnYear_rank_28");
+        Object compositeIndexYearOnYear_rank_8Channels = result.get("compositeIndex_yearOnYear_rank_8Channels");
+
+        //������������
+        StringBuilder compositeIndexBuilder = new StringBuilder();
+        if (compositeIndex_rank_168 != null) {
+            compositeIndexBuilder.append("���168���������������" + compositeIndex_rank_168 + "���");
+        }
+        if (compositeIndex_rank_28 != null) {
+            compositeIndexBuilder.append("���'2+26'���������������" + compositeIndex_rank_28 + "���");
+        }
+        if (compositeIndex_rank_8Channels != null) {
+            compositeIndexBuilder.append("���������������������������������" + compositeIndex_rank_8Channels + "���");
+        }
+        //������������������
+        StringBuilder compositeIndexYearOnYearBuilder = new StringBuilder();
+        if (compositeIndexYearOnYear_rank_168 != null) {
+            compositeIndexYearOnYearBuilder.append("���168���������������" + compositeIndexYearOnYear_rank_168 + "���");
+        }
+        if (compositeIndexYearOnYear_rank_28 != null) {
+            compositeIndexYearOnYearBuilder.append("���'2+26'���������������" + compositeIndexYearOnYear_rank_28 + "���");
+        }
+        if (compositeIndexYearOnYear_rank_8Channels != null) {
+            compositeIndexYearOnYearBuilder.append("���������������������������������" + compositeIndexYearOnYear_rank_8Channels + "���");
+        }
+
+
+        Object pm2_5 = result.get("PM2_5");
+        Object pm2_5_rank_168 = result.get("PM2_5_rank_168");
+        Object pm2_5_rank_28 = result.get("PM2_5_rank_28");
+        Object pm2_5_rank_8Channels = result.get("PM2_5_rank_8Channels");
+
+        Object pm2_5YearOnYear = result.get("PM2_5_yearOnYear");
+        Object pm2_5YearOnYear_rank_168 = result.get("PM2_5_yearOnYear_rank_168");
+        Object pm2_5YearOnYear_rank_28 = result.get("PM2_5_yearOnYear_rank_28");
+        Object pm2_5YearOnYear_rank_8Channels = result.get("PM2_5_yearOnYear_rank_8Channels");
+
+        //PM2.5������
+        StringBuilder pm25Builder = new StringBuilder();
+        if (pm2_5_rank_168 != null) {
+            pm25Builder.append("���168���������������" + pm2_5_rank_168 + "���");
+        }
+        if (pm2_5_rank_28 != null) {
+            pm25Builder.append("���'2+26'���������������" + pm2_5_rank_28 + "���");
+        }
+        if (pm2_5_rank_8Channels != null) {
+            pm25Builder.append("���������������������������������" + pm2_5_rank_8Channels + "���");
+        }
+        //PM2.5������������
+        StringBuilder pm25YearOnYearBuilder = new StringBuilder();
+        if (pm2_5YearOnYear_rank_168 != null) {
+            pm25YearOnYearBuilder.append("���168���������������" + pm2_5YearOnYear_rank_168 + "���");
+        }
+        if (pm2_5YearOnYear_rank_28 != null) {
+            pm25YearOnYearBuilder.append("���'2+26'���������������" + pm2_5YearOnYear_rank_28 + "���");
+        }
+        if (pm2_5YearOnYear_rank_8Channels != null) {
+            pm25YearOnYearBuilder.append("���������������������������������" + pm2_5YearOnYear_rank_8Channels + "���");
+        }
+
+        String currentRanking = announcementProperties.getCurrentRanking();
+        currentRanking = currentRanking.replace("{start-end}", startEnd)
+                .replace("{cityName}", cityName)
+                .replace("{compositeIndex}", compositeIndex == null ? "_" : compositeIndex.toString())
+                .replace("{compositeIndexRanking}", compositeIndexBuilder.toString().equals("") ? "_" : compositeIndexBuilder.toString())
+                .replace("{compositeIndexYearOnYearRanking}", compositeIndexYearOnYearBuilder.toString().equals("") ? "_" : compositeIndexYearOnYearBuilder.toString())
+                .replace("{PM2_5}", pm2_5 == null ? "_" : pm2_5.toString())
+                .replace("{PM2_5Ranking}", pm25Builder.toString().equals("") ? "_" : pm25Builder.toString())
+                .replace("{PM2_5YearOnYearRanking}", pm25YearOnYearBuilder.toString().equals("") ? "_" : pm25YearOnYearBuilder.toString());
+        String strCompositeIndexYearOnYear = "_";
+        if (compositeIndexYearOnYear != null) {
+            if (compositeIndexYearOnYear.toString().startsWith("-")) {
+                strCompositeIndexYearOnYear = compositeIndexYearOnYear.toString().replace("-", "������");
+            } else if (Double.parseDouble(compositeIndexYearOnYear.toString().replace("%", "")) == 0d) {
+                strCompositeIndexYearOnYear = compositeIndexYearOnYear.toString();
+            } else {
+                strCompositeIndexYearOnYear = "������" + compositeIndexYearOnYear;
+            }
+        }
+        String strPM25YearOnYear = "_";
+        if (pm2_5YearOnYear != null) {
+            if (pm2_5YearOnYear.toString().startsWith("-")) {
+                strPM25YearOnYear = pm2_5YearOnYear.toString().replace("-", "������");
+            } else {
+                strPM25YearOnYear = "������" + pm2_5YearOnYear;
+            }
+        }
+        currentRanking = currentRanking.replace("{compositeIndexYearOnYear}", strCompositeIndexYearOnYear).replace("{PM2_5YearOnYear}", strPM25YearOnYear);
+
+        return currentRanking;
+    }
+
+    /**
+     * @param month    ������
+     * @param cityName ���������������
+     * @param result   ������������map
+     * @description ������������������������������������������
+     */
+    private String compositeIndexContrastResponse(String month, String cityName, Integer areaSize, Map<String, Object> result) {
+        Object compositeIndex = result.get("compositeIndex");
+        List<String> compositeIndexLowerList = (List<String>) result.get("compositeIndexLower");
+        List<String> compositeIndexHigherList = (List<String>) result.get("compositeIndexHigher");
+        List<String> compositeIndexEqualList = (List<String>) result.get("compositeIndexEqual");
+
+
+        //������������
+        String compositeIndexLower = compositeIndexLowerList.stream()
+                .map(String::valueOf).collect(Collectors.joining(","));
+        String compositeIndexHigher = compositeIndexHigherList.stream()
+                .map(String::valueOf).collect(Collectors.joining(","));
+        String compositeIndexEqual = compositeIndexEqualList.stream()
+                .map(String::valueOf).collect(Collectors.joining(","));
+
+        StringBuilder builder = new StringBuilder();
+        if (compositeIndexLowerList.size() != 0) {
+            builder.append("���" + compositeIndexLowerList.size() + "������������������������������������������������������������������������������").append(compositeIndexLower + "���");
+        }
+        if (compositeIndexEqualList.size() != 0) {
+            builder.append(compositeIndexEqual + "" + compositeIndexEqualList.size() + "������������������������������������������������������������������");
+        }
+        if (compositeIndexHigherList.size() != 0) {
+            builder.append("���" + compositeIndexHigherList.size() + "������������������������������������������������������������������������������").append(compositeIndexHigher);
+        }
+
+
+        String currentContrastCompositeIndex = announcementProperties.getContrastCompositeIndex();
+        currentContrastCompositeIndex = currentContrastCompositeIndex.replace("{month}", month)
+                .replace("{cityName}", cityName)
+                .replace("{compositeIndex}", compositeIndex == null ? "_" : compositeIndex.toString())
+                .replace("{areaSize}", areaSize + "")
+                .replace("{details}", builder.toString().equals("") ? "_" : builder.toString());
+        return currentContrastCompositeIndex;
+    }
+
+    /**
+     * @param month    ������
+     * @param cityName ���������������
+     * @param result   ������������map
+     * @description ������������������������
+     */
+    private String compositeIndexYearOnYearContrastResponse(String month, String cityName, Integer areaSize, Map<String, Object> result) {
+        Object compositeIndexYearOnYear = result.get("compositeIndexYearOnYear");
+        List<String> compositeIndexYearOnYearLowerList = (List<String>) result.get("compositeIndexYearOnYearLower");
+        List<String> compositeIndexYearOnYearHigherList = (List<String>) result.get("compositeIndexYearOnYearHigher");
+        List<String> compositeIndexYearOnYearEqualList = (List<String>) result.get("compositeIndexYearOnYearEqual");
+        List<String> compositeIndexYearOnYearHigherOfPositiveList = (List<String>) result.get("compositeIndexYearOnYearHigherOfPositive");
+        List<String> compositeIndexYearOnYearHigherOfNegativeList = (List<String>) result.get("compositeIndexYearOnYearHigherOfNegative");
+        List<String> compositeIndexYearOnYearHigherOfZeroList = (List<String>) result.get("compositeIndexYearOnYearHigherOfZero");
+
+        //������������������
+        String compositeIndexYearOnYearLower = compositeIndexYearOnYearLowerList.stream()
+                .map(String::valueOf).collect(Collectors.joining(","));
+        String compositeIndexYearOnYearEqual = compositeIndexYearOnYearEqualList.stream()
+                .map(String::valueOf).collect(Collectors.joining(","));
+        String compositeIndexYearOnYearHigherOfPositive = compositeIndexYearOnYearHigherOfPositiveList.stream()
+                .map(String::valueOf).collect(Collectors.joining(","));
+        String compositeIndexYearOnYearHigherOfNegative = compositeIndexYearOnYearHigherOfNegativeList.stream()
+                .map(String::valueOf).collect(Collectors.joining(","));
+
+
+        StringBuilder builder = new StringBuilder();
+        if (compositeIndexYearOnYearLowerList.size() != 0) {
+            builder.append("���" + compositeIndexYearOnYearLowerList.size() + "������������������������������������������������������������������������������������������������")
+                    .append(compositeIndexYearOnYearLower + "���");
+        }
+        if (compositeIndexYearOnYearEqualList.size() != 0) {
+            builder.append(compositeIndexYearOnYearEqual + "" + compositeIndexYearOnYearEqualList.size() + "���������������������������������������������������������������������������������������");
+        }
+        if (compositeIndexYearOnYearHigherList.size() != 0) {
+            builder.append("���" + compositeIndexYearOnYearHigherList.size() + "������������������������������������������������������������������������������������");
+            if (compositeIndexYearOnYearHigherOfPositiveList.size() != 0) {
+                builder.append("���������" + compositeIndexYearOnYearHigherOfPositiveList.size() + "������������������������������������������������������������������������" + compositeIndexYearOnYearHigherOfPositive + "���");
+            }
+            if (compositeIndexYearOnYearHigherOfZeroList.size() != 0) {
+                builder.append("���������" + compositeIndexYearOnYearHigherOfZeroList.size() + "������������������������������������������������������������");
+            }
+            if (compositeIndexYearOnYearHigherOfNegativeList.size() != 0) {
+                builder.append("���������" + compositeIndexYearOnYearHigherOfNegativeList.size() + "������������������������������������������������������������������������" + compositeIndexYearOnYearHigherOfNegative);
+            }
+        }
+
+
+        String strCompositeIndexYearOnYear = "_";
+        if (compositeIndexYearOnYear != null) {
+            if (compositeIndexYearOnYear.toString().startsWith("-")) {
+                strCompositeIndexYearOnYear = compositeIndexYearOnYear.toString().replace("-", "������");
+            } else if (Double.parseDouble(compositeIndexYearOnYear.toString().replace("%", "")) == 0d) {
+                strCompositeIndexYearOnYear = compositeIndexYearOnYear.toString();
+            } else {
+                strCompositeIndexYearOnYear = "������" + compositeIndexYearOnYear;
+            }
+        }
+        String currentContrastCompositeIndexYearOnYear = announcementProperties.getContrastCompositeIndexYearOnYear();
+        currentContrastCompositeIndexYearOnYear = currentContrastCompositeIndexYearOnYear.replace("{month}", month)
+                .replace("{cityName}", cityName)
+                .replace("{compositeIndexYearOnYear}", strCompositeIndexYearOnYear)
+                .replace("{areaSize}", areaSize + "")
+                .replace("{details}", builder.toString().equals("") ? "_" : builder.toString());
+
+
+        return currentContrastCompositeIndexYearOnYear;
+    }
+
+    /**
+     * @param month    ������
+     * @param cityName ���������������
+     * @param result   ������������map
+     * @description PM2.5������
+     */
+    private String pm25tContrastResponse(String month, String cityName, Integer areaSize, Map<String, Object> result) {
+        Object pm25 = result.get("PM2_5");
+        List<String> pm25LowerList = (List<String>) result.get("PM2_5Lower");
+        List<String> pm25HigherList = (List<String>) result.get("PM2_5Higher");
+        List<String> pm25EqualList = (List<String>) result.get("PM2_5Equal");
+
+
+        //PM2.5������
+        String pm25Lower = pm25LowerList.stream()
+                .map(String::valueOf).collect(Collectors.joining(","));
+        String pm25Higher = pm25HigherList.stream()
+                .map(String::valueOf).collect(Collectors.joining(","));
+        String pm25Equal = pm25EqualList.stream()
+                .map(String::valueOf).collect(Collectors.joining(","));
+
+        StringBuilder builder = new StringBuilder();
+        if (pm25LowerList.size() != 0) {
+            builder.append("���" + pm25LowerList.size() + "���������������������PM2.5���������������������������������������������").append(pm25Lower);
+        }
+        if (pm25EqualList.size() != 0) {
+            builder.append(pm25Equal + "" + pm25EqualList.size() + "���������������������PM2.5������������������������������������");
+        }
+        if (pm25HigherList.size() != 0) {
+            builder.append("���" + pm25HigherList.size() + "���������������������PM2.5���������������������������������������������").append(pm25Higher);
+        }
+
+        String propertiesCurrentContrastPm25 = announcementProperties.getContrastPm25();
+        propertiesCurrentContrastPm25 = propertiesCurrentContrastPm25.replace("{month}", month)
+                .replace("{cityName}", cityName)
+                .replace("{PM2_5}", pm25 == null ? "_" : pm25.toString())
+                .replace("{areaSize}", areaSize + "")
+                .replace("{details}", builder.toString().equals("") ? "_" : builder.toString());
+        return propertiesCurrentContrastPm25;
+    }
+
+
+    /**
+     * @param month    ������
+     * @param cityName ���������������
+     * @param result   ������������map
+     * @description PM2.5������������������������
+     */
+    private String pm25YearOnYearContrastResponse(String month, String cityName, Integer areaSize, Map<String, Object> result) {
+        Object pm25YearOnYear = result.get("PM2_5YearOnYear");
+
+        List<String> pm25YearOnYearLowerList = (List<String>) result.get("PM2_5YearOnYearLower");
+        List<String> pm25YearOnYearHigherList = (List<String>) result.get("PM2_5YearOnYearHigher");
+        List<String> pm25YearOnYearEqualList = (List<String>) result.get("PM2_5YearOnYearEqual");
+        List<String> pm25YearOnYearLowerOfPositiveList = (List<String>) result.get("pm25YearOnYearLowerOfPositive");
+        List<String> pm25YearOnYearLowerOfNegativeList = (List<String>) result.get("pm25YearOnYearLowerOfNegative");
+        List<String> pm25YearOnYearLowerOfZeroList = (List<String>) result.get("pm25YearOnYearLowerOfZero");
+
+
+        //PM2.5������������
+        String pm25YearOnYearHigher = pm25YearOnYearHigherList.stream()
+                .map(String::valueOf).collect(Collectors.joining(","));
+        String pm25YearOnYearEqual = pm25YearOnYearEqualList.stream()
+                .map(String::valueOf).collect(Collectors.joining(","));
+        String pm25YearOnYearLowerOfPositive = pm25YearOnYearLowerOfPositiveList.stream()
+                .map(String::valueOf).collect(Collectors.joining(","));
+        String pm25YearOnYearLowerOfNegative = pm25YearOnYearLowerOfNegativeList.stream()
+                .map(String::valueOf).collect(Collectors.joining(","));
+
+        StringBuilder builder = new StringBuilder();
+        if (pm25YearOnYearLowerList.size() != 0) {
+            builder.append("���" + pm25YearOnYearLowerList.size() + "���������������������PM2.5���������������������������������������������������");
+            if (pm25YearOnYearLowerOfNegativeList.size() != 0) {
+                builder.append("���������" + pm25YearOnYearLowerOfNegativeList.size() + "���������������������PM2.5���������������������������������������")
+                        .append(pm25YearOnYearLowerOfNegative);
+            }
+            if (pm25YearOnYearLowerOfZeroList.size() != 0) {
+                builder.append("���" + pm25YearOnYearLowerOfZeroList.size() + "���������������������PM2.5���������������������������");
+            }
+            if (pm25YearOnYearLowerOfPositiveList.size() != 0) {
+                builder.append("���" + pm25YearOnYearLowerOfPositiveList.size() + "���������������������PM2.5���������������������������������������").append(pm25YearOnYearLowerOfPositive);
+            }
+        }
+        if (pm25YearOnYearEqualList.size() != 0) {
+            builder.append(pm25YearOnYearEqual + "" + pm25YearOnYearEqualList.size() + "���������������������PM2.5������������������������������������������������������");
+        }
+        if (pm25YearOnYearHigherList.size() != 0) {
+            builder.append("���" + pm25YearOnYearHigherList.size() + "���������������������PM2.5���������������������������������������������������������������").append(pm25YearOnYearHigher);
+        }
+
+
+        String strPM25IndexYearOnYear = "_";
+        if (pm25YearOnYear != null) {
+            if (pm25YearOnYear.toString().startsWith("-")) {
+                strPM25IndexYearOnYear = pm25YearOnYear.toString().replace("-", "������");
+            } else if (Double.parseDouble(pm25YearOnYear.toString().replace("%", "")) == 0d) {
+                strPM25IndexYearOnYear = pm25YearOnYear.toString();
+            } else {
+                strPM25IndexYearOnYear = "������" + pm25YearOnYear;
+            }
+        }
+
+        String currentContrastPm25YearOnYear = announcementProperties.getContrastPm25YearOnYear();
+        currentContrastPm25YearOnYear = currentContrastPm25YearOnYear.replace("{month}", month)
+                .replace("{cityName}", cityName)
+                .replace("{PM2_5YearOnYear}", strPM25IndexYearOnYear)
+                .replace("{areaSize}", areaSize + "")
+                .replace("{details}", builder.toString().equals("") ? "_" : builder.toString());
+        return currentContrastPm25YearOnYear;
+    }
+
+    /**
+     * @param data       ������������
+     * @param regionCode ������������������
+     * @param rankField  ������������
+     * @description ���������������������������
+     */
+    private Integer ranking(List<CityAqiMonthly> data, Integer regionCode, String rankField) {
+        Integer rank = null;
+        data = data.stream().sorted((o1, o2) -> {
+            Map<String, Object> map1 = JSONObject.parseObject(o1.getValue(), Map.class);
+            Map<String, Object> map2 = JSONObject.parseObject(o2.getValue(), Map.class);
+            Object b1 = map1.get(rankField);
+            Object b2 = map2.get(rankField);
+
+            if (b1 != null && b2 != null) {
+                double aDouble1 = Double.parseDouble(b1.toString().replace("%", ""));
+                double aDouble2 = Double.parseDouble(b2.toString().replace("%", ""));
+                return aDouble1 > aDouble2 ? 1 : -1;
+            } else if (b1 == null && b2 == null) {
+                return 0;
+            } else if (b1 == null) {
+                return -1;
+            } else {
+                return 1;
+            }
+        }).collect(Collectors.toList());
+        for (int i = 0; i < data.size(); i++) {
+            Integer cityCode = data.get(i).getCityCode();
+            if (cityCode.equals(regionCode)) {
+                Map<String, Object> map = JSONObject.parseObject(data.get(i).getValue(), Map.class);
+                if (map.get(rankField) != null) {
+                    rank = i + 1;
+                }
+                break;
+            }
+        }
+        return rank;
+    }
+
+
+    /**
+     * @param regionCode ������������
+     * @param time       ������
+     * @description ���������168���������������������
+     */
+    private Map<String, Object> getOneSixEightCitiesRanking(Integer regionCode, Date time) {
+        Map<String, Object> result = new HashMap<>();
+
+        if (specialCitiesProperties.isOneSixEightCities(regionCode)) {
+
+            QueryWrapper<CityAqiMonthly> queryWrapper = new QueryWrapper<>();
+            List<SysArea> oneSixEightCities = specialCitiesProperties.getOneSixEightCities();
+            List<Integer> oneSixEightCitiesCodes = oneSixEightCities.stream()
+                    .map(SysArea::getAreaCode)
+                    .collect(Collectors.toList());
+            //������168������������
+            queryWrapper.clear();
+            queryWrapper.select("city_code", "value")
+                    .eq("time", time)
+                    .in("city_code", oneSixEightCitiesCodes);
+            List<CityAqiMonthly> monthlyData168 = cityAqiMonthlyMapper.selectList(queryWrapper);
+
+            //������168������������
+            Integer compositeIndexRank = ranking(monthlyData168, regionCode, "compositeIndex");
+            //������������168������������
+            Integer compositeIndexYearOnYearRank = ranking(monthlyData168, regionCode, "compositeIndex_yearOnYear");
+
+            //PM2.5������168������������
+            Integer pm25Rank = ranking(monthlyData168, regionCode, "PM2_5");
+            //PM2.5������������168������������
+            Integer pm25YearOnYearRank = ranking(monthlyData168, regionCode, "PM2_5_yearOnYear");
+
+
+            //���������������
+            result.put("compositeIndex_rank_168", compositeIndexRank == null ? "_" : compositeIndexRank);
+            result.put("compositeIndex_yearOnYear_rank_168", compositeIndexYearOnYearRank == null ? "_" : compositeIndexYearOnYearRank);
+            result.put("PM2_5_rank_168", pm25Rank == null ? "_" : pm25Rank);
+            result.put("PM2_5_yearOnYear_rank_168", pm25YearOnYearRank == null ? "_" : pm25YearOnYearRank);
+        }
+        return result;
+    }
+
+
+    //1-x���168������������
+    private Map<String, Object> getOneSixEightCitiesRankingOfCumulative(Integer regionCode, Date time) {
+        Map<String, Object> result = new HashMap<>();
+
+        if (specialCitiesProperties.isOneSixEightCities(regionCode)) {
+            List<SysArea> oneSixEightCities = specialCitiesProperties.getOneSixEightCities();
+            List<Integer> oneSixEightCitiesCodes = oneSixEightCities.stream()
+                    .map(SysArea::getAreaCode)
+                    .collect(Collectors.toList());
+
+            //168������1-x���������
+            Map<Integer, Map<String, Object>> cumulative168Result = getMonthlyCumulativeResult(time, oneSixEightCitiesCodes);
+            List<CityAqiMonthly> cumulativeData168 = new ArrayList<>();
+            for (Map.Entry<Integer, Map<String, Object>> entry : cumulative168Result.entrySet()) {
+                CityAqiMonthly cityAqiMonthly = new CityAqiMonthly();
+                Integer cityCode = entry.getKey();
+                Map<String, Object> value = entry.getValue();
+                cityAqiMonthly.setCityCode(cityCode);
+                cityAqiMonthly.setValue(JSONObject.toJSONString(value));
+                cumulativeData168.add(cityAqiMonthly);
+            }
+
+
+            //������168������������
+            Integer compositeIndexRank = ranking(cumulativeData168, regionCode, "compositeIndex");
+            //������������168������������
+            Integer compositeIndexYearOnYearRank = ranking(cumulativeData168, regionCode, "compositeIndex_yearOnYear");
+
+            //PM2.5������168������������
+            Integer pm25Rank = ranking(cumulativeData168, regionCode, "PM2_5");
+            //PM2.5������������168������������
+            Integer pm25YearOnYearRank = ranking(cumulativeData168, regionCode, "PM2_5_yearOnYear");
+
+
+            //���������������
+            result.put("compositeIndex_rank_168", compositeIndexRank == null ? "_" : compositeIndexRank);
+            result.put("compositeIndex_yearOnYear_rank_168", compositeIndexYearOnYearRank == null ? "_" : compositeIndexYearOnYearRank);
+            result.put("PM2_5_rank_168", pm25Rank == null ? "_" : pm25Rank);
+            result.put("PM2_5_yearOnYear_rank_168", pm25YearOnYearRank == null ? "_" : pm25YearOnYearRank);
+        }
+        return result;
+    }
+
+    //1-x���2+26������������
+    private Map<String, Object> getTwentyEightCitiesRankingOfCumulative(Integer regionCode, Date time) {
+        Map<String, Object> result = new HashMap<>();
+
+        if (specialCitiesProperties.isTwentyEightCities(regionCode)) {
+            List<SysArea> twentyCities = specialCitiesProperties.getTwentyEightCities();
+            List<Integer> twentyCitiesCodes = twentyCities.stream()
+                    .map(SysArea::getAreaCode)
+                    .collect(Collectors.toList());
+
+            //168������1-x���������
+            Map<Integer, Map<String, Object>> cumulative168Result = getMonthlyCumulativeResult(time, twentyCitiesCodes);
+            List<CityAqiMonthly> cumulativeData168 = new ArrayList<>();
+            for (Map.Entry<Integer, Map<String, Object>> entry : cumulative168Result.entrySet()) {
+                CityAqiMonthly cityAqiMonthly = new CityAqiMonthly();
+                Integer cityCode = entry.getKey();
+                Map<String, Object> value = entry.getValue();
+                cityAqiMonthly.setCityCode(cityCode);
+                cityAqiMonthly.setValue(JSONObject.toJSONString(value));
+                cumulativeData168.add(cityAqiMonthly);
+            }
+
+
+            //������168������������
+            Integer compositeIndexRank = ranking(cumulativeData168, regionCode, "compositeIndex");
+            //������������168������������
+            Integer compositeIndexYearOnYearRank = ranking(cumulativeData168, regionCode, "compositeIndex_yearOnYear");
+
+            //PM2.5������168������������
+            Integer pm25Rank = ranking(cumulativeData168, regionCode, "PM2_5");
+            //PM2.5������������168������������
+            Integer pm25YearOnYearRank = ranking(cumulativeData168, regionCode, "PM2_5_yearOnYear");
+
+
+            //���������������
+            result.put("compositeIndex_rank_28", compositeIndexRank == null ? "_" : compositeIndexRank);
+            result.put("compositeIndex_yearOnYear_rank_28", compositeIndexYearOnYearRank == null ? "_" : compositeIndexYearOnYearRank);
+            result.put("PM2_5_rank_28", pm25Rank == null ? "_" : pm25Rank);
+            result.put("PM2_5_yearOnYear_rank_28", pm25YearOnYearRank == null ? "_" : pm25YearOnYearRank);
+        }
+        return result;
+    }
+
+    //1-x������������������������������
+    private Map<String, Object> getHeBeiEightCitiesRankingOfCumulative(Integer regionCode, Date time) {
+        Map<String, Object> result = new HashMap<>();
+
+        if (specialCitiesProperties.isHeBeiEightCities(regionCode)) {
+            List<SysArea> heBeiEightCities = specialCitiesProperties.getHeBeiEightCities();
+            List<Integer> heBeiEightCitiesCodes = heBeiEightCities.stream()
+                    .map(SysArea::getAreaCode)
+                    .collect(Collectors.toList());
+
+            //168������1-x���������
+            Map<Integer, Map<String, Object>> cumulative168Result = getMonthlyCumulativeResult(time, heBeiEightCitiesCodes);
+            List<CityAqiMonthly> cumulativeData168 = new ArrayList<>();
+            for (Map.Entry<Integer, Map<String, Object>> entry : cumulative168Result.entrySet()) {
+                CityAqiMonthly cityAqiMonthly = new CityAqiMonthly();
+                Integer cityCode = entry.getKey();
+                Map<String, Object> value = entry.getValue();
+                cityAqiMonthly.setCityCode(cityCode);
+                cityAqiMonthly.setValue(JSONObject.toJSONString(value));
+                cumulativeData168.add(cityAqiMonthly);
+            }
+
+
+            //������168������������
+            Integer compositeIndexRank = ranking(cumulativeData168, regionCode, "compositeIndex");
+            //������������168������������
+            Integer compositeIndexYearOnYearRank = ranking(cumulativeData168, regionCode, "compositeIndex_yearOnYear");
+
+            //PM2.5������168������������
+            Integer pm25Rank = ranking(cumulativeData168, regionCode, "PM2_5");
+            //PM2.5������������168������������
+            Integer pm25YearOnYearRank = ranking(cumulativeData168, regionCode, "PM2_5_yearOnYear");
+
+
+            //���������������
+            result.put("compositeIndex_rank_8channels", compositeIndexRank == null ? "_" : compositeIndexRank);
+            result.put("compositeIndex_YearOnYear_rank_8channels", compositeIndexYearOnYearRank == null ? "_" : compositeIndexYearOnYearRank);
+            result.put("PM2_5_rank_8channels", pm25Rank == null ? "_" : pm25Rank);
+            result.put("PM2_5_yearOnYear_rank_8channels", pm25YearOnYearRank == null ? "_" : pm25YearOnYearRank);
+        }
+        return result;
+    }
+
+
+    /**
+     * @param regionCode ������������
+     * @param time       ������
+     * @description ���������2+26���������������������
+     */
+    private Map<String, Object> getTwentyEightCitiesRanking(Integer regionCode, Date time) {
+        Map<String, Object> result = new HashMap<>();
+
+        if (specialCitiesProperties.isTwentyEightCities(regionCode)) {
+
+            QueryWrapper<CityAqiMonthly> queryWrapper = new QueryWrapper<>();
+            List<SysArea> twentyEightCities = specialCitiesProperties.getTwentyEightCities();
+            List<Integer> twentyEightCitiesCodes = twentyEightCities.stream()
+                    .map(SysArea::getAreaCode)
+                    .collect(Collectors.toList());
+            //������2+26������������
+            queryWrapper.select("city_code", "value")
+                    .eq("time", time)
+                    .in("city_code", twentyEightCitiesCodes);
+            List<CityAqiMonthly> monthlyData28 = cityAqiMonthlyMapper.selectList(queryWrapper);
+
+            //������2+26������������
+            Integer compositeIndexRank = ranking(monthlyData28, regionCode, "compositeIndex");
+            //������������2+26������������
+            Integer compositeIndexYearOnYearRank = ranking(monthlyData28, regionCode, "compositeIndex_yearOnYear");
+
+            //PM2.5������2+26������������
+            Integer pm25Rank = ranking(monthlyData28, regionCode, "PM2_5");
+            //PM2.5������������2+26������������
+            Integer pm25YearOnYearRank = ranking(monthlyData28, regionCode, "PM2_5_yearOnYear");
+
+
+            //���������������
+            result.put("compositeIndex_rank_28", compositeIndexRank == null ? "_" : compositeIndexRank);
+            result.put("compositeIndex_yearOnYear_rank_28", compositeIndexYearOnYearRank == null ? "_" : compositeIndexYearOnYearRank);
+            result.put("PM2_5_rank_28", pm25Rank == null ? "_" : pm25Rank);
+            result.put("PM2_5_yearOnYear_rank_28", pm25YearOnYearRank == null ? "_" : pm25YearOnYearRank);
+        }
+        return result;
+    }
+
+    /**
+     * @param regionCode ������������
+     * @param time       ������
+     * @description ���������������������������������������������
+     */
+    private Map<String, Object> getHeBeiEightCitiesRanking(Integer regionCode, Date time) {
+        Map<String, Object> result = new HashMap<>();
+
+        if (specialCitiesProperties.isHeBeiEightCities(regionCode)) {
+            QueryWrapper<CityAqiMonthly> queryWrapper = new QueryWrapper<>();
+            List<SysArea> heBeiEightCities = specialCitiesProperties.getHeBeiEightCities();
+            List<Integer> heBeiEightCitiesCodes = heBeiEightCities.stream()
+                    .map(SysArea::getAreaCode)
+                    .collect(Collectors.toList());
+            //������2+26������������
+            queryWrapper.select("city_code", "value")
+                    .eq("time", time)
+                    .in("city_code", heBeiEightCitiesCodes);
+            List<CityAqiMonthly> monthlyData8Channels = cityAqiMonthlyMapper.selectList(queryWrapper);
+
+            //���������������������������
+            Integer compositeIndexRank = ranking(monthlyData8Channels, regionCode, "compositeIndex");
+            //���������������������������������
+            Integer compositeIndexYearOnYearRank = ranking(monthlyData8Channels, regionCode, "compositeIndex_yearOnYear");
+
+            //PM2.5���������������������������
+            Integer pm25Rank = ranking(monthlyData8Channels, regionCode, "PM2_5");
+            //PM2.5���������������������������������
+            Integer pm25YearOnYearRank = ranking(monthlyData8Channels, regionCode, "PM2_5_yearOnYear");
+
+
+            //���������������
+            result.put("compositeIndex_rank_8channels", compositeIndexRank == null ? "_" : compositeIndexRank);
+            result.put("compositeIndex_yearOnYear_rank_8channels", compositeIndexYearOnYearRank == null ? "_" : compositeIndexYearOnYearRank);
+            result.put("PM2_5_rank_8channels", pm25Rank == null ? "_" : pm25Rank);
+            result.put("PM2_5_yearOnYear_rank_8channels", pm25YearOnYearRank == null ? "_" : pm25YearOnYearRank);
+        }
+        return result;
+    }
+
+    /**
+     * @param areasMap                 ��������������������� areaCode--areaName
+     * @param areaData                 ���������������������
+     * @param compositeIndex           ���������������������������������
+     * @param compositeIndexYearOnYear ���������������������������������������
+     * @param pm25                     ���������������������������pm.5������
+     * @param pm25YearOnYear           ���������������������������pm2.5������
+     * @description ���������������������������������������������������������������������������
+     */
+    private Map<String, Object> getAreaCurrentMonthResult(Map<Integer, String> areasMap, List<CityAqiMonthly> areaData, Double compositeIndex, String compositeIndexYearOnYear, Integer pm25, String pm25YearOnYear) {
+        Map<String, Object> result = new HashMap<>();
+        result.put("compositeIndex", compositeIndex);
+        result.put("compositeIndexYearOnYear", compositeIndexYearOnYear);
+        result.put("PM2_5", null);
+        if (pm25 != null) {
+            result.put("PM2_5", pm25 + "ug/m��");
+        }
+        result.put("PM2_5YearOnYear", pm25YearOnYear);
+
+
+        //���������������������������������������������
+        List<String> compositeIndexLowerList = new ArrayList<>();
+        //���������������������������������������������
+        List<String> compositeIndexHigherList = new ArrayList<>();
+        //���������������������������������������������
+        List<String> compositeIndexEqualList = new ArrayList<>();
+
+        //���������������������������������������������������
+        List<String> compositeIndexYearOnYearLowerList = new ArrayList<>();
+        //���������������������������������������������������
+        List<String> compositeIndexYearOnYearHigherList = new ArrayList<>();
+        //���������������������������������������������������
+        List<String> compositeIndexYearOnYearEqualList = new ArrayList<>();
+        //���������������������������������������������������������������������������������
+        List<String> compositeIndexYearOnYearHigherOfPositiveList = new ArrayList<>();
+        //���������������������������������������������������������������������������������
+        List<String> compositeIndexYearOnYearHigherOfNegativeList = new ArrayList<>();
+        //���������������������������������������������������������������������������0���
+        List<String> compositeIndexYearOnYearHigherOfZeroList = new ArrayList<>();
+
+
+        //pm2.5���������������������������������������
+        List<String> pm25LowerList = new ArrayList<>();
+        //pm2.5���������������������������������������
+        List<String> pm25HigherList = new ArrayList<>();
+        //pm2.5���������������������������������������
+        List<String> pm25EqualList = new ArrayList<>();
+
+        //pm2.5���������������������������������������������
+        List<String> pm25YearOnYearLowerList = new ArrayList<>();
+        //pm2.5������������������������������������������������
+        List<String> pm25YearOnYearHigherList = new ArrayList<>();
+        ///pm2.5���������������������������������������������
+        List<String> pm25YearOnYearEqualList = new ArrayList<>();
+        //pm2.5���������������������������������������������������������������������������
+        List<String> pm25YearOnYearLowerOfPositiveList = new ArrayList<>();
+        //pm2.5���������������������������������������������������������������������������
+        List<String> pm25YearOnYearLowerOfNegativeList = new ArrayList<>();
+        //pm2.5������������������������������������������������������������������0���
+        List<String> pm25YearOnYearLowerOfZeroList = new ArrayList<>();
+
+
+        Double cityCompositeIndexYearOnYear = null;
+        if (compositeIndexYearOnYear != null) {
+            cityCompositeIndexYearOnYear = Double.parseDouble(compositeIndexYearOnYear.replace("%", ""));
+        }
+        Double citypm25YearOnYear = null;
+        if (pm25YearOnYear != null) {
+            citypm25YearOnYear = Double.parseDouble(pm25YearOnYear.replace("%", ""));
+        }
+
+
+        for (CityAqiMonthly areaDatum : areaData) {
+            Integer areaCode = areaDatum.getCityCode();
+            Map<String, Object> valueMap = JSONObject.parseObject(areaDatum.getValue(), Map.class);
+            double compositeIndexValue = Double.parseDouble(valueMap.get("compositeIndex").toString());
+
+            Double compositeIndexYearOnYearValue = null;
+            if (valueMap.get("compositeIndex_yearOnYear") != null) {
+                compositeIndexYearOnYearValue = Double.parseDouble(valueMap.get("compositeIndex_yearOnYear").toString().replace("%", ""));
+            }
+
+            int pm25Value = (int) Double.parseDouble(valueMap.get("PM2_5").toString());
+            Double pm25YearOnYearValue = null;
+            if (valueMap.get("PM2_5_yearOnYear") != null) {
+                pm25YearOnYearValue = Double.parseDouble(valueMap.get("PM2_5_yearOnYear").toString().replace("%", ""));
+            }
+
+            String areaName = areasMap.get(areaCode);
+            if (compositeIndexValue < compositeIndex) {
+                compositeIndexLowerList.add(areaName + "���" + compositeIndexValue + "���");
+            } else if (compositeIndexValue > compositeIndex) {
+                compositeIndexHigherList.add(areaName + "���" + compositeIndexValue + "���");
+            } else {
+                compositeIndexEqualList.add(areaName + "���" + compositeIndexValue + "���");
+            }
+
+            if (cityCompositeIndexYearOnYear != null && compositeIndexYearOnYearValue != null) {
+                String compositeIndex_yearOnYear = valueMap.get("compositeIndex_yearOnYear").toString();
+                if (compositeIndexYearOnYearValue > 0d) {
+                    compositeIndex_yearOnYear = "������" + compositeIndex_yearOnYear;
+                } else if (compositeIndexYearOnYearValue < 0d) {
+                    compositeIndex_yearOnYear = compositeIndex_yearOnYear.replace("-", "������");
+                }
+
+                if (compositeIndexYearOnYearValue > cityCompositeIndexYearOnYear) {
+
+                    compositeIndexYearOnYearHigherList.add(areaName + "���" + compositeIndex_yearOnYear + "���");
+                    if (compositeIndexYearOnYearValue > 0) {
+                        compositeIndexYearOnYearHigherOfPositiveList.add(areaName + "���" + compositeIndex_yearOnYear + "���");
+                    } else if (compositeIndexYearOnYearValue < 0) {
+                        compositeIndexYearOnYearHigherOfNegativeList.add(areaName + "���" + compositeIndex_yearOnYear + "���");
+                    } else {
+                        compositeIndexYearOnYearHigherOfZeroList.add(areaName + "���" + compositeIndex_yearOnYear + "���");
+                    }
+                } else if (compositeIndexYearOnYearValue < cityCompositeIndexYearOnYear) {
+                    compositeIndexYearOnYearLowerList.add(areaName + "���" + compositeIndex_yearOnYear + "���");
+                } else {
+                    compositeIndexYearOnYearEqualList.add(areaName + "���" + compositeIndex_yearOnYear + "���");
+                }
+            }
+
+
+            if (pm25Value < pm25) {
+                pm25LowerList.add(areaName + "���" + pm25Value + "ug/m�����");
+            } else if (pm25Value > pm25) {
+                pm25HigherList.add(areaName + "���" + pm25Value + "ug/m�����");
+            } else {
+                pm25EqualList.add(areaName + "���" + pm25Value + "ug/m�����");
+            }
+
+            if (citypm25YearOnYear != null && pm25YearOnYearValue != null) {
+                String pm2_5_yearOnYear = valueMap.get("PM2_5_yearOnYear").toString();
+                if (pm25YearOnYearValue > 0d) {
+                    pm2_5_yearOnYear = "������" + pm2_5_yearOnYear;
+                } else if (pm25YearOnYearValue < 0d) {
+                    pm2_5_yearOnYear = pm2_5_yearOnYear.replace("-", "������");
+                }
+
+                if (pm25YearOnYearValue < citypm25YearOnYear) {
+                    pm25YearOnYearLowerList.add(areaName + "���" + pm2_5_yearOnYear + "���");
+                    if (pm25YearOnYearValue > 0) {
+                        pm25YearOnYearLowerOfPositiveList.add(areaName + "���" + pm2_5_yearOnYear + "���");
+                    } else if (pm25YearOnYearValue < 0) {
+                        pm25YearOnYearLowerOfNegativeList.add(areaName + "���" + pm2_5_yearOnYear + "���");
+                    } else {
+                        pm25YearOnYearLowerOfZeroList.add(areaName + "���" + pm2_5_yearOnYear + "���");
+                    }
+                } else if (pm25YearOnYearValue > citypm25YearOnYear) {
+                    pm25YearOnYearHigherList.add(areaName + "���" + pm2_5_yearOnYear + "���");
+                } else {
+                    pm25YearOnYearEqualList.add(areaName + "���" + pm2_5_yearOnYear + "���");
+                }
+            }
+
+        }
+        result.put("compositeIndexLower", compositeIndexLowerList);
+        result.put("compositeIndexHigher", compositeIndexHigherList);
+        result.put("compositeIndexEqual", compositeIndexEqualList);
+        result.put("compositeIndexYearOnYearHigher", compositeIndexYearOnYearHigherList);
+        result.put("compositeIndexYearOnYearLower", compositeIndexYearOnYearLowerList);
+        result.put("compositeIndexYearOnYearEqual", compositeIndexYearOnYearEqualList);
+        result.put("compositeIndexYearOnYearHigherOfPositive", compositeIndexYearOnYearHigherOfPositiveList);
+        result.put("compositeIndexYearOnYearHigherOfNegative", compositeIndexYearOnYearHigherOfNegativeList);
+        result.put("compositeIndexYearOnYearHigherOfZero", compositeIndexYearOnYearHigherOfZeroList);
+
+        result.put("PM2_5Lower", pm25LowerList);
+        result.put("PM2_5Higher", pm25HigherList);
+        result.put("PM2_5Equal", pm25EqualList);
+        result.put("PM2_5YearOnYearHigher", pm25YearOnYearHigherList);
+        result.put("PM2_5YearOnYearLower", pm25YearOnYearLowerList);
+        result.put("PM2_5YearOnYearEqual", pm25YearOnYearEqualList);
+        result.put("pm25YearOnYearLowerOfPositive", pm25YearOnYearLowerOfPositiveList);
+        result.put("pm25YearOnYearLowerOfNegative", pm25YearOnYearLowerOfNegativeList);
+        result.put("pm25YearOnYearLowerOfZero", pm25YearOnYearLowerOfZeroList);
+
+        return result;
+    }
+
+    //1-x���������������
+    public Map<Integer, Map<String, Object>> getMonthlyCumulativeResult(Date time, List<Integer> regionCodes) {
+
+        //���������������������1���1���
+        Date start = DateUtils.getFirstDayOfYear(time);
+        //���������������������������������
+        Date end = DateUtils.getLastDayOfMonth(time);
+
+        //������������������������1-x���������������
+        QueryWrapper<CityAqiDaily> cityAqiDailyQueryWrapper = new QueryWrapper<>();
+        cityAqiDailyQueryWrapper.select("city_code", "value")
+                .ge("time", start)
+                .le("time", end)
+                .in("city_code", regionCodes);
+        List<CityAqiDaily> cityAqiDailyList = cityAqiDailyService.list(cityAqiDailyQueryWrapper);
+        if (cityAqiDailyList.size() == 0) {
+            return null;
+        }
+
+        Map<Integer, Map<String, Object>> currentMap = getMonthCumulative(cityAqiDailyList);
+
+        //������������������������1-x������������������������������������
+        cityAqiDailyQueryWrapper.clear();
+        cityAqiDailyQueryWrapper.select("city_code", "value")
+                .ge("time", DateUtils.addYears(start, -1))
+                .le("time", DateUtils.addYears(end, -1))
+                .in("city_code", regionCodes);
+        List<CityAqiDaily> lastCityAqiDailyList = cityAqiDailyService.list(cityAqiDailyQueryWrapper);
+        Map<Integer, Map<String, Object>> lastMap = getMonthCumulative(lastCityAqiDailyList);
+
+        //���������������������
+        for (Map.Entry<Integer, Map<String, Object>> entry : currentMap.entrySet()) {
+            Integer regionCode = entry.getKey();
+            Map<String, Object> current = entry.getValue();
+            Map<String, Object> last = lastMap.get(regionCode);
+            Map<String, Object> map = yearOnYearOfSensor(last, current);
+            if (map != null) {
+                currentMap.put(regionCode, yearOnYearOfSensor(last, current));
+            }
+        }
+        return currentMap;
+    }
+
+    /**
+     * @param last    ������������
+     * @param current ������������
+     * @description ���������������������
+     */
+    private Map<String, Object> yearOnYearOfSensor(Map<String, Object> last, Map<String, Object> current) {
+        if (last == null) {
+            return null;
+        }
+        //���������������������������
+        List<String> sensors = Arrays.asList("PM2_5", "PM10", "SO2", "NO2", "CO", "O3", "compositeIndex");
+        for (String sensor : sensors) {
+            //������������������������
+            double lastValue = Double.parseDouble(last.get(sensor).toString());
+            //������������������
+            double currentValue = Double.parseDouble(current.get(sensor).toString());
+            DecimalFormat decimalFormat = new DecimalFormat("0.00%");
+            String format = decimalFormat.format((currentValue - lastValue) / lastValue);
+            current.put(sensor + "_yearOnYear", format);
+            current.put(sensor + "_last", lastValue);
+        }
+        return current;
+    }
+
+    /**
+     * @param data ���������������������������
+     * @description ������1-x������������������
+     */
+    private Map<Integer, Map<String, Object>> getMonthCumulative(List<CityAqiDaily> data) {
+        Map<Integer, Map<String, Object>> result = new HashMap<>();
+        //���������������������������
+        List<String> sensors = Arrays.asList("PM2_5", "PM10", "SO2", "NO2");
+        //���city_code������
+        Map<Integer, List<CityAqiDaily>> cityDataMap = data.stream()
+                .collect(Collectors.groupingBy(CityAqiDaily::getCityCode));
+        //1-x���������������
+        cityDataMap.forEach((cityCode, list) -> {
+            Map<String, Object> jsonMap = new HashMap<>();
+            Map<String, Object> params = new HashMap<>();
+            List<Map<String, Object>> temp = new ArrayList<>();
+            for (CityAqiDaily cityAqiDaily : list) {
+                Map<String, Object> valueMap = JSONObject.parseObject(cityAqiDaily.getValue(), Map.class);
+                Map<String, Object> tempMap = new HashMap<>();
+                Map<String, Object> hashMap = new HashMap<>();
+                tempMap.put(Constants.SENSOR_CODE_CO, valueMap.get("CO"));
+                tempMap.put(Constants.SENSOR_CODE_O3, valueMap.get("O3"));
+                hashMap.put("value", JSONObject.toJSONString(tempMap));
+                temp.add(hashMap);
+            }
+            params.put("data", temp);
+            //1. CO 95������������������������
+            Map<String, Object> coAvgOfWeekOrMonth = AmendUtils.getCOAvgOfWeekOrMonth(params);
+            if (!ObjectUtils.isEmpty(coAvgOfWeekOrMonth)) {
+                jsonMap.put("CO", coAvgOfWeekOrMonth.get(Constants.SENSOR_CODE_CO));
+            }
+
+            //2. O3 90������������������������
+            Map<String, Object> o3AvgOfWeekOrMonth = AmendUtils.getO3AvgOfWeekOrMonth(params);
+            if (!ObjectUtils.isEmpty(o3AvgOfWeekOrMonth)) {
+                jsonMap.put("O3", o3AvgOfWeekOrMonth.get(Constants.SENSOR_CODE_O3));
+            }
+
+            sensors.forEach(sensor -> {
+                OptionalDouble optionalDouble = list.stream().flatMapToDouble(v -> {
+                    Map<String, Object> dataValue = JSONObject.parseObject(v.getValue(), Map.class);
+                    double aDouble = Double.parseDouble(dataValue.get(sensor).toString());
+                    return DoubleStream.of(aDouble);
+                }).average();
+                if (optionalDouble.isPresent()) {
+                    //���������������������
+                    jsonMap.put(sensor, AmendUtils.sciCal(optionalDouble.getAsDouble(), 0));
+                }
+            });
+
+            //1-x���������
+            Double compositeIndex = ComprehensiveIndexUtils.dailyData(jsonMap);
+            jsonMap.put("compositeIndex", compositeIndex);
+            result.put(cityCode, jsonMap);
+        });
+        return result;
+    }
+
+    /**
+     * 1.�����������������������������������������������������������������������20%+��������������������������������������30%���+���PM2.5�����������������������������20%+PM2.5��������������������������������30%������
+     * 2.���������������������������������������������������������������
+     * 3.���������������������������������������������������������������PM2.5���������������������������������������������������������������������������������������������������������
+     *
+     * @param list     ���������������������
+     * @param areasMap ��������������������� areaCode--areaName
+     * @description ���������������pm2.5������������������
+     */
+    private List<Map<String, Object>> airQualityRankingOfCurrentReport(List<CityAqiMonthly> list, Map<Integer, String> areasMap) {
+        List<Map<String, Object>> result = new ArrayList<>();
+
+        Map<Integer, CityAqiMonthly> dataMap = new HashMap<>();
+        for (CityAqiMonthly cityAqiMonthly : list) {
+            dataMap.put(cityAqiMonthly.getCityCode(), cityAqiMonthly);
+        }
+
+
+        for (Map.Entry<Integer, String> entry : areasMap.entrySet()) {
+            Map<String, Object> resultMap = new HashMap<>();
+            Integer regionCode = entry.getKey();
+            String areaName = entry.getValue();
+            resultMap.put("areaName", areaName);
+
+            //������������
+            resultMap.put("compositeIndex", null);
+            //������������
+            resultMap.put("compositeIndexYearOnYear", null);
+            //������PM2.5
+            resultMap.put("PM2_5", null);
+            //PM2.5������
+            resultMap.put("PM2_5YearOnYear", null);
+            if (dataMap.get(regionCode) != null) {
+                Map<String, Object> valueMap = JSONObject.parseObject(dataMap.get(regionCode).getValue(), Map.class);
+
+                resultMap.put("compositeIndex", valueMap.get("compositeIndex"));
+                resultMap.put("compositeIndexYearOnYear", valueMap.get("compositeIndex_yearOnYear"));
+
+                resultMap.put("PM2_5", valueMap.get("PM2_5"));
+                resultMap.put("PM2_5YearOnYear", valueMap.get("PM2_5_yearOnYear"));
+            }
+            result.add(resultMap);
+        }
+
+        //1.������������������������
+        result.sort((o1, o2) -> {
+            if (o1.get("compositeIndex") != null && o2.get("compositeIndex") != null) {
+                double compositeIndexAbs1 = Math.abs(Double.parseDouble(o1.get("compositeIndex").toString()));
+                double compositeIndexAbs2 = Math.abs(Double.parseDouble(o2.get("compositeIndex").toString()));
+                return compositeIndexAbs1 > compositeIndexAbs2 ? 1 : -1;
+            } else if (o1.get("compositeIndex") == null && o2.get("compositeIndex") == null) {
+                return 0;
+            } else if (o1.get("compositeIndex") == null) {
+                return -1;
+            } else {
+                return 1;
+            }
+        });
+        //������������������
+        for (int i = 0; i < result.size(); i++) {
+            Map<String, Object> map = result.get(i);
+            map.put("compositeIndexRank", i + 1);
+        }
+
+        //2.���������������������������(������������)������
+        result.sort((o1, o2) -> {
+            if (o1.get("compositeIndexYearOnYear") != null && o2.get("compositeIndexYearOnYear") != null) {
+                double compositeIndexYearOnYear1 = Double.parseDouble(o1.get("compositeIndexYearOnYear").toString().replace("%", ""));
+                double compositeIndexYearOnYear2 = Double.parseDouble(o2.get("compositeIndexYearOnYear").toString().replace("%", ""));
+                return compositeIndexYearOnYear1 > compositeIndexYearOnYear2 ? 1 : -1;
+            } else if (o1.get("compositeIndexYearOnYear") == null && o2.get("compositeIndexYearOnYear") == null) {
+                return 0;
+            } else if (o1.get("compositeIndexYearOnYear") == null) {
+                return -1;
+            } else {
+                return 1;
+            }
+        });
+        //������������������
+        for (int i = 0; i < result.size(); i++) {
+            Map<String, Object> map = result.get(i);
+            map.put("compositeIndexYearOnYearRank", i + 1);
+        }
+
+        //3.���pm2.5���������������������
+        result.sort((o1, o2) -> {
+            if (o1.get("PM2_5") != null && o2.get("PM2_5") != null) {
+                double compositeIndexAbs1 = Math.abs(Double.parseDouble(o1.get("PM2_5").toString()));
+                double compositeIndexAbs2 = Math.abs(Double.parseDouble(o2.get("PM2_5").toString()));
+                return compositeIndexAbs1 > compositeIndexAbs2 ? 1 : -1;
+            } else if (o1.get("PM2_5") == null && o2.get("PM2_5") == null) {
+                return 0;
+            } else if (o1.get("PM2_5") == null) {
+                return -1;
+            } else {
+                return 1;
+            }
+        });
+        //������������������
+        for (int i = 0; i < result.size(); i++) {
+            Map<String, Object> map = result.get(i);
+            map.put("PM2_5Rank", i + 1);
+        }
+
+        //4.���pm2.5������������������
+        result.sort((o1, o2) -> {
+            if (o1.get("PM2_5YearOnYear") != null && o2.get("PM2_5YearOnYear") != null) {
+                double pm25YearOnYear1 = Double.parseDouble(o1.get("PM2_5YearOnYear").toString().replace("%", ""));
+                double pm25YearOnYear2 = Double.parseDouble(o2.get("PM2_5YearOnYear").toString().replace("%", ""));
+                return pm25YearOnYear1 > pm25YearOnYear2 ? 1 : -1;
+            } else if (o1.get("PM2_5YearOnYear") == null && o2.get("PM2_5YearOnYear") == null) {
+                return 0;
+            } else if (o1.get("PM2_5YearOnYear") == null) {
+                return -1;
+            } else {
+                return 1;
+            }
+        });
+        //������������������
+        for (int i = 0; i < result.size(); i++) {
+            Map<String, Object> map = result.get(i);
+            map.put("PM2_5YearOnYearRank", i + 1);
+        }
+
+        //������������������
+        for (Map<String, Object> map : result) {
+            int compositeIndexRank = Integer.parseInt(map.get("compositeIndexRank").toString());
+            int compositeIndexYearOnYearRank = Integer.parseInt(map.get("compositeIndexYearOnYearRank").toString());
+            int pm25Rank = Integer.parseInt(map.get("PM2_5Rank").toString());
+            int pm25YearOnYearRank = Integer.parseInt(map.get("PM2_5YearOnYearRank").toString());
+
+            double score = compositeIndexRank * 0.2 + compositeIndexYearOnYearRank * 0.3 + pm25Rank * 0.2 + pm25YearOnYearRank * 0.3;
+            String format = String.format("%.1f", score);
+            map.put("score", Double.parseDouble(format));
+        }
+
+
+        //������������
+        result.sort((o1, o2) -> {
+            double score1 = Double.parseDouble(o1.get("score").toString());
+            double score2 = Double.parseDouble(o2.get("score").toString());
+            if (score1 > score2) {
+                return 1;
+            } else if (score1 < score2) {
+                return -1;
+            }
+
+            //���������������������������������������������������������������PM2.5���������������������������������������������������������������������������������������������������������
+            int pm251 = Integer.parseInt(o1.get("PM2_5").toString());
+            int pm252 = Integer.parseInt(o2.get("PM2_5").toString());
+
+            if (pm251 > pm252) {
+                return 1;
+            } else if (pm251 < pm252) {
+                return -1;
+            }
+
+            //pm2.5������������������������������������������
+            double pm25YearOnYear1 = Double.parseDouble(o1.get("PM2_5YearOnYearRank").toString().replace("%", ""));
+            double pm25YearOnYear2 = Double.parseDouble(o2.get("PM2_5YearOnYearRank").toString().replace("%", ""));
+            return pm25YearOnYear1 > pm25YearOnYear2 ? 1 : -1;
+        });
+        //������������������������
+        for (int i = 0; i < result.size(); i++) {
+            Map<String, Object> map = result.get(i);
+            map.put("resultRank", i + 1);
+        }
+        for (Map<String, Object> map : result) {
+            for (Map.Entry<String, Object> entry : map.entrySet()) {
+                if (entry.getValue() == null) {
+                    map.put(entry.getKey(), "");
+                }
+            }
+        }
+
+
+        return result;
+    }
+
+    /**
+     * @param list     ���������������1-x������������
+     * @param areasMap ��������������������� areaCode--areaName
+     * @description 1-x���������������������������������pm2.5������������
+     */
+    private List<Map<String, Object>> cumulativeAirQualityRankingReport(List<CityAqiMonthly> list, Map<Integer, String> areasMap) {
+        List<Map<String, Object>> result = new ArrayList<>();
+
+
+        Map<Integer, CityAqiMonthly> dataMap = new HashMap<>();
+        for (CityAqiMonthly cityAqiMonthly : list) {
+            dataMap.put(cityAqiMonthly.getCityCode(), cityAqiMonthly);
+        }
+
+        for (Map.Entry<Integer, String> entry : areasMap.entrySet()) {
+            Map<String, Object> resultMap = new HashMap<>();
+            Integer regionCode = entry.getKey();
+            String areaName = entry.getValue();
+            resultMap.put("areaName", areaName);
+
+            //������������
+            resultMap.put("compositeIndex", null);
+            //������������
+            resultMap.put("compositeIndexLast", null);
+            //������������
+            resultMap.put("compositeIndexYearOnYear", null);
+            //������PM2.5
+            resultMap.put("PM2_5", null);
+            //������PM2.5
+            resultMap.put("PM2_5Last", null);
+            //PM2.5������
+            resultMap.put("PM2_5YearOnYear", null);
+            if (dataMap.get(regionCode) != null) {
+                Map<String, Object> valueMap = JSONObject.parseObject(dataMap.get(regionCode).getValue(), Map.class);
+
+                //������������������������������
+                resultMap.put("compositeIndex", valueMap.get("compositeIndex"));
+                resultMap.put("compositeIndexLast", valueMap.get("compositeIndex_last"));
+                resultMap.put("compositeIndexYearOnYear", valueMap.get("compositeIndex_yearOnYear"));
+
+                //PM2.5������������������������
+                resultMap.put("PM2_5", valueMap.get("PM2_5"));
+                resultMap.put("PM2_5Last", valueMap.get("PM2_5_last"));
+                resultMap.put("PM2_5YearOnYear", valueMap.get("PM2_5_yearOnYear"));
+            }
+            result.add(resultMap);
+        }
+
+        //���pm2.5������������
+        result.sort((o1, o2) -> {
+            if (o1.get("PM2_5YearOnYear") != null && o2.get("PM2_5YearOnYear") != null) {
+                double pm25YearOnYear1 = Double.parseDouble(o1.get("PM2_5YearOnYear").toString().replace("%", ""));
+                double pm25YearOnYear2 = Double.parseDouble(o2.get("PM2_5YearOnYear").toString().replace("%", ""));
+                return pm25YearOnYear1 > pm25YearOnYear2 ? 1 : -1;
+            } else if (o1.get("PM2_5YearOnYear") == null && o2.get("PM2_5YearOnYear") == null) {
+                return 0;
+            } else if (o1.get("PM2_5YearOnYear") == null) {
+                return -1;
+            } else {
+                return 1;
+            }
+        });
+        //������������������
+        for (int i = 0; i < result.size(); i++) {
+            Map<String, Object> map = result.get(i);
+            map.put("PM2_5YearOnYearRank", i + 1);
+        }
+
+
+        //���������������������������
+        result.sort((o1, o2) -> {
+            if (o1.get("compositeIndexYearOnYear") != null && o2.get("compositeIndexYearOnYear") != null) {
+                double compositeIndexYearOnYear1 = Double.parseDouble(o1.get("compositeIndexYearOnYear").toString().replace("%", ""));
+                double compositeIndexYearOnYear2 = Double.parseDouble(o2.get("compositeIndexYearOnYear").toString().replace("%", ""));
+                return compositeIndexYearOnYear1 > compositeIndexYearOnYear2 ? 1 : -1;
+            } else if (o1.get("compositeIndexYearOnYear") == null && o2.get("compositeIndexYearOnYear") == null) {
+                return 0;
+            } else if (o1.get("compositeIndexYearOnYear") == null) {
+                return -1;
+            } else {
+                return 1;
+            }
+        });
+        //������������������
+        for (int i = 0; i < result.size(); i++) {
+            Map<String, Object> map = result.get(i);
+            map.put("compositeIndexYearOnYearRank", i + 1);
+        }
+        for (Map<String, Object> map : result) {
+            for (Map.Entry<String, Object> entry : map.entrySet()) {
+                if (entry.getValue() == null) {
+                    map.put(entry.getKey(), "");
+                }
+            }
+        }
+
+        return result;
+    }
+
+    /**
+     * @param time      ������������
+     * @param areaCodes ���������������code������
+     * @param areaData  ���������������������������
+     * @param areasMap  ���������������������
+     * @description ������������������PM10,SO2,NO2,CO,O3���������������
+     */
+    private List<Map<String, Object>> currentFiveSensorsContrastReport(Date time, List<Integer> areaCodes, List<CityAqiMonthly> areaData, Map<Integer, String> areasMap) {
+        List<Map<String, Object>> result = new ArrayList<>();
+
+        //������������
+        Map<Integer, CityAqiMonthly> dataCurrent = new HashMap<>();
+        for (CityAqiMonthly cityAqiMonthly : areaData) {
+            dataCurrent.put(cityAqiMonthly.getCityCode(), cityAqiMonthly);
+        }
+
+
+        //������������������������
+        QueryWrapper<CityAqiMonthly> queryWrapper = new QueryWrapper<>();
+        queryWrapper.clear();
+        queryWrapper.select("city_code", "value")
+                .eq("time", DateUtils.addYears(time, -1))
+                .in("city_code", areaCodes);
+        List<CityAqiMonthly> monthlyDataLast = cityAqiMonthlyMapper.selectList(queryWrapper);
+        Map<Integer, CityAqiMonthly> dataLast = new HashMap<>();
+        for (CityAqiMonthly cityAqiMonthly : monthlyDataLast) {
+            dataLast.put(cityAqiMonthly.getCityCode(), cityAqiMonthly);
+        }
+
+
+        //���������������������������������
+        List<String> sensors = Arrays.asList("PM10", "SO2", "NO2", "CO", "O3");
+        for (Map.Entry<Integer, String> entry : areasMap.entrySet()) {
+            Map<String, Object> resultMap = new HashMap<>();
+            String areaName = entry.getValue();
+            Integer regionCode = entry.getKey();
+            resultMap.put("areaName", areaName);
+
+            for (String sensor : sensors) {
+                resultMap.put(sensor, null);
+                resultMap.put(sensor + "YearOnYear", null);
+                if (dataCurrent.get(regionCode) != null) {
+                    Map<String, Object> currentValue = JSONObject.parseObject(dataCurrent.get(regionCode).getValue(), Map.class);
+                    resultMap.put(sensor, currentValue.get(sensor));
+                    resultMap.put(sensor + "YearOnYear", currentValue.get(sensor + "_yearOnYear"));
+                }
+
+                //���������������������������
+                resultMap.put(sensor + "Last", null);
+                if (dataLast.get(regionCode) != null) {
+                    Map<String, Object> lastValue = JSONObject.parseObject(dataLast.get(regionCode).getValue(), Map.class);
+                    //���������������������������
+                    resultMap.put(sensor + "Last", lastValue.get(sensor));
+                }
+            }
+            result.add(resultMap);
+        }
+        for (Map<String, Object> map : result) {
+            for (Map.Entry<String, Object> entry : map.entrySet()) {
+                if (entry.getValue() == null) {
+                    map.put(entry.getKey(), "");
+                }
+            }
+        }
+        return result;
+    }
+
+    /**
+     * @param areaData ���������������1-x���������������������������������������������������������
+     * @param areasMap ���������������������
+     * @description 1-x������������������PM10,SO2,NO2,CO,O3���������������
+     */
+    private List<Map<String, Object>> cumulativeFiveSensorsContrastReport(List<CityAqiMonthly> areaData, Map<Integer, String> areasMap) {
+        List<Map<String, Object>> result = new ArrayList<>();
+
+        //������������
+        Map<Integer, CityAqiMonthly> dataCurrent = new HashMap<>();
+        for (CityAqiMonthly cityAqiMonthly : areaData) {
+            dataCurrent.put(cityAqiMonthly.getCityCode(), cityAqiMonthly);
+        }
+
+
+        List<String> sensors = Arrays.asList("PM10", "SO2", "NO2", "CO", "O3");
+        for (Map.Entry<Integer, String> entry : areasMap.entrySet()) {
+            Map<String, Object> resultMap = new HashMap<>();
+            String areaName = entry.getValue();
+            Integer regionCode = entry.getKey();
+            resultMap.put("areaName", areaName);
+            for (String sensor : sensors) {
+                resultMap.put(sensor, null);
+                resultMap.put(sensor + "Last", null);
+                resultMap.put(sensor + "YearOnYear", null);
+                if (dataCurrent.get(regionCode) != null) {
+                    Map<String, Object> valueMap = JSONObject.parseObject(dataCurrent.get(regionCode).getValue(), Map.class);
+                    resultMap.put(sensor, valueMap.get(sensor));
+                    resultMap.put(sensor + "Last", valueMap.get(sensor + "_last"));
+                    resultMap.put(sensor + "YearOnYear", valueMap.get(sensor + "_yearOnYear"));
+                }
+            }
+            result.add(resultMap);
+        }
+
+        for (Map<String, Object> map : result) {
+            for (Map.Entry<String, Object> entry : map.entrySet()) {
+                if (entry.getValue() == null) {
+                    map.put(entry.getKey(), "");
+                }
+            }
+        }
+        return result;
+    }
 }
diff --git a/screen-api/src/main/resources/application-announcement.yml b/screen-api/src/main/resources/application-announcement.yml
new file mode 100644
index 0000000..d2f0628
--- /dev/null
+++ b/screen-api/src/main/resources/application-announcement.yml
@@ -0,0 +1,7 @@
+announcement:
+  currentRanking: "{start-end}���{cityName}���������������{compositeIndex}���{compositeIndexRanking}���������������������{compositeIndexYearOnYear}���{compositeIndexYearOnYearRanking}���PM2.5���������������{PM2_5}���{PM2_5Ranking}���PM2.5������������������{PM2_5YearOnYear}���{PM2_5YearOnYearRanking}������������������������������������������������������������������������������������"
+  cumulativeRanking: "{start-end}���{cityName}���������������{compositeIndex}���{compositeIndexRanking}���������������������{compositeIndexYearOnYear}���{compositeIndexYearOnYearRanking}���PM2.5���������������{PM2_5}���{PM2_5Ranking}���PM2.5������������������{PM2_5YearOnYear}���{PM2_5YearOnYearRanking}������������������������������������������������������������������������������������"
+  contrast-compositeIndex: "{month}���������{cityName}���������������������������������������{compositeIndex}���{cityName}{areaSize}������������������������{details}���"
+  contrast-compositeIndexYearOnYear: "{month}���������{cityName}������������������������������������������{compositeIndexYearOnYear}���{cityName}{areaSize}������������������������{details}���"
+  contrast-pm25: "{month}���������{cityName}������������PM2.5���������������{PM2_5}���{cityName}{areaSize}������������������������{details}���"
+  contrast-pm25YearOnYear: "{month}���������{cityName}PM2.5������������������������������{PM2_5YearOnYear}���{cityName}{areaSize}������������������������{details}���"
diff --git a/screen-api/src/main/resources/application-dev.yml b/screen-api/src/main/resources/application-dev.yml
index e9b1136..44d60b5 100644
--- a/screen-api/src/main/resources/application-dev.yml
+++ b/screen-api/src/main/resources/application-dev.yml
@@ -13,7 +13,7 @@
 spring:
   profiles:
     active: dev
-    include: bulletin,specialCity
+    include: bulletin, specialCity, announcement
   application:
     name: screen-api
   redis:
diff --git a/screen-common/src/main/java/com/moral/constant/RedisConstants.java b/screen-common/src/main/java/com/moral/constant/RedisConstants.java
index 5343376..ec037ea 100644
--- a/screen-common/src/main/java/com/moral/constant/RedisConstants.java
+++ b/screen-common/src/main/java/com/moral/constant/RedisConstants.java
@@ -98,5 +98,10 @@
      * */
     public static final String CITY_WEATHER = "city_weather";
 
+    /*
+     * redis���������������������������������
+     * */
+    public static final String AQI_ANNOUNCEMENT = "aqi_announcement";
+
 
 }
diff --git a/screen-job/src/main/java/com/moral/api/service/impl/CityAqiDailyServiceImpl.java b/screen-job/src/main/java/com/moral/api/service/impl/CityAqiDailyServiceImpl.java
index 5ec2a3e..b98b8fc 100644
--- a/screen-job/src/main/java/com/moral/api/service/impl/CityAqiDailyServiceImpl.java
+++ b/screen-job/src/main/java/com/moral/api/service/impl/CityAqiDailyServiceImpl.java
@@ -59,8 +59,8 @@
         //������������������aqi������������
         QueryWrapper<CityAqi> wrapper = new QueryWrapper<>();
         wrapper.select("city_code", "time", "value")
-                .ge("time", DateUtils.dateToDateString(start))
-                .lt("time", DateUtils.dateToDateString(end));
+                .ge("time", start)
+                .lt("time", end);
         List<Map<String, Object>> dailyData = cityAqiService.listMaps(wrapper);
 
         if (dailyData.size() == 0) {
diff --git a/screen-job/src/main/java/com/moral/api/service/impl/CityAqiMonthlyServiceImpl.java b/screen-job/src/main/java/com/moral/api/service/impl/CityAqiMonthlyServiceImpl.java
index 2ce3389..346a637 100644
--- a/screen-job/src/main/java/com/moral/api/service/impl/CityAqiMonthlyServiceImpl.java
+++ b/screen-job/src/main/java/com/moral/api/service/impl/CityAqiMonthlyServiceImpl.java
@@ -76,35 +76,39 @@
         //������������������aqi������������
         QueryWrapper<CityAqiDaily> wrapper = new QueryWrapper<>();
         wrapper.select("city_code", "time", "value")
-                .ge("time", DateUtils.dateToDateString(start))
-                .lt("time", DateUtils.dateToDateString(end));
+                .ge("time", start)
+                .lt("time", end);
         List<Map<String, Object>> monthlyData = cityAqiDailyService.listMaps(wrapper);
 
         if (monthlyData.size() == 0) {
             return;
         }
         //���city_code������
-        Map<String, List<Map<String, Object>>> data = monthlyData.stream()
-                .collect(Collectors.groupingBy(o -> o.get("city_code").toString()));
+        Map<Integer, List<Map<String, Object>>> data = monthlyData.stream()
+                .collect(Collectors.groupingBy(o ->Integer.parseInt(o.get("city_code").toString()) ));
 
         //������������������������������������������������
         QueryWrapper<CityAqiMonthly> queryWrapper = new QueryWrapper<>();
         queryWrapper.select("city_code", "value")
-                .eq("time", DateUtils.dateToDateString(lastLastMonth));
+                .eq("time", lastLastMonth);
         //������������������
-        List<CityAqiMonthly> lastCityAqiMonthly = cityAqiMonthlyMapper.selectList(queryWrapper);
-        Map<Integer, List<CityAqiMonthly>> lastMonthData = lastCityAqiMonthly.stream()
-                .collect(Collectors.groupingBy(CityAqiMonthly::getCityCode));
+        List<CityAqiMonthly> lastCityAqiMonthlyList = cityAqiMonthlyMapper.selectList(queryWrapper);
+        Map<Integer, CityAqiMonthly> lastMonthData = new HashMap<>();
+        for (CityAqiMonthly cityAqiMonthly : lastCityAqiMonthlyList) {
+            lastMonthData.put(cityAqiMonthly.getCityCode(), cityAqiMonthly);
+        }
 
 
         //������������������������
         Date thisMonthOfLastYear = DateUtils.addYears(start, -1);
         queryWrapper.clear();
         queryWrapper.select("city_code", "value")
-                .eq("time", DateUtils.dateToDateString(thisMonthOfLastYear));
+                .eq("time", thisMonthOfLastYear);
         List<CityAqiMonthly> thisMonthOfLastYearList = cityAqiMonthlyMapper.selectList(queryWrapper);
-        Map<Integer, List<CityAqiMonthly>> thisMonthOfLastYearData = thisMonthOfLastYearList.stream()
-                .collect(Collectors.groupingBy(CityAqiMonthly::getCityCode));
+        Map<Integer, CityAqiMonthly> thisMonthOfLastYearData = new HashMap<>();
+        for (CityAqiMonthly cityAqiMonthly : thisMonthOfLastYearList) {
+            thisMonthOfLastYearData.put(cityAqiMonthly.getCityCode(), cityAqiMonthly);
+        }
 
 
         List<CityAqiMonthly> cityAqiMonthlyList = new ArrayList<>();
@@ -113,7 +117,7 @@
         data.forEach((cityCode, value) -> {
             CityAqiMonthly cityAqiMonthly = new CityAqiMonthly();
             Map<String, Object> jsonMap = new HashMap<>();
-            cityAqiMonthly.setCityCode(Integer.parseInt(cityCode));
+            cityAqiMonthly.setCityCode(cityCode);
             cityAqiMonthly.setTime(finalStart);
 
             Map<String, Object> params = new HashMap<>();
@@ -157,10 +161,9 @@
             jsonMap.put("compositeIndex", compositeIndex);
 
             //���������������������������(������������������)
-            List<CityAqiMonthly> cityAqiMonthlies = lastMonthData.get(Integer.parseInt(cityCode));
-            if (!ObjectUtils.isEmpty(cityAqiMonthlies)) {
-                CityAqiMonthly monthly = cityAqiMonthlies.get(0);
-                Map<String, Object> map = JSONObject.parseObject(monthly.getValue(), Map.class);
+            CityAqiMonthly lastCityAqiMonthly = lastMonthData.get(cityCode);
+            if (lastCityAqiMonthly != null) {
+                Map<String, Object> map = JSONObject.parseObject(lastCityAqiMonthly.getValue(), Map.class);
                 double lastCompositeIndex = Double.parseDouble(map.get("compositeIndex").toString());
                 DecimalFormat decimalFormat = new DecimalFormat("0.00%");
                 String format = decimalFormat.format((compositeIndex - lastCompositeIndex) / lastCompositeIndex);
@@ -168,7 +171,7 @@
             }
 
             //���������������������������������������������
-            List<CityAqiMonthly> thisMonthOfLastYears = thisMonthOfLastYearData.get(Integer.parseInt(cityCode));
+            CityAqiMonthly thisMonthOfLastYears = thisMonthOfLastYearData.get(cityCode);
 
             //���������������������
             Map<String, Object> yearOnYearValue = yearOnYearOfSensor(thisMonthOfLastYears, jsonMap);
@@ -187,14 +190,13 @@
      * @param thisMonthOfLastYearData ������������������
      * @param currentData             ������������������
      */
-    private Map<String, Object> yearOnYearOfSensor(List<CityAqiMonthly> thisMonthOfLastYearData, Map<String, Object> currentData) {
+    private Map<String, Object> yearOnYearOfSensor(CityAqiMonthly thisMonthOfLastYearData, Map<String, Object> currentData) {
         Map<String, Object> result = null;
         //���������������������������
         List<String> sensors = Arrays.asList("PM2_5", "PM10", "SO2", "NO2", "CO", "O3", "compositeIndex");
-        if (!ObjectUtils.isEmpty(thisMonthOfLastYearData)) {
+        if (thisMonthOfLastYearData != null) {
             result = new HashMap<>();
-            CityAqiMonthly monthly = thisMonthOfLastYearData.get(0);
-            Map<String, Object> map = JSONObject.parseObject(monthly.getValue(), Map.class);
+            Map<String, Object> map = JSONObject.parseObject(thisMonthOfLastYearData.getValue(), Map.class);
             for (String sensor : sensors) {
                 //������������������������
                 double thisMonthOfLeastYearValue = Double.parseDouble(map.get(sensor).toString());
diff --git a/screen-job/src/main/java/com/moral/api/service/impl/CityAqiServiceImpl.java b/screen-job/src/main/java/com/moral/api/service/impl/CityAqiServiceImpl.java
index 1b3630d..9fcb377 100644
--- a/screen-job/src/main/java/com/moral/api/service/impl/CityAqiServiceImpl.java
+++ b/screen-job/src/main/java/com/moral/api/service/impl/CityAqiServiceImpl.java
@@ -85,7 +85,7 @@
                     .collect(Collectors.toList());
 
             queryWrapper.select("city_code", "time", "value")
-                    .ge("time", DateUtils.dateToDateString(start))
+                    .ge("time", start)
                     .in("city_code", cityCodes);
             List<Map<String, Object>> selectMaps = cityAqiMapper.selectMaps(queryWrapper);
 
diff --git a/screen-job/src/main/java/com/moral/api/service/impl/CityAqiYearlyServiceImpl.java b/screen-job/src/main/java/com/moral/api/service/impl/CityAqiYearlyServiceImpl.java
index 245369d..92736ee 100644
--- a/screen-job/src/main/java/com/moral/api/service/impl/CityAqiYearlyServiceImpl.java
+++ b/screen-job/src/main/java/com/moral/api/service/impl/CityAqiYearlyServiceImpl.java
@@ -77,8 +77,8 @@
         //������������������aqi���������
         QueryWrapper<CityAqiDaily> wrapper = new QueryWrapper<>();
         wrapper.select("city_code", "time", "value")
-                .ge("time", DateUtils.dateToDateString(start))
-                .lt("time", DateUtils.dateToDateString(end));
+                .ge("time", start)
+                .lt("time", end);
         List<Map<String, Object>> monthlyData = cityAqiDailyService.listMaps(wrapper);
 
         if (monthlyData.size() == 0) {
@@ -91,11 +91,14 @@
         //������������
         QueryWrapper<CityAqiYearly> queryWrapper = new QueryWrapper<>();
         queryWrapper.select("city_code", "value")
-                .eq("time", DateUtils.dateToDateString(lastLastYear));
+                .eq("time", lastLastYear);
         //������������������
-        List<CityAqiYearly> lastCityAqiYearly = cityAqiYearlyMapper.selectList(queryWrapper);
-        Map<Integer, List<CityAqiYearly>> lastYearData = lastCityAqiYearly.stream()
-                .collect(Collectors.groupingBy(CityAqiYearly::getCityCode));
+        List<CityAqiYearly> lastCityAqiYearlyList = cityAqiYearlyMapper.selectList(queryWrapper);
+        Map<Integer, CityAqiYearly> lastYearData = new HashMap<>();
+        for (CityAqiYearly cityAqiYearly : lastCityAqiYearlyList) {
+            lastYearData.put(cityAqiYearly.getCityCode(), cityAqiYearly);
+        }
+
 
         List<CityAqiYearly> cityAqiYearlyList = new ArrayList<>();
 
@@ -135,10 +138,9 @@
             jsonMap.put("compositeIndex", compositeIndex);
 
             //���������������������������
-            List<CityAqiYearly> cityAqiYearlies = lastYearData.get(Integer.parseInt(cityCode));
-            if (!ObjectUtils.isEmpty(cityAqiYearlies)) {
-                CityAqiYearly yearly = cityAqiYearlies.get(0);
-                Map<String, Object> map = JSONObject.parseObject(yearly.getValue(), Map.class);
+            CityAqiYearly lastCityAqiYearly = lastYearData.get(Integer.parseInt(cityCode));
+            if (lastCityAqiYearly != null) {
+                Map<String, Object> map = JSONObject.parseObject(lastCityAqiYearly.getValue(), Map.class);
                 double lastCompositeIndex = Double.parseDouble(map.get("compositeIndex").toString());
                 DecimalFormat decimalFormat = new DecimalFormat("0.00%");
                 String format = decimalFormat.format((compositeIndex - lastCompositeIndex) / lastCompositeIndex);

--
Gitblit v1.8.0