diff --git a/ruoyi-extend/ruoyi-monitor-admin/pom.xml b/ruoyi-extend/ruoyi-monitor-admin/pom.xml
index 91194c61b..e14d3ab04 100644
--- a/ruoyi-extend/ruoyi-monitor-admin/pom.xml
+++ b/ruoyi-extend/ruoyi-monitor-admin/pom.xml
@@ -39,6 +39,11 @@
lombok
+
+ org.dromara
+ ruoyi-common-mail
+
+
diff --git a/ruoyi-extend/ruoyi-monitor-admin/src/main/java/org/dromara/monitor/admin/config/properties/NotifyProperties.java b/ruoyi-extend/ruoyi-monitor-admin/src/main/java/org/dromara/monitor/admin/config/properties/NotifyProperties.java
new file mode 100644
index 000000000..870122efe
--- /dev/null
+++ b/ruoyi-extend/ruoyi-monitor-admin/src/main/java/org/dromara/monitor/admin/config/properties/NotifyProperties.java
@@ -0,0 +1,80 @@
+package org.dromara.monitor.admin.config.properties;
+
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * 通知配置
+ *
+ * @author AprilWind
+ */
+@Data
+@ConfigurationProperties(prefix = "notify")
+@Configuration
+public class NotifyProperties {
+
+ /**
+ * 邮件设置
+ */
+ private Mail mail;
+
+ /**
+ * WebHooks 设置
+ */
+ private WebHook webHook;
+
+ @Data
+ @NoArgsConstructor
+ public static class Mail {
+
+ /**
+ * 发送开关
+ */
+ private Boolean enabled = false;
+
+ /**
+ * 收件人
+ */
+ private String to;
+
+ /**
+ * 主题
+ */
+ private String subject;
+
+ /**
+ * 邮件模版
+ */
+ private String template;
+
+ }
+
+ @Data
+ @NoArgsConstructor
+ public static class WebHook {
+
+ /**
+ * 发送开关
+ */
+ private Boolean enabled = false;
+
+ /**
+ * 0默认 1密码 2签名密钥
+ */
+ private String type;
+
+ /**
+ * Post地址
+ */
+ private String url;
+
+ /**
+ * WebHook发送模版
+ */
+ private String template;
+
+ }
+
+}
diff --git a/ruoyi-extend/ruoyi-monitor-admin/src/main/java/org/dromara/monitor/admin/event/NotifierEvent.java b/ruoyi-extend/ruoyi-monitor-admin/src/main/java/org/dromara/monitor/admin/event/NotifierEvent.java
new file mode 100644
index 000000000..7df53e460
--- /dev/null
+++ b/ruoyi-extend/ruoyi-monitor-admin/src/main/java/org/dromara/monitor/admin/event/NotifierEvent.java
@@ -0,0 +1,44 @@
+package org.dromara.monitor.admin.event;
+
+import lombok.Data;
+
+import java.io.Serial;
+import java.io.Serializable;
+
+/**
+ * 通知事件
+ *
+ * @author AprilWind
+ */
+@Data
+public class NotifierEvent implements Serializable {
+
+ @Serial
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * 实例注册名称
+ */
+ private String registName;
+
+ /**
+ * 实例状态名称
+ */
+ private String statusName;
+
+ /**
+ * 实例ID
+ */
+ private String instanceId;
+
+ /**
+ * 实例状态
+ */
+ private String status;
+
+ /**
+ * 服务URL
+ */
+ private String serviceUrl;
+
+}
diff --git a/ruoyi-extend/ruoyi-monitor-admin/src/main/java/org/dromara/monitor/admin/notifier/CustomNotifier.java b/ruoyi-extend/ruoyi-monitor-admin/src/main/java/org/dromara/monitor/admin/notifier/CustomNotifier.java
index 838eefc47..e2b529449 100644
--- a/ruoyi-extend/ruoyi-monitor-admin/src/main/java/org/dromara/monitor/admin/notifier/CustomNotifier.java
+++ b/ruoyi-extend/ruoyi-monitor-admin/src/main/java/org/dromara/monitor/admin/notifier/CustomNotifier.java
@@ -6,6 +6,8 @@ import de.codecentric.boot.admin.server.domain.events.InstanceEvent;
import de.codecentric.boot.admin.server.domain.events.InstanceStatusChangedEvent;
import de.codecentric.boot.admin.server.notify.AbstractEventNotifier;
import lombok.extern.slf4j.Slf4j;
+import org.dromara.common.core.utils.SpringUtils;
+import org.dromara.monitor.admin.event.NotifierEvent;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Mono;
@@ -49,6 +51,13 @@ public class CustomNotifier extends AbstractEventNotifier {
};
log.info("Instance Status Change: 状态名称【{}】, 注册名称【{}】, 实例ID【{}】, 状态【{}】, 服务URL【{}】",
statusName, registName, instanceId, status, serviceUrl);
+ NotifierEvent notifier = new NotifierEvent();
+ notifier.setRegistName(registName);
+ notifier.setStatusName(statusName);
+ notifier.setInstanceId(instanceId);
+ notifier.setStatus(status);
+ notifier.setServiceUrl(serviceUrl);
+ SpringUtils.context().publishEvent(notifier);
}
});
}
diff --git a/ruoyi-extend/ruoyi-monitor-admin/src/main/java/org/dromara/monitor/admin/notifier/InfoNotifier.java b/ruoyi-extend/ruoyi-monitor-admin/src/main/java/org/dromara/monitor/admin/notifier/InfoNotifier.java
new file mode 100644
index 000000000..2f2538986
--- /dev/null
+++ b/ruoyi-extend/ruoyi-monitor-admin/src/main/java/org/dromara/monitor/admin/notifier/InfoNotifier.java
@@ -0,0 +1,128 @@
+package org.dromara.monitor.admin.notifier;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.dromara.common.core.utils.StringUtils;
+import org.dromara.common.mail.utils.MailUtils;
+
+import org.dromara.monitor.admin.config.properties.NotifyProperties;
+import org.dromara.monitor.admin.event.NotifierEvent;
+import org.springframework.context.event.EventListener;
+import org.springframework.scheduling.annotation.Async;
+import org.springframework.stereotype.Service;
+
+import java.net.URI;
+import java.net.http.HttpClient;
+import java.net.http.HttpRequest;
+import java.net.http.HttpResponse;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * 信息通知
+ *
+ * @author AprilWind
+ */
+@RequiredArgsConstructor
+@Slf4j
+@Service
+public class InfoNotifier {
+ private final NotifyProperties notifyProperties;
+
+ /**
+ * 服务上线或者离线
+ *
+ * @param notifier 通知事件
+ */
+ @Async
+ @EventListener(condition = "#notifier.status == 'UP' || #notifier.status == 'OFFLINE'")
+ public void infoNotification(NotifierEvent notifier) {
+ sendWebHook(notifier);
+ }
+
+ /**
+ * 服务下线或者发送异常
+ *
+ * @param notifier 通知事件
+ */
+ @Async
+ @EventListener(condition = "#notifier.status == 'DOWN' || #notifier.status == 'UNKNOWN'")
+ public void errNotification(NotifierEvent notifier) {
+ sendMail(notifier);
+ sendWebHook(notifier);
+ }
+
+ /**
+ * 发送邮件通知
+ *
+ * @param notifier 包含通知信息的对象
+ */
+ public void sendMail(NotifierEvent notifier) {
+ NotifyProperties.Mail mail = notifyProperties.getMail();
+ if (mail.getEnabled()) {
+ String message = StringUtils.format(mail.getTemplate(), notifier.getRegistName(), notifier.getInstanceId(),
+ notifier.getStatusName(), notifier.getStatus(), notifier.getServiceUrl());
+ try {
+ MailUtils.sendHtml(mail.getTo(), notifier.getRegistName() + notifier.getStatusName(), message);
+ log.info("邮件已发送至: {}", mail.getTo());
+ } catch (Exception e) {
+ log.error("邮件发送失败: ", e);
+ }
+ }
+ }
+
+ /**
+ * 发送WebHook通知
+ *
+ * @param notifier 包含通知信息的对象
+ */
+ public void sendWebHook(NotifierEvent notifier) {
+ NotifyProperties.WebHook webHook = notifyProperties.getWebHook();
+ if (webHook.getEnabled()) {
+ String title = notifier.getRegistName() + notifier.getStatusName();
+ String message = StringUtils.format(webHook.getTemplate(), title,
+ notifier.getRegistName(), notifier.getInstanceId(), notifier.getStatus(), notifier.getServiceUrl());
+ try {
+ sendWebHookMessage(webHook.getUrl(), title, message);
+ log.info("WebHook消息已发送至: {}", webHook.getUrl());
+ } catch (Exception e) {
+ log.error("WebHook消息发送失败: ", e);
+ }
+ }
+ }
+
+ private void sendWebHookMessage(String url, String title, String markdownMessage) throws Exception {
+ // 构造消息体
+ Map messageBody = new HashMap<>();
+ messageBody.put("msgtype", "markdown");
+
+ Map markdownContent = new HashMap<>();
+ markdownContent.put("title", title + "通知");
+ markdownContent.put("text", markdownMessage);
+ messageBody.put("markdown", markdownContent);
+
+ // 将消息体转换为JSON字符串
+ ObjectMapper objectMapper = new ObjectMapper();
+ String jsonMessage = objectMapper.writeValueAsString(messageBody);
+
+ // 创建HTTP客户端
+ HttpClient client = HttpClient.newHttpClient();
+ HttpRequest request = HttpRequest.newBuilder()
+ .uri(new URI(url))
+ .header("Content-Type", "application/json")
+ .POST(HttpRequest.BodyPublishers.ofString(jsonMessage))
+ .build();
+
+ // 发送请求
+ HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString());
+
+ // 处理响应
+ if (response.statusCode() == 200) {
+ log.info("WebHook消息发送成功");
+ } else {
+ log.error("WebHook消息发送失败: {}", response.body());
+ }
+ }
+
+}
diff --git a/ruoyi-extend/ruoyi-monitor-admin/src/main/resources/application.yml b/ruoyi-extend/ruoyi-monitor-admin/src/main/resources/application.yml
index beee58775..18d845caa 100644
--- a/ruoyi-extend/ruoyi-monitor-admin/src/main/resources/application.yml
+++ b/ruoyi-extend/ruoyi-monitor-admin/src/main/resources/application.yml
@@ -46,3 +46,47 @@ spring.boot.admin.client:
userpassword: ${spring.boot.admin.client.password}
username: ruoyi
password: 123456
+
+--- # mail 邮件发送
+mail:
+ enabled: false
+ host: smtp.163.com
+ port: 465
+ # 是否需要用户名密码验证
+ auth: true
+ # 发送方,遵循RFC-822标准
+ from: xxx@163.com
+ # 用户名(注意:如果使用foxmail邮箱,此处user为qq号)
+ user: xxx@163.com
+ # 密码(注意,某些邮箱需要为SMTP服务单独设置密码,详情查看相关帮助)
+ pass: xxxxxxxxxx
+ # 使用 STARTTLS安全连接,STARTTLS是对纯文本通信协议的扩展。
+ starttlsEnable: true
+ # 使用SSL安全连接
+ sslEnable: true
+ # SMTP超时时长,单位毫秒,缺省值不超时
+ timeout: 0
+ # Socket连接超时值,单位毫秒,缺省值不超时
+ connectionTimeout: 0
+
+# 服务通知
+notify:
+ mail:
+ enabled: false
+ to:
+ subject: 系统通知
+ template: "
+ 服务名: {} ({})
+ 状态: {} ({})
+ 服务IP: {}
+ "
+ web-hook:
+ enabled: false
+ type: 0
+ url:
+ template: |
+ ### {}通知
+ - **服务名**: {}
+ - **实例ID**: {}
+ - **状态**: {}
+ - **服务IP**: {}