Quellcode durchsuchen

Merge branch 'master' of http://git.jiayuepowertech.com:9022/xusl/cpp

zy vor 6 Monaten
Ursprung
Commit
13ebe995cf

+ 13 - 3
cpp-admin/src/main/java/com/cpp/web/controller/accuracy/AccuracyPassRateController.java

@@ -6,12 +6,11 @@ import com.cpp.web.domain.enums.DataSourcesEnum;
 import com.cpp.web.domain.enums.ForecastTypeEnum;
 import com.cpp.web.service.accuracy.AccuracyPassRateService;
 import lombok.RequiredArgsConstructor;
-import org.springframework.web.bind.annotation.GetMapping;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.bind.annotation.*;
 
 import java.util.Date;
 import java.util.List;
+import java.util.Map;
 
 @RestController
 @RequiredArgsConstructor
@@ -24,4 +23,15 @@ public class AccuracyPassRateController {
         List<AccuracyPassRate> accuracyPassRateList = accuracyPassRateService.findByTimeBetweenAndForecastTypeAndDataSourcesAndAgoAndForecastModelAndStationCode(new Date(startTime),new Date(endTime),forecastType,dataSources,ago,forecastModel,stationCode);
         return R.ok(accuracyPassRateList);
     }
+
+    @GetMapping("/getByMonthBetweenAndForecastTypeAndStationCode")
+    public R getByMonthBetweenAndForecastTypeAndStationCode(Long startTime,Long endTime,ForecastTypeEnum forecastType, DataSourcesEnum dataSources, String stationCode){
+        Map<String, Double> map = accuracyPassRateService.findByMonthBetweenAndForecastTypeAndStationCode(startTime,endTime,forecastType,dataSources,stationCode);
+        return R.ok(map);
+    }
+    @GetMapping("/getBySingleMonthBetweenAndForecastTypeAndStationCode")
+    public R getBySingleMonthBetweenAndForecastTypeAndStationCode(Long startTime,Long endTime,ForecastTypeEnum forecastType, DataSourcesEnum dataSources, String stationCode,String stationName,String uploadDataSources){
+        List<Map<String,String>> accuracyPassRateList = accuracyPassRateService.finfBySingleMonthBetweenAndForecastTypeAndStationCode(startTime,endTime,forecastType,dataSources,stationCode,stationName,uploadDataSources);
+        return R.ok(accuracyPassRateList);
+    }
 }

+ 5 - 0
cpp-admin/src/main/java/com/cpp/web/service/accuracy/AccuracyPassRateService.java

@@ -7,6 +7,7 @@ import com.cpp.web.domain.enums.ForecastTypeEnum;
 
 import java.util.Date;
 import java.util.List;
+import java.util.Map;
 
 /**
  * idp_forecast_power_short_term
@@ -18,4 +19,8 @@ public interface AccuracyPassRateService extends IService<AccuracyPassRate> {
     List<AccuracyPassRate> findByTimeBetweenAndForecastTypeAndDataSourcesAndAgoAndForecastModelAndStationCode(Date startTime, Date endTime, ForecastTypeEnum forecastType, DataSourcesEnum dataSources,Integer ago, String forecastModel,String stationCode);
 
     List<AccuracyPassRate> findByTimeBetweenAndStationCode(Date startTime,Date endTime,String stationCode);
+
+    Map<String, Double> findByMonthBetweenAndForecastTypeAndStationCode(Long startTime, Long endTime, ForecastTypeEnum forecastType, DataSourcesEnum dataSources, String stationCode);
+
+    List<Map<String,String>> finfBySingleMonthBetweenAndForecastTypeAndStationCode(Long startTime, Long endTime, ForecastTypeEnum forecastType, DataSourcesEnum dataSources, String stationCode,String stationName,String uploadDataSources);
 }

+ 122 - 2
cpp-admin/src/main/java/com/cpp/web/service/accuracy/impl/AccuracyPassRateServiceImpl.java

@@ -7,10 +7,15 @@ import com.cpp.web.domain.enums.DataSourcesEnum;
 import com.cpp.web.domain.enums.ForecastTypeEnum;
 import com.cpp.web.mapper.accuracy.AccuracyPassRateMapper;
 import com.cpp.web.service.accuracy.AccuracyPassRateService;
+import com.cpp.web.utils.StartAndEndMonthUtil;
 import org.springframework.stereotype.Service;
 
-import java.util.Date;
-import java.util.List;
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+import java.text.SimpleDateFormat;
+import java.util.*;
+import java.util.function.Function;
+import java.util.stream.Collectors;
 
 /**
  * idp_forecast_power_short_term
@@ -60,4 +65,119 @@ public class AccuracyPassRateServiceImpl extends ServiceImpl<AccuracyPassRateMap
         wrapper.eq("forecast_type","dq");
         return list(wrapper);
     }
+
+    @Override
+    public Map<String, Double> findByMonthBetweenAndForecastTypeAndStationCode(Long startTime, Long endTime, ForecastTypeEnum forecastType, DataSourcesEnum dataSources,String stationCode) {
+
+        QueryWrapper<AccuracyPassRate> wrapper = new QueryWrapper<>();
+        if (stationCode != null && !stationCode.equals("")) {
+            wrapper.eq("station_code", stationCode);
+        }
+        if (startTime != null && endTime != null) {
+            wrapper.between("time", new Date(StartAndEndMonthUtil.getBeginAndEndMonth(startTime,1)), new Date(StartAndEndMonthUtil.getBeginAndEndMonth(endTime,2)));
+        }
+        if (forecastType != null && !forecastType.equals("")) {
+            wrapper.eq("forecast_type", forecastType);
+        }
+        if (dataSources != null && !dataSources.equals("")) {
+            wrapper.eq("data_sources", dataSources);
+        }
+        List<AccuracyPassRate> accuracyPassRateList = baseMapper.selectList(wrapper);
+
+        Map<String, Double> sortMap = new TreeMap<>();
+        if (null != accuracyPassRateList && accuracyPassRateList.size() > 0) {
+            accuracyPassRateList = accuracyPassRateList.stream().filter(f -> !"无可用数据计算".equals(f.getAccuracy()) && !"无计算公式".equals(f.getAccuracy())).collect(Collectors.toList());
+
+            Function<String, Double> stringToDouble = s -> {
+                String numberStr = s.replace("%", "");
+                return Double.parseDouble(numberStr);
+            };
+            Map<String, Double> averageByCategory = accuracyPassRateList.stream()
+                    .filter(apr -> apr.getForecastModel()!=null)
+                    .collect(Collectors.groupingBy(
+                            AccuracyPassRate::getForecastModel,
+                            Collectors.averagingDouble(apr -> stringToDouble.apply(apr.getAccuracy()))
+                    ));
+
+            sortMap = new TreeMap<>(new Comparator<String>() {
+                @Override
+                public int compare(String o1, String o2) {
+                    return (int) (averageByCategory.get(o2) - averageByCategory.get(o1));
+                }
+            });
+            sortMap.putAll(averageByCategory);
+
+        }
+        return sortMap;
+    }
+
+    @Override
+    public List<Map<String,String>> finfBySingleMonthBetweenAndForecastTypeAndStationCode(Long startTime, Long endTime, ForecastTypeEnum forecastType, DataSourcesEnum dataSources, String stationCode,String stationName,String uploadDataSources) {
+        List<Map<String,String>> list = new ArrayList<>();
+        QueryWrapper<AccuracyPassRate> wrapper = new QueryWrapper<>();
+        QueryWrapper<AccuracyPassRate> wrapper2 = new QueryWrapper<>();
+        List<long[]> longs = StartAndEndMonthUtil.singleMonth(startTime, endTime);
+        Collections.reverse(longs);
+        if (stationCode != null && !stationCode.equals("")) {
+            wrapper.eq("station_code", stationCode);
+            wrapper2.eq("station_code", stationCode);
+        }
+
+        if (forecastType != null && !forecastType.equals("")) {
+            wrapper.eq("forecast_type", forecastType);
+            wrapper2.eq("forecast_type", forecastType);
+        }
+        if (dataSources != null && !dataSources.equals("")) {
+            wrapper.eq("data_sources", dataSources);
+            wrapper2.eq("data_sources", uploadDataSources);
+        }
+        for (long[] aLong : longs) {
+            int count = 0;
+            if (startTime != null && endTime != null) {
+                wrapper.between("time", new Date(aLong[0]), new Date(aLong[1]));
+                wrapper.orderByDesc("time");
+                wrapper2.between("time", new Date(aLong[0]), new Date(aLong[1]));
+                wrapper2.orderByDesc("time");
+            }
+            List<AccuracyPassRate> accuracyPassRateList = baseMapper.selectList(wrapper);
+            List<AccuracyPassRate> accuracyPassRateList2 = baseMapper.selectList(wrapper2);
+
+            accuracyPassRateList = accuracyPassRateList.stream().filter(f -> !"无可用数据计算".equals(f.getAccuracy()) && !"无计算公式".equals(f.getAccuracy())).collect(Collectors.toList());
+            accuracyPassRateList2 = accuracyPassRateList2.stream().filter(f -> !"无可用数据计算".equals(f.getAccuracy()) && !"无计算公式".equals(f.getAccuracy())).collect(Collectors.toList());
+            BigDecimal collect = BigDecimal.valueOf(accuracyPassRateList2.stream().collect(Collectors.summingDouble(s -> Double.valueOf(s.getAccuracy().replace("%", ""))))/accuracyPassRateList2.size()).setScale(2, RoundingMode.HALF_UP);
+
+            Function<String, Double> stringToDouble = s -> {
+                String numberStr = s.replace("%", "");
+                return Double.parseDouble(numberStr);
+            };
+            Map<String, Double> averageByCategory = accuracyPassRateList.stream()
+                    .filter(apr -> apr.getForecastModel()!=null)
+                    .collect(Collectors.groupingBy(
+                            AccuracyPassRate::getForecastModel,
+                            Collectors.averagingDouble(apr -> stringToDouble.apply(apr.getAccuracy()))
+                    ));
+
+            Map<String, Double> sortMap = new TreeMap<>();
+            sortMap = new TreeMap<>(new Comparator<String>() {
+                @Override
+                public int compare(String o1, String o2) {
+                    return (int) (averageByCategory.get(o2) - averageByCategory.get(o1));
+                }
+            });
+            sortMap.putAll(averageByCategory);
+
+            for (Map.Entry<String, Double> entry : sortMap.entrySet()) {
+                Map<String,String> map = new HashMap<>();
+                map.put("station",stationName);
+                map.put("month",new SimpleDateFormat("yyyy-MM").format(new Date(aLong[0])));
+                map.put("preModels",entry.getKey());
+                map.put("shortAccuracy",BigDecimal.valueOf(entry.getValue()).setScale(2, RoundingMode.HALF_UP).toString());
+                map.put("uploadShortAccuracy",collect.toString());
+                map.put("accuracyRanking",String.valueOf(++count));
+                list.add(map);
+            }
+
+        }
+        return list;
+    }
 }

+ 63 - 0
cpp-admin/src/main/java/com/cpp/web/utils/StartAndEndMonthUtil.java

@@ -0,0 +1,63 @@
+package com.cpp.web.utils;
+
+import java.time.Instant;
+import java.time.LocalDate;
+import java.time.ZoneId;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
+public class StartAndEndMonthUtil {
+    public static List<long[]> singleMonth(Long startTime, Long endTime){
+        LocalDate startDate = new Date(startTime).toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
+        LocalDate endDate = new Date(endTime).toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
+
+        List<long[]> segments = new ArrayList<>();
+
+        while (startDate.isBefore(endDate) || startDate.isEqual(endDate)) {
+            LocalDate nextMonth = startDate.plusMonths(1);
+            long startSegment = startDate.atStartOfDay(ZoneId.systemDefault()).toInstant().toEpochMilli();
+            long endSegment = nextMonth.atStartOfDay(ZoneId.systemDefault()).toInstant().toEpochMilli();
+
+            // 确保结束时间不超过原始的结束时间
+            if (endSegment > endTime) {
+                endSegment = getBeginAndEndMonth(endTime,2);
+            }
+
+            segments.add(new long[]{startSegment, endSegment});
+
+            startDate = nextMonth;
+        }
+        // 输出分段结果
+        for (long[] segment : segments) {
+//            System.out.println("Start: " + segment[0] + ", End: " + segment[1]);
+            System.out.println(segment);
+        }
+        return segments;
+    }
+
+    public static Long getBeginAndEndMonth(Long temp,Integer type){
+        long newTemp = 0;
+        // 将时间戳转换为 LocalDate
+        LocalDate date = Instant.ofEpochMilli(temp)
+                .atZone(ZoneId.systemDefault())
+                .toLocalDate();
+        if (type == 1){
+            // 获取该月的第一天
+            LocalDate firstDayOfMonth = date.withDayOfMonth(1);
+
+            // 将 LocalDate 转换回时间戳
+            newTemp = firstDayOfMonth.atStartOfDay(ZoneId.systemDefault()).toInstant().toEpochMilli();
+        }else {
+            // 获取该月的最后一天
+            LocalDate lastDayOfMonth = date.withDayOfMonth(date.lengthOfMonth());
+
+            // 将 LocalDate 转换回时间戳
+            newTemp = lastDayOfMonth.atTime(23, 59, 59, 999999999)
+                    .atZone(ZoneId.systemDefault())
+                    .toInstant()
+                    .toEpochMilli();
+        }
+        return newTemp;
+    }
+}

+ 304 - 0
cpp-ui/src/views/regulation/modelAccuracyStatistics/index.vue

@@ -0,0 +1,304 @@
+<template>
+  <div class="app-container">
+    <el-tabs v-model="activeName" type="card" @tab-click="handleClick">
+      <el-tab-pane label="短期" name="dq"></el-tab-pane>
+      <el-tab-pane label="超短期" name="cdq"></el-tab-pane>
+    </el-tabs>
+    <div class="dark-el-input dark-el-button">
+      <el-form ref="queryForm" size="small" :inline="true" popper-class="cpp-popper">
+        <el-form-item label="时间范围">
+          <el-date-picker
+              :clearable="false"
+              v-model="dateTime"
+              type="monthrange"
+              range-separator="至"
+              :picker-options="expireDateOption"
+              :unlink-panels="true"
+              @change="onchange"
+              format="yyyy-MM"
+              placeholder="选择生成日期"
+              popper-class="cpp-popper">
+          </el-date-picker>
+        </el-form-item>
+        <el-form-item label="场站名称">
+          <el-select v-model="stationCode" placeholder="请选择" popper-class="cpp-popper" @change="onchange">
+            <el-option
+                v-for="item in stationList"
+                :key="item.value"
+                :label="item.label"
+                :value="item.value">
+            </el-option>
+          </el-select>
+        </el-form-item>
+        <el-form-item>
+          <el-button type="primary" size="small" style="margin-left: 5px" icon="el-icon-search" @click="dataQuery">
+            查询
+          </el-button>
+          <el-button type="primary" size="small" style="margin-left: 5px" icon="el-icon-download" @click="exportFile">
+            导出
+          </el-button>
+        </el-form-item>
+      </el-form>
+    </div>
+    <div id="masChart" style="width: 100%;height: 450px"></div>
+    <div v-if="activeName == 'dq'">
+      <vxe-table  ref="shortTermTable"  border v-loading="loading" :data="tableData" class="custom-table" max-height="300px"
+                element-loading-background="rgba(8, 61, 92,1)">
+        <vxe-table-column title="场站" align="center" field="station"/>
+        <vxe-table-column title="月份" align="center" field="month" :formatter="monthFormatter"/>
+        <vxe-table-column title="预测模型" align="center" field="preModels"/>
+        <vxe-table-column title="短期准确率(%)" align="center" field="shortAccuracy"/>
+        <vxe-table-column title="上报文件短期准确率(%)" align="center" field="uploadShortAccuracy"></vxe-table-column>
+        <vxe-table-column title="准确率排名" align="center" field="accuracyRanking"/>
+      </vxe-table>
+    </div>
+    <div v-else>
+      <vxe-table ref="ultraShortTermTable" border v-loading="loading" :data="tableData" class="custom-table" max-height="300px"
+                element-loading-background="rgba(8, 61, 92,1)">
+        <vxe-table-column title="场站" align="center" field="station"/>
+        <vxe-table-column title="月份" align="center" field="month" :formatter="monthFormatter"/>
+        <vxe-table-column title="预测模型" align="center" field="preModels"/>
+        <vxe-table-column title="超短期准确率(%)" align="center" field="shortAccuracy"/>
+        <vxe-table-column title="上报文件超短期准确率(%)" align="center" field="uploadShortAccuracy"></vxe-table-column>
+        <vxe-table-column title="准确率排名" align="center" field="accuracyRanking"/>
+      </vxe-table>
+    </div>
+  </div>
+</template>
+
+
+<script>
+import * as echarts from "echarts";
+
+export default {
+  name: 'inverterinfo',
+  data() {
+    return {
+      activeName:'dq',
+      stationName:'',
+      showDeleteButton:true,
+      expireDateOption: {
+        disabledDate(time) {
+          const today = new Date();
+          const threeYearDate = new Date(today)
+          threeYearDate.setFullYear(today.getFullYear() - 3)
+          return time.getTime()<threeYearDate.getTime() || time.getTime()>today.getTime()
+        }
+      },
+      loading: false,
+      xData:[],
+      yData:[],
+      tableData: [],
+      dataS:'E2',
+      dataS4:'E4',
+      form: [],
+      dateTime: [ new Date(new Date().getFullYear(), new Date().getMonth(), 1).getTime(), new Date(new Date().getFullYear(), new Date().getMonth() + 1, 0, 23, 59, 59, 999).getTime()],
+      stationList: [],
+      stationCode: '',
+      forecastType: "dq",
+    }
+  },
+   created() {
+    this.getStationCode()
+  },
+  mounted() {},
+  beforeDestroy() {
+    if (!this.chart) {
+      return
+    }
+    this.chart.dispose()
+    this.chart = null
+  },
+  computed: {},
+  methods: {
+    monthFormatter({cellValue, row, column}){
+      return cellValue;
+    },
+    onchange(){
+      this.dataQuery()
+    },
+    handleClick(tab, event){
+      this.forecastType = tab.name
+      this.dataQuery()
+    },
+    exportFile(){
+      const modifyData = this.tableData.map(item=>({
+        ...item,
+        month:item.month.substring(0,4)+"年"+item.month.substring(5,item.month.length)+"月"
+      }))
+      let startMonth = new Date(this.dateTime[0]).getFullYear() + '-' +(new Date(this.dateTime[0]).getMonth()+1)+'月'
+      let endMonth = new Date(this.dateTime[1]).getFullYear() + '-' +(new Date(this.dateTime[1]).getMonth()+1)+'月'
+      if (this.activeName == 'dq'){
+        this.$refs.shortTermTable.exportData({
+          filename: startMonth==endMonth?startMonth+'短期模型准确率统计':startMonth+'至'+endMonth+'短期模型准确率统计',
+          type: 'csv',
+          isHeader: true,
+          isFooter: true,
+          data:modifyData
+        })
+      }else if (this.activeName == 'cdq'){
+        this.$refs.ultraShortTermTable.exportData({
+          filename: startMonth==endMonth?startMonth+'超短期模型准确率统计':startMonth+'至'+endMonth+'超短期模型准确率统计',
+          type: 'csv',
+          isHeader: true,
+          isFooter: true,
+          data:modifyData
+        })
+      }
+    },
+    dataQuery(){
+      var find = this.stationList.find(s=>s.value == this.stationCode);
+      this.stationName = find.label
+      this.xData = []
+      this.yData = []
+      let startTime = Math.round(this.dateTime[0])
+      let endTime = Math.round(this.dateTime[1])
+      let queryParams = {
+        "stationCode": this.stationCode,
+        "startTime": startTime,
+        "endTime": endTime,
+        "forecastType": this.forecastType,
+        "dataSources": 'E2',
+        "uploadDataSources": 'E4',
+        "stationName":this.stationName
+      }
+      this.$axios.get('/accuracyPassRate/getByMonthBetweenAndForecastTypeAndStationCode', {params: queryParams}).then(response => {
+        if (response.data){
+          for (const [key,value] of Object.entries(response.data)){
+            this.xData.push(key)
+            this.yData.push(value.toFixed(2))
+          }
+        }
+      }).catch(() => {}).finally(()=>{
+        this.initChart()
+      })
+      this.$axios.get('/accuracyPassRate/getBySingleMonthBetweenAndForecastTypeAndStationCode', {params: queryParams}).then(response => {
+        if (response.data){
+          this.tableData = response.data || []
+        }
+      })
+    },
+    getStationCode() {
+      this.$axios({url: '/electricfield/all', method: 'get'}).then(response => {
+        this.stationList = response.data
+        if (this.stationList.length > 0) {
+          this.stationCode = this.stationList[0].value
+        }
+        this.dataQuery()
+      })
+    },
+    initChart() {
+      var chartDom = document.getElementById('masChart');
+      this.chart = echarts.init(chartDom);
+      this.chart.setOption({
+        title: {
+          top: 20,
+          text: '综合准确率',
+          textStyle: {
+            color:'#fff',
+            fontWeight: 'normal',
+            fontSize: 16,
+          },
+          left: 'center'
+        },
+        tooltip: {
+          trigger: 'axis',
+          axisPointer: {
+            type: 'shadow'
+          }
+        },
+        grid: {
+          left: '3%',
+          right: '4%',
+          bottom: '3%',
+          containLabel: true
+        },
+        xAxis: [
+          {
+            type: 'category',
+            data: this.xData,
+            axisTick: {
+              alignWithLabel: true
+            },
+            axisLabel: {
+              textStyle: {
+                color: '#ffffff',
+                fontWeight: 500,
+                fontSize: '14'
+              },
+            },
+          }
+        ],
+        yAxis: [
+          {
+            name: '%',
+            nameTextStyle: {
+              color: '#ffffff',
+              fontSize: 12,
+              padding: 10
+            },
+            type: 'value',
+            axisLabel: {
+              textStyle: {
+                color: '#ffffff',
+                fontWeight: 500,
+                fontSize: '14'
+              },
+            },
+          }
+        ],
+        series: [
+          {
+            // name: 'Direct',
+            type: 'bar',
+            barWidth: '60%',
+            data: this.yData,
+            markLine:{
+              data:[
+                {
+                  silent:true,
+                  yAxis:65,
+                  label:{
+                    position:'end',
+                    formatter:'65',
+                    color:'#4d69da',
+                  },
+                  lineStyle:{
+                    type:'dashed',
+                    color:'#3c73ea',
+                    width:1
+                  }
+                },
+              ]
+            },
+          }
+        ]
+      })
+    },
+  }
+}
+</script>
+<style scoped>
+::v-deep .el-tabs__nav-scroll{
+  display: flex;
+  justify-content: center;
+  width: 50% !important;
+  margin: 0 auto !important;
+}
+
+/deep/ .reg-config-btu .el-button:first-child {
+  margin-top: 5px;
+}
+
+/deep/ .reg-config-btu .el-button {
+  margin-top: 10px;
+}
+
+/deep/ .reg-config-btu .el-button:last-child {
+  margin-bottom: 10px;
+}
+
+/deep/ .reg-config-btu .el-button + .el-button {
+  margin-left: 0px;
+}
+</style>