feat: 1.4.0

1. 新增飞书告警
This commit is contained in:
byteblogs168 2023-05-31 15:38:11 +08:00
parent 1d0e792eb0
commit 373c72e35d
30 changed files with 153 additions and 18 deletions

View File

@ -0,0 +1,16 @@
package com.aizuda.easy.retry.common.core.alarm;
import lombok.Data;
/**
* 飞书地址
*
* @author: www.byteblogs.com
* @date : 2023-05-31 13:45
* @since 1.4.0
*/
@Data
public class LarkAttribute {
private String larkUrl;
}

View File

@ -0,0 +1,114 @@
package com.aizuda.easy.retry.common.core.alarm.strategy;
import cn.hutool.http.ContentType;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import cn.hutool.http.HttpUtil;
import com.aizuda.easy.retry.common.core.alarm.AlarmContext;
import com.aizuda.easy.retry.common.core.alarm.LarkAttribute;
import com.aizuda.easy.retry.common.core.enums.AlarmTypeEnum;
import com.aizuda.easy.retry.common.core.log.LogUtils;
import com.aizuda.easy.retry.common.core.util.JsonUtil;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Builder;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* 飞书通知
*
* @author: www.byteblogs.com
* @date : 2021-11-25 09:20
* @since 1.4.0
*/
@Component
@Slf4j
public class LarkAlarm extends AbstractAlarm<AlarmContext> {
@Override
public Integer getAlarmType() {
return AlarmTypeEnum.LARK.getValue();
}
@Override
public boolean asyncSendMessage(AlarmContext context) {
threadPoolExecutor.execute(() -> {
syncSendMessage(context);
});
return true;
}
@Override
public boolean syncSendMessage(AlarmContext context) {
LarkAttribute larkAttribute = JsonUtil.parseObject(context.getNotifyAttribute(), LarkAttribute.class);
Map<String, Object> map = new HashMap<>();
map.put("header", buildHeader(context.getTitle()));
map.put("elements", buildElements(context.getText()));
LarkMessage builder = LarkMessage.builder()
.msgType("interactive")
.card(map).build();
try {
HttpRequest post = HttpUtil.createPost(larkAttribute.getLarkUrl());
HttpRequest request = post.body(JsonUtil.toJsonString(builder), ContentType.JSON.toString());
HttpResponse execute = request.execute();
LogUtils.debug(log, JsonUtil.toJsonString(execute));
} catch (Exception e) {
log.error("发送lark消息失败", e);
return false;
}
return true;
}
private List buildElements(final String text) {
Map<String, String> map = new HashMap<>();
map.put("tag", "markdown");
map.put("content", text);
return Collections.singletonList(map);
}
private Map<String, Object> buildHeader(final String title) {
Map<String, Object> map = new HashMap<>();
map.put("template", "red");
Map<String, String> titleMap = new HashMap<>();
titleMap.put("content", title);
titleMap.put("tag", "plain_text");
map.put("title", titleMap);
return map;
}
@Override
public boolean asyncSendMessage(List<AlarmContext> notifierElements) {
for (AlarmContext notifierElement : notifierElements) {
asyncSendMessage(notifierElement);
}
return Boolean.TRUE;
}
@Data
@Builder
private static class LarkMessage {
@JsonProperty("msg_type")
private String msgType;
private Map<String, Object> card;
}
}

View File

@ -22,6 +22,11 @@ public enum AlarmTypeEnum {
* 企业通知
*/
QI_YE_WECHAT(3),
/**
* 飞书
*/
LARK(4),
;
private final int value;

View File

@ -36,16 +36,16 @@ public class AlarmNotifyThreadSchedule {
private static final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
private static String retryErrorMoreThresholdTextMessageFormatter =
"<font face=\"微软雅黑\" color=#ff0000 size=4>{}环境 重试失败数据监控</font> </br>" +
"> 名称:{} </br>" +
"> 时间窗口:{} ~ {} </br>" +
"<font face=\"微软雅黑\" color=#ff0000 size=4>{}环境 重试失败数据监控</font> \n" +
"> 名称:{} \n" +
"> 时间窗口:{} ~ {} \n" +
"> **共计:{}** \n";
private static String retryTaskMoreThresholdTextMessageFormatter =
"<font face=\"微软雅黑\" color=#ff0000 size=4>{}环境 重试数据监控</font> </br>" +
"> 名称:{} </br>" +
"> 时间:{} </br>" +
"> **共计:{}** </br>";
"<font face=\"微软雅黑\" color=#ff0000 size=4>{}环境 重试数据监控</font> \n" +
"> 名称:{} \n" +
"> 时间:{} \n" +
"> **共计:{}** \n";
@Autowired
private RetryDeadLetterMapper retryDeadLetterMapper;
@ -60,8 +60,8 @@ public class AlarmNotifyThreadSchedule {
/**
* 监控重试表中数据总量是否到达阈值
*/
@Scheduled(cron = "0 0/10 * * * ?")
@SchedulerLock(name = "retryTaskMoreThreshold", lockAtMostFor = "PT10M", lockAtLeastFor = "PT10M")
@Scheduled(cron = "0 0/1 * * * ?")
@SchedulerLock(name = "retryTaskMoreThreshold", lockAtMostFor = "PT1M", lockAtLeastFor = "PT1M")
public void retryTaskMoreThreshold() {
LogUtils.info(log, "retryTaskMoreThreshold time[{}] ip:[{}]", LocalDateTime.now(), HostUtils.getIp());

View File

@ -1 +1 @@
<!DOCTYPE html><html lang="zh-cmn-Hans"><head><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width,initial-scale=1"><link rel="icon" href="/logo.png"><title>Easy-Retry</title><style>.first-loading-wrp{display:flex;justify-content:center;align-items:center;flex-direction:column;min-height:420px;height:100%}.first-loading-wrp>h1{font-size:128px}.first-loading-wrp .loading-wrp{padding:98px;display:flex;justify-content:center;align-items:center}.dot{animation:antRotate 1.2s infinite linear;transform:rotate(45deg);position:relative;display:inline-block;font-size:32px;width:32px;height:32px;box-sizing:border-box}.dot i{width:14px;height:14px;position:absolute;display:block;background-color:#1890ff;border-radius:100%;transform:scale(.75);transform-origin:50% 50%;opacity:.3;animation:antSpinMove 1s infinite linear alternate}.dot i:nth-child(1){top:0;left:0}.dot i:nth-child(2){top:0;right:0;-webkit-animation-delay:.4s;animation-delay:.4s}.dot i:nth-child(3){right:0;bottom:0;-webkit-animation-delay:.8s;animation-delay:.8s}.dot i:nth-child(4){bottom:0;left:0;-webkit-animation-delay:1.2s;animation-delay:1.2s}@keyframes antRotate{to{-webkit-transform:rotate(405deg);transform:rotate(405deg)}}@-webkit-keyframes antRotate{to{-webkit-transform:rotate(405deg);transform:rotate(405deg)}}@keyframes antSpinMove{to{opacity:1}}@-webkit-keyframes antSpinMove{to{opacity:1}}</style><link href="/css/chunk-758b2aa4.1c9c785f.css" rel="prefetch"><link href="/css/chunk-77aaac2a.99cbbcce.css" rel="prefetch"><link href="/css/chunk-ff9025ec.cd4961ff.css" rel="prefetch"><link href="/css/user.6ccd4506.css" rel="prefetch"><link href="/js/chunk-2d0a4079.3bbb423d.js" rel="prefetch"><link href="/js/chunk-2d0b7230.fe38dce0.js" rel="prefetch"><link href="/js/chunk-2d0c8f97.72c6927b.js" rel="prefetch"><link href="/js/chunk-2d0d43f4.f5bb36b7.js" rel="prefetch"><link href="/js/chunk-2d0f085f.b8852e6c.js" rel="prefetch"><link href="/js/chunk-2d21a08f.6cb549d6.js" rel="prefetch"><link href="/js/chunk-2d228eef.955019b5.js" rel="prefetch"><link href="/js/chunk-40597980.5e57166b.js" rel="prefetch"><link href="/js/chunk-732da7b5.251276b0.js" rel="prefetch"><link href="/js/chunk-758b2aa4.b88de219.js" rel="prefetch"><link href="/js/chunk-77aaac2a.bb4621bb.js" rel="prefetch"><link href="/js/chunk-ff9025ec.a77618ce.js" rel="prefetch"><link href="/js/fail.fe1af659.js" rel="prefetch"><link href="/js/lang-zh-CN-account-settings.f8f25eaf.js" rel="prefetch"><link href="/js/lang-zh-CN-account.c724e71d.js" rel="prefetch"><link href="/js/lang-zh-CN-dashboard-analysis.d7cabd65.js" rel="prefetch"><link href="/js/lang-zh-CN-dashboard.5180154b.js" rel="prefetch"><link href="/js/lang-zh-CN-form-basicForm.ff3088ac.js" rel="prefetch"><link href="/js/lang-zh-CN-form.cc39e450.js" rel="prefetch"><link href="/js/lang-zh-CN-global.bf0df5c8.js" rel="prefetch"><link href="/js/lang-zh-CN-menu.25425a62.js" rel="prefetch"><link href="/js/lang-zh-CN-result-fail.232762aa.js" rel="prefetch"><link href="/js/lang-zh-CN-result-success.3519c60c.js" rel="prefetch"><link href="/js/lang-zh-CN-result.32c5cf1c.js" rel="prefetch"><link href="/js/lang-zh-CN-setting.8c2ce690.js" rel="prefetch"><link href="/js/lang-zh-CN-user.81513cba.js" rel="prefetch"><link href="/js/lang-zh-CN.3dfa92aa.js" rel="prefetch"><link href="/js/user.8401c9c6.js" rel="prefetch"><link href="/css/app.b0a9877f.css" rel="preload" as="style"><link href="/css/chunk-vendors.5be6e05a.css" rel="preload" as="style"><link href="/js/app.b84c86ee.js" rel="preload" as="script"><link href="/js/chunk-vendors.87c1e94d.js" rel="preload" as="script"><link href="/css/chunk-vendors.5be6e05a.css" rel="stylesheet"><link href="/css/app.b0a9877f.css" rel="stylesheet"></head><body><noscript><strong>We're sorry but vue-antd-pro doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id="app"><div class="first-loading-wrp"><h2>Easy-Retry</h2><div class="loading-wrp"><span class="dot dot-spin"><i></i><i></i><i></i><i></i></span></div><div style="display: flex; justify-content: center; align-items: center;">分布式重试服务平台</div></div></div><script src="//cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.min.js"></script><script src="//cdn.jsdelivr.net/npm/vue-router@3.5.1/dist/vue-router.min.js"></script><script src="//cdn.jsdelivr.net/npm/vuex@3.1.1/dist/vuex.min.js"></script><script src="//cdn.jsdelivr.net/npm/axios@0.21.1/dist/axios.min.js"></script><script src="/js/chunk-vendors.87c1e94d.js"></script><script src="/js/app.b84c86ee.js"></script></body></html>
<!DOCTYPE html><html lang="zh-cmn-Hans"><head><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width,initial-scale=1"><link rel="icon" href="/logo.png"><title>Easy-Retry</title><style>.first-loading-wrp{display:flex;justify-content:center;align-items:center;flex-direction:column;min-height:420px;height:100%}.first-loading-wrp>h1{font-size:128px}.first-loading-wrp .loading-wrp{padding:98px;display:flex;justify-content:center;align-items:center}.dot{animation:antRotate 1.2s infinite linear;transform:rotate(45deg);position:relative;display:inline-block;font-size:32px;width:32px;height:32px;box-sizing:border-box}.dot i{width:14px;height:14px;position:absolute;display:block;background-color:#1890ff;border-radius:100%;transform:scale(.75);transform-origin:50% 50%;opacity:.3;animation:antSpinMove 1s infinite linear alternate}.dot i:nth-child(1){top:0;left:0}.dot i:nth-child(2){top:0;right:0;-webkit-animation-delay:.4s;animation-delay:.4s}.dot i:nth-child(3){right:0;bottom:0;-webkit-animation-delay:.8s;animation-delay:.8s}.dot i:nth-child(4){bottom:0;left:0;-webkit-animation-delay:1.2s;animation-delay:1.2s}@keyframes antRotate{to{-webkit-transform:rotate(405deg);transform:rotate(405deg)}}@-webkit-keyframes antRotate{to{-webkit-transform:rotate(405deg);transform:rotate(405deg)}}@keyframes antSpinMove{to{opacity:1}}@-webkit-keyframes antSpinMove{to{opacity:1}}</style><link href="/css/chunk-758b2aa4.1c9c785f.css" rel="prefetch"><link href="/css/chunk-77aaac2a.99cbbcce.css" rel="prefetch"><link href="/css/chunk-ff9025ec.cd4961ff.css" rel="prefetch"><link href="/css/user.6ccd4506.css" rel="prefetch"><link href="/js/chunk-2d0a4079.3fd83a06.js" rel="prefetch"><link href="/js/chunk-2d0b7230.c3459f7c.js" rel="prefetch"><link href="/js/chunk-2d0c8f97.d91a8ff0.js" rel="prefetch"><link href="/js/chunk-2d0d43f4.bcedfa8c.js" rel="prefetch"><link href="/js/chunk-2d0f085f.182cc3eb.js" rel="prefetch"><link href="/js/chunk-2d21a08f.00dcc26b.js" rel="prefetch"><link href="/js/chunk-2d228eef.b6c79dd3.js" rel="prefetch"><link href="/js/chunk-40597980.defad4c9.js" rel="prefetch"><link href="/js/chunk-732da7b5.8f5bea60.js" rel="prefetch"><link href="/js/chunk-758b2aa4.05d3a08d.js" rel="prefetch"><link href="/js/chunk-77aaac2a.355915ce.js" rel="prefetch"><link href="/js/chunk-ff9025ec.72b2b536.js" rel="prefetch"><link href="/js/fail.525733e9.js" rel="prefetch"><link href="/js/lang-zh-CN-account-settings.f8f25eaf.js" rel="prefetch"><link href="/js/lang-zh-CN-account.f7a734c3.js" rel="prefetch"><link href="/js/lang-zh-CN-dashboard-analysis.d7cabd65.js" rel="prefetch"><link href="/js/lang-zh-CN-dashboard.ffd6ecbd.js" rel="prefetch"><link href="/js/lang-zh-CN-form-basicForm.ff3088ac.js" rel="prefetch"><link href="/js/lang-zh-CN-form.39cd9999.js" rel="prefetch"><link href="/js/lang-zh-CN-global.bf0df5c8.js" rel="prefetch"><link href="/js/lang-zh-CN-menu.25425a62.js" rel="prefetch"><link href="/js/lang-zh-CN-result-fail.232762aa.js" rel="prefetch"><link href="/js/lang-zh-CN-result-success.3519c60c.js" rel="prefetch"><link href="/js/lang-zh-CN-result.b3df3bc6.js" rel="prefetch"><link href="/js/lang-zh-CN-setting.8c2ce690.js" rel="prefetch"><link href="/js/lang-zh-CN-user.81513cba.js" rel="prefetch"><link href="/js/lang-zh-CN.6ae67127.js" rel="prefetch"><link href="/js/user.7c451146.js" rel="prefetch"><link href="/css/app.37a20ada.css" rel="preload" as="style"><link href="/css/chunk-vendors.5be6e05a.css" rel="preload" as="style"><link href="/js/app.11cd2525.js" rel="preload" as="script"><link href="/js/chunk-vendors.b8c3b6d4.js" rel="preload" as="script"><link href="/css/chunk-vendors.5be6e05a.css" rel="stylesheet"><link href="/css/app.37a20ada.css" rel="stylesheet"></head><body><noscript><strong>We're sorry but vue-antd-pro doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id="app"><div class="first-loading-wrp"><h2>Easy-Retry</h2><div class="loading-wrp"><span class="dot dot-spin"><i></i><i></i><i></i><i></i></span></div><div style="display: flex; justify-content: center; align-items: center;">分布式重试服务平台</div></div></div><script src="//cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.min.js"></script><script src="//cdn.jsdelivr.net/npm/vue-router@3.5.1/dist/vue-router.min.js"></script><script src="//cdn.jsdelivr.net/npm/vuex@3.1.1/dist/vuex.min.js"></script><script src="//cdn.jsdelivr.net/npm/axios@0.21.1/dist/axios.min.js"></script><script src="/js/chunk-vendors.b8c3b6d4.js"></script><script src="/js/app.11cd2525.js"></script></body></html>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -109,7 +109,7 @@
<a-input
placeholder="请输入飞书URL"
v-decorator="[
'feiShuUrl',
'larkUrl',
{rules: [{ required: true, message: '请输入飞书URL', whitespace: true}]}
]" />
</a-form-item>
@ -385,7 +385,7 @@ export default {
setTimeout(resolve, 1500)
}).then(() => {
const { form } = this
const formData = pick(record.notifyAttribute, ['dingDingUrl', 'feiShuUrl', 'user', 'pass', 'host', 'port', 'from', 'tos'])
const formData = pick(record.notifyAttribute, ['dingDingUrl', 'larkUrl', 'user', 'pass', 'host', 'port', 'from', 'tos'])
console.log(formData)
form.setFieldsValue(formData)
})
@ -422,7 +422,7 @@ export default {
if (record.notifyType === '1') {
s = '钉钉地址:' + text['dingDingUrl'] + ';'
} else if (record.notifyType === '4') {
s = '飞书地址:' + text['feiShuUrl'] + ';'
s = '飞书地址:' + text['larkUrl'] + ';'
}
return s