能源概览接口

main
liutao 5 days ago
parent 8fcf74187b
commit 8546d1e67e

@ -104,6 +104,13 @@ public class EnergyDeviceController {
return success(energyDeviceService.queryDataRecords(deviceConsumptionReqVO));
}
@GetMapping("/queryOverviewData")
@Operation(summary = "查询能源总览数据")
@PreAuthorize("@ss.hasPermission('mes:energy-device:queryRecords')")
public CommonResult<EnergyOverviewRespVO> queryOverviewData(@Valid EnergyOverviewReqVO reqVO) {
return success(energyDeviceService.queryOverviewData(reqVO));
}
@GetMapping("/record-export-excel")
@Operation(summary = "导出数据记录 Excel")
@PreAuthorize("@ss.hasPermission('mes:energy-device:exportRecords')")

@ -108,6 +108,7 @@ public interface EnergyDeviceService {
List<EnergyDeviceRespVO> queryDataRecords(EnergyDeviceConsumptionReqVO deviceConsumptionReqVO);
EnergyOverviewRespVO queryOverviewData(EnergyOverviewReqVO reqVO);
List<HourEnergyValueVO> lastEnergyStatistics(Long deviceTypeId,Long orgId);

@ -630,6 +630,39 @@ public class EnergyDeviceServiceImpl implements EnergyDeviceService {
return result;
}
@Override
public EnergyOverviewRespVO queryOverviewData(EnergyOverviewReqVO reqVO) {
EnergyOverviewRespVO respVO = new EnergyOverviewRespVO();
List<EnergyDeviceDO> devices = queryDevices(null, reqVO.getOrgId(), reqVO.getEnergyTypeId(), null);
if (devices.isEmpty()) {
respVO.setMetrics(buildOverviewMetrics(Collections.emptyList(), Collections.emptyList(), reqVO));
respVO.setTrendChart(buildEmptyTrendChart(reqVO));
respVO.setRegionChart(buildRegionChart(Collections.emptyList()));
respVO.setRankList(Collections.emptyList());
respVO.setTotal(0L);
respVO.setDetailList(Collections.emptyList());
return respVO;
}
EnergyDeviceConsumptionReqVO consumptionReqVO = new EnergyDeviceConsumptionReqVO();
consumptionReqVO.setOrgId(reqVO.getOrgId());
consumptionReqVO.setStartTime(reqVO.getStartTime());
consumptionReqVO.setEndTime(reqVO.getEndTime());
consumptionReqVO.setIds(devices.stream().map(item -> String.valueOf(item.getId())).collect(Collectors.joining(",")));
List<EnergyDeviceRespVO> records = queryDataRecords(consumptionReqVO);
records.sort(Comparator.comparing((EnergyDeviceRespVO item) -> parseEnergyValue(item.getEnergyConsumption())).reversed());
respVO.setMetrics(buildOverviewMetrics(records, devices, reqVO));
respVO.setTrendChart(buildTrendChart(devices, reqVO));
respVO.setRegionChart(buildRegionChart(records));
respVO.setRankList(buildRankList(records));
respVO.setTotal((long) records.size());
respVO.setDetailList(paginateDetails(records, devices, reqVO));
return respVO;
}
private Map<Long, TimePointCache> buildCacheFromRow(
List<EnergyDeviceDO> devices,
Map<Long, Map<String, Object>> rowMap,
@ -719,19 +752,353 @@ public class EnergyDeviceServiceImpl implements EnergyDeviceService {
/** 查询设备列表 */
private List<EnergyDeviceDO> queryDevices(EnergyDeviceConsumptionReqVO req) {
return queryDevices(req.getName(), req.getOrgId(), null, req.getIds());
}
private List<EnergyDeviceDO> queryDevices(String name, Long orgId, Long deviceTypeId, String ids) {
return energyDeviceMapper.selectList(
Wrappers.<EnergyDeviceDO>lambdaQuery()
.in(StringUtils.isNotBlank(req.getIds()),
.in(StringUtils.isNotBlank(ids),
EnergyDeviceDO::getId,
StringUtils.isNotBlank(req.getIds())
? Arrays.asList(req.getIds().split(","))
StringUtils.isNotBlank(ids)
? Arrays.asList(ids.split(","))
: null)
.like(StringUtils.isNotBlank(req.getName()), EnergyDeviceDO::getName, req.getName())
.eq(req.getOrgId() != null, EnergyDeviceDO::getOrgId, req.getOrgId())
.like(StringUtils.isNotBlank(name), EnergyDeviceDO::getName, name)
.eq(orgId != null, EnergyDeviceDO::getOrgId, orgId)
.eq(deviceTypeId != null, EnergyDeviceDO::getDeviceTypeId, deviceTypeId)
.orderByDesc(EnergyDeviceDO::getCreateTime)
);
}
private List<EnergyOverviewRespVO.MetricItem> buildOverviewMetrics(List<EnergyDeviceRespVO> records,
List<EnergyDeviceDO> devices,
EnergyOverviewReqVO reqVO) {
double totalValue = records.stream().mapToDouble(item -> parseEnergyValue(item.getEnergyConsumption())).sum();
EnergyDeviceRespVO topRecord = records.stream().findFirst().orElse(null);
Map<String, Double> regionTotals = new LinkedHashMap<>();
for (EnergyDeviceRespVO record : records) {
regionTotals.merge(StringUtils.defaultIfBlank(record.getOrgName(), "-"),
parseEnergyValue(record.getEnergyConsumption()), Double::sum);
}
Map.Entry<String, Double> maxRegion = regionTotals.entrySet().stream()
.max(Map.Entry.comparingByValue())
.orElse(null);
List<EnergyOverviewRespVO.MetricItem> metrics = new ArrayList<>();
metrics.add(buildMetric("total", "总用量", formatDouble(totalValue), "", "设备数", String.valueOf(devices.size()), null, null));
metrics.add(buildMetric("deviceCount", "统计设备", String.valueOf(records.size()), "台", "区域数", String.valueOf(regionTotals.size()), null, null));
metrics.add(buildMetric("topDevice", "最高能耗设备",
topRecord != null ? topRecord.getName() : "-",
"",
"用量",
topRecord != null ? formatDouble(parseEnergyValue(topRecord.getEnergyConsumption())) : "0",
null,
null));
metrics.add(buildMetric("topRegion", "最高能耗区域",
maxRegion != null ? maxRegion.getKey() : "-",
"",
"用量",
maxRegion != null ? formatDouble(maxRegion.getValue()) : "0",
null,
null));
metrics.add(buildMetric("range", "统计时段",
buildRangeLabel(reqVO),
"",
"开始",
StringUtils.defaultIfBlank(reqVO.getStartTime(), "-"),
null,
null));
return metrics;
}
private EnergyOverviewRespVO.MetricItem buildMetric(String key, String label, String value, String unit,
String subLabel, String subValue, String change, Boolean down) {
EnergyOverviewRespVO.MetricItem item = new EnergyOverviewRespVO.MetricItem();
item.setKey(key);
item.setLabel(label);
item.setValue(value);
item.setUnit(unit);
item.setSubLabel(subLabel);
item.setSubValue(subValue);
item.setChange(change);
item.setDown(down);
return item;
}
private EnergyOverviewRespVO.TrendChart buildTrendChart(List<EnergyDeviceDO> devices, EnergyOverviewReqVO reqVO) {
EnergyOverviewRespVO.TrendChart chart = new EnergyOverviewRespVO.TrendChart();
chart.setUnit("");
if (StringUtils.isBlank(reqVO.getStartTime()) || StringUtils.isBlank(reqVO.getEndTime())) {
chart.setXAxis(Collections.emptyList());
chart.setData(Collections.emptyList());
return chart;
}
try {
LocalDateTime start = LocalDateTime.parse(reqVO.getStartTime(), DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
LocalDateTime end = LocalDateTime.parse(reqVO.getEndTime(), DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
Map<Long, List<OperationRulesVO>> deviceRulesMap = buildDeviceRulesMap(devices);
Set<Long> sourceDeviceIds = collectRuleDeviceIds(deviceRulesMap);
Map<Long, DeviceContactModelDO> pointInfoMap = batchGetPointInfoFromLocalDB(new ArrayList<>(collectPointIds(deviceRulesMap)));
if (sourceDeviceIds.isEmpty()) {
chart.setXAxis(Collections.emptyList());
chart.setData(Collections.emptyList());
return chart;
}
boolean sameDay = start.toLocalDate().equals(end.toLocalDate()) && !start.equals(end);
if (sameDay) {
return buildHourTrendChart(chart, deviceRulesMap, sourceDeviceIds, pointInfoMap, start, end);
}
return buildDayTrendChart(chart, deviceRulesMap, sourceDeviceIds, pointInfoMap, start.toLocalDate(), end.toLocalDate());
} catch (Exception e) {
log.warn("构建能源总览趋势数据失败, req={}", reqVO, e);
chart.setXAxis(Collections.emptyList());
chart.setData(Collections.emptyList());
return chart;
}
}
private EnergyOverviewRespVO.TrendChart buildHourTrendChart(EnergyOverviewRespVO.TrendChart chart,
Map<Long, List<OperationRulesVO>> deviceRulesMap,
Set<Long> sourceDeviceIds,
Map<Long, DeviceContactModelDO> pointInfoMap,
LocalDateTime start,
LocalDateTime end) {
DateTimeFormatter hourKeyFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH");
DateTimeFormatter displayFormatter = DateTimeFormatter.ofPattern("HH:00");
List<Map<String, Object>> allRows = tDengineService.newQueryLastDataByHourBatch(sourceDeviceIds, start, end);
Map<String, Map<Long, TimePointCache>> hourCacheMap = buildTimeCacheByHour(allRows, pointInfoMap, deviceRulesMap);
List<String> xAxis = new ArrayList<>();
List<String> data = new ArrayList<>();
LocalDateTime cursor = start.withMinute(0).withSecond(0);
LocalDateTime limit = end.withMinute(0).withSecond(0);
while (!cursor.isAfter(limit)) {
String hourKey = cursor.format(hourKeyFormatter);
double total = sumGroupedEnergy(deviceRulesMap, hourCacheMap.getOrDefault(hourKey, Collections.emptyMap()));
xAxis.add(cursor.format(displayFormatter));
data.add(formatDouble(total));
cursor = cursor.plusHours(1);
}
chart.setXAxis(xAxis);
chart.setData(data);
return chart;
}
private EnergyOverviewRespVO.TrendChart buildDayTrendChart(EnergyOverviewRespVO.TrendChart chart,
Map<Long, List<OperationRulesVO>> deviceRulesMap,
Set<Long> sourceDeviceIds,
Map<Long, DeviceContactModelDO> pointInfoMap,
LocalDate startDate,
LocalDate endDate) {
int days = (int) (endDate.toEpochDay() - startDate.toEpochDay()) + 1;
List<Map<String, Object>> allRows = tDengineService.queryLastDataByDayBatch(sourceDeviceIds, startDate, days);
Map<String, Map<Long, TimePointCache>> dayCacheMap = buildTimeCacheByDay(allRows, pointInfoMap, deviceRulesMap);
List<String> xAxis = new ArrayList<>();
List<String> data = new ArrayList<>();
LocalDate cursor = startDate;
while (!cursor.isAfter(endDate)) {
String dayKey = cursor.toString();
double total = sumGroupedEnergy(deviceRulesMap, dayCacheMap.getOrDefault(dayKey, Collections.emptyMap()));
xAxis.add(dayKey);
data.add(formatDouble(total));
cursor = cursor.plusDays(1);
}
chart.setXAxis(xAxis);
chart.setData(data);
return chart;
}
private Map<String, Map<Long, TimePointCache>> buildTimeCacheByHour(List<Map<String, Object>> allRows,
Map<Long, DeviceContactModelDO> pointInfoMap,
Map<Long, List<OperationRulesVO>> deviceRulesMap) {
Map<String, Map<Long, TimePointCache>> cacheMap = new LinkedHashMap<>();
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH");
for (Map<String, Object> row : allRows) {
LocalDateTime ts = (LocalDateTime) row.get("timestamp");
Long deviceId = (Long) row.get("deviceId");
if (ts == null || deviceId == null) {
continue;
}
String key = ts.format(formatter);
cacheMap.computeIfAbsent(key, item -> new HashMap<>()).put(deviceId,
buildTimePointCache(deviceId, ts, row, pointInfoMap, deviceRulesMap));
}
return cacheMap;
}
private Map<String, Map<Long, TimePointCache>> buildTimeCacheByDay(List<Map<String, Object>> allRows,
Map<Long, DeviceContactModelDO> pointInfoMap,
Map<Long, List<OperationRulesVO>> deviceRulesMap) {
Map<String, Map<Long, TimePointCache>> cacheMap = new LinkedHashMap<>();
for (Map<String, Object> row : allRows) {
String dayKey = (String) row.get("day");
LocalDateTime ts = (LocalDateTime) row.get("timestamp");
Long deviceId = (Long) row.get("deviceId");
if (StringUtils.isBlank(dayKey) || ts == null || deviceId == null) {
continue;
}
cacheMap.computeIfAbsent(dayKey, item -> new HashMap<>()).put(deviceId,
buildTimePointCache(deviceId, ts, row, pointInfoMap, deviceRulesMap));
}
return cacheMap;
}
private TimePointCache buildTimePointCache(Long deviceId,
LocalDateTime ts,
Map<String, Object> row,
Map<Long, DeviceContactModelDO> pointInfoMap,
Map<Long, List<OperationRulesVO>> deviceRulesMap) {
TimePointCache cache = new TimePointCache();
cache.setTimestamp(ts);
Map<Long, Double> pointValueMap = new HashMap<>();
List<OperationRulesVO> rules = deviceRulesMap.values().stream()
.flatMap(List::stream)
.filter(item -> deviceId.equals(item.getDeviceId()))
.collect(Collectors.toList());
for (OperationRulesVO rule : rules) {
DeviceContactModelDO pointInfo = pointInfoMap.get(rule.getPointId());
if (pointInfo == null || StringUtils.isBlank(pointInfo.getAttributeCode())) {
continue;
}
Object value = row.get(pointInfo.getAttributeCode().toLowerCase());
if (value == null) {
continue;
}
try {
pointValueMap.put(rule.getPointId(), new BigDecimal(value.toString()).setScale(2, RoundingMode.HALF_UP).doubleValue());
} catch (Exception e) {
log.warn("解析趋势点位值失败, deviceId={}, pointId={}, value={}", deviceId, rule.getPointId(), value, e);
}
}
cache.setPointValueMap(pointValueMap);
return cache;
}
private double sumGroupedEnergy(Map<Long, List<OperationRulesVO>> deviceRulesMap, Map<Long, TimePointCache> cacheMap) {
double total = 0D;
for (Map.Entry<Long, List<OperationRulesVO>> entry : deviceRulesMap.entrySet()) {
total += calculateByRules(entry.getKey(), entry.getValue(), cacheMap);
}
return total;
}
private EnergyOverviewRespVO.TrendChart buildEmptyTrendChart(EnergyOverviewReqVO reqVO) {
EnergyOverviewRespVO.TrendChart chart = new EnergyOverviewRespVO.TrendChart();
chart.setUnit("");
chart.setXAxis(Collections.emptyList());
chart.setData(Collections.emptyList());
return chart;
}
private EnergyOverviewRespVO.RegionChart buildRegionChart(List<EnergyDeviceRespVO> records) {
EnergyOverviewRespVO.RegionChart chart = new EnergyOverviewRespVO.RegionChart();
chart.setUnit("");
Map<String, Double> regionMap = new LinkedHashMap<>();
double total = 0D;
for (EnergyDeviceRespVO record : records) {
double value = parseEnergyValue(record.getEnergyConsumption());
total += value;
regionMap.merge(StringUtils.defaultIfBlank(record.getOrgName(), "-"), value, Double::sum);
}
List<EnergyOverviewRespVO.RegionItem> items = new ArrayList<>();
for (Map.Entry<String, Double> entry : regionMap.entrySet()) {
EnergyOverviewRespVO.RegionItem item = new EnergyOverviewRespVO.RegionItem();
item.setName(entry.getKey());
item.setValue(formatDouble(entry.getValue()));
item.setPercent(total <= 0 ? "0" : formatDouble(entry.getValue() * 100 / total));
items.add(item);
}
items.sort(Comparator.comparing((EnergyOverviewRespVO.RegionItem item) -> parseEnergyValue(item.getValue())).reversed());
chart.setTotalValue(formatDouble(total));
chart.setItems(items);
return chart;
}
private List<EnergyOverviewRespVO.RankItem> buildRankList(List<EnergyDeviceRespVO> records) {
return records.stream().limit(5).map(item -> {
EnergyOverviewRespVO.RankItem rankItem = new EnergyOverviewRespVO.RankItem();
rankItem.setId(item.getId());
rankItem.setName(item.getName());
rankItem.setRegion(item.getOrgName());
rankItem.setValue(item.getEnergyConsumption());
return rankItem;
}).collect(Collectors.toList());
}
private List<EnergyOverviewRespVO.DetailItem> paginateDetails(List<EnergyDeviceRespVO> records,
List<EnergyDeviceDO> devices,
EnergyOverviewReqVO reqVO) {
Map<Long, EnergyDeviceDO> deviceMap = devices.stream().collect(Collectors.toMap(EnergyDeviceDO::getId, Function.identity(), (a, b) -> a));
int pageNo = reqVO.getPageNo() == null || reqVO.getPageNo() < 1 ? 1 : reqVO.getPageNo();
int pageSize = reqVO.getPageSize() == null || reqVO.getPageSize() < 1 ? 10 : reqVO.getPageSize();
int fromIndex = Math.min((pageNo - 1) * pageSize, records.size());
int toIndex = Math.min(fromIndex + pageSize, records.size());
List<EnergyOverviewRespVO.DetailItem> details = new ArrayList<>();
for (EnergyDeviceRespVO item : records.subList(fromIndex, toIndex)) {
EnergyOverviewRespVO.DetailItem detailItem = new EnergyOverviewRespVO.DetailItem();
detailItem.setId(item.getId());
detailItem.setName(item.getName());
detailItem.setEnergyType(Optional.ofNullable(deviceMap.get(item.getId())).map(EnergyDeviceDO::getDeviceTypeName).orElse(item.getDeviceTypeName()));
detailItem.setRegion(item.getOrgName());
detailItem.setValue(item.getEnergyConsumption());
detailItem.setStartTime(reqVO.getStartTime());
detailItem.setEndTime(reqVO.getEndTime());
details.add(detailItem);
}
return details;
}
private Map<Long, List<OperationRulesVO>> buildDeviceRulesMap(List<EnergyDeviceDO> devices) {
Map<Long, List<OperationRulesVO>> result = new LinkedHashMap<>();
for (EnergyDeviceDO device : devices) {
List<OperationRulesVO> rules = parseRules(device);
if (!rules.isEmpty()) {
result.put(device.getId(), rules);
}
}
return result;
}
private Set<Long> collectRuleDeviceIds(Map<Long, List<OperationRulesVO>> deviceRulesMap) {
return deviceRulesMap.values().stream()
.flatMap(List::stream)
.map(OperationRulesVO::getDeviceId)
.filter(Objects::nonNull)
.collect(Collectors.toSet());
}
private Set<Long> collectPointIds(Map<Long, List<OperationRulesVO>> deviceRulesMap) {
return deviceRulesMap.values().stream()
.flatMap(List::stream)
.map(OperationRulesVO::getPointId)
.filter(Objects::nonNull)
.collect(Collectors.toSet());
}
private String buildRangeLabel(EnergyOverviewReqVO reqVO) {
if (StringUtils.isBlank(reqVO.getStartTime()) || StringUtils.isBlank(reqVO.getEndTime())) {
return "-";
}
return reqVO.getStartTime() + " ~ " + reqVO.getEndTime();
}
private double parseEnergyValue(String value) {
if (StringUtils.isBlank(value)) {
return 0D;
}
try {
return Double.parseDouble(value.replace(",", ""));
} catch (Exception e) {
return 0D;
}
}
/** 解析设备规则 */
private List<OperationRulesVO> parseRules(EnergyDeviceDO device) {
if (device == null || StringUtils.isBlank(device.getRules())) return Collections.emptyList();

Loading…
Cancel
Save