feat:email status monitor

This commit is contained in:
nieziyan 2023-11-23 16:33:30 +08:00
parent a732adbd92
commit 5bd6ecb437
8 changed files with 294 additions and 60 deletions

View File

@ -10,6 +10,7 @@ import org.springframework.format.annotation.DateTimeFormat;
import java.io.Serializable;
import java.time.LocalDate;
import java.util.Date;
@Data
@TableName(value = "sys_database")
@ -58,9 +59,9 @@ public class SysDatabase implements Serializable {
* 创建时间
*/
@TableField(value = "create_time")
@JsonFormat(pattern = "yyyy-MM-dd")
@DateTimeFormat(pattern = "yyyy-MM-dd")
private LocalDate createTime;
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date createTime;
/**
* 创建人
@ -72,9 +73,9 @@ public class SysDatabase implements Serializable {
* 修改时间
*/
@TableField(value = "update_time")
@JsonFormat(pattern = "yyyy-MM-dd")
@DateTimeFormat(pattern = "yyyy-MM-dd")
private LocalDate updateTime;
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date updateTime;
/**
* 修改人

View File

@ -10,6 +10,7 @@ import org.springframework.format.annotation.DateTimeFormat;
import java.io.Serializable;
import java.time.LocalDate;
import java.util.Date;
/**
* 邮件管理数据表
@ -64,9 +65,9 @@ public class SysEmail implements Serializable {
* 创建日期
*/
@TableField(value = "create_time")
@JsonFormat(pattern = "yyyy-MM-dd")
@DateTimeFormat(pattern = "yyyy-MM-dd")
private LocalDate createTime;
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date createTime;
/**
* 创建人员
@ -78,9 +79,9 @@ public class SysEmail implements Serializable {
* 修改日期
*/
@TableField(value = "update_time")
@JsonFormat(pattern = "yyyy-MM-dd")
@DateTimeFormat(pattern = "yyyy-MM-dd")
private LocalDate updateTime;
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date updateTime;
/**
* 修改人员

View File

@ -0,0 +1,27 @@
package org.jeecg.modules.base.enums;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import lombok.AllArgsConstructor;
import lombok.Getter;
@Getter
@AllArgsConstructor
public enum Enabled {
ENABLED(1, "启用"), DISENABLED(0, "禁用");
private Integer value;
private String desc;
public static Enabled valueOf(Integer value){
if (ObjectUtil.isNull(value))
return null;
for (Enabled enabled : Enabled.values()) {
if (enabled.getValue().compareTo(value) == 0)
return enabled;
}
return null;
}
}

View File

@ -7,6 +7,7 @@ import lombok.Getter;
@Getter
@AllArgsConstructor
public enum Item {
EMAIL_CONN("1", "邮箱服务连接情况"),
DATABASE_CONN("2", "数据源连接情况");
private String value;

View File

@ -9,6 +9,7 @@ import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.jeecg.common.api.vo.Result;
import org.jeecg.common.constant.CommonConstant;
import org.jeecg.common.constant.Prompt;
import org.jeecg.common.constant.RedisConstant;
import org.jeecg.common.constant.SymbolConstant;
@ -20,6 +21,7 @@ import org.jeecg.modules.base.dto.SourceDto;
import org.jeecg.modules.base.entity.Rule;
import org.jeecg.modules.base.entity.postgre.AlarmRule;
import org.jeecg.modules.base.bizVo.AlarmRuleVo;
import org.jeecg.modules.base.enums.Enabled;
import org.jeecg.modules.mapper.AlarmRuleMapper;
import org.jeecg.modules.service.IAlarmRuleService;
import org.jeecg.modules.service.ISysDatabaseService;
@ -31,6 +33,7 @@ import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;
import java.util.*;
import static org.jeecg.modules.base.enums.Enabled.ENABLED;
import static org.jeecg.modules.base.enums.SourceType.*;
@Service("alarmRuleService")
@ -84,7 +87,8 @@ public class AlarmRuleServiceImpl extends ServiceImpl<AlarmRuleMapper, AlarmRule
}
boolean success = save(alarmRule);
if (success){
rule2Redis();
// 将新增的规则同步到redis
addRule(alarmRule);
return Result.OK(Prompt.ADD_SUCC);
}
return Result.error(Prompt.ADD_ERR);
@ -92,12 +96,13 @@ public class AlarmRuleServiceImpl extends ServiceImpl<AlarmRuleMapper, AlarmRule
@Override
public Result<?> update(AlarmRule alarmRule) {
LambdaQueryWrapper<AlarmRule> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(AlarmRule::getId, alarmRule.getId());
AlarmRule alarmRuleOld = this.baseMapper.selectOne(wrapper);
AlarmRule alarmRuleOld = getById(alarmRule.getId());
if (ObjectUtil.isNull(alarmRuleOld)){
return Result.error(Prompt.DATA_NOT_EXITS);
}
String ruleId = alarmRuleOld.getId();
// 保存oldRule的sourceType 避免修改了sourceType
String sourceTypeOld = alarmRuleOld.getSourceType();
Rule rule = alarmRule.getRule();
if (ObjectUtil.isNotNull(rule)){
String operator = JSON.toJSONString(rule);
@ -105,7 +110,7 @@ public class AlarmRuleServiceImpl extends ServiceImpl<AlarmRuleMapper, AlarmRule
}
boolean success = updateById(alarmRule);
if (success){
rule2Redis();
updateRule(ruleId, sourceTypeOld);
return Result.OK(Prompt.UPDATE_SUCC);
}
return Result.error(Prompt.UPDATE_ERR);
@ -113,9 +118,10 @@ public class AlarmRuleServiceImpl extends ServiceImpl<AlarmRuleMapper, AlarmRule
@Override
public Result<?> deleteById(String alarmRuleId) {
AlarmRule alarmRule = getById(alarmRuleId);
boolean success = removeById(alarmRuleId);
if (success){
rule2Redis();
delRule(alarmRule);
return Result.OK(Prompt.DELETE_SUCC);
}
return Result.error(Prompt.DELETE_ERR);
@ -140,29 +146,22 @@ public class AlarmRuleServiceImpl extends ServiceImpl<AlarmRuleMapper, AlarmRule
alarmRule.setId(alarmRuleId);
alarmRule.setEnabled(enabled);
if (updateById(alarmRule)){
rule2Redis();
updateRule(alarmRuleId, enabled);
return Result.OK(Prompt.UPDATE_SUCC);
}
return Result.error(Prompt.UPDATE_ERR);
}
/*
* 将所有警告规则放到Redis缓存
* 1.规则表数据变动
* 2.系统启动后
* 系统启动后将所有警告规则放到Redis缓存
**/
@PostConstruct
public void rule2Redis(){
String colon = SymbolConstant.COLON;
String prefixRule = RedisConstant.PREFIX_RULE;
// 收集所有待删除的key
List<String> keys = new ArrayList<>();
keys.addAll(redisStreamUtil.keys(prefixRule + DATABASE.getType()));
keys.addAll(redisStreamUtil.keys(prefixRule + EMAIL.getType()));
keys.addAll(redisStreamUtil.keys(prefixRule + SERVER.getType()));
LambdaQueryWrapper<AlarmRule> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(AlarmRule::getEnabled,1);
wrapper.eq(AlarmRule::getEnabled, ENABLED.getValue());
List<AlarmRule> alarmRules = this.list(wrapper);
Map<String, AlarmRule> ruleMap = new HashMap<>();
for (AlarmRule alarmRule : alarmRules) {
@ -171,9 +170,70 @@ public class AlarmRuleServiceImpl extends ServiceImpl<AlarmRuleMapper, AlarmRule
String ruleKey = prefixRule + sourceType + colon + ruleId;
ruleMap.put(ruleKey,alarmRule);
}
// 删除旧规则
redisUtil.del(ArrayUtil.toArray(keys, String.class));
// 添加修改后的规则
redisStreamUtil.setRules(ruleMap);
}
private void delRule(AlarmRule alarmRule){
String colon = SymbolConstant.COLON;
String prefixRule = RedisConstant.PREFIX_RULE;
String ruleId = alarmRule.getId();
String sourceType = alarmRule.getSourceType();
String ruleKey = prefixRule + sourceType + colon + ruleId;
if (redisUtil.hasKey(ruleKey))
redisUtil.del(ruleKey);
}
private void addRule(AlarmRule alarmRule){
String colon = SymbolConstant.COLON;
String prefixRule = RedisConstant.PREFIX_RULE;
String ruleId = alarmRule.getId();
String sourceType = alarmRule.getSourceType();
String ruleKey = prefixRule + sourceType + colon + ruleId;
redisUtil.set(ruleKey, alarmRule);
}
/*
* 修改规则内容
* */
private void updateRule(String ruleId, String sourceTypeOld){
// 删除旧的rule(避免修改sourceType后,Reidis的key改变,旧的rule无法被新的覆盖)
String colon = SymbolConstant.COLON;
String prefixRule = RedisConstant.PREFIX_RULE;
AlarmRule alarmRule = getById(ruleId);
String ruleKeyOld = prefixRule + sourceTypeOld + colon + ruleId;
if (redisUtil.hasKey(ruleKeyOld))
redisUtil.del(ruleKeyOld);
String sourceType = alarmRule.getSourceType();
String ruleKey = prefixRule + sourceType + colon + ruleId;
redisUtil.set(ruleKey, alarmRule);
}
/*
* 修改规则状态 启用|禁用
* */
private void updateRule(String ruleId, Integer enabled){
String colon = SymbolConstant.COLON;
String prefixRule = RedisConstant.PREFIX_RULE;
AlarmRule alarmRule = getById(ruleId);
String sourceType = alarmRule.getSourceType();
String ruleKey = prefixRule + sourceType + colon + ruleId;
Enabled enabledE = Enabled.valueOf(enabled);
if (ObjectUtil.isNotNull(enabledE)){
switch (enabledE){
case ENABLED:
redisUtil.set(ruleKey, alarmRule);
break;
case DISENABLED:
if (redisUtil.hasKey(ruleKey))
redisUtil.del(ruleKey);
break;
default:
break;
}
}
}
}

View File

@ -5,7 +5,6 @@ import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.ReUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.druid.util.JdbcUtils;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
@ -16,7 +15,6 @@ import org.jeecg.common.constant.enums.DbType;
import org.jeecg.common.system.vo.DictModel;
import org.jeecg.common.util.JDBCUtil;
import org.jeecg.common.util.RedisUtil;
import org.jeecg.config.mybatis.DSSwitcher;
import org.jeecg.modules.base.dto.DBInfo;
import org.jeecg.modules.base.dto.DatabaseDto;
import org.jeecg.modules.base.dto.SourceDto;
@ -134,7 +132,7 @@ public class SysDatabaseServiceImpl extends ServiceImpl<SysDatabaseMapper, SysDa
}
boolean success = save(sysDatabase);
if (success){
status2Redis();
saveOrUpdateStatus(sysDatabase);
return Result.OK(Prompt.ADD_SUCC);
}
return Result.error(Prompt.ADD_ERR);
@ -177,7 +175,7 @@ public class SysDatabaseServiceImpl extends ServiceImpl<SysDatabaseMapper, SysDa
}
boolean success = updateById(sysDatabase);
if (success) {
status2Redis();
saveOrUpdateStatus(sysDatabase);
return Result.OK(Prompt.UPDATE_SUCC);
}
return Result.error(Prompt.UPDATE_ERR);
@ -188,7 +186,7 @@ public class SysDatabaseServiceImpl extends ServiceImpl<SysDatabaseMapper, SysDa
public Result<?> deleteById(String id) {
boolean success = removeById(id);
if(success) {
statusDel(id);
delStatus(id);
return Result.OK(Prompt.DELETE_SUCC);
}
return Result.error(Prompt.DELETE_ERR);
@ -398,12 +396,29 @@ public class SysDatabaseServiceImpl extends ServiceImpl<SysDatabaseMapper, SysDa
}
/*
* 删除指定id的数据源的状态值
* */
private void statusDel(String id){
* 新增|修改数据源信息后更新数据源状态值
* */
private void saveOrUpdateStatus(SysDatabase database){
String id = database.getId();
String dbUrl = database.getDbUrl();
String dbDriver = database.getDbDriver();
String dbUsername = database.getDbUsername();
String dbPassword = database.getDbPassword();
boolean isConn = JDBCUtil.isConnection(dbUrl, dbDriver, dbUsername, dbPassword);
String prefixStatus = RedisConstant.PREFIX_STATUS;
String databaseStatus = RedisConstant.DATABASE_STATUS;
String statusKey = prefixStatus + databaseStatus;
redisUtil.hdel(statusKey, id);
redisUtil.hset(statusKey, id, isConn);
}
/*
* 删除指定id的数据源的状态值
* */
private void delStatus(String databaseId){
String prefixStatus = RedisConstant.PREFIX_STATUS;
String databaseStatus = RedisConstant.DATABASE_STATUS;
String statusKey = prefixStatus + databaseStatus;
if(redisUtil.hHasKey(statusKey, databaseId))
redisUtil.hdel(statusKey, databaseId);
}
}

View File

@ -2,15 +2,11 @@ package org.jeecg.modules.service.impl;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.sun.mail.imap.IMAPFolder;
import com.sun.mail.imap.IMAPStore;
import org.jeecg.common.api.QueryRequest;
import org.jeecg.common.api.vo.Result;
import org.jeecg.common.constant.CommonConstant;
@ -19,10 +15,12 @@ import org.jeecg.common.constant.Prompt;
import org.jeecg.common.constant.RedisConstant;
import org.jeecg.common.email.emuns.SysMailType;
import org.jeecg.common.util.EmailUtil;
import org.jeecg.common.util.JDBCUtil;
import org.jeecg.common.util.RedisUtil;
import org.jeecg.modules.base.dto.EmailDto;
import org.jeecg.modules.base.dto.IdCount;
import org.jeecg.modules.base.dto.SourceDto;
import org.jeecg.modules.base.entity.postgre.SysDatabase;
import org.jeecg.modules.base.entity.postgre.SysEmail;
import org.jeecg.modules.base.bizVo.SourceVo;
import org.jeecg.modules.entity.AlarmHistory;
@ -32,8 +30,6 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.mail.*;
import java.security.Key;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.*;
@ -117,12 +113,11 @@ public class SysEmailServiceImpl extends ServiceImpl<SysEmailMapper, SysEmail> i
LambdaQueryWrapper<SysEmail> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(SysEmail::getName,name);
if (CollUtil.isNotEmpty(list(wrapper)))
return Result.error("Name"+Prompt.NOT_REPEAT);
return Result.error("Name" + Prompt.NOT_REPEAT);
boolean success = save(sysEmail);
if (success) {
// 更新邮箱连接状态到redis
status2Redis();
saveOrUpdateStatus(sysEmail);
return Result.OK(Prompt.ADD_SUCC);
}
return Result.error(Prompt.ADD_ERR);
@ -144,8 +139,7 @@ public class SysEmailServiceImpl extends ServiceImpl<SysEmailMapper, SysEmail> i
}
boolean success = updateById(sysEmail);
if (success) {
// 更新邮箱连接状态到redis
status2Redis();
saveOrUpdateStatus(sysEmail);
return Result.OK(Prompt.UPDATE_SUCC);
}
return Result.error(Prompt.UPDATE_ERR);
@ -156,7 +150,7 @@ public class SysEmailServiceImpl extends ServiceImpl<SysEmailMapper, SysEmail> i
public Result<?> deleteById(String id) {
boolean success = this.removeById(id);
if (success){
statusDel(id);
delStatus(id);
return Result.OK(Prompt.DELETE_SUCC);
}
return Result.error(Prompt.DELETE_ERR);
@ -213,10 +207,6 @@ public class SysEmailServiceImpl extends ServiceImpl<SysEmailMapper, SysEmail> i
return sourceDtos;
}
/*
* 调用场景
* 1.应用启动 2.修改邮箱信息 3.新增邮箱信息
* */
@Override
public void status2Redis() {
// 获取所有配置的邮箱服务器
@ -237,12 +227,27 @@ public class SysEmailServiceImpl extends ServiceImpl<SysEmailMapper, SysEmail> i
}
/*
* 删除指定id的邮箱服务器的状态值
* */
private void statusDel(String id){
* 新增|修改邮箱服务信息后更新邮箱服务状态值
* */
private void saveOrUpdateStatus(SysEmail email){
String id = email.getId();
String address = email.getEmailServerAddress();
Integer port = email.getPort();
boolean isConn = EmailUtil.isConnection(address, port);
String prefixStatus = RedisConstant.PREFIX_STATUS;
String emailStatus = RedisConstant.EMAIL_STATUS;
String statusKey = prefixStatus + emailStatus;
redisUtil.hdel(statusKey, id);
redisUtil.hset(statusKey, id, isConn);
}
/*
* 删除指定id的邮箱服务器的状态值
* */
private void delStatus(String emailId){
String prefixStatus = RedisConstant.PREFIX_STATUS;
String emailStatus = RedisConstant.EMAIL_STATUS;
String statusKey = prefixStatus + emailStatus;
if(redisUtil.hHasKey(statusKey, emailId))
redisUtil.hdel(statusKey, emailId);
}
}

View File

@ -0,0 +1,124 @@
package org.jeecg.modules.quartz.job;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.jeecg.common.constant.RedisConstant;
import org.jeecg.common.util.NumUtil;
import org.jeecg.modules.base.entity.Rule;
import org.jeecg.modules.base.entity.postgre.AlarmLog;
import org.jeecg.modules.base.entity.postgre.AlarmRule;
import org.jeecg.modules.base.entity.postgre.SysDatabase;
import org.jeecg.modules.base.enums.Item;
import org.jeecg.modules.quartz.entity.Monitor;
import org.quartz.*;
import java.util.Set;
import static org.jeecg.modules.base.enums.SourceType.DATABASE;
import static org.jeecg.modules.base.enums.SourceType.EMAIL;
@Data
@Slf4j
@PersistJobDataAfterExecution
@DisallowConcurrentExecution
public class EmailJob extends Monitor implements Job{
/**
* 解析Email预警规则
**/
@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
init();
// 查询所有Email的报警规则,根据报警规则查询监控项数据
String pattern = RedisConstant.PREFIX_RULE + EMAIL.getType();
Set<String> keys = getRedisStreamUtil().keys(pattern);
if (CollUtil.isEmpty(keys)) return;
String prefixSilence = RedisConstant.PREFIX_SILENCE;
String operator = null;
for (String ruleKey : keys) {
try {
AlarmRule alarmRule = (AlarmRule) getRedisStreamUtil().get(ruleKey);
// 如果报警规则为空,或者在沉默周期内,跳过当前规则
operator = alarmRule.getOperator();
String ruleId = alarmRule.getId();
String itemId = alarmRule.getItemId();
String silenceKey = prefixSilence + ruleId;
boolean hasKey = getRedisStreamUtil().hasKey(silenceKey);
boolean blank1 = StrUtil.isBlank(operator);
boolean blank2 = StrUtil.isBlank(itemId);
if (blank1 || blank2 || hasKey) continue;
// 根据sourceId查询Email信息
String sourceId = alarmRule.getSourceId();
/*SysDatabase database = getAlarmClient().getDatabase(sourceId);
if (ObjectUtil.isNull(database)) continue;*/
// 根据监控项id选择要查询的监控项信息
Item item = Item.of(itemId);
if (ObjectUtil.isNull(item)) continue;
Number current = null;
switch (item){
case EMAIL_CONN: // 监控项-1: 测试邮箱服务是否可以连接成功
current = isConnection(sourceId);
break;
// 追加的监控项...
default:
break;
}
// 解析预警规则,判断是否需要报警
ObjectMapper mapper = new ObjectMapper();
Rule rule = mapper.readValue(operator, Rule.class);
String op = rule.getOperator();
Double threshold = rule.getThreshold();
boolean needWarn = NumUtil.compare(current, threshold, op);
if (needWarn){
// 记录报警日志
AlarmLog alarmLog = new AlarmLog();
alarmLog.setRuleId(ruleId);
alarmLog.setOperator(operator);
alarmLog.setAlarmValue(StrUtil.toString(current));
String ruleName = alarmRule.getName();
String message = "您设定的预警规则:"+ruleName+"," +
"预警信息为:"+ operator + ",当前值为:" + current;
alarmLog.setAlarmInfo(message);
getAlarmClient().create(alarmLog);
// 规则触发报警后,设置该规则的沉默周期(如果有)
// 沉默周期失效之前,该规则不会再次被触发
Long silenceCycle = alarmRule.getSilenceCycle();
ruleSilence(silenceKey, silenceCycle);
// 发送报警信息
String groupId = alarmRule.getContactId();
String notific = alarmRule.getNotification();
getSendMessage().send(message, groupId, notific);
}
} catch (JsonProcessingException e) {
log.error("Email预警规则{}解析失败: {}", operator, e.getMessage());
}catch (RuntimeException e){
log.error("Email监控异常: {}",e.getMessage());
}
}
destroy();
}
/*
* 监控项-1: 测试邮箱服务是否可以连接成功 (0:失败 1:成功)
* */
private Integer isConnection(String emailId){
int res = 1;
String prefixStatus = RedisConstant.PREFIX_STATUS;
String emailStatus = RedisConstant.EMAIL_STATUS;
String statusKey = prefixStatus + emailStatus;
Boolean status = (Boolean)getRedisUtil().hget(statusKey, emailId);
if (ObjectUtil.isNull(status) || !status) res = 0;
return res;
}
}