package cn.iocoder.yudao.module.bi.service.decision; import cn.iocoder.yudao.module.bi.dal.dataobject.decision.BiAlertRecordDO; import cn.iocoder.yudao.module.bi.dal.dataobject.decision.BiAlertRuleDO; import cn.iocoder.yudao.module.bi.dal.dataobject.decision.BiKpiDefinitionDO; import cn.iocoder.yudao.module.bi.dal.mysql.decision.BiAlertRecordMapper; import cn.iocoder.yudao.module.bi.dal.mysql.decision.BiAlertRuleMapper; import cn.iocoder.yudao.module.bi.dal.mysql.decision.BiKpiDefinitionMapper; import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; import java.math.BigDecimal; import java.time.LocalDateTime; import java.util.List; import java.util.Map; /** * 预警定时评估任务 * * 定期扫描所有活跃预警规则,评估阈值并生成预警记录 * * @author 超级管理员 */ @Slf4j @Component public class AlertEvaluationTask { @Resource private BiAlertRuleMapper alertRuleMapper; @Resource private BiKpiDefinitionMapper kpiMapper; @Resource private BiAlertRecordMapper alertRecordMapper; @Resource private JdbcTemplate jdbcTemplate; /** * 每5分钟评估一次所有活跃规则 */ @Scheduled(fixedDelayString = "${bi.alert.evaluation.interval:300000}") public void evaluateAllAlertRules() { log.debug("[evaluateAllAlertRules][开始评估预警规则]"); List rules = alertRuleMapper.selectList( BiAlertRuleDO::getEnabled, 1); if (rules.isEmpty()) { return; } for (BiAlertRuleDO rule : rules) { try { evaluateRule(rule); } catch (Exception e) { log.error("[evaluateAllAlertRules][规则 {} 评估异常]", rule.getId(), e); } } } private void evaluateRule(BiAlertRuleDO rule) { BiKpiDefinitionDO kpi = kpiMapper.selectById(rule.getKpiId()); if (kpi == null || kpi.getStatus() == 0) { return; } // 执行 KPI 查询 BigDecimal currentValue = executeQuery(kpi.getQuerySql()); if (currentValue == null) { return; } // 比较阈值 int cmp = currentValue.compareTo(rule.getThresholdValue()); boolean triggered = switch (rule.getComparisonOperator()) { case ">" -> cmp > 0; case "<" -> cmp < 0; case ">=" -> cmp >= 0; case "<=" -> cmp <= 0; default -> false; }; if (!triggered) { return; } // 检查是否已有未处理的同KPI预警(去重) Long existingCount = alertRecordMapper.selectCountByStatus(0); if (existingCount > 0) { List existing = alertRecordMapper.selectListByStatus(0); boolean dup = existing.stream().anyMatch(r -> r.getKpiId().equals(rule.getKpiId())); if (dup) { return; } } // 生成预警记录 BiAlertRecordDO record = BiAlertRecordDO.builder() .ruleId(rule.getId()) .kpiId(kpi.getId()) .kpiCode(kpi.getCode()) .kpiName(kpi.getName()) .actualValue(currentValue) .thresholdValue(rule.getThresholdValue()) .severity(rule.getSeverity()) .alertMessage(String.format("%s 当前值 %s 超出阈值 %s", kpi.getName(), currentValue, rule.getThresholdValue())) .status(0) .triggeredTime(LocalDateTime.now()) .build(); alertRecordMapper.insert(record); log.warn("[evaluateRule][触发预警] KPI={}, 当前值={}, 阈值={}", kpi.getName(), currentValue, rule.getThresholdValue()); } private BigDecimal executeQuery(String sql) { if (sql == null || sql.isBlank()) { return null; } try { List> result = jdbcTemplate.queryForList(sql); if (result.isEmpty()) { return null; } Object value = result.get(0).values().iterator().next(); if (value instanceof Number) { return BigDecimal.valueOf(((Number) value).doubleValue()); } return null; } catch (Exception e) { log.error("[executeQuery][SQL执行失败] {}", sql, e); return null; } } }