Browse Source

[0917] 短期功率预测时区修复

taosj 1 month ago
parent
commit
9f0c014844

+ 59 - 0
forecast-backend-server/src/main/java/com/ufo/base/common/utils/TimeUtils.java

@@ -0,0 +1,59 @@
+package com.ufo.base.common.utils;
+
+import java.time.Instant;
+import java.time.ZoneId;
+import java.time.ZonedDateTime;
+import java.time.format.DateTimeFormatter;
+
+/**
+ * 时区转换工具类
+ * 系统数据库统一使用 UTC unix 秒存储时间,展示时使用 GMT+8(北京时间)
+ *
+ * @author ufo
+ */
+public class TimeUtils
+{
+    /** GMT+8 zone */
+    private static final ZoneId ZONE_GMT8 = ZoneId.of("GMT+8");
+
+    /** GMT+8 偏移秒数 */
+    private static final long GMT8_OFFSET_SECONDS = 8L * 3600;
+
+    /** 默认日期格式 */
+    private static final DateTimeFormatter DEFAULT_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
+
+    /**
+     * 将 GMT+8 时间(unix 秒)转换为 UTC 时间(unix 秒)
+     *
+     * @param gmt8Seconds GMT+8 时间 unix 秒
+     * @return UTC 时间 unix 秒
+     */
+    public static long gmt8ToUtc(long gmt8Seconds)
+    {
+        return gmt8Seconds - GMT8_OFFSET_SECONDS;
+    }
+
+    /**
+     * 将 UTC 时间(unix 秒)格式化为 GMT+8 日期字符串
+     *
+     * @param utcSeconds UTC 时间 unix 秒
+     * @return GMT+8 格式日期字符串,默认 yyyy-MM-dd HH:mm:ss
+     */
+    public static String utcToGmt8Format(long utcSeconds)
+    {
+        return utcToGmt8Format(utcSeconds, DEFAULT_FORMATTER);
+    }
+
+    /**
+     * 将 UTC 时间(unix 秒)格式化为 GMT+8 日期字符串
+     *
+     * @param utcSeconds UTC 时间 unix 秒
+     * @param formatter 日期格式
+     * @return GMT+8 格式日期字符串
+     */
+    public static String utcToGmt8Format(long utcSeconds, DateTimeFormatter formatter)
+    {
+        return ZonedDateTime.ofInstant(Instant.ofEpochSecond(utcSeconds), ZONE_GMT8)
+                .format(formatter);
+    }
+}

+ 47 - 7
forecast-backend-server/src/main/java/com/ufo/project/forecast/shortterm/power/controller/ShortTermPowerForecastingController.java

@@ -2,8 +2,13 @@ package com.ufo.project.forecast.shortterm.power.controller;
 
 import java.io.IOException;
 import java.net.URLEncoder;
+import java.math.BigDecimal;
+import java.math.RoundingMode;
 import java.util.ArrayList;
+import java.util.LinkedHashMap;
 import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
 import javax.servlet.http.HttpServletResponse;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.security.access.prepost.PreAuthorize;
@@ -13,6 +18,7 @@ import org.springframework.web.bind.annotation.RequestParam;
 import org.springframework.web.bind.annotation.RestController;
 import com.alibaba.excel.EasyExcel;
 import com.ufo.base.common.utils.DateUtils;
+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.base.framework.web.controller.BaseController;
@@ -82,20 +88,53 @@ public class ShortTermPowerForecastingController extends BaseController
         long now = System.currentTimeMillis() / 1000L;
         long end = now + (long) forecastHours * 3600L;
         List<DataShortTermPowerForecastIssued> issuedList = dataShortTermPowerForecastIssuedService
-                .selectByTimeRange(stationId.toString(), now, end);
-        if (issuedList == null)
+                .selectByTimeRange(stationId.toString(), TimeUtils.gmt8ToUtc(now), TimeUtils.gmt8ToUtc(end));
+        if (issuedList == null || issuedList.isEmpty())
         {
             return result;
         }
-        for (DataShortTermPowerForecastIssued issued : issuedList)
+
+        // 按 forecast_time 分组,统计每个时间点的平均功率
+        Map<Long, List<DataShortTermPowerForecastIssued>> groupedByTime = issuedList.stream()
+                .collect(Collectors.groupingBy(
+                        DataShortTermPowerForecastIssued::getForecastTime,
+                        LinkedHashMap::new,
+                        Collectors.toList()));
+
+        for (Map.Entry<Long, List<DataShortTermPowerForecastIssued>> entry : groupedByTime.entrySet())
         {
+            List<DataShortTermPowerForecastIssued> items = entry.getValue();
+            if (items.isEmpty())
+            {
+                continue;
+            }
+
+            BigDecimal sum = BigDecimal.ZERO;
+            int validCount = 0;
+            for (DataShortTermPowerForecastIssued issued : items)
+            {
+                try
+                {
+                    if (issued.getPower() != null && !issued.getPower().isEmpty())
+                    {
+                        sum = sum.add(new BigDecimal(issued.getPower()));
+                        validCount++;
+                    }
+                }
+                catch (NumberFormatException ignored)
+                {
+                }
+            }
+
             ShortTermPowerForecastData data = new ShortTermPowerForecastData();
-            data.setTime(ExcelForecastFileUtil.formatTime(String.valueOf(issued.getStartTime())));
-            try
+            data.setTime(TimeUtils.utcToGmt8Format(entry.getKey()));
+            // 计算平均值,保留3位小数(四舍五入)
+            if (validCount > 0)
             {
-                data.setPower(Double.parseDouble(issued.getPower()));
+                BigDecimal avg = sum.divide(BigDecimal.valueOf(validCount), 3, RoundingMode.HALF_UP);
+                data.setPower(avg.doubleValue());
             }
-            catch (NumberFormatException e)
+            else
             {
                 data.setPower(0.0);
             }
@@ -103,6 +142,7 @@ public class ShortTermPowerForecastingController extends BaseController
             data.setCurtailmentPower(0.0);
             result.add(data);
         }
+
         return result;
     }
 }

+ 2 - 2
forecast-backend-server/src/main/resources/mybatis/supershort/DataUltraShortTermPowerForecastingMapper.xml

@@ -74,8 +74,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
 
     <select id="selectByTimeRange" resultMap="DataUltraShortTermPowerForecastingResult">
         <include refid="selectDataUltraShortTermPowerForecastingVo"/>
-        where entity_id = #{entityId} and start_time between #{startTime} and #{endTime}
-        order by start_time asc
+        where entity_id = #{entityId} and forecast_time between #{startTime} and #{endTime}
+        order by forecast_time asc
     </select>
 
     <insert id="insertDataUltraShortTermPowerForecasting" parameterType="DataUltraShortTermPowerForecasting" useGeneratedKeys="true" keyProperty="Id">