Pārlūkot izejas kodu

优化响应申报容量和预估响应容量接口的申报容量逻辑改为响应时段内同时间段历史负荷均值 × 执行时段小时数 / 2,并取历史完整响应日(剔除响应日>= 今日)的数据参与计算

james 1 dienu atpakaļ
vecāks
revīzija
bc5e22f729

+ 8 - 4
service-vpp/service-vpp-biz/src/main/java/com/usky/vpp/controller/web/SiteController.java

@@ -178,7 +178,7 @@ public class SiteController {
      * <p>完整路径:GET /prod-api/service-vpp/site/{siteId}/declared-capacity</p>
      *
      * <p>根据站点 ID、响应开始/结束时间,计算需求响应申报容量:</p>
-     * <p><b>申报容量 = 响应时段内同时间段历史负荷均值 × 修正系数 K × 执行时段小时数 / 2</b></p>
+     * <p><b>申报容量 = 响应时段内同时间段历史负荷均值 × 执行时段小时数 / 2</b></p>
      *
      * <p><b>Query 参数</b></p>
      * <ul>
@@ -186,13 +186,17 @@ public class SiteController {
      *   <li>responseEndTime — 响应结束时间,必填,格式 yyyy-MM-dd HH:mm:ss(需与开始时间同一天)</li>
      * </ul>
      *
-     * <p>典型历史日、修正系数 K 规则与基线管理接口一致;历史均值仅统计响应开始至结束时段。</p>
+     * <p><b>典型历史日选取规则</b>(基于 Hutool 法定节假日 + 调休判定)</p>
+     * <ul>
+     *   <li>响应日为工作日:取邀约日前最近 5 个工作日;剔除响应当日、历史响应日(vpp_dr_event 已结束)和响应日 &gt;= 今日</li>
+     *   <li>响应日为非工作日:取邀约日前最近 3 个非工作日(含法定节假日);剔除响应当日、历史响应日和响应日 &gt;= 今日</li>
+     * </ul>
+     * <p>历史均值仅统计响应开始至结束时段。</p>
      *
      * <p><b>响应 data:SiteDeclaredCapacityVO</b></p>
      * <ul>
      *   <li>declaredCapacityKw — 申报容量 kW</li>
-     *   <li>correctionFactorK — 修正系数 K</li>
-     *   <li>referenceDates — 典型历史日</li>
+     *   <li>referenceDates — 参与计算的典型历史日</li>
      *   <li>points — 响应时段各时间点申报容量(DeclaredCapacityPointVO)</li>
      * </ul>
      *

+ 2 - 1
service-vpp/service-vpp-biz/src/main/java/com/usky/vpp/service/VppBaselineService.java

@@ -22,7 +22,8 @@ public interface VppBaselineService {
     /**
      * 查询站点响应申报容量。
      *
-     * <p>响应时段内同时间段历史负荷均值 × 修正系数 K × 执行时段小时数 / 2。</p>
+     * <p>响应时段内同时间段历史负荷均值 × 执行时段小时数 / 2。</p>
+     * <p>典型历史日选取:工作日 5 天 / 非工作日 3 天;剔除响应当日、历史响应日及响应日 &gt;= 今日。</p>
      *
      * @param siteId            站点 ID
      * @param responseStartTime 响应开始时间,格式 yyyy-MM-dd HH:mm:ss

+ 27 - 26
service-vpp/service-vpp-biz/src/main/java/com/usky/vpp/service/impl/VppBaselineServiceImpl.java

@@ -178,8 +178,9 @@ public class VppBaselineServiceImpl implements VppBaselineService {
     /**
      * 查询站点响应申报容量和预估响应容量。
      *
-     * <p>公式:响应时段内同时间段历史负荷均值 × 修正系数 K × 执行时段小时数 / 2。</p>
-     * <p>典型历史日、修正系数 K 计算规则与基线管理一致;历史均值仅取响应开始至结束时段。</p>
+     * <p>公式:响应时段内同时间段历史负荷均值 × 执行时段小时数 / 2。</p>
+     * <p>典型历史日选取规则(工作日 5 天 / 非工作日 3 天),剔除响应当日、历史响应日(vpp_dr_event 已结束)
+     * 及响应日 &gt;= 今日。</p>
      */
     @Override
     public SiteDeclaredCapacityVO getSiteDeclaredCapacity(Long siteId,
@@ -201,7 +202,7 @@ public class VppBaselineServiceImpl implements VppBaselineService {
             return result;
         }
 
-        Set<LocalDate> excludeDates = loadResponseHistoryDates();
+        Set<LocalDate> excludeDates = loadDeclaredCapacityExcludeDates();
         boolean workday = VppBaselineHelper.isWorkdayResponse(responseDate);
         result.setResponseDayType(VppWorkdayHelper.resolveDayType(responseDate));
         result.setResponseDayTypeLabel(VppWorkdayHelper.resolveDayTypeLabel(responseDate));
@@ -209,26 +210,18 @@ public class VppBaselineServiceImpl implements VppBaselineService {
                 ? VppBaselineHelper.WEEKDAY_REFERENCE_COUNT
                 : VppBaselineHelper.NON_WEEKDAY_REFERENCE_COUNT;
         List<LocalDate> referenceDates = VppBaselineHelper.selectReferenceDates(
-                responseDate, workday, excludeDates, requiredCount);
+                responseDate, workday, excludeDates, requiredCount, LocalDate.now());
         result.setReferenceDates(referenceDates.stream()
                 .map(VppBaselineHelper::formatDate)
                 .collect(Collectors.toList()));
+        if (referenceDates.isEmpty()) {
+            return result;
+        }
 
-        LocalDateTime correctionWindowStart = startTime.minusHours(VppBaselineHelper.CORRECTION_HOURS);
-        Map<String, BigDecimal> correctionBaselineByTime = vppTsdbQueryService.queryBaselineAvgByTimePoint(
-                deviceUuids,
-                referenceDates,
-                correctionWindowStart.toLocalTime(),
-                startTime.toLocalTime(),
-                VppBaselineHelper.POWER_METRICS,
-                VppBaselineHelper.INTERVAL_MINUTES,
-                null);
         Map<String, BigDecimal> todayActualByTime = VppBaselineHelper.buildActualLoadByTimePoint(
                 vppTsdbQueryService.queryDeviceMetricHistory(
-                        deviceUuids, responseDate.atStartOfDay(), startTime, VppBaselineHelper.POWER_METRICS),
+                        deviceUuids, responseDate.atStartOfDay(), endTime, VppBaselineHelper.POWER_METRICS),
                 VppBaselineHelper.INTERVAL_MINUTES);
-        BigDecimal correctionFactorK = VppBaselineHelper.calculateCorrectionFactor(
-                startTime, todayActualByTime, correctionBaselineByTime);
 
         Map<String, BigDecimal> historicalAvgByTime = vppTsdbQueryService.queryBaselineAvgByTimePoint(
                 deviceUuids,
@@ -242,12 +235,12 @@ public class VppBaselineServiceImpl implements VppBaselineService {
             return result;
         }
 
-        result.setCorrectionFactorK(correctionFactorK);
         result.setDeclaredCapacityKw(VppBaselineHelper.calculateDeclaredCapacityKw(
-                historicalAvgByTime, startTime, endTime, correctionFactorK));
-        result.setEstimatedResponseCapacityKw(VppBaselineHelper.calculateEstimatedResponseCapacityKw(todayActualByTime, startTime, endTime));
+                historicalAvgByTime, startTime, endTime));
+        result.setEstimatedResponseCapacityKw(VppBaselineHelper.calculateEstimatedResponseCapacityKw(
+                todayActualByTime, startTime, endTime));
         result.setPoints(VppBaselineHelper.buildDeclaredCapacityPoints(
-                historicalAvgByTime, startTime, endTime, correctionFactorK));
+                historicalAvgByTime, startTime, endTime));
         result.setDataSource("tsdb");
         return result;
     }
@@ -257,15 +250,13 @@ public class VppBaselineServiceImpl implements VppBaselineService {
                                                                  LocalDate responseDate,
                                                                  LocalDateTime startTime,
                                                                  LocalDateTime endTime) {
-        BigDecimal correctionFactorK = VppBaselineHelper.mockCorrectionFactor(siteId, responseDate);
         result.setReferenceDates(new ArrayList<>());
         result.setResponseDayType(VppWorkdayHelper.resolveDayType(responseDate));
         result.setResponseDayTypeLabel(VppWorkdayHelper.resolveDayTypeLabel(responseDate));
-        result.setCorrectionFactorK(correctionFactorK);
         result.setDeclaredCapacityKw(VppBaselineHelper.mockDeclaredCapacityKw(
-                siteId, responseDate, startTime, endTime, correctionFactorK));
+                siteId, responseDate, startTime, endTime));
         result.setPoints(VppBaselineHelper.buildMockDeclaredCapacityPoints(
-                siteId, responseDate, startTime, endTime, correctionFactorK));
+                siteId, responseDate, startTime, endTime));
         result.setDataSource("mock");
         return result;
     }
@@ -325,17 +316,27 @@ public class VppBaselineServiceImpl implements VppBaselineService {
     }
 
     private Set<LocalDate> loadResponseHistoryDates() {
+        return new HashSet<>(loadCompleteResponseDates());
+    }
+
+    /** 申报容量参考日剔除:历史响应日(vpp_dr_event 已结束)。 */
+    private Set<LocalDate> loadDeclaredCapacityExcludeDates() {
+        return loadResponseHistoryDates();
+    }
+
+    /** 已结束需求响应事件的起始日期(历史完整响应日)。 */
+    private List<LocalDate> loadCompleteResponseDates() {
         List<VppDrEvent> events = drEventMapper.selectList(
                 new LambdaQueryWrapper<VppDrEvent>()
                         .eq(VppDrEvent::getEventStatus, EVENT_STATUS_ENDED)
                         .eq(VppDrEvent::getDeleteFlag, VppAuditHelper.NOT_DELETED));
-        Set<LocalDate> dates = new HashSet<>();
+        Set<LocalDate> dates = new LinkedHashSet<>();
         for (VppDrEvent event : events) {
             if (event.getStartTime() != null) {
                 dates.add(event.getStartTime().toLocalDate());
             }
         }
-        return dates;
+        return new ArrayList<>(dates);
     }
 
     private VppSite resolveSite(Long siteId) {

+ 1 - 1
service-vpp/service-vpp-biz/src/main/java/com/usky/vpp/service/impl/VppResponseMonitorServiceImpl.java

@@ -283,7 +283,7 @@ public class VppResponseMonitorServiceImpl implements VppResponseMonitorService
                     responseStart.toLocalTime(), responseEnd.toLocalTime(),
                     VppBaselineHelper.POWER_METRICS, interval, null);
             tsdbDeclaredKw = VppBaselineHelper.calculateDeclaredCapacityKw(
-                    responseWindowBaseline, responseStart, responseEnd, correctionFactorK);
+                    responseWindowBaseline, responseStart, responseEnd);
         }
         BigDecimal finalDeclared = tsdbDeclaredKw.compareTo(BigDecimal.ZERO) > 0
                 ? tsdbDeclaredKw : declaredCapacityKw;

+ 1 - 1
service-vpp/service-vpp-biz/src/main/java/com/usky/vpp/service/vo/DeclaredCapacityPointVO.java

@@ -14,6 +14,6 @@ public class DeclaredCapacityPointVO {
     private String time;
     /** 同时间段历史负荷均值 kW */
     private BigDecimal historicalAvgKw;
-    /** 申报容量 kW = 历史均值 × K × 执行时段小时数 / 2 */
+    /** 申报容量 kW = 历史均值 × 执行时段小时数 / 2 */
     private BigDecimal declaredCapacityKw;
 }

+ 2 - 4
service-vpp/service-vpp-biz/src/main/java/com/usky/vpp/service/vo/SiteDeclaredCapacityVO.java

@@ -18,13 +18,11 @@ public class SiteDeclaredCapacityVO {
     private LocalDateTime responseStartTime;
     /** 响应结束时间 */
     private LocalDateTime responseEndTime;
-    /** 日内修正系数 K(0.7 ~ 1.2) */
-    private BigDecimal correctionFactorK;
-    /** 响应申报容量 kW(历史均值 × K × 执行时段小时数 / 2) */
+    /** 响应申报容量 kW(历史均值 × 执行时段小时数 / 2) */
     private BigDecimal declaredCapacityKw;
     /** 预估响应容量 kW(estimated_response_capacity_kw) */
     private BigDecimal estimatedResponseCapacityKw;
-    /** 典型历史日(yyyy-MM-dd) */
+    /** 参与计算的典型历史日(yyyy-MM-dd) */
     private List<String> referenceDates;
     /** 响应日类型:WORKDAY / NON_WORKDAY */
     private String responseDayType;

+ 30 - 22
service-vpp/service-vpp-biz/src/main/java/com/usky/vpp/util/VppBaselineHelper.java

@@ -67,19 +67,35 @@ public final class VppBaselineHelper {
 
     /**
      * 选取典型历史日:工作日取前 5 个工作日,非工作日取前 3 个非工作日;
-     * 仅剔除响应当日与历史响应日(不含节假日前后日期)
+     * 剔除 {@code excludeDates} 中的日期及响应当日
      */
     public static List<LocalDate> selectReferenceDates(LocalDate responseDate,
                                                        boolean workday,
                                                        Set<LocalDate> excludeDates,
                                                        int requiredCount) {
-        Set<LocalDate> excludes = new HashSet<>(excludeDates);
+        return selectReferenceDates(responseDate, workday, excludeDates, requiredCount, null);
+    }
+
+    /**
+     * 选取典型历史日,并可指定 {@code today}:候选参考日须严格早于 {@code today}(响应日 &gt;= 今日不参与选取)。
+     */
+    public static List<LocalDate> selectReferenceDates(LocalDate responseDate,
+                                                       boolean workday,
+                                                       Set<LocalDate> excludeDates,
+                                                       int requiredCount,
+                                                       LocalDate today) {
+        Set<LocalDate> excludes = new HashSet<>(excludeDates != null ? excludeDates : Collections.emptySet());
         excludes.add(responseDate);
 
         List<LocalDate> result = new ArrayList<>();
         LocalDate cursor = responseDate.minusDays(1);
         int guard = 0;
         while (result.size() < requiredCount && guard < 400) {
+            if (today != null && !cursor.isBefore(today)) {
+                cursor = cursor.minusDays(1);
+                guard++;
+                continue;
+            }
             if (excludes.contains(cursor)) {
                 cursor = cursor.minusDays(1);
                 guard++;
@@ -284,17 +300,16 @@ public final class VppBaselineHelper {
     }
 
     /**
-     * 响应申报容量 = 响应时段内历史负荷均值 × 修正系数 K × 执行时段小时数 / 2。
+     * 响应申报容量 = 响应时段内历史负荷均值 × 执行时段小时数 / 2。
      */
     public static BigDecimal calculateDeclaredCapacityKw(Map<String, BigDecimal> historicalAvgByTime,
-                                                       LocalDateTime windowStart,
-                                                       LocalDateTime windowEnd,
-                                                       BigDecimal correctionFactorK) {
+                                                         LocalDateTime windowStart,
+                                                         LocalDateTime windowEnd) {
         BigDecimal historicalAvg = averageValuesInWindow(historicalAvgByTime, windowStart, windowEnd);
         if (historicalAvg == null || historicalAvg.compareTo(BigDecimal.ZERO) <= 0) {
             return BigDecimal.ZERO;
         }
-        return applyDeclaredCapacityFormula(historicalAvg, correctionFactorK, windowStart, windowEnd);
+        return applyDeclaredCapacityFormula(historicalAvg, windowStart, windowEnd);
     }
 
     /**
@@ -311,19 +326,16 @@ public final class VppBaselineHelper {
     }
 
     private static BigDecimal applyDeclaredCapacityFormula(BigDecimal historicalAvg,
-                                                           BigDecimal correctionFactorK,
                                                            LocalDateTime windowStart,
                                                            LocalDateTime windowEnd) {
-        BigDecimal k = correctionFactorK != null ? correctionFactorK : K_DEFAULT;
         BigDecimal windowHours = responseWindowHours(windowStart, windowEnd);
-        return scale(historicalAvg.multiply(k).multiply(windowHours)
+        return scale(historicalAvg.multiply(windowHours)
                 .divide(DECLARED_CAPACITY_DIVISOR, 6, RoundingMode.HALF_UP));
     }
 
     public static List<DeclaredCapacityPointVO> buildDeclaredCapacityPoints(Map<String, BigDecimal> historicalAvgByTime,
-                                                                          LocalDateTime windowStart,
-                                                                          LocalDateTime windowEnd,
-                                                                          BigDecimal correctionFactorK) {
+                                                                              LocalDateTime windowStart,
+                                                                              LocalDateTime windowEnd) {
         List<DeclaredCapacityPointVO> points = new ArrayList<>();
         if (historicalAvgByTime == null || historicalAvgByTime.isEmpty()) {
             return points;
@@ -339,7 +351,7 @@ public final class VppBaselineHelper {
             point.setTime(entry.getKey());
             point.setHistoricalAvgKw(scale(historicalAvg));
             point.setDeclaredCapacityKw(applyDeclaredCapacityFormula(
-                    historicalAvg, correctionFactorK, windowStart, windowEnd));
+                    historicalAvg, windowStart, windowEnd));
             points.add(point);
         }
         return points;
@@ -348,25 +360,21 @@ public final class VppBaselineHelper {
     public static BigDecimal mockDeclaredCapacityKw(Long siteId,
                                                     LocalDate responseDate,
                                                     LocalDateTime windowStart,
-                                                    LocalDateTime windowEnd,
-                                                    BigDecimal correctionFactorK) {
+                                                    LocalDateTime windowEnd) {
         return calculateDeclaredCapacityKw(
                 buildMockHistoricalWindow(siteId, responseDate, windowStart, windowEnd),
                 windowStart,
-                windowEnd,
-                correctionFactorK);
+                windowEnd);
     }
 
     public static List<DeclaredCapacityPointVO> buildMockDeclaredCapacityPoints(Long siteId,
                                                                                 LocalDate responseDate,
                                                                                 LocalDateTime windowStart,
-                                                                                LocalDateTime windowEnd,
-                                                                                BigDecimal correctionFactorK) {
+                                                                                LocalDateTime windowEnd) {
         return buildDeclaredCapacityPoints(
                 buildMockHistoricalWindow(siteId, responseDate, windowStart, windowEnd),
                 windowStart,
-                windowEnd,
-                correctionFactorK);
+                windowEnd);
     }
 
     private static Map<String, BigDecimal> buildMockHistoricalWindow(Long siteId,