feat:【IoT 物联网】新增告警记录管理功能,包括告警记录接口和前端展示页面

This commit is contained in:
YunaiV
2025-06-28 16:46:39 +08:00
parent 779cde24ec
commit db03c6d7a8
22 changed files with 467 additions and 57 deletions

View File

@@ -1,16 +1,19 @@
package cn.iocoder.yudao.framework.mybatis.config;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.mybatis.core.handler.DefaultDBFieldHandler;
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.autoconfigure.MybatisPlusAutoConfiguration;
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import com.baomidou.mybatisplus.core.incrementer.IKeyGenerator;
import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
import com.baomidou.mybatisplus.extension.incrementer.*;
import com.baomidou.mybatisplus.extension.parser.JsqlParserGlobal;
import com.baomidou.mybatisplus.extension.parser.cache.JdkSerialCaffeineJsqlParseCache;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.ibatis.annotations.Mapper;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.autoconfigure.AutoConfiguration;
@@ -18,6 +21,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.core.env.ConfigurableEnvironment;
import java.util.List;
import java.util.concurrent.TimeUnit;
/**
@@ -73,4 +77,11 @@ public class YudaoMybatisAutoConfiguration {
throw new IllegalArgumentException(StrUtil.format("DbType{} 找不到合适的 IKeyGenerator 实现类", dbType));
}
@Bean
public JacksonTypeHandler jacksonTypeHandler(List<ObjectMapper> objectMappers) {
// 特殊:设置 JacksonTypeHandler 的 ObjectMapper
JacksonTypeHandler.setObjectMapper(CollUtil.getFirst(objectMappers));
return new JacksonTypeHandler(Object.class);
}
}

View File

@@ -1,5 +1,6 @@
package cn.iocoder.yudao.module.iot.controller.admin.alert;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
@@ -15,15 +16,18 @@ import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.validation.Valid;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSetByFlatMap;
@Tag(name = "管理后台 - IoT 告警配置")
@@ -34,7 +38,7 @@ public class IotAlertConfigController {
@Resource
private IotAlertConfigService alertConfigService;
@Resource
private AdminUserApi adminUserApi;
@@ -76,12 +80,26 @@ public class IotAlertConfigController {
@PreAuthorize("@ss.hasPermission('iot:alert-config:query')")
public CommonResult<PageResult<IotAlertConfigRespVO>> getAlertConfigPage(@Valid IotAlertConfigPageReqVO pageReqVO) {
PageResult<IotAlertConfigDO> pageResult = alertConfigService.getAlertConfigPage(pageReqVO);
// 转换返回
Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(
convertSetByFlatMap(pageResult.getList(), config -> config.getReceiveUserIds().stream()));
return success(BeanUtils.toBean(pageResult, IotAlertConfigRespVO.class, vo ->
vo.setReceiveUserNames(vo.getReceiveUserIds().stream().map(userMap::get)
.filter(Objects::nonNull).map(AdminUserRespDTO::getNickname).collect(Collectors.toList()))));
return success(BeanUtils.toBean(pageResult, IotAlertConfigRespVO.class, vo -> {
vo.setReceiveUserNames(vo.getReceiveUserIds().stream()
.map(userMap::get)
.filter(Objects::nonNull)
.map(AdminUserRespDTO::getNickname)
.collect(Collectors.toList()));
}));
}
@GetMapping("/simple-list")
@Operation(summary = "获得告警配置简单列表", description = "只包含被开启的告警配置,主要用于前端的下拉选项")
@PreAuthorize("@ss.hasPermission('iot:alert-config:query')")
public CommonResult<List<IotAlertConfigRespVO>> getAlertConfigSimpleList() {
List<IotAlertConfigDO> list = alertConfigService.getAlertConfigListByStatus(CommonStatusEnum.ENABLE.getStatus());
return success(convertList(list, config -> // 只返回 id、name 字段
new IotAlertConfigRespVO().setId(config.getId()).setName(config.getName())));
}
}

View File

@@ -0,0 +1,56 @@
package cn.iocoder.yudao.module.iot.controller.admin.alert;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.iot.controller.admin.alert.vo.recrod.IotAlertRecordPageReqVO;
import cn.iocoder.yudao.module.iot.controller.admin.alert.vo.recrod.IotAlertRecordProcessReqVO;
import cn.iocoder.yudao.module.iot.controller.admin.alert.vo.recrod.IotAlertRecordRespVO;
import cn.iocoder.yudao.module.iot.dal.dataobject.alert.IotAlertRecordDO;
import cn.iocoder.yudao.module.iot.service.alert.IotAlertRecordService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.validation.Valid;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
@Tag(name = "管理后台 - IoT 告警记录")
@RestController
@RequestMapping("/iot/alert-record")
@Validated
public class IotAlertRecordController {
@Resource
private IotAlertRecordService alertRecordService;
@GetMapping("/get")
@Operation(summary = "获得告警记录")
@Parameter(name = "id", description = "编号", required = true, example = "1024")
@PreAuthorize("@ss.hasPermission('iot:alert-record:query')")
public CommonResult<IotAlertRecordRespVO> getAlertRecord(@RequestParam("id") Long id) {
IotAlertRecordDO alertRecord = alertRecordService.getAlertRecord(id);
return success(BeanUtils.toBean(alertRecord, IotAlertRecordRespVO.class));
}
@GetMapping("/page")
@Operation(summary = "获得告警记录分页")
@PreAuthorize("@ss.hasPermission('iot:alert-record:query')")
public CommonResult<PageResult<IotAlertRecordRespVO>> getAlertRecordPage(@Valid IotAlertRecordPageReqVO pageReqVO) {
PageResult<IotAlertRecordDO> pageResult = alertRecordService.getAlertRecordPage(pageReqVO);
return success(BeanUtils.toBean(pageResult, IotAlertRecordRespVO.class));
}
@PutMapping("/process")
@Operation(summary = "处理告警记录")
@PreAuthorize("@ss.hasPermission('iot:alert-record:process')")
public CommonResult<Boolean> processAlertRecord(@Valid @RequestBody IotAlertRecordProcessReqVO processReqVO) {
alertRecordService.processAlertRecord(processReqVO);
return success(true);
}
}

View File

@@ -0,0 +1,35 @@
package cn.iocoder.yudao.module.iot.controller.admin.alert.vo.recrod;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
@Schema(description = "管理后台 - IoT 告警记录分页 Request VO")
@Data
public class IotAlertRecordPageReqVO extends PageParam {
@Schema(description = "告警配置编号", example = "29320")
private Long configId;
@Schema(description = "告警级别", example = "1")
private Integer level;
@Schema(description = "产品编号", example = "2050")
private Long productId;
@Schema(description = "设备编号", example = "21727")
private String deviceId;
@Schema(description = "是否处理", example = "true")
private Boolean processStatus;
@Schema(description = "创建时间")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime[] createTime;
}

View File

@@ -0,0 +1,18 @@
package cn.iocoder.yudao.module.iot.controller.admin.alert.vo.recrod;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
@Schema(description = "管理后台 - IoT 告警记录处理 Request VO")
@Data
public class IotAlertRecordProcessReqVO {
@Schema(description = "记录编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
@NotNull(message = "记录编号不能为空")
private Long id;
@Schema(description = "处理结果(备注)", requiredMode = Schema.RequiredMode.REQUIRED, example = "已处理告警,问题已解决")
private String processRemark;
}

View File

@@ -0,0 +1,43 @@
package cn.iocoder.yudao.module.iot.controller.admin.alert.vo.recrod;
import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDateTime;
@Schema(description = "管理后台 - IoT 告警记录 Response VO")
@Data
public class IotAlertRecordRespVO {
@Schema(description = "记录编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "19904")
private Long id;
@Schema(description = "告警配置编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "29320")
private Long configId;
@Schema(description = "告警名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "张三")
private String configName;
@Schema(description = "告警级别", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Integer configLevel;
@Schema(description = "产品编号", example = "2050")
private Long productId;
@Schema(description = "设备编号", example = "21727")
private Long deviceId;
@Schema(description = "触发的设备消息")
private IotDeviceMessage deviceMessage;
@Schema(description = "是否处理", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Boolean processStatus;
@Schema(description = "处理结果(备注)", example = "你说的对")
private String processRemark;
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
private LocalDateTime createTime;
}

View File

@@ -19,7 +19,7 @@ import lombok.NoArgsConstructor;
*
* @author 芋道源码
*/
@TableName("iot_alert_record")
@TableName(value = "iot_alert_record", autoResultMap = true)
@KeySequence("iot_alert_record_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
@Data
@Builder
@@ -44,6 +44,13 @@ public class IotAlertRecordDO extends BaseDO {
* 冗余 {@link IotAlertConfigDO#getName()}
*/
private String configName;
/**
* 告警级别
*
* 冗余 {@link IotAlertConfigDO#getLevel()}
* 字典 {@link cn.iocoder.yudao.module.iot.enums.DictTypeConstants#ALERT_LEVEL}
*/
private Integer configLevel;
/**
* 产品编号
@@ -56,7 +63,7 @@ public class IotAlertRecordDO extends BaseDO {
*
* 关联 {@link IotDeviceDO#getId()}
*/
private String deviceId;
private Long deviceId;
/**
* 触发的设备消息
*/

View File

@@ -198,14 +198,6 @@ public class IotRuleSceneDO extends TenantBaseDO {
*/
private ActionDeviceControl deviceControl;
/**
* 数据桥接编号
*
* 必填:当 {@link #type} 为 {@link IotRuleSceneActionTypeEnum#DATA_BRIDGE} 时
* 关联:{@link IotDataSinkDO#getId()}
*/
private Long dataBridgeId;
}
/**

View File

@@ -3,10 +3,13 @@ package cn.iocoder.yudao.module.iot.dal.mysql.alert;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.framework.mybatis.core.util.MyBatisUtils;
import cn.iocoder.yudao.module.iot.controller.admin.alert.vo.config.IotAlertConfigPageReqVO;
import cn.iocoder.yudao.module.iot.dal.dataobject.alert.IotAlertConfigDO;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
/**
* IoT 告警配置 Mapper
*
@@ -23,4 +26,14 @@ public interface IotAlertConfigMapper extends BaseMapperX<IotAlertConfigDO> {
.orderByDesc(IotAlertConfigDO::getId));
}
default List<IotAlertConfigDO> selectListByStatus(Integer status) {
return selectList(IotAlertConfigDO::getStatus, status);
}
default List<IotAlertConfigDO> selectListBySceneRuleIdAndStatus(Long sceneRuleId, Integer status) {
return selectList(new LambdaQueryWrapperX<IotAlertConfigDO>()
.eq(IotAlertConfigDO::getStatus, status)
.apply(MyBatisUtils.findInSet("scene_rule_id", sceneRuleId)));
}
}

View File

@@ -0,0 +1,29 @@
package cn.iocoder.yudao.module.iot.dal.mysql.alert;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.module.iot.controller.admin.alert.vo.recrod.IotAlertRecordPageReqVO;
import cn.iocoder.yudao.module.iot.dal.dataobject.alert.IotAlertRecordDO;
import org.apache.ibatis.annotations.Mapper;
/**
* IoT 告警记录 Mapper
*
* @author 芋道源码
*/
@Mapper
public interface IotAlertRecordMapper extends BaseMapperX<IotAlertRecordDO> {
default PageResult<IotAlertRecordDO> selectPage(IotAlertRecordPageReqVO reqVO) {
return selectPage(reqVO, new LambdaQueryWrapperX<IotAlertRecordDO>()
.eqIfPresent(IotAlertRecordDO::getConfigId, reqVO.getConfigId())
.eqIfPresent(IotAlertRecordDO::getConfigLevel, reqVO.getLevel())
.eqIfPresent(IotAlertRecordDO::getProductId, reqVO.getProductId())
.eqIfPresent(IotAlertRecordDO::getDeviceId, reqVO.getDeviceId())
.eqIfPresent(IotAlertRecordDO::getProcessStatus, reqVO.getProcessStatus())
.betweenIfPresent(IotAlertRecordDO::getCreateTime, reqVO.getCreateTime())
.orderByDesc(IotAlertRecordDO::getId));
}
}

View File

@@ -67,4 +67,7 @@ public interface ErrorCodeConstants {
// ========== IoT 告警配置 1-050-013-000 ==========
ErrorCode ALERT_CONFIG_NOT_EXISTS = new ErrorCode(1_050_013_000, "IoT 告警配置不存在");
// ========== IoT 告警记录 1-050-014-000 ==========
ErrorCode ALERT_RECORD_NOT_EXISTS = new ErrorCode(1_050_014_000, "IoT 告警记录不存在");
}

View File

@@ -10,8 +10,6 @@ import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
// TODO @puhui999后面重构哈
/**
* 针对 {@link IotDeviceMessage} 的消费者,处理数据流转
*

View File

@@ -6,6 +6,8 @@ import cn.iocoder.yudao.module.iot.controller.admin.alert.vo.config.IotAlertConf
import cn.iocoder.yudao.module.iot.dal.dataobject.alert.IotAlertConfigDO;
import jakarta.validation.Valid;
import java.util.List;
/**
* IoT 告警配置 Service 接口
*
@@ -51,4 +53,20 @@ public interface IotAlertConfigService {
*/
PageResult<IotAlertConfigDO> getAlertConfigPage(IotAlertConfigPageReqVO pageReqVO);
/**
* 获得告警配置列表
*
* @param status 状态
* @return 告警配置列表
*/
List<IotAlertConfigDO> getAlertConfigListByStatus(Integer status);
/**
* 获得告警配置列表
*
* @param sceneRuleId 场景流动规则编号
* @return 告警配置列表
*/
List<IotAlertConfigDO> getAlertConfigListBySceneRuleIdAndStatus(Long sceneRuleId, Integer status);
}

View File

@@ -9,9 +9,12 @@ import cn.iocoder.yudao.module.iot.dal.mysql.alert.IotAlertConfigMapper;
import cn.iocoder.yudao.module.iot.service.rule.scene.IotRuleSceneService;
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
import jakarta.annotation.Resource;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
import java.util.List;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.ALERT_CONFIG_NOT_EXISTS;
@@ -28,6 +31,7 @@ public class IotAlertConfigServiceImpl implements IotAlertConfigService {
private IotAlertConfigMapper alertConfigMapper;
@Resource
@Lazy // 延迟,避免循环依赖报错
private IotRuleSceneService ruleSceneService;
@Resource
@@ -39,6 +43,7 @@ public class IotAlertConfigServiceImpl implements IotAlertConfigService {
ruleSceneService.validateRuleSceneList(createReqVO.getSceneRuleIds());
adminUserApi.validateUserList(createReqVO.getReceiveUserIds());
// 插入
IotAlertConfigDO alertConfig = BeanUtils.toBean(createReqVO, IotAlertConfigDO.class);
alertConfigMapper.insert(alertConfig);
return alertConfig.getId();
@@ -81,4 +86,14 @@ public class IotAlertConfigServiceImpl implements IotAlertConfigService {
return alertConfigMapper.selectPage(pageReqVO);
}
@Override
public List<IotAlertConfigDO> getAlertConfigListByStatus(Integer status) {
return alertConfigMapper.selectListByStatus(status);
}
@Override
public List<IotAlertConfigDO> getAlertConfigListBySceneRuleIdAndStatus(Long sceneRuleId, Integer status) {
return alertConfigMapper.selectListBySceneRuleIdAndStatus(sceneRuleId, status);
}
}

View File

@@ -0,0 +1,49 @@
package cn.iocoder.yudao.module.iot.service.alert;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.iot.controller.admin.alert.vo.recrod.IotAlertRecordPageReqVO;
import cn.iocoder.yudao.module.iot.controller.admin.alert.vo.recrod.IotAlertRecordProcessReqVO;
import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
import cn.iocoder.yudao.module.iot.dal.dataobject.alert.IotAlertConfigDO;
import cn.iocoder.yudao.module.iot.dal.dataobject.alert.IotAlertRecordDO;
/**
* IoT 告警记录 Service 接口
*
* @author 芋道源码
*/
public interface IotAlertRecordService {
/**
* 获得告警记录
*
* @param id 编号
* @return 告警记录
*/
IotAlertRecordDO getAlertRecord(Long id);
/**
* 获得告警记录分页
*
* @param pageReqVO 分页查询
* @return 告警记录分页
*/
PageResult<IotAlertRecordDO> getAlertRecordPage(IotAlertRecordPageReqVO pageReqVO);
/**
* 处理告警记录
*
* @param processReqVO 处理请求
*/
void processAlertRecord(IotAlertRecordProcessReqVO processReqVO);
/**
* 创建告警记录
*
* @param config 告警配置
* @param deviceMessage 设备消息,可为空
* @return 告警记录编号
*/
Long createAlertRecord(IotAlertConfigDO config, IotDeviceMessage deviceMessage);
}

View File

@@ -0,0 +1,80 @@
package cn.iocoder.yudao.module.iot.service.alert;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.iot.controller.admin.alert.vo.recrod.IotAlertRecordPageReqVO;
import cn.iocoder.yudao.module.iot.controller.admin.alert.vo.recrod.IotAlertRecordProcessReqVO;
import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
import cn.iocoder.yudao.module.iot.dal.dataobject.alert.IotAlertConfigDO;
import cn.iocoder.yudao.module.iot.dal.dataobject.alert.IotAlertRecordDO;
import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO;
import cn.iocoder.yudao.module.iot.dal.mysql.alert.IotAlertRecordMapper;
import cn.iocoder.yudao.module.iot.service.device.IotDeviceService;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.ALERT_RECORD_NOT_EXISTS;
/**
* IoT 告警记录 Service 实现类
*
* @author 芋道源码
*/
@Service
@Validated
public class IotAlertRecordServiceImpl implements IotAlertRecordService {
@Resource
private IotAlertRecordMapper alertRecordMapper;
@Resource
private IotDeviceService deviceService;
@Override
public IotAlertRecordDO getAlertRecord(Long id) {
return alertRecordMapper.selectById(id);
}
@Override
public PageResult<IotAlertRecordDO> getAlertRecordPage(IotAlertRecordPageReqVO pageReqVO) {
return alertRecordMapper.selectPage(pageReqVO);
}
@Override
public void processAlertRecord(IotAlertRecordProcessReqVO processReqVO) {
// 校验告警记录是否存在
IotAlertRecordDO alertRecord = alertRecordMapper.selectById(processReqVO.getId());
if (alertRecord == null) {
throw exception(ALERT_RECORD_NOT_EXISTS);
}
// 更新处理状态和备注
alertRecordMapper.updateById(IotAlertRecordDO.builder()
.id(processReqVO.getId())
.processStatus(true)
.processRemark(processReqVO.getProcessRemark())
.build());
}
@Override
public Long createAlertRecord(IotAlertConfigDO config, IotDeviceMessage message) {
// 构建告警记录
IotAlertRecordDO.IotAlertRecordDOBuilder builder = IotAlertRecordDO.builder()
.configId(config.getId()).configName(config.getName()).configLevel(config.getLevel())
.processStatus(false);
if (message != null) {
builder.deviceMessage(message);
// 填充设备信息
IotDeviceDO device = deviceService.getDeviceFromCache(message.getDeviceId());
if (device!= null) {
builder.productId(device.getProductId()).deviceId(device.getId());
}
}
// 插入记录
IotAlertRecordDO record = builder.build();
alertRecordMapper.insert(record);
return record.getId();
}
}

View File

@@ -12,7 +12,7 @@ import java.util.Collection;
import java.util.List;
/**
* IoT 规则场景 Service 接口
* IoT 规则场景规则 Service 接口
*
* @author 芋道源码
*/
@@ -57,10 +57,10 @@ public interface IotRuleSceneService {
PageResult<IotRuleSceneDO> getRuleScenePage(IotRuleScenePageReqVO pageReqVO);
/**
* 校验规则场景编号们是否存在。如下情况,视为无效:
* 1. 规则场景编号不存在
* 校验规则场景联动规则编号们是否存在。如下情况,视为无效:
* 1. 规则场景联动规则编号不存在
*
* @param ids 规则场景编号数组
* @param ids 场景联动规则编号数组
*/
void validateRuleSceneList(Collection<Long> ids);
@@ -91,7 +91,7 @@ public interface IotRuleSceneService {
/**
* 基于 {@link IotRuleSceneTriggerTypeEnum#TIMER} 场景,执行规则场景
*
* @param id 场景编号
* @param id 场景联动规则编号
*/
void executeRuleSceneByTimer(Long id);

View File

@@ -443,7 +443,7 @@ public class IotRuleSceneServiceImpl implements IotRuleSceneService {
// 3.2 执行动作
actions.forEach(action -> {
try {
action.execute(message, actionConfig);
action.execute(message, ruleScene, actionConfig);
log.info("[executeRuleSceneAction][消息({}) 规则场景编号({}) 的执行动作({}) 成功]",
message, ruleScene.getId(), actionConfig);
} catch (Exception e) {

View File

@@ -1,28 +0,0 @@
package cn.iocoder.yudao.module.iot.service.rule.scene.action;
import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotRuleSceneDO;
import cn.iocoder.yudao.module.iot.enums.rule.IotRuleSceneActionTypeEnum;
import org.springframework.stereotype.Component;
import javax.annotation.Nullable;
/**
* IoT 告警触发的 {@link IotSceneRuleAction} 实现类
*
* @author 芋道源码
*/
@Component
public class IotAlertTriggerRuleSceneAction implements IotSceneRuleAction {
@Override
public void execute(@Nullable IotDeviceMessage message, IotRuleSceneDO.ActionConfig config) {
// TODO @AI
}
@Override
public IotRuleSceneActionTypeEnum getType() {
return IotRuleSceneActionTypeEnum.ALERT_TRIGGER;
}
}

View File

@@ -0,0 +1,49 @@
package cn.iocoder.yudao.module.iot.service.rule.scene.action;
import cn.hutool.core.collection.CollUtil;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
import cn.iocoder.yudao.module.iot.dal.dataobject.alert.IotAlertConfigDO;
import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotRuleSceneDO;
import cn.iocoder.yudao.module.iot.enums.rule.IotRuleSceneActionTypeEnum;
import cn.iocoder.yudao.module.iot.service.alert.IotAlertConfigService;
import cn.iocoder.yudao.module.iot.service.alert.IotAlertRecordService;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Component;
import javax.annotation.Nullable;
import java.util.List;
// TODO @puhui999、@芋艿:未测试;需要场景联动开发完
/**
* IoT 告警触发的 {@link IotSceneRuleAction} 实现类
*
* @author 芋道源码
*/
@Component
public class IotAlertTriggerSceneRuleAction implements IotSceneRuleAction {
@Resource
private IotAlertConfigService alertConfigService;
@Resource
private IotAlertRecordService alertRecordService;
@Override
public void execute(@Nullable IotDeviceMessage message,
IotRuleSceneDO rule, IotRuleSceneDO.ActionConfig actionConfig) throws Exception {
List<IotAlertConfigDO> alertConfigs = alertConfigService.getAlertConfigListBySceneRuleIdAndStatus(
rule.getId(), CommonStatusEnum.ENABLE.getStatus());
if (CollUtil.isEmpty(alertConfigs)) {
return;
}
alertConfigs.forEach(alertConfig ->
alertRecordService.createAlertRecord(alertConfig, message));
}
@Override
public IotRuleSceneActionTypeEnum getType() {
return IotRuleSceneActionTypeEnum.ALERT_TRIGGER;
}
}

View File

@@ -26,23 +26,24 @@ public class IotDeviceControlRuleSceneAction implements IotSceneRuleAction {
private IotDeviceMessageService deviceMessageService;
@Override
public void execute(IotDeviceMessage message, IotRuleSceneDO.ActionConfig config) {
IotRuleSceneDO.ActionDeviceControl control = config.getDeviceControl();
public void execute(IotDeviceMessage message,
IotRuleSceneDO rule, IotRuleSceneDO.ActionConfig actionConfig) {
IotRuleSceneDO.ActionDeviceControl control = actionConfig.getDeviceControl();
Assert.notNull(control, "设备控制配置不能为空");
// 遍历每个设备,下发消息
control.getDeviceNames().forEach(deviceName -> {
IotDeviceDO device = deviceService.getDeviceFromCache(control.getProductKey(), deviceName);
if (device == null) {
log.error("[execute][message({}) config({}) 对应的设备不存在]", message, config);
log.error("[execute][message({}) actionConfig({}) 对应的设备不存在]", message, actionConfig);
return;
}
try {
// TODO @芋艿:@puhui999这块可能要改从 type => method
IotDeviceMessage downstreamMessage = deviceMessageService.sendDeviceMessage(IotDeviceMessage.requestOf(
control.getType() + control.getIdentifier(), control.getData()).setDeviceId(device.getId()));
log.info("[execute][message({}) config({}) 下发消息({})成功]", message, config, downstreamMessage);
log.info("[execute][message({}) actionConfig({}) 下发消息({})成功]", message, actionConfig, downstreamMessage);
} catch (Exception e) {
log.error("[execute][message({}) config({}) 下发消息失败]", message, config, e);
log.error("[execute][message({}) actionConfig({}) 下发消息失败]", message, actionConfig, e);
}
});
}

View File

@@ -19,9 +19,12 @@ public interface IotSceneRuleAction {
* @param message 消息,允许空
* 1. 空的情况:定时触发
* 2. 非空的情况:设备触发
* @param config 配置
* @param rule 规则
* @param actionConfig 执行配置(实际对应规则里的哪条执行配置)
*/
void execute(@Nullable IotDeviceMessage message, IotRuleSceneDO.ActionConfig config) throws Exception;
void execute(@Nullable IotDeviceMessage message,
IotRuleSceneDO rule,
IotRuleSceneDO.ActionConfig actionConfig) throws Exception;
/**
* 获得类型