|
|
@@ -1,10 +1,13 @@
|
|
|
package com.ufo.project.forecast.mediumterm.power.controller;
|
|
|
|
|
|
+import java.math.BigDecimal;
|
|
|
+import java.math.RoundingMode;
|
|
|
import java.util.ArrayList;
|
|
|
-import java.util.Comparator;
|
|
|
+import java.util.HashMap;
|
|
|
+import java.util.LinkedHashMap;
|
|
|
import java.util.List;
|
|
|
import java.util.Map;
|
|
|
-import java.util.TreeMap;
|
|
|
+import java.util.TreeSet;
|
|
|
import javax.servlet.http.HttpServletResponse;
|
|
|
import org.springframework.security.access.prepost.PreAuthorize;
|
|
|
import org.springframework.beans.factory.annotation.Autowired;
|
|
|
@@ -15,11 +18,18 @@ import org.springframework.web.bind.annotation.DeleteMapping;
|
|
|
import org.springframework.web.bind.annotation.PathVariable;
|
|
|
import org.springframework.web.bind.annotation.RequestBody;
|
|
|
import org.springframework.web.bind.annotation.RequestMapping;
|
|
|
+import org.springframework.web.bind.annotation.RequestParam;
|
|
|
import org.springframework.web.bind.annotation.RestController;
|
|
|
+import com.ufo.base.common.utils.TimeUtils;
|
|
|
import com.ufo.base.framework.aspectj.lang.annotation.Log;
|
|
|
import com.ufo.base.framework.aspectj.lang.enums.BusinessType;
|
|
|
import com.ufo.project.forecast.mediumterm.power.domain.DataMediumTermPowerForecastCorrect;
|
|
|
+import com.ufo.project.forecast.mediumterm.power.domain.DataMediumTermPowerForecastReport;
|
|
|
+import com.ufo.project.forecast.mediumterm.power.mapper.DataMediumTermPowerForecastCorrectMapper;
|
|
|
+import com.ufo.project.forecast.mediumterm.power.mapper.DataMediumTermPowerForecastReportMapper;
|
|
|
import com.ufo.project.forecast.mediumterm.power.service.IDataMediumTermPowerForecastCorrectService;
|
|
|
+import com.ufo.project.model.domain.DataElectricityLocal;
|
|
|
+import com.ufo.project.model.service.IDataElectricityLocalService;
|
|
|
import com.ufo.base.framework.web.controller.BaseController;
|
|
|
import com.ufo.base.framework.web.domain.AjaxResult;
|
|
|
import com.ufo.base.common.utils.poi.ExcelUtil;
|
|
|
@@ -27,7 +37,7 @@ import com.ufo.base.framework.web.page.TableDataInfo;
|
|
|
|
|
|
/**
|
|
|
* 中期功率修正数据Controller
|
|
|
- *
|
|
|
+ *
|
|
|
* @author ufo
|
|
|
* @date 2026-05-05
|
|
|
*/
|
|
|
@@ -38,6 +48,15 @@ public class DataMediumTermPowerForecastCorrectController extends BaseController
|
|
|
@Autowired
|
|
|
private IDataMediumTermPowerForecastCorrectService dataMediumTermPowerForecastCorrectService;
|
|
|
|
|
|
+ @Autowired
|
|
|
+ private DataMediumTermPowerForecastReportMapper dataMediumTermPowerForecastReportMapper;
|
|
|
+
|
|
|
+ @Autowired
|
|
|
+ private DataMediumTermPowerForecastCorrectMapper dataMediumTermPowerForecastCorrectMapper;
|
|
|
+
|
|
|
+ @Autowired
|
|
|
+ private IDataElectricityLocalService dataElectricityLocalService;
|
|
|
+
|
|
|
/**
|
|
|
* 查询中期功率修正数据列表
|
|
|
*/
|
|
|
@@ -50,12 +69,67 @@ public class DataMediumTermPowerForecastCorrectController extends BaseController
|
|
|
return getDataTable(list);
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * 查询中期功率预测图表数据(上报/修正/观测三条曲线, 时间点取三者并集, 缺失补0)
|
|
|
+ */
|
|
|
@PreAuthorize("@ss.hasPermi('mediumterm:powerForecastCorrect:list')")
|
|
|
@GetMapping("/chart")
|
|
|
- public TableDataInfo chart(DataMediumTermPowerForecastCorrect dataMediumTermPowerForecastCorrect)
|
|
|
+ public AjaxResult chart(@RequestParam("stationId") Long stationId,
|
|
|
+ @RequestParam("forecastStartTime") Long forecastStartTime,
|
|
|
+ @RequestParam("forecastEndTime") Long forecastEndTime)
|
|
|
{
|
|
|
- List<DataMediumTermPowerForecastCorrect> list = this.queryList(dataMediumTermPowerForecastCorrect);
|
|
|
- return getDataTable(list);
|
|
|
+ String entityId = String.valueOf(stationId);
|
|
|
+
|
|
|
+ List<DataMediumTermPowerForecastReport> reportList = dataMediumTermPowerForecastReportMapper.selectLatestByWindow(entityId, forecastStartTime, forecastEndTime);
|
|
|
+ Map<Long, Double> reportPowerMap = new HashMap<>();
|
|
|
+ if (reportList != null)
|
|
|
+ {
|
|
|
+ for (DataMediumTermPowerForecastReport item : reportList)
|
|
|
+ {
|
|
|
+ if (item.getForecastTime() != null)
|
|
|
+ {
|
|
|
+ reportPowerMap.put(item.getForecastTime(), parsePower(item.getPower()));
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ List<DataMediumTermPowerForecastCorrect> correctList = dataMediumTermPowerForecastCorrectMapper.selectLatestByWindow(entityId, forecastStartTime, forecastEndTime);
|
|
|
+ Map<Long, Double> correctPowerMap = new HashMap<>();
|
|
|
+ if (correctList != null)
|
|
|
+ {
|
|
|
+ for (DataMediumTermPowerForecastCorrect item : correctList)
|
|
|
+ {
|
|
|
+ if (item.getForecastTime() != null)
|
|
|
+ {
|
|
|
+ correctPowerMap.put(item.getForecastTime(), parsePower(item.getPower()));
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ Map<Long, Double> monitorPowerMap = new HashMap<>();
|
|
|
+ List<DataElectricityLocal> localList = dataElectricityLocalService.selectActualPowerList(
|
|
|
+ stationId, TimeUtils.utcToGmt8Format(forecastStartTime), TimeUtils.utcToGmt8Format(forecastEndTime));
|
|
|
+ if (localList != null)
|
|
|
+ {
|
|
|
+ for (DataElectricityLocal item : localList)
|
|
|
+ {
|
|
|
+ if (item.getTm() != null)
|
|
|
+ {
|
|
|
+ monitorPowerMap.put(item.getTm().getTime() / 1000, item.getActualPower() == null ? 0.0 : item.getActualPower());
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ TreeSet<Long> times = new TreeSet<>();
|
|
|
+ times.addAll(reportPowerMap.keySet());
|
|
|
+ times.addAll(correctPowerMap.keySet());
|
|
|
+ times.addAll(monitorPowerMap.keySet());
|
|
|
+
|
|
|
+ Map<String, Object> body = new LinkedHashMap<>();
|
|
|
+ body.put("report", buildSeries(times, reportPowerMap));
|
|
|
+ body.put("correct", buildSeries(times, correctPowerMap));
|
|
|
+ body.put("monitor", buildSeries(times, monitorPowerMap));
|
|
|
+ return AjaxResult.success(body);
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
@@ -64,36 +138,161 @@ public class DataMediumTermPowerForecastCorrectController extends BaseController
|
|
|
@PreAuthorize("@ss.hasPermi('mediumterm:powerForecastCorrect:export')")
|
|
|
@Log(title = "中期功率修正数据", businessType = BusinessType.EXPORT)
|
|
|
@PostMapping("/export/excel")
|
|
|
- public void export(HttpServletResponse response, DataMediumTermPowerForecastCorrect dataMediumTermPowerForecastCorrect)
|
|
|
+ public void export(HttpServletResponse response, @RequestParam("stationId") Long stationId,
|
|
|
+ @RequestParam("forecastStartTime") Long forecastStartTime,
|
|
|
+ @RequestParam("forecastEndTime") Long forecastEndTime)
|
|
|
{
|
|
|
- List<DataMediumTermPowerForecastCorrect> list = this.queryList(dataMediumTermPowerForecastCorrect);
|
|
|
+ List<DataMediumTermPowerForecastCorrect> list = dataMediumTermPowerForecastCorrectMapper.selectLatestByWindow(
|
|
|
+ String.valueOf(stationId), forecastStartTime, forecastEndTime);
|
|
|
ExcelUtil<DataMediumTermPowerForecastCorrect> util = new ExcelUtil<DataMediumTermPowerForecastCorrect>(DataMediumTermPowerForecastCorrect.class);
|
|
|
util.exportExcel(response, list, "中期功率修正数据数据");
|
|
|
}
|
|
|
|
|
|
- private List<DataMediumTermPowerForecastCorrect> queryList(DataMediumTermPowerForecastCorrect dataMediumTermPowerForecastCorrect)
|
|
|
+ /**
|
|
|
+ * 系数调整: 以窗口内上报数据为基准, 按系数生成/更新修正数据
|
|
|
+ */
|
|
|
+ @PreAuthorize("@ss.hasPermi('mediumterm:powerForecastCorrect:edit')")
|
|
|
+ @Log(title = "中期功率修正数据", businessType = BusinessType.UPDATE)
|
|
|
+ @PostMapping("/multiplierUpdate")
|
|
|
+ public AjaxResult multiplierUpdate(@RequestParam("stationId") Long stationId,
|
|
|
+ @RequestParam("forecastStartTime") Long forecastStartTime,
|
|
|
+ @RequestParam("forecastEndTime") Long forecastEndTime,
|
|
|
+ @RequestParam("multiplier") Double multiplier)
|
|
|
{
|
|
|
- List<DataMediumTermPowerForecastCorrect> list = dataMediumTermPowerForecastCorrectService.selectDataMediumTermPowerForecastCorrectList(dataMediumTermPowerForecastCorrect);
|
|
|
- if (list == null || list.isEmpty())
|
|
|
+ if (multiplier == null || multiplier < 0.01 || multiplier > 100.00)
|
|
|
{
|
|
|
- return list;
|
|
|
+ return AjaxResult.error("调整系数必须在0.01-100.00之间");
|
|
|
}
|
|
|
-
|
|
|
- // 按 forecast_time 分组,每组按 start_time desc, id desc 取第一条(即同一时间点最新起报的数据)
|
|
|
- Comparator<DataMediumTermPowerForecastCorrect> latestFirst = Comparator
|
|
|
- .comparing(DataMediumTermPowerForecastCorrect::getStartTime, Comparator.nullsFirst(Comparator.<Long>naturalOrder()))
|
|
|
- .thenComparing(DataMediumTermPowerForecastCorrect::getId, Comparator.nullsFirst(Comparator.<Long>naturalOrder()));
|
|
|
- Map<Long, DataMediumTermPowerForecastCorrect> latestByForecastTime = new TreeMap<>();
|
|
|
- for (DataMediumTermPowerForecastCorrect item : list)
|
|
|
+ String entityId = String.valueOf(stationId);
|
|
|
+ List<DataMediumTermPowerForecastReport> reportList = dataMediumTermPowerForecastReportMapper.selectLatestByWindow(entityId, forecastStartTime, forecastEndTime);
|
|
|
+ if (reportList == null || reportList.isEmpty())
|
|
|
{
|
|
|
- if (item.getForecastTime() == null)
|
|
|
+ return AjaxResult.error("窗口内无上报数据,无法调整");
|
|
|
+ }
|
|
|
+ BigDecimal factor = BigDecimal.valueOf(multiplier);
|
|
|
+ for (DataMediumTermPowerForecastReport report : reportList)
|
|
|
+ {
|
|
|
+ if (report.getForecastTime() == null)
|
|
|
{
|
|
|
continue;
|
|
|
}
|
|
|
- latestByForecastTime.merge(item.getForecastTime(), item,
|
|
|
- (existing, candidate) -> latestFirst.compare(candidate, existing) > 0 ? candidate : existing);
|
|
|
+ String newPower = multiplyPower(report.getPower(), factor);
|
|
|
+ DataMediumTermPowerForecastCorrect existing = dataMediumTermPowerForecastCorrectMapper.selectLatestByEntityAndForecastTime(entityId, report.getForecastTime());
|
|
|
+ if (existing != null)
|
|
|
+ {
|
|
|
+ long newStartTime = report.getStartTime() != null
|
|
|
+ && (existing.getStartTime() == null || existing.getStartTime() < report.getStartTime())
|
|
|
+ ? report.getStartTime() : existing.getStartTime();
|
|
|
+ existing.setPower(newPower);
|
|
|
+ existing.setStartTime(newStartTime);
|
|
|
+ dataMediumTermPowerForecastCorrectMapper.updateDataMediumTermPowerForecastCorrect(existing);
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ DataMediumTermPowerForecastCorrect row = new DataMediumTermPowerForecastCorrect();
|
|
|
+ row.setDataSourcegrabId(report.getDataSourcegrabId());
|
|
|
+ row.setDataSourcegrabPatternName(report.getDataSourcegrabPatternName());
|
|
|
+ row.setEntityId(report.getEntityId());
|
|
|
+ row.setStationIds(report.getStationIds());
|
|
|
+ row.setEntityType(report.getEntityType());
|
|
|
+ row.setEntityName(report.getEntityName());
|
|
|
+ row.setPredays(report.getPredays());
|
|
|
+ row.setStartTime(report.getStartTime());
|
|
|
+ row.setForecastTime(report.getForecastTime());
|
|
|
+ row.setPower(newPower);
|
|
|
+ dataMediumTermPowerForecastCorrectMapper.insertDataMediumTermPowerForecastCorrect(row);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return AjaxResult.success();
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 单点修正: 更新指定预测时刻的修正功率(无修正数据时以上报数据为模板新增)
|
|
|
+ */
|
|
|
+ @PreAuthorize("@ss.hasPermi('mediumterm:powerForecastCorrect:edit')")
|
|
|
+ @Log(title = "中期功率修正数据", businessType = BusinessType.UPDATE)
|
|
|
+ @PostMapping("/update")
|
|
|
+ public AjaxResult update(@RequestBody DataMediumTermPowerForecastCorrect dataMediumTermPowerForecastCorrect)
|
|
|
+ {
|
|
|
+ String entityId = dataMediumTermPowerForecastCorrect.getEntityId();
|
|
|
+ Long forecastTime = dataMediumTermPowerForecastCorrect.getForecastTime();
|
|
|
+ String power = dataMediumTermPowerForecastCorrect.getPower();
|
|
|
+ if (entityId == null || entityId.isEmpty() || forecastTime == null || power == null || power.trim().isEmpty())
|
|
|
+ {
|
|
|
+ return AjaxResult.error("参数不完整");
|
|
|
+ }
|
|
|
+ try
|
|
|
+ {
|
|
|
+ Double.parseDouble(power.trim());
|
|
|
+ }
|
|
|
+ catch (NumberFormatException e)
|
|
|
+ {
|
|
|
+ return AjaxResult.error("修正功率必须为数字");
|
|
|
+ }
|
|
|
+
|
|
|
+ DataMediumTermPowerForecastCorrect existing = dataMediumTermPowerForecastCorrectMapper.selectLatestByEntityAndForecastTime(entityId, forecastTime);
|
|
|
+ if (existing != null)
|
|
|
+ {
|
|
|
+ existing.setPower(power.trim());
|
|
|
+ dataMediumTermPowerForecastCorrectMapper.updateDataMediumTermPowerForecastCorrect(existing);
|
|
|
+ return AjaxResult.success();
|
|
|
+ }
|
|
|
+
|
|
|
+ // 无修正数据时, 取该时刻最新一条上报数据作为新增模板
|
|
|
+ List<DataMediumTermPowerForecastReport> reportList = dataMediumTermPowerForecastReportMapper.selectLatestByWindow(entityId, forecastTime - 1, forecastTime);
|
|
|
+ DataMediumTermPowerForecastReport report = (reportList == null || reportList.isEmpty()) ? null : reportList.get(0);
|
|
|
+ if (report == null)
|
|
|
+ {
|
|
|
+ return AjaxResult.error("未找到对应时刻的上报数据,无法新增修正数据");
|
|
|
+ }
|
|
|
+ DataMediumTermPowerForecastCorrect row = new DataMediumTermPowerForecastCorrect();
|
|
|
+ row.setDataSourcegrabId(report.getDataSourcegrabId());
|
|
|
+ row.setDataSourcegrabPatternName(report.getDataSourcegrabPatternName());
|
|
|
+ row.setEntityId(report.getEntityId());
|
|
|
+ row.setStationIds(report.getStationIds());
|
|
|
+ row.setEntityType(report.getEntityType());
|
|
|
+ row.setEntityName(report.getEntityName());
|
|
|
+ row.setPredays(report.getPredays());
|
|
|
+ row.setStartTime(System.currentTimeMillis() / 1000);
|
|
|
+ row.setForecastTime(forecastTime);
|
|
|
+ row.setPower(power.trim());
|
|
|
+ dataMediumTermPowerForecastCorrectMapper.insertDataMediumTermPowerForecastCorrect(row);
|
|
|
+ return AjaxResult.success();
|
|
|
+ }
|
|
|
+
|
|
|
+ private double parsePower(String rawPower)
|
|
|
+ {
|
|
|
+ if (rawPower == null || rawPower.trim().isEmpty())
|
|
|
+ {
|
|
|
+ return 0.0;
|
|
|
+ }
|
|
|
+ try
|
|
|
+ {
|
|
|
+ return Double.parseDouble(rawPower.trim());
|
|
|
+ }
|
|
|
+ catch (NumberFormatException e)
|
|
|
+ {
|
|
|
+ return 0.0;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private String multiplyPower(String rawPower, BigDecimal factor)
|
|
|
+ {
|
|
|
+ return BigDecimal.valueOf(parsePower(rawPower)).multiply(factor)
|
|
|
+ .setScale(2, RoundingMode.HALF_UP).toPlainString();
|
|
|
+ }
|
|
|
+
|
|
|
+ private List<Map<String, Object>> buildSeries(TreeSet<Long> times, Map<Long, Double> powerMap)
|
|
|
+ {
|
|
|
+ List<Map<String, Object>> series = new ArrayList<>();
|
|
|
+ for (Long time : times)
|
|
|
+ {
|
|
|
+ Map<String, Object> point = new LinkedHashMap<>();
|
|
|
+ point.put("time", TimeUtils.utcToGmt8Format(time));
|
|
|
+ point.put("power", powerMap.getOrDefault(time, 0.0));
|
|
|
+ series.add(point);
|
|
|
}
|
|
|
- return new ArrayList<>(latestByForecastTime.values());
|
|
|
+ return series;
|
|
|
}
|
|
|
|
|
|
/**
|