Commit 27e08266e085064dbf07bda0cee6b0aff3eddb50

Authored by 芋道源码
Committed by Gitee
2 parents 569d6514 cdf316e7

!1269 【代码优化】IoT: 优化数据桥梁

Merge pull request !1269 from puhui999/iot
Showing 30 changed files with 858 additions and 470 deletions
yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/DictTypeConstants.java
... ... @@ -15,5 +15,7 @@ public class DictTypeConstants {
15 15 public static final String VALIDATE_TYPE = "iot_validate_type";
16 16  
17 17 public static final String DEVICE_STATE = "iot_device_state";
  18 + public static final String IOT_DATA_BRIDGE_DIRECTION_ENUM = "iot_data_bridge_direction_enum";
  19 + public static final String IOT_DATA_BRIDGE_TYPE_ENUM = "iot_data_bridge_type_enum";
18 20  
19 21 }
... ...
yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/ErrorCodeConstants.java
... ... @@ -70,4 +70,7 @@ public interface ErrorCodeConstants {
70 70 // ========== MQTT 通信相关 1-050-009-000 ==========
71 71 ErrorCode MQTT_TOPIC_ILLEGAL = new ErrorCode(1_050_009_000, "topic illegal");
72 72  
  73 + // ========== IoT 数据桥梁 1-050-010-000 ==========
  74 + ErrorCode DATA_BRIDGE_NOT_EXISTS = new ErrorCode(1_050_010_000, "IoT 数据桥梁不存在");
  75 +
73 76 }
74 77 \ No newline at end of file
... ...
yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/rule/IotDataBridgDirectionEnum.java renamed to yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/rule/IotDataBridgeDirectionEnum.java
... ... @@ -13,14 +13,14 @@ import java.util.Arrays;
13 13 */
14 14 @RequiredArgsConstructor
15 15 @Getter
16   -public enum IotDataBridgDirectionEnum implements ArrayValuable<Integer> {
  16 +public enum IotDataBridgeDirectionEnum implements ArrayValuable<Integer> {
17 17  
18 18 INPUT(1), // 输入
19 19 OUTPUT(2); // 输出
20 20  
21 21 private final Integer type;
22 22  
23   - public static final Integer[] ARRAYS = Arrays.stream(values()).map(IotDataBridgDirectionEnum::getType).toArray(Integer[]::new);
  23 + public static final Integer[] ARRAYS = Arrays.stream(values()).map(IotDataBridgeDirectionEnum::getType).toArray(Integer[]::new);
24 24  
25 25 @Override
26 26 public Integer[] array() {
... ...
yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/rule/IotDataBridgTypeEnum.java renamed to yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/rule/IotDataBridgeTypeEnum.java
... ... @@ -13,25 +13,26 @@ import java.util.Arrays;
13 13 */
14 14 @RequiredArgsConstructor
15 15 @Getter
16   -public enum IotDataBridgTypeEnum implements ArrayValuable<Integer> {
  16 +public enum IotDataBridgeTypeEnum implements ArrayValuable<Integer> {
17 17  
18   - HTTP(1),
19   - TCP(2),
20   - WEBSOCKET(3),
  18 + HTTP(1, "HTTP"),
  19 + TCP(2, "TCP"),
  20 + WEBSOCKET(3, "WEBSOCKET"),
21 21  
22   - MQTT(10),
  22 + MQTT(10, "MQTT"),
23 23  
24   - DATABASE(20),
25   - REDIS_STREAM(21),
  24 + DATABASE(20, "DATABASE"),
  25 + REDIS_STREAM(21, "REDIS_STREAM"),
26 26  
27   - ROCKETMQ(30),
28   - RABBITMQ(31),
29   - KAFKA(32)
30   - ;
  27 + ROCKETMQ(30, "ROCKETMQ"),
  28 + RABBITMQ(31, "RABBITMQ"),
  29 + KAFKA(32, "KAFKA");
31 30  
32 31 private final Integer type;
33 32  
34   - public static final Integer[] ARRAYS = Arrays.stream(values()).map(IotDataBridgTypeEnum::getType).toArray(Integer[]::new);
  33 + private final String name;
  34 +
  35 + public static final Integer[] ARRAYS = Arrays.stream(values()).map(IotDataBridgeTypeEnum::getType).toArray(Integer[]::new);
35 36  
36 37 @Override
37 38 public Integer[] array() {
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/IotDataBridgeController.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.controller.admin.rule;
  2 +
  3 +import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog;
  4 +import cn.iocoder.yudao.framework.common.pojo.CommonResult;
  5 +import cn.iocoder.yudao.framework.common.pojo.PageParam;
  6 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  7 +import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
  8 +import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
  9 +import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.databridge.IotDataBridgePageReqVO;
  10 +import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.databridge.IotDataBridgeRespVO;
  11 +import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.databridge.IotDataBridgeSaveReqVO;
  12 +import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotDataBridgeDO;
  13 +import cn.iocoder.yudao.module.iot.service.rule.IotDataBridgeService;
  14 +import io.swagger.v3.oas.annotations.Operation;
  15 +import io.swagger.v3.oas.annotations.Parameter;
  16 +import io.swagger.v3.oas.annotations.tags.Tag;
  17 +import jakarta.annotation.Resource;
  18 +import jakarta.servlet.http.HttpServletResponse;
  19 +import jakarta.validation.Valid;
  20 +import org.springframework.security.access.prepost.PreAuthorize;
  21 +import org.springframework.validation.annotation.Validated;
  22 +import org.springframework.web.bind.annotation.*;
  23 +
  24 +import java.io.IOException;
  25 +import java.util.List;
  26 +
  27 +import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT;
  28 +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
  29 +
  30 +@Tag(name = "管理后台 - IoT 数据桥梁")
  31 +@RestController
  32 +@RequestMapping("/iot/data-bridge")
  33 +@Validated
  34 +public class IotDataBridgeController {
  35 +
  36 + @Resource
  37 + private IotDataBridgeService dataBridgeService;
  38 +
  39 + @PostMapping("/create")
  40 + @Operation(summary = "创建IoT 数据桥梁")
  41 + @PreAuthorize("@ss.hasPermission('iot:data-bridge:create')")
  42 + public CommonResult<Long> createDataBridge(@Valid @RequestBody IotDataBridgeSaveReqVO createReqVO) {
  43 + return success(dataBridgeService.createDataBridge(createReqVO));
  44 + }
  45 +
  46 + @PutMapping("/update")
  47 + @Operation(summary = "更新IoT 数据桥梁")
  48 + @PreAuthorize("@ss.hasPermission('iot:data-bridge:update')")
  49 + public CommonResult<Boolean> updateDataBridge(@Valid @RequestBody IotDataBridgeSaveReqVO updateReqVO) {
  50 + dataBridgeService.updateDataBridge(updateReqVO);
  51 + return success(true);
  52 + }
  53 +
  54 + @DeleteMapping("/delete")
  55 + @Operation(summary = "删除IoT 数据桥梁")
  56 + @Parameter(name = "id", description = "编号", required = true)
  57 + @PreAuthorize("@ss.hasPermission('iot:data-bridge:delete')")
  58 + public CommonResult<Boolean> deleteDataBridge(@RequestParam("id") Long id) {
  59 + dataBridgeService.deleteDataBridge(id);
  60 + return success(true);
  61 + }
  62 +
  63 + @GetMapping("/get")
  64 + @Operation(summary = "获得IoT 数据桥梁")
  65 + @Parameter(name = "id", description = "编号", required = true, example = "1024")
  66 + @PreAuthorize("@ss.hasPermission('iot:data-bridge:query')")
  67 + public CommonResult<IotDataBridgeRespVO> getDataBridge(@RequestParam("id") Long id) {
  68 + IotDataBridgeDO dataBridge = dataBridgeService.getDataBridge(id);
  69 + return success(BeanUtils.toBean(dataBridge, IotDataBridgeRespVO.class));
  70 + }
  71 +
  72 + @GetMapping("/page")
  73 + @Operation(summary = "获得IoT 数据桥梁分页")
  74 + @PreAuthorize("@ss.hasPermission('iot:data-bridge:query')")
  75 + public CommonResult<PageResult<IotDataBridgeRespVO>> getDataBridgePage(@Valid IotDataBridgePageReqVO pageReqVO) {
  76 + PageResult<IotDataBridgeDO> pageResult = dataBridgeService.getDataBridgePage(pageReqVO);
  77 + return success(BeanUtils.toBean(pageResult, IotDataBridgeRespVO.class));
  78 + }
  79 +
  80 + @GetMapping("/export-excel")
  81 + @Operation(summary = "导出IoT 数据桥梁 Excel")
  82 + @PreAuthorize("@ss.hasPermission('iot:data-bridge:export')")
  83 + @ApiAccessLog(operateType = EXPORT)
  84 + public void exportDataBridgeExcel(@Valid IotDataBridgePageReqVO pageReqVO,
  85 + HttpServletResponse response) throws IOException {
  86 + pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
  87 + List<IotDataBridgeDO> list = dataBridgeService.getDataBridgePage(pageReqVO).getList();
  88 + // 导出 Excel
  89 + ExcelUtils.write(response, "IoT 数据桥梁.xls", "数据", IotDataBridgeRespVO.class,
  90 + BeanUtils.toBean(list, IotDataBridgeRespVO.class));
  91 + }
  92 +
  93 +}
0 94 \ No newline at end of file
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/IotDataBridgePageReqVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.controller.admin.rule.vo.databridge;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.PageParam;
  4 +import io.swagger.v3.oas.annotations.media.Schema;
  5 +import lombok.Data;
  6 +import lombok.EqualsAndHashCode;
  7 +import lombok.ToString;
  8 +import org.springframework.format.annotation.DateTimeFormat;
  9 +
  10 +import java.time.LocalDateTime;
  11 +
  12 +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
  13 +
  14 +@Schema(description = "管理后台 - IoT 数据桥梁分页 Request VO")
  15 +@Data
  16 +@EqualsAndHashCode(callSuper = true)
  17 +@ToString(callSuper = true)
  18 +public class IotDataBridgePageReqVO extends PageParam {
  19 +
  20 + @Schema(description = "桥梁名称", example = "赵六")
  21 + private String name;
  22 +
  23 + @Schema(description = "桥梁描述", example = "随便")
  24 + private String description;
  25 +
  26 + @Schema(description = "桥梁状态", example = "2")
  27 + private Integer status;
  28 +
  29 + @Schema(description = "桥梁方向")
  30 + private Integer direction;
  31 +
  32 + @Schema(description = "桥梁类型", example = "1")
  33 + private Integer type;
  34 +
  35 + @Schema(description = "创建时间")
  36 + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
  37 + private LocalDateTime[] createTime;
  38 +
  39 +}
0 40 \ No newline at end of file
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/IotDataBridgeRespVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.controller.admin.rule.vo.databridge;
  2 +
  3 +import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat;
  4 +import cn.iocoder.yudao.framework.excel.core.convert.DictConvert;
  5 +import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.databridge.config.IotDataBridgeConfig;
  6 +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
  7 +import com.alibaba.excel.annotation.ExcelProperty;
  8 +import io.swagger.v3.oas.annotations.media.Schema;
  9 +import lombok.Data;
  10 +
  11 +import java.time.LocalDateTime;
  12 +
  13 +import static cn.iocoder.yudao.module.iot.enums.DictTypeConstants.IOT_DATA_BRIDGE_DIRECTION_ENUM;
  14 +import static cn.iocoder.yudao.module.iot.enums.DictTypeConstants.IOT_DATA_BRIDGE_TYPE_ENUM;
  15 +import static cn.iocoder.yudao.module.system.enums.DictTypeConstants.COMMON_STATUS;
  16 +
  17 +@Schema(description = "管理后台 - IoT 数据桥梁 Response VO")
  18 +@Data
  19 +@ExcelIgnoreUnannotated
  20 +public class IotDataBridgeRespVO {
  21 +
  22 + @Schema(description = "桥梁编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "18564")
  23 + @ExcelProperty("桥梁编号")
  24 + private Long id;
  25 +
  26 + @Schema(description = "桥梁名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "赵六")
  27 + @ExcelProperty("桥梁名称")
  28 + private String name;
  29 +
  30 + @Schema(description = "桥梁描述", example = "随便")
  31 + @ExcelProperty("桥梁描述")
  32 + private String description;
  33 +
  34 + @Schema(description = "桥梁状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
  35 + @ExcelProperty(value = "桥梁状态", converter = DictConvert.class)
  36 + @DictFormat(COMMON_STATUS)
  37 + private Integer status;
  38 +
  39 + @Schema(description = "桥梁方向", requiredMode = Schema.RequiredMode.REQUIRED)
  40 + @ExcelProperty(value = "桥梁方向", converter = DictConvert.class)
  41 + @DictFormat(IOT_DATA_BRIDGE_DIRECTION_ENUM)
  42 + private Integer direction;
  43 +
  44 + @Schema(description = "桥梁类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
  45 + @ExcelProperty(value = "桥梁类型", converter = DictConvert.class)
  46 + @DictFormat(IOT_DATA_BRIDGE_TYPE_ENUM)
  47 + private Integer type;
  48 +
  49 + @Schema(description = "桥梁配置")
  50 + @ExcelProperty("桥梁配置")
  51 + private IotDataBridgeConfig config;
  52 +
  53 + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
  54 + @ExcelProperty("创建时间")
  55 + private LocalDateTime createTime;
  56 +
  57 +}
0 58 \ No newline at end of file
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/IotDataBridgeSaveReqVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.controller.admin.rule.vo.databridge;
  2 +
  3 +import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.databridge.config.IotDataBridgeConfig;
  4 +import io.swagger.v3.oas.annotations.media.Schema;
  5 +import jakarta.validation.constraints.NotEmpty;
  6 +import jakarta.validation.constraints.NotNull;
  7 +import lombok.Data;
  8 +
  9 +@Schema(description = "管理后台 - IoT 数据桥梁新增/修改 Request VO")
  10 +@Data
  11 +public class IotDataBridgeSaveReqVO {
  12 +
  13 + @Schema(description = "桥梁编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "18564")
  14 + private Long id;
  15 +
  16 + @Schema(description = "桥梁名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "赵六")
  17 + @NotEmpty(message = "桥梁名称不能为空")
  18 + private String name;
  19 +
  20 + @Schema(description = "桥梁描述", example = "随便")
  21 + private String description;
  22 +
  23 + @Schema(description = "桥梁状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
  24 + @NotNull(message = "桥梁状态不能为空")
  25 + private Integer status;
  26 +
  27 + @Schema(description = "桥梁方向", requiredMode = Schema.RequiredMode.REQUIRED)
  28 + @NotNull(message = "桥梁方向不能为空")
  29 + private Integer direction;
  30 +
  31 + @Schema(description = "桥梁类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
  32 + @NotNull(message = "桥梁类型不能为空")
  33 + private Integer type;
  34 +
  35 + @Schema(description = "桥梁配置")
  36 + @NotNull(message = "桥梁配置不能为空")
  37 + private IotDataBridgeConfig config;
  38 +
  39 +}
0 40 \ No newline at end of file
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/config/IotDataBridgeConfig.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.controller.admin.rule.vo.databridge.config;
  2 +
  3 +import com.fasterxml.jackson.annotation.JsonSubTypes;
  4 +import com.fasterxml.jackson.annotation.JsonTypeInfo;
  5 +import lombok.Data;
  6 +
  7 +/**
  8 + * 抽象类 IotDataBridgeConfig
  9 + *
  10 + * 用于表示数据桥梁配置数据的通用类型,根据具体的 "type" 字段动态映射到对应的子类。
  11 + * 提供多态支持,适用于不同类型的数据结构序列化和反序列化场景。
  12 + *
  13 + * @author HUIHUI
  14 + */
  15 +@Data
  16 +@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type", visible = true)
  17 +@JsonSubTypes({
  18 + @JsonSubTypes.Type(value = IotDataBridgeHttpConfig.class, name = "HTTP"),
  19 + @JsonSubTypes.Type(value = IotDataBridgeKafkaMQConfig.class, name = "KAFKA"),
  20 + @JsonSubTypes.Type(value = IotDataBridgeMqttConfig.class, name = "MQTT"),
  21 + @JsonSubTypes.Type(value = IotDataBridgeRabbitMQConfig.class, name = "RABBITMQ"),
  22 + @JsonSubTypes.Type(value = IotDataBridgeRedisStreamMQConfig.class, name = "REDIS_STREAM"),
  23 + @JsonSubTypes.Type(value = IotDataBridgeRocketMQConfig.class, name = "ROCKETMQ"),
  24 +})
  25 +public abstract class IotDataBridgeConfig {
  26 +
  27 + /**
  28 + * 配置类型
  29 + */
  30 + private String type;
  31 +
  32 +}
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/config/IotDataBridgeHttpConfig.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.controller.admin.rule.vo.databridge.config;
  2 +
  3 +import lombok.Data;
  4 +
  5 +import java.util.Map;
  6 +
  7 +/**
  8 + * HTTP 配置
  9 + *
  10 + * @author HUIHUI
  11 + */
  12 +@Data
  13 +public class IotDataBridgeHttpConfig extends IotDataBridgeConfig {
  14 +
  15 + /**
  16 + * 请求 URL
  17 + */
  18 + private String url;
  19 + /**
  20 + * 请求方法
  21 + */
  22 + private String method;
  23 + /**
  24 + * 请求头
  25 + */
  26 + private Map<String, String> headers;
  27 + /**
  28 + * 请求参数
  29 + */
  30 + private Map<String, String> query;
  31 + /**
  32 + * 请求体
  33 + */
  34 + private String body;
  35 +
  36 +}
0 37 \ No newline at end of file
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/config/IotDataBridgeKafkaMQConfig.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.controller.admin.rule.vo.databridge.config;
  2 +
  3 +import lombok.Data;
  4 +
  5 +/**
  6 + * Kafka 配置
  7 + *
  8 + * @author HUIHUI
  9 + */
  10 +@Data
  11 +public class IotDataBridgeKafkaMQConfig extends IotDataBridgeConfig {
  12 +
  13 + /**
  14 + * Kafka 服务器地址
  15 + */
  16 + private String bootstrapServers;
  17 + /**
  18 + * 用户名
  19 + */
  20 + private String username;
  21 + /**
  22 + * 密码
  23 + */
  24 + private String password;
  25 + /**
  26 + * 是否启用 SSL
  27 + */
  28 + private Boolean ssl;
  29 +
  30 + /**
  31 + * 主题
  32 + */
  33 + private String topic;
  34 +
  35 +}
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/config/IotDataBridgeMqttConfig.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.controller.admin.rule.vo.databridge.config;
  2 +
  3 +import lombok.Data;
  4 +
  5 +/**
  6 + * MQTT 配置
  7 + *
  8 + * @author HUIHUI
  9 + */
  10 +@Data
  11 +public class IotDataBridgeMqttConfig extends IotDataBridgeConfig {
  12 +
  13 + /**
  14 + * MQTT 服务器地址
  15 + */
  16 + private String url;
  17 + /**
  18 + * 用户名
  19 + */
  20 + private String username;
  21 + /**
  22 + * 密码
  23 + */
  24 + private String password;
  25 + /**
  26 + * 客户端编号
  27 + */
  28 + private String clientId;
  29 + /**
  30 + * 主题
  31 + */
  32 + private String topic;
  33 +
  34 +}
0 35 \ No newline at end of file
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/config/IotDataBridgeRabbitMQConfig.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.controller.admin.rule.vo.databridge.config;
  2 +
  3 +import lombok.Data;
  4 +
  5 +/**
  6 + * RabbitMQ 配置
  7 + *
  8 + * @author HUIHUI
  9 + */
  10 +@Data
  11 +public class IotDataBridgeRabbitMQConfig extends IotDataBridgeConfig {
  12 +
  13 + /**
  14 + * RabbitMQ 服务器地址
  15 + */
  16 + private String host;
  17 + /**
  18 + * 端口
  19 + */
  20 + private Integer port;
  21 + /**
  22 + * 虚拟主机
  23 + */
  24 + private String virtualHost;
  25 + /**
  26 + * 用户名
  27 + */
  28 + private String username;
  29 + /**
  30 + * 密码
  31 + */
  32 + private String password;
  33 +
  34 + /**
  35 + * 交换机名称
  36 + */
  37 + private String exchange;
  38 + /**
  39 + * 路由键
  40 + */
  41 + private String routingKey;
  42 + /**
  43 + * 队列名称
  44 + */
  45 + private String queue;
  46 +}
0 47 \ No newline at end of file
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/config/IotDataBridgeRedisStreamMQConfig.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.controller.admin.rule.vo.databridge.config;
  2 +
  3 +import lombok.Data;
  4 +
  5 +/**
  6 + * Redis Stream MQ 配置
  7 + *
  8 + * @author HUIHUI
  9 + */
  10 +@Data
  11 +public class IotDataBridgeRedisStreamMQConfig extends IotDataBridgeConfig {
  12 +
  13 + /**
  14 + * Redis 服务器地址
  15 + */
  16 + private String host;
  17 + /**
  18 + * 端口
  19 + */
  20 + private Integer port;
  21 + /**
  22 + * 密码
  23 + */
  24 + private String password;
  25 + /**
  26 + * 数据库索引
  27 + */
  28 + private Integer database;
  29 +
  30 + /**
  31 + * 主题
  32 + */
  33 + private String topic;
  34 +}
0 35 \ No newline at end of file
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/config/IotDataBridgeRocketMQConfig.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.controller.admin.rule.vo.databridge.config;
  2 +
  3 +import lombok.Data;
  4 +
  5 +/**
  6 + * RocketMQ 配置
  7 + *
  8 + * @author HUIHUI
  9 + */
  10 +@Data
  11 +public class IotDataBridgeRocketMQConfig extends IotDataBridgeConfig {
  12 +
  13 + /**
  14 + * RocketMQ 名称服务器地址
  15 + */
  16 + private String nameServer;
  17 + /**
  18 + * 访问密钥
  19 + */
  20 + private String accessKey;
  21 + /**
  22 + * 秘密钥匙
  23 + */
  24 + private String secretKey;
  25 +
  26 + /**
  27 + * 生产者组
  28 + */
  29 + private String group;
  30 + /**
  31 + * 主题
  32 + */
  33 + private String topic;
  34 + /**
  35 + * 标签
  36 + */
  37 + private String tags;
  38 +
  39 +}
0 40 \ No newline at end of file
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/package-info.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.controller.admin.rule.vo;
0 2 \ No newline at end of file
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/IotDataBridgeDO.java
1 1 package cn.iocoder.yudao.module.iot.dal.dataobject.rule;
2 2  
3 3 import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
  4 +import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.databridge.config.IotDataBridgeConfig;
  5 +import cn.iocoder.yudao.module.iot.enums.rule.IotDataBridgeDirectionEnum;
  6 +import cn.iocoder.yudao.module.iot.enums.rule.IotDataBridgeTypeEnum;
4 7 import com.baomidou.mybatisplus.annotation.KeySequence;
5 8 import com.baomidou.mybatisplus.annotation.TableField;
6 9 import com.baomidou.mybatisplus.annotation.TableId;
7 10 import com.baomidou.mybatisplus.annotation.TableName;
8 11 import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
9   -import com.fasterxml.jackson.annotation.JsonTypeInfo;
10 12 import lombok.*;
11 13  
12   -import java.util.Map;
13   -
14 14 /**
15 15 * IoT 数据桥梁 DO
16 16 *
17 17 * @author 芋道源码
18 18 */
19   -@TableName("iot_data_bridge")
  19 +@TableName(value = "iot_data_bridge", autoResultMap = true)
20 20 @KeySequence("iot_data_bridge_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
21 21 @Data
22 22 @EqualsAndHashCode(callSuper = true)
... ... @@ -48,14 +48,14 @@ public class IotDataBridgeDO extends BaseDO {
48 48 /**
49 49 * 桥梁方向
50 50 *
51   - * 枚举 {@link cn.iocoder.yudao.module.iot.enums.rule.IotDataBridgDirectionEnum}
  51 + * 枚举 {@link IotDataBridgeDirectionEnum}
52 52 */
53 53 private Integer direction;
54 54  
55 55 /**
56 56 * 桥梁类型
57 57 *
58   - * 枚举 {@link cn.iocoder.yudao.module.iot.enums.rule.IotDataBridgTypeEnum}
  58 + * 枚举 {@link IotDataBridgeTypeEnum}
59 59 */
60 60 private Integer type;
61 61  
... ... @@ -63,211 +63,6 @@ public class IotDataBridgeDO extends BaseDO {
63 63 * 桥梁配置
64 64 */
65 65 @TableField(typeHandler = JacksonTypeHandler.class)
66   - private Config config;
67   -
68   - /**
69   - * 文件客户端的配置
70   - * 不同实现的客户端,需要不同的配置,通过子类来定义
71   - *
72   - * @author 芋道源码
73   - */
74   - @JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)
75   - // @JsonTypeInfo 注解的作用,Jackson 多态
76   - // 1. 序列化到时数据库时,增加 @class 属性。
77   - // 2. 反序列化到内存对象时,通过 @class 属性,可以创建出正确的类型
78   - public interface Config {
79   - }
80   -
81   - /**
82   - * HTTP 配置
83   - */
84   - @Data
85   - public static class HttpConfig implements Config {
86   -
87   - /**
88   - * 请求 URL
89   - */
90   - private String url;
91   - /**
92   - * 请求方法
93   - */
94   - private String method;
95   - /**
96   - * 请求头
97   - */
98   - private Map<String, String> headers;
99   - /**
100   - * 请求参数
101   - */
102   - private Map<String, String> query;
103   - /**
104   - * 请求体
105   - */
106   - private String body;
107   -
108   - }
109   -
110   - /**
111   - * MQTT 配置
112   - */
113   - @Data
114   - public static class MqttConfig implements Config {
115   -
116   - /**
117   - * MQTT 服务器地址
118   - */
119   - private String url;
120   - /**
121   - * 用户名
122   - */
123   - private String username;
124   - /**
125   - * 密码
126   - */
127   - private String password;
128   - /**
129   - * 客户端编号
130   - */
131   - private String clientId;
132   - /**
133   - * 主题
134   - */
135   - private String topic;
136   -
137   - }
138   -
139   - /**
140   - * RocketMQ 配置
141   - */
142   - @Data
143   - public static class RocketMQConfig implements Config {
144   -
145   - /**
146   - * RocketMQ 名称服务器地址
147   - */
148   - private String nameServer;
149   - /**
150   - * 访问密钥
151   - */
152   - private String accessKey;
153   - /**
154   - * 秘密钥匙
155   - */
156   - private String secretKey;
157   -
158   - /**
159   - * 生产者组
160   - */
161   - private String group;
162   - /**
163   - * 主题
164   - */
165   - private String topic;
166   - /**
167   - * 标签
168   - */
169   - private String tags;
170   -
171   - }
172   -
173   - /**
174   - * Kafka 配置
175   - */
176   - @Data
177   - public static class KafkaMQConfig implements Config {
178   -
179   - /**
180   - * Kafka 服务器地址
181   - */
182   - private String bootstrapServers;
183   - /**
184   - * 用户名
185   - */
186   - private String username;
187   - /**
188   - * 密码
189   - */
190   - private String password;
191   - /**
192   - * 是否启用 SSL
193   - */
194   - private Boolean ssl;
195   -
196   - /**
197   - * 主题
198   - */
199   - private String topic;
200   -
201   - }
202   -
203   - /**
204   - * RabbitMQ 配置
205   - */
206   - @Data
207   - public static class RabbitMQConfig implements Config {
208   -
209   - /**
210   - * RabbitMQ 服务器地址
211   - */
212   - private String host;
213   - /**
214   - * 端口
215   - */
216   - private Integer port;
217   - /**
218   - * 虚拟主机
219   - */
220   - private String virtualHost;
221   - /**
222   - * 用户名
223   - */
224   - private String username;
225   - /**
226   - * 密码
227   - */
228   - private String password;
229   -
230   - /**
231   - * 交换机名称
232   - */
233   - private String exchange;
234   - /**
235   - * 路由键
236   - */
237   - private String routingKey;
238   - /**
239   - * 队列名称
240   - */
241   - private String queue;
242   - }
243   -
244   - /**
245   - * Redis Stream MQ 配置
246   - */
247   - @Data
248   - public static class RedisStreamMQConfig implements Config {
249   -
250   - /**
251   - * Redis 服务器地址
252   - */
253   - private String host;
254   - /**
255   - * 端口
256   - */
257   - private Integer port;
258   - /**
259   - * 密码
260   - */
261   - private String password;
262   - /**
263   - * 数据库索引
264   - */
265   - private Integer database;
266   -
267   - /**
268   - * 主题
269   - */
270   - private String topic;
271   - }
  66 + private IotDataBridgeConfig config;
272 67  
273 68 }
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/rule/IotDataBridgeMapper.java
1 1 package cn.iocoder.yudao.module.iot.dal.mysql.rule;
2 2  
  3 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
3 4 import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
  5 +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
  6 +import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.databridge.IotDataBridgePageReqVO;
4 7 import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotDataBridgeDO;
5 8 import org.apache.ibatis.annotations.Mapper;
6 9  
  10 +/**
  11 + * IoT 数据桥梁 Mapper
  12 + *
  13 + * @author HUIHUI
  14 + */
7 15 @Mapper
8 16 public interface IotDataBridgeMapper extends BaseMapperX<IotDataBridgeDO> {
9   -}
  17 +
  18 + default PageResult<IotDataBridgeDO> selectPage(IotDataBridgePageReqVO reqVO) {
  19 + return selectPage(reqVO, new LambdaQueryWrapperX<IotDataBridgeDO>()
  20 + .likeIfPresent(IotDataBridgeDO::getName, reqVO.getName())
  21 + .likeIfPresent(IotDataBridgeDO::getDescription, reqVO.getDescription())
  22 + .eqIfPresent(IotDataBridgeDO::getStatus, reqVO.getStatus())
  23 + .eqIfPresent(IotDataBridgeDO::getDirection, reqVO.getDirection())
  24 + .eqIfPresent(IotDataBridgeDO::getType, reqVO.getType())
  25 + .betweenIfPresent(IotDataBridgeDO::getCreateTime, reqVO.getCreateTime())
  26 + .orderByDesc(IotDataBridgeDO::getId));
  27 + }
  28 +
  29 +}
10 30 \ No newline at end of file
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/IotDataBridgeService.java
1 1 package cn.iocoder.yudao.module.iot.service.rule;
2 2  
  3 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  4 +import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.databridge.IotDataBridgePageReqVO;
  5 +import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.databridge.IotDataBridgeSaveReqVO;
3 6 import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotDataBridgeDO;
  7 +import jakarta.validation.Valid;
4 8  
5 9 /**
6   - * IoT 数据桥梁的 Service 接口
  10 + * IoT 数据桥梁 Service 接口
7 11 *
8   - * @author 芋道源码
  12 + * @author HUIHUI
9 13 */
10 14 public interface IotDataBridgeService {
11 15  
12 16 /**
13   - * 获得指定数据桥梁
  17 + * 创建IoT 数据桥梁
14 18 *
15   - * @param id 数据桥梁编号
16   - * @return 数据桥梁
  19 + * @param createReqVO 创建信息
  20 + * @return 编号
17 21 */
18   - IotDataBridgeDO getIotDataBridge(Long id);
  22 + Long createDataBridge(@Valid IotDataBridgeSaveReqVO createReqVO);
19 23  
20   -}
  24 + /**
  25 + * 更新IoT 数据桥梁
  26 + *
  27 + * @param updateReqVO 更新信息
  28 + */
  29 + void updateDataBridge(@Valid IotDataBridgeSaveReqVO updateReqVO);
  30 +
  31 + /**
  32 + * 删除IoT 数据桥梁
  33 + *
  34 + * @param id 编号
  35 + */
  36 + void deleteDataBridge(Long id);
  37 +
  38 + /**
  39 + * 获得IoT 数据桥梁
  40 + *
  41 + * @param id 编号
  42 + * @return IoT 数据桥梁
  43 + */
  44 + IotDataBridgeDO getDataBridge(Long id);
  45 +
  46 + /**
  47 + * 获得IoT 数据桥梁分页
  48 + *
  49 + * @param pageReqVO 分页查询
  50 + * @return IoT 数据桥梁分页
  51 + */
  52 + PageResult<IotDataBridgeDO> getDataBridgePage(IotDataBridgePageReqVO pageReqVO);
  53 +
  54 +}
21 55 \ No newline at end of file
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/IotDataBridgeServiceImpl.java
1 1 package cn.iocoder.yudao.module.iot.service.rule;
2 2  
3   -import cn.hutool.core.map.MapUtil;
4   -import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
  3 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  4 +import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
  5 +import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.databridge.IotDataBridgePageReqVO;
  6 +import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.databridge.IotDataBridgeSaveReqVO;
5 7 import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotDataBridgeDO;
6 8 import cn.iocoder.yudao.module.iot.dal.mysql.rule.IotDataBridgeMapper;
7   -import cn.iocoder.yudao.module.iot.enums.rule.IotDataBridgTypeEnum;
8 9 import jakarta.annotation.Resource;
9   -import lombok.extern.slf4j.Slf4j;
10 10 import org.springframework.stereotype.Service;
11 11 import org.springframework.validation.annotation.Validated;
12 12  
13   -import java.util.Objects;
  13 +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
  14 +import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.DATA_BRIDGE_NOT_EXISTS;
14 15  
15 16 /**
16   - * IoT 数据桥梁的 Service 实现类
  17 + * IoT 数据桥梁 Service 实现类
17 18 *
18   - * @author 芋道源码
  19 + * @author HUIHUI
19 20 */
20 21 @Service
21 22 @Validated
22   -@Slf4j
23 23 public class IotDataBridgeServiceImpl implements IotDataBridgeService {
24 24  
25 25 @Resource
26 26 private IotDataBridgeMapper dataBridgeMapper;
27 27  
28   - // TODO @芋艿:临时测试
29 28 @Override
30   - public IotDataBridgeDO getIotDataBridge(Long id) {
31   - if (Objects.equals(id, 1L)) {
32   - IotDataBridgeDO.HttpConfig config = new IotDataBridgeDO.HttpConfig()
33   - .setUrl("http://127.0.0.1:48080/test")
34   -// .setMethod("POST")
35   - .setMethod("GET")
36   - .setQuery(MapUtil.of("aaa", "bbb"))
37   - .setHeaders(MapUtil.of("ccc", "ddd"))
38   - .setBody(JsonUtils.toJsonString(MapUtil.of("eee", "fff")));
39   - return IotDataBridgeDO.builder().id(1L).name("芋道").description("芋道源码").status(0).direction(1)
40   - .type(IotDataBridgTypeEnum.HTTP.getType()).config(config).build();
  29 + public Long createDataBridge(IotDataBridgeSaveReqVO createReqVO) {
  30 + // 插入
  31 + IotDataBridgeDO dataBridge = BeanUtils.toBean(createReqVO, IotDataBridgeDO.class);
  32 + dataBridgeMapper.insert(dataBridge);
  33 + // 返回
  34 + return dataBridge.getId();
  35 + }
  36 +
  37 + @Override
  38 + public void updateDataBridge(IotDataBridgeSaveReqVO updateReqVO) {
  39 + // 校验存在
  40 + validateDataBridgeExists(updateReqVO.getId());
  41 + // 更新
  42 + IotDataBridgeDO updateObj = BeanUtils.toBean(updateReqVO, IotDataBridgeDO.class);
  43 + dataBridgeMapper.updateById(updateObj);
  44 + }
  45 +
  46 + @Override
  47 + public void deleteDataBridge(Long id) {
  48 + // 校验存在
  49 + validateDataBridgeExists(id);
  50 + // 删除
  51 + dataBridgeMapper.deleteById(id);
  52 + }
  53 +
  54 + private void validateDataBridgeExists(Long id) {
  55 + if (dataBridgeMapper.selectById(id) == null) {
  56 + throw exception(DATA_BRIDGE_NOT_EXISTS);
41 57 }
  58 + }
  59 +
  60 + @Override
  61 + public IotDataBridgeDO getDataBridge(Long id) {
42 62 return dataBridgeMapper.selectById(id);
43 63 }
44 64  
45   -}
  65 + @Override
  66 + public PageResult<IotDataBridgeDO> getDataBridgePage(IotDataBridgePageReqVO pageReqVO) {
  67 + return dataBridgeMapper.selectPage(pageReqVO);
  68 + }
  69 +
  70 +}
46 71 \ No newline at end of file
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/IotRuleSceneAction.java
... ... @@ -22,7 +22,7 @@ public interface IotRuleSceneAction {
22 22 * 2. 非空的情况:设备触发
23 23 * @param config 配置
24 24 */
25   - void execute(@Nullable IotDeviceMessage message, IotRuleSceneDO.ActionConfig config);
  25 + void execute(@Nullable IotDeviceMessage message, IotRuleSceneDO.ActionConfig config) throws Exception;
26 26  
27 27 /**
28 28 * 获得类型
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/IotRuleSceneDataBridgeAction.java
... ... @@ -26,17 +26,17 @@ public class IotRuleSceneDataBridgeAction implements IotRuleSceneAction {
26 26 @Resource
27 27 private IotDataBridgeService dataBridgeService;
28 28 @Resource
29   - private List<IotDataBridgeExecute> dataBridgeExecutes;
  29 + private List<IotDataBridgeExecute<?>> dataBridgeExecutes;
30 30  
31 31 @Override
32   - public void execute(IotDeviceMessage message, IotRuleSceneDO.ActionConfig config) {
  32 + public void execute(IotDeviceMessage message, IotRuleSceneDO.ActionConfig config) throws Exception {
33 33 // 1.1 如果消息为空,直接返回
34 34 if (message == null) {
35 35 return;
36 36 }
37 37 // 1.2 获得数据桥梁
38 38 Assert.notNull(config.getDataBridgeId(), "数据桥梁编号不能为空");
39   - IotDataBridgeDO dataBridge = dataBridgeService.getIotDataBridge(config.getDataBridgeId());
  39 + IotDataBridgeDO dataBridge = dataBridgeService.getDataBridge(config.getDataBridgeId());
40 40 if (dataBridge == null || dataBridge.getConfig() == null) {
41 41 log.error("[execute][message({}) config({}) 对应的数据桥梁不存在]", message, config);
42 42 return;
... ... @@ -47,7 +47,9 @@ public class IotRuleSceneDataBridgeAction implements IotRuleSceneAction {
47 47 }
48 48  
49 49 // 2. 执行数据桥接操作
50   - dataBridgeExecutes.forEach(execute -> execute.execute(message, dataBridge));
  50 + for (IotDataBridgeExecute<?> execute : dataBridgeExecutes) {
  51 + execute.execute(message, dataBridge);
  52 + }
51 53 }
52 54  
53 55 @Override
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/AbstractCacheableDataBridgeExecute.java
1 1 package cn.iocoder.yudao.module.iot.service.rule.action.databridge;
2 2  
  3 +import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotDataBridgeDO;
  4 +import cn.iocoder.yudao.module.iot.mq.message.IotDeviceMessage;
3 5 import com.google.common.cache.CacheBuilder;
4 6 import com.google.common.cache.CacheLoader;
5 7 import com.google.common.cache.LoadingCache;
  8 +import com.google.common.cache.RemovalListener;
6 9 import lombok.extern.slf4j.Slf4j;
7 10  
8 11 import java.time.Duration;
... ... @@ -15,22 +18,36 @@ import java.time.Duration;
15 18 /**
16 19 * 带缓存功能的数据桥梁执行器抽象类
17 20 *
  21 + * 该类提供了一个通用的缓存机制,用于管理各类数据桥接的生产者(Producer)实例。
  22 + *
  23 + * 主要特点:
  24 + * - 基于Guava Cache实现高效的生产者实例缓存管理
  25 + * - 自动处理生产者的生命周期(创建、获取、关闭)
  26 + * - 支持30分钟未访问自动过期清理机制
  27 + * - 异常处理与日志记录,便于问题排查
  28 + *
  29 + * 子类需要实现:
  30 + * - initProducer(Config) - 初始化特定类型的生产者实例
  31 + * - closeProducer(Producer) - 关闭生产者实例并释放资源
  32 + *
  33 + * @param <Config> 配置信息类型,用于初始化生产者
  34 + * @param <Producer> 生产者类型,负责将数据发送到目标系统
18 35 * @author HUIHUI
19 36 */
20 37 @Slf4j
21   -public abstract class AbstractCacheableDataBridgeExecute implements IotDataBridgeExecute {
  38 +public abstract class AbstractCacheableDataBridgeExecute<Config, Producer> implements IotDataBridgeExecute<Config> {
22 39  
23   - // TODO @huihui:AbstractCacheableDataBridgeExecute<Producer> 这样,下面的 Object, Object 就有了类型;另外 IotDataBridgeDO.Config 可以替代一个 Object 哇,
24 40 /**
25 41 * Producer 缓存
26 42 */
27   - private final LoadingCache<Object, Object> PRODUCER_CACHE = CacheBuilder.newBuilder()
28   - .expireAfterAccess(Duration.ofMinutes(30))
29   - .removalListener(notification -> {
30   - Object producer = notification.getValue();
  43 + private final LoadingCache<Config, Producer> PRODUCER_CACHE = CacheBuilder.newBuilder()
  44 + .expireAfterAccess(Duration.ofMinutes(30)) // 30 分钟未访问就提前过期
  45 + .removalListener((RemovalListener<Config, Producer>) notification -> {
  46 + Producer producer = notification.getValue();
31 47 if (producer == null) {
32 48 return;
33 49 }
  50 +
34 51 try {
35 52 closeProducer(producer);
36 53 log.info("[PRODUCER_CACHE][配置({}) 对应的 producer 已关闭]", notification.getKey());
... ... @@ -38,15 +55,18 @@ public abstract class AbstractCacheableDataBridgeExecute implements IotDataBridg
38 55 log.error("[PRODUCER_CACHE][配置({}) 对应的 producer 关闭失败]", notification.getKey(), e);
39 56 }
40 57 })
41   - .build(new CacheLoader<Object, Object>() {
42   -
  58 + .build(new CacheLoader<Config, Producer>() {
43 59 @Override
44   - public Object load(Object config) throws Exception {
45   - Object producer = initProducer(config);
46   - log.info("[PRODUCER_CACHE][配置({}) 对应的 producer 已创建并启动]", config);
47   - return producer;
  60 + public Producer load(Config config) throws Exception {
  61 + try {
  62 + Producer producer = initProducer(config);
  63 + log.info("[PRODUCER_CACHE][配置({}) 对应的 producer 已创建并启动]", config);
  64 + return producer;
  65 + } catch (Exception e) {
  66 + log.error("[PRODUCER_CACHE][配置({}) 对应的 producer 创建启动失败]", config, e);
  67 + throw e; // 抛出异常,触发缓存加载失败机制
  68 + }
48 69 }
49   -
50 70 });
51 71  
52 72 /**
... ... @@ -55,7 +75,7 @@ public abstract class AbstractCacheableDataBridgeExecute implements IotDataBridg
55 75 * @param config 配置信息
56 76 * @return 生产者对象
57 77 */
58   - protected Object getProducer(Object config) throws Exception {
  78 + protected Producer getProducer(Config config) throws Exception {
59 79 return PRODUCER_CACHE.get(config);
60 80 }
61 81  
... ... @@ -66,13 +86,29 @@ public abstract class AbstractCacheableDataBridgeExecute implements IotDataBridg
66 86 * @return 生产者对象
67 87 * @throws Exception 如果初始化失败
68 88 */
69   - protected abstract Object initProducer(Object config) throws Exception;
  89 + protected abstract Producer initProducer(Config config) throws Exception;
70 90  
71 91 /**
72 92 * 关闭生产者
73 93 *
74 94 * @param producer 生产者对象
75 95 */
76   - protected abstract void closeProducer(Object producer);
  96 + protected abstract void closeProducer(Producer producer) throws Exception;
  97 +
  98 + @Override
  99 + @SuppressWarnings({"unchecked"})
  100 + public void execute(IotDeviceMessage message, IotDataBridgeDO dataBridge) {
  101 + // 1.1 校验数据桥梁类型
  102 + if (!getType().equals(dataBridge.getType())) {
  103 + return;
  104 + }
  105 +
  106 + // 1.2 执行对应的数据桥梁发送消息
  107 + try {
  108 + execute0(message, (Config) dataBridge.getConfig());
  109 + } catch (Exception e) {
  110 + log.error("[execute][桥梁配置 config({}) 对应的 message({}) 发送异常]", dataBridge.getConfig(), message, e);
  111 + }
  112 + }
77 113  
78 114 }
79 115 \ No newline at end of file
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotDataBridgeExecute.java
... ... @@ -9,9 +9,14 @@ import cn.iocoder.yudao.module.iot.mq.message.IotDeviceMessage;
9 9 *
10 10 * @author HUIHUI
11 11 */
12   -public interface IotDataBridgeExecute {
  12 +public interface IotDataBridgeExecute<Config> {
13 13  
14   - // TODO @huihui:要不搞个 getType?然后 execute0 由子类实现。这样,子类的 executeRedisStream ,其实就是 execute0 了。
  14 + /**
  15 + * 获取数据桥梁类型
  16 + *
  17 + * @return 数据桥梁类型
  18 + */
  19 + Integer getType();
15 20  
16 21 /**
17 22 * 执行数据桥梁操作
... ... @@ -19,6 +24,23 @@ public interface IotDataBridgeExecute {
19 24 * @param message 设备消息
20 25 * @param dataBridge 数据桥梁
21 26 */
22   - void execute(IotDeviceMessage message, IotDataBridgeDO dataBridge);
  27 + @SuppressWarnings({"unchecked"})
  28 + default void execute(IotDeviceMessage message, IotDataBridgeDO dataBridge) throws Exception {
  29 + // 1.1 校验数据桥梁类型
  30 + if (!getType().equals(dataBridge.getType())) {
  31 + return;
  32 + }
  33 +
  34 + // 1.2 执行对应的数据桥梁发送消息
  35 + execute0(message, (Config) dataBridge.getConfig());
  36 + }
  37 +
  38 + /**
  39 + * 【真正】执行数据桥梁操作
  40 + *
  41 + * @param message 设备消息
  42 + * @param config 桥梁配置
  43 + */
  44 + void execute0(IotDeviceMessage message, Config config) throws Exception;
23 45  
24 46 }
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotHttpDataBridgeExecute.java
... ... @@ -3,8 +3,8 @@ package cn.iocoder.yudao.module.iot.service.rule.action.databridge;
3 3 import cn.hutool.core.collection.CollUtil;
4 4 import cn.iocoder.yudao.framework.common.util.http.HttpUtils;
5 5 import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
6   -import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotDataBridgeDO;
7   -import cn.iocoder.yudao.module.iot.enums.rule.IotDataBridgTypeEnum;
  6 +import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.databridge.config.IotDataBridgeHttpConfig;
  7 +import cn.iocoder.yudao.module.iot.enums.rule.IotDataBridgeTypeEnum;
8 8 import cn.iocoder.yudao.module.iot.mq.message.IotDeviceMessage;
9 9 import jakarta.annotation.Resource;
10 10 import lombok.extern.slf4j.Slf4j;
... ... @@ -25,23 +25,19 @@ import static cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils.HEADER_
25 25 */
26 26 @Component
27 27 @Slf4j
28   -public class IotHttpDataBridgeExecute implements IotDataBridgeExecute {
  28 +public class IotHttpDataBridgeExecute implements IotDataBridgeExecute<IotDataBridgeHttpConfig> {
29 29  
30 30 @Resource
31 31 private RestTemplate restTemplate;
32 32  
33 33 @Override
34   - public void execute(IotDeviceMessage message, IotDataBridgeDO dataBridge) {
35   - // 1.1 校验数据桥梁的类型 == HTTP
36   - if (!IotDataBridgTypeEnum.HTTP.getType().equals(dataBridge.getType())) {
37   - return;
38   - }
39   - // 1.2 执行 HTTP 请求
40   - executeHttp(message, (IotDataBridgeDO.HttpConfig) dataBridge.getConfig());
  34 + public Integer getType() {
  35 + return IotDataBridgeTypeEnum.HTTP.getType();
41 36 }
42 37  
  38 + @Override
43 39 @SuppressWarnings({"unchecked", "deprecation"})
44   - private void executeHttp(IotDeviceMessage message, IotDataBridgeDO.HttpConfig config) {
  40 + public void execute0(IotDeviceMessage message, IotDataBridgeHttpConfig config) {
45 41 String url = null;
46 42 HttpMethod method = HttpMethod.valueOf(config.getMethod().toUpperCase());
47 43 HttpEntity<String> requestEntity = null;
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotKafkaMQDataBridgeExecute.java
1 1 package cn.iocoder.yudao.module.iot.service.rule.action.databridge;
2 2  
  3 +import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.databridge.config.IotDataBridgeKafkaMQConfig;
3 4 import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotDataBridgeDO;
4   -import cn.iocoder.yudao.module.iot.enums.rule.IotDataBridgTypeEnum;
  5 +import cn.iocoder.yudao.module.iot.enums.rule.IotDataBridgeTypeEnum;
5 6 import cn.iocoder.yudao.module.iot.mq.message.IotDeviceMessage;
6 7 import lombok.extern.slf4j.Slf4j;
7 8 import org.apache.kafka.clients.producer.ProducerConfig;
8 9 import org.apache.kafka.common.serialization.StringSerializer;
  10 +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
9 11 import org.springframework.kafka.core.DefaultKafkaProducerFactory;
10 12 import org.springframework.kafka.core.KafkaTemplate;
11 13 import org.springframework.stereotype.Component;
... ... @@ -15,65 +17,53 @@ import java.time.LocalDateTime;
15 17 import java.util.HashMap;
16 18 import java.util.Map;
17 19 import java.util.concurrent.TimeUnit;
18   -import java.util.concurrent.TimeoutException;
19 20  
20 21 /**
21 22 * Kafka 的 {@link IotDataBridgeExecute} 实现类
22 23 *
23 24 * @author HUIHUI
24 25 */
  26 +@ConditionalOnClass(name = "org.springframework.kafka.core.KafkaTemplate")
25 27 @Component
26 28 @Slf4j
27   -public class IotKafkaMQDataBridgeExecute extends AbstractCacheableDataBridgeExecute {
  29 +public class IotKafkaMQDataBridgeExecute extends
  30 + AbstractCacheableDataBridgeExecute<IotDataBridgeKafkaMQConfig, KafkaTemplate<String, String>> {
28 31  
29   - private static final Duration SEND_TIMEOUT = Duration.ofMillis(10);
  32 + private static final Duration SEND_TIMEOUT = Duration.ofMillis(10000); // 10 秒超时时间
30 33  
31 34 @Override
32   - public void execute(IotDeviceMessage message, IotDataBridgeDO dataBridge) {
33   - // 1. 校验数据桥梁的类型 == KAFKA
34   - if (!IotDataBridgTypeEnum.KAFKA.getType().equals(dataBridge.getType())) {
35   - return;
36   - }
37   - // 2. 执行 Kafka 发送消息
38   - executeKafka(message, (IotDataBridgeDO.KafkaMQConfig) dataBridge.getConfig());
  35 + public Integer getType() {
  36 + return IotDataBridgeTypeEnum.KAFKA.getType();
39 37 }
40 38  
41   - @SuppressWarnings("unchecked")
42   - private void executeKafka(IotDeviceMessage message, IotDataBridgeDO.KafkaMQConfig config) {
43   - try {
44   - // 1. 获取或创建 KafkaTemplate
45   - KafkaTemplate<String, String> kafkaTemplate = (KafkaTemplate<String, String>) getProducer(config);
46   -
47   - // 2. 发送消息并等待结果
48   - kafkaTemplate.send(config.getTopic(), message.toString())
49   - .get(SEND_TIMEOUT.getSeconds(), TimeUnit.SECONDS); // 添加超时等待
50   - log.info("[executeKafka][message({}) 发送成功]", message);
51   - } catch (TimeoutException e) {
52   - log.error("[executeKafka][message({}) config({}) 发送超时]", message, config, e);
53   - } catch (Exception e) {
54   - log.error("[executeKafka][message({}) config({}) 发送异常]", message, config, e);
55   - }
  39 + @Override
  40 + public void execute0(IotDeviceMessage message, IotDataBridgeKafkaMQConfig config) throws Exception {
  41 + // 1. 获取或创建 KafkaTemplate
  42 + KafkaTemplate<String, String> kafkaTemplate = getProducer(config);
  43 +
  44 + // 2. 发送消息并等待结果
  45 + kafkaTemplate.send(config.getTopic(), message.toString())
  46 + .get(SEND_TIMEOUT.getSeconds(), TimeUnit.SECONDS); // 添加超时等待
  47 + log.info("[execute0][message({}) 发送成功]", message);
56 48 }
57 49  
58 50 @Override
59   - protected Object initProducer(Object config) {
60   - IotDataBridgeDO.KafkaMQConfig kafkaConfig = (IotDataBridgeDO.KafkaMQConfig) config;
61   -
  51 + protected KafkaTemplate<String, String> initProducer(IotDataBridgeKafkaMQConfig config) {
62 52 // 1.1 构建生产者配置
63 53 Map<String, Object> props = new HashMap<>();
64   - props.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, kafkaConfig.getBootstrapServers());
  54 + props.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, config.getBootstrapServers());
65 55 props.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class);
66 56 props.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class);
67 57 // 1.2 如果配置了认证信息
68   - if (kafkaConfig.getUsername() != null && kafkaConfig.getPassword() != null) {
  58 + if (config.getUsername() != null && config.getPassword() != null) {
69 59 props.put("security.protocol", "SASL_PLAINTEXT");
70 60 props.put("sasl.mechanism", "PLAIN");
71 61 props.put("sasl.jaas.config",
72 62 "org.apache.kafka.common.security.plain.PlainLoginModule required username=\""
73   - + kafkaConfig.getUsername() + "\" password=\"" + kafkaConfig.getPassword() + "\";");
  63 + + config.getUsername() + "\" password=\"" + config.getPassword() + "\";");
74 64 }
75 65 // 1.3 如果启用 SSL
76   - if (Boolean.TRUE.equals(kafkaConfig.getSsl())) {
  66 + if (Boolean.TRUE.equals(config.getSsl())) {
77 67 props.put("security.protocol", "SSL");
78 68 }
79 69  
... ... @@ -83,10 +73,8 @@ public class IotKafkaMQDataBridgeExecute extends AbstractCacheableDataBridgeExec
83 73 }
84 74  
85 75 @Override
86   - protected void closeProducer(Object producer) {
87   - if (producer instanceof KafkaTemplate) {
88   - ((KafkaTemplate<?, ?>) producer).destroy();
89   - }
  76 + protected void closeProducer(KafkaTemplate<String, String> producer) {
  77 + producer.destroy();
90 78 }
91 79  
92 80 // TODO @芋艿:测试代码,后续清理
... ... @@ -95,7 +83,7 @@ public class IotKafkaMQDataBridgeExecute extends AbstractCacheableDataBridgeExec
95 83 IotKafkaMQDataBridgeExecute action = new IotKafkaMQDataBridgeExecute();
96 84  
97 85 // 2. 创建共享的配置
98   - IotDataBridgeDO.KafkaMQConfig config = new IotDataBridgeDO.KafkaMQConfig();
  86 + IotDataBridgeKafkaMQConfig config = new IotDataBridgeKafkaMQConfig();
99 87 config.setBootstrapServers("127.0.0.1:9092");
100 88 config.setTopic("test-topic");
101 89 config.setSsl(false);
... ... @@ -117,10 +105,10 @@ public class IotKafkaMQDataBridgeExecute extends AbstractCacheableDataBridgeExec
117 105  
118 106 // 4. 执行两次测试,验证缓存
119 107 log.info("[main][第一次执行,应该会创建新的 producer]");
120   - action.executeKafka(message, config);
  108 + action.execute(message, new IotDataBridgeDO().setType(action.getType()).setConfig(config));
121 109  
122 110 log.info("[main][第二次执行,应该会复用缓存的 producer]");
123   - action.executeKafka(message, config);
  111 + action.execute(message, new IotDataBridgeDO().setType(action.getType()).setConfig(config));
124 112 }
125 113  
126 114 }
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotRabbitMQDataBridgeExecute.java
1 1 package cn.iocoder.yudao.module.iot.service.rule.action.databridge;
2 2  
  3 +import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.databridge.config.IotDataBridgeRabbitMQConfig;
3 4 import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotDataBridgeDO;
4   -import cn.iocoder.yudao.module.iot.enums.rule.IotDataBridgTypeEnum;
  5 +import cn.iocoder.yudao.module.iot.enums.rule.IotDataBridgeTypeEnum;
5 6 import cn.iocoder.yudao.module.iot.mq.message.IotDeviceMessage;
6 7 import com.rabbitmq.client.Channel;
7 8 import com.rabbitmq.client.Connection;
8 9 import com.rabbitmq.client.ConnectionFactory;
9 10 import lombok.extern.slf4j.Slf4j;
  11 +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
10 12 import org.springframework.stereotype.Component;
11 13  
12 14 import java.nio.charset.StandardCharsets;
... ... @@ -17,51 +19,44 @@ import java.time.LocalDateTime;
17 19 *
18 20 * @author HUIHUI
19 21 */
  22 +@ConditionalOnClass(name = "com.rabbitmq.client.Channel")
20 23 @Component
21 24 @Slf4j
22   -public class IotRabbitMQDataBridgeExecute extends AbstractCacheableDataBridgeExecute {
  25 +public class IotRabbitMQDataBridgeExecute extends
  26 + AbstractCacheableDataBridgeExecute<IotDataBridgeRabbitMQConfig, Channel> {
  27 +
23 28  
24 29 @Override
25   - public void execute(IotDeviceMessage message, IotDataBridgeDO dataBridge) {
26   - // 1.1 校验数据桥梁的类型 == RABBITMQ
27   - if (!IotDataBridgTypeEnum.RABBITMQ.getType().equals(dataBridge.getType())) {
28   - return;
29   - }
30   - // 1.2 执行 RabbitMQ 发送消息
31   - executeRabbitMQ(message, (IotDataBridgeDO.RabbitMQConfig) dataBridge.getConfig());
  30 + public Integer getType() {
  31 + return IotDataBridgeTypeEnum.RABBITMQ.getType();
32 32 }
33 33  
34   - private void executeRabbitMQ(IotDeviceMessage message, IotDataBridgeDO.RabbitMQConfig config) {
35   - try {
36   - // 1. 获取或创建 Channel
37   - Channel channel = (Channel) getProducer(config);
38   -
39   - // 2.1 声明交换机、队列和绑定关系
40   - channel.exchangeDeclare(config.getExchange(), "direct", true);
41   - channel.queueDeclare(config.getQueue(), true, false, false, null);
42   - channel.queueBind(config.getQueue(), config.getExchange(), config.getRoutingKey());
43   -
44   - // 2.2 发送消息
45   - channel.basicPublish(config.getExchange(), config.getRoutingKey(), null,
46   - message.toString().getBytes(StandardCharsets.UTF_8));
47   - log.info("[executeRabbitMQ][message({}) config({}) 发送成功]", message, config);
48   - } catch (Exception e) {
49   - log.error("[executeRabbitMQ][message({}) config({}) 发送异常]", message, config, e);
50   - }
  34 + @Override
  35 + public void execute0(IotDeviceMessage message, IotDataBridgeRabbitMQConfig config) throws Exception {
  36 + // 1. 获取或创建 Channel
  37 + Channel channel = getProducer(config);
  38 +
  39 + // 2.1 声明交换机、队列和绑定关系
  40 + channel.exchangeDeclare(config.getExchange(), "direct", true);
  41 + channel.queueDeclare(config.getQueue(), true, false, false, null);
  42 + channel.queueBind(config.getQueue(), config.getExchange(), config.getRoutingKey());
  43 +
  44 + // 2.2 发送消息
  45 + channel.basicPublish(config.getExchange(), config.getRoutingKey(), null,
  46 + message.toString().getBytes(StandardCharsets.UTF_8));
  47 + log.info("[executeRabbitMQ][message({}) config({}) 发送成功]", message, config);
51 48 }
52 49  
53 50 @Override
54 51 @SuppressWarnings("resource")
55   - protected Object initProducer(Object config) throws Exception {
56   - IotDataBridgeDO.RabbitMQConfig rabbitConfig = (IotDataBridgeDO.RabbitMQConfig) config;
57   -
  52 + protected Channel initProducer(IotDataBridgeRabbitMQConfig config) throws Exception {
58 53 // 1. 创建连接工厂
59 54 ConnectionFactory factory = new ConnectionFactory();
60   - factory.setHost(rabbitConfig.getHost());
61   - factory.setPort(rabbitConfig.getPort());
62   - factory.setVirtualHost(rabbitConfig.getVirtualHost());
63   - factory.setUsername(rabbitConfig.getUsername());
64   - factory.setPassword(rabbitConfig.getPassword());
  55 + factory.setHost(config.getHost());
  56 + factory.setPort(config.getPort());
  57 + factory.setVirtualHost(config.getVirtualHost());
  58 + factory.setUsername(config.getUsername());
  59 + factory.setPassword(config.getPassword());
65 60  
66 61 // 2. 创建连接
67 62 Connection connection = factory.newConnection();
... ... @@ -71,20 +66,13 @@ public class IotRabbitMQDataBridgeExecute extends AbstractCacheableDataBridgeExe
71 66 }
72 67  
73 68 @Override
74   - protected void closeProducer(Object producer) {
75   - if (producer instanceof Channel) {
76   - try {
77   - Channel channel = (Channel) producer;
78   - if (channel.isOpen()) {
79   - channel.close();
80   - }
81   - Connection connection = channel.getConnection();
82   - if (connection.isOpen()) {
83   - connection.close();
84   - }
85   - } catch (Exception e) {
86   - log.error("[closeProducer][关闭 RabbitMQ 连接异常]", e);
87   - }
  69 + protected void closeProducer(Channel channel) throws Exception {
  70 + if (channel.isOpen()) {
  71 + channel.close();
  72 + }
  73 + Connection connection = channel.getConnection();
  74 + if (connection.isOpen()) {
  75 + connection.close();
88 76 }
89 77 }
90 78  
... ... @@ -94,7 +82,7 @@ public class IotRabbitMQDataBridgeExecute extends AbstractCacheableDataBridgeExe
94 82 IotRabbitMQDataBridgeExecute action = new IotRabbitMQDataBridgeExecute();
95 83  
96 84 // 2. 创建共享的配置
97   - IotDataBridgeDO.RabbitMQConfig config = new IotDataBridgeDO.RabbitMQConfig();
  85 + IotDataBridgeRabbitMQConfig config = new IotDataBridgeRabbitMQConfig();
98 86 config.setHost("localhost");
99 87 config.setPort(5672);
100 88 config.setVirtualHost("/");
... ... @@ -118,10 +106,12 @@ public class IotRabbitMQDataBridgeExecute extends AbstractCacheableDataBridgeExe
118 106 .build();
119 107  
120 108 // 4. 执行两次测试,验证缓存
121   - log.info("[main][第一次执行,应该会创建新的 channel]");
122   - action.executeRabbitMQ(message, config);
  109 + // 4. 执行两次测试,验证缓存
  110 + log.info("[main][第一次执行,应该会创建新的 producer]");
  111 + action.execute(message, new IotDataBridgeDO().setType(action.getType()).setConfig(config));
123 112  
124   - log.info("[main][第二次执行,应该会复用缓存的 channel]");
125   - action.executeRabbitMQ(message, config);
  113 + log.info("[main][第二次执行,应该会复用缓存的 producer]");
  114 + action.execute(message, new IotDataBridgeDO().setType(action.getType()).setConfig(config));
126 115 }
  116 +
127 117 }
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotRedisStreamMQDataBridgeExecute.java
... ... @@ -2,8 +2,9 @@ package cn.iocoder.yudao.module.iot.service.rule.action.databridge;
2 2  
3 3 import cn.hutool.core.util.ReflectUtil;
4 4 import cn.hutool.core.util.StrUtil;
  5 +import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.databridge.config.IotDataBridgeRedisStreamMQConfig;
5 6 import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotDataBridgeDO;
6   -import cn.iocoder.yudao.module.iot.enums.rule.IotDataBridgTypeEnum;
  7 +import cn.iocoder.yudao.module.iot.enums.rule.IotDataBridgeTypeEnum;
7 8 import cn.iocoder.yudao.module.iot.mq.message.IotDeviceMessage;
8 9 import com.fasterxml.jackson.databind.ObjectMapper;
9 10 import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
... ... @@ -29,47 +30,36 @@ import java.time.LocalDateTime;
29 30 */
30 31 @Component
31 32 @Slf4j
32   -public class IotRedisStreamMQDataBridgeExecute extends AbstractCacheableDataBridgeExecute {
  33 +public class IotRedisStreamMQDataBridgeExecute extends
  34 + AbstractCacheableDataBridgeExecute<IotDataBridgeRedisStreamMQConfig, RedisTemplate<String, Object>> {
33 35  
34 36 @Override
35   - public void execute(IotDeviceMessage message, IotDataBridgeDO dataBridge) {
36   - // 1.1 校验数据桥梁类型
37   - if (!IotDataBridgTypeEnum.REDIS_STREAM.getType().equals(dataBridge.getType())) {
38   - return;
39   - }
40   - // 1.2 执行消息发送
41   - executeRedisStream(message, (IotDataBridgeDO.RedisStreamMQConfig) dataBridge.getConfig());
  37 + public Integer getType() {
  38 + return IotDataBridgeTypeEnum.REDIS_STREAM.getType();
42 39 }
43 40  
44   - @SuppressWarnings("unchecked")
45   - // TODO @huihui:try catch 交给父类来做,子类不处理异常
46   - private void executeRedisStream(IotDeviceMessage message, IotDataBridgeDO.RedisStreamMQConfig config) {
47   - try {
48   - // 1. 获取 RedisTemplate
49   - RedisTemplate<String, Object> redisTemplate = (RedisTemplate<String, Object>) getProducer(config);
50   -
51   - // 2. 创建并发送 Stream 记录
52   - ObjectRecord<String, IotDeviceMessage> record = StreamRecords.newRecord()
53   - .ofObject(message).withStreamKey(config.getTopic());
54   - String recordId = String.valueOf(redisTemplate.opsForStream().add(record));
55   - log.info("[executeRedisStream][消息发送成功] messageId: {}, config: {}", recordId, config);
56   - } catch (Exception e) {
57   - log.error("[executeRedisStream][消息发送失败] message: {}, config: {}", message, config, e);
58   - }
  41 + @Override
  42 + public void execute0(IotDeviceMessage message, IotDataBridgeRedisStreamMQConfig config) throws Exception {
  43 + // 1. 获取 RedisTemplate
  44 + RedisTemplate<String, Object> redisTemplate = getProducer(config);
  45 +
  46 + // 2. 创建并发送 Stream 记录
  47 + ObjectRecord<String, IotDeviceMessage> record = StreamRecords.newRecord()
  48 + .ofObject(message).withStreamKey(config.getTopic());
  49 + String recordId = String.valueOf(redisTemplate.opsForStream().add(record));
  50 + log.info("[executeRedisStream][消息发送成功] messageId: {}, config: {}", recordId, config);
59 51 }
60 52  
61 53 @Override
62   - protected Object initProducer(Object config) {
63   - IotDataBridgeDO.RedisStreamMQConfig redisConfig = (IotDataBridgeDO.RedisStreamMQConfig) config;
64   -
  54 + protected RedisTemplate<String, Object> initProducer(IotDataBridgeRedisStreamMQConfig config) {
65 55 // 1.1 创建 Redisson 配置
66 56 Config redissonConfig = new Config();
67 57 SingleServerConfig serverConfig = redissonConfig.useSingleServer()
68   - .setAddress("redis://" + redisConfig.getHost() + ":" + redisConfig.getPort())
69   - .setDatabase(redisConfig.getDatabase());
  58 + .setAddress("redis://" + config.getHost() + ":" + config.getPort())
  59 + .setDatabase(config.getDatabase());
70 60 // 1.2 设置密码(如果有)
71   - if (StrUtil.isNotBlank(redisConfig.getPassword())) {
72   - serverConfig.setPassword(redisConfig.getPassword());
  61 + if (StrUtil.isNotBlank(config.getPassword())) {
  62 + serverConfig.setPassword(config.getPassword());
73 63 }
74 64  
75 65 // TODO @huihui:看看能不能简化一些。按道理说,不用这么多的哈。
... ... @@ -90,17 +80,10 @@ public class IotRedisStreamMQDataBridgeExecute extends AbstractCacheableDataBrid
90 80 }
91 81  
92 82 @Override
93   - protected void closeProducer(Object producer) {
94   - // TODO @huihui:try catch 交给父类来做。子类不处理异常
95   - if (producer instanceof RedisTemplate) {
96   - RedisConnectionFactory factory = ((RedisTemplate<?, ?>) producer).getConnectionFactory();
97   - try {
98   - if (factory != null) {
99   - ((RedissonConnectionFactory) factory).destroy();
100   - }
101   - } catch (Exception e) {
102   - log.error("[closeProducer][关闭 redisson 连接异常]", e);
103   - }
  83 + protected void closeProducer(RedisTemplate<String, Object> producer) throws Exception {
  84 + RedisConnectionFactory factory = producer.getConnectionFactory();
  85 + if (factory != null) {
  86 + ((RedissonConnectionFactory) factory).destroy();
104 87 }
105 88 }
106 89  
... ... @@ -119,7 +102,7 @@ public class IotRedisStreamMQDataBridgeExecute extends AbstractCacheableDataBrid
119 102 IotRedisStreamMQDataBridgeExecute action = new IotRedisStreamMQDataBridgeExecute();
120 103  
121 104 // 2. 创建共享的配置
122   - IotDataBridgeDO.RedisStreamMQConfig config = new IotDataBridgeDO.RedisStreamMQConfig();
  105 + IotDataBridgeRedisStreamMQConfig config = new IotDataBridgeRedisStreamMQConfig();
123 106 config.setHost("127.0.0.1");
124 107 config.setPort(6379);
125 108 config.setDatabase(0);
... ... @@ -140,11 +123,11 @@ public class IotRedisStreamMQDataBridgeExecute extends AbstractCacheableDataBrid
140 123 .build();
141 124  
142 125 // 4. 执行两次测试,验证缓存
143   - log.info("[main][第一次执行,应该会创建新的 RedisTemplate]");
144   - action.executeRedisStream(message, config);
  126 + log.info("[main][第一次执行,应该会创建新的 producer]");
  127 + action.execute(message, new IotDataBridgeDO().setType(action.getType()).setConfig(config));
145 128  
146   - log.info("[main][第二次执行,应该会复用缓存的 RedisTemplate]");
147   - action.executeRedisStream(message, config);
  129 + log.info("[main][第二次执行,应该会复用缓存的 producer]");
  130 + action.execute(message, new IotDataBridgeDO().setType(action.getType()).setConfig(config));
148 131 }
149 132  
150 133 }
151 134 \ No newline at end of file
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotRocketMQDataBridgeExecute.java
1 1 package cn.iocoder.yudao.module.iot.service.rule.action.databridge;
2 2  
  3 +import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.databridge.config.IotDataBridgeRocketMQConfig;
3 4 import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotDataBridgeDO;
4   -import cn.iocoder.yudao.module.iot.enums.rule.IotDataBridgTypeEnum;
  5 +import cn.iocoder.yudao.module.iot.enums.rule.IotDataBridgeTypeEnum;
5 6 import cn.iocoder.yudao.module.iot.mq.message.IotDeviceMessage;
6 7 import lombok.extern.slf4j.Slf4j;
7 8 import org.apache.rocketmq.client.producer.DefaultMQProducer;
... ... @@ -9,6 +10,7 @@ import org.apache.rocketmq.client.producer.SendResult;
9 10 import org.apache.rocketmq.client.producer.SendStatus;
10 11 import org.apache.rocketmq.common.message.Message;
11 12 import org.apache.rocketmq.remoting.common.RemotingHelper;
  13 +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
12 14 import org.springframework.stereotype.Component;
13 15  
14 16 import java.time.LocalDateTime;
... ... @@ -18,58 +20,49 @@ import java.time.LocalDateTime;
18 20 *
19 21 * @author HUIHUI
20 22 */
  23 +@ConditionalOnClass(name = "org.apache.rocketmq.client.producer.DefaultMQProducer")
21 24 @Component
22 25 @Slf4j
23   -public class IotRocketMQDataBridgeExecute extends AbstractCacheableDataBridgeExecute {
  26 +public class IotRocketMQDataBridgeExecute extends
  27 + AbstractCacheableDataBridgeExecute<IotDataBridgeRocketMQConfig, DefaultMQProducer> {
24 28  
25 29 @Override
26   - public void execute(IotDeviceMessage message, IotDataBridgeDO dataBridge) {
27   - // 1.1 校验数据桥梁的类型 == ROCKETMQ
28   - if (!IotDataBridgTypeEnum.ROCKETMQ.getType().equals(dataBridge.getType())) {
29   - return;
30   - }
31   - // 1.2 执行 RocketMQ 发送消息
32   - executeRocketMQ(message, (IotDataBridgeDO.RocketMQConfig) dataBridge.getConfig());
  30 + public Integer getType() {
  31 + return IotDataBridgeTypeEnum.ROCKETMQ.getType();
33 32 }
34 33  
35   - private void executeRocketMQ(IotDeviceMessage message, IotDataBridgeDO.RocketMQConfig config) {
36   - try {
37   - // 1. 获取或创建 Producer
38   - DefaultMQProducer producer = (DefaultMQProducer) getProducer(config);
  34 + @Override
  35 + public void execute0(IotDeviceMessage message, IotDataBridgeRocketMQConfig config) throws Exception {
  36 + // 1. 获取或创建 Producer
  37 + DefaultMQProducer producer = getProducer(config);
39 38  
40   - // 2.1 创建消息对象,指定Topic、Tag和消息体
41   - Message msg = new Message(
42   - config.getTopic(),
43   - config.getTags(),
44   - message.toString().getBytes(RemotingHelper.DEFAULT_CHARSET)
45   - );
46   - // 2.2 发送同步消息并处理结果
47   - SendResult sendResult = producer.send(msg);
48   - // 2.3 处理发送结果
49   - if (SendStatus.SEND_OK.equals(sendResult.getSendStatus())) {
50   - log.info("[executeRocketMQ][message({}) config({}) 发送成功,结果({})]", message, config, sendResult);
51   - } else {
52   - log.error("[executeRocketMQ][message({}) config({}) 发送失败,结果({})]", message, config, sendResult);
53   - }
54   - } catch (Exception e) {
55   - log.error("[executeRocketMQ][message({}) config({}) 发送异常]", message, config, e);
  39 + // 2.1 创建消息对象,指定Topic、Tag和消息体
  40 + Message msg = new Message(
  41 + config.getTopic(),
  42 + config.getTags(),
  43 + message.toString().getBytes(RemotingHelper.DEFAULT_CHARSET)
  44 + );
  45 + // 2.2 发送同步消息并处理结果
  46 + SendResult sendResult = producer.send(msg);
  47 + // 2.3 处理发送结果
  48 + if (SendStatus.SEND_OK.equals(sendResult.getSendStatus())) {
  49 + log.info("[executeRocketMQ][message({}) config({}) 发送成功,结果({})]", message, config, sendResult);
  50 + } else {
  51 + log.error("[executeRocketMQ][message({}) config({}) 发送失败,结果({})]", message, config, sendResult);
56 52 }
57 53 }
58 54  
59 55 @Override
60   - protected Object initProducer(Object config) throws Exception {
61   - IotDataBridgeDO.RocketMQConfig rocketMQConfig = (IotDataBridgeDO.RocketMQConfig) config;
62   - DefaultMQProducer producer = new DefaultMQProducer(rocketMQConfig.getGroup());
63   - producer.setNamesrvAddr(rocketMQConfig.getNameServer());
  56 + protected DefaultMQProducer initProducer(IotDataBridgeRocketMQConfig config) throws Exception {
  57 + DefaultMQProducer producer = new DefaultMQProducer(config.getGroup());
  58 + producer.setNamesrvAddr(config.getNameServer());
64 59 producer.start();
65 60 return producer;
66 61 }
67 62  
68 63 @Override
69   - protected void closeProducer(Object producer) {
70   - if (producer instanceof DefaultMQProducer) {
71   - ((DefaultMQProducer) producer).shutdown();
72   - }
  64 + protected void closeProducer(DefaultMQProducer producer) {
  65 + producer.shutdown();
73 66 }
74 67  
75 68 // TODO @芋艿:测试代码,后续清理
... ... @@ -78,7 +71,7 @@ public class IotRocketMQDataBridgeExecute extends AbstractCacheableDataBridgeExe
78 71 IotRocketMQDataBridgeExecute action = new IotRocketMQDataBridgeExecute();
79 72  
80 73 // 2. 创建共享的配置
81   - IotDataBridgeDO.RocketMQConfig config = new IotDataBridgeDO.RocketMQConfig();
  74 + IotDataBridgeRocketMQConfig config = new IotDataBridgeRocketMQConfig();
82 75 config.setNameServer("127.0.0.1:9876");
83 76 config.setGroup("test-group");
84 77 config.setTopic("test-topic");
... ... @@ -99,10 +92,10 @@ public class IotRocketMQDataBridgeExecute extends AbstractCacheableDataBridgeExe
99 92  
100 93 // 4. 执行两次测试,验证缓存
101 94 log.info("[main][第一次执行,应该会创建新的 producer]");
102   - action.executeRocketMQ(message, config);
  95 + action.execute(message, new IotDataBridgeDO().setType(action.getType()).setConfig(config));
103 96  
104 97 log.info("[main][第二次执行,应该会复用缓存的 producer]");
105   - action.executeRocketMQ(message, config);
  98 + action.execute(message, new IotDataBridgeDO().setType(action.getType()).setConfig(config));
106 99 }
107 100  
108 101 }
... ...
yudao-server/pom.xml
... ... @@ -114,6 +114,19 @@
114 114 <artifactId>yudao-module-iot-biz</artifactId>
115 115 <version>${revision}</version>
116 116 </dependency>
  117 + <!-- IoT 数据桥梁的执行器所需消息队列。如果您只需要使用 rocketmq 那么则注释掉其它消息队列即可 -->
  118 + <dependency>
  119 + <groupId>org.apache.rocketmq</groupId>
  120 + <artifactId>rocketmq-spring-boot-starter</artifactId>
  121 + </dependency>
  122 + <dependency>
  123 + <groupId>org.springframework.kafka</groupId>
  124 + <artifactId>spring-kafka</artifactId>
  125 + </dependency>
  126 + <dependency>
  127 + <groupId>org.springframework.boot</groupId>
  128 + <artifactId>spring-boot-starter-amqp</artifactId>
  129 + </dependency>
117 130  
118 131 <!-- spring boot 配置所需依赖 -->
119 132 <dependency>
... ...