|
@@ -8,12 +8,14 @@ import com.usky.common.security.utils.SecurityUtils;
|
|
|
import com.usky.vpp.constant.VppDrEventStatus;
|
|
import com.usky.vpp.constant.VppDrEventStatus;
|
|
|
import com.usky.vpp.domain.VppBiddingConfig;
|
|
import com.usky.vpp.domain.VppBiddingConfig;
|
|
|
import com.usky.vpp.domain.VppCustomer;
|
|
import com.usky.vpp.domain.VppCustomer;
|
|
|
|
|
+import com.usky.vpp.domain.VppDevice;
|
|
|
import com.usky.vpp.domain.VppDrEvent;
|
|
import com.usky.vpp.domain.VppDrEvent;
|
|
|
import com.usky.vpp.domain.VppDrInvitation;
|
|
import com.usky.vpp.domain.VppDrInvitation;
|
|
|
import com.usky.vpp.domain.VppResourcePoint;
|
|
import com.usky.vpp.domain.VppResourcePoint;
|
|
|
import com.usky.vpp.domain.VppResponseDeviationPrice;
|
|
import com.usky.vpp.domain.VppResponseDeviationPrice;
|
|
|
import com.usky.vpp.domain.VppSite;
|
|
import com.usky.vpp.domain.VppSite;
|
|
|
import com.usky.vpp.mapper.VppCustomerMapper;
|
|
import com.usky.vpp.mapper.VppCustomerMapper;
|
|
|
|
|
+import com.usky.vpp.mapper.VppDeviceMapper;
|
|
|
import com.usky.vpp.mapper.VppDrEventMapper;
|
|
import com.usky.vpp.mapper.VppDrEventMapper;
|
|
|
import com.usky.vpp.mapper.VppDrInvitationMapper;
|
|
import com.usky.vpp.mapper.VppDrInvitationMapper;
|
|
|
import com.usky.vpp.mapper.VppResourcePointMapper;
|
|
import com.usky.vpp.mapper.VppResourcePointMapper;
|
|
@@ -21,6 +23,7 @@ import com.usky.vpp.mapper.VppSiteMapper;
|
|
|
import com.usky.vpp.service.VppBaselineService;
|
|
import com.usky.vpp.service.VppBaselineService;
|
|
|
import com.usky.vpp.service.VppDrMonitorService;
|
|
import com.usky.vpp.service.VppDrMonitorService;
|
|
|
import com.usky.vpp.service.VppSubsidyPredictionCalculator;
|
|
import com.usky.vpp.service.VppSubsidyPredictionCalculator;
|
|
|
|
|
+import com.usky.vpp.service.VppTsdbQueryService;
|
|
|
import com.usky.vpp.service.vo.BaselinePointVO;
|
|
import com.usky.vpp.service.vo.BaselinePointVO;
|
|
|
import com.usky.vpp.service.vo.DrMonitorCurveVO;
|
|
import com.usky.vpp.service.vo.DrMonitorCurveVO;
|
|
|
import com.usky.vpp.service.vo.DrMonitorRecordVO;
|
|
import com.usky.vpp.service.vo.DrMonitorRecordVO;
|
|
@@ -29,6 +32,7 @@ import com.usky.vpp.service.vo.DrMonitorSummaryVO;
|
|
|
import com.usky.vpp.service.vo.SiteBaselineVO;
|
|
import com.usky.vpp.service.vo.SiteBaselineVO;
|
|
|
import com.usky.vpp.service.vo.SubsidyPredictionCalcResult;
|
|
import com.usky.vpp.service.vo.SubsidyPredictionCalcResult;
|
|
|
import com.usky.vpp.util.VppAuditHelper;
|
|
import com.usky.vpp.util.VppAuditHelper;
|
|
|
|
|
+import com.usky.vpp.util.VppBaselineHelper;
|
|
|
import com.usky.vpp.util.VppDrParticipationHelper;
|
|
import com.usky.vpp.util.VppDrParticipationHelper;
|
|
|
import com.usky.vpp.util.VppPageHelper;
|
|
import com.usky.vpp.util.VppPageHelper;
|
|
|
import com.usky.vpp.util.VppResourceTypeHelper;
|
|
import com.usky.vpp.util.VppResourceTypeHelper;
|
|
@@ -48,6 +52,7 @@ import java.time.format.DateTimeParseException;
|
|
|
import java.util.ArrayList;
|
|
import java.util.ArrayList;
|
|
|
import java.util.Collections;
|
|
import java.util.Collections;
|
|
|
import java.util.Comparator;
|
|
import java.util.Comparator;
|
|
|
|
|
+import java.util.LinkedHashMap;
|
|
|
import java.util.LinkedHashSet;
|
|
import java.util.LinkedHashSet;
|
|
|
import java.util.List;
|
|
import java.util.List;
|
|
|
import java.util.Map;
|
|
import java.util.Map;
|
|
@@ -67,8 +72,11 @@ public class VppDrMonitorServiceImpl implements VppDrMonitorService {
|
|
|
private static final int REPLY_ACCEPT = 1;
|
|
private static final int REPLY_ACCEPT = 1;
|
|
|
private static final int SUPPORT_PEAK = 1;
|
|
private static final int SUPPORT_PEAK = 1;
|
|
|
private static final int SCALE = 4;
|
|
private static final int SCALE = 4;
|
|
|
|
|
+ private static final int CURVE_BEFORE_HOURS = 2;
|
|
|
|
|
+ private static final int CURVE_AFTER_HOURS = 1;
|
|
|
private static final DateTimeFormatter DATE_FMT = DateTimeFormatter.ofPattern("yyyy-MM-dd");
|
|
private static final DateTimeFormatter DATE_FMT = DateTimeFormatter.ofPattern("yyyy-MM-dd");
|
|
|
private static final DateTimeFormatter DATE_TIME_FMT = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
|
|
private static final DateTimeFormatter DATE_TIME_FMT = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
|
|
|
|
|
+ private static final DateTimeFormatter TIME_FMT = DateTimeFormatter.ofPattern("HH:mm");
|
|
|
private static final BigDecimal ONE = BigDecimal.ONE;
|
|
private static final BigDecimal ONE = BigDecimal.ONE;
|
|
|
private static final BigDecimal HUNDRED = new BigDecimal("100");
|
|
private static final BigDecimal HUNDRED = new BigDecimal("100");
|
|
|
|
|
|
|
@@ -81,10 +89,14 @@ public class VppDrMonitorServiceImpl implements VppDrMonitorService {
|
|
|
@Autowired
|
|
@Autowired
|
|
|
private VppResourcePointMapper resourcePointMapper;
|
|
private VppResourcePointMapper resourcePointMapper;
|
|
|
@Autowired
|
|
@Autowired
|
|
|
|
|
+ private VppDeviceMapper deviceMapper;
|
|
|
|
|
+ @Autowired
|
|
|
private VppSiteMapper siteMapper;
|
|
private VppSiteMapper siteMapper;
|
|
|
@Autowired
|
|
@Autowired
|
|
|
private VppBaselineService baselineService;
|
|
private VppBaselineService baselineService;
|
|
|
@Autowired
|
|
@Autowired
|
|
|
|
|
+ private VppTsdbQueryService tsdbQueryService;
|
|
|
|
|
+ @Autowired
|
|
|
private VppSubsidyPredictionCalculator subsidyPredictionCalculator;
|
|
private VppSubsidyPredictionCalculator subsidyPredictionCalculator;
|
|
|
|
|
|
|
|
@Override
|
|
@Override
|
|
@@ -180,9 +192,19 @@ public class VppDrMonitorServiceImpl implements VppDrMonitorService {
|
|
|
if (event != null && VppAuditHelper.isDeleted(event.getDeleteFlag())) {
|
|
if (event != null && VppAuditHelper.isDeleted(event.getDeleteFlag())) {
|
|
|
event = null;
|
|
event = null;
|
|
|
}
|
|
}
|
|
|
- String responseStartTime = resolveInvitationResponseStartTime(invitation, event);
|
|
|
|
|
- SiteBaselineVO baseline = baselineService.getSiteBaseline(invitation.getSiteId(), responseStartTime);
|
|
|
|
|
|
|
+ LocalDateTime responseStart = resolveInvitationResponseStart(invitation, event);
|
|
|
|
|
+ LocalDateTime responseEnd = resolveInvitationResponseEnd(invitation, event);
|
|
|
|
|
+ LocalDateTime queryStart = responseStart.minusHours(CURVE_BEFORE_HOURS);
|
|
|
|
|
+ LocalDateTime queryEnd = responseEnd.plusHours(CURVE_AFTER_HOURS);
|
|
|
|
|
+ if (queryEnd.isBefore(queryStart)) {
|
|
|
|
|
+ throw new BusinessException("响应时段无效");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ SiteBaselineVO baseline = baselineService.getSiteBaseline(
|
|
|
|
|
+ invitation.getSiteId(), responseStart.format(DATE_TIME_FMT));
|
|
|
BigDecimal actualKw = nz(invitation.getActualResponseCapacityKw()).setScale(SCALE, RoundingMode.HALF_UP);
|
|
BigDecimal actualKw = nz(invitation.getActualResponseCapacityKw()).setScale(SCALE, RoundingMode.HALF_UP);
|
|
|
|
|
+ Map<String, BigDecimal> actualLoadByTime = queryActualLoadByTime(
|
|
|
|
|
+ invitation.getSiteId(), tenantId, queryStart, queryEnd);
|
|
|
|
|
|
|
|
DrMonitorCurveVO curve = new DrMonitorCurveVO();
|
|
DrMonitorCurveVO curve = new DrMonitorCurveVO();
|
|
|
curve.setId(invitation.getId());
|
|
curve.setId(invitation.getId());
|
|
@@ -198,19 +220,22 @@ public class VppDrMonitorServiceImpl implements VppDrMonitorService {
|
|
|
}
|
|
}
|
|
|
if (baseline != null && StringUtils.hasText(baseline.getDataSource())) {
|
|
if (baseline != null && StringUtils.hasText(baseline.getDataSource())) {
|
|
|
curve.setDataSource(baseline.getDataSource());
|
|
curve.setDataSource(baseline.getDataSource());
|
|
|
|
|
+ } else if (!actualLoadByTime.isEmpty()) {
|
|
|
|
|
+ curve.setDataSource("tsdb");
|
|
|
}
|
|
}
|
|
|
- if (baseline == null || CollectionUtils.isEmpty(baseline.getPoints())) {
|
|
|
|
|
- return curve;
|
|
|
|
|
- }
|
|
|
|
|
- for (BaselinePointVO point : baseline.getPoints()) {
|
|
|
|
|
- if (!StringUtils.hasText(point.getTime())) {
|
|
|
|
|
- continue;
|
|
|
|
|
- }
|
|
|
|
|
|
|
+
|
|
|
|
|
+ Map<String, BigDecimal> baselineByTime = toBaselineByTime(baseline);
|
|
|
|
|
+ int interval = VppBaselineHelper.INTERVAL_MINUTES;
|
|
|
|
|
+ LocalDateTime cursor = alignToInterval(queryStart, interval);
|
|
|
|
|
+ while (!cursor.isAfter(queryEnd)) {
|
|
|
|
|
+ String timeKey = cursor.toLocalTime().format(TIME_FMT);
|
|
|
DrMonitorCurveVO.Point curvePoint = new DrMonitorCurveVO.Point();
|
|
DrMonitorCurveVO.Point curvePoint = new DrMonitorCurveVO.Point();
|
|
|
- curvePoint.setTime(point.getTime());
|
|
|
|
|
- curvePoint.setBaselineKw(nz(point.getPredictedBaselineKw()).setScale(SCALE, RoundingMode.HALF_UP));
|
|
|
|
|
|
|
+ curvePoint.setTime(timeKey);
|
|
|
|
|
+ curvePoint.setBaselineKw(scaleKw(baselineByTime.get(timeKey)));
|
|
|
|
|
+ curvePoint.setActualLoadKw(scaleKw(actualLoadByTime.get(timeKey)));
|
|
|
curvePoint.setActualResponseCapacityKw(actualKw);
|
|
curvePoint.setActualResponseCapacityKw(actualKw);
|
|
|
curve.getPoints().add(curvePoint);
|
|
curve.getPoints().add(curvePoint);
|
|
|
|
|
+ cursor = cursor.plusMinutes(interval);
|
|
|
}
|
|
}
|
|
|
return curve;
|
|
return curve;
|
|
|
}
|
|
}
|
|
@@ -399,18 +424,94 @@ public class VppDrMonitorServiceImpl implements VppDrMonitorService {
|
|
|
return vo;
|
|
return vo;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- private String resolveInvitationResponseStartTime(VppDrInvitation invitation, VppDrEvent event) {
|
|
|
|
|
|
|
+ private LocalDateTime resolveInvitationResponseStart(VppDrInvitation invitation, VppDrEvent event) {
|
|
|
if (invitation.getExecuteStartDate() != null) {
|
|
if (invitation.getExecuteStartDate() != null) {
|
|
|
LocalTime timeOfDay = (event != null && event.getStartTime() != null)
|
|
LocalTime timeOfDay = (event != null && event.getStartTime() != null)
|
|
|
? event.getStartTime().toLocalTime() : LocalTime.MIN;
|
|
? event.getStartTime().toLocalTime() : LocalTime.MIN;
|
|
|
- return LocalDateTime.of(invitation.getExecuteStartDate(), timeOfDay).format(DATE_TIME_FMT);
|
|
|
|
|
|
|
+ return LocalDateTime.of(invitation.getExecuteStartDate(), timeOfDay);
|
|
|
}
|
|
}
|
|
|
if (event != null && event.getStartTime() != null) {
|
|
if (event != null && event.getStartTime() != null) {
|
|
|
- return event.getStartTime().format(DATE_TIME_FMT);
|
|
|
|
|
|
|
+ return event.getStartTime();
|
|
|
}
|
|
}
|
|
|
throw new BusinessException("邀约缺少响应时间");
|
|
throw new BusinessException("邀约缺少响应时间");
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ private LocalDateTime resolveInvitationResponseEnd(VppDrInvitation invitation, VppDrEvent event) {
|
|
|
|
|
+ if (invitation.getExecuteEndDate() != null) {
|
|
|
|
|
+ LocalTime timeOfDay = (event != null && event.getEndTime() != null)
|
|
|
|
|
+ ? event.getEndTime().toLocalTime() : LocalTime.of(23, 59, 59);
|
|
|
|
|
+ return LocalDateTime.of(invitation.getExecuteEndDate(), timeOfDay);
|
|
|
|
|
+ }
|
|
|
|
|
+ if (event != null && event.getEndTime() != null) {
|
|
|
|
|
+ return event.getEndTime();
|
|
|
|
|
+ }
|
|
|
|
|
+ throw new BusinessException("邀约缺少响应结束时间");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 按站点可控设备查询实际负荷,时间范围为响应开始前 2 小时至结束后 1 小时。
|
|
|
|
|
+ * <p>调用方式对齐 {@link VppTsdbQueryService#queryBaselineAvgByTimePoint}:
|
|
|
|
|
+ * 设备 UUID + 时段 + {@link VppBaselineHelper#POWER_METRICS},再按 interval 聚合成 HH:mm。</p>
|
|
|
|
|
+ */
|
|
|
|
|
+ private Map<String, BigDecimal> queryActualLoadByTime(Long siteId, Integer tenantId,
|
|
|
|
|
+ LocalDateTime queryStart, LocalDateTime queryEnd) {
|
|
|
|
|
+ List<String> deviceUuids = listSiteDeviceUuids(siteId, tenantId);
|
|
|
|
|
+ if (deviceUuids.isEmpty()) {
|
|
|
|
|
+ return Collections.emptyMap();
|
|
|
|
|
+ }
|
|
|
|
|
+ return VppBaselineHelper.buildActualLoadByTimePoint(
|
|
|
|
|
+ tsdbQueryService.queryDeviceMetricHistory(
|
|
|
|
|
+ deviceUuids, queryStart, queryEnd, VppBaselineHelper.POWER_METRICS),
|
|
|
|
|
+ VppBaselineHelper.INTERVAL_MINUTES);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private List<String> listSiteDeviceUuids(Long siteId, Integer tenantId) {
|
|
|
|
|
+ List<VppResourcePoint> resources = listPeakResources(Collections.singletonList(siteId), tenantId);
|
|
|
|
|
+ if (CollectionUtils.isEmpty(resources)) {
|
|
|
|
|
+ return Collections.emptyList();
|
|
|
|
|
+ }
|
|
|
|
|
+ Set<Long> deviceIds = resources.stream()
|
|
|
|
|
+ .map(VppResourcePoint::getDeviceId)
|
|
|
|
|
+ .filter(Objects::nonNull)
|
|
|
|
|
+ .collect(Collectors.toSet());
|
|
|
|
|
+ if (deviceIds.isEmpty()) {
|
|
|
|
|
+ return Collections.emptyList();
|
|
|
|
|
+ }
|
|
|
|
|
+ Set<String> uuids = new LinkedHashSet<>();
|
|
|
|
|
+ for (VppDevice device : deviceMapper.selectBatchIds(deviceIds)) {
|
|
|
|
|
+ if (device == null || VppAuditHelper.isDeleted(device.getDeleteFlag())) {
|
|
|
|
|
+ continue;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (StringUtils.hasText(device.getDeviceUuid())) {
|
|
|
|
|
+ uuids.add(device.getDeviceUuid().trim());
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ return new ArrayList<>(uuids);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private static Map<String, BigDecimal> toBaselineByTime(SiteBaselineVO baseline) {
|
|
|
|
|
+ if (baseline == null || CollectionUtils.isEmpty(baseline.getPoints())) {
|
|
|
|
|
+ return Collections.emptyMap();
|
|
|
|
|
+ }
|
|
|
|
|
+ Map<String, BigDecimal> result = new LinkedHashMap<>();
|
|
|
|
|
+ for (BaselinePointVO point : baseline.getPoints()) {
|
|
|
|
|
+ if (!StringUtils.hasText(point.getTime())) {
|
|
|
|
|
+ continue;
|
|
|
|
|
+ }
|
|
|
|
|
+ result.put(point.getTime(), point.getPredictedBaselineKw());
|
|
|
|
|
+ }
|
|
|
|
|
+ return result;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private static LocalDateTime alignToInterval(LocalDateTime time, int intervalMinutes) {
|
|
|
|
|
+ int minute = time.getMinute() / intervalMinutes * intervalMinutes;
|
|
|
|
|
+ return time.withMinute(minute).withSecond(0).withNano(0);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private static BigDecimal scaleKw(BigDecimal value) {
|
|
|
|
|
+ return value == null ? null : value.setScale(SCALE, RoundingMode.HALF_UP);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
private List<VppResourcePoint> listPeakResources(List<Long> siteIds, Integer tenantId) {
|
|
private List<VppResourcePoint> listPeakResources(List<Long> siteIds, Integer tenantId) {
|
|
|
return resourcePointMapper.selectList(new LambdaQueryWrapper<VppResourcePoint>()
|
|
return resourcePointMapper.selectList(new LambdaQueryWrapper<VppResourcePoint>()
|
|
|
.eq(VppResourcePoint::getDeleteFlag, VppAuditHelper.NOT_DELETED)
|
|
.eq(VppResourcePoint::getDeleteFlag, VppAuditHelper.NOT_DELETED)
|