|
|
@@ -1,16 +1,18 @@
|
|
|
package com.usky.ems.service.impl;
|
|
|
|
|
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
|
|
+import com.usky.common.core.bean.ApiResult;
|
|
|
import com.usky.common.core.exception.BusinessException;
|
|
|
import com.usky.common.security.utils.SecurityUtils;
|
|
|
-import com.usky.ems.domain.DmpDevice;
|
|
|
-import com.usky.ems.domain.EmsDevice;
|
|
|
-import com.usky.ems.domain.EmsDeviceFunction;
|
|
|
-import com.usky.ems.mapper.DmpDeviceMapper;
|
|
|
-import com.usky.ems.mapper.EmsDeviceFunctionMapper;
|
|
|
-import com.usky.ems.mapper.EmsDeviceMapper;
|
|
|
+import com.usky.demo.RemoteTsdbProxyService;
|
|
|
+import com.usky.demo.domain.HistorysInnerRequestVO;
|
|
|
+import com.usky.demo.domain.HistorysInnerResultVO;
|
|
|
+import com.usky.demo.domain.MetricVO;
|
|
|
+import com.usky.ems.domain.*;
|
|
|
+import com.usky.ems.mapper.*;
|
|
|
import com.usky.ems.service.EmsReportService;
|
|
|
import com.usky.ems.service.vo.*;
|
|
|
+import lombok.extern.slf4j.Slf4j;
|
|
|
import org.springframework.beans.factory.annotation.Autowired;
|
|
|
import org.springframework.stereotype.Service;
|
|
|
import org.springframework.util.StringUtils;
|
|
|
@@ -22,6 +24,7 @@ import java.math.BigDecimal;
|
|
|
import java.nio.charset.StandardCharsets;
|
|
|
import java.time.LocalDate;
|
|
|
import java.time.LocalDateTime;
|
|
|
+import java.time.LocalTime;
|
|
|
import java.time.format.DateTimeFormatter;
|
|
|
import java.util.*;
|
|
|
import java.util.stream.Collectors;
|
|
|
@@ -30,6 +33,7 @@ import java.util.stream.Collectors;
|
|
|
* 统计报表服务实现(设备列表基于 leo 设备与属性点位,统计与导出为占位/预留)
|
|
|
*/
|
|
|
@Service
|
|
|
+@Slf4j
|
|
|
public class EmsReportServiceImpl implements EmsReportService {
|
|
|
|
|
|
private static final String[] ENERGY_TYPE_NAMES = {"", "电", "水", "气"};
|
|
|
@@ -40,6 +44,16 @@ public class EmsReportServiceImpl implements EmsReportService {
|
|
|
private DmpDeviceMapper dmpDeviceMapper;
|
|
|
@Autowired
|
|
|
private EmsDeviceFunctionMapper emsDeviceFunctionMapper;
|
|
|
+ @Autowired
|
|
|
+ private RemoteTsdbProxyService remoteTsdbProxyService;
|
|
|
+ @Autowired
|
|
|
+ private BaseSpaceGatewayMapper baseSpaceGatewayMapper;
|
|
|
+ @Autowired
|
|
|
+ private EmsDeviceItemCodeMapper emsDeviceItemCodeMapper;
|
|
|
+ @Autowired
|
|
|
+ private EmsProductEnergyTypeMapper emsProductEnergyTypeMapper;
|
|
|
+ @Autowired
|
|
|
+ private BaseSpaceMapper baseSpaceMapper;
|
|
|
|
|
|
private String energyTypeName(Long energyTypeId) {
|
|
|
if (energyTypeId == null || energyTypeId < 1 || energyTypeId > 3) return "";
|
|
|
@@ -177,8 +191,8 @@ public class EmsReportServiceImpl implements EmsReportService {
|
|
|
// 生成列定义
|
|
|
List<EnergyReportResponse.ColumnItem> columnList = generateEnergyReportColumns(request.getTimeType(), startTime, endTime);
|
|
|
|
|
|
- // 生成数据列表
|
|
|
- List<EnergyReportResponse.ValueItem> valueList = generateEnergyReportData(request, startTime, endTime);
|
|
|
+ // 生成数据列表(按设备分组的二维数组)
|
|
|
+ List<List<EnergyReportResponse.ValueItem>> valueList = generateEnergyReportData(request, startTime, endTime);
|
|
|
|
|
|
EnergyReportResponse response = new EnergyReportResponse();
|
|
|
response.setColumnList(columnList);
|
|
|
@@ -201,10 +215,10 @@ public class EmsReportServiceImpl implements EmsReportService {
|
|
|
}
|
|
|
|
|
|
// 校验时间参数
|
|
|
- validateTimeParams(startTime, endTime, request.getDateType());
|
|
|
+ validateTimeParams(startTime, endTime, request.getTimeType());
|
|
|
|
|
|
// 生成标题列
|
|
|
- List<ItemReportResponse.TitleItem> titleList = generateItemReportTitles(request.getDateType(), startTime, endTime);
|
|
|
+ List<ItemReportResponse.TitleItem> titleList = generateItemReportTitles(request.getTimeType(), startTime, endTime);
|
|
|
|
|
|
// 生成数据列表
|
|
|
List<Map<String, Object>> dataList = generateItemReportData(request, startTime, endTime);
|
|
|
@@ -230,13 +244,13 @@ public class EmsReportServiceImpl implements EmsReportService {
|
|
|
}
|
|
|
|
|
|
// 校验时间参数
|
|
|
- validateTimeParams(startTime, endTime, request.getDateType());
|
|
|
+ validateTimeParams(startTime, endTime, request.getTimeType());
|
|
|
|
|
|
// 生成列定义
|
|
|
- List<EnergyReportResponse.ColumnItem> columnList = generateSpaceReportColumns(request.getDateType(), startTime, endTime);
|
|
|
+ List<EnergyReportResponse.ColumnItem> columnList = generateSpaceReportColumns(request.getTimeType(), startTime, endTime);
|
|
|
|
|
|
- // 生成数据列表
|
|
|
- List<Map<String, Object>> valueList = generateSpaceReportData(request, startTime, endTime);
|
|
|
+ // 生成数据列表(按区域/空间分组的二维数组)
|
|
|
+ List<List<Map<String, Object>>> valueList = generateSpaceReportData(request, startTime, endTime);
|
|
|
|
|
|
// 计算总计
|
|
|
Number total = calculateSpaceReportTotal(valueList);
|
|
|
@@ -273,35 +287,125 @@ public class EmsReportServiceImpl implements EmsReportService {
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * 生成能源报表数据
|
|
|
+ * 生成能源报表数据(按设备分组的二维数组)
|
|
|
+ * 外层 List 代表不同的设备
|
|
|
+ * 内层 List 包含该设备下所有请求的功能点(属性)数据
|
|
|
*/
|
|
|
- private List<EnergyReportResponse.ValueItem> generateEnergyReportData(EnergyReportRequest request, LocalDateTime startTime, LocalDateTime endTime) {
|
|
|
- List<EnergyReportResponse.ValueItem> valueList = new ArrayList<>();
|
|
|
+ private List<List<EnergyReportResponse.ValueItem>> generateEnergyReportData(EnergyReportRequest request, LocalDateTime startTime, LocalDateTime endTime) {
|
|
|
+ List<List<EnergyReportResponse.ValueItem>> groupedValueList = new ArrayList<>();
|
|
|
+
|
|
|
+ // 获取设备uuid列表
|
|
|
+ List<Integer> deviceIds = request.getDeviceList().stream()
|
|
|
+ .filter(device -> device != null && device.getId() != null)
|
|
|
+ .map(EnergyReportRequest.DeviceItem::getId)
|
|
|
+ .collect(Collectors.toList());
|
|
|
+ List<String> deviceUuids = getUuidList(deviceIds);
|
|
|
+
|
|
|
+ // 查询tsdb历史数据
|
|
|
+ HistorysInnerRequestVO historyRequestVO = new HistorysInnerRequestVO();
|
|
|
+ historyRequestVO.setDeviceuuid(deviceUuids);
|
|
|
+ historyRequestVO.setStartTime(request.getStartTime());
|
|
|
+ historyRequestVO.setEndTime(request.getEndTime());
|
|
|
+ historyRequestVO.setMetrics(request.getFuncList() != null && !request.getFuncList().isEmpty()
|
|
|
+ ? request.getFuncList().stream().map(EnergyReportRequest.FuncItem::getIdentifier).collect(Collectors.toList())
|
|
|
+ : new ArrayList<>());
|
|
|
+ log.info("历史数据接口请求:{}", historyRequestVO);
|
|
|
+ ApiResult<List<HistorysInnerResultVO>> listHistoryData = remoteTsdbProxyService.queryHistoryDeviceData(historyRequestVO);
|
|
|
+
|
|
|
+ // 判断历史数据是否为空
|
|
|
+ boolean hasData = !isHistoryDataEmpty(listHistoryData);
|
|
|
+ log.warn("历史数据为空,设备uuid:{}", deviceUuids);
|
|
|
+ log.info("历史数据接口返回:{}", listHistoryData);
|
|
|
+
|
|
|
+ // 如果有数据,解析TSDB返回的结果
|
|
|
+ Map<String, Map<String, List<Map<String, Object>>>> tsdbDataMap = new HashMap<>();
|
|
|
+ Map<String, String> deviceIdMap = new HashMap<>(); // deviceuuid -> device_id
|
|
|
+ if (hasData) {
|
|
|
+ tsdbDataMap = parseTsdbData(listHistoryData.getData());
|
|
|
+ deviceIdMap = extractDeviceIdMap(listHistoryData.getData());
|
|
|
+ }
|
|
|
+ log.info("设备id映射:{}", deviceIdMap);
|
|
|
+ log.info("tsdb数据:{}", tsdbDataMap);
|
|
|
|
|
|
for (EnergyReportRequest.DeviceItem device : request.getDeviceList()) {
|
|
|
if (device == null || StringUtils.isEmpty(device.getId())) {
|
|
|
continue;
|
|
|
}
|
|
|
|
|
|
- // 查询该设备的能耗数据
|
|
|
- Map<String, String> timeData = queryDeviceEnergyData(device.getId(), startTime, endTime, request.getTimeType());
|
|
|
+ // 为当前设备创建一个子列表
|
|
|
+ List<EnergyReportResponse.ValueItem> deviceValueList = new ArrayList<>();
|
|
|
+
|
|
|
+ // 获取设备UUID用于匹配TSDB数据
|
|
|
+ DmpDevice dmpDevice = dmpDeviceMapper.selectById(device.getId());
|
|
|
+ String deviceUuid = dmpDevice != null ? dmpDevice.getDeviceUuid() : null;
|
|
|
+
|
|
|
+ // 如果没有功能点列表,为设备创建一个默认记录
|
|
|
+ if (request.getFuncList() == null || request.getFuncList().isEmpty()) {
|
|
|
+ Map<String, String> timeData;
|
|
|
+ String total;
|
|
|
+
|
|
|
+ if (hasData && deviceUuid != null && tsdbDataMap.containsKey(deviceUuid)) {
|
|
|
+ // 从TSDB数据中获取
|
|
|
+ timeData = buildTimeDataFromTsdb(tsdbDataMap.get(deviceUuid), request.getTimeType(), startTime, endTime);
|
|
|
+ total = calculateTotal(timeData);
|
|
|
+ } else {
|
|
|
+ // 生成空数据结构
|
|
|
+ timeData = generateEmptyTimeData(request.getTimeType(), startTime, endTime);
|
|
|
+ total = "-";
|
|
|
+ }
|
|
|
|
|
|
- // 计算合计
|
|
|
- String total = calculateTotal(timeData);
|
|
|
+ EnergyReportResponse.ValueItem item = new EnergyReportResponse.ValueItem();
|
|
|
+ item.setDeviceId(deviceIdMap.getOrDefault(deviceUuid, null));
|
|
|
+ item.setDeviceName(device.getName());
|
|
|
+ item.setCommAddress(device.getCommAddress());
|
|
|
+ item.setIdentifier("-");
|
|
|
+ item.setTotal(total);
|
|
|
+ item.setTimeData(timeData);
|
|
|
|
|
|
- EnergyReportResponse.ValueItem item = new EnergyReportResponse.ValueItem();
|
|
|
- item.setDeviceId(device.getId());
|
|
|
- item.setDeviceName(device.getName());
|
|
|
- item.setCommAddress(device.getCommAddress());
|
|
|
- item.setIdentifier(request.getFuncList() != null && !request.getFuncList().isEmpty()
|
|
|
- ? request.getFuncList().get(0).getIdentifierName() : "-");
|
|
|
- item.setTotal(total);
|
|
|
- item.setTimeData(timeData);
|
|
|
+ deviceValueList.add(item);
|
|
|
+ } else {
|
|
|
+ // 为每个功能点创建一条记录,添加到当前设备的子列表中
|
|
|
+ for (EnergyReportRequest.FuncItem func : request.getFuncList()) {
|
|
|
+ if (func == null || StringUtils.isEmpty(func.getIdentifier())) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ Map<String, String> timeData;
|
|
|
+ String total;
|
|
|
+
|
|
|
+ // 如果有历史数据,从真实数据中查询;否则使用空值
|
|
|
+ if (hasData && deviceUuid != null && tsdbDataMap.containsKey(deviceUuid)) {
|
|
|
+ // 从TSDB数据中获取指定功能点的数据
|
|
|
+ timeData = buildTimeDataFromTsdbForMetric(
|
|
|
+ tsdbDataMap.get(deviceUuid),
|
|
|
+ func.getIdentifier(),
|
|
|
+ request.getTimeType(),
|
|
|
+ startTime,
|
|
|
+ endTime);
|
|
|
+ total = calculateTotal(timeData);
|
|
|
+ } else {
|
|
|
+ // 生成空数据结构
|
|
|
+ timeData = generateEmptyTimeData(request.getTimeType(), startTime, endTime);
|
|
|
+ total = "-";
|
|
|
+ }
|
|
|
+
|
|
|
+ EnergyReportResponse.ValueItem item = new EnergyReportResponse.ValueItem();
|
|
|
+ item.setDeviceId(deviceIdMap.getOrDefault(deviceUuid, null));
|
|
|
+ item.setDeviceName(device.getName());
|
|
|
+ item.setCommAddress(device.getCommAddress());
|
|
|
+ item.setIdentifier(func.getIdentifierName() != null ? func.getIdentifierName() : func.getIdentifier());
|
|
|
+ item.setTotal(total);
|
|
|
+ item.setTimeData(timeData);
|
|
|
+
|
|
|
+ deviceValueList.add(item);
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
- valueList.add(item);
|
|
|
+ // 将当前设备的所有功能点数据添加到外层列表
|
|
|
+ groupedValueList.add(deviceValueList);
|
|
|
}
|
|
|
|
|
|
- return valueList;
|
|
|
+ return groupedValueList;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
@@ -321,6 +425,39 @@ public class EmsReportServiceImpl implements EmsReportService {
|
|
|
return data;
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * 生成空的时间数据结构(用于无数据时填充)
|
|
|
+ */
|
|
|
+ private Map<String, String> generateEmptyTimeData(String timeType, LocalDateTime startTime, LocalDateTime endTime) {
|
|
|
+ Map<String, String> data = new LinkedHashMap<>();
|
|
|
+
|
|
|
+ List<String> timeLabels = generateTimeLabels(timeType, startTime, endTime);
|
|
|
+ for (String label : timeLabels) {
|
|
|
+ String key = "_" + label.replace("时", "").replace("日", "").replace("月", "");
|
|
|
+ data.put(key, "-"); // 空值填充
|
|
|
+ }
|
|
|
+
|
|
|
+ return data;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 查询设备指定功能点的能耗数据(模拟实现,实际应从TDengine或能耗表查询)
|
|
|
+ */
|
|
|
+ private Map<String, String> queryDeviceEnergyDataWithFunc(String deviceId, String identifier,
|
|
|
+ LocalDateTime startTime, LocalDateTime endTime, String timeType) {
|
|
|
+ Map<String, String> data = new LinkedHashMap<>();
|
|
|
+
|
|
|
+ // TODO: 实际应从数据库查询真实数据,并根据identifier区分不同功能点
|
|
|
+ // 这里使用模拟数据
|
|
|
+ List<String> timeLabels = generateTimeLabels(timeType, startTime, endTime);
|
|
|
+ for (String label : timeLabels) {
|
|
|
+ String key = "_" + label.replace("时", "").replace("日", "").replace("月", "");
|
|
|
+ data.put(key, "-"); // 模拟无数据
|
|
|
+ }
|
|
|
+
|
|
|
+ return data;
|
|
|
+ }
|
|
|
+
|
|
|
/**
|
|
|
* 计算合计值
|
|
|
*/
|
|
|
@@ -345,6 +482,187 @@ public class EmsReportServiceImpl implements EmsReportService {
|
|
|
return hasData ? sum.toString() : "-";
|
|
|
}
|
|
|
|
|
|
+ // ==================== TSDB数据解析方法 ====================
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 解析TSDB返回的数据
|
|
|
+ * 返回结构:Map<deviceuuid, Map<metric, List<metricItems>>>
|
|
|
+ */
|
|
|
+ private Map<String, Map<String, List<Map<String, Object>>>> parseTsdbData(List<HistorysInnerResultVO> tsdbResultList) {
|
|
|
+ Map<String, Map<String, List<Map<String, Object>>>> dataMap = new HashMap<>();
|
|
|
+
|
|
|
+ if (tsdbResultList == null || tsdbResultList.isEmpty()) {
|
|
|
+ return dataMap;
|
|
|
+ }
|
|
|
+
|
|
|
+ for (HistorysInnerResultVO result : tsdbResultList) {
|
|
|
+ if (result == null || !StringUtils.hasText(result.getDeviceuuid())) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ String deviceUuid = result.getDeviceuuid();
|
|
|
+ Map<String, List<Map<String, Object>>> metricMap = new HashMap<>();
|
|
|
+
|
|
|
+ if (result.getMetrics() != null && !result.getMetrics().isEmpty()) {
|
|
|
+ for (MetricVO metricVO : result.getMetrics()) {
|
|
|
+ if (metricVO != null && StringUtils.hasText(metricVO.getMetric())) {
|
|
|
+ metricMap.put(metricVO.getMetric(), metricVO.getMetricItems());
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ dataMap.put(deviceUuid, metricMap);
|
|
|
+ }
|
|
|
+
|
|
|
+ return dataMap;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 从TSDB返回结果中提取device_id映射
|
|
|
+ * 返回结构:Map<deviceuuid, device_id>
|
|
|
+ */
|
|
|
+ private Map<String, String> extractDeviceIdMap(List<HistorysInnerResultVO> tsdbResultList) {
|
|
|
+ Map<String, String> deviceIdMap = new HashMap<>();
|
|
|
+
|
|
|
+ if (tsdbResultList == null || tsdbResultList.isEmpty()) {
|
|
|
+ return deviceIdMap;
|
|
|
+ }
|
|
|
+
|
|
|
+ for (HistorysInnerResultVO result : tsdbResultList) {
|
|
|
+ if (result == null || !StringUtils.hasText(result.getDeviceuuid())) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ String deviceUuid = result.getDeviceuuid();
|
|
|
+ // 从tags中获取device_id
|
|
|
+ if (result.getTags() != null && result.getTags().containsKey("device_id")) {
|
|
|
+ String deviceId = (String) result.getTags().get("device_id");
|
|
|
+ deviceIdMap.put(deviceUuid, deviceId);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return deviceIdMap;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 从TSDB数据中构建时间数据(所有功能点)
|
|
|
+ */
|
|
|
+ private Map<String, String> buildTimeDataFromTsdb(Map<String, List<Map<String, Object>>> metricDataMap,
|
|
|
+ String timeType, LocalDateTime startTime, LocalDateTime endTime) {
|
|
|
+ Map<String, String> timeData = new LinkedHashMap<>();
|
|
|
+
|
|
|
+ List<String> timeLabels = generateTimeLabels(timeType, startTime, endTime);
|
|
|
+ for (String label : timeLabels) {
|
|
|
+ String key = "_" + label.replace("时", "").replace("日", "").replace("月", "");
|
|
|
+
|
|
|
+ // 如果有数据,取第一个功能点的数据作为示例
|
|
|
+ if (!metricDataMap.isEmpty()) {
|
|
|
+ // 获取第一个功能点的数据
|
|
|
+ List<Map<String, Object>> firstMetricItems = metricDataMap.values().iterator().next();
|
|
|
+ String value = findValueByTimestamp(firstMetricItems, label, timeType, startTime, endTime);
|
|
|
+ timeData.put(key, value);
|
|
|
+ } else {
|
|
|
+ timeData.put(key, "-");
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return timeData;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 从TSDB数据中构建指定功能点的时间数据
|
|
|
+ */
|
|
|
+ private Map<String, String> buildTimeDataFromTsdbForMetric(Map<String, List<Map<String, Object>>> metricDataMap,
|
|
|
+ String metric,
|
|
|
+ String timeType,
|
|
|
+ LocalDateTime startTime,
|
|
|
+ LocalDateTime endTime) {
|
|
|
+ Map<String, String> timeData = new LinkedHashMap<>();
|
|
|
+
|
|
|
+ List<String> timeLabels = generateTimeLabels(timeType, startTime, endTime);
|
|
|
+
|
|
|
+ // 获取指定功能点的数据
|
|
|
+ List<Map<String, Object>> metricItems = metricDataMap.get(metric);
|
|
|
+
|
|
|
+ for (String label : timeLabels) {
|
|
|
+ String key = "_" + label.replace("时", "").replace("日", "").replace("月", "");
|
|
|
+
|
|
|
+ if (metricItems != null && !metricItems.isEmpty()) {
|
|
|
+ String value = findValueByTimestamp(metricItems, label, timeType, startTime, endTime);
|
|
|
+ timeData.put(key, value);
|
|
|
+ } else {
|
|
|
+ timeData.put(key, "-");
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return timeData;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 根据时间标签查找对应的值
|
|
|
+ * 按小时查询时,将小时范围划分为时间段,匹配对应时间点的数据
|
|
|
+ */
|
|
|
+ private String findValueByTimestamp(List<Map<String, Object>> metricItems,
|
|
|
+ String timeLabel,
|
|
|
+ String timeType,
|
|
|
+ LocalDateTime startTime,
|
|
|
+ LocalDateTime endTime) {
|
|
|
+ if (metricItems == null || metricItems.isEmpty()) {
|
|
|
+ return "-";
|
|
|
+ }
|
|
|
+
|
|
|
+ // 解析时间标签,找到对应的时间点
|
|
|
+ // 例如:"1时" -> 需要从数据中找到该小时范围内的第一个数据点
|
|
|
+ try {
|
|
|
+ int hour = Integer.parseInt(timeLabel.replace("时", ""));
|
|
|
+ LocalDateTime targetTime = startTime.withHour(hour).withMinute(0).withSecond(0).withNano(0);
|
|
|
+ LocalDateTime nextTime = targetTime.plusHours(1);
|
|
|
+
|
|
|
+ // 查找该时间范围内的数据
|
|
|
+ for (Map<String, Object> item : metricItems) {
|
|
|
+ if (item.containsKey("timestamp") && item.containsKey("value")) {
|
|
|
+ String timestampStr = (String) item.get("timestamp");
|
|
|
+ if (StringUtils.hasText(timestampStr)) {
|
|
|
+ LocalDateTime itemTime = parseDateTime(timestampStr);
|
|
|
+ if (itemTime != null && !itemTime.isBefore(targetTime) && itemTime.isBefore(nextTime)) {
|
|
|
+ // 找到匹配的数据,进行单位转换和精度处理
|
|
|
+ String valueStr = (String) item.get("value");
|
|
|
+ if (StringUtils.hasText(valueStr)) {
|
|
|
+ try {
|
|
|
+ BigDecimal value = new BigDecimal(valueStr);
|
|
|
+ // 除以10000后保留两位小数
|
|
|
+ value = value.divide(new BigDecimal("10000")).setScale(2, java.math.RoundingMode.HALF_UP);
|
|
|
+ return value.toString();
|
|
|
+ } catch (NumberFormatException e) {
|
|
|
+ return "-";
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } catch (NumberFormatException e) {
|
|
|
+ // 如果解析失败,返回第一个数据点的值
|
|
|
+ if (!metricItems.isEmpty()) {
|
|
|
+ Map<String, Object> firstItem = metricItems.get(0);
|
|
|
+ if (firstItem.containsKey("value")) {
|
|
|
+ String valueStr = (String) firstItem.get("value");
|
|
|
+ if (StringUtils.hasText(valueStr)) {
|
|
|
+ try {
|
|
|
+ BigDecimal value = new BigDecimal(valueStr);
|
|
|
+ value = value.divide(new BigDecimal("10000")).setScale(2, java.math.RoundingMode.HALF_UP);
|
|
|
+ return value.toString();
|
|
|
+ } catch (NumberFormatException ex) {
|
|
|
+ return "-";
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return "-";
|
|
|
+ }
|
|
|
+
|
|
|
// ==================== 分项报表私有方法 ====================
|
|
|
|
|
|
/**
|
|
|
@@ -384,7 +702,7 @@ public class EmsReportServiceImpl implements EmsReportService {
|
|
|
|
|
|
// 查询分项能耗数据(模拟)
|
|
|
Map<String, Number> timeData = queryItemEnergyData(itemCode.getCode(), request.getSpaceId(),
|
|
|
- startTime, endTime, request.getDateType());
|
|
|
+ startTime, endTime, request.getTimeType());
|
|
|
|
|
|
// 计算合计
|
|
|
Number total = calculateItemTotal(timeData);
|
|
|
@@ -457,16 +775,24 @@ public class EmsReportServiceImpl implements EmsReportService {
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * 生成区域报表数据
|
|
|
+ * 生成区域报表数据(按区域/空间分组的二维数组)
|
|
|
+ * 外层 List 代表不同的区域
|
|
|
+ * 内层 List 包含该区域下所有设备的详细数据记录
|
|
|
*/
|
|
|
- private List<Map<String, Object>> generateSpaceReportData(SpaceReportRequest request, LocalDateTime startTime, LocalDateTime endTime) {
|
|
|
- List<Map<String, Object>> valueList = new ArrayList<>();
|
|
|
+ private List<List<Map<String, Object>>> generateSpaceReportData(SpaceReportRequest request, LocalDateTime startTime, LocalDateTime endTime) {
|
|
|
+ List<List<Map<String, Object>>> groupedValueList = new ArrayList<>();
|
|
|
|
|
|
for (SpaceReportRequest.SpaceItem space : request.getSpaces()) {
|
|
|
if (space == null || space.getId() == null) {
|
|
|
continue;
|
|
|
}
|
|
|
|
|
|
+ // 查询区域分项能耗数据
|
|
|
+ Map<String, Number> timeData = querySpaceItemEnergyData(request, startTime, endTime, request.getTimeType());
|
|
|
+
|
|
|
+ // 为当前区域创建一个子列表
|
|
|
+ List<Map<String, Object>> spaceValueList = new ArrayList<>();
|
|
|
+
|
|
|
for (SpaceReportRequest.ItemCodeItem itemCode : request.getItemCodes()) {
|
|
|
if (itemCode == null || StringUtils.isEmpty(itemCode.getCode())) {
|
|
|
continue;
|
|
|
@@ -476,9 +802,6 @@ public class EmsReportServiceImpl implements EmsReportService {
|
|
|
row.put("spaceName", space.getName());
|
|
|
row.put("itemName", itemCode.getName());
|
|
|
|
|
|
- // 查询区域分项能耗数据(模拟)
|
|
|
- Map<String, Number> timeData = querySpaceItemEnergyData(space.getId(), itemCode.getCode(),
|
|
|
- startTime, endTime, request.getDateType());
|
|
|
|
|
|
// 计算合计
|
|
|
Number total = calculateItemTotal(timeData);
|
|
|
@@ -491,42 +814,329 @@ public class EmsReportServiceImpl implements EmsReportService {
|
|
|
index++;
|
|
|
}
|
|
|
|
|
|
- valueList.add(row);
|
|
|
+ spaceValueList.add(row);
|
|
|
}
|
|
|
+
|
|
|
+ // 将当前区域的所有数据添加到外层列表
|
|
|
+ groupedValueList.add(spaceValueList);
|
|
|
}
|
|
|
|
|
|
- return valueList;
|
|
|
+ return groupedValueList;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * 查询区域分项能耗数据(模拟实现)
|
|
|
+ * 查询区域分项能耗数据
|
|
|
*/
|
|
|
- private Map<String, Number> querySpaceItemEnergyData(Long spaceId, String itemCode,
|
|
|
- LocalDateTime startTime, LocalDateTime endTime, String dateType) {
|
|
|
+ private Map<String, Number> querySpaceItemEnergyData(SpaceReportRequest request, LocalDateTime startTime, LocalDateTime endTime, String dateType) {
|
|
|
Map<String, Number> data = new LinkedHashMap<>();
|
|
|
+ List<Long> spaceIds = request.getSpaces().stream().map(SpaceReportRequest.SpaceItem::getId).collect(Collectors.toList());
|
|
|
+ spaceIds = spaceIds(spaceIds);
|
|
|
+ List<String> getGatewayUuids = getGatewayUuidsBySpaceIds(spaceIds).stream().map(BaseSpaceGateway::getGatewayUuid).collect(Collectors.toList());
|
|
|
+ List<String> getDeviceUuids = getDeviceUuidsByGatewayIds(getGatewayUuids).stream().map(DmpDevice::getDeviceUuid).collect(Collectors.toList());
|
|
|
+ Integer energyType = getEnergyTypeByDeviceUuid(getDeviceUuids.get(0));
|
|
|
+ Long productId = getProductIdByEnergyType(energyType);
|
|
|
+ if (productId != 360L) {
|
|
|
+ throw new BusinessException("暂不支持该设备类型");
|
|
|
+ }
|
|
|
+ // 按code将uuid分组
|
|
|
+ Map<String, List<String>> itemCodeByDeviceUuid = getItemCodeByDeviceUuid(getDeviceUuids);
|
|
|
+ // TODO:当前模拟数据都是电表设备,获取总有功功率
|
|
|
+ List<String> metrics = new ArrayList<>();
|
|
|
+ metrics.add("totalactiveenergyf");
|
|
|
+ HistorysInnerRequestVO historyRequestVO = new HistorysInnerRequestVO();
|
|
|
+ historyRequestVO.setDeviceuuid(getDeviceUuids);
|
|
|
+ historyRequestVO.setStartTime(request.getStartTime());
|
|
|
+ historyRequestVO.setEndTime(request.getEndTime());
|
|
|
+ historyRequestVO.setMetrics(metrics);
|
|
|
+ log.info("历史数据接口请求:{}", historyRequestVO);
|
|
|
+ ApiResult<List<HistorysInnerResultVO>> listHistoryData = remoteTsdbProxyService.queryHistoryDeviceData(historyRequestVO);
|
|
|
+ // 判断历史数据是否为空
|
|
|
+ boolean hasData = !isHistoryDataEmpty(listHistoryData);
|
|
|
+ log.info("历史数据接口返回:{}", listHistoryData);
|
|
|
+
|
|
|
+ // 如果有数据,解析TSDB返回的结果
|
|
|
+ if (hasData) {
|
|
|
+ // 1. 解析TSDB数据,构建设备UUID -> 指标 -> 时间点 -> 值的映射
|
|
|
+ Map<String, Map<String, TreeMap<LocalDateTime, Double>>> deviceMetricTimeValueMap = new HashMap<>();
|
|
|
+
|
|
|
+ for (HistorysInnerResultVO result : listHistoryData.getData()) {
|
|
|
+ String deviceUuid = result.getDeviceuuid();
|
|
|
+ Map<String, TreeMap<LocalDateTime, Double>> metricTimeValueMap = deviceMetricTimeValueMap.computeIfAbsent(deviceUuid, k -> new HashMap<>());
|
|
|
+
|
|
|
+ for (MetricVO metric : result.getMetrics()) {
|
|
|
+ String metricName = metric.getMetric();
|
|
|
+ TreeMap<LocalDateTime, Double> timeValueMap = metricTimeValueMap.computeIfAbsent(metricName, k -> new TreeMap<>());
|
|
|
+
|
|
|
+ for (Map<String, Object> point : metric.getMetricItems()) {
|
|
|
+ try {
|
|
|
+ // 解析时间戳字符串为LocalDateTime
|
|
|
+ String timestampStr = point.get("timestamp").toString();
|
|
|
+ // 处理可能的时间格式,移除末尾的.0
|
|
|
+ if (timestampStr.endsWith(".0")) {
|
|
|
+ timestampStr = timestampStr.substring(0, timestampStr.length() - 2);
|
|
|
+ }
|
|
|
+ LocalDateTime timestamp = LocalDateTime.parse(timestampStr, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
|
|
|
+ Double value = Double.parseDouble(point.get("value").toString());
|
|
|
+ timeValueMap.put(timestamp, value);
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("解析数据失败,设备:{},指标:{},值:{}", deviceUuid, metricName, point.get("value"), e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
- // TODO: 实际应从数据库查询真实数据
|
|
|
- List<String> timeLabels = generateTimeLabels(dateType, startTime, endTime);
|
|
|
- for (String label : timeLabels) {
|
|
|
- data.put(label, 0); // 模拟数据为0
|
|
|
+ // 2. 按itemCode分组计算用量
|
|
|
+ Map<String, Map<String, Double>> itemCodeTimeUsageMap = new HashMap<>(); // itemCode -> 时间段 -> 用量
|
|
|
+ Map<String, Double> itemCodeTotalUsageMap = new HashMap<>(); // itemCode -> 总用量
|
|
|
+
|
|
|
+ // 初始化所有itemCode的总用量
|
|
|
+ for (String itemCode : itemCodeByDeviceUuid.keySet()) {
|
|
|
+ itemCodeTotalUsageMap.put(itemCode, 0.0);
|
|
|
+ itemCodeTimeUsageMap.put(itemCode, new HashMap<>());
|
|
|
+ }
|
|
|
+
|
|
|
+ // 3. 生成时间标签分段
|
|
|
+ List<String> timeLabels = generateTimeLabels(dateType, startTime, endTime);
|
|
|
+
|
|
|
+ // 4. 按dateType生成时间分段
|
|
|
+ Map<String, List<LocalDateTime>> timeSegments = new HashMap<>();
|
|
|
+
|
|
|
+ if ("hour".equals(dateType)) {
|
|
|
+ // 按小时分段
|
|
|
+ LocalDateTime current = startTime.withMinute(0).withSecond(0).withNano(0);
|
|
|
+ while (!current.isAfter(endTime)) {
|
|
|
+ String hourLabel = current.getHour() + "时";
|
|
|
+ LocalDateTime segmentStart = current;
|
|
|
+ LocalDateTime segmentEnd = current.plusHours(1);
|
|
|
+ timeSegments.put(hourLabel, Arrays.asList(segmentStart, segmentEnd));
|
|
|
+ current = current.plusHours(1);
|
|
|
+ }
|
|
|
+ } else if ("day".equals(dateType)) {
|
|
|
+ // 按天分段
|
|
|
+ LocalDateTime current = startTime.with(LocalTime.MIN);
|
|
|
+ while (!current.isAfter(endTime)) {
|
|
|
+ String dateLabel = current.format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
|
|
|
+ LocalDateTime segmentStart = current.with(LocalTime.MIN);
|
|
|
+ LocalDateTime segmentEnd = current.with(LocalTime.MAX);
|
|
|
+ timeSegments.put(dateLabel, Arrays.asList(segmentStart, segmentEnd));
|
|
|
+ current = current.plusDays(1);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 5. 计算每个itemCode在每个时间段的用量
|
|
|
+ for (Map.Entry<String, List<String>> entry : itemCodeByDeviceUuid.entrySet()) {
|
|
|
+ String itemCode = entry.getKey();
|
|
|
+ List<String> deviceUuids = entry.getValue();
|
|
|
+ Map<String, Double> timeUsageMap = itemCodeTimeUsageMap.get(itemCode);
|
|
|
+
|
|
|
+ // 初始化时间段用量
|
|
|
+ for (String timeLabel : timeSegments.keySet()) {
|
|
|
+ timeUsageMap.putIfAbsent(timeLabel, 0.0);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 遍历该itemCode下的所有设备
|
|
|
+ for (String deviceUuid : deviceUuids) {
|
|
|
+ if (!deviceMetricTimeValueMap.containsKey(deviceUuid)) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ Map<String, TreeMap<LocalDateTime, Double>> metricTimeValueMap = deviceMetricTimeValueMap.get(deviceUuid);
|
|
|
+ if (!metricTimeValueMap.containsKey("totalactiveenergyf")) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ TreeMap<LocalDateTime, Double> timeValueMap = metricTimeValueMap.get("totalactiveenergyf");
|
|
|
+ if (timeValueMap.isEmpty()) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 按时间段计算用量差值
|
|
|
+ for (Map.Entry<String, List<LocalDateTime>> segmentEntry : timeSegments.entrySet()) {
|
|
|
+ String timeLabel = segmentEntry.getKey();
|
|
|
+ List<LocalDateTime> segment = segmentEntry.getValue();
|
|
|
+ LocalDateTime segmentStart = segment.get(0);
|
|
|
+ LocalDateTime segmentEnd = segment.get(1);
|
|
|
+
|
|
|
+ // 获取时间段内的数据点(包含前后缓冲时间,确保能取到首尾数据)
|
|
|
+ TreeMap<LocalDateTime, Double> segmentData = new TreeMap<>();
|
|
|
+ for (Map.Entry<LocalDateTime, Double> entryData : timeValueMap.entrySet()) {
|
|
|
+ LocalDateTime timestamp = entryData.getKey();
|
|
|
+ // 考虑时间段前1小时和后1小时的数据,确保能取到边界数据
|
|
|
+ if (!timestamp.isBefore(segmentStart.minusHours(1)) && !timestamp.isAfter(segmentEnd.plusHours(1))) {
|
|
|
+ segmentData.put(timestamp, entryData.getValue());
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (segmentData.size() < 2) {
|
|
|
+ continue; // 数据点不足,无法计算差值
|
|
|
+ }
|
|
|
+
|
|
|
+ // 找到时间段内的第一个和最后一个有效数据点
|
|
|
+ LocalDateTime firstTimestampInSegment = null;
|
|
|
+ LocalDateTime lastTimestampInSegment = null;
|
|
|
+
|
|
|
+ for (LocalDateTime timestamp : segmentData.keySet()) {
|
|
|
+ if (!timestamp.isBefore(segmentStart)) {
|
|
|
+ if (firstTimestampInSegment == null || timestamp.isBefore(firstTimestampInSegment)) {
|
|
|
+ firstTimestampInSegment = timestamp;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (!timestamp.isAfter(segmentEnd)) {
|
|
|
+ if (lastTimestampInSegment == null || timestamp.isAfter(lastTimestampInSegment)) {
|
|
|
+ lastTimestampInSegment = timestamp;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 如果找不到有效的时间点,使用整个时间段的首尾数据点
|
|
|
+ if (firstTimestampInSegment == null) {
|
|
|
+ firstTimestampInSegment = segmentData.ceilingKey(segmentStart.minusHours(1));
|
|
|
+ }
|
|
|
+ if (lastTimestampInSegment == null) {
|
|
|
+ lastTimestampInSegment = segmentData.floorKey(segmentEnd.plusHours(1));
|
|
|
+ }
|
|
|
+
|
|
|
+ if (firstTimestampInSegment == null || lastTimestampInSegment == null) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ Double firstValue = segmentData.get(firstTimestampInSegment);
|
|
|
+ Double lastValue = segmentData.get(lastTimestampInSegment);
|
|
|
+ Double usage = lastValue - firstValue;
|
|
|
+
|
|
|
+ // 处理计数器重置的情况(用量为负数)
|
|
|
+ if (usage < 0) {
|
|
|
+ // 假设设备重置,取最后的值作为用量
|
|
|
+ usage = lastValue;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 累加到当前itemCode的用量
|
|
|
+ timeUsageMap.put(timeLabel, timeUsageMap.get(timeLabel) + usage);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 6. 计算总量(所有时间段的用量之和)
|
|
|
+ for (String itemCode : itemCodeByDeviceUuid.keySet()) {
|
|
|
+ Map<String, Double> timeUsageMap = itemCodeTimeUsageMap.get(itemCode);
|
|
|
+ Double totalUsage = timeUsageMap.values().stream().mapToDouble(Double::doubleValue).sum();
|
|
|
+ itemCodeTotalUsageMap.put(itemCode, totalUsage);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 7. 将结果放入返回的data中
|
|
|
+ // 这里假设我们返回第一个itemCode的数据,或者根据业务需求调整
|
|
|
+ if (!itemCodeTimeUsageMap.isEmpty()) {
|
|
|
+ String firstItemCode = itemCodeTimeUsageMap.keySet().iterator().next();
|
|
|
+ Map<String, Double> timeUsageMap = itemCodeTimeUsageMap.get(firstItemCode);
|
|
|
+
|
|
|
+ // 按时间标签顺序放入结果
|
|
|
+ for (String timeLabel : timeLabels) {
|
|
|
+ if (timeUsageMap.containsKey(timeLabel)) {
|
|
|
+ data.put(timeLabel, timeUsageMap.get(timeLabel));
|
|
|
+ } else {
|
|
|
+ data.put(timeLabel, 0);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 添加总量
|
|
|
+ data.put("total", itemCodeTotalUsageMap.get(firstItemCode));
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ log.warn("历史数据为空,设备uuid:{}", getDeviceUuids);
|
|
|
+ // 无数据时初始化所有时间标签为0
|
|
|
+ List<String> timeLabels = generateTimeLabels(dateType, startTime, endTime);
|
|
|
+ for (String label : timeLabels) {
|
|
|
+ data.put(label, 0);
|
|
|
+ }
|
|
|
+ data.put("total", 0);
|
|
|
}
|
|
|
|
|
|
return data;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * 计算区域报表总计
|
|
|
+ * 查询所有区域id(包含子区域id)
|
|
|
+ */
|
|
|
+ private List<Long> spaceIds(List<Long> spaceId) {
|
|
|
+ return baseSpaceMapper.selectList(
|
|
|
+ new LambdaQueryWrapper<BaseSpace>()
|
|
|
+ .in(BaseSpace::getId, spaceId)
|
|
|
+ .or().in(BaseSpace::getParentId, spaceId)
|
|
|
+ ).stream().map(BaseSpace::getId).collect(Collectors.toList());
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 通过energy_type获取product_id
|
|
|
+ */
|
|
|
+ private Long getProductIdByEnergyType(Integer energyType) {
|
|
|
+ return emsProductEnergyTypeMapper.selectOne(
|
|
|
+ new LambdaQueryWrapper<EmsProductEnergyType>()
|
|
|
+ .eq(EmsProductEnergyType::getEnergyType, energyType)
|
|
|
+ ).getProductId();
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 通过device_uuid按item_code分组
|
|
|
+ */
|
|
|
+ private Map<String, List<String>> getItemCodeByDeviceUuid(List<String> deviceUuids) {
|
|
|
+ return emsDeviceItemCodeMapper.selectList(
|
|
|
+ new LambdaQueryWrapper<EmsDeviceItemCode>()
|
|
|
+ .in(EmsDeviceItemCode::getDeviceUuid, deviceUuids)
|
|
|
+ ).stream().collect(Collectors.groupingBy(
|
|
|
+ EmsDeviceItemCode::getItemCode,
|
|
|
+ Collectors.mapping(
|
|
|
+ EmsDeviceItemCode::getDeviceUuid,
|
|
|
+ Collectors.toList()
|
|
|
+ )
|
|
|
+ ));
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 通过device_uuid获取energy_type
|
|
|
*/
|
|
|
- private Number calculateSpaceReportTotal(List<Map<String, Object>> valueList) {
|
|
|
- if (valueList == null || valueList.isEmpty()) {
|
|
|
+ private Integer getEnergyTypeByDeviceUuid(String deviceUuid) {
|
|
|
+ return emsDeviceItemCodeMapper.selectList(
|
|
|
+ new LambdaQueryWrapper<EmsDeviceItemCode>()
|
|
|
+ .eq(EmsDeviceItemCode::getDeviceUuid, deviceUuid)
|
|
|
+ ).get(0).getEnergyType();
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 根据网关id获取设备UUID列表
|
|
|
+ */
|
|
|
+ private List<DmpDevice> getDeviceUuidsByGatewayIds(List<String> gatewayIds) {
|
|
|
+ return new ArrayList<>(dmpDeviceMapper.selectList(
|
|
|
+ new LambdaQueryWrapper<DmpDevice>()
|
|
|
+ .in(DmpDevice::getGatewayUuid, gatewayIds)));
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 根据区域id获取网关UUID列表
|
|
|
+ */
|
|
|
+ private List<BaseSpaceGateway> getGatewayUuidsBySpaceIds(List<Long> spaceIds) {
|
|
|
+ return new ArrayList<>(baseSpaceGatewayMapper.selectList(
|
|
|
+ new LambdaQueryWrapper<BaseSpaceGateway>()
|
|
|
+ .in(BaseSpaceGateway::getSpaceId, spaceIds)));
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 计算区域报表总计(适配二维数组结构)
|
|
|
+ */
|
|
|
+ private Number calculateSpaceReportTotal(List<List<Map<String, Object>>> groupedValueList) {
|
|
|
+ if (groupedValueList == null || groupedValueList.isEmpty()) {
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
double sum = 0;
|
|
|
- for (Map<String, Object> row : valueList) {
|
|
|
- Object total = row.get("total");
|
|
|
- if (total instanceof Number) {
|
|
|
- sum += ((Number) total).doubleValue();
|
|
|
+ for (List<Map<String, Object>> group : groupedValueList) {
|
|
|
+ if (group == null || group.isEmpty()) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ for (Map<String, Object> row : group) {
|
|
|
+ Object total = row.get("total");
|
|
|
+ if (total instanceof Number) {
|
|
|
+ sum += ((Number) total).doubleValue();
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
return sum;
|
|
|
@@ -569,9 +1179,11 @@ public class EmsReportServiceImpl implements EmsReportService {
|
|
|
throw new BusinessException("按年查询时,起止时间必须是同一年");
|
|
|
}
|
|
|
break;
|
|
|
- default:
|
|
|
- // 默认不校验
|
|
|
+ case "customize":
|
|
|
+ // 自定义查询:不进行时间校验
|
|
|
break;
|
|
|
+ default:
|
|
|
+ throw new BusinessException("查询时间类型错误!请重试");
|
|
|
}
|
|
|
}
|
|
|
|
|
|
@@ -651,6 +1263,8 @@ public class EmsReportServiceImpl implements EmsReportService {
|
|
|
}
|
|
|
}
|
|
|
break;
|
|
|
+ case "custom":
|
|
|
+ break;
|
|
|
default:
|
|
|
// 默认按日查询,返回小时数据
|
|
|
for (int i = 0; i < 24; i++) {
|
|
|
@@ -676,6 +1290,23 @@ public class EmsReportServiceImpl implements EmsReportService {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * 获取设备UUID列表
|
|
|
+ */
|
|
|
+ private List<String> getUuidList(List<Integer> deviceIds) {
|
|
|
+ return deviceIds.stream().map(deviceId -> {
|
|
|
+ DmpDevice device = dmpDeviceMapper.selectById(deviceId);
|
|
|
+ return device != null ? device.getDeviceUuid() : null;
|
|
|
+ }).collect(Collectors.toList());
|
|
|
+ }
|
|
|
+
|
|
|
+ private boolean isHistoryDataEmpty(ApiResult<?> result) {
|
|
|
+ return result == null
|
|
|
+ || !result.isSuccess()
|
|
|
+ || result.getStatus() != ApiResult.ResultStatus.SUCCESS
|
|
|
+ || result.getData() == null;
|
|
|
+ }
|
|
|
+
|
|
|
// ==================== 空响应创建方法 ====================
|
|
|
|
|
|
private EnergyReportResponse createEmptyEnergyReport() {
|