review:【iot 物联网】场景联动的逻辑

This commit is contained in:
YunaiV
2025-08-04 22:13:07 +08:00
parent e7a09de3da
commit 00bd4293f0
4 changed files with 7 additions and 285 deletions

View File

@@ -23,6 +23,7 @@ import lombok.NoArgsConstructor;
import java.util.List;
// TODO @puhui999名字改成 IotSceneRuleDO
/**
* IoT 场景联动规则 DO
*

View File

@@ -1,240 +0,0 @@
package cn.iocoder.yudao.module.iot.dal.dataobject.rule;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO;
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.device.IotDeviceDO;
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.enums.rule.IotRuleSceneActionTypeEnum;
import cn.iocoder.yudao.module.iot.enums.rule.IotRuleSceneConditionOperatorEnum;
import cn.iocoder.yudao.module.iot.enums.rule.IotRuleSceneConditionTypeEnum;
import cn.iocoder.yudao.module.iot.enums.rule.IotRuleSceneTriggerTypeEnum;
import com.baomidou.mybatisplus.annotation.KeySequence;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
// TODO @puhui999还是在 IotRuleSceneDO 里搞,这里主要可以看到变化字段哈。
/**
* IoT 场景联动 DO
*
* 基于 {@link Trigger} 触发 {@link Action}
*
* @author 芋道源码
*/
@TableName(value = "iot_scene_rule", autoResultMap = true)
@KeySequence("iot_scene_rule_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class IotSceneRuleDO extends TenantBaseDO {
/**
* 场景联动编号
*/
@TableId
private Long id;
/**
* 场景联动名称
*/
private String name;
/**
* 场景联动描述
*/
private String description;
/**
* 场景联动状态
*
* 枚举 {@link CommonStatusEnum}
*/
private Integer status;
/**
* 场景定义配置
*/
@TableField(typeHandler = JacksonTypeHandler.class)
private List<Trigger> triggers;
/**
* 场景动作配置
*/
@TableField(typeHandler = JacksonTypeHandler.class)
private List<Action> actions;
/**
* 场景定义配置
*/
@Data
public static class Trigger {
// ========== 事件部分 ==========
/**
* 场景事件类型
*
* 枚举 {@link IotRuleSceneTriggerTypeEnum}
* 1. {@link IotRuleSceneTriggerTypeEnum#DEVICE_STATE_UPDATE} 时operator 非空,并且 value 为在线状态
* 2. {@link IotRuleSceneTriggerTypeEnum#DEVICE_PROPERTY_POST}
* {@link IotRuleSceneTriggerTypeEnum#DEVICE_EVENT_POST} 时identifier、operator 非空,并且 value 为属性值
* 3. {@link IotRuleSceneTriggerTypeEnum#DEVICE_EVENT_POST}
* {@link IotRuleSceneTriggerTypeEnum#DEVICE_SERVICE_INVOKE} 时identifier 非空,但是 operator、value 为空
* 4. {@link IotRuleSceneTriggerTypeEnum#TIMER} 时conditions 非空,并且设备无关(无需 productId、deviceId 字段)
*/
private Integer type;
/**
* 产品编号
*
* 关联 {@link IotProductDO#getId()}
*/
private Long productId;
/**
* 设备编号
*
* 关联 {@link IotDeviceDO#getId()}
* 特殊:如果为 {@link IotDeviceDO#DEVICE_ID_ALL} 时,则是全部设备
*/
private Long deviceId;
/**
* 物模型标识符
*
* 对应:{@link IotThingModelDO#getIdentifier()}
*/
private String identifier;
/**
* 操作符
*
* 枚举 {@link IotRuleSceneConditionOperatorEnum}
*/
private String operator;
/**
* 参数(属性值、在线状态)
*
* 如果有多个值,则使用 "," 分隔,类似 "1,2,3"。
* 例如说,{@link IotRuleSceneConditionOperatorEnum#IN}、{@link IotRuleSceneConditionOperatorEnum#BETWEEN}
*/
private String value;
/**
* CRON 表达式
*/
private String cronExpression;
// ========== 条件部分 ==========
/**
* 触发条件分组(状态条件分组)的数组
*
* 第一层 List分组与分组之间是“或”的关系
* 第二层 List条件与条件之间是“且”的关系
*/
private List<List<TriggerCondition>> conditionGroups;
}
/**
* 触发条件(状态条件)
*/
@Data
public static class TriggerCondition {
/**
* 触发条件类型
*
* 枚举 {@link IotRuleSceneConditionTypeEnum}
* 1. {@link IotRuleSceneConditionTypeEnum#DEVICE_STATE} 时operator 非空,并且 value 为在线状态
* 2. {@link IotRuleSceneConditionTypeEnum#DEVICE_PROPERTY} 时identifier、operator 非空,并且 value 为属性值
* 3. {@link IotRuleSceneConditionTypeEnum#CURRENT_TIME} 时operator 非空(使用 DATE_TIME_ 和 TIME_ 部分),并且 value 非空
*/
private Integer type;
/**
* 产品编号
*
* 关联 {@link IotProductDO#getId()}
*/
private Long productId;
/**
* 设备编号
*
* 关联 {@link IotDeviceDO#getId()}
*/
private Long deviceId;
/**
* 标识符(属性)
*
* 关联 {@link IotThingModelDO#getIdentifier()}
*/
private String identifier;
/**
* 操作符
*
* 枚举 {@link IotRuleSceneConditionOperatorEnum}
*/
private String operator;
/**
* 参数
*
* 如果有多个值,则使用 "," 分隔,类似 "1,2,3"。
* 例如说,{@link IotRuleSceneConditionOperatorEnum#IN}、{@link IotRuleSceneConditionOperatorEnum#BETWEEN}
*/
private String param;
}
/**
* 场景动作配置
*/
@Data
public static class Action {
/**
* 执行类型
*
* 枚举 {@link IotRuleSceneActionTypeEnum}
* 1. {@link IotRuleSceneActionTypeEnum#DEVICE_PROPERTY_SET} 时params 非空
* {@link IotRuleSceneActionTypeEnum#DEVICE_SERVICE_INVOKE} 时params 非空
* 2. {@link IotRuleSceneActionTypeEnum#ALERT_TRIGGER} 时alertConfigId 为空,因为是 {@link IotAlertConfigDO} 里面关联它
* 3. {@link IotRuleSceneActionTypeEnum#ALERT_RECOVER} 时alertConfigId 非空
*/
private Integer type;
/**
* 产品编号
*
* 关联 {@link IotProductDO#getId()}
*/
private Long productId;
/**
* 设备编号
*
* 关联 {@link IotDeviceDO#getId()}
*/
private Long deviceId;
/**
* 请求参数
*
* 一般来说,对应 {@link IotDeviceMessage#getParams()} 请求参数
*/
private Object params;
/**
* 告警配置编号
*
* 关联 {@link IotAlertConfigDO#getId()}
*/
private Long alertConfigId;
}
}

View File

@@ -77,10 +77,8 @@ public class IotRuleSceneServiceImpl implements IotRuleSceneService {
@Override
public Long createRuleScene(IotRuleSceneSaveReqVO createReqVO) {
// 插入
IotRuleSceneDO ruleScene = BeanUtils.toBean(createReqVO, IotRuleSceneDO.class);
ruleSceneMapper.insert(ruleScene);
// 返回
return ruleScene.getId();
}
@@ -147,45 +145,7 @@ public class IotRuleSceneServiceImpl implements IotRuleSceneService {
@Override
@TenantIgnore // 忽略租户隔离:因为 IotRuleSceneMessageHandler 调用时,一般未传递租户,所以需要忽略
public List<IotRuleSceneDO> getRuleSceneListByProductKeyAndDeviceNameFromCache(String productKey, String deviceName) {
// TODO @芋艿:测试代码示例(使用新结构),可根据需要启用
if (false) {
// 创建测试规则场景
IotRuleSceneDO ruleScene = new IotRuleSceneDO();
ruleScene.setId(1L);
ruleScene.setName("测试场景");
ruleScene.setStatus(CommonStatusEnum.ENABLE.getStatus());
// 创建触发器
IotRuleSceneDO.Trigger trigger = new IotRuleSceneDO.Trigger();
trigger.setType(IotRuleSceneTriggerTypeEnum.DEVICE_PROPERTY_POST.getType());
trigger.setProductId(1L); // 假设产品ID为1
trigger.setDeviceId(1L); // 假设设备ID为1
trigger.setIdentifier("temperature"); // 温度属性
trigger.setOperator(IotRuleSceneConditionOperatorEnum.GREATER_THAN.getOperator());
trigger.setValue("25"); // 温度大于25度
// 创建条件分组
IotRuleSceneDO.TriggerCondition condition = new IotRuleSceneDO.TriggerCondition();
condition.setType(IotRuleSceneConditionTypeEnum.DEVICE_PROPERTY.getType());
condition.setIdentifier("temperature");
condition.setOperator(IotRuleSceneConditionOperatorEnum.GREATER_THAN.getOperator());
condition.setParam("25");
trigger.setConditionGroups(ListUtil.toList(Collections.singleton(ListUtil.toList(condition))));
ruleScene.setTriggers(ListUtil.toList(trigger));
// 创建动作
IotRuleSceneDO.Action action = new IotRuleSceneDO.Action();
action.setType(IotRuleSceneActionTypeEnum.DEVICE_PROPERTY_SET.getType());
action.setProductId(1L);
action.setDeviceId(1L);
action.setParams(MapUtil.of("fan", "on")); // 打开风扇
ruleScene.setActions(ListUtil.toList(action));
return ListUtil.toList(ruleScene);
}
// TODO @puhui999一些注释看看要不要优化下
// 注意:旧的测试代码已删除,因为使用了废弃的数据结构
// 如需测试,请使用上面的新结构测试代码示例
List<IotRuleSceneDO> list = ruleSceneMapper.selectList();
@@ -471,13 +431,13 @@ public class IotRuleSceneServiceImpl implements IotRuleSceneService {
return false;
}
// 2. 构建 Spring 表达式的变量
// 2.1 构建 Spring 表达式的变量
Map<String, Object> springExpressionVariables = MapUtil.<String, Object>builder()
.put(IotRuleSceneConditionOperatorEnum.SPRING_EXPRESSION_SOURCE, sourceValue)
.build();
// 3. 根据操作符类型处理参数值
// 2.2 根据操作符类型处理参数值
if (StrUtil.isNotBlank(paramValue)) {
// TODO @puhui999这里是不是在 IotRuleSceneConditionOperatorEnum 加个属性;
if (operatorEnum == IotRuleSceneConditionOperatorEnum.IN
|| operatorEnum == IotRuleSceneConditionOperatorEnum.NOT_IN
|| operatorEnum == IotRuleSceneConditionOperatorEnum.BETWEEN
@@ -493,7 +453,7 @@ public class IotRuleSceneServiceImpl implements IotRuleSceneService {
}
}
// 4. 计算 Spring 表达式
// 3. 计算 Spring 表达式
return (Boolean) SpringExpressionUtils.parseExpression(operatorEnum.getSpringExpression(), springExpressionVariables);
} catch (Exception e) {
log.error("[evaluateCondition][条件评估异常] sourceValue: {}, operator: {}, paramValue: {}",

View File

@@ -23,6 +23,7 @@ public class IotDeviceControlRuleSceneAction implements IotSceneRuleAction {
@Resource
private IotDeviceMessageService deviceMessageService;
// TODO @puhui999这里
@Override
public void execute(IotDeviceMessage message,
IotRuleSceneDO rule, IotRuleSceneDO.Action actionConfig) {