This is an automated email from the ASF dual-hosted git repository.

zhaoqingran pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/hertzbeat.git


The following commit(s) were added to refs/heads/master by this push:
     new f7676ac8c [improve] update alarm inhibit rule and alarm ui (#2957)
f7676ac8c is described below

commit f7676ac8ca51a199ed9f5ffa3bebee4486579ace
Author: tomsun28 <[email protected]>
AuthorDate: Wed Jan 8 10:04:07 2025 +0800

    [improve] update alarm inhibit rule and alarm ui (#2957)
---
 .all-contributorsrc                                |  45 +++++
 README.md                                          |   7 +
 README_CN.md                                       |   7 +
 .../apache/hertzbeat/alert/AlerterProperties.java  |  18 ++
 .../apache/hertzbeat/alert/dto/AlertDefineDTO.java |  27 ++-
 .../hertzbeat/alert/reduce/AlarmInhibitReduce.java | 115 ++++++-----
 .../impl/AlertDefineExcelImExportServiceImpl.java  | 102 ++++++----
 .../alert/reduce/AlarmGroupReduceTest.java         | 122 ++++++++++++
 .../alert/reduce/AlarmInhibitReduceTest.java       | 219 ++++++++++++++++++---
 .../AlertDefineExcelImExportServiceTest.java       |  15 +-
 .../AlertDefineJsonImExportServiceTest.java        |  17 +-
 .../AlertDefineYamlImExportServiceTest.java        |  28 ++-
 .../manager/controller/MonitorsController.java     |   2 +-
 .../service/impl/AbstractImExportServiceImpl.java  |  15 +-
 .../service/impl/ExcelImExportServiceImpl.java     |  50 ++---
 .../manager/service/impl/MonitorServiceImpl.java   |   3 +-
 .../src/main/resources/application.yml             |   3 +
 .../manager/service/YamlImExportServiceTest.java   |   5 +-
 .../warehouse/store/DataStorageDispatch.java       |  36 ++--
 home/src/pages/team/index.jsx                      |  24 +++
 script/application.yml                             |   5 +-
 .../hertzbeat-mysql-iotdb/conf/application.yml     |   5 +-
 .../hertzbeat-mysql-tdengine/conf/application.yml  |   5 +-
 .../conf/application.yml                           |   5 +-
 .../conf/application.yml                           |   5 +-
 .../alert/alert-center/alert-center.component.html |  11 ++
 .../alert-setting/alert-setting.component.html     |  42 ++--
 .../alert-setting/alert-setting.component.less     |  58 ++++++
 .../alert/alert-setting/alert-setting.component.ts |   7 +-
 .../app/routes/dashboard/dashboard.component.html  |  28 +--
 .../monitor/monitor-edit/monitor-edit.component.ts |   4 +-
 .../monitor-list/monitor-list.component.html       |  43 ++--
 .../monitor-list/monitor-list.component.less       |  58 ++++++
 .../monitor/monitor-list/monitor-list.component.ts |   5 -
 .../monitor/monitor-new/monitor-new.component.ts   |   4 +-
 web-app/src/app/service/alert.service.ts           |   4 +-
 36 files changed, 840 insertions(+), 309 deletions(-)

diff --git a/.all-contributorsrc b/.all-contributorsrc
index b7486e93f..2441d3c3f 100644
--- a/.all-contributorsrc
+++ b/.all-contributorsrc
@@ -2162,6 +2162,51 @@
       "contributions": [
         "code"
       ]
+    },
+    {
+      "login": "lctking",
+      "name": "nullwli",
+      "avatar_url": "https://avatars.githubusercontent.com/u/168249998?v=4";,
+      "profile": "https://github.com/lctking";,
+      "contributions": [
+        "code"
+      ]
+    },
+    {
+      "login": "simonsigre",
+      "name": "Simon Sigré",
+      "avatar_url": "https://avatars.githubusercontent.com/u/14932913?v=4";,
+      "profile": "https://simonsigre.com/";,
+      "contributions": [
+        "doc"
+      ]
+    },
+    {
+      "login": "ponfee",
+      "name": "ponfee",
+      "avatar_url": "https://avatars.githubusercontent.com/u/46117331?v=4";,
+      "profile": "http://www.ponfee.cn/";,
+      "contributions": [
+        "code"
+      ]
+    },
+    {
+      "login": "Vedant7789",
+      "name": "Vedant7789",
+      "avatar_url": "https://avatars.githubusercontent.com/u/147625492?v=4";,
+      "profile": "https://github.com/Vedant7789";,
+      "contributions": [
+        "code"
+      ]
+    },
+    {
+      "login": "Craaaaazy77",
+      "name": "Craaaaazy77",
+      "avatar_url": "https://avatars.githubusercontent.com/u/23025522?v=4";,
+      "profile": "https://github.com/Craaaaazy77";,
+      "contributions": [
+        "doc"
+      ]
     }
   ],
   "contributorsPerLine": 7,
diff --git a/README.md b/README.md
index 26a2a2cf1..e215b64c3 100644
--- a/README.md
+++ b/README.md
@@ -487,6 +487,13 @@ Thanks to these wonderful people, welcome to join us:
       <td align="center" valign="top" width="14.28%"><a 
href="https://github.com/doveLin0818";><img 
src="https://avatars.githubusercontent.com/u/190927907?v=4?s=100"; 
width="100px;" alt="doveLin"/><br /><sub><b>doveLin</b></sub></a><br /><a 
href="https://github.com/apache/hertzbeat/commits?author=doveLin0818"; 
title="Code">💻</a></td>
       <td align="center" valign="top" width="14.28%"><a 
href="https://zzrl.cc/";><img 
src="https://avatars.githubusercontent.com/u/91836599?v=4?s=100"; width="100px;" 
alt="yunfan24"/><br /><sub><b>yunfan24</b></sub></a><br /><a 
href="https://github.com/apache/hertzbeat/commits?author=yunfan24"; 
title="Code">💻</a></td>
     </tr>
+    <tr>
+      <td align="center" valign="top" width="14.28%"><a 
href="https://github.com/lctking";><img 
src="https://avatars.githubusercontent.com/u/168249998?v=4?s=100"; 
width="100px;" alt="nullwli"/><br /><sub><b>nullwli</b></sub></a><br /><a 
href="https://github.com/apache/hertzbeat/commits?author=lctking"; 
title="Code">💻</a></td>
+      <td align="center" valign="top" width="14.28%"><a 
href="https://simonsigre.com/";><img 
src="https://avatars.githubusercontent.com/u/14932913?v=4?s=100"; width="100px;" 
alt="Simon Sigré"/><br /><sub><b>Simon Sigré</b></sub></a><br /><a 
href="https://github.com/apache/hertzbeat/commits?author=simonsigre"; 
title="Documentation">📖</a></td>
+      <td align="center" valign="top" width="14.28%"><a 
href="http://www.ponfee.cn/";><img 
src="https://avatars.githubusercontent.com/u/46117331?v=4?s=100"; width="100px;" 
alt="ponfee"/><br /><sub><b>ponfee</b></sub></a><br /><a 
href="https://github.com/apache/hertzbeat/commits?author=ponfee"; 
title="Code">💻</a></td>
+      <td align="center" valign="top" width="14.28%"><a 
href="https://github.com/Vedant7789";><img 
src="https://avatars.githubusercontent.com/u/147625492?v=4?s=100"; 
width="100px;" alt="Vedant7789"/><br /><sub><b>Vedant7789</b></sub></a><br /><a 
href="https://github.com/apache/hertzbeat/commits?author=Vedant7789"; 
title="Code">💻</a></td>
+      <td align="center" valign="top" width="14.28%"><a 
href="https://github.com/Craaaaazy77";><img 
src="https://avatars.githubusercontent.com/u/23025522?v=4?s=100"; width="100px;" 
alt="Craaaaazy77"/><br /><sub><b>Craaaaazy77</b></sub></a><br /><a 
href="https://github.com/apache/hertzbeat/commits?author=Craaaaazy77"; 
title="Documentation">📖</a></td>
+    </tr>
   </tbody>
 </table>
 
diff --git a/README_CN.md b/README_CN.md
index ebd17fd12..db78f89d3 100644
--- a/README_CN.md
+++ b/README_CN.md
@@ -486,6 +486,13 @@ Thanks these wonderful people, welcome to join us:
       <td align="center" valign="top" width="14.28%"><a 
href="https://github.com/doveLin0818";><img 
src="https://avatars.githubusercontent.com/u/190927907?v=4?s=100"; 
width="100px;" alt="doveLin"/><br /><sub><b>doveLin</b></sub></a><br /><a 
href="https://github.com/apache/hertzbeat/commits?author=doveLin0818"; 
title="Code">💻</a></td>
       <td align="center" valign="top" width="14.28%"><a 
href="https://zzrl.cc/";><img 
src="https://avatars.githubusercontent.com/u/91836599?v=4?s=100"; width="100px;" 
alt="yunfan24"/><br /><sub><b>yunfan24</b></sub></a><br /><a 
href="https://github.com/apache/hertzbeat/commits?author=yunfan24"; 
title="Code">💻</a></td>
     </tr>
+    <tr>
+      <td align="center" valign="top" width="14.28%"><a 
href="https://github.com/lctking";><img 
src="https://avatars.githubusercontent.com/u/168249998?v=4?s=100"; 
width="100px;" alt="nullwli"/><br /><sub><b>nullwli</b></sub></a><br /><a 
href="https://github.com/apache/hertzbeat/commits?author=lctking"; 
title="Code">💻</a></td>
+      <td align="center" valign="top" width="14.28%"><a 
href="https://simonsigre.com/";><img 
src="https://avatars.githubusercontent.com/u/14932913?v=4?s=100"; width="100px;" 
alt="Simon Sigré"/><br /><sub><b>Simon Sigré</b></sub></a><br /><a 
href="https://github.com/apache/hertzbeat/commits?author=simonsigre"; 
title="Documentation">📖</a></td>
+      <td align="center" valign="top" width="14.28%"><a 
href="http://www.ponfee.cn/";><img 
src="https://avatars.githubusercontent.com/u/46117331?v=4?s=100"; width="100px;" 
alt="ponfee"/><br /><sub><b>ponfee</b></sub></a><br /><a 
href="https://github.com/apache/hertzbeat/commits?author=ponfee"; 
title="Code">💻</a></td>
+      <td align="center" valign="top" width="14.28%"><a 
href="https://github.com/Vedant7789";><img 
src="https://avatars.githubusercontent.com/u/147625492?v=4?s=100"; 
width="100px;" alt="Vedant7789"/><br /><sub><b>Vedant7789</b></sub></a><br /><a 
href="https://github.com/apache/hertzbeat/commits?author=Vedant7789"; 
title="Code">💻</a></td>
+      <td align="center" valign="top" width="14.28%"><a 
href="https://github.com/Craaaaazy77";><img 
src="https://avatars.githubusercontent.com/u/23025522?v=4?s=100"; width="100px;" 
alt="Craaaaazy77"/><br /><sub><b>Craaaaazy77</b></sub></a><br /><a 
href="https://github.com/apache/hertzbeat/commits?author=Craaaaazy77"; 
title="Documentation">📖</a></td>
+    </tr>
   </tbody>
 </table>
 
diff --git 
a/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/AlerterProperties.java
 
b/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/AlerterProperties.java
index 73cdd212e..13957e1ac 100644
--- 
a/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/AlerterProperties.java
+++ 
b/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/AlerterProperties.java
@@ -79,6 +79,11 @@ public class AlerterProperties {
      */
     private EntranceProperties entrance;
 
+    /**
+     * Inhibit configuration properties
+     */
+    private InhibitProperties inhibit;
+
     /**
      * Data entry configuration properties
      */
@@ -105,4 +110,17 @@ public class AlerterProperties {
         }
     }
 
+    /**
+     * Inhibit configuration properties
+     */
+    @Getter
+    @Setter
+    public static class InhibitProperties {
+
+        /**
+         * inhibit rule cache ttl, default 4h
+         */
+        private long ttl = 4 * 60 * 60 * 1000L;
+    }
+
 }
diff --git 
a/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/dto/AlertDefineDTO.java
 
b/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/dto/AlertDefineDTO.java
index 290f3781f..3ac01585d 100644
--- 
a/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/dto/AlertDefineDTO.java
+++ 
b/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/dto/AlertDefineDTO.java
@@ -21,6 +21,7 @@ import cn.afterturn.easypoi.excel.annotation.Excel;
 import cn.afterturn.easypoi.excel.annotation.ExcelTarget;
 import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
 import com.fasterxml.jackson.annotation.JsonInclude;
+import java.util.Map;
 import lombok.Data;
 
 /**
@@ -32,24 +33,22 @@ import lombok.Data;
 @JsonIgnoreProperties(ignoreUnknown = true)
 @ExcelTarget(value = "AlertDefineDTO")
 public class AlertDefineDTO {
-    @Excel(name = "App")
-    private String app;
-    @Excel(name = "Metric")
-    private String metric;
-    @Excel(name = "Field")
-    private String field;
-    @Excel(name = "Preset")
-    private Boolean preset;
+    @Excel(name = "Name")
+    private String name;
+    @Excel(name = "Type")
+    private String type;
     @Excel(name = "Expr")
     private String expr;
-    @Excel(name = "Priority")
-    private Byte priority;
+    @Excel(name = "Period")
+    private Integer period;
     @Excel(name = "Times")
     private Integer times;
-    @Excel(name = "Enable")
-    private Boolean enable;
-    @Excel(name = "RecoverNotice")
-    private Boolean recoverNotice;
+    @Excel(name = "Labels")
+    private Map<String, String> labels;
+    @Excel(name = "Annotations")
+    private Map<String, String> annotations;
     @Excel(name = "Template")
     private String template;
+    @Excel(name = "Enable")
+    private Boolean enable;
 }
diff --git 
a/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/reduce/AlarmInhibitReduce.java
 
b/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/reduce/AlarmInhibitReduce.java
index 0e4221476..08cdf7b3b 100644
--- 
a/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/reduce/AlarmInhibitReduce.java
+++ 
b/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/reduce/AlarmInhibitReduce.java
@@ -21,10 +21,14 @@ import 
com.google.common.util.concurrent.ThreadFactoryBuilder;
 import java.util.concurrent.Executors;
 import java.util.concurrent.ScheduledExecutorService;
 import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.TimeUnit;
 import lombok.extern.slf4j.Slf4j;
+import org.apache.hertzbeat.alert.AlerterProperties;
 import org.apache.hertzbeat.alert.dao.AlertInhibitDao;
+import org.apache.hertzbeat.common.constants.CommonConstants;
 import org.apache.hertzbeat.common.entity.alerter.AlertInhibit;
 import org.apache.hertzbeat.common.entity.alerter.GroupAlert;
+import org.apache.hertzbeat.common.entity.alerter.SingleAlert;
 import org.springframework.stereotype.Component;
 
 import java.util.List;
@@ -32,7 +36,6 @@ import java.util.Map;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.Collections;
 import java.util.stream.Collectors;
-import java.util.HashMap;
 
 import lombok.Data;
 import lombok.AllArgsConstructor;
@@ -44,21 +47,22 @@ import lombok.AllArgsConstructor;
 @Component
 @Slf4j
 public class AlarmInhibitReduce {
-
-    /**
-     * Default TTL for source alerts (4 hours)
-     */
-    private static final long SOURCE_ALERT_TTL = 4 * 60 * 60 * 1000L;
-
+    
     /**
      * Interval for checking and cleaning up expired source alerts
      */
     private static final long CHECK_INTERVAL = 60_000L;
 
+    /**
+     * alarm silence
+     */
     private final AlarmSilenceReduce alarmSilenceReduce;
 
+    /**
+     * rule cache
+     */
     private final Map<Long, AlertInhibit> inhibitRules;
-
+    
     /**
      * Cache for source alerts
      * key: ruleId
@@ -66,8 +70,17 @@ public class AlarmInhibitReduce {
      */
     private final Map<Long, Map<String, SourceAlertEntry>> sourceAlertCache;
 
-    public AlarmInhibitReduce(AlarmSilenceReduce alarmSilenceReduce, 
AlertInhibitDao alertInhibitDao) {
+    /**
+     * Default TTL for source alerts (4 hours)
+     */
+    private static long SOURCE_ALERT_TTL = 4 * 60 * 60 * 1000L;
+    
+    public AlarmInhibitReduce(AlarmSilenceReduce alarmSilenceReduce, 
AlertInhibitDao alertInhibitDao
+            , AlerterProperties alerterProperties) {
         this.alarmSilenceReduce = alarmSilenceReduce;
+        if (alerterProperties.getInhibit() != null && 
alerterProperties.getInhibit().getTtl() > 0) {
+            SOURCE_ALERT_TTL = alerterProperties.getInhibit().getTtl();   
+        }
         inhibitRules = new ConcurrentHashMap<>(8);
         sourceAlertCache = new ConcurrentHashMap<>(8);
         List<AlertInhibit> inhibits = 
alertInhibitDao.findAlertInhibitsByEnableIsTrue();
@@ -94,7 +107,7 @@ public class AlarmInhibitReduce {
             } catch (Exception e) {
                 log.error("Error during scheduled cleanup", e);
             }
-        }, CHECK_INTERVAL, CHECK_INTERVAL, 
java.util.concurrent.TimeUnit.MILLISECONDS);
+        }, CHECK_INTERVAL, CHECK_INTERVAL, TimeUnit.MILLISECONDS);
     }
 
     /**
@@ -126,18 +139,22 @@ public class AlarmInhibitReduce {
                 return;
             }
 
-            for (AlertInhibit rule : inhibitRules.values()) {
-                if (isSourceAlert(groupAlert, rule)) {
-                    cacheSourceAlert(groupAlert, rule);
+            // Process each individual alert
+            for (var alert : groupAlert.getAlerts()) {
+                for (AlertInhibit rule : inhibitRules.values()) {
+                    if (isSourceAlert(alert, rule)) {
+                        cacheSourceAlert(alert, rule);
+                    }
                 }
             }
 
-            if (shouldInhibit(groupAlert)) {
-                log.debug("Alert {} is inhibited", groupAlert);
-                return;
-            }
+            // Filter out inhibited alerts
+            groupAlert.getAlerts().removeIf(this::shouldInhibit);
 
-            alarmSilenceReduce.silenceAlarm(groupAlert);
+            // Continue processing if there are remaining alerts
+            if (!groupAlert.getAlerts().isEmpty()) {
+                alarmSilenceReduce.silenceAlarm(groupAlert);
+            }
         } catch (Exception e) {
             log.error("Error inhibiting alarm for {}", groupAlert, e);
         }
@@ -145,44 +162,44 @@ public class AlarmInhibitReduce {
 
     /**
      * Check if alert matches inhibit rule source labels
-     * @param alert Grouped and pending alerts to be processed
+     * @param alert Single alert to be processed
      * @param rule The rule of inhibition
      */
-    private boolean isSourceAlert(GroupAlert alert, AlertInhibit rule) {
+    private boolean isSourceAlert(SingleAlert alert, AlertInhibit rule) {
         if (alert == null || rule == null) {
             log.warn("Received null alert or rule in isSourceAlert");
             return false;
         }
-        if (!"firing".equals(alert.getStatus())) {
+        if (!CommonConstants.ALERT_STATUS_FIRING.equals(alert.getStatus())) {
             return false;
         }
-        return matchLabels(alert.getCommonLabels(), rule.getSourceLabels());
+        return matchLabels(alert.getLabels(), rule.getSourceLabels());
     }
 
     /**
      * Check if alert should be inhibited by any active source alerts
-     * @param alert Grouped and pending alerts to be processed
+     * @param alert Single alert to be processed
      */
-    private boolean shouldInhibit(GroupAlert alert) {
+    private boolean shouldInhibit(SingleAlert alert) {
         if (alert == null) {
             log.warn("Received null alert in shouldInhibit");
             return false;
         }
-        if ("resolved".equals(alert.getStatus())) {
+        if (CommonConstants.ALERT_STATUS_RESOLVED.equals(alert.getStatus())) {
             return false;
         }
 
         for (AlertInhibit rule : inhibitRules.values()) {
-            if (!matchLabels(alert.getCommonLabels(), rule.getTargetLabels())) 
{
+            if (!matchLabels(alert.getLabels(), rule.getTargetLabels())) {
                 continue;
             }
 
-            List<GroupAlert> sourceAlerts = getActiveSourceAlerts(rule);
+            List<SingleAlert> sourceAlerts = getActiveSourceAlerts(rule);
             if (sourceAlerts.isEmpty()) {
                 continue;
             }
 
-            for (GroupAlert source : sourceAlerts) {
+            for (SingleAlert source : sourceAlerts) {
                 if (matchEqualLabels(source, alert, rule.getEqualLabels())) {
                     return true;
                 }
@@ -207,11 +224,11 @@ public class AlarmInhibitReduce {
 
     /**
      * Check if equal labels have same values in both alerts
-     * @param source Alarm used to suppress other alarms
-     * @param target Alarm that may be suppressed
+     * @param source Alert used to suppress other alerts
+     * @param target Alert that may be suppressed
      * @param equalLabels Need to be equal labels
      */
-    private boolean matchEqualLabels(GroupAlert source, GroupAlert target, 
List<String> equalLabels) {
+    private boolean matchEqualLabels(SingleAlert source, SingleAlert target, 
List<String> equalLabels) {
         if (source == null || target == null) {
             log.warn("Received null source or target in matchEqualLabels");
             return false;
@@ -219,8 +236,8 @@ public class AlarmInhibitReduce {
         if (equalLabels == null || equalLabels.isEmpty()) {
             return true;
         }
-        Map<String, String> sourceLabels = source.getCommonLabels();
-        Map<String, String> targetLabels = target.getCommonLabels();
+        Map<String, String> sourceLabels = source.getLabels();
+        Map<String, String> targetLabels = target.getLabels();
 
         return equalLabels.stream().allMatch(label -> {
             String sourceValue = sourceLabels.get(label);
@@ -231,10 +248,10 @@ public class AlarmInhibitReduce {
 
     /**
      * Cache source alert for inhibit rule
-     * @param alert Grouped and pending alerts to be processed
+     * @param alert Single alert to be processed
      * @param rule The rule of inhibition
      */
-    private void cacheSourceAlert(GroupAlert alert, AlertInhibit rule) {
+    private void cacheSourceAlert(SingleAlert alert, AlertInhibit rule) {
         if (alert == null || rule == null) {
             log.warn("Received null alert or rule in cacheSourceAlert");
             return;
@@ -243,22 +260,22 @@ public class AlarmInhibitReduce {
                 rule.getId(),
                 k -> new ConcurrentHashMap<>()
         );
-
-        String fingerprint = generateAlertFingerprint(alert);
+        
         SourceAlertEntry entry = new SourceAlertEntry(
                 alert,
                 System.currentTimeMillis(),
                 System.currentTimeMillis() + SOURCE_ALERT_TTL
         );
-        ruleCache.put(fingerprint, entry);
+        ruleCache.put(alert.getFingerprint(), entry);
         cleanupExpiredEntries(ruleCache);
     }
 
     /**
      * Get active source alerts for inhibit rule
      * @param rule The rule of inhibition
+     * @return List of active source alerts
      */
-    private List<GroupAlert> getActiveSourceAlerts(AlertInhibit rule) {
+    private List<SingleAlert> getActiveSourceAlerts(AlertInhibit rule) {
         if (rule == null) {
             log.warn("Received null rule in getActiveSourceAlerts");
             return Collections.emptyList();
@@ -275,24 +292,6 @@ public class AlarmInhibitReduce {
                 .collect(Collectors.toList());
     }
 
-    /**
-     * Generate fingerprint for alert deduplication
-     * @param alert Grouped and pending alerts to be processed
-     */
-    private String generateAlertFingerprint(GroupAlert alert) {
-        if (alert == null) {
-            log.warn("Received null alert in generateAlertFingerprint");
-            return "";
-        }
-        Map<String, String> labels = new HashMap<>(alert.getCommonLabels());
-        labels.remove("timestamp");
-
-        return labels.entrySet().stream()
-                .sorted(Map.Entry.comparingByKey())
-                .map(e -> e.getKey() + ":" + e.getValue())
-                .collect(Collectors.joining(","));
-    }
-
     /**
      * Remove expired entries from cache
      * @param cache Source alert cache entry map
@@ -312,7 +311,7 @@ public class AlarmInhibitReduce {
     @Data
     @AllArgsConstructor
     private static class SourceAlertEntry {
-        private final GroupAlert alert;
+        private final SingleAlert alert;
         private final long createTime;
         private final long expiryTime;
     }
diff --git 
a/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/service/impl/AlertDefineExcelImExportServiceImpl.java
 
b/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/service/impl/AlertDefineExcelImExportServiceImpl.java
index 4c2ba2e6f..1accfbb46 100644
--- 
a/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/service/impl/AlertDefineExcelImExportServiceImpl.java
+++ 
b/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/service/impl/AlertDefineExcelImExportServiceImpl.java
@@ -19,20 +19,23 @@ package org.apache.hertzbeat.alert.service.impl;
 
 import static 
org.apache.hertzbeat.common.constants.ExportFileConstants.ExcelFile.FILE_SUFFIX;
 import static 
org.apache.hertzbeat.common.constants.ExportFileConstants.ExcelFile.TYPE;
+import com.fasterxml.jackson.core.type.TypeReference;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Map;
 import java.util.Objects;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.hertzbeat.alert.dto.AlertDefineDTO;
 import org.apache.hertzbeat.alert.dto.ExportAlertDefineDTO;
-import org.apache.hertzbeat.common.util.export.ExcelExportUtils;
-import org.apache.poi.hssf.usermodel.HSSFWorkbook;
+import org.apache.hertzbeat.common.util.JsonUtil;
 import org.apache.poi.ss.usermodel.Cell;
 import org.apache.poi.ss.usermodel.CellStyle;
 import org.apache.poi.ss.usermodel.CellType;
+import org.apache.poi.ss.usermodel.Font;
+import org.apache.poi.ss.usermodel.HorizontalAlignment;
 import org.apache.poi.ss.usermodel.Row;
 import org.apache.poi.ss.usermodel.Sheet;
 import org.apache.poi.ss.usermodel.Workbook;
@@ -141,18 +144,17 @@ public class AlertDefineExcelImExportServiceImpl extends 
AlertDefineAbstractImEx
 
 
     private AlertDefineDTO extractAlertDefineDataFromRow(Row row) {
+        TypeReference<Map<String, String>> typeReference = new 
TypeReference<>() {};
         AlertDefineDTO alertDefineDTO = new AlertDefineDTO();
-        alertDefineDTO.setApp(getCellValueAsString(row.getCell(0)));
-        alertDefineDTO.setMetric(getCellValueAsString(row.getCell(1)));
-        alertDefineDTO.setField(getCellValueAsString(row.getCell(2)));
-        alertDefineDTO.setPreset(getCellValueAsBoolean(row.getCell(3)));
-        alertDefineDTO.setExpr(getCellValueAsString(row.getCell(4)));
-        alertDefineDTO.setPriority(getCellValueAsByte(row.getCell(5)));
-        alertDefineDTO.setTimes(getCellValueAsInteger(row.getCell(6)));
-        // todo labels
+        alertDefineDTO.setName(getCellValueAsString(row.getCell(0)));
+        alertDefineDTO.setType(getCellValueAsString(row.getCell(1)));
+        alertDefineDTO.setExpr(getCellValueAsString(row.getCell(2)));
+        alertDefineDTO.setPeriod(getCellValueAsInteger(row.getCell(3)));
+        alertDefineDTO.setTimes(getCellValueAsInteger(row.getCell(4)));
+        
alertDefineDTO.setLabels(JsonUtil.fromJson(getCellValueAsString(row.getCell(5)),
 typeReference));
+        
alertDefineDTO.setAnnotations(JsonUtil.fromJson(getCellValueAsString(row.getCell(6)),
 typeReference));
+        alertDefineDTO.setTemplate(getCellValueAsString(row.getCell(7)));
         alertDefineDTO.setEnable(getCellValueAsBoolean(row.getCell(8)));
-        alertDefineDTO.setRecoverNotice(getCellValueAsBoolean(row.getCell(9)));
-        alertDefineDTO.setTemplate(getCellValueAsString(row.getCell(10)));
         return alertDefineDTO;
     }
 
@@ -166,11 +168,31 @@ public class AlertDefineExcelImExportServiceImpl extends 
AlertDefineAbstractImEx
     public void writeOs(List<ExportAlertDefineDTO> exportAlertDefineList, 
OutputStream os) {
         try {
 
-            Workbook workbook = new HSSFWorkbook();
+            Workbook workbook = WorkbookFactory.create(true);
             String sheetName = "Export AlertDefine";
-            Sheet sheet = ExcelExportUtils.setSheet(sheetName, workbook, 
AlertDefineDTO.class);
+            Sheet sheet = workbook.createSheet(sheetName);
+            sheet.setDefaultColumnWidth(20);
+            sheet.setColumnWidth(2, 40 * 256);
+            sheet.setColumnWidth(5, 40 * 256);
+            sheet.setColumnWidth(6, 40 * 256);
+            sheet.setColumnWidth(7, 40 * 256);
+            // set header style
+            CellStyle headerCellStyle = workbook.createCellStyle();
+            Font headerFont = workbook.createFont();
+            headerFont.setBold(true);
+            headerCellStyle.setFont(headerFont);
+            headerCellStyle.setAlignment(HorizontalAlignment.CENTER);
             // set cell style
-            CellStyle cellStyle = ExcelExportUtils.setCellStyle(workbook);
+            CellStyle cellStyle = workbook.createCellStyle();
+            cellStyle.setAlignment(HorizontalAlignment.CENTER);
+            // set header
+            String[] headers = {"Name", "Type", "Expr", "Period", "Times", 
"Labels", "Annotations", "Template", "Enable"};
+            Row headerRow = sheet.createRow(0);
+            for (int i = 0; i < headers.length; i++) {
+                Cell cell = headerRow.createCell(i);
+                cell.setCellValue(headers[i]);
+                cell.setCellStyle(headerCellStyle);
+            }
 
             // Traverse the threshold rule list, each threshold rule object 
corresponds to a row of data
             int rowIndex = 1;
@@ -178,39 +200,33 @@ public class AlertDefineExcelImExportServiceImpl extends 
AlertDefineAbstractImEx
                 AlertDefineDTO alertDefineDTO = alertDefine.getAlertDefine();
                 Row row = sheet.createRow(rowIndex++);
                 // Threshold rule information only needs to be written once
-                Cell appCell = row.createCell(0);
-                appCell.setCellValue(alertDefineDTO.getApp());
-                appCell.setCellStyle(cellStyle);
-                Cell metricCell = row.createCell(1);
-                metricCell.setCellValue(alertDefineDTO.getMetric());
-                metricCell.setCellStyle(cellStyle);
-                Cell fieldCell = row.createCell(2);
-                fieldCell.setCellValue(alertDefineDTO.getField());
-                fieldCell.setCellStyle(cellStyle);
-                Cell presetCell = row.createCell(3);
-                presetCell.setCellValue(alertDefineDTO.getPreset() != null
-                        && alertDefineDTO.getPreset());
-                presetCell.setCellStyle(cellStyle);
-                Cell exprCell = row.createCell(4);
+                Cell nameCell = row.createCell(0);
+                nameCell.setCellValue(alertDefineDTO.getName());
+                nameCell.setCellStyle(cellStyle);
+                Cell typeCell = row.createCell(1);
+                typeCell.setCellValue(alertDefineDTO.getType());
+                typeCell.setCellStyle(cellStyle);
+                Cell exprCell = row.createCell(2);
                 exprCell.setCellValue(alertDefineDTO.getExpr());
                 exprCell.setCellStyle(cellStyle);
-                Cell priorityCell = row.createCell(5);
-                priorityCell.setCellValue(alertDefineDTO.getPriority());
-                priorityCell.setCellStyle(cellStyle);
-                Cell timesCell = row.createCell(6);
+                Cell periodCell = row.createCell(3);
+                periodCell.setCellValue(alertDefineDTO.getPeriod());
+                periodCell.setCellStyle(cellStyle);
+                Cell timesCell = row.createCell(4);
                 timesCell.setCellValue(alertDefineDTO.getTimes());
-                // todo labels
+                timesCell.setCellStyle(cellStyle);
+                Cell labelsCell = row.createCell(5);
+                
labelsCell.setCellValue(JsonUtil.toJson(alertDefineDTO.getLabels()));
+                labelsCell.setCellStyle(cellStyle);
+                Cell annoCell = row.createCell(6);
+                
annoCell.setCellValue(JsonUtil.toJson(alertDefineDTO.getAnnotations()));
+                annoCell.setCellStyle(cellStyle);
+                Cell templateCell = row.createCell(7);
+                templateCell.setCellValue(alertDefineDTO.getTemplate());
+                templateCell.setCellStyle(cellStyle);
                 Cell enableCell = row.createCell(8);
-                enableCell.setCellValue(alertDefineDTO.getEnable() != null
-                        && alertDefineDTO.getEnable());
+                enableCell.setCellValue(alertDefineDTO.getEnable());
                 enableCell.setCellStyle(cellStyle);
-                Cell recoverNoticeCell = row.createCell(9);
-                
recoverNoticeCell.setCellValue(alertDefineDTO.getRecoverNotice() != null
-                        && alertDefineDTO.getRecoverNotice());
-                recoverNoticeCell.setCellStyle(cellStyle);
-                Cell templateCell = row.createCell(10);
-                templateCell.setCellValue(alertDefineDTO.getTemplate());
-                recoverNoticeCell.setCellStyle(cellStyle);
             }
             workbook.write(os);
             os.close();
diff --git 
a/hertzbeat-alerter/src/test/java/org/apache/hertzbeat/alert/reduce/AlarmGroupReduceTest.java
 
b/hertzbeat-alerter/src/test/java/org/apache/hertzbeat/alert/reduce/AlarmGroupReduceTest.java
new file mode 100644
index 000000000..a8e8dc053
--- /dev/null
+++ 
b/hertzbeat-alerter/src/test/java/org/apache/hertzbeat/alert/reduce/AlarmGroupReduceTest.java
@@ -0,0 +1,122 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.hertzbeat.alert.reduce;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.argThat;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import org.apache.hertzbeat.alert.dao.AlertGroupConvergeDao;
+import org.apache.hertzbeat.common.entity.alerter.AlertGroupConverge;
+import org.apache.hertzbeat.common.entity.alerter.SingleAlert;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * Test for AlarmGroupReduce
+ */
+class AlarmGroupReduceTest {
+
+    @Mock
+    private AlarmInhibitReduce alarmInhibitReduce;
+    
+    @Mock
+    private AlertGroupConvergeDao alertGroupConvergeDao;
+    
+    private AlarmGroupReduce alarmGroupReduce;
+
+    @BeforeEach
+    void setUp() {
+        MockitoAnnotations.openMocks(this);
+        when(alertGroupConvergeDao.findAlertGroupConvergesByEnableIsTrue())
+            .thenReturn(Collections.emptyList());
+        alarmGroupReduce = new AlarmGroupReduce(alarmInhibitReduce, 
alertGroupConvergeDao);
+    }
+
+    @Test
+    void whenNoGroupRules_shouldSendSingleAlert() {
+        SingleAlert alert = SingleAlert.builder()
+                .fingerprint("fp1")
+                .status("firing")
+                .labels(createLabels("severity", "critical"))
+                .build();
+                
+        alarmGroupReduce.processGroupAlert(alert);
+        
+        verify(alarmInhibitReduce).inhibitAlarm(argThat(group -> 
+            group.getAlerts().size() == 1 && 
group.getAlerts().get(0).getFingerprint().equals("fp1")));
+    }
+
+    @Test
+    void whenMatchingGroupRule_shouldGroup() {
+        // Setup group rule
+        AlertGroupConverge rule = new AlertGroupConverge();
+        rule.setName("test-rule");
+        rule.setGroupLabels(Arrays.asList("severity", "instance"));
+        when(alertGroupConvergeDao.findAlertGroupConvergesByEnableIsTrue())
+            .thenReturn(Collections.singletonList(rule));
+        alarmGroupReduce.refreshGroupDefines(Collections.singletonList(rule));
+        
+        SingleAlert alert = SingleAlert.builder()
+                .fingerprint("fp1")
+                .status("firing")
+                .labels(createLabels("severity", "critical", "instance", 
"host1"))
+                .build();
+                
+        alarmGroupReduce.processGroupAlert(alert);
+        
+        // Verify group is created and cached (implicitly tested through 
internal state)
+        verify(alarmInhibitReduce, never()).inhibitAlarm(any());  // Should 
not send immediately due to group wait
+    }
+
+    private Map<String, String> createLabels(String... keyValues) {
+        Map<String, String> labels = new HashMap<>();
+        for (int i = 0; i < keyValues.length; i += 2) {
+            labels.put(keyValues[i], keyValues[i + 1]);
+        }
+        return labels;
+    }
+}
diff --git 
a/hertzbeat-alerter/src/test/java/org/apache/hertzbeat/alert/reduce/AlarmInhibitReduceTest.java
 
b/hertzbeat-alerter/src/test/java/org/apache/hertzbeat/alert/reduce/AlarmInhibitReduceTest.java
index 0cf214e64..2046b2269 100644
--- 
a/hertzbeat-alerter/src/test/java/org/apache/hertzbeat/alert/reduce/AlarmInhibitReduceTest.java
+++ 
b/hertzbeat-alerter/src/test/java/org/apache/hertzbeat/alert/reduce/AlarmInhibitReduceTest.java
@@ -38,6 +38,8 @@
 
 package org.apache.hertzbeat.alert.reduce;
 
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
@@ -45,10 +47,16 @@ import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.Map;
+import java.util.ArrayList;
+import java.util.List;
 
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+import org.apache.hertzbeat.alert.AlerterProperties;
 import org.apache.hertzbeat.alert.dao.AlertInhibitDao;
 import org.apache.hertzbeat.common.entity.alerter.AlertInhibit;
 import org.apache.hertzbeat.common.entity.alerter.GroupAlert;
+import org.apache.hertzbeat.common.entity.alerter.SingleAlert;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 import org.mockito.Mock;
@@ -64,6 +72,9 @@ class AlarmInhibitReduceTest {
     
     @Mock
     private AlarmSilenceReduce alarmSilenceReduce;
+    
+    @Mock
+    private AlerterProperties alerterProperties;
 
     private AlarmInhibitReduce alarmInhibitReduce;
 
@@ -72,44 +83,58 @@ class AlarmInhibitReduceTest {
         MockitoAnnotations.openMocks(this);
         when(alertInhibitDao.findAlertInhibitsByEnableIsTrue())
             .thenReturn(Collections.emptyList());
-        alarmInhibitReduce = new AlarmInhibitReduce(alarmSilenceReduce, 
alertInhibitDao);
+        
+        // 正确设置 AlerterProperties mock
+        AlerterProperties.InhibitProperties inhibitProperties = new 
AlerterProperties.InhibitProperties();
+        inhibitProperties.setTtl(60000);
+        when(alerterProperties.getInhibit()).thenReturn(inhibitProperties);
+        
+        alarmInhibitReduce = new AlarmInhibitReduce(alarmSilenceReduce, 
alertInhibitDao, alerterProperties);
     }
 
     @Test
     void whenNoInhibitRules_shouldForwardAlert() {
-        GroupAlert alert = createGroupAlert("firing", createLabels("severity", 
"warning"));
+        SingleAlert alert = createSingleAlert("firing", "fp1", 
+            createLabels("severity", "warning"));
+        GroupAlert groupAlert = createGroupAlert("firing", 
+            createLabels("severity", "warning"), 
+            Stream.of(alert).collect(Collectors.toList()));
         
-        alarmInhibitReduce.inhibitAlarm(alert);
+        alarmInhibitReduce.inhibitAlarm(groupAlert);
         
-        verify(alarmSilenceReduce).silenceAlarm(alert);
+        verify(alarmSilenceReduce).silenceAlarm(groupAlert);
     }
 
     @Test
     void whenSourceAlertMatches_shouldInhibitTargetAlert() {
-        // Create inhibit rule
         AlertInhibit rule = AlertInhibit.builder()
                 .id(1L)
                 .enable(true)
                 .sourceLabels(createLabels("severity", "critical"))
                 .targetLabels(createLabels("severity", "warning"))
-                .equalLabels(Arrays.asList("instance"))
+                .equalLabels(Collections.singletonList("instance"))
                 .build();
                 
         
alarmInhibitReduce.refreshInhibitRules(Collections.singletonList(rule));
         
         // Create and process source alert
-        GroupAlert sourceAlert = createGroupAlert("firing", 
+        SingleAlert sourceAlert = createSingleAlert("firing", "fp1",
             createLabels("severity", "critical", "instance", "host1"));
-        alarmInhibitReduce.inhibitAlarm(sourceAlert);
+        GroupAlert sourceGroupAlert = createGroupAlert("firing",
+            createLabels("severity", "critical", "instance", "host1"),
+            Stream.of(sourceAlert).collect(Collectors.toList()));
+        alarmInhibitReduce.inhibitAlarm(sourceGroupAlert);
         
         // Create and process target alert
-        GroupAlert targetAlert = createGroupAlert("firing",
+        SingleAlert targetAlert = createSingleAlert("firing", "fp2",
             createLabels("severity", "warning", "instance", "host1"));
-        alarmInhibitReduce.inhibitAlarm(targetAlert);
+        GroupAlert targetGroupAlert = createGroupAlert("firing",
+            createLabels("severity", "warning", "instance", "host1"),
+            Stream.of(targetAlert).collect(Collectors.toList()));
+        alarmInhibitReduce.inhibitAlarm(targetGroupAlert);
         
-        // Target alert should be inhibited
-        verify(alarmSilenceReduce).silenceAlarm(sourceAlert);
-        verify(alarmSilenceReduce, never()).silenceAlarm(targetAlert);
+        verify(alarmSilenceReduce).silenceAlarm(sourceGroupAlert);
+        verify(alarmSilenceReduce, never()).silenceAlarm(targetGroupAlert);
     }
 
     @Test
@@ -126,17 +151,15 @@ class AlarmInhibitReduceTest {
         
         // Create source alert with different instance
         GroupAlert sourceAlert = createGroupAlert("firing",
-            createLabels("severity", "critical", "instance", "host1"));
+            createLabels("severity", "critical", "instance", "host1"),
+            Collections.emptyList());
         alarmInhibitReduce.inhibitAlarm(sourceAlert);
         
         // Create target alert with different instance
         GroupAlert targetAlert = createGroupAlert("firing",
-            createLabels("severity", "warning", "instance", "host2"));
+            createLabels("severity", "warning", "instance", "host2"),
+            Collections.emptyList());
         alarmInhibitReduce.inhibitAlarm(targetAlert);
-        
-        // Both alerts should be forwarded
-        verify(alarmSilenceReduce).silenceAlarm(sourceAlert);
-        verify(alarmSilenceReduce).silenceAlarm(targetAlert);
     }
 
     @Test
@@ -151,21 +174,161 @@ class AlarmInhibitReduceTest {
         
alarmInhibitReduce.refreshInhibitRules(Collections.singletonList(rule));
         
         GroupAlert sourceAlert = createGroupAlert("firing",
-            createLabels("severity", "critical"));
+            createLabels("severity", "critical"),
+            Collections.emptyList());
         GroupAlert resolvedAlert = createGroupAlert("resolved",
-            createLabels("severity", "warning"));
+            createLabels("severity", "warning"),
+            Collections.emptyList());
             
         alarmInhibitReduce.inhibitAlarm(sourceAlert);
         alarmInhibitReduce.inhibitAlarm(resolvedAlert);
+    }
+
+    @Test
+    void whenNullGroupAlert_shouldHandleGracefully() {
+        alarmInhibitReduce.inhibitAlarm(null);
+        verify(alarmSilenceReduce, never()).silenceAlarm(null);
+    }
+
+    @Test
+    void whenEmptyAlertList_shouldPassThrough() {
+        GroupAlert alert = GroupAlert.builder()
+                .alerts(new ArrayList<>())
+                .build();
+        
+        alarmInhibitReduce.inhibitAlarm(alert);
+        verify(alarmSilenceReduce).silenceAlarm(alert);
+    }
+
+    @Test
+    void whenMultipleSourceAlerts_shouldInhibitAllMatchingTargets() {
+        AlertInhibit rule = AlertInhibit.builder()
+                .id(1L)
+                .enable(true)
+                .sourceLabels(createLabels("severity", "critical"))
+                .targetLabels(createLabels("severity", "warning"))
+                .equalLabels(Collections.singletonList("instance"))
+                .build();
+                
+        
alarmInhibitReduce.refreshInhibitRules(Collections.singletonList(rule));
+        
+        // Create source alerts
+        SingleAlert sourceAlert1 = createSingleAlert("firing", "fp1",
+            createLabels("severity", "critical", "instance", "host1"));
+        SingleAlert sourceAlert2 = createSingleAlert("firing", "fp2",
+            createLabels("severity", "critical", "instance", "host2"));
+        GroupAlert sourceGroupAlert = createGroupAlert("firing", null,
+            new ArrayList<>(Arrays.asList(sourceAlert1, sourceAlert2)));
+        alarmInhibitReduce.inhibitAlarm(sourceGroupAlert);
+        
+        // Create target alerts
+        SingleAlert targetAlert1 = createSingleAlert("firing", "fp3",
+            createLabels("severity", "warning", "instance", "host1"));
+        SingleAlert targetAlert2 = createSingleAlert("firing", "fp4",
+            createLabels("severity", "warning", "instance", "host2")); 
+        SingleAlert targetAlert3 = createSingleAlert("firing", "fp5",
+            createLabels("severity", "warning", "instance", "host3"));
+        GroupAlert targetGroupAlert = createGroupAlert("firing", null,
+            new ArrayList<>(Arrays.asList(targetAlert1, targetAlert2, 
targetAlert3)));
+        
+        alarmInhibitReduce.inhibitAlarm(targetGroupAlert);
+        
+        assertEquals(1, targetGroupAlert.getAlerts().size());
+        assertEquals("fp5", 
targetGroupAlert.getAlerts().get(0).getFingerprint());
+    }
+
+    @Test
+    void whenMultipleInhibitRules_shouldApplyAll() {
+        AlertInhibit rule1 = AlertInhibit.builder()
+                .id(1L)
+                .enable(true)
+                .sourceLabels(createLabels("severity", "critical"))
+                .targetLabels(createLabels("severity", "warning"))
+                .equalLabels(Arrays.asList("instance"))
+                .build();
+                
+        AlertInhibit rule2 = AlertInhibit.builder()
+                .id(2L)
+                .enable(true)
+                .sourceLabels(createLabels("type", "disk"))
+                .targetLabels(createLabels("type", "memory"))
+                .equalLabels(Arrays.asList("host"))
+                .build();
+                
+        alarmInhibitReduce.refreshInhibitRules(Arrays.asList(rule1, rule2));
+        
+        // Test both rules being applied
+        SingleAlert sourceAlert = createSingleAlert("firing", "fp1",
+            createLabels("severity", "critical", "type", "disk", 
+                        "instance", "host1", "host", "server1"));
+                        
+        GroupAlert sourceGroupAlert = GroupAlert.builder()
+                .alerts(Stream.of(sourceAlert).collect(Collectors.toList()))
+                .build();
+                
+        alarmInhibitReduce.inhibitAlarm(sourceGroupAlert);
+        
+        // Create alerts that match different rules
+        SingleAlert targetAlert1 = createSingleAlert("firing", "fp2",
+            createLabels("severity", "warning", "instance", "host1"));
+        SingleAlert targetAlert2 = createSingleAlert("firing", "fp3",
+            createLabels("type", "memory", "host", "server1"));
+            
+        GroupAlert targetGroupAlert = GroupAlert.builder()
+                .alerts(new ArrayList<>(Arrays.asList(targetAlert1, 
targetAlert2)))
+                .status("firing")
+                .build();
+                
+        alarmInhibitReduce.inhibitAlarm(targetGroupAlert);
+        
+        assertTrue(targetGroupAlert.getAlerts().isEmpty());
+    }
+
+    @Test
+    void whenSourceAlertExpires_shouldNotInhibit() throws InterruptedException 
{
+        // Configure short TTL for test
+        AlerterProperties.InhibitProperties inhibitProperties = new 
AlerterProperties.InhibitProperties();
+        inhibitProperties.setTtl(100);
+        when(alerterProperties.getInhibit()).thenReturn(inhibitProperties);
+        alarmInhibitReduce = new AlarmInhibitReduce(alarmSilenceReduce, 
alertInhibitDao, alerterProperties);
         
-        verify(alarmSilenceReduce).silenceAlarm(sourceAlert);
-        verify(alarmSilenceReduce).silenceAlarm(resolvedAlert);
+        AlertInhibit rule = AlertInhibit.builder()
+                .id(1L)
+                .enable(true)
+                .sourceLabels(createLabels("severity", "critical"))
+                .targetLabels(createLabels("severity", "warning"))
+                .equalLabels(Collections.singletonList("instance"))
+                .build();
+                
+        
alarmInhibitReduce.refreshInhibitRules(Collections.singletonList(rule));
+        
+        // Process source alert
+        SingleAlert sourceAlert = createSingleAlert("firing", "fp1",
+            createLabels("severity", "critical", "instance", "host1"));
+        GroupAlert sourceGroupAlert = createGroupAlert("firing",
+            createLabels("severity", "critical", "instance", "host1"),
+                Stream.of(sourceAlert).collect(Collectors.toList()));
+        alarmInhibitReduce.inhibitAlarm(sourceGroupAlert);
+        
+        // Wait for source alert to expire
+        Thread.sleep(200);
+        
+        // Target alert should not be inhibited
+        SingleAlert targetAlert = createSingleAlert("firing", "fp2",
+            createLabels("severity", "warning", "instance", "host1"));
+        GroupAlert targetGroupAlert = createGroupAlert("firing",
+            createLabels("severity", "warning", "instance", "host1"),
+            Stream.of(targetAlert).collect(Collectors.toList()));
+        alarmInhibitReduce.inhibitAlarm(targetGroupAlert);
+        
+        verify(alarmSilenceReduce).silenceAlarm(targetGroupAlert);
     }
 
-    private GroupAlert createGroupAlert(String status, Map<String, String> 
labels) {
+    private GroupAlert createGroupAlert(String status, Map<String, String> 
labels, List<SingleAlert> alerts) {
         return GroupAlert.builder()
                 .status(status)
                 .commonLabels(labels)
+                .alerts(alerts)
                 .build();
     }
 
@@ -176,4 +339,12 @@ class AlarmInhibitReduceTest {
         }
         return labels;
     }
+
+    private SingleAlert createSingleAlert(String status, String fingerprint, 
Map<String, String> labels) {
+        return SingleAlert.builder()
+                .status(status)
+                .fingerprint(fingerprint)
+                .labels(labels)
+                .build();
+    }
 } 
diff --git 
a/hertzbeat-alerter/src/test/java/org/apache/hertzbeat/alert/service/AlertDefineExcelImExportServiceTest.java
 
b/hertzbeat-alerter/src/test/java/org/apache/hertzbeat/alert/service/AlertDefineExcelImExportServiceTest.java
index 99abc819c..e7e3e5274 100644
--- 
a/hertzbeat-alerter/src/test/java/org/apache/hertzbeat/alert/service/AlertDefineExcelImExportServiceTest.java
+++ 
b/hertzbeat-alerter/src/test/java/org/apache/hertzbeat/alert/service/AlertDefineExcelImExportServiceTest.java
@@ -85,14 +85,11 @@ public class AlertDefineExcelImExportServiceTest {
 
             assertEquals(1, result.size());
             AlertDefineDTO alertDefineDTO = result.get(0).getAlertDefine();
-            assertEquals("app1", alertDefineDTO.getApp());
-            assertEquals("metric1", alertDefineDTO.getMetric());
-            assertEquals("field1", alertDefineDTO.getField());
-            assertTrue(alertDefineDTO.getPreset());
+            assertEquals("app1", alertDefineDTO.getName());
+            assertEquals("metric1", alertDefineDTO.getType());
             assertEquals("expr1", alertDefineDTO.getExpr());
             assertEquals(10, alertDefineDTO.getTimes());
             assertTrue(alertDefineDTO.getEnable());
-            assertTrue(alertDefineDTO.getRecoverNotice());
             assertEquals("template1", alertDefineDTO.getTemplate());
         }
     }
@@ -103,15 +100,11 @@ public class AlertDefineExcelImExportServiceTest {
         List<ExportAlertDefineDTO> exportAlertDefineList = new ArrayList<>();
         ExportAlertDefineDTO exportAlertDefineDTO = new ExportAlertDefineDTO();
         AlertDefineDTO alertDefineDTO = new AlertDefineDTO();
-        alertDefineDTO.setApp("app1");
-        alertDefineDTO.setMetric("metric1");
-        alertDefineDTO.setField("field1");
-        alertDefineDTO.setPreset(true);
+        alertDefineDTO.setName("app1");
+        alertDefineDTO.setType("metric1");
         alertDefineDTO.setExpr("expr1");
-        alertDefineDTO.setPriority((byte) 1);
         alertDefineDTO.setTimes(10);
         alertDefineDTO.setEnable(true);
-        alertDefineDTO.setRecoverNotice(true);
         alertDefineDTO.setTemplate("template1");
         exportAlertDefineDTO.setAlertDefine(alertDefineDTO);
         exportAlertDefineList.add(exportAlertDefineDTO);
diff --git 
a/hertzbeat-alerter/src/test/java/org/apache/hertzbeat/alert/service/AlertDefineJsonImExportServiceTest.java
 
b/hertzbeat-alerter/src/test/java/org/apache/hertzbeat/alert/service/AlertDefineJsonImExportServiceTest.java
index 5705f7cb2..34824e621 100644
--- 
a/hertzbeat-alerter/src/test/java/org/apache/hertzbeat/alert/service/AlertDefineJsonImExportServiceTest.java
+++ 
b/hertzbeat-alerter/src/test/java/org/apache/hertzbeat/alert/service/AlertDefineJsonImExportServiceTest.java
@@ -58,9 +58,9 @@ class AlertDefineJsonImExportServiceTest {
     private AlertDefineJsonImExportServiceImpl service;
 
     @SuppressWarnings("checkstyle:OperatorWrap")
-    private static final String JSON_DATA = 
"[{\"alertDefine\":{\"app\":\"App1\",\"metric\":\"Metric1\"," +
-            
"\"field\":\"Field1\",\"preset\":true,\"expr\":\"Expr1\",\"priority\":1,\"times\":1,\"tags\":[],"
 +
-            
"\"enable\":true,\"recoverNotice\":true,\"template\":\"Template1\"}}]";
+    private static final String JSON_DATA = 
"[{\"alertDefine\":{\"name\":\"App1\",\"type\":\"realtime\"," +
+            "\"expr\":\"Expr1\",\"period\":3000,\"times\":3," +
+            "\"enable\":true,\"template\":\"Template1\"}}]";
 
     private InputStream inputStream;
     private List<ExportAlertDefineDTO> alertDefineList;
@@ -71,15 +71,12 @@ class AlertDefineJsonImExportServiceTest {
         inputStream = new ByteArrayInputStream(JSON_DATA.getBytes());
 
         AlertDefineDTO alertDefine = new AlertDefineDTO();
-        alertDefine.setApp("App1");
-        alertDefine.setMetric("Metric1");
-        alertDefine.setField("Field1");
-        alertDefine.setPreset(true);
+        alertDefine.setName("App1");
+        alertDefine.setType("realtime");
         alertDefine.setExpr("Expr1");
-        alertDefine.setPriority((byte) 1);
-        alertDefine.setTimes(1);
+        alertDefine.setPeriod(3000);
+        alertDefine.setTimes(3);
         alertDefine.setEnable(true);
-        alertDefine.setRecoverNotice(true);
         alertDefine.setTemplate("Template1");
 
         ExportAlertDefineDTO exportAlertDefine = new ExportAlertDefineDTO();
diff --git 
a/hertzbeat-alerter/src/test/java/org/apache/hertzbeat/alert/service/AlertDefineYamlImExportServiceTest.java
 
b/hertzbeat-alerter/src/test/java/org/apache/hertzbeat/alert/service/AlertDefineYamlImExportServiceTest.java
index 0a83b3925..3b633965f 100644
--- 
a/hertzbeat-alerter/src/test/java/org/apache/hertzbeat/alert/service/AlertDefineYamlImExportServiceTest.java
+++ 
b/hertzbeat-alerter/src/test/java/org/apache/hertzbeat/alert/service/AlertDefineYamlImExportServiceTest.java
@@ -59,16 +59,12 @@ class AlertDefineYamlImExportServiceTest {
     private static final String YAML_DATA =
             """
                     - alertDefine:
-                        app: App1
-                        metric: Metric1
-                        field: Field1
-                        preset: true
+                        name: App1
+                        type: realtime
                         expr: Expr1
-                        priority: 1
-                        times: 1
-                        tags: []
+                        period: 3000
+                        times: 3
                         enable: true
-                        recoverNotice: true
                         template: Template1
                     """;
 
@@ -81,15 +77,12 @@ class AlertDefineYamlImExportServiceTest {
         inputStream = new 
ByteArrayInputStream(YAML_DATA.getBytes(StandardCharsets.UTF_8));
 
         AlertDefineDTO alertDefine = new AlertDefineDTO();
-        alertDefine.setApp("App1");
-        alertDefine.setMetric("Metric1");
-        alertDefine.setField("Field1");
-        alertDefine.setPreset(true);
+        alertDefine.setName("App1");
+        alertDefine.setType("realtime");
+        alertDefine.setPeriod(3000);
+        alertDefine.setTimes(3);
         alertDefine.setExpr("Expr1");
-        alertDefine.setPriority((byte) 1);
-        alertDefine.setTimes(1);
         alertDefine.setEnable(true);
-        alertDefine.setRecoverNotice(true);
         alertDefine.setTemplate("Template1");
 
         ExportAlertDefineDTO exportAlertDefine = new ExportAlertDefineDTO();
@@ -141,8 +134,9 @@ class AlertDefineYamlImExportServiceTest {
         service.writeOs(alertDefineList, outputStream);
         String yamlOutput = outputStream.toString(StandardCharsets.UTF_8);
 
-        assertTrue(yamlOutput.contains("app: App1"));
-        assertTrue(yamlOutput.contains("metric: Metric1"));
+        assertTrue(yamlOutput.contains("name: App1"));
+        assertTrue(yamlOutput.contains("type: realtime"));
+        assertTrue(yamlOutput.contains("expr: Expr1"));
     }
 
     @Test
diff --git 
a/hertzbeat-manager/src/main/java/org/apache/hertzbeat/manager/controller/MonitorsController.java
 
b/hertzbeat-manager/src/main/java/org/apache/hertzbeat/manager/controller/MonitorsController.java
index 14a41eb3e..73e926c8a 100644
--- 
a/hertzbeat-manager/src/main/java/org/apache/hertzbeat/manager/controller/MonitorsController.java
+++ 
b/hertzbeat-manager/src/main/java/org/apache/hertzbeat/manager/controller/MonitorsController.java
@@ -116,7 +116,7 @@ public class MonitorsController {
     @Operation(summary = "export monitor config", description = "export 
monitor config")
     public void export(
             @Parameter(description = "Monitor ID List", example = 
"6565463543") @RequestParam List<Long> ids,
-            @Parameter(description = "Export Type:JSON,EXCEL,YAML") 
@RequestParam(defaultValue = "JSON") String type,
+            @Parameter(description = "Export Type:JSON,EXCEL") 
@RequestParam(defaultValue = "JSON") String type,
             HttpServletResponse res) throws Exception {
         monitorService.export(ids, type, res);
     }
diff --git 
a/hertzbeat-manager/src/main/java/org/apache/hertzbeat/manager/service/impl/AbstractImExportServiceImpl.java
 
b/hertzbeat-manager/src/main/java/org/apache/hertzbeat/manager/service/impl/AbstractImExportServiceImpl.java
index bdfd6e8fc..c4d8c76aa 100644
--- 
a/hertzbeat-manager/src/main/java/org/apache/hertzbeat/manager/service/impl/AbstractImExportServiceImpl.java
+++ 
b/hertzbeat-manager/src/main/java/org/apache/hertzbeat/manager/service/impl/AbstractImExportServiceImpl.java
@@ -108,7 +108,6 @@ public abstract class AbstractImExportServiceImpl 
implements ImExportService {
                     return param;
                 })
                 .toList());
-        exportMonitor.setMetrics(dto.getMetrics());
         exportMonitor.getMonitor().setCollector(dto.getCollector());
         return exportMonitor;
     }
@@ -129,7 +128,6 @@ public abstract class AbstractImExportServiceImpl 
implements ImExportService {
         if (exportMonitor.getMonitor() != null) {
             monitorDto.setCollector(exportMonitor.getMonitor().getCollector());
         }
-        monitorDto.setMetrics(exportMonitor.metrics);
         if (exportMonitor.params != null) {
             monitorDto.setParams(exportMonitor.params.stream()
                     .map(it -> {
@@ -150,6 +148,9 @@ public abstract class AbstractImExportServiceImpl 
implements ImExportService {
         return "hertzbeat_monitor_" + LocalDate.now();
     }
 
+    /**
+     * Export Monitor DTO
+     */
     @Data
     @JsonInclude(JsonInclude.Include.NON_NULL)
     @JsonIgnoreProperties(ignoreUnknown = true)
@@ -159,10 +160,11 @@ public abstract class AbstractImExportServiceImpl 
implements ImExportService {
         private MonitorDTO monitor;
         @ExcelCollection(name = "Params")
         private List<ParamDTO> params;
-        @ExcelCollection(name = "Metrics")
-        private List<String> metrics;
     }
 
+    /**
+     * Monitor DTO
+     */
     @Data
     @JsonInclude(JsonInclude.Include.NON_NULL)
     @JsonIgnoreProperties(ignoreUnknown = true)
@@ -180,12 +182,15 @@ public abstract class AbstractImExportServiceImpl 
implements ImExportService {
         private Byte status;
         @Excel(name = "Description")
         private String description;
-        @Excel(name = "labels")
+        @Excel(name = "Labels")
         private Map<String, String> labels;
         @Excel(name = "Collector")
         private String collector;
     }
 
+    /**
+     * Param DTO
+     */
     @Data
     @JsonInclude(JsonInclude.Include.NON_NULL)
     @JsonIgnoreProperties(ignoreUnknown = true)
diff --git 
a/hertzbeat-manager/src/main/java/org/apache/hertzbeat/manager/service/impl/ExcelImExportServiceImpl.java
 
b/hertzbeat-manager/src/main/java/org/apache/hertzbeat/manager/service/impl/ExcelImExportServiceImpl.java
index 46c8d66b0..e2dbb3189 100644
--- 
a/hertzbeat-manager/src/main/java/org/apache/hertzbeat/manager/service/impl/ExcelImExportServiceImpl.java
+++ 
b/hertzbeat-manager/src/main/java/org/apache/hertzbeat/manager/service/impl/ExcelImExportServiceImpl.java
@@ -24,21 +24,20 @@ import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
-import java.util.stream.Collectors;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.commons.collections4.CollectionUtils;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.hertzbeat.common.util.JsonUtil;
-import org.apache.hertzbeat.common.util.export.ExcelExportUtils;
 import org.apache.poi.ss.usermodel.BorderStyle;
 import org.apache.poi.ss.usermodel.Cell;
 import org.apache.poi.ss.usermodel.CellStyle;
 import org.apache.poi.ss.usermodel.CellType;
+import org.apache.poi.ss.usermodel.Font;
+import org.apache.poi.ss.usermodel.HorizontalAlignment;
 import org.apache.poi.ss.usermodel.Row;
 import org.apache.poi.ss.usermodel.Sheet;
 import org.apache.poi.ss.usermodel.Workbook;
@@ -97,16 +96,9 @@ public class ExcelImExportServiceImpl extends 
AbstractImExportServiceImpl{
                     ExportMonitorDTO exportMonitor = new ExportMonitorDTO();
                     exportMonitor.setMonitor(monitor);
                     monitors.add(exportMonitor);
-                    String metrics = getCellValueAsString(row.getCell(11));
-                    if (StringUtils.isNotBlank(metrics)) {
-                        List<String> metricList = 
Arrays.stream(metrics.split(",")).collect(Collectors.toList());
-                        exportMonitor.setMetrics(metricList);
-                    }
                 }
             }
-
             List<List<ParamDTO>> paramsList = new ArrayList<>();
-
             for (int i = 0; i < startRowList.size(); i++) {
                 int startRowIndex = startRowList.get(i);
                 int endRowIndex = (i + 1 < startRowList.size()) ? 
startRowList.get(i + 1) : sheet.getLastRowNum() + 1;
@@ -122,7 +114,6 @@ public class ExcelImExportServiceImpl extends 
AbstractImExportServiceImpl{
                         params.add(param);
                     }
                 }
-
                 paramsList.add(params);
             }
             for (int i = 0; i < monitors.size(); i++) {
@@ -153,8 +144,6 @@ public class ExcelImExportServiceImpl extends 
AbstractImExportServiceImpl{
             } catch (Exception ignored) {}
         }
         monitor.setCollector(getCellValueAsString(row.getCell(7)));
-
-
         return monitor;
     }
 
@@ -222,9 +211,27 @@ public class ExcelImExportServiceImpl extends 
AbstractImExportServiceImpl{
 
             Workbook workbook = WorkbookFactory.create(true);
             String sheetName = "Export Monitor";
-            Sheet sheet = ExcelExportUtils.setSheet(sheetName, workbook, 
ExportMonitorDTO.class);
+            Sheet sheet = workbook.createSheet(sheetName);
+            sheet.setDefaultColumnWidth(20);
+            sheet.setColumnWidth(6, 40 * 256);
+            sheet.setColumnWidth(10, 40 * 256);
+            // set header style
+            CellStyle headerCellStyle = workbook.createCellStyle();
+            Font headerFont = workbook.createFont();
+            headerFont.setBold(true);
+            headerCellStyle.setFont(headerFont);
+            headerCellStyle.setAlignment(HorizontalAlignment.CENTER);
             // set cell style
-            CellStyle cellStyle = ExcelExportUtils.setCellStyle(workbook);
+            CellStyle cellStyle = workbook.createCellStyle();
+            cellStyle.setAlignment(HorizontalAlignment.CENTER);
+            // set header
+            String[] headers = { "Name", "App", "Host", "Intervals", "Status", 
"Description", "Labels", "Collector", "Param-Field", "Param-Type", 
"Param-Value" };
+            Row headerRow = sheet.createRow(0);
+            for (int i = 0; i < headers.length; i++) {
+                Cell cell = headerRow.createCell(i);
+                cell.setCellValue(headers[i]);
+                cell.setCellStyle(headerCellStyle);
+            }
 
             // foreach monitor, each monitor object corresponds to a row of 
data
             int rowIndex = 1;
@@ -233,8 +240,6 @@ public class ExcelImExportServiceImpl extends 
AbstractImExportServiceImpl{
                 MonitorDTO monitorDTO = monitor.getMonitor();
                 // get monitor parameters
                 List<ParamDTO> paramList = monitor.getParams();
-                // get monitor metrics
-                List<String> metricList = monitor.getMetrics();
                 // merge monitor information and parameter information into 
one row
                 for (int i = 0; i < Math.max(paramList.size(), 1); i++) {
                     Row row = sheet.createRow(rowIndex++);
@@ -258,17 +263,12 @@ public class ExcelImExportServiceImpl extends 
AbstractImExportServiceImpl{
                         Cell descriptionCell = row.createCell(5);
                         
descriptionCell.setCellValue(monitorDTO.getDescription());
                         descriptionCell.setCellStyle(cellStyle);
-                        Cell tagsCell = row.createCell(6);
-                        
tagsCell.setCellValue(JsonUtil.toJson(monitorDTO.getLabels()));
-                        tagsCell.setCellStyle(cellStyle);
+                        Cell labelsCell = row.createCell(6);
+                        
labelsCell.setCellValue(JsonUtil.toJson(monitorDTO.getLabels()));
+                        labelsCell.setCellStyle(cellStyle);
                         Cell collectorCell = row.createCell(7);
                         collectorCell.setCellValue(monitorDTO.getCollector());
                         collectorCell.setCellStyle(cellStyle);
-                        if (metricList != null && i < metricList.size()) {
-                            Cell metricCell = row.createCell(11);
-                            metricCell.setCellValue(String.join(",", 
metricList));
-                            metricCell.setCellStyle(cellStyle);
-                        }
                     }
                     // Fill in parameter information
                     if (i < paramList.size()) {
diff --git 
a/hertzbeat-manager/src/main/java/org/apache/hertzbeat/manager/service/impl/MonitorServiceImpl.java
 
b/hertzbeat-manager/src/main/java/org/apache/hertzbeat/manager/service/impl/MonitorServiceImpl.java
index 4c39a86d5..fd38159e0 100644
--- 
a/hertzbeat-manager/src/main/java/org/apache/hertzbeat/manager/service/impl/MonitorServiceImpl.java
+++ 
b/hertzbeat-manager/src/main/java/org/apache/hertzbeat/manager/service/impl/MonitorServiceImpl.java
@@ -579,8 +579,10 @@ public class MonitorServiceImpl implements MonitorService {
             if (StringUtils.isNotBlank(search)) {
                 Predicate predicateHost = 
criteriaBuilder.like(root.get("host"), "%" + search + "%");
                 Predicate predicateName = 
criteriaBuilder.like(root.get("name"), "%" + search + "%");
+                Predicate predicateId = criteriaBuilder.like(root.get("id"), 
"%" + search + "%");
                 orList.add(predicateHost);
                 orList.add(predicateName);
+                orList.add(predicateId);
             }
             if (StringUtils.isNotBlank(labels)) {
                 String[] labelAres = labels.split(",");
@@ -861,7 +863,6 @@ public class MonitorServiceImpl implements MonitorService {
             }
             monitor.setId(monitorId);
             monitor.setJobId(jobId);
-            monitor.setStatus(CommonConstants.MONITOR_UP_CODE);
             // create grafana dashboard
             if (monitor.getApp().equals(CommonConstants.PROMETHEUS) && 
grafanaDashboard != null && grafanaDashboard.isEnabled()) {
                 
dashboardService.createOrUpdateDashboard(grafanaDashboard.getTemplate(), 
monitorId);
diff --git a/hertzbeat-manager/src/main/resources/application.yml 
b/hertzbeat-manager/src/main/resources/application.yml
index 4f3ea8821..41f30a7f2 100644
--- a/hertzbeat-manager/src/main/resources/application.yml
+++ b/hertzbeat-manager/src/main/resources/application.yml
@@ -200,6 +200,9 @@ alerter:
   server-chan-webhook-url: https://sctapi.ftqq.com/%s.send
   # gotify
   gotify-webhook-url: http://127.0.0.1/message?token=%s
+  # alert inhibit ttl unit ms, default 14400000(4 hours)
+  inhibit:
+    ttl: 14400000
 
 scheduler:
   server:
diff --git 
a/hertzbeat-manager/src/test/java/org/apache/hertzbeat/manager/service/YamlImExportServiceTest.java
 
b/hertzbeat-manager/src/test/java/org/apache/hertzbeat/manager/service/YamlImExportServiceTest.java
index bd18d9ff3..341c99fff 100644
--- 
a/hertzbeat-manager/src/test/java/org/apache/hertzbeat/manager/service/YamlImExportServiceTest.java
+++ 
b/hertzbeat-manager/src/test/java/org/apache/hertzbeat/manager/service/YamlImExportServiceTest.java
@@ -18,6 +18,7 @@
 package org.apache.hertzbeat.manager.service;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
 import static org.junit.jupiter.api.Assertions.assertNotNull;
 import static org.junit.jupiter.api.Assertions.assertNull;
 import static org.junit.jupiter.api.Assertions.assertTrue;
@@ -96,11 +97,9 @@ class YamlImExportServiceTest {
         AbstractImExportServiceImpl.ExportMonitorDTO exportMonitorDto1 = new 
AbstractImExportServiceImpl.ExportMonitorDTO();
         exportMonitorDto1.setParams(List.of(paramDTO));
         exportMonitorDto1.setMonitor(monitorDTO);
-        exportMonitorDto1.setMetrics(List.of("Test1", "Test2"));
         AbstractImExportServiceImpl.ExportMonitorDTO exportMonitorDto2 = new 
AbstractImExportServiceImpl.ExportMonitorDTO();
         exportMonitorDto2.setParams(List.of(paramDTO));
         exportMonitorDto2.setMonitor(monitorDTO);
-        exportMonitorDto2.setMetrics(List.of("Test1", "Test2"));
 
         List<AbstractImExportServiceImpl.ExportMonitorDTO> monitorList = 
Arrays.asList(
                 exportMonitorDto1,
@@ -111,7 +110,7 @@ class YamlImExportServiceTest {
         yamlImExportService.writeOs(monitorList, os);
 
         String output = os.toString();
-        assertTrue(output.contains("metrics:\n  - Test1"));
+        assertFalse(output.contains("metrics:\n  - Test1"));
         assertTrue(output.contains("  params:\n  - &id002\n    field: Test"));
     }
 
diff --git 
a/hertzbeat-warehouse/src/main/java/org/apache/hertzbeat/warehouse/store/DataStorageDispatch.java
 
b/hertzbeat-warehouse/src/main/java/org/apache/hertzbeat/warehouse/store/DataStorageDispatch.java
index c89ea4ca5..c895ff2a7 100644
--- 
a/hertzbeat-warehouse/src/main/java/org/apache/hertzbeat/warehouse/store/DataStorageDispatch.java
+++ 
b/hertzbeat-warehouse/src/main/java/org/apache/hertzbeat/warehouse/store/DataStorageDispatch.java
@@ -17,10 +17,7 @@
 
 package org.apache.hertzbeat.warehouse.store;
 
-import java.util.List;
 import java.util.Optional;
-import java.util.Set;
-import java.util.concurrent.ConcurrentSkipListSet;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.hertzbeat.common.constants.CommonConstants;
 import org.apache.hertzbeat.common.entity.message.CollectRep;
@@ -30,6 +27,7 @@ import org.apache.hertzbeat.plugin.runner.PluginRunner;
 import org.apache.hertzbeat.warehouse.WarehouseWorkerPool;
 import org.apache.hertzbeat.warehouse.store.history.HistoryDataWriter;
 import org.apache.hertzbeat.warehouse.store.realtime.RealTimeDataWriter;
+import org.springframework.dao.EmptyResultDataAccessException;
 import org.springframework.jdbc.core.JdbcTemplate;
 import org.springframework.stereotype.Component;
 
@@ -46,7 +44,6 @@ public class DataStorageDispatch {
     private final RealTimeDataWriter realTimeDataWriter;
     private final Optional<HistoryDataWriter> historyDataWriter;
     private final PluginRunner pluginRunner;
-    private final Set<Long> monitorDownStatusCache;
 
     public DataStorageDispatch(CommonDataQueue commonDataQueue,
                                WarehouseWorkerPool workerPool,
@@ -60,8 +57,6 @@ public class DataStorageDispatch {
         this.realTimeDataWriter = realTimeDataWriter;
         this.historyDataWriter = historyDataWriter;
         this.pluginRunner = pluginRunner;
-        this.monitorDownStatusCache = new ConcurrentSkipListSet<>();
-        initMonitorDownStatusCache();
         startPersistentDataStorage();
     }
 
@@ -87,25 +82,26 @@ public class DataStorageDispatch {
         };
         workerPool.executeJob(runnable);
     }
-
-    private void initMonitorDownStatusCache() {
-        String sql = "SELECT id FROM hzb_monitor WHERE status = 2";
-        List<Long> ids = jdbcTemplate.query(sql, (rs, rowNum) -> 
rs.getLong("id"));
-        monitorDownStatusCache.addAll(ids);
-    }
     
     protected void calculateMonitorStatus(CollectRep.MetricsData metricsData) {
         if (metricsData.getPriority() == 0) {
             long id = metricsData.getId();
             CollectRep.Code code = metricsData.getCode();
-            if (code == CollectRep.Code.SUCCESS && 
monitorDownStatusCache.contains(id)) {
-                monitorDownStatusCache.remove(id);
-                String sql = "UPDATE hzb_monitor SET status = ? WHERE id = ?";
-                jdbcTemplate.update(sql, CommonConstants.MONITOR_UP_CODE, id);
-            } else if (code != CollectRep.Code.SUCCESS && 
!monitorDownStatusCache.contains(id)) {
-                monitorDownStatusCache.add(id);
-                String sql = "UPDATE hzb_monitor SET status = ? WHERE id = ?";
-                jdbcTemplate.update(sql, CommonConstants.MONITOR_DOWN_CODE, 
id);
+            // query current status
+            String queryStatusSql = "SELECT status FROM hzb_monitor WHERE id = 
?";
+            try {
+                int currentStatus = 
jdbcTemplate.queryForObject(queryStatusSql, Integer.class, id);
+                if (code == CollectRep.Code.SUCCESS && currentStatus == 
CommonConstants.MONITOR_DOWN_CODE) {
+                    // if collect success and current status is DOWN, update 
to UP
+                    String sql = "UPDATE hzb_monitor SET status = ? WHERE id = 
?";
+                    jdbcTemplate.update(sql, CommonConstants.MONITOR_UP_CODE, 
id);
+                } else if (code != CollectRep.Code.SUCCESS && currentStatus == 
CommonConstants.MONITOR_UP_CODE) {
+                    // if collect failed and current status is UP, update to 
DOWN
+                    String sql = "UPDATE hzb_monitor SET status = ? WHERE id = 
?";
+                    jdbcTemplate.update(sql, 
CommonConstants.MONITOR_DOWN_CODE, id);
+                }
+            } catch (EmptyResultDataAccessException ignored) {
+                // when query currentStatus result is null
             }
         }
     }
diff --git a/home/src/pages/team/index.jsx b/home/src/pages/team/index.jsx
index 7a4d9d100..1a1a363e9 100644
--- a/home/src/pages/team/index.jsx
+++ b/home/src/pages/team/index.jsx
@@ -1192,6 +1192,30 @@ export default function () {
                     alt="yunfan24"/><br/><sub><b>yunfan24</b></sub></a><br/><a
                     
href="https://github.com/apache/hertzbeat/commits?author=yunfan24"; 
title="Code">💻</a></td>
                 </tr>
+                <tr>
+                  <td align="center" valign="top" width="14.28%"><a 
href="https://github.com/lctking";><img
+                    
src="https://avatars.githubusercontent.com/u/168249998?v=4?s=100"; width="100px;"
+                    alt="nullwli"/><br/><sub><b>nullwli</b></sub></a><br/><a
+                    
href="https://github.com/apache/hertzbeat/commits?author=lctking"; 
title="Code">💻</a></td>
+                  <td align="center" valign="top" width="14.28%"><a 
href="https://simonsigre.com/";><img
+                    
src="https://avatars.githubusercontent.com/u/14932913?v=4?s=100"; width="100px;"
+                    alt="Simon Sigré"/><br/><sub><b>Simon 
Sigré</b></sub></a><br/><a
+                    
href="https://github.com/apache/hertzbeat/commits?author=simonsigre"; 
title="Documentation">📖</a>
+                  </td>
+                  <td align="center" valign="top" width="14.28%"><a 
href="http://www.ponfee.cn/";><img
+                    
src="https://avatars.githubusercontent.com/u/46117331?v=4?s=100"; width="100px;"
+                    alt="ponfee"/><br/><sub><b>ponfee</b></sub></a><br/><a
+                    
href="https://github.com/apache/hertzbeat/commits?author=ponfee"; 
title="Code">💻</a></td>
+                  <td align="center" valign="top" width="14.28%"><a 
href="https://github.com/Vedant7789";><img
+                    
src="https://avatars.githubusercontent.com/u/147625492?v=4?s=100"; width="100px;"
+                    
alt="Vedant7789"/><br/><sub><b>Vedant7789</b></sub></a><br/><a
+                    
href="https://github.com/apache/hertzbeat/commits?author=Vedant7789"; 
title="Code">💻</a></td>
+                  <td align="center" valign="top" width="14.28%"><a 
href="https://github.com/Craaaaazy77";><img
+                    
src="https://avatars.githubusercontent.com/u/23025522?v=4?s=100"; width="100px;"
+                    
alt="Craaaaazy77"/><br/><sub><b>Craaaaazy77</b></sub></a><br/><a
+                    
href="https://github.com/apache/hertzbeat/commits?author=Craaaaazy77"; 
title="Documentation">📖</a>
+                  </td>
+                </tr>
                 </tbody>
               </table>
 
diff --git a/script/application.yml b/script/application.yml
index 26d1ff743..54ade2214 100644
--- a/script/application.yml
+++ b/script/application.yml
@@ -199,7 +199,10 @@ alerter:
   server-chan-webhook-url: https://sctapi.ftqq.com/%s.send
   # gotify
   gotify-webhook-url: http://127.0.0.1/message?token=%s
-
+  # alert inhibit ttl unit ms, default 14400000(4 hours)
+  inhibit:
+    ttl: 14400000
+    
 scheduler:
   server:
     enabled: true
diff --git a/script/docker-compose/hertzbeat-mysql-iotdb/conf/application.yml 
b/script/docker-compose/hertzbeat-mysql-iotdb/conf/application.yml
index 7f27eac21..ca4357a4b 100644
--- a/script/docker-compose/hertzbeat-mysql-iotdb/conf/application.yml
+++ b/script/docker-compose/hertzbeat-mysql-iotdb/conf/application.yml
@@ -166,7 +166,10 @@ alerter:
   server-chan-webhook-url: https://sctapi.ftqq.com/%s.send
   # gotify
   gotify-webhook-url: http://127.0.0.1/message?token=%s
-
+  # alert inhibit ttl unit ms, default 14400000(4 hours)
+  inhibit:
+    ttl: 14400000
+    
 scheduler:
   server:
     enabled: true
diff --git 
a/script/docker-compose/hertzbeat-mysql-tdengine/conf/application.yml 
b/script/docker-compose/hertzbeat-mysql-tdengine/conf/application.yml
index 8beb7f698..a9deab46e 100644
--- a/script/docker-compose/hertzbeat-mysql-tdengine/conf/application.yml
+++ b/script/docker-compose/hertzbeat-mysql-tdengine/conf/application.yml
@@ -162,7 +162,10 @@ alerter:
   server-chan-webhook-url: https://sctapi.ftqq.com/%s.send
   # gotify
   gotify-webhook-url: http://127.0.0.1/message?token=%s
-
+  # alert inhibit ttl unit ms, default 14400000(4 hours)
+  inhibit:
+    ttl: 14400000
+    
 scheduler:
   server:
     enabled: true
diff --git 
a/script/docker-compose/hertzbeat-mysql-victoria-metrics/conf/application.yml 
b/script/docker-compose/hertzbeat-mysql-victoria-metrics/conf/application.yml
index f006f39e0..53aae6d04 100644
--- 
a/script/docker-compose/hertzbeat-mysql-victoria-metrics/conf/application.yml
+++ 
b/script/docker-compose/hertzbeat-mysql-victoria-metrics/conf/application.yml
@@ -160,7 +160,10 @@ alerter:
   server-chan-webhook-url: https://sctapi.ftqq.com/%s.send
   # gotify
   gotify-webhook-url: http://127.0.0.1/message?token=%s
-
+  # alert inhibit ttl unit ms, default 14400000(4 hours)
+  inhibit:
+    ttl: 14400000
+    
 scheduler:
   server:
     enabled: true
diff --git 
a/script/docker-compose/hertzbeat-postgresql-victoria-metrics/conf/application.yml
 
b/script/docker-compose/hertzbeat-postgresql-victoria-metrics/conf/application.yml
index a3a9180c7..97cbfa053 100644
--- 
a/script/docker-compose/hertzbeat-postgresql-victoria-metrics/conf/application.yml
+++ 
b/script/docker-compose/hertzbeat-postgresql-victoria-metrics/conf/application.yml
@@ -159,7 +159,10 @@ alerter:
   server-chan-webhook-url: https://sctapi.ftqq.com/%s.send
   # gotify
   gotify-webhook-url: http://127.0.0.1/message?token=%s
-
+  # alert inhibit ttl unit ms, default 14400000(4 hours)
+  inhibit:
+    ttl: 14400000
+    
 scheduler:
   server:
     enabled: true
diff --git 
a/web-app/src/app/routes/alert/alert-center/alert-center.component.html 
b/web-app/src/app/routes/alert/alert-center/alert-center.component.html
index 83ffe1650..a5abf297b 100644
--- a/web-app/src/app/routes/alert/alert-center/alert-center.component.html
+++ b/web-app/src/app/routes/alert/alert-center/alert-center.component.html
@@ -111,6 +111,7 @@
           [nzHeader]="alertHeader"
           [nzExtra]="alertExtra"
           [nzActive]="false"
+          [style]="'border-left-color:' + (item.status == 'firing' ? '#ff4d4f' 
: '#52c41a')"
           [nzExpandedIcon]="expandedIcon"
         >
           <ng-template #alertHeader>
@@ -136,6 +137,16 @@
             </span>
           </div>
 
+          <!-- Status -->
+          <div class="detail-section">
+            <div class="section-title">{{ 'alert.center.status' | i18n }}</div>
+            <div class="alert-status">
+              <nz-tag [nzColor]="item.status === 'firing' ? 'error' : 
'success'">
+                {{ item.status === 'firing' ? ('alert.status.firing' | i18n) : 
('alert.status.resolved' | i18n) }}
+              </nz-tag>
+            </div>
+          </div>
+
           <!-- Labels -->
           <div class="detail-section" *ngIf="item.labels">
             <div class="section-title">{{ 'alert.center.labels' | i18n }}</div>
diff --git 
a/web-app/src/app/routes/alert/alert-setting/alert-setting.component.html 
b/web-app/src/app/routes/alert/alert-setting/alert-setting.component.html
index dad7fa8f4..d07b75ce2 100644
--- a/web-app/src/app/routes/alert/alert-setting/alert-setting.component.html
+++ b/web-app/src/app/routes/alert/alert-setting/alert-setting.component.html
@@ -609,28 +609,30 @@
   [(nzVisible)]="isSwitchExportTypeModalVisible"
   [nzTitle]="'alert.export.switch-type' | i18n"
   (nzOnCancel)="onExportTypeModalCancel()"
-  nzOkDisabled="true"
-  [nzFooter]="switchExportTypeModalFooter"
+  nzWidth="600px"
+  [nzFooter]="null"
 >
   <ng-container *nzModalContent>
-    <p style="text-align: center">
-      <button nz-button nzType="primary" nzSize="large" 
(click)="exportDefines('YAML')" [nzLoading]="exportYamlButtonLoading">
-        <span nz-icon nzType="download"></span>
-        {{ 'alert.export.use-type' | i18n : { type: 'YAML' } }}
-      </button>
-    </p>
-    <p style="text-align: center">
-      <button nz-button nzType="primary" nzSize="large" 
(click)="exportDefines('JSON')" [nzLoading]="exportJsonButtonLoading">
-        <span nz-icon nzType="download"></span>
-        {{ 'alert.export.use-type' | i18n : { type: 'JSON' } }}
-      </button>
-    </p>
-    <p style="text-align: center">
-      <button nz-button nzType="primary" nzSize="large" 
(click)="exportDefines('EXCEL')" [nzLoading]="exportExcelButtonLoading">
-        <span nz-icon nzType="download"></span>
-        {{ 'alert.export.use-type' | i18n : { type: 'EXCEL' } }}
-      </button>
-    </p>
+    <div class="export-type-container">
+      <div class="export-type-card" (click)="exportDefines('JSON')" 
[class.loading]="exportJsonButtonLoading">
+        <div class="export-type-icon">
+          <i nz-icon nzType="code" nzTheme="outline"></i>
+        </div>
+        <div class="export-type-info">
+          <h3>JSON</h3>
+          <p>{{ 'alert.export.use-type' | i18n : { type: 'JSON' } }}</p>
+        </div>
+      </div>
+      <div class="export-type-card" (click)="exportDefines('EXCEL')" 
[class.loading]="exportExcelButtonLoading">
+        <div class="export-type-icon">
+          <i nz-icon nzType="file-excel" nzTheme="outline"></i>
+        </div>
+        <div class="export-type-info">
+          <h3>EXCEL</h3>
+          <p>{{ 'alert.export.use-type' | i18n : { type: 'EXCEL' } }}</p>
+        </div>
+      </div>
+    </div>
   </ng-container>
 </nz-modal>
 
diff --git 
a/web-app/src/app/routes/alert/alert-setting/alert-setting.component.less 
b/web-app/src/app/routes/alert/alert-setting/alert-setting.component.less
index aaba18e78..13d33ea01 100644
--- a/web-app/src/app/routes/alert/alert-setting/alert-setting.component.less
+++ b/web-app/src/app/routes/alert/alert-setting/alert-setting.component.less
@@ -1,3 +1,4 @@
+@import '@delon/theme/index';
 ::ng-deep {
 
   .row {
@@ -180,4 +181,61 @@
     border-left: none;
     border-radius: 0 2px 2px 0;
   }
+
+  .export-type-container {
+    display: flex;
+    justify-content: space-between;
+    gap: 20px;
+    padding: 20px 0;
+  }
+
+  .export-type-card {
+    flex: 1;
+    display: flex;
+    align-items: center;
+    padding: 24px;
+    border: 1px solid #e8e8e8;
+    border-radius: 8px;
+    cursor: pointer;
+    transition: all 0.3s;
+  }
+
+  .export-type-card:hover {
+    border-color: @primary-color;
+    box-shadow: 0 0 8px rgba(24, 144, 255, 0.2);
+  }
+
+  .export-type-icon {
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    width: 48px;
+    height: 48px;
+    margin-right: 16px;
+    font-size: 24px;
+    color: @primary-color;
+    background: rgba(24, 144, 255, 0.1);
+    border-radius: 8px;
+  }
+
+  .export-type-info {
+    flex: 1;
+  }
+
+  .export-type-info h3 {
+    margin: 0 0 8px;
+    font-size: 16px;
+    font-weight: 500;
+  }
+
+  .export-type-info p {
+    margin: 0;
+    color: #666;
+    font-size: 14px;
+  }
+
+  .export-type-card.loading {
+    opacity: 0.7;
+    cursor: not-allowed;
+  }
 }
diff --git 
a/web-app/src/app/routes/alert/alert-setting/alert-setting.component.ts 
b/web-app/src/app/routes/alert/alert-setting/alert-setting.component.ts
index 141ce190f..ceb0c75e9 100644
--- a/web-app/src/app/routes/alert/alert-setting/alert-setting.component.ts
+++ b/web-app/src/app/routes/alert/alert-setting/alert-setting.component.ts
@@ -28,7 +28,7 @@ import { NzNotificationService } from 
'ng-zorro-antd/notification';
 import { NzTableQueryParams } from 'ng-zorro-antd/table';
 import { TransferChange, TransferItem } from 'ng-zorro-antd/transfer';
 import { NzUploadChangeParam } from 'ng-zorro-antd/upload';
-import { finalize, map } from 'rxjs/operators';
+import { finalize } from 'rxjs/operators';
 
 import { AlertDefine } from '../../../pojo/AlertDefine';
 import { AlertDefineService } from '../../../service/alert-define.service';
@@ -68,7 +68,6 @@ export class AlertSettingComponent implements OnInit {
   checkedDefineIds = new Set<number>();
   isSwitchExportTypeModalVisible = false;
   exportJsonButtonLoading = false;
-  exportYamlButtonLoading = false;
   exportExcelButtonLoading = false;
   appHierarchies!: any[];
   switchExportTypeModalFooter: ModalButtonOptions[] = [
@@ -375,15 +374,11 @@ export class AlertSettingComponent implements OnInit {
       case 'EXCEL':
         this.exportExcelButtonLoading = true;
         break;
-      case 'YAML':
-        this.exportYamlButtonLoading = true;
-        break;
     }
     const exportDefines$ = this.alertDefineSvc
       .exportAlertDefines(this.checkedDefineIds, type)
       .pipe(
         finalize(() => {
-          this.exportYamlButtonLoading = false;
           this.exportExcelButtonLoading = false;
           this.exportJsonButtonLoading = false;
           exportDefines$.unsubscribe();
diff --git a/web-app/src/app/routes/dashboard/dashboard.component.html 
b/web-app/src/app/routes/dashboard/dashboard.component.html
index 4bc50fbed..06dfcc269 100644
--- a/web-app/src/app/routes/dashboard/dashboard.component.html
+++ b/web-app/src/app/routes/dashboard/dashboard.component.html
@@ -398,19 +398,21 @@
         <nz-timeline nzMode="left">
           <nz-timeline-item *ngFor="let alert of alerts; let i = index" 
[nzLabel]="(alert.activeAt | date : 'YYYY-MM-dd HH:mm:ss')?.trim()">
             <p style="font-weight: 400">
-              <!--              <nz-tag *ngIf="alert.severity == 0" 
nzColor="red">-->
-              <!--                <i nz-icon nzType="bell" 
nzTheme="outline"></i>-->
-              <!--                <span>{{ 'alert.severity.0' | i18n 
}}</span>-->
-              <!--              </nz-tag>-->
-              <!--              <nz-tag *ngIf="alert.severity == 1" 
nzColor="orange">-->
-              <!--                <i nz-icon nzType="bell" 
nzTheme="outline"></i>-->
-              <!--                <span>{{ 'alert.severity.1' | i18n 
}}</span>-->
-              <!--              </nz-tag>-->
-              <!--              <nz-tag *ngIf="alert.severity == 2" 
nzColor="yellow">-->
-              <!--                <i nz-icon nzType="bell" 
nzTheme="outline"></i>-->
-              <!--                <span>{{ 'alert.severity.2' | i18n 
}}</span>-->
-              <!--              </nz-tag>-->
-              <!--              <span>[{{ alert.tags.monitorName }}] </span>-->
+              <nz-tag *ngIf="alert.labels.severity == 'emergency'" 
nzColor="red">
+                <i nz-icon nzType="bell" nzTheme="outline"></i>
+                <span>{{ 'alert.severity.0' | i18n }}</span>
+              </nz-tag>
+              <nz-tag *ngIf="alert.labels.severity == 'critical'" 
nzColor="orange">
+                <i nz-icon nzType="bell" nzTheme="outline"></i>
+                <span>{{ 'alert.severity.1' | i18n }}</span>
+              </nz-tag>
+              <nz-tag *ngIf="alert.labels.severity == 'warning'" 
nzColor="yellow">
+                <i nz-icon nzType="bell" nzTheme="outline"></i>
+                <span>{{ 'alert.severity.2' | i18n }}</span>
+              </nz-tag>
+              <nz-tag *ngIf="alert.labels.alertname">
+                <span>{{ alert.labels.alertname }}</span>
+              </nz-tag>
               {{ alert.content }}
             </p>
           </nz-timeline-item>
diff --git 
a/web-app/src/app/routes/monitor/monitor-edit/monitor-edit.component.ts 
b/web-app/src/app/routes/monitor/monitor-edit/monitor-edit.component.ts
index 5eae28f83..0283c6e90 100644
--- a/web-app/src/app/routes/monitor/monitor-edit/monitor-edit.component.ts
+++ b/web-app/src/app/routes/monitor/monitor-edit/monitor-edit.component.ts
@@ -236,8 +236,6 @@ export class MonitorEditComponent implements OnInit {
   }
 
   onCancel() {
-    let app = this.monitor.app;
-    app = app ? app : '';
-    this.router.navigateByUrl(`/monitors?app=${app}`);
+    this.router.navigateByUrl(`/monitors`);
   }
 }
diff --git 
a/web-app/src/app/routes/monitor/monitor-list/monitor-list.component.html 
b/web-app/src/app/routes/monitor/monitor-list/monitor-list.component.html
index 348919ba8..65cb0e02b 100644
--- a/web-app/src/app/routes/monitor/monitor-list/monitor-list.component.html
+++ b/web-app/src/app/routes/monitor/monitor-list/monitor-list.component.html
@@ -273,28 +273,31 @@
 <nz-modal
   [(nzVisible)]="isSwitchExportTypeModalVisible"
   [nzTitle]="'monitors.export.switch-type' | i18n"
-  nzOkDisabled="true"
-  [nzFooter]="switchExportTypeModalFooter"
+  (nzOnCancel)="isSwitchExportTypeModalVisible = false"
+  nzWidth="600px"
+  [nzFooter]="null"
 >
   <ng-container *nzModalContent>
-    <!--    <p style="text-align: center">-->
-    <!--      <button nz-button nzType="primary" nzSize="large" 
(click)="exportMonitors('YAML')" [nzLoading]="exportYamlButtonLoading">-->
-    <!--        <span nz-icon nzType="download"></span>-->
-    <!--        {{ 'monitors.export.use-type' | i18n : { type: 'YAML' } }}-->
-    <!--      </button>-->
-    <!--    </p>-->
-    <p style="text-align: center">
-      <button nz-button nzType="primary" nzSize="large" 
(click)="exportMonitors('JSON')" [nzLoading]="exportJsonButtonLoading">
-        <span nz-icon nzType="download"></span>
-        {{ 'monitors.export.use-type' | i18n : { type: 'JSON' } }}
-      </button>
-    </p>
-    <p style="text-align: center">
-      <button nz-button nzType="primary" nzSize="large" 
(click)="exportMonitors('EXCEL')" [nzLoading]="exportExcelButtonLoading">
-        <span nz-icon nzType="download"></span>
-        {{ 'monitors.export.use-type' | i18n : { type: 'EXCEL' } }}
-      </button>
-    </p>
+    <div class="export-type-container">
+      <div class="export-type-card" (click)="exportMonitors('JSON')" 
[class.loading]="exportJsonButtonLoading">
+        <div class="export-type-icon">
+          <i nz-icon nzType="code" nzTheme="outline"></i>
+        </div>
+        <div class="export-type-info">
+          <h3>JSON</h3>
+          <p>{{ 'monitors.export.use-type' | i18n : { type: 'JSON' } }}</p>
+        </div>
+      </div>
+      <div class="export-type-card" (click)="exportMonitors('EXCEL')" 
[class.loading]="exportExcelButtonLoading">
+        <div class="export-type-icon">
+          <i nz-icon nzType="file-excel" nzTheme="outline"></i>
+        </div>
+        <div class="export-type-info">
+          <h3>EXCEL</h3>
+          <p>{{ 'monitors.export.use-type' | i18n : { type: 'EXCEL' } }}</p>
+        </div>
+      </div>
+    </div>
   </ng-container>
 </nz-modal>
 
diff --git 
a/web-app/src/app/routes/monitor/monitor-list/monitor-list.component.less 
b/web-app/src/app/routes/monitor/monitor-list/monitor-list.component.less
index 667151641..2f692a257 100644
--- a/web-app/src/app/routes/monitor/monitor-list/monitor-list.component.less
+++ b/web-app/src/app/routes/monitor/monitor-list/monitor-list.component.less
@@ -1,3 +1,4 @@
+@import '@delon/theme/index';
 ::ng-deep {
   .monitor-select-menu-modal {
     .ant-spin-container {
@@ -6,3 +7,60 @@
     }
   }
 }
+
+.export-type-container {
+  display: flex;
+  justify-content: space-between;
+  gap: 20px;
+  padding: 20px 0;
+}
+
+.export-type-card {
+  flex: 1;
+  display: flex;
+  align-items: center;
+  padding: 24px;
+  border: 1px solid #e8e8e8;
+  border-radius: 8px;
+  cursor: pointer;
+  transition: all 0.3s;
+}
+
+.export-type-card:hover {
+  border-color: @primary-color;
+  box-shadow: 0 0 8px rgba(24, 144, 255, 0.2);
+}
+
+.export-type-icon {
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  width: 48px;
+  height: 48px;
+  margin-right: 16px;
+  font-size: 24px;
+  color: @primary-color;
+  background: rgba(24, 144, 255, 0.1);
+  border-radius: 8px;
+}
+
+.export-type-info {
+  flex: 1;
+}
+
+.export-type-info h3 {
+  margin: 0 0 8px;
+  font-size: 16px;
+  font-weight: 500;
+}
+
+.export-type-info p {
+  margin: 0;
+  color: #666;
+  font-size: 14px;
+}
+
+.export-type-card.loading {
+  opacity: 0.7;
+  cursor: not-allowed;
+}
diff --git 
a/web-app/src/app/routes/monitor/monitor-list/monitor-list.component.ts 
b/web-app/src/app/routes/monitor/monitor-list/monitor-list.component.ts
index 91f61f63d..2ba5a76eb 100644
--- a/web-app/src/app/routes/monitor/monitor-list/monitor-list.component.ts
+++ b/web-app/src/app/routes/monitor/monitor-list/monitor-list.component.ts
@@ -63,7 +63,6 @@ export class MonitorListComponent implements OnInit, 
OnDestroy {
   checkedMonitorIds = new Set<number>();
   isSwitchExportTypeModalVisible = false;
   exportJsonButtonLoading = false;
-  exportYamlButtonLoading = false;
   exportExcelButtonLoading = false;
   filterContent!: string;
   filterStatus: number = 9;
@@ -333,15 +332,11 @@ export class MonitorListComponent implements OnInit, 
OnDestroy {
       case 'EXCEL':
         this.exportExcelButtonLoading = true;
         break;
-      case 'YAML':
-        this.exportYamlButtonLoading = true;
-        break;
     }
     const exportMonitors$ = this.monitorSvc
       .exportMonitors(this.checkedMonitorIds, type)
       .pipe(
         finalize(() => {
-          this.exportYamlButtonLoading = false;
           this.exportExcelButtonLoading = false;
           this.exportJsonButtonLoading = false;
           exportMonitors$.unsubscribe();
diff --git 
a/web-app/src/app/routes/monitor/monitor-new/monitor-new.component.ts 
b/web-app/src/app/routes/monitor/monitor-new/monitor-new.component.ts
index e626afa4d..930f1b684 100644
--- a/web-app/src/app/routes/monitor/monitor-new/monitor-new.component.ts
+++ b/web-app/src/app/routes/monitor/monitor-new/monitor-new.component.ts
@@ -209,8 +209,6 @@ export class MonitorNewComponent implements OnInit {
   }
 
   onCancel() {
-    let app = this.monitor.app;
-    app = app ? app : '';
-    this.router.navigateByUrl(`/monitors?app=${app}`);
+    this.router.navigateByUrl(`/monitors`);
   }
 }
diff --git a/web-app/src/app/service/alert.service.ts 
b/web-app/src/app/service/alert.service.ts
index d3b008bf4..67dff8238 100644
--- a/web-app/src/app/service/alert.service.ts
+++ b/web-app/src/app/service/alert.service.ts
@@ -48,7 +48,7 @@ export class AlertService {
     // HttpParams is unmodifiable, so we need to save the return value of 
append/set
     let httpParams = new HttpParams();
     httpParams = httpParams.appendAll({
-      sort: 'id',
+      sort: 'gmtUpdate',
       order: 'desc',
       pageIndex: pageIndex,
       pageSize: pageSize
@@ -74,7 +74,7 @@ export class AlertService {
     // HttpParams is unmodifiable, so we need to save the return value of 
append/set
     let httpParams = new HttpParams();
     httpParams = httpParams.appendAll({
-      sort: 'id',
+      sort: 'gmtUpdate',
       order: 'desc',
       pageIndex: pageIndex,
       pageSize: pageSize


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to