package com.ruoyi.http.service.impl; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.ruoyi.common.utils.http.HttpUtils; import com.ruoyi.http.config.TqdianbiaoConfig; import com.ruoyi.http.mapper.TqdianbiaoEleRecordMapper; import com.ruoyi.http.mapper.TqdianbiaoMeterMapper; import com.ruoyi.http.pojo.TqdianbiaoEleRecord; import com.ruoyi.http.pojo.TqdianbiaoMeter; import com.ruoyi.http.service.TqdianbiaoEleSyncService; import com.ruoyi.http.service.TqdianbiaoSyncLogService; import com.ruoyi.http.util.StatisticEleParseUtil; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import org.springframework.util.StringUtils; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import java.util.List; @Service @Slf4j @RequiredArgsConstructor public class TqdianbiaoEleSyncServiceImpl implements TqdianbiaoEleSyncService { private static final DateTimeFormatter HOUR_FMT = DateTimeFormatter.ofPattern("yyyyMMddHH"); private static final DateTimeFormatter DAY_FMT = DateTimeFormatter.ofPattern("yyyyMMdd"); private static final DateTimeFormatter MONTH_FMT = DateTimeFormatter.ofPattern("yyyyMM"); private static final DateTimeFormatter YEAR_FMT = DateTimeFormatter.ofPattern("yyyy"); private final TqdianbiaoConfig config; private final TqdianbiaoEleRecordMapper eleRecordMapper; private final TqdianbiaoMeterMapper meterMapper; private final TqdianbiaoSyncLogService syncLogService; @Override public int syncHourData() { int window = config.getSync().getHourWindow() != null ? config.getSync().getHourWindow() : 2; LocalDateTime end = LocalDateTime.now().withMinute(0).withSecond(0).withNano(0); LocalDateTime start = end.minusHours(window); String startTime = start.format(HOUR_FMT); String endTime = end.format(HOUR_FMT); return syncDimension("hour", startTime, endTime); } @Override public int syncLast3DaysHourData() { LocalDateTime start = LocalDate.now().minusDays(3).atStartOfDay(); LocalDateTime end = LocalDateTime.now().withMinute(0).withSecond(0).withNano(0); return syncHourRangeInChunks(start, end); } /** 按最多 24 小时一段分批拉取(平台接口限制) */ private int syncHourRangeInChunks(LocalDateTime start, LocalDateTime end) { int total = 0; LocalDateTime windowStart = start; while (!windowStart.isAfter(end)) { LocalDateTime windowEnd = windowStart.plusHours(23); if (windowEnd.isAfter(end)) { windowEnd = end; } total += syncDimension("hour", windowStart.format(HOUR_FMT), windowEnd.format(HOUR_FMT)); windowStart = windowEnd.plusHours(1); } return total; } @Override public int syncDayData() { LocalDate yesterday = LocalDate.now().minusDays(1); String day = yesterday.format(DAY_FMT); return syncDimension("day", day, day); } @Override public int syncMonthData() { LocalDate now = LocalDate.now(); String month = now.format(MONTH_FMT); return syncDimension("month", month, month); } @Override public int syncYearData() { LocalDate now = LocalDate.now(); String year = now.format(YEAR_FMT); return syncDimension("year", year, year); } private int syncDimension(String dimension, String startTime, String endTime) { return syncDimension(dimension, startTime, endTime, config.getIgnoreRadio()); } private int syncDimension(String dimension, String startTime, String endTime, int ignoreRadio) { try { String url = config.getBaseUrl() + "/Api/StatisticEle/" + dimension; String param = String.format( "auth=%s&start_time=%s&end_time=%s&ignore_radio=%d", config.getAuth(), startTime, endTime, ignoreRadio ); log.info("同步电量数据: {} {}-{} ignore_radio={}", dimension, startTime, endTime, ignoreRadio); String raw = HttpUtils.sendGet(url, param); List records = StatisticEleParseUtil.parseToEntities(raw, dimension); enrichMeterInfo(records); int count = upsertRecords(records); syncLogService.logSuccess(dimension, startTime, endTime, count); return count; } catch (Exception e) { log.error("电量同步失败 dimension={} {}-{}", dimension, startTime, endTime, e); syncLogService.logFailure(dimension, startTime, endTime, e.getMessage()); throw e; } } private void enrichMeterInfo(List records) { for (TqdianbiaoEleRecord record : records) { if (record.getMeterId() == null) { continue; } TqdianbiaoMeter meter = meterMapper.selectOne( Wrappers.lambdaQuery() .eq(TqdianbiaoMeter::getMeterId, record.getMeterId()) .last("LIMIT 1")); if (meter != null && StringUtils.hasText(meter.getMeterName())) { record.setMeterName(meter.getMeterName()); } } } private int upsertRecords(List records) { int count = 0; for (TqdianbiaoEleRecord record : records) { TqdianbiaoEleRecord existing = eleRecordMapper.selectOne( Wrappers.lambdaQuery() .eq(TqdianbiaoEleRecord::getMeterId, record.getMeterId()) .eq(TqdianbiaoEleRecord::getDimension, record.getDimension()) .eq(TqdianbiaoEleRecord::getTimeKey, record.getTimeKey()) .last("LIMIT 1")); if (existing == null) { eleRecordMapper.insert(record); } else { record.setId(existing.getId()); eleRecordMapper.updateById(record); } count++; } return count; } }