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<BiAlertRuleDO> 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<BiAlertRecordDO> 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<Map<String, Object>> 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;
|
}
|
}
|
|
}
|