Commit 48cfcdadc13a642f28859912c7e808f0f9d80302

Authored by YunaiV
1 parent 4f84182d

【代码优化】IoT:实现规则 IotRuleSceneDeviceControlAction 执行器

Showing 17 changed files with 306 additions and 115 deletions
yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/rule/IotRuleSceneActionTypeEnum.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.enums.rule;
  2 +
  3 +import cn.iocoder.yudao.framework.common.core.ArrayValuable;
  4 +import lombok.Getter;
  5 +import lombok.RequiredArgsConstructor;
  6 +
  7 +import java.util.Arrays;
  8 +
  9 +/**
  10 + * Iot 规则场景的触发类型枚举
  11 + *
  12 + * 设备触发,定时触发
  13 + */
  14 +@RequiredArgsConstructor
  15 +@Getter
  16 +public enum IotRuleSceneActionTypeEnum implements ArrayValuable<Integer> {
  17 +
  18 + DEVICE_CONTROL(1), // 设备执行
  19 + ALERT(2), // 告警执行
  20 + DATA_BRIDGE(3); // 桥接执行
  21 +
  22 + private final Integer type;
  23 +
  24 + public static final Integer[] ARRAYS = Arrays.stream(values()).map(IotRuleSceneActionTypeEnum::getType).toArray(Integer[]::new);
  25 +
  26 + @Override
  27 + public Integer[] array() {
  28 + return ARRAYS;
  29 + }
  30 +
  31 +}
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/device/IotDeviceDO.java
@@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.iot.dal.dataobject.device; @@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.iot.dal.dataobject.device;
2 2
3 import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; 3 import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
4 import cn.iocoder.yudao.framework.mybatis.core.type.LongSetTypeHandler; 4 import cn.iocoder.yudao.framework.mybatis.core.type.LongSetTypeHandler;
  5 +import cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO;
5 import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; 6 import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO;
6 import cn.iocoder.yudao.module.iot.enums.device.IotDeviceStateEnum; 7 import cn.iocoder.yudao.module.iot.enums.device.IotDeviceStateEnum;
7 import com.baomidou.mybatisplus.annotation.KeySequence; 8 import com.baomidou.mybatisplus.annotation.KeySequence;
@@ -27,7 +28,7 @@ import java.util.Set; @@ -27,7 +28,7 @@ import java.util.Set;
27 @Builder 28 @Builder
28 @NoArgsConstructor 29 @NoArgsConstructor
29 @AllArgsConstructor 30 @AllArgsConstructor
30 -public class IotDeviceDO extends BaseDO { 31 +public class IotDeviceDO extends TenantBaseDO {
31 32
32 /** 33 /**
33 * 设备 ID,主键,自增 34 * 设备 ID,主键,自增
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/IotRuleSceneDO.java
1 package cn.iocoder.yudao.module.iot.dal.dataobject.rule; 1 package cn.iocoder.yudao.module.iot.dal.dataobject.rule;
2 2
3 -import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; 3 +import cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO;
4 import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; 4 import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO;
5 import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; 5 import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO;
6 import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotThingModelDO; 6 import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotThingModelDO;
7 import cn.iocoder.yudao.module.iot.enums.device.IotDeviceMessageIdentifierEnum; 7 import cn.iocoder.yudao.module.iot.enums.device.IotDeviceMessageIdentifierEnum;
8 import cn.iocoder.yudao.module.iot.enums.device.IotDeviceMessageTypeEnum; 8 import cn.iocoder.yudao.module.iot.enums.device.IotDeviceMessageTypeEnum;
  9 +import cn.iocoder.yudao.module.iot.enums.rule.IotRuleSceneActionTypeEnum;
9 import cn.iocoder.yudao.module.iot.enums.rule.IotRuleSceneTriggerConditionParameterOperatorEnum; 10 import cn.iocoder.yudao.module.iot.enums.rule.IotRuleSceneTriggerConditionParameterOperatorEnum;
  11 +import cn.iocoder.yudao.module.iot.enums.rule.IotRuleSceneTriggerTypeEnum;
10 import com.baomidou.mybatisplus.annotation.KeySequence; 12 import com.baomidou.mybatisplus.annotation.KeySequence;
11 import com.baomidou.mybatisplus.annotation.TableField; 13 import com.baomidou.mybatisplus.annotation.TableField;
12 import com.baomidou.mybatisplus.annotation.TableId; 14 import com.baomidou.mybatisplus.annotation.TableId;
@@ -30,7 +32,7 @@ import java.util.Map; @@ -30,7 +32,7 @@ import java.util.Map;
30 @Builder 32 @Builder
31 @NoArgsConstructor 33 @NoArgsConstructor
32 @AllArgsConstructor 34 @AllArgsConstructor
33 -public class IotRuleSceneDO extends BaseDO { 35 +public class IotRuleSceneDO extends TenantBaseDO {
34 36
35 /** 37 /**
36 * 场景编号 38 * 场景编号
@@ -56,24 +58,26 @@ public class IotRuleSceneDO extends BaseDO { @@ -56,24 +58,26 @@ public class IotRuleSceneDO extends BaseDO {
56 * 触发器数组 58 * 触发器数组
57 */ 59 */
58 @TableField(typeHandler = JacksonTypeHandler.class) 60 @TableField(typeHandler = JacksonTypeHandler.class)
59 - private List<Trigger> triggers; 61 + private List<TriggerConfig> triggers;
  62 +
  63 + // TODO @芋艿:需要调研下 https://help.aliyun.com/zh/iot/user-guide/scene-orchestration-1?spm=a2c4g.11186623.help-menu-30520.d_2_4_5_0.45413908fxCSVa
60 64
61 /** 65 /**
62 * 执行器数组 66 * 执行器数组
63 */ 67 */
64 @TableField(typeHandler = JacksonTypeHandler.class) 68 @TableField(typeHandler = JacksonTypeHandler.class)
65 - private List<Actuator> actuators; 69 + private List<ActionConfig> actions;
66 70
67 /** 71 /**
68 - * 触发器 72 + * 触发器配置
69 */ 73 */
70 @Data 74 @Data
71 - public static class Trigger { 75 + public static class TriggerConfig {
72 76
73 /** 77 /**
74 * 触发类型 78 * 触发类型
75 * 79 *
76 - * 枚举 {@link cn.iocoder.yudao.module.iot.enums.rule.IotRuleSceneTriggerTypeEnum} 80 + * 枚举 {@link IotRuleSceneTriggerTypeEnum}
77 */ 81 */
78 private Integer type; 82 private Integer type;
79 83
@@ -93,14 +97,15 @@ public class IotRuleSceneDO extends BaseDO { @@ -93,14 +97,15 @@ public class IotRuleSceneDO extends BaseDO {
93 /** 97 /**
94 * 触发条件数组 98 * 触发条件数组
95 * 99 *
96 - * TODO @芋艿:注释说明 100 + * 当 {@link #type} 为 {@link IotRuleSceneTriggerTypeEnum#DEVICE} 时,必填
  101 + * 条件与条件之间,是“或”的关系
97 */ 102 */
98 private List<TriggerCondition> conditions; 103 private List<TriggerCondition> conditions;
99 104
100 /** 105 /**
101 * CRON 表达式 106 * CRON 表达式
102 * 107 *
103 - * TODO @芋艿:注释说明 108 + * 当 {@link #type} 为 {@link IotRuleSceneTriggerTypeEnum#TIMER} 时,必填
104 */ 109 */
105 private String cronExpression; 110 private String cronExpression;
106 111
@@ -127,6 +132,8 @@ public class IotRuleSceneDO extends BaseDO { @@ -127,6 +132,8 @@ public class IotRuleSceneDO extends BaseDO {
127 132
128 /** 133 /**
129 * 参数数组 134 * 参数数组
  135 + *
  136 + * 参数与参数之间,是“或”的关系
130 */ 137 */
131 private List<TriggerConditionParameter> parameters; 138 private List<TriggerConditionParameter> parameters;
132 139
@@ -163,60 +170,60 @@ public class IotRuleSceneDO extends BaseDO { @@ -163,60 +170,60 @@ public class IotRuleSceneDO extends BaseDO {
163 } 170 }
164 171
165 /** 172 /**
166 - * 执行器 173 + * 执行器配置
167 */ 174 */
168 @Data 175 @Data
169 - public static class Actuator { 176 + public static class ActionConfig {
170 177
171 /** 178 /**
172 * 执行类型 179 * 执行类型
173 * 180 *
174 - * TODO @芋艿:control、alert、webhook(待定) 181 + * 枚举 {@link IotRuleSceneActionTypeEnum}
175 */ 182 */
176 private Integer type; 183 private Integer type;
177 184
178 /** 185 /**
179 - * 产品标识  
180 - *  
181 - * 关联 {@link IotProductDO#getProductKey()}  
182 - */  
183 - private String productKey;  
184 - /**  
185 - * 设备名称数组  
186 - *  
187 - * 关联 {@link IotDeviceDO#getDeviceName()}  
188 - */  
189 - private List<String> deviceNames;  
190 -  
191 - /**  
192 - * 控制数组 186 + * 设备控制
193 * 187 *
194 - * TODO 芋艿:类型的情况下 188 + * 当 {@link #type} 为 {@link IotRuleSceneActionTypeEnum#DEVICE_CONTROL} 时,必填
195 */ 189 */
196 - private List<ActuatorControl> controls; 190 + private ActionDeviceControl deviceControl;
197 191
198 /** 192 /**
199 * 数据桥接编号 193 * 数据桥接编号
200 * 194 *
201 - * TODO 芋艿:暂定! 195 + * 当 {@link #type} 为 {@link IotRuleSceneActionTypeEnum#DATA_BRIDGE} 时,必填
202 * TODO 芋艿:关联 196 * TODO 芋艿:关联
203 */ 197 */
204 - private Long bridgeId; 198 + private Long dataBridgeId;
205 199
206 } 200 }
207 201
208 /** 202 /**
209 - * 执行器控制 203 + * 执行设备控制
210 */ 204 */
211 @Data 205 @Data
212 - public static class ActuatorControl { 206 + public static class ActionDeviceControl {
  207 +
  208 + /**
  209 + * 产品标识
  210 + *
  211 + * 关联 {@link IotProductDO#getProductKey()}
  212 + */
  213 + private String productKey;
  214 + /**
  215 + * 设备名称数组
  216 + *
  217 + * 关联 {@link IotDeviceDO#getDeviceName()}
  218 + */
  219 + private List<String> deviceNames;
213 220
214 /** 221 /**
215 * 消息类型 222 * 消息类型
216 * 223 *
217 * 枚举 {@link IotDeviceMessageTypeEnum#PROPERTY}、{@link IotDeviceMessageTypeEnum#SERVICE} 224 * 枚举 {@link IotDeviceMessageTypeEnum#PROPERTY}、{@link IotDeviceMessageTypeEnum#SERVICE}
218 */ 225 */
219 - private Integer type; 226 + private String type;
220 /** 227 /**
221 * 消息标识符 228 * 消息标识符
222 * 229 *
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/redis/RedisKeyConstants.java
@@ -17,7 +17,7 @@ public interface RedisKeyConstants { @@ -17,7 +17,7 @@ public interface RedisKeyConstants {
17 * HASH KEY:identifier 属性标识 17 * HASH KEY:identifier 属性标识
18 * VALUE 数据类型:String(JSON) {@link IotDevicePropertyDO} 18 * VALUE 数据类型:String(JSON) {@link IotDevicePropertyDO}
19 */ 19 */
20 - String DEVICE_PROPERTY = "device_property:%s"; 20 + String DEVICE_PROPERTY = "iot:device_property:%s";
21 21
22 /** 22 /**
23 * 设备的最后上报时间,采用 ZSET 结构 23 * 设备的最后上报时间,采用 ZSET 结构
@@ -25,23 +25,23 @@ public interface RedisKeyConstants { @@ -25,23 +25,23 @@ public interface RedisKeyConstants {
25 * KEY 格式:{deviceKey} 25 * KEY 格式:{deviceKey}
26 * SCORE:上报时间 26 * SCORE:上报时间
27 */ 27 */
28 - String DEVICE_REPORT_TIMES = "device_report_times"; 28 + String DEVICE_REPORT_TIMES = "iot:device_report_times";
29 29
30 /** 30 /**
31 - * 设备信息的数据缓存,使用 Spring Cache 操作 31 + * 设备信息的数据缓存,使用 Spring Cache 操作(忽略租户)
32 * 32 *
33 * KEY 格式:device_${productKey}_${deviceKey} 33 * KEY 格式:device_${productKey}_${deviceKey}
34 * VALUE 数据类型:String(JSON) 34 * VALUE 数据类型:String(JSON)
35 */ 35 */
36 - String DEVICE = "device"; 36 + String DEVICE = "iot:device";
37 37
38 /** 38 /**
39 - * 物模型的数据缓存,使用 Spring Cache 操作 39 + * 物模型的数据缓存,使用 Spring Cache 操作(忽略租户)
40 * 40 *
41 * KEY 格式:thing_model_${productKey} 41 * KEY 格式:thing_model_${productKey}
42 * VALUE 数据类型:String 数组(JSON),即 {@link cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotThingModelDO} 列表 42 * VALUE 数据类型:String 数组(JSON),即 {@link cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotThingModelDO} 列表
43 */ 43 */
44 - String THING_MODEL_LIST = "thing_model_list"; 44 + String THING_MODEL_LIST = "iot:thing_model_list";
45 45
46 /** 46 /**
47 * 设备插件的插件进程编号的映射,采用 HASH 结构 47 * 设备插件的插件进程编号的映射,采用 HASH 结构
@@ -50,6 +50,6 @@ public interface RedisKeyConstants { @@ -50,6 +50,6 @@ public interface RedisKeyConstants {
50 * HASH KEY:${deviceKey} 50 * HASH KEY:${deviceKey}
51 * VALUE:插件进程编号,对应 {@link IotPluginInstanceDO#getProcessId()} 字段 51 * VALUE:插件进程编号,对应 {@link IotPluginInstanceDO#getProcessId()} 字段
52 */ 52 */
53 - String DEVICE_PLUGIN_INSTANCE_PROCESS_IDS = "device_plugin_instance_process_ids"; 53 + String DEVICE_PLUGIN_INSTANCE_PROCESS_IDS = "iot:device_plugin_instance_process_ids";
54 54
55 } 55 }
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/consumer/rule/IotRuleSceneMessageHandler.java
@@ -24,7 +24,7 @@ public class IotRuleSceneMessageHandler { @@ -24,7 +24,7 @@ public class IotRuleSceneMessageHandler {
24 @Async 24 @Async
25 public void onMessage(IotDeviceMessage message) { 25 public void onMessage(IotDeviceMessage message) {
26 log.info("[onMessage][消息内容({})]", message); 26 log.info("[onMessage][消息内容({})]", message);
27 - ruleSceneService.executeRuleScene(message); 27 + ruleSceneService.executeRuleSceneByDevice(message);
28 } 28 }
29 29
30 } 30 }
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/consumer/rule/package-info.java deleted
1 -/**  
2 - * TODO 芋艿:未来实现一个 IotRuleMessageConsumer  
3 - */  
4 -package cn.iocoder.yudao.module.iot.mq.consumer.rule;  
5 \ No newline at end of file 0 \ No newline at end of file
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/message/IotDeviceMessage.java
@@ -67,4 +67,9 @@ public class IotDeviceMessage { @@ -67,4 +67,9 @@ public class IotDeviceMessage {
67 */ 67 */
68 private LocalDateTime reportTime; 68 private LocalDateTime reportTime;
69 69
  70 + /**
  71 + * 租户编号
  72 + */
  73 + private Long tenantId;
  74 +
70 } 75 }
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceServiceImpl.java
@@ -9,6 +9,7 @@ import cn.iocoder.yudao.framework.common.exception.ServiceException; @@ -9,6 +9,7 @@ import cn.iocoder.yudao.framework.common.exception.ServiceException;
9 import cn.iocoder.yudao.framework.common.pojo.PageResult; 9 import cn.iocoder.yudao.framework.common.pojo.PageResult;
10 import cn.iocoder.yudao.framework.common.util.object.BeanUtils; 10 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
11 import cn.iocoder.yudao.framework.common.util.validation.ValidationUtils; 11 import cn.iocoder.yudao.framework.common.util.validation.ValidationUtils;
  12 +import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore;
12 import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils; 13 import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils;
13 import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.*; 14 import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.*;
14 import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; 15 import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO;
@@ -261,13 +262,9 @@ public class IotDeviceServiceImpl implements IotDeviceService { @@ -261,13 +262,9 @@ public class IotDeviceServiceImpl implements IotDeviceService {
261 } 262 }
262 263
263 @Override 264 @Override
264 - public IotDeviceDO getDeviceByProductKeyAndDeviceNameFromCache(String productKey, String deviceName) {  
265 - // 保证在 @CacheEvict 之前,忽略租户  
266 - return TenantUtils.executeIgnore(() -> getSelf().getDeviceByProductKeyAndDeviceNameFromCache0(productKey, deviceName));  
267 - }  
268 -  
269 @Cacheable(value = RedisKeyConstants.DEVICE, key = "#productKey + '_' + #deviceName", unless = "#result == null") 265 @Cacheable(value = RedisKeyConstants.DEVICE, key = "#productKey + '_' + #deviceName", unless = "#result == null")
270 - public IotDeviceDO getDeviceByProductKeyAndDeviceNameFromCache0(String productKey, String deviceName) { 266 + @TenantIgnore // 忽略租户信息,跨租户 productKey + deviceName 是唯一的
  267 + public IotDeviceDO getDeviceByProductKeyAndDeviceNameFromCache(String productKey, String deviceName) {
271 return deviceMapper.selectByProductKeyAndDeviceName(productKey, deviceName); 268 return deviceMapper.selectByProductKeyAndDeviceName(productKey, deviceName);
272 } 269 }
273 270
@@ -389,8 +386,8 @@ public class IotDeviceServiceImpl implements IotDeviceService { @@ -389,8 +386,8 @@ public class IotDeviceServiceImpl implements IotDeviceService {
389 } 386 }
390 387
391 private void deleteDeviceCache(IotDeviceDO device) { 388 private void deleteDeviceCache(IotDeviceDO device) {
392 - // 保证在 @CacheEvict 之前,忽略租户  
393 - TenantUtils.executeIgnore(() -> getSelf().deleteDeviceCache0(device)); 389 + // 保证 Spring AOP 触发
  390 + getSelf().deleteDeviceCache0(device);
394 } 391 }
395 392
396 private void deleteDeviceCache(List<IotDeviceDO> devices) { 393 private void deleteDeviceCache(List<IotDeviceDO> devices) {
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/control/IotDeviceDownstreamService.java
1 package cn.iocoder.yudao.module.iot.service.device.control; 1 package cn.iocoder.yudao.module.iot.service.device.control;
2 2
3 import cn.iocoder.yudao.module.iot.controller.admin.device.vo.control.IotDeviceDownstreamReqVO; 3 import cn.iocoder.yudao.module.iot.controller.admin.device.vo.control.IotDeviceDownstreamReqVO;
  4 +import cn.iocoder.yudao.module.iot.mq.message.IotDeviceMessage;
4 import jakarta.validation.Valid; 5 import jakarta.validation.Valid;
5 6
6 /** 7 /**
@@ -16,7 +17,8 @@ public interface IotDeviceDownstreamService { @@ -16,7 +17,8 @@ public interface IotDeviceDownstreamService {
16 * 设备下行,可用于设备模拟 17 * 设备下行,可用于设备模拟
17 * 18 *
18 * @param downstreamReqVO 设备下行请求 VO 19 * @param downstreamReqVO 设备下行请求 VO
  20 + * @return 下发消息
19 */ 21 */
20 - void downstreamDevice(@Valid IotDeviceDownstreamReqVO downstreamReqVO); 22 + IotDeviceMessage downstreamDevice(@Valid IotDeviceDownstreamReqVO downstreamReqVO);
21 23
22 } 24 }
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/control/IotDeviceDownstreamServiceImpl.java
@@ -54,7 +54,7 @@ public class IotDeviceDownstreamServiceImpl implements IotDeviceDownstreamServic @@ -54,7 +54,7 @@ public class IotDeviceDownstreamServiceImpl implements IotDeviceDownstreamServic
54 private IotDeviceProducer deviceProducer; 54 private IotDeviceProducer deviceProducer;
55 55
56 @Override 56 @Override
57 - public void downstreamDevice(IotDeviceDownstreamReqVO downstreamReqVO) { 57 + public IotDeviceMessage downstreamDevice(IotDeviceDownstreamReqVO downstreamReqVO) {
58 // 校验设备是否存在 58 // 校验设备是否存在
59 IotDeviceDO device = deviceService.validateDeviceExists(downstreamReqVO.getId()); 59 IotDeviceDO device = deviceService.validateDeviceExists(downstreamReqVO.getId());
60 // TODO 芋艿:父设备的处理 60 // TODO 芋艿:父设备的处理
@@ -62,31 +62,28 @@ public class IotDeviceDownstreamServiceImpl implements IotDeviceDownstreamServic @@ -62,31 +62,28 @@ public class IotDeviceDownstreamServiceImpl implements IotDeviceDownstreamServic
62 62
63 // 服务调用 63 // 服务调用
64 if (Objects.equals(downstreamReqVO.getType(), IotDeviceMessageTypeEnum.SERVICE.getType())) { 64 if (Objects.equals(downstreamReqVO.getType(), IotDeviceMessageTypeEnum.SERVICE.getType())) {
65 - invokeDeviceService(downstreamReqVO, device, parentDevice);  
66 - return; 65 + return invokeDeviceService(downstreamReqVO, device, parentDevice);
67 } 66 }
68 // 属性相关 67 // 属性相关
69 if (Objects.equals(downstreamReqVO.getType(), IotDeviceMessageTypeEnum.PROPERTY.getType())) { 68 if (Objects.equals(downstreamReqVO.getType(), IotDeviceMessageTypeEnum.PROPERTY.getType())) {
70 // 属性设置 69 // 属性设置
71 if (Objects.equals(downstreamReqVO.getIdentifier(), 70 if (Objects.equals(downstreamReqVO.getIdentifier(),
72 IotDeviceMessageIdentifierEnum.PROPERTY_SET.getIdentifier())) { 71 IotDeviceMessageIdentifierEnum.PROPERTY_SET.getIdentifier())) {
73 - setDeviceProperty(downstreamReqVO, device, parentDevice);  
74 - return; 72 + return setDeviceProperty(downstreamReqVO, device, parentDevice);
75 } 73 }
76 // 属性设置 74 // 属性设置
77 if (Objects.equals(downstreamReqVO.getIdentifier(), 75 if (Objects.equals(downstreamReqVO.getIdentifier(),
78 IotDeviceMessageIdentifierEnum.PROPERTY_GET.getIdentifier())) { 76 IotDeviceMessageIdentifierEnum.PROPERTY_GET.getIdentifier())) {
79 - getDeviceProperty(downstreamReqVO, device, parentDevice);  
80 - return; 77 + return getDeviceProperty(downstreamReqVO, device, parentDevice);
81 } 78 }
82 } 79 }
83 // 配置下发 80 // 配置下发
84 if (Objects.equals(downstreamReqVO.getType(), IotDeviceMessageTypeEnum.CONFIG.getType()) 81 if (Objects.equals(downstreamReqVO.getType(), IotDeviceMessageTypeEnum.CONFIG.getType())
85 && Objects.equals(downstreamReqVO.getIdentifier(), IotDeviceMessageIdentifierEnum.CONFIG_SET.getIdentifier())) { 82 && Objects.equals(downstreamReqVO.getIdentifier(), IotDeviceMessageIdentifierEnum.CONFIG_SET.getIdentifier())) {
86 - setDeviceConfig(downstreamReqVO, device, parentDevice);  
87 - return; 83 + return setDeviceConfig(downstreamReqVO, device, parentDevice);
88 } 84 }
89 // TODO 芋艿:ota 升级 85 // TODO 芋艿:ota 升级
  86 + return null;
90 } 87 }
91 88
92 /** 89 /**
@@ -95,9 +92,10 @@ public class IotDeviceDownstreamServiceImpl implements IotDeviceDownstreamServic @@ -95,9 +92,10 @@ public class IotDeviceDownstreamServiceImpl implements IotDeviceDownstreamServic
95 * @param downstreamReqVO 下行请求 92 * @param downstreamReqVO 下行请求
96 * @param device 设备 93 * @param device 设备
97 * @param parentDevice 父设备 94 * @param parentDevice 父设备
  95 + * @return 下发消息
98 */ 96 */
99 @SuppressWarnings("unchecked") 97 @SuppressWarnings("unchecked")
100 - private void invokeDeviceService(IotDeviceDownstreamReqVO downstreamReqVO, 98 + private IotDeviceMessage invokeDeviceService(IotDeviceDownstreamReqVO downstreamReqVO,
101 IotDeviceDO device, IotDeviceDO parentDevice) { 99 IotDeviceDO device, IotDeviceDO parentDevice) {
102 // 1. 参数校验 100 // 1. 参数校验
103 if (!(downstreamReqVO.getData() instanceof Map<?, ?>)) { 101 if (!(downstreamReqVO.getData() instanceof Map<?, ?>)) {
@@ -124,6 +122,7 @@ public class IotDeviceDownstreamServiceImpl implements IotDeviceDownstreamServic @@ -124,6 +122,7 @@ public class IotDeviceDownstreamServiceImpl implements IotDeviceDownstreamServic
124 device.getDeviceKey(), reqDTO, result); 122 device.getDeviceKey(), reqDTO, result);
125 throw exception(DEVICE_DOWNSTREAM_FAILED, result.getMsg()); 123 throw exception(DEVICE_DOWNSTREAM_FAILED, result.getMsg());
126 } 124 }
  125 + return message;
127 } 126 }
128 127
129 /** 128 /**
@@ -132,10 +131,11 @@ public class IotDeviceDownstreamServiceImpl implements IotDeviceDownstreamServic @@ -132,10 +131,11 @@ public class IotDeviceDownstreamServiceImpl implements IotDeviceDownstreamServic
132 * @param downstreamReqVO 下行请求 131 * @param downstreamReqVO 下行请求
133 * @param device 设备 132 * @param device 设备
134 * @param parentDevice 父设备 133 * @param parentDevice 父设备
  134 + * @return 下发消息
135 */ 135 */
136 @SuppressWarnings("unchecked") 136 @SuppressWarnings("unchecked")
137 - private void setDeviceProperty(IotDeviceDownstreamReqVO downstreamReqVO,  
138 - IotDeviceDO device, IotDeviceDO parentDevice) { 137 + private IotDeviceMessage setDeviceProperty(IotDeviceDownstreamReqVO downstreamReqVO,
  138 + IotDeviceDO device, IotDeviceDO parentDevice) {
139 // 1. 参数校验 139 // 1. 参数校验
140 if (!(downstreamReqVO.getData() instanceof Map<?, ?>)) { 140 if (!(downstreamReqVO.getData() instanceof Map<?, ?>)) {
141 throw new ServiceException(BAD_REQUEST.getCode(), "data 不是 Map 类型"); 141 throw new ServiceException(BAD_REQUEST.getCode(), "data 不是 Map 类型");
@@ -162,6 +162,7 @@ public class IotDeviceDownstreamServiceImpl implements IotDeviceDownstreamServic @@ -162,6 +162,7 @@ public class IotDeviceDownstreamServiceImpl implements IotDeviceDownstreamServic
162 device.getDeviceKey(), reqDTO, result); 162 device.getDeviceKey(), reqDTO, result);
163 throw exception(DEVICE_DOWNSTREAM_FAILED, result.getMsg()); 163 throw exception(DEVICE_DOWNSTREAM_FAILED, result.getMsg());
164 } 164 }
  165 + return message;
165 } 166 }
166 167
167 /** 168 /**
@@ -170,10 +171,11 @@ public class IotDeviceDownstreamServiceImpl implements IotDeviceDownstreamServic @@ -170,10 +171,11 @@ public class IotDeviceDownstreamServiceImpl implements IotDeviceDownstreamServic
170 * @param downstreamReqVO 下行请求 171 * @param downstreamReqVO 下行请求
171 * @param device 设备 172 * @param device 设备
172 * @param parentDevice 父设备 173 * @param parentDevice 父设备
  174 + * @return 下发消息
173 */ 175 */
174 @SuppressWarnings("unchecked") 176 @SuppressWarnings("unchecked")
175 - private void getDeviceProperty(IotDeviceDownstreamReqVO downstreamReqVO,  
176 - IotDeviceDO device, IotDeviceDO parentDevice) { 177 + private IotDeviceMessage getDeviceProperty(IotDeviceDownstreamReqVO downstreamReqVO,
  178 + IotDeviceDO device, IotDeviceDO parentDevice) {
177 // 1. 参数校验 179 // 1. 参数校验
178 if (!(downstreamReqVO.getData() instanceof List<?>)) { 180 if (!(downstreamReqVO.getData() instanceof List<?>)) {
179 throw new ServiceException(BAD_REQUEST.getCode(), "data 不是 List 类型"); 181 throw new ServiceException(BAD_REQUEST.getCode(), "data 不是 List 类型");
@@ -200,6 +202,7 @@ public class IotDeviceDownstreamServiceImpl implements IotDeviceDownstreamServic @@ -200,6 +202,7 @@ public class IotDeviceDownstreamServiceImpl implements IotDeviceDownstreamServic
200 device.getDeviceKey(), reqDTO, result); 202 device.getDeviceKey(), reqDTO, result);
201 throw exception(DEVICE_DOWNSTREAM_FAILED, result.getMsg()); 203 throw exception(DEVICE_DOWNSTREAM_FAILED, result.getMsg());
202 } 204 }
  205 + return message;
203 } 206 }
204 207
205 /** 208 /**
@@ -208,10 +211,11 @@ public class IotDeviceDownstreamServiceImpl implements IotDeviceDownstreamServic @@ -208,10 +211,11 @@ public class IotDeviceDownstreamServiceImpl implements IotDeviceDownstreamServic
208 * @param downstreamReqVO 下行请求 211 * @param downstreamReqVO 下行请求
209 * @param device 设备 212 * @param device 设备
210 * @param parentDevice 父设备 213 * @param parentDevice 父设备
  214 + * @return 下发消息
211 */ 215 */
212 @SuppressWarnings({"unchecked", "unused"}) 216 @SuppressWarnings({"unchecked", "unused"})
213 - private void setDeviceConfig(IotDeviceDownstreamReqVO downstreamReqVO,  
214 - IotDeviceDO device, IotDeviceDO parentDevice) { 217 + private IotDeviceMessage setDeviceConfig(IotDeviceDownstreamReqVO downstreamReqVO,
  218 + IotDeviceDO device, IotDeviceDO parentDevice) {
215 // 1. 参数转换,无需校验 219 // 1. 参数转换,无需校验
216 Map<String, Object> config = JsonUtils.parseObject(device.getConfig(), Map.class); 220 Map<String, Object> config = JsonUtils.parseObject(device.getConfig(), Map.class);
217 221
@@ -235,6 +239,7 @@ public class IotDeviceDownstreamServiceImpl implements IotDeviceDownstreamServic @@ -235,6 +239,7 @@ public class IotDeviceDownstreamServiceImpl implements IotDeviceDownstreamServic
235 device.getDeviceKey(), reqDTO, result); 239 device.getDeviceKey(), reqDTO, result);
236 throw exception(DEVICE_DOWNSTREAM_FAILED, result.getMsg()); 240 throw exception(DEVICE_DOWNSTREAM_FAILED, result.getMsg());
237 } 241 }
  242 + return message;
238 } 243 }
239 244
240 /** 245 /**
@@ -275,7 +280,8 @@ public class IotDeviceDownstreamServiceImpl implements IotDeviceDownstreamServic @@ -275,7 +280,8 @@ public class IotDeviceDownstreamServiceImpl implements IotDeviceDownstreamServic
275 private void sendDeviceMessage(IotDeviceMessage message, IotDeviceDO device, Integer code) { 280 private void sendDeviceMessage(IotDeviceMessage message, IotDeviceDO device, Integer code) {
276 // 1. 完善消息 281 // 1. 完善消息
277 message.setProductKey(device.getProductKey()).setDeviceName(device.getDeviceName()) 282 message.setProductKey(device.getProductKey()).setDeviceName(device.getDeviceName())
278 - .setDeviceKey(device.getDeviceKey()); 283 + .setDeviceKey(device.getDeviceKey())
  284 + .setTenantId(device.getTenantId());
279 Assert.notNull(message.getRequestId(), "requestId 不能为空"); 285 Assert.notNull(message.getRequestId(), "requestId 不能为空");
280 if (message.getReportTime() == null) { 286 if (message.getReportTime() == null) {
281 message.setReportTime(LocalDateTime.now()); 287 message.setReportTime(LocalDateTime.now());
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/control/IotDeviceUpstreamServiceImpl.java
@@ -98,26 +98,27 @@ public class IotDeviceUpstreamServiceImpl implements IotDeviceUpstreamService { @@ -98,26 +98,27 @@ public class IotDeviceUpstreamServiceImpl implements IotDeviceUpstreamService {
98 updateReqDTO.getProductKey(), updateReqDTO.getDeviceName()); 98 updateReqDTO.getProductKey(), updateReqDTO.getDeviceName());
99 return; 99 return;
100 } 100 }
101 - // 1.2 记录设备的最后时间  
102 - updateDeviceLastTime(device, updateReqDTO);  
103 - // 1.3 当前状态一致,不处理  
104 - if (Objects.equals(device.getState(), updateReqDTO.getState())) {  
105 - return;  
106 - }  
107 -  
108 - // 2. 更新设备状态  
109 - TenantUtils.executeIgnore(() ->  
110 - deviceService.updateDeviceState(device.getId(), updateReqDTO.getState()));  
111 -  
112 - // 3. TODO 芋艿:子设备的关联  
113 -  
114 - // 4. 发送设备消息  
115 - IotDeviceMessage message = BeanUtils.toBean(updateReqDTO, IotDeviceMessage.class)  
116 - .setType(IotDeviceMessageTypeEnum.STATE.getType())  
117 - .setIdentifier(ObjUtil.equals(updateReqDTO.getState(), IotDeviceStateEnum.ONLINE.getState())  
118 - ? IotDeviceMessageIdentifierEnum.STATE_ONLINE.getIdentifier()  
119 - : IotDeviceMessageIdentifierEnum.STATE_OFFLINE.getIdentifier());  
120 - sendDeviceMessage(message, device); 101 + TenantUtils.execute(device.getTenantId(), () -> {
  102 + // 1.2 记录设备的最后时间
  103 + updateDeviceLastTime(device, updateReqDTO);
  104 + // 1.3 当前状态一致,不处理
  105 + if (Objects.equals(device.getState(), updateReqDTO.getState())) {
  106 + return;
  107 + }
  108 +
  109 + // 2. 更新设备状态
  110 + deviceService.updateDeviceState(device.getId(), updateReqDTO.getState());
  111 +
  112 + // 3. TODO 芋艿:子设备的关联
  113 +
  114 + // 4. 发送设备消息
  115 + IotDeviceMessage message = BeanUtils.toBean(updateReqDTO, IotDeviceMessage.class)
  116 + .setType(IotDeviceMessageTypeEnum.STATE.getType())
  117 + .setIdentifier(ObjUtil.equals(updateReqDTO.getState(), IotDeviceStateEnum.ONLINE.getState())
  118 + ? IotDeviceMessageIdentifierEnum.STATE_ONLINE.getIdentifier()
  119 + : IotDeviceMessageIdentifierEnum.STATE_OFFLINE.getIdentifier());
  120 + sendDeviceMessage(message, device);
  121 + });
121 } 122 }
122 123
123 @Override 124 @Override
@@ -174,7 +175,8 @@ public class IotDeviceUpstreamServiceImpl implements IotDeviceUpstreamService { @@ -174,7 +175,8 @@ public class IotDeviceUpstreamServiceImpl implements IotDeviceUpstreamService {
174 175
175 private void sendDeviceMessage(IotDeviceMessage message, IotDeviceDO device) { 176 private void sendDeviceMessage(IotDeviceMessage message, IotDeviceDO device) {
176 // 1. 完善消息 177 // 1. 完善消息
177 - message.setDeviceKey(device.getDeviceKey()); 178 + message.setDeviceKey(device.getDeviceKey())
  179 + .setTenantId(device.getTenantId());
178 if (StrUtil.isEmpty(message.getRequestId())) { 180 if (StrUtil.isEmpty(message.getRequestId())) {
179 message.setRequestId(IdUtil.fastSimpleUUID()); 181 message.setRequestId(IdUtil.fastSimpleUUID());
180 } 182 }
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/IotRuleSceneService.java
1 package cn.iocoder.yudao.module.iot.service.rule; 1 package cn.iocoder.yudao.module.iot.service.rule;
2 2
3 import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotRuleSceneDO; 3 import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotRuleSceneDO;
  4 +import cn.iocoder.yudao.module.iot.enums.rule.IotRuleSceneTriggerTypeEnum;
4 import cn.iocoder.yudao.module.iot.mq.message.IotDeviceMessage; 5 import cn.iocoder.yudao.module.iot.mq.message.IotDeviceMessage;
5 6
6 import java.util.List; 7 import java.util.List;
@@ -22,10 +23,12 @@ public interface IotRuleSceneService { @@ -22,10 +23,12 @@ public interface IotRuleSceneService {
22 List<IotRuleSceneDO> getRuleSceneListByProductKeyAndDeviceNameFromCache(String productKey, String deviceName); 23 List<IotRuleSceneDO> getRuleSceneListByProductKeyAndDeviceNameFromCache(String productKey, String deviceName);
23 24
24 /** 25 /**
25 - * 执行规则场景 26 + * 基于 {@link IotRuleSceneTriggerTypeEnum#DEVICE} 场景,执行规则场景
26 * 27 *
27 * @param message 消息 28 * @param message 消息
28 */ 29 */
29 - void executeRuleScene(IotDeviceMessage message); 30 + void executeRuleSceneByDevice(IotDeviceMessage message);
  31 +
  32 + // TODO @芋艿:基于 timer 场景,执行规则场景
30 33
31 } 34 }
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/IotRuleSceneServiceImpl.java
@@ -11,13 +11,16 @@ import cn.iocoder.yudao.framework.common.util.number.NumberUtils; @@ -11,13 +11,16 @@ import cn.iocoder.yudao.framework.common.util.number.NumberUtils;
11 import cn.iocoder.yudao.framework.common.util.object.ObjectUtils; 11 import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
12 import cn.iocoder.yudao.framework.common.util.spring.SpringExpressionUtils; 12 import cn.iocoder.yudao.framework.common.util.spring.SpringExpressionUtils;
13 import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore; 13 import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore;
  14 +import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils;
14 import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotRuleSceneDO; 15 import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotRuleSceneDO;
15 import cn.iocoder.yudao.module.iot.dal.mysql.rule.IotRuleSceneMapper; 16 import cn.iocoder.yudao.module.iot.dal.mysql.rule.IotRuleSceneMapper;
16 import cn.iocoder.yudao.module.iot.enums.device.IotDeviceMessageIdentifierEnum; 17 import cn.iocoder.yudao.module.iot.enums.device.IotDeviceMessageIdentifierEnum;
17 import cn.iocoder.yudao.module.iot.enums.device.IotDeviceMessageTypeEnum; 18 import cn.iocoder.yudao.module.iot.enums.device.IotDeviceMessageTypeEnum;
  19 +import cn.iocoder.yudao.module.iot.enums.rule.IotRuleSceneActionTypeEnum;
18 import cn.iocoder.yudao.module.iot.enums.rule.IotRuleSceneTriggerConditionParameterOperatorEnum; 20 import cn.iocoder.yudao.module.iot.enums.rule.IotRuleSceneTriggerConditionParameterOperatorEnum;
19 import cn.iocoder.yudao.module.iot.enums.rule.IotRuleSceneTriggerTypeEnum; 21 import cn.iocoder.yudao.module.iot.enums.rule.IotRuleSceneTriggerTypeEnum;
20 import cn.iocoder.yudao.module.iot.mq.message.IotDeviceMessage; 22 import cn.iocoder.yudao.module.iot.mq.message.IotDeviceMessage;
  23 +import cn.iocoder.yudao.module.iot.service.rule.action.IotRuleSceneAction;
21 import jakarta.annotation.Resource; 24 import jakarta.annotation.Resource;
22 import lombok.extern.slf4j.Slf4j; 25 import lombok.extern.slf4j.Slf4j;
23 import org.springframework.stereotype.Service; 26 import org.springframework.stereotype.Service;
@@ -43,6 +46,9 @@ public class IotRuleSceneServiceImpl implements IotRuleSceneService { @@ -43,6 +46,9 @@ public class IotRuleSceneServiceImpl implements IotRuleSceneService {
43 @Resource 46 @Resource
44 private IotRuleSceneMapper ruleSceneMapper; 47 private IotRuleSceneMapper ruleSceneMapper;
45 48
  49 + @Resource
  50 + private List<IotRuleSceneAction> ruleSceneActions;
  51 +
46 // TODO 芋艿,缓存待实现 52 // TODO 芋艿,缓存待实现
47 @Override 53 @Override
48 @TenantIgnore // 忽略租户隔离:因为 IotRuleSceneMessageHandler 调用时,一般未传递租户,所以需要忽略 54 @TenantIgnore // 忽略租户隔离:因为 IotRuleSceneMessageHandler 调用时,一般未传递租户,所以需要忽略
@@ -50,7 +56,7 @@ public class IotRuleSceneServiceImpl implements IotRuleSceneService { @@ -50,7 +56,7 @@ public class IotRuleSceneServiceImpl implements IotRuleSceneService {
50 if (true) { 56 if (true) {
51 IotRuleSceneDO ruleScene01 = new IotRuleSceneDO(); 57 IotRuleSceneDO ruleScene01 = new IotRuleSceneDO();
52 ruleScene01.setTriggers(CollUtil.newArrayList()); 58 ruleScene01.setTriggers(CollUtil.newArrayList());
53 - IotRuleSceneDO.Trigger trigger01 = new IotRuleSceneDO.Trigger(); 59 + IotRuleSceneDO.TriggerConfig trigger01 = new IotRuleSceneDO.TriggerConfig();
54 trigger01.setType(IotRuleSceneTriggerTypeEnum.DEVICE.getType()); 60 trigger01.setType(IotRuleSceneTriggerTypeEnum.DEVICE.getType());
55 trigger01.setConditions(CollUtil.newArrayList()); 61 trigger01.setConditions(CollUtil.newArrayList());
56 // 属性 62 // 属性
@@ -120,7 +126,7 @@ public class IotRuleSceneServiceImpl implements IotRuleSceneService { @@ -120,7 +126,7 @@ public class IotRuleSceneServiceImpl implements IotRuleSceneService {
120 condition02.setIdentifier(IotDeviceMessageIdentifierEnum.STATE_ONLINE.getIdentifier()); 126 condition02.setIdentifier(IotDeviceMessageIdentifierEnum.STATE_ONLINE.getIdentifier());
121 condition02.setParameters(CollUtil.newArrayList()); 127 condition02.setParameters(CollUtil.newArrayList());
122 trigger01.getConditions().add(condition02); 128 trigger01.getConditions().add(condition02);
123 - // TODO 芋艿:事件 129 + // 事件
124 IotRuleSceneDO.TriggerCondition condition03 = new IotRuleSceneDO.TriggerCondition(); 130 IotRuleSceneDO.TriggerCondition condition03 = new IotRuleSceneDO.TriggerCondition();
125 condition03.setType(IotDeviceMessageTypeEnum.EVENT.getType()); 131 condition03.setType(IotDeviceMessageTypeEnum.EVENT.getType());
126 condition03.setIdentifier("xxx"); 132 condition03.setIdentifier("xxx");
@@ -131,13 +137,28 @@ public class IotRuleSceneServiceImpl implements IotRuleSceneService { @@ -131,13 +137,28 @@ public class IotRuleSceneServiceImpl implements IotRuleSceneService {
131 parameter030.setValue("1"); 137 parameter030.setValue("1");
132 trigger01.getConditions().add(condition03); 138 trigger01.getConditions().add(condition03);
133 ruleScene01.getTriggers().add(trigger01); 139 ruleScene01.getTriggers().add(trigger01);
  140 + // 动作
  141 + ruleScene01.setActions(CollUtil.newArrayList());
  142 + IotRuleSceneDO.ActionConfig action01 = new IotRuleSceneDO.ActionConfig();
  143 + action01.setType(IotRuleSceneActionTypeEnum.DEVICE_CONTROL.getType());
  144 + IotRuleSceneDO.ActionDeviceControl actionDeviceControl01 = new IotRuleSceneDO.ActionDeviceControl();
  145 + actionDeviceControl01.setProductKey("4aymZgOTOOCrDKRT");
  146 + actionDeviceControl01.setDeviceNames(ListUtil.of("small"));
  147 + actionDeviceControl01.setType(IotDeviceMessageTypeEnum.PROPERTY.getType());
  148 + actionDeviceControl01.setIdentifier(IotDeviceMessageIdentifierEnum.PROPERTY_SET.getIdentifier());
  149 + actionDeviceControl01.setData(MapUtil.<String, Object>builder()
  150 + .put("power", 1)
  151 + .put("color", "red")
  152 + .build());
  153 + action01.setDeviceControl(actionDeviceControl01);
  154 + ruleScene01.getActions().add(action01);
134 return ListUtil.toList(ruleScene01); 155 return ListUtil.toList(ruleScene01);
135 } 156 }
136 157
137 List<IotRuleSceneDO> list = ruleSceneMapper.selectList(); 158 List<IotRuleSceneDO> list = ruleSceneMapper.selectList();
138 // TODO @芋艿:需要考虑开启状态 159 // TODO @芋艿:需要考虑开启状态
139 return filterList(list, ruleScene -> { 160 return filterList(list, ruleScene -> {
140 - for (IotRuleSceneDO.Trigger trigger : ruleScene.getTriggers()) { 161 + for (IotRuleSceneDO.TriggerConfig trigger : ruleScene.getTriggers()) {
141 if (ObjUtil.notEqual(trigger.getProductKey(), productKey)) { 162 if (ObjUtil.notEqual(trigger.getProductKey(), productKey)) {
142 continue; 163 continue;
143 } 164 }
@@ -151,12 +172,17 @@ public class IotRuleSceneServiceImpl implements IotRuleSceneService { @@ -151,12 +172,17 @@ public class IotRuleSceneServiceImpl implements IotRuleSceneService {
151 } 172 }
152 173
153 @Override 174 @Override
154 - public void executeRuleScene(IotDeviceMessage message) {  
155 - // 1. 获得设备匹配的规则场景  
156 - List<IotRuleSceneDO> ruleScenes = getMatchedRuleSceneList(message);  
157 - if (CollUtil.isEmpty(ruleScenes)) {  
158 - return;  
159 - } 175 + public void executeRuleSceneByDevice(IotDeviceMessage message) {
  176 + TenantUtils.execute(message.getTenantId(), () -> {
  177 + // 1. 获得设备匹配的规则场景
  178 + List<IotRuleSceneDO> ruleScenes = getMatchedRuleSceneList(message);
  179 + if (CollUtil.isEmpty(ruleScenes)) {
  180 + return;
  181 + }
  182 +
  183 + // 2. 执行规则场景
  184 + executeRuleSceneAction(message, ruleScenes);
  185 + });
160 } 186 }
161 187
162 /** 188 /**
@@ -176,7 +202,7 @@ public class IotRuleSceneServiceImpl implements IotRuleSceneService { @@ -176,7 +202,7 @@ public class IotRuleSceneServiceImpl implements IotRuleSceneService {
176 202
177 // 2. 匹配 trigger 触发器的条件 203 // 2. 匹配 trigger 触发器的条件
178 return filterList(ruleScenes, ruleScene -> { 204 return filterList(ruleScenes, ruleScene -> {
179 - for (IotRuleSceneDO.Trigger trigger : ruleScene.getTriggers()) { 205 + for (IotRuleSceneDO.TriggerConfig trigger : ruleScene.getTriggers()) {
180 // 2.1 非设备触发,不匹配 206 // 2.1 非设备触发,不匹配
181 if (ObjUtil.notEqual(trigger.getType(), IotRuleSceneTriggerTypeEnum.DEVICE.getType())) { 207 if (ObjUtil.notEqual(trigger.getType(), IotRuleSceneTriggerTypeEnum.DEVICE.getType())) {
182 return false; 208 return false;
@@ -219,7 +245,7 @@ public class IotRuleSceneServiceImpl implements IotRuleSceneService { @@ -219,7 +245,7 @@ public class IotRuleSceneServiceImpl implements IotRuleSceneService {
219 */ 245 */
220 @SuppressWarnings({"unchecked", "DataFlowIssue"}) 246 @SuppressWarnings({"unchecked", "DataFlowIssue"})
221 private boolean isTriggerConditionParameterMatched(IotDeviceMessage message, IotRuleSceneDO.TriggerConditionParameter parameter, 247 private boolean isTriggerConditionParameterMatched(IotDeviceMessage message, IotRuleSceneDO.TriggerConditionParameter parameter,
222 - IotRuleSceneDO ruleScene, IotRuleSceneDO.Trigger trigger) { 248 + IotRuleSceneDO ruleScene, IotRuleSceneDO.TriggerConfig trigger) {
223 // 1.1 校验操作符是否合法 249 // 1.1 校验操作符是否合法
224 IotRuleSceneTriggerConditionParameterOperatorEnum operator = 250 IotRuleSceneTriggerConditionParameterOperatorEnum operator =
225 IotRuleSceneTriggerConditionParameterOperatorEnum.operatorOf(parameter.getOperator()); 251 IotRuleSceneTriggerConditionParameterOperatorEnum.operatorOf(parameter.getOperator());
@@ -266,4 +292,36 @@ public class IotRuleSceneServiceImpl implements IotRuleSceneService { @@ -266,4 +292,36 @@ public class IotRuleSceneServiceImpl implements IotRuleSceneService {
266 } 292 }
267 } 293 }
268 294
  295 + /**
  296 + * 执行规则场景的动作
  297 + *
  298 + * @param message 设备消息
  299 + * @param ruleScenes 规则场景列表
  300 + */
  301 + private void executeRuleSceneAction(IotDeviceMessage message, List<IotRuleSceneDO> ruleScenes) {
  302 + // 1. 遍历规则场景
  303 + ruleScenes.forEach(ruleScene -> {
  304 + // 2. 遍历规则场景的动作
  305 + ruleScene.getActions().forEach(actionConfig -> {
  306 + // 3.1 获取对应的动作 Action 数组
  307 + List<IotRuleSceneAction> actions = filterList(ruleSceneActions,
  308 + action -> action.getType().getType().equals(actionConfig.getType()));
  309 + if (CollUtil.isEmpty(actions)) {
  310 + return;
  311 + }
  312 + // 3.2 执行动作
  313 + actions.forEach(action -> {
  314 + try {
  315 + action.execute(message, actionConfig);
  316 + log.info("[executeRuleSceneAction][消息({}) 规则场景编号({}) 的执行动作({}) 成功]",
  317 + message, ruleScene.getId(), actionConfig);
  318 + } catch (Exception e) {
  319 + log.error("[executeRuleSceneAction][消息({}) 规则场景编号({}) 的执行动作({}) 执行异常]",
  320 + message, ruleScene.getId(), actionConfig, e);
  321 + }
  322 + });
  323 + });
  324 + });
  325 + }
  326 +
269 } 327 }
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/IotRuleSceneAction.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.service.rule.action;
  2 +
  3 +import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotRuleSceneDO;
  4 +import cn.iocoder.yudao.module.iot.enums.rule.IotRuleSceneActionTypeEnum;
  5 +import cn.iocoder.yudao.module.iot.mq.message.IotDeviceMessage;
  6 +
  7 +/**
  8 + * IOT 规则场景的场景执行器接口
  9 + *
  10 + * @author 芋道源码
  11 + */
  12 +public interface IotRuleSceneAction {
  13 +
  14 + /**
  15 + * 执行场景
  16 + *
  17 + * @param message 消息
  18 + * @param config 配置
  19 + */
  20 + void execute(IotDeviceMessage message, IotRuleSceneDO.ActionConfig config);
  21 +
  22 + /**
  23 + * 获得类型
  24 + *
  25 + * @return 类型
  26 + */
  27 + IotRuleSceneActionTypeEnum getType();
  28 +
  29 +}
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/IotRuleSceneDeviceControlAction.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.service.rule.action;
  2 +
  3 +import cn.hutool.core.lang.Assert;
  4 +import cn.iocoder.yudao.module.iot.controller.admin.device.vo.control.IotDeviceDownstreamReqVO;
  5 +import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO;
  6 +import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotRuleSceneDO;
  7 +import cn.iocoder.yudao.module.iot.enums.rule.IotRuleSceneActionTypeEnum;
  8 +import cn.iocoder.yudao.module.iot.mq.message.IotDeviceMessage;
  9 +import cn.iocoder.yudao.module.iot.service.device.IotDeviceService;
  10 +import cn.iocoder.yudao.module.iot.service.device.control.IotDeviceDownstreamService;
  11 +import jakarta.annotation.Resource;
  12 +import lombok.extern.slf4j.Slf4j;
  13 +import org.springframework.stereotype.Component;
  14 +
  15 +/**
  16 + * IoT 设备控制的 {@link IotRuleSceneAction} 实现类
  17 + *
  18 + * @author 芋道源码
  19 + */
  20 +@Component
  21 +@Slf4j
  22 +public class IotRuleSceneDeviceControlAction implements IotRuleSceneAction {
  23 +
  24 + @Resource
  25 + private IotDeviceDownstreamService deviceDownstreamService;
  26 + @Resource
  27 + private IotDeviceService deviceService;
  28 +
  29 + @Override
  30 + public void execute(IotDeviceMessage message, IotRuleSceneDO.ActionConfig config) {
  31 + IotRuleSceneDO.ActionDeviceControl control = config.getDeviceControl();
  32 + Assert.notNull(control, "设备控制配置不能为空");
  33 + // 遍历每个设备,下发消息
  34 + control.getDeviceNames().forEach(deviceName -> {
  35 + IotDeviceDO device = deviceService.getDeviceByProductKeyAndDeviceNameFromCache(control.getProductKey(), deviceName);
  36 + if (device == null) {
  37 + log.error("[execute][message({}) config({}) 对应的设备不存在]", message, config);
  38 + return;
  39 + }
  40 + try {
  41 + IotDeviceMessage downstreamMessage = deviceDownstreamService.downstreamDevice(new IotDeviceDownstreamReqVO()
  42 + .setId(device.getId()).setType(control.getType()).setIdentifier(control.getIdentifier())
  43 + .setData(control.getData()));
  44 + log.info("[execute][message({}) config({}) 下发消息({})成功]", message, config, downstreamMessage);
  45 + } catch (Exception e) {
  46 + log.error("[execute][message({}) config({}) 下发消息失败]", message, config, e);
  47 + }
  48 + });
  49 + }
  50 +
  51 + @Override
  52 + public IotRuleSceneActionTypeEnum getType() {
  53 + return IotRuleSceneActionTypeEnum.DEVICE_CONTROL;
  54 + }
  55 +
  56 +}
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thingmodel/IotThingModelServiceImpl.java
@@ -6,7 +6,7 @@ import cn.hutool.core.util.StrUtil; @@ -6,7 +6,7 @@ import cn.hutool.core.util.StrUtil;
6 import cn.hutool.extra.spring.SpringUtil; 6 import cn.hutool.extra.spring.SpringUtil;
7 import cn.iocoder.yudao.framework.common.pojo.PageResult; 7 import cn.iocoder.yudao.framework.common.pojo.PageResult;
8 import cn.iocoder.yudao.framework.common.util.object.BeanUtils; 8 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
9 -import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils; 9 +import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore;
10 import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.ThingModelEvent; 10 import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.ThingModelEvent;
11 import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.ThingModelParam; 11 import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.ThingModelParam;
12 import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.ThingModelService; 12 import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.ThingModelService;
@@ -135,13 +135,9 @@ public class IotThingModelServiceImpl implements IotThingModelService { @@ -135,13 +135,9 @@ public class IotThingModelServiceImpl implements IotThingModelService {
135 } 135 }
136 136
137 @Override 137 @Override
138 - public List<IotThingModelDO> getThingModelListByProductKeyFromCache(String productKey) {  
139 - // 保证在 @CacheEvict 之前,忽略租户  
140 - return TenantUtils.executeIgnore(() -> getSelf().getThingModelListByProductKeyFromCache0(productKey));  
141 - }  
142 -  
143 @Cacheable(value = RedisKeyConstants.THING_MODEL_LIST, key = "#productKey") 138 @Cacheable(value = RedisKeyConstants.THING_MODEL_LIST, key = "#productKey")
144 - public List<IotThingModelDO> getThingModelListByProductKeyFromCache0(String productKey) { 139 + @TenantIgnore // 忽略租户信息,跨租户 productKey 是唯一的
  140 + public List<IotThingModelDO> getThingModelListByProductKeyFromCache(String productKey) {
145 return thingModelMapper.selectListByProductKey(productKey); 141 return thingModelMapper.selectListByProductKey(productKey);
146 } 142 }
147 143
@@ -354,8 +350,8 @@ public class IotThingModelServiceImpl implements IotThingModelService { @@ -354,8 +350,8 @@ public class IotThingModelServiceImpl implements IotThingModelService {
354 } 350 }
355 351
356 private void deleteThingModelListCache(String productKey) { 352 private void deleteThingModelListCache(String productKey) {
357 - // 保证在 @CacheEvict 之前,忽略租户  
358 - TenantUtils.executeIgnore(() -> getSelf().deleteThingModelListCache0(productKey)); 353 + // 保证 Spring AOP 触发
  354 + getSelf().deleteThingModelListCache0(productKey);
359 } 355 }
360 356
361 @CacheEvict(value = RedisKeyConstants.THING_MODEL_LIST, key = "#productKey") 357 @CacheEvict(value = RedisKeyConstants.THING_MODEL_LIST, key = "#productKey")
yudao-server/src/main/resources/application.yaml
@@ -311,6 +311,8 @@ yudao: @@ -311,6 +311,8 @@ yudao:
311 - mail_account 311 - mail_account
312 - mail_template 312 - mail_template
313 - sms_template 313 - sms_template
  314 + - iot:device
  315 + - iot:thing_model_list
314 sms-code: # 短信验证码相关的配置项 316 sms-code: # 短信验证码相关的配置项
315 expire-times: 10m 317 expire-times: 10m
316 send-frequency: 1m 318 send-frequency: 1m