2025-10-6学习笔记

目录

报警数据过滤及处理:

报警数据:

查询老人异常数据要通知的护理员:

通过角色名称查询用户id:

定时任务:


报警数据过滤及处理:

报警数据:

一旦有了报警数据之后,可能需要通知多个人,咱们要为每一个要通知的人保存一条报警数据告警数据表结构:alert_data

如果若依的生成代码功能将代码添加到项目当中

查询老人异常数据要通知的护理员:

DeviceMapper:

java 复制代码
/**
 * 根据随身设备id查询老人关联的护理人员id列表
 * @param iotId 设备id
 * @return  护理人员列表
 */
List<Long> selectNursingIdsByIotIdWithElder(@Param("iotId") String iotId);

/**
 * 根据固定设备id查询老人关联的护理人员id列表
 * @param iotId 设备id
 * @return  护理人员列表
 */
List<Long> selectNursingIdsByIotIdWithBed(@Param("iotId") String iotId);

DeviceMapper.xml:

XML 复制代码
<select id="selectNursingIdsByIotIdWithElder" resultType="java.lang.Long">
    select ne.nursing_id
    from device d
             left join nursing_elder ne on ne.elder_id = d.binding_location
    where d.location_type = 0
      and d.iot_id = #{iotId}
</select>
<select id="selectNursingIdsByIotIdWithBed" resultType="java.lang.Long">
    select ne.nursing_id
    from device d
             left join elder e on e.bed_id = d.binding_location
             left join nursing_elder ne on ne.elder_id = e.id
    where d.location_type = 1 and d.physical_location_type = 2
      and d.iot_id = #{iotId}
</select>

通过角色名称查询用户id:

system模块下SysUserRoleMapper:

java 复制代码
@Select("select sur.user_id from sys_user_role sur left join sys_role sr on sur.role_id = sr.role_id where sr.role_name = #{roleName}")
List<Long> selectUserIdByRoleName(String roleName);

定时任务:

platform模块下task包下创建AlertTask类:

java 复制代码
package com.zzyl.nursing.task;

import com.zzyl.nursing.service.IAlertRuleService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

@Component
@Slf4j
public class AlertTask {

    @Autowired
    private IAlertRuleService alertRuleService;

    public void deviceDataAlertFilter() {
        alertRuleService.alertFilter();
    }
}

需要在后台管理系统中创建定时任务:

0 * * * * ? 表示每分钟整执行一次

AlertRuleService:

java 复制代码
/**
 * 过滤报警
 */
void alertFilter();

AlertRuleServiceImpl:

java 复制代码
@Autowired
private SysUserRoleMapper userRoleMapper;

@Autowired
private DeviceMapper deviceMapper;

@Autowired
private IAlertDataService alertDataService;

@Value("${alert.deviceMaintainerRole}")
private String deviceMaintainerRole;

@Value("${alert.managerRole}")
private String managerRole;

@Autowired
private RedisTemplate<String, String> redisTemplate;

/**
 * 报警过滤
 */
@Override
public void alertFilter() {
    // 查询所有规则,遍历规则
    long count = count(Wrappers.<AlertRule>lambdaQuery().eq(AlertRule::getStatus, 1));
    if (count <= 0) {
        return;
    }
    // 查询所有上报的数据
    List<Object> values = redisTemplate.opsForHash().values(CacheConstants.IOT_DEVICE_LAST_DATA);
    if (CollUtil.isEmpty(values)) {
        return;
    }
    // 解析上报的数据
    List<DeviceData> deviceDatas = new ArrayList<>();
    values.forEach(v -> deviceDatas.addAll(JSONUtil.toList(v.toString(), DeviceData.class)));

    // 遍历报警数据,逐条处理
    deviceDatas.forEach(d -> alertFilter(d));
}

/**
 * 逐条过滤报警数据
 *
 * @param deviceData 设备数据
 */
private void alertFilter(DeviceData deviceData) {
    // 判断当前上报的数据是否超过了1分钟
    LocalDateTime alarmTime = deviceData.getAlarmTime();
    long between = LocalDateTimeUtil.between(alarmTime, LocalDateTime.now(), ChronoUnit.SECONDS);
    if (between > 60) {
        return;
    }
    // 查询所有的该产品规则和该物模型的规则
    List<AlertRule> allRules = list(Wrappers.<AlertRule>lambdaQuery()
            .eq(AlertRule::getProductKey, deviceData.getProductKey())
            .eq(AlertRule::getIotId, "-1")
            .eq(AlertRule::getFunctionId, deviceData.getFunctionId())
            .eq(AlertRule::getStatus, 1));

    List<AlertRule> iotIdRules = list(Wrappers.<AlertRule>lambdaQuery()
            .eq(AlertRule::getProductKey, deviceData.getProductKey())
            .eq(AlertRule::getIotId, deviceData.getIotId())
            .eq(AlertRule::getFunctionId, deviceData.getFunctionId())
            .eq(AlertRule::getStatus, 1));
    // 合并
    Collection<AlertRule> allArertRules = CollUtil.addAll(allRules, iotIdRules);
    // 如果为空,则中断
    if (CollUtil.isEmpty(allArertRules)) {
        return;
    }
    // 按照过滤规则和上报的数据进行匹配
    allArertRules.forEach(alertRule -> deviceDataAlarmHandler(alertRule, deviceData));

}

/**
 * 过滤数据是否触发报警规则
 *
 * @param rule
 * @param deviceData
 */
private void deviceDataAlarmHandler(AlertRule rule, DeviceData deviceData) {
    // 判断上报时间是否在规则的生效时段内 00:00:00~23:59:59
    String[] split = rule.getAlertEffectivePeriod().split("~");
    LocalTime startTime = LocalTime.parse(split[0]);
    LocalTime endTime = LocalTime.parse(split[1]);
    // 获取上报时间
    LocalTime time = LocalDateTimeUtil.of(deviceData.getAlarmTime()).toLocalTime();
    // 不在上报时间内,则结束请求
    if (time.isBefore(startTime) || time.isAfter(endTime)) {
        return;
    }
    // 获取IOTID
    String iotId = deviceData.getIotId();
    // 统计次数的key
    String aggCountKey = CacheConstants.ALERT_TRIGGER_COUNT_PREFIX + iotId + ":" + deviceData.getFunctionId() + ":" + rule.getId();

    // 数据对比,上报的数据与规则中的阈值进行对比
    // 两个参数x,y(参数有顺序要求,左边是上报的数据,后边是规则的数据)  x==y 返回0  x>y 返回大于0  x<y 返回小于0的数值
    int compare = NumberUtil.compare(Double.valueOf(deviceData.getDataValue()), rule.getValue());
    if ((rule.getOperator().equals(">=") && compare >= 0) || (rule.getOperator().equals("<") && compare < 0)) {
        log.info("当前上报的数据符合规则异常");
    } else {
        // 正常的数据
        redisTemplate.delete(aggCountKey);
        return;
    }
    // 异常的数据会走到这里
    // 判断是否在沉默周期内
    String silentKey = CacheConstants.ALERT_SILENT_PREFIX + iotId + ":" + deviceData.getFunctionId() + ":" + rule.getId();
    String silentData = redisTemplate.opsForValue().get(silentKey);
    if (StringUtils.isNotEmpty(silentData)) {
        return;
    }
    // 持续周期的逻辑
    String aggData = redisTemplate.opsForValue().get(aggCountKey);
    int count = StringUtils.isEmpty(aggData) ? 1 : Integer.parseInt(aggData) + 1;
    // 如果count与持续周期的值相等,则触发报警
    if (ObjectUtil.notEqual(count, rule.getDuration())) {
        // 不相等
        redisTemplate.opsForValue().set(aggCountKey, count + "");
        return;
    }
    // 删除redis的报警数据
    redisTemplate.delete(aggCountKey);
    // 存储数据到沉默周期,设置一个过期时间,规则中的沉默周期
    redisTemplate.opsForValue().set(silentKey, "1", rule.getAlertSilentPeriod(), TimeUnit.MINUTES);

    // 报警数据,需要找到对应的人
    List<Long> userIds = new ArrayList<>();
    if (rule.getAlertDataType().equals(0)) {
        // 老人异常数据
        if (deviceData.getLocationType().equals(0)) {
            // 说明是报警手表,直接可以找到老人的id,通过老人id,找到对应的护理员
            userIds = deviceMapper.selectNursingIdsByIotIdWithElder(iotId);
        } else if (deviceData.getLocationType().equals(1) && deviceData.getPhysicalLocationType().equals(2)) {
            // 说明是床位设备,可以通过床位id找到老人,通过老人id,找到对应的护理员
            userIds = deviceMapper.selectNursingIdsByIotIdWithBed(iotId);
        }
    } else {
        // 设备异常数据,找维修工,或者是行政人员
        userIds = userRoleMapper.selectUserIdByRoleName(deviceMaintainerRole);
    }
    // 不论是哪种情况,都要通知超级管理员
    List<Long> managerIds = userRoleMapper.selectUserIdByRoleName(managerRole);
    Collection<Long> allUserIds = CollUtil.addAll(userIds, managerIds);
    // 去重
    allUserIds = CollUtil.distinct(allUserIds);

    // 保存报警数据
    insertAlertData(allUserIds, rule, deviceData);
}

/**
 * 保存报警数据
 *
 * @param allUserIds
 * @param rule
 * @param deviceData
 */
private void insertAlertData(Collection<Long> allUserIds, AlertRule rule, DeviceData deviceData) {
    // 对象拷贝
    AlertData alertData = BeanUtil.toBean(deviceData, AlertData.class);
    alertData.setAlertRuleId(rule.getId());
    // 心率<60,持续3个周期就报警
    String alertReason = CharSequenceUtil.format("{}{}{},持续{}个周期就报警", rule.getFunctionName(), rule.getOperator(), rule.getValue(), rule.getDuration());
    alertData.setAlertReason(alertReason);
    alertData.setStatus(0);
    alertData.setType(rule.getAlertDataType());
    // 遍历allUserIds
    List<AlertData> list = allUserIds.stream().map(userId -> {
        AlertData dbAlertData = BeanUtil.toBean(alertData, AlertData.class);
        dbAlertData.setUserId(userId);
        dbAlertData.setId(null);
        return dbAlertData;
    }).collect(Collectors.toList());

    // 批量保存
    alertDataService.saveBatch(list);

}

CacheConstants:

java 复制代码
/**
 * 报警规则连续触发次数,缓存前缀
 */
public static final String ALERT_TRIGGER_COUNT_PREFIX = "iot:alert_trigger_count:";

/**
 * 报警规则沉默周期,缓存前缀
 */
public static final String ALERT_SILENT_PREFIX = "iot:alert_silent:";

admin模块下application-dev.yml:

bash 复制代码
alert:
    deviceMaintainerRole: 维修工
    managerRole: 超级管理员
相关推荐
EnCi Zheng3 小时前
JJWT 依赖包完全指南-从入门到精通
java·spring boot
夜流冰3 小时前
工程师 - Raspberry Pi Pico程序:读取SPI数据后从串口输出
笔记
koo3643 小时前
李宏毅机器学习笔记16
人工智能·笔记·机器学习
心疼你的一切3 小时前
使用Unity引擎开发Rokid主机应用的全面配置交互操作
学习·游戏·unity·c#·游戏引擎·交互
这周也會开心3 小时前
Tomcat本地部署SpringBoot项目
java·spring boot·tomcat
小猪佩奇TONY3 小时前
C++ 学习(3) ----设计模式
c++·学习·设计模式
Brookty3 小时前
【算法】二分查找(一)朴素二分
java·学习·算法·leetcode·二分查找
小满、3 小时前
什么是 Maven?关于 Maven 的命令、依赖传递、聚合与继承
java·maven
bawangtianzun4 小时前
重链剖分 学习记录
数据结构·c++·学习·算法