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 2  
3 3 import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
4 4 import cn.iocoder.yudao.framework.mybatis.core.type.LongSetTypeHandler;
  5 +import cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO;
5 6 import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO;
6 7 import cn.iocoder.yudao.module.iot.enums.device.IotDeviceStateEnum;
7 8 import com.baomidou.mybatisplus.annotation.KeySequence;
... ... @@ -27,7 +28,7 @@ import java.util.Set;
27 28 @Builder
28 29 @NoArgsConstructor
29 30 @AllArgsConstructor
30   -public class IotDeviceDO extends BaseDO {
  31 +public class IotDeviceDO extends TenantBaseDO {
31 32  
32 33 /**
33 34 * 设备 ID,主键,自增
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/IotRuleSceneDO.java
1 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 4 import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO;
5 5 import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO;
6 6 import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotThingModelDO;
7 7 import cn.iocoder.yudao.module.iot.enums.device.IotDeviceMessageIdentifierEnum;
8 8 import cn.iocoder.yudao.module.iot.enums.device.IotDeviceMessageTypeEnum;
  9 +import cn.iocoder.yudao.module.iot.enums.rule.IotRuleSceneActionTypeEnum;
9 10 import cn.iocoder.yudao.module.iot.enums.rule.IotRuleSceneTriggerConditionParameterOperatorEnum;
  11 +import cn.iocoder.yudao.module.iot.enums.rule.IotRuleSceneTriggerTypeEnum;
10 12 import com.baomidou.mybatisplus.annotation.KeySequence;
11 13 import com.baomidou.mybatisplus.annotation.TableField;
12 14 import com.baomidou.mybatisplus.annotation.TableId;
... ... @@ -30,7 +32,7 @@ import java.util.Map;
30 32 @Builder
31 33 @NoArgsConstructor
32 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 58 * 触发器数组
57 59 */
58 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 68 @TableField(typeHandler = JacksonTypeHandler.class)
65   - private List<Actuator> actuators;
  69 + private List<ActionConfig> actions;
66 70  
67 71 /**
68   - * 触发器
  72 + * 触发器配置
69 73 */
70 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 82 private Integer type;
79 83  
... ... @@ -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 103 private List<TriggerCondition> conditions;
99 104  
100 105 /**
101 106 * CRON 表达式
102 107 *
103   - * TODO @芋艿:注释说明
  108 + * 当 {@link #type} 为 {@link IotRuleSceneTriggerTypeEnum#TIMER} 时,必填
104 109 */
105 110 private String cronExpression;
106 111  
... ... @@ -127,6 +132,8 @@ public class IotRuleSceneDO extends BaseDO {
127 132  
128 133 /**
129 134 * 参数数组
  135 + *
  136 + * 参数与参数之间,是“或”的关系
130 137 */
131 138 private List<TriggerConditionParameter> parameters;
132 139  
... ... @@ -163,60 +170,60 @@ public class IotRuleSceneDO extends BaseDO {
163 170 }
164 171  
165 172 /**
166   - * 执行器
  173 + * 执行器配置
167 174 */
168 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 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 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 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 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 17 * HASH KEY:identifier 属性标识
18 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 23 * 设备的最后上报时间,采用 ZSET 结构
... ... @@ -25,23 +25,23 @@ public interface RedisKeyConstants {
25 25 * KEY 格式:{deviceKey}
26 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 33 * KEY 格式:device_${productKey}_${deviceKey}
34 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 41 * KEY 格式:thing_model_${productKey}
42 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 47 * 设备插件的插件进程编号的映射,采用 HASH 结构
... ... @@ -50,6 +50,6 @@ public interface RedisKeyConstants {
50 50 * HASH KEY:${deviceKey}
51 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 24 @Async
25 25 public void onMessage(IotDeviceMessage message) {
26 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 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 67 */
68 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 9 import cn.iocoder.yudao.framework.common.pojo.PageResult;
10 10 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
11 11 import cn.iocoder.yudao.framework.common.util.validation.ValidationUtils;
  12 +import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore;
12 13 import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils;
13 14 import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.*;
14 15 import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO;
... ... @@ -261,13 +262,9 @@ public class IotDeviceServiceImpl implements IotDeviceService {
261 262 }
262 263  
263 264 @Override
264   - public IotDeviceDO getDeviceByProductKeyAndDeviceNameFromCache(String productKey, String deviceName) {
265   - // 保证在 @CacheEvict 之前,忽略租户
266   - return TenantUtils.executeIgnore(() -> getSelf().getDeviceByProductKeyAndDeviceNameFromCache0(productKey, deviceName));
267   - }
268   -
269 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 268 return deviceMapper.selectByProductKeyAndDeviceName(productKey, deviceName);
272 269 }
273 270  
... ... @@ -389,8 +386,8 @@ public class IotDeviceServiceImpl implements IotDeviceService {
389 386 }
390 387  
391 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 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 1 package cn.iocoder.yudao.module.iot.service.device.control;
2 2  
3 3 import cn.iocoder.yudao.module.iot.controller.admin.device.vo.control.IotDeviceDownstreamReqVO;
  4 +import cn.iocoder.yudao.module.iot.mq.message.IotDeviceMessage;
4 5 import jakarta.validation.Valid;
5 6  
6 7 /**
... ... @@ -16,7 +17,8 @@ public interface IotDeviceDownstreamService {
16 17 * 设备下行,可用于设备模拟
17 18 *
18 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 54 private IotDeviceProducer deviceProducer;
55 55  
56 56 @Override
57   - public void downstreamDevice(IotDeviceDownstreamReqVO downstreamReqVO) {
  57 + public IotDeviceMessage downstreamDevice(IotDeviceDownstreamReqVO downstreamReqVO) {
58 58 // 校验设备是否存在
59 59 IotDeviceDO device = deviceService.validateDeviceExists(downstreamReqVO.getId());
60 60 // TODO 芋艿:父设备的处理
... ... @@ -62,31 +62,28 @@ public class IotDeviceDownstreamServiceImpl implements IotDeviceDownstreamServic
62 62  
63 63 // 服务调用
64 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 68 if (Objects.equals(downstreamReqVO.getType(), IotDeviceMessageTypeEnum.PROPERTY.getType())) {
70 69 // 属性设置
71 70 if (Objects.equals(downstreamReqVO.getIdentifier(),
72 71 IotDeviceMessageIdentifierEnum.PROPERTY_SET.getIdentifier())) {
73   - setDeviceProperty(downstreamReqVO, device, parentDevice);
74   - return;
  72 + return setDeviceProperty(downstreamReqVO, device, parentDevice);
75 73 }
76 74 // 属性设置
77 75 if (Objects.equals(downstreamReqVO.getIdentifier(),
78 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 81 if (Objects.equals(downstreamReqVO.getType(), IotDeviceMessageTypeEnum.CONFIG.getType())
85 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 85 // TODO 芋艿:ota 升级
  86 + return null;
90 87 }
91 88  
92 89 /**
... ... @@ -95,9 +92,10 @@ public class IotDeviceDownstreamServiceImpl implements IotDeviceDownstreamServic
95 92 * @param downstreamReqVO 下行请求
96 93 * @param device 设备
97 94 * @param parentDevice 父设备
  95 + * @return 下发消息
98 96 */
99 97 @SuppressWarnings("unchecked")
100   - private void invokeDeviceService(IotDeviceDownstreamReqVO downstreamReqVO,
  98 + private IotDeviceMessage invokeDeviceService(IotDeviceDownstreamReqVO downstreamReqVO,
101 99 IotDeviceDO device, IotDeviceDO parentDevice) {
102 100 // 1. 参数校验
103 101 if (!(downstreamReqVO.getData() instanceof Map<?, ?>)) {
... ... @@ -124,6 +122,7 @@ public class IotDeviceDownstreamServiceImpl implements IotDeviceDownstreamServic
124 122 device.getDeviceKey(), reqDTO, result);
125 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 131 * @param downstreamReqVO 下行请求
133 132 * @param device 设备
134 133 * @param parentDevice 父设备
  134 + * @return 下发消息
135 135 */
136 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 139 // 1. 参数校验
140 140 if (!(downstreamReqVO.getData() instanceof Map<?, ?>)) {
141 141 throw new ServiceException(BAD_REQUEST.getCode(), "data 不是 Map 类型");
... ... @@ -162,6 +162,7 @@ public class IotDeviceDownstreamServiceImpl implements IotDeviceDownstreamServic
162 162 device.getDeviceKey(), reqDTO, result);
163 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 171 * @param downstreamReqVO 下行请求
171 172 * @param device 设备
172 173 * @param parentDevice 父设备
  174 + * @return 下发消息
173 175 */
174 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 179 // 1. 参数校验
178 180 if (!(downstreamReqVO.getData() instanceof List<?>)) {
179 181 throw new ServiceException(BAD_REQUEST.getCode(), "data 不是 List 类型");
... ... @@ -200,6 +202,7 @@ public class IotDeviceDownstreamServiceImpl implements IotDeviceDownstreamServic
200 202 device.getDeviceKey(), reqDTO, result);
201 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 211 * @param downstreamReqVO 下行请求
209 212 * @param device 设备
210 213 * @param parentDevice 父设备
  214 + * @return 下发消息
211 215 */
212 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 219 // 1. 参数转换,无需校验
216 220 Map<String, Object> config = JsonUtils.parseObject(device.getConfig(), Map.class);
217 221  
... ... @@ -235,6 +239,7 @@ public class IotDeviceDownstreamServiceImpl implements IotDeviceDownstreamServic
235 239 device.getDeviceKey(), reqDTO, result);
236 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 280 private void sendDeviceMessage(IotDeviceMessage message, IotDeviceDO device, Integer code) {
276 281 // 1. 完善消息
277 282 message.setProductKey(device.getProductKey()).setDeviceName(device.getDeviceName())
278   - .setDeviceKey(device.getDeviceKey());
  283 + .setDeviceKey(device.getDeviceKey())
  284 + .setTenantId(device.getTenantId());
279 285 Assert.notNull(message.getRequestId(), "requestId 不能为空");
280 286 if (message.getReportTime() == null) {
281 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 98 updateReqDTO.getProductKey(), updateReqDTO.getDeviceName());
99 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 124 @Override
... ... @@ -174,7 +175,8 @@ public class IotDeviceUpstreamServiceImpl implements IotDeviceUpstreamService {
174 175  
175 176 private void sendDeviceMessage(IotDeviceMessage message, IotDeviceDO device) {
176 177 // 1. 完善消息
177   - message.setDeviceKey(device.getDeviceKey());
  178 + message.setDeviceKey(device.getDeviceKey())
  179 + .setTenantId(device.getTenantId());
178 180 if (StrUtil.isEmpty(message.getRequestId())) {
179 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 1 package cn.iocoder.yudao.module.iot.service.rule;
2 2  
3 3 import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotRuleSceneDO;
  4 +import cn.iocoder.yudao.module.iot.enums.rule.IotRuleSceneTriggerTypeEnum;
4 5 import cn.iocoder.yudao.module.iot.mq.message.IotDeviceMessage;
5 6  
6 7 import java.util.List;
... ... @@ -22,10 +23,12 @@ public interface IotRuleSceneService {
22 23 List<IotRuleSceneDO> getRuleSceneListByProductKeyAndDeviceNameFromCache(String productKey, String deviceName);
23 24  
24 25 /**
25   - * 执行规则场景
  26 + * 基于 {@link IotRuleSceneTriggerTypeEnum#DEVICE} 场景,执行规则场景
26 27 *
27 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 11 import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
12 12 import cn.iocoder.yudao.framework.common.util.spring.SpringExpressionUtils;
13 13 import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore;
  14 +import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils;
14 15 import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotRuleSceneDO;
15 16 import cn.iocoder.yudao.module.iot.dal.mysql.rule.IotRuleSceneMapper;
16 17 import cn.iocoder.yudao.module.iot.enums.device.IotDeviceMessageIdentifierEnum;
17 18 import cn.iocoder.yudao.module.iot.enums.device.IotDeviceMessageTypeEnum;
  19 +import cn.iocoder.yudao.module.iot.enums.rule.IotRuleSceneActionTypeEnum;
18 20 import cn.iocoder.yudao.module.iot.enums.rule.IotRuleSceneTriggerConditionParameterOperatorEnum;
19 21 import cn.iocoder.yudao.module.iot.enums.rule.IotRuleSceneTriggerTypeEnum;
20 22 import cn.iocoder.yudao.module.iot.mq.message.IotDeviceMessage;
  23 +import cn.iocoder.yudao.module.iot.service.rule.action.IotRuleSceneAction;
21 24 import jakarta.annotation.Resource;
22 25 import lombok.extern.slf4j.Slf4j;
23 26 import org.springframework.stereotype.Service;
... ... @@ -43,6 +46,9 @@ public class IotRuleSceneServiceImpl implements IotRuleSceneService {
43 46 @Resource
44 47 private IotRuleSceneMapper ruleSceneMapper;
45 48  
  49 + @Resource
  50 + private List<IotRuleSceneAction> ruleSceneActions;
  51 +
46 52 // TODO 芋艿,缓存待实现
47 53 @Override
48 54 @TenantIgnore // 忽略租户隔离:因为 IotRuleSceneMessageHandler 调用时,一般未传递租户,所以需要忽略
... ... @@ -50,7 +56,7 @@ public class IotRuleSceneServiceImpl implements IotRuleSceneService {
50 56 if (true) {
51 57 IotRuleSceneDO ruleScene01 = new IotRuleSceneDO();
52 58 ruleScene01.setTriggers(CollUtil.newArrayList());
53   - IotRuleSceneDO.Trigger trigger01 = new IotRuleSceneDO.Trigger();
  59 + IotRuleSceneDO.TriggerConfig trigger01 = new IotRuleSceneDO.TriggerConfig();
54 60 trigger01.setType(IotRuleSceneTriggerTypeEnum.DEVICE.getType());
55 61 trigger01.setConditions(CollUtil.newArrayList());
56 62 // 属性
... ... @@ -120,7 +126,7 @@ public class IotRuleSceneServiceImpl implements IotRuleSceneService {
120 126 condition02.setIdentifier(IotDeviceMessageIdentifierEnum.STATE_ONLINE.getIdentifier());
121 127 condition02.setParameters(CollUtil.newArrayList());
122 128 trigger01.getConditions().add(condition02);
123   - // TODO 芋艿:事件
  129 + // 事件
124 130 IotRuleSceneDO.TriggerCondition condition03 = new IotRuleSceneDO.TriggerCondition();
125 131 condition03.setType(IotDeviceMessageTypeEnum.EVENT.getType());
126 132 condition03.setIdentifier("xxx");
... ... @@ -131,13 +137,28 @@ public class IotRuleSceneServiceImpl implements IotRuleSceneService {
131 137 parameter030.setValue("1");
132 138 trigger01.getConditions().add(condition03);
133 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 155 return ListUtil.toList(ruleScene01);
135 156 }
136 157  
137 158 List<IotRuleSceneDO> list = ruleSceneMapper.selectList();
138 159 // TODO @芋艿:需要考虑开启状态
139 160 return filterList(list, ruleScene -> {
140   - for (IotRuleSceneDO.Trigger trigger : ruleScene.getTriggers()) {
  161 + for (IotRuleSceneDO.TriggerConfig trigger : ruleScene.getTriggers()) {
141 162 if (ObjUtil.notEqual(trigger.getProductKey(), productKey)) {
142 163 continue;
143 164 }
... ... @@ -151,12 +172,17 @@ public class IotRuleSceneServiceImpl implements IotRuleSceneService {
151 172 }
152 173  
153 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 202  
177 203 // 2. 匹配 trigger 触发器的条件
178 204 return filterList(ruleScenes, ruleScene -> {
179   - for (IotRuleSceneDO.Trigger trigger : ruleScene.getTriggers()) {
  205 + for (IotRuleSceneDO.TriggerConfig trigger : ruleScene.getTriggers()) {
180 206 // 2.1 非设备触发,不匹配
181 207 if (ObjUtil.notEqual(trigger.getType(), IotRuleSceneTriggerTypeEnum.DEVICE.getType())) {
182 208 return false;
... ... @@ -219,7 +245,7 @@ public class IotRuleSceneServiceImpl implements IotRuleSceneService {
219 245 */
220 246 @SuppressWarnings({"unchecked", "DataFlowIssue"})
221 247 private boolean isTriggerConditionParameterMatched(IotDeviceMessage message, IotRuleSceneDO.TriggerConditionParameter parameter,
222   - IotRuleSceneDO ruleScene, IotRuleSceneDO.Trigger trigger) {
  248 + IotRuleSceneDO ruleScene, IotRuleSceneDO.TriggerConfig trigger) {
223 249 // 1.1 校验操作符是否合法
224 250 IotRuleSceneTriggerConditionParameterOperatorEnum operator =
225 251 IotRuleSceneTriggerConditionParameterOperatorEnum.operatorOf(parameter.getOperator());
... ... @@ -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 6 import cn.hutool.extra.spring.SpringUtil;
7 7 import cn.iocoder.yudao.framework.common.pojo.PageResult;
8 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 10 import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.ThingModelEvent;
11 11 import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.ThingModelParam;
12 12 import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.ThingModelService;
... ... @@ -135,13 +135,9 @@ public class IotThingModelServiceImpl implements IotThingModelService {
135 135 }
136 136  
137 137 @Override
138   - public List<IotThingModelDO> getThingModelListByProductKeyFromCache(String productKey) {
139   - // 保证在 @CacheEvict 之前,忽略租户
140   - return TenantUtils.executeIgnore(() -> getSelf().getThingModelListByProductKeyFromCache0(productKey));
141   - }
142   -
143 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 141 return thingModelMapper.selectListByProductKey(productKey);
146 142 }
147 143  
... ... @@ -354,8 +350,8 @@ public class IotThingModelServiceImpl implements IotThingModelService {
354 350 }
355 351  
356 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 357 @CacheEvict(value = RedisKeyConstants.THING_MODEL_LIST, key = "#productKey")
... ...
yudao-server/src/main/resources/application.yaml
... ... @@ -311,6 +311,8 @@ yudao:
311 311 - mail_account
312 312 - mail_template
313 313 - sms_template
  314 + - iot:device
  315 + - iot:thing_model_list
314 316 sms-code: # 短信验证码相关的配置项
315 317 expire-times: 10m
316 318 send-frequency: 1m
... ...