数据中台预警监控功能简要设计

需求:

  针对产品主要流程中一些异常情况, 数据中台接收各个业务系统,其他中台系统发送的异常事件。 满足触发阀值之后进行短信告警功能。

 

设计:

  1、配置监控器:

      监控事件、监控范围、触发阀值、对应负责人(配置负责人)

      监控器监控的范围可以是全项目单个事件,也可以是指定项目的单个事件

  2、某个事件连续触发阀值次数的时候,生成告警记录,发送短信。已告警的所属事件继续出发时候累计触发次数

  3、告警记录点击处理,清除掉此记录上面累计的所有堆积事件,监控器重置,重新开始接收

 

  大体表结构:

-- scp_alert_monitoring_configuration: 预警监控配置表
-- scp_alert_contact: 告警联系人表
-- 异常记录表 scp_business_exception_record
-- 告警计数器表 scp_alarm_counter
-- 异常记录告警计数器关联表 scp_exception_record_alarm_counter_ref
-- 预警记录表 scp_alarm_record
-- 预警短信信息表 scp_alarm_sms_record

 

设计图:

 

 

 

 

 

 

 

 

针对各个事件的数据清洗,采用策略模式分发处理

监控器的创建与维护,每一次接收新事件记录时候校验维护一次,因为配置信息不在中台数据库

数据库表sql:

CREATE TABLE `supply_chain_dsc`.`scp_alert_monitoring_configuration` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`alert_event` varchar(3) DEFAULT NULL COMMENT '预警事件(01-授信决策失败、02-授信决策超时、03-额度激活失败、04-钱包开通失败、05-用信决策失败、06-用信决策超时、07-支付失败、08-客户自主还款失败)',
`monitoring_scope` varchar(2) DEFAULT NULL COMMENT '监控范围(01-单项目、02-全平台)',
`cumulative_times` int(11) DEFAULT NULL COMMENT '累计次数',
`alarm_level` varchar(2) DEFAULT NULL COMMENT '告警级别:01-A、02-B',
`is_deleted` tinyint(1) NOT NULL DEFAULT '0' COMMENT '逻辑删除(1:已删除,0:未删除)',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=17 DEFAULT CHARSET=utf8 ROW_FORMAT=COMPACT COMMENT='预警监控配置表';


CREATE TABLE `supply_chain_dsc`.`scp_alert_contact` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`personnel_type` varchar(2) DEFAULT NULL COMMENT '人员类型(01-客户经理、02-运营经理、03-产品经理、04-风险经理)',
`name` varchar(20) DEFAULT NULL COMMENT '姓名',
`phone_no` varchar(15) NOT NULL COMMENT '手机号',
`is_deleted` tinyint(1) NOT NULL DEFAULT '0' COMMENT '逻辑删除(1:已删除,0:未删除)',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8 ROW_FORMAT=COMPACT COMMENT='告警联系人表';

CREATE TABLE `supply_chain_dsc`.`scp_dsc_alarm_counter` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`project_id` varchar(64) DEFAULT NULL COMMENT '项目id',
`monitoring_scope` char(2) NOT NULL COMMENT '阀值范围:01-单项目、02-全平台',
`event_type` char(2) NOT NULL COMMENT '异常类型:01-授信决策失败,02-授信决策超时,03-额度激活失败,04-钱包开通失败,05-用信决策失败,06-用信决策超时,07-支付失败,08-客户自主还款失败',
`alarm_trigger_base_times` int(11) NOT NULL COMMENT '告警触发基础次数',
`alarm_level` char(2) NOT NULL COMMENT '告警级别:01-A、02-B',
`trigger_times` int(11) NOT NULL DEFAULT '0' COMMENT '触发次数',
`alarm_status` char(1) NOT NULL DEFAULT '2' COMMENT '告警状态:1-已告警,2-未告警',
`is_deleted` tinyint(1) NOT NULL DEFAULT '0' COMMENT '''逻辑删除(1:已删除,0:未删除)''',
`reset_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '重置时间',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=15 DEFAULT CHARSET=utf8 COMMENT='告警计数器表';
CREATE TABLE `supply_chain_dsc`.`scp_dsc_alarm_record` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`project_id` varchar(64) DEFAULT NULL COMMENT '项目id',
`alarm_counter_id` int(11) NOT NULL COMMENT '计数器id',
`trigger_times` int(11) NOT NULL COMMENT '触发次数',
`monitoring_scope` char(2) NOT NULL COMMENT '监控范围(01-单项目、02-全平台)',
`event_type` char(2) NOT NULL COMMENT '预警事件(01-授信决策失败、02-授信决策超时、03-额度激活失败、04-钱包开通失败、05-用信决策失败、06-用信决策超时、07-支付失败、08-客户自主还款失败)',
`alarm_level` char(2) NOT NULL COMMENT '告警级别:01-A、02-B',
`status` char(1) NOT NULL DEFAULT '1' COMMENT '状态:1-未处理、2-已处理',
`deal_time` datetime DEFAULT NULL COMMENT '处理时间',
`deal_user_phone` varchar(255) DEFAULT NULL COMMENT '处理人手机号',
`deal_user_name` varchar(255) DEFAULT NULL COMMENT '处理人',
`create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`id`),
KEY `index_alarm_counter_id` (`alarm_counter_id`) COMMENT '告警计数器id索引'
) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=utf8 COMMENT='预警记录表';
CREATE TABLE `supply_chain_dsc`.`supply_chain_dsc`.`scp_dsc_alarm_sms_record` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`alarm_record_id` int(11) NOT NULL COMMENT '告警记录id',
`name` varchar(20) DEFAULT NULL COMMENT '姓名',
`phone_no` varchar(15) DEFAULT NULL COMMENT '手机号',
`content` varchar(500) NOT NULL COMMENT '短信内容',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`id`),
KEY `index_alarm_record_id` (`alarm_record_id`) COMMENT '告警记录id索引'
) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=utf8 COMMENT='告警短信记录表';
CREATE TABLE `supply_chain_dsc`.`scp_dsc_business_exception_record` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`exception_type` char(2) NOT NULL COMMENT '异常类型:01-授信决策失败,02-授信决策超时,03-额度激活失败,04-钱包开通失败,05-用信决策失败,06-用信决策超时,07-支付失败,08-客户自主还款失败',
`business_no` varchar(100) NOT NULL COMMENT '业务单号',
`project_id` varchar(64) NOT NULL COMMENT '项目id',
`project_name` varchar(255) DEFAULT NULL COMMENT '项目名称',
`user_id` varchar(50) DEFAULT NULL COMMENT '用户id',
`user_name` varchar(100) DEFAULT NULL COMMENT '用户名称',
`fail_reason` varchar(1024) NOT NULL COMMENT '错误原因',
`fail_reason_detail` varchar(500) DEFAULT NULL COMMENT '错误详细原因',
`remark` varchar(200) DEFAULT NULL COMMENT '备注',
`trigger_time` datetime NOT NULL COMMENT '触发时间',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建日期',
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=214 DEFAULT CHARSET=utf8 COMMENT='业务异常记录表';
CREATE TABLE `supply_chain_dsc`.`scp_dsc_exception_record_alarm_counter_ref` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`alarm_counter_id` int(11) NOT NULL COMMENT '告警计数器id',
`exception_record_id` int(11) NOT NULL COMMENT '异常记录id',
`alarm_record_id` int(11) DEFAULT NULL COMMENT '告警记录id',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=198 DEFAULT CHARSET=utf8 COMMENT='异常记录告警计数器关联表';
View Code

 

为了方便下次理解,贴主要代码:

package com.wxsbank.supplychain.dsc.business.impl;

import cn.hutool.core.collection.CollUtil;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.wxsbank.common.exception.BusinessMessage;
import com.wxsbank.framework.common.exception.impl.BusinessException;
import com.wxsbank.framework.spring.annotation.Business;
import com.wxsbank.supplychain.dsc.business.ExceptionEventCleanStrategy;
import com.wxsbank.supplychain.dsc.business.IExceptionAlarmBusiness;
import com.wxsbank.supplychain.dsc.business.SmsBusiness;
import com.wxsbank.supplychain.dsc.dto.input.AlarmQueryDTO;
import com.wxsbank.supplychain.dsc.dto.output.AlarmRepayInfoSimpleDTO;
import com.wxsbank.supplychain.dsc.entity.*;
import com.wxsbank.supplychain.dsc.enums.*;
import com.wxsbank.supplychain.dsc.event.AlarmPageEvent;
import com.wxsbank.supplychain.dsc.event.BatchClearAlarmEvent;
import com.wxsbank.supplychain.dsc.event.SmsEvent;
import com.wxsbank.supplychain.dsc.model.AlarmMessageModel;
import com.wxsbank.supplychain.dsc.model.AlarmMessagePageModel;
import com.wxsbank.supplychain.dsc.service.*;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionSynchronizationAdapter;
import org.springframework.transaction.support.TransactionSynchronizationManager;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 异常告警业务处理类
 * @author zhen
 */
@Business
@Slf4j
public class ExceptionAlarmBusinessImpl implements IExceptionAlarmBusiness {

    @Resource
    private RedissonClient redissonClient;

    @Resource
    private IScpAlertMonitoringConfigurationService alertMonitoringConfigurationService;

    @Resource
    private IScpAlarmCounterService alarmCounterService;

    @Resource
    private IScpAlarmRecordService alarmRecordService;

    @Resource
    private IScpBusinessExceptionRecordService businessExceptionRecordService;

    @Resource
    private IScpExceptionRecordAlarmCounterRefService exceptionRecordAlarmCounterRefService;

    @Resource
    private IScpAlertContactService alertContactService;

    @Resource
    private IScpAlarmSmsRecordService smsRecordService;

    @Resource
    private ICreditApplyInfoService creditApplyInfoService;

    @Resource
    private IUseCreditApplyInfoService useCreditApplyInfoService;

    @Resource
    private ICreditApplyStatusRecordService creditApplyStatusRecordService;

    @Resource
    private IUseCreditApplyStatusRecordService useCreditApplyStatusRecordService;

    @Resource
    private IRepayInfoService repayInfoService;

    @Resource
    private IRmScpPclGrantCreditApplyService pclGrantCreditApplyService;

    @Resource
    private IPclUseCreditApplyInfoService pclUseCreditApplyInfoService;

    @Resource
    private SmsBusiness smsBusiness;

    @Value("${exception.monitor.alarm.sms}")
    private String monitorAlarmSmsTemplate;


    @Override
    @Transactional(rollbackFor = Exception.class)
    public void batchClearAlarm(BatchClearAlarmEvent event) {
        List<Integer> alarmRecordIdList = event.getAlarmRecordIdList();
        if (CollUtil.isEmpty(alarmRecordIdList)) {
            return;
        }
        // 判断解除人的合法性
        ScpAlertContact scpAlertContact = alertContactService.findByNameAndPhone(event.getDealUserName(), event.getDealUserPhone());
        if (scpAlertContact == null) {
            throw new BusinessException(BusinessMessage.ALERT_CONTACT_NOT_EXIST);
        }

        List<ScpAlarmRecord> scpAlarmRecordList = new ArrayList<>(alarmRecordIdList.size());
        ScpAlarmRecord scpAlarmRecord;
        ScpAlarmCounter scpAlarmCounter;
        for (Integer id : alarmRecordIdList) {
            // 告警计数器表触发次数清零
            ScpAlarmRecord alarmRecord = alarmRecordService.getById(id);
            if (alarmRecord == null || AlarmRecordStatusEnum.PROCESSED.getValue().equals(alarmRecord.getStatus())) {
                continue;
            }
            scpAlarmCounter = new ScpAlarmCounter();
            scpAlarmCounter.setId(alarmRecord.getAlarmCounterId());
            scpAlarmCounter.setTriggerTimes(0);
            scpAlarmCounter.setAlarmStatus(AlarmStatusEnum.UN_COMPLETE_ALARM.getValue());
            scpAlarmCounter.setResetTime(LocalDateTime.now());
            alarmCounterService.updateById(scpAlarmCounter);
            // 预警记录
            scpAlarmRecord = new ScpAlarmRecord();
            scpAlarmRecord.setId(id);
            // 预警记录已被处理
            scpAlarmRecord.setStatus(AlarmRecordStatusEnum.PROCESSED.getValue());
            scpAlarmRecord.setDealTime(LocalDateTime.now());
            scpAlarmRecord.setDealUserName(event.getDealUserName());
            scpAlarmRecord.setDealUserPhone(event.getDealUserPhone());
            scpAlarmRecordList.add(scpAlarmRecord);
        }
        // 预警记录表更新处理人信息
        alarmRecordService.updateBatchById(scpAlarmRecordList);
    }

    @Override
    public void clearAndSaveBusinessException(JSONObject content, ExceptionEventCleanStrategy cleanStrategy, String businessNo) {

        final String keyPrefix = "EXCEPTION_EVENT_TRIGGER_";
        String key = keyPrefix + businessNo + "_" + cleanStrategy.getExceptionEventType().getValue();
        RLock lock = redissonClient.getLock(key);
        try {
            lock.lock(2, TimeUnit.MINUTES);
            //清洗数据
            Integer exceptionRecordId = cleanStrategy.cleanAndSave(content);
            if (exceptionRecordId == -1) {
                // -1 表示清洗数据出现数据异常
                return;
            }
            if (exceptionRecordId != null){

                //创建或销毁计数器
                repairCounterInfo(exceptionRecordId);

                //异步触发计数器
                Integer finalExceptionRecordId = exceptionRecordId;
                CompletableFuture.runAsync(() ->
                        triggerAlarmMonitorChain(finalExceptionRecordId)
                );

            }
        } catch (Exception e) {
            log.error("【LEVEL:LOW】【业务异常数据清洗 type:" + cleanStrategy.getExceptionEventType().getDesc() + " data:" + content.toJSONString()
                    + "】数据异常!", e);
        } finally {
            lock.unlock();
        }

    }

    /**
     * 触发报警监控链
     * @param businessExceptionRecordId
     */
    @Transactional
    @Override
    public void triggerAlarmMonitorChain(Integer businessExceptionRecordId) {
        ScpBusinessExceptionRecord exceptionRecord = businessExceptionRecordService.getById(businessExceptionRecordId);
        if (exceptionRecord == null) {
            return;
        }

        List<ScpAlarmCounter> counters = queryTriggerCounter(exceptionRecord);
        if(counters.isEmpty()){
            return;
        }

        for(ScpAlarmCounter counter : counters){
            String key = "EXCEPTION_EVENT_COUNTER_TRIGGER_" + counter.getId();
            // 增加一个同步锁,处理针对同一counter累加的并发问题
            RLock lock = redissonClient.getLock(key);
            try {
                lock.lock(2, TimeUnit.MINUTES);
                if (AlarmStatusEnum.COMPLETED_ALARM.getValue().equals(counter.getAlarmStatus())){
                    triggerInCompletedAlarmCounter(counter, businessExceptionRecordId);
                }else {
                    triggerInUnCompletedAlarmCounter(counter, businessExceptionRecordId);
                }
            }catch(Exception e){
                log.error("【LEVEL:LOW】【预警监控触发异常 counterId:" + counter.getId() + " businessExceptionRecordId:" + businessExceptionRecordId
                        + " 事件件类型:" + ExceptionEventTypeEnum.getDescByValue(counter.getEventType()) + "】数据异常!", e);
            }finally {
                if (lock.isLocked()){
                    lock.unlock();
                }
            }

        }

    }

    @Override
    public void sendAlarmSms(Integer alarmRecordId) {
        List<ScpAlarmSmsRecord> smsRecordList = smsRecordService.queryByAlarmRecordId(alarmRecordId);
        for (ScpAlarmSmsRecord alarmSmsRecord : smsRecordList){
            SmsEvent event = new SmsEvent();
            event.setContent(alarmSmsRecord.getContent());
            event.setPhone(alarmSmsRecord.getPhoneNo());
            smsBusiness.customMsgSend(event);
        }

    }

    @Override
    public List<UseCreditApplyInfo> selectDelayAndNoTriggerUseCreditInfoList(LocalDateTime startTime, LocalDateTime endTime,
                                                                             LocalDateTime maxCreateTime) {
        return useCreditApplyInfoService.selectDelayAndNoTriggerUseCreditInfoList(startTime, endTime, maxCreateTime);
    }

    @Override
    public List<PclUseCreditApplyInfo> selectDelayAndNoTriggerPersonUseCreditApplyInfoList(LocalDateTime startTime,
                                                                                           LocalDateTime endTime,
                                                                                           LocalDateTime maxCreateTime) {
        return pclUseCreditApplyInfoService.selectDelayAndNoTriggerPersonUseCreditApplyInfoList(startTime, endTime, maxCreateTime);
    }

    @Override
    public List<CreditApplyInfo> selectDelayAndNoTriggerCreditApplyInfoList(LocalDateTime startTime, LocalDateTime endTime,
                                                                            LocalDateTime maxCreateTime) {
        return creditApplyInfoService.selectDelayAndNoTriggerCreditApplyInfoList(startTime, endTime, maxCreateTime);
    }

    @Override
    public List<RmScpPclGrantCreditApply> selectDelayAndNoTriggerPersonCreditApplyInfoList(LocalDateTime startTime,
                                                                                           LocalDateTime endTime,
                                                                                           LocalDateTime maxCreateTime) {
        return pclGrantCreditApplyService.selectDelayAndNoTriggerPersonCreditApplyInfoList(startTime, endTime, maxCreateTime);
    }

    @Override
    public List<CreditApplyStatusRecord> selectFailAndNoTriggerCreditApplyStatusList(LocalDateTime startTime, LocalDateTime endTime) {
        return creditApplyStatusRecordService.selectFailAndNoTriggerCreditApplyStatusList(startTime, endTime);
    }

    @Override
    public List<UseCreditApplyStatusRecord> selectFailAndNoTriggerUseCreditApplyStatusList(LocalDateTime startTime, LocalDateTime endTime) {
        return useCreditApplyStatusRecordService.selectFailAndNoTriggerUseCreditApplyStatusList(startTime, endTime);
    }

    @Override
    public List<AlarmRepayInfoSimpleDTO> selectFailAndNoTriggerRepayInfoList(LocalDateTime startTime, LocalDateTime endTime) {
        return repayInfoService.selectFailAndNoTriggerRepayInfoList(startTime, endTime);
    }

    @Override
    public AlarmMessagePageModel queryAlarmPage(AlarmPageEvent event) {
        AlarmQueryDTO alarmQueryDTO = new AlarmQueryDTO();
        alarmQueryDTO.setName(event.getName());
        alarmQueryDTO.setPhoneNo(event.getPhoneNo());
        alarmQueryDTO.setProjectId(event.getProjectId());
        alarmQueryDTO.setPageNum(event.getPageNum());
        alarmQueryDTO.setPageSize(event.getPageSize());
        // 查询未处理的告警记录
        alarmQueryDTO.setStatus("1");
        IPage<ScpAlarmRecord> scpAlarmRecordIPage = alarmRecordService.queryAlarmRecordByCondition(alarmQueryDTO);
        List<ScpAlarmRecord> scpAlarmRecords = scpAlarmRecordIPage.getRecords();
        if (CollUtil.isEmpty(scpAlarmRecords)) {
            return null;
        }
        AlarmMessagePageModel alarmMessagePageModel = new AlarmMessagePageModel();
        List<AlarmMessageModel> alarmMessageModelList = new ArrayList<>(scpAlarmRecords.size());
        AlarmMessageModel alarmMessageModel;
        for (ScpAlarmRecord scpAlarmRecord : scpAlarmRecords) {
            alarmMessageModel = new AlarmMessageModel();
            alarmMessageModel.setAlarmRecordId(scpAlarmRecord.getId());
            alarmMessageModel.setMonitoringScope(scpAlarmRecord.getMonitoringScope());
            alarmMessageModel.setEventType(scpAlarmRecord.getEventType());
            alarmMessageModel.setAlarmLevel(scpAlarmRecord.getAlarmLevel());
            alarmMessageModel.setTriggerTime(
                    scpAlarmRecord.getCreateTime().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
            alarmMessageModel.setProjectId(scpAlarmRecord.getProjectId());
            alarmMessageModel.setProjectName(scpAlarmRecord.getProjectName());
            alarmMessageModelList.add(alarmMessageModel);
        }
        alarmMessagePageModel.setTotalNum((int) scpAlarmRecordIPage.getPages());
        alarmMessagePageModel.setTotalSize((int) scpAlarmRecordIPage.getTotal());
        alarmMessagePageModel.setAlarmMessageModelList(alarmMessageModelList);
        return alarmMessagePageModel;
    }

    /**
     * 在未完成的计数器触发
     * @param counter
     * @param businessExceptionRecordId
     */
    private void triggerInUnCompletedAlarmCounter(ScpAlarmCounter counter, Integer businessExceptionRecordId){
        int nowTriggerTimes = counter.getTriggerTimes() +1;

        //计数器触发次数+1
        ScpAlarmCounter updateCounter = new ScpAlarmCounter();
        updateCounter.setId(counter.getId());
        updateCounter.setTriggerTimes(counter.getTriggerTimes() + 1);
        alarmCounterService.updateById(updateCounter);

        //异常数据-触发器关联记录落库
        ScpExceptionRecordAlarmCounterRef ref = new ScpExceptionRecordAlarmCounterRef();
        ref.setAlarmCounterId(counter.getId());
        ref.setExceptionRecordId(businessExceptionRecordId);
        exceptionRecordAlarmCounterRefService.save(ref);

        //触发告警
        if(nowTriggerTimes == counter.getAlarmTriggerBaseTimes()){
            //触发器状态变更已触发
            updateCounter = new ScpAlarmCounter();
            updateCounter.setId(counter.getId());
            updateCounter.setAlarmStatus(AlarmStatusEnum.COMPLETED_ALARM.getValue());
            alarmCounterService.updateById(updateCounter);

            ScpBusinessExceptionRecord businessExceptionRecord = businessExceptionRecordService.getById(businessExceptionRecordId);
            //创建告警记录
            ScpAlarmRecord alarmRecord = new ScpAlarmRecord();
            alarmRecord.setTriggerTimes(nowTriggerTimes);
            alarmRecord.setAlarmCounterId(counter.getId());
            alarmRecord.setProjectId(counter.getProjectId());
            alarmRecord.setEventType(counter.getEventType());
            alarmRecord.setAlarmLevel(counter.getAlarmLevel());
            alarmRecord.setMonitoringScope(counter.getMonitoringScope());
            alarmRecord.setStatus(AlarmRecordStatusEnum.UNTREATED.getValue());
            alarmRecord.setProjectName(businessExceptionRecord.getProjectName());
            alarmRecordService.eachIdSave(alarmRecord);

            //为计数器内包含的时间关联与告警记录创建关联
            List<ScpExceptionRecordAlarmCounterRef> refList
                    = exceptionRecordAlarmCounterRefService.selectListByCurrentCounter(counter);
            refList.forEach(item->
                    item.setAlarmRecordId(alarmRecord.getId())
            );
            exceptionRecordAlarmCounterRefService.updateBatchById(refList);

            //目前是发送给所有的联系人
            List<ScpAlertContact> contactList = alertContactService.queryValidContactList();
            List<ScpAlarmSmsRecord> smsSendList = new ArrayList<>();
            if(!contactList.isEmpty()){
                for(ScpAlertContact  contact : contactList){
                    //创建smsRecord记录
                    ScpAlarmSmsRecord scpAlarmSmsRecord = new ScpAlarmSmsRecord();
                    scpAlarmSmsRecord.setName(contact.getName());
                    scpAlarmSmsRecord.setPhoneNo(contact.getPhoneNo());
                    scpAlarmSmsRecord.setAlarmRecordId(alarmRecord.getId());
                    String content = String.format(monitorAlarmSmsTemplate, AlarmLevelEnum.getDescByValue(alarmRecord.getAlarmLevel()),
                            MonitorScopeEnum.getDescByValue(alarmRecord.getMonitoringScope())
                                    + (MonitorScopeEnum.SINGLE_PROJECT.getValue().equals(alarmRecord.getMonitoringScope()) ?
                                    "(" + alarmRecord.getProjectName() + ")" : "")
                                    + " " + ExceptionEventTypeEnum.getDescByValue(alarmRecord.getEventType()));
                    scpAlarmSmsRecord.setContent(content);
                    smsSendList.add(scpAlarmSmsRecord);
                }
                //告警记录绑定发送短信信息落库
                smsRecordService.saveBatch(smsSendList);

                //异步触发发告警短信
                if(TransactionSynchronizationManager.isActualTransactionActive()){
                    //如果事务未提交,则提交之后调用异步方法
                    TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() {
                        @Override
                        public void afterCommit() {
                            CompletableFuture.runAsync(() ->
                                    sendAlarmSms(alarmRecord.getId())
                            );
                        }
                    });
                }else {
                    CompletableFuture.runAsync(() ->
                            sendAlarmSms(alarmRecord.getId())
                    );

                }

            }


        }


    }

    /**
     * 在已完成的计数器触发
     * @param counter
     * @param businessExceptionRecordId
     */
    private void triggerInCompletedAlarmCounter(ScpAlarmCounter counter, Integer businessExceptionRecordId){
        ScpAlarmCounter updateCounter = new ScpAlarmCounter();
        updateCounter.setId(counter.getId());
        updateCounter.setTriggerTimes(counter.getTriggerTimes() + 1);
        alarmCounterService.updateById(updateCounter);
        ScpAlarmRecord alarmRecord = alarmRecordService.findUntreatedRecordByCounter(counter.getId());

        ScpAlarmRecord updateAlarmRecord = new ScpAlarmRecord();
        updateAlarmRecord.setId(alarmRecord.getId());
        updateAlarmRecord.setTriggerTimes(alarmRecord.getTriggerTimes() + 1);
        alarmRecordService.updateById(updateAlarmRecord);

        ScpExceptionRecordAlarmCounterRef ref = new ScpExceptionRecordAlarmCounterRef();
        ref.setAlarmCounterId(counter.getId());
        ref.setAlarmRecordId(alarmRecord.getId());
        ref.setExceptionRecordId(businessExceptionRecordId);
        exceptionRecordAlarmCounterRefService.save(ref);
    }

    /**
     * 查询将要触发的counter
     * @param exceptionRecord
     * @return
     */
    private List<ScpAlarmCounter> queryTriggerCounter(ScpBusinessExceptionRecord exceptionRecord) {

        List<ScpAlarmCounter> counterList = alarmCounterService.queryWaitTriggerCounterList(exceptionRecord.getExceptionType(),
                MonitorScopeEnum.ALL_PROJECT.getValue(), null);

        List<ScpAlarmCounter> singleProjectCounterList
                = alarmCounterService.queryWaitTriggerCounterList(exceptionRecord.getExceptionType(),
                MonitorScopeEnum.SINGLE_PROJECT.getValue(), exceptionRecord.getProjectId());

        counterList.addAll(singleProjectCounterList);
        return counterList;
    }

    /**
     * 维护计数器
     * @param exceptionRecordId  异常事件id
     */
    private void repairCounterInfo(Integer exceptionRecordId){

        // 1、找到应该删除的counter,完成删除
        List<Integer> counterIds = alarmCounterService.queryShouldInvalidCounterIds();
        if (!counterIds.isEmpty()) {
            alarmCounterService.invalidCounter(counterIds);
        }

        String key = "EXCEPTION_EVENT_TRIGGER_ADD_COUNTER";
        RLock lock = redissonClient.getLock(key);
        try {
            lock.lock(2, TimeUnit.MINUTES);

            // 2、找到应该新建的counter,完成新建
            ScpBusinessExceptionRecord exceptionRecord = businessExceptionRecordService.getById(exceptionRecordId);
            String eventType = exceptionRecord.getExceptionType();
            String projectId = exceptionRecord.getProjectId();
            List<ScpAlertMonitoringConfiguration> allScopeConfigurations
                    = alertMonitoringConfigurationService.queryByEventAndScope(eventType, MonitorScopeEnum.ALL_PROJECT.getValue());
            List<ScpAlertMonitoringConfiguration> singleProjectConfigurations
                    = alertMonitoringConfigurationService.queryByEventAndScope(eventType, MonitorScopeEnum.SINGLE_PROJECT.getValue());

            for (ScpAlertMonitoringConfiguration configuration : allScopeConfigurations) {
                ScpAlarmCounter counter = alarmCounterService.findAllProjectMatchValidCounter(configuration);
                if (counter == null) {
                    createNewCounter(configuration, null);
                }
            }

            for (ScpAlertMonitoringConfiguration configuration : singleProjectConfigurations) {
                ScpAlarmCounter counter = alarmCounterService.findSingleProjectMatchValidCounter(configuration, projectId);
                if (counter == null) {
                    createNewCounter(configuration, projectId);
                }
            }
        }catch(Exception e) {
            log.error("【LEVEL:LOW】【维护计数器 新增计数器异常】", e);
            throw e;
        }finally {
            if(lock.isLocked()){
                lock.unlock();
            }
        }

    }


    /**
     * 创建新的计数器
     * @param configuration 配置器
     * @param projectId  项目id
     */
    private void createNewCounter(ScpAlertMonitoringConfiguration configuration, String projectId){
        ScpAlarmCounter counter = new ScpAlarmCounter();
        counter.setAlarmLevel(configuration.getAlarmLevel());
        counter.setMonitoringScope(configuration.getMonitoringScope());
        counter.setEventType(configuration.getAlertEvent());
        counter.setAlarmTriggerBaseTimes(configuration.getCumulativeTimes());
        counter.setAlarmStatus(AlarmStatusEnum.UN_COMPLETE_ALARM.getValue());
        counter.setProjectId(projectId);
        alarmCounterService.save(counter);
    }


}
View Code

 

posted @ 2022-12-06 16:12  guodaxia  阅读(492)  评论(0编辑  收藏  举报