Commit bc0292eb6122cf3b47141399fb85d4203e0f4c94

Authored by puhui999
1 parent fe0c1bbf

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

yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/action/IotDeviceControlSceneRuleAction.java
1 1 package cn.iocoder.yudao.module.iot.service.rule.scene.action;
2 2  
  3 +import cn.hutool.core.collection.CollUtil;
  4 +import cn.hutool.core.util.StrUtil;
  5 +import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum;
3 6 import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
  7 +import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO;
4 8 import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotSceneRuleDO;
5 9 import cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleActionTypeEnum;
6 10 import cn.iocoder.yudao.module.iot.service.device.IotDeviceService;
... ... @@ -9,8 +13,11 @@ import jakarta.annotation.Resource;
9 13 import lombok.extern.slf4j.Slf4j;
10 14 import org.springframework.stereotype.Component;
11 15  
  16 +import java.util.List;
  17 +import java.util.Map;
  18 +
12 19 /**
13   - * IoT 设备控制的 {@link IotSceneRuleAction} 实现类
  20 + * IoT 设备属性设置的 {@link IotSceneRuleAction} 实现类
14 21 *
15 22 * @author 芋道源码
16 23 */
... ... @@ -23,28 +30,108 @@ public class IotDeviceControlSceneRuleAction implements IotSceneRuleAction {
23 30 @Resource
24 31 private IotDeviceMessageService deviceMessageService;
25 32  
26   - // TODO @puhui999:这里
27 33 @Override
28 34 public void execute(IotDeviceMessage message,
29 35 IotSceneRuleDO rule, IotSceneRuleDO.Action actionConfig) {
30   - //IotSceneRuleDO.ActionDeviceControl control = actionConfig.getDeviceControl();
31   - //Assert.notNull(control, "设备控制配置不能为空");
32   - //// 遍历每个设备,下发消息
33   - //control.getDeviceNames().forEach(deviceName -> {
34   - // IotDeviceDO device = deviceService.getDeviceFromCache(control.getProductKey(), deviceName);
35   - // if (device == null) {
36   - // log.error("[execute][message({}) actionConfig({}) 对应的设备不存在]", message, actionConfig);
37   - // return;
38   - // }
39   - // try {
40   - // // TODO @芋艿:@puhui999:这块可能要改,从 type => method
41   - // IotDeviceMessage downstreamMessage = deviceMessageService.sendDeviceMessage(IotDeviceMessage.requestOf(
42   - // control.getType() + control.getIdentifier(), control.getData()).setDeviceId(device.getId()));
43   - // log.info("[execute][message({}) actionConfig({}) 下发消息({})成功]", message, actionConfig, downstreamMessage);
44   - // } catch (Exception e) {
45   - // log.error("[execute][message({}) actionConfig({}) 下发消息失败]", message, actionConfig, e);
46   - // }
47   - //});
  36 + // 1. 参数校验
  37 + if (actionConfig.getDeviceId() == null) {
  38 + log.error("[execute][规则场景({}) 动作配置({}) 设备编号不能为空]", rule.getId(), actionConfig);
  39 + return;
  40 + }
  41 + if (StrUtil.isEmpty(actionConfig.getIdentifier())) {
  42 + log.error("[execute][规则场景({}) 动作配置({}) 属性标识符不能为空]", rule.getId(), actionConfig);
  43 + return;
  44 + }
  45 +
  46 + // 2. 判断是否为全部设备
  47 + if (IotDeviceDO.DEVICE_ID_ALL.equals(actionConfig.getDeviceId())) {
  48 + executeForAllDevices(message, rule, actionConfig);
  49 + } else {
  50 + executeForSingleDevice(message, rule, actionConfig);
  51 + }
  52 + }
  53 +
  54 + /**
  55 + * 为单个设备执行属性设置
  56 + */
  57 + private void executeForSingleDevice(IotDeviceMessage message,
  58 + IotSceneRuleDO rule, IotSceneRuleDO.Action actionConfig) {
  59 + // 1. 获取设备信息
  60 + IotDeviceDO device = deviceService.getDeviceFromCache(actionConfig.getDeviceId());
  61 + if (device == null) {
  62 + log.error("[executeForSingleDevice][规则场景({}) 动作配置({}) 对应的设备({}) 不存在]",
  63 + rule.getId(), actionConfig, actionConfig.getDeviceId());
  64 + return;
  65 + }
  66 +
  67 + // 2. 执行属性设置
  68 + executePropertySetForDevice(rule, actionConfig, device);
  69 + }
  70 +
  71 + /**
  72 + * 为产品下的所有设备执行属性设置
  73 + */
  74 + private void executeForAllDevices(IotDeviceMessage message,
  75 + IotSceneRuleDO rule, IotSceneRuleDO.Action actionConfig) {
  76 + // 1. 参数校验
  77 + if (actionConfig.getProductId() == null) {
  78 + log.error("[executeForAllDevices][规则场景({}) 动作配置({}) 产品编号不能为空]", rule.getId(), actionConfig);
  79 + return;
  80 + }
  81 +
  82 + // 2. 获取产品下的所有设备
  83 + List<IotDeviceDO> devices = deviceService.getDeviceListByProductId(actionConfig.getProductId());
  84 + if (CollUtil.isEmpty(devices)) {
  85 + log.warn("[executeForAllDevices][规则场景({}) 动作配置({}) 产品({}) 下没有设备]",
  86 + rule.getId(), actionConfig, actionConfig.getProductId());
  87 + return;
  88 + }
  89 +
  90 + // 3. 遍历所有设备执行属性设置
  91 + for (IotDeviceDO device : devices) {
  92 + executePropertySetForDevice(rule, actionConfig, device);
  93 + }
  94 + }
  95 +
  96 + /**
  97 + * 为指定设备执行属性设置
  98 + */
  99 + private void executePropertySetForDevice(IotSceneRuleDO rule, IotSceneRuleDO.Action actionConfig, IotDeviceDO device) {
  100 + // 1. 构建属性设置消息
  101 + IotDeviceMessage downstreamMessage = buildPropertySetMessage(actionConfig, device);
  102 + if (downstreamMessage == null) {
  103 + log.error("[executePropertySetForDevice][规则场景({}) 动作配置({}) 设备({}) 构建属性设置消息失败]",
  104 + rule.getId(), actionConfig, device.getId());
  105 + return;
  106 + }
  107 +
  108 + // 2. 发送设备消息
  109 + try {
  110 + IotDeviceMessage result = deviceMessageService.sendDeviceMessage(downstreamMessage, device);
  111 + log.info("[executePropertySetForDevice][规则场景({}) 动作配置({}) 设备({}) 属性设置消息({}) 发送成功]",
  112 + rule.getId(), actionConfig, device.getId(), result.getId());
  113 + } catch (Exception e) {
  114 + log.error("[executePropertySetForDevice][规则场景({}) 动作配置({}) 设备({}) 属性设置消息发送失败]",
  115 + rule.getId(), actionConfig, device.getId(), e);
  116 + }
  117 + }
  118 +
  119 + /**
  120 + * 构建属性设置消息
  121 + *
  122 + * @param actionConfig 动作配置
  123 + * @param device 设备信息
  124 + * @return 设备消息
  125 + */
  126 + private IotDeviceMessage buildPropertySetMessage(IotSceneRuleDO.Action actionConfig, IotDeviceDO device) {
  127 + try {
  128 + // 属性设置参数格式: {"properties": {"identifier": value}}
  129 + Object params = Map.of("properties", Map.of(actionConfig.getIdentifier(), actionConfig.getParams()));
  130 + return IotDeviceMessage.requestOf(IotDeviceMessageMethodEnum.PROPERTY_SET.getMethod(), params);
  131 + } catch (Exception e) {
  132 + log.error("[buildPropertySetMessage][构建属性设置消息异常]", e);
  133 + return null;
  134 + }
48 135 }
49 136  
50 137 @Override
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/action/IotDeviceServiceInvokeSceneRuleAction.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.service.rule.scene.action;
  2 +
  3 +import cn.hutool.core.collection.CollUtil;
  4 +import cn.hutool.core.util.StrUtil;
  5 +import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum;
  6 +import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
  7 +import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO;
  8 +import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotSceneRuleDO;
  9 +import cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleActionTypeEnum;
  10 +import cn.iocoder.yudao.module.iot.service.device.IotDeviceService;
  11 +import cn.iocoder.yudao.module.iot.service.device.message.IotDeviceMessageService;
  12 +import jakarta.annotation.Resource;
  13 +import lombok.extern.slf4j.Slf4j;
  14 +import org.springframework.stereotype.Component;
  15 +
  16 +import java.util.List;
  17 +import java.util.Map;
  18 +
  19 +/**
  20 + * IoT 设备服务调用的 {@link IotSceneRuleAction} 实现类
  21 + *
  22 + * @author HUIHUI
  23 + */
  24 +@Component
  25 +@Slf4j
  26 +public class IotDeviceServiceInvokeSceneRuleAction implements IotSceneRuleAction {
  27 +
  28 + @Resource
  29 + private IotDeviceService deviceService;
  30 + @Resource
  31 + private IotDeviceMessageService deviceMessageService;
  32 +
  33 + @Override
  34 + public void execute(IotDeviceMessage message,
  35 + IotSceneRuleDO rule, IotSceneRuleDO.Action actionConfig) {
  36 + // 1. 参数校验
  37 + if (actionConfig.getDeviceId() == null) {
  38 + log.error("[execute][规则场景({}) 动作配置({}) 设备编号不能为空]", rule.getId(), actionConfig);
  39 + return;
  40 + }
  41 + if (StrUtil.isEmpty(actionConfig.getIdentifier())) {
  42 + log.error("[execute][规则场景({}) 动作配置({}) 服务标识符不能为空]", rule.getId(), actionConfig);
  43 + return;
  44 + }
  45 +
  46 + // 2. 判断是否为全部设备
  47 + if (IotDeviceDO.DEVICE_ID_ALL.equals(actionConfig.getDeviceId())) {
  48 + executeForAllDevices(message, rule, actionConfig);
  49 + } else {
  50 + executeForSingleDevice(message, rule, actionConfig);
  51 + }
  52 + }
  53 +
  54 + /**
  55 + * 为单个设备执行服务调用
  56 + */
  57 + private void executeForSingleDevice(IotDeviceMessage message,
  58 + IotSceneRuleDO rule, IotSceneRuleDO.Action actionConfig) {
  59 + // 1. 获取设备信息
  60 + IotDeviceDO device = deviceService.getDeviceFromCache(actionConfig.getDeviceId());
  61 + if (device == null) {
  62 + log.error("[executeForSingleDevice][规则场景({}) 动作配置({}) 对应的设备({}) 不存在]",
  63 + rule.getId(), actionConfig, actionConfig.getDeviceId());
  64 + return;
  65 + }
  66 +
  67 + // 2. 执行服务调用
  68 + executeServiceInvokeForDevice(rule, actionConfig, device);
  69 + }
  70 +
  71 + /**
  72 + * 为产品下的所有设备执行服务调用
  73 + */
  74 + private void executeForAllDevices(IotDeviceMessage message,
  75 + IotSceneRuleDO rule, IotSceneRuleDO.Action actionConfig) {
  76 + // 1. 参数校验
  77 + if (actionConfig.getProductId() == null) {
  78 + log.error("[executeForAllDevices][规则场景({}) 动作配置({}) 产品编号不能为空]", rule.getId(), actionConfig);
  79 + return;
  80 + }
  81 +
  82 + // 2. 获取产品下的所有设备
  83 + List<IotDeviceDO> devices = deviceService.getDeviceListByProductId(actionConfig.getProductId());
  84 + if (CollUtil.isEmpty(devices)) {
  85 + log.warn("[executeForAllDevices][规则场景({}) 动作配置({}) 产品({}) 下没有设备]",
  86 + rule.getId(), actionConfig, actionConfig.getProductId());
  87 + return;
  88 + }
  89 +
  90 + // 3. 遍历所有设备执行服务调用
  91 + for (IotDeviceDO device : devices) {
  92 + executeServiceInvokeForDevice(rule, actionConfig, device);
  93 + }
  94 + }
  95 +
  96 + /**
  97 + * 为指定设备执行服务调用
  98 + */
  99 + private void executeServiceInvokeForDevice(IotSceneRuleDO rule, IotSceneRuleDO.Action actionConfig, IotDeviceDO device) {
  100 + // 1. 构建服务调用消息
  101 + IotDeviceMessage downstreamMessage = buildServiceInvokeMessage(actionConfig, device);
  102 + if (downstreamMessage == null) {
  103 + log.error("[executeServiceInvokeForDevice][规则场景({}) 动作配置({}) 设备({}) 构建服务调用消息失败]",
  104 + rule.getId(), actionConfig, device.getId());
  105 + return;
  106 + }
  107 +
  108 + // 2. 发送设备消息
  109 + try {
  110 + IotDeviceMessage result = deviceMessageService.sendDeviceMessage(downstreamMessage, device);
  111 + log.info("[executeServiceInvokeForDevice][规则场景({}) 动作配置({}) 设备({}) 服务调用消息({}) 发送成功]",
  112 + rule.getId(), actionConfig, device.getId(), result.getId());
  113 + } catch (Exception e) {
  114 + log.error("[executeServiceInvokeForDevice][规则场景({}) 动作配置({}) 设备({}) 服务调用消息发送失败]",
  115 + rule.getId(), actionConfig, device.getId(), e);
  116 + }
  117 + }
  118 +
  119 + /**
  120 + * 构建服务调用消息
  121 + *
  122 + * @param actionConfig 动作配置
  123 + * @param device 设备信息
  124 + * @return 设备消息
  125 + */
  126 + private IotDeviceMessage buildServiceInvokeMessage(IotSceneRuleDO.Action actionConfig, IotDeviceDO device) {
  127 + try {
  128 + // 服务调用参数格式: {"identifier": "serviceId", "params": {...}}
  129 + Object params = Map.of(
  130 + "identifier", actionConfig.getIdentifier(),
  131 + "params", actionConfig.getParams() != null ? actionConfig.getParams() : Map.of()
  132 + );
  133 + return IotDeviceMessage.requestOf(IotDeviceMessageMethodEnum.SERVICE_INVOKE.getMethod(), params);
  134 + } catch (Exception e) {
  135 + log.error("[buildServiceInvokeMessage][构建服务调用消息异常]", e);
  136 + return null;
  137 + }
  138 + }
  139 +
  140 + @Override
  141 + public IotSceneRuleActionTypeEnum getType() {
  142 + return IotSceneRuleActionTypeEnum.DEVICE_SERVICE_INVOKE;
  143 + }
  144 +
  145 +}
... ...