停电开始时间超过7天自动归为已复电

;停电分时、日K图、日累计停电用户需按停电类型拆分故障停电、计划停电、全部(20%)
This commit is contained in:
yufengshuo 2026-03-24 18:00:19 +08:00
parent b69dc1e3b4
commit c7cbf45c2d
9 changed files with 217 additions and 20 deletions

View File

@ -7,6 +7,7 @@ import org.apache.ibatis.annotations.Param;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
/**
* 分时停电事件 -- mapper类
@ -35,4 +36,21 @@ public interface DnerHourlyPowerOutageEventMapper extends BaseMapper<DnerHourlyP
List<DnerHourlyPowerOutageEvent> selectByOrgCodesAndDataTime(@Param("orgCodes") List<String> orgCodes,
@Param("startDate") String startDate,
@Param("endDate") String endDate);
/**
* 查询 outageState=2 且按 orgCode+日期分组后最小 dataTime 早于指定时间的分组列表
* 返回 Map 包含 orgCodedateGroup(yyyy-MM-dd) 字段
*
* @param beforeTime 截止时间当前时间减7天
* @return 超期 orgCode+日期分组列表
*/
List<Map<String, Object>> selectExpiredDateGroups(@Param("beforeTime") String beforeTime);
/**
* orgCode+日期分组批量将 outageState 更新为 3
*
* @param groups orgCode+dateGroup 组合列表
* @return 更新行数
*/
int batchUpdateOutageStateByGroups(@Param("groups") List<Map<String, String>> groups);
}

View File

@ -103,6 +103,16 @@ public class DnerDailyPowerOutageEvent {
*/
private Integer scheduledUserCount;
/**
* 已复电停电影响用户数
*/
private Integer restoredUserCount;
/**
* 未复电停电影响用户数
*/
private Integer notRestoredUserCount;
/**
* 停电状态1-待停电2-停电中3-已复电
*/

View File

@ -20,4 +20,11 @@ public interface IDnerHourlyPowerOutageEventService extends IService<DnerHourlyP
* @return 返回结果
*/
HourlyPowerOutageEventChartVO queryIntradayData(String orgCode, String startDate, String endDate);
/**
* 停电开始时间超过7天自动归为已复电
*
*/
void setResumedScheduledTask();
}

View File

@ -244,6 +244,10 @@ public class DnerDailyPowerOutageEventBatchService {
daily.setExtremeWindSpeedHourly(fmt(list.stream().mapToDouble(e -> toDouble(e.getExtremeWindSpeedHourly())).max().orElse(0.0)));
daily.setPowerOutageDuration(fmt(list.stream().mapToDouble(e -> toDouble(e.getPowerOutageDuration())).max().orElse(0.0)));
daily.setFaultUserCount(list.stream().filter(Objects::nonNull).map(DnerHourlyPowerOutageEvent::getFaultUserCount).filter(Objects::nonNull).mapToInt(Integer::intValue).sum());
daily.setScheduledUserCount(list.stream().filter(Objects::nonNull).map(DnerHourlyPowerOutageEvent::getScheduledUserCount).filter(Objects::nonNull).mapToInt(Integer::intValue).sum());
daily.setRestoredUserCount(list.stream().filter(Objects::nonNull).map(DnerHourlyPowerOutageEvent::getRestoredUserCount).filter(Objects::nonNull).mapToInt(Integer::intValue).sum());
daily.setNotRestoredUserCount(list.stream().filter(Objects::nonNull).map(DnerHourlyPowerOutageEvent::getNotRestoredUserCount).filter(Objects::nonNull).mapToInt(Integer::intValue).sum());
return daily;
} catch (Exception e) {
log.error("转换日K线数据失败 - 日期: {}, 区域: {}, 错误: {}", date, orgCode, e.getMessage(), e);

View File

@ -7,13 +7,14 @@ import com.southern.power.grid.entity.HourlyChartCollectVO;
import com.southern.power.grid.entity.HourlyPowerOutageEventChartVO;
import com.southern.power.grid.service.IDnerHourlyPowerOutageEventService;
import com.southern.power.grid.utils.TimeUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
@ -23,6 +24,7 @@ import java.util.stream.Collectors;
* @author: junzhangfm
* @date: 2026/3/13
**/
@Slf4j
@Service
public class DnerHourlyPowerOutageEventServiceImpl
extends ServiceImpl<DnerHourlyPowerOutageEventMapper, DnerHourlyPowerOutageEvent>
@ -85,5 +87,53 @@ public class DnerHourlyPowerOutageEventServiceImpl
result.getWindList().add(0.0); // 小时极大风速
}
/**
* 每批次更新的日期分组数量控制单条 SQL IN 列表长度
*/
private static final int BATCH_SIZE = 200;
@Override
@Transactional(rollbackFor = Exception.class)
public void setResumedScheduledTask() {
// 7天前的时间点格式与 data_time 字段一致yyyy-MM-dd HH:mm
String beforeTime = LocalDateTime.now()
.minusDays(7)
.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:00:00"));
// 1. orgCode+日期 分组聚合只返回超期的分组标识不拉取业务行数据
List<Map<String, Object>> expiredGroups =
dnerHourlyPowerOutageEventMapper.selectExpiredDateGroups(beforeTime);
if (expiredGroups == null || expiredGroups.isEmpty()) {
log.info("[setResumedScheduledTask] 无超期停电数据,跳过更新");
return;
}
// 转换为 Map<String, String> XML foreach 使用
List<Map<String, String>> groups = expiredGroups.stream().map(m -> {
Map<String, String> g = new HashMap<>(4);
g.put("orgCode", String.valueOf(m.get("orgCode")));
g.put("dateGroup", String.valueOf(m.get("dateGroup")));
return g;
}).collect(Collectors.toList());
log.info("[setResumedScheduledTask] 共 {} 个超期分组orgCode+日期),开始批量更新", groups.size());
// 2. 分批次执行 UPDATE控制每条 SQL OR 条件数量避免过长
int total = 0;
for (
int i = 0; i < groups.size(); i += BATCH_SIZE) {
List<Map<String, String>> batch = groups.subList(i, Math.min(i + BATCH_SIZE, groups.size()));
int updated = dnerHourlyPowerOutageEventMapper.batchUpdateOutageStateByGroups(batch);
total += updated;
log.info("[setResumedScheduledTask] 批次 {}/{} 完成,本批更新 {} 行",
(i / BATCH_SIZE + 1),
(int) Math.ceil((double) groups.size() / BATCH_SIZE),
updated);
}
log.info("[setResumedScheduledTask] 全部完成,累计更新 {} 行", total);
}
}

View File

@ -4,15 +4,11 @@ import com.southern.power.grid.dao.DnerHourlyPowerOutageEventMapper;
import com.southern.power.grid.dao.NwSiteAreaConfigurationMapper;
import com.southern.power.grid.dao.RegionalWeatherDataMapper;
import com.southern.power.grid.dao.WeatherSiteAreaConfigurationMapper;
import com.southern.power.grid.entity.DataExcelEntity;
import com.southern.power.grid.entity.DnerHourlyPowerOutageEvent;
import com.southern.power.grid.entity.NwSiteAreaConfiguration;
import com.southern.power.grid.entity.RegionalWeatherData;
import com.southern.power.grid.entity.WeatherSiteAreaConfiguration;
import com.southern.power.grid.entity.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.ObjectUtils;
import javax.annotation.Resource;
import java.time.LocalDateTime;
@ -77,16 +73,37 @@ public class HourlyOutageExcelProcessService {
// continue;
// }
// 只有当前不是已复电状态才需要判断
if (!"已复电".equals(row.getOutageState())) {
boolean needSetRestored = false;
// 场景1停电时长为空 用当前时间 - 开始时间 判断是否超7天
if (ObjectUtils.isEmpty(row.getLengthOutage())) {
needSetRestored = LocalDateTime.now().isAfter(row.getStartTime().plusDays(7));
}
// 场景2停电时长不为空 直接判断时长是否7天
else {
needSetRestored = row.getLengthOutage() >= 7L * 24 * 60;
}
// 满足条件则设置为已复电
if (needSetRestored) {
row.setOutageState("已复电");
}
}
// 3.1 根据南网省//区县 district_code
String districtCode = findDistrictCode(row.getProvince(), row.getCity(), row.getDistrict());
if (districtCode == null) {
// 找不到映射可记录日志或统计
log.info("districtCode is null! {}, {}, {}", row.getProvince(), row.getCity(), row.getDistrict());
log.info("区域编码没有找到! {}, {}, {}", row.getProvince(), row.getCity(), row.getDistrict());
continue;
}
// 3.2 获取区域时段内气象数据
// 3.2 获取区域时段内气象数据(需要包含开始时间的整时数据)
List<RegionalWeatherData> regionalWeatherDataList =
regionalWeatherDataMapper.selectByOrgCodeAndDataTime(districtCode, DB_DATETIME_STR.format(row.getStartTime()));
regionalWeatherDataMapper.selectByOrgCodeAndDataTime(districtCode,
DateTimeFormatter.ofPattern("yyyy-MM-dd HH:00:00").format(row.getStartTime()));
// 3.4 组装分时停电事件实体
for (RegionalWeatherData regionalWeatherData : regionalWeatherDataList) {
@ -122,6 +139,7 @@ public class HourlyOutageExcelProcessService {
/**
* 累加已存在实体的字段
*
* @param existing 已存在的实体
* @param row 当前 Excel 行数据
*/
@ -131,7 +149,8 @@ public class HourlyOutageExcelProcessService {
/**
* 加载南网区划配置
* **/
*
**/
private void loadNwAreaConfig() {
nwAreaMap.clear();
List<NwSiteAreaConfiguration> list = nwSiteAreaConfigurationMapper.selectAll();
@ -147,7 +166,8 @@ public class HourlyOutageExcelProcessService {
/**
* 加载气象区划配置表
* **/
*
**/
private void loadWeatherAreaConfig() {
weatherAreaMap.clear();
List<WeatherSiteAreaConfiguration> list = weatherSiteAreaConfigurationMapper.selectAll();
@ -190,8 +210,28 @@ public class HourlyOutageExcelProcessService {
// Excel 本身的业务字段
event.setUserCount(row.getUserCount());
event.setOutageState(row.getOutageState());
event.setOutageType(row.getOutageType());
// 停电状态映射1-待停电2-停电中3-已复电
String outageStateStr = row.getOutageState();
if ("待停电".equals(outageStateStr)) {
event.setOutageState("1");
} else if ("停电中".equals(outageStateStr)) {
event.setOutageState("2");
} else if ("已复电".equals(outageStateStr)) {
event.setOutageState("3");
} else {
event.setOutageState(outageStateStr); // 保留原值或根据需要设置默认值
}
// 停电类型映射1-故障类2-计划类
String outageTypeStr = row.getOutageType();
if ("故障类".equals(outageTypeStr)) {
event.setOutageType("1");
} else if ("计划类".equals(outageTypeStr)) {
event.setOutageType("2");
} else {
event.setOutageType(outageTypeStr); // 保留原值或根据需要设置默认值
}
// 气象字段如不存在数据可以为 null
if (regionalWeatherData != null) {

View File

@ -0,0 +1,38 @@
package com.southern.power.grid.task;
import com.southern.power.grid.service.IDnerHourlyPowerOutageEventService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
/**
* 停电开始时间超过7天自动归为已复电
*
* @author fsyud
* {@code @date} 2026/03/20
*/
@Slf4j
@Component
public class OutageStateSetScheduledTask {
@Resource
private IDnerHourlyPowerOutageEventService hourlyPowerOutageEventService;
/* @PostConstruct
public void init() {
hourlyPowerOutageEventService.setResumedScheduledTask();
}*/
//@Scheduled(cron = "0 0 0 * * ?")
public void schedule() {
log.info("【设置已复电状态任务】定时触发--开始");
try {
hourlyPowerOutageEventService.setResumedScheduledTask();
log.info("【设置已复电状态任务】定时触发--结束");
} catch (Exception e) {
log.error("【设置已复电状态任务】执行异常", e);
throw new RuntimeException(e);
}
}
}

View File

@ -63,4 +63,34 @@
and data_time <![CDATA[ >= ]]> #{startDate}
and data_time <![CDATA[ <= ]]> #{endDate}
</select>
<!--
查询 outageState=2 的数据,按 org_code + DATE(data_time) 分组,
取每组最小 data_time若早于 beforeTime当前时间-7天则返回该分组。
只返回 orgCode+日期字符串,不拉取业务数据,避免大量数据进入内存。
-->
<select id="selectExpiredDateGroups" resultType="java.util.Map">
SELECT org_code AS orgCode, DATE(data_time) AS dateGroup
FROM dner_hourly_power_outage_event
WHERE outage_state = '2'
GROUP BY org_code, DATE(data_time)
HAVING MIN(data_time) <![CDATA[ < ]]> #{beforeTime}
</select>
<!--
按 orgCode+日期分组批量更新 outageState 为 3全部在 DB 层完成。
使用 OR 拼接每个 (org_code, DATE(data_time)) 组合条件。
-->
<update id="batchUpdateOutageStateByGroups">
UPDATE dner_hourly_power_outage_event
SET outage_state = '3',
update_time = NOW()
WHERE outage_state = '2'
AND (
<foreach collection="groups" item="g" separator=" OR ">
(org_code = #{g.orgCode} AND DATE(data_time) = #{g.dateGroup})
</foreach>
)
</update>
</mapper>