learshaw 2 месяцев назад
Родитель
Сommit
a0e75043f6

+ 214 - 40
ems/ems-core/src/main/java/com/ruoyi/ems/service/alarm/InspectionAlarmAdapter.java

@@ -1,12 +1,9 @@
 /*
  * 文 件 名:  InspectionAlarmAdapter
  * 版    权:  华设设计集团股份有限公司
- * 描    述:  <描述>
+ * 描    述:  巡检与告警集成适配器 - 完整实现版
  * 修 改 人:  lvwenbin
  * 修改时间:  2026/2/3
- * 跟踪单号:  <跟踪单号>
- * 修改单号:  <修改单号>
- * 修改内容:  <修改内容>
  */
 package com.ruoyi.ems.service.alarm;
 
@@ -21,6 +18,7 @@ import com.ruoyi.ems.domain.InspectionRule;
 import com.ruoyi.ems.service.IEmsObjAttrValueService;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.commons.collections4.CollectionUtils;
+import org.apache.commons.lang3.StringUtils;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Component;
 
@@ -34,11 +32,15 @@ import java.util.stream.Collectors;
  * 巡检与告警集成适配器
  *
  * 【功能说明】
- * 将自动巡检的检查结果转换为告警
- * 实现巡检和告警的联动
+ * 1. 将自动巡检的异常结果转换为告警
+ * 2. 复用告警规则引擎进行检查
+ * 3. 实现巡检和告警的联动
  *
- * 【使用方式】
- * 在InspectionPlanServiceImpl中调用此适配器
+ * 【调用时机】
+ * 在 InspectionPlanServiceImpl.executeAutoInspection() 执行完成后调用
+ *
+ * 【告警来源】
+ * - Alarm.SOURCE_INSPECTION (2): 自动巡检产生的告警
  */
 @Slf4j
 @Component
@@ -53,6 +55,9 @@ public class InspectionAlarmAdapter {
     /**
      * 处理巡检结果,生成告警
      *
+     * 【核心入口方法】
+     * 在巡检执行完成后调用,将异常设备生成告警
+     *
      * @param report 巡检报告
      * @param details 巡检明细列表
      * @return 生成的告警列表
@@ -61,95 +66,147 @@ public class InspectionAlarmAdapter {
         List<Alarm> alarms = new ArrayList<>();
 
         if (report == null || CollectionUtils.isEmpty(details)) {
+            log.debug("巡检报告或明细为空,跳过告警生成");
             return alarms;
         }
 
         log.info("开始处理巡检结果生成告警: reportCode={}, detailCount={}",
             report.getReportCode(), details.size());
 
+        int processedCount = 0;
+        int alarmCount = 0;
+
         for (InspectionReportDetail detail : details) {
             // 只处理异常的设备
             if (detail.getResultStatus() == null || detail.getResultStatus() == 0) {
                 continue;
             }
 
+            processedCount++;
+
             try {
                 List<Alarm> deviceAlarms = processDeviceDetail(detail, report.getReportCode());
-                alarms.addAll(deviceAlarms);
+                if (CollectionUtils.isNotEmpty(deviceAlarms)) {
+                    alarms.addAll(deviceAlarms);
+                    alarmCount += deviceAlarms.size();
+                }
             } catch (Exception e) {
                 log.error("处理设备巡检结果异常: deviceCode={}, error={}",
-                    detail.getDeviceCode(), e.getMessage());
+                    detail.getDeviceCode(), e.getMessage(), e);
             }
         }
 
-        log.info("巡检结果告警生成完成: reportCode={}, alarmCount={}",
-            report.getReportCode(), alarms.size());
+        log.info("巡检结果告警生成完成: reportCode={}, 处理异常设备数={}, 生成告警数={}",
+            report.getReportCode(), processedCount, alarmCount);
 
         return alarms;
     }
 
     /**
-     * 处理单个设备的巡检明细
+     * 处理单个设备的巡检明细,生成告警
      */
     private List<Alarm> processDeviceDetail(InspectionReportDetail detail, String reportCode) {
         List<Alarm> alarms = new ArrayList<>();
 
-        // 构建设备信息
+        // 构建设备信息对象
+        EmsDevice device = buildDeviceFromDetail(detail);
+
+        // 获取设备属性值(从检查项结果或数据库)
+        Map<String, String> attrValues = getDeviceAttrValues(detail);
+
+        if (attrValues.isEmpty()) {
+            log.warn("设备属性值为空,无法生成告警: deviceCode={}", detail.getDeviceCode());
+            return alarms;
+        }
+
+        // 调用告警处理服务进行规则检查和告警生成
+        try {
+            List<Alarm> generatedAlarms = alarmProcessService.processInspection(
+                device, attrValues, reportCode);
+
+            if (CollectionUtils.isNotEmpty(generatedAlarms)) {
+                alarms.addAll(generatedAlarms);
+                log.debug("设备[{}]生成告警数量: {}", detail.getDeviceCode(), generatedAlarms.size());
+            }
+        } catch (Exception e) {
+            log.error("调用告警处理服务异常: deviceCode={}, error={}",
+                detail.getDeviceCode(), e.getMessage());
+        }
+
+        return alarms;
+    }
+
+    /**
+     * 从巡检明细构建设备对象
+     */
+    private EmsDevice buildDeviceFromDetail(InspectionReportDetail detail) {
         EmsDevice device = new EmsDevice();
         device.setDeviceCode(detail.getDeviceCode());
         device.setDeviceName(detail.getDeviceName());
         device.setDeviceModel(detail.getDeviceModel());
         device.setDeviceModelName(detail.getDeviceModelName());
         device.setLocation(detail.getLocation());
-        // 从areaPath解析areaCode
-        if (detail.getAreaPath() != null && detail.getAreaPath().contains(",")) {
+        device.setAreaPath(detail.getAreaPath());
+
+        // 从areaPath解析areaCode(取最后一个)
+        if (StringUtils.isNotBlank(detail.getAreaPath())) {
             String[] paths = detail.getAreaPath().split(",");
             if (paths.length > 0) {
-                device.setAreaCode(paths[paths.length - 1]);
+                device.setAreaCode(paths[paths.length - 1].trim());
             }
         }
 
-        // 获取设备属性值
-        Map<String, String> attrValues = getDeviceAttrValues(detail);
-
-        // 调用告警处理服务
-        List<Alarm> generatedAlarms = alarmProcessService.processInspection(device, attrValues, reportCode);
-        alarms.addAll(generatedAlarms);
+        // 从检查项中提取设备状态
+        List<CheckItemResult> checkItems = detail.getCheckItemList();
+        if (CollectionUtils.isNotEmpty(checkItems)) {
+            for (CheckItemResult item : checkItems) {
+                if ("deviceStatus".equals(item.getAttrKey()) && item.getActualValue() != null) {
+                    try {
+                        device.setDeviceStatus(Integer.parseInt(item.getActualValue()));
+                    } catch (NumberFormatException ignored) {
+                    }
+                    break;
+                }
+            }
+        }
 
-        return alarms;
+        return device;
     }
 
     /**
      * 从巡检明细中提取属性值
+     * 优先从检查项结果获取,否则从数据库获取
      */
     private Map<String, String> getDeviceAttrValues(InspectionReportDetail detail) {
         Map<String, String> attrValues = new HashMap<>();
 
-        // 从checkItems中提取
+        // 1. 优先从checkItems中提取(已有的检查结果)
         List<CheckItemResult> checkItems = detail.getCheckItemList();
         if (CollectionUtils.isNotEmpty(checkItems)) {
             for (CheckItemResult item : checkItems) {
-                if (item.getAttrKey() != null && item.getActualValue() != null) {
+                if (StringUtils.isNotBlank(item.getAttrKey())
+                    && StringUtils.isNotBlank(item.getActualValue())) {
                     attrValues.put(item.getAttrKey(), item.getActualValue());
                 }
             }
         }
 
-        // 如果checkItems为空,从数据库获取
-        if (attrValues.isEmpty() && detail.getDeviceModel() != null) {
+        // 2. 如果checkItems数据不完整,从数据库补充
+        if (attrValues.size() < 3 && StringUtils.isNotBlank(detail.getDeviceModel())) {
             try {
                 List<EmsObjAttrValue> dbValues = attrValueService.selectByObjCode(
                     detail.getDeviceModel(), detail.getDeviceCode());
                 if (CollectionUtils.isNotEmpty(dbValues)) {
-                    attrValues = dbValues.stream()
-                        .filter(v -> v.getAttrKey() != null && v.getAttrValue() != null)
-                        .collect(Collectors.toMap(
-                            EmsObjAttrValue::getAttrKey,
-                            EmsObjAttrValue::getAttrValue,
-                            (v1, v2) -> v2));
+                    for (EmsObjAttrValue v : dbValues) {
+                        if (v.getAttrKey() != null && v.getAttrValue() != null) {
+                            // 不覆盖已有值
+                            attrValues.putIfAbsent(v.getAttrKey(), v.getAttrValue());
+                        }
+                    }
                 }
             } catch (Exception e) {
-                log.warn("获取设备属性值失败: deviceCode={}", detail.getDeviceCode());
+                log.warn("获取设备属性值失败: deviceCode={}, error={}",
+                    detail.getDeviceCode(), e.getMessage());
             }
         }
 
@@ -160,8 +217,8 @@ public class InspectionAlarmAdapter {
      * 将巡检规则转换为告警规则格式
      *
      * 【说明】
-     * 此方法用于复用巡检规则配置
-     * 当巡检规则和告警规则需要统一时使用
+     * 用于需要复用巡检规则配置的场景
+     * 当巡检规则和告警规则需要统一管理时使用
      *
      * @param inspectionRule 巡检规则
      * @return 告警规则
@@ -189,20 +246,137 @@ public class InspectionAlarmAdapter {
         // 根据检查类型设置操作符
         if (inspectionRule.getCheckType() != null) {
             switch (inspectionRule.getCheckType()) {
-                case 1: // 范围
+                case 1: // 范围检查
                     alarmRule.setOperator(AlarmRule.OP_RANGE);
                     break;
-                case 2: // 等值
+                case 2: // 等值检查
                     alarmRule.setOperator(AlarmRule.OP_EQ);
                     break;
                 case 4: // 在线状态
                     alarmRule.setOperator(AlarmRule.OP_EQ);
+                    alarmRule.setThresholdValue("0"); // 离线值
                     break;
                 default:
+                    alarmRule.setOperator(AlarmRule.OP_NE);
                     break;
             }
         }
 
         return alarmRule;
     }
-}
+
+    /**
+     * 批量转换巡检规则为告警规则
+     *
+     * @param inspectionRules 巡检规则列表
+     * @return 告警规则列表
+     */
+    public List<AlarmRule> convertToAlarmRules(List<InspectionRule> inspectionRules) {
+        if (CollectionUtils.isEmpty(inspectionRules)) {
+            return new ArrayList<>();
+        }
+
+        return inspectionRules.stream()
+            .filter(r -> r != null && r.getEnabled() != null && r.getEnabled() == 1)
+            .map(this::convertToAlarmRule)
+            .filter(r -> r != null)
+            .collect(Collectors.toList());
+    }
+
+    /**
+     * 根据检查项结果直接生成告警(不依赖告警规则)
+     *
+     * 【用途】
+     * 当巡检检查项结果已经明确标记异常时,直接生成告警
+     * 无需再次进行规则匹配
+     *
+     * @param detail 巡检明细
+     * @param reportCode 报告代码
+     * @return 告警列表
+     */
+    public List<Alarm> createAlarmsFromCheckItems(InspectionReportDetail detail, String reportCode) {
+        List<Alarm> alarms = new ArrayList<>();
+
+        List<CheckItemResult> checkItems = detail.getCheckItemList();
+        if (CollectionUtils.isEmpty(checkItems)) {
+            return alarms;
+        }
+
+        for (CheckItemResult item : checkItems) {
+            // 只处理异常项
+            if (item.getStatus() == null || item.getStatus() == 0) {
+                continue;
+            }
+
+            try {
+                Alarm alarm = createAlarmFromCheckItem(detail, item, reportCode);
+                if (alarm != null) {
+                    alarms.add(alarm);
+                }
+            } catch (Exception e) {
+                log.warn("从检查项创建告警失败: deviceCode={}, ruleCode={}, error={}",
+                    detail.getDeviceCode(), item.getRuleCode(), e.getMessage());
+            }
+        }
+
+        return alarms;
+    }
+
+    /**
+     * 从单个检查项创建告警
+     */
+    private Alarm createAlarmFromCheckItem(InspectionReportDetail detail,
+        CheckItemResult item,
+        String reportCode) {
+        Alarm alarm = new Alarm();
+
+        // 设置告警ID(由AlarmProcessService处理)
+        alarm.setAlarmId(null);
+
+        // 设置区域(从areaPath解析)
+        if (StringUtils.isNotBlank(detail.getAreaPath())) {
+            String[] paths = detail.getAreaPath().split(",");
+            if (paths.length > 0) {
+                alarm.setAreaCode(paths[paths.length - 1].trim());
+            }
+        }
+
+        // 设置目标信息
+        alarm.setTargetType(2); // 设备
+        alarm.setTargetCode(detail.getDeviceCode());
+        alarm.setTargetName(detail.getDeviceName());
+        alarm.setDeviceModel(detail.getDeviceModel());
+        alarm.setDeviceModelName(detail.getDeviceModelName());
+        alarm.setLocation(detail.getLocation());
+
+        // 设置规则和属性信息
+        alarm.setRuleCode(item.getRuleCode());
+        alarm.setRuleName(item.getRuleName());
+        alarm.setAttrKey(item.getAttrKey());
+        alarm.setAttrName(item.getAttrName());
+        alarm.setAttrValue(item.getActualValue());
+        alarm.setThresholdValue(item.getExpectRange());
+
+        // 设置告警级别(默认一般)
+        alarm.setAlarmLevel(Alarm.LEVEL_NORMAL);
+
+        // 设置告警代码(从规则代码派生)
+        alarm.setAlarmCode("INSP_" + (StringUtils.isNotBlank(item.getRuleCode())
+            ? item.getRuleCode() : "UNKNOWN"));
+
+        // 设置告警消息
+        String msg = StringUtils.isNotBlank(item.getMessage())
+            ? item.getMessage()
+            : String.format("设备[%s]%s异常,当前值:%s",
+            detail.getDeviceName(),
+            item.getAttrName() != null ? item.getAttrName() : item.getAttrKey(),
+            item.getActualValue());
+        alarm.setAlarmMsg(msg);
+
+        // 设置来源信息
+        alarm.setAlarmSource(Alarm.SOURCE_INSPECTION);
+        alarm.setSourceRef(reportCode);
+
+        return alarm;
+    }
+}

+ 40 - 22
ems/ems-core/src/main/java/com/ruoyi/ems/service/impl/InspectionPlanServiceImpl.java

@@ -4,6 +4,7 @@ import com.alibaba.fastjson2.JSON;
 import com.alibaba.fastjson2.JSONArray;
 import com.huashe.common.utils.DateUtils;
 import com.huashe.common.utils.uuid.Seq;
+import com.ruoyi.ems.domain.Alarm;
 import com.ruoyi.ems.domain.CheckItemResult;
 import com.ruoyi.ems.domain.EmsDevice;
 import com.ruoyi.ems.domain.EmsObjAttrValue;
@@ -20,6 +21,7 @@ import com.ruoyi.ems.model.QueryDevice;
 import com.ruoyi.ems.service.IEmsDeviceService;
 import com.ruoyi.ems.service.IEmsObjAttrValueService;
 import com.ruoyi.ems.service.IInspectionPlanService;
+import com.ruoyi.ems.service.alarm.InspectionAlarmAdapter;
 import org.apache.commons.collections4.CollectionUtils;
 import org.apache.commons.lang3.StringUtils;
 import org.slf4j.Logger;
@@ -40,6 +42,7 @@ import java.util.stream.Collectors;
 
 /**
  * 巡检计划Service实现
+ *
  */
 @Service
 public class InspectionPlanServiceImpl implements IInspectionPlanService {
@@ -89,6 +92,12 @@ public class InspectionPlanServiceImpl implements IInspectionPlanService {
     @Autowired
     private IEmsObjAttrValueService attrValueService;
 
+    /**
+     * 【新增】注入告警适配器
+     */
+    @Autowired
+    private InspectionAlarmAdapter inspectionAlarmAdapter;
+
     @Override
     public InspectionPlan selectById(Long id) {
         InspectionPlan plan = planMapper.selectById(id);
@@ -170,7 +179,6 @@ public class InspectionPlanServiceImpl implements IInspectionPlanService {
         return cnt;
     }
 
-
     @Override
     @Transactional(rollbackFor = Exception.class)
     public int deleteByIds(Long[] ids) {
@@ -185,6 +193,9 @@ public class InspectionPlanServiceImpl implements IInspectionPlanService {
 
     /**
      * 执行自动巡检
+     *
+     * 【重要改动】
+     * 在巡检完成后调用 InspectionAlarmAdapter 生成告警
      */
     @Override
     @Transactional(rollbackFor = Exception.class)
@@ -206,6 +217,8 @@ public class InspectionPlanServiceImpl implements IInspectionPlanService {
         planMapper.updateStatus(planCode, PLAN_STATUS_RUNNING);
 
         InspectionReport report = null;
+        List<InspectionReportDetail> details = new ArrayList<>();
+
         try {
             // 获取需要巡检的设备列表
             List<EmsDevice> devices = getInspectionDevices(plan);
@@ -220,7 +233,7 @@ public class InspectionPlanServiceImpl implements IInspectionPlanService {
             List<InspectionRule> rules = ruleMapper.selectEnabledByPlanCode(planCode);
             log.info("获取到启用的巡检规则数量: {}", rules != null ? rules.size() : 0);
 
-            // 如果没有规则,创建默认规则(检查设备在线状态)
+            // 如果没有规则,创建默认规则
             if (CollectionUtils.isEmpty(rules)) {
                 log.warn("没有配置巡检规则,将使用默认规则(设备在线状态检查)");
                 rules = createDefaultRules();
@@ -231,7 +244,6 @@ public class InspectionPlanServiceImpl implements IInspectionPlanService {
             log.info("创建巡检报告: {}", report.getReportCode());
 
             // 执行巡检
-            List<InspectionReportDetail> details = new ArrayList<>();
             int normalCount = 0;
             int abnormalCount = 0;
 
@@ -288,6 +300,30 @@ public class InspectionPlanServiceImpl implements IInspectionPlanService {
             // 更新计划状态
             planMapper.updateStatus(planCode, PLAN_STATUS_COMPLETED);
 
+            // ========== 【新增】调用告警适配器生成告警 ==========
+            if (abnormalCount > 0) {
+                try {
+                    List<Alarm> alarms = inspectionAlarmAdapter.processInspectionResult(report, details);
+                    if (CollectionUtils.isNotEmpty(alarms)) {
+                        log.info("巡检告警生成完成: reportCode={}, 告警数量={}",
+                            report.getReportCode(), alarms.size());
+
+                        // 更新报告摘要,添加告警信息
+                        summary.put("generatedAlarms", alarms.size());
+                        summary.put("alarmIds", alarms.stream()
+                            .map(Alarm::getAlarmId)
+                            .collect(Collectors.toList()));
+                        report.setResultSummary(JSON.toJSONString(summary));
+                        reportMapper.update(report);
+                    }
+                } catch (Exception e) {
+                    // 告警生成失败不影响巡检主流程
+                    log.error("巡检告警生成异常: reportCode={}, error={}",
+                        report.getReportCode(), e.getMessage(), e);
+                }
+            }
+            // ========== 告警适配器调用结束 ==========
+
             // 返回完整报告
             report.setDetails(details);
             return report;
@@ -325,7 +361,7 @@ public class InspectionPlanServiceImpl implements IInspectionPlanService {
             throw new RuntimeException("该计划不是手动巡检类型");
         }
 
-        // 校验计划状态(只有待执行状态可以提交)
+        // 校验计划状态
         if (plan.getPlanStatus() != PLAN_STATUS_PENDING) {
             throw new RuntimeException("该计划已完成或已取消,无法提交报告");
         }
@@ -344,8 +380,6 @@ public class InspectionPlanServiceImpl implements IInspectionPlanService {
         report.setExecutor(StringUtils.isNotBlank(submit.getSubmitter()) ? submit.getSubmitter() : plan.getExecutor());
         report.setResultStatus(submit.getResultStatus() != null ? submit.getResultStatus() : RESULT_NORMAL);
         report.setManualRemark(submit.getManualRemark());
-
-        // 手动巡检不统计设备数量(由人工填写报告)
         report.setTotalCount(0);
         report.setNormalCount(0);
         report.setAbnormalCount(0);
@@ -356,7 +390,6 @@ public class InspectionPlanServiceImpl implements IInspectionPlanService {
         summary.put("targetNames", plan.getTargetNames());
         summary.put("submitter", submit.getSubmitter());
         summary.put("submitTime", DateUtils.dateTimeNow());
-
         if (StringUtils.isNotBlank(submit.getRemark())) {
             summary.put("remark", submit.getRemark());
         }
@@ -375,9 +408,6 @@ public class InspectionPlanServiceImpl implements IInspectionPlanService {
 
     // ===================== 以下为私有辅助方法 =====================
 
-    /**
-     * 创建默认巡检规则
-     */
     private List<InspectionRule> createDefaultRules() {
         List<InspectionRule> rules = new ArrayList<>();
         InspectionRule rule = new InspectionRule();
@@ -393,9 +423,6 @@ public class InspectionPlanServiceImpl implements IInspectionPlanService {
         return rules;
     }
 
-    /**
-     * 创建异常报告明细
-     */
     private InspectionReportDetail createErrorDetail(EmsDevice device, String reportCode, String errorMsg) {
         InspectionReportDetail detail = new InspectionReportDetail();
         detail.setReportCode(reportCode);
@@ -422,9 +449,6 @@ public class InspectionPlanServiceImpl implements IInspectionPlanService {
         return detail;
     }
 
-    /**
-     * 根据计划获取需要巡检的设备列表
-     */
     private List<EmsDevice> getInspectionDevices(InspectionPlan plan) {
         List<EmsDevice> devices = new ArrayList<>();
         List<String> targetCodes = plan.getTargetCodeList();
@@ -502,9 +526,6 @@ public class InspectionPlanServiceImpl implements IInspectionPlanService {
         return devices;
     }
 
-    /**
-     * 检查单个设备
-     */
     private InspectionReportDetail checkDevice(EmsDevice device, List<InspectionRule> rules, String reportCode) {
         InspectionReportDetail detail = new InspectionReportDetail();
         detail.setReportCode(reportCode);
@@ -580,9 +601,6 @@ public class InspectionPlanServiceImpl implements IInspectionPlanService {
         return detail;
     }
 
-    /**
-     * 检查单个规则
-     */
     private CheckItemResult checkRule(InspectionRule rule, EmsDevice device, Map<String, String> attrValueMap) {
         CheckItemResult result = new CheckItemResult();
         result.setRuleCode(rule.getRuleCode());