初始提交:添加分时处理

This commit is contained in:
liuzhiming 2026-03-17 09:34:16 +08:00
parent c6da443080
commit 0ff1fd3cef
8 changed files with 467 additions and 6 deletions

View File

@ -5,6 +5,7 @@ import com.southern.power.grid.entity.DnerHourlyPowerOutageEvent;
import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param; import org.apache.ibatis.annotations.Param;
import java.time.LocalDateTime;
import java.util.List; import java.util.List;
/** /**
@ -25,4 +26,8 @@ public interface DnerHourlyPowerOutageEventMapper extends BaseMapper<DnerHourlyP
* @return 插入成功的条数 * @return 插入成功的条数
*/ */
int batchInsert(List<DnerHourlyPowerOutageEvent> list); int batchInsert(List<DnerHourlyPowerOutageEvent> list);
List<DnerHourlyPowerOutageEvent> selectByDistrictsAndTime(@Param("districtCodes") List<String> districtCodes,
@Param("startTime") LocalDateTime startTime,
@Param("endTime") LocalDateTime endTime);
} }

View File

@ -0,0 +1,18 @@
package com.southern.power.grid.entity;
import lombok.Data;
import java.util.List;
/**
* 分时统计结果DTO
*/
@Data
public class HourlyStatisticDTO {
private List<String> timePoints; // 时间点列表格式yyyy-MM-dd HH:00:00
private List<Long> userCounts; // 停电影响用户数
private List<Double> precipitations; // 小时降水量
private List<Double> avgTemperatures; // 平均气温
private List<Double> maxTemperatures; // 最高气温
private List<Double> minTemperatures; // 最低气温
private List<Double> maxWindSpeeds; // 极大风速
}

View File

@ -13,7 +13,7 @@ public class WeatherDataDTO {
private String temperature; private String temperature;
private String hourlyMaxTemperature; private String hourlyMaxTemperature;
private String hourlyMinTemperature; private String hourlyMinTemperature;
private String hourlyPrecipitation; private Double hourlyPrecipitation;
private String dailyPrecipitation; private String dailyPrecipitation;
private String extremeWindSpeedHourly; private String extremeWindSpeedHourly;
} }

View File

@ -10,7 +10,6 @@ import com.southern.power.grid.entity.WeatherDataDTO;
import com.southern.power.grid.entity.WeatherSiteAreaConfiguration; import com.southern.power.grid.entity.WeatherSiteAreaConfiguration;
import com.southern.power.grid.service.WeatherDataService; import com.southern.power.grid.service.WeatherDataService;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource; import javax.annotation.Resource;
import java.time.LocalDateTime; import java.time.LocalDateTime;
@ -51,7 +50,7 @@ public class HourlyOutageExcelProcessService {
* 对外主入口处理一批 Excel 解析后的数据按逻辑补齐气象数据并批量入库 * 对外主入口处理一批 Excel 解析后的数据按逻辑补齐气象数据并批量入库
*/ */
public void process(List<DataExcelEntity> excelRows) { public void process(List<DataExcelEntity> excelRows) {
// 1. 加载南网配置表 // 1. 加载南网区划配置表
loadNwAreaConfig(); loadNwAreaConfig();
// 2. 加载气象区划配置表 // 2. 加载气象区划配置表
loadWeatherAreaConfig(); loadWeatherAreaConfig();
@ -90,6 +89,9 @@ public class HourlyOutageExcelProcessService {
} }
} }
/**
* 加载南网区划配置
* **/
private void loadNwAreaConfig() { private void loadNwAreaConfig() {
nwAreaMap.clear(); nwAreaMap.clear();
List<NwSiteAreaConfiguration> list = nwSiteAreaConfigurationMapper.selectAll(); List<NwSiteAreaConfiguration> list = nwSiteAreaConfigurationMapper.selectAll();
@ -103,6 +105,9 @@ public class HourlyOutageExcelProcessService {
} }
} }
/**
* 加载气象区划配置表
* **/
private void loadWeatherAreaConfig() { private void loadWeatherAreaConfig() {
weatherAreaMap.clear(); weatherAreaMap.clear();
List<WeatherSiteAreaConfiguration> list = weatherSiteAreaConfigurationMapper.selectAll(); List<WeatherSiteAreaConfiguration> list = weatherSiteAreaConfigurationMapper.selectAll();
@ -146,7 +151,7 @@ public class HourlyOutageExcelProcessService {
e.setTemperature(weatherData.getTemperature()); e.setTemperature(weatherData.getTemperature());
e.setHourlyMaxTemperature(weatherData.getHourlyMaxTemperature()); e.setHourlyMaxTemperature(weatherData.getHourlyMaxTemperature());
e.setHourlyMinTemperature(weatherData.getHourlyMinTemperature()); e.setHourlyMinTemperature(weatherData.getHourlyMinTemperature());
e.setHourlyPrecipitation(weatherData.getHourlyPrecipitation()); // e.setHourlyPrecipitation(weatherData.getHourlyPrecipitation());
e.setDailyPrecipitation(weatherData.getDailyPrecipitation()); e.setDailyPrecipitation(weatherData.getDailyPrecipitation());
e.setExtremeWindSpeedHourly(weatherData.getExtremeWindSpeedHourly()); e.setExtremeWindSpeedHourly(weatherData.getExtremeWindSpeedHourly());
} }

View File

@ -0,0 +1,411 @@
package com.southern.power.grid.service.impl;
import com.southern.power.grid.dao.DnerHourlyPowerOutageEventMapper;
import com.southern.power.grid.dao.NwSiteAreaConfigurationMapper;
import com.southern.power.grid.dao.WeatherSiteAreaConfigurationMapper;
import com.southern.power.grid.entity.DnerHourlyPowerOutageEvent;
import com.southern.power.grid.entity.HourlyStatisticDTO;
import com.southern.power.grid.entity.NwSiteAreaConfiguration;
import com.southern.power.grid.entity.WeatherDataDTO;
import com.southern.power.grid.entity.WeatherSiteAreaConfiguration;
import com.southern.power.grid.service.WeatherDataService;
import com.southern.power.grid.utils.TimeUtil;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
@Service
public class HourlyStatisticService {
@Resource
private NwSiteAreaConfigurationMapper nwSiteAreaConfigurationMapper;
@Resource
private WeatherSiteAreaConfigurationMapper weatherSiteAreaConfigurationMapper;
@Resource
private WeatherDataService weatherDataService;
@Resource
private DnerHourlyPowerOutageEventMapper dnerHourlyPowerOutageEventMapper;
private static final DateTimeFormatter TIME_POINT_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:00:00");
/**
* 获取分时统计全量数据包含所有指标
* @param areaType 区域类型province-city-county-
* @param areaCode 区域编码//县编码县为org_code
* @param startTime 开始时间包含
* @param endTime 结束时间包含
* @return 分时统计结果
*/
public HourlyStatisticDTO getHourlyStatistics(String areaType, String areaCode,
LocalDateTime startTime, LocalDateTime endTime) {
// 1. 生成时间点列表整点
List<LocalDateTime> timePoints = generateTimePoints(startTime, endTime);
if (timePoints.isEmpty()) {
return new HourlyStatisticDTO();
}
// 2. 根据区域类型获取需要统计的区县编码列表
List<String> districtCodes = getDistrictCodesByArea(areaType, areaCode);
if (districtCodes.isEmpty()) {
return new HourlyStatisticDTO();
}
// 3. 分别计算各指标
HourlyStatisticDTO result = new HourlyStatisticDTO();
result.setTimePoints(timePoints.stream().map(t -> t.format(TIME_POINT_FORMATTER)).collect(Collectors.toList()));
// 3.1 停电用户数直接查分时停电事件表
Map<LocalDateTime, Long> userCountMap = calculateUserCount(districtCodes, timePoints);
result.setUserCounts(fillList(timePoints, userCountMap, 0L));
// 3.2 降水量需要多站点平均
Map<LocalDateTime, Double> precipMap = calculatePrecipitation(districtCodes, timePoints);
result.setPrecipitations(fillList(timePoints, precipMap, 0.0));
// 3.3 气温平均/最高/最低
TemperatureResult tempResult = calculateTemperature(districtCodes, timePoints);
result.setAvgTemperatures(fillList(timePoints, tempResult.getAvgMap(), 0.0));
result.setMaxTemperatures(fillList(timePoints, tempResult.getMaxMap(), 0.0));
result.setMinTemperatures(fillList(timePoints, tempResult.getMinMap(), 0.0));
// 3.4 风速极大值
Map<LocalDateTime, Double> windMap = calculateWindSpeed(districtCodes, timePoints);
result.setMaxWindSpeeds(fillList(timePoints, windMap, 0.0));
return result;
}
/**
* 生成整点时间序列从startTime的整点开始到endTime的整点结束包含首尾
*/
private List<LocalDateTime> generateTimePoints(LocalDateTime start, LocalDateTime end) {
List<LocalDateTime> points = new ArrayList<>();
LocalDateTime current = start.withMinute(0).withSecond(0).withNano(0);
LocalDateTime endPoint = end.withMinute(0).withSecond(0).withNano(0);
while (!current.isAfter(endPoint)) {
points.add(current);
current = current.plusHours(1);
}
return points;
}
/**
* 根据区域类型获取所有需要统计的区县编码列表
*/
private List<String> getDistrictCodesByArea(String areaType, String areaCode) {
if ("county".equalsIgnoreCase(areaType)) {
return Collections.singletonList(areaCode);
}
// 查询南网配置表根据省/市编码获取所有区县编码
List<NwSiteAreaConfiguration> list;
if ("province".equalsIgnoreCase(areaType)) {
list = new ArrayList<>();// nwSiteAreaConfigurationMapper.selectByProvince(areaCode);
} else if ("city".equalsIgnoreCase(areaType)) {
list = new ArrayList<>(); // nwSiteAreaConfigurationMapper.selectByCity(areaCode);
} else {
return Collections.emptyList();
}
if (CollectionUtils.isEmpty(list)) {
return Collections.emptyList();
}
return list.stream()
.map(NwSiteAreaConfiguration::getDistrictCode)
.filter(Objects::nonNull)
.distinct()
.collect(Collectors.toList());
}
// ===================== 停电用户数统计 =====================
private Map<LocalDateTime, Long> calculateUserCount(List<String> districtCodes, List<LocalDateTime> timePoints) {
// 查询所有区县在时间范围内的停电事件记录
List<DnerHourlyPowerOutageEvent> events = dnerHourlyPowerOutageEventMapper.selectByDistrictsAndTime(
districtCodes, timePoints.get(0), timePoints.get(timePoints.size() - 1).plusHours(1));
// 按区县和整点时刻聚合user_count
Map<LocalDateTime, Long> result = new HashMap<>();
for (LocalDateTime tp : timePoints) {
result.put(tp, 0L);
}
if (CollectionUtils.isEmpty(events)) {
return result;
}
// 注意data_time是停电开始时间需按照停电开始时间 > 整点时刻的规则计入该整点
// 但我们表中没有结束时间无法精确统计正在停电中此处采用简化规则按开始时间所在小时计入
// 若未来表添加end_time字段可修改为更精确的逻辑
for (DnerHourlyPowerOutageEvent event : events) {
if (event.getDataTime() == null) continue;
LocalDateTime start = TimeUtil.parseToLocalDateTime(event.getDataTime()); // 需要工具类解析字符串
if (start == null) continue;
// 找到大于start的最小整点即start所在的下一个整点
LocalDateTime targetHour = start.withMinute(0).withSecond(0).withNano(0).plusHours(1);
if (result.containsKey(targetHour)) {
result.merge(targetHour, event.getUserCount() == null ? 0L : event.getUserCount().longValue(), Long::sum);
}
}
return result;
}
// ===================== 降水量统计多站平均 =====================
private Map<LocalDateTime, Double> calculatePrecipitation(List<String> districtCodes, List<LocalDateTime> timePoints) {
// 1. 获取所有区县关联的气象站ID
List<String> stationIds = getStationIdsByDistricts(districtCodes);
if (stationIds.isEmpty()) {
return emptyMap(timePoints);
}
// 2. 批量获取所有站点的气象数据按小时
Map<String, Map<LocalDateTime, WeatherDataDTO>> stationDataMap = batchFetchWeatherData(stationIds, timePoints);
// 3. 按小时计算各县平均降水量
// 由于降水量县级规则是国家站+区域站平均但我们有所有站的数据需要先计算各县的平均值再汇总
// 简化直接对所有站的数据按小时取平均因为最终市级还需要再平均最终结果等价于所有站的平均
// 但需求要求县级统计时是县级内平均市级统计时是各县平均再平均因此必须按县分组先计算平均值
// 此处我们实现为先按县分组每个小时取该县所有站的降水量平均值得到各县小时降水量然后所有县的小时降水量再平均市级或直接使用县级
// 但本方法返回值是总体结果需要在调用前已经确定是县级还是市级但调用时districtCodes已经是一个集合方法需要返回这个集合整体的平均降水量
// 因此对每个小时计算所有区县在该小时的平均降水量即先计算每个县内所有站的平均再对这些县的平均取平均
return calculateAverageByDistrict(stationDataMap, districtCodes, timePoints,
WeatherDataDTO::getHourlyPrecipitation);
}
// ===================== 气温统计 =====================
private TemperatureResult calculateTemperature(List<String> districtCodes, List<LocalDateTime> timePoints) {
// 1. 获取所有区县关联的气象站ID
List<String> stationIds = getStationIdsByDistricts(districtCodes);
if (stationIds.isEmpty()) {
return new TemperatureResult(emptyMap(timePoints), emptyMap(timePoints), emptyMap(timePoints));
}
// 2. 获取所有站点的气象数据
Map<String, Map<LocalDateTime, WeatherDataDTO>> stationDataMap = batchFetchWeatherData(stationIds, timePoints);
// 3. 分别计算平均最高最低
Map<LocalDateTime, Double> avgMap = calculateAverageByDistrict(stationDataMap, districtCodes, timePoints,
d -> parseDouble(d.getTemperature()));
Map<LocalDateTime, Double> maxMap = calculateExtremeByDistrict(stationDataMap, districtCodes, timePoints,
d -> parseDouble(d.getHourlyMaxTemperature()), true);
Map<LocalDateTime, Double> minMap = calculateExtremeByDistrict(stationDataMap, districtCodes, timePoints,
d -> parseDouble(d.getHourlyMinTemperature()), false);
return new TemperatureResult(avgMap, maxMap, minMap);
}
// ===================== 风速统计极大值 =====================
private Map<LocalDateTime, Double> calculateWindSpeed(List<String> districtCodes, List<LocalDateTime> timePoints) {
List<String> stationIds = getStationIdsByDistricts(districtCodes);
if (stationIds.isEmpty()) {
return emptyMap(timePoints);
}
Map<String, Map<LocalDateTime, WeatherDataDTO>> stationDataMap = batchFetchWeatherData(stationIds, timePoints);
// 风速规则先取各县内所有站的最大值再取所有县的最大值市级
return calculateExtremeByDistrict(stationDataMap, districtCodes, timePoints,
d -> parseDouble(d.getExtremeWindSpeedHourly()), true);
}
// ===================== 通用辅助方法 =====================
/**
* 根据区县编码列表获取所有气象站ID去重
*/
private List<String> getStationIdsByDistricts(List<String> districtCodes) {
if (CollectionUtils.isEmpty(districtCodes)) {
return Collections.emptyList();
}
List<WeatherSiteAreaConfiguration> configs = new ArrayList<>(); // new weatherSiteAreaConfigurationMapper.selectByDistrictCodes(districtCodes);
if (CollectionUtils.isEmpty(configs)) {
return Collections.emptyList();
}
return configs.stream()
.map(WeatherSiteAreaConfiguration::getStationId)
.filter(Objects::nonNull)
.distinct()
.collect(Collectors.toList());
}
/**
* 批量获取多个站点在多个时间点的气象数据
* @return Map<stationId, Map<小时, WeatherDataDTO>>
*/
private Map<String, Map<LocalDateTime, WeatherDataDTO>> batchFetchWeatherData(List<String> stationIds,
List<LocalDateTime> timePoints) {
Map<String, Map<LocalDateTime, WeatherDataDTO>> result = new HashMap<>();
for (String stationId : stationIds) {
Map<LocalDateTime, WeatherDataDTO> stationHourly = new HashMap<>();
for (LocalDateTime hour : timePoints) {
WeatherDataDTO data = weatherDataService.getWeatherData(stationId, hour);
if (data != null) {
stationHourly.put(hour, data);
}
}
result.put(stationId, stationHourly);
}
return result;
}
/**
* 计算按县平均后的指标先求每个县内所有站的平均再对各县平均取平均
* @param stationDataMap 站点原始数据
* @param districtCodes 区县列表
* @param timePoints 时间点
* @param valueExtractor 从WeatherDataDTO提取数值的函数
* @return 每个小时的平均值
*/
private Map<LocalDateTime, Double> calculateAverageByDistrict(Map<String, Map<LocalDateTime, WeatherDataDTO>> stationDataMap,
List<String> districtCodes,
List<LocalDateTime> timePoints,
Function<WeatherDataDTO, Double> valueExtractor) {
// 1. 建立区县 -> 站点ID列表的映射
Map<String, List<String>> districtStationMap = getDistrictStationMap(districtCodes);
Map<LocalDateTime, Double> result = new HashMap<>();
for (LocalDateTime hour : timePoints) {
List<Double> districtAverages = new ArrayList<>();
for (String districtCode : districtCodes) {
List<String> stations = districtStationMap.getOrDefault(districtCode, Collections.emptyList());
List<Double> values = new ArrayList<>();
for (String stationId : stations) {
WeatherDataDTO data = stationDataMap.getOrDefault(stationId, Collections.emptyMap()).get(hour);
Double val = valueExtractor.apply(data);
if (val != null) {
values.add(val);
}
}
if (!values.isEmpty()) {
double avg = values.stream().mapToDouble(Double::doubleValue).average().orElse(0.0);
districtAverages.add(avg);
}
}
// 所有县的平均再平均
if (!districtAverages.isEmpty()) {
double overallAvg = districtAverages.stream().mapToDouble(Double::doubleValue).average().orElse(0.0);
result.put(hour, overallAvg);
} else {
result.put(hour, 0.0);
}
}
return result;
}
/**
* 计算按县极值后的总体极值先求每个县内所有站的极值再取所有县的极值
* @param isMax true-最大值false-最小值
*/
private Map<LocalDateTime, Double> calculateExtremeByDistrict(Map<String, Map<LocalDateTime, WeatherDataDTO>> stationDataMap,
List<String> districtCodes,
List<LocalDateTime> timePoints,
Function<WeatherDataDTO, Double> valueExtractor,
boolean isMax) {
Map<String, List<String>> districtStationMap = getDistrictStationMap(districtCodes);
Map<LocalDateTime, Double> result = new HashMap<>();
for (LocalDateTime hour : timePoints) {
List<Double> districtExtremes = new ArrayList<>();
for (String districtCode : districtCodes) {
List<String> stations = districtStationMap.getOrDefault(districtCode, Collections.emptyList());
Double extreme = null;
for (String stationId : stations) {
WeatherDataDTO data = stationDataMap.getOrDefault(stationId, Collections.emptyMap()).get(hour);
Double val = valueExtractor.apply(data);
if (val != null) {
if (extreme == null) {
extreme = val;
} else if (isMax) {
extreme = Math.max(extreme, val);
} else {
extreme = Math.min(extreme, val);
}
}
}
if (extreme != null) {
districtExtremes.add(extreme);
}
}
// 取所有县极值的极值
if (!districtExtremes.isEmpty()) {
double overall = isMax ?
districtExtremes.stream().mapToDouble(Double::doubleValue).max().orElse(0.0) :
districtExtremes.stream().mapToDouble(Double::doubleValue).min().orElse(0.0);
result.put(hour, overall);
} else {
result.put(hour, 0.0);
}
}
return result;
}
/**
* 获取区县到站点ID列表的映射
*/
private Map<String, List<String>> getDistrictStationMap(List<String> districtCodes) {
List<WeatherSiteAreaConfiguration> configs = new ArrayList<>(); // weatherSiteAreaConfigurationMapper.selectByDistrictCodes(districtCodes);
if (CollectionUtils.isEmpty(configs)) {
return Collections.emptyMap();
}
return configs.stream()
.filter(c -> c.getStationId() != null)
.collect(Collectors.groupingBy(WeatherSiteAreaConfiguration::getDistrictCode,
Collectors.mapping(WeatherSiteAreaConfiguration::getStationId, Collectors.toList())));
}
/**
* 填充列表确保每个时间点都有值
*/
private <T> List<T> fillList(List<LocalDateTime> timePoints, Map<LocalDateTime, T> map, T defaultValue) {
List<T> list = new ArrayList<>();
for (LocalDateTime tp : timePoints) {
list.add(map.getOrDefault(tp, defaultValue));
}
return list;
}
private Map<LocalDateTime, Double> emptyMap(List<LocalDateTime> timePoints) {
Map<LocalDateTime, Double> map = new HashMap<>();
for (LocalDateTime tp : timePoints) {
map.put(tp, 0.0);
}
return map;
}
private Double parseDouble(String str) {
try {
return str == null ? null : Double.parseDouble(str);
} catch (NumberFormatException e) {
return null;
}
}
// 内部类用于返回气温的三个Map
private static class TemperatureResult {
private final Map<LocalDateTime, Double> avgMap;
private final Map<LocalDateTime, Double> maxMap;
private final Map<LocalDateTime, Double> minMap;
public TemperatureResult(Map<LocalDateTime, Double> avgMap,
Map<LocalDateTime, Double> maxMap,
Map<LocalDateTime, Double> minMap) {
this.avgMap = avgMap;
this.maxMap = maxMap;
this.minMap = minMap;
}
public Map<LocalDateTime, Double> getAvgMap() { return avgMap; }
public Map<LocalDateTime, Double> getMaxMap() { return maxMap; }
public Map<LocalDateTime, Double> getMinMap() { return minMap; }
}
}

View File

@ -93,7 +93,7 @@ public class WeatherDataServiceImpl implements WeatherDataService {
dto.setTemperature(n.getTemperature()); dto.setTemperature(n.getTemperature());
dto.setHourlyMaxTemperature(n.getHourlyMaxTemperature()); dto.setHourlyMaxTemperature(n.getHourlyMaxTemperature());
dto.setHourlyMinTemperature(n.getHourlyMinTemperature()); dto.setHourlyMinTemperature(n.getHourlyMinTemperature());
dto.setHourlyPrecipitation(n.getHourlyPrecipitation()); // dto.setHourlyPrecipitation(n.getHourlyPrecipitation());
dto.setDailyPrecipitation(n.getDailyPrecipitation()); dto.setDailyPrecipitation(n.getDailyPrecipitation());
dto.setExtremeWindSpeedHourly(n.getExtremeWindSpeedHourly()); dto.setExtremeWindSpeedHourly(n.getExtremeWindSpeedHourly());
return dto; return dto;
@ -107,7 +107,7 @@ public class WeatherDataServiceImpl implements WeatherDataService {
dto.setTemperature(r.getTemperature()); dto.setTemperature(r.getTemperature());
dto.setHourlyMaxTemperature(r.getHourlyMaxTemperature()); dto.setHourlyMaxTemperature(r.getHourlyMaxTemperature());
dto.setHourlyMinTemperature(r.getHourlyMinTemperature()); dto.setHourlyMinTemperature(r.getHourlyMinTemperature());
dto.setHourlyPrecipitation(r.getHourlyPrecipitation()); // dto.setHourlyPrecipitation(r.getHourlyPrecipitation());
dto.setDailyPrecipitation(r.getDailyPrecipitation()); dto.setDailyPrecipitation(r.getDailyPrecipitation());
dto.setExtremeWindSpeedHourly(r.getExtremeWindSpeedHourly()); dto.setExtremeWindSpeedHourly(r.getExtremeWindSpeedHourly());
return dto; return dto;

View File

@ -3,6 +3,7 @@ package com.southern.power.grid.utils;
import java.time.LocalDate; import java.time.LocalDate;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@ -122,4 +123,17 @@ public class TimeUtil {
// String strings = getBeforeNumDays("2026-03-15", 5); // String strings = getBeforeNumDays("2026-03-15", 5);
System.out.println(14/5); System.out.println(14/5);
} }
private static final DateTimeFormatter DEFAULT_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
public static LocalDateTime parseToLocalDateTime(String dateTimeStr) {
if (dateTimeStr == null || dateTimeStr.isEmpty()) {
return null;
}
try {
return LocalDateTime.parse(dateTimeStr, DEFAULT_FORMATTER);
} catch (DateTimeParseException e) {
return null;
}
}
} }

View File

@ -42,4 +42,12 @@
) )
</foreach> </foreach>
</insert> </insert>
<select id="selectByDistrictsAndTime" resultType="com.southern.power.grid.entity.DnerHourlyPowerOutageEvent">
SELECT * FROM dner_hourly_power_outage_event
WHERE org_code IN
<foreach collection="districtCodes" item="code" open="(" separator="," close=")">
#{code}
</foreach>
</select>
</mapper> </mapper>