Bladeren bron

对接营服中心平台业务逻辑

james 4 dagen geleden
bovenliggende
commit
39be96acf5
22 gewijzigde bestanden met toevoegingen van 487 en 124 verwijderingen
  1. 12 0
      service-job/src/main/java/com/ruoyi/job/task/RyTask.java
  2. 6 0
      service-vpp/service-vpp-api/src/main/java/com/usky/vpp/RemoteVppTaskService.java
  3. 10 0
      service-vpp/service-vpp-api/src/main/java/com/usky/vpp/factory/RemoteVppTaskFactory.java
  4. 13 5
      service-vpp/service-vpp-biz/src/main/java/com/usky/vpp/client/VppUnHttpExecutor.java
  5. 1 1
      service-vpp/service-vpp-biz/src/main/java/com/usky/vpp/config/VppUnTenantContext.java
  6. 1 0
      service-vpp/service-vpp-biz/src/main/java/com/usky/vpp/constant/VppDrEventStatus.java
  7. 18 0
      service-vpp/service-vpp-biz/src/main/java/com/usky/vpp/controller/api/ServiceVppTaskApi.java
  8. 3 25
      service-vpp/service-vpp-biz/src/main/java/com/usky/vpp/job/VppUnPropertiesLoader.java
  9. 13 0
      service-vpp/service-vpp-biz/src/main/java/com/usky/vpp/service/VppUnReportService.java
  10. 16 2
      service-vpp/service-vpp-biz/src/main/java/com/usky/vpp/service/impl/VppBaselineServiceImpl.java
  11. 22 2
      service-vpp/service-vpp-biz/src/main/java/com/usky/vpp/service/impl/VppDrEventIngestServiceImpl.java
  12. 1 0
      service-vpp/service-vpp-biz/src/main/java/com/usky/vpp/service/impl/VppDrServiceImpl.java
  13. 1 0
      service-vpp/service-vpp-biz/src/main/java/com/usky/vpp/service/impl/VppSiteServiceImpl.java
  14. 9 7
      service-vpp/service-vpp-biz/src/main/java/com/usky/vpp/service/impl/VppUnIntegrationServiceImpl.java
  15. 251 22
      service-vpp/service-vpp-biz/src/main/java/com/usky/vpp/service/impl/VppUnReportServiceImpl.java
  16. 2 0
      service-vpp/service-vpp-biz/src/main/java/com/usky/vpp/service/vo/DrMonitorSiteResourceVO.java
  17. 1 1
      service-vpp/service-vpp-biz/src/main/java/com/usky/vpp/service/vo/SiteDeclaredCapacityVO.java
  18. 1 0
      service-vpp/service-vpp-biz/src/main/java/com/usky/vpp/service/vo/SiteListVO.java
  19. 14 7
      service-vpp/service-vpp-biz/src/main/java/com/usky/vpp/util/VppBaselineHelper.java
  20. 81 52
      service-vpp/service-vpp-biz/src/main/java/com/usky/vpp/util/VppUnMessageBuilder.java
  21. 8 0
      service-vpp/service-vpp-biz/src/main/java/com/usky/vpp/web/advice/VppUnDnCryptoRequestAdvice.java
  22. 3 0
      service-vpp/service-vpp-biz/src/main/java/com/usky/vpp/web/advice/VppUnDnCryptoResponseAdvice.java

+ 12 - 0
service-job/src/main/java/com/ruoyi/job/task/RyTask.java

@@ -157,4 +157,16 @@ public class RyTask {
         remoteVppTaskService.pollUn(tenantId);
     }
 
+    // 运管平台 UN Token 刷新与注册
+    public void registerUn(Integer tenantId) {
+        System.out.println("租户:" + tenantId + " 运管平台 UN 注册定时任务开始执行......");
+        remoteVppTaskService.registerUn(tenantId);
+    }
+
+    // 运管平台 UN 曲线数据报告(IntervalDataReport)
+    public void intervalDataReportUn(Integer tenantId) {
+        System.out.println("租户:" + tenantId + " 运管平台 UN 曲线数据报告定时任务开始执行......");
+        remoteVppTaskService.intervalDataReportUn(tenantId);
+    }
+
 }

+ 6 - 0
service-vpp/service-vpp-api/src/main/java/com/usky/vpp/RemoteVppTaskService.java

@@ -13,4 +13,10 @@ public interface RemoteVppTaskService {
 
     @GetMapping("/pollUn")
     void pollUn(@RequestParam(value = "tenantId") Integer tenantId);
+
+    @GetMapping("/registerUn")
+    void registerUn(@RequestParam(value = "tenantId") Integer tenantId);
+
+    @GetMapping("/intervalDataReportUn")
+    void intervalDataReportUn(@RequestParam(value = "tenantId") Integer tenantId);
 }

+ 10 - 0
service-vpp/service-vpp-api/src/main/java/com/usky/vpp/factory/RemoteVppTaskFactory.java

@@ -25,6 +25,16 @@ public class RemoteVppTaskFactory implements FallbackFactory<RemoteVppTaskServic
             public void pollUn(Integer tenantId) {
                 throw new FeignBadRequestException(500, "定时 UN Poll 异常, tenantId=" + tenantId + throwable.getMessage());
             }
+
+            @Override
+            public void registerUn(Integer tenantId) {
+                throw new FeignBadRequestException(500, "定时 UN 注册异常, tenantId=" + tenantId + throwable.getMessage());
+            }
+
+            @Override
+            public void intervalDataReportUn(Integer tenantId) {
+                throw new FeignBadRequestException(500, "定时 UN 曲线数据报告异常, tenantId=" + tenantId + throwable.getMessage());
+            }
         };
     }
 }

+ 13 - 5
service-vpp/service-vpp-biz/src/main/java/com/usky/vpp/client/VppUnHttpExecutor.java

@@ -32,6 +32,9 @@ public class VppUnHttpExecutor {
 
     private static final Logger log = LoggerFactory.getLogger(VppUnHttpExecutor.class);
 
+    /** Token 获取接口使用明文,不参与 SM2/SM3 加解密。 */
+    private static final String TOKEN_REQUEST = "TokenRequest";
+
     @Autowired
     private VppUnPropertiesRegistry propertiesRegistry;
 
@@ -75,7 +78,8 @@ public class VppUnHttpExecutor {
             headers.setAccept(java.util.Collections.singletonList(MediaType.APPLICATION_JSON));
 
             String requestBody = plainJson;
-            if (cryptoService.isActive()) {
+            boolean useCrypto = useCrypto(serviceName);
+            if (useCrypto && cryptoService.isActive()) {
                 requestBody = cryptoService.encryptRequest(plainJson);
                 headers.set("X-Sign", cryptoService.signRequest(requestBody));
             }
@@ -85,11 +89,11 @@ public class VppUnHttpExecutor {
 
             String url = normalizeUrl(properties.getBaseUrl()) + "/" + serviceName;
             log.debug("租户 {} UN 请求 {} crypto={} token={}",
-                    VppUnTenantContext.resolveTenantId(), url, cryptoService.isActive(), withToken);
+                    VppUnTenantContext.resolveTenantId(), url, useCrypto && cryptoService.isActive(), withToken);
 
             ResponseEntity<String> response = restTemplate.exchange(
                     url, HttpMethod.POST, new HttpEntity<>(requestBody, headers), String.class);
-            return decodeResponseBody(response.getBody(), response.getHeaders().getFirst("X-Sign"));
+            return decodeResponseBody(response.getBody(), response.getHeaders().getFirst("X-Sign"), useCrypto);
         } catch (HttpStatusCodeException ex) {
             String message = ex.getResponseBodyAsString();
             log.warn("UN 请求 {} 失败 status={} body={}", serviceName, ex.getRawStatusCode(), message);
@@ -118,12 +122,12 @@ public class VppUnHttpExecutor {
                 || message.contains("JWTDecodeException"));
     }
 
-    private String decodeResponseBody(String body, String signHeader) {
+    private String decodeResponseBody(String body, String signHeader, boolean useCrypto) {
         if (!StringUtils.hasText(body)) {
             return "{}";
         }
         String trimmed = body.trim();
-        if (cryptoService.isActive() && !trimmed.startsWith("{")) {
+        if (useCrypto && cryptoService.isActive() && !trimmed.startsWith("{")) {
             if (!cryptoService.verifyResponse(trimmed, signHeader)) {
                 throw new BusinessException("UN 响应验签失败");
             }
@@ -132,6 +136,10 @@ public class VppUnHttpExecutor {
         return trimmed;
     }
 
+    private static boolean useCrypto(String serviceName) {
+        return !TOKEN_REQUEST.equals(serviceName);
+    }
+
     private static String normalizeUrl(String baseUrl) {
         if (baseUrl.endsWith("/")) {
             return baseUrl.substring(0, baseUrl.length() - 1);

+ 1 - 1
service-vpp/service-vpp-biz/src/main/java/com/usky/vpp/config/VppUnTenantContext.java

@@ -34,7 +34,7 @@ public final class VppUnTenantContext {
         if (tenantId != null && tenantId > 0) {
             return tenantId;
         }
-        throw new BusinessException("缺少租户上下文,无法获取运管平台配置");
+        throw new BusinessException("缺少租户上下文,无法获取运管平台配置租户Id");
     }
 
     public static void clear() {

+ 1 - 0
service-vpp/service-vpp-biz/src/main/java/com/usky/vpp/constant/VppDrEventStatus.java

@@ -12,6 +12,7 @@ public final class VppDrEventStatus {
     public static final int EXECUTING = 3;
     public static final int ENDED = 4;
     public static final int CANCELLED = 5;
+    public static final int LAST_PLACE_SPLITTING = 6;
 
     private VppDrEventStatus() {
     }

+ 18 - 0
service-vpp/service-vpp-biz/src/main/java/com/usky/vpp/controller/api/ServiceVppTaskApi.java

@@ -2,7 +2,9 @@ package com.usky.vpp.controller.api;
 
 import com.usky.vpp.RemoteVppTaskService;
 import com.usky.vpp.service.VppSiteCompletionRateTaskService;
+import com.usky.vpp.service.VppUnIntervalDataReportTaskService;
 import com.usky.vpp.service.VppUnPollTaskService;
+import com.usky.vpp.service.VppUnRegisterTaskService;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.web.bind.annotation.RestController;
 
@@ -15,6 +17,12 @@ public class ServiceVppTaskApi implements RemoteVppTaskService {
     @Autowired
     private VppUnPollTaskService unPollTaskService;
 
+    @Autowired
+    private VppUnRegisterTaskService unRegisterTaskService;
+
+    @Autowired
+    private VppUnIntervalDataReportTaskService unIntervalDataReportTaskService;
+
     @Override
     public void refreshSiteAvgCompletionRate() {
         siteCompletionRateTaskService.refreshSiteAvgCompletionRate();
@@ -24,4 +32,14 @@ public class ServiceVppTaskApi implements RemoteVppTaskService {
     public void pollUn(Integer tenantId) {
         unPollTaskService.pollUn(tenantId);
     }
+
+    @Override
+    public void registerUn(Integer tenantId) {
+        unRegisterTaskService.registerUn(tenantId);
+    }
+
+    @Override
+    public void intervalDataReportUn(Integer tenantId) {
+        unIntervalDataReportTaskService.intervalDataReportUn(tenantId);
+    }
 }

+ 3 - 25
service-vpp/service-vpp-biz/src/main/java/com/usky/vpp/job/VppUnPropertiesLoader.java

@@ -2,16 +2,12 @@ package com.usky.vpp.job;
 
 import com.usky.vpp.config.VppUnProperties;
 import com.usky.vpp.config.VppUnPropertiesRegistry;
-import com.usky.vpp.config.VppUnTenantContext;
-import com.usky.vpp.service.VppUnIntegrationService;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
+import com.usky.vpp.service.VppUnRegisterTaskService;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.boot.ApplicationArguments;
 import org.springframework.boot.ApplicationRunner;
 import org.springframework.core.annotation.Order;
 import org.springframework.stereotype.Component;
-import org.springframework.util.StringUtils;
 
 /**
  * 服务启动时从 vpp_tenant_config 加载 UN 配置至内存,并按需自动注册 UN
@@ -20,13 +16,11 @@ import org.springframework.util.StringUtils;
 @Order(100)
 public class VppUnPropertiesLoader implements ApplicationRunner {
 
-    private static final Logger log = LoggerFactory.getLogger(VppUnPropertiesLoader.class);
-
     @Autowired
     private VppUnPropertiesRegistry propertiesRegistry;
 
     @Autowired
-    private VppUnIntegrationService integrationService;
+    private VppUnRegisterTaskService unRegisterTaskService;
 
     @Override
     public void run(ApplicationArguments args) {
@@ -39,23 +33,7 @@ public class VppUnPropertiesLoader implements ApplicationRunner {
             if (!Boolean.TRUE.equals(properties.getAutoRegisterOnStartup()) || !properties.isOutboundActive()) {
                 continue;
             }
-            Integer tenantId = properties.getTenantId();
-            VppUnTenantContext.runWithTenant(tenantId, () -> {
-                try {
-                    integrationService.refreshToken();
-                    integrationService.loadPersistedRegistrationId();
-                    boolean reregister = StringUtils.hasText(properties.getRegistrationId());
-                    if (reregister) {
-                        log.info("租户 {} 使用已持久化的 registrationID={} 向 UN 重新注册",
-                                tenantId, properties.getRegistrationId());
-                    }
-                    integrationService.register();
-                    log.info("租户 {} 启动{} UN 完成 registrationID={}",
-                            tenantId, reregister ? "重新注册" : "注册", properties.getRegistrationId());
-                } catch (Exception ex) {
-                    log.warn("租户 {} 启动注册 UN 失败: {}", tenantId, ex.getMessage());
-                }
-            });
+            unRegisterTaskService.registerUn(properties.getTenantId());
         }
     }
 }

+ 13 - 0
service-vpp/service-vpp-biz/src/main/java/com/usky/vpp/service/VppUnReportService.java

@@ -8,4 +8,17 @@ import java.util.Map;
 public interface VppUnReportService {
 
     void handleCreateReportRequest(Map<String, Object> createReportRequest);
+
+    /** 提交 RegisterReportRequest(元数据报告)。 */
+    void submitRegisterReport();
+
+    /**
+     * 提交 IntervalDataReportRequest(实时/区间数据报告)。
+     *
+     * @param createReportRequest UN Poll 下发的 CreateReportRequest;主动上报时可传 null
+     */
+    void submitIntervalDataReport(Map<String, Object> createReportRequest);
+
+    /** 提交 MomentDataReportRequest(实时数据报告,主动上报)。 */
+    void submitMomentDataReport();
 }

+ 16 - 2
service-vpp/service-vpp-biz/src/main/java/com/usky/vpp/service/impl/VppBaselineServiceImpl.java

@@ -197,7 +197,9 @@ public class VppBaselineServiceImpl implements VppBaselineService {
     /**
      * 查询站点响应申报容量和预估响应容量。
      *
-     * <p>公式:响应时段内同时间段历史负荷均值 × 执行时段小时数 / 2。</p>
+     * <p>申报容量公式:响应时段内同时间段历史负荷均值 × 执行时段小时数 / 2。</p>
+     * <p>预估响应容量公式:响应时段内今日负荷均值 − 响应时段内历史负荷均值 × 修正系数 K;
+     * K 由响应开始前 2 小时今日实测 / 同时段历史基线计算,限制在 [0.7, 1.2]。</p>
      * <p>典型历史日选取规则(工作日 5 天 / 非工作日 3 天),剔除响应当日、历史响应日(vpp_dr_event 已结束)
      * 及响应日 &gt;= 今日。</p>
      */
@@ -256,10 +258,22 @@ public class VppBaselineServiceImpl implements VppBaselineService {
             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);
+        BigDecimal correctionFactorK = VppBaselineHelper.calculateCorrectionFactor(
+                startTime, todayActualByTime, correctionBaselineByTime);
+
         result.setDeclaredCapacityKw(VppBaselineHelper.calculateDeclaredCapacityKw(
                 historicalAvgByTime, startTime, endTime));
         result.setEstimatedResponseCapacityKw(VppBaselineHelper.calculateEstimatedResponseCapacityKw(
-                todayActualByTime, startTime, endTime));
+                todayActualByTime, historicalAvgByTime, correctionFactorK, startTime, endTime));
         result.setPoints(VppBaselineHelper.buildDeclaredCapacityPoints(
                 historicalAvgByTime, startTime, endTime));
         result.setDataSource("tsdb");

+ 22 - 2
service-vpp/service-vpp-biz/src/main/java/com/usky/vpp/service/impl/VppDrEventIngestServiceImpl.java

@@ -5,8 +5,10 @@ import com.fasterxml.jackson.core.JsonProcessingException;
 import com.fasterxml.jackson.databind.ObjectMapper;
 import com.usky.vpp.constant.VppDrEventStatus;
 import com.usky.vpp.domain.VppDrEvent;
+import com.usky.vpp.domain.VppDrParticipation;
 import com.usky.vpp.enums.VppUnEventPhase;
 import com.usky.vpp.mapper.VppDrEventMapper;
+import com.usky.vpp.mapper.VppDrParticipationMapper;
 import com.usky.vpp.service.VppDrEventIngestService;
 import com.usky.vpp.service.VppDrExecutionBootstrapService;
 import com.usky.vpp.service.VppUnDrSyncService;
@@ -35,10 +37,13 @@ public class VppDrEventIngestServiceImpl implements VppDrEventIngestService {
     private static final int EVENT_STATUS_EXECUTING = VppDrEventStatus.EXECUTING;
     private static final int EVENT_STATUS_ENDED = VppDrEventStatus.ENDED;
     private static final int EVENT_STATUS_CANCELLED = VppDrEventStatus.CANCELLED;
+    private static final int EVENT_STATUS_SPLITTING = VppDrEventStatus.LAST_PLACE_SPLITTING;
 
     @Autowired
     private VppDrEventMapper eventMapper;
     @Autowired
+    private VppDrParticipationMapper participationMapper;
+    @Autowired
     private VppUnDrSyncService unDrSyncService;
     @Autowired
     private VppDrExecutionBootstrapService executionBootstrapService;
@@ -197,8 +202,9 @@ public class VppDrEventIngestServiceImpl implements VppDrEventIngestService {
                 unDrSyncService.syncTargetResources(event, resources, false);
                 break;
             case SPLIT_NOTICE:
-                event.setEventStatus(EVENT_STATUS_PENDING);
-                unDrSyncService.syncTargetResources(event, resources, true);
+                event.setEventStatus(EVENT_STATUS_SPLITTING);
+                event.setTargetCapacityKw(VppUnEventParser.sumResourceLoadKw(resources, true));
+                deleteEventParticipations(event);
                 break;
             case SPLIT_RESULT:
                 event.setEventStatus(EVENT_STATUS_DECLARED);
@@ -224,6 +230,20 @@ public class VppDrEventIngestServiceImpl implements VppDrEventIngestService {
         }
     }
 
+    private void deleteEventParticipations(VppDrEvent event) {
+        if (event == null || event.getId() == null) {
+            return;
+        }
+        List<VppDrParticipation> participations = participationMapper.selectList(
+                new LambdaQueryWrapper<VppDrParticipation>()
+                        .eq(VppDrParticipation::getEventId, event.getId())
+                        .eq(VppDrParticipation::getDeleteFlag, VppAuditHelper.NOT_DELETED));
+        for (VppDrParticipation participation : participations) {
+            VppAuditHelper.fillSoftDelete(participation);
+            participationMapper.updateById(participation);
+        }
+    }
+
     private void applyBaseFields(VppDrEvent event, Map<String, Object> eventMap, Map<String, Object> descriptor) {
         String comment = VppUnPayloadHelper.getString(descriptor, "comment");
         event.setEventName(StringUtils.hasText(comment) ? comment : event.getEventId());

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

@@ -61,6 +61,7 @@ public class VppDrServiceImpl implements VppDrService {
     private static final int EVENT_STATUS_EXECUTING = VppDrEventStatus.EXECUTING;
     private static final int EVENT_STATUS_ENDED = VppDrEventStatus.ENDED;
     private static final int EVENT_STATUS_CANCELLED = VppDrEventStatus.CANCELLED;
+    private static final int EVENT_STATUS_SPLITTING = VppDrEventStatus.LAST_PLACE_SPLITTING;
 
     private static final int PARTICIPATE_STATUS_ACCEPT = 1;
 

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

@@ -232,6 +232,7 @@ public class VppSiteServiceImpl implements VppSiteService {
             vo.setOwnerName(site.getOwnerName());
             vo.setContactName(site.getContactName());
             vo.setContactPhone(site.getContactPhone());
+            vo.setUnResourceId(site.getUnResourceId());
             vo.setResponsePriority(site.getResponsePriority());
             vo.setCreateTime(site.getCreateTime());
             List<VppResourcePoint> siteResources = resourceBySite.getOrDefault(site.getId(), Collections.emptyList());

+ 9 - 7
service-vpp/service-vpp-biz/src/main/java/com/usky/vpp/service/impl/VppUnIntegrationServiceImpl.java

@@ -179,13 +179,15 @@ public class VppUnIntegrationServiceImpl implements VppUnIntegrationService {
             register();
             return;
         }
-        if ("CreateReportRequest".equals(root)) {
-            try {
-                reportService.handleCreateReportRequest(response);
-            } catch (Exception ex) {
-                log.warn("处理 CreateReportRequest 失败: {}", ex.getMessage());
-            }
-        }
+
+//        //删除
+//        if ("CreateReportRequest".equals(root)) {
+//            try {
+//                reportService.handleCreateReportRequest(response);
+//            } catch (Exception ex) {
+//                log.warn("处理 CreateReportRequest 失败: {}", ex.getMessage());
+//            }
+//        }
     }
 
     private VppUnProperties currentProperties() {

+ 251 - 22
service-vpp/service-vpp-biz/src/main/java/com/usky/vpp/service/impl/VppUnReportServiceImpl.java

@@ -4,22 +4,38 @@ import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.usky.vpp.client.VppUnClient;
 import com.usky.vpp.config.VppUnProperties;
 import com.usky.vpp.config.VppUnPropertiesRegistry;
+import com.usky.vpp.config.VppUnTenantContext;
+import com.usky.vpp.domain.VppDevice;
 import com.usky.vpp.domain.VppResourcePoint;
 import com.usky.vpp.domain.VppSite;
+import com.usky.vpp.mapper.VppDeviceMapper;
 import com.usky.vpp.mapper.VppResourcePointMapper;
+import com.usky.vpp.mapper.VppSiteMapper;
+import com.usky.vpp.service.VppTsdbQueryService;
 import com.usky.vpp.service.VppUnReportService;
-import com.usky.vpp.util.VppAuditHelper;
-import com.usky.vpp.util.VppSiteResourceHelper;
-import com.usky.vpp.util.VppUnMessageBuilder;
-import com.usky.vpp.util.VppUnPayloadHelper;
+import com.usky.vpp.constant.VppDrEventStatus;
+import com.usky.vpp.domain.VppDrEvent;
+import com.usky.vpp.mapper.VppDrEventMapper;
+import com.usky.vpp.util.*;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 import org.springframework.util.StringUtils;
 
+import java.math.BigDecimal;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.HashMap;
+import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import java.util.stream.Collectors;
 
 @Service
 public class VppUnReportServiceImpl implements VppUnReportService {
@@ -33,7 +49,15 @@ public class VppUnReportServiceImpl implements VppUnReportService {
     @Autowired
     private VppResourcePointMapper resourcePointMapper;
     @Autowired
+    private VppSiteMapper siteMapper;
+    @Autowired
+    private VppDeviceMapper deviceMapper;
+    @Autowired
     private VppSiteResourceHelper siteResourceHelper;
+    @Autowired
+    private VppTsdbQueryService vppTsdbQueryService;
+    @Autowired
+    private VppDrEventMapper drEventMapper;
 
     @Override
     public void handleCreateReportRequest(Map<String, Object> createReportRequest) {
@@ -52,13 +76,13 @@ public class VppUnReportServiceImpl implements VppUnReportService {
         }
         switch (reportRequestId) {
             case "MetaDataReport":
-                submitRegisterReport();
+                doSubmitRegisterReport();
                 break;
             case "IntervalDataReport":
-                submitIntervalDataReport(createReportRequest);
+                doSubmitIntervalDataReport(createReportRequest);
                 break;
             case "MomentDataReport":
-                submitMomentDataReport(createReportRequest);
+                doSubmitMomentDataReport(createReportRequest);
                 break;
 //            case "LoadForecastReport":
 //                submitLoadForecastReport(createReportRequest);
@@ -72,33 +96,123 @@ public class VppUnReportServiceImpl implements VppUnReportService {
         }
     }
 
-    private void submitRegisterReport() {
+    @Override
+    public void submitRegisterReport() {
         VppUnProperties properties = propertiesRegistry.getCurrent();
+        if (!properties.isOutboundActive()) {
+            return;
+        }
+        doSubmitRegisterReport();
+    }
+
+    private void doSubmitRegisterReport() {
+        VppUnProperties properties = propertiesRegistry.getCurrent();
+        Integer tenantId = VppUnTenantContext.resolveTenantId();
+        List<VppSite> sites = listActiveSites(tenantId);
         List<VppResourcePoint> resources = listActiveResources(false);
-        Map<Long, VppSite> siteMap = siteResourceHelper.loadSiteMap(resources);
-        Map<String, Object> request = VppUnMessageBuilder.buildRegisterReportRequest(resources, siteMap, properties);
+        Map<Long, String> deviceUnResourceIdMap = loadDeviceUnResourceIdMap(resources);
+        Map<String, Object> request = VppUnMessageBuilder.buildRegisterReportRequest(
+                properties, sites, resources, deviceUnResourceIdMap);
         unClient.post("RegisterReportRequest", request, true);
-        log.info("已提交 RegisterReportRequest");
+        long siteMetricCount = sites.stream()
+                .filter(site -> site != null && StringUtils.hasText(site.getUnResourceId()))
+                .count();
+        long resourceMetricCount = resources.stream()
+                .filter(resource -> resource != null
+                        && resource.getDeviceId() != null
+                        && StringUtils.hasText(deviceUnResourceIdMap.get(resource.getDeviceId())))
+                .count();
+        log.info("已提交 RegisterReportRequest,虚拟电厂量测点={},站点量测点={},资源点量测点={}",
+                VppUnRegisterMetricDefinitions.VPP_REGISTER_METRICS.size(), siteMetricCount, resourceMetricCount);
     }
 
-    private void submitIntervalDataReport(Map<String, Object> createReportRequest) {
+    @Override
+    public void submitIntervalDataReport(Map<String, Object> createReportRequest) {
         VppUnProperties properties = propertiesRegistry.getCurrent();
-        List<VppResourcePoint> resources = listActiveResources(true);
-        Map<Long, VppSite> siteMap = siteResourceHelper.loadSiteMap(resources);
+        if (!properties.isOutboundActive()) {
+            return;
+        }
+        doSubmitIntervalDataReport(createReportRequest);
+    }
+
+    private void doSubmitIntervalDataReport(Map<String, Object> createReportRequest) {
+        VppUnProperties properties = propertiesRegistry.getCurrent();
+        Integer tenantId = VppUnTenantContext.resolveTenantId();
+        List<VppSite> sites = listActiveSites(tenantId);
+        List<VppResourcePoint> resources = listActiveResources(false);
+        List<VppResourcePoint> peakResources = listActiveResources(true);
+        Map<Long, String> deviceUnResourceIdMap = loadDeviceUnResourceIdMap(resources);
+        Map<Long, VppDevice> deviceMap = loadDeviceMap(resources);
+        Map<Long, List<String>> peakDeviceUuidsBySite = groupPeakDeviceUuidsBySite(peakResources, deviceMap);
+        List<String> tenantPeakDeviceUuids = collectDeviceUuids(peakResources, deviceMap);
+
+        LocalDate forecastDate = VppUnIntervalDataHelper.resolveForecastDate();
+        boolean workday = VppBaselineHelper.isWorkdayResponse(forecastDate);
+        int requiredCount = workday
+                ? VppBaselineHelper.WEEKDAY_REFERENCE_COUNT
+                : VppBaselineHelper.NON_WEEKDAY_REFERENCE_COUNT;
+        List<LocalDate> referenceDates = VppBaselineHelper.selectReferenceDates(
+                forecastDate, workday, loadResponseHistoryDates(), requiredCount, LocalDate.now());
+
+        LocalDateTime reportTime = LocalDateTime.now();
+        List<VppUnRegisterReportIndex.Entry> entries = VppUnRegisterReportIndex.buildEntries(
+                properties, sites, resources, deviceUnResourceIdMap);
+        List<Map<String, Object>> pointCurveData = new ArrayList<>(entries.size());
+        for (VppUnRegisterReportIndex.Entry entry : entries) {
+            List<String> deviceUuids = resolveMomentDataDeviceUuids(
+                    entry, tenantPeakDeviceUuids, peakDeviceUuidsBySite, deviceMap);
+            List<Map<String, Object>> curveValues = VppUnIntervalDataHelper.buildForecastCurveValues(
+                    vppTsdbQueryService, deviceUuids, referenceDates, forecastDate, entry.getMetric());
+            pointCurveData.add(VppUnMessageBuilder.buildIntervalCurveDataEntry(entry.getRid(), curveValues));
+        }
+
         Map<String, Object> request = VppUnMessageBuilder.buildIntervalDataReportRequest(
-                createReportRequest, resources, siteMap, properties);
+                properties, createReportRequest, pointCurveData, reportTime);
         unClient.post("IntervalDataReportRequest", request, true);
-        log.info("已提交 IntervalDataReportRequest");
+        log.info("已提交 IntervalDataReportRequest,pointCurveData={},预测日={},参考日数={}",
+                pointCurveData.size(), forecastDate, referenceDates.size());
+    }
+
+    @Override
+    public void submitMomentDataReport() {
+        VppUnProperties properties = propertiesRegistry.getCurrent();
+        if (!properties.isOutboundActive()) {
+            return;
+        }
+        doSubmitMomentDataReport(null);
     }
 
-    private void submitMomentDataReport(Map<String, Object> createReportRequest) {
+    private void doSubmitMomentDataReport(Map<String, Object> createReportRequest) {
         VppUnProperties properties = propertiesRegistry.getCurrent();
-        List<VppResourcePoint> resources = listActiveResources(true);
-        Map<Long, VppSite> siteMap = siteResourceHelper.loadSiteMap(resources);
+        Integer tenantId = VppUnTenantContext.resolveTenantId();
+        List<VppSite> sites = listActiveSites(tenantId);
+        List<VppResourcePoint> resources = listActiveResources(false);
+        List<VppResourcePoint> peakResources = listActiveResources(true);
+        Map<Long, String> deviceUnResourceIdMap = loadDeviceUnResourceIdMap(resources);
+        Map<Long, VppDevice> deviceMap = loadDeviceMap(resources);
+        Map<Long, List<String>> peakDeviceUuidsBySite = groupPeakDeviceUuidsBySite(peakResources, deviceMap);
+        List<String> tenantPeakDeviceUuids = collectDeviceUuids(peakResources, deviceMap);
+
+        LocalDateTime windowEnd = VppUnMomentDataHelper.resolveWindowEnd(LocalDateTime.now());
+        LocalDateTime windowStart = VppUnMomentDataHelper.resolveWindowStart(windowEnd);
+
+        List<VppUnRegisterReportIndex.Entry> entries = VppUnRegisterReportIndex.buildEntries(
+                properties, sites, resources, deviceUnResourceIdMap);
+        List<Map<String, Object>> pointData = new ArrayList<>(entries.size());
+        for (VppUnRegisterReportIndex.Entry entry : entries) {
+            List<String> deviceUuids = resolveMomentDataDeviceUuids(
+                    entry, tenantPeakDeviceUuids, peakDeviceUuidsBySite, deviceMap);
+            BigDecimal value = VppUnMomentDataHelper.aggregateMetricValue(
+                    vppTsdbQueryService, deviceUuids, windowStart, windowEnd, entry.getMetric());
+            pointData.add(VppUnMessageBuilder.buildMomentDataPoint(
+                    entry.getRid(), value, windowEnd, "good"));
+        }
+
         Map<String, Object> request = VppUnMessageBuilder.buildMomentDataReportRequest(
-                createReportRequest, resources, siteMap, properties);
+                properties, createReportRequest, pointData, windowEnd);
         unClient.post("MomentDataReportRequest", request, true);
-        log.info("已提交 MomentDataReportRequest");
+        log.info("已提交 MomentDataReportRequest,pointData={},窗口 {} ~ {}",
+                pointData.size(), windowStart, windowEnd);
     }
 
     private void submitLoadForecastReport(Map<String, Object> createReportRequest) {
@@ -121,16 +235,131 @@ public class VppUnReportServiceImpl implements VppUnReportService {
         log.info("已提交 RegulateForecastReportRequest");
     }
 
+    private List<VppSite> listActiveSites(Integer tenantId) {
+        return siteMapper.selectList(new LambdaQueryWrapper<VppSite>()
+                .eq(VppSite::getDeleteFlag, VppAuditHelper.NOT_DELETED)
+                .eq(tenantId != null, VppSite::getTenantId, tenantId)
+                .orderByAsc(VppSite::getId));
+    }
+
     private List<VppResourcePoint> listActiveResources(boolean controllableOnly) {
+        Integer tenantId = VppUnTenantContext.resolveTenantId();
         LambdaQueryWrapper<VppResourcePoint> wrapper = new LambdaQueryWrapper<VppResourcePoint>()
                 .eq(VppResourcePoint::getDeleteFlag, VppAuditHelper.NOT_DELETED)
+                .eq(tenantId != null, VppResourcePoint::getTenantId, tenantId)
                 .orderByAsc(VppResourcePoint::getId);
         if (controllableOnly) {
-            wrapper.eq(VppResourcePoint::getIsControl, 1);
+            wrapper.eq(VppResourcePoint::getIsSupportPeak, 1);
         }
         return resourcePointMapper.selectList(wrapper);
     }
 
+    private Map<Long, VppDevice> loadDeviceMap(List<VppResourcePoint> resources) {
+        if (resources == null || resources.isEmpty()) {
+            return Collections.emptyMap();
+        }
+        List<Long> deviceIds = resources.stream()
+                .map(VppResourcePoint::getDeviceId)
+                .filter(Objects::nonNull)
+                .distinct()
+                .collect(Collectors.toList());
+        if (deviceIds.isEmpty()) {
+            return Collections.emptyMap();
+        }
+        return deviceMapper.selectBatchIds(deviceIds).stream()
+                .filter(device -> device != null && !VppAuditHelper.isDeleted(device.getDeleteFlag()))
+                .collect(Collectors.toMap(VppDevice::getId, device -> device, (a, b) -> a));
+    }
+
+    private Map<Long, List<String>> groupPeakDeviceUuidsBySite(List<VppResourcePoint> peakResources,
+                                                                 Map<Long, VppDevice> deviceMap) {
+        Map<Long, List<String>> grouped = new HashMap<>();
+        if (peakResources == null || deviceMap == null) {
+            return grouped;
+        }
+        for (VppResourcePoint resource : peakResources) {
+            if (resource == null || resource.getSiteId() == null || resource.getDeviceId() == null) {
+                continue;
+            }
+            VppDevice device = deviceMap.get(resource.getDeviceId());
+            if (device == null || !StringUtils.hasText(device.getDeviceUuid())) {
+                continue;
+            }
+            grouped.computeIfAbsent(resource.getSiteId(), key -> new ArrayList<>())
+                    .add(device.getDeviceUuid().trim());
+        }
+        return grouped;
+    }
+
+    private List<String> collectDeviceUuids(List<VppResourcePoint> resources, Map<Long, VppDevice> deviceMap) {
+        if (resources == null || deviceMap == null) {
+            return Collections.emptyList();
+        }
+        Set<String> uuids = new LinkedHashSet<>();
+        for (VppResourcePoint resource : resources) {
+            if (resource == null || resource.getDeviceId() == null) {
+                continue;
+            }
+            VppDevice device = deviceMap.get(resource.getDeviceId());
+            if (device != null && StringUtils.hasText(device.getDeviceUuid())) {
+                uuids.add(device.getDeviceUuid().trim());
+            }
+        }
+        return new ArrayList<>(uuids);
+    }
+
+    private List<String> resolveMomentDataDeviceUuids(VppUnRegisterReportIndex.Entry entry,
+                                                      List<String> tenantPeakDeviceUuids,
+                                                      Map<Long, List<String>> peakDeviceUuidsBySite,
+                                                      Map<Long, VppDevice> deviceMap) {
+        if (entry.getScope() == VppUnRegisterReportIndex.Scope.VPP) {
+            return tenantPeakDeviceUuids;
+        }
+        if (entry.getScope() == VppUnRegisterReportIndex.Scope.SITE && entry.getSite() != null) {
+            return peakDeviceUuidsBySite.getOrDefault(entry.getSite().getId(), Collections.emptyList());
+        }
+        if (entry.getScope() == VppUnRegisterReportIndex.Scope.RESOURCE && entry.getResource() != null) {
+            VppDevice device = deviceMap.get(entry.getResource().getDeviceId());
+            if (device != null && StringUtils.hasText(device.getDeviceUuid())) {
+                return Collections.singletonList(device.getDeviceUuid().trim());
+            }
+        }
+        return Collections.emptyList();
+    }
+
+    private Map<Long, String> loadDeviceUnResourceIdMap(List<VppResourcePoint> resources) {
+        if (resources == null || resources.isEmpty()) {
+            return Collections.emptyMap();
+        }
+        List<Long> deviceIds = resources.stream()
+                .map(VppResourcePoint::getDeviceId)
+                .filter(Objects::nonNull)
+                .distinct()
+                .collect(Collectors.toList());
+        if (deviceIds.isEmpty()) {
+            return Collections.emptyMap();
+        }
+        return deviceMapper.selectBatchIds(deviceIds).stream()
+                .filter(device -> device != null
+                        && !VppAuditHelper.isDeleted(device.getDeleteFlag())
+                        && StringUtils.hasText(device.getUnResourceId()))
+                .collect(Collectors.toMap(VppDevice::getId, VppDevice::getUnResourceId, (a, b) -> a));
+    }
+
+    private Set<LocalDate> loadResponseHistoryDates() {
+        List<VppDrEvent> events = drEventMapper.selectList(
+                new LambdaQueryWrapper<VppDrEvent>()
+                        .eq(VppDrEvent::getEventStatus, VppDrEventStatus.ENDED)
+                        .eq(VppDrEvent::getDeleteFlag, VppAuditHelper.NOT_DELETED));
+        Set<LocalDate> dates = new LinkedHashSet<>();
+        for (VppDrEvent event : events) {
+            if (event.getStartTime() != null) {
+                dates.add(event.getStartTime().toLocalDate());
+            }
+        }
+        return dates;
+    }
+
     @SuppressWarnings("unchecked")
     private String resolveReportRequestId(Map<String, Object> request) {
         String id = VppUnPayloadHelper.getString(request, "reportRequestID", "reportRequestId");

+ 2 - 0
service-vpp/service-vpp-biz/src/main/java/com/usky/vpp/service/vo/DrMonitorSiteResourceVO.java

@@ -29,5 +29,7 @@ public class DrMonitorSiteResourceVO {
         private Integer isSupportPeak;
         private BigDecimal maxUpKw;
         private BigDecimal minDownKw;
+        private Integer isSupportFm;
+        private String remark;
     }
 }

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

@@ -20,7 +20,7 @@ public class SiteDeclaredCapacityVO {
     private LocalDateTime responseEndTime;
     /** 响应申报容量 kW(历史均值 × 执行时段小时数 / 2) */
     private BigDecimal declaredCapacityKw;
-    /** 预估响应容量 kW(estimated_response_capacity_kw) */
+    /** 预估响应容量 kW:响应时段今日负荷均值 − 历史负荷均值 × 修正系数 */
     private BigDecimal estimatedResponseCapacityKw;
     /** 参与计算的典型历史日(yyyy-MM-dd) */
     private List<String> referenceDates;

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

@@ -26,6 +26,7 @@ public class SiteListVO {
     private String contactName;
     private String contactPhone;
     private Integer responsePriority;
+    private String unResourceId;
     /** 站点下资源类型列表(去重) */
     private List<String> resourceTypes;
     /** 站点下资源点数量 */

+ 14 - 7
service-vpp/service-vpp-biz/src/main/java/com/usky/vpp/util/VppBaselineHelper.java

@@ -329,16 +329,23 @@ public final class VppBaselineHelper {
     }
 
     /**
-     * 预估响应容量 = 响应时段内今日负荷均值。
+     * 预估响应容量 = 响应时段内今日负荷均值 − 响应时段内历史负荷均值 × 修正系数
      */
-    public static BigDecimal calculateEstimatedResponseCapacityKw(Map<String, BigDecimal> historicalAvgByTime,
-                                                         LocalDateTime windowStart,
-                                                         LocalDateTime windowEnd) {
-        BigDecimal historicalAvg = averageValuesInWindow(historicalAvgByTime, windowStart, windowEnd);
-        if (historicalAvg == null || historicalAvg.compareTo(BigDecimal.ZERO) <= 0) {
+    public static BigDecimal calculateEstimatedResponseCapacityKw(Map<String, BigDecimal> todayActualByTime,
+                                                                  Map<String, BigDecimal> historicalAvgByTime,
+                                                                  BigDecimal correctionFactorK,
+                                                                  LocalDateTime windowStart,
+                                                                  LocalDateTime windowEnd) {
+        BigDecimal todayAvg = averageValuesInWindow(todayActualByTime, windowStart, windowEnd);
+        if (todayAvg == null) {
             return BigDecimal.ZERO;
         }
-        return historicalAvg;
+        BigDecimal historicalAvg = averageValuesInWindow(historicalAvgByTime, windowStart, windowEnd);
+        BigDecimal k = correctionFactorK != null ? correctionFactorK : K_DEFAULT;
+        BigDecimal correctedHistorical = historicalAvg != null
+                ? historicalAvg.multiply(k)
+                : BigDecimal.ZERO;
+        return scale(todayAvg.subtract(correctedHistorical));
     }
 
     private static BigDecimal applyDeclaredCapacityFormula(BigDecimal historicalAvg,

+ 81 - 52
service-vpp/service-vpp-biz/src/main/java/com/usky/vpp/util/VppUnMessageBuilder.java

@@ -24,6 +24,8 @@ public final class VppUnMessageBuilder {
     private static final DateTimeFormatter CREATED_DATE_TIME =
             DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
 
+    private static final DateTimeFormatter MOMENT_DATE_TIME = DateTimeFormatter.ISO_LOCAL_DATE_TIME;
+
     private VppUnMessageBuilder() {
     }
 
@@ -98,48 +100,93 @@ public final class VppUnMessageBuilder {
         return resp;
     }
 
-    public static Map<String, Object> buildRegisterReportRequest(List<VppResourcePoint> resources,
-                                                                 Map<Long, VppSite> siteMap,
-                                                                 VppUnProperties properties) {
+    /**
+     * 构造 RegisterReportRequest:虚拟电厂 13 项量测点 + 站点/资源点有功功率(kW)。
+     */
+    public static Map<String, Object> buildRegisterReportRequest(VppUnProperties properties,
+                                                                 List<VppSite> sites,
+                                                                 List<VppResourcePoint> resources,
+                                                                 Map<Long, String> deviceUnResourceIdMap) {
         Map<String, Object> req = buildBaseRequest("RegisterReportRequest", properties);
         req.put("reportRequestID", "MetaDataReport");
         List<Map<String, Object>> reports = new ArrayList<>();
-        int rid = 0;
-        if (resources != null) {
-            for (VppResourcePoint resource : resources) {
-                VppSite site = siteMap != null ? siteMap.get(resource.getSiteId()) : null;
-                reports.add(buildMetadataReportItem(resource, site, rid++, properties));
-            }
-        }
-        if (reports.isEmpty()) {
-            reports.add(buildDefaultVppMetadataReport(properties));
+        String createdDateTime = CREATED_DATE_TIME.format(LocalDateTime.now());
+        for (VppUnRegisterReportIndex.Entry entry : VppUnRegisterReportIndex.buildEntries(
+                properties, sites, resources, deviceUnResourceIdMap)) {
+            String resourceId = resolveRegisterResourceId(entry, properties, deviceUnResourceIdMap);
+            reports.add(buildVppMetadataReportItem(entry.getRid(), entry.getMetric(), resourceId, createdDateTime));
         }
         req.put("report", reports);
         return req;
     }
 
-    public static Map<String, Object> buildIntervalDataReportRequest(Map<String, Object> createReportRequest,
-                                                                     List<VppResourcePoint> resources,
-                                                                     Map<Long, VppSite> siteMap,
-                                                                     VppUnProperties properties) {
+    private static String resolveRegisterResourceId(VppUnRegisterReportIndex.Entry entry,
+                                                      VppUnProperties properties,
+                                                      Map<Long, String> deviceUnResourceIdMap) {
+        if (entry.getScope() == VppUnRegisterReportIndex.Scope.SITE) {
+            return entry.getSite().getUnResourceId();
+        }
+        if (entry.getScope() == VppUnRegisterReportIndex.Scope.RESOURCE) {
+            return deviceUnResourceIdMap.get(entry.getResource().getDeviceId());
+        }
+        return properties.getDnId();
+    }
+
+    public static Map<String, Object> buildIntervalDataReportRequest(VppUnProperties properties,
+                                                                     Map<String, Object> createReportRequest,
+                                                                     List<Map<String, Object>> pointCurveData,
+                                                                     LocalDateTime reportTime) {
         Map<String, Object> req = buildBaseRequest("IntervalDataReportRequest", properties);
         req.put("reportRequestID", resolveReportRequestIdFromPoll(createReportRequest, "IntervalDataReport"));
-        req.put("createdDateTime", CREATED_DATE_TIME.format(LocalDateTime.now()));
-        req.put("pointData", buildPointData(resources, siteMap, properties));
+        LocalDateTime createdAt = reportTime != null ? reportTime : LocalDateTime.now();
+        req.put("createdDateTime", MOMENT_DATE_TIME.format(createdAt));
+        req.put("pointCurveData", pointCurveData != null ? pointCurveData : new ArrayList<>());
         return req;
     }
 
-    public static Map<String, Object> buildMomentDataReportRequest(Map<String, Object> createReportRequest,
-                                                                   List<VppResourcePoint> resources,
-                                                                   Map<Long, VppSite> siteMap,
-                                                                   VppUnProperties properties) {
+    public static Map<String, Object> buildIntervalCurveDataEntry(int rid, List<Map<String, Object>> values) {
+        Map<String, Object> entry = new LinkedHashMap<>();
+        entry.put("rID", rid);
+        Map<String, Object> irregular = new LinkedHashMap<>();
+        irregular.put("values", values != null ? values : new ArrayList<>());
+        entry.put("irregular", irregular);
+        return entry;
+    }
+
+    public static Map<String, Object> buildIntervalCurveValue(BigDecimal value,
+                                                              LocalDateTime timestamp,
+                                                              String quality) {
+        Map<String, Object> point = new LinkedHashMap<>();
+        point.put("value", value != null ? value : BigDecimal.ZERO);
+        point.put("timestamp", MOMENT_DATE_TIME.format(timestamp != null ? timestamp : LocalDateTime.now()));
+        point.put("quality", StringUtils.hasText(quality) ? quality : "good");
+        return point;
+    }
+
+    public static Map<String, Object> buildMomentDataReportRequest(VppUnProperties properties,
+                                                                   Map<String, Object> createReportRequest,
+                                                                   List<Map<String, Object>> pointData,
+                                                                   LocalDateTime reportTime) {
         Map<String, Object> req = buildBaseRequest("MomentDataReportRequest", properties);
         req.put("reportRequestID", resolveReportRequestIdFromPoll(createReportRequest, "MomentDataReport"));
-        req.put("createdDateTime", CREATED_DATE_TIME.format(LocalDateTime.now()));
-        req.put("pointData", buildPointData(resources, siteMap, properties));
+        LocalDateTime createdAt = reportTime != null ? reportTime : LocalDateTime.now();
+        req.put("createdDateTime", MOMENT_DATE_TIME.format(createdAt));
+        req.put("pointData", pointData != null ? pointData : new ArrayList<>());
         return req;
     }
 
+    public static Map<String, Object> buildMomentDataPoint(int rid,
+                                                           BigDecimal value,
+                                                           LocalDateTime timestamp,
+                                                           String quality) {
+        Map<String, Object> point = new LinkedHashMap<>();
+        point.put("rID", rid);
+        point.put("value", value != null ? value : BigDecimal.ZERO);
+        point.put("timestamp", MOMENT_DATE_TIME.format(timestamp != null ? timestamp : LocalDateTime.now()));
+        point.put("quality", StringUtils.hasText(quality) ? quality : "good");
+        return point;
+    }
+
     public static Map<String, Object> buildLoadForecastReportRequest(Map<String, Object> createReportRequest,
                                                                    List<VppResourcePoint> resources,
                                                                    Map<Long, VppSite> siteMap,
@@ -162,40 +209,22 @@ public final class VppUnMessageBuilder {
         return req;
     }
 
-    private static Map<String, Object> buildDefaultVppMetadataReport(VppUnProperties properties) {
-        Map<String, Object> report = new LinkedHashMap<>();
-        report.put("createdDateTime", CREATED_DATE_TIME.format(LocalDateTime.now()));
-        Map<String, Object> description = new LinkedHashMap<>();
-        description.put("rID", 0);
-        Map<String, Object> metric = new LinkedHashMap<>();
-        metric.put("metricName", "AP");
-        metric.put("multiplier", "k");
-        metric.put("symbol", "W");
-        description.put("metric", metric);
-        Map<String, Object> dataSource = new LinkedHashMap<>();
-        dataSource.put("resourceID", java.util.Collections.singletonList(properties.getDnId()));
-        description.put("reportDataSource", dataSource);
-        description.put("readingType", "Summed");
-        report.put("reportDescription", description);
-        return report;
-    }
-
-    private static Map<String, Object> buildMetadataReportItem(VppResourcePoint resource, VppSite site, int rid,
-                                                               VppUnProperties properties) {
+    private static Map<String, Object> buildVppMetadataReportItem(int rid,
+                                                                  VppUnRegisterMetricDefinitions.RegisterMetric metric,
+                                                                  String resourceId,
+                                                                  String createdDateTime) {
         Map<String, Object> report = new LinkedHashMap<>();
-        report.put("createdDateTime", CREATED_DATE_TIME.format(LocalDateTime.now()));
+        report.put("createdDateTime", createdDateTime);
         Map<String, Object> description = new LinkedHashMap<>();
         description.put("rID", rid);
-        Map<String, Object> metric = new LinkedHashMap<>();
-        metric.put("metricName", "AP");
-        metric.put("multiplier", "k");
-        metric.put("symbol", "W");
-        description.put("metric", metric);
+        Map<String, Object> metricBody = new LinkedHashMap<>();
+        metricBody.put("metricName", metric.getMetricName());
+        metricBody.put("multiplier", metric.getMultiplier());
+        metricBody.put("symbol", metric.getSymbol());
+        description.put("metric", metricBody);
         Map<String, Object> dataSource = new LinkedHashMap<>();
-        String resourceId = resolveUnResourceId(resource, site, properties);
         dataSource.put("resourceID", java.util.Collections.singletonList(resourceId));
         description.put("reportDataSource", dataSource);
-        description.put("readingType", "Summed");
         report.put("reportDescription", description);
         return report;
     }

+ 8 - 0
service-vpp/service-vpp-biz/src/main/java/com/usky/vpp/web/advice/VppUnDnCryptoRequestAdvice.java

@@ -29,6 +29,9 @@ public class VppUnDnCryptoRequestAdvice extends RequestBodyAdviceAdapter {
     @Override
     public boolean supports(MethodParameter methodParameter, Type targetType,
                             Class<? extends HttpMessageConverter<?>> converterType) {
+        if (isTokenRequest(methodParameter)) {
+            return false;
+        }
         return cryptoService.isAnyActive();
     }
 
@@ -48,6 +51,11 @@ public class VppUnDnCryptoRequestAdvice extends RequestBodyAdviceAdapter {
         return new DecodedHttpInputMessage(inputMessage.getHeaders(), plain.getBytes(StandardCharsets.UTF_8));
     }
 
+    private static boolean isTokenRequest(MethodParameter methodParameter) {
+        return methodParameter.getMethod() != null
+                && "tokenRequest".equals(methodParameter.getMethod().getName());
+    }
+
     private static class DecodedHttpInputMessage implements HttpInputMessage {
         private final HttpHeaders headers;
         private final byte[] body;

+ 3 - 0
service-vpp/service-vpp-biz/src/main/java/com/usky/vpp/web/advice/VppUnDnCryptoResponseAdvice.java

@@ -26,6 +26,9 @@ public class VppUnDnCryptoResponseAdvice implements ResponseBodyAdvice<Object> {
 
     @Override
     public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
+        if (returnType.getMethod() != null && "tokenRequest".equals(returnType.getMethod().getName())) {
+            return false;
+        }
         return cryptoService.isAnyActive();
     }