Просмотр исходного кода

!1302 【代码优化】IoT: 物模型、场景联动
Merge pull request !1302 from puhui999/iot

芋道源码 7 месяцев назад
Родитель
Сommit
37fdd6247d
33 измененных файлов с 436 добавлено и 490 удалено
  1. 2 2
      yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/ErrorCodeConstants.java
  2. 6 7
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/IotRuleSceneController.java
  3. 1 1
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/config/IotDataBridgeAbstractConfig.java
  4. 1 2
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/config/IotDataBridgeRedisStreamConfig.java
  5. 0 2
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/package-info.java
  6. 1 1
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/scene/IotRuleScenePageReqVO.java
  7. 4 5
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/scene/IotRuleSceneRespVO.java
  8. 4 5
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/scene/IotRuleSceneSaveReqVO.java
  9. 0 38
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/scene/config/IotRuleSceneActionConfig.java
  10. 0 58
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/scene/config/IotRuleSceneActionDeviceControl.java
  11. 0 38
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/scene/config/IotRuleSceneTriggerCondition.java
  12. 0 38
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/scene/config/IotRuleSceneTriggerConditionParameter.java
  13. 0 54
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/scene/config/IotRuleSceneTriggerConfig.java
  14. 2 2
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/IotThingModelController.http
  15. 27 4
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/IotThingModelController.java
  16. 9 6
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/dataType/ThingModelArrayDataSpecs.java
  17. 8 9
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/dataType/ThingModelBoolOrEnumDataSpecs.java
  18. 2 0
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/dataType/ThingModelDateOrTextDataSpecs.java
  19. 8 3
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/dataType/ThingModelNumericDataSpec.java
  20. 19 14
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/dataType/ThingModelStructDataSpecs.java
  21. 189 6
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/IotRuleSceneDO.java
  22. 1 1
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/rule/IotRuleSceneMapper.java
  23. 7 7
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/IotRuleSceneService.java
  24. 37 38
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/IotRuleSceneServiceImpl.java
  25. 2 2
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/IotRuleSceneAction.java
  26. 2 2
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/IotRuleSceneAlertAction.java
  27. 2 2
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/IotRuleSceneDataBridgeAction.java
  28. 3 4
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/IotRuleSceneDeviceControlAction.java
  29. 1 1
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/AbstractCacheableDataBridgeExecute.java
  30. 12 27
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotRedisStreamDataBridgeExecute.java
  31. 0 9
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thingmodel/IotThingModelService.java
  32. 0 27
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thingmodel/IotThingModelServiceImpl.java
  33. 86 75
      yudao-module-iot/yudao-module-iot-biz/src/test/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotDataBridgeExecuteTest.java

+ 2 - 2
yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/ErrorCodeConstants.java

@@ -72,8 +72,8 @@ public interface ErrorCodeConstants {
     // ========== IoT 数据桥梁 1-050-010-000 ==========
     ErrorCode DATA_BRIDGE_NOT_EXISTS = new ErrorCode(1_050_010_000, "IoT 数据桥梁不存在");
 
-    // ========== IoT 规则场景(场景联动 1-050-011-000 ==========
-    ErrorCode RULE_SCENE_NOT_EXISTS = new ErrorCode(1_050_011_000, "IoT 规则场景(场景联动不存在");
+    // ========== IoT 场景联动 1-050-011-000 ==========
+    ErrorCode RULE_SCENE_NOT_EXISTS = new ErrorCode(1_050_011_000, "IoT 场景联动不存在");
 
     // ========== IoT 产品脚本信息 1-050-012-000 ==========
     ErrorCode PRODUCT_SCRIPT_NOT_EXISTS = new ErrorCode(1_050_012_000, "IoT 产品脚本信息不存在");

+ 6 - 7
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/IotRuleSceneController.java

@@ -20,8 +20,7 @@ import org.springframework.web.bind.annotation.*;
 
 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
 
-// TODO @芋艿:规则场景 要不要,统一改成 场景联动
-@Tag(name = "管理后台 - IoT 规则场景")
+@Tag(name = "管理后台 - IoT 场景联动")
 @RestController
 @RequestMapping("/iot/rule-scene")
 @Validated
@@ -31,14 +30,14 @@ public class IotRuleSceneController {
     private IotRuleSceneService ruleSceneService;
 
     @PostMapping("/create")
-    @Operation(summary = "创建规则场景(场景联动")
+    @Operation(summary = "创建场景联动")
     @PreAuthorize("@ss.hasPermission('iot:rule-scene:create')")
     public CommonResult<Long> createRuleScene(@Valid @RequestBody IotRuleSceneSaveReqVO createReqVO) {
         return success(ruleSceneService.createRuleScene(createReqVO));
     }
 
     @PutMapping("/update")
-    @Operation(summary = "更新规则场景(场景联动")
+    @Operation(summary = "更新场景联动")
     @PreAuthorize("@ss.hasPermission('iot:rule-scene:update')")
     public CommonResult<Boolean> updateRuleScene(@Valid @RequestBody IotRuleSceneSaveReqVO updateReqVO) {
         ruleSceneService.updateRuleScene(updateReqVO);
@@ -46,7 +45,7 @@ public class IotRuleSceneController {
     }
 
     @DeleteMapping("/delete")
-    @Operation(summary = "删除规则场景(场景联动")
+    @Operation(summary = "删除场景联动")
     @Parameter(name = "id", description = "编号", required = true)
     @PreAuthorize("@ss.hasPermission('iot:rule-scene:delete')")
     public CommonResult<Boolean> deleteRuleScene(@RequestParam("id") Long id) {
@@ -55,7 +54,7 @@ public class IotRuleSceneController {
     }
 
     @GetMapping("/get")
-    @Operation(summary = "获得规则场景(场景联动")
+    @Operation(summary = "获得场景联动")
     @Parameter(name = "id", description = "编号", required = true, example = "1024")
     @PreAuthorize("@ss.hasPermission('iot:rule-scene:query')")
     public CommonResult<IotRuleSceneRespVO> getRuleScene(@RequestParam("id") Long id) {
@@ -64,7 +63,7 @@ public class IotRuleSceneController {
     }
 
     @GetMapping("/page")
-    @Operation(summary = "获得规则场景(场景联动分页")
+    @Operation(summary = "获得场景联动分页")
     @PreAuthorize("@ss.hasPermission('iot:rule-scene:query')")
     public CommonResult<PageResult<IotRuleSceneRespVO>> getRuleScenePage(@Valid IotRuleScenePageReqVO pageReqVO) {
         PageResult<IotRuleSceneDO> pageResult = ruleSceneService.getRuleScenePage(pageReqVO);

+ 1 - 1
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/config/IotDataBridgeAbstractConfig.java

@@ -18,7 +18,7 @@ import lombok.Data;
 @JsonSubTypes({
         @JsonSubTypes.Type(value = IotDataBridgeHttpConfig.class, name = "1"),
         @JsonSubTypes.Type(value = IotDataBridgeMqttConfig.class, name = "10"),
-        @JsonSubTypes.Type(value = IotDataBridgeRedisStreamMQConfig.class, name = "21"),
+        @JsonSubTypes.Type(value = IotDataBridgeRedisStreamConfig.class, name = "21"),
         @JsonSubTypes.Type(value = IotDataBridgeRocketMQConfig.class, name = "30"),
         @JsonSubTypes.Type(value = IotDataBridgeRabbitMQConfig.class, name = "31"),
         @JsonSubTypes.Type(value = IotDataBridgeKafkaMQConfig.class, name = "32"),

+ 1 - 2
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/config/IotDataBridgeRedisStreamMQConfig.java → yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/config/IotDataBridgeRedisStreamConfig.java

@@ -2,14 +2,13 @@ package cn.iocoder.yudao.module.iot.controller.admin.rule.vo.databridge.config;
 
 import lombok.Data;
 
-// TODO @puhui999:MQ 可以去掉哈。stream 更精准
 /**
  * IoT Redis Stream 配置 {@link IotDataBridgeAbstractConfig} 实现类
  *
  * @author HUIHUI
  */
 @Data
-public class IotDataBridgeRedisStreamMQConfig extends IotDataBridgeAbstractConfig {
+public class IotDataBridgeRedisStreamConfig extends IotDataBridgeAbstractConfig {
 
     /**
      * Redis 服务器地址

+ 0 - 2
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/package-info.java

@@ -1,2 +0,0 @@
-// TODO @芋艿:占位
-package cn.iocoder.yudao.module.iot.controller.admin.rule.vo;

+ 1 - 1
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/scene/IotRuleScenePageReqVO.java

@@ -13,7 +13,7 @@ import java.time.LocalDateTime;
 
 import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
 
-@Schema(description = "管理后台 - IoT 规则场景(场景联动分页 Request VO")
+@Schema(description = "管理后台 - IoT 场景联动分页 Request VO")
 @Data
 @EqualsAndHashCode(callSuper = true)
 @ToString(callSuper = true)

+ 4 - 5
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/scene/IotRuleSceneRespVO.java

@@ -1,14 +1,13 @@
 package cn.iocoder.yudao.module.iot.controller.admin.rule.vo.scene;
 
-import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.scene.config.IotRuleSceneActionConfig;
-import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.scene.config.IotRuleSceneTriggerConfig;
+import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotRuleSceneDO;
 import io.swagger.v3.oas.annotations.media.Schema;
 import lombok.Data;
 
 import java.time.LocalDateTime;
 import java.util.List;
 
-@Schema(description = "管理后台 - IoT 规则场景(场景联动 Response VO")
+@Schema(description = "管理后台 - IoT 场景联动 Response VO")
 @Data
 public class IotRuleSceneRespVO {
 
@@ -25,10 +24,10 @@ public class IotRuleSceneRespVO {
     private Integer status;
 
     @Schema(description = "触发器数组", requiredMode = Schema.RequiredMode.REQUIRED)
-    private List<IotRuleSceneTriggerConfig> triggers;
+    private List<IotRuleSceneDO.TriggerConfig> triggers;
 
     @Schema(description = "执行器数组", requiredMode = Schema.RequiredMode.REQUIRED)
-    private List<IotRuleSceneActionConfig> actions;
+    private List<IotRuleSceneDO.ActionConfig> actions;
 
     @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
     private LocalDateTime createTime;

+ 4 - 5
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/scene/IotRuleSceneSaveReqVO.java

@@ -2,8 +2,7 @@ package cn.iocoder.yudao.module.iot.controller.admin.rule.vo.scene;
 
 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
 import cn.iocoder.yudao.framework.common.validation.InEnum;
-import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.scene.config.IotRuleSceneActionConfig;
-import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.scene.config.IotRuleSceneTriggerConfig;
+import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotRuleSceneDO;
 import io.swagger.v3.oas.annotations.media.Schema;
 import jakarta.validation.constraints.NotEmpty;
 import jakarta.validation.constraints.NotNull;
@@ -11,7 +10,7 @@ import lombok.Data;
 
 import java.util.List;
 
-@Schema(description = "管理后台 - IoT 规则场景(场景联动新增/修改 Request VO")
+@Schema(description = "管理后台 - IoT 场景联动新增/修改 Request VO")
 @Data
 public class IotRuleSceneSaveReqVO {
 
@@ -32,10 +31,10 @@ public class IotRuleSceneSaveReqVO {
 
     @Schema(description = "触发器数组", requiredMode = Schema.RequiredMode.REQUIRED)
     @NotEmpty(message = "触发器数组不能为空")
-    private List<IotRuleSceneTriggerConfig> triggers;
+    private List<IotRuleSceneDO.TriggerConfig> triggers;
 
     @Schema(description = "执行器数组", requiredMode = Schema.RequiredMode.REQUIRED)
     @NotEmpty(message = "执行器数组不能为空")
-    private List<IotRuleSceneActionConfig> actions;
+    private List<IotRuleSceneDO.ActionConfig> actions;
 
 }

+ 0 - 38
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/scene/config/IotRuleSceneActionConfig.java

@@ -1,38 +0,0 @@
-package cn.iocoder.yudao.module.iot.controller.admin.rule.vo.scene.config;
-
-import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotDataBridgeDO;
-import cn.iocoder.yudao.module.iot.enums.rule.IotRuleSceneActionTypeEnum;
-import lombok.Data;
-
-// TODO @puhui999:这个要不内嵌到 IoTRuleSceneDO 里面?
-/**
- * 执行器配置
- *
- * @author 芋道源码
- */
-@Data
-public class IotRuleSceneActionConfig {
-
-    /**
-     * 执行类型
-     *
-     * 枚举 {@link IotRuleSceneActionTypeEnum}
-     */
-    private Integer type;
-
-    /**
-     * 设备控制
-     *
-     * 必填:当 {@link #type} 为 {@link IotRuleSceneActionTypeEnum#DEVICE_CONTROL} 时
-     */
-    private IotRuleSceneActionDeviceControl deviceControl;
-
-    /**
-     * 数据桥接编号
-     *
-     * 必填:当 {@link #type} 为 {@link IotRuleSceneActionTypeEnum#DATA_BRIDGE} 时
-     * 关联:{@link IotDataBridgeDO#getId()}
-     */
-    private Long dataBridgeId;
-
-}

+ 0 - 58
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/scene/config/IotRuleSceneActionDeviceControl.java

@@ -1,58 +0,0 @@
-package cn.iocoder.yudao.module.iot.controller.admin.rule.vo.scene.config;
-
-import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO;
-import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO;
-import cn.iocoder.yudao.module.iot.enums.device.IotDeviceMessageIdentifierEnum;
-import cn.iocoder.yudao.module.iot.enums.device.IotDeviceMessageTypeEnum;
-import lombok.Data;
-
-import java.util.List;
-import java.util.Map;
-
-// TODO @puhui999:这个要不内嵌到 IoTRuleSceneDO 里面?
-/**
- * 执行设备控制
- *
- * @author 芋道源码
- */
-@Data
-public class IotRuleSceneActionDeviceControl {
-
-    /**
-     * 产品标识
-     *
-     * 关联 {@link IotProductDO#getProductKey()}
-     */
-    private String productKey;
-    /**
-     * 设备名称数组
-     *
-     * 关联 {@link IotDeviceDO#getDeviceName()}
-     */
-    private List<String> deviceNames;
-
-    /**
-     * 消息类型
-     *
-     * 枚举 {@link IotDeviceMessageTypeEnum#PROPERTY}、{@link IotDeviceMessageTypeEnum#SERVICE}
-     */
-    private String type;
-    /**
-     * 消息标识符
-     *
-     * 枚举 {@link IotDeviceMessageIdentifierEnum}
-     *
-     * 1. 属性设置:对应 {@link IotDeviceMessageIdentifierEnum#PROPERTY_SET}
-     * 2. 服务调用:对应 {@link IotDeviceMessageIdentifierEnum#SERVICE_INVOKE}
-     */
-    private String identifier;
-
-    /**
-     * 具体数据
-     *
-     * 1. 属性设置:在 {@link #type} 是 {@link IotDeviceMessageTypeEnum#PROPERTY} 时,对应 properties
-     * 2. 服务调用:在 {@link #type} 是 {@link IotDeviceMessageTypeEnum#SERVICE} 时,对应 params
-     */
-    private Map<String, Object> data;
-
-}

+ 0 - 38
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/scene/config/IotRuleSceneTriggerCondition.java

@@ -1,38 +0,0 @@
-package cn.iocoder.yudao.module.iot.controller.admin.rule.vo.scene.config;
-
-import cn.iocoder.yudao.module.iot.enums.device.IotDeviceMessageIdentifierEnum;
-import cn.iocoder.yudao.module.iot.enums.device.IotDeviceMessageTypeEnum;
-import lombok.Data;
-
-import java.util.List;
-
-// TODO @puhui999:这个要不内嵌到 IoTRuleSceneDO 里面?
-/**
- * 触发条件
- *
- * @author 芋道源码
- */
-@Data
-public class IotRuleSceneTriggerCondition {
-
-    /**
-     * 消息类型
-     *
-     * 枚举 {@link IotDeviceMessageTypeEnum}
-     */
-    private String type;
-    /**
-     * 消息标识符
-     *
-     * 枚举 {@link IotDeviceMessageIdentifierEnum}
-     */
-    private String identifier;
-
-    /**
-     * 参数数组
-     *
-     * 参数与参数之间,是“或”的关系
-     */
-    private List<IotRuleSceneTriggerConditionParameter> parameters;
-
-}

+ 0 - 38
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/scene/config/IotRuleSceneTriggerConditionParameter.java

@@ -1,38 +0,0 @@
-package cn.iocoder.yudao.module.iot.controller.admin.rule.vo.scene.config;
-
-import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotThingModelDO;
-import cn.iocoder.yudao.module.iot.enums.rule.IotRuleSceneTriggerConditionParameterOperatorEnum;
-import lombok.Data;
-
-// TODO @puhui999:这个要不内嵌到 IoTRuleSceneDO 里面?
-/**
- * 触发条件参数
- *
- * @author 芋道源码
- */
-@Data
-public class IotRuleSceneTriggerConditionParameter {
-
-    /**
-     * 标识符(属性、事件、服务)
-     *
-     * 关联 {@link IotThingModelDO#getIdentifier()}
-     */
-    private String identifier;
-
-    /**
-     * 操作符
-     *
-     * 枚举 {@link IotRuleSceneTriggerConditionParameterOperatorEnum}
-     */
-    private String operator;
-
-    /**
-     * 比较值
-     *
-     * 如果有多个值,则使用 "," 分隔,类似 "1,2,3"。
-     * 例如说,{@link IotRuleSceneTriggerConditionParameterOperatorEnum#IN}、{@link IotRuleSceneTriggerConditionParameterOperatorEnum#BETWEEN}
-     */
-    private String value;
-
-}

+ 0 - 54
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/scene/config/IotRuleSceneTriggerConfig.java

@@ -1,54 +0,0 @@
-package cn.iocoder.yudao.module.iot.controller.admin.rule.vo.scene.config;
-
-import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO;
-import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO;
-import cn.iocoder.yudao.module.iot.enums.rule.IotRuleSceneTriggerTypeEnum;
-import lombok.Data;
-
-import java.util.List;
-
-// TODO @puhui999:这个要不内嵌到 IoTRuleSceneDO 里面?
-/**
- * 触发器配置
- *
- * @author 芋道源码
- */
-@Data
-public class IotRuleSceneTriggerConfig {
-
-    /**
-     * 触发类型
-     *
-     * 枚举 {@link IotRuleSceneTriggerTypeEnum}
-     */
-    private Integer type;
-
-    /**
-     * 产品标识
-     *
-     * 关联 {@link IotProductDO#getProductKey()}
-     */
-    private String productKey;
-    /**
-     * 设备名称数组
-     *
-     * 关联 {@link IotDeviceDO#getDeviceName()}
-     */
-    private List<String> deviceNames;
-
-    /**
-     * 触发条件数组
-     *
-     * 必填:当 {@link #type} 为 {@link IotRuleSceneTriggerTypeEnum#DEVICE} 时
-     * 条件与条件之间,是“或”的关系
-     */
-    private List<IotRuleSceneTriggerCondition> conditions;
-
-    /**
-     * CRON 表达式
-     *
-     * 必填:当 {@link #type} 为 {@link IotRuleSceneTriggerTypeEnum#TIMER} 时
-     */
-    private String cronExpression;
-
-}

+ 2 - 2
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/IotThingModelController.http

@@ -174,7 +174,7 @@ GET {{baseUrl}}/iot/product-thing-model/get?id=67
 tenant-id: {{adminTenentId}}
 Authorization: Bearer {{token}}
 
-### 请求 /iot/product-thing-model/tsl-by-product-id 接口 => 成功
-GET {{baseUrl}}/iot/product-thing-model/tsl-by-product-id?productId=1001
+### 请求 /iot/product-thing-model/get-tsl 接口 => 成功
+GET {{baseUrl}}/iot/product-thing-model/get-tsl?productId=1001
 tenant-id: {{adminTenentId}}
 Authorization: Bearer {{token}}

+ 27 - 4
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/IotThingModelController.java

@@ -1,10 +1,13 @@
 package cn.iocoder.yudao.module.iot.controller.admin.thingmodel;
 
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.util.ObjUtil;
 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
 import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.vo.*;
 import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotThingModelDO;
+import cn.iocoder.yudao.module.iot.enums.thingmodel.IotThingModelTypeEnum;
 import cn.iocoder.yudao.module.iot.service.thingmodel.IotThingModelService;
 import io.swagger.v3.oas.annotations.Operation;
 import io.swagger.v3.oas.annotations.Parameter;
@@ -18,6 +21,8 @@ import org.springframework.web.bind.annotation.*;
 import java.util.List;
 
 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
+import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
+import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.filterList;
 
 @Tag(name = "管理后台 - IoT 产品物模型")
 @RestController
@@ -61,13 +66,31 @@ public class IotThingModelController {
         return success(BeanUtils.toBean(thingModel, IotThingModelRespVO.class));
     }
 
-    // TODO @puhui999:要不叫 get-tsl,去掉 product-id;后续,把
-    @GetMapping("/tsl-by-product-id")
+    @GetMapping("/get-tsl")
     @Operation(summary = "获得产品物模型 TSL")
     @Parameter(name = "productId", description = "产品 ID", required = true, example = "1024")
     @PreAuthorize("@ss.hasPermission('iot:thing-model:query')")
-    public CommonResult<IotThingModelTSLRespVO> getThingModelTslByProductId(@RequestParam("productId") Long productId) {
-        return success(thingModelService.getThingModelTslByProductId(productId));
+    public CommonResult<IotThingModelTSLRespVO> getThingModelTsl(@RequestParam("productId") Long productId) {
+        IotThingModelTSLRespVO tslRespVO = new IotThingModelTSLRespVO();
+        // 1. 获得产品所有物模型定义
+        List<IotThingModelDO> thingModels = thingModelService.getThingModelListByProductId(productId);
+        if (CollUtil.isEmpty(thingModels)) {
+            return success(tslRespVO);
+        }
+
+        // 2.1 设置公共部分参数
+        IotThingModelDO thingModel = thingModels.get(0);
+        tslRespVO.setProductId(thingModel.getProductId()).setProductKey(thingModel.getProductKey());
+        // 2.2 处理属性列表
+        tslRespVO.setProperties(convertList(filterList(thingModels, item ->
+                ObjUtil.equal(IotThingModelTypeEnum.PROPERTY.getType(), item.getType())), IotThingModelDO::getProperty));
+        // 2.3 处理服务列表
+        tslRespVO.setServices(convertList(filterList(thingModels, item ->
+                ObjUtil.equal(IotThingModelTypeEnum.SERVICE.getType(), item.getType())), IotThingModelDO::getService));
+        // 2.4 处理事件列表
+        tslRespVO.setEvents(convertList(filterList(thingModels, item ->
+                ObjUtil.equal(IotThingModelTypeEnum.EVENT.getType(), item.getType())), IotThingModelDO::getEvent));
+        return success(tslRespVO);
     }
 
     @GetMapping("/list")

+ 9 - 6
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/dataType/ThingModelArrayDataSpecs.java

@@ -1,6 +1,10 @@
 package cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.dataType;
 
 import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import jakarta.validation.Valid;
+import jakarta.validation.constraints.NotEmpty;
+import jakarta.validation.constraints.NotNull;
+import jakarta.validation.constraints.Pattern;
 import lombok.Data;
 import lombok.EqualsAndHashCode;
 
@@ -16,18 +20,17 @@ import java.util.List;
 @JsonIgnoreProperties({"dataType"}) // 忽略子类中的 dataType 字段,从而避免重复
 public class ThingModelArrayDataSpecs extends ThingModelDataSpecs {
 
-    /**
-     * 数组中的元素个数
-     */
+    @NotNull(message = "数组元素个数不能为空")
     private Integer size;
-    /**
-     * 数组中的元素的数据类型。可选值:struct、int、float、double 或 text
-     */
+
+    @NotEmpty(message = "数组元素的数据类型不能为空")
+    @Pattern(regexp = "^(struct|int|float|double|text)$", message = "数组元素的数据类型必须为:struct、int、float、double 或 text")
     private String childDataType;
     /**
      * 数据类型(childDataType)为列表型 struct 的数据规范存储在 dataSpecsList 中
      * 此时 struct 取值范围为:int、float、double、text、date、enum、bool
      */
+    @Valid
     private List<ThingModelDataSpecs> dataSpecsList;
 
 }

+ 8 - 9
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/dataType/ThingModelBoolOrEnumDataSpecs.java

@@ -1,6 +1,9 @@
 package cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.dataType;
 
 import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import jakarta.validation.constraints.NotEmpty;
+import jakarta.validation.constraints.NotNull;
+import jakarta.validation.constraints.Pattern;
 import lombok.Data;
 import lombok.EqualsAndHashCode;
 
@@ -16,16 +19,12 @@ import lombok.EqualsAndHashCode;
 @JsonIgnoreProperties({"dataType"}) // 忽略子类中的 dataType 字段,从而避免重复
 public class ThingModelBoolOrEnumDataSpecs extends ThingModelDataSpecs {
 
-    // TODO @puhui999:要不写下参数校验?这样,注释可以简洁一点
-    /**
-     * 枚举项的名称。
-     * 可包含中文、大小写英文字母、数字、下划线(_)和短划线(-)
-     * 必须以中文、英文字母或数字开头,长度不超过 20 个字符
-     */
+    @NotEmpty(message = "枚举项的名称不能为空")
+    @Pattern(regexp = "^[\\u4e00-\\u9fa5a-zA-Z0-9][\\u4e00-\\u9fa5a-zA-Z0-9_-]{0,19}$",
+            message = "枚举项的名称只能包含中文、大小写英文字母、数字、下划线和短划线,必须以中文、英文字母或数字开头,长度不超过 20 个字符")
     private String name;
-    /**
-     * 枚举值。
-     */
+
+    @NotNull(message = "枚举值不能为空")
     private Integer value;
 
 }

+ 2 - 0
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/dataType/ThingModelDateOrTextDataSpecs.java

@@ -1,6 +1,7 @@
 package cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.dataType;
 
 import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import jakarta.validation.constraints.Max;
 import lombok.Data;
 import lombok.EqualsAndHashCode;
 
@@ -20,6 +21,7 @@ public class ThingModelDateOrTextDataSpecs extends ThingModelDataSpecs {
      * 数据长度,单位为字节。取值不能超过 2048。
      * 当 dataType 为 text 时,需传入该参数。
      */
+    @Max(value = 2048, message = "数据长度不能超过 2048")
     private Integer length;
     /**
      * 默认值,可选参数,用于存储默认值。

+ 8 - 3
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/dataType/ThingModelNumericDataSpec.java

@@ -1,6 +1,8 @@
 package cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.dataType;
 
 import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import jakarta.validation.constraints.NotEmpty;
+import jakarta.validation.constraints.Pattern;
 import lombok.Data;
 import lombok.EqualsAndHashCode;
 
@@ -18,18 +20,21 @@ public class ThingModelNumericDataSpec extends ThingModelDataSpecs {
 
     /**
      * 最大值,需转为字符串类型。值必须与 dataType 类型一致。
-     * 例如,当 dataType 为 int 时,取值为 "200",而不是 200。
      */
+    @NotEmpty(message = "最大值不能为空")
+    @Pattern(regexp = "^-?\\d+(\\.\\d+)?$", message = "最大值必须为数值类型")
     private String max;
     /**
      * 最小值,需转为字符串类型。值必须与 dataType 类型一致。
-     * 例如,当 dataType 为 int 时,取值为 "0",而不是 0。
      */
+    @NotEmpty(message = "最小值不能为空")
+    @Pattern(regexp = "^-?\\d+(\\.\\d+)?$", message = "最小值必须为数值类型")
     private String min;
     /**
      * 步长,需转为字符串类型。值必须与 dataType 类型一致。
-     * 例如,当 dataType 为 int 时,取值为 "10",而不是 10。
      */
+    @NotEmpty(message = "步长不能为空")
+    @Pattern(regexp = "^-?\\d+(\\.\\d+)?$", message = "步长必须为数值类型")
     private String step;
     /**
      * 精度。当 dataType 为 float 或 double 时可选传入。

+ 19 - 14
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/dataType/ThingModelStructDataSpecs.java

@@ -1,7 +1,11 @@
 package cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.dataType;
 
+import cn.iocoder.yudao.framework.common.validation.InEnum;
 import cn.iocoder.yudao.module.iot.enums.thingmodel.IotThingModelAccessModeEnum;
 import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import jakarta.validation.Valid;
+import jakarta.validation.constraints.NotEmpty;
+import jakarta.validation.constraints.Pattern;
 import lombok.Data;
 import lombok.EqualsAndHashCode;
 
@@ -17,35 +21,36 @@ import java.util.List;
 @JsonIgnoreProperties({"dataType"}) // 忽略子类中的 dataType 字段,从而避免重复
 public class ThingModelStructDataSpecs extends ThingModelDataSpecs {
 
-    /**
-     * 属性标识符
-     */
+    @NotEmpty(message = "属性标识符不能为空")
+    @Pattern(regexp = "^[a-zA-Z][a-zA-Z0-9_]{0,31}$", message = "属性标识符只能由字母、数字和下划线组成,必须以字母开头,长度不超过 32 个字符")
     private String identifier;
-    /**
-     * 属性名称
-     */
+
+    @NotEmpty(message = "属性名称不能为空")
     private String name;
-    /**
-     * 云端可以对该属性进行的操作类型
-     *
-     * 枚举 {@link IotThingModelAccessModeEnum}
-     */
+
+    @NotEmpty(message = "操作类型不能为空")
+    @InEnum(IotThingModelAccessModeEnum.class)
     private String accessMode;
+
     /**
      * 是否是标准品类的必选服务
      */
     private Boolean required;
-    /**
-     * struct 数据的数据类型
-     */
+
+    @NotEmpty(message = "数据类型不能为空")
+    @Pattern(regexp = "^(int|float|double|text|date|enum|bool)$", message = "数据类型必须为:int、float、double、text、date、enum、bool")
     private String childDataType;
+
     /**
      * 数据类型(dataType)为非列表型(int、float、double、text、date、array)的数据规范存储在 dataSpecs 中
      */
+    @Valid
     private ThingModelDataSpecs dataSpecs;
+
     /**
      * 数据类型(dataType)为列表型(enum、bool、struct)的数据规范存储在 dataSpecsList 中
      */
+    @Valid
     private List<ThingModelDataSpecs> dataSpecsList;
 
 }

+ 189 - 6
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/IotRuleSceneDO.java

@@ -1,8 +1,14 @@
 package cn.iocoder.yudao.module.iot.dal.dataobject.rule;
 
 import cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO;
-import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.scene.config.IotRuleSceneActionConfig;
-import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.scene.config.IotRuleSceneTriggerConfig;
+import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO;
+import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO;
+import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotThingModelDO;
+import cn.iocoder.yudao.module.iot.enums.device.IotDeviceMessageIdentifierEnum;
+import cn.iocoder.yudao.module.iot.enums.device.IotDeviceMessageTypeEnum;
+import cn.iocoder.yudao.module.iot.enums.rule.IotRuleSceneActionTypeEnum;
+import cn.iocoder.yudao.module.iot.enums.rule.IotRuleSceneTriggerConditionParameterOperatorEnum;
+import cn.iocoder.yudao.module.iot.enums.rule.IotRuleSceneTriggerTypeEnum;
 import com.baomidou.mybatisplus.annotation.KeySequence;
 import com.baomidou.mybatisplus.annotation.TableField;
 import com.baomidou.mybatisplus.annotation.TableId;
@@ -14,13 +20,14 @@ import lombok.Data;
 import lombok.NoArgsConstructor;
 
 import java.util.List;
+import java.util.Map;
 
 /**
- * IoT 规则场景(场景联动 DO
+ * IoT 场景联动 DO
  *
  * @author 芋道源码
  */
-@TableName("iot_rule_scene")
+@TableName(value = "iot_rule_scene", autoResultMap = true)
 @KeySequence("iot_rule_scene_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
 @Data
 @Builder
@@ -52,12 +59,188 @@ public class IotRuleSceneDO extends TenantBaseDO {
      * 触发器数组
      */
     @TableField(typeHandler = JacksonTypeHandler.class)
-    private List<IotRuleSceneTriggerConfig> triggers;
+    private List<TriggerConfig> triggers;
 
     /**
      * 执行器数组
      */
     @TableField(typeHandler = JacksonTypeHandler.class)
-    private List<IotRuleSceneActionConfig> actions;
+    private List<ActionConfig> actions;
+
+    /**
+     * 触发器配置
+     */
+    @Data
+    public static class TriggerConfig {
+
+        /**
+         * 触发类型
+         *
+         * 枚举 {@link IotRuleSceneTriggerTypeEnum}
+         */
+        private Integer type;
+
+        /**
+         * 产品标识
+         *
+         * 关联 {@link IotProductDO#getProductKey()}
+         */
+        private String productKey;
+        /**
+         * 设备名称数组
+         *
+         * 关联 {@link IotDeviceDO#getDeviceName()}
+         */
+        private List<String> deviceNames;
+
+        /**
+         * 触发条件数组
+         *
+         * 必填:当 {@link #type} 为 {@link IotRuleSceneTriggerTypeEnum#DEVICE} 时
+         * 条件与条件之间,是“或”的关系
+         */
+        private List<TriggerCondition> conditions;
+
+        /**
+         * CRON 表达式
+         *
+         * 必填:当 {@link #type} 为 {@link IotRuleSceneTriggerTypeEnum#TIMER} 时
+         */
+        private String cronExpression;
+
+    }
+
+    /**
+     * 触发条件
+     */
+    @Data
+    public static class TriggerCondition {
+
+        /**
+         * 消息类型
+         *
+         * 枚举 {@link IotDeviceMessageTypeEnum}
+         */
+        private String type;
+        /**
+         * 消息标识符
+         *
+         * 枚举 {@link IotDeviceMessageIdentifierEnum}
+         */
+        private String identifier;
+
+        /**
+         * 参数数组
+         *
+         * 参数与参数之间,是“或”的关系
+         */
+        private List<TriggerConditionParameter> parameters;
+
+    }
+
+    /**
+     * 触发条件参数
+     */
+    @Data
+    public static class TriggerConditionParameter {
+
+        /**
+         * 标识符(属性、事件、服务)
+         *
+         * 关联 {@link IotThingModelDO#getIdentifier()}
+         */
+        private String identifier;
+
+        /**
+         * 操作符
+         *
+         * 枚举 {@link IotRuleSceneTriggerConditionParameterOperatorEnum}
+         */
+        private String operator;
+
+        /**
+         * 比较值
+         *
+         * 如果有多个值,则使用 "," 分隔,类似 "1,2,3"。
+         * 例如说,{@link IotRuleSceneTriggerConditionParameterOperatorEnum#IN}、{@link IotRuleSceneTriggerConditionParameterOperatorEnum#BETWEEN}
+         */
+        private String value;
+
+    }
+
+    /**
+     * 执行器配置
+     */
+    @Data
+    public static class ActionConfig {
+
+        /**
+         * 执行类型
+         *
+         * 枚举 {@link IotRuleSceneActionTypeEnum}
+         */
+        private Integer type;
+
+        /**
+         * 设备控制
+         *
+         * 必填:当 {@link #type} 为 {@link IotRuleSceneActionTypeEnum#DEVICE_CONTROL} 时
+         */
+        private ActionDeviceControl deviceControl;
+
+        /**
+         * 数据桥接编号
+         *
+         * 必填:当 {@link #type} 为 {@link IotRuleSceneActionTypeEnum#DATA_BRIDGE} 时
+         * 关联:{@link IotDataBridgeDO#getId()}
+         */
+        private Long dataBridgeId;
+
+    }
+
+    /**
+     * 执行设备控制
+     */
+    @Data
+    public static class ActionDeviceControl {
+
+        /**
+         * 产品标识
+         *
+         * 关联 {@link IotProductDO#getProductKey()}
+         */
+        private String productKey;
+        /**
+         * 设备名称数组
+         *
+         * 关联 {@link IotDeviceDO#getDeviceName()}
+         */
+        private List<String> deviceNames;
+
+        /**
+         * 消息类型
+         *
+         * 枚举 {@link IotDeviceMessageTypeEnum#PROPERTY}、{@link IotDeviceMessageTypeEnum#SERVICE}
+         */
+        private String type;
+        /**
+         * 消息标识符
+         *
+         * 枚举 {@link IotDeviceMessageIdentifierEnum}
+         *
+         * 1. 属性设置:对应 {@link IotDeviceMessageIdentifierEnum#PROPERTY_SET}
+         * 2. 服务调用:对应 {@link IotDeviceMessageIdentifierEnum#SERVICE_INVOKE}
+         */
+        private String identifier;
+
+        /**
+         * 具体数据
+         *
+         * 1. 属性设置:在 {@link #type} 是 {@link IotDeviceMessageTypeEnum#PROPERTY} 时,对应 properties
+         * 2. 服务调用:在 {@link #type} 是 {@link IotDeviceMessageTypeEnum#SERVICE} 时,对应 params
+         */
+        private Map<String, Object> data;
+
+    }
 
 }

+ 1 - 1
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/rule/IotRuleSceneMapper.java

@@ -8,7 +8,7 @@ import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotRuleSceneDO;
 import org.apache.ibatis.annotations.Mapper;
 
 /**
- * IoT 规则场景(场景联动 Mapper
+ * IoT 场景联动 Mapper
  *
  * @author HUIHUI
  */

+ 7 - 7
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/IotRuleSceneService.java

@@ -18,7 +18,7 @@ import java.util.List;
 public interface IotRuleSceneService {
 
     /**
-     * 创建规则场景(场景联动
+     * 创建场景联动
      *
      * @param createReqVO 创建信息
      * @return 编号
@@ -26,32 +26,32 @@ public interface IotRuleSceneService {
     Long createRuleScene(@Valid IotRuleSceneSaveReqVO createReqVO);
 
     /**
-     * 更新规则场景(场景联动
+     * 更新场景联动
      *
      * @param updateReqVO 更新信息
      */
     void updateRuleScene(@Valid IotRuleSceneSaveReqVO updateReqVO);
 
     /**
-     * 删除规则场景(场景联动
+     * 删除场景联动
      *
      * @param id 编号
      */
     void deleteRuleScene(Long id);
 
     /**
-     * 获得规则场景(场景联动
+     * 获得场景联动
      *
      * @param id 编号
-     * @return 规则场景(场景联动
+     * @return 场景联动
      */
     IotRuleSceneDO getRuleScene(Long id);
 
     /**
-     * 获得规则场景(场景联动分页
+     * 获得场景联动分页
      *
      * @param pageReqVO 分页查询
-     * @return 规则场景(场景联动分页
+     * @return 场景联动分页
      */
     PageResult<IotRuleSceneDO> getRuleScenePage(IotRuleScenePageReqVO pageReqVO);
 

+ 37 - 38
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/IotRuleSceneServiceImpl.java

@@ -17,7 +17,6 @@ import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore;
 import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils;
 import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.scene.IotRuleScenePageReqVO;
 import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.scene.IotRuleSceneSaveReqVO;
-import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.scene.config.*;
 import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotRuleSceneDO;
 import cn.iocoder.yudao.module.iot.dal.mysql.rule.IotRuleSceneMapper;
 import cn.iocoder.yudao.module.iot.enums.device.IotDeviceMessageIdentifierEnum;
@@ -118,82 +117,82 @@ public class IotRuleSceneServiceImpl implements IotRuleSceneService {
         if (true) {
             IotRuleSceneDO ruleScene01 = new IotRuleSceneDO();
             ruleScene01.setTriggers(CollUtil.newArrayList());
-            IotRuleSceneTriggerConfig trigger01 = new IotRuleSceneTriggerConfig();
+            IotRuleSceneDO.TriggerConfig trigger01 = new IotRuleSceneDO.TriggerConfig();
             trigger01.setType(IotRuleSceneTriggerTypeEnum.DEVICE.getType());
             trigger01.setConditions(CollUtil.newArrayList());
             // 属性
-            IotRuleSceneTriggerCondition condition01 = new IotRuleSceneTriggerCondition();
+            IotRuleSceneDO.TriggerCondition condition01 = new IotRuleSceneDO.TriggerCondition();
             condition01.setType(IotDeviceMessageTypeEnum.PROPERTY.getType());
             condition01.setIdentifier(IotDeviceMessageIdentifierEnum.PROPERTY_REPORT.getIdentifier());
             condition01.setParameters(CollUtil.newArrayList());
-//            IotRuleSceneTriggerConditionParameter parameter010 = new IotRuleSceneTriggerConditionParameter();
+//            IotRuleSceneDO.TriggerConditionParameter parameter010 = new IotRuleSceneDO.TriggerConditionParameter();
 //            parameter010.setIdentifier("width");
 //            parameter010.setOperator(IotRuleSceneTriggerConditionParameterOperatorEnum.EQUALS.getOperator());
 //            parameter010.setValue("abc");
 //            condition01.getParameters().add(parameter010);
-            IotRuleSceneTriggerConditionParameter parameter011 = new IotRuleSceneTriggerConditionParameter();
+            IotRuleSceneDO.TriggerConditionParameter parameter011 = new IotRuleSceneDO.TriggerConditionParameter();
             parameter011.setIdentifier("width");
             parameter011.setOperator(IotRuleSceneTriggerConditionParameterOperatorEnum.EQUALS.getOperator());
             parameter011.setValue("1");
             condition01.getParameters().add(parameter011);
-            IotRuleSceneTriggerConditionParameter parameter012 = new IotRuleSceneTriggerConditionParameter();
+            IotRuleSceneDO.TriggerConditionParameter parameter012 = new IotRuleSceneDO.TriggerConditionParameter();
             parameter012.setIdentifier("width");
             parameter012.setOperator(IotRuleSceneTriggerConditionParameterOperatorEnum.NOT_EQUALS.getOperator());
             parameter012.setValue("2");
             condition01.getParameters().add(parameter012);
-            IotRuleSceneTriggerConditionParameter parameter013 = new IotRuleSceneTriggerConditionParameter();
+            IotRuleSceneDO.TriggerConditionParameter parameter013 = new IotRuleSceneDO.TriggerConditionParameter();
             parameter013.setIdentifier("width");
             parameter013.setOperator(IotRuleSceneTriggerConditionParameterOperatorEnum.GREATER_THAN.getOperator());
             parameter013.setValue("0");
             condition01.getParameters().add(parameter013);
-            IotRuleSceneTriggerConditionParameter parameter014 = new IotRuleSceneTriggerConditionParameter();
+            IotRuleSceneDO.TriggerConditionParameter parameter014 = new IotRuleSceneDO.TriggerConditionParameter();
             parameter014.setIdentifier("width");
             parameter014.setOperator(IotRuleSceneTriggerConditionParameterOperatorEnum.GREATER_THAN_OR_EQUALS.getOperator());
             parameter014.setValue("0");
             condition01.getParameters().add(parameter014);
-            IotRuleSceneTriggerConditionParameter parameter015 = new IotRuleSceneTriggerConditionParameter();
+            IotRuleSceneDO.TriggerConditionParameter parameter015 = new IotRuleSceneDO.TriggerConditionParameter();
             parameter015.setIdentifier("width");
             parameter015.setOperator(IotRuleSceneTriggerConditionParameterOperatorEnum.LESS_THAN.getOperator());
             parameter015.setValue("2");
             condition01.getParameters().add(parameter015);
-            IotRuleSceneTriggerConditionParameter parameter016 = new IotRuleSceneTriggerConditionParameter();
+            IotRuleSceneDO.TriggerConditionParameter parameter016 = new IotRuleSceneDO.TriggerConditionParameter();
             parameter016.setIdentifier("width");
             parameter016.setOperator(IotRuleSceneTriggerConditionParameterOperatorEnum.LESS_THAN_OR_EQUALS.getOperator());
             parameter016.setValue("2");
             condition01.getParameters().add(parameter016);
-            IotRuleSceneTriggerConditionParameter parameter017 = new IotRuleSceneTriggerConditionParameter();
+            IotRuleSceneDO.TriggerConditionParameter parameter017 = new IotRuleSceneDO.TriggerConditionParameter();
             parameter017.setIdentifier("width");
             parameter017.setOperator(IotRuleSceneTriggerConditionParameterOperatorEnum.IN.getOperator());
             parameter017.setValue("1,2,3");
             condition01.getParameters().add(parameter017);
-            IotRuleSceneTriggerConditionParameter parameter018 = new IotRuleSceneTriggerConditionParameter();
+            IotRuleSceneDO.TriggerConditionParameter parameter018 = new IotRuleSceneDO.TriggerConditionParameter();
             parameter018.setIdentifier("width");
             parameter018.setOperator(IotRuleSceneTriggerConditionParameterOperatorEnum.NOT_IN.getOperator());
             parameter018.setValue("0,2,3");
             condition01.getParameters().add(parameter018);
-            IotRuleSceneTriggerConditionParameter parameter019 = new IotRuleSceneTriggerConditionParameter();
+            IotRuleSceneDO.TriggerConditionParameter parameter019 = new IotRuleSceneDO.TriggerConditionParameter();
             parameter019.setIdentifier("width");
             parameter019.setOperator(IotRuleSceneTriggerConditionParameterOperatorEnum.BETWEEN.getOperator());
             parameter019.setValue("1,3");
             condition01.getParameters().add(parameter019);
-            IotRuleSceneTriggerConditionParameter parameter020 = new IotRuleSceneTriggerConditionParameter();
+            IotRuleSceneDO.TriggerConditionParameter parameter020 = new IotRuleSceneDO.TriggerConditionParameter();
             parameter020.setIdentifier("width");
             parameter020.setOperator(IotRuleSceneTriggerConditionParameterOperatorEnum.NOT_BETWEEN.getOperator());
             parameter020.setValue("2,3");
             condition01.getParameters().add(parameter020);
             trigger01.getConditions().add(condition01);
             // 状态
-            IotRuleSceneTriggerCondition condition02 = new IotRuleSceneTriggerCondition();
+            IotRuleSceneDO.TriggerCondition condition02 = new IotRuleSceneDO.TriggerCondition();
             condition02.setType(IotDeviceMessageTypeEnum.STATE.getType());
             condition02.setIdentifier(IotDeviceMessageIdentifierEnum.STATE_ONLINE.getIdentifier());
             condition02.setParameters(CollUtil.newArrayList());
             trigger01.getConditions().add(condition02);
             // 事件
-            IotRuleSceneTriggerCondition condition03 = new IotRuleSceneTriggerCondition();
+            IotRuleSceneDO.TriggerCondition condition03 = new IotRuleSceneDO.TriggerCondition();
             condition03.setType(IotDeviceMessageTypeEnum.EVENT.getType());
             condition03.setIdentifier("xxx");
             condition03.setParameters(CollUtil.newArrayList());
-            IotRuleSceneTriggerConditionParameter parameter030 = new IotRuleSceneTriggerConditionParameter();
+            IotRuleSceneDO.TriggerConditionParameter parameter030 = new IotRuleSceneDO.TriggerConditionParameter();
             parameter030.setIdentifier("width");
             parameter030.setOperator(IotRuleSceneTriggerConditionParameterOperatorEnum.EQUALS.getOperator());
             parameter030.setValue("1");
@@ -202,21 +201,21 @@ public class IotRuleSceneServiceImpl implements IotRuleSceneService {
             // 动作
             ruleScene01.setActions(CollUtil.newArrayList());
             // 设备控制
-            IotRuleSceneActionConfig action01 = new IotRuleSceneActionConfig();
+            IotRuleSceneDO.ActionConfig action01 = new IotRuleSceneDO.ActionConfig();
             action01.setType(IotRuleSceneActionTypeEnum.DEVICE_CONTROL.getType());
-            IotRuleSceneActionDeviceControl iotRuleSceneActionDeviceControl01 = new IotRuleSceneActionDeviceControl();
-            iotRuleSceneActionDeviceControl01.setProductKey("4aymZgOTOOCrDKRT");
-            iotRuleSceneActionDeviceControl01.setDeviceNames(ListUtil.of("small"));
-            iotRuleSceneActionDeviceControl01.setType(IotDeviceMessageTypeEnum.PROPERTY.getType());
-            iotRuleSceneActionDeviceControl01.setIdentifier(IotDeviceMessageIdentifierEnum.PROPERTY_SET.getIdentifier());
-            iotRuleSceneActionDeviceControl01.setData(MapUtil.<String, Object>builder()
+            IotRuleSceneDO.ActionDeviceControl actionDeviceControl01 = new IotRuleSceneDO.ActionDeviceControl();
+            actionDeviceControl01.setProductKey("4aymZgOTOOCrDKRT");
+            actionDeviceControl01.setDeviceNames(ListUtil.of("small"));
+            actionDeviceControl01.setType(IotDeviceMessageTypeEnum.PROPERTY.getType());
+            actionDeviceControl01.setIdentifier(IotDeviceMessageIdentifierEnum.PROPERTY_SET.getIdentifier());
+            actionDeviceControl01.setData(MapUtil.<String, Object>builder()
                     .put("power", 1)
                     .put("color", "red")
                     .build());
-            action01.setDeviceControl(iotRuleSceneActionDeviceControl01);
+            action01.setDeviceControl(actionDeviceControl01);
 //            ruleScene01.getActions().add(action01); // TODO 芋艿:先不测试了
             // 数据桥接(http)
-            IotRuleSceneActionConfig action02 = new IotRuleSceneActionConfig();
+            IotRuleSceneDO.ActionConfig action02 = new IotRuleSceneDO.ActionConfig();
             action02.setType(IotRuleSceneActionTypeEnum.DATA_BRIDGE.getType());
             action02.setDataBridgeId(1L);
             ruleScene01.getActions().add(action02);
@@ -226,7 +225,7 @@ public class IotRuleSceneServiceImpl implements IotRuleSceneService {
         List<IotRuleSceneDO> list = ruleSceneMapper.selectList();
         // TODO @芋艿:需要考虑开启状态
         return filterList(list, ruleScene -> {
-            for (IotRuleSceneTriggerConfig trigger : ruleScene.getTriggers()) {
+            for (IotRuleSceneDO.TriggerConfig trigger : ruleScene.getTriggers()) {
                 if (ObjUtil.notEqual(trigger.getProductKey(), productKey)) {
                     continue;
                 }
@@ -261,13 +260,13 @@ public class IotRuleSceneServiceImpl implements IotRuleSceneService {
         IotRuleSceneDO scene = new IotRuleSceneDO().setStatus(CommonStatusEnum.ENABLE.getStatus());
         if (true) {
             scene.setTenantId(1L);
-            IotRuleSceneTriggerConfig iotRuleSceneTriggerConfig = new IotRuleSceneTriggerConfig();
-            iotRuleSceneTriggerConfig.setType(IotRuleSceneTriggerTypeEnum.TIMER.getType());
-            scene.setTriggers(ListUtil.toList(iotRuleSceneTriggerConfig));
+            IotRuleSceneDO.TriggerConfig triggerConfig = new IotRuleSceneDO.TriggerConfig();
+            triggerConfig.setType(IotRuleSceneTriggerTypeEnum.TIMER.getType());
+            scene.setTriggers(ListUtil.toList(triggerConfig));
             // 动作
-            IotRuleSceneActionConfig action01 = new IotRuleSceneActionConfig();
+            IotRuleSceneDO.ActionConfig action01 = new IotRuleSceneDO.ActionConfig();
             action01.setType(IotRuleSceneActionTypeEnum.DEVICE_CONTROL.getType());
-            IotRuleSceneActionDeviceControl iotRuleSceneActionDeviceControl01 = new IotRuleSceneActionDeviceControl();
+            IotRuleSceneDO.ActionDeviceControl iotRuleSceneActionDeviceControl01 = new IotRuleSceneDO.ActionDeviceControl();
             iotRuleSceneActionDeviceControl01.setProductKey("4aymZgOTOOCrDKRT");
             iotRuleSceneActionDeviceControl01.setDeviceNames(ListUtil.of("small"));
             iotRuleSceneActionDeviceControl01.setType(IotDeviceMessageTypeEnum.PROPERTY.getType());
@@ -288,7 +287,7 @@ public class IotRuleSceneServiceImpl implements IotRuleSceneService {
             return;
         }
         // 1.2 判断是否有定时触发器,避免脏数据
-        IotRuleSceneTriggerConfig config = CollUtil.findOne(scene.getTriggers(),
+        IotRuleSceneDO.TriggerConfig config = CollUtil.findOne(scene.getTriggers(),
                 trigger -> ObjUtil.equals(trigger.getType(), IotRuleSceneTriggerTypeEnum.TIMER.getType()));
         if (config == null) {
             log.error("[executeRuleSceneByTimer][规则场景({}) 不存在定时触发器]", scene);
@@ -317,7 +316,7 @@ public class IotRuleSceneServiceImpl implements IotRuleSceneService {
 
         // 2. 匹配 trigger 触发器的条件
         return filterList(ruleScenes, ruleScene -> {
-            for (IotRuleSceneTriggerConfig trigger : ruleScene.getTriggers()) {
+            for (IotRuleSceneDO.TriggerConfig trigger : ruleScene.getTriggers()) {
                 // 2.1 非设备触发,不匹配
                 if (ObjUtil.notEqual(trigger.getType(), IotRuleSceneTriggerTypeEnum.DEVICE.getType())) {
                     return false;
@@ -328,13 +327,13 @@ public class IotRuleSceneServiceImpl implements IotRuleSceneService {
                     return false;
                 }
                 // 2.3 多个条件,只需要满足一个即可
-                IotRuleSceneTriggerCondition matchedCondition = CollUtil.findOne(trigger.getConditions(), condition -> {
+                IotRuleSceneDO.TriggerCondition matchedCondition = CollUtil.findOne(trigger.getConditions(), condition -> {
                     if (ObjUtil.notEqual(message.getType(), condition.getType())
                             || ObjUtil.notEqual(message.getIdentifier(), condition.getIdentifier())) {
                         return false;
                     }
                     // 多个条件参数,必须全部满足。所以,下面的逻辑就是找到一个不满足的条件参数
-                    IotRuleSceneTriggerConditionParameter notMatchedParameter = CollUtil.findOne(condition.getParameters(),
+                    IotRuleSceneDO.TriggerConditionParameter notMatchedParameter = CollUtil.findOne(condition.getParameters(),
                             parameter -> !isTriggerConditionParameterMatched(message, parameter, ruleScene, trigger));
                     return notMatchedParameter == null;
                 });
@@ -360,8 +359,8 @@ public class IotRuleSceneServiceImpl implements IotRuleSceneService {
      * @return 是否匹配
      */
     @SuppressWarnings({"unchecked", "DataFlowIssue"})
-    private boolean isTriggerConditionParameterMatched(IotDeviceMessage message, IotRuleSceneTriggerConditionParameter parameter,
-                                                       IotRuleSceneDO ruleScene, IotRuleSceneTriggerConfig trigger) {
+    private boolean isTriggerConditionParameterMatched(IotDeviceMessage message, IotRuleSceneDO.TriggerConditionParameter parameter,
+                                                       IotRuleSceneDO ruleScene, IotRuleSceneDO.TriggerConfig trigger) {
         // 1.1 校验操作符是否合法
         IotRuleSceneTriggerConditionParameterOperatorEnum operator =
                 IotRuleSceneTriggerConditionParameterOperatorEnum.operatorOf(parameter.getOperator());

+ 2 - 2
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/IotRuleSceneAction.java

@@ -1,6 +1,6 @@
 package cn.iocoder.yudao.module.iot.service.rule.action;
 
-import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.scene.config.IotRuleSceneActionConfig;
+import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotRuleSceneDO;
 import cn.iocoder.yudao.module.iot.enums.rule.IotRuleSceneActionTypeEnum;
 import cn.iocoder.yudao.module.iot.mq.message.IotDeviceMessage;
 
@@ -23,7 +23,7 @@ public interface IotRuleSceneAction {
      *                2. 非空的情况:设备触发
      * @param config  配置
      */
-    void execute(@Nullable IotDeviceMessage message, IotRuleSceneActionConfig config) throws Exception;
+    void execute(@Nullable IotDeviceMessage message, IotRuleSceneDO.ActionConfig config) throws Exception;
 
     /**
      * 获得类型

+ 2 - 2
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/IotRuleSceneAlertAction.java

@@ -1,6 +1,6 @@
 package cn.iocoder.yudao.module.iot.service.rule.action;
 
-import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.scene.config.IotRuleSceneActionConfig;
+import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotRuleSceneDO;
 import cn.iocoder.yudao.module.iot.enums.rule.IotRuleSceneActionTypeEnum;
 import cn.iocoder.yudao.module.iot.mq.message.IotDeviceMessage;
 import org.springframework.stereotype.Component;
@@ -16,7 +16,7 @@ import javax.annotation.Nullable;
 public class IotRuleSceneAlertAction implements IotRuleSceneAction {
 
     @Override
-    public void execute(@Nullable IotDeviceMessage message, IotRuleSceneActionConfig config) {
+    public void execute(@Nullable IotDeviceMessage message, IotRuleSceneDO.ActionConfig config) {
         // TODO @芋艿:待实现
     }
 

+ 2 - 2
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/IotRuleSceneDataBridgeAction.java

@@ -2,8 +2,8 @@ package cn.iocoder.yudao.module.iot.service.rule.action;
 
 import cn.hutool.core.lang.Assert;
 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
-import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.scene.config.IotRuleSceneActionConfig;
 import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotDataBridgeDO;
+import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotRuleSceneDO;
 import cn.iocoder.yudao.module.iot.enums.rule.IotRuleSceneActionTypeEnum;
 import cn.iocoder.yudao.module.iot.mq.message.IotDeviceMessage;
 import cn.iocoder.yudao.module.iot.service.rule.IotDataBridgeService;
@@ -29,7 +29,7 @@ public class IotRuleSceneDataBridgeAction implements IotRuleSceneAction {
     private List<IotDataBridgeExecute<?>> dataBridgeExecutes;
 
     @Override
-    public void execute(IotDeviceMessage message, IotRuleSceneActionConfig config) throws Exception {
+    public void execute(IotDeviceMessage message, IotRuleSceneDO.ActionConfig config) throws Exception {
         // 1.1 如果消息为空,直接返回
         if (message == null) {
             return;

+ 3 - 4
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/IotRuleSceneDeviceControlAction.java

@@ -2,9 +2,8 @@ package cn.iocoder.yudao.module.iot.service.rule.action;
 
 import cn.hutool.core.lang.Assert;
 import cn.iocoder.yudao.module.iot.controller.admin.device.vo.control.IotDeviceDownstreamReqVO;
-import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.scene.config.IotRuleSceneActionConfig;
-import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.scene.config.IotRuleSceneActionDeviceControl;
 import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO;
+import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotRuleSceneDO;
 import cn.iocoder.yudao.module.iot.enums.rule.IotRuleSceneActionTypeEnum;
 import cn.iocoder.yudao.module.iot.mq.message.IotDeviceMessage;
 import cn.iocoder.yudao.module.iot.service.device.IotDeviceService;
@@ -28,8 +27,8 @@ public class IotRuleSceneDeviceControlAction implements IotRuleSceneAction {
     private IotDeviceService deviceService;
 
     @Override
-    public void execute(IotDeviceMessage message, IotRuleSceneActionConfig config) {
-        IotRuleSceneActionDeviceControl control = config.getDeviceControl();
+    public void execute(IotDeviceMessage message, IotRuleSceneDO.ActionConfig config) {
+        IotRuleSceneDO.ActionDeviceControl control = config.getDeviceControl();
         Assert.notNull(control, "设备控制配置不能为空");
         // 遍历每个设备,下发消息
         control.getDeviceNames().forEach(deviceName -> {

+ 1 - 1
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/AbstractCacheableDataBridgeExecute.java

@@ -101,7 +101,7 @@ public abstract class AbstractCacheableDataBridgeExecute<Config, Producer> imple
     @Override
     @SuppressWarnings({"unchecked"})
     public void execute(IotDeviceMessage message, IotDataBridgeDO dataBridge) {
-        if (ObjUtil.notEqual(message.getType(), getType())) {
+        if (ObjUtil.notEqual(dataBridge.getType(), getType())) {
             return;
         }
         try {

+ 12 - 27
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotRedisStreamMQDataBridgeExecute.java → yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotRedisStreamDataBridgeExecute.java

@@ -1,12 +1,9 @@
 package cn.iocoder.yudao.module.iot.service.rule.action.databridge;
 
-import cn.hutool.core.util.ReflectUtil;
 import cn.hutool.core.util.StrUtil;
-import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.databridge.config.IotDataBridgeRedisStreamMQConfig;
+import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.databridge.config.IotDataBridgeRedisStreamConfig;
 import cn.iocoder.yudao.module.iot.enums.rule.IotDataBridgeTypeEnum;
 import cn.iocoder.yudao.module.iot.mq.message.IotDeviceMessage;
-import com.fasterxml.jackson.databind.ObjectMapper;
-import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
 import lombok.extern.slf4j.Slf4j;
 import org.redisson.Redisson;
 import org.redisson.api.RedissonClient;
@@ -21,14 +18,14 @@ import org.springframework.data.redis.serializer.RedisSerializer;
 import org.springframework.stereotype.Component;
 
 /**
- * Redis Stream MQ 的 {@link IotDataBridgeExecute} 实现类
+ * Redis Stream 的 {@link IotDataBridgeExecute} 实现类
  *
  * @author HUIHUI
  */
 @Component
 @Slf4j
-public class IotRedisStreamMQDataBridgeExecute extends
-        AbstractCacheableDataBridgeExecute<IotDataBridgeRedisStreamMQConfig, RedisTemplate<String, Object>> {
+public class IotRedisStreamDataBridgeExecute extends
+        AbstractCacheableDataBridgeExecute<IotDataBridgeRedisStreamConfig, RedisTemplate<String, Object>> {
 
     @Override
     public Integer getType() {
@@ -36,7 +33,7 @@ public class IotRedisStreamMQDataBridgeExecute extends
     }
 
     @Override
-    public void execute0(IotDeviceMessage message, IotDataBridgeRedisStreamMQConfig config) throws Exception {
+    public void execute0(IotDeviceMessage message, IotDataBridgeRedisStreamConfig config) throws Exception {
         // 1. 获取 RedisTemplate
         RedisTemplate<String, Object> redisTemplate = getProducer(config);
 
@@ -48,7 +45,7 @@ public class IotRedisStreamMQDataBridgeExecute extends
     }
 
     @Override
-    protected RedisTemplate<String, Object> initProducer(IotDataBridgeRedisStreamMQConfig config) {
+    protected RedisTemplate<String, Object> initProducer(IotDataBridgeRedisStreamConfig config) {
         // 1.1 创建 Redisson 配置
         Config redissonConfig = new Config();
         SingleServerConfig serverConfig = redissonConfig.useSingleServer()
@@ -59,20 +56,17 @@ public class IotRedisStreamMQDataBridgeExecute extends
             serverConfig.setPassword(config.getPassword());
         }
 
-        // TODO @huihui:看看能不能简化一些。按道理说,不用这么多的哈。
-        // 2.1 创建 RedissonClient
+        // TODO @芋艿:看看怎么优化
+        // 创建 RedisTemplate 并配置
         RedissonClient redisson = Redisson.create(redissonConfig);
-        // 2.2 创建并配置 RedisTemplate
         RedisTemplate<String, Object> template = new RedisTemplate<>();
-        // 设置 RedisConnection 工厂。😈 它就是实现多种 Java Redis 客户端接入的秘密工厂。感兴趣的胖友,可以自己去撸下。
         template.setConnectionFactory(new RedissonConnectionFactory(redisson));
-        // 使用 String 序列化方式,序列化 KEY 。
+        // 设置序列化器
         template.setKeySerializer(RedisSerializer.string());
         template.setHashKeySerializer(RedisSerializer.string());
-        // 使用 JSON 序列化方式(库是 Jackson ),序列化 VALUE 。
-        template.setValueSerializer(buildRedisSerializer());
-        template.setHashValueSerializer(buildRedisSerializer());
-        template.afterPropertiesSet();// 初始化
+        template.setValueSerializer(RedisSerializer.json());
+        template.setHashValueSerializer(RedisSerializer.json());
+        template.afterPropertiesSet();
         return template;
     }
 
@@ -84,13 +78,4 @@ public class IotRedisStreamMQDataBridgeExecute extends
         }
     }
 
-    // TODO @huihui:看看能不能简化一些。按道理说,不用这么多的哈。
-    public static RedisSerializer<?> buildRedisSerializer() {
-        RedisSerializer<Object> json = RedisSerializer.json();
-        // 解决 LocalDateTime 的序列化
-        ObjectMapper objectMapper = (ObjectMapper) ReflectUtil.getFieldValue(json, "mapper");
-        objectMapper.registerModules(new JavaTimeModule());
-        return json;
-    }
-
 }

+ 0 - 9
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thingmodel/IotThingModelService.java

@@ -4,7 +4,6 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult;
 import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.vo.IotThingModelListReqVO;
 import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.vo.IotThingModelPageReqVO;
 import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.vo.IotThingModelSaveReqVO;
-import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.vo.IotThingModelTSLRespVO;
 import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotThingModelDO;
 import jakarta.validation.Valid;
 
@@ -91,12 +90,4 @@ public interface IotThingModelService {
      */
     Long getThingModelCount(LocalDateTime createTime);
 
-    /**
-     * 通过产品 ID 获取产品物模型 TSL
-     *
-     * @param productId 产品 ID
-     * @return 产品物模型 TSL
-     */
-    IotThingModelTSLRespVO getThingModelTslByProductId(Long productId);
-
 }

+ 0 - 27
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thingmodel/IotThingModelServiceImpl.java

@@ -1,7 +1,6 @@
 package cn.iocoder.yudao.module.iot.service.thingmodel;
 
 import cn.hutool.core.collection.CollUtil;
-import cn.hutool.core.util.ObjUtil;
 import cn.hutool.core.util.ObjectUtil;
 import cn.hutool.core.util.StrUtil;
 import cn.hutool.extra.spring.SpringUtil;
@@ -14,7 +13,6 @@ import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.ThingModelS
 import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.vo.IotThingModelListReqVO;
 import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.vo.IotThingModelPageReqVO;
 import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.vo.IotThingModelSaveReqVO;
-import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.vo.IotThingModelTSLRespVO;
 import cn.iocoder.yudao.module.iot.convert.thingmodel.IotThingModelConvert;
 import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO;
 import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotThingModelDO;
@@ -151,31 +149,6 @@ public class IotThingModelServiceImpl implements IotThingModelService {
         return thingModelMapper.selectList(reqVO);
     }
 
-    // TODO @puhui999:这个转换,放在 controller 貌似也行?
-    @Override
-    public IotThingModelTSLRespVO getThingModelTslByProductId(Long productId) {
-        IotThingModelTSLRespVO tslRespVO = new IotThingModelTSLRespVO();
-        // 1. 获得产品所有物模型定义
-        List<IotThingModelDO> thingModels = thingModelMapper.selectListByProductId(productId);
-        if (CollUtil.isEmpty(thingModels)) {
-            return tslRespVO;
-        }
-
-        // 2.1 设置公共部分参数
-        IotThingModelDO thingModel = thingModels.get(0);
-        tslRespVO.setProductId(thingModel.getProductId()).setProductKey(thingModel.getProductKey());
-        // 2.2 处理属性列表
-        tslRespVO.setProperties(convertList(filterList(thingModels, item ->
-                ObjUtil.equal(IotThingModelTypeEnum.PROPERTY.getType(), item.getType())), IotThingModelDO::getProperty));
-        // 2.3 处理服务列表
-        tslRespVO.setServices(convertList(filterList(thingModels, item ->
-                ObjUtil.equal(IotThingModelTypeEnum.SERVICE.getType(), item.getType())), IotThingModelDO::getService));
-        // 2.4 处理事件列表
-        tslRespVO.setEvents(convertList(filterList(thingModels, item ->
-                ObjUtil.equal(IotThingModelTypeEnum.EVENT.getType(), item.getType())), IotThingModelDO::getEvent));
-        return tslRespVO;
-    }
-
     /**
      * 校验功能是否存在
      *

+ 86 - 75
yudao-module-iot/yudao-module-iot-biz/src/test/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotDataBridgeExecuteTest.java

@@ -22,7 +22,7 @@ import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.Mockito.when;
 
 /**
- * {@link IotDataBridgeExecute} 实现类的测试
+ * {@link IotDataBridgeExecute} 实现类的单元测试
  *
  * @author HUIHUI
  */
@@ -41,114 +41,125 @@ public class IotDataBridgeExecuteTest extends BaseMockitoUnitTest {
     @BeforeEach
     public void setUp() {
         // 创建共享的测试消息
-        message = IotDeviceMessage.builder().requestId("TEST-001").reportTime(LocalDateTime.now()).tenantId(1L)
-                .productKey("testProduct").deviceName("testDevice").deviceKey("testDeviceKey")
-                .type("property").identifier("temperature").data("{\"value\": 60}")
+        message = IotDeviceMessage.builder()
+                .requestId("TEST-001")
+                .reportTime(LocalDateTime.now())
+                .tenantId(1L)
+                .productKey("testProduct")
+                .deviceName("testDevice")
+                .deviceKey("testDeviceKey")
+                .type("property")
+                .identifier("temperature")
+                .data("{\"value\": 60}")
                 .build();
-
-        // 配置 RestTemplate mock 返回成功响应
-        // TODO @puhui999:这个应该放到 testHttpDataBridge 里
-        when(restTemplate.exchange(anyString(), any(HttpMethod.class), any(), any(Class.class)))
-                .thenReturn(new ResponseEntity<>("Success", HttpStatus.OK));
     }
 
     @Test
-    public void testKafkaMQDataBridge() {
+    public void testKafkaMQDataBridge() throws Exception {
         // 1. 创建执行器实例
         IotKafkaMQDataBridgeExecute action = new IotKafkaMQDataBridgeExecute();
 
         // 2. 创建配置
-        // TODO @puhui999:可以改成链式哈。
-        IotDataBridgeKafkaMQConfig config = new IotDataBridgeKafkaMQConfig();
-        config.setBootstrapServers("127.0.0.1:9092");
-        config.setTopic("test-topic");
-        config.setSsl(false);
-        config.setUsername(null);
-        config.setPassword(null);
-
-        // 3. 执行两次测试,验证缓存
-        log.info("[testKafkaMQDataBridge][第一次执行,应该会创建新的 producer]");
-        action.execute(message, new IotDataBridgeDO().setType(action.getType()).setConfig(config));
-
-        log.info("[testKafkaMQDataBridge][第二次执行,应该会复用缓存的 producer]");
-        action.execute(message, new IotDataBridgeDO().setType(action.getType()).setConfig(config));
+        IotDataBridgeKafkaMQConfig config = new IotDataBridgeKafkaMQConfig()
+                .setBootstrapServers("127.0.0.1:9092")
+                .setTopic("test-topic")
+                .setSsl(false)
+                .setUsername(null)
+                .setPassword(null);
+
+        // 3. 执行测试并验证缓存
+        executeAndVerifyCache(action, config, "KafkaMQ");
     }
 
     @Test
-    public void testRabbitMQDataBridge() {
+    public void testRabbitMQDataBridge() throws Exception {
         // 1. 创建执行器实例
         IotRabbitMQDataBridgeExecute action = new IotRabbitMQDataBridgeExecute();
 
         // 2. 创建配置
-        IotDataBridgeRabbitMQConfig config = new IotDataBridgeRabbitMQConfig();
-        config.setHost("localhost");
-        config.setPort(5672);
-        config.setVirtualHost("/");
-        config.setUsername("admin");
-        config.setPassword("123456");
-        config.setExchange("test-exchange");
-        config.setRoutingKey("test-key");
-        config.setQueue("test-queue");
-
-        // 3. 执行两次测试,验证缓存
-        log.info("[testRabbitMQDataBridge][第一次执行,应该会创建新的 producer]");
-        action.execute(message, new IotDataBridgeDO().setType(action.getType()).setConfig(config));
-
-        log.info("[testRabbitMQDataBridge][第二次执行,应该会复用缓存的 producer]");
-        action.execute(message, new IotDataBridgeDO().setType(action.getType()).setConfig(config));
+        IotDataBridgeRabbitMQConfig config = new IotDataBridgeRabbitMQConfig()
+                .setHost("localhost")
+                .setPort(5672)
+                .setVirtualHost("/")
+                .setUsername("admin")
+                .setPassword("123456")
+                .setExchange("test-exchange")
+                .setRoutingKey("test-key")
+                .setQueue("test-queue");
+
+        // 3. 执行测试并验证缓存
+        executeAndVerifyCache(action, config, "RabbitMQ");
     }
 
     @Test
-    public void testRedisStreamMQDataBridge() {
+    public void testRedisStreamDataBridge() throws Exception {
         // 1. 创建执行器实例
-        IotRedisStreamMQDataBridgeExecute action = new IotRedisStreamMQDataBridgeExecute();
+        IotRedisStreamDataBridgeExecute action = new IotRedisStreamDataBridgeExecute();
 
         // 2. 创建配置
-        IotDataBridgeRedisStreamMQConfig config = new IotDataBridgeRedisStreamMQConfig();
-        config.setHost("127.0.0.1");
-        config.setPort(6379);
-        config.setDatabase(0);
-        config.setPassword("123456");
-        config.setTopic("test-stream");
-
-        // 3. 执行两次测试,验证缓存
-        log.info("[testRedisStreamMQDataBridge][第一次执行,应该会创建新的 producer]");
-        action.execute(message, new IotDataBridgeDO().setType(action.getType()).setConfig(config));
-
-        log.info("[testRedisStreamMQDataBridge][第二次执行,应该会复用缓存的 producer]");
-        action.execute(message, new IotDataBridgeDO().setType(action.getType()).setConfig(config));
+        IotDataBridgeRedisStreamConfig config = new IotDataBridgeRedisStreamConfig()
+                .setHost("127.0.0.1")
+                .setPort(6379)
+                .setDatabase(0)
+                .setPassword("123456")
+                .setTopic("test-stream");
+
+        // 3. 执行测试并验证缓存
+        executeAndVerifyCache(action, config, "RedisStream");
     }
 
     @Test
-    public void testRocketMQDataBridge() {
+    public void testRocketMQDataBridge() throws Exception {
         // 1. 创建执行器实例
         IotRocketMQDataBridgeExecute action = new IotRocketMQDataBridgeExecute();
 
         // 2. 创建配置
-        IotDataBridgeRocketMQConfig config = new IotDataBridgeRocketMQConfig();
-        config.setNameServer("127.0.0.1:9876");
-        config.setGroup("test-group");
-        config.setTopic("test-topic");
-        config.setTags("test-tag");
-
-        // 3. 执行两次测试,验证缓存
-        log.info("[testRocketMQDataBridge][第一次执行,应该会创建新的 producer]");
-        action.execute(message, new IotDataBridgeDO().setType(action.getType()).setConfig(config));
-
-        log.info("[testRocketMQDataBridge][第二次执行,应该会复用缓存的 producer]");
-        action.execute(message, new IotDataBridgeDO().setType(action.getType()).setConfig(config));
+        IotDataBridgeRocketMQConfig config = new IotDataBridgeRocketMQConfig()
+                .setNameServer("127.0.0.1:9876")
+                .setGroup("test-group")
+                .setTopic("test-topic")
+                .setTags("test-tag");
+
+        // 3. 执行测试并验证缓存
+        executeAndVerifyCache(action, config, "RocketMQ");
     }
 
     @Test
     public void testHttpDataBridge() throws Exception {
-        // 创建配置
-        IotDataBridgeHttpConfig config = new IotDataBridgeHttpConfig();
-        config.setUrl("https://doc.iocoder.cn/");
-        config.setMethod(HttpMethod.GET.name());
+        // 1. 配置 RestTemplate mock 返回成功响应
+        when(restTemplate.exchange(anyString(), any(HttpMethod.class), any(), any(Class.class)))
+                .thenReturn(new ResponseEntity<>("Success", HttpStatus.OK));
 
-        // 执行测试
+        // 2. 创建配置
+        IotDataBridgeHttpConfig config = new IotDataBridgeHttpConfig()
+                .setUrl("https://doc.iocoder.cn/")
+                .setMethod(HttpMethod.GET.name());
+
+        // 3. 执行测试
         log.info("[testHttpDataBridge][执行HTTP数据桥接测试]");
-        httpDataBridgeExecute.execute(message, new IotDataBridgeDO().setType(httpDataBridgeExecute.getType()).setConfig(config));
+        httpDataBridgeExecute.execute(message, new IotDataBridgeDO()
+                .setType(httpDataBridgeExecute.getType())
+                .setConfig(config));
+    }
+
+    /**
+     * 执行测试并验证缓存的通用方法
+     *
+     * @param action 执行器实例
+     * @param config 配置对象
+     * @param mqType MQ类型
+     * @throws Exception 如果执行过程中发生异常
+     */
+    private void executeAndVerifyCache(IotDataBridgeExecute<?> action, IotDataBridgeAbstractConfig config, String mqType) throws Exception {
+        log.info("[test{}DataBridge][第一次执行,应该会创建新的 producer]", mqType);
+        action.execute(message, new IotDataBridgeDO()
+                .setType(action.getType())
+                .setConfig(config));
+
+        log.info("[test{}DataBridge][第二次执行,应该会复用缓存的 producer]", mqType);
+        action.execute(message, new IotDataBridgeDO()
+                .setType(action.getType())
+                .setConfig(config));
     }
 
 }