Browse Source

feat: 火情报警
1. 火情识别接口制作
2. 火情识别弹窗通知

kindring 1 year ago
parent
commit
14d4ff99f0

+ 6 - 1
src/main/java/com/genersoft/iot/vmp/storager/IAiControlStorage.java

@@ -5,6 +5,8 @@ import com.genersoft.iot.vmp.vmanager.bean.AiAlarmData;
 import com.github.pagehelper.PageInfo;
 import org.springframework.stereotype.Component;
 
+import java.util.List;
+
 /**
  * @description:ai相关接口
  * @author: kindring
@@ -13,8 +15,11 @@ import org.springframework.stereotype.Component;
 @SuppressWarnings("rawtypes")
 public interface IAiControlStorage {
     public PageInfo<AiAlarmData> searchAiAlarm(int arithmetic,String key,String alarmState,String startTime, String endTime, String order, String sort, int p, int l);
+
     public AiAlarmData getAlarmData(String alarmId);
-    public int getUnreadAlarmTotal();
+
+    public List<AiAlarmData> getUnreadAlarmTotal();
+
     public int markAllAlarm(int arithmetic);
     public int changeAiAlarm(String alarmId,String cmder);
 }

+ 8 - 2
src/main/java/com/genersoft/iot/vmp/storager/dao/HfyDevAiMapper.java

@@ -263,7 +263,7 @@ public interface HfyDevAiMapper {
             "</where>" +
             "ORDER BY ${sort} ${order}" +
             "</script>")
-    List<AiAlarmData> getAiAlarms(int arithmetic,String key,String alarmState,int startTime, int endTime, String order , String sort);
+    List<AiAlarmData> getAiAlarms(int arithmetic, String key, String alarmState, int startTime, int endTime, String order, String sort);
 
     @Select("select * from ai_alarm" +
             " where alarmId = #{alarmId}")
@@ -273,6 +273,12 @@ public interface HfyDevAiMapper {
             " where alarmState = #{alarmState}")
     public int getAlarmStateTotal(String alarmStateStr);
 
+    @Select("select" +
+            " *" +
+            " from ai_alarm" +
+            " where alarmState = #{alarmState}" +
+            " ORDER BY createTime")
+    List<AiAlarmData> getAlarmByState(String alarmStateStr);
 
 
     @Select("select * from ai_alarm_item" +
@@ -280,7 +286,7 @@ public interface HfyDevAiMapper {
     List<AiAlarm> getAiAlarmItemsByAlarmId(String alarmId);
 
     @Update("update ai_alarm set alarmState = #{alarmState} where alarmId = #{alarmId}")
-    int changeAlarmState(String alarmId,int alarmState);
+    int changeAlarmState(String alarmId, int alarmState);
 
     @Update("update ai_alarm set alarmState = #{alarmState} where arithmetic = #{arithmetic} and alarmState = 1")
     int changeAllAlarmState(int arithmetic,int alarmState);

+ 4 - 3
src/main/java/com/genersoft/iot/vmp/storager/impl/AiControlStorageImpl.java

@@ -86,11 +86,12 @@ public class AiControlStorageImpl implements IAiControlStorage {
         return alarmData;
     }
 
-    public int getUnreadAlarmTotal(){
+    public List<AiAlarmData> getUnreadAlarmTotal() {
 //        HfyDevAiMapper.getAiAlarmItemsByAlarmId(alarmId);
-        int alarmTotal = HfyDevAiMapper.getAlarmStateTotal("1");
+        List<AiAlarmData> aiAlarmData = HfyDevAiMapper.getAlarmByState("1");
+        // 获取所有未读的告警的简写
 //        logger.info("[测试数据库]alarmTotal:{}",alarmTotal);
-        return alarmTotal;
+        return aiAlarmData;
     }
 
     /**

+ 48 - 27
src/main/java/com/genersoft/iot/vmp/storager/impl/VideoManagerStorageImpl.java

@@ -737,61 +737,82 @@ public class VideoManagerStorageImpl implements IVideoManagerStorage {
 						 String firmware_version, String timestamp, String battery, String signal, String temp_env, String temp_cpu, String ccid){
 		// 1. 转储上传文件
 		UploadService uploadHandle = new UploadService();
-		String filePaths = uploadHandle.saveDevAlarm(sipConfig.getMediaPath(),deviceId,uploads);
+		String filePaths = uploadHandle.saveDevAlarm(sipConfig.getMediaPath(), deviceId, uploads);
 		Date date = new Date();
-		long unixTimestamp = date.getTime()/1000;
-		logger.info("[文件操作] 告警文件存储成功 文件:{}",filePaths);
-		logger.info("[数据库操作] alarmItems:{}",alarmItems);
-		int arithmeticNum = Integer.parseInt(arithmetic);
-		arithmeticNum = arithmeticNum + 1;
-		arithmetic = String.valueOf(arithmeticNum);
-
-		HfyDevAiMapper.saveAiAlarm(deviceId,channelId,arithmetic,alarmItems.size(), String.valueOf(unixTimestamp),filePaths,
-				firmware_version, timestamp, battery,signal,temp_env,temp_cpu,ccid);
+		long unixTimestamp = date.getTime() / 1000;
+		logger.info("[文件操作] 告警文件存储成功 文件:{}", filePaths);
+		logger.info("[数据库操作] alarmItems:{}", alarmItems);
+//		int arithmeticNum = Integer.parseInt(arithmetic);
+//		arithmeticNum = arithmeticNum + 1;
+//		arithmetic = String.valueOf(arithmeticNum);
+
+		HfyDevAiMapper.saveAiAlarm(deviceId, channelId, arithmetic, alarmItems.size(), String.valueOf(unixTimestamp), filePaths,
+				firmware_version, timestamp, battery, signal, temp_env, temp_cpu, ccid);
 		logger.info("[数据库操作] saveAiAlarm ok");
-		List<AiAlarm> agoAlarms = HfyDevAiMapper.findInsertAiAlarm(deviceId,channelId,arithmetic,alarmItems.size(), String.valueOf(unixTimestamp),filePaths);
-		if(agoAlarms.isEmpty()){logger.info("[数据库操作] 存储的数据文件无法找到{}",filePaths);return 1;}
+		List<AiAlarm> agoAlarms = HfyDevAiMapper.findInsertAiAlarm(deviceId, channelId, arithmetic, alarmItems.size(), String.valueOf(unixTimestamp), filePaths);
+		if (agoAlarms.isEmpty()) {
+			logger.info("[数据库操作] 存储的数据文件无法找到{}", filePaths);
+			return 1;
+		}
 		AiAlarm agoAlarm = agoAlarms.stream().findFirst().get();
-		logger.info("[数据库操作] findInsertAiAlarm ok ,alarmId:{}",agoAlarm.getAlarmId());
+		logger.info("[数据库操作] findInsertAiAlarm ok ,alarmId:{}", agoAlarm.getAlarmId());
 
 		// 4. 存储info信息
 		int AlarmId = agoAlarm.getAlarmId();
 		Boolean isFindUid = false;
-//		arithmetic = "2";
 		if (alarmItems.size()>0){
 			for (int i = 0; i < alarmItems.size(); i++) {
 				// 遍历 jsonarray 数组,把每一个对象转成 json 对象
 				JSONObject alarm = alarmItems.getJSONObject(i);
 				String msgid = alarm.getString("msgid");
 				String track_id = alarm.getString("track_id");
+				String id = alarm.getString("id");
 				String uid = alarm.getString("uid");
 				int x1 = alarm.getIntValue("x1");
 				int y1 = alarm.getIntValue("y1");
 				int x2 = alarm.getIntValue("x2");
 				int y2 = alarm.getIntValue("y2");
 				String similarity = String.valueOf(alarm.getDoubleValue("similarity"));
-				logger.info("Uid={}",uid);
-				if(!uid.equals("-1") ){
+				String scoreStr = String.valueOf(alarm.getDoubleValue("score"));
+				logger.info("Uid={}", uid);
+				if (uid != null && !uid.equals("-1")) {
 					isFindUid = true;
 				}
-				String info = "uid="+uid;
+				String info = "";
+				if (arithmetic.equals("1")) {
+					info = "人脸识别";
+				} else if (arithmetic.equals("2")) {
+					info = id;
+					track_id = arithmetic;
+					similarity = scoreStr;
+				} else {
+					if (arithmetic.equals("6")) {
+						info = "吊车";
+					} else if (arithmetic.equals("7")) {
+						info = "塔吊";
+					} else if (arithmetic.equals("8")) {
+						info = "火焰";
+					} else if (arithmetic.equals("9")) {
+						info = "施工机械";
+					} else if (arithmetic.equals("10")) {
+						info = "吊线异物";
+					} else if (arithmetic.equals("11")) {
+						info = "烟雾";
+					}
+					track_id = id;
+					similarity = scoreStr;
+				}
 				HfyDevAiMapper.inertAiarmItem(AlarmId, similarity,
-						x1,x2,y1,y2,
-						info,track_id,uid);
+						x1, x2, y1, y2,
+						info, track_id, uid);
 			}
 		}
 		logger.info("arithmetic:{}",arithmetic);
-		if(arithmetic == "1"){
-			logger.info("匹配到人脸识别的上传数据");
-		}
-		if(isFindUid){
-			logger.info("匹配到uid");
-		}
 		// 指定算法修改指定的记录
-		if(arithmeticNum == 1 && isFindUid){
+		if (arithmetic.equals("1") && isFindUid) {
 			logger.info("成功匹配到人脸");
 			// 修改是否有匹配到库中的数据
-			HfyDevAiMapper.changeAlarmType(AlarmId,1);
+			HfyDevAiMapper.changeAlarmType(AlarmId, 1);
 		}
 
 		// 返回正常值

+ 32 - 0
src/main/java/com/genersoft/iot/vmp/vmanager/bean/Ai_parse.java

@@ -0,0 +1,32 @@
+package com.genersoft.iot.vmp.vmanager.bean;
+
+import com.alibaba.fastjson2.JSONArray;
+
+public class Ai_parse {
+    private String arithmetic;
+    private JSONArray ai_info;
+
+    public String getArithmetic() {
+        return arithmetic;
+    }
+
+    ;
+
+    public void setArithmetic(String arithmetic) {
+        this.arithmetic = arithmetic;
+    }
+
+    ;
+
+    public JSONArray getAi_info() {
+        return ai_info;
+    }
+
+    ;
+
+    public void setAi_info(JSONArray ai_info) {
+        this.ai_info = ai_info;
+    }
+
+    ;
+}

+ 19 - 0
src/main/java/com/genersoft/iot/vmp/vmanager/bean/BodyAiAlarm.java

@@ -263,5 +263,24 @@ public class BodyAiAlarm {
         this.upload7 = upload7;
     }
 
+    public String toString() {
+        return "BodyAiAlarm{" +
+                "type='" + type + '\'' +
+                ", icharge='" + icharge + '\'' +
+                ", iload='" + iload + '\'' +
+                ", vcharge='" + vcharge + '\'' +
+                ", zoom_rate='" + zoom_rate + '\'' +
+                ", ccid='" + ccid + '\'' +
+                ", upload=" + upload +
+                ", upload0=" + upload0 +
+                ", upload1=" + upload1 +
+                ", upload2=" + upload2 +
+                ", upload3=" + upload3 +
+                ", upload4=" + upload4 +
+                ", upload5=" + upload5 +
+                ", upload6=" + upload6 +
+                ", upload7=" + upload7 +
+                '}';
+    }
 
 }

+ 4 - 2
src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/aiLib/AiApi.java

@@ -14,6 +14,8 @@ import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.web.bind.annotation.*;
 
+import java.util.List;
+
 @Tag(name = "ai数据接口")
 @RestController
 @RequestMapping("/ai")
@@ -72,9 +74,9 @@ public class AiApi {
     @GetMapping("/unread")
     public WVPResult getAlarmInfo(){
         WVPResult result = new WVPResult<>();
-        int count = storager.getUnreadAlarmTotal();
+        List<AiAlarmData> aiAlarmDataList = storager.getUnreadAlarmTotal();
         result.setCode(ErrorCode.SUCCESS.getCode());
-        result.setData(count);
+        result.setData(aiAlarmDataList);
         return result;
     }
 

+ 87 - 15
src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/aiLib/AiControl.java

@@ -3,6 +3,7 @@ package com.genersoft.iot.vmp.vmanager.gb28181.aiLib;
 import cn.dev33.satoken.annotation.SaIgnore;
 import com.alibaba.fastjson2.JSON;
 import com.alibaba.fastjson2.JSONArray;
+import com.alibaba.fastjson2.JSONObject;
 import com.genersoft.iot.vmp.conf.SipConfig;
 import com.genersoft.iot.vmp.conf.security.saToken.SaAdminCheckRole;
 import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
@@ -144,22 +145,27 @@ public class AiControl {
     @PostMapping("/alarm")
     @SaIgnore
     @ResponseBody
-    public WVPResult alarm(BodyAiAlarm bodyAiAlarm){
+    public WVPResult alarm(BodyAiAlarm bodyAiAlarm) {
         System.out.println("Received request body: " + bodyAiAlarm);
         System.out.println("Received request body: " + bodyAiAlarm.getReco_info());
-        logger.info("body{}",bodyAiAlarm);
-        logger.info("devId:{}",bodyAiAlarm.getDev_id());
-        logger.info("results:{}",bodyAiAlarm.getReco_info());
+        logger.info("body{}", bodyAiAlarm.toString());
+        logger.info("devId:{}", bodyAiAlarm.getDev_id());
+        logger.info("results:{}", bodyAiAlarm.getReco_info());
         // ps upload 0-8 为兼容性集成,以防多文件上传
         String deviceId = bodyAiAlarm.getDev_id();
         String channelId = bodyAiAlarm.getChannelId();
-        String arithmetic = bodyAiAlarm.getType();
+        // 解析ai识别结果
+        Ai_parse ai_parse = getAiInfo(bodyAiAlarm);
+        if (ai_parse == null) {
+            logger.error("[ai Parse] 无法解析 ai 信息");
+            return WVPResult.fail(ErrorCode.ERROR400.getCode(), ErrorCode.ERROR400.getMsg());
+        }
 //        判断是否存在失败到的数据
-        if (bodyAiAlarm.getReco_info().contains("failed")){
+        if (bodyAiAlarm.getReco_info().contains("failed")) {
             logger.error("ai upload not reco_info");
-            return WVPResult.fail(ErrorCode.ERROR400.getCode(),ErrorCode.ERROR400.getMsg());
+            return WVPResult.fail(ErrorCode.ERROR400.getCode(), ErrorCode.ERROR400.getMsg());
         }
-        JSONArray results_recoInfos = JSON.parseObject(bodyAiAlarm.getReco_info()).getJSONArray("results");
+
 
 //        List<AiAlarm> items = results_recoInfos;
         String firmware_version = bodyAiAlarm.getFirmware_version();
@@ -180,7 +186,7 @@ public class AiControl {
                 upload7 = bodyAiAlarm.getUpload7();
 
         logger.debug("上报ai识别接口调用 body:{}", bodyAiAlarm);
-        logger.debug("上报ai识别接口调用 items:{}", results_recoInfos);
+        logger.debug("上报ai识别接口调用 items:{}", ai_parse.getAi_info());
         logger.info("test upload{},upload0{}", upload, upload0);
         WVPResult result = new WVPResult<>();
         // 处理上传文件参数,合并 不同upload字段下的 file 文件
@@ -200,8 +206,8 @@ public class AiControl {
             result.setMsg("未接收到上传文件.待处理");
         }else{
             //
-            storager.saveAlarm(deviceId,channelId,arithmetic, results_recoInfos,uploads,
-                    firmware_version,timestamp,battery,signal,temp_env,temp_cpu,ccid);
+            storager.saveAlarm(deviceId, channelId, ai_parse.getArithmetic(), ai_parse.getAi_info(), uploads,
+                    firmware_version, timestamp, battery, signal, temp_env, temp_cpu, ccid);
             result.setCode(ErrorCode.SUCCESS.getCode());
             result.setMsg("ok");
         }
@@ -217,10 +223,76 @@ public class AiControl {
      * @param uploads
      */
     public void hbUploads(@RequestParam(value = "upload", required = false) MultipartFile upload, @RequestParam(value = "upload0", required = false) MultipartFile upload0, @RequestParam(value = "upload1", required = false) MultipartFile upload1, @RequestParam(value = "upload2", required = false) MultipartFile upload2, List<MultipartFile> uploads) {
-        if(upload!=null&&!upload.isEmpty()){ uploads.add(upload); }
-        if(upload0!=null&&!upload0.isEmpty()){ uploads.add(upload0); }
-        if(upload1!=null&&!upload1.isEmpty()){ uploads.add(upload1); }
-        if(upload2!=null&&!upload2.isEmpty()){ uploads.add(upload2); }
+        if (upload != null && !upload.isEmpty()) {
+            uploads.add(upload);
+        }
+        if (upload0 != null && !upload0.isEmpty()) {
+            uploads.add(upload0);
+        }
+        if (upload1 != null && !upload1.isEmpty()) {
+            uploads.add(upload1);
+        }
+        if (upload2 != null && !upload2.isEmpty()) {
+            uploads.add(upload2);
+        }
     }
     //
+
+    /**
+     * 获取算法类型和识别后的json数据
+     *
+     * @param bodyAiAlarm
+     * @return Ai_parse | null
+     */
+    public Ai_parse getAiInfo(BodyAiAlarm bodyAiAlarm) {
+//        人脸 1, 识别从 6开始, 中间预留部分给其它可能的算法, 车牌 2
+        String arithmetic = "";
+        JSONObject reco_info = JSON.parseObject(bodyAiAlarm.getReco_info());
+        if (reco_info == null) {
+            logger.error("ai upload not reco_info");
+            return null;
+        }
+        JSONArray ai_info = reco_info.getJSONArray("results");
+        if (ai_info == null) {
+            // 判断是否是车牌识别
+            ai_info = JSON.parseObject(bodyAiAlarm.getReco_info()).getJSONArray("plates");
+            arithmetic = "2";
+
+        }
+        // 判断是否为空数组
+        if (ai_info == null || 0 == ai_info.size()) {
+            logger.error("ai upload not reco_info");
+            return null;
+        }
+        // 获取
+        // 判断是人脸还是识别算法, 人脸识别的 识别对象中包含 msgid 识别则是 id
+        if (arithmetic.equals("") && ai_info.getJSONObject(0).containsKey("msgid")) {
+            arithmetic = "1";
+        }
+        // 判断具体是哪一项识别项
+        if (arithmetic.equals("")) {
+            // 根据id 文本判断是什么识别
+            // "DiaoChe","TaDiao","fire","ShiGongJiXie","DaoXianYiWu","smoke"
+            String id = ai_info.getJSONObject(0).getString("id");
+            if (id.equals("DiaoChe")) {
+                arithmetic = "6";
+            } else if (id.equals("TaDiao")) {
+                arithmetic = "7";
+            } else if (id.equals("fire")) {
+                arithmetic = "8";
+            } else if (id.equals("ShiGongJiXie")) {
+                arithmetic = "9";
+            } else if (id.equals("DaoXianYiWu")) {
+                arithmetic = "10";
+            } else if (id.equals("smoke")) {
+                arithmetic = "11";
+            }
+        }
+        Ai_parse ai_parse = new Ai_parse();
+        ai_parse.setArithmetic(arithmetic);
+        ai_parse.setAi_info(ai_info);
+        // 同时返回算法类型和识别后的json数据
+
+        return ai_parse;
+    }
 }

+ 2 - 2
web_src/src/components/bell.vue

@@ -72,9 +72,9 @@
           @sort-change="tableSortChange"
         >
           <el-table-column prop="alarmId" label="id" :sortable="'alarmId'"></el-table-column>
-          <el-table-column label="是否识别" >
+          <el-table-column label="是否识别" v-if="aiTypeVal == '1'">
             <template slot-scope="scope">
-              <span>{{parseAlarmType(scope.row.alarmType)}}</span>
+              <span>{{ parseAlarmType(scope.row.alarmType) }}</span>
             </template>
           </el-table-column>
           <el-table-column prop="deviceId" label="设备号"></el-table-column>

+ 3 - 3
web_src/src/components/mediaView.vue

@@ -15,10 +15,10 @@
     <div class="tab-box" style="width: 100%;">
       <div class="viewBox">
         <div class="view">
-          <img v-show="mediaImages[mediaInd]" :src="'/aiLib/'+mediaImages[mediaInd].url" alt="">
-          <div v-show="mediaImages[mediaInd]" class="toolBox">
+          <img v-if="mediaImages[mediaInd]" :src="'/aiLib/'+mediaImages[mediaInd].url" alt="">
+          <div v-if="mediaImages[mediaInd]" class="toolBox">
             <a :href="'/aiLib/'+mediaImages[mediaInd].url">
-              <el-button icon="el-icon-download" circle />
+              <el-button icon="el-icon-download" circle/>
             </a>
 
           </div>

+ 38 - 2
web_src/src/layout/UiHeader.vue

@@ -64,6 +64,7 @@ export default {
       username: '',
       activeIndex: this.$route.path,
       unreadAlarmCount: 0,
+      showFireAlarm: [],
       // 刷新时间
       refreshTime: 30 * 1000,
       requestInProgress: false,
@@ -175,13 +176,48 @@ export default {
       }
 
       if (res.data.code === 0) {
-        this.unreadAlarmCount = res.data.data;
+        let arr = res.data.data;
+        this.unreadAlarmCount = arr.length;
+        if (this.unreadAlarmCount < 0) {
+          return
+        }
+        // 寻找火情信息, 并进行去重
+        for (let i = 0; i < arr.length; i++) {
+          let alarmIndex = this.showFireAlarm.findIndex(item => item.alarmId === arr[i].alarmId);
+          if (alarmIndex === -1) {
+            this.showFireAlarm.push(arr[i]);
+            // 显示
+            this.fireAlarm(this.showFireAlarm.length - 1)
+          }
+        }
       } else {
-        console.log("未知错误")
+        console.log("未知错误");
         this.$message.error(res.data.msg);
       }
 
     },
+
+    fireAlarm(index) {
+      let alarm = this.showFireAlarm[index];
+      let fireUrl = `#/alarm/${alarm.alarmId}`
+      // 获取页面路由, 判断是否已经是告警了
+      let url = window.location.href;
+      if (url.indexOf(fireUrl) !== -1) {
+        console.log(`已经在处理该火情信息`)
+        return
+      }
+      this.$notify.error({
+        title: '火情告警',
+        dangerouslyUseHTMLString: true,
+        message: `<span>检测到火情信息, 请立即处理</span> <br/>
+                   <a href="${fireUrl}" target="_blank">
+                     <span>立即查看</span>
+                   </a>`,
+        duration: 0,
+      });
+    },
+
+
   },
   destroyed() {
     window.removeEventListener('beforeunload', e => this.beforeunloadHandler(e))

+ 5 - 5
web_src/src/map/ai.js

@@ -6,8 +6,8 @@ const arithmeticEnum = {
     text: '人脸识别',
   },
   fire:{
-    val: 2,
-    text: '火情识别'
+      val: 8,
+      text: '火情识别'
   },
   carPlate:{
     val: 3,
@@ -62,9 +62,9 @@ export const alarmStates = {
 }
 
 const aiTypes = {
-  face:arithmeticEnum.face.val,
-  fire:arithmeticEnum.fire.val,
-  carPlate:arithmeticEnum.carPlate.val,
+    face: arithmeticEnum.face.val,
+    carPlate: arithmeticEnum.carPlate.val,
+    fire: arithmeticEnum.fire.val,
 }
   /**
  * 通过数字查找对应的值

+ 4 - 1
参考文档/数据库扩展.md

@@ -55,11 +55,14 @@
 | arithmetic | int | 1-3 | 对应的算法.人脸,火情,车牌, |
 
 ### ai告警库 ai_alarm
+> ai算法目前包含 人脸 1, 识别从 6开始, 中间预留部分给其它可能的算法, 车牌 2
+> 识别算法包含 "DiaoChe","TaDiao","fire","ShiGongJiXie","DaoXianYiWu","smoke"
+> 吊车, 塔吊, 火, 施工机械, 吊线异物, 烟
 
 | 字段               | 类型      | 可选值 | 备注                  |
 |------------------|---------|-----|---------------------|
 | alarmId          | pk      |     |                     |
-| arithmetic       | int     | 1-3 | 算法                  |
+| arithmetic       | int     | 1-3 | 算法 人脸, 识别, 车牌       |
 | mediaPath        | varchar | 255 | 资源地址                |
 | rawMediaPath     | varchar | 255 | 资源地址                |
 | alarmState       | int     | 1-5 | 告警状态 1 未读, 2忽略,3已处理 |