Commit f88d30f1039757870225e687a3b8a434acb6238c

Authored by 芋道源码
Committed by Gitee
2 parents 50ac2ca5 a328dcf1

!1403 perf:【IoT 物联网】场景规则匹配优化

Merge pull request !1403 from puhui999/feature/iot
Showing 43 changed files with 2209 additions and 966 deletions
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceController.java
@@ -132,7 +132,7 @@ public class IotDeviceController { @@ -132,7 +132,7 @@ public class IotDeviceController {
132 List<IotDeviceDO> list = deviceService.getDeviceListByCondition(deviceType, productId); 132 List<IotDeviceDO> list = deviceService.getDeviceListByCondition(deviceType, productId);
133 return success(convertList(list, device -> // 只返回 id、name、productId 字段 133 return success(convertList(list, device -> // 只返回 id、name、productId 字段
134 new IotDeviceRespVO().setId(device.getId()).setDeviceName(device.getDeviceName()) 134 new IotDeviceRespVO().setId(device.getId()).setDeviceName(device.getDeviceName())
135 - .setProductId(device.getProductId()))); 135 + .setProductId(device.getProductId()).setState(device.getState())));
136 } 136 }
137 137
138 @PostMapping("/import") 138 @PostMapping("/import")
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/IotRuleSceneController.java renamed to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/IotSceneRuleController.java
@@ -4,12 +4,12 @@ import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; @@ -4,12 +4,12 @@ import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
4 import cn.iocoder.yudao.framework.common.pojo.CommonResult; 4 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
5 import cn.iocoder.yudao.framework.common.pojo.PageResult; 5 import cn.iocoder.yudao.framework.common.pojo.PageResult;
6 import cn.iocoder.yudao.framework.common.util.object.BeanUtils; 6 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
7 -import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.scene.IotRuleScenePageReqVO;  
8 -import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.scene.IotRuleSceneRespVO;  
9 -import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.scene.IotRuleSceneSaveReqVO;  
10 -import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.scene.IotRuleSceneUpdateStatusReqVO; 7 +import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.scene.IotSceneRulePageReqVO;
  8 +import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.scene.IotSceneRuleRespVO;
  9 +import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.scene.IotSceneRuleSaveReqVO;
  10 +import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.scene.IotSceneRuleUpdateStatusReqVO;
11 import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotSceneRuleDO; 11 import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotSceneRuleDO;
12 -import cn.iocoder.yudao.module.iot.service.rule.scene.IotRuleSceneService; 12 +import cn.iocoder.yudao.module.iot.service.rule.scene.IotSceneRuleService;
13 import io.swagger.v3.oas.annotations.Operation; 13 import io.swagger.v3.oas.annotations.Operation;
14 import io.swagger.v3.oas.annotations.Parameter; 14 import io.swagger.v3.oas.annotations.Parameter;
15 import io.swagger.v3.oas.annotations.tags.Tag; 15 import io.swagger.v3.oas.annotations.tags.Tag;
@@ -24,71 +24,70 @@ import java.util.List; @@ -24,71 +24,70 @@ import java.util.List;
24 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; 24 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
25 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; 25 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
26 26
27 -// TODO @puhui999:SceneRule 方法名,类名等;  
28 @Tag(name = "管理后台 - IoT 场景联动") 27 @Tag(name = "管理后台 - IoT 场景联动")
29 @RestController 28 @RestController
30 -@RequestMapping("/iot/rule-scene") 29 +@RequestMapping("/iot/scene-rule")
31 @Validated 30 @Validated
32 -public class IotRuleSceneController { 31 +public class IotSceneRuleController {
33 32
34 @Resource 33 @Resource
35 - private IotRuleSceneService ruleSceneService; 34 + private IotSceneRuleService sceneRuleService;
36 35
37 @PostMapping("/create") 36 @PostMapping("/create")
38 @Operation(summary = "创建场景联动") 37 @Operation(summary = "创建场景联动")
39 - @PreAuthorize("@ss.hasPermission('iot:rule-scene:create')")  
40 - public CommonResult<Long> createRuleScene(@Valid @RequestBody IotRuleSceneSaveReqVO createReqVO) {  
41 - return success(ruleSceneService.createRuleScene(createReqVO)); 38 + @PreAuthorize("@ss.hasPermission('iot:scene-rule:create')")
  39 + public CommonResult<Long> createSceneRule(@Valid @RequestBody IotSceneRuleSaveReqVO createReqVO) {
  40 + return success(sceneRuleService.createSceneRule(createReqVO));
42 } 41 }
43 42
44 @PutMapping("/update") 43 @PutMapping("/update")
45 @Operation(summary = "更新场景联动") 44 @Operation(summary = "更新场景联动")
46 - @PreAuthorize("@ss.hasPermission('iot:rule-scene:update')")  
47 - public CommonResult<Boolean> updateRuleScene(@Valid @RequestBody IotRuleSceneSaveReqVO updateReqVO) {  
48 - ruleSceneService.updateRuleScene(updateReqVO); 45 + @PreAuthorize("@ss.hasPermission('iot:scene-rule:update')")
  46 + public CommonResult<Boolean> updateSceneRule(@Valid @RequestBody IotSceneRuleSaveReqVO updateReqVO) {
  47 + sceneRuleService.updateSceneRule(updateReqVO);
49 return success(true); 48 return success(true);
50 } 49 }
51 50
52 @PutMapping("/update-status") 51 @PutMapping("/update-status")
53 @Operation(summary = "更新场景联动状态") 52 @Operation(summary = "更新场景联动状态")
54 - @PreAuthorize("@ss.hasPermission('iot:rule-scene:update')")  
55 - public CommonResult<Boolean> updateRuleSceneStatus(@Valid @RequestBody IotRuleSceneUpdateStatusReqVO updateReqVO) {  
56 - ruleSceneService.updateRuleSceneStatus(updateReqVO.getId(), updateReqVO.getStatus()); 53 + @PreAuthorize("@ss.hasPermission('iot:scene-rule:update')")
  54 + public CommonResult<Boolean> updateSceneRuleStatus(@Valid @RequestBody IotSceneRuleUpdateStatusReqVO updateReqVO) {
  55 + sceneRuleService.updateSceneRuleStatus(updateReqVO.getId(), updateReqVO.getStatus());
57 return success(true); 56 return success(true);
58 } 57 }
59 58
60 @DeleteMapping("/delete") 59 @DeleteMapping("/delete")
61 @Operation(summary = "删除场景联动") 60 @Operation(summary = "删除场景联动")
62 @Parameter(name = "id", description = "编号", required = true) 61 @Parameter(name = "id", description = "编号", required = true)
63 - @PreAuthorize("@ss.hasPermission('iot:rule-scene:delete')")  
64 - public CommonResult<Boolean> deleteRuleScene(@RequestParam("id") Long id) {  
65 - ruleSceneService.deleteRuleScene(id); 62 + @PreAuthorize("@ss.hasPermission('iot:scene-rule:delete')")
  63 + public CommonResult<Boolean> deleteSceneRule(@RequestParam("id") Long id) {
  64 + sceneRuleService.deleteSceneRule(id);
66 return success(true); 65 return success(true);
67 } 66 }
68 67
69 @GetMapping("/get") 68 @GetMapping("/get")
70 @Operation(summary = "获得场景联动") 69 @Operation(summary = "获得场景联动")
71 @Parameter(name = "id", description = "编号", required = true, example = "1024") 70 @Parameter(name = "id", description = "编号", required = true, example = "1024")
72 - @PreAuthorize("@ss.hasPermission('iot:rule-scene:query')")  
73 - public CommonResult<IotRuleSceneRespVO> getRuleScene(@RequestParam("id") Long id) {  
74 - IotSceneRuleDO ruleScene = ruleSceneService.getRuleScene(id);  
75 - return success(BeanUtils.toBean(ruleScene, IotRuleSceneRespVO.class)); 71 + @PreAuthorize("@ss.hasPermission('iot:scene-rule:query')")
  72 + public CommonResult<IotSceneRuleRespVO> getSceneRule(@RequestParam("id") Long id) {
  73 + IotSceneRuleDO sceneRule = sceneRuleService.getSceneRule(id);
  74 + return success(BeanUtils.toBean(sceneRule, IotSceneRuleRespVO.class));
76 } 75 }
77 76
78 @GetMapping("/page") 77 @GetMapping("/page")
79 @Operation(summary = "获得场景联动分页") 78 @Operation(summary = "获得场景联动分页")
80 - @PreAuthorize("@ss.hasPermission('iot:rule-scene:query')")  
81 - public CommonResult<PageResult<IotRuleSceneRespVO>> getRuleScenePage(@Valid IotRuleScenePageReqVO pageReqVO) {  
82 - PageResult<IotSceneRuleDO> pageResult = ruleSceneService.getRuleScenePage(pageReqVO);  
83 - return success(BeanUtils.toBean(pageResult, IotRuleSceneRespVO.class)); 79 + @PreAuthorize("@ss.hasPermission('iot:scene-rule:query')")
  80 + public CommonResult<PageResult<IotSceneRuleRespVO>> getSceneRulePage(@Valid IotSceneRulePageReqVO pageReqVO) {
  81 + PageResult<IotSceneRuleDO> pageResult = sceneRuleService.getSceneRulePage(pageReqVO);
  82 + return success(BeanUtils.toBean(pageResult, IotSceneRuleRespVO.class));
84 } 83 }
85 84
86 @GetMapping("/simple-list") 85 @GetMapping("/simple-list")
87 @Operation(summary = "获取场景联动的精简信息列表", description = "主要用于前端的下拉选项") 86 @Operation(summary = "获取场景联动的精简信息列表", description = "主要用于前端的下拉选项")
88 - public CommonResult<List<IotRuleSceneRespVO>> getRuleSceneSimpleList() {  
89 - List<IotSceneRuleDO> list = ruleSceneService.getRuleSceneListByStatus(CommonStatusEnum.ENABLE.getStatus()); 87 + public CommonResult<List<IotSceneRuleRespVO>> getSceneRuleSimpleList() {
  88 + List<IotSceneRuleDO> list = sceneRuleService.getSceneRuleListByStatus(CommonStatusEnum.ENABLE.getStatus());
90 return success(convertList(list, scene -> // 只返回 id、name 字段 89 return success(convertList(list, scene -> // 只返回 id、name 字段
91 - new IotRuleSceneRespVO().setId(scene.getId()).setName(scene.getName()))); 90 + new IotSceneRuleRespVO().setId(scene.getId()).setName(scene.getName())));
92 } 91 }
93 92
94 } 93 }
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/scene/IotRuleScenePageReqVO.java renamed to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/scene/IotSceneRulePageReqVO.java
@@ -17,7 +17,7 @@ import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_ @@ -17,7 +17,7 @@ import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_
17 @Data 17 @Data
18 @EqualsAndHashCode(callSuper = true) 18 @EqualsAndHashCode(callSuper = true)
19 @ToString(callSuper = true) 19 @ToString(callSuper = true)
20 -public class IotRuleScenePageReqVO extends PageParam { 20 +public class IotSceneRulePageReqVO extends PageParam {
21 21
22 @Schema(description = "场景名称", example = "赵六") 22 @Schema(description = "场景名称", example = "赵六")
23 private String name; 23 private String name;
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/scene/IotRuleSceneRespVO.java renamed to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/scene/IotSceneRuleRespVO.java
@@ -9,7 +9,7 @@ import java.util.List; @@ -9,7 +9,7 @@ import java.util.List;
9 9
10 @Schema(description = "管理后台 - IoT 场景联动 Response VO") 10 @Schema(description = "管理后台 - IoT 场景联动 Response VO")
11 @Data 11 @Data
12 -public class IotRuleSceneRespVO { 12 +public class IotSceneRuleRespVO {
13 13
14 @Schema(description = "场景编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "15865") 14 @Schema(description = "场景编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "15865")
15 private Long id; 15 private Long id;
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/scene/IotRuleSceneSaveReqVO.java renamed to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/scene/IotSceneRuleSaveReqVO.java
@@ -12,7 +12,7 @@ import java.util.List; @@ -12,7 +12,7 @@ import java.util.List;
12 12
13 @Schema(description = "管理后台 - IoT 场景联动新增/修改 Request VO") 13 @Schema(description = "管理后台 - IoT 场景联动新增/修改 Request VO")
14 @Data 14 @Data
15 -public class IotRuleSceneSaveReqVO { 15 +public class IotSceneRuleSaveReqVO {
16 16
17 @Schema(description = "场景编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "15865") 17 @Schema(description = "场景编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "15865")
18 private Long id; 18 private Long id;
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/scene/IotRuleSceneUpdateStatusReqVO.java renamed to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/scene/IotSceneRuleUpdateStatusReqVO.java
@@ -8,7 +8,7 @@ import lombok.Data; @@ -8,7 +8,7 @@ import lombok.Data;
8 8
9 @Schema(description = "管理后台 - IoT 场景联动更新状态 Request VO") 9 @Schema(description = "管理后台 - IoT 场景联动更新状态 Request VO")
10 @Data 10 @Data
11 -public class IotRuleSceneUpdateStatusReqVO { 11 +public class IotSceneRuleUpdateStatusReqVO {
12 12
13 @Schema(description = "场景联动编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") 13 @Schema(description = "场景联动编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
14 @NotNull(message = "场景联动编号不能为空") 14 @NotNull(message = "场景联动编号不能为空")
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/IotSceneRuleDO.java
@@ -7,10 +7,10 @@ import cn.iocoder.yudao.module.iot.dal.dataobject.alert.IotAlertConfigDO; @@ -7,10 +7,10 @@ import cn.iocoder.yudao.module.iot.dal.dataobject.alert.IotAlertConfigDO;
7 import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; 7 import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO;
8 import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; 8 import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO;
9 import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotThingModelDO; 9 import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotThingModelDO;
10 -import cn.iocoder.yudao.module.iot.enums.rule.IotRuleSceneActionTypeEnum;  
11 -import cn.iocoder.yudao.module.iot.enums.rule.IotRuleSceneConditionOperatorEnum;  
12 -import cn.iocoder.yudao.module.iot.enums.rule.IotRuleSceneConditionTypeEnum;  
13 -import cn.iocoder.yudao.module.iot.enums.rule.IotRuleSceneTriggerTypeEnum; 10 +import cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleActionTypeEnum;
  11 +import cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleConditionOperatorEnum;
  12 +import cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleConditionTypeEnum;
  13 +import cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleTriggerTypeEnum;
14 import com.baomidou.mybatisplus.annotation.KeySequence; 14 import com.baomidou.mybatisplus.annotation.KeySequence;
15 import com.baomidou.mybatisplus.annotation.TableField; 15 import com.baomidou.mybatisplus.annotation.TableField;
16 import com.baomidou.mybatisplus.annotation.TableId; 16 import com.baomidou.mybatisplus.annotation.TableId;
@@ -79,13 +79,13 @@ public class IotSceneRuleDO extends TenantBaseDO { @@ -79,13 +79,13 @@ public class IotSceneRuleDO extends TenantBaseDO {
79 /** 79 /**
80 * 场景事件类型 80 * 场景事件类型
81 * 81 *
82 - * 枚举 {@link IotRuleSceneTriggerTypeEnum}  
83 - * 1. {@link IotRuleSceneTriggerTypeEnum#DEVICE_STATE_UPDATE} 时,operator 非空,并且 value 为在线状态  
84 - * 2. {@link IotRuleSceneTriggerTypeEnum#DEVICE_PROPERTY_POST}  
85 - * {@link IotRuleSceneTriggerTypeEnum#DEVICE_EVENT_POST} 时,identifier、operator 非空,并且 value 为属性值  
86 - * 3. {@link IotRuleSceneTriggerTypeEnum#DEVICE_EVENT_POST}  
87 - * {@link IotRuleSceneTriggerTypeEnum#DEVICE_SERVICE_INVOKE} 时,identifier 非空,但是 operator、value 为空  
88 - * 4. {@link IotRuleSceneTriggerTypeEnum#TIMER} 时,conditions 非空,并且设备无关(无需 productId、deviceId 字段) 82 + * 枚举 {@link IotSceneRuleTriggerTypeEnum}
  83 + * 1. {@link IotSceneRuleTriggerTypeEnum#DEVICE_STATE_UPDATE} 时,operator 非空,并且 value 为在线状态
  84 + * 2. {@link IotSceneRuleTriggerTypeEnum#DEVICE_PROPERTY_POST}
  85 + * {@link IotSceneRuleTriggerTypeEnum#DEVICE_EVENT_POST} 时,identifier、operator 非空,并且 value 为属性值
  86 + * 3. {@link IotSceneRuleTriggerTypeEnum#DEVICE_EVENT_POST}
  87 + * {@link IotSceneRuleTriggerTypeEnum#DEVICE_SERVICE_INVOKE} 时,identifier 非空,但是 operator、value 为空
  88 + * 4. {@link IotSceneRuleTriggerTypeEnum#TIMER} 时,conditions 非空,并且设备无关(无需 productId、deviceId 字段)
89 */ 89 */
90 private Integer type; 90 private Integer type;
91 91
@@ -111,14 +111,14 @@ public class IotSceneRuleDO extends TenantBaseDO { @@ -111,14 +111,14 @@ public class IotSceneRuleDO extends TenantBaseDO {
111 /** 111 /**
112 * 操作符 112 * 操作符
113 * 113 *
114 - * 枚举 {@link IotRuleSceneConditionOperatorEnum} 114 + * 枚举 {@link IotSceneRuleConditionOperatorEnum}
115 */ 115 */
116 private String operator; 116 private String operator;
117 /** 117 /**
118 * 参数(属性值、在线状态) 118 * 参数(属性值、在线状态)
119 * <p> 119 * <p>
120 * 如果有多个值,则使用 "," 分隔,类似 "1,2,3"。 120 * 如果有多个值,则使用 "," 分隔,类似 "1,2,3"。
121 - * 例如说,{@link IotRuleSceneConditionOperatorEnum#IN}、{@link IotRuleSceneConditionOperatorEnum#BETWEEN} 121 + * 例如说,{@link IotSceneRuleConditionOperatorEnum#IN}、{@link IotSceneRuleConditionOperatorEnum#BETWEEN}
122 */ 122 */
123 private String value; 123 private String value;
124 124
@@ -148,10 +148,10 @@ public class IotSceneRuleDO extends TenantBaseDO { @@ -148,10 +148,10 @@ public class IotSceneRuleDO extends TenantBaseDO {
148 /** 148 /**
149 * 触发条件类型 149 * 触发条件类型
150 * 150 *
151 - * 枚举 {@link IotRuleSceneConditionTypeEnum}  
152 - * 1. {@link IotRuleSceneConditionTypeEnum#DEVICE_STATE} 时,operator 非空,并且 value 为在线状态  
153 - * 2. {@link IotRuleSceneConditionTypeEnum#DEVICE_PROPERTY} 时,identifier、operator 非空,并且 value 为属性值  
154 - * 3. {@link IotRuleSceneConditionTypeEnum#CURRENT_TIME} 时,operator 非空(使用 DATE_TIME_ 和 TIME_ 部分),并且 value 非空 151 + * 枚举 {@link IotSceneRuleConditionTypeEnum}
  152 + * 1. {@link IotSceneRuleConditionTypeEnum#DEVICE_STATE} 时,operator 非空,并且 value 为在线状态
  153 + * 2. {@link IotSceneRuleConditionTypeEnum#DEVICE_PROPERTY} 时,identifier、operator 非空,并且 value 为属性值
  154 + * 3. {@link IotSceneRuleConditionTypeEnum#CURRENT_TIME} 时,operator 非空(使用 DATE_TIME_ 和 TIME_ 部分),并且 value 非空
155 */ 155 */
156 private Integer type; 156 private Integer type;
157 157
@@ -176,14 +176,14 @@ public class IotSceneRuleDO extends TenantBaseDO { @@ -176,14 +176,14 @@ public class IotSceneRuleDO extends TenantBaseDO {
176 /** 176 /**
177 * 操作符 177 * 操作符
178 * 178 *
179 - * 枚举 {@link IotRuleSceneConditionOperatorEnum} 179 + * 枚举 {@link IotSceneRuleConditionOperatorEnum}
180 */ 180 */
181 private String operator; 181 private String operator;
182 /** 182 /**
183 * 参数 183 * 参数
184 * 184 *
185 * 如果有多个值,则使用 "," 分隔,类似 "1,2,3"。 185 * 如果有多个值,则使用 "," 分隔,类似 "1,2,3"。
186 - * 例如说,{@link IotRuleSceneConditionOperatorEnum#IN}、{@link IotRuleSceneConditionOperatorEnum#BETWEEN} 186 + * 例如说,{@link IotSceneRuleConditionOperatorEnum#IN}、{@link IotSceneRuleConditionOperatorEnum#BETWEEN}
187 */ 187 */
188 private String param; 188 private String param;
189 189
@@ -198,11 +198,11 @@ public class IotSceneRuleDO extends TenantBaseDO { @@ -198,11 +198,11 @@ public class IotSceneRuleDO extends TenantBaseDO {
198 /** 198 /**
199 * 执行类型 199 * 执行类型
200 * 200 *
201 - * 枚举 {@link IotRuleSceneActionTypeEnum}  
202 - * 1. {@link IotRuleSceneActionTypeEnum#DEVICE_PROPERTY_SET} 时,params 非空  
203 - * {@link IotRuleSceneActionTypeEnum#DEVICE_SERVICE_INVOKE} 时,params 非空  
204 - * 2. {@link IotRuleSceneActionTypeEnum#ALERT_TRIGGER} 时,alertConfigId 为空,因为是 {@link IotAlertConfigDO} 里面关联它  
205 - * 3. {@link IotRuleSceneActionTypeEnum#ALERT_RECOVER} 时,alertConfigId 非空 201 + * 枚举 {@link IotSceneRuleActionTypeEnum}
  202 + * 1. {@link IotSceneRuleActionTypeEnum#DEVICE_PROPERTY_SET} 时,params 非空
  203 + * {@link IotSceneRuleActionTypeEnum#DEVICE_SERVICE_INVOKE} 时,params 非空
  204 + * 2. {@link IotSceneRuleActionTypeEnum#ALERT_TRIGGER} 时,alertConfigId 为空,因为是 {@link IotAlertConfigDO} 里面关联它
  205 + * 3. {@link IotSceneRuleActionTypeEnum#ALERT_RECOVER} 时,alertConfigId 非空
206 */ 206 */
207 private Integer type; 207 private Integer type;
208 208
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/config/IotAbstractDataSinkConfig.java
@@ -18,7 +18,7 @@ import lombok.Data; @@ -18,7 +18,7 @@ import lombok.Data;
18 @JsonSubTypes({ 18 @JsonSubTypes({
19 @JsonSubTypes.Type(value = IotDataSinkHttpConfig.class, name = "1"), 19 @JsonSubTypes.Type(value = IotDataSinkHttpConfig.class, name = "1"),
20 @JsonSubTypes.Type(value = IotDataSinkMqttConfig.class, name = "10"), 20 @JsonSubTypes.Type(value = IotDataSinkMqttConfig.class, name = "10"),
21 - @JsonSubTypes.Type(value = IotDataSinkRedisStreamConfig.class, name = "21"), 21 + @JsonSubTypes.Type(value = IotDataSinkRedisConfig.class, name = "21"),
22 @JsonSubTypes.Type(value = IotDataSinkRocketMQConfig.class, name = "30"), 22 @JsonSubTypes.Type(value = IotDataSinkRocketMQConfig.class, name = "30"),
23 @JsonSubTypes.Type(value = IotDataSinkRabbitMQConfig.class, name = "31"), 23 @JsonSubTypes.Type(value = IotDataSinkRabbitMQConfig.class, name = "31"),
24 @JsonSubTypes.Type(value = IotDataSinkKafkaConfig.class, name = "32"), 24 @JsonSubTypes.Type(value = IotDataSinkKafkaConfig.class, name = "32"),
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/config/IotDataSinkRedisStreamConfig.java renamed to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/config/IotDataSinkRedisConfig.java
1 package cn.iocoder.yudao.module.iot.dal.dataobject.rule.config; 1 package cn.iocoder.yudao.module.iot.dal.dataobject.rule.config;
2 2
  3 +import cn.iocoder.yudao.framework.common.validation.InEnum;
  4 +import cn.iocoder.yudao.module.iot.enums.rule.IotRedisDataStructureEnum;
3 import lombok.Data; 5 import lombok.Data;
4 6
5 /** 7 /**
6 - * IoT Redis Stream 配置 {@link IotAbstractDataSinkConfig} 实现类 8 + * IoT Redis 配置 {@link IotAbstractDataSinkConfig} 实现类
7 * 9 *
8 * @author HUIHUI 10 * @author HUIHUI
9 */ 11 */
10 @Data 12 @Data
11 -public class IotDataSinkRedisStreamConfig extends IotAbstractDataSinkConfig { 13 +public class IotDataSinkRedisConfig extends IotAbstractDataSinkConfig {
12 14
13 /** 15 /**
14 * Redis 服务器地址 16 * Redis 服务器地址
@@ -28,7 +30,35 @@ public class IotDataSinkRedisStreamConfig extends IotAbstractDataSinkConfig { @@ -28,7 +30,35 @@ public class IotDataSinkRedisStreamConfig extends IotAbstractDataSinkConfig {
28 private Integer database; 30 private Integer database;
29 31
30 /** 32 /**
31 - * 主题 33 + * Redis 数据结构类型
  34 + * <p>
  35 + * 枚举 {@link IotRedisDataStructureEnum}
  36 + */
  37 + @InEnum(IotRedisDataStructureEnum.class)
  38 + private Integer dataStructure;
  39 +
  40 + /**
  41 + * 主题/键名
  42 + * <p>
  43 + * 对于不同的数据结构:
  44 + * - Stream: 流的键名
  45 + * - Hash: Hash 的键名
  46 + * - List: 列表的键名
  47 + * - Set: 集合的键名
  48 + * - ZSet: 有序集合的键名
  49 + * - String: 字符串的键名
32 */ 50 */
33 private String topic; 51 private String topic;
34 -}  
35 \ No newline at end of file 52 \ No newline at end of file
  53 +
  54 + /**
  55 + * Hash 字段名(仅当 dataStructure 为 HASH 时使用)
  56 + */
  57 + private String hashField;
  58 +
  59 + /**
  60 + * ZSet 分数字段(仅当 dataStructure 为 ZSET 时使用)
  61 + * 指定消息中哪个字段作为分数,如果不指定则使用当前时间戳
  62 + */
  63 + private String scoreField;
  64 +
  65 +}
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/rule/IotRuleSceneMapper.java renamed to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/rule/IotSceneRuleMapper.java
@@ -3,7 +3,7 @@ package cn.iocoder.yudao.module.iot.dal.mysql.rule; @@ -3,7 +3,7 @@ package cn.iocoder.yudao.module.iot.dal.mysql.rule;
3 import cn.iocoder.yudao.framework.common.pojo.PageResult; 3 import cn.iocoder.yudao.framework.common.pojo.PageResult;
4 import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; 4 import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
5 import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; 5 import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
6 -import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.scene.IotRuleScenePageReqVO; 6 +import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.scene.IotSceneRulePageReqVO;
7 import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotSceneRuleDO; 7 import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotSceneRuleDO;
8 import org.apache.ibatis.annotations.Mapper; 8 import org.apache.ibatis.annotations.Mapper;
9 9
@@ -15,9 +15,9 @@ import java.util.List; @@ -15,9 +15,9 @@ import java.util.List;
15 * @author HUIHUI 15 * @author HUIHUI
16 */ 16 */
17 @Mapper 17 @Mapper
18 -public interface IotRuleSceneMapper extends BaseMapperX<IotSceneRuleDO> { 18 +public interface IotSceneRuleMapper extends BaseMapperX<IotSceneRuleDO> {
19 19
20 - default PageResult<IotSceneRuleDO> selectPage(IotRuleScenePageReqVO reqVO) { 20 + default PageResult<IotSceneRuleDO> selectPage(IotSceneRulePageReqVO reqVO) {
21 return selectPage(reqVO, new LambdaQueryWrapperX<IotSceneRuleDO>() 21 return selectPage(reqVO, new LambdaQueryWrapperX<IotSceneRuleDO>()
22 .likeIfPresent(IotSceneRuleDO::getName, reqVO.getName()) 22 .likeIfPresent(IotSceneRuleDO::getName, reqVO.getName())
23 .likeIfPresent(IotSceneRuleDO::getDescription, reqVO.getDescription()) 23 .likeIfPresent(IotSceneRuleDO::getDescription, reqVO.getDescription())
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/enums/rule/IotDataSinkTypeEnum.java
@@ -22,7 +22,7 @@ public enum IotDataSinkTypeEnum implements ArrayValuable&lt;Integer&gt; { @@ -22,7 +22,7 @@ public enum IotDataSinkTypeEnum implements ArrayValuable&lt;Integer&gt; {
22 MQTT(10, "MQTT"), // TODO 待实现; 22 MQTT(10, "MQTT"), // TODO 待实现;
23 23
24 DATABASE(20, "Database"), // TODO @puhui999:待实现;可以简单点,对应的表名是什么,字段先固定了。 24 DATABASE(20, "Database"), // TODO @puhui999:待实现;可以简单点,对应的表名是什么,字段先固定了。
25 - REDIS_STREAM(21, "Redis Stream"), // TODO @puhui999:改成 Redis;然后枚举不同的数据结构?这样,枚举就可以是 Redis 了 25 + REDIS(21, "Redis"),
26 26
27 ROCKETMQ(30, "RocketMQ"), 27 ROCKETMQ(30, "RocketMQ"),
28 RABBITMQ(31, "RabbitMQ"), 28 RABBITMQ(31, "RabbitMQ"),
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/enums/rule/IotRedisDataStructureEnum.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 Redis 数据结构类型枚举
  11 + *
  12 + * @author HUIHUI
  13 + */
  14 +@RequiredArgsConstructor
  15 +@Getter
  16 +public enum IotRedisDataStructureEnum implements ArrayValuable<Integer> {
  17 +
  18 + STREAM(1, "Stream"),
  19 + HASH(2, "Hash"),
  20 + LIST(3, "List"),
  21 + SET(4, "Set"),
  22 + ZSET(5, "ZSet"),
  23 + STRING(6, "String");
  24 +
  25 + private final Integer type;
  26 +
  27 + private final String name;
  28 +
  29 + public static final Integer[] ARRAYS = Arrays.stream(values()).map(IotRedisDataStructureEnum::getType).toArray(Integer[]::new);
  30 +
  31 + @Override
  32 + public Integer[] array() {
  33 + return ARRAYS;
  34 + }
  35 +
  36 +}
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/enums/rule/IotRuleSceneActionTypeEnum.java renamed to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/enums/rule/IotSceneRuleActionTypeEnum.java
@@ -14,7 +14,7 @@ import java.util.Arrays; @@ -14,7 +14,7 @@ import java.util.Arrays;
14 */ 14 */
15 @RequiredArgsConstructor 15 @RequiredArgsConstructor
16 @Getter 16 @Getter
17 -public enum IotRuleSceneActionTypeEnum implements ArrayValuable<Integer> { 17 +public enum IotSceneRuleActionTypeEnum implements ArrayValuable<Integer> {
18 18
19 /** 19 /**
20 * 设备属性设置 20 * 设备属性设置
@@ -42,7 +42,7 @@ public enum IotRuleSceneActionTypeEnum implements ArrayValuable&lt;Integer&gt; { @@ -42,7 +42,7 @@ public enum IotRuleSceneActionTypeEnum implements ArrayValuable&lt;Integer&gt; {
42 42
43 private final Integer type; 43 private final Integer type;
44 44
45 - public static final Integer[] ARRAYS = Arrays.stream(values()).map(IotRuleSceneActionTypeEnum::getType).toArray(Integer[]::new); 45 + public static final Integer[] ARRAYS = Arrays.stream(values()).map(IotSceneRuleActionTypeEnum::getType).toArray(Integer[]::new);
46 46
47 @Override 47 @Override
48 public Integer[] array() { 48 public Integer[] array() {
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/enums/rule/IotRuleSceneConditionOperatorEnum.java renamed to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/enums/rule/IotSceneRuleConditionOperatorEnum.java
@@ -14,7 +14,7 @@ import java.util.Arrays; @@ -14,7 +14,7 @@ import java.util.Arrays;
14 */ 14 */
15 @RequiredArgsConstructor 15 @RequiredArgsConstructor
16 @Getter 16 @Getter
17 -public enum IotRuleSceneConditionOperatorEnum implements ArrayValuable<String> { 17 +public enum IotSceneRuleConditionOperatorEnum implements ArrayValuable<String> {
18 18
19 EQUALS("=", "#source == #value"), 19 EQUALS("=", "#source == #value"),
20 NOT_EQUALS("!=", "!(#source == #value)"), 20 NOT_EQUALS("!=", "!(#source == #value)"),
@@ -53,7 +53,7 @@ public enum IotRuleSceneConditionOperatorEnum implements ArrayValuable&lt;String&gt; { @@ -53,7 +53,7 @@ public enum IotRuleSceneConditionOperatorEnum implements ArrayValuable&lt;String&gt; {
53 private final String operator; 53 private final String operator;
54 private final String springExpression; 54 private final String springExpression;
55 55
56 - public static final String[] ARRAYS = Arrays.stream(values()).map(IotRuleSceneConditionOperatorEnum::getOperator).toArray(String[]::new); 56 + public static final String[] ARRAYS = Arrays.stream(values()).map(IotSceneRuleConditionOperatorEnum::getOperator).toArray(String[]::new);
57 57
58 /** 58 /**
59 * Spring 表达式 - 原始值 59 * Spring 表达式 - 原始值
@@ -68,7 +68,7 @@ public enum IotRuleSceneConditionOperatorEnum implements ArrayValuable&lt;String&gt; { @@ -68,7 +68,7 @@ public enum IotRuleSceneConditionOperatorEnum implements ArrayValuable&lt;String&gt; {
68 */ 68 */
69 public static final String SPRING_EXPRESSION_VALUE_LIST = "values"; 69 public static final String SPRING_EXPRESSION_VALUE_LIST = "values";
70 70
71 - public static IotRuleSceneConditionOperatorEnum operatorOf(String operator) { 71 + public static IotSceneRuleConditionOperatorEnum operatorOf(String operator) {
72 return ArrayUtil.firstMatch(item -> item.getOperator().equals(operator), values()); 72 return ArrayUtil.firstMatch(item -> item.getOperator().equals(operator), values());
73 } 73 }
74 74
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/enums/rule/IotRuleSceneConditionTypeEnum.java renamed to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/enums/rule/IotSceneRuleConditionTypeEnum.java
@@ -13,7 +13,7 @@ import java.util.Arrays; @@ -13,7 +13,7 @@ import java.util.Arrays;
13 */ 13 */
14 @RequiredArgsConstructor 14 @RequiredArgsConstructor
15 @Getter 15 @Getter
16 -public enum IotRuleSceneConditionTypeEnum implements ArrayValuable<Integer> { 16 +public enum IotSceneRuleConditionTypeEnum implements ArrayValuable<Integer> {
17 17
18 DEVICE_STATE(1, "设备状态"), 18 DEVICE_STATE(1, "设备状态"),
19 DEVICE_PROPERTY(2, "设备属性"), 19 DEVICE_PROPERTY(2, "设备属性"),
@@ -25,7 +25,7 @@ public enum IotRuleSceneConditionTypeEnum implements ArrayValuable&lt;Integer&gt; { @@ -25,7 +25,7 @@ public enum IotRuleSceneConditionTypeEnum implements ArrayValuable&lt;Integer&gt; {
25 private final Integer type; 25 private final Integer type;
26 private final String name; 26 private final String name;
27 27
28 - public static final Integer[] ARRAYS = Arrays.stream(values()).map(IotRuleSceneConditionTypeEnum::getType).toArray(Integer[]::new); 28 + public static final Integer[] ARRAYS = Arrays.stream(values()).map(IotSceneRuleConditionTypeEnum::getType).toArray(Integer[]::new);
29 29
30 @Override 30 @Override
31 public Integer[] array() { 31 public Integer[] array() {
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/enums/rule/IotRuleSceneTriggerTypeEnum.java renamed to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/enums/rule/IotSceneRuleTriggerTypeEnum.java
@@ -16,10 +16,7 @@ import java.util.Arrays; @@ -16,10 +16,7 @@ import java.util.Arrays;
16 */ 16 */
17 @RequiredArgsConstructor 17 @RequiredArgsConstructor
18 @Getter 18 @Getter
19 -public enum IotRuleSceneTriggerTypeEnum implements ArrayValuable<Integer> {  
20 -  
21 - @Deprecated  
22 - DEVICE(1), // 设备触发 // TODO @puhui999:@芋艿:这个可以作废 19 +public enum IotSceneRuleTriggerTypeEnum implements ArrayValuable<Integer> {
23 20
24 // TODO @芋艿:后续“对应”部分,要 @下,等包结构梳理完; 21 // TODO @芋艿:后续“对应”部分,要 @下,等包结构梳理完;
25 /** 22 /**
@@ -56,11 +53,25 @@ public enum IotRuleSceneTriggerTypeEnum implements ArrayValuable&lt;Integer&gt; { @@ -56,11 +53,25 @@ public enum IotRuleSceneTriggerTypeEnum implements ArrayValuable&lt;Integer&gt; {
56 53
57 private final Integer type; 54 private final Integer type;
58 55
59 - public static final Integer[] ARRAYS = Arrays.stream(values()).map(IotRuleSceneTriggerTypeEnum::getType).toArray(Integer[]::new); 56 + public static final Integer[] ARRAYS = Arrays.stream(values()).map(IotSceneRuleTriggerTypeEnum::getType).toArray(Integer[]::new);
60 57
61 @Override 58 @Override
62 public Integer[] array() { 59 public Integer[] array() {
63 return ARRAYS; 60 return ARRAYS;
64 } 61 }
65 62
  63 +
  64 + /**
  65 + * 根据类型值查找触发器类型枚举
  66 + *
  67 + * @param typeValue 类型值
  68 + * @return 触发器类型枚举
  69 + */
  70 + public static IotSceneRuleTriggerTypeEnum findTriggerTypeEnum(Integer typeValue) {
  71 + return Arrays.stream(IotSceneRuleTriggerTypeEnum.values())
  72 + .filter(type -> type.getType().equals(typeValue))
  73 + .findFirst()
  74 + .orElse(null);
  75 + }
  76 +
66 } 77 }
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/job/rule/IotRuleSceneJob.java renamed to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/job/rule/IotSceneRuleJob.java
1 package cn.iocoder.yudao.module.iot.job.rule; 1 package cn.iocoder.yudao.module.iot.job.rule;
2 2
3 import cn.hutool.core.map.MapUtil; 3 import cn.hutool.core.map.MapUtil;
4 -import cn.iocoder.yudao.module.iot.enums.rule.IotRuleSceneTriggerTypeEnum;  
5 -import cn.iocoder.yudao.module.iot.service.rule.scene.IotRuleSceneService; 4 +import cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleTriggerTypeEnum;
  5 +import cn.iocoder.yudao.module.iot.service.rule.scene.IotSceneRuleService;
6 import jakarta.annotation.Resource; 6 import jakarta.annotation.Resource;
7 import lombok.extern.slf4j.Slf4j; 7 import lombok.extern.slf4j.Slf4j;
8 import org.quartz.JobExecutionContext; 8 import org.quartz.JobExecutionContext;
@@ -11,48 +11,48 @@ import org.springframework.scheduling.quartz.QuartzJobBean; @@ -11,48 +11,48 @@ import org.springframework.scheduling.quartz.QuartzJobBean;
11 import java.util.Map; 11 import java.util.Map;
12 12
13 /** 13 /**
14 - * IoT 规则场景 Job,用于执行 {@link IotRuleSceneTriggerTypeEnum#TIMER} 类型的规则场景 14 + * IoT 规则场景 Job,用于执行 {@link IotSceneRuleTriggerTypeEnum#TIMER} 类型的规则场景
15 * 15 *
16 * @author 芋道源码 16 * @author 芋道源码
17 */ 17 */
18 @Slf4j 18 @Slf4j
19 -public class IotRuleSceneJob extends QuartzJobBean { 19 +public class IotSceneRuleJob extends QuartzJobBean {
20 20
21 /** 21 /**
22 * JobData Key - 规则场景编号 22 * JobData Key - 规则场景编号
23 */ 23 */
24 - public static final String JOB_DATA_KEY_RULE_SCENE_ID = "ruleSceneId"; 24 + public static final String JOB_DATA_KEY_RULE_SCENE_ID = "sceneRuleId";
25 25
26 @Resource 26 @Resource
27 - private IotRuleSceneService ruleSceneService; 27 + private IotSceneRuleService sceneRuleService;
28 28
29 @Override 29 @Override
30 protected void executeInternal(JobExecutionContext context) { 30 protected void executeInternal(JobExecutionContext context) {
31 // 获得规则场景编号 31 // 获得规则场景编号
32 - Long ruleSceneId = context.getMergedJobDataMap().getLong(JOB_DATA_KEY_RULE_SCENE_ID); 32 + Long sceneRuleId = context.getMergedJobDataMap().getLong(JOB_DATA_KEY_RULE_SCENE_ID);
33 33
34 // 执行规则场景 34 // 执行规则场景
35 - ruleSceneService.executeRuleSceneByTimer(ruleSceneId); 35 + sceneRuleService.executeSceneRuleByTimer(sceneRuleId);
36 } 36 }
37 37
38 /** 38 /**
39 * 创建 JobData Map 39 * 创建 JobData Map
40 * 40 *
41 - * @param ruleSceneId 规则场景编号 41 + * @param sceneRuleId 规则场景编号
42 * @return JobData Map 42 * @return JobData Map
43 */ 43 */
44 - public static Map<String, Object> buildJobDataMap(Long ruleSceneId) {  
45 - return MapUtil.of(JOB_DATA_KEY_RULE_SCENE_ID, ruleSceneId); 44 + public static Map<String, Object> buildJobDataMap(Long sceneRuleId) {
  45 + return MapUtil.of(JOB_DATA_KEY_RULE_SCENE_ID, sceneRuleId);
46 } 46 }
47 47
48 /** 48 /**
49 * 创建 Job 名字 49 * 创建 Job 名字
50 * 50 *
51 - * @param ruleSceneId 规则场景编号 51 + * @param sceneRuleId 规则场景编号
52 * @return Job 名字 52 * @return Job 名字
53 */ 53 */
54 - public static String buildJobName(Long ruleSceneId) {  
55 - return String.format("%s_%d", IotRuleSceneJob.class.getSimpleName(), ruleSceneId); 54 + public static String buildJobName(Long sceneRuleId) {
  55 + return String.format("%s_%d", IotSceneRuleJob.class.getSimpleName(), sceneRuleId);
56 } 56 }
57 57
58 } 58 }
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/consumer/rule/IotRuleSceneMessageHandler.java renamed to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/consumer/rule/IotSceneRuleMessageHandler.java
@@ -3,7 +3,7 @@ package cn.iocoder.yudao.module.iot.mq.consumer.rule; @@ -3,7 +3,7 @@ package cn.iocoder.yudao.module.iot.mq.consumer.rule;
3 import cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageBus; 3 import cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageBus;
4 import cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageSubscriber; 4 import cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageSubscriber;
5 import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; 5 import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
6 -import cn.iocoder.yudao.module.iot.service.rule.scene.IotRuleSceneService; 6 +import cn.iocoder.yudao.module.iot.service.rule.scene.IotSceneRuleService;
7 import jakarta.annotation.PostConstruct; 7 import jakarta.annotation.PostConstruct;
8 import jakarta.annotation.Resource; 8 import jakarta.annotation.Resource;
9 import lombok.extern.slf4j.Slf4j; 9 import lombok.extern.slf4j.Slf4j;
@@ -17,10 +17,10 @@ import org.springframework.stereotype.Component; @@ -17,10 +17,10 @@ import org.springframework.stereotype.Component;
17 */ 17 */
18 @Component 18 @Component
19 @Slf4j 19 @Slf4j
20 -public class IotRuleSceneMessageHandler implements IotMessageSubscriber<IotDeviceMessage> { 20 +public class IotSceneRuleMessageHandler implements IotMessageSubscriber<IotDeviceMessage> {
21 21
22 @Resource 22 @Resource
23 - private IotRuleSceneService ruleSceneService; 23 + private IotSceneRuleService sceneRuleService;
24 24
25 @Resource 25 @Resource
26 private IotMessageBus messageBus; 26 private IotMessageBus messageBus;
@@ -46,7 +46,7 @@ public class IotRuleSceneMessageHandler implements IotMessageSubscriber&lt;IotDevic @@ -46,7 +46,7 @@ public class IotRuleSceneMessageHandler implements IotMessageSubscriber&lt;IotDevic
46 return; 46 return;
47 } 47 }
48 log.info("[onMessage][消息内容({})]", message); 48 log.info("[onMessage][消息内容({})]", message);
49 - ruleSceneService.executeRuleSceneByDevice(message); 49 + sceneRuleService.executeSceneRuleByDevice(message);
50 } 50 }
51 51
52 } 52 }
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/alert/IotAlertConfigServiceImpl.java
@@ -6,7 +6,7 @@ import cn.iocoder.yudao.module.iot.controller.admin.alert.vo.config.IotAlertConf @@ -6,7 +6,7 @@ import cn.iocoder.yudao.module.iot.controller.admin.alert.vo.config.IotAlertConf
6 import cn.iocoder.yudao.module.iot.controller.admin.alert.vo.config.IotAlertConfigSaveReqVO; 6 import cn.iocoder.yudao.module.iot.controller.admin.alert.vo.config.IotAlertConfigSaveReqVO;
7 import cn.iocoder.yudao.module.iot.dal.dataobject.alert.IotAlertConfigDO; 7 import cn.iocoder.yudao.module.iot.dal.dataobject.alert.IotAlertConfigDO;
8 import cn.iocoder.yudao.module.iot.dal.mysql.alert.IotAlertConfigMapper; 8 import cn.iocoder.yudao.module.iot.dal.mysql.alert.IotAlertConfigMapper;
9 -import cn.iocoder.yudao.module.iot.service.rule.scene.IotRuleSceneService; 9 +import cn.iocoder.yudao.module.iot.service.rule.scene.IotSceneRuleService;
10 import cn.iocoder.yudao.module.system.api.user.AdminUserApi; 10 import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
11 import jakarta.annotation.Resource; 11 import jakarta.annotation.Resource;
12 import org.springframework.context.annotation.Lazy; 12 import org.springframework.context.annotation.Lazy;
@@ -32,7 +32,7 @@ public class IotAlertConfigServiceImpl implements IotAlertConfigService { @@ -32,7 +32,7 @@ public class IotAlertConfigServiceImpl implements IotAlertConfigService {
32 32
33 @Resource 33 @Resource
34 @Lazy // 延迟,避免循环依赖报错 34 @Lazy // 延迟,避免循环依赖报错
35 - private IotRuleSceneService ruleSceneService; 35 + private IotSceneRuleService sceneRuleService;
36 36
37 @Resource 37 @Resource
38 private AdminUserApi adminUserApi; 38 private AdminUserApi adminUserApi;
@@ -40,7 +40,7 @@ public class IotAlertConfigServiceImpl implements IotAlertConfigService { @@ -40,7 +40,7 @@ public class IotAlertConfigServiceImpl implements IotAlertConfigService {
40 @Override 40 @Override
41 public Long createAlertConfig(IotAlertConfigSaveReqVO createReqVO) { 41 public Long createAlertConfig(IotAlertConfigSaveReqVO createReqVO) {
42 // 校验关联数据是否存在 42 // 校验关联数据是否存在
43 - ruleSceneService.validateRuleSceneList(createReqVO.getSceneRuleIds()); 43 + sceneRuleService.validateSceneRuleList(createReqVO.getSceneRuleIds());
44 adminUserApi.validateUserList(createReqVO.getReceiveUserIds()); 44 adminUserApi.validateUserList(createReqVO.getReceiveUserIds());
45 45
46 // 插入 46 // 插入
@@ -54,7 +54,7 @@ public class IotAlertConfigServiceImpl implements IotAlertConfigService { @@ -54,7 +54,7 @@ public class IotAlertConfigServiceImpl implements IotAlertConfigService {
54 // 校验存在 54 // 校验存在
55 validateAlertConfigExists(updateReqVO.getId()); 55 validateAlertConfigExists(updateReqVO.getId());
56 // 校验关联数据是否存在 56 // 校验关联数据是否存在
57 - ruleSceneService.validateRuleSceneList(updateReqVO.getSceneRuleIds()); 57 + sceneRuleService.validateSceneRuleList(updateReqVO.getSceneRuleIds());
58 adminUserApi.validateUserList(updateReqVO.getReceiveUserIds()); 58 adminUserApi.validateUserList(updateReqVO.getReceiveUserIds());
59 59
60 // 更新 60 // 更新
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/data/action/IotRedisRuleAction.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.service.rule.data.action;
  2 +
  3 +import cn.hutool.core.util.StrUtil;
  4 +import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
  5 +import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
  6 +import cn.iocoder.yudao.module.iot.dal.dataobject.rule.config.IotDataSinkRedisConfig;
  7 +import cn.iocoder.yudao.module.iot.enums.rule.IotDataSinkTypeEnum;
  8 +import cn.iocoder.yudao.module.iot.enums.rule.IotRedisDataStructureEnum;
  9 +import lombok.extern.slf4j.Slf4j;
  10 +import org.redisson.Redisson;
  11 +import org.redisson.api.RedissonClient;
  12 +import org.redisson.config.Config;
  13 +import org.redisson.config.SingleServerConfig;
  14 +import org.redisson.spring.data.connection.RedissonConnectionFactory;
  15 +import org.springframework.data.redis.connection.RedisConnectionFactory;
  16 +import org.springframework.data.redis.connection.stream.ObjectRecord;
  17 +import org.springframework.data.redis.connection.stream.StreamRecords;
  18 +import org.springframework.data.redis.core.RedisTemplate;
  19 +import org.springframework.data.redis.serializer.RedisSerializer;
  20 +import org.springframework.stereotype.Component;
  21 +
  22 +import java.util.Map;
  23 +
  24 +/**
  25 + * Redis 的 {@link IotDataRuleAction} 实现类
  26 + * 支持多种 Redis 数据结构:Stream、Hash、List、Set、ZSet、String
  27 + *
  28 + * @author HUIHUI
  29 + */
  30 +@Component
  31 +@Slf4j
  32 +public class IotRedisRuleAction extends
  33 + IotDataRuleCacheableAction<IotDataSinkRedisConfig, RedisTemplate<String, Object>> {
  34 +
  35 + @Override
  36 + public Integer getType() {
  37 + return IotDataSinkTypeEnum.REDIS.getType();
  38 + }
  39 +
  40 + @Override
  41 + public void execute(IotDeviceMessage message, IotDataSinkRedisConfig config) throws Exception {
  42 + // 1. 获取 RedisTemplate
  43 + RedisTemplate<String, Object> redisTemplate = getProducer(config);
  44 +
  45 + // 2. 根据数据结构类型执行不同的操作
  46 + String messageJson = JsonUtils.toJsonString(message);
  47 + IotRedisDataStructureEnum dataStructure = getDataStructureByType(config.getDataStructure());
  48 +
  49 + switch (dataStructure) {
  50 + case STREAM:
  51 + executeStream(redisTemplate, config, messageJson);
  52 + break;
  53 + case HASH:
  54 + executeHash(redisTemplate, config, message, messageJson);
  55 + break;
  56 + case LIST:
  57 + executeList(redisTemplate, config, messageJson);
  58 + break;
  59 + case SET:
  60 + executeSet(redisTemplate, config, messageJson);
  61 + break;
  62 + case ZSET:
  63 + executeZSet(redisTemplate, config, message, messageJson);
  64 + break;
  65 + case STRING:
  66 + executeString(redisTemplate, config, messageJson);
  67 + break;
  68 + default:
  69 + throw new IllegalArgumentException("不支持的 Redis 数据结构类型: " + dataStructure);
  70 + }
  71 +
  72 + log.info("[execute][消息发送成功] dataStructure: {}, config: {}", dataStructure.getName(), config);
  73 + }
  74 +
  75 + /**
  76 + * 执行 Stream 操作
  77 + */
  78 + private void executeStream(RedisTemplate<String, Object> redisTemplate, IotDataSinkRedisConfig config, String messageJson) {
  79 + ObjectRecord<String, ?> record = StreamRecords.newRecord()
  80 + .ofObject(messageJson).withStreamKey(config.getTopic());
  81 + redisTemplate.opsForStream().add(record);
  82 + }
  83 +
  84 + /**
  85 + * 执行 Hash 操作
  86 + */
  87 + private void executeHash(RedisTemplate<String, Object> redisTemplate, IotDataSinkRedisConfig config,
  88 + IotDeviceMessage message, String messageJson) {
  89 + String hashField = StrUtil.isNotBlank(config.getHashField()) ?
  90 + config.getHashField() : String.valueOf(message.getDeviceId());
  91 + redisTemplate.opsForHash().put(config.getTopic(), hashField, messageJson);
  92 + }
  93 +
  94 + /**
  95 + * 执行 List 操作
  96 + */
  97 + private void executeList(RedisTemplate<String, Object> redisTemplate, IotDataSinkRedisConfig config, String messageJson) {
  98 + redisTemplate.opsForList().rightPush(config.getTopic(), messageJson);
  99 + }
  100 +
  101 + /**
  102 + * 执行 Set 操作
  103 + */
  104 + private void executeSet(RedisTemplate<String, Object> redisTemplate, IotDataSinkRedisConfig config, String messageJson) {
  105 + redisTemplate.opsForSet().add(config.getTopic(), messageJson);
  106 + }
  107 +
  108 + /**
  109 + * 执行 ZSet 操作
  110 + */
  111 + private void executeZSet(RedisTemplate<String, Object> redisTemplate, IotDataSinkRedisConfig config,
  112 + IotDeviceMessage message, String messageJson) {
  113 + double score;
  114 + if (StrUtil.isNotBlank(config.getScoreField())) {
  115 + // 尝试从消息中获取分数字段
  116 + try {
  117 + Map<String, Object> messageMap = JsonUtils.parseObject(messageJson, Map.class);
  118 + Object scoreValue = messageMap.get(config.getScoreField());
  119 + score = scoreValue instanceof Number ? ((Number) scoreValue).doubleValue() : System.currentTimeMillis();
  120 + } catch (Exception e) {
  121 + score = System.currentTimeMillis();
  122 + }
  123 + } else {
  124 + // 使用当前时间戳作为分数
  125 + score = System.currentTimeMillis();
  126 + }
  127 + redisTemplate.opsForZSet().add(config.getTopic(), messageJson, score);
  128 + }
  129 +
  130 + /**
  131 + * 执行 String 操作
  132 + */
  133 + private void executeString(RedisTemplate<String, Object> redisTemplate, IotDataSinkRedisConfig config, String messageJson) {
  134 + redisTemplate.opsForValue().set(config.getTopic(), messageJson);
  135 + }
  136 +
  137 + @Override
  138 + protected RedisTemplate<String, Object> initProducer(IotDataSinkRedisConfig config) {
  139 + // 1.1 创建 Redisson 配置
  140 + Config redissonConfig = new Config();
  141 + SingleServerConfig serverConfig = redissonConfig.useSingleServer()
  142 + .setAddress("redis://" + config.getHost() + ":" + config.getPort())
  143 + .setDatabase(config.getDatabase());
  144 + // 1.2 设置密码(如果有)
  145 + if (StrUtil.isNotBlank(config.getPassword())) {
  146 + serverConfig.setPassword(config.getPassword());
  147 + }
  148 +
  149 + // 2.1 创建 RedisTemplate 并配置
  150 + RedissonClient redisson = Redisson.create(redissonConfig);
  151 + RedisTemplate<String, Object> template = new RedisTemplate<>();
  152 + template.setConnectionFactory(new RedissonConnectionFactory(redisson));
  153 + // 2.2 设置序列化器
  154 + template.setKeySerializer(RedisSerializer.string());
  155 + template.setHashKeySerializer(RedisSerializer.string());
  156 + template.setValueSerializer(RedisSerializer.json());
  157 + template.setHashValueSerializer(RedisSerializer.json());
  158 + template.afterPropertiesSet();
  159 + return template;
  160 + }
  161 +
  162 + @Override
  163 + protected void closeProducer(RedisTemplate<String, Object> producer) throws Exception {
  164 + RedisConnectionFactory factory = producer.getConnectionFactory();
  165 + if (factory != null) {
  166 + ((RedissonConnectionFactory) factory).destroy();
  167 + }
  168 + }
  169 +
  170 + /**
  171 + * 根据类型值获取数据结构枚举
  172 + */
  173 + private IotRedisDataStructureEnum getDataStructureByType(Integer type) {
  174 + for (IotRedisDataStructureEnum dataStructure : IotRedisDataStructureEnum.values()) {
  175 + if (dataStructure.getType().equals(type)) {
  176 + return dataStructure;
  177 + }
  178 + }
  179 + throw new IllegalArgumentException("不支持的 Redis 数据结构类型: " + type);
  180 + }
  181 +
  182 +}
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/data/action/IotRedisStreamRuleAction.java deleted
1 -package cn.iocoder.yudao.module.iot.service.rule.data.action;  
2 -  
3 -import cn.hutool.core.util.StrUtil;  
4 -import cn.iocoder.yudao.framework.common.util.json.JsonUtils;  
5 -import cn.iocoder.yudao.module.iot.dal.dataobject.rule.config.IotDataSinkRedisStreamConfig;  
6 -import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;  
7 -import cn.iocoder.yudao.module.iot.enums.rule.IotDataSinkTypeEnum;  
8 -import lombok.extern.slf4j.Slf4j;  
9 -import org.redisson.Redisson;  
10 -import org.redisson.api.RedissonClient;  
11 -import org.redisson.config.Config;  
12 -import org.redisson.config.SingleServerConfig;  
13 -import org.redisson.spring.data.connection.RedissonConnectionFactory;  
14 -import org.springframework.data.redis.connection.RedisConnectionFactory;  
15 -import org.springframework.data.redis.connection.stream.ObjectRecord;  
16 -import org.springframework.data.redis.connection.stream.StreamRecords;  
17 -import org.springframework.data.redis.core.RedisTemplate;  
18 -import org.springframework.data.redis.serializer.RedisSerializer;  
19 -import org.springframework.stereotype.Component;  
20 -  
21 -/**  
22 - * Redis Stream 的 {@link IotDataRuleAction} 实现类  
23 - *  
24 - * @author HUIHUI  
25 - */  
26 -@Component  
27 -@Slf4j  
28 -public class IotRedisStreamRuleAction extends  
29 - IotDataRuleCacheableAction<IotDataSinkRedisStreamConfig, RedisTemplate<String, Object>> {  
30 -  
31 - @Override  
32 - public Integer getType() {  
33 - return IotDataSinkTypeEnum.REDIS_STREAM.getType();  
34 - }  
35 -  
36 - @Override  
37 - public void execute(IotDeviceMessage message, IotDataSinkRedisStreamConfig config) throws Exception {  
38 - // 1. 获取 RedisTemplate  
39 - RedisTemplate<String, Object> redisTemplate = getProducer(config);  
40 -  
41 - // 2. 创建并发送 Stream 记录  
42 - ObjectRecord<String, ?> record = StreamRecords.newRecord()  
43 - .ofObject(JsonUtils.toJsonString(message)).withStreamKey(config.getTopic());  
44 - String recordId = String.valueOf(redisTemplate.opsForStream().add(record));  
45 - log.info("[execute][消息发送成功] messageId: {}, config: {}", recordId, config);  
46 - }  
47 -  
48 - @Override  
49 - protected RedisTemplate<String, Object> initProducer(IotDataSinkRedisStreamConfig config) {  
50 - // 1.1 创建 Redisson 配置  
51 - Config redissonConfig = new Config();  
52 - SingleServerConfig serverConfig = redissonConfig.useSingleServer()  
53 - .setAddress("redis://" + config.getHost() + ":" + config.getPort())  
54 - .setDatabase(config.getDatabase());  
55 - // 1.2 设置密码(如果有)  
56 - if (StrUtil.isNotBlank(config.getPassword())) {  
57 - serverConfig.setPassword(config.getPassword());  
58 - }  
59 -  
60 - // 2.1 创建 RedisTemplate 并配置  
61 - RedissonClient redisson = Redisson.create(redissonConfig);  
62 - RedisTemplate<String, Object> template = new RedisTemplate<>();  
63 - template.setConnectionFactory(new RedissonConnectionFactory(redisson));  
64 - // 2.2 设置序列化器  
65 - template.setKeySerializer(RedisSerializer.string());  
66 - template.setHashKeySerializer(RedisSerializer.string());  
67 - template.setValueSerializer(RedisSerializer.json());  
68 - template.setHashValueSerializer(RedisSerializer.json());  
69 - template.afterPropertiesSet();  
70 - return template;  
71 - }  
72 -  
73 - @Override  
74 - protected void closeProducer(RedisTemplate<String, Object> producer) throws Exception {  
75 - RedisConnectionFactory factory = producer.getConnectionFactory();  
76 - if (factory != null) {  
77 - ((RedissonConnectionFactory) factory).destroy();  
78 - }  
79 - }  
80 -  
81 -}  
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/IotRuleSceneService.java renamed to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/IotSceneRuleService.java
1 package cn.iocoder.yudao.module.iot.service.rule.scene; 1 package cn.iocoder.yudao.module.iot.service.rule.scene;
2 2
3 import cn.iocoder.yudao.framework.common.pojo.PageResult; 3 import cn.iocoder.yudao.framework.common.pojo.PageResult;
4 -import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.scene.IotRuleScenePageReqVO;  
5 -import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.scene.IotRuleSceneSaveReqVO; 4 +import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.scene.IotSceneRulePageReqVO;
  5 +import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.scene.IotSceneRuleSaveReqVO;
6 import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; 6 import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
7 import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotSceneRuleDO; 7 import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotSceneRuleDO;
8 -import cn.iocoder.yudao.module.iot.enums.rule.IotRuleSceneTriggerTypeEnum; 8 +import cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleTriggerTypeEnum;
9 import jakarta.validation.Valid; 9 import jakarta.validation.Valid;
10 10
11 import java.util.Collection; 11 import java.util.Collection;
@@ -16,7 +16,7 @@ import java.util.List; @@ -16,7 +16,7 @@ import java.util.List;
16 * 16 *
17 * @author 芋道源码 17 * @author 芋道源码
18 */ 18 */
19 -public interface IotRuleSceneService { 19 +public interface IotSceneRuleService {
20 20
21 /** 21 /**
22 * 创建场景联动 22 * 创建场景联动
@@ -24,14 +24,14 @@ public interface IotRuleSceneService { @@ -24,14 +24,14 @@ public interface IotRuleSceneService {
24 * @param createReqVO 创建信息 24 * @param createReqVO 创建信息
25 * @return 编号 25 * @return 编号
26 */ 26 */
27 - Long createRuleScene(@Valid IotRuleSceneSaveReqVO createReqVO); 27 + Long createSceneRule(@Valid IotSceneRuleSaveReqVO createReqVO);
28 28
29 /** 29 /**
30 * 更新场景联动 30 * 更新场景联动
31 * 31 *
32 * @param updateReqVO 更新信息 32 * @param updateReqVO 更新信息
33 */ 33 */
34 - void updateRuleScene(@Valid IotRuleSceneSaveReqVO updateReqVO); 34 + void updateSceneRule(@Valid IotSceneRuleSaveReqVO updateReqVO);
35 35
36 /** 36 /**
37 * 更新场景联动状态 37 * 更新场景联动状态
@@ -39,14 +39,14 @@ public interface IotRuleSceneService { @@ -39,14 +39,14 @@ public interface IotRuleSceneService {
39 * @param id 场景联动编号 39 * @param id 场景联动编号
40 * @param status 状态 40 * @param status 状态
41 */ 41 */
42 - void updateRuleSceneStatus(Long id, Integer status); 42 + void updateSceneRuleStatus(Long id, Integer status);
43 43
44 /** 44 /**
45 * 删除场景联动 45 * 删除场景联动
46 * 46 *
47 * @param id 编号 47 * @param id 编号
48 */ 48 */
49 - void deleteRuleScene(Long id); 49 + void deleteSceneRule(Long id);
50 50
51 /** 51 /**
52 * 获得场景联动 52 * 获得场景联动
@@ -54,7 +54,7 @@ public interface IotRuleSceneService { @@ -54,7 +54,7 @@ public interface IotRuleSceneService {
54 * @param id 编号 54 * @param id 编号
55 * @return 场景联动 55 * @return 场景联动
56 */ 56 */
57 - IotSceneRuleDO getRuleScene(Long id); 57 + IotSceneRuleDO getSceneRule(Long id);
58 58
59 /** 59 /**
60 * 获得场景联动分页 60 * 获得场景联动分页
@@ -62,7 +62,7 @@ public interface IotRuleSceneService { @@ -62,7 +62,7 @@ public interface IotRuleSceneService {
62 * @param pageReqVO 分页查询 62 * @param pageReqVO 分页查询
63 * @return 场景联动分页 63 * @return 场景联动分页
64 */ 64 */
65 - PageResult<IotSceneRuleDO> getRuleScenePage(IotRuleScenePageReqVO pageReqVO); 65 + PageResult<IotSceneRuleDO> getSceneRulePage(IotSceneRulePageReqVO pageReqVO);
66 66
67 /** 67 /**
68 * 校验规则场景联动规则编号们是否存在。如下情况,视为无效: 68 * 校验规则场景联动规则编号们是否存在。如下情况,视为无效:
@@ -70,7 +70,7 @@ public interface IotRuleSceneService { @@ -70,7 +70,7 @@ public interface IotRuleSceneService {
70 * 70 *
71 * @param ids 场景联动规则编号数组 71 * @param ids 场景联动规则编号数组
72 */ 72 */
73 - void validateRuleSceneList(Collection<Long> ids); 73 + void validateSceneRuleList(Collection<Long> ids);
74 74
75 /** 75 /**
76 * 获得指定状态的场景联动列表 76 * 获得指定状态的场景联动列表
@@ -78,29 +78,33 @@ public interface IotRuleSceneService { @@ -78,29 +78,33 @@ public interface IotRuleSceneService {
78 * @param status 状态 78 * @param status 状态
79 * @return 场景联动列表 79 * @return 场景联动列表
80 */ 80 */
81 - List<IotSceneRuleDO> getRuleSceneListByStatus(Integer status); 81 + List<IotSceneRuleDO> getSceneRuleListByStatus(Integer status);
82 82
83 /** 83 /**
84 * 【缓存】获得指定设备的场景列表 84 * 【缓存】获得指定设备的场景列表
85 * 85 *
86 - * @param productKey 产品 Key  
87 - * @param deviceName 设备名称 86 + * @param productId 产品 ID
  87 + * @param deviceId 设备 ID
88 * @return 场景列表 88 * @return 场景列表
89 */ 89 */
90 - List<IotSceneRuleDO> getRuleSceneListByProductKeyAndDeviceNameFromCache(String productKey, String deviceName); 90 + List<IotSceneRuleDO> getSceneRuleListByProductIdAndDeviceIdFromCache(Long productId, Long deviceId);
91 91
92 /** 92 /**
93 - * 基于 {@link IotRuleSceneTriggerTypeEnum#DEVICE} 场景,执行规则场景  
94 - * 93 + * 基于 {@link IotSceneRuleTriggerTypeEnum} 场景,执行规则场景
  94 + * 1. {@link IotSceneRuleTriggerTypeEnum#DEVICE_STATE_UPDATE}
  95 + * 2. {@link IotSceneRuleTriggerTypeEnum#DEVICE_PROPERTY_POST}
  96 + * {@link IotSceneRuleTriggerTypeEnum#DEVICE_EVENT_POST}
  97 + * 3. {@link IotSceneRuleTriggerTypeEnum#DEVICE_EVENT_POST}
  98 + * {@link IotSceneRuleTriggerTypeEnum#DEVICE_SERVICE_INVOKE}
95 * @param message 消息 99 * @param message 消息
96 */ 100 */
97 - void executeRuleSceneByDevice(IotDeviceMessage message); 101 + void executeSceneRuleByDevice(IotDeviceMessage message);
98 102
99 /** 103 /**
100 - * 基于 {@link IotRuleSceneTriggerTypeEnum#TIMER} 场景,执行规则场景 104 + * 基于 {@link IotSceneRuleTriggerTypeEnum#TIMER} 场景,执行规则场景
101 * 105 *
102 * @param id 场景联动规则编号 106 * @param id 场景联动规则编号
103 */ 107 */
104 - void executeRuleSceneByTimer(Long id); 108 + void executeSceneRuleByTimer(Long id);
105 109
106 } 110 }
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/IotRuleSceneServiceImpl.java renamed to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/IotSceneRuleServiceImpl.java
@@ -7,6 +7,7 @@ import cn.hutool.core.text.CharPool; @@ -7,6 +7,7 @@ import cn.hutool.core.text.CharPool;
7 import cn.hutool.core.util.NumberUtil; 7 import cn.hutool.core.util.NumberUtil;
8 import cn.hutool.core.util.ObjUtil; 8 import cn.hutool.core.util.ObjUtil;
9 import cn.hutool.core.util.StrUtil; 9 import cn.hutool.core.util.StrUtil;
  10 +import cn.hutool.extra.spring.SpringUtil;
10 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; 11 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
11 import cn.iocoder.yudao.framework.common.pojo.PageResult; 12 import cn.iocoder.yudao.framework.common.pojo.PageResult;
12 import cn.iocoder.yudao.framework.common.util.number.NumberUtils; 13 import cn.iocoder.yudao.framework.common.util.number.NumberUtils;
@@ -15,21 +16,20 @@ import cn.iocoder.yudao.framework.common.util.object.ObjectUtils; @@ -15,21 +16,20 @@ import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
15 import cn.iocoder.yudao.framework.common.util.spring.SpringExpressionUtils; 16 import cn.iocoder.yudao.framework.common.util.spring.SpringExpressionUtils;
16 import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore; 17 import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore;
17 import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils; 18 import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils;
18 -import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.scene.IotRuleScenePageReqVO;  
19 -import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.scene.IotRuleSceneSaveReqVO; 19 +import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.scene.IotSceneRulePageReqVO;
  20 +import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.scene.IotSceneRuleSaveReqVO;
20 import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; 21 import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
21 -import cn.iocoder.yudao.module.iot.core.util.IotDeviceMessageUtils;  
22 import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; 22 import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO;
23 import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; 23 import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO;
24 import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotSceneRuleDO; 24 import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotSceneRuleDO;
25 -import cn.iocoder.yudao.module.iot.dal.mysql.rule.IotRuleSceneMapper;  
26 -import cn.iocoder.yudao.module.iot.enums.rule.IotRuleSceneConditionOperatorEnum;  
27 -import cn.iocoder.yudao.module.iot.enums.rule.IotRuleSceneConditionTypeEnum;  
28 -import cn.iocoder.yudao.module.iot.enums.rule.IotRuleSceneTriggerTypeEnum; 25 +import cn.iocoder.yudao.module.iot.dal.mysql.rule.IotSceneRuleMapper;
  26 +import cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleConditionOperatorEnum;
  27 +import cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleTriggerTypeEnum;
29 import cn.iocoder.yudao.module.iot.framework.job.core.IotSchedulerManager; 28 import cn.iocoder.yudao.module.iot.framework.job.core.IotSchedulerManager;
30 import cn.iocoder.yudao.module.iot.service.device.IotDeviceService; 29 import cn.iocoder.yudao.module.iot.service.device.IotDeviceService;
31 import cn.iocoder.yudao.module.iot.service.product.IotProductService; 30 import cn.iocoder.yudao.module.iot.service.product.IotProductService;
32 import cn.iocoder.yudao.module.iot.service.rule.scene.action.IotSceneRuleAction; 31 import cn.iocoder.yudao.module.iot.service.rule.scene.action.IotSceneRuleAction;
  32 +import cn.iocoder.yudao.module.iot.service.rule.scene.matcher.IotSceneRuleMatcherManager;
33 import jakarta.annotation.Resource; 33 import jakarta.annotation.Resource;
34 import lombok.extern.slf4j.Slf4j; 34 import lombok.extern.slf4j.Slf4j;
35 import org.springframework.stereotype.Service; 35 import org.springframework.stereotype.Service;
@@ -53,200 +53,166 @@ import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.RULE_SCENE_NO @@ -53,200 +53,166 @@ import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.RULE_SCENE_NO
53 @Service 53 @Service
54 @Validated 54 @Validated
55 @Slf4j 55 @Slf4j
56 -public class IotRuleSceneServiceImpl implements IotRuleSceneService { 56 +public class IotSceneRuleServiceImpl implements IotSceneRuleService {
57 57
58 @Resource 58 @Resource
59 - private IotRuleSceneMapper ruleSceneMapper;  
60 -  
61 - @Resource  
62 - private List<IotSceneRuleAction> ruleSceneActions; 59 + private IotSceneRuleMapper sceneRuleMapper;
63 60
64 @Resource(name = "iotSchedulerManager") 61 @Resource(name = "iotSchedulerManager")
65 private IotSchedulerManager schedulerManager; 62 private IotSchedulerManager schedulerManager;
66 -  
67 @Resource 63 @Resource
68 private IotProductService productService; 64 private IotProductService productService;
69 -  
70 @Resource 65 @Resource
71 private IotDeviceService deviceService; 66 private IotDeviceService deviceService;
  67 + @Resource
  68 + private IotSceneRuleMatcherManager matcherManager;
  69 + @Resource
  70 + private List<IotSceneRuleAction> sceneRuleActions;
72 71
73 @Override 72 @Override
74 - public Long createRuleScene(IotRuleSceneSaveReqVO createReqVO) {  
75 - IotSceneRuleDO ruleScene = BeanUtils.toBean(createReqVO, IotSceneRuleDO.class);  
76 - ruleSceneMapper.insert(ruleScene);  
77 - return ruleScene.getId(); 73 + public Long createSceneRule(IotSceneRuleSaveReqVO createReqVO) {
  74 + IotSceneRuleDO sceneRule = BeanUtils.toBean(createReqVO, IotSceneRuleDO.class);
  75 + sceneRuleMapper.insert(sceneRule);
  76 + return sceneRule.getId();
78 } 77 }
79 78
80 @Override 79 @Override
81 - public void updateRuleScene(IotRuleSceneSaveReqVO updateReqVO) { 80 + public void updateSceneRule(IotSceneRuleSaveReqVO updateReqVO) {
82 // 校验存在 81 // 校验存在
83 - validateRuleSceneExists(updateReqVO.getId()); 82 + validateSceneRuleExists(updateReqVO.getId());
84 // 更新 83 // 更新
85 IotSceneRuleDO updateObj = BeanUtils.toBean(updateReqVO, IotSceneRuleDO.class); 84 IotSceneRuleDO updateObj = BeanUtils.toBean(updateReqVO, IotSceneRuleDO.class);
86 - ruleSceneMapper.updateById(updateObj); 85 + sceneRuleMapper.updateById(updateObj);
87 } 86 }
88 87
89 @Override 88 @Override
90 - public void updateRuleSceneStatus(Long id, Integer status) { 89 + public void updateSceneRuleStatus(Long id, Integer status) {
91 // 校验存在 90 // 校验存在
92 - validateRuleSceneExists(id); 91 + validateSceneRuleExists(id);
93 // 更新状态 92 // 更新状态
94 IotSceneRuleDO updateObj = new IotSceneRuleDO().setId(id).setStatus(status); 93 IotSceneRuleDO updateObj = new IotSceneRuleDO().setId(id).setStatus(status);
95 - ruleSceneMapper.updateById(updateObj); 94 + sceneRuleMapper.updateById(updateObj);
96 } 95 }
97 96
98 @Override 97 @Override
99 - public void deleteRuleScene(Long id) { 98 + public void deleteSceneRule(Long id) {
100 // 校验存在 99 // 校验存在
101 - validateRuleSceneExists(id); 100 + validateSceneRuleExists(id);
102 // 删除 101 // 删除
103 - ruleSceneMapper.deleteById(id); 102 + sceneRuleMapper.deleteById(id);
104 } 103 }
105 104
106 - private void validateRuleSceneExists(Long id) {  
107 - if (ruleSceneMapper.selectById(id) == null) { 105 + private void validateSceneRuleExists(Long id) {
  106 + if (sceneRuleMapper.selectById(id) == null) {
108 throw exception(RULE_SCENE_NOT_EXISTS); 107 throw exception(RULE_SCENE_NOT_EXISTS);
109 } 108 }
110 } 109 }
111 110
112 @Override 111 @Override
113 - public IotSceneRuleDO getRuleScene(Long id) {  
114 - return ruleSceneMapper.selectById(id); 112 + public IotSceneRuleDO getSceneRule(Long id) {
  113 + return sceneRuleMapper.selectById(id);
115 } 114 }
116 115
117 @Override 116 @Override
118 - public PageResult<IotSceneRuleDO> getRuleScenePage(IotRuleScenePageReqVO pageReqVO) {  
119 - return ruleSceneMapper.selectPage(pageReqVO); 117 + public PageResult<IotSceneRuleDO> getSceneRulePage(IotSceneRulePageReqVO pageReqVO) {
  118 + return sceneRuleMapper.selectPage(pageReqVO);
120 } 119 }
121 120
122 @Override 121 @Override
123 - public void validateRuleSceneList(Collection<Long> ids) { 122 + public void validateSceneRuleList(Collection<Long> ids) {
124 if (CollUtil.isEmpty(ids)) { 123 if (CollUtil.isEmpty(ids)) {
125 return; 124 return;
126 } 125 }
127 // 批量查询存在的规则场景 126 // 批量查询存在的规则场景
128 - List<IotSceneRuleDO> existingScenes = ruleSceneMapper.selectByIds(ids); 127 + List<IotSceneRuleDO> existingScenes = sceneRuleMapper.selectByIds(ids);
129 if (existingScenes.size() != ids.size()) { 128 if (existingScenes.size() != ids.size()) {
130 throw exception(RULE_SCENE_NOT_EXISTS); 129 throw exception(RULE_SCENE_NOT_EXISTS);
131 } 130 }
132 } 131 }
133 132
134 @Override 133 @Override
135 - public List<IotSceneRuleDO> getRuleSceneListByStatus(Integer status) {  
136 - return ruleSceneMapper.selectListByStatus(status); 134 + public List<IotSceneRuleDO> getSceneRuleListByStatus(Integer status) {
  135 + return sceneRuleMapper.selectListByStatus(status);
137 } 136 }
138 137
139 // TODO 芋艿,缓存待实现 138 // TODO 芋艿,缓存待实现
140 @Override 139 @Override
141 - @TenantIgnore // 忽略租户隔离:因为 IotRuleSceneMessageHandler 调用时,一般未传递租户,所以需要忽略  
142 - public List<IotSceneRuleDO> getRuleSceneListByProductKeyAndDeviceNameFromCache(String productKey, String deviceName) {  
143 - // TODO @puhui999:一些注释,看看要不要优化下;  
144 - // 注意:旧的测试代码已删除,因为使用了废弃的数据结构  
145 - // 如需测试,请使用上面的新结构测试代码示例  
146 - List<IotSceneRuleDO> list = ruleSceneMapper.selectList(); 140 + @TenantIgnore // 忽略租户隔离:因为 IotSceneRuleMessageHandler 调用时,一般未传递租户,所以需要忽略
  141 + public List<IotSceneRuleDO> getSceneRuleListByProductIdAndDeviceIdFromCache(Long productId, Long deviceId) {
  142 + List<IotSceneRuleDO> list = sceneRuleMapper.selectList();
147 // 只返回启用状态的规则场景 143 // 只返回启用状态的规则场景
148 List<IotSceneRuleDO> enabledList = filterList(list, 144 List<IotSceneRuleDO> enabledList = filterList(list,
149 - ruleScene -> CommonStatusEnum.ENABLE.getStatus().equals(ruleScene.getStatus())); 145 + sceneRule -> CommonStatusEnum.isEnable(sceneRule.getStatus()));
150 146
151 // 根据 productKey 和 deviceName 进行匹配 147 // 根据 productKey 和 deviceName 进行匹配
152 - return filterList(enabledList, ruleScene -> {  
153 - if (CollUtil.isEmpty(ruleScene.getTriggers())) { 148 + return filterList(enabledList, sceneRule -> {
  149 + if (CollUtil.isEmpty(sceneRule.getTriggers())) {
154 return false; 150 return false;
155 } 151 }
156 152
157 - for (IotSceneRuleDO.Trigger trigger : ruleScene.getTriggers()) { 153 + for (IotSceneRuleDO.Trigger trigger : sceneRule.getTriggers()) {
158 // 检查触发器是否匹配指定的产品和设备 154 // 检查触发器是否匹配指定的产品和设备
159 - if (isMatchProductAndDevice(trigger, productKey, deviceName)) {  
160 - return true;  
161 - }  
162 - }  
163 - return false;  
164 - });  
165 - }  
166 -  
167 - /**  
168 - * 检查触发器是否匹配指定的产品和设备  
169 - *  
170 - * @param trigger 触发器  
171 - * @param productKey 产品标识  
172 - * @param deviceName 设备名称  
173 - * @return 是否匹配  
174 - */  
175 - private boolean isMatchProductAndDevice(IotSceneRuleDO.Trigger trigger, String productKey, String deviceName) {  
176 - try {  
177 - // 1. 检查产品是否匹配  
178 - if (trigger.getProductId() != null) {  
179 - // 通过 productKey 获取产品信息  
180 - IotProductDO product = productService.getProductByProductKey(productKey);  
181 - if (product == null || !trigger.getProductId().equals(product.getId())) {  
182 - return false;  
183 - }  
184 - }  
185 -  
186 - // 2. 检查设备是否匹配  
187 - if (trigger.getDeviceId() != null) {  
188 - // 通过 productKey 和 deviceName 获取设备信息  
189 - IotDeviceDO device = deviceService.getDeviceFromCache(productKey, deviceName);  
190 - if (device == null) {  
191 - return false;  
192 - }  
193 -  
194 - // 检查是否是全部设备的特殊标识  
195 - if (IotDeviceDO.DEVICE_ID_ALL.equals(trigger.getDeviceId())) {  
196 - return true; // 匹配所有设备  
197 - }  
198 -  
199 - // 检查具体设备ID是否匹配  
200 - if (!trigger.getDeviceId().equals(device.getId())) { 155 + try {
  156 + // 1. 检查产品是否匹配
  157 + if (trigger.getProductId() == null) {
  158 + return false;
  159 + }
  160 + if (trigger.getDeviceId() == null) {
  161 + return false;
  162 + }
  163 + // 检查是否是全部设备的特殊标识
  164 + if (IotDeviceDO.DEVICE_ID_ALL.equals(trigger.getDeviceId())) {
  165 + return true; // 匹配所有设备
  166 + }
  167 + // 检查具体设备 ID 是否匹配
  168 + return ObjUtil.equal(productId, trigger.getProductId()) && ObjUtil.equal(deviceId, trigger.getDeviceId());
  169 + } catch (Exception e) {
  170 + log.warn("[isMatchProductAndDevice][产品({}) 设备({}) 匹配触发器异常]", productId, deviceId, e);
201 return false; 171 return false;
202 } 172 }
203 } 173 }
204 -  
205 - return true;  
206 - } catch (Exception e) {  
207 - log.warn("[isMatchProductAndDevice][产品({}) 设备({}) 匹配触发器异常]", productKey, deviceName, e);  
208 return false; 174 return false;
209 - } 175 + });
210 } 176 }
211 177
212 @Override 178 @Override
213 - public void executeRuleSceneByDevice(IotDeviceMessage message) { 179 + public void executeSceneRuleByDevice(IotDeviceMessage message) {
214 // TODO @芋艿:这里的 tenantId,通过设备获取; 180 // TODO @芋艿:这里的 tenantId,通过设备获取;
215 TenantUtils.execute(message.getTenantId(), () -> { 181 TenantUtils.execute(message.getTenantId(), () -> {
216 // 1. 获得设备匹配的规则场景 182 // 1. 获得设备匹配的规则场景
217 - List<IotSceneRuleDO> ruleScenes = getMatchedRuleSceneListByMessage(message);  
218 - if (CollUtil.isEmpty(ruleScenes)) { 183 + List<IotSceneRuleDO> sceneRules = getMatchedSceneRuleListByMessage(message);
  184 + if (CollUtil.isEmpty(sceneRules)) {
219 return; 185 return;
220 } 186 }
221 187
222 // 2. 执行规则场景 188 // 2. 执行规则场景
223 - executeRuleSceneAction(message, ruleScenes); 189 + executeSceneRuleAction(message, sceneRules);
224 }); 190 });
225 } 191 }
226 192
227 @Override 193 @Override
228 - public void executeRuleSceneByTimer(Long id) { 194 + public void executeSceneRuleByTimer(Long id) {
229 // 1.1 获得规则场景 195 // 1.1 获得规则场景
230 - IotSceneRuleDO scene = TenantUtils.executeIgnore(() -> ruleSceneMapper.selectById(id)); 196 + IotSceneRuleDO scene = TenantUtils.executeIgnore(() -> sceneRuleMapper.selectById(id));
231 if (scene == null) { 197 if (scene == null) {
232 - log.error("[executeRuleSceneByTimer][规则场景({}) 不存在]", id); 198 + log.error("[executeSceneRuleByTimer][规则场景({}) 不存在]", id);
233 return; 199 return;
234 } 200 }
235 if (CommonStatusEnum.isDisable(scene.getStatus())) { 201 if (CommonStatusEnum.isDisable(scene.getStatus())) {
236 - log.info("[executeRuleSceneByTimer][规则场景({}) 已被禁用]", id); 202 + log.info("[executeSceneRuleByTimer][规则场景({}) 已被禁用]", id);
237 return; 203 return;
238 } 204 }
239 // 1.2 判断是否有定时触发器,避免脏数据 205 // 1.2 判断是否有定时触发器,避免脏数据
240 IotSceneRuleDO.Trigger config = CollUtil.findOne(scene.getTriggers(), 206 IotSceneRuleDO.Trigger config = CollUtil.findOne(scene.getTriggers(),
241 - trigger -> ObjUtil.equals(trigger.getType(), IotRuleSceneTriggerTypeEnum.TIMER.getType())); 207 + trigger -> ObjUtil.equals(trigger.getType(), IotSceneRuleTriggerTypeEnum.TIMER.getType()));
242 if (config == null) { 208 if (config == null) {
243 - log.error("[executeRuleSceneByTimer][规则场景({}) 不存在定时触发器]", scene); 209 + log.error("[executeSceneRuleByTimer][规则场景({}) 不存在定时触发器]", scene);
244 return; 210 return;
245 } 211 }
246 212
247 // 2. 执行规则场景 213 // 2. 执行规则场景
248 TenantUtils.execute(scene.getTenantId(), 214 TenantUtils.execute(scene.getTenantId(),
249 - () -> executeRuleSceneAction(null, ListUtil.toList(scene))); 215 + () -> executeSceneRuleAction(null, ListUtil.toList(scene)));
250 } 216 }
251 217
252 /** 218 /**
@@ -255,204 +221,132 @@ public class IotRuleSceneServiceImpl implements IotRuleSceneService { @@ -255,204 +221,132 @@ public class IotRuleSceneServiceImpl implements IotRuleSceneService {
255 * @param message 设备消息 221 * @param message 设备消息
256 * @return 规则场景列表 222 * @return 规则场景列表
257 */ 223 */
258 - private List<IotSceneRuleDO> getMatchedRuleSceneListByMessage(IotDeviceMessage message) { 224 + private List<IotSceneRuleDO> getMatchedSceneRuleListByMessage(IotDeviceMessage message) {
259 // 1. 匹配设备 225 // 1. 匹配设备
260 // TODO @芋艿:可能需要 getSelf(); 缓存 226 // TODO @芋艿:可能需要 getSelf(); 缓存
261 // 1.1 通过 deviceId 获取设备信息 227 // 1.1 通过 deviceId 获取设备信息
262 IotDeviceDO device = deviceService.getDeviceFromCache(message.getDeviceId()); 228 IotDeviceDO device = deviceService.getDeviceFromCache(message.getDeviceId());
263 if (device == null) { 229 if (device == null) {
264 - log.warn("[getMatchedRuleSceneListByMessage][设备({}) 不存在]", message.getDeviceId()); 230 + log.warn("[getMatchedSceneRuleListByMessage][设备({}) 不存在]", message.getDeviceId());
265 return List.of(); 231 return List.of();
266 } 232 }
267 233
268 // 1.2 通过 productId 获取产品信息 234 // 1.2 通过 productId 获取产品信息
269 IotProductDO product = productService.getProductFromCache(device.getProductId()); 235 IotProductDO product = productService.getProductFromCache(device.getProductId());
270 if (product == null) { 236 if (product == null) {
271 - log.warn("[getMatchedRuleSceneListByMessage][产品({}) 不存在]", device.getProductId()); 237 + log.warn("[getMatchedSceneRuleListByMessage][产品({}) 不存在]", device.getProductId());
272 return List.of(); 238 return List.of();
273 } 239 }
274 240
275 // 1.3 获取匹配的规则场景 241 // 1.3 获取匹配的规则场景
276 - List<IotSceneRuleDO> ruleScenes = getRuleSceneListByProductKeyAndDeviceNameFromCache(  
277 - product.getProductKey(), device.getDeviceName());  
278 - if (CollUtil.isEmpty(ruleScenes)) {  
279 - return ruleScenes; 242 + List<IotSceneRuleDO> sceneRules = getSceneRuleListByProductIdAndDeviceIdFromCache(
  243 + product.getId(), device.getId());
  244 + if (CollUtil.isEmpty(sceneRules)) {
  245 + return sceneRules;
280 } 246 }
281 247
282 - // 2. 匹配 trigger 触发器的条件  
283 - return filterList(ruleScenes, ruleScene -> {  
284 - for (IotSceneRuleDO.Trigger trigger : ruleScene.getTriggers()) {  
285 - // 2.1 检查触发器类型,根据新的枚举值进行匹配  
286 - // TODO @芋艿:需要根据新的触发器类型枚举进行适配  
287 - // 原来使用 IotRuleSceneTriggerTypeEnum.DEVICE,新结构可能有不同的类型  
288 -  
289 - // 2.2 条件分组为空,说明没有匹配的条件,因此不匹配  
290 - if (CollUtil.isEmpty(trigger.getConditionGroups())) {  
291 - return false;  
292 - }  
293 -  
294 - // 2.3 检查条件分组:分组与分组之间是"或"的关系,条件与条件之间是"且"的关系  
295 - boolean anyGroupMatched = false;  
296 - for (List<IotSceneRuleDO.TriggerCondition> conditionGroup : trigger.getConditionGroups()) {  
297 - if (CollUtil.isEmpty(conditionGroup)) {  
298 - continue;  
299 - }  
300 -  
301 - // 检查当前分组中的所有条件是否都匹配(且关系)  
302 - boolean allConditionsMatched = true;  
303 - for (IotSceneRuleDO.TriggerCondition condition : conditionGroup) {  
304 - // TODO @芋艿:这里需要实现具体的条件匹配逻辑  
305 - // 根据新的 TriggerCondition 结构进行匹配  
306 - if (!isTriggerConditionMatched(message, condition, ruleScene, trigger)) {  
307 - allConditionsMatched = false;  
308 - break;  
309 - }  
310 - }  
311 -  
312 - if (allConditionsMatched) {  
313 - anyGroupMatched = true;  
314 - break; // 有一个分组匹配即可  
315 - }  
316 - }  
317 -  
318 - if (anyGroupMatched) {  
319 - log.info("[getMatchedRuleSceneList][消息({}) 匹配到规则场景编号({}) 的触发器({})]", message, ruleScene.getId(), trigger);  
320 - return true;  
321 - }  
322 - }  
323 - return false;  
324 - }); 248 + // 2. 使用重构后的触发器匹配逻辑
  249 + return filterList(sceneRules, sceneRule -> matchSceneRuleTriggers(message, sceneRule));
325 } 250 }
326 251
327 /** 252 /**
328 - * 基于消息,判断触发器的条件是否匹配 253 + * 匹配场景规则的所有触发器
329 * 254 *
330 * @param message 设备消息 255 * @param message 设备消息
331 - * @param condition 触发条件  
332 - * @param ruleScene 规则场景(用于日志,无其它作用)  
333 - * @param trigger 触发器(用于日志,无其它作用) 256 + * @param sceneRule 场景规则
334 * @return 是否匹配 257 * @return 是否匹配
335 */ 258 */
336 - private boolean isTriggerConditionMatched(IotDeviceMessage message, IotSceneRuleDO.TriggerCondition condition,  
337 - IotSceneRuleDO ruleScene, IotSceneRuleDO.Trigger trigger) {  
338 - try {  
339 - // 1. 根据条件类型进行匹配  
340 - if (IotRuleSceneConditionTypeEnum.DEVICE_STATE.getType().equals(condition.getType())) {  
341 - // 设备状态条件匹配  
342 - return matchDeviceStateCondition(message, condition);  
343 - } else if (IotRuleSceneConditionTypeEnum.DEVICE_PROPERTY.getType().equals(condition.getType())) {  
344 - // 设备属性条件匹配  
345 - return matchDevicePropertyCondition(message, condition);  
346 - } else if (IotRuleSceneConditionTypeEnum.CURRENT_TIME.getType().equals(condition.getType())) {  
347 - // 当前时间条件匹配  
348 - return matchCurrentTimeCondition(condition);  
349 - } else {  
350 - log.warn("[isTriggerConditionMatched][规则场景编号({}) 的触发器({}) 存在未知的条件类型({})]",  
351 - ruleScene.getId(), trigger, condition.getType());  
352 - return false;  
353 - }  
354 - } catch (Exception e) {  
355 - log.error("[isTriggerConditionMatched][规则场景编号({}) 的触发器({}) 条件匹配异常]",  
356 - ruleScene.getId(), trigger, e); 259 + private boolean matchSceneRuleTriggers(IotDeviceMessage message, IotSceneRuleDO sceneRule) {
  260 + if (CollUtil.isEmpty(sceneRule.getTriggers())) {
  261 + log.debug("[matchSceneRuleTriggers][规则场景({}) 没有配置触发器]", sceneRule.getId());
357 return false; 262 return false;
358 } 263 }
  264 +
  265 + for (IotSceneRuleDO.Trigger trigger : sceneRule.getTriggers()) {
  266 + if (matchSingleTrigger(message, trigger, sceneRule)) {
  267 + log.info("[matchSceneRuleTriggers][消息({}) 匹配到规则场景编号({}) 的触发器({})]",
  268 + message.getRequestId(), sceneRule.getId(), trigger.getType());
  269 + return true;
  270 + }
  271 + }
  272 + return false;
359 } 273 }
360 274
361 /** 275 /**
362 - * 匹配设备状态条件 276 + * 匹配单个触发器
363 * 277 *
364 * @param message 设备消息 278 * @param message 设备消息
365 - * @param condition 触发条件 279 + * @param trigger 触发器
  280 + * @param sceneRule 场景规则(用于日志)
366 * @return 是否匹配 281 * @return 是否匹配
367 */ 282 */
368 - private boolean matchDeviceStateCondition(IotDeviceMessage message, IotSceneRuleDO.TriggerCondition condition) {  
369 - // TODO @芋艿:需要根据设备状态进行匹配  
370 - // 这里需要检查消息中的设备状态是否符合条件中定义的状态  
371 - log.debug("[matchDeviceStateCondition][设备状态条件匹配逻辑待实现] condition: {}", condition);  
372 - return false; 283 + private boolean matchSingleTrigger(IotDeviceMessage message, IotSceneRuleDO.Trigger trigger, IotSceneRuleDO sceneRule) {
  284 + try {
  285 + // 2. 检查触发器的条件分组
  286 + return matcherManager.isMatched(message, trigger) && isTriggerConditionGroupsMatched(message, trigger, sceneRule);
  287 + } catch (Exception e) {
  288 + log.error("[matchSingleTrigger][触发器匹配异常] sceneRuleId: {}, triggerType: {}, message: {}",
  289 + sceneRule.getId(), trigger.getType(), message, e);
  290 + return false;
  291 + }
373 } 292 }
374 293
375 /** 294 /**
376 - * 匹配设备属性条件 295 + * 检查触发器的条件分组是否匹配
377 * 296 *
378 * @param message 设备消息 297 * @param message 设备消息
379 - * @param condition 触发条件 298 + * @param trigger 触发器
  299 + * @param sceneRule 场景规则(用于日志)
380 * @return 是否匹配 300 * @return 是否匹配
381 */ 301 */
382 - private boolean matchDevicePropertyCondition(IotDeviceMessage message, IotSceneRuleDO.TriggerCondition condition) {  
383 - // 1. 检查标识符是否匹配  
384 - String messageIdentifier = IotDeviceMessageUtils.getIdentifier(message);  
385 - if (StrUtil.isBlank(condition.getIdentifier()) || !condition.getIdentifier().equals(messageIdentifier)) {  
386 - return false; 302 + private boolean isTriggerConditionGroupsMatched(IotDeviceMessage message, IotSceneRuleDO.Trigger trigger, IotSceneRuleDO sceneRule) {
  303 + // 如果没有条件分组,则认为匹配成功(只依赖基础触发器匹配)
  304 + if (CollUtil.isEmpty(trigger.getConditionGroups())) {
  305 + return true;
387 } 306 }
388 307
389 - // 2. 获取消息中的属性值  
390 - Object messageValue = message.getData();  
391 - if (messageValue == null) {  
392 - return false;  
393 - } 308 + // 检查条件分组:分组与分组之间是"或"的关系,条件与条件之间是"且"的关系
  309 + for (List<IotSceneRuleDO.TriggerCondition> conditionGroup : trigger.getConditionGroups()) {
  310 + if (CollUtil.isEmpty(conditionGroup)) {
  311 + continue;
  312 + }
394 313
395 - // 3. 根据操作符进行匹配  
396 - return evaluateCondition(messageValue, condition.getOperator(), condition.getParam());  
397 - } 314 + // 检查当前分组中的所有条件是否都匹配(且关系)
  315 + boolean allConditionsMatched = true;
  316 + for (IotSceneRuleDO.TriggerCondition condition : conditionGroup) {
  317 + if (!isTriggerConditionMatched(message, condition, sceneRule, trigger)) {
  318 + allConditionsMatched = false;
  319 + break;
  320 + }
  321 + }
398 322
399 - /**  
400 - * 匹配当前时间条件  
401 - *  
402 - * @param condition 触发条件  
403 - * @return 是否匹配  
404 - */  
405 - private boolean matchCurrentTimeCondition(IotSceneRuleDO.TriggerCondition condition) {  
406 - // TODO @芋艿:需要根据当前时间进行匹配  
407 - // 这里需要检查当前时间是否符合条件中定义的时间范围  
408 - log.debug("[matchCurrentTimeCondition][当前时间条件匹配逻辑待实现] condition: {}", condition); 323 + // 如果当前分组的所有条件都匹配,则整个触发器匹配成功
  324 + if (allConditionsMatched) {
  325 + return true;
  326 + }
  327 + }
  328 +
  329 + // 所有分组都不匹配
409 return false; 330 return false;
410 } 331 }
411 332
412 /** 333 /**
413 - * 评估条件是否匹配 334 + * 基于消息,判断触发器的子条件是否匹配
414 * 335 *
415 - * @param sourceValue 源值(来自消息)  
416 - * @param operator 操作符  
417 - * @param paramValue 参数值(来自条件配置) 336 + * @param message 设备消息
  337 + * @param condition 触发条件
  338 + * @param sceneRule 规则场景(用于日志,无其它作用)
  339 + * @param trigger 触发器(用于日志,无其它作用)
418 * @return 是否匹配 340 * @return 是否匹配
419 */ 341 */
420 - private boolean evaluateCondition(Object sourceValue, String operator, String paramValue) { 342 + private boolean isTriggerConditionMatched(IotDeviceMessage message, IotSceneRuleDO.TriggerCondition condition,
  343 + IotSceneRuleDO sceneRule, IotSceneRuleDO.Trigger trigger) {
421 try { 344 try {
422 - // 1. 校验操作符是否合法  
423 - IotRuleSceneConditionOperatorEnum operatorEnum = IotRuleSceneConditionOperatorEnum.operatorOf(operator);  
424 - if (operatorEnum == null) {  
425 - log.warn("[evaluateCondition][存在错误的操作符({})]", operator);  
426 - return false;  
427 - }  
428 -  
429 - // 2.1 构建 Spring 表达式的变量  
430 - Map<String, Object> springExpressionVariables = MapUtil.<String, Object>builder()  
431 - .put(IotRuleSceneConditionOperatorEnum.SPRING_EXPRESSION_SOURCE, sourceValue)  
432 - .build();  
433 - // 2.2 根据操作符类型处理参数值  
434 - if (StrUtil.isNotBlank(paramValue)) {  
435 - // TODO @puhui999:这里是不是在 IotRuleSceneConditionOperatorEnum 加个属性;  
436 - if (operatorEnum == IotRuleSceneConditionOperatorEnum.IN  
437 - || operatorEnum == IotRuleSceneConditionOperatorEnum.NOT_IN  
438 - || operatorEnum == IotRuleSceneConditionOperatorEnum.BETWEEN  
439 - || operatorEnum == IotRuleSceneConditionOperatorEnum.NOT_BETWEEN) {  
440 - // 处理多值情况  
441 - List<String> paramValues = StrUtil.split(paramValue, CharPool.COMMA);  
442 - springExpressionVariables.put(IotRuleSceneConditionOperatorEnum.SPRING_EXPRESSION_VALUE_LIST,  
443 - convertList(paramValues, NumberUtil::parseDouble));  
444 - } else {  
445 - // 处理单值情况  
446 - springExpressionVariables.put(IotRuleSceneConditionOperatorEnum.SPRING_EXPRESSION_VALUE,  
447 - NumberUtil.parseDouble(paramValue));  
448 - }  
449 - }  
450 -  
451 - // 3. 计算 Spring 表达式  
452 - return (Boolean) SpringExpressionUtils.parseExpression(operatorEnum.getSpringExpression(), springExpressionVariables); 345 + // 使用重构后的条件匹配管理器进行匹配
  346 + return matcherManager.isConditionMatched(message, condition);
453 } catch (Exception e) { 347 } catch (Exception e) {
454 - log.error("[evaluateCondition][条件评估异常] sourceValue: {}, operator: {}, paramValue: {}",  
455 - sourceValue, operator, paramValue, e); 348 + log.error("[isTriggerConditionMatched][规则场景编号({}) 的触发器({}) 条件匹配异常]",
  349 + sceneRule.getId(), trigger, e);
456 return false; 350 return false;
457 } 351 }
458 } 352 }
@@ -464,19 +358,19 @@ public class IotRuleSceneServiceImpl implements IotRuleSceneService { @@ -464,19 +358,19 @@ public class IotRuleSceneServiceImpl implements IotRuleSceneService {
464 * 358 *
465 * @param message 设备消息 359 * @param message 设备消息
466 * @param condition 触发条件 360 * @param condition 触发条件
467 - * @param ruleScene 规则场景(用于日志,无其它作用) 361 + * @param sceneRule 规则场景(用于日志,无其它作用)
468 * @param trigger 触发器(用于日志,无其它作用) 362 * @param trigger 触发器(用于日志,无其它作用)
469 * @return 是否匹配 363 * @return 是否匹配
470 */ 364 */
471 @SuppressWarnings({"unchecked", "DataFlowIssue"}) 365 @SuppressWarnings({"unchecked", "DataFlowIssue"})
472 private boolean isTriggerConditionParameterMatched(IotDeviceMessage message, IotSceneRuleDO.TriggerCondition condition, 366 private boolean isTriggerConditionParameterMatched(IotDeviceMessage message, IotSceneRuleDO.TriggerCondition condition,
473 - IotSceneRuleDO ruleScene, IotSceneRuleDO.Trigger trigger) { 367 + IotSceneRuleDO sceneRule, IotSceneRuleDO.Trigger trigger) {
474 // 1.1 校验操作符是否合法 368 // 1.1 校验操作符是否合法
475 - IotRuleSceneConditionOperatorEnum operator =  
476 - IotRuleSceneConditionOperatorEnum.operatorOf(condition.getOperator()); 369 + IotSceneRuleConditionOperatorEnum operator =
  370 + IotSceneRuleConditionOperatorEnum.operatorOf(condition.getOperator());
477 if (operator == null) { 371 if (operator == null) {
478 log.error("[isTriggerConditionParameterMatched][规则场景编号({}) 的触发器({}) 存在错误的操作符({})]", 372 log.error("[isTriggerConditionParameterMatched][规则场景编号({}) 的触发器({}) 存在错误的操作符({})]",
479 - ruleScene.getId(), trigger, condition.getOperator()); 373 + sceneRule.getId(), trigger, condition.getOperator());
480 return false; 374 return false;
481 } 375 }
482 // 1.2 校验消息是否包含对应的值 376 // 1.2 校验消息是否包含对应的值
@@ -488,31 +382,31 @@ public class IotRuleSceneServiceImpl implements IotRuleSceneService { @@ -488,31 +382,31 @@ public class IotRuleSceneServiceImpl implements IotRuleSceneService {
488 // 2.1 构建 Spring 表达式的变量 382 // 2.1 构建 Spring 表达式的变量
489 Map<String, Object> springExpressionVariables = new HashMap<>(); 383 Map<String, Object> springExpressionVariables = new HashMap<>();
490 try { 384 try {
491 - springExpressionVariables.put(IotRuleSceneConditionOperatorEnum.SPRING_EXPRESSION_SOURCE, messageValue);  
492 - springExpressionVariables.put(IotRuleSceneConditionOperatorEnum.SPRING_EXPRESSION_VALUE, condition.getParam()); 385 + springExpressionVariables.put(IotSceneRuleConditionOperatorEnum.SPRING_EXPRESSION_SOURCE, messageValue);
  386 + springExpressionVariables.put(IotSceneRuleConditionOperatorEnum.SPRING_EXPRESSION_VALUE, condition.getParam());
493 List<String> parameterValues = StrUtil.splitTrim(condition.getParam(), CharPool.COMMA); 387 List<String> parameterValues = StrUtil.splitTrim(condition.getParam(), CharPool.COMMA);
494 - springExpressionVariables.put(IotRuleSceneConditionOperatorEnum.SPRING_EXPRESSION_VALUE_LIST, parameterValues); 388 + springExpressionVariables.put(IotSceneRuleConditionOperatorEnum.SPRING_EXPRESSION_VALUE_LIST, parameterValues);
495 // 特殊:解决数字的比较。因为 Spring 是基于它的 compareTo 方法,对数字的比较存在问题! 389 // 特殊:解决数字的比较。因为 Spring 是基于它的 compareTo 方法,对数字的比较存在问题!
496 - if (ObjectUtils.equalsAny(operator, IotRuleSceneConditionOperatorEnum.BETWEEN,  
497 - IotRuleSceneConditionOperatorEnum.NOT_BETWEEN,  
498 - IotRuleSceneConditionOperatorEnum.GREATER_THAN,  
499 - IotRuleSceneConditionOperatorEnum.GREATER_THAN_OR_EQUALS,  
500 - IotRuleSceneConditionOperatorEnum.LESS_THAN,  
501 - IotRuleSceneConditionOperatorEnum.LESS_THAN_OR_EQUALS) 390 + if (ObjectUtils.equalsAny(operator, IotSceneRuleConditionOperatorEnum.BETWEEN,
  391 + IotSceneRuleConditionOperatorEnum.NOT_BETWEEN,
  392 + IotSceneRuleConditionOperatorEnum.GREATER_THAN,
  393 + IotSceneRuleConditionOperatorEnum.GREATER_THAN_OR_EQUALS,
  394 + IotSceneRuleConditionOperatorEnum.LESS_THAN,
  395 + IotSceneRuleConditionOperatorEnum.LESS_THAN_OR_EQUALS)
502 && NumberUtil.isNumber(messageValue) 396 && NumberUtil.isNumber(messageValue)
503 && NumberUtils.isAllNumber(parameterValues)) { 397 && NumberUtils.isAllNumber(parameterValues)) {
504 - springExpressionVariables.put(IotRuleSceneConditionOperatorEnum.SPRING_EXPRESSION_SOURCE, 398 + springExpressionVariables.put(IotSceneRuleConditionOperatorEnum.SPRING_EXPRESSION_SOURCE,
505 NumberUtil.parseDouble(messageValue)); 399 NumberUtil.parseDouble(messageValue));
506 - springExpressionVariables.put(IotRuleSceneConditionOperatorEnum.SPRING_EXPRESSION_VALUE, 400 + springExpressionVariables.put(IotSceneRuleConditionOperatorEnum.SPRING_EXPRESSION_VALUE,
507 NumberUtil.parseDouble(condition.getParam())); 401 NumberUtil.parseDouble(condition.getParam()));
508 - springExpressionVariables.put(IotRuleSceneConditionOperatorEnum.SPRING_EXPRESSION_VALUE_LIST, 402 + springExpressionVariables.put(IotSceneRuleConditionOperatorEnum.SPRING_EXPRESSION_VALUE_LIST,
509 convertList(parameterValues, NumberUtil::parseDouble)); 403 convertList(parameterValues, NumberUtil::parseDouble));
510 } 404 }
511 // 2.2 计算 Spring 表达式 405 // 2.2 计算 Spring 表达式
512 return (Boolean) SpringExpressionUtils.parseExpression(operator.getSpringExpression(), springExpressionVariables); 406 return (Boolean) SpringExpressionUtils.parseExpression(operator.getSpringExpression(), springExpressionVariables);
513 } catch (Exception e) { 407 } catch (Exception e) {
514 log.error("[isTriggerConditionParameterMatched][消息({}) 规则场景编号({}) 的触发器({}) 的匹配表达式({}/{}) 计算异常]", 408 log.error("[isTriggerConditionParameterMatched][消息({}) 规则场景编号({}) 的触发器({}) 的匹配表达式({}/{}) 计算异常]",
515 - message, ruleScene.getId(), trigger, operator, springExpressionVariables, e); 409 + message, sceneRule.getId(), trigger, operator, springExpressionVariables, e);
516 return false; 410 return false;
517 } 411 }
518 } 412 }
@@ -521,15 +415,15 @@ public class IotRuleSceneServiceImpl implements IotRuleSceneService { @@ -521,15 +415,15 @@ public class IotRuleSceneServiceImpl implements IotRuleSceneService {
521 * 执行规则场景的动作 415 * 执行规则场景的动作
522 * 416 *
523 * @param message 设备消息 417 * @param message 设备消息
524 - * @param ruleScenes 规则场景列表 418 + * @param sceneRules 规则场景列表
525 */ 419 */
526 - private void executeRuleSceneAction(IotDeviceMessage message, List<IotSceneRuleDO> ruleScenes) { 420 + private void executeSceneRuleAction(IotDeviceMessage message, List<IotSceneRuleDO> sceneRules) {
527 // 1. 遍历规则场景 421 // 1. 遍历规则场景
528 - ruleScenes.forEach(ruleScene -> { 422 + sceneRules.forEach(sceneRule -> {
529 // 2. 遍历规则场景的动作 423 // 2. 遍历规则场景的动作
530 - ruleScene.getActions().forEach(actionConfig -> { 424 + sceneRule.getActions().forEach(actionConfig -> {
531 // 3.1 获取对应的动作 Action 数组 425 // 3.1 获取对应的动作 Action 数组
532 - List<IotSceneRuleAction> actions = filterList(ruleSceneActions, 426 + List<IotSceneRuleAction> actions = filterList(sceneRuleActions,
533 action -> action.getType().getType().equals(actionConfig.getType())); 427 action -> action.getType().getType().equals(actionConfig.getType()));
534 if (CollUtil.isEmpty(actions)) { 428 if (CollUtil.isEmpty(actions)) {
535 return; 429 return;
@@ -537,16 +431,20 @@ public class IotRuleSceneServiceImpl implements IotRuleSceneService { @@ -537,16 +431,20 @@ public class IotRuleSceneServiceImpl implements IotRuleSceneService {
537 // 3.2 执行动作 431 // 3.2 执行动作
538 actions.forEach(action -> { 432 actions.forEach(action -> {
539 try { 433 try {
540 - action.execute(message, ruleScene, actionConfig);  
541 - log.info("[executeRuleSceneAction][消息({}) 规则场景编号({}) 的执行动作({}) 成功]",  
542 - message, ruleScene.getId(), actionConfig); 434 + action.execute(message, sceneRule, actionConfig);
  435 + log.info("[executeSceneRuleAction][消息({}) 规则场景编号({}) 的执行动作({}) 成功]",
  436 + message, sceneRule.getId(), actionConfig);
543 } catch (Exception e) { 437 } catch (Exception e) {
544 - log.error("[executeRuleSceneAction][消息({}) 规则场景编号({}) 的执行动作({}) 执行异常]",  
545 - message, ruleScene.getId(), actionConfig, e); 438 + log.error("[executeSceneRuleAction][消息({}) 规则场景编号({}) 的执行动作({}) 执行异常]",
  439 + message, sceneRule.getId(), actionConfig, e);
546 } 440 }
547 }); 441 });
548 }); 442 });
549 }); 443 });
550 } 444 }
551 445
  446 + private IotSceneRuleServiceImpl getSelf() {
  447 + return SpringUtil.getBean(IotSceneRuleServiceImpl.class);
  448 + }
  449 +
552 } 450 }
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/action/IotAlertRecoverSceneRuleAction.java
@@ -5,7 +5,7 @@ import cn.hutool.core.util.StrUtil; @@ -5,7 +5,7 @@ import cn.hutool.core.util.StrUtil;
5 import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; 5 import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
6 import cn.iocoder.yudao.module.iot.dal.dataobject.alert.IotAlertRecordDO; 6 import cn.iocoder.yudao.module.iot.dal.dataobject.alert.IotAlertRecordDO;
7 import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotSceneRuleDO; 7 import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotSceneRuleDO;
8 -import cn.iocoder.yudao.module.iot.enums.rule.IotRuleSceneActionTypeEnum; 8 +import cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleActionTypeEnum;
9 import cn.iocoder.yudao.module.iot.service.alert.IotAlertRecordService; 9 import cn.iocoder.yudao.module.iot.service.alert.IotAlertRecordService;
10 import jakarta.annotation.Resource; 10 import jakarta.annotation.Resource;
11 import org.springframework.stereotype.Component; 11 import org.springframework.stereotype.Component;
@@ -42,8 +42,8 @@ public class IotAlertRecoverSceneRuleAction implements IotSceneRuleAction { @@ -42,8 +42,8 @@ public class IotAlertRecoverSceneRuleAction implements IotSceneRuleAction {
42 } 42 }
43 43
44 @Override 44 @Override
45 - public IotRuleSceneActionTypeEnum getType() {  
46 - return IotRuleSceneActionTypeEnum.ALERT_RECOVER; 45 + public IotSceneRuleActionTypeEnum getType() {
  46 + return IotSceneRuleActionTypeEnum.ALERT_RECOVER;
47 } 47 }
48 48
49 } 49 }
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/action/IotAlertTriggerSceneRuleAction.java
@@ -5,7 +5,7 @@ import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; @@ -5,7 +5,7 @@ import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
5 import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; 5 import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
6 import cn.iocoder.yudao.module.iot.dal.dataobject.alert.IotAlertConfigDO; 6 import cn.iocoder.yudao.module.iot.dal.dataobject.alert.IotAlertConfigDO;
7 import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotSceneRuleDO; 7 import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotSceneRuleDO;
8 -import cn.iocoder.yudao.module.iot.enums.rule.IotRuleSceneActionTypeEnum; 8 +import cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleActionTypeEnum;
9 import cn.iocoder.yudao.module.iot.service.alert.IotAlertConfigService; 9 import cn.iocoder.yudao.module.iot.service.alert.IotAlertConfigService;
10 import cn.iocoder.yudao.module.iot.service.alert.IotAlertRecordService; 10 import cn.iocoder.yudao.module.iot.service.alert.IotAlertRecordService;
11 import cn.iocoder.yudao.module.system.api.mail.MailSendApi; 11 import cn.iocoder.yudao.module.system.api.mail.MailSendApi;
@@ -62,8 +62,8 @@ public class IotAlertTriggerSceneRuleAction implements IotSceneRuleAction { @@ -62,8 +62,8 @@ public class IotAlertTriggerSceneRuleAction implements IotSceneRuleAction {
62 } 62 }
63 63
64 @Override 64 @Override
65 - public IotRuleSceneActionTypeEnum getType() {  
66 - return IotRuleSceneActionTypeEnum.ALERT_TRIGGER; 65 + public IotSceneRuleActionTypeEnum getType() {
  66 + return IotSceneRuleActionTypeEnum.ALERT_TRIGGER;
67 } 67 }
68 68
69 } 69 }
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/action/IotDeviceControlRuleSceneAction.java renamed to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/action/IotDeviceControlSceneRuleAction.java
@@ -2,7 +2,7 @@ package cn.iocoder.yudao.module.iot.service.rule.scene.action; @@ -2,7 +2,7 @@ package cn.iocoder.yudao.module.iot.service.rule.scene.action;
2 2
3 import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; 3 import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
4 import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotSceneRuleDO; 4 import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotSceneRuleDO;
5 -import cn.iocoder.yudao.module.iot.enums.rule.IotRuleSceneActionTypeEnum; 5 +import cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleActionTypeEnum;
6 import cn.iocoder.yudao.module.iot.service.device.IotDeviceService; 6 import cn.iocoder.yudao.module.iot.service.device.IotDeviceService;
7 import cn.iocoder.yudao.module.iot.service.device.message.IotDeviceMessageService; 7 import cn.iocoder.yudao.module.iot.service.device.message.IotDeviceMessageService;
8 import jakarta.annotation.Resource; 8 import jakarta.annotation.Resource;
@@ -16,7 +16,7 @@ import org.springframework.stereotype.Component; @@ -16,7 +16,7 @@ import org.springframework.stereotype.Component;
16 */ 16 */
17 @Component 17 @Component
18 @Slf4j 18 @Slf4j
19 -public class IotDeviceControlRuleSceneAction implements IotSceneRuleAction { 19 +public class IotDeviceControlSceneRuleAction implements IotSceneRuleAction {
20 20
21 @Resource 21 @Resource
22 private IotDeviceService deviceService; 22 private IotDeviceService deviceService;
@@ -48,8 +48,8 @@ public class IotDeviceControlRuleSceneAction implements IotSceneRuleAction { @@ -48,8 +48,8 @@ public class IotDeviceControlRuleSceneAction implements IotSceneRuleAction {
48 } 48 }
49 49
50 @Override 50 @Override
51 - public IotRuleSceneActionTypeEnum getType() {  
52 - return IotRuleSceneActionTypeEnum.DEVICE_PROPERTY_SET; 51 + public IotSceneRuleActionTypeEnum getType() {
  52 + return IotSceneRuleActionTypeEnum.DEVICE_PROPERTY_SET;
53 } 53 }
54 54
55 } 55 }
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/action/IotSceneRuleAction.java
@@ -2,7 +2,7 @@ package cn.iocoder.yudao.module.iot.service.rule.scene.action; @@ -2,7 +2,7 @@ package cn.iocoder.yudao.module.iot.service.rule.scene.action;
2 2
3 import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; 3 import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
4 import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotSceneRuleDO; 4 import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotSceneRuleDO;
5 -import cn.iocoder.yudao.module.iot.enums.rule.IotRuleSceneActionTypeEnum; 5 +import cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleActionTypeEnum;
6 6
7 import javax.annotation.Nullable; 7 import javax.annotation.Nullable;
8 8
@@ -31,6 +31,6 @@ public interface IotSceneRuleAction { @@ -31,6 +31,6 @@ public interface IotSceneRuleAction {
31 * 31 *
32 * @return 类型 32 * @return 类型
33 */ 33 */
34 - IotRuleSceneActionTypeEnum getType(); 34 + IotSceneRuleActionTypeEnum getType();
35 35
36 } 36 }
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/AbstractIotSceneRuleMatcher.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.service.rule.scene.matcher;
  2 +
  3 +import cn.hutool.core.util.NumberUtil;
  4 +import cn.hutool.core.util.StrUtil;
  5 +import cn.iocoder.yudao.framework.common.util.spring.SpringExpressionUtils;
  6 +import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
  7 +import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotSceneRuleDO;
  8 +import cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleConditionOperatorEnum;
  9 +import lombok.extern.slf4j.Slf4j;
  10 +
  11 +import java.util.HashMap;
  12 +import java.util.List;
  13 +import java.util.Map;
  14 +
  15 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
  16 +
  17 +/**
  18 + * IoT 场景规则匹配器抽象基类
  19 + * <p>
  20 + * 提供通用的条件评估逻辑和工具方法,支持触发器和条件两种匹配类型
  21 + *
  22 + * @author HUIHUI
  23 + */
  24 +@Slf4j
  25 +public abstract class AbstractIotSceneRuleMatcher implements IotSceneRuleMatcher {
  26 +
  27 + /**
  28 + * 评估条件是否匹配
  29 + *
  30 + * @param sourceValue 源值(来自消息)
  31 + * @param operator 操作符
  32 + * @param paramValue 参数值(来自条件配置)
  33 + * @return 是否匹配
  34 + */
  35 + protected boolean evaluateCondition(Object sourceValue, String operator, String paramValue) {
  36 + try {
  37 + // 1. 校验操作符是否合法
  38 + IotSceneRuleConditionOperatorEnum operatorEnum = IotSceneRuleConditionOperatorEnum.operatorOf(operator);
  39 + if (operatorEnum == null) {
  40 + log.warn("[evaluateCondition][存在错误的操作符({})]", operator);
  41 + return false;
  42 + }
  43 +
  44 + // 2. 构建 Spring 表达式变量
  45 + Map<String, Object> springExpressionVariables = new HashMap<>();
  46 + springExpressionVariables.put(IotSceneRuleConditionOperatorEnum.SPRING_EXPRESSION_SOURCE, sourceValue);
  47 +
  48 + // 处理参数值
  49 + if (StrUtil.isNotBlank(paramValue)) {
  50 + // 处理多值情况(如 IN、BETWEEN 操作符)
  51 + if (paramValue.contains(",")) {
  52 + List<String> paramValues = StrUtil.split(paramValue, ',');
  53 + springExpressionVariables.put(IotSceneRuleConditionOperatorEnum.SPRING_EXPRESSION_VALUE_LIST,
  54 + convertList(paramValues, NumberUtil::parseDouble));
  55 + } else {
  56 + // 处理单值情况
  57 + springExpressionVariables.put(IotSceneRuleConditionOperatorEnum.SPRING_EXPRESSION_VALUE,
  58 + NumberUtil.parseDouble(paramValue));
  59 + }
  60 + }
  61 +
  62 + // 3. 计算 Spring 表达式
  63 + return (Boolean) SpringExpressionUtils.parseExpression(operatorEnum.getSpringExpression(), springExpressionVariables);
  64 + } catch (Exception e) {
  65 + log.error("[evaluateCondition][条件评估异常] sourceValue: {}, operator: {}, paramValue: {}",
  66 + sourceValue, operator, paramValue, e);
  67 + return false;
  68 + }
  69 + }
  70 +
  71 + // ========== 触发器相关工具方法 ==========
  72 +
  73 + /**
  74 + * 检查基础触发器参数是否有效
  75 + *
  76 + * @param trigger 触发器配置
  77 + * @return 是否有效
  78 + */
  79 + protected boolean isBasicTriggerValid(IotSceneRuleDO.Trigger trigger) {
  80 + return trigger != null && trigger.getType() != null;
  81 + }
  82 +
  83 + /**
  84 + * 检查触发器操作符和值是否有效
  85 + *
  86 + * @param trigger 触发器配置
  87 + * @return 是否有效
  88 + */
  89 + protected boolean isTriggerOperatorAndValueValid(IotSceneRuleDO.Trigger trigger) {
  90 + return StrUtil.isNotBlank(trigger.getOperator()) && StrUtil.isNotBlank(trigger.getValue());
  91 + }
  92 +
  93 + /**
  94 + * 记录触发器匹配成功日志
  95 + *
  96 + * @param message 设备消息
  97 + * @param trigger 触发器配置
  98 + */
  99 + protected void logTriggerMatchSuccess(IotDeviceMessage message, IotSceneRuleDO.Trigger trigger) {
  100 + log.debug("[{}][消息({}) 匹配触发器({}) 成功]", getMatcherName(), message.getRequestId(), trigger.getType());
  101 + }
  102 +
  103 + /**
  104 + * 记录触发器匹配失败日志
  105 + *
  106 + * @param message 设备消息
  107 + * @param trigger 触发器配置
  108 + * @param reason 失败原因
  109 + */
  110 + protected void logTriggerMatchFailure(IotDeviceMessage message, IotSceneRuleDO.Trigger trigger, String reason) {
  111 + log.debug("[{}][消息({}) 匹配触发器({}) 失败: {}]", getMatcherName(), message.getRequestId(), trigger.getType(), reason);
  112 + }
  113 +
  114 + // ========== 条件相关工具方法 ==========
  115 +
  116 + /**
  117 + * 检查基础条件参数是否有效
  118 + *
  119 + * @param condition 触发条件
  120 + * @return 是否有效
  121 + */
  122 + protected boolean isBasicConditionValid(IotSceneRuleDO.TriggerCondition condition) {
  123 + return condition != null && condition.getType() != null;
  124 + }
  125 +
  126 + /**
  127 + * 检查条件操作符和参数是否有效
  128 + *
  129 + * @param condition 触发条件
  130 + * @return 是否有效
  131 + */
  132 + protected boolean isConditionOperatorAndParamValid(IotSceneRuleDO.TriggerCondition condition) {
  133 + return StrUtil.isNotBlank(condition.getOperator()) && StrUtil.isNotBlank(condition.getParam());
  134 + }
  135 +
  136 + /**
  137 + * 记录条件匹配成功日志
  138 + *
  139 + * @param message 设备消息
  140 + * @param condition 触发条件
  141 + */
  142 + protected void logConditionMatchSuccess(IotDeviceMessage message, IotSceneRuleDO.TriggerCondition condition) {
  143 + log.debug("[{}][消息({}) 匹配条件({}) 成功]", getMatcherName(), message.getRequestId(), condition.getType());
  144 + }
  145 +
  146 + /**
  147 + * 记录条件匹配失败日志
  148 + *
  149 + * @param message 设备消息
  150 + * @param condition 触发条件
  151 + * @param reason 失败原因
  152 + */
  153 + protected void logConditionMatchFailure(IotDeviceMessage message, IotSceneRuleDO.TriggerCondition condition, String reason) {
  154 + log.debug("[{}][消息({}) 匹配条件({}) 失败: {}]", getMatcherName(), message.getRequestId(), condition.getType(), reason);
  155 + }
  156 +
  157 + // ========== 通用工具方法 ==========
  158 +
  159 + /**
  160 + * 检查标识符是否匹配
  161 + *
  162 + * @param expectedIdentifier 期望的标识符
  163 + * @param actualIdentifier 实际的标识符
  164 + * @return 是否匹配
  165 + */
  166 + protected boolean isIdentifierMatched(String expectedIdentifier, String actualIdentifier) {
  167 + return StrUtil.isNotBlank(expectedIdentifier) && expectedIdentifier.equals(actualIdentifier);
  168 + }
  169 +
  170 +}
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/CurrentTimeConditionMatcher.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.service.rule.scene.matcher;
  2 +
  3 +import cn.hutool.core.util.StrUtil;
  4 +import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
  5 +import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotSceneRuleDO;
  6 +import cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleConditionTypeEnum;
  7 +import lombok.extern.slf4j.Slf4j;
  8 +import org.springframework.stereotype.Component;
  9 +
  10 +import java.time.LocalDateTime;
  11 +import java.time.LocalTime;
  12 +import java.time.format.DateTimeFormatter;
  13 +
  14 +/**
  15 + * 当前时间条件匹配器
  16 + * <p>
  17 + * 处理时间相关的子条件匹配逻辑
  18 + *
  19 + * @author HUIHUI
  20 + */
  21 +@Component
  22 +@Slf4j
  23 +public class CurrentTimeConditionMatcher extends AbstractIotSceneRuleMatcher {
  24 +
  25 + /**
  26 + * 时间格式化器 - HH:mm:ss
  27 + */
  28 + private static final DateTimeFormatter TIME_FORMATTER = DateTimeFormatter.ofPattern("HH:mm:ss");
  29 +
  30 + /**
  31 + * 时间格式化器 - HH:mm
  32 + */
  33 + private static final DateTimeFormatter TIME_FORMATTER_SHORT = DateTimeFormatter.ofPattern("HH:mm");
  34 +
  35 + @Override
  36 + public MatcherType getMatcherType() {
  37 + return MatcherType.CONDITION;
  38 + }
  39 +
  40 + @Override
  41 + public IotSceneRuleConditionTypeEnum getSupportedConditionType() {
  42 + return IotSceneRuleConditionTypeEnum.CURRENT_TIME;
  43 + }
  44 +
  45 + @Override
  46 + public boolean isMatched(IotDeviceMessage message, IotSceneRuleDO.TriggerCondition condition) {
  47 + // 1. 基础参数校验
  48 + if (!isBasicConditionValid(condition)) {
  49 + logConditionMatchFailure(message, condition, "条件基础参数无效");
  50 + return false;
  51 + }
  52 +
  53 + // 2. 检查操作符和参数是否有效
  54 + if (!isConditionOperatorAndParamValid(condition)) {
  55 + logConditionMatchFailure(message, condition, "操作符或参数无效");
  56 + return false;
  57 + }
  58 +
  59 + // 3. 获取当前时间
  60 + LocalDateTime now = LocalDateTime.now();
  61 +
  62 + // 4. 根据操作符类型进行不同的时间匹配
  63 + String operator = condition.getOperator();
  64 + String param = condition.getParam();
  65 +
  66 + boolean matched = false;
  67 +
  68 + try {
  69 + if (operator.startsWith("date_time_")) {
  70 + // 日期时间匹配(时间戳)
  71 + matched = matchDateTime(now, operator, param);
  72 + } else if (operator.startsWith("time_")) {
  73 + // 当日时间匹配(HH:mm:ss)
  74 + matched = matchTime(now.toLocalTime(), operator, param);
  75 + } else {
  76 + // 其他操作符,使用通用条件评估器
  77 + matched = evaluateCondition(now.toEpochSecond(java.time.ZoneOffset.of("+8")), operator, param);
  78 + }
  79 +
  80 + if (matched) {
  81 + logConditionMatchSuccess(message, condition);
  82 + } else {
  83 + logConditionMatchFailure(message, condition, "时间条件不匹配");
  84 + }
  85 +
  86 + } catch (Exception e) {
  87 + log.error("[CurrentTimeConditionMatcher][时间条件匹配异常] operator: {}, param: {}", operator, param, e);
  88 + logConditionMatchFailure(message, condition, "时间条件匹配异常: " + e.getMessage());
  89 + matched = false;
  90 + }
  91 +
  92 + return matched;
  93 + }
  94 +
  95 + /**
  96 + * 匹配日期时间(时间戳)
  97 + */
  98 + private boolean matchDateTime(LocalDateTime now, String operator, String param) {
  99 + long currentTimestamp = now.toEpochSecond(java.time.ZoneOffset.of("+8"));
  100 + return evaluateCondition(currentTimestamp, operator.substring("date_time_".length()), param);
  101 + }
  102 +
  103 + /**
  104 + * 匹配当日时间(HH:mm:ss)
  105 + */
  106 + private boolean matchTime(LocalTime currentTime, String operator, String param) {
  107 + try {
  108 + String actualOperator = operator.substring("time_".length());
  109 +
  110 + if ("between".equals(actualOperator)) {
  111 + // 时间区间匹配
  112 + String[] timeRange = param.split(",");
  113 + if (timeRange.length != 2) {
  114 + return false;
  115 + }
  116 +
  117 + LocalTime startTime = parseTime(timeRange[0].trim());
  118 + LocalTime endTime = parseTime(timeRange[1].trim());
  119 +
  120 + return !currentTime.isBefore(startTime) && !currentTime.isAfter(endTime);
  121 + } else {
  122 + // 单个时间比较
  123 + LocalTime targetTime = parseTime(param);
  124 +
  125 + switch (actualOperator) {
  126 + case ">":
  127 + return currentTime.isAfter(targetTime);
  128 + case "<":
  129 + return currentTime.isBefore(targetTime);
  130 + case ">=":
  131 + return !currentTime.isBefore(targetTime);
  132 + case "<=":
  133 + return !currentTime.isAfter(targetTime);
  134 + case "=":
  135 + return currentTime.equals(targetTime);
  136 + default:
  137 + return false;
  138 + }
  139 + }
  140 + } catch (Exception e) {
  141 + log.error("[CurrentTimeConditionMatcher][时间解析异常] param: {}", param, e);
  142 + return false;
  143 + }
  144 + }
  145 +
  146 + /**
  147 + * 解析时间字符串
  148 + */
  149 + private LocalTime parseTime(String timeStr) {
  150 + if (StrUtil.isBlank(timeStr)) {
  151 + throw new IllegalArgumentException("时间字符串不能为空");
  152 + }
  153 +
  154 + // 尝试不同的时间格式
  155 + try {
  156 + if (timeStr.length() == 5) { // HH:mm
  157 + return LocalTime.parse(timeStr, TIME_FORMATTER_SHORT);
  158 + } else { // HH:mm:ss
  159 + return LocalTime.parse(timeStr, TIME_FORMATTER);
  160 + }
  161 + } catch (Exception e) {
  162 + throw new IllegalArgumentException("时间格式无效: " + timeStr, e);
  163 + }
  164 + }
  165 +
  166 + @Override
  167 + public int getPriority() {
  168 + return 40; // 较低优先级
  169 + }
  170 +
  171 +}
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/DeviceEventPostTriggerMatcher.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.service.rule.scene.matcher;
  2 +
  3 +import cn.hutool.core.util.StrUtil;
  4 +import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum;
  5 +import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
  6 +import cn.iocoder.yudao.module.iot.core.util.IotDeviceMessageUtils;
  7 +import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotSceneRuleDO;
  8 +import cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleTriggerTypeEnum;
  9 +import org.springframework.stereotype.Component;
  10 +
  11 +/**
  12 + * 设备事件上报触发器匹配器
  13 + * <p>
  14 + * 处理设备事件上报的触发器匹配逻辑
  15 + *
  16 + * @author HUIHUI
  17 + */
  18 +@Component
  19 +public class DeviceEventPostTriggerMatcher extends AbstractIotSceneRuleMatcher {
  20 +
  21 + /**
  22 + * 设备事件上报消息方法
  23 + */
  24 + private static final String DEVICE_EVENT_POST_METHOD = IotDeviceMessageMethodEnum.EVENT_POST.getMethod();
  25 +
  26 + @Override
  27 + public MatcherType getMatcherType() {
  28 + return MatcherType.TRIGGER;
  29 + }
  30 +
  31 + @Override
  32 + public IotSceneRuleTriggerTypeEnum getSupportedTriggerType() {
  33 + return IotSceneRuleTriggerTypeEnum.DEVICE_EVENT_POST;
  34 + }
  35 +
  36 + @Override
  37 + public boolean isMatched(IotDeviceMessage message, IotSceneRuleDO.Trigger trigger) {
  38 + // 1. 基础参数校验
  39 + if (!isBasicTriggerValid(trigger)) {
  40 + logTriggerMatchFailure(message, trigger, "触发器基础参数无效");
  41 + return false;
  42 + }
  43 +
  44 + // 2. 检查消息方法是否匹配
  45 + if (!DEVICE_EVENT_POST_METHOD.equals(message.getMethod())) {
  46 + logTriggerMatchFailure(message, trigger, "消息方法不匹配,期望: " + DEVICE_EVENT_POST_METHOD + ", 实际: " + message.getMethod());
  47 + return false;
  48 + }
  49 +
  50 + // 3. 检查标识符是否匹配
  51 + String messageIdentifier = IotDeviceMessageUtils.getIdentifier(message);
  52 + if (!isIdentifierMatched(trigger.getIdentifier(), messageIdentifier)) {
  53 + logTriggerMatchFailure(message, trigger, "标识符不匹配,期望: " + trigger.getIdentifier() + ", 实际: " + messageIdentifier);
  54 + return false;
  55 + }
  56 +
  57 + // 4. 对于事件触发器,通常不需要检查操作符和值,只要事件发生即匹配
  58 + // 但如果配置了操作符和值,则需要进行条件匹配
  59 + if (StrUtil.isNotBlank(trigger.getOperator()) && StrUtil.isNotBlank(trigger.getValue())) {
  60 + Object eventData = message.getData();
  61 + if (eventData == null) {
  62 + logTriggerMatchFailure(message, trigger, "消息中事件数据为空");
  63 + return false;
  64 + }
  65 +
  66 + boolean matched = evaluateCondition(eventData, trigger.getOperator(), trigger.getValue());
  67 + if (!matched) {
  68 + logTriggerMatchFailure(message, trigger, "事件数据条件不匹配");
  69 + return false;
  70 + }
  71 + }
  72 +
  73 + logTriggerMatchSuccess(message, trigger);
  74 + return true;
  75 + }
  76 +
  77 + @Override
  78 + public int getPriority() {
  79 + return 30; // 中等优先级
  80 + }
  81 +
  82 +}
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/DevicePropertyConditionMatcher.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.service.rule.scene.matcher;
  2 +
  3 +import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
  4 +import cn.iocoder.yudao.module.iot.core.util.IotDeviceMessageUtils;
  5 +import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotSceneRuleDO;
  6 +import cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleConditionTypeEnum;
  7 +import org.springframework.stereotype.Component;
  8 +
  9 +/**
  10 + * 设备属性条件匹配器
  11 + * <p>
  12 + * 处理设备属性相关的子条件匹配逻辑
  13 + *
  14 + * @author HUIHUI
  15 + */
  16 +@Component
  17 +public class DevicePropertyConditionMatcher extends AbstractIotSceneRuleMatcher {
  18 +
  19 + @Override
  20 + public MatcherType getMatcherType() {
  21 + return MatcherType.CONDITION;
  22 + }
  23 +
  24 + @Override
  25 + public IotSceneRuleConditionTypeEnum getSupportedConditionType() {
  26 + return IotSceneRuleConditionTypeEnum.DEVICE_PROPERTY;
  27 + }
  28 +
  29 + @Override
  30 + public boolean isMatched(IotDeviceMessage message, IotSceneRuleDO.TriggerCondition condition) {
  31 + // 1. 基础参数校验
  32 + if (!isBasicConditionValid(condition)) {
  33 + logConditionMatchFailure(message, condition, "条件基础参数无效");
  34 + return false;
  35 + }
  36 +
  37 + // 2. 检查标识符是否匹配
  38 + String messageIdentifier = IotDeviceMessageUtils.getIdentifier(message);
  39 + if (!isIdentifierMatched(condition.getIdentifier(), messageIdentifier)) {
  40 + logConditionMatchFailure(message, condition, "标识符不匹配,期望: " + condition.getIdentifier() + ", 实际: " + messageIdentifier);
  41 + return false;
  42 + }
  43 +
  44 + // 3. 检查操作符和参数是否有效
  45 + if (!isConditionOperatorAndParamValid(condition)) {
  46 + logConditionMatchFailure(message, condition, "操作符或参数无效");
  47 + return false;
  48 + }
  49 +
  50 + // 4. 获取属性值
  51 + Object propertyValue = message.getData();
  52 + if (propertyValue == null) {
  53 + logConditionMatchFailure(message, condition, "消息中属性值为空");
  54 + return false;
  55 + }
  56 +
  57 + // 5. 使用条件评估器进行匹配
  58 + boolean matched = evaluateCondition(propertyValue, condition.getOperator(), condition.getParam());
  59 +
  60 + if (matched) {
  61 + logConditionMatchSuccess(message, condition);
  62 + } else {
  63 + logConditionMatchFailure(message, condition, "设备属性条件不匹配");
  64 + }
  65 +
  66 + return matched;
  67 + }
  68 +
  69 + @Override
  70 + public int getPriority() {
  71 + return 25; // 中等优先级
  72 + }
  73 +
  74 +}
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/DevicePropertyPostTriggerMatcher.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.service.rule.scene.matcher;
  2 +
  3 +import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum;
  4 +import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
  5 +import cn.iocoder.yudao.module.iot.core.util.IotDeviceMessageUtils;
  6 +import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotSceneRuleDO;
  7 +import cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleTriggerTypeEnum;
  8 +import org.springframework.stereotype.Component;
  9 +
  10 +/**
  11 + * 设备属性上报触发器匹配器
  12 + * <p>
  13 + * 处理设备属性数据上报的触发器匹配逻辑
  14 + *
  15 + * @author HUIHUI
  16 + */
  17 +@Component
  18 +public class DevicePropertyPostTriggerMatcher extends AbstractIotSceneRuleMatcher {
  19 +
  20 + /**
  21 + * 设备属性上报消息方法
  22 + */
  23 + private static final String DEVICE_PROPERTY_POST_METHOD = IotDeviceMessageMethodEnum.PROPERTY_POST.getMethod();
  24 +
  25 + @Override
  26 + public MatcherType getMatcherType() {
  27 + return MatcherType.TRIGGER;
  28 + }
  29 +
  30 + @Override
  31 + public IotSceneRuleTriggerTypeEnum getSupportedTriggerType() {
  32 + return IotSceneRuleTriggerTypeEnum.DEVICE_PROPERTY_POST;
  33 + }
  34 +
  35 + @Override
  36 + public boolean isMatched(IotDeviceMessage message, IotSceneRuleDO.Trigger trigger) {
  37 + // 1. 基础参数校验
  38 + if (!isBasicTriggerValid(trigger)) {
  39 + logTriggerMatchFailure(message, trigger, "触发器基础参数无效");
  40 + return false;
  41 + }
  42 +
  43 + // 2. 检查消息方法是否匹配
  44 + if (!DEVICE_PROPERTY_POST_METHOD.equals(message.getMethod())) {
  45 + logTriggerMatchFailure(message, trigger, "消息方法不匹配,期望: " + DEVICE_PROPERTY_POST_METHOD + ", 实际: " + message.getMethod());
  46 + return false;
  47 + }
  48 +
  49 + // 3. 检查标识符是否匹配
  50 + String messageIdentifier = IotDeviceMessageUtils.getIdentifier(message);
  51 + if (!isIdentifierMatched(trigger.getIdentifier(), messageIdentifier)) {
  52 + logTriggerMatchFailure(message, trigger, "标识符不匹配,期望: " + trigger.getIdentifier() + ", 实际: " + messageIdentifier);
  53 + return false;
  54 + }
  55 +
  56 + // 4. 检查操作符和值是否有效
  57 + if (!isTriggerOperatorAndValueValid(trigger)) {
  58 + logTriggerMatchFailure(message, trigger, "操作符或值无效");
  59 + return false;
  60 + }
  61 +
  62 + // 5. 获取属性值
  63 + Object propertyValue = message.getData();
  64 + if (propertyValue == null) {
  65 + logTriggerMatchFailure(message, trigger, "消息中属性值为空");
  66 + return false;
  67 + }
  68 +
  69 + // 6. 使用条件评估器进行匹配
  70 + boolean matched = evaluateCondition(propertyValue, trigger.getOperator(), trigger.getValue());
  71 +
  72 + if (matched) {
  73 + logTriggerMatchSuccess(message, trigger);
  74 + } else {
  75 + logTriggerMatchFailure(message, trigger, "属性值条件不匹配");
  76 + }
  77 +
  78 + return matched;
  79 + }
  80 +
  81 + @Override
  82 + public int getPriority() {
  83 + return 20; // 中等优先级
  84 + }
  85 +
  86 +}
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/DeviceServiceInvokeTriggerMatcher.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.service.rule.scene.matcher;
  2 +
  3 +import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum;
  4 +import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
  5 +import cn.iocoder.yudao.module.iot.core.util.IotDeviceMessageUtils;
  6 +import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotSceneRuleDO;
  7 +import cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleTriggerTypeEnum;
  8 +import org.springframework.stereotype.Component;
  9 +
  10 +/**
  11 + * 设备服务调用触发器匹配器
  12 + * <p>
  13 + * 处理设备服务调用的触发器匹配逻辑
  14 + *
  15 + * @author HUIHUI
  16 + */
  17 +@Component
  18 +public class DeviceServiceInvokeTriggerMatcher extends AbstractIotSceneRuleMatcher {
  19 +
  20 + /**
  21 + * 设备服务调用消息方法
  22 + */
  23 + private static final String DEVICE_SERVICE_INVOKE_METHOD = IotDeviceMessageMethodEnum.SERVICE_INVOKE.getMethod();
  24 +
  25 + @Override
  26 + public MatcherType getMatcherType() {
  27 + return MatcherType.TRIGGER;
  28 + }
  29 +
  30 + @Override
  31 + public IotSceneRuleTriggerTypeEnum getSupportedTriggerType() {
  32 + return IotSceneRuleTriggerTypeEnum.DEVICE_SERVICE_INVOKE;
  33 + }
  34 +
  35 + @Override
  36 + public boolean isMatched(IotDeviceMessage message, IotSceneRuleDO.Trigger trigger) {
  37 + // 1. 基础参数校验
  38 + if (!isBasicTriggerValid(trigger)) {
  39 + logTriggerMatchFailure(message, trigger, "触发器基础参数无效");
  40 + return false;
  41 + }
  42 +
  43 + // 2. 检查消息方法是否匹配
  44 + if (!DEVICE_SERVICE_INVOKE_METHOD.equals(message.getMethod())) {
  45 + logTriggerMatchFailure(message, trigger, "消息方法不匹配,期望: " + DEVICE_SERVICE_INVOKE_METHOD + ", 实际: " + message.getMethod());
  46 + return false;
  47 + }
  48 +
  49 + // 3. 检查标识符是否匹配
  50 + String messageIdentifier = IotDeviceMessageUtils.getIdentifier(message);
  51 + if (!isIdentifierMatched(trigger.getIdentifier(), messageIdentifier)) {
  52 + logTriggerMatchFailure(message, trigger, "标识符不匹配,期望: " + trigger.getIdentifier() + ", 实际: " + messageIdentifier);
  53 + return false;
  54 + }
  55 +
  56 + // 4. 对于服务调用触发器,通常只需要匹配服务标识符即可
  57 + // 不需要检查操作符和值,因为服务调用本身就是触发条件
  58 +
  59 + logTriggerMatchSuccess(message, trigger);
  60 + return true;
  61 + }
  62 +
  63 + @Override
  64 + public int getPriority() {
  65 + return 40; // 较低优先级
  66 + }
  67 +
  68 +}
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/DeviceStateConditionMatcher.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.service.rule.scene.matcher;
  2 +
  3 +import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
  4 +import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotSceneRuleDO;
  5 +import cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleConditionTypeEnum;
  6 +import org.springframework.stereotype.Component;
  7 +
  8 +/**
  9 + * 设备状态条件匹配器
  10 + * <p>
  11 + * 处理设备状态相关的子条件匹配逻辑
  12 + *
  13 + * @author HUIHUI
  14 + */
  15 +@Component
  16 +public class DeviceStateConditionMatcher extends AbstractIotSceneRuleMatcher {
  17 +
  18 + @Override
  19 + public MatcherType getMatcherType() {
  20 + return MatcherType.CONDITION;
  21 + }
  22 +
  23 + @Override
  24 + public IotSceneRuleConditionTypeEnum getSupportedConditionType() {
  25 + return IotSceneRuleConditionTypeEnum.DEVICE_STATE;
  26 + }
  27 +
  28 + @Override
  29 + public boolean isMatched(IotDeviceMessage message, IotSceneRuleDO.TriggerCondition condition) {
  30 + // 1. 基础参数校验
  31 + if (!isBasicConditionValid(condition)) {
  32 + logConditionMatchFailure(message, condition, "条件基础参数无效");
  33 + return false;
  34 + }
  35 +
  36 + // 2. 检查操作符和参数是否有效
  37 + if (!isConditionOperatorAndParamValid(condition)) {
  38 + logConditionMatchFailure(message, condition, "操作符或参数无效");
  39 + return false;
  40 + }
  41 +
  42 + // 3. 获取设备状态值
  43 + // 设备状态通常在消息的 data 字段中
  44 + Object stateValue = message.getData();
  45 + if (stateValue == null) {
  46 + logConditionMatchFailure(message, condition, "消息中设备状态值为空");
  47 + return false;
  48 + }
  49 +
  50 + // 4. 使用条件评估器进行匹配
  51 + boolean matched = evaluateCondition(stateValue, condition.getOperator(), condition.getParam());
  52 +
  53 + if (matched) {
  54 + logConditionMatchSuccess(message, condition);
  55 + } else {
  56 + logConditionMatchFailure(message, condition, "设备状态条件不匹配");
  57 + }
  58 +
  59 + return matched;
  60 + }
  61 +
  62 + @Override
  63 + public int getPriority() {
  64 + return 30; // 中等优先级
  65 + }
  66 +
  67 +}
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/DeviceStateUpdateTriggerMatcher.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.service.rule.scene.matcher;
  2 +
  3 +import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum;
  4 +import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
  5 +import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotSceneRuleDO;
  6 +import cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleTriggerTypeEnum;
  7 +import org.springframework.stereotype.Component;
  8 +
  9 +/**
  10 + * 设备状态更新触发器匹配器
  11 + * <p>
  12 + * 处理设备上下线状态变更的触发器匹配逻辑
  13 + *
  14 + * @author HUIHUI
  15 + */
  16 +@Component
  17 +public class DeviceStateUpdateTriggerMatcher extends AbstractIotSceneRuleMatcher {
  18 +
  19 + /**
  20 + * 设备状态更新消息方法
  21 + */
  22 + private static final String DEVICE_STATE_UPDATE_METHOD = IotDeviceMessageMethodEnum.STATE_UPDATE.getMethod();
  23 +
  24 + @Override
  25 + public MatcherType getMatcherType() {
  26 + return MatcherType.TRIGGER;
  27 + }
  28 +
  29 + @Override
  30 + public IotSceneRuleTriggerTypeEnum getSupportedTriggerType() {
  31 + return IotSceneRuleTriggerTypeEnum.DEVICE_STATE_UPDATE;
  32 + }
  33 +
  34 + @Override
  35 + public boolean isMatched(IotDeviceMessage message, IotSceneRuleDO.Trigger trigger) {
  36 + // 1. 基础参数校验
  37 + if (!isBasicTriggerValid(trigger)) {
  38 + logTriggerMatchFailure(message, trigger, "触发器基础参数无效");
  39 + return false;
  40 + }
  41 +
  42 + // 2. 检查消息方法是否匹配
  43 + if (!DEVICE_STATE_UPDATE_METHOD.equals(message.getMethod())) {
  44 + logTriggerMatchFailure(message, trigger, "消息方法不匹配,期望: " + DEVICE_STATE_UPDATE_METHOD + ", 实际: " + message.getMethod());
  45 + return false;
  46 + }
  47 +
  48 + // 3. 检查操作符和值是否有效
  49 + if (!isTriggerOperatorAndValueValid(trigger)) {
  50 + logTriggerMatchFailure(message, trigger, "操作符或值无效");
  51 + return false;
  52 + }
  53 +
  54 + // 4. 获取设备状态值
  55 + Object stateValue = message.getData();
  56 + if (stateValue == null) {
  57 + logTriggerMatchFailure(message, trigger, "消息中设备状态值为空");
  58 + return false;
  59 + }
  60 +
  61 + // 5. 使用条件评估器进行匹配
  62 + boolean matched = evaluateCondition(stateValue, trigger.getOperator(), trigger.getValue());
  63 +
  64 + if (matched) {
  65 + logTriggerMatchSuccess(message, trigger);
  66 + } else {
  67 + logTriggerMatchFailure(message, trigger, "状态值条件不匹配");
  68 + }
  69 +
  70 + return matched;
  71 + }
  72 +
  73 + @Override
  74 + public int getPriority() {
  75 + return 10; // 高优先级
  76 + }
  77 +
  78 +}
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/IotSceneRuleMatcher.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.service.rule.scene.matcher;
  2 +
  3 +import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
  4 +import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotSceneRuleDO;
  5 +import cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleConditionTypeEnum;
  6 +import cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleTriggerTypeEnum;
  7 +
  8 +/**
  9 + * IoT 场景规则匹配器统一接口
  10 + * <p>
  11 + * 支持触发器匹配和条件匹配两种类型,遵循策略模式设计
  12 + * <p>
  13 + * 匹配器类型说明:
  14 + * - 触发器匹配器:用于匹配主触发条件(如设备消息类型、定时器等)
  15 + * - 条件匹配器:用于匹配子条件(如设备状态、属性值、时间条件等)
  16 + *
  17 + * @author HUIHUI
  18 + */
  19 +public interface IotSceneRuleMatcher {
  20 +
  21 + /**
  22 + * 匹配器类型枚举
  23 + */
  24 + enum MatcherType {
  25 + /**
  26 + * 触发器匹配器 - 用于匹配主触发条件
  27 + */
  28 + TRIGGER,
  29 + /**
  30 + * 条件匹配器 - 用于匹配子条件
  31 + */
  32 + CONDITION
  33 + }
  34 +
  35 + /**
  36 + * 获取匹配器类型
  37 + *
  38 + * @return 匹配器类型
  39 + */
  40 + MatcherType getMatcherType();
  41 +
  42 + /**
  43 + * 获取支持的触发器类型(仅触发器匹配器需要实现)
  44 + *
  45 + * @return 触发器类型枚举,条件匹配器返回 null
  46 + */
  47 + default IotSceneRuleTriggerTypeEnum getSupportedTriggerType() {
  48 + return null;
  49 + }
  50 +
  51 + /**
  52 + * 获取支持的条件类型(仅条件匹配器需要实现)
  53 + *
  54 + * @return 条件类型枚举,触发器匹配器返回 null
  55 + */
  56 + default IotSceneRuleConditionTypeEnum getSupportedConditionType() {
  57 + return null;
  58 + }
  59 +
  60 + /**
  61 + * 检查触发器是否匹配消息(仅触发器匹配器需要实现)
  62 + *
  63 + * @param message 设备消息
  64 + * @param trigger 触发器配置
  65 + * @return 是否匹配
  66 + */
  67 + default boolean isMatched(IotDeviceMessage message, IotSceneRuleDO.Trigger trigger) {
  68 + throw new UnsupportedOperationException("触发器匹配方法仅支持触发器匹配器");
  69 + }
  70 +
  71 + /**
  72 + * 检查条件是否匹配消息(仅条件匹配器需要实现)
  73 + *
  74 + * @param message 设备消息
  75 + * @param condition 触发条件
  76 + * @return 是否匹配
  77 + */
  78 + default boolean isMatched(IotDeviceMessage message, IotSceneRuleDO.TriggerCondition condition) {
  79 + throw new UnsupportedOperationException("条件匹配方法仅支持条件匹配器");
  80 + }
  81 +
  82 + /**
  83 + * 获取匹配优先级(数值越小优先级越高)
  84 + * <p>
  85 + * 用于在多个匹配器支持同一类型时确定优先级
  86 + *
  87 + * @return 优先级数值
  88 + */
  89 + default int getPriority() {
  90 + return 100;
  91 + }
  92 +
  93 + /**
  94 + * 获取匹配器名称,用于日志和调试
  95 + *
  96 + * @return 匹配器名称
  97 + */
  98 + default String getMatcherName() {
  99 + return this.getClass().getSimpleName();
  100 + }
  101 +
  102 + /**
  103 + * 是否启用该匹配器
  104 + * <p>
  105 + * 可用于动态开关某些匹配器
  106 + *
  107 + * @return 是否启用
  108 + */
  109 + default boolean isEnabled() {
  110 + return true;
  111 + }
  112 +
  113 +}
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/IotSceneRuleMatcherManager.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.service.rule.scene.matcher;
  2 +
  3 +import cn.hutool.core.collection.CollUtil;
  4 +import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
  5 +import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotSceneRuleDO;
  6 +import cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleConditionTypeEnum;
  7 +import cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleTriggerTypeEnum;
  8 +import lombok.extern.slf4j.Slf4j;
  9 +import org.springframework.stereotype.Component;
  10 +
  11 +import java.util.*;
  12 +import java.util.function.Function;
  13 +import java.util.stream.Collectors;
  14 +
  15 +import static cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleTriggerTypeEnum.findTriggerTypeEnum;
  16 +
  17 +/**
  18 + * IoT 场景规则匹配器统一管理器
  19 + * <p>
  20 + * 负责管理所有匹配器(触发器匹配器和条件匹配器),并提供统一的匹配入口
  21 + *
  22 + * @author HUIHUI
  23 + */
  24 +@Component
  25 +@Slf4j
  26 +public class IotSceneRuleMatcherManager {
  27 +
  28 + /**
  29 + * 触发器匹配器映射表
  30 + * Key: 触发器类型枚举
  31 + * Value: 对应的匹配器实例
  32 + */
  33 + private final Map<IotSceneRuleTriggerTypeEnum, IotSceneRuleMatcher> triggerMatcherMap;
  34 +
  35 + /**
  36 + * 条件匹配器映射表
  37 + * Key: 条件类型枚举
  38 + * Value: 对应的匹配器实例
  39 + */
  40 + private final Map<IotSceneRuleConditionTypeEnum, IotSceneRuleMatcher> conditionMatcherMap;
  41 +
  42 + /**
  43 + * 所有匹配器列表(按优先级排序)
  44 + */
  45 + private final List<IotSceneRuleMatcher> allMatchers;
  46 +
  47 + public IotSceneRuleMatcherManager(List<IotSceneRuleMatcher> matchers) {
  48 + if (CollUtil.isEmpty(matchers)) {
  49 + log.warn("[IotSceneRuleMatcherManager][没有找到任何匹配器]");
  50 + this.triggerMatcherMap = new HashMap<>();
  51 + this.conditionMatcherMap = new HashMap<>();
  52 + this.allMatchers = new ArrayList<>();
  53 + return;
  54 + }
  55 +
  56 + // 按优先级排序并过滤启用的匹配器
  57 + this.allMatchers = matchers.stream()
  58 + .filter(IotSceneRuleMatcher::isEnabled)
  59 + .sorted(Comparator.comparing(IotSceneRuleMatcher::getPriority))
  60 + .collect(Collectors.toList());
  61 +
  62 + // 分离触发器匹配器和条件匹配器
  63 + List<IotSceneRuleMatcher> triggerMatchers = this.allMatchers.stream()
  64 + .filter(matcher -> matcher.getMatcherType() == IotSceneRuleMatcher.MatcherType.TRIGGER)
  65 + .toList();
  66 +
  67 + List<IotSceneRuleMatcher> conditionMatchers = this.allMatchers.stream()
  68 + .filter(matcher -> matcher.getMatcherType() == IotSceneRuleMatcher.MatcherType.CONDITION)
  69 + .toList();
  70 +
  71 + // 构建触发器匹配器映射表
  72 + this.triggerMatcherMap = triggerMatchers.stream()
  73 + .collect(Collectors.toMap(
  74 + IotSceneRuleMatcher::getSupportedTriggerType,
  75 + Function.identity(),
  76 + (existing, replacement) -> {
  77 + log.warn("[IotSceneRuleMatcherManager][触发器类型({})存在多个匹配器,使用优先级更高的: {}]",
  78 + existing.getSupportedTriggerType(),
  79 + existing.getPriority() <= replacement.getPriority() ? existing.getMatcherName() : replacement.getMatcherName());
  80 + return existing.getPriority() <= replacement.getPriority() ? existing : replacement;
  81 + },
  82 + LinkedHashMap::new
  83 + ));
  84 +
  85 + // 构建条件匹配器映射表
  86 + this.conditionMatcherMap = conditionMatchers.stream()
  87 + .collect(Collectors.toMap(
  88 + IotSceneRuleMatcher::getSupportedConditionType,
  89 + Function.identity(),
  90 + (existing, replacement) -> {
  91 + log.warn("[IotSceneRuleMatcherManager][条件类型({})存在多个匹配器,使用优先级更高的: {}]",
  92 + existing.getSupportedConditionType(),
  93 + existing.getPriority() <= replacement.getPriority() ? existing.getMatcherName() : replacement.getMatcherName());
  94 + return existing.getPriority() <= replacement.getPriority() ? existing : replacement;
  95 + },
  96 + LinkedHashMap::new
  97 + ));
  98 +
  99 + log.info("[IotSceneRuleMatcherManager][初始化完成,共加载 {} 个匹配器,其中触发器匹配器 {} 个,条件匹配器 {} 个]",
  100 + this.allMatchers.size(), this.triggerMatcherMap.size(), this.conditionMatcherMap.size());
  101 +
  102 + // 记录触发器匹配器详情
  103 + this.triggerMatcherMap.forEach((type, matcher) ->
  104 + log.info("[IotSceneRuleMatcherManager][触发器匹配器] 类型: {}, 匹配器: {}, 优先级: {}",
  105 + type, matcher.getMatcherName(), matcher.getPriority()));
  106 +
  107 + // 记录条件匹配器详情
  108 + this.conditionMatcherMap.forEach((type, matcher) ->
  109 + log.info("[IotSceneRuleMatcherManager][条件匹配器] 类型: {}, 匹配器: {}, 优先级: {}",
  110 + type, matcher.getMatcherName(), matcher.getPriority()));
  111 + }
  112 +
  113 + /**
  114 + * 检查触发器是否匹配消息(主条件匹配)
  115 + *
  116 + * @param message 设备消息
  117 + * @param trigger 触发器配置
  118 + * @return 是否匹配
  119 + */
  120 + public boolean isMatched(IotDeviceMessage message, IotSceneRuleDO.Trigger trigger) {
  121 + if (message == null || trigger == null || trigger.getType() == null) {
  122 + log.debug("[isMatched][参数无效] message: {}, trigger: {}", message, trigger);
  123 + return false;
  124 + }
  125 +
  126 + // 根据触发器类型查找对应的匹配器
  127 + IotSceneRuleTriggerTypeEnum triggerType = findTriggerTypeEnum(trigger.getType());
  128 + if (triggerType == null) {
  129 + log.warn("[isMatched][未知的触发器类型: {}]", trigger.getType());
  130 + return false;
  131 + }
  132 +
  133 + IotSceneRuleMatcher matcher = triggerMatcherMap.get(triggerType);
  134 + if (matcher == null) {
  135 + log.warn("[isMatched][触发器类型({})没有对应的匹配器]", triggerType);
  136 + return false;
  137 + }
  138 +
  139 + try {
  140 + return matcher.isMatched(message, trigger);
  141 + } catch (Exception e) {
  142 + log.error("[isMatched][触发器匹配异常] message: {}, trigger: {}, matcher: {}",
  143 + message, trigger, matcher.getMatcherName(), e);
  144 + return false;
  145 + }
  146 + }
  147 +
  148 + /**
  149 + * 检查子条件是否匹配消息
  150 + *
  151 + * @param message 设备消息
  152 + * @param condition 触发条件
  153 + * @return 是否匹配
  154 + */
  155 + public boolean isConditionMatched(IotDeviceMessage message, IotSceneRuleDO.TriggerCondition condition) {
  156 + if (message == null || condition == null || condition.getType() == null) {
  157 + log.debug("[isConditionMatched][参数无效] message: {}, condition: {}", message, condition);
  158 + return false;
  159 + }
  160 +
  161 + // 根据条件类型查找对应的匹配器
  162 + IotSceneRuleConditionTypeEnum conditionType = findConditionTypeEnum(condition.getType());
  163 + if (conditionType == null) {
  164 + log.warn("[isConditionMatched][未知的条件类型: {}]", condition.getType());
  165 + return false;
  166 + }
  167 +
  168 + IotSceneRuleMatcher matcher = conditionMatcherMap.get(conditionType);
  169 + if (matcher == null) {
  170 + log.warn("[isConditionMatched][条件类型({})没有对应的匹配器]", conditionType);
  171 + return false;
  172 + }
  173 +
  174 + try {
  175 + return matcher.isMatched(message, condition);
  176 + } catch (Exception e) {
  177 + log.error("[isConditionMatched][条件匹配异常] message: {}, condition: {}, matcher: {}",
  178 + message, condition, matcher.getMatcherName(), e);
  179 + return false;
  180 + }
  181 + }
  182 +
  183 + /**
  184 + * 根据类型值查找条件类型枚举
  185 + *
  186 + * @param typeValue 类型值
  187 + * @return 条件类型枚举
  188 + */
  189 + private IotSceneRuleConditionTypeEnum findConditionTypeEnum(Integer typeValue) {
  190 + return Arrays.stream(IotSceneRuleConditionTypeEnum.values())
  191 + .filter(type -> type.getType().equals(typeValue))
  192 + .findFirst()
  193 + .orElse(null);
  194 + }
  195 +
  196 + /**
  197 + * 获取所有支持的触发器类型
  198 + *
  199 + * @return 支持的触发器类型列表
  200 + */
  201 + public Set<IotSceneRuleTriggerTypeEnum> getSupportedTriggerTypes() {
  202 + return new HashSet<>(triggerMatcherMap.keySet());
  203 + }
  204 +
  205 + /**
  206 + * 获取所有支持的条件类型
  207 + *
  208 + * @return 支持的条件类型列表
  209 + */
  210 + public Set<IotSceneRuleConditionTypeEnum> getSupportedConditionTypes() {
  211 + return new HashSet<>(conditionMatcherMap.keySet());
  212 + }
  213 +
  214 + /**
  215 + * 获取指定触发器类型的匹配器
  216 + *
  217 + * @param triggerType 触发器类型
  218 + * @return 匹配器实例,如果不存在则返回 null
  219 + */
  220 + public IotSceneRuleMatcher getTriggerMatcher(IotSceneRuleTriggerTypeEnum triggerType) {
  221 + return triggerMatcherMap.get(triggerType);
  222 + }
  223 +
  224 + /**
  225 + * 获取指定条件类型的匹配器
  226 + *
  227 + * @param conditionType 条件类型
  228 + * @return 匹配器实例,如果不存在则返回 null
  229 + */
  230 + public IotSceneRuleMatcher getConditionMatcher(IotSceneRuleConditionTypeEnum conditionType) {
  231 + return conditionMatcherMap.get(conditionType);
  232 + }
  233 +
  234 + /**
  235 + * 获取所有匹配器的统计信息
  236 + *
  237 + * @return 统计信息映射表
  238 + */
  239 + public Map<String, Object> getMatcherStatistics() {
  240 + Map<String, Object> statistics = new HashMap<>();
  241 + statistics.put("totalMatchers", allMatchers.size());
  242 + statistics.put("triggerMatchers", triggerMatcherMap.size());
  243 + statistics.put("conditionMatchers", conditionMatcherMap.size());
  244 + statistics.put("supportedTriggerTypes", getSupportedTriggerTypes());
  245 + statistics.put("supportedConditionTypes", getSupportedConditionTypes());
  246 +
  247 + // 触发器匹配器详情
  248 + Map<String, Object> triggerMatcherDetails = new HashMap<>();
  249 + triggerMatcherMap.forEach((type, matcher) -> {
  250 + Map<String, Object> detail = new HashMap<>();
  251 + detail.put("matcherName", matcher.getMatcherName());
  252 + detail.put("priority", matcher.getPriority());
  253 + detail.put("enabled", matcher.isEnabled());
  254 + triggerMatcherDetails.put(type.name(), detail);
  255 + });
  256 + statistics.put("triggerMatcherDetails", triggerMatcherDetails);
  257 +
  258 + // 条件匹配器详情
  259 + Map<String, Object> conditionMatcherDetails = new HashMap<>();
  260 + conditionMatcherMap.forEach((type, matcher) -> {
  261 + Map<String, Object> detail = new HashMap<>();
  262 + detail.put("matcherName", matcher.getMatcherName());
  263 + detail.put("priority", matcher.getPriority());
  264 + detail.put("enabled", matcher.isEnabled());
  265 + conditionMatcherDetails.put(type.name(), detail);
  266 + });
  267 + statistics.put("conditionMatcherDetails", conditionMatcherDetails);
  268 +
  269 + return statistics;
  270 + }
  271 +
  272 +}
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/TimerTriggerMatcher.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.service.rule.scene.matcher;
  2 +
  3 +import cn.hutool.core.util.StrUtil;
  4 +import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
  5 +import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotSceneRuleDO;
  6 +import cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleTriggerTypeEnum;
  7 +import org.springframework.stereotype.Component;
  8 +
  9 +/**
  10 + * 定时触发器匹配器
  11 + * <p>
  12 + * 处理定时触发的触发器匹配逻辑
  13 + * 注意:定时触发器不依赖设备消息,主要用于定时任务场景
  14 + *
  15 + * @author HUIHUI
  16 + */
  17 +@Component
  18 +public class TimerTriggerMatcher extends AbstractIotSceneRuleMatcher {
  19 +
  20 + @Override
  21 + public MatcherType getMatcherType() {
  22 + return MatcherType.TRIGGER;
  23 + }
  24 +
  25 + @Override
  26 + public IotSceneRuleTriggerTypeEnum getSupportedTriggerType() {
  27 + return IotSceneRuleTriggerTypeEnum.TIMER;
  28 + }
  29 +
  30 + @Override
  31 + public boolean isMatched(IotDeviceMessage message, IotSceneRuleDO.Trigger trigger) {
  32 + // 1. 基础参数校验
  33 + if (!isBasicTriggerValid(trigger)) {
  34 + logTriggerMatchFailure(message, trigger, "触发器基础参数无效");
  35 + return false;
  36 + }
  37 +
  38 + // 2. 检查 CRON 表达式是否存在
  39 + if (StrUtil.isBlank(trigger.getCronExpression())) {
  40 + logTriggerMatchFailure(message, trigger, "定时触发器缺少 CRON 表达式");
  41 + return false;
  42 + }
  43 +
  44 + // 3. 定时触发器通常不依赖具体的设备消息
  45 + // 它是通过定时任务调度器触发的,这里主要是验证配置的有效性
  46 +
  47 + // 4. 可以添加 CRON 表达式格式验证
  48 + if (!isValidCronExpression(trigger.getCronExpression())) {
  49 + logTriggerMatchFailure(message, trigger, "CRON 表达式格式无效: " + trigger.getCronExpression());
  50 + return false;
  51 + }
  52 +
  53 + logTriggerMatchSuccess(message, trigger);
  54 + return true;
  55 + }
  56 +
  57 + /**
  58 + * 验证 CRON 表达式格式是否有效
  59 + *
  60 + * @param cronExpression CRON 表达式
  61 + * @return 是否有效
  62 + */
  63 + private boolean isValidCronExpression(String cronExpression) {
  64 + try {
  65 + // 简单的 CRON 表达式格式验证
  66 + // 标准 CRON 表达式应该有 6 或 7 个字段(秒 分 时 日 月 周 [年])
  67 + String[] fields = cronExpression.trim().split("\\s+");
  68 + return fields.length >= 6 && fields.length <= 7;
  69 + } catch (Exception e) {
  70 + return false;
  71 + }
  72 + }
  73 +
  74 + @Override
  75 + public int getPriority() {
  76 + return 50; // 最低优先级,因为定时触发器不依赖消息
  77 + }
  78 +
  79 + @Override
  80 + public boolean isEnabled() {
  81 + // 定时触发器可以根据配置动态启用/禁用
  82 + return true;
  83 + }
  84 +
  85 +}
yudao-module-iot/yudao-module-iot-biz/src/test/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotDataBridgeExecuteTest.java
@@ -85,20 +85,21 @@ public class IotDataBridgeExecuteTest extends BaseMockitoUnitTest { @@ -85,20 +85,21 @@ public class IotDataBridgeExecuteTest extends BaseMockitoUnitTest {
85 } 85 }
86 86
87 @Test 87 @Test
88 - public void testRedisStreamDataBridge() throws Exception { 88 + public void testRedisDataBridge() throws Exception {
89 // 1. 创建执行器实例 89 // 1. 创建执行器实例
90 - IotRedisStreamRuleAction action = new IotRedisStreamRuleAction(); 90 + IotRedisRuleAction action = new IotRedisRuleAction();
91 91
92 - // 2. 创建配置  
93 - IotDataSinkRedisStreamConfig config = new IotDataSinkRedisStreamConfig()  
94 - .setHost("127.0.0.1")  
95 - .setPort(6379)  
96 - .setDatabase(0)  
97 - .setPassword("123456")  
98 - .setTopic("test-stream"); 92 + // 2. 创建配置 - 测试 Stream 数据结构
  93 + IotDataSinkRedisConfig config = new IotDataSinkRedisConfig();
  94 + config.setHost("127.0.0.1");
  95 + config.setPort(6379);
  96 + config.setDatabase(0);
  97 + config.setPassword("123456");
  98 + config.setTopic("test-stream");
  99 + config.setDataStructure(1); // Stream 类型
99 100
100 // 3. 执行测试并验证缓存 101 // 3. 执行测试并验证缓存
101 - executeAndVerifyCache(action, config, "RedisStream"); 102 + executeAndVerifyCache(action, config, "Redis");
102 } 103 }
103 104
104 @Test 105 @Test
yudao-module-iot/yudao-module-iot-biz/src/test/java/cn/iocoder/yudao/module/iot/service/rule/scene/IotRuleSceneServiceSimpleTest.java renamed to yudao-module-iot/yudao-module-iot-biz/src/test/java/cn/iocoder/yudao/module/iot/service/rule/scene/IotSceneRuleServiceSimpleTest.java
@@ -2,9 +2,9 @@ package cn.iocoder.yudao.module.iot.service.rule.scene; @@ -2,9 +2,9 @@ package cn.iocoder.yudao.module.iot.service.rule.scene;
2 2
3 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; 3 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
4 import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest; 4 import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;
5 -import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.scene.IotRuleSceneSaveReqVO; 5 +import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.scene.IotSceneRuleSaveReqVO;
6 import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotSceneRuleDO; 6 import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotSceneRuleDO;
7 -import cn.iocoder.yudao.module.iot.dal.mysql.rule.IotRuleSceneMapper; 7 +import cn.iocoder.yudao.module.iot.dal.mysql.rule.IotSceneRuleMapper;
8 import cn.iocoder.yudao.module.iot.framework.job.core.IotSchedulerManager; 8 import cn.iocoder.yudao.module.iot.framework.job.core.IotSchedulerManager;
9 import cn.iocoder.yudao.module.iot.service.device.IotDeviceService; 9 import cn.iocoder.yudao.module.iot.service.device.IotDeviceService;
10 import cn.iocoder.yudao.module.iot.service.product.IotProductService; 10 import cn.iocoder.yudao.module.iot.service.product.IotProductService;
@@ -24,21 +24,21 @@ import static org.mockito.ArgumentMatchers.any; @@ -24,21 +24,21 @@ import static org.mockito.ArgumentMatchers.any;
24 import static org.mockito.Mockito.*; 24 import static org.mockito.Mockito.*;
25 25
26 /** 26 /**
27 - * {@link IotRuleSceneServiceImpl} 的简化单元测试类 27 + * {@link IotSceneRuleServiceImpl} 的简化单元测试类
28 * 使用 Mockito 进行纯单元测试,不依赖 Spring 容器 28 * 使用 Mockito 进行纯单元测试,不依赖 Spring 容器
29 * 29 *
30 * @author 芋道源码 30 * @author 芋道源码
31 */ 31 */
32 -public class IotRuleSceneServiceSimpleTest extends BaseMockitoUnitTest { 32 +public class IotSceneRuleServiceSimpleTest extends BaseMockitoUnitTest {
33 33
34 @InjectMocks 34 @InjectMocks
35 - private IotRuleSceneServiceImpl ruleSceneService; 35 + private IotSceneRuleServiceImpl sceneRuleService;
36 36
37 @Mock 37 @Mock
38 - private IotRuleSceneMapper ruleSceneMapper; 38 + private IotSceneRuleMapper sceneRuleMapper;
39 39
40 @Mock 40 @Mock
41 - private List<IotSceneRuleAction> ruleSceneActions; 41 + private List<IotSceneRuleAction> sceneRuleActions;
42 42
43 @Mock 43 @Mock
44 private IotSchedulerManager schedulerManager; 44 private IotSchedulerManager schedulerManager;
@@ -50,9 +50,9 @@ public class IotRuleSceneServiceSimpleTest extends BaseMockitoUnitTest { @@ -50,9 +50,9 @@ public class IotRuleSceneServiceSimpleTest extends BaseMockitoUnitTest {
50 private IotDeviceService deviceService; 50 private IotDeviceService deviceService;
51 51
52 @Test 52 @Test
53 - public void testCreateRuleScene_success() { 53 + public void testCreateScene_Rule_success() {
54 // 准备参数 54 // 准备参数
55 - IotRuleSceneSaveReqVO createReqVO = randomPojo(IotRuleSceneSaveReqVO.class, o -> { 55 + IotSceneRuleSaveReqVO createReqVO = randomPojo(IotSceneRuleSaveReqVO.class, o -> {
56 o.setId(null); 56 o.setId(null);
57 o.setStatus(CommonStatusEnum.ENABLE.getStatus()); 57 o.setStatus(CommonStatusEnum.ENABLE.getStatus());
58 o.setTriggers(Collections.singletonList(randomPojo(IotSceneRuleDO.Trigger.class))); 58 o.setTriggers(Collections.singletonList(randomPojo(IotSceneRuleDO.Trigger.class)));
@@ -61,25 +61,25 @@ public class IotRuleSceneServiceSimpleTest extends BaseMockitoUnitTest { @@ -61,25 +61,25 @@ public class IotRuleSceneServiceSimpleTest extends BaseMockitoUnitTest {
61 61
62 // Mock 行为 62 // Mock 行为
63 Long expectedId = randomLongId(); 63 Long expectedId = randomLongId();
64 - when(ruleSceneMapper.insert(any(IotSceneRuleDO.class))).thenAnswer(invocation -> {  
65 - IotSceneRuleDO ruleScene = invocation.getArgument(0);  
66 - ruleScene.setId(expectedId); 64 + when(sceneRuleMapper.insert(any(IotSceneRuleDO.class))).thenAnswer(invocation -> {
  65 + IotSceneRuleDO sceneRule = invocation.getArgument(0);
  66 + sceneRule.setId(expectedId);
67 return 1; 67 return 1;
68 }); 68 });
69 69
70 // 调用 70 // 调用
71 - Long ruleSceneId = ruleSceneService.createRuleScene(createReqVO); 71 + Long sceneRuleId = sceneRuleService.createSceneRule(createReqVO);
72 72
73 // 断言 73 // 断言
74 - assertEquals(expectedId, ruleSceneId);  
75 - verify(ruleSceneMapper, times(1)).insert(any(IotSceneRuleDO.class)); 74 + assertEquals(expectedId, sceneRuleId);
  75 + verify(sceneRuleMapper, times(1)).insert(any(IotSceneRuleDO.class));
76 } 76 }
77 77
78 @Test 78 @Test
79 - public void testUpdateRuleScene_success() { 79 + public void testUpdateScene_Rule_success() {
80 // 准备参数 80 // 准备参数
81 Long id = randomLongId(); 81 Long id = randomLongId();
82 - IotRuleSceneSaveReqVO updateReqVO = randomPojo(IotRuleSceneSaveReqVO.class, o -> { 82 + IotSceneRuleSaveReqVO updateReqVO = randomPojo(IotSceneRuleSaveReqVO.class, o -> {
83 o.setId(id); 83 o.setId(id);
84 o.setStatus(CommonStatusEnum.ENABLE.getStatus()); 84 o.setStatus(CommonStatusEnum.ENABLE.getStatus());
85 o.setTriggers(Collections.singletonList(randomPojo(IotSceneRuleDO.Trigger.class))); 85 o.setTriggers(Collections.singletonList(randomPojo(IotSceneRuleDO.Trigger.class)));
@@ -87,125 +87,125 @@ public class IotRuleSceneServiceSimpleTest extends BaseMockitoUnitTest { @@ -87,125 +87,125 @@ public class IotRuleSceneServiceSimpleTest extends BaseMockitoUnitTest {
87 }); 87 });
88 88
89 // Mock 行为 89 // Mock 行为
90 - IotSceneRuleDO existingRuleScene = randomPojo(IotSceneRuleDO.class, o -> o.setId(id));  
91 - when(ruleSceneMapper.selectById(id)).thenReturn(existingRuleScene);  
92 - when(ruleSceneMapper.updateById(any(IotSceneRuleDO.class))).thenReturn(1); 90 + IotSceneRuleDO existingSceneRule = randomPojo(IotSceneRuleDO.class, o -> o.setId(id));
  91 + when(sceneRuleMapper.selectById(id)).thenReturn(existingSceneRule);
  92 + when(sceneRuleMapper.updateById(any(IotSceneRuleDO.class))).thenReturn(1);
93 93
94 // 调用 94 // 调用
95 - assertDoesNotThrow(() -> ruleSceneService.updateRuleScene(updateReqVO)); 95 + assertDoesNotThrow(() -> sceneRuleService.updateSceneRule(updateReqVO));
96 96
97 // 验证 97 // 验证
98 - verify(ruleSceneMapper, times(1)).selectById(id);  
99 - verify(ruleSceneMapper, times(1)).updateById(any(IotSceneRuleDO.class)); 98 + verify(sceneRuleMapper, times(1)).selectById(id);
  99 + verify(sceneRuleMapper, times(1)).updateById(any(IotSceneRuleDO.class));
100 } 100 }
101 101
102 @Test 102 @Test
103 - public void testDeleteRuleScene_success() { 103 + public void testDeleteSceneRule_success() {
104 // 准备参数 104 // 准备参数
105 Long id = randomLongId(); 105 Long id = randomLongId();
106 106
107 // Mock 行为 107 // Mock 行为
108 - IotSceneRuleDO existingRuleScene = randomPojo(IotSceneRuleDO.class, o -> o.setId(id));  
109 - when(ruleSceneMapper.selectById(id)).thenReturn(existingRuleScene);  
110 - when(ruleSceneMapper.deleteById(id)).thenReturn(1); 108 + IotSceneRuleDO existingSceneRule = randomPojo(IotSceneRuleDO.class, o -> o.setId(id));
  109 + when(sceneRuleMapper.selectById(id)).thenReturn(existingSceneRule);
  110 + when(sceneRuleMapper.deleteById(id)).thenReturn(1);
111 111
112 // 调用 112 // 调用
113 - assertDoesNotThrow(() -> ruleSceneService.deleteRuleScene(id)); 113 + assertDoesNotThrow(() -> sceneRuleService.deleteSceneRule(id));
114 114
115 // 验证 115 // 验证
116 - verify(ruleSceneMapper, times(1)).selectById(id);  
117 - verify(ruleSceneMapper, times(1)).deleteById(id); 116 + verify(sceneRuleMapper, times(1)).selectById(id);
  117 + verify(sceneRuleMapper, times(1)).deleteById(id);
118 } 118 }
119 119
120 @Test 120 @Test
121 - public void testGetRuleScene() { 121 + public void testGetSceneRule() {
122 // 准备参数 122 // 准备参数
123 Long id = randomLongId(); 123 Long id = randomLongId();
124 - IotSceneRuleDO expectedRuleScene = randomPojo(IotSceneRuleDO.class, o -> o.setId(id)); 124 + IotSceneRuleDO expectedSceneRule = randomPojo(IotSceneRuleDO.class, o -> o.setId(id));
125 125
126 // Mock 行为 126 // Mock 行为
127 - when(ruleSceneMapper.selectById(id)).thenReturn(expectedRuleScene); 127 + when(sceneRuleMapper.selectById(id)).thenReturn(expectedSceneRule);
128 128
129 // 调用 129 // 调用
130 - IotSceneRuleDO result = ruleSceneService.getRuleScene(id); 130 + IotSceneRuleDO result = sceneRuleService.getSceneRule(id);
131 131
132 // 断言 132 // 断言
133 - assertEquals(expectedRuleScene, result);  
134 - verify(ruleSceneMapper, times(1)).selectById(id); 133 + assertEquals(expectedSceneRule, result);
  134 + verify(sceneRuleMapper, times(1)).selectById(id);
135 } 135 }
136 136
137 @Test 137 @Test
138 - public void testUpdateRuleSceneStatus_success() { 138 + public void testUpdateSceneRuleStatus_success() {
139 // 准备参数 139 // 准备参数
140 Long id = randomLongId(); 140 Long id = randomLongId();
141 Integer status = CommonStatusEnum.DISABLE.getStatus(); 141 Integer status = CommonStatusEnum.DISABLE.getStatus();
142 142
143 // Mock 行为 143 // Mock 行为
144 - IotSceneRuleDO existingRuleScene = randomPojo(IotSceneRuleDO.class, o -> { 144 + IotSceneRuleDO existingSceneRule = randomPojo(IotSceneRuleDO.class, o -> {
145 o.setId(id); 145 o.setId(id);
146 o.setStatus(CommonStatusEnum.ENABLE.getStatus()); 146 o.setStatus(CommonStatusEnum.ENABLE.getStatus());
147 }); 147 });
148 - when(ruleSceneMapper.selectById(id)).thenReturn(existingRuleScene);  
149 - when(ruleSceneMapper.updateById(any(IotSceneRuleDO.class))).thenReturn(1); 148 + when(sceneRuleMapper.selectById(id)).thenReturn(existingSceneRule);
  149 + when(sceneRuleMapper.updateById(any(IotSceneRuleDO.class))).thenReturn(1);
150 150
151 // 调用 151 // 调用
152 - assertDoesNotThrow(() -> ruleSceneService.updateRuleSceneStatus(id, status)); 152 + assertDoesNotThrow(() -> sceneRuleService.updateSceneRuleStatus(id, status));
153 153
154 // 验证 154 // 验证
155 - verify(ruleSceneMapper, times(1)).selectById(id);  
156 - verify(ruleSceneMapper, times(1)).updateById(any(IotSceneRuleDO.class)); 155 + verify(sceneRuleMapper, times(1)).selectById(id);
  156 + verify(sceneRuleMapper, times(1)).updateById(any(IotSceneRuleDO.class));
157 } 157 }
158 158
159 @Test 159 @Test
160 - public void testExecuteRuleSceneByTimer_success() { 160 + public void testExecuteSceneRuleByTimer_success() {
161 // 准备参数 161 // 准备参数
162 Long id = randomLongId(); 162 Long id = randomLongId();
163 163
164 // Mock 行为 164 // Mock 行为
165 - IotSceneRuleDO ruleScene = randomPojo(IotSceneRuleDO.class, o -> { 165 + IotSceneRuleDO sceneRule = randomPojo(IotSceneRuleDO.class, o -> {
166 o.setId(id); 166 o.setId(id);
167 o.setStatus(CommonStatusEnum.ENABLE.getStatus()); 167 o.setStatus(CommonStatusEnum.ENABLE.getStatus());
168 }); 168 });
169 - when(ruleSceneMapper.selectById(id)).thenReturn(ruleScene); 169 + when(sceneRuleMapper.selectById(id)).thenReturn(sceneRule);
170 170
171 // 调用 171 // 调用
172 - assertDoesNotThrow(() -> ruleSceneService.executeRuleSceneByTimer(id)); 172 + assertDoesNotThrow(() -> sceneRuleService.executeSceneRuleByTimer(id));
173 173
174 // 验证 174 // 验证
175 - verify(ruleSceneMapper, times(1)).selectById(id); 175 + verify(sceneRuleMapper, times(1)).selectById(id);
176 } 176 }
177 177
178 @Test 178 @Test
179 - public void testExecuteRuleSceneByTimer_notExists() { 179 + public void testExecuteSceneRuleByTimer_notExists() {
180 // 准备参数 180 // 准备参数
181 Long id = randomLongId(); 181 Long id = randomLongId();
182 182
183 // Mock 行为 183 // Mock 行为
184 - when(ruleSceneMapper.selectById(id)).thenReturn(null); 184 + when(sceneRuleMapper.selectById(id)).thenReturn(null);
185 185
186 // 调用 - 不存在的场景规则应该不会抛异常,只是记录日志 186 // 调用 - 不存在的场景规则应该不会抛异常,只是记录日志
187 - assertDoesNotThrow(() -> ruleSceneService.executeRuleSceneByTimer(id)); 187 + assertDoesNotThrow(() -> sceneRuleService.executeSceneRuleByTimer(id));
188 188
189 // 验证 189 // 验证
190 - verify(ruleSceneMapper, times(1)).selectById(id); 190 + verify(sceneRuleMapper, times(1)).selectById(id);
191 } 191 }
192 192
193 @Test 193 @Test
194 - public void testExecuteRuleSceneByTimer_disabled() { 194 + public void testExecuteSceneRuleByTimer_disabled() {
195 // 准备参数 195 // 准备参数
196 Long id = randomLongId(); 196 Long id = randomLongId();
197 197
198 // Mock 行为 198 // Mock 行为
199 - IotSceneRuleDO ruleScene = randomPojo(IotSceneRuleDO.class, o -> { 199 + IotSceneRuleDO sceneRule = randomPojo(IotSceneRuleDO.class, o -> {
200 o.setId(id); 200 o.setId(id);
201 o.setStatus(CommonStatusEnum.DISABLE.getStatus()); 201 o.setStatus(CommonStatusEnum.DISABLE.getStatus());
202 }); 202 });
203 - when(ruleSceneMapper.selectById(id)).thenReturn(ruleScene); 203 + when(sceneRuleMapper.selectById(id)).thenReturn(sceneRule);
204 204
205 // 调用 - 禁用的场景规则应该不会执行,只是记录日志 205 // 调用 - 禁用的场景规则应该不会执行,只是记录日志
206 - assertDoesNotThrow(() -> ruleSceneService.executeRuleSceneByTimer(id)); 206 + assertDoesNotThrow(() -> sceneRuleService.executeSceneRuleByTimer(id));
207 207
208 // 验证 208 // 验证
209 - verify(ruleSceneMapper, times(1)).selectById(id); 209 + verify(sceneRuleMapper, times(1)).selectById(id);
210 } 210 }
211 } 211 }
yudao-module-iot/yudao-module-iot-biz/src/test/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/IotSceneRuleTriggerMatcherTest.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.service.rule.scene.matcher;
  2 +
  3 +import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;
  4 +import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
  5 +import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotSceneRuleDO;
  6 +import cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleTriggerTypeEnum;
  7 +import org.junit.jupiter.api.BeforeEach;
  8 +import org.junit.jupiter.api.Test;
  9 +
  10 +import java.util.Arrays;
  11 +import java.util.HashMap;
  12 +import java.util.List;
  13 +
  14 +import static org.junit.jupiter.api.Assertions.*;
  15 +
  16 +/**
  17 + * IoT 场景规则触发器匹配器测试类
  18 + *
  19 + * @author HUIHUI
  20 + */
  21 +public class IotSceneRuleTriggerMatcherTest extends BaseMockitoUnitTest {
  22 +
  23 + private IotSceneRuleMatcherManager matcherManager;
  24 +
  25 + @BeforeEach
  26 + void setUp() {
  27 + // 创建所有匹配器实例
  28 + List<IotSceneRuleMatcher> matchers = Arrays.asList(
  29 + new DeviceStateUpdateTriggerMatcher(),
  30 + new DevicePropertyPostTriggerMatcher(),
  31 + new DeviceEventPostTriggerMatcher(),
  32 + new DeviceServiceInvokeTriggerMatcher(),
  33 + new TimerTriggerMatcher()
  34 + );
  35 +
  36 + // 初始化匹配器管理器
  37 + matcherManager = new IotSceneRuleMatcherManager(matchers);
  38 + }
  39 +
  40 + @Test
  41 + void testDeviceStateUpdateTriggerMatcher() {
  42 + // 1. 准备测试数据
  43 + IotDeviceMessage message = IotDeviceMessage.builder()
  44 + .requestId("test-001")
  45 + .method("thing.state.update")
  46 + .data(1) // 在线状态
  47 + .build();
  48 +
  49 + IotSceneRuleDO.Trigger trigger = new IotSceneRuleDO.Trigger();
  50 + trigger.setType(IotSceneRuleTriggerTypeEnum.DEVICE_STATE_UPDATE.getType());
  51 + trigger.setOperator("=");
  52 + trigger.setValue("1");
  53 +
  54 + // 2. 执行测试
  55 + boolean matched = matcherManager.isMatched(message, trigger);
  56 +
  57 + // 3. 验证结果
  58 + assertTrue(matched, "设备状态更新触发器应该匹配");
  59 + }
  60 +
  61 + @Test
  62 + void testDevicePropertyPostTriggerMatcher() {
  63 + // 1. 准备测试数据
  64 + HashMap<String, Object> params = new HashMap<>();
  65 + IotDeviceMessage message = IotDeviceMessage.builder()
  66 + .requestId("test-002")
  67 + .method("thing.property.post")
  68 + .data(25.5) // 温度值
  69 + .params(params)
  70 + .build();
  71 + // 模拟标识符
  72 + params.put("identifier", "temperature");
  73 +
  74 + IotSceneRuleDO.Trigger trigger = new IotSceneRuleDO.Trigger();
  75 + trigger.setType(IotSceneRuleTriggerTypeEnum.DEVICE_PROPERTY_POST.getType());
  76 + trigger.setIdentifier("temperature");
  77 + trigger.setOperator(">");
  78 + trigger.setValue("20");
  79 +
  80 + // 2. 执行测试
  81 + boolean matched = matcherManager.isMatched(message, trigger);
  82 +
  83 + // 3. 验证结果
  84 + assertTrue(matched, "设备属性上报触发器应该匹配");
  85 + }
  86 +
  87 + @Test
  88 + void testDeviceEventPostTriggerMatcher() {
  89 + // 1. 准备测试数据
  90 + HashMap<String, Object> params = new HashMap<>();
  91 + IotDeviceMessage message = IotDeviceMessage.builder()
  92 + .requestId("test-003")
  93 + .method("thing.event.post")
  94 + .data("alarm_data")
  95 + .params(params)
  96 + .build();
  97 + // 模拟标识符
  98 + params.put("identifier", "high_temperature_alarm");
  99 +
  100 + IotSceneRuleDO.Trigger trigger = new IotSceneRuleDO.Trigger();
  101 + trigger.setType(IotSceneRuleTriggerTypeEnum.DEVICE_EVENT_POST.getType());
  102 + trigger.setIdentifier("high_temperature_alarm");
  103 +
  104 + // 2. 执行测试
  105 + boolean matched = matcherManager.isMatched(message, trigger);
  106 +
  107 + // 3. 验证结果
  108 + assertTrue(matched, "设备事件上报触发器应该匹配");
  109 + }
  110 +
  111 + @Test
  112 + void testDeviceServiceInvokeTriggerMatcher() {
  113 + // 1. 准备测试数据
  114 + HashMap<String, Object> params = new HashMap<>();
  115 + IotDeviceMessage message = IotDeviceMessage.builder()
  116 + .requestId("test-004")
  117 + .method("thing.service.invoke")
  118 + .msg("alarm_data")
  119 + .params(params)
  120 + .build();
  121 + // 模拟标识符
  122 + params.put("identifier", "restart_device");
  123 +
  124 + IotSceneRuleDO.Trigger trigger = new IotSceneRuleDO.Trigger();
  125 + trigger.setType(IotSceneRuleTriggerTypeEnum.DEVICE_SERVICE_INVOKE.getType());
  126 + trigger.setIdentifier("restart_device");
  127 +
  128 + // 2. 执行测试
  129 + boolean matched = matcherManager.isMatched(message, trigger);
  130 +
  131 + // 3. 验证结果
  132 + assertTrue(matched, "设备服务调用触发器应该匹配");
  133 + }
  134 +
  135 + @Test
  136 + void testTimerTriggerMatcher() {
  137 + // 1. 准备测试数据
  138 + IotDeviceMessage message = IotDeviceMessage.builder()
  139 + .requestId("test-005")
  140 + .method("timer.trigger") // 定时触发器不依赖具体消息方法
  141 + .build();
  142 +
  143 + IotSceneRuleDO.Trigger trigger = new IotSceneRuleDO.Trigger();
  144 + trigger.setType(IotSceneRuleTriggerTypeEnum.TIMER.getType());
  145 + trigger.setCronExpression("0 0 12 * * ?"); // 每天中午12点
  146 +
  147 + // 2. 执行测试
  148 + boolean matched = matcherManager.isMatched(message, trigger);
  149 +
  150 + // 3. 验证结果
  151 + assertTrue(matched, "定时触发器应该匹配");
  152 + }
  153 +
  154 + @Test
  155 + void testInvalidTriggerType() {
  156 + // 1. 准备测试数据
  157 + IotDeviceMessage message = IotDeviceMessage.builder()
  158 + .requestId("test-006")
  159 + .method("unknown.method")
  160 + .build();
  161 +
  162 + IotSceneRuleDO.Trigger trigger = new IotSceneRuleDO.Trigger();
  163 + trigger.setType(999); // 无效的触发器类型
  164 +
  165 + // 2. 执行测试
  166 + boolean matched = matcherManager.isMatched(message, trigger);
  167 +
  168 + // 3. 验证结果
  169 + assertFalse(matched, "无效的触发器类型应该不匹配");
  170 + }
  171 +
  172 + @Test
  173 + void testMatcherManagerStatistics() {
  174 + // 1. 执行测试
  175 + var statistics = matcherManager.getMatcherStatistics();
  176 +
  177 + // 2. 验证结果
  178 + assertNotNull(statistics);
  179 + assertEquals(5, statistics.get("totalMatchers"));
  180 + assertEquals(5, statistics.get("enabledMatchers"));
  181 + assertNotNull(statistics.get("supportedTriggerTypes"));
  182 + assertNotNull(statistics.get("matcherDetails"));
  183 + }
  184 +
  185 + @Test
  186 + void testGetSupportedTriggerTypes() {
  187 + // 1. 执行测试
  188 + var supportedTypes = matcherManager.getSupportedTriggerTypes();
  189 +
  190 + // 2. 验证结果
  191 + assertNotNull(supportedTypes);
  192 + assertEquals(5, supportedTypes.size());
  193 + assertTrue(supportedTypes.contains(IotSceneRuleTriggerTypeEnum.DEVICE_STATE_UPDATE));
  194 + assertTrue(supportedTypes.contains(IotSceneRuleTriggerTypeEnum.DEVICE_PROPERTY_POST));
  195 + assertTrue(supportedTypes.contains(IotSceneRuleTriggerTypeEnum.DEVICE_EVENT_POST));
  196 + assertTrue(supportedTypes.contains(IotSceneRuleTriggerTypeEnum.DEVICE_SERVICE_INVOKE));
  197 + assertTrue(supportedTypes.contains(IotSceneRuleTriggerTypeEnum.TIMER));
  198 + }
  199 +
  200 +}
yudao-module-iot/yudao-module-iot-biz/src/test/resources/sql/clean.sql
1 --- TODO @puhui999:sql 格式  
2 --- IoT 模块测试数据清理脚本  
3 -DELETE  
4 -FROM "iot_scene_rule";  
5 -DELETE  
6 -FROM "iot_product";  
7 -DELETE  
8 -FROM "iot_device";  
9 -DELETE  
10 -FROM "iot_thing_model";  
11 -DELETE  
12 -FROM "iot_device_data";  
13 -DELETE  
14 -FROM "iot_alert_config";  
15 -DELETE  
16 -FROM "iot_alert_record";  
17 -DELETE  
18 -FROM "iot_ota_firmware";  
19 -DELETE  
20 -FROM "iot_ota_task";  
21 -DELETE  
22 -FROM "iot_ota_record"; 1 +DELETE FROM "iot_scene_rule";
  2 +DELETE FROM "iot_product";
  3 +DELETE FROM "iot_device";
  4 +DELETE FROM "iot_thing_model";
  5 +DELETE FROM "iot_device_data";
  6 +DELETE FROM "iot_alert_config";
  7 +DELETE FROM "iot_alert_record";
  8 +DELETE FROM "iot_ota_firmware";
  9 +DELETE FROM "iot_ota_task";
  10 +DELETE FROM "iot_ota_record";
yudao-module-iot/yudao-module-iot-biz/src/test/resources/sql/create_tables.sql
1 --- TODO @puhui999:sql 格式  
2 --- IoT 模块测试数据库表结构  
3 --- 基于 H2 数据库语法,兼容 MySQL 模式  
4 -  
5 --- IoT 场景联动规则表  
6 -CREATE TABLE IF NOT EXISTS "iot_scene_rule"  
7 -(  
8 - "id"  
9 - bigint  
10 - NOT  
11 - NULL  
12 - GENERATED  
13 - BY  
14 - DEFAULT AS  
15 - IDENTITY,  
16 - "name"  
17 - varchar  
18 -(  
19 - 255  
20 -) NOT NULL DEFAULT '',  
21 - "description" varchar  
22 -(  
23 - 500  
24 -) DEFAULT NULL, 1 +CREATE TABLE IF NOT EXISTS "iot_scene_rule" (
  2 + "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
  3 + "name" varchar(255) NOT NULL DEFAULT '',
  4 + "description" varchar(500) DEFAULT NULL,
25 "status" tinyint NOT NULL DEFAULT '0', 5 "status" tinyint NOT NULL DEFAULT '0',
26 "triggers" text, 6 "triggers" text,
27 "actions" text, 7 "actions" text,
28 - "creator" varchar  
29 -(  
30 - 64  
31 -) DEFAULT '', 8 + "creator" varchar(64) DEFAULT '',
32 "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, 9 "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
33 - "updater" varchar  
34 -(  
35 - 64  
36 -) DEFAULT '', 10 + "updater" varchar(64) DEFAULT '',
37 "update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, 11 "update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
38 "deleted" bit NOT NULL DEFAULT FALSE, 12 "deleted" bit NOT NULL DEFAULT FALSE,
39 "tenant_id" bigint NOT NULL DEFAULT '0', 13 "tenant_id" bigint NOT NULL DEFAULT '0',
40 - PRIMARY KEY  
41 -(  
42 - "id"  
43 -)  
44 - ) COMMENT 'IoT 场景联动规则表'; 14 + PRIMARY KEY ("id")
  15 +) COMMENT 'IoT 场景联动规则表';
45 16
46 --- IoT 产品表  
47 -CREATE TABLE IF NOT EXISTS "iot_product"  
48 -(  
49 - "id"  
50 - bigint  
51 - NOT  
52 - NULL  
53 - GENERATED  
54 - BY  
55 - DEFAULT AS  
56 - IDENTITY,  
57 - "name"  
58 - varchar  
59 -(  
60 - 255  
61 -) NOT NULL DEFAULT '',  
62 - "product_key" varchar  
63 -(  
64 - 100  
65 -) NOT NULL DEFAULT '', 17 +CREATE TABLE IF NOT EXISTS "iot_product" (
  18 + "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
  19 + "name" varchar(255) NOT NULL DEFAULT '',
  20 + "product_key" varchar(100) NOT NULL DEFAULT '',
66 "protocol_type" tinyint NOT NULL DEFAULT '0', 21 "protocol_type" tinyint NOT NULL DEFAULT '0',
67 "category_id" bigint DEFAULT NULL, 22 "category_id" bigint DEFAULT NULL,
68 - "description" varchar  
69 -(  
70 - 500  
71 -) DEFAULT NULL, 23 + "description" varchar(500) DEFAULT NULL,
72 "data_format" tinyint NOT NULL DEFAULT '0', 24 "data_format" tinyint NOT NULL DEFAULT '0',
73 "device_type" tinyint NOT NULL DEFAULT '0', 25 "device_type" tinyint NOT NULL DEFAULT '0',
74 "net_type" tinyint NOT NULL DEFAULT '0', 26 "net_type" tinyint NOT NULL DEFAULT '0',
75 "validate_type" tinyint NOT NULL DEFAULT '0', 27 "validate_type" tinyint NOT NULL DEFAULT '0',
76 "status" tinyint NOT NULL DEFAULT '0', 28 "status" tinyint NOT NULL DEFAULT '0',
77 - "creator" varchar  
78 -(  
79 - 64  
80 -) DEFAULT '', 29 + "creator" varchar(64) DEFAULT '',
81 "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, 30 "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
82 - "updater" varchar  
83 -(  
84 - 64  
85 -) DEFAULT '', 31 + "updater" varchar(64) DEFAULT '',
86 "update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, 32 "update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
87 "deleted" bit NOT NULL DEFAULT FALSE, 33 "deleted" bit NOT NULL DEFAULT FALSE,
88 "tenant_id" bigint NOT NULL DEFAULT '0', 34 "tenant_id" bigint NOT NULL DEFAULT '0',
89 - PRIMARY KEY  
90 -(  
91 - "id"  
92 -)  
93 - ) COMMENT 'IoT 产品表'; 35 + PRIMARY KEY ("id")
  36 +) COMMENT 'IoT 产品表';
94 37
95 --- IoT 设备表  
96 -CREATE TABLE IF NOT EXISTS "iot_device"  
97 -(  
98 - "id"  
99 - bigint  
100 - NOT  
101 - NULL  
102 - GENERATED  
103 - BY  
104 - DEFAULT AS  
105 - IDENTITY,  
106 - "device_name"  
107 - varchar  
108 -(  
109 - 255  
110 -) NOT NULL DEFAULT '', 38 +CREATE TABLE IF NOT EXISTS "iot_device" (
  39 + "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
  40 + "device_name" varchar(255) NOT NULL DEFAULT '',
111 "product_id" bigint NOT NULL, 41 "product_id" bigint NOT NULL,
112 - "device_key" varchar  
113 -(  
114 - 100  
115 -) NOT NULL DEFAULT '',  
116 - "device_secret" varchar  
117 -(  
118 - 100  
119 -) NOT NULL DEFAULT '',  
120 - "nickname" varchar  
121 -(  
122 - 255  
123 -) DEFAULT NULL, 42 + "device_key" varchar(100) NOT NULL DEFAULT '',
  43 + "device_secret" varchar(100) NOT NULL DEFAULT '',
  44 + "nickname" varchar(255) DEFAULT NULL,
124 "status" tinyint NOT NULL DEFAULT '0', 45 "status" tinyint NOT NULL DEFAULT '0',
125 "status_last_update_time" timestamp DEFAULT NULL, 46 "status_last_update_time" timestamp DEFAULT NULL,
126 "last_online_time" timestamp DEFAULT NULL, 47 "last_online_time" timestamp DEFAULT NULL,
127 "last_offline_time" timestamp DEFAULT NULL, 48 "last_offline_time" timestamp DEFAULT NULL,
128 "active_time" timestamp DEFAULT NULL, 49 "active_time" timestamp DEFAULT NULL,
129 - "ip" varchar  
130 -(  
131 - 50  
132 -) DEFAULT NULL,  
133 - "firmware_version" varchar  
134 -(  
135 - 50  
136 -) DEFAULT NULL, 50 + "ip" varchar(50) DEFAULT NULL,
  51 + "firmware_version" varchar(50) DEFAULT NULL,
137 "device_type" tinyint NOT NULL DEFAULT '0', 52 "device_type" tinyint NOT NULL DEFAULT '0',
138 "gateway_id" bigint DEFAULT NULL, 53 "gateway_id" bigint DEFAULT NULL,
139 "sub_device_count" int NOT NULL DEFAULT '0', 54 "sub_device_count" int NOT NULL DEFAULT '0',
140 - "creator" varchar  
141 -(  
142 - 64  
143 -) DEFAULT '', 55 + "creator" varchar(64) DEFAULT '',
144 "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, 56 "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
145 - "updater" varchar  
146 -(  
147 - 64  
148 -) DEFAULT '', 57 + "updater" varchar(64) DEFAULT '',
149 "update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, 58 "update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
150 "deleted" bit NOT NULL DEFAULT FALSE, 59 "deleted" bit NOT NULL DEFAULT FALSE,
151 "tenant_id" bigint NOT NULL DEFAULT '0', 60 "tenant_id" bigint NOT NULL DEFAULT '0',
152 - PRIMARY KEY  
153 -(  
154 - "id"  
155 -)  
156 - ) COMMENT 'IoT 设备表'; 61 + PRIMARY KEY ("id")
  62 +) COMMENT 'IoT 设备表';
157 63
158 --- IoT 物模型表  
159 -CREATE TABLE IF NOT EXISTS "iot_thing_model"  
160 -(  
161 - "id"  
162 - bigint  
163 - NOT  
164 - NULL  
165 - GENERATED  
166 - BY  
167 - DEFAULT AS  
168 - IDENTITY,  
169 - "product_id"  
170 - bigint  
171 - NOT  
172 - NULL,  
173 - "identifier"  
174 - varchar  
175 -(  
176 - 100  
177 -) NOT NULL DEFAULT '',  
178 - "name" varchar  
179 -(  
180 - 255  
181 -) NOT NULL DEFAULT '',  
182 - "description" varchar  
183 -(  
184 - 500  
185 -) DEFAULT NULL, 64 +CREATE TABLE IF NOT EXISTS "iot_thing_model" (
  65 + "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
  66 + "product_id" bigint NOT NULL,
  67 + "identifier" varchar(100) NOT NULL DEFAULT '',
  68 + "name" varchar(255) NOT NULL DEFAULT '',
  69 + "description" varchar(500) DEFAULT NULL,
186 "type" tinyint NOT NULL DEFAULT '1', 70 "type" tinyint NOT NULL DEFAULT '1',
187 "property" text, 71 "property" text,
188 - "creator" varchar  
189 -(  
190 - 64  
191 -) DEFAULT '', 72 + "creator" varchar(64) DEFAULT '',
192 "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, 73 "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
193 - "updater" varchar  
194 -(  
195 - 64  
196 -) DEFAULT '', 74 + "updater" varchar(64) DEFAULT '',
197 "update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, 75 "update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
198 "deleted" bit NOT NULL DEFAULT FALSE, 76 "deleted" bit NOT NULL DEFAULT FALSE,
199 "tenant_id" bigint NOT NULL DEFAULT '0', 77 "tenant_id" bigint NOT NULL DEFAULT '0',
200 - PRIMARY KEY  
201 -(  
202 - "id"  
203 -)  
204 - ) COMMENT 'IoT 物模型表'; 78 + PRIMARY KEY ("id")
  79 +) COMMENT 'IoT 物模型表';
205 80
206 --- IoT 设备数据表  
207 -CREATE TABLE IF NOT EXISTS "iot_device_data"  
208 -(  
209 - "id"  
210 - bigint  
211 - NOT  
212 - NULL  
213 - GENERATED  
214 - BY  
215 - DEFAULT AS  
216 - IDENTITY,  
217 - "device_id"  
218 - bigint  
219 - NOT  
220 - NULL,  
221 - "product_id"  
222 - bigint  
223 - NOT  
224 - NULL,  
225 - "identifier"  
226 - varchar  
227 -(  
228 - 100  
229 -) NOT NULL DEFAULT '', 81 +CREATE TABLE IF NOT EXISTS "iot_device_data" (
  82 + "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
  83 + "device_id" bigint NOT NULL,
  84 + "product_id" bigint NOT NULL,
  85 + "identifier" varchar(100) NOT NULL DEFAULT '',
230 "type" tinyint NOT NULL DEFAULT '1', 86 "type" tinyint NOT NULL DEFAULT '1',
231 "data" text, 87 "data" text,
232 "ts" bigint NOT NULL DEFAULT '0', 88 "ts" bigint NOT NULL DEFAULT '0',
233 "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, 89 "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
234 - PRIMARY KEY  
235 -(  
236 - "id"  
237 -)  
238 - ) COMMENT 'IoT 设备数据表'; 90 + PRIMARY KEY ("id")
  91 +) COMMENT 'IoT 设备数据表';
239 92
240 --- IoT 告警配置表  
241 -CREATE TABLE IF NOT EXISTS "iot_alert_config"  
242 -(  
243 - "id"  
244 - bigint  
245 - NOT  
246 - NULL  
247 - GENERATED  
248 - BY  
249 - DEFAULT AS  
250 - IDENTITY,  
251 - "name"  
252 - varchar  
253 -(  
254 - 255  
255 -) NOT NULL DEFAULT '', 93 +CREATE TABLE IF NOT EXISTS "iot_alert_config" (
  94 + "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
  95 + "name" varchar(255) NOT NULL DEFAULT '',
256 "product_id" bigint NOT NULL, 96 "product_id" bigint NOT NULL,
257 "device_id" bigint DEFAULT NULL, 97 "device_id" bigint DEFAULT NULL,
258 "rule_id" bigint DEFAULT NULL, 98 "rule_id" bigint DEFAULT NULL,
259 "status" tinyint NOT NULL DEFAULT '0', 99 "status" tinyint NOT NULL DEFAULT '0',
260 - "creator" varchar  
261 -(  
262 - 64  
263 -) DEFAULT '', 100 + "creator" varchar(64) DEFAULT '',
264 "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, 101 "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
265 - "updater" varchar  
266 -(  
267 - 64  
268 -) DEFAULT '', 102 + "updater" varchar(64) DEFAULT '',
269 "update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, 103 "update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
270 "deleted" bit NOT NULL DEFAULT FALSE, 104 "deleted" bit NOT NULL DEFAULT FALSE,
271 "tenant_id" bigint NOT NULL DEFAULT '0', 105 "tenant_id" bigint NOT NULL DEFAULT '0',
272 - PRIMARY KEY  
273 -(  
274 - "id"  
275 -)  
276 - ) COMMENT 'IoT 告警配置表'; 106 + PRIMARY KEY ("id")
  107 +) COMMENT 'IoT 告警配置表';
277 108
278 --- IoT 告警记录表  
279 -CREATE TABLE IF NOT EXISTS "iot_alert_record"  
280 -(  
281 - "id"  
282 - bigint  
283 - NOT  
284 - NULL  
285 - GENERATED  
286 - BY  
287 - DEFAULT AS  
288 - IDENTITY,  
289 - "alert_config_id"  
290 - bigint  
291 - NOT  
292 - NULL,  
293 - "alert_name"  
294 - varchar  
295 -(  
296 - 255  
297 -) NOT NULL DEFAULT '', 109 +CREATE TABLE IF NOT EXISTS "iot_alert_record" (
  110 + "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
  111 + "alert_config_id" bigint NOT NULL,
  112 + "alert_name" varchar(255) NOT NULL DEFAULT '',
298 "product_id" bigint NOT NULL, 113 "product_id" bigint NOT NULL,
299 "device_id" bigint DEFAULT NULL, 114 "device_id" bigint DEFAULT NULL,
300 "rule_id" bigint DEFAULT NULL, 115 "rule_id" bigint DEFAULT NULL,
@@ -303,171 +118,65 @@ CREATE TABLE IF NOT EXISTS &quot;iot_alert_record&quot; @@ -303,171 +118,65 @@ CREATE TABLE IF NOT EXISTS &quot;iot_alert_record&quot;
303 "deal_status" tinyint NOT NULL DEFAULT '0', 118 "deal_status" tinyint NOT NULL DEFAULT '0',
304 "deal_time" timestamp DEFAULT NULL, 119 "deal_time" timestamp DEFAULT NULL,
305 "deal_user_id" bigint DEFAULT NULL, 120 "deal_user_id" bigint DEFAULT NULL,
306 - "deal_remark" varchar  
307 -(  
308 - 500  
309 -) DEFAULT NULL,  
310 - "creator" varchar  
311 -(  
312 - 64  
313 -) DEFAULT '', 121 + "deal_remark" varchar(500) DEFAULT NULL,
  122 + "creator" varchar(64) DEFAULT '',
314 "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, 123 "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
315 - "updater" varchar  
316 -(  
317 - 64  
318 -) DEFAULT '', 124 + "updater" varchar(64) DEFAULT '',
319 "update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, 125 "update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
320 "deleted" bit NOT NULL DEFAULT FALSE, 126 "deleted" bit NOT NULL DEFAULT FALSE,
321 "tenant_id" bigint NOT NULL DEFAULT '0', 127 "tenant_id" bigint NOT NULL DEFAULT '0',
322 - PRIMARY KEY  
323 -(  
324 - "id"  
325 -)  
326 - ) COMMENT 'IoT 告警记录表'; 128 + PRIMARY KEY ("id")
  129 +) COMMENT 'IoT 告警记录表';
327 130
328 --- IoT OTA 固件表  
329 -CREATE TABLE IF NOT EXISTS "iot_ota_firmware"  
330 -(  
331 - "id"  
332 - bigint  
333 - NOT  
334 - NULL  
335 - GENERATED  
336 - BY  
337 - DEFAULT AS  
338 - IDENTITY,  
339 - "name"  
340 - varchar  
341 -(  
342 - 255  
343 -) NOT NULL DEFAULT '', 131 +CREATE TABLE IF NOT EXISTS "iot_ota_firmware" (
  132 + "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
  133 + "name" varchar(255) NOT NULL DEFAULT '',
344 "product_id" bigint NOT NULL, 134 "product_id" bigint NOT NULL,
345 - "version" varchar  
346 -(  
347 - 50  
348 -) NOT NULL DEFAULT '',  
349 - "description" varchar  
350 -(  
351 - 500  
352 -) DEFAULT NULL,  
353 - "file_url" varchar  
354 -(  
355 - 500  
356 -) DEFAULT NULL, 135 + "version" varchar(50) NOT NULL DEFAULT '',
  136 + "description" varchar(500) DEFAULT NULL,
  137 + "file_url" varchar(500) DEFAULT NULL,
357 "file_size" bigint NOT NULL DEFAULT '0', 138 "file_size" bigint NOT NULL DEFAULT '0',
358 "status" tinyint NOT NULL DEFAULT '0', 139 "status" tinyint NOT NULL DEFAULT '0',
359 - "creator" varchar  
360 -(  
361 - 64  
362 -) DEFAULT '', 140 + "creator" varchar(64) DEFAULT '',
363 "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, 141 "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
364 - "updater" varchar  
365 -(  
366 - 64  
367 -) DEFAULT '', 142 + "updater" varchar(64) DEFAULT '',
368 "update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, 143 "update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
369 "deleted" bit NOT NULL DEFAULT FALSE, 144 "deleted" bit NOT NULL DEFAULT FALSE,
370 "tenant_id" bigint NOT NULL DEFAULT '0', 145 "tenant_id" bigint NOT NULL DEFAULT '0',
371 - PRIMARY KEY  
372 -(  
373 - "id"  
374 -)  
375 - ) COMMENT 'IoT OTA 固件表'; 146 + PRIMARY KEY ("id")
  147 +) COMMENT 'IoT OTA 固件表';
376 148
377 --- IoT OTA 升级任务表  
378 -CREATE TABLE IF NOT EXISTS "iot_ota_task"  
379 -(  
380 - "id"  
381 - bigint  
382 - NOT  
383 - NULL  
384 - GENERATED  
385 - BY  
386 - DEFAULT AS  
387 - IDENTITY,  
388 - "name"  
389 - varchar  
390 -(  
391 - 255  
392 -) NOT NULL DEFAULT '', 149 +CREATE TABLE IF NOT EXISTS "iot_ota_task" (
  150 + "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
  151 + "name" varchar(255) NOT NULL DEFAULT '',
393 "firmware_id" bigint NOT NULL, 152 "firmware_id" bigint NOT NULL,
394 "product_id" bigint NOT NULL, 153 "product_id" bigint NOT NULL,
395 "upgrade_type" tinyint NOT NULL DEFAULT '0', 154 "upgrade_type" tinyint NOT NULL DEFAULT '0',
396 "status" tinyint NOT NULL DEFAULT '0', 155 "status" tinyint NOT NULL DEFAULT '0',
397 - "creator" varchar  
398 -(  
399 - 64  
400 -) DEFAULT '', 156 + "creator" varchar(64) DEFAULT '',
401 "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, 157 "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
402 - "updater" varchar  
403 -(  
404 - 64  
405 -) DEFAULT '', 158 + "updater" varchar(64) DEFAULT '',
406 "update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, 159 "update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
407 "deleted" bit NOT NULL DEFAULT FALSE, 160 "deleted" bit NOT NULL DEFAULT FALSE,
408 "tenant_id" bigint NOT NULL DEFAULT '0', 161 "tenant_id" bigint NOT NULL DEFAULT '0',
409 - PRIMARY KEY  
410 -(  
411 - "id"  
412 -)  
413 - ) COMMENT 'IoT OTA 升级任务表'; 162 + PRIMARY KEY ("id")
  163 +) COMMENT 'IoT OTA 升级任务表';
414 164
415 --- IoT OTA 升级记录表  
416 -CREATE TABLE IF NOT EXISTS "iot_ota_record"  
417 -(  
418 - "id"  
419 - bigint  
420 - NOT  
421 - NULL  
422 - GENERATED  
423 - BY  
424 - DEFAULT AS  
425 - IDENTITY,  
426 - "task_id"  
427 - bigint  
428 - NOT  
429 - NULL,  
430 - "firmware_id"  
431 - bigint  
432 - NOT  
433 - NULL,  
434 - "device_id"  
435 - bigint  
436 - NOT  
437 - NULL,  
438 - "status"  
439 - tinyint  
440 - NOT  
441 - NULL  
442 - DEFAULT  
443 - '0',  
444 - "progress"  
445 - int  
446 - NOT  
447 - NULL  
448 - DEFAULT  
449 - '0',  
450 - "error_msg"  
451 - varchar  
452 -(  
453 - 500  
454 -) DEFAULT NULL, 165 +CREATE TABLE IF NOT EXISTS "iot_ota_record" (
  166 + "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
  167 + "task_id" bigint NOT NULL,
  168 + "firmware_id" bigint NOT NULL,
  169 + "device_id" bigint NOT NULL,
  170 + "status" tinyint NOT NULL DEFAULT '0',
  171 + "progress" int NOT NULL DEFAULT '0',
  172 + "error_msg" varchar(500) DEFAULT NULL,
455 "start_time" timestamp DEFAULT NULL, 173 "start_time" timestamp DEFAULT NULL,
456 "end_time" timestamp DEFAULT NULL, 174 "end_time" timestamp DEFAULT NULL,
457 - "creator" varchar  
458 -(  
459 - 64  
460 -) DEFAULT '', 175 + "creator" varchar(64) DEFAULT '',
461 "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, 176 "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
462 - "updater" varchar  
463 -(  
464 - 64  
465 -) DEFAULT '', 177 + "updater" varchar(64) DEFAULT '',
466 "update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, 178 "update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
467 "deleted" bit NOT NULL DEFAULT FALSE, 179 "deleted" bit NOT NULL DEFAULT FALSE,
468 "tenant_id" bigint NOT NULL DEFAULT '0', 180 "tenant_id" bigint NOT NULL DEFAULT '0',
469 - PRIMARY KEY  
470 -(  
471 - "id"  
472 -)  
473 - ) COMMENT 'IoT OTA 升级记录表'; 181 + PRIMARY KEY ("id")
  182 +) COMMENT 'IoT OTA 升级记录表';