|
|
@@ -37,6 +37,7 @@ import java.sql.ResultSet;
|
|
|
import java.time.LocalDate;
|
|
|
import java.time.LocalDateTime;
|
|
|
import java.time.LocalTime;
|
|
|
+import java.time.YearMonth;
|
|
|
import java.time.format.DateTimeFormatter;
|
|
|
import java.util.*;
|
|
|
import java.util.stream.Collectors;
|
|
|
@@ -1028,20 +1029,14 @@ public class EmsAnalysisServiceImpl implements EmsAnalysisService {
|
|
|
resp.setEnergyTypeName(id >= 1 && id <= 3 ? ENERGY_TYPE_NAMES[(int) id] : "");
|
|
|
}
|
|
|
|
|
|
- // 设置时间维度
|
|
|
- resp.setDimension(request.getTimeDimension());
|
|
|
-
|
|
|
- // 为每个设备查询对比数据
|
|
|
- List<EmsCompareSeriesItemVO> series = new ArrayList<>();
|
|
|
-
|
|
|
- for (String deviceId : request.getDeviceIds()) {
|
|
|
- EmsCompareSeriesItemVO seriesItem = getDeviceCompareData(deviceId, request.getEnergyTypeId(),
|
|
|
- request.getTimeDimension(), request.getTimeValues());
|
|
|
- if (seriesItem != null) {
|
|
|
- series.add(seriesItem);
|
|
|
- }
|
|
|
- }
|
|
|
+ // 根据查询类型和起止时间生成完整时间轴
|
|
|
+ List<String> timeSeries = generateCompareTimeSeries(request.getTimeDimension(), request.getTimeValues());
|
|
|
+ resp.setTimeDimension(request.getTimeDimension());
|
|
|
+ resp.setDimension(timeSeries);
|
|
|
|
|
|
+ // 批量查询设备历史数据并按时间轴计算对比值
|
|
|
+ List<EmsCompareSeriesItemVO> series = buildCompareSeries(
|
|
|
+ request.getDeviceIds(), request.getTimeDimension(), timeSeries);
|
|
|
resp.setSeries(series);
|
|
|
|
|
|
} catch (Exception e) {
|
|
|
@@ -1052,102 +1047,210 @@ public class EmsAnalysisServiceImpl implements EmsAnalysisService {
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * 获取设备对比数据
|
|
|
+ * 根据查询类型(日/月/年)和起止时间,生成完整时间序列
|
|
|
*/
|
|
|
- private EmsCompareSeriesItemVO getDeviceCompareData(String deviceId, Long energyTypeId, String timeDimension, List<String> timeValues) {
|
|
|
- try (Connection conn = mysqlDataSource.getConnection()) {
|
|
|
- // 获取设备名称
|
|
|
- String deviceName = getDeviceName(deviceId, conn);
|
|
|
-
|
|
|
- List<EmsCompareValueVO> values = new ArrayList<>();
|
|
|
-
|
|
|
- // 为每个时间点查询数据
|
|
|
- for (String timeValue : timeValues) {
|
|
|
- String[] timeRange = getTimeRange(timeDimension, timeValue);
|
|
|
- BigDecimal usage = queryDeviceEnergyForCompare(deviceId, energyTypeId, timeRange[0], timeRange[1], conn);
|
|
|
+ private List<String> generateCompareTimeSeries(String timeDimension, List<String> timeValues) {
|
|
|
+ if (timeValues == null || timeValues.isEmpty()) {
|
|
|
+ return new ArrayList<>();
|
|
|
+ }
|
|
|
+ String start = timeValues.get(0).trim();
|
|
|
+ String end = (timeValues.size() > 1 ? timeValues.get(timeValues.size() - 1) : start).trim();
|
|
|
+ if (start.isEmpty()) {
|
|
|
+ return new ArrayList<>();
|
|
|
+ }
|
|
|
+ if (end.isEmpty()) {
|
|
|
+ end = start;
|
|
|
+ }
|
|
|
|
|
|
- EmsCompareValueVO valueItem = new EmsCompareValueVO();
|
|
|
- valueItem.setTimeValue(timeValue);
|
|
|
- valueItem.setUsage(usage.setScale(2, RoundingMode.HALF_UP));
|
|
|
- values.add(valueItem);
|
|
|
+ try {
|
|
|
+ switch (timeDimension.toUpperCase()) {
|
|
|
+ case "D":
|
|
|
+ return generateDailyRange(start, end);
|
|
|
+ case "M":
|
|
|
+ return generateMonthlyRange(start, end);
|
|
|
+ case "Y":
|
|
|
+ return generateYearlyRange(start, end);
|
|
|
+ default:
|
|
|
+ return new ArrayList<>(timeValues);
|
|
|
}
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("生成对比时间序列失败: timeDimension={}, timeValues={}", timeDimension, timeValues, e);
|
|
|
+ return new ArrayList<>(timeValues);
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
- EmsCompareSeriesItemVO seriesItem = new EmsCompareSeriesItemVO();
|
|
|
- seriesItem.setDeviceId(deviceId);
|
|
|
- seriesItem.setDeviceName(deviceName);
|
|
|
- seriesItem.setValues(values);
|
|
|
-
|
|
|
- return seriesItem;
|
|
|
+ private List<String> generateDailyRange(String start, String end) {
|
|
|
+ List<String> points = new ArrayList<>();
|
|
|
+ LocalDate startDate = LocalDate.parse(start, DateTimeFormatter.ISO_LOCAL_DATE);
|
|
|
+ LocalDate endDate = LocalDate.parse(end, DateTimeFormatter.ISO_LOCAL_DATE);
|
|
|
+ if (endDate.isBefore(startDate)) {
|
|
|
+ LocalDate tmp = startDate;
|
|
|
+ startDate = endDate;
|
|
|
+ endDate = tmp;
|
|
|
+ }
|
|
|
+ DateTimeFormatter formatter = DateTimeFormatter.ISO_LOCAL_DATE;
|
|
|
+ for (LocalDate date = startDate; !date.isAfter(endDate); date = date.plusDays(1)) {
|
|
|
+ points.add(date.format(formatter));
|
|
|
+ }
|
|
|
+ return points;
|
|
|
+ }
|
|
|
|
|
|
- } catch (Exception e) {
|
|
|
- System.err.println("Error getting device compare data: " + e.getMessage());
|
|
|
- return null;
|
|
|
+ private List<String> generateMonthlyRange(String start, String end) {
|
|
|
+ List<String> points = new ArrayList<>();
|
|
|
+ YearMonth startMonth = YearMonth.parse(start, DateTimeFormatter.ofPattern("yyyy-MM"));
|
|
|
+ YearMonth endMonth = YearMonth.parse(end, DateTimeFormatter.ofPattern("yyyy-MM"));
|
|
|
+ if (endMonth.isBefore(startMonth)) {
|
|
|
+ YearMonth tmp = startMonth;
|
|
|
+ startMonth = endMonth;
|
|
|
+ endMonth = tmp;
|
|
|
+ }
|
|
|
+ DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM");
|
|
|
+ for (YearMonth month = startMonth; !month.isAfter(endMonth); month = month.plusMonths(1)) {
|
|
|
+ points.add(month.format(formatter));
|
|
|
}
|
|
|
+ return points;
|
|
|
}
|
|
|
|
|
|
- /**
|
|
|
- * 获取设备名称
|
|
|
- */
|
|
|
- private String getDeviceName(String deviceId, Connection conn) {
|
|
|
- try {
|
|
|
- String sql = "SELECT name FROM ems_device WHERE id = ?";
|
|
|
- try (PreparedStatement stmt = conn.prepareStatement(sql)) {
|
|
|
- stmt.setString(1, deviceId);
|
|
|
- try (ResultSet rs = stmt.executeQuery()) {
|
|
|
- if (rs.next()) {
|
|
|
- return rs.getString("name");
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- } catch (Exception e) {
|
|
|
- System.err.println("Error getting device name: " + e.getMessage());
|
|
|
+ private List<String> generateYearlyRange(String start, String end) {
|
|
|
+ List<String> points = new ArrayList<>();
|
|
|
+ int startYear = Integer.parseInt(start);
|
|
|
+ int endYear = Integer.parseInt(end);
|
|
|
+ if (endYear < startYear) {
|
|
|
+ int tmp = startYear;
|
|
|
+ startYear = endYear;
|
|
|
+ endYear = tmp;
|
|
|
+ }
|
|
|
+ for (int year = startYear; year <= endYear; year++) {
|
|
|
+ points.add(String.valueOf(year));
|
|
|
}
|
|
|
- return "未知设备";
|
|
|
+ return points;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * 查询设备能耗(用于对比)
|
|
|
+ * 批量查询设备对比数据:一次拉取 TSDB 历史数据,再按时间轴分段计算用量
|
|
|
*/
|
|
|
- private BigDecimal queryDeviceEnergyForCompare(String deviceId, Long energyTypeId, String startTime, String endTime, Connection conn) {
|
|
|
- try {
|
|
|
- StringBuilder sql = new StringBuilder();
|
|
|
- sql.append("SELECT SUM(eed.end_value - eed.start_value) as total_usage ");
|
|
|
- sql.append("FROM ems_energy_consumption_device_data eed ");
|
|
|
- if (energyTypeId != null) {
|
|
|
- sql.append("INNER JOIN ems_device ed ON eed.device_id = ed.id ");
|
|
|
- sql.append("INNER JOIN ems_product_energy_type epe ON ed.product_id = epe.product_id ");
|
|
|
- }
|
|
|
- sql.append("WHERE eed.device_id = ? ");
|
|
|
- sql.append("AND eed.start_time >= ? AND eed.end_time <= ? ");
|
|
|
-
|
|
|
- if (energyTypeId != null) {
|
|
|
- sql.append("AND epe.energy_type = ? ");
|
|
|
- }
|
|
|
+ private List<EmsCompareSeriesItemVO> buildCompareSeries(List<String> deviceIds, String timeDimension,
|
|
|
+ List<String> timeSeries) {
|
|
|
+ if (deviceIds == null || deviceIds.isEmpty() || timeSeries == null || timeSeries.isEmpty()) {
|
|
|
+ return Collections.emptyList();
|
|
|
+ }
|
|
|
|
|
|
- try (PreparedStatement stmt = conn.prepareStatement(sql.toString())) {
|
|
|
- int paramIndex = 1;
|
|
|
- stmt.setString(paramIndex++, deviceId);
|
|
|
- stmt.setString(paramIndex++, startTime);
|
|
|
- stmt.setString(paramIndex++, endTime);
|
|
|
+ Map<String, DmpDevice> deviceMap = loadCompareDevices(deviceIds);
|
|
|
+ Set<String> deviceUuids = deviceMap.values().stream()
|
|
|
+ .map(DmpDevice::getDeviceUuid)
|
|
|
+ .filter(StringUtils::hasText)
|
|
|
+ .map(String::trim)
|
|
|
+ .collect(Collectors.toCollection(LinkedHashSet::new));
|
|
|
|
|
|
- if (energyTypeId != null) {
|
|
|
- stmt.setLong(paramIndex++, energyTypeId);
|
|
|
+ Map<String, Map<String, TreeMap<LocalDateTime, Double>>> deviceMetricData = Collections.emptyMap();
|
|
|
+ if (!deviceUuids.isEmpty()) {
|
|
|
+ String[] queryRange = getOverallCompareQueryRange(timeDimension, timeSeries);
|
|
|
+ deviceMetricData = queryDeviceMetricHistory(deviceUuids, queryRange[0], queryRange[1]);
|
|
|
+ }
|
|
|
+
|
|
|
+ List<AverageTimeSegment> segments = buildCompareTimeSegments(timeDimension, timeSeries);
|
|
|
+ List<EmsCompareSeriesItemVO> series = new ArrayList<>();
|
|
|
+ for (String deviceId : deviceIds) {
|
|
|
+ DmpDevice device = deviceMap.get(deviceId);
|
|
|
+ String deviceUuid = device != null && StringUtils.hasText(device.getDeviceUuid())
|
|
|
+ ? device.getDeviceUuid().trim() : null;
|
|
|
+ TreeMap<LocalDateTime, Double> timeValueMap = null;
|
|
|
+ if (deviceUuid != null) {
|
|
|
+ Map<String, TreeMap<LocalDateTime, Double>> metricMap = deviceMetricData.get(deviceUuid);
|
|
|
+ if (metricMap != null) {
|
|
|
+ timeValueMap = metricMap.get(ENERGY_METRIC);
|
|
|
}
|
|
|
+ }
|
|
|
|
|
|
- try (ResultSet rs = stmt.executeQuery()) {
|
|
|
- if (rs.next()) {
|
|
|
- double usage = rs.getDouble("total_usage");
|
|
|
- if (!rs.wasNull()) {
|
|
|
- return new BigDecimal(usage);
|
|
|
- }
|
|
|
+ List<EmsCompareValueVO> values = new ArrayList<>();
|
|
|
+ for (AverageTimeSegment segment : segments) {
|
|
|
+ BigDecimal usage = BigDecimal.ZERO.setScale(USAGE_SCALE, RoundingMode.HALF_UP);
|
|
|
+ if (timeValueMap != null && !timeValueMap.isEmpty()) {
|
|
|
+ SegmentUsageResult usageResult = calculateSegmentUsage(
|
|
|
+ timeValueMap, segment.getStartInclusive(), segment.getEndExclusive());
|
|
|
+ if (usageResult.isHasValidReading()) {
|
|
|
+ usage = BigDecimal.valueOf(usageResult.getUsage())
|
|
|
+ .setScale(USAGE_SCALE, RoundingMode.HALF_UP);
|
|
|
}
|
|
|
}
|
|
|
+ EmsCompareValueVO valueItem = new EmsCompareValueVO();
|
|
|
+ valueItem.setTimeValue(segment.getLabel());
|
|
|
+ valueItem.setUsage(usage);
|
|
|
+ values.add(valueItem);
|
|
|
}
|
|
|
- } catch (Exception e) {
|
|
|
- System.err.println("Error querying device energy for compare: " + e.getMessage());
|
|
|
+
|
|
|
+ EmsCompareSeriesItemVO seriesItem = new EmsCompareSeriesItemVO();
|
|
|
+ seriesItem.setDeviceId(deviceId);
|
|
|
+ seriesItem.setDeviceName(device != null && StringUtils.hasText(device.getDeviceName())
|
|
|
+ ? device.getDeviceName() : "未知设备");
|
|
|
+ seriesItem.setValues(values);
|
|
|
+ series.add(seriesItem);
|
|
|
}
|
|
|
+ return series;
|
|
|
+ }
|
|
|
|
|
|
- return BigDecimal.ZERO;
|
|
|
+ private Map<String, DmpDevice> loadCompareDevices(List<String> deviceIds) {
|
|
|
+ List<DmpDevice> devices = dmpDeviceMapper.selectList(
|
|
|
+ new LambdaQueryWrapper<DmpDevice>()
|
|
|
+ .in(DmpDevice::getDeviceId, deviceIds)
|
|
|
+ .eq(DmpDevice::getDeleteFlag, 0));
|
|
|
+ Map<String, DmpDevice> deviceMap = new LinkedHashMap<>();
|
|
|
+ for (DmpDevice device : devices) {
|
|
|
+ if (device != null && StringUtils.hasText(device.getDeviceId())) {
|
|
|
+ deviceMap.putIfAbsent(device.getDeviceId().trim(), device);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return deviceMap;
|
|
|
+ }
|
|
|
+
|
|
|
+ private String[] getOverallCompareQueryRange(String timeDimension, List<String> timeSeries) {
|
|
|
+ LocalDateTime[] firstSegment = resolveCompareSegmentRange(timeDimension, timeSeries.get(0));
|
|
|
+ LocalDateTime[] lastSegment = resolveCompareSegmentRange(timeDimension, timeSeries.get(timeSeries.size() - 1));
|
|
|
+ return new String[]{
|
|
|
+ firstSegment[0].format(DATE_TIME_FORMATTER),
|
|
|
+ lastSegment[1].minusSeconds(1).format(DATE_TIME_FORMATTER)
|
|
|
+ };
|
|
|
+ }
|
|
|
+
|
|
|
+ private List<AverageTimeSegment> buildCompareTimeSegments(String timeDimension, List<String> timeSeries) {
|
|
|
+ List<AverageTimeSegment> segments = new ArrayList<>();
|
|
|
+ for (String timeValue : timeSeries) {
|
|
|
+ LocalDateTime[] range = resolveCompareSegmentRange(timeDimension, timeValue);
|
|
|
+ segments.add(new AverageTimeSegment(timeValue, range[0], range[1]));
|
|
|
+ }
|
|
|
+ return segments;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 对比分析时间段:日=自然日,月=自然月,年=自然年
|
|
|
+ */
|
|
|
+ private LocalDateTime[] resolveCompareSegmentRange(String timeDimension, String timePoint) {
|
|
|
+ switch (timeDimension.toUpperCase()) {
|
|
|
+ case "D":
|
|
|
+ LocalDate day = LocalDate.parse(timePoint, DateTimeFormatter.ISO_LOCAL_DATE);
|
|
|
+ return new LocalDateTime[]{
|
|
|
+ day.atStartOfDay(),
|
|
|
+ day.plusDays(1).atStartOfDay()
|
|
|
+ };
|
|
|
+ case "M":
|
|
|
+ YearMonth month = YearMonth.parse(timePoint, DateTimeFormatter.ofPattern("yyyy-MM"));
|
|
|
+ return new LocalDateTime[]{
|
|
|
+ month.atDay(1).atStartOfDay(),
|
|
|
+ month.plusMonths(1).atDay(1).atStartOfDay()
|
|
|
+ };
|
|
|
+ case "Y":
|
|
|
+ int year = Integer.parseInt(timePoint);
|
|
|
+ return new LocalDateTime[]{
|
|
|
+ LocalDate.of(year, 1, 1).atStartOfDay(),
|
|
|
+ LocalDate.of(year + 1, 1, 1).atStartOfDay()
|
|
|
+ };
|
|
|
+ default:
|
|
|
+ String[] range = getTimeRange(timeDimension, timePoint);
|
|
|
+ return new LocalDateTime[]{
|
|
|
+ parseDateTime(range[0]),
|
|
|
+ parseDateTime(range[1]).plusSeconds(1)
|
|
|
+ };
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
@Override
|