فهرست منبع

feature: 新增邮件告警模版 #I4U9BT

chenghua 4 سال پیش
والد
کامیت
cf965672b3

+ 11 - 0
manager/pom.xml

@@ -52,6 +52,16 @@
             <artifactId>spring-boot-configuration-processor</artifactId>
             <optional>true</optional>
         </dependency>
+        <!--thymeleaf依赖-->
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-thymeleaf</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>net.sourceforge.nekohtml</groupId>
+            <artifactId>nekohtml</artifactId>
+            <version>1.9.22</version>
+        </dependency>
         <!-- data jdbc -->
         <dependency>
             <groupId>org.springframework.boot</groupId>
@@ -108,6 +118,7 @@
                     <include>sureness.yml</include>
                     <include>banner.txt</include>
                     <include>define/**</include>
+                    <include>**/*.html</include>
                 </includes>
             </resource>
         </resources>

+ 22 - 16
manager/src/main/java/com/usthe/manager/component/alerter/DispatchAlarm.java

@@ -7,17 +7,19 @@ import com.usthe.alert.service.AlertService;
 import com.usthe.common.util.CommonConstants;
 import com.usthe.common.entity.manager.Monitor;
 import com.usthe.common.entity.manager.NoticeReceiver;
+import com.usthe.manager.service.MailService;
 import com.usthe.manager.service.MonitorService;
 import com.usthe.manager.service.NoticeConfigService;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.http.HttpStatus;
 import org.springframework.http.ResponseEntity;
-import org.springframework.mail.SimpleMailMessage;
 import org.springframework.mail.javamail.JavaMailSender;
+import org.springframework.mail.javamail.MimeMessageHelper;
 import org.springframework.stereotype.Component;
 import org.springframework.web.client.ResourceAccessException;
 import org.springframework.web.client.RestTemplate;
 
+import javax.mail.internet.MimeMessage;
 import java.util.Date;
 import java.util.List;
 
@@ -37,10 +39,11 @@ public class DispatchAlarm {
     private NoticeConfigService noticeConfigService;
     private JavaMailSender javaMailSender;
     private RestTemplate restTemplate;
+    private MailService mailService;
 
     public DispatchAlarm(AlerterWorkerPool workerPool, AlerterDataQueue dataQueue,
-                         JavaMailSender javaMailSender,NoticeConfigService noticeConfigService,
-                         AlertService alertService, MonitorService monitorService, RestTemplate restTemplate) {
+                         JavaMailSender javaMailSender, NoticeConfigService noticeConfigService,
+                         AlertService alertService, MonitorService monitorService, RestTemplate restTemplate, MailService mailService) {
         this.workerPool = workerPool;
         this.dataQueue = dataQueue;
         this.alertService = alertService;
@@ -48,6 +51,7 @@ public class DispatchAlarm {
         this.noticeConfigService = noticeConfigService;
         this.javaMailSender = javaMailSender;
         this.restTemplate = restTemplate;
+        this.mailService = mailService;
         startDispatch();
     }
 
@@ -137,19 +141,21 @@ public class DispatchAlarm {
         }
     }
 
-    private void sendEmailAlert(NoticeReceiver receiver, Alert alert) {
-        SimpleMailMessage message = new SimpleMailMessage();
-        message.setSubject("TanCloud探云-监控告警");
-        message.setFrom("gongchao@tancloud.cn");
-        message.setTo(receiver.getEmail());
-        message.setSentDate(new Date());
-        message.setText("探云TanCloud-监控告警\n" +
-                "告警目标对象: " + alert.getTarget() + "\n" +
-                "所属监控ID: " + alert.getMonitorId() + "\n" +
-                "所属监控名称: " + alert.getMonitorName() + "\n" +
-                "告警级别: " + alert.getPriority() + "\n" +
-                "告警详情: \n" + alert.getContent());
-        javaMailSender.send(message);
+
+    private void sendEmailAlert(final NoticeReceiver receiver,final Alert alert){
+        try{
+            MimeMessage mimeMessage = javaMailSender.createMimeMessage();
+            MimeMessageHelper messageHelper = new MimeMessageHelper(mimeMessage,true,"UTF-8");
+            messageHelper.setSubject("TanCloud探云-监控告警"); //设置邮件主题
+            messageHelper.setFrom("gongchao@tancloud.cn"); //设置发件人Email
+            messageHelper.setTo(receiver.getEmail());        //设定收件人Email
+            messageHelper.setSentDate(new Date());
+            String process = mailService.buildHTMLTemplate(alert);
+            messageHelper.setText(process,true);   //设置邮件内容模版
+            javaMailSender.send(mimeMessage);
+        }catch (Exception e){
+            log.error("[邮箱告警] error,Exception information={}",e);
+        }
     }
 
     private List<NoticeReceiver> matchReceiverByNoticeRules(Alert alert) {

+ 24 - 0
manager/src/main/java/com/usthe/manager/service/MailService.java

@@ -0,0 +1,24 @@
+package com.usthe.manager.service;
+
+import com.usthe.common.entity.alerter.Alert;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.core.annotation.Order;
+import org.springframework.stereotype.Service;
+
+/**
+ * 邮箱发送服务
+ *
+ * @author 花城
+ * @version 1.0
+ * @date 2022/2/19 6:11 下午
+ * @Description
+ */
+public interface MailService {
+
+    /**
+     * 构建告警邮件模版
+     * @param alert     告警信息
+     * @return          邮件内容
+     */
+    String buildHTMLTemplate(Alert alert);
+}

+ 39 - 0
manager/src/main/java/com/usthe/manager/service/impl/MailServiceImpl.java

@@ -0,0 +1,39 @@
+package com.usthe.manager.service.impl;
+
+import com.usthe.common.entity.alerter.Alert;
+import com.usthe.manager.service.MailService;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.thymeleaf.TemplateEngine;
+import org.thymeleaf.context.Context;
+
+import javax.annotation.Resource;
+
+/**
+ * 邮箱发送服务接口实现类
+ *
+ * @author 花城
+ * @version 1.0
+ * @date 2022/2/19 6:13 下午
+ * @Description
+ */
+@Slf4j
+@Service
+public class MailServiceImpl implements MailService {
+
+    @Resource
+    private TemplateEngine templateEngine;
+
+    @Override
+    public String buildHTMLTemplate(final Alert alert) {
+        //引入thymeleaf上下文参数渲染页面
+        Context context = new Context();
+        context.setVariable("target",alert.getTarget());
+        context.setVariable("ID",alert.getMonitorId());
+        context.setVariable("name",alert.getMonitorName());
+        context.setVariable("priority",alert.getPriority());
+        context.setVariable("content",alert.getContent());
+        return templateEngine.process("mailAlarm", context);
+    }
+}

+ 15 - 5
manager/src/main/resources/application.yml

@@ -27,9 +27,9 @@ spring:
       on-profile: prod
   datasource:
     driver-class-name: com.mysql.cj.jdbc.Driver
-    username: admin
-    password: admin
-    url: jdbc:mysql://localhost:3306/hertzbeat?useUnicode=true&characterEncoding=utf-8&useSSL=false
+    username: root
+    password: wang1027
+    url: jdbc:mysql://121.40.113.44:3306/hertzbeat?useUnicode=true&characterEncoding=utf-8&useSSL=false
     platform: mysql
     hikari:
       max-lifetime: 120000
@@ -38,8 +38,8 @@ spring:
 
   mail:
     host: smtp.exmail.qq.com
-    username: example@tancloud.cn
-    password: example
+    username: gongchao@tancloud.cn
+    password: C7Roz9Qe3eGkiVM9
     port: 465
     default-encoding: UTF-8
     properties:
@@ -48,6 +48,16 @@ spring:
           socketFactoryClass: javax.net.ssl.SSLSocketFactory
           ssl:
             enable: true
+        debug: false
+
+  thymeleaf:
+    prefix: classpath:/templates/  #prefix:指定模板所在的目录
+    check-template-location: true  #check-tempate-location: 检查模板路径是否存在
+    cache: false  #cache: 是否缓存,开发模式下设置为false,避免改了模板还要重启服务器,线上设置为true,可以提高性能。
+    suffix: .html
+    #encoding: UTF-8
+    #content-type: text/html
+    mode: LEGACYHTML5
 
 warehouse:
   store:

+ 323 - 0
manager/src/main/resources/templates/mailAlarm.html

@@ -0,0 +1,323 @@
+<!DOCTYPE html>
+<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org" lang="en">
+<head>
+    <meta charset="utf-8">
+    <meta http-equiv="X-UA-Compatible" content="IE=edge">
+    <meta name="viewport" content="width=device-width, initial-scale=1">
+
+    <style type="text/css">
+        body {
+            font-family: sans-serif;
+        }
+    </style>
+
+    <style id="table">
+
+        * {
+            margin: 0;
+            padding: 0
+        }
+
+        a {
+            color: #576b95;
+            text-decoration: none
+        }
+
+        html {
+            line-height: 1.6;
+        }
+
+        body {
+            margin: 0;
+            padding: 0 ;
+            font-size: 17px;
+            color: #333;
+            width:100%;
+            max-width:750px
+        }
+
+
+        .view {
+            word-break: break-word;
+            cursor: text;
+            min-height: 440px;
+            word-wrap: break-word;
+            -webkit-hyphens: auto;
+            -ms-hyphens: auto;
+            hyphens: auto;
+        }
+
+        p {
+            clear: both
+        }
+
+        img {
+            *zoom: 1;
+            max-width: 100%;
+            *max-width: 96%;
+            height: auto !important
+        }
+
+        iframe {
+            width: 301px !important;
+            border: 0;
+            /*background-color: none;*/
+        }
+
+        .selectTdClass {
+            background-color: #edf5fa !important
+        }
+
+        table.noBorderTable td,
+        table.noBorderTable th,
+        table.noBorderTable caption {
+            border: 1px dashed #ddd !important
+        }
+
+        table {
+            margin-bottom: 10px;
+            border-collapse: collapse;
+            display: table;
+        }
+
+        td,
+        th {
+            padding: 5px 10px;
+            border: 1px solid #DDD;
+        }
+
+        caption {
+            border: 1px dashed #DDD;
+            border-bottom: 0;
+            padding: 3px;
+            text-align: center;
+        }
+
+        th {
+            border-top: 2px solid #BBB;
+            background: #F7F7F7;
+        }
+
+
+        td p {
+            margin: 0;
+            padding: 0;
+        }
+    </style>
+    <style id="list">
+        ol,
+        ul {
+            margin: 0;
+            padding: 0;
+            -webkit-box-sizing: border-box;
+            box-sizing: border-box;
+            width: 99.9%
+        }
+
+        li {
+            clear: both;
+        }
+
+        .list-paddingleft-1 {
+            padding-left: 1.2em
+        }
+
+        .list-paddingleft-2 {
+            padding-left: 2.2em
+        }
+
+        .list-paddingleft-3 {
+            padding-left: 3.2em
+        }
+    </style>
+    <style>
+        body :not(img) {
+            max-width: 100% !important;
+            word-wrap: break-word !important;
+            box-sizing: border-box !important;
+        }
+
+        body img {
+            max-width: 100% !important;
+            word-wrap: break-word !important;
+        }
+
+        body table {
+            box-sizing: content-box !important;
+        }
+
+        body table * {
+            box-sizing: content-box !important;
+        }
+
+        body p {
+            clear: both;
+            min-height: 1em;
+        }
+
+        .mpa-black-tech {
+            cursor: default;
+            user-select: none;
+        }
+
+        .mpa-dynamic-material {
+            cursor: default;
+            user-select: none;
+        }
+    </style>
+</head>
+<body class="view" spellcheck="false" style="cursor: text">
+<section style="display: flex; justify-content: center; align-items: center; width: 100%; max-width:750px">
+    <section style="width: 1000vw;">
+        <p>&ZeroWidthSpace;<br></p>
+        <section
+                style="display: flex;justify-content: center;align-items: center;width: 100%;">
+            <section
+                    style="display: flex; justify-content: flex-start; align-items: center; flex-direction: column; width: 100%; padding: 16px 6px 16px 12px; border-width: 1px; border-style: solid; border-color: rgb(0, 0, 0); background: url(https://pic.imgdb.cn/item/616572c82ab3f51d91208003.png) 0% 0% / 100% 100% no-repeat rgb(212, 234, 237);">
+                <section
+                        style="display: flex;justify-content: flex-start;align-items: center;flex-direction: column;width: 100%;">
+                    <section
+                            style="height: 0px;width: 52px;align-self: flex-start;z-index: 1;transform: translate(17px, -9px);">
+                        <img th:src="@{https://pic.imgdb.cn/item/61657b052ab3f51d912bcf60.png}"
+                             style="display: block;">
+                    </section>
+                    <section
+                            style="display: flex; justify-content: flex-start; align-items: center; flex-direction: column; width: 100%; background: rgb(232, 232, 232); border-width: 1px; border-style: solid; border-color: rgb(0, 0, 0);">
+                        <section
+                                style="background: rgb(255, 255, 255); border-width: 1px; border-style: solid; border-color: rgb(0, 0, 0); transform: translate(-5px, -5px); width: 100%;">
+                            <section
+                                    style="display: flex;justify-content: center;align-items: center;justify-content: space-between;">
+                                <section
+                                        style="width: 11px; height: 11px; border-width: 1px 0px 0px 1px; border-top-style: solid; border-left-style: solid; border-top-color: rgb(151, 151, 151); border-left-color: rgb(151, 151, 151); border-right-style: initial; border-right-color: initial; border-bottom-style: initial; border-bottom-color: initial; margin-left: 4px; margin-top: 4px;">
+                                    <br>
+                                </section>
+                                <section
+                                        style="width: 11px; height: 11px; border-width: 1px 1px 0px 0px; border-top-style: solid; border-right-style: solid; border-top-color: rgb(151, 151, 151); border-right-color: rgb(151, 151, 151); border-bottom-style: initial; border-bottom-color: initial; border-left-style: initial; border-left-color: initial; margin-right: 4px; margin-top: 4px;">
+                                    <br>
+                                </section>
+                            </section>
+                            <section
+                                    style="display: flex;justify-content: center;align-items: center;width: 100%;">
+                                <section style="width: 66px;margin: 0px 13px 0px 16px;"><img
+                                        th:src="@{https://pic.imgdb.cn/item/61656ad22ab3f51d9114d897.jpg}"
+                                        style="display: block; width: 110px; height: 110px;">
+                                </section>
+                                <section style="padding-right: 12px;">
+                                    <section style="margin-top: 15px;">
+                                        <p
+                                                style="font-size: 14px;font-family: PingFangSC-Semibold, PingFang SC;font-weight: bold;color: #B1B1B1;line-height: 20px;letter-spacing: 5px;">
+                                            TanCloud探云-监控告警
+                                        </p>
+                                    </section>
+                                    <section style="padding: 8px 0px 4px 0px;">
+                                        <p
+                                                style="font-size: 24px;font-family: PingFangSC-Semibold, PingFang SC;font-weight: bold;color: #000000;line-height: 33px;letter-spacing: 2px;">
+                                            告警通知
+                                        </p>
+                                    </section>
+                                    <section style="margin-bottom: 15px;">
+                                        <p
+                                                style="font-size: 16px;font-family: PingFangSC-Semibold, PingFang SC;font-weight: bold;color: #B1B1B1;line-height: 22px;">
+                                            Alarm notification
+                                        </p>
+                                    </section>
+                                </section>
+                            </section>
+                            <section
+                                    style="display: flex;justify-content: center;align-items: center;justify-content: space-between;">
+                                <section
+                                        style="width: 11px; height: 11px; border-width: 0px 0px 1px 1px; border-bottom-style: solid; border-left-style: solid; border-bottom-color: rgb(151, 151, 151); border-left-color: rgb(151, 151, 151); border-top-style: initial; border-top-color: initial; border-right-style: initial; border-right-color: initial; margin-left: 4px; margin-bottom: 4px;">
+                                    <br>
+                                </section>
+                                <section
+                                        style="width: 11px; height: 11px; border-width: 0px 1px 1px 0px; border-right-style: solid; border-bottom-style: solid; border-right-color: rgb(151, 151, 151); border-bottom-color: rgb(151, 151, 151); border-top-style: initial; border-top-color: initial; border-left-style: initial; border-left-color: initial; margin-right: 4px; margin-bottom: 4px;">
+                                    <br>
+                                </section>
+                            </section>
+                        </section>
+                    </section>
+                    <section
+                            style="width: 100%; background: rgb(203, 221, 230); border-width: 1px; border-style: solid; border-color: rgb(0, 0, 0); margin-left: -8px; margin-top: 6px; z-index: 1;">
+                        <section
+                                style="background: rgb(227, 245, 255); border-width: 1px; border-style: solid; border-color: rgb(0, 0, 0); transform: translate(4px, 4px); text-align: center; padding: 12px 28px; white-space: nowrap;">
+                            <p
+                                    style="font-size: 16px;font-family: PingFangSC-Medium, PingFang SC;font-weight: bold;color: #000000;line-height: 22px;">
+                                TanCloud探云
+                            </p>
+                        </section>
+                    </section>
+                    <section
+                            style="width: 52px;height: 0px;z-index: 1;align-self: flex-end;transform: translate(-10px, -3px);">
+                        <img th:src="@{https://pic.imgdb.cn/item/61657b052ab3f51d912bcf60.png}"
+                             style="display: block;">
+                    </section>
+                </section>
+            </section>
+        </section>
+        <p><br></p>
+        <section
+                style="display: flex;justify-content: center;align-items: center;width: 100%;padding:0;">
+            <section
+                    style="display: flex; justify-content: flex-start; align-items: center; flex-direction: column; width: 100%; padding: 16px; background: rgb(212, 234, 237); border-width: 1px; border-style: solid; border-color: rgb(0, 0, 0);">
+                <section
+                        style="display: flex;justify-content: flex-start;align-items: center;flex-direction: column;width: 100%;">
+                    <section
+                            style="display: flex; justify-content: flex-start; align-items: center; flex-direction: column; width: 100%; background: rgb(232, 232, 232); border-width: 1px; border-style: solid; border-color: rgb(0, 0, 0);">
+                        <section
+                                style="background: rgb(255, 255, 255); border-width: 1px; border-style: solid; border-color: rgb(0, 0, 0); transform: translate(-5px, -5px); width: 100%;">
+                            <section
+                                    style="display: flex;justify-content: center;align-items: center;justify-content: space-between;">
+                                <section
+                                        style="width: 11px; height: 11px; border-width: 1px 0px 0px 1px; border-top-style: solid; border-left-style: solid; border-top-color: rgb(151, 151, 151); border-left-color: rgb(151, 151, 151); border-right-style: initial; border-right-color: initial; border-bottom-style: initial; border-bottom-color: initial; margin-left: 4px; margin-top: 4px;">
+                                    <br>
+                                </section>
+                                <section
+                                        style="width: 11px; height: 11px; border-width: 1px 1px 0px 0px; border-top-style: solid; border-right-style: solid; border-top-color: rgb(151, 151, 151); border-right-color: rgb(151, 151, 151); border-bottom-style: initial; border-bottom-color: initial; border-left-style: initial; border-left-color: initial; margin-right: 4px; margin-top: 4px;">
+                                    <br>
+                                </section>
+                            </section>
+                            <section
+                                    style="display: flex;justify-content: center;align-items: center;width: 100%;padding: 6px 0px;">
+                                <section style="text-align: center;">
+                                    <p style="font-size: 14px; font-family: PingFangSC-Medium,PingFang SC; font-weight: bold; color: rgb(0, 0, 0); line-height: 22px; letter-spacing: 1px;text-align: left;padding-left: 5px;">
+                                        告警目标对象:<span th:text="${target}"></span><br>
+                                        &nbsp;&nbsp;&nbsp;&nbsp;所属监控ID:<span th:text="${ID}"></span><br>
+                                        &nbsp;&nbsp;&nbsp;&nbsp;所属监控名称:<span th:text="${name}"></span><br>
+                                        &nbsp;&nbsp;&nbsp;&nbsp;告警级别:<span th:text="${priority}"></span><br>
+                                        &nbsp;&nbsp;&nbsp;&nbsp;告警详情:<br>
+                                    <span th:text="${content}"></span><br>
+                                    </p>
+                                    <p
+                                            style="font-size: 14px; font-family: PingFangSC-Medium,PingFang SC; font-weight: bold; color: rgb(0, 0, 0); line-height: 22px; letter-spacing: 1px;">
+                                        <br>
+                                    </p>
+                                </section>
+                            </section>
+                            <section
+                                    style="display: flex;justify-content: center;align-items: center;justify-content: space-between;">
+                                <section
+                                        style="width: 11px; height: 11px; border-width: 0px 0px 1px 1px; border-bottom-style: solid; border-left-style: solid; border-bottom-color: rgb(151, 151, 151); border-left-color: rgb(151, 151, 151); border-top-style: initial; border-top-color: initial; border-right-style: initial; border-right-color: initial; margin-left: 4px; margin-bottom: 4px;">
+                                    <br>
+                                </section>
+                                <section
+                                        style="width: 11px; height: 11px; border-width: 0px 1px 1px 0px; border-right-style: solid; border-bottom-style: solid; border-right-color: rgb(151, 151, 151); border-bottom-color: rgb(151, 151, 151); border-top-style: initial; border-top-color: initial; border-left-style: initial; border-left-color: initial; margin-right: 4px; margin-bottom: 4px;">
+                                    <br>
+                                </section>
+                            </section>
+                        </section>
+                    </section>
+                </section>
+            </section>
+        </section>
+        <p><br></p>
+        <p><br></p>
+
+    </section>
+</section>
+</section>
+</section>
+</section>
+</section>
+</section>
+</body>
+</html>

+ 5 - 4
web-app/package.json

@@ -41,6 +41,7 @@
     "@delon/util": "^12.4.2",
     "ajv": "^8.6.2",
     "ajv-formats": "^2.1.1",
+    "ajv-keywords": "^5.1.0",
     "echarts": "^5.2.2",
     "ng-alain": "^12.4.2",
     "ng-zorro-antd": "^12.0.2",
@@ -84,12 +85,12 @@
     "ng-alain-plugin-theme": "^12.0.0",
     "prettier": "^2.2.1",
     "source-map-explorer": "^2.5.2",
-    "stylelint": "^13.13.1",
+    "stylelint": "^14.5.1",
     "stylelint-config-prettier": "^8.0.2",
-    "stylelint-config-rational-order": "^0.1.2",
-    "stylelint-config-standard": "^22.0.0",
+    "stylelint-config-rational-order": "^0.0.4",
+    "stylelint-config-standard": "^25.0.0",
     "stylelint-declaration-block-no-ignored-properties": "^2.4.0",
-    "stylelint-order": "^4.1.0",
+    "stylelint-order": "^5.0.0",
     "typescript": "~4.3.5"
   },
   "lint-staged": {