Commit f88d30f1039757870225e687a3b8a434acb6238c
Committed by
Gitee
!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<Integer> { | @@ -22,7 +22,7 @@ public enum IotDataSinkTypeEnum implements ArrayValuable<Integer> { | ||
| 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<Integer> { | @@ -42,7 +42,7 @@ public enum IotRuleSceneActionTypeEnum implements ArrayValuable<Integer> { | ||
| 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<String> { | @@ -53,7 +53,7 @@ public enum IotRuleSceneConditionOperatorEnum implements ArrayValuable<String> { | ||
| 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<String> { | @@ -68,7 +68,7 @@ public enum IotRuleSceneConditionOperatorEnum implements ArrayValuable<String> { | ||
| 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<Integer> { | @@ -25,7 +25,7 @@ public enum IotRuleSceneConditionTypeEnum implements ArrayValuable<Integer> { | ||
| 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<Integer> { | @@ -56,11 +53,25 @@ public enum IotRuleSceneTriggerTypeEnum implements ArrayValuable<Integer> { | ||
| 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<IotDevic | @@ -46,7 +46,7 @@ public class IotRuleSceneMessageHandler implements IotMessageSubscriber<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 "iot_alert_record" | @@ -303,171 +118,65 @@ CREATE TABLE IF NOT EXISTS "iot_alert_record" | ||
| 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 升级记录表'; |