From 600242462e1008c4bc29e4158b6d43d2896da417 Mon Sep 17 00:00:00 2001 From: yufengshuo Date: Tue, 17 Mar 2026 17:57:48 +0800 Subject: [PATCH] =?UTF-8?q?=E6=8F=90=E4=BA=A4RegionalWeatherData-=E5=AE=9A?= =?UTF-8?q?=E6=97=B6=E5=90=8C=E6=AD=A5=E4=BB=A3=E7=A0=81(=E8=BF=98?= =?UTF-8?q?=E6=B2=A1=E8=87=AA=E6=B5=8B)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../grid/SouthernPowerGridApplication.java | 2 + .../power/grid/entity/SyncWeatherDataVO.java | 16 ++ .../service/IRegionalWeatherDataService.java | 6 + .../impl/RegionalWeatherDataServiceImpl.java | 267 +++++++++++++++++- .../grid/task/WeatherDataScheduleTask.java | 46 +++ 5 files changed, 336 insertions(+), 1 deletion(-) create mode 100644 src/main/java/com/southern/power/grid/entity/SyncWeatherDataVO.java create mode 100644 src/main/java/com/southern/power/grid/task/WeatherDataScheduleTask.java diff --git a/src/main/java/com/southern/power/grid/SouthernPowerGridApplication.java b/src/main/java/com/southern/power/grid/SouthernPowerGridApplication.java index a672c7b..673f8e5 100644 --- a/src/main/java/com/southern/power/grid/SouthernPowerGridApplication.java +++ b/src/main/java/com/southern/power/grid/SouthernPowerGridApplication.java @@ -3,6 +3,7 @@ package com.southern.power.grid; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.scheduling.annotation.EnableAsync; +import org.springframework.scheduling.annotation.EnableScheduling; /** * MyBatis-Plus测试应用启动类 @@ -11,6 +12,7 @@ import org.springframework.scheduling.annotation.EnableAsync; */ @SpringBootApplication @EnableAsync +@EnableScheduling public class SouthernPowerGridApplication { public static void main(String[] args) { diff --git a/src/main/java/com/southern/power/grid/entity/SyncWeatherDataVO.java b/src/main/java/com/southern/power/grid/entity/SyncWeatherDataVO.java new file mode 100644 index 0000000..dfe901e --- /dev/null +++ b/src/main/java/com/southern/power/grid/entity/SyncWeatherDataVO.java @@ -0,0 +1,16 @@ +package com.southern.power.grid.entity; + +import lombok.Data; + +@Data +public class SyncWeatherDataVO { + private String orgCode; //区域编码 + private String dataTime; // 资料时次 + private String temperature; // 气温 + private String hourlyMaxTemperature; // 小时内最高气温 + private String hourlyMinTemperature; // 小时内最低气温 + private String hourlyPrecipitation; // 小时降水量 + private String dailyPrecipitation; // 日累计降水量 + private String extremeWindSpeedHourly; // 小时内极大风速 + +} diff --git a/src/main/java/com/southern/power/grid/service/IRegionalWeatherDataService.java b/src/main/java/com/southern/power/grid/service/IRegionalWeatherDataService.java index d4cb0cd..173f529 100644 --- a/src/main/java/com/southern/power/grid/service/IRegionalWeatherDataService.java +++ b/src/main/java/com/southern/power/grid/service/IRegionalWeatherDataService.java @@ -1,4 +1,10 @@ package com.southern.power.grid.service; public interface IRegionalWeatherDataService { + + /** + * 定时同步天气数据 + * + */ + void scheduleSyncWeatherData(); } diff --git a/src/main/java/com/southern/power/grid/service/impl/RegionalWeatherDataServiceImpl.java b/src/main/java/com/southern/power/grid/service/impl/RegionalWeatherDataServiceImpl.java index 17d0eba..1516c1a 100644 --- a/src/main/java/com/southern/power/grid/service/impl/RegionalWeatherDataServiceImpl.java +++ b/src/main/java/com/southern/power/grid/service/impl/RegionalWeatherDataServiceImpl.java @@ -1,16 +1,281 @@ package com.southern.power.grid.service.impl; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.southern.power.grid.dao.NationalWeatherStationMapper; import com.southern.power.grid.dao.RegionalWeatherDataMapper; -import com.southern.power.grid.entity.RegionalWeatherData; +import com.southern.power.grid.dao.RegionalWeatherStationMapper; +import com.southern.power.grid.dao.WeatherSiteAreaConfigurationMapper; +import com.southern.power.grid.entity.*; import com.southern.power.grid.service.IRegionalWeatherDataService; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.util.CollectionUtils; + +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.*; +import java.util.stream.Collectors; +import java.util.stream.Stream; @Service @Slf4j @RequiredArgsConstructor public class RegionalWeatherDataServiceImpl extends ServiceImpl implements IRegionalWeatherDataService { + + private final RegionalWeatherStationMapper regionalWeatherStationMapper; + private final NationalWeatherStationMapper nationalWeatherStationMapper; + private final WeatherSiteAreaConfigurationMapper weatherSiteAreaConfigurationMapper; + private final Map weatherAreaMap = new HashMap<>(); + + + @Override + @Transactional(rollbackFor = Exception.class) + public void scheduleSyncWeatherData() { + try { + //获取当前时间前一个整点时间 + String prevHourDate = LocalDateTime.now().minusHours(1).format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:00:00")); + List regionalWeatherStations = regionalWeatherStationMapper.selectList(new LambdaQueryWrapper() + .eq(RegionalWeatherStation::getDataTime, prevHourDate)); + List nationalWeatherStations = nationalWeatherStationMapper.selectList(new LambdaQueryWrapper() + .eq(NationalWeatherStation::getDataTime, prevHourDate)); + if (CollectionUtils.isEmpty(regionalWeatherStations) && CollectionUtils.isEmpty(nationalWeatherStations)) { + return; + } + loadWeatherAreaConfig(); + List list = convertToSyncList(regionalWeatherStations, nationalWeatherStations); + if (CollectionUtils.isEmpty(list)) { + return; + } + List dataList = convert(list, prevHourDate); + this.saveBatch(dataList, 700); + } catch (Exception e) { + log.error("定时同步区域气象数据失败:{}", e.getMessage(), e); + throw new RuntimeException(e); + } + } + + public List convert(List list, String prevHourDate) { + if (list == null || list.isEmpty()) { + return Collections.emptyList(); + } + + // 1. 按 orgCode 分组 + Map> groupMap = + list.stream().collect(Collectors.groupingBy(SyncWeatherDataVO::getOrgCode)); + + // 2. 分组处理 + List result = new ArrayList<>(); + + for (Map.Entry> entry : groupMap.entrySet()) { + String orgCode = entry.getKey(); + List groupList = entry.getValue(); + + int size = groupList.size(); + + BigDecimal sumHourlyPrecipitation = BigDecimal.ZERO; + BigDecimal sumTemperature = BigDecimal.ZERO; + + BigDecimal maxTemp = null; + BigDecimal minTemp = null; + BigDecimal maxWind = null; + + for (SyncWeatherDataVO vo : groupList) { + + BigDecimal hourlyPre = parse(vo.getHourlyPrecipitation()); + BigDecimal temp = parse(vo.getTemperature()); + BigDecimal maxT = parse(vo.getHourlyMaxTemperature()); + BigDecimal minT = parse(vo.getHourlyMinTemperature()); + BigDecimal wind = parse(vo.getExtremeWindSpeedHourly()); + + // 累加 + sumHourlyPrecipitation = sumHourlyPrecipitation.add(hourlyPre); + sumTemperature = sumTemperature.add(temp); + + // 最大值 + if (maxTemp == null || maxT.compareTo(maxTemp) > 0) { + maxTemp = maxT; + } + + // 最小值 + if (minTemp == null || minT.compareTo(minTemp) < 0) { + minTemp = minT; + } + + // 最大风速 + if (maxWind == null || wind.compareTo(maxWind) > 0) { + maxWind = wind; + } + } + + // 平均值(保留2位小数) + String avgHourlyPrecipitation = divide(sumHourlyPrecipitation, size); + String avgTemperature = divide(sumTemperature, size); + + // 3. 构建实体 + RegionalWeatherData data = new RegionalWeatherData(); + data.setOrgCode(orgCode); + data.setDataTime(prevHourDate); + data.setHourlyPrecipitation(avgHourlyPrecipitation); + data.setDailyPrecipitation("0"); + data.setTemperature(avgTemperature); + data.setHourlyMaxTemperature(toStr(maxTemp)); + data.setHourlyMinTemperature(toStr(minTemp)); + data.setExtremeWindSpeedHourly(toStr(maxWind)); + + result.add(data); + } + + return result; + } + + /** + * String → BigDecimal(空值安全) + */ + private static BigDecimal parse(String val) { + if (val == null || val.trim().isEmpty()) { + return BigDecimal.ZERO; + } + try { + return new BigDecimal(val.trim()); + } catch (Exception e) { + return BigDecimal.ZERO; + } + } + + /** + * 平均值计算(保留2位小数) + */ + private static String divide(BigDecimal sum, int size) { + if (size == 0) { + return "0"; + } + return sum.divide(BigDecimal.valueOf(size), 2, RoundingMode.HALF_UP).toString(); + } + + /** + * BigDecimal → String + */ + private static String toStr(BigDecimal val) { + return val == null ? "0" : val.setScale(2, RoundingMode.HALF_UP).toString(); + } + + + /** + * 加载气象区划配置表 + * + **/ + private void loadWeatherAreaConfig() { + weatherAreaMap.clear(); + List list = weatherSiteAreaConfigurationMapper.selectAll(); + if (CollectionUtils.isEmpty(list)) return; + Map tempMap = list.stream() + .filter(Objects::nonNull) + .filter(config -> config.getWeatherProvince() != null + && config.getWeatherCity() != null + && config.getWeatherDistrict() != null + && config.getDistrictCode() != null) + .collect(Collectors.toMap( + config -> String.join(":", + config.getWeatherProvince(), + config.getWeatherCity(), + config.getWeatherDistrict()), + WeatherSiteAreaConfiguration::getDistrictCode, + (oldValue, newValue) -> oldValue + )); + + // 全部放入你的成员变量 Map + weatherAreaMap.putAll(tempMap); + } + + public List convertToSyncList( + List regionalList, + List nationalList) { + + Stream regionalStream = regionalList.stream() + .map(item -> buildVO( + item.getProvince(), + item.getCity(), + item.getDistrict(), + item.getDataTime(), + item.getTemperature(), + item.getHourlyMaxTemperature(), + item.getHourlyMinTemperature(), + item.getHourlyPrecipitation(), + item.getDailyPrecipitation(), + item.getExtremeWindSpeedHourly(), + weatherAreaMap + )) + .filter(Objects::nonNull); + + Stream nationalStream = nationalList.stream() + .map(item -> buildVO( + item.getProvince(), + item.getCity(), + item.getDistrict(), + item.getDataTime(), + item.getTemperature(), + item.getHourlyMaxTemperature(), + item.getHourlyMinTemperature(), + item.getHourlyPrecipitation(), + item.getDailyPrecipitation(), + item.getExtremeWindSpeedHourly(), + weatherAreaMap + )) + .filter(Objects::nonNull); + + return Stream.concat(regionalStream, nationalStream) + .collect(Collectors.toList()); + } + + private static SyncWeatherDataVO buildVO( + String province, + String city, + String district, + String dataTime, + String temperature, + String hourlyMaxTemperature, + String hourlyMinTemperature, + String hourlyPrecipitation, + String dailyPrecipitation, + String extremeWindSpeedHourly, + Map weatherAreaMap) { + + String key = buildKey(province, city, district); + String orgCode = weatherAreaMap.get(key); + + // 没匹配上区域编码,直接丢弃 + if (orgCode == null) { + return null; + } + + SyncWeatherDataVO vo = new SyncWeatherDataVO(); + vo.setOrgCode(orgCode); + vo.setDataTime(dataTime); + vo.setTemperature(temperature); + vo.setHourlyMaxTemperature(hourlyMaxTemperature); + vo.setHourlyMinTemperature(hourlyMinTemperature); + vo.setHourlyPrecipitation(hourlyPrecipitation); + vo.setDailyPrecipitation(dailyPrecipitation); + vo.setExtremeWindSpeedHourly(extremeWindSpeedHourly); + + return vo; + } + + private static String buildKey(String province, String city, String district) { + return String.join(":", + safe(province), + safe(city), + safe(district) + ); + } + + private static String safe(String str) { + return str == null ? "" : str.trim(); + } } diff --git a/src/main/java/com/southern/power/grid/task/WeatherDataScheduleTask.java b/src/main/java/com/southern/power/grid/task/WeatherDataScheduleTask.java new file mode 100644 index 0000000..c4dfe0e --- /dev/null +++ b/src/main/java/com/southern/power/grid/task/WeatherDataScheduleTask.java @@ -0,0 +1,46 @@ +package com.southern.power.grid.task; + +import com.southern.power.grid.service.IRegionalWeatherDataService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +import javax.annotation.Resource; +import java.util.concurrent.atomic.AtomicBoolean; + +@Slf4j +@Component +public class WeatherDataScheduleTask { + + @Resource + private IRegionalWeatherDataService regionalWeatherDataService; + + // 防止并发执行 + private final AtomicBoolean running = new AtomicBoolean(false); + + /** + * 每小时第20分钟执行 + */ + //@Scheduled(cron = "0 20 * * * ?") + public void schedule() { + log.info("【区域天气数据同步任务】定时触发"); + execute(); + } + + /** + * 实际执行逻辑 + */ + private void execute() { + if (!running.compareAndSet(false, true)) { + log.warn("任务正在执行,跳过本次触发"); + return; + } + + try { + regionalWeatherDataService.scheduleSyncWeatherData(); + } catch (Exception e) { + log.error("【区域天气数据同步任务】执行异常", e); + } finally { + running.set(false); + } + } +}