【代码优化】IoT:实现规则 IotRuleSceneDeviceControlAction 执行器
This commit is contained in:
parent
4f84182dab
commit
48cfcdadc1
|
@ -0,0 +1,31 @@
|
||||||
|
package cn.iocoder.yudao.module.iot.enums.rule;
|
||||||
|
|
||||||
|
import cn.iocoder.yudao.framework.common.core.ArrayValuable;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Iot 规则场景的触发类型枚举
|
||||||
|
*
|
||||||
|
* 设备触发,定时触发
|
||||||
|
*/
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
@Getter
|
||||||
|
public enum IotRuleSceneActionTypeEnum implements ArrayValuable<Integer> {
|
||||||
|
|
||||||
|
DEVICE_CONTROL(1), // 设备执行
|
||||||
|
ALERT(2), // 告警执行
|
||||||
|
DATA_BRIDGE(3); // 桥接执行
|
||||||
|
|
||||||
|
private final Integer type;
|
||||||
|
|
||||||
|
public static final Integer[] ARRAYS = Arrays.stream(values()).map(IotRuleSceneActionTypeEnum::getType).toArray(Integer[]::new);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Integer[] array() {
|
||||||
|
return ARRAYS;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.iot.dal.dataobject.device;
|
||||||
|
|
||||||
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
|
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
|
||||||
import cn.iocoder.yudao.framework.mybatis.core.type.LongSetTypeHandler;
|
import cn.iocoder.yudao.framework.mybatis.core.type.LongSetTypeHandler;
|
||||||
|
import cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO;
|
||||||
import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO;
|
import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO;
|
||||||
import cn.iocoder.yudao.module.iot.enums.device.IotDeviceStateEnum;
|
import cn.iocoder.yudao.module.iot.enums.device.IotDeviceStateEnum;
|
||||||
import com.baomidou.mybatisplus.annotation.KeySequence;
|
import com.baomidou.mybatisplus.annotation.KeySequence;
|
||||||
|
@ -27,7 +28,7 @@ import java.util.Set;
|
||||||
@Builder
|
@Builder
|
||||||
@NoArgsConstructor
|
@NoArgsConstructor
|
||||||
@AllArgsConstructor
|
@AllArgsConstructor
|
||||||
public class IotDeviceDO extends BaseDO {
|
public class IotDeviceDO extends TenantBaseDO {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 设备 ID,主键,自增
|
* 设备 ID,主键,自增
|
||||||
|
|
|
@ -1,12 +1,14 @@
|
||||||
package cn.iocoder.yudao.module.iot.dal.dataobject.rule;
|
package cn.iocoder.yudao.module.iot.dal.dataobject.rule;
|
||||||
|
|
||||||
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
|
import cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO;
|
||||||
import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO;
|
import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO;
|
||||||
import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO;
|
import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO;
|
||||||
import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotThingModelDO;
|
import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotThingModelDO;
|
||||||
import cn.iocoder.yudao.module.iot.enums.device.IotDeviceMessageIdentifierEnum;
|
import cn.iocoder.yudao.module.iot.enums.device.IotDeviceMessageIdentifierEnum;
|
||||||
import cn.iocoder.yudao.module.iot.enums.device.IotDeviceMessageTypeEnum;
|
import cn.iocoder.yudao.module.iot.enums.device.IotDeviceMessageTypeEnum;
|
||||||
|
import cn.iocoder.yudao.module.iot.enums.rule.IotRuleSceneActionTypeEnum;
|
||||||
import cn.iocoder.yudao.module.iot.enums.rule.IotRuleSceneTriggerConditionParameterOperatorEnum;
|
import cn.iocoder.yudao.module.iot.enums.rule.IotRuleSceneTriggerConditionParameterOperatorEnum;
|
||||||
|
import cn.iocoder.yudao.module.iot.enums.rule.IotRuleSceneTriggerTypeEnum;
|
||||||
import com.baomidou.mybatisplus.annotation.KeySequence;
|
import com.baomidou.mybatisplus.annotation.KeySequence;
|
||||||
import com.baomidou.mybatisplus.annotation.TableField;
|
import com.baomidou.mybatisplus.annotation.TableField;
|
||||||
import com.baomidou.mybatisplus.annotation.TableId;
|
import com.baomidou.mybatisplus.annotation.TableId;
|
||||||
|
@ -30,7 +32,7 @@ import java.util.Map;
|
||||||
@Builder
|
@Builder
|
||||||
@NoArgsConstructor
|
@NoArgsConstructor
|
||||||
@AllArgsConstructor
|
@AllArgsConstructor
|
||||||
public class IotRuleSceneDO extends BaseDO {
|
public class IotRuleSceneDO extends TenantBaseDO {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 场景编号
|
* 场景编号
|
||||||
|
@ -56,24 +58,26 @@ public class IotRuleSceneDO extends BaseDO {
|
||||||
* 触发器数组
|
* 触发器数组
|
||||||
*/
|
*/
|
||||||
@TableField(typeHandler = JacksonTypeHandler.class)
|
@TableField(typeHandler = JacksonTypeHandler.class)
|
||||||
private List<Trigger> triggers;
|
private List<TriggerConfig> triggers;
|
||||||
|
|
||||||
|
// TODO @芋艿:需要调研下 https://help.aliyun.com/zh/iot/user-guide/scene-orchestration-1?spm=a2c4g.11186623.help-menu-30520.d_2_4_5_0.45413908fxCSVa
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 执行器数组
|
* 执行器数组
|
||||||
*/
|
*/
|
||||||
@TableField(typeHandler = JacksonTypeHandler.class)
|
@TableField(typeHandler = JacksonTypeHandler.class)
|
||||||
private List<Actuator> actuators;
|
private List<ActionConfig> actions;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 触发器
|
* 触发器配置
|
||||||
*/
|
*/
|
||||||
@Data
|
@Data
|
||||||
public static class Trigger {
|
public static class TriggerConfig {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 触发类型
|
* 触发类型
|
||||||
*
|
*
|
||||||
* 枚举 {@link cn.iocoder.yudao.module.iot.enums.rule.IotRuleSceneTriggerTypeEnum}
|
* 枚举 {@link IotRuleSceneTriggerTypeEnum}
|
||||||
*/
|
*/
|
||||||
private Integer type;
|
private Integer type;
|
||||||
|
|
||||||
|
@ -93,14 +97,15 @@ public class IotRuleSceneDO extends BaseDO {
|
||||||
/**
|
/**
|
||||||
* 触发条件数组
|
* 触发条件数组
|
||||||
*
|
*
|
||||||
* TODO @芋艿:注释说明
|
* 当 {@link #type} 为 {@link IotRuleSceneTriggerTypeEnum#DEVICE} 时,必填
|
||||||
|
* 条件与条件之间,是“或”的关系
|
||||||
*/
|
*/
|
||||||
private List<TriggerCondition> conditions;
|
private List<TriggerCondition> conditions;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* CRON 表达式
|
* CRON 表达式
|
||||||
*
|
*
|
||||||
* TODO @芋艿:注释说明
|
* 当 {@link #type} 为 {@link IotRuleSceneTriggerTypeEnum#TIMER} 时,必填
|
||||||
*/
|
*/
|
||||||
private String cronExpression;
|
private String cronExpression;
|
||||||
|
|
||||||
|
@ -127,6 +132,8 @@ public class IotRuleSceneDO extends BaseDO {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 参数数组
|
* 参数数组
|
||||||
|
*
|
||||||
|
* 参数与参数之间,是“或”的关系
|
||||||
*/
|
*/
|
||||||
private List<TriggerConditionParameter> parameters;
|
private List<TriggerConditionParameter> parameters;
|
||||||
|
|
||||||
|
@ -163,18 +170,41 @@ public class IotRuleSceneDO extends BaseDO {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 执行器
|
* 执行器配置
|
||||||
*/
|
*/
|
||||||
@Data
|
@Data
|
||||||
public static class Actuator {
|
public static class ActionConfig {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 执行类型
|
* 执行类型
|
||||||
*
|
*
|
||||||
* TODO @芋艿:control、alert、webhook(待定)
|
* 枚举 {@link IotRuleSceneActionTypeEnum}
|
||||||
*/
|
*/
|
||||||
private Integer type;
|
private Integer type;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设备控制
|
||||||
|
*
|
||||||
|
* 当 {@link #type} 为 {@link IotRuleSceneActionTypeEnum#DEVICE_CONTROL} 时,必填
|
||||||
|
*/
|
||||||
|
private ActionDeviceControl deviceControl;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 数据桥接编号
|
||||||
|
*
|
||||||
|
* 当 {@link #type} 为 {@link IotRuleSceneActionTypeEnum#DATA_BRIDGE} 时,必填
|
||||||
|
* TODO 芋艿:关联
|
||||||
|
*/
|
||||||
|
private Long dataBridgeId;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 执行设备控制
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public static class ActionDeviceControl {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 产品标识
|
* 产品标识
|
||||||
*
|
*
|
||||||
|
@ -188,35 +218,12 @@ public class IotRuleSceneDO extends BaseDO {
|
||||||
*/
|
*/
|
||||||
private List<String> deviceNames;
|
private List<String> deviceNames;
|
||||||
|
|
||||||
/**
|
|
||||||
* 控制数组
|
|
||||||
*
|
|
||||||
* TODO 芋艿:类型的情况下
|
|
||||||
*/
|
|
||||||
private List<ActuatorControl> controls;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 数据桥接编号
|
|
||||||
*
|
|
||||||
* TODO 芋艿:暂定!
|
|
||||||
* TODO 芋艿:关联
|
|
||||||
*/
|
|
||||||
private Long bridgeId;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 执行器控制
|
|
||||||
*/
|
|
||||||
@Data
|
|
||||||
public static class ActuatorControl {
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 消息类型
|
* 消息类型
|
||||||
*
|
*
|
||||||
* 枚举 {@link IotDeviceMessageTypeEnum#PROPERTY}、{@link IotDeviceMessageTypeEnum#SERVICE}
|
* 枚举 {@link IotDeviceMessageTypeEnum#PROPERTY}、{@link IotDeviceMessageTypeEnum#SERVICE}
|
||||||
*/
|
*/
|
||||||
private Integer type;
|
private String type;
|
||||||
/**
|
/**
|
||||||
* 消息标识符
|
* 消息标识符
|
||||||
*
|
*
|
||||||
|
|
|
@ -17,7 +17,7 @@ public interface RedisKeyConstants {
|
||||||
* HASH KEY:identifier 属性标识
|
* HASH KEY:identifier 属性标识
|
||||||
* VALUE 数据类型:String(JSON) {@link IotDevicePropertyDO}
|
* VALUE 数据类型:String(JSON) {@link IotDevicePropertyDO}
|
||||||
*/
|
*/
|
||||||
String DEVICE_PROPERTY = "device_property:%s";
|
String DEVICE_PROPERTY = "iot:device_property:%s";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 设备的最后上报时间,采用 ZSET 结构
|
* 设备的最后上报时间,采用 ZSET 结构
|
||||||
|
@ -25,23 +25,23 @@ public interface RedisKeyConstants {
|
||||||
* KEY 格式:{deviceKey}
|
* KEY 格式:{deviceKey}
|
||||||
* SCORE:上报时间
|
* SCORE:上报时间
|
||||||
*/
|
*/
|
||||||
String DEVICE_REPORT_TIMES = "device_report_times";
|
String DEVICE_REPORT_TIMES = "iot:device_report_times";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 设备信息的数据缓存,使用 Spring Cache 操作
|
* 设备信息的数据缓存,使用 Spring Cache 操作(忽略租户)
|
||||||
*
|
*
|
||||||
* KEY 格式:device_${productKey}_${deviceKey}
|
* KEY 格式:device_${productKey}_${deviceKey}
|
||||||
* VALUE 数据类型:String(JSON)
|
* VALUE 数据类型:String(JSON)
|
||||||
*/
|
*/
|
||||||
String DEVICE = "device";
|
String DEVICE = "iot:device";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 物模型的数据缓存,使用 Spring Cache 操作
|
* 物模型的数据缓存,使用 Spring Cache 操作(忽略租户)
|
||||||
*
|
*
|
||||||
* KEY 格式:thing_model_${productKey}
|
* KEY 格式:thing_model_${productKey}
|
||||||
* VALUE 数据类型:String 数组(JSON),即 {@link cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotThingModelDO} 列表
|
* VALUE 数据类型:String 数组(JSON),即 {@link cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotThingModelDO} 列表
|
||||||
*/
|
*/
|
||||||
String THING_MODEL_LIST = "thing_model_list";
|
String THING_MODEL_LIST = "iot:thing_model_list";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 设备插件的插件进程编号的映射,采用 HASH 结构
|
* 设备插件的插件进程编号的映射,采用 HASH 结构
|
||||||
|
@ -50,6 +50,6 @@ public interface RedisKeyConstants {
|
||||||
* HASH KEY:${deviceKey}
|
* HASH KEY:${deviceKey}
|
||||||
* VALUE:插件进程编号,对应 {@link IotPluginInstanceDO#getProcessId()} 字段
|
* VALUE:插件进程编号,对应 {@link IotPluginInstanceDO#getProcessId()} 字段
|
||||||
*/
|
*/
|
||||||
String DEVICE_PLUGIN_INSTANCE_PROCESS_IDS = "device_plugin_instance_process_ids";
|
String DEVICE_PLUGIN_INSTANCE_PROCESS_IDS = "iot:device_plugin_instance_process_ids";
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,7 +24,7 @@ public class IotRuleSceneMessageHandler {
|
||||||
@Async
|
@Async
|
||||||
public void onMessage(IotDeviceMessage message) {
|
public void onMessage(IotDeviceMessage message) {
|
||||||
log.info("[onMessage][消息内容({})]", message);
|
log.info("[onMessage][消息内容({})]", message);
|
||||||
ruleSceneService.executeRuleScene(message);
|
ruleSceneService.executeRuleSceneByDevice(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +0,0 @@
|
||||||
/**
|
|
||||||
* TODO 芋艿:未来实现一个 IotRuleMessageConsumer
|
|
||||||
*/
|
|
||||||
package cn.iocoder.yudao.module.iot.mq.consumer.rule;
|
|
|
@ -67,4 +67,9 @@ public class IotDeviceMessage {
|
||||||
*/
|
*/
|
||||||
private LocalDateTime reportTime;
|
private LocalDateTime reportTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 租户编号
|
||||||
|
*/
|
||||||
|
private Long tenantId;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,7 @@ import cn.iocoder.yudao.framework.common.exception.ServiceException;
|
||||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||||
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
||||||
import cn.iocoder.yudao.framework.common.util.validation.ValidationUtils;
|
import cn.iocoder.yudao.framework.common.util.validation.ValidationUtils;
|
||||||
|
import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore;
|
||||||
import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils;
|
import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils;
|
||||||
import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.*;
|
import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.*;
|
||||||
import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO;
|
import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO;
|
||||||
|
@ -261,13 +262,9 @@ public class IotDeviceServiceImpl implements IotDeviceService {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public IotDeviceDO getDeviceByProductKeyAndDeviceNameFromCache(String productKey, String deviceName) {
|
|
||||||
// 保证在 @CacheEvict 之前,忽略租户
|
|
||||||
return TenantUtils.executeIgnore(() -> getSelf().getDeviceByProductKeyAndDeviceNameFromCache0(productKey, deviceName));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Cacheable(value = RedisKeyConstants.DEVICE, key = "#productKey + '_' + #deviceName", unless = "#result == null")
|
@Cacheable(value = RedisKeyConstants.DEVICE, key = "#productKey + '_' + #deviceName", unless = "#result == null")
|
||||||
public IotDeviceDO getDeviceByProductKeyAndDeviceNameFromCache0(String productKey, String deviceName) {
|
@TenantIgnore // 忽略租户信息,跨租户 productKey + deviceName 是唯一的
|
||||||
|
public IotDeviceDO getDeviceByProductKeyAndDeviceNameFromCache(String productKey, String deviceName) {
|
||||||
return deviceMapper.selectByProductKeyAndDeviceName(productKey, deviceName);
|
return deviceMapper.selectByProductKeyAndDeviceName(productKey, deviceName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -389,8 +386,8 @@ public class IotDeviceServiceImpl implements IotDeviceService {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void deleteDeviceCache(IotDeviceDO device) {
|
private void deleteDeviceCache(IotDeviceDO device) {
|
||||||
// 保证在 @CacheEvict 之前,忽略租户
|
// 保证 Spring AOP 触发
|
||||||
TenantUtils.executeIgnore(() -> getSelf().deleteDeviceCache0(device));
|
getSelf().deleteDeviceCache0(device);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void deleteDeviceCache(List<IotDeviceDO> devices) {
|
private void deleteDeviceCache(List<IotDeviceDO> devices) {
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package cn.iocoder.yudao.module.iot.service.device.control;
|
package cn.iocoder.yudao.module.iot.service.device.control;
|
||||||
|
|
||||||
import cn.iocoder.yudao.module.iot.controller.admin.device.vo.control.IotDeviceDownstreamReqVO;
|
import cn.iocoder.yudao.module.iot.controller.admin.device.vo.control.IotDeviceDownstreamReqVO;
|
||||||
|
import cn.iocoder.yudao.module.iot.mq.message.IotDeviceMessage;
|
||||||
import jakarta.validation.Valid;
|
import jakarta.validation.Valid;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -16,7 +17,8 @@ public interface IotDeviceDownstreamService {
|
||||||
* 设备下行,可用于设备模拟
|
* 设备下行,可用于设备模拟
|
||||||
*
|
*
|
||||||
* @param downstreamReqVO 设备下行请求 VO
|
* @param downstreamReqVO 设备下行请求 VO
|
||||||
|
* @return 下发消息
|
||||||
*/
|
*/
|
||||||
void downstreamDevice(@Valid IotDeviceDownstreamReqVO downstreamReqVO);
|
IotDeviceMessage downstreamDevice(@Valid IotDeviceDownstreamReqVO downstreamReqVO);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -54,7 +54,7 @@ public class IotDeviceDownstreamServiceImpl implements IotDeviceDownstreamServic
|
||||||
private IotDeviceProducer deviceProducer;
|
private IotDeviceProducer deviceProducer;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void downstreamDevice(IotDeviceDownstreamReqVO downstreamReqVO) {
|
public IotDeviceMessage downstreamDevice(IotDeviceDownstreamReqVO downstreamReqVO) {
|
||||||
// 校验设备是否存在
|
// 校验设备是否存在
|
||||||
IotDeviceDO device = deviceService.validateDeviceExists(downstreamReqVO.getId());
|
IotDeviceDO device = deviceService.validateDeviceExists(downstreamReqVO.getId());
|
||||||
// TODO 芋艿:父设备的处理
|
// TODO 芋艿:父设备的处理
|
||||||
|
@ -62,31 +62,28 @@ public class IotDeviceDownstreamServiceImpl implements IotDeviceDownstreamServic
|
||||||
|
|
||||||
// 服务调用
|
// 服务调用
|
||||||
if (Objects.equals(downstreamReqVO.getType(), IotDeviceMessageTypeEnum.SERVICE.getType())) {
|
if (Objects.equals(downstreamReqVO.getType(), IotDeviceMessageTypeEnum.SERVICE.getType())) {
|
||||||
invokeDeviceService(downstreamReqVO, device, parentDevice);
|
return invokeDeviceService(downstreamReqVO, device, parentDevice);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
// 属性相关
|
// 属性相关
|
||||||
if (Objects.equals(downstreamReqVO.getType(), IotDeviceMessageTypeEnum.PROPERTY.getType())) {
|
if (Objects.equals(downstreamReqVO.getType(), IotDeviceMessageTypeEnum.PROPERTY.getType())) {
|
||||||
// 属性设置
|
// 属性设置
|
||||||
if (Objects.equals(downstreamReqVO.getIdentifier(),
|
if (Objects.equals(downstreamReqVO.getIdentifier(),
|
||||||
IotDeviceMessageIdentifierEnum.PROPERTY_SET.getIdentifier())) {
|
IotDeviceMessageIdentifierEnum.PROPERTY_SET.getIdentifier())) {
|
||||||
setDeviceProperty(downstreamReqVO, device, parentDevice);
|
return setDeviceProperty(downstreamReqVO, device, parentDevice);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
// 属性设置
|
// 属性设置
|
||||||
if (Objects.equals(downstreamReqVO.getIdentifier(),
|
if (Objects.equals(downstreamReqVO.getIdentifier(),
|
||||||
IotDeviceMessageIdentifierEnum.PROPERTY_GET.getIdentifier())) {
|
IotDeviceMessageIdentifierEnum.PROPERTY_GET.getIdentifier())) {
|
||||||
getDeviceProperty(downstreamReqVO, device, parentDevice);
|
return getDeviceProperty(downstreamReqVO, device, parentDevice);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// 配置下发
|
// 配置下发
|
||||||
if (Objects.equals(downstreamReqVO.getType(), IotDeviceMessageTypeEnum.CONFIG.getType())
|
if (Objects.equals(downstreamReqVO.getType(), IotDeviceMessageTypeEnum.CONFIG.getType())
|
||||||
&& Objects.equals(downstreamReqVO.getIdentifier(), IotDeviceMessageIdentifierEnum.CONFIG_SET.getIdentifier())) {
|
&& Objects.equals(downstreamReqVO.getIdentifier(), IotDeviceMessageIdentifierEnum.CONFIG_SET.getIdentifier())) {
|
||||||
setDeviceConfig(downstreamReqVO, device, parentDevice);
|
return setDeviceConfig(downstreamReqVO, device, parentDevice);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
// TODO 芋艿:ota 升级
|
// TODO 芋艿:ota 升级
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -95,9 +92,10 @@ public class IotDeviceDownstreamServiceImpl implements IotDeviceDownstreamServic
|
||||||
* @param downstreamReqVO 下行请求
|
* @param downstreamReqVO 下行请求
|
||||||
* @param device 设备
|
* @param device 设备
|
||||||
* @param parentDevice 父设备
|
* @param parentDevice 父设备
|
||||||
|
* @return 下发消息
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
private void invokeDeviceService(IotDeviceDownstreamReqVO downstreamReqVO,
|
private IotDeviceMessage invokeDeviceService(IotDeviceDownstreamReqVO downstreamReqVO,
|
||||||
IotDeviceDO device, IotDeviceDO parentDevice) {
|
IotDeviceDO device, IotDeviceDO parentDevice) {
|
||||||
// 1. 参数校验
|
// 1. 参数校验
|
||||||
if (!(downstreamReqVO.getData() instanceof Map<?, ?>)) {
|
if (!(downstreamReqVO.getData() instanceof Map<?, ?>)) {
|
||||||
|
@ -124,6 +122,7 @@ public class IotDeviceDownstreamServiceImpl implements IotDeviceDownstreamServic
|
||||||
device.getDeviceKey(), reqDTO, result);
|
device.getDeviceKey(), reqDTO, result);
|
||||||
throw exception(DEVICE_DOWNSTREAM_FAILED, result.getMsg());
|
throw exception(DEVICE_DOWNSTREAM_FAILED, result.getMsg());
|
||||||
}
|
}
|
||||||
|
return message;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -132,9 +131,10 @@ public class IotDeviceDownstreamServiceImpl implements IotDeviceDownstreamServic
|
||||||
* @param downstreamReqVO 下行请求
|
* @param downstreamReqVO 下行请求
|
||||||
* @param device 设备
|
* @param device 设备
|
||||||
* @param parentDevice 父设备
|
* @param parentDevice 父设备
|
||||||
|
* @return 下发消息
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
private void setDeviceProperty(IotDeviceDownstreamReqVO downstreamReqVO,
|
private IotDeviceMessage setDeviceProperty(IotDeviceDownstreamReqVO downstreamReqVO,
|
||||||
IotDeviceDO device, IotDeviceDO parentDevice) {
|
IotDeviceDO device, IotDeviceDO parentDevice) {
|
||||||
// 1. 参数校验
|
// 1. 参数校验
|
||||||
if (!(downstreamReqVO.getData() instanceof Map<?, ?>)) {
|
if (!(downstreamReqVO.getData() instanceof Map<?, ?>)) {
|
||||||
|
@ -162,6 +162,7 @@ public class IotDeviceDownstreamServiceImpl implements IotDeviceDownstreamServic
|
||||||
device.getDeviceKey(), reqDTO, result);
|
device.getDeviceKey(), reqDTO, result);
|
||||||
throw exception(DEVICE_DOWNSTREAM_FAILED, result.getMsg());
|
throw exception(DEVICE_DOWNSTREAM_FAILED, result.getMsg());
|
||||||
}
|
}
|
||||||
|
return message;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -170,9 +171,10 @@ public class IotDeviceDownstreamServiceImpl implements IotDeviceDownstreamServic
|
||||||
* @param downstreamReqVO 下行请求
|
* @param downstreamReqVO 下行请求
|
||||||
* @param device 设备
|
* @param device 设备
|
||||||
* @param parentDevice 父设备
|
* @param parentDevice 父设备
|
||||||
|
* @return 下发消息
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
private void getDeviceProperty(IotDeviceDownstreamReqVO downstreamReqVO,
|
private IotDeviceMessage getDeviceProperty(IotDeviceDownstreamReqVO downstreamReqVO,
|
||||||
IotDeviceDO device, IotDeviceDO parentDevice) {
|
IotDeviceDO device, IotDeviceDO parentDevice) {
|
||||||
// 1. 参数校验
|
// 1. 参数校验
|
||||||
if (!(downstreamReqVO.getData() instanceof List<?>)) {
|
if (!(downstreamReqVO.getData() instanceof List<?>)) {
|
||||||
|
@ -200,6 +202,7 @@ public class IotDeviceDownstreamServiceImpl implements IotDeviceDownstreamServic
|
||||||
device.getDeviceKey(), reqDTO, result);
|
device.getDeviceKey(), reqDTO, result);
|
||||||
throw exception(DEVICE_DOWNSTREAM_FAILED, result.getMsg());
|
throw exception(DEVICE_DOWNSTREAM_FAILED, result.getMsg());
|
||||||
}
|
}
|
||||||
|
return message;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -208,9 +211,10 @@ public class IotDeviceDownstreamServiceImpl implements IotDeviceDownstreamServic
|
||||||
* @param downstreamReqVO 下行请求
|
* @param downstreamReqVO 下行请求
|
||||||
* @param device 设备
|
* @param device 设备
|
||||||
* @param parentDevice 父设备
|
* @param parentDevice 父设备
|
||||||
|
* @return 下发消息
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings({"unchecked", "unused"})
|
@SuppressWarnings({"unchecked", "unused"})
|
||||||
private void setDeviceConfig(IotDeviceDownstreamReqVO downstreamReqVO,
|
private IotDeviceMessage setDeviceConfig(IotDeviceDownstreamReqVO downstreamReqVO,
|
||||||
IotDeviceDO device, IotDeviceDO parentDevice) {
|
IotDeviceDO device, IotDeviceDO parentDevice) {
|
||||||
// 1. 参数转换,无需校验
|
// 1. 参数转换,无需校验
|
||||||
Map<String, Object> config = JsonUtils.parseObject(device.getConfig(), Map.class);
|
Map<String, Object> config = JsonUtils.parseObject(device.getConfig(), Map.class);
|
||||||
|
@ -235,6 +239,7 @@ public class IotDeviceDownstreamServiceImpl implements IotDeviceDownstreamServic
|
||||||
device.getDeviceKey(), reqDTO, result);
|
device.getDeviceKey(), reqDTO, result);
|
||||||
throw exception(DEVICE_DOWNSTREAM_FAILED, result.getMsg());
|
throw exception(DEVICE_DOWNSTREAM_FAILED, result.getMsg());
|
||||||
}
|
}
|
||||||
|
return message;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -275,7 +280,8 @@ public class IotDeviceDownstreamServiceImpl implements IotDeviceDownstreamServic
|
||||||
private void sendDeviceMessage(IotDeviceMessage message, IotDeviceDO device, Integer code) {
|
private void sendDeviceMessage(IotDeviceMessage message, IotDeviceDO device, Integer code) {
|
||||||
// 1. 完善消息
|
// 1. 完善消息
|
||||||
message.setProductKey(device.getProductKey()).setDeviceName(device.getDeviceName())
|
message.setProductKey(device.getProductKey()).setDeviceName(device.getDeviceName())
|
||||||
.setDeviceKey(device.getDeviceKey());
|
.setDeviceKey(device.getDeviceKey())
|
||||||
|
.setTenantId(device.getTenantId());
|
||||||
Assert.notNull(message.getRequestId(), "requestId 不能为空");
|
Assert.notNull(message.getRequestId(), "requestId 不能为空");
|
||||||
if (message.getReportTime() == null) {
|
if (message.getReportTime() == null) {
|
||||||
message.setReportTime(LocalDateTime.now());
|
message.setReportTime(LocalDateTime.now());
|
||||||
|
|
|
@ -98,6 +98,7 @@ public class IotDeviceUpstreamServiceImpl implements IotDeviceUpstreamService {
|
||||||
updateReqDTO.getProductKey(), updateReqDTO.getDeviceName());
|
updateReqDTO.getProductKey(), updateReqDTO.getDeviceName());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
TenantUtils.execute(device.getTenantId(), () -> {
|
||||||
// 1.2 记录设备的最后时间
|
// 1.2 记录设备的最后时间
|
||||||
updateDeviceLastTime(device, updateReqDTO);
|
updateDeviceLastTime(device, updateReqDTO);
|
||||||
// 1.3 当前状态一致,不处理
|
// 1.3 当前状态一致,不处理
|
||||||
|
@ -106,8 +107,7 @@ public class IotDeviceUpstreamServiceImpl implements IotDeviceUpstreamService {
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. 更新设备状态
|
// 2. 更新设备状态
|
||||||
TenantUtils.executeIgnore(() ->
|
deviceService.updateDeviceState(device.getId(), updateReqDTO.getState());
|
||||||
deviceService.updateDeviceState(device.getId(), updateReqDTO.getState()));
|
|
||||||
|
|
||||||
// 3. TODO 芋艿:子设备的关联
|
// 3. TODO 芋艿:子设备的关联
|
||||||
|
|
||||||
|
@ -118,6 +118,7 @@ public class IotDeviceUpstreamServiceImpl implements IotDeviceUpstreamService {
|
||||||
? IotDeviceMessageIdentifierEnum.STATE_ONLINE.getIdentifier()
|
? IotDeviceMessageIdentifierEnum.STATE_ONLINE.getIdentifier()
|
||||||
: IotDeviceMessageIdentifierEnum.STATE_OFFLINE.getIdentifier());
|
: IotDeviceMessageIdentifierEnum.STATE_OFFLINE.getIdentifier());
|
||||||
sendDeviceMessage(message, device);
|
sendDeviceMessage(message, device);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -174,7 +175,8 @@ public class IotDeviceUpstreamServiceImpl implements IotDeviceUpstreamService {
|
||||||
|
|
||||||
private void sendDeviceMessage(IotDeviceMessage message, IotDeviceDO device) {
|
private void sendDeviceMessage(IotDeviceMessage message, IotDeviceDO device) {
|
||||||
// 1. 完善消息
|
// 1. 完善消息
|
||||||
message.setDeviceKey(device.getDeviceKey());
|
message.setDeviceKey(device.getDeviceKey())
|
||||||
|
.setTenantId(device.getTenantId());
|
||||||
if (StrUtil.isEmpty(message.getRequestId())) {
|
if (StrUtil.isEmpty(message.getRequestId())) {
|
||||||
message.setRequestId(IdUtil.fastSimpleUUID());
|
message.setRequestId(IdUtil.fastSimpleUUID());
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package cn.iocoder.yudao.module.iot.service.rule;
|
package cn.iocoder.yudao.module.iot.service.rule;
|
||||||
|
|
||||||
import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotRuleSceneDO;
|
import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotRuleSceneDO;
|
||||||
|
import cn.iocoder.yudao.module.iot.enums.rule.IotRuleSceneTriggerTypeEnum;
|
||||||
import cn.iocoder.yudao.module.iot.mq.message.IotDeviceMessage;
|
import cn.iocoder.yudao.module.iot.mq.message.IotDeviceMessage;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -22,10 +23,12 @@ public interface IotRuleSceneService {
|
||||||
List<IotRuleSceneDO> getRuleSceneListByProductKeyAndDeviceNameFromCache(String productKey, String deviceName);
|
List<IotRuleSceneDO> getRuleSceneListByProductKeyAndDeviceNameFromCache(String productKey, String deviceName);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 执行规则场景
|
* 基于 {@link IotRuleSceneTriggerTypeEnum#DEVICE} 场景,执行规则场景
|
||||||
*
|
*
|
||||||
* @param message 消息
|
* @param message 消息
|
||||||
*/
|
*/
|
||||||
void executeRuleScene(IotDeviceMessage message);
|
void executeRuleSceneByDevice(IotDeviceMessage message);
|
||||||
|
|
||||||
|
// TODO @芋艿:基于 timer 场景,执行规则场景
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,13 +11,16 @@ import cn.iocoder.yudao.framework.common.util.number.NumberUtils;
|
||||||
import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
|
import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
|
||||||
import cn.iocoder.yudao.framework.common.util.spring.SpringExpressionUtils;
|
import cn.iocoder.yudao.framework.common.util.spring.SpringExpressionUtils;
|
||||||
import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore;
|
import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore;
|
||||||
|
import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils;
|
||||||
import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotRuleSceneDO;
|
import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotRuleSceneDO;
|
||||||
import cn.iocoder.yudao.module.iot.dal.mysql.rule.IotRuleSceneMapper;
|
import cn.iocoder.yudao.module.iot.dal.mysql.rule.IotRuleSceneMapper;
|
||||||
import cn.iocoder.yudao.module.iot.enums.device.IotDeviceMessageIdentifierEnum;
|
import cn.iocoder.yudao.module.iot.enums.device.IotDeviceMessageIdentifierEnum;
|
||||||
import cn.iocoder.yudao.module.iot.enums.device.IotDeviceMessageTypeEnum;
|
import cn.iocoder.yudao.module.iot.enums.device.IotDeviceMessageTypeEnum;
|
||||||
|
import cn.iocoder.yudao.module.iot.enums.rule.IotRuleSceneActionTypeEnum;
|
||||||
import cn.iocoder.yudao.module.iot.enums.rule.IotRuleSceneTriggerConditionParameterOperatorEnum;
|
import cn.iocoder.yudao.module.iot.enums.rule.IotRuleSceneTriggerConditionParameterOperatorEnum;
|
||||||
import cn.iocoder.yudao.module.iot.enums.rule.IotRuleSceneTriggerTypeEnum;
|
import cn.iocoder.yudao.module.iot.enums.rule.IotRuleSceneTriggerTypeEnum;
|
||||||
import cn.iocoder.yudao.module.iot.mq.message.IotDeviceMessage;
|
import cn.iocoder.yudao.module.iot.mq.message.IotDeviceMessage;
|
||||||
|
import cn.iocoder.yudao.module.iot.service.rule.action.IotRuleSceneAction;
|
||||||
import jakarta.annotation.Resource;
|
import jakarta.annotation.Resource;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
@ -43,6 +46,9 @@ public class IotRuleSceneServiceImpl implements IotRuleSceneService {
|
||||||
@Resource
|
@Resource
|
||||||
private IotRuleSceneMapper ruleSceneMapper;
|
private IotRuleSceneMapper ruleSceneMapper;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private List<IotRuleSceneAction> ruleSceneActions;
|
||||||
|
|
||||||
// TODO 芋艿,缓存待实现
|
// TODO 芋艿,缓存待实现
|
||||||
@Override
|
@Override
|
||||||
@TenantIgnore // 忽略租户隔离:因为 IotRuleSceneMessageHandler 调用时,一般未传递租户,所以需要忽略
|
@TenantIgnore // 忽略租户隔离:因为 IotRuleSceneMessageHandler 调用时,一般未传递租户,所以需要忽略
|
||||||
|
@ -50,7 +56,7 @@ public class IotRuleSceneServiceImpl implements IotRuleSceneService {
|
||||||
if (true) {
|
if (true) {
|
||||||
IotRuleSceneDO ruleScene01 = new IotRuleSceneDO();
|
IotRuleSceneDO ruleScene01 = new IotRuleSceneDO();
|
||||||
ruleScene01.setTriggers(CollUtil.newArrayList());
|
ruleScene01.setTriggers(CollUtil.newArrayList());
|
||||||
IotRuleSceneDO.Trigger trigger01 = new IotRuleSceneDO.Trigger();
|
IotRuleSceneDO.TriggerConfig trigger01 = new IotRuleSceneDO.TriggerConfig();
|
||||||
trigger01.setType(IotRuleSceneTriggerTypeEnum.DEVICE.getType());
|
trigger01.setType(IotRuleSceneTriggerTypeEnum.DEVICE.getType());
|
||||||
trigger01.setConditions(CollUtil.newArrayList());
|
trigger01.setConditions(CollUtil.newArrayList());
|
||||||
// 属性
|
// 属性
|
||||||
|
@ -120,7 +126,7 @@ public class IotRuleSceneServiceImpl implements IotRuleSceneService {
|
||||||
condition02.setIdentifier(IotDeviceMessageIdentifierEnum.STATE_ONLINE.getIdentifier());
|
condition02.setIdentifier(IotDeviceMessageIdentifierEnum.STATE_ONLINE.getIdentifier());
|
||||||
condition02.setParameters(CollUtil.newArrayList());
|
condition02.setParameters(CollUtil.newArrayList());
|
||||||
trigger01.getConditions().add(condition02);
|
trigger01.getConditions().add(condition02);
|
||||||
// TODO 芋艿:事件
|
// 事件
|
||||||
IotRuleSceneDO.TriggerCondition condition03 = new IotRuleSceneDO.TriggerCondition();
|
IotRuleSceneDO.TriggerCondition condition03 = new IotRuleSceneDO.TriggerCondition();
|
||||||
condition03.setType(IotDeviceMessageTypeEnum.EVENT.getType());
|
condition03.setType(IotDeviceMessageTypeEnum.EVENT.getType());
|
||||||
condition03.setIdentifier("xxx");
|
condition03.setIdentifier("xxx");
|
||||||
|
@ -131,13 +137,28 @@ public class IotRuleSceneServiceImpl implements IotRuleSceneService {
|
||||||
parameter030.setValue("1");
|
parameter030.setValue("1");
|
||||||
trigger01.getConditions().add(condition03);
|
trigger01.getConditions().add(condition03);
|
||||||
ruleScene01.getTriggers().add(trigger01);
|
ruleScene01.getTriggers().add(trigger01);
|
||||||
|
// 动作
|
||||||
|
ruleScene01.setActions(CollUtil.newArrayList());
|
||||||
|
IotRuleSceneDO.ActionConfig action01 = new IotRuleSceneDO.ActionConfig();
|
||||||
|
action01.setType(IotRuleSceneActionTypeEnum.DEVICE_CONTROL.getType());
|
||||||
|
IotRuleSceneDO.ActionDeviceControl actionDeviceControl01 = new IotRuleSceneDO.ActionDeviceControl();
|
||||||
|
actionDeviceControl01.setProductKey("4aymZgOTOOCrDKRT");
|
||||||
|
actionDeviceControl01.setDeviceNames(ListUtil.of("small"));
|
||||||
|
actionDeviceControl01.setType(IotDeviceMessageTypeEnum.PROPERTY.getType());
|
||||||
|
actionDeviceControl01.setIdentifier(IotDeviceMessageIdentifierEnum.PROPERTY_SET.getIdentifier());
|
||||||
|
actionDeviceControl01.setData(MapUtil.<String, Object>builder()
|
||||||
|
.put("power", 1)
|
||||||
|
.put("color", "red")
|
||||||
|
.build());
|
||||||
|
action01.setDeviceControl(actionDeviceControl01);
|
||||||
|
ruleScene01.getActions().add(action01);
|
||||||
return ListUtil.toList(ruleScene01);
|
return ListUtil.toList(ruleScene01);
|
||||||
}
|
}
|
||||||
|
|
||||||
List<IotRuleSceneDO> list = ruleSceneMapper.selectList();
|
List<IotRuleSceneDO> list = ruleSceneMapper.selectList();
|
||||||
// TODO @芋艿:需要考虑开启状态
|
// TODO @芋艿:需要考虑开启状态
|
||||||
return filterList(list, ruleScene -> {
|
return filterList(list, ruleScene -> {
|
||||||
for (IotRuleSceneDO.Trigger trigger : ruleScene.getTriggers()) {
|
for (IotRuleSceneDO.TriggerConfig trigger : ruleScene.getTriggers()) {
|
||||||
if (ObjUtil.notEqual(trigger.getProductKey(), productKey)) {
|
if (ObjUtil.notEqual(trigger.getProductKey(), productKey)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -151,12 +172,17 @@ public class IotRuleSceneServiceImpl implements IotRuleSceneService {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void executeRuleScene(IotDeviceMessage message) {
|
public void executeRuleSceneByDevice(IotDeviceMessage message) {
|
||||||
|
TenantUtils.execute(message.getTenantId(), () -> {
|
||||||
// 1. 获得设备匹配的规则场景
|
// 1. 获得设备匹配的规则场景
|
||||||
List<IotRuleSceneDO> ruleScenes = getMatchedRuleSceneList(message);
|
List<IotRuleSceneDO> ruleScenes = getMatchedRuleSceneList(message);
|
||||||
if (CollUtil.isEmpty(ruleScenes)) {
|
if (CollUtil.isEmpty(ruleScenes)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 2. 执行规则场景
|
||||||
|
executeRuleSceneAction(message, ruleScenes);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -176,7 +202,7 @@ public class IotRuleSceneServiceImpl implements IotRuleSceneService {
|
||||||
|
|
||||||
// 2. 匹配 trigger 触发器的条件
|
// 2. 匹配 trigger 触发器的条件
|
||||||
return filterList(ruleScenes, ruleScene -> {
|
return filterList(ruleScenes, ruleScene -> {
|
||||||
for (IotRuleSceneDO.Trigger trigger : ruleScene.getTriggers()) {
|
for (IotRuleSceneDO.TriggerConfig trigger : ruleScene.getTriggers()) {
|
||||||
// 2.1 非设备触发,不匹配
|
// 2.1 非设备触发,不匹配
|
||||||
if (ObjUtil.notEqual(trigger.getType(), IotRuleSceneTriggerTypeEnum.DEVICE.getType())) {
|
if (ObjUtil.notEqual(trigger.getType(), IotRuleSceneTriggerTypeEnum.DEVICE.getType())) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -219,7 +245,7 @@ public class IotRuleSceneServiceImpl implements IotRuleSceneService {
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings({"unchecked", "DataFlowIssue"})
|
@SuppressWarnings({"unchecked", "DataFlowIssue"})
|
||||||
private boolean isTriggerConditionParameterMatched(IotDeviceMessage message, IotRuleSceneDO.TriggerConditionParameter parameter,
|
private boolean isTriggerConditionParameterMatched(IotDeviceMessage message, IotRuleSceneDO.TriggerConditionParameter parameter,
|
||||||
IotRuleSceneDO ruleScene, IotRuleSceneDO.Trigger trigger) {
|
IotRuleSceneDO ruleScene, IotRuleSceneDO.TriggerConfig trigger) {
|
||||||
// 1.1 校验操作符是否合法
|
// 1.1 校验操作符是否合法
|
||||||
IotRuleSceneTriggerConditionParameterOperatorEnum operator =
|
IotRuleSceneTriggerConditionParameterOperatorEnum operator =
|
||||||
IotRuleSceneTriggerConditionParameterOperatorEnum.operatorOf(parameter.getOperator());
|
IotRuleSceneTriggerConditionParameterOperatorEnum.operatorOf(parameter.getOperator());
|
||||||
|
@ -266,4 +292,36 @@ public class IotRuleSceneServiceImpl implements IotRuleSceneService {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 执行规则场景的动作
|
||||||
|
*
|
||||||
|
* @param message 设备消息
|
||||||
|
* @param ruleScenes 规则场景列表
|
||||||
|
*/
|
||||||
|
private void executeRuleSceneAction(IotDeviceMessage message, List<IotRuleSceneDO> ruleScenes) {
|
||||||
|
// 1. 遍历规则场景
|
||||||
|
ruleScenes.forEach(ruleScene -> {
|
||||||
|
// 2. 遍历规则场景的动作
|
||||||
|
ruleScene.getActions().forEach(actionConfig -> {
|
||||||
|
// 3.1 获取对应的动作 Action 数组
|
||||||
|
List<IotRuleSceneAction> actions = filterList(ruleSceneActions,
|
||||||
|
action -> action.getType().getType().equals(actionConfig.getType()));
|
||||||
|
if (CollUtil.isEmpty(actions)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 3.2 执行动作
|
||||||
|
actions.forEach(action -> {
|
||||||
|
try {
|
||||||
|
action.execute(message, actionConfig);
|
||||||
|
log.info("[executeRuleSceneAction][消息({}) 规则场景编号({}) 的执行动作({}) 成功]",
|
||||||
|
message, ruleScene.getId(), actionConfig);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("[executeRuleSceneAction][消息({}) 规则场景编号({}) 的执行动作({}) 执行异常]",
|
||||||
|
message, ruleScene.getId(), actionConfig, e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,29 @@
|
||||||
|
package cn.iocoder.yudao.module.iot.service.rule.action;
|
||||||
|
|
||||||
|
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.mq.message.IotDeviceMessage;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* IOT 规则场景的场景执行器接口
|
||||||
|
*
|
||||||
|
* @author 芋道源码
|
||||||
|
*/
|
||||||
|
public interface IotRuleSceneAction {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 执行场景
|
||||||
|
*
|
||||||
|
* @param message 消息
|
||||||
|
* @param config 配置
|
||||||
|
*/
|
||||||
|
void execute(IotDeviceMessage message, IotRuleSceneDO.ActionConfig config);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获得类型
|
||||||
|
*
|
||||||
|
* @return 类型
|
||||||
|
*/
|
||||||
|
IotRuleSceneActionTypeEnum getType();
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,56 @@
|
||||||
|
package cn.iocoder.yudao.module.iot.service.rule.action;
|
||||||
|
|
||||||
|
import cn.hutool.core.lang.Assert;
|
||||||
|
import cn.iocoder.yudao.module.iot.controller.admin.device.vo.control.IotDeviceDownstreamReqVO;
|
||||||
|
import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO;
|
||||||
|
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.mq.message.IotDeviceMessage;
|
||||||
|
import cn.iocoder.yudao.module.iot.service.device.IotDeviceService;
|
||||||
|
import cn.iocoder.yudao.module.iot.service.device.control.IotDeviceDownstreamService;
|
||||||
|
import jakarta.annotation.Resource;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* IoT 设备控制的 {@link IotRuleSceneAction} 实现类
|
||||||
|
*
|
||||||
|
* @author 芋道源码
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
@Slf4j
|
||||||
|
public class IotRuleSceneDeviceControlAction implements IotRuleSceneAction {
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private IotDeviceDownstreamService deviceDownstreamService;
|
||||||
|
@Resource
|
||||||
|
private IotDeviceService deviceService;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void execute(IotDeviceMessage message, IotRuleSceneDO.ActionConfig config) {
|
||||||
|
IotRuleSceneDO.ActionDeviceControl control = config.getDeviceControl();
|
||||||
|
Assert.notNull(control, "设备控制配置不能为空");
|
||||||
|
// 遍历每个设备,下发消息
|
||||||
|
control.getDeviceNames().forEach(deviceName -> {
|
||||||
|
IotDeviceDO device = deviceService.getDeviceByProductKeyAndDeviceNameFromCache(control.getProductKey(), deviceName);
|
||||||
|
if (device == null) {
|
||||||
|
log.error("[execute][message({}) config({}) 对应的设备不存在]", message, config);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
IotDeviceMessage downstreamMessage = deviceDownstreamService.downstreamDevice(new IotDeviceDownstreamReqVO()
|
||||||
|
.setId(device.getId()).setType(control.getType()).setIdentifier(control.getIdentifier())
|
||||||
|
.setData(control.getData()));
|
||||||
|
log.info("[execute][message({}) config({}) 下发消息({})成功]", message, config, downstreamMessage);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("[execute][message({}) config({}) 下发消息失败]", message, config, e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IotRuleSceneActionTypeEnum getType() {
|
||||||
|
return IotRuleSceneActionTypeEnum.DEVICE_CONTROL;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -6,7 +6,7 @@ import cn.hutool.core.util.StrUtil;
|
||||||
import cn.hutool.extra.spring.SpringUtil;
|
import cn.hutool.extra.spring.SpringUtil;
|
||||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||||
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
||||||
import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils;
|
import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore;
|
||||||
import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.ThingModelEvent;
|
import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.ThingModelEvent;
|
||||||
import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.ThingModelParam;
|
import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.ThingModelParam;
|
||||||
import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.ThingModelService;
|
import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.ThingModelService;
|
||||||
|
@ -135,13 +135,9 @@ public class IotThingModelServiceImpl implements IotThingModelService {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<IotThingModelDO> getThingModelListByProductKeyFromCache(String productKey) {
|
|
||||||
// 保证在 @CacheEvict 之前,忽略租户
|
|
||||||
return TenantUtils.executeIgnore(() -> getSelf().getThingModelListByProductKeyFromCache0(productKey));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Cacheable(value = RedisKeyConstants.THING_MODEL_LIST, key = "#productKey")
|
@Cacheable(value = RedisKeyConstants.THING_MODEL_LIST, key = "#productKey")
|
||||||
public List<IotThingModelDO> getThingModelListByProductKeyFromCache0(String productKey) {
|
@TenantIgnore // 忽略租户信息,跨租户 productKey 是唯一的
|
||||||
|
public List<IotThingModelDO> getThingModelListByProductKeyFromCache(String productKey) {
|
||||||
return thingModelMapper.selectListByProductKey(productKey);
|
return thingModelMapper.selectListByProductKey(productKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -354,8 +350,8 @@ public class IotThingModelServiceImpl implements IotThingModelService {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void deleteThingModelListCache(String productKey) {
|
private void deleteThingModelListCache(String productKey) {
|
||||||
// 保证在 @CacheEvict 之前,忽略租户
|
// 保证 Spring AOP 触发
|
||||||
TenantUtils.executeIgnore(() -> getSelf().deleteThingModelListCache0(productKey));
|
getSelf().deleteThingModelListCache0(productKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
@CacheEvict(value = RedisKeyConstants.THING_MODEL_LIST, key = "#productKey")
|
@CacheEvict(value = RedisKeyConstants.THING_MODEL_LIST, key = "#productKey")
|
||||||
|
|
|
@ -311,6 +311,8 @@ yudao:
|
||||||
- mail_account
|
- mail_account
|
||||||
- mail_template
|
- mail_template
|
||||||
- sms_template
|
- sms_template
|
||||||
|
- iot:device
|
||||||
|
- iot:thing_model_list
|
||||||
sms-code: # 短信验证码相关的配置项
|
sms-code: # 短信验证码相关的配置项
|
||||||
expire-times: 10m
|
expire-times: 10m
|
||||||
send-frequency: 1m
|
send-frequency: 1m
|
||||||
|
|
Loading…
Reference in New Issue