diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/modules/base/entity/postgre/SysDatabase.java b/jeecg-boot-base-core/src/main/java/org/jeecg/modules/base/entity/postgre/SysDatabase.java index f8c35629..33cba269 100644 --- a/jeecg-boot-base-core/src/main/java/org/jeecg/modules/base/entity/postgre/SysDatabase.java +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/modules/base/entity/postgre/SysDatabase.java @@ -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; /** * 修改人 diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/modules/base/entity/postgre/SysEmail.java b/jeecg-boot-base-core/src/main/java/org/jeecg/modules/base/entity/postgre/SysEmail.java index 1f06aabc..76faf19f 100644 --- a/jeecg-boot-base-core/src/main/java/org/jeecg/modules/base/entity/postgre/SysEmail.java +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/modules/base/entity/postgre/SysEmail.java @@ -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; /** * 修改人员 diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/modules/base/enums/Enabled.java b/jeecg-boot-base-core/src/main/java/org/jeecg/modules/base/enums/Enabled.java new file mode 100644 index 00000000..5492013c --- /dev/null +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/modules/base/enums/Enabled.java @@ -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; + } +} diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/modules/base/enums/Item.java b/jeecg-boot-base-core/src/main/java/org/jeecg/modules/base/enums/Item.java index 79f98993..c82afb12 100644 --- a/jeecg-boot-base-core/src/main/java/org/jeecg/modules/base/enums/Item.java +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/modules/base/enums/Item.java @@ -7,6 +7,7 @@ import lombok.Getter; @Getter @AllArgsConstructor public enum Item { + EMAIL_CONN("1", "邮箱服务连接情况"), DATABASE_CONN("2", "数据源连接情况"); private String value; diff --git a/jeecg-module-abnormal-alarm/src/main/java/org/jeecg/modules/service/impl/AlarmRuleServiceImpl.java b/jeecg-module-abnormal-alarm/src/main/java/org/jeecg/modules/service/impl/AlarmRuleServiceImpl.java index 62cd5dd2..51432c3b 100644 --- a/jeecg-module-abnormal-alarm/src/main/java/org/jeecg/modules/service/impl/AlarmRuleServiceImpl.java +++ b/jeecg-module-abnormal-alarm/src/main/java/org/jeecg/modules/service/impl/AlarmRuleServiceImpl.java @@ -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 update(AlarmRule alarmRule) { - LambdaQueryWrapper 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 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 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 wrapper = new LambdaQueryWrapper<>(); - wrapper.eq(AlarmRule::getEnabled,1); + wrapper.eq(AlarmRule::getEnabled, ENABLED.getValue()); List alarmRules = this.list(wrapper); Map ruleMap = new HashMap<>(); for (AlarmRule alarmRule : alarmRules) { @@ -171,9 +170,70 @@ public class AlarmRuleServiceImpl extends ServiceImpl 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 i LambdaQueryWrapper 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 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 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 i return sourceDtos; } - /* - * 调用场景 - * 1.应用启动 2.修改邮箱信息 3.新增邮箱信息 - * */ @Override public void status2Redis() { // 获取所有配置的邮箱服务器 @@ -237,12 +227,27 @@ public class SysEmailServiceImpl extends ServiceImpl 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); } } diff --git a/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/quartz/job/EmailJob.java b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/quartz/job/EmailJob.java new file mode 100644 index 00000000..05a7135b --- /dev/null +++ b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/quartz/job/EmailJob.java @@ -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 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; + } +}