424 lines
16 KiB
Java
424 lines
16 KiB
Java
package com.gunshi.project.ss.timetask;
|
||
|
||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||
import com.gunshi.project.ss.common.model.StRiverRReal;
|
||
import com.gunshi.project.ss.common.model.StRsvrRReal;
|
||
import com.gunshi.project.ss.common.model.StStbprpB;
|
||
import com.gunshi.project.ss.common.util.LocalDateTimeConverter;
|
||
import com.gunshi.project.ss.entity.so.WeatherSo;
|
||
import com.gunshi.project.ss.entity.vo.*;
|
||
import com.gunshi.project.ss.model.*;
|
||
import com.gunshi.project.ss.service.*;
|
||
import jakarta.annotation.Resource;
|
||
import lombok.extern.slf4j.Slf4j;
|
||
import org.springframework.beans.factory.annotation.Autowired;
|
||
import org.springframework.context.annotation.Profile;
|
||
import org.springframework.scheduling.annotation.EnableScheduling;
|
||
import org.springframework.stereotype.Component;
|
||
|
||
import java.math.BigDecimal;
|
||
import java.math.RoundingMode;
|
||
import java.time.LocalDate;
|
||
import java.time.LocalDateTime;
|
||
import java.time.ZoneId;
|
||
import java.time.format.DateTimeFormatter;
|
||
import java.util.ArrayList;
|
||
import java.util.List;
|
||
import java.util.stream.Collectors;
|
||
|
||
@EnableScheduling//开启定时任务
|
||
@Component
|
||
@Slf4j
|
||
@Profile({"prod","dev","local"})
|
||
public class WarningRuleTask {
|
||
|
||
|
||
@Autowired
|
||
private WarningRuleService warningRuleService;
|
||
|
||
|
||
@Autowired
|
||
private WarningRuleInfoService warningRuleInfoService;
|
||
|
||
//河道水位
|
||
@Autowired
|
||
private RiverWaterService riverWaterService;
|
||
|
||
@Autowired
|
||
private StRiverRRealService stRiverRRealService;
|
||
|
||
|
||
//水库水位
|
||
@Autowired
|
||
private ReservoirWaterService reservoirWaterService;
|
||
|
||
@Autowired
|
||
private StRsvrRRealService stRsvrRRealService;
|
||
|
||
@Autowired
|
||
private ForecastResultsService forecastResultsService;
|
||
|
||
@Resource
|
||
private ForecastService forecastService;
|
||
|
||
|
||
@Autowired
|
||
private RainBasinDivisionService rainBasinDivisionService;
|
||
|
||
// @Scheduled(fixedDelay = 60 * 60 * 1000) // 75分钟,单位毫秒
|
||
// @Async
|
||
public void warningRuleExecute(){
|
||
//获取配置的预警规则
|
||
List<WarningRule> warningRules = warningRuleService.listData();
|
||
//过滤出启动了的预警规则
|
||
warningRules = warningRules.stream().filter(o ->{
|
||
return o.getStatus() != null && o.getStatus() == 1;
|
||
}).collect(Collectors.toList());
|
||
|
||
for (WarningRule warningRule : warningRules) {
|
||
//获取预警类型
|
||
String warningType = warningRule.getWarningType();
|
||
//获取预警的规则细节
|
||
List<WarningCondition> conditions = warningRule.getConditions();
|
||
|
||
if (conditions == null || conditions.isEmpty()) {
|
||
continue;
|
||
}
|
||
|
||
// 为每个条件计算实际值并设置是否满足
|
||
for (WarningCondition condition : conditions) {
|
||
BigDecimal actualValue = calculateActualValue(condition, warningType);
|
||
condition.setIsEnjoy(compareValue(actualValue, condition));
|
||
}
|
||
|
||
// 根据条件关系判断整个规则是否满足
|
||
boolean isRuleSatisfied = evaluateConditions(conditions);
|
||
|
||
// 这里可以根据 isRuleSatisfied 进行后续处理,比如触发预警等
|
||
if (isRuleSatisfied) {
|
||
log.info("预警规则 {} 满足条件,触发预警", warningRule.getId());
|
||
|
||
// 生成规则信息
|
||
String ruleInfo = generateRuleInfo(conditions);
|
||
|
||
WarningRuleInfo warningRuleInfo = new WarningRuleInfo();
|
||
warningRuleInfo.setRuleId(warningRule.getId());
|
||
warningRuleInfo.setConditions(conditions);
|
||
warningRuleInfo.setCreateTime(LocalDateTime.now());
|
||
warningRuleInfo.setStatus(0);
|
||
warningRuleInfoService.save(warningRuleInfo);
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 生成规则信息描述
|
||
*/
|
||
private String generateRuleInfo(List<WarningCondition> conditions) {
|
||
List<String> conditionInfos = new ArrayList<>();
|
||
|
||
for (WarningCondition condition : conditions) {
|
||
if (condition.getIsEnjoy()) { // 只记录满足条件的报警信息
|
||
String conditionInfo = generateSingleConditionInfo(condition);
|
||
if (conditionInfo != null && !conditionInfo.isEmpty()) {
|
||
conditionInfos.add(conditionInfo);
|
||
}
|
||
}
|
||
}
|
||
|
||
return String.join(";", conditionInfos);
|
||
}
|
||
|
||
|
||
@Autowired
|
||
private StStbprpBService stStbprpBService;
|
||
/**
|
||
* 生成单个条件的描述信息 - 修改格式
|
||
*/
|
||
private String generateSingleConditionInfo(WarningCondition condition) {
|
||
// String indicatorType = condition.getIndicatorType();
|
||
// String stcd = condition.getStcd();
|
||
String indicatorType = null;
|
||
String stcd = null;
|
||
String stnm="";
|
||
List<StStbprpB> stStbprpBS = stStbprpBService.lambdaQuery().eq(StStbprpB::getStcd, stcd).last("limit 1").list();
|
||
if(stStbprpBS != null && !stStbprpBS.isEmpty()){
|
||
stnm = stStbprpBS.get(0).getStnm();
|
||
}
|
||
BigDecimal thresholdValue = condition.getThresholdValue();
|
||
Integer durationHours = condition.getDurationHours();
|
||
|
||
switch (indicatorType) {
|
||
case "REAL_WATER_LEVEL":
|
||
BigDecimal currentWaterLevel = getRzByStcd(stcd);
|
||
return String.format("%s测点监测水位%sm %s %sm",
|
||
stnm, currentWaterLevel, condition.getOperator(), thresholdValue);
|
||
|
||
case "PEAK_FLOW":
|
||
return String.format("未来%dh预报洪峰流量 %s %s m³/s",
|
||
durationHours != null ? durationHours : 24, condition.getOperator(), thresholdValue);
|
||
|
||
case "RAINFALL":
|
||
BigDecimal currentRainfall = getRealRainFall(stcd, durationHours);
|
||
return String.format("%s测点过去%dh降雨量%smm %s %smm",
|
||
stnm, durationHours != null ? durationHours : 24,
|
||
currentRainfall, condition.getOperator(), thresholdValue);
|
||
|
||
// case "WATER_STORAGE":
|
||
// BigDecimal currentStoragePercent = getWaterStorage(condition.getYear());
|
||
// return String.format("蓄水量 %s 同期%s%%",
|
||
// condition.getOperator(), thresholdValue);
|
||
|
||
case "FORECAST_RAINFALL":
|
||
return String.format("%s测点未来%dh降雨量 %s %smm",
|
||
stnm, durationHours != null ? durationHours : 24,
|
||
condition.getOperator(), thresholdValue);
|
||
|
||
default:
|
||
return String.format("未知指标类型%s %s %s", stcd, condition.getOperator(), thresholdValue);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 计算条件的实际值
|
||
*/
|
||
private BigDecimal calculateActualValue(WarningCondition condition, String warningType) {
|
||
// String indicatorType = condition.getIndicatorType();
|
||
// String stcd = condition.getStcd();
|
||
String indicatorType = null;
|
||
String stcd = null;
|
||
Integer durationHours = condition.getDurationHours();
|
||
|
||
switch (indicatorType) {
|
||
case "REAL_WATER_LEVEL":
|
||
return getRzByStcd(stcd);
|
||
case "PEAK_FLOW":
|
||
return getFloowMaxQ(durationHours);
|
||
case "RAINFALL":
|
||
return getRealRainFall(stcd, durationHours);
|
||
case "WATER_STORAGE":
|
||
// return getWaterStorage(condition.getYear());
|
||
case "FORECAST_RAINFALL":
|
||
return getForecastRainFall(stcd, durationHours);
|
||
default:
|
||
return BigDecimal.ZERO;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 比较实际值和阈值
|
||
*/
|
||
private boolean compareValue(BigDecimal actualValue, WarningCondition condition) {
|
||
if (actualValue == null || condition.getThresholdValue() == null) {
|
||
return false;
|
||
}
|
||
|
||
String operator = condition.getOperator();
|
||
BigDecimal threshold = condition.getThresholdValue();
|
||
|
||
switch (operator) {
|
||
case ">":
|
||
return actualValue.compareTo(threshold) > 0;
|
||
case ">=":
|
||
return actualValue.compareTo(threshold) >= 0;
|
||
case "<":
|
||
return actualValue.compareTo(threshold) < 0;
|
||
case "<=":
|
||
return actualValue.compareTo(threshold) <= 0;
|
||
default:
|
||
return false;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 评估条件组合是否满足
|
||
* 处理 AND/OR 逻辑关系
|
||
*/
|
||
private boolean evaluateConditions(List<WarningCondition> conditions) {
|
||
if (conditions.isEmpty()) {
|
||
return false;
|
||
}
|
||
|
||
boolean result = false;
|
||
boolean currentGroup = conditions.get(0).getIsEnjoy();
|
||
|
||
for (int i = 0; i < conditions.size() - 1; i++) {
|
||
WarningCondition current = conditions.get(i);
|
||
WarningCondition next = conditions.get(i + 1);
|
||
String relation = current.getRelationType();
|
||
|
||
if ("AND".equals(relation)) {
|
||
// AND 关系:当前组与下一个条件进行与运算
|
||
currentGroup = currentGroup && next.getIsEnjoy();
|
||
} else if ("OR".equals(relation)) {
|
||
// OR 关系:当前结果与当前组进行或运算,然后开始新的组
|
||
result = result || currentGroup;
|
||
currentGroup = next.getIsEnjoy();
|
||
}
|
||
}
|
||
|
||
// 处理最后一个条件
|
||
result = result || currentGroup;
|
||
|
||
return result;
|
||
}
|
||
|
||
//获取实时水位
|
||
private BigDecimal getRzByStcd(String stcd){
|
||
//获取河道水位的站点编码
|
||
List<String> riverStcd = riverWaterService.getRiverStcd();
|
||
if(riverStcd.contains(stcd)){
|
||
LambdaQueryWrapper<StRiverRReal> queryWrapper = new LambdaQueryWrapper<>();
|
||
queryWrapper.eq(StRiverRReal::getStcd,stcd);
|
||
StRiverRReal stRiverRReal = stRiverRRealService.getBaseMapper().selectOne(queryWrapper);
|
||
if(stRiverRReal != null){
|
||
return stRiverRReal.getZ();
|
||
}
|
||
return BigDecimal.ZERO;
|
||
}else{
|
||
LambdaQueryWrapper<StRsvrRReal> queryWrapper = new LambdaQueryWrapper<>();
|
||
queryWrapper.eq(StRsvrRReal::getStcd,stcd);
|
||
StRsvrRReal stRsvrRReal = stRsvrRRealService.getBaseMapper().selectOne(queryWrapper);
|
||
if(stRsvrRReal != null){
|
||
return stRsvrRReal.getRz();
|
||
}
|
||
return BigDecimal.ZERO;
|
||
}
|
||
}
|
||
|
||
//获取预报洪峰流量
|
||
private BigDecimal getFloowMaxQ(Integer durationHours){
|
||
ForecastTask forecastTask = new ForecastTask();
|
||
// 设置当前时间整点
|
||
LocalDateTime now = LocalDateTime.now();
|
||
LocalDateTime nowHour = now.withMinute(0).withSecond(0).withNano(0);
|
||
forecastTask.setNowTime(LocalDateTimeConverter.toDate(nowHour));
|
||
|
||
// 设置开始时间(当前时间减去一天)
|
||
LocalDateTime startTime = nowHour.minusDays(1);
|
||
forecastTask.setStartTime(LocalDateTimeConverter.toDate(startTime));
|
||
|
||
// 设置结束时间(当前时间加上durationHours)
|
||
LocalDateTime endTime = nowHour.plusHours(durationHours);
|
||
forecastTask.setEndTime(LocalDateTimeConverter.toDate(endTime));
|
||
|
||
forecastTask.setForecastWarm(1);
|
||
forecastTask.setForecastPeriod(durationHours); // 设置预报时长
|
||
List<ForecastResultVo> humanForecastResult = forecastResultsService.getHumanForecastResult(forecastTask);
|
||
//过滤出预报的数据
|
||
humanForecastResult = humanForecastResult.stream().filter(o ->{
|
||
return o.getIspreDrp().equals("1") && o.getYcRkQValue() != null;
|
||
}).collect(Collectors.toList());
|
||
BigDecimal totalQ = BigDecimal.ZERO;
|
||
for (ForecastResultVo forecastResultVo : humanForecastResult) {
|
||
totalQ = totalQ.add(forecastResultVo.getYcRkQValue());
|
||
}
|
||
return totalQ;
|
||
}
|
||
|
||
|
||
//获取降雨量的值
|
||
public BigDecimal getRealRainFall(String stcd,Integer durationHours){
|
||
StPptnDetailsVo stPptnDetailsVo = rainBasinDivisionService.queryStPptnDetailsByStcd(stcd);
|
||
if (stPptnDetailsVo == null) {
|
||
return BigDecimal.ZERO; // 或返回 BigDecimal.ZERO,根据业务需求调整
|
||
}
|
||
|
||
BigDecimal result = switch (durationHours) {
|
||
case 1 -> stPptnDetailsVo.getH1();
|
||
case 3 -> stPptnDetailsVo.getH3();
|
||
case 6 -> stPptnDetailsVo.getH6();
|
||
case 12 -> stPptnDetailsVo.getH12();
|
||
case 24 -> stPptnDetailsVo.getH24();
|
||
case 48 -> stPptnDetailsVo.getH48();
|
||
// 3. 未匹配的时长(如2、5等),返回null或默认值
|
||
default -> BigDecimal.ZERO; // 或 throw new IllegalArgumentException("不支持的时长:" + durationHours);
|
||
};
|
||
return result == null?BigDecimal.ZERO:result;
|
||
}
|
||
|
||
@Autowired
|
||
private HisWaterDataService hisWaterDataService;
|
||
|
||
//获取蓄水量 - 修改后的逻辑
|
||
private BigDecimal getWaterStorage(String year){
|
||
List<AttResBaseVo> list = reservoirWaterService.list();
|
||
if (list == null || list.isEmpty()) {
|
||
return BigDecimal.ZERO;
|
||
}
|
||
|
||
AttResBaseVo reservoir = list.get(0);
|
||
BigDecimal nowCap = reservoir.getNowCap() == null ? BigDecimal.ZERO : reservoir.getNowCap();
|
||
BigDecimal deadCap = reservoir.getDeadCap() == null ? BigDecimal.ZERO : reservoir.getDeadCap();
|
||
BigDecimal availableWater = nowCap.subtract(deadCap); // 当前可用水量
|
||
|
||
// 获取当前月份
|
||
int currentMonth = LocalDateTime.now().getMonthValue();
|
||
String month = String.valueOf(currentMonth);
|
||
|
||
// 获取某年同月的蓄水量作为基准值
|
||
List<HisWaterData> hisWaterDataList = hisWaterDataService.lambdaQuery()
|
||
.eq(HisWaterData::getYear, year)
|
||
.eq(HisWaterData::getMonth, month)
|
||
.eq(HisWaterData::getType,2)
|
||
.list();
|
||
|
||
BigDecimal baseWaterStorage = BigDecimal.ZERO;
|
||
if (!hisWaterDataList.isEmpty()) {
|
||
HisWaterData hisWaterData = hisWaterDataList.get(0);
|
||
baseWaterStorage = hisWaterData.getAvgWater() == null ? BigDecimal.ZERO : hisWaterData.getAvgWater();
|
||
}
|
||
|
||
// 如果基准值为0,避免除零错误,返回0
|
||
if (baseWaterStorage.compareTo(BigDecimal.ZERO) == 0) {
|
||
return BigDecimal.ZERO;
|
||
}
|
||
|
||
// 计算当前可用水量占同期蓄水量的百分比
|
||
BigDecimal percentage = availableWater.divide(baseWaterStorage, 4, RoundingMode.HALF_UP)
|
||
.multiply(new BigDecimal("100"));
|
||
|
||
return percentage;
|
||
}
|
||
|
||
|
||
|
||
//获取预测降雨量
|
||
private BigDecimal getForecastRainFall(String stcd,Integer durationHours) {
|
||
DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("yyyyMMdd");
|
||
// 获取当前日期(默认系统时区,可指定时区如:ZoneId.of("UTC"))
|
||
LocalDate today = LocalDate.now(ZoneId.systemDefault());
|
||
String todayDateStr = today.format(dateFormatter); // 结果示例:20251120
|
||
String tm = todayDateStr + "08"; // 拼接固定小时,最终格式:2025112008
|
||
WeatherSo weatherSo = new WeatherSo();
|
||
weatherSo.setTm(tm);
|
||
List<ForeRainVo> fore = forecastService.fore(weatherSo);
|
||
for (ForeRainVo foreRainVo : fore) {
|
||
if (stcd.equals(foreRainVo.getStcd())) {
|
||
BigDecimal forecastRain = BigDecimal.ZERO;
|
||
switch (durationHours) {
|
||
case 1:
|
||
forecastRain = foreRainVo.getH1();
|
||
return forecastRain;
|
||
case 3:
|
||
forecastRain = foreRainVo.getH3();
|
||
return forecastRain;
|
||
case 6:
|
||
forecastRain = foreRainVo.getH6();
|
||
return forecastRain;
|
||
case 12:
|
||
forecastRain = foreRainVo.getH12();
|
||
return forecastRain;
|
||
case 24:
|
||
forecastRain = new BigDecimal(foreRainVo.getH24());
|
||
return forecastRain;
|
||
default:
|
||
return BigDecimal.ZERO;
|
||
}
|
||
}
|
||
}
|
||
return BigDecimal.ZERO;
|
||
}
|
||
|
||
}
|