Browse Source

feat: 【IoT 物联网】重构设备属性设置执行器,支持单设备和批量设备操作。重构设备属性设置执行器,支持单设备和批量设备操作。

puhui999 9 months ago
parent
commit
bc0292eb61

+ 107 - 20
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/action/IotDeviceControlSceneRuleAction.java

@@ -1,6 +1,10 @@
 package cn.iocoder.yudao.module.iot.service.rule.scene.action;
 
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.util.StrUtil;
+import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum;
 import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
+import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO;
 import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotSceneRuleDO;
 import cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleActionTypeEnum;
 import cn.iocoder.yudao.module.iot.service.device.IotDeviceService;
@@ -9,8 +13,11 @@ import jakarta.annotation.Resource;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.stereotype.Component;
 
+import java.util.List;
+import java.util.Map;
+
 /**
- * IoT 设备控制的 {@link IotSceneRuleAction} 实现类
+ * IoT 设备属性设置的 {@link IotSceneRuleAction} 实现类
  *
  * @author 芋道源码
  */
@@ -23,28 +30,108 @@ public class IotDeviceControlSceneRuleAction implements IotSceneRuleAction {
     @Resource
     private IotDeviceMessageService deviceMessageService;
 
-    // TODO @puhui999:这里
     @Override
     public void execute(IotDeviceMessage message,
                         IotSceneRuleDO rule, IotSceneRuleDO.Action actionConfig) {
-        //IotSceneRuleDO.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({}) 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({}) actionConfig({}) 下发消息({})成功]", message, actionConfig, downstreamMessage);
-        //    } catch (Exception e) {
-        //        log.error("[execute][message({}) actionConfig({}) 下发消息失败]", message, actionConfig, e);
-        //    }
-        //});
+        // 1. 参数校验
+        if (actionConfig.getDeviceId() == null) {
+            log.error("[execute][规则场景({}) 动作配置({}) 设备编号不能为空]", rule.getId(), actionConfig);
+            return;
+        }
+        if (StrUtil.isEmpty(actionConfig.getIdentifier())) {
+            log.error("[execute][规则场景({}) 动作配置({}) 属性标识符不能为空]", rule.getId(), actionConfig);
+            return;
+        }
+
+        // 2. 判断是否为全部设备
+        if (IotDeviceDO.DEVICE_ID_ALL.equals(actionConfig.getDeviceId())) {
+            executeForAllDevices(message, rule, actionConfig);
+        } else {
+            executeForSingleDevice(message, rule, actionConfig);
+        }
+    }
+
+    /**
+     * 为单个设备执行属性设置
+     */
+    private void executeForSingleDevice(IotDeviceMessage message,
+                                        IotSceneRuleDO rule, IotSceneRuleDO.Action actionConfig) {
+        // 1. 获取设备信息
+        IotDeviceDO device = deviceService.getDeviceFromCache(actionConfig.getDeviceId());
+        if (device == null) {
+            log.error("[executeForSingleDevice][规则场景({}) 动作配置({}) 对应的设备({}) 不存在]",
+                    rule.getId(), actionConfig, actionConfig.getDeviceId());
+            return;
+        }
+
+        // 2. 执行属性设置
+        executePropertySetForDevice(rule, actionConfig, device);
+    }
+
+    /**
+     * 为产品下的所有设备执行属性设置
+     */
+    private void executeForAllDevices(IotDeviceMessage message,
+                                      IotSceneRuleDO rule, IotSceneRuleDO.Action actionConfig) {
+        // 1. 参数校验
+        if (actionConfig.getProductId() == null) {
+            log.error("[executeForAllDevices][规则场景({}) 动作配置({}) 产品编号不能为空]", rule.getId(), actionConfig);
+            return;
+        }
+
+        // 2. 获取产品下的所有设备
+        List<IotDeviceDO> devices = deviceService.getDeviceListByProductId(actionConfig.getProductId());
+        if (CollUtil.isEmpty(devices)) {
+            log.warn("[executeForAllDevices][规则场景({}) 动作配置({}) 产品({}) 下没有设备]",
+                    rule.getId(), actionConfig, actionConfig.getProductId());
+            return;
+        }
+
+        // 3. 遍历所有设备执行属性设置
+        for (IotDeviceDO device : devices) {
+            executePropertySetForDevice(rule, actionConfig, device);
+        }
+    }
+
+    /**
+     * 为指定设备执行属性设置
+     */
+    private void executePropertySetForDevice(IotSceneRuleDO rule, IotSceneRuleDO.Action actionConfig, IotDeviceDO device) {
+        // 1. 构建属性设置消息
+        IotDeviceMessage downstreamMessage = buildPropertySetMessage(actionConfig, device);
+        if (downstreamMessage == null) {
+            log.error("[executePropertySetForDevice][规则场景({}) 动作配置({}) 设备({}) 构建属性设置消息失败]",
+                    rule.getId(), actionConfig, device.getId());
+            return;
+        }
+
+        // 2. 发送设备消息
+        try {
+            IotDeviceMessage result = deviceMessageService.sendDeviceMessage(downstreamMessage, device);
+            log.info("[executePropertySetForDevice][规则场景({}) 动作配置({}) 设备({}) 属性设置消息({}) 发送成功]",
+                    rule.getId(), actionConfig, device.getId(), result.getId());
+        } catch (Exception e) {
+            log.error("[executePropertySetForDevice][规则场景({}) 动作配置({}) 设备({}) 属性设置消息发送失败]",
+                    rule.getId(), actionConfig, device.getId(), e);
+        }
+    }
+
+    /**
+     * 构建属性设置消息
+     *
+     * @param actionConfig 动作配置
+     * @param device       设备信息
+     * @return 设备消息
+     */
+    private IotDeviceMessage buildPropertySetMessage(IotSceneRuleDO.Action actionConfig, IotDeviceDO device) {
+        try {
+            // 属性设置参数格式: {"properties": {"identifier": value}}
+            Object params = Map.of("properties", Map.of(actionConfig.getIdentifier(), actionConfig.getParams()));
+            return IotDeviceMessage.requestOf(IotDeviceMessageMethodEnum.PROPERTY_SET.getMethod(), params);
+        } catch (Exception e) {
+            log.error("[buildPropertySetMessage][构建属性设置消息异常]", e);
+            return null;
+        }
     }
 
     @Override

+ 145 - 0
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/action/IotDeviceServiceInvokeSceneRuleAction.java

@@ -0,0 +1,145 @@
+package cn.iocoder.yudao.module.iot.service.rule.scene.action;
+
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.util.StrUtil;
+import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum;
+import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
+import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO;
+import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotSceneRuleDO;
+import cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleActionTypeEnum;
+import cn.iocoder.yudao.module.iot.service.device.IotDeviceService;
+import cn.iocoder.yudao.module.iot.service.device.message.IotDeviceMessageService;
+import jakarta.annotation.Resource;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Component;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * IoT 设备服务调用的 {@link IotSceneRuleAction} 实现类
+ *
+ * @author HUIHUI
+ */
+@Component
+@Slf4j
+public class IotDeviceServiceInvokeSceneRuleAction implements IotSceneRuleAction {
+
+    @Resource
+    private IotDeviceService deviceService;
+    @Resource
+    private IotDeviceMessageService deviceMessageService;
+
+    @Override
+    public void execute(IotDeviceMessage message,
+                        IotSceneRuleDO rule, IotSceneRuleDO.Action actionConfig) {
+        // 1. 参数校验
+        if (actionConfig.getDeviceId() == null) {
+            log.error("[execute][规则场景({}) 动作配置({}) 设备编号不能为空]", rule.getId(), actionConfig);
+            return;
+        }
+        if (StrUtil.isEmpty(actionConfig.getIdentifier())) {
+            log.error("[execute][规则场景({}) 动作配置({}) 服务标识符不能为空]", rule.getId(), actionConfig);
+            return;
+        }
+
+        // 2. 判断是否为全部设备
+        if (IotDeviceDO.DEVICE_ID_ALL.equals(actionConfig.getDeviceId())) {
+            executeForAllDevices(message, rule, actionConfig);
+        } else {
+            executeForSingleDevice(message, rule, actionConfig);
+        }
+    }
+
+    /**
+     * 为单个设备执行服务调用
+     */
+    private void executeForSingleDevice(IotDeviceMessage message,
+                                        IotSceneRuleDO rule, IotSceneRuleDO.Action actionConfig) {
+        // 1. 获取设备信息
+        IotDeviceDO device = deviceService.getDeviceFromCache(actionConfig.getDeviceId());
+        if (device == null) {
+            log.error("[executeForSingleDevice][规则场景({}) 动作配置({}) 对应的设备({}) 不存在]",
+                    rule.getId(), actionConfig, actionConfig.getDeviceId());
+            return;
+        }
+
+        // 2. 执行服务调用
+        executeServiceInvokeForDevice(rule, actionConfig, device);
+    }
+
+    /**
+     * 为产品下的所有设备执行服务调用
+     */
+    private void executeForAllDevices(IotDeviceMessage message,
+                                      IotSceneRuleDO rule, IotSceneRuleDO.Action actionConfig) {
+        // 1. 参数校验
+        if (actionConfig.getProductId() == null) {
+            log.error("[executeForAllDevices][规则场景({}) 动作配置({}) 产品编号不能为空]", rule.getId(), actionConfig);
+            return;
+        }
+
+        // 2. 获取产品下的所有设备
+        List<IotDeviceDO> devices = deviceService.getDeviceListByProductId(actionConfig.getProductId());
+        if (CollUtil.isEmpty(devices)) {
+            log.warn("[executeForAllDevices][规则场景({}) 动作配置({}) 产品({}) 下没有设备]",
+                    rule.getId(), actionConfig, actionConfig.getProductId());
+            return;
+        }
+
+        // 3. 遍历所有设备执行服务调用
+        for (IotDeviceDO device : devices) {
+            executeServiceInvokeForDevice(rule, actionConfig, device);
+        }
+    }
+
+    /**
+     * 为指定设备执行服务调用
+     */
+    private void executeServiceInvokeForDevice(IotSceneRuleDO rule, IotSceneRuleDO.Action actionConfig, IotDeviceDO device) {
+        // 1. 构建服务调用消息
+        IotDeviceMessage downstreamMessage = buildServiceInvokeMessage(actionConfig, device);
+        if (downstreamMessage == null) {
+            log.error("[executeServiceInvokeForDevice][规则场景({}) 动作配置({}) 设备({}) 构建服务调用消息失败]",
+                    rule.getId(), actionConfig, device.getId());
+            return;
+        }
+
+        // 2. 发送设备消息
+        try {
+            IotDeviceMessage result = deviceMessageService.sendDeviceMessage(downstreamMessage, device);
+            log.info("[executeServiceInvokeForDevice][规则场景({}) 动作配置({}) 设备({}) 服务调用消息({}) 发送成功]",
+                    rule.getId(), actionConfig, device.getId(), result.getId());
+        } catch (Exception e) {
+            log.error("[executeServiceInvokeForDevice][规则场景({}) 动作配置({}) 设备({}) 服务调用消息发送失败]",
+                    rule.getId(), actionConfig, device.getId(), e);
+        }
+    }
+
+    /**
+     * 构建服务调用消息
+     *
+     * @param actionConfig 动作配置
+     * @param device       设备信息
+     * @return 设备消息
+     */
+    private IotDeviceMessage buildServiceInvokeMessage(IotSceneRuleDO.Action actionConfig, IotDeviceDO device) {
+        try {
+            // 服务调用参数格式: {"identifier": "serviceId", "params": {...}}
+            Object params = Map.of(
+                    "identifier", actionConfig.getIdentifier(),
+                    "params", actionConfig.getParams() != null ? actionConfig.getParams() : Map.of()
+            );
+            return IotDeviceMessage.requestOf(IotDeviceMessageMethodEnum.SERVICE_INVOKE.getMethod(), params);
+        } catch (Exception e) {
+            log.error("[buildServiceInvokeMessage][构建服务调用消息异常]", e);
+            return null;
+        }
+    }
+
+    @Override
+    public IotSceneRuleActionTypeEnum getType() {
+        return IotSceneRuleActionTypeEnum.DEVICE_SERVICE_INVOKE;
+    }
+
+}