package com.ruoyi.http.service.impl;
|
|
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
import com.ruoyi.common.exception.ServiceException;
|
import com.ruoyi.common.utils.http.HttpUtils;
|
import com.ruoyi.common.utils.poi.ExcelUtil;
|
import com.ruoyi.http.config.TqdianbiaoConfig;
|
import com.ruoyi.http.mapper.TqdianbiaoCollectorMapper;
|
import com.ruoyi.http.mapper.TqdianbiaoEleRecordMapper;
|
import com.ruoyi.http.mapper.TqdianbiaoMeterMapper;
|
import com.ruoyi.http.mapper.TqdianbiaoSyncLogMapper;
|
import com.ruoyi.http.pojo.TqdianbiaoCollector;
|
import com.ruoyi.http.pojo.TqdianbiaoEleRecord;
|
import com.ruoyi.http.pojo.TqdianbiaoSyncLog;
|
import com.ruoyi.http.service.StatisticEleService;
|
import com.ruoyi.http.util.StatisticEleAggregateUtil;
|
import com.ruoyi.http.util.StatisticEleAggregateUtil.HourRange;
|
import com.ruoyi.http.vo.StatisticEleRecordVo;
|
import com.ruoyi.http.vo.StatisticEleSummaryVo;
|
import com.ruoyi.http.vo.StatisticEleSyncStatusVo;
|
import jakarta.servlet.http.HttpServletResponse;
|
import lombok.RequiredArgsConstructor;
|
import lombok.extern.slf4j.Slf4j;
|
import org.springframework.stereotype.Service;
|
import org.springframework.util.StringUtils;
|
|
import java.time.format.DateTimeFormatter;
|
import java.util.HashMap;
|
import java.util.List;
|
import java.util.Map;
|
import java.util.Set;
|
import java.util.function.Function;
|
import java.util.stream.Collectors;
|
|
@Service
|
@Slf4j
|
@RequiredArgsConstructor
|
public class StatisticEleServiceImpl implements StatisticEleService {
|
|
private static final Set<String> STAT_DIMENSIONS = Set.of("day", "month", "quarter", "year");
|
private static final List<String> DATA_DIMENSIONS = List.of("hour", "manual");
|
private static final DateTimeFormatter LOG_TIME_FMT = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
|
|
private final TqdianbiaoConfig config;
|
private final TqdianbiaoEleRecordMapper eleRecordMapper;
|
private final TqdianbiaoMeterMapper meterMapper;
|
private final TqdianbiaoCollectorMapper collectorMapper;
|
private final TqdianbiaoSyncLogMapper syncLogMapper;
|
|
@Override
|
public String fetchRawData(String dimension, String startTime, String endTime) {
|
if (!"hour".equals(dimension)) {
|
throw new ServiceException("仅支持拉取小时原始数据");
|
}
|
String url = config.getBaseUrl() + "/Api/StatisticEle/hour";
|
String param = String.format(
|
"auth=%s&start_time=%s&end_time=%s&ignore_radio=%d",
|
config.getAuth(), startTime, endTime, config.getIgnoreRadio()
|
);
|
log.warn("调用远程电表接口(调试): {}?{}", url, param);
|
return HttpUtils.sendGet(url, param);
|
}
|
|
@Override
|
public List<StatisticEleRecordVo> listRecords(String dimension, String startTime, String endTime, Integer ignoreRadio) {
|
if ("hour".equals(dimension) || "collection".equals(dimension)) {
|
return queryHourRecords(startTime, endTime);
|
}
|
return aggregateFromHour(dimension, startTime, endTime, true);
|
}
|
|
@Override
|
public StatisticEleSummaryVo getSummary(String dimension, String startTime, String endTime) {
|
if (!StringUtils.hasText(startTime) || !StringUtils.hasText(endTime)) {
|
throw new ServiceException("开始时间和结束时间不能为空");
|
}
|
if ("hour".equals(dimension)) {
|
List<StatisticEleRecordVo> detailRecords = queryHourRecords(startTime, endTime);
|
List<StatisticEleRecordVo> chartRecords = StatisticEleAggregateUtil.aggregateHourToBuckets(
|
detailRecords, StatisticEleAggregateUtil.HOUR_TO_HOUR);
|
return buildSummary(detailRecords, chartRecords);
|
}
|
if (!STAT_DIMENSIONS.contains(dimension)) {
|
throw new ServiceException("统计维度无效,支持 hour/day/month/quarter/year");
|
}
|
|
List<StatisticEleRecordVo> detailRecords = aggregateFromHour(dimension, startTime, endTime, true);
|
List<StatisticEleRecordVo> chartRecords = aggregateFromHour(dimension, startTime, endTime, false);
|
return buildSummary(detailRecords, chartRecords);
|
}
|
|
private StatisticEleSummaryVo buildSummary(
|
List<StatisticEleRecordVo> detailRecords, List<StatisticEleRecordVo> chartRecords) {
|
StatisticEleAggregateUtil.StatisticEleSummaryMetrics metrics =
|
StatisticEleAggregateUtil.calcMetrics(chartRecords);
|
StatisticEleSummaryVo summary = new StatisticEleSummaryVo();
|
summary.setRecords(detailRecords);
|
summary.setChartRecords(chartRecords);
|
summary.setRecordCount(metrics.getRecordCount());
|
summary.setTotalConsumption(metrics.getTotalConsumption());
|
summary.setAvgConsumption(metrics.getAvgConsumption());
|
summary.setMaxConsumption(metrics.getMaxConsumption());
|
summary.setMinConsumption(metrics.getMinConsumption());
|
return summary;
|
}
|
|
@Override
|
public StatisticEleSummaryVo getYesterdaySummary() {
|
HourRange range = StatisticEleAggregateUtil.yesterdayHourRange();
|
List<StatisticEleRecordVo> records = queryHourRecords(range.startTime(), range.endTime());
|
List<StatisticEleRecordVo> chartRecords = StatisticEleAggregateUtil.aggregateHourToBuckets(
|
records, StatisticEleAggregateUtil.HOUR_TO_HOUR);
|
StatisticEleSummaryVo summary = buildSummary(records, chartRecords);
|
summary.setTotalConsumption(round(StatisticEleAggregateUtil.sumRecordsTotal(records)));
|
return summary;
|
}
|
|
private static double round(double value) {
|
return Math.round(value * 100.0) / 100.0;
|
}
|
|
@Override
|
public void exportRecords(String dimension, String startTime, String endTime, HttpServletResponse response) {
|
List<StatisticEleRecordVo> records;
|
if ("hour".equals(dimension)) {
|
records = queryHourRecords(startTime, endTime);
|
} else {
|
records = aggregateFromHour(dimension, startTime, endTime, true);
|
}
|
ExcelUtil<StatisticEleRecordVo> util = new ExcelUtil<>(StatisticEleRecordVo.class);
|
util.exportExcel(response, records, "能耗统计数据");
|
}
|
|
@Override
|
public StatisticEleSyncStatusVo getSyncStatus() {
|
StatisticEleSyncStatusVo status = new StatisticEleSyncStatusVo();
|
status.setMeterCount(Math.toIntExact(meterMapper.selectCount(null)));
|
status.setCollectorCount(Math.toIntExact(collectorMapper.selectCount(null)));
|
status.setOnlineCollectorCount(Math.toIntExact(collectorMapper.selectCount(
|
Wrappers.<TqdianbiaoCollector>lambdaQuery().eq(TqdianbiaoCollector::getOnline, true))));
|
|
Map<String, Long> recordCountByDimension = new HashMap<>();
|
recordCountByDimension.put("hour", eleRecordMapper.selectCount(
|
Wrappers.<TqdianbiaoEleRecord>lambdaQuery().eq(TqdianbiaoEleRecord::getDimension, "hour")));
|
status.setRecordCountByDimension(recordCountByDimension);
|
|
Map<String, String> lastSyncTimeByType = new HashMap<>();
|
for (String syncType : List.of("collector", "meter", "hour")) {
|
TqdianbiaoSyncLog latest = syncLogMapper.selectOne(
|
Wrappers.<TqdianbiaoSyncLog>lambdaQuery()
|
.eq(TqdianbiaoSyncLog::getSyncType, syncType)
|
.eq(TqdianbiaoSyncLog::getStatus, "success")
|
.orderByDesc(TqdianbiaoSyncLog::getCreateTime)
|
.last("LIMIT 1"));
|
if (latest != null && latest.getCreateTime() != null) {
|
lastSyncTimeByType.put(syncType, latest.getCreateTime().format(LOG_TIME_FMT));
|
}
|
}
|
status.setLastSyncTimeByType(lastSyncTimeByType);
|
return status;
|
}
|
|
private List<StatisticEleRecordVo> queryHourRecords(String startTime, String endTime) {
|
String normalizedStart = StatisticEleAggregateUtil.normalizeQueryStartTimeKey(startTime);
|
String normalizedEnd = StatisticEleAggregateUtil.normalizeQueryEndTimeKey(endTime);
|
List<StatisticEleRecordVo> records = eleRecordMapper.selectRecordList(DATA_DIMENSIONS, normalizedStart, normalizedEnd);
|
StatisticEleAggregateUtil.normalizeConsumptions(records);
|
return records;
|
}
|
|
private List<StatisticEleRecordVo> aggregateFromHour(
|
String dimension, String startTime, String endTime, boolean perMeter) {
|
HourRange range = StatisticEleAggregateUtil.toHourQueryRange(dimension, startTime, endTime);
|
List<StatisticEleRecordVo> hourRecords = queryHourRecords(range.startTime(), range.endTime());
|
Function<String, String> bucketFn = StatisticEleAggregateUtil.bucketFn(dimension);
|
if (perMeter) {
|
return StatisticEleAggregateUtil.aggregateHourPerMeter(hourRecords, bucketFn);
|
}
|
return StatisticEleAggregateUtil.aggregateHourToBuckets(hourRecords, bucketFn);
|
}
|
}
|